/* Copyright (c) 2007-2008 CSIRO
   Copyright (c) 2007-2011 Xiph.Org Foundation
   Originally written by Jean-Marc Valin, Gregory Maxwell, Koen Vos,
   Timothy B. Terriberry, and the Opus open-source contributors
   Ported to Java by Logan Stromberg

   Redistribution and use in source and binary forms, with or without
   modification, are permitted provided that the following conditions
   are met:

   - Redistributions of source code must retain the above copyright
   notice, this list of conditions and the following disclaimer.

   - Redistributions in binary form must reproduce the above copyright
   notice, this list of conditions and the following disclaimer in the
   documentation and/or other materials provided with the distribution.

   - Neither the name of Internet Society, IETF or IETF Trust, nor the
   names of specific contributors, may be used to endorse or promote
   products derived from this software without specific prior written
   permission.

   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
   OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package org.concentus;

class Bands {

    static int hysteresis_decision(
            int val,
            int[] thresholds,
            int[] hysteresis,
            int N,
            int prev) {
        int i;
        for (i = 0; i < N; i++) {
            if (val < thresholds[i]) {
                break;
            }
        }

        if (i > prev && val < thresholds[prev] + hysteresis[prev]) {
            i = prev;
        }

        if (i < prev && val > thresholds[prev - 1] - hysteresis[prev - 1]) {
            i = prev;
        }

        return i;
    }

    static int celt_lcg_rand(int seed) {
        return (1664525 * seed + 1013904223);
    }

    /* This is a cos() approximation designed to be bit-exact on any platform. Bit exactness
       with this approximation is important because it has an impact on the bit allocation */
    static int bitexact_cos(int x) {
        int tmp;
        int x2;
        tmp = (4096 + ((int) (x) * (x))) >> 13;
        Inlines.OpusAssert(tmp <= 32767);
        x2 = (tmp);
        x2 = ((32767 - x2) + Inlines.FRAC_MUL16(x2, (-7651 + Inlines.FRAC_MUL16(x2, (8277 + Inlines.FRAC_MUL16(-626, x2))))));
        Inlines.OpusAssert(x2 <= 32766);
        return (1 + x2);
    }

    static int bitexact_log2tan(int isin, int icos) {
        int lc = Inlines.EC_ILOG((long) icos);
        int ls = Inlines.EC_ILOG((long) isin);
        icos <<= 15 - lc;
        isin <<= 15 - ls;
        return (ls - lc) * (1 << 11)
                + Inlines.FRAC_MUL16(isin, Inlines.FRAC_MUL16(isin, -2597) + 7932)
                - Inlines.FRAC_MUL16(icos, Inlines.FRAC_MUL16(icos, -2597) + 7932);
    }

    /* Compute the amplitude (sqrt energy) in each of the bands */
    static void compute_band_energies(CeltMode m, int[][] X, int[][] bandE, int end, int C, int LM) {
        int i, c, N;
        short[] eBands = m.eBands;
        N = m.shortMdctSize << LM;
        c = 0;

        do {
            for (i = 0; i < end; i++) {
                int j;
                int maxval = 0;
                int sum = 0;
                maxval = Inlines.celt_maxabs32(X[c], (eBands[i] << LM), (eBands[i + 1] - eBands[i]) << LM);
                if (maxval > 0) {
                    int shift = Inlines.celt_ilog2(maxval) - 14 + (((m.logN[i] >> EntropyCoder.BITRES) + LM + 1) >> 1);
                    j = eBands[i] << LM;
                    if (shift > 0) {
                        do {
                            sum = Inlines.MAC16_16(sum, Inlines.EXTRACT16(Inlines.SHR32(X[c][j], shift)),
                                    Inlines.EXTRACT16(Inlines.SHR32(X[c][j], shift)));
                        } while (++j < eBands[i + 1] << LM);
                    } else {
                        do {
                            sum = Inlines.MAC16_16(sum, Inlines.EXTRACT16(Inlines.SHL32(X[c][j], -shift)),
                                    Inlines.EXTRACT16(Inlines.SHL32(X[c][j], -shift)));
                        } while (++j < eBands[i + 1] << LM);
                    }
                    /* We're adding one here to ensure the normalized band isn't larger than unity norm */
                    bandE[c][i] = CeltConstants.EPSILON + Inlines.VSHR32(Inlines.celt_sqrt(sum), -shift);
                } else {
                    bandE[c][i] = CeltConstants.EPSILON;
                }
                /*printf ("%f ", bandE[i+c*m->nbEBands]);*/
            }
        } while (++c < C);
    }

    /* Normalise each band such that the energy is one. */
    static void normalise_bands(CeltMode m, int[][] freq, int[][] X, int[][] bandE, int end, int C, int M) {
        int i, c;
        short[] eBands = m.eBands;
        c = 0;
        do {
            i = 0;
            do {
                int g;
                int j, shift;
                int E;
                shift = Inlines.celt_zlog2(bandE[c][i]) - 13;
                E = Inlines.VSHR32(bandE[c][i], shift);
                g = Inlines.EXTRACT16(Inlines.celt_rcp(Inlines.SHL32(E, 3)));
                j = M * eBands[i];
                do {
                    X[c][j] = Inlines.MULT16_16_Q15(Inlines.VSHR32(freq[c][j], shift - 1), g);
                } while (++j < M * eBands[i + 1]);
            } while (++i < end);
        } while (++c < C);
    }

    /* De-normalise the energy to produce the synthesis from the unit-energy bands */
    static void denormalise_bands(CeltMode m, int[] X,
            int[] freq, int freq_ptr, int[] bandLogE, int bandLogE_ptr, int start,
            int end, int M, int downsample, int silence) {
        int i, N;
        int bound;
        int f;
        int x;
        short[] eBands = m.eBands;
        N = M * m.shortMdctSize;
        bound = M * eBands[end];
        if (downsample != 1) {
            bound = Inlines.IMIN(bound, N / downsample);
        }
        if (silence != 0) {
            bound = 0;
            start = end = 0;
        }
        f = freq_ptr;
        x = M * eBands[start];

        for (i = 0; i < M * eBands[start]; i++) {
            freq[f++] = 0;
        }

        for (i = start; i < end; i++) {
            int j, band_end;
            int g;
            int lg;
            int shift;

            j = M * eBands[i];
            band_end = M * eBands[i + 1];
            lg = Inlines.ADD16(bandLogE[bandLogE_ptr + i], Inlines.SHL16(CeltTables.eMeans[i], 6));

            /* Handle the integer part of the log energy */
            shift = 16 - (lg >> CeltConstants.DB_SHIFT);
            if (shift > 31) {
                shift = 0;
                g = 0;
            } else {
                /* Handle the fractional part. */
                g = Inlines.celt_exp2_frac(lg & ((1 << CeltConstants.DB_SHIFT) - 1));
            }
            /* Handle extreme gains with negative shift. */
            if (shift < 0) {
                /* For shift < -2 we'd be likely to overflow, so we're capping
                      the gain here. This shouldn't happen unless the bitstream is
                      already corrupted. */
                if (shift < -2) {
                    g = 32767;
                    shift = -2;
                }
                do {
                    freq[f] = Inlines.SHR32(Inlines.MULT16_16(X[x], g), -shift);
                } while (++j < band_end);
            } else {
                do {
                    freq[f++] = Inlines.SHR32(Inlines.MULT16_16(X[x++], g), shift);
                } while (++j < band_end);
            }
        }

        Inlines.OpusAssert(start <= end);
        Arrays.MemSetWithOffset(freq, 0, freq_ptr + bound, N - bound);
    }

    /* This prevents energy collapse for transients with multiple short MDCTs */
    static void anti_collapse(CeltMode m, int[][] X_, short[] collapse_masks, int LM, int C, int size,
            int start, int end, int[] logE, int[] prev1logE,
            int[] prev2logE, int[] pulses, int seed) {
        int c, i, j, k;
        for (i = start; i < end; i++) {
            int N0;
            int thresh, sqrt_1;
            int depth;
            int shift;
            int thresh32;

            N0 = m.eBands[i + 1] - m.eBands[i];
            /* depth in 1/8 bits */
            Inlines.OpusAssert(pulses[i] >= 0);
            depth = Inlines.celt_udiv(1 + pulses[i], (m.eBands[i + 1] - m.eBands[i])) >> LM;

            thresh32 = Inlines.SHR32(Inlines.celt_exp2((0 - Inlines.SHL16((depth), 10 - EntropyCoder.BITRES))), 1);
            thresh = (Inlines.MULT16_32_Q15(((short) (0.5 + (0.5f) * (((int) 1) << (15))))/*Inlines.QCONST16(0.5f, 15)*/, Inlines.MIN32(32767, thresh32)));
            {
                int t;
                t = N0 << LM;
                shift = Inlines.celt_ilog2(t) >> 1;
                t = Inlines.SHL32(t, (7 - shift) << 1);
                sqrt_1 = Inlines.celt_rsqrt_norm(t);
            }

            c = 0;
            do {
                int X;
                int prev1;
                int prev2;
                int Ediff;
                int r;
                int renormalize = 0;
                prev1 = prev1logE[c * m.nbEBands + i];
                prev2 = prev2logE[c * m.nbEBands + i];
                if (C == 1) {
                    prev1 = Inlines.MAX16(prev1, prev1logE[m.nbEBands + i]);
                    prev2 = Inlines.MAX16(prev2, prev2logE[m.nbEBands + i]);
                }
                Ediff = Inlines.EXTEND32(logE[c * m.nbEBands + i]) - Inlines.EXTEND32(Inlines.MIN16(prev1, prev2));
                Ediff = Inlines.MAX32(0, Ediff);

                if (Ediff < 16384) {
                    int r32 = Inlines.SHR32(Inlines.celt_exp2((short) (0 - Inlines.EXTRACT16(Ediff))), 1);
                    r = (2 * Inlines.MIN16(16383, (r32)));
                } else {
                    r = 0;
                }
                if (LM == 3) {
                    r = Inlines.MULT16_16_Q14(23170, Inlines.MIN32(23169, r)); // opus bug: was MIN32
                }
                r = Inlines.SHR16(Inlines.MIN16(thresh, r), 1);
                r = (Inlines.SHR32(Inlines.MULT16_16_Q15(sqrt_1, r), shift));

                X = m.eBands[i] << LM;
                for (k = 0; k < 1 << LM; k++) {
                    /* Detect collapse */
                    if ((collapse_masks[i * C + c] & 1 << k) == 0) {
                        /* Fill with noise */
                        int Xk = X + k;
                        for (j = 0; j < N0; j++) {
                            seed = celt_lcg_rand(seed);
                            X_[c][Xk + (j << LM)] = ((seed & 0x8000) != 0 ? r : 0 - r);
                        }
                        renormalize = 1;
                    }
                }
                /* We just added some energy, so we need to renormalise */
                if (renormalize != 0) {
                    VQ.renormalise_vector(X_[c], X, N0 << LM, CeltConstants.Q15ONE);
                }
            } while (++c < C);
        }
    }

    static void intensity_stereo(CeltMode m, int[] X, int X_ptr, int[] Y, int Y_ptr, int[][] bandE, int bandID, int N) {
        int i = bandID;
        int j;
        int a1, a2;
        int left, right;
        int norm;
        int shift = Inlines.celt_zlog2(Inlines.MAX32(bandE[0][i], bandE[1][i])) - 13;
        left = Inlines.VSHR32(bandE[0][i], shift);
        right = Inlines.VSHR32(bandE[1][i], shift);
        norm = CeltConstants.EPSILON + Inlines.celt_sqrt(CeltConstants.EPSILON + Inlines.MULT16_16(left, left) + Inlines.MULT16_16(right, right));
        a1 = Inlines.DIV32_16(Inlines.SHL32(left, 14), norm);
        a2 = Inlines.DIV32_16(Inlines.SHL32(right, 14), norm);
        for (j = 0; j < N; j++) {
            int r, l;
            l = X[X_ptr + j];
            r = Y[Y_ptr + j];
            X[X_ptr + j] = Inlines.EXTRACT16(Inlines.SHR32(Inlines.MAC16_16(Inlines.MULT16_16(a1, l), a2, r), 14));
            /* Side is not encoded, no need to calculate */
        }
    }

    static void stereo_split(int[] X, int X_ptr, int[] Y, int Y_ptr, int N) {
        int j;
        for (j = 0; j < N; j++) {
            int r, l;
            l = Inlines.MULT16_16(((short) (0.5 + (.70710678f) * (((int) 1) << (15))))/*Inlines.QCONST16(.70710678f, 15)*/, X[X_ptr + j]);
            r = Inlines.MULT16_16(((short) (0.5 + (.70710678f) * (((int) 1) << (15))))/*Inlines.QCONST16(.70710678f, 15)*/, Y[Y_ptr + j]);
            X[X_ptr + j] = Inlines.EXTRACT16(Inlines.SHR32(Inlines.ADD32(l, r), 15));
            Y[Y_ptr + j] = Inlines.EXTRACT16(Inlines.SHR32(Inlines.SUB32(r, l), 15));
        }
    }

    static void stereo_merge(int[] X, int X_ptr, int[] Y, int Y_ptr, int mid, int N) {
        int j;
        BoxedValueInt xp = new BoxedValueInt(0);
        BoxedValueInt side = new BoxedValueInt(0);
        int El, Er;
        int mid2;
        int kl, kr;
        int t, lgain, rgain;

        /* Compute the norm of X+Y and X-Y as |X|^2 + |Y|^2 +/- sum(xy) */
        Kernels.dual_inner_prod(Y, Y_ptr, X, X_ptr, Y, Y_ptr, N, xp, side);
        /* Compensating for the mid normalization */
        xp.Val = Inlines.MULT16_32_Q15(mid, xp.Val);
        /* mid and side are in Q15, not Q14 like X and Y */
        mid2 = Inlines.SHR16(mid, 1);
        El = Inlines.MULT16_16(mid2, mid2) + side.Val - (2 * xp.Val);
        Er = Inlines.MULT16_16(mid2, mid2) + side.Val + (2 * xp.Val);
        if (Er < ((int) (0.5 + (6e-4f) * (((int) 1) << (28))))/*Inlines.QCONST32(6e-4f, 28)*/ || El < ((int) (0.5 + (6e-4f) * (((int) 1) << (28))))/*Inlines.QCONST32(6e-4f, 28)*/) {
            System.arraycopy(X, X_ptr, Y, Y_ptr, N);
            return;
        }

        kl = Inlines.celt_ilog2(El) >> 1;
        kr = Inlines.celt_ilog2(Er) >> 1;
        t = Inlines.VSHR32(El, (kl - 7) << 1);
        lgain = Inlines.celt_rsqrt_norm(t);
        t = Inlines.VSHR32(Er, (kr - 7) << 1);
        rgain = Inlines.celt_rsqrt_norm(t);

        if (kl < 7) {
            kl = 7;
        }
        if (kr < 7) {
            kr = 7;
        }

        for (j = 0; j < N; j++) {
            int r, l;
            /* Apply mid scaling (side is already scaled) */
            l = Inlines.MULT16_16_P15(mid, X[X_ptr + j]);
            r = Y[Y_ptr + j];
            X[X_ptr + j] = Inlines.EXTRACT16(Inlines.PSHR32(Inlines.MULT16_16(lgain, Inlines.SUB16(l, r)), kl + 1));
            Y[Y_ptr + j] = Inlines.EXTRACT16(Inlines.PSHR32(Inlines.MULT16_16(rgain, Inlines.ADD16(l, r)), kr + 1));
        }
    }

    /* Decide whether we should spread the pulses in the current frame */
    static int spreading_decision(CeltMode m, int[][] X, BoxedValueInt average,
            int last_decision, BoxedValueInt hf_average, BoxedValueInt tapset_decision, int update_hf,
            int end, int C, int M) {
        int i, c;
        int sum = 0, nbBands = 0;
        short[] eBands = m.eBands;
        int decision;
        int hf_sum = 0;

        Inlines.OpusAssert(end > 0);

        if (M * (eBands[end] - eBands[end - 1]) <= 8) {
            return Spread.SPREAD_NONE;
        }

        c = 0;

        do {
            for (i = 0; i < end; i++) {
                int j, N, tmp = 0;
                int[] tcount = {0, 0, 0};
                int[] x = X[c];
                int x_ptr = M * eBands[i];
                N = M * (eBands[i + 1] - eBands[i]);
                if (N <= 8) {
                    continue;
                }
                /* Compute rough CDF of |x[j]| */
                for (j = x_ptr; j < N + x_ptr; j++) {
                    int x2N;
                    /* Q13 */

                    x2N = Inlines.MULT16_16(Inlines.MULT16_16_Q15(x[j], x[j]), N);
                    if (x2N < ((short) (0.5 + (0.25f) * (((int) 1) << (13))))/*Inlines.QCONST16(0.25f, 13)*/) {
                        tcount[0]++;
                    }
                    if (x2N < ((short) (0.5 + (0.0625f) * (((int) 1) << (13))))/*Inlines.QCONST16(0.0625f, 13)*/) {
                        tcount[1]++;
                    }
                    if (x2N < ((short) (0.5 + (0.015625f) * (((int) 1) << (13))))/*Inlines.QCONST16(0.015625f, 13)*/) {
                        tcount[2]++;
                    }
                }

                /* Only include four last bands (8 kHz and up) */
                if (i > m.nbEBands - 4) {
                    hf_sum += Inlines.celt_udiv(32 * (tcount[1] + tcount[0]), N);
                }

                tmp = (2 * tcount[2] >= N ? 1 : 0) + (2 * tcount[1] >= N ? 1 : 0) + (2 * tcount[0] >= N ? 1 : 0);
                sum += tmp * 256;
                nbBands++;
            }
        } while (++c < C);

        if (update_hf != 0) {
            if (hf_sum != 0) {
                hf_sum = Inlines.celt_udiv(hf_sum, C * (4 - m.nbEBands + end));
            }

            hf_average.Val = (hf_average.Val + hf_sum) >> 1;
            hf_sum = hf_average.Val;

            if (tapset_decision.Val == 2) {
                hf_sum += 4;
            } else if (tapset_decision.Val == 0) {
                hf_sum -= 4;
            }
            if (hf_sum > 22) {
                tapset_decision.Val = 2;
            } else if (hf_sum > 18) {
                tapset_decision.Val = 1;
            } else {
                tapset_decision.Val = 0;
            }
        }

        Inlines.OpusAssert(nbBands > 0);
        /* end has to be non-zero */
        Inlines.OpusAssert(sum >= 0);
        sum = Inlines.celt_udiv(sum, nbBands);

        /* Recursive averaging */
        sum = (sum + average.Val) >> 1;
        average.Val = sum;

        /* Hysteresis */
        sum = (3 * sum + (((3 - last_decision) << 7) + 64) + 2) >> 2;
        if (sum < 80) {
            decision = Spread.SPREAD_AGGRESSIVE;
        } else if (sum < 256) {
            decision = Spread.SPREAD_NORMAL;
        } else if (sum < 384) {
            decision = Spread.SPREAD_LIGHT;
        } else {
            decision = Spread.SPREAD_NONE;
        }
        return decision;
    }

    static void deinterleave_hadamard(int[] X, int X_ptr, int N0, int stride, int hadamard) {
        int i, j;
        int N;
        N = N0 * stride;
        int[] tmp = new int[N];

        Inlines.OpusAssert(stride > 0);
        if (hadamard != 0) {
            int ordery = (stride - 2);

            for (i = 0; i < stride; i++) {
                for (j = 0; j < N0; j++) {
                    tmp[CeltTables.ordery_table[ordery + i] * N0 + j] = X[j * stride + i + X_ptr];
                }
            }
        } else {
            for (i = 0; i < stride; i++) {
                for (j = 0; j < N0; j++) {
                    tmp[i * N0 + j] = X[j * stride + i + X_ptr];
                }
            }
        }

        System.arraycopy(tmp, 0, X, X_ptr, N);
    }

    static void interleave_hadamard(int[] X, int X_ptr, int N0, int stride, int hadamard) {
        int i, j;
        int N;
        N = N0 * stride;
        int[] tmp = new int[N];

        if (hadamard != 0) {
            int ordery = (stride - 2);
            for (i = 0; i < stride; i++) {
                for (j = 0; j < N0; j++) {
                    tmp[j * stride + i] = X[CeltTables.ordery_table[ordery + i] * N0 + j + X_ptr];
                }
            }
        } else {
            for (i = 0; i < stride; i++) {
                for (j = 0; j < N0; j++) {
                    tmp[j * stride + i] = X[i * N0 + j + X_ptr];
                }
            }
        }

        System.arraycopy(tmp, 0, X, X_ptr, N);
    }

    static void haar1(int[] X, int X_ptr, int N0, int stride) {
        int i, j;
        N0 >>= 1;
        for (i = 0; i < stride; i++) {
            for (j = 0; j < N0; j++) {
                int tmpidx = X_ptr + i + (stride * 2 * j);
                int tmp1, tmp2;
                tmp1 = Inlines.MULT16_16(((short) (0.5 + (.70710678f) * (((int) 1) << (15))))/*Inlines.QCONST16(.70710678f, 15)*/, X[tmpidx]);
                tmp2 = Inlines.MULT16_16(((short) (0.5 + (.70710678f) * (((int) 1) << (15))))/*Inlines.QCONST16(.70710678f, 15)*/, X[tmpidx + stride]);
                X[tmpidx] = Inlines.EXTRACT16(Inlines.PSHR32(Inlines.ADD32(tmp1, tmp2), 15));
                X[tmpidx + stride] = Inlines.EXTRACT16(Inlines.PSHR32(Inlines.SUB32(tmp1, tmp2), 15));
            }
        }
    }

    static void haar1ZeroOffset(int[] X, int N0, int stride) {
        int i, j;
        N0 >>= 1;
        for (i = 0; i < stride; i++) {
            for (j = 0; j < N0; j++) {
                int tmpidx = i + (stride * 2 * j);
                int tmp1, tmp2;
                tmp1 = Inlines.MULT16_16(((short) (0.5 + (.70710678f) * (((int) 1) << (15))))/*Inlines.QCONST16(.70710678f, 15)*/, X[tmpidx]);
                tmp2 = Inlines.MULT16_16(((short) (0.5 + (.70710678f) * (((int) 1) << (15))))/*Inlines.QCONST16(.70710678f, 15)*/, X[tmpidx + stride]);
                X[tmpidx] = Inlines.EXTRACT16(Inlines.PSHR32(Inlines.ADD32(tmp1, tmp2), 15));
                X[tmpidx + stride] = Inlines.EXTRACT16(Inlines.PSHR32(Inlines.SUB32(tmp1, tmp2), 15));
            }
        }
    }

    static int compute_qn(int N, int b, int offset, int pulse_cap, int stereo) {
        short[] exp2_table8
                = {16384, 17866, 19483, 21247, 23170, 25267, 27554, 30048};
        int qn, qb;
        int N2 = 2 * N - 1;
        if (stereo != 0 && N == 2) {
            N2--;
        }

        /* The upper limit ensures that in a stereo split with itheta==16384, we'll
            always have enough bits left over to code at least one pulse in the
            side; otherwise it would collapse, since it doesn't get folded. */
        qb = Inlines.celt_sudiv(b + N2 * offset, N2);
        qb = Inlines.IMIN(b - pulse_cap - (4 << EntropyCoder.BITRES), qb);

        qb = Inlines.IMIN(8 << EntropyCoder.BITRES, qb);

        if (qb < (1 << EntropyCoder.BITRES >> 1)) {
            qn = 1;
        } else {
            qn = exp2_table8[qb & 0x7] >> (14 - (qb >> EntropyCoder.BITRES));
            qn = (qn + 1) >> 1 << 1;
        }
        Inlines.OpusAssert(qn <= 256);
        return qn;
    }

    public static class band_ctx {

        public int encode;
        public CeltMode m;
        public int i;
        public int intensity;
        public int spread;
        public int tf_change;
        public EntropyCoder ec;
        public int remaining_bits;
        public int[][] bandE;
        public int seed;
    };

    public static class split_ctx {

        public int inv;
        public int imid;
        public int iside;
        public int delta;
        public int itheta;
        public int qalloc;
    };

    static void compute_theta(band_ctx ctx, split_ctx sctx,
            int[] X, int X_ptr, int[] Y, int Y_ptr, int N, BoxedValueInt b, int B, int B0,
            int LM,
            int stereo, BoxedValueInt fill) {
        int qn;
        int itheta = 0;
        int delta;
        int imid, iside;
        int qalloc;
        int pulse_cap;
        int offset;
        int tell;
        int inv = 0;
        int encode;
        CeltMode m;
        int i;
        int intensity;
        EntropyCoder ec; // porting note: pointer
        int[][] bandE;

        encode = ctx.encode;
        m = ctx.m;
        i = ctx.i;
        intensity = ctx.intensity;
        ec = ctx.ec;
        bandE = ctx.bandE;

        /* Decide on the resolution to give to the split parameter theta */
        pulse_cap = m.logN[i] + LM * (1 << EntropyCoder.BITRES);
        offset = (pulse_cap >> 1) - (stereo != 0 && N == 2 ? CeltConstants.QTHETA_OFFSET_TWOPHASE : CeltConstants.QTHETA_OFFSET);
        qn = compute_qn(N, b.Val, offset, pulse_cap, stereo);
        if (stereo != 0 && i >= intensity) {
            qn = 1;
        }

        if (encode != 0) {
            /* theta is the atan() of the ratio between the (normalized)
               side and mid. With just that parameter, we can re-scale both
               mid and side because we know that 1) they have unit norm and
               2) they are orthogonal. */
            itheta = VQ.stereo_itheta(X, X_ptr, Y, Y_ptr, stereo, N);
        }

        tell = (int) ec.tell_frac();

        if (qn != 1) {
            if (encode != 0) {
                itheta = (itheta * qn + 8192) >> 14;
            }

            /* Entropy coding of the angle. We use a uniform pdf for the
               time split, a step for stereo, and a triangular one for the rest. */
            if (stereo != 0 && N > 2) {
                int p0 = 3;
                int x = itheta;
                int x0 = qn / 2;
                long ft = Inlines.CapToUInt32(p0 * (x0 + 1) + x0);
                /* Use a probability of p0 up to itheta=8192 and then use 1 after */
                if (encode != 0) {
                    ec.encode(
                            (x <= x0
                                    ? (p0 * x)
                                    : ((x - 1 - x0) + (x0 + 1) * p0)),
                            (x <= x0
                                    ? (p0 * (x + 1))
                                    : ((x - x0) + (x0 + 1) * p0)),
                            ft);
                } else {
                    int fs = (int) ec.decode(ft);
                    if (fs < (x0 + 1) * p0) {
                        x = fs / p0;
                    } else {
                        x = x0 + 1 + (fs - (x0 + 1) * p0);
                    }

                    ec.dec_update(
                            (x <= x0
                                    ? p0 * x
                                    : (x - 1 - x0) + (x0 + 1) * p0),
                            (x <= x0
                                    ? p0 * (x + 1)
                                    : (x - x0) + (x0 + 1) * p0),
                            ft);
                    itheta = x;
                }
            } else if (B0 > 1 || stereo != 0) {
                /* Uniform pdf */
                if (encode != 0) {
                    ec.enc_uint(itheta, (qn + 1));
                } else {
                    itheta = (int) ec.dec_uint(qn + 1);
                }
            } else {
                int fs = 1, ft;
                ft = ((qn >> 1) + 1) * ((qn >> 1) + 1);
                if (encode != 0) {
                    int fl;

                    fs = itheta <= (qn >> 1) ? itheta + 1 : qn + 1 - itheta;
                    fl = itheta <= (qn >> 1) ? itheta * (itheta + 1) >> 1
                            : ft - ((qn + 1 - itheta) * (qn + 2 - itheta) >> 1);

                    ec.encode(fl, (fl + fs), ft);
                } else {
                    /* Triangular pdf */
                    int fl = 0;
                    int fm;
                    fm = (int) ec.decode(ft);

                    if (fm < ((qn >> 1) * ((qn >> 1) + 1) >> 1)) {
                        itheta = (Inlines.isqrt32(8 * fm + 1) - 1) >> 1;
                        fs = itheta + 1;
                        fl = itheta * (itheta + 1) >> 1;
                    } else {
                        itheta = (2 * (qn + 1) - Inlines.isqrt32(8 * (ft - fm - 1) + 1)) >> 1;
                        fs = qn + 1 - itheta;
                        fl = ft - ((qn + 1 - itheta) * (qn + 2 - itheta) >> 1);
                    }

                    ec.dec_update(fl, (fl + fs), ft);
                }
            }
            Inlines.OpusAssert(itheta >= 0);
            itheta = Inlines.celt_udiv(itheta * 16384, qn);
            if (encode != 0 && stereo != 0) {
                if (itheta == 0) {
                    intensity_stereo(m, X, X_ptr, Y, Y_ptr, bandE, i, N);
                } else {
                    stereo_split(X, X_ptr, Y, Y_ptr, N);
                }
            }
        } else if (stereo != 0) {
            if (encode != 0) {
                inv = itheta > 8192 ? 1 : 0;
                if (inv != 0) {
                    int j;
                    for (j = 0; j < N; j++) {
                        Y[Y_ptr + j] = (0 - Y[Y_ptr + j]);
                    }
                }
                intensity_stereo(m, X, X_ptr, Y, Y_ptr, bandE, i, N);
            }
            if (b.Val > 2 << EntropyCoder.BITRES && ctx.remaining_bits > 2 << EntropyCoder.BITRES) {
                if (encode != 0) {
                    ec.enc_bit_logp(inv, 2);
                } else {
                    inv = ec.dec_bit_logp(2);
                }
            } else {
                inv = 0;
            }
            itheta = 0;
        }
        qalloc = (int) ec.tell_frac() - tell;
        b.Val -= qalloc;

        if (itheta == 0) {
            imid = 32767;
            iside = 0;
            fill.Val &= (1 << B) - 1;
            delta = -16384;
        } else if (itheta == 16384) {
            imid = 0;
            iside = 32767;
            fill.Val &= ((1 << B) - 1) << B;
            delta = 16384;
        } else {
            imid = bitexact_cos((short) itheta);
            iside = bitexact_cos((short) (16384 - itheta));
            /* This is the mid vs side allocation that minimizes squared error
               in that band. */
            delta = Inlines.FRAC_MUL16((N - 1) << 7, bitexact_log2tan(iside, imid));
        }

        sctx.inv = inv;
        sctx.imid = imid;
        sctx.iside = iside;
        sctx.delta = delta;
        sctx.itheta = itheta;
        sctx.qalloc = qalloc;
    }

    static int quant_band_n1(band_ctx ctx, int[] X, int X_ptr, int[] Y, int Y_ptr, int b,
            int[] lowband_out, int lowband_out_ptr) {
        int resynth = ctx.encode == 0 ? 1 : 0;
        int c;
        int stereo;
        int[] x = X;
        int x_ptr = X_ptr;
        int encode;
        EntropyCoder ec; // porting note: pointer

        encode = ctx.encode;
        ec = ctx.ec;

        stereo = (Y != null) ? 1 : 0;
        c = 0;
        do {
            int sign = 0;
            if (ctx.remaining_bits >= 1 << EntropyCoder.BITRES) {
                if (encode != 0) {
                    sign = x[x_ptr] < 0 ? 1 : 0;
                    ec.enc_bits(sign, 1);
                } else {
                    sign = ec.dec_bits(1);
                }
                ctx.remaining_bits -= 1 << EntropyCoder.BITRES;
                b -= 1 << EntropyCoder.BITRES;
            }
            if (resynth != 0) {
                x[x_ptr] = sign != 0 ? 0 - CeltConstants.NORM_SCALING : CeltConstants.NORM_SCALING;
            }
            x = Y;
            x_ptr = Y_ptr;
        } while (++c < 1 + stereo);
        if (lowband_out != null) {
            lowband_out[lowband_out_ptr] = Inlines.SHR16(X[X_ptr], 4);
        }

        return 1;
    }

    /* This function is responsible for encoding and decoding a mono partition.
       It can split the band in two and transmit the energy difference with
       the two half-bands. It can be called recursively so bands can end up being
       split in 8 parts. */
    static int quant_partition(band_ctx ctx, int[] X, int X_ptr,
            int N, int b, int B, int[] lowband, int lowband_ptr,
            int LM,
            int gain, int fill) {
        int cache_ptr;
        int q;
        int curr_bits;
        int imid = 0, iside = 0;
        int B0 = B;
        int mid = 0, side = 0;
        int cm = 0;
        int resynth = (ctx.encode == 0) ? 1 : 0;
        int Y = 0;
        int encode;
        CeltMode m; //porting note: pointer
        int i;
        int spread;
        EntropyCoder ec; //porting note: pointer

        encode = ctx.encode;
        m = ctx.m;
        i = ctx.i;
        spread = ctx.spread;
        ec = ctx.ec;
        short[] cache = m.cache.bits;
        /* If we need 1.5 more bits than we can produce, split the band in two. */
        cache_ptr = m.cache.index[(LM + 1) * m.nbEBands + i];
        if (LM != -1 && b > cache[cache_ptr + cache[cache_ptr]] + 12 && N > 2) {
            int mbits, sbits, delta;
            int itheta;
            int qalloc;
            split_ctx sctx = new split_ctx();
            int next_lowband2 = 0;
            int rebalance;

            N >>= 1;
            Y = X_ptr + N;
            LM -= 1;
            if (B == 1) {
                fill = (fill & 1) | (fill << 1);
            }

            B = (B + 1) >> 1;

            BoxedValueInt boxed_b = new BoxedValueInt(b);
            BoxedValueInt boxed_fill = new BoxedValueInt(fill);
            compute_theta(ctx, sctx, X, X_ptr, X, Y, N, boxed_b, B, B0, LM, 0, boxed_fill);
            b = boxed_b.Val;
            fill = boxed_fill.Val;

            imid = sctx.imid;
            iside = sctx.iside;
            delta = sctx.delta;
            itheta = sctx.itheta;
            qalloc = sctx.qalloc;
            mid = (imid);
            side = (iside);

            /* Give more bits to low-energy MDCTs than they would otherwise deserve */
            if (B0 > 1 && ((itheta & 0x3fff) != 0)) {
                if (itheta > 8192) /* Rough approximation for pre-echo masking */ {
                    delta -= delta >> (4 - LM);
                } else /* Corresponds to a forward-masking slope of 1.5 dB per 10 ms */ {
                    delta = Inlines.IMIN(0, delta + (N << EntropyCoder.BITRES >> (5 - LM)));
                }
            }
            mbits = Inlines.IMAX(0, Inlines.IMIN(b, (b - delta) / 2));
            sbits = b - mbits;
            ctx.remaining_bits -= qalloc;

            if (lowband != null) {
                next_lowband2 = (lowband_ptr + N);
                /* >32-bit split case */
            }

            rebalance = ctx.remaining_bits;
            if (mbits >= sbits) {
                cm = quant_partition(ctx, X, X_ptr, N, mbits, B,
                        lowband, lowband_ptr, LM,
                        Inlines.MULT16_16_P15(gain, mid), fill);
                rebalance = mbits - (rebalance - ctx.remaining_bits);
                if (rebalance > 3 << EntropyCoder.BITRES && itheta != 0) {
                    sbits += rebalance - (3 << EntropyCoder.BITRES);
                }
                cm |= quant_partition(ctx, X, Y, N, sbits, B,
                        lowband, next_lowband2, LM,
                        Inlines.MULT16_16_P15(gain, side), fill >> B) << (B0 >> 1);
            } else {
                cm = quant_partition(ctx, X, Y, N, sbits, B,
                        lowband, next_lowband2, LM,
                        Inlines.MULT16_16_P15(gain, side), fill >> B) << (B0 >> 1);
                rebalance = sbits - (rebalance - ctx.remaining_bits);
                if (rebalance > 3 << EntropyCoder.BITRES && itheta != 16384) {
                    mbits += rebalance - (3 << EntropyCoder.BITRES);
                }
                cm |= quant_partition(ctx, X, X_ptr, N, mbits, B,
                        lowband, lowband_ptr, LM,
                        Inlines.MULT16_16_P15(gain, mid), fill);
            }
        } else {
            /* This is the basic no-split case */
            q = Rate.bits2pulses(m, i, LM, b);
            curr_bits = Rate.pulses2bits(m, i, LM, q);
            ctx.remaining_bits -= curr_bits;

            /* Ensures we can never bust the budget */
            while (ctx.remaining_bits < 0 && q > 0) {
                ctx.remaining_bits += curr_bits;
                q--;
                curr_bits = Rate.pulses2bits(m, i, LM, q);
                ctx.remaining_bits -= curr_bits;
            }

            if (q != 0) {
                int K = Rate.get_pulses(q);

                /* Finally do the actual quantization */
                if (encode != 0) {
                    cm = VQ.alg_quant(X, X_ptr, N, K, spread, B, ec);
                } else {
                    cm = VQ.alg_unquant(X, X_ptr, N, K, spread, B, ec, gain);
                }
            } else {
                /* If there's no pulse, fill the band anyway */
                int j;

                if (resynth != 0) {
                    int cm_mask;
                    /* B can be as large as 16, so this shift might overflow an int on a
                       16-bit platform; use a long to get defined behavior.*/
                    cm_mask = (1 << B) - 1;
                    fill = fill & cm_mask;

                    if (fill == 0) {
                        Arrays.MemSetWithOffset(X, 0, X_ptr, N);
                    } else {
                        if (lowband == null) {
                            /* Noise */
                            for (j = 0; j < N; j++) {
                                ctx.seed = celt_lcg_rand(ctx.seed);
                                X[X_ptr + j] = (((int) ctx.seed) >> 20);
                            }
                            cm = cm_mask;
                        } else {
                            /* Folded spectrum */
                            for (j = 0; j < N; j++) {
                                int tmp;
                                ctx.seed = celt_lcg_rand(ctx.seed);
                                /* About 48 dB below the "normal" folding level */
                                tmp = ((short) (0.5 + (1.0f / 256) * (((int) 1) << (10))))/*Inlines.QCONST16(1.0f / 256, 10)*/;
                                tmp = (((ctx.seed) & 0x8000) != 0 ? tmp : 0 - tmp);
                                X[X_ptr + j] = (lowband[lowband_ptr + j] + tmp);
                            }
                            cm = fill;
                        }

                        VQ.renormalise_vector(X, X_ptr, N, gain);
                    }
                }
            }
        }

        return cm;
    }

    private static final byte[] bit_interleave_table = {0, 1, 1, 1, 2, 3, 3, 3, 2, 3, 3, 3, 2, 3, 3, 3};

    private static final short[] bit_deinterleave_table = {
        0x00, 0x03, 0x0C, 0x0F, 0x30, 0x33, 0x3C, 0x3F,
        0xC0, 0xC3, 0xCC, 0xCF, 0xF0, 0xF3, 0xFC, 0xFF
    };

    /* This function is responsible for encoding and decoding a band for the mono case. */
    static int quant_band(band_ctx ctx, int[] X, int X_ptr,
            int N, int b, int B, int[] lowband, int lowband_ptr,
            int LM, int[] lowband_out, int lowband_out_ptr,
            int gain, int[] lowband_scratch, int lowband_scratch_ptr, int fill) {
        int N0 = N;
        int N_B = N;
        int N_B0;
        int B0 = B;
        int time_divide = 0;
        int recombine = 0;
        int longBlocks;
        int cm = 0;
        int resynth = ctx.encode == 0 ? 1 : 0;
        int k;
        int encode;
        int tf_change;

        encode = ctx.encode;
        tf_change = ctx.tf_change;

        longBlocks = B0 == 1 ? 1 : 0;

        N_B = Inlines.celt_udiv(N_B, B);

        /* Special case for one sample */
        if (N == 1) {
            return quant_band_n1(ctx, X, X_ptr, null, 0, b, lowband_out, lowband_out_ptr);
        }

        if (tf_change > 0) {
            recombine = tf_change;
        }
        /* Band recombining to increase frequency resolution */

        if (lowband_scratch != null && lowband != null && (recombine != 0 || ((N_B & 1) == 0 && tf_change < 0) || B0 > 1)) {
            System.arraycopy(lowband, lowband_ptr, lowband_scratch, lowband_scratch_ptr, N);
            lowband = lowband_scratch;
            lowband_ptr = lowband_scratch_ptr;
        }

        for (k = 0; k < recombine; k++) {
            if (encode != 0) {
                haar1(X, X_ptr, N >> k, 1 << k);
            }
            if (lowband != null) {
                haar1(lowband, lowband_ptr, N >> k, 1 << k);
            }
            int idx1 = fill & 0xF;
            int idx2 = fill >> 4;
            if (idx1 < 0) {
                System.out.println("e");
            }
            if (idx2 < 0) {
                System.out.println("e");
            }
            fill = bit_interleave_table[fill & 0xF] | bit_interleave_table[fill >> 4] << 2;
        }
        B >>= recombine;
        N_B <<= recombine;

        /* Increasing the time resolution */
        while ((N_B & 1) == 0 && tf_change < 0) {
            if (encode != 0) {
                haar1(X, X_ptr, N_B, B);
            }
            if (lowband != null) {
                haar1(lowband, lowband_ptr, N_B, B);
            }
            fill |= fill << B;
            B <<= 1;
            N_B >>= 1;
            time_divide++;
            tf_change++;
        }
        B0 = B;
        N_B0 = N_B;

        /* Reorganize the samples in time order instead of frequency order */
        if (B0 > 1) {
            if (encode != 0) {
                deinterleave_hadamard(X, X_ptr, N_B >> recombine, B0 << recombine, longBlocks);
            }
            if (lowband != null) {
                deinterleave_hadamard(lowband, lowband_ptr, N_B >> recombine, B0 << recombine, longBlocks);
            }
        }

        cm = quant_partition(ctx, X, X_ptr, N, b, B, lowband, lowband_ptr, LM, gain, fill);

        /* This code is used by the decoder and by the resynthesis-enabled encoder */
        if (resynth != 0) {
            /* Undo the sample reorganization going from time order to frequency order */
            if (B0 > 1) {
                interleave_hadamard(X, X_ptr, N_B >> recombine, B0 << recombine, longBlocks);
            }

            /* Undo time-freq changes that we did earlier */
            N_B = N_B0;
            B = B0;
            for (k = 0; k < time_divide; k++) {
                B >>= 1;
                N_B <<= 1;
                cm |= cm >> B;
                haar1(X, X_ptr, N_B, B);
            }

            for (k = 0; k < recombine; k++) {
                cm = bit_deinterleave_table[cm];
                haar1(X, X_ptr, N0 >> k, 1 << k);
            }
            B <<= recombine;

            /* Scale output for later folding */
            if (lowband_out != null) {
                int j;
                int n;
                n = (Inlines.celt_sqrt(Inlines.SHL32(N0, 22)));
                for (j = 0; j < N0; j++) {
                    lowband_out[lowband_out_ptr + j] = Inlines.MULT16_16_Q15(n, X[X_ptr + j]);
                }
            }

            cm = cm & ((1 << B) - 1);
        }
        return cm;
    }

    /* This function is responsible for encoding and decoding a band for the stereo case. */
    static int quant_band_stereo(band_ctx ctx, int[] X, int X_ptr, int[] Y, int Y_ptr,
            int N, int b, int B, int[] lowband, int lowband_ptr,
            int LM, int[] lowband_out, int lowband_out_ptr,
            int[] lowband_scratch, int lowband_scratch_ptr, int fill) {
        int imid = 0, iside = 0;
        int inv = 0;
        int mid = 0, side = 0;
        int cm = 0;
        int resynth = ctx.encode == 0 ? 1 : 0;
        int mbits, sbits, delta;
        int itheta;
        int qalloc;
        split_ctx sctx = new split_ctx(); // porting note: stack var
        int orig_fill;
        int encode;
        EntropyCoder ec; //porting note: pointer

        encode = ctx.encode;
        ec = ctx.ec;

        /* Special case for one sample */
        if (N == 1) {
            return quant_band_n1(ctx, X, X_ptr, Y, Y_ptr, b, lowband_out, lowband_out_ptr);
        }

        orig_fill = fill;

        BoxedValueInt boxed_b = new BoxedValueInt(b);
        BoxedValueInt boxed_fill = new BoxedValueInt(fill);
        compute_theta(ctx, sctx, X, X_ptr, Y, Y_ptr, N, boxed_b, B, B, LM, 1, boxed_fill);
        b = boxed_b.Val;
        fill = boxed_fill.Val;

        inv = sctx.inv;
        imid = sctx.imid;
        iside = sctx.iside;
        delta = sctx.delta;
        itheta = sctx.itheta;
        qalloc = sctx.qalloc;
        mid = (imid);
        side = (iside);

        /* This is a special case for N=2 that only works for stereo and takes
           advantage of the fact that mid and side are orthogonal to encode
           the side with just one bit. */
        if (N == 2) {
            int c;
            int sign = 0;
            int[] x2, y2;
            int x2_ptr, y2_ptr;
            mbits = b;
            sbits = 0;
            /* Only need one bit for the side. */
            if (itheta != 0 && itheta != 16384) {
                sbits = 1 << EntropyCoder.BITRES;
            }
            mbits -= sbits;
            c = itheta > 8192 ? 1 : 0;
            ctx.remaining_bits -= qalloc + sbits;
            if (c != 0) {
                x2 = Y;
                x2_ptr = Y_ptr;
                y2 = X;
                y2_ptr = X_ptr;
            } else {
                x2 = X;
                x2_ptr = X_ptr;
                y2 = Y;
                y2_ptr = Y_ptr;
            }

            if (sbits != 0) {
                if (encode != 0) {
                    /* Here we only need to encode a sign for the side. */
                    sign = (x2[x2_ptr] * y2[Y_ptr + 1] - x2[x2_ptr + 1] * y2[Y_ptr] < 0) ? 1 : 0;
                    ec.enc_bits(sign, 1);
                } else {
                    sign = ec.dec_bits(1);
                }
            }
            sign = 1 - 2 * sign;
            /* We use orig_fill here because we want to fold the side, but if
               itheta==16384, we'll have cleared the low bits of fill. */
            cm = quant_band(ctx, x2, x2_ptr, N, mbits, B, lowband, lowband_ptr,
                    LM, lowband_out, lowband_out_ptr, CeltConstants.Q15ONE, lowband_scratch, lowband_scratch_ptr, orig_fill);

            /* We don't split N=2 bands, so cm is either 1 or 0 (for a fold-collapse),
               and there's no need to worry about mixing with the other channel. */
            y2[Y_ptr] = ((0 - sign) * x2[x2_ptr + 1]);
            y2[Y_ptr + 1] = (sign * x2[x2_ptr]);
            if (resynth != 0) {
                int tmp;
                X[X_ptr] = Inlines.MULT16_16_Q15(mid, X[X_ptr]);
                X[X_ptr + 1] = Inlines.MULT16_16_Q15(mid, X[X_ptr + 1]);
                Y[Y_ptr] = Inlines.MULT16_16_Q15(side, Y[Y_ptr]);
                Y[Y_ptr + 1] = Inlines.MULT16_16_Q15(side, Y[Y_ptr + 1]);
                tmp = X[X_ptr];
                X[X_ptr] = Inlines.SUB16(tmp, Y[Y_ptr]);
                Y[Y_ptr] = Inlines.ADD16(tmp, Y[Y_ptr]);
                tmp = X[X_ptr + 1];
                X[X_ptr + 1] = Inlines.SUB16(tmp, Y[Y_ptr + 1]);
                Y[Y_ptr + 1] = Inlines.ADD16(tmp, Y[Y_ptr + 1]);
            }
        } else {
            /* "Normal" split code */
            int rebalance;

            mbits = Inlines.IMAX(0, Inlines.IMIN(b, (b - delta) / 2));
            sbits = b - mbits;
            ctx.remaining_bits -= qalloc;

            rebalance = ctx.remaining_bits;
            if (mbits >= sbits) {
                /* In stereo mode, we do not apply a scaling to the mid because we need the normalized
                   mid for folding later. */
                cm = quant_band(ctx, X, X_ptr, N, mbits, B,
                        lowband, lowband_ptr, LM, lowband_out, lowband_out_ptr,
                        CeltConstants.Q15ONE, lowband_scratch, lowband_scratch_ptr, fill);
                rebalance = mbits - (rebalance - ctx.remaining_bits);
                if (rebalance > 3 << EntropyCoder.BITRES && itheta != 0) {
                    sbits += rebalance - (3 << EntropyCoder.BITRES);
                }

                /* For a stereo split, the high bits of fill are always zero, so no
                   folding will be done to the side. */
                cm |= quant_band(ctx, Y, Y_ptr, N, sbits, B,
                        null, 0, LM, null, 0,
                        side, null, 0, fill >> B);
            } else {
                /* For a stereo split, the high bits of fill are always zero, so no
                   folding will be done to the side. */
                cm = quant_band(ctx, Y, Y_ptr, N, sbits, B,
                        null, 0, LM, null, 0,
                        side, null, 0, fill >> B);
                rebalance = sbits - (rebalance - ctx.remaining_bits);
                if (rebalance > 3 << EntropyCoder.BITRES && itheta != 16384) {
                    mbits += rebalance - (3 << EntropyCoder.BITRES);
                }
                /* In stereo mode, we do not apply a scaling to the mid because we need the normalized
                   mid for folding later. */
                cm |= quant_band(ctx, X, X_ptr, N, mbits, B,
                        lowband, lowband_ptr, LM, lowband_out, lowband_out_ptr,
                        CeltConstants.Q15ONE, lowband_scratch, lowband_scratch_ptr, fill);
            }
        }


        /* This code is used by the decoder and by the resynthesis-enabled encoder */
        if (resynth != 0) {
            if (N != 2) {
                stereo_merge(X, X_ptr, Y, Y_ptr, mid, N);
            }
            if (inv != 0) {
                int j;
                for (j = Y_ptr; j < N + Y_ptr; j++) {
                    Y[j] = (short) (0 - Y[j]);
                }
            }
        }

        return cm;
    }

    static void quant_all_bands(int encode, CeltMode m, int start, int end,
            int[] X_, int[] Y_, short[] collapse_masks,
            int[][] bandE, int[] pulses, int shortBlocks, int spread,
            int dual_stereo, int intensity, int[] tf_res, int total_bits,
            int balance, EntropyCoder ec, int LM, int codedBands,
            BoxedValueInt seed) {
        int i;
        int remaining_bits;
        short[] eBands = m.eBands;
        int[] norm;
        int norm2;
        int[] lowband_scratch;
        int lowband_scratch_ptr;
        int B;
        int M;
        int lowband_offset;
        int update_lowband = 1;
        int C = Y_ != null ? 2 : 1;
        int norm_offset;
        int resynth = encode == 0 ? 1 : 0;
        band_ctx ctx = new band_ctx(); // porting note: stack var

        M = 1 << LM;
        B = (shortBlocks != 0) ? M : 1;
        norm_offset = M * eBands[start];

        /* No need to allocate norm for the last band because we don't need an
           output in that band. */
        norm = new int[(C * (M * eBands[m.nbEBands - 1] - norm_offset))];
        norm2 = M * eBands[m.nbEBands - 1] - norm_offset;

        /* We can use the last band as scratch space because we don't need that
           scratch space for the last band. */
        lowband_scratch = X_;
        lowband_scratch_ptr = M * eBands[m.nbEBands - 1];

        lowband_offset = 0;
        ctx.bandE = bandE;
        ctx.ec = ec;
        ctx.encode = encode;
        ctx.intensity = intensity;
        ctx.m = m;
        ctx.seed = seed.Val;
        ctx.spread = spread;
        for (i = start; i < end; i++) {
            int tell;
            int b;
            int N;
            int curr_balance;
            int effective_lowband = -1;
            int[] X, Y;
            int X_ptr, Y_ptr;
            Y_ptr = 0;
            int tf_change = 0;
            long x_cm;
            long y_cm;
            int last;

            ctx.i = i;
            last = (i == end - 1) ? 1 : 0;

            X = X_;
            X_ptr = (M * eBands[i]);
            if (Y_ != null) {
                Y = Y_;
                Y_ptr = (M * eBands[i]);
            } else {
                Y = null;
            }
            N = M * eBands[i + 1] - M * eBands[i];
            tell = (int) ec.tell_frac();

            /* Compute how many bits we want to allocate to this band */
            if (i != start) {
                balance -= tell;
            }
            remaining_bits = total_bits - tell - 1;
            ctx.remaining_bits = remaining_bits;
            if (i <= codedBands - 1) {
                curr_balance = Inlines.celt_sudiv(balance, Inlines.IMIN(3, codedBands - i));
                b = Inlines.IMAX(0, Inlines.IMIN(16383, Inlines.IMIN(remaining_bits + 1, pulses[i] + curr_balance)));
            } else {
                b = 0;
            }

            if (resynth != 0 && M * eBands[i] - N >= M * eBands[start] && (update_lowband != 0 || lowband_offset == 0)) {
                lowband_offset = i;
            }

            tf_change = tf_res[i];
            ctx.tf_change = tf_change;
            if (i >= m.effEBands) {
                X = norm;
                X_ptr = 0;
                if (Y_ != null) {
                    Y = norm;
                    Y_ptr = 0;
                }
                lowband_scratch = null;
            }
            if (i == end - 1) {
                lowband_scratch = null;
            }

            /* Get a conservative estimate of the collapse_mask's for the bands we're
               going to be folding from. */
            if (lowband_offset != 0 && (spread != Spread.SPREAD_AGGRESSIVE || B > 1 || tf_change < 0)) {
                int fold_start;
                int fold_end;
                int fold_i;
                /* This ensures we never repeat spectral content within one band */
                effective_lowband = Inlines.IMAX(0, M * eBands[lowband_offset] - norm_offset - N);
                fold_start = lowband_offset;
                while (M * eBands[--fold_start] > effective_lowband + norm_offset) ;
                fold_end = lowband_offset - 1;
                while (M * eBands[++fold_end] < effective_lowband + norm_offset + N) ;
                x_cm = y_cm = 0;
                fold_i = fold_start;
                do {
                    x_cm |= collapse_masks[fold_i * C + 0];
                    y_cm |= collapse_masks[fold_i * C + C - 1];
                } while (++fold_i < fold_end);
            } /* Otherwise, we'll be using the LCG to fold, so all blocks will (almost
               always) be non-zero. */ else {
                x_cm = y_cm = ((1 << B) - 1);
            }

            if (dual_stereo != 0 && i == intensity) {
                int j;

                /* Switch off dual stereo to do intensity. */
                dual_stereo = 0;
                if (resynth != 0) {
                    for (j = 0; j < M * eBands[i] - norm_offset; j++) {
                        norm[j] = (Inlines.HALF32(norm[j] + norm[norm2 + j]));
                    }
                }
            }
            if (dual_stereo != 0) {
                x_cm = quant_band(ctx,
                        X,
                        X_ptr,
                        N,
                        b / 2,
                        B,
                        effective_lowband != -1 ? norm : null,
                        effective_lowband,
                        LM,
                        last != 0 ? null : norm,
                        M * eBands[i] - norm_offset,
                        CeltConstants.Q15ONE,
                        lowband_scratch,
                        lowband_scratch_ptr,
                        (int) x_cm);
                y_cm = quant_band(
                        ctx,
                        Y,
                        Y_ptr,
                        N,
                        b / 2,
                        B,
                        effective_lowband != -1 ? norm : null,
                        norm2 + effective_lowband,
                        LM,
                        last != 0 ? null : norm,
                        norm2 + (M * eBands[i] - norm_offset),
                        CeltConstants.Q15ONE,
                        lowband_scratch,
                        lowband_scratch_ptr,
                        (int) y_cm);
            } else {
                if (Y != null) {
                    x_cm = quant_band_stereo(
                            ctx,
                            X,
                            X_ptr,
                            Y,
                            Y_ptr,
                            N,
                            b,
                            B,
                            effective_lowband != -1 ? norm : null,
                            effective_lowband,
                            LM,
                            last != 0 ? null : norm,
                            M * eBands[i] - norm_offset,
                            lowband_scratch,
                            lowband_scratch_ptr,
                            (int) (x_cm | y_cm));
                } else {
                    x_cm = quant_band(
                            ctx,
                            X,
                            X_ptr,
                            N,
                            b,
                            B,
                            effective_lowband != -1 ? norm : null,
                            effective_lowband,
                            LM,
                            last != 0 ? null : norm,
                            M * eBands[i] - norm_offset,
                            CeltConstants.Q15ONE,
                            lowband_scratch,
                            lowband_scratch_ptr,
                            (int) (x_cm | y_cm)); // opt: lots of pointers are created here too
                }
                y_cm = x_cm;
            }
            collapse_masks[i * C + 0] = (short) (x_cm & 0xFF);
            collapse_masks[i * C + C - 1] = (short) (y_cm & 0xFF);
            balance += pulses[i] + tell;

            /* Update the folding position only as long as we have 1 bit/sample depth. */
            update_lowband = (b > (N << EntropyCoder.BITRES)) ? 1 : 0;
        }

        seed.Val = ctx.seed;
    }
}
