Skip to content

hex_big_integer_division.hpp

SECTIONMath INCLUDEnoya/hex_big_integer_division.hpp

Divide nonnegative hexadecimal integers stored in 32-bit limbs. Karatsuba supplies subquadratic multiplication, while Burnikel-Ziegler splits a normalized 2n-by-n division into balanced 3n/2-by-n and n-by-n/2 subproblems; short blocks fall back to Knuth-style long division.

Verified by division_of_hex_big_integers.

\[ \displaystyle A = BQ + R,\; 0\le R < B \]

Implementation

View on GitHub

#ifndef NOYA_HEX_BIG_INTEGER_DIVISION_HPP
#define NOYA_HEX_BIG_INTEGER_DIVISION_HPP 1

/// @complexity Time: O(n^(log_2 3)) for n hexadecimal digits.
/// Space: O(n log n).

#include <algorithm>
#include <cassert>
#include <cmath>
#include <cstdint>
#include <iostream>
#include <string>
#include <string_view>
#include <tuple>
#include <type_traits>
#include <utility>
#include <vector>

namespace noya {
namespace hex_big_integer_division_internal {

using namespace std;

struct BigInteger
{
  using M = BigInteger;

  bool neg;
  vector<uint32_t> bits;
  static constexpr int logD = 8;

  BigInteger() : neg(false), bits() {}

  BigInteger(bool n, const vector<uint32_t> &d) : neg(n), bits(d) {}

  BigInteger(uint32_t x) : neg(false)
  {
    bits = _integer_to_vector(x);
  }

  BigInteger(int32_t x) : neg(false)
  {
    if (x < 0)
      neg = true, x = -x;
    bits = _integer_to_vector((uint32_t)x);
  }

  template <typename I, enable_if_t<is_integral_v<I> ||
                                    is_same_v<I, __int128_t>> * = nullptr>
  BigInteger(I x) : neg(false)
  {
    if constexpr (is_signed_v<I> || is_same_v<I, __int128_t>)
    {
      if (x < 0)
        neg = true, x = -x;
    }
    bits = _integer_to_vector(x);
  }

  BigInteger(const string &S) : neg(false)
  {
    assert(!S.empty());
    if (S.size() == 1u && S[0] == '0')
      return;
    int l = 0;
    if (S[0] == '-')
      ++l, neg = true;

    for (int ie = S.size(); l < ie; ie -= logD)
    {
      int is = max(l, ie - logD);
      uint32_t x = 0;
      for (int i = is; i < ie; i++)
      {
        x <<= 4;
        if ('0' <= S[i] and S[i] <= '9')
        {
          x |= S[i] - '0';
        }
        else if ('A' <= S[i] and S[i] <= 'F')
        {
          x |= S[i] - 'A' + 10;
        }
        else if ('a' <= S[i] and S[i] <= 'f')
        {
          x |= S[i] - 'a' + 10;
        }
        else
        {
          assert(false);
        }
      }
      bits.push_back(x);
    }
  }

  friend M operator+(const M &lhs, const M &rhs)
  {
    if (lhs.neg == rhs.neg)
      return {lhs.neg, _add(lhs.bits, rhs.bits)};
    if (_leq(lhs.bits, rhs.bits))
    {
      // |l| <= |r|
      auto c = _sub(rhs.bits, lhs.bits);
      bool n = _is_zero(c) ? false : rhs.neg;
      return {n, c};
    }
    auto c = _sub(lhs.bits, rhs.bits);
    bool n = _is_zero(c) ? false : lhs.neg;
    return {n, c};
  }
  friend M operator-(const M &lhs, const M &rhs) { return lhs + (-rhs); }

  friend M operator*(const M &lhs, const M &rhs)
  {
    auto c = _mul(lhs.bits, rhs.bits);
    bool n = _is_zero(c) ? false : (lhs.neg ^ rhs.neg);
    return {n, c};
  }
  friend pair<M, M> divmod(const M &lhs, const M &rhs)
  {
    auto dm = _divmod(lhs.bits, rhs.bits);
    bool dn = _is_zero(dm.first) ? false : lhs.neg != rhs.neg;
    bool mn = _is_zero(dm.second) ? false : lhs.neg;
    return {M{dn, dm.first}, M{mn, dm.second}};
  }
  friend M operator/(const M &lhs, const M &rhs)
  {
    return divmod(lhs, rhs).first;
  }
  friend M operator%(const M &lhs, const M &rhs)
  {
    return divmod(lhs, rhs).second;
  }

  M &operator+=(const M &rhs) { return (*this) = (*this) + rhs; }
  M &operator-=(const M &rhs) { return (*this) = (*this) - rhs; }
  M &operator*=(const M &rhs) { return (*this) = (*this) * rhs; }
  M &operator/=(const M &rhs) { return (*this) = (*this) / rhs; }
  M &operator%=(const M &rhs) { return (*this) = (*this) % rhs; }

  M operator-() const
  {
    if (is_zero())
      return *this;
    return {!neg, bits};
  }
  M operator+() const { return *this; }
  friend M abs(const M &m) { return {false, m.bits}; }
  bool is_zero() const { return _is_zero(bits); }

  friend bool operator==(const M &lhs, const M &rhs)
  {
    return lhs.neg == rhs.neg && lhs.bits == rhs.bits;
  }
  friend bool operator!=(const M &lhs, const M &rhs)
  {
    return lhs.neg != rhs.neg || lhs.bits != rhs.bits;
  }
  friend bool operator<(const M &lhs, const M &rhs)
  {
    if (lhs == rhs)
      return false;
    return _neq_lt(lhs, rhs);
  }
  friend bool operator<=(const M &lhs, const M &rhs)
  {
    if (lhs == rhs)
      return true;
    return _neq_lt(lhs, rhs);
  }
  friend bool operator>(const M &lhs, const M &rhs)
  {
    if (lhs == rhs)
      return false;
    return _neq_lt(rhs, lhs);
  }
  friend bool operator>=(const M &lhs, const M &rhs)
  {
    if (lhs == rhs)
      return true;
    return _neq_lt(rhs, lhs);
  }

  string to_string() const
  {
    if (is_zero())
      return "0";
    string res;
    if (neg)
      res.push_back('-');
    for (int i = _size() - 1; i >= 0; i--)
    {
      res += _itos(bits[i], i != _size() - 1);
    }
    return res;
  }

  friend istream &operator>>(istream &is, M &m)
  {
    string s;
    is >> s;
    m = M{s};
    return is;
  }

