/*
 ** Copyright 2003-2010, VisualOn, Inc.
 **
 ** Licensed under the Apache License, Version 2.0 (the "License");
 ** you may not use this file except in compliance with the License.
 ** You may obtain a copy of the License at
 **
 **     http://www.apache.org/licenses/LICENSE-2.0
 **
 ** Unless required by applicable law or agreed to in writing, software
 ** distributed under the License is distributed on an "AS IS" BASIS,
 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 ** See the License for the specific language governing permissions and
 ** limitations under the License.
 */

/***********************************************************************
*      File: c4t64fx.c                                                 *
*                                                                      *
*      Description:Performs algebraic codebook search for higher modes *
*                                                                      *
************************************************************************/

/************************************************************************
* Function: ACELP_4t64_fx()                                             *
*                                                                       *
* 20, 36, 44, 52, 64, 72, 88 bits algebraic codebook.                   *
* 4 tracks x 16 positions per track = 64 samples.                       *
*                                                                       *
* 20 bits --> 4 pulses in a frame of 64 samples.                        *
* 36 bits --> 8 pulses in a frame of 64 samples.                        *
* 44 bits --> 10 pulses in a frame of 64 samples.                       *
* 52 bits --> 12 pulses in a frame of 64 samples.                       *
* 64 bits --> 16 pulses in a frame of 64 samples.                       *
* 72 bits --> 18 pulses in a frame of 64 samples.                       *
* 88 bits --> 24 pulses in a frame of 64 samples.                       *
*                                                                       *
* All pulses can have two (2) possible amplitudes: +1 or -1.            *
* Each pulse can have sixteen (16) possible positions.                  *
*************************************************************************/

#include "typedef.h"
#include "basic_op.h"
#include "math_op.h"
#include "acelp.h"
#include "cnst.h"

#include "q_pulse.h"

#undef LOG_TAG
#define LOG_TAG "amrwbenc"
#include "log/log.h"

static Word16 tipos[36] = {
    0, 1, 2, 3,                            /* starting point &ipos[0], 1st iter */
    1, 2, 3, 0,                            /* starting point &ipos[4], 2nd iter */
    2, 3, 0, 1,                            /* starting point &ipos[8], 3rd iter */
    3, 0, 1, 2,                            /* starting point &ipos[12], 4th iter */
    0, 1, 2, 3,
    1, 2, 3, 0,
    2, 3, 0, 1,
    3, 0, 1, 2,
    0, 1, 2, 3};                           /* end point for 24 pulses &ipos[35], 4th iter */

#define NB_PULSE_MAX  24

#define L_SUBFR   64
#define NB_TRACK  4
#define STEP      4
#define NB_POS    16
#define MSIZE     256
#define NB_MAX    8
#define NPMAXPT   ((NB_PULSE_MAX+NB_TRACK-1)/NB_TRACK)

/* Private functions */
void cor_h_vec_012(
        Word16 h[],                           /* (i) scaled impulse response                 */
        Word16 vec[],                         /* (i) scaled vector (/8) to correlate with h[] */
        Word16 track,                         /* (i) track to use                            */
        Word16 sign[],                        /* (i) sign vector                             */
        Word16 rrixix[][NB_POS],              /* (i) correlation of h[x] with h[x]      */
        Word16 cor_1[],                       /* (o) result of correlation (NB_POS elements) */
        Word16 cor_2[]                        /* (o) result of correlation (NB_POS elements) */
        );

void cor_h_vec_012_asm(
        Word16 h[],                           /* (i) scaled impulse response                 */
        Word16 vec[],                         /* (i) scaled vector (/8) to correlate with h[] */
        Word16 track,                         /* (i) track to use                            */
        Word16 sign[],                        /* (i) sign vector                             */
        Word16 rrixix[][NB_POS],              /* (i) correlation of h[x] with h[x]      */
        Word16 cor_1[],                       /* (o) result of correlation (NB_POS elements) */
        Word16 cor_2[]                        /* (o) result of correlation (NB_POS elements) */
        );

void cor_h_vec_30(
        Word16 h[],                           /* (i) scaled impulse response                 */
        Word16 vec[],                         /* (i) scaled vector (/8) to correlate with h[] */
        Word16 track,                         /* (i) track to use                            */
        Word16 sign[],                        /* (i) sign vector                             */
        Word16 rrixix[][NB_POS],              /* (i) correlation of h[x] with h[x]      */
        Word16 cor_1[],                       /* (o) result of correlation (NB_POS elements) */
        Word16 cor_2[]                        /* (o) result of correlation (NB_POS elements) */
        );

