sbase

suckless unix tools
git clone git://git.suckless.org/sbase
Log | Files | Refs | README | LICENSE

commit 608f88f08fcb049f30a1963f2bc72e429a63ba42
parent a1bf3adbc5f69f236319b99bd0a6aab6138c0014
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:
MMakefile | 1+
Adc.1 | 260+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adc.c | 2199+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atests/0026-dc.sh | 68++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atests/0027-dc.sh | 138+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atests/0028-dc.sh | 139+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atests/0029-dc.sh | 196+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atests/0030-dc.sh | 296+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atests/0031-dc.sh | 218+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atests/0032-dc.sh | 287+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atests/0033-dc.sh | 137+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atests/0034-dc.sh | 150+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atests/0035-dc.sh | 30++++++++++++++++++++++++++++++
Atests/0036-dc.sh | 64++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atests/0037-dc.sh | 77+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atests/0038-dc.sh | 72++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atests/0039-dc.sh | 101+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atests/0040-dc.sh | 135+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atests/0041-dc.sh | 101+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atests/0042-dc.sh | 107+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atests/0043-dc.sh | 59+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atests/0044-dc.sh | 36++++++++++++++++++++++++++++++++++++
22 files changed, 4871 insertions(+), 0 deletions(-)

diff --git a/Makefile b/Makefile @@ -110,6 +110,7 @@ BIN =\ cron\ cut\ date\ + dc\ dd\ dirname\ du\ 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. 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; +} 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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