/*
 *  Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

/*
 * entropy_coding.c
 *
 * This file contains all functions used to arithmetically
 * encode the iSAC bistream.
 *
 */

#include <stddef.h>

#include "arith_routins.h"
#include "spectrum_ar_model_tables.h"
#include "pitch_gain_tables.h"
#include "pitch_lag_tables.h"
#include "entropy_coding.h"
#include "lpc_tables.h"
#include "settings.h"
#include "signal_processing_library.h"

/*
 * Eenumerations for arguments to functions WebRtcIsacfix_MatrixProduct1()
 * and WebRtcIsacfix_MatrixProduct2().
*/

enum matrix_index_factor {
  kTIndexFactor1 = 1,
  kTIndexFactor2 = 2,
  kTIndexFactor3 = SUBFRAMES,
  kTIndexFactor4 = LPC_SHAPE_ORDER
};

enum matrix_index_step {
  kTIndexStep1 = 1,
  kTIndexStep2 = SUBFRAMES,
  kTIndexStep3 = LPC_SHAPE_ORDER
};

enum matrixprod_loop_count {
  kTLoopCount1 = SUBFRAMES,
  kTLoopCount2 = 2,
  kTLoopCount3 = LPC_SHAPE_ORDER
};

enum matrix1_shift_value {
  kTMatrix1_shift0 = 0,
  kTMatrix1_shift1 = 1,
  kTMatrix1_shift5 = 5
};

enum matrixprod_init_case {
  kTInitCase0 = 0,
  kTInitCase1 = 1
};

/*
  This function implements the fix-point correspondant function to lrint.

  FLP: (int32_t)floor(flt+.499999999999)
  FIP: (fixVal+roundVal)>>qDomain

  where roundVal = 2^(qDomain-1) = 1<<(qDomain-1)

*/
static __inline int32_t CalcLrIntQ(int32_t fixVal, int16_t qDomain) {
  return (fixVal + (1 << (qDomain - 1))) >> qDomain;
}

/*
  __inline uint32_t stepwise(int32_t dinQ10) {

  int32_t ind, diQ10, dtQ10;

  diQ10 = dinQ10;
  if (diQ10 < DPMIN_Q10)
  diQ10 = DPMIN_Q10;
  if (diQ10 >= DPMAX_Q10)
  diQ10 = DPMAX_Q10 - 1;

  dtQ10 = diQ10 - DPMIN_Q10;*/ /* Q10 + Q10 = Q10 */
/* ind = (dtQ10 * 5) >> 10;  */ /* 2^10 / 5 = 0.2 in Q10  */
/* Q10 -> Q0 */

/* return rpointsFIX_Q10[ind];

   }
*/

/* logN(x) = logN(2)*log2(x) = 0.6931*log2(x). Output in Q8. */
/* The input argument X to logN(X) is 2^17 times higher than the
   input floating point argument Y to log(Y), since the X value
   is a Q17 value. This can be compensated for after the call, by
   subraction a value Z for each Q-step. One Q-step means that
   X gets 2 thimes higher, i.e. Z = logN(2)*256 = 0.693147180559*256 =
   177.445678 should be subtracted (since logN() returns a Q8 value).
   For a X value in Q17, the value 177.445678*17 = 3017 should be
   subtracted */
static int16_t CalcLogN(int32_t arg) {
  int16_t zeros, log2, frac, logN;

  zeros=WebRtcSpl_NormU32(arg);
  frac=(int16_t)WEBRTC_SPL_RSHIFT_U32(WEBRTC_SPL_LSHIFT_W32(arg, zeros)&0x7FFFFFFF, 23);
  log2=(int16_t)(WEBRTC_SPL_LSHIFT_W32(31-zeros, 8)+frac); // log2(x) in Q8
  logN=(int16_t)WEBRTC_SPL_MUL_16_16_RSFT(log2,22713,15); //Q8*Q15 log(2) = 0.693147 = 22713 in Q15
  logN=logN+11; //Scalar compensation which minimizes the (log(x)-logN(x))^2 error over all x.

  return logN;
}


/*
  expN(x) = 2^(a*x), where a = log2(e) ~= 1.442695

  Input:  Q8  (int16_t)
  Output: Q17 (int32_t)

  a = log2(e) = log2(exp(1)) ~= 1.442695  ==>  a = 23637 in Q14 (1.442688)
  To this value, 700 is added or subtracted in order to get an average error
  nearer zero, instead of always same-sign.
*/

static int32_t CalcExpN(int16_t x) {
  int16_t ax, axINT, axFRAC;
  int16_t exp16;
  int32_t exp;

  if (x>=0) {
    //  ax=(int16_t)WEBRTC_SPL_MUL_16_16_RSFT(x, 23637-700, 14); //Q8
    ax=(int16_t)WEBRTC_SPL_MUL_16_16_RSFT(x, 23637, 14); //Q8
    axINT = ax >> 8;  //Q0
    axFRAC = ax&0x00FF;
    exp16 = WEBRTC_SPL_LSHIFT_W32(1, axINT); //Q0
    axFRAC = axFRAC+256; //Q8
    exp = WEBRTC_SPL_MUL_16_16(exp16, axFRAC); // Q0*Q8 = Q8
    exp = WEBRTC_SPL_LSHIFT_W32(exp, 9); //Q17
  } else {
    //  ax=(int16_t)WEBRTC_SPL_MUL_16_16_RSFT(x, 23637+700, 14); //Q8
    ax=(int16_t)WEBRTC_SPL_MUL_16_16_RSFT(x, 23637, 14); //Q8
    ax = -ax;
    axINT = 1 + (ax >> 8);  //Q0
    axFRAC = 0x00FF - (ax&0x00FF);
    exp16 = (int16_t)(32768 >> axINT);  // Q15
    axFRAC = axFRAC+256; //Q8
    exp = WEBRTC_SPL_MUL_16_16(exp16, axFRAC); // Q15*Q8 = Q23
    exp >>= 6;  // Q17
  }

  return exp;
}


/* compute correlation from power spectrum */
static void CalcCorrelation(int32_t *PSpecQ12, int32_t *CorrQ7)
{
  int32_t summ[FRAMESAMPLES/8];
  int32_t diff[FRAMESAMPLES/8];
  int32_t sum;
  int k, n;

  for (k = 0; k < FRAMESAMPLES/8; k++) {
    summ[k] = (PSpecQ12[k] + PSpecQ12[FRAMESAMPLES / 4 - 1 - k] + 16) >> 5;
    diff[k] = (PSpecQ12[k] - PSpecQ12[FRAMESAMPLES / 4 - 1 - k] + 16) >> 5;
  }

  sum = 2;
  for (n = 0; n < FRAMESAMPLES/8; n++)
    sum += summ[n];
  CorrQ7[0] = sum;

  for (k = 0; k < AR_ORDER; k += 2) {
    sum = 0;
    for (n = 0; n < FRAMESAMPLES/8; n++)
      sum += (WebRtcIsacfix_kCos[k][n] * diff[n] + 256) >> 9;
    CorrQ7[k+1] = sum;
  }

  for (k=1; k<AR_ORDER; k+=2) {
    sum = 0;
    for (n = 0; n < FRAMESAMPLES/8; n++)
      sum += (WebRtcIsacfix_kCos[k][n] * summ[n] + 256) >> 9;
    CorrQ7[k+1] = sum;
  }
}


/* compute inverse AR power spectrum */
static void CalcInvArSpec(const int16_t *ARCoefQ12,
                          const int32_t gainQ10,
                          int32_t *CurveQ16)
{
  int32_t CorrQ11[AR_ORDER+1];
  int32_t sum, tmpGain;
  int32_t diffQ16[FRAMESAMPLES/8];
  const int16_t *CS_ptrQ9;
  int k, n;
  int16_t round, shftVal = 0, sh;

  sum = 0;
  for (n = 0; n < AR_ORDER+1; n++)
    sum += WEBRTC_SPL_MUL(ARCoefQ12[n], ARCoefQ12[n]);    /* Q24 */
  sum = ((sum >> 6) * 65 + 32768) >> 16;  /* Result in Q8. */
  CorrQ11[0] = (sum * gainQ10 + 256) >> 9;

  /* To avoid overflow, we shift down gainQ10 if it is large. We will not lose any precision */
  if(gainQ10>400000){
    tmpGain = gainQ10 >> 3;
    round = 32;
    shftVal = 6;
  } else {
    tmpGain = gainQ10;
    round = 256;
    shftVal = 9;
  }

  for (k = 1; k < AR_ORDER+1; k++) {
    sum = 16384;
    for (n = k; n < AR_ORDER+1; n++)
      sum += WEBRTC_SPL_MUL(ARCoefQ12[n-k], ARCoefQ12[n]);  /* Q24 */
    sum >>= 15;
    CorrQ11[k] = (sum * tmpGain + round) >> shftVal;
  }
  sum = WEBRTC_SPL_LSHIFT_W32(CorrQ11[0], 7);
  for (n = 0; n < FRAMESAMPLES/8; n++)
    CurveQ16[n] = sum;

  for (k = 1; k < AR_ORDER; k += 2) {
    for (n = 0; n < FRAMESAMPLES/8; n++)
      CurveQ16[n] += (WebRtcIsacfix_kCos[k][n] * CorrQ11[k + 1] + 2) >> 2;
  }

  CS_ptrQ9 = WebRtcIsacfix_kCos[0];

  /* If CorrQ11[1] too large we avoid getting overflow in the calculation by shifting */
  sh=WebRtcSpl_NormW32(CorrQ11[1]);
  if (CorrQ11[1]==0) /* Use next correlation */
    sh=WebRtcSpl_NormW32(CorrQ11[2]);

  if (sh<9)
    shftVal = 9 - sh;
  else
    shftVal = 0;

  for (n = 0; n < FRAMESAMPLES/8; n++)
    diffQ16[n] = (CS_ptrQ9[n] * (CorrQ11[1] >> shftVal) + 2) >> 2;
  for (k = 2; k < AR_ORDER; k += 2) {
    CS_ptrQ9 = WebRtcIsacfix_kCos[k];
    for (n = 0; n < FRAMESAMPLES/8; n++)
      diffQ16[n] += (CS_ptrQ9[n] * (CorrQ11[k + 1] >> shftVal) + 2) >> 2;
  }

  for (k=0; k<FRAMESAMPLES/8; k++) {
    CurveQ16[FRAMESAMPLES/4-1 - k] = CurveQ16[k] - WEBRTC_SPL_LSHIFT_W32(diffQ16[k], shftVal);
    CurveQ16[k] += WEBRTC_SPL_LSHIFT_W32(diffQ16[k], shftVal);
  }
}

