/*
    Zlib – библиотека сжатия данных общего назначения. Версия 1.1.0
    Это изменённая объектно-ориентированная версия библиотеки, полностью
    совместимая с оригинальной библиотекой.
    
    Copyright © 1995–2005 Jean-loup Gailly и Mark Adler
    Copyright © 2000–2011 ymnk, JCraft, Inc.
    Copyright © 2016–2017, 2019–2022 Малик Разработчик
    
    Эта библиотека поставляется «как есть», без каких-либо явных или
    подразумеваемых гарантий. Ни при каких обстоятельствах авторы не
    несут какой-либо ответственности в случае потери данных вследствие
    использования данной библиотеки.
    
    Разрешается всем использовать эту библиотеку для любых целей, в том
    числе и для коммерческих приложений, а также изменять её и
    распространять свободно при соблюдении следующих условий:
    
        1. Оригинал библиотеки не должен быть искажён; вы не должны
    заявлять, что именно вы написали оригинальную библиотеку. Если вы
    используете эту библиотеку в своём программном продукте, то ссылка
    на авторов библиотеки была бы желательна, но это не является
    обязательным требованием.
    
        2. Изменённые версии исходных текстов должны быть отчётливо
    маркированы и не должны выдаваться за оригинал библиотеки.
    
        3. Эти замечания не могут быть удалены либо изменены при
    каком-либо варианте распространения исходных текстов.
*/

package malik.emulator.compression.zlib;

import java.io.*;

public class ZStream extends Zlib
{
    int dataType;
    int nextInIndex;
    int availIn;
    int nextOutIndex;
    int availOut;
    long totalIn;
    long totalOut;
    byte[] nextIn;
    byte[] nextOut;
    Deflate dstate;
    Inflate istate;
    Checksum32 adler;
    String message;

    public ZStream() {
        this.adler = new Adler32();
    }

    public void free() {
        nextIn = null;
        nextOut = null;
        message = null;
    }

    public void setAvailableIn(int availableIn) {
        this.availIn = availableIn;
    }

    public void setAvailableOut(int availableOut) {
        this.availOut = availableOut;
    }

    public void setNextIn(byte[] nextIn) {
        this.nextIn = nextIn;
    }

    public void setNextInIndex(int nextInIndex) {
        this.nextInIndex = nextInIndex;
    }

    public void setNextOut(byte[] nextOut) {
        this.nextOut = nextOut;
    }

    public void setNextOutIndex(int nextOutIndex) {
        this.nextOutIndex = nextOutIndex;
    }

    public void setInput(byte[] src) {
        setInput(src, 0, src == null ? 0 : src.length, false);
    }

    public void setInput(byte[] src, boolean append) {
        setInput(src, 0, src == null ? 0 : src.length, append);
    }

    public void setInput(byte[] src, int offset, int count, boolean append) {
        byte[] tmp;
        if(count <= 0 && append && nextIn != null) return;
        if(availIn > 0 && append)
        {
            tmp = new byte[availIn + count];
            Array.copy(nextIn, nextInIndex, tmp, 0, availIn);
            Array.copy(src, offset, tmp, availIn, count);
            nextIn = tmp;
            nextInIndex = 0;
            availIn += count;
        } else
        {
            nextIn = src;
            nextInIndex = offset;
            availIn = count;
        }
    }

    public void setOutput(byte[] dst) {
        setOutput(dst, 0, dst == null ? 0 : dst.length);
    }

    public void setOutput(byte[] dst, int offset, int length) {
        nextOut = dst;
        nextOutIndex = offset;
        availOut = length;
    }

    public boolean inflateFinished() {
        return istate != null && istate.mode == 12;
    }

    public int inflateInit() {
        return inflateInit(DEF_WBITS, false);
    }

    public int inflateInit(boolean nowrap) {
        return inflateInit(DEF_WBITS, nowrap);
    }

    public int inflateInit(int wbits) {
        return inflateInit(wbits, false);
    }

    public int inflateInit(int wbits, boolean nowrap) {
        return (istate = new Inflate(this)).inflateInit(nowrap ? -wbits : wbits);
    }

    public int inflateSync() {
        return istate == null ? STREAM_ERROR : istate.inflateSync();
    }

    public int inflateSyncPoint() {
        return istate == null ? STREAM_ERROR : istate.inflateSyncPoint();
    }

    public int inflateSetDictionary(byte[] dictionary, int dictLength) {
        return istate == null ? STREAM_ERROR : istate.inflateSetDictionary(dictionary, dictLength);
    }

    public int inflate(int flush) {
        return istate == null ? STREAM_ERROR : istate.inflate(flush);
    }

    public int inflateEnd() {
        int result;
        if(istate == null) return STREAM_ERROR;
        result = istate.inflateEnd();
        istate = null;
        return result;
    }

    public int deflateInit(int level) {
        return deflateInit(level, MAX_WBITS, false);
    }

    public int deflateInit(int level, boolean nowrap) {
        return deflateInit(level, MAX_WBITS, nowrap);
    }

    public int deflateInit(int level, int bits) {
        return deflateInit(level, bits, false);
    }

    public int deflateInit(int level, int bits, boolean nowrap) {
        return (dstate = new Deflate(this)).deflateInit(level, nowrap ? -bits : bits);
    }

    public int deflateInit(int level, int bits, int memlevel) {
        return (dstate = new Deflate(this)).deflateInit(level, bits, memlevel);
    }

    public int deflateParams(int level, int strategy) {
        return dstate == null ? STREAM_ERROR : dstate.deflateParams(level, strategy);
    }

    public int deflateSetDictionary(byte[] dictionary, int dictLength) {
        return dstate == null ? STREAM_ERROR : dstate.deflateSetDictionary(dictionary, dictLength);
    }

    public int deflate(int flush) {
        return dstate == null ? STREAM_ERROR : dstate.deflate(flush);
    }

    public int deflateEnd() {
        int result;
        if(dstate == null) return STREAM_ERROR;
        result = dstate.deflateEnd();
        dstate = null;
        return result;
    }

    public int getAdler() {
        return adler.value();
    }

    public int getAvailableIn() {
        return availIn;
    }

    public int getAvailableOut() {
        return availOut;
    }

    public int getNextInIndex() {
        return nextInIndex;
    }

    public int getNextOutIndex() {
        return nextOutIndex;
    }

    public long getTotalIn() {
        return totalIn;
    }

    public long getTotalOut() {
        return totalOut;
    }

    public byte[] getNextIn() {
        return nextIn;
    }

    public byte[] getNextOut() {
        return nextOut;
    }

    public String getMessage() {
        return message;
    }

    final void flushPending() {
        int len = dstate.pending;
        if(len > availOut) len = availOut;
        if(len == 0) return;
        Array.copy(dstate.pendingBuf, dstate.pendingOut, nextOut, nextOutIndex, len);
        nextOutIndex += len;
        dstate.pendingOut += len;
        totalOut += len;
        availOut -= len;
        dstate.pending -= len;
        if(dstate.pending == 0) dstate.pendingOut = 0;
    }

    final int readBuf(byte[] dst, int offset, int count) {
        int length = availIn;
        if(length > count) length = count;
        if(length == 0) return 0;
        availIn -= length;
        if(dstate.wrap != 0) adler.update(nextIn, nextInIndex, length);
        Array.copy(nextIn, nextInIndex, dst, offset, length);
        nextInIndex += length;
        totalIn += length;
        return length;
    }
}

final class Config extends Zlib
{
    public final int goodLength;
    public final int maxLazy;
    public final int niceLength;
    public final int maxChain;
    public final int func;

    public Config(int goodLength, int maxLazy, int niceLength, int maxChain, int func) {
        this.goodLength = goodLength;
        this.maxLazy = maxLazy;
        this.niceLength = niceLength;
        this.maxChain = maxChain;
        this.func = func;
    }
}

final class Deflate extends Zlib
{
    private static final int NEED_MORE = 0;
    private static final int BLOCK_DONE = 1;
    private static final int FINISH_STARTED = 2;
    private static final int FINISH_DONE = 3;
    private static final int PRESET_DICT = 0x20;
    private static final int INIT_STATE = 42;
    private static final int BUSY_STATE = 113;
    private static final int FINISH_STATE = 666;
    private static final int Z_DEFLATED = 8;
    private static final int STORED_BLOCK = 0;
    private static final int STATIC_TREES = 1;
    private static final int DYN_TREES = 2;
    private static final int Z_BINARY = 0;
    private static final int Z_ASCII = 1;
    private static final int Z_UNKNOWN = 2;
    private static final int BUF_SIZE = 8 * 2;
    private static final int REP_3_6 = 16;
    private static final int REPZ_3_10 = 17;
    private static final int REPZ_11_138 = 18;
    private static final int MIN_MATCH = 3;
    private static final int MAX_MATCH = 258;
    private static final int MIN_LOOKAHEAD = MAX_MATCH + MIN_MATCH + 1;
    private static final int MAX_BITS = 15;
    private static final int D_CODES = 30;
    private static final int BL_CODES = 19;
    private static final int LENGTH_CODES = 29;
    private static final int LITERALS = 256;
    private static final int L_CODES = LITERALS + LENGTH_CODES + 1;
    private static final int HEAP_SIZE = 2 * L_CODES + 1;
    private static final int END_BLOCK = 256;
    private static final int MAX_MEM_LEVEL = 9;
    private static final int DEF_MEM_LEVEL = 8;
    private static final int STORED = 0;
    private static final int FAST = 1;
    private static final int SLOW = 2;

    private static final byte[] DIST_CODE;
    private static final byte[] BL_ORDER;
    private static final byte[] LENGTH_CODE;
    private static final int[] BASE_LENGTH;
    private static final int[] BASE_DIST;
    private static final Config[] CONFIG_TABLE;
    private static final String[] ERROR_MESSAGE;

    static {
        DIST_CODE = new byte[] {
                 0,      1,      2,      3,      4,      4,      5,      5,      6,      6,      6,      6,      7,      7,      7,      7,
                 8,      8,      8,      8,      8,      8,      8,      8,      9,      9,      9,      9,      9,      9,      9,      9,
                10,     10,     10,     10,     10,     10,     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,
                12,     12,     12,     12,     12,     12,     12,     12,     12,     12,     12,     12,     12,     12,     12,     12,
                12,     12,     12,     12,     12,     12,     12,     12,     12,     12,     12,     12,     12,     12,     12,     12,
                13,     13,     13,     13,     13,     13,     13,     13,     13,     13,     13,     13,     13,     13,     13,     13,
                13,     13,     13,     13,     13,     13,     13,     13,     13,     13,     13,     13,     13,     13,     13,     13,
                14,     14,     14,     14,     14,     14,     14,     14,     14,     14,     14,     14,     14,     14,     14,     14,
                14,     14,     14,     14,     14,     14,     14,     14,     14,     14,     14,     14,     14,     14,     14,     14,
                14,     14,     14,     14,     14,     14,     14,     14,     14,     14,     14,     14,     14,     14,     14,     14,
                14,     14,     14,     14,     14,     14,     14,     14,     14,     14,     14,     14,     14,     14,     14,     14,
                15,     15,     15,     15,     15,     15,     15,     15,     15,     15,     15,     15,     15,     15,     15,     15,
                15,     15,     15,     15,     15,     15,     15,     15,     15,     15,     15,     15,     15,     15,     15,     15,
                15,     15,     15,     15,     15,     15,     15,     15,     15,     15,     15,     15,     15,     15,     15,     15,
                15,     15,     15,     15,     15,     15,     15,     15,     15,     15,     15,     15,     15,     15,     15,     15,
                 0,      0,     16,     17,     18,     18,     19,     19,     20,     20,     20,     20,     21,     21,     21,     21,
                22,     22,     22,     22,     22,     22,     22,     22,     23,     23,     23,     23,     23,     23,     23,     23,
                24,     24,     24,     24,     24,     24,     24,     24,     24,     24,     24,     24,     24,     24,     24,     24,
                25,     25,     25,     25,     25,     25,     25,     25,     25,     25,     25,     25,     25,     25,     25,     25,
                26,     26,     26,     26,     26,     26,     26,     26,     26,     26,     26,     26,     26,     26,     26,     26,
                26,     26,     26,     26,     26,     26,     26,     26,     26,     26,     26,     26,     26,     26,     26,     26,
                27,     27,     27,     27,     27,     27,     27,     27,     27,     27,     27,     27,     27,     27,     27,     27,
                27,     27,     27,     27,     27,     27,     27,     27,     27,     27,     27,     27,     27,     27,     27,     27,
                28,     28,     28,     28,     28,     28,     28,     28,     28,     28,     28,     28,     28,     28,     28,     28,
                28,     28,     28,     28,     28,     28,     28,     28,     28,     28,     28,     28,     28,     28,     28,     28,
                28,     28,     28,     28,     28,     28,     28,     28,     28,     28,     28,     28,     28,     28,     28,     28,
                28,     28,     28,     28,     28,     28,     28,     28,     28,     28,     28,     28,     28,     28,     28,     28,
                29,     29,     29,     29,     29,     29,     29,     29,     29,     29,     29,     29,     29,     29,     29,     29,
                29,     29,     29,     29,     29,     29,     29,     29,     29,     29,     29,     29,     29,     29,     29,     29,
                29,     29,     29,     29,     29,     29,     29,     29,     29,     29,     29,     29,     29,     29,     29,     29,
                29,     29,     29,     29,     29,     29,     29,     29,     29,     29,     29,     29,     29,     29,     29,     29
        };
        BL_ORDER = new byte[] {
                16,     17,     18,      0,      8,      7,      9,      6,     10,      5,     11,      4,     12,      3,     13,      2,
                14,      1,     15
        };
        LENGTH_CODE = new byte[] {
                 0,      1,      2,      3,      4,      5,      6,      7,      8,      8,      9,      9,     10,     10,     11,     11,
                12,     12,     12,     12,     13,     13,     13,     13,     14,     14,     14,     14,     15,     15,     15,     15,
                16,     16,     16,     16,     16,     16,     16,     16,     17,     17,     17,     17,     17,     17,     17,     17,
                18,     18,     18,     18,     18,     18,     18,     18,     19,     19,     19,     19,     19,     19,     19,     19,
                20,     20,     20,     20,     20,     20,     20,     20,     20,     20,     20,     20,     20,     20,     20,     20,
                21,     21,     21,     21,     21,     21,     21,     21,     21,     21,     21,     21,     21,     21,     21,     21,
                22,     22,     22,     22,     22,     22,     22,     22,     22,     22,     22,     22,     22,     22,     22,     22,
                23,     23,     23,     23,     23,     23,     23,     23,     23,     23,     23,     23,     23,     23,     23,     23,
                24,     24,     24,     24,     24,     24,     24,     24,     24,     24,     24,     24,     24,     24,     24,     24,
                24,     24,     24,     24,     24,     24,     24,     24,     24,     24,     24,     24,     24,     24,     24,     24,
                25,     25,     25,     25,     25,     25,     25,     25,     25,     25,     25,     25,     25,     25,     25,     25,
                25,     25,     25,     25,     25,     25,     25,     25,     25,     25,     25,     25,     25,     25,     25,     25,
                26,     26,     26,     26,     26,     26,     26,     26,     26,     26,     26,     26,     26,     26,     26,     26,
                26,     26,     26,     26,     26,     26,     26,     26,     26,     26,     26,     26,     26,     26,     26,     26,
                27,     27,     27,     27,     27,     27,     27,     27,     27,     27,     27,     27,     27,     27,     27,     27,
                27,     27,     27,     27,     27,     27,     27,     27,     27,     27,     27,     27,     27,     27,     27,     28
        };
        BASE_LENGTH = new int[] {
                 0,      1,      2,      3,      4,      5,      6,      7,      8,     10,     12,     14,     16,     20,     24,     28,
                32,     40,     48,     56,     64,     80,     96,    112,    128,    160,    192,    224,      0
        };
        BASE_DIST = new int[] {
                 0,      1,      2,      3,      4,      6,      8,     12,     16,     24,     32,     48,     64,     96,    128,    192,
               256,    384,    512,    768,   1024,   1536,   2048,   3072,   4096,   6144,   8192,  12288,  16384,  24576
        };
        CONFIG_TABLE = new Config[] {
            new Config(0, 0, 0, 0, STORED),
            new Config(4, 4, 8, 4, FAST),
            new Config(4, 5, 16, 8, FAST),
            new Config(4, 6, 32, 32, FAST),
            new Config(4, 4, 16, 16, SLOW),
            new Config(8, 16, 32, 32, SLOW),
            new Config(8, 16, 128, 128, SLOW),
            new Config(8, 32, 128, 256, SLOW),
            new Config(32, 128, 258, 1024, SLOW),
            new Config(32, 258, 258, 4096, SLOW)
        };
        ERROR_MESSAGE = new String[] {
            "need dictionary",
            "stream end",
            "",
            "file error",
            "stream error",
            "data error",
            "insufficient memory",
            "buffer error",
            "incompatible version",
            ""
        };
    }

