commit 59c738ae74207f1eaf7153d0e3edefaa69ba83ab
Author: david.sorber <david.sorber@gmail.com>
Date:   Sat Nov 1 22:01:25 2025 -0400

    Modernize packJPG CMakeLists.txt. Remove Boost dependency.

diff --git a/software/libpackjpg/lib_src/CMakeLists.txt b/software/libpackjpg/lib_src/CMakeLists.txt
index 2c2a382..d9f94ab 100644
--- a/software/libpackjpg/lib_src/CMakeLists.txt
+++ b/software/libpackjpg/lib_src/CMakeLists.txt
@@ -1,36 +1,182 @@
-project("packJPG")
-cmake_minimum_required(VERSION 3.2)
+CMAKE_MINIMUM_REQUIRED(VERSION 3.16)
+PROJECT("packJPG")
 
-set(CMAKE_CXX_STANDARD 14)
+################################################################################
+# Check GCC version
+################################################################################
+IF ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
+    # Require at least gcc 10.3 for C++20 support
+    IF (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 10.3)
+        MESSAGE(FATAL_ERROR "GCC version must be at least 10.3!")
+    ENDIF()
+ENDIF()
 
-include_directories(..)
+# This is a neat trick to prevent in source builds
+IF (CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR)
+    MESSAGE(FATAL_ERROR "Prevented in-tree build. This is bad practice; you "
+            "should be running cmake from inside a build directory.")
+ENDIF()
 
-set(packjpg_sources
-    ../aricoder.cpp
-    ../bitops.cpp
-    ../packjpg.cpp
+
+################################################################################
+# Build configuration options
+################################################################################
+
+IF (NOT CMAKE_BUILD_TYPE)
+    SET(CMAKE_BUILD_TYPE ReleaseWithDebug CACHE STRING
+            "Choose the type of build, options are: None Debug ReceiverRelease ReleaseWithDebug."
+            FORCE )
+ENDIF()
+
+MESSAGE(STATUS "Build Type: " ${CMAKE_BUILD_TYPE})
+
+ADD_CUSTOM_TARGET(Debug
+        COMMAND ${CMAKE_COMMAND} -DCMAKE_BUILD_TYPE=Debug ${CMAKE_SOURCE_DIR}
+        COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target all
+        COMMENT "Switch CMAKE_BUILD_TYPE to Debug"
+)
+
+ADD_CUSTOM_TARGET(ReceiverRelease
+        COMMAND ${CMAKE_COMMAND} -DCMAKE_BUILD_TYPE=LibRelease ${CMAKE_SOURCE_DIR}
+        COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target all
+        COMMENT "Switch CMAKE_BUILD_TYPE to ReceiverRelease"
+)
+
+ADD_CUSTOM_TARGET(ReleaseWithDebug
+        COMMAND ${CMAKE_COMMAND} -DCMAKE_BUILD_TYPE=ReleaseWithDebug ${CMAKE_SOURCE_DIR}
+        COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target all
+        COMMENT "Switch CMAKE_BUILD_TYPE to ReleaseWithDebug"
+)
+
+
+# Use /etc/os-release to determine Linux flavor and version
+EXECUTE_PROCESS(COMMAND bash -c "grep \"^NAME=\" /etc/os-release | cut -c7- | rev | cut -c2- | rev"
+        OUTPUT_VARIABLE DISTRIB_ID
+        OUTPUT_STRIP_TRAILING_WHITESPACE
+)
+
+EXECUTE_PROCESS(COMMAND bash -c "grep \"^VERSION_ID=\" /etc/os-release | cut -c13- | rev | cut -c2- | rev"
+        OUTPUT_VARIABLE DISTRIB_RELEASE
+        OUTPUT_STRIP_TRAILING_WHITESPACE
+)
+
+# Output the parsed ID and release version
+MESSAGE(STATUS "DISTRIB_ID:      " ${DISTRIB_ID})
+MESSAGE(STATUS "DISTRIB_RELEASE: " ${DISTRIB_RELEASE})
+
+# Do not let CMake add the build directory path to RPATH. This means that CL/SL
+# cannot be run from the build directory and must be properly installed.
+SET(CMAKE_SKIP_BUILD_RPATH  TRUE)
+
+# Uncomment this line to enable AddressSanitizer
+# SET(CMAKE_CXX_FLAGS "-fsanitize=address -fno-omit-frame-pointer")
+
+# Detect presence of mold linker and use it if found
+IF (EXISTS "/usr/bin/mold")
+    MESSAGE(STATUS "Using mold linker!")
+    # Note this only works on gcc >= 12.1
+    SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fuse-ld=mold")
+    SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fuse-ld=mold")
+ELSE()
+    MESSAGE(WARNING "Mold linker not detected. Consider installing mold (add BL APT repo: "
+            "https://athena.blacklynx.tech/apt/): sudo apt install mold")
+ENDIF()
+
+#
+# Set the compile flags
+#
+SET(CMAKE_CXX_STANDARD 20)
+SET(CMAKE_CXX_STANDARD_REQUIRED ON)
+SET(CMAKE_CXX_EXTENSIONS OFF)
+# SET(CMAKE_CXX_VISIBILITY_PRESET hidden)
+
+# This length is used along with the __FILE__ macro for logging
+STRING(LENGTH ${CMAKE_CURRENT_SOURCE_DIR} BPLEN)
+ADD_DEFINITIONS("-DBUILD_PATH_LEN=${BPLEN}")
+
+# Ensure symbols from statically linked libraries are hidden
+SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wl,--exclude-libs=ALL")
+
+# Enable warnings
+SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wno-unused-parameter")
+SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wno-unused-parameter")
+
+IF (CMAKE_BUILD_TYPE STREQUAL "LibRelease")
+
+    # Tune for Haswell minimum
+    SET(COMPILE_FLAGS "-O3 -flto=auto -ffast-math -ffp-contract=fast -fno-verbose-asm -march=skylake -mfma -mavx2")
+
+    SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COMPILE_FLAGS}")
+    SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COMPILE_FLAGS}")
+
+ELSEIF (CMAKE_BUILD_TYPE STREQUAL "ReleaseWithDebug")
+
+    # Tune for Haswell minimum
+    #    SET(COMPILE_FLAGS "-g -O3 -flto=auto -march=haswell -mtune=generic -mavx2")
+    SET(COMPILE_FLAGS "-g -O3 -flto=auto -ffast-math -ffp-contract=fast -fno-verbose-asm -march=skylake -mfma -mavx2")
+
+    SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COMPILE_FLAGS}")
+    SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COMPILE_FLAGS}")
+
+ELSEIF (CMAKE_BUILD_TYPE STREQUAL "Debug")
+
+    SET(COMPILE_FLAGS "-g -O0 -march=skylake -mtune=generic")
+
+    SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COMPILE_FLAGS}")
+    SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COMPILE_FLAGS}")
+
+ENDIF()
+
+# Set the library install path depending on the Linux flavor
+SET(LIBRARY_INSTALL_PATH "")
+IF ((DISTRIB_ID STREQUAL "Ubuntu") OR (DISTRIB_ID STREQUAL "Linux Mint"))
+    SET(LIBRARY_INSTALL_PATH "/usr/lib/x86_64-linux-gnu/")
+ELSE()
+    # CentOS/RedHat/Scientific
+    SET(LIBRARY_INSTALL_PATH "/usr/lib64/")
+ENDIF()
+
+IF(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
+    set(CMAKE_INSTALL_PREFIX "/" CACHE PATH "..." FORCE)
+ENDIF()
+
+
+################################################################################
+# Library Dependencies
+################################################################################
+SET(EXTERNAL_LIBS)
+
+
+################################################################################
+# Source
+################################################################################
+INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/src)
+INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/external)
+
+SET(packjpg_sources
+    ${CMAKE_CURRENT_SOURCE_DIR}/src/aricoder.cpp
+    ${CMAKE_CURRENT_SOURCE_DIR}/src/bitops.cpp
+    ${CMAKE_CURRENT_SOURCE_DIR}/src/packjpg.cpp
 )
 