static void CalcRootInvArSpec(const int16_t *ARCoefQ12,
                              const int32_t gainQ10,
                              uint16_t *CurveQ8)
{
  int32_t CorrQ11[AR_ORDER+1];
  int32_t sum, tmpGain;
  int32_t summQ16[FRAMESAMPLES/8];
  int32_t diffQ16[FRAMESAMPLES/8];

  const int16_t *CS_ptrQ9;
  int k, n, i;
  int16_t round, shftVal = 0, sh;
  int32_t res, in_sqrt, newRes;

  sum = 0;
  for (n = 0; n < AR_ORDER+1; n++)
    sum += WEBRTC_SPL_MUL(ARCoefQ12[n], ARCoefQ12[n]);    /* Q24 */
  sum = ((sum >> 6) * 65 + 32768) >> 16;  /* Result in Q8. */
  CorrQ11[0] = (sum * gainQ10 + 256) >> 9;

  /* To avoid overflow, we shift down gainQ10 if it is large. We will not lose any precision */
  if(gainQ10>400000){
    tmpGain = gainQ10 >> 3;
    round = 32;
    shftVal = 6;
  } else {
    tmpGain = gainQ10;
    round = 256;
    shftVal = 9;
  }

  for (k = 1; k < AR_ORDER+1; k++) {
    sum = 16384;
    for (n = k; n < AR_ORDER+1; n++)
      sum += WEBRTC_SPL_MUL(ARCoefQ12[n-k], ARCoefQ12[n]);  /* Q24 */
    sum >>= 15;
    CorrQ11[k] = (sum * tmpGain + round) >> shftVal;
  }
  sum = WEBRTC_SPL_LSHIFT_W32(CorrQ11[0], 7);
  for (n = 0; n < FRAMESAMPLES/8; n++)
    summQ16[n] = sum;

  for (k = 1; k < (AR_ORDER); k += 2) {
    for (n = 0; n < FRAMESAMPLES/8; n++)
      summQ16[n] += ((CorrQ11[k + 1] * WebRtcIsacfix_kCos[k][n]) + 2) >> 2;
  }

  CS_ptrQ9 = WebRtcIsacfix_kCos[0];

  /* If CorrQ11[1] too large we avoid getting overflow in the calculation by shifting */
  sh=WebRtcSpl_NormW32(CorrQ11[1]);
  if (CorrQ11[1]==0) /* Use next correlation */
    sh=WebRtcSpl_NormW32(CorrQ11[2]);

  if (sh<9)
    shftVal = 9 - sh;
  else
    shftVal = 0;

  for (n = 0; n < FRAMESAMPLES/8; n++)
    diffQ16[n] = (CS_ptrQ9[n] * (CorrQ11[1] >> shftVal) + 2) >> 2;
  for (k = 2; k < AR_ORDER; k += 2) {
    CS_ptrQ9 = WebRtcIsacfix_kCos[k];
    for (n = 0; n < FRAMESAMPLES/8; n++)
      diffQ16[n] += (CS_ptrQ9[n] * (CorrQ11[k + 1] >> shftVal) + 2) >> 2;
  }

  in_sqrt = summQ16[0] + WEBRTC_SPL_LSHIFT_W32(diffQ16[0], shftVal);

  /* convert to magnitude spectrum, by doing square-roots (modified from SPLIB)  */
  res = 1 << (WebRtcSpl_GetSizeInBits(in_sqrt) >> 1);

  for (k = 0; k < FRAMESAMPLES/8; k++)
  {
    in_sqrt = summQ16[k] + WEBRTC_SPL_LSHIFT_W32(diffQ16[k], shftVal);
    i = 10;

    /* make in_sqrt positive to prohibit sqrt of negative values */
    if(in_sqrt<0)
      in_sqrt=-in_sqrt;

    newRes = (in_sqrt / res + res) >> 1;
    do
    {
      res = newRes;
      newRes = (in_sqrt / res + res) >> 1;
    } while (newRes != res && i-- > 0);

    CurveQ8[k] = (int16_t)newRes;
  }
  for (k = FRAMESAMPLES/8; k < FRAMESAMPLES/4; k++) {

    in_sqrt = summQ16[FRAMESAMPLES/4-1 - k] - WEBRTC_SPL_LSHIFT_W32(diffQ16[FRAMESAMPLES/4-1 - k], shftVal);
    i = 10;

    /* make in_sqrt positive to prohibit sqrt of negative values */
    if(in_sqrt<0)
      in_sqrt=-in_sqrt;

    newRes = (in_sqrt / res + res) >> 1;
    do
    {
      res = newRes;
      newRes = (in_sqrt / res + res) >> 1;
    } while (newRes != res && i-- > 0);

    CurveQ8[k] = (int16_t)newRes;
  }

}



/* generate array of dither samples in Q7 */
static void GenerateDitherQ7(int16_t *bufQ7,
                             uint32_t seed,
                             int16_t length,
                             int16_t AvgPitchGain_Q12)
{
  int   k;
  int16_t dither1_Q7, dither2_Q7, dither_gain_Q14, shft;

  if (AvgPitchGain_Q12 < 614)  /* this threshold should be equal to that in decode_spec() */
  {
    for (k = 0; k < length-2; k += 3)
    {
      /* new random unsigned int32_t */
      seed = WEBRTC_SPL_UMUL(seed, 196314165) + 907633515;

      /* fixed-point dither sample between -64 and 64 (Q7) */
      dither1_Q7 = (int16_t)(((int32_t)seed + 16777216) >> 25);

      /* new random unsigned int32_t */
      seed = WEBRTC_SPL_UMUL(seed, 196314165) + 907633515;

      /* fixed-point dither sample between -64 and 64 */
      dither2_Q7 = (int16_t)((seed + 16777216) >> 25);

      shft = (int16_t)(WEBRTC_SPL_RSHIFT_U32(seed, 25) & 15);
      if (shft < 5)
      {
        bufQ7[k]   = dither1_Q7;
        bufQ7[k+1] = dither2_Q7;
        bufQ7[k+2] = 0;
      }
      else if (shft < 10)
      {
        bufQ7[k]   = dither1_Q7;
        bufQ7[k+1] = 0;
        bufQ7[k+2] = dither2_Q7;
      }
      else
      {
        bufQ7[k]   = 0;
        bufQ7[k+1] = dither1_Q7;
        bufQ7[k+2] = dither2_Q7;
      }
    }
  }
  else
  {
    dither_gain_Q14 = (int16_t)(22528 - WEBRTC_SPL_MUL(10, AvgPitchGain_Q12));

    /* dither on half of the coefficients */
    for (k = 0; k < length-1; k += 2)
    {
      /* new random unsigned int32_t */
      seed = WEBRTC_SPL_UMUL(seed, 196314165) + 907633515;

      /* fixed-point dither sample between -64 and 64 */
      dither1_Q7 = (int16_t)(((int32_t)seed + 16777216) >> 25);

      /* dither sample is placed in either even or odd index */
      shft = (int16_t)(WEBRTC_SPL_RSHIFT_U32(seed, 25) & 1);     /* either 0 or 1 */

      bufQ7[k + shft] = (int16_t)((dither_gain_Q14 * dither1_Q7 + 8192) >> 14);
      bufQ7[k + 1 - shft] = 0;
    }
  }
}




/*
 * function to decode the complex spectrum from the bitstream
 * returns the total number of bytes in the stream
 */
int16_t WebRtcIsacfix_DecodeSpec(Bitstr_dec *streamdata,
                                 int16_t *frQ7,
                                 int16_t *fiQ7,
                                 int16_t AvgPitchGain_Q12)
{
  int16_t  data[FRAMESAMPLES];
  int32_t  invARSpec2_Q16[FRAMESAMPLES/4];
  int16_t  ARCoefQ12[AR_ORDER+1];
  int16_t  RCQ15[AR_ORDER];
  int16_t  gainQ10;
  int32_t  gain2_Q10;
  int16_t  len;
  int          k;

  /* create dither signal */
  GenerateDitherQ7(data, streamdata->W_upper, FRAMESAMPLES, AvgPitchGain_Q12); /* Dither is output in vector 'Data' */

  /* decode model parameters */
  if (WebRtcIsacfix_DecodeRcCoef(streamdata, RCQ15) < 0)
    return -ISAC_RANGE_ERROR_DECODE_SPECTRUM;


  WebRtcSpl_ReflCoefToLpc(RCQ15, AR_ORDER, ARCoefQ12);

  if (WebRtcIsacfix_DecodeGain2(streamdata, &gain2_Q10) < 0)
    return -ISAC_RANGE_ERROR_DECODE_SPECTRUM;

  /* compute inverse AR power spectrum */
  CalcInvArSpec(ARCoefQ12, gain2_Q10, invARSpec2_Q16);

  /* arithmetic decoding of spectrum */
  /* 'data' input and output. Input = Dither */
  len = WebRtcIsacfix_DecLogisticMulti2(data, streamdata, invARSpec2_Q16, (int16_t)FRAMESAMPLES);

  if (len<1)
    return -ISAC_RANGE_ERROR_DECODE_SPECTRUM;

  /* subtract dither and scale down spectral samples with low SNR */
  if (AvgPitchGain_Q12 <= 614)
  {
    for (k = 0; k < FRAMESAMPLES; k += 4)
    {
      gainQ10 = WebRtcSpl_DivW32W16ResW16(WEBRTC_SPL_LSHIFT_W32((int32_t)30, 10),
                                              (int16_t)WEBRTC_SPL_RSHIFT_U32(invARSpec2_Q16[k>>2] + (uint32_t)2195456, 16));
      *frQ7++ = (int16_t)((data[k] * gainQ10 + 512) >> 10);
      *fiQ7++ = (int16_t)((data[k + 1] * gainQ10 + 512) >> 10);
      *frQ7++ = (int16_t)((data[k + 2] * gainQ10 + 512) >> 10);
      *fiQ7++ = (int16_t)((data[k + 3] * gainQ10 + 512) >> 10);
    }
  }
  else
  {
    for (k = 0; k < FRAMESAMPLES; k += 4)
    {
      gainQ10 = WebRtcSpl_DivW32W16ResW16(WEBRTC_SPL_LSHIFT_W32((int32_t)36, 10),
                                              (int16_t)WEBRTC_SPL_RSHIFT_U32(invARSpec2_Q16[k>>2] + (uint32_t)2654208, 16));
      *frQ7++ = (int16_t)((data[k] * gainQ10 + 512) >> 10);
      *fiQ7++ = (int16_t)((data[k + 1] * gainQ10 + 512) >> 10);
      *frQ7++ = (int16_t)((data[k + 2] * gainQ10 + 512) >> 10);
      *fiQ7++ = (int16_t)((data[k + 3] * gainQ10 + 512) >> 10);
    }
  }

  return len;
}


