///////////////////////////// executable code ////////////////////////////////


    // Read the median log2 values from the specifed metadata structure, convert
    // them back to 32-bit unsigned values and store them. If length is not
    // exactly correct then we flag and return an error.

    internal static int read_entropy_vars(WavpackStream wps, WavpackMetadata wpmd)
    {
        byte[]     byteptr = wpmd.data;
        int[]      b_array = new int[12];
        int        i       = 0;
        words_data w       = new words_data();

        for (i = 0; i < 6; i++)
        {
            b_array[i] = (int)(byteptr[i] & 0xff);
        }

        w.holding_one  = 0;
        w.holding_zero = 0;

        if (wpmd.byte_length != 12)
        {
            if ((wps.wphdr.flags & (Defines.MONO_FLAG | Defines.FALSE_STEREO)) == 0)
            {
                return(Defines.FALSE);
            }
        }

        w.c[0].median[0] = exp2s(b_array[0] + (b_array[1] << 8));
        w.c[0].median[1] = exp2s(b_array[2] + (b_array[3] << 8));
        w.c[0].median[2] = exp2s(b_array[4] + (b_array[5] << 8));

        if ((wps.wphdr.flags & (Defines.MONO_FLAG | Defines.FALSE_STEREO)) == 0)
        {
            for (i = 6; i < 12; i++)
            {
                b_array[i] = (int)(byteptr[i] & 0xff);
            }
            w.c[1].median[0] = exp2s(b_array[6] + (b_array[7] << 8));
            w.c[1].median[1] = exp2s(b_array[8] + (b_array[9] << 8));
            w.c[1].median[2] = exp2s(b_array[10] + (b_array[11] << 8));
        }

        wps.w = w;

        return(Defines.TRUE);
    }
    // Read the next word from the bitstream "wvbits" and return the value. This
    // function can be used for hybrid or lossless streams, but since an
    // optimized version is available for lossless this function would normally
    // be used for hybrid only. If a hybrid lossless stream is being read then
    // the "correction" offset is written at the specified pointer. A return value
    // of WORD_EOF indicates that the end of the bitstream was reached (all 1s) or
    // some other error occurred.

    internal static int get_words(long nsamples, long flags, words_data w, Bitstream bs, int[] buffer, int bufferStartPos)
    {
        entropy_data[] c = w.c;
        int            csamples;
        int            buffer_counter = bufferStartPos;
        int            entidx         = 1;

        if ((flags & (Defines.MONO_FLAG | Defines.FALSE_STEREO)) == 0)
        // if not mono
        {
            nsamples *= 2;
        }
        else
        {
            // it is mono
            entidx = 0;
        }

        for (csamples = 0; csamples < nsamples; ++csamples)
        {
            long ones_count, low, high, mid;

            if ((flags & (Defines.MONO_FLAG | Defines.FALSE_STEREO)) == 0)
            // if not mono
            {
                if (entidx == 1)
                {
                    entidx = 0;
                }
                else
                {
                    entidx = 1;
                }
            }

            if ((w.c[0].median[0] & ~1) == 0 && w.holding_zero == 0 && w.holding_one == 0 && (w.c[1].median[0] & ~1) == 0)
            {
                long mask;
                int  cbits;

                if (w.zeros_acc > 0)
                {
                    --w.zeros_acc;

                    if (w.zeros_acc > 0)
                    {
                        c[entidx].slow_level  -= ((c[entidx].slow_level + SLO) >> SLS);
                        buffer[buffer_counter] = 0;
                        buffer_counter++;
                        continue;
                    }
                }
                else
                {
                    cbits = 0;
                    bs    = BitsUtils.getbit(bs);

                    while (cbits < 33 && bs.bitval > 0)
                    {
                        cbits++;
                        bs = BitsUtils.getbit(bs);
                    }

                    if (cbits == 33)
                    {
                        break;
                    }

                    if (cbits < 2)
                    {
                        w.zeros_acc = cbits;
                    }
                    else
                    {
                        --cbits;

                        for (mask = 1, w.zeros_acc = 0; cbits > 0; mask <<= 1)
                        {
                            bs = BitsUtils.getbit(bs);

                            if (bs.bitval > 0)
                            {
                                w.zeros_acc |= mask;
                            }
                            cbits--;
                        }

                        w.zeros_acc |= mask;
                    }

                    if (w.zeros_acc > 0)
                    {
                        c[entidx].slow_level -= ((c[entidx].slow_level + SLO) >> SLS);
                        w.c[0].median[0]      = 0;
                        w.c[0].median[1]      = 0;
                        w.c[0].median[2]      = 0;
                        w.c[1].median[0]      = 0;
                        w.c[1].median[1]      = 0;
                        w.c[1].median[2]      = 0;

                        buffer[buffer_counter] = 0;
                        buffer_counter++;
                        continue;
                    }
                }
            }

            if (w.holding_zero > 0)
            {
                ones_count = w.holding_zero = 0;
            }
            else
            {
                int next8;
                int uns_buf;

                if (bs.bc < 8)
                {
                    bs.ptr++;
                    bs.buf_index++;

                    if (bs.ptr == bs.end)
                    {
                        bs = BitsUtils.bs_read(bs);
                    }

                    uns_buf = (int)(bs.buf[bs.buf_index] & 0xff);

                    bs.sr = bs.sr | (uns_buf << bs.bc);                     // values in buffer must be unsigned

                    next8 = (int)(bs.sr & 0xff);

                    bs.bc += 8;
                }
                else
                {
                    next8 = (int)(bs.sr & 0xff);
                }

                if (next8 == 0xff)
                {
                    bs.bc  -= 8;
                    bs.sr >>= 8;

                    ones_count = 8;
                    bs         = BitsUtils.getbit(bs);

                    while (ones_count < (LIMIT_ONES + 1) && bs.bitval > 0)
                    {
                        ones_count++;
                        bs = BitsUtils.getbit(bs);
                    }

                    if (ones_count == (LIMIT_ONES + 1))
                    {
                        break;
                    }

                    if (ones_count == LIMIT_ONES)
                    {
                        int mask;
                        int cbits;

                        cbits = 0;
                        bs    = BitsUtils.getbit(bs);

                        while (cbits < 33 && bs.bitval > 0)
                        {
                            cbits++;
                            bs = BitsUtils.getbit(bs);
                        }

                        if (cbits == 33)
                        {
                            break;
                        }

                        if (cbits < 2)
                        {
                            ones_count = cbits;
                        }
                        else
                        {
                            for (mask = 1, ones_count = 0; --cbits > 0; mask <<= 1)
                            {
                                bs = BitsUtils.getbit(bs);

                                if (bs.bitval > 0)
                                {
                                    ones_count |= mask;
                                }
                            }
                            ones_count |= mask;
                        }

                        ones_count += LIMIT_ONES;
                    }
                }
                else
                {
                    bs.bc = (int)(bs.bc - ((ones_count = ones_count_table[next8]) + 1));
                    bs.sr = bs.sr >> (int)(ones_count + 1);                      // needs to be unsigned
                }

                if (w.holding_one > 0)
                {
                    w.holding_one = ones_count & 1;
                    ones_count    = (ones_count >> 1) + 1;
                }
                else
                {
                    w.holding_one = ones_count & 1;
                    ones_count  >>= 1;
                }

                w.holding_zero = (int)(~w.holding_one & 1);
            }

            if ((flags & Defines.HYBRID_FLAG) > 0 && ((flags & (Defines.MONO_FLAG | Defines.FALSE_STEREO)) > 0 || (csamples & 1) == 0))
            {
                w = update_error_limit(w, flags);
            }

            if (ones_count == 0)
            {
                low  = 0;
                high = (((c[entidx].median[0]) >> 4) + 1) - 1;

                // for c# I replace the division by DIV0 with >> 7
                c[entidx].median[0] -= (((c[entidx].median[0] + (DIV0 - 2)) >> 7) * 2);
            }
            else
            {
                low = (((c[entidx].median[0]) >> 4) + 1);

                // for c# I replace the division by DIV0 with >> 7
                c[entidx].median[0] += ((c[entidx].median[0] + DIV0) >> 7) * 5;

                if (ones_count == 1)
                {
                    high = low + (((c[entidx].median[1]) >> 4) + 1) - 1;
                    // for c# I replace the division by DIV1 with >> 6
                    c[entidx].median[1] -= ((c[entidx].median[1] + (DIV1 - 2)) >> 6) * 2;
                }
                else
                {
                    low += (((c[entidx].median[1]) >> 4) + 1);
                    // for c# I replace the division by DIV1 with >> 6
                    c[entidx].median[1] += ((c[entidx].median[1] + DIV1) >> 6) * 5;

                    if (ones_count == 2)
                    {
                        high = low + (((c[entidx].median[2]) >> 4) + 1) - 1;
                        // for c# I replace the division by DIV2 with >> 5
                        c[entidx].median[2] -= ((c[entidx].median[2] + (DIV2 - 2)) >> 5) * 2;
                    }
                    else
                    {
                        low += (ones_count - 2) * (((c[entidx].median[2]) >> 4) + 1);
                        high = low + (((c[entidx].median[2]) >> 4) + 1) - 1;
                        // for c# I replace the division by DIV2 with >> 5
                        c[entidx].median[2] += ((c[entidx].median[2] + DIV2) >> 5) * 5;
                    }
                }
            }

            mid = (high + low + 1) >> 1;

            if (c[entidx].error_limit == 0)
            {
                mid = read_code(bs, high - low);

                mid = mid + low;
            }
            else
            {
                while (high - low > c[entidx].error_limit)
                {
                    bs = BitsUtils.getbit(bs);

                    if (bs.bitval > 0)
                    {
                        mid = (high + (low = mid) + 1) >> 1;
                    }
                    else
                    {
                        mid = ((high = mid - 1) + low + 1) >> 1;
                    }
                }
            }

            bs = BitsUtils.getbit(bs);

            if (bs.bitval > 0)
            {
                buffer[buffer_counter] = (int)~mid;
            }
            else
            {
                buffer[buffer_counter] = (int)mid;
            }

            buffer_counter++;

            if ((flags & Defines.HYBRID_BITRATE) > 0)
            {
                c[entidx].slow_level = c[entidx].slow_level - ((c[entidx].slow_level + SLO) >> SLS) + mylog2(mid);
            }
        }

        w.c = c;

        if ((flags & (Defines.MONO_FLAG | Defines.FALSE_STEREO)) != 0)
        {
            return(csamples);
        }
        else
        {
            return(csamples / 2);
        }
    }
    // This function is called during both encoding and decoding of hybrid data to
    // update the "error_limit" variable which determines the maximum sample error
    // allowed in the main bitstream. In the HYBRID_BITRATE mode (which is the only
    // currently implemented) this is calculated from the slow_level values and the
    // bitrate accumulators. Note that the bitrate accumulators can be changing.

    internal static words_data update_error_limit(words_data w, long flags)
    {
        int bitrate_0 = (int)((w.bitrate_acc[0] += w.bitrate_delta[0]) >> 16);

        if ((flags & (Defines.MONO_FLAG | Defines.FALSE_STEREO)) != 0)
        {
            if ((flags & Defines.HYBRID_BITRATE) != 0)
            {
                int slow_log_0 = (int)((w.c[0].slow_level + SLO) >> SLS);

                if (slow_log_0 - bitrate_0 > -0x100)
                {
                    w.c[0].error_limit = exp2s(slow_log_0 - bitrate_0 + 0x100);
                }
                else
                {
                    w.c[0].error_limit = 0;
                }
            }
            else
            {
                w.c[0].error_limit = exp2s(bitrate_0);
            }
        }
        else
        {
            int bitrate_1 = (int)((w.bitrate_acc[1] += w.bitrate_delta[1]) >> 16);

            if ((flags & Defines.HYBRID_BITRATE) != 0)
            {
                int slow_log_0 = (int)((w.c[0].slow_level + SLO) >> SLS);
                int slow_log_1 = (int)((w.c[1].slow_level + SLO) >> SLS);

                if ((flags & Defines.HYBRID_BALANCE) != 0)
                {
                    int balance = (slow_log_1 - slow_log_0 + bitrate_1 + 1) >> 1;

                    if (balance > bitrate_0)
                    {
                        bitrate_1 = bitrate_0 * 2;
                        bitrate_0 = 0;
                    }
                    else if (-balance > bitrate_0)
                    {
                        bitrate_0 = bitrate_0 * 2;
                        bitrate_1 = 0;
                    }
                    else
                    {
                        bitrate_1 = bitrate_0 + balance;
                        bitrate_0 = bitrate_0 - balance;
                    }
                }

                if (slow_log_0 - bitrate_0 > -0x100)
                {
                    w.c[0].error_limit = exp2s(slow_log_0 - bitrate_0 + 0x100);
                }
                else
                {
                    w.c[0].error_limit = 0;
                }

                if (slow_log_1 - bitrate_1 > -0x100)
                {
                    w.c[1].error_limit = exp2s(slow_log_1 - bitrate_1 + 0x100);
                }
                else
                {
                    w.c[1].error_limit = 0;
                }
            }
            else
            {
                w.c[0].error_limit = exp2s(bitrate_0);
                w.c[1].error_limit = exp2s(bitrate_1);
            }
        }

        return(w);
    }