/*====================================================================================
    EVS Codec 3GPP TS26.443 Jun 30, 2015. Version CR 26.443-0006
  ====================================================================================*/

#include <stdlib.h>
#include <math.h>
#include "options.h"
#include "cnst.h"
#include "rom_com.h"
#include "prot.h"

static float const * get_edct_table(short length)
{
    float const * edct_table = NULL;
    switch (length)
    {
    case 1200:
        edct_table = edct_table_600;
        break;
    case 960:
        edct_table = edct_table_480;
        break;
    case 640:
        edct_table = edct_table_320;
        break;
    case 320:
        edct_table = edct_table_160;
        break;
    case 256:
        edct_table = edct_table_128;
        break;
    case 240:
        edct_table = edct_table_120;
        break;
    case 200:
        edct_table = edct_table_100;
        break;
    case 160:
        edct_table = edct_table_80;
        break;
    case 40:
        edct_table = edct_table_20;
        break;
    case 800:
        edct_table = edct_table_400;
        break;
    case 512:
        edct_table = edct_table_256;
        break;
    case 480:
        edct_table = edct_table_240;
        break;
    case 384:
        edct_table = edct_table_192;
        break;
    case 400:
        edct_table = edct_table_200;
        break;
    case 128:
        edct_table = edct_table_64;
        break;
    case 120:
        edct_table = edct_table_60;
        break;
    case 80:
        edct_table = edct_table_40;
        break;
    case 64:
        edct_table = edct_table_32;
        break;
    case 32:
        edct_table = edct_table_16;
        break;
    case 20:
        edct_table = edct_table_10;
        break;
    default:
        fprintf(stderr, "edct/edst(): length is not in table!");
        exit(-1);
        break;
    }
    return edct_table;
}

/*-----------------------------------------------------------------*
 * edct()
 *
 * DCT transform
 *-----------------------------------------------------------------*/

void edct(
    const float *x,         /* i  : input signal        */
    float *y,         /* o  : output transform    */
    short length      /* i  : length              */
)
{
    short i;
    float re[L_FRAME_PLUS/2];
    float im[L_FRAME_PLUS/2];
    const float *edct_table = 0;
    float local;

    edct_table = get_edct_table(length);

    /* Twiddling and Pre-rotate */
    for (i = 0; i < length/2; i++)
    {
        re[i] = x[2*i] * edct_table[i] + x[length-1-2*i] * edct_table[length/2-1-i];
        im[i] = x[length-1-2*i] * edct_table[i] - x[2*i] * edct_table[length/2-1-i];
    }

    DoFFT(re, im, length/2);

    local = (0.75f * EVS_PI)/ length;

    for (i = 0; i < length/2; i++)
    {
        float tmp;
        tmp = re[i] - im[i] * local;  /* 3*pi/(4*length) is a constant */
        im[i] = im[i] + re[i] * local;
        re[i] = tmp;
    }

    /* Post-rotate and obtain the output data */
    for (i = 0; i < length/2; i++)
    {
        y[2*i] = re[i] * edct_table[i] + im[i] * edct_table[length/2-1-i];
        y[length-1-2*i] = re[i] * edct_table[length/2-1-i] - im[i] * edct_table[i];
    }

    return;
}

/*-------------------------------------------------------------------------*
 * FUNCTION : edst()
 *
 * PURPOSE : DST_IV transform
 *-------------------------------------------------------------------------*/
void edst(
    const float *x,         /* i  : input signal        */
    float *y,         /* o  : output transform    */
    short length      /* i  : length              */
)
{
    short i;
    float re[L_FRAME_PLUS/2];
    float im[L_FRAME_PLUS/2];
    const float *edct_table = 0;
    float local;

    edct_table = get_edct_table(length);

    /* Twiddling and Pre-rotate */
    for (i = 0; i < length/2; i++)
    {
        re[i] = x[length-1-2*i] * edct_table[i] + x[2*i] *edct_table[length/2-1-i];
        im[i] = x[2*i] * edct_table[i] - x[length-1-2*i] * edct_table[length/2-1-i];
    }

    DoFFT(re, im, length/2);

    local = (0.75f * EVS_PI)/ length;

    for (i = 0; i < length/2; i++)
    {
        float tmp;
        tmp = re[i] - im[i] * local;  /* 3*pi/(4*length) is a constant */
        im[i] = im[i] + re[i] * local;
        re[i] = tmp;
    }

    /* Post-rotate and obtain the output data */
    for (i = 0; i < length/2; i++)
    {
        y[2*i] = re[i] * edct_table[i] + im[i] * edct_table[length/2-1-i];
        y[length-1-2*i] = im[i] * edct_table[i] - re[i] * edct_table[length/2-1-i];
    }

    return;
}

/*-----------------------------------------------------------------*
 * iedct_short()
 *
 * Inverse EDCT for short frames
 *-----------------------------------------------------------------*/

void iedct_short(
    const float *in,                /* i  : input vector     */
    float *out,               /* o  : output vector    */
    const short segment_length      /* i  : length           */
)
{
    float alias[MAX_SEGMENT_LENGTH];
    short i;

    edct(in, alias, segment_length/2);

    for (i = 0; i < segment_length/4; i++)
    {
        out[i] = alias[segment_length/4 + i];
        out[segment_length/4 + i] = -alias[segment_length/2 - 1 - i];
        out[segment_length/2 + i] = -alias[segment_length/4 - 1 - i];
        out[3*segment_length/4 + i] = -alias[i];
    }

    return;
}