int WebRtcIsacfix_EncodeSpec(const int16_t *fr,
                             const int16_t *fi,
                             Bitstr_enc *streamdata,
                             int16_t AvgPitchGain_Q12)
{
  int16_t  dataQ7[FRAMESAMPLES];
  int32_t  PSpec[FRAMESAMPLES/4];
  uint16_t invARSpecQ8[FRAMESAMPLES/4];
  int32_t  CorrQ7[AR_ORDER+1];
  int32_t  CorrQ7_norm[AR_ORDER+1];
  int16_t  RCQ15[AR_ORDER];
  int16_t  ARCoefQ12[AR_ORDER+1];
  int32_t  gain2_Q10;
  int16_t  val;
  int32_t  nrg;
  uint32_t sum;
  int16_t  lft_shft;
  int16_t  status;
  int          k, n, j;


  /* create dither_float signal */
  GenerateDitherQ7(dataQ7, streamdata->W_upper, FRAMESAMPLES, AvgPitchGain_Q12);

  /* add dither and quantize, and compute power spectrum */
  /* Vector dataQ7 contains Dither in Q7 */
  for (k = 0; k < FRAMESAMPLES; k += 4)
  {
    val = ((*fr++ + dataQ7[k]   + 64) & 0xFF80) - dataQ7[k]; /* Data = Dither */
    dataQ7[k] = val;            /* New value in Data */
    sum = WEBRTC_SPL_UMUL(val, val);

    val = ((*fi++ + dataQ7[k+1] + 64) & 0xFF80) - dataQ7[k+1]; /* Data = Dither */
    dataQ7[k+1] = val;            /* New value in Data */
    sum += WEBRTC_SPL_UMUL(val, val);

    val = ((*fr++ + dataQ7[k+2] + 64) & 0xFF80) - dataQ7[k+2]; /* Data = Dither */
    dataQ7[k+2] = val;            /* New value in Data */
    sum += WEBRTC_SPL_UMUL(val, val);

    val = ((*fi++ + dataQ7[k+3] + 64) & 0xFF80) - dataQ7[k+3]; /* Data = Dither */
    dataQ7[k+3] = val;            /* New value in Data */
    sum += WEBRTC_SPL_UMUL(val, val);

    PSpec[k>>2] = WEBRTC_SPL_RSHIFT_U32(sum, 2);
  }

  /* compute correlation from power spectrum */
  CalcCorrelation(PSpec, CorrQ7);


  /* find AR coefficients */
  /* number of bit shifts to 14-bit normalize CorrQ7[0] (leaving room for sign) */
  lft_shft = WebRtcSpl_NormW32(CorrQ7[0]) - 18;

  if (lft_shft > 0) {
    for (k=0; k<AR_ORDER+1; k++)
      CorrQ7_norm[k] = WEBRTC_SPL_LSHIFT_W32(CorrQ7[k], lft_shft);
  } else {
    for (k=0; k<AR_ORDER+1; k++)
      CorrQ7_norm[k] = CorrQ7[k] >> -lft_shft;
  }

  /* find RC coefficients */
  WebRtcSpl_AutoCorrToReflCoef(CorrQ7_norm, AR_ORDER, RCQ15);

  /* quantize & code RC Coef */
  status = WebRtcIsacfix_EncodeRcCoef(RCQ15, streamdata);
  if (status < 0) {
    return status;
  }

  /* RC -> AR coefficients */
  WebRtcSpl_ReflCoefToLpc(RCQ15, AR_ORDER, ARCoefQ12);

  /* compute ARCoef' * Corr * ARCoef in Q19 */
  nrg = 0;
  for (j = 0; j <= AR_ORDER; j++) {
    for (n = 0; n <= j; n++)
      nrg += (ARCoefQ12[j] * ((CorrQ7_norm[j - n] * ARCoefQ12[n] + 256) >> 9) +
          4) >> 3;
    for (n = j+1; n <= AR_ORDER; n++)
      nrg += (ARCoefQ12[j] * ((CorrQ7_norm[n - j] * ARCoefQ12[n] + 256) >> 9) +
          4) >> 3;
  }

  if (lft_shft > 0)
    nrg >>= lft_shft;
  else
    nrg = WEBRTC_SPL_LSHIFT_W32(nrg, -lft_shft);

  if(nrg>131072)
    gain2_Q10 = WebRtcSpl_DivResultInQ31(FRAMESAMPLES >> 2, nrg);  /* also shifts 31 bits to the left! */
  else
    gain2_Q10 = FRAMESAMPLES >> 2;

  /* quantize & code gain2_Q10 */
  if (WebRtcIsacfix_EncodeGain2(&gain2_Q10, streamdata))
    return -1;

  /* compute inverse AR magnitude spectrum */
  CalcRootInvArSpec(ARCoefQ12, gain2_Q10, invARSpecQ8);


  /* arithmetic coding of spectrum */
  status = WebRtcIsacfix_EncLogisticMulti2(streamdata, dataQ7, invARSpecQ8, (int16_t)FRAMESAMPLES);
  if ( status )
    return( status );

  return 0;
}


/* Matlab's LAR definition */
static void Rc2LarFix(const int16_t *rcQ15, int32_t *larQ17, int16_t order) {

  /*

    This is a piece-wise implemenetation of a rc2lar-function (all values in the comment
    are Q15 values and  are based on [0 24956/32768 30000/32768 32500/32768], i.e.
    [0.76159667968750   0.91552734375000   0.99182128906250]

    x0  x1           a                 k              x0(again)         b
    ==================================================================================
    0.00 0.76:   0                  2.625997508581   0                  0
    0.76 0.91:   2.000012018559     7.284502668663   0.761596679688    -3.547841027073
    0.91 0.99:   3.121320351712    31.115835041229   0.915527343750   -25.366077452148
    0.99 1.00:   5.495270168700   686.663805654056   0.991821289063  -675.552510708011

    The implementation is y(x)= a + (x-x0)*k, but this can be simplified to

    y(x) = a-x0*k + x*k = b + x*k, where b = a-x0*k

    akx=[0                 2.625997508581   0
    2.000012018559     7.284502668663   0.761596679688
    3.121320351712    31.115835041229   0.915527343750
    5.495270168700   686.663805654056   0.991821289063];

    b = akx(:,1) - akx(:,3).*akx(:,2)

    [ 0.0
    -3.547841027073
    -25.366077452148
    -675.552510708011]

  */

  int k;
  int16_t rc;
  int32_t larAbsQ17;

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

    rc = WEBRTC_SPL_ABS_W16(rcQ15[k]); //Q15

    /* Calculate larAbsQ17 in Q17 from rc in Q15 */

    if (rc<24956) {  //0.7615966 in Q15
      // (Q15*Q13)>>11 = Q17
      larAbsQ17 = WEBRTC_SPL_MUL_16_16_RSFT(rc, 21512, 11);
    } else if (rc<30000) { //0.91552734375 in Q15
      // Q17 + (Q15*Q12)>>10 = Q17
      larAbsQ17 = -465024 + WEBRTC_SPL_MUL_16_16_RSFT(rc, 29837, 10);
    } else if (rc<32500) { //0.99182128906250 in Q15
      // Q17 + (Q15*Q10)>>8 = Q17
      larAbsQ17 = -3324784 + WEBRTC_SPL_MUL_16_16_RSFT(rc, 31863, 8);
    } else  {
      // Q17 + (Q15*Q5)>>3 = Q17
      larAbsQ17 = -88546020 + WEBRTC_SPL_MUL_16_16_RSFT(rc, 21973, 3);
    }

    if (rcQ15[k]>0) {
      larQ17[k] = larAbsQ17;
    } else {
      larQ17[k] = -larAbsQ17;
    }
  }
}


static void Lar2RcFix(const int32_t *larQ17, int16_t *rcQ15,  int16_t order) {

  /*
    This is a piece-wise implemenetation of a lar2rc-function
    See comment in Rc2LarFix() about details.
  */

  int k;
  int16_t larAbsQ11;
  int32_t rc;

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

    larAbsQ11 = (int16_t)WEBRTC_SPL_ABS_W32((larQ17[k] + 32) >> 6);  // Q11

    if (larAbsQ11<4097) { //2.000012018559 in Q11
      // Q11*Q16>>12 = Q15
      rc = WEBRTC_SPL_MUL_16_16_RSFT(larAbsQ11, 24957, 12);
    } else if (larAbsQ11<6393) { //3.121320351712 in Q11
      // (Q11*Q17 + Q13)>>13 = Q15
      rc = (larAbsQ11 * 17993 + 130738688) >> 13;
    } else if (larAbsQ11<11255) { //5.495270168700 in Q11
      // (Q11*Q19 + Q30)>>15 = Q15
      rc = (larAbsQ11 * 16850 + 875329820) >> 15;
    } else  {
      // (Q11*Q24>>16 + Q19)>>4 = Q15
      rc = (((larAbsQ11 * 24433) >> 16) + 515804) >> 4;
    }

    if (larQ17[k]<=0) {
      rc = -rc;
    }

    rcQ15[k] = (int16_t) rc;  // Q15
  }
}

static void Poly2LarFix(int16_t *lowbandQ15,
                        int16_t orderLo,
                        int16_t *hibandQ15,
                        int16_t orderHi,
                        int16_t Nsub,
                        int32_t *larsQ17) {

  int k, n;
  int32_t *outpQ17;
  int16_t orderTot;
  int32_t larQ17[MAX_ORDER];   // Size 7+6 is enough

  orderTot = (orderLo + orderHi);
  outpQ17 = larsQ17;
  for (k = 0; k < Nsub; k++) {

    Rc2LarFix(lowbandQ15, larQ17, orderLo);

    for (n = 0; n < orderLo; n++)
      outpQ17[n] = larQ17[n]; //Q17

    Rc2LarFix(hibandQ15, larQ17, orderHi);

    for (n = 0; n < orderHi; n++)
      outpQ17[n + orderLo] = larQ17[n]; //Q17;

    outpQ17 += orderTot;
    lowbandQ15 += orderLo;
    hibandQ15 += orderHi;
  }
}


static void Lar2polyFix(int32_t *larsQ17,
                        int16_t *lowbandQ15,
                        int16_t orderLo,
                        int16_t *hibandQ15,
                        int16_t orderHi,
                        int16_t Nsub) {

  int k, n;
  int16_t orderTot;
  int16_t *outplQ15, *outphQ15;
  int32_t *inpQ17;
  int16_t rcQ15[7+6];

  orderTot = (orderLo + orderHi);
  outplQ15 = lowbandQ15;
  outphQ15 = hibandQ15;
  inpQ17 = larsQ17;
  for (k = 0; k < Nsub; k++) {

    /* gains not handled here as in the FLP version */

    /* Low band */
    Lar2RcFix(&inpQ17[0], rcQ15, orderLo);
    for (n = 0; n < orderLo; n++)
      outplQ15[n] = rcQ15[n]; // Refl. coeffs

    /* High band */
    Lar2RcFix(&inpQ17[orderLo], rcQ15, orderHi);
    for (n = 0; n < orderHi; n++)
      outphQ15[n] = rcQ15[n]; // Refl. coeffs

    inpQ17 += orderTot;
    outplQ15 += orderLo;
    outphQ15 += orderHi;
  }
}

/*
Function WebRtcIsacfix_MatrixProduct1C() does one form of matrix multiplication.
It first shifts input data of one matrix, determines the right indexes for the
two matrixes, multiply them, and write the results into an output buffer.

Note that two factors (or, multipliers) determine the initialization values of
the variable |matrix1_index| in the code. The relationship is
|matrix1_index| = |matrix1_index_factor1| * |matrix1_index_factor2|, where
|matrix1_index_factor1| is given by the argument while |matrix1_index_factor2|
is determined by the value of argument |matrix1_index_init_case|;
|matrix1_index_factor2| is the value of the outmost loop counter j (when
|matrix1_index_init_case| is 0), or the value of the middle loop counter k (when
|matrix1_index_init_case| is non-zero).

|matrix0_index| is determined the same way.

Arguments:
  matrix0[]:                 matrix0 data in Q15 domain.
  matrix1[]:                 matrix1 data.
  matrix_product[]:          output data (matrix product).
  matrix1_index_factor1:     The first of two factors determining the
                             initialization value of matrix1_index.
  matrix0_index_factor1:     The first of two factors determining the
                             initialization value of matrix0_index.
  matrix1_index_init_case:   Case number for selecting the second of two
                             factors determining the initialization value
                             of matrix1_index and matrix0_index.
  matrix1_index_step:        Incremental step for matrix1_index.
  matrix0_index_step:        Incremental step for matrix0_index.
  inner_loop_count:          Maximum count of the inner loop.
  mid_loop_count:            Maximum count of the intermediate loop.
  shift:                     Left shift value for matrix1.
*/
void WebRtcIsacfix_MatrixProduct1C(const int16_t matrix0[],
                                   const int32_t matrix1[],
                                   int32_t matrix_product[],
                                   const int matrix1_index_factor1,
                                   const int matrix0_index_factor1,
                                   const int matrix1_index_init_case,
                                   const int matrix1_index_step,
                                   const int matrix0_index_step,
                                   const int inner_loop_count,
                                   const int mid_loop_count,
                                   const int shift) {
  int j = 0, k = 0, n = 0;
  int matrix0_index = 0, matrix1_index = 0, matrix_prod_index = 0;
  int* matrix0_index_factor2 = &k;
  int* matrix1_index_factor2 = &j;
  if (matrix1_index_init_case != 0) {
    matrix0_index_factor2 = &j;
    matrix1_index_factor2 = &k;
  }

  for (j = 0; j < SUBFRAMES; j++) {
    matrix_prod_index = mid_loop_count * j;
    for (k = 0; k < mid_loop_count; k++) {
      int32_t sum32 = 0;
      matrix0_index = matrix0_index_factor1 * (*matrix0_index_factor2);
      matrix1_index = matrix1_index_factor1 * (*matrix1_index_factor2);
      for (n = 0; n < inner_loop_count; n++) {
        sum32 += (WEBRTC_SPL_MUL_16_32_RSFT16(matrix0[matrix0_index],
                                              matrix1[matrix1_index] << shift));
        matrix0_index += matrix0_index_step;
        matrix1_index += matrix1_index_step;
      }
      matrix_product[matrix_prod_index] = sum32;
      matrix_prod_index++;
    }
  }
}

