/*
 * 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 "codecParameters.h"
#include "basicOperationsMacros.h"
#include "utils.h"
#include <stdlib.h>

#include "fixedCodebookSearch.h"

/*** local functions ***/
void computeImpulseResponseCorrelationMatrix(word16_t impulseResponse[], word16_t correlationSignal[], int correlationSignalSign[], word32_t Phi[L_SUBFRAME][L_SUBFRAME]);
void computePhiDiagonal(int j, word16_t impulseResponse[], word32_t Phi[L_SUBFRAME][L_SUBFRAME], uint16_t PhiScaling);

/*****************************************************************************/
/* fixedCodebookSearch: compute fixed codebook parameters (codeword and sign)*/
/*      compute also fixed codebook vector as in spec 3.8.1                  */
/*    parameters:                                                            */
/*      -(i) targetSignal: 40 values as in spec A.3.6 in Q0                  */
/*      -(i) impulseResponse: 40 values as in spec A.3.5 in Q12              */
/*      -(i) intPitchDelay: current integer pitch delay                      */
/*      -(i) lastQuantizedAdaptativeCodebookGain: previous subframe pitch    */
/*           gain quantized in Q14                                           */
/*      -(i) filteredAdaptativeCodebookVector : 40 values in Q0              */
/*      -(i) adaptativeCodebookGain : in Q14                                 */
/*      -(o) fixedCodebookParameter                                          */
/*      -(o) fixedCodebookPulsesSigns                                        */
/*      -(o) fixedCodebookVector : 40 values as in spec 3.8, eq45 in Q13     */
/*      -(o) fixedCodebookVectorConvolved : 40 values as in spec 3.9, eq64   */
/*           in Q12.                                                         */
/*                                                                           */
/*****************************************************************************/
void fixedCodebookSearch(word16_t targetSignal[], word16_t impulseResponse[], int16_t intPitchDelay, word16_t lastQuantizedAdaptativeCodebookGain, word16_t filteredAdaptativeCodebookVector[], word16_t adaptativeCodebookGain,
			uint16_t *fixedCodebookParameter, uint16_t *fixedCodebookPulsesSigns, word16_t fixedCodebookVector[], word16_t fixedCodebookVectorConvolved[])
{
	int i,j,n;
	word16_t fixedCodebookTargetSignal[L_SUBFRAME];
	word32_t correlationSignal32[L_SUBFRAME]; /* on 32 bits in Q12 */ 
	word16_t correlationSignal[L_SUBFRAME]; /* normalised to fit on 13 bits */
	word32_t correlationSignalMax = 0;
	word32_t abscCrrelationSignal32;
	uint16_t correlationSignalMaxNorm;
	int correlationSignalSign[L_SUBFRAME]; /* to store the sign of each correlationSignal element */
	/* build the matrix Ф' : impulseResponse correlation matrix spec 3.8.1 eq51, eq56 and eq57 */
	/* correlationSignal turns to absolute values and sign of elements is stored in correlationSignalSign */
	word32_t Phi[L_SUBFRAME][L_SUBFRAME] = {{0}};
	int m3Base;
	int i0=0, i1=0, i2=0, i3=0;
	word32_t correlationSquareMax = -1;
	word32_t energyMax = 1;
	int m0=0, m1=0, m2=0, m3=0;
	int mSwitch[2][4] = {{2,3,0,1},{3,0,1,2}};
	int mIndex;
	int jx = 0;

	/* compute the target signal for fixed codebook spec 3.8.1 eq50 : fixedCodebookTargetSignal[i] = targetSignal[i] - (adaptativeCodebookGain * filteredAdaptativeCodebookVector[i]) */
	for (i=0; i<L_SUBFRAME; i++) {
		fixedCodebookTargetSignal[i] = MSU16_16_Q14(targetSignal[i], filteredAdaptativeCodebookVector[i], adaptativeCodebookGain); /* adaptativeCodebookGain in Q14, other values in Q0 */
	}

	/* update impulse vector as in spec 3.8 eq49 */
	for (i=intPitchDelay; i<L_SUBFRAME; i++) {
		impulseResponse[i] = MAC16_16_Q14(impulseResponse[i], impulseResponse[i-intPitchDelay], lastQuantizedAdaptativeCodebookGain); /* h[n] = h[n] + β*h[n-T], impulseResponse in Q12, lastQuantizedAdaptativeCodebookGain in Q14 */
	}

	/* compute the correlation signal as in spec 3.8.1 eq52 */
	/* compute on 32 bits and get the maximum */
	for (n=0; n<L_SUBFRAME; n++) {
		correlationSignal32[n] = 0;
		for (i=n; i<L_SUBFRAME; i++) {
			correlationSignal32[n] = MAC16_16(correlationSignal32[n], fixedCodebookTargetSignal[i], impulseResponse[i-n]);
		}
		abscCrrelationSignal32 = correlationSignal32[n]>=0?correlationSignal32[n]:-correlationSignal32[n];
		if (abscCrrelationSignal32>correlationSignalMax) {
			correlationSignalMax = abscCrrelationSignal32;
		}
	}
	/* normalise on 13 bits */
	correlationSignalMaxNorm = countLeadingZeros(correlationSignalMax);
	if (correlationSignalMaxNorm<18) { /* if it doesn't already fit on 13 bits */
		for (i=0; i<L_SUBFRAME; i++) {
			correlationSignal[i] = (word16_t)(SHR(correlationSignal32[i], 18-correlationSignalMaxNorm));
		 }
	} else { /* it fits on 13 bits, just copy it to the 16 bits buffer */
		for (i=0; i<L_SUBFRAME; i++) {
			correlationSignal[i] = (word16_t)correlationSignal32[i];
		}
	}

	computeImpulseResponseCorrelationMatrix(impulseResponse, correlationSignal, correlationSignalSign, Phi);
	
	/* search for impulses leading to a max in C^2/E : spec 3.8.1 eq53 */
	/* algorithm, not described in spec, retrieved from ITU code */
	/* by tracks are intended series of index m0 track is 0,5,10,...35. m1 is 1,6,11,..,36. m2 is 2,7,12,..,37. m3 is 3,8,13,..,38. m4 is 4,9,14,..,39 */
	/* note index m3 will follow track m3 and m4 */
	/* The following search is performed twice: first with m3 index following m3 track, and then m3 index follow m4 track */
	/*    The following operation are performed twice, first using indexes m2, m3, m0, m1 and then m3, m0, m1, m2 as following */
	/*       description is made for the first run, for the second one, juste substitute m2 by m3, m3 by m0, m0 by m1 and m1 by m2 */
	/*       - search in m2 track two maxima for the correlation Signal. For each of this maximum : */
	/*         -- compute for the whole m3 track (8 values) the values C^2 and E (see eq58 and 59) and keep the one giving the best ratio */
	/*       - compute for the whole tracks m0 and m1 (64 values) the values C^2 and E (keeping the m2 and m3 previously computed) and save the one giving the best ratio */
	for (m3Base=3; m3Base<5; m3Base++) {
		for(mIndex=0; mIndex<2; mIndex++) {
			/* define for this loop on m3 track the Correlation and Energy giving the maximum of eq53 */
			word32_t m3TrackCorrelationSquare = -1;
			word32_t m3TrackEnergy = 1;

			/* Loop on the two maxima of correlation in the m2 index */
			int firstM2 = 0; /* save the first maximum index to not select it again */
			word16_t correlationM2M3Max = 0; /* stores the contribution of m2 and m3 impulses to the correlation for the maximum selected */
			word32_t energyM2M3Max = 0; /* same thing but for the energy */
			for (i=0; i<2; i++) {
				word16_t correlationM2 = -1;
				int currentM2=0;
				word32_t energyM2;
				for (j=mSwitch[mIndex][0]; j<L_SUBFRAME; j+=5) { /* in the m2 range, find the correlation Max -> select m2 */
					if (correlationSignal[j]>correlationM2 && j!=firstM2) {
						currentM2 = j;
						correlationM2=correlationSignal[j];
					}
				}
				firstM2 = currentM2; /* to avoid selecting the same maximum at next iteration */

				energyM2 = Phi[currentM2][currentM2]; /* compute the energy with terms of eq55 using m2 only: Phi'(m2,m2) */		
			
				/* with selected m2, test the 8 m3 possibilities for the current m3 track */
				for (j=mSwitch[mIndex][1]; j<L_SUBFRAME; j+=5) {
					word16_t correlationM2M3 = ADD16(correlationM2, correlationSignal[j]); /* compute the correlation sum due to m2 and m3 pulses */
					word32_t energyM2M3 =  ADD32(energyM2, ADD32(Phi[currentM2][j], Phi[j][j])); /* compute the energy if eq55 using term including m2 and m3: Phi'(m2,m2) is already in energyM2 + Phi'(m2,m3) + Phi'(m3,m3) */
					word32_t correlationM2M3Square = MULT16_16(correlationM2M3, correlationM2M3);
					/* check if the current correlation/energy couple gives better results than the stored one : maximise C^2/E -> C^2/E > C^2max/Emax => Emax*C^2 > C^2max*E */
					if (MULT32_32(m3TrackEnergy,correlationM2M3Square) > MULT32_32(energyM2M3, m3TrackCorrelationSquare)) {
						m3TrackCorrelationSquare = correlationM2M3Square;
						m3TrackEnergy = energyM2M3;
						correlationM2M3Max = correlationM2M3;
						m3 = j;
						m2 = currentM2; 
					}
				}
			}
			energyM2M3Max = m3TrackEnergy;

			/* reset the current m3 track correlationSquare and energy */
			m3TrackCorrelationSquare = -1;
			m3TrackEnergy = 1;

			for (i=mSwitch[mIndex][2]; i<L_SUBFRAME; i+=5) { /* test the 8 possibilities for m0 track */
				word16_t correlationM2M3M0 = ADD16(correlationM2M3Max, correlationSignal[i]); /* compute correlation with current m0 taking in account the previously selected m2 and m3 */
				word32_t energyM2M3M0 = ADD32(energyM2M3Max, ADD32(Phi[i][i], ADD32(Phi[i][m2], Phi[i][m3]))); /* add to the previously computed energy the terms of eq59 we can compute with the selected m0: Phi'(m0,m0) + Phi'(m0,m2) + Phi'(m0,m3) */ 
				for (j=mSwitch[mIndex][3]; j<L_SUBFRAME; j+=5) { /* test the 8 possibilities for m1 track */
					word16_t correlationM2M3M0M1 = ADD16(correlationM2M3M0, correlationSignal[j]); /* compute correlation with current m1 taking in account the previously selected m2, m3 and m0 */
					word32_t energyM2M3M0M1 = ADD32(energyM2M3M0, ADD32(Phi[j][i], ADD32(Phi[j][j], ADD32(Phi[j][m2], Phi[j][m3])))); /* add to the previously computed energy the terms of eq59 we can compute with the selected m1: Phi'(m1,m0) + Phi'(m1,m1) + Phi'(m1,m2) + Phi'(m1,m3) */ 
					word32_t correlationM2M3M0M1Square = MULT16_16(correlationM2M3M0M1, correlationM2M3M0M1);
					/* check if the current correlation/energy couple gives better results than the stored one : maximise C^2/E -> C^2/E > C^2max/Emax => Emax*C^2 > C^2max*E */
					if (MULT32_32(m3TrackEnergy,correlationM2M3M0M1Square) > MULT32_32(energyM2M3M0M1, m3TrackCorrelationSquare)) {
						m3TrackCorrelationSquare = correlationM2M3M0M1Square;
						m3TrackEnergy = energyM2M3M0M1;
						m1 = j;
						m0 = i; 
					}
				}
			}

			/* check with currently selected indexes if this one is better */
			if (MULT32_32(energyMax,m3TrackCorrelationSquare) > MULT32_32(m3TrackEnergy, correlationSquareMax)) {
				correlationSquareMax = m3TrackCorrelationSquare;
				energyMax = m3TrackEnergy;
				if (mIndex==0) {
					i0 = m0;
					i1 = m1;
					i2 = m2;
					i3 = m3;
				} else {
					i0 = m3;
					i1 = m0;
					i2 = m1;
					i3 = m2;
				}
				jx = m3Base - 3; /* needed for parameter computation apec 3.8.2 eq62 */
			}

			
		}
		mSwitch[0][1]++; mSwitch[1][0]++; /*increment the m3Base into the mSwitch */
	}

	/* compute the fixedCodebookVector */
	for (i=0; i<L_SUBFRAME; i++) {
		fixedCodebookVector[i] = 0; /* reset the vector */
	}

	/* set the four pulses, in Q13 */
	fixedCodebookVector[i0] = SSHL((word16_t)correlationSignalSign[i0], 13);
	fixedCodebookVector[i1] = SSHL((word16_t)correlationSignalSign[i1], 13);
	fixedCodebookVector[i2] = SSHL((word16_t)correlationSignalSign[i2], 13);
	fixedCodebookVector[i3] = SSHL((word16_t)correlationSignalSign[i3], 13);

	/* adapt it according to eq48 */
	for (i=intPitchDelay; i<L_SUBFRAME; i++) {
		fixedCodebookVector[i] = MAC16_16_Q14(fixedCodebookVector[i], fixedCodebookVector[i-intPitchDelay], lastQuantizedAdaptativeCodebookGain); /* h[n] = h[n] + β*h[n-T], fixedCodebookVector in Q13, lastQuantizedAdaptativeCodebookGain in Q14 */
	}

	/* compute the parameters */
	*fixedCodebookParameter = (uint16_t)(	MULT16_16_Q15((word16_t)i0, O2_IN_Q15)
					+	((MULT16_16_Q15((word16_t)i1, O2_IN_Q15))<<3)
					+	((MULT16_16_Q15((word16_t)i2, O2_IN_Q15))<<6)
					+	((((MULT16_16_Q15((word16_t)i3, O2_IN_Q15))<<1)+jx)<<9)
				);
	*fixedCodebookPulsesSigns = (uint16_t)((correlationSignalSign[i0]+1)>>1) | /* as in spec 3.8.2 eq61 */
					(uint16_t)(((correlationSignalSign[i1]+1)>>1)<<1) |
					(uint16_t)(((correlationSignalSign[i2]+1)>>1)<<2) |
					(uint16_t)(((correlationSignalSign[i3]+1)>>1)<<3);
	
	/* compute the fixedCodebook vector convolved with impulse response spec 3.9 eq64 */
	/* this vector is used in gain quantization but computed here because it's faster doing it having directly the impulses positions */
	/* eq64 make use of fixedCodebook vector adapted by eq48, using the impulse position(and thus fixed codebook vector before the adaptation)  but */
	/* the impulse response adapted as in eq49 gives the same output */
	/* reset the vector */
	for (i=0; i<i0; i++) fixedCodebookVectorConvolved[i] = 0;

 	if(correlationSignalSign[i0] > 0) {
		for(i=i0, j=0; i<L_SUBFRAME; i++, j++) {
			fixedCodebookVectorConvolved[i] = impulseResponse[j];
		}
	} else {
		for(i=i0, j=0; i<L_SUBFRAME; i++, j++) {
			fixedCodebookVectorConvolved[i] = -impulseResponse[j];
		}
	}

 	if(correlationSignalSign[i1] > 0) {
		for(i=i1, j=0; i<L_SUBFRAME; i++, j++) {
			fixedCodebookVectorConvolved[i] = ADD16(fixedCodebookVectorConvolved[i], impulseResponse[j]);
		}
	} else {
		for(i=i1, j=0; i<L_SUBFRAME; i++, j++) {
			fixedCodebookVectorConvolved[i] = SUB16(fixedCodebookVectorConvolved[i], impulseResponse[j]);
		}
	}

 	if(correlationSignalSign[i2] > 0) {
		for(i=i2, j=0; i<L_SUBFRAME; i++, j++) {
			fixedCodebookVectorConvolved[i] = ADD16(fixedCodebookVectorConvolved[i], impulseResponse[j]);
		}
	} else {
		for(i=i2, j=0; i<L_SUBFRAME; i++, j++) {
			fixedCodebookVectorConvolved[i] = SUB16(fixedCodebookVectorConvolved[i], impulseResponse[j]);
		}
	}

 	if(correlationSignalSign[i3] > 0) {
		for(i=i3, j=0; i<L_SUBFRAME; i++, j++) {
			fixedCodebookVectorConvolved[i] = ADD16(fixedCodebookVectorConvolved[i], impulseResponse[j]);
		}
	} else {
		for(i=i3, j=0; i<L_SUBFRAME; i++, j++) {
			fixedCodebookVectorConvolved[i] = SUB16(fixedCodebookVectorConvolved[i], impulseResponse[j]);
		}
	}

	return;
}


