/******************************************************************************
 *                                                                            *
 * Copyright (C) 2018 The Android Open Source Project
 *
 * 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.
 *
 *****************************************************************************
 * Originally developed and contributed by Ittiam Systems Pvt. Ltd, Bangalore
*/
#ifndef IXHEAACD_BASIC_OPS32_H
#define IXHEAACD_BASIC_OPS32_H

static PLATFORM_INLINE WORD32 ixheaacd_min32(WORD32 a, WORD32 b) {
  WORD32 min_val;

  min_val = (a < b) ? a : b;

  return min_val;
}

static PLATFORM_INLINE WORD32 ixheaacd_max32(WORD32 a, WORD32 b) {
  WORD32 max_val;

  max_val = (a > b) ? a : b;

  return max_val;
}

static PLATFORM_INLINE WORD32 ixheaacd_shl32(WORD32 a, WORD b) {
  WORD32 out_val;

  b = ((UWORD32)(b << 24) >> 24);
  if (b > 31)
    out_val = 0;
  else
    out_val = (WORD32)a << b;

  return out_val;
}

static PLATFORM_INLINE WORD32 ixheaacd_shr32(WORD32 a, WORD b) {
  WORD32 out_val;

  b = ((UWORD32)(b << 24) >> 24);
  if (b >= 31) {
    if (a < 0)
      out_val = -1;
    else
      out_val = 0;
  } else {
    out_val = (WORD32)a >> b;
  }

  return out_val;
}

static PLATFORM_INLINE WORD32 ixheaacd_shl32_sat(WORD32 a, WORD b) {
  WORD32 out_val = a;
  for (; b > 0; b--) {
    if (a > (WORD32)0X3fffffffL) {
      out_val = MAX_32;
      break;
    } else if (a < (WORD32)0xc0000000L) {
      out_val = MIN_32;
      break;
    }

    a = ixheaacd_shl32(a, 1);
    out_val = a;
  }
  return (out_val);
}

static PLATFORM_INLINE WORD32 ixheaacd_shl32_dir(WORD32 a, WORD b) {
  WORD32 out_val;

  if (b < 0) {
    out_val = ixheaacd_shr32(a, -b);
  } else {
    out_val = ixheaacd_shl32(a, b);
  }

  return out_val;
}

static PLATFORM_INLINE WORD32 ixheaacd_shl32_dir_sat(WORD32 a, WORD b) {
  WORD32 out_val;

  if (b < 0) {
    out_val = ixheaacd_shr32(a, -b);
  } else {
    out_val = ixheaacd_shl32_sat(a, b);
  }

  return out_val;
}

static PLATFORM_INLINE WORD32 ixheaacd_shr32_dir(WORD32 a, WORD b) {
  WORD32 out_val;

  if (b < 0) {
    out_val = ixheaacd_shl32(a, -b);
  } else {
    out_val = ixheaacd_shr32(a, b);
  }

  return out_val;
}

static PLATFORM_INLINE WORD32 shr32_dir_sat(WORD32 a, WORD b) {
  WORD32 out_val;

  if (b < 0) {
    out_val = ixheaacd_shl32_sat(a, -b);
  } else {
    out_val = ixheaacd_shr32(a, b);
  }

  return out_val;
}

static PLATFORM_INLINE WORD32 ixheaacd_mult16x16in32(WORD16 a, WORD16 b) {
  WORD32 product;

  product = (WORD32)a * (WORD32)b;

  return product;
}

static PLATFORM_INLINE WORD32 mult16x16in32_32(WORD32 a, WORD32 b) {
  WORD32 product;

  product = (WORD32)a * (WORD32)b;

  return product;
}

static PLATFORM_INLINE WORD32 ixheaacd_mult16x16in32_shl(WORD16 a, WORD16 b) {
  WORD32 product;

  product = ixheaacd_shl32(ixheaacd_mult16x16in32(a, b), 1);

  return product;
}

static PLATFORM_INLINE WORD32 ixheaacd_mult16x16in32_shl_sat(WORD16 a,
                                                             WORD16 b) {
  WORD32 product;
  product = (WORD32)a * (WORD32)b;
  if (product != (WORD32)0x40000000L) {
    product = ixheaacd_shl32(product, 1);
  } else {
    product = MAX_32;
  }
  return product;
}