  friend ostream &operator<<(ostream &os, const M &m)
  {
    return os << m.to_string();
  }

private:
  // size
  int _size() const { return bits.size(); }
  // a == b
  static bool _eq(const vector<uint32_t> &a, const vector<uint32_t> &b) { return a == b; }
  // a < b
  static bool _lt(const vector<uint32_t> &a, const vector<uint32_t> &b)
  {
    return _lt(a.cbegin(), a.cend(), b.cbegin(), b.cend());
  }
  // a <= b
  static bool _leq(const vector<uint32_t> &a, const vector<uint32_t> &b)
  {
    return _eq(a, b) || _lt(a, b);
  }
  // a < b (s.t. a != b)
  static bool _neq_lt(const M &lhs, const M &rhs)
  {
    assert(lhs != rhs);
    if (lhs.neg != rhs.neg)
      return lhs.neg;
    bool f = _lt(lhs.bits, rhs.bits);
    if (f)
      return !lhs.neg;
    return lhs.neg;
  }
  // a == 0
  static bool _is_zero(const vector<uint32_t> &a) { return a.empty(); }
  // a == 1
  static bool _is_one(const vector<uint32_t> &a)
  {
    return (int)a.size() == 1 && a[0] == 1;
  }
  // 末尾 0 を削除
  static void _shrink(vector<uint32_t> &a)
  {
    while (a.size() && a.back() == 0)
      a.pop_back();
  }
  // 末尾 0 を削除
  void _shrink()
  {
    while (_size() && bits.back() == 0)
      bits.pop_back();
  }
  // a + b
  static vector<uint32_t> _add(const vector<uint32_t> &a, const vector<uint32_t> &b)
  {
    vector<uint32_t> c(max<int>(a.size(), b.size()) + 1);
    _add(
        a.cbegin(), a.cend(),
        b.cbegin(), b.cend(),
        c.begin(), c.end());
    _shrink(c);
    return c;
  }
  // a - b
  static vector<uint32_t> _sub(const vector<uint32_t> &a, const vector<uint32_t> &b)
  {
    assert(_leq(b, a));
    vector<uint32_t> c{a};
    _sub(
        c.begin(), c.end(),
        b.cbegin(), b.cend());
    _shrink(c);
    return c;
  }

  // a * b
  static vector<uint32_t> _mul(const vector<uint32_t> &a, const vector<uint32_t> &b)
  {
    if (_is_zero(a) || _is_zero(b))
      return {};
    if (_is_one(a))
      return b;
    if (_is_one(b))
      return a;

    vector<uint32_t> c(a.size() + b.size());
    _mul(
        a.cbegin(), a.cend(),
        b.cbegin(), b.cend(),
        c.begin(), c.end());
    _shrink(c);
    return c;
  }

  // a / b
  static pair<vector<uint32_t>, vector<uint32_t>> _divmod(const vector<uint32_t> &a, const vector<uint32_t> &b)
  {
    if (_is_zero(b))
    {
      cerr << "Divide by Zero Exception" << endl;
      exit(1);
    }

    if (a.size() < b.size())
    {
      return {{}, a};
    }

    vector<uint32_t> q(a.size() - b.size() + 1);
    vector<uint32_t> r(b.size());

    _divmod(
        a.cbegin(), a.cend(),
        b.cbegin(), b.cend(),
        q.begin(), q.end(),
        r.begin(), r.end());
    _shrink(q);
    _shrink(r);
    return {q, r};
  }

  using iter = typename vector<uint32_t>::iterator;
  using citer = typename vector<uint32_t>::const_iterator;

  // a + b
  static void _add(
      citer a, citer a_end,
      citer b, citer b_end,
      iter c, iter c_end)
  {
    if (a_end - a < b_end - b)
    {
      swap(a, b);
      swap(a_end, b_end);
    }
    const size_t asize = a_end - a;
    const size_t bsize = b_end - b;
    const size_t csize = c_end - c;
    assert(asize <= csize);

    uint32_t carry = 0;
    for (size_t i = 0; i < bsize; i++)
    {
      uint64_t v = (uint64_t)a[i] + b[i] + carry;
      c[i] = (uint32_t)v;
      carry = v >> 32;
    }
    for (size_t i = bsize; i < asize; i++)
    {
      uint64_t v = (uint64_t)a[i] + carry;
      c[i] = (uint32_t)v;
      carry = v >> 32;
    }
    if (carry != 0)
    {
      assert(c + asize < c_end);
      c[asize] = carry;
    }
  }

  // a += b
  static void _add(
      iter a, iter a_end,
      citer b, citer b_end)
  {
    const size_t asize = a_end - a;
    const size_t bsize = b_end - b;

    uint32_t carry = 0;
    for (size_t i = 0; i < bsize; i++)
    {
      uint64_t v = (uint64_t)a[i] + b[i] + carry;
      a[i] = (uint32_t)v;
      carry = v >> 32;
    }
    for (size_t i = bsize; carry != 0 && i < asize; i++)
    {
      uint64_t v = (uint64_t)a[i] + carry;
      a[i] = (uint32_t)v;
      carry = v >> 32;
    }
  }
  // a -= b
  static void _sub(
      iter a, iter a_end,
      citer b, citer b_end)
  {
    const size_t asize = a_end - a;
    const size_t bsize = b_end - b;

    int32_t carry = 0;
    for (size_t i = 0; i < bsize; i++)
    {
      int64_t v = (int64_t)a[i] - b[i] + carry;
      a[i] = v;
      carry = v >> 32;
    }
    for (size_t i = bsize; carry != 0 && i < asize; i++)
    {
      int64_t v = (int64_t)a[i] + carry;
      a[i] = v;
      carry = v >> 32;
    }
    assert(carry == 0);
  }

  // a * b
  static void _mul(
      citer a, citer a_end,
      citer b, citer b_end,
      iter c, iter c_end)
  {
    if (a_end - a < b_end - b)
    {
      swap(a, b);
      swap(a_end, b_end);
    }
    const size_t asize = a_end - a;
    const size_t bsize = b_end - b;
    const size_t csize = c_end - c;
    assert(csize == asize + bsize);

    if (bsize <= 128)
    {
      _mul_naive(
          a, a_end,
          b, b_end,
          c, c_end);
      return;
    }
    // Karatsuba reduces four half-size products to three.
    // |  A_hi  |  A_lo  |
    // |  B_hi  |  B_lo  |

    // z0 := A_lo * B_lo
    // z2 := A_hi * B_hi
    // z1 := A_hi * B_lo + A_lo * B_hi

    // z1 = (A_hi+A_lo) * (B_hi+B_lo) - z0 - z2

    // A * B = (z2<<(2*shift)) + (z1<<shift) + z0

    const size_t n = (asize + 1) >> 1;
    if (bsize <= n)
    {
      // |  A_hi  |  A_lo  |
      // |   0    |   B    |

      // A_lo * B
      _mul(
          a, a + n,
          b, b_end,
          c, c + n + bsize);

      vector<uint32_t> carry(c + n, c + n + bsize);
      fill(c + n, c + n + bsize, 0);

      // A_hi * B
      _mul(
          a + n, a_end,
          b, b_end,
          c + n, c_end);

      _add(c + n, c_end,
           carry.cbegin(), carry.cend());
    }
    else
    {
      // A_lo * B_lo
      _mul(
          a, a + n,
          b, b + n,
          c, c + n + n);

      // A_hi * B_hi
      _mul(
          a + n, a_end,
          b + n, b_end,
          c + n + n, c_end);

      vector<uint32_t> a1(n + 1);
      vector<uint32_t> b1(n + 1);
      vector<uint32_t> z1(2 * n + 2);
      _add(
          a, a + n,
          a + n, a_end,
          a1.begin(), a1.end());
      _add(
          b, b + n,
          b + n, b_end,
          b1.begin(), b1.end());
      _mul(
          a1.cbegin(), a1.cend(),
          b1.cbegin(), b1.cend(),
          z1.begin(), z1.end());

      _sub(
          z1.begin(), z1.end(),
          c, c + n + n);
      _sub(
          z1.begin(), z1.end(),
          c + n + n, c_end);

      _shrink(z1);
      _add(c + n, c_end,
           z1.begin(), z1.end());
    }
  }

