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

/**
 * compute inverse of LPC prediction gain, and
 * test if LPC coefficients are stable (all poles within unit circle)
 *
 * @author Jing Dai
 * @author Dingxin Xu
 */
public class LPCInvPredGainFLP
{
    static final float RC_THRESHOLD =       0.9999f;

    /**
     *     compute inverse of LPC prediction gain, and
     *  test if LPC coefficients are stable (all poles within unit circle)
     *  this code is based on SKP_Silk_a2k_FLP().
     * @param invGain inverse prediction gain, energy domain
     * @param A prediction coefficients [order]
     * @param A_offset offset of valid data.
     * @param order prediction order
     * @return returns 1 if unstable, otherwise 0
     */
    static int SKP_Silk_LPC_inverse_pred_gain_FLP(   /* O:   returns 1 if unstable, otherwise 0      */
        float[]       invGain,               /* O:   inverse prediction gain, energy domain  */
        float[]       A,                     /* I:   prediction coefficients [order]         */
        int A_offset,
        int           order                  /* I:   prediction order                        */
    )
    {
        int   k, n;
        double    rc, rc_mult1, rc_mult2;
        float[][] Atmp = new float[ 2 ][ SigProcFIX.SKP_Silk_MAX_ORDER_LPC ];
        float[] Aold, Anew;

        Anew = Atmp[ order & 1 ];
        for(int i_djinn=0; i_djinn<order; i_djinn++)
            Anew[i_djinn] = A[A_offset+i_djinn];

        invGain[0] = 1.0f;
        for( k = order - 1; k > 0; k-- )
        {
            rc = -Anew[ k ];
            if (rc > RC_THRESHOLD || rc < -RC_THRESHOLD)
            {
                return 1;
            }
            rc_mult1 = 1.0f - rc * rc;
            rc_mult2 = 1.0f / rc_mult1;
            invGain[0] *= (float)rc_mult1;
            /* swap pointers */
            Aold = Anew;
            Anew = Atmp[ k & 1 ];
            for( n = 0; n < k; n++ ) {
                Anew[ n ] = (float)( ( Aold[ n ] - Aold[ k - n - 1 ] * rc ) * rc_mult2 );
            }
        }
        rc = -Anew[ 0 ];
        if ( rc > RC_THRESHOLD || rc < -RC_THRESHOLD ) {
            return 1;
        }
        rc_mult1 = 1.0f - rc * rc;
        invGain[0] *= (float)rc_mult1;
        return 0;
    }
}