static PLATFORM_INLINE WORD32 ixheaacd_add32(WORD32 a, WORD32 b) {
  WORD32 sum;

  sum = (WORD32)a + (WORD32)b;

  return sum;
}

static PLATFORM_INLINE WORD32 ixheaacd_sub32(WORD32 a, WORD32 b) {
  WORD32 diff;

  diff = (WORD32)a - (WORD32)b;

  return diff;
}

static PLATFORM_INLINE WORD32 ixheaacd_add32_sat(WORD32 a, WORD32 b) {
  WORD64 sum;

  sum = (WORD64)a + (WORD64)b;

  if ((((WORD32)a ^ (WORD32)b) & (WORD32)MIN_32) == 0) {
    if (((WORD32)sum ^ (WORD32)a) & (WORD32)MIN_32) {
      sum = (a < 0) ? MIN_32 : MAX_32;
    }
  }

  return (WORD32)sum;
}

static PLATFORM_INLINE WORD32 ixheaacd_add32_sat3(WORD32 a, WORD32 b,
                                                  WORD32 c) {
  WORD64 sum;

  sum = (WORD64)a + (WORD64)b;

  sum = (WORD64)sum + (WORD64)c;

  if (sum > MAX_32) {
    sum = MAX_32;
  }
  if (sum < MIN_32) {
    sum = MIN_32;
  }

  return (WORD32)sum;
}

static PLATFORM_INLINE WORD32 ixheaacd_sub32_sat(WORD32 a, WORD32 b) {
  WORD64 diff;

  diff = (WORD64)a - (WORD64)b;

  if ((((WORD32)a ^ (WORD32)b) & (WORD32)MIN_32) != 0) {
    if (((WORD32)diff ^ (WORD32)a) & (WORD32)MIN_32) {
      diff = (a < 0L) ? MIN_32 : MAX_32;
    }
  }

  return (WORD32)diff;
}

static PLATFORM_INLINE WORD ixheaacd_norm32(WORD32 a) {
  WORD norm_val;

  if (a == 0) {
    norm_val = 31;
  } else {
    if (a == (WORD32)0xffffffffL) {
      norm_val = 31;
    } else {
      if (a < 0) {
        a = ~a;
      }
      for (norm_val = 0; a < (WORD32)0x40000000L; norm_val++) {
        a <<= 1;
      }
    }
  }

  return norm_val;
}

static PLATFORM_INLINE WORD ixheaacd_pnorm32(WORD32 a) {
  WORD norm_val;

  if (a == 0) {
    norm_val = 31;
  } else {
    for (norm_val = 0; a < (WORD32)0x40000000L; norm_val++) {
      a <<= 1;
    }
  }

  return norm_val;
}

static PLATFORM_INLINE WORD bin_expo32(WORD32 a) {
  WORD bin_expo_val;

  bin_expo_val = 31 - ixheaacd_norm32(a);

  return bin_expo_val;
}

static PLATFORM_INLINE WORD32 ixheaacd_abs32(WORD32 a) {
  WORD32 abs_val;

  abs_val = a;

  if (a < 0) {
    abs_val = -a;
  }

  return abs_val;
}

static PLATFORM_INLINE WORD32 ixheaacd_abs32_nrm(WORD32 a) {
  WORD32 abs_val;

  abs_val = a;

  if (a < 0) {
    abs_val = ~a;
  }

  return abs_val;
}

static PLATFORM_INLINE WORD32 ixheaacd_abs32_sat(WORD32 a) {
  WORD32 abs_val;

  abs_val = a;

  if (a == MIN_32) {
    abs_val = MAX_32;
  } else if (a < 0) {
    abs_val = -a;
  }

  return abs_val;
}

static PLATFORM_INLINE WORD32 ixheaacd_negate32(WORD32 a) {
  WORD32 neg_val;

  neg_val = -a;

  return neg_val;
}

static PLATFORM_INLINE WORD32 ixheaacd_negate32_sat(WORD32 a) {
  WORD32 neg_val;

  if (a == MIN_32) {
    neg_val = MAX_32;
  } else {
    neg_val = -a;
  }
  return neg_val;
}

