commit a2411dde9f4374f0ce76fec10fce3d9eb8e10498
Author: David Sorber <david.sorber@gmail.com>
Date:   Mon Jul 9 19:04:55 2018 -0400

    Adding "raw" packJPG source from the upstream repository (commit 59e8d45d463cfab96155ffccb77b1207b18c614b).

diff --git a/software/packJPG_library/.gitignore b/software/packJPG_library/.gitignore
new file mode 100644
index 0000000..782adf8
--- /dev/null
+++ b/software/packJPG_library/.gitignore
@@ -0,0 +1,43 @@
+# Compiled Object files
+*.slo
+*.lo
+*.o
+*.obj
+
+# Precompiled Headers
+*.gch
+*.pch
+
+# Compiled Dynamic libraries
+*.so
+*.dylib
+*.dll
+
+# Fortran module files
+*.mod
+
+# Compiled Static libraries
+*.lai
+*.la
+*.a
+*.lib
+
+# Executables
+*.exe
+*.out
+*.app
+
+# Folders to ignore
+/zzz_backup/
+
+# Test / output files
+*.jpg
+*.jpeg
+*.pjg
+*.pgm
+*.nfo
+*.hdr
+*.huf
+*.coll?
+*.zdst?
+*.dist
diff --git a/software/packJPG_library/LICENSE b/software/packJPG_library/LICENSE
new file mode 100644
index 0000000..fb6d90b
--- /dev/null
+++ b/software/packJPG_library/LICENSE
@@ -0,0 +1,166 @@
+GNU LESSER GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+  This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+  0. Additional Definitions.
+
+  As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+  "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+  An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+  A "Combined Work" is a work produced by combining or linking an
+Application with the Library.  The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+  The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+  The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+  1. Exception to Section 3 of the GNU GPL.
+
+  You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+  2. Conveying Modified Versions.
+
+  If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+   a) under this License, provided that you make a good faith effort to
+   ensure that, in the event an Application does not supply the
+   function or data, the facility still operates, and performs
+   whatever part of its purpose remains meaningful, or
+
+   b) under the GNU GPL, with none of the additional permissions of
+   this License applicable to that copy.
+
+  3. Object Code Incorporating Material from Library Header Files.
+
+  The object code form of an Application may incorporate material from
+a header file that is part of the Library.  You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+   a) Give prominent notice with each copy of the object code that the
+   Library is used in it and that the Library and its use are
+   covered by this License.
+
+   b) Accompany the object code with a copy of the GNU GPL and this license
+   document.
+
+  4. Combined Works.
+
+  You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+   a) Give prominent notice with each copy of the Combined Work that
+   the Library is used in it and that the Library and its use are
+   covered by this License.
+
+   b) Accompany the Combined Work with a copy of the GNU GPL and this license
+   document.
+
+   c) For a Combined Work that displays copyright notices during
+   execution, include the copyright notice for the Library among
+   these notices, as well as a reference directing the user to the
+   copies of the GNU GPL and this license document.
+
+   d) Do one of the following:
+
+       0) Convey the Minimal Corresponding Source under the terms of this
+       License, and the Corresponding Application Code in a form
+       suitable for, and under terms that permit, the user to
+       recombine or relink the Application with a modified version of
+       the Linked Version to produce a modified Combined Work, in the
+       manner specified by section 6 of the GNU GPL for conveying
+       Corresponding Source.
+
+       1) Use a suitable shared library mechanism for linking with the
+       Library.  A suitable mechanism is one that (a) uses at run time
+       a copy of the Library already present on the user's computer
+       system, and (b) will operate properly with a modified version
+       of the Library that is interface-compatible with the Linked
+       Version.
+
+   e) Provide Installation Information, but only if you would otherwise
+   be required to provide such information under section 6 of the
+   GNU GPL, and only to the extent that such information is
+   necessary to install and execute a modified version of the
+   Combined Work produced by recombining or relinking the
+   Application with a modified version of the Linked Version. (If
+   you use option 4d0, the Installation Information must accompany
+   the Minimal Corresponding Source and Corresponding Application
+   Code. If you use option 4d1, you must provide the Installation
+   Information in the manner specified by section 6 of the GNU GPL
+   for conveying Corresponding Source.)
+
+  5. Combined Libraries.
+
+  You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+   a) Accompany the combined library with a copy of the same work based
+   on the Library, uncombined with any other library facilities,
+   conveyed under the terms of this License.
+
+   b) Give prominent notice with the combined library that part of it
+   is a work based on the Library, and explaining where to find the
+   accompanying uncombined form of the same work.
+
+  6. Revised Versions of the GNU Lesser General Public License.
+
+  The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser 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
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+  If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
+
diff --git a/software/packJPG_library/Readme.txt b/software/packJPG_library/Readme.txt
new file mode 100644
index 0000000..8aa5e16
--- /dev/null
+++ b/software/packJPG_library/Readme.txt
@@ -0,0 +1,268 @@
+packJPG v2.5k (01/22/2016)
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+packJPG is a compression program specially designed for further
+compression of JPEG images without causing any further loss. Typically
+it reduces the file size of a JPEG file by 20%.
+
+
+LGPL v3 license and special permissions
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+All programs in this package are free software; you can redistribute 
+them and/or modify them under the terms of the GNU Lesser General Public 
+License as published by the Free Software Foundation; either version 3 
+of the License, or (at your option) any later version. 
+
+The package 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 Lesser 
+General Public License for more details at 
+http://www.gnu.org/copyleft/lgpl.html. 
+
+If the LGPL v3 license is not compatible with your software project you 
+might contact us and ask for a special permission to use the packJPG 
+library under different conditions. In any case, usage of the packJPG 
+algorithm under the LGPL v3 or above is highly advised and special 
+permissions will only be given where necessary on a case by case basis. 
+This offer is aimed mainly at closed source freeware developers seeking 
+to add PJG support to their software projects. 
+
+Copyright 2006...2014 by HTW Aalen University and Matthias Stirner.
+
+
+Usage of packJPG
+~~~~~~~~~~~~~~~~
+
+JPEG files are compressed and PJG files are decompressed using this
+command:
+
+ "packJPG [file(s)]"
+
+packJPG recognizes file types on its own and decides whether to compress
+(JPG) or decompress (PJG). For unrecognized file types no action is
+taken. Files are recognized by content, not by extension.
+
+packJPG supports wildcards like "*.*" and drag and drop of multiple 
+files. Filenames for output files are created automatically. In default
+mode, files are never overwritten. If a filename is already in use,
+packJPG creates a new filename by adding underscores.
+
+If "-" is used as a filename input from stdin is assumed and output is
+written to stdout. This can be useful for example if jpegtran is to be
+used as a preprocessor.
+
+Usage examples:
+
+ "packJPG *.pjg"
+ "packJPG lena.jpg"
+ "packJPG kodim??.jpg"
+ "packJPG - < sail.pjg > sail.jpg"
+
+
+Command line switches
+~~~~~~~~~~~~~~~~~~~~~
+
+ -ver  verify files after processing
+ -v?   level of verbosity; 0,1 or 2 is allowed (default 0)
+ -np   no pause after processing files
+ -o    overwrite existing files
+ -p    proceed on warnings
+ -d    discard meta-info
+
+By default, compression is cancelled on warnings. If warnings are 
+skipped by using "-p", most files with warnings can also be compressed, 
+but JPEG files reconstructed from PJG files might not be bitwise 
+identical with the original JPEG files. There won't be any loss to 
+image data or quality however.
+
+Unnecessary meta information can be discarded using "-d". This reduces 
+compressed files' sizes. Be warned though, reconstructed files won't be 
+bitwise identical with the original files and meta information will be 
+lost forever. As with "-p" there won't be any loss to image data or 
+quality. 
+
+There is no known case in which a file compressed by packJPG (without 
+the "-p" option, see above) couldn't be reconstructed to exactly the 
+state it was before. If you want an additional layer of safety you can 
+also use the verify option "-ver". In this mode, files are compressed, 
+then decompressed and the decompressed file compared to the original 
+file. If this test doesn't pass there will be an error message and the 
+compressed file won't be written to the drive. 
+
+Please note that the "-ver" option should never be used in conjunction 
+with the "-d" and/or "-p" options. As stated above, the "-p" and "-d" 
+options will most likely lead to reconstructed JPG files not being 
+bitwise identical to the original JPG files. In turn, the verification 
+process may fail on various files although nothing actually went wrong. 
+
+Usage examples:
+
+ "packJPG -v1 -o baboon.pjg"
+ "packJPG -ver lena.jpg"
+ "packJPG -d tiffany.jpg"
+ "packJPG -p *.jpg"
+
+
+Known Limitations 
+~~~~~~~~~~~~~~~~~ 
+
+packJPG is a compression program specially for JPEG files, so it doesn't 
+compress other file types.
+
+packJPG has low error tolerance. JPEG files might not work with packJPG 
+even if they work perfectly with other image processing software. The 
+command line switch "-p" can be used to increase error tolerance and 
+compatibility.
+
+If you try to drag and drop to many files at once, there might be a 
+windowed error message about missing privileges. In that case you can 
+try it again with less files or consider using the command prompt. 
+packJPG has been tested to work perfectly with thousands of files from 
+the command line. This issue also happens with drag and drop in other 
+applications, so it might not be a limitation of packJPG but a 
+limitation of Windows.
+
+Compressed PJG files are not compatible between different packJPG 
+versions. You will get an error message if you try to decompress PJG 
+files with a different version than the one used for compression. You 
+may download older versions of packJPG from:
+http://www.elektronik.htw-aalen.de/packJPG/binaries/old/
+
+
+Open source release / developer info
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The packJPG source codes is found inside the "source" subdirectory. 
+Additional documents aimed to developers, containing detailed 
+instructions on compiling the source code and using special 
+functionality, are included in the "packJPG" subdirectory. 
+ 
+
+History
+~~~~~~~
+
+v1.9a (04/20/2007) (non public)
+ - first released version
+ - only for testing purposes
+
+v2.0  (05/28/2007) (public)
+ - first public version of packJPG
+ - minor improvements to overall compression
+ - minor bugfixes
+
+v2.2  (08/05/2007) (public)
+ - around 40% faster compression & decompression
+ - major improvements to overall compression (around 2% on average)
+ - reading from stdin, writing to stdout
+ - smaller executable
+ - minor bugfixes
+ - various minor improvements
+ 
+v2.3  (09/18/2007) (public)
+ - compatibility with JPEG progressive mode
+ - compatibility with JPEG extended sequential mode
+ - compatibility with the CMYK color space
+ - compatibility with older CPUs
+ - around 15% faster compression & decompression 
+ - new switch: [-d] (discard meta-info)
+ - various bugfixes
+
+v2.3a (11/21/2007) (public)
+ - crash issue with certain images fixed
+ - compatibility with packJPG v2.3 maintained
+
+v2.3b (12/20/2007) (public)
+ - some minor errors in the packJPG library fixed
+ - compatibility with packJPG v2.3 maintained
+ 
+v2.4 (03/24/2010) (public)
+ - major improvements (1%...2%) to overall compression
+ - around 10% faster compression & decompression
+ - major improvements to JPG compatibility
+ - size of executable reduced to ~33%
+ - new switch: [-ver] (verify file after processing)
+ - new switch: [-np] (no pause after processing)
+ - new progress bar output mode
+ - arithmetic coding routines rewritten from scratch
+ - various smaller improvements to numerous to list here
+ - new SFX (self extracting) archive format
+ 
+v2.5 (11/11/2011) (public)
+ - improvements (~0.5%) to overall compression
+ - several minor bugfixes
+ - major code cleanup
+ - removed packJPX from the package
+ - added packARC to the package
+ - packJPG is now open source!
+ 
+v2.5a (11/21/11) (public)
+ - source code compatibility improvements (Gerhard Seelmann)
+ - avoid some compiler warnings (Gerhard Seelmann)
+ - source code clean up (Gerhard Seelmann)
+ 
+v2.5b (01/27/12) (public)
+ - further removal of redundant code
+ - some fixes for the packJPG static library
+ - compiler fix for Mac OS (thanks to Sergio Lopez)
+ - improved compression ratio calculation
+ - eliminated the need for temp files
+ 
+v2.5c (04/13/12) (public)
+ - various source code optimizations
+ 
+v2.5d (07/03/12) (public)
+ - fixed a rare bug with progressive JPEG
+ 
+v2.5e (07/03/12) (public)
+ - some minor source code optimizations
+ - changed packJPG licensing to LGPL
+ - moved packARC to a separate package
+ 
+v2.5f (02/24/13) (public)
+ - fixed a minor bug in the JPG parser (thanks to Stephan Busch)
+ 
+v2.5g (09/14/13) (public)
+ - fixed a rare crash bug with manipulated JPEG files
+ 
+v2.5h (12/07/13) (public)
+ - added a warning for inefficient huffman coding (thanks to Moinak Ghosh)
+ 
+v2.5i (12/26/13) (public)
+ - fixed possible crash with malformed JPEG (thanks to Moinak Ghosh)
+ 
+v2.5j (01/15/14) (public)
+ - various source code optimizations (using cppcheck)
+
+v2.5k (01/22/16) (public)
+ - Updated contact info
+ - fixed a minor bug
+
+
+Acknowledgements
+~~~~~~~~~~~~~~~~
+
+packJPG is the result of countless hours of research and development. It
+is part of my final year project for Hochschule Aalen.
+
+Prof. Dr. Gerhard Seelmann from Hochschule Aalen supported my
+development of packJPG with his extensive knowledge in the field of data
+compression. Without his advice, packJPG would not be possible.
+
+The official developer blog for packJPG is hosted by encode.ru.
+
+packJPG logo and icon are designed by Michael Kaufmann.
+
+
+Contact
+~~~~~~~
+
+The official developer blog for packJPG:
+ http://packjpg.encode.ru/
+ 
+For questions and bug reports:
+ packjpg (at) matthiasstirner.com
+
+
+____________________________________
+packJPG by Matthias Stirner, 01/2016
diff --git a/software/packJPG_library/docs/contents.txt b/software/packJPG_library/docs/contents.txt
new file mode 100644
index 0000000..b083538
--- /dev/null
+++ b/software/packJPG_library/docs/contents.txt
@@ -0,0 +1,56 @@
+packJPG package - table of contents
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+Source files
+~~~~~~~~~~~~
+
+All files needed to compile packJPG as an executable or as a library are 
+included in the 'source' subdirectory': 
+
+- 'Makefile' (universal makefile)
+- 'aricoder.cpp' (arithmetic coder source file)
+- 'aricoder.h' (arithmetic coder header file)
+- 'bitops.cpp' (bitwise file I/O routines source file)
+- 'bitops.h' (bitwise file I/O routines header file)
+- 'dct8x8.h' (discrete cosine transform header file)
+- 'packjpg.cpp' (packJPG main source file)
+- 'packjpglib.h' (packJPG static library header file)
+- 'pjpgtbl.h' (helper tables header file)
+
+These files are included as well, but are not necessarily needed for 
+compiling packJPG: 
+
+- 'packjpg.spec' (RPM spec file, provided by Bryan Stillwell)
+- 'Makefile_osx' (special OS X Makefile, provided by Ryan Flynn)
+- 'packjpgdll.h' (packJPG DLL header file)
+- 'file_icon.ico' (suggested .pjg icon in .ico format)
+- 'app_icon.ico' (application icon in .ico format)
+- 'icon.res' (application icon in Windows .res format)
+
+
+Documentation
+~~~~~~~~~~~~~
+
+The packJPG source code is well commented. Additionally, these files are 
+included in the 'doc' subdirectory. 
+
+- 'developer.txt' (information about developer functions)
+- 'sample_images.zip' (sample images, referenced in 'developer.txt')
+- 'AalenUniv.jpg' (sample image, showing Aalen University)
+- 'dsci0692.jpg' (sample image, showing a flower)
+- 'usage.txt' (generic usage information)
+- 'howtocompile.txt' (compile instructions)
+- 'versionnumbering.txt' (version numbering guideline)
+- 'contents.txt' (the file you are currently reading)
+
+
+*NOT* included
+~~~~~~~~~~~~~~
+
+There's no further documentation on the packJPG algorithms - all 
+documentation is included within the source code. 
+
+
+____________________________________
+packJPG by Matthias Stirner, 10/2012
\ No newline at end of file
diff --git a/software/packJPG_library/docs/developer.txt b/software/packJPG_library/docs/developer.txt
new file mode 100644
index 0000000..7e275c8
--- /dev/null
+++ b/software/packJPG_library/docs/developer.txt
@@ -0,0 +1,84 @@
+-Developer functions detailed descriptions
+ -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ -
+ -packJPG contains functionality especially designed for developers. This 
+ -document details how to use the developer functions. 
+ -
+ -
+ -Developer switches
+ -~~~~~~~~~~~~~~~~~~
+ -
+ - -dev    needed to enable any of the switches bewlow
+ - 
+ - -test   test algorithms, alert if error
+ - -split  split jpeg (to header & image data)
+ - -coll?  write coefficients (0=std,1=dhf,2=squ,3=unc)
+ - -fcol?  write filtered coefficients (see above)
+ - -zdst   write zero distribution lists to files	
+ - -info   write debug info to .nfo file
+ - -dist   write distribution data to file
+ - -pgm    convert and write to pgm files
+ - 
+ - -s?     set global number of segments (1<=s<=49)
+ - -t?     set global noise threshold (0<=t<=10)
+ - -s?,?   set number of segments for component
+ - -t?,?   set noise threshold for component
+ -
+ - 
+ -Developer switches usage
+ -~~~~~~~~~~~~~~~~~~~~~~~~
+ - 
+ -The developer switch "-ver" is needed the enable any developer function. 
+ -Other developer functions won't work without it. 
+ -
+ -"-test" is a special case of the "-ver" option. Files are also verified, 
+ -but if any error happens, all the output will be kept for the developer 
+ -to have a look at it. 
+ -
+ -"-split" splits a JPEG file to huffman coded image data and header data. 
+ -This mode is useful if a closer look at the header of a given file is 
+ -needed. 
+ -
+ -"-coll?" and "-fcol?" write decompressed DCT coefficients to separate 
+ -files (one for each color component). DCT coefficients are stored in two 
+ -byte signed short integers. In the "-fcol?" option, DC coefficents are 
+ -replaced by the corresponding DC prediction errors, while using the 
+ -"-coll?" option will dump DC coeffcients without any prediction applied. 
+ -The "?" has to be replaced by a number 0, 1, 2 or 3, which will 
+ -determine the order the coefficients will be stored inside the files. 
+ -Have a look at the included sample images or the source code to get an 
+ -idea on which each number stands for. 
+ -
+ -"-zdst" writes zero distribution lists to files (one for each color 
+ -component). For each 8x8 image data block in a given color component, a 
+ -zero distribution list contains the number of coefficients unequal zero. 
+ -These numbers are stored as unsigned one byte chars. In packJPG, zero 
+ -distribution lists are used as a replacement for JPEGs EOB symbol and 
+ -for segmentation of data. 
+ -
+ -"-info" dumps a ".nfo" text file with data about the JPEG file. 
+ -Structure of header, type of coding and quantization tables for each 
+ -component are included in this file. 
+ -
+ -"-dump" dumps distribution for a given file to ".dist" files (again, one 
+ -file per color component). For each band seperately this files contain 
+ -the number of coefficients = 0; 1; 2; 3; 4; .... (absolute values). Have 
+ -a look at the source code to get an idea about the structure of these 
+ -files. 
+ -
+ -"-pgm" mainly serves as a test function for the integrated DCT routines. 
+ -Each color component is transformed back to it's image representation 
+ -using the IDCT and dumped seperately as a ".PGM" (Portable GrayMap) 
+ -file.
+ -
+ -"-t?" and "-s?" control specific compression settings. By manipulating 
+ -these parameters, you might achieve higher compression. By default, 
+ -these values are set automatically. The default for segmentation ("-s?") 
+ -is 10. The default for the noise threshold ("-t?") is dependant on the 
+ -size of the input file. Higher sizes mean higher t - theres more data 
+ -for the statistical model to learn in bigger files, so less has to be 
+ -considered noise. 
+ -
+ -
+ -____________________________________
+ -packJPG by Matthias Stirner, 11/2011
diff --git a/software/packJPG_library/docs/howtocompile.txt b/software/packJPG_library/docs/howtocompile.txt
new file mode 100644
index 0000000..4f5e658
--- /dev/null
+++ b/software/packJPG_library/docs/howtocompile.txt
@@ -0,0 +1,63 @@
+How to compile packJPG (01/30/2012)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This document gives detailed instructions on compiling packJPG either as 
+an executable or a static/shared libary. 
+
+
+Compiling packJPG as an executable
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
+
+Makefiles or provided for Microsoft Windows NT ('Makefile') and Unix 
+('Makefile.unix'). Both have been tested and verified to work in GCC 
+v3.4.5, but they should work with other compilers and newer/older 
+versions of the GCC as well. If you want to compile packJPG in Microsoft 
+Visual Studio for Microsoft Windows, just create a new project and add 
+all source files to it. 
+
+
+Compiling packJPG as a static or shared library 
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
+
+If you want to compile packJPG as a static or shared library, add all 
+the source files to a new static/shared library project. 'BUILD_LIB' 
+(without the ') has to be defined either at the beginning of 
+'packJPG.cpp' or from the compilers options. 'BUILD_DLL' has to be 
+defined in addition only if a shared library is to be compiled (don't 
+define this for a static library!).
+
+The source file 'packJPGdll.h' contains all public function declarations 
+of the library and can be included in external projects which use the 
+packJPG shared library. 
+
+
+Compiling developer functions
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
+
+If you want to include developer functions in the packJPG executable, 
+'DEV_BUILD' has to be defined. Developer functions are not available in 
+the packJPG library. They are not needed in any way for basic packJPG 
+functionality (i.e. compression and decompression) and can be left out 
+to compile a smaller executable. Read 'developer.txt' for more 
+information. 
+
+
+Defines overview / summary
+~~~~~~~~~~~~~~~~~~~~~~~~~~ 
+
+This paragraph gives a short overview over preprocessor defines 
+mentioned in the above paragraphs. 
+
+BUILD_LIB
+Has to be defined when compiling a static or shared library. 
+
+BUILD_DLL
+Has to defined when compiling a shared library only. 
+
+BUILD_DEV
+If this is defined, developer functions are included in the executable. 
+In library builds developer functions are not available.
+
+
+____________________________________
+packJPG by Matthias Stirner, 01/2012 
\ No newline at end of file
diff --git a/software/packJPG_library/docs/lgpl-3.0.txt b/software/packJPG_library/docs/lgpl-3.0.txt
new file mode 100644
index 0000000..65c5ca8
--- /dev/null
+++ b/software/packJPG_library/docs/lgpl-3.0.txt
@@ -0,0 +1,165 @@
+                   GNU LESSER GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+  This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+  0. Additional Definitions.
+
+  As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+  "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+  An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+  A "Combined Work" is a work produced by combining or linking an
+Application with the Library.  The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+  The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+  The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+  1. Exception to Section 3 of the GNU GPL.
+
+  You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+  2. Conveying Modified Versions.
+
+  If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+   a) under this License, provided that you make a good faith effort to
+   ensure that, in the event an Application does not supply the
+   function or data, the facility still operates, and performs
+   whatever part of its purpose remains meaningful, or
+
+   b) under the GNU GPL, with none of the additional permissions of
+   this License applicable to that copy.
+
+  3. Object Code Incorporating Material from Library Header Files.
+
+  The object code form of an Application may incorporate material from
+a header file that is part of the Library.  You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+   a) Give prominent notice with each copy of the object code that the
+   Library is used in it and that the Library and its use are
+   covered by this License.
+
+   b) Accompany the object code with a copy of the GNU GPL and this license
+   document.
+
+  4. Combined Works.
+
+  You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+   a) Give prominent notice with each copy of the Combined Work that
+   the Library is used in it and that the Library and its use are
+   covered by this License.
+
+   b) Accompany the Combined Work with a copy of the GNU GPL and this license
+   document.
+
+   c) For a Combined Work that displays copyright notices during
+   execution, include the copyright notice for the Library among
+   these notices, as well as a reference directing the user to the
+   copies of the GNU GPL and this license document.
+
+   d) Do one of the following:
+
+       0) Convey the Minimal Corresponding Source under the terms of this
+       License, and the Corresponding Application Code in a form
+       suitable for, and under terms that permit, the user to
+       recombine or relink the Application with a modified version of
+       the Linked Version to produce a modified Combined Work, in the
+       manner specified by section 6 of the GNU GPL for conveying
+       Corresponding Source.
+
+       1) Use a suitable shared library mechanism for linking with the
+       Library.  A suitable mechanism is one that (a) uses at run time
+       a copy of the Library already present on the user's computer
+       system, and (b) will operate properly with a modified version
+       of the Library that is interface-compatible with the Linked
+       Version.
+
+   e) Provide Installation Information, but only if you would otherwise
+   be required to provide such information under section 6 of the
+   GNU GPL, and only to the extent that such information is
+   necessary to install and execute a modified version of the
+   Combined Work produced by recombining or relinking the
+   Application with a modified version of the Linked Version. (If
+   you use option 4d0, the Installation Information must accompany
+   the Minimal Corresponding Source and Corresponding Application
+   Code. If you use option 4d1, you must provide the Installation
+   Information in the manner specified by section 6 of the GNU GPL
+   for conveying Corresponding Source.)
+
+  5. Combined Libraries.
+
+  You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+   a) Accompany the combined library with a copy of the same work based
+   on the Library, uncombined with any other library facilities,
+   conveyed under the terms of this License.
+
+   b) Give prominent notice with the combined library that part of it
+   is a work based on the Library, and explaining where to find the
+   accompanying uncombined form of the same work.
+
+  6. Revised Versions of the GNU Lesser General Public License.
+
+  The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser 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
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+  If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
diff --git a/software/packJPG_library/docs/sample_images.zip b/software/packJPG_library/docs/sample_images.zip
new file mode 100644
index 0000000..daa2041
Binary files /dev/null and b/software/packJPG_library/docs/sample_images.zip differ
diff --git a/software/packJPG_library/docs/versionnumbering.txt b/software/packJPG_library/docs/versionnumbering.txt
new file mode 100644
index 0000000..efbca5b
--- /dev/null
+++ b/software/packJPG_library/docs/versionnumbering.txt
@@ -0,0 +1,27 @@
+Version numbering guideline
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+The packJPG version consists of two parts the main version number and 
+the sub version string. An example: 'packJPG v2.4a', an older version of 
+packJPG, has the main version number '2.4' and the sub version string 
+'a'. 
+
+Compressed .pjg files are required to be compatible between subversions: 
+packJPG v2.4a compressed .pjg files can be extracted by packJPG v2.4, 
+packJPG v2.4b, packJPG v2.4c, ... and vice versa. Only if there is a 
+change in the main version number, encoded files can be incompatible 
+between each other: packJPG v2.4 won't extract packJPG v2.3 compressed 
+.pjg files and vice versa. 
+
+The subversion string is used to indicate smaller changes that won't 
+break compatibility, like, f.e., bug fixes or speed improvements. It can 
+also be empty (for the first new main version in a series) and should be 
+enumerated as 'a', 'b', 'c', ... , 'z', 'aa', 'ab', 'ac', ..., 'zz'. For 
+any highly specific improvements it might also be used differently, to 
+indicate these changes, f.e. 'packJPG v2.5fast'. 
+
+The main version number is changed directly in the source code via the 
+'pjgversion' (main version number) and the 'subversion' (subversion 
+string) constant variables. 
+
diff --git a/software/packJPG_library/source/Makefile b/software/packJPG_library/source/Makefile
new file mode 100644
index 0000000..b927b9b
--- /dev/null
+++ b/software/packJPG_library/source/Makefile
@@ -0,0 +1,56 @@
+# packXXX Makefile, based on UniMake: Universal Makefile
+# Created by Matthias Stirner, 01/2016
+
+TARGET   = packJPG
+CC       = gcc
+CPP      = g++
+RC       = windres -O coff
+CPPFLAGS = -I. -O3 -Wall -pedantic -funroll-loops -ffast-math -fsched-spec-load -fomit-frame-pointer -std=c++14
+LDFLAGS  = -static -static-libgcc -static-libstdc++ -lstdc++fs
+CSRC     = $(wildcard *.c)
+CPPSRC   = $(wildcard *.cpp)
+DEPS     = $(wildcard *.h) Makefile
+OBJ      = $(patsubst %.c,%.o,$(CSRC)) $(patsubst %.cpp,%.o,$(CPPSRC))
+
+# conditional stuff
+ifeq ($(OS),Windows_NT)
+LDFLAGS  += -lpthread -L libwinpthread-1.dll
+RES       = icons.res
+UPX      := -upx --best --lzma $(TARGET).exe
+else
+CPPFLAGS += -DUNIX
+RC        = 
+RES       =
+UPX       =
+endif
+
+%.o: %.cpp $(DEPS)
+	$(CPP) -c -o $@ $< $(CPPFLAGS)
+	
+%.res: %.rc
+	@-$(RC) $< $@
+
+$(TARGET): $(OBJ) $(RES)
+	$(CPP) -o $@ $^ -s $(LDFLAGS)
+	$(UPX)
+
+.PHONY: all dev lib dll
+
+all: $(TARGET)
+
+dev: CPPFLAGS += -DDEV_BUILD
+dev: $(TARGET)
+
+lib: CPPFLAGS += -DBUILD_LIB
+lib: $(OBJ)
+	ar r $(TARGET)lib.a $(OBJ)
+	ranlib $(TARGET)lib.a
+    
+dll: CPPFLAGS += -DBUILD_DLL
+dll: LDFLAGS  += -Wl,--out-implib,libpackJPG.a -fvisibility=hidden
+dll: $(OBJ)
+	$(CPP) -shared -o $(TARGET).dll $^ $(LDFLAGS)
+
+clean:
+	@echo clean...
+	@-rm *.o *.a $(TARGET) $(TARGET).exe $(TARGET).dll
diff --git a/software/packJPG_library/source/Makefile_osx b/software/packJPG_library/source/Makefile_osx
new file mode 100644
index 0000000..f8347af
--- /dev/null
+++ b/software/packJPG_library/source/Makefile_osx
@@ -0,0 +1,22 @@
+# Project: packJPG
+# Makefile created by Ryan Flynn 2014.02.06
+# Working with Clang 5.1
+
+CC      = cc
+CPP     = c++
+RC      = windres
+CFLAGS  = -I. -DUNIX -DDEV_BUILD -O3 -Wall -pedantic -funroll-loops -ffast-math -fomit-frame-pointer -std=c++14
+LDFLAGS = -lstdc++fs
+DEPS    = bitops.h aricoder.h pjpgtbl.h dct8x8.h Makefile
+OBJ     = bitops.o aricoder.o packjpg.o
+RES     = icon.res
+BIN     = packjpg
+
+%.o: %.cpp $(DEPS)
+	$(CPP) -c -o $@ $< $(CFLAGS)
+
+$(BIN): $(OBJ)
+	$(CPP) -o $@ $^ $(LDFLAGS)
+
+clean:
+	$(RM) $(OBJ) $(BIN)
diff --git a/software/packJPG_library/source/app_icon.ico b/software/packJPG_library/source/app_icon.ico
new file mode 100644
index 0000000..5130bd2
Binary files /dev/null and b/software/packJPG_library/source/app_icon.ico differ
diff --git a/software/packJPG_library/source/aricoder.cpp b/software/packJPG_library/source/aricoder.cpp
new file mode 100644
index 0000000..4e911a9
--- /dev/null
+++ b/software/packJPG_library/source/aricoder.cpp
@@ -0,0 +1,747 @@
+#include "aricoder.h"
+
+#include "bitops.h"
+
+#include <algorithm>
+#include <cstdlib>
+#include <functional>
+#include <limits>
+
+template <std::uint8_t bit>
+void ArithmeticBitWriter::write_bit() {
+    // add bit at last position
+    curr_byte_ = (curr_byte_ << 1) | bit;
+	// increment bit position
+	curr_bit_++;
+
+	// write bit if done
+	if (curr_bit_ == 8) {
+		data_.emplace_back(curr_byte_);
+		curr_bit_ = 0;
+	}
+}
+
+void ArithmeticBitWriter::write_n_zero_bits(std::size_t n) {
+	if (n + curr_bit_ >= 8) {
+		auto remainingBits = 8 - curr_bit_;
+		n -= remainingBits;
+		curr_byte_ <<= remainingBits;
+		data_.emplace_back(curr_byte_);
+		curr_bit_ = 0;
+	}
+
+	while (n >= 8) {
+		data_.emplace_back(0);
+		n -= 8;
+	}
+
+	curr_byte_ <<= n;
+	curr_bit_ += n;
+}
+
+void ArithmeticBitWriter::write_n_one_bits(std::size_t n) {
+	constexpr std::uint8_t all_ones = std::numeric_limits<std::uint8_t>::max();
+	if (n + curr_bit_ >= 8) {
+		auto remainingBits = 8 - curr_bit_;
+		n -= remainingBits;
+		curr_byte_ <<= remainingBits;
+		curr_byte_ |= all_ones >> (8 - remainingBits);
+		data_.emplace_back(curr_byte_);
+		curr_bit_ = 0;
+	}
+
+	while (n >= 8) {
+		data_.emplace_back(all_ones);
+		n -= 8;
+	}
+
+	curr_byte_ = (curr_byte_ << n) | (all_ones >> (8 - n));
+	curr_bit_ += n;
+}
+
+void ArithmeticBitWriter::pad() {
+	while (curr_bit_ > 0) {
+		write_bit<0>();
+	}
+}
+
+std::vector<std::uint8_t> ArithmeticBitWriter::get_data() const {
+	return data_;
+}
+
+ArithmeticDecoder::ArithmeticDecoder(Reader& reader) : reader_(reader) {
+    // code buffer has to be filled before starting decoding
+	for (std::uint32_t i = 0; i < CODER_USE_BITS; i++ ) {
+		ccode = ( ccode << 1 ) | read_bit();
+    }
+}
+
+ArithmeticEncoder::ArithmeticEncoder(Writer& writer) : writer_(writer) {}
+
+ArithmeticEncoder::~ArithmeticEncoder() {
+    if (!finalized) {
+        this->finalize();
+    }
+}
+
+void ArithmeticEncoder::finalize() {
+    if (finalized) {
+        return;
+    }
+    
+    // due to clow < CODER_LIMIT050, and chigh >= CODER_LIMIT050
+    // there are only two possible cases
+    if (clow < CODER_LIMIT025) {
+        bitwriter_->write_bit<0>();
+        bitwriter_->write_bit<1>();
+        bitwriter_->write_n_one_bits(nrbits);
+        nrbits = 0;
+    } else {
+         // case b.), clow >= CODER_LIMIT025
+        bitwriter_->write_bit<1>();
+    }
+     // done, zeroes are auto-read by the decoder
+
+    bitwriter_->pad(); // Pad code with zeroes.
+    writer_.write(bitwriter_->get_data());
+    
+    finalized = true;
+}
+
+/* -----------------------------------------------
+	arithmetic encoder function
+	----------------------------------------------- */
+	
+void ArithmeticEncoder::encode( symbol* s )
+{	
+	// Make local copies of clow_ and chigh_ for cache performance:
+	uint32_t clow_local = clow;
+	uint32_t chigh_local = chigh;
+	// update steps, low count, high count
+	cstep = (chigh_local - clow_local + 1) / s->scale;
+	chigh_local = clow_local + (cstep * s->high_count) - 1;
+	clow_local = clow_local + (cstep * s->low_count);
+	
+	// e3 scaling is performed for speed and to avoid underflows
+	// if both, low and high are either in the lower half or in the higher half
+	// one bit can be safely shifted out
+	while ( clow_local >= CODER_LIMIT050  || chigh_local < CODER_LIMIT050  ) {
+		if (chigh_local < CODER_LIMIT050 ) {	// this means both, high and low are below, and 0 can be safely shifted out
+			// write 0 bit
+			bitwriter_->write_bit<0>();
+			// shift out remaing e3 bits
+            bitwriter_->write_n_one_bits(nrbits);
+            nrbits = 0;
+		}
+		else { // if the first wasn't the case, it's clow >= CODER_LIMIT050
+			// write 1 bit
+			bitwriter_->write_bit<1>();
+			clow_local &= CODER_LIMIT050 - 1;
+			chigh_local &= CODER_LIMIT050 - 1;
+			// shift out remaing e3 bits
+			bitwriter_->write_n_zero_bits(nrbits);
+            nrbits = 0;
+		}
+		clow_local <<= 1;
+		chigh_local <<= 1;
+		chigh_local++;
+	}
+	
+	// e3 scaling, to make sure that theres enough space between low and high
+	while ( (clow_local >= CODER_LIMIT025 ) && (chigh_local < CODER_LIMIT075 ) ) {
+		nrbits++;
+		clow_local &= CODER_LIMIT025 - 1;
+		chigh_local ^= CODER_LIMIT025 + CODER_LIMIT050;
+		// clow  -= CODER_LIMIT025;
+		// chigh -= CODER_LIMIT025;
+		clow_local <<= 1;
+		chigh_local <<= 1;
+		chigh_local++;
+	}
+
+	clow = clow_local;
+	chigh = chigh_local;
+}
+
+
+/* -----------------------------------------------
+	arithmetic decoder get count function
+	----------------------------------------------- */
+	
+unsigned int ArithmeticDecoder::decode_count( symbol* s )
+{
+	// update cstep, which is needed to remove the symbol from the stream later
+	cstep = ( ( chigh - clow ) + 1 ) / s->scale;
+	
+	// return counts, needed to decode the symbol from the statistical model
+	return ( ccode - clow ) / cstep;
+}
+
+/* -----------------------------------------------
+	arithmetic decoder function
+	----------------------------------------------- */
+	
+void ArithmeticDecoder::decode( symbol* s )
+{
+	// no actual decoding takes place, as this has to happen in the statistical model
+	// the symbol has to be removed from the stream, though
+	
+	// alread have steps updated from decoder_count
+	// update low count and high count
+	uint32_t ccode_local = ccode;
+	uint32_t clow_local = clow;
+	uint32_t chigh_local = clow_local + (cstep * s->high_count) - 1;
+	clow_local = clow_local + (cstep * s->low_count);
+	
+	// e3 scaling is performed for speed and to avoid underflows
+	// if both, low and high are either in the lower half or in the higher half
+	// one bit can be safely shifted out
+	while ( (clow_local >= CODER_LIMIT050 ) || (chigh_local < CODER_LIMIT050 ) ) {
+		if (clow_local >= CODER_LIMIT050 ) {
+			clow_local &= CODER_LIMIT050 - 1;
+			chigh_local &= CODER_LIMIT050 - 1;
+			ccode_local &= CODER_LIMIT050 - 1;
+		} // if the first wasn't the case, it's chigh < CODER_LIMIT050
+		clow_local <<= 1;
+		chigh_local <<= 1;
+		chigh_local++;
+		ccode_local <<= 1;
+		ccode_local |= read_bit();
+	}
+	
+	// e3 scaling, to make sure that theres enough space between low and high
+	while ( (clow_local >= CODER_LIMIT025 ) && (chigh_local < CODER_LIMIT075 ) ) {
+		clow_local &= CODER_LIMIT025 - 1;
+		chigh_local ^= CODER_LIMIT025 + CODER_LIMIT050;
+		// clow  -= CODER_LIMIT025;
+		// chigh -= CODER_LIMIT025;
+		ccode_local -= CODER_LIMIT025;
+		clow_local <<= 1;
+		chigh_local <<= 1;
+		chigh_local++;
+		ccode_local <<= 1;
+		ccode_local |= read_bit();
+	}	
+	chigh = chigh_local;
+	clow = clow_local;
+	ccode = ccode_local;
+}
+
+/* -----------------------------------------------
+	bit reader function
+	----------------------------------------------- */
+	
+unsigned char ArithmeticDecoder::read_bit()
+{
+	// read in new byte if needed
+	if ( cbit == 0 ) {
+		if ( !reader_.read_byte(&bbyte)) // read next byte if available
+			bbyte = 0; // if no more data is left in the stream
+		cbit = 8;
+	}
+	
+	// decrement current bit position
+	cbit--;	
+	// return bit at cbit position
+	return BITN( bbyte, cbit );
+}
+
+
+/* -----------------------------------------------
+	universal statistical model for arithmetic coding
+	
+	boundaries of this model:
+	max_s (maximum symbol) -> 1 <= max_s <= 1024 (???)
+	max_c (maximum context) -> 1 <= max_c <= 1024 (???)
+	max_o (maximum order) -> -1 <= max_o <= 4
+	c_lim (maximum count) -> 2 <= c_lim <= 4096 (???)
+	WARNING: this can be memory intensive, so don't overdo it
+	max_s == 256; max_c == 256; max_o == 4 would be way too much
+	----------------------------------------------- */
+	
+model_s::model_s( int max_s, int max_c, int max_o, int c_lim ) :
+		// Copy settings into the model:
+		max_symbol(max_s),
+		max_context(max_c),
+		max_order(max_o + 1),
+		max_count(c_lim),
+
+		current_order(max_o + 1),
+		sb0_count(max_s),
+
+		totals(max_s + 2),
+		scoreboard(new bool[max_s]),
+		contexts(max_o + 3)
+{
+	std::fill(scoreboard, scoreboard + max_symbol, false);
+	
+	// set up null table
+	table_s* null_table = new table_s;
+	null_table->counts = std::vector<uint16_t>(max_symbol, uint16_t(1));  // Set all probabilities to 1.
+
+	// set up internal counts
+	null_table->max_count = 1;
+	null_table->max_symbol = max_symbol;
+	
+	// set up start table
+	table_s* start_table = new table_s;
+	start_table->links = std::vector<table_s*>(max_context);
+	
+	// integrate tables into contexts
+	contexts[ 0 ] = null_table;
+	contexts[ 1 ] = start_table;
+	
+	// build initial 'normal' tables
+	for (int i = 2; i <= max_order; i++ ) {
+		// set up current order table
+		contexts[i] = new table_s;
+		// build forward links
+		if ( i < max_order ) {
+			contexts[i]->links = std::vector<table_s*>(max_context);
+		}
+		contexts[ i - 1 ]->links[ 0 ] = contexts[ i ];
+	}
+}
+
+
+/* -----------------------------------------------
+	model class destructor - recursive cleanup of memory is done here
+	----------------------------------------------- */
+
+model_s::~model_s()
+{	
+	// clean up each 'normal' table
+	delete contexts[1];
+	
+	// clean up null table
+	delete contexts[0];
+	
+	// free everything else
+	delete[] scoreboard;
+}
+
+
+/* -----------------------------------------------
+	Updates statistics for a specific symbol / resets to highest order.
+	Use -1 if you just want to reset without updating statistics.
+	----------------------------------------------- */
+void model_s::update_model( int symbol )
+{		
+	// only contexts, that were actually used to encode
+	// the symbol get its count updated
+	if ( symbol >= 0 ) {
+		for (int local_order = ( current_order < 1 ) ? 1 : current_order;
+				local_order <= max_order; local_order++ ) {
+			table_s* context = contexts[ local_order ];
+			auto& count = context->counts[symbol];
+			// update count for specific symbol & scale
+			count++;
+			// store side information for totalize_table
+			context->max_count = std::max(count, context->max_count);
+			context->max_symbol = std::max(uint16_t(symbol + 1), context->max_symbol);
+			// if count for that symbol have gone above the maximum count
+			// the table has to be resized (scale factor 2)
+			if (count == max_count) {
+				context->rescale_table();
+			}
+		}
+	}
+	
+	// reset scoreboard and current order
+	current_order = max_order;
+	std::fill(scoreboard, scoreboard + max_symbol, false);
+	sb0_count = max_symbol;
+}
+
+
+/* -----------------------------------------------
+	shift in one context (max no of contexts is max_c)
+	----------------------------------------------- */
+	
+void model_s::shift_context( int c )
+{	
+	// shifting is not possible if max_order is below 1
+	// or context index is negative
+	if ( ( max_order < 2 ) || ( c < 0 ) ) return;
+	
+	// shift each orders' context
+	for (int i = max_order; i > 1; i-- ) {
+		// this is the new current order context
+		table_s* context = contexts[ i - 1 ]->links[ c ];
+		
+		// check if context exists, build if needed
+		if ( context == nullptr ) {
+			// reserve memory for next table_s
+			context = new table_s;
+			// finished here if this is a max order context
+			if ( i < max_order ) {
+				// build links to higher order tables otherwise
+				context->links.resize(max_context);
+			}
+			// put context to its right place
+			contexts[ i - 1 ]->links[ c ] = context;
+		}
+		
+		// switch context
+		contexts[ i ] = context;
+	}
+}
+
+
+/* -----------------------------------------------
+	Flushes the entire model by calling rescale_table on all contexts.
+	----------------------------------------------- */
+	
+void model_s::flush_model()
+{
+	contexts[1]->recursive_flush();
+}
+
+
+/* -----------------------------------------------
+	Excludes every symbol above c.
+	----------------------------------------------- */
+	
+void model_s::exclude_symbols(int c)
+{
+	// exclusions are back to normal after update_model is used	
+
+	for ( c = c + 1; c < max_symbol; c++ ) {
+		if ( !scoreboard[ c ] ) {
+			scoreboard[ c ] = true;
+			sb0_count--;
+		}
+	}
+}
+
+
+/* -----------------------------------------------
+	converts an int to a symbol, needed only when encoding
+	----------------------------------------------- */
+	
+int model_s::convert_int_to_symbol( int c, symbol *s )
+{
+	// search the symbol c in the current context table_s,
+	// return scale, low- and high counts	
+
+	// totalize table for the current context
+	table_s* context = contexts[ current_order ];
+	totalize_table( context );
+	
+	// finding the scale is easy
+	s->scale = totals[ 0 ];
+	
+	// check if that symbol exists in the current table. send escape otherwise
+	if ( context->counts[ c ] > 0 ) {
+		// return high and low count for the current symbol
+		s->low_count  = totals[ c + 2 ];
+		s->high_count = totals[ c + 1 ];
+		return 0;
+	}
+	
+	// return high and low count for the escape symbol
+	s->low_count  = totals[ 1 ];
+	s->high_count = totals[ 0 ];
+	current_order--;
+	return 1;
+}
+
+
+/* -----------------------------------------------
+	returns the current context scale needed only when decoding
+	----------------------------------------------- */
+	
+void model_s::get_symbol_scale( symbol *s )
+{
+	// getting the scale is easy: totalize the table_s, use accumulated count -> done
+	totalize_table( contexts[ current_order ] );
+	s->scale = totals[ 0 ];
+}
+
+
+/* -----------------------------------------------
+	converts a count to an int, called after get_symbol_scale
+	----------------------------------------------- */
+	
+int model_s::convert_symbol_to_int(uint32_t count, symbol *s)
+{
+	// seek the symbol that matches the count,
+	// also, set low- and high count for the symbol - it has to be removed from the stream
+
+
+	// go through the totals table, search the symbol that matches the count
+	int c;
+	for (c = 1; c < int(totals.size()); c++) {
+		if (count >= totals[c]) {
+			break;
+		}
+	}
+	// set up the current symbol
+	s->low_count = totals[c]; // It is guaranteed that there exists such a symbol.
+	s->high_count = totals[c - 1]; // This is guaranteed to not go out of bounds since the search started at index 1 of totals.
+	// send escape if escape symbol encountered
+	if (c == 1) {
+		current_order--;
+		return ESCAPE_SYMBOL;
+	}
+	
+	// return symbol value
+	return c - 2 ; // Since c is not one and is a positive number, this will be nonnegative.
+}
+
+
+/* -----------------------------------------------
+	totals are calculated by accumulating counts in the current table_s
+	----------------------------------------------- */
+
+void model_s::totalize_table( table_s* context )
+{
+	// update exclusion is used, so this has to be done each time
+	// escape probability calculation also takes place here
+	
+	// accumulated counts must never exceed CODER_MAXSCALE
+	// as CODER_MAXSCALE is big enough, though, (2^29), this shouldn't happen and is not checked
+	
+	const auto& counts = context->counts;
+	
+	// check counts
+	if (!counts.empty()) {	// if counts are already set
+		// locally store current fill/symbol count
+		int local_symb = sb0_count;
+
+		// set the last symbol of the totals to zero
+		int i = context->max_symbol - 1;
+		totals[i + 2] = 0;
+
+		// (re)set current total
+		uint32_t curr_total = 0;
+
+		// go reverse though the whole counts table and accumulate counts
+		// leave space at the beginning of the table for the escape symbol
+		for (; i >= 0; i--) {
+			// only count probability if the current symbol is not 'scoreboard - excluded'
+			if (!scoreboard[i]) {
+				uint16_t curr_count = counts[i];
+				if (curr_count > 0) {
+					// add counts for the current symbol
+					curr_total += curr_count;
+					// exclude symbol from scoreboard
+					scoreboard[i] = true;
+					sb0_count--;
+				}
+			}
+			totals[i + 1] = curr_total;
+		}
+		// here the escape calculation needs to take place
+		uint32_t esc_prob;
+		if (local_symb == sb0_count) {
+			esc_prob = 1;
+		} else if (sb0_count == 0) {
+			esc_prob = 0;
+		} else {
+			// esc_prob = 1;
+			esc_prob  =  sb0_count * ( local_symb - sb0_count );
+			esc_prob /= ( local_symb * context->max_count );
+			esc_prob++;
+		}
+		// include escape probability in totals table
+		totals[ 0 ] = totals[ 1 ] + esc_prob;
+	} else { // if counts are not already set
+		// setup counts for current table
+		context->counts.resize(max_symbol);
+		// set totals table -> only escape probability included
+		totals[ 0 ] = 1;
+		totals[ 1 ] = 0;
+	}	
+}
+
+/* -----------------------------------------------
+	special version of model_s for binary coding
+	
+	boundaries of this model:
+	... (maximum symbol) -> 2 (0 or 1 )
+	max_c (maximum context) -> 1 <= max_c <= 1024 (???)
+	max_o (maximum order) -> -1 <= max_o <= 4
+	----------------------------------------------- */
+
+model_b::model_b( int max_c, int max_o, int c_lim ) :
+		// Copy settings into the model:
+		max_context(max_c),
+		max_order(max_o + 1),
+		max_count(c_lim),
+
+		contexts(max_o + 3)
+{
+	// set up null table
+	table* null_table = new table;
+	null_table->counts = std::vector<uint16_t>(2, uint16_t(1));
+	null_table->scale = uint32_t(2);
+	
+	// set up start table
+	table* start_table = new table;
+	start_table->links = std::vector<table*>(max_context);
+		
+	// integrate tables into contexts
+	contexts[ 0 ] = null_table;
+	contexts[ 1 ] = start_table;
+	
+	// build initial 'normal' tables
+	for (int i = 2; i <= max_order; i++ ) {
+		// set up current order table
+		contexts[i] = new table;
+		// build forward links
+		if ( i < max_order ) {
+			contexts[i]->links = std::vector<table*>(max_context);
+		}
+		contexts[ i - 1 ]->links[ 0 ] = contexts[ i ];
+	}
+}
+
+
+/* -----------------------------------------------
+	model class destructor - recursive cleanup of memory is done here
+	----------------------------------------------- */
+	
+model_b::~model_b()
+{
+	// clean up each 'normal' table
+	delete contexts[1];
+	
+	// clean up null table
+	delete contexts[0];
+}
+
+
+/* -----------------------------------------------
+	updates statistics for a specific symbol / resets to highest order
+	----------------------------------------------- */
+	
+void model_b::update_model( int symbol )
+{
+	// use -1 if you just want to reset without updating statistics
+	
+	table* context = contexts[ max_order ];
+	
+	// only contexts, that were actually used to encode
+	// the symbol get their counts updated
+	if ( ( symbol >= 0 ) && ( max_order >= 0 ) ) {
+		// update count for specific symbol & scale
+		context->counts[ symbol ]++;
+		context->scale++;
+		// if counts for that symbol have gone above the maximum count
+		// the table has to be resized (scale factor 2)
+		if ( context->counts[ symbol ] >= max_count )
+			context->rescale_table();
+	}
+}
+
+
+/* -----------------------------------------------
+	shift in one context (max no of contexts is max_c)
+	----------------------------------------------- */
+	
+void model_b::shift_context( int c )
+{
+	// shifting is not possible if max_order is below 1
+	// or context index is negative
+	if ( (max_order < 2 ) || ( c < 0 ) ) return;
+	
+	// shift each orders' context
+	for (int i = max_order; i > 1; i-- ) {
+		// this is the new current order context
+		table* context = contexts[ i - 1 ]->links[ c ];
+		
+		// check if context exists, build if needed
+		if ( context == nullptr ) {
+			// reserve memory for next table
+			context = new table;		
+			// finished here if this is a max order context
+			if ( i < max_order) {
+				// build links to higher order tables otherwise
+				context->links.resize(max_context);
+			}
+			// put context to its right place
+			contexts[ i - 1 ]->links[ c ] = context;
+		}
+		
+		// switch context
+		contexts[ i ] = context;
+	}
+}
+
+
+/* -----------------------------------------------
+	Flushes the entire model by calling rescale_table on all contexts.
+	----------------------------------------------- */
+	
+void model_b::flush_model()
+{
+	contexts[1]->recursive_flush();
+}
+
+
+/* -----------------------------------------------
+	converts an int to a symbol, needed only when encoding
+	----------------------------------------------- */
+	
+int model_b::convert_int_to_symbol( int c, symbol *s )
+{
+	table* context = contexts[ max_order ];
+	
+	// check if counts are available
+	context->check_counts();
+	
+	// finding the scale is easy
+	s->scale = context->scale;
+	
+	// return high and low count for current symbol
+	if ( c == 0 ) { // if 0 is to be encoded
+		s->low_count  = uint32_t(0);
+		s->high_count = context->counts[ 0 ];
+	}
+	else { // if 1 is to be encoded
+		s->low_count  = context->counts[ 0 ];
+		s->high_count = context->scale;
+	}
+	
+	return 1;
+}
+
+
+/* -----------------------------------------------
+	returns the current context scale needed only when decoding
+	----------------------------------------------- */
+	
+void model_b::get_symbol_scale( symbol *s )
+{
+	table* context = contexts[ max_order ];
+	
+	// check if counts are available
+	context->check_counts();
+	
+	// getting the scale is easy
+	s->scale = context->scale;
+}
+
+
+/* -----------------------------------------------
+	converts a count to an int, called after get_symbol_scale
+	----------------------------------------------- */
+	
+int model_b::convert_symbol_to_int(uint32_t count, symbol *s)
+{
+	table* context = contexts[ max_order ];
+	auto counts0 = context->counts[ 0 ];
+	
+	// set up the current symbol
+	if ( count < counts0 ) {
+		s->low_count  = uint32_t(0);
+		s->high_count = counts0;
+		return 0;
+	}
+	else {
+		s->low_count  = counts0;
+		s->high_count = s->scale;
+		return 1;
+	}
+}
diff --git a/software/packJPG_library/source/aricoder.h b/software/packJPG_library/source/aricoder.h
new file mode 100644
index 0000000..826ebe3
--- /dev/null
+++ b/software/packJPG_library/source/aricoder.h
@@ -0,0 +1,364 @@
+#ifndef ARICODER_H
+#define ARICODER_H
+
+#include "bitops.h"
+
+#include <algorithm>
+#include <cstdint>
+#include <memory>
+#include <vector>
+
+// defines for coder
+constexpr uint32_t CODER_USE_BITS = 31; // Must never be above 31.
+constexpr uint32_t CODER_LIMIT100 = uint32_t(1 << CODER_USE_BITS);
+constexpr uint32_t CODER_LIMIT025 = CODER_LIMIT100 / 4;
+constexpr uint32_t CODER_LIMIT050 = (CODER_LIMIT100 / 4) * 2;
+constexpr uint32_t CODER_LIMIT075 = (CODER_LIMIT100 / 4) * 3;
+constexpr uint32_t CODER_MAXSCALE = CODER_LIMIT025 - 1;
+constexpr uint32_t ESCAPE_SYMBOL = CODER_LIMIT025;
+
+// symbol struct, used in arithmetic coding
+struct symbol {
+	uint32_t low_count;
+	uint32_t high_count;
+	uint32_t scale;
+};
+
+// table struct, used in in statistical models,
+// holding all info needed for one context
+struct table {
+	// counts for each symbol contained in the table
+	std::vector<uint16_t> counts;
+	// links to higher order contexts
+	std::vector<table*> links;
+	// accumulated counts
+	uint32_t scale = uint32_t(0);
+
+	/* -----------------------------------------------
+	Recursively deletes all the tables pointed to in links.
+	----------------------------------------------- */
+	~table() {
+		for (auto& link : links) {
+			if (link != nullptr) {
+				delete link;
+			}
+		}
+	}
+
+	/* -----------------------------------------------
+	Checks if counts exist, creating it if it does not.
+	----------------------------------------------- */
+	inline void check_counts() {
+		// check if counts are available
+		if (counts.empty()) {
+			// setup counts for current table
+			counts.resize(2, uint16_t(1));
+			// set scale
+			scale = uint32_t(2);
+		}
+	}
+
+	/* -----------------------------------------------
+	Resizes the table by rightshifting each count by 1.
+	----------------------------------------------- */
+	inline void rescale_table() {
+		// Do nothing if counts is not set:
+		if (!counts.empty()) {
+			// Scale the table by bitshifting each count, be careful not to set any count zero:
+			counts[0] = std::max(uint16_t(1), uint16_t(counts[0] >> 1));
+			counts[1] = std::max(uint16_t(1), uint16_t(counts[1] >> 1));
+			scale = counts[0] + counts[1];
+		}
+	}
+
+	/* -----------------------------------------------
+	Recursively runs rescale_table on this and all linked contexts.
+	----------------------------------------------- */
+	inline void recursive_flush() {
+		for (auto& link : links) {
+			if (link != nullptr) {
+				link->recursive_flush();
+			}
+		}
+		// rescale specific table
+		rescale_table();
+	}
+};
+
+// special table struct, used in in model_s,
+// holding additional info for a speedier 'totalize_table'
+struct table_s {
+	// counts for each symbol contained in the table
+	std::vector<uint16_t> counts;
+	// links to higher order contexts
+	std::vector<table_s*> links;
+	// speedup info
+	uint16_t max_count = uint16_t(0);
+	uint16_t max_symbol = uint16_t(0);
+
+	/* -----------------------------------------------
+	Recursively deletes all the tables pointed to in links.
+	----------------------------------------------- */
+	~table_s() {
+		for (auto& link : links) {
+			if (link != nullptr) {
+				delete link;
+			}
+		}
+	}
+
+	/* -----------------------------------------------
+	Resizes the table by rightshifting each count by 1.
+	----------------------------------------------- */
+	inline void rescale_table() {
+		// Nothing to do if counts has not been set.
+		if (counts.empty()) return;
+
+		// now scale the table by bitshifting each count
+		int lst_symbol = max_symbol;
+		int i;
+		for (i = 0; i < lst_symbol; i++) {
+			counts[i] >>= 1; // Counts will not become negative since it is an unsigned type.
+		}
+
+		// also rescale tables max count
+		max_count >>= 1;
+
+		// seek for new last symbol
+		for (i = lst_symbol - 1; i >= 0; i--) {
+			if (counts[i] > 0) {
+				break;
+			}
+		}
+		max_symbol = i + 1;
+	}
+
+	/* -----------------------------------------------
+	Recursively runs rescale_table on this and all linked contexts.
+	----------------------------------------------- */
+	inline void recursive_flush() {
+		for (auto& link : links) {
+			if (link != nullptr) {
+				link->recursive_flush();
+			}
+		}
+
+		// rescale specific table
+		rescale_table();
+	}
+};
+
+class ArithmeticBitWriter {
+public:
+	template <std::uint8_t bit>
+	void write_bit();
+
+	void write_n_zero_bits(std::size_t n);
+
+	void write_n_one_bits(std::size_t n);
+
+	void pad();
+
+	std::vector<std::uint8_t> get_data() const;
+
+
+private:
+	std::vector<std::uint8_t> data_;
+	std::uint8_t curr_byte_ = 0;
+	std::size_t curr_bit_ = 0;
+};
+
+
+/* -----------------------------------------------
+	class for arithmetic coding of data to/from iostream
+	----------------------------------------------- */
+
+class ArithmeticEncoder
+{
+	public:
+    ArithmeticEncoder(Writer& writer);
+	~ArithmeticEncoder();
+	void encode( symbol* s );
+
+    void finalize();
+	
+	private:
+	
+	// i/o variables
+    
+    bool finalized = false;
+    Writer& writer_;
+    std::unique_ptr<ArithmeticBitWriter> bitwriter_ = std::make_unique<ArithmeticBitWriter>();
+	
+	// arithmetic coding variables
+	unsigned int ccode = 0;
+	unsigned int clow = 0;
+	unsigned int chigh = CODER_LIMIT100 - 1;
+	unsigned int cstep = 0;
+	unsigned int nrbits = 0;
+};
+
+class ArithmeticDecoder {
+	public:
+    ArithmeticDecoder(Reader& reader);
+	~ArithmeticDecoder() {}
+	unsigned int decode_count( symbol* s );
+	void decode( symbol* s );
+	
+	private:
+	unsigned char read_bit();
+	
+	// i/o variables
+    Reader& reader_;
+	unsigned char bbyte = 0;
+	unsigned char cbit = 0;
+	
+	// arithmetic coding variables
+	unsigned int ccode = 0;
+	unsigned int clow = 0;
+	unsigned int chigh = CODER_LIMIT100 - 1;
+	unsigned int cstep = 0;
+};
+
+
+/* -----------------------------------------------
+	universal statistical model for arithmetic coding
+	----------------------------------------------- */
+	
+class model_s
+{	
+	public:
+	
+	model_s( int max_s, int max_c, int max_o, int c_lim );
+	~model_s();
+	
+	void update_model( int symbol );
+	void shift_context( int c );
+	void flush_model();
+	void exclude_symbols(int c);
+	
+	int  convert_int_to_symbol( int c, symbol *s );
+	void get_symbol_scale( symbol *s );
+	int  convert_symbol_to_int(uint32_t count, symbol *s);
+	
+	private:
+
+	inline void totalize_table(table_s* context);
+
+	const int max_symbol;
+	const int max_context;
+	const int max_order;
+	const int max_count;
+
+	int current_order;
+	int sb0_count;
+	
+	std::vector<uint32_t> totals;
+	bool* scoreboard;
+	std::vector<table_s*> contexts;
+};
+
+
+/* -----------------------------------------------
+	binary statistical model for arithmetic coding
+	----------------------------------------------- */
+	
+class model_b
+{	
+	public:
+	
+	model_b( int max_c, int max_o, int c_lim );
+	~model_b();
+	
+	void update_model( int symbol );
+	void shift_context( int c );
+	void flush_model();
+	
+	int  convert_int_to_symbol( int c, symbol *s );
+	void get_symbol_scale( symbol *s );
+	int  convert_symbol_to_int(uint32_t count, symbol *s);	
+	
+	private:
+	
+	const int max_context;
+	const int max_order;
+	const int max_count;
+	
+	std::vector<table*> contexts;
+};
+
+// Base case for shifting an arbitrary number of contexts into the model.
+template <typename M>
+static void shift_model(M) {}
+
+// Shift an arbitrary number of contexts into the model (at most max_c contexts).
+template <typename M, typename C, typename... Cargs>
+static void shift_model(M model, C context, Cargs ... contextList) {
+	model->shift_context(context);
+	shift_model(model, contextList...);
+}
+
+/* -----------------------------------------------
+	generic model_s encoder function
+	----------------------------------------------- */
+static inline void encode_ari( ArithmeticEncoder* encoder, model_s* model, int c )
+{
+	symbol s;
+	int esc;
+	
+	do {		
+		esc = model->convert_int_to_symbol( c, &s );
+		encoder->encode( &s );
+	} while ( esc );
+	model->update_model( c );
+}
+
+/* -----------------------------------------------
+	generic model_s decoder function
+	----------------------------------------------- */	
+static inline int decode_ari( ArithmeticDecoder* decoder, model_s* model )
+{
+	symbol s;
+	uint32_t count;
+	int c;
+	
+	do{
+		model->get_symbol_scale( &s );
+		count = decoder->decode_count( &s );
+		c = model->convert_symbol_to_int( count, &s );
+		decoder->decode( &s );	
+	} while ( c == ESCAPE_SYMBOL );
+	model->update_model( c );
+	
+	return c;
+}
+
+/* -----------------------------------------------
+	generic model_b encoder function
+	----------------------------------------------- */	
+static inline void encode_ari( ArithmeticEncoder* encoder, model_b* model, int c )
+{
+	symbol s;
+	
+	model->convert_int_to_symbol( c, &s );
+	encoder->encode( &s );
+	model->update_model( c );
+}
+
+/* -----------------------------------------------
+	generic model_b decoder function
+	----------------------------------------------- */	
+static inline int decode_ari( ArithmeticDecoder* decoder, model_b* model )
+{
+	symbol s;
+	
+	model->get_symbol_scale( &s );
+	uint32_t count = decoder->decode_count( &s );
+	int c = model->convert_symbol_to_int( count, &s );
+	decoder->decode( &s );	
+	model->update_model( c );
+	
+	return c;
+}
+
+#endif
diff --git a/software/packJPG_library/source/bitops.cpp b/software/packJPG_library/source/bitops.cpp
new file mode 100644
index 0000000..2296cb2
--- /dev/null
+++ b/software/packJPG_library/source/bitops.cpp
@@ -0,0 +1,639 @@
+/*
+This file contains special classes for bitwise
+reading and writing of arrays
+*/
+
+#include "bitops.h"
+
+#include <algorithm>
+#include <array>
+#include <cstdio>
+#include <cstdlib>
+#include <experimental/filesystem>
+#include <fstream>
+#include <stdexcept>
+
+#if defined(_WIN32) || defined(WIN32)
+#include <fcntl.h>
+#include <io.h>
+#endif
+
+
+/* -----------------------------------------------
+	constructor for BitReader class
+	----------------------------------------------- */	
+
+BitReader::BitReader( unsigned char* array, int size ) {
+	data = array;
+	lbyte = size;	
+}
+
+/* -----------------------------------------------
+	destructor for BitReader class
+	----------------------------------------------- */	
+
+BitReader::~BitReader() {}
+
+/* -----------------------------------------------
+	reads n bits from BitReader
+	----------------------------------------------- */	
+
+unsigned int BitReader::read( int nbits ) {
+	unsigned int retval = 0;
+	
+	if ( eof()) {
+		peof_ += nbits;
+		return 0;
+	}
+	
+	while ( nbits >= cbit ) {
+		nbits -= cbit;
+		retval |= ( RBITS( data[cbyte], cbit ) << nbits );		
+        update_curr_byte();
+        if (eof()) {
+            peof_ = nbits;
+            return retval;
+        }
+	}
+	
+	if ( nbits > 0 ) {		
+		retval |= ( MBITS( data[cbyte], cbit, (cbit-nbits) ) );
+		cbit -= nbits;		
+	}
+	
+	return retval;
+}
+
+/* -----------------------------------------------
+	reads one bit from BitReader
+	----------------------------------------------- */	
+	
+unsigned char BitReader::read_bit() {
+	if (eof()) {
+		peof_++;
+		return 0;
+	}
+	
+	// read one bit
+	unsigned char bit = BITN( data[cbyte], --cbit );
+	if ( cbit == 0 ) {
+        update_curr_byte();
+	} 
+	
+	return bit;
+}
+
+void BitReader::update_curr_byte() {
+    cbyte++;
+    eof_ = cbyte == lbyte;
+    cbit = 8;
+}
+
+/* -----------------------------------------------
+	to skip padding from current byte
+	----------------------------------------------- */
+
+unsigned char BitReader::unpad( unsigned char fillbit ) {
+	if ( ( cbit == 8 ) || eof()) {
+        return fillbit;
+    } else {
+		fillbit = read( 1 );
+		while ( cbit != 8 ) {
+            read( 1 );
+        }
+	}
+	
+	return fillbit;
+}
+
+/* -----------------------------------------------
+	get current position in array
+	----------------------------------------------- */	
+
+int BitReader::getpos() {
+	return cbyte;
+}
+
+/* -----------------------------------------------
+	get current bit position
+	----------------------------------------------- */
+	
+int BitReader::getbitp() {
+	return cbit;
+}
+
+/* -----------------------------------------------
+	set byte and bit position
+	----------------------------------------------- */
+	
+void BitReader::setpos( int pbyte, int pbit ) {
+	if ( pbyte < lbyte ) {
+		// reset eof
+		eof_ = false;
+		// set positions
+		cbyte = pbyte;
+		cbit = pbit;
+	} else {
+		// set eof
+		eof_ = true;
+		// set positions
+		cbyte = lbyte;
+		cbit = 8;
+		peof_ = ( ( pbyte - lbyte ) * 8 ) + 8 - pbit;
+	}	
+}
+
+/* -----------------------------------------------
+	rewind n bits
+	----------------------------------------------- */
+	
+void BitReader::rewind_bits( int nbits ) {
+	if (eof()) {
+		if (nbits > peof_) {
+			nbits -= peof_;
+			peof_ = 0;
+		} else {
+			peof_ -= nbits;
+			return;
+		}
+		eof_ = false;
+	}
+
+	cbit += nbits;
+	cbyte -= cbit / 8;
+	cbit = cbit % 8;
+	if ( cbyte < 0 ) {
+		cbyte = 0;
+		cbit = 8;
+	}
+}
+
+bool BitReader::eof() {
+	return eof_;
+}
+
+int BitReader::peof() {
+	return peof_;
+}
+
+BitWriter::BitWriter(std::uint8_t padbit) : padbit_(padbit) {}
+
+BitWriter::~BitWriter() {}
+
+std::uint32_t rbits32(std::uint32_t val, std::size_t n) {
+    return val & (0xFFFFFFFF >> (32 - n));
+}
+
+std::uint32_t mbits32(std::uint32_t val, std::size_t l, std::size_t r) {
+    return rbits32(val, l) >> r;
+}
+
+void BitWriter::write_u16(std::uint16_t val, std::size_t num_bits) {
+	while (num_bits >= curr_bit_) {
+		curr_byte_ |= mbits32(val, num_bits, num_bits - curr_bit_);
+		num_bits -= curr_bit_;
+		write_curr_byte();
+	}
+
+	if (num_bits > 0) {
+		curr_byte_ |= rbits32(val, num_bits) << (curr_bit_ - num_bits);
+		curr_bit_ -= num_bits;
+	}
+}
+
+void BitWriter::write_bit(std::uint8_t bit) {
+	curr_bit_--;
+	curr_byte_ |= bit << curr_bit_;
+	if (curr_bit_ == 0) {
+		write_curr_byte();
+	}
+}
+
+void BitWriter::write_curr_byte() {
+	bytes_.emplace_back(curr_byte_);
+	curr_byte_ = 0;
+	curr_bit_ = 8;
+}
+
+void BitWriter::pad() {
+	while (curr_bit_ < 8) {
+		write_bit(padbit_);
+	}
+}
+
+std::vector<std::uint8_t> BitWriter::get_bytes() {
+	pad(); // Pad the last bits of the current byte before returning the written bytes.
+	return bytes_;
+}
+
+unsigned char* BitWriter::get_c_bytes() {
+    pad(); // Pad the last bits of the current byte before returning the written bytes.
+    unsigned char* c_bytes = new unsigned char[bytes_.size()];
+    std::copy(std::begin(bytes_), std::end(bytes_), c_bytes);
+    return c_bytes;
+}
+
+std::size_t BitWriter::num_bytes_written() const {
+	return bytes_.size();
+}
+
+unsigned char* Reader::get_c_data() {
+    const auto data = this->get_data();
+    auto c_data_copy = (unsigned char*)std::malloc(data.size() * sizeof data[0]);
+    if (c_data_copy == nullptr) {
+        return nullptr;
+    }
+
+    std::copy(std::begin(data), std::end(data), c_data_copy);
+    return c_data_copy;
+}
+
+MemoryReader::MemoryReader(const std::vector<std::uint8_t>& bytes) :
+	data_(bytes),
+	cbyte_(std::begin(data_)) {
+}
+
+MemoryReader::MemoryReader(const std::uint8_t* bytes, std::size_t size) :
+    data_(bytes, bytes + size),
+    cbyte_(std::begin(data_)) {
+}
+
+std::size_t MemoryReader::read(std::uint8_t* to, std::size_t num_to_read) {
+	if (num_to_read == 0 || to == nullptr) {
+		return 0;
+	}
+	auto numAvailable = std::distance(cbyte_, std::end(data_));
+	auto numRead = std::min(static_cast<std::size_t>(numAvailable), num_to_read);
+	auto end = std::next(cbyte_, numRead);
+	std::copy(cbyte_, end, to);
+	cbyte_ = end;
+	return numRead;
+}
+
+std::size_t MemoryReader::read(std::vector<std::uint8_t>& into, std::size_t n, std::size_t offset) {
+	const std::size_t num_available = get_size() - num_bytes_read(); // The number of bytes in the reader not yet read.
+	const std::size_t num_to_read = std::min(n, num_available); // How many bytes will be read.
+	if (into.size() < num_to_read + offset) {
+		into.resize(num_to_read + offset);
+	}
+
+	const auto end = std::next(cbyte_, num_to_read);
+	const auto write_start = std::next(std::begin(into), offset);
+	std::copy(cbyte_, end, write_start);
+	cbyte_ = end;
+	return num_to_read;
+}
+
+std::uint8_t MemoryReader::read_byte() {
+	if (end_of_reader()) {
+		throw std::runtime_error("No bytes left to read");
+	} else {
+		std::uint8_t the_byte = *cbyte_;
+		++cbyte_;
+		return the_byte;
+	}
+}
+
+bool MemoryReader::read_byte(std::uint8_t* byte) {
+	if (end_of_reader()) {
+		return false;
+	} else {
+		*byte = *cbyte_;
+		++cbyte_;
+		return true;
+	}
+}
+
+void MemoryReader::skip(std::size_t n) {
+	auto num_to_skip = std::min(n, std::size_t(std::distance(cbyte_, std::end(data_))));
+	cbyte_ += num_to_skip;
+}
+
+void MemoryReader::rewind_bytes(std::size_t n) {
+	auto num_to_rewind = std::min(n, std::size_t(std::distance(std::begin(data_), cbyte_)));
+	auto new_pos = std::distance(std::begin(data_), cbyte_) - num_to_rewind;
+	cbyte_ = std::next(std::begin(data_), new_pos);
+}
+
+void MemoryReader::rewind() {
+	cbyte_ = std::begin(data_);
+}
+
+std::size_t MemoryReader::num_bytes_read() {
+	return std::distance(std::begin(data_), cbyte_);
+}
+
+std::size_t MemoryReader::get_size() {
+	return data_.size();
+}
+
+std::vector<std::uint8_t> MemoryReader::get_data() {
+	return data_;
+}
+
+bool MemoryReader::error() {
+	return false;
+}
+
+bool MemoryReader::end_of_reader() {
+	return cbyte_ == std::end(data_);
+}
+
+unsigned char* Writer::get_c_data() {
+    try {
+        const auto data = this->get_data();
+        auto c_data_copy = (unsigned char*)std::malloc(data.size() * sizeof data[0]);
+        if (c_data_copy == nullptr) {
+            return nullptr;
+        }
+
+        std::copy(std::begin(data), std::end(data), c_data_copy);
+        return c_data_copy;
+    } catch (const std::exception&) {
+        return nullptr;
+    }
+}
+
+MemoryWriter::MemoryWriter() {}
+
+std::size_t MemoryWriter::write(const std::uint8_t* from, std::size_t n) {
+	data_.insert(std::end(data_), from, from + n);
+	return n;
+}
+
+std::size_t MemoryWriter::write(const std::vector<std::uint8_t>& bytes) {
+	data_.insert(std::end(data_), std::begin(bytes), std::end(bytes));
+	return bytes.size();
+}
+
+std::size_t MemoryWriter::write(const std::array<std::uint8_t, 2>& bytes) {
+	data_.insert(std::end(data_), std::begin(bytes), std::end(bytes));
+	return bytes.size();
+}
+
+bool MemoryWriter::write_byte(std::uint8_t byte) {
+	data_.emplace_back(byte);
+	return true;
+}
+
+std::vector<std::uint8_t> MemoryWriter::get_data() {
+	return data_;
+}
+
+void MemoryWriter::reset() {
+	data_.resize(0);
+}
+
+std::size_t MemoryWriter::num_bytes_written() {
+	return data_.size();
+}
+
+bool MemoryWriter::error() {
+	return false;
+}
+
+FileWriter::FileWriter(const std::string& file_path) : file_path_(file_path) {
+	fptr_ = std::fopen(file_path.c_str(), "wb");
+	if (fptr_ != nullptr) {
+		file_buffer_.reserve(32768);
+		std::setvbuf(fptr_, file_buffer_.data(), _IOFBF, file_buffer_.capacity());
+	} else {
+		throw std::runtime_error("Unable to open " + file_path_ + " for writing.");
+	}
+
+}
+
+FileWriter::~FileWriter() {
+	if (fptr_ != nullptr) {
+		std::fflush(fptr_);
+		std::fclose(fptr_);
+	}
+}
+
+std::size_t FileWriter::write(const std::uint8_t* from, std::size_t n) {
+	return std::fwrite(from, sizeof from[0], n, fptr_);
+}
+
+std::size_t FileWriter::write(const std::vector<std::uint8_t>& bytes) {
+	return write(bytes.data(), bytes.size());
+}
+
+std::size_t FileWriter::write(const std::array<std::uint8_t, 2>& bytes) {
+	return write(bytes.data(), 2);
+}
+
+bool FileWriter::write_byte(std::uint8_t byte) {
+	return std::fputc(byte, fptr_) == byte;
+}
+
+std::vector<std::uint8_t> FileWriter::get_data() {
+	std::fflush(fptr_);
+	if (std::ifstream is{ file_path_, std::ios::binary | std::ios::ate }) {
+		const auto size = is.tellg();
+		std::vector<std::uint8_t> data_copy(size);
+		is.seekg(0);
+		if (is.read(reinterpret_cast<char*>(data_copy.data()), size)) {
+			return data_copy;
+		} else {
+			throw std::runtime_error("FileWriter::get_data: unable to read bytes from file.");
+		}
+	} else {
+		throw std::runtime_error("FileWriter::get_data: unable to open read stream for file.");
+	}
+}
+
+void FileWriter::reset() {
+	std::fseek(fptr_, 0, SEEK_SET);
+}
+
+std::size_t FileWriter::num_bytes_written() {
+	std::fflush(fptr_);
+	return std::experimental::filesystem::file_size(file_path_);
+}
+
+bool FileWriter::error() {
+	return fptr_ == nullptr || std::ferror(fptr_);
+}
+
+StreamWriter::StreamWriter() {
+	writer_ = std::make_unique<MemoryWriter>();
+}
+
+StreamWriter::~StreamWriter() {
+#if defined(_WIN32) || defined(WIN32)
+	const int result = _setmode(_fileno(stdout), _O_BINARY);
+	if (result == -1) {
+		return;
+	}
+#endif
+	const auto& data = writer_->get_data();
+	fwrite(data.data(), sizeof data[0], data.size(), stdout);
+}
+
+std::size_t StreamWriter::write(const std::uint8_t* from, std::size_t n) {
+	return writer_->write(from, n);
+}
+
+std::size_t StreamWriter::write(const std::vector<std::uint8_t>& bytes) {
+	return writer_->write(bytes);
+}
+
+std::size_t StreamWriter::write(const std::array<std::uint8_t, 2>& bytes) {
+	return writer_->write(bytes);
+}
+
+bool StreamWriter::write_byte(std::uint8_t byte) {
+	return writer_->write_byte(byte);
+}
+
+std::vector<std::uint8_t> StreamWriter::get_data() {
+	return writer_->get_data();
+}
+
+void StreamWriter::reset() {
+	writer_->reset();
+}
+
+std::size_t StreamWriter::num_bytes_written() {
+	return writer_->num_bytes_written();
+}
+
+bool StreamWriter::error() {
+	return writer_->error();
+}
+
+
+
+FileReader::FileReader(const std::string& file_path) {
+	if (std::ifstream is{ file_path, std::ios::binary | std::ios::ate }) {
+		const auto size = is.tellg();
+		std::vector<std::uint8_t> data(size);
+		is.seekg(0);
+		if (is.read(reinterpret_cast<char*>(data.data()), size)) {
+			reader_ = std::make_unique<MemoryReader>(data);
+		} else {
+			throw std::runtime_error("FileReader: unable to read bytes from " + file_path);
+		}
+	} else {
+		throw std::runtime_error("FileReader: unable to open read stream for " + file_path);
+	}
+}
+
+FileReader::~FileReader() {}
+
+std::size_t FileReader::read(std::uint8_t* to, std::size_t num_to_read) {
+	return reader_->read(to, num_to_read);
+}
+
+std::size_t FileReader::read(std::vector<std::uint8_t>& into, std::size_t num_to_read, std::size_t offset) {
+	return reader_->read(into, num_to_read, offset);
+}
+
+std::uint8_t FileReader::read_byte() {
+	return reader_->read_byte();
+}
+
+bool FileReader::read_byte(std::uint8_t* to) {
+	return reader_->read_byte(to);
+}
+
+void FileReader::skip(std::size_t n) {
+	return reader_->skip(n);
+}
+
+void FileReader::rewind_bytes(std::size_t n) {
+	return reader_->rewind_bytes(n);
+}
+
+void FileReader::rewind() {
+	reader_->rewind();
+}
+
+std::size_t FileReader::num_bytes_read() {
+	return reader_->num_bytes_read();
+}
+
+std::size_t FileReader::get_size() {
+	return reader_->get_size();
+}
+
+std::vector<std::uint8_t> FileReader::get_data() {
+	return reader_->get_data();
+}
+
+bool FileReader::error() {
+	return reader_->error();
+}
+
+bool FileReader::end_of_reader() {
+	return reader_->end_of_reader();
+}
+
+StreamReader::StreamReader() {
+#if defined(_WIN32) || defined(WIN32)
+	const int result = _setmode(_fileno(stdin), _O_BINARY);
+	if (result == -1) {
+		throw std::runtime_error("Unable to set mode for stdin");
+	}
+#endif
+	// read whole stream into memory buffer
+	std::vector<std::uint8_t> stream_data;
+	constexpr auto buffer_capacity = 1024 * 1024;
+	std::vector<std::uint8_t> buffer(buffer_capacity);
+
+	auto bytes_read = std::fread(buffer.data(), sizeof buffer[0], buffer_capacity, stdin);
+	while (bytes_read > 0) {
+		stream_data.insert(std::end(stream_data), std::begin(buffer), std::begin(buffer) + bytes_read);
+		bytes_read = std::fread(buffer.data(), sizeof buffer[0], buffer_capacity, stdin);
+	}
+
+	reader_ = std::make_unique<MemoryReader>(stream_data);
+}
+
+std::size_t StreamReader::read(std::uint8_t* to, std::size_t num_to_read) {
+	return reader_->read(to, num_to_read);
+}
+
+std::size_t StreamReader::read(std::vector<std::uint8_t>& into, std::size_t num_to_read, std::size_t offset) {
+	return reader_->read(into, num_to_read, offset);
+}
+
+std::uint8_t StreamReader::read_byte() {
+	return reader_->read_byte();
+}
+
+bool StreamReader::read_byte(std::uint8_t* to) {
+	return reader_->read_byte(to);
+}
+
+void StreamReader::skip(std::size_t n) {
+	reader_->skip(n);
+}
+
+void StreamReader::rewind_bytes(std::size_t n) {
+	reader_->rewind_bytes(n);
+}
+
+void StreamReader::rewind() {
+	reader_->rewind();
+}
+
+std::size_t StreamReader::num_bytes_read() {
+	return reader_->num_bytes_read();
+}
+
+std::size_t StreamReader::get_size() {
+	return reader_->get_size();
+}
+
+std::vector<std::uint8_t> StreamReader::get_data() {
+	return reader_->get_data();
+}
+
+bool StreamReader::error() {
+	return reader_->error();
+}
+
+bool StreamReader::end_of_reader() {
+	return reader_->end_of_reader();
+}
diff --git a/software/packJPG_library/source/bitops.h b/software/packJPG_library/source/bitops.h
new file mode 100644
index 0000000..73ba2d3
--- /dev/null
+++ b/software/packJPG_library/source/bitops.h
@@ -0,0 +1,310 @@
+#ifndef BITOPS_H
+#define BITOPS_H
+
+#define RBITS( c, n )		( c & ( 0xFF >> (8 - n) ) )
+#define LBITS( c, n )		( c >> (8 - n) )
+#define MBITS( c, l, r )	( RBITS( c,l ) >> r )
+#define BITN( c, n )		( (c >> n) & 0x1 )
+#define BITLEN( l, v )		for ( l = 0; ( v >> l ) > 0; l++ )
+#define FDIV2( v, p )		( ( v < 0 ) ? -( (-v) >> p ) : ( v >> p ) )
+
+#include <cstdint>
+#include <memory>
+#include <string>
+#include <vector>
+
+	
+/* -----------------------------------------------
+	class to read arrays bitwise
+	----------------------------------------------- */
+class BitReader {
+public:
+	BitReader( unsigned char* array, int size );
+	~BitReader();	
+	unsigned int read( int nbits );
+	unsigned char read_bit();
+	unsigned char unpad( unsigned char fillbit );
+	int getpos();
+	int getbitp();
+	void setpos( int pbyte, int pbit );
+	void rewind_bits( int nbits );
+	bool eof();
+	int peof();
+	
+private:
+    void update_curr_byte();
+
+	unsigned char* data = nullptr;
+	int lbyte = 0;
+	int cbyte = 0;
+	int cbit = 8;
+	int peof_ = 0;
+	bool eof_ = false;
+};
+
+
+class BitWriter {
+public:
+	BitWriter(std::uint8_t padbit);
+	~BitWriter();
+	/*
+	* Writes the first (lowest) n bits from val (n is assumed to be less than or equal to 16).
+	*/
+	void write_u16(std::uint16_t val, std::size_t num_bits);
+
+	/*
+	* Writes the bit (assumed to be 0 or 1).
+	*/
+	void write_bit(std::uint8_t bit);
+	/**
+	* Fills in the remainder of the current byte using the padbit.
+	*/
+	void pad();
+    /**
+    * Returns a copy of the bytes written to the writer, padding the current byte if necessary.
+    */
+	std::vector<std::uint8_t> get_bytes();
+    /**
+    * Allocates and returns a copy of the bytes written to the writer, padding the current byte if necessary.
+    */
+    unsigned char* get_c_bytes();
+	/*
+	* Returns the number of bytes written. If a byte has not been fully written (e.g., only 5 bits have been written),
+	* then it is not counted in the number of bytes written.
+	*/
+	std::size_t num_bytes_written() const;
+private:
+	void write_curr_byte();
+
+	std::vector<std::uint8_t> bytes_;
+	const std::uint8_t padbit_; // A bit, either 0 or 1, to fill the unwritten bits of the current byte with.
+	std::uint8_t curr_byte_ = 0; // The current byte being written.
+	std::size_t curr_bit_ = 8; // The position of the next bit in the current byte.
+};
+
+class Reader {
+public:
+	Reader() {}
+
+	virtual ~Reader() {}
+
+	/*
+	* Reads the minimum of n and the number of unread bytes to the pointer.
+	*/
+	virtual std::size_t read(std::uint8_t* to, std::size_t n) = 0;
+
+	/*
+	* Reads the minimum of n and the number of unread bytes to the vector, starting at the given offset in the
+	* vector. If the destination vector is too small, it is resized. Returns the number of bytes read.
+	*/
+	virtual std::size_t read(std::vector<std::uint8_t>& into, std::size_t n, std::size_t offset = 0) = 0;
+
+	/*
+	* Returns one byte from the reader, throwing a std::runtime_error exception if there are none left to read.
+	*/
+	virtual std::uint8_t read_byte() = 0;
+
+	/*
+	* Reads one byte to the pointer, returning whether there was a byte available to writer.
+	*/
+	virtual bool read_byte(std::uint8_t* to) = 0;
+
+	/*
+	 * Skips the minimum of the n and the number of unread bytes left in the reader.
+	 */
+	virtual void skip(std::size_t n) = 0;
+
+	/*
+	 * Moves the reader back by the minimum of n and the number of bytes already read in the reader.
+	 */
+	virtual void rewind_bytes(std::size_t n) = 0;
+
+	/*
+	* Resets the number of bytes read to zero.
+	*/
+	virtual void rewind() = 0;
+
+	/*
+	* Returns the number of bytes read.
+	*/
+	virtual std::size_t num_bytes_read() = 0;
+
+	/*
+	* Returns the number of bytes in the reader.
+	*/
+	virtual std::size_t get_size() = 0;
+
+	/*
+	* Returns a copy of the data backing the reader.
+	*/
+	virtual std::vector<std::uint8_t> get_data() = 0;
+
+    unsigned char* get_c_data();
+
+	virtual bool error() = 0;
+	/*
+	* Returns whether all bytes in the reader have been read.
+	*/
+	virtual bool end_of_reader() = 0;
+};
+
+class MemoryReader : public Reader {
+public:
+	MemoryReader(const std::vector<std::uint8_t>& bytes);
+    MemoryReader(const std::uint8_t* bytes, std::size_t size);
+
+	~MemoryReader() {}
+
+	std::size_t read(std::uint8_t* to, std::size_t num_to_read) override;
+	std::size_t read(std::vector<std::uint8_t>& into, std::size_t num_to_read, std::size_t offset = 0) override;
+	std::uint8_t read_byte() override;
+	bool read_byte(std::uint8_t* to) override;
+
+	void skip(std::size_t n) override;
+	void rewind_bytes(std::size_t n) override;
+	void rewind() override;
+
+	std::size_t num_bytes_read() override;
+	std::size_t get_size() override;
+	std::vector<std::uint8_t> get_data() override;
+	bool error() override;
+	bool end_of_reader() override;
+
+private:
+	const std::vector<std::uint8_t> data_;
+	std::vector<std::uint8_t>::const_iterator cbyte_; // The position in the data of the byte being read.
+};
+
+class FileReader : public Reader {
+public:
+	FileReader(const std::string& file_path);
+	~FileReader();
+
+	std::size_t read(std::uint8_t* to, std::size_t num_to_read) override;
+	std::size_t read(std::vector<std::uint8_t>& into, std::size_t num_to_read, std::size_t offset = 0) override;
+	std::uint8_t read_byte() override;
+	bool read_byte(std::uint8_t* to) override;
+
+	void skip(std::size_t n) override;
+	void rewind_bytes(std::size_t n) override;
+	void rewind() override;
+
+	std::size_t num_bytes_read() override;
+	std::size_t get_size() override;
+	std::vector<std::uint8_t> get_data() override;
+	bool error() override;
+	bool end_of_reader() override;
+
+private:
+	std::unique_ptr<MemoryReader> reader_;
+};
+
+class StreamReader : public Reader {
+public:
+	StreamReader();
+
+	~StreamReader() {}
+
+	std::size_t read(std::uint8_t* to, std::size_t num_to_read) override;
+	std::size_t read(std::vector<std::uint8_t>& into, std::size_t num_to_read, std::size_t offset = 0) override;
+	std::uint8_t read_byte() override;
+	bool read_byte(std::uint8_t* to) override;
+
+	void skip(std::size_t n) override;
+	void rewind_bytes(std::size_t n) override;
+	void rewind() override;
+
+	std::size_t num_bytes_read() override;
+	std::size_t get_size() override;
+	std::vector<std::uint8_t> get_data() override;
+	bool error() override;
+	bool end_of_reader() override;
+
+private:
+	std::unique_ptr<MemoryReader> reader_;
+};
+
+class Writer {
+public:
+	Writer() {}
+
+	virtual ~Writer() {}
+
+	virtual std::size_t write(const std::uint8_t* from, std::size_t n) = 0;
+	virtual std::size_t write(const std::vector<std::uint8_t>& bytes) = 0;
+	virtual std::size_t write(const std::array<std::uint8_t, 2>& bytes) = 0;
+
+	virtual bool write_byte(std::uint8_t byte) = 0;
+
+	virtual std::vector<std::uint8_t> get_data() = 0;
+    unsigned char* get_c_data();
+
+	virtual void reset() = 0;
+	virtual std::size_t num_bytes_written() = 0;
+	virtual bool error() = 0;
+};
+
+class MemoryWriter : public Writer {
+public:
+	MemoryWriter();
+
+	~MemoryWriter() {}
+
+	std::size_t write(const std::uint8_t* from, std::size_t n) override;
+	std::size_t write(const std::vector<std::uint8_t>& bytes) override;
+	std::size_t write(const std::array<std::uint8_t, 2>& bytes) override;
+	bool write_byte(std::uint8_t byte) override;
+
+	std::vector<std::uint8_t> get_data() override;
+
+	void reset() override;
+	std::size_t num_bytes_written() override;
+	bool error() override;
+
+private:
+	std::vector<std::uint8_t> data_;
+};
+
+class FileWriter : public Writer {
+public:
+	FileWriter(const std::string& file_path);
+	~FileWriter();
+
+	std::size_t write(const std::uint8_t* from, std::size_t n) override;
+	std::size_t write(const std::vector<std::uint8_t>& bytes) override;
+	std::size_t write(const std::array<std::uint8_t, 2>& bytes) override;
+	bool write_byte(std::uint8_t byte) override;
+
+	std::vector<std::uint8_t> get_data() override;
+
+	void reset() override;
+	std::size_t num_bytes_written() override;
+	bool error() override;
+
+private:
+	FILE* fptr_ = nullptr;
+	std::vector<char> file_buffer_; // Used to replace the default file buffer for reads/writes to improve performance.
+	const std::string file_path_;
+};
+
+class StreamWriter : public Writer {
+public:
+	StreamWriter();
+	~StreamWriter();
+
+	std::size_t write(const std::uint8_t* from, std::size_t n) override;
+	std::size_t write(const std::vector<std::uint8_t>& bytes) override;
+	std::size_t write(const std::array<std::uint8_t, 2>& bytes) override;
+	bool write_byte(std::uint8_t byte) override;
+
+	std::vector<std::uint8_t> get_data() override;
+
+	void reset() override;
+	std::size_t num_bytes_written() override;
+	bool error() override;
+
+private:
+	std::unique_ptr<MemoryWriter> writer_;
+};
+
+#endif
diff --git a/software/packJPG_library/source/dct8x8.h b/software/packJPG_library/source/dct8x8.h
new file mode 100644
index 0000000..25fcead
--- /dev/null
+++ b/software/packJPG_library/source/dct8x8.h
@@ -0,0 +1,1166 @@
+#define DCT_RSC_FACTOR		8192
+#define DCT_RESCALE( v )	( ( ( v > 0 ) ? ( v + (DCT_RSC_FACTOR/2) ) : ( v - (DCT_RSC_FACTOR/2) ) ) / DCT_RSC_FACTOR )
+
+
+// precalculated int values for 8x8 IDCT, multplied by 8192
+const int icos_idct_8x8[ 4096 ] =
+{
+	 1024,  1420,  1338,  1204,  1024,   805,   554,   283, 
+	 1420,  1970,  1856,  1670,  1420,  1116,   769,   392, 
+	 1338,  1856,  1748,  1573,  1338,  1051,   724,   369, 
+	 1204,  1670,  1573,  1416,  1204,   946,   652,   332, 
+	 1024,  1420,  1338,  1204,  1024,   805,   554,   283, 
+	  805,  1116,  1051,   946,   805,   632,   435,   222, 
+	  554,   769,   724,   652,   554,   435,   300,   153, 
+	  283,   392,   369,   332,   283,   222,   153,    78, 
+	 1024,  1204,   554,  -283, -1024, -1420, -1338,  -805, 
+	 1420,  1670,   769,  -392, -1420, -1970, -1856, -1116, 
+	 1338,  1573,   724,  -369, -1338, -1856, -1748, -1051, 
+	 1204,  1416,   652,  -332, -1204, -1670, -1573,  -946, 
+	 1024,  1204,   554,  -283, -1024, -1420, -1338,  -805, 
+	  805,   946,   435,  -222,  -805, -1116, -1051,  -632, 
+	  554,   652,   300,  -153,  -554,  -769,  -724,  -435, 
+	  283,   332,   153,   -78,  -283,  -392,  -369,  -222, 
+	 1024,   805,  -554, -1420, -1024,   283,  1338,  1204, 
+	 1420,  1116,  -769, -1970, -1420,   392,  1856,  1670, 
+	 1338,  1051,  -724, -1856, -1338,   369,  1748,  1573, 
+	 1204,   946,  -652, -1670, -1204,   332,  1573,  1416, 
+	 1024,   805,  -554, -1420, -1024,   283,  1338,  1204, 
+	  805,   632,  -435, -1116,  -805,   222,  1051,   946, 
+	  554,   435,  -300,  -769,  -554,   153,   724,   652, 
+	  283,   222,  -153,  -392,  -283,    78,   369,   332, 
+	 1024,   283, -1338,  -805,  1024,  1204,  -554, -1420, 
+	 1420,   392, -1856, -1116,  1420,  1670,  -769, -1970, 
+	 1338,   369, -1748, -1051,  1338,  1573,  -724, -1856, 
+	 1204,   332, -1573,  -946,  1204,  1416,  -652, -1670, 
+	 1024,   283, -1338,  -805,  1024,  1204,  -554, -1420, 
+	  805,   222, -1051,  -632,   805,   946,  -435, -1116, 
+	  554,   153,  -724,  -435,   554,   652,  -300,  -769, 
+	  283,    78,  -369,  -222,   283,   332,  -153,  -392, 
+	 1024,  -283, -1338,   805,  1024, -1204,  -554,  1420, 
+	 1420,  -392, -1856,  1116,  1420, -1670,  -769,  1970, 
+	 1338,  -369, -1748,  1051,  1338, -1573,  -724,  1856, 
+	 1204,  -332, -1573,   946,  1204, -1416,  -652,  1670, 
+	 1024,  -283, -1338,   805,  1024, -1204,  -554,  1420, 
+	  805,  -222, -1051,   632,   805,  -946,  -435,  1116, 
+	  554,  -153,  -724,   435,   554,  -652,  -300,   769, 
+	  283,   -78,  -369,   222,   283,  -332,  -153,   392, 
+	 1024,  -805,  -554,  1420, -1024,  -283,  1338, -1204, 
+	 1420, -1116,  -769,  1970, -1420,  -392,  1856, -1670, 
+	 1338, -1051,  -724,  1856, -1338,  -369,  1748, -1573, 
+	 1204,  -946,  -652,  1670, -1204,  -332,  1573, -1416, 
+	 1024,  -805,  -554,  1420, -1024,  -283,  1338, -1204, 
+	  805,  -632,  -435,  1116,  -805,  -222,  1051,  -946, 
+	  554,  -435,  -300,   769,  -554,  -153,   724,  -652, 
+	  283,  -222,  -153,   392,  -283,   -78,   369,  -332, 
+	 1024, -1204,   554,   283, -1024,  1420, -1338,   805, 
+	 1420, -1670,   769,   392, -1420,  1970, -1856,  1116, 
+	 1338, -1573,   724,   369, -1338,  1856, -1748,  1051, 
+	 1204, -1416,   652,   332, -1204,  1670, -1573,   946, 
+	 1024, -1204,   554,   283, -1024,  1420, -1338,   805, 
+	  805,  -946,   435,   222,  -805,  1116, -1051,   632, 
+	  554,  -652,   300,   153,  -554,   769,  -724,   435, 
+	  283,  -332,   153,    78,  -283,   392,  -369,   222, 
+	 1024, -1420,  1338, -1204,  1024,  -805,   554,  -283, 
+	 1420, -1970,  1856, -1670,  1420, -1116,   769,  -392, 
+	 1338, -1856,  1748, -1573,  1338, -1051,   724,  -369, 
+	 1204, -1670,  1573, -1416,  1204,  -946,   652,  -332, 
+	 1024, -1420,  1338, -1204,  1024,  -805,   554,  -283, 
+	  805, -1116,  1051,  -946,   805,  -632,   435,  -222, 
+	  554,  -769,   724,  -652,   554,  -435,   300,  -153, 
+	  283,  -392,   369,  -332,   283,  -222,   153,   -78, 
+	 1024,  1420,  1338,  1204,  1024,   805,   554,   283, 
+	 1204,  1670,  1573,  1416,  1204,   946,   652,   332, 
+	  554,   769,   724,   652,   554,   435,   300,   153, 
+	 -283,  -392,  -369,  -332,  -283,  -222,  -153,   -78, 
+	-1024, -1420, -1338, -1204, -1024,  -805,  -554,  -283, 
+	-1420, -1970, -1856, -1670, -1420, -1116,  -769,  -392, 
+	-1338, -1856, -1748, -1573, -1338, -1051,  -724,  -369, 
+	 -805, -1116, -1051,  -946,  -805,  -632,  -435,  -222, 
+	 1024,  1204,   554,  -283, -1024, -1420, -1338,  -805, 
+	 1204,  1416,   652,  -332, -1204, -1670, -1573,  -946, 
+	  554,   652,   300,  -153,  -554,  -769,  -724,  -435, 
+	 -283,  -332,  -153,    78,   283,   392,   369,   222, 
+	-1024, -1204,  -554,   283,  1024,  1420,  1338,   805, 
+	-1420, -1670,  -769,   392,  1420,  1970,  1856,  1116, 
+	-1338, -1573,  -724,   369,  1338,  1856,  1748,  1051, 
+	 -805,  -946,  -435,   222,   805,  1116,  1051,   632, 
+	 1024,   805,  -554, -1420, -1024,   283,  1338,  1204, 
+	 1204,   946,  -652, -1670, -1204,   332,  1573,  1416, 
+	  554,   435,  -300,  -769,  -554,   153,   724,   652, 
+	 -283,  -222,   153,   392,   283,   -78,  -369,  -332, 
+	-1024,  -805,   554,  1420,  1024,  -283, -1338, -1204, 
+	-1420, -1116,   769,  1970,  1420,  -392, -1856, -1670, 
+	-1338, -1051,   724,  1856,  1338,  -369, -1748, -1573, 
+	 -805,  -632,   435,  1116,   805,  -222, -1051,  -946, 
+	 1024,   283, -1338,  -805,  1024,  1204,  -554, -1420, 
+	 1204,   332, -1573,  -946,  1204,  1416,  -652, -1670, 
+	  554,   153,  -724,  -435,   554,   652,  -300,  -769, 
+	 -283,   -78,   369,   222,  -283,  -332,   153,   392, 
+	-1024,  -283,  1338,   805, -1024, -1204,   554,  1420, 
+	-1420,  -392,  1856,  1116, -1420, -1670,   769,  1970, 
+	-1338,  -369,  1748,  1051, -1338, -1573,   724,  1856, 
+	 -805,  -222,  1051,   632,  -805,  -946,   435,  1116, 
+	 1024,  -283, -1338,   805,  1024, -1204,  -554,  1420, 
+	 1204,  -332, -1573,   946,  1204, -1416,  -652,  1670, 
+	  554,  -153,  -724,   435,   554,  -652,  -300,   769, 
+	 -283,    78,   369,  -222,  -283,   332,   153,  -392, 
+	-1024,   283,  1338,  -805, -1024,  1204,   554, -1420, 
+	-1420,   392,  1856, -1116, -1420,  1670,   769, -1970, 
+	-1338,   369,  1748, -1051, -1338,  1573,   724, -1856, 
+	 -805,   222,  1051,  -632,  -805,   946,   435, -1116, 
+	 1024,  -805,  -554,  1420, -1024,  -283,  1338, -1204, 
+	 1204,  -946,  -652,  1670, -1204,  -332,  1573, -1416, 
+	  554,  -435,  -300,   769,  -554,  -153,   724,  -652, 
+	 -283,   222,   153,  -392,   283,    78,  -369,   332, 
+	-1024,   805,   554, -1420,  1024,   283, -1338,  1204, 
+	-1420,  1116,   769, -1970,  1420,   392, -1856,  1670, 
+	-1338,  1051,   724, -1856,  1338,   369, -1748,  1573, 
+	 -805,   632,   435, -1116,   805,   222, -1051,   946, 
+	 1024, -1204,   554,   283, -1024,  1420, -1338,   805, 
+	 1204, -1416,   652,   332, -1204,  1670, -1573,   946, 
+	  554,  -652,   300,   153,  -554,   769,  -724,   435, 
+	 -283,   332,  -153,   -78,   283,  -392,   369,  -222, 
+	-1024,  1204,  -554,  -283,  1024, -1420,  1338,  -805, 
+	-1420,  1670,  -769,  -392,  1420, -1970,  1856, -1116, 
+	-1338,  1573,  -724,  -369,  1338, -1856,  1748, -1051, 
+	 -805,   946,  -435,  -222,   805, -1116,  1051,  -632, 
+	 1024, -1420,  1338, -1204,  1024,  -805,   554,  -283, 
+	 1204, -1670,  1573, -1416,  1204,  -946,   652,  -332, 
+	  554,  -769,   724,  -652,   554,  -435,   300,  -153, 
+	 -283,   392,  -369,   332,  -283,   222,  -153,    78, 
+	-1024,  1420, -1338,  1204, -1024,   805,  -554,   283, 
+	-1420,  1970, -1856,  1670, -1420,  1116,  -769,   392, 
+	-1338,  1856, -1748,  1573, -1338,  1051,  -724,   369, 
+	 -805,  1116, -1051,   946,  -805,   632,  -435,   222, 
+	 1024,  1420,  1338,  1204,  1024,   805,   554,   283, 
+	  805,  1116,  1051,   946,   805,   632,   435,   222, 
+	 -554,  -769,  -724,  -652,  -554,  -435,  -300,  -153, 
+	-1420, -1970, -1856, -1670, -1420, -1116,  -769,  -392, 
+	-1024, -1420, -1338, -1204, -1024,  -805,  -554,  -283, 
+	  283,   392,   369,   332,   283,   222,   153,    78, 
+	 1338,  1856,  1748,  1573,  1338,  1051,   724,   369, 
+	 1204,  1670,  1573,  1416,  1204,   946,   652,   332, 
+	 1024,  1204,   554,  -283, -1024, -1420, -1338,  -805, 
+	  805,   946,   435,  -222,  -805, -1116, -1051,  -632, 
+	 -554,  -652,  -300,   153,   554,   769,   724,   435, 
+	-1420, -1670,  -769,   392,  1420,  1970,  1856,  1116, 
+	-1024, -1204,  -554,   283,  1024,  1420,  1338,   805, 
+	  283,   332,   153,   -78,  -283,  -392,  -369,  -222, 
+	 1338,  1573,   724,  -369, -1338, -1856, -1748, -1051, 
+	 1204,  1416,   652,  -332, -1204, -1670, -1573,  -946, 
+	 1024,   805,  -554, -1420, -1024,   283,  1338,  1204, 
+	  805,   632,  -435, -1116,  -805,   222,  1051,   946, 
+	 -554,  -435,   300,   769,   554,  -153,  -724,  -652, 
+	-1420, -1116,   769,  1970,  1420,  -392, -1856, -1670, 
+	-1024,  -805,   554,  1420,  1024,  -283, -1338, -1204, 
+	  283,   222,  -153,  -392,  -283,    78,   369,   332, 
+	 1338,  1051,  -724, -1856, -1338,   369,  1748,  1573, 
+	 1204,   946,  -652, -1670, -1204,   332,  1573,  1416, 
+	 1024,   283, -1338,  -805,  1024,  1204,  -554, -1420, 
+	  805,   222, -1051,  -632,   805,   946,  -435, -1116, 
+	 -554,  -153,   724,   435,  -554,  -652,   300,   769, 
+	-1420,  -392,  1856,  1116, -1420, -1670,   769,  1970, 
+	-1024,  -283,  1338,   805, -1024, -1204,   554,  1420, 
+	  283,    78,  -369,  -222,   283,   332,  -153,  -392, 
+	 1338,   369, -1748, -1051,  1338,  1573,  -724, -1856, 
+	 1204,   332, -1573,  -946,  1204,  1416,  -652, -1670, 
+	 1024,  -283, -1338,   805,  1024, -1204,  -554,  1420, 
+	  805,  -222, -1051,   632,   805,  -946,  -435,  1116, 
+	 -554,   153,   724,  -435,  -554,   652,   300,  -769, 
+	-1420,   392,  1856, -1116, -1420,  1670,   769, -1970, 
+	-1024,   283,  1338,  -805, -1024,  1204,   554, -1420, 
+	  283,   -78,  -369,   222,   283,  -332,  -153,   392, 
+	 1338,  -369, -1748,  1051,  1338, -1573,  -724,  1856, 
+	 1204,  -332, -1573,   946,  1204, -1416,  -652,  1670, 
+	 1024,  -805,  -554,  1420, -1024,  -283,  1338, -1204, 
+	  805,  -632,  -435,  1116,  -805,  -222,  1051,  -946, 
+	 -554,   435,   300,  -769,   554,   153,  -724,   652, 
+	-1420,  1116,   769, -1970,  1420,   392, -1856,  1670, 
+	-1024,   805,   554, -1420,  1024,   283, -1338,  1204, 
+	  283,  -222,  -153,   392,  -283,   -78,   369,  -332, 
+	 1338, -1051,  -724,  1856, -1338,  -369,  1748, -1573, 
+	 1204,  -946,  -652,  1670, -1204,  -332,  1573, -1416, 
+	 1024, -1204,   554,   283, -1024,  1420, -1338,   805, 
+	  805,  -946,   435,   222,  -805,  1116, -1051,   632, 
+	 -554,   652,  -300,  -153,   554,  -769,   724,  -435, 
+	-1420,  1670,  -769,  -392,  1420, -1970,  1856, -1116, 
+	-1024,  1204,  -554,  -283,  1024, -1420,  1338,  -805, 
+	  283,  -332,   153,    78,  -283,   392,  -369,   222, 
+	 1338, -1573,   724,   369, -1338,  1856, -1748,  1051, 
+	 1204, -1416,   652,   332, -1204,  1670, -1573,   946, 
+	 1024, -1420,  1338, -1204,  1024,  -805,   554,  -283, 
+	  805, -1116,  1051,  -946,   805,  -632,   435,  -222, 
+	 -554,   769,  -724,   652,  -554,   435,  -300,   153, 
+	-1420,  1970, -1856,  1670, -1420,  1116,  -769,   392, 
+	-1024,  1420, -1338,  1204, -1024,   805,  -554,   283, 
+	  283,  -392,   369,  -332,   283,  -222,   153,   -78, 
+	 1338, -1856,  1748, -1573,  1338, -1051,   724,  -369, 
+	 1204, -1670,  1573, -1416,  1204,  -946,   652,  -332, 
+	 1024,  1420,  1338,  1204,  1024,   805,   554,   283, 
+	  283,   392,   369,   332,   283,   222,   153,    78, 
+	-1338, -1856, -1748, -1573, -1338, -1051,  -724,  -369, 
+	 -805, -1116, -1051,  -946,  -805,  -632,  -435,  -222, 
+	 1024,  1420,  1338,  1204,  1024,   805,   554,   283, 
+	 1204,  1670,  1573,  1416,  1204,   946,   652,   332, 
+	 -554,  -769,  -724,  -652,  -554,  -435,  -300,  -153, 
+	-1420, -1970, -1856, -1670, -1420, -1116,  -769,  -392, 
+	 1024,  1204,   554,  -283, -1024, -1420, -1338,  -805, 
+	  283,   332,   153,   -78,  -283,  -392,  -369,  -222, 
+	-1338, -1573,  -724,   369,  1338,  1856,  1748,  1051, 
+	 -805,  -946,  -435,   222,   805,  1116,  1051,   632, 
+	 1024,  1204,   554,  -283, -1024, -1420, -1338,  -805, 
+	 1204,  1416,   652,  -332, -1204, -1670, -1573,  -946, 
+	 -554,  -652,  -300,   153,   554,   769,   724,   435, 
+	-1420, -1670,  -769,   392,  1420,  1970,  1856,  1116, 
+	 1024,   805,  -554, -1420, -1024,   283,  1338,  1204, 
+	  283,   222,  -153,  -392,  -283,    78,   369,   332, 
+	-1338, -1051,   724,  1856,  1338,  -369, -1748, -1573, 
+	 -805,  -632,   435,  1116,   805,  -222, -1051,  -946, 
+	 1024,   805,  -554, -1420, -1024,   283,  1338,  1204, 
+	 1204,   946,  -652, -1670, -1204,   332,  1573,  1416, 
+	 -554,  -435,   300,   769,   554,  -153,  -724,  -652, 
+	-1420, -1116,   769,  1970,  1420,  -392, -1856, -1670, 
+	 1024,   283, -1338,  -805,  1024,  1204,  -554, -1420, 
+	  283,    78,  -369,  -222,   283,   332,  -153,  -392, 
+	-1338,  -369,  1748,  1051, -1338, -1573,   724,  1856, 
+	 -805,  -222,  1051,   632,  -805,  -946,   435,  1116, 
+	 1024,   283, -1338,  -805,  1024,  1204,  -554, -1420, 
+	 1204,   332, -1573,  -946,  1204,  1416,  -652, -1670, 
+	 -554,  -153,   724,   435,  -554,  -652,   300,   769, 
+	-1420,  -392,  1856,  1116, -1420, -1670,   769,  1970, 
+	 1024,  -283, -1338,   805,  1024, -1204,  -554,  1420, 
+	  283,   -78,  -369,   222,   283,  -332,  -153,   392, 
+	-1338,   369,  1748, -1051, -1338,  1573,   724, -1856, 
+	 -805,   222,  1051,  -632,  -805,   946,   435, -1116, 
+	 1024,  -283, -1338,   805,  1024, -1204,  -554,  1420, 
+	 1204,  -332, -1573,   946,  1204, -1416,  -652,  1670, 
+	 -554,   153,   724,  -435,  -554,   652,   300,  -769, 
+	-1420,   392,  1856, -1116, -1420,  1670,   769, -1970, 
+	 1024,  -805,  -554,  1420, -1024,  -283,  1338, -1204, 
+	  283,  -222,  -153,   392,  -283,   -78,   369,  -332, 
+	-1338,  1051,   724, -1856,  1338,   369, -1748,  1573, 
+	 -805,   632,   435, -1116,   805,   222, -1051,   946, 
+	 1024,  -805,  -554,  1420, -1024,  -283,  1338, -1204, 
+	 1204,  -946,  -652,  1670, -1204,  -332,  1573, -1416, 
+	 -554,   435,   300,  -769,   554,   153,  -724,   652, 
+	-1420,  1116,   769, -1970,  1420,   392, -1856,  1670, 
+	 1024, -1204,   554,   283, -1024,  1420, -1338,   805, 
+	  283,  -332,   153,    78,  -283,   392,  -369,   222, 
+	-1338,  1573,  -724,  -369,  1338, -1856,  1748, -1051, 
+	 -805,   946,  -435,  -222,   805, -1116,  1051,  -632, 
+	 1024, -1204,   554,   283, -1024,  1420, -1338,   805, 
+	 1204, -1416,   652,   332, -1204,  1670, -1573,   946, 
+	 -554,   652,  -300,  -153,   554,  -769,   724,  -435, 
+	-1420,  1670,  -769,  -392,  1420, -1970,  1856, -1116, 
+	 1024, -1420,  1338, -1204,  1024,  -805,   554,  -283, 
+	  283,  -392,   369,  -332,   283,  -222,   153,   -78, 
+	-1338,  1856, -1748,  1573, -1338,  1051,  -724,   369, 
+	 -805,  1116, -1051,   946,  -805,   632,  -435,   222, 
+	 1024, -1420,  1338, -1204,  1024,  -805,   554,  -283, 
+	 1204, -1670,  1573, -1416,  1204,  -946,   652,  -332, 
+	 -554,   769,  -724,   652,  -554,   435,  -300,   153, 
+	-1420,  1970, -1856,  1670, -1420,  1116,  -769,   392, 
+	 1024,  1420,  1338,  1204,  1024,   805,   554,   283, 
+	 -283,  -392,  -369,  -332,  -283,  -222,  -153,   -78, 
+	-1338, -1856, -1748, -1573, -1338, -1051,  -724,  -369, 
+	  805,  1116,  1051,   946,   805,   632,   435,   222, 
+	 1024,  1420,  1338,  1204,  1024,   805,   554,   283, 
+	-1204, -1670, -1573, -1416, -1204,  -946,  -652,  -332, 
+	 -554,  -769,  -724,  -652,  -554,  -435,  -300,  -153, 
+	 1420,  1970,  1856,  1670,  1420,  1116,   769,   392, 
+	 1024,  1204,   554,  -283, -1024, -1420, -1338,  -805, 
+	 -283,  -332,  -153,    78,   283,   392,   369,   222, 
+	-1338, -1573,  -724,   369,  1338,  1856,  1748,  1051, 
+	  805,   946,   435,  -222,  -805, -1116, -1051,  -632, 
+	 1024,  1204,   554,  -283, -1024, -1420, -1338,  -805, 
+	-1204, -1416,  -652,   332,  1204,  1670,  1573,   946, 
+	 -554,  -652,  -300,   153,   554,   769,   724,   435, 
+	 1420,  1670,   769,  -392, -1420, -1970, -1856, -1116, 
+	 1024,   805,  -554, -1420, -1024,   283,  1338,  1204, 
+	 -283,  -222,   153,   392,   283,   -78,  -369,  -332, 
+	-1338, -1051,   724,  1856,  1338,  -369, -1748, -1573, 
+	  805,   632,  -435, -1116,  -805,   222,  1051,   946, 
+	 1024,   805,  -554, -1420, -1024,   283,  1338,  1204, 
+	-1204,  -946,   652,  1670,  1204,  -332, -1573, -1416, 
+	 -554,  -435,   300,   769,   554,  -153,  -724,  -652, 
+	 1420,  1116,  -769, -1970, -1420,   392,  1856,  1670, 
+	 1024,   283, -1338,  -805,  1024,  1204,  -554, -1420, 
+	 -283,   -78,   369,   222,  -283,  -332,   153,   392, 
+	-1338,  -369,  1748,  1051, -1338, -1573,   724,  1856, 
+	  805,   222, -1051,  -632,   805,   946,  -435, -1116, 
+	 1024,   283, -1338,  -805,  1024,  1204,  -554, -1420, 
+	-1204,  -332,  1573,   946, -1204, -1416,   652,  1670, 
+	 -554,  -153,   724,   435,  -554,  -652,   300,   769, 
+	 1420,   392, -1856, -1116,  1420,  1670,  -769, -1970, 
+	 1024,  -283, -1338,   805,  1024, -1204,  -554,  1420, 
+	 -283,    78,   369,  -222,  -283,   332,   153,  -392, 
+	-1338,   369,  1748, -1051, -1338,  1573,   724, -1856, 
+	  805,  -222, -1051,   632,   805,  -946,  -435,  1116, 
+	 1024,  -283, -1338,   805,  1024, -1204,  -554,  1420, 
+	-1204,   332,  1573,  -946, -1204,  1416,   652, -1670, 
+	 -554,   153,   724,  -435,  -554,   652,   300,  -769, 
+	 1420,  -392, -1856,  1116,  1420, -1670,  -769,  1970, 
+	 1024,  -805,  -554,  1420, -1024,  -283,  1338, -1204, 
+	 -283,   222,   153,  -392,   283,    78,  -369,   332, 
+	-1338,  1051,   724, -1856,  1338,   369, -1748,  1573, 
+	  805,  -632,  -435,  1116,  -805,  -222,  1051,  -946, 
+	 1024,  -805,  -554,  1420, -1024,  -283,  1338, -1204, 
+	-1204,   946,   652, -1670,  1204,   332, -1573,  1416, 
+	 -554,   435,   300,  -769,   554,   153,  -724,   652, 
+	 1420, -1116,  -769,  1970, -1420,  -392,  1856, -1670, 
+	 1024, -1204,   554,   283, -1024,  1420, -1338,   805, 
+	 -283,   332,  -153,   -78,   283,  -392,   369,  -222, 
+	-1338,  1573,  -724,  -369,  1338, -1856,  1748, -1051, 
+	  805,  -946,   435,   222,  -805,  1116, -1051,   632, 
+	 1024, -1204,   554,   283, -1024,  1420, -1338,   805, 
+	-1204,  1416,  -652,  -332,  1204, -1670,  1573,  -946, 
+	 -554,   652,  -300,  -153,   554,  -769,   724,  -435, 
+	 1420, -1670,   769,   392, -1420,  1970, -1856,  1116, 
+	 1024, -1420,  1338, -1204,  1024,  -805,   554,  -283, 
+	 -283,   392,  -369,   332,  -283,   222,  -153,    78, 
+	-1338,  1856, -1748,  1573, -1338,  1051,  -724,   369, 
+	  805, -1116,  1051,  -946,   805,  -632,   435,  -222, 
+	 1024, -1420,  1338, -1204,  1024,  -805,   554,  -283, 
+	-1204,  1670, -1573,  1416, -1204,   946,  -652,   332, 
+	 -554,   769,  -724,   652,  -554,   435,  -300,   153, 
+	 1420, -1970,  1856, -1670,  1420, -1116,   769,  -392, 
+	 1024,  1420,  1338,  1204,  1024,   805,   554,   283, 
+	 -805, -1116, -1051,  -946,  -805,  -632,  -435,  -222, 
+	 -554,  -769,  -724,  -652,  -554,  -435,  -300,  -153, 
+	 1420,  1970,  1856,  1670,  1420,  1116,   769,   392, 
+	-1024, -1420, -1338, -1204, -1024,  -805,  -554,  -283, 
+	 -283,  -392,  -369,  -332,  -283,  -222,  -153,   -78, 
+	 1338,  1856,  1748,  1573,  1338,  1051,   724,   369, 
+	-1204, -1670, -1573, -1416, -1204,  -946,  -652,  -332, 
+	 1024,  1204,   554,  -283, -1024, -1420, -1338,  -805, 
+	 -805,  -946,  -435,   222,   805,  1116,  1051,   632, 
+	 -554,  -652,  -300,   153,   554,   769,   724,   435, 
+	 1420,  1670,   769,  -392, -1420, -1970, -1856, -1116, 
+	-1024, -1204,  -554,   283,  1024,  1420,  1338,   805, 
+	 -283,  -332,  -153,    78,   283,   392,   369,   222, 
+	 1338,  1573,   724,  -369, -1338, -1856, -1748, -1051, 
+	-1204, -1416,  -652,   332,  1204,  1670,  1573,   946, 
+	 1024,   805,  -554, -1420, -1024,   283,  1338,  1204, 
+	 -805,  -632,   435,  1116,   805,  -222, -1051,  -946, 
+	 -554,  -435,   300,   769,   554,  -153,  -724,  -652, 
+	 1420,  1116,  -769, -1970, -1420,   392,  1856,  1670, 
+	-1024,  -805,   554,  1420,  1024,  -283, -1338, -1204, 
+	 -283,  -222,   153,   392,   283,   -78,  -369,  -332, 
+	 1338,  1051,  -724, -1856, -1338,   369,  1748,  1573, 
+	-1204,  -946,   652,  1670,  1204,  -332, -1573, -1416, 
+	 1024,   283, -1338,  -805,  1024,  1204,  -554, -1420, 
+	 -805,  -222,  1051,   632,  -805,  -946,   435,  1116, 
+	 -554,  -153,   724,   435,  -554,  -652,   300,   769, 
+	 1420,   392, -1856, -1116,  1420,  1670,  -769, -1970, 
+	-1024,  -283,  1338,   805, -1024, -1204,   554,  1420, 
+	 -283,   -78,   369,   222,  -283,  -332,   153,   392, 
+	 1338,   369, -1748, -1051,  1338,  1573,  -724, -1856, 
+	-1204,  -332,  1573,   946, -1204, -1416,   652,  1670, 
+	 1024,  -283, -1338,   805,  1024, -1204,  -554,  1420, 
+	 -805,   222,  1051,  -632,  -805,   946,   435, -1116, 
+	 -554,   153,   724,  -435,  -554,   652,   300,  -769, 
+	 1420,  -392, -1856,  1116,  1420, -1670,  -769,  1970, 
+	-1024,   283,  1338,  -805, -1024,  1204,   554, -1420, 
+	 -283,    78,   369,  -222,  -283,   332,   153,  -392, 
+	 1338,  -369, -1748,  1051,  1338, -1573,  -724,  1856, 
+	-1204,   332,  1573,  -946, -1204,  1416,   652, -1670, 
+	 1024,  -805,  -554,  1420, -1024,  -283,  1338, -1204, 
+	 -805,   632,   435, -1116,   805,   222, -1051,   946, 
+	 -554,   435,   300,  -769,   554,   153,  -724,   652, 
+	 1420, -1116,  -769,  1970, -1420,  -392,  1856, -1670, 
+	-1024,   805,   554, -1420,  1024,   283, -1338,  1204, 
+	 -283,   222,   153,  -392,   283,    78,  -369,   332, 
+	 1338, -1051,  -724,  1856, -1338,  -369,  1748, -1573, 
+	-1204,   946,   652, -1670,  1204,   332, -1573,  1416, 
+	 1024, -1204,   554,   283, -1024,  1420, -1338,   805, 
+	 -805,   946,  -435,  -222,   805, -1116,  1051,  -632, 
+	 -554,   652,  -300,  -153,   554,  -769,   724,  -435, 
+	 1420, -1670,   769,   392, -1420,  1970, -1856,  1116, 
+	-1024,  1204,  -554,  -283,  1024, -1420,  1338,  -805, 
+	 -283,   332,  -153,   -78,   283,  -392,   369,  -222, 
+	 1338, -1573,   724,   369, -1338,  1856, -1748,  1051, 
+	-1204,  1416,  -652,  -332,  1204, -1670,  1573,  -946, 
+	 1024, -1420,  1338, -1204,  1024,  -805,   554,  -283, 
+	 -805,  1116, -1051,   946,  -805,   632,  -435,   222, 
+	 -554,   769,  -724,   652,  -554,   435,  -300,   153, 
+	 1420, -1970,  1856, -1670,  1420, -1116,   769,  -392, 
+	-1024,  1420, -1338,  1204, -1024,   805,  -554,   283, 
+	 -283,   392,  -369,   332,  -283,   222,  -153,    78, 
+	 1338, -1856,  1748, -1573,  1338, -1051,   724,  -369, 
+	-1204,  1670, -1573,  1416, -1204,   946,  -652,   332, 
+	 1024,  1420,  1338,  1204,  1024,   805,   554,   283, 
+	-1204, -1670, -1573, -1416, -1204,  -946,  -652,  -332, 
+	  554,   769,   724,   652,   554,   435,   300,   153, 
+	  283,   392,   369,   332,   283,   222,   153,    78, 
+	-1024, -1420, -1338, -1204, -1024,  -805,  -554,  -283, 
+	 1420,  1970,  1856,  1670,  1420,  1116,   769,   392, 
+	-1338, -1856, -1748, -1573, -1338, -1051,  -724,  -369, 
+	  805,  1116,  1051,   946,   805,   632,   435,   222, 
+	 1024,  1204,   554,  -283, -1024, -1420, -1338,  -805, 
+	-1204, -1416,  -652,   332,  1204,  1670,  1573,   946, 
+	  554,   652,   300,  -153,  -554,  -769,  -724,  -435, 
+	  283,   332,   153,   -78,  -283,  -392,  -369,  -222, 
+	-1024, -1204,  -554,   283,  1024,  1420,  1338,   805, 
+	 1420,  1670,   769,  -392, -1420, -1970, -1856, -1116, 
+	-1338, -1573,  -724,   369,  1338,  1856,  1748,  1051, 
+	  805,   946,   435,  -222,  -805, -1116, -1051,  -632, 
+	 1024,   805,  -554, -1420, -1024,   283,  1338,  1204, 
+	-1204,  -946,   652,  1670,  1204,  -332, -1573, -1416, 
+	  554,   435,  -300,  -769,  -554,   153,   724,   652, 
+	  283,   222,  -153,  -392,  -283,    78,   369,   332, 
+	-1024,  -805,   554,  1420,  1024,  -283, -1338, -1204, 
+	 1420,  1116,  -769, -1970, -1420,   392,  1856,  1670, 
+	-1338, -1051,   724,  1856,  1338,  -369, -1748, -1573, 
+	  805,   632,  -435, -1116,  -805,   222,  1051,   946, 
+	 1024,   283, -1338,  -805,  1024,  1204,  -554, -1420, 
+	-1204,  -332,  1573,   946, -1204, -1416,   652,  1670, 
+	  554,   153,  -724,  -435,   554,   652,  -300,  -769, 
+	  283,    78,  -369,  -222,   283,   332,  -153,  -392, 
+	-1024,  -283,  1338,   805, -1024, -1204,   554,  1420, 
+	 1420,   392, -1856, -1116,  1420,  1670,  -769, -1970, 
+	-1338,  -369,  1748,  1051, -1338, -1573,   724,  1856, 
+	  805,   222, -1051,  -632,   805,   946,  -435, -1116, 
+	 1024,  -283, -1338,   805,  1024, -1204,  -554,  1420, 
+	-1204,   332,  1573,  -946, -1204,  1416,   652, -1670, 
+	  554,  -153,  -724,   435,   554,  -652,  -300,   769, 
+	  283,   -78,  -369,   222,   283,  -332,  -153,   392, 
+	-1024,   283,  1338,  -805, -1024,  1204,   554, -1420, 
+	 1420,  -392, -1856,  1116,  1420, -1670,  -769,  1970, 
+	-1338,   369,  1748, -1051, -1338,  1573,   724, -1856, 
+	  805,  -222, -1051,   632,   805,  -946,  -435,  1116, 
+	 1024,  -805,  -554,  1420, -1024,  -283,  1338, -1204, 
+	-1204,   946,   652, -1670,  1204,   332, -1573,  1416, 
+	  554,  -435,  -300,   769,  -554,  -153,   724,  -652, 
+	  283,  -222,  -153,   392,  -283,   -78,   369,  -332, 
+	-1024,   805,   554, -1420,  1024,   283, -1338,  1204, 
+	 1420, -1116,  -769,  1970, -1420,  -392,  1856, -1670, 
+	-1338,  1051,   724, -1856,  1338,   369, -1748,  1573, 
+	  805,  -632,  -435,  1116,  -805,  -222,  1051,  -946, 
+	 1024, -1204,   554,   283, -1024,  1420, -1338,   805, 
+	-1204,  1416,  -652,  -332,  1204, -1670,  1573,  -946, 
+	  554,  -652,   300,   153,  -554,   769,  -724,   435, 
+	  283,  -332,   153,    78,  -283,   392,  -369,   222, 
+	-1024,  1204,  -554,  -283,  1024, -1420,  1338,  -805, 
+	 1420, -1670,   769,   392, -1420,  1970, -1856,  1116, 
+	-1338,  1573,  -724,  -369,  1338, -1856,  1748, -1051, 
+	  805,  -946,   435,   222,  -805,  1116, -1051,   632, 
+	 1024, -1420,  1338, -1204,  1024,  -805,   554,  -283, 
+	-1204,  1670, -1573,  1416, -1204,   946,  -652,   332, 
+	  554,  -769,   724,  -652,   554,  -435,   300,  -153, 
+	  283,  -392,   369,  -332,   283,  -222,   153,   -78, 
+	-1024,  1420, -1338,  1204, -1024,   805,  -554,   283, 
+	 1420, -1970,  1856, -1670,  1420, -1116,   769,  -392, 
+	-1338,  1856, -1748,  1573, -1338,  1051,  -724,   369, 
+	  805, -1116,  1051,  -946,   805,  -632,   435,  -222, 
+	 1024,  1420,  1338,  1204,  1024,   805,   554,   283, 
+	-1420, -1970, -1856, -1670, -1420, -1116,  -769,  -392, 
+	 1338,  1856,  1748,  1573,  1338,  1051,   724,   369, 
+	-1204, -1670, -1573, -1416, -1204,  -946,  -652,  -332, 
+	 1024,  1420,  1338,  1204,  1024,   805,   554,   283, 
+	 -805, -1116, -1051,  -946,  -805,  -632,  -435,  -222, 
+	  554,   769,   724,   652,   554,   435,   300,   153, 
+	 -283,  -392,  -369,  -332,  -283,  -222,  -153,   -78, 
+	 1024,  1204,   554,  -283, -1024, -1420, -1338,  -805, 
+	-1420, -1670,  -769,   392,  1420,  1970,  1856,  1116, 
+	 1338,  1573,   724,  -369, -1338, -1856, -1748, -1051, 
+	-1204, -1416,  -652,   332,  1204,  1670,  1573,   946, 
+	 1024,  1204,   554,  -283, -1024, -1420, -1338,  -805, 
+	 -805,  -946,  -435,   222,   805,  1116,  1051,   632, 
+	  554,   652,   300,  -153,  -554,  -769,  -724,  -435, 
+	 -283,  -332,  -153,    78,   283,   392,   369,   222, 
+	 1024,   805,  -554, -1420, -1024,   283,  1338,  1204, 
+	-1420, -1116,   769,  1970,  1420,  -392, -1856, -1670, 
+	 1338,  1051,  -724, -1856, -1338,   369,  1748,  1573, 
+	-1204,  -946,   652,  1670,  1204,  -332, -1573, -1416, 
+	 1024,   805,  -554, -1420, -1024,   283,  1338,  1204, 
+	 -805,  -632,   435,  1116,   805,  -222, -1051,  -946, 
+	  554,   435,  -300,  -769,  -554,   153,   724,   652, 
+	 -283,  -222,   153,   392,   283,   -78,  -369,  -332, 
+	 1024,   283, -1338,  -805,  1024,  1204,  -554, -1420, 
+	-1420,  -392,  1856,  1116, -1420, -1670,   769,  1970, 
+	 1338,   369, -1748, -1051,  1338,  1573,  -724, -1856, 
+	-1204,  -332,  1573,   946, -1204, -1416,   652,  1670, 
+	 1024,   283, -1338,  -805,  1024,  1204,  -554, -1420, 
+	 -805,  -222,  1051,   632,  -805,  -946,   435,  1116, 
+	  554,   153,  -724,  -435,   554,   652,  -300,  -769, 
+	 -283,   -78,   369,   222,  -283,  -332,   153,   392, 
+	 1024,  -283, -1338,   805,  1024, -1204,  -554,  1420, 
+	-1420,   392,  1856, -1116, -1420,  1670,   769, -1970, 
+	 1338,  -369, -1748,  1051,  1338, -1573,  -724,  1856, 
+	-1204,   332,  1573,  -946, -1204,  1416,   652, -1670, 
+	 1024,  -283, -1338,   805,  1024, -1204,  -554,  1420, 
+	 -805,   222,  1051,  -632,  -805,   946,   435, -1116, 
+	  554,  -153,  -724,   435,   554,  -652,  -300,   769, 
+	 -283,    78,   369,  -222,  -283,   332,   153,  -392, 
+	 1024,  -805,  -554,  1420, -1024,  -283,  1338, -1204, 
+	-1420,  1116,   769, -1970,  1420,   392, -1856,  1670, 
+	 1338, -1051,  -724,  1856, -1338,  -369,  1748, -1573, 
+	-1204,   946,   652, -1670,  1204,   332, -1573,  1416, 
+	 1024,  -805,  -554,  1420, -1024,  -283,  1338, -1204, 
+	 -805,   632,   435, -1116,   805,   222, -1051,   946, 
+	  554,  -435,  -300,   769,  -554,  -153,   724,  -652, 
+	 -283,   222,   153,  -392,   283,    78,  -369,   332, 
+	 1024, -1204,   554,   283, -1024,  1420, -1338,   805, 
+	-1420,  1670,  -769,  -392,  1420, -1970,  1856, -1116, 
+	 1338, -1573,   724,   369, -1338,  1856, -1748,  1051, 
+	-1204,  1416,  -652,  -332,  1204, -1670,  1573,  -946, 
+	 1024, -1204,   554,   283, -1024,  1420, -1338,   805, 
+	 -805,   946,  -435,  -222,   805, -1116,  1051,  -632, 
+	  554,  -652,   300,   153,  -554,   769,  -724,   435, 
+	 -283,   332,  -153,   -78,   283,  -392,   369,  -222, 
+	 1024, -1420,  1338, -1204,  1024,  -805,   554,  -283, 
+	-1420,  1970, -1856,  1670, -1420,  1116,  -769,   392, 
+	 1338, -1856,  1748, -1573,  1338, -1051,   724,  -369, 
+	-1204,  1670, -1573,  1416, -1204,   946,  -652,   332, 
+	 1024, -1420,  1338, -1204,  1024,  -805,   554,  -283, 
+	 -805,  1116, -1051,   946,  -805,   632,  -435,   222, 
+	  554,  -769,   724,  -652,   554,  -435,   300,  -153, 
+	 -283,   392,  -369,   332,  -283,   222,  -153,    78, 
+};
+
+// precalculated int values for 8x8 FDCT, multplied by 8192
+const int icos_fdct_8x8[ 4096 ] =
+{
+	 1024,  1024,  1024,  1024,  1024,  1024,  1024,  1024, 
+	 1024,  1024,  1024,  1024,  1024,  1024,  1024,  1024, 
+	 1024,  1024,  1024,  1024,  1024,  1024,  1024,  1024, 
+	 1024,  1024,  1024,  1024,  1024,  1024,  1024,  1024, 
+	 1024,  1024,  1024,  1024,  1024,  1024,  1024,  1024, 
+	 1024,  1024,  1024,  1024,  1024,  1024,  1024,  1024, 
+	 1024,  1024,  1024,  1024,  1024,  1024,  1024,  1024, 
+	 1024,  1024,  1024,  1024,  1024,  1024,  1024,  1024, 
+	 1420,  1204,   805,   283,  -283,  -805, -1204, -1420, 
+	 1420,  1204,   805,   283,  -283,  -805, -1204, -1420, 
+	 1420,  1204,   805,   283,  -283,  -805, -1204, -1420, 
+	 1420,  1204,   805,   283,  -283,  -805, -1204, -1420, 
+	 1420,  1204,   805,   283,  -283,  -805, -1204, -1420, 
+	 1420,  1204,   805,   283,  -283,  -805, -1204, -1420, 
+	 1420,  1204,   805,   283,  -283,  -805, -1204, -1420, 
+	 1420,  1204,   805,   283,  -283,  -805, -1204, -1420, 
+	 1338,   554,  -554, -1338, -1338,  -554,   554,  1338, 
+	 1338,   554,  -554, -1338, -1338,  -554,   554,  1338, 
+	 1338,   554,  -554, -1338, -1338,  -554,   554,  1338, 
+	 1338,   554,  -554, -1338, -1338,  -554,   554,  1338, 
+	 1338,   554,  -554, -1338, -1338,  -554,   554,  1338, 
+	 1338,   554,  -554, -1338, -1338,  -554,   554,  1338, 
+	 1338,   554,  -554, -1338, -1338,  -554,   554,  1338, 
+	 1338,   554,  -554, -1338, -1338,  -554,   554,  1338, 
+	 1204,  -283, -1420,  -805,   805,  1420,   283, -1204, 
+	 1204,  -283, -1420,  -805,   805,  1420,   283, -1204, 
+	 1204,  -283, -1420,  -805,   805,  1420,   283, -1204, 
+	 1204,  -283, -1420,  -805,   805,  1420,   283, -1204, 
+	 1204,  -283, -1420,  -805,   805,  1420,   283, -1204, 
+	 1204,  -283, -1420,  -805,   805,  1420,   283, -1204, 
+	 1204,  -283, -1420,  -805,   805,  1420,   283, -1204, 
+	 1204,  -283, -1420,  -805,   805,  1420,   283, -1204, 
+	 1024, -1024, -1024,  1024,  1024, -1024, -1024,  1024, 
+	 1024, -1024, -1024,  1024,  1024, -1024, -1024,  1024, 
+	 1024, -1024, -1024,  1024,  1024, -1024, -1024,  1024, 
+	 1024, -1024, -1024,  1024,  1024, -1024, -1024,  1024, 
+	 1024, -1024, -1024,  1024,  1024, -1024, -1024,  1024, 
+	 1024, -1024, -1024,  1024,  1024, -1024, -1024,  1024, 
+	 1024, -1024, -1024,  1024,  1024, -1024, -1024,  1024, 
+	 1024, -1024, -1024,  1024,  1024, -1024, -1024,  1024, 
+	  805, -1420,   283,  1204, -1204,  -283,  1420,  -805, 
+	  805, -1420,   283,  1204, -1204,  -283,  1420,  -805, 
+	  805, -1420,   283,  1204, -1204,  -283,  1420,  -805, 
+	  805, -1420,   283,  1204, -1204,  -283,  1420,  -805, 
+	  805, -1420,   283,  1204, -1204,  -283,  1420,  -805, 
+	  805, -1420,   283,  1204, -1204,  -283,  1420,  -805, 
+	  805, -1420,   283,  1204, -1204,  -283,  1420,  -805, 
+	  805, -1420,   283,  1204, -1204,  -283,  1420,  -805, 
+	  554, -1338,  1338,  -554,  -554,  1338, -1338,   554, 
+	  554, -1338,  1338,  -554,  -554,  1338, -1338,   554, 
+	  554, -1338,  1338,  -554,  -554,  1338, -1338,   554, 
+	  554, -1338,  1338,  -554,  -554,  1338, -1338,   554, 
+	  554, -1338,  1338,  -554,  -554,  1338, -1338,   554, 
+	  554, -1338,  1338,  -554,  -554,  1338, -1338,   554, 
+	  554, -1338,  1338,  -554,  -554,  1338, -1338,   554, 
+	  554, -1338,  1338,  -554,  -554,  1338, -1338,   554, 
+	  283,  -805,  1204, -1420,  1420, -1204,   805,  -283, 
+	  283,  -805,  1204, -1420,  1420, -1204,   805,  -283, 
+	  283,  -805,  1204, -1420,  1420, -1204,   805,  -283, 
+	  283,  -805,  1204, -1420,  1420, -1204,   805,  -283, 
+	  283,  -805,  1204, -1420,  1420, -1204,   805,  -283, 
+	  283,  -805,  1204, -1420,  1420, -1204,   805,  -283, 
+	  283,  -805,  1204, -1420,  1420, -1204,   805,  -283, 
+	  283,  -805,  1204, -1420,  1420, -1204,   805,  -283, 
+	 1420,  1420,  1420,  1420,  1420,  1420,  1420,  1420, 
+	 1204,  1204,  1204,  1204,  1204,  1204,  1204,  1204, 
+	  805,   805,   805,   805,   805,   805,   805,   805, 
+	  283,   283,   283,   283,   283,   283,   283,   283, 
+	 -283,  -283,  -283,  -283,  -283,  -283,  -283,  -283, 
+	 -805,  -805,  -805,  -805,  -805,  -805,  -805,  -805, 
+	-1204, -1204, -1204, -1204, -1204, -1204, -1204, -1204, 
+	-1420, -1420, -1420, -1420, -1420, -1420, -1420, -1420, 
+	 1970,  1670,  1116,   392,  -392, -1116, -1670, -1970, 
+	 1670,  1416,   946,   332,  -332,  -946, -1416, -1670, 
+	 1116,   946,   632,   222,  -222,  -632,  -946, -1116, 
+	  392,   332,   222,    78,   -78,  -222,  -332,  -392, 
+	 -392,  -332,  -222,   -78,    78,   222,   332,   392, 
+	-1116,  -946,  -632,  -222,   222,   632,   946,  1116, 
+	-1670, -1416,  -946,  -332,   332,   946,  1416,  1670, 
+	-1970, -1670, -1116,  -392,   392,  1116,  1670,  1970, 
+	 1856,   769,  -769, -1856, -1856,  -769,   769,  1856, 
+	 1573,   652,  -652, -1573, -1573,  -652,   652,  1573, 
+	 1051,   435,  -435, -1051, -1051,  -435,   435,  1051, 
+	  369,   153,  -153,  -369,  -369,  -153,   153,   369, 
+	 -369,  -153,   153,   369,   369,   153,  -153,  -369, 
+	-1051,  -435,   435,  1051,  1051,   435,  -435, -1051, 
+	-1573,  -652,   652,  1573,  1573,   652,  -652, -1573, 
+	-1856,  -769,   769,  1856,  1856,   769,  -769, -1856, 
+	 1670,  -392, -1970, -1116,  1116,  1970,   392, -1670, 
+	 1416,  -332, -1670,  -946,   946,  1670,   332, -1416, 
+	  946,  -222, -1116,  -632,   632,  1116,   222,  -946, 
+	  332,   -78,  -392,  -222,   222,   392,    78,  -332, 
+	 -332,    78,   392,   222,  -222,  -392,   -78,   332, 
+	 -946,   222,  1116,   632,  -632, -1116,  -222,   946, 
+	-1416,   332,  1670,   946,  -946, -1670,  -332,  1416, 
+	-1670,   392,  1970,  1116, -1116, -1970,  -392,  1670, 
+	 1420, -1420, -1420,  1420,  1420, -1420, -1420,  1420, 
+	 1204, -1204, -1204,  1204,  1204, -1204, -1204,  1204, 
+	  805,  -805,  -805,   805,   805,  -805,  -805,   805, 
+	  283,  -283,  -283,   283,   283,  -283,  -283,   283, 
+	 -283,   283,   283,  -283,  -283,   283,   283,  -283, 
+	 -805,   805,   805,  -805,  -805,   805,   805,  -805, 
+	-1204,  1204,  1204, -1204, -1204,  1204,  1204, -1204, 
+	-1420,  1420,  1420, -1420, -1420,  1420,  1420, -1420, 
+	 1116, -1970,   392,  1670, -1670,  -392,  1970, -1116, 
+	  946, -1670,   332,  1416, -1416,  -332,  1670,  -946, 
+	  632, -1116,   222,   946,  -946,  -222,  1116,  -632, 
+	  222,  -392,    78,   332,  -332,   -78,   392,  -222, 
+	 -222,   392,   -78,  -332,   332,    78,  -392,   222, 
+	 -632,  1116,  -222,  -946,   946,   222, -1116,   632, 
+	 -946,  1670,  -332, -1416,  1416,   332, -1670,   946, 
+	-1116,  1970,  -392, -1670,  1670,   392, -1970,  1116, 
+	  769, -1856,  1856,  -769,  -769,  1856, -1856,   769, 
+	  652, -1573,  1573,  -652,  -652,  1573, -1573,   652, 
+	  435, -1051,  1051,  -435,  -435,  1051, -1051,   435, 
+	  153,  -369,   369,  -153,  -153,   369,  -369,   153, 
+	 -153,   369,  -369,   153,   153,  -369,   369,  -153, 
+	 -435,  1051, -1051,   435,   435, -1051,  1051,  -435, 
+	 -652,  1573, -1573,   652,   652, -1573,  1573,  -652, 
+	 -769,  1856, -1856,   769,   769, -1856,  1856,  -769, 
+	  392, -1116,  1670, -1970,  1970, -1670,  1116,  -392, 
+	  332,  -946,  1416, -1670,  1670, -1416,   946,  -332, 
+	  222,  -632,   946, -1116,  1116,  -946,   632,  -222, 
+	   78,  -222,   332,  -392,   392,  -332,   222,   -78, 
+	  -78,   222,  -332,   392,  -392,   332,  -222,    78, 
+	 -222,   632,  -946,  1116, -1116,   946,  -632,   222, 
+	 -332,   946, -1416,  1670, -1670,  1416,  -946,   332, 
+	 -392,  1116, -1670,  1970, -1970,  1670, -1116,   392, 
+	 1338,  1338,  1338,  1338,  1338,  1338,  1338,  1338, 
+	  554,   554,   554,   554,   554,   554,   554,   554, 
+	 -554,  -554,  -554,  -554,  -554,  -554,  -554,  -554, 
+	-1338, -1338, -1338, -1338, -1338, -1338, -1338, -1338, 
+	-1338, -1338, -1338, -1338, -1338, -1338, -1338, -1338, 
+	 -554,  -554,  -554,  -554,  -554,  -554,  -554,  -554, 
+	  554,   554,   554,   554,   554,   554,   554,   554, 
+	 1338,  1338,  1338,  1338,  1338,  1338,  1338,  1338, 
+	 1856,  1573,  1051,   369,  -369, -1051, -1573, -1856, 
+	  769,   652,   435,   153,  -153,  -435,  -652,  -769, 
+	 -769,  -652,  -435,  -153,   153,   435,   652,   769, 
+	-1856, -1573, -1051,  -369,   369,  1051,  1573,  1856, 
+	-1856, -1573, -1051,  -369,   369,  1051,  1573,  1856, 
+	 -769,  -652,  -435,  -153,   153,   435,   652,   769, 
+	  769,   652,   435,   153,  -153,  -435,  -652,  -769, 
+	 1856,  1573,  1051,   369,  -369, -1051, -1573, -1856, 
+	 1748,   724,  -724, -1748, -1748,  -724,   724,  1748, 
+	  724,   300,  -300,  -724,  -724,  -300,   300,   724, 
+	 -724,  -300,   300,   724,   724,   300,  -300,  -724, 
+	-1748,  -724,   724,  1748,  1748,   724,  -724, -1748, 
+	-1748,  -724,   724,  1748,  1748,   724,  -724, -1748, 
+	 -724,  -300,   300,   724,   724,   300,  -300,  -724, 
+	  724,   300,  -300,  -724,  -724,  -300,   300,   724, 
+	 1748,   724,  -724, -1748, -1748,  -724,   724,  1748, 
+	 1573,  -369, -1856, -1051,  1051,  1856,   369, -1573, 
+	  652,  -153,  -769,  -435,   435,   769,   153,  -652, 
+	 -652,   153,   769,   435,  -435,  -769,  -153,   652, 
+	-1573,   369,  1856,  1051, -1051, -1856,  -369,  1573, 
+	-1573,   369,  1856,  1051, -1051, -1856,  -369,  1573, 
+	 -652,   153,   769,   435,  -435,  -769,  -153,   652, 
+	  652,  -153,  -769,  -435,   435,   769,   153,  -652, 
+	 1573,  -369, -1856, -1051,  1051,  1856,   369, -1573, 
+	 1338, -1338, -1338,  1338,  1338, -1338, -1338,  1338, 
+	  554,  -554,  -554,   554,   554,  -554,  -554,   554, 
+	 -554,   554,   554,  -554,  -554,   554,   554,  -554, 
+	-1338,  1338,  1338, -1338, -1338,  1338,  1338, -1338, 
+	-1338,  1338,  1338, -1338, -1338,  1338,  1338, -1338, 
+	 -554,   554,   554,  -554,  -554,   554,   554,  -554, 
+	  554,  -554,  -554,   554,   554,  -554,  -554,   554, 
+	 1338, -1338, -1338,  1338,  1338, -1338, -1338,  1338, 
+	 1051, -1856,   369,  1573, -1573,  -369,  1856, -1051, 
+	  435,  -769,   153,   652,  -652,  -153,   769,  -435, 
+	 -435,   769,  -153,  -652,   652,   153,  -769,   435, 
+	-1051,  1856,  -369, -1573,  1573,   369, -1856,  1051, 
+	-1051,  1856,  -369, -1573,  1573,   369, -1856,  1051, 
+	 -435,   769,  -153,  -652,   652,   153,  -769,   435, 
+	  435,  -769,   153,   652,  -652,  -153,   769,  -435, 
+	 1051, -1856,   369,  1573, -1573,  -369,  1856, -1051, 
+	  724, -1748,  1748,  -724,  -724,  1748, -1748,   724, 
+	  300,  -724,   724,  -300,  -300,   724,  -724,   300, 
+	 -300,   724,  -724,   300,   300,  -724,   724,  -300, 
+	 -724,  1748, -1748,   724,   724, -1748,  1748,  -724, 
+	 -724,  1748, -1748,   724,   724, -1748,  1748,  -724, 
+	 -300,   724,  -724,   300,   300,  -724,   724,  -300, 
+	  300,  -724,   724,  -300,  -300,   724,  -724,   300, 
+	  724, -1748,  1748,  -724,  -724,  1748, -1748,   724, 
+	  369, -1051,  1573, -1856,  1856, -1573,  1051,  -369, 
+	  153,  -435,   652,  -769,   769,  -652,   435,  -153, 
+	 -153,   435,  -652,   769,  -769,   652,  -435,   153, 
+	 -369,  1051, -1573,  1856, -1856,  1573, -1051,   369, 
+	 -369,  1051, -1573,  1856, -1856,  1573, -1051,   369, 
+	 -153,   435,  -652,   769,  -769,   652,  -435,   153, 
+	  153,  -435,   652,  -769,   769,  -652,   435,  -153, 
+	  369, -1051,  1573, -1856,  1856, -1573,  1051,  -369, 
+	 1204,  1204,  1204,  1204,  1204,  1204,  1204,  1204, 
+	 -283,  -283,  -283,  -283,  -283,  -283,  -283,  -283, 
+	-1420, -1420, -1420, -1420, -1420, -1420, -1420, -1420, 
+	 -805,  -805,  -805,  -805,  -805,  -805,  -805,  -805, 
+	  805,   805,   805,   805,   805,   805,   805,   805, 
+	 1420,  1420,  1420,  1420,  1420,  1420,  1420,  1420, 
+	  283,   283,   283,   283,   283,   283,   283,   283, 
+	-1204, -1204, -1204, -1204, -1204, -1204, -1204, -1204, 
+	 1670,  1416,   946,   332,  -332,  -946, -1416, -1670, 
+	 -392,  -332,  -222,   -78,    78,   222,   332,   392, 
+	-1970, -1670, -1116,  -392,   392,  1116,  1670,  1970, 
+	-1116,  -946,  -632,  -222,   222,   632,   946,  1116, 
+	 1116,   946,   632,   222,  -222,  -632,  -946, -1116, 
+	 1970,  1670,  1116,   392,  -392, -1116, -1670, -1970, 
+	  392,   332,   222,    78,   -78,  -222,  -332,  -392, 
+	-1670, -1416,  -946,  -332,   332,   946,  1416,  1670, 
+	 1573,   652,  -652, -1573, -1573,  -652,   652,  1573, 
+	 -369,  -153,   153,   369,   369,   153,  -153,  -369, 
+	-1856,  -769,   769,  1856,  1856,   769,  -769, -1856, 
+	-1051,  -435,   435,  1051,  1051,   435,  -435, -1051, 
+	 1051,   435,  -435, -1051, -1051,  -435,   435,  1051, 
+	 1856,   769,  -769, -1856, -1856,  -769,   769,  1856, 
+	  369,   153,  -153,  -369,  -369,  -153,   153,   369, 
+	-1573,  -652,   652,  1573,  1573,   652,  -652, -1573, 
+	 1416,  -332, -1670,  -946,   946,  1670,   332, -1416, 
+	 -332,    78,   392,   222,  -222,  -392,   -78,   332, 
+	-1670,   392,  1970,  1116, -1116, -1970,  -392,  1670, 
+	 -946,   222,  1116,   632,  -632, -1116,  -222,   946, 
+	  946,  -222, -1116,  -632,   632,  1116,   222,  -946, 
+	 1670,  -392, -1970, -1116,  1116,  1970,   392, -1670, 
+	  332,   -78,  -392,  -222,   222,   392,    78,  -332, 
+	-1416,   332,  1670,   946,  -946, -1670,  -332,  1416, 
+	 1204, -1204, -1204,  1204,  1204, -1204, -1204,  1204, 
+	 -283,   283,   283,  -283,  -283,   283,   283,  -283, 
+	-1420,  1420,  1420, -1420, -1420,  1420,  1420, -1420, 
+	 -805,   805,   805,  -805,  -805,   805,   805,  -805, 
+	  805,  -805,  -805,   805,   805,  -805,  -805,   805, 
+	 1420, -1420, -1420,  1420,  1420, -1420, -1420,  1420, 
+	  283,  -283,  -283,   283,   283,  -283,  -283,   283, 
+	-1204,  1204,  1204, -1204, -1204,  1204,  1204, -1204, 
+	  946, -1670,   332,  1416, -1416,  -332,  1670,  -946, 
+	 -222,   392,   -78,  -332,   332,    78,  -392,   222, 
+	-1116,  1970,  -392, -1670,  1670,   392, -1970,  1116, 
+	 -632,  1116,  -222,  -946,   946,   222, -1116,   632, 
+	  632, -1116,   222,   946,  -946,  -222,  1116,  -632, 
+	 1116, -1970,   392,  1670, -1670,  -392,  1970, -1116, 
+	  222,  -392,    78,   332,  -332,   -78,   392,  -222, 
+	 -946,  1670,  -332, -1416,  1416,   332, -1670,   946, 
+	  652, -1573,  1573,  -652,  -652,  1573, -1573,   652, 
+	 -153,   369,  -369,   153,   153,  -369,   369,  -153, 
+	 -769,  1856, -1856,   769,   769, -1856,  1856,  -769, 
+	 -435,  1051, -1051,   435,   435, -1051,  1051,  -435, 
+	  435, -1051,  1051,  -435,  -435,  1051, -1051,   435, 
+	  769, -1856,  1856,  -769,  -769,  1856, -1856,   769, 
+	  153,  -369,   369,  -153,  -153,   369,  -369,   153, 
+	 -652,  1573, -1573,   652,   652, -1573,  1573,  -652, 
+	  332,  -946,  1416, -1670,  1670, -1416,   946,  -332, 
+	  -78,   222,  -332,   392,  -392,   332,  -222,    78, 
+	 -392,  1116, -1670,  1970, -1970,  1670, -1116,   392, 
+	 -222,   632,  -946,  1116, -1116,   946,  -632,   222, 
+	  222,  -632,   946, -1116,  1116,  -946,   632,  -222, 
+	  392, -1116,  1670, -1970,  1970, -1670,  1116,  -392, 
+	   78,  -222,   332,  -392,   392,  -332,   222,   -78, 
+	 -332,   946, -1416,  1670, -1670,  1416,  -946,   332, 
+	 1024,  1024,  1024,  1024,  1024,  1024,  1024,  1024, 
+	-1024, -1024, -1024, -1024, -1024, -1024, -1024, -1024, 
+	-1024, -1024, -1024, -1024, -1024, -1024, -1024, -1024, 
+	 1024,  1024,  1024,  1024,  1024,  1024,  1024,  1024, 
+	 1024,  1024,  1024,  1024,  1024,  1024,  1024,  1024, 
+	-1024, -1024, -1024, -1024, -1024, -1024, -1024, -1024, 
+	-1024, -1024, -1024, -1024, -1024, -1024, -1024, -1024, 
+	 1024,  1024,  1024,  1024,  1024,  1024,  1024,  1024, 
+	 1420,  1204,   805,   283,  -283,  -805, -1204, -1420, 
+	-1420, -1204,  -805,  -283,   283,   805,  1204,  1420, 
+	-1420, -1204,  -805,  -283,   283,   805,  1204,  1420, 
+	 1420,  1204,   805,   283,  -283,  -805, -1204, -1420, 
+	 1420,  1204,   805,   283,  -283,  -805, -1204, -1420, 
+	-1420, -1204,  -805,  -283,   283,   805,  1204,  1420, 
+	-1420, -1204,  -805,  -283,   283,   805,  1204,  1420, 
+	 1420,  1204,   805,   283,  -283,  -805, -1204, -1420, 
+	 1338,   554,  -554, -1338, -1338,  -554,   554,  1338, 
+	-1338,  -554,   554,  1338,  1338,   554,  -554, -1338, 
+	-1338,  -554,   554,  1338,  1338,   554,  -554, -1338, 
+	 1338,   554,  -554, -1338, -1338,  -554,   554,  1338, 
+	 1338,   554,  -554, -1338, -1338,  -554,   554,  1338, 
+	-1338,  -554,   554,  1338,  1338,   554,  -554, -1338, 
+	-1338,  -554,   554,  1338,  1338,   554,  -554, -1338, 
+	 1338,   554,  -554, -1338, -1338,  -554,   554,  1338, 
+	 1204,  -283, -1420,  -805,   805,  1420,   283, -1204, 
+	-1204,   283,  1420,   805,  -805, -1420,  -283,  1204, 
+	-1204,   283,  1420,   805,  -805, -1420,  -283,  1204, 
+	 1204,  -283, -1420,  -805,   805,  1420,   283, -1204, 
+	 1204,  -283, -1420,  -805,   805,  1420,   283, -1204, 
+	-1204,   283,  1420,   805,  -805, -1420,  -283,  1204, 
+	-1204,   283,  1420,   805,  -805, -1420,  -283,  1204, 
+	 1204,  -283, -1420,  -805,   805,  1420,   283, -1204, 
+	 1024, -1024, -1024,  1024,  1024, -1024, -1024,  1024, 
+	-1024,  1024,  1024, -1024, -1024,  1024,  1024, -1024, 
+	-1024,  1024,  1024, -1024, -1024,  1024,  1024, -1024, 
+	 1024, -1024, -1024,  1024,  1024, -1024, -1024,  1024, 
+	 1024, -1024, -1024,  1024,  1024, -1024, -1024,  1024, 
+	-1024,  1024,  1024, -1024, -1024,  1024,  1024, -1024, 
+	-1024,  1024,  1024, -1024, -1024,  1024,  1024, -1024, 
+	 1024, -1024, -1024,  1024,  1024, -1024, -1024,  1024, 
+	  805, -1420,   283,  1204, -1204,  -283,  1420,  -805, 
+	 -805,  1420,  -283, -1204,  1204,   283, -1420,   805, 
+	 -805,  1420,  -283, -1204,  1204,   283, -1420,   805, 
+	  805, -1420,   283,  1204, -1204,  -283,  1420,  -805, 
+	  805, -1420,   283,  1204, -1204,  -283,  1420,  -805, 
+	 -805,  1420,  -283, -1204,  1204,   283, -1420,   805, 
+	 -805,  1420,  -283, -1204,  1204,   283, -1420,   805, 
+	  805, -1420,   283,  1204, -1204,  -283,  1420,  -805, 
+	  554, -1338,  1338,  -554,  -554,  1338, -1338,   554, 
+	 -554,  1338, -1338,   554,   554, -1338,  1338,  -554, 
+	 -554,  1338, -1338,   554,   554, -1338,  1338,  -554, 
+	  554, -1338,  1338,  -554,  -554,  1338, -1338,   554, 
+	  554, -1338,  1338,  -554,  -554,  1338, -1338,   554, 
+	 -554,  1338, -1338,   554,   554, -1338,  1338,  -554, 
+	 -554,  1338, -1338,   554,   554, -1338,  1338,  -554, 
+	  554, -1338,  1338,  -554,  -554,  1338, -1338,   554, 
+	  283,  -805,  1204, -1420,  1420, -1204,   805,  -283, 
+	 -283,   805, -1204,  1420, -1420,  1204,  -805,   283, 
+	 -283,   805, -1204,  1420, -1420,  1204,  -805,   283, 
+	  283,  -805,  1204, -1420,  1420, -1204,   805,  -283, 
+	  283,  -805,  1204, -1420,  1420, -1204,   805,  -283, 
+	 -283,   805, -1204,  1420, -1420,  1204,  -805,   283, 
+	 -283,   805, -1204,  1420, -1420,  1204,  -805,   283, 
+	  283,  -805,  1204, -1420,  1420, -1204,   805,  -283, 
+	  805,   805,   805,   805,   805,   805,   805,   805, 
+	-1420, -1420, -1420, -1420, -1420, -1420, -1420, -1420, 
+	  283,   283,   283,   283,   283,   283,   283,   283, 
+	 1204,  1204,  1204,  1204,  1204,  1204,  1204,  1204, 
+	-1204, -1204, -1204, -1204, -1204, -1204, -1204, -1204, 
+	 -283,  -283,  -283,  -283,  -283,  -283,  -283,  -283, 
+	 1420,  1420,  1420,  1420,  1420,  1420,  1420,  1420, 
+	 -805,  -805,  -805,  -805,  -805,  -805,  -805,  -805, 
+	 1116,   946,   632,   222,  -222,  -632,  -946, -1116, 
+	-1970, -1670, -1116,  -392,   392,  1116,  1670,  1970, 
+	  392,   332,   222,    78,   -78,  -222,  -332,  -392, 
+	 1670,  1416,   946,   332,  -332,  -946, -1416, -1670, 
+	-1670, -1416,  -946,  -332,   332,   946,  1416,  1670, 
+	 -392,  -332,  -222,   -78,    78,   222,   332,   392, 
+	 1970,  1670,  1116,   392,  -392, -1116, -1670, -1970, 
+	-1116,  -946,  -632,  -222,   222,   632,   946,  1116, 
+	 1051,   435,  -435, -1051, -1051,  -435,   435,  1051, 
+	-1856,  -769,   769,  1856,  1856,   769,  -769, -1856, 
+	  369,   153,  -153,  -369,  -369,  -153,   153,   369, 
+	 1573,   652,  -652, -1573, -1573,  -652,   652,  1573, 
+	-1573,  -652,   652,  1573,  1573,   652,  -652, -1573, 
+	 -369,  -153,   153,   369,   369,   153,  -153,  -369, 
+	 1856,   769,  -769, -1856, -1856,  -769,   769,  1856, 
+	-1051,  -435,   435,  1051,  1051,   435,  -435, -1051, 
+	  946,  -222, -1116,  -632,   632,  1116,   222,  -946, 
+	-1670,   392,  1970,  1116, -1116, -1970,  -392,  1670, 
+	  332,   -78,  -392,  -222,   222,   392,    78,  -332, 
+	 1416,  -332, -1670,  -946,   946,  1670,   332, -1416, 
+	-1416,   332,  1670,   946,  -946, -1670,  -332,  1416, 
+	 -332,    78,   392,   222,  -222,  -392,   -78,   332, 
+	 1670,  -392, -1970, -1116,  1116,  1970,   392, -1670, 
+	 -946,   222,  1116,   632,  -632, -1116,  -222,   946, 
+	  805,  -805,  -805,   805,   805,  -805,  -805,   805, 
+	-1420,  1420,  1420, -1420, -1420,  1420,  1420, -1420, 
+	  283,  -283,  -283,   283,   283,  -283,  -283,   283, 
+	 1204, -1204, -1204,  1204,  1204, -1204, -1204,  1204, 
+	-1204,  1204,  1204, -1204, -1204,  1204,  1204, -1204, 
+	 -283,   283,   283,  -283,  -283,   283,   283,  -283, 
+	 1420, -1420, -1420,  1420,  1420, -1420, -1420,  1420, 
+	 -805,   805,   805,  -805,  -805,   805,   805,  -805, 
+	  632, -1116,   222,   946,  -946,  -222,  1116,  -632, 
+	-1116,  1970,  -392, -1670,  1670,   392, -1970,  1116, 
+	  222,  -392,    78,   332,  -332,   -78,   392,  -222, 
+	  946, -1670,   332,  1416, -1416,  -332,  1670,  -946, 
+	 -946,  1670,  -332, -1416,  1416,   332, -1670,   946, 
+	 -222,   392,   -78,  -332,   332,    78,  -392,   222, 
+	 1116, -1970,   392,  1670, -1670,  -392,  1970, -1116, 
+	 -632,  1116,  -222,  -946,   946,   222, -1116,   632, 
+	  435, -1051,  1051,  -435,  -435,  1051, -1051,   435, 
+	 -769,  1856, -1856,   769,   769, -1856,  1856,  -769, 
+	  153,  -369,   369,  -153,  -153,   369,  -369,   153, 
+	  652, -1573,  1573,  -652,  -652,  1573, -1573,   652, 
+	 -652,  1573, -1573,   652,   652, -1573,  1573,  -652, 
+	 -153,   369,  -369,   153,   153,  -369,   369,  -153, 
+	  769, -1856,  1856,  -769,  -769,  1856, -1856,   769, 
+	 -435,  1051, -1051,   435,   435, -1051,  1051,  -435, 
+	  222,  -632,   946, -1116,  1116,  -946,   632,  -222, 
+	 -392,  1116, -1670,  1970, -1970,  1670, -1116,   392, 
+	   78,  -222,   332,  -392,   392,  -332,   222,   -78, 
+	  332,  -946,  1416, -1670,  1670, -1416,   946,  -332, 
+	 -332,   946, -1416,  1670, -1670,  1416,  -946,   332, 
+	  -78,   222,  -332,   392,  -392,   332,  -222,    78, 
+	  392, -1116,  1670, -1970,  1970, -1670,  1116,  -392, 
+	 -222,   632,  -946,  1116, -1116,   946,  -632,   222, 
+	  554,   554,   554,   554,   554,   554,   554,   554, 
+	-1338, -1338, -1338, -1338, -1338, -1338, -1338, -1338, 
+	 1338,  1338,  1338,  1338,  1338,  1338,  1338,  1338, 
+	 -554,  -554,  -554,  -554,  -554,  -554,  -554,  -554, 
+	 -554,  -554,  -554,  -554,  -554,  -554,  -554,  -554, 
+	 1338,  1338,  1338,  1338,  1338,  1338,  1338,  1338, 
+	-1338, -1338, -1338, -1338, -1338, -1338, -1338, -1338, 
+	  554,   554,   554,   554,   554,   554,   554,   554, 
+	  769,   652,   435,   153,  -153,  -435,  -652,  -769, 
+	-1856, -1573, -1051,  -369,   369,  1051,  1573,  1856, 
+	 1856,  1573,  1051,   369,  -369, -1051, -1573, -1856, 
+	 -769,  -652,  -435,  -153,   153,   435,   652,   769, 
+	 -769,  -652,  -435,  -153,   153,   435,   652,   769, 
+	 1856,  1573,  1051,   369,  -369, -1051, -1573, -1856, 
+	-1856, -1573, -1051,  -369,   369,  1051,  1573,  1856, 
+	  769,   652,   435,   153,  -153,  -435,  -652,  -769, 
+	  724,   300,  -300,  -724,  -724,  -300,   300,   724, 
+	-1748,  -724,   724,  1748,  1748,   724,  -724, -1748, 
+	 1748,   724,  -724, -1748, -1748,  -724,   724,  1748, 
+	 -724,  -300,   300,   724,   724,   300,  -300,  -724, 
+	 -724,  -300,   300,   724,   724,   300,  -300,  -724, 
+	 1748,   724,  -724, -1748, -1748,  -724,   724,  1748, 
+	-1748,  -724,   724,  1748,  1748,   724,  -724, -1748, 
+	  724,   300,  -300,  -724,  -724,  -300,   300,   724, 
+	  652,  -153,  -769,  -435,   435,   769,   153,  -652, 
+	-1573,   369,  1856,  1051, -1051, -1856,  -369,  1573, 
+	 1573,  -369, -1856, -1051,  1051,  1856,   369, -1573, 
+	 -652,   153,   769,   435,  -435,  -769,  -153,   652, 
+	 -652,   153,   769,   435,  -435,  -769,  -153,   652, 
+	 1573,  -369, -1856, -1051,  1051,  1856,   369, -1573, 
+	-1573,   369,  1856,  1051, -1051, -1856,  -369,  1573, 
+	  652,  -153,  -769,  -435,   435,   769,   153,  -652, 
+	  554,  -554,  -554,   554,   554,  -554,  -554,   554, 
+	-1338,  1338,  1338, -1338, -1338,  1338,  1338, -1338, 
+	 1338, -1338, -1338,  1338,  1338, -1338, -1338,  1338, 
+	 -554,   554,   554,  -554,  -554,   554,   554,  -554, 
+	 -554,   554,   554,  -554,  -554,   554,   554,  -554, 
+	 1338, -1338, -1338,  1338,  1338, -1338, -1338,  1338, 
+	-1338,  1338,  1338, -1338, -1338,  1338,  1338, -1338, 
+	  554,  -554,  -554,   554,   554,  -554,  -554,   554, 
+	  435,  -769,   153,   652,  -652,  -153,   769,  -435, 
+	-1051,  1856,  -369, -1573,  1573,   369, -1856,  1051, 
+	 1051, -1856,   369,  1573, -1573,  -369,  1856, -1051, 
+	 -435,   769,  -153,  -652,   652,   153,  -769,   435, 
+	 -435,   769,  -153,  -652,   652,   153,  -769,   435, 
+	 1051, -1856,   369,  1573, -1573,  -369,  1856, -1051, 
+	-1051,  1856,  -369, -1573,  1573,   369, -1856,  1051, 
+	  435,  -769,   153,   652,  -652,  -153,   769,  -435, 
+	  300,  -724,   724,  -300,  -300,   724,  -724,   300, 
+	 -724,  1748, -1748,   724,   724, -1748,  1748,  -724, 
+	  724, -1748,  1748,  -724,  -724,  1748, -1748,   724, 
+	 -300,   724,  -724,   300,   300,  -724,   724,  -300, 
+	 -300,   724,  -724,   300,   300,  -724,   724,  -300, 
+	  724, -1748,  1748,  -724,  -724,  1748, -1748,   724, 
+	 -724,  1748, -1748,   724,   724, -1748,  1748,  -724, 
+	  300,  -724,   724,  -300,  -300,   724,  -724,   300, 
+	  153,  -435,   652,  -769,   769,  -652,   435,  -153, 
+	 -369,  1051, -1573,  1856, -1856,  1573, -1051,   369, 
+	  369, -1051,  1573, -1856,  1856, -1573,  1051,  -369, 
+	 -153,   435,  -652,   769,  -769,   652,  -435,   153, 
+	 -153,   435,  -652,   769,  -769,   652,  -435,   153, 
+	  369, -1051,  1573, -1856,  1856, -1573,  1051,  -369, 
+	 -369,  1051, -1573,  1856, -1856,  1573, -1051,   369, 
+	  153,  -435,   652,  -769,   769,  -652,   435,  -153, 
+	  283,   283,   283,   283,   283,   283,   283,   283, 
+	 -805,  -805,  -805,  -805,  -805,  -805,  -805,  -805, 
+	 1204,  1204,  1204,  1204,  1204,  1204,  1204,  1204, 
+	-1420, -1420, -1420, -1420, -1420, -1420, -1420, -1420, 
+	 1420,  1420,  1420,  1420,  1420,  1420,  1420,  1420, 
+	-1204, -1204, -1204, -1204, -1204, -1204, -1204, -1204, 
+	  805,   805,   805,   805,   805,   805,   805,   805, 
+	 -283,  -283,  -283,  -283,  -283,  -283,  -283,  -283, 
+	  392,   332,   222,    78,   -78,  -222,  -332,  -392, 
+	-1116,  -946,  -632,  -222,   222,   632,   946,  1116, 
+	 1670,  1416,   946,   332,  -332,  -946, -1416, -1670, 
+	-1970, -1670, -1116,  -392,   392,  1116,  1670,  1970, 
+	 1970,  1670,  1116,   392,  -392, -1116, -1670, -1970, 
+	-1670, -1416,  -946,  -332,   332,   946,  1416,  1670, 
+	 1116,   946,   632,   222,  -222,  -632,  -946, -1116, 
+	 -392,  -332,  -222,   -78,    78,   222,   332,   392, 
+	  369,   153,  -153,  -369,  -369,  -153,   153,   369, 
+	-1051,  -435,   435,  1051,  1051,   435,  -435, -1051, 
+	 1573,   652,  -652, -1573, -1573,  -652,   652,  1573, 
+	-1856,  -769,   769,  1856,  1856,   769,  -769, -1856, 
+	 1856,   769,  -769, -1856, -1856,  -769,   769,  1856, 
+	-1573,  -652,   652,  1573,  1573,   652,  -652, -1573, 
+	 1051,   435,  -435, -1051, -1051,  -435,   435,  1051, 
+	 -369,  -153,   153,   369,   369,   153,  -153,  -369, 
+	  332,   -78,  -392,  -222,   222,   392,    78,  -332, 
+	 -946,   222,  1116,   632,  -632, -1116,  -222,   946, 
+	 1416,  -332, -1670,  -946,   946,  1670,   332, -1416, 
+	-1670,   392,  1970,  1116, -1116, -1970,  -392,  1670, 
+	 1670,  -392, -1970, -1116,  1116,  1970,   392, -1670, 
+	-1416,   332,  1670,   946,  -946, -1670,  -332,  1416, 
+	  946,  -222, -1116,  -632,   632,  1116,   222,  -946, 
+	 -332,    78,   392,   222,  -222,  -392,   -78,   332, 
+	  283,  -283,  -283,   283,   283,  -283,  -283,   283, 
+	 -805,   805,   805,  -805,  -805,   805,   805,  -805, 
+	 1204, -1204, -1204,  1204,  1204, -1204, -1204,  1204, 
+	-1420,  1420,  1420, -1420, -1420,  1420,  1420, -1420, 
+	 1420, -1420, -1420,  1420,  1420, -1420, -1420,  1420, 
+	-1204,  1204,  1204, -1204, -1204,  1204,  1204, -1204, 
+	  805,  -805,  -805,   805,   805,  -805,  -805,   805, 
+	 -283,   283,   283,  -283,  -283,   283,   283,  -283, 
+	  222,  -392,    78,   332,  -332,   -78,   392,  -222, 
+	 -632,  1116,  -222,  -946,   946,   222, -1116,   632, 
+	  946, -1670,   332,  1416, -1416,  -332,  1670,  -946, 
+	-1116,  1970,  -392, -1670,  1670,   392, -1970,  1116, 
+	 1116, -1970,   392,  1670, -1670,  -392,  1970, -1116, 
+	 -946,  1670,  -332, -1416,  1416,   332, -1670,   946, 
+	  632, -1116,   222,   946,  -946,  -222,  1116,  -632, 
+	 -222,   392,   -78,  -332,   332,    78,  -392,   222, 
+	  153,  -369,   369,  -153,  -153,   369,  -369,   153, 
+	 -435,  1051, -1051,   435,   435, -1051,  1051,  -435, 
+	  652, -1573,  1573,  -652,  -652,  1573, -1573,   652, 
+	 -769,  1856, -1856,   769,   769, -1856,  1856,  -769, 
+	  769, -1856,  1856,  -769,  -769,  1856, -1856,   769, 
+	 -652,  1573, -1573,   652,   652, -1573,  1573,  -652, 
+	  435, -1051,  1051,  -435,  -435,  1051, -1051,   435, 
+	 -153,   369,  -369,   153,   153,  -369,   369,  -153, 
+	   78,  -222,   332,  -392,   392,  -332,   222,   -78, 
+	 -222,   632,  -946,  1116, -1116,   946,  -632,   222, 
+	  332,  -946,  1416, -1670,  1670, -1416,   946,  -332, 
+	 -392,  1116, -1670,  1970, -1970,  1670, -1116,   392, 
+	  392, -1116,  1670, -1970,  1970, -1670,  1116,  -392, 
+	 -332,   946, -1416,  1670, -1670,  1416,  -946,   332, 
+	  222,  -632,   946, -1116,  1116,  -946,   632,  -222, 
+	  -78,   222,  -332,   392,  -392,   332,  -222,    78, 
+};
+
+// precalculated int base values for 8x8 DCT, multplied by 8192
+const int icos_base_8x8[ 64 ] =
+{
+	 8192,  8192,  8192,  8192,  8192,  8192,  8192,  8192, 
+	11363,  9633,  6436,  2260, -2260, -6436, -9633, -11363, 
+	10703,  4433, -4433, -10703, -10703, -4433,  4433, 10703, 
+	 9633, -2260, -11363, -6436,  6436, 11363,  2260, -9633, 
+	 8192, -8192, -8192,  8192,  8192, -8192, -8192,  8192, 
+	 6436, -11363,  2260,  9633, -9633, -2260, 11363, -6436, 
+	 4433, -10703, 10703, -4433, -4433, 10703, -10703,  4433, 
+	 2260, -6436,  9633, -11363, 11363, -9633,  6436, -2260, 
+};
+
+// precalculated int values for 1x8 IDCT, multplied by 8192
+const int icos_idct_1x8[ 64 ] =
+{
+	 1024,  1420,  1338,  1204,  1024,   805,   554,   283, 
+	 1024,  1204,   554,  -283, -1024, -1420, -1338,  -805, 
+	 1024,   805,  -554, -1420, -1024,   283,  1338,  1204, 
+	 1024,   283, -1338,  -805,  1024,  1204,  -554, -1420, 
+	 1024,  -283, -1338,   805,  1024, -1204,  -554,  1420, 
+	 1024,  -805,  -554,  1420, -1024,  -283,  1338, -1204, 
+	 1024, -1204,   554,   283, -1024,  1420, -1338,   805, 
+	 1024, -1420,  1338, -1204,  1024,  -805,   554,  -283, 
+};
+
+// precalculated int values for 1x8 FDCT, multplied by 8192
+const int icos_fdct_1x8[ 64 ] =
+{
+	 8192,  8192,  8192,  8192,  8192,  8192,  8192,  8192, 
+	11363,  9633,  6436,  2260, -2260, -6436, -9633, -11363, 
+	10703,  4433, -4433, -10703, -10703, -4433,  4433, 10703, 
+	 9633, -2260, -11363, -6436,  6436, 11363,  2260, -9633, 
+	 8192, -8192, -8192,  8192,  8192, -8192, -8192,  8192, 
+	 6436, -11363,  2260,  9633, -9633, -2260, 11363, -6436, 
+	 4433, -10703, 10703, -4433, -4433, 10703, -10703,  4433, 
+	 2260, -6436,  9633, -11363, 11363, -9633,  6436, -2260, 
+};
+
+
+// dct functions follow, you need to rescale the results using DCT_RESCALE
+
+/* -----------------------------------------------
+	inverse 8x8 DCT transform
+	----------------------------------------------- */
+inline int idct_2d_fst_8x8( signed short* F, int ix, int iy )
+{
+	int idct;
+	int ixy;
+	int i;
+	
+	
+	// calculate start index
+	ixy = ( ( iy * 8 ) + ix ) * 64;
+	
+	// begin transform
+	idct = 0;
+	for ( i = 0; i < 64; i++ )
+		idct += F[ i ] * icos_idct_8x8[ ixy++ ];
+	
+	
+	return idct;
+}
+
+/* -----------------------------------------------
+	forward 8x8 DCT transform
+	----------------------------------------------- */
+inline int fdct_2d_fst_8x8( unsigned char* f, int iu, int iv )
+{
+	int fdct;
+	int iuv;
+	int i;
+	
+	
+	// calculate start index
+	iuv = ( ( iv * 8 ) + iu ) * 64;
+	
+	// begin transform
+	fdct = 0;
+	for ( i = 0; i < 64; i++ )
+		fdct += f[ i ] * icos_fdct_8x8[ iuv++ ];
+	
+	
+	return fdct;
+}
+
+/* -----------------------------------------------
+	inverse 1D-8 DCT transform
+	----------------------------------------------- */
+inline int idct_1d_fst_8( signed short* F, int ix )
+{
+	int idct;
+	int i;
+	
+	
+	// calculate start index
+	ix *= 8;
+	
+	// begin transform
+	idct = 0;
+	for ( i = 0; i < 8; i++ )
+		idct += F[ i ] * icos_idct_1x8[ ix++ ];
+	
+	
+	return idct;
+}
+
+/* -----------------------------------------------
+	forward 1D-8 DCT transform
+	----------------------------------------------- */
+inline int fdct_1d_fst_8( unsigned char* f, int iu )
+{
+	int fdct;
+	int i;
+	
+	
+	// calculate start index
+	iu *= 8;
+	
+	// begin transform
+	fdct = 0;
+	for ( i = 0; i < 8; i++ )
+		fdct += f[ i ] * icos_fdct_1x8[ iu++ ];
+	
+	
+	return fdct;
+}
+
diff --git a/software/packJPG_library/source/file_icon.ico b/software/packJPG_library/source/file_icon.ico
new file mode 100644
index 0000000..9fa97ee
Binary files /dev/null and b/software/packJPG_library/source/file_icon.ico differ
diff --git a/software/packJPG_library/source/icons.rc b/software/packJPG_library/source/icons.rc
new file mode 100644
index 0000000..db53cd0
--- /dev/null
+++ b/software/packJPG_library/source/icons.rc
@@ -0,0 +1,2 @@
+app      ICON    app_icon.ico
+ICONS1   ICON    file_icon.ico
\ No newline at end of file
diff --git a/software/packJPG_library/source/icons.res b/software/packJPG_library/source/icons.res
new file mode 100644
index 0000000..8217b38
Binary files /dev/null and b/software/packJPG_library/source/icons.res differ
diff --git a/software/packJPG_library/source/packjpg.cpp b/software/packJPG_library/source/packjpg.cpp
new file mode 100644
index 0000000..6f34cda
--- /dev/null
+++ b/software/packJPG_library/source/packjpg.cpp
@@ -0,0 +1,7263 @@
+/*
+packJPG v2.5k (01/22/2016)
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+packJPG is a compression program specially designed for further
+compression of JPEG images without causing any further loss. Typically
+it reduces the file size of a JPEG file by 20%.
+
+
+LGPL v3 license and special permissions
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+All programs in this package are free software; you can redistribute 
+them and/or modify them under the terms of the GNU Lesser General Public 
+License as published by the Free Software Foundation; either version 3 
+of the License, or (at your option) any later version. 
+
+The package 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 Lesser 
+General Public License for more details at 
+http://www.gnu.org/copyleft/lgpl.html. 
+
+If the LGPL v3 license is not compatible with your software project you 
+might contact us and ask for a special permission to use the packJPG 
+library under different conditions. In any case, usage of the packJPG 
+algorithm under the LGPL v3 or above is highly advised and special 
+permissions will only be given where necessary on a case by case basis. 
+This offer is aimed mainly at closed source freeware developers seeking 
+to add PJG support to their software projects. 
+
+Copyright 2006...2014 by HTW Aalen University and Matthias Stirner.
+
+
+Usage of packJPG
+~~~~~~~~~~~~~~~~
+
+JPEG files are compressed and PJG files are decompressed using this
+command:
+
+ "packJPG [file(s)]"
+
+packJPG recognizes file types on its own and decides whether to compress
+(JPG) or decompress (PJG). For unrecognized file types no action is
+taken. Files are recognized by content, not by extension.
+
+packJPG supports wildcards like "*.*" and drag and drop of multiple 
+files. Filenames for output files are created automatically. In default
+mode, files are never overwritten. If a filename is already in use,
+packJPG creates a new filename by adding underscores.
+
+If "-" is used as a filename input from stdin is assumed and output is
+written to stdout. This can be useful for example if jpegtran is to be
+used as a preprocessor.
+
+Usage examples:
+
+ "packJPG *.pjg"
+ "packJPG lena.jpg"
+ "packJPG kodim??.jpg"
+ "packJPG - < sail.pjg > sail.jpg"
+
+
+Command line switches
+~~~~~~~~~~~~~~~~~~~~~
+
+ -ver  verify files after processing
+ -v?   level of verbosity; 0,1 or 2 is allowed (default 0)
+ -np   no pause after processing files
+ -o    overwrite existing files
+ -p    proceed on warnings
+ -d    discard meta-info
+
+By default, compression is cancelled on warnings. If warnings are 
+skipped by using "-p", most files with warnings can also be compressed, 
+but JPEG files reconstructed from PJG files might not be bitwise 
+identical with the original JPEG files. There won't be any loss to 
+image data or quality however.
+
+Unnecessary meta information can be discarded using "-d". This reduces 
+compressed files' sizes. Be warned though, reconstructed files won't be 
+bitwise identical with the original files and meta information will be 
+lost forever. As with "-p" there won't be any loss to image data or 
+quality. 
+
+There is no known case in which a file compressed by packJPG (without 
+the "-p" option, see above) couldn't be reconstructed to exactly the 
+state it was before. If you want an additional layer of safety you can 
+also use the verify option "-ver". In this mode, files are compressed, 
+then decompressed and the decompressed file compared to the original 
+file. If this test doesn't pass there will be an error message and the 
+compressed file won't be written to the drive. 
+
+Please note that the "-ver" option should never be used in conjunction 
+with the "-d" and/or "-p" options. As stated above, the "-p" and "-d" 
+options will most likely lead to reconstructed JPG files not being 
+bitwise identical to the original JPG files. In turn, the verification 
+process may fail on various files although nothing actually went wrong. 
+
+Usage examples:
+
+ "packJPG -v1 -o baboon.pjg"
+ "packJPG -ver lena.jpg"
+ "packJPG -d tiffany.jpg"
+ "packJPG -p *.jpg"
+
+
+Known Limitations 
+~~~~~~~~~~~~~~~~~ 
+
+packJPG is a compression program specially for JPEG files, so it doesn't 
+compress other file types.
+
+packJPG has low error tolerance. JPEG files might not work with packJPG 
+even if they work perfectly with other image processing software. The 
+command line switch "-p" can be used to increase error tolerance and 
+compatibility.
+
+If you try to drag and drop to many files at once, there might be a 
+windowed error message about missing privileges. In that case you can 
+try it again with less files or consider using the command prompt. 
+packJPG has been tested to work perfectly with thousands of files from 
+the command line. This issue also happens with drag and drop in other 
+applications, so it might not be a limitation of packJPG but a 
+limitation of Windows.
+
+Compressed PJG files are not compatible between different packJPG 
+versions. You will get an error message if you try to decompress PJG 
+files with a different version than the one used for compression. You 
+may download older versions of packJPG from:
+http://www.elektronik.htw-aalen.de/packJPG/binaries/old/
+
+
+Open source release / developer info
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The packJPG source codes is found inside the "source" subdirectory. 
+Additional documents aimed to developers, containing detailed 
+instructions on compiling the source code and using special 
+functionality, are included in the "packJPG" subdirectory. 
+ 
+
+History
+~~~~~~~
+
+v1.9a (04/20/2007) (non public)
+ - first released version
+ - only for testing purposes
+
+v2.0  (05/28/2007) (public)
+ - first public version of packJPG
+ - minor improvements to overall compression
+ - minor bugfixes
+
+v2.2  (08/05/2007) (public)
+ - around 40% faster compression & decompression
+ - major improvements to overall compression (around 2% on average)
+ - reading from stdin, writing to stdout
+ - smaller executable
+ - minor bugfixes
+ - various minor improvements
+ 
+v2.3  (09/18/2007) (public)
+ - compatibility with JPEG progressive mode
+ - compatibility with JPEG extended sequential mode
+ - compatibility with the CMYK color space
+ - compatibility with older CPUs
+ - around 15% faster compression & decompression 
+ - new switch: [-d] (discard meta-info)
+ - various bugfixes
+
+v2.3a (11/21/2007) (public)
+ - crash issue with certain images fixed
+ - compatibility with packJPG v2.3 maintained
+
+v2.3b (12/20/2007) (public)
+ - some minor errors in the packJPG library fixed
+ - compatibility with packJPG v2.3 maintained
+ 
+v2.4 (03/24/2010) (public)
+ - major improvements (1%...2%) to overall compression
+ - around 10% faster compression & decompression
+ - major improvements to JPG compatibility
+ - size of executable reduced to ~33%
+ - new switch: [-ver] (verify file after processing)
+ - new switch: [-np] (no pause after processing)
+ - new progress bar output mode
+ - arithmetic coding routines rewritten from scratch
+ - various smaller improvements to numerous to list here
+ - new SFX (self extracting) archive format
+ 
+v2.5 (11/11/2011) (public)
+ - improvements (~0.5%) to overall compression
+ - several minor bugfixes
+ - major code cleanup
+ - removed packJPX from the package
+ - added packARC to the package
+ - packJPG is now open source!
+ 
+v2.5a (11/21/11) (public)
+ - source code compatibility improvements (Gerhard Seelmann)
+ - avoid some compiler warnings (Gerhard Seelmann)
+ - source code clean up (Gerhard Seelmann)
+ 
+v2.5b (01/27/12) (public)
+ - further removal of redundant code
+ - some fixes for the packJPG static library
+ - compiler fix for Mac OS (thanks to Sergio Lopez)
+ - improved compression ratio calculation
+ - eliminated the need for temp files
+ 
+v2.5c (04/13/12) (public)
+ - various source code optimizations
+ 
+v2.5d (07/03/12) (public)
+ - fixed a rare bug with progressive JPEG
+ 
+v2.5e (07/03/12) (public)
+ - some minor source code optimizations
+ - changed packJPG licensing to LGPL
+ - moved packARC to a separate package
+ 
+v2.5f (02/24/13) (public)
+ - fixed a minor bug in the JPG parser (thanks to Stephan Busch)
+ 
+v2.5g (09/14/13) (public)
+ - fixed a rare crash bug with manipulated JPEG files
+ 
+v2.5h (12/07/13) (public)
+ - added a warning for inefficient huffman coding (thanks to Moinak Ghosh)
+ 
+v2.5i (12/26/13) (public)
+ - fixed possible crash with malformed JPEG (thanks to Moinak Ghosh)
+ 
+v2.5j (01/15/14) (public)
+ - various source code optimizations (using cppcheck)
+
+v2.5k (01/22/16) (public)
+ - Updated contact info
+ - fixed a minor bug
+
+
+Acknowledgements
+~~~~~~~~~~~~~~~~
+
+packJPG is the result of countless hours of research and development. It
+is part of my final year project for Hochschule Aalen.
+
+Prof. Dr. Gerhard Seelmann from Hochschule Aalen supported my
+development of packJPG with his extensive knowledge in the field of data
+compression. Without his advice, packJPG would not be possible.
+
+The official developer blog for packJPG is hosted by encode.ru.
+
+packJPG logo and icon are designed by Michael Kaufmann.
+
+
+Contact
+~~~~~~~
+
+The official developer blog for packJPG:
+ http://packjpg.encode.ru/
+ 
+For questions and bug reports:
+ packjpg (at) matthiasstirner.com
+
+
+____________________________________
+packJPG by Matthias Stirner, 01/2016
+*/
+
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <string>
+#include <cmath>
+#include <ctime>
+#include <memory>
+#include <stdexcept>
+#include <vector>
+
+#include "bitops.h"
+#include "aricoder.h"
+#include "pjpgtbl.h"
+#include "dct8x8.h"
+
+#if defined BUILD_DLL // define BUILD_LIB from the compiler options if you want to compile a DLL!
+	#define BUILD_LIB
+#endif
+
+#if defined BUILD_LIB // define BUILD_LIB as compiler option if you want to compile a library!
+	#include "packjpglib.h"
+#endif
+
+#define INTERN static
+
+#define INIT_MODEL_S(a,b,c) new model_s( a, b, c, 255 )
+#define INIT_MODEL_B(a,b)   new model_b( a, b, 255 )
+
+// #define USE_PLOCOI // uncomment to use loco-i predictor instead of 1DDCT predictor
+// #define DEV_BUILD // uncomment to include developer functions
+// #define DEV_INFOS // uncomment to include developer information
+
+#define QUANT(cm,bp)	( cmpnfo[cm].qtable[ bp ] )
+#define MAX_V(cm,bp)	( ( QUANT(cm,bp) > 0 ) ? ( ( freqmax[bp] + QUANT(cm,bp) - 1 ) /  QUANT(cm,bp) ) : 0 )
+// #define QUN_V(v,cm,bp)	( ( QUANT(cm,bp) > 0 ) ? ( ( v > 0 ) ? ( v + (QUANT(cm,bp)/2) ) /  QUANT(cm,bp) : ( v - (QUANT(cm,bp)/2) ) /  QUANT(cm,bp) ) : 0 )
+
+#define ENVLI(s,v)		( ( v > 0 ) ? v : ( v - 1 ) + ( 1 << s ) )
+#define DEVLI(s,n)		( ( n >= ( 1 << (s - 1) ) ) ? n : n + 1 - ( 1 << s ) )
+#define E_ENVLI(s,v)	( v - ( 1 << s ) )
+#define E_DEVLI(s,n)	( n + ( 1 << s ) )
+
+#define ABS(v1)			( (v1 < 0) ? -v1 : v1 )
+#define ABSDIFF(v1,v2)	( (v1 > v2) ? (v1 - v2) : (v2 - v1) )
+#define IPOS(w,v,h)		( ( v * w ) + h )
+#define NPOS(n1,n2,p)	( ( ( p / n1 ) * n2 ) + ( p % n1 ) )
+#define ROUND_F(v1)		( (v1 < 0) ? (int) (v1 - 0.5) : (int) (v1 + 0.5) )
+#define DIV_INT(v1,v2)	( (v1 < 0) ? (v1 - (v2>>1)) / v2 : (v1 + (v2>>1)) / v2 )
+#define B_SHORT(v1,v2)	( ( ((int) v1) << 8 ) + ((int) v2) )
+#define BITLEN1024P(v)	( pbitlen_0_1024[ v ] )
+#define BITLEN2048N(v)	( (pbitlen_n2048_2047+2048)[ v ] )
+#define CLAMPED(l,h,v)	( ( v < l ) ? l : ( v > h ) ? h : v )
+
+#define MEM_ERRMSG	"out of memory error"
+#define FRD_ERRMSG	"could not read file / file not found: %s"
+#define FWR_ERRMSG	"could not write file / file write-protected: %s"
+#define MSG_SIZE	128
+#define BARLEN		36
+
+// special realloc with guaranteed free() of previous memory
+static inline void* frealloc( void* ptr, size_t size ) {
+	void* n_ptr = realloc( ptr, (size) ? size : 1 );
+	if ( n_ptr == NULL ) free( ptr );
+	return n_ptr;
+}
+
+
+
+/* -----------------------------------------------
+	struct declarations
+	----------------------------------------------- */
+
+struct componentInfo {
+	unsigned short* qtable; // quantization table
+	int huffdc; // no of huffman table (DC)
+	int huffac; // no of huffman table (AC)
+	int sfv; // sample factor vertical
+	int sfh; // sample factor horizontal	
+	int mbs; // blocks in mcu
+	int bcv; // block count vertical (interleaved)
+	int bch; // block count horizontal (interleaved)
+	int bc;  // block count (all) (interleaved)
+	int ncv; // block count vertical (non interleaved)
+	int nch; // block count horizontal (non interleaved)
+	int nc;  // block count (all) (non interleaved)
+	int sid; // statistical identity
+	int jid; // jpeg internal id
+};
+
+struct huffCodes {
+	unsigned short cval[ 256 ];
+	unsigned short clen[ 256 ];
+	unsigned short max_eobrun;
+};
+
+struct huffTree {
+	unsigned short l[ 256 ];
+	unsigned short r[ 256 ];
+};
+
+
+/* -----------------------------------------------
+	function declarations: main interface
+	----------------------------------------------- */
+#if !defined( BUILD_LIB )
+INTERN void initialize_options( int argc, char** argv );
+INTERN void process_ui( void );
+INTERN inline const char* get_status( bool (*function)() );
+INTERN void show_help( void );
+#endif
+INTERN void process_file( void );
+INTERN void execute( bool (*function)() );
+
+
+/* -----------------------------------------------
+	function declarations: main functions
+	----------------------------------------------- */
+#if !defined( BUILD_LIB )
+INTERN bool check_file( void );
+INTERN bool swap_streams( void );
+INTERN bool compare_output( void );
+#endif
+INTERN bool reset_buffers( void );
+INTERN bool read_jpeg( void );
+INTERN bool merge_jpeg( void );
+INTERN bool decode_jpeg( void );
+INTERN bool recode_jpeg( void );
+INTERN bool adapt_icos( void );
+INTERN bool predict_dc( void );
+INTERN bool unpredict_dc( void );
+INTERN bool check_value_range( void );
+INTERN bool calc_zdst_lists( void );
+INTERN bool pack_pjg( void );
+INTERN bool unpack_pjg( void );
+
+
+/* -----------------------------------------------
+	function declarations: jpeg-specific
+	----------------------------------------------- */
+
+INTERN bool jpg_setup_imginfo( void );
+INTERN bool jpg_parse_jfif( unsigned char type, unsigned int len, unsigned char* segment );
+INTERN bool jpg_rebuild_header( void );
+
+INTERN int jpg_decode_block_seq( BitReader* huffr, huffTree* dctree, huffTree* actree, short* block );
+INTERN int jpg_encode_block_seq( BitWriter* huffw, huffCodes* dctbl, huffCodes* actbl, short* block );
+
+INTERN int jpg_decode_dc_prg_fs( BitReader* huffr, huffTree* dctree, short* block );
+INTERN int jpg_encode_dc_prg_fs( BitWriter* huffw, huffCodes* dctbl, short* block );
+INTERN int jpg_decode_ac_prg_fs( BitReader* huffr, huffTree* actree, short* block,
+						int* eobrun, int from, int to );
+INTERN int jpg_encode_ac_prg_fs( BitWriter* huffw, huffCodes* actbl, short* block,
+						int* eobrun, int from, int to );
+
+INTERN int jpg_decode_dc_prg_sa( BitReader* huffr, short* block );
+INTERN int jpg_encode_dc_prg_sa( BitWriter* huffw, short* block );
+INTERN int jpg_decode_ac_prg_sa( BitReader* huffr, huffTree* actree, short* block,
+						int* eobrun, int from, int to );
+INTERN int jpg_encode_ac_prg_sa( BitWriter* huffw, std::vector<std::uint8_t>& storw, huffCodes* actbl,
+						short* block, int* eobrun, int from, int to );
+
+INTERN int jpg_decode_eobrun_sa( BitReader* huffr, short* block, int* eobrun, int from, int to );
+INTERN int jpg_encode_eobrun( BitWriter* huffw, huffCodes* actbl, int* eobrun );
+INTERN int jpg_encode_crbits( BitWriter* huffw, std::vector<std::uint8_t>& storw );
+
+INTERN int jpg_next_huffcode( BitReader *huffw, huffTree *ctree );
+INTERN int jpg_next_mcupos( int* mcu, int* cmp, int* csc, int* sub, int* dpos, int* rstw );
+INTERN int jpg_next_mcuposn( int* cmp, int* dpos, int* rstw );
+INTERN int jpg_skip_eobrun( int* cmp, int* dpos, int* rstw, int* eobrun );
+
+INTERN void jpg_build_huffcodes( unsigned char *clen, unsigned char *cval,
+				huffCodes *hc, huffTree *ht );
+
+/* -----------------------------------------------
+	function declarations: pjg-specific
+	----------------------------------------------- */
+	
+INTERN bool pjg_encode_zstscan( ArithmeticEncoder* enc, int cmp );
+INTERN bool pjg_encode_zdst_high( ArithmeticEncoder* enc, int cmp );
+INTERN bool pjg_encode_zdst_low( ArithmeticEncoder* enc, int cmp );
+INTERN bool pjg_encode_dc( ArithmeticEncoder* enc, int cmp );
+INTERN bool pjg_encode_ac_high( ArithmeticEncoder* enc, int cmp );
+INTERN bool pjg_encode_ac_low( ArithmeticEncoder* enc, int cmp );
+INTERN bool pjg_encode_generic( ArithmeticEncoder* enc, unsigned char* data, int len );
+INTERN bool pjg_encode_bit( ArithmeticEncoder* enc, unsigned char bit );
+
+INTERN bool pjg_decode_zstscan( ArithmeticDecoder* dec, int cmp );
+INTERN bool pjg_decode_zdst_high( ArithmeticDecoder* dec, int cmp );
+INTERN bool pjg_decode_zdst_low( ArithmeticDecoder* dec, int cmp );
+INTERN bool pjg_decode_dc( ArithmeticDecoder* dec, int cmp );
+INTERN bool pjg_decode_ac_high( ArithmeticDecoder* dec, int cmp );
+INTERN bool pjg_decode_ac_low( ArithmeticDecoder* dec, int cmp );
+INTERN bool pjg_decode_generic( ArithmeticDecoder* dec, unsigned char** data, int* len );
+INTERN bool pjg_decode_bit( ArithmeticDecoder* dec, unsigned char* bit );
+
+INTERN void pjg_get_zerosort_scan( unsigned char* sv, int cmp );
+INTERN bool pjg_optimize_header( void );
+INTERN bool pjg_unoptimize_header( void );
+
+INTERN void pjg_aavrg_prepare( unsigned short** abs_coeffs, int* weights, unsigned short* abs_store, int cmp );
+INTERN int pjg_aavrg_context( unsigned short** abs_coeffs, int* weights, int pos, int p_y, int p_x, int r_x );
+INTERN int pjg_lakh_context( signed short** coeffs_x, signed short** coeffs_a, int* pred_cf, int pos );
+INTERN void get_context_nnb( int pos, int w, int *a, int *b );
+
+
+/* -----------------------------------------------
+	function declarations: DCT
+	----------------------------------------------- */
+
+#if !defined(BUILD_LIB) && defined(DEV_BUILD)
+INTERN int idct_2d_fst_8x8( int cmp, int dpos, int ix, int iy );
+#endif
+INTERN int idct_2d_fst_1x8( int cmp, int dpos, int ix, int iy );
+INTERN int idct_2d_fst_8x1( int cmp, int dpos, int ix, int iy );
+
+
+/* -----------------------------------------------
+	function declarations: prediction
+	----------------------------------------------- */
+
+#if defined( USE_PLOCOI )
+INTERN int dc_coll_predictor( int cmp, int dpos );
+#else
+INTERN int dc_1ddct_predictor( int cmp, int dpos );
+#endif
+INTERN inline int plocoi( int a, int b, int c );
+INTERN inline int median_int( int* values, int size );
+INTERN inline float median_float( float* values, int size );
+
+
+/* -----------------------------------------------
+	function declarations: miscelaneous helpers
+	----------------------------------------------- */
+#if !defined( BUILD_LIB )
+INTERN inline void progress_bar( int current, int last );
+INTERN inline char* create_filename( const char* base, const char* extension );
+INTERN inline char* unique_filename( const char* base, const char* extension );
+INTERN inline void set_extension( char* filename, const char* extension );
+INTERN inline void add_underscore( char* filename );
+#endif
+INTERN inline bool file_exists( const char* filename );
+
+
+/* -----------------------------------------------
+	function declarations: developers functions
+	----------------------------------------------- */
+
+// these are developers functions, they are not needed
+// in any way to compress jpg or decompress pjg
+#if !defined(BUILD_LIB) && defined(DEV_BUILD)
+INTERN int collmode = 0; // write mode for collections: 0 -> std, 1 -> dhf, 2 -> squ, 3 -> unc
+INTERN bool dump_hdr( void );
+INTERN bool dump_huf( void );
+INTERN bool dump_coll( void );
+INTERN bool dump_zdst( void );
+INTERN bool dump_file( const char* base, const char* ext, void* data, int bpv, int size );
+INTERN bool dump_errfile( void );
+INTERN bool dump_info( void );
+INTERN bool dump_dist( void );
+INTERN bool dump_pgm( void );
+#endif
+
+
+/* -----------------------------------------------
+	global variables: library only variables
+	----------------------------------------------- */
+#if defined(BUILD_LIB)
+INTERN int lib_in_type  = -1;
+INTERN int lib_out_type = -1;
+#endif
+
+
+/* -----------------------------------------------
+	global variables: data storage
+	----------------------------------------------- */
+
+INTERN unsigned short qtables[4][64];				// quantization tables
+INTERN huffCodes      hcodes[2][4];				// huffman codes
+INTERN huffTree       htrees[2][4];				// huffman decoding trees
+INTERN unsigned char  htset[2][4];					// 1 if huffman table is set
+
+INTERN unsigned char* grbgdata		   =   NULL;	// garbage data
+INTERN unsigned char* hdrdata          =   NULL;   // header data
+INTERN unsigned char* huffdata         =   NULL;   // huffman coded data
+INTERN int            hufs             =    0  ;   // size of huffman data
+INTERN int            hdrs             =    0  ;   // size of header
+INTERN int            grbs             =    0  ;   // size of garbage
+
+INTERN unsigned int*  rstp             =   NULL;   // restart markers positions in huffdata
+INTERN unsigned int*  scnp             =   NULL;   // scan start positions in huffdata
+INTERN int            rstc             =    0  ;   // count of restart markers
+INTERN int            scnc             =    0  ;   // count of scans
+INTERN int            rsti             =    0  ;   // restart interval
+INTERN char           padbit           =    -1 ;   // padbit (for huffman coding)
+INTERN unsigned char* rst_err          =   NULL;   // number of wrong-set RST markers per scan
+
+INTERN unsigned char* zdstdata[4]      = { NULL }; // zero distribution (# of non-zeroes) lists (for higher 7x7 block)
+INTERN unsigned char* eobxhigh[4]      = { NULL }; // eob in x direction (for higher 7x7 block)
+INTERN unsigned char* eobyhigh[4]      = { NULL }; // eob in y direction (for higher 7x7 block)
+INTERN unsigned char* zdstxlow[4]		= { NULL }; // # of non zeroes for first row
+INTERN unsigned char* zdstylow[4]		= { NULL }; // # of non zeroes for first collumn
+INTERN signed short*  colldata[4][64]  = {{NULL}}; // collection sorted DCT coefficients
+
+INTERN unsigned char* freqscan[4]      = { NULL }; // optimized order for frequency scans (only pointers to scans)
+INTERN unsigned char  zsrtscan[4][64];				// zero optimized frequency scan
+
+INTERN int adpt_idct_8x8[ 4 ][ 8 * 8 * 8 * 8 ];	// precalculated/adapted values for idct (8x8)
+INTERN int adpt_idct_1x8[ 4 ][ 1 * 1 * 8 * 8 ];	// precalculated/adapted values for idct (1x8)
+INTERN int adpt_idct_8x1[ 4 ][ 8 * 8 * 1 * 1 ];	// precalculated/adapted values for idct (8x1)
+
+
+/* -----------------------------------------------
+	global variables: info about image
+	----------------------------------------------- */
+
+// seperate info for each color component
+INTERN componentInfo cmpnfo[ 4 ];
+
+INTERN int cmpc        = 0; // component count
+INTERN int imgwidth    = 0; // width of image
+INTERN int imgheight   = 0; // height of image
+
+INTERN int sfhm        = 0; // max horizontal sample factor
+INTERN int sfvm        = 0; // max verical sample factor
+INTERN int mcuv        = 0; // mcus per line
+INTERN int mcuh        = 0; // mcus per collumn
+INTERN int mcuc        = 0; // count of mcus
+
+
+/* -----------------------------------------------
+	global variables: info about current scan
+	----------------------------------------------- */
+
+INTERN int cs_cmpc      =   0  ; // component count in current scan
+INTERN int cs_cmp[ 4 ]  = { 0 }; // component numbers  in current scan
+INTERN int cs_from      =   0  ; // begin - band of current scan ( inclusive )
+INTERN int cs_to        =   0  ; // end - band of current scan ( inclusive )
+INTERN int cs_sah       =   0  ; // successive approximation bit pos high
+INTERN int cs_sal       =   0  ; // successive approximation bit pos low
+	
+
+/* -----------------------------------------------
+	global variables: info about files
+	----------------------------------------------- */
+	
+INTERN char*  jpgfilename = NULL;	// name of JPEG file
+INTERN char*  pjgfilename = NULL;	// name of PJG file
+INTERN int    jpgfilesize;			// size of JPEG file
+INTERN int    pjgfilesize;			// size of PJG file
+INTERN int    jpegtype = 0;			// type of JPEG coding: 0->unknown, 1->sequential, 2->progressive
+INTERN int    filetype;				// type of current file
+INTERN std::unique_ptr<Reader> str_in;	// input stream
+INTERN std::unique_ptr<Writer> str_out;	// output stream
+
+#if !defined(BUILD_LIB)
+INTERN std::unique_ptr<Reader> str_str;	// storage stream
+
+INTERN char** filelist = NULL;		// list of files to process 
+INTERN int    file_cnt = 0;			// count of files in list
+INTERN int    file_no  = 0;			// number of current file
+
+INTERN char** err_list = NULL;		// list of error messages 
+INTERN int*   err_tp   = NULL;		// list of error types
+#endif
+
+#if defined(DEV_INFOS)
+INTERN int    dev_size_hdr      = 0;
+INTERN int    dev_size_cmp[ 4 ] = { 0 };
+INTERN int    dev_size_zsr[ 4 ] = { 0 };
+INTERN int    dev_size_dc[ 4 ]  = { 0 };
+INTERN int    dev_size_ach[ 4 ] = { 0 };
+INTERN int    dev_size_acl[ 4 ] = { 0 };
+INTERN int    dev_size_zdh[ 4 ] = { 0 };
+INTERN int    dev_size_zdl[ 4 ] = { 0 };
+#endif
+
+
+/* -----------------------------------------------
+	global variables: messages
+	----------------------------------------------- */
+
+INTERN char errormessage [ MSG_SIZE ];
+INTERN bool (*errorfunction)();
+INTERN int  errorlevel;
+// meaning of errorlevel:
+// -1 -> wrong input
+// 0 -> no error
+// 1 -> warning
+// 2 -> fatal error
+
+
+/* -----------------------------------------------
+	global variables: settings
+	----------------------------------------------- */
+
+#if !defined( BUILD_LIB )
+INTERN int  verbosity  = -1;	// level of verbosity
+INTERN bool overwrite  = false;	// overwrite files yes / no
+INTERN bool wait_exit  = true;	// pause after finished yes / no
+INTERN int  verify_lv  = 0;		// verification level ( none (0), simple (1), detailed output (2) )
+INTERN int  err_tol    = 1;		// error threshold ( proceed on warnings yes (2) / no (1) )
+INTERN bool disc_meta  = false;	// discard meta-info yes / no
+
+INTERN bool developer  = false;	// allow developers functions yes/no
+INTERN bool auto_set   = true;	// automatic find best settings yes/no
+INTERN int  action = A_COMPRESS;// what to do with JPEG/PJG files
+
+INTERN FILE*  msgout   = stdout;// stream for output of messages
+INTERN bool   pipe_on  = false;	// use stdin/stdout instead of filelist
+#else
+INTERN int  err_tol    = 1;		// error threshold ( proceed on warnings yes (2) / no (1) )
+INTERN bool disc_meta  = false;	// discard meta-info yes / no
+INTERN bool auto_set   = true;	// automatic find best settings yes/no
+INTERN int  action = A_COMPRESS;// what to do with JPEG/PJG files
+#endif
+
+INTERN unsigned char nois_trs[ 4 ] = {6,6,6,6}; // bit pattern noise threshold
+INTERN unsigned char segm_cnt[ 4 ] = {10,10,10,10}; // number of segments
+#if !defined( BUILD_LIB )
+INTERN unsigned char orig_set[ 8 ] = { 0 }; // store array for settings
+#endif
+
+
+/* -----------------------------------------------
+	global variables: info about program
+	----------------------------------------------- */
+
+INTERN const unsigned char appversion = 25;
+INTERN const char*  subversion   = "k";
+INTERN const char*  apptitle     = "packJPG";
+INTERN const char*  appname      = "packjpg";
+INTERN const char*  versiondate  = "01/22/2016";
+INTERN const char*  author       = "Matthias Stirner / Se";
+#if !defined(BUILD_LIB)
+INTERN const char*  website      = "http://packjpg.encode.ru/";
+INTERN const char*	copyright    = "2006-2016 HTW Aalen University & Matthias Stirner";
+INTERN const char*  email        = "packjpg (at) matthiasstirner.com";
+INTERN const char*  pjg_ext      = "pjg";
+INTERN const char*  jpg_ext      = "jpg";
+#endif
+INTERN const char   pjg_magic[] = { 'J', 'S' };
+
+
+/* -----------------------------------------------
+	main-function
+	----------------------------------------------- */
+
+#if !defined(BUILD_LIB)
+int main( int argc, char** argv )
+{	
+	sprintf( errormessage, "no errormessage specified" );
+	
+	clock_t begin, end;
+	
+	int error_cnt = 0;
+	int warn_cnt  = 0;
+	
+	double acc_jpgsize = 0;
+	double acc_pjgsize = 0;
+	
+	int kbps;
+	double cr;
+	double total;
+	
+	errorlevel = 0;
+	
+	
+	// read options from command line
+	initialize_options( argc, argv );
+	
+	// write program info to screen
+	fprintf( msgout,  "\n--> %s v%i.%i%s (%s) by %s <--\n",
+			apptitle, appversion / 10, appversion % 10, subversion, versiondate, author );
+	fprintf( msgout, "Copyright %s\nAll rights reserved\n\n", copyright );
+	
+	// check if user input is wrong, show help screen if it is
+	if ( ( file_cnt == 0 ) ||
+		( ( !developer ) && ( (action != A_COMPRESS) || (!auto_set) || (verify_lv > 1) ) ) ) {
+		show_help();
+		return -1;
+	}
+	
+	// display warning if not using automatic settings
+	if ( !auto_set ) {
+		fprintf( msgout,  " custom compression settings: \n" );
+		fprintf( msgout,  " -------------------------------------------------\n" );
+		fprintf( msgout,  " no of segments    ->  %3i[0] %3i[1] %3i[2] %3i[3]\n",
+				segm_cnt[0], segm_cnt[1], segm_cnt[2], segm_cnt[3] );
+		fprintf( msgout,  " noise threshold   ->  %3i[0] %3i[1] %3i[2] %3i[3]\n",
+				nois_trs[0], nois_trs[1], nois_trs[2], nois_trs[3] );
+		fprintf( msgout,  " -------------------------------------------------\n\n" );
+	}
+	
+	// (re)set program has to be done first
+	reset_buffers();
+	
+	// process file(s) - this is the main function routine
+	begin = clock();
+	for ( file_no = 0; file_no < file_cnt; file_no++ ) {	
+		// process current file
+		process_ui();
+		// store error message and type if any
+		if ( errorlevel > 0 ) {
+			err_list[ file_no ] = (char*) calloc( MSG_SIZE, sizeof( char ) );
+			err_tp[ file_no ] = errorlevel;
+			if ( err_list[ file_no ] != NULL )
+				strcpy( err_list[ file_no ], errormessage );
+		}
+		// count errors / warnings / file sizes
+		if ( errorlevel >= err_tol ) error_cnt++;
+		else {
+			if ( errorlevel == 1 ) warn_cnt++;
+			acc_jpgsize += jpgfilesize;
+			acc_pjgsize += pjgfilesize;
+		}
+	}
+	end = clock();
+	
+	// errors summary: only needed for -v2 or progress bar
+	if ( ( verbosity == -1 ) || ( verbosity == 2 ) ) {
+		// print summary of errors to screen
+		if ( error_cnt > 0 ) {
+			fprintf( stderr, "\n\nfiles with errors:\n" );
+			fprintf( stderr, "------------------\n" );
+			for ( file_no = 0; file_no < file_cnt; file_no++ ) {
+				if ( err_tp[ file_no ] >= err_tol ) {
+					fprintf( stderr, "%s (%s)\n", filelist[ file_no ], err_list[ file_no ] );
+				}
+			}
+		}
+		// print summary of warnings to screen
+		if ( warn_cnt > 0 ) {
+			fprintf( stderr, "\n\nfiles with warnings:\n" );
+			fprintf( stderr, "------------------\n" );
+			for ( file_no = 0; file_no < file_cnt; file_no++ ) {
+				if ( err_tp[ file_no ] == 1 ) {
+					fprintf( stderr, "%s (%s)\n", filelist[ file_no ], err_list[ file_no ] );
+				}
+			}
+		}
+	}
+	
+	// show statistics
+	fprintf( msgout,  "\n\n-> %i file(s) processed, %i error(s), %i warning(s)\n",
+		file_cnt, error_cnt, warn_cnt );
+	if ( ( file_cnt > error_cnt ) && ( verbosity != 0 ) &&
+	 ( action == A_COMPRESS ) ) {
+		acc_jpgsize /= 1024.0; acc_pjgsize /= 1024.0;
+		total = (double) ( end - begin ) / CLOCKS_PER_SEC; 
+		kbps  = ( total > 0 ) ? ( acc_jpgsize / total ) : acc_jpgsize;
+		cr    = ( acc_jpgsize > 0 ) ? ( 100.0 * acc_pjgsize / acc_jpgsize ) : 0;
+		
+		fprintf( msgout,  " --------------------------------- \n" );
+		if ( total >= 0 ) {
+			fprintf( msgout,  " total time        : %8.2f sec\n", total );
+			fprintf( msgout,  " avrg. kbyte per s : %8i byte\n", kbps );
+		}
+		else {
+			fprintf( msgout,  " total time        : %8s sec\n", "N/A" );
+			fprintf( msgout,  " avrg. kbyte per s : %8s byte\n", "N/A" );
+		}
+		fprintf( msgout,  " avrg. comp. ratio : %8.2f %%\n", cr );		
+		fprintf( msgout,  " --------------------------------- \n" );
+		#if defined(DEV_INFOS)
+		if ( acc_jpgsize > 0 ) { 
+			fprintf( msgout,  " header %%          : %8.2f %%\n", 100.0 * dev_size_hdr / acc_jpgsize );
+			if ( dev_size_cmp[0] > 0 ) fprintf( msgout,  " component [0] %%   : %8.2f %%\n", 100.0 * dev_size_cmp[0] / acc_jpgsize );
+			if ( dev_size_cmp[1] > 0 ) fprintf( msgout,  " component [1] %%   : %8.2f %%\n", 100.0 * dev_size_cmp[1] / acc_jpgsize );
+			if ( dev_size_cmp[2] > 0 ) fprintf( msgout,  " component [2] %%   : %8.2f %%\n", 100.0 * dev_size_cmp[2] / acc_jpgsize );
+			if ( dev_size_cmp[3] > 0 ) fprintf( msgout,  " component [3] %%   : %8.2f %%\n", 100.0 * dev_size_cmp[3] / acc_jpgsize );
+			fprintf( msgout,  " --------------------------------- \n" );
+			for ( int i = 0; i < 4; i++ ) {
+				if ( dev_size_cmp[i] == 0 ) break;
+				fprintf( msgout,  " ac coeffs h [%i] %% : %8.2f %%\n", i, 100.0 * dev_size_ach[i] / acc_jpgsize );
+				fprintf( msgout,  " ac coeffs l [%i] %% : %8.2f %%\n", i, 100.0 * dev_size_acl[i] / acc_jpgsize );
+				fprintf( msgout,  " dc coeffs   [%i] %% : %8.2f %%\n", i, 100.0 * dev_size_dc[i] / acc_jpgsize );
+				fprintf( msgout,  " zero dist h [%i] %% : %8.2f %%\n", i, 100.0 * dev_size_zdh[i] / acc_jpgsize );
+				fprintf( msgout,  " zero dist l [%i] %% : %8.2f %%\n", i, 100.0 * dev_size_zdl[i] / acc_jpgsize );
+				fprintf( msgout,  " zero sort   [%i] %% : %8.2f %%\n", i, 100.0 * dev_size_zsr[i] / acc_jpgsize );
+				fprintf( msgout,  " --------------------------------- \n" );
+			}
+		}
+		#endif
+	}
+	
+	// pause before exit
+	if ( wait_exit && ( msgout != stderr ) ) {
+		fprintf( msgout, "\n\n< press ENTER >\n" );
+		fgetc( stdin );
+	}
+	
+	
+	return 0;
+}
+#endif
+
+/* ----------------------- Begin of library only functions -------------------------- */
+
+/* -----------------------------------------------
+	DLL export converter function
+	----------------------------------------------- */
+	
+#if defined(BUILD_LIB)
+EXPORT bool pjglib_convert_stream2stream( char* msg )
+{
+	// process in main function
+	return pjglib_convert_stream2mem( NULL, NULL, msg ); 
+}
+#endif
+
+
+/* -----------------------------------------------
+	DLL export converter function
+	----------------------------------------------- */
+
+#if defined(BUILD_LIB)
+EXPORT bool pjglib_convert_file2file( char* in, char* out, char* msg )
+{
+	// init streams
+	pjglib_init_streams( (void*) in, 0, 0, (void*) out, 0 );
+	
+	// process in main function
+	return pjglib_convert_stream2mem( NULL, NULL, msg ); 
+}
+#endif
+
+
+/* -----------------------------------------------
+	DLL export converter function
+	----------------------------------------------- */
+	
+#if defined(BUILD_LIB)
+EXPORT bool pjglib_convert_stream2mem( unsigned char** out_file, unsigned int* out_size, char* msg )
+{
+	clock_t begin, end;
+	int total;
+	float cr;	
+	
+	
+	// use automatic settings
+	auto_set = true;
+	
+	// (re)set buffers
+	reset_buffers();
+	action = A_COMPRESS;
+	
+	// main compression / decompression routines
+	begin = clock();
+	
+	// process one file
+	process_file();
+	
+	// fetch pointer and size of output (only for memory output)
+	if ( ( errorlevel < err_tol ) && ( lib_out_type == 1 ) &&
+		 ( out_file != NULL ) && ( out_size != NULL ) ) {
+		*out_size = str_out->num_bytes_written();
+		*out_file = str_out->get_c_data();
+	}
+	
+	// close iostreams
+    str_in.reset(nullptr);
+    str_out.reset(nullptr);
+	
+	end = clock();
+	
+	// copy errormessage / remove files if error (and output is file)
+	if ( errorlevel >= err_tol ) {
+		if ( lib_out_type == 0 ) {
+			if ( filetype == F_JPG ) {
+				if ( file_exists( pjgfilename ) ) remove( pjgfilename );
+			} else if ( filetype == F_PJG ) {
+				if ( file_exists( jpgfilename ) ) remove( jpgfilename );
+			}
+		}
+		if ( msg != NULL ) strcpy( msg, errormessage );
+		return false;
+	}
+	
+	// get compression info
+	total = (int) ( (double) (( end - begin ) * 1000) / CLOCKS_PER_SEC );
+	cr    = ( jpgfilesize > 0 ) ? ( 100.0 * pjgfilesize / jpgfilesize ) : 0;
+	
+	// write success message else
+	if ( msg != NULL ) {
+		switch( filetype )
+		{
+			case F_JPG:
+				sprintf( msg, "Compressed to %s (%.2f%%) in %ims",
+					pjgfilename, cr, ( total >= 0 ) ? total : -1 );
+				break;
+			case F_PJG:
+				sprintf( msg, "Decompressed to %s (%.2f%%) in %ims",
+					jpgfilename, cr, ( total >= 0 ) ? total : -1 );
+				break;
+			case F_UNK:
+				sprintf( msg, "Unknown filetype" );
+				break;	
+		}
+	}
+	
+	
+	return true;
+}
+#endif
+
+
+/* -----------------------------------------------
+	DLL export init input (file/mem)
+	----------------------------------------------- */
+	
+#if defined(BUILD_LIB)
+EXPORT void pjglib_init_streams( void* in_src, int in_type, int in_size, void* out_dest, int out_type )
+{
+	/* a short reminder about input/output stream types:
+	
+	if input is file
+	----------------
+	in_scr -> name of input file
+	in_type -> 0
+	in_size -> ignore
+	
+	if input is memory
+	------------------
+	in_scr -> array containg data
+	in_type -> 1
+	in_size -> size of data array
+	
+	if input is *FILE (f.e. stdin)
+	------------------------------
+	in_src -> stream pointer
+	in_type -> 2
+	in_size -> ignore
+	
+	vice versa for output streams! */
+	
+	unsigned char buffer[ 2 ];
+	
+	// (re)set errorlevel
+	errorfunction = NULL;
+	errorlevel = 0;
+	jpgfilesize = 0;
+	pjgfilesize = 0;
+	
+
+    switch (in_type) {
+        case 0:
+            try {
+                str_in = std::make_unique<FileReader>((char*)in_src);
+            } catch (const std::runtime_error&) {
+                sprintf(errormessage, "error opening input file %s", (char*)in_src);
+		        errorlevel = 2;
+		        return;
+            }
+            break;
+        case 1:
+            str_in = std::make_unique<MemoryReader>((unsigned char*)in_src, in_size);
+            break;
+        case 2:
+			try {
+				str_in = std::make_unique<StreamReader>();
+			} catch (const std::runtime_error& e) {
+				sprintf(errormessage, e.what());
+				errorlevel = 2;
+				return;
+			}
+            break;
+        default:
+            sprintf(errormessage, "Invalid input type: %i", in_type);
+		    errorlevel = 2;
+		    return;
+    }
+
+    switch (out_type) {
+        case 0:
+            try {
+                str_out = std::make_unique<FileWriter>((char*)out_dest);
+            } catch (const std::runtime_error&) {
+                sprintf(errormessage, "error opening output file %s", (char*)out_dest);
+		        errorlevel = 2;
+		        return;
+            }
+            break;
+        case 1:
+            str_out = std::make_unique<MemoryWriter>();
+            break;
+        case 2:
+			try {
+				str_out = std::make_unique<StreamWriter>();
+			} catch (const std::runtime_error& e) {
+				sprintf(errormessage, e.what());
+				errorlevel = 2;
+				return;
+			}
+            break;
+        default:
+            sprintf(errormessage, "Invalid output type: %i", out_type);
+		    errorlevel = 2;
+		    return;
+    }
+	
+	// free memory from filenames if needed
+	if (jpgfilename != nullptr) {
+		free(jpgfilename);
+		jpgfilename = nullptr;
+	}
+	if (pjgfilename != nullptr) {
+		free(pjgfilename);
+		pjgfilename = nullptr;
+	}
+	
+	// check input stream
+	str_in->read( buffer, 2 );
+	if ( ( buffer[0] == 0xFF ) && ( buffer[1] == 0xD8 ) ) {
+		// file is JPEG
+		filetype = F_JPG;
+		// copy filenames
+		jpgfilename = (char*) calloc( (  in_type == 0 ) ? strlen( (char*) in_src   ) + 1 : 32, sizeof( char ) );
+		pjgfilename = (char*) calloc( ( out_type == 0 ) ? strlen( (char*) out_dest ) + 1 : 32, sizeof( char ) );
+		strcpy( jpgfilename, (  in_type == 0 ) ? (char*) in_src   : "JPG in memory" );
+		strcpy( pjgfilename, ( out_type == 0 ) ? (char*) out_dest : "PJG in memory" );
+	}
+	else if ( (buffer[0] == pjg_magic[0]) && (buffer[1] == pjg_magic[1]) ) {
+		// file is PJG
+		filetype = F_PJG;
+		// copy filenames
+		pjgfilename = (char*) calloc( (  in_type == 0 ) ? strlen( (char*) in_src   ) + 1 : 32, sizeof( char ) );
+		jpgfilename = (char*) calloc( ( out_type == 0 ) ? strlen( (char*) out_dest ) + 1 : 32, sizeof( char ) );
+		strcpy( pjgfilename, (  in_type == 0 ) ? (char*) in_src   : "PJG in memory" );
+		strcpy( jpgfilename, ( out_type == 0 ) ? (char*) out_dest : "JPG in memory" );
+	}
+	else {
+		// file is neither
+		filetype = F_UNK;
+		sprintf( errormessage, "filetype of input stream is unknown" );
+		errorlevel = 2;
+		return;
+	}
+	
+	// store types of in-/output
+	lib_in_type  = in_type;
+	lib_out_type = out_type;
+}
+#endif
+
+
+/* -----------------------------------------------
+	DLL export version information
+	----------------------------------------------- */
+	
+#if defined(BUILD_LIB)
+EXPORT const char* pjglib_version_info( void )
+{
+	static char v_info[ 256 ];
+	
+	// copy version info to string
+	sprintf( v_info, "--> %s library v%i.%i%s (%s) by %s <--",
+			apptitle, appversion / 10, appversion % 10, subversion, versiondate, author );
+			
+	return (const char*) v_info;
+}
+#endif
+
+
+/* -----------------------------------------------
+	DLL export version information
+	----------------------------------------------- */
+	
+#if defined(BUILD_LIB)
+EXPORT const char* pjglib_short_name( void )
+{
+	static char v_name[ 256 ];
+	
+	// copy version info to string
+	sprintf( v_name, "%s v%i.%i%s",
+			apptitle, appversion / 10, appversion % 10, subversion );
+			
+	return (const char*) v_name;
+}
+#endif
+
+/* ----------------------- End of libary only functions -------------------------- */
+
+/* ----------------------- Begin of main interface functions -------------------------- */
+
+
+/* -----------------------------------------------
+	reads in commandline arguments
+	----------------------------------------------- */
+	
+#if !defined(BUILD_LIB)	
+INTERN void initialize_options( int argc, char** argv )
+{	
+	int tmp_val;
+	char** tmp_flp;
+	int i;
+	
+	
+	// get memory for filelist & preset with NULL
+	filelist = (char**) calloc( argc, sizeof( char* ) );
+	for ( i = 0; i < argc; i++ )
+		filelist[ i ] = NULL;
+	
+	// preset temporary filelist pointer
+	tmp_flp = filelist;
+	
+	
+	// read in arguments
+	while ( --argc > 0 ) {
+		argv++;
+		// switches begin with '-'
+		if ( strcmp((*argv), "-p" ) == 0 ) {
+			err_tol = 2;
+		}
+		else if ( strcmp((*argv), "-d" ) == 0 ) {
+			disc_meta = true;
+		}		
+		else if ( strcmp((*argv), "-ver" ) == 0 ) {
+			verify_lv = ( verify_lv < 1 ) ? 1 : verify_lv;
+		}
+		else if ( sscanf( (*argv), "-v%i", &tmp_val ) == 1 ){
+			verbosity = tmp_val;
+			verbosity = ( verbosity < 0 ) ? 0 : verbosity;
+			verbosity = ( verbosity > 2 ) ? 2 : verbosity;			
+		}
+		else if ( strcmp((*argv), "-vp" ) == 0 ) {
+			verbosity = -1;
+		}
+		else if ( strcmp((*argv), "-np" ) == 0 ) {
+			wait_exit = false;
+		}
+		else if ( strcmp((*argv), "-o" ) == 0 ) {
+			overwrite = true;
+		}
+		#if defined(DEV_BUILD)
+		else if ( strcmp((*argv), "-dev") == 0 ) {
+			developer = true;
+		}
+		else if ( strcmp((*argv), "-test") == 0 ) {
+			verify_lv = 2;
+		}
+		else if ( sscanf( (*argv), "-t%i,%i", &i, &tmp_val ) == 2 ) {			
+			i = ( i < 0 ) ? 0 : i;
+			i = ( i > 3 ) ? 3 : i;
+			tmp_val = ( tmp_val < 0  ) ?  0 : tmp_val;
+			tmp_val = ( tmp_val > 10 ) ? 10 : tmp_val;
+			nois_trs[ i ] = tmp_val;
+			auto_set = false;
+		}
+		else if ( sscanf( (*argv), "-s%i,%i", &i, &tmp_val ) == 2 ) {
+			i = ( i < 0 ) ? 0 : i;
+			i = ( i > 3 ) ? 3 : i;
+			tmp_val = ( tmp_val <  1 ) ?  1 : tmp_val;
+			tmp_val = ( tmp_val > 49 ) ? 49 : tmp_val;
+			segm_cnt[ i ] = tmp_val;
+			auto_set = false;
+		}
+		else if ( sscanf( (*argv), "-t%i", &tmp_val ) == 1 ) {
+			tmp_val = ( tmp_val < 0  ) ?  0 : tmp_val;
+			tmp_val = ( tmp_val > 10 ) ? 10 : tmp_val;
+			nois_trs[0] = tmp_val;
+			nois_trs[1] = tmp_val;
+			nois_trs[2] = tmp_val;
+			nois_trs[3] = tmp_val;
+			auto_set = false;
+		}
+		else if ( sscanf( (*argv), "-s%i", &tmp_val ) == 1 ) {
+			tmp_val = ( tmp_val <  1 ) ?  1 : tmp_val;
+			tmp_val = ( tmp_val > 64 ) ? 64 : tmp_val;
+			segm_cnt[0] = tmp_val;
+			segm_cnt[1] = tmp_val;
+			segm_cnt[2] = tmp_val;
+			segm_cnt[3] = tmp_val;
+			auto_set = false;
+		}
+		else if ( sscanf( (*argv), "-coll%i", &tmp_val ) == 1 ) {
+			tmp_val = ( tmp_val < 0 ) ? 0 : tmp_val;
+			tmp_val = ( tmp_val > 5 ) ? 5 : tmp_val;
+			collmode = tmp_val;
+			action = A_COLL_DUMP;
+		}
+		else if ( sscanf( (*argv), "-fcol%i", &tmp_val ) == 1 ) {
+			tmp_val = ( tmp_val < 0 ) ? 0 : tmp_val;
+			tmp_val = ( tmp_val > 5 ) ? 5 : tmp_val;
+			collmode = tmp_val;
+			action = A_FCOLL_DUMP;
+		}
+		else if ( strcmp((*argv), "-split") == 0 ) {
+			action = A_SPLIT_DUMP;
+		}
+		else if ( strcmp((*argv), "-zdst") == 0 ) {
+			action = A_ZDST_DUMP;
+		}	
+		else if ( strcmp((*argv), "-info") == 0 ) {
+			action = A_TXT_INFO;
+		}
+		else if ( strcmp((*argv), "-dist") == 0 ) {
+			action = A_DIST_INFO;
+		}
+		else if ( strcmp((*argv), "-pgm") == 0 ) {
+			action = A_PGM_DUMP;
+		}
+	   	else if ( ( strcmp((*argv), "-comp") == 0) ) {
+			action = A_COMPRESS;
+		}
+		#endif
+		else if ( strcmp((*argv), "-") == 0 ) {
+			// switch standard message out stream
+			msgout = stderr;
+			// use "-" as placeholder for stdin
+			*(tmp_flp++) = (char*) "-";
+		}
+		else {
+			// if argument is not switch, it's a filename
+			*(tmp_flp++) = *argv;
+		}		
+	}
+	
+	// count number of files (or filenames) in filelist
+	for ( file_cnt = 0; filelist[ file_cnt ] != NULL; file_cnt++ );
+	
+	// alloc arrays for error messages and types storage
+	err_list = (char**) calloc( file_cnt, sizeof( char* ) );
+	err_tp   = (int*) calloc( file_cnt, sizeof( int ) );
+	
+	// backup settings - needed to restore original setting later
+	if ( !auto_set ) {
+		orig_set[ 0 ] = nois_trs[ 0 ];
+		orig_set[ 1 ] = nois_trs[ 1 ];
+		orig_set[ 2 ] = nois_trs[ 2 ];
+		orig_set[ 3 ] = nois_trs[ 3 ];
+		orig_set[ 4 ] = segm_cnt[ 0 ];
+		orig_set[ 5 ] = segm_cnt[ 1 ];
+		orig_set[ 6 ] = segm_cnt[ 2 ];
+		orig_set[ 7 ] = segm_cnt[ 3 ];
+	}
+	else {
+		for ( i = 0; i < 8; i++ )
+			orig_set[ i ] = 0;
+	}	
+}
+#endif
+
+
+/* -----------------------------------------------
+	UI for processing one file
+	----------------------------------------------- */
+	
+#if !defined(BUILD_LIB)
+INTERN void process_ui( void )
+{
+	clock_t begin, end;
+	const char* actionmsg  = NULL;
+	const char* errtypemsg = NULL;
+	int total, bpms;
+	float cr;	
+	
+	
+	errorfunction = NULL;
+	errorlevel = 0;
+	jpgfilesize = 0;
+	pjgfilesize = 0;	
+	#if !defined(DEV_BUILD)
+	action = A_COMPRESS;
+	#endif
+	
+	// compare file name, set pipe if needed
+	if ( ( strcmp( filelist[ file_no ], "-" ) == 0 ) && ( action == A_COMPRESS ) ) {
+		pipe_on = true;
+		filelist[ file_no ] = (char*) "STDIN";
+	}
+	else {		
+		pipe_on = false;
+	}
+	
+	if ( verbosity >= 0 ) { // standard UI
+		fprintf( msgout,  "\nProcessing file %i of %i \"%s\" -> ",
+					file_no + 1, file_cnt, filelist[ file_no ] );
+		
+		if ( verbosity > 1 )
+			fprintf( msgout,  "\n----------------------------------------" );
+		
+		// check input file and determine filetype
+		execute( check_file );
+		
+		// get specific action message
+		if ( filetype == F_UNK ) actionmsg = "unknown filetype";
+		else switch ( action ) {
+			case A_COMPRESS:	actionmsg = ( filetype == F_JPG ) ? "Compressing" : "Decompressing";	break;			
+			case A_SPLIT_DUMP:	actionmsg = "Splitting"; break;			
+			case A_COLL_DUMP:	actionmsg = "Extracting Colls"; break;
+			case A_FCOLL_DUMP:	actionmsg = "Extracting FColls"; break;
+			case A_ZDST_DUMP:	actionmsg = "Extracting ZDST lists"; break;			
+			case A_TXT_INFO:	actionmsg = "Extracting info"; break;		
+			case A_DIST_INFO:	actionmsg = "Extracting distributions";	break;		
+			case A_PGM_DUMP:	actionmsg = "Converting"; break;
+		}
+		
+		if ( verbosity < 2 ) fprintf( msgout, "%s -> ", actionmsg );
+	}
+	else { // progress bar UI
+		// update progress message
+		fprintf( msgout, "Processing file %2i of %2i ", file_no + 1, file_cnt );
+		progress_bar( file_no, file_cnt );
+		fprintf( msgout, "\r" );
+		execute( check_file );
+	}
+	fflush( msgout );
+	
+	
+	// main function routine
+	begin = clock();
+	
+	// streams are initiated, start processing file
+	process_file();
+	
+	// close iostreams
+    str_in.reset(nullptr);
+    str_out.reset(nullptr);
+    str_str.reset(nullptr);
+	// delete if broken or if output not needed
+	if ( ( !pipe_on ) && ( ( errorlevel >= err_tol ) || ( action != A_COMPRESS ) ) ) {
+		if ( filetype == F_JPG ) {
+			if ( file_exists( pjgfilename ) ) remove( pjgfilename );
+		} else if ( filetype == F_PJG ) {
+			if ( file_exists( jpgfilename ) ) remove( jpgfilename );
+		}
+	}
+	
+	end = clock();	
+	
+	// speed and compression ratio calculation
+	total = (int) ( (double) (( end - begin ) * 1000) / CLOCKS_PER_SEC );
+	bpms  = ( total > 0 ) ? ( jpgfilesize / total ) : jpgfilesize;
+	cr    = ( jpgfilesize > 0 ) ? ( 100.0 * pjgfilesize / jpgfilesize ) : 0;
+
+	
+	if ( verbosity >= 0 ) { // standard UI
+		if ( verbosity > 1 )
+			fprintf( msgout,  "\n----------------------------------------" );
+		
+		// display success/failure message
+		switch ( verbosity ) {
+			case 0:			
+				if ( errorlevel < err_tol ) {
+					if ( action == A_COMPRESS ) fprintf( msgout,  "%.2f%%", cr );
+					else fprintf( msgout, "DONE" );
+				}
+				else fprintf( msgout,  "ERROR" );
+				if ( errorlevel > 0 ) fprintf( msgout,  "\n" );
+				break;
+			
+			case 1:
+				fprintf( msgout, "%s\n",  ( errorlevel < err_tol ) ? "DONE" : "ERROR" );
+				break;
+			
+			case 2:
+				if ( errorlevel < err_tol ) fprintf( msgout,  "\n-> %s OK\n", actionmsg );
+				else  fprintf( msgout,  "\n-> %s ERROR\n", actionmsg );
+				break;
+		}
+		
+		// set type of error message
+		switch ( errorlevel ) {
+			case 0:	errtypemsg = "none"; break;
+			case 1: errtypemsg = ( err_tol > 1 ) ?  "warning (ignored)" : "warning (skipped file)"; break;
+			case 2: errtypemsg = "fatal error"; break;
+		}
+		
+		// error/ warning message
+		if ( errorlevel > 0 ) {
+			fprintf( msgout, " %s -> %s:\n", get_status( errorfunction ), errtypemsg  );
+			fprintf( msgout, " %s\n", errormessage );
+		}
+		if ( (verbosity > 0) && (errorlevel < err_tol) && (action == A_COMPRESS) ) {
+			if ( total >= 0 ) {
+				fprintf( msgout,  " time taken  : %7i msec\n", total );
+				fprintf( msgout,  " byte per ms : %7i byte\n", bpms );
+			}
+			else {
+				fprintf( msgout,  " time taken  : %7s msec\n", "N/A" );
+				fprintf( msgout,  " byte per ms : %7s byte\n", "N/A" );
+			}
+			fprintf( msgout,  " comp. ratio : %7.2f %%\n", cr );		
+		}	
+		if ( ( verbosity > 1 ) && ( action == A_COMPRESS ) )
+			fprintf( msgout,  "\n" );
+	}
+	else { // progress bar UI
+		// if this is the last file, update progress bar one last time
+		if ( file_no + 1 == file_cnt ) {
+			// update progress message
+			fprintf( msgout, "Processed %2i of %2i files ", file_no + 1, file_cnt );
+			progress_bar( 1, 1 );
+			fprintf( msgout, "\r" );
+		}	
+	}
+}
+#endif
+
+
+/* -----------------------------------------------
+	gets statusmessage for function
+	----------------------------------------------- */
+	
+#if !defined(BUILD_LIB)
+INTERN inline const char* get_status( bool (*function)() )
+{	
+	if ( function == NULL ) {
+		return "unknown action";
+	} else if ( function == *check_file ) {
+		return "Determining filetype";
+	} else if ( function == *read_jpeg ) {
+		return "Reading header & image data";
+	} else if ( function == *merge_jpeg ) {
+		return "Merging header & image data";
+	} else if ( function == *decode_jpeg ) {
+		return "Decompressing JPEG image data";
+	} else if ( function == *recode_jpeg ) {
+		return "Recompressing JPEG image data";
+	} else if ( function == *adapt_icos ) {
+		return "Adapting DCT precalc. tables";
+	} else if ( function == *predict_dc ) {
+		return "Applying prediction to DC";
+	} else if ( function == *unpredict_dc ) {
+		return "Removing prediction from DC";
+	} else if ( function == *check_value_range ) {
+		return "Checking values range";
+	} else if ( function == *calc_zdst_lists ) {
+		return "Calculating zero dist lists";
+	} else if ( function == *pack_pjg ) {
+		return "Compressing data to PJG";
+	} else if ( function == *unpack_pjg ) {
+		return "Uncompressing data from PJG";
+	} else if ( function == *swap_streams ) {
+		return "Swapping input/output streams";
+	} else if ( function == *compare_output ) {
+		return "Verifying output stream";
+	} else if ( function == *reset_buffers ) {
+		return "Resetting program";
+	}
+	#if defined(DEV_BUILD)
+	else if ( function == *dump_hdr ) {
+		return "Writing header data to file";
+	} else if ( function == *dump_huf ) {
+		return "Writing huffman data to file";
+	} else if ( function == *dump_coll ) {
+		return "Writing collections to files";
+	} else if ( function == *dump_zdst ) {
+		return "Writing zdist lists to files";
+	} else if ( function == *dump_errfile ) {
+		return "Writing error info to file";
+	} else if ( function == *dump_info ) {
+		return "Writing info to files";
+	} else if ( function == *dump_dist ) {
+		return "Writing distributions to files";
+	} else if ( function == *dump_pgm ) {
+		return "Writing converted image to pgm";
+	}
+	#endif
+	else {
+		return "Function description missing!";
+	}
+}
+#endif
+
+
+/* -----------------------------------------------
+	shows help in case of wrong input
+	----------------------------------------------- */
+	
+#if !defined(BUILD_LIB)
+INTERN void show_help( void )
+{	
+	fprintf( msgout, "\n" );
+	fprintf( msgout, "Website: %s\n", website );
+	fprintf( msgout, "Email  : %s\n", email );
+	fprintf( msgout, "\n" );
+	fprintf( msgout, "Usage: %s [switches] [filename(s)]", appname );
+	fprintf( msgout, "\n" );
+	fprintf( msgout, "\n" );
+	fprintf( msgout, " [-ver]   verify files after processing\n" );
+	fprintf( msgout, " [-v?]    set level of verbosity (max: 2) (def: 0)\n" );
+	fprintf( msgout, " [-np]    no pause after processing files\n" );
+	fprintf( msgout, " [-o]     overwrite existing files\n" );
+	fprintf( msgout, " [-p]     proceed on warnings\n" );
+	fprintf( msgout, " [-d]     discard meta-info\n" );
+	#if defined(DEV_BUILD)
+	if ( developer ) {
+	fprintf( msgout, "\n" );
+	fprintf( msgout, " [-s?]    set global number of segments (1<=s<=49)\n" );
+	fprintf( msgout, " [-t?]    set global noise threshold (0<=t<=10)\n" );
+	fprintf( msgout, "\n" );	
+	fprintf( msgout, " [-s?,?]  set number of segments for component\n" );
+	fprintf( msgout, " [-t?,?]  set noise threshold for component\n" );
+	fprintf( msgout, "\n" );
+	fprintf( msgout, " [-test]  test algorithms, alert if error\n" );
+	fprintf( msgout, " [-split] split jpeg (to header & image data)\n" );
+	fprintf( msgout, " [-coll?] write collections (0=std,1=dhf,2=squ,3=unc)\n" );
+	fprintf( msgout, " [-fcol?] write predicted collections (see above)\n" );
+	fprintf( msgout, " [-zdst]  write zero distribution lists\n" );	
+	fprintf( msgout, " [-info]  write debug info to .nfo file\n" );	
+	fprintf( msgout, " [-dist]  write distribution data to file\n" );
+	fprintf( msgout, " [-pgm]   convert and write to pgm files\n" );
+	}
+	#endif
+	fprintf( msgout, "\n" );
+	fprintf( msgout, "Examples: \"%s -v1 -o baboon.%s\"\n", appname, pjg_ext );
+	fprintf( msgout, "          \"%s -p *.%s\"\n", appname, jpg_ext );	
+}
+#endif
+
+
+/* -----------------------------------------------
+	processes one file
+	----------------------------------------------- */
+
+INTERN void process_file( void )
+{	
+	if ( filetype == F_JPG ) {
+		switch ( action ) {
+			case A_COMPRESS:
+				execute( read_jpeg );
+				execute( decode_jpeg );
+				execute( check_value_range );
+				execute( adapt_icos );
+				execute( predict_dc );
+				execute( calc_zdst_lists );
+				execute( pack_pjg );
+				#if !defined(BUILD_LIB)	
+				if ( verify_lv > 0 ) { // verifcation
+					execute( reset_buffers );
+					execute( swap_streams );
+					execute( unpack_pjg );
+					execute( adapt_icos );
+					execute( unpredict_dc );
+					execute( recode_jpeg );
+					execute( merge_jpeg );
+					execute( compare_output );
+				}
+				#endif
+				break;
+				
+			#if !defined(BUILD_LIB) && defined(DEV_BUILD)
+			case A_SPLIT_DUMP:
+				execute( read_jpeg );
+				execute( dump_hdr );
+				execute( dump_huf );
+				break;
+				
+			case A_COLL_DUMP:
+				execute( read_jpeg );
+				execute( decode_jpeg );
+				execute( dump_coll );
+				break;
+				
+			case A_FCOLL_DUMP:
+				execute( read_jpeg );
+				execute( decode_jpeg );
+				execute( check_value_range );
+				execute( adapt_icos );
+				execute( predict_dc );
+				execute( dump_coll );
+				break;
+				
+			case A_ZDST_DUMP:
+				execute( read_jpeg );
+				execute( decode_jpeg );
+				execute( check_value_range );
+				execute( adapt_icos );
+				execute( predict_dc );
+				execute( calc_zdst_lists );
+				execute( dump_zdst );
+				break;
+				
+			case A_TXT_INFO:
+				execute( read_jpeg );
+				execute( dump_info );
+				break;
+				
+			case A_DIST_INFO:
+				execute( read_jpeg );
+				execute( decode_jpeg );
+				execute( check_value_range );
+				execute( adapt_icos );
+				execute( predict_dc );
+				execute( dump_dist );
+				break;
+			
+			case A_PGM_DUMP:
+				execute( read_jpeg );
+				execute( decode_jpeg );
+				execute( adapt_icos );
+				execute( dump_pgm );
+				break;
+			#else
+			default:
+				break;
+			#endif
+		}
+	}
+	else if ( filetype == F_PJG )	{
+		switch ( action )
+		{
+			case A_COMPRESS:
+				execute( unpack_pjg );
+				execute( adapt_icos );
+				execute( unpredict_dc );
+				execute( recode_jpeg );
+				execute( merge_jpeg );
+				#if !defined(BUILD_LIB)
+				if ( verify_lv > 0 ) { // verify
+					execute( reset_buffers );
+					execute( swap_streams );
+					execute( read_jpeg );
+					execute( decode_jpeg );
+					execute( check_value_range );
+					execute( adapt_icos );
+					execute( predict_dc );
+					execute( calc_zdst_lists );
+					execute( pack_pjg );
+					execute( compare_output );
+				}
+				#endif
+				break;
+				
+			#if !defined(BUILD_LIB) && defined(DEV_BUILD)
+			case A_SPLIT_DUMP:
+				execute( unpack_pjg );
+				execute( adapt_icos );
+				execute( unpredict_dc );
+				execute( recode_jpeg );
+				execute( dump_hdr );
+				execute( dump_huf );
+				break;
+				
+			case A_COLL_DUMP:
+				execute( unpack_pjg );
+				execute( adapt_icos );			
+				execute( unpredict_dc );
+				execute( dump_coll );
+				break;
+				
+			case A_FCOLL_DUMP:				
+				execute( unpack_pjg );
+				execute( dump_coll );
+				break;
+				
+			case A_ZDST_DUMP:
+				execute( unpack_pjg );
+				execute( dump_zdst );
+				break;
+			
+			case A_TXT_INFO:
+				execute( unpack_pjg );
+				execute( dump_info );
+				break;
+			
+			case A_DIST_INFO:
+				execute( unpack_pjg );
+				execute( dump_dist );
+				break;
+			
+			case A_PGM_DUMP:
+				execute( unpack_pjg );
+				execute( adapt_icos );
+				execute( unpredict_dc );
+				execute( dump_pgm );
+				break;
+			#else
+			default:
+				break;
+			#endif
+		}
+	}	
+	#if !defined(BUILD_LIB) && defined(DEV_BUILD)
+	// write error file if verify lv > 1
+	if ( ( verify_lv > 1 ) && ( errorlevel >= err_tol ) )
+		dump_errfile();
+	#endif
+	// reset buffers
+	reset_buffers();
+}
+
+
+/* -----------------------------------------------
+	main-function execution routine
+	----------------------------------------------- */
+
+INTERN void execute( bool (*function)() )
+{
+	if ( errorlevel < err_tol ) {
+		#if !defined BUILD_LIB
+		clock_t begin, end;
+		bool success;
+		int total;
+		
+		// write statusmessage
+		if ( verbosity == 2 ) {
+			fprintf( msgout,  "\n%s ", get_status( function ) );
+			for ( int i = strlen( get_status( function ) ); i <= 30; i++ )
+				fprintf( msgout,  " " );			
+		}
+		
+		// set starttime
+		begin = clock();
+		// call function
+		success = ( *function )();
+		// set endtime
+		end = clock();
+		
+		if ( ( errorlevel > 0 ) && ( errorfunction == NULL ) )
+			errorfunction = function;
+		
+		// write time or failure notice
+		if ( success ) {
+			total = (int) ( (double) (( end - begin ) * 1000) / CLOCKS_PER_SEC );
+			if ( verbosity == 2 ) fprintf( msgout,  "%6ims", ( total >= 0 ) ? total : -1 );
+		}
+		else {
+			errorfunction = function;
+			if ( verbosity == 2 ) fprintf( msgout,  "%8s", "ERROR" );
+		}
+		#else
+		// call function
+		( *function )();
+		
+		// store errorfunction if needed
+		if ( ( errorlevel > 0 ) && ( errorfunction == NULL ) )
+			errorfunction = function;
+		#endif
+	}
+}
+
+/* ----------------------- End of main interface functions -------------------------- */
+
+/* ----------------------- Begin of main functions -------------------------- */
+
+
+/* -----------------------------------------------
+	check file and determine filetype
+	----------------------------------------------- */
+
+#if !defined(BUILD_LIB)
+INTERN bool check_file( void )
+{	
+	unsigned char fileid[ 2 ] = { 0, 0 };
+	const char* filename = filelist[ file_no ];
+	
+	try {
+		if (pipe_on) {
+			str_in = std::make_unique<StreamReader>();
+		} else {
+			str_in = std::make_unique<FileReader>(std::string(filename));
+		}
+	} catch (const std::runtime_error& e) {
+		std::strcpy(errormessage, e.what());
+		errorlevel = 2;
+		return false;
+	}
+	
+	// free memory from filenames if needed
+	if (jpgfilename != nullptr) {
+		free(jpgfilename);
+		jpgfilename = nullptr;
+	}
+	if (pjgfilename != nullptr) {
+		free(pjgfilename);
+		pjgfilename = nullptr;
+	}
+	
+	// immediately return error if 2 bytes can't be read
+	if ( str_in->read( fileid, 2 ) != 2 ) { 
+		filetype = F_UNK;
+		sprintf( errormessage, "file doesn't contain enough data" );
+		errorlevel = 2;
+		return false;
+	}
+	
+	// check file id, determine filetype
+	if ( ( fileid[0] == 0xFF ) && ( fileid[1] == 0xD8 ) ) {
+		// file is JPEG
+		filetype = F_JPG;
+		// create filenames
+		if ( !pipe_on ) {
+			jpgfilename = (char*) calloc( strlen( filename ) + 1, sizeof( char ) );
+			strcpy( jpgfilename, filename );
+			pjgfilename = ( overwrite ) ?
+				create_filename( filename, (char*) pjg_ext ) :
+				unique_filename( filename, (char*) pjg_ext );
+		}
+		else {
+			jpgfilename = create_filename( "STDIN", NULL );
+			pjgfilename = create_filename( "STDOUT", NULL );
+		}
+
+		try {
+			if (pipe_on) {
+				str_out = std::make_unique<StreamWriter>();
+			} else {
+				str_out = std::make_unique<FileWriter>(std::string(pjgfilename));
+			}
+		} catch (const std::runtime_error& e) {
+			std::strcpy(errormessage, e.what());
+            errorlevel = 2;
+            return false;
+		}
+		// JPEG specific settings - restore original settings
+		if ( orig_set[ 0 ] == 0 )
+			auto_set = true;
+		else {	
+			nois_trs[ 0 ] = orig_set[ 0 ];
+			nois_trs[ 1 ] = orig_set[ 1 ];
+			nois_trs[ 2 ] = orig_set[ 2 ];
+			nois_trs[ 3 ] = orig_set[ 3 ];
+			segm_cnt[ 0 ] = orig_set[ 4 ];
+			segm_cnt[ 1 ] = orig_set[ 5 ];
+			segm_cnt[ 2 ] = orig_set[ 6 ];
+			segm_cnt[ 3 ] = orig_set[ 7 ];
+			auto_set = false;
+		}
+	}
+	else if ( ( fileid[0] == pjg_magic[0] ) && ( fileid[1] == pjg_magic[1] ) ) {
+		// file is PJG
+		filetype = F_PJG;
+		// create filenames
+		if ( !pipe_on ) {
+			pjgfilename = (char*) calloc( strlen( filename ) + 1, sizeof( char ) );
+			strcpy( pjgfilename, filename );
+			jpgfilename = ( overwrite ) ?
+				create_filename( filename, (char*) jpg_ext ) :
+				unique_filename( filename, (char*) jpg_ext );
+		}
+		else {
+			jpgfilename = create_filename( "STDOUT", NULL );
+			pjgfilename = create_filename( "STDIN", NULL );
+		}
+		// open output stream, check for errors
+        if (pipe_on) {
+            str_out = std::make_unique<StreamWriter>();
+        } else {
+            str_out = std::make_unique<FileWriter>(std::string(jpgfilename));
+        }
+		// PJG specific settings - auto unless specified otherwise
+		auto_set = true;
+	}
+	else {
+		// file is neither
+		filetype = F_UNK;
+		sprintf( errormessage, "filetype of file \"%s\" is unknown", filename );
+		errorlevel = 2;
+		return false;		
+	}
+	
+	
+	return true;
+}
+#endif
+
+
+/* -----------------------------------------------
+	swap streams / init verification
+	----------------------------------------------- */
+	
+#if !defined(BUILD_LIB)
+INTERN bool swap_streams( void )	
+{
+	unsigned char dmp[ 2 ];
+	
+	// store input stream
+	str_str = std::move(str_in);
+	str_str->rewind();
+	
+	// replace input stream by output stream / switch mode for reading / read first bytes
+    str_in = std::make_unique<MemoryReader>(str_out->get_data());
+	str_in->read( dmp, 2 );
+	
+	// open new stream for output / check for errors
+    str_out = std::make_unique<MemoryWriter>();
+	
+	return true;
+}
+#endif
+
+
+/* -----------------------------------------------
+	comparison between input & output
+	----------------------------------------------- */
+
+#if !defined(BUILD_LIB)
+INTERN bool compare_output( void )
+{
+    if (str_out->error()) {
+        sprintf(errormessage, "error in comparison stream");
+        errorlevel = 2;
+        return false;
+    } else if (str_in->error()) {
+        sprintf(errormessage, "error in output stream");
+        errorlevel = 2;
+        return false;
+    } else if (str_str->error()) {
+        sprintf(errormessage, "error in input stream");
+        errorlevel = 2;
+        return false;
+    }
+    
+	const auto verif_data = str_out->get_data();
+    const auto orig_data = str_str->get_data();
+    
+	if (verif_data.size() != orig_data.size()) {
+		sprintf( errormessage, "file sizes do not match" );
+		errorlevel = 2;
+		return false;
+	}
+	const auto result = std::mismatch(std::begin(orig_data),
+	                                  std::end(orig_data),
+	                                  std::begin(verif_data),
+	                                  std::end(verif_data));
+	if (result.first != std::end(orig_data) || result.second != std::end(verif_data)) {
+		const auto first_diff = std::distance(std::begin(orig_data), result.first);
+        sprintf( errormessage, "difference found at 0x%ld", first_diff );
+		errorlevel = 2;
+		return false;
+	}
+	return true;
+}
+#endif
+
+
+/* -----------------------------------------------
+	set each variable to its initial value
+	----------------------------------------------- */
+
+INTERN bool reset_buffers( void )
+{
+	int cmp, bpos;
+	int i;
+	
+	
+	// -- free buffers --
+	
+	// free buffers & set pointers NULL
+	if ( hdrdata  != NULL ) free ( hdrdata );
+	if ( huffdata != NULL ) free ( huffdata );
+	if ( grbgdata != NULL ) free ( grbgdata );
+	if ( rst_err  != NULL ) free ( rst_err );
+	if ( rstp     != NULL ) free ( rstp );
+	if ( scnp     != NULL ) free ( scnp );
+	hdrdata   = NULL;
+	huffdata  = NULL;
+	grbgdata  = NULL;
+	rst_err   = NULL;
+	rstp      = NULL;
+	scnp      = NULL;
+	
+	// free image arrays
+	for ( cmp = 0; cmp < 4; cmp++ )	{
+		if ( zdstdata[ cmp ] != NULL ) free( zdstdata[cmp] );
+		if ( eobxhigh[ cmp ] != NULL ) free( eobxhigh[cmp] );
+		if ( eobyhigh[ cmp ] != NULL ) free( eobyhigh[cmp] );
+		if ( zdstxlow[ cmp ] != NULL ) free( zdstxlow[cmp] );
+		if ( zdstylow[ cmp ] != NULL ) free( zdstylow[cmp] );
+		zdstdata[ cmp ] = NULL;
+		eobxhigh[ cmp ] = NULL;
+		eobyhigh[ cmp ] = NULL;
+		zdstxlow[ cmp ] = NULL;
+		zdstylow[ cmp ] = NULL;
+		freqscan[ cmp ] = (unsigned char*) stdscan;
+		
+		for ( bpos = 0; bpos < 64; bpos++ ) {
+			if ( colldata[ cmp ][ bpos ] != NULL ) free( colldata[cmp][bpos] );
+			colldata[ cmp ][ bpos ] = NULL;
+		}		
+	}
+	
+	
+	// -- set variables --
+	
+	// preset componentinfo
+	for ( cmp = 0; cmp < 4; cmp++ ) {
+		cmpnfo[ cmp ].sfv = -1;
+		cmpnfo[ cmp ].sfh = -1;
+		cmpnfo[ cmp ].mbs = -1;
+		cmpnfo[ cmp ].bcv = -1;
+		cmpnfo[ cmp ].bch = -1;
+		cmpnfo[ cmp ].bc  = -1;
+		cmpnfo[ cmp ].ncv = -1;
+		cmpnfo[ cmp ].nch = -1;
+		cmpnfo[ cmp ].nc  = -1;
+		cmpnfo[ cmp ].sid = -1;
+		cmpnfo[ cmp ].jid = -1;
+		cmpnfo[ cmp ].qtable = NULL;
+		cmpnfo[ cmp ].huffdc = -1;
+		cmpnfo[ cmp ].huffac = -1;
+	}
+	
+	// preset imgwidth / imgheight / component count 
+	imgwidth  = 0;
+	imgheight = 0;
+	cmpc      = 0;
+	
+	// preset mcu info variables / restart interval
+	sfhm      = 0;
+	sfvm      = 0;
+	mcuc      = 0;
+	mcuh      = 0;
+	mcuv      = 0;
+	rsti      = 0;
+	
+	// reset quantization / huffman tables
+	for ( i = 0; i < 4; i++ ) {
+		htset[ 0 ][ i ] = 0;
+		htset[ 1 ][ i ] = 0;
+		for ( bpos = 0; bpos < 64; bpos++ )
+			qtables[ i ][ bpos ] = 0;
+	}
+	
+	// preset jpegtype
+	jpegtype  = 0;
+	
+	// reset padbit
+	padbit = -1;
+	
+	
+	return true;
+}
+
+
+/* -----------------------------------------------
+	Read in header & image data
+	----------------------------------------------- */
+	
+INTERN bool read_jpeg( void )
+{
+	unsigned char* segment = NULL; // storage for current segment
+	unsigned int   ssize = 1024; // current size of segment array
+	unsigned char  type = 0x00; // type of current marker segment
+	unsigned int   len  = 0; // length of current marker segment
+	unsigned int   crst = 0; // current rst marker counter
+	unsigned int   cpos = 0; // rst marker counter
+	unsigned char  tmp;	
+	
+	MemoryWriter* huffw;	
+	MemoryWriter* hdrw;
+	MemoryWriter* grbgw;	
+	
+	
+	// preset count of scans
+	scnc = 0;
+	
+	// start headerwriter
+	hdrw = new MemoryWriter();
+	hdrs = 0; // size of header data, start with 0
+	
+	// start huffman writer
+	huffw = new MemoryWriter();
+	hufs  = 0; // size of image data, start with 0
+	
+	// alloc memory for segment data first
+	segment = ( unsigned char* ) calloc( ssize, sizeof( char ) );
+	if ( segment == NULL ) {
+		sprintf( errormessage, MEM_ERRMSG );
+		errorlevel = 2;
+		return false;
+	}
+	
+	// JPEG reader loop
+	while ( true ) {
+		if ( type == 0xDA ) { // if last marker was sos
+			// switch to huffman data reading mode
+			cpos = 0;
+			crst = 0;
+			while ( true ) {
+				// read byte from imagedata
+				if ( str_in->read_byte(&tmp) == 0 )
+					break;
+					
+				// non-0xFF loop
+				if ( tmp != 0xFF ) {
+					crst = 0;
+					while ( tmp != 0xFF ) {
+						huffw->write_byte( tmp );
+						if ( str_in->read_byte(&tmp) == 0 )
+							break;
+					}
+				}
+				
+				// treatment of 0xFF
+				if ( tmp == 0xFF ) {
+					if ( str_in->read_byte(&tmp) == 0 )
+						break; // read next byte & check
+					if ( tmp == 0x00 ) {
+						crst = 0;
+						// no zeroes needed -> ignore 0x00. write 0xFF
+						huffw->write_byte( 0xFF );
+					}
+					else if ( tmp == 0xD0 + ( cpos % 8 ) ) { // restart marker
+						// increment rst counters
+						cpos++;
+						crst++;
+					}
+					else { // in all other cases leave it to the header parser routines
+						// store number of wrongly set rst markers
+						if ( crst > 0 ) {
+							if ( rst_err == NULL ) {
+								rst_err = (unsigned char*) calloc( scnc + 1, sizeof( char ) );
+								if ( rst_err == NULL ) {
+									sprintf( errormessage, MEM_ERRMSG );
+									errorlevel = 2;
+									return false;
+								}
+							}
+						}
+						if ( rst_err != NULL ) {
+							// realloc and set only if needed
+							rst_err = ( unsigned char* ) frealloc( rst_err, ( scnc + 1 ) * sizeof( char ) );
+							if ( rst_err == NULL ) {
+								sprintf( errormessage, MEM_ERRMSG );
+								errorlevel = 2;
+								return false;
+							}
+							if ( crst > 255 ) {
+								sprintf( errormessage, "Severe false use of RST markers (%i)", (int) crst );
+								errorlevel = 1;
+								crst = 255;
+							}
+							rst_err[ scnc ] = crst;							
+						}
+						// end of current scan
+						scnc++;
+						// on with the header parser routines
+						segment[ 0 ] = 0xFF;
+						segment[ 1 ] = tmp;
+						break;
+					}
+				}
+				else {
+					// otherwise this means end-of-file, so break out
+					break;
+				}
+			}
+		}
+		else {
+			// read in next marker
+			if ( str_in->read( segment, 2 ) != 2 ) break;
+			if ( segment[ 0 ] != 0xFF ) {
+				// ugly fix for incorrect marker segment sizes
+				sprintf( errormessage, "size mismatch in marker segment FF %2X", type );
+				errorlevel = 2;
+				if ( type == 0xFE ) { //  if last marker was COM try again
+					if ( str_in->read( segment, 2 ) != 2 ) break;
+					if ( segment[ 0 ] == 0xFF ) errorlevel = 1;
+				}
+				if ( errorlevel == 2 ) {
+					delete ( hdrw );
+					delete ( huffw );
+					free ( segment );
+					return false;
+				}
+			}
+		}
+		
+		// read segment type
+		type = segment[ 1 ];
+		
+		// if EOI is encountered make a quick exit
+		if ( type == 0xD9 ) {
+			// get pointer for header data & size
+			hdrdata  = hdrw->get_c_data();
+			hdrs     = hdrw->num_bytes_written();
+			// get pointer for huffman data & size
+			huffdata = huffw->get_c_data();
+			hufs     = huffw->num_bytes_written();
+			// everything is done here now
+			break;			
+		}
+		
+		// read in next segments' length and check it
+		if ( str_in->read( segment + 2, 2 ) != 2 ) break;
+		len = 2 + B_SHORT( segment[ 2 ], segment[ 3 ] );
+		if ( len < 4 ) break;
+		
+		// realloc segment data if needed
+		if ( ssize < len ) {
+			segment = ( unsigned char* ) frealloc( segment, len );
+			if ( segment == NULL ) {
+				sprintf( errormessage, MEM_ERRMSG );
+				errorlevel = 2;
+				delete ( hdrw );
+				delete ( huffw );
+				return false;
+			}
+			ssize = len;
+		}
+		
+		// read rest of segment, store back in header writer
+		if ( str_in->read( ( segment + 4 ), ( len - 4 ) ) !=
+			( unsigned short ) ( len - 4 ) ) break;
+		hdrw->write( segment, len );
+	}
+	// JPEG reader loop end
+	
+	// free writers
+	delete ( hdrw );
+	delete ( huffw );
+	
+	// check if everything went OK
+	if ( ( hdrs == 0 ) || ( hufs == 0 ) ) {
+		sprintf( errormessage, "unexpected end of data encountered" );
+		errorlevel = 2;
+		return false;
+	}
+	
+	// store garbage after EOI if needed
+	grbs = str_in->read_byte(&tmp);
+	if ( grbs > 0 ) {
+		grbgw = new MemoryWriter();
+		grbgw->write_byte( tmp );
+		while( true ) {
+			len = str_in->read( segment, ssize );
+			if ( len == 0 ) break;
+			grbgw->write( segment, len );
+		}
+		grbgdata = grbgw->get_c_data();
+		grbs     = grbgw->num_bytes_written();
+		delete grbgw;
+	}
+	
+	// free segment
+	free( segment );
+	
+	// get filesize
+	jpgfilesize = str_in->get_size();	
+	
+	// parse header for image info
+	if ( !jpg_setup_imginfo() ) {
+		return false;
+	}
+	
+	
+	return true;
+}
+
+
+/* -----------------------------------------------
+	Merges header & image data to jpeg
+	----------------------------------------------- */
+	
+INTERN bool merge_jpeg( void )
+{
+	unsigned char SOI[ 2 ] = { 0xFF, 0xD8 }; // SOI segment
+	unsigned char EOI[ 2 ] = { 0xFF, 0xD9 }; // EOI segment
+	unsigned char mrk = 0xFF; // marker start
+	unsigned char stv = 0x00; // 0xFF stuff value
+	unsigned char rst = 0xD0; // restart marker
+	
+	unsigned char  type = 0x00; // type of current marker segment
+	unsigned int   len  = 0; // length of current marker segment
+	unsigned int   hpos = 0; // current position in header
+	unsigned int   ipos = 0; // current position in imagedata
+	unsigned int   rpos = 0; // current restart marker position
+	unsigned int   cpos = 0; // in scan corrected rst marker position
+	unsigned int   scan = 1; // number of current scan
+	unsigned int   tmp; // temporary storage variable
+	
+	
+	// write SOI
+	str_out->write( SOI, 2 );
+	
+	// JPEG writing loop
+	while ( true )
+	{		
+		// store current header position
+		tmp = hpos;
+		
+		// seek till start-of-scan
+		for ( type = 0x00; type != 0xDA; ) {
+			if ( ( int ) hpos >= hdrs ) break;
+			type = hdrdata[ hpos + 1 ];
+			len = 2 + B_SHORT( hdrdata[ hpos + 2 ], hdrdata[ hpos + 3 ] );
+			hpos += len;
+		}
+		
+		// write header data to file
+		str_out->write( hdrdata + tmp, ( hpos - tmp ) );
+		
+		// get out if last marker segment type was not SOS
+		if ( type != 0xDA ) break;
+		
+		
+		// (re)set corrected rst pos
+		cpos = 0;
+		
+		// write & expand huffman coded image data
+		for ( ipos = scnp[ scan - 1 ]; ipos < scnp[ scan ]; ipos++ ) {
+			// write current byte
+			str_out->write_byte(huffdata[ipos]);
+			// check current byte, stuff if needed
+			if ( huffdata[ ipos ] == 0xFF )
+				str_out->write_byte(stv);
+			// insert restart markers if needed
+			if ( rstp != NULL ) {
+				if ( ipos == rstp[ rpos ] ) {
+					rst = 0xD0 + ( cpos % 8 );
+					str_out->write_byte(mrk);
+					str_out->write_byte(rst);
+					rpos++; cpos++;
+				}
+			}
+		}
+		// insert false rst markers at end if needed
+		if ( rst_err != NULL ) {
+			while ( rst_err[ scan - 1 ] > 0 ) {
+				rst = 0xD0 + ( cpos % 8 );
+				str_out->write_byte(mrk);
+				str_out->write_byte(rst);
+				cpos++;	rst_err[ scan - 1 ]--;
+			}
+		}
+
+		// proceed with next scan
+		scan++;
+	}
+	
+	// write EOI
+	str_out->write( EOI, 2 );
+	
+	// write garbage if needed
+	if ( grbs > 0 )
+		str_out->write( grbgdata, grbs );
+	
+	// errormessage if write error
+	if ( str_out->error() ) {
+		sprintf( errormessage, "write error, possibly drive is full" );
+		errorlevel = 2;		
+		return false;
+	}
+	
+	// get filesize
+	jpgfilesize = str_out->num_bytes_written();
+	
+	
+	return true;
+}
+
+
+/* -----------------------------------------------
+	JPEG decoding routine
+	----------------------------------------------- */
+
+INTERN bool decode_jpeg( void )
+{
+	BitReader* huffr; // bitwise reader for image data
+	
+	unsigned char  type = 0x00; // type of current marker segment
+	unsigned int   len  = 0; // length of current marker segment
+	unsigned int   hpos = 0; // current position in header
+	
+	int lastdc[ 4 ]; // last dc for each component
+	short block[ 64 ]; // store block for coeffs
+	int peobrun; // previous eobrun
+	int eobrun; // run of eobs
+	int rstw; // restart wait counter
+	
+	int cmp, bpos, dpos;
+	int mcu, sub, csc;
+	int eob, sta;
+	
+	
+	// open huffman coded image data for input in BitReader
+	huffr = new BitReader( huffdata, hufs );
+	
+	// preset count of scans
+	scnc = 0;
+	
+	// JPEG decompression loop
+	while ( true )
+	{
+		// seek till start-of-scan, parse only DHT, DRI and SOS
+		for ( type = 0x00; type != 0xDA; ) {
+			if ( ( int ) hpos >= hdrs ) break;
+			type = hdrdata[ hpos + 1 ];
+			len = 2 + B_SHORT( hdrdata[ hpos + 2 ], hdrdata[ hpos + 3 ] );
+			if ( ( type == 0xC4 ) || ( type == 0xDA ) || ( type == 0xDD ) ) {
+				if ( !jpg_parse_jfif( type, len, &( hdrdata[ hpos ] ) ) ) {
+					return false;
+				}
+			}
+			hpos += len;
+		}
+		
+		// get out if last marker segment type was not SOS
+		if ( type != 0xDA ) break;
+		
+		// check if huffman tables are available
+		for ( csc = 0; csc < cs_cmpc; csc++ ) {
+			cmp = cs_cmp[ csc ];
+			if ( ( ( cs_sal == 0 ) && ( htset[ 0 ][ cmpnfo[cmp].huffdc ] == 0 ) ) ||
+				 ( ( cs_sah >  0 ) && ( htset[ 1 ][ cmpnfo[cmp].huffac ] == 0 ) ) ) {
+				sprintf( errormessage, "huffman table missing in scan%i", scnc );
+				delete huffr;
+				errorlevel = 2;
+				return false;
+			}
+		}
+		
+		
+		// intial variables set for decoding
+		cmp  = cs_cmp[ 0 ];
+		csc  = 0;
+		mcu  = 0;
+		sub  = 0;
+		dpos = 0;
+		
+		// JPEG imagedata decoding routines
+		while ( true )
+		{			
+			// (re)set last DCs for diff coding
+			lastdc[ 0 ] = 0;
+			lastdc[ 1 ] = 0;
+			lastdc[ 2 ] = 0;
+			lastdc[ 3 ] = 0;
+			
+			// (re)set status
+			eob = 0;
+			sta = 0;
+			
+			// (re)set eobrun
+			eobrun  = 0;
+			peobrun = 0;
+			
+			// (re)set rst wait counter
+			rstw = rsti;
+			
+			// decoding for interleaved data
+			if ( cs_cmpc > 1 )
+			{				
+				if ( jpegtype == 1 ) {
+					// ---> sequential interleaved decoding <---
+					while ( sta == 0 ) {
+						// decode block
+						eob = jpg_decode_block_seq( huffr,
+							&(htrees[ 0 ][ cmpnfo[cmp].huffdc ]),
+							&(htrees[ 1 ][ cmpnfo[cmp].huffdc ]),
+							block );
+						
+						// check for non optimal coding
+						if ( ( eob > 1 ) && ( block[ eob - 1 ] == 0 ) ) {
+							sprintf( errormessage, "reconstruction of inefficient coding not supported" );
+							errorlevel = 1;
+						}
+						
+						// fix dc
+						block[ 0 ] += lastdc[ cmp ];
+						lastdc[ cmp ] = block[ 0 ];
+						
+						// copy to colldata
+						for ( bpos = 0; bpos < eob; bpos++ )
+							colldata[ cmp ][ bpos ][ dpos ] = block[ bpos ];
+						
+						// check for errors, proceed if no error encountered
+						if ( eob < 0 ) sta = -1;
+						else sta = jpg_next_mcupos( &mcu, &cmp, &csc, &sub, &dpos, &rstw );
+					}
+				}
+				else if ( cs_sah == 0 ) {
+					// ---> progressive interleaved DC decoding <---
+					// ---> succesive approximation first stage <---
+					while ( sta == 0 ) {
+						sta = jpg_decode_dc_prg_fs( huffr,
+							&(htrees[ 0 ][ cmpnfo[cmp].huffdc ]),
+							block );
+						
+						// fix dc for diff coding
+						colldata[cmp][0][dpos] = block[0] + lastdc[ cmp ];
+						lastdc[ cmp ] = colldata[cmp][0][dpos];
+						
+						// bitshift for succesive approximation
+						colldata[cmp][0][dpos] <<= cs_sal;
+						
+						// next mcupos if no error happened
+						if ( sta != -1 )
+							sta = jpg_next_mcupos( &mcu, &cmp, &csc, &sub, &dpos, &rstw );
+					}
+				}
+				else {
+					// ---> progressive interleaved DC decoding <---
+					// ---> succesive approximation later stage <---					
+					while ( sta == 0 ) {
+						// decode next bit
+						sta = jpg_decode_dc_prg_sa( huffr,
+							block );
+						
+						// shift in next bit
+						colldata[cmp][0][dpos] += block[0] << cs_sal;
+						
+						// next mcupos if no error happened
+						if ( sta != -1 )
+							sta = jpg_next_mcupos( &mcu, &cmp, &csc, &sub, &dpos, &rstw );
+					}
+				}
+			}
+			else // decoding for non interleaved data
+			{
+				if ( jpegtype == 1 ) {
+					// ---> sequential non interleaved decoding <---
+					while ( sta == 0 ) {
+						// decode block
+						eob = jpg_decode_block_seq( huffr,
+							&(htrees[ 0 ][ cmpnfo[cmp].huffdc ]),
+							&(htrees[ 1 ][ cmpnfo[cmp].huffdc ]),
+							block );
+						
+						// check for non optimal coding
+						if ( ( eob > 1 ) && ( block[ eob - 1 ] == 0 ) ) {
+							sprintf( errormessage, "reconstruction of inefficient coding not supported" );
+							errorlevel = 1;
+						}
+						
+						// fix dc
+						block[ 0 ] += lastdc[ cmp ];
+						lastdc[ cmp ] = block[ 0 ];
+						
+						// copy to colldata
+						for ( bpos = 0; bpos < eob; bpos++ )
+							colldata[ cmp ][ bpos ][ dpos ] = block[ bpos ];
+						
+						// check for errors, proceed if no error encountered
+						if ( eob < 0 ) sta = -1;
+						else sta = jpg_next_mcuposn( &cmp, &dpos, &rstw );
+					}
+				}
+				else if ( cs_to == 0 ) {					
+					if ( cs_sah == 0 ) {
+						// ---> progressive non interleaved DC decoding <---
+						// ---> succesive approximation first stage <---
+						while ( sta == 0 ) {
+							sta = jpg_decode_dc_prg_fs( huffr,
+								&(htrees[ 0 ][ cmpnfo[cmp].huffdc ]),
+								block );
+								
+							// fix dc for diff coding
+							colldata[cmp][0][dpos] = block[0] + lastdc[ cmp ];
+							lastdc[ cmp ] = colldata[cmp][0][dpos];
+							
+							// bitshift for succesive approximation
+							colldata[cmp][0][dpos] <<= cs_sal;
+							
+							// check for errors, increment dpos otherwise
+							if ( sta != -1 )
+								sta = jpg_next_mcuposn( &cmp, &dpos, &rstw );
+						}
+					}
+					else {
+						// ---> progressive non interleaved DC decoding <---
+						// ---> succesive approximation later stage <---
+						while( sta == 0 ) {
+							// decode next bit
+							sta = jpg_decode_dc_prg_sa( huffr,
+								block );
+							
+							// shift in next bit
+							colldata[cmp][0][dpos] += block[0] << cs_sal;
+							
+							// check for errors, increment dpos otherwise
+							if ( sta != -1 )
+								sta = jpg_next_mcuposn( &cmp, &dpos, &rstw );
+						}
+					}
+				}
+				else {
+					if ( cs_sah == 0 ) {
+						// ---> progressive non interleaved AC decoding <---
+						// ---> succesive approximation first stage <---
+						while ( sta == 0 ) {
+							if ( eobrun == 0 ) {
+								// decode block
+								eob = jpg_decode_ac_prg_fs( huffr,
+									&(htrees[ 1 ][ cmpnfo[cmp].huffac ]),
+									block, &eobrun, cs_from, cs_to );
+								
+								if ( eobrun > 0 ) {
+									// check for non optimal coding
+									if ( ( eob == cs_from )  && ( peobrun > 0 ) &&
+										( peobrun <	hcodes[ 1 ][ cmpnfo[cmp].huffac ].max_eobrun - 1 ) ) {
+										sprintf( errormessage,
+											"reconstruction of inefficient coding not supported" );
+										errorlevel = 1;
+									}
+									peobrun = eobrun;
+									eobrun--;
+								} else peobrun = 0;
+							
+								// copy to colldata
+								for ( bpos = cs_from; bpos < eob; bpos++ )
+									colldata[ cmp ][ bpos ][ dpos ] = block[ bpos ] << cs_sal;
+							} else eobrun--;
+							
+							// check for errors
+							if ( eob < 0 ) sta = -1;
+							else sta = jpg_skip_eobrun( &cmp, &dpos, &rstw, &eobrun );
+							
+							// proceed only if no error encountered
+							if ( sta == 0 )
+								sta = jpg_next_mcuposn( &cmp, &dpos, &rstw );
+						}
+					}
+					else {
+						// ---> progressive non interleaved AC decoding <---
+						// ---> succesive approximation later stage <---
+						while ( sta == 0 ) {
+							// copy from colldata
+							for ( bpos = cs_from; bpos <= cs_to; bpos++ )
+								block[ bpos ] = colldata[ cmp ][ bpos ][ dpos ];
+							
+							if ( eobrun == 0 ) {
+								// decode block (long routine)
+								eob = jpg_decode_ac_prg_sa( huffr,
+									&(htrees[ 1 ][ cmpnfo[cmp].huffac ]),
+									block, &eobrun, cs_from, cs_to );
+								
+								if ( eobrun > 0 ) {
+									// check for non optimal coding
+									if ( ( eob == cs_from ) && ( peobrun > 0 ) &&
+										( peobrun < hcodes[ 1 ][ cmpnfo[cmp].huffac ].max_eobrun - 1 ) ) {
+										sprintf( errormessage,
+											"reconstruction of inefficient coding not supported" );
+										errorlevel = 1;
+									}
+									
+									// store eobrun
+									peobrun = eobrun;
+									eobrun--;
+								} else peobrun = 0;
+							}
+							else {
+								// decode block (short routine)
+								eob = jpg_decode_eobrun_sa( huffr,
+									block, &eobrun, cs_from, cs_to );
+								eobrun--;
+							}
+								
+							// copy back to colldata
+							for ( bpos = cs_from; bpos <= cs_to; bpos++ )
+								colldata[ cmp ][ bpos ][ dpos ] += block[ bpos ] << cs_sal;
+							
+							// proceed only if no error encountered
+							if ( eob < 0 ) sta = -1;
+							else sta = jpg_next_mcuposn( &cmp, &dpos, &rstw );
+						}
+					}
+				}
+			}			
+			
+			// unpad huffman reader / check padbit
+			if ( padbit != -1 ) {
+				if ( padbit != huffr->unpad( padbit ) ) {
+					sprintf( errormessage, "inconsistent use of padbits" );
+					padbit = 1;
+					errorlevel = 1;
+				}
+			}
+			else {
+				padbit = huffr->unpad( padbit );
+			}
+			
+			// evaluate status
+			if ( sta == -1 ) { // status -1 means error
+				sprintf( errormessage, "decode error in scan%i / mcu%i",
+					scnc, ( cs_cmpc > 1 ) ? mcu : dpos );
+				delete huffr;
+				errorlevel = 2;
+				return false;
+			}
+			else if ( sta == 2 ) { // status 2/3 means done
+				scnc++; // increment scan counter
+				break; // leave decoding loop, everything is done here
+			}
+			// else if ( sta == 1 ); // status 1 means restart - so stay in the loop
+		}
+	}
+	
+	// check for missing data
+	if ( huffr->peof() > 0 ) {
+		sprintf( errormessage, "coded image data truncated / too short" );
+		errorlevel = 1;
+	}
+	
+	// check for surplus data
+	if ( !huffr->eof()) {
+		sprintf( errormessage, "surplus data found after coded image data" );
+		errorlevel = 1;
+	}
+	
+	// clean up
+	delete( huffr );
+	
+	
+	return true;
+}
+
+
+/* -----------------------------------------------
+	JPEG encoding routine
+	----------------------------------------------- */
+
+INTERN bool recode_jpeg( void )
+{
+	BitWriter*  huffw; // bitwise writer for image data
+	
+	unsigned char  type = 0x00; // type of current marker segment
+	unsigned int   len  = 0; // length of current marker segment
+	unsigned int   hpos = 0; // current position in header
+		
+	int lastdc[ 4 ]; // last dc for each component0
+	short block[ 64 ]; // store block for coeffs
+	int eobrun; // run of eobs
+	int rstw; // restart wait counter
+	
+	int cmp, bpos, dpos;
+	int mcu, sub, csc;
+	int eob, sta;
+	int tmp;
+	
+	
+	// open huffman coded image data in BitWriter
+	huffw = new BitWriter(padbit);
+	
+	// init storage writer
+	std::vector<std::uint8_t> storw; // Storage for correction bits.
+	
+	// preset count of scans and restarts
+	scnc = 0;
+	rstc = 0;
+	
+	// JPEG decompression loop
+	while ( true )
+	{
+		// seek till start-of-scan, parse only DHT, DRI and SOS
+		for ( type = 0x00; type != 0xDA; ) {
+			if ( ( int ) hpos >= hdrs ) break;
+			type = hdrdata[ hpos + 1 ];
+			len = 2 + B_SHORT( hdrdata[ hpos + 2 ], hdrdata[ hpos + 3 ] );
+			if ( ( type == 0xC4 ) || ( type == 0xDA ) || ( type == 0xDD ) ) {
+				if ( !jpg_parse_jfif( type, len, &( hdrdata[ hpos ] ) ) ) {
+					return false;
+				}
+				hpos += len;
+			}
+			else {
+				hpos += len;
+				continue;
+			}			
+		}
+		
+		// get out if last marker segment type was not SOS
+		if ( type != 0xDA ) break;
+		
+		
+		// (re)alloc scan positons array
+		if ( scnp == NULL ) scnp = ( unsigned int* ) calloc( scnc + 2, sizeof( int ) );
+		else scnp = ( unsigned int* ) frealloc( scnp, ( scnc + 2 ) * sizeof( int ) );
+		if ( scnp == NULL ) {
+			sprintf( errormessage, MEM_ERRMSG );
+			errorlevel = 2;
+			return false;
+		}
+		
+		// (re)alloc restart marker positons array if needed
+		if ( rsti > 0 ) {
+			tmp = rstc + ( ( cs_cmpc > 1 ) ?
+				( mcuc / rsti ) : ( cmpnfo[ cs_cmp[ 0 ] ].bc / rsti ) );
+			if ( rstp == NULL ) rstp = ( unsigned int* ) calloc( tmp + 1, sizeof( int ) );
+			else rstp = ( unsigned int* ) frealloc( rstp, ( tmp + 1 ) * sizeof( int ) );
+			if ( rstp == NULL ) {
+				sprintf( errormessage, MEM_ERRMSG );
+				errorlevel = 2;
+				return false;
+			}
+		}		
+		
+		// intial variables set for encoding
+		cmp  = cs_cmp[ 0 ];
+		csc  = 0;
+		mcu  = 0;
+		sub  = 0;
+		dpos = 0;
+		
+		// store scan position
+		scnp[ scnc ] = huffw->num_bytes_written();
+		
+		// JPEG imagedata encoding routines
+		while ( true )
+		{
+			// (re)set last DCs for diff coding
+			lastdc[ 0 ] = 0;
+			lastdc[ 1 ] = 0;
+			lastdc[ 2 ] = 0;
+			lastdc[ 3 ] = 0;
+			
+			// (re)set status
+			sta = 0;
+			
+			// (re)set eobrun
+			eobrun = 0;
+			
+			// (re)set rst wait counter
+			rstw = rsti;
+			
+			// encoding for interleaved data
+			if ( cs_cmpc > 1 )
+			{				
+				if ( jpegtype == 1 ) {
+					// ---> sequential interleaved encoding <---
+					while ( sta == 0 ) {
+						// copy from colldata
+						for ( bpos = 0; bpos < 64; bpos++ )
+							block[ bpos ] = colldata[ cmp ][ bpos ][ dpos ];
+						
+						// diff coding for dc
+						block[ 0 ] -= lastdc[ cmp ];
+						lastdc[ cmp ] = colldata[ cmp ][ 0 ][ dpos ];
+						
+						// encode block
+						eob = jpg_encode_block_seq( huffw,
+							&(hcodes[ 0 ][ cmpnfo[cmp].huffdc ]),
+							&(hcodes[ 1 ][ cmpnfo[cmp].huffac ]),
+							block );
+						
+						// check for errors, proceed if no error encountered
+						if ( eob < 0 ) sta = -1;
+						else sta = jpg_next_mcupos( &mcu, &cmp, &csc, &sub, &dpos, &rstw );
+					}
+				}
+				else if ( cs_sah == 0 ) {
+					// ---> progressive interleaved DC encoding <---
+					// ---> succesive approximation first stage <---
+					while ( sta == 0 ) {
+						// diff coding & bitshifting for dc 
+						tmp = colldata[ cmp ][ 0 ][ dpos ] >> cs_sal;
+						block[ 0 ] = tmp - lastdc[ cmp ];
+						lastdc[ cmp ] = tmp;
+						
+						// encode dc
+						sta = jpg_encode_dc_prg_fs( huffw,
+							&(hcodes[ 0 ][ cmpnfo[cmp].huffdc ]),
+							block );
+						
+						// next mcupos if no error happened
+						if ( sta != -1 )
+							sta = jpg_next_mcupos( &mcu, &cmp, &csc, &sub, &dpos, &rstw );
+					}
+				}
+				else {
+					// ---> progressive interleaved DC encoding <---
+					// ---> succesive approximation later stage <---
+					while ( sta == 0 ) {
+						// fetch bit from current bitplane
+						block[ 0 ] = BITN( colldata[ cmp ][ 0 ][ dpos ], cs_sal );
+						
+						// encode dc correction bit
+						sta = jpg_encode_dc_prg_sa( huffw, block );
+						
+						// next mcupos if no error happened
+						if ( sta != -1 )
+							sta = jpg_next_mcupos( &mcu, &cmp, &csc, &sub, &dpos, &rstw );
+					}
+				}
+			}
+			else // encoding for non interleaved data
+			{
+				if ( jpegtype == 1 ) {
+					// ---> sequential non interleaved encoding <---
+					while ( sta == 0 ) {
+						// copy from colldata
+						for ( bpos = 0; bpos < 64; bpos++ )
+							block[ bpos ] = colldata[ cmp ][ bpos ][ dpos ];
+						
+						// diff coding for dc
+						block[ 0 ] -= lastdc[ cmp ];
+						lastdc[ cmp ] = colldata[ cmp ][ 0 ][ dpos ];
+						
+						// encode block
+						eob = jpg_encode_block_seq( huffw,
+							&(hcodes[ 0 ][ cmpnfo[cmp].huffdc ]),
+							&(hcodes[ 1 ][ cmpnfo[cmp].huffac ]),
+							block );
+						
+						// check for errors, proceed if no error encountered
+						if ( eob < 0 ) sta = -1;
+						else sta = jpg_next_mcuposn( &cmp, &dpos, &rstw );	
+					}
+				}
+				else if ( cs_to == 0 ) {
+					if ( cs_sah == 0 ) {
+						// ---> progressive non interleaved DC encoding <---
+						// ---> succesive approximation first stage <---
+						while ( sta == 0 ) {
+							// diff coding & bitshifting for dc 
+							tmp = colldata[ cmp ][ 0 ][ dpos ] >> cs_sal;
+							block[ 0 ] = tmp - lastdc[ cmp ];
+							lastdc[ cmp ] = tmp;
+							
+							// encode dc
+							sta = jpg_encode_dc_prg_fs( huffw,
+								&(hcodes[ 0 ][ cmpnfo[cmp].huffdc ]),
+								block );							
+							
+							// check for errors, increment dpos otherwise
+							if ( sta != -1 )
+								sta = jpg_next_mcuposn( &cmp, &dpos, &rstw );
+						}
+					}
+					else {
+						// ---> progressive non interleaved DC encoding <---
+						// ---> succesive approximation later stage <---
+						while ( sta == 0 ) {
+							// fetch bit from current bitplane
+							block[ 0 ] = BITN( colldata[ cmp ][ 0 ][ dpos ], cs_sal );
+							
+							// encode dc correction bit
+							sta = jpg_encode_dc_prg_sa( huffw, block );
+							
+							// next mcupos if no error happened
+							if ( sta != -1 )
+								sta = jpg_next_mcuposn( &cmp, &dpos, &rstw );
+						}
+					}
+				}
+				else {
+					if ( cs_sah == 0 ) {
+						// ---> progressive non interleaved AC encoding <---
+						// ---> succesive approximation first stage <---
+						while ( sta == 0 ) {
+							// copy from colldata
+							for ( bpos = cs_from; bpos <= cs_to; bpos++ )
+								block[ bpos ] =
+									FDIV2( colldata[ cmp ][ bpos ][ dpos ], cs_sal );
+							
+							// encode block
+							eob = jpg_encode_ac_prg_fs( huffw,
+								&(hcodes[ 1 ][ cmpnfo[cmp].huffac ]),
+								block, &eobrun, cs_from, cs_to );
+							
+							// check for errors, proceed if no error encountered
+							if ( eob < 0 ) sta = -1;
+							else sta = jpg_next_mcuposn( &cmp, &dpos, &rstw );
+						}						
+						
+						// encode remaining eobrun
+						jpg_encode_eobrun( huffw,
+							&(hcodes[ 1 ][ cmpnfo[cmp].huffac ]),
+							&eobrun );
+					}
+					else {
+						// ---> progressive non interleaved AC encoding <---
+						// ---> succesive approximation later stage <---
+						while ( sta == 0 ) {
+							// copy from colldata
+							for ( bpos = cs_from; bpos <= cs_to; bpos++ )
+								block[ bpos ] =
+									FDIV2( colldata[ cmp ][ bpos ][ dpos ], cs_sal );
+							
+							// encode block
+							eob = jpg_encode_ac_prg_sa( huffw, storw,
+								&(hcodes[ 1 ][ cmpnfo[cmp].huffac ]),
+								block, &eobrun, cs_from, cs_to );
+							
+							// check for errors, proceed if no error encountered
+							if ( eob < 0 ) sta = -1;
+							else sta = jpg_next_mcuposn( &cmp, &dpos, &rstw );
+						}						
+						
+						// encode remaining eobrun
+						jpg_encode_eobrun( huffw,
+							&(hcodes[ 1 ][ cmpnfo[cmp].huffac ]),
+							&eobrun );
+							
+						// encode remaining correction bits
+						jpg_encode_crbits( huffw, storw );
+					}
+				}
+			}
+			
+			// pad huffman writer
+			huffw->pad();
+			
+			// evaluate status
+			if ( sta == -1 ) { // status -1 means error
+				sprintf( errormessage, "encode error in scan%i / mcu%i",
+					scnc, ( cs_cmpc > 1 ) ? mcu : dpos );
+				delete huffw;
+				errorlevel = 2;
+				return false;
+			}
+			else if ( sta == 2 ) { // status 2 means done
+				scnc++; // increment scan counter
+				break; // leave decoding loop, everything is done here
+			}
+			else if ( sta == 1 ) { // status 1 means restart
+				if ( rsti > 0 ) // store rstp & stay in the loop
+					rstp[ rstc++ ] = huffw->num_bytes_written() - 1;
+			}
+		}
+	}
+	
+	// get data into huffdata
+	huffdata = huffw->get_c_bytes();
+	hufs = huffw->num_bytes_written();	
+	delete huffw;
+	
+	// store last scan & restart positions
+	scnp[ scnc ] = hufs;
+	if ( rstp != NULL )
+		rstp[ rstc ] = hufs;
+	
+	
+	return true;
+}
+
+
+/* -----------------------------------------------
+	adapt ICOS tables for quantizer tables
+	----------------------------------------------- */
+	
+INTERN bool adapt_icos( void )
+{
+	unsigned short quant[ 64 ]; // local copy of quantization
+	int ipos;
+	int cmp;
+	
+	
+	for ( cmp = 0; cmp < cmpc; cmp++ ) {
+		// make a local copy of the quantization values, check
+		for ( ipos = 0; ipos < 64; ipos++ ) {
+			quant[ ipos ] = QUANT( cmp, zigzag[ ipos ] );
+			if ( quant[ ipos ] >= 2048 ) // if this is true, it can be safely assumed (for 8 bit JPEG), that all coefficients are zero
+				quant[ ipos ] = 0;
+		}
+		// adapt idct 8x8 table
+		for ( ipos = 0; ipos < 64 * 64; ipos++ )
+			adpt_idct_8x8[ cmp ][ ipos ] = icos_idct_8x8[ ipos ] * quant[ ipos % 64 ];
+		// adapt idct 1x8 table
+		for ( ipos = 0; ipos < 8 * 8; ipos++ )
+			adpt_idct_1x8[ cmp ][ ipos ] = icos_idct_1x8[ ipos ] * quant[ ( ipos % 8 ) * 8 ];
+		// adapt idct 8x1 table
+		for ( ipos = 0; ipos < 8 * 8; ipos++ )
+			adpt_idct_8x1[ cmp ][ ipos ] = icos_idct_1x8[ ipos ] * quant[ ipos % 8 ];
+	}
+	
+	
+	return true;
+}
+
+
+/* -----------------------------------------------
+	filter DC coefficients
+	----------------------------------------------- */
+
+INTERN bool predict_dc( void )
+{
+	signed short* coef;
+	int absmaxp;
+	int absmaxn;
+	int corr_f;
+	int cmp, dpos;	
+	
+	
+	// apply prediction, store prediction error instead of DC
+	for ( cmp = 0; cmp < cmpc; cmp++ ) {
+		absmaxp = MAX_V( cmp, 0 );
+		absmaxn = -absmaxp;
+		corr_f = ( ( 2 * absmaxp ) + 1 );
+		
+		for ( dpos = cmpnfo[cmp].bc - 1; dpos > 0; dpos-- )	{
+			coef = &(colldata[cmp][0][dpos]);
+			#if defined( USE_PLOCOI )
+			(*coef) -= dc_coll_predictor( cmp, dpos ); // loco-i predictor
+			#else
+			(*coef) -= dc_1ddct_predictor( cmp, dpos ); // 1d dct
+			#endif
+			
+			// fix range
+			if ( (*coef) > absmaxp ) (*coef) -= corr_f;
+			else if ( (*coef) < absmaxn ) (*coef) += corr_f;
+		}
+	}
+	
+	return true;
+}
+
+
+/* -----------------------------------------------
+	unpredict DC coefficients
+	----------------------------------------------- */
+
+INTERN bool unpredict_dc( void )
+{	
+	signed short* coef;
+	int absmaxp;
+	int absmaxn;
+	int corr_f;
+	int cmp, dpos;
+	
+	
+	// remove prediction, store DC instead of prediction error
+	for ( cmp = 0; cmp < cmpc; cmp++ ) {
+		absmaxp = MAX_V( cmp, 0 );
+		absmaxn = -absmaxp;
+		corr_f = ( ( 2 * absmaxp ) + 1 );
+		
+		for ( dpos = 1; dpos < cmpnfo[cmp].bc; dpos++ ) {
+			coef = &(colldata[cmp][0][dpos]);
+			#if defined( USE_PLOCOI )
+			(*coef) += dc_coll_predictor( cmp, dpos ); // loco-i predictor
+			#else
+			(*coef) += dc_1ddct_predictor( cmp, dpos ); // 1d dct predictor
+			#endif
+			
+			// fix range
+			if ( (*coef) > absmaxp ) (*coef) -= corr_f;
+			else if ( (*coef) < absmaxn ) (*coef) += corr_f;
+		}
+	}
+	
+	
+	return true;
+}
+
+
+/* -----------------------------------------------
+	checks range of values, error if out of bounds
+	----------------------------------------------- */
+
+INTERN bool check_value_range( void )
+{
+	int absmax;
+	int cmp, bpos, dpos;
+	
+	// out of range should never happen with unmodified JPEGs
+	for ( cmp = 0; cmp < cmpc; cmp++ )
+	for ( bpos = 0; bpos < 64; bpos++ ) {
+		absmax = MAX_V( cmp, bpos );
+		for ( dpos = 0; dpos < cmpnfo[cmp].bc; dpos++ )
+		if ( ( colldata[cmp][bpos][dpos] > absmax ) ||
+			 ( colldata[cmp][bpos][dpos] < -absmax ) ) {
+			sprintf( errormessage, "value out of range error: cmp%i, frq%i, val %i, max %i",
+					cmp, bpos, colldata[cmp][bpos][dpos], absmax );
+			errorlevel = 2;
+			return false;
+		}
+	}
+	
+	
+	return true;
+}
+
+
+/* -----------------------------------------------
+	calculate zero distribution lists
+	----------------------------------------------- */
+	
+INTERN bool calc_zdst_lists( void )
+{
+	int cmp, bpos, dpos;
+	int b_x, b_y;
+	
+	
+	// this functions counts, for each DCT block, the number of non-zero coefficients
+	for ( cmp = 0; cmp < cmpc; cmp++ )
+	{
+		// preset zdstlist
+		memset( zdstdata[cmp], 0, cmpnfo[cmp].bc * sizeof( char ) );
+		
+		// calculate # on non-zeroes per block (separately for lower 7x7 block & first row/collumn)
+		for ( bpos = 1; bpos < 64; bpos++ ) {
+			b_x = unzigzag[ bpos ] % 8;
+			b_y = unzigzag[ bpos ] / 8;
+			if ( b_x == 0 ) {
+				for ( dpos = 0; dpos < cmpnfo[cmp].bc; dpos++ )
+					if ( colldata[cmp][bpos][dpos] != 0 ) zdstylow[cmp][dpos]++;
+			}
+			else if ( b_y == 0 ) {
+				for ( dpos = 0; dpos < cmpnfo[cmp].bc; dpos++ )
+					if ( colldata[cmp][bpos][dpos] != 0 ) zdstxlow[cmp][dpos]++;
+			}
+			else {
+				for ( dpos = 0; dpos < cmpnfo[cmp].bc; dpos++ )
+					if ( colldata[cmp][bpos][dpos] != 0 ) zdstdata[cmp][dpos]++;
+			}
+		}
+	}
+	
+	
+	return true;
+}
+
+
+/* -----------------------------------------------
+	packs all parts to compressed pjg
+	----------------------------------------------- */
+	
+INTERN bool pack_pjg( void )
+{
+	unsigned char hcode;
+	int cmp;
+	#if defined(DEV_INFOS)
+	int dev_size = 0;
+	#endif
+	
+	
+	// PJG-Header
+	str_out->write( reinterpret_cast<const unsigned char*>(pjg_magic), 2 );
+	
+	// store settings if not auto
+	if ( !auto_set ) {
+		hcode = 0x00;
+		str_out->write_byte(hcode);
+		str_out->write( nois_trs, 4 );
+		str_out->write( segm_cnt, 4 );
+	}
+	
+	// store version number
+	hcode = appversion;
+	str_out->write_byte(hcode);
+	
+	
+	// init arithmetic compression
+	auto encoder = new ArithmeticEncoder(*str_out);
+	
+	// discard meta information from header if option set
+	if ( disc_meta )
+		if ( !jpg_rebuild_header() ) return false;	
+	// optimize header for compression
+	if ( !pjg_optimize_header() ) return false;	
+	// set padbit to 1 if previously unset
+	if ( padbit == -1 )	padbit = 1;
+	
+	// encode JPG header
+	#if !defined(DEV_INFOS)	
+	if ( !pjg_encode_generic( encoder, hdrdata, hdrs ) ) return false;
+	#else
+	dev_size = str_out->getpos();
+	if ( !pjg_encode_generic( encoder, hdrdata, hdrs ) ) return false;
+	dev_size_hdr += str_out->getpos() - dev_size;
+	#endif
+	// store padbit (padbit can't be retrieved from the header)
+	if ( !pjg_encode_bit( encoder, padbit ) ) return false;	
+	// also encode one bit to signal false/correct use of RST markers
+	if ( !pjg_encode_bit( encoder, ( rst_err == NULL ) ? 0 : 1 ) ) return false;
+	// encode # of false set RST markers per scan
+	if ( rst_err != NULL )
+		if ( !pjg_encode_generic( encoder, rst_err, scnc ) ) return false;
+	
+	// encode actual components data
+	for ( cmp = 0; cmp < cmpc; cmp++ ) {		
+		#if !defined(DEV_INFOS)
+		// encode frequency scan ('zero-sort-scan')
+		if ( !pjg_encode_zstscan( encoder, cmp ) ) return false;
+		// encode zero-distribution-lists for higher (7x7) ACs
+		if ( !pjg_encode_zdst_high( encoder, cmp ) ) return false;
+		// encode coefficients for higher (7x7) ACs
+		if ( !pjg_encode_ac_high( encoder, cmp ) ) return false;
+		// encode zero-distribution-lists for lower ACs
+		if ( !pjg_encode_zdst_low( encoder, cmp ) ) return false;
+		// encode coefficients for first row / collumn ACs
+		if ( !pjg_encode_ac_low( encoder, cmp ) ) return false;
+		// encode coefficients for DC
+		if ( !pjg_encode_dc( encoder, cmp ) ) return false;		
+		#else
+		dev_size = str_out->getpos();
+		// encode frequency scan ('zero-sort-scan')
+		if ( !pjg_encode_zstscan( encoder, cmp ) ) return false;		
+		dev_size_zsr[ cmp ] += str_out->getpos() - dev_size;
+		dev_size = str_out->getpos();
+		// encode zero-distribution-lists for higher (7x7) ACs
+		if ( !pjg_encode_zdst_high( encoder, cmp ) ) return false;
+		dev_size_zdh[ cmp ] += str_out->getpos() - dev_size;
+		dev_size = str_out->getpos();
+		// encode coefficients for higher (7x7) ACs
+		if ( !pjg_encode_ac_high( encoder, cmp ) ) return false;
+		dev_size_ach[ cmp ] += str_out->getpos() - dev_size;
+		dev_size = str_out->getpos();
+		// encode zero-distribution-lists for lower ACs
+		if ( !pjg_encode_zdst_low( encoder, cmp ) ) return false;
+		dev_size_zdl[ cmp ] += str_out->getpos() - dev_size;
+		dev_size = str_out->getpos();
+		// encode coefficients for first row / collumn ACs
+		if ( !pjg_encode_ac_low( encoder, cmp ) ) return false;
+		dev_size_acl[ cmp ] += str_out->getpos() - dev_size;
+		dev_size = str_out->getpos();
+		// encode coefficients for DC
+		if ( !pjg_encode_dc( encoder, cmp ) ) return false;
+		dev_size_dc[ cmp ] += str_out->getpos() - dev_size;
+		dev_size_cmp[ cmp ] = 
+			dev_size_zsr[ cmp ] + dev_size_zdh[ cmp ] +	dev_size_zdl[ cmp ] +
+			dev_size_ach[ cmp ] + dev_size_acl[ cmp ] +	dev_size_dc[ cmp ];
+		#endif
+	}
+	
+	// encode checkbit for garbage (0 if no garbage, 1 if garbage has to be coded)
+	if ( !pjg_encode_bit( encoder, ( grbs > 0 ) ? 1 : 0 ) ) return false;
+	// encode garbage data only if needed
+	if ( grbs > 0 )
+		if ( !pjg_encode_generic( encoder, grbgdata, grbs ) ) return false;
+	
+	// finalize arithmetic compression
+	delete( encoder );
+	
+	
+	// errormessage if write error
+	if ( str_out->error() ) {
+		sprintf( errormessage, "write error, possibly drive is full" );
+		errorlevel = 2;		
+		return false;
+	}
+	
+	// get filesize
+	pjgfilesize = str_out->num_bytes_written();
+	
+	
+	return true;
+}
+
+
+/* -----------------------------------------------
+	unpacks compressed pjg to colldata
+	----------------------------------------------- */
+	
+INTERN bool unpack_pjg( void )
+{
+	unsigned char hcode;
+	unsigned char cb;
+	int cmp;
+	
+	
+	// check header codes ( maybe position in other function ? )
+	while( true ) {
+		str_in->read_byte(&hcode);
+		if ( hcode == 0x00 ) {
+			// retrieve compression settings from file
+			str_in->read( nois_trs, 4 );
+			str_in->read( segm_cnt, 4 );
+			auto_set = false;
+		}
+		else if ( hcode >= 0x14 ) {
+			// compare version number
+			if ( hcode != appversion ) {
+				sprintf( errormessage, "incompatible file, use %s v%i.%i",
+					appname, hcode / 10, hcode % 10 );
+				errorlevel = 2;
+				return false;
+			}
+			else break;
+		}
+		else {
+			sprintf( errormessage, "unknown header code, use newer version of %s", appname );
+			errorlevel = 2;
+			return false;
+		}
+	}
+	
+	
+	// init arithmetic compression
+	auto decoder = new ArithmeticDecoder(*str_in);
+	
+	// decode JPG header
+	if ( !pjg_decode_generic( decoder, &hdrdata, &hdrs ) ) return false;
+	// retrieve padbit from stream
+	if (!pjg_decode_bit(decoder, &cb)) {
+		return false;
+	}
+	padbit = cb;
+	// decode one bit that signals false /correct use of RST markers
+	if ( !pjg_decode_bit( decoder, &cb ) ) return false;
+	// decode # of false set RST markers per scan only if available
+	if ( cb == 1 )
+		if ( !pjg_decode_generic( decoder, &rst_err, NULL ) ) return false;
+	
+	// undo header optimizations
+	if ( !pjg_unoptimize_header() )	return false;	
+	// discard meta information from header if option set
+	if ( disc_meta )
+		if ( !jpg_rebuild_header() ) return false;
+	// parse header for image-info
+	if ( !jpg_setup_imginfo() ) return false;
+	
+	// decode actual components data
+	for ( cmp = 0; cmp < cmpc; cmp++ ) {		
+		// decode frequency scan ('zero-sort-scan')
+		if ( !pjg_decode_zstscan( decoder, cmp ) ) return false;		
+		// decode zero-distribution-lists for higher (7x7) ACs
+		if ( !pjg_decode_zdst_high( decoder, cmp ) ) return false;
+		// decode coefficients for higher (7x7) ACs
+		if ( !pjg_decode_ac_high( decoder, cmp ) ) return false;
+		// decode zero-distribution-lists for lower ACs
+		if ( !pjg_decode_zdst_low( decoder, cmp ) ) return false;
+		// decode coefficients for first row / collumn ACs
+		if ( !pjg_decode_ac_low( decoder, cmp ) ) return false;	
+		// decode coefficients for DC
+		if ( !pjg_decode_dc( decoder, cmp ) ) return false;	
+	}
+	
+	// retrieve checkbit for garbage (0 if no garbage, 1 if garbage has to be coded)
+	if ( !pjg_decode_bit( decoder, &cb ) ) return false;
+	
+	// decode garbage data only if available
+	if ( cb == 0 ) grbs = 0;
+	else if ( !pjg_decode_generic( decoder, &grbgdata, &grbs ) ) return false;
+	
+	// finalize arithmetic compression
+	delete( decoder );
+	
+	
+	// get filesize
+	pjgfilesize = str_in->get_size();
+	
+	
+	return true;
+}
+
+/* ----------------------- End of main functions -------------------------- */
+
+/* ----------------------- Begin of JPEG specific functions -------------------------- */
+
+
+/* -----------------------------------------------
+	Parses header for imageinfo
+	----------------------------------------------- */
+INTERN bool jpg_setup_imginfo( void )
+{
+	unsigned char  type = 0x00; // type of current marker segment
+	unsigned int   len  = 0; // length of current marker segment
+	unsigned int   hpos = 0; // position in header
+	
+	int cmp, bpos;
+	int i;
+	
+	// header parser loop
+	while ( ( int ) hpos < hdrs ) {
+		type = hdrdata[ hpos + 1 ];
+		len = 2 + B_SHORT( hdrdata[ hpos + 2 ], hdrdata[ hpos + 3 ] );
+		// do not parse DHT & DRI
+		if ( ( type != 0xDA ) && ( type != 0xC4 ) && ( type != 0xDD ) ) {
+			if ( !jpg_parse_jfif( type, len, &( hdrdata[ hpos ] ) ) )
+				return false;
+		}
+		hpos += len;
+	}
+	
+	// check if information is complete
+	if ( cmpc == 0 ) {
+		sprintf( errormessage, "header contains incomplete information" );
+		errorlevel = 2;
+		return false;
+	}
+	for ( cmp = 0; cmp < cmpc; cmp++ ) {
+		if ( ( cmpnfo[cmp].sfv == 0 ) ||
+			 ( cmpnfo[cmp].sfh == 0 ) ||
+			 ( cmpnfo[cmp].qtable == NULL ) ||
+			 ( cmpnfo[cmp].qtable[0] == 0 ) ||
+			 ( jpegtype == 0 ) ) {
+			sprintf( errormessage, "header information is incomplete" );
+			errorlevel = 2;
+			return false;
+		}
+	}
+	
+	// do all remaining component info calculations
+	for ( cmp = 0; cmp < cmpc; cmp++ ) {
+		if ( cmpnfo[ cmp ].sfh > sfhm ) sfhm = cmpnfo[ cmp ].sfh;
+		if ( cmpnfo[ cmp ].sfv > sfvm ) sfvm = cmpnfo[ cmp ].sfv;
+	}
+	mcuv = ( int ) ceil( (float) imgheight / (float) ( 8 * sfhm ) );
+	mcuh = ( int ) ceil( (float) imgwidth  / (float) ( 8 * sfvm ) );
+	mcuc  = mcuv * mcuh;
+	for ( cmp = 0; cmp < cmpc; cmp++ ) {
+		cmpnfo[ cmp ].mbs = cmpnfo[ cmp ].sfv * cmpnfo[ cmp ].sfh;		
+		cmpnfo[ cmp ].bcv = mcuv * cmpnfo[ cmp ].sfh;
+		cmpnfo[ cmp ].bch = mcuh * cmpnfo[ cmp ].sfv;
+		cmpnfo[ cmp ].bc  = cmpnfo[ cmp ].bcv * cmpnfo[ cmp ].bch;
+		cmpnfo[ cmp ].ncv = ( int ) ceil( (float) imgheight * 
+							( (float) cmpnfo[ cmp ].sfh / ( 8.0 * sfhm ) ) );
+		cmpnfo[ cmp ].nch = ( int ) ceil( (float) imgwidth * 
+							( (float) cmpnfo[ cmp ].sfv / ( 8.0 * sfvm ) ) );
+		cmpnfo[ cmp ].nc  = cmpnfo[ cmp ].ncv * cmpnfo[ cmp ].nch;
+	}
+	
+	// decide components' statistical ids
+	if ( cmpc <= 3 ) {
+		for ( cmp = 0; cmp < cmpc; cmp++ ) cmpnfo[ cmp ].sid = cmp;
+	}
+	else {
+		for ( cmp = 0; cmp < cmpc; cmp++ ) cmpnfo[ cmp ].sid = 0;
+	}
+	
+	// alloc memory for further operations
+	for ( cmp = 0; cmp < cmpc; cmp++ )
+	{
+		// alloc memory for colls
+		for ( bpos = 0; bpos < 64; bpos++ ) {
+			colldata[cmp][bpos] = (short int*) calloc ( cmpnfo[cmp].bc, sizeof( short ) );
+			if (colldata[cmp][bpos] == NULL) {
+				sprintf( errormessage, MEM_ERRMSG );
+				errorlevel = 2;
+				return false;
+			}
+		}
+		
+		// alloc memory for zdstlist / eob x / eob y
+		zdstdata[cmp] = (unsigned char*) calloc( cmpnfo[cmp].bc, sizeof( char ) );
+		eobxhigh[cmp] = (unsigned char*) calloc( cmpnfo[cmp].bc, sizeof( char ) );
+		eobyhigh[cmp] = (unsigned char*) calloc( cmpnfo[cmp].bc, sizeof( char ) );
+		zdstxlow[cmp] = (unsigned char*) calloc( cmpnfo[cmp].bc, sizeof( char ) );
+		zdstylow[cmp] = (unsigned char*) calloc( cmpnfo[cmp].bc, sizeof( char ) );
+		if ( ( zdstdata[cmp] == NULL ) ||
+			( eobxhigh[cmp] == NULL ) || ( eobyhigh[cmp] == NULL ) ||
+			( zdstxlow[cmp] == NULL ) || ( zdstylow[cmp] == NULL ) ) {
+			sprintf( errormessage, MEM_ERRMSG );
+			errorlevel = 2;
+			return false;
+		}
+	}
+	
+	// also decide automatic settings here
+	if ( auto_set ) {
+		for ( cmp = 0; cmp < cmpc; cmp++ ) {
+			for ( i = 0;
+				conf_sets[ i ][ cmpnfo[cmp].sid ] > (unsigned int) cmpnfo[ cmp ].bc;
+				i++ );
+			segm_cnt[ cmp ] = conf_segm[ i ][ cmpnfo[cmp].sid ];
+			nois_trs[ cmp ] = conf_ntrs[ i ][ cmpnfo[cmp].sid ];
+		}
+	}
+	
+	
+	return true;
+}
+
+
+/* -----------------------------------------------
+	Parse routines for JFIF segments
+	----------------------------------------------- */
+INTERN bool jpg_parse_jfif( unsigned char type, unsigned int len, unsigned char* segment )
+{
+	unsigned int hpos = 4; // current position in segment, start after segment header
+	int lval, rval; // temporary variables
+	int skip;
+	int cmp;
+	int i;
+	
+	
+	switch ( type )
+	{
+		case 0xC4: // DHT segment
+			// build huffman trees & codes
+			while ( hpos < len ) {
+				lval = LBITS( segment[ hpos ], 4 );
+				rval = RBITS( segment[ hpos ], 4 );
+				if ( ((lval < 0) || (lval >= 2)) || ((rval < 0) || (rval >= 4)) )
+					break;
+					
+				hpos++;
+				// build huffman codes & trees
+				jpg_build_huffcodes( &(segment[ hpos + 0 ]), &(segment[ hpos + 16 ]),
+					&(hcodes[ lval ][ rval ]), &(htrees[ lval ][ rval ]) );
+				htset[ lval ][ rval ] = 1;
+				
+				skip = 16;
+				for ( i = 0; i < 16; i++ )		
+					skip += ( int ) segment[ hpos + i ];				
+				hpos += skip;
+			}
+			
+			if ( hpos != len ) {
+				// if we get here, something went wrong
+				sprintf( errormessage, "size mismatch in dht marker" );
+				errorlevel = 2;
+				return false;
+			}
+			return true;
+		
+		case 0xDB: // DQT segment
+			// copy quantization tables to internal memory
+			while ( hpos < len ) {
+				lval = LBITS( segment[ hpos ], 4 );
+				rval = RBITS( segment[ hpos ], 4 );
+				if ( (lval < 0) || (lval >= 2) ) break;
+				if ( (rval < 0) || (rval >= 4) ) break;
+				hpos++;				
+				if ( lval == 0 ) { // 8 bit precision
+					for ( i = 0; i < 64; i++ ) {
+						qtables[ rval ][ i ] = ( unsigned short ) segment[ hpos + i ];
+						if ( qtables[ rval ][ i ] == 0 ) break;
+					}
+					hpos += 64;
+				}
+				else { // 16 bit precision
+					for ( i = 0; i < 64; i++ ) {
+						qtables[ rval ][ i ] =
+							B_SHORT( segment[ hpos + (2*i) ], segment[ hpos + (2*i) + 1 ] );
+						if ( qtables[ rval ][ i ] == 0 ) break;
+					}
+					hpos += 128;
+				}
+			}
+			
+			if ( hpos != len ) {
+				// if we get here, something went wrong
+				sprintf( errormessage, "size mismatch in dqt marker" );
+				errorlevel = 2;
+				return false;
+			}
+			return true;
+			
+		case 0xDD: // DRI segment
+			// define restart interval
+			rsti = B_SHORT( segment[ hpos ], segment[ hpos + 1 ] );			
+			return true;
+			
+		case 0xDA: // SOS segment
+			// prepare next scan
+			cs_cmpc = segment[ hpos ];
+			if ( cs_cmpc > cmpc ) {
+				sprintf( errormessage, "%i components in scan, only %i are allowed",
+							cs_cmpc, cmpc );
+				errorlevel = 2;
+				return false;
+			}
+			hpos++;
+			for ( i = 0; i < cs_cmpc; i++ ) {
+				for ( cmp = 0; ( segment[ hpos ] != cmpnfo[ cmp ].jid ) && ( cmp < cmpc ); cmp++ );
+				if ( cmp == cmpc ) {
+					sprintf( errormessage, "component id mismatch in start-of-scan" );
+					errorlevel = 2;
+					return false;
+				}
+				cs_cmp[ i ] = cmp;
+				cmpnfo[ cmp ].huffdc = LBITS( segment[ hpos + 1 ], 4 );
+				cmpnfo[ cmp ].huffac = RBITS( segment[ hpos + 1 ], 4 );
+				if ( ( cmpnfo[ cmp ].huffdc < 0 ) || ( cmpnfo[ cmp ].huffdc >= 4 ) ||
+					 ( cmpnfo[ cmp ].huffac < 0 ) || ( cmpnfo[ cmp ].huffac >= 4 ) ) {
+					sprintf( errormessage, "huffman table number mismatch" );
+					errorlevel = 2;
+					return false;
+				}
+				hpos += 2;
+			}
+			cs_from = segment[ hpos + 0 ];
+			cs_to   = segment[ hpos + 1 ];
+			cs_sah  = LBITS( segment[ hpos + 2 ], 4 );
+			cs_sal  = RBITS( segment[ hpos + 2 ], 4 );
+			// check for errors
+			if ( ( cs_from > cs_to ) || ( cs_from > 63 ) || ( cs_to > 63 ) ) {
+				sprintf( errormessage, "spectral selection parameter out of range" );
+				errorlevel = 2;
+				return false;
+			}
+			if ( ( cs_sah >= 12 ) || ( cs_sal >= 12 ) ) {
+				sprintf( errormessage, "successive approximation parameter out of range" );
+				errorlevel = 2;
+				return false;
+			}
+			return true;
+		
+		case 0xC0: // SOF0 segment
+			// coding process: baseline DCT
+			
+		case 0xC1: // SOF1 segment
+			// coding process: extended sequential DCT
+		
+		case 0xC2: // SOF2 segment
+			// coding process: progressive DCT
+			
+			// set JPEG coding type
+			if ( type == 0xC2 )
+				jpegtype = 2;
+			else
+				jpegtype = 1;
+				
+			// check data precision, only 8 bit is allowed
+			lval = segment[ hpos ];
+			if ( lval != 8 ) {
+				sprintf( errormessage, "%i bit data precision is not supported", lval );
+				errorlevel = 2;
+				return false;
+			}
+			
+			// image size, height & component count
+			imgheight = B_SHORT( segment[ hpos + 1 ], segment[ hpos + 2 ] );
+			imgwidth  = B_SHORT( segment[ hpos + 3 ], segment[ hpos + 4 ] );
+			cmpc      = segment[ hpos + 5 ];
+			if ( ( imgwidth == 0 ) || ( imgheight == 0 ) ) {
+				sprintf( errormessage, "resolution is %ix%i, possible malformed JPEG", imgwidth, imgheight );
+				errorlevel = 2;
+				return false;
+			}
+			if ( cmpc > 4 ) {
+				sprintf( errormessage, "image has %i components, max 4 are supported", cmpc );
+				errorlevel = 2;
+				return false;
+			}
+			
+			hpos += 6;
+			// components contained in image
+			for ( cmp = 0; cmp < cmpc; cmp++ ) {
+				cmpnfo[ cmp ].jid = segment[ hpos ];
+				cmpnfo[ cmp ].sfv = LBITS( segment[ hpos + 1 ], 4 );
+				cmpnfo[ cmp ].sfh = RBITS( segment[ hpos + 1 ], 4 );				
+				cmpnfo[ cmp ].qtable = qtables[ segment[ hpos + 2 ] ];
+				hpos += 3;
+			}
+			
+			return true;
+		
+		case 0xC3: // SOF3 segment
+			// coding process: lossless sequential
+			sprintf( errormessage, "sof3 marker found, image is coded lossless" );
+			errorlevel = 2;
+			return false;
+		
+		case 0xC5: // SOF5 segment
+			// coding process: differential sequential DCT
+			sprintf( errormessage, "sof5 marker found, image is coded diff. sequential" );
+			errorlevel = 2;
+			return false;
+		
+		case 0xC6: // SOF6 segment
+			// coding process: differential progressive DCT
+			sprintf( errormessage, "sof6 marker found, image is coded diff. progressive" );
+			errorlevel = 2;
+			return false;
+		
+		case 0xC7: // SOF7 segment
+			// coding process: differential lossless
+			sprintf( errormessage, "sof7 marker found, image is coded diff. lossless" );
+			errorlevel = 2;
+			return false;
+			
+		case 0xC9: // SOF9 segment
+			// coding process: arithmetic extended sequential DCT
+			sprintf( errormessage, "sof9 marker found, image is coded arithm. sequential" );
+			errorlevel = 2;
+			return false;
+			
+		case 0xCA: // SOF10 segment
+			// coding process: arithmetic extended sequential DCT
+			sprintf( errormessage, "sof10 marker found, image is coded arithm. progressive" );
+			errorlevel = 2;
+			return false;
+			
+		case 0xCB: // SOF11 segment
+			// coding process: arithmetic extended sequential DCT
+			sprintf( errormessage, "sof11 marker found, image is coded arithm. lossless" );
+			errorlevel = 2;
+			return false;
+			
+		case 0xCD: // SOF13 segment
+			// coding process: arithmetic differntial sequential DCT
+			sprintf( errormessage, "sof13 marker found, image is coded arithm. diff. sequential" );
+			errorlevel = 2;
+			return false;
+			
+		case 0xCE: // SOF14 segment
+			// coding process: arithmetic differential progressive DCT
+			sprintf( errormessage, "sof14 marker found, image is coded arithm. diff. progressive" );
+			errorlevel = 2;
+			return false;
+		
+		case 0xCF: // SOF15 segment
+			// coding process: arithmetic differntial lossless
+			sprintf( errormessage, "sof15 marker found, image is coded arithm. diff. lossless" );
+			errorlevel = 2;
+			return false;
+			
+		case 0xE0: // APP0 segment	
+		case 0xE1: // APP1 segment
+		case 0xE2: // APP2 segment
+		case 0xE3: // APP3 segment
+		case 0xE4: // APP4 segment
+		case 0xE5: // APP5 segment
+		case 0xE6: // APP6 segment
+		case 0xE7: // APP7 segment
+		case 0xE8: // APP8 segment
+		case 0xE9: // APP9 segment
+		case 0xEA: // APP10 segment
+		case 0xEB: // APP11 segment
+		case 0xEC: // APP12 segment
+		case 0xED: // APP13 segment
+		case 0xEE: // APP14 segment
+		case 0xEF: // APP15 segment
+		case 0xFE: // COM segment
+			// do nothing - return true
+			return true;
+			
+		case 0xD0: // RST0 segment
+		case 0xD1: // RST1 segment
+		case 0xD2: // RST2 segment
+		case 0xD3: // RST3 segment
+		case 0xD4: // RST4 segment
+		case 0xD5: // RST5 segment
+		case 0xD6: // RST6 segment
+		case 0xD7: // RST7 segment
+			// return errormessage - RST is out of place here
+			sprintf( errormessage, "rst marker found out of place" );
+			errorlevel = 2;
+			return false;
+		
+		case 0xD8: // SOI segment
+			// return errormessage - start-of-image is out of place here
+			sprintf( errormessage, "soi marker found out of place" );
+			errorlevel = 2;
+			return false;
+		
+		case 0xD9: // EOI segment
+			// return errormessage - end-of-image is out of place here
+			sprintf( errormessage, "eoi marker found out of place" );
+			errorlevel = 2;
+			return false;
+			
+		default: // unknown marker segment
+			// return warning
+			sprintf( errormessage, "unknown marker found: FF %2X", type );
+			errorlevel = 1;
+			return true;
+	}
+}
+
+
+/* -----------------------------------------------
+	JFIF header rebuilding routine
+	----------------------------------------------- */
+INTERN bool jpg_rebuild_header( void )
+{	
+	MemoryWriter* hdrw; // new header writer
+	
+	unsigned char  type = 0x00; // type of current marker segment
+	unsigned int   len  = 0; // length of current marker segment
+	unsigned int   hpos = 0; // position in header	
+	
+	
+	// start headerwriter
+	hdrw = new MemoryWriter();
+	
+	// header parser loop
+	while ( ( int ) hpos < hdrs ) {
+		type = hdrdata[ hpos + 1 ];
+		len = 2 + B_SHORT( hdrdata[ hpos + 2 ], hdrdata[ hpos + 3 ] );
+		// discard any unneeded meta info
+		if ( ( type == 0xDA ) || ( type == 0xC4 ) || ( type == 0xDB ) ||
+			 ( type == 0xC0 ) || ( type == 0xC1 ) || ( type == 0xC2 ) ||
+			 ( type == 0xDD ) ) {
+			hdrw->write( &(hdrdata[ hpos ]), len );
+		}
+		hpos += len;
+	}
+	
+	// replace current header with the new one
+	free( hdrdata );
+	hdrdata = hdrw->get_c_data();
+	hdrs    = hdrw->num_bytes_written();
+	delete( hdrw );
+	
+	
+	return true;
+}
+
+
+/* -----------------------------------------------
+	sequential block decoding routine
+	----------------------------------------------- */
+INTERN int jpg_decode_block_seq( BitReader* huffr, huffTree* dctree, huffTree* actree, short* block )
+{
+	unsigned short n;
+	unsigned char  s;
+	unsigned char  z;
+	int eob = 64;
+	int bpos;
+	int hc;
+	
+	
+	// decode dc
+	hc = jpg_next_huffcode( huffr, dctree );
+	if ( hc < 0 ) return -1; // return error
+	else s = ( unsigned char ) hc;
+	n = huffr->read( s );	
+	block[ 0 ] = DEVLI( s, n );
+	
+	// decode ac
+	for ( bpos = 1; bpos < 64; )
+	{
+		// decode next
+		hc = jpg_next_huffcode( huffr, actree );
+		// analyse code
+		if ( hc > 0 ) {
+			z = LBITS( hc, 4 );
+			s = RBITS( hc, 4 );
+			n = huffr->read( s );
+			if ( ( z + bpos ) >= 64 )
+				return -1; // run is to long
+			while ( z > 0 ) { // write zeroes
+				block[ bpos++ ] = 0;
+				z--;
+			}
+			block[ bpos++ ] = ( short ) DEVLI( s, n ); // decode cvli
+		}
+		else if ( hc == 0 ) { // EOB
+			eob = bpos;			
+			// while( bpos < 64 ) // fill remaining block with zeroes
+			//	block[ bpos++ ] = 0;
+			break;
+		}
+		else {
+			return -1; // return error
+		}
+	}
+	
+	
+	// return position of eob
+	return eob;
+}
+
+
+/* -----------------------------------------------
+	sequential block encoding routine
+	----------------------------------------------- */
+INTERN int jpg_encode_block_seq( BitWriter* huffw, huffCodes* dctbl, huffCodes* actbl, short* block )
+{
+	unsigned short n;
+	unsigned char  s;
+	unsigned char  z;
+	int bpos;
+	int hc;
+	
+	
+	// encode DC
+	s = BITLEN2048N( block[ 0 ] );
+	n = ENVLI( s, block[ 0 ] );
+	huffw->write_u16( dctbl->cval[ s ], dctbl->clen[ s ] );
+	huffw->write_u16( n, s );
+	
+	// encode AC
+	z = 0;
+	for ( bpos = 1; bpos < 64; bpos++ )
+	{
+		// if nonzero is encountered
+		if ( block[ bpos ] != 0 ) {
+			// write remaining zeroes
+			while ( z >= 16 ) {
+				huffw->write_u16( actbl->cval[ 0xF0 ], actbl->clen[ 0xF0 ] );
+				z -= 16;
+			}			
+			// vli encode
+			s = BITLEN2048N( block[ bpos ] );
+			n = ENVLI( s, block[ bpos ] );
+			hc = ( ( z << 4 ) + s );
+			// write to huffman writer
+			huffw->write_u16( actbl->cval[ hc ], actbl->clen[ hc ] );
+			huffw->write_u16( n, s );
+			// reset zeroes
+			z = 0;
+		}
+		else { // increment zero counter
+			z++;
+		}
+	}
+	// write eob if needed
+	if ( z > 0 )
+		huffw->write_u16( actbl->cval[ 0x00 ], actbl->clen[ 0x00 ] );
+		
+	
+	return 64 - z;
+}
+
+
+/* -----------------------------------------------
+	progressive DC decoding routine
+	----------------------------------------------- */
+INTERN int jpg_decode_dc_prg_fs( BitReader* huffr, huffTree* dctree, short* block )
+{
+	unsigned short n;
+	unsigned char  s;
+	int hc;
+	
+	
+	// decode dc
+	hc = jpg_next_huffcode( huffr, dctree );
+	if ( hc < 0 ) return -1; // return error
+	else s = ( unsigned char ) hc;
+	n = huffr->read( s );	
+	block[ 0 ] = DEVLI( s, n );
+	
+	
+	// return 0 if everything is ok
+	return 0;
+}
+
+
+/* -----------------------------------------------
+	progressive DC encoding routine
+	----------------------------------------------- */
+INTERN int jpg_encode_dc_prg_fs( BitWriter* huffw, huffCodes* dctbl, short* block )
+{
+	unsigned short n;
+	unsigned char  s;
+	
+	
+	// encode DC	
+	s = BITLEN2048N( block[ 0 ] );
+	n = ENVLI( s, block[ 0 ] );
+	huffw->write_u16( dctbl->cval[ s ], dctbl->clen[ s ] );
+	huffw->write_u16( n, s );
+	
+	
+	// return 0 if everything is ok
+	return 0;
+}
+
+
+/* -----------------------------------------------
+	progressive AC decoding routine
+	----------------------------------------------- */
+INTERN int jpg_decode_ac_prg_fs( BitReader* huffr, huffTree* actree, short* block, int* eobrun, int from, int to )
+{
+	unsigned short n;
+	unsigned char  s;
+	unsigned char  z;
+	int eob = to + 1;
+	int bpos;
+	int hc;
+	int l;
+	int r;
+	
+	
+	// decode ac
+	for ( bpos = from; bpos <= to; )
+	{
+		// decode next
+		hc = jpg_next_huffcode( huffr, actree );
+		if ( hc < 0 ) return -1;
+		l = LBITS( hc, 4 );
+		r = RBITS( hc, 4 );
+		// analyse code
+		if ( ( l == 15 ) || ( r > 0 ) ) { // decode run/level combination
+			z = l;
+			s = r;
+			n = huffr->read( s );
+			if ( ( z + bpos ) > to )
+				return -1; // run is to long			
+			while ( z > 0 ) { // write zeroes
+				block[ bpos++ ] = 0;
+				z--;
+			}			
+			block[ bpos++ ] = ( short ) DEVLI( s, n ); // decode cvli
+		}
+		else { // decode eobrun
+			eob = bpos;
+			s = l;
+			n = huffr->read( s );
+			(*eobrun) = E_DEVLI( s, n );			
+			// while( bpos <= to ) // fill remaining block with zeroes
+			//	block[ bpos++ ] = 0;
+			break;
+		}
+	}
+	
+	
+	// return position of eob
+	return eob;
+}
+
+
+/* -----------------------------------------------
+	progressive AC encoding routine
+	----------------------------------------------- */
+INTERN int jpg_encode_ac_prg_fs( BitWriter* huffw, huffCodes* actbl, short* block, int* eobrun, int from, int to )
+{
+	unsigned short n;
+	unsigned char  s;
+	unsigned char  z;
+	int bpos;
+	int hc;
+	
+	// encode AC
+	z = 0;
+	for ( bpos = from; bpos <= to; bpos++ )
+	{
+		// if nonzero is encountered
+		if ( block[ bpos ] != 0 ) {
+			// encode eobrun
+			jpg_encode_eobrun( huffw, actbl, eobrun );
+			// write remaining zeroes
+			while ( z >= 16 ) {
+				huffw->write_u16( actbl->cval[ 0xF0 ], actbl->clen[ 0xF0 ] );
+				z -= 16;
+			}			
+			// vli encode
+			s = BITLEN2048N( block[ bpos ] );
+			n = ENVLI( s, block[ bpos ] );
+			hc = ( ( z << 4 ) + s );
+			// write to huffman writer
+			huffw->write_u16( actbl->cval[ hc ], actbl->clen[ hc ] );
+			huffw->write_u16( n, s );
+			// reset zeroes
+			z = 0;
+		}
+		else { // increment zero counter
+			z++;
+		}
+	}
+	
+	// check eob, increment eobrun if needed
+	if ( z > 0 ) {
+		(*eobrun)++;
+		// check eobrun, encode if needed
+		if ( (*eobrun) == actbl->max_eobrun )
+			jpg_encode_eobrun( huffw, actbl, eobrun );
+		return 1 + to - z;		
+	}
+	else {
+		return 1 + to;
+	}
+}
+
+
+/* -----------------------------------------------
+	progressive DC SA decoding routine
+	----------------------------------------------- */
+INTERN int jpg_decode_dc_prg_sa( BitReader* huffr, short* block )
+{
+	// decode next bit of dc coefficient
+	block[ 0 ] = huffr->read( 1 );
+	
+	// return 0 if everything is ok
+	return 0;
+}
+
+
+/* -----------------------------------------------
+	progressive DC SA encoding routine
+	----------------------------------------------- */
+INTERN int jpg_encode_dc_prg_sa( BitWriter* huffw, short* block )
+{
+	// enocode next bit of dc coefficient
+	huffw->write_u16( block[ 0 ], 1 );
+	
+	// return 0 if everything is ok
+	return 0;
+}
+
+
+/* -----------------------------------------------
+	progressive AC SA decoding routine
+	----------------------------------------------- */
+INTERN int jpg_decode_ac_prg_sa( BitReader* huffr, huffTree* actree, short* block, int* eobrun, int from, int to )
+{
+	unsigned short n;
+	unsigned char  s;
+	signed char    z;
+	signed char    v;
+	int bpos = from;
+	int eob = to;
+	int hc;
+	int l;
+	int r;
+	
+	
+	// decode AC succesive approximation bits
+	if ( (*eobrun) == 0 ) while ( bpos <= to )
+	{
+		// decode next
+		hc = jpg_next_huffcode( huffr, actree );
+		if ( hc < 0 ) return -1;
+		l = LBITS( hc, 4 );
+		r = RBITS( hc, 4 );
+		// analyse code
+		if ( ( l == 15 ) || ( r > 0 ) ) { // decode run/level combination
+			z = l;
+			s = r;
+			if ( s == 0 ) v = 0;
+			else if ( s == 1 ) {
+				n = huffr->read( 1 );
+				v = ( n == 0 ) ? -1 : 1; // fast decode vli
+			}
+			else return -1; // decoding error
+			// write zeroes / write correction bits
+			while ( true ) {
+				if ( block[ bpos ] == 0 ) { // skip zeroes / write value
+					if ( z > 0 ) z--;
+					else {
+						block[ bpos++ ] = v;
+						break;
+					}
+				}
+				else { // read correction bit
+					n = huffr->read( 1 );
+					block[ bpos ] = ( block[ bpos ] > 0 ) ? n : -n;
+				}
+				if ( bpos++ >= to ) return -1; // error check					
+			}
+		}
+		else { // decode eobrun
+			eob = bpos;
+			s = l;
+			n = huffr->read( s );
+			(*eobrun) = E_DEVLI( s, n );
+			break;
+		}
+	}
+	
+	// read after eob correction bits
+	if ( (*eobrun) > 0 ) {
+		for ( ; bpos <= to; bpos++ ) {
+			if ( block[ bpos ] != 0 ) {
+				n = huffr->read( 1 );
+				block[ bpos ] = ( block[ bpos ] > 0 ) ? n : -n;
+			}
+		}
+	}
+	
+	// return eob
+	return eob;
+}
+
+
+/* -----------------------------------------------
+	progressive AC SA encoding routine
+	----------------------------------------------- */
+INTERN int jpg_encode_ac_prg_sa( BitWriter* huffw, std::vector<std::uint8_t>& storw, huffCodes* actbl, short* block, int* eobrun, int from, int to )
+{
+	unsigned short n;
+	unsigned char  s;
+	unsigned char  z;
+	int eob = from;
+	int bpos;
+	int hc;
+	
+	// check if block contains any newly nonzero coefficients and find out position of eob
+	for ( bpos = to; bpos >= from; bpos-- )	{
+		if ( ( block[ bpos ] == 1 ) || ( block[ bpos ] == -1 ) ) {
+			eob = bpos + 1;
+			break;
+		}
+	}
+	
+	// encode eobrun if needed
+	if ( ( eob > from ) && ( (*eobrun) > 0 ) ) {
+		jpg_encode_eobrun( huffw, actbl, eobrun );
+		jpg_encode_crbits( huffw, storw );
+	}
+	
+	// encode AC
+	z = 0;
+	for ( bpos = from; bpos < eob; bpos++ )
+	{
+		// if zero is encountered
+		if ( block[ bpos ] == 0 ) {
+			z++; // increment zero counter
+			if ( z == 16 ) { // write zeroes if needed
+				huffw->write_u16( actbl->cval[ 0xF0 ], actbl->clen[ 0xF0 ] );
+				jpg_encode_crbits( huffw, storw );
+				z = 0;
+			}
+		}
+		// if nonzero is encountered
+		else if ( ( block[ bpos ] == 1 ) || ( block[ bpos ] == -1 ) ) {
+			// vli encode			
+			s = BITLEN2048N( block[ bpos ] );
+			n = ENVLI( s, block[ bpos ] );
+			hc = ( ( z << 4 ) + s );
+			// write to huffman writer
+			huffw->write_u16( actbl->cval[ hc ], actbl->clen[ hc ] );
+			huffw->write_u16( n, s );
+			// write correction bits
+			jpg_encode_crbits( huffw, storw );
+			// reset zeroes
+			z = 0;
+		}
+		else { // store correction bits
+			n = block[ bpos ] & 0x1;
+			storw.emplace_back(n);
+		}
+	}
+	
+	// fast processing after eob
+	for ( ;bpos <= to; bpos++ )
+	{
+		if ( block[ bpos ] != 0 ) { // store correction bits
+			n = block[ bpos ] & 0x1;
+			storw.emplace_back(n);
+		}
+	}
+	
+	// check eob, increment eobrun if needed
+	if ( eob <= to ) {
+		(*eobrun)++;	
+		// check eobrun, encode if needed
+		if ( (*eobrun) == actbl->max_eobrun ) {
+			jpg_encode_eobrun( huffw, actbl, eobrun );
+			jpg_encode_crbits( huffw, storw );		
+		}
+	}	
+	
+	// return eob
+	return eob;
+}
+
+
+/* -----------------------------------------------
+	run of EOB SA decoding routine
+	----------------------------------------------- */
+INTERN int jpg_decode_eobrun_sa( BitReader* huffr, short* block, int* eobrun, int from, int to )
+{
+	unsigned short n;
+	int bpos;
+	
+	
+	// fast eobrun decoding routine for succesive approximation
+	for ( bpos = from; bpos <= to; bpos++ ) {
+		if ( block[ bpos ] != 0 ) {
+			n = huffr->read( 1 );
+			block[ bpos ] = ( block[ bpos ] > 0 ) ? n : -n;
+		}
+	}
+	
+	
+	return 0;
+}
+
+
+/* -----------------------------------------------
+	run of EOB encoding routine
+	----------------------------------------------- */
+INTERN int jpg_encode_eobrun( BitWriter* huffw, huffCodes* actbl, int* eobrun )
+{
+	unsigned short n;
+	unsigned char  s;
+	int hc;
+	
+	
+	if ( (*eobrun) > 0 ) {
+		while ( (*eobrun) > actbl->max_eobrun ) {
+			huffw->write_u16( actbl->cval[ 0xE0 ], actbl->clen[ 0xE0 ] );
+			huffw->write_u16( E_ENVLI( 14, 32767 ), 14 );
+			(*eobrun) -= actbl->max_eobrun;
+		}
+		BITLEN( s, (*eobrun) );
+		s--;
+		n = E_ENVLI( s, (*eobrun) );
+		hc = ( s << 4 );
+		huffw->write_u16( actbl->cval[ hc ], actbl->clen[ hc ] );
+		huffw->write_u16( n, s );
+		(*eobrun) = 0;
+	}
+
+	
+	return 0;
+}
+
+
+/* -----------------------------------------------
+	correction bits encoding routine
+	----------------------------------------------- */
+INTERN int jpg_encode_crbits( BitWriter* huffw, std::vector<std::uint8_t>& storw )
+{	
+	for (const std::uint8_t bit : storw) {
+		huffw->write_bit(bit);
+    }
+	storw.clear();
+	return 0;
+}
+
+
+/* -----------------------------------------------
+	returns next code (from huffman-tree & -data)
+	----------------------------------------------- */
+INTERN int jpg_next_huffcode( BitReader *huffw, huffTree *ctree )
+{	
+	int node = 0;
+	
+	
+	while ( node < 256 ) {
+		node = ( huffw->read( 1 ) == 1 ) ?
+				ctree->r[ node ] : ctree->l[ node ];
+		if ( node == 0 ) break;
+	}
+	
+	return ( node - 256 );
+}
+
+
+/* -----------------------------------------------
+	calculates next position for MCU
+	----------------------------------------------- */
+INTERN int jpg_next_mcupos( int* mcu, int* cmp, int* csc, int* sub, int* dpos, int* rstw )
+{
+	int sta = 0; // status
+	
+	
+	// increment all counts where needed
+	if ( ( ++(*sub) ) >= cmpnfo[(*cmp)].mbs ) {
+		(*sub) = 0;
+		
+		if ( ( ++(*csc) ) >= cs_cmpc ) {
+			(*csc) = 0;
+			(*cmp) = cs_cmp[ 0 ];
+			(*mcu)++;
+			if ( (*mcu) >= mcuc ) sta = 2;
+			else if ( rsti > 0 )
+				if ( --(*rstw) == 0 ) sta = 1;
+		}
+		else {
+			(*cmp) = cs_cmp[(*csc)];
+		}
+	}
+	
+	// get correct position in image ( x & y )
+	if ( cmpnfo[(*cmp)].sfh > 1 ) { // to fix mcu order
+		(*dpos)  = ( (*mcu) / mcuh ) * cmpnfo[(*cmp)].sfh + ( (*sub) / cmpnfo[(*cmp)].sfv );
+		(*dpos) *= cmpnfo[(*cmp)].bch;
+		(*dpos) += ( (*mcu) % mcuh ) * cmpnfo[(*cmp)].sfv + ( (*sub) % cmpnfo[(*cmp)].sfv );
+	}
+	else if ( cmpnfo[(*cmp)].sfv > 1 ) {
+		// simple calculation to speed up things if simple fixing is enough
+		(*dpos) = ( (*mcu) * cmpnfo[(*cmp)].mbs ) + (*sub);
+	}
+	else {
+		// no calculations needed without subsampling
+		(*dpos) = (*mcu);
+	}
+	
+	
+	return sta;
+}
+
+
+/* -----------------------------------------------
+	calculates next position (non interleaved)
+	----------------------------------------------- */
+INTERN int jpg_next_mcuposn( int* cmp, int* dpos, int* rstw )
+{
+	// increment position
+	(*dpos)++;
+	
+	// fix for non interleaved mcu - horizontal
+	if ( cmpnfo[(*cmp)].bch != cmpnfo[(*cmp)].nch ) {
+		if ( (*dpos) % cmpnfo[(*cmp)].bch == cmpnfo[(*cmp)].nch )
+			(*dpos) += ( cmpnfo[(*cmp)].bch - cmpnfo[(*cmp)].nch );
+	}
+	
+	// fix for non interleaved mcu - vertical
+	if ( cmpnfo[(*cmp)].bcv != cmpnfo[(*cmp)].ncv ) {
+		if ( (*dpos) / cmpnfo[(*cmp)].bch == cmpnfo[(*cmp)].ncv )
+			(*dpos) = cmpnfo[(*cmp)].bc;
+	}
+	
+	// check position
+	if ( (*dpos) >= cmpnfo[(*cmp)].bc ) return 2;
+	else if ( rsti > 0 )
+		if ( --(*rstw) == 0 ) return 1;
+	
+
+	return 0;
+}
+
+
+/* -----------------------------------------------
+	skips the eobrun, calculates next position
+	----------------------------------------------- */
+INTERN int jpg_skip_eobrun( int* cmp, int* dpos, int* rstw, int* eobrun )
+{
+	if ( (*eobrun) > 0 ) // error check for eobrun
+	{		
+		// compare rst wait counter if needed
+		if ( rsti > 0 ) {
+			if ( (*eobrun) > (*rstw) )
+				return -1;
+			else
+				(*rstw) -= (*eobrun);
+		}
+		
+		// fix for non interleaved mcu - horizontal
+		if ( cmpnfo[(*cmp)].bch != cmpnfo[(*cmp)].nch ) {
+			(*dpos) += ( ( ( (*dpos) % cmpnfo[(*cmp)].bch ) + (*eobrun) ) /
+						cmpnfo[(*cmp)].nch ) * ( cmpnfo[(*cmp)].bch - cmpnfo[(*cmp)].nch );
+		}
+		
+		// fix for non interleaved mcu - vertical
+		if ( cmpnfo[(*cmp)].bcv != cmpnfo[(*cmp)].ncv ) {
+			if ( (*dpos) / cmpnfo[(*cmp)].bch >= cmpnfo[(*cmp)].ncv )
+				(*dpos) += ( cmpnfo[(*cmp)].bcv - cmpnfo[(*cmp)].ncv ) *
+						cmpnfo[(*cmp)].bch;
+		}		
+		
+		// skip blocks 
+		(*dpos) += (*eobrun);
+		
+		// reset eobrun
+		(*eobrun) = 0;
+		
+		// check position
+		if ( (*dpos) == cmpnfo[(*cmp)].bc ) return 2;
+		else if ( (*dpos) > cmpnfo[(*cmp)].bc ) return -1;
+		else if ( rsti > 0 ) 
+			if ( (*rstw) == 0 ) return 1;
+	}
+	
+	return 0;
+}
+
+
+/* -----------------------------------------------
+	creates huffman-codes & -trees from dht-data
+	----------------------------------------------- */
+INTERN void jpg_build_huffcodes( unsigned char *clen, unsigned char *cval,	huffCodes *hc, huffTree *ht )
+{
+	int nextfree;	
+	int code;
+	int node;
+	int i, j, k;
+	
+	
+	// fill with zeroes
+	memset( hc->clen, 0, 256 * sizeof( short ) );
+	memset( hc->cval, 0, 256 * sizeof( short ) );
+	memset( ht->l, 0, 256 * sizeof( short ) );
+	memset( ht->r, 0, 256 * sizeof( short ) );
+	
+	// 1st part -> build huffman codes
+	
+	// creating huffman-codes	
+	k = 0;
+	code = 0;	
+	
+	// symbol-value of code is its position in the table
+	for( i = 0; i < 16; i++ ) {
+		for( j = 0; j < (int) clen[ i ]; j++ ) {
+			hc->clen[ (int) cval[k] ] = 1 + i;
+			hc->cval[ (int) cval[k] ] = code;
+			
+			k++;			
+			code++;
+		}		
+		code = code << 1;
+	}
+	
+	// find out eobrun max value
+	hc->max_eobrun = 0;
+	for ( i = 14; i >= 0; i-- ) {
+		if ( hc->clen[ i << 4 ] > 0 ) {
+			hc->max_eobrun = ( 2 << i ) - 1;
+			break;
+		}
+	}
+	
+	// 2nd -> part use codes to build the coding tree
+	
+	// initial value for next free place
+	nextfree = 1;
+
+	// work through every code creating links between the nodes (represented through ints)
+	for ( i = 0; i < 256; i++ )	{
+		// (re)set current node
+		node = 0;   		   		
+		// go through each code & store path
+		for ( j = hc->clen[ i ] - 1; j > 0; j-- ) {
+			if ( BITN( hc->cval[ i ], j ) == 1 ) {
+				if ( ht->r[ node ] == 0 )
+					 ht->r[ node ] = nextfree++;
+				node = ht->r[ node ];
+			}
+			else{
+				if ( ht->l[ node ] == 0 )
+					ht->l[ node ] = nextfree++;
+				node = ht->l[ node ];
+			}   					
+		}
+		// last link is number of targetvalue + 256
+		if ( hc->clen[ i ] > 0 ) {
+			if ( BITN( hc->cval[ i ], 0 ) == 1 )
+				ht->r[ node ] = i + 256;
+			else
+				ht->l[ node ] = i + 256;
+		}	   	
+	}
+}
+
+/* ----------------------- End of JPEG specific functions -------------------------- */
+
+/* ----------------------- End of PJG specific functions -------------------------- */
+
+
+/* -----------------------------------------------
+	encodes frequency scanorder to pjg
+	----------------------------------------------- */
+INTERN bool pjg_encode_zstscan( ArithmeticEncoder* enc, int cmp )
+{
+	model_s* model;
+	
+	unsigned char freqlist[ 64 ];
+	int tpos; // true position
+	int cpos; // coded position
+	int c, i;
+	
+	
+	// calculate zero sort scan
+	pjg_get_zerosort_scan( zsrtscan[cmp], cmp );
+	
+	// preset freqlist
+	for ( i = 0; i < 64; i++ )
+		freqlist[ i ] = stdscan[ i ];
+		
+	// init model
+	model = INIT_MODEL_S( 64, 64, 1 );
+	
+	// encode scanorder
+	for ( i = 1; i < 64; i++ )
+	{			
+		// reduce range of model
+		model->exclude_symbols(64 - i);
+		
+		// compare remaining list to remainnig scan
+		tpos = 0;
+		for ( c = i; c < 64; c++ ) {
+			// search next val != 0 in list
+			for ( tpos++; freqlist[ tpos ] == 0; tpos++ );
+			// get out if not a match
+			if ( freqlist[ tpos ] != zsrtscan[ cmp ][ c ] ) break;				
+		}
+		if ( c == 64 ) {
+			// remaining list is in sorted scanorder
+			// encode zero and make a quick exit
+			encode_ari( enc, model, 0 );
+			break;
+		}
+		
+		// list is not in sorted order -> next pos hat to be encoded
+		cpos = 1;
+		// encode position
+		for ( tpos = 0; freqlist[ tpos ] != zsrtscan[ cmp ][ i ]; tpos++ )
+			if ( freqlist[ tpos ] != 0 ) cpos++;
+		// remove from list
+		freqlist[ tpos ] = 0;
+		
+		// encode coded position in list
+		encode_ari( enc, model, cpos );
+		model->shift_context( cpos );		
+	}
+	
+	// delete model
+	delete( model );
+	
+	// set zero sort scan as freqscan
+	freqscan[ cmp ] = zsrtscan[ cmp ];
+	
+	
+	return true;
+}
+
+
+/* -----------------------------------------------
+	encodes # of non zeroes to pjg (high)
+	----------------------------------------------- */	
+INTERN bool pjg_encode_zdst_high( ArithmeticEncoder* enc, int cmp )
+{
+	model_s* model;
+	
+	unsigned char* zdstls;
+	int dpos;
+	int a, b;
+	int bc;
+	int w;
+	
+	
+	// init model, constants
+	model = INIT_MODEL_S( 49 + 1, 25 + 1, 1 );
+	zdstls = zdstdata[ cmp ];
+	w = cmpnfo[cmp].bch;
+	bc = cmpnfo[cmp].bc;
+	
+	// arithmetic encode zero-distribution-list
+	for ( dpos = 0; dpos < bc; dpos++ ) {
+		// context modelling - use average of above and left as context
+		get_context_nnb( dpos, w, &a, &b );
+		a = ( a >= 0 ) ? zdstls[ a ] : 0;
+		b = ( b >= 0 ) ? zdstls[ b ] : 0;
+		// shift context
+		model->shift_context( ( a + b + 2 ) / 4 );
+		// encode symbol
+		encode_ari( enc, model, zdstls[ dpos ] );
+	}
+	
+	// clean up
+	delete( model );
+	
+	
+	return true;
+}
+
+
+/* -----------------------------------------------
+	encodes # of non zeroes to pjg (low)
+	----------------------------------------------- */	
+INTERN bool pjg_encode_zdst_low( ArithmeticEncoder* enc, int cmp )
+{
+	model_s* model;
+	
+	unsigned char* zdstls_x;
+	unsigned char* zdstls_y;
+	unsigned char* ctx_zdst;
+	unsigned char* ctx_eobx;
+	unsigned char* ctx_eoby;
+	
+	int dpos;
+	int bc;
+	
+	
+	// init model, constants
+	model = INIT_MODEL_S( 8, 8, 2 );
+	zdstls_x = zdstxlow[ cmp ];
+	zdstls_y = zdstylow[ cmp ];
+	ctx_eobx = eobxhigh[ cmp ];
+	ctx_eoby = eobyhigh[ cmp ];
+	ctx_zdst = zdstdata[ cmp ];
+	bc = cmpnfo[cmp].bc;
+	
+	// arithmetic encode zero-distribution-list (first row)
+	for ( dpos = 0; dpos < bc; dpos++ ) {
+		model->shift_context( ( ctx_zdst[dpos] + 3 ) / 7 ); // shift context
+		model->shift_context( ctx_eobx[dpos] ); // shift context
+		encode_ari( enc, model, zdstls_x[ dpos ] ); // encode symbol
+	}
+	// arithmetic encode zero-distribution-list (first collumn)
+	for ( dpos = 0; dpos < bc; dpos++ ) {
+		model->shift_context( ( ctx_zdst[dpos] + 3 ) / 7 ); // shift context
+		model->shift_context( ctx_eoby[dpos] ); // shift context
+		encode_ari( enc, model, zdstls_y[ dpos ] ); // encode symbol
+	}
+	
+	// clean up
+	delete( model );
+	
+	
+	return true;
+}
+
+
+/* -----------------------------------------------
+	encodes DC coefficients to pjg
+	----------------------------------------------- */
+INTERN bool pjg_encode_dc( ArithmeticEncoder* enc, int cmp )
+{
+	unsigned char* segm_tab;
+	
+	model_s* mod_len;
+	model_b* mod_sgn;
+	model_b* mod_res;
+	
+	unsigned char* zdstls; // pointer to zero distribution list
+	signed short* coeffs; // pointer to current coefficent data
+	
+	unsigned short* absv_store; // absolute coefficients values storage
+	unsigned short* c_absc[ 6 ]; // quick access array for contexts
+	int c_weight[ 6 ]; // weighting for contexts
+
+	int ctx_avr; // 'average' context
+	int ctx_len; // context for bit length
+	
+	int max_val; // max value
+	int max_len; // max bitlength
+	
+	int dpos;
+	int clen, absv, sgn;
+	int snum;
+	int bt, bp;
+	
+	int p_x, p_y;
+	int r_x; //, r_y;
+	int w, bc;
+	
+	
+	// decide segmentation setting
+	segm_tab = segm_tables[ segm_cnt[ cmp ] - 1 ];
+	
+	// get max absolute value/bit length
+	max_val = MAX_V( cmp, 0 );
+	max_len = BITLEN1024P( max_val );
+	
+	// init models for bitlenghts and -patterns	
+	mod_len = INIT_MODEL_S( max_len + 1, ( segm_cnt[cmp] > max_len ) ? segm_cnt[cmp] : max_len + 1, 2 );
+	mod_res = INIT_MODEL_B( ( segm_cnt[cmp] < 16 ) ? 1 << 4 : segm_cnt[cmp], 2 );
+	mod_sgn = INIT_MODEL_B( 1, 0 );
+	
+	// set width/height of each band
+	bc = cmpnfo[cmp].bc;
+	w = cmpnfo[cmp].bch;
+	
+	// allocate memory for absolute values storage
+	absv_store = (unsigned short*) calloc ( bc, sizeof( short ) );
+	if ( absv_store == NULL ) {
+		sprintf( errormessage, MEM_ERRMSG );
+		errorlevel = 2;
+		return false;
+	}
+	
+	// set up context quick access array
+	pjg_aavrg_prepare( c_absc, c_weight, absv_store, cmp );
+	
+	// locally store pointer to coefficients and zero distribution list
+	coeffs = colldata[ cmp ][ 0 ];
+	zdstls = zdstdata[ cmp ];	
+	
+	// arithmetic compression loop
+	for ( dpos = 0; dpos < bc; dpos++ )
+	{		
+		//calculate x/y positions in band
+		p_y = dpos / w;
+		// r_y = h - ( p_y + 1 );
+		p_x = dpos % w;
+		r_x = w - ( p_x + 1 );
+		
+		// get segment-number from zero distribution list and segmentation set
+		snum = segm_tab[ zdstls[dpos] ];
+		// calculate contexts (for bit length)
+		ctx_avr = pjg_aavrg_context( c_absc, c_weight, dpos, p_y, p_x, r_x ); // AVERAGE context
+		ctx_len = BITLEN1024P( ctx_avr ); // BITLENGTH context
+		// shift context / do context modelling (segmentation is done per context)
+		shift_model( mod_len, ctx_len, snum );
+		
+		// simple treatment if coefficient is zero
+		if ( coeffs[ dpos ] == 0 ) {
+			// encode bit length (0) of current coefficient			
+			encode_ari( enc, mod_len, 0 );
+		}
+		else {
+			// get absolute val, sign & bit length for current coefficient
+			absv = ABS( coeffs[dpos] );
+			clen = BITLEN1024P( absv );
+			sgn = ( coeffs[dpos] > 0 ) ? 0 : 1;
+			// encode bit length of current coefficient
+			encode_ari( enc, mod_len, clen );
+			// encoding of residual
+			// first set bit must be 1, so we start at clen - 2
+			for ( bp = clen - 2; bp >= 0; bp-- ) {
+				shift_model( mod_res, snum, bp ); // shift in 2 contexts
+				// encode/get bit
+				bt = BITN( absv, bp );
+				encode_ari( enc, mod_res, bt );
+			}
+			// encode sign
+			encode_ari( enc, mod_sgn, sgn );
+			// store absolute value
+			absv_store[ dpos ] = absv;
+		}
+	}
+	
+	// free memory / clear models
+	free( absv_store );
+	delete ( mod_len );
+	delete ( mod_res );
+	delete ( mod_sgn );
+	
+	
+	return true;
+}
+
+
+/* -----------------------------------------------
+	encodes high (7x7) AC coefficients to pjg
+	----------------------------------------------- */
+INTERN bool pjg_encode_ac_high( ArithmeticEncoder* enc, int cmp )
+{
+	unsigned char* segm_tab;
+	
+	model_s* mod_len;
+	model_b* mod_sgn;
+	model_b* mod_res;
+	
+	unsigned char* zdstls; // pointer to zero distribution list
+	unsigned char* eob_x; // pointer to x eobs
+	unsigned char* eob_y; // pointer to y eobs
+	signed short* coeffs; // pointer to current coefficent data
+	
+	unsigned short* absv_store; // absolute coefficients values storage
+	unsigned short* c_absc[ 6 ]; // quick access array for contexts
+	int c_weight[ 6 ]; // weighting for contexts
+	
+	unsigned char* sgn_store; // sign storage for context	
+	unsigned char* sgn_nbh; // left signs neighbor
+	unsigned char* sgn_nbv; // upper signs neighbor
+
+	int ctx_avr; // 'average' context
+	int ctx_len; // context for bit length
+	int ctx_sgn; // context for sign
+	
+	int max_val; // max value
+	int max_len; // max bitlength
+	
+	int bpos, dpos;
+	int clen, absv, sgn;
+	int snum;
+	int bt, bp;
+	int i;
+	
+	int b_x, b_y;
+	int p_x, p_y;
+	int r_x; //, r_y;
+	int w, bc;
+	
+	
+	// decide segmentation setting
+	segm_tab = segm_tables[ segm_cnt[ cmp ] - 1 ];
+	
+	// init models for bitlenghts and -patterns
+	mod_len = INIT_MODEL_S( 11, ( segm_cnt[cmp] > 11 ) ? segm_cnt[cmp] : 11, 2 );
+	mod_res = INIT_MODEL_B( ( segm_cnt[cmp] < 16 ) ? 1 << 4 : segm_cnt[cmp], 2 );
+	mod_sgn = INIT_MODEL_B( 9, 1 );
+	
+	// set width/height of each band
+	bc = cmpnfo[cmp].bc;
+	w = cmpnfo[cmp].bch;
+	
+	// allocate memory for absolute values & signs storage
+	absv_store = (unsigned short*) calloc ( bc, sizeof( short ) );	
+	sgn_store = (unsigned char*) calloc ( bc, sizeof( char ) );
+	zdstls = (unsigned char*) calloc ( bc, sizeof( char ) );
+	if ( ( absv_store == NULL ) || ( sgn_store == NULL ) || ( zdstls == NULL ) ) {
+		if ( absv_store != NULL ) free( absv_store );
+		if ( sgn_store != NULL ) free( sgn_store );
+		if ( zdstls != NULL ) free( zdstls );
+		sprintf( errormessage, MEM_ERRMSG );
+		errorlevel = 2;
+		return false;
+	}
+	
+	// set up quick access arrays for signs context
+	sgn_nbh = sgn_store - 1;
+	sgn_nbv = sgn_store - w;	
+	
+	// locally store pointer to eob x / eob y
+	eob_x = eobxhigh[ cmp ];
+	eob_y = eobyhigh[ cmp ];
+	
+	// preset x/y eobs
+	memset( eob_x, 0x00, bc * sizeof( char ) );
+	memset( eob_y, 0x00, bc * sizeof( char ) );
+	
+	// make a local copy of the zero distribution list
+	for ( dpos = 0; dpos < bc; dpos++ )
+		zdstls[ dpos ] = zdstdata[ cmp ][ dpos ];
+	
+	// work through lower 7x7 bands in order of freqscan
+	for ( i = 1; i < 64; i++ )
+	{		
+		// work through blocks in order of frequency scan
+		bpos = (int) freqscan[cmp][i];
+		b_x = unzigzag[ bpos ] % 8;
+		b_y = unzigzag[ bpos ] / 8;
+	
+		if ( ( b_x == 0 ) || ( b_y == 0 ) )
+			continue; // process remaining coefficients elsewhere
+	
+		// preset absolute values/sign storage
+		memset( absv_store, 0x00, bc * sizeof( short ) );
+		memset( sgn_store, 0x00, bc * sizeof( char ) );
+		
+		// set up average context quick access arrays
+		pjg_aavrg_prepare( c_absc, c_weight, absv_store, cmp );
+		
+		// locally store pointer to coefficients
+		coeffs = colldata[ cmp ][ bpos ];
+		
+		// get max bit length
+		max_val = MAX_V( cmp, bpos );
+		max_len = BITLEN1024P( max_val );
+		
+		// arithmetic compression loo
+		for ( dpos = 0; dpos < bc; dpos++ )
+		{		
+			// skip if beyound eob
+			if ( zdstls[dpos] == 0 )
+				continue;
+		
+			//calculate x/y positions in band
+			p_y = dpos / w;
+			// r_y = h - ( p_y + 1 );
+			p_x = dpos % w;
+			r_x = w - ( p_x + 1 );
+		
+			// get segment-number from zero distribution list and segmentation set
+			snum = segm_tab[ zdstls[dpos] ];
+			// calculate contexts (for bit length)
+			ctx_avr = pjg_aavrg_context( c_absc, c_weight, dpos, p_y, p_x, r_x ); // AVERAGE context
+			ctx_len = BITLEN1024P( ctx_avr ); // BITLENGTH context				
+			// shift context / do context modelling (segmentation is done per context)
+			shift_model( mod_len, ctx_len, snum );
+			mod_len->exclude_symbols(max_len);		
+		
+			// simple treatment if coefficient is zero
+			if ( coeffs[ dpos ] == 0 ) {
+				// encode bit length (0) of current coefficien
+				encode_ari( enc, mod_len, 0 );
+			}
+			else {
+				// get absolute val, sign & bit length for current coefficient
+				absv = ABS( coeffs[dpos] );
+				clen = BITLEN1024P( absv );
+				sgn = ( coeffs[dpos] > 0 ) ? 0 : 1;
+				// encode bit length of current coefficient				
+				encode_ari( enc, mod_len, clen );
+				// encoding of residual
+				// first set bit must be 1, so we start at clen - 2
+				for ( bp = clen - 2; bp >= 0; bp-- ) { 
+					shift_model( mod_res, snum, bp ); // shift in 2 contexts
+					// encode/get bit
+					bt = BITN( absv, bp );
+					encode_ari( enc, mod_res, bt );
+				}
+				// encode sign				
+				ctx_sgn = ( p_x > 0 ) ? sgn_nbh[ dpos ] : 0; // sign context
+				if ( p_y > 0 ) ctx_sgn += 3 * sgn_nbv[ dpos ]; // IMPROVE !!!!!!!!!!!
+				mod_sgn->shift_context( ctx_sgn );
+				encode_ari( enc, mod_sgn, sgn );
+				// store absolute value/sign, decrement zdst
+				absv_store[ dpos ] = absv;
+				sgn_store[ dpos ] = sgn + 1;
+				zdstls[dpos]--;
+				// recalculate x/y eob				
+				if ( b_x > eob_x[dpos] ) eob_x[dpos] = b_x;
+				if ( b_y > eob_y[dpos] ) eob_y[dpos] = b_y;
+			}
+		}
+		// flush models
+		mod_len->flush_model();
+		mod_res->flush_model();
+		mod_sgn->flush_model();
+	}
+	
+	// free memory / clear models
+	free( absv_store );
+	free( sgn_store );
+	free( zdstls );
+	delete ( mod_len );
+	delete ( mod_res );
+	delete ( mod_sgn );
+	
+	
+	return true;
+}
+
+
+/* -----------------------------------------------
+	encodes first row/col AC coefficients to pjg
+	----------------------------------------------- */
+INTERN bool pjg_encode_ac_low( ArithmeticEncoder* enc, int cmp )
+{
+	model_s* mod_len;
+	model_b* mod_sgn;
+	model_b* mod_res;
+	model_b* mod_top;
+	
+	unsigned char* zdstls; // pointer to row/col # of non-zeroes
+	signed short* coeffs; // pointer to current coefficent data
+	
+	signed short* coeffs_x[ 8 ]; // prediction coeffs - current block
+	signed short* coeffs_a[ 8 ]; // prediction coeffs - neighboring block
+	int pred_cf[ 8 ]; // prediction multipliers
+	
+	int ctx_lak; // lakhani context
+	int ctx_abs; // absolute context
+	int ctx_len; // context for bit length
+	int ctx_res; // bit plane context for residual
+	int ctx_sgn; // context for sign
+	
+	int max_valp; // max value (+)
+	int max_valn; // max value (-)
+	int max_len; // max bitlength
+	int thrs_bp; // residual threshold bitplane
+	int* edge_c; // edge criteria
+	
+	int bpos, dpos;
+	int clen, absv, sgn;
+	int bt, bp;
+	int i;
+	
+	int b_x, b_y;
+	int p_x, p_y;
+	int w, bc;
+	
+	
+	// init models for bitlenghts and -patterns
+	mod_len = INIT_MODEL_S( 11, ( segm_cnt[cmp] > 11 ) ? segm_cnt[cmp] : 11, 2 );
+	mod_res = INIT_MODEL_B( 1 << 4, 2 );
+	mod_top = INIT_MODEL_B( ( nois_trs[cmp] > 4 ) ? 1 << nois_trs[cmp] : 1 << 4, 3 );
+	mod_sgn = INIT_MODEL_B( 11, 1 );
+	
+	// set width/height of each band
+	bc = cmpnfo[cmp].bc;
+	w = cmpnfo[cmp].bch;
+	
+	// work through each first row / first collumn band
+	for ( i = 2; i < 16; i++ )
+	{		
+		// alternate between first row and first collumn
+		b_x = ( i % 2 == 0 ) ? i / 2 : 0;
+		b_y = ( i % 2 == 1 ) ? i / 2 : 0;
+		bpos = (int) zigzag[ b_x + (8*b_y) ];
+		
+		// locally store pointer to band coefficients
+		coeffs = colldata[ cmp ][ bpos ];
+		// store pointers to prediction coefficients
+		if ( b_x == 0 ) {
+			for ( ; b_x < 8; b_x++ ) {
+				coeffs_x[ b_x ] = colldata[ cmp ][ zigzag[b_x+(8*b_y)] ];
+				coeffs_a[ b_x ] = colldata[ cmp ][ zigzag[b_x+(8*b_y)] ] - 1;
+				pred_cf[ b_x ] = icos_base_8x8[ b_x * 8 ] * QUANT ( cmp, zigzag[b_x+(8*b_y)] );
+			} b_x = 0;
+			zdstls = zdstylow[ cmp ];
+			edge_c = &p_x;
+		}
+		else { // if ( b_y == 0 )
+			for ( ; b_y < 8; b_y++ ) {
+				coeffs_x[ b_y ] = colldata[ cmp ][ zigzag[b_x+(8*b_y)] ];
+				coeffs_a[ b_y ] = colldata[ cmp ][ zigzag[b_x+(8*b_y)] ] - w;
+				pred_cf[ b_y ] = icos_base_8x8[ b_y * 8 ] * QUANT ( cmp, zigzag[b_x+(8*b_y)] );
+			} b_y = 0;
+			zdstls = zdstxlow[ cmp ];
+			edge_c = &p_y;
+		}
+		
+		// get max bit length / other info
+		max_valp = MAX_V( cmp, bpos );
+		max_valn = -max_valp;
+		max_len = BITLEN1024P( max_valp );
+		thrs_bp = ( max_len > nois_trs[cmp] ) ? max_len - nois_trs[cmp] : 0;
+		
+		// arithmetic compression loop
+		for ( dpos = 0; dpos < bc; dpos++ )
+		{
+			// skip if beyound eob
+			if ( zdstls[ dpos ] == 0 )
+				continue;
+			
+			// calculate x/y positions in band
+			p_y = dpos / w;
+			p_x = dpos % w;
+			
+			// edge treatment / calculate LAKHANI context
+			if ( (*edge_c) > 0 )
+				ctx_lak = pjg_lakh_context( coeffs_x, coeffs_a, pred_cf, dpos );
+			else ctx_lak = 0;
+			ctx_lak = CLAMPED( max_valn, max_valp, ctx_lak );
+			ctx_len = BITLEN2048N( ctx_lak ); // BITLENGTH context
+			
+			// shift context / do context modelling (segmentation is done per context)
+			shift_model( mod_len, ctx_len, zdstls[ dpos ] );
+			mod_len->exclude_symbols(max_len);			
+			
+			// simple treatment if coefficient is zero
+			if ( coeffs[ dpos ] == 0 ) {
+				// encode bit length (0) of current coefficient
+				encode_ari( enc, mod_len, 0 );
+			}
+			else {
+				// get absolute val, sign & bit length for current coefficient
+				absv = ABS( coeffs[dpos] );
+				clen = BITLEN2048N( absv );
+				sgn = ( coeffs[dpos] > 0 ) ? 0 : 1;
+				// encode bit length of current coefficient
+				encode_ari( enc, mod_len, clen );
+				// encoding of residual
+				bp = clen - 2; // first set bit must be 1, so we start at clen - 2
+				ctx_res = ( bp >= thrs_bp ) ? 1 : 0;
+				ctx_abs = ABS( ctx_lak );
+				ctx_sgn = ( ctx_lak == 0 ) ? 0 : ( ctx_lak > 0 ) ? 1 : 2;
+				for ( ; bp >= thrs_bp; bp-- ) {						
+					shift_model( mod_top, ctx_abs >> thrs_bp, ctx_res, clen - thrs_bp ); // shift in 3 contexts
+					// encode/get bit
+					bt = BITN( absv, bp );
+					encode_ari( enc, mod_top, bt );
+					// update context
+					ctx_res = ctx_res << 1;
+					if ( bt ) ctx_res |= 1; 
+				}
+				for ( ; bp >= 0; bp-- ) {
+					shift_model( mod_res, zdstls[ dpos ], bp ); // shift in 2 contexts
+					// encode/get bit
+					bt = BITN( absv, bp );
+					encode_ari( enc, mod_res, bt );
+				}
+				// encode sign
+				shift_model( mod_sgn, ctx_len, ctx_sgn );
+				encode_ari( enc, mod_sgn, sgn );
+				// decrement # of non zeroes
+				zdstls[ dpos ]--;
+			}
+		}
+		// flush models
+		mod_len->flush_model();
+		mod_res->flush_model();
+		mod_top->flush_model();
+		mod_sgn->flush_model();
+	}
+	
+	// free memory / clear models
+	delete ( mod_len );
+	delete ( mod_res );
+	delete ( mod_top );
+	delete ( mod_sgn );
+	
+	
+	return true;
+}
+
+
+/* -----------------------------------------------
+	encodes a stream of generic (8bit) data to pjg
+	----------------------------------------------- */
+INTERN bool pjg_encode_generic( ArithmeticEncoder* enc, unsigned char* data, int len )
+{
+	model_s* model;
+	int i;
+	
+	
+	// arithmetic encode data
+	model = INIT_MODEL_S( 256 + 1, 256, 1 );
+	for ( i = 0; i < len; i++ )
+	{
+		encode_ari( enc, model, data[ i ] );
+		model->shift_context( data[ i ] );
+	}
+	// encode end-of-data symbol (256)
+	encode_ari( enc, model, 256 );
+	delete( model );
+	
+	
+	return true;
+}
+
+
+/* -----------------------------------------------
+	encodes one bit to pjg
+	----------------------------------------------- */
+INTERN bool pjg_encode_bit( ArithmeticEncoder* enc, unsigned char bit )
+{
+	model_b* model;
+	
+	
+	// encode one bit
+	model = INIT_MODEL_B( 1, -1 );
+	encode_ari( enc, model, bit );
+	delete( model );
+	
+	
+	return true;
+}
+
+
+/* -----------------------------------------------
+	encodes frequency scanorder to pjg
+	----------------------------------------------- */
+INTERN bool pjg_decode_zstscan( ArithmeticDecoder* dec, int cmp )
+{	
+	model_s* model;;
+	
+	unsigned char freqlist[ 64 ];
+	int tpos; // true position
+	int cpos; // coded position
+	int i;
+	
+	
+	// set first position in zero sort scan
+	zsrtscan[ cmp ][ 0 ] = 0;
+	
+	// preset freqlist
+	for ( i = 0; i < 64; i++ )
+		freqlist[ i ] = stdscan[ i ];
+		
+	// init model
+	model = INIT_MODEL_S( 64, 64, 1 );
+	
+	// encode scanorder
+	for ( i = 1; i < 64; i++ )
+	{			
+		// reduce range of model
+		model->exclude_symbols(64 - i);
+		
+		// decode symbol
+		cpos = decode_ari( dec, model );
+		model->shift_context( cpos );
+		
+		if ( cpos == 0 ) {
+			// remaining list is identical to scan
+			// fill the scan & make a quick exit				
+			for ( tpos = 0; i < 64; i++ ) {
+				while ( freqlist[ ++tpos ] == 0 );
+				zsrtscan[ cmp ][ i ] = freqlist[ tpos ];
+			}
+			break;
+		}
+		
+		// decode position from list
+		for ( tpos = 0; tpos < 64; tpos++ ) {
+			if ( freqlist[ tpos ] != 0 ) cpos--;
+			if ( cpos == 0 ) break;
+		}
+			
+		// write decoded position to zero sort scan
+		zsrtscan[ cmp ][ i ] = freqlist[ tpos ];
+		// remove from list
+		freqlist[ tpos ] = 0;
+	}
+	
+	// delete model
+	delete( model  );		
+	
+	// set zero sort scan as freqscan
+	freqscan[ cmp ] = zsrtscan[ cmp ];
+	
+	
+	return true;
+}
+
+
+/* -----------------------------------------------
+	decodes # of non zeroes from pjg (high)
+	----------------------------------------------- */
+INTERN bool pjg_decode_zdst_high( ArithmeticDecoder* dec, int cmp )
+{
+	model_s* model;
+	
+	unsigned char* zdstls;
+	int dpos;
+	int a, b;
+	int bc;
+	int w;
+	
+	
+	// init model, constants
+	model = INIT_MODEL_S( 49 + 1, 25 + 1, 1 );
+	zdstls = zdstdata[ cmp ];
+	w = cmpnfo[cmp].bch;
+	bc = cmpnfo[cmp].bc;
+	
+	// arithmetic decode zero-distribution-list
+	for ( dpos = 0; dpos < bc; dpos++ )	{			
+		// context modelling - use average of above and left as context		
+		get_context_nnb( dpos, w, &a, &b );
+		a = ( a >= 0 ) ? zdstls[ a ] : 0;
+		b = ( b >= 0 ) ? zdstls[ b ] : 0;
+		// shift context
+		model->shift_context( ( a + b + 2 ) / 4 );
+		// decode symbol
+		zdstls[ dpos ] = decode_ari( dec, model );
+	}
+	
+	// clean up
+	delete( model );
+	
+	
+	return true;
+}
+
+
+/* -----------------------------------------------
+	decodes # of non zeroes from pjg (low)
+	----------------------------------------------- */	
+INTERN bool pjg_decode_zdst_low( ArithmeticDecoder* dec, int cmp )
+{
+	model_s* model;
+	
+	unsigned char* zdstls_x;
+	unsigned char* zdstls_y;
+	unsigned char* ctx_zdst;
+	unsigned char* ctx_eobx;
+	unsigned char* ctx_eoby;
+	
+	int dpos;
+	int bc;
+	
+	
+	// init model, constants
+	model = INIT_MODEL_S( 8, 8, 2 );
+	zdstls_x = zdstxlow[ cmp ];
+	zdstls_y = zdstylow[ cmp ];
+	ctx_eobx = eobxhigh[ cmp ];
+	ctx_eoby = eobyhigh[ cmp ];
+	ctx_zdst = zdstdata[ cmp ];
+	bc = cmpnfo[cmp].bc;
+	
+	// arithmetic encode zero-distribution-list (first row)
+	for ( dpos = 0; dpos < bc; dpos++ ) {
+		model->shift_context( ( ctx_zdst[dpos] + 3 ) / 7 ); // shift context
+		model->shift_context( ctx_eobx[dpos] ); // shift context
+		zdstls_x[ dpos ] = decode_ari( dec, model ); // decode symbol
+	}
+	// arithmetic encode zero-distribution-list (first collumn)
+	for ( dpos = 0; dpos < bc; dpos++ ) {
+		model->shift_context( ( ctx_zdst[dpos] + 3 ) / 7 ); // shift context
+		model->shift_context( ctx_eoby[dpos] ); // shift context
+		zdstls_y[ dpos ] = decode_ari( dec, model ); // decode symbol
+	}
+	
+	// clean up
+	delete( model );
+	
+	
+	return true;
+}
+
+
+/* -----------------------------------------------
+	decodes DC coefficients from pjg
+	----------------------------------------------- */
+INTERN bool pjg_decode_dc( ArithmeticDecoder* dec, int cmp )
+{
+	unsigned char* segm_tab;
+	
+	model_s* mod_len;
+	model_b* mod_sgn;
+	model_b* mod_res;
+	
+	unsigned char* zdstls; // pointer to zero distribution list
+	signed short* coeffs; // pointer to current coefficent data
+	
+	unsigned short* absv_store; // absolute coefficients values storage
+	unsigned short* c_absc[ 6 ]; // quick access array for contexts
+	int c_weight[ 6 ]; // weighting for contexts
+
+	int ctx_avr; // 'average' context
+	int ctx_len; // context for bit length
+	
+	int max_val; // max value
+	int max_len; // max bitlength
+	
+	int dpos;
+	int clen, absv, sgn;
+	int snum;
+	int bt, bp;
+	
+	int p_x, p_y;
+	int r_x; //, r_y;
+	int w, bc;
+	
+	
+	// decide segmentation setting
+	segm_tab = segm_tables[ segm_cnt[ cmp ] - 1 ];
+	
+	// get max absolute value/bit length
+	max_val = MAX_V( cmp, 0 );
+	max_len = BITLEN1024P( max_val );
+	
+	// init models for bitlenghts and -patterns
+	mod_len = INIT_MODEL_S( max_len + 1, ( segm_cnt[cmp] > max_len ) ? segm_cnt[cmp] : max_len + 1, 2 );
+	mod_res = INIT_MODEL_B( ( segm_cnt[cmp] < 16 ) ? 1 << 4 : segm_cnt[cmp], 2 );
+	mod_sgn = INIT_MODEL_B( 1, 0 );
+	
+	// set width/height of each band
+	bc = cmpnfo[cmp].bc;
+	w = cmpnfo[cmp].bch;
+	
+	// allocate memory for absolute values storage
+	absv_store = (unsigned short*) calloc ( bc, sizeof( short ) );
+	if ( absv_store == NULL ) {
+		sprintf( errormessage, MEM_ERRMSG );
+		errorlevel = 2;
+		return false;
+	}
+	
+	// set up context quick access array
+	pjg_aavrg_prepare( c_absc, c_weight, absv_store, cmp );
+	
+	// locally store pointer to coefficients and zero distribution list
+	coeffs = colldata[ cmp ][ 0 ];
+	zdstls = zdstdata[ cmp ];	
+	
+	// arithmetic compression loop
+	for ( dpos = 0; dpos < bc; dpos++ )
+	{		
+		//calculate x/y positions in band
+		p_y = dpos / w;
+		// r_y = h - ( p_y + 1 );
+		p_x = dpos % w;
+		r_x = w - ( p_x + 1 );
+		
+		// get segment-number from zero distribution list and segmentation set
+		snum = segm_tab[ zdstls[dpos] ];
+		// calculate contexts (for bit length)
+		ctx_avr = pjg_aavrg_context( c_absc, c_weight, dpos, p_y, p_x, r_x ); // AVERAGE context
+		ctx_len = BITLEN1024P( ctx_avr ); // BITLENGTH context				
+		// shift context / do context modelling (segmentation is done per context)
+		shift_model( mod_len, ctx_len, snum );
+		// decode bit length of current coefficient
+		clen = decode_ari( dec, mod_len );
+		
+		// simple treatment if coefficient is zero
+		if ( clen == 0 ) {
+			// coeffs[ dpos ] = 0;
+		}
+		else {
+			// decoding of residual
+			absv = 1;
+			// first set bit must be 1, so we start at clen - 2
+			for ( bp = clen - 2; bp >= 0; bp-- ) {
+				shift_model( mod_res, snum, bp ); // shift in 2 contexts
+				// decode bit
+				bt = decode_ari( dec, mod_res );
+				// update absv
+				absv = absv << 1;
+				if ( bt ) absv |= 1; 
+			}
+			// decode sign
+			sgn = decode_ari( dec, mod_sgn );
+			// copy to colldata
+			coeffs[ dpos ] = ( sgn == 0 ) ? absv : -absv;
+			// store absolute value/sign
+			absv_store[ dpos ] = absv;
+		}
+	}
+	
+	// free memory / clear models
+	free( absv_store );
+	delete ( mod_len );
+	delete ( mod_res );
+	delete ( mod_sgn );
+	
+	
+	return true;
+}
+
+
+/* -----------------------------------------------
+	decodes high (7x7) AC coefficients to pjg
+	----------------------------------------------- */
+INTERN bool pjg_decode_ac_high( ArithmeticDecoder* dec, int cmp )
+{
+	unsigned char* segm_tab;
+	
+	model_s* mod_len;
+	model_b* mod_sgn;
+	model_b* mod_res;
+	
+	unsigned char* zdstls; // pointer to zero distribution list
+	unsigned char* eob_x; // pointer to x eobs
+	unsigned char* eob_y; // pointer to y eobs
+	signed short* coeffs; // pointer to current coefficent data
+	
+	unsigned short* absv_store; // absolute coefficients values storage
+	unsigned short* c_absc[ 6 ]; // quick access array for contexts
+	int c_weight[ 6 ]; // weighting for contexts
+	
+	unsigned char* sgn_store; // sign storage for context	
+	unsigned char* sgn_nbh; // left signs neighbor
+	unsigned char* sgn_nbv; // upper signs neighbor
+
+	int ctx_avr; // 'average' context
+	int ctx_len; // context for bit length
+	int ctx_sgn; // context for sign
+	
+	int max_val; // max value
+	int max_len; // max bitlength
+	
+	int bpos, dpos;
+	int clen, absv, sgn;
+	int snum;
+	int bt, bp;
+	int i;
+	
+	int b_x, b_y;
+	int p_x, p_y;
+	int r_x;
+	int w, bc;
+	
+	
+	// decide segmentation setting
+	segm_tab = segm_tables[ segm_cnt[ cmp ] - 1 ];
+	
+	// init models for bitlenghts and -patterns
+	mod_len = INIT_MODEL_S( 11, ( segm_cnt[cmp] > 11 ) ? segm_cnt[cmp] : 11, 2 );
+	mod_res = INIT_MODEL_B( ( segm_cnt[cmp] < 16 ) ? 1 << 4 : segm_cnt[cmp], 2 );
+	mod_sgn = INIT_MODEL_B( 9, 1 );
+	
+	// set width/height of each band
+	bc = cmpnfo[cmp].bc;
+	w = cmpnfo[cmp].bch;
+	
+	// allocate memory for absolute values & signs storage
+	absv_store = (unsigned short*) calloc ( bc, sizeof( short ) );	
+	sgn_store = (unsigned char*) calloc ( bc, sizeof( char ) );
+	zdstls = (unsigned char*) calloc ( bc, sizeof( char ) );
+	if ( ( absv_store == NULL ) || ( sgn_store == NULL ) || ( zdstls == NULL ) ) {
+		if ( absv_store != NULL ) free( absv_store );
+		if ( sgn_store != NULL ) free( sgn_store );
+		if ( zdstls != NULL ) free( zdstls );
+		sprintf( errormessage, MEM_ERRMSG );
+		errorlevel = 2;
+		return false;
+	}
+	
+	// set up quick access arrays for signs context
+	sgn_nbh = sgn_store - 1;
+	sgn_nbv = sgn_store - w;	
+	
+	// locally store pointer to eob x / eob y
+	eob_x = eobxhigh[ cmp ];
+	eob_y = eobyhigh[ cmp ];
+	
+	// preset x/y eobs
+	memset( eob_x, 0x00, bc * sizeof( char ) );
+	memset( eob_y, 0x00, bc * sizeof( char ) );
+	
+	// make a local copy of the zero distribution list
+	for ( dpos = 0; dpos < bc; dpos++ )
+		zdstls[ dpos ] = zdstdata[ cmp ][ dpos ];
+	
+	// work through lower 7x7 bands in order of freqscan
+	for ( i = 1; i < 64; i++ )
+	{		
+		// work through blocks in order of frequency scan
+		bpos = (int) freqscan[cmp][i];
+		b_x = unzigzag[ bpos ] % 8;
+		b_y = unzigzag[ bpos ] / 8;		
+		
+		if ( ( b_x == 0 ) || ( b_y == 0 ) )
+				continue; // process remaining coefficients elsewhere
+		
+		// preset absolute values/sign storage
+		memset( absv_store, 0x00, bc * sizeof( short ) );
+		memset( sgn_store, 0x00, bc * sizeof( char ) );
+		
+		// set up average context quick access arrays
+		pjg_aavrg_prepare( c_absc, c_weight, absv_store, cmp );
+		
+		// locally store pointer to coefficients
+		coeffs = colldata[ cmp ][ bpos ];
+		
+		// get max bit length
+		max_val = MAX_V( cmp, bpos );
+		max_len = BITLEN1024P( max_val );
+		
+		// arithmetic compression loop
+		for ( dpos = 0; dpos < bc; dpos++ )
+		{
+			// skip if beyound eob
+			if ( zdstls[dpos] == 0 )
+				continue;
+			
+			//calculate x/y positions in band
+			p_y = dpos / w;
+			// r_y = h - ( p_y + 1 );
+			p_x = dpos % w;
+			r_x = w - ( p_x + 1 );					
+			
+			// get segment-number from zero distribution list and segmentation set
+			snum = segm_tab[ zdstls[dpos] ];
+			// calculate contexts (for bit length)
+			ctx_avr = pjg_aavrg_context( c_absc, c_weight, dpos, p_y, p_x, r_x ); // AVERAGE context
+			ctx_len = BITLEN1024P( ctx_avr ); // BITLENGTH context				
+			// shift context / do context modelling (segmentation is done per context)
+			shift_model( mod_len, ctx_len, snum );
+			mod_len->exclude_symbols(max_len);
+			
+			// decode bit length of current coefficient
+			clen = decode_ari( dec, mod_len );			
+			// simple treatment if coefficient is zero
+			if ( clen == 0 ) {
+				// coeffs[ dpos ] = 0;
+			}
+			else {
+				// decoding of residual
+				absv = 1;
+				// first set bit must be 1, so we start at clen - 2
+				for ( bp = clen - 2; bp >= 0; bp-- ) {
+					shift_model( mod_res, snum, bp ); // shift in 2 contexts
+					// decode bit
+					bt = decode_ari( dec, mod_res );
+					// update absv
+					absv = absv << 1;
+					if ( bt ) absv |= 1; 
+				}
+				// decode sign
+				ctx_sgn = ( p_x > 0 ) ? sgn_nbh[ dpos ] : 0; // sign context
+				if ( p_y > 0 ) ctx_sgn += 3 * sgn_nbv[ dpos ]; // IMPROVE! !!!!!!!!!!!
+				mod_sgn->shift_context( ctx_sgn );
+				sgn = decode_ari( dec, mod_sgn );
+				// copy to colldata
+				coeffs[ dpos ] = ( sgn == 0 ) ? absv : -absv;
+				// store absolute value/sign, decrement zdst
+				absv_store[ dpos ] = absv;
+				sgn_store[ dpos ] = sgn + 1;
+				zdstls[dpos]--;
+				// recalculate x/y eob
+				if ( b_x > eob_x[dpos] ) eob_x[dpos] = b_x;
+				if ( b_y > eob_y[dpos] ) eob_y[dpos] = b_y;	
+			}
+		}
+		// flush models
+		mod_len->flush_model();
+		mod_res->flush_model();
+		mod_sgn->flush_model();
+	}
+	
+	// free memory / clear models
+	free( absv_store );
+	free( sgn_store );
+	free( zdstls );
+	delete ( mod_len );
+	delete ( mod_res );
+	delete ( mod_sgn );
+	
+	
+	return true;
+}
+
+
+/* -----------------------------------------------
+	decodes high (7x7) AC coefficients to pjg
+	----------------------------------------------- */
+INTERN bool pjg_decode_ac_low( ArithmeticDecoder* dec, int cmp )
+{
+	model_s* mod_len;
+	model_b* mod_sgn;
+	model_b* mod_res;
+	model_b* mod_top;
+	
+	unsigned char* zdstls; // pointer to row/col # of non-zeroes
+	signed short* coeffs; // pointer to current coefficent data
+	
+	signed short* coeffs_x[ 8 ]; // prediction coeffs - current block
+	signed short* coeffs_a[ 8 ]; // prediction coeffs - neighboring block
+	int pred_cf[ 8 ]; // prediction multipliers
+
+	int ctx_lak; // lakhani context
+	int ctx_abs; // absolute context
+	int ctx_len; // context for bit length
+	int ctx_res; // bit plane context for residual
+	int ctx_sgn; // context for sign
+	
+	int max_valp; // max value (+)
+	int max_valn; // max value (-)
+	int max_len; // max bitlength
+	int thrs_bp; // residual threshold bitplane
+	int* edge_c; // edge criteria
+	
+	int bpos, dpos;
+	int clen, absv, sgn;
+	int bt, bp;
+	int i;
+	
+	int b_x, b_y;
+	int p_x, p_y;
+	int w, bc;
+	
+	
+	// init models for bitlenghts and -patterns
+	mod_len = INIT_MODEL_S( 11, ( segm_cnt[cmp] > 11 ) ? segm_cnt[cmp] : 11, 2 );
+	mod_res = INIT_MODEL_B( 1 << 4, 2 );
+	mod_top = INIT_MODEL_B( ( nois_trs[cmp] > 4 ) ? 1 << nois_trs[cmp] : 1 << 4, 3 );
+	mod_sgn = INIT_MODEL_B( 11, 1 );
+	
+	// set width/height of each band
+	bc = cmpnfo[cmp].bc;
+	w = cmpnfo[cmp].bch;
+	
+	// work through each first row / first collumn band
+	for ( i = 2; i < 16; i++ )
+	{		
+		// alternate between first row and first collumn
+		b_x = ( i % 2 == 0 ) ? i / 2 : 0;
+		b_y = ( i % 2 == 1 ) ? i / 2 : 0;
+		bpos = (int) zigzag[ b_x + (8*b_y) ];
+		
+		// locally store pointer to band coefficients
+		coeffs = colldata[ cmp ][ bpos ];
+		// store pointers to prediction coefficients
+		if ( b_x == 0 ) {
+			for ( ; b_x < 8; b_x++ ) {
+				coeffs_x[ b_x ] = colldata[ cmp ][ zigzag[b_x+(8*b_y)] ];
+				coeffs_a[ b_x ] = colldata[ cmp ][ zigzag[b_x+(8*b_y)] ] - 1;
+				pred_cf[ b_x ] = icos_base_8x8[ b_x * 8 ] * QUANT ( cmp, zigzag[b_x+(8*b_y)] );
+			} b_x = 0;
+			zdstls = zdstylow[ cmp ];
+			edge_c = &p_x;
+		}
+		else { // if ( b_y == 0 )
+			for ( ; b_y < 8; b_y++ ) {
+				coeffs_x[ b_y ] = colldata[ cmp ][ zigzag[b_x+(8*b_y)] ];
+				coeffs_a[ b_y ] = colldata[ cmp ][ zigzag[b_x+(8*b_y)] ] - w;
+				pred_cf[ b_y ] = icos_base_8x8[ b_y * 8 ] * QUANT ( cmp, zigzag[b_x+(8*b_y)] );
+			} b_y = 0;
+			zdstls = zdstxlow[ cmp ];
+			edge_c = &p_y;
+		}
+		
+		// get max bit length / other info
+		max_valp = MAX_V( cmp, bpos );
+		max_valn = -max_valp;
+		max_len = BITLEN1024P( max_valp );
+		thrs_bp = ( max_len > nois_trs[cmp] ) ? max_len - nois_trs[cmp] : 0;
+		
+		// arithmetic compression loop
+		for ( dpos = 0; dpos < bc; dpos++ )
+		{
+			// skip if beyound eob
+			if ( zdstls[ dpos ] == 0 )
+				continue;
+			
+			//calculate x/y positions in band
+			p_y = dpos / w;
+			p_x = dpos % w;
+			
+			// edge treatment / calculate LAKHANI context
+			if ( (*edge_c) > 0 )
+				ctx_lak = pjg_lakh_context( coeffs_x, coeffs_a, pred_cf, dpos );
+			else ctx_lak = 0;
+			ctx_lak = CLAMPED( max_valn, max_valp, ctx_lak );
+			ctx_len = BITLEN2048N( ctx_lak ); // BITLENGTH context				
+			// shift context / do context modelling (segmentation is done per context)
+			shift_model( mod_len, ctx_len, zdstls[ dpos ] );
+			mod_len->exclude_symbols(max_len);
+			
+			// decode bit length of current coefficient
+			clen = decode_ari( dec, mod_len );
+			// simple treatment if coefficients == 0
+			if ( clen == 0 ) {
+				// coeffs[ dpos ] = 0;
+			}
+			else {
+				// decoding of residual
+				bp = clen - 2; // first set bit must be 1, so we start at clen - 2
+				ctx_res = ( bp >= thrs_bp ) ? 1 : 0;
+				ctx_abs = ABS( ctx_lak );
+				ctx_sgn = ( ctx_lak == 0 ) ? 0 : ( ctx_lak > 0 ) ? 1 : 2;
+				for ( ; bp >= thrs_bp; bp-- ) {						
+					shift_model( mod_top, ctx_abs >> thrs_bp, ctx_res, clen - thrs_bp ); // shift in 3 contexts
+					// decode bit
+					bt = decode_ari( dec, mod_top );
+					// update context
+					ctx_res = ctx_res << 1;
+					if ( bt ) ctx_res |= 1; 
+				}
+				absv = ( ctx_res == 0 ) ? 1 : ctx_res; // !!!!
+				for ( ; bp >= 0; bp-- ) {
+					shift_model( mod_res, zdstls[ dpos ], bp ); // shift in 2 contexts
+					// decode bit
+					bt = decode_ari( dec, mod_res );
+					// update absv
+					absv = absv << 1;
+					if ( bt ) absv |= 1; 
+				}
+				// decode sign
+				shift_model( mod_sgn, zdstls[ dpos ], ctx_sgn );
+				sgn = decode_ari( dec, mod_sgn );
+				// copy to colldata
+				coeffs[ dpos ] = ( sgn == 0 ) ? absv : -absv;
+				// decrement # of non zeroes
+				zdstls[ dpos ]--;
+			}
+		}
+		// flush models
+		mod_len->flush_model();
+		mod_res->flush_model();
+		mod_top->flush_model();
+		mod_sgn->flush_model();
+	}
+	
+	// free memory / clear models
+	delete ( mod_len );
+	delete ( mod_res );
+	delete ( mod_top );
+	delete ( mod_sgn );
+	
+	
+	return true;
+}
+
+
+/* -----------------------------------------------
+	deodes a stream of generic (8bit) data from pjg
+	----------------------------------------------- */
+INTERN bool pjg_decode_generic( ArithmeticDecoder* dec, unsigned char** data, int* len )
+{
+	MemoryWriter* bwrt;
+	model_s* model;
+	int c;
+	
+	
+	// start byte writer
+	bwrt = new MemoryWriter();
+	
+	// decode header, ending with 256 symbol
+	model = INIT_MODEL_S( 256 + 1, 256, 1 );
+	while ( true ) {
+		c = decode_ari( dec, model );
+		if ( c == 256 ) break;
+		bwrt->write_byte( (unsigned char) c );
+		model->shift_context( c );
+	}
+	delete( model );
+	
+	// check for out of memory
+	if ( bwrt->error() ) {
+		delete bwrt;
+		sprintf( errormessage, MEM_ERRMSG );
+		errorlevel = 2;
+		return false;
+	}
+	
+	// get data/length and close byte writer
+	(*data) = bwrt->get_c_data();
+	if ( len != NULL ) (*len) = bwrt->num_bytes_written();
+	delete bwrt;
+	
+	
+	return true;
+}
+
+
+/* -----------------------------------------------
+	decodes one bit from pjg
+	----------------------------------------------- */
+INTERN bool pjg_decode_bit( ArithmeticDecoder* dec, unsigned char* bit )
+{
+	model_b* model;
+	
+	
+	model = INIT_MODEL_B( 1, -1 );
+	(*bit) = decode_ari( dec, model );
+	delete( model );
+	
+	
+	return true;
+}
+
+
+/* -----------------------------------------------
+	get zero sort frequency scan vector
+	----------------------------------------------- */
+INTERN void pjg_get_zerosort_scan( unsigned char* sv, int cmp )
+{
+	unsigned int zdist[ 64 ]; // distributions of zeroes per band
+	int bc = cmpnfo[cmp].bc;
+	int bpos, dpos;	
+	bool done = false;
+	int swap;
+	int i;
+	
+		
+	// preset sv & zdist
+	for ( i = 0; i < 64; i++ ) {
+		sv[ i ] = i;
+		zdist[ i ] = 0;
+	}	
+	
+	// count zeroes for each frequency
+	for ( bpos = 0; bpos < 64; bpos++ )
+	for ( dpos = 0; dpos < bc; dpos++ )			
+		if ( colldata[cmp][bpos][dpos] == 0 ) zdist[ bpos ]++;
+	
+	// bubble sort according to count of zeroes (descending order)
+	while ( !done ) {
+		done = true;
+		for ( i = 2; i < 64; i++ )
+		if ( zdist[ i ] < zdist[ i - 1 ] ) {
+		
+			swap = zdist[ i ];
+			zdist[ i ] = zdist[ i - 1 ];
+			zdist[ i - 1 ] = swap;
+			
+			swap = sv[ i ];
+			sv[ i ] = sv[ i - 1 ];
+			sv[ i - 1 ] = swap;
+			
+			done = false;			
+		}
+	}
+}
+
+
+/* -----------------------------------------------
+	optimizes JFIF header for compression
+	----------------------------------------------- */
+INTERN bool pjg_optimize_header( void )
+{
+	unsigned char  type = 0x00; // type of current marker segment
+	unsigned int   len  = 0; // length of current marker segment
+	unsigned int   hpos = 0; // position in header
+	
+	unsigned int fpos; // end of marker position
+	unsigned int skip; // bytes to skip
+	unsigned int spos; // sub position
+	int i;
+	
+	
+	// search for DHT (0xFFC4) & DQT (0xFFDB) marker segments	
+	// header parser loop
+	while ( ( int ) hpos < hdrs ) {
+		type = hdrdata[ hpos + 1 ];
+		len = 2 + B_SHORT( hdrdata[ hpos + 2 ], hdrdata[ hpos + 3 ] );
+		if ( type == 0xC4 ) { // for DHT
+			fpos = hpos + len; // reassign length to end position
+			hpos += 4; // skip marker & length
+			while ( hpos < fpos ) {			
+				hpos++;				
+				// table found - compare with each of the four standard tables		
+				for ( i = 0; i < 4; i++ ) {
+					for ( spos = 0; spos < std_huff_lengths[ i ]; spos++ ) {
+						if ( hdrdata[ hpos + spos ] != std_huff_tables[ i ][ spos ] )
+							break;
+					}
+					// check if comparison ok
+					if ( spos != std_huff_lengths[ i ] )
+						continue;
+					
+					// if we get here, the table matches the standard table
+					// number 'i', so it can be replaced
+					hdrdata[ hpos + 0 ] = std_huff_lengths[ i ] - 16 - i;
+					hdrdata[ hpos + 1 ] = i;
+					for ( spos = 2; spos < std_huff_lengths[ i ]; spos++ )
+						hdrdata[ hpos + spos ] = 0x00;
+					// everything done here, so leave
+					break;
+				}
+								
+				skip = 16;
+				for ( i = 0; i < 16; i++ )		
+					skip += ( int ) hdrdata[ hpos + i ];				
+				hpos += skip;
+			}
+		}
+		else if ( type == 0xDB ) { // for DQT
+			fpos = hpos + len; // reassign length to end position
+			hpos += 4; // skip marker & length
+			while ( hpos < fpos ) {
+				i = LBITS( hdrdata[ hpos ], 4 );				
+				hpos++;
+				// table found
+				if ( i == 1 ) { // get out for 16 bit precision
+					hpos += 128;
+					continue;
+				}
+				// do diff coding for 8 bit precision
+				for ( spos = 63; spos > 0; spos-- )
+					hdrdata[ hpos + spos ] -= hdrdata[ hpos + spos - 1 ];
+					
+				hpos += 64;
+			}
+		}
+		else { // skip segment
+			hpos += len;
+		}		
+	}
+	
+	
+	return true;
+}
+
+
+/* -----------------------------------------------
+	undoes the header optimizations
+	----------------------------------------------- */
+INTERN bool pjg_unoptimize_header( void )
+{
+	unsigned char  type = 0x00; // type of current marker segment
+	unsigned int   len  = 0; // length of current marker segment
+	unsigned int   hpos = 0; // position in header
+	
+	unsigned int fpos; // end of marker position
+	unsigned int skip; // bytes to skip
+	unsigned int spos; // sub position	
+	int i;
+	
+	
+	// search for DHT (0xFFC4) & DQT (0xFFDB) marker segments	
+	// header parser loop
+	while ( ( int ) hpos < hdrs ) {
+		type = hdrdata[ hpos + 1 ];
+		len = 2 + B_SHORT( hdrdata[ hpos + 2 ], hdrdata[ hpos + 3 ] );
+		
+		if ( type == 0xC4 ) { // for DHT
+			fpos = hpos + len; // reassign length to end position
+			hpos += 4; // skip marker & length
+			while ( hpos < fpos ) {			
+				hpos++;
+				// table found - check if modified
+				if ( hdrdata[ hpos ] > 2 ) {	
+					// reinsert the standard table
+					i = hdrdata[ hpos + 1 ];
+					for ( spos = 0; spos < std_huff_lengths[ i ]; spos++ ) {
+						hdrdata[ hpos + spos ] = std_huff_tables[ i ][ spos ];
+					}
+				}
+								
+				skip = 16;
+				for ( i = 0; i < 16; i++ )		
+					skip += ( int ) hdrdata[ hpos + i ];				
+				hpos += skip;
+			}
+		}
+		else if ( type == 0xDB ) { // for DQT
+			fpos = hpos + len; // reassign length to end position
+			hpos += 4; // skip marker & length
+			while ( hpos < fpos ) {
+				i = LBITS( hdrdata[ hpos ], 4 );				
+				hpos++;
+				// table found
+				if ( i == 1 ) { // get out for 16 bit precision
+					hpos += 128;
+					continue;
+				}
+				// undo diff coding for 8 bit precision
+				for ( spos = 1; spos < 64; spos++ )
+					hdrdata[ hpos + spos ] += hdrdata[ hpos + spos - 1 ];
+					
+				hpos += 64;
+			}
+		}
+		else { // skip segment
+			hpos += len;
+		}		
+	}
+	
+	
+	return true;
+}
+
+
+/* -----------------------------------------------
+	preparations for special average context
+	----------------------------------------------- */
+INTERN void pjg_aavrg_prepare( unsigned short** abs_coeffs, int* weights, unsigned short* abs_store, int cmp )
+{
+	int w = cmpnfo[cmp].bch;
+	
+	// set up quick access arrays for all prediction positions
+	abs_coeffs[ 0 ] = abs_store + (  0 + ((-2)*w) ); // top-top
+	abs_coeffs[ 1 ] = abs_store + ( -1 + ((-1)*w) ); // top-left
+	abs_coeffs[ 2 ] = abs_store + (  0 + ((-1)*w) ); // top
+	abs_coeffs[ 3 ] = abs_store + (  1 + ((-1)*w) ); // top-right
+	abs_coeffs[ 4 ] = abs_store + ( -2 + (( 0)*w) ); // left-left
+	abs_coeffs[ 5 ] = abs_store + ( -1 + (( 0)*w) ); // left
+	// copy context weighting factors
+	weights[ 0 ] = abs_ctx_weights_lum[ 0 ][ 0 ][ 2 ]; // top-top
+	weights[ 1 ] = abs_ctx_weights_lum[ 0 ][ 1 ][ 1 ]; // top-left
+	weights[ 2 ] = abs_ctx_weights_lum[ 0 ][ 1 ][ 2 ]; // top
+	weights[ 3 ] = abs_ctx_weights_lum[ 0 ][ 1 ][ 3 ]; // top-right
+	weights[ 4 ] = abs_ctx_weights_lum[ 0 ][ 2 ][ 0 ]; // left-left
+	weights[ 5 ] = abs_ctx_weights_lum[ 0 ][ 2 ][ 1 ]; // left
+}
+
+
+/* -----------------------------------------------
+	special average context used in coeff encoding
+	----------------------------------------------- */
+INTERN int pjg_aavrg_context( unsigned short** abs_coeffs, int* weights, int pos, int p_y, int p_x, int r_x )
+{
+	int ctx_avr = 0; // AVERAGE context
+	int w_ctx = 0; // accumulated weight of context
+	int w_curr; // current weight of context
+	
+	
+	// different cases due to edge treatment
+	if ( p_y >= 2 ) {
+		w_curr = weights[ 0 ]; ctx_avr += abs_coeffs[ 0 ][ pos ] * w_curr; w_ctx += w_curr;
+		w_curr = weights[ 2 ]; ctx_avr += abs_coeffs[ 2 ][ pos ] * w_curr; w_ctx += w_curr;
+		if ( p_x >= 2 ) {
+			w_curr = weights[ 1 ]; ctx_avr += abs_coeffs[ 1 ][ pos ] * w_curr; w_ctx += w_curr;
+			w_curr = weights[ 4 ]; ctx_avr += abs_coeffs[ 4 ][ pos ] * w_curr; w_ctx += w_curr;
+			w_curr = weights[ 5 ]; ctx_avr += abs_coeffs[ 5 ][ pos ] * w_curr; w_ctx += w_curr;
+		}
+		else if ( p_x == 1 ) {
+			w_curr = weights[ 1 ]; ctx_avr += abs_coeffs[ 1 ][ pos ] * w_curr; w_ctx += w_curr;
+			w_curr = weights[ 5 ]; ctx_avr += abs_coeffs[ 5 ][ pos ] * w_curr; w_ctx += w_curr;
+		}
+		if ( r_x >= 1 ) {
+			w_curr = weights[ 3 ]; ctx_avr += abs_coeffs[ 3 ][ pos ] * w_curr; w_ctx += w_curr;
+		}
+	}
+	else if ( p_y == 1 ) {
+		w_curr = weights[ 2 ]; ctx_avr += abs_coeffs[ 2 ][ pos ] * w_curr; w_ctx += w_curr;
+		if ( p_x >= 2 ) {
+			w_curr = weights[ 1 ]; ctx_avr += abs_coeffs[ 1 ][ pos ] * w_curr; w_ctx += w_curr;
+			w_curr = weights[ 4 ]; ctx_avr += abs_coeffs[ 4 ][ pos ] * w_curr; w_ctx += w_curr;
+			w_curr = weights[ 5 ]; ctx_avr += abs_coeffs[ 5 ][ pos ] * w_curr; w_ctx += w_curr;
+		}
+		else if ( p_x == 1 ) {
+			w_curr = weights[ 1 ]; ctx_avr += abs_coeffs[ 1 ][ pos ] * w_curr; w_ctx += w_curr;
+			w_curr = weights[ 5 ]; ctx_avr += abs_coeffs[ 5 ][ pos ] * w_curr; w_ctx += w_curr;
+		}
+		if ( r_x >= 1 ) {
+			w_curr = weights[ 3 ]; ctx_avr += abs_coeffs[ 3 ][ pos ] * w_curr; w_ctx += w_curr;
+		}
+	}
+	else {
+		if ( p_x >= 2 ) {
+			w_curr = weights[ 4 ]; ctx_avr += abs_coeffs[ 4 ][ pos ] * w_curr; w_ctx += w_curr;
+			w_curr = weights[ 5 ]; ctx_avr += abs_coeffs[ 5 ][ pos ] * w_curr; w_ctx += w_curr;
+		}
+		else if ( p_x == 1 ) {
+			w_curr = weights[ 5 ]; ctx_avr += abs_coeffs[ 5 ][ pos ] * w_curr; w_ctx += w_curr;
+		}
+	}
+	
+	// return average context
+	return ( w_ctx != 0 ) ? ( ctx_avr + ( w_ctx / 2 ) ) / w_ctx : 0;
+}
+
+
+/* -----------------------------------------------
+	lakhani ac context used in coeff encoding
+	----------------------------------------------- */
+INTERN int pjg_lakh_context( signed short** coeffs_x, signed short** coeffs_a, int* pred_cf, int pos )
+{
+	int pred = 0;
+	
+	// calculate partial prediction
+	pred -= ( coeffs_x[ 1 ][ pos ] + coeffs_a[ 1 ][ pos ] ) * pred_cf[ 1 ];
+	pred -= ( coeffs_x[ 2 ][ pos ] - coeffs_a[ 2 ][ pos ] ) * pred_cf[ 2 ];
+	pred -= ( coeffs_x[ 3 ][ pos ] + coeffs_a[ 3 ][ pos ] ) * pred_cf[ 3 ];
+	pred -= ( coeffs_x[ 4 ][ pos ] - coeffs_a[ 4 ][ pos ] ) * pred_cf[ 4 ];
+	pred -= ( coeffs_x[ 5 ][ pos ] + coeffs_a[ 5 ][ pos ] ) * pred_cf[ 5 ];
+	pred -= ( coeffs_x[ 6 ][ pos ] - coeffs_a[ 6 ][ pos ] ) * pred_cf[ 6 ];
+	pred -= ( coeffs_x[ 7 ][ pos ] + coeffs_a[ 7 ][ pos ] ) * pred_cf[ 7 ];
+	// normalize / quantize partial prediction
+	pred = ( ( pred > 0 ) ? ( pred + (pred_cf[0]/2) ) : ( pred - (pred_cf[0]/2) ) ) / pred_cf[ 0 ];
+	// complete prediction
+	pred += coeffs_a[ 0 ][ pos ];
+	
+	return pred;
+}
+
+
+/* -----------------------------------------------
+	Calculates coordinates for nearest neighbor context
+	----------------------------------------------- */
+INTERN void get_context_nnb( int pos, int w, int *a, int *b )
+{
+	// this function calculates and returns coordinates for
+	// a simple 2D context
+	if ( pos == 0 ) {
+		*a = -1;
+		*b = -1;
+	}
+	else if ( ( pos % w ) == 0 ) {
+		*b = pos - w;
+		if ( pos >= ( w << 1 ) )
+			*a = pos - ( w << 1 );
+		else
+			*a = *b;
+	}
+	else if ( pos < w ) {
+		*a = pos - 1;
+		if ( pos >= 2 )
+			*b = pos - 2;
+		else
+			*b = *a;
+	}
+	else {
+		*a = pos - 1;
+		*b = pos - w;
+	}
+}
+
+/* ----------------------- End of PJG specific functions -------------------------- */
+
+/* ----------------------- Begin ofDCT specific functions -------------------------- */
+
+
+/* -----------------------------------------------
+	inverse DCT transform using precalc tables (fast)
+	----------------------------------------------- */
+#if !defined(BUILD_LIB) && defined(DEV_BUILD)
+INTERN int idct_2d_fst_8x8( int cmp, int dpos, int ix, int iy )
+{
+	int idct = 0;
+	int ixy;
+	
+	
+	// calculate start index
+	ixy = ( ( iy << 3 ) + ix ) << 6;
+	
+	// begin transform
+	idct += colldata[ cmp ][  0 ][ dpos ] * adpt_idct_8x8[ cmp ][ ixy + 0 ];
+	idct += colldata[ cmp ][  1 ][ dpos ] * adpt_idct_8x8[ cmp ][ ixy + 1 ];
+	idct += colldata[ cmp ][  5 ][ dpos ] * adpt_idct_8x8[ cmp ][ ixy + 2 ];
+	idct += colldata[ cmp ][  6 ][ dpos ] * adpt_idct_8x8[ cmp ][ ixy + 3 ];
+	idct += colldata[ cmp ][ 14 ][ dpos ] * adpt_idct_8x8[ cmp ][ ixy + 4 ];
+	idct += colldata[ cmp ][ 15 ][ dpos ] * adpt_idct_8x8[ cmp ][ ixy + 5 ];
+	idct += colldata[ cmp ][ 27 ][ dpos ] * adpt_idct_8x8[ cmp ][ ixy + 6 ];
+	idct += colldata[ cmp ][ 28 ][ dpos ] * adpt_idct_8x8[ cmp ][ ixy + 7 ];
+	idct += colldata[ cmp ][  2 ][ dpos ] * adpt_idct_8x8[ cmp ][ ixy + 8 ];
+	idct += colldata[ cmp ][  4 ][ dpos ] * adpt_idct_8x8[ cmp ][ ixy + 9 ];
+	idct += colldata[ cmp ][  7 ][ dpos ] * adpt_idct_8x8[ cmp ][ ixy + 10 ];
+	idct += colldata[ cmp ][ 13 ][ dpos ] * adpt_idct_8x8[ cmp ][ ixy + 11 ];
+	idct += colldata[ cmp ][ 16 ][ dpos ] * adpt_idct_8x8[ cmp ][ ixy + 12 ];
+	idct += colldata[ cmp ][ 26 ][ dpos ] * adpt_idct_8x8[ cmp ][ ixy + 13 ];
+	idct += colldata[ cmp ][ 29 ][ dpos ] * adpt_idct_8x8[ cmp ][ ixy + 14 ];
+	idct += colldata[ cmp ][ 42 ][ dpos ] * adpt_idct_8x8[ cmp ][ ixy + 15 ];
+	idct += colldata[ cmp ][  3 ][ dpos ] * adpt_idct_8x8[ cmp ][ ixy + 16 ];
+	idct += colldata[ cmp ][  8 ][ dpos ] * adpt_idct_8x8[ cmp ][ ixy + 17 ];
+	idct += colldata[ cmp ][ 12 ][ dpos ] * adpt_idct_8x8[ cmp ][ ixy + 18 ];
+	idct += colldata[ cmp ][ 17 ][ dpos ] * adpt_idct_8x8[ cmp ][ ixy + 19 ];
+	idct += colldata[ cmp ][ 25 ][ dpos ] * adpt_idct_8x8[ cmp ][ ixy + 20 ];
+	idct += colldata[ cmp ][ 30 ][ dpos ] * adpt_idct_8x8[ cmp ][ ixy + 21 ];
+	idct += colldata[ cmp ][ 41 ][ dpos ] * adpt_idct_8x8[ cmp ][ ixy + 22 ];
+	idct += colldata[ cmp ][ 43 ][ dpos ] * adpt_idct_8x8[ cmp ][ ixy + 23 ];
+	idct += colldata[ cmp ][  9 ][ dpos ] * adpt_idct_8x8[ cmp ][ ixy + 24 ];
+	idct += colldata[ cmp ][ 11 ][ dpos ] * adpt_idct_8x8[ cmp ][ ixy + 25 ];
+	idct += colldata[ cmp ][ 18 ][ dpos ] * adpt_idct_8x8[ cmp ][ ixy + 26 ];
+	idct += colldata[ cmp ][ 24 ][ dpos ] * adpt_idct_8x8[ cmp ][ ixy + 27 ];
+	idct += colldata[ cmp ][ 31 ][ dpos ] * adpt_idct_8x8[ cmp ][ ixy + 28 ];
+	idct += colldata[ cmp ][ 40 ][ dpos ] * adpt_idct_8x8[ cmp ][ ixy + 29 ];
+	idct += colldata[ cmp ][ 44 ][ dpos ] * adpt_idct_8x8[ cmp ][ ixy + 30 ];
+	idct += colldata[ cmp ][ 53 ][ dpos ] * adpt_idct_8x8[ cmp ][ ixy + 31 ];
+	idct += colldata[ cmp ][ 10 ][ dpos ] * adpt_idct_8x8[ cmp ][ ixy + 32 ];
+	idct += colldata[ cmp ][ 19 ][ dpos ] * adpt_idct_8x8[ cmp ][ ixy + 33 ];
+	idct += colldata[ cmp ][ 23 ][ dpos ] * adpt_idct_8x8[ cmp ][ ixy + 34 ];
+	idct += colldata[ cmp ][ 32 ][ dpos ] * adpt_idct_8x8[ cmp ][ ixy + 35 ];
+	idct += colldata[ cmp ][ 39 ][ dpos ] * adpt_idct_8x8[ cmp ][ ixy + 36 ];
+	idct += colldata[ cmp ][ 45 ][ dpos ] * adpt_idct_8x8[ cmp ][ ixy + 37 ];
+	idct += colldata[ cmp ][ 52 ][ dpos ] * adpt_idct_8x8[ cmp ][ ixy + 38 ];
+	idct += colldata[ cmp ][ 54 ][ dpos ] * adpt_idct_8x8[ cmp ][ ixy + 39 ];
+	idct += colldata[ cmp ][ 20 ][ dpos ] * adpt_idct_8x8[ cmp ][ ixy + 40 ];
+	idct += colldata[ cmp ][ 22 ][ dpos ] * adpt_idct_8x8[ cmp ][ ixy + 41 ];
+	idct += colldata[ cmp ][ 33 ][ dpos ] * adpt_idct_8x8[ cmp ][ ixy + 42 ];
+	idct += colldata[ cmp ][ 38 ][ dpos ] * adpt_idct_8x8[ cmp ][ ixy + 43 ];
+	idct += colldata[ cmp ][ 46 ][ dpos ] * adpt_idct_8x8[ cmp ][ ixy + 44 ];
+	idct += colldata[ cmp ][ 51 ][ dpos ] * adpt_idct_8x8[ cmp ][ ixy + 45 ];
+	idct += colldata[ cmp ][ 55 ][ dpos ] * adpt_idct_8x8[ cmp ][ ixy + 46 ];
+	idct += colldata[ cmp ][ 60 ][ dpos ] * adpt_idct_8x8[ cmp ][ ixy + 47 ];
+	idct += colldata[ cmp ][ 21 ][ dpos ] * adpt_idct_8x8[ cmp ][ ixy + 48 ];
+	idct += colldata[ cmp ][ 34 ][ dpos ] * adpt_idct_8x8[ cmp ][ ixy + 49 ];
+	idct += colldata[ cmp ][ 37 ][ dpos ] * adpt_idct_8x8[ cmp ][ ixy + 50 ];
+	idct += colldata[ cmp ][ 47 ][ dpos ] * adpt_idct_8x8[ cmp ][ ixy + 51 ];
+	idct += colldata[ cmp ][ 50 ][ dpos ] * adpt_idct_8x8[ cmp ][ ixy + 52 ];
+	idct += colldata[ cmp ][ 56 ][ dpos ] * adpt_idct_8x8[ cmp ][ ixy + 53 ];
+	idct += colldata[ cmp ][ 59 ][ dpos ] * adpt_idct_8x8[ cmp ][ ixy + 54 ];
+	idct += colldata[ cmp ][ 61 ][ dpos ] * adpt_idct_8x8[ cmp ][ ixy + 55 ];
+	idct += colldata[ cmp ][ 35 ][ dpos ] * adpt_idct_8x8[ cmp ][ ixy + 56 ];
+	idct += colldata[ cmp ][ 36 ][ dpos ] * adpt_idct_8x8[ cmp ][ ixy + 57 ];
+	idct += colldata[ cmp ][ 48 ][ dpos ] * adpt_idct_8x8[ cmp ][ ixy + 58 ];
+	idct += colldata[ cmp ][ 49 ][ dpos ] * adpt_idct_8x8[ cmp ][ ixy + 59 ];
+	idct += colldata[ cmp ][ 57 ][ dpos ] * adpt_idct_8x8[ cmp ][ ixy + 60 ];
+	idct += colldata[ cmp ][ 58 ][ dpos ] * adpt_idct_8x8[ cmp ][ ixy + 61 ];
+	idct += colldata[ cmp ][ 62 ][ dpos ] * adpt_idct_8x8[ cmp ][ ixy + 62 ];
+	idct += colldata[ cmp ][ 63 ][ dpos ] * adpt_idct_8x8[ cmp ][ ixy + 63 ];
+	
+	
+	return idct;
+}
+#endif
+
+
+/* -----------------------------------------------
+	inverse DCT transform using precalc tables (fast)
+	----------------------------------------------- */
+INTERN int idct_2d_fst_8x1( int cmp, int dpos, int ix, int iy )
+{
+	int idct = 0;
+	int ixy;
+	
+	
+	// calculate start index
+	ixy = ix << 3;
+	
+	// begin transform
+	idct += colldata[ cmp ][  0 ][ dpos ] * adpt_idct_8x1[ cmp ][ ixy + 0 ];
+	idct += colldata[ cmp ][  1 ][ dpos ] * adpt_idct_8x1[ cmp ][ ixy + 1 ];
+	idct += colldata[ cmp ][  5 ][ dpos ] * adpt_idct_8x1[ cmp ][ ixy + 2 ];
+	idct += colldata[ cmp ][  6 ][ dpos ] * adpt_idct_8x1[ cmp ][ ixy + 3 ];
+	idct += colldata[ cmp ][ 14 ][ dpos ] * adpt_idct_8x1[ cmp ][ ixy + 4 ];
+	idct += colldata[ cmp ][ 15 ][ dpos ] * adpt_idct_8x1[ cmp ][ ixy + 5 ];
+	idct += colldata[ cmp ][ 27 ][ dpos ] * adpt_idct_8x1[ cmp ][ ixy + 6 ];
+	idct += colldata[ cmp ][ 28 ][ dpos ] * adpt_idct_8x1[ cmp ][ ixy + 7 ];
+	
+	
+	return idct;
+}
+
+
+/* -----------------------------------------------
+	inverse DCT transform using precalc tables (fast)
+	----------------------------------------------- */
+INTERN int idct_2d_fst_1x8( int cmp, int dpos, int ix, int iy )
+{
+	int idct = 0;
+	int ixy;
+	
+	
+	// calculate start index
+	ixy = iy << 3;
+	
+	// begin transform
+	idct += colldata[ cmp ][  0 ][ dpos ] * adpt_idct_1x8[ cmp ][ ixy + 0 ];
+	idct += colldata[ cmp ][  2 ][ dpos ] * adpt_idct_1x8[ cmp ][ ixy + 1 ];
+	idct += colldata[ cmp ][  3 ][ dpos ] * adpt_idct_1x8[ cmp ][ ixy + 2 ];
+	idct += colldata[ cmp ][  9 ][ dpos ] * adpt_idct_1x8[ cmp ][ ixy + 3 ];
+	idct += colldata[ cmp ][ 10 ][ dpos ] * adpt_idct_1x8[ cmp ][ ixy + 4 ];
+	idct += colldata[ cmp ][ 20 ][ dpos ] * adpt_idct_1x8[ cmp ][ ixy + 5 ];
+	idct += colldata[ cmp ][ 21 ][ dpos ] * adpt_idct_1x8[ cmp ][ ixy + 6 ];
+	idct += colldata[ cmp ][ 35 ][ dpos ] * adpt_idct_1x8[ cmp ][ ixy + 7 ];
+	
+	
+	return idct;
+}
+
+/* ----------------------- End of DCT specific functions -------------------------- */
+
+/* ----------------------- Begin of prediction functions -------------------------- */
+
+
+/* -----------------------------------------------
+	returns predictor for collection data
+	----------------------------------------------- */
+#if defined(USE_PLOCOI)
+INTERN int dc_coll_predictor( int cmp, int dpos )
+{
+	signed short* coefs = colldata[ cmp ][ 0 ];
+	int w = cmpnfo[cmp].bch;
+	int a, b, c;
+	
+	if ( dpos < w ) {
+		a = coefs[ dpos - 1 ];
+		b = 0;
+		c = 0;
+	}
+	else if ( (dpos%w) == 0 ) {
+		a = 0;
+		b = coefs[ dpos - w ];
+		c = 0;
+	}
+	else {
+		a = coefs[ dpos - 1 ];
+		b = coefs[ dpos - w ];
+		c = coefs[ dpos - 1 - w ];
+	}
+	
+	return plocoi( a, b, c );
+}
+#endif
+
+
+/* -----------------------------------------------
+	1D DCT predictor for DC coefficients
+	----------------------------------------------- */
+#if !defined(USE_PLOCOI)
+INTERN int dc_1ddct_predictor( int cmp, int dpos )
+{
+	int w  = cmpnfo[cmp].bch;
+	int px = ( dpos % w );
+	int py = ( dpos / w );
+	
+	int pred;	
+	int pa = 0;
+	int pb = 0;
+	int xa = 0;
+	int xb = 0;
+	int swap;
+	
+	
+	// store current block DC coefficient
+	swap = colldata[ cmp ][ 0 ][ dpos ];
+	colldata[ cmp ][ 0 ][ dpos ] = 0;
+	
+	// calculate prediction
+	if ( ( px > 0 ) && ( py > 0 ) ) {
+		pa = idct_2d_fst_8x1( cmp, dpos - 1, 7, 0 );
+		pb = idct_2d_fst_1x8( cmp, dpos - w, 0, 7 );
+		xa = idct_2d_fst_8x1( cmp, dpos, 0, 0 );
+		xb = idct_2d_fst_1x8( cmp, dpos, 0, 0 );
+		pred = ( ( pa - xa ) + ( pb - xb ) ) * ( 8 / 2 );
+	}
+	else if ( px > 0 ) {
+		pa = idct_2d_fst_8x1( cmp, dpos - 1, 7, 0 );
+		xa = idct_2d_fst_8x1( cmp, dpos, 0, 0 );
+		pred = ( pa - xa ) * 8;
+	}
+	else if ( py > 0 ) {
+		pb = idct_2d_fst_1x8( cmp, dpos - w, 0, 7 );
+		xb = idct_2d_fst_1x8( cmp, dpos, 0, 0 );
+		pred = ( pb - xb ) * 8;
+	}
+	else {
+		pred = 0;
+	}
+	
+	// write back current DCT coefficient
+	colldata[ cmp ][ 0 ][ dpos ] = swap;	
+	
+	// clamp and quantize predictor
+	pred = CLAMPED( -( 1024 * DCT_RSC_FACTOR ), ( 1016 * DCT_RSC_FACTOR ), pred );
+	pred = pred / QUANT( cmp, 0 );
+	pred = DCT_RESCALE( pred );
+	
+	
+	return pred;
+}
+#endif
+
+
+/* -----------------------------------------------
+	loco-i predictor
+	----------------------------------------------- */
+INTERN inline int plocoi( int a, int b, int c )
+{
+	// a -> left; b -> above; c -> above-left
+	int min, max;
+	
+	min = ( a < b ) ? a : b;
+	max = ( a > b ) ? a : b;
+	
+	if ( c >= max ) return min;
+	if ( c <= min ) return max;
+	
+	return a + b - c;
+}
+
+
+/* -----------------------------------------------
+	calculates median out of an integer array
+	----------------------------------------------- */
+INTERN inline int median_int( int* values, int size )
+{
+	int middle = ( size >> 1 );
+	bool done;
+	int swap;
+	int i;
+	
+	
+	// sort data first
+	done = false;
+	while ( !done ) {
+		done = true;
+		for ( i = 1; i < size; i++ )
+		if ( values[ i ] < values[ i - 1 ] ) {
+			swap = values[ i ];
+			values[ i ] = values[ i - 1 ];
+			values[ i - 1 ] = swap;
+			done = false;
+		}
+	}
+	
+	// return median
+	return ( ( size % 2 ) == 0 ) ?
+		( values[ middle ] + values[ middle - 1 ] ) / 2 : values[ middle ];
+}
+
+
+/* -----------------------------------------------
+	calculates median out of an float array
+	----------------------------------------------- */
+INTERN inline float median_float( float* values, int size )
+{
+	int middle = ( size >> 1 );
+	bool done;
+	float swap;
+	int i;
+	
+	
+	// sort data first
+	done = false;
+	while ( !done ) {
+		done = true;
+		for ( i = 1; i < size; i++ )
+		if ( values[ i ] < values[ i - 1 ] ) {
+			swap = values[ i ];
+			values[ i ] = values[ i - 1 ];
+			values[ i - 1 ] = swap;
+			done = false;
+		}
+	}
+	
+	// return median	
+	if ( ( size % 2 ) == 0 ) {
+		return ( values[ middle ] + values[ middle - 1 ] ) / 2.0;
+	}
+	else
+		return ( values[ middle ] );
+}
+
+/* ----------------------- End of prediction functions -------------------------- */
+
+/* ----------------------- Begin of miscellaneous helper functions -------------------------- */
+
+
+/* -----------------------------------------------
+	displays progress bar on screen
+	----------------------------------------------- */
+#if !defined(BUILD_LIB)
+INTERN inline void progress_bar( int current, int last )
+{
+	int barpos = ( ( current * BARLEN ) + ( last / 2 ) ) / last;
+	int i;
+	
+	
+	// generate progress bar
+	fprintf( msgout, "[" );
+	#if defined(_WIN32)
+	for ( i = 0; i < barpos; i++ )
+		fprintf( msgout, "\xFE" );
+	#else
+	for ( i = 0; i < barpos; i++ )
+		fprintf( msgout, "X" );
+	#endif
+	for (  ; i < BARLEN; i++ )
+		fprintf( msgout, " " );
+	fprintf( msgout, "]" );
+}
+#endif
+
+/* -----------------------------------------------
+	creates filename, callocs memory for it
+	----------------------------------------------- */
+#if !defined(BUILD_LIB)
+INTERN inline char* create_filename( const char* base, const char* extension )
+{
+	int len = strlen( base ) + ( ( extension == NULL ) ? 0 : strlen( extension ) + 1 ) + 1;	
+	char* filename = (char*) calloc( len, sizeof( char ) );	
+	
+	// create a filename from base & extension
+	strcpy( filename, base );
+	set_extension( filename, extension );
+	
+	return filename;
+}
+#endif
+
+/* -----------------------------------------------
+	creates filename, callocs memory for it
+	----------------------------------------------- */
+#if !defined(BUILD_LIB)
+INTERN inline char* unique_filename( const char* base, const char* extension )
+{
+	int len = strlen( base ) + ( ( extension == NULL ) ? 0 : strlen( extension ) + 1 ) + 1;	
+	char* filename = (char*) calloc( len, sizeof( char ) );	
+	
+	// create a unique filename using underscores
+	strcpy( filename, base );
+	set_extension( filename, extension );
+	while ( file_exists( filename ) ) {
+		len += sizeof( char );
+		filename = (char*) realloc( filename, len );
+		add_underscore( filename );
+	}
+	
+	return filename;
+}
+#endif
+
+/* -----------------------------------------------
+	changes extension of filename
+	----------------------------------------------- */
+#if !defined(BUILD_LIB)
+INTERN inline void set_extension( char* filename, const char* extension )
+{
+	char* extstr;
+	
+	// find position of extension in filename	
+	extstr = ( strrchr( filename, '.' ) == NULL ) ?
+		strrchr( filename, '\0' ) : strrchr( filename, '.' );
+	
+	// set new extension
+	if ( extension != NULL ) {
+		(*extstr++) = '.';
+		strcpy( extstr, extension );
+	}
+	else
+		(*extstr) = '\0';
+}
+#endif
+
+/* -----------------------------------------------
+	adds underscore after filename
+	----------------------------------------------- */
+#if !defined(BUILD_LIB)
+INTERN inline void add_underscore( char* filename )
+{
+	char* tmpname = (char*) calloc( strlen( filename ) + 1, sizeof( char ) );
+	char* extstr;
+	
+	// copy filename to tmpname
+	strcpy( tmpname, filename );
+	// search extension in filename
+	extstr = strrchr( filename, '.' );
+	
+	// add underscore before extension
+	if ( extstr != NULL ) {
+		(*extstr++) = '_';
+		strcpy( extstr, strrchr( tmpname, '.' ) );
+	}
+	else
+		sprintf( filename, "%s_", tmpname );
+		
+	// free memory
+	free( tmpname );
+}
+#endif
+
+/* -----------------------------------------------
+	checks if a file exists
+	----------------------------------------------- */
+INTERN inline bool file_exists( const char* filename )
+{
+	// needed for both, executable and library
+	FILE* fp = fopen( filename, "rb" );
+	
+	if ( fp == NULL ) return false;
+	else {
+		fclose( fp );
+		return true;
+	}
+}
+
+/* ----------------------- End of miscellaneous helper functions -------------------------- */
+
+/* ----------------------- Begin of developers functions -------------------------- */
+
+
+/* -----------------------------------------------
+	Writes header file
+	----------------------------------------------- */
+#if !defined(BUILD_LIB) && defined(DEV_BUILD)
+INTERN bool dump_hdr( void )
+{
+	const char* ext = "hdr";
+	const char* basename = filelist[ file_no ];
+	
+	if ( !dump_file( basename, ext, hdrdata, 1, hdrs ) )
+		return false;	
+	
+	return true;
+}
+#endif
+
+
+/* -----------------------------------------------
+	Writes huffman coded file
+	----------------------------------------------- */
+#if !defined(BUILD_LIB) && defined(DEV_BUILD)
+INTERN bool dump_huf( void )
+{
+	const char* ext = "huf";
+	const char* basename = filelist[ file_no ];
+	
+	if ( !dump_file( basename, ext, huffdata, 1, hufs ) )
+		return false;
+	
+	return true;
+}
+#endif
+
+
+/* -----------------------------------------------
+	Writes collections of DCT coefficients
+	----------------------------------------------- */
+#if !defined(BUILD_LIB) && defined(DEV_BUILD)
+INTERN bool dump_coll( void )
+{
+	FILE* fp;
+	
+	char* fn;
+	const char* ext[ 4 ];
+	const char* base;
+	int cmp, bpos, dpos;
+	int i, j;
+	
+	ext[0] = "coll0";
+	ext[1] = "coll1";
+	ext[2] = "coll2";
+	ext[3] = "coll3";
+	base = filelist[ file_no ];
+	
+	
+	for ( cmp = 0; cmp < cmpc; cmp++ ) {
+		
+		// create filename
+		fn = create_filename( base, ext[ cmp ] );
+		
+		// open file for output
+		fp = fopen( fn, "wb" );
+		if ( fp == NULL ){
+			sprintf( errormessage, FWR_ERRMSG, fn);
+			errorlevel = 2;
+			return false;
+		}
+		free( fn );
+		
+		switch ( collmode ) {
+			
+			case 0: // standard collections
+				for ( bpos = 0; bpos < 64; bpos++ )
+					fwrite( colldata[cmp][bpos], sizeof( short ), cmpnfo[cmp].bc, fp );
+				break;
+				
+			case 1: // sequential order collections, 'dhufs'
+				for ( dpos = 0; dpos < cmpnfo[cmp].bc; dpos++ )
+				for ( bpos = 0; bpos < 64; bpos++ )
+					fwrite( &(colldata[cmp][bpos][dpos]), sizeof( short ), 1, fp );
+				break;
+				
+			case 2: // square collections
+				dpos = 0;
+				for ( i = 0; i < 64; ) {
+					bpos = zigzag[ i++ ];
+					fwrite( &(colldata[cmp][bpos][dpos]), sizeof( short ),
+						cmpnfo[cmp].bch, fp );
+					if ( ( i % 8 ) == 0 ) {
+						dpos += cmpnfo[cmp].bch;
+						if ( dpos >= cmpnfo[cmp].bc ) {
+							dpos = 0;
+						}
+						else {
+							i -= 8;
+						}
+					}
+				}
+				break;
+				
+			case 3: // uncollections
+				for ( i = 0; i < ( cmpnfo[cmp].bcv * 8 ); i++ )			
+				for ( j = 0; j < ( cmpnfo[cmp].bch * 8 ); j++ ) {
+					bpos = zigzag[ ( ( i % 8 ) * 8 ) + ( j % 8 ) ];
+					dpos = ( ( i / 8 ) * cmpnfo[cmp].bch ) + ( j / 8 );
+					fwrite( &(colldata[cmp][bpos][dpos]), sizeof( short ), 1, fp );
+				}
+				break;
+				
+			case 4: // square collections / alt order (even/uneven)
+				dpos = 0;
+				for ( i = 0; i < 64; ) {
+					bpos = even_zigzag[ i++ ];
+					fwrite( &(colldata[cmp][bpos][dpos]), sizeof( short ),
+						cmpnfo[cmp].bch, fp );
+					if ( ( i % 8 ) == 0 ) {
+						dpos += cmpnfo[cmp].bch;
+						if ( dpos >= cmpnfo[cmp].bc ) {
+							dpos = 0;
+						}
+						else {
+							i -= 8;
+						}
+					}
+				}
+				break;
+				
+			case 5: // uncollections / alt order (even/uneven)
+				for ( i = 0; i < ( cmpnfo[cmp].bcv * 8 ); i++ )			
+				for ( j = 0; j < ( cmpnfo[cmp].bch * 8 ); j++ ) {
+					bpos = even_zigzag[ ( ( i % 8 ) * 8 ) + ( j % 8 ) ];
+					dpos = ( ( i / 8 ) * cmpnfo[cmp].bch ) + ( j / 8 );
+					fwrite( &(colldata[cmp][bpos][dpos]), sizeof( short ), 1, fp );
+				}
+				break;
+		}
+		
+		fclose( fp );
+	}
+	
+	return true;
+}
+#endif
+
+
+/* -----------------------------------------------
+	Writes zero distribution data to file;
+	----------------------------------------------- */
+#if !defined(BUILD_LIB) && defined(DEV_BUILD)
+INTERN bool dump_zdst( void )
+{
+	const char* ext[4];
+	const char* basename;
+	int cmp;
+	
+	
+	ext[0] = "zdst0";
+	ext[1] = "zdst1";
+	ext[2] = "zdst2";
+	ext[3] = "zdst3";
+	basename = filelist[ file_no ];
+	
+	for ( cmp = 0; cmp < cmpc; cmp++ )
+		if ( !dump_file( basename, ext[cmp], zdstdata[cmp], 1, cmpnfo[cmp].bc ) )
+			return false;
+			
+	
+	return true;
+}
+#endif
+
+
+/* -----------------------------------------------
+	Writes to file
+	----------------------------------------------- */
+#if !defined(BUILD_LIB) && defined(DEV_BUILD)
+INTERN bool dump_file( const char* base, const char* ext, void* data, int bpv, int size )
+{	
+	FILE* fp;
+	char* fn;
+	
+	// create filename
+	fn = create_filename( base, ext );
+	
+	// open file for output
+	fp = fopen( fn, "wb" );	
+	if ( fp == NULL ) {
+		sprintf( errormessage, FWR_ERRMSG, fn);
+		errorlevel = 2;
+		return false;
+	}
+	free( fn );
+	
+	// write & close
+	fwrite( data, bpv, size, fp );
+	fclose( fp );
+	
+	return true;
+}
+#endif
+
+
+/* -----------------------------------------------
+	Writes error info file
+	----------------------------------------------- */
+#if !defined(BUILD_LIB) && defined(DEV_BUILD)
+INTERN bool dump_errfile( void )
+{
+	FILE* fp;
+	char* fn;
+	
+	
+	// return immediately if theres no error
+	if ( errorlevel == 0 ) return true;
+	
+	// create filename based on errorlevel
+	if ( errorlevel == 1 ) {
+		fn = create_filename( filelist[ file_no ], "wrn.nfo" );
+	}
+	else {
+		fn = create_filename( filelist[ file_no ], "err.nfo" );
+	}
+	
+	// open file for output
+	fp = fopen( fn, "w" );
+	if ( fp == NULL ){
+		sprintf( errormessage, FWR_ERRMSG, fn);
+		errorlevel = 2;
+		return false;
+	}
+	free( fn );
+	
+	// write status and errormessage to file
+	fprintf( fp, "--> error (level %i) in file \"%s\" <--\n", errorlevel, filelist[ file_no ] );
+	fprintf( fp, "\n" );
+	// write error specification to file
+	fprintf( fp, " %s -> %s:\n", get_status( errorfunction ),
+			( errorlevel == 1 ) ? "warning" : "error" );
+	fprintf( fp, " %s\n", errormessage );
+	
+	// done, close file
+	fclose( fp );
+	
+	
+	return true;
+}
+#endif
+
+
+/* -----------------------------------------------
+	Writes info to textfile
+	----------------------------------------------- */
+#if !defined(BUILD_LIB) && defined(DEV_BUILD)
+INTERN bool dump_info( void )
+{	
+	FILE* fp;
+	char* fn;
+	
+	unsigned char  type = 0x00; // type of current marker segment
+	unsigned int   len  = 0; // length of current marker segment
+	unsigned int   hpos = 0; // position in header		
+	
+	int cmp, bpos;
+	int i;
+	
+	
+	// create filename
+	fn = create_filename( filelist[ file_no ], "nfo" );
+	
+	// open file for output
+	fp = fopen( fn, "w" );
+	if ( fp == NULL ){
+		sprintf( errormessage, FWR_ERRMSG, fn);
+		errorlevel = 2;
+		return false;
+	}
+	free( fn );
+
+	// info about image
+	fprintf( fp, "<Infofile for JPEG image %s>\n\n\n", jpgfilename );
+	fprintf( fp, "coding process: %s\n", ( jpegtype == 1 ) ? "sequential" : "progressive" );
+	// fprintf( fp, "no of scans: %i\n", scnc );
+	fprintf( fp, "imageheight: %i / imagewidth: %i\n", imgheight, imgwidth );
+	fprintf( fp, "component count: %i\n", cmpc );
+	fprintf( fp, "mcu count: %i/%i/%i (all/v/h)\n\n", mcuc, mcuv, mcuh );
+	
+	// info about header
+	fprintf( fp, "\nfile header structure:\n" );
+	fprintf( fp, " type  length   hpos\n" );
+	// header parser loop
+	for ( hpos = 0; (int) hpos < hdrs; hpos += len ) {
+		type = hdrdata[ hpos + 1 ];
+		len = 2 + B_SHORT( hdrdata[ hpos + 2 ], hdrdata[ hpos + 3 ] );
+		fprintf( fp, " FF%2X  %6i %6i\n", (int) type, (int) len, (int) hpos );
+	}
+	fprintf( fp, " _END       0 %6i\n", (int) hpos );
+	fprintf( fp, "\n" );
+	
+	// info about compression settings	
+	fprintf( fp, "\ncompression settings:\n" );
+	fprintf( fp, " no of segments    ->  %3i[0] %3i[1] %3i[2] %3i[3]\n",
+			segm_cnt[0], segm_cnt[1], segm_cnt[2], segm_cnt[3] );
+	fprintf( fp, " noise threshold   ->  %3i[0] %3i[1] %3i[2] %3i[3]\n",
+			nois_trs[0], nois_trs[1], nois_trs[2], nois_trs[3] );
+	fprintf( fp, "\n" );
+	
+	// info about components
+	for ( cmp = 0; cmp < cmpc; cmp++ ) {
+		fprintf( fp, "\n" );
+		fprintf( fp, "component number %i ->\n", cmp );
+		fprintf( fp, "sample factors: %i/%i (v/h)\n", cmpnfo[cmp].sfv, cmpnfo[cmp].sfh );
+		fprintf( fp, "blocks per mcu: %i\n", cmpnfo[cmp].mbs );
+		fprintf( fp, "block count (mcu): %i/%i/%i (all/v/h)\n",
+			cmpnfo[cmp].bc, cmpnfo[cmp].bcv, cmpnfo[cmp].bch );
+		fprintf( fp, "block count (sng): %i/%i/%i (all/v/h)\n",
+			cmpnfo[cmp].nc, cmpnfo[cmp].ncv, cmpnfo[cmp].nch );
+		fprintf( fp, "quantiser table ->" );
+		for ( i = 0; i < 64; i++ ) {
+			bpos = zigzag[ i ];
+			if ( ( i % 8 ) == 0 ) fprintf( fp, "\n" );
+			fprintf( fp, "%4i, ", QUANT( cmp, bpos ) );
+		}
+		fprintf( fp, "\n" );
+		fprintf( fp, "maximum values ->" );
+		for ( i = 0; i < 64; i++ ) {
+			bpos = zigzag[ i ];
+			if ( ( i % 8 ) == 0 ) fprintf( fp, "\n" );
+			fprintf( fp, "%4i, ", MAX_V( cmp, bpos ) );
+		}
+		fprintf( fp, "\n\n" );
+	}
+	
+	
+	fclose( fp );
+	
+	
+	return true;
+}
+#endif
+
+
+/* -----------------------------------------------
+	Writes distribution for use in valdist.h
+	----------------------------------------------- */
+#if !defined(BUILD_LIB) && defined(DEV_BUILD)
+INTERN bool dump_dist( void )
+{
+	FILE* fp;
+	char* fn;
+	
+	unsigned int dist[ 1024 + 1 ];
+	int cmp, bpos, dpos;
+	int i;
+	
+	
+	// create filename
+	fn = create_filename( filelist[ file_no ], "dist" );
+	
+	// open file for output
+	fp = fopen( fn, "wb" );
+	free( fn );
+	if ( fp == NULL ){
+		sprintf( errormessage, FWR_ERRMSG, fn);
+		errorlevel = 2;
+		return false;
+	}
+	
+	// calculate & write distributions for each frequency
+	for ( cmp = 0; cmp < cmpc; cmp++ )
+	for ( bpos = 0; bpos < 64; bpos++ ) {		
+		// preset dist with zeroes
+		for ( i = 0; i <= 1024; i++ ) dist[ i ] = 0;
+		// get distribution
+		for ( dpos = 0; dpos < cmpnfo[cmp].bc; dpos++ )
+			dist[ ABS( colldata[cmp][bpos][dpos] ) ]++;
+		// write to file
+		fwrite( dist, sizeof( int ), 1024 + 1, fp );
+	}
+	
+	
+	// close file
+	fclose( fp );
+	
+	return true;
+}
+#endif
+
+
+/* -----------------------------------------------
+	Do inverse DCT and write pgms
+	----------------------------------------------- */
+#if !defined(BUILD_LIB) && defined(DEV_BUILD)
+INTERN bool dump_pgm( void )
+{	
+	unsigned char* imgdata;
+	
+	FILE* fp;
+	char* fn;
+	const char* ext[4];
+	
+	int cmp, dpos;
+	int pix_v;
+	int xpos, ypos, dcpos;
+	int x, y;
+	
+	
+	ext[0] = "cmp0.pgm";
+	ext[1] = "cmp1.pgm";
+	ext[2] = "cmp2.pgm";
+	ext[3] = "cmp3.pgm";
+	
+	
+	for ( cmp = 0; cmp < cmpc; cmp++ )
+	{
+		// create filename
+		fn = create_filename( filelist[ file_no ], ext[ cmp ] );
+		
+		// open file for output
+		fp = fopen( fn, "wb" );		
+		if ( fp == NULL ){
+			sprintf( errormessage, FWR_ERRMSG, fn );
+			errorlevel = 2;
+			return false;
+		}
+		free( fn );
+		
+		// alloc memory for image data
+		imgdata = (unsigned char*) calloc ( cmpnfo[cmp].bc * 64, sizeof( char ) );
+		if ( imgdata == NULL ) {
+			fclose( fp );
+			sprintf( errormessage, MEM_ERRMSG );
+			errorlevel = 2;
+			return false;
+		}
+		
+		for ( dpos = 0; dpos < cmpnfo[cmp].bc; dpos++ )	{	
+			// do inverse DCT, store in imgdata
+			dcpos  = ( ( ( dpos / cmpnfo[cmp].bch ) * cmpnfo[cmp].bch ) << 6 ) +
+					   ( ( dpos % cmpnfo[cmp].bch ) << 3 );
+			for ( y = 0; y < 8; y++ ) {
+				ypos = dcpos + ( y * ( cmpnfo[cmp].bch << 3 ) );
+				for ( x = 0; x < 8; x++ ) {
+					xpos = ypos + x;
+					pix_v = idct_2d_fst_8x8( cmp, dpos, x, y );
+					pix_v = DCT_RESCALE( pix_v );
+					pix_v = pix_v + 128;
+					imgdata[ xpos ] = ( unsigned char ) CLAMPED( 0, 255, pix_v );
+				}
+			}			
+		}
+		
+		// write PGM header
+		fprintf( fp, "P5\n" );
+		fprintf( fp, "# created by %s v%i.%i%s (%s) by %s\n",
+			apptitle, appversion / 10, appversion % 10, subversion, versiondate, author );
+		fprintf( fp, "%i %i\n", cmpnfo[cmp].bch * 8, cmpnfo[cmp].bcv * 8 );
+		fprintf( fp, "255\n" );
+		
+		// write image data
+		fwrite( imgdata, sizeof( char ), cmpnfo[cmp].bc * 64, fp );
+		
+		// free memory
+		free( imgdata );
+		
+		// close file
+		fclose( fp );
+	}
+	
+	return true;
+}
+#endif
+
+/* ----------------------- End of developers functions -------------------------- */
+
+/* ----------------------- End of file -------------------------- */
+
diff --git a/software/packJPG_library/source/packjpg.spec b/software/packJPG_library/source/packjpg.spec
new file mode 100644
index 0000000..e3aa20a
--- /dev/null
+++ b/software/packJPG_library/source/packjpg.spec
@@ -0,0 +1,43 @@
+Name:		packjpg
+Version:	2.5j
+Release:	1%{?dist}
+Summary:	Lossless JPEG re-compression
+URL:		http://www.elektronik.htw-aalen.de/packjpg/
+License:	LGPL
+Group:		Applications/File
+BuildRoot:	%{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
+Source0:	%{name}-%{version}.tar.xz
+Patch0:		%{name}-build-fix.patch
+
+%description
+packJPG is a compression program specially designed for further
+compression of JPEG images without causing any further loss. Typically
+it reduces the file size of a JPEG file by 20%.
+
+%prep
+%setup -q
+%patch0 -p1
+
+
+%build
+cd source
+CFLAGS="%{optflags}" LDFLAGS="%{?__global_ldflags}" make -f Makefile_linux %{?_smp_mflags}
+
+
+%install
+rm -rf %{buildroot}
+install -D -m755 source/%{name} %{buildroot}%{_bindir}/%{name}
+
+
+%clean
+rm -rf %{buildroot}
+
+
+%files
+%defattr(-,root,root)
+%{_bindir}/%{name}
+
+
+%changelog
+* Fri Jul 18 2014 Bryan Stillwell <bstillwell@photobucket.com> - 2.5j-1
+- Initial packaging
diff --git a/software/packJPG_library/source/packjpgdll.h b/software/packJPG_library/source/packjpgdll.h
new file mode 100644
index 0000000..8a49cf9
--- /dev/null
+++ b/software/packJPG_library/source/packjpgdll.h
@@ -0,0 +1,36 @@
+// packJPGdll.h - function import declarations for the packJPG DLL
+#define IMPORT __declspec( dllimport )
+
+/* -----------------------------------------------
+	function declarations: library only functions
+	----------------------------------------------- */
+	
+IMPORT bool pjglib_convert_stream2stream( char* msg );
+IMPORT bool pjglib_convert_file2file( char* in, char* out, char* msg );
+IMPORT bool pjglib_convert_stream2mem( unsigned char** out_file, unsigned int* out_size, char* msg );
+IMPORT void pjglib_init_streams( void* in_src, int in_type, int in_size, void* out_dest, int out_type );
+IMPORT const char* pjglib_version_info( void );
+IMPORT const char* pjglib_short_name( void );
+
+/* a short reminder about input/output stream types
+   for the pjglib_init_streams() function
+	
+	if input is file
+	----------------
+	in_scr -> name of input file
+	in_type -> 0
+	in_size -> ignore
+	
+	if input is memory
+	------------------
+	in_scr -> array containg data
+	in_type -> 1
+	in_size -> size of data array
+	
+	if input is *FILE (f.e. stdin)
+	------------------------------
+	in_src -> stream pointer
+	in_type -> 2
+	in_size -> ignore
+	
+	vice versa for output streams! */
diff --git a/software/packJPG_library/source/packjpglib.h b/software/packJPG_library/source/packjpglib.h
new file mode 100644
index 0000000..852215c
--- /dev/null
+++ b/software/packJPG_library/source/packjpglib.h
@@ -0,0 +1,40 @@
+// packJPGlib.h - function declarations for the packJPG library
+#if defined BUILD_DLL
+	#define EXPORT __declspec( dllexport )
+#else
+	#define EXPORT extern
+#endif
+
+/* -----------------------------------------------
+	function declarations: library only functions
+	----------------------------------------------- */
+
+EXPORT bool pjglib_convert_stream2stream( char* msg );
+EXPORT bool pjglib_convert_file2file( char* in, char* out, char* msg );
+EXPORT bool pjglib_convert_stream2mem( unsigned char** out_file, unsigned int* out_size, char* msg );
+EXPORT void pjglib_init_streams( void* in_src, int in_type, int in_size, void* out_dest, int out_type );
+EXPORT const char* pjglib_version_info( void );
+EXPORT const char* pjglib_short_name( void );
+
+/* a short reminder about input/output stream types
+   for the pjglib_init_streams() function
+	
+	if input is file
+	----------------
+	in_scr -> name of input file
+	in_type -> 0
+	in_size -> ignore
+	
+	if input is memory
+	------------------
+	in_scr -> array containg data
+	in_type -> 1
+	in_size -> size of data array
+	
+	if input is *FILE (f.e. stdin)
+	------------------------------
+	in_src -> stream pointer
+	in_type -> 2
+	in_size -> ignore
+	
+	vice versa for output streams! */
diff --git a/software/packJPG_library/source/pjpgtbl.h b/software/packJPG_library/source/pjpgtbl.h
new file mode 100644
index 0000000..c81083e
--- /dev/null
+++ b/software/packJPG_library/source/pjpgtbl.h
@@ -0,0 +1,1659 @@
+/* -----------------------------------------------
+	defines for use in packJPG processing
+	----------------------------------------------- */
+
+// action defines
+#define A_COMPRESS			1
+#define A_SPLIT_DUMP		2
+#define A_COLL_DUMP			3
+#define A_FCOLL_DUMP		4
+#define A_ZDST_DUMP			5
+#define A_TXT_INFO			6
+#define A_DIST_INFO			7
+#define A_PGM_DUMP			8
+
+// file type defines
+#define F_JPG				1
+#define F_PJG				2
+#define F_UNK				3
+
+
+/* -----------------------------------------------
+	compression helper tables
+	----------------------------------------------- */
+
+// maxima for each frequency in zigzag order
+// dc maximum is fixed by offset (+4/QUANT)
+static const unsigned short int freqmax[] =
+{
+	1024,  931,  932,  985,  858,  985,  968,  884, 
+	 884,  967, 1020,  841,  871,  840, 1020,  968, 
+	 932,  875,  876,  932,  969, 1020,  838,  985, 
+	 844,  985,  838, 1020, 1020,  854,  878,  967, 
+	 967,  878,  854, 1020,  854,  871,  886, 1020, 
+	 886,  871,  854,  854,  870,  969,  969,  870, 
+	 854,  838, 1010,  838, 1020,  837, 1020,  969, 
+	 969, 1020,  838, 1020,  838, 1020, 1020,  838
+};
+
+/*
+// maxima for each frequency - IJG DCT float (not used)
+static const unsigned short int freqmax_float[] =
+{
+	1024,  924,  942,  924, 1020,  924,  942,  924,
+	 924,  837,  854,  837,  924,  837,  854,  837,
+	 942,  854,  871,  854,  942,  854,  871,  854,
+	 924,  837,  854,  837,  924,  837,  854,  837,
+	1020,  924,  942,  924, 1020,  924,  942,  924,
+	 924,  837,  854,  837,  924,  837,  854,  837,
+	 942,  854,  871,  854,  942,  854,  871,  854,
+	 924,  837,  854,  837,  924,  837,  854,  837
+};
+*/
+
+/*
+// maxima for each frequency - IJG DCT int (not used)
+static const unsigned short int freqmax_int[] =
+{
+	1024,  924,  942,  924, 1020,  924,  942,  924,
+	 924,  838,  854,  838,  924,  838,  854,  838,
+	 942,  854,  871,  854,  942,  854,  871,  854,
+	 924,  837,  854,  837,  924,  837,  854,  837,
+	1020,  924,  942,  924, 1020,  924,  942,  924,
+	 924,  838,  854,  838,  924,  838,  854,  838,
+	 942,  854,  871,  854,  942,  854,  871,  854,
+	 924,  838,  854,  838,  924,  838,  854,  838
+};
+*/
+
+/*
+// maxima for each frequency - IJG DCT fast (not used)
+static const unsigned short int freqmax_fast[] =
+{
+	1024,  931,  985,  968, 1020,  968, 1020, 1020,
+	 932,  858,  884,  840,  932,  812,  854,  854,
+	 985,  884,  849,  875,  985,  878,  821,  821,
+	 967,  841,  876,  844,  967,  886,  870,  726,
+	1020,  932,  985,  967, 1020,  969, 1020, 1020,
+	 969,  812,  878,  886,  969,  829,  969,  727,
+	1020,  854,  821,  870, 1010,  969, 1020, 1020,
+	1020,  854,  821,  725, 1020,  727, 1020,  510
+};
+*/
+
+/*
+// maxima for each frequency - IJG DCT max (not used)
+static const unsigned short int freqmax_ijg[] =
+{
+	1024,  931,  985,  968, 1020,  968, 1020, 1020,
+	 932,  858,  884,  840,  932,  838,  854,  854,
+	 985,  884,  871,  875,  985,  878,  871,  854,
+	 967,  841,  876,  844,  967,  886,  870,  837,
+	1020,  932,  985,  967, 1020,  969, 1020, 1020,
+	 969,  838,  878,  886,  969,  838,  969,  838,
+	1020,  854,  871,  870, 1010,  969, 1020, 1020,
+	1020,  854,  854,  838, 1020,  838, 1020,  838
+};
+*/
+
+/*
+// valdist 99.0% quantiles
+int vdqu[ 3 ][ 64 ] =
+{
+	// table for statistical id 0
+	{
+		 206,  346,  187,  126,   91,   68,   51,   42, 
+		 353,  173,  124,   87,   63,   49,   39,   33, 
+		 186,  119,   93,   70,   53,   42,   34,   29, 
+		 127,   84,   68,   55,   44,   35,   29,   25, 
+		  88,   63,   53,   44,   36,   30,   25,   22, 
+		  67,   48,   42,   36,   30,   26,   22,   20, 
+		  48,   38,   33,   29,   25,   22,   20,   18, 
+		  43,   32,   29,   26,   23,   20,   18,   17, 
+	},
+	// table for statistical id 1
+	{
+		  23,   86,   46,   28,   19,   14,   11,    9, 
+		  93,   48,   32,   21,   15,   11,    9,    7, 
+		  51,   32,   24,   18,   13,   10,    8,    7, 
+		  31,   22,   18,   14,   11,    9,    7,    6, 
+		  21,   16,   14,   11,    9,    7,    6,    5, 
+		  15,   12,   11,    9,    7,    6,    5,    5, 
+		  12,   10,    9,    7,    6,    5,    5,    4, 
+		  10,    8,    7,    6,    5,    5,    4,    4, 
+	},
+	// table for statistical id 2
+	{
+		  25,   89,   45,   27,   18,   13,   10,    8, 
+		  94,   48,   31,   21,   15,   11,    8,    6, 
+		  49,   32,   24,   17,   13,   10,    8,    6, 
+		  29,   22,   18,   14,   11,    8,    7,    5, 
+		  20,   16,   13,   11,    9,    7,    6,    5, 
+		  15,   12,   11,    9,    7,    6,    5,    4, 
+		  11,    9,    9,    7,    6,    5,    4,    4, 
+		   8,    7,    7,    6,    5,    4,    4,    3, 
+	},
+};
+*/
+
+// standard scan = zigzag scan
+static const unsigned char stdscan[] =
+{
+	 0,  1,  2,  3,  4,  5,  6,  7,
+	 8,  9, 10, 11, 12, 13, 14, 15,
+	16,	17, 18, 19, 20, 21, 22, 23,
+	24, 25, 26, 27, 28, 29, 30, 31,
+	32, 33, 34, 35, 36, 37, 38, 39,
+	40, 41, 42, 43, 44, 45, 46, 47,
+	48, 49, 50, 51, 52, 53, 54, 55,
+	56, 57, 58, 59, 60, 61, 62, 63
+};
+
+// zigzag scan conversion table
+static const unsigned char zigzag[] =
+{
+	 0,  1,  5,  6, 14, 15, 27, 28,
+	 2,  4,  7, 13, 16, 26, 29, 42,
+	 3,  8, 12, 17, 25, 30, 41, 43,
+	 9, 11, 18, 24, 31, 40, 44, 53,
+	10, 19, 23, 32, 39, 45, 52, 54,
+	20, 22, 33, 38, 46, 51, 55, 60,
+	21, 34, 37, 47, 50, 56, 59, 61,
+	35, 36, 48, 49, 57, 58, 62, 63
+};
+
+// zigzag scan reverse conversion table
+static const unsigned char unzigzag[] =
+{
+	 0,  1,  8, 16,  9,  2,  3, 10,
+	17, 24, 32, 25, 18, 11,  4,  5,
+	12, 19, 26, 33, 40, 48, 41, 34,
+	27, 20, 13,  6,  7, 14, 21, 28,
+	35, 42, 49, 56, 57, 50, 43, 36,
+	29, 22, 15, 23, 30, 37, 44, 51,
+	58, 59, 52, 45, 38, 31, 39, 46,
+	53, 60, 61, 54, 47, 55, 62, 63
+};
+
+// even/uneven zigzag scan conversion table
+static const unsigned char even_zigzag[] =
+{
+
+	 0,  5, 14, 27,  1,  6, 15, 28, 
+	 3, 12, 25, 41,  8, 17, 30, 43, 
+	10, 23, 39, 52, 19, 32, 45, 54, 
+	21, 37, 50, 59, 34, 47, 56, 61, 
+	 2,  7, 16, 29,  4, 13, 26, 42, 
+	 9, 18, 31, 44, 11, 24, 40, 53, 
+	20, 33, 46, 55, 22, 38, 51, 60, 
+	35, 48, 57, 62, 36, 49, 58, 63, 
+};
+
+// context weighting for each band (luminance) (from POTY 2006/2007)
+static const signed int abs_ctx_weights_lum[ 64 ][ 3 ][ 5 ] =
+{
+	{ // DCT(0/0)
+		{  0,  0,  7,  0,  0, },
+		{  0,  6, 11,  8,  0, },
+		{  9, 13,  0,  0,  0, },
+	},
+	{ // DCT(1/0)
+		{  0,  0,  8,  0,  0, },
+		{  0,  3, 28,  7,  0, },
+		{  5,  7,  0,  0,  0, },
+	},
+	{ // DCT(0/1)
+		{  0,  0,  5,  0,  0, },
+		{  0,  4,  4,  6,  0, },
+		{  8, 30,  0,  0,  0, },
+	},
+	{ // DCT(0/2)
+		{  0,  0,  5,  0,  0, },
+		{  0,  3,  5,  5,  0, },
+		{ 11, 27,  0,  0,  0, },
+	},
+	{ // DCT(1/1)
+		{  0,  0,  4,  0,  0, },
+		{  0,  7, 13, 10,  0, },
+		{  6, 16,  0,  0,  0, },
+	},
+	{ // DCT(2/0)
+		{  0,  0, 10,  0,  0, },
+		{  0,  2, 25,  6,  0, },
+		{  6,  9,  0,  0,  0, },
+	},
+	{ // DCT(3/0)
+		{  0,  0, 10,  0,  0, },
+		{  0,  1, 24,  6,  0, },
+		{  7,  9,  0,  0,  0, },
+	},
+	{ // DCT(2/1)
+		{  0,  0,  5,  0,  0, },
+		{  0,  7, 15, 10,  0, },
+		{  8, 12,  0,  0,  0, },
+	},
+	{ // DCT(1/2)
+		{  0,  0,  6,  0,  0, },
+		{  0,  8,  8, 10,  0, },
+		{  7, 18,  0,  0,  0, },
+	},
+	{ // DCT(0/3)
+		{  0,  0,  6,  0,  0, },
+		{  0,  3,  5,  5,  0, },
+		{ 12, 26,  0,  0,  0, },
+	},
+	{ // DCT(0/4)
+		{  0,  0,  6,  0,  0, },
+		{  0,  3,  6,  5,  0, },
+		{ 13, 25,  0,  0,  0, },
+	},
+	{ // DCT(1/3)
+		{  0,  0,  6,  0,  0, },
+		{  0,  7,  8,  8,  0, },
+		{  8, 19,  0,  0,  0, },
+	},
+	{ // DCT(2/2)
+		{  0,  0,  5,  0,  0, },
+		{  0,  8, 11, 11,  0, },
+		{  8, 14,  0,  0,  0, },
+	},
+	{ // DCT(3/1)
+		{  0,  0,  6,  0,  0, },
+		{  0,  6, 16,  9,  0, },
+		{  8, 11,  0,  0,  0, },
+	},
+	{ // DCT(4/0)
+		{  0,  0, 11,  0,  0, },
+		{  0,  1, 24,  5,  0, },
+		{  7, 10,  0,  0,  0, },
+	},
+	{ // DCT(5/0)
+		{  0,  0, 11,  0,  0, },
+		{  0,  1, 24,  5,  0, },
+		{  7, 10,  0,  0,  0, },
+	},
+	{ // DCT(4/1)
+		{  0,  0,  7,  0,  0, },
+		{  0,  6, 16,  9,  0, },
+		{  8, 11,  0,  0,  0, },
+	},
+	{ // DCT(3/2)
+		{  0,  0,  5,  0,  0, },
+		{  0,  8, 12, 11,  0, },
+		{  8, 12,  0,  0,  0, },
+	},
+	{ // DCT(2/3)
+		{  0,  0,  6,  0,  0, },
+		{  0,  8,  9, 10,  0, },
+		{  8, 15,  0,  0,  0, },
+	},
+	{ // DCT(1/4)
+		{  0,  0,  7,  0,  0, },
+		{  0,  6,  8,  8,  0, },
+		{  9, 19,  0,  0,  0, },
+	},
+	{ // DCT(0/5)
+		{  0,  0,  6,  0,  0, },
+		{  0,  2,  6,  5,  0, },
+		{ 13, 25,  0,  0,  0, },
+	},
+	{ // DCT(0/6)
+		{  0,  0,  5,  0,  0, },
+		{  0,  2,  6,  5,  0, },
+		{ 13, 24,  0,  0,  0, },
+	},
+	{ // DCT(1/5)
+		{  0,  0,  7,  0,  0, },
+		{  0,  6,  8,  7,  0, },
+		{ 10, 19,  0,  0,  0, },
+	},
+	{ // DCT(2/4)
+		{  0,  0,  6,  0,  0, },
+		{  0,  8,  9,  9,  0, },
+		{  8, 16,  0,  0,  0, },
+	},
+	{ // DCT(3/3)
+		{  0,  0,  6,  0,  0, },
+		{  0,  8, 10, 11,  0, },
+		{  8, 13,  0,  0,  0, },
+	},
+	{ // DCT(4/2)
+		{  0,  0,  6,  0,  0, },
+		{  0,  7, 13, 10,  0, },
+		{  8, 11,  0,  0,  0, },
+	},
+	{ // DCT(5/1)
+		{  0,  0,  7,  0,  0, },
+		{  0,  6, 15,  8,  0, },
+		{  8, 11,  0,  0,  0, },
+	},
+	{ // DCT(6/0)
+		{  0,  0, 11,  0,  0, },
+		{  0,  1, 23,  5,  0, },
+		{  7, 10,  0,  0,  0, },
+	},
+	{ // DCT(7/0)
+		{  0,  0, 11,  0,  0, },
+		{  0,  1, 23,  5,  0, },
+		{  7,  9,  0,  0,  0, },
+	},
+	{ // DCT(6/1)
+		{  0,  0,  7,  0,  0, },
+		{  0,  6, 15,  8,  0, },
+		{  8, 10,  0,  0,  0, },
+	},
+	{ // DCT(5/2)
+		{  0,  0,  7,  0,  0, },
+		{  0,  7, 13,  9,  0, },
+		{  8, 11,  0,  0,  0, },
+	},
+	{ // DCT(4/3)
+		{  0,  0,  6,  0,  0, },
+		{  0,  8, 11, 10,  0, },
+		{  8, 12,  0,  0,  0, },
+	},
+	{ // DCT(3/4)
+		{  0,  0,  6,  0,  0, },
+		{  0,  8,  9, 10,  0, },
+		{  8, 14,  0,  0,  0, },
+	},
+	{ // DCT(2/5)
+		{  0,  0,  7,  0,  0, },
+		{  0,  7,  8,  8,  0, },
+		{  9, 16,  0,  0,  0, },
+	},
+	{ // DCT(1/6)
+		{  0,  0,  7,  0,  0, },
+		{  0,  6,  7,  7,  0, },
+		{ 10, 18,  0,  0,  0, },
+	},
+	{ // DCT(0/7)
+		{  0,  0,  6,  0,  0, },
+		{  0,  3,  6,  5,  0, },
+		{ 13, 24,  0,  0,  0, },
+	},
+	{ // DCT(1/7)
+		{  0,  0,  6,  0,  0, },
+		{  0,  5,  7,  6,  0, },
+		{ 10, 18,  0,  0,  0, },
+	},
+	{ // DCT(2/6)
+		{  0,  0,  7,  0,  0, },
+		{  0,  7,  8,  8,  0, },
+		{  9, 15,  0,  0,  0, },
+	},
+	{ // DCT(3/5)
+		{  0,  0,  6,  0,  0, },
+		{  0,  8,  9,  9,  0, },
+		{  8, 14,  0,  0,  0, },
+	},
+	{ // DCT(4/4)
+		{  0,  0,  6,  0,  0, },
+		{  0,  8, 10, 10,  0, },
+		{  8, 12,  0,  0,  0, },
+	},
+	{ // DCT(5/3)
+		{  0,  0,  6,  0,  0, },
+		{  0,  7, 11, 10,  0, },
+		{  8, 11,  0,  0,  0, },
+	},
+	{ // DCT(6/2)
+		{  0,  0,  7,  0,  0, },
+		{  0,  7, 13,  8,  0, },
+		{  8, 11,  0,  0,  0, },
+	},
+	{ // DCT(7/1)
+		{  0,  0,  8,  0,  0, },
+		{  0,  5, 15,  8,  0, },
+		{  8, 10,  0,  0,  0, },
+	},
+	{ // DCT(7/2)
+		{  0,  0,  7,  0,  0, },
+		{  0,  6, 13,  8,  0, },
+		{  8, 10,  0,  0,  0, },
+	},
+	{ // DCT(6/3)
+		{  0,  0,  6,  0,  0, },
+		{  0,  7, 11,  9,  0, },
+		{  8, 10,  0,  0,  0, },
+	},
+	{ // DCT(5/4)
+		{  0,  0,  6,  0,  0, },
+		{  0,  8, 10, 10,  0, },
+		{  8, 11,  0,  0,  0, },
+	},
+	{ // DCT(4/5)
+		{  0,  0,  6,  0,  0, },
+		{  0,  8,  9,  9,  0, },
+		{  8, 12,  0,  0,  0, },
+	},
+	{ // DCT(3/6)
+		{  0,  0,  6,  0,  0, },
+		{  0,  8,  8,  8,  0, },
+		{  8, 13,  0,  0,  0, },
+	},
+	{ // DCT(2/7)
+		{  0,  0,  6,  0,  0, },
+		{  0,  6,  7,  7,  0, },
+		{  9, 16,  0,  0,  0, },
+	},
+	{ // DCT(3/7)
+		{  0,  0,  6,  0,  0, },
+		{  0,  7,  7,  7,  0, },
+		{  8, 14,  0,  0,  0, },
+	},
+	{ // DCT(4/6)
+		{  0,  0,  6,  0,  0, },
+		{  0,  8,  9,  9,  0, },
+		{  7, 12,  0,  0,  0, },
+	},
+	{ // DCT(5/5)
+		{  0,  0,  6,  0,  0, },
+		{  0,  8,  9, 10,  0, },
+		{  8, 11,  0,  0,  0, },
+	},
+	{ // DCT(6/4)
+		{  0,  0,  6,  0,  0, },
+		{  0,  7, 10,  9,  0, },
+		{  7, 10,  0,  0,  0, },
+	},
+	{ // DCT(7/3)
+		{  0,  0,  6,  0,  0, },
+		{  0,  7, 12,  8,  0, },
+		{  8, 10,  0,  0,  0, },
+	},
+	{ // DCT(7/4)
+		{  0,  0,  6,  0,  0, },
+		{  0,  7, 11,  8,  0, },
+		{  7,  9,  0,  0,  0, },
+	},
+	{ // DCT(6/5)
+		{  0,  0,  6,  0,  0, },
+		{  0,  7,  9,  9,  0, },
+		{  7, 10,  0,  0,  0, },
+	},
+	{ // DCT(5/6)
+		{  0,  0,  6,  0,  0, },
+		{  0,  8,  9,  9,  0, },
+		{  7, 11,  0,  0,  0, },
+	},
+	{ // DCT(4/7)
+		{  0,  0,  6,  0,  0, },
+		{  0,  7,  7,  8,  0, },
+		{  7, 12,  0,  0,  0, },
+	},
+	{ // DCT(5/7)
+		{  0,  0,  6,  0,  0, },
+		{  0,  8,  8,  8,  0, },
+		{  7, 11,  0,  0,  0, },
+	},
+	{ // DCT(6/6)
+		{  0,  0,  8,  0,  0, },
+		{  0,  6,  7,  8,  0, },
+		{  8, 12,  0,  0,  0, },
+	},
+	{ // DCT(7/5)
+		{  0,  0,  6,  0,  0, },
+		{  0,  7, 10,  9,  0, },
+		{  7,  9,  0,  0,  0, },
+	},
+	{ // DCT(7/6)
+		{  0,  0,  6,  0,  0, },
+		{  0,  7,  9,  8,  0, },
+		{  7, 10,  0,  0,  0, },
+	},
+	{ // DCT(6/7)
+		{  0,  0,  6,  0,  0, },
+		{  0,  7,  7,  8,  0, },
+		{  7, 11,  0,  0,  0, },
+	},
+	{ // DCT(7/7)
+		{  0,  0,  6,  0,  0, },
+		{  0,  8,  8,  9,  0, },
+		{  7,  9,  0,  0,  0, },
+	},
+};
+
+/*
+// context weighting for each band (chrominance) (from POTY 2006/2007)
+static const signed int abs_ctx_weights_chr[ 64 ][ 3 ][ 5 ] =
+{
+	{ // DCT(0/0)
+		{  0,  0,  7,  0,  0, },
+		{  0,  6, 11,  8,  0, },
+		{  9, 13,  0,  0,  0, },
+	},
+	{ // DCT(1/0)
+		{  0,  0,  6,  0,  0, },
+		{  0,  4, 28,  8,  0, },
+		{  5,  7,  0,  0,  0, },
+	},
+	{ // DCT(0/1)
+		{  0,  0,  5,  0,  0, },
+		{  0,  4,  5,  7,  0, },
+		{  7, 29,  0,  0,  0, },
+	},
+	{ // DCT(0/2)
+		{  0,  0,  5,  0,  0, },
+		{  0,  4,  7,  6,  0, },
+		{ 10, 23,  0,  0,  0, },
+	},
+	{ // DCT(1/1)
+		{  0,  0,  4,  0,  0, },
+		{  0,  7, 13, 10,  0, },
+		{  6, 16,  0,  0,  0, },
+	},
+	{ // DCT(2/0)
+		{  0,  0,  9,  0,  0, },
+		{  0,  3, 22,  7,  0, },
+		{  6,  9,  0,  0,  0, },
+	},
+	{ // DCT(3/0)
+		{  0,  0, 10,  0,  0, },
+		{  0,  4, 19,  7,  0, },
+		{  6,  9,  0,  0,  0, },
+	},
+	{ // DCT(2/1)
+		{  0,  0,  6,  0,  0, },
+		{  0,  7, 14, 10,  0, },
+		{  7, 11,  0,  0,  0, },
+	},
+	{ // DCT(1/2)
+		{  0,  0,  6,  0,  0, },
+		{  0,  8,  9,  9,  0, },
+		{  7, 16,  0,  0,  0, },
+	},
+	{ // DCT(0/3)
+		{  0,  0,  6,  0,  0, },
+		{  0,  4,  7,  6,  0, },
+		{ 10, 20,  0,  0,  0, },
+	},
+	{ // DCT(0/4)
+		{  0,  0,  6,  0,  0, },
+		{  0,  4,  7,  6,  0, },
+		{ 10, 19,  0,  0,  0, },
+	},
+	{ // DCT(1/3)
+		{  0,  0,  6,  0,  0, },
+		{  0,  7,  8,  8,  0, },
+		{  7, 16,  0,  0,  0, },
+	},
+	{ // DCT(2/2)
+		{  0,  0,  6,  0,  0, },
+		{  0,  8, 10, 10,  0, },
+		{  7, 12,  0,  0,  0, },
+	},
+	{ // DCT(3/1)
+		{  0,  0,  6,  0,  0, },
+		{  0,  6, 14,  9,  0, },
+		{  7, 10,  0,  0,  0, },
+	},
+	{ // DCT(4/0)
+		{  0,  0, 10,  0,  0, },
+		{  0,  3, 19,  6,  0, },
+		{  6,  8,  0,  0,  0, },
+	},
+	{ // DCT(5/0)
+		{  0,  0,  9,  0,  0, },
+		{  0,  3, 18,  6,  0, },
+		{  6,  8,  0,  0,  0, },
+	},
+	{ // DCT(4/1)
+		{  0,  0,  6,  0,  0, },
+		{  0,  6, 13,  8,  0, },
+		{  7,  9,  0,  0,  0, },
+	},
+	{ // DCT(3/2)
+		{  0,  0,  6,  0,  0, },
+		{  0,  7, 11,  9,  0, },
+		{  7, 10,  0,  0,  0, },
+	},
+	{ // DCT(2/3)
+		{  0,  0,  6,  0,  0, },
+		{  0,  7,  9,  9,  0, },
+		{  7, 12,  0,  0,  0, },
+	},
+	{ // DCT(1/4)
+		{  0,  0,  6,  0,  0, },
+		{  0,  6,  8,  7,  0, },
+		{  7, 14,  0,  0,  0, },
+	},
+	{ // DCT(0/5)
+		{  0,  0,  5,  0,  0, },
+		{  0,  4,  7,  5,  0, },
+		{ 10, 19,  0,  0,  0, },
+	},
+	{ // DCT(0/6)
+		{  0,  0,  5,  0,  0, },
+		{  0,  4,  7,  5,  0, },
+		{ 10, 18,  0,  0,  0, },
+	},
+	{ // DCT(1/5)
+		{  0,  0,  6,  0,  0, },
+		{  0,  6,  8,  7,  0, },
+		{  7, 14,  0,  0,  0, },
+	},
+	{ // DCT(2/4)
+		{  0,  0,  6,  0,  0, },
+		{  0,  7,  8,  8,  0, },
+		{  7, 12,  0,  0,  0, },
+	},
+	{ // DCT(3/3)
+		{  0,  0,  6,  0,  0, },
+		{  0,  7,  9,  9,  0, },
+		{  7, 10,  0,  0,  0, },
+	},
+	{ // DCT(4/2)
+		{  0,  0,  6,  0,  0, },
+		{  0,  7, 11,  9,  0, },
+		{  6,  9,  0,  0,  0, },
+	},
+	{ // DCT(5/1)
+		{  0,  0,  6,  0,  0, },
+		{  0,  6, 12,  7,  0, },
+		{  6,  9,  0,  0,  0, },
+	},
+	{ // DCT(6/0)
+		{  0,  0,  9,  0,  0, },
+		{  0,  3, 18,  5,  0, },
+		{  5,  7,  0,  0,  0, },
+	},
+	{ // DCT(7/0)
+		{  0,  0,  9,  0,  0, },
+		{  0,  3, 18,  5,  0, },
+		{  5,  6,  0,  0,  0, },
+	},
+	{ // DCT(6/1)
+		{  0,  0,  6,  0,  0, },
+		{  0,  6, 12,  7,  0, },
+		{  6,  8,  0,  0,  0, },
+	},
+	{ // DCT(5/2)
+		{  0,  0,  6,  0,  0, },
+		{  0,  6, 10,  8,  0, },
+		{  6,  9,  0,  0,  0, },
+	},
+	{ // DCT(4/3)
+		{  0,  0,  6,  0,  0, },
+		{  0,  7, 10,  9,  0, },
+		{  6,  9,  0,  0,  0, },
+	},
+	{ // DCT(3/4)
+		{  0,  0,  6,  0,  0, },
+		{  0,  7,  9,  9,  0, },
+		{  7, 11,  0,  0,  0, },
+	},
+	{ // DCT(2/5)
+		{  0,  0,  6,  0,  0, },
+		{  0,  6,  8,  7,  0, },
+		{  7, 12,  0,  0,  0, },
+	},
+	{ // DCT(1/6)
+		{  0,  0,  5,  0,  0, },
+		{  0,  6,  7,  6,  0, },
+		{  7, 14,  0,  0,  0, },
+	},
+	{ // DCT(0/7)
+		{  0,  0,  5,  0,  0, },
+		{  0,  3,  6,  5,  0, },
+		{  9, 21,  0,  0,  0, },
+	},
+	{ // DCT(1/7)
+		{  0,  0,  5,  0,  0, },
+		{  0,  5,  7,  6,  0, },
+		{  7, 14,  0,  0,  0, },
+	},
+	{ // DCT(2/6)
+		{  0,  0,  5,  0,  0, },
+		{  0,  6,  8,  7,  0, },
+		{  7, 12,  0,  0,  0, },
+	},
+	{ // DCT(3/5)
+		{  0,  0,  6,  0,  0, },
+		{  0,  7,  8,  8,  0, },
+		{  7, 11,  0,  0,  0, },
+	},
+	{ // DCT(4/4)
+		{  0,  0,  6,  0,  0, },
+		{  0,  7,  9,  8,  0, },
+		{  6, 10,  0,  0,  0, },
+	},
+	{ // DCT(5/3)
+		{  0,  0,  6,  0,  0, },
+		{  0,  6, 10,  8,  0, },
+		{  6,  9,  0,  0,  0, },
+	},
+	{ // DCT(6/2)
+		{  0,  0,  6,  0,  0, },
+		{  0,  6, 10,  7,  0, },
+		{  6,  8,  0,  0,  0, },
+	},
+	{ // DCT(7/1)
+		{  0,  0,  6,  0,  0, },
+		{  0,  5, 12,  7,  0, },
+		{  6,  7,  0,  0,  0, },
+	},
+	{ // DCT(7/2)
+		{  0,  0,  6,  0,  0, },
+		{  0,  6, 11,  7,  0, },
+		{  6,  8,  0,  0,  0, },
+	},
+	{ // DCT(6/3)
+		{  0,  0,  6,  0,  0, },
+		{  0,  6,  9,  8,  0, },
+		{  6,  8,  0,  0,  0, },
+	},
+	{ // DCT(5/4)
+		{  0,  0,  6,  0,  0, },
+		{  0,  7,  9,  8,  0, },
+		{  6,  9,  0,  0,  0, },
+	},
+	{ // DCT(4/5)
+		{  0,  0,  6,  0,  0, },
+		{  0,  7,  8,  8,  0, },
+		{  6, 10,  0,  0,  0, },
+	},
+	{ // DCT(3/6)
+		{  0,  0,  6,  0,  0, },
+		{  0,  7,  8,  7,  0, },
+		{  7, 11,  0,  0,  0, },
+	},
+	{ // DCT(2/7)
+		{  0,  0,  5,  0,  0, },
+		{  0,  6,  7,  6,  0, },
+		{  6, 12,  0,  0,  0, },
+	},
+	{ // DCT(3/7)
+		{  0,  0,  5,  0,  0, },
+		{  0,  6,  7,  6,  0, },
+		{  6, 11,  0,  0,  0, },
+	},
+	{ // DCT(4/6)
+		{  0,  0,  6,  0,  0, },
+		{  0,  7,  8,  8,  0, },
+		{  6, 10,  0,  0,  0, },
+	},
+	{ // DCT(5/5)
+		{  0,  0,  6,  0,  0, },
+		{  0,  7,  8,  8,  0, },
+		{  6,  9,  0,  0,  0, },
+	},
+	{ // DCT(6/4)
+		{  0,  0,  6,  0,  0, },
+		{  0,  6,  9,  8,  0, },
+		{  6,  8,  0,  0,  0, },
+	},
+	{ // DCT(7/3)
+		{  0,  0,  6,  0,  0, },
+		{  0,  6, 10,  7,  0, },
+		{  6,  8,  0,  0,  0, },
+	},
+	{ // DCT(7/4)
+		{  0,  0,  6,  0,  0, },
+		{  0,  6,  9,  7,  0, },
+		{  6,  8,  0,  0,  0, },
+	},
+	{ // DCT(6/5)
+		{  0,  0,  6,  0,  0, },
+		{  0,  7,  8,  8,  0, },
+		{  6,  8,  0,  0,  0, },
+	},
+	{ // DCT(5/6)
+		{  0,  0,  6,  0,  0, },
+		{  0,  7,  8,  8,  0, },
+		{  6,  9,  0,  0,  0, },
+	},
+	{ // DCT(4/7)
+		{  0,  0,  6,  0,  0, },
+		{  0,  7,  7,  7,  0, },
+		{  6, 10,  0,  0,  0, },
+	},
+	{ // DCT(5/7)
+		{  0,  0,  6,  0,  0, },
+		{  0,  7,  7,  7,  0, },
+		{  6,  9,  0,  0,  0, },
+	},
+	{ // DCT(6/6)
+		{  0,  0,  6,  0,  0, },
+		{  0,  7, 10,  8,  0, },
+		{  6, 10,  0,  0,  0, },
+	},
+	{ // DCT(7/5)
+		{  0,  0,  6,  0,  0, },
+		{  0,  6,  8,  7,  0, },
+		{  6,  8,  0,  0,  0, },
+	},
+	{ // DCT(7/6)
+		{  0,  0,  6,  0,  0, },
+		{  0,  7, 10,  8,  0, },
+		{  6, 10,  0,  0,  0, },
+	},
+	{ // DCT(6/7)
+		{  0,  0,  6,  0,  0, },
+		{  0,  7, 10,  8,  0, },
+		{  6, 10,  0,  0,  0, },
+	},
+	{ // DCT(7/7)
+		{  0,  0,  6,  0,  0, },
+		{  0,  7, 10,  8,  0, },
+		{  6,  9,  0,  0,  0, },
+	},
+};
+*/
+
+// tresholds (size of component) for configuration sets
+static const unsigned int conf_sets[][3] =
+{
+	{ 76800, 19200, 19200 }, // 2480x1920
+	{ 19200,  4800,  4800 }, // 1280x960
+	{ 10800,  2700,  2700 }, // 960x720
+	{  4800,  1200,  1200 }, // 640x480
+	{  1200,   300,   300 }, // 320x240
+	{     0,     0,     0 }  // 0x0
+};
+
+// configuration sets for number of segments
+static const unsigned char conf_segm[][3] =
+{
+	{ 10, 10, 10 },
+	{ 10, 10, 10 },
+	{ 10, 10, 10 },
+	{ 10, 10, 10 },
+	{ 10, 10, 10 },
+	{ 10, 10, 10 }
+};
+
+// configuration sets for noise thresholds
+static const unsigned char conf_ntrs[][3] =
+{
+	{  7,  7,  7 },
+	{  6,  6,  6 },
+	{  5,  5,  5 },
+	{  5,  5,  5 },
+	{  4,  4,  4 },
+	{  4,  4,  4 }
+};
+
+
+// standard huffman tables, found in JPEG specification, Chapter K.3
+static const unsigned char std_huff_tables[4][272] =
+{
+	{	// standard luma dc table (0/0)
+		0x00,0x01,0x05,0x01,0x01,0x01,0x01,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+		0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B
+	},
+	{	// standard chroma dc table (0/1)
+		0x00,0x03,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x00,0x00,0x00,0x00,0x00,
+		0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B
+	},
+	{	// standard luma ac table (1/0)
+		0x00,0x02,0x01,0x03,0x03,0x02,0x04,0x03,0x05,0x05,0x04,0x04,0x00,0x00,0x01,0x7D,
+		0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07,
+		0x22,0x71,0x14,0x32,0x81,0x91,0xA1,0x08,0x23,0x42,0xB1,0xC1,0x15,0x52,0xD1,0xF0,
+		0x24,0x33,0x62,0x72,0x82,0x09,0x0A,0x16,0x17,0x18,0x19,0x1A,0x25,0x26,0x27,0x28,
+		0x29,0x2A,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x43,0x44,0x45,0x46,0x47,0x48,0x49,
+		0x4A,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0x63,0x64,0x65,0x66,0x67,0x68,0x69,
+		0x6A,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7A,0x83,0x84,0x85,0x86,0x87,0x88,0x89,
+		0x8A,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,
+		0xA8,0xA9,0xAA,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xC2,0xC3,0xC4,0xC5,
+		0xC6,0xC7,0xC8,0xC9,0xCA,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xE1,0xE2,
+		0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,
+		0xF9,0xFA
+	},
+	{	// standard chroma ac table (1/1)
+		0x00,0x02,0x01,0x02,0x04,0x04,0x03,0x04,0x07,0x05,0x04,0x04,0x00,0x01,0x02,0x77,
+		0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71,
+		0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91,0xA1,0xB1,0xC1,0x09,0x23,0x33,0x52,0xF0,
+		0x15,0x62,0x72,0xD1,0x0A,0x16,0x24,0x34,0xE1,0x25,0xF1,0x17,0x18,0x19,0x1A,0x26,
+		0x27,0x28,0x29,0x2A,0x35,0x36,0x37,0x38,0x39,0x3A,0x43,0x44,0x45,0x46,0x47,0x48,
+		0x49,0x4A,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0x63,0x64,0x65,0x66,0x67,0x68,
+		0x69,0x6A,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7A,0x82,0x83,0x84,0x85,0x86,0x87,
+		0x88,0x89,0x8A,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0xA2,0xA3,0xA4,0xA5,
+		0xA6,0xA7,0xA8,0xA9,0xAA,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xC2,0xC3,
+		0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,
+		0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,
+		0xF9,0xFA
+	}
+};
+
+// lengths of standard huffmann tables
+static const unsigned char std_huff_lengths[ 4 ] =	{ 28, 28, 178, 178 };
+
+
+// precalculated bit lengths for values 0...1024
+int pbitlen_0_1024[] =
+{
+	 0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, // 32
+	 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, // 64
+	 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 96
+	 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 128
+	 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, // 160
+	 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, // 192
+	 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, // 224
+	 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, // 256
+	 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 288
+	 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 320
+	 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 352
+	 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 384
+	 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 416
+	 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 448
+	 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 480
+	 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 512
+	10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, // 544
+	10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, // 576
+	10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, // 608
+	10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, // 640
+	10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, // 672
+	10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, // 704
+	10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, // 736
+	10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, // 768
+	10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, // 800
+	10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, // 832
+	10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, // 864
+	10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, // 896
+	10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, // 928
+	10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, // 960
+	10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, // 992
+	10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, // 1024
+	11
+};
+
+// precalculated bit lengths for values -2048...2047
+int pbitlen_n2048_2047[] =
+{
+	12,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, // -2016
+	11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, // -1984
+	11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, // -1952
+	11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, // -1920
+	11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, // -1888
+	11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, // -1856
+	11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, // -1824
+	11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, // -1792
+	11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, // -1760
+	11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, // -1728
+	11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, // -1696
+	11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, // -1664
+	11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, // -1632
+	11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, // -1600
+	11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, // -1568
+	11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, // -1536
+	11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, // -1504
+	11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, // -1472
+	11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, // -1440
+	11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, // -1408
+	11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, // -1376
+	11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, // -1344
+	11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, // -1312
+	11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, // -1280
+	11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, // -1248
+	11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, // -1216
+	11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, // -1184
+	11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, // -1152
+	11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, // -1120
+	11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, // -1088
+	11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, // -1056
+	11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, // -1024
+	11,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, // -992
+	10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, // -960
+	10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, // -928
+	10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, // -896
+	10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, // -864
+	10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, // -832
+	10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, // -800
+	10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, // -768
+	10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, // -736
+	10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, // -704
+	10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, // -672
+	10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, // -640
+	10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, // -608
+	10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, // -576
+	10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, // -544
+	10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, // -512
+	10, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // -480
+	 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // -448
+	 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // -416
+	 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // -384
+	 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // -352
+	 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // -320
+	 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // -288
+	 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // -256
+	 9, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, // -224
+	 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, // -192
+	 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, // -160
+	 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, // -128
+	 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // -96
+	 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // -64
+	 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, // -32
+	 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 2, 2, 1, // 0
+	 0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, // 32
+	 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, // 64
+	 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 96
+	 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 128
+	 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, // 160
+	 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, // 192
+	 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, // 224
+	 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, // 256
+	 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 288
+	 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 320
+	 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 352
+	 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 384
+	 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 416
+	 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 448
+	 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 480
+	 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 512
+	10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, // 544
+	10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, // 576
+	10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, // 608
+	10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, // 640
+	10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, // 672
+	10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, // 704
+	10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, // 736
+	10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, // 768
+	10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, // 800
+	10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, // 832
+	10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, // 864
+	10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, // 896
+	10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, // 928
+	10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, // 960
+	10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, // 992
+	10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, // 1024
+	11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, // 1056
+	11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, // 1088
+	11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, // 1120
+	11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, // 1152
+	11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, // 1184
+	11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, // 1216
+	11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, // 1248
+	11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, // 1280
+	11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, // 1312
+	11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, // 1344
+	11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, // 1376
+	11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, // 1408
+	11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, // 1440
+	11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, // 1472
+	11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, // 1504
+	11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, // 1536
+	11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, // 1568
+	11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, // 1600
+	11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, // 1632
+	11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, // 1664
+	11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, // 1696
+	11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, // 1728
+	11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, // 1760
+	11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, // 1792
+	11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, // 1824
+	11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, // 1856
+	11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, // 1888
+	11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, // 1920
+	11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, // 1952
+	11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, // 1984
+	11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, // 2016
+	11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11  // 2048
+};
+
+
+// precalculated segmentation settings (the 0th setting corresponds to 1 segments)
+unsigned char segm_tables[ 49 ][ 50 ] =
+{
+	{  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 
+	   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, }, // s0
+	{  0,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1, 
+	   1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1, }, // s1
+	{  0,  1,  1,  1,  1,  1,  1,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2, 
+	   2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2, }, // s2
+	{  0,  1,  1,  1,  2,  2,  2,  2,  2,  2,  2,  2,  2,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3, 
+	   3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3, }, // s3
+	{  0,  1,  1,  2,  2,  2,  2,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  4,  4,  4,  4,  4,  4, 
+	   4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4, }, // s4
+	{  0,  1,  2,  2,  2,  3,  3,  3,  3,  3,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  5,  5,  5, 
+	   5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5, }, // s5
+	{  0,  1,  2,  2,  3,  3,  3,  4,  4,  4,  4,  4,  4,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5, 
+	   5,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6, }, // s6
+	{  0,  1,  2,  3,  3,  4,  4,  4,  4,  5,  5,  5,  5,  5,  5,  5,  6,  6,  6,  6,  6,  6,  6,  6,  6, 
+	   6,  6,  6,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7, }, // s7
+	{  0,  1,  2,  3,  4,  4,  4,  5,  5,  5,  5,  6,  6,  6,  6,  6,  6,  6,  6,  7,  7,  7,  7,  7,  7, 
+	   7,  7,  7,  7,  7,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8, }, // s8
+	{  0,  1,  2,  3,  4,  4,  5,  5,  5,  6,  6,  6,  6,  7,  7,  7,  7,  7,  7,  7,  7,  8,  8,  8,  8, 
+	   8,  8,  8,  8,  8,  8,  8,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9, }, // s9
+	{  0,  1,  2,  3,  4,  5,  5,  6,  6,  6,  7,  7,  7,  7,  7,  8,  8,  8,  8,  8,  8,  8,  9,  9,  9, 
+	   9,  9,  9,  9,  9,  9,  9,  9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, }, // s10
+	{  0,  1,  2,  3,  4,  5,  6,  6,  7,  7,  7,  7,  8,  8,  8,  8,  8,  9,  9,  9,  9,  9,  9,  9, 10, 
+	  10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, }, // s11
+	{  0,  1,  2,  3,  4,  5,  6,  7,  7,  7,  8,  8,  8,  9,  9,  9,  9,  9,  9, 10, 10, 10, 10, 10, 10, 
+	  10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, }, // s12
+	{  0,  1,  2,  3,  4,  5,  6,  7,  8,  8,  8,  9,  9,  9,  9, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 
+	  11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, }, // s13
+	{  0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  9,  9, 10, 10, 10, 10, 11, 11, 11, 11, 11, 12, 12, 12, 12, 
+	  12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, }, // s14
+	{  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 10, 10, 11, 11, 11, 11, 12, 12, 12, 12, 12, 13, 13, 13, 
+	  13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, }, // s15
+	{  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 11, 11, 11, 12, 12, 12, 12, 13, 13, 13, 13, 13, 14, 
+	  14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, }, // s16
+	{  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14, 14, 
+	  15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, }, // s17
+	{  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 
+	  15, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, }, // s18
+	{  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 
+	  16, 16, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, }, // s19
+	{  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 15, 15, 15, 16, 16, 16, 17, 17, 17, 
+	  17, 17, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, }, // s20
+	{  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 16, 16, 17, 17, 17, 17, 18, 18, 
+	  18, 18, 18, 19, 19, 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 21, 21, 21, 21, 21, 21, 21, 21, 21, }, // s21
+	{  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 17, 17, 18, 18, 18, 18, 19, 
+	  19, 19, 19, 19, 20, 20, 20, 20, 20, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, 22, }, // s22
+	{  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 18, 18, 19, 19, 19, 19, 
+	  20, 20, 20, 20, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, 23, }, // s23
+	{  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 19, 19, 20, 20, 20, 
+	  20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, }, // s24
+	{  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 20, 21, 21, 21, 
+	  21, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, }, // s25
+	{  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 21, 22, 22, 
+	  22, 22, 23, 23, 23, 23, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, }, // s26
+	{  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 22, 23, 
+	  23, 23, 23, 24, 24, 24, 24, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, }, // s27
+	{  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 
+	  24, 24, 24, 25, 25, 25, 25, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, }, // s28
+	{  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 
+	  25, 25, 25, 25, 26, 26, 26, 26, 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, }, // s29
+	{  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 
+	  25, 26, 26, 26, 27, 27, 27, 27, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 30, 30, 30, 30, 30, 30, 30, }, // s30
+	{  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 
+	  25, 26, 27, 27, 27, 28, 28, 28, 28, 29, 29, 29, 29, 30, 30, 30, 30, 30, 31, 31, 31, 31, 31, 31, 31, }, // s31
+	{  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 
+	  25, 26, 27, 28, 28, 29, 29, 29, 29, 30, 30, 30, 30, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, }, // s32
+	{  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 
+	  25, 26, 27, 28, 29, 29, 30, 30, 30, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, }, // s33
+	{  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 
+	  25, 26, 27, 28, 29, 30, 31, 31, 31, 31, 32, 32, 32, 32, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, 34, }, // s34
+	{  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 
+	  25, 26, 27, 28, 29, 30, 31, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, }, // s35
+	{  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 
+	  25, 26, 27, 28, 29, 30, 31, 32, 33, 33, 34, 34, 34, 34, 35, 35, 35, 35, 35, 36, 36, 36, 36, 36, 36, }, // s36
+	{  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 
+	  25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 34, 35, 35, 35, 35, 36, 36, 36, 36, 37, 37, 37, 37, 37, 37, }, // s37
+	{  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 
+	  25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 36, 36, 36, 37, 37, 37, 37, 38, 38, 38, 38, 38, 38, }, // s38
+	{  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 
+	  25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 37, 37, 38, 38, 38, 38, 39, 39, 39, 39, 39, 39, }, // s39
+	{  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 
+	  25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 38, 39, 39, 39, 39, 40, 40, 40, 40, 40, 40, }, // s40
+	{  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 
+	  25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 39, 40, 40, 40, 40, 41, 41, 41, 41, 41, }, // s41
+	{  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 
+	  25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 41, 41, 41, 42, 42, 42, 42, 42, }, // s42
+	{  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 
+	  25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 42, 42, 43, 43, 43, 43, 43, }, // s43
+	{  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 
+	  25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 43, 44, 44, 44, 44, 44, }, // s44
+	{  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 
+	  25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 45, 45, 45, 45, }, // s45
+	{  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 
+	  25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 46, 46, 46, }, // s46
+	{  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 
+	  25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 47, 47, }, // s47
+	{  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 
+	  25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 48, }, // s48
+};
+
+
+/*
+// old stuff starting here - no more used, therefore commented out
+
+// zagzig scan, can be used instead of zigzag scan
+static const unsigned char zagscan[] =
+{
+	 0,  2,  1,  5,  4,  3,  9,  8,
+	 7,  6, 14, 13, 12, 11, 10, 20,
+	19,	18, 17, 16, 15, 27, 26, 25,
+	24, 23, 22, 21, 35, 34, 33, 32,
+	31, 30, 29, 28, 42, 41, 40, 39,
+	38, 37, 36, 48, 47, 46, 45, 44,
+	43, 53, 52, 51, 50, 49, 57, 56,
+	55, 54, 60, 59, 58, 62, 61, 63
+};
+
+// distance scan, can be used instead of zigzag scan
+static const unsigned char distscan[] =
+{
+	 0,  2,  1,  4,  3,  5,  7,  8,
+	12,  6,  9, 11, 13, 17, 18, 10,
+	14,	16, 19, 24, 25, 23, 20, 32,
+	31, 15, 26, 22, 33, 30, 39, 38,
+	40, 27, 21, 34, 29, 41, 37, 46,
+	45, 44, 47, 35, 28, 51, 36, 42,
+	52, 50, 48, 43, 53, 49, 56, 55,
+	54, 57, 59, 60, 58, 62, 61, 63
+};
+
+// diagonal / horizontal / vertical scan, don't use this unless you want bad results
+static const unsigned char dhvscan[] =
+{
+	 0,  4, 12, 24, 39, 51, 59, 63,
+	 1,  5,  6,  7, 13, 14, 15, 16,
+	17, 25, 26, 27, 28, 29, 30, 31,
+	40, 41, 42, 43, 44, 45, 52, 53,
+	54, 55, 60, 61,
+	 2,  3,  8,  9, 10, 11, 18, 19,
+	20, 21, 22, 23, 32, 33, 34, 35,
+	36, 37, 38, 46, 47, 48, 49, 50,
+	56, 57, 58, 62
+};
+
+// sign relevancy scan
+static const unsigned char sgnscan[] =
+{
+	 0,  1,  2,  3,  5,  6,  9, 10,
+	14, 15, 20, 21, 27, 28, 35,  4,
+	 7, 13, 16, 26, 29, 42,  8,	12,
+	17, 25, 30, 41, 43,	11, 18, 24,
+	31, 40, 44, 53, 19, 23, 32, 39,
+	45, 52, 54,	22, 33, 38, 46, 51,
+	55, 60, 34, 37, 47, 50, 56, 59,
+	61, 36, 48, 49, 57, 58, 62, 63
+};
+
+// even/uneven zigzag scan reverse conversion table
+static const int even_natural_order[] =
+{
+
+	 0,  8,  9,  3,  1, 16,  2, 10, 
+	12, 26, 40, 41, 19, 33, 48, 34, 
+	35, 49, 57, 43, 42, 56, 50, 36, 
+	58, 52, 38, 39, 59, 45, 31, 46, 
+	17, 32, 18,  4, 24, 25, 11,  5, 
+	27, 13,  7, 21, 20,  6, 14, 28, 
+	29, 15, 30, 44, 22, 23, 37, 51, 
+	53, 61, 47, 62, 60, 54, 55, 63, 
+};
+
+// scans for each frequency 
+static const char freqalign[] =
+{
+	'm', 'v', 'v', 'v', 'v', 'v', 'v', 'v',
+	'h', 'm', 'v', 'v', 'v', 'v', 'v', 'v',
+	'h', 'h', 'm', 'v', 'v', 'v', 'v', 'v',
+	'h', 'h', 'h', 'm', 'v', 'v', 'v', 'v',
+	'h', 'h', 'h', 'h', 'm', 'v', 'v', 'v',
+	'h', 'h', 'h', 'h', 'h', 'm', 'v', 'v',
+	'h', 'h', 'h', 'h', 'h', 'h', 'm', 'v',
+	'h', 'h', 'h', 'h', 'h', 'h', 'h', 'm'
+};
+
+// chengjie tu subband classification
+static const unsigned char ctxclass[] =
+{
+	0, 1, 3, 3, 3, 6, 6, 6, // 0 -> DC (DC subband)
+	2, 5, 5, 5, 6, 6, 6, 6, // 1 -> PV (principal vertical)
+	4, 5, 5, 5, 6, 6, 6, 6, // 2 -> PH (principal horizontal)
+	4, 5, 5, 6, 6, 6, 6, 6, // 3 -> LV (low-frequency vertical)
+	4, 6, 6, 6, 6, 6, 6, 6, // 4 -> LH (low-frequency horizontal)
+	6, 6, 6, 6, 6, 6, 6, 6, // 5 -> LD (low-frequency diagonal)
+	6, 6, 6, 6, 6, 6, 6, 6, // 6 -> HP (high-pass)
+	6, 6, 6, 6, 6, 6, 6, 6
+};
+
+// context weighting for subband classification
+static const signed int ctx_weights[ 7 ][ 3 ][ 5 ] =
+{
+	{ // 0 -> DC (DC subband)
+		{  0,  0,  2,  0,  0 },
+		{  0,  3,  4,  3,  0 },
+		{  2,  4,  0,  0,  0 }
+	},
+	{ // 1 -> PV (principal vertical)
+		{  0,  0,  3,  0,  0 },
+		{  0,  0,  6,  0,  0 },
+		{  0,  0,  0,  0,  0 }
+	},
+	{ // 2 -> PH (principal horizontal)
+		{  0,  0,  0,  0,  0 },
+		{  0,  0,  0,  0,  0 },
+		{  3,  6,  0,  0,  0 }
+	},
+	{ // 3 -> LV (low-frequency vertical)
+		{  0,  0,  3,  0,  0 },
+		{  0,  1,  6,  1,  0 },
+		{  0,  1,  0,  0,  0 }
+	},
+	{ // 4 -> LH (low-frequency horizontal)
+		{  0,  0,  0,  0,  0 },
+		{  0,  1,  1,  1,  0 },
+		{  3,  6,  0,  0,  0 }
+	},
+	{ // 5 -> LD (low-frequency diagonal)
+		{  0,  0,  2,  0,  0 },
+		{  0,  3,  4,  3,  0 },
+		{  2,  4,  0,  0,  0 }
+	},
+	{ // 6 -> HP (high-pass)
+		{  0,  0,  2,  0,  0 },
+		{  0,  3,  4,  3,  0 },
+		{  2,  4,  0,  0,  0 }
+	}
+};
+
+// context weighting for each band (from mixedlum27 set)
+static const signed int abs_ctx_weights[ 64 ][ 3 ][ 5 ] =
+{
+	{ // DCT(0/0)
+		{  0,  0,  7,  0,  0, },
+		{  0,  6, 11,  8,  0, },
+		{  9, 13,  0,  0,  0, },
+	},
+	{ // DCT(1/0)
+		{  0,  0,  7,  0,  0, },
+		{  0,  4, 30,  6,  0, },
+		{  5,  6,  0,  0,  0, },
+	},
+	{ // DCT(0/1)
+		{  0,  0,  4,  0,  0, },
+		{  0,  3,  3,  6,  0, },
+		{  7, 35,  0,  0,  0, },
+	},
+	{ // DCT(0/2)
+		{  0,  0,  5,  0,  0, },
+		{  0,  2,  5,  6,  0, },
+		{ 10, 29,  0,  0,  0, },
+	},
+	{ // DCT(1/1)
+		{  0,  0,  4,  0,  0, },
+		{  0,  7, 13, 11,  0, },
+		{  5, 16,  0,  0,  0, },
+	},
+	{ // DCT(2/0)
+		{  0,  0, 10,  0,  0, },
+		{  0,  2, 27,  6,  0, },
+		{  5,  8,  0,  0,  0, },
+	},
+	{ // DCT(3/0)
+		{  0,  0, 11,  0,  0, },
+		{  0,  2, 24,  6,  0, },
+		{  6,  9,  0,  0,  0, },
+	},
+	{ // DCT(2/1)
+		{  0,  0,  5,  0,  0, },
+		{  0,  8, 15, 10,  0, },
+		{  6, 12,  0,  0,  0, },
+	},
+	{ // DCT(1/2)
+		{  0,  0,  6,  0,  0, },
+		{  0,  7,  9, 11,  0, },
+		{  7, 17,  0,  0,  0, },
+	},
+	{ // DCT(0/3)
+		{  0,  0,  6,  0,  0, },
+		{  0,  3,  6,  6,  0, },
+		{ 11, 26,  0,  0,  0, },
+	},
+	{ // DCT(0/4)
+		{  0,  0,  6,  0,  0, },
+		{  0,  3,  6,  7,  0, },
+		{ 11, 24,  0,  0,  0, },
+	},
+	{ // DCT(1/3)
+		{  0,  0,  6,  0,  0, },
+		{  0,  6,  9, 10,  0, },
+		{  8, 17,  0,  0,  0, },
+	},
+	{ // DCT(2/2)
+		{  0,  0,  5,  0,  0, },
+		{  0,  9, 11, 11,  0, },
+		{  7, 13,  0,  0,  0, },
+	},
+	{ // DCT(3/1)
+		{  0,  0,  6,  0,  0, },
+		{  0,  7, 16, 10,  0, },
+		{  7, 11,  0,  0,  0, },
+	},
+	{ // DCT(4/0)
+		{  0,  0, 11,  0,  0, },
+		{  0,  3, 22,  6,  0, },
+		{  7,  9,  0,  0,  0, },
+	},
+	{ // DCT(5/0)
+		{  0,  0, 11,  0,  0, },
+		{  0,  3, 19,  6,  0, },
+		{  7,  9,  0,  0,  0, },
+	},
+	{ // DCT(4/1)
+		{  0,  0,  7,  0,  0, },
+		{  0,  7, 15,  9,  0, },
+		{  8, 11,  0,  0,  0, },
+	},
+	{ // DCT(3/2)
+		{  0,  0,  6,  0,  0, },
+		{  0,  8, 13, 10,  0, },
+		{  7, 12,  0,  0,  0, },
+	},
+	{ // DCT(2/3)
+		{  0,  0,  6,  0,  0, },
+		{  0,  8, 10, 11,  0, },
+		{  7, 14,  0,  0,  0, },
+	},
+	{ // DCT(1/4)
+		{  0,  0,  7,  0,  0, },
+		{  0,  6,  9, 10,  0, },
+		{  8, 16,  0,  0,  0, },
+	},
+	{ // DCT(0/5)
+		{  0,  0,  5,  0,  0, },
+		{  0,  3,  6,  6,  0, },
+		{ 13, 24,  0,  0,  0, },
+	},
+	{ // DCT(0/6)
+		{  0,  0,  5,  0,  0, },
+		{  0,  3,  6,  6,  0, },
+		{ 13, 20,  0,  0,  0, },
+	},
+	{ // DCT(1/5)
+		{  0,  0,  7,  0,  0, },
+		{  0,  7,  9,  9,  0, },
+		{  8, 15,  0,  0,  0, },
+	},
+	{ // DCT(2/4)
+		{  0,  0,  7,  0,  0, },
+		{  0,  8, 10, 10,  0, },
+		{  7, 13,  0,  0,  0, },
+	},
+	{ // DCT(3/3)
+		{  0,  0,  7,  0,  0, },
+		{  0,  9, 10, 11,  0, },
+		{  7, 12,  0,  0,  0, },
+	},
+	{ // DCT(4/2)
+		{  0,  0,  7,  0,  0, },
+		{  0,  8, 12,  9,  0, },
+		{  8, 11,  0,  0,  0, },
+	},
+	{ // DCT(5/1)
+		{  0,  0,  7,  0,  0, },
+		{  0,  7, 13,  9,  0, },
+		{  8, 10,  0,  0,  0, },
+	},
+	{ // DCT(6/0)
+		{  0,  0, 12,  0,  0, },
+		{  0,  3, 18,  5,  0, },
+		{  5,  7,  0,  0,  0, },
+	},
+	{ // DCT(7/0)
+		{  0,  0, 13,  0,  0, },
+		{  0,  3, 19,  3,  0, },
+		{  5,  6,  0,  0,  0, },
+	},
+	{ // DCT(6/1)
+		{  0,  0,  7,  0,  0, },
+		{  0,  6, 11,  8,  0, },
+		{  7,  9,  0,  0,  0, },
+	},
+	{ // DCT(5/2)
+		{  0,  0,  7,  0,  0, },
+		{  0,  7, 11,  8,  0, },
+		{  7, 10,  0,  0,  0, },
+	},
+	{ // DCT(4/3)
+		{  0,  0,  6,  0,  0, },
+		{  0,  8, 10, 10,  0, },
+		{  7, 11,  0,  0,  0, },
+	},
+	{ // DCT(3/4)
+		{  0,  0,  7,  0,  0, },
+		{  0,  8, 10, 10,  0, },
+		{  7, 11,  0,  0,  0, },
+	},
+	{ // DCT(2/5)
+		{  0,  0,  6,  0,  0, },
+		{  0,  7,  9,  9,  0, },
+		{  8, 12,  0,  0,  0, },
+	},
+	{ // DCT(1/6)
+		{  0,  0,  6,  0,  0, },
+		{  0,  6,  8,  8,  0, },
+		{  8, 12,  0,  0,  0, },
+	},
+	{ // DCT(0/7)
+		{  0,  0,  3,  0,  0, },
+		{  0,  1,  4,  4,  0, },
+		{ 17, 24,  0,  0,  0, },
+	},
+	{ // DCT(1/7)
+		{  0,  0,  5,  0,  0, },
+		{  0,  5,  7,  7,  0, },
+		{  6, 11,  0,  0,  0, },
+	},
+	{ // DCT(2/6)
+		{  0,  0,  6,  0,  0, },
+		{  0,  7,  8,  7,  0, },
+		{  7, 10,  0,  0,  0, },
+	},
+	{ // DCT(3/5)
+		{  0,  0,  6,  0,  0, },
+		{  0,  7,  9,  8,  0, },
+		{  7, 11,  0,  0,  0, },
+	},
+	{ // DCT(4/4)
+		{  0,  0,  6,  0,  0, },
+		{  0,  7,  9,  9,  0, },
+		{  7, 10,  0,  0,  0, },
+	},
+	{ // DCT(5/3)
+		{  0,  0,  6,  0,  0, },
+		{  0,  7,  9,  8,  0, },
+		{  7,  9,  0,  0,  0, },
+	},
+	{ // DCT(6/2)
+		{  0,  0,  7,  0,  0, },
+		{  0,  6,  9,  7,  0, },
+		{  6,  9,  0,  0,  0, },
+	},
+	{ // DCT(7/1)
+		{  0,  0,  8,  0,  0, },
+		{  0,  7, 11,  8,  0, },
+		{  8,  9,  0,  0,  0, },
+	},
+	{ // DCT(7/2)
+		{  0,  0,  7,  0,  0, },
+		{  0,  7,  9,  8,  0, },
+		{  8, 10,  0,  0,  0, },
+	},
+	{ // DCT(6/3)
+		{  0,  0,  5,  0,  0, },
+		{  0,  6,  9,  7,  0, },
+		{  6,  8,  0,  0,  0, },
+	},
+	{ // DCT(5/4)
+		{  0,  0,  6,  0,  0, },
+		{  0,  7,  8,  8,  0, },
+		{  6,  8,  0,  0,  0, },
+	},
+	{ // DCT(4/5)
+		{  0,  0,  5,  0,  0, },
+		{  0,  7,  9,  8,  0, },
+		{  6,  9,  0,  0,  0, },
+	},
+	{ // DCT(3/6)
+		{  0,  0,  6,  0,  0, },
+		{  0,  7,  7,  7,  0, },
+		{  6, 10,  0,  0,  0, },
+	},
+	{ // DCT(2/7)
+		{  0,  0,  5,  0,  0, },
+		{  0,  6,  6,  6,  0, },
+		{  6, 10,  0,  0,  0, },
+	},
+	{ // DCT(3/7)
+		{  0,  0,  5,  0,  0, },
+		{  0,  6,  6,  6,  0, },
+		{  6,  9,  0,  0,  0, },
+	},
+	{ // DCT(4/6)
+		{  0,  0,  6,  0,  0, },
+		{  0,  5,  7,  7,  0, },
+		{  6,  8,  0,  0,  0, },
+	},
+	{ // DCT(5/5)
+		{  0,  0,  6,  0,  0, },
+		{  0,  6,  7,  7,  0, },
+		{  6,  8,  0,  0,  0, },
+	},
+	{ // DCT(6/4)
+		{  0,  0,  5,  0,  0, },
+		{  0,  5,  7,  7,  0, },
+		{  6,  8,  0,  0,  0, },
+	},
+	{ // DCT(7/3)
+		{  0,  0,  6,  0,  0, },
+		{  0,  6,  9,  7,  0, },
+		{  7,  8,  0,  0,  0, },
+	},
+	{ // DCT(7/4)
+		{  0,  0,  6,  0,  0, },
+		{  0,  5,  7,  6,  0, },
+		{  7,  8,  0,  0,  0, },
+	},
+	{ // DCT(6/5)
+		{  0,  0,  5,  0,  0, },
+		{  0,  5,  6,  6,  0, },
+		{  6,  6,  0,  0,  0, },
+	},
+	{ // DCT(5/6)
+		{  0,  0,  5,  0,  0, },
+		{  0,  5,  6,  6,  0, },
+		{  5,  8,  0,  0,  0, },
+	},
+	{ // DCT(4/7)
+		{  0,  0,  5,  0,  0, },
+		{  0,  5,  7,  5,  0, },
+		{  7,  7,  0,  0,  0, },
+	},
+	{ // DCT(5/7)
+		{  0,  0,  7,  0,  0, },
+		{  0,  3,  7,  6,  0, },
+		{  6,  7,  0,  0,  0, },
+	},
+	{ // DCT(6/6)
+		{  0,  0,  5,  0,  0, },
+		{  0,  4,  5,  6,  0, },
+		{  5,  6,  0,  0,  0, },
+	},
+	{ // DCT(7/5)
+		{  0,  0,  5,  0,  0, },
+		{  0,  4,  7,  5,  0, },
+		{  7,  8,  0,  0,  0, },
+	},
+	{ // DCT(7/6)
+		{  0,  0,  5,  0,  0, },
+		{  0,  4,  7,  5,  0, },
+		{  5,  6,  0,  0,  0, },
+	},
+	{ // DCT(6/7)
+		{  0,  0,  6,  0,  0, },
+		{  0,  6,  6,  6,  0, },
+		{  6,  7,  0,  0,  0, },
+	},
+	{ // DCT(7/7)
+		{  0,  0, 10,  0,  0, },
+		{  0,  4,  8,  5,  0, },
+		{ 11,  8,  0,  0,  0, },
+	},
+};
+*/
