#include "il2cpp-config.h"
#include "il2cpp-object-internals.h"
#include "il2cpp-class-internals.h"
#include "icalls/mscorlib/System/Decimal.h"
#include "utils/StringUtils.h"
#include "vm/Exception.h"
#include <cmath>
#include <algorithm>

typedef enum
{
    IL2CPP_DECIMAL_OK,
    IL2CPP_DECIMAL_OVERFLOW,
    IL2CPP_DECIMAL_INVALID_ARGUMENT,
    IL2CPP_DECIMAL_DIVBYZERO,
    IL2CPP_DECIMAL_ARGUMENT_OUT_OF_RANGE
} Il2CppDecimalStatus;

#ifndef FC_GC_POLL
#   define FC_GC_POLL()
#endif

// Double floating point Bias
#define IL2CPP_DOUBLE_BIAS 1022

// Single floating point Bias
#define IL2CPP_SINGLE_BIAS 126

static const uint32_t ten_to_nine = 1000000000U;
static const uint32_t ten_to_ten_div_4 = 2500000000U;
#define POWER10_MAX     9
#define DECIMAL_NEG ((uint8_t)0x80)
#define DECMAX 28
#define DECIMAL_SCALE(dec)       ((dec).u.u.scale)
#define DECIMAL_SIGN(dec)        ((dec).u.u.sign)
#define DECIMAL_SIGNSCALE(dec)   ((dec).u.signscale)
#define DECIMAL_LO32(dec)        ((dec).v.v.Lo32)
#define DECIMAL_MID32(dec)       ((dec).v.v.Mid32)
#define DECIMAL_HI32(dec)        ((dec).Hi32)
#if IL2CPP_BYTE_ORDER != IL2CPP_LITTLE_ENDIAN
# define DECIMAL_LO64_GET(dec)   (((uint64_t)((dec).v.v.Mid32) << 32) | (dec).v.v.Lo32)
# define DECIMAL_LO64_SET(dec, value)   {(dec).v.v.Lo32 = (value); (dec).v.v.Mid32 = ((value) >> 32); }
#else
# define DECIMAL_LO64_GET(dec)    ((dec).v.Lo64)
# define DECIMAL_LO64_SET(dec, value)   {(dec).v.Lo64 = value; }
#endif

#define DECIMAL_SETZERO(dec) {DECIMAL_LO32(dec) = 0; DECIMAL_MID32(dec) = 0; DECIMAL_HI32(dec) = 0; DECIMAL_SIGNSCALE(dec) = 0;}
#define COPYDEC(dest, src) {DECIMAL_SIGNSCALE(dest) = DECIMAL_SIGNSCALE(src); DECIMAL_HI32(dest) = DECIMAL_HI32(src); \
    DECIMAL_MID32(dest) = DECIMAL_MID32(src); DECIMAL_LO32(dest) = DECIMAL_LO32(src); }

#define DEC_SCALE_MAX   28
#define POWER10_MAX     9

#define OVFL_MAX_9_HI   4
#define OVFL_MAX_9_MID  1266874889
#define OVFL_MAX_9_LO   3047500985u

#define OVFL_MAX_5_HI   42949
#define OVFL_MAX_5_MID  2890341191

#define OVFL_MAX_1_HI   429496729

typedef union
{
    uint64_t int64;
    struct
    {
#if IL2CPP_BYTE_ORDER == IL2CPP_BIG_ENDIAN
        uint32_t Hi;
        uint32_t Lo;
#else
        uint32_t Lo;
        uint32_t Hi;
#endif
    } u;
} SPLIT64;

static const SPLIT64    ten_to_eighteen = { 1000000000000000000ULL };

#if IL2CPP_BYTE_ORDER == IL2CPP_BIG_ENDIAN
const Il2CppDouble_double ds2to64 = { { 0, IL2CPP_DOUBLE_BIAS + 65, 0, 0 } };
#else
const Il2CppDouble_double ds2to64 = { { 0, 0, IL2CPP_DOUBLE_BIAS + 65, 0 } };
#endif

//
// Data tables
//

static const uint32_t power10[POWER10_MAX + 1] =
{
    1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000
};


static const double double_power10[] =
{
    1, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9,
    1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
    1e20, 1e21, 1e22, 1e23, 1e24, 1e25, 1e26, 1e27, 1e28, 1e29,
    1e30, 1e31, 1e32, 1e33, 1e34, 1e35, 1e36, 1e37, 1e38, 1e39,
    1e40, 1e41, 1e42, 1e43, 1e44, 1e45, 1e46, 1e47, 1e48, 1e49,
    1e50, 1e51, 1e52, 1e53, 1e54, 1e55, 1e56, 1e57, 1e58, 1e59,
    1e60, 1e61, 1e62, 1e63, 1e64, 1e65, 1e66, 1e67, 1e68, 1e69,
    1e70, 1e71, 1e72, 1e73, 1e74, 1e75, 1e76, 1e77, 1e78, 1e79,
    1e80
};

const SPLIT64 sdl_power10[] = { { 10000000000ULL },          // 1E10
                                { 100000000000ULL }, // 1E11
                                { 1000000000000ULL }, // 1E12
                                { 10000000000000ULL }, // 1E13
                                { 100000000000000ULL } }; // 1E14

static const uint64_t long_power10[] =
{
    1,
    10ULL,
    100ULL,
    1000ULL,
    10000ULL,
    100000ULL,
    1000000ULL,
    10000000ULL,
    100000000ULL,
    1000000000ULL,
    10000000000ULL,
    100000000000ULL,
    1000000000000ULL,
    10000000000000ULL,
    100000000000000ULL,
    1000000000000000ULL,
    10000000000000000ULL,
    100000000000000000ULL,
    1000000000000000000ULL,
    10000000000000000000ULL
};

typedef struct
{
    uint32_t Hi, Mid, Lo;
} DECOVFL;

const DECOVFL power_overflow[] =
{
    // This is a table of the largest values that can be in the upper two
    // ULONGs of a 96-bit number that will not overflow when multiplied
    // by a given power.  For the upper word, this is a table of
    // 2^32 / 10^n for 1 <= n <= 9.  For the lower word, this is the
    // remaining fraction part * 2^32.  2^32 = 4294967296.
    //
    { 429496729u, 2576980377u, 2576980377u }, // 10^1 remainder 0.6
    { 42949672u,  4123168604u, 687194767u }, // 10^2 remainder 0.16
    { 4294967u,   1271310319u, 2645699854u }, // 10^3 remainder 0.616
    { 429496u,    3133608139u, 694066715u }, // 10^4 remainder 0.1616
    { 42949u,     2890341191u, 2216890319u }, // 10^5 remainder 0.51616
    { 4294u,      4154504685u, 2369172679u }, // 10^6 remainder 0.551616
    { 429u,       2133437386u, 4102387834u }, // 10^7 remainder 0.9551616
    { 42u,        4078814305u, 410238783u }, // 10^8 remainder 0.09991616
    { 4u,         1266874889u, 3047500985u }, // 10^9 remainder 0.709551616
};


#define IL2CPP_UInt32x32To64(a, b) ((uint64_t)((uint32_t)(a)) * (uint64_t)((uint32_t)(b)))
#define Div64by32(num, den) ((uint32_t)((uint64_t)(num) / (uint32_t)(den)))
#define Mod64by32(num, den) ((uint32_t)((uint64_t)(num) % (uint32_t)(den)))

static double
fnDblPower10(int ix)
{
    const int maxIx = (sizeof(double_power10) / sizeof(double_power10[0]));
    IL2CPP_ASSERT(ix >= 0);
    if (ix < maxIx)
        return double_power10[ix];
    return pow(10.0, ix);
} // double fnDblPower10()

static inline int64_t
DivMod32by32(int32_t num, int32_t den)
{
    SPLIT64  sdl;

    sdl.u.Lo = num / den;
    sdl.u.Hi = num % den;
    return sdl.int64;
}

static inline int64_t
DivMod64by32(int64_t num, int32_t den)
{
    SPLIT64  sdl;

    sdl.u.Lo = Div64by32(num, den);
    sdl.u.Hi = Mod64by32(num, den);
    return sdl.int64;
}

static uint64_t
UInt64x64To128(SPLIT64 op1, SPLIT64 op2, uint64_t *hi)
{
    SPLIT64  tmp1;
    SPLIT64  tmp2;
    SPLIT64  tmp3;

    tmp1.int64 = IL2CPP_UInt32x32To64(op1.u.Lo, op2.u.Lo); // lo partial prod
    tmp2.int64 = IL2CPP_UInt32x32To64(op1.u.Lo, op2.u.Hi); // mid 1 partial prod
    tmp1.u.Hi += tmp2.u.Lo;
    if (tmp1.u.Hi < tmp2.u.Lo)  // test for carry
        tmp2.u.Hi++;
    tmp3.int64 = IL2CPP_UInt32x32To64(op1.u.Hi, op2.u.Hi) + (uint64_t)tmp2.u.Hi;
    tmp2.int64 = IL2CPP_UInt32x32To64(op1.u.Hi, op2.u.Lo);
    tmp1.u.Hi += tmp2.u.Lo;
    if (tmp1.u.Hi < tmp2.u.Lo)  // test for carry
        tmp2.u.Hi++;
    tmp3.int64 += (uint64_t)tmp2.u.Hi;

    *hi = tmp3.int64;
    return tmp1.int64;
}

/**
* FullDiv64By32:
*
* Entry:
*   pdlNum  - Pointer to 64-bit dividend
*   ulDen   - 32-bit divisor
*
* Purpose:
*   Do full divide, yielding 64-bit result and 32-bit remainder.
*
* Exit:
*   Quotient overwrites dividend.
*   Returns remainder.
*
* Exceptions:
*   None.
*/
// Was: FullDiv64By32
static uint32_t
FullDiv64By32(uint64_t *num, uint32_t den)
{
    SPLIT64  tmp;
    SPLIT64  res;

    tmp.int64 = *num;
    res.u.Hi = 0;

    if (tmp.u.Hi >= den)
    {
        // DivMod64by32 returns quotient in Lo, remainder in Hi.
        //
        res.u.Lo = tmp.u.Hi;
        res.int64 = DivMod64by32(res.int64, den);
        tmp.u.Hi = res.u.Hi;
        res.u.Hi = res.u.Lo;
    }

    tmp.int64 = DivMod64by32(tmp.int64, den);
    res.u.Lo = tmp.u.Lo;
    *num = res.int64;
    return tmp.u.Hi;
}

/***
* SearchScale
*
* Entry:
*   res_hi - Top uint32_t of quotient
*   res_mid - Middle uint32_t of quotient
*   res_lo - Bottom uint32_t of quotient
*   scale  - Scale factor of quotient, range -DEC_SCALE_MAX to DEC_SCALE_MAX
*
* Purpose:
*   Determine the max power of 10, <= 9, that the quotient can be scaled
*   up by and still fit in 96 bits.
*
* Exit:
*   Returns power of 10 to scale by, -1 if overflow error.
*
***********************************************************************/

