/*---------------------------------------------------------------------------*/
/*
  File: sparslec.cpp

  Sparse LEC.
*/
/*---------------------------------------------------------------------------*/

#include "platform.h"
#include "sdsputil.h"
#include "sparslec.h"
#ifdef __ANDROID__
void sdsp_printf(const char *format,...);
#endif

/* #define TRACE_ */
/* #define SPARSE_LEC_SIMULATE_ECHO_ */

#define FILE_ "SPARSLEC.CPP"

NAMESPACE_BEGIN

/*---------------------------------------------------------------------------*/

#ifdef TRACE_
#define trace(a) a
#else
#define trace(a)
#endif


/*---------------------------------------------------------------------------*/

static void ans_detector_init (t_ans_detector *p)
{
  p->angle = 0;
  p->angle_increment = ANS_DETECTOR_NOM_ANGLE_INCREMENT;
  p->increment_offset = ANS_DETECTOR_INCREMENT_OFFSET;
  p->energy = 0;
  p->phase_x = 0;
  p->phase_y = 0;
  p->fails = 0;
  p->reversals = 0;
  p->reduction_counter = 0;
  p->pass_counter = 0;
  p->fail_counter = 0;
  p->pass_reversals = 0;
  p->state = 0;
}


void sparse_lec_init (t_sparse_lec *p_lec, word total_length, word tail_length)
{
  word i;

  p_lec->total_length = total_length & (0xffff << SPARSE_LEC_SECTION_LENGTH_EXP);
  p_lec->tail_length = tail_length & (0xffff << SPARSE_LEC_SECTION_LENGTH_EXP);
  p_lec->state = 0;

  p_lec->max_sin = 0;
  for (i = 0; i < p_lec->total_length; i++)
    p_lec->coef_buffer[i] = 0;
  p_lec->delay_pos = 0;
  p_lec->first_section_active = TRUE;
  p_lec->first_section_proportionate = 0x7fff;

  p_lec->active.eout_gain = ((dword) SPARSE_LEC_EXPECTED_RETURN_LOSS) << 16;
  p_lec->active.loss_limit = ((dword) SPARSE_LEC_ENHANCED_RETURN_LOSS) << 16;
  p_lec->active.min_loss_limit = SPARSE_LEC_MIN_LOSS_LIMIT;
  p_lec->active.max_loss_limit = SPARSE_LEC_ENHANCED_RETURN_LOSS;
  p_lec->active.max_rin = SPARSE_LEC_MAX_RIN_BIAS;
  p_lec->active.double_talk_threshold = SPARSE_LEC_DOUBLE_TALK_THRESHOLD;
  p_lec->active.double_talk = 0;
  p_lec->active.mu_exponent = -SPARSE_LEC_MAX_TOTAL_LENGTH_EXP - 1;
  p_lec->active.mu_fraction = 32767;

  p_lec->passive.eout_gain = ((dword) SPARSE_LEC_EXPECTED_RETURN_LOSS) << 16;
  p_lec->passive.loss_limit = ((dword) SPARSE_LEC_EXPECTED_RETURN_LOSS) << 16;
  p_lec->passive.min_loss_limit = SPARSE_LEC_MIN_LOSS_LIMIT;
  p_lec->passive.max_loss_limit = SPARSE_LEC_EXPECTED_RETURN_LOSS;
  p_lec->passive.max_rin = SPARSE_LEC_MAX_RIN_BIAS;
  p_lec->passive.double_talk_threshold = SPARSE_LEC_DOUBLE_TALK_THRESHOLD;
  p_lec->passive.double_talk = 0;
#ifdef __ANDROID__
  p_lec->passive.mu_exponent = -SPARSE_LEC_MAX_TOTAL_LENGTH_EXP - 1 - 1;
#else
  p_lec->passive.mu_exponent = -SPARSE_LEC_MAX_TOTAL_LENGTH_EXP - 1 - 3;
#endif
  p_lec->passive.mu_fraction = 32767;

  for (i = 0; i < SPARSE_LEC_ACTIVE_DEPTH; i++)
    p_lec->active_step_table[i] = 0;
  for (i = 0; i < SPARSE_LEC_PASSIVE_DEPTH; i++)
    p_lec->passive_step_table[i] = 0;
  p_lec->section_offset = 0;
  p_lec->active_step_number = 0;
  p_lec->passive_step_number = 0;
  for (i = 0; i < (p_lec->total_length >> SPARSE_LEC_SECTION_LENGTH_EXP); i++)
    p_lec->section_base_buffer[i] = p_lec->total_length - ((i+1) << SPARSE_LEC_SECTION_LENGTH_EXP);
  for (i = 0; i < (p_lec->tail_length >> SPARSE_LEC_SECTION_LENGTH_EXP); i++)
    p_lec->proportionate_buffer[i] = 0x7fff;
  for (i = 0; i < (p_lec->total_length >> SPARSE_LEC_SECTION_LENGTH_EXP); i++)
    p_lec->coef_sum_buffer[i] = 0;
  p_lec->cng_random_number_seed = 0;

  p_lec->g164_disable_release = 0;
  p_lec->g165_disable_release = 0;

  sparse_lec_flush (p_lec);

  p_lec->ans_trace_reduction_counter = 0;
  p_lec->lec_trace_reduction_counter = 0;
  p_lec->trace_tag = 0;
}


void sparse_lec_flush (t_sparse_lec *p_lec)
{
  dword d;
  word i, n;
  short s;

  p_lec->rin_dc_offset = 0;
  p_lec->sin_dc_offset = 0;
  for (i = 0; i < p_lec->total_length + SPARSE_LEC_MAX_DEPTH; i++)
    p_lec->delay_buffer[i] = 0;
  for (i = 0; i < SPARSE_LEC_SECTION_BLOCKS; i++)
  {
    p_lec->active_rin_energy_low_table[i] = 0;
    p_lec->active_rin_energy_high_table[i] = 0;
  }
  for (i = 0; i < (p_lec->total_length >> SPARSE_LEC_SECTION_LENGTH_EXP); i++)
    p_lec->max_rin_buffer[i] = SPARSE_LEC_MAX_RIN_BIAS;
  p_lec->max_rin_pos = 0;
  p_lec->max_rin = SPARSE_LEC_MAX_RIN_BIAS;

  p_lec->active.rin_energy_low = 0;
  p_lec->active.rin_energy_high = 0;

  p_lec->passive.rin_energy_low = 0;
  p_lec->passive.rin_energy_high = 0;

  p_lec->activity_counter = 0;
  p_lec->activity_max_rin = 0;
  p_lec->activity_max_rin_2 = 0;

  for (i = 0; i < (p_lec->tail_length >> SPARSE_LEC_SECTION_LENGTH_EXP); i++)
    p_lec->active.rin_energy_low += (((dword)(p_lec->proportionate_buffer[i])) * ((dword) SPARSE_LEC_ACTIVE_RIN_ENERGY_BIAS)) >> 15;
  d = (word)(((dword)((p_lec->active.loss_limit >> 16) * (p_lec->active.loss_limit >> 16))) << 1);
  if (d < ((((dword) SPARSE_LEC_MIN_LOSS_LIMIT) * ((dword) SPARSE_LEC_MIN_LOSS_LIMIT)) << 1))
    d = (((dword) SPARSE_LEC_MIN_LOSS_LIMIT) * ((dword) SPARSE_LEC_MIN_LOSS_LIMIT)) << 1;
  s = 1 - sdsp_dword_leading_zeroes (d);
  d <<= -s;
  n = (word)(((dword)(0x3fffffff / (d >> 16))) >> 1);
  p_lec->active.rin_energy_low = (((dword)((((dword)(p_lec->active.rin_energy_low & 0xffff)) * ((dword) n)))) >> 15) +
    ((((dword)(p_lec->active.rin_energy_low >> 16)) * ((dword) n)) << 1);;
  p_lec->active.rin_energy_high = p_lec->active.rin_energy_low >> (32 - (2 - s));
  p_lec->active.rin_energy_low <<= 2 - s;
  for (i = 0; i < (p_lec->total_length >> SPARSE_LEC_SECTION_LENGTH_EXP); i++)
    p_lec->passive.rin_energy_low += (((dword) 0x7fff) * ((dword) SPARSE_LEC_RIN_ENERGY_BIAS)) << 1;

  p_lec->nlp_level = 0;
  p_lec->cng_lowpass_gain = ((dword) 0x0001) << 16;
  p_lec->cng_lowpass_slope = ((dword) 0x7fff) << 16;
  for (i = 0; i < SPARSE_LEC_CNG_HISTORY_SIZE; i++)
    p_lec->cng_eout_history[i] = 0;
  for (i = 0; i < SPARSE_LEC_CNG_HISTORY_SIZE; i++)
    p_lec->cng_noise_history[i] = 0;
  p_lec->cng_history_pos = 0;
  p_lec->cng_eout_energy = 0;
  p_lec->cng_noise_energy = 0;
  p_lec->cng_eout_hp_energy = 0;
  p_lec->cng_noise_hp_energy = 0;
  p_lec->cng_active = FALSE;

  ans_detector_init (&(p_lec->rin_ans_detector));
  ans_detector_init (&(p_lec->sin_ans_detector));
}


/*---------------------------------------------------------------------------*/

/* #define ANS_DETECTOR_TRACE_ */
#define SPARSE_LEC_TRACE_