  // a * b (naive)
  static void _mul_naive(
      citer a, citer a_end,
      citer b, citer b_end,
      iter c, iter c_end)
  {
    const size_t asize = a_end - a;
    const size_t bsize = b_end - b;
    const size_t csize = c_end - c;
    if (asize == 0 || bsize == 0)
      return;
    assert(csize == asize + bsize);

    for (size_t i = 0; i < asize; i++)
    {
      uint32_t carry = 0;
      for (size_t j = 0; j < bsize; j++)
      {
        uint64_t p = 1LL * a[i] * b[j] + carry + c[i + j];
        c[i + j] = p;
        carry = p >> 32;
      }
      c[i + bsize] = carry;
    }
  }

  static const int _div_naive_th = 64;

  // a / b
  static void _divmod(
      citer a, citer a_end,
      citer b, citer b_end,
      iter quo, iter quo_end,
      iter rem, iter rem_end)
  {
    const size_t asize = a_end - a;
    const size_t bsize = b_end - b;
    const size_t qsize = quo_end - quo;
    const size_t rsize = rem_end - rem;

    assert(bsize > 0);
    assert(qsize == asize - bsize + 1);
    assert(rsize == bsize);

    if (min(asize - bsize, bsize) <= _div_naive_th)
    {
      _divmod_naive(
          a, a_end,
          b, b_end,
          quo, quo_end,
          rem, rem_end);
      return;
    }

    // Round the divisor length to balanced blocks before recursive division.

    size_t n;
    {
      size_t m = (bsize + _div_naive_th - 1) / _div_naive_th;
      if (m > 1)
        m = 1 << (32 - __builtin_clz(m - 1));

      size_t j = (bsize + m - 1) / m;
      n = j * m;
    }

    const int sigmaDigit = n - bsize;
    const int sigmaShift = __builtin_clz(*(b_end - 1));

    vector<uint32_t> x(asize + sigmaDigit + (__builtin_clz(*(a_end - 1)) <= sigmaShift ? 1 : 0));
    vector<uint32_t> y(n);
    vector<uint32_t> r(n + 1);
    vector<uint32_t> z(2 * n);

    copy(a, a_end, x.begin() + sigmaDigit);
    copy(b, b_end, y.begin() + sigmaDigit);

    _left_shift(x.begin() + sigmaDigit, x.end(), sigmaShift);
    _left_shift(y.begin() + sigmaDigit, y.end(), sigmaShift);

    size_t t = max<size_t>(2, (x.size() + n - 1) / n);
    copy(x.cbegin() + (t - 2) * n, x.cend(), z.begin());

    const size_t quo_upper_len = quo_end - (quo + (t - 2) * n);
    if (quo_upper_len < n)
    {
      vector<uint32_t> qq(n);
      _divmod_d2n1n(
          z.begin(), z.end(),
          y.begin(), y.end(),
          qq.begin(), qq.end(),
          r.begin(), r.end());
      copy(qq.cbegin(), qq.cbegin() + quo_upper_len, quo + (t - 2) * n);
    }
    else
    {
      _divmod_d2n1n(
          z.begin(), z.end(),
          y.begin(), y.end(),
          quo + (t - 2) * n, quo + (t - 1) * n,
          r.begin(), r.end());
    }

    for (int i = t - 3; i >= 0; --i)
    {
      copy(x.begin() + i * n, x.begin() + (i + 1) * n, z.begin());
      copy(r.begin(), r.begin() + n, z.begin() + n);
      fill(r.begin(), r.end(), 0);

      _divmod_d2n1n(
          z.begin(), z.end(),
          y.begin(), y.end(),
          quo + i * n, quo + (i + 1) * n,
          r.begin(), r.end());
    }

    _shrink(r);
    copy(r.cbegin() + sigmaDigit, r.cbegin() + sigmaDigit + rsize, rem);
    _right_shift(rem, rem_end, sigmaShift);
  }

  // a(2n bits) / b(n bits)
  static void _divmod_d2n1n(
      citer a, citer a_end,
      citer b, citer b_end,
      iter quo, iter quo_end,
      iter rem, iter rem_end)
  {
    const size_t asize = a_end - a;
    const size_t bsize = b_end - b;
    const size_t qsize = quo_end - quo;
    const size_t rsize = rem_end - rem;
    const size_t n = bsize;

    assert(asize == 2 * n);
    assert(qsize == n);
    assert(rsize == n + 1);
    assert(_lt(a + n, a_end, b, b_end));

    if (n % 2 != 0 || n <= _div_naive_th)
    {
      _divmod_naive(
          a, a_end,
          b, b_end,
          quo, quo_end,
          rem, rem_end);
      return;
    }
    const size_t half = n >> 1;
    vector<uint32_t> r1(n + half + 1);
    copy(a, a + half, r1.begin());

    _divmod_d3n2n(
        a + half, a_end,
        b, b_end,
        quo + half, quo_end,
        r1.begin() + half, r1.end());
    _divmod_d3n2n(
        r1.cbegin(), r1.cend() - 1,
        b, b_end,
        quo, quo + half,
        rem, rem_end);
  }

  // a(3n bits) / b(2n bits)
  static void _divmod_d3n2n(
      citer a, citer a_end,
      citer b, citer b_end,
      iter quo, iter quo_end,
      iter rem, iter rem_end)
  {
    const size_t asize = a_end - a;
    const size_t bsize = b_end - b;
    const size_t qsize = quo_end - quo;
    const size_t rsize = rem_end - rem;
    const size_t n = bsize >> 1;

    assert(asize == 3 * n);
    assert(bsize == 2 * n);
    assert(qsize == n);
    assert(rsize == 2 * n + 1);
    assert(_lt(a + n, a_end, b, b_end));

    vector<uint32_t> d(2 * n);

    if (_lt(a + n + n, a_end, b + n, b_end))
    {
      _divmod_d2n1n(
          a + n, a_end,
          b + n, b_end,
          quo, quo_end,
          rem + n, rem_end);
      _mul(
          quo, quo_end,
          b, b + n,
          d.begin(), d.end());
    }
    else
    {
      fill(quo, quo_end, UINT32_MAX);

      _add(
          a + n, a + n + n,
          b + n, b_end,
          rem + n, rem_end);
      copy(b, b + n, d.begin() + n);
      _sub(d.begin(), d.end(), b, b + n);
    }

    copy(a, a + n, rem);

    while (_lt(rem, rem_end, d.cbegin(), d.cend()))
    {
      _add(rem, rem_end, b, b_end);
      vector<uint32_t> one{1};
      _sub(quo, quo_end, one.cbegin(), one.cend());
    }
    _sub(rem, rem_end, d.cbegin(), d.cend());
  }

