static void bmod(void) { struct number *a, *b, *r; BN_CTX *ctx; u_int scale; a = pop_number(); if (a == NULL) return; b = pop_number(); if (b == NULL) { push_number(a); return; } r = new_number(); scale = max(a->scale, b->scale); r->scale = scale; if (BN_is_zero(a->number)) warnx("remainder by zero"); else { normalize(a, scale); normalize(b, scale); ctx = BN_CTX_new(); bn_checkp(ctx); bn_check(BN_mod(r->number, b->number, a->number, ctx)); BN_CTX_free(ctx); } push_number(r); free_number(a); free_number(b); }
static void bsub(void) { struct number *a, *b, *r; a = pop_number(); if (a == NULL) return; b = pop_number(); if (b == NULL) { push_number(a); return; } r = new_number(); r->scale = max(a->scale, b->scale); if (r->scale > a->scale) normalize(a, r->scale); else if (r->scale > b->scale) normalize(b, r->scale); bn_check(BN_sub(r->number, b->number, a->number)); push_number(r); free_number(a); free_number(b); }
static bool compare_numbers(enum bcode_compare type, struct number *a, struct number *b) { u_int scale; int cmp; scale = max(a->scale, b->scale); if (scale > a->scale) normalize(a, scale); else if (scale > b->scale) normalize(b, scale); cmp = BN_cmp(a->number, b->number); free_number(a); free_number(b); switch (type) { case BCODE_EQUAL: return (cmp == 0); case BCODE_NOT_EQUAL: return (cmp != 0); case BCODE_LESS: return (cmp < 0); case BCODE_NOT_LESS: return (cmp >= 0); case BCODE_GREATER: return (cmp > 0); case BCODE_NOT_GREATER: return (cmp <= 0); } return (false); }
static void store_array(void) { struct number *inumber; struct value *value; struct stack *stack; u_long idx; int reg; reg = readreg(); if (reg >= 0) { inumber = pop_number(); if (inumber == NULL) return; value = pop(); if (value == NULL) { free_number(inumber); return; } idx = get_ulong(inumber); if (BN_is_negative(inumber->number)) { warnx("negative idx"); stack_free_value(value); } else if (idx == ULONG_MAX || idx > MAX_ARRAY_INDEX) { warnx("idx too big"); stack_free_value(value); } else { stack = &bmachine.reg[reg]; frame_assign(stack, idx, value); } free_number(inumber); } }
static void store_array(void) { int reg; struct number *inumber; u_long index; struct value *value; struct stack *stack; reg = readreg(); if (reg >= 0) { inumber = pop_number(); if (inumber == NULL) return; value = pop(); if (value == NULL) { free_number(inumber); return; } index = get_ulong(inumber); if (BN_cmp(inumber->number, &zero) < 0) { warnx("negative index"); stack_free_value(value); } else if (index == BN_MASK2 || index > MAX_ARRAY_INDEX) { warnx("index too big"); stack_free_value(value); } else { stack = &bmachine.reg[reg]; frame_assign(stack, index, value); } free_number(inumber); } }
static void bdivmod(void) { struct number *a, *b, *frac, *quotient, *rdiv, *remainder; BN_CTX *ctx; u_int scale; a = pop_number(); if (a == NULL) return; b = pop_number(); if (b == NULL) { push_number(a); return; } rdiv = new_number(); quotient = new_number(); remainder = new_number(); scale = max(a->scale, b->scale); rdiv->scale = 0; remainder->scale = scale; quotient->scale = bmachine.scale; scale = max(a->scale, b->scale); if (BN_is_zero(a->number)) warnx("divide by zero"); else { normalize(a, scale); normalize(b, scale); ctx = BN_CTX_new(); bn_checkp(ctx); /* * Unlike other languages' divmod operations, dc is specified * to return the remainder and the full quotient, rather than * the remainder and the floored quotient. bn(3) has no * function to calculate both. So we'll use BN_div to get the * remainder and floored quotient, then calculate the full * quotient from those. * * quotient = rdiv + remainder / divisor */ bn_check(BN_div(rdiv->number, remainder->number, b->number, a->number, ctx)); frac = div_number(remainder, a, bmachine.scale); normalize(rdiv, bmachine.scale); normalize(remainder, scale); bn_check(BN_add(quotient->number, rdiv->number, frac->number)); free_number(frac); BN_CTX_free(ctx); } push_number(quotient); push_number(remainder); free_number(rdiv); free_number(a); free_number(b); }
static void load_array(void) { struct number *inumber, *n; struct stack *stack; struct value *v; struct value copy; u_long idx; int reg; reg = readreg(); if (reg >= 0) { inumber = pop_number(); if (inumber == NULL) return; idx = get_ulong(inumber); if (BN_is_negative(inumber->number)) warnx("negative idx"); else if (idx == ULONG_MAX || idx > MAX_ARRAY_INDEX) warnx("idx too big"); else { stack = &bmachine.reg[reg]; v = frame_retrieve(stack, idx); if (v == NULL || v->type == BCODE_NONE) { n = new_number(); bn_check(BN_zero(n->number)); push_number(n); } else push(stack_dup_value(v, ©)); } free_number(inumber); } }
static void load_array(void) { int reg; struct number *inumber, *n; u_long index; struct stack *stack; struct value *v, copy; reg = readreg(); if (reg >= 0) { inumber = pop_number(); if (inumber == NULL) return; index = get_ulong(inumber); if (BN_cmp(inumber->number, &zero) < 0) warnx("negative index"); else if (index == BN_MASK2 || index > MAX_ARRAY_INDEX) warnx("index too big"); else { stack = &bmachine.reg[reg]; v = frame_retrieve(stack, index); if (v == NULL) { n = new_number(); bn_check(BN_zero(n->number)); push_number(n); } else push(stack_dup_value(v, ©)); } free_number(inumber); } }
static void bdiv(void) { struct number *a, *b, *r; a = pop_number(); if (a == NULL) return; b = pop_number(); if (b == NULL) { push_number(a); return; } r = div_number(b, a, bmachine.scale); push_number(r); free_number(a); free_number(b); }
static void bdivmod(void) { struct number *a, *b; struct number *rdiv, *rmod; u_int scale; BN_CTX *ctx; a = pop_number(); if (a == NULL) { return; } b = pop_number(); if (b == NULL) { push_number(a); return; } rdiv = new_number(); rmod = new_number(); rdiv->scale = bmachine.scale; rmod->scale = max(b->scale, a->scale + bmachine.scale); scale = max(a->scale, b->scale); if (BN_is_zero(a->number)) warnx("divide by zero"); else { normalize(a, scale); normalize(b, scale + bmachine.scale); ctx = BN_CTX_new(); bn_checkp(ctx); bn_check(BN_div(rdiv->number, rmod->number, b->number, a->number, ctx)); BN_CTX_free(ctx); } push_number(rdiv); push_number(rmod); free_number(a); free_number(b); }
static void bmul(void) { struct number *a, *b, *r; a = pop_number(); if (a == NULL) return; b = pop_number(); if (b == NULL) { push_number(a); return; } r = new_number(); bmul_number(r, a, b, bmachine.scale); push_number(r); free_number(a); free_number(b); }
static void bmul(void) { struct number *a, *b; struct number *r; a = pop_number(); if (a == NULL) { return; } b = pop_number(); if (b == NULL) { push_number(a); return; } r = new_number(); bmul_number(r, a, b); push_number(r); free_number(a); free_number(b); }
static u_int count_digits(const struct number *n) { struct number *int_part, *fract_part; u_int i; if (BN_is_zero(n->number)) return n->scale ? n->scale : 1; int_part = new_number(); fract_part = new_number(); fract_part->scale = n->scale; split_number(n, int_part->number, fract_part->number); i = 0; while (!BN_is_zero(int_part->number)) { BN_div_word(int_part->number, 10); i++; } free_number(int_part); free_number(fract_part); return (i + n->scale); }
static void bdiv(void) { struct number *a, *b, *r; BN_CTX *ctx; u_int scale; a = pop_number(); if (a == NULL) { return; } b = pop_number(); if (b == NULL) { push_number(a); return; } r = new_number(); r->scale = bmachine.scale; scale = max(a->scale, b->scale); if (BN_is_zero(a->number)) warnx("divide by zero"); else { normalize(a, scale); normalize(b, scale + r->scale); ctx = BN_CTX_new(); bn_checkp(ctx); bn_check(BN_div(r->number, NULL, b->number, a->number, ctx)); BN_CTX_free(ctx); } push_number(r); free_number(a); free_number(b); }
static void set_obase(void) { struct number *n; u_long base; n = pop_number(); if (n != NULL) { base = get_ulong(n); if (base != ULONG_MAX && base > 1 && base <= UINT_MAX) bmachine.obase = (u_int)base; else warnx("output base must be a number greater than 1"); free_number(n); } }
/* Clear number or string, but leave value itself */ void stack_free_value(struct value *v) { switch (v->type) { case BCODE_NONE: break; case BCODE_NUMBER: free_number(v->u.num); break; case BCODE_STRING: free(v->u.string); break; } array_free(v->array); v->array = NULL; }
static void set_obase(void) { struct number *n; u_long base; n = pop_number(); if (n != NULL) { base = get_ulong(n); if (base != BN_MASK2 && base > 1) bmachine.obase = base; else warnx("output base must be a number greater than 1"); free_number(n); } }
static void bsqrt(void) { struct number *n; struct number *r; BIGNUM *x, *y; u_int scale, onecount; BN_CTX *ctx; onecount = 0; n = pop_number(); if (n == NULL) { return; } if (BN_is_zero(n->number)) { r = new_number(); push_number(r); } else if (BN_is_negative(n->number)) warnx("square root of negative number"); else { scale = max(bmachine.scale, n->scale); normalize(n, 2*scale); x = BN_dup(n->number); bn_checkp(x); bn_check(BN_rshift(x, x, BN_num_bits(x)/2)); y = BN_new(); bn_checkp(y); ctx = BN_CTX_new(); bn_checkp(ctx); for (;;) { bn_checkp(BN_copy(y, x)); bn_check(BN_div(x, NULL, n->number, x, ctx)); bn_check(BN_add(x, x, y)); bn_check(BN_rshift1(x, x)); if (bsqrt_stop(x, y, &onecount)) break; } r = bmalloc(sizeof(*r)); r->scale = scale; r->number = y; BN_free(x); BN_CTX_free(ctx); push_number(r); } free_number(n); }
static void set_ibase(void) { struct number *n; u_long base; n = pop_number(); if (n != NULL) { base = get_ulong(n); if (base != ULONG_MAX && 2 <= base && base <= 16) bmachine.ibase = (u_int)base; else warnx("input base must be a number between 2 and 16 " "(inclusive)"); free_number(n); } }
static void set_scale(void) { struct number *n; u_long scale; n = pop_number(); if (n != NULL) { if (BN_is_negative(n->number)) warnx("scale must be a nonnegative number"); else { scale = get_ulong(n); if (scale != ULONG_MAX && scale <= UINT_MAX) bmachine.scale = (u_int)scale; else warnx("scale too large"); } free_number(n); } }
static void set_scale(void) { struct number *n; u_long scale; n = pop_number(); if (n != NULL) { if (BN_cmp(n->number, &zero) < 0) warnx("scale must be a nonnegative number"); else { scale = get_ulong(n); if (scale != BN_MASK2) bmachine.scale = scale; else warnx("scale too large"); } free_number(n); } }
static void quitN(void) { struct number *n; u_long i; n = pop_number(); if (n == NULL) return; i = get_ulong(n); free_number(n); if (i == ULONG_MAX || i == 0) warnx("Q command requires a number >= 1"); else if (bmachine.readsp < i) warnx("Q command argument exceeded string execution depth"); else { while (i-- > 0) { src_free(); bmachine.readsp--; } } }
void printnumber(FILE *f, const struct number *b, u_int base) { struct number *int_part, *fract_part; int digits; char buf[11]; size_t sz; int i; struct stack stack; char *p; charcount = 0; lastchar = -1; if (BN_is_zero(b->number)) putcharwrap(f, '0'); int_part = new_number(); fract_part = new_number(); fract_part->scale = b->scale; if (base <= 16) digits = 1; else { digits = snprintf(buf, sizeof(buf), "%u", base-1); } split_number(b, int_part->number, fract_part->number); i = 0; stack_init(&stack); while (!BN_is_zero(int_part->number)) { BN_ULONG rem = BN_div_word(int_part->number, base); stack_pushstring(&stack, get_digit(rem, digits, base)); i++; } sz = i; if (BN_cmp(b->number, &zero) < 0) putcharwrap(f, '-'); for (i = 0; i < sz; i++) { p = stack_popstring(&stack); if (base > 16) putcharwrap(f, ' '); printwrap(f, p); free(p); } stack_clear(&stack); if (b->scale > 0) { struct number *num_base; BIGNUM mult, stop; putcharwrap(f, '.'); num_base = new_number(); BN_set_word(num_base->number, base); BN_init(&mult); BN_one(&mult); BN_init(&stop); BN_one(&stop); scale_number(&stop, b->scale); i = 0; while (BN_cmp(&mult, &stop) < 0) { u_long rem; if (i && base > 16) putcharwrap(f, ' '); i = 1; bmul_number(fract_part, fract_part, num_base); split_number(fract_part, int_part->number, NULL); rem = BN_get_word(int_part->number); p = get_digit(rem, digits, base); int_part->scale = 0; normalize(int_part, fract_part->scale); BN_sub(fract_part->number, fract_part->number, int_part->number); printwrap(f, p); free(p); BN_mul_word(&mult, base); } free_number(num_base); BN_free(&mult); BN_free(&stop); } flushwrap(f); free_number(int_part); free_number(fract_part); }
void free_signed_number(SignedNumber *num1) { free_number(num1->LeastSignificantBlock); }
static void bexp(void) { struct number *a, *p; struct number *r; bool neg; u_int scale; p = pop_number(); if (p == NULL) { return; } a = pop_number(); if (a == NULL) { push_number(p); return; } if (p->scale != 0) warnx("Runtime warning: non-zero scale in exponent"); normalize(p, 0); neg = false; if (BN_cmp(p->number, &zero) < 0) { neg = true; negate(p); scale = bmachine.scale; } else { /* Posix bc says min(a.scale * b, max(a.scale, scale) */ u_long b; u_int m; b = BN_get_word(p->number); m = max(a->scale, bmachine.scale); scale = a->scale * (u_int)b; if (scale > m || (a->scale > 0 && (b == BN_MASK2 || b > UINT_MAX))) scale = m; } if (BN_is_zero(p->number)) { r = new_number(); bn_check(BN_one(r->number)); normalize(r, scale); } else { while (!BN_is_bit_set(p->number, 0)) { bmul_number(a, a, a); bn_check(BN_rshift1(p->number, p->number)); } r = dup_number(a); normalize(r, scale); bn_check(BN_rshift1(p->number, p->number)); while (!BN_is_zero(p->number)) { bmul_number(a, a, a); if (BN_is_bit_set(p->number, 0)) bmul_number(r, r, a); bn_check(BN_rshift1(p->number, p->number)); } if (neg) { BN_CTX *ctx; BIGNUM *one; one = BN_new(); bn_checkp(one); bn_check(BN_one(one)); ctx = BN_CTX_new(); bn_checkp(ctx); scale_number(one, r->scale + scale); normalize(r, scale); bn_check(BN_div(r->number, NULL, one, r->number, ctx)); BN_free(one); BN_CTX_free(ctx); } else normalize(r, scale); } push_number(r); free_number(a); free_number(p); }
static void bexp(void) { struct number *a, *p; struct number *r; bool neg; u_int rscale; p = pop_number(); if (p == NULL) return; a = pop_number(); if (a == NULL) { push_number(p); return; } if (p->scale != 0) { BIGNUM *i, *f; i = BN_new(); bn_checkp(i); f = BN_new(); bn_checkp(f); split_number(p, i, f); if (!BN_is_zero(f)) warnx("Runtime warning: non-zero fractional part in exponent"); BN_free(i); BN_free(f); } normalize(p, 0); neg = false; if (BN_is_negative(p->number)) { neg = true; negate(p); rscale = bmachine.scale; } else { /* Posix bc says min(a.scale * b, max(a.scale, scale) */ u_long b; u_int m; b = BN_get_word(p->number); m = max(a->scale, bmachine.scale); rscale = a->scale * (u_int)b; if (rscale > m || (a->scale > 0 && (b == ULONG_MAX || b > UINT_MAX))) rscale = m; } if (BN_is_zero(p->number)) { r = new_number(); bn_check(BN_one(r->number)); normalize(r, rscale); } else { u_int ascale, mscale; ascale = a->scale; while (!BN_is_bit_set(p->number, 0)) { ascale *= 2; bmul_number(a, a, a, ascale); bn_check(BN_rshift1(p->number, p->number)); } r = dup_number(a); bn_check(BN_rshift1(p->number, p->number)); mscale = ascale; while (!BN_is_zero(p->number)) { ascale *= 2; bmul_number(a, a, a, ascale); if (BN_is_bit_set(p->number, 0)) { mscale += ascale; bmul_number(r, r, a, mscale); } bn_check(BN_rshift1(p->number, p->number)); } if (neg) { BN_CTX *ctx; BIGNUM *one; one = BN_new(); bn_checkp(one); bn_check(BN_one(one)); ctx = BN_CTX_new(); bn_checkp(ctx); scale_number(one, r->scale + rscale); if (BN_is_zero(r->number)) warnx("divide by zero"); else bn_check(BN_div(r->number, NULL, one, r->number, ctx)); BN_free(one); BN_CTX_free(ctx); r->scale = rscale; } else normalize(r, rscale); } push_number(r); free_number(a); free_number(p); }