/**
 * PROJECT:         ExectOS
 * COPYRIGHT:       See COPYING.md in the top level directory
 * FILE:            xtoskrnl/rtl/math.cc
 * DESCRIPTION:     Kernel math support
 * DEVELOPERS:      Rafal Kupiec <belliash@codingworkshop.eu.org>
 */

#include <xtos.hh>


/**
 * Converts the 32-bit signed value to a large integer.
 *
 * @param Value
 *        Supplies the value to convert.
 *
 * @return This routine returns the large integer representation of the given value.
 *
 * @since XT 1.0
 */
XTAPI
LARGE_INTEGER
RTL::Math::ConvertToLargeInteger32(IN LONG Value)
{
    LARGE_INTEGER LargeInt;

    /* Convert the value to a large integer and return it */
    LargeInt.QuadPart = Value;
    return LargeInt;
}

/**
 * Converts the 32-bit unsigned value to a large integer.
 *
 * @param Value
 *        Supplies the value to convert.
 *
 * @return This routine returns the large integer representation of the given value.
 *
 * @since XT 1.0
 */
XTAPI
LARGE_INTEGER
RTL::Math::ConvertToLargeIntegerUnsigned32(IN ULONG Value)
{
    LARGE_INTEGER LargeInt;

    /* Convert the value to a large integer and return it */
    LargeInt.QuadPart = Value;
    return LargeInt;
}

/**
 * Determines the number of leading zero bits in a 32-bit unsigned value.
 *
 * @param Value
 *        Supplies the value whose leading zero bits are to be counted.
 *
 * @return This routine returns the number of leading zero bits in the given value.
 *
 * @since XT 1.0
 */
XTAPI
INT
RTL::Math::CountLeadingZeroes32(IN ULONG Value)
{
    /* Return a number of leading zero bits */
    return __builtin_clzl(Value);
}

/**
 * Determines the number of leading zero bits in a 64-bit unsigned value.
 *
 * @param Value
 *        Supplies the value whose leading zero bits are to be counted.
 *
 * @return This routine returns the number of leading zero bits in the given value.
 *
 * @since XT 1.0
 */
XTAPI
INT
RTL::Math::CountLeadingZeroes64(IN ULONGLONG Value)
{
    /* Return a number of leading zero bits */
    return __builtin_clzll(Value);
}

/**
 * Determines the number of trailing zero bits in a 32-bit unsigned value.
 *
 * @param Value
 *        Supplies the value whose trailing zero bits are to be counted.
 *
 * @return This routine returns the number of trailing zero bits in the given value.
 *
 * @since XT 1.0
 */
XTAPI
INT
RTL::Math::CountTrailingZeroes32(IN ULONG Value)
{
    /* Return a number of trailing zero bits */
    return __builtin_ctzl(Value);
}

/**
 * Determines the number of trailing zero bits in a 64-bit unsigned value.
 *
 * @param Value
 *        Supplies the value whose trailing zero bits are to be counted.
 *
 * @return This routine returns the number of trailing zero bits in the given value.
 *
 * @since XT 1.0
 */
XTAPI
INT
RTL::Math::CountTrailingZeroes64(IN ULONGLONG Value)
{
    /* Return a number of trailing zero bits */
    return __builtin_ctzll(Value);
}

/**
 * Performs a 32-bit divide operation on signed integer numbers.
 *
 * @param Dividend
 *        Supplies a number that is going to be divided.
 *
 * @param Divisor
 *        Supplies a number by which the dividend is divided.
 *
 * @param Remainder
 *        Supplies a pointer that receives the divide remainder.
 *
 * @return This routine returns the quotient.
 *
 * @since XT 1.0
 */
XTAPI
LONGLONG
RTL::Math::Divide32(IN LONG Dividend,
                    IN LONG Divisor,
                    OUT PLONG Remainder)
{
    LONG Quotient;

    /* Calculate the quotient */
    Quotient = Dividend / Divisor;

    /* Make sure a pointer to remainder provided */
    if(Remainder)
    {
        /* Calculate remainder */
        *Remainder = Dividend - (Quotient * Divisor);
    }

    /* Return the quotient */
    return Quotient;
}

/**
 * Performs a 64-bit divide operation on signed integer numbers.
 *
 * @param Dividend
 *        Supplies a number that is going to be divided.
 *
 * @param Divisor
 *        Supplies a number by which the dividend is divided.
 *
 * @param Remainder
 *        Supplies a pointer that receives the divide remainder.
 *
 * @return This routine returns the quotient.
 *
 * @since XT 1.0
 */
