/*
 * Copyright (c) 2011-2019 Belledonne Communications SARL.
 *
 * This file is part of bcg729.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

#include "typedef.h"
#include "basicOperationsMacros.h"
#include "codecParameters.h"
#include "g729FixedPointMath.h"
#include "codebooks.h"

/*****************************************************************************/
/* insertionSort : sort an array in growing order using insertion algorithm  */
/*    parameters :                                                           */
/*      -(i/o) x: the array to be sorted                                     */
/*      -(i) length: the array length                                        */
/*                                                                           */
/*****************************************************************************/
void insertionSort(word16_t x[], int length) 
{
	int i, j;
	word16_t currentValue;

	for (i=1; i<length; i++) {
		currentValue = x[i];

		j = i-1;
		while ((j>=0) && (x[j]>currentValue)) {
			x[j+1] = x[j];
			j--;
		}

		x[j+1] = currentValue;
	}

	return;
}

/*****************************************************************************/
/* getMinInArray : get the minimum value from an array                       */
/*    parameters :                                                           */
/*      -(i) x: the array to be searched                                     */
/*      -(i) length: the array length                                        */
/*    returns : the minimum value found in the array                         */
/*                                                                           */
/*****************************************************************************/
word16_t getMinInArray(word16_t x[], int length) {
	word16_t min = MAXINT16;
	int i;
	for (i=0; i<length; i++) {
		if (x[i]<min) min = x[i];
	}
	return min;
}

/*****************************************************************************/
/* computeParity : compute parity for pitch delay adaptative codebook index  */
/*      XOR of the 6 MSB (pitchDelay on 8 bits)                              */
/*    parameters :                                                           */
/*      -(i) adaptativeCodebookIndex: the pitch delay on 8 bits              */
/*    return value :                                                         */
/*      the parity bit                                                       */
/*                                                                           */
/*****************************************************************************/
uint16_t computeParity(uint16_t adaptativeCodebookIndex)
{
	int i;
	uint16_t parity = 1;
	adaptativeCodebookIndex = SHR(adaptativeCodebookIndex,2); /* ignore the two LSB */

	for (i=0; i<6; i++) {
		parity ^= adaptativeCodebookIndex&(uint16_t)1; /* XOR with the LSB */
		adaptativeCodebookIndex = SHR(adaptativeCodebookIndex,1); 
	}

	return parity;
}

/*****************************************************************************/
/* rearrangeCoefficients: rearrange coefficients according to spec 3.2.4     */
/*      Have a minimum distance of J beetwen two consecutive coefficients    */
/*    parameters:                                                            */
/*      -(i/o) qLSP: 10 ordered coefficients in Q13 replaced by new values   */
/*             if needed                                                     */
/*      -(i) J: minimum distance between coefficients in Q0.13 (10 or 5)     */
/*                                                                           */
/*****************************************************************************/
void rearrangeCoefficients(word16_t qLSP[], word16_t J)
{ /* qLSP in Q2.13 and J in Q0.13(fitting on 4 bits: possible values 10 and 5) */
	int i=1;
	word16_t delta; /* in Q0.13 */

	for (i=1; i<NB_LSP_COEFF; i++) {
		delta = (ADD16(SUB16(qLSP[i-1],qLSP[i]),J))/2; /* delta = (l[i-1] - l[i] +J)/2 */
		if (delta > 0) {
			qLSP[i-1] = SUB16(qLSP[i-1], delta); /* qLSP still in Q2.13 */
			qLSP[i]   = ADD16(qLSP[i], delta);
		}
	}

	return;
}

/*****************************************************************************/
/* synthesisFilter : compute 1/[A(z)] using the following algorithm          */
/*      filteredSignal[n] = inputSignal[n]                                   */
/*                  - Sum(i=1..10)filterCoefficients[i]*filteredSignal[n-i]  */
/*      for n in [0, L_SUBFRAME[                                             */
/*    parameters:                                                            */
/*      -(i) inputSignal: 40 values in Q0                                    */
/*      -(i) filterCoefficients: 10 coefficients in Q12                      */
/*      -(i/o) filteredSignal: 50 values in Q0 accessed in ranges [-10,-1]   */
/*             as input and [0, 39] as output.                               */
/*                                                                           */
/*****************************************************************************/
void synthesisFilter(word16_t inputSignal[], word16_t filterCoefficients[], word16_t filteredSignal[])
{
	int i;
	for (i=0; i<L_SUBFRAME; i++) {
		word32_t acc = SSHL(inputSignal[i],12); /* acc get the first term of the sum, in Q12 (inputSignal is in Q0)*/
		int j;
		for (j=0; j<NB_LSP_COEFF; j++) {
			acc = MSU16_16(acc, filterCoefficients[j], filteredSignal[i-j-1]); /* filterCoefficients in Q12 and signal in Q0 -> acc in Q12 */
		}
		filteredSignal[i] = (word16_t)SATURATE(PSHR(acc, 12), MAXINT16); /* shift right acc to get it back in Q0 and check overflow on 16 bits */
	}

	return;
}