static const word ans_detector_sin_cos_table[ANS_DETECTOR_SIN_COS_TABLE_SIZE] =
{
  0x0000, 0x0c8c, 0x18f9, 0x2528, 0x30fc, 0x3c57, 0x471d, 0x5134,
  0x5a82, 0x62f2, 0x6a6e, 0x70e3, 0x7642, 0x7a7d, 0x7d8a, 0x7f62,
  0x7fff, 0x7f62, 0x7d8a, 0x7a7d, 0x7642, 0x70e3, 0x6a6e, 0x62f2,
  0x5a82, 0x5134, 0x471d, 0x3c57, 0x30fc, 0x2528, 0x18f9, 0x0c8c,
  0x0000, 0xf374, 0xe707, 0xdad8, 0xcf04, 0xc3a9, 0xb8e3, 0xaecc,
  0xa57e, 0x9d0e, 0x9592, 0x8f1d, 0x89be, 0x8583, 0x8276, 0x809e,
  0x8000, 0x809e, 0x8276, 0x8583, 0x89be, 0x8f1d, 0x9592, 0x9d0e,
  0xa57e, 0xaecc, 0xb8e3, 0xc3a9, 0xcf04, 0xdad8, 0xe707, 0xf374,
  0x0000, 0x0c8c, 0x18f9, 0x2528, 0x30fc, 0x3c57, 0x471d, 0x5134,
  0x5a82, 0x62f2, 0x6a6e, 0x70e3, 0x7642, 0x7a7d, 0x7d8a, 0x7f62
};


static byte ans_detector_exec (t_ans_detector *p, short Sample)
{
  word i, k;
  short s, x, y, energy_low, energy_high, increment_offset;

  k = (word)((Sample >= 0) ? Sample : -Sample);
  p->energy += (((int32) k) - (p->energy >> 16)) * ((int32)(2 * ANS_DETECTOR_ENERGY_GAIN));

  i = p->angle >> (16 - ANS_DETECTOR_SIN_COS_TABLE_EXP);
  s = ans_detector_sin_cos_table[i + (1 << (ANS_DETECTOR_SIN_COS_TABLE_EXP - 2))];
  s = (short)(((int32)(((int32) Sample) * ((int32) s))) >> 15);
  p->phase_x += (((int32) s) - (p->phase_x >> 16)) * ((int32)(2 * ANS_DETECTOR_PHASE_GAIN));
  s = ans_detector_sin_cos_table[i];
  s = (short)(((int32)(((int32) Sample) * ((int32) s))) >> 15);
  p->phase_y += (((int32) s) - (p->phase_y >> 16)) * ((int32)(2 * ANS_DETECTOR_PHASE_GAIN));

  x = (short)(p->phase_x >> 16);
  y = (short)(p->phase_y >> 16);
  k = (word)(((dword)(((dword)(p->energy >> 16)) * ((dword)(ANS_DETECTOR_ENERGY_LOW_THRESHOLD >> 1)))) >> 14);
  energy_low = (k <= 0x7fff) ? k : 0x7fff;
  k = (word)(((dword)(((dword)(p->energy >> 16)) * ((dword)(ANS_DETECTOR_ENERGY_HIGH_THRESHOLD >> 1)))) >> 14);
  energy_high = (k <= 0x7fff) ? k : 0x7fff;
  increment_offset = p->increment_offset;
  if (x >= 0)
  {
    if (y >= 0)
    {
      if (y >= x)
      {
        (p->fails)++;
        p->increment_offset = -ANS_DETECTOR_INCREMENT_OFFSET;
      }
      else
      {
        if ((x <= energy_low) || (x >= energy_high))
          (p->fails)++;
        p->increment_offset = -ANS_DETECTOR_INCREMENT_OFFSET_HALF;
      }
    }
    else
    {
      if (-y >= x)
      {
        (p->fails)++;
        p->increment_offset = ANS_DETECTOR_INCREMENT_OFFSET;
      }
      else
      {
        if ((x <= energy_low) || (x >= energy_high))
          (p->fails)++;
        p->increment_offset = ANS_DETECTOR_INCREMENT_OFFSET_HALF;
      }
    }
  }
  else
  {
    if (y >= 0)
    {
      if (y >= -x)
      {
        (p->fails)++;
        p->increment_offset = -ANS_DETECTOR_INCREMENT_OFFSET;
      }
      else if (-x <= (energy_low >> 1))
      {
        (p->fails)++;
        p->increment_offset = (p->increment_offset >= 0) ? ANS_DETECTOR_INCREMENT_OFFSET_HALF : -ANS_DETECTOR_INCREMENT_OFFSET_HALF;
      }
      else
      {
        p->increment_offset = ANS_DETECTOR_INCREMENT_OFFSET_HALF;
        (p->reversals)++;
        p->angle += 0x8000;
        p->phase_x = -(p->phase_x);
        p->phase_y = -(p->phase_y);
      }
    }
    else
    {
      if (-y >= -x)
      {
        (p->fails)++;
        p->increment_offset = ANS_DETECTOR_INCREMENT_OFFSET;
      }
      else if (-x <= (energy_low >> 1))
      {
        (p->fails)++;
        p->increment_offset = (p->increment_offset >= 0) ? ANS_DETECTOR_INCREMENT_OFFSET_HALF : -ANS_DETECTOR_INCREMENT_OFFSET_HALF;
      }
      else
      {
        p->increment_offset = -ANS_DETECTOR_INCREMENT_OFFSET;
        (p->reversals)++;
        p->angle += 0x8000;
        p->phase_x = -(p->phase_x);
        p->phase_y = -(p->phase_y);
      }
    }
  }
  p->angle_increment += p->increment_offset >> 3;
  if (p->angle_increment + p->increment_offset > ANS_DETECTOR_MAX_ANGLE_INCREMENT)
    p->angle_increment = ANS_DETECTOR_MAX_ANGLE_INCREMENT - p->increment_offset;
  if (p->angle_increment + p->increment_offset < ANS_DETECTOR_MIN_ANGLE_INCREMENT)
    p->angle_increment = ANS_DETECTOR_MIN_ANGLE_INCREMENT - p->increment_offset;
  p->angle += p->angle_increment + p->increment_offset + ((p->increment_offset - increment_offset) << ANS_DETECTOR_ANGLE_HIT_EXP);

  if (++(p->reduction_counter) != ANS_DETECTOR_REDUCTION_RATIO)
    return (FALSE);
  p->reduction_counter = 0;

  if ((p->energy >> 16) >= ANS_DETECTOR_RELEASE_ENERGY)
    p->state |= ANS_DETECTOR_STATE_ENERGY_DETECTED;
  else
    p->state &= ~ANS_DETECTOR_STATE_ENERGY_DETECTED;

  p->fail_counter += p->fails;
  if ((p->fail_counter < ANS_DETECTOR_MAX_FAIL_TIME)
   && ((p->energy >> 16) >= ANS_DETECTOR_DETECT_ENERGY))
  {
    if (p->reversals)
    {
      if (p->pass_counter < ANS_DETECTOR_MIN_PASS_TIME)
      {
        p->pass_reversals = 0;
        p->state &= ~ANS_DETECTOR_STATE_G165_DETECTED;
      }
      else if ((p->pass_reversals == 0)
       || (p->pass_counter < ANS_DETECTOR_MIN_REVERSAL_DISTANCE)
       || (p->pass_counter > ANS_DETECTOR_MAX_REVERSAL_DISTANCE))
      {
        p->pass_reversals = 1;
        p->state &= ~ANS_DETECTOR_STATE_G165_DETECTED;
      }
      else
      {
        if (p->pass_reversals < ANS_DETECTOR_MIN_G165_REVERSALS)
          (p->pass_reversals)++;
      }
      p->pass_counter = 0;
      p->fail_counter = 0;
    }
    else
    {
      if (p->pass_counter > ANS_DETECTOR_MAX_REVERSAL_DISTANCE)
        p->state &= ~ANS_DETECTOR_STATE_G165_DETECTED;
      else
      {
        if ((p->pass_counter < ANS_DETECTOR_MIN_PASS_TIME)
         && (p->pass_counter + ANS_DETECTOR_REDUCTION_RATIO - p->fails >= ANS_DETECTOR_MIN_PASS_TIME))
        {
          p->fail_counter = 0;
          if (p->pass_reversals == ANS_DETECTOR_MIN_G165_REVERSALS)
            p->state |= ANS_DETECTOR_STATE_G165_DETECTED;
        }
        p->pass_counter += ANS_DETECTOR_REDUCTION_RATIO - p->fails;
        if (p->pass_counter >= ANS_DETECTOR_MIN_G164_DETECT_TIME)
          p->state |= ANS_DETECTOR_STATE_G164_DETECTED;
      }
    }
  }
  else
  {
    p->pass_counter = 0;
    p->fail_counter = 0;
    p->pass_reversals = 0;
    p->state &= ~(ANS_DETECTOR_STATE_G164_DETECTED | ANS_DETECTOR_STATE_G165_DETECTED);
  }
  p->fails = 0;
  p->reversals = 0;
  return (TRUE);
}


/*---------------------------------------------------------------------------*/

