/**
         * Read and decode the block's Huffman tables
         * @return A decoder for the Huffman stage that uses the decoded tables
         * Exception if the input stream reaches EOF before all table data has been read
         */
        private BZip2HuffmanStageDecoder ReadHuffmanTables()
        {
            var tableCodeLengths = new byte[HUFFMAN_MAXIMUM_TABLES, BZip2MTFAndRLE2StageEncoder.HUFFMAN_MAXIMUM_ALPHABET_SIZE];

            /* Read Huffman symbol to output byte map */
            uint huffmanUsedRanges = bitInputStream.ReadBits(16);
            int huffmanSymbolCount = 0;

            for (int i = 0; i < 16; i++)
            {
                if ((huffmanUsedRanges & ((1 << 15) >> i)) == 0)
                    continue;
                for (int j = 0, k = i << 4; j < 16; j++, k++)
                {
                    if (bitInputStream.ReadBoolean())
                    {
                        huffmanSymbolMap[huffmanSymbolCount++] = (byte)k;
                    }
                }
            }
            var endOfBlockSymbol = huffmanSymbolCount + 1;
            this.huffmanEndOfBlockSymbol = endOfBlockSymbol;

            /* Read total number of tables and selectors*/
            uint totalTables = bitInputStream.ReadBits(3);
            uint totalSelectors = bitInputStream.ReadBits(15);
            if ((totalTables < HUFFMAN_MINIMUM_TABLES)
                || (totalTables > HUFFMAN_MAXIMUM_TABLES)
                || (totalSelectors < 1)
                || (totalSelectors > HUFFMAN_MAXIMUM_SELECTORS))
            {
                throw new Exception("BZip2 block Huffman tables invalid");
            }

            /* Read and decode MTFed Huffman selector list */
            var tableMTF = new MoveToFront();
            var selectors = new byte[totalSelectors];
            for (var selector = 0; selector < totalSelectors; selector++)
            {
                selectors[selector] = tableMTF.IndexToFront((int)bitInputStream.ReadUnary());
            }

            /* Read the Canonical Huffman code lengths for each table */
            for (var table = 0; table < totalTables; table++)
            {
                int currentLength = (int)bitInputStream.ReadBits(5);
                for (var i = 0; i <= endOfBlockSymbol; i++)
                {
                    while (bitInputStream.ReadBoolean())
                    {
                        currentLength += bitInputStream.ReadBoolean() ? -1 : 1;
                    }
                    tableCodeLengths[table, i] = (byte)currentLength;
                }
            }

            return new BZip2HuffmanStageDecoder(bitInputStream, endOfBlockSymbol + 1, tableCodeLengths, selectors);
        }
        /**
         * Write out the selector list and Huffman tables
         * @Exception on any I/O error writing the data
         */
        private void writeSelectorsAndHuffmanTables()
        {
            int totalSelectors = selectors.Length;
            int totalTables = huffmanCodeLengths.GetLength(0);

            bitOutputStream.WriteBits (3, (uint)totalTables);
            bitOutputStream.WriteBits (15, (uint)totalSelectors);

            // Write the selectors
            var selectorMTF = new MoveToFront();
            for (int i = 0; i < totalSelectors; i++)
            {
                bitOutputStream.WriteUnary (selectorMTF.ValueToFront (selectors[i]));
            }

            // Write the Huffman tables
            for (int i = 0; i < totalTables; i++)
            {
                var currentLength = huffmanCodeLengths[i,0];

                bitOutputStream.WriteBits (5, (uint)currentLength);

                for (var j = 0; j < mtfAlphabetSize; j++) {
                    var codeLength = huffmanCodeLengths[i,j];
                    var value = (currentLength < codeLength) ? 2u : 3u;
                    var delta = Math.Abs (codeLength - currentLength);
                    while (delta-- > 0) {
                        bitOutputStream.WriteBits (2, value);
                    }
                    bitOutputStream.WriteBoolean (false);
                    currentLength = codeLength;
                }
            }
        }
        /**
         * Reads the Huffman encoded data from the input stream, performs Run-Length Decoding and
         * applies the Move To Front transform to reconstruct the Burrows-Wheeler Transform array
         * @param huffmanDecoder The Huffman decoder through which symbols are read
         * Exception if an end-of-block symbol was not decoded within the declared block size
         */
        private void DecodeHuffmanData(BZip2HuffmanStageDecoder huffmanDecoder)
        {
            var symbolMTF = new MoveToFront();
            int _bwtBlockLength = 0;
            int repeatCount = 0;
            int repeatIncrement = 1;
            int mtfValue = 0;

            while (true)
            {
                var nextSymbol = huffmanDecoder.NextSymbol();

                if (nextSymbol == BZip2MTFAndRLE2StageEncoder.RLE_SYMBOL_RUNA)
                {
                    repeatCount += repeatIncrement;
                    repeatIncrement <<= 1;
                }
                else if (nextSymbol == BZip2MTFAndRLE2StageEncoder.RLE_SYMBOL_RUNB)
                {
                    repeatCount += repeatIncrement << 1;
                    repeatIncrement <<= 1;
                }
                else
                {
                    byte nextByte;
                    if (repeatCount > 0)
                    {
                        if (_bwtBlockLength + repeatCount > this.bwtBlock.Length)
                            throw new Exception("BZip2 block exceeds declared block size");

                        nextByte = huffmanSymbolMap[mtfValue];
                        bwtByteCounts[nextByte & 0xff] += repeatCount;
                        while (--repeatCount >= 0)
                        {
                            bwtBlock[_bwtBlockLength++] = nextByte;
                        }

                        repeatCount = 0;
                        repeatIncrement = 1;
                    }

                    if (nextSymbol == huffmanEndOfBlockSymbol)
                        break;

                    if (_bwtBlockLength >= this.bwtBlock.Length)
                        throw new Exception("BZip2 block exceeds declared block size");

                    mtfValue = symbolMTF.IndexToFront(nextSymbol - 1) & 0xff;

                    nextByte = huffmanSymbolMap[mtfValue];
                    bwtByteCounts[nextByte & 0xff]++;
                    bwtBlock[_bwtBlockLength++] = nextByte;
                }
            }

            this.bwtBlockLength = _bwtBlockLength;
        }