/*****************************************************************************/
/* correlateVectors : compute the correlations between two vectors of        */
/*      L_SUBFRAME length: c[i] = Sum(x[j]*y[j-i]) j in [i, L_SUBFRAME[      */
/*    parameters:                                                            */
/*      -(i) x : L_SUBFRAME length input vector on 16 bits                   */
/*      -(i) y : L_SUBFRAME length input vector on 16 bits                   */
/*      -(o) c : L_SUBFRAME length output vector on 32 bits                  */
/*                                                                           */
/*****************************************************************************/
void correlateVectors (word16_t x[], word16_t y[], word32_t c[])
{
	int i,j;
	for (i=0; i<L_SUBFRAME; i++) {
		c[i] = 0;
		for (j=i; j<L_SUBFRAME; j++) {
			c[i] = MAC16_16(c[i], x[j], y[j-i]);
		}
	}

	return;
}

/*** gain related functions ***/
/*****************************************************************************/
/* MACodeGainPrediction : spec 3.9.1                                         */
/*    parameters:                                                            */
/*      -(i) previousGainPredictionError: qU(m) in eq69 4 values in Q10      */
/*      -(i) fixedCodebookVector: the current subframe fixed codebook vector */
/*           40 values in Q1.13                                              */
/*    return value :                                                         */
/*      - predicted Fixed Codebook gain on 32 bits in Q16 range [3, 1830]    */
/*                                                                           */
/*****************************************************************************/
word32_t MACodeGainPrediction(word16_t *previousGainPredictionError, word16_t *fixedCodebookVector)
{

	/***** algorithm described in spec 3.9.1 *****/
	/*** first compute eq66: E = 10log10((Sum0-39(fixedCodebookVector^2))/40) ***/
	/*** directly used in eq71 in E| - E = 30 - 10log10((Sum0-39(fixedCodebookVector^2))/40) */
	/* = 30     -   10log10((Sum*2^26)/(40*2^26)) */
	/* = 30 + 10log10(40*2^26) - (10/log2(10))*log2(Sum*2^26) [The Sum is computed in Q26] */
	/* = 30 + 94.2884 -  3.0103*log2(Sum*2^26) */
	/* = 124.2884 - 3.0103*log2(Sum*2^26) = E| - E as used in eq71 */

	/* compute the sum of squares of fixedCodebookVector in Q26 */
	int i;
	word32_t fixedCodebookVectorSquaresSum = 0;
	word32_t acc;

	for (i=0; i<L_SUBFRAME; i++) {
		if (fixedCodebookVector[i] != 0) { /* as most of the codebook vector is egal to 0, it worth checking it to avoid useless multiplications */
 			/* fixedCodebookVector in Q1.13 and final sum in range [4, 8.48] (4 values filled with abs values: 1,1,1.8 and 1.8 in the vector give max value of sum) */
			/* fixedCodebookVectorSquaresSum is then in Q4.26 */
			fixedCodebookVectorSquaresSum = MAC16_16(fixedCodebookVectorSquaresSum, fixedCodebookVector[i], fixedCodebookVector[i]);
		}
	}

	/* compute E| - E as in eq71, result in Q16 */
	/* acc = 124.2884 - 3.0103*log2(Sum) */
	/* acc = 8145364[32 bits Q16] - 24660[16 bits Q2.13]*log2(Sum)[32 bits Q16] */
	acc = MAC16_32_Q13(8145364, -24660, g729Log2_Q0Q16(fixedCodebookVectorSquaresSum)); /* acc in Q16 */

	/* accumulate the MA prediction described in eq69 to the previous Sum, result will be in E~(m) + E| -E as used in eq71 */
	/* acc in Q16->Q24, previousGainPredictionError in Q10 and MAPredictionCoefficients in Q0.14*/
	acc = SHL(acc,8); /* acc in Q24 to match the fixed point of next accumulations */
	for (i=0; i<4; i++) {
		acc = MAC16_16(acc, previousGainPredictionError[i], MAPredictionCoefficients[i]); 
	}
	/* acc range [10, 65] -> Q6.24 */
	

	/* compute eq71, we already have the exposant in acc so */
	/* g'c = 10^(acc/20)                                    */
	/*     = 2^((acc*ln(10))/(20*ln(2)))                    */
	/*     = 2^(0,1661*acc)                                 */
	acc = SHR(acc,2); /* Q24->Q22 */
	acc = MULT16_32_Q15(5442, acc); /* 5442 is 0.1661 in Q15 -> acc now in Q4.22 range [1.6, 10.8] */
	acc = PSHR(acc,11); /* get acc in Q4.11 */

	return g729Exp2_Q11Q16((word16_t)acc); /* acc fits on 16 bits, cast it to word16_t to send it to the exp2 function, output in Q16*/
}


