# define COMMA_SUCCESS ,BC_STATUS_SUCCESS
#endif
-#define STACK_HAS_MORE_THAN(s, n) ((s)->len > ((size_t)(n)))
-#define STACK_HAS_EQUAL_OR_MORE_THAN(s, n) ((s)->len >= ((size_t)(n)))
-
-#define BC_NUM_NEG(n, neg) ((((ssize_t)(n)) ^ -((ssize_t)(neg))) + (neg))
-#define BC_NUM_ONE(n) ((n)->len == 1 && (n)->rdx == 0 && (n)->num[0] == 1)
-#define BC_NUM_INT(n) ((n)->len - (n)->rdx)
-//#define BC_NUM_AREQ(a, b) (BC_MAX((a)->rdx, (b)->rdx) + BC_MAX(BC_NUM_INT(a), BC_NUM_INT(b)) + 1)
-static /*ALWAYS_INLINE*/ size_t BC_NUM_AREQ(BcNum *a, BcNum *b)
-{
- return BC_MAX(a->rdx, b->rdx) + BC_MAX(BC_NUM_INT(a), BC_NUM_INT(b)) + 1;
-}
-//#define BC_NUM_MREQ(a, b, scale) (BC_NUM_INT(a) + BC_NUM_INT(b) + BC_MAX((scale), (a)->rdx + (b)->rdx) + 1)
-static /*ALWAYS_INLINE*/ size_t BC_NUM_MREQ(BcNum *a, BcNum *b, size_t scale)
-{
- return BC_NUM_INT(a) + BC_NUM_INT(b) + BC_MAX(scale, a->rdx + b->rdx) + 1;
-}
-
-typedef void (*BcNumDigitOp)(size_t, size_t, bool) FAST_FUNC;
-
-typedef BC_STATUS (*BcNumBinaryOp)(BcNum *, BcNum *, BcNum *, size_t) FAST_FUNC;
-
-static BC_STATUS zbc_num_binary(BcNum *a, BcNum *b, BcNum *c, size_t scale,
- BcNumBinaryOp op, size_t req);
-static FAST_FUNC BC_STATUS zbc_num_a(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale);
-static FAST_FUNC BC_STATUS zbc_num_s(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale);
-static FAST_FUNC BC_STATUS zbc_num_p(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale);
-static FAST_FUNC BC_STATUS zbc_num_m(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale);
-static FAST_FUNC BC_STATUS zbc_num_d(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale);
-static FAST_FUNC BC_STATUS zbc_num_rem(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale);
-
-static FAST_FUNC BC_STATUS zbc_num_add(BcNum *a, BcNum *b, BcNum *c, size_t scale)
-{
- BcNumBinaryOp op = (!a->neg == !b->neg) ? zbc_num_a : zbc_num_s;
- (void) scale;
- RETURN_STATUS(zbc_num_binary(a, b, c, false, op, BC_NUM_AREQ(a, b)));
-}
-
-static FAST_FUNC BC_STATUS zbc_num_sub(BcNum *a, BcNum *b, BcNum *c, size_t scale)
-{
- BcNumBinaryOp op = (!a->neg == !b->neg) ? zbc_num_s : zbc_num_a;
- (void) scale;
- RETURN_STATUS(zbc_num_binary(a, b, c, true, op, BC_NUM_AREQ(a, b)));
-}
-
-static FAST_FUNC BC_STATUS zbc_num_mul(BcNum *a, BcNum *b, BcNum *c, size_t scale)
-{
- size_t req = BC_NUM_MREQ(a, b, scale);
- RETURN_STATUS(zbc_num_binary(a, b, c, scale, zbc_num_m, req));
-}
-
-static FAST_FUNC BC_STATUS zbc_num_div(BcNum *a, BcNum *b, BcNum *c, size_t scale)
-{
- size_t req = BC_NUM_MREQ(a, b, scale);
- RETURN_STATUS(zbc_num_binary(a, b, c, scale, zbc_num_d, req));
-}
-
-static FAST_FUNC BC_STATUS zbc_num_mod(BcNum *a, BcNum *b, BcNum *c, size_t scale)
-{
- size_t req = BC_NUM_MREQ(a, b, scale);
- RETURN_STATUS(zbc_num_binary(a, b, c, scale, zbc_num_rem, req));
-}
-
-static FAST_FUNC BC_STATUS zbc_num_pow(BcNum *a, BcNum *b, BcNum *c, size_t scale)
-{
- RETURN_STATUS(zbc_num_binary(a, b, c, scale, zbc_num_p, a->len * b->len + 1));
-}
-
-static const BcNumBinaryOp zbc_program_ops[] = {
- zbc_num_pow, zbc_num_mul, zbc_num_div, zbc_num_mod, zbc_num_add, zbc_num_sub,
-};
-#define zbc_num_add(...) (zbc_num_add(__VA_ARGS__) COMMA_SUCCESS)
-#define zbc_num_sub(...) (zbc_num_sub(__VA_ARGS__) COMMA_SUCCESS)
-#define zbc_num_mul(...) (zbc_num_mul(__VA_ARGS__) COMMA_SUCCESS)
-#define zbc_num_div(...) (zbc_num_div(__VA_ARGS__) COMMA_SUCCESS)
-#define zbc_num_mod(...) (zbc_num_mod(__VA_ARGS__) COMMA_SUCCESS)
-#define zbc_num_pow(...) (zbc_num_pow(__VA_ARGS__) COMMA_SUCCESS)
+//
+// Utility routines
+//
static void fflush_and_check(void)
{
}
#endif
-static int bad_input_byte(char c)
-{
- if ((c < ' ' && c != '\t' && c != '\r' && c != '\n') // also allow '\v' '\f'?
- || c > 0x7e
- ) {
- bc_error_fmt("illegal character 0x%02x", c);
- return 1;
- }
- return 0;
-}
-
-// Note: it _appends_ data from fp to vec.
-static void bc_read_line(BcVec *vec, FILE *fp)
-{
- again:
- fflush_and_check();
-
-#if ENABLE_FEATURE_BC_SIGNALS
- if (G_interrupt) { // ^C was pressed
- intr:
- if (fp != stdin) {
- // ^C while running a script (bc SCRIPT): die.
- // We do not return to interactive prompt:
- // user might be running us from a shell,
- // and SCRIPT might be intended to terminate
- // (e.g. contain a "halt" stmt).
- // ^C dropping user into a bc prompt instead of
- // the shell would be unexpected.
- xfunc_die();
- }
- // ^C while interactive input
- G_interrupt = 0;
- // GNU bc says "interrupted execution."
- // GNU dc says "Interrupt!"
- fputs("\ninterrupted execution\n", stderr);
- }
-
-# if ENABLE_FEATURE_EDITING
- if (G_ttyin && fp == stdin) {
- int n, i;
-# define line_buf bb_common_bufsiz1
- n = read_line_input(G.line_input_state, "", line_buf, COMMON_BUFSIZE);
- if (n <= 0) { // read errors or EOF, or ^D, or ^C
- if (n == 0) // ^C
- goto intr;
- bc_vec_pushZeroByte(vec); // ^D or EOF (or error)
- return;
- }
- i = 0;
- for (;;) {
- char c = line_buf[i++];
- if (!c) break;
- if (bad_input_byte(c)) goto again;
- }
- bc_vec_concat(vec, line_buf);
-# undef line_buf
- } else
-# endif
-#endif
- {
- int c;
- bool bad_chars = 0;
- size_t len = vec->len;
-
- do {
-#if ENABLE_FEATURE_BC_SIGNALS
- if (G_interrupt) {
- // ^C was pressed: ignore entire line, get another one
- vec->len = len;
- goto intr;
- }
-#endif
- do c = fgetc(fp); while (c == '\0');
- if (c == EOF) {
- if (ferror(fp))
- bb_perror_msg_and_die("input error");
- // Note: EOF does not append '\n'
- break;
- }
- bad_chars |= bad_input_byte(c);
- bc_vec_pushByte(vec, (char)c);
- } while (c != '\n');
-
- if (bad_chars) {
- // Bad chars on this line
- if (!G.prog.file) { // stdin
- // ignore entire line, get another one
- vec->len = len;
- goto again;
- }
- bb_perror_msg_and_die("file '%s' is not text", G.prog.file);
- }
- bc_vec_pushZeroByte(vec);
- }
-}
-
static void bc_num_setToZero(BcNum *n, size_t scale)
{
n->len = 0;
}
}
+#define BC_NUM_NEG(n, neg) ((((ssize_t)(n)) ^ -((ssize_t)(neg))) + (neg))
+#define BC_NUM_ONE(n) ((n)->len == 1 && (n)->rdx == 0 && (n)->num[0] == 1)
+#define BC_NUM_INT(n) ((n)->len - (n)->rdx)
+//#define BC_NUM_AREQ(a, b) (BC_MAX((a)->rdx, (b)->rdx) + BC_MAX(BC_NUM_INT(a), BC_NUM_INT(b)) + 1)
+static /*ALWAYS_INLINE*/ size_t BC_NUM_AREQ(BcNum *a, BcNum *b)
+{
+ return BC_MAX(a->rdx, b->rdx) + BC_MAX(BC_NUM_INT(a), BC_NUM_INT(b)) + 1;
+}
+//#define BC_NUM_MREQ(a, b, scale) (BC_NUM_INT(a) + BC_NUM_INT(b) + BC_MAX((scale), (a)->rdx + (b)->rdx) + 1)
+static /*ALWAYS_INLINE*/ size_t BC_NUM_MREQ(BcNum *a, BcNum *b, size_t scale)
+{
+ return BC_NUM_INT(a) + BC_NUM_INT(b) + BC_MAX(scale, a->rdx + b->rdx) + 1;
+}
+
static ssize_t bc_num_cmp(BcNum *a, BcNum *b)
{
size_t i, min, a_int, b_int, diff;
}
#define zbc_num_shift(...) (zbc_num_shift(__VA_ARGS__) COMMA_SUCCESS)
-static BC_STATUS zbc_num_inv(BcNum *a, BcNum *b, size_t scale)
+typedef BC_STATUS (*BcNumBinaryOp)(BcNum *, BcNum *, BcNum *, size_t) FAST_FUNC;
+
+static BC_STATUS zbc_num_binary(BcNum *a, BcNum *b, BcNum *c, size_t scale,
+ BcNumBinaryOp op, size_t req)
{
- BcNum one;
- BcDig num[2];
+ BcStatus s;
+ BcNum num2, *ptr_a, *ptr_b;
+ bool init = false;
- one.cap = 2;
- one.num = num;
- bc_num_one(&one);
+ if (c == a) {
+ ptr_a = &num2;
+ memcpy(ptr_a, c, sizeof(BcNum));
+ init = true;
+ } else
+ ptr_a = a;
- RETURN_STATUS(zbc_num_div(&one, a, b, scale));
-}
-#define zbc_num_inv(...) (zbc_num_inv(__VA_ARGS__) COMMA_SUCCESS)
+ if (c == b) {
+ ptr_b = &num2;
+ if (c != a) {
+ memcpy(ptr_b, c, sizeof(BcNum));
+ init = true;
+ }
+ } else
+ ptr_b = b;
-static FAST_FUNC BC_STATUS zbc_num_a(BcNum *a, BcNum *b, BcNum *restrict c, size_t sub)
-{
- BcDig *ptr, *ptr_a, *ptr_b, *ptr_c;
- size_t i, max, min_rdx, min_int, diff, a_int, b_int;
- unsigned carry;
+ if (init)
+ bc_num_init(c, req);
+ else
+ bc_num_expand(c, req);
- // Because this function doesn't need to use scale (per the bc spec),
- // I am hijacking it to say whether it's doing an add or a subtract.
+ s = BC_STATUS_SUCCESS;
+ IF_ERROR_RETURN_POSSIBLE(s =) op(ptr_a, ptr_b, c, scale);
- if (a->len == 0) {
- bc_num_copy(c, b);
- if (sub && c->len) c->neg = !c->neg;
- RETURN_STATUS(BC_STATUS_SUCCESS);
- }
- if (b->len == 0) {
- bc_num_copy(c, a);
- RETURN_STATUS(BC_STATUS_SUCCESS);
- }
+ if (init) bc_num_free(&num2);
- c->neg = a->neg;
- c->rdx = BC_MAX(a->rdx, b->rdx);
- min_rdx = BC_MIN(a->rdx, b->rdx);
- c->len = 0;
+ RETURN_STATUS(s);
+}
+#define zbc_num_binary(...) (zbc_num_binary(__VA_ARGS__) COMMA_SUCCESS)
- if (a->rdx > b->rdx) {
- diff = a->rdx - b->rdx;
- ptr = a->num;
- ptr_a = a->num + diff;
- ptr_b = b->num;
- } else {
- diff = b->rdx - a->rdx;
- ptr = b->num;
- ptr_a = a->num;
+static FAST_FUNC BC_STATUS zbc_num_a(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale);
+static FAST_FUNC BC_STATUS zbc_num_s(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale);
+static FAST_FUNC BC_STATUS zbc_num_p(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale);
+static FAST_FUNC BC_STATUS zbc_num_m(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale);
+static FAST_FUNC BC_STATUS zbc_num_d(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale);
+static FAST_FUNC BC_STATUS zbc_num_rem(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale);
+
+static FAST_FUNC BC_STATUS zbc_num_add(BcNum *a, BcNum *b, BcNum *c, size_t scale)
+{
+ BcNumBinaryOp op = (!a->neg == !b->neg) ? zbc_num_a : zbc_num_s;
+ (void) scale;
+ RETURN_STATUS(zbc_num_binary(a, b, c, false, op, BC_NUM_AREQ(a, b)));
+}
+
+static FAST_FUNC BC_STATUS zbc_num_sub(BcNum *a, BcNum *b, BcNum *c, size_t scale)
+{
+ BcNumBinaryOp op = (!a->neg == !b->neg) ? zbc_num_s : zbc_num_a;
+ (void) scale;
+ RETURN_STATUS(zbc_num_binary(a, b, c, true, op, BC_NUM_AREQ(a, b)));
+}
+
+static FAST_FUNC BC_STATUS zbc_num_mul(BcNum *a, BcNum *b, BcNum *c, size_t scale)
+{
+ size_t req = BC_NUM_MREQ(a, b, scale);
+ RETURN_STATUS(zbc_num_binary(a, b, c, scale, zbc_num_m, req));
+}
+
+static FAST_FUNC BC_STATUS zbc_num_div(BcNum *a, BcNum *b, BcNum *c, size_t scale)
+{
+ size_t req = BC_NUM_MREQ(a, b, scale);
+ RETURN_STATUS(zbc_num_binary(a, b, c, scale, zbc_num_d, req));
+}
+
+static FAST_FUNC BC_STATUS zbc_num_mod(BcNum *a, BcNum *b, BcNum *c, size_t scale)
+{
+ size_t req = BC_NUM_MREQ(a, b, scale);
+ RETURN_STATUS(zbc_num_binary(a, b, c, scale, zbc_num_rem, req));
+}
+
+static FAST_FUNC BC_STATUS zbc_num_pow(BcNum *a, BcNum *b, BcNum *c, size_t scale)
+{
+ RETURN_STATUS(zbc_num_binary(a, b, c, scale, zbc_num_p, a->len * b->len + 1));
+}
+
+static const BcNumBinaryOp zbc_program_ops[] = {
+ zbc_num_pow, zbc_num_mul, zbc_num_div, zbc_num_mod, zbc_num_add, zbc_num_sub,
+};
+#define zbc_num_add(...) (zbc_num_add(__VA_ARGS__) COMMA_SUCCESS)
+#define zbc_num_sub(...) (zbc_num_sub(__VA_ARGS__) COMMA_SUCCESS)
+#define zbc_num_mul(...) (zbc_num_mul(__VA_ARGS__) COMMA_SUCCESS)
+#define zbc_num_div(...) (zbc_num_div(__VA_ARGS__) COMMA_SUCCESS)
+#define zbc_num_mod(...) (zbc_num_mod(__VA_ARGS__) COMMA_SUCCESS)
+#define zbc_num_pow(...) (zbc_num_pow(__VA_ARGS__) COMMA_SUCCESS)
+
+static BC_STATUS zbc_num_inv(BcNum *a, BcNum *b, size_t scale)
+{
+ BcNum one;
+ BcDig num[2];
+
+ one.cap = 2;
+ one.num = num;
+ bc_num_one(&one);
+
+ RETURN_STATUS(zbc_num_div(&one, a, b, scale));
+}
+#define zbc_num_inv(...) (zbc_num_inv(__VA_ARGS__) COMMA_SUCCESS)
+
+static FAST_FUNC BC_STATUS zbc_num_a(BcNum *a, BcNum *b, BcNum *restrict c, size_t sub)
+{
+ BcDig *ptr, *ptr_a, *ptr_b, *ptr_c;
+ size_t i, max, min_rdx, min_int, diff, a_int, b_int;
+ unsigned carry;
+
+ // Because this function doesn't need to use scale (per the bc spec),
+ // I am hijacking it to say whether it's doing an add or a subtract.
+
+ if (a->len == 0) {
+ bc_num_copy(c, b);
+ if (sub && c->len) c->neg = !c->neg;
+ RETURN_STATUS(BC_STATUS_SUCCESS);
+ }
+ if (b->len == 0) {
+ bc_num_copy(c, a);
+ RETURN_STATUS(BC_STATUS_SUCCESS);
+ }
+
+ c->neg = a->neg;
+ c->rdx = BC_MAX(a->rdx, b->rdx);
+ min_rdx = BC_MIN(a->rdx, b->rdx);
+ c->len = 0;
+
+ if (a->rdx > b->rdx) {
+ diff = a->rdx - b->rdx;
+ ptr = a->num;
+ ptr_a = a->num + diff;
+ ptr_b = b->num;
+ } else {
+ diff = b->rdx - a->rdx;
+ ptr = b->num;
+ ptr_a = a->num;
ptr_b = b->num + diff;
}
}
#define zbc_num_p(...) (zbc_num_p(__VA_ARGS__) COMMA_SUCCESS)
-static BC_STATUS zbc_num_binary(BcNum *a, BcNum *b, BcNum *c, size_t scale,
- BcNumBinaryOp op, size_t req)
+static BC_STATUS zbc_num_sqrt(BcNum *a, BcNum *restrict b, size_t scale)
{
BcStatus s;
- BcNum num2, *ptr_a, *ptr_b;
- bool init = false;
+ BcNum num1, num2, half, f, fprime, *x0, *x1, *temp;
+ size_t pow, len, digs, digs1, resrdx, req, times = 0;
+ ssize_t cmp = 1, cmp1 = SSIZE_MAX, cmp2 = SSIZE_MAX;
- if (c == a) {
- ptr_a = &num2;
- memcpy(ptr_a, c, sizeof(BcNum));
- init = true;
- } else
- ptr_a = a;
+ req = BC_MAX(scale, a->rdx) + ((BC_NUM_INT(a) + 1) >> 1) + 1;
+ bc_num_expand(b, req);
- if (c == b) {
- ptr_b = &num2;
- if (c != a) {
- memcpy(ptr_b, c, sizeof(BcNum));
- init = true;
- }
- } else
- ptr_b = b;
+ if (a->len == 0) {
+ bc_num_setToZero(b, scale);
+ RETURN_STATUS(BC_STATUS_SUCCESS);
+ }
+ if (a->neg) {
+ RETURN_STATUS(bc_error("negative number"));
+ }
+ if (BC_NUM_ONE(a)) {
+ bc_num_one(b);
+ bc_num_extend(b, scale);
+ RETURN_STATUS(BC_STATUS_SUCCESS);
+ }
- if (init)
- bc_num_init(c, req);
- else
- bc_num_expand(c, req);
+ scale = BC_MAX(scale, a->rdx) + 1;
+ len = a->len + scale;
- s = BC_STATUS_SUCCESS;
- IF_ERROR_RETURN_POSSIBLE(s =) op(ptr_a, ptr_b, c, scale);
+ bc_num_init(&num1, len);
+ bc_num_init(&num2, len);
+ bc_num_init_DEF_SIZE(&half);
- if (init) bc_num_free(&num2);
+ bc_num_one(&half);
+ half.num[0] = 5;
+ half.rdx = 1;
- RETURN_STATUS(s);
-}
-#define zbc_num_binary(...) (zbc_num_binary(__VA_ARGS__) COMMA_SUCCESS)
+ bc_num_init(&f, len);
+ bc_num_init(&fprime, len);
-static bool bc_num_strValid(const char *val, size_t base)
-{
- BcDig b;
- bool radix;
+ x0 = &num1;
+ x1 = &num2;
- b = (BcDig)(base <= 10 ? base + '0' : base - 10 + 'A');
- radix = false;
- for (;;) {
- BcDig c = *val++;
- if (c == '\0')
- break;
- if (c == '.') {
- if (radix) return false;
- radix = true;
- continue;
- }
- if (c < '0' || c >= b || (c > '9' && c < 'A'))
- return false;
+ bc_num_one(x0);
+ pow = BC_NUM_INT(a);
+
+ if (pow) {
+ if (pow & 1)
+ x0->num[0] = 2;
+ else
+ x0->num[0] = 6;
+
+ pow -= 2 - (pow & 1);
+
+ bc_num_extend(x0, pow);
+
+ // Make sure to move the radix back.
+ x0->rdx -= pow;
}
- return true;
-}
-// Note: n is already "bc_num_zero()"ed,
-// leading zeroes in "val" are removed
-static void bc_num_parseDecimal(BcNum *n, const char *val)
-{
- size_t len, i;
- const char *ptr;
+ x0->rdx = digs = digs1 = 0;
+ resrdx = scale + 2;
+ len = BC_NUM_INT(x0) + resrdx - 1;
- len = strlen(val);
- if (len == 0)
- return;
+ while (cmp != 0 || digs < len) {
+ s = zbc_num_div(a, x0, &f, resrdx);
+ if (s) goto err;
+ s = zbc_num_add(x0, &f, &fprime, resrdx);
+ if (s) goto err;
+ s = zbc_num_mul(&fprime, &half, x1, resrdx);
+ if (s) goto err;
- bc_num_expand(n, len);
+ cmp = bc_num_cmp(x1, x0);
+ digs = x1->len - (unsigned long long) llabs(cmp);
- ptr = strchr(val, '.');
+ if (cmp == cmp2 && digs == digs1)
+ times += 1;
+ else
+ times = 0;
- n->rdx = 0;
- if (ptr != NULL)
- n->rdx = (size_t)((val + len) - (ptr + 1));
+ resrdx += times > 4;
- for (i = 0; val[i]; ++i) {
- if (val[i] != '0' && val[i] != '.') {
- // Not entirely zero value - convert it, and exit
- i = len - 1;
- for (;;) {
- n->num[n->len] = val[i] - '0';
- ++n->len;
- skip_dot:
- if (i == 0) break;
- if (val[--i] == '.') goto skip_dot;
- }
- break;
- }
+ cmp2 = cmp1;
+ cmp1 = cmp;
+ digs1 = digs;
+
+ temp = x0;
+ x0 = x1;
+ x1 = temp;
}
- // if for() exits without hitting if(), the value is entirely zero
+
+ bc_num_copy(b, x0);
+ scale -= 1;
+ if (b->rdx > scale) bc_num_truncate(b, b->rdx - scale);
+ err:
+ bc_num_free(&fprime);
+ bc_num_free(&f);
+ bc_num_free(&half);
+ bc_num_free(&num2);
+ bc_num_free(&num1);
+ RETURN_STATUS(s);
}
-
-// Note: n is already "bc_num_zero()"ed,
-// leading zeroes in "val" are removed
-static void bc_num_parseBase(BcNum *n, const char *val, unsigned base_t)
-{
- BcStatus s;
- BcNum temp, mult, result;
- BcNum base;
- BcDig base_digs[ULONG_NUM_BUFSIZE];
- BcDig c = '\0';
- unsigned long v;
- size_t i, digits;
-
- for (i = 0; ; ++i) {
- if (val[i] == '\0')
- return;
- if (val[i] != '.' && val[i] != '0')
- break;
- }
-
- bc_num_init_DEF_SIZE(&temp);
- bc_num_init_DEF_SIZE(&mult);
- base.cap = ARRAY_SIZE(base_digs);
- base.num = base_digs;
- bc_num_ulong2num(&base, base_t);
-
- for (;;) {
- c = *val++;
- if (c == '\0') goto int_err;
- if (c == '.') break;
-
- v = (unsigned long) (c <= '9' ? c - '0' : c - 'A' + 10);
-
- s = zbc_num_mul(n, &base, &mult, 0);
- if (s) goto int_err;
- bc_num_ulong2num(&temp, v);
- s = zbc_num_add(&mult, &temp, n, 0);
- if (s) goto int_err;
- }
-
- bc_num_init(&result, base.len);
- //bc_num_zero(&result); - already is
- bc_num_one(&mult);
-
- digits = 0;
- for (;;) {
- c = *val++;
- if (c == '\0') break;
- digits++;
-
- v = (unsigned long) (c <= '9' ? c - '0' : c - 'A' + 10);
-
- s = zbc_num_mul(&result, &base, &result, 0);
- if (s) goto err;
- bc_num_ulong2num(&temp, v);
- s = zbc_num_add(&result, &temp, &result, 0);
- if (s) goto err;
- s = zbc_num_mul(&mult, &base, &mult, 0);
- if (s) goto err;
- }
-
- s = zbc_num_div(&result, &mult, &result, digits);
- if (s) goto err;
- s = zbc_num_add(n, &result, n, digits);
- if (s) goto err;
-
- if (n->len != 0) {
- if (n->rdx < digits)
- bc_num_extend(n, digits - n->rdx);
- } else
- bc_num_zero(n);
- err:
- bc_num_free(&result);
- int_err:
- bc_num_free(&mult);
- bc_num_free(&temp);
-}
-
-static BC_STATUS zbc_num_parse(BcNum *n, const char *val, unsigned base_t)
-{
- if (!bc_num_strValid(val, base_t))
- RETURN_STATUS(bc_error("bad number string"));
-
- bc_num_zero(n);
- while (*val == '0') val++;
-
- if (base_t == 10)
- bc_num_parseDecimal(n, val);
- else
- bc_num_parseBase(n, val, base_t);
-
- RETURN_STATUS(BC_STATUS_SUCCESS);
-}
-#define zbc_num_parse(...) (zbc_num_parse(__VA_ARGS__) COMMA_SUCCESS)
-
-static BC_STATUS zbc_num_sqrt(BcNum *a, BcNum *restrict b, size_t scale)
-{
- BcStatus s;
- BcNum num1, num2, half, f, fprime, *x0, *x1, *temp;
- size_t pow, len, digs, digs1, resrdx, req, times = 0;
- ssize_t cmp = 1, cmp1 = SSIZE_MAX, cmp2 = SSIZE_MAX;
-
- req = BC_MAX(scale, a->rdx) + ((BC_NUM_INT(a) + 1) >> 1) + 1;
- bc_num_expand(b, req);
-
- if (a->len == 0) {
- bc_num_setToZero(b, scale);
- RETURN_STATUS(BC_STATUS_SUCCESS);
- }
- if (a->neg) {
- RETURN_STATUS(bc_error("negative number"));
- }
- if (BC_NUM_ONE(a)) {
- bc_num_one(b);
- bc_num_extend(b, scale);
- RETURN_STATUS(BC_STATUS_SUCCESS);
- }
-
- scale = BC_MAX(scale, a->rdx) + 1;
- len = a->len + scale;
-
- bc_num_init(&num1, len);
- bc_num_init(&num2, len);
- bc_num_init_DEF_SIZE(&half);
-
- bc_num_one(&half);
- half.num[0] = 5;
- half.rdx = 1;
-
- bc_num_init(&f, len);
- bc_num_init(&fprime, len);
-
- x0 = &num1;
- x1 = &num2;
-
- bc_num_one(x0);
- pow = BC_NUM_INT(a);
-
- if (pow) {
- if (pow & 1)
- x0->num[0] = 2;
- else
- x0->num[0] = 6;
-
- pow -= 2 - (pow & 1);
-
- bc_num_extend(x0, pow);
-
- // Make sure to move the radix back.
- x0->rdx -= pow;
- }
-
- x0->rdx = digs = digs1 = 0;
- resrdx = scale + 2;
- len = BC_NUM_INT(x0) + resrdx - 1;
-
- while (cmp != 0 || digs < len) {
- s = zbc_num_div(a, x0, &f, resrdx);
- if (s) goto err;
- s = zbc_num_add(x0, &f, &fprime, resrdx);
- if (s) goto err;
- s = zbc_num_mul(&fprime, &half, x1, resrdx);
- if (s) goto err;
-
- cmp = bc_num_cmp(x1, x0);
- digs = x1->len - (unsigned long long) llabs(cmp);
-
- if (cmp == cmp2 && digs == digs1)
- times += 1;
- else
- times = 0;
-
- resrdx += times > 4;
-
- cmp2 = cmp1;
- cmp1 = cmp;
- digs1 = digs;
-
- temp = x0;
- x0 = x1;
- x1 = temp;
- }
-
- bc_num_copy(b, x0);
- scale -= 1;
- if (b->rdx > scale) bc_num_truncate(b, b->rdx - scale);
- err:
- bc_num_free(&fprime);
- bc_num_free(&f);
- bc_num_free(&half);
- bc_num_free(&num2);
- bc_num_free(&num1);
- RETURN_STATUS(s);
-}
-#define zbc_num_sqrt(...) (zbc_num_sqrt(__VA_ARGS__) COMMA_SUCCESS)
+#define zbc_num_sqrt(...) (zbc_num_sqrt(__VA_ARGS__) COMMA_SUCCESS)
static BC_STATUS zbc_num_divmod(BcNum *a, BcNum *b, BcNum *c, BcNum *d,
size_t scale)
bc_num_free(&base);
RETURN_STATUS(s);
}
-#define zdc_num_modexp(...) (zdc_num_modexp(__VA_ARGS__) COMMA_SUCCESS)
-#endif // ENABLE_DC
+#define zdc_num_modexp(...) (zdc_num_modexp(__VA_ARGS__) COMMA_SUCCESS)
+#endif // ENABLE_DC
+
+static FAST_FUNC void bc_string_free(void *string)
+{
+ free(*(char**)string);
+}
+
+static void bc_func_init(BcFunc *f)
+{
+ bc_char_vec_init(&f->code);
+ IF_BC(bc_vec_init(&f->labels, sizeof(size_t), NULL);)
+ IF_BC(bc_vec_init(&f->autos, sizeof(BcId), bc_id_free);)
+ IF_BC(bc_vec_init(&f->strs, sizeof(char *), bc_string_free);)
+ IF_BC(bc_vec_init(&f->consts, sizeof(char *), bc_string_free);)
+ IF_BC(f->nparams = 0;)
+}
+
+static FAST_FUNC void bc_func_free(void *func)
+{
+ BcFunc *f = (BcFunc *) func;
+ bc_vec_free(&f->code);
+ IF_BC(bc_vec_free(&f->labels);)
+ IF_BC(bc_vec_free(&f->autos);)
+ IF_BC(bc_vec_free(&f->strs);)
+ IF_BC(bc_vec_free(&f->consts);)
+}
+
+static void bc_array_expand(BcVec *a, size_t len);
+
+static void bc_array_init(BcVec *a, bool nums)
+{
+ if (nums)
+ bc_vec_init(a, sizeof(BcNum), bc_num_free);
+ else
+ bc_vec_init(a, sizeof(BcVec), bc_vec_free);
+ bc_array_expand(a, 1);
+}
+
+static void bc_array_expand(BcVec *a, size_t len)
+{
+ if (a->dtor == bc_num_free
+ // && a->size == sizeof(BcNum) - always true
+ ) {
+ BcNum n;
+ while (len > a->len) {
+ bc_num_init_DEF_SIZE(&n);
+ bc_vec_push(a, &n);
+ }
+ } else {
+ BcVec v;
+ while (len > a->len) {
+ bc_array_init(&v, true);
+ bc_vec_push(a, &v);
+ }
+ }
+}
+
+static void bc_array_copy(BcVec *d, const BcVec *s)
+{
+ BcNum *dnum, *snum;
+ size_t i;
+
+ bc_vec_pop_all(d);
+ bc_vec_expand(d, s->cap);
+ d->len = s->len;
+
+ dnum = (void*)d->v;
+ snum = (void*)s->v;
+ for (i = 0; i < s->len; i++, dnum++, snum++) {
+ bc_num_init(dnum, snum->len);
+ bc_num_copy(dnum, snum);
+ }
+}
+
+#if ENABLE_DC
+static void dc_result_copy(BcResult *d, BcResult *src)
+{
+ d->t = src->t;
+
+ switch (d->t) {
+ case BC_RESULT_TEMP:
+ case BC_RESULT_IBASE:
+ case BC_RESULT_SCALE:
+ case BC_RESULT_OBASE:
+ bc_num_init(&d->d.n, src->d.n.len);
+ bc_num_copy(&d->d.n, &src->d.n);
+ break;
+ case BC_RESULT_VAR:
+ case BC_RESULT_ARRAY:
+ case BC_RESULT_ARRAY_ELEM:
+ d->d.id.name = xstrdup(src->d.id.name);
+ break;
+ case BC_RESULT_CONSTANT:
+ IF_BC(case BC_RESULT_LAST:)
+ case BC_RESULT_ONE:
+ case BC_RESULT_STR:
+ memcpy(&d->d.n, &src->d.n, sizeof(BcNum));
+ break;
+ }
+}
+#endif // ENABLE_DC
+
+static FAST_FUNC void bc_result_free(void *result)
+{
+ BcResult *r = (BcResult *) result;
+
+ switch (r->t) {
+ case BC_RESULT_TEMP:
+ case BC_RESULT_IBASE:
+ case BC_RESULT_SCALE:
+ case BC_RESULT_OBASE:
+ bc_num_free(&r->d.n);
+ break;
+ case BC_RESULT_VAR:
+ case BC_RESULT_ARRAY:
+ case BC_RESULT_ARRAY_ELEM:
+ free(r->d.id.name);
+ break;
+ default:
+ // Do nothing.
+ break;
+ }
+}
+
+static int bad_input_byte(char c)
+{
+ if ((c < ' ' && c != '\t' && c != '\r' && c != '\n') // also allow '\v' '\f'?
+ || c > 0x7e
+ ) {
+ bc_error_fmt("illegal character 0x%02x", c);
+ return 1;
+ }
+ return 0;
+}
+
+// Note: it _appends_ data from fp to vec.
+static void bc_read_line(BcVec *vec, FILE *fp)
+{
+ again:
+ fflush_and_check();
+
+#if ENABLE_FEATURE_BC_SIGNALS
+ if (G_interrupt) { // ^C was pressed
+ intr:
+ if (fp != stdin) {
+ // ^C while running a script (bc SCRIPT): die.
+ // We do not return to interactive prompt:
+ // user might be running us from a shell,
+ // and SCRIPT might be intended to terminate
+ // (e.g. contain a "halt" stmt).
+ // ^C dropping user into a bc prompt instead of
+ // the shell would be unexpected.
+ xfunc_die();
+ }
+ // ^C while interactive input
+ G_interrupt = 0;
+ // GNU bc says "interrupted execution."
+ // GNU dc says "Interrupt!"
+ fputs("\ninterrupted execution\n", stderr);
+ }
+
+# if ENABLE_FEATURE_EDITING
+ if (G_ttyin && fp == stdin) {
+ int n, i;
+# define line_buf bb_common_bufsiz1
+ n = read_line_input(G.line_input_state, "", line_buf, COMMON_BUFSIZE);
+ if (n <= 0) { // read errors or EOF, or ^D, or ^C
+ if (n == 0) // ^C
+ goto intr;
+ bc_vec_pushZeroByte(vec); // ^D or EOF (or error)
+ return;
+ }
+ i = 0;
+ for (;;) {
+ char c = line_buf[i++];
+ if (!c) break;
+ if (bad_input_byte(c)) goto again;
+ }
+ bc_vec_concat(vec, line_buf);
+# undef line_buf
+ } else
+# endif
+#endif
+ {
+ int c;
+ bool bad_chars = 0;
+ size_t len = vec->len;
+
+ do {
+#if ENABLE_FEATURE_BC_SIGNALS
+ if (G_interrupt) {
+ // ^C was pressed: ignore entire line, get another one
+ vec->len = len;
+ goto intr;
+ }
+#endif
+ do c = fgetc(fp); while (c == '\0');
+ if (c == EOF) {
+ if (ferror(fp))
+ bb_perror_msg_and_die("input error");
+ // Note: EOF does not append '\n'
+ break;
+ }
+ bad_chars |= bad_input_byte(c);
+ bc_vec_pushByte(vec, (char)c);
+ } while (c != '\n');
+
+ if (bad_chars) {
+ // Bad chars on this line
+ if (!G.prog.file) { // stdin
+ // ignore entire line, get another one
+ vec->len = len;
+ goto again;
+ }
+ bb_perror_msg_and_die("file '%s' is not text", G.prog.file);
+ }
+ bc_vec_pushZeroByte(vec);
+ }
+}
+
+//
+// Parsing routines
+//
+
+static bool bc_num_strValid(const char *val, size_t base)
+{
+ BcDig b;
+ bool radix;
+
+ b = (BcDig)(base <= 10 ? base + '0' : base - 10 + 'A');
+ radix = false;
+ for (;;) {
+ BcDig c = *val++;
+ if (c == '\0')
+ break;
+ if (c == '.') {
+ if (radix) return false;
+ radix = true;
+ continue;
+ }
+ if (c < '0' || c >= b || (c > '9' && c < 'A'))
+ return false;
+ }
+ return true;
+}
-#if ENABLE_BC
-static BC_STATUS zbc_func_insert(BcFunc *f, char *name, bool var)
+// Note: n is already "bc_num_zero()"ed,
+// leading zeroes in "val" are removed
+static void bc_num_parseDecimal(BcNum *n, const char *val)
{
- BcId *autoid;
- BcId a;
- size_t i;
+ size_t len, i;
+ const char *ptr;
- autoid = (void*)f->autos.v;
- for (i = 0; i < f->autos.len; i++, autoid++) {
- if (strcmp(name, autoid->name) == 0)
- RETURN_STATUS(bc_error("function parameter or auto var has the same name as another"));
- }
+ len = strlen(val);
+ if (len == 0)
+ return;
- a.idx = var;
- a.name = name;
+ bc_num_expand(n, len);
- bc_vec_push(&f->autos, &a);
+ ptr = strchr(val, '.');
- RETURN_STATUS(BC_STATUS_SUCCESS);
-}
-#define zbc_func_insert(...) (zbc_func_insert(__VA_ARGS__) COMMA_SUCCESS)
-#endif
+ n->rdx = 0;
+ if (ptr != NULL)
+ n->rdx = (size_t)((val + len) - (ptr + 1));
-static FAST_FUNC void bc_string_free(void *string)
-{
- free(*(char**)string);
+ for (i = 0; val[i]; ++i) {
+ if (val[i] != '0' && val[i] != '.') {
+ // Not entirely zero value - convert it, and exit
+ i = len - 1;
+ for (;;) {
+ n->num[n->len] = val[i] - '0';
+ ++n->len;
+ skip_dot:
+ if (i == 0) break;
+ if (val[--i] == '.') goto skip_dot;
+ }
+ break;
+ }
+ }
+ // if for() exits without hitting if(), the value is entirely zero
}
-static void bc_func_init(BcFunc *f)
+// Note: n is already "bc_num_zero()"ed,
+// leading zeroes in "val" are removed
+static void bc_num_parseBase(BcNum *n, const char *val, unsigned base_t)
{
- bc_char_vec_init(&f->code);
- IF_BC(bc_vec_init(&f->labels, sizeof(size_t), NULL);)
- IF_BC(bc_vec_init(&f->autos, sizeof(BcId), bc_id_free);)
- IF_BC(bc_vec_init(&f->strs, sizeof(char *), bc_string_free);)
- IF_BC(bc_vec_init(&f->consts, sizeof(char *), bc_string_free);)
- IF_BC(f->nparams = 0;)
-}
+ BcStatus s;
+ BcNum temp, mult, result;
+ BcNum base;
+ BcDig base_digs[ULONG_NUM_BUFSIZE];
+ BcDig c = '\0';
+ unsigned long v;
+ size_t i, digits;
-static FAST_FUNC void bc_func_free(void *func)
-{
- BcFunc *f = (BcFunc *) func;
- bc_vec_free(&f->code);
- IF_BC(bc_vec_free(&f->labels);)
- IF_BC(bc_vec_free(&f->autos);)
- IF_BC(bc_vec_free(&f->strs);)
- IF_BC(bc_vec_free(&f->consts);)
-}
+ for (i = 0; ; ++i) {
+ if (val[i] == '\0')
+ return;
+ if (val[i] != '.' && val[i] != '0')
+ break;
+ }
-static void bc_array_expand(BcVec *a, size_t len);
+ bc_num_init_DEF_SIZE(&temp);
+ bc_num_init_DEF_SIZE(&mult);
+ base.cap = ARRAY_SIZE(base_digs);
+ base.num = base_digs;
+ bc_num_ulong2num(&base, base_t);
-static void bc_array_init(BcVec *a, bool nums)
-{
- if (nums)
- bc_vec_init(a, sizeof(BcNum), bc_num_free);
- else
- bc_vec_init(a, sizeof(BcVec), bc_vec_free);
- bc_array_expand(a, 1);
-}
+ for (;;) {
+ c = *val++;
+ if (c == '\0') goto int_err;
+ if (c == '.') break;
-static void bc_array_expand(BcVec *a, size_t len)
-{
- if (a->dtor == bc_num_free
- // && a->size == sizeof(BcNum) - always true
- ) {
- BcNum n;
- while (len > a->len) {
- bc_num_init_DEF_SIZE(&n);
- bc_vec_push(a, &n);
- }
- } else {
- BcVec v;
- while (len > a->len) {
- bc_array_init(&v, true);
- bc_vec_push(a, &v);
- }
+ v = (unsigned long) (c <= '9' ? c - '0' : c - 'A' + 10);
+
+ s = zbc_num_mul(n, &base, &mult, 0);
+ if (s) goto int_err;
+ bc_num_ulong2num(&temp, v);
+ s = zbc_num_add(&mult, &temp, n, 0);
+ if (s) goto int_err;
}
-}
-static void bc_array_copy(BcVec *d, const BcVec *s)
-{
- BcNum *dnum, *snum;
- size_t i;
+ bc_num_init(&result, base.len);
+ //bc_num_zero(&result); - already is
+ bc_num_one(&mult);
- bc_vec_pop_all(d);
- bc_vec_expand(d, s->cap);
- d->len = s->len;
+ digits = 0;
+ for (;;) {
+ c = *val++;
+ if (c == '\0') break;
+ digits++;
- dnum = (void*)d->v;
- snum = (void*)s->v;
- for (i = 0; i < s->len; i++, dnum++, snum++) {
- bc_num_init(dnum, snum->len);
- bc_num_copy(dnum, snum);
+ v = (unsigned long) (c <= '9' ? c - '0' : c - 'A' + 10);
+
+ s = zbc_num_mul(&result, &base, &result, 0);
+ if (s) goto err;
+ bc_num_ulong2num(&temp, v);
+ s = zbc_num_add(&result, &temp, &result, 0);
+ if (s) goto err;
+ s = zbc_num_mul(&mult, &base, &mult, 0);
+ if (s) goto err;
}
-}
-#if ENABLE_DC
-static void dc_result_copy(BcResult *d, BcResult *src)
-{
- d->t = src->t;
+ s = zbc_num_div(&result, &mult, &result, digits);
+ if (s) goto err;
+ s = zbc_num_add(n, &result, n, digits);
+ if (s) goto err;
- switch (d->t) {
- case BC_RESULT_TEMP:
- case BC_RESULT_IBASE:
- case BC_RESULT_SCALE:
- case BC_RESULT_OBASE:
- bc_num_init(&d->d.n, src->d.n.len);
- bc_num_copy(&d->d.n, &src->d.n);
- break;
- case BC_RESULT_VAR:
- case BC_RESULT_ARRAY:
- case BC_RESULT_ARRAY_ELEM:
- d->d.id.name = xstrdup(src->d.id.name);
- break;
- case BC_RESULT_CONSTANT:
- IF_BC(case BC_RESULT_LAST:)
- case BC_RESULT_ONE:
- case BC_RESULT_STR:
- memcpy(&d->d.n, &src->d.n, sizeof(BcNum));
- break;
- }
+ if (n->len != 0) {
+ if (n->rdx < digits)
+ bc_num_extend(n, digits - n->rdx);
+ } else
+ bc_num_zero(n);
+ err:
+ bc_num_free(&result);
+ int_err:
+ bc_num_free(&mult);
+ bc_num_free(&temp);
}
-#endif // ENABLE_DC
-static FAST_FUNC void bc_result_free(void *result)
+static BC_STATUS zbc_num_parse(BcNum *n, const char *val, unsigned base_t)
{
- BcResult *r = (BcResult *) result;
+ if (!bc_num_strValid(val, base_t))
+ RETURN_STATUS(bc_error("bad number string"));
- switch (r->t) {
- case BC_RESULT_TEMP:
- case BC_RESULT_IBASE:
- case BC_RESULT_SCALE:
- case BC_RESULT_OBASE:
- bc_num_free(&r->d.n);
- break;
- case BC_RESULT_VAR:
- case BC_RESULT_ARRAY:
- case BC_RESULT_ARRAY_ELEM:
- free(r->d.id.name);
- break;
- default:
- // Do nothing.
- break;
- }
+ bc_num_zero(n);
+ while (*val == '0') val++;
+
+ if (base_t == 10)
+ bc_num_parseDecimal(n, val);
+ else
+ bc_num_parseBase(n, val, base_t);
+
+ RETURN_STATUS(BC_STATUS_SUCCESS);
}
+#define zbc_num_parse(...) (zbc_num_parse(__VA_ARGS__) COMMA_SUCCESS)
static void bc_lex_lineComment(BcLex *l)
{
}
#define zbc_parse_break_or_continue(...) (zbc_parse_break_or_continue(__VA_ARGS__) COMMA_SUCCESS)
+static BC_STATUS zbc_func_insert(BcFunc *f, char *name, bool var)
+{
+ BcId *autoid;
+ BcId a;
+ size_t i;
+
+ autoid = (void*)f->autos.v;
+ for (i = 0; i < f->autos.len; i++, autoid++) {
+ if (strcmp(name, autoid->name) == 0)
+ RETURN_STATUS(bc_error("function parameter or auto var has the same name as another"));
+ }
+
+ a.idx = var;
+ a.name = name;
+
+ bc_vec_push(&f->autos, &a);
+
+ RETURN_STATUS(BC_STATUS_SUCCESS);
+}
+#define zbc_func_insert(...) (zbc_func_insert(__VA_ARGS__) COMMA_SUCCESS)
+
static BC_STATUS zbc_parse_funcdef(BcParse *p)
{
BcStatus s;
#endif // ENABLE_DC
+//
+// Execution engine
+//
+
+#define STACK_HAS_MORE_THAN(s, n) ((s)->len > ((size_t)(n)))
+#define STACK_HAS_EQUAL_OR_MORE_THAN(s, n) ((s)->len >= ((size_t)(n)))
+
static BcVec* bc_program_search(char *id, bool var)
{
BcId e, *ptr;
bc_num_printHex((size_t) n->num[i], 1, i == rdx);
}
+typedef void (*BcNumDigitOp)(size_t, size_t, bool) FAST_FUNC;
+
static BC_STATUS zbc_num_printNum(BcNum *n, unsigned base_t, size_t width, BcNumDigitOp print)
{
BcStatus s;