    public static boolean smaller(int[] tree, int n, int m, byte[] depth) {
        int tn2 = tree[n * 2];
        int tm2 = tree[m * 2];
        return tn2 < tm2 || (tn2 == tm2 && depth[n] <= depth[m]);
    }

    public static int deflateCopy(ZStream dst, ZStream src) {
        byte[] next;
        if(src.dstate == null) return STREAM_ERROR;
        if((next = src.nextIn) != null) dst.nextIn = dup(next);
        dst.nextInIndex = src.nextInIndex;
        dst.availIn = src.availIn;
        dst.totalIn = src.totalIn;
        if((next = src.nextOut) != null) dst.nextOut = dup(next);
        dst.nextOutIndex = src.nextOutIndex;
        dst.availOut = src.availOut;
        dst.totalOut = src.totalOut;
        dst.message = src.message;
        dst.dataType = src.dataType;
        dst.adler = src.adler.copy();
        (dst.dstate = (Deflate) src.dstate.clone()).stream = dst;
        return OK;
    }

    private static int dcode(int dist) {
        return dist < 256 ? DIST_CODE[dist] : DIST_CODE[256 + (dist >>> 7)];
    }

    private static byte[] dup(byte[] buf) {
        int len;
        byte[] result;
        Array.copy(buf, 0, result = new byte[len = buf.length], 0, len);
        return result;
    }

    private static int[] dup(int[] buf) {
        int len;
        int[] result;
        Array.copy(buf, 0, result = new int[len = buf.length], 0, len);
        return result;
    }

    public int pendingOut;
    public int pending;
    public int wrap;
    public int level;
    public int heapLen;
    public int heapMax;
    public int optLen;
    public int staticLen;
    private int status;
    private int pendingBufSize;
    private int dataType;
    private int lastFlush;
    private int wSize;
    private int wBits;
    private int wMask;
    private int windowSize;
    private int insh;
    private int hashSize;
    private int hashBits;
    private int hashMask;
    private int hashShift;
    private int blockStart;
    private int matchLength;
    private int prevMatch;
    private int matchAvailable;
    private int strStart;
    private int matchStart;
    private int lookahead;
    private int prevLength;
    private int maxChainLength;
    private int maxLazyMatch;
    private int strategy;
    private int goodMatch;
    private int niceMatch;
    private int litBufSize;
    private int lastLit;
    private int matches;
    private int lastEobLen;
    private int lBuf;
    private int dBuf;
    private int biBuf;
    private int biValid;
    public byte[] pendingBuf;
    public byte[] depth;
    private byte[] window;
    public int[] blCount;
    public int[] heap;
    private int[] prev;
    private int[] head;
    private int[] dynLtree;
    private int[] dynDtree;
    private int[] blTree;
    private Tree lDesc;
    private Tree dDesc;
    private Tree blDesc;
    private GZIPHeader gheader;
    private ZStream stream;

    public Deflate(ZStream stream) {
        this.wrap = 1;
        this.depth = new byte[2 * L_CODES + 1];
        this.blCount = new int[MAX_BITS + 1];
        this.heap = new int[2 * L_CODES + 1];
        this.dynLtree = new int[HEAP_SIZE * 2];
        this.dynDtree = new int[(2 * D_CODES + 1) * 2];
        this.blTree = new int[(2 * BL_CODES + 1) * 2];
        this.lDesc = new Tree();
        this.dDesc = new Tree();
        this.blDesc = new Tree();
        this.stream = stream;
    }

    public void pqDownHeap(int[] tree, int k) {
        int v;
        int j;
        int heapLen = this.heapLen;
        byte[] depth = this.depth;
        int[] heap;
        for(v = (heap = this.heap)[k], j = k << 1; j <= heapLen; k = j, j <<= 1)
        {
            if(j < heapLen && smaller(tree, heap[j + 1], heap[j], depth)) j++;
            if(smaller(tree, v, heap[j], depth)) break;
            heap[k] = heap[j];
        }
        heap[k] = v;
    }

    public void putBytes(byte[] data, int offset, int length) {
        Array.copy(data, offset, pendingBuf, pending, length);
        pending += length;
    }

    public void putByte(byte c) {
        pendingBuf[pending++] = c;
    }

    public void putShort(int w) {
        putByte((byte) w);
        putByte((byte) (w >>> 8));
    }

    public int deflateInit(int level, int bits, int memlevel) {
        return deflateInit(level, Z_DEFLATED, bits, memlevel, DEFAULT_STRATEGY);
    }

    public int deflateInit(int level, int bits) {
        return deflateInit(level, Z_DEFLATED, bits, DEF_MEM_LEVEL, DEFAULT_STRATEGY);
    }

    public int deflateInit(int level) {
        return deflateInit(level, MAX_WBITS);
    }

    public int deflateParams(int lvl, int strtg) {
        int err = OK;
        if(lvl == DEFAULT_COMPRESSION) lvl = 6;
        if(lvl < 0 || lvl > 9 || strtg < 0 || strtg > HUFFMAN_ONLY) return STREAM_ERROR;
        if(CONFIG_TABLE[level].func != CONFIG_TABLE[lvl].func && stream.totalIn != 0) err = stream.deflate(PARTIAL_FLUSH);
        if(level != lvl)
        {
            Config config = CONFIG_TABLE[level];
            level = lvl;
            maxLazyMatch = config.maxLazy;
            goodMatch = config.goodLength;
            niceMatch = config.niceLength;
            maxChainLength = config.maxChain;
        }
        strategy = strtg;
        return err;
    }

    public int deflateSetDictionary(byte[] dictionary, int dictLength) {
        int index = 0;
        int length = dictLength;
        if(dictionary == null || status != INIT_STATE) return STREAM_ERROR;
        stream.adler.update(dictionary, 0, dictLength);
        if(length < MIN_MATCH) return OK;
        if(length > wSize - MIN_LOOKAHEAD)
        {
            length = wSize - MIN_LOOKAHEAD;
            index = dictLength - length;
        }
        Array.copy(dictionary, index, window, 0, length);
        strStart = length;
        blockStart = length;
        insh = window[0] & 0xff;
        insh = ((insh << hashShift) ^ (window[1] & 0xff)) & hashMask;
        for(int n = 0; n <= length - MIN_MATCH; n++)
        {
            insh = ((insh << hashShift) ^ (window[n + (MIN_MATCH - 1)] & 0xff)) & hashMask;
            prev[n & wMask] = head[insh];
            head[insh] = (short) n;
        }
        return OK;
    }

    public int deflate(int flush) {
        int oldFlush;
        if(flush > FINISH || flush < 0) return STREAM_ERROR;
        if(stream.nextOut == null || stream.nextIn == null && stream.availIn != 0 || status == FINISH_STATE && flush != FINISH)
        {
            stream.message = ERROR_MESSAGE[NEED_DICT - STREAM_ERROR];
            return STREAM_ERROR;
        }
        if(stream.availOut == 0)
        {
            stream.message = ERROR_MESSAGE[NEED_DICT - BUF_ERROR];
            return BUF_ERROR;
        }
        oldFlush = lastFlush;
        lastFlush = flush;
        if(status == INIT_STATE)
        {
            if(wrap == 2)
            {
                getGZIPHeader().put(this);
                status = BUSY_STATE;
                stream.adler.reset();
            } else
            {
                int header = (Z_DEFLATED + ((wBits - 8) << 4)) << 8;
                int levelFlags = ((level - 1) & 0xff) >> 1;
                if(levelFlags > 3) levelFlags = 3;
                header |= (levelFlags << 6);
                if(strStart != 0) header |= PRESET_DICT;
                header += 31 - (header % 31);
                status = BUSY_STATE;
                putShortMSB(header);
                if(strStart != 0)
                {
                    int adler = stream.adler.value();
                    putShortMSB(adler >>> 16);
                    putShortMSB(adler & 0xffff);
                }
                stream.adler.reset();
            }
        }
        if(pending != 0)
        {
            stream.flushPending();
            if(stream.availOut == 0)
            {
                lastFlush = -1;
                return OK;
            }
        }
        else if(stream.availIn == 0 && flush <= oldFlush && flush != FINISH)
        {
            stream.message = ERROR_MESSAGE[NEED_DICT - BUF_ERROR];
            return BUF_ERROR;
        }
        if(status == FINISH_STATE && stream.availIn != 0)
        {
            stream.message = ERROR_MESSAGE[NEED_DICT - BUF_ERROR];
            return BUF_ERROR;
        }
        if(stream.availIn != 0 || lookahead != 0 || flush != NO_FLUSH && status != FINISH_STATE)
        {
            int bstate = -1;
            switch(CONFIG_TABLE[level].func)
            {
            default:
                break;
            case STORED:
                bstate = deflateStored(flush);
                break;
            case FAST:
                bstate = deflateFast(flush);
                break;
            case SLOW:
                bstate = deflateSlow(flush);
                break;
            }
            if(bstate == FINISH_STARTED || bstate == FINISH_DONE) status = FINISH_STATE;
            if(bstate == NEED_MORE || bstate == FINISH_STARTED)
            {
                if(stream.availOut == 0) lastFlush = -1;
                return OK;
            }
            if(bstate == BLOCK_DONE)
            {
                if(flush == PARTIAL_FLUSH)
                {
                    trAlign();
                } else
                {
                    trStoredBlock(0, 0, false);
                    if(flush == FULL_FLUSH) for(int i = 0; i < hashSize; i++) head[i] = 0;
                }
                stream.flushPending();
                if(stream.availOut == 0)
                {
                    lastFlush = -1;
                    return OK;
                }
            }
        }
        if(flush != FINISH) return OK;
        if(wrap <= 0) return STREAM_END;
        if(wrap == 2)
        {
            int adler = stream.adler.value();
            int totalIn = (int) stream.totalIn;
            putByte((byte) adler);
            putByte((byte) (adler >> 8));
            putByte((byte) (adler >> 16));
            putByte((byte) (adler >> 24));
            putByte((byte) totalIn);
            putByte((byte) (totalIn >> 8));
            putByte((byte) (totalIn >> 16));
            putByte((byte) (totalIn >> 24));
            getGZIPHeader().setCRC(adler);
        } else
        {
            int adler = stream.adler.value();
            putShortMSB(adler >>> 16);
            putShortMSB(adler & 0xffff);
        }
        stream.flushPending();
        if(wrap > 0) wrap = -wrap;
        return pending != 0 ? OK : STREAM_END;
    }

    public int deflateEnd() {
        int status;
        if((status = this.status) != INIT_STATE && status != BUSY_STATE && status != FINISH_STATE) return STREAM_ERROR;
        pendingBuf = null;
        head = null;
        prev = null;
        window = null;
        return status == BUSY_STATE ? DATA_ERROR : OK;
    }

    private void lmInit() {
        int i;
        windowSize = 2 * wSize;
        head[hashSize - 1] = 0;
        for(i = 0; i < hashSize - 1; i++) head[i] = 0;
        Config config = CONFIG_TABLE[level];
        maxLazyMatch = config.maxLazy;
        goodMatch = config.goodLength;
        niceMatch = config.niceLength;
        maxChainLength = config.maxChain;
        strStart = 0;
        blockStart = 0;
        lookahead = 0;
        matchLength = prevLength = MIN_MATCH - 1;
        matchAvailable = 0;
        insh = 0;
    }

    private void trInit() {
        lDesc.dynTree = dynLtree;
        lDesc.statDesc = StaticTree.STATIC_L_DESC;
        dDesc.dynTree = dynDtree;
        dDesc.statDesc = StaticTree.STATIC_D_DESC;
        blDesc.dynTree = blTree;
        blDesc.statDesc = StaticTree.STATIC_BL_DESC;
        biBuf = 0;
        biValid = 0;
        lastEobLen = 8;
        initBlock();
    }

    private void initBlock() {
        int i;
        for(i = 0; i < L_CODES; i++) dynLtree[i * 2] = 0;
        for(i = 0; i < D_CODES; i++) dynDtree[i * 2] = 0;
        for(i = 0; i < BL_CODES; i++) blTree[i * 2] = 0;
        dynLtree[END_BLOCK * 2] = 1;
        optLen = staticLen = 0;
        lastLit = matches = 0;
    }

    private void scanTree(int[] tree, int maxCode) {
        int n;
        int prevlen = -1;
        int curlen;
        int nextlen = tree[0 * 2 + 1];
        int count = 0;
        int maxCount = 7;
        int minCount = 4;
        if(nextlen == 0)
        {
            maxCount = 138;
            minCount = 3;
        }
        tree[(maxCode + 1) * 2 + 1] = (short) 0xffff;
        for(n = 0; n <= maxCode; n++)
        {
            curlen = nextlen;
            nextlen = tree[(n + 1) * 2 + 1];
            if(++count < maxCount && curlen == nextlen) continue;
            if(count < minCount)
            {
                blTree[curlen * 2] += count;
            }
            else if(curlen != 0)
            {
                if(curlen != prevlen) blTree[curlen * 2]++;
                blTree[REP_3_6 * 2]++;
            }
            else if(count <= 10)
            {
                blTree[REPZ_3_10 * 2]++;
            }
            else
            {
                blTree[REPZ_11_138 * 2]++;
            }
            count = 0;
            prevlen = curlen;
            if(nextlen == 0)
            {
                maxCount = 138;
                minCount = 3;
            }
            else if(curlen == nextlen)
            {
                maxCount = 6;
                minCount = 3;
            }
            else
            {
                maxCount = 7;
                minCount = 4;
            }
        }
    }

    private void sendAllTrees(int lcodes, int dcodes, int blcodes) {
        int rank;
        sendBits(lcodes - 257, 5);
        sendBits(dcodes - 1, 5);
        sendBits(blcodes - 4, 4);
        for(rank = 0; rank < blcodes; rank++) sendBits(blTree[BL_ORDER[rank] * 2 + 1], 3);
        sendTree(dynLtree, lcodes - 1);
        sendTree(dynDtree, dcodes - 1);
    }

    private void sendTree(int[] tree, int maxCode) {
        int n;
        int prevlen = -1;
        int curlen;
        int nextlen = tree[0 * 2 + 1];
        int count = 0;
        int maxCount = 7;
        int minCount = 4;
        if(nextlen == 0)
        {
            maxCount = 138;
            minCount = 3;
        }
        for(n = 0; n <= maxCode; n++)
        {
            curlen = nextlen;
            nextlen = tree[(n + 1) * 2 + 1];
            if(++count < maxCount && curlen == nextlen) continue;
            if(count < minCount)
            {
                do
                {
                    sendCode(curlen, blTree);
                } while(--count != 0);
            }
            else if(curlen != 0)
            {
                if(curlen != prevlen)
                {
                    sendCode(curlen, blTree);
                    count--;
                }
                sendCode(REP_3_6, blTree);
                sendBits(count - 3, 2);
            }
            else if(count <= 10)
            {
                sendCode(REPZ_3_10, blTree);
                sendBits(count - 3, 3);
            }
            else
            {
                sendCode(REPZ_11_138, blTree);
                sendBits(count - 11, 7);
            }
            count = 0;
            prevlen = curlen;
            if(nextlen == 0)
            {
                maxCount = 138;
                minCount = 3;
            }
            else if(curlen == nextlen)
            {
                maxCount = 6;
                minCount = 3;
            }
            else
            {
                maxCount = 7;
                minCount = 4;
            }
        }
    }