/*
Function WebRtcIsacfix_MatrixProduct2C() returns the product of two matrixes,
one of which has two columns. It first has to determine the correct index of
the first matrix before doing the actual element multiplication.

Arguments:
  matrix0[]:                 A matrix in Q15 domain.
  matrix1[]:                 A matrix in Q21 domain.
  matrix_product[]:          Output data in Q17 domain.
  matrix0_index_factor:      A factor determining the initialization value
                             of matrix0_index.
  matrix0_index_step:        Incremental step for matrix0_index.
*/
void WebRtcIsacfix_MatrixProduct2C(const int16_t matrix0[],
                                   const int32_t matrix1[],
                                   int32_t matrix_product[],
                                   const int matrix0_index_factor,
                                   const int matrix0_index_step) {
  int j = 0, n = 0;
  int matrix1_index = 0, matrix0_index = 0, matrix_prod_index = 0;
  for (j = 0; j < SUBFRAMES; j++) {
    int32_t sum32 = 0, sum32_2 = 0;
    matrix1_index = 0;
    matrix0_index = matrix0_index_factor * j;
    for (n = SUBFRAMES; n > 0; n--) {
      sum32 += (WEBRTC_SPL_MUL_16_32_RSFT16(matrix0[matrix0_index],
                                            matrix1[matrix1_index]));
      sum32_2 += (WEBRTC_SPL_MUL_16_32_RSFT16(matrix0[matrix0_index],
                                            matrix1[matrix1_index + 1]));
      matrix1_index += 2;
      matrix0_index += matrix0_index_step;
    }
    matrix_product[matrix_prod_index] = sum32 >> 3;
    matrix_product[matrix_prod_index + 1] = sum32_2 >> 3;
    matrix_prod_index += 2;
  }
}

int WebRtcIsacfix_DecodeLpc(int32_t *gain_lo_hiQ17,
                            int16_t *LPCCoef_loQ15,
                            int16_t *LPCCoef_hiQ15,
                            Bitstr_dec *streamdata,
                            int16_t *outmodel) {

  int32_t larsQ17[KLT_ORDER_SHAPE]; // KLT_ORDER_GAIN+KLT_ORDER_SHAPE == (ORDERLO+ORDERHI)*SUBFRAMES
  int err;

  err = WebRtcIsacfix_DecodeLpcCoef(streamdata, larsQ17, gain_lo_hiQ17, outmodel);
  if (err<0)  // error check
    return -ISAC_RANGE_ERROR_DECODE_LPC;

  Lar2polyFix(larsQ17, LPCCoef_loQ15, ORDERLO, LPCCoef_hiQ15, ORDERHI, SUBFRAMES);

  return 0;
}

/* decode & dequantize LPC Coef */
int WebRtcIsacfix_DecodeLpcCoef(Bitstr_dec *streamdata,
                                int32_t *LPCCoefQ17,
                                int32_t *gain_lo_hiQ17,
                                int16_t *outmodel)
{
  int j, k, n;
  int err;
  int16_t pos, pos2, posg, poss;
  int16_t gainpos;
  int16_t model;
  int16_t index_QQ[KLT_ORDER_SHAPE];
  int32_t tmpcoeffs_gQ17[KLT_ORDER_GAIN];
  int32_t tmpcoeffs2_gQ21[KLT_ORDER_GAIN];
  int16_t tmpcoeffs_sQ10[KLT_ORDER_SHAPE];
  int32_t tmpcoeffs_sQ17[KLT_ORDER_SHAPE];
  int32_t tmpcoeffs2_sQ18[KLT_ORDER_SHAPE];
  int32_t sumQQ;
  int16_t sumQQ16;
  int32_t tmp32;



  /* entropy decoding of model number */
  err = WebRtcIsacfix_DecHistOneStepMulti(&model, streamdata, WebRtcIsacfix_kModelCdfPtr, WebRtcIsacfix_kModelInitIndex, 1);
  if (err<0)  // error check
    return err;

  /* entropy decoding of quantization indices */
  err = WebRtcIsacfix_DecHistOneStepMulti(index_QQ, streamdata, WebRtcIsacfix_kCdfShapePtr[model], WebRtcIsacfix_kInitIndexShape[model], KLT_ORDER_SHAPE);
  if (err<0)  // error check
    return err;
  /* find quantization levels for coefficients */
  for (k=0; k<KLT_ORDER_SHAPE; k++) {
    tmpcoeffs_sQ10[WebRtcIsacfix_kSelIndShape[k]] = WebRtcIsacfix_kLevelsShapeQ10[WebRtcIsacfix_kOfLevelsShape[model]+WebRtcIsacfix_kOffsetShape[model][k] + index_QQ[k]];
  }

  err = WebRtcIsacfix_DecHistOneStepMulti(index_QQ, streamdata, WebRtcIsacfix_kCdfGainPtr[model], WebRtcIsacfix_kInitIndexGain[model], KLT_ORDER_GAIN);
  if (err<0)  // error check
    return err;
  /* find quantization levels for coefficients */
  for (k=0; k<KLT_ORDER_GAIN; k++) {
    tmpcoeffs_gQ17[WebRtcIsacfix_kSelIndGain[k]] = WebRtcIsacfix_kLevelsGainQ17[WebRtcIsacfix_kOfLevelsGain[model]+ WebRtcIsacfix_kOffsetGain[model][k] + index_QQ[k]];
  }


  /* inverse KLT  */

  /* left transform */  // Transpose matrix!
  WebRtcIsacfix_MatrixProduct1(WebRtcIsacfix_kT1GainQ15[model], tmpcoeffs_gQ17,
                               tmpcoeffs2_gQ21, kTIndexFactor2, kTIndexFactor2,
                               kTInitCase0, kTIndexStep1, kTIndexStep1,
                               kTLoopCount2, kTLoopCount2, kTMatrix1_shift5);

  poss = 0;
  for (j=0; j<SUBFRAMES; j++) {
    for (k=0; k<LPC_SHAPE_ORDER; k++) {
      sumQQ = 0;
      pos = LPC_SHAPE_ORDER * j;
      pos2 = LPC_SHAPE_ORDER * k;
      for (n=0; n<LPC_SHAPE_ORDER; n++) {
        sumQQ += WEBRTC_SPL_MUL_16_16_RSFT(tmpcoeffs_sQ10[pos], WebRtcIsacfix_kT1ShapeQ15[model][pos2], 7); // (Q10*Q15)>>7 = Q18
        pos++;
        pos2++;
      }
      tmpcoeffs2_sQ18[poss] = sumQQ; //Q18
      poss++;
    }
  }

  /* right transform */ // Transpose matrix
  WebRtcIsacfix_MatrixProduct2(WebRtcIsacfix_kT2GainQ15[0], tmpcoeffs2_gQ21,
                               tmpcoeffs_gQ17, kTIndexFactor1, kTIndexStep2);
  WebRtcIsacfix_MatrixProduct1(WebRtcIsacfix_kT2ShapeQ15[model],
      tmpcoeffs2_sQ18, tmpcoeffs_sQ17, kTIndexFactor1, kTIndexFactor1,
      kTInitCase1, kTIndexStep3, kTIndexStep2, kTLoopCount1, kTLoopCount3,
      kTMatrix1_shift0);

  /* scaling, mean addition, and gain restoration */
  gainpos = 0;
  posg = 0;poss = 0;pos=0;
  for (k=0; k<SUBFRAMES; k++) {

    /* log gains */
    // Divide by 4 and get Q17 to Q8, i.e. shift 2+9.
    sumQQ16 = (int16_t)(tmpcoeffs_gQ17[posg] >> 11);
    sumQQ16 += WebRtcIsacfix_kMeansGainQ8[model][posg];
    sumQQ = CalcExpN(sumQQ16); // Q8 in and Q17 out
    gain_lo_hiQ17[gainpos] = sumQQ; //Q17
    gainpos++;
    posg++;

    // Divide by 4 and get Q17 to Q8, i.e. shift 2+9.
    sumQQ16 = (int16_t)(tmpcoeffs_gQ17[posg] >> 11);
    sumQQ16 += WebRtcIsacfix_kMeansGainQ8[model][posg];
    sumQQ = CalcExpN(sumQQ16); // Q8 in and Q17 out
    gain_lo_hiQ17[gainpos] = sumQQ; //Q17
    gainpos++;
    posg++;

    /* lo band LAR coeffs */
    for (n=0; n<ORDERLO; n++, pos++, poss++) {
      tmp32 = WEBRTC_SPL_MUL_16_32_RSFT16(31208, tmpcoeffs_sQ17[poss]); // (Q16*Q17)>>16 = Q17, with 1/2.1 = 0.47619047619 ~= 31208 in Q16
      tmp32 = tmp32 + WebRtcIsacfix_kMeansShapeQ17[model][poss]; // Q17+Q17 = Q17
      LPCCoefQ17[pos] = tmp32;
    }

    /* hi band LAR coeffs */
    for (n=0; n<ORDERHI; n++, pos++, poss++) {
      tmp32 = WEBRTC_SPL_LSHIFT_W32(WEBRTC_SPL_MUL_16_32_RSFT16(18204, tmpcoeffs_sQ17[poss]), 3); // ((Q13*Q17)>>16)<<3 = Q17, with 1/0.45 = 2.222222222222 ~= 18204 in Q13
      tmp32 = tmp32 + WebRtcIsacfix_kMeansShapeQ17[model][poss]; // Q17+Q17 = Q17
      LPCCoefQ17[pos] = tmp32;
    }
  }


  *outmodel=model;

  return 0;
}