#define SPARSE_LEC_PASSIVE_STEPS_EXP \
    ((SPARSE_LEC_PASSIVE_STEPS <= 0x01) ? 0 : \
    ((SPARSE_LEC_PASSIVE_STEPS <= 0x02) ? 1 : \
    ((SPARSE_LEC_PASSIVE_STEPS <= 0x04) ? 2 : \
    ((SPARSE_LEC_PASSIVE_STEPS <= 0x08) ? 3 : \
    ((SPARSE_LEC_PASSIVE_STEPS <= 0x10) ? 4 : \
    ((SPARSE_LEC_PASSIVE_STEPS <= 0x20) ? 5 : \
    ((SPARSE_LEC_PASSIVE_STEPS <= 0x40) ? 6 : \
    ((SPARSE_LEC_PASSIVE_STEPS <= 0x80) ? 7 : 8))))))))


static void sparse_lec_update (t_sparse_lec_update *p_upd, short *step_table, word max_sin, word max_eout)
{
  dword d, acc_low;
  int32 l, acc_high;
  short energy_exp, eout_exp, loss_exp;
  word energy_quot;
  short s, t;
  word i, k, n, m;

  d = (p_upd->rin_energy_low >> SPARSE_LEC_MAX_TOTAL_LENGTH_EXP) |
    (p_upd->rin_energy_high << (32 - SPARSE_LEC_MAX_TOTAL_LENGTH_EXP));
  energy_exp = 1 - sdsp_dword_leading_zeroes (d);
  if (energy_exp >= 0)
    d >>= energy_exp;
  else
    d <<= -energy_exp;
  energy_quot = (word)(((dword)(0x3fffffff / (d >> 16))) >> 1);
  n = (word)(p_upd->eout_gain >> 16);
  if (n > 0x7fff)
    n = 0x7fff;
  m = (max_eout < max_sin) ? max_eout : max_sin;
  if ((m > ((word)(((dword)(((dword)(p_upd->max_rin)) * ((dword)(p_upd->double_talk_threshold)))) >> 15))))
    p_upd->double_talk = SPARSE_LEC_DOUBLE_TALK_HANGOVER;
  if (p_upd->double_talk != 0)
  {
    if (n > SPARSE_LEC_LIMITER_DOUBLE_TALK_GAIN)
      n = SPARSE_LEC_LIMITER_DOUBLE_TALK_GAIN;
  }
  acc_low = 0;
  acc_high = 0;
  for (i = 0; i < SPARSE_LEC_ACTIVE_DEPTH; i++)
  {
    step_table[i] = (short)(((int32)(((((int32)(step_table[i])) * ((dword) n)) << 1) + 0x8000)) >> 16);
    d = (((int32)(step_table[i])) * ((int32)(step_table[i]))) << 1;
    acc_low += d;
    if (acc_low < d)
      acc_high++;
  }
  d = (acc_high << 30) | (acc_low >> 2);
  d = (((dword)((((dword)(d & 0xffff)) * ((dword) energy_quot)))) >> 15) +
    ((((dword)(d >> 16)) * ((dword) energy_quot)) << 1);
  eout_exp = 1 - sdsp_dword_leading_zeroes (d);
  l = ((p_upd->loss_limit >> 16) * (p_upd->loss_limit >> 16)) << 1;
  loss_exp = 1 - sdsp_dword_leading_zeroes ((dword) l);
  s = (short)((l << (-loss_exp - 3)) >> 16);
  t = (-energy_exp - SPARSE_LEC_MAX_TOTAL_LENGTH_EXP + 11 + 1) + (-loss_exp - 3);
  if (t >= 0)
  {
    if (t > -eout_exp)
      k = 0x7fff;
    else
      k = (word)((d << t) >> 16);
  }
  else
  {
    if (-t >= 16 + eout_exp)
      k = 0;
    else
      k = (word)((d >> -t) >> 16);
  }
  s -= k;
  s = (short)(((int32)(((int32) s) * ((int32) SPARSE_LEC_LIMITER_FAST_SLOPE))) >> 15);
  if (s < 0)
  {
    l = (((dword)(-s)) * ((dword)(p_upd->eout_gain >> 16))) << 1;
    p_upd->eout_gain = (p_upd->eout_gain > ((dword) l) + 0x10000) ?
      p_upd->eout_gain - l : 0x10000;
  }
  else
  {
    l = (((dword) s) * ((dword)(p_upd->eout_gain >> 16))) << 2;
    p_upd->eout_gain = (((dword) l) < (((dword) SPARSE_LEC_LIMITER_MAX_GAIN) << 16) + 0xffff - p_upd->eout_gain) ?
      p_upd->eout_gain + l : (((dword) SPARSE_LEC_LIMITER_MAX_GAIN) << 16) + 0xffff;
  }
  s = (short)((((int32)(0x8000 - (p_upd->eout_gain >> 16))) * ((int32) SPARSE_LEC_LIMITER_SLOW_SLOPE)) >> 15);
  p_upd->loss_limit += (((int32) s) * ((dword)(p_upd->loss_limit >> 16))) << 1;
  if (p_upd->loss_limit < (((dword)(p_upd->min_loss_limit)) << 16))
    p_upd->loss_limit = ((dword)(p_upd->min_loss_limit)) << 16;
  else if (p_upd->loss_limit > (((dword)(p_upd->max_loss_limit)) << 16) + 0xffff)
    p_upd->loss_limit = (((dword)(p_upd->max_loss_limit)) << 16) + 0xffff;
  if (p_upd->double_talk != 0)
  {
    (p_upd->double_talk)--;
    p_upd->loss_limit = ((dword) SPARSE_LEC_LOSS_LIMIT_DOUBLETALK) << 16;
    p_upd->eout_gain = ((dword) SPARSE_LEC_EXPECTED_RETURN_LOSS) << 16;
  }

  n = (word)(((dword)(((dword)(p_upd->loss_limit >> 16)) * ((dword)(p_upd->max_rin)))) >> 15);
  m = (word)(((dword)(((dword) energy_quot) * ((dword)(p_upd->mu_fraction)))) >> 15);
  for (i = 0; i < SPARSE_LEC_ACTIVE_DEPTH; i++)
  {
    if (step_table[i] > (short) n)
      step_table[i] = n;
    else if (step_table[i] < -((short) n))
      step_table[i] = -((short) n);

    s = (short)(((int32)(((((int32)(step_table[i])) * ((dword) m)) << 1) + 0x8000)) >> 16);
    k = (word)((s >= 0) ? s : -s);
    eout_exp = 1 - sdsp_word_leading_zeroes (k);
    t = (-energy_exp + 1) + p_upd->mu_exponent;
    if (t >= 0)
    {
      if (t > -eout_exp)
        s = (s >= 0) ? 0x7fff : -0x7fff;
      else
        s <<= t;
    }
    else
    {
      if (-t >= 16 + eout_exp)
        s = 0;
      else
        s >>= -t;
    }
    step_table[i] = s;
  }
}