    private void sendCode(int c, int[] tree) {
        int c2 = c * 2;
        sendBits(tree[c2] & 0xffff, tree[c2 + 1] & 0xffff);
    }

    private void sendBits(int value, int length) {
        int val;
        int len = length;
        if(biValid > BUF_SIZE - len)
        {
            val = value;
            biBuf |= (val << biValid) & 0xffff;
            putShort(biBuf);
            biBuf = (short) (val >>> (BUF_SIZE - biValid));
            biValid += len - BUF_SIZE;
        } else
        {
            biBuf |= (value << biValid) & 0xffff;
            biValid += len;
        }
    }

    private void putShortMSB(int b) {
        putByte((byte) (b >> 8));
        putByte((byte) b);
    }

    private void trAlign() {
        sendBits(STATIC_TREES << 1, 3);
        sendCode(END_BLOCK, StaticTree.STATIC_LTREE);
        biFlush();
        if(lastEobLen - biValid < -2)
        {
            sendBits(STATIC_TREES << 1, 3);
            sendCode(END_BLOCK, StaticTree.STATIC_LTREE);
            biFlush();
        }
        lastEobLen = 7;
    }

    private void compressBlock(int[] ltree, int[] dtree) {
        int dist;
        int code;
        int extra;
        int lc;
        int lx = 0;
        if(lastLit != 0) do
        {
            dist = (pendingBuf[dBuf + lx * 2] & 0xff) << 8 | pendingBuf[dBuf + lx * 2 + 1] & 0xff;
            lc = pendingBuf[lBuf + lx] & 0xff;
            lx++;
            if(dist == 0)
            {
                sendCode(lc, ltree);
            } else
            {
                code = LENGTH_CODE[lc];
                sendCode(code + LITERALS + 1, ltree);
                extra = StaticTree.EXTRA_LBITS[code];
                if(extra != 0)
                {
                    lc -= BASE_LENGTH[code];
                    sendBits(lc, extra);
                }
                dist--;
                code = dcode(dist);
                sendCode(code, dtree);
                extra = StaticTree.EXTRA_DBITS[code];
                if(extra != 0)
                {
                    dist -= BASE_DIST[code];
                    sendBits(dist, extra);
                }
            }
        } while(lx < lastLit);
        sendCode(END_BLOCK, ltree);
        lastEobLen = ltree[END_BLOCK * 2 + 1];
    }

    private void setDataType() {
        int n = 0;
        int asciiFreq = 0;
        int binFreq = 0;
        while(n < 7)
        {
            binFreq += dynLtree[n * 2];
            n++;
        }
        while(n < 128)
        {
            asciiFreq += dynLtree[n * 2];
            n++;
        }
        while(n < LITERALS)
        {
            binFreq += dynLtree[n * 2];
            n++;
        }
        dataType = binFreq > (asciiFreq >>> 2) ? Z_BINARY : Z_ASCII;
    }

    private void biFlush() {
        if(biValid == 16)
        {
            putShort(biBuf);
            biBuf = 0;
            biValid = 0;
        }
        else if(biValid >= 8)
        {
            putByte((byte) biBuf);
            biBuf >>>= 8;
            biValid -= 8;
        }
    }

    private void biWindup() {
        if(biValid > 8)
        {
            putShort(biBuf);
        }
        else if(biValid > 0)
        {
            putByte((byte) biBuf);
        }
        biBuf = 0;
        biValid = 0;
    }

    private void copyBlock(int buf, int len, boolean header) {
        biWindup();
        lastEobLen = 8;
        if(header)
        {
            putShort((short) len);
            putShort((short) ~len);
        }
        putBytes(window, buf, len);
    }

    private void flushBlockOnly(boolean eof) {
        trFlushBlock(blockStart >= 0 ? blockStart : -1, strStart - blockStart, eof);
        blockStart = strStart;
        stream.flushPending();
    }

    private void trStoredBlock(int buf, int storedLen, boolean eof) {
        sendBits((STORED_BLOCK << 1) + (eof ? 1 : 0), 3);
        copyBlock(buf, storedLen, true);
    }

    private void trFlushBlock(int buf, int storedLen, boolean eof) {
        int optLenb;
        int staticLenb;
        int maxBlindex = 0;
        if(level > 0)
        {
            if(dataType == Z_UNKNOWN) setDataType();
            lDesc.buildTree(this);
            dDesc.buildTree(this);
            maxBlindex = buildBlTree();
            optLenb = (optLen + (3 + 7)) >>> 3;
            staticLenb = (staticLen + (3 + 7)) >>> 3;
            if(staticLenb <= optLenb) optLenb = staticLenb;
        } else
        {
            optLenb = staticLenb = storedLen + 5;
        }
        if(storedLen + 4 <= optLenb && buf != -1)
        {
            trStoredBlock(buf, storedLen, eof);
        }
        else if(staticLenb == optLenb)
        {
            sendBits((STATIC_TREES << 1) + (eof ? 1 : 0), 3);
            compressBlock(StaticTree.STATIC_LTREE, StaticTree.STATIC_DTREE);
        }
        else
        {
            sendBits((DYN_TREES << 1) + (eof ? 1 : 0), 3);
            sendAllTrees(lDesc.maxCode + 1, dDesc.maxCode + 1, maxBlindex + 1);
            compressBlock(dynLtree, dynDtree);
        }
        initBlock();
        if(eof) biWindup();
    }

    private void fillWindow() {
        int n;
        int m;
        int p;
        int more;
        do
        {
            more = windowSize - lookahead - strStart;
            if(more == 0 && strStart == 0 && lookahead == 0)
            {
                more = wSize;
            }
            else if(more == -1)
            {
                more--;
            }
            else if(strStart >= (wSize << 1) - MIN_LOOKAHEAD)
            {
                Array.copy(window, wSize, window, 0, wSize);
                matchStart -= wSize;
                strStart -= wSize;
                blockStart -= wSize;
                n = hashSize;
                p = n;
                do
                {
                    m = head[--p] & 0xffff;
                    head[p] = m >= wSize ? (short) (m - wSize) : 0;
                } while(--n != 0);
                n = wSize;
                p = n;
                do
                {
                    m = prev[--p] & 0xffff;
                    prev[p] = m >= wSize ? (short) (m - wSize) : 0;
                } while(--n != 0);
                more += wSize;
            }
            if(stream.availIn == 0) return;
            n = stream.readBuf(window, strStart + lookahead, more);
            lookahead += n;
            if(lookahead >= MIN_MATCH)
            {
                insh = window[strStart] & 0xff;
                insh = ((insh << hashShift) ^ (window[strStart + 1] & 0xff)) & hashMask;
            }
        } while(lookahead < MIN_LOOKAHEAD && stream.availIn != 0);
    }

    private boolean trTally(int dst, int lc) {
        int outLength;
        int inLength;
        int dcode;
        pendingBuf[dBuf + lastLit * 2] = (byte) (dst >>> 8);
        pendingBuf[dBuf + lastLit * 2 + 1] = (byte) dst;
        pendingBuf[lBuf + lastLit] = (byte) lc;
        lastLit++;
        if(dst == 0)
        {
            dynLtree[lc * 2]++;
        } else
        {
            matches++;
            dst--;
            dynLtree[(LENGTH_CODE[lc] + LITERALS + 1) * 2]++;
            dynDtree[dcode(dst) * 2]++;
        }
        if((lastLit & 0x1fff) == 0 && level > 2)
        {
            outLength = lastLit * 8;
            inLength = strStart - blockStart;
            for(dcode = 0; dcode < D_CODES; dcode++) outLength += (int) dynDtree[dcode * 2] * (5L + StaticTree.EXTRA_DBITS[dcode]);
            outLength >>>= 3;
            if((matches < (lastLit / 2)) && outLength < inLength / 2) return true;
        }
        return lastLit == litBufSize - 1;
    }

    private int buildBlTree() {
        int maxBlindex;
        scanTree(dynLtree, lDesc.maxCode);
        scanTree(dynDtree, dDesc.maxCode);
        blDesc.buildTree(this);
        for(maxBlindex = BL_CODES - 1; maxBlindex >= 3; maxBlindex--) if(blTree[BL_ORDER[maxBlindex] * 2 + 1] != 0) break;
        optLen += 3 * (maxBlindex + 1) + (5 + 5 + 4);
        return maxBlindex;
    }

    private int deflateInit(int level, int method, int windowBits, int memLevel, int strategy) {
        int wraplocal = 1;
        stream.message = null;
        if(level == DEFAULT_COMPRESSION) level = 6;
        if(windowBits < 0)
        {
            wraplocal = 0;
            windowBits = -windowBits;
        }
        else if(windowBits > 15)
        {
            wraplocal = 2;
            windowBits -= 16;
            stream.adler = new CRC32();
        }
        if(memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != Z_DEFLATED || windowBits < 9 || windowBits > 15 || level < 0 || level > 9 || strategy < 0 || strategy > HUFFMAN_ONLY)
        {
            return STREAM_ERROR;
        }
        wrap = wraplocal;
        wBits = windowBits;
        wSize = 1 << wBits;
        wMask = wSize - 1;
        hashBits = memLevel + 7;
        hashSize = 1 << hashBits;
        hashMask = hashSize - 1;
        hashShift = (hashBits + MIN_MATCH - 1) / MIN_MATCH;
        window = new byte[wSize * 2];
        prev = new int[wSize];
        head = new int[hashSize];
        litBufSize = 1 << (memLevel + 6);
        pendingBuf = new byte[litBufSize * 4];
        pendingBufSize = litBufSize * 4;
        dBuf = litBufSize / 2;
        lBuf = (1 + 2) * litBufSize;
        this.level = level;
        this.strategy = strategy;
        return deflateReset();
    }

    private int deflateStored(int flush) {
        int maxStart;
        int maxBlockSize = 0xffff;
        if(maxBlockSize > pendingBufSize - 5) maxBlockSize = pendingBufSize - 5;
        do
        {
            if(lookahead <= 1)
            {
                fillWindow();
                if(lookahead == 0 && flush == NO_FLUSH) return NEED_MORE;
                if(lookahead == 0) break;
            }
            strStart += lookahead;
            lookahead = 0;
            maxStart = blockStart + maxBlockSize;
            if(strStart == 0 || strStart >= maxStart)
            {
                lookahead = (int) (strStart - maxStart);
                strStart = (int) maxStart;
                flushBlockOnly(false);
                if(stream.availOut == 0) return NEED_MORE;
            }
            if(strStart - blockStart >= wSize - MIN_LOOKAHEAD)
            {
                flushBlockOnly(false);
                if(stream.availOut == 0) return NEED_MORE;
            }
        } while(true);
        flushBlockOnly(flush == FINISH);
        return stream.availOut == 0 ? (flush == FINISH ? FINISH_STARTED : NEED_MORE) : (flush == FINISH ? FINISH_DONE : BLOCK_DONE);
    }

    private int deflateFast(int flush) {
        boolean bflush;
        int hashHead = 0;
        do
        {
            if(lookahead < MIN_LOOKAHEAD)
            {
                fillWindow();
                if(lookahead < MIN_LOOKAHEAD && flush == NO_FLUSH) return NEED_MORE;
                if(lookahead == 0) break;
            }
            if(lookahead >= MIN_MATCH)
            {
                insh = ((insh << hashShift) ^ (window[(strStart) + (MIN_MATCH - 1)] & 0xff)) & hashMask;
                hashHead = (head[insh] & 0xffff);
                prev[strStart & wMask] = head[insh];
                head[insh] = (short) strStart;
            }
            if(hashHead != 0L && ((strStart - hashHead) & 0xffff) <= wSize - MIN_LOOKAHEAD && strategy != HUFFMAN_ONLY) matchLength = longestMatch(hashHead);
            if(matchLength >= MIN_MATCH)
            {
                bflush = trTally(strStart - matchStart, matchLength - MIN_MATCH);
                lookahead -= matchLength;
                if(matchLength <= maxLazyMatch && lookahead >= MIN_MATCH)
                {
                    matchLength--;
                    do
                    {
                        strStart++;
                        insh = ((insh << hashShift) ^ (window[(strStart) + (MIN_MATCH - 1)] & 0xff)) & hashMask;
                        hashHead = (head[insh] & 0xffff);
                        prev[strStart & wMask] = head[insh];
                        head[insh] = (short) strStart;
                    } while(--matchLength != 0);
                    strStart++;
                } else
                {
                    strStart += matchLength;
                    matchLength = 0;
                    insh = window[strStart] & 0xff;
                    insh = ((insh << hashShift) ^ (window[strStart + 1] & 0xff)) & hashMask;
                }
            } else
            {
                bflush = trTally(0, window[strStart] & 0xff);
                lookahead--;
                strStart++;
            }
            if(bflush)
            {
                flushBlockOnly(false);
                if(stream.availOut == 0) return NEED_MORE;
            }
        } while(true);
        flushBlockOnly(flush == FINISH);
        return stream.availOut == 0 ? (flush == FINISH ? FINISH_STARTED : NEED_MORE) : (flush == FINISH ? FINISH_DONE : BLOCK_DONE);
    }

    private int deflateSlow(int flush) {
        boolean bflush;
        int hashHead = 0;
        do
        {
            if(lookahead < MIN_LOOKAHEAD)
            {
                fillWindow();
                if(lookahead < MIN_LOOKAHEAD && flush == NO_FLUSH) return NEED_MORE;
                if(lookahead == 0) break;
            }
            if(lookahead >= MIN_MATCH)
            {
                insh = ((insh << hashShift) ^ (window[(strStart) + (MIN_MATCH - 1)] & 0xff)) & hashMask;
                hashHead = (head[insh] & 0xffff);
                prev[strStart & wMask] = head[insh];
                head[insh] = (short) strStart;
            }
            prevLength = matchLength;
            prevMatch = matchStart;
            matchLength = MIN_MATCH - 1;
            if(hashHead != 0 && prevLength < maxLazyMatch && ((strStart - hashHead) & 0xffff) <= wSize - MIN_LOOKAHEAD)
            {
                if(strategy != HUFFMAN_ONLY) matchLength = longestMatch(hashHead);
                if(matchLength <= 5 && (strategy == FILTERED || matchLength == MIN_MATCH && strStart - matchStart > 4096)) matchLength = MIN_MATCH - 1;
            }
            if(prevLength >= MIN_MATCH && matchLength <= prevLength)
            {
                int maxInsert = strStart + lookahead - MIN_MATCH;
                bflush = trTally(strStart - 1 - prevMatch, prevLength - MIN_MATCH);
                lookahead -= prevLength - 1;
                prevLength -= 2;
                do if(++strStart <= maxInsert)
                {
                    insh = ((insh << hashShift) ^ (window[(strStart) + (MIN_MATCH - 1)] & 0xff)) & hashMask;
                    hashHead = (head[insh] & 0xffff);
                    prev[strStart & wMask] = head[insh];
                    head[insh] = (short) strStart;
                } while(--prevLength != 0);
                matchAvailable = 0;
                matchLength = MIN_MATCH - 1;
                strStart++;
                if(bflush)
                {
                    flushBlockOnly(false);
                    if(stream.availOut == 0) return NEED_MORE;
                }
            }
            else if(matchAvailable != 0)
            {
                bflush = trTally(0, window[strStart - 1] & 0xff);
                if(bflush) flushBlockOnly(false);
                strStart++;
                lookahead--;
                if(stream.availOut == 0) return NEED_MORE;
            }
            else
            {
                matchAvailable = 1;
                strStart++;
                lookahead--;
            }
        } while(true);
        if(matchAvailable != 0)
        {
            bflush = trTally(0, window[strStart - 1] & 0xff);
            matchAvailable = 0;
        }
        flushBlockOnly(flush == FINISH);
        return stream.availOut == 0 ? (flush == FINISH ? FINISH_STARTED : NEED_MORE) : (flush == FINISH ? FINISH_DONE : BLOCK_DONE);
    }

    private int deflateReset() {
        stream.totalIn = stream.totalOut = 0;
        stream.message = null;
        stream.dataType = Z_UNKNOWN;
        pending = 0;
        pendingOut = 0;
        if(wrap < 0) wrap = -wrap;
        status = (wrap == 0) ? BUSY_STATE : INIT_STATE;
        stream.adler.reset();
        lastFlush = NO_FLUSH;
        trInit();
        lmInit();
        return OK;
    }