/* estimate codel length of LPC Coef */
static int EstCodeLpcCoef(int32_t *LPCCoefQ17,
                          int32_t *gain_lo_hiQ17,
                          int16_t *model,
                          int32_t *sizeQ11,
                          Bitstr_enc *streamdata,
                          IsacSaveEncoderData* encData,
                          transcode_obj *transcodingParam) {
  int j, k, n;
  int16_t posQQ, pos2QQ, gainpos;
  int16_t  pos, poss, posg, offsg;
  int16_t index_gQQ[KLT_ORDER_GAIN], index_sQQ[KLT_ORDER_SHAPE];
  int16_t index_ovr_gQQ[KLT_ORDER_GAIN], index_ovr_sQQ[KLT_ORDER_SHAPE];
  int32_t BitsQQ;

  int16_t tmpcoeffs_gQ6[KLT_ORDER_GAIN];
  int32_t tmpcoeffs_gQ17[KLT_ORDER_GAIN];
  int32_t tmpcoeffs_sQ17[KLT_ORDER_SHAPE];
  int32_t tmpcoeffs2_gQ21[KLT_ORDER_GAIN];
  int32_t tmpcoeffs2_sQ17[KLT_ORDER_SHAPE];
  int32_t sumQQ;
  int32_t tmp32;
  int16_t sumQQ16;
  int status = 0;

  /* write LAR coefficients to statistics file */
  /* Save data for creation of multiple bitstreams (and transcoding) */
  if (encData != NULL) {
    for (k=0; k<KLT_ORDER_GAIN; k++) {
      encData->LPCcoeffs_g[KLT_ORDER_GAIN*encData->startIdx + k] = gain_lo_hiQ17[k];
    }
  }

  /* log gains, mean removal and scaling */
  posg = 0;poss = 0;pos=0; gainpos=0;

  for (k=0; k<SUBFRAMES; k++) {
    /* log gains */

    /* The input argument X to logN(X) is 2^17 times higher than the
       input floating point argument Y to log(Y), since the X value
       is a Q17 value. This can be compensated for after the call, by
       subraction a value Z for each Q-step. One Q-step means that
       X gets 2 times higher, i.e. Z = logN(2)*256 = 0.693147180559*256 =
       177.445678 should be subtracted (since logN() returns a Q8 value).
       For a X value in Q17, the value 177.445678*17 = 3017 should be
       subtracted */
    tmpcoeffs_gQ6[posg] = CalcLogN(gain_lo_hiQ17[gainpos])-3017; //Q8
    tmpcoeffs_gQ6[posg] -= WebRtcIsacfix_kMeansGainQ8[0][posg]; //Q8, but Q6 after not-needed mult. by 4
    posg++; gainpos++;

    tmpcoeffs_gQ6[posg] = CalcLogN(gain_lo_hiQ17[gainpos])-3017; //Q8
    tmpcoeffs_gQ6[posg] -= WebRtcIsacfix_kMeansGainQ8[0][posg]; //Q8, but Q6 after not-needed mult. by 4
    posg++; gainpos++;

    /* lo band LAR coeffs */
    for (n=0; n<ORDERLO; n++, poss++, pos++) {
      tmp32 = LPCCoefQ17[pos] - WebRtcIsacfix_kMeansShapeQ17[0][poss]; //Q17
      tmp32 = WEBRTC_SPL_MUL_16_32_RSFT16(17203, tmp32<<3); // tmp32 = 2.1*tmp32
      tmpcoeffs_sQ17[poss] = tmp32; //Q17
    }

    /* hi band LAR coeffs */
    for (n=0; n<ORDERHI; n++, poss++, pos++) {
      tmp32 = LPCCoefQ17[pos] - WebRtcIsacfix_kMeansShapeQ17[0][poss]; //Q17
      tmp32 = WEBRTC_SPL_MUL_16_32_RSFT16(14746, tmp32<<1); // tmp32 = 0.45*tmp32
      tmpcoeffs_sQ17[poss] = tmp32; //Q17
    }

  }


  /* KLT  */

  /* left transform */
  offsg = 0;
  posg = 0;
  for (j=0; j<SUBFRAMES; j++) {
    // Q21 = Q6 * Q15
    sumQQ = WEBRTC_SPL_MUL_16_16(tmpcoeffs_gQ6[offsg],
        WebRtcIsacfix_kT1GainQ15[0][0]);
    sumQQ += WEBRTC_SPL_MUL_16_16(tmpcoeffs_gQ6[offsg + 1],
        WebRtcIsacfix_kT1GainQ15[0][2]);
    tmpcoeffs2_gQ21[posg] = sumQQ;
    posg++;

    // Q21 = Q6 * Q15
    sumQQ = WEBRTC_SPL_MUL_16_16(tmpcoeffs_gQ6[offsg],
        WebRtcIsacfix_kT1GainQ15[0][1]);
    sumQQ += WEBRTC_SPL_MUL_16_16(tmpcoeffs_gQ6[offsg + 1],
        WebRtcIsacfix_kT1GainQ15[0][3]);
    tmpcoeffs2_gQ21[posg] = sumQQ;
    posg++;

    offsg += 2;
  }

  WebRtcIsacfix_MatrixProduct1(WebRtcIsacfix_kT1ShapeQ15[0], tmpcoeffs_sQ17,
      tmpcoeffs2_sQ17, kTIndexFactor4, kTIndexFactor1, kTInitCase0,
      kTIndexStep1, kTIndexStep3, kTLoopCount3, kTLoopCount3, kTMatrix1_shift1);

  /* right transform */
  WebRtcIsacfix_MatrixProduct2(WebRtcIsacfix_kT2GainQ15[0], tmpcoeffs2_gQ21,
                               tmpcoeffs_gQ17, kTIndexFactor3, kTIndexStep1);

  WebRtcIsacfix_MatrixProduct1(WebRtcIsacfix_kT2ShapeQ15[0], tmpcoeffs2_sQ17,
      tmpcoeffs_sQ17, kTIndexFactor1, kTIndexFactor3, kTInitCase1, kTIndexStep3,
      kTIndexStep1, kTLoopCount1, kTLoopCount3, kTMatrix1_shift1);

  /* quantize coefficients */

  BitsQQ = 0;
  for (k=0; k<KLT_ORDER_GAIN; k++) //ATTN: ok?
  {
    posQQ = WebRtcIsacfix_kSelIndGain[k];
    pos2QQ= (int16_t)CalcLrIntQ(tmpcoeffs_gQ17[posQQ], 17);

    index_gQQ[k] = pos2QQ + WebRtcIsacfix_kQuantMinGain[k]; //ATTN: ok?
    if (index_gQQ[k] < 0) {
      index_gQQ[k] = 0;
    }
    else if (index_gQQ[k] > WebRtcIsacfix_kMaxIndGain[k]) {
      index_gQQ[k] = WebRtcIsacfix_kMaxIndGain[k];
    }
    index_ovr_gQQ[k] = WebRtcIsacfix_kOffsetGain[0][k]+index_gQQ[k];
    posQQ = WebRtcIsacfix_kOfLevelsGain[0] + index_ovr_gQQ[k];

    /* Save data for creation of multiple bitstreams */
    if (encData != NULL) {
      encData->LPCindex_g[KLT_ORDER_GAIN*encData->startIdx + k] = index_gQQ[k];
    }

    /* determine number of bits */
    sumQQ = WebRtcIsacfix_kCodeLenGainQ11[posQQ]; //Q11
    BitsQQ += sumQQ;
  }

  for (k=0; k<KLT_ORDER_SHAPE; k++) //ATTN: ok?
  {
    index_sQQ[k] = (int16_t)(CalcLrIntQ(tmpcoeffs_sQ17[WebRtcIsacfix_kSelIndShape[k]], 17) + WebRtcIsacfix_kQuantMinShape[k]); //ATTN: ok?

    if (index_sQQ[k] < 0)
      index_sQQ[k] = 0;
    else if (index_sQQ[k] > WebRtcIsacfix_kMaxIndShape[k])
      index_sQQ[k] = WebRtcIsacfix_kMaxIndShape[k];
    index_ovr_sQQ[k] = WebRtcIsacfix_kOffsetShape[0][k]+index_sQQ[k];

    posQQ = WebRtcIsacfix_kOfLevelsShape[0] + index_ovr_sQQ[k];
    sumQQ = WebRtcIsacfix_kCodeLenShapeQ11[posQQ]; //Q11
    BitsQQ += sumQQ;
  }



  *model = 0;
  *sizeQ11=BitsQQ;

  /* entropy coding of model number */
  status = WebRtcIsacfix_EncHistMulti(streamdata, model, WebRtcIsacfix_kModelCdfPtr, 1);
  if (status < 0) {
    return status;
  }

  /* entropy coding of quantization indices - shape only */
  status = WebRtcIsacfix_EncHistMulti(streamdata, index_sQQ, WebRtcIsacfix_kCdfShapePtr[0], KLT_ORDER_SHAPE);
  if (status < 0) {
    return status;
  }

  /* Save data for creation of multiple bitstreams */
  if (encData != NULL) {
    for (k=0; k<KLT_ORDER_SHAPE; k++)
    {
      encData->LPCindex_s[KLT_ORDER_SHAPE*encData->startIdx + k] = index_sQQ[k];
    }
  }
  /* save the state of the bitstream object 'streamdata' for the possible bit-rate reduction */
  transcodingParam->full         = streamdata->full;
  transcodingParam->stream_index = streamdata->stream_index;
  transcodingParam->streamval    = streamdata->streamval;
  transcodingParam->W_upper      = streamdata->W_upper;
  transcodingParam->beforeLastWord     = streamdata->stream[streamdata->stream_index-1];
  transcodingParam->lastWord     = streamdata->stream[streamdata->stream_index];

  /* entropy coding of index */
  status = WebRtcIsacfix_EncHistMulti(streamdata, index_gQQ, WebRtcIsacfix_kCdfGainPtr[0], KLT_ORDER_GAIN);
  if (status < 0) {
    return status;
  }

  /* find quantization levels for shape coefficients */
  for (k=0; k<KLT_ORDER_SHAPE; k++) {
    tmpcoeffs_sQ17[WebRtcIsacfix_kSelIndShape[k]] = WEBRTC_SPL_MUL(128, WebRtcIsacfix_kLevelsShapeQ10[WebRtcIsacfix_kOfLevelsShape[0]+index_ovr_sQQ[k]]);

  }
  /* inverse KLT  */

  /* left transform */  // Transpose matrix!
  WebRtcIsacfix_MatrixProduct1(WebRtcIsacfix_kT1ShapeQ15[0], tmpcoeffs_sQ17,
      tmpcoeffs2_sQ17, kTIndexFactor4, kTIndexFactor4, kTInitCase0,
      kTIndexStep1, kTIndexStep1, kTLoopCount3, kTLoopCount3, kTMatrix1_shift1);

  /* right transform */ // Transpose matrix
  WebRtcIsacfix_MatrixProduct1(WebRtcIsacfix_kT2ShapeQ15[0], tmpcoeffs2_sQ17,
      tmpcoeffs_sQ17, kTIndexFactor1, kTIndexFactor1, kTInitCase1, kTIndexStep3,
      kTIndexStep2, kTLoopCount1, kTLoopCount3, kTMatrix1_shift1);

  /* scaling, mean addition, and gain restoration */
  poss = 0;pos=0;
  for (k=0; k<SUBFRAMES; k++) {

    /* lo band LAR coeffs */
    for (n=0; n<ORDERLO; n++, pos++, poss++) {
      tmp32 = WEBRTC_SPL_MUL_16_32_RSFT16(31208, tmpcoeffs_sQ17[poss]); // (Q16*Q17)>>16 = Q17, with 1/2.1 = 0.47619047619 ~= 31208 in Q16
      tmp32 = tmp32 + WebRtcIsacfix_kMeansShapeQ17[0][poss]; // Q17+Q17 = Q17
      LPCCoefQ17[pos] = tmp32;
    }

    /* hi band LAR coeffs */
    for (n=0; n<ORDERHI; n++, pos++, poss++) {
      tmp32 = WEBRTC_SPL_LSHIFT_W32(WEBRTC_SPL_MUL_16_32_RSFT16(18204, tmpcoeffs_sQ17[poss]), 3); // ((Q13*Q17)>>16)<<3 = Q17, with 1/0.45 = 2.222222222222 ~= 18204 in Q13
      tmp32 = tmp32 + WebRtcIsacfix_kMeansShapeQ17[0][poss]; // Q17+Q17 = Q17
      LPCCoefQ17[pos] = tmp32;
    }

  }

  //to update tmpcoeffs_gQ17 to the proper state
  for (k=0; k<KLT_ORDER_GAIN; k++) {
    tmpcoeffs_gQ17[WebRtcIsacfix_kSelIndGain[k]] = WebRtcIsacfix_kLevelsGainQ17[WebRtcIsacfix_kOfLevelsGain[0]+index_ovr_gQQ[k]];
  }



  /* find quantization levels for coefficients */

  /* left transform */
  offsg = 0;
  posg = 0;
  for (j=0; j<SUBFRAMES; j++) {
    // (Q15 * Q17) >> (16 - 1) = Q17; Q17 << 4 = Q21.
    sumQQ = (WEBRTC_SPL_MUL_16_32_RSFT16(WebRtcIsacfix_kT1GainQ15[0][0],
                                         tmpcoeffs_gQ17[offsg]) << 1);
    sumQQ += (WEBRTC_SPL_MUL_16_32_RSFT16(WebRtcIsacfix_kT1GainQ15[0][1],
                                          tmpcoeffs_gQ17[offsg + 1]) << 1);
    tmpcoeffs2_gQ21[posg] = WEBRTC_SPL_LSHIFT_W32(sumQQ, 4);
    posg++;

    sumQQ = (WEBRTC_SPL_MUL_16_32_RSFT16(WebRtcIsacfix_kT1GainQ15[0][2],
                                         tmpcoeffs_gQ17[offsg]) << 1);
    sumQQ += (WEBRTC_SPL_MUL_16_32_RSFT16(WebRtcIsacfix_kT1GainQ15[0][3],
                                          tmpcoeffs_gQ17[offsg + 1]) << 1);
    tmpcoeffs2_gQ21[posg] = WEBRTC_SPL_LSHIFT_W32(sumQQ, 4);
    posg++;
    offsg += 2;
  }

  /* right transform */ // Transpose matrix
  WebRtcIsacfix_MatrixProduct2(WebRtcIsacfix_kT2GainQ15[0], tmpcoeffs2_gQ21,
                               tmpcoeffs_gQ17, kTIndexFactor1, kTIndexStep2);

  /* scaling, mean addition, and gain restoration */
  posg = 0;
  gainpos = 0;
  for (k=0; k<2*SUBFRAMES; k++) {

    // Divide by 4 and get Q17 to Q8, i.e. shift 2+9.
    sumQQ16 = (int16_t)(tmpcoeffs_gQ17[posg] >> 11);
    sumQQ16 += WebRtcIsacfix_kMeansGainQ8[0][posg];
    sumQQ = CalcExpN(sumQQ16); // Q8 in and Q17 out
    gain_lo_hiQ17[gainpos] = sumQQ; //Q17

    gainpos++;
    pos++;posg++;
  }

  return 0;
}