/*****************************************************************************/
/* computeImpulseResponseCorrelationMatrix: as in spec 3.8.1 eq51, eq56, eq57*/
/*    parameters:                                                            */
/*      -(i) impulseResponse : 40 values in Q12                              */
/*      -(i/o) correlationSignal: 40 values in Q12 get absolute value of     */
/*                input as output as specified in spec 3.8.1                 */
/*      -(o) correlationSignalSign : 40 values of -1 or 1 : the sign of      */
/*                the input correlationSignal elements                       */
/*                                                                           */
/*    return value: a triangular matrix composed of Phi(i,j) in Q24          */
/*         where Phi(i,j) = ∑h(n-i)*h(n-j) n in i..39                        */
/*         The matrix is then modified as decribed in eq56 and eq 57         */
/*                                                                           */
/*  Algorithm : the matrix elements are computed for j<=i                    */
/*     due to matrix element definition we have                              */
/*      Phi(i,j) = Phi(i+1,j+1) + h(39-i)*h(39-j)                            */
/*   - The matrix is computed starting from element Phi(39,x)=h(0)*h(39-x)   */
/*   and adding terms h(39-i)*h(39-j) give all the element of the diagonal   */
/*   up to Phi(39-x,0)                                                       */
/*   - Some diagonals are not computed because not needed :                  */
/*     Phi(39,34), Phi(39,29), Phi(39,24), .. Phi(39,4)                      */
/*   - The matrix elements signs are then adjusted according to eq56         */
/*   - The correlationSignal is modified to get absolute values of each      */
/*   element in it.                                                          */
/*   - Matrix elements are then duplicated to make access to them easier     */
/*                                                                           */
/*****************************************************************************/
void computeImpulseResponseCorrelationMatrix(word16_t impulseResponse[], word16_t correlationSignal[], int correlationSignalSign[], word32_t Phi[L_SUBFRAME][L_SUBFRAME])
{
	int i,j,iComp;
	word32_t acc = 0;
	uint16_t PhiScaling = 0;
	int correlationSignalSignInv[L_SUBFRAME];

	/* first compute the diagonal Phi(x,x) : Phi(39,39) = h[0]^2 # Phi(38,38) = Phi(39,39)+h[1]^2 */
	/* this diagonal must be divided by 2 according to spec 3.8.1 eq57 */
	for (i=0, iComp=L_SUBFRAME-1; i<L_SUBFRAME; i++, iComp--) { /* i in [0..39], iComp in [39..0] */
		acc = MAC16_16(acc, impulseResponse[i], impulseResponse[i]); /* impulseResponse in Q12 -> acc in Q24 */
		Phi[iComp][iComp] = SHR(acc,1); /* divide by 2: eq57*/
	}

	/* check for possible overflow: Phi will be summed 10 times, so max Phi (by construction Phi[0][0]*2 is the max of Phi-> 2*Phi[0][0]*10 must be < 0x7fff ffff -> Phi[0][0]< 0x06666666 - otherwise scale Phi)*/
	if (Phi[0][0]>0x6666666) {
		PhiScaling = 3 - countLeadingZeros((Phi[0][0]<<1) + 0x3333333); /* complement 0xccccccc adding 0x3333333 to shift by one when max(2*Phi[0][0]) is in 0x0fffffff < max < 0xcccccc */
		for (i=0; i<L_SUBFRAME; i++) {
			Phi[i][i] = SHR(Phi[i][i],PhiScaling);
		}
	}
	
	/* Compute all diagonals but the 34, 29, 24, 19, 14, 9 and 4*/
	for (i=0; i<8; i++) {
		for (j=0; j<4; j++) {
			computePhiDiagonal(5*i+j, impulseResponse, Phi, PhiScaling);
		}
	}

	/* correlationSignal -> absolute value and get sign (and his inverse in an array) */
	for (i=0; i<L_SUBFRAME; i++) {
		if (correlationSignal[i] >= 0) {
			correlationSignalSign[i] = 1;
			correlationSignalSignInv[i] = -1;
		} else { /* correlationSignal < 0 */
			correlationSignalSign[i] = -1;
			correlationSignalSignInv[i] = 1;
			correlationSignal[i] = -correlationSignal[i];
		}
	}

	/* modify the signs according to eq56 */
	for (i=0; i<L_SUBFRAME; i++) {
		int *signOfCorrelationSignalJ;
		
		if (correlationSignalSign[i]>0) { /* is sign(correlationSignal[i]) is positive, use the correlationSignalSign otherwise the inverted one */
			signOfCorrelationSignalJ = correlationSignalSign;
		} else {
			signOfCorrelationSignalJ = correlationSignalSignInv;
		}
		for (j=0; j<=i; j++) { /* multiply by the selected sign the matrix element */
			/* Note : even the not needed and thus not computed elements are multiplicated... might found other way to do this sign stuff to be more efficient */
			Phi[i][j] =  Phi[i][j] * signOfCorrelationSignalJ[j];
		}
	}
	
	/* duplicate the usefull values to their symetric part to get easier acces to the matrix elements */
	for (i=0; i<8; i++) {
		for (j=0; j<4; j++) {
			int k;
			int startIndex = 5*i+j;
			for(k=0; k<=startIndex; k++) {
				Phi[startIndex-k][L_SUBFRAME-1-k] = Phi[L_SUBFRAME-1-k][startIndex-k];
			}
		}
	}
	return;
}

/* compute a diagonal of Phi values: start from Phi(39,j) and step Phi(38, j-1) down to Phi(39-j, 0) */
/*      Phi(i,j) = Phi(i+1,j+1) + h(39-i)*h(39-j)                            */
void computePhiDiagonal(int j, word16_t impulseResponse[], word32_t Phi[L_SUBFRAME][L_SUBFRAME], uint16_t PhiScaling)
{
	word32_t acc = 0;
	int i=L_SUBFRAME -1;
	int iComp = 0;
	int jComp = L_SUBFRAME - 1 - j;

	if (PhiScaling == 0) {
		for (; j>=0; j--, i--, jComp++, iComp++) {
			acc = MAC16_16(acc, impulseResponse[iComp], impulseResponse[jComp]);
			Phi[i][j] = acc;
		}
	} else {
		for (; j>=0; j--, i--, jComp++, iComp++) {
			acc = MAC16_16(acc, impulseResponse[iComp], impulseResponse[jComp]);
			Phi[i][j] = SHR(acc,PhiScaling);
		}
	}
}