/*****************************************************************************/
/* computeGainPredictionError : apply eq72 to compute current fixed Codebook */
/*      gain prediction error and store the result in the adhoc array        */
/*    parameters :                                                           */
/*      -(i) fixedCodebookGainCorrectionFactor: gamma in eq72 in Q3.12       */
/*      -(i/o) previousGainPredictionError: array to be updated in Q10       */
/*                                                                           */
/*****************************************************************************/
void computeGainPredictionError(word16_t fixedCodebookGainCorrectionFactor, word16_t *previousGainPredictionError)
{
	/* need to compute eq72: 20log10(fixedCodebookGainCorrectionFactor) */
	/*  = (20/log2(10))*log2(fixedCodebookGainCorrectionFactor) */
	/*  = 6.0206*log2(fixedCodebookGainCorrectionFactor) */
	/* log2 input in Q0, output in Q16,fixedCodebookGainCorrectionFactor being in Q12, we shall substract 12 in Q16(786432) to the result of log2 function -> final result in Q2.16 */
	word32_t currentGainPredictionError = SUB32(g729Log2_Q0Q16(fixedCodebookGainCorrectionFactor), 786432); 
	currentGainPredictionError = PSHR(MULT16_32_Q12(24660, currentGainPredictionError),6); /* 24660 = 6.0206 in Q3.12 -> mult result in Q16, precise shift right to get it in Q4.10 */
	
	/* shift the array and insert the current Prediction Error */
	previousGainPredictionError[3] = previousGainPredictionError[2];
	previousGainPredictionError[2] = previousGainPredictionError[1];
	previousGainPredictionError[1] = previousGainPredictionError[0];
	previousGainPredictionError[0] = (word16_t)currentGainPredictionError;
}


/*** bitStream to parameters Array conversions functions ***/
/* Note: these functions are in utils.c because used by test source code too */

/*****************************************************************************/
/* parametersArray2BitStream : convert array of parameters to bitStream      */
/*      according to spec 4 - Table 8 and following mapping of values        */
/*               0 -> L0 (1 bit)                                             */
/*               1 -> L1 (7 bits)                                            */
/*               2 -> L2 (5 bits)                                            */
/*               3 -> L3 (5 bits)                                            */
/*               4 -> P1 (8 bit)                                             */
/*               5 -> P0 (1 bits)                                            */
/*               6 -> C1 (13 bits)                                           */
/*               7 -> S1 (4 bits)                                            */
/*               8 -> GA1(3 bits)                                            */
/*               9 -> GB1(4 bits)                                            */
/*               10 -> P2 (5 bits)                                           */
/*               11 -> C2 (13 bits)                                          */
/*               12 -> S2 (4 bits)                                           */
/*               13 -> GA2(3 bits)                                           */
/*               14 -> GB2(4 bits)                                           */
/*    parameters:                                                            */
/*      -(i) parameters : 16 values parameters array                         */
/*      -(o) bitStream : the 16 values streamed on 80 bits in a              */
/*           10*8bits values array                                           */
/*                                                                           */
/*****************************************************************************/
void parametersArray2BitStream(uint16_t parameters[], uint8_t bitStream[])
{
	bitStream[0] = ((parameters[0]&((uint16_t) 0x1))<<7) |
			(parameters[1]&((uint16_t) 0x7f)); 
	
	bitStream[1] = ((parameters[2]&((uint16_t) 0x1f))<<3) |
			((parameters[3]>>2)&((uint16_t) 0x7));

	bitStream[2] = ((parameters[3]&((uint16_t) 0x3))<<6) |
			((parameters[4]>>2)&((uint16_t) 0x3f));

	bitStream[3] = ((parameters[4]&((uint16_t) 0x3))<<6) |
			((parameters[5]&((uint16_t) 0x1))<<5) |
			((parameters[6]>>8)&((uint16_t) 0x1f));

	bitStream[4] = ((parameters[6])&((uint16_t) 0xff));

	bitStream[5] = ((parameters[7]&((uint16_t) 0xf))<<4) |
			((parameters[8]&((uint16_t) 0x7))<<1) |
			((parameters[9]>>3)&((uint16_t) 0x1));

	bitStream[6] = ((parameters[9]&((uint16_t) 0x7))<<5) |
			(parameters[10]&((uint16_t) 0x1f)); 

	bitStream[7] = ((parameters[11]>>5)&((uint16_t) 0xff));

	bitStream[8] = ((parameters[11]&((uint16_t) 0x1f))<<3) |
			((parameters[12]>>1)&((uint16_t) 0x7));

	bitStream[9] = ((parameters[12]&((uint16_t) 0x1))<<7) |
			((parameters[13]&((uint16_t) 0x7))<<4) |
			(parameters[14]&((uint16_t) 0xf));

	return;
}