static int
SearchScale(uint32_t res_hi, uint32_t res_mid, uint32_t res_lo, int scale)
{
    int   cur_scale;

    // Quick check to stop us from trying to scale any more.
    //
    if (res_hi > OVFL_MAX_1_HI || scale >= DEC_SCALE_MAX)
    {
        cur_scale = 0;
        goto HaveScale;
    }

    if (scale > DEC_SCALE_MAX - 9)
    {
        // We can't scale by 10^9 without exceeding the max scale factor.
        // See if we can scale to the max.  If not, we'll fall into
        // standard search for scale factor.
        //
        cur_scale = DEC_SCALE_MAX - scale;
        if (res_hi < power_overflow[cur_scale - 1].Hi)
            goto HaveScale;

        if (res_hi == power_overflow[cur_scale - 1].Hi)
        {
        UpperEq:
            if (res_mid > power_overflow[cur_scale - 1].Mid ||
                (res_mid == power_overflow[cur_scale - 1].Mid && res_lo > power_overflow[cur_scale - 1].Lo))
            {
                cur_scale--;
            }
            goto HaveScale;
        }
    }
    else if (res_hi < OVFL_MAX_9_HI || (res_hi == OVFL_MAX_9_HI && res_mid < OVFL_MAX_9_MID) || (res_hi == OVFL_MAX_9_HI && res_mid == OVFL_MAX_9_MID && res_lo <= OVFL_MAX_9_LO))
        return 9;

    // Search for a power to scale by < 9.  Do a binary search
    // on power_overflow[].
    //
    cur_scale = 5;
    if (res_hi < OVFL_MAX_5_HI)
        cur_scale = 7;
    else if (res_hi > OVFL_MAX_5_HI)
        cur_scale = 3;
    else
        goto UpperEq;

    // cur_scale is 3 or 7.
    //
    if (res_hi < power_overflow[cur_scale - 1].Hi)
        cur_scale++;
    else if (res_hi > power_overflow[cur_scale - 1].Hi)
        cur_scale--;
    else
        goto UpperEq;

    // cur_scale is 2, 4, 6, or 8.
    //
    // In all cases, we already found we could not use the power one larger.
    // So if we can use this power, it is the biggest, and we're done.  If
    // we can't use this power, the one below it is correct for all cases
    // unless it's 10^1 -- we might have to go to 10^0 (no scaling).
    //
    if (res_hi > power_overflow[cur_scale - 1].Hi)
        cur_scale--;

    if (res_hi == power_overflow[cur_scale - 1].Hi)
        goto UpperEq;

HaveScale:
    // cur_scale = largest power of 10 we can scale by without overflow,
    // cur_scale < 9.  See if this is enough to make scale factor
    // positive if it isn't already.
    //
    if (cur_scale + scale < 0)
        cur_scale = -1;

    return cur_scale;
}

/**
* Div96By32
*
* Entry:
*   rgulNum - Pointer to 96-bit dividend as array of uint32_ts, least-sig first
*   ulDen   - 32-bit divisor.
*
* Purpose:
*   Do full divide, yielding 96-bit result and 32-bit remainder.
*
* Exit:
*   Quotient overwrites dividend.
*   Returns remainder.
*
* Exceptions:
*   None.
*
*/
static uint32_t
Div96By32(uint32_t *num, uint32_t den)
{
    SPLIT64  tmp;

    tmp.u.Hi = 0;

    if (num[2] != 0)
        goto Div3Word;

    if (num[1] >= den)
        goto Div2Word;

    tmp.u.Hi = num[1];
    num[1] = 0;
    goto Div1Word;

Div3Word:
    tmp.u.Lo = num[2];
    tmp.int64 = DivMod64by32(tmp.int64, den);
    num[2] = tmp.u.Lo;
Div2Word:
    tmp.u.Lo = num[1];
    tmp.int64 = DivMod64by32(tmp.int64, den);
    num[1] = tmp.u.Lo;
Div1Word:
    tmp.u.Lo = num[0];
    tmp.int64 = DivMod64by32(tmp.int64, den);
    num[0] = tmp.u.Lo;
    return tmp.u.Hi;
}

/***
* DecFixInt
*
* Entry:
*   pdecRes - Pointer to Decimal result location
*   operand  - Pointer to Decimal operand
*
* Purpose:
*   Chop the value to integer.  Return remainder so Int() function
*   can round down if non-zero.
*
* Exit:
*   Returns remainder.
*
* Exceptions:
*   None.
*
***********************************************************************/

static uint32_t
DecFixInt(Il2CppDecimal * result, Il2CppDecimal * operand)
{
    uint32_t   num[3];
    uint32_t   rem;
    uint32_t   pwr;
    int     scale;

    if (operand->u.u.scale > 0)
    {
        num[0] = operand->v.v.Lo32;
        num[1] = operand->v.v.Mid32;
        num[2] = operand->Hi32;
        scale = operand->u.u.scale;
        result->u.u.sign = operand->u.u.sign;
        rem = 0;

        do
        {
            if (scale > POWER10_MAX)
                pwr = ten_to_nine;
            else
                pwr = power10[scale];

            rem |= Div96By32(num, pwr);
            scale -= 9;
        }
        while (scale > 0);

        result->v.v.Lo32 = num[0];
        result->v.v.Mid32 = num[1];
        result->Hi32 = num[2];
        result->u.u.scale = 0;

        return rem;
    }

    COPYDEC(*result, *operand);
    // Odd, the Microsoft code does not set result->reserved to zero on this case
    return 0;
}

/**
* ScaleResult:
*
* Entry:
*   res - Array of uint32_ts with value, least-significant first.
*   hi_res  - Index of last non-zero value in res.
*   scale  - Scale factor for this value, range 0 - 2 * DEC_SCALE_MAX
*
* Purpose:
*   See if we need to scale the result to fit it in 96 bits.
*   Perform needed scaling.  Adjust scale factor accordingly.
*
* Exit:
*   res updated in place, always 3 uint32_ts.
*   New scale factor returned, -1 if overflow error.
*
*/
static int
ScaleResult(uint32_t *res, int hi_res, int scale)
{
    int     new_scale;
    int     cur;
    uint32_t   pwr;
    uint32_t   tmp;
    uint32_t   sticky;
    SPLIT64 sdlTmp;

    // See if we need to scale the result.  The combined scale must
    // be <= DEC_SCALE_MAX and the upper 96 bits must be zero.
    //
    // Start by figuring a lower bound on the scaling needed to make
    // the upper 96 bits zero.  hi_res is the index into res[]
    // of the highest non-zero uint32_t.
    //
    new_scale = hi_res * 32 - 64 - 1;
    if (new_scale > 0)
    {
        // Find the MSB.
        //
        tmp = res[hi_res];
        if (!(tmp & 0xFFFF0000))
        {
            new_scale -= 16;
            tmp <<= 16;
        }
        if (!(tmp & 0xFF000000))
        {
            new_scale -= 8;
            tmp <<= 8;
        }
        if (!(tmp & 0xF0000000))
        {
            new_scale -= 4;
            tmp <<= 4;
        }
        if (!(tmp & 0xC0000000))
        {
            new_scale -= 2;
            tmp <<= 2;
        }
        if (!(tmp & 0x80000000))
        {
            new_scale--;
            tmp <<= 1;
        }

        // Multiply bit position by log10(2) to figure it's power of 10.
        // We scale the log by 256.  log(2) = .30103, * 256 = 77.  Doing this
        // with a multiply saves a 96-byte lookup table.  The power returned
        // is <= the power of the number, so we must add one power of 10
        // to make it's integer part zero after dividing by 256.
        //
        // Note: the result of this multiplication by an approximation of
        // log10(2) have been exhaustively checked to verify it gives the
        // correct result.  (There were only 95 to check...)
        //
        new_scale = ((new_scale * 77) >> 8) + 1;

        // new_scale = min scale factor to make high 96 bits zero, 0 - 29.
        // This reduces the scale factor of the result.  If it exceeds the
        // current scale of the result, we'll overflow.
        //
        if (new_scale > scale)
            return -1;
    }
    else
        new_scale = 0;

    // Make sure we scale by enough to bring the current scale factor
    // into valid range.
    //
    if (new_scale < scale - DEC_SCALE_MAX)
        new_scale = scale - DEC_SCALE_MAX;

    if (new_scale != 0)
    {
        // Scale by the power of 10 given by new_scale.  Note that this is
        // NOT guaranteed to bring the number within 96 bits -- it could
        // be 1 power of 10 short.
        //
        scale -= new_scale;
        sticky = 0;
        sdlTmp.u.Hi = 0; // initialize remainder

        for (;;)
        {
            sticky |= sdlTmp.u.Hi; // record remainder as sticky bit

            if (new_scale > POWER10_MAX)
                pwr = ten_to_nine;
            else
                pwr = power10[new_scale];

            // Compute first quotient.
            // DivMod64by32 returns quotient in Lo, remainder in Hi.
            //
            sdlTmp.int64 = DivMod64by32(res[hi_res], pwr);
            res[hi_res] = sdlTmp.u.Lo;
            cur = hi_res - 1;

            if (cur >= 0)
            {
                // If first quotient was 0, update hi_res.
                //
                if (sdlTmp.u.Lo == 0)
                    hi_res--;

                // Compute subsequent quotients.
                //
                do
                {
                    sdlTmp.u.Lo = res[cur];
                    sdlTmp.int64 = DivMod64by32(sdlTmp.int64, pwr);
                    res[cur] = sdlTmp.u.Lo;
                    cur--;
                }
                while (cur >= 0);
            }

            new_scale -= POWER10_MAX;
            if (new_scale > 0)
                continue; // scale some more

            // If we scaled enough, hi_res would be 2 or less.  If not,
            // divide by 10 more.
            //
            if (hi_res > 2)
            {
                new_scale = 1;
                scale--;
                continue; // scale by 10
            }

            // Round final result.  See if remainder >= 1/2 of divisor.
            // If remainder == 1/2 divisor, round up if odd or sticky bit set.
            //
            pwr >>= 1;  // power of 10 always even
            if (pwr <= sdlTmp.u.Hi && (pwr < sdlTmp.u.Hi ||
                                       ((res[0] & 1) | sticky)))
            {
                cur = -1;
                while (++res[++cur] == 0)
                    ;

                if (cur > 2)
                {
                    // The rounding caused us to carry beyond 96 bits.
                    // Scale by 10 more.
                    //
                    hi_res = cur;
                    sticky = 0;  // no sticky bit
                    sdlTmp.u.Hi = 0; // or remainder
                    new_scale = 1;
                    scale--;
                    continue; // scale by 10
                }
            }

            // We may have scaled it more than we planned.  Make sure the scale
            // factor hasn't gone negative, indicating overflow.
            //
            if (scale < 0)
                return -1;

            return scale;
        } // for(;;)
    }
    return scale;
}

// Returns: IL2CPP_DECIMAL_OK, or IL2CPP_DECIMAL_INVALID_ARGUMENT
static Il2CppDecimalStatus
il2cpp_decimal_to_double_result(Il2CppDecimal *input, double *result)
{
    SPLIT64  tmp;
    double   dbl;

    if (DECIMAL_SCALE(*input) > DECMAX || (DECIMAL_SIGN(*input) & ~DECIMAL_NEG) != 0)
        return IL2CPP_DECIMAL_INVALID_ARGUMENT;

    tmp.u.Lo = DECIMAL_LO32(*input);
    tmp.u.Hi = DECIMAL_MID32(*input);

    if ((int32_t)DECIMAL_MID32(*input) < 0)
        dbl = (ds2to64.d + (double)(int64_t)tmp.int64 +
            (double)DECIMAL_HI32(*input) * ds2to64.d) / fnDblPower10(DECIMAL_SCALE(*input));
    else
        dbl = ((double)(int64_t)tmp.int64 +
            (double)DECIMAL_HI32(*input) * ds2to64.d) / fnDblPower10(DECIMAL_SCALE(*input));

    if (DECIMAL_SIGN(*input))
        dbl = -dbl;

    *result = dbl;
    return IL2CPP_DECIMAL_OK;
}