short sparse_lec_exec (t_sparse_lec *p_lec, short Rin, short Sin)
{
  short *delay_ptr, *coef_ptr, *delay_end;
  short Sout, filt, eout;
  word i, j, k, m, n, sect, max_eout, delay_length;
  int32 l, acc_high;
  short s, t, p;
  dword d, acc_low;
  word min_active_sum, min_active_sect, max_passive_sum, max_passive_sect;
  word abs_step_table[SPARSE_LEC_PASSIVE_DEPTH];
  short prop_step_table[SPARSE_LEC_ACTIVE_DEPTH];
  short max_step_table_step[SPARSE_LEC_PASSIVE_STEPS];
  short max_step_table_minus_pos[SPARSE_LEC_PASSIVE_STEPS];
#ifdef SPARSE_LEC_TRACE_
  byte active_table[1 << (SPARSE_LEC_MAX_TOTAL_LENGTH_EXP - SPARSE_LEC_SECTION_LENGTH_EXP)];
  char trace_line[128 + (1 << (SPARSE_LEC_MAX_TOTAL_LENGTH_EXP - SPARSE_LEC_SECTION_LENGTH_EXP)) + SPARSE_LEC_MAX_TAIL_LENGTH / 8];
  char *trace_p;
static byte hex_digit_table[] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
#endif
#ifdef SPARSE_LEC_SIMULATE_ECHO_
#define SIMULATE_ECHO_DELAY  200
static short simulate_echo_buffer[SIMULATE_ECHO_DELAY] = { 0 };
static word simulate_echo_pos = 0;
#endif

#ifdef SPARSE_LEC_SIMULATE_ECHO_
  l = Sin + (simulate_echo_buffer[simulate_echo_pos] >> 1);
  simulate_echo_buffer[simulate_echo_pos] = Rin;
  simulate_echo_pos++;
  if (simulate_echo_pos == SIMULATE_ECHO_DELAY)
    simulate_echo_pos = 0;
  if (l > 32767)
    Sin = 32767;
  else if (l < -32768)
    Sin = -32768;
  else
    Sin = (short) l;
#endif
  Sout = Sin;
  if (!(p_lec->options & DSP_LEC_ENABLE_ECHO_CANCELLER))
    return (Sout);

  l = Rin - (p_lec->rin_dc_offset >> 16);
  if (l < -32768)
    l = -32768;
  else if (l > 32767)
    l = 32767;
  Rin = (short) l;
  p_lec->rin_dc_offset += ((int32) Rin) * ((int32)(2 * 0x0080));

  l = Sin - (p_lec->sin_dc_offset >> 16);
  if (l < -32768)
    l = -32768;
  else if (l > 32767)
    l = 32767;
  Sin = (short) l;
  p_lec->sin_dc_offset += ((int32) Sin) * ((int32)(2 * 0x0080));
  k = (word)((Sin >= 0) ? Sin : -Sin);
  if (k > p_lec->max_sin)
    p_lec->max_sin = k;

  if (p_lec->options & DSP_LEC_ENABLE_2100HZ_DETECTOR)
  {
    ans_detector_exec (&(p_lec->rin_ans_detector), Rin);
    if (ans_detector_exec (&(p_lec->sin_ans_detector), Sin))
    {
      n = p_lec->rin_ans_detector.state | p_lec->sin_ans_detector.state;
      if ((n & ANS_DETECTOR_STATE_G165_DETECTED)
       || ((n & ANS_DETECTOR_STATE_ENERGY_DETECTED)
        && (p_lec->g165_disable_release != 0)))
      {
        p_lec->g165_disable_release = ANS_DETECTOR_RELEASE_TIME;
      }
      if (p_lec->g165_disable_release != 0)
        (p_lec->g165_disable_release)--;

      if ((n & ANS_DETECTOR_STATE_G164_DETECTED)
       || ((n & ANS_DETECTOR_STATE_ENERGY_DETECTED)
        && (p_lec->g164_disable_release != 0)))
      {
        p_lec->g164_disable_release = ANS_DETECTOR_RELEASE_TIME;
      }
      if (p_lec->g164_disable_release != 0)
        (p_lec->g164_disable_release)--;

      p_lec->state &= ~(DSP_LEC_STATE_CONTIGNUOUS_2100HZ | DSP_LEC_STATE_REVERSED_2100HZ | DSP_LEC_STATE_AUTO_DISABLED);
      if (p_lec->g164_disable_release != 0)
      {
        p_lec->state |= DSP_LEC_STATE_CONTIGNUOUS_2100HZ;
        if (!(p_lec->options & DSP_LEC_REQUIRE_2100HZ_REVERSALS))
          p_lec->state |= DSP_LEC_STATE_AUTO_DISABLED;
      }
      if (p_lec->g165_disable_release != 0)
        p_lec->state |= DSP_LEC_STATE_REVERSED_2100HZ | DSP_LEC_STATE_AUTO_DISABLED;
#ifdef ANS_DETECTOR_TRACE_
      if (p_lec->trace_tag)
      {
        p_lec->ans_trace_reduction_counter += ANS_DETECTOR_REDUCTION_RATIO;
        if (p_lec->ans_trace_reduction_counter >= p_lec->total_length)
        {
          p_lec->ans_trace_reduction_counter = 0;
          debug->iprintf("%s: energy %5lu  Phase %6lu/%6lu/%3u/%3u  Cntr %3u/%3u/%3u  state %04x\n", p_lec->trace_tag,
            (long)(p_lec->rin_ans_detector.energy >> 16),
            (long)(p_lec->rin_ans_detector.phase_x >> 16),
            (long)(p_lec->rin_ans_detector.phase_y >> 16),
            (int)(p_lec->rin_ans_detector.increment_offset),
            (int)(p_lec->rin_ans_detector.angle_increment - ANS_DETECTOR_NOM_ANGLE_INCREMENT),
            (int)(p_lec->rin_ans_detector.pass_counter),
            (int)(p_lec->rin_ans_detector.fail_counter),
            (int)(p_lec->rin_ans_detector.pass_reversals),
            (unsigned int)(p_lec->rin_ans_detector.state));
        }
      }
#endif
    }
    if (p_lec->state & DSP_LEC_STATE_AUTO_DISABLED)
      return (Sout);
  }
  if (p_lec->options & DSP_LEC_MANUAL_DISABLE)
    return (Sout);

  delay_length = p_lec->total_length + SPARSE_LEC_MAX_DEPTH;
  p_lec->delay_buffer[p_lec->delay_pos] = Rin;
  if (++(p_lec->delay_pos) == delay_length)
    p_lec->delay_pos = 0;

  l = 0;
  for (sect = 0; sect < (p_lec->tail_length >> SPARSE_LEC_SECTION_LENGTH_EXP); sect++)
  {
    k = p_lec->section_base_buffer[sect];
    coef_ptr = &(p_lec->coef_buffer[k]);
    j = p_lec->delay_pos + k + SPARSE_LEC_MAX_DEPTH;
    if (j >= delay_length)
      j -= delay_length;
    delay_ptr = &(p_lec->delay_buffer[j]);
    if (j + SPARSE_LEC_SECTION_LENGTH <= delay_length)
    {
#if (SPARSE_LEC_SECTION_LENGTH == 16)
      l += ((int32)(delay_ptr[0])) * ((int32)(coef_ptr[0]));
      l += ((int32)(delay_ptr[1])) * ((int32)(coef_ptr[1]));
      l += ((int32)(delay_ptr[2])) * ((int32)(coef_ptr[2]));
      l += ((int32)(delay_ptr[3])) * ((int32)(coef_ptr[3]));
      l += ((int32)(delay_ptr[4])) * ((int32)(coef_ptr[4]));
      l += ((int32)(delay_ptr[5])) * ((int32)(coef_ptr[5]));
      l += ((int32)(delay_ptr[6])) * ((int32)(coef_ptr[6]));
      l += ((int32)(delay_ptr[7])) * ((int32)(coef_ptr[7]));
      l += ((int32)(delay_ptr[8])) * ((int32)(coef_ptr[8]));
      l += ((int32)(delay_ptr[9])) * ((int32)(coef_ptr[9]));
      l += ((int32)(delay_ptr[10])) * ((int32)(coef_ptr[10]));
      l += ((int32)(delay_ptr[11])) * ((int32)(coef_ptr[11]));
      l += ((int32)(delay_ptr[12])) * ((int32)(coef_ptr[12]));
      l += ((int32)(delay_ptr[13])) * ((int32)(coef_ptr[13]));
      l += ((int32)(delay_ptr[14])) * ((int32)(coef_ptr[14]));
      l += ((int32)(delay_ptr[15])) * ((int32)(coef_ptr[15]));
#else
      delay_end = &delay_ptr[SPARSE_LEC_SECTION_LENGTH];
      do
      {
        l += ((int32)(*(delay_ptr++))) * ((int32)(*(coef_ptr++)));
      } while (delay_ptr < delay_end);
#endif
    }
    else
    {
      delay_end = &delay_ptr[delay_length - j];
      do
      {
        l += ((int32)(*(delay_ptr++))) * ((int32)(*(coef_ptr++)));
      } while (delay_ptr < delay_end);
      delay_ptr = p_lec->delay_buffer;
      delay_end = &delay_ptr[SPARSE_LEC_SECTION_LENGTH - (delay_length - j)];
      do
      {
        l += ((int32)(*(delay_ptr++))) * ((int32)(*(coef_ptr++)));
      } while (delay_ptr < delay_end);
    }
  }
  l <<= 1;
  filt = (short)((l + 0x8000) >> 16);
  l = ((int32) Sin) - ((int32) filt);
  if (l > 32767)
    l = 32767;
  else if (l < -32768)
    l = -32768;
  eout = (short) l;

/**/
/*eout = Sin;*/

  p_lec->active_step_table[p_lec->active_step_number] = eout;
  if (++(p_lec->active_step_number) == SPARSE_LEC_ACTIVE_DEPTH)
  {
    p_lec->active_step_number = 0;

    max_eout = 0;
    for (i = 0; i < SPARSE_LEC_ACTIVE_DEPTH; i++)
    {
      p_lec->passive_step_table[p_lec->passive_step_number + i] = p_lec->active_step_table[i];
      k = (word)((p_lec->active_step_table[i] >= 0) ? p_lec->active_step_table[i] : - p_lec->active_step_table[i]);
      if (k > max_eout)
        max_eout = k;
    }

    if (p_lec->active.rin_energy_low < p_lec->active_rin_energy_low_table[p_lec->section_offset])
      (p_lec->active.rin_energy_high)--;
    p_lec->active.rin_energy_low -= p_lec->active_rin_energy_low_table[p_lec->section_offset];
    p_lec->active.rin_energy_high -= p_lec->active_rin_energy_high_table[p_lec->section_offset];
    acc_low = 0;
    acc_high = 0;
    for (sect = 0; sect < (p_lec->tail_length >> SPARSE_LEC_SECTION_LENGTH_EXP); sect++)
    {
      p = p_lec->proportionate_buffer[sect];
      j = p_lec->delay_pos + p_lec->section_base_buffer[sect] + SPARSE_LEC_MAX_DEPTH + SPARSE_LEC_SECTION_LENGTH - 1;
      if (j >= delay_length)
        j -= delay_length;
      if (j >= SPARSE_LEC_ACTIVE_DEPTH - 1)
      {
#if (SPARSE_LEC_ACTIVE_DEPTH == 4)
        t = p_lec->delay_buffer[j - 3];
        s = (short)(((int32)(((int32) p) * ((int32) t))) >> 15);
        d = (((int32) t) * ((int32) s));
        acc_low += d;
        if (acc_low < d)
          acc_high++;
        t = p_lec->delay_buffer[j - 2];
        s = (short)(((int32)(((int32) p) * ((int32) t))) >> 15);
        d = (((int32) t) * ((int32) s));
        acc_low += d;
        if (acc_low < d)
          acc_high++;
        t = p_lec->delay_buffer[j - 1];
        s = (short)(((int32)(((int32) p) * ((int32) t))) >> 15);
        d = (((int32) t) * ((int32) s));
        acc_low += d;
        if (acc_low < d)
          acc_high++;
        t = p_lec->delay_buffer[j];
        s = (short)(((int32)(((int32) p) * ((int32) t))) >> 15);
        d = (((int32) t) * ((int32) s));
        acc_low += d;
        if (acc_low < d)
          acc_high++;
#else
        for (i = 0; i < SPARSE_LEC_ACTIVE_DEPTH; i++)
        {
          t = p_lec->delay_buffer[j - (SPARSE_LEC_ACTIVE_DEPTH - 1 - i)];
          s = (short)(((int32)(((int32) p) * ((int32) t))) >> 15);
          d = (((int32) t) * ((int32) s));
          acc_low += d;
          if (acc_low < d)
            acc_high++;
        }
#endif
      }
      else
      {
        for (i = 0; i < SPARSE_LEC_ACTIVE_DEPTH; i++)
        {
          t = p_lec->delay_buffer[((j >= SPARSE_LEC_ACTIVE_DEPTH - 1 - i) ? j : j + delay_length) - (SPARSE_LEC_ACTIVE_DEPTH - 1 - i)];
          s = (short)(((int32)(((int32) p) * ((int32) t))) >> 15);
          d = (((int32) t) * ((int32) s));
          acc_low += d;
          if (acc_low < d)
            acc_high++;
        }
      }
    }
    acc_high = (acc_high << 1) | (acc_low >> 31);
    acc_low <<= 1;
    p_lec->active_rin_energy_low_table[p_lec->section_offset] = acc_low;
    p_lec->active_rin_energy_high_table[p_lec->section_offset] = acc_high;

    if (++(p_lec->section_offset) == SPARSE_LEC_SECTION_BLOCKS)
      p_lec->section_offset = 0;
    p_lec->active.rin_energy_low += acc_low;
    if (p_lec->active.rin_energy_low < acc_low)
      (p_lec->active.rin_energy_high)++;
    p_lec->active.rin_energy_high += acc_high;

    j = p_lec->delay_pos + SPARSE_LEC_MAX_DEPTH - SPARSE_LEC_ACTIVE_DEPTH;
    if (j >= delay_length)
      j -= delay_length;
    for (i = 0; i < SPARSE_LEC_ACTIVE_DEPTH; i++)
    {
      d = (((int32)(p_lec->delay_buffer[j])) * ((int32)(p_lec->delay_buffer[j]))) << 1;
      if (p_lec->passive.rin_energy_low < d)
        (p_lec->passive.rin_energy_high)--;
      p_lec->passive.rin_energy_low -= d;
      if (++j == delay_length)
        j = 0;
    }
    k = 0;
    j = p_lec->delay_pos + delay_length - SPARSE_LEC_ACTIVE_DEPTH;
    if (j >= delay_length)
      j -= delay_length;
    for (i = 0; i < SPARSE_LEC_ACTIVE_DEPTH; i++)
    {
      n = (word)((p_lec->delay_buffer[j] >= 0) ? p_lec->delay_buffer[j] : -p_lec->delay_buffer[j]);
      if (n > k)
        k = n;
      d = (((int32)(p_lec->delay_buffer[j])) * ((int32)(p_lec->delay_buffer[j]))) << 1;
      p_lec->passive.rin_energy_low += d;
      if (p_lec->passive.rin_energy_low < d)
        (p_lec->passive.rin_energy_high)++;
      if (++j == delay_length)
        j = 0;
    }

    if (k > p_lec->max_rin)
    {
      p_lec->max_rin = k;
      if (p_lec->first_section_active) {
        j = (word)(((dword)(((dword)(p_lec->first_section_proportionate)) * ((dword) k))) >> 15);
        if (j > p_lec->active.max_rin)
          p_lec->active.max_rin = j;
      }
      if (k > p_lec->passive.max_rin)
        p_lec->passive.max_rin = k;
    }
    if (p_lec->section_offset == 0)
    {
      if (p_lec->max_rin > p_lec->activity_max_rin)
        p_lec->activity_max_rin = p_lec->max_rin;
      if (++(p_lec->activity_counter) == SPARSE_LEC_ACTIVITY_INTERVAL)
      {
        p_lec->activity_counter = 0;
        if ((((word)(((dword)(((dword)(p_lec->activity_max_rin)) * ((dword) SPARSE_LEC_ACTIVITY_RATIO))) >> 15)) > p_lec->activity_max_rin_2)
         || (((word)(((dword)(((dword)(p_lec->activity_max_rin_2)) * ((dword) SPARSE_LEC_ACTIVITY_RATIO))) >> 15)) > p_lec->activity_max_rin))
        {
          p_lec->active.min_loss_limit = SPARSE_LEC_MIN_LOSS_LIMIT;
          p_lec->passive.min_loss_limit = SPARSE_LEC_MIN_LOSS_LIMIT;
        }
        else
        {
          p_lec->active.min_loss_limit = SPARSE_LEC_MIN_LOSS_LIMIT_INACTIVE;
          p_lec->passive.min_loss_limit = SPARSE_LEC_MIN_LOSS_LIMIT_INACTIVE;
        }
        p_lec->activity_max_rin_2 = p_lec->activity_max_rin;
        p_lec->activity_max_rin = 0;
      }
      j = p_lec->max_rin_pos + (p_lec->total_length >> SPARSE_LEC_SECTION_LENGTH_EXP) - 1;
      if (j >= (p_lec->total_length >> SPARSE_LEC_SECTION_LENGTH_EXP))
        j -= (p_lec->total_length >> SPARSE_LEC_SECTION_LENGTH_EXP);
      if (p_lec->max_rin > p_lec->max_rin_buffer[j])
        p_lec->max_rin_buffer[j] = p_lec->max_rin;
      p_lec->max_rin_buffer[p_lec->max_rin_pos] = p_lec->max_rin;
      if (++p_lec->max_rin_pos == (p_lec->total_length >> SPARSE_LEC_SECTION_LENGTH_EXP))
        p_lec->max_rin_pos = 0;
      p_lec->max_rin = SPARSE_LEC_MAX_RIN_BIAS;
      p_lec->active.max_rin = SPARSE_LEC_MAX_RIN_BIAS;
      for (sect = 0; sect < (p_lec->tail_length >> SPARSE_LEC_SECTION_LENGTH_EXP); sect++)
      {
        j = p_lec->max_rin_pos + (p_lec->section_base_buffer[sect] >> SPARSE_LEC_SECTION_LENGTH_EXP);
        if (j >= (p_lec->total_length >> SPARSE_LEC_SECTION_LENGTH_EXP))
          j -= (p_lec->total_length >> SPARSE_LEC_SECTION_LENGTH_EXP);
        k = (word)(((dword)(((dword)(p_lec->proportionate_buffer[sect])) * ((dword)(p_lec->max_rin_buffer[j])))) >> 15);
        if (k > p_lec->active.max_rin)
          p_lec->active.max_rin = k;
      }
      p_lec->passive.max_rin = SPARSE_LEC_MAX_RIN_BIAS;
      j = p_lec->max_rin_pos;
      for (i = 0; i < (p_lec->total_length >> SPARSE_LEC_SECTION_LENGTH_EXP); i += 2)
      {
        if (p_lec->max_rin_buffer[j] > p_lec->passive.max_rin)
          p_lec->passive.max_rin = p_lec->max_rin_buffer[j];
        j += 2;
        if (j >= (p_lec->total_length >> SPARSE_LEC_SECTION_LENGTH_EXP))
          j -= (p_lec->total_length >> SPARSE_LEC_SECTION_LENGTH_EXP);
      }
    }

    sparse_lec_update (&(p_lec->active), p_lec->active_step_table, p_lec->max_sin, max_eout);

    sparse_lec_update (&(p_lec->passive),
      &(p_lec->passive_step_table[p_lec->passive_step_number]), p_lec->max_sin, max_eout);

    p_lec->passive_step_number += SPARSE_LEC_ACTIVE_DEPTH;
    if (p_lec->passive_step_number == SPARSE_LEC_PASSIVE_DEPTH)
      p_lec->passive_step_number = 0;

    if (!(p_lec->options & DSP_LEC_FREEZE_COEFFICIENTS))
    {
      for (sect = 0; sect < (p_lec->tail_length >> SPARSE_LEC_SECTION_LENGTH_EXP); sect++)
      {
        k = p_lec->proportionate_buffer[sect];
        for (i = 0; i < SPARSE_LEC_ACTIVE_DEPTH; i++)
        {
          prop_step_table[i] = (short)(((int32)(((((int32)(p_lec->active_step_table[i])) * ((int32) k)) << 1) + 0x8000)) >> 16);
        }
        k = p_lec->section_base_buffer[sect];
        coef_ptr = &(p_lec->coef_buffer[k]);
        j = p_lec->delay_pos + k + SPARSE_LEC_MAX_DEPTH;
        if (j >= delay_length)
          j -= delay_length;
        if ((j >= SPARSE_LEC_ACTIVE_DEPTH - 1) && (j + SPARSE_LEC_SECTION_LENGTH <= delay_length))
        {
          delay_ptr = &(p_lec->delay_buffer[j - (SPARSE_LEC_ACTIVE_DEPTH - 1)]);
          for (i = 0; i < SPARSE_LEC_SECTION_LENGTH; i++)
          {
            l = 0;
#if (SPARSE_LEC_ACTIVE_DEPTH == 4)
            l += ((int32)(((int32)(prop_step_table[0])) * ((int32)(delay_ptr[0])))) >> (SPARSE_LEC_ACTIVE_DEPTH_EXP - 1);
            l += ((int32)(((int32)(prop_step_table[1])) * ((int32)(delay_ptr[1])))) >> (SPARSE_LEC_ACTIVE_DEPTH_EXP - 1);
            l += ((int32)(((int32)(prop_step_table[2])) * ((int32)(delay_ptr[2])))) >> (SPARSE_LEC_ACTIVE_DEPTH_EXP - 1);
            l += ((int32)(((int32)(prop_step_table[3])) * ((int32)(delay_ptr[3])))) >> (SPARSE_LEC_ACTIVE_DEPTH_EXP - 1);
#else
            for (n = 0; n < SPARSE_LEC_ACTIVE_DEPTH; n++) {
              l += ((int32)(((int32)(prop_step_table[n])) * ((int32)(delay_ptr[n])))) >> (SPARSE_LEC_ACTIVE_DEPTH_EXP - 1);
            }
#endif
            l = (l + (0x4000 >> (SPARSE_LEC_ACTIVE_DEPTH_EXP - 1))) >> (15 - (SPARSE_LEC_ACTIVE_DEPTH_EXP - 1));
            l += *coef_ptr;
            if (l > 32767)
              l = 32767;
            else if (l < -32768)
              l = -32768;
            *(coef_ptr++) = (short) l;
            delay_ptr++;
          }
        }
        else
        {
          for (i = 0; i < SPARSE_LEC_SECTION_LENGTH; i++)
          {
            delay_ptr = &(p_lec->delay_buffer[j - (SPARSE_LEC_ACTIVE_DEPTH - 1)]);
            l = 0;
            for (n = 0; n < SPARSE_LEC_ACTIVE_DEPTH; n++) {
              l += ((int32)(((int32)(prop_step_table[n])) * ((int32)(delay_ptr[(j >= SPARSE_LEC_ACTIVE_DEPTH - 1 - n) ? n : delay_length + n])))) >> (SPARSE_LEC_ACTIVE_DEPTH_EXP - 1);
            }
            l = (l + (0x4000 >> (SPARSE_LEC_ACTIVE_DEPTH_EXP - 1))) >> (15 - (SPARSE_LEC_ACTIVE_DEPTH_EXP - 1));
            l += *coef_ptr;
            if (l > 32767)
              l = 32767;
            else if (l < -32768)
              l = -32768;
            *(coef_ptr++) = (short) l;
            if (++j == delay_length)
              j = 0;
          }
        }
      }

      if (p_lec->passive_step_number == 0)
      {
        for (i = 0; i < SPARSE_LEC_PASSIVE_DEPTH; i++)
        {
          s = p_lec->passive_step_table[i];
          abs_step_table[i] = (word)((s >= 0) ? s : -s);
        }
        for (n = 0; n < SPARSE_LEC_PASSIVE_STEPS; n++)
        {
          j = 0;
          k = 0;
          for (i = 0; i < SPARSE_LEC_PASSIVE_DEPTH; i++)
          {
            if (abs_step_table[i] > k)
            {
              k = abs_step_table[i];
              j = i;
            }
          }
          max_step_table_minus_pos[n] = -(SPARSE_LEC_PASSIVE_DEPTH - 1 - j);
          max_step_table_step[n] = p_lec->passive_step_table[j];
          abs_step_table[j] = 0;
        }
        for (sect = (p_lec->tail_length >> SPARSE_LEC_SECTION_LENGTH_EXP); sect < (p_lec->total_length >> SPARSE_LEC_SECTION_LENGTH_EXP); sect++)
        {
          k = p_lec->section_base_buffer[sect];
          coef_ptr = &(p_lec->coef_buffer[k]);
          j = p_lec->delay_pos + k + SPARSE_LEC_MAX_DEPTH;
          if (j >= delay_length)
            j -= delay_length;
          if ((j >= SPARSE_LEC_PASSIVE_DEPTH - 1) && (j + SPARSE_LEC_SECTION_LENGTH <= delay_length))
          {
            delay_ptr = &(p_lec->delay_buffer[j]);
            for (i = 0; i < SPARSE_LEC_SECTION_LENGTH; i++)
            {
              l = 0;
#if (SPARSE_LEC_PASSIVE_STEPS == 6)
              l += ((int32)(((int32)(max_step_table_step[0])) * ((int32)(delay_ptr[max_step_table_minus_pos[0]])))) >> (SPARSE_LEC_PASSIVE_STEPS_EXP - 1);
              l += ((int32)(((int32)(max_step_table_step[1])) * ((int32)(delay_ptr[max_step_table_minus_pos[1]])))) >> (SPARSE_LEC_PASSIVE_STEPS_EXP - 1);
              l += ((int32)(((int32)(max_step_table_step[2])) * ((int32)(delay_ptr[max_step_table_minus_pos[2]])))) >> (SPARSE_LEC_PASSIVE_STEPS_EXP - 1);
              l += ((int32)(((int32)(max_step_table_step[3])) * ((int32)(delay_ptr[max_step_table_minus_pos[3]])))) >> (SPARSE_LEC_PASSIVE_STEPS_EXP - 1);
              l += ((int32)(((int32)(max_step_table_step[4])) * ((int32)(delay_ptr[max_step_table_minus_pos[4]])))) >> (SPARSE_LEC_PASSIVE_STEPS_EXP - 1);
              l += ((int32)(((int32)(max_step_table_step[5])) * ((int32)(delay_ptr[max_step_table_minus_pos[5]])))) >> (SPARSE_LEC_PASSIVE_STEPS_EXP - 1);
#else
              for (n = 0; n < SPARSE_LEC_PASSIVE_STEPS; n++) {
                l += ((int32)(((int32)(max_step_table_step[n])) * ((int32)(delay_ptr[max_step_table_minus_pos[n]])))) >> (SPARSE_LEC_PASSIVE_STEPS_EXP - 1);
              }
#endif
              l = (l + (0x4000 >> (SPARSE_LEC_PASSIVE_STEPS_EXP - 1))) >> (15 - (SPARSE_LEC_PASSIVE_STEPS_EXP - 1));
              l += *coef_ptr;
              if (l > 32767)
                l = 32767;
              else if (l < -32768)
                l = -32768;
              *(coef_ptr++) = (short) l;
              delay_ptr++;
            }
          }
          else
          {
            for (i = 0; i < SPARSE_LEC_SECTION_LENGTH; i++)
            {
              delay_ptr = &(p_lec->delay_buffer[j]);
              l = 0;
              for (n = 0; n < SPARSE_LEC_PASSIVE_STEPS; n++)
              {
                s = max_step_table_minus_pos[n];
                l += ((int32)(((int32)(max_step_table_step[n])) * ((int32)(delay_ptr[(-j <= s) ? s : delay_length + s])))) >> (SPARSE_LEC_PASSIVE_STEPS_EXP - 1);
              }
              l = (l + (0x4000 >> (SPARSE_LEC_PASSIVE_STEPS_EXP - 1))) >> (15 - (SPARSE_LEC_PASSIVE_STEPS_EXP - 1));
              l += *coef_ptr;
              if (l > 32767)
                l = 32767;
              else if (l < -32768)
                l = -32768;
              *(coef_ptr++) = (short) l;
              if (++j == delay_length)
                j = 0;
            }
          }
        }
        for (sect = 0; sect < (p_lec->total_length >> SPARSE_LEC_SECTION_LENGTH_EXP); sect++)
        {
          d = 0;
          k = p_lec->section_base_buffer[sect];
          for (i = 0; i < SPARSE_LEC_SECTION_LENGTH; i++)
          {
            s = p_lec->coef_buffer[k];
            d += (word)((s >= 0) ? s : -s);
            k++;
          }
          p_lec->coef_sum_buffer[sect] = (word)(d >> SPARSE_LEC_SECTION_LENGTH_EXP);
          if (p_lec->coef_sum_buffer[sect] > 0x7fff)
            p_lec->coef_sum_buffer[sect] = 0x7fff;
        }

        min_active_sum = 0xffff;
        min_active_sect = 0;
        for (sect = 0; sect < (p_lec->tail_length >> SPARSE_LEC_SECTION_LENGTH_EXP); sect++)
        {
          if (p_lec->coef_sum_buffer[sect] < min_active_sum)
          {
            min_active_sum = p_lec->coef_sum_buffer[sect];
            min_active_sect = sect;
          }
        }
        max_passive_sum = 0;
        max_passive_sect = 0;
        for (sect = (p_lec->tail_length >> SPARSE_LEC_SECTION_LENGTH_EXP); sect < (p_lec->total_length >> SPARSE_LEC_SECTION_LENGTH_EXP); sect++)
        {
          if (p_lec->coef_sum_buffer[sect] > max_passive_sum)
          {
            max_passive_sum = p_lec->coef_sum_buffer[sect];
            max_passive_sect = sect;
          }
        }
#ifdef SPARSE_LEC_TRACE_
        trace_p = trace_line;
        if (p_lec->trace_tag)
        {
          p_lec->lec_trace_reduction_counter += SPARSE_LEC_PASSIVE_DEPTH;
          if (p_lec->lec_trace_reduction_counter >= p_lec->total_length)
          {
            p_lec->lec_trace_reduction_counter = 0;
            trace_p += _sprintf (trace_p, "NLP %5i, Lim %5u Act %3u/%5lu/%5lu Pas %3u/%5lu/%5lu SectExp ",
              (int)((short)(p_lec->nlp_level >> 16)), (int)(p_lec->active.max_loss_limit),
              (int)(p_lec->active.double_talk), (long)(p_lec->active.eout_gain >> 16), (long)(p_lec->active.loss_limit >> 16),
              (int)(p_lec->passive.double_talk), (long)(p_lec->passive.eout_gain >> 16), (long)(p_lec->passive.loss_limit >> 16));

            for (sect = 0; sect < (p_lec->total_length >> SPARSE_LEC_SECTION_LENGTH_EXP); sect++)
            {
              k = (p_lec->section_base_buffer[sect] >> SPARSE_LEC_SECTION_LENGTH_EXP);
              n = sdsp_word_leading_zeroes (p_lec->coef_sum_buffer[sect]);
              if (n > 15)
                n = 15;
              if (sect < (p_lec->tail_length >> SPARSE_LEC_SECTION_LENGTH_EXP))
                n |= 0x80;
              active_table[k] = (byte) n;
            }
            for (i = 0; i < (p_lec->total_length >> SPARSE_LEC_SECTION_LENGTH_EXP); i++)
            {
              if (active_table[i] & 0x80)
                *(trace_p++) = '*';
              *(trace_p++) = hex_digit_table[active_table[i] & 0xf];
            }
          }
        }
#endif
        if (max_passive_sum > min_active_sum)
        {
#ifdef SPARSE_LEC_TRACE_
/*
          if (p_lec->trace_tag && (p_lec->lec_trace_reduction_counter == 0))
            p += _sprintf (p, " %3u<->%3u", (int) min_active_sect, (int) max_passive_sect);
*/
#endif
          if (p_lec->section_base_buffer[min_active_sect] == p_lec->total_length - SPARSE_LEC_SECTION_LENGTH)
            p_lec->first_section_active = FALSE;
          if (p_lec->section_base_buffer[max_passive_sect] == p_lec->total_length - SPARSE_LEC_SECTION_LENGTH)
            p_lec->first_section_active = TRUE;

          k = p_lec->section_base_buffer[min_active_sect];
          p_lec->section_base_buffer[min_active_sect] = p_lec->section_base_buffer[max_passive_sect];
          p_lec->section_base_buffer[max_passive_sect] = k;
          p_lec->coef_sum_buffer[min_active_sect] = p_lec->coef_sum_buffer[max_passive_sect];

          p_lec->active.max_rin = SPARSE_LEC_MAX_RIN_BIAS;
          for (sect = 0; sect < (p_lec->tail_length >> SPARSE_LEC_SECTION_LENGTH_EXP); sect++)
          {
            j = p_lec->max_rin_pos + (p_lec->section_base_buffer[sect] >> SPARSE_LEC_SECTION_LENGTH_EXP);
            if (j >= (p_lec->total_length >> SPARSE_LEC_SECTION_LENGTH_EXP))
              j -= (p_lec->total_length >> SPARSE_LEC_SECTION_LENGTH_EXP);
            k = (word)(((dword)(((dword)(p_lec->proportionate_buffer[sect])) * ((dword)(p_lec->max_rin_buffer[j])))) >> 15);
            if (k > p_lec->active.max_rin)
              p_lec->active.max_rin = k;
          }
#ifdef __ANDROID__
          p_lec->passive.mu_exponent = -SPARSE_LEC_MAX_TOTAL_LENGTH_EXP - 1 - 1;
#else
          p_lec->passive.mu_exponent = -SPARSE_LEC_MAX_TOTAL_LENGTH_EXP - 1 - 3;
#endif
        }
        else
        {
#ifdef SPARSE_LEC_TRACE_
/*
          if (p_lec->trace_tag && (p_lec->lec_trace_reduction_counter == 0))
            trace_p += _sprintf (trace_p, "    <->   ");
*/
#endif
#ifdef __ANDROID__
          p_lec->passive.mu_exponent = -SPARSE_LEC_MAX_TOTAL_LENGTH_EXP - 1 - 1 + 2;
#else
          p_lec->passive.mu_exponent = -SPARSE_LEC_MAX_TOTAL_LENGTH_EXP - 1 - 3 + 2;
#endif
        }
        k = 0x0001 << SPARSE_LEC_PROPORTIONATE_EXP;
        for (sect = 0; sect < (p_lec->tail_length >> SPARSE_LEC_SECTION_LENGTH_EXP); sect++)
        {
          if (p_lec->coef_sum_buffer[sect] > k)
            k = p_lec->coef_sum_buffer[sect];
        }
        s = 1 - sdsp_word_leading_zeroes (k);
        n = (s > -3) ? SPARSE_LEC_EXPECTED_RETURN_LOSS :
          (((-(3 + s) & 1) ? (((int32)(((int32) SPARSE_LEC_EXPECTED_RETURN_LOSS) * ((int32) 0x5a82))) >> 15) : SPARSE_LEC_EXPECTED_RETURN_LOSS) >> ((-(3 + s)) >> 1));
        p_lec->active.max_loss_limit = n;
        n = ((word)(((dword)(0x3fffffff / ((dword)(k << -s)))) >> 1)) << 1;
        k >>= SPARSE_LEC_PROPORTIONATE_EXP;
        for (sect = 0; sect < (p_lec->tail_length >> SPARSE_LEC_SECTION_LENGTH_EXP); sect++)
        {
          if (p_lec->coef_sum_buffer[sect] < k)
            p_lec->proportionate_buffer[sect] = 0x7fff >> SPARSE_LEC_PROPORTIONATE_EXP;
          else
          {
            m = p_lec->coef_sum_buffer[sect] << -s;
            p_lec->proportionate_buffer[sect] = (word)(((dword)(((dword) m) * ((dword) n))) >> 15);
          }
          if (p_lec->section_base_buffer[sect] == p_lec->total_length - SPARSE_LEC_SECTION_LENGTH)
            p_lec->first_section_proportionate = p_lec->proportionate_buffer[sect];
        }

        p_lec->active.rin_energy_low = 0;
        p_lec->active.rin_energy_high = 0;
        for (i = 0; i < (p_lec->tail_length >> SPARSE_LEC_SECTION_LENGTH_EXP); i++)
          p_lec->active.rin_energy_low += (((dword)(p_lec->proportionate_buffer[i])) * ((dword) SPARSE_LEC_ACTIVE_RIN_ENERGY_BIAS)) >> 15;
        d = (word)(((dword)((p_lec->active.loss_limit >> 16) * (p_lec->active.loss_limit >> 16))) << 1);
        if (d < ((((dword) SPARSE_LEC_MIN_LOSS_LIMIT) * ((dword) SPARSE_LEC_MIN_LOSS_LIMIT)) << 1))
          d = (((dword) SPARSE_LEC_MIN_LOSS_LIMIT) * ((dword) SPARSE_LEC_MIN_LOSS_LIMIT)) << 1;
        s = 1 - sdsp_dword_leading_zeroes (d);
        d <<= -s;
        n = (word)(((dword)(0x3fffffff / (d >> 16))) >> 1);
        p_lec->active.rin_energy_low = (((dword)((((dword)(p_lec->active.rin_energy_low & 0xffff)) * ((dword) n)))) >> 15) +
          ((((dword)(p_lec->active.rin_energy_low >> 16)) * ((dword) n)) << 1);;
        p_lec->active.rin_energy_high = p_lec->active.rin_energy_low >> (32 - (2 - s));
        p_lec->active.rin_energy_low <<= 2 - s;
        for (p_lec->section_offset = 0; p_lec->section_offset < SPARSE_LEC_SECTION_BLOCKS; p_lec->section_offset++)
        {
          acc_low = 0;
          acc_high = 0;
          for (sect = 0; sect < (p_lec->tail_length >> SPARSE_LEC_SECTION_LENGTH_EXP); sect++)
          {
            p = p_lec->proportionate_buffer[sect];
            j = p_lec->delay_pos + p_lec->section_base_buffer[sect] + (p_lec->section_offset << SPARSE_LEC_ACTIVE_DEPTH_EXP) +
              SPARSE_LEC_MAX_DEPTH + SPARSE_LEC_ACTIVE_DEPTH - 1;
            if (j >= delay_length)
              j -= delay_length;
            if (j >= SPARSE_LEC_ACTIVE_DEPTH - 1)
            {
#if (SPARSE_LEC_ACTIVE_DEPTH == 4)
              t = p_lec->delay_buffer[j - 3];
              s = (short)(((int32)(((int32) p) * ((int32) t))) >> 15);
              d = (((int32) t) * ((int32) s));
              acc_low += d;
              if (acc_low < d)
                acc_high++;
              t = p_lec->delay_buffer[j - 2];
              s = (short)(((int32)(((int32) p) * ((int32) t))) >> 15);
              d = (((int32) t) * ((int32) s));
              acc_low += d;
              if (acc_low < d)
                acc_high++;
              t = p_lec->delay_buffer[j - 1];
              s = (short)(((int32)(((int32) p) * ((int32) t))) >> 15);
              d = (((int32) t) * ((int32) s));
              acc_low += d;
              if (acc_low < d)
                acc_high++;
              t = p_lec->delay_buffer[j];
              s = (short)(((int32)(((int32) p) * ((int32) t))) >> 15);
              d = (((int32) t) * ((int32) s));
              acc_low += d;
              if (acc_low < d)
                acc_high++;
#else
              for (i = 0; i < SPARSE_LEC_ACTIVE_DEPTH; i++)
              {
                t = p_lec->delay_buffer[j - (SPARSE_LEC_ACTIVE_DEPTH - 1 - i)];
                s = (short)(((int32)(((int32) p) * ((int32) t))) >> 15);
                d = (((int32) t) * ((int32) s));
                acc_low += d;
                if (acc_low < d)
                  acc_high++;
              }
#endif
            }
            else
            {
              for (i = 0; i < SPARSE_LEC_ACTIVE_DEPTH; i++)
              {
                t = p_lec->delay_buffer[((j >= SPARSE_LEC_ACTIVE_DEPTH - 1 - i) ? j : j + delay_length) - (SPARSE_LEC_ACTIVE_DEPTH - 1 - i)];
                s = (short)(((int32)(((int32) p) * ((int32) t))) >> 15);
                d = (((int32) t) * ((int32) s));
                acc_low += d;
                if (acc_low < d)
                  acc_high++;
              }
            }
          }
          acc_high = (acc_high << 1) | (acc_low >> 31);
          acc_low <<= 1;
          p_lec->active_rin_energy_low_table[p_lec->section_offset] = acc_low;
          p_lec->active_rin_energy_high_table[p_lec->section_offset] = acc_high;
          p_lec->active.rin_energy_low += acc_low;
          if (p_lec->active.rin_energy_low < acc_low)
            (p_lec->active.rin_energy_high)++;
          p_lec->active.rin_energy_high += acc_high;
        }
        p_lec->section_offset = 0;
#ifdef SPARSE_LEC_TRACE_
        if (p_lec->trace_tag && (p_lec->lec_trace_reduction_counter == 0))
        {
          *trace_p = '\0';
//          printf("%s: %s", p_lec->trace_tag, trace_line);
#ifdef __ANDROID__
          sdsp_printf("%s: %s", p_lec->trace_tag, trace_line);
#else
          debug->iprintf("%s: %s", p_lec->trace_tag, trace_line);
#endif
        }
#endif
      }
    }
    n = (word)(p_lec->active.loss_limit >> 16);
    if (n > p_lec->active.max_loss_limit)
      n = p_lec->active.max_loss_limit;
    n = (word)(((dword)(((dword) n) * ((dword)(p_lec->active.max_rin)))) >> 15);
    n = (word)(((dword)(((dword) n) *
      ((dword)((p_lec->nlp_level < 0) ? 2 * SPARSE_LEC_NLP_THRESHOLD : SPARSE_LEC_NLP_THRESHOLD)))) >> 15);
    s = max_eout - n;
    p_lec->nlp_level += (((int32)(s - (p_lec->nlp_level >> 16))) * ((int32) SPARSE_LEC_NLP_LEVEL_SLOPE)) << 1;
    p_lec->max_sin = 0;
  }
  Sout = eout;
  if (p_lec->options & DSP_LEC_ENABLE_NONLINEAR_PROCESSING)
  {
    if (p_lec->options & DSP_LEC_ENABLE_COMFORT_NOISE_GENERATION)
    {
      sdsp_fill_gaussian_noise (&s, 1, (word)(p_lec->cng_lowpass_gain >> 16), &p_lec->cng_random_number_seed);
      p_lec->cng_lowpass_accu += (((int32)(s - (p_lec->cng_lowpass_accu >> 16))) * ((int32)(p_lec->cng_lowpass_slope >> 16))) << 1;
      t = (short)(p_lec->cng_lowpass_accu >> 16);

      p_lec->cng_eout_energy += (((int32)(((int32) eout) * ((int32) eout))) >> SPARSE_LEC_CNG_HISTORY_SIZE_EXP) -
          (((int32)(((int32)(p_lec->cng_eout_history[p_lec->cng_history_pos])) * ((int32)(p_lec->cng_eout_history[p_lec->cng_history_pos])))) >> SPARSE_LEC_CNG_HISTORY_SIZE_EXP);
      p_lec->cng_noise_energy += (((int32)(((int32) t) * ((int32) t))) >> SPARSE_LEC_CNG_HISTORY_SIZE_EXP) -
          (((int32)(((int32)(p_lec->cng_noise_history[p_lec->cng_history_pos])) * ((int32)(p_lec->cng_noise_history[p_lec->cng_history_pos])))) >> SPARSE_LEC_CNG_HISTORY_SIZE_EXP);
      i = (p_lec->cng_history_pos != 0) ? p_lec->cng_history_pos - 1 : SPARSE_LEC_CNG_HISTORY_SIZE - 1;
      j = (p_lec->cng_history_pos < SPARSE_LEC_CNG_HISTORY_SIZE - 1) ? p_lec->cng_history_pos + 1 : 0;
      p_lec->cng_eout_hp_energy += ((eout >= p_lec->cng_eout_history[i]) ? eout - p_lec->cng_eout_history[i] : p_lec->cng_eout_history[i] - eout) -
          ((p_lec->cng_eout_history[j] >= p_lec->cng_eout_history[p_lec->cng_history_pos]) ?
            p_lec->cng_eout_history[j] - p_lec->cng_eout_history[p_lec->cng_history_pos] : p_lec->cng_eout_history[p_lec->cng_history_pos] - p_lec->cng_eout_history[j]);
      p_lec->cng_noise_hp_energy += ((t >= p_lec->cng_noise_history[i]) ? t - p_lec->cng_noise_history[i] : p_lec->cng_noise_history[i] - t) -
          ((p_lec->cng_noise_history[j] >= p_lec->cng_noise_history[p_lec->cng_history_pos]) ?
            p_lec->cng_noise_history[j] - p_lec->cng_noise_history[p_lec->cng_history_pos] : p_lec->cng_noise_history[p_lec->cng_history_pos] - p_lec->cng_noise_history[j]);
      if (p_lec->nlp_level < 0)
      {
        if (!p_lec->cng_active)
        {
          if (((short)((eout - t) ^ (p_lec->cng_eout_history[i] - t))) < 0)
            p_lec->cng_active = true;
        }
      }
      else
      {
        if (((p_lec->nlp_level >> 16) <= SPARSE_LEC_CNG_MAX_LEVEL)
         && (p_lec->cng_eout_energy <= SPARSE_LEC_CNG_MAX_LEVEL * SPARSE_LEC_CNG_MAX_LEVEL))
        {
          if (p_lec->cng_eout_energy >= p_lec->cng_noise_energy)
          {
            p_lec->cng_lowpass_gain += (((dword)(p_lec->cng_lowpass_gain >> 16)) * ((dword) SPARSE_LEC_CNG_ADAPTATION_SLOPE)) << 1;
            if (p_lec->cng_lowpass_gain > (((dword) 0x7fff) << 16))
              p_lec->cng_lowpass_gain = ((dword) 0x7fff) << 16;
          }
          else
          {
            p_lec->cng_lowpass_gain -= (((dword)(p_lec->cng_lowpass_gain >> 16)) * ((dword) SPARSE_LEC_CNG_ADAPTATION_SLOPE)) << 1;
            if (p_lec->cng_lowpass_gain < (((dword) 0x0001) << 16))
              p_lec->cng_lowpass_gain = ((dword) 0x0001) << 16;
          }
          if (p_lec->cng_eout_hp_energy >= p_lec->cng_noise_hp_energy)
          {
            p_lec->cng_lowpass_slope += (((dword)(p_lec->cng_lowpass_slope >> 16)) * ((dword) SPARSE_LEC_CNG_ADAPTATION_SLOPE)) << 1;
            if (p_lec->cng_lowpass_slope > (((dword) 0x7fff) << 16))
              p_lec->cng_lowpass_slope = ((dword) 0x7fff) << 16;
          }
          else
          {
            p_lec->cng_lowpass_slope -= (((dword)(p_lec->cng_lowpass_slope >> 16)) * ((dword) SPARSE_LEC_CNG_ADAPTATION_SLOPE)) << 1;
            if (p_lec->cng_lowpass_slope < (((dword) 0x0001) << 16))
              p_lec->cng_lowpass_slope = ((dword) 0x0001) << 16;
          }
        }
        if (p_lec->cng_active)
        {
          if (((short)((eout - t) ^ (p_lec->cng_eout_history[i] - t))) < 0)
            p_lec->cng_active = false;
        }
      }
      if (p_lec->cng_active)
        Sout = t;
      p_lec->cng_eout_history[p_lec->cng_history_pos] = eout;
      p_lec->cng_noise_history[p_lec->cng_history_pos] = t;
      p_lec->cng_history_pos = (p_lec->cng_history_pos < SPARSE_LEC_CNG_HISTORY_SIZE - 1) ? p_lec->cng_history_pos + 1 : 0;
    }
    else
    {
      if (p_lec->nlp_level < 0)
      {
        if (p_lec->options & DSP_LEC_NLP_WITH_ATTENUATION)
          Sout >>= 1;
        else
          Sout = 0;
      }
    }
  }
  return (Sout);
}


NAMESPACE_END

/*---------------------------------------------------------------------------*/