void search_ixiy(
        Word16 nb_pos_ix,                     /* (i) nb of pos for pulse 1 (1..8)       */
        Word16 track_x,                       /* (i) track of pulse 1                   */
        Word16 track_y,                       /* (i) track of pulse 2                   */
        Word16 * ps,                          /* (i/o) correlation of all fixed pulses  */
        Word16 * alp,                         /* (i/o) energy of all fixed pulses       */
        Word16 * ix,                          /* (o) position of pulse 1                */
        Word16 * iy,                          /* (o) position of pulse 2                */
        Word16 dn[],                          /* (i) corr. between target and h[]       */
        Word16 dn2[],                         /* (i) vector of selected positions       */
        Word16 cor_x[],                       /* (i) corr. of pulse 1 with fixed pulses */
        Word16 cor_y[],                       /* (i) corr. of pulse 2 with fixed pulses */
        Word16 rrixiy[][MSIZE]                /* (i) corr. of pulse 1 with pulse 2   */
        );


void ACELP_4t64_fx(
        Word16 dn[],                          /* (i) <12b : correlation between target x[] and H[]      */
        Word16 cn[],                          /* (i) <12b : residual after long term prediction         */
        Word16 H[],                           /* (i) Q12: impulse response of weighted synthesis filter */
        Word16 code[],                        /* (o) Q9 : algebraic (fixed) codebook excitation         */
        Word16 y[],                           /* (o) Q9 : filtered fixed codebook excitation            */
        Word16 nbbits,                        /* (i) : 20, 36, 44, 52, 64, 72 or 88 bits                */
        Word16 ser_size,                      /* (i) : bit rate                                         */
        Word16 _index[]                       /* (o) : index (20): 5+5+5+5 = 20 bits.                   */
        /* (o) : index (36): 9+9+9+9 = 36 bits.                   */
        /* (o) : index (44): 13+9+13+9 = 44 bits.                 */
        /* (o) : index (52): 13+13+13+13 = 52 bits.               */
        /* (o) : index (64): 2+2+2+2+14+14+14+14 = 64 bits.       */
        /* (o) : index (72): 10+2+10+2+10+14+10+14 = 72 bits.     */
        /* (o) : index (88): 11+11+11+11+11+11+11+11 = 88 bits.   */
        )
{
    Word32 i, j, k;
    Word16 st, ix, iy, pos, index, track, nb_pulse, nbiter, j_temp;
    Word16 psk, ps, alpk, alp, val, k_cn, k_dn, exp;
    Word16 *p0, *p1, *p2, *p3, *psign;
    Word16 *h, *h_inv, *ptr_h1, *ptr_h2, *ptr_hf, h_shift;
    Word32 s, cor, L_tmp, L_index;
    Word16 dn2[L_SUBFR], sign[L_SUBFR], vec[L_SUBFR];
    Word16 ind[NPMAXPT * NB_TRACK];
    Word16 codvec[NB_PULSE_MAX], nbpos[10];
    Word16 cor_x[NB_POS], cor_y[NB_POS], pos_max[NB_TRACK];
    Word16 h_buf[4 * L_SUBFR];
    Word16 rrixix[NB_TRACK][NB_POS], rrixiy[NB_TRACK][MSIZE];
    Word16 ipos[NB_PULSE_MAX];

    switch (nbbits)
    {
        case 20:                               /* 20 bits, 4 pulses, 4 tracks */
            nbiter = 4;                          /* 4x16x16=1024 loop */
            alp = 8192;                          /* alp = 2.0 (Q12) */
            nb_pulse = 4;
            nbpos[0] = 4;
            nbpos[1] = 8;
            break;
        case 36:                               /* 36 bits, 8 pulses, 4 tracks */
            nbiter = 4;                          /* 4x20x16=1280 loop */
            alp = 4096;                          /* alp = 1.0 (Q12) */
            nb_pulse = 8;
            nbpos[0] = 4;
            nbpos[1] = 8;
            nbpos[2] = 8;
            break;
        case 44:                               /* 44 bits, 10 pulses, 4 tracks */
            nbiter = 4;                          /* 4x26x16=1664 loop */
            alp = 4096;                          /* alp = 1.0 (Q12) */
            nb_pulse = 10;
            nbpos[0] = 4;
            nbpos[1] = 6;
            nbpos[2] = 8;
            nbpos[3] = 8;
            break;
        case 52:                               /* 52 bits, 12 pulses, 4 tracks */
            nbiter = 4;                          /* 4x26x16=1664 loop */
            alp = 4096;                          /* alp = 1.0 (Q12) */
            nb_pulse = 12;
            nbpos[0] = 4;
            nbpos[1] = 6;
            nbpos[2] = 8;
            nbpos[3] = 8;
            break;
        case 64:                               /* 64 bits, 16 pulses, 4 tracks */
            nbiter = 3;                          /* 3x36x16=1728 loop */
            alp = 3277;                          /* alp = 0.8 (Q12) */
            nb_pulse = 16;
            nbpos[0] = 4;
            nbpos[1] = 4;
            nbpos[2] = 6;
            nbpos[3] = 6;
            nbpos[4] = 8;
            nbpos[5] = 8;
            break;
        case 72:                               /* 72 bits, 18 pulses, 4 tracks */
            nbiter = 3;                          /* 3x35x16=1680 loop */
            alp = 3072;                          /* alp = 0.75 (Q12) */
            nb_pulse = 18;
            nbpos[0] = 2;
            nbpos[1] = 3;
            nbpos[2] = 4;
            nbpos[3] = 5;
            nbpos[4] = 6;
            nbpos[5] = 7;
            nbpos[6] = 8;
            break;
        case 88:                               /* 88 bits, 24 pulses, 4 tracks */
            if(ser_size > 462)
                nbiter = 1;
            else
                nbiter = 2;                    /* 2x53x16=1696 loop */

            alp = 2048;                          /* alp = 0.5 (Q12) */
            nb_pulse = 24;
            nbpos[0] = 2;
            nbpos[1] = 2;
            nbpos[2] = 3;
            nbpos[3] = 4;
            nbpos[4] = 5;
            nbpos[5] = 6;
            nbpos[6] = 7;
            nbpos[7] = 8;
            nbpos[8] = 8;
            nbpos[9] = 8;
            break;
        default:
            nbiter = 0;
            alp = 0;
            nb_pulse = 0;
    }

    for (i = 0; i < nb_pulse; i++)
    {
        codvec[i] = i;
    }

    /*----------------------------------------------------------------*
     * Find sign for each pulse position.                             *
     *----------------------------------------------------------------*/
    /* calculate energy for normalization of cn[] and dn[] */
    /* set k_cn = 32..32767 (ener_cn = 2^30..256-0) */
#ifdef ASM_OPT                  /* asm optimization branch */
    s = Dot_product12_asm(cn, cn, L_SUBFR, &exp);
#else
    s = Dot_product12(cn, cn, L_SUBFR, &exp);
#endif

    Isqrt_n(&s, &exp);
    s = L_shl(s, (exp + 5));
    k_cn = extract_h(L_add(s, 0x8000));

    /* set k_dn = 32..512 (ener_dn = 2^30..2^22) */
#ifdef ASM_OPT                      /* asm optimization branch */
    s = Dot_product12_asm(dn, dn, L_SUBFR, &exp);
#else
    s = Dot_product12(dn, dn, L_SUBFR, &exp);
#endif

    Isqrt_n(&s, &exp);
    k_dn = voround(L_shl(s, (exp + 5 + 3)));    /* k_dn = 256..4096 */
    k_dn = vo_mult_r(alp, k_dn);              /* alp in Q12 */

    /* mix normalized cn[] and dn[] */
    p0 = cn;
    p1 = dn;
    p2 = dn2;

    for (i = 0; i < L_SUBFR/4; i++)
    {
        s = L_add((k_cn* (*p0++)), (k_dn * (*p1++)));
        *p2++ = s >> 7;
        s = L_add((k_cn* (*p0++)), (k_dn * (*p1++)));
        *p2++ = s >> 7;
        s = L_add((k_cn* (*p0++)), (k_dn * (*p1++)));
        *p2++ = s >> 7;
        s = L_add((k_cn* (*p0++)), (k_dn * (*p1++)));
        *p2++ = s >> 7;
    }

    /* set sign according to dn2[] = k_cn*cn[] + k_dn*dn[]    */
    for(i = 0; i < L_SUBFR; i++)
    {
        val = dn[i];
        ps = dn2[i];
        if (ps >= 0)
        {
            sign[i] = 32767;             /* sign = +1 (Q12) */
            vec[i] = -32768;
        } else
        {
            sign[i] = -32768;            /* sign = -1 (Q12) */
            vec[i] = 32767;
            dn[i] = -val;
            dn2[i] = -ps;
        }
    }
    /*----------------------------------------------------------------*
     * Select NB_MAX position per track according to max of dn2[].    *
     *----------------------------------------------------------------*/
    pos = 0;
    for (i = 0; i < NB_TRACK; i++)
    {
        for (k = 0; k < NB_MAX; k++)
        {
            ps = -1;
            for (j = i; j < L_SUBFR; j += STEP)
            {
                if(dn2[j] > ps)
                {
                    ps = dn2[j];
                    pos = j;
                }
            }
            dn2[pos] = (k - NB_MAX);     /* dn2 < 0 when position is selected */
            if (k == 0)
            {
                pos_max[i] = pos;
            }
        }
    }

    /*--------------------------------------------------------------*
     * Scale h[] to avoid overflow and to get maximum of precision  *
     * on correlation.                                              *
     *                                                              *
     * Maximum of h[] (h[0]) is fixed to 2048 (MAX16 / 16).         *
     *  ==> This allow addition of 16 pulses without saturation.    *
     *                                                              *
     * Energy worst case (on resonant impulse response),            *
     * - energy of h[] is approximately MAX/16.                     *
     * - During search, the energy is divided by 8 to avoid         *
     *   overflow on "alp". (energy of h[] = MAX/128).              *
     *  ==> "alp" worst case detected is 22854 on sinusoidal wave.  *
     *--------------------------------------------------------------*/

    /* impulse response buffer for fast computation */

    h = h_buf;
    h_inv = h_buf + (2 * L_SUBFR);
    L_tmp = 0;
    for (i = 0; i < L_SUBFR; i++)
    {
        *h++ = 0;
        *h_inv++ = 0;
        L_tmp = L_add(L_tmp, (H[i] * H[i]) << 1);
    }
    /* scale h[] down (/2) when energy of h[] is high with many pulses used */
    val = extract_h(L_tmp);
    h_shift = 0;

    if ((nb_pulse >= 12) && (val > 1024))
    {
        h_shift = 1;
    }
    p0 = H;
    p1 = h;
    p2 = h_inv;

    for (i = 0; i < L_SUBFR/4; i++)
    {
        *p1 = *p0++ >> h_shift;
        *p2++ = -(*p1++);
        *p1 = *p0++ >> h_shift;
        *p2++ = -(*p1++);
        *p1 = *p0++ >> h_shift;
        *p2++ = -(*p1++);
        *p1 = *p0++ >> h_shift;
        *p2++ = -(*p1++);
    }

    /*------------------------------------------------------------*
     * Compute rrixix[][] needed for the codebook search.         *
     * This algorithm compute impulse response energy of all      *
     * positions (16) in each track (4).       Total = 4x16 = 64. *
     *------------------------------------------------------------*/

    /* storage order --> i3i3, i2i2, i1i1, i0i0 */

    /* Init pointers to last position of rrixix[] */
    p0 = &rrixix[0][NB_POS - 1];
    p1 = &rrixix[1][NB_POS - 1];
    p2 = &rrixix[2][NB_POS - 1];
    p3 = &rrixix[3][NB_POS - 1];

    ptr_h1 = h;
    cor = 0x00008000L;                             /* for rounding */
    for (i = 0; i < NB_POS; i++)
    {
        cor = L_add(cor, vo_L_mult((*ptr_h1), (*ptr_h1)));
        ptr_h1++;
        *p3-- = extract_h(cor);
        cor = L_add(cor, vo_L_mult((*ptr_h1), (*ptr_h1)));
        ptr_h1++;
        *p2-- = extract_h(cor);
        cor = L_add(cor, vo_L_mult((*ptr_h1), (*ptr_h1)));
        ptr_h1++;
        *p1-- = extract_h(cor);
        cor = L_add(cor, vo_L_mult((*ptr_h1), (*ptr_h1)));
        ptr_h1++;
        *p0-- = extract_h(cor);
    }

    /*------------------------------------------------------------*
     * Compute rrixiy[][] needed for the codebook search.         *
     * This algorithm compute correlation between 2 pulses        *
     * (2 impulses responses) in 4 possible adjacents tracks.     *
     * (track 0-1, 1-2, 2-3 and 3-0).     Total = 4x16x16 = 1024. *
     *------------------------------------------------------------*/

    /* storage order --> i2i3, i1i2, i0i1, i3i0 */

    pos = MSIZE - 1;
    ptr_hf = h + 1;

    for (k = 0; k < NB_POS; k++)
    {
        p3 = &rrixiy[2][pos];
        p2 = &rrixiy[1][pos];
        p1 = &rrixiy[0][pos];
        p0 = &rrixiy[3][pos - NB_POS];

        cor = 0x00008000L;                   /* for rounding */
        ptr_h1 = h;
        ptr_h2 = ptr_hf;

        for (i = k + 1; i < NB_POS; i++)
        {
            cor = L_add(cor, vo_L_mult((*ptr_h1), (*ptr_h2)));
            ptr_h1++;
            ptr_h2++;
            *p3 = extract_h(cor);
            cor = L_add(cor, vo_L_mult((*ptr_h1), (*ptr_h2)));
            ptr_h1++;
            ptr_h2++;
            *p2 = extract_h(cor);
            cor = L_add(cor, vo_L_mult((*ptr_h1), (*ptr_h2)));
            ptr_h1++;
            ptr_h2++;
            *p1 = extract_h(cor);
            cor = L_add(cor, vo_L_mult((*ptr_h1), (*ptr_h2)));
            ptr_h1++;
            ptr_h2++;
            *p0 = extract_h(cor);

            p3 -= (NB_POS + 1);
            p2 -= (NB_POS + 1);
            p1 -= (NB_POS + 1);
            p0 -= (NB_POS + 1);
        }
        cor = L_add(cor, vo_L_mult((*ptr_h1), (*ptr_h2)));
        ptr_h1++;
        ptr_h2++;
        *p3 = extract_h(cor);
        cor = L_add(cor, vo_L_mult((*ptr_h1), (*ptr_h2)));
        ptr_h1++;
        ptr_h2++;
        *p2 = extract_h(cor);
        cor = L_add(cor, vo_L_mult((*ptr_h1), (*ptr_h2)));
        ptr_h1++;
        ptr_h2++;
        *p1 = extract_h(cor);

        pos -= NB_POS;
        ptr_hf += STEP;
    }

    /* storage order --> i3i0, i2i3, i1i2, i0i1 */

    pos = MSIZE - 1;
    ptr_hf = h + 3;

    for (k = 0; k < NB_POS; k++)
    {
        p3 = &rrixiy[3][pos];
        p2 = &rrixiy[2][pos - 1];
        p1 = &rrixiy[1][pos - 1];
        p0 = &rrixiy[0][pos - 1];

        cor = 0x00008000L;                              /* for rounding */
        ptr_h1 = h;
        ptr_h2 = ptr_hf;

        for (i = k + 1; i < NB_POS; i++)
        {
            cor = L_add(cor, vo_L_mult((*ptr_h1), (*ptr_h2)));
            ptr_h1++;
            ptr_h2++;
            *p3 = extract_h(cor);
            cor = L_add(cor, vo_L_mult((*ptr_h1), (*ptr_h2)));
            ptr_h1++;
            ptr_h2++;
            *p2 = extract_h(cor);
            cor = L_add(cor, vo_L_mult((*ptr_h1), (*ptr_h2)));
            ptr_h1++;
            ptr_h2++;
            *p1 = extract_h(cor);
            cor = L_add(cor, vo_L_mult((*ptr_h1), (*ptr_h2)));
            ptr_h1++;
            ptr_h2++;
            *p0 = extract_h(cor);

            p3 -= (NB_POS + 1);
            p2 -= (NB_POS + 1);
            p1 -= (NB_POS + 1);
            p0 -= (NB_POS + 1);
        }
        cor = L_add(cor, vo_L_mult((*ptr_h1), (*ptr_h2)));
        ptr_h1++;
        ptr_h2++;
        *p3 = extract_h(cor);

        pos--;
        ptr_hf += STEP;
    }

    /*------------------------------------------------------------*
     * Modification of rrixiy[][] to take signs into account.     *
     *------------------------------------------------------------*/

    p0 = &rrixiy[0][0];

    for (k = 0; k < NB_TRACK; k++)
    {
        j_temp = (k + 1)&0x03;
        for (i = k; i < L_SUBFR; i += STEP)
        {
            psign = sign;
            if (psign[i] < 0)
            {
                psign = vec;
            }
            j = j_temp;
            for (; j < L_SUBFR; j += STEP)
            {
                *p0 = vo_mult(*p0, psign[j]);
                p0++;
            }
        }
    }

    /*-------------------------------------------------------------------*
     *                       Deep first search                           *
     *-------------------------------------------------------------------*/

    psk = -1;
    alpk = 1;

    for (k = 0; k < nbiter; k++)
    {
        j_temp = k<<2;
        for (i = 0; i < nb_pulse; i++)
            ipos[i] = tipos[j_temp + i];

        if(nbbits == 20)
        {
            pos = 0;
            ps = 0;
            alp = 0;
            for (i = 0; i < L_SUBFR; i++)
            {
                vec[i] = 0;
            }
        } else if ((nbbits == 36) || (nbbits == 44))
        {
            /* first stage: fix 2 pulses */
            pos = 2;

            ix = ind[0] = pos_max[ipos[0]];
            iy = ind[1] = pos_max[ipos[1]];
            ps = dn[ix] + dn[iy];
            i = ix >> 2;                /* ix / STEP */
            j = iy >> 2;                /* iy / STEP */
            s = rrixix[ipos[0]][i] << 13;
            s += rrixix[ipos[1]][j] << 13;
            i = (i << 4) + j;         /* (ix/STEP)*NB_POS + (iy/STEP) */
            s += rrixiy[ipos[0]][i] << 14;
            alp = (s + 0x8000) >> 16;
            if (sign[ix] < 0)
                p0 = h_inv - ix;
            else
                p0 = h - ix;
            if (sign[iy] < 0)
                p1 = h_inv - iy;
            else
                p1 = h - iy;

            for (i = 0; i < L_SUBFR; i++)
            {
                vec[i] = (*p0++) + (*p1++);
            }

            if(nbbits == 44)
            {
                ipos[8] = 0;
                ipos[9] = 1;
            }
        } else
        {
            /* first stage: fix 4 pulses */
            pos = 4;

            ix = ind[0] = pos_max[ipos[0]];
            iy = ind[1] = pos_max[ipos[1]];
            i = ind[2] = pos_max[ipos[2]];
            j = ind[3] = pos_max[ipos[3]];
            ps = add1(add1(add1(dn[ix], dn[iy]), dn[i]), dn[j]);

            if (sign[ix] < 0)
                p0 = h_inv - ix;
            else
                p0 = h - ix;

            if (sign[iy] < 0)
                p1 = h_inv - iy;
            else
                p1 = h - iy;

            if (sign[i] < 0)
                p2 = h_inv - i;
            else
                p2 = h - i;

            if (sign[j] < 0)
                p3 = h_inv - j;
            else
                p3 = h - j;

            L_tmp = 0L;
            for(i = 0; i < L_SUBFR; i++)
            {
                Word32 vecSq2;
                vec[i]  = add1(add1(add1(*p0++, *p1++), *p2++), *p3++);
                vecSq2 = (vec[i] * vec[i]) << 1;
                if (vecSq2 > 0 && L_tmp > INT_MAX - vecSq2) {
                    L_tmp = INT_MAX;
                } else if (vecSq2 < 0 && L_tmp < INT_MIN - vecSq2) {
                    L_tmp = INT_MIN;
                } else {
                    L_tmp  += vecSq2;
                }
            }

            alp = ((L_tmp >> 3) + 0x8000) >> 16;

            if(nbbits == 72)
            {
                ipos[16] = 0;
                ipos[17] = 1;
            }
        }

        /* other stages of 2 pulses */

        for (j = pos, st = 0; j < nb_pulse; j += 2, st++)
        {
            /*--------------------------------------------------*
             * Calculate correlation of all possible positions  *
             * of the next 2 pulses with previous fixed pulses. *
             * Each pulse can have 16 possible positions.       *
             *--------------------------------------------------*/
            if(ipos[j] == 3)
            {
                cor_h_vec_30(h, vec, ipos[j], sign, rrixix, cor_x, cor_y);
            }
            else
            {
#ifdef ASM_OPT                 /* asm optimization branch */
                cor_h_vec_012_asm(h, vec, ipos[j], sign, rrixix, cor_x, cor_y);
#else
                cor_h_vec_012(h, vec, ipos[j], sign, rrixix, cor_x, cor_y);
#endif
            }
            /*--------------------------------------------------*
             * Find best positions of 2 pulses.                 *
             *--------------------------------------------------*/
            search_ixiy(nbpos[st], ipos[j], ipos[j + 1], &ps, &alp,
                    &ix, &iy, dn, dn2, cor_x, cor_y, rrixiy);

            ind[j] = ix;
            ind[j + 1] = iy;

            if (sign[ix] < 0)
                p0 = h_inv - ix;
            else
                p0 = h - ix;
            if (sign[iy] < 0)
                p1 = h_inv - iy;
            else
                p1 = h - iy;

            for (i = 0; i < L_SUBFR; i+=4)
            {
                vec[i]   += add1((*p0++), (*p1++));
                vec[i+1] += add1((*p0++), (*p1++));
                vec[i+2] += add1((*p0++), (*p1++));
                vec[i+3] += add1((*p0++), (*p1++));
            }
        }
        /* memorise the best codevector */
        ps = vo_mult(ps, ps);
        s = L_sub(vo_L_mult(alpk, ps), vo_L_mult(psk, alp));
        if (s > 0)
        {
            psk = ps;
            alpk = alp;
            for (i = 0; i < nb_pulse; i++)
            {
                codvec[i] = ind[i];
            }
            for (i = 0; i < L_SUBFR; i++)
            {
                y[i] = vec[i];
            }
        }
    }
    /*-------------------------------------------------------------------*
     * Build the codeword, the filtered codeword and index of codevector.*
     *-------------------------------------------------------------------*/
    for (i = 0; i < NPMAXPT * NB_TRACK; i++)
    {
        ind[i] = -1;
    }
    for (i = 0; i < L_SUBFR; i++)
    {
        code[i] = 0;
        y[i] = vo_shr_r(y[i], 3);               /* Q12 to Q9 */
    }
    val = (512 >> h_shift);               /* codeword in Q9 format */
    for (k = 0; k < nb_pulse; k++)
    {
        i = codvec[k];                       /* read pulse position */
        j = sign[i];                         /* read sign           */
        index = i >> 2;                 /* index = pos of pulse (0..15) */
        track = (Word16) (i & 0x03);         /* track = i % NB_TRACK (0..3)  */

        if (j > 0)
        {
            code[i] += val;
            codvec[k] += 128;
        } else
        {
            code[i] -= val;
            index += NB_POS;
        }

        i = (Word16)((vo_L_mult(track, NPMAXPT) >> 1));

        while (i < NPMAXPT * NB_TRACK && ind[i] >= 0)
        {
            i += 1;
        }
        if (i < NPMAXPT * NB_TRACK) {
            ind[i] = index;
        } else {
            ALOGE("b/132647222, OOB access in ind array track=%d i=%d", track, i);
            android_errorWriteLog(0x534e4554, "132647222");
        }
    }

    k = 0;
    /* Build index of codevector */
    if(nbbits == 20)
    {
        for (track = 0; track < NB_TRACK; track++)
        {
            _index[track] = (Word16)(quant_1p_N1(ind[k], 4));
            k += NPMAXPT;
        }
    } else if(nbbits == 36)
    {
        for (track = 0; track < NB_TRACK; track++)
        {
            _index[track] = (Word16)(quant_2p_2N1(ind[k], ind[k + 1], 4));
            k += NPMAXPT;
        }
    } else if(nbbits == 44)
    {
        for (track = 0; track < NB_TRACK - 2; track++)
        {
            _index[track] = (Word16)(quant_3p_3N1(ind[k], ind[k + 1], ind[k + 2], 4));
            k += NPMAXPT;
        }
        for (track = 2; track < NB_TRACK; track++)
        {
            _index[track] = (Word16)(quant_2p_2N1(ind[k], ind[k + 1], 4));
            k += NPMAXPT;
        }
    } else if(nbbits == 52)
    {
        for (track = 0; track < NB_TRACK; track++)
        {
            _index[track] = (Word16)(quant_3p_3N1(ind[k], ind[k + 1], ind[k + 2], 4));
            k += NPMAXPT;
        }
    } else if(nbbits == 64)
    {
        for (track = 0; track < NB_TRACK; track++)
        {
            L_index = quant_4p_4N(&ind[k], 4);
            _index[track] = (Word16)((L_index >> 14) & 3);
            _index[track + NB_TRACK] = (Word16)(L_index & 0x3FFF);
            k += NPMAXPT;
        }
    } else if(nbbits == 72)
    {
        for (track = 0; track < NB_TRACK - 2; track++)
        {
            L_index = quant_5p_5N(&ind[k], 4);
            _index[track] = (Word16)((L_index >> 10) & 0x03FF);
            _index[track + NB_TRACK] = (Word16)(L_index & 0x03FF);
            k += NPMAXPT;
        }
        for (track = 2; track < NB_TRACK; track++)
        {
            L_index = quant_4p_4N(&ind[k], 4);
            _index[track] = (Word16)((L_index >> 14) & 3);
            _index[track + NB_TRACK] = (Word16)(L_index & 0x3FFF);
            k += NPMAXPT;
        }
    } else if(nbbits == 88)
    {
        for (track = 0; track < NB_TRACK; track++)
        {
            L_index = quant_6p_6N_2(&ind[k], 4);
            _index[track] = (Word16)((L_index >> 11) & 0x07FF);
            _index[track + NB_TRACK] = (Word16)(L_index & 0x07FF);
            k += NPMAXPT;
        }
    }
    return;
}