XTAPI
LONGLONG
RTL::Math::Divide64(IN LONGLONG Dividend,
                    IN LONGLONG Divisor,
                    OUT PLONGLONG Remainder)
{
    LONGLONG DividendSign, DivisorSign, Quotient, UDividend, UDivisor;

    /* Remove the sign bit from dividend and divisor if present */
    DividendSign = Dividend >> ((sizeof(LONGLONG) * BITS_PER_BYTE) - 1);
    DivisorSign = Divisor >> ((sizeof(LONGLONG) * BITS_PER_BYTE) - 1);
    UDividend = (Dividend ^ DividendSign) - DividendSign;
    UDivisor = (Divisor ^ DivisorSign) - DivisorSign;

    /* Calculate the quotient */
    DividendSign ^= DivisorSign;
    Quotient = (DivideUnsigned64(UDividend, UDivisor, NULLPTR) ^ DividendSign) - DividendSign;

    /* Make sure a pointer to remainder provided */
    if(Remainder)
    {
        /* Calculate remainder */
        *Remainder = Dividend - (Quotient * Divisor);
    }

    /* Return the quotient */
    return Quotient;
}

/**
 * Performs a 32-bit divide operation on unsigned integer numbers.
 *
 * @param Dividend
 *        Supplies an unsigned number that is going to be divided.
 *
 * @param Divisor
 *        Supplies an unsigned number by which the dividend is divided.
 *
 * @param Remainder
 *        Supplies a pointer that receives the unsigned divide remainder.
 *
 * @return This routine returns the quotient.
 *
 * @since XT 1.0
 */
XTAPI
ULONGLONG
RTL::Math::DivideUnsigned32(IN ULONG Dividend,
                            IN ULONG Divisor,
                            OUT PULONG Remainder)
{
    /* Make sure a pointer to remainder provided */
    if(Remainder)
    {
        /* Calculate remainder */
        *Remainder = Dividend % Divisor;
    }

    /* Return the quotient */
    return Dividend / Divisor;
}

/**
 * Performs a 64-bit divide operation on unsigned integer numbers.
 *
 * @param Dividend
 *        Supplies an unsigned number that is going to be divided.
 *
 * @param Divisor
 *        Supplies an unsigned number by which the dividend is divided.
 *
 * @param Remainder
 *        Supplies a pointer that receives the unsigned divide remainder.
 *
 * @return This routine returns the quotient.
 *
 * @since XT 1.0
 */