    private int longestMatch(int curMatch) {
        byte scanEnd;
        byte scanEnd1;
        int chainLength = maxChainLength;
        int scan = strStart;
        int match;
        int len;
        int bestLen = prevLength;
        int limit = strStart > wSize - MIN_LOOKAHEAD ? strStart - (wSize - MIN_LOOKAHEAD) : 0;
        int niceMatchLocal = this.niceMatch;
        int wmask = this.wMask;
        int strend = strStart + MAX_MATCH;
        scanEnd1 = window[scan + bestLen - 1];
        scanEnd = window[scan + bestLen];
        if(prevLength >= goodMatch) chainLength >>= 2;
        if(niceMatchLocal > lookahead) niceMatchLocal = lookahead;
        do
        {
            match = curMatch;
            if(window[match + bestLen] != scanEnd || window[match + bestLen - 1] != scanEnd1 || window[match] != window[scan] || window[++match] != window[scan + 1]) continue;
            scan += 2;
            match++;
            do
            {
            } while(
                window[++scan] == window[++match] && window[++scan] == window[++match] && window[++scan] == window[++match] && window[++scan] == window[++match] &&
                window[++scan] == window[++match] && window[++scan] == window[++match] && window[++scan] == window[++match] && window[++scan] == window[++match] && scan < strend
            );
            len = MAX_MATCH - (strend - scan);
            scan = strend - MAX_MATCH;
            if(len > bestLen)
            {
                matchStart = curMatch;
                bestLen = len;
                if(len >= niceMatchLocal) break;
                scanEnd1 = window[scan + bestLen - 1];
                scanEnd = window[scan + bestLen];
            }
        } while((curMatch = prev[curMatch & wmask] & 0xffff) > limit && --chainLength != 0);
        return bestLen <= lookahead ? bestLen : lookahead;
    }

    private Object clone() {
        Deflate dst = new Deflate(stream);
        dst.pendingBuf = dup(dst.pendingBuf);
        dst.window = dup(dst.window);
        dst.prev = dup(dst.prev);
        dst.head = dup(dst.head);
        dst.dynLtree = dup(dst.dynLtree);
        dst.dynDtree = dup(dst.dynDtree);
        dst.blTree = dup(dst.blTree);
        dst.blCount = dup(dst.blCount);
        dst.heap = dup(dst.heap);
        dst.depth = dup(dst.depth);
        dst.lDesc.dynTree = dst.dynLtree;
        dst.dDesc.dynTree = dst.dynDtree;
        dst.blDesc.dynTree = dst.blTree;
        if(dst.gheader != null) dst.gheader = (GZIPHeader) dst.gheader.clone();
        return dst;
    }

    private GZIPHeader getGZIPHeader() {
        GZIPHeader result;
        if((result = gheader) == null) gheader = result = new GZIPHeader();
        return result;
    }
}

final class InfBlocks extends Zlib
{
    private static final int MANY = 1440;
    private static final int TYPE = 0;
    private static final int LENS = 1;
    private static final int STORED = 2;
    private static final int TABLE = 3;
    private static final int BTREE = 4;
    private static final int DTREE = 5;
    private static final int CODES = 6;
    private static final int DRY = 7;
    private static final int DONE = 8;
    private static final int BAD = 9;

    private static final int[] INFLATE_MASK;
    private static final int[] BORDER;

    static {
        INFLATE_MASK = new int[] {
            0x0000, 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff,
            0xffff
        };
        BORDER = new int[] {
                16,     17,     18,      0,      8,      7,      9,      6,     10,      5,     11,      4,     12,      3,     13,      2,
                14,      1,     15
        };
    }

    private boolean check;
    public int bitk;
    public int bitb;
    public int end;
    public int read;
    public int write;
    private int mode;
    private int left;
    private int table;
    private int index;
    private int last;
    public byte[] window;
    private int[] blens;
    private int[] bb;
    private int[] tb;
    private int[] hufts;
    private InfCodes codes;
    private InfTree inftree;

    public InfBlocks(ZStream z, int w) {
        this.end = w;
        this.window = new byte[w];
        this.check = z.istate.wrap != 0;
        this.mode = TYPE;
        this.bb = new int[1];
        this.tb = new int[1];
        this.hufts = new int[MANY * 3];
        this.codes = new InfCodes();
        this.inftree = new InfTree();
        reset(z);
    }

    public void free(ZStream z) {
        reset(z);
        window = null;
        hufts = null;
    }

    public void reset(ZStream z) {
        mode = TYPE;
        bitk = 0;
        bitb = 0;
        read = write = 0;
        if(check) z.adler.reset();
    }

    public void setDictionary(byte[] d, int start, int n) {
        Array.copy(d, start, window, 0, n);
        read = write = n;
    }

    public int syncPoint() {
        return mode == LENS ? 1 : 0;
    }

    public int proc(ZStream z, int r) {
        int t;
        int b;
        int k;
        int p;
        int n;
        int q;
        int m;
        p = z.nextInIndex;
        n = z.availIn;
        b = bitb;
        k = bitk;
        q = write;
        m = q < read ? read - q - 1 : end - q;
        do
        {
            switch(mode)
            {
            default:
                r = STREAM_ERROR;
                bitb = b;
                bitk = k;
                z.availIn = n;
                z.totalIn += p - z.nextInIndex;
                z.nextInIndex = p;
                write = q;
                return inflateFlush(z, r);
            case TYPE:
                while(k < 3)
                {
                    if(n != 0)
                    {
                        r = OK;
                    } else
                    {
                        bitb = b;
                        bitk = k;
                        z.availIn = n;
                        z.totalIn += p - z.nextInIndex;
                        z.nextInIndex = p;
                        write = q;
                        return inflateFlush(z, r);
                    }
                    n--;
                    b |= (z.nextIn[p++] & 0xff) << k;
                    k += 8;
                }
                t = b & 7;
                last = t & 1;
                switch(t >>> 1)
                {
                default:
                    break;
                case 0:
                    b >>>= 3;
                    k -= 3;
                    t = k & 7;
                    b >>>= t;
                    k -= t;
                    mode = LENS;
                    break;
                case 1:
                    int[] bl = new int[1];
                    int[] bd = new int[1];
                    int[][] tl = new int[1][];
                    int[][] td = new int[1][];
                    InfTree.inflateTreesFixed(bl, bd, tl, td);
                    codes.init(bl[0], bd[0], tl[0], 0, td[0], 0, z);
                    b >>>= 3;
                    k -= 3;
                    mode = CODES;
                    break;
                case 2:
                    b >>>= 3;
                    k -= 3;
                    mode = TABLE;
                    break;
                case 3:
                    b >>>= 3;
                    k -= 3;
                    mode = BAD;
                    z.message = "invalid block type";
                    r = DATA_ERROR;
                    bitb = b;
                    bitk = k;
                    z.availIn = n;
                    z.totalIn += p - z.nextInIndex;
                    z.nextInIndex = p;
                    write = q;
                    return inflateFlush(z, r);
                }
                break;
            case LENS:
                while(k < 32)
                {
                    if(n != 0)
                    {
                        r = OK;
                    } else
                    {
                        bitb = b;
                        bitk = k;
                        z.availIn = n;
                        z.totalIn += p - z.nextInIndex;
                        z.nextInIndex = p;
                        write = q;
                        return inflateFlush(z, r);
                    }
                    n--;
                    b |= (z.nextIn[p++] & 0xff) << k;
                    k += 8;
                }
                if(((~b >>> 16) & 0xffff) != (b & 0xffff))
                {
                    mode = BAD;
                    z.message = "invalid stored block lengths";
                    r = DATA_ERROR;
                    bitb = b;
                    bitk = k;
                    z.availIn = n;
                    z.totalIn += p - z.nextInIndex;
                    z.nextInIndex = p;
                    write = q;
                    return inflateFlush(z, r);
                }
                left = (b & 0xffff);
                b = k = 0;
                mode = left != 0 ? STORED : (last != 0 ? DRY : TYPE);
                break;
            case STORED:
                if(n == 0)
                {
                    bitb = b;
                    bitk = k;
                    z.availIn = n;
                    z.totalIn += p - z.nextInIndex;
                    z.nextInIndex = p;
                    write = q;
                    return inflateFlush(z, r);
                }
                if(m == 0)
                {
                    if(q == end && read != 0)
                    {
                        q = 0;
                        m = q < read ? read - q - 1 : end - q;
                    }
                    if(m == 0)
                    {
                        write = q;
                        r = inflateFlush(z, r);
                        q = write;
                        m = q < read ? read - q - 1 : end - q;
                        if(q == end && read != 0)
                        {
                            q = 0;
                            m = q < read ? read - q - 1 : end - q;
                        }
                        if(m == 0)
                        {
                            bitb = b;
                            bitk = k;
                            z.availIn = n;
                            z.totalIn += p - z.nextInIndex;
                            z.nextInIndex = p;
                            write = q;
                            return inflateFlush(z, r);
                        }
                    }
                }
                r = OK;
                t = left;
                if(t > n) t = n;
                if(t > m) t = m;
                Array.copy(z.nextIn, p, window, q, t);
                p += t;
                n -= t;
                q += t;
                m -= t;
                if((left -= t) != 0) break;
                mode = last != 0 ? DRY : TYPE;
                break;
            case TABLE:
                while(k < 14)
                {
                    if(n != 0)
                    {
                        r = OK;
                    } else
                    {
                        bitb = b;
                        bitk = k;
                        z.availIn = n;
                        z.totalIn += p - z.nextInIndex;
                        z.nextInIndex = p;
                        write = q;
                        return inflateFlush(z, r);
                    }
                    n--;
                    b |= (z.nextIn[p++] & 0xff) << k;
                    k += 8;
                }
                table = t = (b & 0x3fff);
                if((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29)
                {
                    mode = BAD;
                    z.message = "too many length or distance symbols";
                    r = DATA_ERROR;
                    bitb = b;
                    bitk = k;
                    z.availIn = n;
                    z.totalIn += p - z.nextInIndex;
                    z.nextInIndex = p;
                    write = q;
                    return inflateFlush(z, r);
                }
                t = (t & 0x1f) + ((t >> 5) & 0x1f) + 258;
                if(blens == null || blens.length < t)
                {
                    blens = new int[t];
                } else
                {
                    for(int i = 0; i < t; i++) blens[i] = 0;
                }
                b >>>= 14;
                k -= 14;
                index = 0;
                mode = BTREE;
                /* fall through */
            case BTREE:
                while(index < (table >>> 10) + 4)
                {
                    while(k < (3))
                    {
                        if(n != 0)
                        {
                            r = OK;
                        } else
                        {
                            bitb = b;
                            bitk = k;
                            z.availIn = n;
                            z.totalIn += p - z.nextInIndex;
                            z.nextInIndex = p;
                            write = q;
                            return inflateFlush(z, r);
                        }
                        n--;
                        b |= (z.nextIn[p++] & 0xff) << k;
                        k += 8;
                    }
                    blens[BORDER[index++]] = b & 7;
                    b >>>= 3;
                    k -= 3;
                }
                while(index < 19) blens[BORDER[index++]] = 0;
                bb[0] = 7;
                t = inftree.inflateTreesBits(blens, bb, tb, hufts, z);
                if(t != OK)
                {
                    r = t;
                    if(r == DATA_ERROR)
                    {
                        blens = null;
                        mode = BAD;
                    }
                    bitb = b;
                    bitk = k;
                    z.availIn = n;
                    z.totalIn += p - z.nextInIndex;
                    z.nextInIndex = p;
                    write = q;
                    return inflateFlush(z, r);
                }
                index = 0;
                mode = DTREE;
                /* fall through */
            case DTREE:
                do
                {
                    int i;
                    int j;
                    int c;
                    t = table;
                    if(index >= (t & 0x1f) + ((t >> 5) & 0x1f) + 258) break;
                    t = bb[0];
                    while(k < t)
                    {
                        if(n != 0)
                        {
                            r = OK;
                        } else
                        {
                            bitb = b;
                            bitk = k;
                            z.availIn = n;
                            z.totalIn += p - z.nextInIndex;
                            z.nextInIndex = p;
                            write = q;
                            return inflateFlush(z, r);
                        }
                        n--;
                        b |= (z.nextIn[p++] & 0xff) << k;
                        k += 8;
                    }
                    t = hufts[(tb[0] + (b & INFLATE_MASK[t])) * 3 + 1];
                    c = hufts[(tb[0] + (b & INFLATE_MASK[t])) * 3 + 2];
                    if(c < 16)
                    {
                        b >>>= t;
                        k -= t;
                        blens[index++] = c;
                    } else
                    {
                        i = c == 18 ? 7 : c - 14;
                        j = c == 18 ? 11 : 3;
                        while(k < t + i)
                        {
                            if(n != 0)
                            {
                                r = OK;
                            } else
                            {
                                bitb = b;
                                bitk = k;
                                z.availIn = n;
                                z.totalIn += p - z.nextInIndex;
                                z.nextInIndex = p;
                                write = q;
                                return inflateFlush(z, r);
                            }
                            n--;
                            b |= (z.nextIn[p++] & 0xff) << k;
                            k += 8;
                        }
                        b >>>= t;
                        k -= t;
                        j += b & INFLATE_MASK[i];
                        b >>>= i;
                        k -= i;
                        i = index;
                        t = table;
                        if(i + j > (t & 0x1f) + ((t >> 5) & 0x1f) + 258 || (c == 16 && i < 1))
                        {
                            blens = null;
                            mode = BAD;
                            z.message = "invalid bit length repeat";
                            r = DATA_ERROR;
                            bitb = b;
                            bitk = k;
                            z.availIn = n;
                            z.totalIn += p - z.nextInIndex;
                            z.nextInIndex = p;
                            write = q;
                            return inflateFlush(z, r);
                        }
                        c = c == 16 ? blens[i - 1] : 0;
                        do
                        {
                            blens[i++] = c;
                        } while(--j != 0);
                        index = i;
                    }
                } while(true);
                tb[0] = -1;
                int[] bl = new int[1];
                int[] bd = new int[1];
                int[] tl = new int[1];
                int[] td = new int[1];
                bl[0] = 9;
                bd[0] = 6;
                t = table;
                t = inftree.inflateTreesDynamic((t & 0x1f) + 257, ((t >> 5) & 0x1f) + 1, blens, bl, bd, tl, td, hufts, z);
                if(t != OK)
                {
                    if(t == DATA_ERROR)
                    {
                        blens = null;
                        mode = BAD;
                    }
                    r = t;
                    bitb = b;
                    bitk = k;
                    z.availIn = n;
                    z.totalIn += p - z.nextInIndex;
                    z.nextInIndex = p;
                    write = q;
                    return inflateFlush(z, r);
                }
                codes.init(bl[0], bd[0], hufts, tl[0], hufts, td[0], z);
                mode = CODES;
                /* fall through */
            case CODES:
                bitb = b;
                bitk = k;
                z.availIn = n;
                z.totalIn += p - z.nextInIndex;
                z.nextInIndex = p;
                write = q;
                if((r = codes.proc(this, z, r)) != STREAM_END) return inflateFlush(z, r);
                r = OK;
                p = z.nextInIndex;
                n = z.availIn;
                b = bitb;
                k = bitk;
                q = write;
                m = q < read ? read - q - 1 : end - q;
                if(last == 0)
                {
                    mode = TYPE;
                    break;
                }
                mode = DRY;
                /* fall through */
            case DRY:
                write = q;
                r = inflateFlush(z, r);
                q = write;
                m = q < read ? read - q - 1 : end - q;
                if(read != write)
                {
                    bitb = b;
                    bitk = k;
                    z.availIn = n;
                    z.totalIn += p - z.nextInIndex;
                    z.nextInIndex = p;
                    write = q;
                    return inflateFlush(z, r);
                }
                mode = DONE;
                /* fall through */
            case DONE:
                r = STREAM_END;
                bitb = b;
                bitk = k;
                z.availIn = n;
                z.totalIn += p - z.nextInIndex;
                z.nextInIndex = p;
                write = q;
                return inflateFlush(z, r);
            case BAD:
                r = DATA_ERROR;
                bitb = b;
                bitk = k;
                z.availIn = n;
                z.totalIn += p - z.nextInIndex;
                z.nextInIndex = p;
                write = q;
                return inflateFlush(z, r);
            }
        } while(true);
    }

    public int inflateFlush(ZStream z, int r) {
        int n;
        int p;
        int q;
        p = z.nextOutIndex;
        q = read;
        n = (q <= write ? write : end) - q;
        if(n > z.availOut) n = z.availOut;
        if(n != 0 && r == BUF_ERROR) r = OK;
        z.availOut -= n;
        z.totalOut += n;
        if(check) z.adler.update(window, q, n);
        Array.copy(window, q, z.nextOut, p, n);
        p += n;
        q += n;
        if(q == end)
        {
            q = 0;
            if(write == end) write = 0;
            n = write - q;
            if(n > z.availOut) n = z.availOut;
            if(n != 0 && r == BUF_ERROR) r = OK;
            z.availOut -= n;
            z.totalOut += n;
            if(check) z.adler.update(window, q, n);
            Array.copy(window, q, z.nextOut, p, n);
            p += n;
            q += n;
        }
        z.nextOutIndex = p;
        read = q;
        return r;
    }
}

final class InfCodes extends Zlib
{
    private static final int START = 0;
    private static final int LEN = 1;
    private static final int LENEXT = 2;
    private static final int DIST = 3;
    private static final int DISTEXT = 4;
    private static final int COPY = 5;
    private static final int LIT = 6;
    private static final int WASH = 7;
    private static final int END = 8;
    private static final int BADCODE = 9;