  // a / b (naive)
  static void _divmod_naive(
      citer a, citer a_end,
      citer b, citer b_end,
      iter quo, iter quo_end,
      iter rem, iter rem_end)
  {
    const size_t asize = a_end - a;
    const size_t bsize = b_end - b;
    if (bsize == 0)
    {
      cerr << "Divide by Zero Exception" << endl;
      exit(1);
    }

    if (bsize == 1)
    {
      uint64_t carry = 0;
      for (size_t i = asize - 1; 0 <= (int)i; --i)
      {
        uint64_t v = (carry << 32) | a[i];
        quo[i] = v / (*b);
        carry = v - quo[i] * (*b);
      }
      *rem = (uint32_t)carry;
      return;
    }
    if (max(asize, bsize) <= 2)
    {
      uint64_t a64 = _iter_to_integer<uint64_t>(a, a_end), b64 = _iter_to_integer<uint64_t>(b, b_end);
      _integer_to_iter(a64 / b64, quo, quo_end);
      _integer_to_iter(a64 % b64, rem, rem_end);
      return;
    }
    if (_lt(a, a_end, b, b_end))
    {
      if (asize > bsize)
        a_end = a + bsize;
      copy(a, a_end, rem);
      return;
    }
    // B > 0xFFFFFFFF, A >= B

    // 割る数のビットを大きくする
    const int shift = __builtin_clz(*(b_end - 1));
    vector<uint32_t> x(asize + (__builtin_clz(*(a_end - 1)) < shift ? 1 : 0));
    copy(a, a_end, x.begin());
    vector<uint32_t> y(b, b_end);

    _left_shift(x.begin(), x.end(), shift);
    _left_shift(y.begin(), y.end(), shift);

    uint32_t yb = y.back();
    vector<uint32_t> qv(x.size() - y.size() + 1);
    vector<uint32_t> rv(x.end() - y.size(), x.end());
    for (int i = qv.size() - 1; i >= 0; i--)
    {
      if (rv.size() < y.size())
      {
        // do nothing
      }
      else if (rv.size() == y.size())
      {
        if (_leq(y, rv))
        {
          qv[i] = 1, rv = _sub(rv, y);
        }
      }
      else
      {
        assert(y.size() + 1 == rv.size());
        uint64_t rb = rv[rv.size() - 1];
        rb <<= 32;
        rb |= rv[rv.size() - 2];
        uint64_t q = rb / yb;
        if (q > UINT32_MAX)
          q = UINT32_MAX;

        vector<uint32_t> yq = _mul(y, {(uint32_t)q});
        // 真の商は q-2 以上 q+1 以下だが自信が無いので念のため while を回す
        while (_lt(rv, yq))
          q--, yq = _sub(yq, y);
        rv = _sub(rv, yq);
        while (_leq(y, rv))
          q++, rv = _sub(rv, y);
        qv[i] = q;
      }
      if (i)
        rv.insert(begin(rv), x[i - 1]);
      _shrink(rv);
    }

    _shrink(qv);
    _right_shift(rv.begin(), rv.end(), shift);
    _shrink(rv);

    copy(qv.cbegin(), qv.cend(), quo);
    copy(rv.cbegin(), rv.cend(), rem);
  }

  // int -> string
  // 先頭かどうかに応じて zero padding するかを決める
  static string _itos(uint32_t x, bool zero_padding)
  {
    string res;
    for (int i = 0; i < logD; i++)
    {
      uint32_t d = x % 16;
      if (d < 10)
        res.push_back('0' + d);
      else if (d < 16)
        res.push_back('A' + d - 10);
      else
        assert(false);
      x /= 16;
    }
    if (!zero_padding)
    {
      while (res.size() && res.back() == '0')
        res.pop_back();
      assert(!res.empty());
    }
    reverse(begin(res), end(res));
    return res;
  }

  template <typename I, enable_if_t<is_integral_v<I> ||
                                    is_same_v<I, __int128_t>> * = nullptr>
  static I _iter_to_integer(citer it, const citer end)
  {
    I res = 0;
    int shift = 0;
    while (it != end)
    {
      res |= (I)*it << shift;
      shift += 32;
      ++it;
    }
    return res;
  }

  template <typename I, enable_if_t<is_integral_v<I> ||
                                    is_same_v<I, __int128_t>> * = nullptr>
  static void _integer_to_iter(I x, iter it, const iter end)
  {
    if constexpr (is_signed_v<I> || is_same_v<I, __int128_t>)
    {
      assert(x >= 0);
    }
    while (x)
    {
      assert(it < end);
      *it = (uint32_t)x;
      ++it;
      x >>= 32;
    }
  }

  static vector<uint32_t> _integer_to_vector(uint32_t x)
  {
    return vector<uint32_t>{x};
  }

  template <typename I, enable_if_t<is_integral_v<I> ||
                                    is_same_v<I, __int128_t>> * = nullptr>
  static vector<uint32_t> _integer_to_vector(I x)
  {
    if constexpr (is_signed_v<I> || is_same_v<I, __int128_t>)
    {
      assert(x >= 0);
    }
    vector<uint32_t> rt;
    while (x)
    {
      rt.push_back((uint32_t)x);
      x >>= 32;
    }
    return rt;
  }

  // a << shift
  static void _left_shift(const iter a, const iter a_end, int shift)
  {
    assert(shift < 32);
    if (shift == 0)
      return;
    const int back = 32 - shift;
    uint32_t carry = 0;
    for (iter it = a; it < a_end; ++it)
    {
      uint32_t v = (*it << shift) | carry;
      carry = *it >> back;
      *it = v;
    }
    assert(carry == 0);
  }
  // a >> shift
  static void _right_shift(const iter a, const iter a_end, int shift)
  {
    assert(shift < 32);
    if (shift == 0)
      return;
    const int back = 32 - shift;
    uint32_t carry = 0;
    for (iter it = a_end - 1; a <= it; --it)
    {
      uint32_t v = (*it >> shift) | carry;
      carry = *it << back;
      *it = v;
    }
    assert(carry == 0);
  }

  // a < b
  static bool _lt(
      citer a, citer a_end,
      citer b, citer b_end)
  {
    size_t asize = a_end - a;
    size_t bsize = b_end - b;

    if (asize != bsize)
    {
      if (asize < bsize)
      {
        auto non_zero = std::find_if(b + asize, b_end, [](uint32_t x)
                                     { return x != 0; });
        if (non_zero != b_end)
          return true;

        b_end = b + asize;
        bsize = asize;
      }
      else
      {
        auto non_zero = std::find_if(a + bsize, a_end, [](uint32_t x)
                                     { return x != 0; });
        if (non_zero != a_end)
          return false;
        a_end = a + bsize;
        asize = bsize;
      }
    }
    for (int i = asize - 1; i >= 0; i--)
    {
      if (a[i] != b[i])
        return a[i] < b[i];
    }
    return false;
  }

  static void _dump(const vector<uint32_t> &a, string s = "")
  {
    _dump(a.cbegin(), a.cend(), s);
  }

  static void _dump(citer a, citer a_end, string s = "")
  {
    if (!s.empty())
      cerr << s << " : ";
    cerr << "{ ";
    for (citer it = a; it < a_end; ++it)
    {
      cerr << *it << ", ";
    }
    cerr << "}" << endl;
  }
};

using bigint = BigInteger;


} // namespace hex_big_integer_division_internal

/// @brief Divide nonnegative hexadecimal integers stored in 32-bit limbs.
/// Karatsuba supplies subquadratic multiplication, while Burnikel-Ziegler
/// splits a normalized 2n-by-n division into balanced 3n/2-by-n and
/// n-by-n/2 subproblems; short blocks fall back to Knuth-style long division.
inline std::pair<std::string, std::string>
divide_hex_big_integers(std::string_view dividend, std::string_view divisor) {
  using hex_big_integer_division_internal::bigint;
  bigint first{std::string(dividend)};
  bigint second{std::string(divisor)};
  auto [quotient, remainder] = divmod(first, second);
  return {quotient.to_string(), remainder.to_string()};
}

} // namespace noya