/*-------------------------------------------------------------------*
 * Function  cor_h_vec()                                             *
 * ~~~~~~~~~~~~~~~~~~~~~                                             *
 * Compute correlations of h[] with vec[] for the specified track.   *
 *-------------------------------------------------------------------*/
void cor_h_vec_30(
        Word16 h[],                           /* (i) scaled impulse response                 */
        Word16 vec[],                         /* (i) scaled vector (/8) to correlate with h[] */
        Word16 track,                         /* (i) track to use                            */
        Word16 sign[],                        /* (i) sign vector                             */
        Word16 rrixix[][NB_POS],              /* (i) correlation of h[x] with h[x]      */
        Word16 cor_1[],                       /* (o) result of correlation (NB_POS elements) */
        Word16 cor_2[]                        /* (o) result of correlation (NB_POS elements) */
        )
{
    Word32 i, j, pos, corr;
    Word16 *p0, *p1, *p2,*p3,*cor_x,*cor_y;
    Word32 L_sum1,L_sum2;
    cor_x = cor_1;
    cor_y = cor_2;
    p0 = rrixix[track];
    p3 = rrixix[0];
    pos = track;

    for (i = 0; i < NB_POS; i+=2)
    {
        L_sum1 = L_sum2 = 0L;
        p1 = h;
        p2 = &vec[pos];
        for (j=pos;j < L_SUBFR; j++)
        {
            L_sum1 = L_add(L_sum1, *p1 * *p2);
            p2-=3;
            L_sum2 = L_add(L_sum2, *p1++ * *p2);
            p2+=4;
        }
        p2-=3;
        L_sum2 = L_add(L_sum2, *p1++ * *p2++);
        L_sum2 = L_add(L_sum2, *p1++ * *p2++);
        L_sum2 = L_add(L_sum2, *p1++ * *p2++);

        L_sum1 = L_shl(L_sum1, 2);
        L_sum2 = L_shl(L_sum2, 2);

        corr = voround(L_sum1);
        *cor_x++ = mult(corr, sign[pos]) + (*p0++);
        corr = voround(L_sum2);
        *cor_y++ = mult(corr, sign[pos-3]) + (*p3++);
        pos += STEP;

        L_sum1 = L_sum2 = 0L;
        p1 = h;
        p2 = &vec[pos];
        for (j=pos;j < L_SUBFR; j++)
        {
            L_sum1 = L_add(L_sum1, *p1 * *p2);
            p2-=3;
            L_sum2 = L_add(L_sum2, *p1++ * *p2);
            p2+=4;
        }
        p2-=3;
        L_sum2 = L_add(L_sum2, *p1++ * *p2++);
        L_sum2 = L_add(L_sum2, *p1++ * *p2++);
        L_sum2 = L_add(L_sum2, *p1++ * *p2++);

        L_sum1 = L_shl(L_sum1, 2);
        L_sum2 = L_shl(L_sum2, 2);

        corr = voround(L_sum1);
        *cor_x++ = mult(corr, sign[pos]) + (*p0++);
        corr = voround(L_sum2);
        *cor_y++ = mult(corr, sign[pos-3]) + (*p3++);
        pos += STEP;
    }
    return;
}