// Returns: IL2CPP_DECIMAL_OK, or IL2CPP_DECIMAL_INVALID_ARGUMENT
static Il2CppDecimalStatus
il2cpp_decimal_to_float_result(Il2CppDecimal *input, float *result)
{
    double   dbl;

    if (DECIMAL_SCALE(*input) > DECMAX || (DECIMAL_SIGN(*input) & ~DECIMAL_NEG) != 0)
        return IL2CPP_DECIMAL_INVALID_ARGUMENT;

    // Can't overflow; no errors possible.
    //
    il2cpp_decimal_to_double_result(input, &dbl);
    *result = (float)dbl;
    return IL2CPP_DECIMAL_OK;
}

static Il2CppDecimalStatus
DecAddSub(Il2CppDecimal *left, Il2CppDecimal *right, Il2CppDecimal *result, int8_t sign)
{
    uint32_t     num[6];
    uint32_t     pwr;
    int       scale;
    int       hi_prod;
    int       cur;
    SPLIT64   tmp;
    Il2CppDecimal decRes;
    Il2CppDecimal decTmp;
    Il2CppDecimal *pdecTmp;

    sign ^= (right->u.u.sign ^ left->u.u.sign) & DECIMAL_NEG;

    if (right->u.u.scale == left->u.u.scale)
    {
        // Scale factors are equal, no alignment necessary.
        //
        decRes.u.signscale = left->u.signscale;

    AlignedAdd:
        if (sign)
        {
            // Signs differ - subtract
            //
            DECIMAL_LO64_SET(decRes, DECIMAL_LO64_GET(*left) - DECIMAL_LO64_GET(*right));
            DECIMAL_HI32(decRes) = DECIMAL_HI32(*left) - DECIMAL_HI32(*right);

            // Propagate carry
            //
            if (DECIMAL_LO64_GET(decRes) > DECIMAL_LO64_GET(*left))
            {
                decRes.Hi32--;
                if (decRes.Hi32 >= left->Hi32)
                    goto SignFlip;
            }
            else if (decRes.Hi32 > left->Hi32)
            {
                // Got negative result.  Flip its sign.
                //
            SignFlip:
                DECIMAL_LO64_SET(decRes, (uint64_t)DECIMAL_LO64_GET(decRes));
                decRes.Hi32 = ~decRes.Hi32;
                if (DECIMAL_LO64_GET(decRes) == 0)
                    decRes.Hi32++;
                decRes.u.u.sign ^= DECIMAL_NEG;
            }
        }
        else
        {
            // Signs are the same - add
            //
            DECIMAL_LO64_SET(decRes, DECIMAL_LO64_GET(*left) + DECIMAL_LO64_GET(*right));
            decRes.Hi32 = left->Hi32 + right->Hi32;

            // Propagate carry
            //
            if (DECIMAL_LO64_GET(decRes) < DECIMAL_LO64_GET(*left))
            {
                decRes.Hi32++;
                if (decRes.Hi32 <= left->Hi32)
                    goto AlignedScale;
            }
            else if (decRes.Hi32 < left->Hi32)
            {
            AlignedScale:
                // The addition carried above 96 bits.  Divide the result by 10,
                // dropping the scale factor.
                //
                if (decRes.u.u.scale == 0)
                    return IL2CPP_DECIMAL_OVERFLOW;
                decRes.u.u.scale--;

                tmp.u.Lo = decRes.Hi32;
                tmp.u.Hi = 1;
                tmp.int64 = DivMod64by32(tmp.int64, 10);
                decRes.Hi32 = tmp.u.Lo;

                tmp.u.Lo = decRes.v.v.Mid32;
                tmp.int64 = DivMod64by32(tmp.int64, 10);
                decRes.v.v.Mid32 = tmp.u.Lo;

                tmp.u.Lo = decRes.v.v.Lo32;
                tmp.int64 = DivMod64by32(tmp.int64, 10);
                decRes.v.v.Lo32 = tmp.u.Lo;

                // See if we need to round up.
                //
                if (tmp.u.Hi >= 5 && (tmp.u.Hi > 5 || (decRes.v.v.Lo32 & 1)))
                {
                    DECIMAL_LO64_SET(decRes, DECIMAL_LO64_GET(decRes) + 1)
                    if (DECIMAL_LO64_GET(decRes) == 0)
                        decRes.Hi32++;
                }
            }
        }
    }
    else
    {
        // Scale factors are not equal.  Assume that a larger scale
        // factor (more decimal places) is likely to mean that number
        // is smaller.  Start by guessing that the right operand has
        // the larger scale factor.  The result will have the larger
        // scale factor.
        //
        decRes.u.u.scale = right->u.u.scale;  // scale factor of "smaller"
        decRes.u.u.sign = left->u.u.sign;    // but sign of "larger"
        scale = decRes.u.u.scale - left->u.u.scale;

        if (scale < 0)
        {
            // Guessed scale factor wrong. Swap operands.
            //
            scale = -scale;
            decRes.u.u.scale = left->u.u.scale;
            decRes.u.u.sign ^= sign;
            pdecTmp = right;
            right = left;
            left = pdecTmp;
        }

        // *left will need to be multiplied by 10^scale so
        // it will have the same scale as *right.  We could be
        // extending it to up to 192 bits of precision.
        //
        if (scale <= POWER10_MAX)
        {
            // Scaling won't make it larger than 4 uint32_ts
            //
            pwr = power10[scale];
            DECIMAL_LO64_SET(decTmp, IL2CPP_UInt32x32To64(left->v.v.Lo32, pwr));
            tmp.int64 = IL2CPP_UInt32x32To64(left->v.v.Mid32, pwr);
            tmp.int64 += decTmp.v.v.Mid32;
            decTmp.v.v.Mid32 = tmp.u.Lo;
            decTmp.Hi32 = tmp.u.Hi;
            tmp.int64 = IL2CPP_UInt32x32To64(left->Hi32, pwr);
            tmp.int64 += decTmp.Hi32;
            if (tmp.u.Hi == 0)
            {
                // Result fits in 96 bits.  Use standard aligned add.
                //
                decTmp.Hi32 = tmp.u.Lo;
                left = &decTmp;
                goto AlignedAdd;
            }
            num[0] = decTmp.v.v.Lo32;
            num[1] = decTmp.v.v.Mid32;
            num[2] = tmp.u.Lo;
            num[3] = tmp.u.Hi;
            hi_prod = 3;
        }
        else
        {
            // Have to scale by a bunch.  Move the number to a buffer
            // where it has room to grow as it's scaled.
            //
            num[0] = left->v.v.Lo32;
            num[1] = left->v.v.Mid32;
            num[2] = left->Hi32;
            hi_prod = 2;

            // Scan for zeros in the upper words.
            //
            if (num[2] == 0)
            {
                hi_prod = 1;
                if (num[1] == 0)
                {
                    hi_prod = 0;
                    if (num[0] == 0)
                    {
                        // Left arg is zero, return right.
                        //
                        DECIMAL_LO64_SET(decRes, DECIMAL_LO64_GET(*right));
                        decRes.Hi32 = right->Hi32;
                        decRes.u.u.sign ^= sign;
                        goto RetDec;
                    }
                }
            }

            // Scaling loop, up to 10^9 at a time.  hi_prod stays updated
            // with index of highest non-zero uint32_t.
            //
            for (; scale > 0; scale -= POWER10_MAX)
            {
                if (scale > POWER10_MAX)
                    pwr = ten_to_nine;
                else
                    pwr = power10[scale];

                tmp.u.Hi = 0;
                for (cur = 0; cur <= hi_prod; cur++)
                {
                    tmp.int64 = IL2CPP_UInt32x32To64(num[cur], pwr) + tmp.u.Hi;
                    num[cur] = tmp.u.Lo;
                }

                if (tmp.u.Hi != 0)
                    // We're extending the result by another uint32_t.
                    num[++hi_prod] = tmp.u.Hi;
            }
        }

        // Scaling complete, do the add.  Could be subtract if signs differ.
        //
        tmp.u.Lo = num[0];
        tmp.u.Hi = num[1];

        if (sign)
        {
            // Signs differ, subtract.
            //
            DECIMAL_LO64_SET(decRes, tmp.int64 - DECIMAL_LO64_GET(*right));
            decRes.Hi32 = num[2] - right->Hi32;

            // Propagate carry
            //
            if (DECIMAL_LO64_GET(decRes) > tmp.int64)
            {
                decRes.Hi32--;
                if (decRes.Hi32 >= num[2])
                    goto LongSub;
            }
            else if (decRes.Hi32 > num[2])
            {
            LongSub:
                // If num has more than 96 bits of precision, then we need to
                // carry the subtraction into the higher bits.  If it doesn't,
                // then we subtracted in the wrong order and have to flip the
                // sign of the result.
                //
                if (hi_prod <= 2)
                    goto SignFlip;

                cur = 3;
                while (num[cur++]-- == 0)
                    ;
                if (num[hi_prod] == 0)
                    hi_prod--;
            }
        }
        else
        {
            // Signs the same, add.
            //
            DECIMAL_LO64_SET(decRes, tmp.int64 + DECIMAL_LO64_GET(*right));
            decRes.Hi32 = num[2] + right->Hi32;

            // Propagate carry
            //
            if (DECIMAL_LO64_GET(decRes) < tmp.int64)
            {
                decRes.Hi32++;
                if (decRes.Hi32 <= num[2])
                    goto LongAdd;
            }
            else if (decRes.Hi32 < num[2])
            {
            LongAdd:
                // Had a carry above 96 bits.
                //
                cur = 3;
                do
                {
                    if (hi_prod < cur)
                    {
                        num[cur] = 1;
                        hi_prod = cur;
                        break;
                    }
                }
                while (++num[cur++] == 0);
            }
        }

        if (hi_prod > 2)
        {
            num[0] = decRes.v.v.Lo32;
            num[1] = decRes.v.v.Mid32;
            num[2] = decRes.Hi32;
            decRes.u.u.scale = ScaleResult(num, hi_prod, decRes.u.u.scale);
            if (decRes.u.u.scale == (uint8_t)-1)
                return IL2CPP_DECIMAL_OVERFLOW;

            decRes.v.v.Lo32 = num[0];
            decRes.v.v.Mid32 = num[1];
            decRes.Hi32 = num[2];
        }
    }

RetDec:
    COPYDEC(*result, decRes);
    // Odd, the Microsoft code does not set result->reserved to zero on this case
    return IL2CPP_DECIMAL_OK;
}

