static av_always_inline int vorbis_residue_decode_internal(vorbis_context *vc,

                                                           vorbis_residue *vr,

                                                           unsigned ch,

                                                           uint8_t *do_not_decode,

                                                           float *vec,

                                                           unsigned vlen,

                                                           unsigned ch_left,

                                                           int vr_type)

{

    GetBitContext *gb = &vc->gb;

    unsigned c_p_c        = vc->codebooks[vr->classbook].dimensions;

    uint8_t *classifs = vr->classifs;

    unsigned pass, ch_used, i, j, k, l;

    unsigned max_output = (ch - 1) * vlen;

    int ptns_to_read = vr->ptns_to_read;

    int libvorbis_bug = 0;



    if (vr_type == 2) {

        for (j = 1; j < ch; ++j)

            do_not_decode[0] &= do_not_decode[j];  // FIXME - clobbering input

        if (do_not_decode[0])

            return 0;

        ch_used = 1;

        max_output += vr->end / ch;

    } else {

        ch_used = ch;

        max_output += vr->end;

    }



    if (max_output > ch_left * vlen) {

        if (max_output <= ch_left * vlen + vr->partition_size*ch_used/ch) {

            ptns_to_read--;

            libvorbis_bug = 1;

        } else {

            av_log(vc->avctx, AV_LOG_ERROR, "Insufficient output buffer\n");

            return AVERROR_INVALIDDATA;

        }

    }



    av_dlog(NULL, " residue type 0/1/2 decode begin, ch: %d  cpc %d  \n", ch, c_p_c);



    for (pass = 0; pass <= vr->maxpass; ++pass) { // FIXME OPTIMIZE?

        int voffset, partition_count, j_times_ptns_to_read;



        voffset = vr->begin;

        for (partition_count = 0; partition_count < ptns_to_read;) {  // SPEC        error

            if (!pass) {

                int ret;

                if ((ret = setup_classifs(vc, vr, do_not_decode, ch_used, partition_count)) < 0)

                    return ret;

            }

            for (i = 0; (i < c_p_c) && (partition_count < ptns_to_read); ++i) {

                for (j_times_ptns_to_read = 0, j = 0; j < ch_used; ++j) {

                    unsigned voffs;



                    if (!do_not_decode[j]) {

                        unsigned vqclass = classifs[j_times_ptns_to_read + partition_count];

                        int vqbook  = vr->books[vqclass][pass];



                        if (vqbook >= 0 && vc->codebooks[vqbook].codevectors) {

                            unsigned coffs;

                            unsigned dim  = vc->codebooks[vqbook].dimensions;

                            unsigned step = FASTDIV(vr->partition_size << 1, dim << 1);

                            vorbis_codebook codebook = vc->codebooks[vqbook];



                            if (vr_type == 0) {



                                voffs = voffset+j*vlen;

                                for (k = 0; k < step; ++k) {

                                    coffs = get_vlc2(gb, codebook.vlc.table, codebook.nb_bits, 3) * dim;

                                    for (l = 0; l < dim; ++l)

                                        vec[voffs + k + l * step] += codebook.codevectors[coffs + l];

                                }

                            } else if (vr_type == 1) {

                                voffs = voffset + j * vlen;

                                for (k = 0; k < step; ++k) {

                                    coffs = get_vlc2(gb, codebook.vlc.table, codebook.nb_bits, 3) * dim;

                                    for (l = 0; l < dim; ++l, ++voffs) {

                                        vec[voffs]+=codebook.codevectors[coffs+l];



                                        av_dlog(NULL, " pass %d offs: %d curr: %f change: %f cv offs.: %d  \n",

                                                pass, voffs, vec[voffs], codebook.codevectors[coffs+l], coffs);

                                    }

                                }

                            } else if (vr_type == 2 && ch == 2 && (voffset & 1) == 0 && (dim & 1) == 0) { // most frequent case optimized

                                voffs = voffset >> 1;



                                if (dim == 2) {

                                    for (k = 0; k < step; ++k) {

                                        coffs = get_vlc2(gb, codebook.vlc.table, codebook.nb_bits, 3) * 2;

                                        vec[voffs + k       ] += codebook.codevectors[coffs    ];

                                        vec[voffs + k + vlen] += codebook.codevectors[coffs + 1];

                                    }

                                } else if (dim == 4) {

                                    for (k = 0; k < step; ++k, voffs += 2) {

                                        coffs = get_vlc2(gb, codebook.vlc.table, codebook.nb_bits, 3) * 4;

                                        vec[voffs           ] += codebook.codevectors[coffs    ];

                                        vec[voffs + 1       ] += codebook.codevectors[coffs + 2];

                                        vec[voffs + vlen    ] += codebook.codevectors[coffs + 1];

                                        vec[voffs + vlen + 1] += codebook.codevectors[coffs + 3];

                                    }

                                } else

                                for (k = 0; k < step; ++k) {

                                    coffs = get_vlc2(gb, codebook.vlc.table, codebook.nb_bits, 3) * dim;

                                    for (l = 0; l < dim; l += 2, voffs++) {

                                        vec[voffs       ] += codebook.codevectors[coffs + l    ];

                                        vec[voffs + vlen] += codebook.codevectors[coffs + l + 1];



                                        av_dlog(NULL, " pass %d offs: %d curr: %f change: %f cv offs.: %d+%d  \n",

                                                pass, voffset / ch + (voffs % ch) * vlen,

                                                vec[voffset / ch + (voffs % ch) * vlen],

                                                codebook.codevectors[coffs + l], coffs, l);

                                    }

                                }



                            } else if (vr_type == 2) {

                                unsigned voffs_div = FASTDIV(voffset << 1, ch <<1);

                                unsigned voffs_mod = voffset - voffs_div * ch;



                                for (k = 0; k < step; ++k) {

                                    coffs = get_vlc2(gb, codebook.vlc.table, codebook.nb_bits, 3) * dim;

                                    for (l = 0; l < dim; ++l) {

                                        vec[voffs_div + voffs_mod * vlen] +=

                                            codebook.codevectors[coffs + l];



                                        av_dlog(NULL, " pass %d offs: %d curr: %f change: %f cv offs.: %d+%d  \n",

                                                pass, voffs_div + voffs_mod * vlen,

                                                vec[voffs_div + voffs_mod * vlen],

                                                codebook.codevectors[coffs + l], coffs, l);



                                        if (++voffs_mod == ch) {

                                            voffs_div++;

                                            voffs_mod = 0;

                                        }

                                    }

                                }

                            }

                        }

                    }

                    j_times_ptns_to_read += ptns_to_read;

                }

                ++partition_count;

                voffset += vr->partition_size;

            }

        }

        if (libvorbis_bug && !pass) {

            for (j = 0; j < ch_used; ++j) {

                if (!do_not_decode[j]) {

                    get_vlc2(&vc->gb, vc->codebooks[vr->classbook].vlc.table,

                                vc->codebooks[vr->classbook].nb_bits, 3);

                }

            }

        }

    }

    return 0;

}