void cor_h_vec_012(
        Word16 h[],                           /* (i) scaled impulse response                 */
        Word16 vec[],                         /* (i) scaled vector (/8) to correlate with h[] */
        Word16 track,                         /* (i) track to use                            */
        Word16 sign[],                        /* (i) sign vector                             */
        Word16 rrixix[][NB_POS],              /* (i) correlation of h[x] with h[x]      */
        Word16 cor_1[],                       /* (o) result of correlation (NB_POS elements) */
        Word16 cor_2[]                        /* (o) result of correlation (NB_POS elements) */
        )
{
    Word32 i, j, pos, corr;
    Word16 *p0, *p1, *p2,*p3,*cor_x,*cor_y;
    Word32 L_sum1,L_sum2;
    cor_x = cor_1;
    cor_y = cor_2;
    p0 = rrixix[track];
    p3 = rrixix[track+1];
    pos = track;

    for (i = 0; i < NB_POS; i+=2)
    {
        L_sum1 = L_sum2 = 0L;
        p1 = h;
        p2 = &vec[pos];
        for (j=62-pos ;j >= 0; j--)
        {
            L_sum1 = L_add(L_sum1, *p1 * *p2++);
            L_sum2 = L_add(L_sum2, *p1++ * *p2);
        }
        L_sum1 = L_add(L_sum1, *p1 * *p2);
        L_sum1 = L_shl(L_sum1, 2);
        L_sum2 = L_shl(L_sum2, 2);

        corr = voround(L_sum1);
        cor_x[i] = vo_mult(corr, sign[pos]) + (*p0++);
        corr = voround(L_sum2);
        cor_y[i] = vo_mult(corr, sign[pos + 1]) + (*p3++);
        pos += STEP;

        L_sum1 = L_sum2 = 0L;
        p1 = h;
        p2 = &vec[pos];
        for (j= 62-pos;j >= 0; j--)
        {
            L_sum1 = L_add(L_sum1, *p1 * *p2++);
            L_sum2 = L_add(L_sum2, *p1++ * *p2);
        }
        L_sum1 = L_add(L_sum1, *p1 * *p2);
        L_sum1 = L_shl(L_sum1, 2);
        L_sum2 = L_shl(L_sum2, 2);

        corr = voround(L_sum1);
        cor_x[i+1] = vo_mult(corr, sign[pos]) + (*p0++);
        corr = voround(L_sum2);
        cor_y[i+1] = vo_mult(corr, sign[pos + 1]) + (*p3++);
        pos += STEP;
    }
    return;
}