// Returns IL2CPP_DECIMAL_OK or IL2CPP_DECIMAL_OVERFLOW
static Il2CppDecimalStatus
il2cpp_decimal_from_double(double input_d, Il2CppDecimal *result)
{
    int         exp;    // number of bits to left of binary point
    int         power;  // power-of-10 scale factor
    SPLIT64     sdlMant;
    SPLIT64     sdlLo;
    double      dbl;
    int         lmax, cur;  // temps used during scale reduction
    uint32_t       pwr_cur;
    uint32_t       quo;
    Il2CppDouble_double input;
    input.d = input_d;

    // The most we can scale by is 10^28, which is just slightly more
    // than 2^93.  So a float with an exponent of -94 could just
    // barely reach 0.5, but smaller exponents will always round to zero.
    //
    if ((exp = input.s.exp - IL2CPP_DOUBLE_BIAS) < -94)
    {
        DECIMAL_SETZERO(*result);
        return IL2CPP_DECIMAL_OK;
    }

    if (exp > 96)
        return IL2CPP_DECIMAL_OVERFLOW;

    // Round the input to a 15-digit integer.  The R8 format has
    // only 15 digits of precision, and we want to keep garbage digits
    // out of the Decimal were making.
    //
    // Calculate max power of 10 input value could have by multiplying
    // the exponent by log10(2).  Using scaled integer multiplcation,
    // log10(2) * 2 ^ 16 = .30103 * 65536 = 19728.3.
    //
    dbl = fabs(input.d);
    power = 14 - ((exp * 19728) >> 16);

    if (power >= 0)
    {
        // We have less than 15 digits, scale input up.
        //
        if (power > DECMAX)
            power = DECMAX;

        dbl = dbl * double_power10[power];
    }
    else
    {
        if (power != -1 || dbl >= 1E15)
            dbl = dbl / fnDblPower10(-power);
        else
            power = 0; // didn't scale it
    }

    IL2CPP_ASSERT(dbl < 1E15);
    if (dbl < 1E14 && power < DECMAX)
    {
        dbl *= 10;
        power++;
        IL2CPP_ASSERT(dbl >= 1E14);
    }

    // Round to int64
    //
    sdlMant.int64 = (int64_t)dbl;
    dbl -= (double)(int64_t)sdlMant.int64;  // dif between input & integer
    if (dbl > 0.5 || (dbl == 0.5 && (sdlMant.u.Lo & 1)))
        sdlMant.int64++;

    if (sdlMant.int64 == 0)
    {
        DECIMAL_SETZERO(*result);
        return IL2CPP_DECIMAL_OK;
    }

    if (power < 0)
    {
        // Add -power factors of 10, -power <= (29 - 15) = 14.
        //
        power = -power;
        if (power < 10)
        {
            sdlLo.int64 = IL2CPP_UInt32x32To64(sdlMant.u.Lo, (uint32_t)long_power10[power]);
            sdlMant.int64 = IL2CPP_UInt32x32To64(sdlMant.u.Hi, (uint32_t)long_power10[power]);
            sdlMant.int64 += sdlLo.u.Hi;
            sdlLo.u.Hi = sdlMant.u.Lo;
            sdlMant.u.Lo = sdlMant.u.Hi;
        }
        else
        {
            // Have a big power of 10.
            //
            IL2CPP_ASSERT(power <= 14);
            sdlLo.int64 = UInt64x64To128(sdlMant, sdl_power10[power - 10], &sdlMant.int64);

            if (sdlMant.u.Hi != 0)
                return IL2CPP_DECIMAL_OVERFLOW;
        }
        DECIMAL_LO32(*result) = sdlLo.u.Lo;
        DECIMAL_MID32(*result) = sdlLo.u.Hi;
        DECIMAL_HI32(*result) = sdlMant.u.Lo;
        DECIMAL_SCALE(*result) = 0;
    }
    else
    {
        // Factor out powers of 10 to reduce the scale, if possible.
        // The maximum number we could factor out would be 14.  This
        // comes from the fact we have a 15-digit number, and the
        // MSD must be non-zero -- but the lower 14 digits could be
        // zero.  Note also the scale factor is never negative, so
        // we can't scale by any more than the power we used to
        // get the integer.
        //
        // DivMod64by32 returns the quotient in Lo, the remainder in Hi.
        //
        lmax = std::min(power, 14);

        // lmax is the largest power of 10 to try, lmax <= 14.
        // We'll try powers 8, 4, 2, and 1 unless they're too big.
        //
        for (cur = 8; cur > 0; cur >>= 1)
        {
            if (cur > lmax)
                continue;

            pwr_cur = (uint32_t)long_power10[cur];

            if (sdlMant.u.Hi >= pwr_cur)
            {
                // Overflow if we try to divide in one step.
                //
                sdlLo.int64 = DivMod64by32(sdlMant.u.Hi, pwr_cur);
                quo = sdlLo.u.Lo;
                sdlLo.u.Lo = sdlMant.u.Lo;
                sdlLo.int64 = DivMod64by32(sdlLo.int64, pwr_cur);
            }
            else
            {
                quo = 0;
                sdlLo.int64 = DivMod64by32(sdlMant.int64, pwr_cur);
            }

            if (sdlLo.u.Hi == 0)
            {
                sdlMant.u.Hi = quo;
                sdlMant.u.Lo = sdlLo.u.Lo;
                power -= cur;
                lmax -= cur;
            }
        }

        DECIMAL_HI32(*result) = 0;
        DECIMAL_SCALE(*result) = power;
        DECIMAL_LO32(*result) = sdlMant.u.Lo;
        DECIMAL_MID32(*result) = sdlMant.u.Hi;
    }

    DECIMAL_SIGN(*result) = (char)input.s.sign << 7;
    return IL2CPP_DECIMAL_OK;
}

// Decimal multiply
// Returns: IL2CPP_DECIMAL_OVERFLOW or IL2CPP_DECIMAL_OK
static Il2CppDecimalStatus il2cpp_decimal_multiply_result(Il2CppDecimal *left, Il2CppDecimal *right, Il2CppDecimal *result)
{
    SPLIT64 tmp;
    SPLIT64 tmp2;
    SPLIT64 tmp3;
    int     scale;
    int     hi_prod;
    uint32_t   pwr;
    uint32_t   rem_lo;
    uint32_t   rem_hi;
    uint32_t   prod[6];

    scale = left->u.u.scale + right->u.u.scale;

    if ((left->Hi32 | left->v.v.Mid32 | right->Hi32 | right->v.v.Mid32) == 0)
    {
        // Upper 64 bits are zero.
        //
        tmp.int64 = IL2CPP_UInt32x32To64(left->v.v.Lo32, right->v.v.Lo32);
        if (scale > DEC_SCALE_MAX)
        {
            // Result scale is too big.  Divide result by power of 10 to reduce it.
            // If the amount to divide by is > 19 the result is guaranteed
            // less than 1/2.  [max value in 64 bits = 1.84E19]
            //
            scale -= DEC_SCALE_MAX;
            if (scale > 19)
            {
            ReturnZero:
                DECIMAL_SETZERO(*result);
                return IL2CPP_DECIMAL_OK;
            }

            if (scale > POWER10_MAX)
            {
                // Divide by 1E10 first, to get the power down to a 32-bit quantity.
                // 1E10 itself doesn't fit in 32 bits, so we'll divide by 2.5E9 now
                // then multiply the next divisor by 4 (which will be a max of 4E9).
                //
                rem_lo = FullDiv64By32(&tmp.int64, ten_to_ten_div_4);
                pwr = power10[scale - 10] << 2;
            }
            else
            {
                pwr = power10[scale];
                rem_lo = 0;
            }

            // Power to divide by fits in 32 bits.
            //
            rem_hi = FullDiv64By32(&tmp.int64, pwr);

            // Round result.  See if remainder >= 1/2 of divisor.
            // Divisor is a power of 10, so it is always even.
            //
            pwr >>= 1;
            if (rem_hi >= pwr && (rem_hi > pwr || (rem_lo | (tmp.u.Lo & 1))))
                tmp.int64++;

            scale = DEC_SCALE_MAX;
        }
        DECIMAL_LO32(*result) = tmp.u.Lo;
        DECIMAL_MID32(*result) = tmp.u.Hi;
        DECIMAL_HI32(*result) = 0;
    }
    else
    {
        // At least one operand has bits set in the upper 64 bits.
        //
        // Compute and accumulate the 9 partial products into a
        // 192-bit (24-byte) result.
        //
        //                [l-h][l-m][l-l]   left high, middle, low
        //             x  [r-h][r-m][r-l]   right high, middle, low
        // ------------------------------
        //
        //                     [0-h][0-l]   l-l * r-l
        //                [1ah][1al]        l-l * r-m
        //                [1bh][1bl]        l-m * r-l
        //           [2ah][2al]             l-m * r-m
        //           [2bh][2bl]             l-l * r-h
        //           [2ch][2cl]             l-h * r-l
        //      [3ah][3al]                  l-m * r-h
        //      [3bh][3bl]                  l-h * r-m
        // [4-h][4-l]                       l-h * r-h
        // ------------------------------
        // [p-5][p-4][p-3][p-2][p-1][p-0]   prod[] array
        //
        tmp.int64 = IL2CPP_UInt32x32To64(left->v.v.Lo32, right->v.v.Lo32);
        prod[0] = tmp.u.Lo;

        tmp2.int64 = IL2CPP_UInt32x32To64(left->v.v.Lo32, right->v.v.Mid32) + tmp.u.Hi;

        tmp.int64 = IL2CPP_UInt32x32To64(left->v.v.Mid32, right->v.v.Lo32);
        tmp.int64 += tmp2.int64; // this could generate carry
        prod[1] = tmp.u.Lo;
        if (tmp.int64 < tmp2.int64) // detect carry
            tmp2.u.Hi = 1;
        else
            tmp2.u.Hi = 0;
        tmp2.u.Lo = tmp.u.Hi;

        tmp.int64 = IL2CPP_UInt32x32To64(left->v.v.Mid32, right->v.v.Mid32) + tmp2.int64;

        if (left->Hi32 | right->Hi32)
        {
            // Highest 32 bits is non-zero.  Calculate 5 more partial products.
            //
            tmp2.int64 = IL2CPP_UInt32x32To64(left->v.v.Lo32, right->Hi32);
            tmp.int64 += tmp2.int64; // this could generate carry
            if (tmp.int64 < tmp2.int64) // detect carry
                tmp3.u.Hi = 1;
            else
                tmp3.u.Hi = 0;

            tmp2.int64 = IL2CPP_UInt32x32To64(left->Hi32, right->v.v.Lo32);
            tmp.int64 += tmp2.int64; // this could generate carry
            prod[2] = tmp.u.Lo;
            if (tmp.int64 < tmp2.int64) // detect carry
                tmp3.u.Hi++;
            tmp3.u.Lo = tmp.u.Hi;

            tmp.int64 = IL2CPP_UInt32x32To64(left->v.v.Mid32, right->Hi32);
            tmp.int64 += tmp3.int64; // this could generate carry
            if (tmp.int64 < tmp3.int64) // detect carry
                tmp3.u.Hi = 1;
            else
                tmp3.u.Hi = 0;

            tmp2.int64 = IL2CPP_UInt32x32To64(left->Hi32, right->v.v.Mid32);
            tmp.int64 += tmp2.int64; // this could generate carry
            prod[3] = tmp.u.Lo;
            if (tmp.int64 < tmp2.int64) // detect carry
                tmp3.u.Hi++;
            tmp3.u.Lo = tmp.u.Hi;

            tmp.int64 = IL2CPP_UInt32x32To64(left->Hi32, right->Hi32) + tmp3.int64;
            prod[4] = tmp.u.Lo;
            prod[5] = tmp.u.Hi;

            hi_prod = 5;
        }
        else
        {
            prod[2] = tmp.u.Lo;
            prod[3] = tmp.u.Hi;
            hi_prod = 3;
        }

        // Check for leading zero uint32_ts on the product
        //
        while (prod[hi_prod] == 0)
        {
            hi_prod--;
            if (hi_prod < 0)
                goto ReturnZero;
        }

        scale = ScaleResult(prod, hi_prod, scale);
        if (scale == -1)
            return IL2CPP_DECIMAL_OVERFLOW;

        result->v.v.Lo32 = prod[0];
        result->v.v.Mid32 = prod[1];
        result->Hi32 = prod[2];
    }

    result->u.u.sign = right->u.u.sign ^ left->u.u.sign;
    result->u.u.scale = (char)scale;
    return IL2CPP_DECIMAL_OK;
}

// Add a 32 bit unsigned long to an array of 3 unsigned longs representing a 96 integer
// Returns FALSE if there is an overflow
static bool
Add32To96(uint32_t *num, uint32_t value)
{
    num[0] += value;
    if (num[0] < value)
    {
        if (++num[1] == 0)
        {
            if (++num[2] == 0)
            {
                return false;
            }
        }
    }
    return true;
}