int WebRtcIsacfix_EstCodeLpcGain(int32_t *gain_lo_hiQ17,
                                 Bitstr_enc *streamdata,
                                 IsacSaveEncoderData* encData) {
  int j, k;
  int16_t posQQ, pos2QQ, gainpos;
  int16_t posg;
  int16_t index_gQQ[KLT_ORDER_GAIN];

  int16_t tmpcoeffs_gQ6[KLT_ORDER_GAIN];
  int32_t tmpcoeffs_gQ17[KLT_ORDER_GAIN];
  int32_t tmpcoeffs2_gQ21[KLT_ORDER_GAIN];
  int32_t sumQQ;
  int status = 0;

  /* write LAR coefficients to statistics file */
  /* Save data for creation of multiple bitstreams (and transcoding) */
  if (encData != NULL) {
    for (k=0; k<KLT_ORDER_GAIN; k++) {
      encData->LPCcoeffs_g[KLT_ORDER_GAIN*encData->startIdx + k] = gain_lo_hiQ17[k];
    }
  }

  /* log gains, mean removal and scaling */
  posg = 0; gainpos = 0;

  for (k=0; k<SUBFRAMES; k++) {
    /* log gains */

    /* The input argument X to logN(X) is 2^17 times higher than the
       input floating point argument Y to log(Y), since the X value
       is a Q17 value. This can be compensated for after the call, by
       subraction a value Z for each Q-step. One Q-step means that
       X gets 2 times higher, i.e. Z = logN(2)*256 = 0.693147180559*256 =
       177.445678 should be subtracted (since logN() returns a Q8 value).
       For a X value in Q17, the value 177.445678*17 = 3017 should be
       subtracted */
    tmpcoeffs_gQ6[posg] = CalcLogN(gain_lo_hiQ17[gainpos])-3017; //Q8
    tmpcoeffs_gQ6[posg] -= WebRtcIsacfix_kMeansGainQ8[0][posg]; //Q8, but Q6 after not-needed mult. by 4
    posg++; gainpos++;

    tmpcoeffs_gQ6[posg] = CalcLogN(gain_lo_hiQ17[gainpos])-3017; //Q8
    tmpcoeffs_gQ6[posg] -= WebRtcIsacfix_kMeansGainQ8[0][posg]; //Q8, but Q6 after not-needed mult. by 4
    posg++; gainpos++;
  }


  /* KLT  */

  /* left transform */
  posg = 0;
  for (j=0; j<SUBFRAMES; j++) {
      // Q21 = Q6 * Q15
      sumQQ = WEBRTC_SPL_MUL_16_16(tmpcoeffs_gQ6[j * 2],
                                   WebRtcIsacfix_kT1GainQ15[0][0]);
      sumQQ += WEBRTC_SPL_MUL_16_16(tmpcoeffs_gQ6[j * 2 + 1],
                                    WebRtcIsacfix_kT1GainQ15[0][2]);
      tmpcoeffs2_gQ21[posg] = sumQQ;
      posg++;

      sumQQ = WEBRTC_SPL_MUL_16_16(tmpcoeffs_gQ6[j * 2],
                                   WebRtcIsacfix_kT1GainQ15[0][1]);
      sumQQ += WEBRTC_SPL_MUL_16_16(tmpcoeffs_gQ6[j * 2 + 1],
                                    WebRtcIsacfix_kT1GainQ15[0][3]);
      tmpcoeffs2_gQ21[posg] = sumQQ;
      posg++;
  }

  /* right transform */
  WebRtcIsacfix_MatrixProduct2(WebRtcIsacfix_kT2GainQ15[0], tmpcoeffs2_gQ21,
                               tmpcoeffs_gQ17, kTIndexFactor3, kTIndexStep1);

  /* quantize coefficients */

  for (k=0; k<KLT_ORDER_GAIN; k++) //ATTN: ok?
  {
    posQQ = WebRtcIsacfix_kSelIndGain[k];
    pos2QQ= (int16_t)CalcLrIntQ(tmpcoeffs_gQ17[posQQ], 17);

    index_gQQ[k] = pos2QQ + WebRtcIsacfix_kQuantMinGain[k]; //ATTN: ok?
    if (index_gQQ[k] < 0) {
      index_gQQ[k] = 0;
    }
    else if (index_gQQ[k] > WebRtcIsacfix_kMaxIndGain[k]) {
      index_gQQ[k] = WebRtcIsacfix_kMaxIndGain[k];
    }

    /* Save data for creation of multiple bitstreams */
    if (encData != NULL) {
      encData->LPCindex_g[KLT_ORDER_GAIN*encData->startIdx + k] = index_gQQ[k];
    }
  }

  /* entropy coding of index */
  status = WebRtcIsacfix_EncHistMulti(streamdata, index_gQQ, WebRtcIsacfix_kCdfGainPtr[0], KLT_ORDER_GAIN);
  if (status < 0) {
    return status;
  }

  return 0;
}


int WebRtcIsacfix_EncodeLpc(int32_t *gain_lo_hiQ17,
                            int16_t *LPCCoef_loQ15,
                            int16_t *LPCCoef_hiQ15,
                            int16_t *model,
                            int32_t *sizeQ11,
                            Bitstr_enc *streamdata,
                            IsacSaveEncoderData* encData,
                            transcode_obj *transcodeParam)
{
  int status = 0;
  int32_t larsQ17[KLT_ORDER_SHAPE]; // KLT_ORDER_SHAPE == (ORDERLO+ORDERHI)*SUBFRAMES
  // = (6+12)*6 == 108

  Poly2LarFix(LPCCoef_loQ15, ORDERLO, LPCCoef_hiQ15, ORDERHI, SUBFRAMES, larsQ17);

  status = EstCodeLpcCoef(larsQ17, gain_lo_hiQ17, model, sizeQ11,
                          streamdata, encData, transcodeParam);
  if (status < 0) {
    return (status);
  }

  Lar2polyFix(larsQ17, LPCCoef_loQ15, ORDERLO, LPCCoef_hiQ15, ORDERHI, SUBFRAMES);

  return 0;
}


/* decode & dequantize RC */
int WebRtcIsacfix_DecodeRcCoef(Bitstr_dec *streamdata, int16_t *RCQ15)
{
  int k, err;
  int16_t index[AR_ORDER];

  /* entropy decoding of quantization indices */
  err = WebRtcIsacfix_DecHistOneStepMulti(index, streamdata, WebRtcIsacfix_kRcCdfPtr, WebRtcIsacfix_kRcInitInd, AR_ORDER);
  if (err<0)  // error check
    return err;

  /* find quantization levels for reflection coefficients */
  for (k=0; k<AR_ORDER; k++)
  {
    RCQ15[k] = *(WebRtcIsacfix_kRcLevPtr[k] + index[k]);
  }

  return 0;
}



/* quantize & code RC */
int WebRtcIsacfix_EncodeRcCoef(int16_t *RCQ15, Bitstr_enc *streamdata)
{
  int k;
  int16_t index[AR_ORDER];
  int status;

  /* quantize reflection coefficients (add noise feedback?) */
  for (k=0; k<AR_ORDER; k++)
  {
    index[k] = WebRtcIsacfix_kRcInitInd[k];

    if (RCQ15[k] > WebRtcIsacfix_kRcBound[index[k]])
    {
      while (RCQ15[k] > WebRtcIsacfix_kRcBound[index[k] + 1])
        index[k]++;
    }
    else
    {
      while (RCQ15[k] < WebRtcIsacfix_kRcBound[--index[k]]) ;
    }

    RCQ15[k] = *(WebRtcIsacfix_kRcLevPtr[k] + index[k]);
  }


  /* entropy coding of quantization indices */
  status = WebRtcIsacfix_EncHistMulti(streamdata, index, WebRtcIsacfix_kRcCdfPtr, AR_ORDER);

  /* If error in WebRtcIsacfix_EncHistMulti(), status will be negative, otherwise 0 */
  return status;
}


