1252884aeSStefan Eßer /* 2252884aeSStefan Eßer * ***************************************************************************** 3252884aeSStefan Eßer * 43aa99676SStefan Eßer * SPDX-License-Identifier: BSD-2-Clause 5252884aeSStefan Eßer * 63aa99676SStefan Eßer * Copyright (c) 2018-2020 Gavin D. Howard and contributors. 7252884aeSStefan Eßer * 8252884aeSStefan Eßer * Redistribution and use in source and binary forms, with or without 9252884aeSStefan Eßer * modification, are permitted provided that the following conditions are met: 10252884aeSStefan Eßer * 11252884aeSStefan Eßer * * Redistributions of source code must retain the above copyright notice, this 12252884aeSStefan Eßer * list of conditions and the following disclaimer. 13252884aeSStefan Eßer * 14252884aeSStefan Eßer * * Redistributions in binary form must reproduce the above copyright notice, 15252884aeSStefan Eßer * this list of conditions and the following disclaimer in the documentation 16252884aeSStefan Eßer * and/or other materials provided with the distribution. 17252884aeSStefan Eßer * 18252884aeSStefan Eßer * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19252884aeSStefan Eßer * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20252884aeSStefan Eßer * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21252884aeSStefan Eßer * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22252884aeSStefan Eßer * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23252884aeSStefan Eßer * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24252884aeSStefan Eßer * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25252884aeSStefan Eßer * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26252884aeSStefan Eßer * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27252884aeSStefan Eßer * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28252884aeSStefan Eßer * POSSIBILITY OF SUCH DAMAGE. 29252884aeSStefan Eßer * 30252884aeSStefan Eßer * ***************************************************************************** 31252884aeSStefan Eßer * 32252884aeSStefan Eßer * Code for the number type. 33252884aeSStefan Eßer * 34252884aeSStefan Eßer */ 35252884aeSStefan Eßer 36252884aeSStefan Eßer #include <assert.h> 37252884aeSStefan Eßer #include <ctype.h> 38252884aeSStefan Eßer #include <stdbool.h> 39252884aeSStefan Eßer #include <stdlib.h> 40252884aeSStefan Eßer #include <string.h> 41252884aeSStefan Eßer #include <setjmp.h> 42252884aeSStefan Eßer #include <limits.h> 43252884aeSStefan Eßer 44252884aeSStefan Eßer #include <status.h> 45252884aeSStefan Eßer #include <num.h> 46252884aeSStefan Eßer #include <rand.h> 47252884aeSStefan Eßer #include <vm.h> 48252884aeSStefan Eßer 49252884aeSStefan Eßer static void bc_num_m(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale); 50252884aeSStefan Eßer 51252884aeSStefan Eßer static inline ssize_t bc_num_neg(size_t n, bool neg) { 52252884aeSStefan Eßer return (((ssize_t) n) ^ -((ssize_t) neg)) + neg; 53252884aeSStefan Eßer } 54252884aeSStefan Eßer 55252884aeSStefan Eßer ssize_t bc_num_cmpZero(const BcNum *n) { 56252884aeSStefan Eßer return bc_num_neg((n)->len != 0, (n)->neg); 57252884aeSStefan Eßer } 58252884aeSStefan Eßer 59252884aeSStefan Eßer static inline size_t bc_num_int(const BcNum *n) { 60252884aeSStefan Eßer return n->len ? n->len - n->rdx : 0; 61252884aeSStefan Eßer } 62252884aeSStefan Eßer 63252884aeSStefan Eßer static void bc_num_expand(BcNum *restrict n, size_t req) { 64252884aeSStefan Eßer 65252884aeSStefan Eßer assert(n != NULL); 66252884aeSStefan Eßer 67252884aeSStefan Eßer req = req >= BC_NUM_DEF_SIZE ? req : BC_NUM_DEF_SIZE; 68252884aeSStefan Eßer 69252884aeSStefan Eßer if (req > n->cap) { 70252884aeSStefan Eßer 71252884aeSStefan Eßer BC_SIG_LOCK; 72252884aeSStefan Eßer 73252884aeSStefan Eßer n->num = bc_vm_realloc(n->num, BC_NUM_SIZE(req)); 74252884aeSStefan Eßer n->cap = req; 75252884aeSStefan Eßer 76252884aeSStefan Eßer BC_SIG_UNLOCK; 77252884aeSStefan Eßer } 78252884aeSStefan Eßer } 79252884aeSStefan Eßer 80252884aeSStefan Eßer static void bc_num_setToZero(BcNum *restrict n, size_t scale) { 81252884aeSStefan Eßer assert(n != NULL); 82252884aeSStefan Eßer n->scale = scale; 83252884aeSStefan Eßer n->len = n->rdx = 0; 84252884aeSStefan Eßer n->neg = false; 85252884aeSStefan Eßer } 86252884aeSStefan Eßer 87252884aeSStefan Eßer static inline void bc_num_zero(BcNum *restrict n) { 88252884aeSStefan Eßer bc_num_setToZero(n, 0); 89252884aeSStefan Eßer } 90252884aeSStefan Eßer 91252884aeSStefan Eßer void bc_num_one(BcNum *restrict n) { 92252884aeSStefan Eßer bc_num_zero(n); 93252884aeSStefan Eßer n->len = 1; 94252884aeSStefan Eßer n->num[0] = 1; 95252884aeSStefan Eßer } 96252884aeSStefan Eßer 97252884aeSStefan Eßer static void bc_num_clean(BcNum *restrict n) { 98252884aeSStefan Eßer 99252884aeSStefan Eßer while (BC_NUM_NONZERO(n) && !n->num[n->len - 1]) n->len -= 1; 100252884aeSStefan Eßer 101252884aeSStefan Eßer if (BC_NUM_ZERO(n)) { 102252884aeSStefan Eßer n->neg = false; 103252884aeSStefan Eßer n->rdx = 0; 104252884aeSStefan Eßer } 105252884aeSStefan Eßer else if (n->len < n->rdx) n->len = n->rdx; 106252884aeSStefan Eßer } 107252884aeSStefan Eßer 108252884aeSStefan Eßer static size_t bc_num_log10(size_t i) { 109252884aeSStefan Eßer size_t len; 110252884aeSStefan Eßer for (len = 1; i; i /= BC_BASE, ++len); 111252884aeSStefan Eßer assert(len - 1 <= BC_BASE_DIGS + 1); 112252884aeSStefan Eßer return len - 1; 113252884aeSStefan Eßer } 114252884aeSStefan Eßer 115252884aeSStefan Eßer static inline size_t bc_num_zeroDigits(const BcDig *n) { 116252884aeSStefan Eßer assert(*n >= 0); 117252884aeSStefan Eßer assert(((size_t) *n) < BC_BASE_POW); 118252884aeSStefan Eßer return BC_BASE_DIGS - bc_num_log10((size_t) *n); 119252884aeSStefan Eßer } 120252884aeSStefan Eßer 121252884aeSStefan Eßer static size_t bc_num_intDigits(const BcNum *n) { 122252884aeSStefan Eßer size_t digits = bc_num_int(n) * BC_BASE_DIGS; 123252884aeSStefan Eßer if (digits > 0) digits -= bc_num_zeroDigits(n->num + n->len - 1); 124252884aeSStefan Eßer return digits; 125252884aeSStefan Eßer } 126252884aeSStefan Eßer 127252884aeSStefan Eßer static size_t bc_num_nonzeroLen(const BcNum *restrict n) { 128252884aeSStefan Eßer size_t i, len = n->len; 129252884aeSStefan Eßer assert(len == n->rdx); 130252884aeSStefan Eßer for (i = len - 1; i < len && !n->num[i]; --i); 131252884aeSStefan Eßer assert(i + 1 > 0); 132252884aeSStefan Eßer return i + 1; 133252884aeSStefan Eßer } 134252884aeSStefan Eßer 135252884aeSStefan Eßer static BcDig bc_num_addDigits(BcDig a, BcDig b, bool *carry) { 136252884aeSStefan Eßer 137252884aeSStefan Eßer assert(((BcBigDig) BC_BASE_POW) * 2 == ((BcDig) BC_BASE_POW) * 2); 138252884aeSStefan Eßer assert(a < BC_BASE_POW); 139252884aeSStefan Eßer assert(b < BC_BASE_POW); 140252884aeSStefan Eßer 141252884aeSStefan Eßer a += b + *carry; 142252884aeSStefan Eßer *carry = (a >= BC_BASE_POW); 143252884aeSStefan Eßer if (*carry) a -= BC_BASE_POW; 144252884aeSStefan Eßer 145252884aeSStefan Eßer assert(a >= 0); 146252884aeSStefan Eßer assert(a < BC_BASE_POW); 147252884aeSStefan Eßer 148252884aeSStefan Eßer return a; 149252884aeSStefan Eßer } 150252884aeSStefan Eßer 151252884aeSStefan Eßer static BcDig bc_num_subDigits(BcDig a, BcDig b, bool *carry) { 152252884aeSStefan Eßer 153252884aeSStefan Eßer assert(a < BC_BASE_POW); 154252884aeSStefan Eßer assert(b < BC_BASE_POW); 155252884aeSStefan Eßer 156252884aeSStefan Eßer b += *carry; 157252884aeSStefan Eßer *carry = (a < b); 158252884aeSStefan Eßer if (*carry) a += BC_BASE_POW; 159252884aeSStefan Eßer 160252884aeSStefan Eßer assert(a - b >= 0); 161252884aeSStefan Eßer assert(a - b < BC_BASE_POW); 162252884aeSStefan Eßer 163252884aeSStefan Eßer return a - b; 164252884aeSStefan Eßer } 165252884aeSStefan Eßer 166252884aeSStefan Eßer static void bc_num_addArrays(BcDig *restrict a, const BcDig *restrict b, 167252884aeSStefan Eßer size_t len) 168252884aeSStefan Eßer { 169252884aeSStefan Eßer size_t i; 170252884aeSStefan Eßer bool carry = false; 171252884aeSStefan Eßer 172252884aeSStefan Eßer for (i = 0; i < len; ++i) a[i] = bc_num_addDigits(a[i], b[i], &carry); 173252884aeSStefan Eßer 174252884aeSStefan Eßer for (; carry; ++i) a[i] = bc_num_addDigits(a[i], 0, &carry); 175252884aeSStefan Eßer } 176252884aeSStefan Eßer 177252884aeSStefan Eßer static void bc_num_subArrays(BcDig *restrict a, const BcDig *restrict b, 178252884aeSStefan Eßer size_t len) 179252884aeSStefan Eßer { 180252884aeSStefan Eßer size_t i; 181252884aeSStefan Eßer bool carry = false; 182252884aeSStefan Eßer 183252884aeSStefan Eßer for (i = 0; i < len; ++i) a[i] = bc_num_subDigits(a[i], b[i], &carry); 184252884aeSStefan Eßer 185252884aeSStefan Eßer for (; carry; ++i) a[i] = bc_num_subDigits(a[i], 0, &carry); 186252884aeSStefan Eßer } 187252884aeSStefan Eßer 188252884aeSStefan Eßer static void bc_num_mulArray(const BcNum *restrict a, BcBigDig b, 189252884aeSStefan Eßer BcNum *restrict c) 190252884aeSStefan Eßer { 191252884aeSStefan Eßer size_t i; 192252884aeSStefan Eßer BcBigDig carry = 0; 193252884aeSStefan Eßer 194252884aeSStefan Eßer assert(b <= BC_BASE_POW); 195252884aeSStefan Eßer 196252884aeSStefan Eßer if (a->len + 1 > c->cap) bc_num_expand(c, a->len + 1); 197252884aeSStefan Eßer 198252884aeSStefan Eßer memset(c->num, 0, BC_NUM_SIZE(c->cap)); 199252884aeSStefan Eßer 200252884aeSStefan Eßer for (i = 0; i < a->len; ++i) { 201252884aeSStefan Eßer BcBigDig in = ((BcBigDig) a->num[i]) * b + carry; 202252884aeSStefan Eßer c->num[i] = in % BC_BASE_POW; 203252884aeSStefan Eßer carry = in / BC_BASE_POW; 204252884aeSStefan Eßer } 205252884aeSStefan Eßer 206252884aeSStefan Eßer assert(carry < BC_BASE_POW); 207252884aeSStefan Eßer c->num[i] = (BcDig) carry; 208252884aeSStefan Eßer c->len = a->len; 209252884aeSStefan Eßer c->len += (carry != 0); 210252884aeSStefan Eßer 211252884aeSStefan Eßer bc_num_clean(c); 212252884aeSStefan Eßer 213252884aeSStefan Eßer assert(!c->neg || BC_NUM_NONZERO(c)); 214252884aeSStefan Eßer assert(c->rdx <= c->len || !c->len); 215252884aeSStefan Eßer assert(!c->len || c->num[c->len - 1] || c->rdx == c->len); 216252884aeSStefan Eßer } 217252884aeSStefan Eßer 218252884aeSStefan Eßer static void bc_num_divArray(const BcNum *restrict a, BcBigDig b, 219252884aeSStefan Eßer BcNum *restrict c, BcBigDig *rem) 220252884aeSStefan Eßer { 221252884aeSStefan Eßer size_t i; 222252884aeSStefan Eßer BcBigDig carry = 0; 223252884aeSStefan Eßer 224252884aeSStefan Eßer assert(c->cap >= a->len); 225252884aeSStefan Eßer 226252884aeSStefan Eßer for (i = a->len - 1; i < a->len; --i) { 227252884aeSStefan Eßer BcBigDig in = ((BcBigDig) a->num[i]) + carry * BC_BASE_POW; 228252884aeSStefan Eßer assert(in / b < BC_BASE_POW); 229252884aeSStefan Eßer c->num[i] = (BcDig) (in / b); 230252884aeSStefan Eßer carry = in % b; 231252884aeSStefan Eßer } 232252884aeSStefan Eßer 233252884aeSStefan Eßer c->len = a->len; 234252884aeSStefan Eßer bc_num_clean(c); 235252884aeSStefan Eßer *rem = carry; 236252884aeSStefan Eßer 237252884aeSStefan Eßer assert(!c->neg || BC_NUM_NONZERO(c)); 238252884aeSStefan Eßer assert(c->rdx <= c->len || !c->len); 239252884aeSStefan Eßer assert(!c->len || c->num[c->len - 1] || c->rdx == c->len); 240252884aeSStefan Eßer } 241252884aeSStefan Eßer 242252884aeSStefan Eßer static ssize_t bc_num_compare(const BcDig *restrict a, const BcDig *restrict b, 243252884aeSStefan Eßer size_t len) 244252884aeSStefan Eßer { 245252884aeSStefan Eßer size_t i; 246252884aeSStefan Eßer BcDig c = 0; 247252884aeSStefan Eßer for (i = len - 1; i < len && !(c = a[i] - b[i]); --i); 248252884aeSStefan Eßer return bc_num_neg(i + 1, c < 0); 249252884aeSStefan Eßer } 250252884aeSStefan Eßer 251252884aeSStefan Eßer ssize_t bc_num_cmp(const BcNum *a, const BcNum *b) { 252252884aeSStefan Eßer 253252884aeSStefan Eßer size_t i, min, a_int, b_int, diff; 254252884aeSStefan Eßer BcDig *max_num, *min_num; 255252884aeSStefan Eßer bool a_max, neg = false; 256252884aeSStefan Eßer ssize_t cmp; 257252884aeSStefan Eßer 258252884aeSStefan Eßer assert(a != NULL && b != NULL); 259252884aeSStefan Eßer 260252884aeSStefan Eßer if (a == b) return 0; 261252884aeSStefan Eßer if (BC_NUM_ZERO(a)) return bc_num_neg(b->len != 0, !b->neg); 262252884aeSStefan Eßer if (BC_NUM_ZERO(b)) return bc_num_cmpZero(a); 263252884aeSStefan Eßer if (a->neg) { 264252884aeSStefan Eßer if (b->neg) neg = true; 265252884aeSStefan Eßer else return -1; 266252884aeSStefan Eßer } 267252884aeSStefan Eßer else if (b->neg) return 1; 268252884aeSStefan Eßer 269252884aeSStefan Eßer a_int = bc_num_int(a); 270252884aeSStefan Eßer b_int = bc_num_int(b); 271252884aeSStefan Eßer a_int -= b_int; 272252884aeSStefan Eßer 273252884aeSStefan Eßer if (a_int) return neg ? -((ssize_t) a_int) : (ssize_t) a_int; 274252884aeSStefan Eßer 275252884aeSStefan Eßer a_max = (a->rdx > b->rdx); 276252884aeSStefan Eßer 277252884aeSStefan Eßer if (a_max) { 278252884aeSStefan Eßer min = b->rdx; 279252884aeSStefan Eßer diff = a->rdx - b->rdx; 280252884aeSStefan Eßer max_num = a->num + diff; 281252884aeSStefan Eßer min_num = b->num; 282252884aeSStefan Eßer } 283252884aeSStefan Eßer else { 284252884aeSStefan Eßer min = a->rdx; 285252884aeSStefan Eßer diff = b->rdx - a->rdx; 286252884aeSStefan Eßer max_num = b->num + diff; 287252884aeSStefan Eßer min_num = a->num; 288252884aeSStefan Eßer } 289252884aeSStefan Eßer 290252884aeSStefan Eßer cmp = bc_num_compare(max_num, min_num, b_int + min); 291252884aeSStefan Eßer 292252884aeSStefan Eßer if (cmp) return bc_num_neg((size_t) cmp, !a_max == !neg); 293252884aeSStefan Eßer 294252884aeSStefan Eßer for (max_num -= diff, i = diff - 1; i < diff; --i) { 295252884aeSStefan Eßer if (max_num[i]) return bc_num_neg(1, !a_max == !neg); 296252884aeSStefan Eßer } 297252884aeSStefan Eßer 298252884aeSStefan Eßer return 0; 299252884aeSStefan Eßer } 300252884aeSStefan Eßer 301252884aeSStefan Eßer void bc_num_truncate(BcNum *restrict n, size_t places) { 302252884aeSStefan Eßer 303252884aeSStefan Eßer size_t places_rdx; 304252884aeSStefan Eßer 305252884aeSStefan Eßer if (!places) return; 306252884aeSStefan Eßer 307252884aeSStefan Eßer places_rdx = n->rdx ? n->rdx - BC_NUM_RDX(n->scale - places) : 0; 308252884aeSStefan Eßer assert(places <= n->scale && (BC_NUM_ZERO(n) || places_rdx <= n->len)); 309252884aeSStefan Eßer 310252884aeSStefan Eßer n->scale -= places; 311252884aeSStefan Eßer n->rdx -= places_rdx; 312252884aeSStefan Eßer 313252884aeSStefan Eßer if (BC_NUM_NONZERO(n)) { 314252884aeSStefan Eßer 315252884aeSStefan Eßer size_t pow; 316252884aeSStefan Eßer 317252884aeSStefan Eßer pow = n->scale % BC_BASE_DIGS; 318252884aeSStefan Eßer pow = pow ? BC_BASE_DIGS - pow : 0; 319252884aeSStefan Eßer pow = bc_num_pow10[pow]; 320252884aeSStefan Eßer 321252884aeSStefan Eßer n->len -= places_rdx; 322252884aeSStefan Eßer memmove(n->num, n->num + places_rdx, BC_NUM_SIZE(n->len)); 323252884aeSStefan Eßer 324252884aeSStefan Eßer // Clear the lower part of the last digit. 325252884aeSStefan Eßer if (BC_NUM_NONZERO(n)) n->num[0] -= n->num[0] % (BcDig) pow; 326252884aeSStefan Eßer 327252884aeSStefan Eßer bc_num_clean(n); 328252884aeSStefan Eßer } 329252884aeSStefan Eßer } 330252884aeSStefan Eßer 331252884aeSStefan Eßer static void bc_num_extend(BcNum *restrict n, size_t places) { 332252884aeSStefan Eßer 333252884aeSStefan Eßer size_t places_rdx; 334252884aeSStefan Eßer 335252884aeSStefan Eßer if (!places) return; 336252884aeSStefan Eßer if (BC_NUM_ZERO(n)) { 337252884aeSStefan Eßer n->scale += places; 338252884aeSStefan Eßer return; 339252884aeSStefan Eßer } 340252884aeSStefan Eßer 341252884aeSStefan Eßer places_rdx = BC_NUM_RDX(places + n->scale) - n->rdx; 342252884aeSStefan Eßer 343252884aeSStefan Eßer if (places_rdx) { 344252884aeSStefan Eßer bc_num_expand(n, bc_vm_growSize(n->len, places_rdx)); 345252884aeSStefan Eßer memmove(n->num + places_rdx, n->num, BC_NUM_SIZE(n->len)); 346252884aeSStefan Eßer memset(n->num, 0, BC_NUM_SIZE(places_rdx)); 347252884aeSStefan Eßer } 348252884aeSStefan Eßer 349252884aeSStefan Eßer n->rdx += places_rdx; 350252884aeSStefan Eßer n->scale += places; 351252884aeSStefan Eßer n->len += places_rdx; 352252884aeSStefan Eßer 353252884aeSStefan Eßer assert(n->rdx == BC_NUM_RDX(n->scale)); 354252884aeSStefan Eßer } 355252884aeSStefan Eßer 356252884aeSStefan Eßer static void bc_num_retireMul(BcNum *restrict n, size_t scale, 357252884aeSStefan Eßer bool neg1, bool neg2) 358252884aeSStefan Eßer { 359252884aeSStefan Eßer if (n->scale < scale) bc_num_extend(n, scale - n->scale); 360252884aeSStefan Eßer else bc_num_truncate(n, n->scale - scale); 361252884aeSStefan Eßer 362252884aeSStefan Eßer bc_num_clean(n); 363252884aeSStefan Eßer if (BC_NUM_NONZERO(n)) n->neg = (!neg1 != !neg2); 364252884aeSStefan Eßer } 365252884aeSStefan Eßer 366252884aeSStefan Eßer static void bc_num_split(const BcNum *restrict n, size_t idx, 367252884aeSStefan Eßer BcNum *restrict a, BcNum *restrict b) 368252884aeSStefan Eßer { 369252884aeSStefan Eßer assert(BC_NUM_ZERO(a)); 370252884aeSStefan Eßer assert(BC_NUM_ZERO(b)); 371252884aeSStefan Eßer 372252884aeSStefan Eßer if (idx < n->len) { 373252884aeSStefan Eßer 374252884aeSStefan Eßer b->len = n->len - idx; 375252884aeSStefan Eßer a->len = idx; 376252884aeSStefan Eßer a->scale = a->rdx = b->scale = b->rdx = 0; 377252884aeSStefan Eßer 378252884aeSStefan Eßer assert(a->cap >= a->len); 379252884aeSStefan Eßer assert(b->cap >= b->len); 380252884aeSStefan Eßer 381252884aeSStefan Eßer memcpy(b->num, n->num + idx, BC_NUM_SIZE(b->len)); 382252884aeSStefan Eßer memcpy(a->num, n->num, BC_NUM_SIZE(idx)); 383252884aeSStefan Eßer 384252884aeSStefan Eßer bc_num_clean(b); 385252884aeSStefan Eßer } 386252884aeSStefan Eßer else bc_num_copy(a, n); 387252884aeSStefan Eßer 388252884aeSStefan Eßer bc_num_clean(a); 389252884aeSStefan Eßer } 390252884aeSStefan Eßer 391252884aeSStefan Eßer static size_t bc_num_shiftZero(BcNum *restrict n) { 392252884aeSStefan Eßer 393252884aeSStefan Eßer size_t i; 394252884aeSStefan Eßer 395252884aeSStefan Eßer assert(!n->rdx || BC_NUM_ZERO(n)); 396252884aeSStefan Eßer 397252884aeSStefan Eßer for (i = 0; i < n->len && !n->num[i]; ++i); 398252884aeSStefan Eßer 399252884aeSStefan Eßer n->len -= i; 400252884aeSStefan Eßer n->num += i; 401252884aeSStefan Eßer 402252884aeSStefan Eßer return i; 403252884aeSStefan Eßer } 404252884aeSStefan Eßer 405252884aeSStefan Eßer static void bc_num_unshiftZero(BcNum *restrict n, size_t places_rdx) { 406252884aeSStefan Eßer n->len += places_rdx; 407252884aeSStefan Eßer n->num -= places_rdx; 408252884aeSStefan Eßer } 409252884aeSStefan Eßer 410252884aeSStefan Eßer static void bc_num_shift(BcNum *restrict n, BcBigDig dig) { 411252884aeSStefan Eßer 412252884aeSStefan Eßer size_t i, len = n->len; 413252884aeSStefan Eßer BcBigDig carry = 0, pow; 414252884aeSStefan Eßer BcDig *ptr = n->num; 415252884aeSStefan Eßer 416252884aeSStefan Eßer assert(dig < BC_BASE_DIGS); 417252884aeSStefan Eßer 418252884aeSStefan Eßer pow = bc_num_pow10[dig]; 419252884aeSStefan Eßer dig = bc_num_pow10[BC_BASE_DIGS - dig]; 420252884aeSStefan Eßer 421252884aeSStefan Eßer for (i = len - 1; i < len; --i) { 422252884aeSStefan Eßer BcBigDig in, temp; 423252884aeSStefan Eßer in = ((BcBigDig) ptr[i]); 424252884aeSStefan Eßer temp = carry * dig; 425252884aeSStefan Eßer carry = in % pow; 426252884aeSStefan Eßer ptr[i] = ((BcDig) (in / pow)) + (BcDig) temp; 427252884aeSStefan Eßer } 428252884aeSStefan Eßer 429252884aeSStefan Eßer assert(!carry); 430252884aeSStefan Eßer } 431252884aeSStefan Eßer 432252884aeSStefan Eßer static void bc_num_shiftLeft(BcNum *restrict n, size_t places) { 433252884aeSStefan Eßer 434252884aeSStefan Eßer BcBigDig dig; 435252884aeSStefan Eßer size_t places_rdx; 436252884aeSStefan Eßer bool shift; 437252884aeSStefan Eßer 438252884aeSStefan Eßer if (!places) return; 439252884aeSStefan Eßer if (places > n->scale) { 440252884aeSStefan Eßer size_t size = bc_vm_growSize(BC_NUM_RDX(places - n->scale), n->len); 441252884aeSStefan Eßer if (size > SIZE_MAX - 1) bc_vm_err(BC_ERROR_MATH_OVERFLOW); 442252884aeSStefan Eßer } 443252884aeSStefan Eßer if (BC_NUM_ZERO(n)) { 444252884aeSStefan Eßer if (n->scale >= places) n->scale -= places; 445252884aeSStefan Eßer else n->scale = 0; 446252884aeSStefan Eßer return; 447252884aeSStefan Eßer } 448252884aeSStefan Eßer 449252884aeSStefan Eßer dig = (BcBigDig) (places % BC_BASE_DIGS); 450252884aeSStefan Eßer shift = (dig != 0); 451252884aeSStefan Eßer places_rdx = BC_NUM_RDX(places); 452252884aeSStefan Eßer 453252884aeSStefan Eßer if (n->scale) { 454252884aeSStefan Eßer 455252884aeSStefan Eßer if (n->rdx >= places_rdx) { 456252884aeSStefan Eßer 457252884aeSStefan Eßer size_t mod = n->scale % BC_BASE_DIGS, revdig; 458252884aeSStefan Eßer 459252884aeSStefan Eßer mod = mod ? mod : BC_BASE_DIGS; 460252884aeSStefan Eßer revdig = dig ? BC_BASE_DIGS - dig : 0; 461252884aeSStefan Eßer 462252884aeSStefan Eßer if (mod + revdig > BC_BASE_DIGS) places_rdx = 1; 463252884aeSStefan Eßer else places_rdx = 0; 464252884aeSStefan Eßer } 465252884aeSStefan Eßer else places_rdx -= n->rdx; 466252884aeSStefan Eßer } 467252884aeSStefan Eßer 468252884aeSStefan Eßer if (places_rdx) { 469252884aeSStefan Eßer bc_num_expand(n, bc_vm_growSize(n->len, places_rdx)); 470252884aeSStefan Eßer memmove(n->num + places_rdx, n->num, BC_NUM_SIZE(n->len)); 471252884aeSStefan Eßer memset(n->num, 0, BC_NUM_SIZE(places_rdx)); 472252884aeSStefan Eßer n->len += places_rdx; 473252884aeSStefan Eßer } 474252884aeSStefan Eßer 475252884aeSStefan Eßer if (places > n->scale) n->scale = n->rdx = 0; 476252884aeSStefan Eßer else { 477252884aeSStefan Eßer n->scale -= places; 478252884aeSStefan Eßer n->rdx = BC_NUM_RDX(n->scale); 479252884aeSStefan Eßer } 480252884aeSStefan Eßer 481252884aeSStefan Eßer if (shift) bc_num_shift(n, BC_BASE_DIGS - dig); 482252884aeSStefan Eßer 483252884aeSStefan Eßer bc_num_clean(n); 484252884aeSStefan Eßer } 485252884aeSStefan Eßer 486252884aeSStefan Eßer static void bc_num_shiftRight(BcNum *restrict n, size_t places) { 487252884aeSStefan Eßer 488252884aeSStefan Eßer BcBigDig dig; 489252884aeSStefan Eßer size_t places_rdx, scale, scale_mod, int_len, expand; 490252884aeSStefan Eßer bool shift; 491252884aeSStefan Eßer 492252884aeSStefan Eßer if (!places) return; 493252884aeSStefan Eßer if (BC_NUM_ZERO(n)) { 494252884aeSStefan Eßer n->scale += places; 495252884aeSStefan Eßer bc_num_expand(n, BC_NUM_RDX(n->scale)); 496252884aeSStefan Eßer return; 497252884aeSStefan Eßer } 498252884aeSStefan Eßer 499252884aeSStefan Eßer dig = (BcBigDig) (places % BC_BASE_DIGS); 500252884aeSStefan Eßer shift = (dig != 0); 501252884aeSStefan Eßer scale = n->scale; 502252884aeSStefan Eßer scale_mod = scale % BC_BASE_DIGS; 503252884aeSStefan Eßer scale_mod = scale_mod ? scale_mod : BC_BASE_DIGS; 504252884aeSStefan Eßer int_len = bc_num_int(n); 505252884aeSStefan Eßer places_rdx = BC_NUM_RDX(places); 506252884aeSStefan Eßer 507252884aeSStefan Eßer if (scale_mod + dig > BC_BASE_DIGS) { 508252884aeSStefan Eßer expand = places_rdx - 1; 509252884aeSStefan Eßer places_rdx = 1; 510252884aeSStefan Eßer } 511252884aeSStefan Eßer else { 512252884aeSStefan Eßer expand = places_rdx; 513252884aeSStefan Eßer places_rdx = 0; 514252884aeSStefan Eßer } 515252884aeSStefan Eßer 516252884aeSStefan Eßer if (expand > int_len) expand -= int_len; 517252884aeSStefan Eßer else expand = 0; 518252884aeSStefan Eßer 519252884aeSStefan Eßer bc_num_extend(n, places_rdx * BC_BASE_DIGS); 520252884aeSStefan Eßer bc_num_expand(n, bc_vm_growSize(expand, n->len)); 521252884aeSStefan Eßer memset(n->num + n->len, 0, BC_NUM_SIZE(expand)); 522252884aeSStefan Eßer n->len += expand; 523252884aeSStefan Eßer n->scale = n->rdx = 0; 524252884aeSStefan Eßer 525252884aeSStefan Eßer if (shift) bc_num_shift(n, dig); 526252884aeSStefan Eßer 527252884aeSStefan Eßer n->scale = scale + places; 528252884aeSStefan Eßer n->rdx = BC_NUM_RDX(n->scale); 529252884aeSStefan Eßer 530252884aeSStefan Eßer bc_num_clean(n); 531252884aeSStefan Eßer 532252884aeSStefan Eßer assert(n->rdx <= n->len && n->len <= n->cap); 533252884aeSStefan Eßer assert(n->rdx == BC_NUM_RDX(n->scale)); 534252884aeSStefan Eßer } 535252884aeSStefan Eßer 536252884aeSStefan Eßer static void bc_num_inv(BcNum *a, BcNum *b, size_t scale) { 537252884aeSStefan Eßer 538252884aeSStefan Eßer BcNum one; 539252884aeSStefan Eßer BcDig num[2]; 540252884aeSStefan Eßer 541252884aeSStefan Eßer assert(BC_NUM_NONZERO(a)); 542252884aeSStefan Eßer 543252884aeSStefan Eßer bc_num_setup(&one, num, sizeof(num) / sizeof(BcDig)); 544252884aeSStefan Eßer bc_num_one(&one); 545252884aeSStefan Eßer 546252884aeSStefan Eßer bc_num_div(&one, a, b, scale); 547252884aeSStefan Eßer } 548252884aeSStefan Eßer 549252884aeSStefan Eßer #if BC_ENABLE_EXTRA_MATH 550252884aeSStefan Eßer static void bc_num_intop(const BcNum *a, const BcNum *b, BcNum *restrict c, 551252884aeSStefan Eßer BcBigDig *v) 552252884aeSStefan Eßer { 553252884aeSStefan Eßer if (BC_ERR(b->rdx)) bc_vm_err(BC_ERROR_MATH_NON_INTEGER); 554252884aeSStefan Eßer bc_num_copy(c, a); 555252884aeSStefan Eßer bc_num_bigdig(b, v); 556252884aeSStefan Eßer } 557252884aeSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH 558252884aeSStefan Eßer 559252884aeSStefan Eßer static void bc_num_as(BcNum *a, BcNum *b, BcNum *restrict c, size_t sub) { 560252884aeSStefan Eßer 561252884aeSStefan Eßer BcDig *ptr_c, *ptr_l, *ptr_r; 562252884aeSStefan Eßer size_t i, min_rdx, max_rdx, diff, a_int, b_int, min_len, max_len, max_int; 563252884aeSStefan Eßer size_t len_l, len_r; 564252884aeSStefan Eßer bool b_neg, do_sub, do_rev_sub, carry; 565252884aeSStefan Eßer 566252884aeSStefan Eßer // Because this function doesn't need to use scale (per the bc spec), 567252884aeSStefan Eßer // I am hijacking it to say whether it's doing an add or a subtract. 568252884aeSStefan Eßer // Convert substraction to addition of negative second operand. 569252884aeSStefan Eßer 570252884aeSStefan Eßer if (BC_NUM_ZERO(b)) { 571252884aeSStefan Eßer bc_num_copy(c, a); 572252884aeSStefan Eßer return; 573252884aeSStefan Eßer } 574252884aeSStefan Eßer if (BC_NUM_ZERO(a)) { 575252884aeSStefan Eßer bc_num_copy(c, b); 576252884aeSStefan Eßer c->neg = (b->neg != sub); 577252884aeSStefan Eßer return; 578252884aeSStefan Eßer } 579252884aeSStefan Eßer 580252884aeSStefan Eßer // Invert sign of b if it is to be subtracted. This operation must 581252884aeSStefan Eßer // preced the tests for any of the operands being zero. 582252884aeSStefan Eßer b_neg = (b->neg != sub); 583252884aeSStefan Eßer 584252884aeSStefan Eßer // Actually add the numbers if their signs are equal, else subtract. 585252884aeSStefan Eßer do_sub = (a->neg != b_neg); 586252884aeSStefan Eßer 587252884aeSStefan Eßer a_int = bc_num_int(a); 588252884aeSStefan Eßer b_int = bc_num_int(b); 589252884aeSStefan Eßer max_int = BC_MAX(a_int, b_int); 590252884aeSStefan Eßer 591252884aeSStefan Eßer min_rdx = BC_MIN(a->rdx, b->rdx); 592252884aeSStefan Eßer max_rdx = BC_MAX(a->rdx, b->rdx); 593252884aeSStefan Eßer diff = max_rdx - min_rdx; 594252884aeSStefan Eßer 595252884aeSStefan Eßer max_len = max_int + max_rdx; 596252884aeSStefan Eßer 597252884aeSStefan Eßer if (do_sub) { 598252884aeSStefan Eßer 599252884aeSStefan Eßer // Check whether b has to be subtracted from a or a from b. 600252884aeSStefan Eßer if (a_int != b_int) do_rev_sub = (a_int < b_int); 601252884aeSStefan Eßer else if (a->rdx > b->rdx) 602252884aeSStefan Eßer do_rev_sub = (bc_num_compare(a->num + diff, b->num, b->len) < 0); 603252884aeSStefan Eßer else 604252884aeSStefan Eßer do_rev_sub = (bc_num_compare(a->num, b->num + diff, a->len) <= 0); 605252884aeSStefan Eßer } 606252884aeSStefan Eßer else { 607252884aeSStefan Eßer 608252884aeSStefan Eßer // The result array of the addition might come out one element 609252884aeSStefan Eßer // longer than the bigger of the operand arrays. 610252884aeSStefan Eßer max_len += 1; 611252884aeSStefan Eßer do_rev_sub = (a_int < b_int); 612252884aeSStefan Eßer } 613252884aeSStefan Eßer 614252884aeSStefan Eßer assert(max_len <= c->cap); 615252884aeSStefan Eßer 616252884aeSStefan Eßer if (do_rev_sub) { 617252884aeSStefan Eßer ptr_l = b->num; 618252884aeSStefan Eßer ptr_r = a->num; 619252884aeSStefan Eßer len_l = b->len; 620252884aeSStefan Eßer len_r = a->len; 621252884aeSStefan Eßer } 622252884aeSStefan Eßer else { 623252884aeSStefan Eßer ptr_l = a->num; 624252884aeSStefan Eßer ptr_r = b->num; 625252884aeSStefan Eßer len_l = a->len; 626252884aeSStefan Eßer len_r = b->len; 627252884aeSStefan Eßer } 628252884aeSStefan Eßer 629252884aeSStefan Eßer ptr_c = c->num; 630252884aeSStefan Eßer carry = false; 631252884aeSStefan Eßer 632252884aeSStefan Eßer if (diff) { 633252884aeSStefan Eßer 634252884aeSStefan Eßer // If the rdx values of the operands do not match, the result will 635252884aeSStefan Eßer // have low end elements that are the positive or negative trailing 636252884aeSStefan Eßer // elements of the operand with higher rdx value. 637252884aeSStefan Eßer if ((a->rdx > b->rdx) != do_rev_sub) { 638252884aeSStefan Eßer 639252884aeSStefan Eßer // !do_rev_sub && a->rdx > b->rdx || do_rev_sub && b->rdx > a->rdx 640252884aeSStefan Eßer // The left operand has BcDig values that need to be copied, 641252884aeSStefan Eßer // either from a or from b (in case of a reversed subtraction). 642252884aeSStefan Eßer memcpy(ptr_c, ptr_l, BC_NUM_SIZE(diff)); 643252884aeSStefan Eßer ptr_l += diff; 644252884aeSStefan Eßer len_l -= diff; 645252884aeSStefan Eßer } 646252884aeSStefan Eßer else { 647252884aeSStefan Eßer 648252884aeSStefan Eßer // The right operand has BcDig values that need to be copied 649252884aeSStefan Eßer // or subtracted from zero (in case of a subtraction). 650252884aeSStefan Eßer if (do_sub) { 651252884aeSStefan Eßer 652252884aeSStefan Eßer // do_sub (do_rev_sub && a->rdx > b->rdx || 653252884aeSStefan Eßer // !do_rev_sub && b->rdx > a->rdx) 654252884aeSStefan Eßer for (i = 0; i < diff; i++) 655252884aeSStefan Eßer ptr_c[i] = bc_num_subDigits(0, ptr_r[i], &carry); 656252884aeSStefan Eßer } 657252884aeSStefan Eßer else { 658252884aeSStefan Eßer 659252884aeSStefan Eßer // !do_sub && b->rdx > a->rdx 660252884aeSStefan Eßer memcpy(ptr_c, ptr_r, BC_NUM_SIZE(diff)); 661252884aeSStefan Eßer } 662252884aeSStefan Eßer 663252884aeSStefan Eßer ptr_r += diff; 664252884aeSStefan Eßer len_r -= diff; 665252884aeSStefan Eßer } 666252884aeSStefan Eßer 667252884aeSStefan Eßer ptr_c += diff; 668252884aeSStefan Eßer } 669252884aeSStefan Eßer 670252884aeSStefan Eßer min_len = BC_MIN(len_l, len_r); 671252884aeSStefan Eßer 672252884aeSStefan Eßer // After dealing with possible low array elements that depend on only one 673252884aeSStefan Eßer // operand, the actual add or subtract can be performed as if the rdx of 674252884aeSStefan Eßer // both operands was the same. 675252884aeSStefan Eßer // Inlining takes care of eliminating constant zero arguments to 676252884aeSStefan Eßer // addDigit/subDigit (checked in disassembly of resulting bc binary 677252884aeSStefan Eßer // compiled with gcc and clang). 678252884aeSStefan Eßer if (do_sub) { 679252884aeSStefan Eßer for (i = 0; i < min_len; ++i) 680252884aeSStefan Eßer ptr_c[i] = bc_num_subDigits(ptr_l[i], ptr_r[i], &carry); 681252884aeSStefan Eßer for (; i < len_l; ++i) ptr_c[i] = bc_num_subDigits(ptr_l[i], 0, &carry); 682252884aeSStefan Eßer } 683252884aeSStefan Eßer else { 684252884aeSStefan Eßer for (i = 0; i < min_len; ++i) 685252884aeSStefan Eßer ptr_c[i] = bc_num_addDigits(ptr_l[i], ptr_r[i], &carry); 686252884aeSStefan Eßer for (; i < len_l; ++i) ptr_c[i] = bc_num_addDigits(ptr_l[i], 0, &carry); 687252884aeSStefan Eßer ptr_c[i] = bc_num_addDigits(0, 0, &carry); 688252884aeSStefan Eßer } 689252884aeSStefan Eßer 690252884aeSStefan Eßer assert(carry == false); 691252884aeSStefan Eßer 692252884aeSStefan Eßer // The result has the same sign as a, unless the operation was a 693252884aeSStefan Eßer // reverse subtraction (b - a). 694252884aeSStefan Eßer c->neg = (a->neg != (do_sub && do_rev_sub)); 695252884aeSStefan Eßer c->len = max_len; 696252884aeSStefan Eßer c->rdx = max_rdx; 697252884aeSStefan Eßer c->scale = BC_MAX(a->scale, b->scale); 698252884aeSStefan Eßer 699252884aeSStefan Eßer bc_num_clean(c); 700252884aeSStefan Eßer } 701252884aeSStefan Eßer 702252884aeSStefan Eßer static void bc_num_m_simp(const BcNum *a, const BcNum *b, BcNum *restrict c) 703252884aeSStefan Eßer { 704252884aeSStefan Eßer size_t i, alen = a->len, blen = b->len, clen; 705252884aeSStefan Eßer BcDig *ptr_a = a->num, *ptr_b = b->num, *ptr_c; 706252884aeSStefan Eßer BcBigDig sum = 0, carry = 0; 707252884aeSStefan Eßer 708252884aeSStefan Eßer assert(sizeof(sum) >= sizeof(BcDig) * 2); 709252884aeSStefan Eßer assert(!a->rdx && !b->rdx); 710252884aeSStefan Eßer 711252884aeSStefan Eßer clen = bc_vm_growSize(alen, blen); 712252884aeSStefan Eßer bc_num_expand(c, bc_vm_growSize(clen, 1)); 713252884aeSStefan Eßer 714252884aeSStefan Eßer ptr_c = c->num; 715252884aeSStefan Eßer memset(ptr_c, 0, BC_NUM_SIZE(c->cap)); 716252884aeSStefan Eßer 717252884aeSStefan Eßer for (i = 0; i < clen; ++i) { 718252884aeSStefan Eßer 719252884aeSStefan Eßer ssize_t sidx = (ssize_t) (i - blen + 1); 720252884aeSStefan Eßer size_t j = (size_t) BC_MAX(0, sidx), k = BC_MIN(i, blen - 1); 721252884aeSStefan Eßer 722252884aeSStefan Eßer for (; j < alen && k < blen; ++j, --k) { 723252884aeSStefan Eßer 724252884aeSStefan Eßer sum += ((BcBigDig) ptr_a[j]) * ((BcBigDig) ptr_b[k]); 725252884aeSStefan Eßer 726252884aeSStefan Eßer if (sum >= ((BcBigDig) BC_BASE_POW) * BC_BASE_POW) { 727252884aeSStefan Eßer carry += sum / BC_BASE_POW; 728252884aeSStefan Eßer sum %= BC_BASE_POW; 729252884aeSStefan Eßer } 730252884aeSStefan Eßer } 731252884aeSStefan Eßer 732252884aeSStefan Eßer if (sum >= BC_BASE_POW) { 733252884aeSStefan Eßer carry += sum / BC_BASE_POW; 734252884aeSStefan Eßer sum %= BC_BASE_POW; 735252884aeSStefan Eßer } 736252884aeSStefan Eßer 737252884aeSStefan Eßer ptr_c[i] = (BcDig) sum; 738252884aeSStefan Eßer assert(ptr_c[i] < BC_BASE_POW); 739252884aeSStefan Eßer sum = carry; 740252884aeSStefan Eßer carry = 0; 741252884aeSStefan Eßer } 742252884aeSStefan Eßer 743252884aeSStefan Eßer // This should always be true because there should be no carry on the last 744252884aeSStefan Eßer // digit; multiplication never goes above the sum of both lengths. 745252884aeSStefan Eßer assert(!sum); 746252884aeSStefan Eßer 747252884aeSStefan Eßer c->len = clen; 748252884aeSStefan Eßer } 749252884aeSStefan Eßer 750252884aeSStefan Eßer static void bc_num_shiftAddSub(BcNum *restrict n, const BcNum *restrict a, 751252884aeSStefan Eßer size_t shift, BcNumShiftAddOp op) 752252884aeSStefan Eßer { 753252884aeSStefan Eßer assert(n->len >= shift + a->len); 754252884aeSStefan Eßer assert(!n->rdx && !a->rdx); 755252884aeSStefan Eßer op(n->num + shift, a->num, a->len); 756252884aeSStefan Eßer } 757252884aeSStefan Eßer 758252884aeSStefan Eßer static void bc_num_k(BcNum *a, BcNum *b, BcNum *restrict c) { 759252884aeSStefan Eßer 760252884aeSStefan Eßer size_t max, max2, total; 761252884aeSStefan Eßer BcNum l1, h1, l2, h2, m2, m1, z0, z1, z2, temp; 762252884aeSStefan Eßer BcDig *digs, *dig_ptr; 763252884aeSStefan Eßer BcNumShiftAddOp op; 764252884aeSStefan Eßer bool aone = BC_NUM_ONE(a); 765252884aeSStefan Eßer 766252884aeSStefan Eßer assert(BC_NUM_ZERO(c)); 767252884aeSStefan Eßer 768252884aeSStefan Eßer if (BC_NUM_ZERO(a) || BC_NUM_ZERO(b)) return; 769252884aeSStefan Eßer if (aone || BC_NUM_ONE(b)) { 770252884aeSStefan Eßer bc_num_copy(c, aone ? b : a); 771252884aeSStefan Eßer if ((aone && a->neg) || b->neg) c->neg = !c->neg; 772252884aeSStefan Eßer return; 773252884aeSStefan Eßer } 774252884aeSStefan Eßer if (a->len < BC_NUM_KARATSUBA_LEN || b->len < BC_NUM_KARATSUBA_LEN) { 775252884aeSStefan Eßer bc_num_m_simp(a, b, c); 776252884aeSStefan Eßer return; 777252884aeSStefan Eßer } 778252884aeSStefan Eßer 779252884aeSStefan Eßer max = BC_MAX(a->len, b->len); 780252884aeSStefan Eßer max = BC_MAX(max, BC_NUM_DEF_SIZE); 781252884aeSStefan Eßer max2 = (max + 1) / 2; 782252884aeSStefan Eßer 783252884aeSStefan Eßer total = bc_vm_arraySize(BC_NUM_KARATSUBA_ALLOCS, max); 784252884aeSStefan Eßer 785252884aeSStefan Eßer BC_SIG_LOCK; 786252884aeSStefan Eßer 787252884aeSStefan Eßer digs = dig_ptr = bc_vm_malloc(BC_NUM_SIZE(total)); 788252884aeSStefan Eßer 789252884aeSStefan Eßer bc_num_setup(&l1, dig_ptr, max); 790252884aeSStefan Eßer dig_ptr += max; 791252884aeSStefan Eßer bc_num_setup(&h1, dig_ptr, max); 792252884aeSStefan Eßer dig_ptr += max; 793252884aeSStefan Eßer bc_num_setup(&l2, dig_ptr, max); 794252884aeSStefan Eßer dig_ptr += max; 795252884aeSStefan Eßer bc_num_setup(&h2, dig_ptr, max); 796252884aeSStefan Eßer dig_ptr += max; 797252884aeSStefan Eßer bc_num_setup(&m1, dig_ptr, max); 798252884aeSStefan Eßer dig_ptr += max; 799252884aeSStefan Eßer bc_num_setup(&m2, dig_ptr, max); 800252884aeSStefan Eßer max = bc_vm_growSize(max, 1); 801252884aeSStefan Eßer bc_num_init(&z0, max); 802252884aeSStefan Eßer bc_num_init(&z1, max); 803252884aeSStefan Eßer bc_num_init(&z2, max); 804252884aeSStefan Eßer max = bc_vm_growSize(max, max) + 1; 805252884aeSStefan Eßer bc_num_init(&temp, max); 806252884aeSStefan Eßer 807252884aeSStefan Eßer BC_SETJMP_LOCKED(err); 808252884aeSStefan Eßer 809252884aeSStefan Eßer BC_SIG_UNLOCK; 810252884aeSStefan Eßer 811252884aeSStefan Eßer bc_num_split(a, max2, &l1, &h1); 812252884aeSStefan Eßer bc_num_split(b, max2, &l2, &h2); 813252884aeSStefan Eßer 814252884aeSStefan Eßer bc_num_expand(c, max); 815252884aeSStefan Eßer c->len = max; 816252884aeSStefan Eßer memset(c->num, 0, BC_NUM_SIZE(c->len)); 817252884aeSStefan Eßer 818252884aeSStefan Eßer bc_num_sub(&h1, &l1, &m1, 0); 819252884aeSStefan Eßer bc_num_sub(&l2, &h2, &m2, 0); 820252884aeSStefan Eßer 821252884aeSStefan Eßer if (BC_NUM_NONZERO(&h1) && BC_NUM_NONZERO(&h2)) { 822252884aeSStefan Eßer 823252884aeSStefan Eßer bc_num_m(&h1, &h2, &z2, 0); 824252884aeSStefan Eßer bc_num_clean(&z2); 825252884aeSStefan Eßer 826252884aeSStefan Eßer bc_num_shiftAddSub(c, &z2, max2 * 2, bc_num_addArrays); 827252884aeSStefan Eßer bc_num_shiftAddSub(c, &z2, max2, bc_num_addArrays); 828252884aeSStefan Eßer } 829252884aeSStefan Eßer 830252884aeSStefan Eßer if (BC_NUM_NONZERO(&l1) && BC_NUM_NONZERO(&l2)) { 831252884aeSStefan Eßer 832252884aeSStefan Eßer bc_num_m(&l1, &l2, &z0, 0); 833252884aeSStefan Eßer bc_num_clean(&z0); 834252884aeSStefan Eßer 835252884aeSStefan Eßer bc_num_shiftAddSub(c, &z0, max2, bc_num_addArrays); 836252884aeSStefan Eßer bc_num_shiftAddSub(c, &z0, 0, bc_num_addArrays); 837252884aeSStefan Eßer } 838252884aeSStefan Eßer 839252884aeSStefan Eßer if (BC_NUM_NONZERO(&m1) && BC_NUM_NONZERO(&m2)) { 840252884aeSStefan Eßer 841252884aeSStefan Eßer bc_num_m(&m1, &m2, &z1, 0); 842252884aeSStefan Eßer bc_num_clean(&z1); 843252884aeSStefan Eßer 844252884aeSStefan Eßer op = (m1.neg != m2.neg) ? bc_num_subArrays : bc_num_addArrays; 845252884aeSStefan Eßer bc_num_shiftAddSub(c, &z1, max2, op); 846252884aeSStefan Eßer } 847252884aeSStefan Eßer 848252884aeSStefan Eßer err: 849252884aeSStefan Eßer BC_SIG_MAYLOCK; 850252884aeSStefan Eßer free(digs); 851252884aeSStefan Eßer bc_num_free(&temp); 852252884aeSStefan Eßer bc_num_free(&z2); 853252884aeSStefan Eßer bc_num_free(&z1); 854252884aeSStefan Eßer bc_num_free(&z0); 855252884aeSStefan Eßer BC_LONGJMP_CONT; 856252884aeSStefan Eßer } 857252884aeSStefan Eßer 858252884aeSStefan Eßer static void bc_num_m(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale) { 859252884aeSStefan Eßer 860252884aeSStefan Eßer BcNum cpa, cpb; 861252884aeSStefan Eßer size_t ascale, bscale, ardx, brdx, azero = 0, bzero = 0, zero, len, rscale; 862252884aeSStefan Eßer 863252884aeSStefan Eßer bc_num_zero(c); 864252884aeSStefan Eßer ascale = a->scale; 865252884aeSStefan Eßer bscale = b->scale; 866252884aeSStefan Eßer scale = BC_MAX(scale, ascale); 867252884aeSStefan Eßer scale = BC_MAX(scale, bscale); 868252884aeSStefan Eßer 869252884aeSStefan Eßer rscale = ascale + bscale; 870252884aeSStefan Eßer scale = BC_MIN(rscale, scale); 871252884aeSStefan Eßer 872252884aeSStefan Eßer if ((a->len == 1 || b->len == 1) && !a->rdx && !b->rdx) { 873252884aeSStefan Eßer 874252884aeSStefan Eßer BcNum *operand; 875252884aeSStefan Eßer BcBigDig dig; 876252884aeSStefan Eßer 877252884aeSStefan Eßer if (a->len == 1) { 878252884aeSStefan Eßer dig = (BcBigDig) a->num[0]; 879252884aeSStefan Eßer operand = b; 880252884aeSStefan Eßer } 881252884aeSStefan Eßer else { 882252884aeSStefan Eßer dig = (BcBigDig) b->num[0]; 883252884aeSStefan Eßer operand = a; 884252884aeSStefan Eßer } 885252884aeSStefan Eßer 886252884aeSStefan Eßer bc_num_mulArray(operand, dig, c); 887252884aeSStefan Eßer 888252884aeSStefan Eßer if (BC_NUM_NONZERO(c)) c->neg = (a->neg != b->neg); 889252884aeSStefan Eßer 890252884aeSStefan Eßer return; 891252884aeSStefan Eßer } 892252884aeSStefan Eßer 893252884aeSStefan Eßer BC_SIG_LOCK; 894252884aeSStefan Eßer 895252884aeSStefan Eßer bc_num_init(&cpa, a->len + a->rdx); 896252884aeSStefan Eßer bc_num_init(&cpb, b->len + b->rdx); 897252884aeSStefan Eßer 898252884aeSStefan Eßer BC_SETJMP_LOCKED(err); 899252884aeSStefan Eßer 900252884aeSStefan Eßer BC_SIG_UNLOCK; 901252884aeSStefan Eßer 902252884aeSStefan Eßer bc_num_copy(&cpa, a); 903252884aeSStefan Eßer bc_num_copy(&cpb, b); 904252884aeSStefan Eßer 905252884aeSStefan Eßer cpa.neg = cpb.neg = false; 906252884aeSStefan Eßer 907252884aeSStefan Eßer ardx = cpa.rdx * BC_BASE_DIGS; 908252884aeSStefan Eßer bc_num_shiftLeft(&cpa, ardx); 909252884aeSStefan Eßer 910252884aeSStefan Eßer brdx = cpb.rdx * BC_BASE_DIGS; 911252884aeSStefan Eßer bc_num_shiftLeft(&cpb, brdx); 912252884aeSStefan Eßer 913252884aeSStefan Eßer // We need to reset the jump here because azero and bzero are used in the 914252884aeSStefan Eßer // cleanup, and local variables are not guaranteed to be the same after a 915252884aeSStefan Eßer // jump. 916252884aeSStefan Eßer BC_SIG_LOCK; 917252884aeSStefan Eßer 918252884aeSStefan Eßer BC_UNSETJMP; 919252884aeSStefan Eßer 920252884aeSStefan Eßer azero = bc_num_shiftZero(&cpa); 921252884aeSStefan Eßer bzero = bc_num_shiftZero(&cpb); 922252884aeSStefan Eßer 923252884aeSStefan Eßer BC_SETJMP_LOCKED(err); 924252884aeSStefan Eßer 925252884aeSStefan Eßer BC_SIG_UNLOCK; 926252884aeSStefan Eßer 927252884aeSStefan Eßer bc_num_clean(&cpa); 928252884aeSStefan Eßer bc_num_clean(&cpb); 929252884aeSStefan Eßer 930252884aeSStefan Eßer bc_num_k(&cpa, &cpb, c); 931252884aeSStefan Eßer 932252884aeSStefan Eßer zero = bc_vm_growSize(azero, bzero); 933252884aeSStefan Eßer len = bc_vm_growSize(c->len, zero); 934252884aeSStefan Eßer 935252884aeSStefan Eßer bc_num_expand(c, len); 936252884aeSStefan Eßer bc_num_shiftLeft(c, (len - c->len) * BC_BASE_DIGS); 937252884aeSStefan Eßer bc_num_shiftRight(c, ardx + brdx); 938252884aeSStefan Eßer 939252884aeSStefan Eßer bc_num_retireMul(c, scale, a->neg, b->neg); 940252884aeSStefan Eßer 941252884aeSStefan Eßer err: 942252884aeSStefan Eßer BC_SIG_MAYLOCK; 943252884aeSStefan Eßer bc_num_unshiftZero(&cpb, bzero); 944252884aeSStefan Eßer bc_num_free(&cpb); 945252884aeSStefan Eßer bc_num_unshiftZero(&cpa, azero); 946252884aeSStefan Eßer bc_num_free(&cpa); 947252884aeSStefan Eßer BC_LONGJMP_CONT; 948252884aeSStefan Eßer } 949252884aeSStefan Eßer 950252884aeSStefan Eßer static bool bc_num_nonZeroDig(BcDig *restrict a, size_t len) { 951252884aeSStefan Eßer size_t i; 952252884aeSStefan Eßer bool nonzero = false; 953252884aeSStefan Eßer for (i = len - 1; !nonzero && i < len; --i) nonzero = (a[i] != 0); 954252884aeSStefan Eßer return nonzero; 955252884aeSStefan Eßer } 956252884aeSStefan Eßer 957252884aeSStefan Eßer static ssize_t bc_num_divCmp(const BcDig *a, const BcNum *b, size_t len) { 958252884aeSStefan Eßer 959252884aeSStefan Eßer ssize_t cmp; 960252884aeSStefan Eßer 961252884aeSStefan Eßer if (b->len > len && a[len]) cmp = bc_num_compare(a, b->num, len + 1); 962252884aeSStefan Eßer else if (b->len <= len) { 963252884aeSStefan Eßer if (a[len]) cmp = 1; 964252884aeSStefan Eßer else cmp = bc_num_compare(a, b->num, len); 965252884aeSStefan Eßer } 966252884aeSStefan Eßer else cmp = -1; 967252884aeSStefan Eßer 968252884aeSStefan Eßer return cmp; 969252884aeSStefan Eßer } 970252884aeSStefan Eßer 971252884aeSStefan Eßer static void bc_num_divExtend(BcNum *restrict a, BcNum *restrict b, 972252884aeSStefan Eßer BcBigDig divisor) 973252884aeSStefan Eßer { 974252884aeSStefan Eßer size_t pow; 975252884aeSStefan Eßer 976252884aeSStefan Eßer assert(divisor < BC_BASE_POW); 977252884aeSStefan Eßer 978252884aeSStefan Eßer pow = BC_BASE_DIGS - bc_num_log10((size_t) divisor); 979252884aeSStefan Eßer 980252884aeSStefan Eßer bc_num_shiftLeft(a, pow); 981252884aeSStefan Eßer bc_num_shiftLeft(b, pow); 982252884aeSStefan Eßer } 983252884aeSStefan Eßer 984252884aeSStefan Eßer static void bc_num_d_long(BcNum *restrict a, BcNum *restrict b, 985252884aeSStefan Eßer BcNum *restrict c, size_t scale) 986252884aeSStefan Eßer { 987252884aeSStefan Eßer BcBigDig divisor; 988252884aeSStefan Eßer size_t len, end, i, rdx; 989252884aeSStefan Eßer BcNum cpb; 990252884aeSStefan Eßer bool nonzero = false; 991252884aeSStefan Eßer 992252884aeSStefan Eßer assert(b->len < a->len); 993252884aeSStefan Eßer len = b->len; 994252884aeSStefan Eßer end = a->len - len; 995252884aeSStefan Eßer assert(len >= 1); 996252884aeSStefan Eßer 997252884aeSStefan Eßer bc_num_expand(c, a->len); 998252884aeSStefan Eßer memset(c->num, 0, c->cap * sizeof(BcDig)); 999252884aeSStefan Eßer 1000252884aeSStefan Eßer c->rdx = a->rdx; 1001252884aeSStefan Eßer c->scale = a->scale; 1002252884aeSStefan Eßer c->len = a->len; 1003252884aeSStefan Eßer 1004252884aeSStefan Eßer divisor = (BcBigDig) b->num[len - 1]; 1005252884aeSStefan Eßer 1006252884aeSStefan Eßer if (len > 1 && bc_num_nonZeroDig(b->num, len - 1)) { 1007252884aeSStefan Eßer 1008252884aeSStefan Eßer nonzero = (divisor > 1 << ((10 * BC_BASE_DIGS) / 6 + 1)); 1009252884aeSStefan Eßer 1010252884aeSStefan Eßer if (!nonzero) { 1011252884aeSStefan Eßer 1012252884aeSStefan Eßer bc_num_divExtend(a, b, divisor); 1013252884aeSStefan Eßer 1014252884aeSStefan Eßer len = BC_MAX(a->len, b->len); 1015252884aeSStefan Eßer bc_num_expand(a, len + 1); 1016252884aeSStefan Eßer 1017252884aeSStefan Eßer if (len + 1 > a->len) a->len = len + 1; 1018252884aeSStefan Eßer 1019252884aeSStefan Eßer len = b->len; 1020252884aeSStefan Eßer end = a->len - len; 1021252884aeSStefan Eßer divisor = (BcBigDig) b->num[len - 1]; 1022252884aeSStefan Eßer 1023252884aeSStefan Eßer nonzero = bc_num_nonZeroDig(b->num, len - 1); 1024252884aeSStefan Eßer } 1025252884aeSStefan Eßer } 1026252884aeSStefan Eßer 1027252884aeSStefan Eßer divisor += nonzero; 1028252884aeSStefan Eßer 1029252884aeSStefan Eßer bc_num_expand(c, a->len); 1030252884aeSStefan Eßer memset(c->num, 0, BC_NUM_SIZE(c->cap)); 1031252884aeSStefan Eßer 1032252884aeSStefan Eßer assert(c->scale >= scale); 1033252884aeSStefan Eßer rdx = c->rdx - BC_NUM_RDX(scale); 1034252884aeSStefan Eßer 1035252884aeSStefan Eßer BC_SIG_LOCK; 1036252884aeSStefan Eßer 1037252884aeSStefan Eßer bc_num_init(&cpb, len + 1); 1038252884aeSStefan Eßer 1039252884aeSStefan Eßer BC_SETJMP_LOCKED(err); 1040252884aeSStefan Eßer 1041252884aeSStefan Eßer BC_SIG_UNLOCK; 1042252884aeSStefan Eßer 1043252884aeSStefan Eßer i = end - 1; 1044252884aeSStefan Eßer 1045252884aeSStefan Eßer for (; i < end && i >= rdx && BC_NUM_NONZERO(a); --i) { 1046252884aeSStefan Eßer 1047252884aeSStefan Eßer ssize_t cmp; 1048252884aeSStefan Eßer BcDig *n; 1049252884aeSStefan Eßer BcBigDig result; 1050252884aeSStefan Eßer 1051252884aeSStefan Eßer n = a->num + i; 1052252884aeSStefan Eßer assert(n >= a->num); 1053252884aeSStefan Eßer result = 0; 1054252884aeSStefan Eßer 1055252884aeSStefan Eßer cmp = bc_num_divCmp(n, b, len); 1056252884aeSStefan Eßer 1057252884aeSStefan Eßer while (cmp >= 0) { 1058252884aeSStefan Eßer 1059252884aeSStefan Eßer BcBigDig n1, dividend, q; 1060252884aeSStefan Eßer 1061252884aeSStefan Eßer n1 = (BcBigDig) n[len]; 1062252884aeSStefan Eßer dividend = n1 * BC_BASE_POW + (BcBigDig) n[len - 1]; 1063252884aeSStefan Eßer q = (dividend / divisor); 1064252884aeSStefan Eßer 1065252884aeSStefan Eßer if (q <= 1) { 1066252884aeSStefan Eßer q = 1; 1067252884aeSStefan Eßer bc_num_subArrays(n, b->num, len); 1068252884aeSStefan Eßer } 1069252884aeSStefan Eßer else { 1070252884aeSStefan Eßer 1071252884aeSStefan Eßer assert(q <= BC_BASE_POW); 1072252884aeSStefan Eßer 1073252884aeSStefan Eßer bc_num_mulArray(b, (BcBigDig) q, &cpb); 1074252884aeSStefan Eßer bc_num_subArrays(n, cpb.num, cpb.len); 1075252884aeSStefan Eßer } 1076252884aeSStefan Eßer 1077252884aeSStefan Eßer result += q; 1078252884aeSStefan Eßer assert(result <= BC_BASE_POW); 1079252884aeSStefan Eßer 1080252884aeSStefan Eßer if (nonzero) cmp = bc_num_divCmp(n, b, len); 1081252884aeSStefan Eßer else cmp = -1; 1082252884aeSStefan Eßer } 1083252884aeSStefan Eßer 1084252884aeSStefan Eßer assert(result < BC_BASE_POW); 1085252884aeSStefan Eßer 1086252884aeSStefan Eßer c->num[i] = (BcDig) result; 1087252884aeSStefan Eßer } 1088252884aeSStefan Eßer 1089252884aeSStefan Eßer err: 1090252884aeSStefan Eßer BC_SIG_MAYLOCK; 1091252884aeSStefan Eßer bc_num_free(&cpb); 1092252884aeSStefan Eßer BC_LONGJMP_CONT; 1093252884aeSStefan Eßer } 1094252884aeSStefan Eßer 1095252884aeSStefan Eßer static void bc_num_d(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale) { 1096252884aeSStefan Eßer 1097252884aeSStefan Eßer size_t len; 1098252884aeSStefan Eßer BcNum cpa, cpb; 1099252884aeSStefan Eßer 1100252884aeSStefan Eßer if (BC_NUM_ZERO(b)) bc_vm_err(BC_ERROR_MATH_DIVIDE_BY_ZERO); 1101252884aeSStefan Eßer if (BC_NUM_ZERO(a)) { 1102252884aeSStefan Eßer bc_num_setToZero(c, scale); 1103252884aeSStefan Eßer return; 1104252884aeSStefan Eßer } 1105252884aeSStefan Eßer if (BC_NUM_ONE(b)) { 1106252884aeSStefan Eßer bc_num_copy(c, a); 1107252884aeSStefan Eßer bc_num_retireMul(c, scale, a->neg, b->neg); 1108252884aeSStefan Eßer return; 1109252884aeSStefan Eßer } 1110252884aeSStefan Eßer if (!a->rdx && !b->rdx && b->len == 1 && !scale) { 1111252884aeSStefan Eßer BcBigDig rem; 1112252884aeSStefan Eßer bc_num_divArray(a, (BcBigDig) b->num[0], c, &rem); 1113252884aeSStefan Eßer bc_num_retireMul(c, scale, a->neg, b->neg); 1114252884aeSStefan Eßer return; 1115252884aeSStefan Eßer } 1116252884aeSStefan Eßer 1117252884aeSStefan Eßer len = bc_num_mulReq(a, b, scale); 1118252884aeSStefan Eßer 1119252884aeSStefan Eßer BC_SIG_LOCK; 1120252884aeSStefan Eßer 1121252884aeSStefan Eßer bc_num_init(&cpa, len); 1122252884aeSStefan Eßer bc_num_copy(&cpa, a); 1123252884aeSStefan Eßer bc_num_createCopy(&cpb, b); 1124252884aeSStefan Eßer 1125252884aeSStefan Eßer BC_SETJMP_LOCKED(err); 1126252884aeSStefan Eßer 1127252884aeSStefan Eßer BC_SIG_UNLOCK; 1128252884aeSStefan Eßer 1129252884aeSStefan Eßer len = b->len; 1130252884aeSStefan Eßer 1131252884aeSStefan Eßer if (len > cpa.len) { 1132252884aeSStefan Eßer bc_num_expand(&cpa, bc_vm_growSize(len, 2)); 1133252884aeSStefan Eßer bc_num_extend(&cpa, (len - cpa.len) * BC_BASE_DIGS); 1134252884aeSStefan Eßer } 1135252884aeSStefan Eßer 1136252884aeSStefan Eßer cpa.scale = cpa.rdx * BC_BASE_DIGS; 1137252884aeSStefan Eßer 1138252884aeSStefan Eßer bc_num_extend(&cpa, b->scale); 1139252884aeSStefan Eßer cpa.rdx -= BC_NUM_RDX(b->scale); 1140252884aeSStefan Eßer cpa.scale = cpa.rdx * BC_BASE_DIGS; 1141252884aeSStefan Eßer 1142252884aeSStefan Eßer if (scale > cpa.scale) { 1143252884aeSStefan Eßer bc_num_extend(&cpa, scale); 1144252884aeSStefan Eßer cpa.scale = cpa.rdx * BC_BASE_DIGS; 1145252884aeSStefan Eßer } 1146252884aeSStefan Eßer 1147252884aeSStefan Eßer if (cpa.cap == cpa.len) bc_num_expand(&cpa, bc_vm_growSize(cpa.len, 1)); 1148252884aeSStefan Eßer 1149252884aeSStefan Eßer // We want an extra zero in front to make things simpler. 1150252884aeSStefan Eßer cpa.num[cpa.len++] = 0; 1151252884aeSStefan Eßer 1152252884aeSStefan Eßer if (cpa.rdx == cpa.len) cpa.len = bc_num_nonzeroLen(&cpa); 1153252884aeSStefan Eßer if (cpb.rdx == cpb.len) cpb.len = bc_num_nonzeroLen(&cpb); 1154252884aeSStefan Eßer cpb.scale = cpb.rdx = 0; 1155252884aeSStefan Eßer 1156252884aeSStefan Eßer bc_num_d_long(&cpa, &cpb, c, scale); 1157252884aeSStefan Eßer 1158252884aeSStefan Eßer bc_num_retireMul(c, scale, a->neg, b->neg); 1159252884aeSStefan Eßer 1160252884aeSStefan Eßer err: 1161252884aeSStefan Eßer BC_SIG_MAYLOCK; 1162252884aeSStefan Eßer bc_num_free(&cpb); 1163252884aeSStefan Eßer bc_num_free(&cpa); 1164252884aeSStefan Eßer BC_LONGJMP_CONT; 1165252884aeSStefan Eßer } 1166252884aeSStefan Eßer 1167252884aeSStefan Eßer static void bc_num_r(BcNum *a, BcNum *b, BcNum *restrict c, 1168252884aeSStefan Eßer BcNum *restrict d, size_t scale, size_t ts) 1169252884aeSStefan Eßer { 1170252884aeSStefan Eßer BcNum temp; 1171252884aeSStefan Eßer bool neg; 1172252884aeSStefan Eßer 1173252884aeSStefan Eßer if (BC_NUM_ZERO(b)) bc_vm_err(BC_ERROR_MATH_DIVIDE_BY_ZERO); 1174252884aeSStefan Eßer if (BC_NUM_ZERO(a)) { 1175252884aeSStefan Eßer bc_num_setToZero(c, ts); 1176252884aeSStefan Eßer bc_num_setToZero(d, ts); 1177252884aeSStefan Eßer return; 1178252884aeSStefan Eßer } 1179252884aeSStefan Eßer 1180252884aeSStefan Eßer BC_SIG_LOCK; 1181252884aeSStefan Eßer 1182252884aeSStefan Eßer bc_num_init(&temp, d->cap); 1183252884aeSStefan Eßer 1184252884aeSStefan Eßer BC_SETJMP_LOCKED(err); 1185252884aeSStefan Eßer 1186252884aeSStefan Eßer BC_SIG_UNLOCK; 1187252884aeSStefan Eßer 1188252884aeSStefan Eßer bc_num_d(a, b, c, scale); 1189252884aeSStefan Eßer 1190252884aeSStefan Eßer if (scale) scale = ts + 1; 1191252884aeSStefan Eßer 1192252884aeSStefan Eßer bc_num_m(c, b, &temp, scale); 1193252884aeSStefan Eßer bc_num_sub(a, &temp, d, scale); 1194252884aeSStefan Eßer 1195252884aeSStefan Eßer if (ts > d->scale && BC_NUM_NONZERO(d)) bc_num_extend(d, ts - d->scale); 1196252884aeSStefan Eßer 1197252884aeSStefan Eßer neg = d->neg; 1198252884aeSStefan Eßer bc_num_retireMul(d, ts, a->neg, b->neg); 1199252884aeSStefan Eßer d->neg = BC_NUM_NONZERO(d) ? neg : false; 1200252884aeSStefan Eßer 1201252884aeSStefan Eßer err: 1202252884aeSStefan Eßer BC_SIG_MAYLOCK; 1203252884aeSStefan Eßer bc_num_free(&temp); 1204252884aeSStefan Eßer BC_LONGJMP_CONT; 1205252884aeSStefan Eßer } 1206252884aeSStefan Eßer 1207252884aeSStefan Eßer static void bc_num_rem(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale) { 1208252884aeSStefan Eßer 1209252884aeSStefan Eßer BcNum c1; 1210252884aeSStefan Eßer size_t ts; 1211252884aeSStefan Eßer 1212252884aeSStefan Eßer ts = bc_vm_growSize(scale, b->scale); 1213252884aeSStefan Eßer ts = BC_MAX(ts, a->scale); 1214252884aeSStefan Eßer 1215252884aeSStefan Eßer BC_SIG_LOCK; 1216252884aeSStefan Eßer 1217252884aeSStefan Eßer bc_num_init(&c1, bc_num_mulReq(a, b, ts)); 1218252884aeSStefan Eßer 1219252884aeSStefan Eßer BC_SETJMP_LOCKED(err); 1220252884aeSStefan Eßer 1221252884aeSStefan Eßer BC_SIG_UNLOCK; 1222252884aeSStefan Eßer 1223252884aeSStefan Eßer bc_num_r(a, b, &c1, c, scale, ts); 1224252884aeSStefan Eßer 1225252884aeSStefan Eßer err: 1226252884aeSStefan Eßer BC_SIG_MAYLOCK; 1227252884aeSStefan Eßer bc_num_free(&c1); 1228252884aeSStefan Eßer BC_LONGJMP_CONT; 1229252884aeSStefan Eßer } 1230252884aeSStefan Eßer 1231252884aeSStefan Eßer static void bc_num_p(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale) { 1232252884aeSStefan Eßer 1233252884aeSStefan Eßer BcNum copy; 1234252884aeSStefan Eßer BcBigDig pow = 0; 1235252884aeSStefan Eßer size_t i, powrdx, resrdx; 1236252884aeSStefan Eßer bool neg, zero; 1237252884aeSStefan Eßer 1238252884aeSStefan Eßer if (BC_ERR(b->rdx)) bc_vm_err(BC_ERROR_MATH_NON_INTEGER); 1239252884aeSStefan Eßer 1240252884aeSStefan Eßer if (BC_NUM_ZERO(b)) { 1241252884aeSStefan Eßer bc_num_one(c); 1242252884aeSStefan Eßer return; 1243252884aeSStefan Eßer } 1244252884aeSStefan Eßer if (BC_NUM_ZERO(a)) { 1245252884aeSStefan Eßer if (b->neg) bc_vm_err(BC_ERROR_MATH_DIVIDE_BY_ZERO); 1246252884aeSStefan Eßer bc_num_setToZero(c, scale); 1247252884aeSStefan Eßer return; 1248252884aeSStefan Eßer } 1249252884aeSStefan Eßer if (BC_NUM_ONE(b)) { 1250252884aeSStefan Eßer if (!b->neg) bc_num_copy(c, a); 1251252884aeSStefan Eßer else bc_num_inv(a, c, scale); 1252252884aeSStefan Eßer return; 1253252884aeSStefan Eßer } 1254252884aeSStefan Eßer 1255252884aeSStefan Eßer BC_SIG_LOCK; 1256252884aeSStefan Eßer 1257252884aeSStefan Eßer neg = b->neg; 1258252884aeSStefan Eßer b->neg = false; 1259252884aeSStefan Eßer bc_num_bigdig(b, &pow); 1260252884aeSStefan Eßer b->neg = neg; 1261252884aeSStefan Eßer 1262252884aeSStefan Eßer bc_num_createCopy(©, a); 1263252884aeSStefan Eßer 1264252884aeSStefan Eßer BC_SETJMP_LOCKED(err); 1265252884aeSStefan Eßer 1266252884aeSStefan Eßer BC_SIG_UNLOCK; 1267252884aeSStefan Eßer 1268252884aeSStefan Eßer if (!neg) { 1269252884aeSStefan Eßer size_t max = BC_MAX(scale, a->scale), scalepow = a->scale * pow; 1270252884aeSStefan Eßer scale = BC_MIN(scalepow, max); 1271252884aeSStefan Eßer } 1272252884aeSStefan Eßer 1273252884aeSStefan Eßer for (powrdx = a->scale; !(pow & 1); pow >>= 1) { 1274252884aeSStefan Eßer powrdx <<= 1; 1275252884aeSStefan Eßer bc_num_mul(©, ©, ©, powrdx); 1276252884aeSStefan Eßer } 1277252884aeSStefan Eßer 1278252884aeSStefan Eßer bc_num_copy(c, ©); 1279252884aeSStefan Eßer resrdx = powrdx; 1280252884aeSStefan Eßer 1281252884aeSStefan Eßer while (pow >>= 1) { 1282252884aeSStefan Eßer 1283252884aeSStefan Eßer powrdx <<= 1; 1284252884aeSStefan Eßer bc_num_mul(©, ©, ©, powrdx); 1285252884aeSStefan Eßer 1286252884aeSStefan Eßer if (pow & 1) { 1287252884aeSStefan Eßer resrdx += powrdx; 1288252884aeSStefan Eßer bc_num_mul(c, ©, c, resrdx); 1289252884aeSStefan Eßer } 1290252884aeSStefan Eßer } 1291252884aeSStefan Eßer 1292252884aeSStefan Eßer if (neg) bc_num_inv(c, c, scale); 1293252884aeSStefan Eßer 1294252884aeSStefan Eßer if (c->scale > scale) bc_num_truncate(c, c->scale - scale); 1295252884aeSStefan Eßer 1296252884aeSStefan Eßer // We can't use bc_num_clean() here. 1297252884aeSStefan Eßer for (zero = true, i = 0; zero && i < c->len; ++i) zero = !c->num[i]; 1298252884aeSStefan Eßer if (zero) bc_num_setToZero(c, scale); 1299252884aeSStefan Eßer 1300252884aeSStefan Eßer err: 1301252884aeSStefan Eßer BC_SIG_MAYLOCK; 1302252884aeSStefan Eßer bc_num_free(©); 1303252884aeSStefan Eßer BC_LONGJMP_CONT; 1304252884aeSStefan Eßer } 1305252884aeSStefan Eßer 1306252884aeSStefan Eßer #if BC_ENABLE_EXTRA_MATH 1307252884aeSStefan Eßer static void bc_num_place(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale) { 1308252884aeSStefan Eßer 1309252884aeSStefan Eßer BcBigDig val = 0; 1310252884aeSStefan Eßer 1311252884aeSStefan Eßer BC_UNUSED(scale); 1312252884aeSStefan Eßer 1313252884aeSStefan Eßer bc_num_intop(a, b, c, &val); 1314252884aeSStefan Eßer 1315252884aeSStefan Eßer if (val < c->scale) bc_num_truncate(c, c->scale - val); 1316252884aeSStefan Eßer else if (val > c->scale) bc_num_extend(c, val - c->scale); 1317252884aeSStefan Eßer } 1318252884aeSStefan Eßer 1319252884aeSStefan Eßer static void bc_num_left(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale) { 1320252884aeSStefan Eßer 1321252884aeSStefan Eßer BcBigDig val = 0; 1322252884aeSStefan Eßer 1323252884aeSStefan Eßer BC_UNUSED(scale); 1324252884aeSStefan Eßer 1325252884aeSStefan Eßer bc_num_intop(a, b, c, &val); 1326252884aeSStefan Eßer 1327252884aeSStefan Eßer bc_num_shiftLeft(c, (size_t) val); 1328252884aeSStefan Eßer } 1329252884aeSStefan Eßer 1330252884aeSStefan Eßer static void bc_num_right(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale) { 1331252884aeSStefan Eßer 1332252884aeSStefan Eßer BcBigDig val = 0; 1333252884aeSStefan Eßer 1334252884aeSStefan Eßer BC_UNUSED(scale); 1335252884aeSStefan Eßer 1336252884aeSStefan Eßer bc_num_intop(a, b, c, &val); 1337252884aeSStefan Eßer 1338252884aeSStefan Eßer if (BC_NUM_ZERO(c)) return; 1339252884aeSStefan Eßer 1340252884aeSStefan Eßer bc_num_shiftRight(c, (size_t) val); 1341252884aeSStefan Eßer } 1342252884aeSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH 1343252884aeSStefan Eßer 1344252884aeSStefan Eßer static void bc_num_binary(BcNum *a, BcNum *b, BcNum *c, size_t scale, 1345252884aeSStefan Eßer BcNumBinaryOp op, size_t req) 1346252884aeSStefan Eßer { 1347252884aeSStefan Eßer BcNum num2, *ptr_a, *ptr_b; 1348252884aeSStefan Eßer bool init = false; 1349252884aeSStefan Eßer 1350252884aeSStefan Eßer assert(a != NULL && b != NULL && c != NULL && op != NULL); 1351252884aeSStefan Eßer 1352252884aeSStefan Eßer BC_SIG_LOCK; 1353252884aeSStefan Eßer 1354252884aeSStefan Eßer if (c == a) { 1355252884aeSStefan Eßer 1356252884aeSStefan Eßer ptr_a = &num2; 1357252884aeSStefan Eßer 1358252884aeSStefan Eßer memcpy(ptr_a, c, sizeof(BcNum)); 1359252884aeSStefan Eßer init = true; 1360252884aeSStefan Eßer } 1361252884aeSStefan Eßer else ptr_a = a; 1362252884aeSStefan Eßer 1363252884aeSStefan Eßer if (c == b) { 1364252884aeSStefan Eßer 1365252884aeSStefan Eßer ptr_b = &num2; 1366252884aeSStefan Eßer 1367252884aeSStefan Eßer if (c != a) { 1368252884aeSStefan Eßer memcpy(ptr_b, c, sizeof(BcNum)); 1369252884aeSStefan Eßer init = true; 1370252884aeSStefan Eßer } 1371252884aeSStefan Eßer } 1372252884aeSStefan Eßer else ptr_b = b; 1373252884aeSStefan Eßer 1374252884aeSStefan Eßer if (init) { 1375252884aeSStefan Eßer 1376252884aeSStefan Eßer bc_num_init(c, req); 1377252884aeSStefan Eßer 1378252884aeSStefan Eßer BC_SETJMP_LOCKED(err); 1379252884aeSStefan Eßer BC_SIG_UNLOCK; 1380252884aeSStefan Eßer } 1381252884aeSStefan Eßer else { 1382252884aeSStefan Eßer BC_SIG_UNLOCK; 1383252884aeSStefan Eßer bc_num_expand(c, req); 1384252884aeSStefan Eßer } 1385252884aeSStefan Eßer 1386252884aeSStefan Eßer op(ptr_a, ptr_b, c, scale); 1387252884aeSStefan Eßer 1388252884aeSStefan Eßer assert(!c->neg || BC_NUM_NONZERO(c)); 1389252884aeSStefan Eßer assert(c->rdx <= c->len || !c->len); 1390252884aeSStefan Eßer assert(!c->len || c->num[c->len - 1] || c->rdx == c->len); 1391252884aeSStefan Eßer 1392252884aeSStefan Eßer err: 1393252884aeSStefan Eßer if (init) { 1394252884aeSStefan Eßer BC_SIG_MAYLOCK; 1395252884aeSStefan Eßer bc_num_free(&num2); 1396252884aeSStefan Eßer BC_LONGJMP_CONT; 1397252884aeSStefan Eßer } 1398252884aeSStefan Eßer } 1399252884aeSStefan Eßer 1400252884aeSStefan Eßer #ifndef NDEBUG 1401252884aeSStefan Eßer static bool bc_num_strValid(const char *val) { 1402252884aeSStefan Eßer 1403252884aeSStefan Eßer bool radix = false; 1404252884aeSStefan Eßer size_t i, len = strlen(val); 1405252884aeSStefan Eßer 1406252884aeSStefan Eßer if (!len) return true; 1407252884aeSStefan Eßer 1408252884aeSStefan Eßer for (i = 0; i < len; ++i) { 1409252884aeSStefan Eßer 1410252884aeSStefan Eßer BcDig c = val[i]; 1411252884aeSStefan Eßer 1412252884aeSStefan Eßer if (c == '.') { 1413252884aeSStefan Eßer 1414252884aeSStefan Eßer if (radix) return false; 1415252884aeSStefan Eßer 1416252884aeSStefan Eßer radix = true; 1417252884aeSStefan Eßer continue; 1418252884aeSStefan Eßer } 1419252884aeSStefan Eßer 1420252884aeSStefan Eßer if (!(isdigit(c) || isupper(c))) return false; 1421252884aeSStefan Eßer } 1422252884aeSStefan Eßer 1423252884aeSStefan Eßer return true; 1424252884aeSStefan Eßer } 1425252884aeSStefan Eßer #endif // NDEBUG 1426252884aeSStefan Eßer 1427252884aeSStefan Eßer static BcBigDig bc_num_parseChar(char c, size_t base_t) { 1428252884aeSStefan Eßer 1429252884aeSStefan Eßer if (isupper(c)) { 1430252884aeSStefan Eßer c = BC_NUM_NUM_LETTER(c); 1431252884aeSStefan Eßer c = ((size_t) c) >= base_t ? (char) base_t - 1 : c; 1432252884aeSStefan Eßer } 1433252884aeSStefan Eßer else c -= '0'; 1434252884aeSStefan Eßer 1435252884aeSStefan Eßer return (BcBigDig) (uchar) c; 1436252884aeSStefan Eßer } 1437252884aeSStefan Eßer 1438252884aeSStefan Eßer static void bc_num_parseDecimal(BcNum *restrict n, const char *restrict val) { 1439252884aeSStefan Eßer 1440252884aeSStefan Eßer size_t len, i, temp, mod; 1441252884aeSStefan Eßer const char *ptr; 1442252884aeSStefan Eßer bool zero = true, rdx; 1443252884aeSStefan Eßer 1444252884aeSStefan Eßer for (i = 0; val[i] == '0'; ++i); 1445252884aeSStefan Eßer 1446252884aeSStefan Eßer val += i; 1447252884aeSStefan Eßer assert(!val[0] || isalnum(val[0]) || val[0] == '.'); 1448252884aeSStefan Eßer 1449252884aeSStefan Eßer // All 0's. We can just return, since this 1450252884aeSStefan Eßer // procedure expects a virgin (already 0) BcNum. 1451252884aeSStefan Eßer if (!val[0]) return; 1452252884aeSStefan Eßer 1453252884aeSStefan Eßer len = strlen(val); 1454252884aeSStefan Eßer 1455252884aeSStefan Eßer ptr = strchr(val, '.'); 1456252884aeSStefan Eßer rdx = (ptr != NULL); 1457252884aeSStefan Eßer 1458252884aeSStefan Eßer for (i = 0; i < len && (zero = (val[i] == '0' || val[i] == '.')); ++i); 1459252884aeSStefan Eßer 1460*d213476dSStefan Eßer n->scale = (size_t) (rdx * (((uintptr_t) (val + len)) - 1461*d213476dSStefan Eßer (((uintptr_t) ptr) + 1))); 1462252884aeSStefan Eßer n->rdx = BC_NUM_RDX(n->scale); 1463252884aeSStefan Eßer 1464252884aeSStefan Eßer i = len - (ptr == val ? 0 : i) - rdx; 1465252884aeSStefan Eßer temp = BC_NUM_ROUND_POW(i); 1466252884aeSStefan Eßer mod = n->scale % BC_BASE_DIGS; 1467252884aeSStefan Eßer i = mod ? BC_BASE_DIGS - mod : 0; 1468252884aeSStefan Eßer n->len = ((temp + i) / BC_BASE_DIGS); 1469252884aeSStefan Eßer 1470252884aeSStefan Eßer bc_num_expand(n, n->len); 1471252884aeSStefan Eßer memset(n->num, 0, BC_NUM_SIZE(n->len)); 1472252884aeSStefan Eßer 1473252884aeSStefan Eßer if (zero) n->len = n->rdx = 0; 1474252884aeSStefan Eßer else { 1475252884aeSStefan Eßer 1476252884aeSStefan Eßer BcBigDig exp, pow; 1477252884aeSStefan Eßer 1478252884aeSStefan Eßer assert(i <= BC_NUM_BIGDIG_MAX); 1479252884aeSStefan Eßer 1480252884aeSStefan Eßer exp = (BcBigDig) i; 1481252884aeSStefan Eßer pow = bc_num_pow10[exp]; 1482252884aeSStefan Eßer 1483252884aeSStefan Eßer for (i = len - 1; i < len; --i, ++exp) { 1484252884aeSStefan Eßer 1485252884aeSStefan Eßer char c = val[i]; 1486252884aeSStefan Eßer 1487252884aeSStefan Eßer if (c == '.') exp -= 1; 1488252884aeSStefan Eßer else { 1489252884aeSStefan Eßer 1490252884aeSStefan Eßer size_t idx = exp / BC_BASE_DIGS; 1491252884aeSStefan Eßer 1492252884aeSStefan Eßer if (isupper(c)) c = '9'; 1493252884aeSStefan Eßer n->num[idx] += (((BcBigDig) c) - '0') * pow; 1494252884aeSStefan Eßer 1495252884aeSStefan Eßer if ((exp + 1) % BC_BASE_DIGS == 0) pow = 1; 1496252884aeSStefan Eßer else pow *= BC_BASE; 1497252884aeSStefan Eßer } 1498252884aeSStefan Eßer } 1499252884aeSStefan Eßer } 1500252884aeSStefan Eßer } 1501252884aeSStefan Eßer 1502252884aeSStefan Eßer static void bc_num_parseBase(BcNum *restrict n, const char *restrict val, 1503252884aeSStefan Eßer BcBigDig base) 1504252884aeSStefan Eßer { 1505252884aeSStefan Eßer BcNum temp, mult1, mult2, result1, result2, *m1, *m2, *ptr; 1506252884aeSStefan Eßer char c = 0; 1507252884aeSStefan Eßer bool zero = true; 1508252884aeSStefan Eßer BcBigDig v; 1509252884aeSStefan Eßer size_t i, digs, len = strlen(val); 1510252884aeSStefan Eßer 1511252884aeSStefan Eßer for (i = 0; zero && i < len; ++i) zero = (val[i] == '.' || val[i] == '0'); 1512252884aeSStefan Eßer if (zero) return; 1513252884aeSStefan Eßer 1514252884aeSStefan Eßer BC_SIG_LOCK; 1515252884aeSStefan Eßer 1516252884aeSStefan Eßer bc_num_init(&temp, BC_NUM_BIGDIG_LOG10); 1517252884aeSStefan Eßer bc_num_init(&mult1, BC_NUM_BIGDIG_LOG10); 1518252884aeSStefan Eßer 1519252884aeSStefan Eßer BC_SETJMP_LOCKED(int_err); 1520252884aeSStefan Eßer 1521252884aeSStefan Eßer BC_SIG_UNLOCK; 1522252884aeSStefan Eßer 1523252884aeSStefan Eßer for (i = 0; i < len && (c = val[i]) && c != '.'; ++i) { 1524252884aeSStefan Eßer 1525252884aeSStefan Eßer v = bc_num_parseChar(c, base); 1526252884aeSStefan Eßer 1527252884aeSStefan Eßer bc_num_mulArray(n, base, &mult1); 1528252884aeSStefan Eßer bc_num_bigdig2num(&temp, v); 1529252884aeSStefan Eßer bc_num_add(&mult1, &temp, n, 0); 1530252884aeSStefan Eßer } 1531252884aeSStefan Eßer 1532252884aeSStefan Eßer if (i == len && !(c = val[i])) goto int_err; 1533252884aeSStefan Eßer 1534252884aeSStefan Eßer assert(c == '.'); 1535252884aeSStefan Eßer 1536252884aeSStefan Eßer BC_SIG_LOCK; 1537252884aeSStefan Eßer 1538252884aeSStefan Eßer BC_UNSETJMP; 1539252884aeSStefan Eßer 1540252884aeSStefan Eßer bc_num_init(&mult2, BC_NUM_BIGDIG_LOG10); 1541252884aeSStefan Eßer bc_num_init(&result1, BC_NUM_DEF_SIZE); 1542252884aeSStefan Eßer bc_num_init(&result2, BC_NUM_DEF_SIZE); 1543252884aeSStefan Eßer bc_num_one(&mult1); 1544252884aeSStefan Eßer 1545252884aeSStefan Eßer BC_SETJMP_LOCKED(err); 1546252884aeSStefan Eßer 1547252884aeSStefan Eßer BC_SIG_UNLOCK; 1548252884aeSStefan Eßer 1549252884aeSStefan Eßer m1 = &mult1; 1550252884aeSStefan Eßer m2 = &mult2; 1551252884aeSStefan Eßer 1552252884aeSStefan Eßer for (i += 1, digs = 0; i < len && (c = val[i]); ++i, ++digs) { 1553252884aeSStefan Eßer 1554252884aeSStefan Eßer v = bc_num_parseChar(c, base); 1555252884aeSStefan Eßer 1556252884aeSStefan Eßer bc_num_mulArray(&result1, base, &result2); 1557252884aeSStefan Eßer 1558252884aeSStefan Eßer bc_num_bigdig2num(&temp, v); 1559252884aeSStefan Eßer bc_num_add(&result2, &temp, &result1, 0); 1560252884aeSStefan Eßer bc_num_mulArray(m1, base, m2); 1561252884aeSStefan Eßer 1562252884aeSStefan Eßer if (m2->len < m2->rdx) m2->len = m2->rdx; 1563252884aeSStefan Eßer 1564252884aeSStefan Eßer ptr = m1; 1565252884aeSStefan Eßer m1 = m2; 1566252884aeSStefan Eßer m2 = ptr; 1567252884aeSStefan Eßer } 1568252884aeSStefan Eßer 1569252884aeSStefan Eßer // This one cannot be a divide by 0 because mult starts out at 1, then is 1570252884aeSStefan Eßer // multiplied by base, and base cannot be 0, so mult cannot be 0. 1571252884aeSStefan Eßer bc_num_div(&result1, m1, &result2, digs * 2); 1572252884aeSStefan Eßer bc_num_truncate(&result2, digs); 1573252884aeSStefan Eßer bc_num_add(n, &result2, n, digs); 1574252884aeSStefan Eßer 1575252884aeSStefan Eßer if (BC_NUM_NONZERO(n)) { 1576252884aeSStefan Eßer if (n->scale < digs) bc_num_extend(n, digs - n->scale); 1577252884aeSStefan Eßer } 1578252884aeSStefan Eßer else bc_num_zero(n); 1579252884aeSStefan Eßer 1580252884aeSStefan Eßer err: 1581252884aeSStefan Eßer BC_SIG_MAYLOCK; 1582252884aeSStefan Eßer bc_num_free(&result2); 1583252884aeSStefan Eßer bc_num_free(&result1); 1584252884aeSStefan Eßer bc_num_free(&mult2); 1585252884aeSStefan Eßer int_err: 1586252884aeSStefan Eßer BC_SIG_MAYLOCK; 1587252884aeSStefan Eßer bc_num_free(&mult1); 1588252884aeSStefan Eßer bc_num_free(&temp); 1589252884aeSStefan Eßer BC_LONGJMP_CONT; 1590252884aeSStefan Eßer } 1591252884aeSStefan Eßer 1592252884aeSStefan Eßer static void bc_num_printNewline(void) { 1593252884aeSStefan Eßer if (vm.nchars >= vm.line_len - 1) { 1594252884aeSStefan Eßer bc_vm_putchar('\\'); 1595252884aeSStefan Eßer bc_vm_putchar('\n'); 1596252884aeSStefan Eßer } 1597252884aeSStefan Eßer } 1598252884aeSStefan Eßer 1599252884aeSStefan Eßer static void bc_num_putchar(int c) { 1600252884aeSStefan Eßer if (c != '\n') bc_num_printNewline(); 1601252884aeSStefan Eßer bc_vm_putchar(c); 1602252884aeSStefan Eßer } 1603252884aeSStefan Eßer 1604252884aeSStefan Eßer #if DC_ENABLED 1605252884aeSStefan Eßer static void bc_num_printChar(size_t n, size_t len, bool rdx) { 1606252884aeSStefan Eßer BC_UNUSED(rdx); 1607252884aeSStefan Eßer BC_UNUSED(len); 1608252884aeSStefan Eßer assert(len == 1); 1609252884aeSStefan Eßer bc_vm_putchar((uchar) n); 1610252884aeSStefan Eßer } 1611252884aeSStefan Eßer #endif // DC_ENABLED 1612252884aeSStefan Eßer 1613252884aeSStefan Eßer static void bc_num_printDigits(size_t n, size_t len, bool rdx) { 1614252884aeSStefan Eßer 1615252884aeSStefan Eßer size_t exp, pow; 1616252884aeSStefan Eßer 1617252884aeSStefan Eßer bc_num_putchar(rdx ? '.' : ' '); 1618252884aeSStefan Eßer 1619252884aeSStefan Eßer for (exp = 0, pow = 1; exp < len - 1; ++exp, pow *= BC_BASE); 1620252884aeSStefan Eßer 1621252884aeSStefan Eßer for (exp = 0; exp < len; pow /= BC_BASE, ++exp) { 1622252884aeSStefan Eßer size_t dig = n / pow; 1623252884aeSStefan Eßer n -= dig * pow; 1624252884aeSStefan Eßer bc_num_putchar(((uchar) dig) + '0'); 1625252884aeSStefan Eßer } 1626252884aeSStefan Eßer } 1627252884aeSStefan Eßer 1628252884aeSStefan Eßer static void bc_num_printHex(size_t n, size_t len, bool rdx) { 1629252884aeSStefan Eßer 1630252884aeSStefan Eßer BC_UNUSED(len); 1631252884aeSStefan Eßer 1632252884aeSStefan Eßer assert(len == 1); 1633252884aeSStefan Eßer 1634252884aeSStefan Eßer if (rdx) bc_num_putchar('.'); 1635252884aeSStefan Eßer 1636252884aeSStefan Eßer bc_num_putchar(bc_num_hex_digits[n]); 1637252884aeSStefan Eßer } 1638252884aeSStefan Eßer 1639252884aeSStefan Eßer static void bc_num_printDecimal(const BcNum *restrict n) { 1640252884aeSStefan Eßer 1641252884aeSStefan Eßer size_t i, j, rdx = n->rdx; 1642252884aeSStefan Eßer bool zero = true; 1643252884aeSStefan Eßer size_t buffer[BC_BASE_DIGS]; 1644252884aeSStefan Eßer 1645252884aeSStefan Eßer if (n->neg) bc_num_putchar('-'); 1646252884aeSStefan Eßer 1647252884aeSStefan Eßer for (i = n->len - 1; i < n->len; --i) { 1648252884aeSStefan Eßer 1649252884aeSStefan Eßer BcDig n9 = n->num[i]; 1650252884aeSStefan Eßer size_t temp; 1651252884aeSStefan Eßer bool irdx = (i == rdx - 1); 1652252884aeSStefan Eßer 1653252884aeSStefan Eßer zero = (zero & !irdx); 1654252884aeSStefan Eßer temp = n->scale % BC_BASE_DIGS; 1655252884aeSStefan Eßer temp = i || !temp ? 0 : BC_BASE_DIGS - temp; 1656252884aeSStefan Eßer 1657252884aeSStefan Eßer memset(buffer, 0, BC_BASE_DIGS * sizeof(size_t)); 1658252884aeSStefan Eßer 1659252884aeSStefan Eßer for (j = 0; n9 && j < BC_BASE_DIGS; ++j) { 1660*d213476dSStefan Eßer buffer[j] = ((size_t) n9) % BC_BASE; 1661252884aeSStefan Eßer n9 /= BC_BASE; 1662252884aeSStefan Eßer } 1663252884aeSStefan Eßer 1664252884aeSStefan Eßer for (j = BC_BASE_DIGS - 1; j < BC_BASE_DIGS && j >= temp; --j) { 1665252884aeSStefan Eßer bool print_rdx = (irdx & (j == BC_BASE_DIGS - 1)); 1666252884aeSStefan Eßer zero = (zero && buffer[j] == 0); 1667252884aeSStefan Eßer if (!zero) bc_num_printHex(buffer[j], 1, print_rdx); 1668252884aeSStefan Eßer } 1669252884aeSStefan Eßer } 1670252884aeSStefan Eßer } 1671252884aeSStefan Eßer 1672252884aeSStefan Eßer #if BC_ENABLE_EXTRA_MATH 1673252884aeSStefan Eßer static void bc_num_printExponent(const BcNum *restrict n, bool eng) { 1674252884aeSStefan Eßer 1675252884aeSStefan Eßer bool neg = (n->len <= n->rdx); 1676252884aeSStefan Eßer BcNum temp, exp; 1677252884aeSStefan Eßer size_t places, mod; 1678252884aeSStefan Eßer BcDig digs[BC_NUM_BIGDIG_LOG10]; 1679252884aeSStefan Eßer 1680252884aeSStefan Eßer BC_SIG_LOCK; 1681252884aeSStefan Eßer 1682252884aeSStefan Eßer bc_num_createCopy(&temp, n); 1683252884aeSStefan Eßer 1684252884aeSStefan Eßer BC_SETJMP_LOCKED(exit); 1685252884aeSStefan Eßer 1686252884aeSStefan Eßer BC_SIG_UNLOCK; 1687252884aeSStefan Eßer 1688252884aeSStefan Eßer if (neg) { 1689252884aeSStefan Eßer 1690252884aeSStefan Eßer size_t i, idx = bc_num_nonzeroLen(n) - 1; 1691252884aeSStefan Eßer 1692252884aeSStefan Eßer places = 1; 1693252884aeSStefan Eßer 1694252884aeSStefan Eßer for (i = BC_BASE_DIGS - 1; i < BC_BASE_DIGS; --i) { 1695252884aeSStefan Eßer if (bc_num_pow10[i] > (BcBigDig) n->num[idx]) places += 1; 1696252884aeSStefan Eßer else break; 1697252884aeSStefan Eßer } 1698252884aeSStefan Eßer 1699252884aeSStefan Eßer places += (n->rdx - (idx + 1)) * BC_BASE_DIGS; 1700252884aeSStefan Eßer mod = places % 3; 1701252884aeSStefan Eßer 1702252884aeSStefan Eßer if (eng && mod != 0) places += 3 - mod; 1703252884aeSStefan Eßer bc_num_shiftLeft(&temp, places); 1704252884aeSStefan Eßer } 1705252884aeSStefan Eßer else { 1706252884aeSStefan Eßer places = bc_num_intDigits(n) - 1; 1707252884aeSStefan Eßer mod = places % 3; 1708252884aeSStefan Eßer if (eng && mod != 0) places -= 3 - (3 - mod); 1709252884aeSStefan Eßer bc_num_shiftRight(&temp, places); 1710252884aeSStefan Eßer } 1711252884aeSStefan Eßer 1712252884aeSStefan Eßer bc_num_printDecimal(&temp); 1713252884aeSStefan Eßer bc_num_putchar('e'); 1714252884aeSStefan Eßer 1715252884aeSStefan Eßer if (!places) { 1716252884aeSStefan Eßer bc_num_printHex(0, 1, false); 1717252884aeSStefan Eßer goto exit; 1718252884aeSStefan Eßer } 1719252884aeSStefan Eßer 1720252884aeSStefan Eßer if (neg) bc_num_putchar('-'); 1721252884aeSStefan Eßer 1722252884aeSStefan Eßer bc_num_setup(&exp, digs, BC_NUM_BIGDIG_LOG10); 1723252884aeSStefan Eßer bc_num_bigdig2num(&exp, (BcBigDig) places); 1724252884aeSStefan Eßer 1725252884aeSStefan Eßer bc_num_printDecimal(&exp); 1726252884aeSStefan Eßer 1727252884aeSStefan Eßer exit: 1728252884aeSStefan Eßer BC_SIG_MAYLOCK; 1729252884aeSStefan Eßer bc_num_free(&temp); 1730252884aeSStefan Eßer BC_LONGJMP_CONT; 1731252884aeSStefan Eßer } 1732252884aeSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH 1733252884aeSStefan Eßer 1734252884aeSStefan Eßer static void bc_num_printFixup(BcNum *restrict n, BcBigDig rem, 1735252884aeSStefan Eßer BcBigDig pow, size_t idx) 1736252884aeSStefan Eßer { 1737252884aeSStefan Eßer size_t i, len = n->len - idx; 1738252884aeSStefan Eßer BcBigDig acc; 1739252884aeSStefan Eßer BcDig *a = n->num + idx; 1740252884aeSStefan Eßer 1741252884aeSStefan Eßer if (len < 2) return; 1742252884aeSStefan Eßer 1743252884aeSStefan Eßer for (i = len - 1; i > 0; --i) { 1744252884aeSStefan Eßer 1745252884aeSStefan Eßer acc = ((BcBigDig) a[i]) * rem + ((BcBigDig) a[i - 1]); 1746252884aeSStefan Eßer a[i - 1] = (BcDig) (acc % pow); 1747252884aeSStefan Eßer acc /= pow; 1748252884aeSStefan Eßer acc += (BcBigDig) a[i]; 1749252884aeSStefan Eßer 1750252884aeSStefan Eßer if (acc >= BC_BASE_POW) { 1751252884aeSStefan Eßer 1752252884aeSStefan Eßer if (i == len - 1) { 1753252884aeSStefan Eßer len = bc_vm_growSize(len, 1); 1754252884aeSStefan Eßer bc_num_expand(n, bc_vm_growSize(len, idx)); 1755252884aeSStefan Eßer a = n->num + idx; 1756252884aeSStefan Eßer a[len - 1] = 0; 1757252884aeSStefan Eßer } 1758252884aeSStefan Eßer 1759252884aeSStefan Eßer a[i + 1] += acc / BC_BASE_POW; 1760252884aeSStefan Eßer acc %= BC_BASE_POW; 1761252884aeSStefan Eßer } 1762252884aeSStefan Eßer 1763252884aeSStefan Eßer assert(acc < BC_BASE_POW); 1764252884aeSStefan Eßer a[i] = (BcDig) acc; 1765252884aeSStefan Eßer } 1766252884aeSStefan Eßer 1767252884aeSStefan Eßer n->len = len + idx; 1768252884aeSStefan Eßer } 1769252884aeSStefan Eßer 1770252884aeSStefan Eßer static void bc_num_printPrepare(BcNum *restrict n, BcBigDig rem, 1771252884aeSStefan Eßer BcBigDig pow) 1772252884aeSStefan Eßer { 1773252884aeSStefan Eßer size_t i; 1774252884aeSStefan Eßer 1775252884aeSStefan Eßer for (i = 0; i < n->len; ++i) bc_num_printFixup(n, rem, pow, i); 1776252884aeSStefan Eßer 1777252884aeSStefan Eßer for (i = 0; i < n->len; ++i) { 1778252884aeSStefan Eßer 1779252884aeSStefan Eßer assert(pow == ((BcBigDig) ((BcDig) pow))); 1780252884aeSStefan Eßer 1781252884aeSStefan Eßer if (n->num[i] >= (BcDig) pow) { 1782252884aeSStefan Eßer 1783252884aeSStefan Eßer if (i + 1 == n->len) { 1784252884aeSStefan Eßer n->len = bc_vm_growSize(n->len, 1); 1785252884aeSStefan Eßer bc_num_expand(n, n->len); 1786252884aeSStefan Eßer n->num[i + 1] = 0; 1787252884aeSStefan Eßer } 1788252884aeSStefan Eßer 1789252884aeSStefan Eßer assert(pow < BC_BASE_POW); 1790252884aeSStefan Eßer n->num[i + 1] += n->num[i] / ((BcDig) pow); 1791252884aeSStefan Eßer n->num[i] %= (BcDig) pow; 1792252884aeSStefan Eßer } 1793252884aeSStefan Eßer } 1794252884aeSStefan Eßer } 1795252884aeSStefan Eßer 1796252884aeSStefan Eßer static void bc_num_printNum(BcNum *restrict n, BcBigDig base, 1797252884aeSStefan Eßer size_t len, BcNumDigitOp print) 1798252884aeSStefan Eßer { 1799252884aeSStefan Eßer BcVec stack; 1800252884aeSStefan Eßer BcNum intp, fracp1, fracp2, digit, flen1, flen2, *n1, *n2, *temp; 1801252884aeSStefan Eßer BcBigDig dig = 0, *ptr, acc, exp; 1802252884aeSStefan Eßer size_t i, j; 1803252884aeSStefan Eßer bool radix; 1804252884aeSStefan Eßer BcDig digit_digs[BC_NUM_BIGDIG_LOG10 + 1]; 1805252884aeSStefan Eßer 1806252884aeSStefan Eßer assert(base > 1); 1807252884aeSStefan Eßer 1808252884aeSStefan Eßer if (BC_NUM_ZERO(n)) { 1809252884aeSStefan Eßer print(0, len, false); 1810252884aeSStefan Eßer return; 1811252884aeSStefan Eßer } 1812252884aeSStefan Eßer 1813252884aeSStefan Eßer // This function uses an algorithm that Stefan Esser <se@freebsd.org> came 1814252884aeSStefan Eßer // up with to print the integer part of a number. What it does is convert 1815252884aeSStefan Eßer // intp into a number of the specified base, but it does it directly, 1816252884aeSStefan Eßer // instead of just doing a series of divisions and printing the remainders 1817252884aeSStefan Eßer // in reverse order. 1818252884aeSStefan Eßer // 1819252884aeSStefan Eßer // Let me explain in a bit more detail: 1820252884aeSStefan Eßer // 1821252884aeSStefan Eßer // The algorithm takes the current least significant digit (after intp has 1822252884aeSStefan Eßer // been converted to an integer) and the next to least significant digit, 1823252884aeSStefan Eßer // and it converts the least significant digit into one of the specified 1824252884aeSStefan Eßer // base, putting any overflow into the next to least significant digit. It 1825252884aeSStefan Eßer // iterates through the whole number, from least significant to most 1826252884aeSStefan Eßer // significant, doing this conversion. At the end of that iteration, the 1827252884aeSStefan Eßer // least significant digit is converted, but the others are not, so it 1828252884aeSStefan Eßer // iterates again, starting at the next to least significant digit. It keeps 1829252884aeSStefan Eßer // doing that conversion, skipping one more digit than the last time, until 1830252884aeSStefan Eßer // all digits have been converted. Then it prints them in reverse order. 1831252884aeSStefan Eßer // 1832252884aeSStefan Eßer // That is the gist of the algorithm. It leaves out several things, such as 1833252884aeSStefan Eßer // the fact that digits are not always converted into the specified base, 1834252884aeSStefan Eßer // but into something close, basically a power of the specified base. In 1835252884aeSStefan Eßer // Stefan's words, "You could consider BcDigs to be of base 10^BC_BASE_DIGS 1836252884aeSStefan Eßer // in the normal case and obase^N for the largest value of N that satisfies 1837252884aeSStefan Eßer // obase^N <= 10^BC_BASE_DIGS. [This means that] the result is not in base 1838252884aeSStefan Eßer // "obase", but in base "obase^N", which happens to be printable as a number 1839252884aeSStefan Eßer // of base "obase" without consideration for neighbouring BcDigs." This fact 1840252884aeSStefan Eßer // is what necessitates the existence of the loop later in this function. 1841252884aeSStefan Eßer // 1842252884aeSStefan Eßer // The conversion happens in bc_num_printPrepare() where the outer loop 1843252884aeSStefan Eßer // happens and bc_num_printFixup() where the inner loop, or actual 1844252884aeSStefan Eßer // conversion, happens. 1845252884aeSStefan Eßer 1846252884aeSStefan Eßer BC_SIG_LOCK; 1847252884aeSStefan Eßer 1848252884aeSStefan Eßer bc_vec_init(&stack, sizeof(BcBigDig), NULL); 1849252884aeSStefan Eßer bc_num_init(&fracp1, n->rdx); 1850252884aeSStefan Eßer 1851252884aeSStefan Eßer bc_num_createCopy(&intp, n); 1852252884aeSStefan Eßer 1853252884aeSStefan Eßer BC_SETJMP_LOCKED(err); 1854252884aeSStefan Eßer 1855252884aeSStefan Eßer BC_SIG_UNLOCK; 1856252884aeSStefan Eßer 1857252884aeSStefan Eßer bc_num_truncate(&intp, intp.scale); 1858252884aeSStefan Eßer 1859252884aeSStefan Eßer bc_num_sub(n, &intp, &fracp1, 0); 1860252884aeSStefan Eßer 1861252884aeSStefan Eßer if (base != vm.last_base) { 1862252884aeSStefan Eßer 1863252884aeSStefan Eßer vm.last_pow = 1; 1864252884aeSStefan Eßer vm.last_exp = 0; 1865252884aeSStefan Eßer 1866252884aeSStefan Eßer while (vm.last_pow * base <= BC_BASE_POW) { 1867252884aeSStefan Eßer vm.last_pow *= base; 1868252884aeSStefan Eßer vm.last_exp += 1; 1869252884aeSStefan Eßer } 1870252884aeSStefan Eßer 1871252884aeSStefan Eßer vm.last_rem = BC_BASE_POW - vm.last_pow; 1872252884aeSStefan Eßer vm.last_base = base; 1873252884aeSStefan Eßer } 1874252884aeSStefan Eßer 1875252884aeSStefan Eßer exp = vm.last_exp; 1876252884aeSStefan Eßer 1877252884aeSStefan Eßer if (vm.last_rem != 0) bc_num_printPrepare(&intp, vm.last_rem, vm.last_pow); 1878252884aeSStefan Eßer 1879252884aeSStefan Eßer for (i = 0; i < intp.len; ++i) { 1880252884aeSStefan Eßer 1881252884aeSStefan Eßer acc = (BcBigDig) intp.num[i]; 1882252884aeSStefan Eßer 1883252884aeSStefan Eßer for (j = 0; j < exp && (i < intp.len - 1 || acc != 0); ++j) 1884252884aeSStefan Eßer { 1885252884aeSStefan Eßer if (j != exp - 1) { 1886252884aeSStefan Eßer dig = acc % base; 1887252884aeSStefan Eßer acc /= base; 1888252884aeSStefan Eßer } 1889252884aeSStefan Eßer else { 1890252884aeSStefan Eßer dig = acc; 1891252884aeSStefan Eßer acc = 0; 1892252884aeSStefan Eßer } 1893252884aeSStefan Eßer 1894252884aeSStefan Eßer assert(dig < base); 1895252884aeSStefan Eßer 1896252884aeSStefan Eßer bc_vec_push(&stack, &dig); 1897252884aeSStefan Eßer } 1898252884aeSStefan Eßer 1899252884aeSStefan Eßer assert(acc == 0); 1900252884aeSStefan Eßer } 1901252884aeSStefan Eßer 1902252884aeSStefan Eßer for (i = 0; i < stack.len; ++i) { 1903252884aeSStefan Eßer ptr = bc_vec_item_rev(&stack, i); 1904252884aeSStefan Eßer assert(ptr != NULL); 1905252884aeSStefan Eßer print(*ptr, len, false); 1906252884aeSStefan Eßer } 1907252884aeSStefan Eßer 1908252884aeSStefan Eßer if (!n->scale) goto err; 1909252884aeSStefan Eßer 1910252884aeSStefan Eßer BC_SIG_LOCK; 1911252884aeSStefan Eßer 1912252884aeSStefan Eßer BC_UNSETJMP; 1913252884aeSStefan Eßer 1914252884aeSStefan Eßer bc_num_init(&fracp2, n->rdx); 1915252884aeSStefan Eßer bc_num_setup(&digit, digit_digs, sizeof(digit_digs) / sizeof(BcDig)); 1916252884aeSStefan Eßer bc_num_init(&flen1, BC_NUM_BIGDIG_LOG10); 1917252884aeSStefan Eßer bc_num_init(&flen2, BC_NUM_BIGDIG_LOG10); 1918252884aeSStefan Eßer 1919252884aeSStefan Eßer BC_SETJMP_LOCKED(frac_err); 1920252884aeSStefan Eßer 1921252884aeSStefan Eßer BC_SIG_UNLOCK; 1922252884aeSStefan Eßer 1923252884aeSStefan Eßer bc_num_one(&flen1); 1924252884aeSStefan Eßer 1925252884aeSStefan Eßer radix = true; 1926252884aeSStefan Eßer n1 = &flen1; 1927252884aeSStefan Eßer n2 = &flen2; 1928252884aeSStefan Eßer 1929252884aeSStefan Eßer fracp2.scale = n->scale; 1930252884aeSStefan Eßer fracp2.rdx = BC_NUM_RDX(fracp2.scale); 1931252884aeSStefan Eßer 1932252884aeSStefan Eßer while (bc_num_intDigits(n1) < n->scale + 1) { 1933252884aeSStefan Eßer 1934252884aeSStefan Eßer bc_num_expand(&fracp2, fracp1.len + 1); 1935252884aeSStefan Eßer bc_num_mulArray(&fracp1, base, &fracp2); 1936252884aeSStefan Eßer if (fracp2.len < fracp2.rdx) fracp2.len = fracp2.rdx; 1937252884aeSStefan Eßer 1938252884aeSStefan Eßer // fracp is guaranteed to be non-negative and small enough. 1939252884aeSStefan Eßer bc_num_bigdig2(&fracp2, &dig); 1940252884aeSStefan Eßer 1941252884aeSStefan Eßer bc_num_bigdig2num(&digit, dig); 1942252884aeSStefan Eßer bc_num_sub(&fracp2, &digit, &fracp1, 0); 1943252884aeSStefan Eßer 1944252884aeSStefan Eßer print(dig, len, radix); 1945252884aeSStefan Eßer bc_num_mulArray(n1, base, n2); 1946252884aeSStefan Eßer 1947252884aeSStefan Eßer radix = false; 1948252884aeSStefan Eßer temp = n1; 1949252884aeSStefan Eßer n1 = n2; 1950252884aeSStefan Eßer n2 = temp; 1951252884aeSStefan Eßer } 1952252884aeSStefan Eßer 1953252884aeSStefan Eßer frac_err: 1954252884aeSStefan Eßer BC_SIG_MAYLOCK; 1955252884aeSStefan Eßer bc_num_free(&flen2); 1956252884aeSStefan Eßer bc_num_free(&flen1); 1957252884aeSStefan Eßer bc_num_free(&fracp2); 1958252884aeSStefan Eßer err: 1959252884aeSStefan Eßer BC_SIG_MAYLOCK; 1960252884aeSStefan Eßer bc_num_free(&fracp1); 1961252884aeSStefan Eßer bc_num_free(&intp); 1962252884aeSStefan Eßer bc_vec_free(&stack); 1963252884aeSStefan Eßer BC_LONGJMP_CONT; 1964252884aeSStefan Eßer } 1965252884aeSStefan Eßer 1966252884aeSStefan Eßer static void bc_num_printBase(BcNum *restrict n, BcBigDig base) { 1967252884aeSStefan Eßer 1968252884aeSStefan Eßer size_t width; 1969252884aeSStefan Eßer BcNumDigitOp print; 1970252884aeSStefan Eßer bool neg = n->neg; 1971252884aeSStefan Eßer 1972252884aeSStefan Eßer if (neg) bc_num_putchar('-'); 1973252884aeSStefan Eßer 1974252884aeSStefan Eßer n->neg = false; 1975252884aeSStefan Eßer 1976252884aeSStefan Eßer if (base <= BC_NUM_MAX_POSIX_IBASE) { 1977252884aeSStefan Eßer width = 1; 1978252884aeSStefan Eßer print = bc_num_printHex; 1979252884aeSStefan Eßer } 1980252884aeSStefan Eßer else { 1981252884aeSStefan Eßer assert(base <= BC_BASE_POW); 1982252884aeSStefan Eßer width = bc_num_log10(base - 1); 1983252884aeSStefan Eßer print = bc_num_printDigits; 1984252884aeSStefan Eßer } 1985252884aeSStefan Eßer 1986252884aeSStefan Eßer bc_num_printNum(n, base, width, print); 1987252884aeSStefan Eßer n->neg = neg; 1988252884aeSStefan Eßer } 1989252884aeSStefan Eßer 1990252884aeSStefan Eßer #if DC_ENABLED 1991252884aeSStefan Eßer void bc_num_stream(BcNum *restrict n, BcBigDig base) { 1992252884aeSStefan Eßer bc_num_printNum(n, base, 1, bc_num_printChar); 1993252884aeSStefan Eßer } 1994252884aeSStefan Eßer #endif // DC_ENABLED 1995252884aeSStefan Eßer 1996252884aeSStefan Eßer void bc_num_setup(BcNum *restrict n, BcDig *restrict num, size_t cap) { 1997252884aeSStefan Eßer assert(n != NULL); 1998252884aeSStefan Eßer n->num = num; 1999252884aeSStefan Eßer n->cap = cap; 2000252884aeSStefan Eßer bc_num_zero(n); 2001252884aeSStefan Eßer } 2002252884aeSStefan Eßer 2003252884aeSStefan Eßer void bc_num_init(BcNum *restrict n, size_t req) { 2004252884aeSStefan Eßer 2005252884aeSStefan Eßer BcDig *num; 2006252884aeSStefan Eßer 2007252884aeSStefan Eßer BC_SIG_ASSERT_LOCKED; 2008252884aeSStefan Eßer 2009252884aeSStefan Eßer assert(n != NULL); 2010252884aeSStefan Eßer 2011252884aeSStefan Eßer req = req >= BC_NUM_DEF_SIZE ? req : BC_NUM_DEF_SIZE; 2012252884aeSStefan Eßer 2013252884aeSStefan Eßer if (req == BC_NUM_DEF_SIZE && vm.temps.len) { 2014252884aeSStefan Eßer BcNum *nptr = bc_vec_top(&vm.temps); 2015252884aeSStefan Eßer num = nptr->num; 2016252884aeSStefan Eßer bc_vec_pop(&vm.temps); 2017252884aeSStefan Eßer } 2018252884aeSStefan Eßer else num = bc_vm_malloc(BC_NUM_SIZE(req)); 2019252884aeSStefan Eßer 2020252884aeSStefan Eßer bc_num_setup(n, num, req); 2021252884aeSStefan Eßer } 2022252884aeSStefan Eßer 2023252884aeSStefan Eßer void bc_num_clear(BcNum *restrict n) { 2024252884aeSStefan Eßer n->num = NULL; 2025252884aeSStefan Eßer n->cap = 0; 2026252884aeSStefan Eßer } 2027252884aeSStefan Eßer 2028252884aeSStefan Eßer void bc_num_free(void *num) { 2029252884aeSStefan Eßer 2030252884aeSStefan Eßer BcNum *n = (BcNum*) num; 2031252884aeSStefan Eßer 2032252884aeSStefan Eßer BC_SIG_ASSERT_LOCKED; 2033252884aeSStefan Eßer 2034252884aeSStefan Eßer assert(n != NULL); 2035252884aeSStefan Eßer 2036252884aeSStefan Eßer if (n->cap == BC_NUM_DEF_SIZE) bc_vec_push(&vm.temps, n); 2037252884aeSStefan Eßer else free(n->num); 2038252884aeSStefan Eßer } 2039252884aeSStefan Eßer 2040252884aeSStefan Eßer void bc_num_copy(BcNum *d, const BcNum *s) { 2041252884aeSStefan Eßer assert(d != NULL && s != NULL); 2042252884aeSStefan Eßer if (d == s) return; 2043252884aeSStefan Eßer bc_num_expand(d, s->len); 2044252884aeSStefan Eßer d->len = s->len; 2045252884aeSStefan Eßer d->neg = s->neg; 2046252884aeSStefan Eßer d->rdx = s->rdx; 2047252884aeSStefan Eßer d->scale = s->scale; 2048252884aeSStefan Eßer memcpy(d->num, s->num, BC_NUM_SIZE(d->len)); 2049252884aeSStefan Eßer } 2050252884aeSStefan Eßer 2051252884aeSStefan Eßer void bc_num_createCopy(BcNum *d, const BcNum *s) { 2052252884aeSStefan Eßer BC_SIG_ASSERT_LOCKED; 2053252884aeSStefan Eßer bc_num_init(d, s->len); 2054252884aeSStefan Eßer bc_num_copy(d, s); 2055252884aeSStefan Eßer } 2056252884aeSStefan Eßer 2057252884aeSStefan Eßer void bc_num_createFromBigdig(BcNum *n, BcBigDig val) { 2058252884aeSStefan Eßer BC_SIG_ASSERT_LOCKED; 2059252884aeSStefan Eßer bc_num_init(n, (BC_NUM_BIGDIG_LOG10 - 1) / BC_BASE_DIGS + 1); 2060252884aeSStefan Eßer bc_num_bigdig2num(n, val); 2061252884aeSStefan Eßer } 2062252884aeSStefan Eßer 2063252884aeSStefan Eßer size_t bc_num_scale(const BcNum *restrict n) { 2064252884aeSStefan Eßer return n->scale; 2065252884aeSStefan Eßer } 2066252884aeSStefan Eßer 2067252884aeSStefan Eßer size_t bc_num_len(const BcNum *restrict n) { 2068252884aeSStefan Eßer 2069252884aeSStefan Eßer size_t len = n->len; 2070252884aeSStefan Eßer 2071252884aeSStefan Eßer if (BC_NUM_ZERO(n)) return 0; 2072252884aeSStefan Eßer 2073252884aeSStefan Eßer if (n->rdx == len) { 2074252884aeSStefan Eßer 2075252884aeSStefan Eßer size_t zero, scale; 2076252884aeSStefan Eßer 2077252884aeSStefan Eßer len = bc_num_nonzeroLen(n); 2078252884aeSStefan Eßer 2079252884aeSStefan Eßer scale = n->scale % BC_BASE_DIGS; 2080252884aeSStefan Eßer scale = scale ? scale : BC_BASE_DIGS; 2081252884aeSStefan Eßer 2082252884aeSStefan Eßer zero = bc_num_zeroDigits(n->num + len - 1); 2083252884aeSStefan Eßer 2084252884aeSStefan Eßer len = len * BC_BASE_DIGS - zero - (BC_BASE_DIGS - scale); 2085252884aeSStefan Eßer } 2086252884aeSStefan Eßer else len = bc_num_intDigits(n) + n->scale; 2087252884aeSStefan Eßer 2088252884aeSStefan Eßer return len; 2089252884aeSStefan Eßer } 2090252884aeSStefan Eßer 2091252884aeSStefan Eßer void bc_num_parse(BcNum *restrict n, const char *restrict val, 2092252884aeSStefan Eßer BcBigDig base, bool letter) 2093252884aeSStefan Eßer { 2094252884aeSStefan Eßer assert(n != NULL && val != NULL && base); 2095252884aeSStefan Eßer assert(base >= BC_NUM_MIN_BASE && base <= vm.maxes[BC_PROG_GLOBALS_IBASE]); 2096252884aeSStefan Eßer assert(bc_num_strValid(val)); 2097252884aeSStefan Eßer 2098252884aeSStefan Eßer if (letter) { 2099252884aeSStefan Eßer BcBigDig dig = bc_num_parseChar(val[0], BC_NUM_MAX_LBASE); 2100252884aeSStefan Eßer bc_num_bigdig2num(n, dig); 2101252884aeSStefan Eßer } 2102252884aeSStefan Eßer else if (base == BC_BASE) bc_num_parseDecimal(n, val); 2103252884aeSStefan Eßer else bc_num_parseBase(n, val, base); 2104252884aeSStefan Eßer } 2105252884aeSStefan Eßer 2106252884aeSStefan Eßer void bc_num_print(BcNum *restrict n, BcBigDig base, bool newline) { 2107252884aeSStefan Eßer 2108252884aeSStefan Eßer assert(n != NULL); 2109252884aeSStefan Eßer assert(BC_ENABLE_EXTRA_MATH || base >= BC_NUM_MIN_BASE); 2110252884aeSStefan Eßer 2111252884aeSStefan Eßer bc_num_printNewline(); 2112252884aeSStefan Eßer 2113252884aeSStefan Eßer if (BC_NUM_ZERO(n)) bc_num_printHex(0, 1, false); 2114252884aeSStefan Eßer else if (base == BC_BASE) bc_num_printDecimal(n); 2115252884aeSStefan Eßer #if BC_ENABLE_EXTRA_MATH 2116252884aeSStefan Eßer else if (base == 0 || base == 1) 2117252884aeSStefan Eßer bc_num_printExponent(n, base != 0); 2118252884aeSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH 2119252884aeSStefan Eßer else bc_num_printBase(n, base); 2120252884aeSStefan Eßer 2121252884aeSStefan Eßer if (newline) bc_num_putchar('\n'); 2122252884aeSStefan Eßer } 2123252884aeSStefan Eßer 2124252884aeSStefan Eßer void bc_num_bigdig2(const BcNum *restrict n, BcBigDig *result) { 2125252884aeSStefan Eßer 2126252884aeSStefan Eßer // This function returns no errors because it's guaranteed to succeed if 2127252884aeSStefan Eßer // its preconditions are met. Those preconditions include both parameters 2128252884aeSStefan Eßer // being non-NULL, n being non-negative, and n being less than vm.max. If 2129252884aeSStefan Eßer // all of that is true, then we can just convert without worrying about 2130252884aeSStefan Eßer // negative errors or overflow. We also don't care about signals because 2131252884aeSStefan Eßer // this function should execute in only a few iterations, meaning that 2132252884aeSStefan Eßer // ignoring signals here should be fine. 2133252884aeSStefan Eßer 2134252884aeSStefan Eßer BcBigDig r = 0; 2135252884aeSStefan Eßer 2136252884aeSStefan Eßer assert(n != NULL && result != NULL); 2137252884aeSStefan Eßer assert(!n->neg); 2138252884aeSStefan Eßer assert(bc_num_cmp(n, &vm.max) < 0); 2139252884aeSStefan Eßer assert(n->len - n->rdx <= 3); 2140252884aeSStefan Eßer 2141252884aeSStefan Eßer // There is a small speed win from unrolling the loop here, and since it 2142252884aeSStefan Eßer // only adds 53 bytes, I decided that it was worth it. 2143252884aeSStefan Eßer switch (n->len - n->rdx) { 2144252884aeSStefan Eßer case 3: 2145252884aeSStefan Eßer r = (BcBigDig) n->num[n->rdx + 2]; 2146252884aeSStefan Eßer // Fallthrough. 2147252884aeSStefan Eßer case 2: 2148252884aeSStefan Eßer r = r * BC_BASE_POW + (BcBigDig) n->num[n->rdx + 1]; 2149252884aeSStefan Eßer // Fallthrough. 2150252884aeSStefan Eßer case 1: 2151252884aeSStefan Eßer r = r * BC_BASE_POW + (BcBigDig) n->num[n->rdx]; 2152252884aeSStefan Eßer } 2153252884aeSStefan Eßer 2154252884aeSStefan Eßer *result = r; 2155252884aeSStefan Eßer } 2156252884aeSStefan Eßer 2157252884aeSStefan Eßer void bc_num_bigdig(const BcNum *restrict n, BcBigDig *result) { 2158252884aeSStefan Eßer 2159252884aeSStefan Eßer assert(n != NULL && result != NULL); 2160252884aeSStefan Eßer 2161252884aeSStefan Eßer if (BC_ERR(n->neg)) bc_vm_err(BC_ERROR_MATH_NEGATIVE); 2162252884aeSStefan Eßer if (BC_ERR(bc_num_cmp(n, &vm.max) >= 0)) 2163252884aeSStefan Eßer bc_vm_err(BC_ERROR_MATH_OVERFLOW); 2164252884aeSStefan Eßer 2165252884aeSStefan Eßer bc_num_bigdig2(n, result); 2166252884aeSStefan Eßer } 2167252884aeSStefan Eßer 2168252884aeSStefan Eßer void bc_num_bigdig2num(BcNum *restrict n, BcBigDig val) { 2169252884aeSStefan Eßer 2170252884aeSStefan Eßer BcDig *ptr; 2171252884aeSStefan Eßer size_t i; 2172252884aeSStefan Eßer 2173252884aeSStefan Eßer assert(n != NULL); 2174252884aeSStefan Eßer 2175252884aeSStefan Eßer bc_num_zero(n); 2176252884aeSStefan Eßer 2177252884aeSStefan Eßer if (!val) return; 2178252884aeSStefan Eßer 2179252884aeSStefan Eßer bc_num_expand(n, BC_NUM_BIGDIG_LOG10); 2180252884aeSStefan Eßer 2181252884aeSStefan Eßer for (ptr = n->num, i = 0; val; ++i, val /= BC_BASE_POW) 2182252884aeSStefan Eßer ptr[i] = val % BC_BASE_POW; 2183252884aeSStefan Eßer 2184252884aeSStefan Eßer n->len = i; 2185252884aeSStefan Eßer } 2186252884aeSStefan Eßer 21873aa99676SStefan Eßer #if BC_ENABLE_EXTRA_MATH && BC_ENABLE_RAND 2188252884aeSStefan Eßer void bc_num_rng(const BcNum *restrict n, BcRNG *rng) { 2189252884aeSStefan Eßer 2190252884aeSStefan Eßer BcNum pow, temp, temp2, intn, frac; 2191252884aeSStefan Eßer BcRand state1, state2, inc1, inc2; 2192252884aeSStefan Eßer BcDig pow_num[BC_RAND_NUM_SIZE]; 2193252884aeSStefan Eßer 2194252884aeSStefan Eßer bc_num_setup(&pow, pow_num, sizeof(pow_num) / sizeof(BcDig)); 2195252884aeSStefan Eßer 2196252884aeSStefan Eßer BC_SIG_LOCK; 2197252884aeSStefan Eßer 2198252884aeSStefan Eßer bc_num_init(&temp, n->len); 2199252884aeSStefan Eßer bc_num_init(&temp2, n->len); 2200252884aeSStefan Eßer bc_num_init(&frac, n->rdx); 2201252884aeSStefan Eßer bc_num_init(&intn, bc_num_int(n)); 2202252884aeSStefan Eßer 2203252884aeSStefan Eßer BC_SETJMP_LOCKED(err); 2204252884aeSStefan Eßer 2205252884aeSStefan Eßer BC_SIG_UNLOCK; 2206252884aeSStefan Eßer 2207252884aeSStefan Eßer bc_num_mul(&vm.max, &vm.max, &pow, 0); 2208252884aeSStefan Eßer 2209252884aeSStefan Eßer memcpy(frac.num, n->num, BC_NUM_SIZE(n->rdx)); 2210252884aeSStefan Eßer frac.len = n->rdx; 2211252884aeSStefan Eßer frac.rdx = n->rdx; 2212252884aeSStefan Eßer frac.scale = n->scale; 2213252884aeSStefan Eßer 2214252884aeSStefan Eßer bc_num_mul(&frac, &pow, &temp, 0); 2215252884aeSStefan Eßer 2216252884aeSStefan Eßer bc_num_truncate(&temp, temp.scale); 2217252884aeSStefan Eßer bc_num_copy(&frac, &temp); 2218252884aeSStefan Eßer 2219252884aeSStefan Eßer memcpy(intn.num, n->num + n->rdx, BC_NUM_SIZE(bc_num_int(n))); 2220252884aeSStefan Eßer intn.len = bc_num_int(n); 2221252884aeSStefan Eßer 2222252884aeSStefan Eßer // This assert is here because it has to be true. It is also here to justify 2223252884aeSStefan Eßer // the use of BC_ERROR_SIGNAL_ONLY() on each of the divmod's and mod's 2224252884aeSStefan Eßer // below. 2225252884aeSStefan Eßer assert(BC_NUM_NONZERO(&vm.max)); 2226252884aeSStefan Eßer 2227252884aeSStefan Eßer if (BC_NUM_NONZERO(&frac)) { 2228252884aeSStefan Eßer 2229252884aeSStefan Eßer bc_num_divmod(&frac, &vm.max, &temp, &temp2, 0); 2230252884aeSStefan Eßer 2231252884aeSStefan Eßer // frac is guaranteed to be smaller than vm.max * vm.max (pow). 2232252884aeSStefan Eßer // This means that when dividing frac by vm.max, as above, the 2233252884aeSStefan Eßer // quotient and remainder are both guaranteed to be less than vm.max, 2234252884aeSStefan Eßer // which means we can use bc_num_bigdig2() here and not worry about 2235252884aeSStefan Eßer // overflow. 2236252884aeSStefan Eßer bc_num_bigdig2(&temp2, (BcBigDig*) &state1); 2237252884aeSStefan Eßer bc_num_bigdig2(&temp, (BcBigDig*) &state2); 2238252884aeSStefan Eßer } 2239252884aeSStefan Eßer else state1 = state2 = 0; 2240252884aeSStefan Eßer 2241252884aeSStefan Eßer if (BC_NUM_NONZERO(&intn)) { 2242252884aeSStefan Eßer 2243252884aeSStefan Eßer bc_num_divmod(&intn, &vm.max, &temp, &temp2, 0); 2244252884aeSStefan Eßer 2245252884aeSStefan Eßer // Because temp2 is the mod of vm.max, from above, it is guaranteed 2246252884aeSStefan Eßer // to be small enough to use bc_num_bigdig2(). 2247252884aeSStefan Eßer bc_num_bigdig2(&temp2, (BcBigDig*) &inc1); 2248252884aeSStefan Eßer 2249252884aeSStefan Eßer if (bc_num_cmp(&temp, &vm.max) >= 0) { 2250252884aeSStefan Eßer bc_num_copy(&temp2, &temp); 2251252884aeSStefan Eßer bc_num_mod(&temp2, &vm.max, &temp, 0); 2252252884aeSStefan Eßer } 2253252884aeSStefan Eßer 2254252884aeSStefan Eßer // The if statement above ensures that temp is less than vm.max, which 2255252884aeSStefan Eßer // means that we can use bc_num_bigdig2() here. 2256252884aeSStefan Eßer bc_num_bigdig2(&temp, (BcBigDig*) &inc2); 2257252884aeSStefan Eßer } 2258252884aeSStefan Eßer else inc1 = inc2 = 0; 2259252884aeSStefan Eßer 2260252884aeSStefan Eßer bc_rand_seed(rng, state1, state2, inc1, inc2); 2261252884aeSStefan Eßer 2262252884aeSStefan Eßer err: 2263252884aeSStefan Eßer BC_SIG_MAYLOCK; 2264252884aeSStefan Eßer bc_num_free(&intn); 2265252884aeSStefan Eßer bc_num_free(&frac); 2266252884aeSStefan Eßer bc_num_free(&temp2); 2267252884aeSStefan Eßer bc_num_free(&temp); 2268252884aeSStefan Eßer BC_LONGJMP_CONT; 2269252884aeSStefan Eßer } 2270252884aeSStefan Eßer 2271252884aeSStefan Eßer void bc_num_createFromRNG(BcNum *restrict n, BcRNG *rng) { 2272252884aeSStefan Eßer 2273252884aeSStefan Eßer BcRand s1, s2, i1, i2; 2274252884aeSStefan Eßer BcNum pow, conv, temp1, temp2, temp3; 2275252884aeSStefan Eßer BcDig pow_num[BC_RAND_NUM_SIZE]; 2276252884aeSStefan Eßer BcDig temp1_num[BC_RAND_NUM_SIZE], temp2_num[BC_RAND_NUM_SIZE]; 2277252884aeSStefan Eßer BcDig conv_num[BC_NUM_BIGDIG_LOG10]; 2278252884aeSStefan Eßer 2279252884aeSStefan Eßer BC_SIG_LOCK; 2280252884aeSStefan Eßer 2281252884aeSStefan Eßer bc_num_init(&temp3, 2 * BC_RAND_NUM_SIZE); 2282252884aeSStefan Eßer 2283252884aeSStefan Eßer BC_SETJMP_LOCKED(err); 2284252884aeSStefan Eßer 2285252884aeSStefan Eßer BC_SIG_UNLOCK; 2286252884aeSStefan Eßer 2287252884aeSStefan Eßer bc_num_setup(&pow, pow_num, sizeof(pow_num) / sizeof(BcDig)); 2288252884aeSStefan Eßer bc_num_setup(&temp1, temp1_num, sizeof(temp1_num) / sizeof(BcDig)); 2289252884aeSStefan Eßer bc_num_setup(&temp2, temp2_num, sizeof(temp2_num) / sizeof(BcDig)); 2290252884aeSStefan Eßer bc_num_setup(&conv, conv_num, sizeof(conv_num) / sizeof(BcDig)); 2291252884aeSStefan Eßer 2292252884aeSStefan Eßer // This assert is here because it has to be true. It is also here to justify 2293252884aeSStefan Eßer // the assumption that pow is not zero. 2294252884aeSStefan Eßer assert(BC_NUM_NONZERO(&vm.max)); 2295252884aeSStefan Eßer 2296252884aeSStefan Eßer bc_num_mul(&vm.max, &vm.max, &pow, 0); 2297252884aeSStefan Eßer 2298252884aeSStefan Eßer // Because this is true, we can just use BC_ERROR_SIGNAL_ONLY() below when 2299252884aeSStefan Eßer // dividing by pow. 2300252884aeSStefan Eßer assert(BC_NUM_NONZERO(&pow)); 2301252884aeSStefan Eßer 2302252884aeSStefan Eßer bc_rand_getRands(rng, &s1, &s2, &i1, &i2); 2303252884aeSStefan Eßer 2304252884aeSStefan Eßer bc_num_bigdig2num(&conv, (BcBigDig) s2); 2305252884aeSStefan Eßer 2306252884aeSStefan Eßer bc_num_mul(&conv, &vm.max, &temp1, 0); 2307252884aeSStefan Eßer 2308252884aeSStefan Eßer bc_num_bigdig2num(&conv, (BcBigDig) s1); 2309252884aeSStefan Eßer 2310252884aeSStefan Eßer bc_num_add(&conv, &temp1, &temp2, 0); 2311252884aeSStefan Eßer 2312252884aeSStefan Eßer bc_num_div(&temp2, &pow, &temp3, BC_RAND_STATE_BITS); 2313252884aeSStefan Eßer 2314252884aeSStefan Eßer bc_num_bigdig2num(&conv, (BcBigDig) i2); 2315252884aeSStefan Eßer 2316252884aeSStefan Eßer bc_num_mul(&conv, &vm.max, &temp1, 0); 2317252884aeSStefan Eßer 2318252884aeSStefan Eßer bc_num_bigdig2num(&conv, (BcBigDig) i1); 2319252884aeSStefan Eßer 2320252884aeSStefan Eßer bc_num_add(&conv, &temp1, &temp2, 0); 2321252884aeSStefan Eßer 2322252884aeSStefan Eßer bc_num_add(&temp2, &temp3, n, 0); 2323252884aeSStefan Eßer 2324252884aeSStefan Eßer err: 2325252884aeSStefan Eßer BC_SIG_MAYLOCK; 2326252884aeSStefan Eßer bc_num_free(&temp3); 2327252884aeSStefan Eßer BC_LONGJMP_CONT; 2328252884aeSStefan Eßer } 2329252884aeSStefan Eßer 2330252884aeSStefan Eßer void bc_num_irand(const BcNum *restrict a, BcNum *restrict b, 2331252884aeSStefan Eßer BcRNG *restrict rng) 2332252884aeSStefan Eßer { 2333252884aeSStefan Eßer BcRand r; 2334252884aeSStefan Eßer BcBigDig modl; 2335252884aeSStefan Eßer BcNum pow, pow2, cp, cp2, mod, temp1, temp2, rand; 2336252884aeSStefan Eßer BcNum *p1, *p2, *t1, *t2, *c1, *c2, *tmp; 2337252884aeSStefan Eßer BcDig rand_num[BC_NUM_BIGDIG_LOG10]; 2338252884aeSStefan Eßer bool carry; 2339252884aeSStefan Eßer ssize_t cmp; 2340252884aeSStefan Eßer 2341252884aeSStefan Eßer assert(a != b); 2342252884aeSStefan Eßer 2343252884aeSStefan Eßer if (BC_ERR(a->neg)) bc_vm_err(BC_ERROR_MATH_NEGATIVE); 2344252884aeSStefan Eßer if (BC_ERR(a->rdx)) bc_vm_err(BC_ERROR_MATH_NON_INTEGER); 2345252884aeSStefan Eßer if (BC_NUM_ZERO(a) || BC_NUM_ONE(a)) return; 2346252884aeSStefan Eßer 2347252884aeSStefan Eßer cmp = bc_num_cmp(a, &vm.max); 2348252884aeSStefan Eßer 2349252884aeSStefan Eßer if (cmp <= 0) { 2350252884aeSStefan Eßer 2351252884aeSStefan Eßer BcRand bits = 0; 2352252884aeSStefan Eßer 2353252884aeSStefan Eßer if (cmp < 0) bc_num_bigdig2(a, (BcBigDig*) &bits); 2354252884aeSStefan Eßer 2355252884aeSStefan Eßer // This condition means that bits is a power of 2. In that case, we 2356252884aeSStefan Eßer // can just grab a full-size int and mask out the unneeded bits. 2357252884aeSStefan Eßer // Also, this condition says that 0 is a power of 2, which works for 2358252884aeSStefan Eßer // us, since a value of 0 means a == rng->max. The bitmask will mask 2359252884aeSStefan Eßer // nothing in that case as well. 2360252884aeSStefan Eßer if (!(bits & (bits - 1))) r = bc_rand_int(rng) & (bits - 1); 2361252884aeSStefan Eßer else r = bc_rand_bounded(rng, bits); 2362252884aeSStefan Eßer 2363252884aeSStefan Eßer // We made sure that r is less than vm.max, 2364252884aeSStefan Eßer // so we can use bc_num_bigdig2() here. 2365252884aeSStefan Eßer bc_num_bigdig2num(b, r); 2366252884aeSStefan Eßer 2367252884aeSStefan Eßer return; 2368252884aeSStefan Eßer } 2369252884aeSStefan Eßer 2370252884aeSStefan Eßer // In the case where a is less than rng->max, we have to make sure we have 2371252884aeSStefan Eßer // an exclusive bound. This ensures that it happens. (See below.) 2372252884aeSStefan Eßer carry = (cmp < 0); 2373252884aeSStefan Eßer 2374252884aeSStefan Eßer BC_SIG_LOCK; 2375252884aeSStefan Eßer 2376252884aeSStefan Eßer bc_num_createCopy(&cp, a); 2377252884aeSStefan Eßer 2378252884aeSStefan Eßer bc_num_init(&cp2, cp.len); 2379252884aeSStefan Eßer bc_num_init(&mod, BC_NUM_BIGDIG_LOG10); 2380252884aeSStefan Eßer bc_num_init(&temp1, BC_NUM_DEF_SIZE); 2381252884aeSStefan Eßer bc_num_init(&temp2, BC_NUM_DEF_SIZE); 2382252884aeSStefan Eßer bc_num_init(&pow2, BC_NUM_DEF_SIZE); 2383252884aeSStefan Eßer bc_num_init(&pow, BC_NUM_DEF_SIZE); 2384252884aeSStefan Eßer bc_num_one(&pow); 2385252884aeSStefan Eßer bc_num_setup(&rand, rand_num, sizeof(rand_num) / sizeof(BcDig)); 2386252884aeSStefan Eßer 2387252884aeSStefan Eßer BC_SETJMP_LOCKED(err); 2388252884aeSStefan Eßer 2389252884aeSStefan Eßer BC_SIG_UNLOCK; 2390252884aeSStefan Eßer 2391252884aeSStefan Eßer p1 = &pow; 2392252884aeSStefan Eßer p2 = &pow2; 2393252884aeSStefan Eßer t1 = &temp1; 2394252884aeSStefan Eßer t2 = &temp2; 2395252884aeSStefan Eßer c1 = &cp; 2396252884aeSStefan Eßer c2 = &cp2; 2397252884aeSStefan Eßer 2398252884aeSStefan Eßer // This assert is here because it has to be true. It is also here to justify 2399252884aeSStefan Eßer // the use of BC_ERROR_SIGNAL_ONLY() on each of the divmod's and mod's 2400252884aeSStefan Eßer // below. 2401252884aeSStefan Eßer assert(BC_NUM_NONZERO(&vm.max)); 2402252884aeSStefan Eßer 2403252884aeSStefan Eßer while (BC_NUM_NONZERO(c1)) { 2404252884aeSStefan Eßer 2405252884aeSStefan Eßer bc_num_divmod(c1, &vm.max, c2, &mod, 0); 2406252884aeSStefan Eßer 2407252884aeSStefan Eßer // Because mod is the mod of vm.max, it is guaranteed to be smaller, 2408252884aeSStefan Eßer // which means we can use bc_num_bigdig2() here. 2409252884aeSStefan Eßer bc_num_bigdig(&mod, &modl); 2410252884aeSStefan Eßer 2411252884aeSStefan Eßer if (bc_num_cmp(c1, &vm.max) < 0) { 2412252884aeSStefan Eßer 2413252884aeSStefan Eßer // In this case, if there is no carry, then we know we can generate 2414252884aeSStefan Eßer // an integer *equal* to modl. Thus, we add one if there is no 2415252884aeSStefan Eßer // carry. Otherwise, we add zero, and we are still bounded properly. 2416252884aeSStefan Eßer // Since the last portion is guaranteed to be greater than 1, we 2417252884aeSStefan Eßer // know modl isn't 0 unless there is no carry. 2418252884aeSStefan Eßer modl += !carry; 2419252884aeSStefan Eßer 2420252884aeSStefan Eßer if (modl == 1) r = 0; 2421252884aeSStefan Eßer else if (!modl) r = bc_rand_int(rng); 2422252884aeSStefan Eßer else r = bc_rand_bounded(rng, (BcRand) modl); 2423252884aeSStefan Eßer } 2424252884aeSStefan Eßer else { 2425252884aeSStefan Eßer if (modl) modl -= carry; 2426252884aeSStefan Eßer r = bc_rand_int(rng); 2427252884aeSStefan Eßer carry = (r >= (BcRand) modl); 2428252884aeSStefan Eßer } 2429252884aeSStefan Eßer 2430252884aeSStefan Eßer bc_num_bigdig2num(&rand, r); 2431252884aeSStefan Eßer 2432252884aeSStefan Eßer bc_num_mul(&rand, p1, p2, 0); 2433252884aeSStefan Eßer bc_num_add(p2, t1, t2, 0); 2434252884aeSStefan Eßer 2435252884aeSStefan Eßer if (BC_NUM_NONZERO(c2)) { 2436252884aeSStefan Eßer 2437252884aeSStefan Eßer bc_num_mul(&vm.max, p1, p2, 0); 2438252884aeSStefan Eßer 2439252884aeSStefan Eßer tmp = p1; 2440252884aeSStefan Eßer p1 = p2; 2441252884aeSStefan Eßer p2 = tmp; 2442252884aeSStefan Eßer 2443252884aeSStefan Eßer tmp = c1; 2444252884aeSStefan Eßer c1 = c2; 2445252884aeSStefan Eßer c2 = tmp; 2446252884aeSStefan Eßer } 2447252884aeSStefan Eßer else c1 = c2; 2448252884aeSStefan Eßer 2449252884aeSStefan Eßer tmp = t1; 2450252884aeSStefan Eßer t1 = t2; 2451252884aeSStefan Eßer t2 = tmp; 2452252884aeSStefan Eßer } 2453252884aeSStefan Eßer 2454252884aeSStefan Eßer bc_num_copy(b, t1); 2455252884aeSStefan Eßer bc_num_clean(b); 2456252884aeSStefan Eßer 2457252884aeSStefan Eßer err: 2458252884aeSStefan Eßer BC_SIG_MAYLOCK; 2459252884aeSStefan Eßer bc_num_free(&pow); 2460252884aeSStefan Eßer bc_num_free(&pow2); 2461252884aeSStefan Eßer bc_num_free(&temp2); 2462252884aeSStefan Eßer bc_num_free(&temp1); 2463252884aeSStefan Eßer bc_num_free(&mod); 2464252884aeSStefan Eßer bc_num_free(&cp2); 2465252884aeSStefan Eßer bc_num_free(&cp); 2466252884aeSStefan Eßer BC_LONGJMP_CONT; 2467252884aeSStefan Eßer } 24683aa99676SStefan Eßer #endif // BC_ENABLE_EXTRA_MATH && BC_ENABLE_RAND 2469252884aeSStefan Eßer 2470252884aeSStefan Eßer size_t bc_num_addReq(const BcNum *a, const BcNum *b, size_t scale) { 2471252884aeSStefan Eßer 2472252884aeSStefan Eßer size_t aint, bint, ardx, brdx; 2473252884aeSStefan Eßer 2474252884aeSStefan Eßer BC_UNUSED(scale); 2475252884aeSStefan Eßer 2476252884aeSStefan Eßer ardx = a->rdx; 2477252884aeSStefan Eßer aint = bc_num_int(a); 2478252884aeSStefan Eßer assert(aint <= a->len && ardx <= a->len); 2479252884aeSStefan Eßer 2480252884aeSStefan Eßer brdx = b->rdx; 2481252884aeSStefan Eßer bint = bc_num_int(b); 2482252884aeSStefan Eßer assert(bint <= b->len && brdx <= b->len); 2483252884aeSStefan Eßer 2484252884aeSStefan Eßer ardx = BC_MAX(ardx, brdx); 2485252884aeSStefan Eßer aint = BC_MAX(aint, bint); 2486252884aeSStefan Eßer 2487252884aeSStefan Eßer return bc_vm_growSize(bc_vm_growSize(ardx, aint), 1); 2488252884aeSStefan Eßer } 2489252884aeSStefan Eßer 2490252884aeSStefan Eßer size_t bc_num_mulReq(const BcNum *a, const BcNum *b, size_t scale) { 2491252884aeSStefan Eßer size_t max, rdx; 2492252884aeSStefan Eßer rdx = bc_vm_growSize(a->rdx, b->rdx); 2493252884aeSStefan Eßer max = BC_NUM_RDX(scale); 2494252884aeSStefan Eßer max = bc_vm_growSize(BC_MAX(max, rdx), 1); 2495252884aeSStefan Eßer rdx = bc_vm_growSize(bc_vm_growSize(bc_num_int(a), bc_num_int(b)), max); 2496252884aeSStefan Eßer return rdx; 2497252884aeSStefan Eßer } 2498252884aeSStefan Eßer 2499252884aeSStefan Eßer size_t bc_num_powReq(const BcNum *a, const BcNum *b, size_t scale) { 2500252884aeSStefan Eßer BC_UNUSED(scale); 2501252884aeSStefan Eßer return bc_vm_growSize(bc_vm_growSize(a->len, b->len), 1); 2502252884aeSStefan Eßer } 2503252884aeSStefan Eßer 2504252884aeSStefan Eßer #if BC_ENABLE_EXTRA_MATH 2505252884aeSStefan Eßer size_t bc_num_placesReq(const BcNum *a, const BcNum *b, size_t scale) { 2506252884aeSStefan Eßer BC_UNUSED(scale); 2507252884aeSStefan Eßer return a->len + b->len - a->rdx - b->rdx; 2508252884aeSStefan Eßer } 2509252884aeSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH 2510252884aeSStefan Eßer 2511252884aeSStefan Eßer void bc_num_add(BcNum *a, BcNum *b, BcNum *c, size_t scale) { 2512252884aeSStefan Eßer bc_num_binary(a, b, c, false, bc_num_as, bc_num_addReq(a, b, scale)); 2513252884aeSStefan Eßer } 2514252884aeSStefan Eßer 2515252884aeSStefan Eßer void bc_num_sub(BcNum *a, BcNum *b, BcNum *c, size_t scale) { 2516252884aeSStefan Eßer bc_num_binary(a, b, c, true, bc_num_as, bc_num_addReq(a, b, scale)); 2517252884aeSStefan Eßer } 2518252884aeSStefan Eßer 2519252884aeSStefan Eßer void bc_num_mul(BcNum *a, BcNum *b, BcNum *c, size_t scale) { 2520252884aeSStefan Eßer bc_num_binary(a, b, c, scale, bc_num_m, bc_num_mulReq(a, b, scale)); 2521252884aeSStefan Eßer } 2522252884aeSStefan Eßer 2523252884aeSStefan Eßer void bc_num_div(BcNum *a, BcNum *b, BcNum *c, size_t scale) { 2524252884aeSStefan Eßer bc_num_binary(a, b, c, scale, bc_num_d, bc_num_mulReq(a, b, scale)); 2525252884aeSStefan Eßer } 2526252884aeSStefan Eßer 2527252884aeSStefan Eßer void bc_num_mod(BcNum *a, BcNum *b, BcNum *c, size_t scale) { 2528252884aeSStefan Eßer bc_num_binary(a, b, c, scale, bc_num_rem, bc_num_mulReq(a, b, scale)); 2529252884aeSStefan Eßer } 2530252884aeSStefan Eßer 2531252884aeSStefan Eßer void bc_num_pow(BcNum *a, BcNum *b, BcNum *c, size_t scale) { 2532252884aeSStefan Eßer bc_num_binary(a, b, c, scale, bc_num_p, bc_num_powReq(a, b, scale)); 2533252884aeSStefan Eßer } 2534252884aeSStefan Eßer 2535252884aeSStefan Eßer #if BC_ENABLE_EXTRA_MATH 2536252884aeSStefan Eßer void bc_num_places(BcNum *a, BcNum *b, BcNum *c, size_t scale) { 2537252884aeSStefan Eßer bc_num_binary(a, b, c, scale, bc_num_place, bc_num_placesReq(a, b, scale)); 2538252884aeSStefan Eßer } 2539252884aeSStefan Eßer 2540252884aeSStefan Eßer void bc_num_lshift(BcNum *a, BcNum *b, BcNum *c, size_t scale) { 2541252884aeSStefan Eßer bc_num_binary(a, b, c, scale, bc_num_left, bc_num_placesReq(a, b, scale)); 2542252884aeSStefan Eßer } 2543252884aeSStefan Eßer 2544252884aeSStefan Eßer void bc_num_rshift(BcNum *a, BcNum *b, BcNum *c, size_t scale) { 2545252884aeSStefan Eßer bc_num_binary(a, b, c, scale, bc_num_right, bc_num_placesReq(a, b, scale)); 2546252884aeSStefan Eßer } 2547252884aeSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH 2548252884aeSStefan Eßer 2549252884aeSStefan Eßer void bc_num_sqrt(BcNum *restrict a, BcNum *restrict b, size_t scale) { 2550252884aeSStefan Eßer 2551252884aeSStefan Eßer BcNum num1, num2, half, f, fprime, *x0, *x1, *temp; 2552252884aeSStefan Eßer size_t pow, len, rdx, req, digs, digs1, digs2, resscale; 2553252884aeSStefan Eßer BcDig half_digs[1]; 2554252884aeSStefan Eßer 2555252884aeSStefan Eßer assert(a != NULL && b != NULL && a != b); 2556252884aeSStefan Eßer 2557252884aeSStefan Eßer if (BC_ERR(a->neg)) bc_vm_err(BC_ERROR_MATH_NEGATIVE); 2558252884aeSStefan Eßer 2559252884aeSStefan Eßer if (a->scale > scale) scale = a->scale; 2560252884aeSStefan Eßer 2561252884aeSStefan Eßer len = bc_vm_growSize(bc_num_intDigits(a), 1); 2562252884aeSStefan Eßer rdx = BC_NUM_RDX(scale); 2563252884aeSStefan Eßer req = bc_vm_growSize(BC_MAX(rdx, a->rdx), len >> 1); 2564252884aeSStefan Eßer 2565252884aeSStefan Eßer BC_SIG_LOCK; 2566252884aeSStefan Eßer 2567252884aeSStefan Eßer bc_num_init(b, bc_vm_growSize(req, 1)); 2568252884aeSStefan Eßer 2569252884aeSStefan Eßer BC_SIG_UNLOCK; 2570252884aeSStefan Eßer 2571252884aeSStefan Eßer if (BC_NUM_ZERO(a)) { 2572252884aeSStefan Eßer bc_num_setToZero(b, scale); 2573252884aeSStefan Eßer return; 2574252884aeSStefan Eßer } 2575252884aeSStefan Eßer if (BC_NUM_ONE(a)) { 2576252884aeSStefan Eßer bc_num_one(b); 2577252884aeSStefan Eßer bc_num_extend(b, scale); 2578252884aeSStefan Eßer return; 2579252884aeSStefan Eßer } 2580252884aeSStefan Eßer 2581252884aeSStefan Eßer rdx = BC_NUM_RDX(scale); 2582252884aeSStefan Eßer rdx = BC_MAX(rdx, a->rdx); 2583252884aeSStefan Eßer len = bc_vm_growSize(a->len, rdx); 2584252884aeSStefan Eßer 2585252884aeSStefan Eßer BC_SIG_LOCK; 2586252884aeSStefan Eßer 2587252884aeSStefan Eßer bc_num_init(&num1, len); 2588252884aeSStefan Eßer bc_num_init(&num2, len); 2589252884aeSStefan Eßer bc_num_setup(&half, half_digs, sizeof(half_digs) / sizeof(BcDig)); 2590252884aeSStefan Eßer 2591252884aeSStefan Eßer bc_num_one(&half); 2592252884aeSStefan Eßer half.num[0] = BC_BASE_POW / 2; 2593252884aeSStefan Eßer half.len = 1; 2594252884aeSStefan Eßer half.rdx = 1; 2595252884aeSStefan Eßer half.scale = 1; 2596252884aeSStefan Eßer 2597252884aeSStefan Eßer bc_num_init(&f, len); 2598252884aeSStefan Eßer bc_num_init(&fprime, len); 2599252884aeSStefan Eßer 2600252884aeSStefan Eßer BC_SETJMP_LOCKED(err); 2601252884aeSStefan Eßer 2602252884aeSStefan Eßer BC_SIG_UNLOCK; 2603252884aeSStefan Eßer 2604252884aeSStefan Eßer x0 = &num1; 2605252884aeSStefan Eßer x1 = &num2; 2606252884aeSStefan Eßer 2607252884aeSStefan Eßer bc_num_one(x0); 2608252884aeSStefan Eßer pow = bc_num_intDigits(a); 2609252884aeSStefan Eßer 2610252884aeSStefan Eßer if (pow) { 2611252884aeSStefan Eßer 2612252884aeSStefan Eßer if (pow & 1) x0->num[0] = 2; 2613252884aeSStefan Eßer else x0->num[0] = 6; 2614252884aeSStefan Eßer 2615252884aeSStefan Eßer pow -= 2 - (pow & 1); 2616252884aeSStefan Eßer bc_num_shiftLeft(x0, pow / 2); 2617252884aeSStefan Eßer } 2618252884aeSStefan Eßer 2619252884aeSStefan Eßer x0->scale = x0->rdx = digs = digs1 = digs2 = 0; 2620252884aeSStefan Eßer resscale = (scale + BC_BASE_DIGS) + 2; 2621252884aeSStefan Eßer 2622252884aeSStefan Eßer while (bc_num_cmp(x1, x0)) { 2623252884aeSStefan Eßer 2624252884aeSStefan Eßer assert(BC_NUM_NONZERO(x0)); 2625252884aeSStefan Eßer 2626252884aeSStefan Eßer bc_num_div(a, x0, &f, resscale); 2627252884aeSStefan Eßer bc_num_add(x0, &f, &fprime, resscale); 2628252884aeSStefan Eßer bc_num_mul(&fprime, &half, x1, resscale); 2629252884aeSStefan Eßer 2630252884aeSStefan Eßer temp = x0; 2631252884aeSStefan Eßer x0 = x1; 2632252884aeSStefan Eßer x1 = temp; 2633252884aeSStefan Eßer } 2634252884aeSStefan Eßer 2635252884aeSStefan Eßer bc_num_copy(b, x0); 2636252884aeSStefan Eßer if (b->scale > scale) bc_num_truncate(b, b->scale - scale); 2637252884aeSStefan Eßer 2638252884aeSStefan Eßer assert(!b->neg || BC_NUM_NONZERO(b)); 2639252884aeSStefan Eßer assert(b->rdx <= b->len || !b->len); 2640252884aeSStefan Eßer assert(!b->len || b->num[b->len - 1] || b->rdx == b->len); 2641252884aeSStefan Eßer 2642252884aeSStefan Eßer err: 2643252884aeSStefan Eßer BC_SIG_MAYLOCK; 2644252884aeSStefan Eßer bc_num_free(&fprime); 2645252884aeSStefan Eßer bc_num_free(&f); 2646252884aeSStefan Eßer bc_num_free(&num2); 2647252884aeSStefan Eßer bc_num_free(&num1); 2648252884aeSStefan Eßer BC_LONGJMP_CONT; 2649252884aeSStefan Eßer } 2650252884aeSStefan Eßer 2651252884aeSStefan Eßer void bc_num_divmod(BcNum *a, BcNum *b, BcNum *c, BcNum *d, size_t scale) { 2652252884aeSStefan Eßer 2653252884aeSStefan Eßer BcNum num2, *ptr_a; 2654252884aeSStefan Eßer bool init = false; 2655252884aeSStefan Eßer size_t ts, len; 2656252884aeSStefan Eßer 2657252884aeSStefan Eßer ts = BC_MAX(scale + b->scale, a->scale); 2658252884aeSStefan Eßer len = bc_num_mulReq(a, b, ts); 2659252884aeSStefan Eßer 2660252884aeSStefan Eßer assert(a != NULL && b != NULL && c != NULL && d != NULL); 2661252884aeSStefan Eßer assert(c != d && a != d && b != d && b != c); 2662252884aeSStefan Eßer 2663252884aeSStefan Eßer if (c == a) { 2664252884aeSStefan Eßer 2665252884aeSStefan Eßer memcpy(&num2, c, sizeof(BcNum)); 2666252884aeSStefan Eßer ptr_a = &num2; 2667252884aeSStefan Eßer 2668252884aeSStefan Eßer BC_SIG_LOCK; 2669252884aeSStefan Eßer 2670252884aeSStefan Eßer bc_num_init(c, len); 2671252884aeSStefan Eßer 2672252884aeSStefan Eßer init = true; 2673252884aeSStefan Eßer 2674252884aeSStefan Eßer BC_SETJMP_LOCKED(err); 2675252884aeSStefan Eßer 2676252884aeSStefan Eßer BC_SIG_UNLOCK; 2677252884aeSStefan Eßer } 2678252884aeSStefan Eßer else { 2679252884aeSStefan Eßer ptr_a = a; 2680252884aeSStefan Eßer bc_num_expand(c, len); 2681252884aeSStefan Eßer } 2682252884aeSStefan Eßer 2683252884aeSStefan Eßer if (BC_NUM_NONZERO(a) && !a->rdx && !b->rdx && b->len == 1 && !scale) { 2684252884aeSStefan Eßer 2685252884aeSStefan Eßer BcBigDig rem; 2686252884aeSStefan Eßer 2687252884aeSStefan Eßer bc_num_divArray(ptr_a, (BcBigDig) b->num[0], c, &rem); 2688252884aeSStefan Eßer 2689252884aeSStefan Eßer assert(rem < BC_BASE_POW); 2690252884aeSStefan Eßer 2691252884aeSStefan Eßer d->num[0] = (BcDig) rem; 2692252884aeSStefan Eßer d->len = (rem != 0); 2693252884aeSStefan Eßer } 2694252884aeSStefan Eßer else bc_num_r(ptr_a, b, c, d, scale, ts); 2695252884aeSStefan Eßer 2696252884aeSStefan Eßer assert(!c->neg || BC_NUM_NONZERO(c)); 2697252884aeSStefan Eßer assert(c->rdx <= c->len || !c->len); 2698252884aeSStefan Eßer assert(!c->len || c->num[c->len - 1] || c->rdx == c->len); 2699252884aeSStefan Eßer assert(!d->neg || BC_NUM_NONZERO(d)); 2700252884aeSStefan Eßer assert(d->rdx <= d->len || !d->len); 2701252884aeSStefan Eßer assert(!d->len || d->num[d->len - 1] || d->rdx == d->len); 2702252884aeSStefan Eßer 2703252884aeSStefan Eßer err: 2704252884aeSStefan Eßer if (init) { 2705252884aeSStefan Eßer BC_SIG_MAYLOCK; 2706252884aeSStefan Eßer bc_num_free(&num2); 2707252884aeSStefan Eßer BC_LONGJMP_CONT; 2708252884aeSStefan Eßer } 2709252884aeSStefan Eßer } 2710252884aeSStefan Eßer 2711252884aeSStefan Eßer #if DC_ENABLED 2712252884aeSStefan Eßer void bc_num_modexp(BcNum *a, BcNum *b, BcNum *c, BcNum *restrict d) { 2713252884aeSStefan Eßer 2714252884aeSStefan Eßer BcNum base, exp, two, temp; 2715252884aeSStefan Eßer BcDig two_digs[2]; 2716252884aeSStefan Eßer 2717252884aeSStefan Eßer assert(a != NULL && b != NULL && c != NULL && d != NULL); 2718252884aeSStefan Eßer assert(a != d && b != d && c != d); 2719252884aeSStefan Eßer 2720252884aeSStefan Eßer if (BC_ERR(BC_NUM_ZERO(c))) bc_vm_err(BC_ERROR_MATH_DIVIDE_BY_ZERO); 2721252884aeSStefan Eßer if (BC_ERR(b->neg)) bc_vm_err(BC_ERROR_MATH_NEGATIVE); 2722252884aeSStefan Eßer if (BC_ERR(a->rdx || b->rdx || c->rdx)) 2723252884aeSStefan Eßer bc_vm_err(BC_ERROR_MATH_NON_INTEGER); 2724252884aeSStefan Eßer 2725252884aeSStefan Eßer bc_num_expand(d, c->len); 2726252884aeSStefan Eßer 2727252884aeSStefan Eßer BC_SIG_LOCK; 2728252884aeSStefan Eßer 2729252884aeSStefan Eßer bc_num_init(&base, c->len); 2730252884aeSStefan Eßer bc_num_setup(&two, two_digs, sizeof(two_digs) / sizeof(BcDig)); 2731252884aeSStefan Eßer bc_num_init(&temp, b->len + 1); 2732252884aeSStefan Eßer bc_num_createCopy(&exp, b); 2733252884aeSStefan Eßer 2734252884aeSStefan Eßer BC_SETJMP_LOCKED(err); 2735252884aeSStefan Eßer 2736252884aeSStefan Eßer BC_SIG_UNLOCK; 2737252884aeSStefan Eßer 2738252884aeSStefan Eßer bc_num_one(&two); 2739252884aeSStefan Eßer two.num[0] = 2; 2740252884aeSStefan Eßer bc_num_one(d); 2741252884aeSStefan Eßer 2742252884aeSStefan Eßer // We already checked for 0. 2743252884aeSStefan Eßer bc_num_rem(a, c, &base, 0); 2744252884aeSStefan Eßer 2745252884aeSStefan Eßer while (BC_NUM_NONZERO(&exp)) { 2746252884aeSStefan Eßer 2747252884aeSStefan Eßer // Num two cannot be 0, so no errors. 2748252884aeSStefan Eßer bc_num_divmod(&exp, &two, &exp, &temp, 0); 2749252884aeSStefan Eßer 2750252884aeSStefan Eßer if (BC_NUM_ONE(&temp) && !temp.neg) { 2751252884aeSStefan Eßer 2752252884aeSStefan Eßer bc_num_mul(d, &base, &temp, 0); 2753252884aeSStefan Eßer 2754252884aeSStefan Eßer // We already checked for 0. 2755252884aeSStefan Eßer bc_num_rem(&temp, c, d, 0); 2756252884aeSStefan Eßer } 2757252884aeSStefan Eßer 2758252884aeSStefan Eßer bc_num_mul(&base, &base, &temp, 0); 2759252884aeSStefan Eßer 2760252884aeSStefan Eßer // We already checked for 0. 2761252884aeSStefan Eßer bc_num_rem(&temp, c, &base, 0); 2762252884aeSStefan Eßer } 2763252884aeSStefan Eßer 2764252884aeSStefan Eßer err: 2765252884aeSStefan Eßer BC_SIG_MAYLOCK; 2766252884aeSStefan Eßer bc_num_free(&exp); 2767252884aeSStefan Eßer bc_num_free(&temp); 2768252884aeSStefan Eßer bc_num_free(&base); 2769252884aeSStefan Eßer BC_LONGJMP_CONT; 2770252884aeSStefan Eßer assert(!d->neg || d->len); 2771252884aeSStefan Eßer assert(!d->len || d->num[d->len - 1] || d->rdx == d->len); 2772252884aeSStefan Eßer } 2773252884aeSStefan Eßer #endif // DC_ENABLED 2774252884aeSStefan Eßer 2775252884aeSStefan Eßer #if BC_DEBUG_CODE 2776252884aeSStefan Eßer void bc_num_printDebug(const BcNum *n, const char *name, bool emptyline) { 2777252884aeSStefan Eßer bc_file_puts(&vm.fout, name); 2778252884aeSStefan Eßer bc_file_puts(&vm.fout, ": "); 2779252884aeSStefan Eßer bc_num_printDecimal(n); 2780252884aeSStefan Eßer bc_file_putchar(&vm.fout, '\n'); 2781252884aeSStefan Eßer if (emptyline) bc_file_putchar(&vm.fout, '\n'); 2782252884aeSStefan Eßer vm.nchars = 0; 2783252884aeSStefan Eßer } 2784252884aeSStefan Eßer 2785252884aeSStefan Eßer void bc_num_printDigs(const BcDig *n, size_t len, bool emptyline) { 2786252884aeSStefan Eßer 2787252884aeSStefan Eßer size_t i; 2788252884aeSStefan Eßer 2789252884aeSStefan Eßer for (i = len - 1; i < len; --i) 2790252884aeSStefan Eßer bc_file_printf(&vm.fout, " %lu", (unsigned long) n[i]); 2791252884aeSStefan Eßer 2792252884aeSStefan Eßer bc_file_putchar(&vm.fout, '\n'); 2793252884aeSStefan Eßer if (emptyline) bc_file_putchar(&vm.fout, '\n'); 2794252884aeSStefan Eßer vm.nchars = 0; 2795252884aeSStefan Eßer } 2796252884aeSStefan Eßer 2797252884aeSStefan Eßer void bc_num_printWithDigs(const BcNum *n, const char *name, bool emptyline) { 2798252884aeSStefan Eßer bc_file_puts(&vm.fout, name); 2799252884aeSStefan Eßer bc_file_printf(&vm.fout, " len: %zu, rdx: %zu, scale: %zu\n", 2800252884aeSStefan Eßer name, n->len, n->rdx, n->scale); 2801252884aeSStefan Eßer bc_num_printDigs(n->num, n->len, emptyline); 2802252884aeSStefan Eßer } 2803252884aeSStefan Eßer 2804252884aeSStefan Eßer void bc_num_dump(const char *varname, const BcNum *n) { 2805252884aeSStefan Eßer 2806252884aeSStefan Eßer ulong i, scale = n->scale; 2807252884aeSStefan Eßer 2808252884aeSStefan Eßer bc_file_printf(&vm.ferr, "\n%s = %s", varname, 2809252884aeSStefan Eßer n->len ? (n->neg ? "-" : "+") : "0 "); 2810252884aeSStefan Eßer 2811252884aeSStefan Eßer for (i = n->len - 1; i < n->len; --i) { 2812252884aeSStefan Eßer 2813252884aeSStefan Eßer if (i + 1 == n->rdx) bc_file_puts(&vm.ferr, ". "); 2814252884aeSStefan Eßer 2815252884aeSStefan Eßer if (scale / BC_BASE_DIGS != n->rdx - i - 1) 2816252884aeSStefan Eßer bc_file_printf(&vm.ferr, "%lu ", (unsigned long) n->num[i]); 2817252884aeSStefan Eßer else { 2818252884aeSStefan Eßer 2819252884aeSStefan Eßer int mod = scale % BC_BASE_DIGS; 2820252884aeSStefan Eßer int d = BC_BASE_DIGS - mod; 2821252884aeSStefan Eßer BcDig div; 2822252884aeSStefan Eßer 2823252884aeSStefan Eßer if (mod != 0) { 2824252884aeSStefan Eßer div = n->num[i] / ((BcDig) bc_num_pow10[(ulong) d]); 2825252884aeSStefan Eßer bc_file_printf(&vm.ferr, "%lu", (unsigned long) div); 2826252884aeSStefan Eßer } 2827252884aeSStefan Eßer 2828252884aeSStefan Eßer div = n->num[i] % ((BcDig) bc_num_pow10[(ulong) d]); 2829252884aeSStefan Eßer bc_file_printf(&vm.ferr, " ' %lu ", (unsigned long) div); 2830252884aeSStefan Eßer } 2831252884aeSStefan Eßer } 2832252884aeSStefan Eßer 2833252884aeSStefan Eßer bc_file_printf(&vm.ferr, "(%zu | %zu.%zu / %zu) %lu\n", 2834252884aeSStefan Eßer n->scale, n->len, n->rdx, n->cap, 2835252884aeSStefan Eßer (unsigned long) (void*) n->num); 2836252884aeSStefan Eßer } 2837252884aeSStefan Eßer #endif // BC_DEBUG_CODE 2838