static void
OverflowUnscale(uint32_t *quo, bool remainder)
{
    SPLIT64  sdlTmp;

    // We have overflown, so load the high bit with a one.
    sdlTmp.u.Hi = 1u;
    sdlTmp.u.Lo = quo[2];
    sdlTmp.int64 = DivMod64by32(sdlTmp.int64, 10u);
    quo[2] = sdlTmp.u.Lo;
    sdlTmp.u.Lo = quo[1];
    sdlTmp.int64 = DivMod64by32(sdlTmp.int64, 10u);
    quo[1] = sdlTmp.u.Lo;
    sdlTmp.u.Lo = quo[0];
    sdlTmp.int64 = DivMod64by32(sdlTmp.int64, 10u);
    quo[0] = sdlTmp.u.Lo;
    // The remainder is the last digit that does not fit, so we can use it to work out if we need to round up
    if ((sdlTmp.u.Hi > 5) || ((sdlTmp.u.Hi == 5) && (remainder || (quo[0] & 1))))
    {
        Add32To96(quo, 1u);
    }
}

/**
* IncreaseScale:
*
* Entry:
*   num - Pointer to 96-bit number as array of uint32_ts, least-sig first
*   pwr   - Scale factor to multiply by
*
* Purpose:
*   Multiply the two numbers.  The low 96 bits of the result overwrite
*   the input.  The last 32 bits of the product are the return value.
*
* Exit:
*   Returns highest 32 bits of product.
*
* Exceptions:
*   None.
*
*/
static uint32_t
IncreaseScale(uint32_t *num, uint32_t pwr)
{
    SPLIT64   sdlTmp;

    sdlTmp.int64 = IL2CPP_UInt32x32To64(num[0], pwr);
    num[0] = sdlTmp.u.Lo;
    sdlTmp.int64 = IL2CPP_UInt32x32To64(num[1], pwr) + sdlTmp.u.Hi;
    num[1] = sdlTmp.u.Lo;
    sdlTmp.int64 = IL2CPP_UInt32x32To64(num[2], pwr) + sdlTmp.u.Hi;
    num[2] = sdlTmp.u.Lo;
    return sdlTmp.u.Hi;
}

/***
* Div128By96
*
* Entry:
*   rgulNum - Pointer to 128-bit dividend as array of uint32_ts, least-sig first
*   den - Pointer to 96-bit divisor.
*
* Purpose:
*   Do partial divide, yielding 32-bit result and 96-bit remainder.
*   Top divisor uint32_t must be larger than top dividend uint32_t.  This is
*   assured in the initial call because the divisor is normalized
*   and the dividend can't be.  In subsequent calls, the remainder
*   is multiplied by 10^9 (max), so it can be no more than 1/4 of
*   the divisor which is effectively multiplied by 2^32 (4 * 10^9).
*
* Exit:
*   Remainder overwrites lower 96-bits of dividend.
*   Returns quotient.
*
* Exceptions:
*   None.
*
***********************************************************************/

static uint32_t
Div128By96(uint32_t *num, uint32_t *den)
{
    SPLIT64 sdlQuo;
    SPLIT64 sdlNum;
    SPLIT64 sdlProd1;
    SPLIT64 sdlProd2;

    sdlNum.u.Lo = num[0];
    sdlNum.u.Hi = num[1];

    if (num[3] == 0 && num[2] < den[2])
    {
        // Result is zero.  Entire dividend is remainder.
        //
        return 0;
    }

    // DivMod64by32 returns quotient in Lo, remainder in Hi.
    //
    sdlQuo.u.Lo = num[2];
    sdlQuo.u.Hi = num[3];
    sdlQuo.int64 = DivMod64by32(sdlQuo.int64, den[2]);

    // Compute full remainder, rem = dividend - (quo * divisor).
    //
    sdlProd1.int64 = IL2CPP_UInt32x32To64(sdlQuo.u.Lo, den[0]); // quo * lo divisor
    sdlProd2.int64 = IL2CPP_UInt32x32To64(sdlQuo.u.Lo, den[1]); // quo * mid divisor
    sdlProd2.int64 += sdlProd1.u.Hi;
    sdlProd1.u.Hi = sdlProd2.u.Lo;

    sdlNum.int64 -= sdlProd1.int64;
    num[2] = sdlQuo.u.Hi - sdlProd2.u.Hi; // sdlQuo.Hi is remainder

    // Propagate carries
    //
    if (sdlNum.int64 > ~sdlProd1.int64)
    {
        num[2]--;
        if (num[2] >= ~sdlProd2.u.Hi)
            goto NegRem;
    }
    else if (num[2] > ~sdlProd2.u.Hi)
    {
    NegRem:
        // Remainder went negative.  Add divisor back in until it's positive,
        // a max of 2 times.
        //
        sdlProd1.u.Lo = den[0];
        sdlProd1.u.Hi = den[1];

        for (;;)
        {
            sdlQuo.u.Lo--;
            sdlNum.int64 += sdlProd1.int64;
            num[2] += den[2];

            if (sdlNum.int64 < sdlProd1.int64)
            {
                // Detected carry. Check for carry out of top
                // before adding it in.
                //
                if (num[2]++ < den[2])
                    break;
            }
            if (num[2] < den[2])
                break; // detected carry
        }
    }

    num[0] = sdlNum.u.Lo;
    num[1] = sdlNum.u.Hi;
    return sdlQuo.u.Lo;
}

/**
* Div96By64:
*
* Entry:
*   rgulNum - Pointer to 96-bit dividend as array of uint32_ts, least-sig first
*   sdlDen  - 64-bit divisor.
*
* Purpose:
*   Do partial divide, yielding 32-bit result and 64-bit remainder.
*   Divisor must be larger than upper 64 bits of dividend.
*
* Exit:
*   Remainder overwrites lower 64-bits of dividend.
*   Returns quotient.
*
* Exceptions:
*   None.
*
*/
static uint32_t
Div96By64(uint32_t *num, SPLIT64 den)
{
    SPLIT64 quo;
    SPLIT64 sdlNum;
    SPLIT64 prod;

    sdlNum.u.Lo = num[0];

    if (num[2] >= den.u.Hi)
    {
        // Divide would overflow.  Assume a quotient of 2^32, and set
        // up remainder accordingly.  Then jump to loop which reduces
        // the quotient.
        //
        sdlNum.u.Hi = num[1] - den.u.Lo;
        quo.u.Lo = 0;
        goto NegRem;
    }

    // Hardware divide won't overflow
    //
    if (num[2] == 0 && num[1] < den.u.Hi)
        // Result is zero.  Entire dividend is remainder.
        //
        return 0;

    // DivMod64by32 returns quotient in Lo, remainder in Hi.
    //
    quo.u.Lo = num[1];
    quo.u.Hi = num[2];
    quo.int64 = DivMod64by32(quo.int64, den.u.Hi);
    sdlNum.u.Hi = quo.u.Hi; // remainder

    // Compute full remainder, rem = dividend - (quo * divisor).
    //
    prod.int64 = IL2CPP_UInt32x32To64(quo.u.Lo, den.u.Lo); // quo * lo divisor
    sdlNum.int64 -= prod.int64;

    if (sdlNum.int64 > ~prod.int64)
    {
    NegRem:
        // Remainder went negative.  Add divisor back in until it's positive,
        // a max of 2 times.
        //
        do
        {
            quo.u.Lo--;
            sdlNum.int64 += den.int64;
        }
        while (sdlNum.int64 >= den.int64);
    }

    num[0] = sdlNum.u.Lo;
    num[1] = sdlNum.u.Hi;
    return quo.u.Lo;
}

//
// Returns: IL2CPP_DECIMAL_INVALID_ARGUMENT, IL2CPP_DECIMAL_OK
//
static Il2CppDecimalStatus
il2cpp_decimal_round_result(Il2CppDecimal *input, int cDecimals, Il2CppDecimal *result)
{
    uint32_t num[3];
    uint32_t rem;
    uint32_t sticky;
    uint32_t pwr;
    int scale;

    if (cDecimals < 0)
        return IL2CPP_DECIMAL_INVALID_ARGUMENT;

    scale = input->u.u.scale - cDecimals;
    if (scale > 0)
    {
        num[0] = input->v.v.Lo32;
        num[1] = input->v.v.Mid32;
        num[2] = input->Hi32;
        result->u.u.sign = input->u.u.sign;
        rem = sticky = 0;

        do
        {
            sticky |= rem;
            if (scale > POWER10_MAX)
                pwr = ten_to_nine;
            else
                pwr = power10[scale];

            rem = Div96By32(num, pwr);
            scale -= 9;
        }
        while (scale > 0);

        // Now round.  rem has last remainder, sticky has sticky bits.
        // To do IEEE rounding, we add LSB of result to sticky bits so
        // either causes round up if remainder * 2 == last divisor.
        //
        sticky |= num[0] & 1;
        rem = (rem << 1) + (sticky != 0);
        if (pwr < rem &&
            ++num[0] == 0 &&
            ++num[1] == 0
        )
            ++num[2];

        result->v.v.Lo32 = num[0];
        result->v.v.Mid32 = num[1];
        result->Hi32 = num[2];
        result->u.u.scale = cDecimals;
        return IL2CPP_DECIMAL_OK;
    }

    COPYDEC(*result, *input);
    // Odd, the Microsoft source does not set the result->reserved to zero here.
    return IL2CPP_DECIMAL_OK;
}

