    // Copyright (C) 2003  Davis E. King (davis@dlib.net)
    // License: Boost Software License   See LICENSE.txt for the full license.
    #ifndef DLIB_BIGINT_KERNEL_2_CPp_
    #define DLIB_BIGINT_KERNEL_2_CPp_
    #include "bigint_kernel_2.h"

    #include <iostream>
    #include <cmath>

    namespace dlib
    {

    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------
        // member/friend function definitions
    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------

        bigint_kernel_2::
        bigint_kernel_2 (
        ) :
            slack(25),
            data(new data_record(slack))
        {}

    // ----------------------------------------------------------------------------------------

        bigint_kernel_2::
        bigint_kernel_2 (
            uint32 value
        ) :
            slack(25),
            data(new data_record(slack))
        {
            *(data->number) = static_cast<uint16>(value&0xFFFF);
            *(data->number+1) = static_cast<uint16>((value>>16)&0xFFFF);
            if (*(data->number+1) != 0)
                data->digits_used = 2;
        }

    // ----------------------------------------------------------------------------------------

        bigint_kernel_2::
        bigint_kernel_2 (
            const bigint_kernel_2& item
        ) :
            slack(25),
            data(item.data)
        {
            data->references += 1;
        }

    // ----------------------------------------------------------------------------------------

        bigint_kernel_2::
        ~bigint_kernel_2 (
        )
        {
            if (data->references == 1)
            {
                delete data;
            }
            else
            {
                data->references -= 1;
            }
        }

    // ----------------------------------------------------------------------------------------

        const bigint_kernel_2 bigint_kernel_2::
        operator+ (
            const bigint_kernel_2& rhs
        ) const
        {
            data_record* temp = new data_record (
                        std::max(rhs.data->digits_used,data->digits_used) + slack
                        );
            long_add(data,rhs.data,temp);
            return bigint_kernel_2(temp,0);
        }

    // ----------------------------------------------------------------------------------------

        bigint_kernel_2& bigint_kernel_2::
        operator+= (
            const bigint_kernel_2& rhs
        )
        {
            // if there are other references to our data
            if (data->references != 1)
            {
                data_record* temp = new data_record(std::max(data->digits_used,rhs.data->digits_used)+slack);
                data->references -= 1;
                long_add(data,rhs.data,temp);
                data = temp;
            }
            // if data is not big enough for the result
            else if (data->size <= std::max(data->digits_used,rhs.data->digits_used))
            {
                data_record* temp = new data_record(std::max(data->digits_used,rhs.data->digits_used)+slack);
                long_add(data,rhs.data,temp);
                delete data;
                data = temp;
            }
            // there is enough size and no references
            else
            {
                long_add(data,rhs.data,data);
            }
            return *this;
        }

    // ----------------------------------------------------------------------------------------

        const bigint_kernel_2 bigint_kernel_2::
        operator- (
            const bigint_kernel_2& rhs
        ) const
        {
            data_record* temp = new data_record (
                        data->digits_used + slack
                        );
            long_sub(data,rhs.data,temp);
            return bigint_kernel_2(temp,0);
        }

    // ----------------------------------------------------------------------------------------

        bigint_kernel_2& bigint_kernel_2::
        operator-= (
            const bigint_kernel_2& rhs
        )
        {
            // if there are other references to this data
            if (data->references != 1)
            {
                data_record* temp = new data_record(data->digits_used+slack);
                data->references -= 1;
                long_sub(data,rhs.data,temp);
                data = temp;
            }
            else
            {
                long_sub(data,rhs.data,data);
            }
            return *this;
        }

    // ----------------------------------------------------------------------------------------

        const bigint_kernel_2 bigint_kernel_2::
        operator* (
            const bigint_kernel_2& rhs
        ) const
        {
            data_record* temp = new data_record (
                        data->digits_used + rhs.data->digits_used + slack
                        );
            long_mul(data,rhs.data,temp);
            return bigint_kernel_2(temp,0);
        }

    // ----------------------------------------------------------------------------------------

        bigint_kernel_2& bigint_kernel_2::
        operator*= (
            const bigint_kernel_2& rhs
        )
        {
            // create a data_record to store the result of the multiplication in
            data_record* temp = new data_record(rhs.data->digits_used+data->digits_used+slack);
            long_mul(data,rhs.data,temp);

            // if there are other references to data
            if (data->references != 1)
            {
                data->references -= 1;
            }
            else
            {
                delete data;
            }
            data = temp;
            return *this;
        }

    // ----------------------------------------------------------------------------------------

        const bigint_kernel_2 bigint_kernel_2::
        operator/ (
            const bigint_kernel_2& rhs
        ) const
        {
            data_record* temp = new data_record(data->digits_used+slack);
            data_record* remainder;
            try {
                remainder = new data_record(data->digits_used+slack);
            } catch (...) { delete temp; throw; }

            long_div(data,rhs.data,temp,remainder);
            delete remainder;

            return bigint_kernel_2(temp,0);
        }

    // ----------------------------------------------------------------------------------------

        bigint_kernel_2& bigint_kernel_2::
        operator/= (
            const bigint_kernel_2& rhs
        )
        {

            data_record* temp = new data_record(data->digits_used+slack);
            data_record* remainder;
            try {
                remainder = new data_record(data->digits_used+slack);
            } catch (...) { delete temp; throw; }

            long_div(data,rhs.data,temp,remainder);

            // check if there are other references to data
            if (data->references != 1)
            {
                data->references -= 1;
            }
            // if there are no references to data then it must be deleted
            else
            {
                delete data;
            }
            data = temp;
            delete remainder;

            return *this;
        }

    // ----------------------------------------------------------------------------------------

        const bigint_kernel_2 bigint_kernel_2::
        operator% (
            const bigint_kernel_2& rhs
        ) const
        {
            data_record* temp = new data_record(data->digits_used+slack);
            data_record* remainder;
            try {
                remainder = new data_record(data->digits_used+slack);
            } catch (...) { delete temp; throw; }

            long_div(data,rhs.data,temp,remainder);
            delete temp;
            return bigint_kernel_2(remainder,0);
        }

    // ----------------------------------------------------------------------------------------

        bigint_kernel_2& bigint_kernel_2::
        operator%= (
            const bigint_kernel_2& rhs
        )
        {
            data_record* temp = new data_record(data->digits_used+slack);
            data_record* remainder;
            try {
                remainder = new data_record(data->digits_used+slack);
            } catch (...) { delete temp; throw; }

            long_div(data,rhs.data,temp,remainder);

            // check if there are other references to data
            if (data->references != 1)
            {
                data->references -= 1;
            }
            // if there are no references to data then it must be deleted
            else
            {
                delete data;
            }
            data = remainder;
            delete temp;
            return *this;
        }

    // ----------------------------------------------------------------------------------------

        bool bigint_kernel_2::
        operator < (
            const bigint_kernel_2& rhs
        ) const
        {
            return is_less_than(data,rhs.data);
        }

    // ----------------------------------------------------------------------------------------

        bool bigint_kernel_2::
        operator == (
            const bigint_kernel_2& rhs
        ) const
        {
            return is_equal_to(data,rhs.data);
        }

    // ----------------------------------------------------------------------------------------

        bigint_kernel_2& bigint_kernel_2::
        operator= (
            const bigint_kernel_2& rhs
        )
        {
            if (this == &rhs)
                return *this;

            // if we have the only reference to our data then delete it
            if (data->references == 1)
            {
                delete data;
                data = rhs.data;
                data->references += 1;
            }
            else
            {
                data->references -= 1;
                data = rhs.data;
                data->references += 1;
            }

            return *this;
        }

    // ----------------------------------------------------------------------------------------

        std::ostream& operator<< (
            std::ostream& out_,
            const bigint_kernel_2& rhs
        )
        {
            std::ostream out(out_.rdbuf());

            typedef bigint_kernel_2 bigint;

            bigint::data_record* temp = new bigint::data_record(*rhs.data,0);

            // get a char array big enough to hold the number in ascii format
            char* str;
            try {
                str = new char[(rhs.data->digits_used)*5+10];
            } catch (...) { delete temp; throw; }

            char* str_start = str;
            str += (rhs.data->digits_used)*5+9;
            *str = 0; --str;

            uint16 remainder;
            rhs.short_div(temp,10000,temp,remainder);

            // pull the digits out of remainder
            char a = remainder % 10 + '0';
            remainder /= 10;
            char b = remainder % 10 + '0';
            remainder /= 10;
            char c = remainder % 10 + '0';
            remainder /= 10;
            char d = remainder % 10 + '0';
            remainder /= 10;

            *str = a; --str;
            *str = b; --str;
            *str = c; --str;
            *str = d; --str;

            // keep looping until temp represents zero
            while (temp->digits_used != 1 || *(temp->number) != 0)
            {
                rhs.short_div(temp,10000,temp,remainder);

                // pull the digits out of remainder
                char a = remainder % 10 + '0';
                remainder /= 10;
                char b = remainder % 10 + '0';
                remainder /= 10;
                char c = remainder % 10 + '0';
                remainder /= 10;
                char d = remainder % 10 + '0';
                remainder /= 10;

                *str = a; --str;
                *str = b; --str;
                *str = c; --str;
                *str = d; --str;
            }

            // throw away and extra leading zeros
            ++str;
            if (*str == '0')
                ++str;
            if (*str == '0')
                ++str;
            if (*str == '0')
                ++str;

            out << str;
            delete [] str_start;
            delete temp;
            return out_;

        }

    // ----------------------------------------------------------------------------------------

        std::istream& operator>> (
            std::istream& in_,
            bigint_kernel_2& rhs
        )
        {
            std::istream in(in_.rdbuf());

            // ignore any leading whitespaces
            while (in.peek() == ' ' || in.peek() == '\t' || in.peek() == '\n')
            {
                in.get();
            }

            // if the first digit is not an integer then this is an error
            if ( !(in.peek() >= '0' && in.peek() <= '9'))
            {
                in_.clear(std::ios::failbit);
                return in_;
            }

            int num_read;
            bigint_kernel_2 temp;
            do
            {

                // try to get 4 chars from in
                num_read = 1;
                char a = 0;
                char b = 0;
                char c = 0;
                char d = 0;

                if (in.peek() >= '0' && in.peek() <= '9')
                {
                    num_read *= 10;
                    a = in.get();
                }
                if (in.peek() >= '0' && in.peek() <= '9')
                {
                    num_read *= 10;
                    b = in.get();
                }
                if (in.peek() >= '0' && in.peek() <= '9')
                {
                    num_read *= 10;
                    c = in.get();
                }
                if (in.peek() >= '0' && in.peek() <= '9')
                {
                    num_read *= 10;
                    d = in.get();
                }

                // merge the for digits into an uint16
                uint16 num = 0;
                if (a != 0)
                {
                    num = a - '0';
                }
                if (b != 0)
                {
                    num *= 10;
                    num += b - '0';
                }
                if (c != 0)
                {
                    num *= 10;
                    num += c - '0';
                }
                if (d != 0)
                {
                    num *= 10;
                    num += d - '0';
                }

                if (num_read != 1)
                {
                    // shift the digits in temp left by the number of new digits we just read
                    temp *= num_read;
                    // add in new digits
                    temp += num;
                }

            } while (num_read == 10000);

            rhs = temp;
            return in_;
        }

    // ----------------------------------------------------------------------------------------

        const bigint_kernel_2 operator+ (
            uint16 lhs,
            const bigint_kernel_2& rhs
        )
        {
            typedef bigint_kernel_2 bigint;
            bigint::data_record* temp = new bigint::data_record
                    (rhs.data->digits_used+rhs.slack);

            rhs.short_add(rhs.data,lhs,temp);
            return bigint_kernel_2(temp,0);
        }

    // ----------------------------------------------------------------------------------------

        const bigint_kernel_2 operator+ (
            const bigint_kernel_2& lhs,
            uint16 rhs
        )
        {
            typedef bigint_kernel_2 bigint;
            bigint::data_record* temp = new bigint::data_record
                    (lhs.data->digits_used+lhs.slack);

            lhs.short_add(lhs.data,rhs,temp);
            return bigint_kernel_2(temp,0);
        }

    // ----------------------------------------------------------------------------------------

        bigint_kernel_2& bigint_kernel_2::
        operator+= (
            uint16 rhs
        )
        {
            // if there are other references to this data
            if (data->references != 1)
            {
                data_record* temp = new data_record(data->digits_used+slack);
                data->references -= 1;
                short_add(data,rhs,temp);
                data = temp;
            }
            // or if we need to enlarge data then do so
            else if (data->digits_used == data->size)
            {
                data_record* temp = new data_record(data->digits_used+slack);
                short_add(data,rhs,temp);
                delete data;
                data = temp;
            }
            // or if there is plenty of space and no references
            else
            {
                short_add(data,rhs,data);
            }
            return *this;
        }

    // ----------------------------------------------------------------------------------------

        const bigint_kernel_2 operator- (
            uint16 lhs,
            const bigint_kernel_2& rhs
        )
        {
            typedef bigint_kernel_2 bigint;
            bigint::data_record* temp = new bigint::data_record(rhs.slack);

            *(temp->number) = lhs - *(rhs.data->number);

            return bigint_kernel_2(temp,0);
        }

    // ----------------------------------------------------------------------------------------

        const bigint_kernel_2 operator- (
            const bigint_kernel_2& lhs,
            uint16 rhs
        )
        {
            typedef bigint_kernel_2 bigint;
            bigint::data_record* temp = new bigint::data_record
                    (lhs.data->digits_used+lhs.slack);

            lhs.short_sub(lhs.data,rhs,temp);
            return bigint_kernel_2(temp,0);
        }

    // ----------------------------------------------------------------------------------------

        bigint_kernel_2& bigint_kernel_2::
        operator-= (
            uint16 rhs
        )
        {
            // if there are other references to this data
            if (data->references != 1)
            {
                data_record* temp = new data_record(data->digits_used+slack);
                data->references -= 1;
                short_sub(data,rhs,temp);
                data = temp;
            }
            else
            {
                short_sub(data,rhs,data);
            }
            return *this;
        }

    // ----------------------------------------------------------------------------------------

        const bigint_kernel_2 operator* (
            uint16 lhs,
            const bigint_kernel_2& rhs
        )
        {
            typedef bigint_kernel_2 bigint;
            bigint::data_record* temp = new bigint::data_record
                    (rhs.data->digits_used+rhs.slack);

            rhs.short_mul(rhs.data,lhs,temp);
            return bigint_kernel_2(temp,0);
        }

    // ----------------------------------------------------------------------------------------

        const bigint_kernel_2 operator* (
            const bigint_kernel_2& lhs,
            uint16 rhs
        )
        {
            typedef bigint_kernel_2 bigint;
            bigint::data_record* temp = new bigint::data_record
                    (lhs.data->digits_used+lhs.slack);

            lhs.short_mul(lhs.data,rhs,temp);
            return bigint_kernel_2(temp,0);
        }

    // ----------------------------------------------------------------------------------------

        bigint_kernel_2& bigint_kernel_2::
        operator*= (
            uint16 rhs
        )
        {
            // if there are other references to this data
            if (data->references != 1)
            {
                data_record* temp = new data_record(data->digits_used+slack);
                data->references -= 1;
                short_mul(data,rhs,temp);
                data = temp;
            }
            // or if we need to enlarge data
            else if (data->digits_used == data->size)
            {
                data_record* temp = new data_record(data->digits_used+slack);
                short_mul(data,rhs,temp);
                delete data;
                data = temp;
            }
            else
            {
                short_mul(data,rhs,data);
            }
            return *this;
        }

    // ----------------------------------------------------------------------------------------

        const bigint_kernel_2 operator/ (
            uint16 lhs,
            const bigint_kernel_2& rhs
        )
        {
            typedef bigint_kernel_2 bigint;
            bigint::data_record* temp = new bigint::data_record(rhs.slack);

            // if rhs might not be bigger than lhs
            if (rhs.data->digits_used == 1)
            {
                *(temp->number) = lhs/ *(rhs.data->number);
            }

            return bigint_kernel_2(temp,0);
        }

    // ----------------------------------------------------------------------------------------

        const bigint_kernel_2 operator/ (
            const bigint_kernel_2& lhs,
            uint16 rhs
        )
        {
            typedef bigint_kernel_2 bigint;
            bigint::data_record* temp = new bigint::data_record
                    (lhs.data->digits_used+lhs.slack);

            uint16 remainder;
            lhs.short_div(lhs.data,rhs,temp,remainder);
            return bigint_kernel_2(temp,0);
        }

    // ----------------------------------------------------------------------------------------

        bigint_kernel_2& bigint_kernel_2::
        operator/= (
            uint16 rhs
        )
        {
            uint16 remainder;
            // if there are other references to this data
            if (data->references != 1)
            {
                data_record* temp = new data_record(data->digits_used+slack);
                data->references -= 1;
                short_div(data,rhs,temp,remainder);
                data = temp;
            }
            else
            {
                short_div(data,rhs,data,remainder);
            }
            return *this;
        }

    // ----------------------------------------------------------------------------------------

        const bigint_kernel_2 operator% (
            uint16 lhs,
            const bigint_kernel_2& rhs
        )
        {
            typedef bigint_kernel_2 bigint;
            // temp is zero by default
            bigint::data_record* temp = new bigint::data_record(rhs.slack);

            if (rhs.data->digits_used == 1)
            {
                // if rhs is just an uint16 inside then perform the modulus
                *(temp->number) = lhs % *(rhs.data->number);
            }
            else
            {
                // if rhs is bigger than lhs then the answer is lhs
                *(temp->number) = lhs;
            }

            return bigint_kernel_2(temp,0);
        }

    // ----------------------------------------------------------------------------------------

        const bigint_kernel_2 operator% (
            const bigint_kernel_2& lhs,
            uint16 rhs
        )
        {
            typedef bigint_kernel_2 bigint;
            bigint::data_record* temp = new bigint::data_record(lhs.data->digits_used+lhs.slack);

            uint16 remainder;

            lhs.short_div(lhs.data,rhs,temp,remainder);
            temp->digits_used = 1;
            *(temp->number) = remainder;
            return bigint_kernel_2(temp,0);
        }

    // ----------------------------------------------------------------------------------------

        bigint_kernel_2& bigint_kernel_2::
        operator%= (
            uint16 rhs
        )
        {
            uint16 remainder;
            // if there are other references to this data
            if (data->references != 1)
            {
                data_record* temp = new data_record(data->digits_used+slack);
                data->references -= 1;
                short_div(data,rhs,temp,remainder);
                data = temp;
            }
            else
            {
                short_div(data,rhs,data,remainder);
            }

            data->digits_used = 1;
            *(data->number) = remainder;
            return *this;
        }

    // ----------------------------------------------------------------------------------------

        bool operator < (
            uint16 lhs,
            const bigint_kernel_2& rhs
        )
        {
            return (rhs.data->digits_used > 1 || lhs < *(rhs.data->number) );
        }

    // ----------------------------------------------------------------------------------------

        bool operator < (
            const bigint_kernel_2& lhs,
            uint16 rhs
        )
        {
            return (lhs.data->digits_used == 1 && *(lhs.data->number) < rhs);
        }

    // ----------------------------------------------------------------------------------------

        bool operator == (
            const bigint_kernel_2& lhs,
            uint16 rhs
        )
        {
            return (lhs.data->digits_used == 1 && *(lhs.data->number) == rhs);
        }

    // ----------------------------------------------------------------------------------------

        bool operator == (
            uint16 lhs,
            const bigint_kernel_2& rhs
        )
        {
            return (rhs.data->digits_used == 1 && *(rhs.data->number) == lhs);
        }

    // ----------------------------------------------------------------------------------------

        bigint_kernel_2& bigint_kernel_2::
        operator= (
            uint16 rhs
        )
        {
            // check if there are other references to our data
            if (data->references != 1)
            {
                data->references -= 1;
                try {
                    data = new data_record(slack);
                } catch (...) { data->references += 1; throw; }
            }
            else
            {
                data->digits_used = 1;
            }

            *(data->number) = rhs;

            return *this;
        }

    // ----------------------------------------------------------------------------------------

        bigint_kernel_2& bigint_kernel_2::
        operator++ (
        )
        {
            // if there are other references to this data then make a copy of it
            if (data->references != 1)
            {
                data_record* temp = new data_record(data->digits_used+slack);
                data->references -= 1;
                increment(data,temp);
                data = temp;
            }
            // or if we need to enlarge data then do so
            else if (data->digits_used == data->size)
            {
                data_record* temp = new data_record(data->digits_used+slack);
                increment(data,temp);
                delete data;
                data = temp;
            }
            else
            {
                increment(data,data);
            }

            return *this;
        }

    // ----------------------------------------------------------------------------------------

        const bigint_kernel_2 bigint_kernel_2::
        operator++ (
            int
        )
        {
            data_record* temp; // this is the copy of temp we will return in the end

            data_record* temp2 = new data_record(data->digits_used+slack);
            increment(data,temp2);

            temp = data;
            data = temp2;

            return bigint_kernel_2(temp,0);
        }

    // ----------------------------------------------------------------------------------------

        bigint_kernel_2& bigint_kernel_2::
        operator-- (
        )
        {
            // if there are other references to this data
            if (data->references != 1)
            {
                data_record* temp = new data_record(data->digits_used+slack);
                data->references -= 1;
                decrement(data,temp);
                data = temp;
            }
            else
            {
                decrement(data,data);
            }

            return *this;
        }

    // ----------------------------------------------------------------------------------------

        const bigint_kernel_2 bigint_kernel_2::
        operator-- (
            int
        )
        {
            data_record* temp; // this is the copy of temp we will return in the end

            data_record* temp2 = new data_record(data->digits_used+slack);
            decrement(data,temp2);

            temp = data;
            data = temp2;

            return bigint_kernel_2(temp,0);
        }

    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------
        // private member function definitions
    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------

        void bigint_kernel_2::
        short_add (
            const data_record* data,
            uint16 value,
            data_record* result
        ) const
        {
            // put value into the carry part of temp
            uint32 temp = value;
            temp <<= 16;

            const uint16* number = data->number;
            const uint16* end = number + data->digits_used; // one past the end of number
            uint16* r = result->number;

            while (number != end)
            {
                // add *number and the current carry
                temp = *number + (temp>>16);
                // put the low word of temp into *r
                *r = static_cast<uint16>(temp & 0xFFFF);

                ++number;
                ++r;
            }

            // if there is a final carry
            if ((temp>>16) != 0)
            {
                result->digits_used = data->digits_used + 1;
                // store the carry in the most significant digit of the result
                *r = static_cast<uint16>(temp>>16);
            }
            else
            {
                result->digits_used = data->digits_used;
            }
        }

    // ----------------------------------------------------------------------------------------

        void bigint_kernel_2::
        short_sub (
            const data_record* data,
            uint16 value,
            data_record* result
        ) const
        {

            const uint16* number = data->number;
            const uint16* end = number + data->digits_used - 1;
            uint16* r = result->number;

            uint32 temp = *number - value;

            // put the low word of temp into *data
            *r = static_cast<uint16>(temp & 0xFFFF);

            while (number != end)
            {
                ++number;
                ++r;

                // subtract the carry from *number
                temp = *number - (temp>>31);

                // put the low word of temp into *r
                *r = static_cast<uint16>(temp & 0xFFFF);
            }

            // if we lost a digit in the subtraction
            if (*r == 0)
            {
                if (data->digits_used == 1)
                    result->digits_used = 1;
                else
                    result->digits_used = data->digits_used - 1;
            }
            else
            {
                result->digits_used = data->digits_used;
            }

        }

    // ----------------------------------------------------------------------------------------

        void bigint_kernel_2::
        short_mul (
            const data_record* data,
            uint16 value,
            data_record* result
        ) const
        {

            uint32 temp = 0;

            const uint16* number = data->number;
            uint16* r = result->number;
            const uint16* end = r + data->digits_used;

            while ( r != end)
            {

                // multiply *data and value and add in the carry
                temp = *number*(uint32)value + (temp>>16);

                // put the low word of temp into *data
                *r = static_cast<uint16>(temp & 0xFFFF);

                ++number;
                ++r;
            }

            // if there is a final carry
            if ((temp>>16) != 0)
            {
                result->digits_used = data->digits_used + 1;
                // put the final carry into the most significant digit of the result
                *r = static_cast<uint16>(temp>>16);
            }
            else
            {
                result->digits_used = data->digits_used;
            }

        }

    // ----------------------------------------------------------------------------------------

        void bigint_kernel_2::
        short_div (
            const data_record* data,
            uint16 value,
            data_record* result,
            uint16& rem
        ) const
        {

            uint16 remainder = 0;
            uint32 temp;

            const uint16* number = data->number + data->digits_used - 1;
            const uint16* end = number - data->digits_used;
            uint16* r = result->number + data->digits_used - 1;

            // if we are losing a digit in this division
            if (*number < value)
            {
                if (data->digits_used == 1)
                    result->digits_used = 1;
                else
                    result->digits_used = data->digits_used - 1;
            }
            else
            {
                result->digits_used = data->digits_used;
            }

            // perform the actual division
            while (number != end)
            {

                temp = *number + (((uint32)remainder)<<16);

                *r = static_cast<uint16>(temp/value);
                remainder = static_cast<uint16>(temp%value);

                --number;
                --r;
            }

            rem = remainder;
        }

    // ----------------------------------------------------------------------------------------

        void bigint_kernel_2::
        long_add (
            const data_record* lhs,
            const data_record* rhs,
            data_record* result
        ) const
        {
            // put value into the carry part of temp
            uint32 temp=0;

            uint16* min_num;  // the number with the least digits used
            uint16* max_num;  // the number with the most digits used
            uint16* min_end;  // one past the end of min_num
            uint16* max_end;  // one past the end of max_num
            uint16* r = result->number;

            uint32 max_digits_used;
            if (lhs->digits_used < rhs->digits_used)
            {
                max_digits_used = rhs->digits_used;
                min_num = lhs->number;
                max_num = rhs->number;
                min_end = min_num + lhs->digits_used;
                max_end = max_num + rhs->digits_used;
            }
            else
            {
                max_digits_used = lhs->digits_used;
                min_num = rhs->number;
                max_num = lhs->number;
                min_end = min_num + rhs->digits_used;
                max_end = max_num + lhs->digits_used;
            }

            while (min_num != min_end)
            {
                // add *min_num, *max_num and the current carry
                temp = *min_num + *max_num + (temp>>16);
                // put the low word of temp into *r
                *r = static_cast<uint16>(temp & 0xFFFF);

                ++min_num;
                ++max_num;
                ++r;
            }

            while (max_num != max_end)
            {
                // add *max_num and the current carry
                temp = *max_num + (temp>>16);
                // put the low word of temp into *r
                *r = static_cast<uint16>(temp & 0xFFFF);

                ++max_num;
                ++r;
            }

            // check if there was a final carry
            if ((temp>>16) != 0)
            {
                result->digits_used = max_digits_used + 1;
                // put the carry into the most significant digit in the result
                *r = static_cast<uint16>(temp>>16);
            }
            else
            {
                result->digits_used = max_digits_used;
            }

        }

    // ----------------------------------------------------------------------------------------

        void bigint_kernel_2::
        long_sub (
            const data_record* lhs,
            const data_record* rhs,
            data_record* result
        ) const
        {

            const uint16* number1 = lhs->number;
            const uint16* number2 = rhs->number;
            const uint16* end = number2 + rhs->digits_used;
            uint16* r = result->number;

            uint32 temp =0;

            while (number2 != end)
            {

                // subtract *number2 from *number1 and then subtract any carry
                temp = *number1 - *number2 - (temp>>31);

                // put the low word of temp into *r
                *r = static_cast<uint16>(temp & 0xFFFF);

                ++number1;
                ++number2;
                ++r;
            }

            end = lhs->number + lhs->digits_used;
            while (number1 != end)
            {

                // subtract the carry from *number1
                temp = *number1 - (temp>>31);

                // put the low word of temp into *r
                *r = static_cast<uint16>(temp & 0xFFFF);

                ++number1;
                ++r;
            }

            result->digits_used = lhs->digits_used;
            // adjust the number of digits used appropriately
            --r;
            while (*r == 0 && result->digits_used > 1)
            {
                --r;
                --result->digits_used;
            }
        }

    // ----------------------------------------------------------------------------------------

        void bigint_kernel_2::
        long_div (
            const data_record* lhs,
            const data_record* rhs,
            data_record* result,
            data_record* remainder
        ) const
        {
            // zero result
            result->digits_used = 1;
            *(result->number) = 0;

            uint16* a;
            uint16* b;
            uint16* end;

            // copy lhs into remainder
            remainder->digits_used = lhs->digits_used;
            a = remainder->number;
            end = a + remainder->digits_used;
            b = lhs->number;
            while (a != end)
            {
                *a = *b;
                ++a;
                ++b;
            }

            // if rhs is bigger than lhs then result == 0 and remainder == lhs
            // so then we can quit right now
            if (is_less_than(lhs,rhs))
            {
                return;
            }

            // make a temporary number
            data_record temp(lhs->digits_used + slack);

            // shift rhs left until it is one shift away from being larger than lhs and
            // put the number of left shifts necessary into shifts
            uint32 shifts;
            shifts = (lhs->digits_used - rhs->digits_used) * 16;

            shift_left(rhs,&temp,shifts);

            // while (lhs > temp)
            while (is_less_than(&temp,lhs))
            {
                shift_left(&temp,&temp,1);
                ++shifts;
            }
            // make sure lhs isn't smaller than temp
            while (is_less_than(lhs,&temp))
            {
                shift_right(&temp,&temp);
                --shifts;
            }

            // we want to execute the loop shifts +1 times
            ++shifts;
            while (shifts != 0)
            {
                shift_left(result,result,1);
                // if (temp <= remainder)
                if (!is_less_than(remainder,&temp))
                {
                    long_sub(remainder,&temp,remainder);

                    // increment result
                    uint16* r = result->number;
                    uint16* end = r + result->digits_used;
                    while (true)
                    {
                        ++(*r);
                        // if there was no carry then we are done
                        if (*r != 0)
                            break;

                        ++r;

                        // if we hit the end of r and there is still a carry then
                        // the next digit of r is 1 and there is one more digit used
                        if (r == end)
                        {
                            *r = 1;
                            ++(result->digits_used);
                            break;
                        }
                    }
                }
                shift_right(&temp,&temp);
                --shifts;
            }

        }

    // ----------------------------------------------------------------------------------------

        void bigint_kernel_2::
        long_mul (
            const data_record* lhs,
            const data_record* rhs,
            data_record* result
        ) const
        {
            // if one of the numbers is small then use this simple but O(n^2) algorithm
            if (std::min(lhs->digits_used, rhs->digits_used) < 10)
            {
                // make result be zero
                result->digits_used = 1;
                *(result->number) = 0;

                const data_record* aa;
                const data_record* bb;

                if (lhs->digits_used < rhs->digits_used)
                {
                    // make copies of lhs and rhs and give them an appropriate amount of
                    // extra memory so there won't be any overflows
                    aa = lhs;
                    bb = rhs;
                }
                else
                {
                    // make copies of lhs and rhs and give them an appropriate amount of
                    // extra memory so there won't be any overflows
                    aa = rhs;
                    bb = lhs;
                }

                // copy the larger(approximately) of lhs and rhs into b
                data_record b(*bb,aa->digits_used+slack);

                uint32 shift_value = 0;
                uint16* anum = aa->number;
                uint16* end = anum + aa->digits_used;
                while (anum != end )
                {
                    uint16 bit = 0x0001;

                    for (int i = 0; i < 16; ++i)
                    {
                        // if the specified bit of a is 1
                        if ((*anum & bit) != 0)
                        {
                            shift_left(&b,&b,shift_value);
                            shift_value = 0;
                            long_add(&b,result,result);
                        }
                        ++shift_value;
                        bit <<= 1;
                    }

                    ++anum;
                }
            }
            else  // else if both lhs and rhs are large then use the more complex
                  // O(n*logn) algorithm
            {
                uint32 size = 1;
                // make size a power of 2
                while (size < (lhs->digits_used + rhs->digits_used)*2)
                {
                    size *= 2;
                }

                // allocate some temporary space so we can do the FFT
                ct* a = new ct[size];
                ct* b; try {b = new ct[size]; } catch (...) { delete [] a; throw; }

                // load lhs into the a array.  We are breaking the input number into
                // 8bit chunks for the purpose of using this fft algorithm.  The reason
                // for this is so that we have smaller numbers coming out of the final
                // ifft.  This helps avoid overflow.
                for (uint32 i = 0; i < lhs->digits_used; ++i)
                {
                    a[i*2] = ct((t)(lhs->number[i]&0xFF),0);
                    a[i*2+1] = ct((t)(lhs->number[i]>>8),0);
                }
                for (uint32 i = lhs->digits_used*2; i < size; ++i)
                {
                    a[i] = 0;
                }

                // load rhs into the b array
                for (uint32 i = 0; i < rhs->digits_used; ++i)
                {
                    b[i*2] = ct((t)(rhs->number[i]&0xFF),0);
                    b[i*2+1] = ct((t)(rhs->number[i]>>8),0);
                }
                for (uint32 i = rhs->digits_used*2; i < size; ++i)
                {
                    b[i] = 0;
                }

                // perform the forward fft of a and b
                fft(a,size);
                fft(b,size);

                const double l = 1.0/size;

                // do the pointwise multiply of a and b and also apply the scale
                // factor in this loop too.
                for (unsigned long i = 0; i < size; ++i)
                {
                    a[i] = l*a[i]*b[i];
                }

                // Now compute the inverse fft of the pointwise multiplication of a and b.
                // This is basically the result.  We just have to take care of any carries
                // that should happen.
                ifft(a,size);

                // loop over the result and propagate any carries that need to take place.
                // We will also be moving the resulting numbers into result->number at
                // the same time.
                uint64 carry = 0;
                result->digits_used = 0;
                int zeros = 0;
                const uint32 len = lhs->digits_used + rhs->digits_used;
                for (unsigned long i = 0; i < len; ++i)
                {
                    uint64 num1 = static_cast<uint64>(std::floor(a[i*2].real()+0.5));
                    num1 += carry;
                    carry = 0;
                    if (num1 > 255)
                    {
                        carry = num1 >> 8;
                        num1 = (num1&0xFF);
                    }

                    uint64 num2 = static_cast<uint64>(std::floor(a[i*2+1].real()+0.5));
                    num2 += carry;
                    carry = 0;
                    if (num2 > 255)
                    {
                        carry = num2 >> 8;
                        num2 = (num2&0xFF);
                    }

                    // put the new number into its final place
                    num1 = (num2<<8) | num1;
                    result->number[i] = static_cast<uint16>(num1);

                    // keep track of the number of leading zeros
                    if (num1 == 0)
                        ++zeros;
                    else
                        zeros = 0;
                    ++(result->digits_used);
                }

                // adjust digits_used so that it reflects the actual number
                // of non-zero digits in our representation.
                result->digits_used -= zeros;

                // if the result was zero then adjust the result accordingly
                if (result->digits_used == 0)
                {
                    // make result be zero
                    result->digits_used = 1;
                    *(result->number) = 0;
                }

                // free all the temporary buffers
                delete [] a;
                delete [] b;
            }
        }

    // ----------------------------------------------------------------------------------------

        void bigint_kernel_2::
        shift_left (
            const data_record* data,
            data_record* result,
            uint32 shift_amount
        ) const
        {
            uint32 offset = shift_amount/16;
            shift_amount &= 0xf;  // same as shift_amount %= 16;

            uint16* r = result->number + data->digits_used + offset; // result
            uint16* end = data->number;
            uint16* s = end + data->digits_used; // source
            const uint32 temp = 16 - shift_amount;

            *r = (*(--s) >> temp);
            // set the number of digits used in the result
            // if the upper bits from *s were zero then don't count this first word
            if (*r == 0)
            {
                result->digits_used = data->digits_used + offset;
            }
            else
            {
                result->digits_used = data->digits_used + offset + 1;
            }
            --r;

            while (s != end)
            {
                *r = ((*s << shift_amount) | ( *(s-1) >> temp));
                --r;
                --s;
            }
            *r = *s << shift_amount;

            // now zero the rest of the result
            end = result->number;
            while (r != end)
                *(--r) = 0;

        }

    // ----------------------------------------------------------------------------------------

        void bigint_kernel_2::
        shift_right (
            const data_record* data,
            data_record* result
        ) const
        {

                uint16* r = result->number; // result
                uint16* s = data->number; // source
                uint16* end = s + data->digits_used - 1;

                while (s != end)
                {
                    *r = (*s >> 1) | (*(s+1) << 15);
                    ++r;
                    ++s;
                }
                *r = *s >> 1;

                // calculate the new number for digits_used
                if (*r == 0)
                {
                    if (data->digits_used != 1)
                        result->digits_used = data->digits_used - 1;
                    else
                        result->digits_used = 1;
                }
                else
                {
                    result->digits_used = data->digits_used;
                }

        }

    // ----------------------------------------------------------------------------------------

        bool bigint_kernel_2::
        is_less_than (
            const data_record* lhs,
            const data_record* rhs
        ) const
        {
            uint32 lhs_digits_used = lhs->digits_used;
            uint32 rhs_digits_used = rhs->digits_used;

            // if lhs is definitely less than rhs
            if (lhs_digits_used < rhs_digits_used )
                return true;
            // if lhs is definitely greater than rhs
            else if (lhs_digits_used > rhs_digits_used)
                return false;
            else
            {
                uint16* end = lhs->number;
                uint16* l = end         + lhs_digits_used;
                uint16* r = rhs->number + rhs_digits_used;

                while (l != end)
                {
                    --l;
                    --r;
                    if (*l < *r)
                        return true;
                    else if (*l > *r)
                        return false;
                }

                // at this point we know that they are equal
                return false;
            }

        }

    // ----------------------------------------------------------------------------------------

        bool bigint_kernel_2::
        is_equal_to (
            const data_record* lhs,
            const data_record* rhs
        ) const
        {
            // if lhs and rhs are definitely not equal
            if (lhs->digits_used != rhs->digits_used )
            {
                return false;
            }
            else
            {
                uint16* l = lhs->number;
                uint16* r = rhs->number;
                uint16* end = l + lhs->digits_used;

                while (l != end)
                {
                    if (*l != *r)
                        return false;
                    ++l;
                    ++r;
                }

                // at this point we know that they are equal
                return true;
            }

        }

    // ----------------------------------------------------------------------------------------

        void bigint_kernel_2::
        increment (
            const data_record* source,
            data_record* dest
        ) const
        {
            uint16* s = source->number;
            uint16* d = dest->number;
            uint16* end = s + source->digits_used;
            while (true)
            {
                *d = *s + 1;

                // if there was no carry then break out of the loop
                if (*d != 0)
                {
                    dest->digits_used = source->digits_used;

                    // copy the rest of the digits over to d
                    ++d; ++s;
                    while (s != end)
                    {
                        *d = *s;
                        ++d;
                        ++s;
                    }

                    break;
                }

                ++s;

                // if we have hit the end of s and there was a carry up to this point
                // then just make the next digit 1 and add one to the digits used
                if (s == end)
                {
                    ++d;
                    dest->digits_used = source->digits_used + 1;
                    *d = 1;
                    break;
                }

                ++d;
            }
        }

    // ----------------------------------------------------------------------------------------

        void bigint_kernel_2::
        decrement (
            const data_record* source,
            data_record* dest
        ) const
        {
            uint16* s = source->number;
            uint16* d = dest->number;
            uint16* end = s + source->digits_used;
            while (true)
            {
                *d = *s - 1;

                // if there was no carry then break out of the loop
                if (*d != 0xFFFF)
                {
                    // if we lost a digit in the subtraction
                    if (*d == 0 && s+1 == end)
                    {
                        if (source->digits_used == 1)
                            dest->digits_used = 1;
                        else
                            dest->digits_used = source->digits_used - 1;
                    }
                    else
                    {
                        dest->digits_used = source->digits_used;
                    }
                    break;
                }
                else
                {
                    ++d;
                    ++s;
                }

            }

            // copy the rest of the digits over to d
            ++d;
            ++s;
            while (s != end)
            {
                *d = *s;
                ++d;
                ++s;
            }
        }

    // ----------------------------------------------------------------------------------------

        void bigint_kernel_2::
        fft (
            ct* data,
            unsigned long len
        ) const
        {
            const t pi2 = -2.0*3.1415926535897932384626433832795028841971693993751;

            const unsigned long half = len/2;

            std::vector<ct> twiddle_factors;
            twiddle_factors.resize(half);

            // compute the complex root of unity w
            const t temp = pi2/len;
            ct w = ct(std::cos(temp),std::sin(temp));

            ct w_pow = 1;

            // compute the twiddle factors
            for (std::vector<ct>::size_type j = 0; j < twiddle_factors.size(); ++j)
            {
                twiddle_factors[j] = w_pow;
                w_pow *= w;
            }

            ct a, b;

            // now compute the decimation in frequency.  This first
            // outer loop loops log2(len) number of times
            unsigned long skip = 1;
            for (unsigned long step = half; step != 0; step >>= 1)
            {
                // do blocks of butterflies in this loop
                for (unsigned long j = 0; j < len; j += step*2)
                {
                    // do step butterflies
                    for (unsigned long k = 0; k < step; ++k)
                    {
                        const unsigned long a_idx = j+k;
                        const unsigned long b_idx = j+k+step;
                        a = data[a_idx] + data[b_idx];
                        b = (data[a_idx] - data[b_idx])*twiddle_factors[k*skip];
                        data[a_idx] = a;
                        data[b_idx] = b;
                    }
                }
                skip *= 2;
            }
        }

    // ----------------------------------------------------------------------------------------

        void bigint_kernel_2::
        ifft(
            ct* data,
            unsigned long len
        ) const
        {
            const t pi2 = 2.0*3.1415926535897932384626433832795028841971693993751;

            const unsigned long half = len/2;

            std::vector<ct> twiddle_factors;
            twiddle_factors.resize(half);

            // compute the complex root of unity w
            const t temp = pi2/len;
            ct w = ct(std::cos(temp),std::sin(temp));

            ct w_pow = 1;

            // compute the twiddle factors
            for (std::vector<ct>::size_type j = 0; j < twiddle_factors.size(); ++j)
            {
                twiddle_factors[j] = w_pow;
                w_pow *= w;
            }

            ct a, b;

            // now compute the inverse decimation in frequency.  This first
            // outer loop loops log2(len) number of times
            unsigned long skip = half;
            for (unsigned long step = 1; step <= half; step <<= 1)
            {
                // do blocks of butterflies in this loop
                for (unsigned long j = 0; j < len; j += step*2)
                {
                    // do step butterflies
                    for (unsigned long k = 0; k < step; ++k)
                    {
                        const unsigned long a_idx = j+k;
                        const unsigned long b_idx = j+k+step;
                        data[b_idx] *= twiddle_factors[k*skip];
                        a = data[a_idx] + data[b_idx];
                        b = data[a_idx] - data[b_idx];
                        data[a_idx] = a;
                        data[b_idx] = b;
                    }
                }
                skip /= 2;
            }
        }

    // ----------------------------------------------------------------------------------------

    }
    #endif // DLIB_BIGINT_KERNEL_2_CPp_