    private static final int[] INFLATE_MASK;

    static {
        INFLATE_MASK = new int[] {
            0x0000, 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff,
            0xffff
        };
    }

    private int lbits;
    private int dbits;
    private int mode;
    private int len;
    private int need;
    private int lit;
    private int get;
    private int dist;
    private int treeIndex;
    private int ltreeIndex;
    private int dtreeIndex;
    private int[] tree;
    private int[] ltree;
    private int[] dtree;

    public InfCodes() {
    }

    public void init(int bl, int bd, int[] tl, int tlIndex, int[] td, int tdIndex, ZStream z) {
        mode = START;
        lbits = (byte) bl;
        dbits = (byte) bd;
        ltree = tl;
        ltreeIndex = tlIndex;
        dtree = td;
        dtreeIndex = tdIndex;
        tree = null;
    }

    public int proc(InfBlocks s, ZStream z, int r) {
        int j;
        int tindex;
        int e;
        int b = 0;
        int k = 0;
        int p = 0;
        int n;
        int q;
        int m;
        int f;
        p = z.nextInIndex;
        n = z.availIn;
        b = s.bitb;
        k = s.bitk;
        q = s.write;
        m = q < s.read ? s.read - q - 1 : s.end - q;
        do
        {
            switch(mode)
            {
            default:
                r = STREAM_ERROR;
                s.bitb = b;
                s.bitk = k;
                z.availIn = n;
                z.totalIn += p - z.nextInIndex;
                z.nextInIndex = p;
                s.write = q;
                return s.inflateFlush(z, r);
            case START:
                if(m >= 258 && n >= 10)
                {
                    s.bitb = b;
                    s.bitk = k;
                    z.availIn = n;
                    z.totalIn += p - z.nextInIndex;
                    z.nextInIndex = p;
                    s.write = q;
                    r = inflateFast(lbits, dbits, ltree, ltreeIndex, dtree, dtreeIndex, s, z);
                    p = z.nextInIndex;
                    n = z.availIn;
                    b = s.bitb;
                    k = s.bitk;
                    q = s.write;
                    m = q < s.read ? s.read - q - 1 : s.end - q;
                    if(r != OK)
                    {
                        mode = r == STREAM_END ? WASH : BADCODE;
                        break;
                    }
                }
                need = lbits;
                tree = ltree;
                treeIndex = ltreeIndex;
                mode = LEN;
                /* fall through */
            case LEN:
                j = need;
                while(k < j)
                {
                    if(n != 0)
                    {
                        r = OK;
                    } else
                    {
                        s.bitb = b;
                        s.bitk = k;
                        z.availIn = n;
                        z.totalIn += p - z.nextInIndex;
                        z.nextInIndex = p;
                        s.write = q;
                        return s.inflateFlush(z, r);
                    }
                    n--;
                    b |= (z.nextIn[p++] & 0xff) << k;
                    k += 8;
                }
                tindex = (treeIndex + (b & INFLATE_MASK[j])) * 3;
                b >>>= tree[tindex + 1];
                k -= tree[tindex + 1];
                e = tree[tindex];
                if(e == 0)
                {
                    lit = tree[tindex + 2];
                    mode = LIT;
                    break;
                }
                if((e & 16) != 0)
                {
                    get = e & 15;
                    len = tree[tindex + 2];
                    mode = LENEXT;
                    break;
                }
                if((e & 64) == 0)
                {
                    need = e;
                    treeIndex = tindex / 3 + tree[tindex + 2];
                    break;
                }
                if((e & 32) != 0)
                {
                    mode = WASH;
                    break;
                }
                mode = BADCODE;
                z.message = "invalid literal/length code";
                r = DATA_ERROR;
                s.bitb = b;
                s.bitk = k;
                z.availIn = n;
                z.totalIn += p - z.nextInIndex;
                z.nextInIndex = p;
                s.write = q;
                return s.inflateFlush(z, r);
            case LENEXT:
                j = get;
                while(k < j)
                {
                    if(n != 0)
                    {
                        r = OK;
                    } else
                    {
                        s.bitb = b;
                        s.bitk = k;
                        z.availIn = n;
                        z.totalIn += p - z.nextInIndex;
                        z.nextInIndex = p;
                        s.write = q;
                        return s.inflateFlush(z, r);
                    }
                    n--;
                    b |= (z.nextIn[p++] & 0xff) << k;
                    k += 8;
                }
                len += (b & INFLATE_MASK[j]);
                b >>= j;
                k -= j;
                need = dbits;
                tree = dtree;
                treeIndex = dtreeIndex;
                mode = DIST;
                /* fall through */
            case DIST:
                j = need;
                while(k < j)
                {
                    if(n != 0)
                    {
                        r = OK;
                    } else
                    {
                        s.bitb = b;
                        s.bitk = k;
                        z.availIn = n;
                        z.totalIn += p - z.nextInIndex;
                        z.nextInIndex = p;
                        s.write = q;
                        return s.inflateFlush(z, r);
                    }
                    n--;
                    b |= (z.nextIn[p++] & 0xff) << k;
                    k += 8;
                }
                tindex = (treeIndex + (b & INFLATE_MASK[j])) * 3;
                b >>= tree[tindex + 1];
                k -= tree[tindex + 1];
                e = (tree[tindex]);
                if((e & 16) != 0)
                {
                    get = e & 15;
                    dist = tree[tindex + 2];
                    mode = DISTEXT;
                    break;
                }
                if((e & 64) == 0)
                {
                    need = e;
                    treeIndex = tindex / 3 + tree[tindex + 2];
                    break;
                }
                mode = BADCODE;
                z.message = "invalid distance code";
                r = DATA_ERROR;
                s.bitb = b;
                s.bitk = k;
                z.availIn = n;
                z.totalIn += p - z.nextInIndex;
                z.nextInIndex = p;
                s.write = q;
                return s.inflateFlush(z, r);
            case DISTEXT:
                j = get;
                while(k < j)
                {
                    if(n != 0)
                    {
                        r = OK;
                    } else
                    {
                        s.bitb = b;
                        s.bitk = k;
                        z.availIn = n;
                        z.totalIn += p - z.nextInIndex;
                        z.nextInIndex = p;
                        s.write = q;
                        return s.inflateFlush(z, r);
                    }
                    n--;
                    b |= (z.nextIn[p++] & 0xff) << k;
                    k += 8;
                }
                dist += b & INFLATE_MASK[j];
                b >>= j;
                k -= j;
                mode = COPY;
                /* fall through */
            case COPY:
                f = q - dist;
                while(f < 0) f += s.end;
                while(len != 0)
                {
                    if(m == 0)
                    {
                        if(q == s.end && s.read != 0)
                        {
                            q = 0;
                            m = q < s.read ? s.read - q - 1 : s.end - q;
                        }
                        if(m == 0)
                        {
                            s.write = q;
                            r = s.inflateFlush(z, r);
                            q = s.write;
                            m = q < s.read ? s.read - q - 1 : s.end - q;
                            if(q == s.end && s.read != 0)
                            {
                                q = 0;
                                m = q < s.read ? s.read - q - 1 : s.end - q;
                            }
                            if(m == 0)
                            {
                                s.bitb = b;
                                s.bitk = k;
                                z.availIn = n;
                                z.totalIn += p - z.nextInIndex;
                                z.nextInIndex = p;
                                s.write = q;
                                return s.inflateFlush(z, r);
                            }
                        }
                    }
                    s.window[q++] = s.window[f++];
                    m--;
                    if(f == s.end) f = 0;
                    len--;
                }
                mode = START;
                break;
            case LIT:
                if(m == 0)
                {
                    if(q == s.end && s.read != 0)
                    {
                        q = 0;
                        m = q < s.read ? s.read - q - 1 : s.end - q;
                    }
                    if(m == 0)
                    {
                        s.write = q;
                        r = s.inflateFlush(z, r);
                        q = s.write;
                        m = q < s.read ? s.read - q - 1 : s.end - q;
                        if(q == s.end && s.read != 0)
                        {
                            q = 0;
                            m = q < s.read ? s.read - q - 1 : s.end - q;
                        }
                        if(m == 0)
                        {
                            s.bitb = b;
                            s.bitk = k;
                            z.availIn = n;
                            z.totalIn += p - z.nextInIndex;
                            z.nextInIndex = p;
                            s.write = q;
                            return s.inflateFlush(z, r);
                        }
                    }
                }
                r = OK;
                s.window[q++] = (byte) lit;
                m--;
                mode = START;
                break;
            case WASH:
                if(k > 7)
                {
                    k -= 8;
                    n++;
                    p--;
                }
                s.write = q;
                r = s.inflateFlush(z, r);
                q = s.write;
                m = q < s.read ? s.read - q - 1 : s.end - q;
                if(s.read != s.write)
                {
                    s.bitb = b;
                    s.bitk = k;
                    z.availIn = n;
                    z.totalIn += p - z.nextInIndex;
                    z.nextInIndex = p;
                    s.write = q;
                    return s.inflateFlush(z, r);
                }
                mode = END;
                /* fall through */
            case END:
                r = STREAM_END;
                s.bitb = b;
                s.bitk = k;
                z.availIn = n;
                z.totalIn += p - z.nextInIndex;
                z.nextInIndex = p;
                s.write = q;
                return s.inflateFlush(z, r);
            case BADCODE:
                r = DATA_ERROR;
                s.bitb = b;
                s.bitk = k;
                z.availIn = n;
                z.totalIn += p - z.nextInIndex;
                z.nextInIndex = p;
                s.write = q;
                return s.inflateFlush(z, r);
            }
        } while(true);
    }

    private int inflateFast(int bl, int bd, int[] tl, int tlIndex, int[] td, int tdIndex, InfBlocks s, ZStream z) {
        int t;
        int tpIndex;
        int e;
        int b;
        int k;
        int p;
        int n;
        int q;
        int m;
        int ml;
        int md;
        int c;
        int d;
        int r;
        int tpIndexT3;
        int[] tp;
        p = z.nextInIndex;
        n = z.availIn;
        b = s.bitb;
        k = s.bitk;
        q = s.write;
        m = q < s.read ? s.read - q - 1 : s.end - q;
        ml = INFLATE_MASK[bl];
        md = INFLATE_MASK[bd];
        do
        {
            while(k < 20)
            {
                n--;
                b |= (z.nextIn[p++] & 0xff) << k;
                k += 8;
            }
            t = b & ml;
            tp = tl;
            tpIndex = tlIndex;
            tpIndexT3 = (tpIndex + t) * 3;
            if((e = tp[tpIndexT3]) == 0)
            {
                b >>= (tp[tpIndexT3 + 1]);
                k -= (tp[tpIndexT3 + 1]);
                s.window[q++] = (byte) tp[tpIndexT3 + 2];
                m--;
                continue;
            }
            do
            {
                b >>= tp[tpIndexT3 + 1];
                k -= tp[tpIndexT3 + 1];
                if((e & 16) != 0)
                {
                    e &= 15;
                    c = tp[tpIndexT3 + 2] + (b & INFLATE_MASK[e]);
                    b >>= e;
                    k -= e;
                    while(k < 15)
                    {
                        n--;
                        b |= (z.nextIn[p++] & 0xff) << k;
                        k += 8;
                    }
                    t = b & md;
                    tp = td;
                    tpIndex = tdIndex;
                    tpIndexT3 = (tpIndex + t) * 3;
                    e = tp[tpIndexT3];
                    do
                    {
                        b >>= tp[tpIndexT3 + 1];
                        k -= tp[tpIndexT3 + 1];
                        if((e & 16) != 0)
                        {
                            e &= 15;
                            while(k < e)
                            {
                                n--;
                                b |= (z.nextIn[p++] & 0xff) << k;
                                k += 8;
                            }
                            d = tp[tpIndexT3 + 2] + (b & INFLATE_MASK[e]);
                            b >>= e;
                            k -= e;
                            m -= c;
                            if(q >= d)
                            {
                                r = q - d;
                                if(q - r > 0 && q - r < 2)
                                {
                                    s.window[q++] = s.window[r++];
                                    s.window[q++] = s.window[r++];
                                    c -= 2;
                                } else
                                {
                                    Array.copy(s.window, r, s.window, q, 2);
                                    q += 2;
                                    r += 2;
                                    c -= 2;
                                }
                            } else
                            {
                                r = q - d;
                                do
                                {
                                    r += s.end;
                                } while(r < 0);
                                e = s.end - r;
                                if(c > e)
                                {
                                    c -= e;
                                    if(q - r > 0 && e > q - r)
                                    {
                                        do
                                        {
                                            s.window[q++] = s.window[r++];
                                        } while(--e != 0);
                                    } else
                                    {
                                        Array.copy(s.window, r, s.window, q, e);
                                        q += e;
                                        r += e;
                                        e = 0;
                                    }
                                    r = 0;
                                }
                            }
                            if(q - r > 0 && c > q - r)
                            {
                                do
                                {
                                    s.window[q++] = s.window[r++];
                                } while(--c != 0);
                            } else
                            {
                                Array.copy(s.window, r, s.window, q, c);
                                q += c;
                                r += c;
                                c = 0;
                            }
                            break;
                        }
                        else if((e & 64) == 0)
                        {
                            t += tp[tpIndexT3 + 2];
                            t += (b & INFLATE_MASK[e]);
                            tpIndexT3 = (tpIndex + t) * 3;
                            e = tp[tpIndexT3];
                        }
                        else
                        {
                            z.message = "invalid distance code";
                            c = z.availIn - n;
                            c = (k >> 3) < c ? k >> 3 : c;
                            n += c;
                            p -= c;
                            k -= c << 3;
                            s.bitb = b;
                            s.bitk = k;
                            z.availIn = n;
                            z.totalIn += p - z.nextInIndex;
                            z.nextInIndex = p;
                            s.write = q;
                            return DATA_ERROR;
                        }
                    } while(true);
                    break;
                }
                if((e & 64) == 0)
                {
                    t += tp[tpIndexT3 + 2];
                    t += (b & INFLATE_MASK[e]);
                    tpIndexT3 = (tpIndex + t) * 3;
                    if((e = tp[tpIndexT3]) == 0)
                    {
                        b >>= tp[tpIndexT3 + 1];
                        k -= tp[tpIndexT3 + 1];
                        s.window[q++] = (byte) tp[tpIndexT3 + 2];
                        m--;
                        break;
                    }
                }
                else if((e & 32) != 0)
                {
                    c = z.availIn - n;
                    c = (k >> 3) < c ? k >> 3 : c;
                    n += c;
                    p -= c;
                    k -= c << 3;
                    s.bitb = b;
                    s.bitk = k;
                    z.availIn = n;
                    z.totalIn += p - z.nextInIndex;
                    z.nextInIndex = p;
                    s.write = q;
                    return STREAM_END;
                }
                else
                {
                    z.message = "invalid literal/length code";
                    c = z.availIn - n;
                    c = (k >> 3) < c ? k >> 3 : c;
                    n += c;
                    p -= c;
                    k -= c << 3;
                    s.bitb = b;
                    s.bitk = k;
                    z.availIn = n;
                    z.totalIn += p - z.nextInIndex;
                    z.nextInIndex = p;
                    s.write = q;
                    return DATA_ERROR;
                }
            } while(true);
        } while(m >= 258 && n >= 10);
        c = z.availIn - n;
        c = (k >> 3) < c ? k >> 3 : c;
        n += c;
        p -= c;
        k -= c << 3;
        s.bitb = b;
        s.bitk = k;
        z.availIn = n;
        z.totalIn += p - z.nextInIndex;
        z.nextInIndex = p;
        s.write = q;
        return OK;
    }
}

final class Inflate extends Zlib
{
    private static final int PRESET_DICT = 0x20;
    private static final int Z_DEFLATED = 8;
    private static final int DICT4 = 2;
    private static final int DICT3 = 3;
    private static final int DICT2 = 4;
    private static final int DICT1 = 5;
    private static final int DICT0 = 6;
    private static final int BLOCKS = 7;
    private static final int CHECK4 = 8;
    private static final int CHECK3 = 9;
    private static final int CHECK2 = 10;
    private static final int CHECK1 = 11;
    private static final int DONE = 12;
    private static final int BAD = 13;
    private static final int HEAD = 14;
    private static final int LENGTH = 15;
    private static final int TIME = 16;
    private static final int OS = 17;
    private static final int EXLEN = 18;
    private static final int EXTRA = 19;
    private static final int NAME = 20;
    private static final int COMMENT = 21;
    private static final int HCRC = 22;
    private static final int FLAGS = 23;