XTAPI
ULONGLONG
RTL::Math::DivideUnsigned64(IN ULONGLONG Dividend,
                            IN ULONGLONG Divisor,
                            OUT PULONGLONG Remainder)
{
    ULARGE_INTEGER DividendParts, DivisorParts, QuotientParts, RemainderParts;
    LONGLONG Difference;
    ULONGLONG Shift;
    ULONG Carry;

    /* Make sure divisor is not 0 */
    if(Divisor == 0)
    {
        /* Cannot divide by 0 */
        return 0;
    }

    /* Assign dividend and divisor to large integer representations */
    DividendParts.QuadPart = Dividend;
    DivisorParts.QuadPart = Divisor;

    /* Check if dividend is 32-bit value */
    if(DividendParts.u.HighPart == 0)
    {
        /* Check if devisor is 32-bit value */
        if(DivisorParts.u.HighPart == 0)
        {
            /* 32-bit divide operation, check if remainder provided */
            if(Remainder != NULLPTR)
            {
                /* Calculate remainder */
                *Remainder = DividendParts.u.LowPart % DivisorParts.u.LowPart;
            }

            /* Return the quotient */
            return DividendParts.u.LowPart / DivisorParts.u.LowPart;
        }

        /* 32-bit value divided by a 64-bit value, check if remainder provided */
        if(Remainder != NULLPTR)
        {
            /* Calculate remainder */
            *Remainder = DividendParts.u.LowPart;
        }

        /* Return zero as quotient */
        return 0;
    }

    /* Dividend is a 64-bit value, check if divisor has a low part */
    if(DivisorParts.u.LowPart != 0)
    {
        /* Divisor has a non-zero low part, check if divisor has a high part */
        if(DivisorParts.u.HighPart != 0)
        {
            /* Divisor is 64-bit value, calculate the shift count */
            Shift = CountLeadingZeroes32(DivisorParts.u.HighPart) - CountLeadingZeroes32(DividendParts.u.HighPart);

            /* Check if shift count exceeds 32-bits */
            if(Shift > ((sizeof(ULONG) * BITS_PER_BYTE) - 1))
            {
                /* Check if remainder provided */
                if(Remainder != NULLPTR)
                {
                    /* Calculate remainder */
                    *Remainder = DividendParts.QuadPart;
                }

                return 0;
            }

            /* Increase shift and clear quotient low part */
            Shift++;
            QuotientParts.u.LowPart = 0;

            /* Check if shift is 32-bits */
            if(Shift == (sizeof(ULONG) * BITS_PER_BYTE)) {
                /* Get the quotient high part and remainder */
                QuotientParts.u.HighPart = DividendParts.u.LowPart;
                RemainderParts.u.LowPart = DividendParts.u.HighPart;
                RemainderParts.u.HighPart = 0;
            } else {
                /* Get the quotient high part and remainder */
                QuotientParts.u.HighPart = DividendParts.u.LowPart << ((sizeof(ULONG) * BITS_PER_BYTE) - Shift);
                RemainderParts.u.LowPart = (DividendParts.u.HighPart << ((sizeof(ULONG) * BITS_PER_BYTE) - Shift)) |
                                           (DividendParts.u.LowPart >> Shift);
                RemainderParts.u.HighPart = DividendParts.u.HighPart >> Shift;
            }
        }
        else
        {
            /* Divisor is 32-bit value, calculate the shift count */
            Shift = (sizeof(ULONG) * BITS_PER_BYTE) + 1 +
                    CountLeadingZeroes32(DivisorParts.u.LowPart) -
                    CountLeadingZeroes32(DividendParts.u.HighPart);

            /* Check if shift is 32-bit */
            if(Shift == (sizeof(ULONG) * BITS_PER_BYTE))
            {
                /* Get the quotient and remainder */
                QuotientParts.u.LowPart = 0;
                QuotientParts.u.HighPart = DividendParts.u.LowPart;
                RemainderParts.u.LowPart = DividendParts.u.HighPart;
                RemainderParts.u.HighPart = 0;
            }
            else if(Shift < (sizeof(ULONG) * BITS_PER_BYTE))
            {
                /* Shift is smaller, get the quotient and remainder */
                QuotientParts.u.LowPart = 0;
                QuotientParts.u.HighPart = DividendParts.u.LowPart << ((sizeof(ULONG) * BITS_PER_BYTE) - Shift);
                RemainderParts.u.LowPart = (DividendParts.u.HighPart <<
                                           ((sizeof(ULONG) * BITS_PER_BYTE) - Shift)) |
                                           (DividendParts.u.LowPart >> Shift);
                RemainderParts.u.HighPart = DividendParts.u.HighPart >> Shift;
            }
            else
            {
                /* Shift is larger, get the quotient and remainder */
                QuotientParts.u.LowPart = DividendParts.u.LowPart << ((sizeof(ULONGLONG) * BITS_PER_BYTE) - Shift);
                QuotientParts.u.HighPart = (DividendParts.u.HighPart << ((sizeof(ULONGLONG) * BITS_PER_BYTE) - Shift)) |
                                           (DividendParts.u.LowPart >> (Shift - (sizeof(ULONG) * BITS_PER_BYTE)));
                RemainderParts.u.LowPart = DividendParts.u.HighPart >> (Shift - (sizeof(ULONG) * BITS_PER_BYTE));
                RemainderParts.u.HighPart = 0;
            }
        }
    }
    else
    {
        /* Divisor is 64-bit value, check if dividend has low part set */
        if(DividendParts.u.LowPart == 0)
        {
            /* Check if remainder provided */
            if(Remainder != NULLPTR)
            {
                /* Calculate the remainder */
                RemainderParts.u.HighPart = DividendParts.u.HighPart % DivisorParts.u.HighPart;
                RemainderParts.u.LowPart = 0;
                *Remainder = RemainderParts.QuadPart;
            }

            /* Return the quotient */
            return DividendParts.u.HighPart / DivisorParts.u.HighPart;
        }

        /* Calculate the shift count */
        Shift = CountLeadingZeroes32(DivisorParts.u.HighPart) - CountLeadingZeroes32(DividendParts.u.HighPart);

        /* Check if shift exceeds 32-bits */
        if(Shift > ((sizeof(ULONG) * BITS_PER_BYTE) - 2))
        {
            /* Check if remainder provided */
            if(Remainder != NULLPTR)
            {
                /* Calculate the remainder */
                *Remainder = DividendParts.QuadPart;
            }

            /* Return 0 */
            return 0;
        }

        /* Increase shift and clear quotient low part */
        Shift++;
        QuotientParts.u.LowPart = 0;

        /* Get the quotient high part and remainder */
        QuotientParts.u.HighPart = DividendParts.u.LowPart << ((sizeof(ULONG) * BITS_PER_BYTE) - Shift);
        RemainderParts.u.HighPart = DividendParts.u.HighPart >> Shift;
        RemainderParts.u.LowPart = (DividendParts.u.HighPart <<
                                   ((sizeof(ULONG) * BITS_PER_BYTE) - Shift)) |
                                   (DividendParts.u.LowPart >> Shift);
    }

    /* Perform the division until shift is zero */
    Carry = 0;
    while(Shift > 0)
    {
        /* Shift the remainder and the quotient */
        RemainderParts.u.HighPart = (RemainderParts.u.HighPart << 1) |
                                    (RemainderParts.u.LowPart >>
                                    ((sizeof(ULONG) * BITS_PER_BYTE) - 1));
        RemainderParts.u.LowPart = (RemainderParts.u.LowPart << 1) |
                                   (QuotientParts.u.HighPart >>
                                   ((sizeof(ULONG) * BITS_PER_BYTE) - 1));
        QuotientParts.u.HighPart = (QuotientParts.u.HighPart << 1) |
                                   (QuotientParts.u.LowPart >>
                                   ((sizeof(ULONG) * BITS_PER_BYTE) - 1));
        QuotientParts.u.LowPart = (QuotientParts.u.LowPart << 1) | Carry;

        /* Set the carry and subtract the divisor */
        Difference = (LONGLONG)(DivisorParts.QuadPart - RemainderParts.QuadPart - 1) >>
                     ((sizeof(ULONGLONG) * BITS_PER_BYTE) - 1);
        Carry = Difference & 0x1;
        RemainderParts.QuadPart -= DivisorParts.QuadPart & Difference;
        Shift -= 1;
    }

    /* Add the carry to the quotient */
    QuotientParts.QuadPart = (QuotientParts.QuadPart << 1) | Carry;

    /* Check if remainder provided */
    if(Remainder != NULLPTR)
    {
        /* Calculate the remainder */
        *Remainder = RemainderParts.QuadPart;
    }

    /* Return the quotient */
    return QuotientParts.QuadPart;
}