static PLATFORM_INLINE WORD32 div32(WORD32 a, WORD32 b, WORD *q_format) {
  WORD32 quotient;
  UWORD32 mantissa_nr, mantissa_dr;
  WORD16 sign = 0;

  LOOPINDEX i;
  WORD q_nr, q_dr;

  mantissa_nr = a;
  mantissa_dr = b;
  quotient = 0;

  if ((a < 0) && (0 != b)) {
    a = -a;
    sign = (WORD16)(sign ^ -1);
  }

  if (b < 0) {
    b = -b;
    sign = (WORD16)(sign ^ -1);
  }

  if (0 == b) {
    *q_format = 0;
    return (a);
  }

  quotient = 0;

  q_nr = ixheaacd_norm32(a);
  mantissa_nr = (UWORD32)a << (q_nr);
  q_dr = ixheaacd_norm32(b);
  mantissa_dr = (UWORD32)b << (q_dr);
  *q_format = (WORD)(30 + q_nr - q_dr);

  for (i = 0; i < 31; i++) {
    quotient = quotient << 1;

    if (mantissa_nr >= mantissa_dr) {
      mantissa_nr = mantissa_nr - mantissa_dr;
      quotient += 1;
    }

    mantissa_nr = (UWORD32)mantissa_nr << 1;
  }

  if (sign < 0) {
    quotient = -quotient;
  }

  return quotient;
}

static PLATFORM_INLINE WORD32 ixheaacd_mac16x16in32(WORD32 a, WORD16 b,
                                                    WORD16 c) {
  WORD32 acc;

  acc = ixheaacd_mult16x16in32(b, c);

  acc = ixheaacd_add32(a, acc);

  return acc;
}

static PLATFORM_INLINE WORD32 mac16x16hin32(WORD32 a, WORD32 b, WORD32 c) {
  WORD32 acc;

  acc = ixheaacd_mult16x16in32((WORD16)b, (WORD16)(c >> 16));

  acc = ixheaacd_add32(a, acc);

  return acc;
}

static PLATFORM_INLINE WORD32 ixheaacd_mac16x16in32_shl(WORD32 a, WORD16 b,
                                                        WORD16 c) {
  WORD32 acc;

  acc = ixheaacd_mult16x16in32_shl(b, c);

  acc = ixheaacd_add32(a, acc);

  return acc;
}

static PLATFORM_INLINE WORD32 ixheaacd_mac16x16in32_shl_sat(WORD32 a, WORD16 b,
                                                            WORD16 c) {
  WORD32 acc;

  acc = ixheaacd_mult16x16in32_shl_sat(b, c);

  acc = ixheaacd_add32_sat(a, acc);

  return acc;
}

static PLATFORM_INLINE WORD32 msu16x16in32(WORD32 a, WORD16 b, WORD16 c) {
  WORD32 acc;

  acc = ixheaacd_mult16x16in32(b, c);

  acc = ixheaacd_sub32(a, acc);

  return acc;
}

static PLATFORM_INLINE WORD32 msu16x16in32_shl(WORD32 a, WORD16 b, WORD16 c) {
  WORD32 acc;

  acc = ixheaacd_mult16x16in32_shl(b, c);

  acc = ixheaacd_sub32(a, acc);

  return acc;
}

static PLATFORM_INLINE WORD32 msu16x16in32_shl_sat(WORD32 a, WORD16 b,
                                                   WORD16 c) {
  WORD32 acc;

  acc = ixheaacd_mult16x16in32_shl_sat(b, c);

  acc = ixheaacd_sub32_sat(a, acc);

  return acc;
}

static PLATFORM_INLINE WORD32 add32_shr(WORD32 a, WORD32 b) {
  WORD32 sum;

  a = ixheaacd_shr32(a, 1);
  b = ixheaacd_shr32(b, 1);

  sum = ixheaacd_add32(a, b);

  return sum;
}

static PLATFORM_INLINE WORD32 sub32_shr(WORD32 a, WORD32 b) {
  WORD32 diff;

  a = ixheaacd_shr32(a, 1);
  b = ixheaacd_shr32(b, 1);

  diff = ixheaacd_sub32(a, b);

  return diff;
}
#endif