/***
*tenpow.c - multiply a _LDBL12 by a power of 10
*
*       Copyright (c) 1991-2001, Microsoft Corporation. All rights reserved.
*
*Purpose:
*
*Revision History:
*       07-17-91  GDP   Initial version (ported from assembly)
*       11-15-93  GJF   Merged in NT SDK verions. Replaced MIPS and _ALPHA_
*                       by _M_MRX000 and _M_ALPHA (resp.).
*       10-02-94  BWT   PPC changes
*       07-15-96  GJF   Added parantheses to fix precedence problem in expr.
*                       Also, detab-ed.
*       05-05-99  RDL   Added _M_IA64 to #if def's for alignment.
*
*******************************************************************************/


#include <cv.h>

extern _LDBL12 _pow10pos[];
extern _LDBL12 _pow10neg[];




/***
*void _CALLTYPE5 __ld12mul(_LDBL12 *px, _LDBL12 *py) -
*   _LDBL12 multiplication
*
*Purpose: multiply two _LDBL12 numbers
*
*Entry: px,py: pointers to the _LDBL12 operands
*
*Exit: *px contains the product
*
*Exceptions:
*
*******************************************************************************/

void _CALLTYPE5 __ld12mul(_LDBL12 *px, _LDBL12 *py)
{
    u_short sign = 0;
    _LDBL12 tempman; /*this is actually a 12-byte mantissa,
                         not a 12-byte long double */
    int i;
    u_short expx, expy, expsum;
    int roffs,poffs,qoffs;
    int sticky = 0;

    *UL_LO_12(&tempman) = 0;
    *UL_MED_12(&tempman) = 0;
    *UL_HI_12(&tempman) = 0;

    expx = *U_EXP_12(px);
    expy = *U_EXP_12(py);

    sign = (expx ^ expy) & (u_short)0x8000;
    expx &= 0x7fff;
    expy &= 0x7fff;
    expsum = expx+expy;
    if (expx >= LD_MAXEXP
        || expy >= LD_MAXEXP
        || expsum > LD_MAXEXP+ LD_BIASM1){
        /* overflow to infinity */
        PUT_INF_12(px,sign);
        return;
    }
    if (expsum <= LD_BIASM1-63) {
        /* underflow to zero */
        PUT_ZERO_12(px);
        return;
    }
    if (expx == 0) {
        /*
         * If this is a denormal temp real then the mantissa
         * was shifted right once to set bit 63 to zero.
         */
        expsum++; /* Correct for this */
        if (ISZERO_12(px)) {
            /* put positive sign */
            *U_EXP_12(px) = 0;
            return;
        }
    }
    if (expy == 0) {
        expsum++; /* because arg2 is denormal */
        if (ISZERO_12(py)) {
            PUT_ZERO_12(px);
            return;
        }
    }

    roffs = 0;
    for (i=0;i<5;i++) {
        int j;
        poffs = i<<1;
        qoffs = 8;
        for (j=5-i;j>0;j--) {
	    u_long prod;
#if     defined(_M_MRX000) || defined(_M_ALPHA) || defined(_M_PPC) || defined(_M_IA64)
            /* a variable to hold temprary sums */
            u_long sum;
#endif
            int carry;
            u_short *p, *q;
            u_long *r;
            p = USHORT_12(px,poffs);
            q = USHORT_12(py,qoffs);
            r = ULONG_12(&tempman,roffs);
            prod = (u_long)*p * (u_long)*q;
#if     defined(_M_MRX000) || defined(_M_ALPHA) || defined(_M_PPC) || defined(_M_IA64)
            /* handle misalignment problems */
            if (i&0x1){ /* i is odd */
                carry = __addl(*ALIGN(r), prod, &sum);
                *ALIGN(r) =  sum;
            }
            else /* i is even */
                carry = __addl(*r, prod, r);
#else
            carry = __addl(*r,prod,r);
#endif
            if (carry) {
                /* roffs should be less than 8 in this case */
                (*USHORT_12(&tempman,roffs+4))++;
            }
            poffs+=2;
            qoffs-=2;
        }
        roffs+=2;
    }

    expsum -= LD_BIASM1;

    /* normalize */
    while ((s_short)expsum > 0 &&
           ((*UL_HI_12(&tempman) & MSB_ULONG) == 0)) {
         __shl_12(&tempman);
         expsum--;
    }

    if ((s_short)expsum <= 0) {
        expsum--;
        while ((s_short)expsum < 0) {
            if (*U_XT_12(&tempman) & 0x1)
                sticky++;
            __shr_12(&tempman);
            expsum++;
        }
        if (sticky)
            *U_XT_12(&tempman) |= 0x1;
    }

    if (*U_XT_12(&tempman) > 0x8000 ||
         ((*UL_LO_12(&tempman) & 0x1ffff) == 0x18000)) {
        /* round up */
        if (*UL_MANLO_12(&tempman) == MAX_ULONG) {
            *UL_MANLO_12(&tempman) = 0;
            if (*UL_MANHI_12(&tempman) == MAX_ULONG) {
                *UL_MANHI_12(&tempman) = 0;
                if (*U_EXP_12(&tempman) == MAX_USHORT) {
                    /* 12-byte mantissa overflow */
                    *U_EXP_12(&tempman) = MSB_USHORT;
                    expsum++;
                }
                else
                    (*U_EXP_12(&tempman))++;
            }
            else
                (*UL_MANHI_12(&tempman))++;
        }
        else
            (*UL_MANLO_12(&tempman))++;
    }


    /* check for exponent overflow */
    if (expsum >= 0x7fff){
        PUT_INF_12(px, sign);
        return;
    }

    /* put result in px */
    *U_XT_12(px) = *USHORT_12(&tempman,2);
    *UL_MANLO_12(px) = *UL_MED_12(&tempman);
    *UL_MANHI_12(px) = *UL_HI_12(&tempman);
    *U_EXP_12(px) = expsum | sign;
}



void _CALLTYPE5
__multtenpow12(_LDBL12 *pld12, int pow, unsigned mult12)
{
    _LDBL12 *pow_10p = _pow10pos-8;
    if (pow == 0)
        return;
    if (pow < 0) {
        pow = -pow;
        pow_10p = _pow10neg-8;
    }

    if (!mult12)
        *U_XT_12(pld12) = 0;


    while (pow) {
        int last3; /* the 3 LSBits of pow */
        _LDBL12 unround;
        _LDBL12 *py;

        pow_10p += 7;
        last3 = pow & 0x7;
        pow >>= 3;
        if (last3 == 0)
            continue;
        py = pow_10p + last3;

#ifdef _LDSUPPORT
        if (mult12) {
#endif
            /* do an exact 12byte multiplication */
            if (*U_XT_12(py) >= 0x8000) {
                /* copy number */
                unround = *py;
                /* unround adjacent byte */
                (*UL_MANLO_12(&unround))--;
                /* point to new operand */
                py = &unround;
            }
            __ld12mul(pld12,py);
#ifdef _LDSUPPORT
        }
        else {
            /* do a 10byte multiplication */
            py = (_LDBL12 *)TEN_BYTE_PART(py);
            *(long double *)TEN_BYTE_PART(pld12) *=
                *(long double *)py;
        }
#endif
    }
}