/*-------------------------------------------------------------------*
 * Function  search_ixiy()                                           *
 * ~~~~~~~~~~~~~~~~~~~~~~~                                           *
 * Find the best positions of 2 pulses in a subframe.                *
 *-------------------------------------------------------------------*/

void search_ixiy(
        Word16 nb_pos_ix,                     /* (i) nb of pos for pulse 1 (1..8)       */
        Word16 track_x,                       /* (i) track of pulse 1                   */
        Word16 track_y,                       /* (i) track of pulse 2                   */
        Word16 * ps,                          /* (i/o) correlation of all fixed pulses  */
        Word16 * alp,                         /* (i/o) energy of all fixed pulses       */
        Word16 * ix,                          /* (o) position of pulse 1                */
        Word16 * iy,                          /* (o) position of pulse 2                */
        Word16 dn[],                          /* (i) corr. between target and h[]       */
        Word16 dn2[],                         /* (i) vector of selected positions       */
        Word16 cor_x[],                       /* (i) corr. of pulse 1 with fixed pulses */
        Word16 cor_y[],                       /* (i) corr. of pulse 2 with fixed pulses */
        Word16 rrixiy[][MSIZE]                /* (i) corr. of pulse 1 with pulse 2   */
        )
{
    Word32 x, y, pos, thres_ix;
    Word16 ps1, ps2, sq, sqk;
    Word16 alp_16, alpk;
    Word16 *p0, *p1, *p2;
    Word32 s, alp0, alp1, alp2;

    p0 = cor_x;
    p1 = cor_y;
    p2 = rrixiy[track_x];

    thres_ix = nb_pos_ix - NB_MAX;

    alp0 = L_deposit_h(*alp);
    alp0 = (alp0 + 0x00008000L);       /* for rounding */

    sqk = -1;
    alpk = 1;

    for (x = track_x; x < L_SUBFR; x += STEP)
    {
        ps1 = *ps + dn[x];
        alp1 = L_add(alp0, ((*p0++)<<13));

        if (dn2[x] < thres_ix)
        {
            pos = -1;
            for (y = track_y; y < L_SUBFR; y += STEP)
            {
                ps2 = add1(ps1, dn[y]);

                alp2 = L_add(alp1, ((*p1++)<<13));
                alp2 = L_add(alp2, ((*p2++)<<14));
                alp_16 = extract_h(alp2);
                sq = vo_mult(ps2, ps2);
                s = L_sub(vo_L_mult(alpk, sq), L_mult(sqk, alp_16));

                if (s > 0)
                {
                    sqk = sq;
                    alpk = alp_16;
                    pos = y;
                }
            }
            p1 -= NB_POS;

            if (pos >= 0)
            {
                *ix = x;
                *iy = pos;
            }
        } else
        {
            p2 += NB_POS;
        }
    }

    *ps = add1(*ps, add1(dn[*ix], dn[*iy]));
    *alp = alpk;

    return;
}