-add_definitions("-std=c++14 -O3 -Wall -pedantic")
-add_definitions("-funroll-loops -ffast-math -fomit-frame-pointer")
-add_definitions("-march=native -fuse-ld=gold -flto")
 
-add_library(packjpg SHARED
-    ${packjpg_sources})
+################################################################################
+# Target: shared library
+################################################################################
+ADD_LIBRARY(packjpg SHARED ${packjpg_sources})
 
-install(TARGETS packjpg 
+INSTALL(TARGETS packjpg
         #~ LIBRARY DESTINATION "/usr/lib/x86_64-linux-gnu/")
         LIBRARY DESTINATION "/opt/local/lib/")
         
-install(FILES "packjpg.h"
+INSTALL(FILES "packjpg.h"
         DESTINATION "/opt/local/include/")
 
-# packjpg_simple executable
-find_package(Boost 1.54.0       
-             REQUIRED 
-             COMPONENTS filesystem)  
 
-add_executable(packjpg_simple
-                ../bin/packjpg_simple.cc)
+################################################################################
+# Target: simple utility
+################################################################################
+ADD_EXECUTABLE(packjpg_simple
+    ${CMAKE_CURRENT_SOURCE_DIR}/src/bin/packjpg_simple.cc)
                 
-target_link_libraries(packjpg_simple ${Boost_LIBRARIES} packjpg stdc++fs)
+TARGET_LINK_LIBRARIES(packjpg_simple packjpg )
diff --git a/software/libpackjpg/lib_src/src/aricoder.cpp b/software/libpackjpg/lib_src/src/aricoder.cpp
new file mode 100644
index 0000000..95dc8ee
--- /dev/null
+++ b/software/libpackjpg/lib_src/src/aricoder.cpp
@@ -0,0 +1,820 @@
+#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/libpackjpg/lib_src/src/aricoder.h b/software/libpackjpg/lib_src/src/aricoder.h
new file mode 100644
index 0000000..604e64e
--- /dev/null
+++ b/software/libpackjpg/lib_src/src/aricoder.h
@@ -0,0 +1,397 @@
+#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/libpackjpg/lib_src/src/bin/packjpg_simple.cc b/software/libpackjpg/lib_src/src/bin/packjpg_simple.cc
new file mode 100644
index 0000000..3b5d46f
--- /dev/null
+++ b/software/libpackjpg/lib_src/src/bin/packjpg_simple.cc
@@ -0,0 +1,111 @@
+#include <cstdint>
+#include <cstring>
+#include <filesystem>
+#include <fstream>
+#include <iostream>
+#include <string>
+#include <vector>
+
+
+#include "../packjpg.h"
+
+namespace bfs = std::filesystem;
+
+const std::string JPG_EXTENSION(".jpg");
+const std::string PJG_EXTENSION(".pjg");
+
+int main(int argc, char** argv)
+{
+    std::cout << "packJPG Simple Test Utility" << std::endl;
+    std::cout << "  [" << packJPG::pjglib_version_info() << "]\n" << std::endl;    
+    
+    // Verify that input was provided
+    if (argc < 2)
+    {
+        std::cerr << "ERROR: please provide an input file\n" << std::endl;
+        return -1;
+    }
+    char* file_path = argv[1];
+    
+    // Verify that input is a path to a file
+    if (! bfs::is_regular_file(file_path))
+    {
+        std::cerr << "ERROR: \"" << file_path << "\" is not a file\n" << std::endl;
+        return -1;
+    }    
+    
+    uint32_t file_size = bfs::file_size(file_path);
+    std::cout << "File size:      " << file_size << std::endl;
+    auto file_buffer = new std::vector<unsigned char>(file_size);
+    
+    // Read in the input file
+    std::ifstream input_stream(file_path, std::ios::binary | std::ios::in);
+    if (! input_stream)
+    {
+        std::cerr << "ERROR: unable to read from \"" << file_path << "\"\n" 
+                  << std::endl;
+        return -1;
+    }
+    
+    input_stream.read((char*)file_buffer->data(), file_size);
+    input_stream.close();
+    
+    // Determine input, and therefore output, filetype / extension
+    const std::string* output_file_extension = nullptr;
+    if ((file_buffer->at(0) == 0xFF) && (file_buffer->at(1) == 0xD8))
+    {
+        output_file_extension = &PJG_EXTENSION;
+    }
+    else if ((file_buffer->at(0) == packJPG::pjg_magic[0]) && 
+             (file_buffer->at(1) == packJPG::pjg_magic[1]))
+    {
+        output_file_extension = &JPG_EXTENSION;
+    }
+    else
+    {
+        std::cerr << "Input file does not appear to be valid\n" << std::endl;
+        return -1;
+    }
+    
+    // Setup packJPG instance
+    packJPG* instance = new packJPG();    
+    instance->pjglib_init_streams(file_buffer->data(), 1, file_size, nullptr, 1);
+    
+    // Do compression/decompression
+    unsigned char* out_buffer = nullptr;
+    uint32_t out_size = 0;
+    char message[MSG_SIZE];
+    std::memset(message, 0, MSG_SIZE);
+    
+    bool rc = instance->pjglib_convert_stream2mem(&out_buffer, &out_size, message);
+    if (!rc)
+    {
+        std::cout << "An error occurred during the compression/decompression "
+                  << "operation: " << message << "\n" << std::endl;
+        return -1;
+    }
+    
+    std::cout << "Status message: " << message << std::endl;
+    std::cout << "Output size:    " << out_size << std::endl;
+    
+    bfs::path out_path = bfs::path(file_path).replace_extension(*output_file_extension);
+    std::cout << "Output file:    " << out_path << std::endl;
+    
+    // Write output file
+    std::ofstream output_stream(out_path.string(), std::ios::binary | std::ios::out);
+    if (! output_stream)
+    {
+        std::cerr << "ERROR: unable to read from \"" << file_path << "\"" 
+                  << std::endl;
+        return -1;
+    }
+    output_stream.write((const char*)out_buffer, out_size);
+    output_stream.close();
+    
+    // Clean up
+    std::free(out_buffer);
+    delete instance;
+    delete file_buffer;
+    
+    return 0;
+}
diff --git a/software/libpackjpg/lib_src/src/bitops.cpp b/software/libpackjpg/lib_src/src/bitops.cpp
new file mode 100644
index 0000000..0e4039c
--- /dev/null
+++ b/software/libpackjpg/lib_src/src/bitops.cpp
@@ -0,0 +1,785 @@
+/*
+This file contains special classes for bitwise
+reading and writing of arrays
+*/
+
+#include "bitops.h"
+
+#include <algorithm>
+#include <array>
+#include <cstdio>
+#include <cstdlib>
+#include <filesystem>
+#include <fstream>
+#include <stdexcept>
+
+#if defined(_WIN32) || defined(WIN32)
+#include <fcntl.h>
+#include <io.h>
+#endif
+
+namespace sfs = std::filesystem;
+
+
+/* -----------------------------------------------
+    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 sfs::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/libpackjpg/lib_src/src/bitops.h b/software/libpackjpg/lib_src/src/bitops.h
new file mode 100644
index 0000000..a1f2840
--- /dev/null
+++ b/software/libpackjpg/lib_src/src/bitops.h
@@ -0,0 +1,320 @@
+#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/libpackjpg/lib_src/src/dct8x8.h b/software/libpackjpg/lib_src/src/dct8x8.h
new file mode 100644
index 0000000..3cfdddb
--- /dev/null
+++ b/software/libpackjpg/lib_src/src/dct8x8.h
@@ -0,0 +1,1174 @@
+#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/libpackjpg/lib_src/src/packjpg.cpp b/software/libpackjpg/lib_src/src/packjpg.cpp
new file mode 100644
index 0000000..48d1320
--- /dev/null
+++ b/software/libpackjpg/lib_src/src/packjpg.cpp
@@ -0,0 +1,6103 @@
+/*
+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"
+
+#include "packjpg.h"
+
+
+#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)
+
+
+// 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 == nullptr)
+    {
+        free(ptr);
+    }
+    return n_ptr;
+}
+
+// Initialize static members
+const unsigned char packJPG::appversion = 25;
+const char* packJPG::subversion   = "k";
+const char* packJPG::apptitle     = "packJPG";
+const char* packJPG::appname      = "packjpg";
+const char* packJPG::versiondate  = "01/22/2016";
+const char* packJPG::author       = "Matthias Stirner / Se";
+const char  packJPG::pjg_magic[] = { 'J', 'S' };
+
+
+packJPG::packJPG()
+    : lib_in_type(-1),
+      lib_out_type(-1),
+      grbgdata(nullptr),
+      hdrdata(nullptr),
+      huffdata(nullptr),
+      hufs(0),
+      hdrs(0),
+      grbs(0),
+      rstp(nullptr),
+      scnp(nullptr),
+      rstc(0),
+      scnc(0),
+      rsti(0),
+      padbit(-1),
+      rst_err(nullptr),
+      zdstdata(),
+      eobxhigh(),
+      eobyhigh(),
+      zdstxlow(),
+      zdstylow(),
+      colldata(),
+      freqscan(),
+      zsrtscan(),
+      adpt_idct_8x8(),
+      adpt_idct_1x8(),
+      adpt_idct_8x1(),
+      cmpnfo(),
+      cmpc(0),
+      imgwidth(0),
+      imgheight(0),
+      sfhm(0),
+      sfvm(0),
+      mcuv(0),
+      mcuh(0),
+      mcuc(0),
+      cs_cmpc(0),
+      cs_cmp(),
+      cs_from(0),
+      cs_to(0),
+      cs_sah(0),
+      cs_sal(0),
+      jpgfilename(nullptr),
+      pjgfilename(nullptr),
+      jpgfilesize(0),
+      pjgfilesize(0),
+      jpegtype(0),
+      filetype(0),
+      //~ str_in, // input stream
+      //~ str_out,    // output stream
+      errormessage(),
+      errorfunction(nullptr),
+      errorlevel(0),
+      err_tol(1),
+      disc_meta(false),
+      auto_set(true),
+      action(A_COMPRESS),
+      nois_trs{6, 6, 6, 6},
+      segm_cnt{10, 10, 10, 10}
+{
+}
+    
+packJPG::~packJPG()
+{
+}
+
+const char* packJPG::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;
+}
+
+const char* packJPG::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;
+}
+
+/* ------------------- Begin of library only functions --------------------- */
+
+/* -----------------------------------------------
+    DLL export converter function
+    ----------------------------------------------- */
+bool packJPG::pjglib_convert_stream2stream(char* msg)
+{
+    // process in main function
+    return pjglib_convert_stream2mem(nullptr, nullptr, msg);
+}
+
+/* -----------------------------------------------
+    DLL export converter function
+    ----------------------------------------------- */
+bool packJPG::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(nullptr, nullptr, msg);
+}
+
+/* -----------------------------------------------
+    DLL export converter function
+    ----------------------------------------------- */
+bool packJPG::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 != nullptr) && (out_size != nullptr))
+    {
+        *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 != nullptr)
+        {
+            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 != nullptr)
+    {
+        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;
+}
+
+/* -----------------------------------------------
+    DLL export init input (file/mem)
+    ----------------------------------------------- */
+void packJPG::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 = nullptr;
+    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;
+}
+
+/* -------------------- End of libary only functions ----------------------- */
+
+/* ----------------- Begin of main interface functions --------------------- */
+
+/* -----------------------------------------------
+    processes one file
+    ----------------------------------------------- */
+void packJPG::process_file(void)
+{
+    if (filetype == F_JPG)
+    {
+        switch (action)
+        {
+            case A_COMPRESS:
+                read_jpeg();
+                decode_jpeg();
+                check_value_range();
+                adapt_icos();
+                predict_dc();
+                calc_zdst_lists();
+                pack_pjg();
+                break;
+
+            default:
+                break;
+        }
+    }
+    else if (filetype == F_PJG)
+    {
+        switch (action)
+        {
+            case A_COMPRESS:
+                unpack_pjg();
+                adapt_icos();
+                unpredict_dc();
+                recode_jpeg();
+                merge_jpeg();
+                break;
+
+            default:
+                break;
+        }
+    }
+    
+    // reset buffers
+    reset_buffers();
+}
+
+/* -----------------------------------------------
+    main-function execution routine
+    ----------------------------------------------- */
+void packJPG::execute(bool (*function)())
+{
+    if (errorlevel < err_tol)
+    {
+        // call function
+        (*function)();
+
+        // store errorfunction if needed
+        if ((errorlevel > 0) && (errorfunction == nullptr))
+        {
+            errorfunction = function;
+        }
+    }
+}
+
+/* ----------------------- End of main interface functions ----------------- */
+
+
+/* ----------------------- Begin of main functions ------------------------- */
+
+/* -----------------------------------------------
+    set each variable to its initial value
+    ----------------------------------------------- */
+bool packJPG::reset_buffers(void)
+{
+    int cmp, bpos;
+    int i;
+
+    // -- free buffers --
+
+    // free buffers & set pointers nullptr
+    if (hdrdata  != nullptr)
+    {
+        free(hdrdata);
+    }
+    if (huffdata != nullptr)
+    {
+        free(huffdata);
+    }
+    if (grbgdata != nullptr)
+    {
+        free(grbgdata);
+    }
+    if (rst_err  != nullptr)
+    {
+        free(rst_err);
+    }
+    if (rstp     != nullptr)
+    {
+        free(rstp);
+    }
+    if (scnp     != nullptr)
+    {
+        free(scnp);
+    }
+    hdrdata   = nullptr;
+    huffdata  = nullptr;
+    grbgdata  = nullptr;
+    rst_err   = nullptr;
+    rstp      = nullptr;
+    scnp      = nullptr;
+
+    // free image arrays
+    for (cmp = 0; cmp < 4; cmp++)
+    {
+        if (zdstdata[cmp] != nullptr)
+        {
+            free(zdstdata[cmp]);
+        }
+        if (eobxhigh[cmp] != nullptr)
+        {
+            free(eobxhigh[cmp]);
+        }
+        if (eobyhigh[cmp] != nullptr)
+        {
+            free(eobyhigh[cmp]);
+        }
+        if (zdstxlow[cmp] != nullptr)
+        {
+            free(zdstxlow[cmp]);
+        }
+        if (zdstylow[cmp] != nullptr)
+        {
+            free(zdstylow[cmp]);
+        }
+        zdstdata[cmp] = nullptr;
+        eobxhigh[cmp] = nullptr;
+        eobyhigh[cmp] = nullptr;
+        zdstxlow[cmp] = nullptr;
+        zdstylow[cmp] = nullptr;
+        freqscan[cmp] = (unsigned char*) stdscan;
+
+        for (bpos = 0; bpos < 64; bpos++)
+        {
+            if (colldata[cmp][bpos] != nullptr)
+            {
+                free(colldata[cmp][bpos]);
+            }
+            colldata[cmp][bpos] = nullptr;
+        }
+    }
+
+    // -- 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 = nullptr;
+        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
+    ----------------------------------------------- */
+bool packJPG::read_jpeg(void)
+{
+    unsigned char* segment = nullptr; // 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 == nullptr)
+    {
+        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 == nullptr)
+                            {
+                                rst_err = (unsigned char*) calloc(scnc + 1, sizeof(char));
+                                if (rst_err == nullptr)
+                                {
+                                    sprintf(errormessage, MEM_ERRMSG);
+                                    errorlevel = 2;
+                                    return false;
+                                }
+                            }
+                        }
+                        if (rst_err != nullptr)
+                        {
+                            // realloc and set only if needed
+                            rst_err = (unsigned char*) frealloc(rst_err, (scnc + 1) * sizeof(char));
+                            if (rst_err == nullptr)
+                            {
+                                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 == nullptr)
+            {
+                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
+    ----------------------------------------------- */
+bool packJPG::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 != nullptr)
+            {
+                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 != nullptr)
+        {
+            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
+    ----------------------------------------------- */
+bool packJPG::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 (((jpegtype == 1 || ((cs_cmpc > 1 || cs_to == 0) && cs_sah == 0)) 
+                 && htset[0][cmpnfo[cmp].huffdc] == 0) || 
+			   (jpegtype == 1 && htset[1][cmpnfo[cmp].huffdc] == 0) ||
+			   (cs_cmpc == 1 && cs_to > 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
+    ----------------------------------------------- */
+bool packJPG::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 == nullptr)
+        {
+            scnp = (unsigned int*) calloc(scnc + 2, sizeof(int));
+        }
+        else
+        {
+            scnp = (unsigned int*) frealloc(scnp, (scnc + 2) * sizeof(int));
+        }
+        if (scnp == nullptr)
+        {
+            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 == nullptr)
+            {
+                rstp = (unsigned int*) calloc(tmp + 1, sizeof(int));
+            }
+            else
+            {
+                rstp = (unsigned int*) frealloc(rstp, (tmp + 1) * sizeof(int));
+            }
+            if (rstp == nullptr)
+            {
+                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 != nullptr)
+    {
+        rstp[rstc] = hufs;
+    }
+
+    return true;
+}
+
+/* -----------------------------------------------
+    adapt ICOS tables for quantizer tables
+    ----------------------------------------------- */
+bool packJPG::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
+    ----------------------------------------------- */
+bool packJPG::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
+    ----------------------------------------------- */
+bool packJPG::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
+    ----------------------------------------------- */
+bool packJPG::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
+    ----------------------------------------------- */
+bool packJPG::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
+    ----------------------------------------------- */
+bool packJPG::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 == nullptr) ? 0 : 1))
+    {
+        return false;
+    }
+    // encode # of false set RST markers per scan
+    if (rst_err != nullptr)
+        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
+    ----------------------------------------------- */
+bool packJPG::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, nullptr))
+        {
+            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
+    ----------------------------------------------- */
+bool packJPG::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 == nullptr) ||
+                (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] == nullptr)
+            {
+                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] == nullptr) ||
+                (eobxhigh[cmp] == nullptr) || (eobyhigh[cmp] == nullptr) ||
+                (zdstxlow[cmp] == nullptr) || (zdstylow[cmp] == nullptr))
+        {
+            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
+    ----------------------------------------------- */
+bool packJPG::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
+    ----------------------------------------------- */
+bool packJPG::jpg_rebuild_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
+
+    // start headerwriter
+    MemoryWriter* hdrw = new MemoryWriter(); // new header writer
+
+    // 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
+    ----------------------------------------------- */
+int packJPG::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
+    ----------------------------------------------- */
+int packJPG::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
+    ----------------------------------------------- */
+int packJPG::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
+    ----------------------------------------------- */
+int packJPG::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
+    ----------------------------------------------- */
+int packJPG::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
+    ----------------------------------------------- */
+int packJPG::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
+    ----------------------------------------------- */
+int packJPG::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
+    ----------------------------------------------- */
+int packJPG::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
+    ----------------------------------------------- */
+int packJPG::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
+    ----------------------------------------------- */
+int packJPG::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
+    ----------------------------------------------- */
+int packJPG::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
+    ----------------------------------------------- */
+int packJPG::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
+    ----------------------------------------------- */
+int packJPG::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)
+    ----------------------------------------------- */
+int packJPG::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
+    ----------------------------------------------- */
+int packJPG::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)
+    ----------------------------------------------- */
+int packJPG::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
+    ----------------------------------------------- */
+int packJPG::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
+    ----------------------------------------------- */
+void packJPG::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 ---------------------- */
+
+
+/* ------------------- Begin PJG specific functions ------------------------ */
+
+/* -----------------------------------------------
+    encodes frequency scanorder to pjg
+    ----------------------------------------------- */
+bool packJPG::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)
+    ----------------------------------------------- */
+bool packJPG::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)
+    ----------------------------------------------- */
+bool packJPG::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
+    ----------------------------------------------- */
+bool packJPG::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 == nullptr)
+    {
+        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
+    ----------------------------------------------- */
+bool packJPG::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 == nullptr) || (sgn_store == nullptr) || (zdstls == nullptr))
+    {
+        if (absv_store != nullptr)
+        {
+            free(absv_store);
+        }
+        if (sgn_store != nullptr)
+        {
+            free(sgn_store);
+        }
+        if (zdstls != nullptr)
+        {
+            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
+    ----------------------------------------------- */
+bool packJPG::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
+    ----------------------------------------------- */
+bool packJPG::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
+    ----------------------------------------------- */
+bool packJPG::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
+    ----------------------------------------------- */
+bool packJPG::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)
+    ----------------------------------------------- */
+bool packJPG::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)
+    ----------------------------------------------- */
+bool packJPG::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
+    ----------------------------------------------- */
+bool packJPG::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 == nullptr)
+    {
+        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
+    ----------------------------------------------- */
+bool packJPG::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 == nullptr) || (sgn_store == nullptr) || (zdstls == nullptr))
+    {
+        if (absv_store != nullptr)
+        {
+            free(absv_store);
+        }
+        if (sgn_store != nullptr)
+        {
+            free(sgn_store);
+        }
+        if (zdstls != nullptr)
+        {
+            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
+    ----------------------------------------------- */
+bool packJPG::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
+    ----------------------------------------------- */
+bool packJPG::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 != nullptr)
+    {
+        (*len) = bwrt->num_bytes_written();
+    }
+    delete bwrt;
+
+    return true;
+}
+
+/* -----------------------------------------------
+    decodes one bit from pjg
+    ----------------------------------------------- */
+bool packJPG::pjg_decode_bit(ArithmeticDecoder* dec, unsigned char* bit)
+{
+    model_b* model = INIT_MODEL_B(1, -1);
+    (*bit) = decode_ari(dec, model);
+    delete model;
+
+    return true;
+}
+
+/* -----------------------------------------------
+    get zero sort frequency scan vector
+    ----------------------------------------------- */
+void packJPG::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
+    ----------------------------------------------- */
+bool packJPG::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
+    ----------------------------------------------- */
+bool packJPG::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
+    ----------------------------------------------- */
+void packJPG::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
+    ----------------------------------------------- */
+int packJPG::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
+    ----------------------------------------------- */
+int packJPG::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
+    ----------------------------------------------- */
+void packJPG::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 of DCT specific functions --------------------- */
+
+/* -----------------------------------------------
+    inverse DCT transform using precalc tables (fast)
+    ----------------------------------------------- */
+int packJPG::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)
+    ----------------------------------------------- */
+int packJPG::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)
+int packJPG::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)
+int packJPG::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
diff --git a/software/libpackjpg/lib_src/src/packjpg.h b/software/libpackjpg/lib_src/src/packjpg.h
new file mode 100644
index 0000000..6cbf920
--- /dev/null
+++ b/software/libpackjpg/lib_src/src/packjpg.h
@@ -0,0 +1,543 @@
+#ifndef PACKJPG_H
+#define PACKJPG_H
+
+#include <filesystem>
+#include <memory>
+
+#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
+
+namespace sfs = std::filesystem;
+
+// Forward declarations -- real includes are in the source file so we can use 
+//                         this header externally
+class ArithmeticDecoder;
+class ArithmeticEncoder;
+class BitReader;
+class BitWriter;
+class Reader;
+class Writer;
+
+/* -----------------------------------------------
+    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];
+};
+
+class packJPG
+{
+public:
+    
+    packJPG();
+    
+    ~packJPG();
+    
+    static const char* pjglib_version_info(void);
+    static const char* pjglib_short_name(void);
+
+    /* -----------------------------------------------
+    checks if a file exists
+    ----------------------------------------------- */
+    inline bool file_exists(const char* filename)
+    {
+        return sfs::exists(filename);
+    }
+    
+    /**************************************************************************
+     * Library functions
+     *************************************************************************/ 
+     
+    bool pjglib_convert_stream2stream(char* msg);
+    
+    bool pjglib_convert_file2file(char* in, char* out, char* msg);
+    
+    bool pjglib_convert_stream2mem(
+        unsigned char** out_file, 
+        unsigned int* out_size, 
+        char* msg);
+        
+    void pjglib_init_streams(
+        void* in_src, 
+        int in_type, 
+        int in_size, 
+        void* out_dest, 
+        int out_type);
+        
+    /**************************************************************************
+     * Main interface functions
+     *************************************************************************/
+     
+    void process_file(void);
+    
+    void execute(bool (*function)());
+    
+    /**************************************************************************
+     * Main functions
+     *************************************************************************/    
+     
+    bool reset_buffers(void);
+    
+    bool read_jpeg(void);
+    
+    bool merge_jpeg(void);
+    
+    bool decode_jpeg(void);
+    
+    bool recode_jpeg(void);
+    
+    bool adapt_icos(void);
+    
+    bool predict_dc(void);
+    
+    bool unpredict_dc(void);
+    
+    bool check_value_range(void);
+    
+    bool calc_zdst_lists(void);
+    
+    bool pack_pjg(void);
+    
+    bool unpack_pjg(void);
+    
+    /* -----------------------------------------------
+    global variables: info about program
+    ----------------------------------------------- */
+    static const unsigned char appversion;
+    static const char*  subversion;
+    static const char*  apptitle;
+    static const char*  appname;
+    static const char*  versiondate;
+    static const char*  author;
+    static const char   pjg_magic[2];
+    
+private:
+
+    /**************************************************************************
+     * JPEG specific functions
+     *************************************************************************/ 
+     
+    bool jpg_setup_imginfo(void);
+    
+    bool jpg_parse_jfif(
+        unsigned char type, 
+        unsigned int len, 
+        unsigned char* segment);
+        
+    bool jpg_rebuild_header(void);
+    
+    int jpg_decode_block_seq(
+        BitReader* huffr, 
+        huffTree* dctree, 
+        huffTree* actree, 
+        short* block);
+        
+    int jpg_encode_block_seq(
+        BitWriter* huffw, 
+        huffCodes* dctbl, 
+        huffCodes* actbl, 
+        short* block);
+
+    int jpg_decode_dc_prg_fs(
+        BitReader* huffr, 
+        huffTree* dctree, 
+        short* block);
+        
+    int jpg_encode_dc_prg_fs(
+        BitWriter* huffw, 
+        huffCodes* dctbl, 
+        short* block);
+        
+    int jpg_decode_ac_prg_fs(
+        BitReader* huffr, 
+        huffTree* actree, 
+        short* block, 
+        int* eobrun, 
+        int from, 
+        int to);
+        
+    int jpg_encode_ac_prg_fs(
+        BitWriter* huffw, 
+        huffCodes* actbl, 
+        short* block, 
+        int* eobrun, 
+        int from, 
+        int to);
+
+    int jpg_decode_dc_prg_sa(BitReader* huffr, short* block);
+    
+    int jpg_encode_dc_prg_sa(BitWriter* huffw, short* block);
+    
+    int jpg_decode_ac_prg_sa(
+        BitReader* huffr, 
+        huffTree* actree, 
+        short* block, 
+        int* eobrun, 
+        int from, 
+        int to);
+        
+    int jpg_encode_ac_prg_sa(
+        BitWriter* huffw, 
+        std::vector<std::uint8_t>& storw, 
+        huffCodes* actbl, 
+        short* block, 
+        int* eobrun, 
+        int from, 
+        int to);
+        
+    int jpg_decode_eobrun_sa(
+        BitReader* huffr, 
+        short* block, 
+        int* eobrun, 
+        int from, 
+        int to);
+        
+    int jpg_encode_eobrun(
+        BitWriter* huffw, 
+        huffCodes* actbl, 
+        int* eobrun);
+        
+    int jpg_encode_crbits(
+        BitWriter* huffw, 
+        std::vector<std::uint8_t>& storw);
+        
+    int jpg_next_huffcode(BitReader* huffw, huffTree* ctree);
+    
+    int jpg_next_mcupos(
+        int* mcu, 
+        int* cmp, 
+        int* csc, 
+        int* sub, 
+        int* dpos, 
+        int* rstw);
+        
+    int jpg_next_mcuposn(int* cmp, int* dpos, int* rstw);
+    
+    int jpg_skip_eobrun(int* cmp, int* dpos, int* rstw, int* eobrun);
+    
+    void jpg_build_huffcodes(
+        unsigned char* clen, 
+        unsigned char* cval, 
+        huffCodes* hc, 
+        huffTree* ht);
+        
+    /**************************************************************************
+     * PJG specific functions
+     *************************************************************************/ 
+    
+    bool pjg_encode_zstscan(ArithmeticEncoder* enc, int cmp);
+    
+    bool pjg_encode_zdst_high(ArithmeticEncoder* enc, int cmp);
+    
+    bool pjg_encode_zdst_low(ArithmeticEncoder* enc, int cmp);
+    
+    bool pjg_encode_dc(ArithmeticEncoder* enc, int cmp);
+    
+    bool pjg_encode_ac_high(ArithmeticEncoder* enc, int cmp);
+    
+    bool pjg_encode_ac_low(ArithmeticEncoder* enc, int cmp);
+    
+    bool pjg_encode_generic(
+        ArithmeticEncoder* enc, 
+        unsigned char* data, 
+        int len);
+    
+    bool pjg_encode_bit(ArithmeticEncoder* enc, unsigned char bit);
+    
+    bool pjg_decode_zstscan(ArithmeticDecoder* dec, int cmp);
+    
+    bool pjg_decode_zdst_high(ArithmeticDecoder* dec, int cmp);
+    
+    bool pjg_decode_zdst_low(ArithmeticDecoder* dec, int cmp);
+    
+    bool pjg_decode_dc(ArithmeticDecoder* dec, int cmp);
+    
+    bool pjg_decode_ac_high(ArithmeticDecoder* dec, int cmp);
+    
+    bool pjg_decode_ac_low(ArithmeticDecoder* dec, int cmp);
+    
+    bool pjg_decode_generic(
+        ArithmeticDecoder* dec, 
+        unsigned char** data, 
+        int* len);
+        
+    bool pjg_decode_bit(ArithmeticDecoder* dec, unsigned char* bit);
+    
+    void pjg_get_zerosort_scan(unsigned char* sv, int cmp);
+    
+    bool pjg_optimize_header(void);
+    
+    bool pjg_unoptimize_header(void);
+    
+    void pjg_aavrg_prepare(
+        unsigned short** abs_coeffs, 
+        int* weights, 
+        unsigned short* abs_store, 
+        int cmp);
+
+    int pjg_aavrg_context(
+        unsigned short** abs_coeffs, 
+        int* weights, 
+        int pos, 
+        int p_y, 
+        int p_x, 
+        int r_x);
+        
+    int pjg_lakh_context(
+        signed short** coeffs_x, 
+        signed short** coeffs_a, 
+        int* pred_cf, 
+        int pos);
+
+    void get_context_nnb(int pos, int w, int* a, int* b);
+    
+    /**************************************************************************
+     * DCT functions
+     *************************************************************************/ 
+
+    int idct_2d_fst_8x1(int cmp, int dpos, int ix, int iy);
+    
+    int idct_2d_fst_1x8(int cmp, int dpos, int ix, int iy);
+
+    /**************************************************************************
+     * Prediction functions
+     *************************************************************************/ 
+
+    int dc_coll_predictor(int cmp, int dpos);
+    
+    int dc_1ddct_predictor(int cmp, int dpos);
+    
+    /* -----------------------------------------------
+    loco-i predictor
+    ----------------------------------------------- */
+    inline int plocoi(int a, int b, int c)
+    {
+        // a -> left; b -> above; c -> above-left
+
+        int min = (a < b) ? a : b;
+        int 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
+    ----------------------------------------------- */
+    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
+    ----------------------------------------------- */
+    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]);
+        }
+    }
+
+    /**************************************************************************
+     * Member variables
+     *************************************************************************/ 
+    /* -----------------------------------------------
+    global variables: library only variables
+    ----------------------------------------------- */
+    int lib_in_type;
+    int lib_out_type;
+    
+    /* -----------------------------------------------
+    global variables: data storage
+    ----------------------------------------------- */
+    unsigned short qtables[4][64];  // quantization tables
+    huffCodes      hcodes[2][4];    // huffman codes
+    huffTree       htrees[2][4];    // huffman decoding trees
+    unsigned char  htset[2][4];     // 1 if huffman table is set
+    
+    unsigned char* grbgdata;    // garbage data
+    unsigned char* hdrdata ;    // header data
+    unsigned char* huffdata;    // huffman coded data
+    int            hufs;        // size of huffman data
+    int            hdrs;        // size of header
+    int            grbs;        // size of garbage
+    
+    unsigned int*  rstp;        // restart markers positions in huffdata
+    unsigned int*  scnp;        // scan start positions in huffdata
+    int            rstc;        // count of restart markers
+    int            scnc;        // count of scans
+    int            rsti;        // restart interval
+    char           padbit;      // padbit (for huffman coding)
+    unsigned char* rst_err;     // number of wrong-set RST markers per scan
+    
+    unsigned char* zdstdata[4]; // zero distribution (# of non-zeroes) lists (for higher 7x7 block)
+    unsigned char* eobxhigh[4]; // eob in x direction (for higher 7x7 block)
+    unsigned char* eobyhigh[4]; // eob in y direction (for higher 7x7 block)
+    unsigned char* zdstxlow[4]; // # of non zeroes for first row
+    unsigned char* zdstylow[4]; // # of non zeroes for first collumn
+    signed short*  colldata[4][64];     // collection sorted DCT coefficients
+    
+    unsigned char* freqscan[4];         // optimized order for frequency scans (only pointers to scans)
+    unsigned char  zsrtscan[4][64];      // zero optimized frequency scan
+    
+    int adpt_idct_8x8[4][8 * 8 * 8 * 8];    // precalculated/adapted values for idct (8x8)
+    int adpt_idct_1x8[4][1 * 1 * 8 * 8];    // precalculated/adapted values for idct (1x8)
+    int adpt_idct_8x1[4][8 * 8 * 1 * 1];    // precalculated/adapted values for idct (8x1)
+    
+    /* -----------------------------------------------
+    global variables: info about image
+    ----------------------------------------------- */
+    componentInfo cmpnfo[4]; // seperate info for each color component
+    
+    int cmpc;       // component count
+    int imgwidth;   // width of image
+    int imgheight;  // height of image
+    
+    int sfhm;       // max horizontal sample factor
+    int sfvm;       // max verical sample factor
+    int mcuv;       // mcus per line
+    int mcuh;       // mcus per collumn
+    int mcuc;       // count of mcus
+    
+    /* -----------------------------------------------
+    global variables: info about current scan
+    ----------------------------------------------- */
+    int cs_cmpc;    // component count in current scan
+    int cs_cmp[4];  // component numbers  in current scan
+    int cs_from;    // begin - band of current scan ( inclusive )
+    int cs_to;      // end - band of current scan ( inclusive )
+    int cs_sah;     // successive approximation bit pos high
+    int cs_sal;     // successive approximation bit pos low
+    
+    /* -----------------------------------------------
+    global variables: info about files
+    ----------------------------------------------- */
+    char*  jpgfilename;         // name of JPEG file
+    char*  pjgfilename;         // name of PJG file
+    int    jpgfilesize;         // size of JPEG file
+    int    pjgfilesize;         // size of PJG file
+    int    jpegtype;            // type of JPEG coding: 0->unknown, 1->sequential, 2->progressive
+    int    filetype;            // type of current file
+    std::unique_ptr<Reader> str_in;     // input stream
+    std::unique_ptr<Writer> str_out;    // output stream
+    
+    /* -----------------------------------------------
+    global variables: messages
+    ----------------------------------------------- */
+    char errormessage[MSG_SIZE];
+    bool (*errorfunction)();
+    
+    // meaning of errorlevel:
+    //  -1 -> wrong input
+    //   0 -> no error
+    //   1 -> warning
+    //   2 -> fatal error
+    int  errorlevel;
+    
+    /* -----------------------------------------------
+    global variables: settings
+    ----------------------------------------------- */
+    int  err_tol;   // error threshold ( proceed on warnings yes (2) / no (1) )
+    bool disc_meta; // discard meta-info yes / no
+    bool auto_set;  // automatic find best settings yes/no
+    int  action;    // what to do with JPEG/PJG files
+    
+    unsigned char nois_trs[4];  // bit pattern noise threshold
+    unsigned char segm_cnt[4];  // number of segments
+};
+
+
+#endif
diff --git a/software/libpackjpg/lib_src/src/pjpgtbl.h b/software/libpackjpg/lib_src/src/pjpgtbl.h
new file mode 100644
index 0000000..b4c8ccd
--- /dev/null
+++ b/software/libpackjpg/lib_src/src/pjpgtbl.h
@@ -0,0 +1,1825 @@
+/* -----------------------------------------------
+    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, },
+    },
+};
+*/