#endif // NOYA_HEX_BIG_INTEGER_DIVISION_HPP
#include <algorithm>
#include <cassert>
#include <cmath>
#include <cstdint>
#include <iostream>
#include <string>
#include <string_view>
#include <tuple>
#include <type_traits>
#include <utility>
#include <vector>

/// @complexity Time: O(n^(log_2 3)) for n hexadecimal digits.
/// Space: O(n log n).

namespace noya {
namespace hex_big_integer_division_internal {

using namespace std;

struct BigInteger
{
  using M = BigInteger;

  bool neg;
  vector<uint32_t> bits;
  static constexpr int logD = 8;

  BigInteger() : neg(false), bits() {}

  BigInteger(bool n, const vector<uint32_t> &d) : neg(n), bits(d) {}

  BigInteger(uint32_t x) : neg(false)
  {
    bits = _integer_to_vector(x);
  }

  BigInteger(int32_t x) : neg(false)
  {
    if (x < 0)
      neg = true, x = -x;
    bits = _integer_to_vector((uint32_t)x);
  }

  template <typename I, enable_if_t<is_integral_v<I> ||
                                    is_same_v<I, __int128_t>> * = nullptr>
  BigInteger(I x) : neg(false)
  {
    if constexpr (is_signed_v<I> || is_same_v<I, __int128_t>)
    {
      if (x < 0)
        neg = true, x = -x;
    }
    bits = _integer_to_vector(x);
  }

  BigInteger(const string &S) : neg(false)
  {
    assert(!S.empty());
    if (S.size() == 1u && S[0] == '0')
      return;
    int l = 0;
    if (S[0] == '-')
      ++l, neg = true;

    for (int ie = S.size(); l < ie; ie -= logD)
    {
      int is = max(l, ie - logD);
      uint32_t x = 0;
      for (int i = is; i < ie; i++)
      {
        x <<= 4;
        if ('0' <= S[i] and S[i] <= '9')
        {
          x |= S[i] - '0';
        }
        else if ('A' <= S[i] and S[i] <= 'F')
        {
          x |= S[i] - 'A' + 10;
        }
        else if ('a' <= S[i] and S[i] <= 'f')
        {
          x |= S[i] - 'a' + 10;
        }
        else
        {
          assert(false);
        }
      }
      bits.push_back(x);
    }
  }

  friend M operator+(const M &lhs, const M &rhs)
  {
    if (lhs.neg == rhs.neg)
      return {lhs.neg, _add(lhs.bits, rhs.bits)};
    if (_leq(lhs.bits, rhs.bits))
    {
      // |l| <= |r|
      auto c = _sub(rhs.bits, lhs.bits);
      bool n = _is_zero(c) ? false : rhs.neg;
      return {n, c};
    }
    auto c = _sub(lhs.bits, rhs.bits);
    bool n = _is_zero(c) ? false : lhs.neg;
    return {n, c};
  }
  friend M operator-(const M &lhs, const M &rhs) { return lhs + (-rhs); }

  friend M operator*(const M &lhs, const M &rhs)
  {
    auto c = _mul(lhs.bits, rhs.bits);
    bool n = _is_zero(c) ? false : (lhs.neg ^ rhs.neg);
    return {n, c};
  }
  friend pair<M, M> divmod(const M &lhs, const M &rhs)
  {
    auto dm = _divmod(lhs.bits, rhs.bits);
    bool dn = _is_zero(dm.first) ? false : lhs.neg != rhs.neg;
    bool mn = _is_zero(dm.second) ? false : lhs.neg;
    return {M{dn, dm.first}, M{mn, dm.second}};
  }
  friend M operator/(const M &lhs, const M &rhs)
  {
    return divmod(lhs, rhs).first;
  }
  friend M operator%(const M &lhs, const M &rhs)
  {
    return divmod(lhs, rhs).second;
  }

  M &operator+=(const M &rhs) { return (*this) = (*this) + rhs; }
  M &operator-=(const M &rhs) { return (*this) = (*this) - rhs; }
  M &operator*=(const M &rhs) { return (*this) = (*this) * rhs; }
  M &operator/=(const M &rhs) { return (*this) = (*this) / rhs; }
  M &operator%=(const M &rhs) { return (*this) = (*this) % rhs; }

  M operator-() const
  {
    if (is_zero())
      return *this;
    return {!neg, bits};
  }
  M operator+() const { return *this; }
  friend M abs(const M &m) { return {false, m.bits}; }
  bool is_zero() const { return _is_zero(bits); }

  friend bool operator==(const M &lhs, const M &rhs)
  {
    return lhs.neg == rhs.neg && lhs.bits == rhs.bits;
  }
  friend bool operator!=(const M &lhs, const M &rhs)
  {
    return lhs.neg != rhs.neg || lhs.bits != rhs.bits;
  }
  friend bool operator<(const M &lhs, const M &rhs)
  {
    if (lhs == rhs)
      return false;
    return _neq_lt(lhs, rhs);
  }
  friend bool operator<=(const M &lhs, const M &rhs)
  {
    if (lhs == rhs)
      return true;
    return _neq_lt(lhs, rhs);
  }
  friend bool operator>(const M &lhs, const M &rhs)
  {
    if (lhs == rhs)
      return false;
    return _neq_lt(rhs, lhs);
  }
  friend bool operator>=(const M &lhs, const M &rhs)
  {
    if (lhs == rhs)
      return true;
    return _neq_lt(rhs, lhs);
  }

  string to_string() const
  {
    if (is_zero())
      return "0";
    string res;
    if (neg)
      res.push_back('-');
    for (int i = _size() - 1; i >= 0; i--)
    {
      res += _itos(bits[i], i != _size() - 1);
    }
    return res;
  }

  friend istream &operator>>(istream &is, M &m)
  {
    string s;
    is >> s;
    m = M{s};
    return is;
  }

  friend ostream &operator<<(ostream &os, const M &m)
  {
    return os << m.to_string();
  }

private:
  // size
  int _size() const { return bits.size(); }
  // a == b
  static bool _eq(const vector<uint32_t> &a, const vector<uint32_t> &b) { return a == b; }
  // a < b
  static bool _lt(const vector<uint32_t> &a, const vector<uint32_t> &b)
  {
    return _lt(a.cbegin(), a.cend(), b.cbegin(), b.cend());
  }
  // a <= b
  static bool _leq(const vector<uint32_t> &a, const vector<uint32_t> &b)
  {
    return _eq(a, b) || _lt(a, b);
  }
  // a < b (s.t. a != b)
  static bool _neq_lt(const M &lhs, const M &rhs)
  {
    assert(lhs != rhs);
    if (lhs.neg != rhs.neg)
      return lhs.neg;
    bool f = _lt(lhs.bits, rhs.bits);
    if (f)
      return !lhs.neg;
    return lhs.neg;
  }
  // a == 0
  static bool _is_zero(const vector<uint32_t> &a) { return a.empty(); }
  // a == 1
  static bool _is_one(const vector<uint32_t> &a)
  {
    return (int)a.size() == 1 && a[0] == 1;
  }
  // 末尾 0 を削除
  static void _shrink(vector<uint32_t> &a)
  {
    while (a.size() && a.back() == 0)
      a.pop_back();
  }
  // 末尾 0 を削除
  void _shrink()
  {
    while (_size() && bits.back() == 0)
      bits.pop_back();
  }
  // a + b
  static vector<uint32_t> _add(const vector<uint32_t> &a, const vector<uint32_t> &b)
  {
    vector<uint32_t> c(max<int>(a.size(), b.size()) + 1);
    _add(
        a.cbegin(), a.cend(),
        b.cbegin(), b.cend(),
        c.begin(), c.end());
    _shrink(c);
    return c;
  }
  // a - b
  static vector<uint32_t> _sub(const vector<uint32_t> &a, const vector<uint32_t> &b)
  {
    assert(_leq(b, a));
    vector<uint32_t> c{a};
    _sub(
        c.begin(), c.end(),
        b.cbegin(), b.cend());
    _shrink(c);
    return c;
  }