    private static final byte[] MARK;

    static {
        MARK = new byte[] { 0, 0, -1, -1 };
    }

    private boolean empty;
    public int mode;
    public int wrap;
    private int method;
    private int marker;
    private int wbits;
    private int was;
    private int need;
    private int flags;
    private int needBytes;
    private byte[] crcbuf;
    private ByteArrayOutputStream tmpString;
    private InfBlocks blocks;
    private GZIPHeader gheader;
    private ZStream stream;

    public Inflate(ZStream stream) {
        this.needBytes = -1;
        this.was = -1;
        this.crcbuf = new byte[4];
        this.stream = stream;
    }

    public int inflateInit(int w) {
        stream.message = null;
        blocks = null;
        wrap = 0;
        if(w < 0)
        {
            w = -w;
        } else
        {
            wrap = (w >> 4) + 1;
            if(w < 48) w &= 15;
        }
        if(w < 8 || w > 15)
        {
            inflateEnd();
            return STREAM_ERROR;
        }
        if(blocks != null && wbits != w)
        {
            blocks.free(stream);
            blocks = null;
        }
        wbits = w;
        blocks = new InfBlocks(stream, 1 << w);
        inflateReset();
        return OK;
    }

    public int inflateSync() {
        int n;
        int p;
        int m;
        long r;
        long w;
        if(stream == null) return STREAM_ERROR;
        if(mode != BAD)
        {
            mode = BAD;
            marker = 0;
        }
        if((n = stream.availIn) == 0) return BUF_ERROR;
        p = stream.nextInIndex;
        m = marker;
        while(n != 0 && m < 4)
        {
            if(stream.nextIn[p] == MARK[m])
            {
                m++;
            }
            else if(stream.nextIn[p] != 0)
            {
                m = 0;
            }
            else
            {
                m = 4 - m;
            }
            p++;
            n--;
        }
        stream.totalIn += p - stream.nextInIndex;
        stream.nextInIndex = p;
        stream.availIn = n;
        marker = m;
        if(m != 4) return DATA_ERROR;
        r = stream.totalIn;
        w = stream.totalOut;
        inflateReset();
        stream.totalIn = r;
        stream.totalOut = w;
        mode = BLOCKS;
        return OK;
    }

    public int inflateSyncPoint() {
        return stream == null || blocks == null ? STREAM_ERROR : blocks.syncPoint();
    }

    public int inflateSetDictionary(byte[] dictionary, int dictLength) {
        int index;
        int length;
        int adlerNeed;
        if(stream == null || (mode != DICT0 && wrap != 0)) return STREAM_ERROR;
        index = 0;
        length = dictLength;
        if(mode == DICT0)
        {
            adlerNeed = stream.adler.value();
            stream.adler.reset();
            stream.adler.update(dictionary, 0, dictLength);
            if(stream.adler.value() != adlerNeed) return DATA_ERROR;
        }
        stream.adler.reset();
        if(length >= (1 << wbits))
        {
            length = (1 << wbits) - 1;
            index = dictLength - length;
        }
        blocks.setDictionary(dictionary, index, length);
        mode = BLOCKS;
        return OK;
    }

    public int inflate(int f) {
        int r;
        int b;
        if(stream == null || stream.nextIn == null) return f == FINISH && mode == HEAD ? OK : STREAM_ERROR;
        f = f == FINISH ? BUF_ERROR : OK;
        r = BUF_ERROR;
        do
        {
            switch(mode)
            {
            default:
                return STREAM_ERROR;
            case HEAD:
                if(wrap == 0)
                {
                    mode = BLOCKS;
                    break;
                }
                r = readBytes(stream, 2, r, f);
                if(empty) return r;
                if((wrap & 2) != 0 && need == 0x8b1fL)
                {
                    stream.adler = new CRC32();
                    checksum(2, need);
                    if(gheader == null) gheader = new GZIPHeader();
                    mode = FLAGS;
                    break;
                }
                flags = 0;
                method = need & 0xff;
                b = (need >> 8) & 0xff;
                if((wrap & 1) == 0 || (((method << 8) + b) % 31) != 0)
                {
                    mode = BAD;
                    stream.message = "incorrect header check";
                    break;
                }
                if((method & 0xf) != Z_DEFLATED)
                {
                    mode = BAD;
                    stream.message = "unknown compression method";
                    break;
                }
                if((method >> 4) + 8 > wbits)
                {
                    mode = BAD;
                    stream.message = "invalid window size";
                    break;
                }
                stream.adler = new Adler32();
                if((b & PRESET_DICT) == 0)
                {
                    mode = BLOCKS;
                    break;
                }
                mode = DICT4;
                /* fall through */
            case DICT4:
                if(stream.availIn == 0) return r;
                r = f;
                stream.availIn--;
                stream.totalIn++;
                need = ((stream.nextIn[stream.nextInIndex++] & 0xff) << 24) & 0xff000000;
                mode = DICT3;
                /* fall through */
            case DICT3:
                if(stream.availIn == 0) return r;
                r = f;
                stream.availIn--;
                stream.totalIn++;
                need += ((stream.nextIn[stream.nextInIndex++] & 0xff) << 16) & 0xff0000;
                mode = DICT2;
                /* fall through */
            case DICT2:
                if(stream.availIn == 0) return r;
                r = f;
                stream.availIn--;
                stream.totalIn++;
                need += ((stream.nextIn[stream.nextInIndex++] & 0xff) << 8) & 0xff00;
                mode = DICT1;
                /* fall through */
            case DICT1:
                if(stream.availIn == 0) return r;
                r = f;
                stream.availIn--;
                stream.totalIn++;
                need += (stream.nextIn[stream.nextInIndex++] & 0xffL);
                stream.adler.reset(need);
                mode = DICT0;
                return NEED_DICT;
            case DICT0:
                mode = BAD;
                stream.message = "need dictionary";
                marker = 0;
                return STREAM_ERROR;
            case BLOCKS:
                r = blocks.proc(stream, r);
                if(r == DATA_ERROR)
                {
                    mode = BAD;
                    marker = 0;
                    break;
                }
                if(r == OK) r = f;
                if(r != STREAM_END) return r;
                r = f;
                was = stream.adler.value();
                blocks.reset(stream);
                if(wrap == 0)
                {
                    mode = DONE;
                    break;
                }
                mode = CHECK4;
                /* fall through */
            case CHECK4:
                if(stream.availIn == 0) return r;
                r = f;
                stream.availIn--;
                stream.totalIn++;
                need = ((stream.nextIn[stream.nextInIndex++] & 0xff) << 24) & 0xff000000;
                mode = CHECK3;
                /* fall through */
            case CHECK3:
                if(stream.availIn == 0) return r;
                r = f;
                stream.availIn--;
                stream.totalIn++;
                need += ((stream.nextIn[stream.nextInIndex++] & 0xff) << 16) & 0xff0000;
                mode = CHECK2;
                /* fall through */
            case CHECK2:
                if(stream.availIn == 0) return r;
                r = f;
                stream.availIn--;
                stream.totalIn++;
                need += ((stream.nextIn[stream.nextInIndex++] & 0xff) << 8) & 0xff00;
                mode = CHECK1;
                /* fall through */
            case CHECK1:
                if(stream.availIn == 0) return r;
                r = f;
                stream.availIn--;
                stream.totalIn++;
                need += (stream.nextIn[stream.nextInIndex++] & 0xff);
                if(flags != 0) need = (need & 0xff000000) >>> 24 | (need & 0x00ff0000) >>> 8 | (need & 0x0000ff00) << 8 | (need & 0x000000ff) << 24;
                if(was != need)
                {
                    stream.message = "incorrect data check";
                }
                else if(flags != 0 && gheader != null)
                {
                    gheader.crc = need;
                }
                mode = LENGTH;
                /* fall through */
            case LENGTH:
                if(wrap != 0 && flags != 0)
                {
                    r = readBytes(stream, 4, r, f);
                    if(empty) return r;
                    if(stream.message != null && stream.message.equals("incorrect data check"))
                    {
                        mode = BAD;
                        marker = 5;
                        break;
                    }
                    if(need != (stream.totalOut & 0xffffffffL))
                    {
                        stream.message = "incorrect length check";
                        mode = BAD;
                        break;
                    }
                    stream.message = null;
                } else
                {
                    if(stream.message != null && stream.message.equals("incorrect data check"))
                    {
                        mode = BAD;
                        marker = 5;
                        break;
                    }
                }
                mode = DONE;
                /* fall through */
            case DONE:
                return STREAM_END;
            case BAD:
                return DATA_ERROR;
            case FLAGS:
                r = readBytes(stream, 2, r, f);
                if(empty) return r;
                flags = need & 0xffff;
                if((flags & 0xff) != Z_DEFLATED)
                {
                    stream.message = "unknown compression method";
                    mode = BAD;
                    break;
                }
                if((flags & 0xe000) != 0)
                {
                    stream.message = "unknown header flags set";
                    mode = BAD;
                    break;
                }
                if((flags & 0x0200) != 0) checksum(2, need);
                mode = TIME;
                /* fall through */
            case TIME:
                r = readBytes(stream, 4, r, f);
                if(empty) return r;
                if(gheader != null) gheader.time = need;
                if((flags & 0x0200) != 0) checksum(4, need);
                mode = OS;
                /* fall through */
            case OS:
                r = readBytes(stream, 2, r, f);
                if(empty) return r;
                if(gheader != null)
                {
                    gheader.xflags = need & 0xff;
                    gheader.os = (need >> 8) & 0xff;
                }
                if((flags & 0x0200) != 0) checksum(2, need);
                mode = EXLEN;
                /* fall through */
            case EXLEN:
                if((flags & 0x0400) != 0)
                {
                    r = readBytes(stream, 2, r, f);
                    if(empty) return r;
                    if(gheader != null) gheader.extra = new byte[need & 0xffff];
                    if((flags & 0x0200) != 0) checksum(2, need);
                }
                else if(gheader != null)
                {
                    gheader.extra = null;
                }
                mode = EXTRA;
                /* fall through */
            case EXTRA:
                if((flags & 0x0400) != 0)
                {
                    r = readBytes(stream, r, f);
                    if(empty) return r;
                    if(gheader != null)
                    {
                        byte[] foo = tmpString.toByteArray();
                        tmpString = null;
                        if(foo.length == gheader.extra.length)
                        {
                            Array.copy(foo, 0, gheader.extra, 0, foo.length);
                        } else
                        {
                            stream.message = "bad extra field length";
                            mode = BAD;
                            break;
                        }
                    }
                }
                else if(gheader != null)
                {
                    gheader.extra = null;
                }
                mode = NAME;
                /* fall through */
            case NAME:
                if((flags & 0x0800) != 0)
                {
                    r = readString(stream, r, f);
                    if(empty) return r;
                    if(gheader != null) gheader.name = tmpString.toByteArray();
                    tmpString = null;
                }
                else if(gheader != null)
                {
                    gheader.name = null;
                }
                mode = COMMENT;
                /* fall through */
            case COMMENT:
                if((flags & 0x1000) != 0)
                {
                    r = readString(stream, r, f);
                    if(empty) return r;
                    if(gheader != null) gheader.comment = tmpString.toByteArray();
                    tmpString = null;
                }
                else if(gheader != null)
                {
                    gheader.comment = null;
                }
                mode = HCRC;
                /* fall through */
            case HCRC:
                if((flags & 0x0200) != 0)
                {
                    r = readBytes(stream, 2, r, f);
                    if(empty) return r;
                    if(gheader != null) gheader.hcrc = need & 0xffff;
                    if(need != (stream.adler.value() & 0xffffL))
                    {
                        mode = BAD;
                        stream.message = "header crc mismatch";
                        marker = 5;
                        break;
                    }
                }
                stream.adler = new CRC32();
                mode = BLOCKS;
                break;
            }
        } while(true);
    }

    public int inflateEnd() {
        if(blocks != null) blocks.free(stream);
        return OK;
    }

    private void checksum(int n, int v) {
        int i;
        for(i = 0; i < n; i++)
        {
            crcbuf[i] = (byte) (v & 0xff);
            v >>= 8;
        }
        stream.adler.update(crcbuf, 0, n);
    }

    private int readBytes(ZStream z, int n, int r, int f) {
        if(needBytes == -1)
        {
            needBytes = n;
            need = 0;
        }
        while(needBytes > 0)
        {
            if(z.availIn == 0)
            {
                empty = true;
                return r;
            }
            r = f;
            z.availIn--;
            z.totalIn++;
            need = need | ((z.nextIn[z.nextInIndex++] & 0xff) << ((n - needBytes) * 8));
            needBytes--;
        }
        if(n == 2)
        {
            need &= 0xffffL;
        }
        else if(n == 4)
        {
            need &= 0xffffffff;
        }
        needBytes = -1;
        empty = false;
        return r;
    }

    private int readBytes(ZStream z, int r, int f) {
        if(tmpString == null) tmpString = new ByteArrayOutputStream();
        while(need > 0)
        {
            if(z.availIn == 0)
            {
                empty = true;
                return r;
            }
            r = f;
            z.availIn--;
            z.totalIn++;
            tmpString.write(z.nextIn, z.nextInIndex, 1);
            z.adler.update(z.nextIn, z.nextInIndex, 1);
            z.nextInIndex++;
            need--;
        }
        empty = false;
        return r;
    }

    private int readString(ZStream z, int r, int f) {
        if(tmpString == null) tmpString = new ByteArrayOutputStream();
        int b = 0;
        do
        {
            if(z.availIn == 0)
            {
                empty = true;
                return r;
            }
            r = f;
            z.availIn--;
            z.totalIn++;
            b = z.nextIn[z.nextInIndex];
            if(b != 0) tmpString.write(z.nextIn, z.nextInIndex, 1);
            z.adler.update(z.nextIn, z.nextInIndex, 1);
            z.nextInIndex++;
        } while(b != 0);
        empty = false;
        return r;
    }

    private int inflateReset() {
        if(stream == null) return STREAM_ERROR;
        stream.totalIn = stream.totalOut = 0;
        stream.message = null;
        mode = HEAD;
        needBytes = -1;
        blocks.reset(stream);
        return OK;
    }
}

final class InfTree extends Zlib
{
    private static final int MANY = 1440;
    private static final int BMAX = 15;
    private static final int FIXED_BL = 9;
    private static final int FIXED_BD = 5;

