void powratcomp()

in src/CalcManager/Ratpack/exp.cpp [399:527]


void powratcomp(_Inout_ PRAT* px, _In_ PRAT y, uint32_t radix, int32_t precision)
{
    int32_t sign = SIGN(*px);

    // Take the absolute value
    (*px)->pp->sign = 1;
    (*px)->pq->sign = 1;

    if (zerrat(*px))
    {
        // *px is zero.
        if (rat_lt(y, rat_zero, precision))
        {
            throw(CALC_E_DOMAIN);
        }
        else if (zerrat(y))
        {
            // *px and y are both zero, special case a 1 return.
            DUPRAT(*px, rat_one);
            // Ensure sign is positive.
            sign = 1;
        }
    }
    else
    {
        PRAT pxint = nullptr;
        DUPRAT(pxint, *px);
        subrat(&pxint, rat_one, precision);
        if (rat_gt(pxint, rat_negsmallest, precision) && rat_lt(pxint, rat_smallest, precision) && (sign == 1))
        {
            // *px is one, special case a 1 return.
            DUPRAT(*px, rat_one);
            // Ensure sign is positive.
            sign = 1;
        }
        else
        {
            // Only do the exp if the number isn't zero or one
            PRAT podd = nullptr;
            DUPRAT(podd, y);
            fracrat(&podd, radix, precision);
            if (rat_gt(podd, rat_negsmallest, precision) && rat_lt(podd, rat_smallest, precision))
            {
                // If power is an integer let ratpowi32 deal with it.
                PRAT iy = nullptr;
                DUPRAT(iy, y);
                subrat(&iy, podd, precision);
                int32_t inty = rattoi32(iy, radix, precision);

                PRAT plnx = nullptr;
                DUPRAT(plnx, *px);
                lograt(&plnx, precision);
                mulrat(&plnx, iy, precision);
                if (rat_gt(plnx, rat_max_exp, precision) || rat_lt(plnx, rat_min_exp, precision))
                {
                    // Don't attempt exp of anything large or small.A
                    destroyrat(plnx);
                    destroyrat(iy);
                    destroyrat(pxint);
                    destroyrat(podd);
                    throw(CALC_E_DOMAIN);
                }
                destroyrat(plnx);
                ratpowi32(px, inty, precision);
                if ((inty & 1) == 0)
                {
                    sign = 1;
                }
                destroyrat(iy);
            }
            else
            {
                // power is a fraction
                if (sign == -1)
                {
                    // Need to throw an error if the exponent has an even denominator.
                    // As a first step, the numerator and denominator must be divided by 2 as many times as
                    //     possible, so that 2/6 is allowed.
                    // If the final numerator is still even, the end result should be positive.
                    PRAT pNumerator = nullptr;
                    PRAT pDenominator = nullptr;
                    bool fBadExponent = false;

                    // Get the numbers in arbitrary precision rational number format
                    DUPRAT(pNumerator, rat_zero);   // pNumerator->pq is 1 one
                    DUPRAT(pDenominator, rat_zero); // pDenominator->pq is 1 one

                    DUPNUM(pNumerator->pp, y->pp);
                    pNumerator->pp->sign = 1;
                    DUPNUM(pDenominator->pp, y->pq);
                    pDenominator->pp->sign = 1;

                    while (IsEven(pNumerator, radix, precision) && IsEven(pDenominator, radix, precision)) // both Numerator & denominator is even
                    {
                        divrat(&pNumerator, rat_two, precision);
                        divrat(&pDenominator, rat_two, precision);
                    }
                    if (IsEven(pDenominator, radix, precision)) // denominator is still even
                    {
                        fBadExponent = true;
                    }
                    if (IsEven(pNumerator, radix, precision)) // numerator is still even
                    {
                        sign = 1;
                    }
                    destroyrat(pNumerator);
                    destroyrat(pDenominator);

                    if (fBadExponent)
                    {
                        throw(CALC_E_DOMAIN);
                    }
                }
                else
                {
                    // If the exponent is not odd disregard the sign.
                    sign = 1;
                }

                lograt(px, precision);
                mulrat(px, y, precision);
                exprat(px, radix, precision);
            }
            destroyrat(podd);
        }
        destroyrat(pxint);
    }
    (*px)->pp->sign *= sign;
}