/**
 * Divides a signed large integer by a 32-bit divisor.
 *
 * @param Dividend
 *        Supplies a large integer to be divided.
 *
 * @param Divisor
 *        Supplies a 32-bit divisor.
 *
 * @param Remainder
 *        Supplies a pointer that receives the divide remainder.
 *
 * @return This routine returns the quotient.
 *
 * @since XT 1.0
 */
XTAPI
LARGE_INTEGER
RTL::Math::DivideLargeInteger(IN LARGE_INTEGER Dividend,
                              IN ULONG Divisor,
                              OUT PULONG Remainder)
{
    LONGLONG DividendSign, UDividend;
    LARGE_INTEGER LargeInt;

    /* Remove the sign bit from dividend if present */
    DividendSign = Dividend.QuadPart >> ((sizeof(LONGLONG) * BITS_PER_BYTE) - 1);
    UDividend = (Dividend.QuadPart ^ DividendSign) - DividendSign;

    /* Calculate the quotient */
    LargeInt.QuadPart = (DivideUnsigned64(UDividend, Divisor, NULLPTR) ^ DividendSign) - DividendSign;

    /* Make sure a pointer to remainder provided */
    if(Remainder)
    {
        /* Calculate remainder */
        *Remainder = Dividend.QuadPart - (LargeInt.QuadPart * Divisor);
    }

    /* Return the quotient */
    return LargeInt;
}

/**
 * Gets the base exponent of a given floating point value.
 *
 * @param Value
 *        Supplies the floating point value to get the base exponent of.
 *
 * @param PowerOfTen
 *        Supplies a pointer that receives the power of ten associated with the base exponent.
 *
 * @return This routine returns the base exponent value.
 *
 * @since XT 1.0
 */
