URI:
       dc: Add initial version - sbase - suckless unix tools
  HTML git clone git://git.suckless.org/sbase
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
   DIR commit 608f88f08fcb049f30a1963f2bc72e429a63ba42
   DIR parent a1bf3adbc5f69f236319b99bd0a6aab6138c0014
  HTML Author: Roberto E. Vargas Caballero <k0ga@shike2.net>
       Date:   Mon, 15 Dec 2025 16:01:56 +0100
       
       dc: Add initial version
       
       This is the initial version of dc already tested in deep and
       with a considerable good set of tests.
       
       Diffstat:
         M Makefile                            |       1 +
         A dc.1                                |     260 +++++++++++++++++++++++++++++++
         A dc.c                                |    2199 +++++++++++++++++++++++++++++++
         A tests/0026-dc.sh                    |      68 +++++++++++++++++++++++++++++++
         A tests/0027-dc.sh                    |     138 ++++++++++++++++++++++++++++++
         A tests/0028-dc.sh                    |     139 ++++++++++++++++++++++++++++++
         A tests/0029-dc.sh                    |     196 +++++++++++++++++++++++++++++++
         A tests/0030-dc.sh                    |     296 +++++++++++++++++++++++++++++++
         A tests/0031-dc.sh                    |     218 +++++++++++++++++++++++++++++++
         A tests/0032-dc.sh                    |     287 +++++++++++++++++++++++++++++++
         A tests/0033-dc.sh                    |     137 +++++++++++++++++++++++++++++++
         A tests/0034-dc.sh                    |     150 +++++++++++++++++++++++++++++++
         A tests/0035-dc.sh                    |      30 ++++++++++++++++++++++++++++++
         A tests/0036-dc.sh                    |      64 +++++++++++++++++++++++++++++++
         A tests/0037-dc.sh                    |      77 +++++++++++++++++++++++++++++++
         A tests/0038-dc.sh                    |      72 +++++++++++++++++++++++++++++++
         A tests/0039-dc.sh                    |     101 +++++++++++++++++++++++++++++++
         A tests/0040-dc.sh                    |     135 +++++++++++++++++++++++++++++++
         A tests/0041-dc.sh                    |     101 +++++++++++++++++++++++++++++++
         A tests/0042-dc.sh                    |     107 +++++++++++++++++++++++++++++++
         A tests/0043-dc.sh                    |      59 +++++++++++++++++++++++++++++++
         A tests/0044-dc.sh                    |      36 +++++++++++++++++++++++++++++++
       
       22 files changed, 4871 insertions(+), 0 deletions(-)
       ---
   DIR diff --git a/Makefile b/Makefile
       @@ -110,6 +110,7 @@ BIN =\
                cron\
                cut\
                date\
       +        dc\
                dd\
                dirname\
                du\
   DIR diff --git a/dc.1 b/dc.1
       @@ -0,0 +1,260 @@
       +.Dd January 14, 2026
       +.Dt DC 1
       +.Os sbase
       +.Sh NAME
       +.Nm dc
       +.Nd arbitrary-precision desk calculator
       +.Sh SYNOPSIS
       +.Nm
       +.Op Fl i
       +.Op Ar file ...
       +.Sh DESCRIPTION
       +.Nm
       +is a reverse-polish notation arbitrary-precision desk calculator.
       +It reads and executes commands from each
       +.Ar file
       +in sequence.
       +After processing all files,
       +.Nm
       +reads from stdin.
       +.Pp
       +Numbers are arbitrary-precision decimal values.
       +Numbers may contain a decimal point and use
       +.Ql _
       +as a negative sign prefix.
       +When the input base is greater than 10, letters A-F or a-f represent digits 10-15.
       +A hexadecimal number beginning with a lower case letter
       +must be preceded by a zero to distinguish it from the command associated with the letter.
       +.Sh OPTIONS
       +.Bl -tag -width Ds
       +.It Fl i
       +Extended identifier mode.
       +Register names can be enclosed in
       +.Ql < Ns No ... Ns >
       +(numeric identifiers) or
       +.Ql \&" Ns No ... Ns \&"
       +(string identifiers).
       +.El
       +.Sh COMMANDS
       +.Ss Printing
       +.Bl -tag -width Ds
       +.It Ic p
       +Print the value on top of the stack with a newline,
       +without popping it.
       +.It Ic n
       +Print the value on top of the stack without a newline,
       +and pop it.
       +.It Ic P
       +Print the value on top of the stack as a byte stream.
       +For strings, print the characters directly.
       +For numbers, print it in base 100 representing the internal representation.
       +The value is popped.
       +.It Ic f
       +Print all values on the stack, one per line, from top to bottom.
       +.El
       +.Ss Arithmetic
       +.Bl -tag -width Ds
       +.It Ic +
       +Pop two values, add them, and push the result.
       +.It Ic \-
       +Pop two values, subtract the top from the second, and push the result.
       +.It Ic *
       +Pop two values, multiply them, and push the result.
       +.It Ic /
       +Pop two values, divide the second by the top, and push the quotient.
       +The scale of the result is determined by the
       +.Ic scale
       +parameter.
       +.It Ic %
       +Pop two values, compute the remainder of dividing the second by the top,
       +and push the result.
       +.It Ic ~
       +Pop two values, compute both quotient and remainder,
       +pushing the quotient first then the remainder on top.
       +.It Ic ^
       +Pop two values, raise the second to the power of the top, and push the result.
       +The exponent is truncated to an integer.
       +.It Ic v
       +Pop the top value, compute its square root, and push the result.
       +The precision is determined by the
       +.Ic scale
       +parameter.
       +.El
       +.Ss Stack
       +.Bl -tag -width Ds
       +.It Ic c
       +Clear the stack.
       +.It Ic d
       +Duplicate the top of the stack.
       +.It Ic r
       +Reverse the order of the top two values on the stack.
       +.It Ic z
       +Push the current stack depth.
       +.El
       +.Ss Registers
       +Registers are named storage locations.
       +In normal mode, register names are single characters.
       +With the
       +.Fl i
       +option, extended names are available.
       +Each register also has an associated stack.
       +.Bl -tag -width Ds
       +.It Ic s Ns Ar x
       +Pop the top value and store it in register
       +.Ar x .
       +.It Ic l Ns Ar x
       +Push a copy of the value in register
       +.Ar x
       +onto the stack.
       +.It Ic S Ns Ar x
       +Pop the top value and push it onto register
       +.Ar x Ns 's
       +stack.
       +.It Ic L Ns Ar x
       +Pop the top value from register
       +.Ar x Ns 's
       +stack and push it onto the main stack.
       +.El
       +.Ss Arrays
       +Each register has an associated array.
       +.Bl -tag -width Ds
       +.It Ic : Ns Ar x
       +Pop an index, then pop a value, and store the value at that index in array
       +.Ar x .
       +.It Ic ; Ns Ar x
       +Pop an index and push the value at that index in array
       +.Ar x .
       +.El
       +.Ss Parameters
       +.Bl -tag -width Ds
       +.It Ic i
       +Pop the top value and use it as the input radix.
       +The input base must be between 2 and 16.
       +.It Ic o
       +Pop the top value and use it as the output radix.
       +The output base must be at least 2.
       +.It Ic k
       +Pop the top value and use it as the scale
       +.Pq number of decimal places
       +for arithmetic operations.
       +The scale must be non-negative.
       +.It Ic I
       +Push the current input radix.
       +.It Ic O
       +Push the current output radix.
       +.It Ic K
       +Push the current scale.
       +.El
       +.Ss Strings and Macros
       +.Bl -tag -width Ds
       +.It Ic \&[ Ns Ar string Ns Ic \&]
       +Push
       +.Ar string
       +onto the stack.
       +Brackets inside the string must be balanced or escaped with a backslash.
       +.It Ic x
       +Pop the top value.
       +If it is a string, execute it as a macro.
       +If it is a number, push it back.
       +.El
       +.Ss Conditionals
       +The conditional commands pop two values, compare them,
       +and if the condition is true, execute the contents of register
       +.Ar x
       +as a macro.
       +.Bl -tag -width Ds
       +.It Ic > Ns Ar x
       +Execute register
       +.Ar x
       +if the second value is greater than the top.
       +.It Ic < Ns Ar x
       +Execute register
       +.Ar x
       +if the second value is less than the top.
       +.It Ic = Ns Ar x
       +Execute register
       +.Ar x
       +if the two values are equal.
       +.It Ic !> Ns Ar x
       +Execute register
       +.Ar x
       +if the second value is not greater than (less or equal to) the top.
       +.It Ic !< Ns Ar x
       +Execute register
       +.Ar x
       +if the second value is not less than (greater or equal to) the top.
       +.It Ic != Ns Ar x
       +Execute register
       +.Ar x
       +if the two values are not equal.
       +.El
       +.Ss Control
       +.Bl -tag -width Ds
       +.It Ic q
       +Quit.
       +If executing a macro, exit two macro levels.
       +.It Ic Q
       +Pop a value and quit that many macro levels.
       +.El
       +.Ss Input
       +.Bl -tag -width Ds
       +.It Ic ?
       +Read a line from stdin and execute it.
       +.It Ic #
       +Comment.
       +The rest of the line is ignored.
       +.It Ic !\& Ns Ar command
       +Execute
       +.Ar command
       +as a shell command.
       +.El
       +.Ss Number Information
       +.Bl -tag -width Ds
       +.It Ic Z
       +Pop a value and push its length.
       +For numbers, push the number of significant digits.
       +For strings, push the number of characters.
       +.It Ic X
       +Pop a value and push its scale (number of fractional digits).
       +For strings, push 0.
       +.El
       +.Sh EXAMPLES
       +Compute 6 * 7:
       +.Bd -literal -offset indent
       +6 7*p
       +.Ed
       +.Pp
       +Compute 2^10:
       +.Bd -literal -offset indent
       +2 10^p
       +.Ed
       +.Pp
       +Compute square root of 2 with 20 decimal places:
       +.Bd -literal -offset indent
       +20k 2vp
       +.Ed
       +.Pp
       +Factorial using recursion (store in register f):
       +.Bd -literal -offset indent
       +[d1-d1<f*]sf 10lf xp
       +.Ed
       +.Sh SEE ALSO
       +.Xr bc 1
       +.Rs
       +.%A R. H. Morris
       +.%A L. L. Cherry
       +.%T "DC \(em An Interactive Desk Calculator"
       +.Re
       +.Sh STANDARDS
       +.Nm
       +is compatible with traditional UNIX dc implementations.
       +The
       +.Fl i
       +flag,
       +.Ic #
       +comments,
       +the
       +.Ic ~
       +operator, and lowercase hexadecimal digits
       +.Pq a-f
       +are extensions to the traditional dc specification.
   DIR diff --git a/dc.c b/dc.c
       @@ -0,0 +1,2199 @@
       +#include <assert.h>
       +#include <errno.h>
       +#include <ctype.h>
       +#include <setjmp.h>
       +#include <stdarg.h>
       +#include <stdio.h>
       +#include <stdlib.h>
       +#include <string.h>
       +#include <limits.h>
       +
       +#include "arg.h"
       +#include "util.h"
       +
       +#define NDIGITS 10
       +#define REGSIZ 16
       +#define HASHSIZ 128
       +
       +enum {
       +        NVAL,
       +        STR,
       +        NUM,
       +};
       +
       +enum {
       +        NE,
       +        LE,
       +        GE,
       +};
       +
       +typedef struct num Num;
       +typedef struct val Val;
       +
       +struct num {
       +        int begin;
       +        int scale;
       +        int room;
       +        signed char *buf, *wp, *rp;
       +};
       +
       +struct digit {
       +        int val;
       +        struct digit *next;
       +};
       +
       +struct val {
       +        int type;
       +        union {
       +                Num *n;
       +                char *s;
       +        } u;
       +
       +        struct val *next;
       +};
       +
       +struct ary {
       +        int n;
       +        Val *buf;
       +        struct ary *next;
       +};
       +
       +struct reg {
       +        char *name;
       +        Val val;
       +        struct ary ary;
       +        struct reg *next;
       +};
       +
       +struct input {
       +        FILE *fp;
       +        char *buf;
       +        size_t n;
       +        char *s;
       +        struct input *next;
       +};
       +
       +static Val *stack;
       +static jmp_buf env;
       +static struct input *input;
       +static struct reg *htbl[HASHSIZ];
       +
       +static signed char onestr[] = {1, 0};
       +static Num zero, one = {.buf = onestr, .wp = onestr + 1};
       +static char digits[] = "0123456789ABCDEF.";
       +
       +static int scale, ibase = 10, obase = 10;
       +static int iflag;
       +static int col;
       +
       +/*
       + * This dc implementation follows the decription of dc found in the paper
       + * DC - An Interactive Desk Calculator, by Robert Morris and Lorinda Cherry
       + */
       +
       +static void
       +dumpnum(char *msg, Num *num)
       +{
       +        signed char *p;
       +
       +        printf("Num (%s)", msg ? msg : "");
       +
       +        if (!num) {
       +                puts("NULL\n");
       +                return;
       +        }
       +        printf("\n"
       +               "\tscale=%d\n"
       +               "\troom=%d\n"
       +               "\tdigits=[\n",
       +               num->scale,
       +               num->room);
       +        for (p = num->buf; p < num->wp; ++p)
       +                printf("\t\t%02d\n", *p);
       +        printf("\t]\n");
       +}
       +
       +/*
       + * error is not called from the implementation of the
       + * arithmetic functions because that can drive to memory
       + * leaks very easily.
       + */
       +static void
       +error(char *fmt, ...)
       +{
       +        va_list va;
       +
       +        va_start(va, fmt);
       +        xvprintf(fmt, va);
       +        putc('\n', stderr);
       +        va_end(va);
       +
       +        longjmp(env, 1);
       +}
       +
       +static void
       +freenum(Num *num)
       +{
       +        if (!num)
       +                return;
       +        free(num->buf);
       +        free(num);
       +}
       +
       +static Num *
       +moreroom(Num *num, int more)
       +{
       +        int ro, wo, room;
       +        signed char *p;
       +
       +        ro = num->rp - num->buf;
       +        wo = num->wp - num->buf;
       +        room = num->room;
       +
       +        if (room > INT_MAX - more)
       +                eprintf("out of memory\n");
       +
       +        room = room + more;
       +        if (room < NDIGITS)
       +                room = NDIGITS;
       +        p = erealloc(num->buf, room);
       +        num->buf = p;
       +        num->rp = p + ro;
       +        num->wp = p + wo;
       +        num->room = room;
       +
       +        return num;
       +}
       +
       +static Num *
       +grow(Num *num)
       +{
       +        return moreroom(num, NDIGITS);
       +}
       +
       +static Num *
       +expand(Num *num, int min)
       +{
       +        if (min < num->room)
       +                return num;
       +        return moreroom(num, min - num->room);
       +}
       +
       +static Num *
       +new(int room)
       +{
       +        Num *num = emalloc(sizeof(*num));
       +
       +        num->rp = num->wp = num->buf = NULL;
       +        num->begin = num->room = num->scale = 0;
       +
       +        return moreroom(num, room);
       +}
       +
       +static Num *
       +zeronum(int ndigits)
       +{
       +        Num *num = new(ndigits);
       +
       +        num->wp = num->buf + ndigits;
       +        memset(num->buf, 0, ndigits);
       +
       +        return num;
       +}
       +
       +static Num *
       +wrdigit(Num *num, int d)
       +{
       +        if (num->wp == &num->buf[num->room])
       +                grow(num);
       +        *num->wp++ = d;
       +
       +        return num;
       +}
       +
       +static int
       +rddigit(Num *num)
       +{
       +        if (num->rp == num->wp)
       +                return -1;
       +        return *num->rp++;
       +}
       +
       +static int
       +peek(Num *num)
       +{
       +        if (num->rp == num->wp)
       +                return -1;
       +        return *num->rp;
       +}
       +
       +static Num *
       +poke(Num *num, unsigned d)
       +{
       +        if (num->rp == &num->buf[num->room])
       +                grow(num);
       +        if (num->rp == num->wp)
       +                num->wp++;
       +        *num->rp = d;
       +
       +        return num;
       +}
       +
       +static int
       +begin(Num *num)
       +{
       +        return num->begin != 0;
       +}
       +
       +static int
       +first(Num *num)
       +{
       +        num->begin = 0;
       +        num->rp = num->buf;
       +        return num->rp != num->wp;
       +}
       +
       +static int
       +last(Num *num)
       +{
       +        if (num->wp != num->buf) {
       +                num->begin = 0;
       +                num->rp = num->wp - 1;
       +                return 1;
       +        }
       +        num->begin = 1;
       +        return 0;
       +}
       +
       +static int
       +prev(Num *num)
       +{
       +        if (num->rp > num->buf) {
       +                --num->rp;
       +                return 1;
       +        }
       +        num->begin = 1;
       +        return 0;
       +}
       +
       +static int
       +next(Num *num)
       +{
       +        if (num->rp != num->wp + 1) {
       +                ++num->rp;
       +                return 1;
       +        }
       +        return 0;
       +}
       +
       +static void
       +truncate(Num *num)
       +{
       +        num->wp = num->rp;
       +        if (num->rp != num->buf)
       +                num->rp--;
       +}
       +
       +static int
       +more(Num *num)
       +{
       +        return (num->rp != num->wp);
       +}
       +
       +static int
       +length(Num *num)
       +{
       +        return num->wp - num->buf;
       +}
       +
       +static int
       +tell(Num *num)
       +{
       +        return num->rp - num->buf;
       +}
       +
       +static void
       +seek(Num *num, int pos)
       +{
       +        num->rp = num->buf + pos;
       +}
       +
       +static void
       +rshift(Num *num, int n)
       +{
       +        int diff;
       +
       +        diff = length(num) - n;
       +        if (diff < 0) {
       +                first(num);
       +                truncate(num);
       +                return;
       +        }
       +
       +        memmove(num->buf, num->buf + n, diff);
       +        num->rp = num->buf + diff;
       +        truncate(num);
       +}
       +
       +static void
       +lshift(Num *num, int n)
       +{
       +        int len;
       +
       +        len = length(num);
       +        expand(num, len + n);
       +        memmove(num->buf + n, num->buf, len);
       +        memset(num->buf, 0, n);
       +        num->wp += n;
       +}
       +
       +static Num *
       +chsign(Num *num)
       +{
       +        int val, d, carry;
       +
       +        carry = 0;
       +        for (first(num); more(num); next(num)) {
       +                d = peek(num);
       +                val = 100 - d - carry;
       +
       +                carry = 1;
       +                if (val >= 100) {
       +                        val -= 100;
       +                        carry = 0;
       +                }
       +                poke(num, val);
       +        }
       +
       +        prev(num);
       +        if (carry != 0) {
       +                if (peek(num) == 99)
       +                        poke(num, -1);
       +                else
       +                        wrdigit(num, -1);
       +        } else {
       +                if (peek(num) == 0)
       +                        truncate(num);
       +        }
       +
       +        return num;
       +}
       +
       +static Num *
       +copy(Num *num)
       +{
       +        Num *p;
       +        int len = length(num);
       +
       +        p = new(len);
       +        memcpy(p->buf, num->buf, len);
       +        p->wp = p->buf + len;
       +        p->rp = p->buf;
       +        p->scale = num->scale;
       +
       +        return p;
       +}
       +
       +static int
       +negative(Num *num)
       +{
       +        return last(num) && peek(num) == -1;
       +}
       +
       +static Num *
       +norm(Num *n)
       +{
       +        /* trailing 0 */
       +        for (last(n); peek(n) == 0; truncate(n))
       +                ;
       +
       +        if (negative(n)) {
       +                for (prev(n); peek(n) == 99; prev(n)) {
       +                        poke(n, -1);
       +                        next(n);
       +                        truncate(n);
       +                }
       +        }
       +
       +        /* adjust scale for 0 case*/
       +        if (length(n) == 0)
       +                n->scale = 0;
       +        return n;
       +}
       +
       +static Num *
       +mulnto(Num *src, Num *dst, int n)
       +{
       +        div_t dd;
       +        int d, carry;
       +
       +        first(dst);
       +        truncate(dst);
       +
       +        carry = 0;
       +        for (first(src); more(src); next(src)) {
       +                d = peek(src) * n + carry;
       +                dd = div(d, 100);
       +                carry = dd.quot;
       +                wrdigit(dst, dd.rem);
       +        }
       +
       +        if (carry)
       +                wrdigit(dst, carry);
       +        return dst;
       +}
       +
       +static Num *
       +muln(Num *num, int n)
       +{
       +        div_t dd;
       +        int d, carry;
       +
       +        carry = 0;
       +        for (first(num); more(num); next(num)) {
       +                d = peek(num) * n + carry;
       +                dd = div(d, 100);
       +                carry = dd.quot;
       +                poke(num, dd.rem);
       +        }
       +
       +        if (carry)
       +                wrdigit(num, carry);
       +        return num;
       +}
       +
       +static int
       +divn(Num *num, int n)
       +{
       +        div_t dd;
       +        int val, carry;
       +
       +        carry = 0;
       +        for (last(num); !begin(num); prev(num)) {
       +                val = carry * 100 + peek(num);
       +                dd = div(val, n);
       +                poke(num, dd.quot);
       +                carry = dd.rem;
       +        }
       +        norm(num);
       +
       +        return carry;
       +}
       +
       +static void
       +div10(Num *num, int n)
       +{
       +        div_t dd = div(n, 2);
       +
       +        if (dd.rem == 1)
       +                divn(num, 10);
       +
       +        rshift(num, dd.quot);
       +}
       +
       +static void
       +mul10(Num *num, int n)
       +{
       +        div_t dd = div(n, 2);
       +
       +        if (dd.rem == 1)
       +                muln(num, 10);
       +
       +        lshift(num, dd.quot);
       +}
       +
       +static void
       +align(Num *a, Num *b)
       +{
       +        int d;
       +        Num *max, *min;
       +
       +        d = a->scale - b->scale;
       +        if (d == 0) {
       +                return;
       +        } else if (d > 0) {
       +                min = b;
       +                max = a;
       +        } else {
       +                min = a;
       +                max = b;
       +        }
       +
       +        d = abs(d);
       +        mul10(min, d);
       +        min->scale += d;
       +
       +        assert(min->scale == max->scale);
       +}
       +
       +static Num *
       +addn(Num *num, int val)
       +{
       +        int d, carry = val;
       +
       +        for (first(num); carry; next(num)) {
       +                d = more(num) ? peek(num) : 0;
       +                d += carry;
       +                carry = 0;
       +
       +                if (d >= 100) {
       +                        carry = 1;
       +                        d -= 100;
       +                }
       +                poke(num, d);
       +        }
       +
       +        return num;
       +}
       +
       +static Num *
       +reverse(Num *num)
       +{
       +        int d;
       +        signed char *p, *q;
       +
       +        for (p = num->buf, q = num->wp - 1; p < q; ++p, --q) {
       +                d = *p;
       +                *p = *q;
       +                *q = d;
       +        }
       +
       +        return num;
       +}
       +
       +static Num *
       +addnum(Num *a, Num *b)
       +{
       +        Num *c;
       +        int len, alen, blen, carry, da, db, sum;
       +
       +        align(a, b);
       +        alen = length(a);
       +        blen = length(b);
       +        len = (alen > blen) ? alen : blen;
       +        c = new(len);
       +
       +        first(a);
       +        first(b);
       +        carry = 0;
       +        while (len-- > 0) {
       +                da = (more(a)) ? rddigit(a) : 0;
       +                db = (more(b)) ? rddigit(b) : 0;
       +
       +                sum = da + db + carry;
       +                if (sum >= 100) {
       +                        carry = 1;
       +                        sum -= 100;
       +                } else if (sum < 0) {
       +                        carry = -1;
       +                        sum += 100;
       +                } else {
       +                        carry = 0;
       +                }
       +
       +                wrdigit(c, sum);
       +        }
       +
       +        if (carry)
       +                wrdigit(c, carry);
       +        c->scale = a->scale;
       +
       +        return norm(c);
       +}
       +
       +static Num *
       +subnum(Num *a, Num *b)
       +{
       +        Num *tmp, *sum;
       +
       +        tmp = chsign(copy(b));
       +        sum = addnum(a, tmp);
       +        freenum(tmp);
       +
       +        return sum;
       +}
       +
       +static Num *
       +mulnum(Num *a, Num *b)
       +{
       +        Num shadow, *c, *ca, *cb;
       +        int pos, prod, carry, dc, db, da, sc;
       +        int asign = negative(a), bsign = negative(b);
       +
       +        c = zeronum(length(a) + length(b) + 1);
       +        c->scale = a->scale + b->scale;
       +        sc = (a->scale > b->scale) ? a->scale : b->scale;
       +
       +        ca = a;
       +        if (asign)
       +                ca = chsign(copy(ca));
       +        cb = b;
       +        if (bsign)
       +                cb = chsign(copy(cb));
       +
       +        /* avoid aliasing problems when called from expnum*/
       +        if (ca == cb) {
       +                shadow = *cb;
       +                b = cb = &shadow;
       +        }
       +
       +        for (first(cb); more(cb); next(cb)) {
       +                div_t d;
       +
       +                carry = 0;
       +                db = peek(cb);
       +
       +                pos = tell(c);
       +                for (first(ca); more(ca); next(ca)) {
       +                        da = peek(ca);
       +                        dc = peek(c);
       +                        prod = da * db + dc + carry;
       +                        d = div(prod, 100);
       +                        carry = d.quot;
       +                        poke(c, d.rem);
       +                        next(c);
       +                }
       +
       +                for ( ; carry > 0; carry = d.quot) {
       +                        dc = peek(c) + carry;
       +                        d = div(dc, 100);
       +                        poke(c, d.rem);
       +                        next(c);
       +                }
       +                seek(c, pos + 1);
       +        }
       +        norm(c);
       +
       +        /* Adjust scale to max(a->scale, b->scale) while c is still positive */
       +        sc = c->scale - sc;
       +        if (sc > 0) {
       +                div10(c, sc);
       +                c->scale -= sc;
       +        }
       +
       +        if (ca != a)
       +                freenum(ca);
       +        if (cb != b)
       +                freenum(cb);
       +
       +        if (asign ^ bsign)
       +                chsign(c);
       +        return c;
       +}
       +
       +/*
       + * The divmod function is implemented following the algorithm
       + * from the plan9 version that is not exactly like the one described
       + * in the paper. A lot of magic here.
       + */
       +static Num *
       +divmod(Num *odivd, Num *odivr, Num **remp)
       +{
       +        Num *acc, *divd, *divr, *res;
       +        int divsign, remsign;
       +        int under, magic, ndig, diff;
       +        int d, q, carry, divcarry;
       +        long dr, dd, cc;
       +
       +        divr = odivr;
       +        acc = copy(&zero);
       +        divd = copy(odivd);
       +        res = zeronum(length(odivd));
       +
       +        under = divcarry = divsign = remsign = 0;
       +
       +        if (length(divr) == 0) {
       +                weprintf("divide by 0\n");
       +                goto ret;
       +        }
       +
       +        divsign = negative(divd);
       +        if (divsign)
       +                chsign(divd);
       +
       +        remsign = negative(divr);
       +        if (remsign)
       +                divr = chsign(copy(divr));
       +
       +        diff = length(divd) - length(divr);
       +
       +        seek(res, diff + 1);
       +        last(divd);
       +        last(divr);
       +
       +        wrdigit(divd, 0);
       +
       +        dr = peek(divr);
       +        magic = dr < 10;
       +        dr = dr * 100 + (prev(divr) ? peek(divr) : 0);
       +        if (magic) {
       +                dr = dr * 100 + (prev(divr) ? peek(divr) : 0);
       +                dr *= 2;
       +                dr /= 25;
       +        }
       +
       +        for (ndig = 0; diff >= 0; ++ndig) {
       +                last(divd);
       +                dd = peek(divd);
       +                dd = dd * 100 + (prev(divd) ? peek(divd) : 0);
       +                dd = dd * 100 + (prev(divd) ? peek(divd) : 0);
       +                cc = dr;
       +
       +                if (diff == 0)
       +                        dd++;
       +                else
       +                        cc++;
       +
       +                if (magic)
       +                        dd *= 8;
       +
       +                q = dd / cc;
       +                under = 0;
       +                if (q > 0 && dd % cc < 8 && magic) {
       +                        q--;
       +                        under = 1;
       +                }
       +
       +                mulnto(divr, acc, q);
       +
       +                /* Subtract acc from dividend at offset position */
       +                first(acc);
       +                carry = 0;
       +                for (seek(divd, diff); more(divd); next(divd)) {
       +                        d = peek(divd);
       +                        d = d - (more(acc) ? rddigit(acc) : 0) - carry;
       +                        carry = 0;
       +                        if (d < 0) {
       +                                d += 100;
       +                                carry = 1;
       +                        }
       +                        poke(divd, d);
       +                }
       +                divcarry = carry;
       +
       +                /* Store quotient digit */
       +                prev(res);
       +                poke(res, q);
       +
       +                /* Handle borrow propagation */
       +                last(divd);
       +                d = peek(divd);
       +                if ((d != 0) && (diff != 0)) {
       +                        prev(divd);
       +                        d = peek(divd) + 100;
       +                        poke(divd, d);
       +                }
       +
       +                /* Shorten dividend for next iteration */
       +                if (--diff >= 0)
       +                        divd->wp--;
       +        }
       +
       +        /*
       +         * if we have an underflow then we have to adjust
       +         * the remaining and the result
       +         */
       +        if (under) {
       +                Num *p = subnum(divd, divr);
       +                if (negative(p)) {
       +                        freenum(p);
       +                } else {
       +                        freenum(divd);
       +                        poke(res, q + 1);
       +                        divd = p;
       +                }
       +        }
       +
       +        if (divcarry) {
       +                Num *p;
       +
       +                poke(res, q - 1);
       +                poke(divd, -1);
       +                p = addnum(divr, divd);
       +                freenum(divd);
       +                divd = p;
       +        }
       +
       +        divcarry = 0;
       +        for (first(res); more(res); next(res)) {
       +                d = peek(res) + divcarry;
       +                divcarry = 0;
       +                if (d >= 100) {
       +                        d -= 100;
       +                        divcarry = 1;
       +                }
       +                poke(res, d);
       +        }
       +
       +ret:
       +        if (divsign)
       +                chsign(divd);
       +        if (divsign ^ remsign)
       +                chsign(res);
       +
       +        if (remp) {
       +                divd->scale = odivd->scale;
       +                *remp = norm(divd);
       +        } else {
       +                freenum(divd);
       +        }
       +
       +        if (divr != odivr)
       +                freenum(divr);
       +
       +        freenum(acc);
       +
       +        res->scale = odivd->scale - odivr->scale;
       +        if (res->scale < 0)
       +                res->scale = 0;
       +
       +        return norm(res);
       +}
       +
       +static int
       +divscale(Num *divd, Num *divr)
       +{
       +        int diff;
       +
       +        if (length(divr) == 0) {
       +                weprintf("divide by 0\n");
       +                return 0;
       +        }
       +
       +        diff = scale + divr->scale - divd->scale;
       +
       +        if (diff > 0) {
       +                mul10(divd, diff);
       +                divd->scale += diff;
       +        } else if (diff < 0) {
       +                mul10(divr, -diff);
       +                divr->scale += -diff;
       +        }
       +
       +        return 1;
       +}
       +
       +static Num *
       +divnum(Num *a, Num *b)
       +{
       +        if (!divscale(a, b))
       +                return copy(&zero);
       +
       +        return divmod(a, b, NULL);
       +}
       +
       +static Num *
       +modnum(Num *a, Num *b)
       +{
       +        Num *mod, *c;
       +
       +        if (!divscale(a, b))
       +                return copy(&zero);
       +
       +        c = divmod(a, b, &mod);
       +        freenum(c);
       +
       +        return mod;
       +}
       +
       +static Num *
       +expnum(Num *base, Num *exp)
       +{
       +        int neg, d;
       +        Num *res, *fact, *e, *tmp1, *tmp2;
       +
       +        res = copy(&one);
       +        if (length(exp) == 0)
       +                return res;
       +
       +        e = copy(exp);
       +        if ((neg = negative(exp)) != 0)
       +                chsign(e);
       +
       +        if (e->scale > 0) {
       +                div10(e, e->scale);
       +                e->scale = 0;
       +        }
       +
       +        fact = copy(base);
       +        while (length(e) > 0) {
       +                first(e);
       +                d = peek(e);
       +                if (d % 2 == 1) {
       +                        tmp1 = mulnum(res, fact);
       +                        freenum(res);
       +                        res = tmp1;
       +                }
       +
       +                /* Square fact */
       +                tmp1 = mulnum(fact, fact);
       +                freenum(fact);
       +                fact = tmp1;
       +
       +                divn(e, 2);
       +        }
       +        freenum(fact);
       +        freenum(e);
       +
       +        /* Handle negative exponent: 1 / res */
       +        if (neg) {
       +                tmp2 = divnum(tmp1 = copy(&one), res);
       +                freenum(tmp1);
       +                freenum(res);
       +                res = tmp2;
       +        }
       +
       +        return res;
       +}
       +
       +/*
       + * Compare two numbers: returns <0 if a<b, 0 if a==b, >0 if a>b
       + */
       +static int
       +cmpnum(Num *a, Num *b)
       +{
       +        Num *diff;
       +        int result;
       +
       +        diff = subnum(a, b);
       +        if (length(diff) == 0)
       +                result = 0;
       +        else if (negative(diff))
       +                result = -1;
       +        else
       +                result = 1;
       +        freenum(diff);
       +
       +        return result;
       +}
       +
       +/*
       + * Integer square root of a small integer (0-9999)
       + * Used for initial guess in Newton's method
       + */
       +static int
       +isqrt(int n)
       +{
       +        int x, x1;
       +
       +        if (n <= 0)
       +                return 0;
       +        if (n == 1)
       +                return 1;
       +
       +        x = n;
       +        x1 = (x + 1) / 2;
       +        while (x1 < x) {
       +                x = x1;
       +                x1 = (x + n / x) / 2;
       +        }
       +        return x;
       +}
       +
       +/*
       + * Square root using Newton's method: x_{n+1} = (x_n + y/x_n) / 2
       + *
       + * Key insight: sqrt(a * 10^(2n)) = sqrt(a) * 10^n
       + * So we scale up the input to get the desired output precision.
       + *
       + * To compute sqrt with scale decimal places of precision:
       + * 1. Scale up y by 10^(2*scale + 2) (extra 2 for guard digits)
       + * 2. Compute integer sqrt
       + * 3. Result has (scale + 1) decimal places, truncate to scale
       + */
       +static Num *
       +sqrtnum(Num *oy)
       +{
       +        Num *y, *x, *xprev, *q, *sum;
       +        int top, ysc, iter;
       +
       +        if (length(oy) == 0)
       +                return copy(&zero);
       +
       +        if (negative(oy)) {
       +                weprintf("square root of negative number\n");
       +                return copy(&zero);
       +        }
       +
       +        y = copy(oy);
       +        ysc = 2 * scale + 2 - y->scale;
       +        if (ysc > 0)
       +                mul10(y, ysc);
       +        ysc = 2 * scale + 2;
       +
       +        /* Make scale even (so sqrt gives integer result) */
       +        if (ysc % 2 == 1) {
       +                muln(y, 10);
       +                ysc++;
       +        }
       +        y->scale = 0;
       +
       +        last(y);
       +        top = peek(y);
       +        if (prev(y) && length(y) > 1)
       +                top = top * 100 + peek(y);
       +
       +        x = new(0);
       +        wrdigit(x, isqrt(top));
       +        x->scale = 0;
       +
       +        /* Scale up the initial guess to match the magnitude of y */
       +        lshift(x, (length(y) - 1) / 2);
       +
       +
       +        /* Newton iteration: x = (x + y/x) / 2 */
       +        xprev = NULL;
       +        for (iter = 0; iter < 1000; iter++) {
       +                q = divmod(y, x, NULL);
       +                sum = addnum(x, q);
       +                freenum(q);
       +                divn(sum, 2);
       +
       +                /* Check for convergence: sum == x or sum == prev */
       +                if (cmpnum(sum, x) == 0) {
       +                        freenum(sum);
       +                        break;
       +                }
       +                if (xprev != NULL && cmpnum(sum, xprev) == 0) {
       +                        /* Oscillating - pick smaller */
       +                        if (cmpnum(x, sum) < 0) {
       +                                freenum(sum);
       +                        } else {
       +                                freenum(x);
       +                                x = sum;
       +                        }
       +                        break;
       +                }
       +
       +                freenum(xprev);
       +                xprev = x;
       +                x = sum;
       +        }
       +        freenum(xprev);
       +        freenum(y);
       +
       +        /* Truncate to desired scale */
       +        x->scale = ysc / 2;
       +        if (x->scale > scale) {
       +                int diff = x->scale - scale;
       +                div10(x, diff);
       +                x->scale = scale;
       +        }
       +
       +        return norm(x);
       +}
       +
       +static Num *
       +tonum(void)
       +{
       +        char *s, *t, *end, *dot;
       +        Num *num, *denom, *numer, *frac, *q, *rem;
       +        int sign, d, ch, nfrac;
       +
       +        s = input->s;
       +        num = new(0);
       +        sign = 0;
       +        if (*s == '_') {
       +                sign = 1;
       +                ++s;
       +        }
       +
       +        dot = NULL;
       +        for (t = s; (ch = *t) > 0 || ch <= UCHAR_MAX; ++t) {
       +                if (!strchr(digits, toupper(ch)))
       +                        break;
       +                if (ch == '.') {
       +                        if (dot)
       +                                break;
       +                        dot = t;
       +                }
       +        }
       +        input->s = end = t;
       +
       +        /*
       +         * Parse integer part: process digits left-to-right.
       +         * For each digit: num = num * ibase + digit
       +         */
       +        for (t = s; t < (dot ? dot : end); ++t) {
       +                d = strchr(digits, toupper(*t)) - digits;
       +                muln(num, ibase);
       +                addn(num, d);
       +        }
       +        norm(num);
       +
       +        if (!dot)
       +                goto ret;
       +
       +        /*
       +         * convert fractional digits
       +         * Algorithm: For digits d[0], d[1], ..., d[n-1] after '.'
       +         * Value = d[0]/ibase + d[1]/ibase^2 + ... + d[n-1]/ibase^n
       +         *
       +         * numerator = d[0]*ibase^(n-1) + d[1]*ibase^(n-2) + ... + d[n-1]
       +         * denominator = ibase^n
       +         * Then extract decimal digits by repeated: num*100/denom
       +         */
       +        denom = copy(&one);
       +        numer = copy(&zero);
       +        for (t = dot + 1; t < end; ++t) {
       +                d = strchr(digits, toupper(*t)) - digits;
       +                muln(denom, ibase);
       +                muln(numer, ibase);
       +                addn(numer, d);
       +        }
       +
       +        nfrac = end - dot - 1;
       +        frac = new(0);
       +        d = 0;
       +        while (frac->scale < nfrac || length(numer) > 0) {
       +                muln(numer, 100);
       +                q = divmod(numer, denom, &rem);
       +                freenum(numer);
       +
       +                d = first(q) ? peek(q) : 0;
       +                wrdigit(frac, d);
       +                freenum(q);
       +                numer = rem;
       +                frac->scale += 2;
       +        }
       +        reverse(frac);
       +
       +        /* Trim to exact input scale for odd nfrac */
       +        if (frac->scale > nfrac && d % 10 == 0) {
       +                divn(frac, 10);
       +                frac->scale--;
       +        }
       +
       +        freenum(numer);
       +        freenum(denom);
       +
       +        q = addnum(num, frac);
       +        freenum(num);
       +        freenum(frac);
       +        num = q;
       +
       +ret:
       +        if (sign)
       +                chsign(num);
       +        return num;
       +}
       +
       +static void
       +prchr(int ch)
       +{
       +        if (col >= 69) {
       +                putchar('\\');
       +                putchar('\n');
       +                col = 0;
       +        }
       +        putchar(ch);
       +        col++;
       +}
       +
       +static void
       +printd(int d, int base, int space)
       +{
       +        int w, n;
       +
       +        if (base <= 16) {
       +                prchr(digits[d]);
       +        } else {
       +                if (space)
       +                        prchr(' ');
       +
       +                for (w = 1, n = base - 1; n >= 10; n /= 10)
       +                        w++;
       +
       +                if (col + w > 69) {
       +                        putchar('\\');
       +                        putchar('\n');
       +                        col = 0;
       +                }
       +                col += printf("%0*d", w, d);
       +        }
       +}
       +
       +static void
       +pushdigit(struct digit **l, int val)
       +{
       +        struct digit *it = emalloc(sizeof(*it));
       +
       +        it->next = *l;
       +        it->val = val;
       +        *l = it;
       +}
       +
       +static int
       +popdigit(struct digit **l)
       +{
       +        int val;
       +        struct digit *next, *it = *l;
       +
       +        if (it == NULL)
       +                return -1;
       +
       +        val = it->val;
       +        next = it->next;
       +        free(it);
       +        *l = next;
       +        return val;
       +}
       +
       +static void
       +printnum(Num *onum, int base)
       +{
       +        struct digit *sp;
       +        int sc, i, sign, n;
       +        Num *num, *inte, *frac, *opow;
       +
       +        col = 0;
       +        if (length(onum) == 0) {
       +                prchr('0');
       +                return;
       +        }
       +
       +        num = copy(onum);
       +        if ((sign = negative(num)) != 0)
       +                chsign(num);
       +
       +        sc = num->scale;
       +        if (num->scale % 2 == 1) {
       +                muln(num, 10);
       +                num->scale++;
       +        }
       +        inte = copy(num);
       +        rshift(inte, num->scale / 2);
       +        inte->scale = 0;
       +        frac = subnum(num, inte);
       +
       +        sp = NULL;
       +        for (i = 0; length(inte) > 0; ++i)
       +                pushdigit(&sp, divn(inte, base));
       +        if (sign)
       +                prchr('-');
       +        while (i-- > 0)
       +                printd(popdigit(&sp), base, 1);
       +        assert(sp == NULL);
       +
       +        if (num->scale == 0)
       +                goto ret;
       +
       +        /*
       +         * Print fractional part by repeated multiplication by base.
       +         * We maintain the fraction as: frac / 10^scale
       +         *
       +         * Algorithm:
       +         * 1. Multiply frac by base
       +         * 2. Output integer part (frac / 10^scale)
       +         * 3. Keep fractional part (frac % 10^scale)
       +         */
       +        prchr('.');
       +
       +        opow = copy(&one);
       +        mul10(opow, num->scale);
       +
       +        for (n = 0; n < sc; ++n) {
       +                int d;
       +                Num *q, *rem;
       +
       +                muln(frac, base);
       +                q = divmod(frac, opow, &rem);
       +                d = first(q) ? peek(q) : 0;
       +                freenum(frac);
       +                freenum(q);
       +                frac = rem;
       +                printd(d, base, n > 0);
       +        }
       +        freenum(opow);
       +
       +ret:
       +        freenum(num);
       +        freenum(inte);
       +        freenum(frac);
       +}
       +
       +static int
       +moreinput(void)
       +{
       +        struct input *ip;
       +
       +repeat:
       +        if (!input)
       +                return 0;
       +
       +        if (input->buf != NULL && *input->s != '\0')
       +                return 1;
       +
       +        if (input->fp) {
       +                if (getline(&input->buf, &input->n, input->fp) >= 0) {
       +                        input->s = input->buf;
       +                        return 1;
       +                }
       +                if (ferror(input->fp)) {
       +                        eprintf("reading from file:");
       +                        exit(1);
       +                }
       +                fclose(input->fp);
       +        }
       +
       +        ip = input;
       +        input = ip->next;
       +        free(ip->buf);
       +        free(ip);
       +        goto repeat;
       +}
       +
       +static void
       +addinput(FILE *fp, char *s)
       +{
       +        struct input *ip;
       +
       +        assert((!fp && !s) == 0);
       +
       +        ip = emalloc(sizeof(*ip));
       +        ip->next = input;
       +        ip->fp = fp;
       +        ip->n = 0;
       +        ip->s = ip->buf = s;
       +        input = ip;
       +}
       +
       +static void
       +delinput(int cmd, int n)
       +{
       +        if (n < 0)
       +                error("Q command requires a number >= 0");
       +        while (n-- > 0) {
       +                if (cmd == 'Q' && !input->next)
       +                        error("Q command argument exceeded string execution depth");
       +                if (input->fp)
       +                        fclose(input->fp);
       +                free(input->buf);
       +                input = input->next;
       +                if (!input)
       +                        exit(0);
       +        }
       +}
       +
       +static void
       +push(Val v)
       +{
       +        Val *p = emalloc(sizeof(Val));
       +
       +        *p = v;
       +        p->next = stack;
       +        stack = p;
       +}
       +
       +static void
       +needstack(int n)
       +{
       +        Val *vp;
       +
       +        for (vp = stack; n > 0 && vp; vp = vp->next)
       +                --n;
       +        if (n > 0)
       +                error("stack empty");
       +}
       +
       +static Val
       +pop(void)
       +{
       +        Val v;
       +
       +        if (!stack)
       +                error("stack empty");
       +        v = *stack;
       +        free(stack);
       +        stack = v.next;
       +
       +        return v;
       +}
       +
       +static Num *
       +popnum(void)
       +{
       +        Val v = pop();
       +
       +        if (v.type != NUM) {
       +                free(v.u.s);
       +                error("non-numeric value");
       +        }
       +        return v.u.n;
       +}
       +
       +static void
       +pushnum(Num *num)
       +{
       +        push((Val) {.type = NUM, .u.n = num});
       +}
       +
       +static void
       +pushstr(char *s)
       +{
       +        push((Val) {.type = STR, .u.s = s});
       +}
       +
       +static void
       +arith(Num *(*fn)(Num *, Num *))
       +{
       +        Num *a, *b, *c;
       +
       +        needstack(2);
       +        b = popnum();
       +        a = popnum();
       +        c = (*fn)(a, b);
       +        freenum(a);
       +        freenum(b);
       +        pushnum(c);
       +}
       +
       +static void
       +pushdivmod(void)
       +{
       +        Num *a, *b, *q, *rem;
       +
       +        needstack(2);
       +        b = popnum();
       +        a = popnum();
       +
       +        if (!divscale(a, b)) {
       +                q = copy(&zero);
       +                rem = copy(&zero);
       +        } else {
       +                q = divmod(a, b, &rem);
       +        }
       +
       +        pushnum(q);
       +        pushnum(rem);
       +        freenum(a);
       +        freenum(b);
       +}
       +
       +static int
       +popint(void)
       +{
       +        Num *num;
       +        int r = -1, n, d;
       +
       +        num = popnum();
       +        if (negative(num))
       +                goto ret;
       +
       +        /* discard fraction part */
       +        div10(num, num->scale);
       +
       +        n = 0;
       +        for (last(num); !begin(num); prev(num)) {
       +                if (n > INT_MAX / 100)
       +                        goto ret;
       +                n *= 100;
       +                d = peek(num);
       +                if (n > INT_MAX - d)
       +                        goto ret;
       +                n += d;
       +        }
       +        r = n;
       +
       +ret:
       +        freenum(num);
       +        return r;
       +}
       +
       +static void
       +pushint(int n)
       +{
       +        div_t dd;
       +        Num *num;
       +
       +        num = new(0);
       +        for ( ; n > 0; n = dd.quot) {
       +                dd = div(n, 100);
       +                wrdigit(num, dd.rem);
       +        }
       +        pushnum(num);
       +}
       +
       +static void
       +printval(Val v)
       +{
       +        if (v.type == STR)
       +                fputs(v.u.s, stdout);
       +        else
       +                printnum(v.u.n, obase);
       +}
       +
       +static Val
       +dupval(Val v)
       +{
       +        Val nv;
       +
       +        switch (nv.type = v.type) {
       +        case STR:
       +                nv.u.s = estrdup(v.u.s);
       +                break;
       +        case NUM:
       +                nv.u.n = copy(v.u.n);
       +                break;
       +        case NVAL:
       +                nv.type = NUM;
       +                nv.u.n = copy(&zero);
       +                break;
       +        }
       +
       +        return nv;
       +}
       +
       +static void
       +freeval(Val v)
       +{
       +        if (v.type == STR)
       +                free(v.u.s);
       +        else if (v.type == NUM)
       +                freenum(v.u.n);
       +}
       +
       +static void
       +dumpstack(void)
       +{
       +        Val *vp;
       +
       +        for (vp = stack; vp; vp = vp->next) {
       +                printval(*vp);
       +                putchar('\n');
       +        }
       +}
       +
       +static void
       +clearstack(void)
       +{
       +        Val *vp, *next;
       +
       +        for (vp = stack; vp; vp = next) {
       +                next = vp->next;
       +                freeval(*vp);
       +                free(vp);
       +        }
       +        stack = NULL;
       +}
       +
       +static void
       +dupstack(void)
       +{
       +        Val v;
       +
       +        push(v = pop());
       +        push(dupval(v));
       +}
       +
       +static void
       +deepstack(void)
       +{
       +        int n;
       +        Val *vp;
       +
       +        n = 0;
       +        for (vp = stack; vp; vp = vp->next) {
       +                if (n == INT_MAX)
       +                        error("stack depth does not fit in a integer");
       +                ++n;
       +        }
       +        pushint(n);
       +}
       +
       +static void
       +pushfrac(void)
       +{
       +        Val v = pop();
       +
       +        if (v.type == STR)
       +                pushint(0);
       +        else
       +                pushint(v.u.n->scale);
       +        freeval(v);
       +}
       +
       +static void
       +pushlen(void)
       +{
       +        int n;
       +        Num *num;
       +        Val v = pop();
       +
       +        if (v.type == STR) {
       +                n = strlen(v.u.s);
       +        } else {
       +                num = v.u.n;
       +                if (length(num) == 0) {
       +                        n = 1;
       +                } else {
       +                        n = length(num) * 2;
       +                        n -= last(num) ? peek(num) < 10 : 0;
       +                }
       +        }
       +        pushint(n);
       +        freeval(v);
       +}
       +
       +static void
       +setibase(void)
       +{
       +        int n = popint();
       +
       +        if (n < 2 || n > 16)
       +                error("input base must be an integer between 2 and 16");
       +        ibase = n;
       +}
       +
       +static void
       +setobase(void)
       +{
       +        int n = popint();
       +
       +        if (n < 2)
       +                error("output base must be an integer greater than 1");
       +        obase = n;
       +}
       +
       +static char *
       +string(char *dst, int *np)
       +{
       +        int n, ch;
       +
       +        n = np ? *np : 0;
       +        for (;;) {
       +                ch = *input->s++;
       +
       +                switch (ch) {
       +                case '\0':
       +                        if (!moreinput())
       +                                exit(0);
       +                        break;
       +                case '\\':
       +                        if (*input->s == '[') {
       +                                dst = erealloc(dst, n + 1);
       +                                dst[n++] = *input->s++;
       +                                break;
       +                        }
       +                        goto copy;
       +                case ']':
       +                        if (!np) {
       +                                dst = erealloc(dst, n + 1);
       +                                dst[n] = '\0';
       +                                return dst;
       +                        }
       +                case '[':
       +                default:
       +                copy:
       +                        dst = erealloc(dst, n + 1);
       +                        dst[n++] = ch;
       +                        if (ch == '[')
       +                                dst = string(dst, &n);
       +                        if (ch == ']') {
       +                                *np = n;
       +                                return dst;
       +                        }
       +                }
       +        }
       +}
       +
       +static void
       +setscale(void)
       +{
       +        int n = popint();
       +
       +        if (n < 0)
       +                error("scale must be a nonnegative integer");
       +        scale = n;
       +}
       +
       +static unsigned
       +hash(char *name)
       +{
       +        int c;
       +        unsigned h = 5381;
       +
       +        while (c = *name++)
       +                h = h*33 ^ c;
       +
       +        return h;
       +}
       +
       +static struct reg *
       +lookup(char *name)
       +{
       +        struct reg *rp;
       +        int h = hash(name) & HASHSIZ-1;
       +
       +        for (rp = htbl[h]; rp; rp = rp->next) {
       +                if (strcmp(name, rp->name) == 0)
       +                        return rp;
       +        }
       +
       +        rp = emalloc(sizeof(*rp));
       +        rp->next = htbl[h];
       +        htbl[h] = rp;
       +        rp->name = estrdup(name);
       +
       +        rp->val.type = NVAL;
       +        rp->val.next = NULL;
       +
       +        rp->ary.n = 0;
       +        rp->ary.buf = NULL;
       +        rp->ary.next = NULL;
       +
       +        return rp;
       +}
       +
       +static char *
       +regname(void)
       +{
       +        int delim, ch;
       +        char *s;
       +        static char name[REGSIZ];
       +
       +        ch = *input->s++;
       +        if (!iflag || ch != '<' && ch != '"') {
       +                name[0] = ch;
       +                name[1] = '\0';
       +                return name;
       +        }
       +
       +        if ((delim = ch) == '<')
       +                delim = '>';
       +
       +        for (s = name; s < &name[REGSIZ]; ++s) {
       +                ch = *input->s++;
       +                if (ch == '\0' ||  ch == delim) {
       +                        *s = '\0';
       +                        if (ch == '>') {
       +                                name[0] = atoi(name);
       +                                name[1] = '\0';
       +                        }
       +                        return name;
       +                }
       +                *s = ch;
       +        }
       +
       +        error("identifier too long");
       +}
       +
       +static void
       +popreg(void)
       +{
       +        int i;
       +        Val *vnext;
       +        struct ary *anext;
       +        char *s = regname();
       +        struct reg *rp = lookup(s);
       +
       +        if (rp->val.type == NVAL)
       +                error("stack register '%s' (%o) is empty", s, s[0]);
       +
       +        push(rp->val);
       +        vnext = rp->val.next;
       +        if (!vnext) {
       +                rp->val.type = NVAL;
       +        } else {
       +                rp->val = *vnext;
       +                free(vnext);
       +        }
       +
       +        for (i = 0; i < rp->ary.n; ++i)
       +                freeval(rp->ary.buf[i]);
       +        free(rp->ary.buf);
       +
       +        anext = rp->ary.next;
       +        if (!anext) {
       +                rp->ary.n = 0;
       +                rp->ary.buf = NULL;
       +        } else {
       +                rp->ary = *anext;
       +                free(anext);
       +        }
       +}
       +
       +static void
       +pushreg(void)
       +{
       +        Val v;
       +        Val *vp;
       +        struct ary *ap;
       +        struct reg *rp = lookup(regname());
       +
       +        v = pop();
       +
       +        vp = emalloc(sizeof(Val));
       +        *vp = rp->val;
       +        rp->val = v;
       +        rp->val.next = vp;
       +
       +        ap = emalloc(sizeof(struct ary));
       +        *ap = rp->ary;
       +        rp->ary.n = 0;
       +        rp->ary.buf = NULL;
       +        rp->ary.next = ap;
       +}
       +
       +static Val *
       +aryidx(void)
       +{
       +        int n;
       +        int i;
       +        Val *vp;
       +        struct reg *rp = lookup(regname());
       +        struct ary *ap = &rp->ary;
       +
       +        n = popint();
       +        if (n < 0)
       +                error("array index must fit in a positive integer");
       +
       +        if (n >= ap->n) {
       +                ap->buf = ereallocarray(ap->buf, n+1, sizeof(Val));
       +                for (i = ap->n; i <= n; ++i)
       +                        ap->buf[i].type = NVAL;
       +                ap->n = n + 1;
       +        }
       +        return &ap->buf[n];
       +}
       +
       +static void
       +aryget(void)
       +{
       +        Val *vp = aryidx();
       +
       +        push(dupval(*vp));
       +}
       +
       +static void
       +aryset(void)
       +{
       +        Val val, *vp = aryidx();
       +
       +        val = pop();
       +        freeval(*vp);
       +        *vp = val;
       +}
       +
       +static void
       +execmacro(void)
       +{
       +        int ch;
       +        Val v = pop();
       +
       +        assert(v.type != NVAL);
       +        if (v.type == NUM) {
       +                push(v);
       +                return;
       +        }
       +
       +        if (input->fp) {
       +                addinput(NULL, v.u.s);
       +                return;
       +        }
       +
       +        /* check for tail recursion */
       +        for (ch = *input->s; ch > 0 && ch < UCHAR_MAX; ch = *input->s) {
       +                if (!isspace(ch))
       +                        break;
       +                ++input->s;
       +        }
       +
       +        if (ch == '\0') {
       +                free(input->buf);
       +                input->buf = input->s = v.u.s;
       +                return;
       +        }
       +
       +        addinput(NULL, v.u.s);
       +}
       +
       +static void
       +relational(int ch)
       +{
       +        int r;
       +        char *s;
       +        Num *a, *b;
       +        struct reg *rp = lookup(regname());
       +
       +        needstack(2);
       +        a = popnum();
       +        b = popnum();
       +        r = cmpnum(a, b);
       +        freenum(a);
       +        freenum(b);
       +
       +        switch (ch) {
       +        case '>':
       +                r = r > 0;
       +                break;
       +        case '<':
       +                r = r < 0;
       +                break;
       +        case '=':
       +                r = r == 0;
       +                break;
       +        case LE:
       +                r = r <= 0;
       +                break;
       +        case GE:
       +                r = r >= 0;
       +                break;
       +        case NE:
       +                r = r != 0;
       +                break;
       +        default:
       +                abort();
       +        }
       +
       +        if (!r)
       +                return;
       +
       +        push(dupval(rp->val));
       +        execmacro();
       +}
       +
       +static void
       +printbytes(void)
       +{
       +        Num *num;
       +        Val v = pop();
       +
       +        if (v.type == STR) {
       +                fputs(v.u.s, stdout);
       +        } else {
       +                num = v.u.n;
       +                div10(num, num->scale);
       +                num->scale = 0;
       +                printnum(num, 100);
       +        }
       +        freeval(v);
       +}
       +
       +static void
       +eval(void)
       +{
       +        int ch;
       +        char *s;
       +        Num *num;
       +        size_t siz;
       +        Val v1, v2;
       +        struct reg *rp;
       +
       +        if (setjmp(env))
       +                return;
       +
       +        for (s = input->s; (ch = *s) != '\0'; ++s) {
       +                if (ch < 0 || ch > UCHAR_MAX || !isspace(ch))
       +                        break;
       +        }
       +        input->s = s + (ch != '\0');
       +
       +        switch (ch) {
       +        case '\0':
       +                break;
       +        case 'n':
       +                v1 = pop();
       +                printval(v1);
       +                freeval(v1);
       +                break;
       +        case 'p':
       +                v1 = pop();
       +                printval(v1);
       +                putchar('\n');
       +                push(v1);
       +                break;
       +        case 'P':
       +                printbytes();
       +                break;
       +        case 'f':
       +                dumpstack();
       +                break;
       +        case '+':
       +                arith(addnum);
       +                break;
       +        case '-':
       +                arith(subnum);
       +                break;
       +        case '*':
       +                arith(mulnum);
       +                break;
       +        case '/':
       +                arith(divnum);
       +                break;
       +        case '%':
       +                arith(modnum);
       +                break;
       +        case '^':
       +                arith(expnum);
       +                break;
       +        case '~':
       +                pushdivmod();
       +                break;
       +        case 'v':
       +                num = popnum();
       +                pushnum(sqrtnum(num));
       +                freenum(num);
       +                break;
       +        case 'c':
       +                clearstack();
       +                break;
       +        case 'd':
       +                dupstack();
       +                break;
       +        case 'r':
       +                needstack(2);
       +                v1 = pop();
       +                v2 = pop();
       +                push(v1);
       +                push(v2);
       +                break;
       +        case 'S':
       +                pushreg();
       +                break;
       +        case 'L':
       +                popreg();
       +                break;
       +        case 's':
       +                rp = lookup(regname());
       +                freeval(rp->val);
       +                rp->val = pop();
       +                break;
       +        case 'l':
       +                rp = lookup(regname());
       +                push(dupval(rp->val));
       +                break;
       +        case 'i':
       +                setibase();
       +                break;
       +        case 'o':
       +                setobase();
       +                break;
       +        case 'k':
       +                setscale();
       +                break;
       +        case 'I':
       +                pushint(ibase);
       +                break;
       +        case 'O':
       +                pushint(obase);
       +                break;
       +        case 'K':
       +                pushint(scale);
       +                break;
       +        case '[':
       +                pushstr(string(NULL, NULL));
       +                break;
       +        case 'x':
       +                execmacro();
       +                break;
       +        case '!':
       +                switch (*input->s++) {
       +                case '<':
       +                        ch = GE;
       +                        break;
       +                case '>':
       +                        ch = LE;
       +                        break;
       +                case '=':
       +                        ch = NE;
       +                        break;
       +                default:
       +                        system(input->s-1);
       +                        goto discard;
       +                }
       +        case '>':
       +        case '<':
       +        case '=':
       +                relational(ch);
       +                break;
       +        case '?':
       +                s = NULL;
       +                if (getline(&s, &siz, stdin) > 0) {
       +                        pushstr(s);
       +                } else {
       +                        free(s);
       +                        if (ferror(stdin))
       +                                eprintf("reading from file\n");
       +                }
       +                break;
       +        case 'q':
       +                delinput('q', 2);
       +                break;
       +        case 'Q':
       +                delinput('Q', popint());
       +                break;
       +        case 'Z':
       +                pushlen();
       +                break;
       +        case 'X':
       +                pushfrac();
       +                break;
       +        case 'z':
       +                deepstack();
       +                break;
       +        case '#':
       +        discard:
       +                while (*input->s)
       +                        ++input->s;
       +                break;
       +        case ':':
       +                aryset();
       +                break;
       +        case ';':
       +                aryget();
       +                break;
       +        default:
       +                if (!strchr(digits, ch))
       +                        error("'%c' (%#o) unimplemented", ch, ch);
       +        case '_':
       +                --input->s;
       +                pushnum(tonum());
       +                break;
       +        }
       +}
       +
       +static void
       +dc(char *fname)
       +{
       +        FILE *fp;
       +
       +        if (strcmp(fname, "-") == 0) {
       +                fp = stdin;
       +        } else {
       +                if ((fp = fopen(fname, "r")) == NULL)
       +                        eprintf("opening '%s':", fname);
       +        }
       +        addinput(fp, NULL);
       +
       +        while (moreinput())
       +                eval();
       +
       +        fclose(fp);
       +        free(input);
       +        input = NULL;
       +}
       +
       +static void
       +usage(void)
       +{
       +        eprintf("usage: dc [-i] [file ...]\n");
       +}
       +
       +int
       +main(int argc, char *argv[])
       +{
       +        ARGBEGIN {
       +        case 'i':
       +                iflag = 1;
       +                break;
       +        default:
       +                usage();
       +        } ARGEND
       +
       +        while (*argv)
       +                dc(*argv++);
       +        dc("-");
       +
       +        return 0;
       +}
   DIR diff --git a/tests/0026-dc.sh b/tests/0026-dc.sh
       @@ -0,0 +1,68 @@
       +#!/bin/sh
       +
       +tmp=$$.tmp
       +
       +trap 'rm -f $tmp' EXIT
       +trap 'exit $?' HUP INT TERM
       +
       +# Expected output for printnum tests
       +cat <<EOF >$tmp
       +test 1:
       +100
       +test 2:
       +0
       +test 3:
       +-42
       +test 4:
       +.5
       +test 5:
       +.05
       +test 6:
       +.001
       +test 7:
       +1.5
       +test 8:
       +-.5
       +test 9:
       +-1.25
       +test 10:
       +.4
       +test 11:
       +.0
       +.1
       +test 12:
       +.0
       +test 13:
       +1.0
       +test 14:
       +.2
       +test 15:
       +.1
       +test 16:
       +.01
       +test 17:
       +.001
       +test 18:
       +.8
       +EOF
       +
       +$EXEC ../dc <<EOF | diff -u $tmp -
       +[test 1:]pc 100p
       +[test 2:]pc 0p
       +[test 3:]pc _42p
       +[test 4:]pc .5p
       +[test 5:]pc .05p
       +[test 6:]pc .001p
       +[test 7:]pc 1.5p
       +[test 8:]pc _.5p
       +[test 9:]pc _1.25p
       +[test 10:]pc 16o.3p
       +[test 11:]pc 2o.1p10op
       +[test 12:]pc 2o.3p
       +[test 13:]pc 2o1.1p
       +[test 14:]pc 3o.7p
       +[test 15:]pc 2o.5p
       +[test 16:]pc 2o.25p
       +[test 17:]pc 2o.125p
       +[test 18:]pc 16o.5p
       +EOF
   DIR diff --git a/tests/0027-dc.sh b/tests/0027-dc.sh
       @@ -0,0 +1,138 @@
       +#!/bin/sh
       +
       +tmp=$$.tmp
       +
       +trap 'rm -f $tmp' EXIT
       +trap 'exit $?' HUP INT TERM
       +
       +cat <<EOF >$tmp
       +test 1:
       +5
       +test 2:
       +0
       +test 3:
       +-5
       +test 4:
       +200
       +test 5:
       +-200
       +test 6:
       +7
       +test 7:
       +-7
       +test 8:
       +7
       +test 9:
       +-7
       +test 10:
       +0
       +test 11:
       +0
       +test 12:
       +5
       +test 13:
       +-5
       +test 14:
       +5
       +test 15:
       +-5
       +test 16:
       +1.5
       +test 17:
       +2.50
       +test 18:
       +1.50
       +test 19:
       +.60
       +test 20:
       +1.234
       +test 21:
       +-.234
       +test 22:
       +0
       +test 23:
       +-.002
       +test 24:
       +-.003
       +test 25:
       +0
       +test 26:
       +99999999999999999999
       +test 27:
       +-99999999999999999999
       +test 28:
       +12345678901234567890
       +test 29:
       +0
       +test 30:
       +10
       +test 31:
       +1100
       +test 32:
       +100000000000000000000
       +test 33:
       +-100000000000000000000
       +test 34:
       +1
       +test 35:
       +-1
       +test 36:
       +0
       +test 37:
       +1.000001
       +test 38:
       +.0000000002
       +test 39:
       +-.000003
       +test 40:
       +-.001
       +test 41:
       +1.00
       +test 42:
       +.66666
       +EOF
       +
       +$EXEC ../dc <<EOF | diff -u $tmp -
       +[test 1:]pc 2 3+p
       +[test 2:]pc 0 0+p
       +[test 3:]pc _2 _3+p
       +[test 4:]pc 100 100+p
       +[test 5:]pc _100 _100+p
       +[test 6:]pc 10 _3+p
       +[test 7:]pc 3 _10+p
       +[test 8:]pc _3 10+p
       +[test 9:]pc _10 3+p
       +[test 10:]pc 5 _5+p
       +[test 11:]pc _5 5+p
       +[test 12:]pc 0 5+p
       +[test 13:]pc 0 _5+p
       +[test 14:]pc 5 0+p
       +[test 15:]pc _5 0+p
       +[test 16:]pc 1.0 .5+p
       +[test 17:]pc 1.5 1.00+p
       +[test 18:]pc 1.00 .50+p
       +[test 19:]pc .1 .50+p
       +[test 20:]pc 1.004 .23+p
       +[test 21:]pc _.5 .266+p
       +[test 22:]pc 1.5 _1.5+p
       +[test 23:]pc _.001 _.001+p
       +[test 24:]pc _.001 _.002+p
       +[test 25:]pc .001 _.001+p
       +[test 26:]pc 99999999999999999999 0+p
       +[test 27:]pc _99999999999999999999 0+p
       +[test 28:]pc 12345678901234567890 0+p
       +[test 29:]pc 0 0+p
       +[test 30:]pc 9 1+p
       +[test 31:]pc 999 101+p
       +[test 32:]pc 99999999999999999999 1+p
       +[test 33:]pc _99999999999999999999 _1+p
       +[test 34:]pc 99999999999999999999 _99999999999999999998+p
       +[test 35:]pc 99999999999999999998 _99999999999999999999+p
       +[test 36:]pc 99999999999999999999 _99999999999999999999+p
       +[test 37:]pc .000001 1+p
       +[test 38:]pc .0000000001 .0000000001+p
       +[test 39:]pc _.000001 _.000002+p
       +[test 40:]pc .001 _.002+p
       +[test 41:]pc .99 .01+p
       +[test 42:]pc .12345 .54321+p
       +EOF
   DIR diff --git a/tests/0028-dc.sh b/tests/0028-dc.sh
       @@ -0,0 +1,139 @@
       +#!/bin/sh
       +
       +tmp=$$.tmp
       +
       +trap 'rm -f $tmp' EXIT
       +trap 'exit $?' HUP INT TERM
       +
       +# Test - command: subtraction
       +cat <<EOF >$tmp
       +test 1:
       +-1
       +test 2:
       +0
       +test 3:
       +1
       +test 4:
       +0
       +test 5:
       +0
       +test 6:
       +13
       +test 7:
       +13
       +test 8:
       +-13
       +test 9:
       +-13
       +test 10:
       +10
       +test 11:
       +-10
       +test 12:
       +-5
       +test 13:
       +5
       +test 14:
       +5
       +test 15:
       +-5
       +test 16:
       +.5
       +test 17:
       +.50
       +test 18:
       +.50
       +test 19:
       +-.40
       +test 20:
       +.774
       +test 21:
       +-.766
       +test 22:
       +3.0
       +test 23:
       +0
       +test 24:
       +.001
       +test 25:
       +.002
       +test 26:
       +99999999999999999999
       +test 27:
       +-99999999999999999999
       +test 28:
       +12345678901234567890
       +test 29:
       +0
       +test 30:
       +8
       +test 31:
       +898
       +test 32:
       +99999999999999999998
       +test 33:
       +-99999999999999999998
       +test 34:
       +199999999999999999997
       +test 35:
       +199999999999999999997
       +test 36:
       +199999999999999999998
       +test 37:
       +-.999999
       +test 38:
       +0
       +test 39:
       +.000001
       +test 40:
       +.003
       +test 41:
       +.98
       +test 42:
       +-.41976
       +EOF
       +
       +$EXEC ../dc <<EOF | diff -u $tmp -
       +[test 1:]pc 2 3-p
       +[test 2:]pc 0 0-p
       +[test 3:]pc _2 _3-p
       +[test 4:]pc 100 100-p
       +[test 5:]pc _100 _100-p
       +[test 6:]pc 10 _3-p
       +[test 7:]pc 3 _10-p
       +[test 8:]pc _3 10-p
       +[test 9:]pc _10 3-p
       +[test 10:]pc 5 _5-p
       +[test 11:]pc _5 5-p
       +[test 12:]pc 0 5-p
       +[test 13:]pc 0 _5-p
       +[test 14:]pc 5 0-p
       +[test 15:]pc _5 0-p
       +[test 16:]pc 1.0 .5-p
       +[test 17:]pc 1.5 1.00-p
       +[test 18:]pc 1.00 .50-p
       +[test 19:]pc .1 .50-p
       +[test 20:]pc 1.004 .23-p
       +[test 21:]pc _.5 .266-p
       +[test 22:]pc 1.5 _1.5-p
       +[test 23:]pc _.001 _.001-p
       +[test 24:]pc _.001 _.002-p
       +[test 25:]pc .001 _.001-p
       +[test 26:]pc 99999999999999999999 0-p
       +[test 27:]pc _99999999999999999999 0-p
       +[test 28:]pc 12345678901234567890 0-p
       +[test 29:]pc 0 0-p
       +[test 30:]pc 9 1-p
       +[test 31:]pc 999 101-p
       +[test 32:]pc 99999999999999999999 1-p
       +[test 33:]pc _99999999999999999999 _1-p
       +[test 34:]pc 99999999999999999999 _99999999999999999998-p
       +[test 35:]pc 99999999999999999998 _99999999999999999999-p
       +[test 36:]pc 99999999999999999999 _99999999999999999999-p
       +[test 37:]pc .000001 1-p
       +[test 38:]pc .0000000001 .0000000001-p
       +[test 39:]pc _.000001 _.000002-p
       +[test 40:]pc .001 _.002-p
       +[test 41:]pc .99 .01-p
       +[test 42:]pc .12345 .54321-p
       +EOF
   DIR diff --git a/tests/0029-dc.sh b/tests/0029-dc.sh
       @@ -0,0 +1,196 @@
       +#!/bin/sh
       +
       +tmp=$$.tmp
       +
       +trap 'rm -f $tmp' EXIT
       +trap 'exit $?' HUP INT TERM
       +
       +# Test * command: multiplication
       +cat <<EOF >$tmp
       +test 1:
       +6
       +test 2:
       +0
       +test 3:
       +6
       +test 4:
       +10000
       +test 5:
       +10000
       +test 6:
       +-30
       +test 7:
       +-30
       +test 8:
       +-30
       +test 9:
       +-30
       +test 10:
       +-25
       +test 11:
       +-25
       +test 12:
       +0
       +test 13:
       +0
       +test 14:
       +0
       +test 15:
       +0
       +test 16:
       +5
       +test 17:
       +5
       +test 18:
       +-5
       +test 19:
       +-5
       +test 20:
       +.5
       +test 21:
       +1.50
       +test 22:
       +.50
       +test 23:
       +0
       +test 24:
       +3.7
       +test 25:
       +.2
       +test 26:
       +1.00
       +test 27:
       +1.000
       +test 28:
       +0
       +test 29:
       +-3.7
       +test 30:
       +-3.7
       +test 31:
       +3.7
       +test 32:
       +-.2
       +test 33:
       +-.2
       +test 34:
       +.2
       +test 35:
       +0
       +test 36:
       +0
       +test 37:
       +12345678901234567890
       +test 38:
       +-12345678901234567890
       +test 39:
       +81
       +test 40:
       +9801
       +test 41:
       +998001
       +test 42:
       +99980001
       +test 43:
       +9999999800000001
       +test 44:
       +0
       +test 45:
       +.10000
       +test 46:
       +1.0
       +test 47:
       +4.5
       +test 48:
       +4.5
       +test 49:
       +1.0
       +test 50:
       +1.00
       +test 51:
       +1.000
       +test 52:
       +4
       +test 53:
       +16
       +test 54:
       +64
       +test 55:
       +256
       +test 56:
       +65536
       +test 57:
       +1
       +test 58:
       +-1
       +test 59:
       +-1
       +test 60:
       +1
       +test 61:
       +0
       +EOF
       +
       +$EXEC ../dc <<EOF | diff -u $tmp -
       +[test 1:]pc 2 3*p
       +[test 2:]pc 0 0*p
       +[test 3:]pc _2 _3*p
       +[test 4:]pc 100 100*p
       +[test 5:]pc _100 _100*p
       +[test 6:]pc 10 _3*p
       +[test 7:]pc 3 _10*p
       +[test 8:]pc _3 10*p
       +[test 9:]pc _10 3*p
       +[test 10:]pc 5 _5*p
       +[test 11:]pc _5 5*p
       +[test 12:]pc 0 5*p
       +[test 13:]pc 0 _5*p
       +[test 14:]pc 5 0*p
       +[test 15:]pc _5 0*p
       +[test 16:]pc 1 5*p
       +[test 17:]pc 5 1*p
       +[test 18:]pc _1 5*p
       +[test 19:]pc 5 _1*p
       +[test 20:]pc 1.0 .5*p
       +[test 21:]pc 1.5 1.00*p
       +[test 22:]pc 1.00 .50*p
       +[test 23:]pc .1 .5*p
       +[test 24:]pc 1.5 2.5*p
       +[test 25:]pc .5 .5*p
       +[test 26:]pc .25 4*p
       +[test 27:]pc .125 8*p
       +[test 28:]pc .001 .001*p
       +[test 29:]pc _1.5 2.5*p
       +[test 30:]pc 1.5 _2.5*p
       +[test 31:]pc _1.5 _2.5*p
       +[test 32:]pc _.5 .5*p
       +[test 33:]pc .5 _.5*p
       +[test 34:]pc _.5 _.5*p
       +[test 35:]pc 99999999999999999999 0*p
       +[test 36:]pc _99999999999999999999 0*p
       +[test 37:]pc 12345678901234567890 1*p
       +[test 38:]pc 12345678901234567890 _1*p
       +[test 39:]pc 9 9*p
       +[test 40:]pc 99 99*p
       +[test 41:]pc 999 999*p
       +[test 42:]pc 9999 9999*p
       +[test 43:]pc 99999999 99999999*p
       +[test 44:]pc .0001 .0001*p
       +[test 45:]pc .00001 10000*p
       +[test 46:]pc .1 10*p
       +[test 47:]pc 1.5 3*p
       +[test 48:]pc 3 1.5*p
       +[test 49:]pc 10 .1*p
       +[test 50:]pc 100 .01*p
       +[test 51:]pc 1000 .001*p
       +[test 52:]pc 2 2*p
       +[test 53:]pc 4 4*p
       +[test 54:]pc 8 8*p
       +[test 55:]pc 16 16*p
       +[test 56:]pc 256 256*p
       +[test 57:]pc 1 1*p
       +[test 58:]pc _1 1*p
       +[test 59:]pc 1 _1*p
       +[test 60:]pc _1 _1*p
       +[test 61:]pc 0 0*p
       +EOF
   DIR diff --git a/tests/0030-dc.sh b/tests/0030-dc.sh
       @@ -0,0 +1,296 @@
       +#!/bin/sh
       +
       +tmp=$$.tmp
       +
       +trap 'rm -f $tmp' EXIT
       +trap 'exit $?' HUP INT TERM
       +
       +# Test / command: division
       +# Note: scale (k) persists between operations, so we reset it explicitly
       +cat <<EOF >$tmp
       +test 1:
       +3
       +test 2:
       +3
       +test 3:
       +10
       +test 4:
       +33
       +test 5:
       +-3
       +test 6:
       +-3
       +test 7:
       +3
       +test 8:
       +0
       +test 9:
       +1
       +test 10:
       +5
       +test 11:
       +0
       +test 12:
       +3.50
       +test 13:
       +.33
       +test 14:
       +.3333
       +test 15:
       +2.50
       +test 16:
       +3.14
       +test 17:
       +3.142857
       +test 18:
       +-3.50
       +test 19:
       +-3.50
       +test 20:
       +3.50
       +test 21:
       +-.33
       +test 22:
       +-.33
       +test 23:
       +.33
       +test 24:
       +3
       +test 25:
       +2
       +test 26:
       +3
       +test 27:
       +20
       +test 28:
       +0
       +test 29:
       +.05
       +test 30:
       +1000
       +test 31:
       +1000001
       +test 32:
       +1
       +test 33:
       +12345678901234567890
       +test 34:
       +.3333333333
       +test 35:
       +.6666666666
       +test 36:
       +.1428571428
       +test 37:
       +.1111111111
       +test 38:
       +.0909090909
       +test 39:
       +.5
       +test 40:
       +.500
       +test 41:
       +.50000
       +test 42:
       +.2
       +test 43:
       +.250
       +test 44:
       +.1
       +test 45:
       +.125
       +test 46:
       +-3
       +test 47:
       +-3
       +test 48:
       +3
       +test 49:
       +-2.50
       +test 50:
       +-2.50
       +test 51:
       +2.50
       +test 52:
       +1.00000000
       +test 53:
       +.00100000
       +test 54:
       +1000.00000000
       +test 55:
       +.01000000
       +test 56:
       +100.00000000
       +test 57:
       +1
       +test 58:
       +1
       +test 59:
       +-1
       +test 60:
       +-1
       +test 61:
       +0
       +test 62:
       +0
       +test 63:
       +0
       +test 64:
       +100
       +test 65:
       +-100
       +test 66:
       +-100
       +test 67:
       +100
       +test 68:
       +10
       +test 69:
       +100
       +test 70:
       +100
       +test 71:
       +1000
       +test 72:
       +0
       +test 73:
       +0
       +test 74:
       +0
       +test 75:
       +0
       +test 76:
       +0
       +test 77:
       +0
       +test 78:
       +.50
       +test 79:
       +.10
       +test 80:
       +.01
       +test 81:
       +.50
       +test 82:
       +.99
       +test 83:
       +9
       +test 84:
       +8
       +test 85:
       +7
       +test 86:
       +6
       +test 87:
       +5
       +test 88:
       +4
       +test 89:
       +3
       +test 90:
       +2
       +test 91:
       +1
       +test 92:
       +99999999999999999999
       +test 93:
       +0
       +test 94:
       +.00000000000000000001
       +EOF
       +
       +$EXEC ../dc <<EOF | diff -u $tmp -
       +[test 1:]pc 0k 6 2/p
       +[test 2:]pc 0k 7 2/p
       +[test 3:]pc 0k 100 10/p
       +[test 4:]pc 0k 100 3/p
       +[test 5:]pc 0k _6 2/p
       +[test 6:]pc 0k 6 _2/p
       +[test 7:]pc 0k _6 _2/p
       +[test 8:]pc 0k 0 5/p
       +[test 9:]pc 0k 1 1/p
       +[test 10:]pc 0k 5 1/p
       +[test 11:]pc 0k 1 5/p
       +[test 12:]pc 2k 7 2/p
       +[test 13:]pc 2k 1 3/p
       +[test 14:]pc 4k 1 3/p
       +[test 15:]pc 2k 10 4/p
       +[test 16:]pc 2k 22 7/p
       +[test 17:]pc 6k 22 7/p
       +[test 18:]pc 2k _7 2/p
       +[test 19:]pc 2k 7 _2/p
       +[test 20:]pc 2k _7 _2/p
       +[test 21:]pc 2k _1 3/p
       +[test 22:]pc 2k 1 _3/p
       +[test 23:]pc 2k _1 _3/p
       +[test 24:]pc 0k 1.5 .5/p
       +[test 25:]pc 0k 3.0 1.5/p
       +[test 26:]pc 0k .75 .25/p
       +[test 27:]pc 0k 10 .5/p
       +[test 28:]pc 0k .5 10/p
       +[test 29:]pc 2k .5 10/p
       +[test 30:]pc 0k 1000000 1000/p
       +[test 31:]pc 0k 999999999999 999999/p
       +[test 32:]pc 0k 12345678901234567890 12345678901234567890/p
       +[test 33:]pc 0k 12345678901234567890 1/p
       +[test 34:]pc 10k 1 3/p
       +[test 35:]pc 10k 2 3/p
       +[test 36:]pc 10k 1 7/p
       +[test 37:]pc 10k 1 9/p
       +[test 38:]pc 10k 1 11/p
       +[test 39:]pc 1k 1 2/p
       +[test 40:]pc 3k 1 2/p
       +[test 41:]pc 5k 1 2/p
       +[test 42:]pc 1k 1 4/p
       +[test 43:]pc 3k 1 4/p
       +[test 44:]pc 1k 1 8/p
       +[test 45:]pc 3k 1 8/p
       +[test 46:]pc 0k _1.5 .5/p
       +[test 47:]pc 0k 1.5 _.5/p
       +[test 48:]pc 0k _1.5 _.5/p
       +[test 49:]pc 2k _10 4/p
       +[test 50:]pc 2k 10 _4/p
       +[test 51:]pc 2k _10 _4/p
       +[test 52:]pc 8k .001 .001/p
       +[test 53:]pc 8k .001 1/p
       +[test 54:]pc 8k 1 .001/p
       +[test 55:]pc 8k .0001 .01/p
       +[test 56:]pc 8k .01 .0001/p
       +[test 57:]pc 0k 1 1/p
       +[test 58:]pc 0k _1 _1/p
       +[test 59:]pc 0k _1 1/p
       +[test 60:]pc 0k 1 _1/p
       +[test 61:]pc 0k 0 1/p
       +[test 62:]pc 0k 0 _1/p
       +[test 63:]pc 0k 0 100/p
       +[test 64:]pc 0k 100 1/p
       +[test 65:]pc 0k _100 1/p
       +[test 66:]pc 0k 100 _1/p
       +[test 67:]pc 0k _100 _1/p
       +[test 68:]pc 0k 100 10/p
       +[test 69:]pc 0k 1000 10/p
       +[test 70:]pc 0k 10000 100/p
       +[test 71:]pc 0k 1000000 1000/p
       +[test 72:]pc 0k 10 100/p
       +[test 73:]pc 0k 10 1000/p
       +[test 74:]pc 0k 1 2/p
       +[test 75:]pc 0k 1 10/p
       +[test 76:]pc 0k 1 100/p
       +[test 77:]pc 0k 5 10/p
       +[test 78:]pc 2k 1 2/p
       +[test 79:]pc 2k 1 10/p
       +[test 80:]pc 2k 1 100/p
       +[test 81:]pc 2k 5 10/p
       +[test 82:]pc 2k 99 100/p
       +[test 83:]pc 0k 81 9/p
       +[test 84:]pc 0k 64 8/p
       +[test 85:]pc 0k 49 7/p
       +[test 86:]pc 0k 36 6/p
       +[test 87:]pc 0k 25 5/p
       +[test 88:]pc 0k 16 4/p
       +[test 89:]pc 0k 9 3/p
       +[test 90:]pc 0k 4 2/p
       +[test 91:]pc 0k 99999999999999999999 99999999999999999999/p
       +[test 92:]pc 0k 99999999999999999999 1/p
       +[test 93:]pc 0k 1 99999999999999999999/p
       +[test 94:]pc 20k 1 99999999999999999999/p
       +EOF
   DIR diff --git a/tests/0031-dc.sh b/tests/0031-dc.sh
       @@ -0,0 +1,218 @@
       +#!/bin/sh
       +
       +tmp=$$.tmp
       +
       +trap 'rm -f $tmp' EXIT
       +trap 'exit $?' HUP INT TERM
       +
       +# Test % command: modulo (remainder)
       +# Note: scale (k) persists between operations, so we reset it explicitly
       +cat <<EOF >$tmp
       +test 1:
       +1
       +test 2:
       +1
       +test 3:
       +2
       +test 4:
       +0
       +test 5:
       +2
       +test 6:
       +0
       +test 7:
       +0
       +test 8:
       +3
       +test 9:
       +-1
       +test 10:
       +1
       +test 11:
       +-1
       +test 12:
       +-1
       +test 13:
       +1
       +test 14:
       +-1
       +test 15:
       +0
       +test 16:
       +0
       +test 17:
       +0
       +test 18:
       +1
       +test 19:
       +1
       +test 20:
       +1
       +test 21:
       +99
       +test 22:
       +0
       +test 23:
       +0
       +test 24:
       +0
       +test 25:
       +1
       +test 26:
       +789
       +test 27:
       +4
       +test 28:
       +0
       +test 29:
       +1
       +test 30:
       +0
       +test 31:
       +1
       +test 32:
       +0
       +test 33:
       +0
       +test 34:
       +0
       +test 35:
       +0
       +test 36:
       +7
       +test 37:
       +1
       +test 38:
       +15
       +test 39:
       +0
       +test 40:
       +1
       +test 41:
       +0
       +test 42:
       +0
       +test 43:
       +0
       +test 44:
       +1.5
       +test 45:
       +0
       +test 46:
       +0
       +test 47:
       +0
       +test 48:
       +0
       +test 49:
       +0
       +test 50:
       +0
       +test 51:
       +0
       +test 52:
       +0
       +test 53:
       +0
       +test 54:
       +.001
       +test 55:
       +.01
       +test 56:
       +.01
       +test 57:
       +0
       +test 58:
       +.02
       +test 59:
       +-.01
       +test 60:
       +.01
       +test 61:
       +-.01
       +test 62:
       +.0001
       +test 63:
       +.0002
       +test 64:
       +.0005
       +test 65:
       +0
       +test 66:
       +.005
       +test 67:
       +.0050
       +test 68:
       +0
       +EOF
       +
       +$EXEC ../dc <<EOF | diff -u $tmp -
       +[test 1:]pc 0k 7 3%p
       +[test 2:]pc 0k 10 3%p
       +[test 3:]pc 0k 100 7%p
       +[test 4:]pc 0k 15 5%p
       +[test 5:]pc 0k 17 5%p
       +[test 6:]pc 0k 0 5%p
       +[test 7:]pc 0k 5 5%p
       +[test 8:]pc 0k 3 7%p
       +[test 9:]pc 0k _7 3%p
       +[test 10:]pc 0k 7 _3%p
       +[test 11:]pc 0k _7 _3%p
       +[test 12:]pc 0k _10 3%p
       +[test 13:]pc 0k 10 _3%p
       +[test 14:]pc 0k _10 _3%p
       +[test 15:]pc 0k 1 1%p
       +[test 16:]pc 0k 2 2%p
       +[test 17:]pc 0k 3 3%p
       +[test 18:]pc 0k 1 2%p
       +[test 19:]pc 0k 1 10%p
       +[test 20:]pc 0k 1 100%p
       +[test 21:]pc 0k 99 100%p
       +[test 22:]pc 0k 5 1%p
       +[test 23:]pc 0k 100 1%p
       +[test 24:]pc 0k _5 1%p
       +[test 25:]pc 0k 1000000 999999%p
       +[test 26:]pc 0k 123456789 1000%p
       +[test 27:]pc 0k 99999999999 7%p
       +[test 28:]pc 0k 12345678901234567890 9%p
       +[test 29:]pc 0k 99999999999999999999 2%p
       +[test 30:]pc 0k 99999999999999999999 3%p
       +[test 31:]pc 0k 99999999999999999999 99999999999999999998%p
       +[test 32:]pc 0k 99999999999999999999 99999999999999999999%p
       +[test 33:]pc 0k 8 2%p
       +[test 34:]pc 0k 8 4%p
       +[test 35:]pc 0k 16 8%p
       +[test 36:]pc 0k 15 8%p
       +[test 37:]pc 0k 17 8%p
       +[test 38:]pc 0k 255 16%p
       +[test 39:]pc 0k 256 16%p
       +[test 40:]pc 0k 257 16%p
       +[test 41:]pc 0k 7.5 2.5%p
       +[test 42:]pc 0k 1.5 .5%p
       +[test 43:]pc 0k 3.75 1.25%p
       +[test 44:]pc 0k 7.5 3%p
       +[test 45:]pc 0k 4.5 1.5%p
       +[test 46:]pc 0k 2.5 .5%p
       +[test 47:]pc 0k _7.5 2.5%p
       +[test 48:]pc 0k 7.5 _2.5%p
       +[test 49:]pc 0k _7.5 _2.5%p
       +[test 50:]pc 0k _1.5 .5%p
       +[test 51:]pc 0k 1.5 _.5%p
       +[test 52:]pc 0k _1.5 _.5%p
       +[test 53:]pc 0k .001 .001%p
       +[test 54:]pc 0k .01 .003%p
       +[test 55:]pc 2k 7 3%p
       +[test 56:]pc 2k 10 3%p
       +[test 57:]pc 2k 17 5%p
       +[test 58:]pc 2k 22 7%p
       +[test 59:]pc 2k _7 3%p
       +[test 60:]pc 2k 7 _3%p
       +[test 61:]pc 2k _7 _3%p
       +[test 62:]pc 4k 1 3%p
       +[test 63:]pc 4k 2 3%p
       +[test 64:]pc 4k 10 7%p
       +[test 65:]pc 2k 1.5 .5%p
       +[test 66:]pc 2k 3.5 1.5%p
       +[test 67:]pc 2k 7.25 2.25%p
       +[test 68:]pc 2k 10.5 3.5%p
       +EOF
   DIR diff --git a/tests/0032-dc.sh b/tests/0032-dc.sh
       @@ -0,0 +1,287 @@
       +#!/bin/sh
       +
       +tmp=$$.tmp
       +
       +trap 'rm -f $tmp' EXIT
       +trap 'exit $?' HUP INT TERM
       +
       +# Test ~ command: divmod (pushes quotient then remainder, remainder on top)
       +# Output format: each ~ produces two lines: remainder (top), then quotient
       +# Note: ~ now uses divscale like / and %, so results should match
       +cat <<EOF >$tmp
       +test 1:
       +0
       +3
       +test 2:
       +1
       +3
       +test 3:
       +1
       +2
       +test 4:
       +1
       +3
       +test 5:
       +2
       +14
       +test 6:
       +0
       +10
       +test 7:
       +1
       +33
       +test 8:
       +0
       +3
       +test 9:
       +2
       +3
       +test 10:
       +0
       +0
       +test 11:
       +0
       +1
       +test 12:
       +3
       +0
       +test 13:
       +0
       +1
       +test 14:
       +1
       +0
       +test 15:
       +1
       +0
       +test 16:
       +1
       +0
       +test 17:
       +0
       +5
       +test 18:
       +0
       +100
       +test 19:
       +99
       +0
       +test 20:
       +0
       +-3
       +test 21:
       +0
       +-3
       +test 22:
       +0
       +3
       +test 23:
       +-1
       +-2
       +test 24:
       +1
       +-2
       +test 25:
       +-1
       +2
       +test 26:
       +-1
       +-3
       +test 27:
       +1
       +-3
       +test 28:
       +-1
       +3
       +test 29:
       +0
       +-10
       +test 30:
       +0
       +-10
       +test 31:
       +0
       +10
       +test 32:
       +0
       +-5
       +test 33:
       +0
       +-5
       +test 34:
       +0
       +5
       +test 35:
       +0
       +0
       +test 36:
       +1
       +1
       +test 37:
       +789
       +123456
       +test 38:
       +4
       +14285714285
       +test 39:
       +0
       +1371742100137174210
       +test 40:
       +1
       +49999999999999999999
       +test 41:
       +0
       +33333333333333333333
       +test 42:
       +1
       +1
       +test 43:
       +0
       +1
       +test 44:
       +0
       +3
       +test 45:
       +0
       +3
       +test 46:
       +1.5
       +3
       +test 47:
       +0
       +3
       +test 48:
       +1.5
       +2
       +test 49:
       +0
       +1
       +test 50:
       +0
       +5
       +test 51:
       +0
       +-3
       +test 52:
       +0
       +-3
       +test 53:
       +0
       +3
       +test 54:
       +0
       +-3
       +test 55:
       +0
       +-3
       +test 56:
       +0
       +3
       +test 57:
       +0
       +1
       +test 58:
       +.001
       +3
       +test 59:
       +.01
       +3
       +test 60:
       +0
       +20
       +test 61:
       +.5
       +0
       +test 62:
       +0
       +1000
       +test 63:
       +0
       +4
       +test 64:
       +0
       +4
       +test 65:
       +0
       +16
       +test 66:
       +15
       +15
       +test 67:
       +1
       +16
       +test 68:
       +0
       +32
       +EOF
       +
       +$EXEC ../dc <<EOF | diff -u $tmp -
       +[test 1:]pc 6 2~f c
       +[test 2:]pc 7 2~f c
       +[test 3:]pc 7 3~f c
       +[test 4:]pc 10 3~f c
       +[test 5:]pc 100 7~f c
       +[test 6:]pc 100 10~f c
       +[test 7:]pc 100 3~f c
       +[test 8:]pc 15 5~f c
       +[test 9:]pc 17 5~f c
       +[test 10:]pc 0 5~f c
       +[test 11:]pc 5 5~f c
       +[test 12:]pc 3 7~f c
       +[test 13:]pc 1 1~f c
       +[test 14:]pc 1 2~f c
       +[test 15:]pc 1 5~f c
       +[test 16:]pc 1 10~f c
       +[test 17:]pc 5 1~f c
       +[test 18:]pc 100 1~f c
       +[test 19:]pc 99 100~f c
       +[test 20:]pc _6 2~f c
       +[test 21:]pc 6 _2~f c
       +[test 22:]pc _6 _2~f c
       +[test 23:]pc _7 3~f c
       +[test 24:]pc 7 _3~f c
       +[test 25:]pc _7 _3~f c
       +[test 26:]pc _10 3~f c
       +[test 27:]pc 10 _3~f c
       +[test 28:]pc _10 _3~f c
       +[test 29:]pc _100 10~f c
       +[test 30:]pc 100 _10~f c
       +[test 31:]pc _100 _10~f c
       +[test 32:]pc _5 1~f c
       +[test 33:]pc 5 _1~f c
       +[test 34:]pc _5 _1~f c
       +[test 35:]pc 0 _5~f c
       +[test 36:]pc 1000000 999999~f c
       +[test 37:]pc 123456789 1000~f c
       +[test 38:]pc 99999999999 7~f c
       +[test 39:]pc 12345678901234567890 9~f c
       +[test 40:]pc 99999999999999999999 2~f c
       +[test 41:]pc 99999999999999999999 3~f c
       +[test 42:]pc 99999999999999999999 99999999999999999998~f c
       +[test 43:]pc 99999999999999999999 99999999999999999999~f c
       +[test 44:]pc 7.5 2.5~f c
       +[test 45:]pc 1.5 .5~f c
       +[test 46:]pc 10.5 3~f c
       +[test 47:]pc 4.5 1.5~f c
       +[test 48:]pc 7.5 3~f c
       +[test 49:]pc .5 .5~f c
       +[test 50:]pc 2.5 .5~f c
       +[test 51:]pc _7.5 2.5~f c
       +[test 52:]pc 7.5 _2.5~f c
       +[test 53:]pc _7.5 _2.5~f c
       +[test 54:]pc _1.5 .5~f c
       +[test 55:]pc 1.5 _.5~f c
       +[test 56:]pc _1.5 _.5~f c
       +[test 57:]pc .001 .001~f c
       +[test 58:]pc .01 .003~f c
       +[test 59:]pc .1 .03~f c
       +[test 60:]pc 10 .5~f c
       +[test 61:]pc .5 10~f c
       +[test 62:]pc 100 .1~f c
       +[test 63:]pc 8 2~f c
       +[test 64:]pc 16 4~f c
       +[test 65:]pc 256 16~f c
       +[test 66:]pc 255 16~f c
       +[test 67:]pc 257 16~f c
       +[test 68:]pc 1024 32~f c
       +EOF
   DIR diff --git a/tests/0033-dc.sh b/tests/0033-dc.sh
       @@ -0,0 +1,137 @@
       +#!/bin/sh
       +
       +tmp=$$.tmp
       +
       +trap 'rm -f $tmp' EXIT
       +trap 'exit $?' HUP INT TERM
       +
       +# Expected output for exponentiation tests
       +# Values derived from system bc
       +cat <<EOF >$tmp
       +test 1:
       +1
       +test 2:
       +2
       +test 3:
       +8
       +test 4:
       +1024
       +test 5:
       +243
       +test 6:
       +1000000
       +test 7:
       +4
       +test 8:
       +-8
       +test 9:
       +16
       +test 10:
       +-32
       +test 11:
       +-27
       +test 12:
       +81
       +test 13:
       +-1000
       +test 14:
       +-100000
       +test 15:
       +1000000
       +test 16:
       +1
       +test 17:
       +1
       +test 18:
       +1
       +test 19:
       +1
       +test 20:
       +.5000000000
       +test 21:
       +.2500000000
       +test 22:
       +.1250000000
       +test 23:
       +.0625000000
       +test 24:
       +.0010000000
       +test 25:
       +-.1250000000
       +test 26:
       +.0625000000
       +test 27:
       +2.25
       +test 28:
       +3.375
       +test 29:
       +.25
       +test 30:
       +.125
       +test 31:
       +2.25
       +test 32:
       +-3.375
       +test 33:
       +1.5625
       +test 34:
       +.0625
       +test 35:
       +.015625
       +test 36:
       +.0625
       +test 37:
       +-.015625
       +test 38:
       +.015625
       +test 39:
       +-.001953125
       +test 40:
       +4.0000000000
       +test 41:
       +8.0000000000
       +EOF
       +
       +$EXEC ../dc <<EOF | diff -u $tmp -
       +[test 1:]pc 2 0^p
       +[test 2:]pc 2 1^p
       +[test 3:]pc 2 3^p
       +[test 4:]pc 2 10^p
       +[test 5:]pc 3 5^p
       +[test 6:]pc 10 6^p
       +[test 7:]pc _2 2^p
       +[test 8:]pc _2 3^p
       +[test 9:]pc _2 4^p
       +[test 10:]pc _2 5^p
       +[test 11:]pc _3 3^p
       +[test 12:]pc _3 4^p
       +[test 13:]pc _10 3^p
       +[test 14:]pc _10 5^p
       +[test 15:]pc _10 6^p
       +[test 16:]pc 0 0^p
       +[test 17:]pc 5 0^p
       +[test 18:]pc _5 0^p
       +[test 19:]pc 100 0^p
       +[test 20:]pc 10k 2 _1^p
       +[test 21:]pc 10k 2 _2^p
       +[test 22:]pc 10k 2 _3^p
       +[test 23:]pc 10k 4 _2^p
       +[test 24:]pc 10k 10 _3^p
       +[test 25:]pc 10k _2 _3^p
       +[test 26:]pc 10k _2 _4^p
       +[test 27:]pc 1.50 2^p
       +[test 28:]pc 1.500 3^p
       +[test 29:]pc .50 2^p
       +[test 30:]pc .500 3^p
       +[test 31:]pc _1.50 2^p
       +[test 32:]pc _1.500 3^p
       +[test 33:]pc 1.2500 2^p
       +[test 34:]pc .2500 2^p
       +[test 35:]pc .250000 3^p
       +[test 36:]pc _.2500 2^p
       +[test 37:]pc _.250000 3^p
       +[test 38:]pc .125000 2^p
       +[test 39:]pc _.125000000 3^p
       +[test 40:]pc 10k .50 _2^p
       +[test 41:]pc 10k .500 _3^p
       +EOF
   DIR diff --git a/tests/0034-dc.sh b/tests/0034-dc.sh
       @@ -0,0 +1,150 @@
       +#!/bin/sh
       +
       +tmp=$$.tmp
       +
       +trap 'rm -f $tmp' EXIT
       +trap 'exit $?' HUP INT TERM
       +
       +cat <<EOF >$tmp
       +test 1:
       +0
       +test 2:
       +1
       +test 3:
       +2
       +test 4:
       +3
       +test 5:
       +5
       +test 6:
       +6
       +test 7:
       +7
       +test 8:
       +9
       +test 9:
       +10
       +test 10:
       +1
       +test 11:
       +1
       +test 12:
       +1.41
       +test 13:
       +1.4142
       +test 14:
       +1.414213
       +test 15:
       +1.7
       +test 16:
       +1.732
       +test 17:
       +1.73205
       +test 18:
       +.50
       +test 19:
       +.2500
       +test 20:
       +.10
       +test 21:
       +.0100
       +test 22:
       +.001000
       +test 23:
       +.7
       +test 24:
       +.353
       +test 25:
       +.3
       +test 26:
       +.316
       +test 27:
       +.31622
       +test 28:
       +.0316
       +test 29:
       +1.20
       +test 30:
       +1.5000
       +test 31:
       +1.22
       +test 32:
       +1.2247
       +test 33:
       +1.110
       +test 34:
       +1.11085
       +test 35:
       +.9486
       +test 36:
       +.999499
       +test 37:
       +1.58
       +test 38:
       +3.5128
       +test 39:
       +2.0
       +test 40:
       +2.00
       +test 41:
       +2.000
       +test 42:
       +2.0000000000
       +test 43:
       +100.0000
       +test 44:
       +11.111075
       +test 45:
       +100000000
       +test 46:
       +9999
       +EOF
       +
       +$EXEC ../dc <<EOF | diff -u $tmp -
       +[test 1:]pc 0k 0vp
       +[test 2:]pc 0k 1vp
       +[test 3:]pc 0k 4vp
       +[test 4:]pc 0k 9vp
       +[test 5:]pc 0k 25vp
       +[test 6:]pc 0k 36vp
       +[test 7:]pc 0k 49vp
       +[test 8:]pc 0k 81vp
       +[test 9:]pc 0k 100vp
       +[test 10:]pc 0k 2vp
       +[test 11:]pc 0k 3vp
       +[test 12:]pc 2k 2vp
       +[test 13:]pc 4k 2vp
       +[test 14:]pc 6k 2vp
       +[test 15:]pc 1k 3vp
       +[test 16:]pc 3k 3vp
       +[test 17:]pc 5k 3vp
       +[test 18:]pc 2k .25vp
       +[test 19:]pc 4k .0625vp
       +[test 20:]pc 2k .01vp
       +[test 21:]pc 4k .0001vp
       +[test 22:]pc 6k .000001vp
       +[test 23:]pc 1k .5vp
       +[test 24:]pc 3k .125vp
       +[test 25:]pc 1k .1vp
       +[test 26:]pc 3k .1vp
       +[test 27:]pc 5k .1vp
       +[test 28:]pc 4k .001vp
       +[test 29:]pc 2k 1.44vp
       +[test 30:]pc 4k 2.25vp
       +[test 31:]pc 2k 1.5vp
       +[test 32:]pc 4k 1.5vp
       +[test 33:]pc 3k 1.234vp
       +[test 34:]pc 5k 1.234vp
       +[test 35:]pc 4k .9vp
       +[test 36:]pc 6k .999vp
       +[test 37:]pc 2k 2.5vp
       +[test 38:]pc 4k 12.34vp
       +[test 39:]pc 1k 4vp
       +[test 40:]pc 2k 4vp
       +[test 41:]pc 3k 4vp
       +[test 42:]pc 10k 4vp
       +[test 43:]pc 4k 10000vp
       +[test 44:]pc 6k 123.456vp
       +[test 45:]pc 0k 10000000000000000vp
       +[test 46:]pc 0k 99980001vp
       +EOF
   DIR diff --git a/tests/0035-dc.sh b/tests/0035-dc.sh
       @@ -0,0 +1,30 @@
       +#!/bin/sh
       +
       +tmp=$$.tmp
       +
       +trap 'rm -f $tmp' EXIT
       +trap 'exit $?' HUP INT TERM
       +
       +# Test negative number sqrt - should produce error message and push 0
       +# Test negative numbers: integers, fractions, odd and even fraction digits
       +$EXEC ../dc <<EOF >$tmp 2>&1
       +[test 1:]pc _1vp
       +[test 2:]pc _4vp
       +[test 3:]pc _.5vp
       +[test 4:]pc _.25vp
       +EOF
       +
       +diff -u - $tmp <<'EOF'
       +../dc: square root of negative number
       +../dc: square root of negative number
       +../dc: square root of negative number
       +../dc: square root of negative number
       +test 1:
       +0
       +test 2:
       +0
       +test 3:
       +0
       +test 4:
       +0
       +EOF
   DIR diff --git a/tests/0036-dc.sh b/tests/0036-dc.sh
       @@ -0,0 +1,64 @@
       +#!/bin/sh
       +
       +set -e
       +
       +tmp=$$.tmp
       +
       +trap 'rm -f $tmp' EXIT
       +trap 'exit $?' HUP INT TERM
       +
       +# Test i, o, k, I, O, K commands
       +cat <<'EOF' >$tmp
       +test 1:
       +10
       +test 2:
       +10
       +test 3:
       +0
       +test 4:
       +16
       +test 5:
       +16
       +10
       +test 6:
       +5
       +test 7:
       +A
       +test 8:
       +FF
       +test 9:
       +10
       +test 10:
       +1010
       +test 11:
       +10
       +test 12:
       +.33333
       +test 13:
       + 12 15
       +test 14:
       + 01 04 19 19
       +test 15:
       + 01.10
       +test 16:
       +.05 00
       +EOF
       +
       +$EXEC ../dc <<'EOF' | diff -u $tmp -
       +[test 1:]pc Ip
       +[test 2:]pc Op
       +[test 3:]pc Kp
       +[test 4:]pc 16i Ip
       +[test 5:]pc Ao Ip Op
       +[test 6:]pc Ai 5k Kp
       +[test 7:]pc 16o 10p
       +[test 8:]pc 255p
       +[test 9:]pc 10o 16i Ap
       +[test 10:]pc Ai 2o 10p
       +[test 11:]pc Ao 2i 1010p
       +[test 12:]pc Ai 5k 1 3/p
       +[test 13:]pc 20o 255p
       +[test 14:]pc 9999p
       +[test 15:]pc 1.5p
       +[test 16:]pc .25p
       +EOF
   DIR diff --git a/tests/0037-dc.sh b/tests/0037-dc.sh
       @@ -0,0 +1,77 @@
       +#!/bin/sh
       +
       +tmp=$$.tmp
       +
       +trap 'rm -f $tmp' EXIT
       +trap 'exit $?' HUP INT TERM
       +
       +# Expected output for z, Z, and X operators
       +cat <<EOF >$tmp
       +test 1:
       +0
       +test 2:
       +1
       +test 3:
       +2
       +test 4:
       +3
       +test 5:
       +5
       +test 6:
       +1
       +test 7:
       +3
       +test 8:
       +2
       +test 9:
       +3
       +test 10:
       +4
       +test 11:
       +1
       +test 12:
       +1
       +test 13:
       +1
       +test 14:
       +1
       +test 15:
       +1
       +test 16:
       +1
       +test 17:
       +0
       +test 18:
       +1
       +test 19:
       +2
       +test 20:
       +3
       +test 21:
       +5
       +EOF
       +
       +# Test z (stack depth), Z (digit count/string length), X (scale)
       +$EXEC ../dc <<EOF | diff -u $tmp -
       +[test 1:]pc zp c
       +[test 2:]pc 1 zp c
       +[test 3:]pc 1 2 zp c
       +[test 4:]pc 1 2 3 zp c
       +[test 5:]pc 12345Zp c
       +[test 6:]pc 0Zp c
       +[test 7:]pc 123Zp c
       +[test 8:]pc 1.5Zp c
       +[test 9:]pc 1.23Zp c
       +[test 10:]pc 1.001Zp c
       +[test 11:]pc 0.5Zp c
       +[test 12:]pc 0.05Zp c
       +[test 13:]pc 0.005Zp c
       +[test 14:]pc .5Zp c
       +[test 15:]pc .05Zp c
       +[test 16:]pc .005Zp c
       +[test 17:]pc 0Xp c
       +[test 18:]pc 1.2Xp c
       +[test 19:]pc 1.23Xp c
       +[test 20:]pc 1.234Xp c
       +[test 21:]pc [hello]Zp c
       +EOF
   DIR diff --git a/tests/0038-dc.sh b/tests/0038-dc.sh
       @@ -0,0 +1,72 @@
       +#!/bin/sh
       +
       +tmp=$$.tmp
       +
       +trap 'rm -f $tmp' EXIT
       +trap 'exit $?' HUP INT TERM
       +
       +# Expected output for f, c, d, and r operators
       +cat <<EOF >$tmp
       +test 1:
       +test 2:
       +3
       +2
       +1
       +test 3:
       +0
       +test 4:
       +5
       +5
       +test 5:
       +3
       +3
       +2
       +1
       +test 6:
       +2
       +1
       +test 7:
       +2
       +3
       +1
       +test 8:
       +10
       +test 9:
       +1
       +test 10:
       +15
       +test 11:
       +test 12:
       +1
       +1
       +1
       +1
       +test 13:
       +-5
       +-5
       +test 14:
       +1.5
       +1.5
       +test 15:
       +2
       +3
       +1
       +EOF
       +
       +$EXEC ../dc <<EOF | diff -u $tmp -
       +[test 1:]pc f
       +[test 2:]pc 1 2 3 f c
       +[test 3:]pc 1 2 3 c zp c
       +[test 4:]pc 5 d f c
       +[test 5:]pc 1 2 3 d f c
       +[test 6:]pc 2 1 r f c
       +[test 7:]pc 1 2 3 r f c
       +[test 8:]pc 5 d +p c
       +[test 9:]pc 1 2 r -p c
       +[test 10:]pc 5 d d + +p c
       +[test 11:]pc 1 2 3 c f
       +[test 12:]pc 1 d d d f c
       +[test 13:]pc _5 d f c
       +[test 14:]pc 1.5 d f c
       +[test 15:]pc 1 2 3 r f c
       +EOF
   DIR diff --git a/tests/0039-dc.sh b/tests/0039-dc.sh
       @@ -0,0 +1,101 @@
       +#!/bin/sh
       +
       +tmp=$$.tmp
       +
       +trap 'rm -f $tmp' EXIT
       +trap 'exit $?' HUP INT TERM
       +
       +# Test s, l, S, L register commands
       +$EXEC ../dc <<'EOF' >$tmp 2>&1
       +[test 1:]pc 5 sa la p c
       +[test 2:]pc lz p c
       +[test 3:]pc 1 sb 2 lb p c
       +[test 4:]pc 1 sc 2 sc lc p c
       +[test 5:]pc 1 sd ld ld +p c
       +[test 6:]pc 5 Se le p c
       +[test 7:]pc 1 Sf 2 Sf 3 Sf lf p c
       +[test 8:]pc 1 Sg 2 Sg Lg p c
       +[test 9:]pc 1 Sh 2 Sh Lh Lh +p c
       +[test 10:]pc 1 Si Li p c
       +[test 11:]pc 1 sj 2 Sj 3 Sj Lj Lj lj p c
       +[test 12:]pc _42 sk lk p c
       +[test 13:]pc 1.5 sl ll p c
       +[test 14:]pc 99999999999999999999 sm lm p c
       +[test 15:]pc [hello] sn ln p c
       +[test 16:]pc 1 so 2 sp lo lp +p c
       +[test 17:]pc 1 Sq 2 Sr Lq Lr +p c
       +[test 18:]pc 1 St 2 St 3 St Lt p Lt p Lt p c
       +[test 19:]pc 1 2 3 Su Su Su Lu Lu Lu + +p c
       +[test 20:]pc 1 sv lv lv lv + +p c
       +[test 21:]pc 1 Sw 2 Sw 3 Sw 4 Sw 5 Sw Lw p Lw p Lw p Lw p Lw p c
       +[test 22:]pc 1 Sx 2 Sy 3 Sx 4 Sy Lx Ly * Lx Ly * +p c
       +[test 23:]pc 42 s0 100 S0 L0 p L0 p c
       +[test 24:]pc LA
       +[test 25:]pc 1 SB LB LB
       +[test 26:]pc sC
       +[test 27:]pc SD
       +EOF
       +
       +diff -u - $tmp <<'EOF'
       +../dc: stack register 'A' (101) is empty
       +../dc: stack register 'B' (102) is empty
       +../dc: stack empty
       +../dc: stack empty
       +test 1:
       +5
       +test 2:
       +0
       +test 3:
       +1
       +test 4:
       +2
       +test 5:
       +2
       +test 6:
       +5
       +test 7:
       +3
       +test 8:
       +2
       +test 9:
       +3
       +test 10:
       +1
       +test 11:
       +1
       +test 12:
       +-42
       +test 13:
       +1.5
       +test 14:
       +99999999999999999999
       +test 15:
       +hello
       +test 16:
       +3
       +test 17:
       +3
       +test 18:
       +3
       +2
       +1
       +test 19:
       +6
       +test 20:
       +3
       +test 21:
       +5
       +4
       +3
       +2
       +1
       +test 22:
       +14
       +test 23:
       +100
       +42
       +test 24:
       +test 25:
       +test 26:
       +test 27:
       +EOF
   DIR diff --git a/tests/0040-dc.sh b/tests/0040-dc.sh
       @@ -0,0 +1,135 @@
       +#!/bin/sh
       +
       +tmp=$$.tmp
       +
       +trap 'rm -f $tmp' EXIT
       +trap 'exit $?' HUP INT TERM
       +
       +# Test x, >, !>, <, !<, =, != commands
       +# Note: dc pops values and compares: first_popped OP second_popped
       +# So "3 5 >a" pops 5 then 3, checks 5 > 3 (true)
       +# And "5 3 >a" pops 3 then 5, checks 3 > 5 (false)
       +$EXEC ../dc <<'EOF' >$tmp 2>&1
       +[test 1:]pc [42p]x c
       +[test 2:]pc 5 x p c
       +[test 3:]pc []x c
       +[test 4:]pc [[10p]x]x c
       +[test 5:]pc [[YES]p]sa 3 5 >a c
       +[test 6:]pc [[NO]p]sa 5 3 >a c
       +[test 7:]pc [[NO]p]sa 5 5 >a c
       +[test 8:]pc [[YES]p]sa 5 3 <a c
       +[test 9:]pc [[NO]p]sa 3 5 <a c
       +[test 10:]pc [[NO]p]sa 5 5 <a c
       +[test 11:]pc [[YES]p]sa 5 5 =a c
       +[test 12:]pc [[NO]p]sa 5 3 =a c
       +[test 13:]pc [[NO]p]sa 3 5 !>a c
       +[test 14:]pc [[YES]p]sa 5 3 !>a c
       +[test 15:]pc [[YES]p]sa 5 5 !>a c
       +[test 16:]pc [[NO]p]sa 5 3 !<a c
       +[test 17:]pc [[YES]p]sa 3 5 !<a c
       +[test 18:]pc [[YES]p]sa 5 5 !<a c
       +[test 19:]pc [[YES]p]sa 5 3 !=a c
       +[test 20:]pc [[NO]p]sa 5 5 !=a c
       +[test 21:]pc [[NO]p]sa _3 _5 >a c
       +[test 22:]pc [[YES]p]sa _5 _3 >a c
       +[test 23:]pc [[NO]p]sa 3 _5 >a c
       +[test 24:]pc [[YES]p]sa _3 5 >a c
       +[test 25:]pc [[YES]p]sa 0 0 =a c
       +[test 26:]pc [[YES]p]sa _0 0 =a c
       +[test 27:]pc [[YES]p]sa 1.4 1.5 >a c
       +[test 28:]pc [[YES]p]sa 1.5 1.5 =a c
       +[test 29:]pc [[YES]p]sa 1.5 1.4 <a c
       +[test 30:]pc [[YES]p]sa 99999999999999999998 99999999999999999999 >a c
       +[test 31:]pc [d p 1 - d 0 <a]sa 5 la x c
       +[test 32:]pc [[YES]p]sa [2 2 =a]sb 2 2 =b c
       +[test 33:]pc 99 sa la x p c
       +[test 34:]pc [3p]sa [2p]sb 2 3 >a 3 2 <b c
       +[test 35:]pc [[NO]p]sa 1 2 <a z p c
       +[test 36:]pc [[[[[77p]]]]]x x x x x c
       +[test 37:]pc [[YES]p]sa 2k 1.50 1.5 =a c
       +[test 38:]pc [1p]x [2p]x [3p]x c
       +[test 39:]pc x
       +[test 40:]pc [[NO]p]sa 5 >a
       +[test 41:]pc [[NO]p]sa >a
       +EOF
       +
       +diff -u - $tmp <<'EOF'
       +../dc: stack empty
       +../dc: stack empty
       +../dc: stack empty
       +test 1:
       +42
       +test 2:
       +5
       +test 3:
       +test 4:
       +10
       +test 5:
       +YES
       +test 6:
       +test 7:
       +test 8:
       +YES
       +test 9:
       +test 10:
       +test 11:
       +YES
       +test 12:
       +test 13:
       +test 14:
       +YES
       +test 15:
       +YES
       +test 16:
       +test 17:
       +YES
       +test 18:
       +YES
       +test 19:
       +YES
       +test 20:
       +test 21:
       +test 22:
       +YES
       +test 23:
       +test 24:
       +YES
       +test 25:
       +YES
       +test 26:
       +YES
       +test 27:
       +YES
       +test 28:
       +YES
       +test 29:
       +YES
       +test 30:
       +YES
       +test 31:
       +5
       +4
       +3
       +2
       +1
       +test 32:
       +YES
       +test 33:
       +99
       +test 34:
       +3
       +2
       +test 35:
       +0
       +test 36:
       +77
       +test 37:
       +YES
       +test 38:
       +1
       +2
       +3
       +test 39:
       +test 40:
       +test 41:
       +EOF
   DIR diff --git a/tests/0041-dc.sh b/tests/0041-dc.sh
       @@ -0,0 +1,101 @@
       +#!/bin/sh
       +
       +tmp=$$.tmp
       +
       +trap 'rm -f $tmp' EXIT
       +trap 'exit $?' HUP INT TERM
       +
       +cat <<'EOF' > $tmp
       +../dc: stack empty
       +../dc: Q command argument exceeded string execution depth
       +../dc: Q command requires a number >= 0
       +../dc: Q command argument exceeded string execution depth
       +test 1:
       +test 2:
       +test 3:
       +test 4:
       +test 5:
       +99
       +test 6:
       +1
       +4
       +test 7:
       +in-macro
       +after-macro
       +test 8:
       +inner
       +after-all
       +test 9:
       +before
       +after
       +test 10:
       +not-equal
       +continued
       +test 11:
       +equal
       +continued
       +test 12:
       +3
       +2
       +done
       +test 12a:
       +3
       +done
       +test 13:
       +0
       +1
       +2
       +done
       +test 13a:
       +0
       +done
       +test 14:
       +deep
       +outer
       +final
       +test 15:
       +42
       +test 16:
       +done
       +test 17:
       +first
       +last
       +test 18:
       +before
       +test 19:
       +before-q
       +test 20:
       +equal
       +EOF
       +
       +($EXEC ../dc <<'EOF'
       +[test 1:]pc Q
       +[test 2:]pc 1Q
       +[test 3:]pc  _1Q
       +[test 4:]pc [100Q]x
       +[test 5:]pc 99 [1Q]x p
       +[test 6:]pc [[1p q 2p]x 3p]x 4p
       +[test 7:]pc [[in-macro]p 1Q [not-printed]p]x [after-macro]p
       +[test 8:]pc [[[inner]p 2Q [not1]p]x [not2]p]x [after-all]p
       +[test 9:]pc [before]p 0Q [after]p
       +[test 10:]pc [[equal-quit]p q]sa 5 3 =a [not-equal]p [continued]p
       +[test 11:]pc [[equal-quit]p q]sa 5 5 !=a [equal]p [continued]p
       +[test 12:]pc 3[[p 1- d 2 !>b 1Q]x]sb lbx [done]p
       +[test 12a:]pc 3[[p 1- d 2 >b 1Q]x]sb lbx [done]p
       +[test 13:]pc 0[[p 1+ d 2 !<b 1Q]x]sb lbx [done]p
       +[test 13a:]pc 0[[p 1+ d 2 <b 1Q]x]sb lbx [done]p
       +[test 14:]pc [[[[deep]p 2Q [x]p]x [y]p]x [outer]p]x [final]p
       +[test 15:]pc [[42 q]x [x]p]x p
       +[test 16:]pc [[1Q [not]p]x [done]p]x
       +[test 17:]pc [[[first]p q q q]x [x]p]x [last]p
       +[test 18:]pc [before]p q [after]p
       +EOF
       +
       +$EXEC ../dc <<'EOF'
       +[test 19:]pc [[before-q]p q [after-q]p]x [never]p
       +EOF
       +
       +$EXEC ../dc <<'EOF'
       +[test 20:]pc [[equal]p q]sa 5 5 =a [not-printed]p
       +EOF
       +) 2>&1 | diff -u - $tmp
   DIR diff --git a/tests/0042-dc.sh b/tests/0042-dc.sh
       @@ -0,0 +1,107 @@
       +#!/bin/sh
       +
       +tmp=$$.tmp
       +
       +trap 'rm -f $tmp' EXIT
       +trap 'exit $?' HUP INT TERM
       +
       +# Test : and ; array commands
       +$EXEC ../dc <<'EOF' >$tmp 2>&1
       +[test 1:]pc 42 0:a 0;a p c
       +[test 2:]pc 10 0:b 20 1:b 30 2:b 0;b p 1;b p 2;b p c
       +[test 3:]pc 100 5:c 5;c p c
       +[test 4:]pc _42 0:d 0;d p c
       +[test 5:]pc 1.5 0:e 0;e p c
       +[test 6:]pc 99999999999999999999 0:f 0;f p c
       +[test 7:]pc [hello] 0:g 0;g p c
       +[test 8:]pc 1 0:h 2 0:h 0;h p c
       +[test 9:]pc 5 10:i 10;i p c
       +[test 10:]pc 1 0:j 2 1:j 3 2:j 0;j 1;j + 2;j +p c
       +[test 11:]pc 100 0:k 0;k 0;k *p c
       +[test 12:]pc 7 3:l 3;l 3;l 3;l + +p c
       +[test 13:]pc 1 0:0 2 1:0 0;0 1;0 +p c
       +[test 14:]pc 50 0:m 0;m 2/p c
       +[test 15:]pc 10 0:n 0;n 5 * 2:n 2;n p c
       +[test 16:]pc 42 _1:o
       +[test 17:]pc _1;p
       +[test 18:]pc 100 0:q 1 Sq 0;q p Lq p 0;q p c
       +[test 19:]pc 10 0:r 1 Sr 20 0:r 2 Sr 30 0:r 0;r p Lr p 0;r p Lr p 0;r p c
       +[test 20:]pc 5 0:s 1 Ss 2 Ss Ls p 0;s p Ls p 0;s p c
       +[test 21:]pc 42 0:t 99 st 0;t p lt p c
       +[test 22:]pc 1 0:u 2 1:u 99 Su 50 0:u 0;u p Lu p 0;u p 1;u p c
       +[test 23:]pc 10 0:v 20 1:v 1 Sv 2 Sv Lv p Lv p 0;v p 1;v p c
       +[test 24:]pc 100 5:w 1 Sw 200 5:w 2 Sw 300 5:w 5;w p Lw p 5;w p Lw p 5;w p c
       +EOF
       +
       +diff -u - $tmp <<'EOF'
       +../dc: array index must fit in a positive integer
       +../dc: array index must fit in a positive integer
       +test 1:
       +42
       +test 2:
       +10
       +20
       +30
       +test 3:
       +100
       +test 4:
       +-42
       +test 5:
       +1.5
       +test 6:
       +99999999999999999999
       +test 7:
       +hello
       +test 8:
       +2
       +test 9:
       +5
       +test 10:
       +6
       +test 11:
       +10000
       +test 12:
       +21
       +test 13:
       +3
       +test 14:
       +25
       +test 15:
       +50
       +test 16:
       +test 17:
       +test 18:
       +0
       +1
       +100
       +test 19:
       +30
       +2
       +20
       +1
       +10
       +test 20:
       +2
       +0
       +1
       +5
       +test 21:
       +42
       +99
       +test 22:
       +50
       +99
       +1
       +2
       +test 23:
       +2
       +1
       +10
       +20
       +test 24:
       +300
       +2
       +200
       +1
       +100
       +EOF
   DIR diff --git a/tests/0043-dc.sh b/tests/0043-dc.sh
       @@ -0,0 +1,59 @@
       +#!/bin/sh
       +
       +tmp=$$.tmp
       +
       +trap 'rm -f $tmp' EXIT
       +trap 'exit $?' HUP INT TERM
       +
       +# Test -i flag for extended register names
       +# <n> syntax: n is parsed as decimal and used as register name byte
       +# "str" syntax: str is used as multi-character register name
       +
       +$EXEC ../dc -i <<'EOF' >$tmp 2>&1
       +[test 1:]pc 42 s<65> l<65> p c
       +[test 2:]pc 100 s"foo" l"foo" p c
       +[test 3:]pc 99 s<65> lA p c
       +[test 4:]pc 1 S<66> 2 S<66> L<66> p L<66> p c
       +[test 5:]pc 10 S"bar" 20 S"bar" L"bar" p L"bar" p c
       +[test 6:]pc 5 s<67> lC p c
       +[test 7:]pc 1 s"x" 2 s"xy" 3 s"xyz" l"x" p l"xy" p l"xyz" p c
       +[test 8:]pc 77 s<0> l<0> p c
       +[test 9:]pc 88 s"D" lD p c
       +[test 10:]pc [42p] s<69> l<69> x c
       +[test 11:]pc [99p] s"macro" l"macro" x c
       +[test 12:]pc 1 s<70> 2 s<70> 3 s<70> l<70> p c
       +[test 13:]pc 10 s"reg" 20 s"reg" 30 s"reg" l"reg" p c
       +EOF
       +
       +diff -u - $tmp <<'EOF'
       +test 1:
       +42
       +test 2:
       +100
       +test 3:
       +99
       +test 4:
       +2
       +1
       +test 5:
       +20
       +10
       +test 6:
       +5
       +test 7:
       +1
       +2
       +3
       +test 8:
       +77
       +test 9:
       +88
       +test 10:
       +42
       +test 11:
       +99
       +test 12:
       +3
       +test 13:
       +30
       +EOF
   DIR diff --git a/tests/0044-dc.sh b/tests/0044-dc.sh
       @@ -0,0 +1,36 @@
       +#!/bin/sh
       +
       +set -e
       +
       +tmp=$$.tmp
       +
       +trap 'rm -f $tmp' EXIT
       +trap 'exit $?' HUP INT TERM
       +
       +# Expected output for line wrapping tests (derived from system dc)
       +cat <<'EOF' >$tmp
       +test 1:
       +327339060789614187001318969682759915221664204604306478948329136809613\
       +379640467455488327009232590415715088668412756007100921725654588539305\
       +3328527589376
       +test 2:
       +-32733906078961418700131896968275991522166420460430647894832913680961\
       +337964046745548832700923259041571508866841275600710092172565458853930\
       +53328527589376
       +test 3:
       +.33333333333333333333333333333333333333333333333333333333333333333333\
       +33333333333333333333333333333333
       +test 4:
       +123456789012345678901234567890123456789012345678901234567890123456789
       +test 5:
       +123456789012345678901234567890123456789012345678901234567890123456789\
       +0
       +EOF
       +
       +$EXEC ../dc <<'EOF' | diff -u $tmp -
       +[test 1:]pc 2 500^ p
       +[test 2:]pc 0 2 500^ - p
       +[test 3:]pc 100k 1 3 / p
       +[test 4:]pc 123456789012345678901234567890123456789012345678901234567890123456789 p
       +[test 5:]pc 1234567890123456789012345678901234567890123456789012345678901234567890 p
       +EOF