/******************************************************************************
* *
* 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
*/
#include <stdio.h>
#include <stdlib.h>
#include <float.h>
#include <math.h>
#include <assert.h>
#include <string.h>
#include <ixheaacd_type_def.h>
#include "ixheaacd_acelp_com.h"
extern const WORD32 ixheaacd_factorial_7[8];
extern const WORD32 ixheaacd_iso_code_index_table[LEN_ABS_LEADER];
extern const UWORD8 ixheaacd_iso_code_data_table[LEN_ABS_LEADER];
extern const UWORD32 ixheaacd_signed_leader_is[LEN_ABS_LEADER];
extern const WORD32 ixheaacd_iso_code_num_table[],
ixheaacd_pos_abs_leaders_a3[], ixheaacd_pos_abs_leaders_a4[];
extern const UWORD8 ixheaacd_absolute_leader_tab_da[][8];
extern const UWORD32 ixheaacd_cardinality_offset_table_i3[],
ixheaacd_cardinality_offset_tab_i4[];
static VOID ixheaacd_nearest_neighbor_2d(WORD32 x[], WORD32 y[], WORD32 count,
WORD32 *rem) {
WORD32 i, j, sum;
WORD32 s, e[8], em;
WORD32 rem_temp[8];
memcpy(rem_temp, rem, 8 * sizeof(WORD32));
sum = 0;
for (i = 0; i < 8; i++) {
if (x[i] < 0) {
y[i] = -2 * (((WORD32)(1 - x[i])) >> 1);
} else {
y[i] = 2 * (((WORD32)(1 + x[i])) >> 1);
}
sum += y[i];
if (x[i] % 2 != 0) {
if (x[i] < 0) {
rem_temp[i] = -(rem_temp[i] - (1 << count));
} else {
rem_temp[i] = rem_temp[i] - (1 << count);
}
}
}
if (sum % 4) {
em = 0;
j = 0;
for (i = 0; i < 8; i++) {
e[i] = rem_temp[i];
}
for (i = 0; i < 8; i++) {
if (e[i] < 0) {
s = -e[i];
} else {
s = e[i];
}
if (em < s) {
em = s;
j = i;
}
}
if (e[j] < 0) {
y[j] -= 2;
rem_temp[j] = rem_temp[j] + (2 << count);
} else {
y[j] += 2;
rem_temp[j] = rem_temp[j] - (2 << count);
}
}
memcpy(rem, rem_temp, 8 * sizeof(WORD32));
return;
}
VOID ixheaacd_voronoi_search(WORD32 x[], WORD32 y[], WORD32 count, WORD32 *rem1,
WORD32 *rem2) {
WORD32 i, y0[8], y1[8];
WORD32 e0, e1, x1[8], tmp;
ixheaacd_nearest_neighbor_2d(x, y0, count, rem1);
for (i = 0; i < 8; i++) {
if (x[i] == 0) {
if (rem2[i] == 0) {
x1[i] = x[i] - 1;
} else {
x1[i] = 0;
rem2[i] = rem2[i] - (1 << count);
}
} else {
x1[i] = x[i] - 1;
}
}
ixheaacd_nearest_neighbor_2d(x1, y1, count, rem2);
for (i = 0; i < 8; i++) {
y1[i] += 1;
}
e0 = e1 = 0;
for (i = 0; i < 8; i++) {
tmp = rem1[i];
e0 += tmp * tmp;
tmp = rem2[i];
e1 += tmp * tmp;
}
if (e0 < e1) {
for (i = 0; i < 8; i++) {
y[i] = y0[i];
}
} else {
for (i = 0; i < 8; i++) {
y[i] = y1[i];
}
}
return;
}
VOID ixheaacd_voronoi_idx_dec(WORD32 *kv, WORD32 m, WORD32 *y, WORD32 count) {
WORD32 i, v[8], tmp, sum, *ptr1, *ptr2;
WORD32 z[8];
WORD32 rem1[8], rem2[8];
for (i = 0; i < 8; i++) y[i] = kv[7];
z[7] = y[7] >> count;
rem1[7] = y[7] & (m - 1);
sum = 0;
for (i = 6; i >= 1; i--) {
tmp = 2 * kv[i];
sum += tmp;
y[i] += tmp;
z[i] = y[i] >> count;
rem1[i] = y[i] & (m - 1);
}
y[0] += (4 * kv[0] + sum);
z[0] = (y[0] - 2) >> count;
if (m != 0)
rem1[0] = (y[0] - 2) % m;
else
rem1[0] = (y[0] - 2);
memcpy(rem2, rem1, 8 * sizeof(WORD32));
ixheaacd_voronoi_search(z, v, count, rem1, rem2);
ptr1 = y;
ptr2 = v;
for (i = 0; i < 8; i++) {
*ptr1++ -= m * *ptr2++;
}
}
static VOID ixheaacd_gosset_rank_of_permutation(WORD32 rank, WORD32 *xs) {
WORD32 i, j, a[8], w[8], base, fac, fac_b, target;
j = 0;
w[j] = 1;
a[j] = xs[0];
base = 1;
for (i = 1; i < 8; i++) {
if (xs[i] != xs[i - 1]) {
j++;
w[j] = 1;
a[j] = xs[i];
} else {
w[j]++;
base *= w[j];
}
}
if (w[0] == 8) {
for (i = 0; i < 8; i++) xs[i] = a[0];
} else {
target = rank * base;
fac_b = 1;
for (i = 0; i < 8; i++) {
fac = fac_b * ixheaacd_factorial_7[i];
j = -1;
do {
target -= w[++j] * fac;
} while (target >= 0);
xs[i] = a[j];
target += w[j] * fac;
fac_b *= w[j];
w[j]--;
}
}
return;
}
static WORD32 ixheaacd_get_abs_leader_tbl(const UWORD32 *table,
UWORD32 code_book_ind, WORD32 size) {
WORD32 i;
for (i = 4; i < size; i += 4) {
if (code_book_ind < table[i]) break;
}
if (i > size) i = size;
if (code_book_ind < table[i - 2]) i -= 2;
if (code_book_ind < table[i - 1]) i--;
i--;
return (i);
}
static VOID ixheaacd_gosset_decode_base_index(WORD32 n, UWORD32 code_book_ind,
WORD32 *ya) {
WORD32 i, im, t, sign_code, idx = 0, ks, rank;
if (n < 2) {
for (i = 0; i < 8; i++) ya[i] = 0;
} else {
switch (n) {
case 2:
case 3:
i = ixheaacd_get_abs_leader_tbl(ixheaacd_cardinality_offset_table_i3,
code_book_ind, LEN_I3);
idx = ixheaacd_pos_abs_leaders_a3[i];
break;
case 4:
i = ixheaacd_get_abs_leader_tbl(ixheaacd_cardinality_offset_tab_i4,
code_book_ind, LEN_I4);
idx = ixheaacd_pos_abs_leaders_a4[i];
break;
}
for (i = 0; i < 8; i++) ya[i] = ixheaacd_absolute_leader_tab_da[idx][i];
t = ixheaacd_iso_code_index_table[idx];
im = ixheaacd_iso_code_num_table[idx];
ks = ixheaacd_get_abs_leader_tbl(ixheaacd_signed_leader_is + t,
code_book_ind, im);
sign_code = 2 * ixheaacd_iso_code_data_table[t + ks];
for (i = 7; i >= 0; i--) {
ya[i] *= (1 - (sign_code & 2));
sign_code >>= 1;
}
rank = code_book_ind - ixheaacd_signed_leader_is[t + ks];
ixheaacd_gosset_rank_of_permutation(rank, ya);
}
return;
}
VOID ixheaacd_rotated_gosset_mtx_dec(WORD32 qn, WORD32 code_book_idx,
WORD32 *kv, WORD32 *b) {
WORD32 i, m, c[8];
WORD32 count = 0;
if (qn <= 4) {
ixheaacd_gosset_decode_base_index(qn, code_book_idx, b);
} else {
m = 1;
while (qn > 4) {
m *= 2;
count++;
qn -= 2;
}
ixheaacd_gosset_decode_base_index(qn, code_book_idx, b);
ixheaacd_voronoi_idx_dec(kv, m, c, count);
for (i = 0; i < 8; i++) b[i] = m * b[i] + c[i];
}
return;
}