/* decode & dequantize squared Gain */
int WebRtcIsacfix_DecodeGain2(Bitstr_dec *streamdata, int32_t *gainQ10)
{
  int err;
  int16_t index;

  /* entropy decoding of quantization index */
  err = WebRtcIsacfix_DecHistOneStepMulti(
      &index,
      streamdata,
      WebRtcIsacfix_kGainPtr,
      WebRtcIsacfix_kGainInitInd,
      1);
  /* error check */
  if (err<0) {
    return err;
  }

  /* find quantization level */
  *gainQ10 = WebRtcIsacfix_kGain2Lev[index];

  return 0;
}



/* quantize & code squared Gain */
int WebRtcIsacfix_EncodeGain2(int32_t *gainQ10, Bitstr_enc *streamdata)
{
  int16_t index;
  int status = 0;

  /* find quantization index */
  index = WebRtcIsacfix_kGainInitInd[0];
  if (*gainQ10 > WebRtcIsacfix_kGain2Bound[index])
  {
    while (*gainQ10 > WebRtcIsacfix_kGain2Bound[index + 1])
      index++;
  }
  else
  {
    while (*gainQ10 < WebRtcIsacfix_kGain2Bound[--index]) ;
  }

  /* dequantize */
  *gainQ10 = WebRtcIsacfix_kGain2Lev[index];

  /* entropy coding of quantization index */
  status = WebRtcIsacfix_EncHistMulti(streamdata, &index, WebRtcIsacfix_kGainPtr, 1);

  /* If error in WebRtcIsacfix_EncHistMulti(), status will be negative, otherwise 0 */
  return status;
}


/* code and decode Pitch Gains and Lags functions */

/* decode & dequantize Pitch Gains */
int WebRtcIsacfix_DecodePitchGain(Bitstr_dec *streamdata, int16_t *PitchGains_Q12)
{
  int err;
  int16_t index_comb;
  const uint16_t *pitch_gain_cdf_ptr[1];

  /* entropy decoding of quantization indices */
  *pitch_gain_cdf_ptr = WebRtcIsacfix_kPitchGainCdf;
  err = WebRtcIsacfix_DecHistBisectMulti(&index_comb, streamdata, pitch_gain_cdf_ptr, WebRtcIsacfix_kCdfTableSizeGain, 1);
  /* error check, Q_mean_Gain.. tables are of size 144 */
  if ((err < 0) || (index_comb < 0) || (index_comb >= 144))
    return -ISAC_RANGE_ERROR_DECODE_PITCH_GAIN;

  /* unquantize back to pitch gains by table look-up */
  PitchGains_Q12[0] = WebRtcIsacfix_kPitchGain1[index_comb];
  PitchGains_Q12[1] = WebRtcIsacfix_kPitchGain2[index_comb];
  PitchGains_Q12[2] = WebRtcIsacfix_kPitchGain3[index_comb];
  PitchGains_Q12[3] = WebRtcIsacfix_kPitchGain4[index_comb];

  return 0;
}


/* quantize & code Pitch Gains */
int WebRtcIsacfix_EncodePitchGain(int16_t* PitchGains_Q12,
                                  Bitstr_enc* streamdata,
                                  IsacSaveEncoderData* encData) {
  int k,j;
  int16_t SQ15[PITCH_SUBFRAMES];
  int16_t index[3];
  int16_t index_comb;
  const uint16_t *pitch_gain_cdf_ptr[1];
  int32_t CQ17;
  int status = 0;


  /* get the approximate arcsine (almost linear)*/
  for (k=0; k<PITCH_SUBFRAMES; k++)
    SQ15[k] = (int16_t) WEBRTC_SPL_MUL_16_16_RSFT(PitchGains_Q12[k],33,2); //Q15


  /* find quantization index; only for the first three transform coefficients */
  for (k=0; k<3; k++)
  {
    /*  transform */
    CQ17=0;
    for (j=0; j<PITCH_SUBFRAMES; j++) {
      CQ17 += WEBRTC_SPL_MUL_16_16_RSFT(WebRtcIsacfix_kTransform[k][j], SQ15[j],10); // Q17
    }

    index[k] = (int16_t)((CQ17 + 8192)>>14); // Rounding and scaling with stepsize (=1/0.125=8)

    /* check that the index is not outside the boundaries of the table */
    if (index[k] < WebRtcIsacfix_kLowerlimiGain[k]) index[k] = WebRtcIsacfix_kLowerlimiGain[k];
    else if (index[k] > WebRtcIsacfix_kUpperlimitGain[k]) index[k] = WebRtcIsacfix_kUpperlimitGain[k];
    index[k] -= WebRtcIsacfix_kLowerlimiGain[k];
  }

  /* calculate unique overall index */
  index_comb = (int16_t)(WEBRTC_SPL_MUL(WebRtcIsacfix_kMultsGain[0], index[0]) +
                               WEBRTC_SPL_MUL(WebRtcIsacfix_kMultsGain[1], index[1]) + index[2]);

  /* unquantize back to pitch gains by table look-up */
  // (Y)
  PitchGains_Q12[0] = WebRtcIsacfix_kPitchGain1[index_comb];
  PitchGains_Q12[1] = WebRtcIsacfix_kPitchGain2[index_comb];
  PitchGains_Q12[2] = WebRtcIsacfix_kPitchGain3[index_comb];
  PitchGains_Q12[3] = WebRtcIsacfix_kPitchGain4[index_comb];


  /* entropy coding of quantization pitch gains */
  *pitch_gain_cdf_ptr = WebRtcIsacfix_kPitchGainCdf;
  status = WebRtcIsacfix_EncHistMulti(streamdata, &index_comb, pitch_gain_cdf_ptr, 1);
  if (status < 0) {
    return status;
  }

  /* Save data for creation of multiple bitstreams */
  if (encData != NULL) {
    encData->pitchGain_index[encData->startIdx] = index_comb;
  }

  return 0;
}



/* Pitch LAG */


/* decode & dequantize Pitch Lags */
int WebRtcIsacfix_DecodePitchLag(Bitstr_dec *streamdata,
                                 int16_t *PitchGain_Q12,
                                 int16_t *PitchLags_Q7)
{
  int k, err;
  int16_t index[PITCH_SUBFRAMES];
  const int16_t *mean_val2Q10, *mean_val4Q10;

  const int16_t *lower_limit;
  const uint16_t *init_index;
  const uint16_t *cdf_size;
  const uint16_t **cdf;

  int32_t meangainQ12;
  int32_t CQ11, CQ10,tmp32a,tmp32b;
  int16_t shft,tmp16a,tmp16c;

  meangainQ12=0;
  for (k = 0; k < 4; k++)
    meangainQ12 += PitchGain_Q12[k];

  meangainQ12 >>= 2;  // Get average.

  /* voicing classificiation */
  if (meangainQ12 <= 819) {                 // mean_gain < 0.2
    shft = -1;        // StepSize=2.0;
    cdf = WebRtcIsacfix_kPitchLagPtrLo;
    cdf_size = WebRtcIsacfix_kPitchLagSizeLo;
    mean_val2Q10 = WebRtcIsacfix_kMeanLag2Lo;
    mean_val4Q10 = WebRtcIsacfix_kMeanLag4Lo;
    lower_limit = WebRtcIsacfix_kLowerLimitLo;
    init_index = WebRtcIsacfix_kInitIndLo;
  } else if (meangainQ12 <= 1638) {            // mean_gain < 0.4
    shft = 0;        // StepSize=1.0;
    cdf = WebRtcIsacfix_kPitchLagPtrMid;
    cdf_size = WebRtcIsacfix_kPitchLagSizeMid;
    mean_val2Q10 = WebRtcIsacfix_kMeanLag2Mid;
    mean_val4Q10 = WebRtcIsacfix_kMeanLag4Mid;
    lower_limit = WebRtcIsacfix_kLowerLimitMid;
    init_index = WebRtcIsacfix_kInitIndMid;
  } else {
    shft = 1;        // StepSize=0.5;
    cdf = WebRtcIsacfix_kPitchLagPtrHi;
    cdf_size = WebRtcIsacfix_kPitchLagSizeHi;
    mean_val2Q10 = WebRtcIsacfix_kMeanLag2Hi;
    mean_val4Q10 = WebRtcIsacfix_kMeanLag4Hi;
    lower_limit = WebRtcIsacfix_kLowerLimitHi;
    init_index = WebRtcIsacfix_kInitIndHi;
  }

  /* entropy decoding of quantization indices */
  err = WebRtcIsacfix_DecHistBisectMulti(index, streamdata, cdf, cdf_size, 1);
  if ((err<0) || (index[0]<0))  // error check
    return -ISAC_RANGE_ERROR_DECODE_PITCH_LAG;

  err = WebRtcIsacfix_DecHistOneStepMulti(index+1, streamdata, cdf+1, init_index, 3);
  if (err<0)  // error check
    return -ISAC_RANGE_ERROR_DECODE_PITCH_LAG;


  /* unquantize back to transform coefficients and do the inverse transform: S = T'*C */
  CQ11 = ((int32_t)index[0] + lower_limit[0]);  // Q0
  CQ11 = WEBRTC_SPL_SHIFT_W32(CQ11,11-shft); // Scale with StepSize, Q11
  for (k=0; k<PITCH_SUBFRAMES; k++) {
    tmp32a =  WEBRTC_SPL_MUL_16_32_RSFT11(WebRtcIsacfix_kTransform[0][k], CQ11);
    tmp16a = (int16_t)(tmp32a >> 5);
    PitchLags_Q7[k] = tmp16a;
  }

  CQ10 = mean_val2Q10[index[1]];
  for (k=0; k<PITCH_SUBFRAMES; k++) {
    tmp32b =  (int32_t) WEBRTC_SPL_MUL_16_16_RSFT((int16_t) WebRtcIsacfix_kTransform[1][k], (int16_t) CQ10,10);
    tmp16c = (int16_t)(tmp32b >> 5);
    PitchLags_Q7[k] += tmp16c;
  }

  CQ10 = mean_val4Q10[index[3]];
  for (k=0; k<PITCH_SUBFRAMES; k++) {
    tmp32b =  (int32_t) WEBRTC_SPL_MUL_16_16_RSFT((int16_t) WebRtcIsacfix_kTransform[3][k], (int16_t) CQ10,10);
    tmp16c = (int16_t)(tmp32b >> 5);
    PitchLags_Q7[k] += tmp16c;
  }

  return 0;
}