  // a * b
  static vector<uint32_t> _mul(const vector<uint32_t> &a, const vector<uint32_t> &b)
  {
    if (_is_zero(a) || _is_zero(b))
      return {};
    if (_is_one(a))
      return b;
    if (_is_one(b))
      return a;

    vector<uint32_t> c(a.size() + b.size());
    _mul(
        a.cbegin(), a.cend(),
        b.cbegin(), b.cend(),
        c.begin(), c.end());
    _shrink(c);
    return c;
  }

  // a / b
  static pair<vector<uint32_t>, vector<uint32_t>> _divmod(const vector<uint32_t> &a, const vector<uint32_t> &b)
  {
    if (_is_zero(b))
    {
      cerr << "Divide by Zero Exception" << endl;
      exit(1);
    }

    if (a.size() < b.size())
    {
      return {{}, a};
    }

    vector<uint32_t> q(a.size() - b.size() + 1);
    vector<uint32_t> r(b.size());

    _divmod(
        a.cbegin(), a.cend(),
        b.cbegin(), b.cend(),
        q.begin(), q.end(),
        r.begin(), r.end());
    _shrink(q);
    _shrink(r);
    return {q, r};
  }

  using iter = typename vector<uint32_t>::iterator;
  using citer = typename vector<uint32_t>::const_iterator;

  // a + b
  static void _add(
      citer a, citer a_end,
      citer b, citer b_end,
      iter c, iter c_end)
  {
    if (a_end - a < b_end - b)
    {
      swap(a, b);
      swap(a_end, b_end);
    }
    const size_t asize = a_end - a;
    const size_t bsize = b_end - b;
    const size_t csize = c_end - c;
    assert(asize <= csize);

    uint32_t carry = 0;
    for (size_t i = 0; i < bsize; i++)
    {
      uint64_t v = (uint64_t)a[i] + b[i] + carry;
      c[i] = (uint32_t)v;
      carry = v >> 32;
    }
    for (size_t i = bsize; i < asize; i++)
    {
      uint64_t v = (uint64_t)a[i] + carry;
      c[i] = (uint32_t)v;
      carry = v >> 32;
    }
    if (carry != 0)
    {
      assert(c + asize < c_end);
      c[asize] = carry;
    }
  }

  // a += b
  static void _add(
      iter a, iter a_end,
      citer b, citer b_end)
  {
    const size_t asize = a_end - a;
    const size_t bsize = b_end - b;

    uint32_t carry = 0;
    for (size_t i = 0; i < bsize; i++)
    {
      uint64_t v = (uint64_t)a[i] + b[i] + carry;
      a[i] = (uint32_t)v;
      carry = v >> 32;
    }
    for (size_t i = bsize; carry != 0 && i < asize; i++)
    {
      uint64_t v = (uint64_t)a[i] + carry;
      a[i] = (uint32_t)v;
      carry = v >> 32;
    }
  }
  // a -= b
  static void _sub(
      iter a, iter a_end,
      citer b, citer b_end)
  {
    const size_t asize = a_end - a;
    const size_t bsize = b_end - b;

    int32_t carry = 0;
    for (size_t i = 0; i < bsize; i++)
    {
      int64_t v = (int64_t)a[i] - b[i] + carry;
      a[i] = v;
      carry = v >> 32;
    }
    for (size_t i = bsize; carry != 0 && i < asize; i++)
    {
      int64_t v = (int64_t)a[i] + carry;
      a[i] = v;
      carry = v >> 32;
    }
    assert(carry == 0);
  }

  // a * b
  static void _mul(
      citer a, citer a_end,
      citer b, citer b_end,
      iter c, iter c_end)
  {
    if (a_end - a < b_end - b)
    {
      swap(a, b);
      swap(a_end, b_end);
    }
    const size_t asize = a_end - a;
    const size_t bsize = b_end - b;
    const size_t csize = c_end - c;
    assert(csize == asize + bsize);

    if (bsize <= 128)
    {
      _mul_naive(
          a, a_end,
          b, b_end,
          c, c_end);
      return;
    }
    // Karatsuba reduces four half-size products to three.
    // |  A_hi  |  A_lo  |
    // |  B_hi  |  B_lo  |

    // z0 := A_lo * B_lo
    // z2 := A_hi * B_hi
    // z1 := A_hi * B_lo + A_lo * B_hi

    // z1 = (A_hi+A_lo) * (B_hi+B_lo) - z0 - z2

    // A * B = (z2<<(2*shift)) + (z1<<shift) + z0

    const size_t n = (asize + 1) >> 1;
    if (bsize <= n)
    {
      // |  A_hi  |  A_lo  |
      // |   0    |   B    |

      // A_lo * B
      _mul(
          a, a + n,
          b, b_end,
          c, c + n + bsize);

      vector<uint32_t> carry(c + n, c + n + bsize);
      fill(c + n, c + n + bsize, 0);

      // A_hi * B
      _mul(
          a + n, a_end,
          b, b_end,
          c + n, c_end);

      _add(c + n, c_end,
           carry.cbegin(), carry.cend());
    }
    else
    {
      // A_lo * B_lo
      _mul(
          a, a + n,
          b, b + n,
          c, c + n + n);

      // A_hi * B_hi
      _mul(
          a + n, a_end,
          b + n, b_end,
          c + n + n, c_end);

      vector<uint32_t> a1(n + 1);
      vector<uint32_t> b1(n + 1);
      vector<uint32_t> z1(2 * n + 2);
      _add(
          a, a + n,
          a + n, a_end,
          a1.begin(), a1.end());
      _add(
          b, b + n,
          b + n, b_end,
          b1.begin(), b1.end());
      _mul(
          a1.cbegin(), a1.cend(),
          b1.cbegin(), b1.cend(),
          z1.begin(), z1.end());

      _sub(
          z1.begin(), z1.end(),
          c, c + n + n);
      _sub(
          z1.begin(), z1.end(),
          c + n + n, c_end);

      _shrink(z1);
      _add(c + n, c_end,
           z1.begin(), z1.end());
    }
  }