/*****************************************************************************/
/* CNGparametersArray2BitStream : convert array of parameters to bitStream   */
/*      according to spec B4.3 - Table B2 and following mapping of values    */
/*               0 -> L0 (1 bit)                                             */
/*               1 -> L1 (5 bits)                                            */
/*               2 -> L2 (4 bits)                                            */
/*               3 -> Gain (5 bits)                                          */
/*    parameters:                                                            */
/*      -(i) parameters : 4 values parameters array                          */
/*      -(o) bitStream : the 4 values streamed on 15 bits in a               */
/*           2*8bits values array                                            */
/*                                                                           */
/*****************************************************************************/

void CNGparametersArray2BitStream(uint16_t parameters[], uint8_t bitStream[]) {
	bitStream[0] = ((parameters[0]&((uint16_t) 0x1))<<7) |
			((parameters[1]&((uint16_t) 0x1f))<<2) |
			((parameters[2]>>2)&((uint16_t) 0x3)); 
	
	bitStream[1] = ((parameters[2]&((uint16_t) 0x03))<<6) |
			((parameters[3]&((uint16_t) 0x1f))<<1);
}

/*****************************************************************************/
/* parametersArray2BitStream : convert bitStream to an array of parameters   */
/*             reverse operation of previous funtion                         */
/*    parameters:                                                            */
/*      -(i) bitStream : the 16 values streamed on 80 bits in a              */
/*           10*8bits values array                                           */
/*      -(o) parameters : 16 values parameters array                         */
/*                                                                           */
/*****************************************************************************/
void parametersBitStream2Array(const uint8_t bitStream[], uint16_t parameters[])
{
	parameters[0] = (bitStream[0]>>7)&(uint16_t)0x1;
	parameters[1] = bitStream[0]&(uint16_t)0x7f;
	parameters[2] = (bitStream[1]>>3)&(uint16_t)0x1f;
	parameters[3] = (((uint16_t)bitStream[1]&(uint16_t)0x7)<<2) | ((bitStream[2]>>6)&(uint16_t)0x3);
	parameters[4] = (((uint16_t)bitStream[2])&(uint16_t)0x3f)<<2 | ((bitStream[3]>>6)&(uint16_t)0x3);;
	parameters[5] = (bitStream[3]>>5)&(uint16_t)0x1;
	parameters[6] = (((uint16_t)(bitStream[3]&(uint16_t)0x1f))<<8)| bitStream[4];
	parameters[7] = (bitStream[5]>>4)&(uint16_t)0xf;
	parameters[8] = (bitStream[5]>>1)&(uint16_t)0x7;
	parameters[9] = (((uint16_t)bitStream[5]&(uint16_t)0x1)<<3)|((bitStream[6]>>5)&(uint16_t)0x7);
	parameters[10]= (uint16_t)bitStream[6]&(uint16_t)0x1f;
	parameters[11]= (((uint16_t)bitStream[7])<<5)|((bitStream[8]>>3)&(uint16_t)0x1f);
	parameters[12]= ((bitStream[8]&(uint16_t)0x7)<<1) | ((bitStream[9]>>7)&(uint16_t)0x1);
	parameters[13]= (bitStream[9]>>4)&(uint16_t)0x7;
	parameters[14]= bitStream[9]&(uint16_t)0xf;

	return;
}

/*****************************************************************************/
/* pseudoRandom : generate pseudo random number as in spec 4.4.4 eq96        */
/*    parameters:                                                            */
/*      -(i/o) randomGeneratorSeed(updated by this function)                 */
/*    return value :                                                         */
/*      - a unsigned 16 bits pseudo random number                            */
/*                                                                           */
/*****************************************************************************/
uint16_t pseudoRandom(uint16_t *randomGeneratorSeed)
{
	/* pseudoRandomSeed is stored in an uint16_t var, we shall not worry about overflow here */
	/* pseudoRandomSeed*31821 + 13849; */
	*randomGeneratorSeed = MAC16_16(13849, (*randomGeneratorSeed), 31821);
	return *randomGeneratorSeed;
}