/* quantize & code Pitch Lags */
int WebRtcIsacfix_EncodePitchLag(int16_t* PitchLagsQ7,
                                 int16_t* PitchGain_Q12,
                                 Bitstr_enc* streamdata,
                                 IsacSaveEncoderData* encData) {
  int k, j;
  int16_t index[PITCH_SUBFRAMES];
  int32_t meangainQ12, CQ17;
  int32_t CQ11, CQ10,tmp32a;

  const int16_t *mean_val2Q10,*mean_val4Q10;
  const int16_t *lower_limit, *upper_limit;
  const uint16_t **cdf;
  int16_t shft, tmp16a, tmp16b, tmp16c;
  int32_t tmp32b;
  int status = 0;

  /* compute mean pitch gain */
  meangainQ12=0;
  for (k = 0; k < 4; k++)
    meangainQ12 += PitchGain_Q12[k];

  meangainQ12 >>= 2;

  /* Save data for creation of multiple bitstreams */
  if (encData != NULL) {
    encData->meanGain[encData->startIdx] = meangainQ12;
  }

  /* voicing classificiation */
  if (meangainQ12 <= 819) {                 // mean_gain < 0.2
    shft = -1;        // StepSize=2.0;
    cdf = WebRtcIsacfix_kPitchLagPtrLo;
    mean_val2Q10 = WebRtcIsacfix_kMeanLag2Lo;
    mean_val4Q10 = WebRtcIsacfix_kMeanLag4Lo;
    lower_limit = WebRtcIsacfix_kLowerLimitLo;
    upper_limit = WebRtcIsacfix_kUpperLimitLo;
  } else if (meangainQ12 <= 1638) {            // mean_gain < 0.4
    shft = 0;        // StepSize=1.0;
    cdf = WebRtcIsacfix_kPitchLagPtrMid;
    mean_val2Q10 = WebRtcIsacfix_kMeanLag2Mid;
    mean_val4Q10 = WebRtcIsacfix_kMeanLag4Mid;
    lower_limit = WebRtcIsacfix_kLowerLimitMid;
    upper_limit = WebRtcIsacfix_kUpperLimitMid;
  } else {
    shft = 1;        // StepSize=0.5;
    cdf = WebRtcIsacfix_kPitchLagPtrHi;
    mean_val2Q10 = WebRtcIsacfix_kMeanLag2Hi;
    mean_val4Q10 = WebRtcIsacfix_kMeanLag4Hi;
    lower_limit = WebRtcIsacfix_kLowerLimitHi;
    upper_limit = WebRtcIsacfix_kUpperLimitHi;
  }

  /* find quantization index */
  for (k=0; k<4; k++)
  {
    /*  transform */
    CQ17=0;
    for (j=0; j<PITCH_SUBFRAMES; j++)
      CQ17 += WEBRTC_SPL_MUL_16_16_RSFT(WebRtcIsacfix_kTransform[k][j], PitchLagsQ7[j],2); // Q17

    CQ17 = WEBRTC_SPL_SHIFT_W32(CQ17,shft); // Scale with StepSize

    /* quantize */
    tmp16b = (int16_t)((CQ17 + 65536) >> 17);
    index[k] =  tmp16b;

    /* check that the index is not outside the boundaries of the table */
    if (index[k] < lower_limit[k]) index[k] = lower_limit[k];
    else if (index[k] > upper_limit[k]) index[k] = upper_limit[k];
    index[k] -= lower_limit[k];

    /* Save data for creation of multiple bitstreams */
    if(encData != NULL) {
      encData->pitchIndex[PITCH_SUBFRAMES*encData->startIdx + k] = index[k];
    }
  }

  /* unquantize back to transform coefficients and do the inverse transform: S = T'*C */
  CQ11 = (index[0] + lower_limit[0]);  // Q0
  CQ11 = WEBRTC_SPL_SHIFT_W32(CQ11,11-shft); // Scale with StepSize, Q11

  for (k=0; k<PITCH_SUBFRAMES; k++) {
    tmp32a =  WEBRTC_SPL_MUL_16_32_RSFT11(WebRtcIsacfix_kTransform[0][k], CQ11); // Q12
    tmp16a = (int16_t)(tmp32a >> 5);  // Q7.
    PitchLagsQ7[k] = tmp16a;
  }

  CQ10 = mean_val2Q10[index[1]];
  for (k=0; k<PITCH_SUBFRAMES; k++) {
    tmp32b =  (int32_t) WEBRTC_SPL_MUL_16_16_RSFT((int16_t) WebRtcIsacfix_kTransform[1][k], (int16_t) CQ10,10);
    tmp16c = (int16_t)(tmp32b >> 5);  // Q7.
    PitchLagsQ7[k] += tmp16c;
  }

  CQ10 = mean_val4Q10[index[3]];
  for (k=0; k<PITCH_SUBFRAMES; k++) {
    tmp32b =  (int32_t) WEBRTC_SPL_MUL_16_16_RSFT((int16_t) WebRtcIsacfix_kTransform[3][k], (int16_t) CQ10,10);
    tmp16c = (int16_t)(tmp32b >> 5);  // Q7.
    PitchLagsQ7[k] += tmp16c;
  }

  /* entropy coding of quantization pitch lags */
  status = WebRtcIsacfix_EncHistMulti(streamdata, index, cdf, PITCH_SUBFRAMES);

  /* If error in WebRtcIsacfix_EncHistMulti(), status will be negative, otherwise 0 */
  return status;
}



/* Routines for inband signaling of bandwitdh estimation */
/* Histograms based on uniform distribution of indices */
/* Move global variables later! */


/* cdf array for frame length indicator */
const uint16_t kFrameLenCdf[4] = {
  0, 21845, 43690, 65535};

/* pointer to cdf array for frame length indicator */
const uint16_t *kFrameLenCdfPtr[1] = {kFrameLenCdf};

/* initial cdf index for decoder of frame length indicator */
const uint16_t kFrameLenInitIndex[1] = {1};


int WebRtcIsacfix_DecodeFrameLen(Bitstr_dec *streamdata,
                                 int16_t *framesamples)
{

  int err;
  int16_t frame_mode;

  err = 0;
  /* entropy decoding of frame length [1:30ms,2:60ms] */
  err = WebRtcIsacfix_DecHistOneStepMulti(&frame_mode, streamdata, kFrameLenCdfPtr, kFrameLenInitIndex, 1);
  if (err<0)  // error check
    return -ISAC_RANGE_ERROR_DECODE_FRAME_LENGTH;

  switch(frame_mode) {
    case 1:
      *framesamples = 480; /* 30ms */
      break;
    case 2:
      *framesamples = 960; /* 60ms */
      break;
    default:
      err = -ISAC_DISALLOWED_FRAME_MODE_DECODER;
  }

  return err;
}


int WebRtcIsacfix_EncodeFrameLen(int16_t framesamples, Bitstr_enc *streamdata) {

  int status;
  int16_t frame_mode;

  status = 0;
  frame_mode = 0;
  /* entropy coding of frame length [1:480 samples,2:960 samples] */
  switch(framesamples) {
    case 480:
      frame_mode = 1;
      break;
    case 960:
      frame_mode = 2;
      break;
    default:
      status = - ISAC_DISALLOWED_FRAME_MODE_ENCODER;
  }

  if (status < 0)
    return status;

  status = WebRtcIsacfix_EncHistMulti(streamdata, &frame_mode, kFrameLenCdfPtr, 1);

  return status;
}

/* cdf array for estimated bandwidth */
const uint16_t kBwCdf[25] = {
  0, 2731, 5461, 8192, 10923, 13653, 16384, 19114, 21845, 24576, 27306, 30037,
  32768, 35498, 38229, 40959, 43690, 46421, 49151, 51882, 54613, 57343, 60074,
  62804, 65535};

/* pointer to cdf array for estimated bandwidth */
const uint16_t *kBwCdfPtr[1] = {kBwCdf};

/* initial cdf index for decoder of estimated bandwidth*/
const uint16_t kBwInitIndex[1] = {7};


int WebRtcIsacfix_DecodeSendBandwidth(Bitstr_dec *streamdata, int16_t *BWno) {

  int err;
  int16_t BWno32;

  /* entropy decoding of sender's BW estimation [0..23] */
  err = WebRtcIsacfix_DecHistOneStepMulti(&BWno32, streamdata, kBwCdfPtr, kBwInitIndex, 1);
  if (err<0)  // error check
    return -ISAC_RANGE_ERROR_DECODE_BANDWIDTH;
  *BWno = (int16_t)BWno32;
  return err;

}


int WebRtcIsacfix_EncodeReceiveBandwidth(int16_t *BWno, Bitstr_enc *streamdata)
{
  int status = 0;
  /* entropy encoding of receiver's BW estimation [0..23] */
  status = WebRtcIsacfix_EncHistMulti(streamdata, BWno, kBwCdfPtr, 1);

  return status;
}

/* estimate codel length of LPC Coef */
void WebRtcIsacfix_TranscodeLpcCoef(int32_t *gain_lo_hiQ17,
                                    int16_t *index_gQQ) {
  int j, k;
  int16_t posQQ, pos2QQ;
  int16_t posg, offsg, gainpos;
  int32_t tmpcoeffs_gQ6[KLT_ORDER_GAIN];
  int32_t tmpcoeffs_gQ17[KLT_ORDER_GAIN];
  int32_t tmpcoeffs2_gQ21[KLT_ORDER_GAIN];
  int32_t sumQQ;


  /* log gains, mean removal and scaling */
  posg = 0; gainpos=0;

  for (k=0; k<SUBFRAMES; k++) {
    /* log gains */

    /* The input argument X to logN(X) is 2^17 times higher than the
       input floating point argument Y to log(Y), since the X value
       is a Q17 value. This can be compensated for after the call, by
       subraction a value Z for each Q-step. One Q-step means that
       X gets 2 times higher, i.e. Z = logN(2)*256 = 0.693147180559*256 =
       177.445678 should be subtracted (since logN() returns a Q8 value).
       For a X value in Q17, the value 177.445678*17 = 3017 should be
       subtracted */
    tmpcoeffs_gQ6[posg] = CalcLogN(gain_lo_hiQ17[gainpos])-3017; //Q8
    tmpcoeffs_gQ6[posg] -= WebRtcIsacfix_kMeansGainQ8[0][posg]; //Q8, but Q6 after not-needed mult. by 4
    posg++; gainpos++;

    tmpcoeffs_gQ6[posg] = CalcLogN(gain_lo_hiQ17[gainpos])-3017; //Q8
    tmpcoeffs_gQ6[posg] -= WebRtcIsacfix_kMeansGainQ8[0][posg]; //Q8, but Q6 after not-needed mult. by 4
    posg++; gainpos++;

  }


  /* KLT  */

  /* left transform */
  for (j = 0, offsg = 0; j < SUBFRAMES; j++, offsg += 2) {
    // Q21 = Q6 * Q15
    sumQQ = WEBRTC_SPL_MUL_16_16(tmpcoeffs_gQ6[offsg],
                                 WebRtcIsacfix_kT1GainQ15[0][0]);
    sumQQ += WEBRTC_SPL_MUL_16_16(tmpcoeffs_gQ6[offsg + 1],
                                  WebRtcIsacfix_kT1GainQ15[0][2]);
    tmpcoeffs2_gQ21[offsg] = sumQQ;

    // Q21 = Q6 * Q15
    sumQQ = WEBRTC_SPL_MUL_16_16(tmpcoeffs_gQ6[offsg],
                                 WebRtcIsacfix_kT1GainQ15[0][1]);
    sumQQ += WEBRTC_SPL_MUL_16_16(tmpcoeffs_gQ6[offsg + 1],
                                  WebRtcIsacfix_kT1GainQ15[0][3]);
    tmpcoeffs2_gQ21[offsg + 1] = sumQQ;
  }

  /* right transform */
  WebRtcIsacfix_MatrixProduct2(WebRtcIsacfix_kT2GainQ15[0], tmpcoeffs2_gQ21,
                               tmpcoeffs_gQ17, kTIndexFactor3, kTIndexStep1);

  /* quantize coefficients */
  for (k=0; k<KLT_ORDER_GAIN; k++) //ATTN: ok?
  {
    posQQ = WebRtcIsacfix_kSelIndGain[k];
    pos2QQ= (int16_t)CalcLrIntQ(tmpcoeffs_gQ17[posQQ], 17);

    index_gQQ[k] = pos2QQ + WebRtcIsacfix_kQuantMinGain[k]; //ATTN: ok?
    if (index_gQQ[k] < 0) {
      index_gQQ[k] = 0;
    }
    else if (index_gQQ[k] > WebRtcIsacfix_kMaxIndGain[k]) {
      index_gQQ[k] = WebRtcIsacfix_kMaxIndGain[k];
    }
  }
}
