+/* --- Simple case --- *
+ *
+ * Use a fixed-sized buffer and single-precision arithmetic to pick off
+ * low-order digits. Put each digit in a buffer, working backwards from the
+ * end. If the buffer becomes full, recurse to get another one. Ensure that
+ * there are at least @z@ digits by writing leading zeroes if there aren't
+ * enough real digits.
+ */
+
+static int simple(mpw n, int radix, unsigned z,
+ const mptext_ops *ops, void *p)
+{
+ int rc = 0;
+ char buf[64];
+ unsigned i = sizeof(buf);
+ int rd = radix > 0 ? radix : -radix;
+
+ do {
+ int ch;
+ mpw x;
+
+ x = n % rd;
+ n /= rd;
+ if (radix < 0)
+ ch = x;
+ else if (x < 10)
+ ch = '0' + x;
+ else if (x < 36) /* Ascii specific */
+ ch = 'a' + x - 10;
+ else
+ ch = 'A' + x - 36;
+ buf[--i] = ch;
+ if (z)
+ z--;
+ } while (i && n);
+
+ if (n)
+ rc = simple(n, radix, z, ops, p);
+ else {
+ char zbuf[32];
+ memset(zbuf, (radix < 0) ? 0 : '0', sizeof(zbuf));
+ while (!rc && z >= sizeof(zbuf)) {
+ rc = ops->put(zbuf, sizeof(zbuf), p);
+ z -= sizeof(zbuf);
+ }
+ if (!rc && z)
+ rc = ops->put(zbuf, z, p);
+ }
+ if (!rc)
+ rc = ops->put(buf + i, sizeof(buf) - i, p);
+ BURN(buf);
+ return (rc);
+}
+
+/* --- Complicated case --- *
+ *
+ * If the number is small, fall back to the simple case above. Otherwise
+ * divide and take remainder by current large power of the radix, and emit
+ * each separately. Don't emit a zero quotient. Be very careful about
+ * leading zeroes on the remainder part, because they're deeply significant.
+ */
+
+static int complicated(mp *m, int radix, mp **pr, unsigned i, unsigned z,
+ const mptext_ops *ops, void *p)
+{
+ int rc = 0;
+ mp *q = MP_NEW;
+ unsigned d = 1 << i;
+
+ if (MP_LEN(m) < 2)
+ return (simple(MP_LEN(m) ? m->v[0] : 0, radix, z, ops, p));
+
+ assert(i);
+ mp_div(&q, &m, m, pr[i]);
+ if (!MP_LEN(q))
+ d = z;
+ else {
+ if (z > d)
+ z -= d;
+ else
+ z = 0;
+ rc = complicated(q, radix, pr, i - 1, z, ops, p);
+ }
+ if (!rc)
+ rc = complicated(m, radix, pr, i - 1, d, ops, p);
+ mp_drop(q);
+ return (rc);
+}
+
+/* --- Binary case --- *
+ *
+ * Special case for binary output. Goes much faster.
+ */
+
+static int binary(mp *m, int bit, int radix, const mptext_ops *ops, void *p)
+{
+ mpw *v;
+ mpw a;
+ int rc = 0;
+ unsigned b;
+ unsigned mask;
+ unsigned long n;
+ unsigned f = 0;
+ char buf[8], *q;
+ unsigned x;
+ int ch;
+
+#define f_out 1u
+
+ /* --- Work out where to start --- */
+
+ n = mp_bits(m);
+ if (n % bit)
+ n += bit - (n % bit);
+ b = n % MPW_BITS;
+ n /= MPW_BITS;
+
+ if (n >= MP_LEN(m)) {
+ n--;
+ b += MPW_BITS;
+ }
+
+ v = m->v + n;
+ a = *v;
+ mask = (1 << bit) - 1;
+ q = buf;
+
+ /* --- Main code --- */
+
+ for (;;) {
+ if (b > bit) {
+ b -= bit;
+ x = a >> b;
+ } else {
+ x = a << (bit - b);
+ b += MPW_BITS - bit;
+ if (v == m->v)
+ break;
+ a = *--v;
+ if (b < MPW_BITS)
+ x |= a >> b;
+ }
+ x &= mask;
+ if (!x && !(f & f_out))
+ continue;
+
+ if (radix < 0)
+ ch = x;
+ else if (x < 10)
+ ch = '0' + x;
+ else if (x < 36)
+ ch = 'a' + x - 10; /* Ascii specific */
+ else
+ ch = 'A' + x - 36;
+ *q++ = ch;
+ if (q >= buf + sizeof(buf)) {
+ if ((rc = ops->put(buf, sizeof(buf), p)) != 0)
+ goto done;
+ q = buf;
+ }
+ f |= f_out;
+ }
+
+ x &= mask;
+ if (radix < 0)
+ ch = x;
+ else if (x < 10)
+ ch = '0' + x;
+ else if (x < 36)
+ ch = 'a' + x - 10; /* Ascii specific */
+ else
+ ch = 'A' + x - 36;
+ *q++ = ch;
+ rc = ops->put(buf, q - buf, p);
+
+done:
+ mp_drop(m);
+ return (rc);
+
+#undef f_out
+}
+
+/* --- Main driver code --- */
+