/*
 * g722_1 - a library for the G.722.1 and Annex C codecs
 *
 * basop32.c
 *
 * Adapted by Steve Underwood <steveu@coppice.org> from the reference
 * code supplied with ITU G.722.1
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 */

/*! \file */

#if defined(HAVE_CONFIG_H)
#include <config.h>
#endif

#include <inttypes.h>

#if defined(G722_1_USE_FIXED_POINT)

#include <stdlib.h>
#include <stdio.h>

#include "basop32.h"

int16_t shl(int16_t var1, int16_t var2)
{
    int32_t result;

    if (var2 < 0)
    {
        if (var2 < -16)
            var2 = -16;
        return shr(var1, (int16_t) -var2);
    }
    result = (int32_t) var1*((int32_t) 1 << var2);
    if ((var2 > 15  &&  var1 != 0)  ||  (result != (int32_t) ((int16_t) result)))
        return (var1 > 0)  ?  INT16_MAX  :  INT16_MIN;
    return (int16_t) result;
}
/*- End of function --------------------------------------------------------*/

int16_t shr(int16_t var1, int16_t var2)
{
    if (var2 < 0)
    {
        if (var2 < -16)
            var2 = -16;
        return shl(var1, (int16_t) -var2);
    }
    if (var2 >= 15)
        return (var1 < 0)  ?  -1  :  0;
    if (var1 < 0)
        return ~((~var1) >> var2);
    return var1 >> var2;
}
/*- End of function --------------------------------------------------------*/

int32_t L_add(int32_t L_var1, int32_t L_var2)
{
    int32_t L_var_out;

    L_var_out = L_var1 + L_var2;
    if (((L_var1 ^ L_var2) & INT32_MIN) == 0)
    {
        if ((L_var_out ^ L_var1) & INT32_MIN)
            return (L_var1 < 0)  ?  INT32_MIN  :  INT32_MAX;
    }
    return L_var_out;
}
/*- End of function --------------------------------------------------------*/

int32_t L_sub(int32_t L_var1, int32_t L_var2)
{
    int32_t L_var_out;

    L_var_out = L_var1 - L_var2;
    if (((L_var1 ^ L_var2) & INT32_MIN) != 0)
    {
        if ((L_var_out ^ L_var1) & INT32_MIN)
            return (L_var1 < 0L)  ?  INT32_MIN  :  INT32_MAX;
    }
    return L_var_out;
}
/*- End of function --------------------------------------------------------*/

int32_t L_shl(int32_t L_var1, int16_t var2)
{
    if (var2 <= 0)
    {
        if (var2 < -32)
            var2 = -32;
        return L_shr(L_var1, -var2);
    }
    for (  ;  var2 > 0;  var2--)
    {
        if (L_var1 > (int32_t) 0X3fffffffL)
            return INT32_MAX;
        if (L_var1 < (int32_t) 0xc0000000L)
            return INT32_MIN;
        L_var1 *= 2;
    }
    return L_var1;
}
/*- End of function --------------------------------------------------------*/

int32_t L_shr(int32_t L_var1, int16_t var2)
{
    if (var2 < 0)
    {
        if (var2 < -32)
            var2 = -32;
        return L_shl(L_var1, (int16_t) -var2);
    }
    if (var2 >= 31)
        return (L_var1 < 0L)  ?  -1  :  0;
    if (L_var1 < 0)
        return ~((~L_var1) >> var2);
    return L_var1 >> var2;
}
/*- End of function --------------------------------------------------------*/

/*! \brief Find the bit position of the highest set bit in a word
    \param bits The word to be searched
    \return The bit number of the highest set bit, or -1 if the word is zero. */
static __inline__ int top_bit(unsigned int bits)
{
    int res;

#if defined(__i386__)  ||  defined(__x86_64__)
    __asm__ (" xorl %[res],%[res];\n"
             " decl %[res];\n"
             " bsrl %[bits],%[res]\n"
             : [res] "=&r" (res)
             : [bits] "rm" (bits));
    return res;
#elif defined(__ppc__)  ||   defined(__powerpc__)
    __asm__ ("cntlzw %[res],%[bits];\n"
             : [res] "=&r" (res)
             : [bits] "r" (bits));
    return 31 - res;
#else
    if (bits == 0)
        return -1;
    res = 0;
    if (bits & 0xFFFF0000)
    {
        bits &= 0xFFFF0000;
        res += 16;
    }
    if (bits & 0xFF00FF00)
    {
        bits &= 0xFF00FF00;
        res += 8;
    }
    if (bits & 0xF0F0F0F0)
    {
        bits &= 0xF0F0F0F0;
        res += 4;
    }
    if (bits & 0xCCCCCCCC)
    {
        bits &= 0xCCCCCCCC;
        res += 2;
    }
    if (bits & 0xAAAAAAAA)
    {
        bits &= 0xAAAAAAAA;
        res += 1;
    }
    return res;
#endif
}
/*- End of function --------------------------------------------------------*/

int16_t norm_s(int16_t var1)
{
    if (var1 == 0)
        return 0;
    if (var1 < 0)
        var1 = ~var1;
    return (14 - top_bit(var1));
}
/*- End of function --------------------------------------------------------*/
#endif
/*- End of file ------------------------------------------------------------*/
