﻿/*
 * The MIT License
 *
 * Copyright 2019 Palmtree Software.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#include "pmc_resourceholder_uint.h"
#include "pmc_uint_internal.h"
#include "pmc_autogenerated_inline_func.h"
#include "pmc_basic.h"

namespace Palmtree::Math::Core::Internal::Basic
{

    namespace NDivideExactly
    {

        _BYTE_T inverse8_table[128] =
        {
            1, 171, 205, 183, 57, 163, 197, 239,
            241, 27, 61, 167, 41, 19, 53, 223,
            225, 139, 173, 151, 25, 131, 165, 207,
            209, 251, 29, 135, 9, 243, 21, 191,
            193, 107, 141, 119, 249, 99, 133, 175,
            177, 219, 253, 103, 233, 211, 245, 159,
            161, 75, 109, 87, 217, 67, 101, 143,
            145, 187, 221, 71, 201, 179, 213, 127,
            129, 43, 77, 55, 185, 35, 69, 111,
            113, 155, 189, 39, 169, 147, 181, 95,
            97, 11, 45, 23, 153, 3, 37, 79,
            81, 123, 157, 7, 137, 115, 149, 63,
            65, 235, 13, 247, 121, 227, 5, 47,
            49, 91, 125, 231, 105, 83, 117, 31,
            33, 203, 237, 215, 89, 195, 229, 15,
            17, 59, 93, 199, 73, 51, 85, 255,
        };
    
    }

#if defined(_MSC_VER) && defined(_M_X64)
    namespace NDivRemEmuration
    {
        static _UINT64_T _DIVREM_UNIT_1W(_UINT64_T u_hi, _UINT64_T u_lo, _UINT32_T v, _UINT64_T *r)
        {
            _UINT32_T _r;
            _UINT32_T u1;
            _UINT32_T u2 = _FROMDWORDTOWORD(u_hi, &u1);
            _UINT32_T u3;
            _UINT32_T u4 = _FROMDWORDTOWORD(u_lo, &u3);
            _UINT32_T q1;
            _UINT32_T q2;
            _UINT32_T q3;
            _UINT32_T q4;
            _UINT64_T t;

            t = _FROMWORDTODWORD(0, u1);
            q1 = (_UINT32_T)(t / v);
            _r = (_UINT32_T)(t % v);

            t = _FROMWORDTODWORD(_r, u2);
            q2 = (_UINT32_T)(t / v);
            _r = (_UINT32_T)(t % v);

            t = _FROMWORDTODWORD(_r, u3);
            q3 = (_UINT32_T)(t / v);
            _r = (_UINT32_T)(t % v);

            t = _FROMWORDTODWORD(_r, u4);
            q4 = (_UINT32_T)(t / v);
            *r = (_UINT32_T)(t % v);

#ifdef _DEBUG
            {
                if (*r < 0)
                    throw InternalErrorException(L"内部エラーが発生しました。", L"pmc_divrem.cpp;NDivRemEmuration::_DIVREM_UNIT_1W;1");
                if (*r >= v)
                    throw InternalErrorException(L"内部エラーが発生しました。", L"pmc_divrem.cpp;NDivRemEmuration::_DIVREM_UNIT_1W;2");
                _UINT64_T temp4 = (_UINT64_T)q4 * v;
                _UINT64_T temp3 = (_UINT64_T)q3 * v;
                _UINT64_T temp2 = (_UINT64_T)q2 * v;
                _UINT64_T temp1 = (_UINT64_T)q1 * v;
                _UINT32_T t_u4;
                _UINT32_T t_u3;
                _UINT32_T t_u2;
                _UINT32_T t_u1;
                _UINT32_T t_u0;
                __CARRY_T carry = 0;
                carry = _addcarry_u32(carry, (_UINT32_T)temp4, (_UINT32_T)*r, &t_u4);
                carry = _addcarry_u32(carry, (_UINT32_T)temp3, (_UINT32_T)(temp4 >> 32), &t_u3);
                carry = _addcarry_u32(carry, (_UINT32_T)temp2, (_UINT32_T)(temp3 >> 32), &t_u2);
                carry = _addcarry_u32(carry, (_UINT32_T)temp1, (_UINT32_T)(temp2 >> 32), &t_u1);
                carry = _addcarry_u32(carry, 0, temp1 >> 32, &t_u0);
                if (carry)
                    throw InternalErrorException(L"内部エラーが発生しました。", L"pmc_divrem.cpp;NDivRemEmuration::_DIVREM_UNIT_1W;3");
                if (t_u0 != 0)
                    throw InternalErrorException(L"内部エラーが発生しました。", L"pmc_divrem.cpp;NDivRemEmuration::_DIVREM_UNIT_1W;4");
                if (t_u1 != (u_hi >> 32))
                    throw InternalErrorException(L"内部エラーが発生しました。", L"pmc_divrem.cpp;NDivRemEmuration::_DIVREM_UNIT_1W;5");
                if (t_u2 != (_UINT32_T)u_hi)
                    throw InternalErrorException(L"内部エラーが発生しました。", L"pmc_divrem.cpp;NDivRemEmuration::_DIVREM_UNIT_1W;6");
                if (t_u3 != (u_lo >> 32))
                    throw InternalErrorException(L"内部エラーが発生しました。", L"pmc_divrem.cpp;NDivRemEmuration::_DIVREM_UNIT_1W;7");
                if (t_u4 != (_UINT32_T)u_lo)
                    throw InternalErrorException(L"内部エラーが発生しました。", L"pmc_divrem.cpp;NDivRemEmuration::_DIVREM_UNIT_1W;8");
            }
#endif

            return (_FROMWORDTODWORD(q3, q4));
        }

        static _UINT32_T _Asume_Q_(_UINT32_T uj, _UINT32_T uj1, _UINT32_T v1)
        {
            return (uj == v1 ? (_UINT32_T)-1 : (_UINT32_T)(_FROMWORDTODWORD(uj, uj1) / v1));
        }

        static int _Check_Q_(_UINT32_T q_, _UINT32_T uj, _UINT32_T uj1, _UINT32_T uj2, _UINT32_T v1, _UINT32_T v2)
        {
            _UINT32_T x1_hi;
            _UINT32_T x1_lo = _FROMDWORDTOWORD((_UINT64_T)v2 * q_, &x1_hi);
            _UINT32_T x2_hi;
            _UINT32_T x2_lo = _FROMDWORDTOWORD((_UINT64_T)v1 * q_, &x2_hi);
            _UINT32_T lh2;
            _UINT32_T lh1;
            _UINT32_T lh0;
            __CARRY_T carry = 0;
            carry = _addcarry_u32(carry, x1_lo, 0, &lh2);
            carry = _addcarry_u32(carry, x1_hi, x2_lo, &lh1);
            carry = _addcarry_u32(carry, 0, x2_hi, &lh0);
            _UINT32_T rh2 = uj2;
            _UINT32_T rh1 = uj1;
            _UINT32_T rh0 = uj;
            if (rh0 != lh0)
                return (lh0 > rh0);
            if (rh1 != lh1)
                return (lh1 > rh1);
            return (lh2 > rh2);
        }

        static _UINT32_T _Calculate_Q_(_UINT32_T uj, _UINT32_T uj1, _UINT32_T uj2, _UINT32_T v1, _UINT32_T v2)
        {
            _UINT32_T q_ = _Asume_Q_(uj, uj1, v1);
            if (_Check_Q_(q_, uj, uj1, uj2, v1, v2))
            {
                --q_;
                if (_Check_Q_(q_, uj, uj1, uj2, v1, v2))
                    --q_;
            }
            return (q_);
        }

        static __BORROW_T _MULTIPLY_SUBTRUCT_UNIT(__BORROW_T c, _UINT32_T* k, _UINT32_T* u, _UINT32_T v, _UINT32_T q_)
        {
            _UINT64_T t = (_UINT64_T)v * q_;
            _UINT32_T t_hi;
            _UINT32_T t_lo = _FROMDWORDTOWORD(t, &t_hi);
            _addcarry_u32(_addcarry_u32(0, t_lo, *k, &t_lo), t_hi, 0, &t_hi);
            c = _subborrow_u32(c, *u, t_lo, u);
            *k = t_hi;
            return (c);
        }

        static _UINT64_T _DIVREM_UNIT_nW(_UINT64_T u_hi, _UINT64_T u_lo, _UINT64_T v, _UINT64_T *r)
        {
#ifdef _DEBUG
            _UINT64_T u_hi_orig = u_hi;
            _UINT64_T u_lo_orig = u_lo;
            _UINT64_T v_orig = v;
#endif

            int d = _LZCNT_ALT_64(v);
            _UINT32_T u0;
            if (d == 0)
            {
                u0 = 0;
            }
            else
            {
                u0 = (_UINT32_T)(u_hi >> (64 - d));
                u_hi = (u_hi << d) | (u_lo >> (64 - d));
                u_lo = u_lo << d;
                v = v << d;
            }
            _UINT32_T u1;
            _UINT32_T u2 = _FROMDWORDTOWORD(u_hi, &u1);
            _UINT32_T u3;
            _UINT32_T u4 = _FROMDWORDTOWORD(u_lo, &u3);
            _UINT32_T v1;
            _UINT32_T v2 = _FROMDWORDTOWORD(v, &v1);
            _UINT32_T q1;
            _UINT32_T q2;
            _UINT32_T q3;
            _UINT32_T q_;

            {
                q_ = _Calculate_Q_(u0, u1, u2, v1, v2);

                _UINT64_T t_hi;
                _UINT64_T t_lo = _umul128(_FROMWORDTODWORD(v1, v2), q_, &t_hi);
                _UINT32_T tt;
                _UINT32_T t0 = _FROMDWORDTOWORD(t_hi, &tt);
                _UINT32_T t1;
                _UINT32_T t2 = _FROMDWORDTOWORD(t_lo, &t1);
                if (tt != 0)
                    throw InternalErrorException(L"内部エラーが発生しました。", L"pmc_divrem.cpp;DivRem::_DIVREM_UNIT_nW;1");
                __BORROW_T borrow = 0;
                borrow = _subborrow_u32(borrow, u2, t2, &u2);
                borrow = _subborrow_u32(borrow, u1, t1, &u1);
                borrow = _subborrow_u32(borrow, u0, t0, &u0);
                if (borrow)
                {
                    __CARRY_T carry = 0;
                    carry = _addcarry_u32(carry, u2, v2, &u2);
                    carry = _addcarry_u32(carry, u1, v1, &u1);
                    carry = _addcarry_u32(carry, u0, 0, &u0);
                    --q_;
                }
                q1 = q_;
                if (u0 > 0)
                    throw InternalErrorException(L"内部エラーが発生しました。", L"pmc_divrem.cpp;NDivRemEmuration::_DIVREM_UNIT_nW;2");
            }

            {
                q_ = _Calculate_Q_(u1, u2, u3, v1, v2);

                _UINT64_T t_hi;
                _UINT64_T t_lo = _umul128(_FROMWORDTODWORD(v1, v2), q_, &t_hi);
                _UINT32_T tt;
                _UINT32_T t0 = _FROMDWORDTOWORD(t_hi, &tt);
                _UINT32_T t1;
                _UINT32_T t2 = _FROMDWORDTOWORD(t_lo, &t1);
                if (tt != 0)
                    throw InternalErrorException(L"内部エラーが発生しました。", L"pmc_divrem.cpp;NDivRemEmuration::_DIVREM_UNIT_nW;3");
                __BORROW_T borrow = 0;
                borrow = _subborrow_u32(borrow, u3, t2, &u3);
                borrow = _subborrow_u32(borrow, u2, t1, &u2);
                borrow = _subborrow_u32(borrow, u1, t0, &u1);
                if (borrow)
                {
                    __CARRY_T carry = 0;
                    carry = _addcarry_u32(carry, u3, v2, &u3);
                    carry = _addcarry_u32(carry, u2, v1, &u2);
                    carry = _addcarry_u32(carry, u1, 0, &u1);
                    --q_;
                }
                q2 = q_;
                if (u1 > 0)
                    throw InternalErrorException(L"内部エラーが発生しました。", L"pmc_divrem.cpp;NDivRemEmuration::_DIVREM_UNIT_nW;4");
            }

            {
                q_ = _Calculate_Q_(u2, u3, u4, v1, v2);

                _UINT64_T t_hi;
                _UINT64_T t_lo = _umul128(_FROMWORDTODWORD(v1, v2), q_, &t_hi);
                _UINT32_T tt;
                _UINT32_T t0 = _FROMDWORDTOWORD(t_hi, &tt);
                _UINT32_T t1;
                _UINT32_T t2 = _FROMDWORDTOWORD(t_lo, &t1);
                if (tt != 0)
                    throw InternalErrorException(L"内部エラーが発生しました。", L"pmc_divrem.cpp;NDivRemEmuration::_DIVREM_UNIT_nW;5");
                __BORROW_T borrow = 0;
                borrow = _subborrow_u32(borrow, u4, t2, &u4);
                borrow = _subborrow_u32(borrow, u3, t1, &u3);
                borrow = _subborrow_u32(borrow, u2, t0, &u2);
                if (borrow)
                {
                    __CARRY_T carry = 0;
                    carry = _addcarry_u32(carry, u4, v2, &u4);
                    carry = _addcarry_u32(carry, u3, v1, &u3);
                    carry = _addcarry_u32(carry, u2, 0, &u2);
                    --q_;
                }
                q3 = q_;
                if (u2 > 0)
                    throw InternalErrorException(L"内部エラーが発生しました。", L"pmc_divrem.cpp;NDivRemEmuration::_DIVREM_UNIT_nW;6");
            }

            if (d == 0)
                *r = _FROMWORDTODWORD(u3, u4);
            else
            {

                _UINT32_T r_hi = (u2 << (32 - d)) | (u3 >> d);
                _UINT32_T r_lo = (u3 << (32 - d)) | (u4 >> d);
                *r = _FROMWORDTODWORD(r_hi, r_lo);
            }

#ifdef _DEBUG
            {
                if (*r < 0)
                    throw InternalErrorException(L"内部エラーが発生しました。", L"pmc_divrem.cpp;NDivRemEmuration::_DIVREM_UNIT_nW;7");
                if (*r >= v_orig)
                    throw InternalErrorException(L"内部エラーが発生しました。", L"pmc_divrem.cpp;NDivRemEmuration::_DIVREM_UNIT_nW;8");

                // 検算をする

                _UINT64_T q_hi = _FROMWORDTODWORD(0, q1);
                _UINT64_T q_lo = _FROMWORDTODWORD(q2, q3);

                _UINT64_T t1_hi;
                _UINT64_T t1_lo = _umul128(v_orig, q_lo, &t1_hi);
                _UINT64_T t2_hi;
                _UINT64_T t2_lo = _umul128(v_orig, q_hi, &t2_hi);

                _UINT64_T t_vh;
                _UINT64_T t_hi;
                _UINT64_T t_lo;
                __CARRY_T carry = 0;
                carry = _addcarry_u64(carry, t1_lo, *r, &t_lo);
                carry = _addcarry_u64(carry, t1_hi, t2_lo, &t_hi);
                carry = _addcarry_u64(carry, 0, t2_hi, &t_vh);
                if (carry || t_vh > 0)
                    throw InternalErrorException(L"内部エラーが発生しました。", L"pmc_divrem.cpp;NDivRemEmuration::_DIVREM_UNIT_nW;9");
                if (t_hi != u_hi_orig)
                    throw InternalErrorException(L"内部エラーが発生しました。", L"pmc_divrem.cpp;NDivRemEmuration::_DIVREM_UNIT_nW;10");
                if (t_lo != u_lo_orig)
                    throw InternalErrorException(L"内部エラーが発生しました。", L"pmc_divrem.cpp;NDivRemEmuration::_DIVREM_UNIT_nW;11");
            }
#endif
            return (_FROMWORDTODWORD(q2, q3));
        }
    }

    _UINT64_T __DLLEXPORT_DIVREM _DIVREM_UNIT(_UINT64_T u_hi, _UINT64_T u_lo, _UINT64_T v, _UINT64_T *r)
    {
        _UINT32_T v_hi;
        _UINT32_T v_lo = _FROMDWORDTOWORD(v, &v_hi);
        if (v_hi == 0)
        {
            _UINT64_T q = NDivRemEmuration::_DIVREM_UNIT_1W(u_hi, u_lo, v_lo, r);
            return (q);
        }
        else
        {
            _UINT64_T q = NDivRemEmuration::_DIVREM_UNIT_nW(u_hi, u_lo, v, r);
            return (q);
        }
    }

    _UINT64_T __DLLEXPORT_DIVREM _DIVREM_SINGLE_UNIT(_UINT64_T r, _UINT64_T u, _UINT64_T v, _UINT64_T *q)
    {
        _UINT64_T RR;
        *q = _DIVREM_UNIT(r, u, v, &RR);
        return (RR);
    }
#endif

    namespace NDivRem
    {

        __inline static __UNIT_TYPE Rem_UX_1W(__UNIT_TYPE* u_buf, __UNIT_TYPE u_buf_count, __UNIT_TYPE v)
        {
            // u の最上位ワードは 0 でありうることに注意すること。
            __UNIT_TYPE* up = u_buf + u_buf_count - 1;
            __UNIT_TYPE u_count = u_buf_count;
            __UNIT_TYPE dummy_q;
            __UNIT_TYPE r = 0;
            __UNIT_TYPE count = u_count >> 5;
            while (count != 0)
            {
                r = _DIVREM_SINGLE_UNIT(r, up[-0], v, &dummy_q);
                r = _DIVREM_SINGLE_UNIT(r, up[-1], v, &dummy_q);
                r = _DIVREM_SINGLE_UNIT(r, up[-2], v, &dummy_q);
                r = _DIVREM_SINGLE_UNIT(r, up[-3], v, &dummy_q);
                r = _DIVREM_SINGLE_UNIT(r, up[-4], v, &dummy_q);
                r = _DIVREM_SINGLE_UNIT(r, up[-5], v, &dummy_q);
                r = _DIVREM_SINGLE_UNIT(r, up[-6], v, &dummy_q);
                r = _DIVREM_SINGLE_UNIT(r, up[-7], v, &dummy_q);
                r = _DIVREM_SINGLE_UNIT(r, up[-8], v, &dummy_q);
                r = _DIVREM_SINGLE_UNIT(r, up[-9], v, &dummy_q);
                r = _DIVREM_SINGLE_UNIT(r, up[-10], v, &dummy_q);
                r = _DIVREM_SINGLE_UNIT(r, up[-11], v, &dummy_q);
                r = _DIVREM_SINGLE_UNIT(r, up[-12], v, &dummy_q);
                r = _DIVREM_SINGLE_UNIT(r, up[-13], v, &dummy_q);
                r = _DIVREM_SINGLE_UNIT(r, up[-14], v, &dummy_q);
                r = _DIVREM_SINGLE_UNIT(r, up[-15], v, &dummy_q);
                r = _DIVREM_SINGLE_UNIT(r, up[-16], v, &dummy_q);
                r = _DIVREM_SINGLE_UNIT(r, up[-17], v, &dummy_q);
                r = _DIVREM_SINGLE_UNIT(r, up[-18], v, &dummy_q);
                r = _DIVREM_SINGLE_UNIT(r, up[-19], v, &dummy_q);
                r = _DIVREM_SINGLE_UNIT(r, up[-20], v, &dummy_q);
                r = _DIVREM_SINGLE_UNIT(r, up[-21], v, &dummy_q);
                r = _DIVREM_SINGLE_UNIT(r, up[-22], v, &dummy_q);
                r = _DIVREM_SINGLE_UNIT(r, up[-23], v, &dummy_q);
                r = _DIVREM_SINGLE_UNIT(r, up[-24], v, &dummy_q);
                r = _DIVREM_SINGLE_UNIT(r, up[-25], v, &dummy_q);
                r = _DIVREM_SINGLE_UNIT(r, up[-26], v, &dummy_q);
                r = _DIVREM_SINGLE_UNIT(r, up[-27], v, &dummy_q);
                r = _DIVREM_SINGLE_UNIT(r, up[-28], v, &dummy_q);
                r = _DIVREM_SINGLE_UNIT(r, up[-29], v, &dummy_q);
                r = _DIVREM_SINGLE_UNIT(r, up[-30], v, &dummy_q);
                r = _DIVREM_SINGLE_UNIT(r, up[-31], v, &dummy_q);
                up -= 32;
                --count;
#ifdef ENABLED_PERFORMANCE_COUNTER
                if (sizeof(r) == sizeof(_UINT64_T))
                    AddToDIV64Counter(32);
                else
                    AddToDIV32Counter(32);
#endif
            }

            if (u_count & 0x10)
            {
                r = _DIVREM_SINGLE_UNIT(r, up[-0], v, &dummy_q);
                r = _DIVREM_SINGLE_UNIT(r, up[-1], v, &dummy_q);
                r = _DIVREM_SINGLE_UNIT(r, up[-2], v, &dummy_q);
                r = _DIVREM_SINGLE_UNIT(r, up[-3], v, &dummy_q);
                r = _DIVREM_SINGLE_UNIT(r, up[-4], v, &dummy_q);
                r = _DIVREM_SINGLE_UNIT(r, up[-5], v, &dummy_q);
                r = _DIVREM_SINGLE_UNIT(r, up[-6], v, &dummy_q);
                r = _DIVREM_SINGLE_UNIT(r, up[-7], v, &dummy_q);
                r = _DIVREM_SINGLE_UNIT(r, up[-8], v, &dummy_q);
                r = _DIVREM_SINGLE_UNIT(r, up[-9], v, &dummy_q);
                r = _DIVREM_SINGLE_UNIT(r, up[-10], v, &dummy_q);
                r = _DIVREM_SINGLE_UNIT(r, up[-11], v, &dummy_q);
                r = _DIVREM_SINGLE_UNIT(r, up[-12], v, &dummy_q);
                r = _DIVREM_SINGLE_UNIT(r, up[-13], v, &dummy_q);
                r = _DIVREM_SINGLE_UNIT(r, up[-14], v, &dummy_q);
                r = _DIVREM_SINGLE_UNIT(r, up[-15], v, &dummy_q);
                up -= 16;
#ifdef ENABLED_PERFORMANCE_COUNTER
                if (sizeof(r) == sizeof(_UINT64_T))
                    AddToDIV64Counter(16);
                else
                    AddToDIV32Counter(16);
#endif
            }

            if (u_count & 0x8)
            {
                r = _DIVREM_SINGLE_UNIT(r, up[-0], v, &dummy_q);
                r = _DIVREM_SINGLE_UNIT(r, up[-1], v, &dummy_q);
                r = _DIVREM_SINGLE_UNIT(r, up[-2], v, &dummy_q);
                r = _DIVREM_SINGLE_UNIT(r, up[-3], v, &dummy_q);
                r = _DIVREM_SINGLE_UNIT(r, up[-4], v, &dummy_q);
                r = _DIVREM_SINGLE_UNIT(r, up[-5], v, &dummy_q);
                r = _DIVREM_SINGLE_UNIT(r, up[-6], v, &dummy_q);
                r = _DIVREM_SINGLE_UNIT(r, up[-7], v, &dummy_q);
                up -= 8;
#ifdef ENABLED_PERFORMANCE_COUNTER
                if (sizeof(r) == sizeof(_UINT64_T))
                    AddToDIV64Counter(8);
                else
                    AddToDIV32Counter(8);
#endif
            }

            if (u_count & 0x4)
            {
                r = _DIVREM_SINGLE_UNIT(r, up[-0], v, &dummy_q);
                r = _DIVREM_SINGLE_UNIT(r, up[-1], v, &dummy_q);
                r = _DIVREM_SINGLE_UNIT(r, up[-2], v, &dummy_q);
                r = _DIVREM_SINGLE_UNIT(r, up[-3], v, &dummy_q);
                up -= 4;
#ifdef ENABLED_PERFORMANCE_COUNTER
                if (sizeof(r) == sizeof(_UINT64_T))
                    AddToDIV64Counter(4);
                else
                    AddToDIV32Counter(4);
#endif
            }

            if (u_count & 0x2)
            {
                r = _DIVREM_SINGLE_UNIT(r, up[-0], v, &dummy_q);
                r = _DIVREM_SINGLE_UNIT(r, up[-1], v, &dummy_q);
                up -= 2;
#ifdef ENABLED_PERFORMANCE_COUNTER
                if (sizeof(r) == sizeof(_UINT64_T))
                    AddToDIV64Counter(2);
                else
                    AddToDIV32Counter(2);
#endif
            }

            if (u_count & 0x1)
            {
                r = _DIVREM_SINGLE_UNIT(r, up[-0], v, &dummy_q);
                up -= 1;
#ifdef ENABLED_PERFORMANCE_COUNTER
                if (sizeof(r) == sizeof(_UINT64_T))
                    IncrementDIV64Counter();
                else
                    IncrementDIV32Counter();
#endif
            }

            return (r);
        }

        __inline static __UNIT_TYPE DivRem_UX_1W(__UNIT_TYPE* u_buf, __UNIT_TYPE u_buf_count, __UNIT_TYPE v, __UNIT_TYPE* q_buf, __UNIT_TYPE q_buf_count)
        {
            _ZERO_MEMORY_UNIT(q_buf + u_buf_count, q_buf_count - u_buf_count);
            __UNIT_TYPE* up = u_buf + u_buf_count - 1;
            __UNIT_TYPE* qp = q_buf + u_buf_count - 1;
            __UNIT_TYPE u_count = u_buf_count;
            __UNIT_TYPE r = 0;
            __UNIT_TYPE count = u_count >> 5;
            while (count != 0)
            {
                r = _DIVREM_SINGLE_UNIT(r, up[-0], v, &qp[-0]);
                r = _DIVREM_SINGLE_UNIT(r, up[-1], v, &qp[-1]);
                r = _DIVREM_SINGLE_UNIT(r, up[-2], v, &qp[-2]);
                r = _DIVREM_SINGLE_UNIT(r, up[-3], v, &qp[-3]);
                r = _DIVREM_SINGLE_UNIT(r, up[-4], v, &qp[-4]);
                r = _DIVREM_SINGLE_UNIT(r, up[-5], v, &qp[-5]);
                r = _DIVREM_SINGLE_UNIT(r, up[-6], v, &qp[-6]);
                r = _DIVREM_SINGLE_UNIT(r, up[-7], v, &qp[-7]);
                r = _DIVREM_SINGLE_UNIT(r, up[-8], v, &qp[-8]);
                r = _DIVREM_SINGLE_UNIT(r, up[-9], v, &qp[-9]);
                r = _DIVREM_SINGLE_UNIT(r, up[-10], v, &qp[-10]);
                r = _DIVREM_SINGLE_UNIT(r, up[-11], v, &qp[-11]);
                r = _DIVREM_SINGLE_UNIT(r, up[-12], v, &qp[-12]);
                r = _DIVREM_SINGLE_UNIT(r, up[-13], v, &qp[-13]);
                r = _DIVREM_SINGLE_UNIT(r, up[-14], v, &qp[-14]);
                r = _DIVREM_SINGLE_UNIT(r, up[-15], v, &qp[-15]);
                r = _DIVREM_SINGLE_UNIT(r, up[-16], v, &qp[-16]);
                r = _DIVREM_SINGLE_UNIT(r, up[-17], v, &qp[-17]);
                r = _DIVREM_SINGLE_UNIT(r, up[-18], v, &qp[-18]);
                r = _DIVREM_SINGLE_UNIT(r, up[-19], v, &qp[-19]);
                r = _DIVREM_SINGLE_UNIT(r, up[-20], v, &qp[-20]);
                r = _DIVREM_SINGLE_UNIT(r, up[-21], v, &qp[-21]);
                r = _DIVREM_SINGLE_UNIT(r, up[-22], v, &qp[-22]);
                r = _DIVREM_SINGLE_UNIT(r, up[-23], v, &qp[-23]);
                r = _DIVREM_SINGLE_UNIT(r, up[-24], v, &qp[-24]);
                r = _DIVREM_SINGLE_UNIT(r, up[-25], v, &qp[-25]);
                r = _DIVREM_SINGLE_UNIT(r, up[-26], v, &qp[-26]);
                r = _DIVREM_SINGLE_UNIT(r, up[-27], v, &qp[-27]);
                r = _DIVREM_SINGLE_UNIT(r, up[-28], v, &qp[-28]);
                r = _DIVREM_SINGLE_UNIT(r, up[-29], v, &qp[-29]);
                r = _DIVREM_SINGLE_UNIT(r, up[-30], v, &qp[-30]);
                r = _DIVREM_SINGLE_UNIT(r, up[-31], v, &qp[-31]);
                up -= 32;
                qp -= 32;
                --count;
#ifdef ENABLED_PERFORMANCE_COUNTER
                if (sizeof(r) == sizeof(_UINT64_T))
                    AddToDIV64Counter(32);
                else
                    AddToDIV32Counter(32);
#endif
            }

            if (u_count & 0x10)
            {
                r = _DIVREM_SINGLE_UNIT(r, up[-0], v, &qp[-0]);
                r = _DIVREM_SINGLE_UNIT(r, up[-1], v, &qp[-1]);
                r = _DIVREM_SINGLE_UNIT(r, up[-2], v, &qp[-2]);
                r = _DIVREM_SINGLE_UNIT(r, up[-3], v, &qp[-3]);
                r = _DIVREM_SINGLE_UNIT(r, up[-4], v, &qp[-4]);
                r = _DIVREM_SINGLE_UNIT(r, up[-5], v, &qp[-5]);
                r = _DIVREM_SINGLE_UNIT(r, up[-6], v, &qp[-6]);
                r = _DIVREM_SINGLE_UNIT(r, up[-7], v, &qp[-7]);
                r = _DIVREM_SINGLE_UNIT(r, up[-8], v, &qp[-8]);
                r = _DIVREM_SINGLE_UNIT(r, up[-9], v, &qp[-9]);
                r = _DIVREM_SINGLE_UNIT(r, up[-10], v, &qp[-10]);
                r = _DIVREM_SINGLE_UNIT(r, up[-11], v, &qp[-11]);
                r = _DIVREM_SINGLE_UNIT(r, up[-12], v, &qp[-12]);
                r = _DIVREM_SINGLE_UNIT(r, up[-13], v, &qp[-13]);
                r = _DIVREM_SINGLE_UNIT(r, up[-14], v, &qp[-14]);
                r = _DIVREM_SINGLE_UNIT(r, up[-15], v, &qp[-15]);
                up -= 16;
                qp -= 16;
#ifdef ENABLED_PERFORMANCE_COUNTER
                if (sizeof(r) == sizeof(_UINT64_T))
                    AddToDIV64Counter(16);
                else
                    AddToDIV32Counter(16);
#endif
            }

            if (u_count & 0x8)
            {
                r = _DIVREM_SINGLE_UNIT(r, up[-0], v, &qp[-0]);
                r = _DIVREM_SINGLE_UNIT(r, up[-1], v, &qp[-1]);
                r = _DIVREM_SINGLE_UNIT(r, up[-2], v, &qp[-2]);
                r = _DIVREM_SINGLE_UNIT(r, up[-3], v, &qp[-3]);
                r = _DIVREM_SINGLE_UNIT(r, up[-4], v, &qp[-4]);
                r = _DIVREM_SINGLE_UNIT(r, up[-5], v, &qp[-5]);
                r = _DIVREM_SINGLE_UNIT(r, up[-6], v, &qp[-6]);
                r = _DIVREM_SINGLE_UNIT(r, up[-7], v, &qp[-7]);
                up -= 8;
                qp -= 8;
#ifdef ENABLED_PERFORMANCE_COUNTER
                if (sizeof(r) == sizeof(_UINT64_T))
                    AddToDIV64Counter(8);
                else
                    AddToDIV32Counter(8);
#endif
            }

            if (u_count & 0x4)
            {
                r = _DIVREM_SINGLE_UNIT(r, up[-0], v, &qp[-0]);
                r = _DIVREM_SINGLE_UNIT(r, up[-1], v, &qp[-1]);
                r = _DIVREM_SINGLE_UNIT(r, up[-2], v, &qp[-2]);
                r = _DIVREM_SINGLE_UNIT(r, up[-3], v, &qp[-3]);
                up -= 4;
                qp -= 4;
#ifdef ENABLED_PERFORMANCE_COUNTER
                if (sizeof(r) == sizeof(_UINT64_T))
                    AddToDIV64Counter(4);
                else
                    AddToDIV32Counter(4);
#endif
            }

            if (u_count & 0x2)
            {
                r = _DIVREM_SINGLE_UNIT(r, up[-0], v, &qp[-0]);
                r = _DIVREM_SINGLE_UNIT(r, up[-1], v, &qp[-1]);
                up -= 2;
                qp -= 2;
#ifdef ENABLED_PERFORMANCE_COUNTER
                if (sizeof(r) == sizeof(_UINT64_T))
                    AddToDIV64Counter(2);
                else
                    AddToDIV32Counter(2);
#endif
            }

            if (u_count & 0x1)
            {
                r = _DIVREM_SINGLE_UNIT(r, up[-0], v, &qp[-0]);
                up -= 1;
                qp -= 1;
#ifdef ENABLED_PERFORMANCE_COUNTER
                if (sizeof(r) == sizeof(_UINT64_T))
                    IncrementDIV64Counter();
                else
                    IncrementDIV32Counter();
#endif
            }

            return (r);
        }

        __inline static __UNIT_TYPE AsumeQ_(__UNIT_TYPE uj, __UNIT_TYPE uj_1, __UNIT_TYPE v1)
        {
            if (uj == v1)
                return (-1);
            __UNIT_TYPE r;
            __UNIT_TYPE q = _DIVREM_UNIT(uj, uj_1, v1, &r);
#ifdef ENABLED_PERFORMANCE_COUNTER
            if (sizeof(v1) == sizeof(_UINT64_T))
                IncrementDIV64Counter();
            else
                IncrementDIV32Counter();
#endif
            return (q);
        }

        __inline static bool CheckQ_(__UNIT_TYPE q_, __UNIT_TYPE uj, __UNIT_TYPE uj_1, __UNIT_TYPE uj_2, __UNIT_TYPE v1, __UNIT_TYPE v2)
        {
            __UNIT_TYPE lh_mi;
            __UNIT_TYPE lh_lo = _MULTIPLY_UNIT(v2, q_, &lh_mi);
            __UNIT_TYPE rh_hi;
            __UNIT_TYPE rh_mi;
            __UNIT_TYPE rh_lo = uj_2;
            __UNIT_TYPE t_hi;
            __UNIT_TYPE t_mi = _MULTIPLY_UNIT(q_, v1, &t_hi);
            _SUBTRUCT_UNIT(_SUBTRUCT_UNIT(0, uj_1, t_mi, &rh_mi), uj, t_hi, &rh_hi);

#ifdef ENABLED_PERFORMANCE_COUNTER
            if (sizeof(q_) == sizeof(_UINT64_T))
                AddToMULTI64Counter(2);
            else
                AddToMULTI32Counter(2);
#endif
            if (rh_hi > 0)
                return (false);
            else if (lh_mi > rh_mi)
                return (true);
            else if (lh_mi < rh_mi)
                return (false);
            else
                return (lh_lo > rh_lo);
        }

        __inline static __UNIT_TYPE CalculateQ_(__UNIT_TYPE* u_buf, __UNIT_TYPE u_buf_count, __UNIT_TYPE* v_buf, __UNIT_TYPE v_buf_count, __UNIT_TYPE q_index)
        {
            __UNIT_TYPE u_index = q_index + v_buf_count;
            __UNIT_TYPE uj = u_buf[u_index];
            __UNIT_TYPE uj_1 = u_buf[u_index - 1];
            __UNIT_TYPE uj_2 = u_buf[u_index - 2];
            __UNIT_TYPE v1 = v_buf[v_buf_count - 1];
            __UNIT_TYPE v2 = v_buf[v_buf_count - 2];
            __UNIT_TYPE q_ = AsumeQ_(uj, uj_1, v1);
            if (!CheckQ_(q_, uj, uj_1, uj_2, v1, v2))
                return (q_);
            --q_;
            if (!CheckQ_(q_, uj, uj_1, uj_2, v1, v2))
                return (q_);
            --q_;
            return (q_);
        }

        __inline static void AddOneLine(__UNIT_TYPE* u_buf, __UNIT_TYPE u_buf_count, __UNIT_TYPE* v_buf, __UNIT_TYPE v_buf_count, __UNIT_TYPE q_index)
        {
            Add(u_buf + q_index, u_buf_count - q_index, v_buf, v_buf_count);
        }

        // q_buf = u_buf / v_buf, u_buf %= v_buf
        __inline static void DivRem_UX_UX(__UNIT_TYPE* u_buf, __UNIT_TYPE u_buf_count, __UNIT_TYPE* v_buf, __UNIT_TYPE v_buf_count, __UNIT_TYPE* q_buf, __UNIT_TYPE q_buf_count)
        {
#ifdef _DEBUG
            // v_buf の最上位bitは 1 でなければならない
            if (v_buf_count > 0 && (v_buf[v_buf_count - 1] & ~((__UNIT_TYPE)-1 >> 1)) == 0)
                throw InternalErrorException(L"予期しないルートに到達しました。", L"pmc_divrem.cpp;NDivRem::DivRem_UX_UX;1");
            if (u_buf_count <= v_buf_count)
                throw InternalErrorException(L"予期しないルートに到達しました。", L"pmc_divrem.cpp;NDivRem::DivRem_UX_UX;2");
            if (q_buf != nullptr)
            {
                if (q_buf_count < u_buf_count - v_buf_count)
                    throw InternalErrorException(L"予期しないルートに到達しました。", L"pmc_divrem.cpp;NDivRem::DivRem_UX_UX;3");
            }
#endif
            if (q_buf != nullptr)
                _ZERO_MEMORY_UNIT(q_buf + (u_buf_count - v_buf_count), q_buf_count - (u_buf_count - v_buf_count));
            __UNIT_TYPE q_index = u_buf_count - v_buf_count - 1;

            for (;;)
            {
                __UNIT_TYPE q_ = CalculateQ_(u_buf, u_buf_count, v_buf, v_buf_count, q_index);

                if (NDivRem::MultiplySubtructArray_UNIT(u_buf, u_buf_count, v_buf, v_buf_count, q_index, q_))
                {
                    // 桁借りが発生した場合
                    --q_;
                    AddOneLine(u_buf, u_buf_count, v_buf, v_buf_count, q_index);
                }

                if (q_buf != nullptr)
                    q_buf[q_index] = q_;
                if (q_index == 0)
                    break;
                --q_index;
            }
#ifdef _DEBUG
            if (__Shrink(u_buf, u_buf_count) > v_buf_count)
                throw InternalErrorException(L"予期しないルートに到達しました。", L"pmc_divrem.cpp;NDivRem::DivRem_UX_UX;4");
#endif
        }

        // q_buf = u_buf / v_buf, r_buf = u_buf % v_buf
        __inline static void DivRem_UX_UX(__UNIT_TYPE* u_buf, __UNIT_TYPE u_buf_count, __UNIT_TYPE* v_buf, __UNIT_TYPE v_buf_count, __UNIT_TYPE* work_v_buf, __UNIT_TYPE work_v_buf_count, __UNIT_TYPE* q_buf, __UNIT_TYPE q_buf_count, __UNIT_TYPE* r_buf, __UNIT_TYPE r_buf_count)
        {
#ifdef _DEBUG
            if (u_buf_count > 0 && u_buf[u_buf_count - 1] == 0)
                throw InternalErrorException(L"予期しないルートに到達しました。", L"pmc_divrem.cpp;NDivRem::DivRem_UX_UX;1");
            if (v_buf_count > 0 && v_buf[v_buf_count - 1] == 0)
                throw InternalErrorException(L"予期しないルートに到達しました。", L"pmc_divrem.cpp;NDivRem::DivRem_UX_UX;2");
            if (u_buf_count < v_buf_count)
                throw InternalErrorException(L"予期しないルートに到達しました。", L"pmc_divrem.cpp;NDivRem::DivRem_UX_UX;3");
            if (r_buf_count < u_buf_count)
                throw InternalErrorException(L"予期しないルートに到達しました。", L"pmc_divrem.cpp;NDivRem::DivRem_UX_UX;4");
            if (q_buf != nullptr)
            {
                if (q_buf_count <= u_buf_count - v_buf_count)
                    throw InternalErrorException(L"予期しないルートに到達しました。", L"pmc_divrem.cpp;NDivRem::DivRem_UX_UX;5");
            }
#endif

            _ZERO_MEMORY_UNIT(r_buf + u_buf_count, r_buf_count - u_buf_count);
            __UNIT_TYPE d_factor = _LZCNT_ALT_UNIT(v_buf[v_buf_count - 1]);
            LeftShift(u_buf, u_buf_count, d_factor, r_buf, r_buf_count);
            LeftShift(v_buf, v_buf_count, d_factor, work_v_buf, work_v_buf_count);

            DivRem_UX_UX(r_buf, r_buf_count, work_v_buf, work_v_buf_count, q_buf, q_buf_count);

            RightShift(r_buf, r_buf_count, d_factor);
        }

    }

    void DivRem(__UNIT_TYPE* u_buf, __UNIT_TYPE u_buf_count, __UNIT_TYPE v, __UNIT_TYPE* q_buf, __UNIT_TYPE q_buf_count, __UNIT_TYPE& r)
    {
#ifdef _DEBUG
        if (u_buf_count > 0 && u_buf[u_buf_count - 1] == 0)
            throw InternalErrorException(L"予期しないルートに到達しました。", L"pmc_divrem.cpp;DivRem;1");
#endif
        if (v == 0)
            throw DivisionByZeroException(L"0による除算が行われようとしました。");
        else if (u_buf_count == 0)
        {
            _ZERO_MEMORY_UNIT(q_buf, q_buf_count);
            r = 0;
        }
        else if (u_buf_count == 1)
        {
#ifdef _DEBUG
            if (q_buf_count < 1)
                throw InternalErrorException(L"予期しないルートに到達しました。", L"pmc_divrem.cpp;DivRem;2");
#endif
            __UNIT_TYPE q = _DIVREM_UNIT(0, u_buf[0], v, &r);
#ifdef ENABLED_PERFORMANCE_COUNTER
            if (sizeof(r) == sizeof(_UINT64_T))
                IncrementDIV64Counter();
            else
                IncrementDIV32Counter();
#endif
            q_buf[0] = q;
            _ZERO_MEMORY_UNIT(q_buf + 1, q_buf_count - 1);
        }
        else
            r = NDivRem::DivRem_UX_1W(u_buf, u_buf_count, v, q_buf, q_buf_count);
    }

    void DivRem(__UNIT_TYPE u, __UNIT_TYPE* v_buf, __UNIT_TYPE v_buf_count, __UNIT_TYPE& q, __UNIT_TYPE& r)
    {
#ifdef _DEBUG
        if (v_buf_count > 0 && v_buf[v_buf_count - 1] == 0)
            throw InternalErrorException(L"予期しないルートに到達しました。", L"pmc_divrem.cpp;DivRem;1");
#endif
        if (v_buf_count == 0)
            throw DivisionByZeroException(L"0による除算が行われようとしました。");
        else if (u == 0 || 1 < v_buf_count)
        {
            q = 0;
            r = u;
        }
        else
        {
            q = _DIVREM_UNIT(0, u, v_buf[0], &r);
#ifdef ENABLED_PERFORMANCE_COUNTER
            if (sizeof(r) == sizeof(_UINT64_T))
                IncrementDIV64Counter();
            else
                IncrementDIV32Counter();
#endif
        }
    }

    // q_buf = u_buf / v_buf, u_buf %= v_buf
    void DivRem(__UNIT_TYPE* u_buf, __UNIT_TYPE u_buf_count, __UNIT_TYPE* v_buf, __UNIT_TYPE v_buf_count, __UNIT_TYPE* q_buf, __UNIT_TYPE q_buf_count)
    {
#ifdef _DEBUG
        if (v_buf_count > 0 && (v_buf[v_buf_count - 1] & ~((__UNIT_TYPE)-1 >> 1)) == 0)
            throw InternalErrorException(L"予期しないルートに到達しました。", L"pmc_divrem.cpp;DivRem;1");
#endif
        if (v_buf_count == 0)
            throw DivisionByZeroException(L"0による除算が行われようとしました。");
        else if (v_buf_count == 1)
        {
            if (u_buf_count == 0)
                _ZERO_MEMORY_UNIT(q_buf, q_buf_count);
            else if (u_buf_count == 1)
            {
#ifdef _DEBUG
                if (q_buf_count < 1)
                    throw InternalErrorException(L"予期しないルートに到達しました。", L"pmc_divrem.cpp;DivRem;3");
#endif
                __UNIT_TYPE r;
                __UNIT_TYPE q = _DIVREM_UNIT(0, u_buf[0], v_buf[0], &r);
#ifdef ENABLED_PERFORMANCE_COUNTER
                if (sizeof(r) == sizeof(_UINT64_T))
                    IncrementDIV64Counter();
                else
                    IncrementDIV32Counter();
#endif
                q_buf[0] = q;
                _ZERO_MEMORY_UNIT(q_buf + 1, q_buf_count - 1);
                u_buf[0] = r;
            }
            else
            {
                u_buf[0] = NDivRem::DivRem_UX_1W(u_buf, u_buf_count, v_buf[0], q_buf, q_buf_count);
                _ZERO_MEMORY_UNIT(u_buf + 1, u_buf_count - 1);
            }
        }
        else
        {
            if (u_buf_count < v_buf_count)
                _ZERO_MEMORY_UNIT(q_buf, q_buf_count);
            else
                NDivRem::DivRem_UX_UX(u_buf, u_buf_count, v_buf, v_buf_count, q_buf, q_buf_count);
        }
    }

    // q_buf = u_buf / v_buf, r_buf = u_buf % v_buf
    void DivRem(__UNIT_TYPE* u_buf, __UNIT_TYPE u_buf_count, __UNIT_TYPE* v_buf, __UNIT_TYPE v_buf_count, __UNIT_TYPE* work_v_buf, __UNIT_TYPE work_v_buf_count, __UNIT_TYPE* q_buf, __UNIT_TYPE q_buf_count, __UNIT_TYPE* r_buf, __UNIT_TYPE r_buf_count)
    {
#ifdef _DEBUG
        if (u_buf_count > 0 && u_buf[u_buf_count - 1] == 0)
            throw InternalErrorException(L"予期しないルートに到達しました。", L"pmc_divrem.cpp;DivRem;1");
        if (v_buf_count > 0 && v_buf[v_buf_count - 1] == 0)
            throw InternalErrorException(L"予期しないルートに到達しました。", L"pmc_divrem.cpp;DivRem;2");
#endif
        if (v_buf_count == 0)
            throw DivisionByZeroException(L"0による除算が行われようとしました。");
        else if (v_buf_count == 1)
        {
            if (u_buf_count == 0)
            {
                _ZERO_MEMORY_UNIT(q_buf, q_buf_count);
                _ZERO_MEMORY_UNIT(r_buf, r_buf_count);
            }
            else if (u_buf_count == 1)
            {
#ifdef _DEBUG
                if (q_buf_count < 1)
                    throw InternalErrorException(L"予期しないルートに到達しました。", L"pmc_divrem.cpp;DivRem;1");
                if (r_buf_count < 1)
                    throw InternalErrorException(L"予期しないルートに到達しました。", L"pmc_divrem.cpp;DivRem;2");
#endif
                __UNIT_TYPE r;
                __UNIT_TYPE q = _DIVREM_UNIT(0, u_buf[0], v_buf[0], &r);
#ifdef ENABLED_PERFORMANCE_COUNTER
                if (sizeof(r) == sizeof(_UINT64_T))
                    IncrementDIV64Counter();
                else
                    IncrementDIV32Counter();
#endif
                q_buf[0] = q;
                _ZERO_MEMORY_UNIT(q_buf + 1, q_buf_count - 1);
                r_buf[0] = r;
                _ZERO_MEMORY_UNIT(r_buf + 1, r_buf_count - 1);
            }
            else
            {
#ifdef _DEBUG
                if (r_buf_count < 1)
                    throw InternalErrorException(L"予期しないルートに到達しました。", L"pmc_divrem.cpp;DivRem;3");
#endif
                r_buf[0] = NDivRem::DivRem_UX_1W(u_buf, u_buf_count, v_buf[0], q_buf, q_buf_count);
                _ZERO_MEMORY_UNIT(r_buf + 1, r_buf_count - 1);
            }
        }
        else
        {
            if (u_buf_count < v_buf_count)
            {
                _ZERO_MEMORY_UNIT(q_buf, q_buf_count);
                _COPY_MEMORY_UNIT(r_buf, u_buf, u_buf_count);
                _ZERO_MEMORY_UNIT(r_buf + u_buf_count, r_buf_count - u_buf_count);
            }
            else
                NDivRem::DivRem_UX_UX(u_buf, u_buf_count, v_buf, v_buf_count, work_v_buf, work_v_buf_count, q_buf, q_buf_count, r_buf, r_buf_count);
        }
    }

    void Remainder(__UNIT_TYPE* u_buf, __UNIT_TYPE u_buf_count, __UNIT_TYPE v, __UNIT_TYPE& r)
    {
#ifdef _DEBUG
        if (u_buf_count > 0 && u_buf[u_buf_count - 1] == 0)
            throw InternalErrorException(L"予期しないルートに到達しました。", L"pmc_divrem.cpp;Remainder;1");
#endif
        if (v == 0)
            throw DivisionByZeroException(L"0による除算が行われようとしました。");
        else if (u_buf_count == 0)
            r = 0;
        else if (u_buf_count == 1)
        {
            _DIVREM_UNIT(0, u_buf[0], v, &r);
#ifdef ENABLED_PERFORMANCE_COUNTER
            if (sizeof(r) == sizeof(_UINT64_T))
                IncrementDIV64Counter();
            else
                IncrementDIV32Counter();
#endif
        }
        else
            r = NDivRem::Rem_UX_1W(u_buf, u_buf_count, v);
    }

    void Remainder(__UNIT_TYPE u, __UNIT_TYPE* v_buf, __UNIT_TYPE v_buf_count, __UNIT_TYPE& r)
    {
#ifdef _DEBUG
        if (v_buf_count > 0 && v_buf[v_buf_count - 1] == 0)
            throw InternalErrorException(L"予期しないルートに到達しました。", L"pmc_divrem.cpp;Remainder;1");
#endif
        if (v_buf_count == 0)
            throw DivisionByZeroException(L"0による除算が行われようとしました。");
        else if (u == 0 || 1 < v_buf_count)
            r = u;
        else
        {
            _DIVREM_UNIT(0, u, v_buf[0], &r);
#ifdef ENABLED_PERFORMANCE_COUNTER
            if (sizeof(r) == sizeof(_UINT64_T))
                IncrementDIV64Counter();
            else
                IncrementDIV32Counter();
#endif
        }
    }

    // u_buf %= v_buf
    void Remainder(__UNIT_TYPE* u_buf, __UNIT_TYPE u_buf_count, __UNIT_TYPE* v_buf, __UNIT_TYPE v_buf_count)
    {
#ifdef _DEBUG
        if (v_buf_count > 0 && (v_buf[v_buf_count - 1] & ~((__UNIT_TYPE)-1 >> 1)) == 0)
            throw InternalErrorException(L"予期しないルートに到達しました。", L"pmc_divrem.cpp;Remainder;1");
#endif
        if (v_buf_count == 0)
            throw DivisionByZeroException(L"0による除算が行われようとしました。");
        else if (v_buf_count == 1)
        {
            if (u_buf_count == 0)
            {
                // nop
            }
            else if (u_buf_count == 1)
            {
                __UNIT_TYPE r;
                _DIVREM_UNIT(0, u_buf[0], v_buf[0], &r);
#ifdef ENABLED_PERFORMANCE_COUNTER
                if (sizeof(r) == sizeof(_UINT64_T))
                    IncrementDIV64Counter();
                else
                    IncrementDIV32Counter();
#endif
                u_buf[0] = r;
                _ZERO_MEMORY_UNIT(u_buf + 1, u_buf_count - 1);
            }
            else
            {
                u_buf[0] = NDivRem::Rem_UX_1W(u_buf, u_buf_count, v_buf[0]);
                _ZERO_MEMORY_UNIT(u_buf + 1, u_buf_count - 1);
            }
        }
        else
        {
            if (u_buf_count < v_buf_count)
            {
                // nop
            }
            else
                NDivRem::DivRem_UX_UX(u_buf, u_buf_count, v_buf, v_buf_count, nullptr, 0);
        }
    }

    // q_buf = u_buf / v_buf
    void Remainder(__UNIT_TYPE* u_buf, __UNIT_TYPE u_buf_count, __UNIT_TYPE* v_buf, __UNIT_TYPE v_buf_count, __UNIT_TYPE* work_v_buf, __UNIT_TYPE work_v_buf_count, __UNIT_TYPE* r_buf, __UNIT_TYPE r_buf_count)
    {
#ifdef _DEBUG
        if (u_buf_count > 0 && u_buf[u_buf_count - 1] == 0)
            throw InternalErrorException(L"予期しないルートに到達しました。", L"pmc_divrem.cpp;Remainder;1");
        if (v_buf_count > 0 && v_buf[v_buf_count - 1] == 0)
            throw InternalErrorException(L"予期しないルートに到達しました。", L"pmc_divrem.cpp;Remainder;2");
#endif
        if (v_buf_count == 0)
            throw DivisionByZeroException(L"0による除算が行われようとしました。");
        else if (v_buf_count == 1)
        {
            if (u_buf_count == 0)
                _ZERO_MEMORY_UNIT(r_buf, r_buf_count);
            else if (u_buf_count == 1)
            {
#ifdef _DEBUG
                if (r_buf_count < 1)
                    throw InternalErrorException(L"予期しないルートに到達しました。", L"pmc_divrem.cpp;Remainder;2");
#endif
                __UNIT_TYPE r;
                __UNIT_TYPE q = _DIVREM_UNIT(0, u_buf[0], v_buf[0], &r);
#ifdef ENABLED_PERFORMANCE_COUNTER
                if (sizeof(r) == sizeof(_UINT64_T))
                    IncrementDIV64Counter();
                else
                    IncrementDIV32Counter();
#endif
                r_buf[0] = r;
                _ZERO_MEMORY_UNIT(r_buf + 1, r_buf_count - 1);
            }
            else
            {
#ifdef _DEBUG
                if (r_buf_count < 1)
                    throw InternalErrorException(L"予期しないルートに到達しました。", L"pmc_divrem.cpp;Remainder;3");
#endif
                r_buf[0] = NDivRem::Rem_UX_1W(u_buf, u_buf_count, v_buf[0]);
                _ZERO_MEMORY_UNIT(r_buf + 1, r_buf_count - 1);
            }
        }
        else
        {
            if (u_buf_count < v_buf_count)
            {
                _COPY_MEMORY_UNIT(r_buf, u_buf, u_buf_count);
                _ZERO_MEMORY_UNIT(r_buf + u_buf_count, r_buf_count - u_buf_count);
            }
            else
                NDivRem::DivRem_UX_UX(u_buf, u_buf_count, v_buf, v_buf_count, work_v_buf, work_v_buf_count, nullptr, 0, r_buf, r_buf_count);
        }
    }

}


/*
 * END OF FILE
 */