  // a * b (naive)
  static void _mul_naive(
      citer a, citer a_end,
      citer b, citer b_end,
      iter c, iter c_end)
  {
    const size_t asize = a_end - a;
    const size_t bsize = b_end - b;
    const size_t csize = c_end - c;
    if (asize == 0 || bsize == 0)
      return;
    assert(csize == asize + bsize);

    for (size_t i = 0; i < asize; i++)
    {
      uint32_t carry = 0;
      for (size_t j = 0; j < bsize; j++)
      {
        uint64_t p = 1LL * a[i] * b[j] + carry + c[i + j];
        c[i + j] = p;
        carry = p >> 32;
      }
      c[i + bsize] = carry;
    }
  }

  static const int _div_naive_th = 64;

  // a / b
  static void _divmod(
      citer a, citer a_end,
      citer b, citer b_end,
      iter quo, iter quo_end,
      iter rem, iter rem_end)
  {
    const size_t asize = a_end - a;
    const size_t bsize = b_end - b;
    const size_t qsize = quo_end - quo;
    const size_t rsize = rem_end - rem;

    assert(bsize > 0);
    assert(qsize == asize - bsize + 1);
    assert(rsize == bsize);

    if (min(asize - bsize, bsize) <= _div_naive_th)
    {
      _divmod_naive(
          a, a_end,
          b, b_end,
          quo, quo_end,
          rem, rem_end);
      return;
    }

    // Round the divisor length to balanced blocks before recursive division.

    size_t n;
    {
      size_t m = (bsize + _div_naive_th - 1) / _div_naive_th;
      if (m > 1)
        m = 1 << (32 - __builtin_clz(m - 1));

      size_t j = (bsize + m - 1) / m;
      n = j * m;
    }

    const int sigmaDigit = n - bsize;
    const int sigmaShift = __builtin_clz(*(b_end - 1));

    vector<uint32_t> x(asize + sigmaDigit + (__builtin_clz(*(a_end - 1)) <= sigmaShift ? 1 : 0));
    vector<uint32_t> y(n);
    vector<uint32_t> r(n + 1);
    vector<uint32_t> z(2 * n);

    copy(a, a_end, x.begin() + sigmaDigit);
    copy(b, b_end, y.begin() + sigmaDigit);

    _left_shift(x.begin() + sigmaDigit, x.end(), sigmaShift);
    _left_shift(y.begin() + sigmaDigit, y.end(), sigmaShift);

    size_t t = max<size_t>(2, (x.size() + n - 1) / n);
    copy(x.cbegin() + (t - 2) * n, x.cend(), z.begin());

    const size_t quo_upper_len = quo_end - (quo + (t - 2) * n);
    if (quo_upper_len < n)
    {
      vector<uint32_t> qq(n);
      _divmod_d2n1n(
          z.begin(), z.end(),
          y.begin(), y.end(),
          qq.begin(), qq.end(),
          r.begin(), r.end());
      copy(qq.cbegin(), qq.cbegin() + quo_upper_len, quo + (t - 2) * n);
    }
    else
    {
      _divmod_d2n1n(
          z.begin(), z.end(),
          y.begin(), y.end(),
          quo + (t - 2) * n, quo + (t - 1) * n,
          r.begin(), r.end());
    }

    for (int i = t - 3; i >= 0; --i)
    {
      copy(x.begin() + i * n, x.begin() + (i + 1) * n, z.begin());
      copy(r.begin(), r.begin() + n, z.begin() + n);
      fill(r.begin(), r.end(), 0);

      _divmod_d2n1n(
          z.begin(), z.end(),
          y.begin(), y.end(),
          quo + i * n, quo + (i + 1) * n,
          r.begin(), r.end());
    }

    _shrink(r);
    copy(r.cbegin() + sigmaDigit, r.cbegin() + sigmaDigit + rsize, rem);
    _right_shift(rem, rem_end, sigmaShift);
  }

  // a(2n bits) / b(n bits)
  static void _divmod_d2n1n(
      citer a, citer a_end,
      citer b, citer b_end,
      iter quo, iter quo_end,
      iter rem, iter rem_end)
  {
    const size_t asize = a_end - a;
    const size_t bsize = b_end - b;
    const size_t qsize = quo_end - quo;
    const size_t rsize = rem_end - rem;
    const size_t n = bsize;

    assert(asize == 2 * n);
    assert(qsize == n);
    assert(rsize == n + 1);
    assert(_lt(a + n, a_end, b, b_end));

    if (n % 2 != 0 || n <= _div_naive_th)
    {
      _divmod_naive(
          a, a_end,
          b, b_end,
          quo, quo_end,
          rem, rem_end);
      return;
    }
    const size_t half = n >> 1;
    vector<uint32_t> r1(n + half + 1);
    copy(a, a + half, r1.begin());

    _divmod_d3n2n(
        a + half, a_end,
        b, b_end,
        quo + half, quo_end,
        r1.begin() + half, r1.end());
    _divmod_d3n2n(
        r1.cbegin(), r1.cend() - 1,
        b, b_end,
        quo, quo + half,
        rem, rem_end);
  }

  // a(3n bits) / b(2n bits)
  static void _divmod_d3n2n(
      citer a, citer a_end,
      citer b, citer b_end,
      iter quo, iter quo_end,
      iter rem, iter rem_end)
  {
    const size_t asize = a_end - a;
    const size_t bsize = b_end - b;
    const size_t qsize = quo_end - quo;
    const size_t rsize = rem_end - rem;
    const size_t n = bsize >> 1;

    assert(asize == 3 * n);
    assert(bsize == 2 * n);
    assert(qsize == n);
    assert(rsize == 2 * n + 1);
    assert(_lt(a + n, a_end, b, b_end));

    vector<uint32_t> d(2 * n);

    if (_lt(a + n + n, a_end, b + n, b_end))
    {
      _divmod_d2n1n(
          a + n, a_end,
          b + n, b_end,
          quo, quo_end,
          rem + n, rem_end);
      _mul(
          quo, quo_end,
          b, b + n,
          d.begin(), d.end());
    }
    else
    {
      fill(quo, quo_end, UINT32_MAX);

      _add(
          a + n, a + n + n,
          b + n, b_end,
          rem + n, rem_end);
      copy(b, b + n, d.begin() + n);
      _sub(d.begin(), d.end(), b, b + n);
    }

    copy(a, a + n, rem);

    while (_lt(rem, rem_end, d.cbegin(), d.cend()))
    {
      _add(rem, rem_end, b, b_end);
      vector<uint32_t> one{1};
      _sub(quo, quo_end, one.cbegin(), one.cend());
    }
    _sub(rem, rem_end, d.cbegin(), d.cend());
  }