//
// Returns IL2CPP_DECIMAL_OK or IL2CPP_DECIMAL_OVERFLOW
static Il2CppDecimalStatus
il2cpp_decimal_from_float(float input_f, Il2CppDecimal* result)
{
    int         exp;    // number of bits to left of binary point
    int         power;
    uint32_t       mant;
    double      dbl;
    SPLIT64     sdlLo;
    SPLIT64     sdlHi;
    int         lmax, cur;  // temps used during scale reduction
    Il2CppSingle_float input;
    input.f = input_f;

    // The most we can scale by is 10^28, which is just slightly more
    // than 2^93.  So a float with an exponent of -94 could just
    // barely reach 0.5, but smaller exponents will always round to zero.
    //
    if ((exp = input.s.exp - IL2CPP_SINGLE_BIAS) < -94)
    {
        DECIMAL_SETZERO(*result);
        return IL2CPP_DECIMAL_OK;
    }

    if (exp > 96)
        return IL2CPP_DECIMAL_OVERFLOW;

    // Round the input to a 7-digit integer.  The R4 format has
    // only 7 digits of precision, and we want to keep garbage digits
    // out of the Decimal were making.
    //
    // Calculate max power of 10 input value could have by multiplying
    // the exponent by log10(2).  Using scaled integer multiplcation,
    // log10(2) * 2 ^ 16 = .30103 * 65536 = 19728.3.
    //
    dbl = fabs(input.f);
    power = 6 - ((exp * 19728) >> 16);

    if (power >= 0)
    {
        // We have less than 7 digits, scale input up.
        //
        if (power > DECMAX)
            power = DECMAX;

        dbl = dbl * double_power10[power];
    }
    else
    {
        if (power != -1 || dbl >= 1E7)
            dbl = dbl / fnDblPower10(-power);
        else
            power = 0; // didn't scale it
    }

    IL2CPP_ASSERT(dbl < 1E7);
    if (dbl < 1E6 && power < DECMAX)
    {
        dbl *= 10;
        power++;
        IL2CPP_ASSERT(dbl >= 1E6);
    }

    // Round to integer
    //
    mant = (int32_t)dbl;
    dbl -= (double)mant;  // difference between input & integer
    if (dbl > 0.5 || (dbl == 0.5 && (mant & 1)))
        mant++;

    if (mant == 0)
    {
        DECIMAL_SETZERO(*result);
        return IL2CPP_DECIMAL_OK;
    }

    if (power < 0)
    {
        // Add -power factors of 10, -power <= (29 - 7) = 22.
        //
        power = -power;
        if (power < 10)
        {
            sdlLo.int64 = IL2CPP_UInt32x32To64(mant, (uint32_t)long_power10[power]);

            DECIMAL_LO32(*result) = sdlLo.u.Lo;
            DECIMAL_MID32(*result) = sdlLo.u.Hi;
            DECIMAL_HI32(*result) = 0;
        }
        else
        {
            // Have a big power of 10.
            //
            if (power > 18)
            {
                sdlLo.int64 = IL2CPP_UInt32x32To64(mant, (uint32_t)long_power10[power - 18]);
                sdlLo.int64 = UInt64x64To128(sdlLo, ten_to_eighteen, &sdlHi.int64);

                if (sdlHi.u.Hi != 0)
                    return IL2CPP_DECIMAL_OVERFLOW;
            }
            else
            {
                sdlLo.int64 = IL2CPP_UInt32x32To64(mant, (uint32_t)long_power10[power - 9]);
                sdlHi.int64 = IL2CPP_UInt32x32To64(ten_to_nine, sdlLo.u.Hi);
                sdlLo.int64 = IL2CPP_UInt32x32To64(ten_to_nine, sdlLo.u.Lo);
                sdlHi.int64 += sdlLo.u.Hi;
                sdlLo.u.Hi = sdlHi.u.Lo;
                sdlHi.u.Lo = sdlHi.u.Hi;
            }
            DECIMAL_LO32(*result) = sdlLo.u.Lo;
            DECIMAL_MID32(*result) = sdlLo.u.Hi;
            DECIMAL_HI32(*result) = sdlHi.u.Lo;
        }
        DECIMAL_SCALE(*result) = 0;
    }
    else
    {
        // Factor out powers of 10 to reduce the scale, if possible.
        // The maximum number we could factor out would be 6.  This
        // comes from the fact we have a 7-digit number, and the
        // MSD must be non-zero -- but the lower 6 digits could be
        // zero.  Note also the scale factor is never negative, so
        // we can't scale by any more than the power we used to
        // get the integer.
        //
        // DivMod32by32 returns the quotient in Lo, the remainder in Hi.
        //
        lmax = std::min(power, 6);

        // lmax is the largest power of 10 to try, lmax <= 6.
        // We'll try powers 4, 2, and 1 unless they're too big.
        //
        for (cur = 4; cur > 0; cur >>= 1)
        {
            if (cur > lmax)
                continue;

            sdlLo.int64 = DivMod32by32(mant, (uint32_t)long_power10[cur]);

            if (sdlLo.u.Hi == 0)
            {
                mant = sdlLo.u.Lo;
                power -= cur;
                lmax -= cur;
            }
        }
        DECIMAL_LO32(*result) = mant;
        DECIMAL_MID32(*result) = 0;
        DECIMAL_HI32(*result) = 0;
        DECIMAL_SCALE(*result) = power;
    }

    DECIMAL_SIGN(*result) = (char)input.s.sign << 7;
    return IL2CPP_DECIMAL_OK;
}

// il2cpp_decimal_round_to_int - Decimal Int (round down to integer)
static void
il2cpp_decimal_round_to_int(Il2CppDecimal *pdecOprd, Il2CppDecimal *result)
{
    if (DecFixInt(result, pdecOprd) != 0 && (result->u.u.sign & DECIMAL_NEG))
    {
        // We have chopped off a non-zero amount from a negative value.  Since
        // we round toward -infinity, we must increase the integer result by
        // 1 to make it more negative.  This will never overflow because
        // in order to have a remainder, we must have had a non-zero scale factor.
        // Our scale factor is back to zero now.
        //
        DECIMAL_LO64_SET(*result, DECIMAL_LO64_GET(*result) + 1);
        if (DECIMAL_LO64_GET(*result) == 0)
            result->Hi32++;
    }
}

static void
il2cpp_decimal_fix(Il2CppDecimal *pdecOprd, Il2CppDecimal *result)
{
    DecFixInt(result, pdecOprd);
}

namespace il2cpp
{
namespace icalls
{
namespace mscorlib
{
namespace System
{
    double Decimal::ToDouble(Il2CppDecimal d)
    {
        double result = 0.0;
        // Note: this can fail if the input is an invalid decimal, but for compatibility we should return 0
        il2cpp_decimal_to_double_result(&d, &result);
        return result;
    }

    int32_t Decimal::FCallCompare(Il2CppDecimal* left, Il2CppDecimal* right)
    {
        uint32_t   left_sign;
        uint32_t   right_sign;
        Il2CppDecimal result;

        result.Hi32 = 0; // Just to shut up the compiler

        // First check signs and whether either are zero.  If both are
        // non-zero and of the same sign, just use subtraction to compare.
        //
        left_sign = left->v.v.Lo32 | left->v.v.Mid32 | left->Hi32;
        right_sign = right->v.v.Lo32 | right->v.v.Mid32 | right->Hi32;
        if (left_sign != 0)
            left_sign = (left->u.u.sign & DECIMAL_NEG) | 1;

        if (right_sign != 0)
            right_sign = (right->u.u.sign & DECIMAL_NEG) | 1;

        // left_sign & right_sign have values 1, 0, or 0x81 depending on if the left/right
        // operand is +, 0, or -.
        //
        if (left_sign == right_sign)
        {
            if (left_sign == 0) // both are zero
                return IL2CPP_DECIMAL_CMP_EQ; // return equal

            DecAddSub(left, right, &result, DECIMAL_NEG);
            if (DECIMAL_LO64_GET(result) == 0 && result.Hi32 == 0)
                return IL2CPP_DECIMAL_CMP_EQ;
            if (result.u.u.sign & DECIMAL_NEG)
                return IL2CPP_DECIMAL_CMP_LT;
            return IL2CPP_DECIMAL_CMP_GT;
        }

        //
        // Signs are different.  Use signed byte comparison
        //
        if ((signed char)left_sign > (signed char)right_sign)
            return IL2CPP_DECIMAL_CMP_GT;
        return IL2CPP_DECIMAL_CMP_LT;
    }

    int32_t Decimal::FCallToInt32(Il2CppDecimal d)
    {
        Il2CppDecimal result;

        // The following can not return an error, it only returns INVALID_ARG if the decimals is < 0
        il2cpp_decimal_round_result(&d, 0, &result);

        if (DECIMAL_SCALE(result) != 0)
        {
            d = result;
            il2cpp_decimal_fix(&d, &result);
        }

        if (DECIMAL_HI32(result) == 0 && DECIMAL_MID32(result) == 0)
        {
            int32_t i = DECIMAL_LO32(result);
            if ((int16_t)DECIMAL_SIGNSCALE(result) >= 0)
            {
                if (i >= 0)
                    return i;
            }
            else
            {
                // i is signed here, so for 2147483648 we would see -2147483648 here
                // negating it is undefined behavior by C++ standard (overflow)
                // if changing this, make sure to check -2147483648 converts to Int32
                if (i <= INT32_MAX)
                    return -i;
                else if (i == INT32_MIN)
                    return i;
            }
        }

        vm::Exception::RaiseOverflowException();
        return 0;
    }

    int32_t Decimal::GetHashCode(Il2CppDecimal* _this)
    {
        double dbl;

        if (il2cpp_decimal_to_double_result(_this, &dbl) != IL2CPP_DECIMAL_OK)
            return 0;

        if (dbl == 0.0)
        {
            // Ensure 0 and -0 have the same hash code
            return 0;
        }
        // conversion to double is lossy and produces rounding errors so we mask off the lowest 4 bits
        //
        // For example these two numerically equal decimals with different internal representations produce
        // slightly different results when converted to double:
        //
        // decimal a = new decimal(new int[] { 0x76969696, 0x2fdd49fa, 0x409783ff, 0x00160000 });
        //                     => (decimal)1999021.176470588235294117647000000000 => (double)1999021.176470588
        // decimal b = new decimal(new int[] { 0x3f0f0f0f, 0x1e62edcc, 0x06758d33, 0x00150000 });
        //                     => (decimal)1999021.176470588235294117647000000000 => (double)1999021.1764705882
        //
        return ((((int*)&dbl)[0]) & 0xFFFFFFF0) ^ ((int*)&dbl)[1];
    }

    float Decimal::ToSingle(Il2CppDecimal d)
    {
        float result = 0.0f;
        // Note: this can fail if the input is an invalid decimal, but for compatibility we should return 0
        il2cpp_decimal_to_float_result(&d, &result);
        return result;
    }

    void Decimal::ConstructorDouble(Il2CppDecimal* _this, double value)
    {
        if (il2cpp_decimal_from_double(value, _this) == IL2CPP_DECIMAL_OVERFLOW)
        {
            vm::Exception::RaiseOverflowException();
            return;
        }
        _this->reserved = 0;
    }

    void Decimal::ConstructorFloat(Il2CppDecimal* _this, float value)
    {
        if (il2cpp_decimal_from_float(value, _this) == IL2CPP_DECIMAL_OVERFLOW)
        {
            vm::Exception::RaiseOverflowException();
            return;
        }
        _this->reserved = 0;
    }