    private static final int[] FIXED_TL;
    private static final int[] FIXED_TD;
    private static final int[] CPLENS;
    private static final int[] CPLEXT;
    private static final int[] CPDIST;
    private static final int[] CPDEXT;

    static {
        FIXED_TL = new int[] {
                96,      7,    256,      0,      8,     80,      0,      8,     16,     84,      8,    115,     82,      7,     31,      0,
                 8,    112,      0,      8,     48,      0,      9,    192,     80,      7,     10,      0,      8,     96,      0,      8,
                32,      0,      9,    160,      0,      8,      0,      0,      8,    128,      0,      8,     64,      0,      9,    224,
                80,      7,      6,      0,      8,     88,      0,      8,     24,      0,      9,    144,     83,      7,     59,      0,
                 8,    120,      0,      8,     56,      0,      9,    208,     81,      7,     17,      0,      8,    104,      0,      8,
                40,      0,      9,    176,      0,      8,      8,      0,      8,    136,      0,      8,     72,      0,      9,    240,
                80,      7,      4,      0,      8,     84,      0,      8,     20,     85,      8,    227,     83,      7,     43,      0,
                 8,    116,      0,      8,     52,      0,      9,    200,     81,      7,     13,      0,      8,    100,      0,      8,
                36,      0,      9,    168,      0,      8,      4,      0,      8,    132,      0,      8,     68,      0,      9,    232,
                80,      7,      8,      0,      8,     92,      0,      8,     28,      0,      9,    152,     84,      7,     83,      0,
                 8,    124,      0,      8,     60,      0,      9,    216,     82,      7,     23,      0,      8,    108,      0,      8,
                44,      0,      9,    184,      0,      8,     12,      0,      8,    140,      0,      8,     76,      0,      9,    248,
                80,      7,      3,      0,      8,     82,      0,      8,     18,     85,      8,    163,     83,      7,     35,      0,
                 8,    114,      0,      8,     50,      0,      9,    196,     81,      7,     11,      0,      8,     98,      0,      8,
                34,      0,      9,    164,      0,      8,      2,      0,      8,    130,      0,      8,     66,      0,      9,    228,
                80,      7,      7,      0,      8,     90,      0,      8,     26,      0,      9,    148,     84,      7,     67,      0,
                 8,    122,      0,      8,     58,      0,      9,    212,     82,      7,     19,      0,      8,    106,      0,      8,
                42,      0,      9,    180,      0,      8,     10,      0,      8,    138,      0,      8,     74,      0,      9,    244,
                80,      7,      5,      0,      8,     86,      0,      8,     22,    192,      8,      0,     83,      7,     51,      0,
                 8,    118,      0,      8,     54,      0,      9,    204,     81,      7,     15,      0,      8,    102,      0,      8,
                38,      0,      9,    172,      0,      8,      6,      0,      8,    134,      0,      8,     70,      0,      9,    236,
                80,      7,      9,      0,      8,     94,      0,      8,     30,      0,      9,    156,     84,      7,     99,      0,
                 8,    126,      0,      8,     62,      0,      9,    220,     82,      7,     27,      0,      8,    110,      0,      8,
                46,      0,      9,    188,      0,      8,     14,      0,      8,    142,      0,      8,     78,      0,      9,    252,
                96,      7,    256,      0,      8,     81,      0,      8,     17,     85,      8,    131,     82,      7,     31,      0,
                 8,    113,      0,      8,     49,      0,      9,    194,     80,      7,     10,      0,      8,     97,      0,      8,
                33,      0,      9,    162,      0,      8,      1,      0,      8,    129,      0,      8,     65,      0,      9,    226,
                80,      7,      6,      0,      8,     89,      0,      8,     25,      0,      9,    146,     83,      7,     59,      0,
                 8,    121,      0,      8,     57,      0,      9,    210,     81,      7,     17,      0,      8,    105,      0,      8,
                41,      0,      9,    178,      0,      8,      9,      0,      8,    137,      0,      8,     73,      0,      9,    242,
                80,      7,      4,      0,      8,     85,      0,      8,     21,     80,      8,    258,     83,      7,     43,      0,
                 8,    117,      0,      8,     53,      0,      9,    202,     81,      7,     13,      0,      8,    101,      0,      8,
                37,      0,      9,    170,      0,      8,      5,      0,      8,    133,      0,      8,     69,      0,      9,    234,
                80,      7,      8,      0,      8,     93,      0,      8,     29,      0,      9,    154,     84,      7,     83,      0,
                 8,    125,      0,      8,     61,      0,      9,    218,     82,      7,     23,      0,      8,    109,      0,      8,
                45,      0,      9,    186,      0,      8,     13,      0,      8,    141,      0,      8,     77,      0,      9,    250,
                80,      7,      3,      0,      8,     83,      0,      8,     19,     85,      8,    195,     83,      7,     35,      0,
                 8,    115,      0,      8,     51,      0,      9,    198,     81,      7,     11,      0,      8,     99,      0,      8,
                35,      0,      9,    166,      0,      8,      3,      0,      8,    131,      0,      8,     67,      0,      9,    230,
                80,      7,      7,      0,      8,     91,      0,      8,     27,      0,      9,    150,     84,      7,     67,      0,
                 8,    123,      0,      8,     59,      0,      9,    214,     82,      7,     19,      0,      8,    107,      0,      8,
                43,      0,      9,    182,      0,      8,     11,      0,      8,    139,      0,      8,     75,      0,      9,    246,
                80,      7,      5,      0,      8,     87,      0,      8,     23,    192,      8,      0,     83,      7,     51,      0,
                 8,    119,      0,      8,     55,      0,      9,    206,     81,      7,     15,      0,      8,    103,      0,      8,
                39,      0,      9,    174,      0,      8,      7,      0,      8,    135,      0,      8,     71,      0,      9,    238,
                80,      7,      9,      0,      8,     95,      0,      8,     31,      0,      9,    158,     84,      7,     99,      0,
                 8,    127,      0,      8,     63,      0,      9,    222,     82,      7,     27,      0,      8,    111,      0,      8,
                47,      0,      9,    190,      0,      8,     15,      0,      8,    143,      0,      8,     79,      0,      9,    254,
                96,      7,    256,      0,      8,     80,      0,      8,     16,     84,      8,    115,     82,      7,     31,      0,
                 8,    112,      0,      8,     48,      0,      9,    193,     80,      7,     10,      0,      8,     96,      0,      8,
                32,      0,      9,    161,      0,      8,      0,      0,      8,    128,      0,      8,     64,      0,      9,    225,
                80,      7,      6,      0,      8,     88,      0,      8,     24,      0,      9,    145,     83,      7,     59,      0,
                 8,    120,      0,      8,     56,      0,      9,    209,     81,      7,     17,      0,      8,    104,      0,      8,
                40,      0,      9,    177,      0,      8,      8,      0,      8,    136,      0,      8,     72,      0,      9,    241,
                80,      7,      4,      0,      8,     84,      0,      8,     20,     85,      8,    227,     83,      7,     43,      0,
                 8,    116,      0,      8,     52,      0,      9,    201,     81,      7,     13,      0,      8,    100,      0,      8,
                36,      0,      9,    169,      0,      8,      4,      0,      8,    132,      0,      8,     68,      0,      9,    233,
                80,      7,      8,      0,      8,     92,      0,      8,     28,      0,      9,    153,     84,      7,     83,      0,
                 8,    124,      0,      8,     60,      0,      9,    217,     82,      7,     23,      0,      8,    108,      0,      8,
                44,      0,      9,    185,      0,      8,     12,      0,      8,    140,      0,      8,     76,      0,      9,    249,
                80,      7,      3,      0,      8,     82,      0,      8,     18,     85,      8,    163,     83,      7,     35,      0,
                 8,    114,      0,      8,     50,      0,      9,    197,     81,      7,     11,      0,      8,     98,      0,      8,
                34,      0,      9,    165,      0,      8,      2,      0,      8,    130,      0,      8,     66,      0,      9,    229,
                80,      7,      7,      0,      8,     90,      0,      8,     26,      0,      9,    149,     84,      7,     67,      0,
                 8,    122,      0,      8,     58,      0,      9,    213,     82,      7,     19,      0,      8,    106,      0,      8,
                42,      0,      9,    181,      0,      8,     10,      0,      8,    138,      0,      8,     74,      0,      9,    245,
                80,      7,      5,      0,      8,     86,      0,      8,     22,    192,      8,      0,     83,      7,     51,      0,
                 8,    118,      0,      8,     54,      0,      9,    205,     81,      7,     15,      0,      8,    102,      0,      8,
                38,      0,      9,    173,      0,      8,      6,      0,      8,    134,      0,      8,     70,      0,      9,    237,
                80,      7,      9,      0,      8,     94,      0,      8,     30,      0,      9,    157,     84,      7,     99,      0,
                 8,    126,      0,      8,     62,      0,      9,    221,     82,      7,     27,      0,      8,    110,      0,      8,
                46,      0,      9,    189,      0,      8,     14,      0,      8,    142,      0,      8,     78,      0,      9,    253,
                96,      7,    256,      0,      8,     81,      0,      8,     17,     85,      8,    131,     82,      7,     31,      0,
                 8,    113,      0,      8,     49,      0,      9,    195,     80,      7,     10,      0,      8,     97,      0,      8,
                33,      0,      9,    163,      0,      8,      1,      0,      8,    129,      0,      8,     65,      0,      9,    227,
                80,      7,      6,      0,      8,     89,      0,      8,     25,      0,      9,    147,     83,      7,     59,      0,
                 8,    121,      0,      8,     57,      0,      9,    211,     81,      7,     17,      0,      8,    105,      0,      8,
                41,      0,      9,    179,      0,      8,      9,      0,      8,    137,      0,      8,     73,      0,      9,    243,
                80,      7,      4,      0,      8,     85,      0,      8,     21,     80,      8,    258,     83,      7,     43,      0,
                 8,    117,      0,      8,     53,      0,      9,    203,     81,      7,     13,      0,      8,    101,      0,      8,
                37,      0,      9,    171,      0,      8,      5,      0,      8,    133,      0,      8,     69,      0,      9,    235,
                80,      7,      8,      0,      8,     93,      0,      8,     29,      0,      9,    155,     84,      7,     83,      0,
                 8,    125,      0,      8,     61,      0,      9,    219,     82,      7,     23,      0,      8,    109,      0,      8,
                45,      0,      9,    187,      0,      8,     13,      0,      8,    141,      0,      8,     77,      0,      9,    251,
                80,      7,      3,      0,      8,     83,      0,      8,     19,     85,      8,    195,     83,      7,     35,      0,
                 8,    115,      0,      8,     51,      0,      9,    199,     81,      7,     11,      0,      8,     99,      0,      8,
                35,      0,      9,    167,      0,      8,      3,      0,      8,    131,      0,      8,     67,      0,      9,    231,
                80,      7,      7,      0,      8,     91,      0,      8,     27,      0,      9,    151,     84,      7,     67,      0,
                 8,    123,      0,      8,     59,      0,      9,    215,     82,      7,     19,      0,      8,    107,      0,      8,
                43,      0,      9,    183,      0,      8,     11,      0,      8,    139,      0,      8,     75,      0,      9,    247,
                80,      7,      5,      0,      8,     87,      0,      8,     23,    192,      8,      0,     83,      7,     51,      0,
                 8,    119,      0,      8,     55,      0,      9,    207,     81,      7,     15,      0,      8,    103,      0,      8,
                39,      0,      9,    175,      0,      8,      7,      0,      8,    135,      0,      8,     71,      0,      9,    239,
                80,      7,      9,      0,      8,     95,      0,      8,     31,      0,      9,    159,     84,      7,     99,      0,
                 8,    127,      0,      8,     63,      0,      9,    223,     82,      7,     27,      0,      8,    111,      0,      8,
                47,      0,      9,    191,      0,      8,     15,      0,      8,    143,      0,      8,     79,      0,      9,    255
        };
        FIXED_TD = new int[] {
                80,      5,      1,     87,      5,    257,     83,      5,     17,     91,      5,   4097,     81,      5,      5,     89,
                 5,   1025,     85,      5,     65,     93,      5,  16385,     80,      5,      3,     88,      5,    513,     84,      5,
                33,     92,      5,   8193,     82,      5,      9,     90,      5,   2049,     86,      5,    129,    192,      5,  24577,
                80,      5,      2,     87,      5,    385,     83,      5,     25,     91,      5,   6145,     81,      5,      7,     89,
                 5,   1537,     85,      5,     97,     93,      5,  24577,     80,      5,      4,     88,      5,    769,     84,      5,
                49,     92,      5,  12289,     82,      5,     13,     90,      5,   3073,     86,      5,    193,    192,      5,  24577
        };
        CPLENS = new int[] {
                 3,      4,      5,      6,      7,      8,      9,     10,     11,     13,     15,     17,     19,     23,     27,     31,
                35,     43,     51,     59,     67,     83,     99,    115,    131,    163,    195,    227,    258,      0,      0
        };
        CPLEXT = new int[] {
                 0,      0,      0,      0,      0,      0,      0,      0,      1,      1,      1,      1,      2,      2,      2,      2,
                 3,      3,      3,      3,      4,      4,      4,      4,      5,      5,      5,      5,      0,    112,    112
        };
        CPDIST = new int[] {
                 1,      2,      3,      4,      5,      7,      9,     13,     17,     25,     33,     49,     65,     97,    129,    193,
               257,    385,    513,    769,   1025,   1537,   2049,   3073,   4097,   6145,   8193,  12289,  16385,  24577
        };
        CPDEXT = new int[] {
                 0,      0,      0,      0,      1,      1,      2,      2,      3,      3,      4,      4,      5,      5,      6,      6,
                 7,      7,      8,      8,      9,      9,     10,     10,     11,     11,     12,     12,     13,     13
        };
    }

    public static void inflateTreesFixed(int[] bl, int[] bd, int[][] tl, int[][] td) {
        bl[0] = FIXED_BL;
        bd[0] = FIXED_BD;
        tl[0] = FIXED_TL;
        td[0] = FIXED_TD;
    }

    private int[] hn;
    private int[] v;
    private int[] c;
    private int[] r;
    private int[] u;
    private int[] x;

    public InfTree() {
    }

    public int inflateTreesBits(int[] c, int[] bb, int[] tb, int[] hp, ZStream z) {
        int result;
        initWorkArea(19);
        hn[0] = 0;
        result = huftBuild(c, 0, 19, 19, null, null, tb, bb, hp, hn, v);
        if(result == DATA_ERROR)
        {
            z.message = "oversubscribed dynamic bit lengths tree";
        }
        else if(result == BUF_ERROR || bb[0] == 0)
        {
            z.message = "incomplete dynamic bit lengths tree";
            result = DATA_ERROR;
        }
        return result;
    }

    public int inflateTreesDynamic(int nl, int nd, int[] c, int[] bl, int[] bd, int[] tl, int[] td, int[] hp, ZStream z) {
        int result;
        initWorkArea(288);
        hn[0] = 0;
        result = huftBuild(c, 0, nl, 257, CPLENS, CPLEXT, tl, bl, hp, hn, v);
        if(result != OK || bl[0] == 0)
        {
            if(result == DATA_ERROR)
            {
                z.message = "oversubscribed literal/length tree";
            }
            else if(result != MEM_ERROR)
            {
                z.message = "incomplete literal/length tree";
                result = DATA_ERROR;
            }
            return result;
        }
        initWorkArea(288);
        result = huftBuild(c, nl, nd, 0, CPDIST, CPDEXT, td, bd, hp, hn, v);
        if(result != OK || (bd[0] == 0 && nl > 257))
        {
            if(result == DATA_ERROR)
            {
                z.message = "oversubscribed distance tree";
            }
            else if(result == BUF_ERROR)
            {
                z.message = "incomplete distance tree";
                result = DATA_ERROR;
            }
            else if(result != MEM_ERROR)
            {
                z.message = "empty distance tree with lengths";
                result = DATA_ERROR;
            }
            return result;
        }
        return OK;
    }

