/*
 * Copyright (C) 2004-2010 NXP Software
 * Copyright (C) 2010 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.
 */

#ifndef _LVM_MACROS_H_
#define _LVM_MACROS_H_

/**********************************************************************************
   MUL32x32INTO32(A,B,C,ShiftR)
        C = (A * B) >> ShiftR

        A, B and C are all 32 bit SIGNED numbers and ShiftR can vary from 0 to 64

        The user has to take care that C does not overflow.  The result in case
        of overflow is undefined.

***********************************************************************************/
#define MUL32x32INTO32(A, B, C, ShiftR)                                                        \
    {                                                                                          \
        LVM_INT32 MUL32x32INTO32_temp, MUL32x32INTO32_temp2, MUL32x32INTO32_mask,              \
                MUL32x32INTO32_HH, MUL32x32INTO32_HL, MUL32x32INTO32_LH, MUL32x32INTO32_LL;    \
        LVM_INT32 shiftValue;                                                                  \
        shiftValue = (ShiftR);                                                                 \
        MUL32x32INTO32_mask = 0x0000FFFF;                                                      \
        MUL32x32INTO32_HH = ((LVM_INT32)((LVM_INT16)((A) >> 16)) * ((LVM_INT16)((B) >> 16)));  \
        MUL32x32INTO32_HL = ((LVM_INT32)((B)&MUL32x32INTO32_mask) * ((LVM_INT16)((A) >> 16))); \
        MUL32x32INTO32_LH = ((LVM_INT32)((A)&MUL32x32INTO32_mask) * ((LVM_INT16)((B) >> 16))); \
        MUL32x32INTO32_LL =                                                                    \
                (LVM_INT32)((A)&MUL32x32INTO32_mask) * (LVM_INT32)((B)&MUL32x32INTO32_mask);   \
        MUL32x32INTO32_temp = (LVM_INT32)(MUL32x32INTO32_HL & MUL32x32INTO32_mask) +           \
                              (LVM_INT32)(MUL32x32INTO32_LH & MUL32x32INTO32_mask) +           \
                              (LVM_INT32)((MUL32x32INTO32_LL >> 16) & MUL32x32INTO32_mask);    \
        MUL32x32INTO32_HH = MUL32x32INTO32_HH + (LVM_INT32)(MUL32x32INTO32_HL >> 16) +         \
                            (LVM_INT32)(MUL32x32INTO32_LH >> 16) +                             \
                            (LVM_INT32)(MUL32x32INTO32_temp >> 16);                            \
        MUL32x32INTO32_LL = MUL32x32INTO32_LL + (LVM_INT32)(MUL32x32INTO32_HL << 16) +         \
                            (LVM_INT32)(MUL32x32INTO32_LH << 16);                              \
        if (shiftValue < 32) {                                                                 \
            MUL32x32INTO32_HH = MUL32x32INTO32_HH << (32 - shiftValue);                        \
            MUL32x32INTO32_mask = ((LVM_INT32)1 << (32 - shiftValue)) - 1;                     \
            MUL32x32INTO32_LL = (MUL32x32INTO32_LL >> shiftValue) & MUL32x32INTO32_mask;       \
            MUL32x32INTO32_temp2 = MUL32x32INTO32_HH | MUL32x32INTO32_LL;                      \
        } else {                                                                               \
            MUL32x32INTO32_temp2 = (LVM_INT32)MUL32x32INTO32_HH >> (shiftValue - 32);          \
        }                                                                                      \
        (C) = MUL32x32INTO32_temp2;                                                            \
    }

/**********************************************************************************
   MUL32x16INTO32(A,B,C,ShiftR)
        C = (A * B) >> ShiftR

        A and C are 32 bit SIGNED numbers.  B is a 16 bit SIGNED number.
        ShiftR can vary from 0 to 48

        The user has to take care that C does not overflow.  The result in case
        of overflow is undefined.

***********************************************************************************/
#define MUL32x16INTO32(A, B, C, ShiftR)                                                          \
    {                                                                                            \
        LVM_INT32 MUL32x16INTO32_mask, MUL32x16INTO32_HH, MUL32x16INTO32_LL;                     \
        LVM_INT32 shiftValue;                                                                    \
        shiftValue = (ShiftR);                                                                   \
        MUL32x16INTO32_mask = 0x0000FFFF;                                                        \
        MUL32x16INTO32_HH = ((LVM_INT32)(B) * ((LVM_INT16)((A) >> 16)));                         \
        MUL32x16INTO32_LL = ((LVM_INT32)((A)&MUL32x16INTO32_mask) * (B));                        \
        if (shiftValue < 16) {                                                                   \
            MUL32x16INTO32_HH = (LVM_INT32)((LVM_UINT32)MUL32x16INTO32_HH << (16 - shiftValue)); \
            (C) = MUL32x16INTO32_HH + (LVM_INT32)(MUL32x16INTO32_LL >> shiftValue);              \
        } else if (shiftValue < 32) {                                                            \
            MUL32x16INTO32_HH = (LVM_INT32)(MUL32x16INTO32_HH >> (shiftValue - 16));             \
            (C) = MUL32x16INTO32_HH + (LVM_INT32)(MUL32x16INTO32_LL >> shiftValue);              \
        } else {                                                                                 \
            (C) = MUL32x16INTO32_HH >> (shiftValue - 16);                                        \
        }                                                                                        \
    }

/**********************************************************************************
   ADD2_SAT_32x32(A,B,C)
        C = SAT(A + B)

        A,B and C are 32 bit SIGNED numbers.
***********************************************************************************/
#define ADD2_SAT_32x32(A, B, C)                  \
    {                                            \
        (C) = (A) + (B);                         \
        if ((((C) ^ (A)) & ((C) ^ (B))) >> 31) { \
            if ((A) < 0)                         \
                (C) = 0x80000000l;               \
            else                                 \
                (C) = 0x7FFFFFFFl;               \
        }                                        \
    }

#endif /* _LVM_MACROS_H_ */

/*** End of file ******************************************************************/