    void Decimal::FCallAddSub(Il2CppDecimal* left, Il2CppDecimal* right, uint8_t sign)
    {
        Il2CppDecimal result, decTmp;
        Il2CppDecimal *pdecTmp, *leftOriginal;
        uint32_t    num[6], pwr;
        int         scale, hi_prod, cur;
        SPLIT64     sdlTmp;

        IL2CPP_ASSERT(sign == 0 || sign == DECIMAL_NEG);

        leftOriginal = left;

        sign ^= (DECIMAL_SIGN(*right) ^ DECIMAL_SIGN(*left)) & DECIMAL_NEG;

        if (DECIMAL_SCALE(*right) == DECIMAL_SCALE(*left))
        {
            // Scale factors are equal, no alignment necessary.
            //
            DECIMAL_SIGNSCALE(result) = DECIMAL_SIGNSCALE(*left);

        AlignedAdd:
            if (sign)
            {
                // Signs differ - subtract
                //
                DECIMAL_LO64_SET(result, (DECIMAL_LO64_GET(*left) - DECIMAL_LO64_GET(*right)));
                DECIMAL_HI32(result) = DECIMAL_HI32(*left) - DECIMAL_HI32(*right);

                // Propagate carry
                //
                if (DECIMAL_LO64_GET(result) > DECIMAL_LO64_GET(*left))
                {
                    DECIMAL_HI32(result)--;
                    if (DECIMAL_HI32(result) >= DECIMAL_HI32(*left))
                        goto SignFlip;
                }
                else if (DECIMAL_HI32(result) > DECIMAL_HI32(*left))
                {
                    // Got negative result.  Flip its sign.
                    //
                SignFlip:
                    DECIMAL_LO64_SET(result, -(int64_t)DECIMAL_LO64_GET(result));
                    DECIMAL_HI32(result) = ~DECIMAL_HI32(result);
                    if (DECIMAL_LO64_GET(result) == 0)
                        DECIMAL_HI32(result)++;
                    DECIMAL_SIGN(result) ^= DECIMAL_NEG;
                }
            }
            else
            {
                // Signs are the same - add
                //
                DECIMAL_LO64_SET(result, (DECIMAL_LO64_GET(*left) + DECIMAL_LO64_GET(*right)));
                DECIMAL_HI32(result) = DECIMAL_HI32(*left) + DECIMAL_HI32(*right);

                // Propagate carry
                //
                if (DECIMAL_LO64_GET(result) < DECIMAL_LO64_GET(*left))
                {
                    DECIMAL_HI32(result)++;
                    if (DECIMAL_HI32(result) <= DECIMAL_HI32(*left))
                        goto AlignedScale;
                }
                else if (DECIMAL_HI32(result) < DECIMAL_HI32(*left))
                {
                AlignedScale:
                    // The addition carried above 96 bits.  Divide the result by 10,
                    // dropping the scale factor.
                    //
                    if (DECIMAL_SCALE(result) == 0)
                    {
                        vm::Exception::RaiseOverflowException();
                        return;
                    }
                    DECIMAL_SCALE(result)--;

                    sdlTmp.u.Lo = DECIMAL_HI32(result);
                    sdlTmp.u.Hi = 1;
                    sdlTmp.int64 = DivMod64by32(sdlTmp.int64, 10);
                    DECIMAL_HI32(result) = sdlTmp.u.Lo;

                    sdlTmp.u.Lo = DECIMAL_MID32(result);
                    sdlTmp.int64 = DivMod64by32(sdlTmp.int64, 10);
                    DECIMAL_MID32(result) = sdlTmp.u.Lo;

                    sdlTmp.u.Lo = DECIMAL_LO32(result);
                    sdlTmp.int64 = DivMod64by32(sdlTmp.int64, 10);
                    DECIMAL_LO32(result) = sdlTmp.u.Lo;

                    // See if we need to round up.
                    //
                    if (sdlTmp.u.Hi >= 5 && (sdlTmp.u.Hi > 5 || (DECIMAL_LO32(result) & 1)))
                    {
                        DECIMAL_LO64_SET(result, DECIMAL_LO64_GET(result) + 1);
                        if (DECIMAL_LO64_GET(result) == 0)
                            DECIMAL_HI32(result)++;
                    }
                }
            }
        }
        else
        {
            // Scale factors are not equal.  Assume that a larger scale
            // factor (more decimal places) is likely to mean that number
            // is smaller.  Start by guessing that the right operand has
            // the larger scale factor.  The result will have the larger
            // scale factor.
            //
            DECIMAL_SCALE(result) = DECIMAL_SCALE(*right); // scale factor of "smaller"
            DECIMAL_SIGN(result) = DECIMAL_SIGN(*left); // but sign of "larger"
            scale = DECIMAL_SCALE(result) - DECIMAL_SCALE(*left);

            if (scale < 0)
            {
                // Guessed scale factor wrong. Swap operands.
                //
                scale = -scale;
                DECIMAL_SCALE(result) = DECIMAL_SCALE(*left);
                DECIMAL_SIGN(result) ^= sign;
                pdecTmp = right;
                right = left;
                left = pdecTmp;
            }

            // *left will need to be multiplied by 10^scale so
            // it will have the same scale as *right.  We could be
            // extending it to up to 192 bits of precision.
            //
            if (scale <= POWER10_MAX)
            {
                // Scaling won't make it larger than 4 uint32_ts
                //
                pwr = power10[scale];
                DECIMAL_LO64_SET(decTmp, IL2CPP_UInt32x32To64(DECIMAL_LO32(*left), pwr));
                sdlTmp.int64 = IL2CPP_UInt32x32To64(DECIMAL_MID32(*left), pwr);
                sdlTmp.int64 += DECIMAL_MID32(decTmp);
                DECIMAL_MID32(decTmp) = sdlTmp.u.Lo;
                DECIMAL_HI32(decTmp) = sdlTmp.u.Hi;
                sdlTmp.int64 = IL2CPP_UInt32x32To64(DECIMAL_HI32(*left), pwr);
                sdlTmp.int64 += DECIMAL_HI32(decTmp);
                if (sdlTmp.u.Hi == 0)
                {
                    // Result fits in 96 bits.  Use standard aligned add.
                    //
                    DECIMAL_HI32(decTmp) = sdlTmp.u.Lo;
                    left = &decTmp;
                    goto AlignedAdd;
                }
                num[0] = DECIMAL_LO32(decTmp);
                num[1] = DECIMAL_MID32(decTmp);
                num[2] = sdlTmp.u.Lo;
                num[3] = sdlTmp.u.Hi;
                hi_prod = 3;
            }
            else
            {
                // Have to scale by a bunch.  Move the number to a buffer
                // where it has room to grow as it's scaled.
                //
                num[0] = DECIMAL_LO32(*left);
                num[1] = DECIMAL_MID32(*left);
                num[2] = DECIMAL_HI32(*left);
                hi_prod = 2;

                // Scan for zeros in the upper words.
                //
                if (num[2] == 0)
                {
                    hi_prod = 1;
                    if (num[1] == 0)
                    {
                        hi_prod = 0;
                        if (num[0] == 0)
                        {
                            // Left arg is zero, return right.
                            //
                            DECIMAL_LO64_SET(result, DECIMAL_LO64_GET(*right));
                            DECIMAL_HI32(result) = DECIMAL_HI32(*right);
                            DECIMAL_SIGN(result) ^= sign;
                            goto RetDec;
                        }
                    }
                }

                // Scaling loop, up to 10^9 at a time.  hi_prod stays updated
                // with index of highest non-zero uint32_t.
                //
                for (; scale > 0; scale -= POWER10_MAX)
                {
                    if (scale > POWER10_MAX)
                        pwr = ten_to_nine;
                    else
                        pwr = power10[scale];

                    sdlTmp.u.Hi = 0;
                    for (cur = 0; cur <= hi_prod; cur++)
                    {
                        sdlTmp.int64 = IL2CPP_UInt32x32To64(num[cur], pwr) + sdlTmp.u.Hi;
                        num[cur] = sdlTmp.u.Lo;
                    }

                    if (sdlTmp.u.Hi != 0)
                        // We're extending the result by another uint32_t.
                        num[++hi_prod] = sdlTmp.u.Hi;
                }
            }

            // Scaling complete, do the add.  Could be subtract if signs differ.
            //
            sdlTmp.u.Lo = num[0];
            sdlTmp.u.Hi = num[1];

            if (sign)
            {
                // Signs differ, subtract.
                //
                DECIMAL_LO64_SET(result, (sdlTmp.int64 - DECIMAL_LO64_GET(*right)));
                DECIMAL_HI32(result) = num[2] - DECIMAL_HI32(*right);

                // Propagate carry
                //
                if (DECIMAL_LO64_GET(result) > sdlTmp.int64)
                {
                    DECIMAL_HI32(result)--;
                    if (DECIMAL_HI32(result) >= num[2])
                        goto LongSub;
                }
                else if (DECIMAL_HI32(result) > num[2])
                {
                LongSub:
                    // If num has more than 96 bits of precision, then we need to
                    // carry the subtraction into the higher bits.  If it doesn't,
                    // then we subtracted in the wrong order and have to flip the
                    // sign of the result.
                    //
                    if (hi_prod <= 2)
                        goto SignFlip;

                    cur = 3;
                    while (num[cur++]-- == 0)
                        ;
                    if (num[hi_prod] == 0)
                        hi_prod--;
                }
            }
            else
            {
                // Signs the same, add.
                //
                DECIMAL_LO64_SET(result, (sdlTmp.int64 + DECIMAL_LO64_GET(*right)));
                DECIMAL_HI32(result) = num[2] + DECIMAL_HI32(*right);

                // Propagate carry
                //
                if (DECIMAL_LO64_GET(result) < sdlTmp.int64)
                {
                    DECIMAL_HI32(result)++;
                    if (DECIMAL_HI32(result) <= num[2])
                        goto LongAdd;
                }
                else if (DECIMAL_HI32(result) < num[2])
                {
                LongAdd:
                    // Had a carry above 96 bits.
                    //
                    cur = 3;
                    do
                    {
                        if (hi_prod < cur)
                        {
                            num[cur] = 1;
                            hi_prod = cur;
                            break;
                        }
                    }
                    while (++num[cur++] == 0);
                }
            }

            if (hi_prod > 2)
            {
                num[0] = DECIMAL_LO32(result);
                num[1] = DECIMAL_MID32(result);
                num[2] = DECIMAL_HI32(result);
                DECIMAL_SCALE(result) = (uint8_t)ScaleResult(num, hi_prod, DECIMAL_SCALE(result));
                if (DECIMAL_SCALE(result) == (uint8_t)-1)
                {
                    vm::Exception::RaiseOverflowException();
                    return;
                }

                DECIMAL_LO32(result) = num[0];
                DECIMAL_MID32(result) = num[1];
                DECIMAL_HI32(result) = num[2];
            }
        }

    RetDec:
        left = leftOriginal;
        COPYDEC(*left, result);
        left->reserved = 0;
    }

