#include <stdint.h>
#include <math.h>

typedef union _f32 {
    float f;
    uint32_t i;
} _f32;

/* Hilariously silly dB <-> linear conversions using floating point hacks. Accurate to ~1% or thereabouts. */
static inline float to_dB(float in){
  _f32 tmp;
  tmp.f = fabsf(in);
  return ((float)(tmp.i * 3.58855719e-7f - 382.3080943f));
}

static inline float from_dB(float in){
  _f32 tmp;
  if (in < -200.0f) return 0.0f;
  tmp.i = (2786635.2635f * (in + 382.133542f));
  //tmp.i = (2.78663525922e+05f*(in+3823.080943f));                                                                                                                                              
  //tmp.i = (3.66163873e+05f*(in+2909.498435f));
  return tmp.f;
}

float ff_amr_set_fixed_gain(float fixed_gain_factor, float fixed_mean_energy,
                            float *prediction_error, float energy_mean,
                            const float *pred_table)
{
    // Equations 66-69:
    // ^g_c = ^gamma_gc * 100.05 (predicted dB + mean dB - dB of fixed vector)
    // Note 10^(0.05 * -10log(average x2)) = 1/sqrt((average x2)).
    float err = pred_table[0]*prediction_error[0] + pred_table[1]*prediction_error[1] + pred_table[2]*prediction_error[2] + pred_table[3]*prediction_error[3];
    float val = fixed_gain_factor * from_dB(0.5f * (err + energy_mean));
    // update quantified prediction error energy history
    prediction_error[0] = prediction_error[1];
    prediction_error[1] = prediction_error[2];
    prediction_error[2] = prediction_error[3];
    prediction_error[3] = 2.0f * to_dB(fixed_gain_factor);
    return val / sqrtf(fixed_mean_energy);
}