XTAPI
LONG
RTL::Math::GetBaseExponent(IN DOUBLE Value,
                           OUT PDOUBLE PowerOfTen)
{
    LONG BaseExponent, CurrentExponent, Exponent;
    ULONG ExponentShift, ExponentMask;
    LARGE_DOUBLE Parts;
    DOUBLE Power;

    /* Calculate the exponent mask and shift */
    ExponentMask = DOUBLE_EXPONENT_MASK >> (sizeof(ULONG) * BITS_PER_BYTE);
    ExponentShift = DOUBLE_EXPONENT_SHIFT - (sizeof(ULONG) * BITS_PER_BYTE);

    /* Check if value is zero */
    if(Value == 0.0)
    {
        /* Return the power of ten and the exponent */
        *PowerOfTen = 1.0;
        return 0;
    }

    /* Get the parts of the value and calculate the exponent by multiplying by log10(2) */
    Parts.DoublePart = Value;
    BaseExponent = ((Parts.u.HighPart & ExponentMask) >> ExponentShift) - DOUBLE_EXPONENT_BIAS;
    Exponent = (LONG)((DOUBLE)BaseExponent * 0.30102999566) + 1;

    /* Set the initial values and calculate the exponent */
    CurrentExponent = 0;
    Power = 1.0;
    if(Exponent > 0)
    {
        /* Calculate the exponent */
        while(CurrentExponent + 10 <= Exponent)
        {
            Power *= 0.0000000001;
            CurrentExponent += 10;
        }

        while(CurrentExponent + 1 <= Exponent)
        {
            Power *= 0.1;
            CurrentExponent += 1;
        }
    }
    else
    {
        /* Calculate the exponent */
        while(CurrentExponent - 10 >= Exponent)
        {
            Power *= 10000000000;
            CurrentExponent -= 10;
        }

        while(CurrentExponent - 1 >= Exponent)
        {
            Power *= 10.0;
            CurrentExponent -= 1;
        }
    }

    /* Normalize input value */
    Value *= Power;

    /* Remove all leading zeros, if any */
    while((Value != 0.0) && ((LONG)Value == 0))
    {
        Value *= 10.0;
        Exponent -= 1;
        Power *= 10.0;
    }

    /* Return the power of ten and the exponent */
    *PowerOfTen = Power;
    return Exponent;
}

/**
 * Determines whether a floating-point number is infinite.
 *
 * @param Value
 *        Supplies the floating-point value to test.
 *
 * @return This routine returns TRUE if the argument is infinite or a NaN, or FALSE otherwise.
 *
 * @since XT 1.0
 */
XTAPI
BOOLEAN
RTL::Math::InfiniteDouble(IN DOUBLE Value)
{
    /* DOUBLE argument in IEEE 754 standard format */
    union
    {
        PDOUBLE Double;
        struct
        {
            UINT MantissaLow:32;
            UINT MantissaHigh:20;
            UINT Exponent:11;
            UINT Sign:1;
        } *DoubleS;
    } Var;

    /* Convert input double value to IEEE 754 format */
    Var.Double = &Value;

    /* Return TRUE if it is infinite, or FALSE otherwise */
    return ((Var.DoubleS->Exponent & 0x7FF) == 0x7FF);
}

/**
 * Multiplies a signed large integer by a signed integer.
 *
 * @param Multiplicand
 *        Supplies a large integer to be multiplied.
 *
 * @param Multiplier
 *        Supplies an integer by which the large integer is multiplied.
 *
 * @return This routine returns the result of the multiplication.
 *
 * @since XT 1.0
 */
XTAPI
LARGE_INTEGER
RTL::Math::MultiplyLargeInteger(IN LARGE_INTEGER Multiplicand,
                                IN LONG Multiplier)
{
    LARGE_INTEGER LargeInt;

    /* Perform multiplication and return the result */
    LargeInt.QuadPart = (LONGLONG) Multiplicand.QuadPart * Multiplier;
    return LargeInt;
}

/**
 * Determines whether a floating-point number is a NaN ("Not a Number").
 *
 * @param Value
 *        Supplies the floating-point value to test.
 *
 * @return This routine returns TRUE if the argument is a NaN, or FALSE otherwise.
 *
 * @since XT 1.0
 */
XTAPI
BOOLEAN
RTL::Math::NanDouble(IN DOUBLE Value)
{
    /* DOUBLE argument in IEEE 754 standard format */
    union
    {
        PDOUBLE Double;
        struct
        {
            UINT MantissaLow:32;
            UINT MantissaHigh:20;
            UINT Exponent:11;
            UINT Sign:1;
        } *DoubleS;
    } Var;

    /* Convert input double value to IEEE 754 format */
    Var.Double = &Value;

    /* Return TRUE if it is NaN, or FALSE otherwise */
    return (Var.DoubleS->Exponent == 0x7FF && (Var.DoubleS->MantissaHigh != 0 || Var.DoubleS->MantissaLow != 0));
}