    void Decimal::FCallDivide(Il2CppDecimal* left, Il2CppDecimal* right)
    {
        uint32_t quo[3], quo_save[3], rem[4], divisor[3];
        uint32_t pwr, tmp, tmp1;
        SPLIT64  sdlTmp, sdlDivisor;
        int      scale, cur_scale;
        bool unscale;

        scale = DECIMAL_SCALE(*left) - DECIMAL_SCALE(*right);
        unscale = false;
        divisor[0] = DECIMAL_LO32(*right);
        divisor[1] = DECIMAL_MID32(*right);
        divisor[2] = DECIMAL_HI32(*right);

        if (divisor[1] == 0 && divisor[2] == 0)
        {
            // Divisor is only 32 bits.  Easy divide.
            //
            if (divisor[0] == 0)
            {
                vm::Exception::RaiseDivideByZeroException();
                return;
            }

            quo[0] = DECIMAL_LO32(*left);
            quo[1] = DECIMAL_MID32(*left);
            quo[2] = DECIMAL_HI32(*left);
            rem[0] = Div96By32(quo, divisor[0]);

            for (;;)
            {
                if (rem[0] == 0)
                {
                    if (scale < 0)
                    {
                        cur_scale = std::min(9, -scale);
                        goto HaveScale;
                    }
                    break;
                }
                // We need to unscale if and only if we have a non-zero remainder
                unscale = true;

                // We have computed a quotient based on the natural scale
                // ( <dividend scale> - <divisor scale> ).  We have a non-zero
                // remainder, so now we should increase the scale if possible to
                // include more quotient bits.
                //
                // If it doesn't cause overflow, we'll loop scaling by 10^9 and
                // computing more quotient bits as long as the remainder stays
                // non-zero.  If scaling by that much would cause overflow, we'll
                // drop out of the loop and scale by as much as we can.
                //
                // Scaling by 10^9 will overflow if quo[2].quo[1] >= 2^32 / 10^9
                // = 4.294 967 296.  So the upper limit is quo[2] == 4 and
                // quo[1] == 0.294 967 296 * 2^32 = 1,266,874,889.7+.  Since
                // quotient bits in quo[0] could be all 1's, then 1,266,874,888
                // is the largest value in quo[1] (when quo[2] == 4) that is
                // assured not to overflow.
                //
                cur_scale = SearchScale(quo[2], quo[1], quo[0], scale);
                if (cur_scale == 0)
                {
                    // No more scaling to be done, but remainder is non-zero.
                    // Round quotient.T
                    //
                    tmp = rem[0] << 1;
                    if (tmp < rem[0] || (tmp >= divisor[0] &&
                                         (tmp > divisor[0] || (quo[0] & 1))))
                    {
                    RoundUp:
                        if (!Add32To96(quo, 1))
                        {
                            if (scale == 0)
                            {
                                vm::Exception::RaiseOverflowException();
                                return;
                            }
                            scale--;
                            OverflowUnscale(quo, true);
                            break;
                        }
                    }
                    break;
                }

                if (cur_scale < 0)
                {
                    vm::Exception::RaiseOverflowException();
                    return;
                }

            HaveScale:
                pwr = power10[cur_scale];
                scale += cur_scale;

                if (IncreaseScale(quo, pwr) != 0)
                {
                    vm::Exception::RaiseOverflowException();
                    return;
                }

                sdlTmp.int64 = DivMod64by32(IL2CPP_UInt32x32To64(rem[0], pwr), divisor[0]);
                rem[0] = sdlTmp.u.Hi;

                if (!Add32To96(quo, sdlTmp.u.Lo))
                {
                    if (scale == 0)
                    {
                        vm::Exception::RaiseOverflowException();
                        return;
                    }
                    scale--;
                    OverflowUnscale(quo, (rem[0] != 0));
                    break;
                }
            } // for (;;)
        }
        else
        {
            // Divisor has bits set in the upper 64 bits.
            //
            // Divisor must be fully normalized (shifted so bit 31 of the most
            // significant uint32_t is 1).  Locate the MSB so we know how much to
            // normalize by.  The dividend will be shifted by the same amount so
            // the quotient is not changed.
            //
            if (divisor[2] == 0)
                tmp = divisor[1];
            else
                tmp = divisor[2];

            cur_scale = 0;
            if (!(tmp & 0xFFFF0000))
            {
                cur_scale += 16;
                tmp <<= 16;
            }
            if (!(tmp & 0xFF000000))
            {
                cur_scale += 8;
                tmp <<= 8;
            }
            if (!(tmp & 0xF0000000))
            {
                cur_scale += 4;
                tmp <<= 4;
            }
            if (!(tmp & 0xC0000000))
            {
                cur_scale += 2;
                tmp <<= 2;
            }
            if (!(tmp & 0x80000000))
            {
                cur_scale++;
                tmp <<= 1;
            }

            // Shift both dividend and divisor left by cur_scale.
            //
            sdlTmp.int64 = DECIMAL_LO64_GET(*left) << cur_scale;
            rem[0] = sdlTmp.u.Lo;
            rem[1] = sdlTmp.u.Hi;
            sdlTmp.u.Lo = DECIMAL_MID32(*left);
            sdlTmp.u.Hi = DECIMAL_HI32(*left);
            sdlTmp.int64 <<= cur_scale;
            rem[2] = sdlTmp.u.Hi;
            rem[3] = (DECIMAL_HI32(*left) >> (31 - cur_scale)) >> 1;

            sdlDivisor.u.Lo = divisor[0];
            sdlDivisor.u.Hi = divisor[1];
            sdlDivisor.int64 <<= cur_scale;

            if (divisor[2] == 0)
            {
                // Have a 64-bit divisor in sdlDivisor.  The remainder
                // (currently 96 bits spread over 4 uint32_ts) will be < divisor.
                //
                sdlTmp.u.Lo = rem[2];
                sdlTmp.u.Hi = rem[3];

                quo[2] = 0;
                quo[1] = Div96By64(&rem[1], sdlDivisor);
                quo[0] = Div96By64(rem, sdlDivisor);

                for (;;)
                {
                    if ((rem[0] | rem[1]) == 0)
                    {
                        if (scale < 0)
                        {
                            cur_scale = std::min(9, -scale);
                            goto HaveScale64;
                        }
                        break;
                    }

                    // We need to unscale if and only if we have a non-zero remainder
                    unscale = true;

                    // Remainder is non-zero.  Scale up quotient and remainder by
                    // powers of 10 so we can compute more significant bits.
                    //
                    cur_scale = SearchScale(quo[2], quo[1], quo[0], scale);
                    if (cur_scale == 0)
                    {
                        // No more scaling to be done, but remainder is non-zero.
                        // Round quotient.
                        //
                        sdlTmp.u.Lo = rem[0];
                        sdlTmp.u.Hi = rem[1];
                        if (sdlTmp.u.Hi >= 0x80000000 || (sdlTmp.int64 <<= 1) > sdlDivisor.int64 ||
                            (sdlTmp.int64 == sdlDivisor.int64 && (quo[0] & 1)))
                            goto RoundUp;
                        break;
                    }

                    if (cur_scale < 0)
                    {
                        vm::Exception::RaiseOverflowException();
                        return;
                    }

                HaveScale64:
                    pwr = power10[cur_scale];
                    scale += cur_scale;

                    if (IncreaseScale(quo, pwr) != 0)
                    {
                        vm::Exception::RaiseOverflowException();
                        return;
                    }

                    rem[2] = 0; // rem is 64 bits, IncreaseScale uses 96
                    IncreaseScale(rem, pwr);
                    tmp = Div96By64(rem, sdlDivisor);
                    if (!Add32To96(quo, tmp))
                    {
                        if (scale == 0)
                        {
                            vm::Exception::RaiseOverflowException();
                            return;
                        }
                        scale--;
                        OverflowUnscale(quo, (rem[0] != 0 || rem[1] != 0));
                        break;
                    }
                } // for (;;)
            }
            else
            {
                // Have a 96-bit divisor in divisor[].
                //
                // Start by finishing the shift left by cur_scale.
                //
                sdlTmp.u.Lo = divisor[1];
                sdlTmp.u.Hi = divisor[2];
                sdlTmp.int64 <<= cur_scale;
                divisor[0] = sdlDivisor.u.Lo;
                divisor[1] = sdlDivisor.u.Hi;
                divisor[2] = sdlTmp.u.Hi;

                // The remainder (currently 96 bits spread over 4 uint32_ts)
                // will be < divisor.
                //
                quo[2] = 0;
                quo[1] = 0;
                quo[0] = Div128By96(rem, divisor);

                for (;;)
                {
                    if ((rem[0] | rem[1] | rem[2]) == 0)
                    {
                        if (scale < 0)
                        {
                            cur_scale = std::min(9, -scale);
                            goto HaveScale96;
                        }
                        break;
                    }

                    // We need to unscale if and only if we have a non-zero remainder
                    unscale = true;

                    // Remainder is non-zero.  Scale up quotient and remainder by
                    // powers of 10 so we can compute more significant bits.
                    //
                    cur_scale = SearchScale(quo[2], quo[1], quo[0], scale);
                    if (cur_scale == 0)
                    {
                        // No more scaling to be done, but remainder is non-zero.
                        // Round quotient.
                        //
                        if (rem[2] >= 0x80000000)
                            goto RoundUp;

                        tmp = rem[0] > 0x80000000;
                        tmp1 = rem[1] > 0x80000000;
                        rem[0] <<= 1;
                        rem[1] = (rem[1] << 1) + tmp;
                        rem[2] = (rem[2] << 1) + tmp1;

                        if (rem[2] > divisor[2] || (rem[2] == divisor[2] && (rem[1] > divisor[1] || rem[1] == (divisor[1] && (rem[0] > divisor[0] || (rem[0] == divisor[0] && (quo[0] & 1)))))))
                            goto RoundUp;
                        break;
                    }

                    if (cur_scale < 0)
                    {
                        vm::Exception::RaiseOverflowException();
                        return;
                    }

                HaveScale96:
                    pwr = power10[cur_scale];
                    scale += cur_scale;

                    if (IncreaseScale(quo, pwr) != 0)
                    {
                        vm::Exception::RaiseOverflowException();
                        return;
                    }

                    rem[3] = IncreaseScale(rem, pwr);
                    tmp = Div128By96(rem, divisor);
                    if (!Add32To96(quo, tmp))
                    {
                        if (scale == 0)
                        {
                            vm::Exception::RaiseOverflowException();
                            return;
                        }

                        scale--;
                        OverflowUnscale(quo, (rem[0] != 0 || rem[1] != 0 || rem[2] != 0 || rem[3] != 0));
                        break;
                    }
                } // for (;;)
            }
        }

        // We need to unscale if and only if we have a non-zero remainder
        if (unscale)
        {
            // Try extracting any extra powers of 10 we may have
            // added.  We do this by trying to divide out 10^8, 10^4, 10^2, and 10^1.
            // If a division by one of these powers returns a zero remainder, then
            // we keep the quotient.  If the remainder is not zero, then we restore
            // the previous value.
            //
            // Since 10 = 2 * 5, there must be a factor of 2 for every power of 10
            // we can extract.  We use this as a quick test on whether to try a
            // given power.
            //
            while ((quo[0] & 0xFF) == 0 && scale >= 8)
            {
                quo_save[0] = quo[0];
                quo_save[1] = quo[1];
                quo_save[2] = quo[2];

                if (Div96By32(quo_save, 100000000) == 0)
                {
                    quo[0] = quo_save[0];
                    quo[1] = quo_save[1];
                    quo[2] = quo_save[2];
                    scale -= 8;
                }
                else
                    break;
            }

            if ((quo[0] & 0xF) == 0 && scale >= 4)
            {
                quo_save[0] = quo[0];
                quo_save[1] = quo[1];
                quo_save[2] = quo[2];

                if (Div96By32(quo_save, 10000) == 0)
                {
                    quo[0] = quo_save[0];
                    quo[1] = quo_save[1];
                    quo[2] = quo_save[2];
                    scale -= 4;
                }
            }

            if ((quo[0] & 3) == 0 && scale >= 2)
            {
                quo_save[0] = quo[0];
                quo_save[1] = quo[1];
                quo_save[2] = quo[2];

                if (Div96By32(quo_save, 100) == 0)
                {
                    quo[0] = quo_save[0];
                    quo[1] = quo_save[1];
                    quo[2] = quo_save[2];
                    scale -= 2;
                }
            }

            if ((quo[0] & 1) == 0 && scale >= 1)
            {
                quo_save[0] = quo[0];
                quo_save[1] = quo[1];
                quo_save[2] = quo[2];

                if (Div96By32(quo_save, 10) == 0)
                {
                    quo[0] = quo_save[0];
                    quo[1] = quo_save[1];
                    quo[2] = quo_save[2];
                    scale -= 1;
                }
            }
        }

        DECIMAL_SIGN(*left) = DECIMAL_SIGN(*left) ^ DECIMAL_SIGN(*right);
        DECIMAL_HI32(*left) = quo[2];
        DECIMAL_MID32(*left) = quo[1];
        DECIMAL_LO32(*left) = quo[0];
        DECIMAL_SCALE(*left) = (uint8_t)scale;
        left->reserved = 0;
    }

    void Decimal::FCallFloor(Il2CppDecimal* d)
    {
        Il2CppDecimal decRes;

        il2cpp_decimal_round_to_int(d, &decRes);

        // copy decRes into d
        COPYDEC(*d, decRes);
        d->reserved = 0;
        FC_GC_POLL();
    }

    void Decimal::FCallMultiply(Il2CppDecimal* d1, Il2CppDecimal* d2)
    {
        Il2CppDecimal decRes;

        Il2CppDecimalStatus status = il2cpp_decimal_multiply_result(d1, d2, &decRes);
        if (status != IL2CPP_DECIMAL_OK)
        {
            vm::Exception::RaiseOverflowException();
            return;
        }

        COPYDEC(*d1, decRes);
        d1->reserved = 0;

        FC_GC_POLL();
    }

    void Decimal::FCallRound(Il2CppDecimal* d, int32_t decimals)
    {
        Il2CppDecimal decRes;

        // GC is only triggered for throwing, no need to protect result
        if (decimals < 0 || decimals > 28)
        {
            vm::Exception::RaiseArgumentOutOfRangeException("d");
            return;
        }

        il2cpp_decimal_round_result(d, decimals, &decRes);

        // copy decRes into d
        COPYDEC(*d, decRes);
        d->reserved = 0;

        FC_GC_POLL();
    }

    void Decimal::FCallTruncate(Il2CppDecimal* d)
    {
        Il2CppDecimal decRes;

        il2cpp_decimal_fix(d, &decRes);

        // copy decRes into d
        COPYDEC(*d, decRes);
        d->reserved = 0;
        FC_GC_POLL();
    }
} /* namespace System */
} /* namespace mscorlib */
} /* namespace icalls */
} /* namespace il2cpp */