  // a / b (naive)
  static void _divmod_naive(
      citer a, citer a_end,
      citer b, citer b_end,
      iter quo, iter quo_end,
      iter rem, iter rem_end)
  {
    const size_t asize = a_end - a;
    const size_t bsize = b_end - b;
    if (bsize == 0)
    {
      cerr << "Divide by Zero Exception" << endl;
      exit(1);
    }

    if (bsize == 1)
    {
      uint64_t carry = 0;
      for (size_t i = asize - 1; 0 <= (int)i; --i)
      {
        uint64_t v = (carry << 32) | a[i];
        quo[i] = v / (*b);
        carry = v - quo[i] * (*b);
      }
      *rem = (uint32_t)carry;
      return;
    }
    if (max(asize, bsize) <= 2)
    {
      uint64_t a64 = _iter_to_integer<uint64_t>(a, a_end), b64 = _iter_to_integer<uint64_t>(b, b_end);
      _integer_to_iter(a64 / b64, quo, quo_end);
      _integer_to_iter(a64 % b64, rem, rem_end);
      return;
    }
    if (_lt(a, a_end, b, b_end))
    {
      if (asize > bsize)
        a_end = a + bsize;
      copy(a, a_end, rem);
      return;
    }
    // B > 0xFFFFFFFF, A >= B

    // 割る数のビットを大きくする
    const int shift = __builtin_clz(*(b_end - 1));
    vector<uint32_t> x(asize + (__builtin_clz(*(a_end - 1)) < shift ? 1 : 0));
    copy(a, a_end, x.begin());
    vector<uint32_t> y(b, b_end);

    _left_shift(x.begin(), x.end(), shift);
    _left_shift(y.begin(), y.end(), shift);

    uint32_t yb = y.back();
    vector<uint32_t> qv(x.size() - y.size() + 1);
    vector<uint32_t> rv(x.end() - y.size(), x.end());
    for (int i = qv.size() - 1; i >= 0; i--)
    {
      if (rv.size() < y.size())
      {
        // do nothing
      }
      else if (rv.size() == y.size())
      {
        if (_leq(y, rv))
        {
          qv[i] = 1, rv = _sub(rv, y);
        }
      }
      else
      {
        assert(y.size() + 1 == rv.size());
        uint64_t rb = rv[rv.size() - 1];
        rb <<= 32;
        rb |= rv[rv.size() - 2];
        uint64_t q = rb / yb;
        if (q > UINT32_MAX)
          q = UINT32_MAX;

        vector<uint32_t> yq = _mul(y, {(uint32_t)q});
        // 真の商は q-2 以上 q+1 以下だが自信が無いので念のため while を回す
        while (_lt(rv, yq))
          q--, yq = _sub(yq, y);
        rv = _sub(rv, yq);
        while (_leq(y, rv))
          q++, rv = _sub(rv, y);
        qv[i] = q;
      }
      if (i)
        rv.insert(begin(rv), x[i - 1]);
      _shrink(rv);
    }

    _shrink(qv);
    _right_shift(rv.begin(), rv.end(), shift);
    _shrink(rv);

    copy(qv.cbegin(), qv.cend(), quo);
    copy(rv.cbegin(), rv.cend(), rem);
  }

  // int -> string
  // 先頭かどうかに応じて zero padding するかを決める
  static string _itos(uint32_t x, bool zero_padding)
  {
    string res;
    for (int i = 0; i < logD; i++)
    {
      uint32_t d = x % 16;
      if (d < 10)
        res.push_back('0' + d);
      else if (d < 16)
        res.push_back('A' + d - 10);
      else
        assert(false);
      x /= 16;
    }
    if (!zero_padding)
    {
      while (res.size() && res.back() == '0')
        res.pop_back();
      assert(!res.empty());
    }
    reverse(begin(res), end(res));
    return res;
  }

  template <typename I, enable_if_t<is_integral_v<I> ||
                                    is_same_v<I, __int128_t>> * = nullptr>
  static I _iter_to_integer(citer it, const citer end)
  {
    I res = 0;
    int shift = 0;
    while (it != end)
    {
      res |= (I)*it << shift;
      shift += 32;
      ++it;
    }
    return res;
  }

  template <typename I, enable_if_t<is_integral_v<I> ||
                                    is_same_v<I, __int128_t>> * = nullptr>
  static void _integer_to_iter(I x, iter it, const iter end)
  {
    if constexpr (is_signed_v<I> || is_same_v<I, __int128_t>)
    {
      assert(x >= 0);
    }
    while (x)
    {
      assert(it < end);
      *it = (uint32_t)x;
      ++it;
      x >>= 32;
    }
  }

  static vector<uint32_t> _integer_to_vector(uint32_t x)
  {
    return vector<uint32_t>{x};
  }

  template <typename I, enable_if_t<is_integral_v<I> ||
                                    is_same_v<I, __int128_t>> * = nullptr>
  static vector<uint32_t> _integer_to_vector(I x)
  {
    if constexpr (is_signed_v<I> || is_same_v<I, __int128_t>)
    {
      assert(x >= 0);
    }
    vector<uint32_t> rt;
    while (x)
    {
      rt.push_back((uint32_t)x);
      x >>= 32;
    }
    return rt;
  }

  // a << shift
  static void _left_shift(const iter a, const iter a_end, int shift)
  {
    assert(shift < 32);
    if (shift == 0)
      return;
    const int back = 32 - shift;
    uint32_t carry = 0;
    for (iter it = a; it < a_end; ++it)
    {
      uint32_t v = (*it << shift) | carry;
      carry = *it >> back;
      *it = v;
    }
    assert(carry == 0);
  }
  // a >> shift
  static void _right_shift(const iter a, const iter a_end, int shift)
  {
    assert(shift < 32);
    if (shift == 0)
      return;
    const int back = 32 - shift;
    uint32_t carry = 0;
    for (iter it = a_end - 1; a <= it; --it)
    {
      uint32_t v = (*it >> shift) | carry;
      carry = *it << back;
      *it = v;
    }
    assert(carry == 0);
  }

  // a < b
  static bool _lt(
      citer a, citer a_end,
      citer b, citer b_end)
  {
    size_t asize = a_end - a;
    size_t bsize = b_end - b;

    if (asize != bsize)
    {
      if (asize < bsize)
      {
        auto non_zero = std::find_if(b + asize, b_end, [](uint32_t x)
                                     { return x != 0; });
        if (non_zero != b_end)
          return true;

        b_end = b + asize;
        bsize = asize;
      }
      else
      {
        auto non_zero = std::find_if(a + bsize, a_end, [](uint32_t x)
                                     { return x != 0; });
        if (non_zero != a_end)
          return false;
        a_end = a + bsize;
        asize = bsize;
      }
    }
    for (int i = asize - 1; i >= 0; i--)
    {
      if (a[i] != b[i])
        return a[i] < b[i];
    }
    return false;
  }

  static void _dump(const vector<uint32_t> &a, string s = "")
  {
    _dump(a.cbegin(), a.cend(), s);
  }

  static void _dump(citer a, citer a_end, string s = "")
  {
    if (!s.empty())
      cerr << s << " : ";
    cerr << "{ ";
    for (citer it = a; it < a_end; ++it)
    {
      cerr << *it << ", ";
    }
    cerr << "}" << endl;
  }
};

using bigint = BigInteger;

} // namespace hex_big_integer_division_internal

/// @brief Divide nonnegative hexadecimal integers stored in 32-bit limbs.
/// Karatsuba supplies subquadratic multiplication, while Burnikel-Ziegler
/// splits a normalized 2n-by-n division into balanced 3n/2-by-n and
/// n-by-n/2 subproblems; short blocks fall back to Knuth-style long division.
inline std::pair<std::string, std::string>
divide_hex_big_integers(std::string_view dividend, std::string_view divisor) {
  using hex_big_integer_division_internal::bigint;
  bigint first{std::string(dividend)};
  bigint second{std::string(divisor)};
  auto [quotient, remainder] = divmod(first, second);
  return {quotient.to_string(), remainder.to_string()};
}

} // namespace noya