    private void initWorkArea(int vsize) {
        if(hn == null)
        {
            hn = new int[1];
            v = new int[vsize];
            c = new int[BMAX + 1];
            r = new int[3];
            u = new int[BMAX];
            x = new int[BMAX + 1];
        }
        if(v.length < vsize) v = new int[vsize];
        for(int i = 0; i < vsize; i++) v[i] = 0;
        for(int i = 0; i < BMAX + 1; i++) c[i] = 0;
        for(int i = 0; i < 3; i++) r[i] = 0;
        Array.copy(c, 0, u, 0, BMAX);
        Array.copy(c, 0, x, 0, BMAX + 1);
    }

    private int huftBuild(int[] b, int bindex, int n, int s, int[] d, int[] e, int[] t, int[] m, int[] hp, int[] hn, int[] v) {
        int a;
        int f;
        int g;
        int h;
        int i;
        int j;
        int k;
        int l;
        int p;
        int q;
        int w;
        int xp;
        int y;
        int z;
        int mask;
        p = 0;
        i = n;
        do
        {
            c[b[bindex + p]]++;
            p++;
            i--;
        } while(i != 0);
        if(c[0] == n)
        {
            t[0] = -1;
            m[0] = 0;
            return OK;
        }
        l = m[0];
        for(j = 1; j <= BMAX; j++) if(c[j] != 0) break;
        k = j;
        if(l < j) l = j;
        for(i = BMAX; i != 0; i--) if(c[i] != 0) break;
        g = i;
        if(l > i) l = i;
        m[0] = l;
        for(y = 1 << j; j < i; j++, y <<= 1) if((y -= c[j]) < 0) return DATA_ERROR;
        if((y -= c[i]) < 0) return DATA_ERROR;
        c[i] += y;
        x[1] = j = 0;
        p = 1;
        xp = 2;
        while(--i != 0)
        {
            x[xp] = (j += c[p]);
            xp++;
            p++;
        }
        i = 0;
        p = 0;
        do
        {
            if((j = b[bindex + p]) != 0) v[x[j]++] = i;
            p++;
        } while(++i < n);
        n = x[g];
        x[0] = i = 0;
        p = 0;
        h = -1;
        w = -l;
        u[0] = 0;
        q = 0;
        for(z = 0; k <= g; k++)
        {
            a = c[k];
            while(a-- != 0)
            {
                while(k > w + l)
                {
                    h++;
                    w += l;
                    z = g - w;
                    z = (z > l) ? l : z;
                    if((f = 1 << (j = k - w)) > a + 1)
                    {
                        f -= a + 1;
                        xp = k;
                        if(j < z) while(++j < z)
                        {
                            if((f <<= 1) <= c[++xp]) break;
                            f -= c[xp];
                        }
                    }
                    z = 1 << j;
                    if(hn[0] + z > MANY) return DATA_ERROR;
                    u[h] = q = hn[0];
                    hn[0] += z;
                    if(h != 0)
                    {
                        x[h] = i;
                        r[0] = (byte) j;
                        r[1] = (byte) l;
                        j = i >>> (w - l);
                        r[2] = (int) (q - u[h - 1] - j);
                        Array.copy(r, 0, hp, (u[h - 1] + j) * 3, 3);
                    } else
                    {
                        t[0] = q;
                    }
                }
                r[1] = (byte) (k - w);
                if(p >= n)
                {
                    r[0] = 128 + 64;
                }
                else if(v[p] < s)
                {
                    r[0] = (byte) (v[p] < 256 ? 0 : 32 + 64);
                    r[2] = v[p++];
                }
                else
                {
                    r[0] = (byte) (e[v[p] - s] + 16 + 64);
                    r[2] = d[v[p++] - s];
                }
                f = 1 << (k - w);
                for(j = i >>> w; j < z; j += f) Array.copy(r, 0, hp, (q + j) * 3, 3);
                for(j = 1 << (k - 1); (i & j) != 0; j >>>= 1) i ^= j;
                i ^= j;
                mask = (1 << w) - 1;
                while((i & mask) != x[h])
                {
                    h--;
                    w -= l;
                    mask = (1 << w) - 1;
                }
            }
        }
        return y != 0 && g != 1 ? BUF_ERROR : OK;
    }
}

final class StaticTree extends Zlib
{
    public static final int MAX_BL_BITS = 7;
    private static final int MAX_BITS = 15;
    private static final int BL_CODES = 19;
    private static final int D_CODES = 30;
    private static final int LITERALS = 256;
    private static final int LENGTH_CODES = 29;
    private static final int L_CODES = LITERALS + LENGTH_CODES + 1;

    public static final int[] STATIC_LTREE;
    public static final int[] STATIC_DTREE;
    public static final int[] EXTRA_LBITS;
    public static final int[] EXTRA_DBITS;
    public static final int[] EXTRA_BLBITS;
    public static final StaticTree STATIC_L_DESC;
    public static final StaticTree STATIC_D_DESC;
    public static final StaticTree STATIC_BL_DESC;

    static {
        STATIC_LTREE = new int[] {
                12,      8,    140,      8,     76,      8,    204,      8,     44,      8,    172,      8,    108,      8,    236,      8,
                28,      8,    156,      8,     92,      8,    220,      8,     60,      8,    188,      8,    124,      8,    252,      8,
                 2,      8,    130,      8,     66,      8,    194,      8,     34,      8,    162,      8,     98,      8,    226,      8,
                18,      8,    146,      8,     82,      8,    210,      8,     50,      8,    178,      8,    114,      8,    242,      8,
                10,      8,    138,      8,     74,      8,    202,      8,     42,      8,    170,      8,    106,      8,    234,      8,
                26,      8,    154,      8,     90,      8,    218,      8,     58,      8,    186,      8,    122,      8,    250,      8,
                 6,      8,    134,      8,     70,      8,    198,      8,     38,      8,    166,      8,    102,      8,    230,      8,
                22,      8,    150,      8,     86,      8,    214,      8,     54,      8,    182,      8,    118,      8,    246,      8,
                14,      8,    142,      8,     78,      8,    206,      8,     46,      8,    174,      8,    110,      8,    238,      8,
                30,      8,    158,      8,     94,      8,    222,      8,     62,      8,    190,      8,    126,      8,    254,      8,
                 1,      8,    129,      8,     65,      8,    193,      8,     33,      8,    161,      8,     97,      8,    225,      8,
                17,      8,    145,      8,     81,      8,    209,      8,     49,      8,    177,      8,    113,      8,    241,      8,
                 9,      8,    137,      8,     73,      8,    201,      8,     41,      8,    169,      8,    105,      8,    233,      8,
                25,      8,    153,      8,     89,      8,    217,      8,     57,      8,    185,      8,    121,      8,    249,      8,
                 5,      8,    133,      8,     69,      8,    197,      8,     37,      8,    165,      8,    101,      8,    229,      8,
                21,      8,    149,      8,     85,      8,    213,      8,     53,      8,    181,      8,    117,      8,    245,      8,
                13,      8,    141,      8,     77,      8,    205,      8,     45,      8,    173,      8,    109,      8,    237,      8,
                29,      8,    157,      8,     93,      8,    221,      8,     61,      8,    189,      8,    125,      8,    253,      8,
                19,      9,    275,      9,    147,      9,    403,      9,     83,      9,    339,      9,    211,      9,    467,      9,
                51,      9,    307,      9,    179,      9,    435,      9,    115,      9,    371,      9,    243,      9,    499,      9,
                11,      9,    267,      9,    139,      9,    395,      9,     75,      9,    331,      9,    203,      9,    459,      9,
                43,      9,    299,      9,    171,      9,    427,      9,    107,      9,    363,      9,    235,      9,    491,      9,
                27,      9,    283,      9,    155,      9,    411,      9,     91,      9,    347,      9,    219,      9,    475,      9,
                59,      9,    315,      9,    187,      9,    443,      9,    123,      9,    379,      9,    251,      9,    507,      9,
                 7,      9,    263,      9,    135,      9,    391,      9,     71,      9,    327,      9,    199,      9,    455,      9,
                39,      9,    295,      9,    167,      9,    423,      9,    103,      9,    359,      9,    231,      9,    487,      9,
                23,      9,    279,      9,    151,      9,    407,      9,     87,      9,    343,      9,    215,      9,    471,      9,
                55,      9,    311,      9,    183,      9,    439,      9,    119,      9,    375,      9,    247,      9,    503,      9,
                15,      9,    271,      9,    143,      9,    399,      9,     79,      9,    335,      9,    207,      9,    463,      9,
                47,      9,    303,      9,    175,      9,    431,      9,    111,      9,    367,      9,    239,      9,    495,      9,
                31,      9,    287,      9,    159,      9,    415,      9,     95,      9,    351,      9,    223,      9,    479,      9,
                63,      9,    319,      9,    191,      9,    447,      9,    127,      9,    383,      9,    255,      9,    511,      9,
                 0,      7,     64,      7,     32,      7,     96,      7,     16,      7,     80,      7,     48,      7,    112,      7,
                 8,      7,     72,      7,     40,      7,    104,      7,     24,      7,     88,      7,     56,      7,    120,      7,
                 4,      7,     68,      7,     36,      7,    100,      7,     20,      7,     84,      7,     52,      7,    116,      7,
                 3,      8,    131,      8,     67,      8,    195,      8,     35,      8,    163,      8,     99,      8,    227,      8
        };
        STATIC_DTREE = new int[] {
                 0,      5,     16,      5,      8,      5,     24,      5,      4,      5,     20,      5,     12,      5,     28,      5,
                 2,      5,     18,      5,     10,      5,     26,      5,      6,      5,     22,      5,     14,      5,     30,      5,
                 1,      5,     17,      5,      9,      5,     25,      5,      5,      5,     21,      5,     13,      5,     29,      5,
                 3,      5,     19,      5,     11,      5,     27,      5,      7,      5,     23,      5
        };
        EXTRA_LBITS = new int[] {
                 0,      0,      0,      0,      0,      0,      0,      0,      1,      1,      1,      1,      2,      2,      2,      2,
                 3,      3,      3,      3,      4,      4,      4,      4,      5,      5,      5,      5,      0
        };
        EXTRA_DBITS = new int[] {
                 0,      0,      0,      0,      1,      1,      2,      2,      3,      3,      4,      4,      5,      5,      6,      6,
                 7,      7,      8,      8,      9,      9,     10,     10,     11,     11,     12,     12,     13,     13
        };
        EXTRA_BLBITS = new int[] {
                 0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,
                 2,      3,      7
        };
        STATIC_L_DESC = new StaticTree(STATIC_LTREE, EXTRA_LBITS, LITERALS + 1, L_CODES, MAX_BITS);
        STATIC_D_DESC = new StaticTree(STATIC_DTREE, EXTRA_DBITS, 0, D_CODES, MAX_BITS);
        STATIC_BL_DESC = new StaticTree(null, EXTRA_BLBITS, 0, BL_CODES, MAX_BL_BITS);
    }

    public final int extraBase;
    public final int elems;
    public final int maxLength;
    public final int[] staticTree;
    public final int[] extraBits;

    public StaticTree(int[] staticTree, int[] extraBits, int extraBase, int elems, int maxLength) {
        this.extraBase = extraBase;
        this.elems = elems;
        this.maxLength = maxLength;
        this.staticTree = staticTree;
        this.extraBits = extraBits;
    }
}

final class Tree extends Zlib
{
    private static final int MAX_BITS = 15;
    private static final int LITERALS = 256;
    private static final int LENGTH_CODES = 29;
    private static final int L_CODES = LITERALS + 1 + LENGTH_CODES;
    private static final int HEAP_SIZE = 2 * L_CODES + 1;

    private static void genCodes(int[] tree, int maxCode, int[] blCount) {
        int[] nextCode = new int[MAX_BITS + 1];
        int code = 0;
        int bits;
        int n;
        for(bits = 1; bits <= MAX_BITS; bits++) nextCode[bits] = code = (short) ((code + blCount[bits - 1]) << 1);
        for(n = 0; n <= maxCode; n++)
        {
            int len = tree[n * 2 + 1];
            if(len == 0) continue;
            tree[n * 2] = (short) biReverse(nextCode[len]++, len);
        }
    }

    private static int biReverse(int code, int len) {
        int res = 0;
        do
        {
            res |= code & 1;
            code >>>= 1;
            res <<= 1;
        } while(--len > 0);
        return res >>> 1;
    }

    public int maxCode;
    public int[] dynTree;
    public StaticTree statDesc;

    public Tree() {
    }

    public void buildTree(Deflate s) {
        int elems = statDesc.elems;
        int n;
        int m;
        int maxCodeLocal = -1;
        int node;
        int[] tree = dynTree;
        int[] stree = statDesc.staticTree;
        s.heapLen = 0;
        s.heapMax = HEAP_SIZE;
        for(n = 0; n < elems; n++)
        {
            if(tree[n * 2] != 0)
            {
                s.heap[++s.heapLen] = maxCodeLocal = n;
                s.depth[n] = 0;
            } else
            {
                tree[n * 2 + 1] = 0;
            }
        }
        while(s.heapLen < 2)
        {
            node = s.heap[++s.heapLen] = (maxCodeLocal < 2 ? ++maxCodeLocal : 0);
            tree[node * 2] = 1;
            s.depth[node] = 0;
            s.optLen--;
            if(stree != null) s.staticLen -= stree[node * 2 + 1];
        }
        this.maxCode = maxCodeLocal;
        for(n = s.heapLen / 2; n >= 1; n--) s.pqDownHeap(tree, n);
        node = elems;
        do
        {
            n = s.heap[1];
            s.heap[1] = s.heap[s.heapLen--];
            s.pqDownHeap(tree, 1);
            m = s.heap[1];
            s.heap[--s.heapMax] = n;
            s.heap[--s.heapMax] = m;
            tree[node * 2] = (short) (tree[n * 2] + tree[m * 2]);
            s.depth[node] = (byte) (Math.max(s.depth[n], s.depth[m]) + 1);
            tree[n * 2 + 1] = tree[m * 2 + 1] = (short) node;
            s.heap[1] = node++;
            s.pqDownHeap(tree, 1);
        } while(s.heapLen >= 2);
        s.heap[--s.heapMax] = s.heap[1];
        genBitlen(s);
        genCodes(tree, maxCodeLocal, s.blCount);
    }

    private void genBitlen(Deflate s) {
        int f;
        int base = statDesc.extraBase;
        int maxLength = statDesc.maxLength;
        int h;
        int n;
        int m;
        int bits;
        int xbits;
        int overflow = 0;
        int[] tree = dynTree;
        int[] stree = statDesc.staticTree;
        int[] extra = statDesc.extraBits;
        for(bits = 0; bits <= MAX_BITS; bits++) s.blCount[bits] = 0;
        tree[s.heap[s.heapMax] * 2 + 1] = 0;
        for(h = s.heapMax + 1; h < HEAP_SIZE; h++)
        {
            n = s.heap[h];
            bits = tree[tree[n * 2 + 1] * 2 + 1] + 1;
            if(bits > maxLength)
            {
                bits = maxLength;
                overflow++;
            }
            tree[n * 2 + 1] = (short) bits;
            if(n > maxCode) continue;
            s.blCount[bits]++;
            xbits = 0;
            if(n >= base) xbits = extra[n - base];
            f = tree[n * 2];
            s.optLen += f * (bits + xbits);
            if(stree != null) s.staticLen += f * (stree[n * 2 + 1] + xbits);
        }
        if(overflow == 0) return;
        do
        {
            bits = maxLength - 1;
            while(s.blCount[bits] == 0) bits--;
            s.blCount[bits]--;
            s.blCount[bits + 1] += 2;
            s.blCount[maxLength]--;
            overflow -= 2;
        } while(overflow > 0);
        for(bits = maxLength; bits != 0; bits--)
        {
            n = s.blCount[bits];
            while(n != 0)
            {
                m = s.heap[--h];
                if(m > maxCode) continue;
                if(tree[m * 2 + 1] != bits)
                {
                    s.optLen += (((long) bits) - (long) tree[m * 2 + 1]) * (long) tree[m * 2];
                    tree[m * 2 + 1] = (short) bits;
                }
                n--;
            }
        }
    }
}
