1252884aeSStefan Eßer /* 2252884aeSStefan Eßer * ***************************************************************************** 3252884aeSStefan Eßer * 43aa99676SStefan Eßer * SPDX-License-Identifier: BSD-2-Clause 5252884aeSStefan Eßer * 610328f8bSStefan Eßer * Copyright (c) 2018-2021 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 <num.h> 45252884aeSStefan Eßer #include <rand.h> 46252884aeSStefan Eßer #include <vm.h> 47252884aeSStefan Eßer 48252884aeSStefan Eßer static void bc_num_m(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale); 49252884aeSStefan Eßer 50252884aeSStefan Eßer static inline ssize_t bc_num_neg(size_t n, bool neg) { 51252884aeSStefan Eßer return (((ssize_t) n) ^ -((ssize_t) neg)) + neg; 52252884aeSStefan Eßer } 53252884aeSStefan Eßer 54252884aeSStefan Eßer ssize_t bc_num_cmpZero(const BcNum *n) { 5550696a6eSStefan Eßer return bc_num_neg((n)->len != 0, BC_NUM_NEG(n)); 56252884aeSStefan Eßer } 57252884aeSStefan Eßer 58252884aeSStefan Eßer static inline size_t bc_num_int(const BcNum *n) { 5950696a6eSStefan Eßer return n->len ? n->len - BC_NUM_RDX_VAL(n) : 0; 60252884aeSStefan Eßer } 61252884aeSStefan Eßer 62252884aeSStefan Eßer static void bc_num_expand(BcNum *restrict n, size_t req) { 63252884aeSStefan Eßer 64252884aeSStefan Eßer assert(n != NULL); 65252884aeSStefan Eßer 66252884aeSStefan Eßer req = req >= BC_NUM_DEF_SIZE ? req : BC_NUM_DEF_SIZE; 67252884aeSStefan Eßer 68252884aeSStefan Eßer if (req > n->cap) { 69252884aeSStefan Eßer 70252884aeSStefan Eßer BC_SIG_LOCK; 71252884aeSStefan Eßer 72252884aeSStefan Eßer n->num = bc_vm_realloc(n->num, BC_NUM_SIZE(req)); 73252884aeSStefan Eßer n->cap = req; 74252884aeSStefan Eßer 75252884aeSStefan Eßer BC_SIG_UNLOCK; 76252884aeSStefan Eßer } 77252884aeSStefan Eßer } 78252884aeSStefan Eßer 79252884aeSStefan Eßer static void bc_num_setToZero(BcNum *restrict n, size_t scale) { 80252884aeSStefan Eßer assert(n != NULL); 81252884aeSStefan Eßer n->scale = scale; 82252884aeSStefan Eßer n->len = n->rdx = 0; 83252884aeSStefan Eßer } 84252884aeSStefan Eßer 8550696a6eSStefan Eßer void bc_num_zero(BcNum *restrict n) { 86252884aeSStefan Eßer bc_num_setToZero(n, 0); 87252884aeSStefan Eßer } 88252884aeSStefan Eßer 89252884aeSStefan Eßer void bc_num_one(BcNum *restrict n) { 90252884aeSStefan Eßer bc_num_zero(n); 91252884aeSStefan Eßer n->len = 1; 92252884aeSStefan Eßer n->num[0] = 1; 93252884aeSStefan Eßer } 94252884aeSStefan Eßer 95252884aeSStefan Eßer static void bc_num_clean(BcNum *restrict n) { 96252884aeSStefan Eßer 97252884aeSStefan Eßer while (BC_NUM_NONZERO(n) && !n->num[n->len - 1]) n->len -= 1; 98252884aeSStefan Eßer 9950696a6eSStefan Eßer if (BC_NUM_ZERO(n)) n->rdx = 0; 10050696a6eSStefan Eßer else { 10150696a6eSStefan Eßer size_t rdx = BC_NUM_RDX_VAL(n); 10250696a6eSStefan Eßer if (n->len < rdx) n->len = rdx; 103252884aeSStefan Eßer } 104252884aeSStefan Eßer } 105252884aeSStefan Eßer 106252884aeSStefan Eßer static size_t bc_num_log10(size_t i) { 107252884aeSStefan Eßer size_t len; 108252884aeSStefan Eßer for (len = 1; i; i /= BC_BASE, ++len); 109252884aeSStefan Eßer assert(len - 1 <= BC_BASE_DIGS + 1); 110252884aeSStefan Eßer return len - 1; 111252884aeSStefan Eßer } 112252884aeSStefan Eßer 113252884aeSStefan Eßer static inline size_t bc_num_zeroDigits(const BcDig *n) { 114252884aeSStefan Eßer assert(*n >= 0); 115252884aeSStefan Eßer assert(((size_t) *n) < BC_BASE_POW); 116252884aeSStefan Eßer return BC_BASE_DIGS - bc_num_log10((size_t) *n); 117252884aeSStefan Eßer } 118252884aeSStefan Eßer 119252884aeSStefan Eßer static size_t bc_num_intDigits(const BcNum *n) { 120252884aeSStefan Eßer size_t digits = bc_num_int(n) * BC_BASE_DIGS; 121252884aeSStefan Eßer if (digits > 0) digits -= bc_num_zeroDigits(n->num + n->len - 1); 122252884aeSStefan Eßer return digits; 123252884aeSStefan Eßer } 124252884aeSStefan Eßer 125252884aeSStefan Eßer static size_t bc_num_nonzeroLen(const BcNum *restrict n) { 126252884aeSStefan Eßer size_t i, len = n->len; 12750696a6eSStefan Eßer assert(len == BC_NUM_RDX_VAL(n)); 128252884aeSStefan Eßer for (i = len - 1; i < len && !n->num[i]; --i); 129252884aeSStefan Eßer assert(i + 1 > 0); 130252884aeSStefan Eßer return i + 1; 131252884aeSStefan Eßer } 132252884aeSStefan Eßer 133252884aeSStefan Eßer static BcDig bc_num_addDigits(BcDig a, BcDig b, bool *carry) { 134252884aeSStefan Eßer 135252884aeSStefan Eßer assert(((BcBigDig) BC_BASE_POW) * 2 == ((BcDig) BC_BASE_POW) * 2); 136252884aeSStefan Eßer assert(a < BC_BASE_POW); 137252884aeSStefan Eßer assert(b < BC_BASE_POW); 138252884aeSStefan Eßer 139252884aeSStefan Eßer a += b + *carry; 140252884aeSStefan Eßer *carry = (a >= BC_BASE_POW); 141252884aeSStefan Eßer if (*carry) a -= BC_BASE_POW; 142252884aeSStefan Eßer 143252884aeSStefan Eßer assert(a >= 0); 144252884aeSStefan Eßer assert(a < BC_BASE_POW); 145252884aeSStefan Eßer 146252884aeSStefan Eßer return a; 147252884aeSStefan Eßer } 148252884aeSStefan Eßer 149252884aeSStefan Eßer static BcDig bc_num_subDigits(BcDig a, BcDig b, bool *carry) { 150252884aeSStefan Eßer 151252884aeSStefan Eßer assert(a < BC_BASE_POW); 152252884aeSStefan Eßer assert(b < BC_BASE_POW); 153252884aeSStefan Eßer 154252884aeSStefan Eßer b += *carry; 155252884aeSStefan Eßer *carry = (a < b); 156252884aeSStefan Eßer if (*carry) a += BC_BASE_POW; 157252884aeSStefan Eßer 158252884aeSStefan Eßer assert(a - b >= 0); 159252884aeSStefan Eßer assert(a - b < BC_BASE_POW); 160252884aeSStefan Eßer 161252884aeSStefan Eßer return a - b; 162252884aeSStefan Eßer } 163252884aeSStefan Eßer 164252884aeSStefan Eßer static void bc_num_addArrays(BcDig *restrict a, const BcDig *restrict b, 165252884aeSStefan Eßer size_t len) 166252884aeSStefan Eßer { 167252884aeSStefan Eßer size_t i; 168252884aeSStefan Eßer bool carry = false; 169252884aeSStefan Eßer 170252884aeSStefan Eßer for (i = 0; i < len; ++i) a[i] = bc_num_addDigits(a[i], b[i], &carry); 171252884aeSStefan Eßer 172252884aeSStefan Eßer for (; carry; ++i) a[i] = bc_num_addDigits(a[i], 0, &carry); 173252884aeSStefan Eßer } 174252884aeSStefan Eßer 175252884aeSStefan Eßer static void bc_num_subArrays(BcDig *restrict a, const BcDig *restrict b, 176252884aeSStefan Eßer size_t len) 177252884aeSStefan Eßer { 178252884aeSStefan Eßer size_t i; 179252884aeSStefan Eßer bool carry = false; 180252884aeSStefan Eßer 181252884aeSStefan Eßer for (i = 0; i < len; ++i) a[i] = bc_num_subDigits(a[i], b[i], &carry); 182252884aeSStefan Eßer 183252884aeSStefan Eßer for (; carry; ++i) a[i] = bc_num_subDigits(a[i], 0, &carry); 184252884aeSStefan Eßer } 185252884aeSStefan Eßer 186252884aeSStefan Eßer static void bc_num_mulArray(const BcNum *restrict a, BcBigDig b, 187252884aeSStefan Eßer BcNum *restrict c) 188252884aeSStefan Eßer { 189252884aeSStefan Eßer size_t i; 190252884aeSStefan Eßer BcBigDig carry = 0; 191252884aeSStefan Eßer 192252884aeSStefan Eßer assert(b <= BC_BASE_POW); 193252884aeSStefan Eßer 194252884aeSStefan Eßer if (a->len + 1 > c->cap) bc_num_expand(c, a->len + 1); 195252884aeSStefan Eßer 196252884aeSStefan Eßer memset(c->num, 0, BC_NUM_SIZE(c->cap)); 197252884aeSStefan Eßer 198252884aeSStefan Eßer for (i = 0; i < a->len; ++i) { 199252884aeSStefan Eßer BcBigDig in = ((BcBigDig) a->num[i]) * b + carry; 200252884aeSStefan Eßer c->num[i] = in % BC_BASE_POW; 201252884aeSStefan Eßer carry = in / BC_BASE_POW; 202252884aeSStefan Eßer } 203252884aeSStefan Eßer 204252884aeSStefan Eßer assert(carry < BC_BASE_POW); 205252884aeSStefan Eßer c->num[i] = (BcDig) carry; 206252884aeSStefan Eßer c->len = a->len; 207252884aeSStefan Eßer c->len += (carry != 0); 208252884aeSStefan Eßer 209252884aeSStefan Eßer bc_num_clean(c); 210252884aeSStefan Eßer 21150696a6eSStefan Eßer assert(!BC_NUM_NEG(c) || BC_NUM_NONZERO(c)); 21250696a6eSStefan Eßer assert(BC_NUM_RDX_VAL(c) <= c->len || !c->len); 21350696a6eSStefan Eßer assert(!c->len || c->num[c->len - 1] || BC_NUM_RDX_VAL(c) == c->len); 214252884aeSStefan Eßer } 215252884aeSStefan Eßer 216252884aeSStefan Eßer static void bc_num_divArray(const BcNum *restrict a, BcBigDig b, 217252884aeSStefan Eßer BcNum *restrict c, BcBigDig *rem) 218252884aeSStefan Eßer { 219252884aeSStefan Eßer size_t i; 220252884aeSStefan Eßer BcBigDig carry = 0; 221252884aeSStefan Eßer 222252884aeSStefan Eßer assert(c->cap >= a->len); 223252884aeSStefan Eßer 224252884aeSStefan Eßer for (i = a->len - 1; i < a->len; --i) { 225252884aeSStefan Eßer BcBigDig in = ((BcBigDig) a->num[i]) + carry * BC_BASE_POW; 226252884aeSStefan Eßer assert(in / b < BC_BASE_POW); 227252884aeSStefan Eßer c->num[i] = (BcDig) (in / b); 228252884aeSStefan Eßer carry = in % b; 229252884aeSStefan Eßer } 230252884aeSStefan Eßer 231252884aeSStefan Eßer c->len = a->len; 232252884aeSStefan Eßer bc_num_clean(c); 233252884aeSStefan Eßer *rem = carry; 234252884aeSStefan Eßer 23550696a6eSStefan Eßer assert(!BC_NUM_NEG(c) || BC_NUM_NONZERO(c)); 23650696a6eSStefan Eßer assert(BC_NUM_RDX_VAL(c) <= c->len || !c->len); 23750696a6eSStefan Eßer assert(!c->len || c->num[c->len - 1] || BC_NUM_RDX_VAL(c) == c->len); 238252884aeSStefan Eßer } 239252884aeSStefan Eßer 240252884aeSStefan Eßer static ssize_t bc_num_compare(const BcDig *restrict a, const BcDig *restrict b, 241252884aeSStefan Eßer size_t len) 242252884aeSStefan Eßer { 243252884aeSStefan Eßer size_t i; 244252884aeSStefan Eßer BcDig c = 0; 245252884aeSStefan Eßer for (i = len - 1; i < len && !(c = a[i] - b[i]); --i); 246252884aeSStefan Eßer return bc_num_neg(i + 1, c < 0); 247252884aeSStefan Eßer } 248252884aeSStefan Eßer 249252884aeSStefan Eßer ssize_t bc_num_cmp(const BcNum *a, const BcNum *b) { 250252884aeSStefan Eßer 25150696a6eSStefan Eßer size_t i, min, a_int, b_int, diff, ardx, brdx; 252252884aeSStefan Eßer BcDig *max_num, *min_num; 253252884aeSStefan Eßer bool a_max, neg = false; 254252884aeSStefan Eßer ssize_t cmp; 255252884aeSStefan Eßer 256252884aeSStefan Eßer assert(a != NULL && b != NULL); 257252884aeSStefan Eßer 258252884aeSStefan Eßer if (a == b) return 0; 25950696a6eSStefan Eßer if (BC_NUM_ZERO(a)) return bc_num_neg(b->len != 0, !BC_NUM_NEG(b)); 260252884aeSStefan Eßer if (BC_NUM_ZERO(b)) return bc_num_cmpZero(a); 26150696a6eSStefan Eßer if (BC_NUM_NEG(a)) { 26250696a6eSStefan Eßer if (BC_NUM_NEG(b)) neg = true; 263252884aeSStefan Eßer else return -1; 264252884aeSStefan Eßer } 26550696a6eSStefan Eßer else if (BC_NUM_NEG(b)) return 1; 266252884aeSStefan Eßer 267252884aeSStefan Eßer a_int = bc_num_int(a); 268252884aeSStefan Eßer b_int = bc_num_int(b); 269252884aeSStefan Eßer a_int -= b_int; 270252884aeSStefan Eßer 271252884aeSStefan Eßer if (a_int) return neg ? -((ssize_t) a_int) : (ssize_t) a_int; 272252884aeSStefan Eßer 27350696a6eSStefan Eßer ardx = BC_NUM_RDX_VAL(a); 27450696a6eSStefan Eßer brdx = BC_NUM_RDX_VAL(b); 27550696a6eSStefan Eßer a_max = (ardx > brdx); 276252884aeSStefan Eßer 277252884aeSStefan Eßer if (a_max) { 27850696a6eSStefan Eßer min = brdx; 27950696a6eSStefan Eßer diff = ardx - brdx; 280252884aeSStefan Eßer max_num = a->num + diff; 281252884aeSStefan Eßer min_num = b->num; 282252884aeSStefan Eßer } 283252884aeSStefan Eßer else { 28450696a6eSStefan Eßer min = ardx; 28550696a6eSStefan Eßer diff = brdx - ardx; 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 30350696a6eSStefan Eßer size_t nrdx, places_rdx; 304252884aeSStefan Eßer 305252884aeSStefan Eßer if (!places) return; 306252884aeSStefan Eßer 30750696a6eSStefan Eßer nrdx = BC_NUM_RDX_VAL(n); 30850696a6eSStefan Eßer places_rdx = nrdx ? nrdx - BC_NUM_RDX(n->scale - places) : 0; 309252884aeSStefan Eßer assert(places <= n->scale && (BC_NUM_ZERO(n) || places_rdx <= n->len)); 310252884aeSStefan Eßer 311252884aeSStefan Eßer n->scale -= places; 31250696a6eSStefan Eßer BC_NUM_RDX_SET(n, nrdx - places_rdx); 313252884aeSStefan Eßer 314252884aeSStefan Eßer if (BC_NUM_NONZERO(n)) { 315252884aeSStefan Eßer 316252884aeSStefan Eßer size_t pow; 317252884aeSStefan Eßer 318252884aeSStefan Eßer pow = n->scale % BC_BASE_DIGS; 319252884aeSStefan Eßer pow = pow ? BC_BASE_DIGS - pow : 0; 320252884aeSStefan Eßer pow = bc_num_pow10[pow]; 321252884aeSStefan Eßer 322252884aeSStefan Eßer n->len -= places_rdx; 323252884aeSStefan Eßer memmove(n->num, n->num + places_rdx, BC_NUM_SIZE(n->len)); 324252884aeSStefan Eßer 325252884aeSStefan Eßer // Clear the lower part of the last digit. 326252884aeSStefan Eßer if (BC_NUM_NONZERO(n)) n->num[0] -= n->num[0] % (BcDig) pow; 327252884aeSStefan Eßer 328252884aeSStefan Eßer bc_num_clean(n); 329252884aeSStefan Eßer } 330252884aeSStefan Eßer } 331252884aeSStefan Eßer 33250696a6eSStefan Eßer void bc_num_extend(BcNum *restrict n, size_t places) { 333252884aeSStefan Eßer 33450696a6eSStefan Eßer size_t nrdx, places_rdx; 335252884aeSStefan Eßer 336252884aeSStefan Eßer if (!places) return; 337252884aeSStefan Eßer if (BC_NUM_ZERO(n)) { 338252884aeSStefan Eßer n->scale += places; 339252884aeSStefan Eßer return; 340252884aeSStefan Eßer } 341252884aeSStefan Eßer 34250696a6eSStefan Eßer nrdx = BC_NUM_RDX_VAL(n); 34350696a6eSStefan Eßer places_rdx = BC_NUM_RDX(places + n->scale) - nrdx; 344252884aeSStefan Eßer 345252884aeSStefan Eßer if (places_rdx) { 346252884aeSStefan Eßer bc_num_expand(n, bc_vm_growSize(n->len, places_rdx)); 347252884aeSStefan Eßer memmove(n->num + places_rdx, n->num, BC_NUM_SIZE(n->len)); 348252884aeSStefan Eßer memset(n->num, 0, BC_NUM_SIZE(places_rdx)); 349252884aeSStefan Eßer } 350252884aeSStefan Eßer 35150696a6eSStefan Eßer BC_NUM_RDX_SET(n, nrdx + places_rdx); 352252884aeSStefan Eßer n->scale += places; 353252884aeSStefan Eßer n->len += places_rdx; 354252884aeSStefan Eßer 35550696a6eSStefan Eßer assert(BC_NUM_RDX_VAL(n) == BC_NUM_RDX(n->scale)); 356252884aeSStefan Eßer } 357252884aeSStefan Eßer 358252884aeSStefan Eßer static void bc_num_retireMul(BcNum *restrict n, size_t scale, 359252884aeSStefan Eßer bool neg1, bool neg2) 360252884aeSStefan Eßer { 361252884aeSStefan Eßer if (n->scale < scale) bc_num_extend(n, scale - n->scale); 362252884aeSStefan Eßer else bc_num_truncate(n, n->scale - scale); 363252884aeSStefan Eßer 364252884aeSStefan Eßer bc_num_clean(n); 36550696a6eSStefan Eßer if (BC_NUM_NONZERO(n)) n->rdx = BC_NUM_NEG_VAL(n, !neg1 != !neg2); 366252884aeSStefan Eßer } 367252884aeSStefan Eßer 368252884aeSStefan Eßer static void bc_num_split(const BcNum *restrict n, size_t idx, 369252884aeSStefan Eßer BcNum *restrict a, BcNum *restrict b) 370252884aeSStefan Eßer { 371252884aeSStefan Eßer assert(BC_NUM_ZERO(a)); 372252884aeSStefan Eßer assert(BC_NUM_ZERO(b)); 373252884aeSStefan Eßer 374252884aeSStefan Eßer if (idx < n->len) { 375252884aeSStefan Eßer 376252884aeSStefan Eßer b->len = n->len - idx; 377252884aeSStefan Eßer a->len = idx; 37850696a6eSStefan Eßer a->scale = b->scale = 0; 37950696a6eSStefan Eßer BC_NUM_RDX_SET(a, 0); 38050696a6eSStefan Eßer BC_NUM_RDX_SET(b, 0); 381252884aeSStefan Eßer 382252884aeSStefan Eßer assert(a->cap >= a->len); 383252884aeSStefan Eßer assert(b->cap >= b->len); 384252884aeSStefan Eßer 385252884aeSStefan Eßer memcpy(b->num, n->num + idx, BC_NUM_SIZE(b->len)); 386252884aeSStefan Eßer memcpy(a->num, n->num, BC_NUM_SIZE(idx)); 387252884aeSStefan Eßer 388252884aeSStefan Eßer bc_num_clean(b); 389252884aeSStefan Eßer } 390252884aeSStefan Eßer else bc_num_copy(a, n); 391252884aeSStefan Eßer 392252884aeSStefan Eßer bc_num_clean(a); 393252884aeSStefan Eßer } 394252884aeSStefan Eßer 395252884aeSStefan Eßer static size_t bc_num_shiftZero(BcNum *restrict n) { 396252884aeSStefan Eßer 397252884aeSStefan Eßer size_t i; 398252884aeSStefan Eßer 39950696a6eSStefan Eßer assert(!BC_NUM_RDX_VAL(n) || BC_NUM_ZERO(n)); 400252884aeSStefan Eßer 401252884aeSStefan Eßer for (i = 0; i < n->len && !n->num[i]; ++i); 402252884aeSStefan Eßer 403252884aeSStefan Eßer n->len -= i; 404252884aeSStefan Eßer n->num += i; 405252884aeSStefan Eßer 406252884aeSStefan Eßer return i; 407252884aeSStefan Eßer } 408252884aeSStefan Eßer 409252884aeSStefan Eßer static void bc_num_unshiftZero(BcNum *restrict n, size_t places_rdx) { 410252884aeSStefan Eßer n->len += places_rdx; 411252884aeSStefan Eßer n->num -= places_rdx; 412252884aeSStefan Eßer } 413252884aeSStefan Eßer 414252884aeSStefan Eßer static void bc_num_shift(BcNum *restrict n, BcBigDig dig) { 415252884aeSStefan Eßer 416252884aeSStefan Eßer size_t i, len = n->len; 417252884aeSStefan Eßer BcBigDig carry = 0, pow; 418252884aeSStefan Eßer BcDig *ptr = n->num; 419252884aeSStefan Eßer 420252884aeSStefan Eßer assert(dig < BC_BASE_DIGS); 421252884aeSStefan Eßer 422252884aeSStefan Eßer pow = bc_num_pow10[dig]; 423252884aeSStefan Eßer dig = bc_num_pow10[BC_BASE_DIGS - dig]; 424252884aeSStefan Eßer 425252884aeSStefan Eßer for (i = len - 1; i < len; --i) { 426252884aeSStefan Eßer BcBigDig in, temp; 427252884aeSStefan Eßer in = ((BcBigDig) ptr[i]); 428252884aeSStefan Eßer temp = carry * dig; 429252884aeSStefan Eßer carry = in % pow; 430252884aeSStefan Eßer ptr[i] = ((BcDig) (in / pow)) + (BcDig) temp; 431252884aeSStefan Eßer } 432252884aeSStefan Eßer 433252884aeSStefan Eßer assert(!carry); 434252884aeSStefan Eßer } 435252884aeSStefan Eßer 436252884aeSStefan Eßer static void bc_num_shiftLeft(BcNum *restrict n, size_t places) { 437252884aeSStefan Eßer 438252884aeSStefan Eßer BcBigDig dig; 439252884aeSStefan Eßer size_t places_rdx; 440252884aeSStefan Eßer bool shift; 441252884aeSStefan Eßer 442252884aeSStefan Eßer if (!places) return; 443252884aeSStefan Eßer if (places > n->scale) { 444252884aeSStefan Eßer size_t size = bc_vm_growSize(BC_NUM_RDX(places - n->scale), n->len); 44550696a6eSStefan Eßer if (size > SIZE_MAX - 1) bc_vm_err(BC_ERR_MATH_OVERFLOW); 446252884aeSStefan Eßer } 447252884aeSStefan Eßer if (BC_NUM_ZERO(n)) { 448252884aeSStefan Eßer if (n->scale >= places) n->scale -= places; 449252884aeSStefan Eßer else n->scale = 0; 450252884aeSStefan Eßer return; 451252884aeSStefan Eßer } 452252884aeSStefan Eßer 453252884aeSStefan Eßer dig = (BcBigDig) (places % BC_BASE_DIGS); 454252884aeSStefan Eßer shift = (dig != 0); 455252884aeSStefan Eßer places_rdx = BC_NUM_RDX(places); 456252884aeSStefan Eßer 457252884aeSStefan Eßer if (n->scale) { 458252884aeSStefan Eßer 45950696a6eSStefan Eßer size_t nrdx = BC_NUM_RDX_VAL(n); 46050696a6eSStefan Eßer 46150696a6eSStefan Eßer if (nrdx >= places_rdx) { 462252884aeSStefan Eßer 463252884aeSStefan Eßer size_t mod = n->scale % BC_BASE_DIGS, revdig; 464252884aeSStefan Eßer 465252884aeSStefan Eßer mod = mod ? mod : BC_BASE_DIGS; 466252884aeSStefan Eßer revdig = dig ? BC_BASE_DIGS - dig : 0; 467252884aeSStefan Eßer 468252884aeSStefan Eßer if (mod + revdig > BC_BASE_DIGS) places_rdx = 1; 469252884aeSStefan Eßer else places_rdx = 0; 470252884aeSStefan Eßer } 47150696a6eSStefan Eßer else places_rdx -= nrdx; 472252884aeSStefan Eßer } 473252884aeSStefan Eßer 474252884aeSStefan Eßer if (places_rdx) { 475252884aeSStefan Eßer bc_num_expand(n, bc_vm_growSize(n->len, places_rdx)); 476252884aeSStefan Eßer memmove(n->num + places_rdx, n->num, BC_NUM_SIZE(n->len)); 477252884aeSStefan Eßer memset(n->num, 0, BC_NUM_SIZE(places_rdx)); 478252884aeSStefan Eßer n->len += places_rdx; 479252884aeSStefan Eßer } 480252884aeSStefan Eßer 48150696a6eSStefan Eßer if (places > n->scale) { 48250696a6eSStefan Eßer n->scale = 0; 48350696a6eSStefan Eßer BC_NUM_RDX_SET(n, 0); 48450696a6eSStefan Eßer } 485252884aeSStefan Eßer else { 486252884aeSStefan Eßer n->scale -= places; 48750696a6eSStefan Eßer BC_NUM_RDX_SET(n, BC_NUM_RDX(n->scale)); 488252884aeSStefan Eßer } 489252884aeSStefan Eßer 490252884aeSStefan Eßer if (shift) bc_num_shift(n, BC_BASE_DIGS - dig); 491252884aeSStefan Eßer 492252884aeSStefan Eßer bc_num_clean(n); 493252884aeSStefan Eßer } 494252884aeSStefan Eßer 49550696a6eSStefan Eßer void bc_num_shiftRight(BcNum *restrict n, size_t places) { 496252884aeSStefan Eßer 497252884aeSStefan Eßer BcBigDig dig; 498252884aeSStefan Eßer size_t places_rdx, scale, scale_mod, int_len, expand; 499252884aeSStefan Eßer bool shift; 500252884aeSStefan Eßer 501252884aeSStefan Eßer if (!places) return; 502252884aeSStefan Eßer if (BC_NUM_ZERO(n)) { 503252884aeSStefan Eßer n->scale += places; 504252884aeSStefan Eßer bc_num_expand(n, BC_NUM_RDX(n->scale)); 505252884aeSStefan Eßer return; 506252884aeSStefan Eßer } 507252884aeSStefan Eßer 508252884aeSStefan Eßer dig = (BcBigDig) (places % BC_BASE_DIGS); 509252884aeSStefan Eßer shift = (dig != 0); 510252884aeSStefan Eßer scale = n->scale; 511252884aeSStefan Eßer scale_mod = scale % BC_BASE_DIGS; 512252884aeSStefan Eßer scale_mod = scale_mod ? scale_mod : BC_BASE_DIGS; 513252884aeSStefan Eßer int_len = bc_num_int(n); 514252884aeSStefan Eßer places_rdx = BC_NUM_RDX(places); 515252884aeSStefan Eßer 516252884aeSStefan Eßer if (scale_mod + dig > BC_BASE_DIGS) { 517252884aeSStefan Eßer expand = places_rdx - 1; 518252884aeSStefan Eßer places_rdx = 1; 519252884aeSStefan Eßer } 520252884aeSStefan Eßer else { 521252884aeSStefan Eßer expand = places_rdx; 522252884aeSStefan Eßer places_rdx = 0; 523252884aeSStefan Eßer } 524252884aeSStefan Eßer 525252884aeSStefan Eßer if (expand > int_len) expand -= int_len; 526252884aeSStefan Eßer else expand = 0; 527252884aeSStefan Eßer 528252884aeSStefan Eßer bc_num_extend(n, places_rdx * BC_BASE_DIGS); 529252884aeSStefan Eßer bc_num_expand(n, bc_vm_growSize(expand, n->len)); 530252884aeSStefan Eßer memset(n->num + n->len, 0, BC_NUM_SIZE(expand)); 531252884aeSStefan Eßer n->len += expand; 53250696a6eSStefan Eßer n->scale = 0; 53350696a6eSStefan Eßer BC_NUM_RDX_SET(n, 0); 534252884aeSStefan Eßer 535252884aeSStefan Eßer if (shift) bc_num_shift(n, dig); 536252884aeSStefan Eßer 537252884aeSStefan Eßer n->scale = scale + places; 53850696a6eSStefan Eßer BC_NUM_RDX_SET(n, BC_NUM_RDX(n->scale)); 539252884aeSStefan Eßer 540252884aeSStefan Eßer bc_num_clean(n); 541252884aeSStefan Eßer 54250696a6eSStefan Eßer assert(BC_NUM_RDX_VAL(n) <= n->len && n->len <= n->cap); 54350696a6eSStefan Eßer assert(BC_NUM_RDX_VAL(n) == BC_NUM_RDX(n->scale)); 544252884aeSStefan Eßer } 545252884aeSStefan Eßer 546252884aeSStefan Eßer static void bc_num_inv(BcNum *a, BcNum *b, size_t scale) { 547252884aeSStefan Eßer 548252884aeSStefan Eßer BcNum one; 549252884aeSStefan Eßer BcDig num[2]; 550252884aeSStefan Eßer 551252884aeSStefan Eßer assert(BC_NUM_NONZERO(a)); 552252884aeSStefan Eßer 553252884aeSStefan Eßer bc_num_setup(&one, num, sizeof(num) / sizeof(BcDig)); 554252884aeSStefan Eßer bc_num_one(&one); 555252884aeSStefan Eßer 556252884aeSStefan Eßer bc_num_div(&one, a, b, scale); 557252884aeSStefan Eßer } 558252884aeSStefan Eßer 559252884aeSStefan Eßer #if BC_ENABLE_EXTRA_MATH 560252884aeSStefan Eßer static void bc_num_intop(const BcNum *a, const BcNum *b, BcNum *restrict c, 561252884aeSStefan Eßer BcBigDig *v) 562252884aeSStefan Eßer { 56350696a6eSStefan Eßer if (BC_ERR(BC_NUM_RDX_VAL(b))) bc_vm_err(BC_ERR_MATH_NON_INTEGER); 564252884aeSStefan Eßer bc_num_copy(c, a); 565252884aeSStefan Eßer bc_num_bigdig(b, v); 566252884aeSStefan Eßer } 567252884aeSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH 568252884aeSStefan Eßer 569252884aeSStefan Eßer static void bc_num_as(BcNum *a, BcNum *b, BcNum *restrict c, size_t sub) { 570252884aeSStefan Eßer 571252884aeSStefan Eßer BcDig *ptr_c, *ptr_l, *ptr_r; 572252884aeSStefan Eßer size_t i, min_rdx, max_rdx, diff, a_int, b_int, min_len, max_len, max_int; 57350696a6eSStefan Eßer size_t len_l, len_r, ardx, brdx; 57450696a6eSStefan Eßer bool b_neg, do_sub, do_rev_sub, carry, c_neg; 575252884aeSStefan Eßer 576252884aeSStefan Eßer // Because this function doesn't need to use scale (per the bc spec), 577252884aeSStefan Eßer // I am hijacking it to say whether it's doing an add or a subtract. 578252884aeSStefan Eßer // Convert substraction to addition of negative second operand. 579252884aeSStefan Eßer 580252884aeSStefan Eßer if (BC_NUM_ZERO(b)) { 581252884aeSStefan Eßer bc_num_copy(c, a); 582252884aeSStefan Eßer return; 583252884aeSStefan Eßer } 584252884aeSStefan Eßer if (BC_NUM_ZERO(a)) { 585252884aeSStefan Eßer bc_num_copy(c, b); 58650696a6eSStefan Eßer c->rdx = BC_NUM_NEG_VAL(c, BC_NUM_NEG(b) != sub); 587252884aeSStefan Eßer return; 588252884aeSStefan Eßer } 589252884aeSStefan Eßer 590252884aeSStefan Eßer // Invert sign of b if it is to be subtracted. This operation must 591252884aeSStefan Eßer // preced the tests for any of the operands being zero. 59250696a6eSStefan Eßer b_neg = (BC_NUM_NEG(b) != sub); 593252884aeSStefan Eßer 594252884aeSStefan Eßer // Actually add the numbers if their signs are equal, else subtract. 59550696a6eSStefan Eßer do_sub = (BC_NUM_NEG(a) != b_neg); 596252884aeSStefan Eßer 597252884aeSStefan Eßer a_int = bc_num_int(a); 598252884aeSStefan Eßer b_int = bc_num_int(b); 599252884aeSStefan Eßer max_int = BC_MAX(a_int, b_int); 600252884aeSStefan Eßer 60150696a6eSStefan Eßer ardx = BC_NUM_RDX_VAL(a); 60250696a6eSStefan Eßer brdx = BC_NUM_RDX_VAL(b); 60350696a6eSStefan Eßer min_rdx = BC_MIN(ardx, brdx); 60450696a6eSStefan Eßer max_rdx = BC_MAX(ardx, brdx); 605252884aeSStefan Eßer diff = max_rdx - min_rdx; 606252884aeSStefan Eßer 607252884aeSStefan Eßer max_len = max_int + max_rdx; 608252884aeSStefan Eßer 609252884aeSStefan Eßer if (do_sub) { 610252884aeSStefan Eßer 611252884aeSStefan Eßer // Check whether b has to be subtracted from a or a from b. 612252884aeSStefan Eßer if (a_int != b_int) do_rev_sub = (a_int < b_int); 61350696a6eSStefan Eßer else if (ardx > brdx) 614252884aeSStefan Eßer do_rev_sub = (bc_num_compare(a->num + diff, b->num, b->len) < 0); 615252884aeSStefan Eßer else 616252884aeSStefan Eßer do_rev_sub = (bc_num_compare(a->num, b->num + diff, a->len) <= 0); 617252884aeSStefan Eßer } 618252884aeSStefan Eßer else { 619252884aeSStefan Eßer 620252884aeSStefan Eßer // The result array of the addition might come out one element 621252884aeSStefan Eßer // longer than the bigger of the operand arrays. 622252884aeSStefan Eßer max_len += 1; 623252884aeSStefan Eßer do_rev_sub = (a_int < b_int); 624252884aeSStefan Eßer } 625252884aeSStefan Eßer 626252884aeSStefan Eßer assert(max_len <= c->cap); 627252884aeSStefan Eßer 628252884aeSStefan Eßer if (do_rev_sub) { 629252884aeSStefan Eßer ptr_l = b->num; 630252884aeSStefan Eßer ptr_r = a->num; 631252884aeSStefan Eßer len_l = b->len; 632252884aeSStefan Eßer len_r = a->len; 633252884aeSStefan Eßer } 634252884aeSStefan Eßer else { 635252884aeSStefan Eßer ptr_l = a->num; 636252884aeSStefan Eßer ptr_r = b->num; 637252884aeSStefan Eßer len_l = a->len; 638252884aeSStefan Eßer len_r = b->len; 639252884aeSStefan Eßer } 640252884aeSStefan Eßer 641252884aeSStefan Eßer ptr_c = c->num; 642252884aeSStefan Eßer carry = false; 643252884aeSStefan Eßer 644252884aeSStefan Eßer if (diff) { 645252884aeSStefan Eßer 646252884aeSStefan Eßer // If the rdx values of the operands do not match, the result will 647252884aeSStefan Eßer // have low end elements that are the positive or negative trailing 648252884aeSStefan Eßer // elements of the operand with higher rdx value. 64950696a6eSStefan Eßer if ((ardx > brdx) != do_rev_sub) { 650252884aeSStefan Eßer 65150696a6eSStefan Eßer // !do_rev_sub && ardx > brdx || do_rev_sub && brdx > ardx 652252884aeSStefan Eßer // The left operand has BcDig values that need to be copied, 653252884aeSStefan Eßer // either from a or from b (in case of a reversed subtraction). 654252884aeSStefan Eßer memcpy(ptr_c, ptr_l, BC_NUM_SIZE(diff)); 655252884aeSStefan Eßer ptr_l += diff; 656252884aeSStefan Eßer len_l -= diff; 657252884aeSStefan Eßer } 658252884aeSStefan Eßer else { 659252884aeSStefan Eßer 660252884aeSStefan Eßer // The right operand has BcDig values that need to be copied 661252884aeSStefan Eßer // or subtracted from zero (in case of a subtraction). 662252884aeSStefan Eßer if (do_sub) { 663252884aeSStefan Eßer 66450696a6eSStefan Eßer // do_sub (do_rev_sub && ardx > brdx || 66550696a6eSStefan Eßer // !do_rev_sub && brdx > ardx) 666252884aeSStefan Eßer for (i = 0; i < diff; i++) 667252884aeSStefan Eßer ptr_c[i] = bc_num_subDigits(0, ptr_r[i], &carry); 668252884aeSStefan Eßer } 669252884aeSStefan Eßer else { 670252884aeSStefan Eßer 67150696a6eSStefan Eßer // !do_sub && brdx > ardx 672252884aeSStefan Eßer memcpy(ptr_c, ptr_r, BC_NUM_SIZE(diff)); 673252884aeSStefan Eßer } 674252884aeSStefan Eßer 675252884aeSStefan Eßer ptr_r += diff; 676252884aeSStefan Eßer len_r -= diff; 677252884aeSStefan Eßer } 678252884aeSStefan Eßer 679252884aeSStefan Eßer ptr_c += diff; 680252884aeSStefan Eßer } 681252884aeSStefan Eßer 682252884aeSStefan Eßer min_len = BC_MIN(len_l, len_r); 683252884aeSStefan Eßer 684252884aeSStefan Eßer // After dealing with possible low array elements that depend on only one 685252884aeSStefan Eßer // operand, the actual add or subtract can be performed as if the rdx of 686252884aeSStefan Eßer // both operands was the same. 687252884aeSStefan Eßer // Inlining takes care of eliminating constant zero arguments to 688252884aeSStefan Eßer // addDigit/subDigit (checked in disassembly of resulting bc binary 689252884aeSStefan Eßer // compiled with gcc and clang). 690252884aeSStefan Eßer if (do_sub) { 691252884aeSStefan Eßer for (i = 0; i < min_len; ++i) 692252884aeSStefan Eßer ptr_c[i] = bc_num_subDigits(ptr_l[i], ptr_r[i], &carry); 693252884aeSStefan Eßer for (; i < len_l; ++i) ptr_c[i] = bc_num_subDigits(ptr_l[i], 0, &carry); 694252884aeSStefan Eßer } 695252884aeSStefan Eßer else { 696252884aeSStefan Eßer for (i = 0; i < min_len; ++i) 697252884aeSStefan Eßer ptr_c[i] = bc_num_addDigits(ptr_l[i], ptr_r[i], &carry); 698252884aeSStefan Eßer for (; i < len_l; ++i) ptr_c[i] = bc_num_addDigits(ptr_l[i], 0, &carry); 699252884aeSStefan Eßer ptr_c[i] = bc_num_addDigits(0, 0, &carry); 700252884aeSStefan Eßer } 701252884aeSStefan Eßer 702252884aeSStefan Eßer assert(carry == false); 703252884aeSStefan Eßer 704252884aeSStefan Eßer // The result has the same sign as a, unless the operation was a 705252884aeSStefan Eßer // reverse subtraction (b - a). 70650696a6eSStefan Eßer c_neg = BC_NUM_NEG(a) != (do_sub && do_rev_sub); 70750696a6eSStefan Eßer BC_NUM_RDX_SET_NEG(c, max_rdx, c_neg); 708252884aeSStefan Eßer c->len = max_len; 709252884aeSStefan Eßer c->scale = BC_MAX(a->scale, b->scale); 710252884aeSStefan Eßer 711252884aeSStefan Eßer bc_num_clean(c); 712252884aeSStefan Eßer } 713252884aeSStefan Eßer 714252884aeSStefan Eßer static void bc_num_m_simp(const BcNum *a, const BcNum *b, BcNum *restrict c) 715252884aeSStefan Eßer { 716252884aeSStefan Eßer size_t i, alen = a->len, blen = b->len, clen; 717252884aeSStefan Eßer BcDig *ptr_a = a->num, *ptr_b = b->num, *ptr_c; 718252884aeSStefan Eßer BcBigDig sum = 0, carry = 0; 719252884aeSStefan Eßer 720252884aeSStefan Eßer assert(sizeof(sum) >= sizeof(BcDig) * 2); 72150696a6eSStefan Eßer assert(!BC_NUM_RDX_VAL(a) && !BC_NUM_RDX_VAL(b)); 722252884aeSStefan Eßer 723252884aeSStefan Eßer clen = bc_vm_growSize(alen, blen); 724252884aeSStefan Eßer bc_num_expand(c, bc_vm_growSize(clen, 1)); 725252884aeSStefan Eßer 726252884aeSStefan Eßer ptr_c = c->num; 727252884aeSStefan Eßer memset(ptr_c, 0, BC_NUM_SIZE(c->cap)); 728252884aeSStefan Eßer 729252884aeSStefan Eßer for (i = 0; i < clen; ++i) { 730252884aeSStefan Eßer 731252884aeSStefan Eßer ssize_t sidx = (ssize_t) (i - blen + 1); 732252884aeSStefan Eßer size_t j = (size_t) BC_MAX(0, sidx), k = BC_MIN(i, blen - 1); 733252884aeSStefan Eßer 734252884aeSStefan Eßer for (; j < alen && k < blen; ++j, --k) { 735252884aeSStefan Eßer 736252884aeSStefan Eßer sum += ((BcBigDig) ptr_a[j]) * ((BcBigDig) ptr_b[k]); 737252884aeSStefan Eßer 738252884aeSStefan Eßer if (sum >= ((BcBigDig) BC_BASE_POW) * BC_BASE_POW) { 739252884aeSStefan Eßer carry += sum / BC_BASE_POW; 740252884aeSStefan Eßer sum %= BC_BASE_POW; 741252884aeSStefan Eßer } 742252884aeSStefan Eßer } 743252884aeSStefan Eßer 744252884aeSStefan Eßer if (sum >= BC_BASE_POW) { 745252884aeSStefan Eßer carry += sum / BC_BASE_POW; 746252884aeSStefan Eßer sum %= BC_BASE_POW; 747252884aeSStefan Eßer } 748252884aeSStefan Eßer 749252884aeSStefan Eßer ptr_c[i] = (BcDig) sum; 750252884aeSStefan Eßer assert(ptr_c[i] < BC_BASE_POW); 751252884aeSStefan Eßer sum = carry; 752252884aeSStefan Eßer carry = 0; 753252884aeSStefan Eßer } 754252884aeSStefan Eßer 755252884aeSStefan Eßer // This should always be true because there should be no carry on the last 756252884aeSStefan Eßer // digit; multiplication never goes above the sum of both lengths. 757252884aeSStefan Eßer assert(!sum); 758252884aeSStefan Eßer 759252884aeSStefan Eßer c->len = clen; 760252884aeSStefan Eßer } 761252884aeSStefan Eßer 762252884aeSStefan Eßer static void bc_num_shiftAddSub(BcNum *restrict n, const BcNum *restrict a, 763252884aeSStefan Eßer size_t shift, BcNumShiftAddOp op) 764252884aeSStefan Eßer { 765252884aeSStefan Eßer assert(n->len >= shift + a->len); 76650696a6eSStefan Eßer assert(!BC_NUM_RDX_VAL(n) && !BC_NUM_RDX_VAL(a)); 767252884aeSStefan Eßer op(n->num + shift, a->num, a->len); 768252884aeSStefan Eßer } 769252884aeSStefan Eßer 770252884aeSStefan Eßer static void bc_num_k(BcNum *a, BcNum *b, BcNum *restrict c) { 771252884aeSStefan Eßer 772252884aeSStefan Eßer size_t max, max2, total; 773252884aeSStefan Eßer BcNum l1, h1, l2, h2, m2, m1, z0, z1, z2, temp; 774252884aeSStefan Eßer BcDig *digs, *dig_ptr; 775252884aeSStefan Eßer BcNumShiftAddOp op; 776252884aeSStefan Eßer bool aone = BC_NUM_ONE(a); 777252884aeSStefan Eßer 778252884aeSStefan Eßer assert(BC_NUM_ZERO(c)); 779252884aeSStefan Eßer 780252884aeSStefan Eßer if (BC_NUM_ZERO(a) || BC_NUM_ZERO(b)) return; 781252884aeSStefan Eßer if (aone || BC_NUM_ONE(b)) { 782252884aeSStefan Eßer bc_num_copy(c, aone ? b : a); 78350696a6eSStefan Eßer if ((aone && BC_NUM_NEG(a)) || BC_NUM_NEG(b)) BC_NUM_NEG_TGL(c); 784252884aeSStefan Eßer return; 785252884aeSStefan Eßer } 786252884aeSStefan Eßer if (a->len < BC_NUM_KARATSUBA_LEN || b->len < BC_NUM_KARATSUBA_LEN) { 787252884aeSStefan Eßer bc_num_m_simp(a, b, c); 788252884aeSStefan Eßer return; 789252884aeSStefan Eßer } 790252884aeSStefan Eßer 791252884aeSStefan Eßer max = BC_MAX(a->len, b->len); 792252884aeSStefan Eßer max = BC_MAX(max, BC_NUM_DEF_SIZE); 793252884aeSStefan Eßer max2 = (max + 1) / 2; 794252884aeSStefan Eßer 795252884aeSStefan Eßer total = bc_vm_arraySize(BC_NUM_KARATSUBA_ALLOCS, max); 796252884aeSStefan Eßer 797252884aeSStefan Eßer BC_SIG_LOCK; 798252884aeSStefan Eßer 799252884aeSStefan Eßer digs = dig_ptr = bc_vm_malloc(BC_NUM_SIZE(total)); 800252884aeSStefan Eßer 801252884aeSStefan Eßer bc_num_setup(&l1, dig_ptr, max); 802252884aeSStefan Eßer dig_ptr += max; 803252884aeSStefan Eßer bc_num_setup(&h1, dig_ptr, max); 804252884aeSStefan Eßer dig_ptr += max; 805252884aeSStefan Eßer bc_num_setup(&l2, dig_ptr, max); 806252884aeSStefan Eßer dig_ptr += max; 807252884aeSStefan Eßer bc_num_setup(&h2, dig_ptr, max); 808252884aeSStefan Eßer dig_ptr += max; 809252884aeSStefan Eßer bc_num_setup(&m1, dig_ptr, max); 810252884aeSStefan Eßer dig_ptr += max; 811252884aeSStefan Eßer bc_num_setup(&m2, dig_ptr, max); 812252884aeSStefan Eßer max = bc_vm_growSize(max, 1); 813252884aeSStefan Eßer bc_num_init(&z0, max); 814252884aeSStefan Eßer bc_num_init(&z1, max); 815252884aeSStefan Eßer bc_num_init(&z2, max); 816252884aeSStefan Eßer max = bc_vm_growSize(max, max) + 1; 817252884aeSStefan Eßer bc_num_init(&temp, max); 818252884aeSStefan Eßer 819252884aeSStefan Eßer BC_SETJMP_LOCKED(err); 820252884aeSStefan Eßer 821252884aeSStefan Eßer BC_SIG_UNLOCK; 822252884aeSStefan Eßer 823252884aeSStefan Eßer bc_num_split(a, max2, &l1, &h1); 824252884aeSStefan Eßer bc_num_split(b, max2, &l2, &h2); 825252884aeSStefan Eßer 826252884aeSStefan Eßer bc_num_expand(c, max); 827252884aeSStefan Eßer c->len = max; 828252884aeSStefan Eßer memset(c->num, 0, BC_NUM_SIZE(c->len)); 829252884aeSStefan Eßer 830252884aeSStefan Eßer bc_num_sub(&h1, &l1, &m1, 0); 831252884aeSStefan Eßer bc_num_sub(&l2, &h2, &m2, 0); 832252884aeSStefan Eßer 833252884aeSStefan Eßer if (BC_NUM_NONZERO(&h1) && BC_NUM_NONZERO(&h2)) { 834252884aeSStefan Eßer 83550696a6eSStefan Eßer assert(BC_NUM_RDX_VALID_NP(h1)); 83650696a6eSStefan Eßer assert(BC_NUM_RDX_VALID_NP(h2)); 83750696a6eSStefan Eßer 838252884aeSStefan Eßer bc_num_m(&h1, &h2, &z2, 0); 839252884aeSStefan Eßer bc_num_clean(&z2); 840252884aeSStefan Eßer 841252884aeSStefan Eßer bc_num_shiftAddSub(c, &z2, max2 * 2, bc_num_addArrays); 842252884aeSStefan Eßer bc_num_shiftAddSub(c, &z2, max2, bc_num_addArrays); 843252884aeSStefan Eßer } 844252884aeSStefan Eßer 845252884aeSStefan Eßer if (BC_NUM_NONZERO(&l1) && BC_NUM_NONZERO(&l2)) { 846252884aeSStefan Eßer 84750696a6eSStefan Eßer assert(BC_NUM_RDX_VALID_NP(l1)); 84850696a6eSStefan Eßer assert(BC_NUM_RDX_VALID_NP(l2)); 84950696a6eSStefan Eßer 850252884aeSStefan Eßer bc_num_m(&l1, &l2, &z0, 0); 851252884aeSStefan Eßer bc_num_clean(&z0); 852252884aeSStefan Eßer 853252884aeSStefan Eßer bc_num_shiftAddSub(c, &z0, max2, bc_num_addArrays); 854252884aeSStefan Eßer bc_num_shiftAddSub(c, &z0, 0, bc_num_addArrays); 855252884aeSStefan Eßer } 856252884aeSStefan Eßer 857252884aeSStefan Eßer if (BC_NUM_NONZERO(&m1) && BC_NUM_NONZERO(&m2)) { 858252884aeSStefan Eßer 85950696a6eSStefan Eßer assert(BC_NUM_RDX_VALID_NP(m1)); 86050696a6eSStefan Eßer assert(BC_NUM_RDX_VALID_NP(m1)); 86150696a6eSStefan Eßer 862252884aeSStefan Eßer bc_num_m(&m1, &m2, &z1, 0); 863252884aeSStefan Eßer bc_num_clean(&z1); 864252884aeSStefan Eßer 86550696a6eSStefan Eßer op = (BC_NUM_NEG_NP(m1) != BC_NUM_NEG_NP(m2)) ? 86650696a6eSStefan Eßer bc_num_subArrays : bc_num_addArrays; 867252884aeSStefan Eßer bc_num_shiftAddSub(c, &z1, max2, op); 868252884aeSStefan Eßer } 869252884aeSStefan Eßer 870252884aeSStefan Eßer err: 871252884aeSStefan Eßer BC_SIG_MAYLOCK; 872252884aeSStefan Eßer free(digs); 873252884aeSStefan Eßer bc_num_free(&temp); 874252884aeSStefan Eßer bc_num_free(&z2); 875252884aeSStefan Eßer bc_num_free(&z1); 876252884aeSStefan Eßer bc_num_free(&z0); 877252884aeSStefan Eßer BC_LONGJMP_CONT; 878252884aeSStefan Eßer } 879252884aeSStefan Eßer 880252884aeSStefan Eßer static void bc_num_m(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale) { 881252884aeSStefan Eßer 882252884aeSStefan Eßer BcNum cpa, cpb; 883252884aeSStefan Eßer size_t ascale, bscale, ardx, brdx, azero = 0, bzero = 0, zero, len, rscale; 884252884aeSStefan Eßer 88550696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(a)); 88650696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(b)); 88750696a6eSStefan Eßer 888252884aeSStefan Eßer bc_num_zero(c); 889252884aeSStefan Eßer ascale = a->scale; 890252884aeSStefan Eßer bscale = b->scale; 891252884aeSStefan Eßer scale = BC_MAX(scale, ascale); 892252884aeSStefan Eßer scale = BC_MAX(scale, bscale); 893252884aeSStefan Eßer 894252884aeSStefan Eßer rscale = ascale + bscale; 895252884aeSStefan Eßer scale = BC_MIN(rscale, scale); 896252884aeSStefan Eßer 897252884aeSStefan Eßer if ((a->len == 1 || b->len == 1) && !a->rdx && !b->rdx) { 898252884aeSStefan Eßer 899252884aeSStefan Eßer BcNum *operand; 900252884aeSStefan Eßer BcBigDig dig; 901252884aeSStefan Eßer 902252884aeSStefan Eßer if (a->len == 1) { 903252884aeSStefan Eßer dig = (BcBigDig) a->num[0]; 904252884aeSStefan Eßer operand = b; 905252884aeSStefan Eßer } 906252884aeSStefan Eßer else { 907252884aeSStefan Eßer dig = (BcBigDig) b->num[0]; 908252884aeSStefan Eßer operand = a; 909252884aeSStefan Eßer } 910252884aeSStefan Eßer 911252884aeSStefan Eßer bc_num_mulArray(operand, dig, c); 912252884aeSStefan Eßer 91350696a6eSStefan Eßer if (BC_NUM_NONZERO(c)) 91450696a6eSStefan Eßer c->rdx = BC_NUM_NEG_VAL(c, BC_NUM_NEG(a) != BC_NUM_NEG(b)); 915252884aeSStefan Eßer 916252884aeSStefan Eßer return; 917252884aeSStefan Eßer } 918252884aeSStefan Eßer 91950696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(a)); 92050696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(b)); 92150696a6eSStefan Eßer 922252884aeSStefan Eßer BC_SIG_LOCK; 923252884aeSStefan Eßer 92450696a6eSStefan Eßer bc_num_init(&cpa, a->len + BC_NUM_RDX_VAL(a)); 92550696a6eSStefan Eßer bc_num_init(&cpb, b->len + BC_NUM_RDX_VAL(b)); 926252884aeSStefan Eßer 927252884aeSStefan Eßer BC_SETJMP_LOCKED(err); 928252884aeSStefan Eßer 929252884aeSStefan Eßer BC_SIG_UNLOCK; 930252884aeSStefan Eßer 931252884aeSStefan Eßer bc_num_copy(&cpa, a); 932252884aeSStefan Eßer bc_num_copy(&cpb, b); 933252884aeSStefan Eßer 93450696a6eSStefan Eßer assert(BC_NUM_RDX_VALID_NP(cpa)); 93550696a6eSStefan Eßer assert(BC_NUM_RDX_VALID_NP(cpb)); 936252884aeSStefan Eßer 93750696a6eSStefan Eßer BC_NUM_NEG_CLR_NP(cpa); 93850696a6eSStefan Eßer BC_NUM_NEG_CLR_NP(cpb); 93950696a6eSStefan Eßer 94050696a6eSStefan Eßer assert(BC_NUM_RDX_VALID_NP(cpa)); 94150696a6eSStefan Eßer assert(BC_NUM_RDX_VALID_NP(cpb)); 94250696a6eSStefan Eßer 94350696a6eSStefan Eßer ardx = BC_NUM_RDX_VAL_NP(cpa) * BC_BASE_DIGS; 944252884aeSStefan Eßer bc_num_shiftLeft(&cpa, ardx); 945252884aeSStefan Eßer 94650696a6eSStefan Eßer brdx = BC_NUM_RDX_VAL_NP(cpb) * BC_BASE_DIGS; 947252884aeSStefan Eßer bc_num_shiftLeft(&cpb, brdx); 948252884aeSStefan Eßer 949252884aeSStefan Eßer // We need to reset the jump here because azero and bzero are used in the 950252884aeSStefan Eßer // cleanup, and local variables are not guaranteed to be the same after a 951252884aeSStefan Eßer // jump. 952252884aeSStefan Eßer BC_SIG_LOCK; 953252884aeSStefan Eßer 954252884aeSStefan Eßer BC_UNSETJMP; 955252884aeSStefan Eßer 956252884aeSStefan Eßer azero = bc_num_shiftZero(&cpa); 957252884aeSStefan Eßer bzero = bc_num_shiftZero(&cpb); 958252884aeSStefan Eßer 959252884aeSStefan Eßer BC_SETJMP_LOCKED(err); 960252884aeSStefan Eßer 961252884aeSStefan Eßer BC_SIG_UNLOCK; 962252884aeSStefan Eßer 963252884aeSStefan Eßer bc_num_clean(&cpa); 964252884aeSStefan Eßer bc_num_clean(&cpb); 965252884aeSStefan Eßer 966252884aeSStefan Eßer bc_num_k(&cpa, &cpb, c); 967252884aeSStefan Eßer 968252884aeSStefan Eßer zero = bc_vm_growSize(azero, bzero); 969252884aeSStefan Eßer len = bc_vm_growSize(c->len, zero); 970252884aeSStefan Eßer 971252884aeSStefan Eßer bc_num_expand(c, len); 972252884aeSStefan Eßer bc_num_shiftLeft(c, (len - c->len) * BC_BASE_DIGS); 973252884aeSStefan Eßer bc_num_shiftRight(c, ardx + brdx); 974252884aeSStefan Eßer 97550696a6eSStefan Eßer bc_num_retireMul(c, scale, BC_NUM_NEG(a), BC_NUM_NEG(b)); 976252884aeSStefan Eßer 977252884aeSStefan Eßer err: 978252884aeSStefan Eßer BC_SIG_MAYLOCK; 979252884aeSStefan Eßer bc_num_unshiftZero(&cpb, bzero); 980252884aeSStefan Eßer bc_num_free(&cpb); 981252884aeSStefan Eßer bc_num_unshiftZero(&cpa, azero); 982252884aeSStefan Eßer bc_num_free(&cpa); 983252884aeSStefan Eßer BC_LONGJMP_CONT; 984252884aeSStefan Eßer } 985252884aeSStefan Eßer 986252884aeSStefan Eßer static bool bc_num_nonZeroDig(BcDig *restrict a, size_t len) { 987252884aeSStefan Eßer size_t i; 988252884aeSStefan Eßer bool nonzero = false; 989252884aeSStefan Eßer for (i = len - 1; !nonzero && i < len; --i) nonzero = (a[i] != 0); 990252884aeSStefan Eßer return nonzero; 991252884aeSStefan Eßer } 992252884aeSStefan Eßer 993252884aeSStefan Eßer static ssize_t bc_num_divCmp(const BcDig *a, const BcNum *b, size_t len) { 994252884aeSStefan Eßer 995252884aeSStefan Eßer ssize_t cmp; 996252884aeSStefan Eßer 997252884aeSStefan Eßer if (b->len > len && a[len]) cmp = bc_num_compare(a, b->num, len + 1); 998252884aeSStefan Eßer else if (b->len <= len) { 999252884aeSStefan Eßer if (a[len]) cmp = 1; 1000252884aeSStefan Eßer else cmp = bc_num_compare(a, b->num, len); 1001252884aeSStefan Eßer } 1002252884aeSStefan Eßer else cmp = -1; 1003252884aeSStefan Eßer 1004252884aeSStefan Eßer return cmp; 1005252884aeSStefan Eßer } 1006252884aeSStefan Eßer 1007252884aeSStefan Eßer static void bc_num_divExtend(BcNum *restrict a, BcNum *restrict b, 1008252884aeSStefan Eßer BcBigDig divisor) 1009252884aeSStefan Eßer { 1010252884aeSStefan Eßer size_t pow; 1011252884aeSStefan Eßer 1012252884aeSStefan Eßer assert(divisor < BC_BASE_POW); 1013252884aeSStefan Eßer 1014252884aeSStefan Eßer pow = BC_BASE_DIGS - bc_num_log10((size_t) divisor); 1015252884aeSStefan Eßer 1016252884aeSStefan Eßer bc_num_shiftLeft(a, pow); 1017252884aeSStefan Eßer bc_num_shiftLeft(b, pow); 1018252884aeSStefan Eßer } 1019252884aeSStefan Eßer 1020252884aeSStefan Eßer static void bc_num_d_long(BcNum *restrict a, BcNum *restrict b, 1021252884aeSStefan Eßer BcNum *restrict c, size_t scale) 1022252884aeSStefan Eßer { 1023252884aeSStefan Eßer BcBigDig divisor; 1024252884aeSStefan Eßer size_t len, end, i, rdx; 1025252884aeSStefan Eßer BcNum cpb; 1026252884aeSStefan Eßer bool nonzero = false; 1027252884aeSStefan Eßer 1028252884aeSStefan Eßer assert(b->len < a->len); 1029252884aeSStefan Eßer len = b->len; 1030252884aeSStefan Eßer end = a->len - len; 1031252884aeSStefan Eßer assert(len >= 1); 1032252884aeSStefan Eßer 1033252884aeSStefan Eßer bc_num_expand(c, a->len); 1034252884aeSStefan Eßer memset(c->num, 0, c->cap * sizeof(BcDig)); 1035252884aeSStefan Eßer 103650696a6eSStefan Eßer BC_NUM_RDX_SET(c, BC_NUM_RDX_VAL(a)); 1037252884aeSStefan Eßer c->scale = a->scale; 1038252884aeSStefan Eßer c->len = a->len; 1039252884aeSStefan Eßer 1040252884aeSStefan Eßer divisor = (BcBigDig) b->num[len - 1]; 1041252884aeSStefan Eßer 1042252884aeSStefan Eßer if (len > 1 && bc_num_nonZeroDig(b->num, len - 1)) { 1043252884aeSStefan Eßer 1044252884aeSStefan Eßer nonzero = (divisor > 1 << ((10 * BC_BASE_DIGS) / 6 + 1)); 1045252884aeSStefan Eßer 1046252884aeSStefan Eßer if (!nonzero) { 1047252884aeSStefan Eßer 1048252884aeSStefan Eßer bc_num_divExtend(a, b, divisor); 1049252884aeSStefan Eßer 1050252884aeSStefan Eßer len = BC_MAX(a->len, b->len); 1051252884aeSStefan Eßer bc_num_expand(a, len + 1); 1052252884aeSStefan Eßer 1053252884aeSStefan Eßer if (len + 1 > a->len) a->len = len + 1; 1054252884aeSStefan Eßer 1055252884aeSStefan Eßer len = b->len; 1056252884aeSStefan Eßer end = a->len - len; 1057252884aeSStefan Eßer divisor = (BcBigDig) b->num[len - 1]; 1058252884aeSStefan Eßer 1059252884aeSStefan Eßer nonzero = bc_num_nonZeroDig(b->num, len - 1); 1060252884aeSStefan Eßer } 1061252884aeSStefan Eßer } 1062252884aeSStefan Eßer 1063252884aeSStefan Eßer divisor += nonzero; 1064252884aeSStefan Eßer 1065252884aeSStefan Eßer bc_num_expand(c, a->len); 1066252884aeSStefan Eßer memset(c->num, 0, BC_NUM_SIZE(c->cap)); 1067252884aeSStefan Eßer 1068252884aeSStefan Eßer assert(c->scale >= scale); 106950696a6eSStefan Eßer rdx = BC_NUM_RDX_VAL(c) - BC_NUM_RDX(scale); 1070252884aeSStefan Eßer 1071252884aeSStefan Eßer BC_SIG_LOCK; 1072252884aeSStefan Eßer 1073252884aeSStefan Eßer bc_num_init(&cpb, len + 1); 1074252884aeSStefan Eßer 1075252884aeSStefan Eßer BC_SETJMP_LOCKED(err); 1076252884aeSStefan Eßer 1077252884aeSStefan Eßer BC_SIG_UNLOCK; 1078252884aeSStefan Eßer 1079252884aeSStefan Eßer i = end - 1; 1080252884aeSStefan Eßer 1081252884aeSStefan Eßer for (; i < end && i >= rdx && BC_NUM_NONZERO(a); --i) { 1082252884aeSStefan Eßer 1083252884aeSStefan Eßer ssize_t cmp; 1084252884aeSStefan Eßer BcDig *n; 1085252884aeSStefan Eßer BcBigDig result; 1086252884aeSStefan Eßer 1087252884aeSStefan Eßer n = a->num + i; 1088252884aeSStefan Eßer assert(n >= a->num); 1089252884aeSStefan Eßer result = 0; 1090252884aeSStefan Eßer 1091252884aeSStefan Eßer cmp = bc_num_divCmp(n, b, len); 1092252884aeSStefan Eßer 1093252884aeSStefan Eßer while (cmp >= 0) { 1094252884aeSStefan Eßer 1095252884aeSStefan Eßer BcBigDig n1, dividend, q; 1096252884aeSStefan Eßer 1097252884aeSStefan Eßer n1 = (BcBigDig) n[len]; 1098252884aeSStefan Eßer dividend = n1 * BC_BASE_POW + (BcBigDig) n[len - 1]; 1099252884aeSStefan Eßer q = (dividend / divisor); 1100252884aeSStefan Eßer 1101252884aeSStefan Eßer if (q <= 1) { 1102252884aeSStefan Eßer q = 1; 1103252884aeSStefan Eßer bc_num_subArrays(n, b->num, len); 1104252884aeSStefan Eßer } 1105252884aeSStefan Eßer else { 1106252884aeSStefan Eßer 1107252884aeSStefan Eßer assert(q <= BC_BASE_POW); 1108252884aeSStefan Eßer 1109252884aeSStefan Eßer bc_num_mulArray(b, (BcBigDig) q, &cpb); 1110252884aeSStefan Eßer bc_num_subArrays(n, cpb.num, cpb.len); 1111252884aeSStefan Eßer } 1112252884aeSStefan Eßer 1113252884aeSStefan Eßer result += q; 1114252884aeSStefan Eßer assert(result <= BC_BASE_POW); 1115252884aeSStefan Eßer 1116252884aeSStefan Eßer if (nonzero) cmp = bc_num_divCmp(n, b, len); 1117252884aeSStefan Eßer else cmp = -1; 1118252884aeSStefan Eßer } 1119252884aeSStefan Eßer 1120252884aeSStefan Eßer assert(result < BC_BASE_POW); 1121252884aeSStefan Eßer 1122252884aeSStefan Eßer c->num[i] = (BcDig) result; 1123252884aeSStefan Eßer } 1124252884aeSStefan Eßer 1125252884aeSStefan Eßer err: 1126252884aeSStefan Eßer BC_SIG_MAYLOCK; 1127252884aeSStefan Eßer bc_num_free(&cpb); 1128252884aeSStefan Eßer BC_LONGJMP_CONT; 1129252884aeSStefan Eßer } 1130252884aeSStefan Eßer 1131252884aeSStefan Eßer static void bc_num_d(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale) { 1132252884aeSStefan Eßer 113350696a6eSStefan Eßer size_t len, cpardx; 1134252884aeSStefan Eßer BcNum cpa, cpb; 1135252884aeSStefan Eßer 113650696a6eSStefan Eßer if (BC_NUM_ZERO(b)) bc_vm_err(BC_ERR_MATH_DIVIDE_BY_ZERO); 1137252884aeSStefan Eßer if (BC_NUM_ZERO(a)) { 1138252884aeSStefan Eßer bc_num_setToZero(c, scale); 1139252884aeSStefan Eßer return; 1140252884aeSStefan Eßer } 1141252884aeSStefan Eßer if (BC_NUM_ONE(b)) { 1142252884aeSStefan Eßer bc_num_copy(c, a); 114350696a6eSStefan Eßer bc_num_retireMul(c, scale, BC_NUM_NEG(a), BC_NUM_NEG(b)); 1144252884aeSStefan Eßer return; 1145252884aeSStefan Eßer } 114650696a6eSStefan Eßer if (!BC_NUM_RDX_VAL(a) && !BC_NUM_RDX_VAL(b) && b->len == 1 && !scale) { 1147252884aeSStefan Eßer BcBigDig rem; 1148252884aeSStefan Eßer bc_num_divArray(a, (BcBigDig) b->num[0], c, &rem); 114950696a6eSStefan Eßer bc_num_retireMul(c, scale, BC_NUM_NEG(a), BC_NUM_NEG(b)); 1150252884aeSStefan Eßer return; 1151252884aeSStefan Eßer } 1152252884aeSStefan Eßer 115350696a6eSStefan Eßer len = bc_num_divReq(a, b, scale); 1154252884aeSStefan Eßer 1155252884aeSStefan Eßer BC_SIG_LOCK; 1156252884aeSStefan Eßer 1157252884aeSStefan Eßer bc_num_init(&cpa, len); 1158252884aeSStefan Eßer bc_num_copy(&cpa, a); 1159252884aeSStefan Eßer bc_num_createCopy(&cpb, b); 1160252884aeSStefan Eßer 1161252884aeSStefan Eßer BC_SETJMP_LOCKED(err); 1162252884aeSStefan Eßer 1163252884aeSStefan Eßer BC_SIG_UNLOCK; 1164252884aeSStefan Eßer 1165252884aeSStefan Eßer len = b->len; 1166252884aeSStefan Eßer 1167252884aeSStefan Eßer if (len > cpa.len) { 1168252884aeSStefan Eßer bc_num_expand(&cpa, bc_vm_growSize(len, 2)); 1169252884aeSStefan Eßer bc_num_extend(&cpa, (len - cpa.len) * BC_BASE_DIGS); 1170252884aeSStefan Eßer } 1171252884aeSStefan Eßer 117250696a6eSStefan Eßer cpardx = BC_NUM_RDX_VAL_NP(cpa); 117350696a6eSStefan Eßer cpa.scale = cpardx * BC_BASE_DIGS; 1174252884aeSStefan Eßer 1175252884aeSStefan Eßer bc_num_extend(&cpa, b->scale); 117650696a6eSStefan Eßer cpardx = BC_NUM_RDX_VAL_NP(cpa) - BC_NUM_RDX(b->scale); 117750696a6eSStefan Eßer BC_NUM_RDX_SET_NP(cpa, cpardx); 117850696a6eSStefan Eßer cpa.scale = cpardx * BC_BASE_DIGS; 1179252884aeSStefan Eßer 1180252884aeSStefan Eßer if (scale > cpa.scale) { 1181252884aeSStefan Eßer bc_num_extend(&cpa, scale); 118250696a6eSStefan Eßer cpardx = BC_NUM_RDX_VAL_NP(cpa); 118350696a6eSStefan Eßer cpa.scale = cpardx * BC_BASE_DIGS; 1184252884aeSStefan Eßer } 1185252884aeSStefan Eßer 1186252884aeSStefan Eßer if (cpa.cap == cpa.len) bc_num_expand(&cpa, bc_vm_growSize(cpa.len, 1)); 1187252884aeSStefan Eßer 1188252884aeSStefan Eßer // We want an extra zero in front to make things simpler. 1189252884aeSStefan Eßer cpa.num[cpa.len++] = 0; 1190252884aeSStefan Eßer 119150696a6eSStefan Eßer if (cpardx == cpa.len) cpa.len = bc_num_nonzeroLen(&cpa); 119250696a6eSStefan Eßer if (BC_NUM_RDX_VAL_NP(cpb) == cpb.len) cpb.len = bc_num_nonzeroLen(&cpb); 119350696a6eSStefan Eßer cpb.scale = 0; 119450696a6eSStefan Eßer BC_NUM_RDX_SET_NP(cpb, 0); 1195252884aeSStefan Eßer 1196252884aeSStefan Eßer bc_num_d_long(&cpa, &cpb, c, scale); 1197252884aeSStefan Eßer 119850696a6eSStefan Eßer bc_num_retireMul(c, scale, BC_NUM_NEG(a), BC_NUM_NEG(b)); 1199252884aeSStefan Eßer 1200252884aeSStefan Eßer err: 1201252884aeSStefan Eßer BC_SIG_MAYLOCK; 1202252884aeSStefan Eßer bc_num_free(&cpb); 1203252884aeSStefan Eßer bc_num_free(&cpa); 1204252884aeSStefan Eßer BC_LONGJMP_CONT; 1205252884aeSStefan Eßer } 1206252884aeSStefan Eßer 1207252884aeSStefan Eßer static void bc_num_r(BcNum *a, BcNum *b, BcNum *restrict c, 1208252884aeSStefan Eßer BcNum *restrict d, size_t scale, size_t ts) 1209252884aeSStefan Eßer { 1210252884aeSStefan Eßer BcNum temp; 1211252884aeSStefan Eßer bool neg; 1212252884aeSStefan Eßer 121350696a6eSStefan Eßer if (BC_NUM_ZERO(b)) bc_vm_err(BC_ERR_MATH_DIVIDE_BY_ZERO); 1214252884aeSStefan Eßer if (BC_NUM_ZERO(a)) { 1215252884aeSStefan Eßer bc_num_setToZero(c, ts); 1216252884aeSStefan Eßer bc_num_setToZero(d, ts); 1217252884aeSStefan Eßer return; 1218252884aeSStefan Eßer } 1219252884aeSStefan Eßer 1220252884aeSStefan Eßer BC_SIG_LOCK; 1221252884aeSStefan Eßer 1222252884aeSStefan Eßer bc_num_init(&temp, d->cap); 1223252884aeSStefan Eßer 1224252884aeSStefan Eßer BC_SETJMP_LOCKED(err); 1225252884aeSStefan Eßer 1226252884aeSStefan Eßer BC_SIG_UNLOCK; 1227252884aeSStefan Eßer 1228252884aeSStefan Eßer bc_num_d(a, b, c, scale); 1229252884aeSStefan Eßer 1230252884aeSStefan Eßer if (scale) scale = ts + 1; 1231252884aeSStefan Eßer 123250696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(c)); 123350696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(b)); 123450696a6eSStefan Eßer 1235252884aeSStefan Eßer bc_num_m(c, b, &temp, scale); 1236252884aeSStefan Eßer bc_num_sub(a, &temp, d, scale); 1237252884aeSStefan Eßer 1238252884aeSStefan Eßer if (ts > d->scale && BC_NUM_NONZERO(d)) bc_num_extend(d, ts - d->scale); 1239252884aeSStefan Eßer 124050696a6eSStefan Eßer neg = BC_NUM_NEG(d); 124150696a6eSStefan Eßer bc_num_retireMul(d, ts, BC_NUM_NEG(a), BC_NUM_NEG(b)); 124250696a6eSStefan Eßer d->rdx = BC_NUM_NEG_VAL(d, BC_NUM_NONZERO(d) ? neg : false); 1243252884aeSStefan Eßer 1244252884aeSStefan Eßer err: 1245252884aeSStefan Eßer BC_SIG_MAYLOCK; 1246252884aeSStefan Eßer bc_num_free(&temp); 1247252884aeSStefan Eßer BC_LONGJMP_CONT; 1248252884aeSStefan Eßer } 1249252884aeSStefan Eßer 1250252884aeSStefan Eßer static void bc_num_rem(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale) { 1251252884aeSStefan Eßer 1252252884aeSStefan Eßer BcNum c1; 1253252884aeSStefan Eßer size_t ts; 1254252884aeSStefan Eßer 1255252884aeSStefan Eßer ts = bc_vm_growSize(scale, b->scale); 1256252884aeSStefan Eßer ts = BC_MAX(ts, a->scale); 1257252884aeSStefan Eßer 1258252884aeSStefan Eßer BC_SIG_LOCK; 1259252884aeSStefan Eßer 1260252884aeSStefan Eßer bc_num_init(&c1, bc_num_mulReq(a, b, ts)); 1261252884aeSStefan Eßer 1262252884aeSStefan Eßer BC_SETJMP_LOCKED(err); 1263252884aeSStefan Eßer 1264252884aeSStefan Eßer BC_SIG_UNLOCK; 1265252884aeSStefan Eßer 1266252884aeSStefan Eßer bc_num_r(a, b, &c1, c, scale, ts); 1267252884aeSStefan Eßer 1268252884aeSStefan Eßer err: 1269252884aeSStefan Eßer BC_SIG_MAYLOCK; 1270252884aeSStefan Eßer bc_num_free(&c1); 1271252884aeSStefan Eßer BC_LONGJMP_CONT; 1272252884aeSStefan Eßer } 1273252884aeSStefan Eßer 1274252884aeSStefan Eßer static void bc_num_p(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale) { 1275252884aeSStefan Eßer 1276252884aeSStefan Eßer BcNum copy; 1277252884aeSStefan Eßer BcBigDig pow = 0; 1278252884aeSStefan Eßer size_t i, powrdx, resrdx; 1279252884aeSStefan Eßer bool neg, zero; 1280252884aeSStefan Eßer 128150696a6eSStefan Eßer if (BC_ERR(BC_NUM_RDX_VAL(b))) bc_vm_err(BC_ERR_MATH_NON_INTEGER); 1282252884aeSStefan Eßer 1283252884aeSStefan Eßer if (BC_NUM_ZERO(b)) { 1284252884aeSStefan Eßer bc_num_one(c); 1285252884aeSStefan Eßer return; 1286252884aeSStefan Eßer } 1287252884aeSStefan Eßer if (BC_NUM_ZERO(a)) { 128850696a6eSStefan Eßer if (BC_NUM_NEG(b)) bc_vm_err(BC_ERR_MATH_DIVIDE_BY_ZERO); 1289252884aeSStefan Eßer bc_num_setToZero(c, scale); 1290252884aeSStefan Eßer return; 1291252884aeSStefan Eßer } 1292252884aeSStefan Eßer if (BC_NUM_ONE(b)) { 129350696a6eSStefan Eßer if (!BC_NUM_NEG(b)) bc_num_copy(c, a); 1294252884aeSStefan Eßer else bc_num_inv(a, c, scale); 1295252884aeSStefan Eßer return; 1296252884aeSStefan Eßer } 1297252884aeSStefan Eßer 1298252884aeSStefan Eßer BC_SIG_LOCK; 1299252884aeSStefan Eßer 130050696a6eSStefan Eßer neg = BC_NUM_NEG(b); 130150696a6eSStefan Eßer BC_NUM_NEG_CLR(b); 1302252884aeSStefan Eßer bc_num_bigdig(b, &pow); 130350696a6eSStefan Eßer b->rdx = BC_NUM_NEG_VAL(b, neg); 1304252884aeSStefan Eßer 1305252884aeSStefan Eßer bc_num_createCopy(©, a); 1306252884aeSStefan Eßer 1307252884aeSStefan Eßer BC_SETJMP_LOCKED(err); 1308252884aeSStefan Eßer 1309252884aeSStefan Eßer BC_SIG_UNLOCK; 1310252884aeSStefan Eßer 1311252884aeSStefan Eßer if (!neg) { 1312252884aeSStefan Eßer size_t max = BC_MAX(scale, a->scale), scalepow = a->scale * pow; 1313252884aeSStefan Eßer scale = BC_MIN(scalepow, max); 1314252884aeSStefan Eßer } 1315252884aeSStefan Eßer 1316252884aeSStefan Eßer for (powrdx = a->scale; !(pow & 1); pow >>= 1) { 1317252884aeSStefan Eßer powrdx <<= 1; 131850696a6eSStefan Eßer assert(BC_NUM_RDX_VALID_NP(copy)); 1319252884aeSStefan Eßer bc_num_mul(©, ©, ©, powrdx); 1320252884aeSStefan Eßer } 1321252884aeSStefan Eßer 1322252884aeSStefan Eßer bc_num_copy(c, ©); 1323252884aeSStefan Eßer resrdx = powrdx; 1324252884aeSStefan Eßer 1325252884aeSStefan Eßer while (pow >>= 1) { 1326252884aeSStefan Eßer 1327252884aeSStefan Eßer powrdx <<= 1; 132850696a6eSStefan Eßer assert(BC_NUM_RDX_VALID_NP(copy)); 1329252884aeSStefan Eßer bc_num_mul(©, ©, ©, powrdx); 1330252884aeSStefan Eßer 1331252884aeSStefan Eßer if (pow & 1) { 1332252884aeSStefan Eßer resrdx += powrdx; 133350696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(c)); 133450696a6eSStefan Eßer assert(BC_NUM_RDX_VALID_NP(copy)); 1335252884aeSStefan Eßer bc_num_mul(c, ©, c, resrdx); 1336252884aeSStefan Eßer } 1337252884aeSStefan Eßer } 1338252884aeSStefan Eßer 1339252884aeSStefan Eßer if (neg) bc_num_inv(c, c, scale); 1340252884aeSStefan Eßer 1341252884aeSStefan Eßer if (c->scale > scale) bc_num_truncate(c, c->scale - scale); 1342252884aeSStefan Eßer 1343252884aeSStefan Eßer // We can't use bc_num_clean() here. 1344252884aeSStefan Eßer for (zero = true, i = 0; zero && i < c->len; ++i) zero = !c->num[i]; 1345252884aeSStefan Eßer if (zero) bc_num_setToZero(c, scale); 1346252884aeSStefan Eßer 1347252884aeSStefan Eßer err: 1348252884aeSStefan Eßer BC_SIG_MAYLOCK; 1349252884aeSStefan Eßer bc_num_free(©); 1350252884aeSStefan Eßer BC_LONGJMP_CONT; 1351252884aeSStefan Eßer } 1352252884aeSStefan Eßer 1353252884aeSStefan Eßer #if BC_ENABLE_EXTRA_MATH 1354252884aeSStefan Eßer static void bc_num_place(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale) { 1355252884aeSStefan Eßer 1356252884aeSStefan Eßer BcBigDig val = 0; 1357252884aeSStefan Eßer 1358252884aeSStefan Eßer BC_UNUSED(scale); 1359252884aeSStefan Eßer 1360252884aeSStefan Eßer bc_num_intop(a, b, c, &val); 1361252884aeSStefan Eßer 1362252884aeSStefan Eßer if (val < c->scale) bc_num_truncate(c, c->scale - val); 1363252884aeSStefan Eßer else if (val > c->scale) bc_num_extend(c, val - c->scale); 1364252884aeSStefan Eßer } 1365252884aeSStefan Eßer 1366252884aeSStefan Eßer static void bc_num_left(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale) { 1367252884aeSStefan Eßer 1368252884aeSStefan Eßer BcBigDig val = 0; 1369252884aeSStefan Eßer 1370252884aeSStefan Eßer BC_UNUSED(scale); 1371252884aeSStefan Eßer 1372252884aeSStefan Eßer bc_num_intop(a, b, c, &val); 1373252884aeSStefan Eßer 1374252884aeSStefan Eßer bc_num_shiftLeft(c, (size_t) val); 1375252884aeSStefan Eßer } 1376252884aeSStefan Eßer 1377252884aeSStefan Eßer static void bc_num_right(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale) { 1378252884aeSStefan Eßer 1379252884aeSStefan Eßer BcBigDig val = 0; 1380252884aeSStefan Eßer 1381252884aeSStefan Eßer BC_UNUSED(scale); 1382252884aeSStefan Eßer 1383252884aeSStefan Eßer bc_num_intop(a, b, c, &val); 1384252884aeSStefan Eßer 1385252884aeSStefan Eßer if (BC_NUM_ZERO(c)) return; 1386252884aeSStefan Eßer 1387252884aeSStefan Eßer bc_num_shiftRight(c, (size_t) val); 1388252884aeSStefan Eßer } 1389252884aeSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH 1390252884aeSStefan Eßer 1391252884aeSStefan Eßer static void bc_num_binary(BcNum *a, BcNum *b, BcNum *c, size_t scale, 1392*7e5c51e5SStefan Eßer BcNumBinOp op, size_t req) 1393252884aeSStefan Eßer { 139450696a6eSStefan Eßer BcNum *ptr_a, *ptr_b, num2; 1395252884aeSStefan Eßer bool init = false; 1396252884aeSStefan Eßer 1397252884aeSStefan Eßer assert(a != NULL && b != NULL && c != NULL && op != NULL); 1398252884aeSStefan Eßer 139950696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(a)); 140050696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(b)); 140150696a6eSStefan Eßer 1402252884aeSStefan Eßer BC_SIG_LOCK; 1403252884aeSStefan Eßer 1404252884aeSStefan Eßer if (c == a) { 1405252884aeSStefan Eßer 1406252884aeSStefan Eßer ptr_a = &num2; 1407252884aeSStefan Eßer 1408252884aeSStefan Eßer memcpy(ptr_a, c, sizeof(BcNum)); 1409252884aeSStefan Eßer init = true; 1410252884aeSStefan Eßer } 141150696a6eSStefan Eßer else { 141250696a6eSStefan Eßer ptr_a = a; 141350696a6eSStefan Eßer } 1414252884aeSStefan Eßer 1415252884aeSStefan Eßer if (c == b) { 1416252884aeSStefan Eßer 1417252884aeSStefan Eßer ptr_b = &num2; 1418252884aeSStefan Eßer 1419252884aeSStefan Eßer if (c != a) { 1420252884aeSStefan Eßer memcpy(ptr_b, c, sizeof(BcNum)); 1421252884aeSStefan Eßer init = true; 1422252884aeSStefan Eßer } 1423252884aeSStefan Eßer } 142450696a6eSStefan Eßer else { 142550696a6eSStefan Eßer ptr_b = b; 142650696a6eSStefan Eßer } 1427252884aeSStefan Eßer 1428252884aeSStefan Eßer if (init) { 1429252884aeSStefan Eßer 1430252884aeSStefan Eßer bc_num_init(c, req); 1431252884aeSStefan Eßer 1432252884aeSStefan Eßer BC_SETJMP_LOCKED(err); 1433252884aeSStefan Eßer BC_SIG_UNLOCK; 1434252884aeSStefan Eßer } 1435252884aeSStefan Eßer else { 1436252884aeSStefan Eßer BC_SIG_UNLOCK; 1437252884aeSStefan Eßer bc_num_expand(c, req); 1438252884aeSStefan Eßer } 1439252884aeSStefan Eßer 1440252884aeSStefan Eßer op(ptr_a, ptr_b, c, scale); 1441252884aeSStefan Eßer 144250696a6eSStefan Eßer assert(!BC_NUM_NEG(c) || BC_NUM_NONZERO(c)); 144350696a6eSStefan Eßer assert(BC_NUM_RDX_VAL(c) <= c->len || !c->len); 144450696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(c)); 144550696a6eSStefan Eßer assert(!c->len || c->num[c->len - 1] || BC_NUM_RDX_VAL(c) == c->len); 1446252884aeSStefan Eßer 1447252884aeSStefan Eßer err: 1448252884aeSStefan Eßer if (init) { 1449252884aeSStefan Eßer BC_SIG_MAYLOCK; 1450252884aeSStefan Eßer bc_num_free(&num2); 1451252884aeSStefan Eßer BC_LONGJMP_CONT; 1452252884aeSStefan Eßer } 1453252884aeSStefan Eßer } 1454252884aeSStefan Eßer 145550696a6eSStefan Eßer #if !defined(NDEBUG) || BC_ENABLE_LIBRARY 145650696a6eSStefan Eßer bool bc_num_strValid(const char *restrict val) { 1457252884aeSStefan Eßer 1458252884aeSStefan Eßer bool radix = false; 1459252884aeSStefan Eßer size_t i, len = strlen(val); 1460252884aeSStefan Eßer 1461252884aeSStefan Eßer if (!len) return true; 1462252884aeSStefan Eßer 1463252884aeSStefan Eßer for (i = 0; i < len; ++i) { 1464252884aeSStefan Eßer 1465252884aeSStefan Eßer BcDig c = val[i]; 1466252884aeSStefan Eßer 1467252884aeSStefan Eßer if (c == '.') { 1468252884aeSStefan Eßer 1469252884aeSStefan Eßer if (radix) return false; 1470252884aeSStefan Eßer 1471252884aeSStefan Eßer radix = true; 1472252884aeSStefan Eßer continue; 1473252884aeSStefan Eßer } 1474252884aeSStefan Eßer 1475252884aeSStefan Eßer if (!(isdigit(c) || isupper(c))) return false; 1476252884aeSStefan Eßer } 1477252884aeSStefan Eßer 1478252884aeSStefan Eßer return true; 1479252884aeSStefan Eßer } 148050696a6eSStefan Eßer #endif // !defined(NDEBUG) || BC_ENABLE_LIBRARY 1481252884aeSStefan Eßer 1482252884aeSStefan Eßer static BcBigDig bc_num_parseChar(char c, size_t base_t) { 1483252884aeSStefan Eßer 1484252884aeSStefan Eßer if (isupper(c)) { 1485252884aeSStefan Eßer c = BC_NUM_NUM_LETTER(c); 1486252884aeSStefan Eßer c = ((size_t) c) >= base_t ? (char) base_t - 1 : c; 1487252884aeSStefan Eßer } 1488252884aeSStefan Eßer else c -= '0'; 1489252884aeSStefan Eßer 1490252884aeSStefan Eßer return (BcBigDig) (uchar) c; 1491252884aeSStefan Eßer } 1492252884aeSStefan Eßer 1493252884aeSStefan Eßer static void bc_num_parseDecimal(BcNum *restrict n, const char *restrict val) { 1494252884aeSStefan Eßer 1495252884aeSStefan Eßer size_t len, i, temp, mod; 1496252884aeSStefan Eßer const char *ptr; 1497252884aeSStefan Eßer bool zero = true, rdx; 1498252884aeSStefan Eßer 1499252884aeSStefan Eßer for (i = 0; val[i] == '0'; ++i); 1500252884aeSStefan Eßer 1501252884aeSStefan Eßer val += i; 1502252884aeSStefan Eßer assert(!val[0] || isalnum(val[0]) || val[0] == '.'); 1503252884aeSStefan Eßer 1504252884aeSStefan Eßer // All 0's. We can just return, since this 1505252884aeSStefan Eßer // procedure expects a virgin (already 0) BcNum. 1506252884aeSStefan Eßer if (!val[0]) return; 1507252884aeSStefan Eßer 1508252884aeSStefan Eßer len = strlen(val); 1509252884aeSStefan Eßer 1510252884aeSStefan Eßer ptr = strchr(val, '.'); 1511252884aeSStefan Eßer rdx = (ptr != NULL); 1512252884aeSStefan Eßer 1513252884aeSStefan Eßer for (i = 0; i < len && (zero = (val[i] == '0' || val[i] == '.')); ++i); 1514252884aeSStefan Eßer 1515d213476dSStefan Eßer n->scale = (size_t) (rdx * (((uintptr_t) (val + len)) - 1516d213476dSStefan Eßer (((uintptr_t) ptr) + 1))); 1517252884aeSStefan Eßer 151850696a6eSStefan Eßer BC_NUM_RDX_SET(n, BC_NUM_RDX(n->scale)); 1519252884aeSStefan Eßer i = len - (ptr == val ? 0 : i) - rdx; 1520252884aeSStefan Eßer temp = BC_NUM_ROUND_POW(i); 1521252884aeSStefan Eßer mod = n->scale % BC_BASE_DIGS; 1522252884aeSStefan Eßer i = mod ? BC_BASE_DIGS - mod : 0; 1523252884aeSStefan Eßer n->len = ((temp + i) / BC_BASE_DIGS); 1524252884aeSStefan Eßer 1525252884aeSStefan Eßer bc_num_expand(n, n->len); 1526252884aeSStefan Eßer memset(n->num, 0, BC_NUM_SIZE(n->len)); 1527252884aeSStefan Eßer 152850696a6eSStefan Eßer if (zero) { 152950696a6eSStefan Eßer // I think I can set rdx directly to zero here because n should be a 153050696a6eSStefan Eßer // new number with sign set to false. 153150696a6eSStefan Eßer n->len = n->rdx = 0; 153250696a6eSStefan Eßer } 1533252884aeSStefan Eßer else { 1534252884aeSStefan Eßer 1535252884aeSStefan Eßer BcBigDig exp, pow; 1536252884aeSStefan Eßer 1537252884aeSStefan Eßer assert(i <= BC_NUM_BIGDIG_MAX); 1538252884aeSStefan Eßer 1539252884aeSStefan Eßer exp = (BcBigDig) i; 1540252884aeSStefan Eßer pow = bc_num_pow10[exp]; 1541252884aeSStefan Eßer 1542252884aeSStefan Eßer for (i = len - 1; i < len; --i, ++exp) { 1543252884aeSStefan Eßer 1544252884aeSStefan Eßer char c = val[i]; 1545252884aeSStefan Eßer 1546252884aeSStefan Eßer if (c == '.') exp -= 1; 1547252884aeSStefan Eßer else { 1548252884aeSStefan Eßer 1549252884aeSStefan Eßer size_t idx = exp / BC_BASE_DIGS; 1550252884aeSStefan Eßer 1551252884aeSStefan Eßer if (isupper(c)) c = '9'; 1552252884aeSStefan Eßer n->num[idx] += (((BcBigDig) c) - '0') * pow; 1553252884aeSStefan Eßer 1554252884aeSStefan Eßer if ((exp + 1) % BC_BASE_DIGS == 0) pow = 1; 1555252884aeSStefan Eßer else pow *= BC_BASE; 1556252884aeSStefan Eßer } 1557252884aeSStefan Eßer } 1558252884aeSStefan Eßer } 1559252884aeSStefan Eßer } 1560252884aeSStefan Eßer 1561252884aeSStefan Eßer static void bc_num_parseBase(BcNum *restrict n, const char *restrict val, 1562252884aeSStefan Eßer BcBigDig base) 1563252884aeSStefan Eßer { 1564252884aeSStefan Eßer BcNum temp, mult1, mult2, result1, result2, *m1, *m2, *ptr; 1565252884aeSStefan Eßer char c = 0; 1566252884aeSStefan Eßer bool zero = true; 1567252884aeSStefan Eßer BcBigDig v; 1568252884aeSStefan Eßer size_t i, digs, len = strlen(val); 1569252884aeSStefan Eßer 1570252884aeSStefan Eßer for (i = 0; zero && i < len; ++i) zero = (val[i] == '.' || val[i] == '0'); 1571252884aeSStefan Eßer if (zero) return; 1572252884aeSStefan Eßer 1573252884aeSStefan Eßer BC_SIG_LOCK; 1574252884aeSStefan Eßer 1575252884aeSStefan Eßer bc_num_init(&temp, BC_NUM_BIGDIG_LOG10); 1576252884aeSStefan Eßer bc_num_init(&mult1, BC_NUM_BIGDIG_LOG10); 1577252884aeSStefan Eßer 1578252884aeSStefan Eßer BC_SETJMP_LOCKED(int_err); 1579252884aeSStefan Eßer 1580252884aeSStefan Eßer BC_SIG_UNLOCK; 1581252884aeSStefan Eßer 1582252884aeSStefan Eßer for (i = 0; i < len && (c = val[i]) && c != '.'; ++i) { 1583252884aeSStefan Eßer 1584252884aeSStefan Eßer v = bc_num_parseChar(c, base); 1585252884aeSStefan Eßer 1586252884aeSStefan Eßer bc_num_mulArray(n, base, &mult1); 1587252884aeSStefan Eßer bc_num_bigdig2num(&temp, v); 1588252884aeSStefan Eßer bc_num_add(&mult1, &temp, n, 0); 1589252884aeSStefan Eßer } 1590252884aeSStefan Eßer 159110328f8bSStefan Eßer if (i == len && !val[i]) goto int_err; 1592252884aeSStefan Eßer 159310328f8bSStefan Eßer assert(val[i] == '.'); 1594252884aeSStefan Eßer 1595252884aeSStefan Eßer BC_SIG_LOCK; 1596252884aeSStefan Eßer 1597252884aeSStefan Eßer BC_UNSETJMP; 1598252884aeSStefan Eßer 1599252884aeSStefan Eßer bc_num_init(&mult2, BC_NUM_BIGDIG_LOG10); 1600252884aeSStefan Eßer bc_num_init(&result1, BC_NUM_DEF_SIZE); 1601252884aeSStefan Eßer bc_num_init(&result2, BC_NUM_DEF_SIZE); 1602252884aeSStefan Eßer bc_num_one(&mult1); 1603252884aeSStefan Eßer 1604252884aeSStefan Eßer BC_SETJMP_LOCKED(err); 1605252884aeSStefan Eßer 1606252884aeSStefan Eßer BC_SIG_UNLOCK; 1607252884aeSStefan Eßer 1608252884aeSStefan Eßer m1 = &mult1; 1609252884aeSStefan Eßer m2 = &mult2; 1610252884aeSStefan Eßer 1611252884aeSStefan Eßer for (i += 1, digs = 0; i < len && (c = val[i]); ++i, ++digs) { 1612252884aeSStefan Eßer 161350696a6eSStefan Eßer size_t rdx; 161450696a6eSStefan Eßer 1615252884aeSStefan Eßer v = bc_num_parseChar(c, base); 1616252884aeSStefan Eßer 1617252884aeSStefan Eßer bc_num_mulArray(&result1, base, &result2); 1618252884aeSStefan Eßer 1619252884aeSStefan Eßer bc_num_bigdig2num(&temp, v); 1620252884aeSStefan Eßer bc_num_add(&result2, &temp, &result1, 0); 1621252884aeSStefan Eßer bc_num_mulArray(m1, base, m2); 1622252884aeSStefan Eßer 162350696a6eSStefan Eßer rdx = BC_NUM_RDX_VAL(m2); 162450696a6eSStefan Eßer 162550696a6eSStefan Eßer if (m2->len < rdx) m2->len = rdx; 1626252884aeSStefan Eßer 1627252884aeSStefan Eßer ptr = m1; 1628252884aeSStefan Eßer m1 = m2; 1629252884aeSStefan Eßer m2 = ptr; 1630252884aeSStefan Eßer } 1631252884aeSStefan Eßer 1632252884aeSStefan Eßer // This one cannot be a divide by 0 because mult starts out at 1, then is 1633252884aeSStefan Eßer // multiplied by base, and base cannot be 0, so mult cannot be 0. 1634252884aeSStefan Eßer bc_num_div(&result1, m1, &result2, digs * 2); 1635252884aeSStefan Eßer bc_num_truncate(&result2, digs); 1636252884aeSStefan Eßer bc_num_add(n, &result2, n, digs); 1637252884aeSStefan Eßer 1638252884aeSStefan Eßer if (BC_NUM_NONZERO(n)) { 1639252884aeSStefan Eßer if (n->scale < digs) bc_num_extend(n, digs - n->scale); 1640252884aeSStefan Eßer } 1641252884aeSStefan Eßer else bc_num_zero(n); 1642252884aeSStefan Eßer 1643252884aeSStefan Eßer err: 1644252884aeSStefan Eßer BC_SIG_MAYLOCK; 1645252884aeSStefan Eßer bc_num_free(&result2); 1646252884aeSStefan Eßer bc_num_free(&result1); 1647252884aeSStefan Eßer bc_num_free(&mult2); 1648252884aeSStefan Eßer int_err: 1649252884aeSStefan Eßer BC_SIG_MAYLOCK; 1650252884aeSStefan Eßer bc_num_free(&mult1); 1651252884aeSStefan Eßer bc_num_free(&temp); 1652252884aeSStefan Eßer BC_LONGJMP_CONT; 1653252884aeSStefan Eßer } 1654252884aeSStefan Eßer 165550696a6eSStefan Eßer static inline void bc_num_printNewline(void) { 165650696a6eSStefan Eßer #if !BC_ENABLE_LIBRARY 1657252884aeSStefan Eßer if (vm.nchars >= vm.line_len - 1) { 1658*7e5c51e5SStefan Eßer bc_vm_putchar('\\', bc_flush_none); 1659*7e5c51e5SStefan Eßer bc_vm_putchar('\n', bc_flush_err); 1660252884aeSStefan Eßer } 166150696a6eSStefan Eßer #endif // !BC_ENABLE_LIBRARY 1662252884aeSStefan Eßer } 1663252884aeSStefan Eßer 1664252884aeSStefan Eßer static void bc_num_putchar(int c) { 1665252884aeSStefan Eßer if (c != '\n') bc_num_printNewline(); 1666*7e5c51e5SStefan Eßer bc_vm_putchar(c, bc_flush_save); 1667252884aeSStefan Eßer } 1668252884aeSStefan Eßer 166950696a6eSStefan Eßer #if DC_ENABLED && !BC_ENABLE_LIBRARY 1670252884aeSStefan Eßer static void bc_num_printChar(size_t n, size_t len, bool rdx) { 1671252884aeSStefan Eßer BC_UNUSED(rdx); 1672252884aeSStefan Eßer BC_UNUSED(len); 1673252884aeSStefan Eßer assert(len == 1); 1674*7e5c51e5SStefan Eßer bc_vm_putchar((uchar) n, bc_flush_save); 1675252884aeSStefan Eßer } 167650696a6eSStefan Eßer #endif // DC_ENABLED && !BC_ENABLE_LIBRARY 1677252884aeSStefan Eßer 1678252884aeSStefan Eßer static void bc_num_printDigits(size_t n, size_t len, bool rdx) { 1679252884aeSStefan Eßer 1680252884aeSStefan Eßer size_t exp, pow; 1681252884aeSStefan Eßer 1682252884aeSStefan Eßer bc_num_putchar(rdx ? '.' : ' '); 1683252884aeSStefan Eßer 1684252884aeSStefan Eßer for (exp = 0, pow = 1; exp < len - 1; ++exp, pow *= BC_BASE); 1685252884aeSStefan Eßer 1686252884aeSStefan Eßer for (exp = 0; exp < len; pow /= BC_BASE, ++exp) { 1687252884aeSStefan Eßer size_t dig = n / pow; 1688252884aeSStefan Eßer n -= dig * pow; 1689252884aeSStefan Eßer bc_num_putchar(((uchar) dig) + '0'); 1690252884aeSStefan Eßer } 1691252884aeSStefan Eßer } 1692252884aeSStefan Eßer 1693252884aeSStefan Eßer static void bc_num_printHex(size_t n, size_t len, bool rdx) { 1694252884aeSStefan Eßer 1695252884aeSStefan Eßer BC_UNUSED(len); 1696252884aeSStefan Eßer 1697252884aeSStefan Eßer assert(len == 1); 1698252884aeSStefan Eßer 1699252884aeSStefan Eßer if (rdx) bc_num_putchar('.'); 1700252884aeSStefan Eßer 1701252884aeSStefan Eßer bc_num_putchar(bc_num_hex_digits[n]); 1702252884aeSStefan Eßer } 1703252884aeSStefan Eßer 1704252884aeSStefan Eßer static void bc_num_printDecimal(const BcNum *restrict n) { 1705252884aeSStefan Eßer 170650696a6eSStefan Eßer size_t i, j, rdx = BC_NUM_RDX_VAL(n); 1707252884aeSStefan Eßer bool zero = true; 1708252884aeSStefan Eßer size_t buffer[BC_BASE_DIGS]; 1709252884aeSStefan Eßer 171050696a6eSStefan Eßer if (BC_NUM_NEG(n)) bc_num_putchar('-'); 1711252884aeSStefan Eßer 1712252884aeSStefan Eßer for (i = n->len - 1; i < n->len; --i) { 1713252884aeSStefan Eßer 1714252884aeSStefan Eßer BcDig n9 = n->num[i]; 1715252884aeSStefan Eßer size_t temp; 1716252884aeSStefan Eßer bool irdx = (i == rdx - 1); 1717252884aeSStefan Eßer 1718252884aeSStefan Eßer zero = (zero & !irdx); 1719252884aeSStefan Eßer temp = n->scale % BC_BASE_DIGS; 1720252884aeSStefan Eßer temp = i || !temp ? 0 : BC_BASE_DIGS - temp; 1721252884aeSStefan Eßer 1722252884aeSStefan Eßer memset(buffer, 0, BC_BASE_DIGS * sizeof(size_t)); 1723252884aeSStefan Eßer 1724252884aeSStefan Eßer for (j = 0; n9 && j < BC_BASE_DIGS; ++j) { 1725d213476dSStefan Eßer buffer[j] = ((size_t) n9) % BC_BASE; 1726252884aeSStefan Eßer n9 /= BC_BASE; 1727252884aeSStefan Eßer } 1728252884aeSStefan Eßer 1729252884aeSStefan Eßer for (j = BC_BASE_DIGS - 1; j < BC_BASE_DIGS && j >= temp; --j) { 1730252884aeSStefan Eßer bool print_rdx = (irdx & (j == BC_BASE_DIGS - 1)); 1731252884aeSStefan Eßer zero = (zero && buffer[j] == 0); 1732252884aeSStefan Eßer if (!zero) bc_num_printHex(buffer[j], 1, print_rdx); 1733252884aeSStefan Eßer } 1734252884aeSStefan Eßer } 1735252884aeSStefan Eßer } 1736252884aeSStefan Eßer 1737252884aeSStefan Eßer #if BC_ENABLE_EXTRA_MATH 1738252884aeSStefan Eßer static void bc_num_printExponent(const BcNum *restrict n, bool eng) { 1739252884aeSStefan Eßer 174050696a6eSStefan Eßer size_t places, mod, nrdx = BC_NUM_RDX_VAL(n); 174150696a6eSStefan Eßer bool neg = (n->len <= nrdx); 1742252884aeSStefan Eßer BcNum temp, exp; 1743252884aeSStefan Eßer BcDig digs[BC_NUM_BIGDIG_LOG10]; 1744252884aeSStefan Eßer 1745252884aeSStefan Eßer BC_SIG_LOCK; 1746252884aeSStefan Eßer 1747252884aeSStefan Eßer bc_num_createCopy(&temp, n); 1748252884aeSStefan Eßer 1749252884aeSStefan Eßer BC_SETJMP_LOCKED(exit); 1750252884aeSStefan Eßer 1751252884aeSStefan Eßer BC_SIG_UNLOCK; 1752252884aeSStefan Eßer 1753252884aeSStefan Eßer if (neg) { 1754252884aeSStefan Eßer 1755252884aeSStefan Eßer size_t i, idx = bc_num_nonzeroLen(n) - 1; 1756252884aeSStefan Eßer 1757252884aeSStefan Eßer places = 1; 1758252884aeSStefan Eßer 1759252884aeSStefan Eßer for (i = BC_BASE_DIGS - 1; i < BC_BASE_DIGS; --i) { 1760252884aeSStefan Eßer if (bc_num_pow10[i] > (BcBigDig) n->num[idx]) places += 1; 1761252884aeSStefan Eßer else break; 1762252884aeSStefan Eßer } 1763252884aeSStefan Eßer 176450696a6eSStefan Eßer places += (nrdx - (idx + 1)) * BC_BASE_DIGS; 1765252884aeSStefan Eßer mod = places % 3; 1766252884aeSStefan Eßer 1767252884aeSStefan Eßer if (eng && mod != 0) places += 3 - mod; 1768252884aeSStefan Eßer bc_num_shiftLeft(&temp, places); 1769252884aeSStefan Eßer } 1770252884aeSStefan Eßer else { 1771252884aeSStefan Eßer places = bc_num_intDigits(n) - 1; 1772252884aeSStefan Eßer mod = places % 3; 1773252884aeSStefan Eßer if (eng && mod != 0) places -= 3 - (3 - mod); 1774252884aeSStefan Eßer bc_num_shiftRight(&temp, places); 1775252884aeSStefan Eßer } 1776252884aeSStefan Eßer 1777252884aeSStefan Eßer bc_num_printDecimal(&temp); 1778252884aeSStefan Eßer bc_num_putchar('e'); 1779252884aeSStefan Eßer 1780252884aeSStefan Eßer if (!places) { 1781252884aeSStefan Eßer bc_num_printHex(0, 1, false); 1782252884aeSStefan Eßer goto exit; 1783252884aeSStefan Eßer } 1784252884aeSStefan Eßer 1785252884aeSStefan Eßer if (neg) bc_num_putchar('-'); 1786252884aeSStefan Eßer 1787252884aeSStefan Eßer bc_num_setup(&exp, digs, BC_NUM_BIGDIG_LOG10); 1788252884aeSStefan Eßer bc_num_bigdig2num(&exp, (BcBigDig) places); 1789252884aeSStefan Eßer 1790252884aeSStefan Eßer bc_num_printDecimal(&exp); 1791252884aeSStefan Eßer 1792252884aeSStefan Eßer exit: 1793252884aeSStefan Eßer BC_SIG_MAYLOCK; 1794252884aeSStefan Eßer bc_num_free(&temp); 1795252884aeSStefan Eßer BC_LONGJMP_CONT; 1796252884aeSStefan Eßer } 1797252884aeSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH 1798252884aeSStefan Eßer 1799252884aeSStefan Eßer static void bc_num_printFixup(BcNum *restrict n, BcBigDig rem, 1800252884aeSStefan Eßer BcBigDig pow, size_t idx) 1801252884aeSStefan Eßer { 1802252884aeSStefan Eßer size_t i, len = n->len - idx; 1803252884aeSStefan Eßer BcBigDig acc; 1804252884aeSStefan Eßer BcDig *a = n->num + idx; 1805252884aeSStefan Eßer 1806252884aeSStefan Eßer if (len < 2) return; 1807252884aeSStefan Eßer 1808252884aeSStefan Eßer for (i = len - 1; i > 0; --i) { 1809252884aeSStefan Eßer 1810252884aeSStefan Eßer acc = ((BcBigDig) a[i]) * rem + ((BcBigDig) a[i - 1]); 1811252884aeSStefan Eßer a[i - 1] = (BcDig) (acc % pow); 1812252884aeSStefan Eßer acc /= pow; 1813252884aeSStefan Eßer acc += (BcBigDig) a[i]; 1814252884aeSStefan Eßer 1815252884aeSStefan Eßer if (acc >= BC_BASE_POW) { 1816252884aeSStefan Eßer 1817252884aeSStefan Eßer if (i == len - 1) { 1818252884aeSStefan Eßer len = bc_vm_growSize(len, 1); 1819252884aeSStefan Eßer bc_num_expand(n, bc_vm_growSize(len, idx)); 1820252884aeSStefan Eßer a = n->num + idx; 1821252884aeSStefan Eßer a[len - 1] = 0; 1822252884aeSStefan Eßer } 1823252884aeSStefan Eßer 1824252884aeSStefan Eßer a[i + 1] += acc / BC_BASE_POW; 1825252884aeSStefan Eßer acc %= BC_BASE_POW; 1826252884aeSStefan Eßer } 1827252884aeSStefan Eßer 1828252884aeSStefan Eßer assert(acc < BC_BASE_POW); 1829252884aeSStefan Eßer a[i] = (BcDig) acc; 1830252884aeSStefan Eßer } 1831252884aeSStefan Eßer 1832252884aeSStefan Eßer n->len = len + idx; 1833252884aeSStefan Eßer } 1834252884aeSStefan Eßer 1835252884aeSStefan Eßer static void bc_num_printPrepare(BcNum *restrict n, BcBigDig rem, 1836252884aeSStefan Eßer BcBigDig pow) 1837252884aeSStefan Eßer { 1838252884aeSStefan Eßer size_t i; 1839252884aeSStefan Eßer 1840252884aeSStefan Eßer for (i = 0; i < n->len; ++i) bc_num_printFixup(n, rem, pow, i); 1841252884aeSStefan Eßer 1842252884aeSStefan Eßer for (i = 0; i < n->len; ++i) { 1843252884aeSStefan Eßer 1844252884aeSStefan Eßer assert(pow == ((BcBigDig) ((BcDig) pow))); 1845252884aeSStefan Eßer 1846252884aeSStefan Eßer if (n->num[i] >= (BcDig) pow) { 1847252884aeSStefan Eßer 1848252884aeSStefan Eßer if (i + 1 == n->len) { 1849252884aeSStefan Eßer n->len = bc_vm_growSize(n->len, 1); 1850252884aeSStefan Eßer bc_num_expand(n, n->len); 1851252884aeSStefan Eßer n->num[i + 1] = 0; 1852252884aeSStefan Eßer } 1853252884aeSStefan Eßer 1854252884aeSStefan Eßer assert(pow < BC_BASE_POW); 1855252884aeSStefan Eßer n->num[i + 1] += n->num[i] / ((BcDig) pow); 1856252884aeSStefan Eßer n->num[i] %= (BcDig) pow; 1857252884aeSStefan Eßer } 1858252884aeSStefan Eßer } 1859252884aeSStefan Eßer } 1860252884aeSStefan Eßer 1861252884aeSStefan Eßer static void bc_num_printNum(BcNum *restrict n, BcBigDig base, 1862252884aeSStefan Eßer size_t len, BcNumDigitOp print) 1863252884aeSStefan Eßer { 1864252884aeSStefan Eßer BcVec stack; 1865252884aeSStefan Eßer BcNum intp, fracp1, fracp2, digit, flen1, flen2, *n1, *n2, *temp; 1866252884aeSStefan Eßer BcBigDig dig = 0, *ptr, acc, exp; 186750696a6eSStefan Eßer size_t i, j, nrdx; 1868252884aeSStefan Eßer bool radix; 1869252884aeSStefan Eßer BcDig digit_digs[BC_NUM_BIGDIG_LOG10 + 1]; 1870252884aeSStefan Eßer 1871252884aeSStefan Eßer assert(base > 1); 1872252884aeSStefan Eßer 1873252884aeSStefan Eßer if (BC_NUM_ZERO(n)) { 1874252884aeSStefan Eßer print(0, len, false); 1875252884aeSStefan Eßer return; 1876252884aeSStefan Eßer } 1877252884aeSStefan Eßer 1878252884aeSStefan Eßer // This function uses an algorithm that Stefan Esser <se@freebsd.org> came 1879252884aeSStefan Eßer // up with to print the integer part of a number. What it does is convert 1880252884aeSStefan Eßer // intp into a number of the specified base, but it does it directly, 1881252884aeSStefan Eßer // instead of just doing a series of divisions and printing the remainders 1882252884aeSStefan Eßer // in reverse order. 1883252884aeSStefan Eßer // 1884252884aeSStefan Eßer // Let me explain in a bit more detail: 1885252884aeSStefan Eßer // 1886252884aeSStefan Eßer // The algorithm takes the current least significant digit (after intp has 1887252884aeSStefan Eßer // been converted to an integer) and the next to least significant digit, 1888252884aeSStefan Eßer // and it converts the least significant digit into one of the specified 1889252884aeSStefan Eßer // base, putting any overflow into the next to least significant digit. It 1890252884aeSStefan Eßer // iterates through the whole number, from least significant to most 1891252884aeSStefan Eßer // significant, doing this conversion. At the end of that iteration, the 1892252884aeSStefan Eßer // least significant digit is converted, but the others are not, so it 1893252884aeSStefan Eßer // iterates again, starting at the next to least significant digit. It keeps 1894252884aeSStefan Eßer // doing that conversion, skipping one more digit than the last time, until 1895252884aeSStefan Eßer // all digits have been converted. Then it prints them in reverse order. 1896252884aeSStefan Eßer // 1897252884aeSStefan Eßer // That is the gist of the algorithm. It leaves out several things, such as 1898252884aeSStefan Eßer // the fact that digits are not always converted into the specified base, 1899252884aeSStefan Eßer // but into something close, basically a power of the specified base. In 1900252884aeSStefan Eßer // Stefan's words, "You could consider BcDigs to be of base 10^BC_BASE_DIGS 1901252884aeSStefan Eßer // in the normal case and obase^N for the largest value of N that satisfies 1902252884aeSStefan Eßer // obase^N <= 10^BC_BASE_DIGS. [This means that] the result is not in base 1903252884aeSStefan Eßer // "obase", but in base "obase^N", which happens to be printable as a number 1904252884aeSStefan Eßer // of base "obase" without consideration for neighbouring BcDigs." This fact 1905252884aeSStefan Eßer // is what necessitates the existence of the loop later in this function. 1906252884aeSStefan Eßer // 1907252884aeSStefan Eßer // The conversion happens in bc_num_printPrepare() where the outer loop 1908252884aeSStefan Eßer // happens and bc_num_printFixup() where the inner loop, or actual 1909252884aeSStefan Eßer // conversion, happens. 1910252884aeSStefan Eßer 191150696a6eSStefan Eßer nrdx = BC_NUM_RDX_VAL(n); 191250696a6eSStefan Eßer 1913252884aeSStefan Eßer BC_SIG_LOCK; 1914252884aeSStefan Eßer 1915252884aeSStefan Eßer bc_vec_init(&stack, sizeof(BcBigDig), NULL); 191650696a6eSStefan Eßer bc_num_init(&fracp1, nrdx); 1917252884aeSStefan Eßer 1918252884aeSStefan Eßer bc_num_createCopy(&intp, n); 1919252884aeSStefan Eßer 1920252884aeSStefan Eßer BC_SETJMP_LOCKED(err); 1921252884aeSStefan Eßer 1922252884aeSStefan Eßer BC_SIG_UNLOCK; 1923252884aeSStefan Eßer 1924252884aeSStefan Eßer bc_num_truncate(&intp, intp.scale); 1925252884aeSStefan Eßer 1926252884aeSStefan Eßer bc_num_sub(n, &intp, &fracp1, 0); 1927252884aeSStefan Eßer 1928252884aeSStefan Eßer if (base != vm.last_base) { 1929252884aeSStefan Eßer 1930252884aeSStefan Eßer vm.last_pow = 1; 1931252884aeSStefan Eßer vm.last_exp = 0; 1932252884aeSStefan Eßer 1933252884aeSStefan Eßer while (vm.last_pow * base <= BC_BASE_POW) { 1934252884aeSStefan Eßer vm.last_pow *= base; 1935252884aeSStefan Eßer vm.last_exp += 1; 1936252884aeSStefan Eßer } 1937252884aeSStefan Eßer 1938252884aeSStefan Eßer vm.last_rem = BC_BASE_POW - vm.last_pow; 1939252884aeSStefan Eßer vm.last_base = base; 1940252884aeSStefan Eßer } 1941252884aeSStefan Eßer 1942252884aeSStefan Eßer exp = vm.last_exp; 1943252884aeSStefan Eßer 1944252884aeSStefan Eßer if (vm.last_rem != 0) bc_num_printPrepare(&intp, vm.last_rem, vm.last_pow); 1945252884aeSStefan Eßer 1946252884aeSStefan Eßer for (i = 0; i < intp.len; ++i) { 1947252884aeSStefan Eßer 1948252884aeSStefan Eßer acc = (BcBigDig) intp.num[i]; 1949252884aeSStefan Eßer 1950252884aeSStefan Eßer for (j = 0; j < exp && (i < intp.len - 1 || acc != 0); ++j) 1951252884aeSStefan Eßer { 1952252884aeSStefan Eßer if (j != exp - 1) { 1953252884aeSStefan Eßer dig = acc % base; 1954252884aeSStefan Eßer acc /= base; 1955252884aeSStefan Eßer } 1956252884aeSStefan Eßer else { 1957252884aeSStefan Eßer dig = acc; 1958252884aeSStefan Eßer acc = 0; 1959252884aeSStefan Eßer } 1960252884aeSStefan Eßer 1961252884aeSStefan Eßer assert(dig < base); 1962252884aeSStefan Eßer 1963252884aeSStefan Eßer bc_vec_push(&stack, &dig); 1964252884aeSStefan Eßer } 1965252884aeSStefan Eßer 1966252884aeSStefan Eßer assert(acc == 0); 1967252884aeSStefan Eßer } 1968252884aeSStefan Eßer 1969252884aeSStefan Eßer for (i = 0; i < stack.len; ++i) { 1970252884aeSStefan Eßer ptr = bc_vec_item_rev(&stack, i); 1971252884aeSStefan Eßer assert(ptr != NULL); 1972252884aeSStefan Eßer print(*ptr, len, false); 1973252884aeSStefan Eßer } 1974252884aeSStefan Eßer 1975252884aeSStefan Eßer if (!n->scale) goto err; 1976252884aeSStefan Eßer 1977252884aeSStefan Eßer BC_SIG_LOCK; 1978252884aeSStefan Eßer 1979252884aeSStefan Eßer BC_UNSETJMP; 1980252884aeSStefan Eßer 198150696a6eSStefan Eßer bc_num_init(&fracp2, nrdx); 1982252884aeSStefan Eßer bc_num_setup(&digit, digit_digs, sizeof(digit_digs) / sizeof(BcDig)); 1983252884aeSStefan Eßer bc_num_init(&flen1, BC_NUM_BIGDIG_LOG10); 1984252884aeSStefan Eßer bc_num_init(&flen2, BC_NUM_BIGDIG_LOG10); 1985252884aeSStefan Eßer 1986252884aeSStefan Eßer BC_SETJMP_LOCKED(frac_err); 1987252884aeSStefan Eßer 1988252884aeSStefan Eßer BC_SIG_UNLOCK; 1989252884aeSStefan Eßer 1990252884aeSStefan Eßer bc_num_one(&flen1); 1991252884aeSStefan Eßer 1992252884aeSStefan Eßer radix = true; 1993252884aeSStefan Eßer n1 = &flen1; 1994252884aeSStefan Eßer n2 = &flen2; 1995252884aeSStefan Eßer 1996252884aeSStefan Eßer fracp2.scale = n->scale; 199750696a6eSStefan Eßer BC_NUM_RDX_SET_NP(fracp2, BC_NUM_RDX(fracp2.scale)); 1998252884aeSStefan Eßer 1999252884aeSStefan Eßer while (bc_num_intDigits(n1) < n->scale + 1) { 2000252884aeSStefan Eßer 2001252884aeSStefan Eßer bc_num_expand(&fracp2, fracp1.len + 1); 2002252884aeSStefan Eßer bc_num_mulArray(&fracp1, base, &fracp2); 200350696a6eSStefan Eßer 200450696a6eSStefan Eßer nrdx = BC_NUM_RDX_VAL_NP(fracp2); 200550696a6eSStefan Eßer 200650696a6eSStefan Eßer if (fracp2.len < nrdx) fracp2.len = nrdx; 2007252884aeSStefan Eßer 2008252884aeSStefan Eßer // fracp is guaranteed to be non-negative and small enough. 2009252884aeSStefan Eßer bc_num_bigdig2(&fracp2, &dig); 2010252884aeSStefan Eßer 2011252884aeSStefan Eßer bc_num_bigdig2num(&digit, dig); 2012252884aeSStefan Eßer bc_num_sub(&fracp2, &digit, &fracp1, 0); 2013252884aeSStefan Eßer 2014252884aeSStefan Eßer print(dig, len, radix); 2015252884aeSStefan Eßer bc_num_mulArray(n1, base, n2); 2016252884aeSStefan Eßer 2017252884aeSStefan Eßer radix = false; 2018252884aeSStefan Eßer temp = n1; 2019252884aeSStefan Eßer n1 = n2; 2020252884aeSStefan Eßer n2 = temp; 2021252884aeSStefan Eßer } 2022252884aeSStefan Eßer 2023252884aeSStefan Eßer frac_err: 2024252884aeSStefan Eßer BC_SIG_MAYLOCK; 2025252884aeSStefan Eßer bc_num_free(&flen2); 2026252884aeSStefan Eßer bc_num_free(&flen1); 2027252884aeSStefan Eßer bc_num_free(&fracp2); 2028252884aeSStefan Eßer err: 2029252884aeSStefan Eßer BC_SIG_MAYLOCK; 2030252884aeSStefan Eßer bc_num_free(&fracp1); 2031252884aeSStefan Eßer bc_num_free(&intp); 2032252884aeSStefan Eßer bc_vec_free(&stack); 2033252884aeSStefan Eßer BC_LONGJMP_CONT; 2034252884aeSStefan Eßer } 2035252884aeSStefan Eßer 2036252884aeSStefan Eßer static void bc_num_printBase(BcNum *restrict n, BcBigDig base) { 2037252884aeSStefan Eßer 2038252884aeSStefan Eßer size_t width; 2039252884aeSStefan Eßer BcNumDigitOp print; 204050696a6eSStefan Eßer bool neg = BC_NUM_NEG(n); 2041252884aeSStefan Eßer 2042252884aeSStefan Eßer if (neg) bc_num_putchar('-'); 2043252884aeSStefan Eßer 204450696a6eSStefan Eßer BC_NUM_NEG_CLR(n); 2045252884aeSStefan Eßer 2046252884aeSStefan Eßer if (base <= BC_NUM_MAX_POSIX_IBASE) { 2047252884aeSStefan Eßer width = 1; 2048252884aeSStefan Eßer print = bc_num_printHex; 2049252884aeSStefan Eßer } 2050252884aeSStefan Eßer else { 2051252884aeSStefan Eßer assert(base <= BC_BASE_POW); 2052252884aeSStefan Eßer width = bc_num_log10(base - 1); 2053252884aeSStefan Eßer print = bc_num_printDigits; 2054252884aeSStefan Eßer } 2055252884aeSStefan Eßer 2056252884aeSStefan Eßer bc_num_printNum(n, base, width, print); 205750696a6eSStefan Eßer n->rdx = BC_NUM_NEG_VAL(n, neg); 2058252884aeSStefan Eßer } 2059252884aeSStefan Eßer 206050696a6eSStefan Eßer #if DC_ENABLED && !BC_ENABLE_LIBRARY 2061252884aeSStefan Eßer void bc_num_stream(BcNum *restrict n, BcBigDig base) { 2062252884aeSStefan Eßer bc_num_printNum(n, base, 1, bc_num_printChar); 2063252884aeSStefan Eßer } 206450696a6eSStefan Eßer #endif // DC_ENABLED && !BC_ENABLE_LIBRARY 2065252884aeSStefan Eßer 2066252884aeSStefan Eßer void bc_num_setup(BcNum *restrict n, BcDig *restrict num, size_t cap) { 2067252884aeSStefan Eßer assert(n != NULL); 2068252884aeSStefan Eßer n->num = num; 2069252884aeSStefan Eßer n->cap = cap; 2070252884aeSStefan Eßer bc_num_zero(n); 2071252884aeSStefan Eßer } 2072252884aeSStefan Eßer 2073252884aeSStefan Eßer void bc_num_init(BcNum *restrict n, size_t req) { 2074252884aeSStefan Eßer 2075252884aeSStefan Eßer BcDig *num; 2076252884aeSStefan Eßer 2077252884aeSStefan Eßer BC_SIG_ASSERT_LOCKED; 2078252884aeSStefan Eßer 2079252884aeSStefan Eßer assert(n != NULL); 2080252884aeSStefan Eßer 2081252884aeSStefan Eßer req = req >= BC_NUM_DEF_SIZE ? req : BC_NUM_DEF_SIZE; 2082252884aeSStefan Eßer 2083252884aeSStefan Eßer if (req == BC_NUM_DEF_SIZE && vm.temps.len) { 2084252884aeSStefan Eßer BcNum *nptr = bc_vec_top(&vm.temps); 2085252884aeSStefan Eßer num = nptr->num; 2086252884aeSStefan Eßer bc_vec_pop(&vm.temps); 2087252884aeSStefan Eßer } 2088252884aeSStefan Eßer else num = bc_vm_malloc(BC_NUM_SIZE(req)); 2089252884aeSStefan Eßer 2090252884aeSStefan Eßer bc_num_setup(n, num, req); 2091252884aeSStefan Eßer } 2092252884aeSStefan Eßer 2093252884aeSStefan Eßer void bc_num_clear(BcNum *restrict n) { 2094252884aeSStefan Eßer n->num = NULL; 2095252884aeSStefan Eßer n->cap = 0; 2096252884aeSStefan Eßer } 2097252884aeSStefan Eßer 2098252884aeSStefan Eßer void bc_num_free(void *num) { 2099252884aeSStefan Eßer 2100252884aeSStefan Eßer BcNum *n = (BcNum*) num; 2101252884aeSStefan Eßer 2102252884aeSStefan Eßer BC_SIG_ASSERT_LOCKED; 2103252884aeSStefan Eßer 2104252884aeSStefan Eßer assert(n != NULL); 2105252884aeSStefan Eßer 2106252884aeSStefan Eßer if (n->cap == BC_NUM_DEF_SIZE) bc_vec_push(&vm.temps, n); 2107252884aeSStefan Eßer else free(n->num); 2108252884aeSStefan Eßer } 2109252884aeSStefan Eßer 2110252884aeSStefan Eßer void bc_num_copy(BcNum *d, const BcNum *s) { 2111252884aeSStefan Eßer assert(d != NULL && s != NULL); 2112252884aeSStefan Eßer if (d == s) return; 2113252884aeSStefan Eßer bc_num_expand(d, s->len); 2114252884aeSStefan Eßer d->len = s->len; 211550696a6eSStefan Eßer // I can just copy directly here. 2116252884aeSStefan Eßer d->rdx = s->rdx; 2117252884aeSStefan Eßer d->scale = s->scale; 2118252884aeSStefan Eßer memcpy(d->num, s->num, BC_NUM_SIZE(d->len)); 2119252884aeSStefan Eßer } 2120252884aeSStefan Eßer 2121252884aeSStefan Eßer void bc_num_createCopy(BcNum *d, const BcNum *s) { 2122252884aeSStefan Eßer BC_SIG_ASSERT_LOCKED; 2123252884aeSStefan Eßer bc_num_init(d, s->len); 2124252884aeSStefan Eßer bc_num_copy(d, s); 2125252884aeSStefan Eßer } 2126252884aeSStefan Eßer 2127252884aeSStefan Eßer void bc_num_createFromBigdig(BcNum *n, BcBigDig val) { 2128252884aeSStefan Eßer BC_SIG_ASSERT_LOCKED; 212950696a6eSStefan Eßer bc_num_init(n, BC_NUM_BIGDIG_LOG10); 2130252884aeSStefan Eßer bc_num_bigdig2num(n, val); 2131252884aeSStefan Eßer } 2132252884aeSStefan Eßer 2133252884aeSStefan Eßer size_t bc_num_scale(const BcNum *restrict n) { 2134252884aeSStefan Eßer return n->scale; 2135252884aeSStefan Eßer } 2136252884aeSStefan Eßer 2137252884aeSStefan Eßer size_t bc_num_len(const BcNum *restrict n) { 2138252884aeSStefan Eßer 2139252884aeSStefan Eßer size_t len = n->len; 2140252884aeSStefan Eßer 2141028616d0SStefan Eßer if (BC_NUM_ZERO(n)) return n->scale ? n->scale : 1; 2142252884aeSStefan Eßer 214350696a6eSStefan Eßer if (BC_NUM_RDX_VAL(n) == len) { 2144252884aeSStefan Eßer 2145252884aeSStefan Eßer size_t zero, scale; 2146252884aeSStefan Eßer 2147252884aeSStefan Eßer len = bc_num_nonzeroLen(n); 2148252884aeSStefan Eßer 2149252884aeSStefan Eßer scale = n->scale % BC_BASE_DIGS; 2150252884aeSStefan Eßer scale = scale ? scale : BC_BASE_DIGS; 2151252884aeSStefan Eßer 2152252884aeSStefan Eßer zero = bc_num_zeroDigits(n->num + len - 1); 2153252884aeSStefan Eßer 2154252884aeSStefan Eßer len = len * BC_BASE_DIGS - zero - (BC_BASE_DIGS - scale); 2155252884aeSStefan Eßer } 2156252884aeSStefan Eßer else len = bc_num_intDigits(n) + n->scale; 2157252884aeSStefan Eßer 2158252884aeSStefan Eßer return len; 2159252884aeSStefan Eßer } 2160252884aeSStefan Eßer 216150696a6eSStefan Eßer void bc_num_parse(BcNum *restrict n, const char *restrict val, BcBigDig base) { 216250696a6eSStefan Eßer 2163252884aeSStefan Eßer assert(n != NULL && val != NULL && base); 2164252884aeSStefan Eßer assert(base >= BC_NUM_MIN_BASE && base <= vm.maxes[BC_PROG_GLOBALS_IBASE]); 2165252884aeSStefan Eßer assert(bc_num_strValid(val)); 2166252884aeSStefan Eßer 216750696a6eSStefan Eßer if (!val[1]) { 2168252884aeSStefan Eßer BcBigDig dig = bc_num_parseChar(val[0], BC_NUM_MAX_LBASE); 2169252884aeSStefan Eßer bc_num_bigdig2num(n, dig); 2170252884aeSStefan Eßer } 2171252884aeSStefan Eßer else if (base == BC_BASE) bc_num_parseDecimal(n, val); 2172252884aeSStefan Eßer else bc_num_parseBase(n, val, base); 217350696a6eSStefan Eßer 217450696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(n)); 2175252884aeSStefan Eßer } 2176252884aeSStefan Eßer 2177252884aeSStefan Eßer void bc_num_print(BcNum *restrict n, BcBigDig base, bool newline) { 2178252884aeSStefan Eßer 2179252884aeSStefan Eßer assert(n != NULL); 2180252884aeSStefan Eßer assert(BC_ENABLE_EXTRA_MATH || base >= BC_NUM_MIN_BASE); 2181252884aeSStefan Eßer 2182252884aeSStefan Eßer bc_num_printNewline(); 2183252884aeSStefan Eßer 2184252884aeSStefan Eßer if (BC_NUM_ZERO(n)) bc_num_printHex(0, 1, false); 2185252884aeSStefan Eßer else if (base == BC_BASE) bc_num_printDecimal(n); 2186252884aeSStefan Eßer #if BC_ENABLE_EXTRA_MATH 218750696a6eSStefan Eßer else if (base == 0 || base == 1) bc_num_printExponent(n, base != 0); 2188252884aeSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH 2189252884aeSStefan Eßer else bc_num_printBase(n, base); 2190252884aeSStefan Eßer 2191252884aeSStefan Eßer if (newline) bc_num_putchar('\n'); 2192252884aeSStefan Eßer } 2193252884aeSStefan Eßer 2194252884aeSStefan Eßer void bc_num_bigdig2(const BcNum *restrict n, BcBigDig *result) { 2195252884aeSStefan Eßer 2196252884aeSStefan Eßer // This function returns no errors because it's guaranteed to succeed if 2197252884aeSStefan Eßer // its preconditions are met. Those preconditions include both parameters 2198252884aeSStefan Eßer // being non-NULL, n being non-negative, and n being less than vm.max. If 2199252884aeSStefan Eßer // all of that is true, then we can just convert without worrying about 220050696a6eSStefan Eßer // negative errors or overflow. 2201252884aeSStefan Eßer 2202252884aeSStefan Eßer BcBigDig r = 0; 220350696a6eSStefan Eßer size_t nrdx = BC_NUM_RDX_VAL(n); 2204252884aeSStefan Eßer 2205252884aeSStefan Eßer assert(n != NULL && result != NULL); 220650696a6eSStefan Eßer assert(!BC_NUM_NEG(n)); 2207252884aeSStefan Eßer assert(bc_num_cmp(n, &vm.max) < 0); 220850696a6eSStefan Eßer assert(n->len - nrdx <= 3); 2209252884aeSStefan Eßer 2210252884aeSStefan Eßer // There is a small speed win from unrolling the loop here, and since it 2211252884aeSStefan Eßer // only adds 53 bytes, I decided that it was worth it. 221250696a6eSStefan Eßer switch (n->len - nrdx) { 221350696a6eSStefan Eßer 2214252884aeSStefan Eßer case 3: 221550696a6eSStefan Eßer { 221650696a6eSStefan Eßer r = (BcBigDig) n->num[nrdx + 2]; 221750696a6eSStefan Eßer } 2218252884aeSStefan Eßer // Fallthrough. 221950696a6eSStefan Eßer BC_FALLTHROUGH 222050696a6eSStefan Eßer 2221252884aeSStefan Eßer case 2: 222250696a6eSStefan Eßer { 222350696a6eSStefan Eßer r = r * BC_BASE_POW + (BcBigDig) n->num[nrdx + 1]; 222450696a6eSStefan Eßer } 2225252884aeSStefan Eßer // Fallthrough. 222650696a6eSStefan Eßer BC_FALLTHROUGH 222750696a6eSStefan Eßer 2228252884aeSStefan Eßer case 1: 222950696a6eSStefan Eßer { 223050696a6eSStefan Eßer r = r * BC_BASE_POW + (BcBigDig) n->num[nrdx]; 223150696a6eSStefan Eßer } 2232252884aeSStefan Eßer } 2233252884aeSStefan Eßer 2234252884aeSStefan Eßer *result = r; 2235252884aeSStefan Eßer } 2236252884aeSStefan Eßer 2237252884aeSStefan Eßer void bc_num_bigdig(const BcNum *restrict n, BcBigDig *result) { 2238252884aeSStefan Eßer 2239252884aeSStefan Eßer assert(n != NULL && result != NULL); 2240252884aeSStefan Eßer 224150696a6eSStefan Eßer if (BC_ERR(BC_NUM_NEG(n))) bc_vm_err(BC_ERR_MATH_NEGATIVE); 2242252884aeSStefan Eßer if (BC_ERR(bc_num_cmp(n, &vm.max) >= 0)) 224350696a6eSStefan Eßer bc_vm_err(BC_ERR_MATH_OVERFLOW); 2244252884aeSStefan Eßer 2245252884aeSStefan Eßer bc_num_bigdig2(n, result); 2246252884aeSStefan Eßer } 2247252884aeSStefan Eßer 2248252884aeSStefan Eßer void bc_num_bigdig2num(BcNum *restrict n, BcBigDig val) { 2249252884aeSStefan Eßer 2250252884aeSStefan Eßer BcDig *ptr; 2251252884aeSStefan Eßer size_t i; 2252252884aeSStefan Eßer 2253252884aeSStefan Eßer assert(n != NULL); 2254252884aeSStefan Eßer 2255252884aeSStefan Eßer bc_num_zero(n); 2256252884aeSStefan Eßer 2257252884aeSStefan Eßer if (!val) return; 2258252884aeSStefan Eßer 2259252884aeSStefan Eßer bc_num_expand(n, BC_NUM_BIGDIG_LOG10); 2260252884aeSStefan Eßer 2261252884aeSStefan Eßer for (ptr = n->num, i = 0; val; ++i, val /= BC_BASE_POW) 2262252884aeSStefan Eßer ptr[i] = val % BC_BASE_POW; 2263252884aeSStefan Eßer 2264252884aeSStefan Eßer n->len = i; 2265252884aeSStefan Eßer } 2266252884aeSStefan Eßer 22673aa99676SStefan Eßer #if BC_ENABLE_EXTRA_MATH && BC_ENABLE_RAND 2268252884aeSStefan Eßer void bc_num_rng(const BcNum *restrict n, BcRNG *rng) { 2269252884aeSStefan Eßer 227050696a6eSStefan Eßer BcNum temp, temp2, intn, frac; 2271252884aeSStefan Eßer BcRand state1, state2, inc1, inc2; 227250696a6eSStefan Eßer size_t nrdx = BC_NUM_RDX_VAL(n); 2273252884aeSStefan Eßer 2274252884aeSStefan Eßer BC_SIG_LOCK; 2275252884aeSStefan Eßer 2276252884aeSStefan Eßer bc_num_init(&temp, n->len); 2277252884aeSStefan Eßer bc_num_init(&temp2, n->len); 227850696a6eSStefan Eßer bc_num_init(&frac, nrdx); 2279252884aeSStefan Eßer bc_num_init(&intn, bc_num_int(n)); 2280252884aeSStefan Eßer 2281252884aeSStefan Eßer BC_SETJMP_LOCKED(err); 2282252884aeSStefan Eßer 2283252884aeSStefan Eßer BC_SIG_UNLOCK; 2284252884aeSStefan Eßer 228550696a6eSStefan Eßer assert(BC_NUM_RDX_VALID_NP(vm.max)); 2286252884aeSStefan Eßer 228750696a6eSStefan Eßer memcpy(frac.num, n->num, BC_NUM_SIZE(nrdx)); 228850696a6eSStefan Eßer frac.len = nrdx; 228950696a6eSStefan Eßer BC_NUM_RDX_SET_NP(frac, nrdx); 2290252884aeSStefan Eßer frac.scale = n->scale; 2291252884aeSStefan Eßer 229250696a6eSStefan Eßer assert(BC_NUM_RDX_VALID_NP(frac)); 229350696a6eSStefan Eßer assert(BC_NUM_RDX_VALID_NP(vm.max2)); 229450696a6eSStefan Eßer 229550696a6eSStefan Eßer bc_num_mul(&frac, &vm.max2, &temp, 0); 2296252884aeSStefan Eßer 2297252884aeSStefan Eßer bc_num_truncate(&temp, temp.scale); 2298252884aeSStefan Eßer bc_num_copy(&frac, &temp); 2299252884aeSStefan Eßer 230050696a6eSStefan Eßer memcpy(intn.num, n->num + nrdx, BC_NUM_SIZE(bc_num_int(n))); 2301252884aeSStefan Eßer intn.len = bc_num_int(n); 2302252884aeSStefan Eßer 2303252884aeSStefan Eßer // This assert is here because it has to be true. It is also here to justify 230450696a6eSStefan Eßer // the use of BC_ERR_SIGNAL_ONLY() on each of the divmod's and mod's below. 2305252884aeSStefan Eßer assert(BC_NUM_NONZERO(&vm.max)); 2306252884aeSStefan Eßer 2307252884aeSStefan Eßer if (BC_NUM_NONZERO(&frac)) { 2308252884aeSStefan Eßer 2309252884aeSStefan Eßer bc_num_divmod(&frac, &vm.max, &temp, &temp2, 0); 2310252884aeSStefan Eßer 2311252884aeSStefan Eßer // frac is guaranteed to be smaller than vm.max * vm.max (pow). 2312252884aeSStefan Eßer // This means that when dividing frac by vm.max, as above, the 2313252884aeSStefan Eßer // quotient and remainder are both guaranteed to be less than vm.max, 2314252884aeSStefan Eßer // which means we can use bc_num_bigdig2() here and not worry about 2315252884aeSStefan Eßer // overflow. 2316252884aeSStefan Eßer bc_num_bigdig2(&temp2, (BcBigDig*) &state1); 2317252884aeSStefan Eßer bc_num_bigdig2(&temp, (BcBigDig*) &state2); 2318252884aeSStefan Eßer } 2319252884aeSStefan Eßer else state1 = state2 = 0; 2320252884aeSStefan Eßer 2321252884aeSStefan Eßer if (BC_NUM_NONZERO(&intn)) { 2322252884aeSStefan Eßer 2323252884aeSStefan Eßer bc_num_divmod(&intn, &vm.max, &temp, &temp2, 0); 2324252884aeSStefan Eßer 2325252884aeSStefan Eßer // Because temp2 is the mod of vm.max, from above, it is guaranteed 2326252884aeSStefan Eßer // to be small enough to use bc_num_bigdig2(). 2327252884aeSStefan Eßer bc_num_bigdig2(&temp2, (BcBigDig*) &inc1); 2328252884aeSStefan Eßer 2329252884aeSStefan Eßer if (bc_num_cmp(&temp, &vm.max) >= 0) { 2330252884aeSStefan Eßer bc_num_copy(&temp2, &temp); 2331252884aeSStefan Eßer bc_num_mod(&temp2, &vm.max, &temp, 0); 2332252884aeSStefan Eßer } 2333252884aeSStefan Eßer 2334252884aeSStefan Eßer // The if statement above ensures that temp is less than vm.max, which 2335252884aeSStefan Eßer // means that we can use bc_num_bigdig2() here. 2336252884aeSStefan Eßer bc_num_bigdig2(&temp, (BcBigDig*) &inc2); 2337252884aeSStefan Eßer } 2338252884aeSStefan Eßer else inc1 = inc2 = 0; 2339252884aeSStefan Eßer 2340252884aeSStefan Eßer bc_rand_seed(rng, state1, state2, inc1, inc2); 2341252884aeSStefan Eßer 2342252884aeSStefan Eßer err: 2343252884aeSStefan Eßer BC_SIG_MAYLOCK; 2344252884aeSStefan Eßer bc_num_free(&intn); 2345252884aeSStefan Eßer bc_num_free(&frac); 2346252884aeSStefan Eßer bc_num_free(&temp2); 2347252884aeSStefan Eßer bc_num_free(&temp); 2348252884aeSStefan Eßer BC_LONGJMP_CONT; 2349252884aeSStefan Eßer } 2350252884aeSStefan Eßer 2351252884aeSStefan Eßer void bc_num_createFromRNG(BcNum *restrict n, BcRNG *rng) { 2352252884aeSStefan Eßer 2353252884aeSStefan Eßer BcRand s1, s2, i1, i2; 235450696a6eSStefan Eßer BcNum conv, temp1, temp2, temp3; 2355252884aeSStefan Eßer BcDig temp1_num[BC_RAND_NUM_SIZE], temp2_num[BC_RAND_NUM_SIZE]; 2356252884aeSStefan Eßer BcDig conv_num[BC_NUM_BIGDIG_LOG10]; 2357252884aeSStefan Eßer 2358252884aeSStefan Eßer BC_SIG_LOCK; 2359252884aeSStefan Eßer 2360252884aeSStefan Eßer bc_num_init(&temp3, 2 * BC_RAND_NUM_SIZE); 2361252884aeSStefan Eßer 2362252884aeSStefan Eßer BC_SETJMP_LOCKED(err); 2363252884aeSStefan Eßer 2364252884aeSStefan Eßer BC_SIG_UNLOCK; 2365252884aeSStefan Eßer 2366252884aeSStefan Eßer bc_num_setup(&temp1, temp1_num, sizeof(temp1_num) / sizeof(BcDig)); 2367252884aeSStefan Eßer bc_num_setup(&temp2, temp2_num, sizeof(temp2_num) / sizeof(BcDig)); 2368252884aeSStefan Eßer bc_num_setup(&conv, conv_num, sizeof(conv_num) / sizeof(BcDig)); 2369252884aeSStefan Eßer 2370252884aeSStefan Eßer // This assert is here because it has to be true. It is also here to justify 237150696a6eSStefan Eßer // the assumption that vm.max2 is not zero. 2372252884aeSStefan Eßer assert(BC_NUM_NONZERO(&vm.max)); 2373252884aeSStefan Eßer 237450696a6eSStefan Eßer // Because this is true, we can just use BC_ERR_SIGNAL_ONLY() below when 237550696a6eSStefan Eßer // dividing by vm.max2. 237650696a6eSStefan Eßer assert(BC_NUM_NONZERO(&vm.max2)); 2377252884aeSStefan Eßer 2378252884aeSStefan Eßer bc_rand_getRands(rng, &s1, &s2, &i1, &i2); 2379252884aeSStefan Eßer 2380252884aeSStefan Eßer bc_num_bigdig2num(&conv, (BcBigDig) s2); 2381252884aeSStefan Eßer 238250696a6eSStefan Eßer assert(BC_NUM_RDX_VALID_NP(conv)); 238350696a6eSStefan Eßer 2384252884aeSStefan Eßer bc_num_mul(&conv, &vm.max, &temp1, 0); 2385252884aeSStefan Eßer 2386252884aeSStefan Eßer bc_num_bigdig2num(&conv, (BcBigDig) s1); 2387252884aeSStefan Eßer 2388252884aeSStefan Eßer bc_num_add(&conv, &temp1, &temp2, 0); 2389252884aeSStefan Eßer 239050696a6eSStefan Eßer bc_num_div(&temp2, &vm.max2, &temp3, BC_RAND_STATE_BITS); 2391252884aeSStefan Eßer 2392252884aeSStefan Eßer bc_num_bigdig2num(&conv, (BcBigDig) i2); 2393252884aeSStefan Eßer 239450696a6eSStefan Eßer assert(BC_NUM_RDX_VALID_NP(conv)); 239550696a6eSStefan Eßer 2396252884aeSStefan Eßer bc_num_mul(&conv, &vm.max, &temp1, 0); 2397252884aeSStefan Eßer 2398252884aeSStefan Eßer bc_num_bigdig2num(&conv, (BcBigDig) i1); 2399252884aeSStefan Eßer 2400252884aeSStefan Eßer bc_num_add(&conv, &temp1, &temp2, 0); 2401252884aeSStefan Eßer 2402252884aeSStefan Eßer bc_num_add(&temp2, &temp3, n, 0); 2403252884aeSStefan Eßer 240450696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(n)); 240550696a6eSStefan Eßer 2406252884aeSStefan Eßer err: 2407252884aeSStefan Eßer BC_SIG_MAYLOCK; 2408252884aeSStefan Eßer bc_num_free(&temp3); 2409252884aeSStefan Eßer BC_LONGJMP_CONT; 2410252884aeSStefan Eßer } 2411252884aeSStefan Eßer 2412252884aeSStefan Eßer void bc_num_irand(const BcNum *restrict a, BcNum *restrict b, 2413252884aeSStefan Eßer BcRNG *restrict rng) 2414252884aeSStefan Eßer { 2415252884aeSStefan Eßer BcRand r; 2416252884aeSStefan Eßer BcBigDig modl; 2417252884aeSStefan Eßer BcNum pow, pow2, cp, cp2, mod, temp1, temp2, rand; 2418252884aeSStefan Eßer BcNum *p1, *p2, *t1, *t2, *c1, *c2, *tmp; 2419252884aeSStefan Eßer BcDig rand_num[BC_NUM_BIGDIG_LOG10]; 2420252884aeSStefan Eßer bool carry; 2421252884aeSStefan Eßer ssize_t cmp; 2422252884aeSStefan Eßer 2423252884aeSStefan Eßer assert(a != b); 2424252884aeSStefan Eßer 242550696a6eSStefan Eßer if (BC_ERR(BC_NUM_NEG(a))) bc_vm_err(BC_ERR_MATH_NEGATIVE); 242650696a6eSStefan Eßer if (BC_ERR(BC_NUM_RDX_VAL(a))) bc_vm_err(BC_ERR_MATH_NON_INTEGER); 2427252884aeSStefan Eßer if (BC_NUM_ZERO(a) || BC_NUM_ONE(a)) return; 2428252884aeSStefan Eßer 2429252884aeSStefan Eßer cmp = bc_num_cmp(a, &vm.max); 2430252884aeSStefan Eßer 2431252884aeSStefan Eßer if (cmp <= 0) { 2432252884aeSStefan Eßer 2433252884aeSStefan Eßer BcRand bits = 0; 2434252884aeSStefan Eßer 2435252884aeSStefan Eßer if (cmp < 0) bc_num_bigdig2(a, (BcBigDig*) &bits); 2436252884aeSStefan Eßer 2437252884aeSStefan Eßer // This condition means that bits is a power of 2. In that case, we 2438252884aeSStefan Eßer // can just grab a full-size int and mask out the unneeded bits. 2439252884aeSStefan Eßer // Also, this condition says that 0 is a power of 2, which works for 2440252884aeSStefan Eßer // us, since a value of 0 means a == rng->max. The bitmask will mask 2441252884aeSStefan Eßer // nothing in that case as well. 2442252884aeSStefan Eßer if (!(bits & (bits - 1))) r = bc_rand_int(rng) & (bits - 1); 2443252884aeSStefan Eßer else r = bc_rand_bounded(rng, bits); 2444252884aeSStefan Eßer 2445252884aeSStefan Eßer // We made sure that r is less than vm.max, 2446252884aeSStefan Eßer // so we can use bc_num_bigdig2() here. 2447252884aeSStefan Eßer bc_num_bigdig2num(b, r); 2448252884aeSStefan Eßer 2449252884aeSStefan Eßer return; 2450252884aeSStefan Eßer } 2451252884aeSStefan Eßer 2452252884aeSStefan Eßer // In the case where a is less than rng->max, we have to make sure we have 2453252884aeSStefan Eßer // an exclusive bound. This ensures that it happens. (See below.) 2454252884aeSStefan Eßer carry = (cmp < 0); 2455252884aeSStefan Eßer 2456252884aeSStefan Eßer BC_SIG_LOCK; 2457252884aeSStefan Eßer 2458252884aeSStefan Eßer bc_num_createCopy(&cp, a); 2459252884aeSStefan Eßer 2460252884aeSStefan Eßer bc_num_init(&cp2, cp.len); 2461252884aeSStefan Eßer bc_num_init(&mod, BC_NUM_BIGDIG_LOG10); 2462252884aeSStefan Eßer bc_num_init(&temp1, BC_NUM_DEF_SIZE); 2463252884aeSStefan Eßer bc_num_init(&temp2, BC_NUM_DEF_SIZE); 2464252884aeSStefan Eßer bc_num_init(&pow2, BC_NUM_DEF_SIZE); 2465252884aeSStefan Eßer bc_num_init(&pow, BC_NUM_DEF_SIZE); 2466252884aeSStefan Eßer bc_num_one(&pow); 2467252884aeSStefan Eßer bc_num_setup(&rand, rand_num, sizeof(rand_num) / sizeof(BcDig)); 2468252884aeSStefan Eßer 2469252884aeSStefan Eßer BC_SETJMP_LOCKED(err); 2470252884aeSStefan Eßer 2471252884aeSStefan Eßer BC_SIG_UNLOCK; 2472252884aeSStefan Eßer 2473252884aeSStefan Eßer p1 = &pow; 2474252884aeSStefan Eßer p2 = &pow2; 2475252884aeSStefan Eßer t1 = &temp1; 2476252884aeSStefan Eßer t2 = &temp2; 2477252884aeSStefan Eßer c1 = &cp; 2478252884aeSStefan Eßer c2 = &cp2; 2479252884aeSStefan Eßer 2480252884aeSStefan Eßer // This assert is here because it has to be true. It is also here to justify 248150696a6eSStefan Eßer // the use of BC_ERR_SIGNAL_ONLY() on each of the divmod's and mod's below. 2482252884aeSStefan Eßer assert(BC_NUM_NONZERO(&vm.max)); 2483252884aeSStefan Eßer 2484252884aeSStefan Eßer while (BC_NUM_NONZERO(c1)) { 2485252884aeSStefan Eßer 2486252884aeSStefan Eßer bc_num_divmod(c1, &vm.max, c2, &mod, 0); 2487252884aeSStefan Eßer 2488252884aeSStefan Eßer // Because mod is the mod of vm.max, it is guaranteed to be smaller, 2489252884aeSStefan Eßer // which means we can use bc_num_bigdig2() here. 2490252884aeSStefan Eßer bc_num_bigdig(&mod, &modl); 2491252884aeSStefan Eßer 2492252884aeSStefan Eßer if (bc_num_cmp(c1, &vm.max) < 0) { 2493252884aeSStefan Eßer 2494252884aeSStefan Eßer // In this case, if there is no carry, then we know we can generate 2495252884aeSStefan Eßer // an integer *equal* to modl. Thus, we add one if there is no 2496252884aeSStefan Eßer // carry. Otherwise, we add zero, and we are still bounded properly. 2497252884aeSStefan Eßer // Since the last portion is guaranteed to be greater than 1, we 2498252884aeSStefan Eßer // know modl isn't 0 unless there is no carry. 2499252884aeSStefan Eßer modl += !carry; 2500252884aeSStefan Eßer 2501252884aeSStefan Eßer if (modl == 1) r = 0; 2502252884aeSStefan Eßer else if (!modl) r = bc_rand_int(rng); 2503252884aeSStefan Eßer else r = bc_rand_bounded(rng, (BcRand) modl); 2504252884aeSStefan Eßer } 2505252884aeSStefan Eßer else { 2506252884aeSStefan Eßer if (modl) modl -= carry; 2507252884aeSStefan Eßer r = bc_rand_int(rng); 2508252884aeSStefan Eßer carry = (r >= (BcRand) modl); 2509252884aeSStefan Eßer } 2510252884aeSStefan Eßer 2511252884aeSStefan Eßer bc_num_bigdig2num(&rand, r); 2512252884aeSStefan Eßer 251350696a6eSStefan Eßer assert(BC_NUM_RDX_VALID_NP(rand)); 251450696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(p1)); 251550696a6eSStefan Eßer 2516252884aeSStefan Eßer bc_num_mul(&rand, p1, p2, 0); 2517252884aeSStefan Eßer bc_num_add(p2, t1, t2, 0); 2518252884aeSStefan Eßer 2519252884aeSStefan Eßer if (BC_NUM_NONZERO(c2)) { 2520252884aeSStefan Eßer 252150696a6eSStefan Eßer assert(BC_NUM_RDX_VALID_NP(vm.max)); 252250696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(p1)); 252350696a6eSStefan Eßer 2524252884aeSStefan Eßer bc_num_mul(&vm.max, p1, p2, 0); 2525252884aeSStefan Eßer 2526252884aeSStefan Eßer tmp = p1; 2527252884aeSStefan Eßer p1 = p2; 2528252884aeSStefan Eßer p2 = tmp; 2529252884aeSStefan Eßer 2530252884aeSStefan Eßer tmp = c1; 2531252884aeSStefan Eßer c1 = c2; 2532252884aeSStefan Eßer c2 = tmp; 2533252884aeSStefan Eßer } 2534252884aeSStefan Eßer else c1 = c2; 2535252884aeSStefan Eßer 2536252884aeSStefan Eßer tmp = t1; 2537252884aeSStefan Eßer t1 = t2; 2538252884aeSStefan Eßer t2 = tmp; 2539252884aeSStefan Eßer } 2540252884aeSStefan Eßer 2541252884aeSStefan Eßer bc_num_copy(b, t1); 2542252884aeSStefan Eßer bc_num_clean(b); 2543252884aeSStefan Eßer 254450696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(b)); 254550696a6eSStefan Eßer 2546252884aeSStefan Eßer err: 2547252884aeSStefan Eßer BC_SIG_MAYLOCK; 2548252884aeSStefan Eßer bc_num_free(&pow); 2549252884aeSStefan Eßer bc_num_free(&pow2); 2550252884aeSStefan Eßer bc_num_free(&temp2); 2551252884aeSStefan Eßer bc_num_free(&temp1); 2552252884aeSStefan Eßer bc_num_free(&mod); 2553252884aeSStefan Eßer bc_num_free(&cp2); 2554252884aeSStefan Eßer bc_num_free(&cp); 2555252884aeSStefan Eßer BC_LONGJMP_CONT; 2556252884aeSStefan Eßer } 25573aa99676SStefan Eßer #endif // BC_ENABLE_EXTRA_MATH && BC_ENABLE_RAND 2558252884aeSStefan Eßer 2559252884aeSStefan Eßer size_t bc_num_addReq(const BcNum *a, const BcNum *b, size_t scale) { 2560252884aeSStefan Eßer 2561252884aeSStefan Eßer size_t aint, bint, ardx, brdx; 2562252884aeSStefan Eßer 2563252884aeSStefan Eßer BC_UNUSED(scale); 2564252884aeSStefan Eßer 256550696a6eSStefan Eßer ardx = BC_NUM_RDX_VAL(a); 2566252884aeSStefan Eßer aint = bc_num_int(a); 2567252884aeSStefan Eßer assert(aint <= a->len && ardx <= a->len); 2568252884aeSStefan Eßer 256950696a6eSStefan Eßer brdx = BC_NUM_RDX_VAL(b); 2570252884aeSStefan Eßer bint = bc_num_int(b); 2571252884aeSStefan Eßer assert(bint <= b->len && brdx <= b->len); 2572252884aeSStefan Eßer 2573252884aeSStefan Eßer ardx = BC_MAX(ardx, brdx); 2574252884aeSStefan Eßer aint = BC_MAX(aint, bint); 2575252884aeSStefan Eßer 2576252884aeSStefan Eßer return bc_vm_growSize(bc_vm_growSize(ardx, aint), 1); 2577252884aeSStefan Eßer } 2578252884aeSStefan Eßer 2579252884aeSStefan Eßer size_t bc_num_mulReq(const BcNum *a, const BcNum *b, size_t scale) { 2580252884aeSStefan Eßer size_t max, rdx; 258150696a6eSStefan Eßer rdx = bc_vm_growSize(BC_NUM_RDX_VAL(a), BC_NUM_RDX_VAL(b)); 2582252884aeSStefan Eßer max = BC_NUM_RDX(scale); 2583252884aeSStefan Eßer max = bc_vm_growSize(BC_MAX(max, rdx), 1); 2584252884aeSStefan Eßer rdx = bc_vm_growSize(bc_vm_growSize(bc_num_int(a), bc_num_int(b)), max); 2585252884aeSStefan Eßer return rdx; 2586252884aeSStefan Eßer } 2587252884aeSStefan Eßer 258850696a6eSStefan Eßer size_t bc_num_divReq(const BcNum *a, const BcNum *b, size_t scale) { 258950696a6eSStefan Eßer size_t max, rdx; 259050696a6eSStefan Eßer rdx = bc_vm_growSize(BC_NUM_RDX_VAL(a), BC_NUM_RDX_VAL(b)); 259150696a6eSStefan Eßer max = BC_NUM_RDX(scale); 259250696a6eSStefan Eßer max = bc_vm_growSize(BC_MAX(max, rdx), 1); 259350696a6eSStefan Eßer rdx = bc_vm_growSize(bc_num_int(a), max); 259450696a6eSStefan Eßer return rdx; 259550696a6eSStefan Eßer } 259650696a6eSStefan Eßer 2597252884aeSStefan Eßer size_t bc_num_powReq(const BcNum *a, const BcNum *b, size_t scale) { 2598252884aeSStefan Eßer BC_UNUSED(scale); 2599252884aeSStefan Eßer return bc_vm_growSize(bc_vm_growSize(a->len, b->len), 1); 2600252884aeSStefan Eßer } 2601252884aeSStefan Eßer 2602252884aeSStefan Eßer #if BC_ENABLE_EXTRA_MATH 2603252884aeSStefan Eßer size_t bc_num_placesReq(const BcNum *a, const BcNum *b, size_t scale) { 2604252884aeSStefan Eßer BC_UNUSED(scale); 260550696a6eSStefan Eßer return a->len + b->len - BC_NUM_RDX_VAL(a) - BC_NUM_RDX_VAL(b); 2606252884aeSStefan Eßer } 2607252884aeSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH 2608252884aeSStefan Eßer 2609252884aeSStefan Eßer void bc_num_add(BcNum *a, BcNum *b, BcNum *c, size_t scale) { 261050696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(a)); 261150696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(b)); 2612252884aeSStefan Eßer bc_num_binary(a, b, c, false, bc_num_as, bc_num_addReq(a, b, scale)); 2613252884aeSStefan Eßer } 2614252884aeSStefan Eßer 2615252884aeSStefan Eßer void bc_num_sub(BcNum *a, BcNum *b, BcNum *c, size_t scale) { 261650696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(a)); 261750696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(b)); 2618252884aeSStefan Eßer bc_num_binary(a, b, c, true, bc_num_as, bc_num_addReq(a, b, scale)); 2619252884aeSStefan Eßer } 2620252884aeSStefan Eßer 2621252884aeSStefan Eßer void bc_num_mul(BcNum *a, BcNum *b, BcNum *c, size_t scale) { 262250696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(a)); 262350696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(b)); 2624252884aeSStefan Eßer bc_num_binary(a, b, c, scale, bc_num_m, bc_num_mulReq(a, b, scale)); 2625252884aeSStefan Eßer } 2626252884aeSStefan Eßer 2627252884aeSStefan Eßer void bc_num_div(BcNum *a, BcNum *b, BcNum *c, size_t scale) { 262850696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(a)); 262950696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(b)); 263050696a6eSStefan Eßer bc_num_binary(a, b, c, scale, bc_num_d, bc_num_divReq(a, b, scale)); 2631252884aeSStefan Eßer } 2632252884aeSStefan Eßer 2633252884aeSStefan Eßer void bc_num_mod(BcNum *a, BcNum *b, BcNum *c, size_t scale) { 263450696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(a)); 263550696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(b)); 263650696a6eSStefan Eßer bc_num_binary(a, b, c, scale, bc_num_rem, bc_num_divReq(a, b, scale)); 2637252884aeSStefan Eßer } 2638252884aeSStefan Eßer 2639252884aeSStefan Eßer void bc_num_pow(BcNum *a, BcNum *b, BcNum *c, size_t scale) { 264050696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(a)); 264150696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(b)); 2642252884aeSStefan Eßer bc_num_binary(a, b, c, scale, bc_num_p, bc_num_powReq(a, b, scale)); 2643252884aeSStefan Eßer } 2644252884aeSStefan Eßer 2645252884aeSStefan Eßer #if BC_ENABLE_EXTRA_MATH 2646252884aeSStefan Eßer void bc_num_places(BcNum *a, BcNum *b, BcNum *c, size_t scale) { 264750696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(a)); 264850696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(b)); 2649252884aeSStefan Eßer bc_num_binary(a, b, c, scale, bc_num_place, bc_num_placesReq(a, b, scale)); 2650252884aeSStefan Eßer } 2651252884aeSStefan Eßer 2652252884aeSStefan Eßer void bc_num_lshift(BcNum *a, BcNum *b, BcNum *c, size_t scale) { 265350696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(a)); 265450696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(b)); 2655252884aeSStefan Eßer bc_num_binary(a, b, c, scale, bc_num_left, bc_num_placesReq(a, b, scale)); 2656252884aeSStefan Eßer } 2657252884aeSStefan Eßer 2658252884aeSStefan Eßer void bc_num_rshift(BcNum *a, BcNum *b, BcNum *c, size_t scale) { 265950696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(a)); 266050696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(b)); 2661252884aeSStefan Eßer bc_num_binary(a, b, c, scale, bc_num_right, bc_num_placesReq(a, b, scale)); 2662252884aeSStefan Eßer } 2663252884aeSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH 2664252884aeSStefan Eßer 2665252884aeSStefan Eßer void bc_num_sqrt(BcNum *restrict a, BcNum *restrict b, size_t scale) { 2666252884aeSStefan Eßer 2667252884aeSStefan Eßer BcNum num1, num2, half, f, fprime, *x0, *x1, *temp; 266810328f8bSStefan Eßer size_t pow, len, rdx, req, resscale; 2669252884aeSStefan Eßer BcDig half_digs[1]; 2670252884aeSStefan Eßer 2671252884aeSStefan Eßer assert(a != NULL && b != NULL && a != b); 2672252884aeSStefan Eßer 267350696a6eSStefan Eßer if (BC_ERR(BC_NUM_NEG(a))) bc_vm_err(BC_ERR_MATH_NEGATIVE); 2674252884aeSStefan Eßer 2675252884aeSStefan Eßer if (a->scale > scale) scale = a->scale; 2676252884aeSStefan Eßer 2677252884aeSStefan Eßer len = bc_vm_growSize(bc_num_intDigits(a), 1); 2678252884aeSStefan Eßer rdx = BC_NUM_RDX(scale); 267950696a6eSStefan Eßer req = bc_vm_growSize(BC_MAX(rdx, BC_NUM_RDX_VAL(a)), len >> 1); 2680252884aeSStefan Eßer 2681252884aeSStefan Eßer BC_SIG_LOCK; 2682252884aeSStefan Eßer 2683252884aeSStefan Eßer bc_num_init(b, bc_vm_growSize(req, 1)); 2684252884aeSStefan Eßer 2685252884aeSStefan Eßer BC_SIG_UNLOCK; 2686252884aeSStefan Eßer 268750696a6eSStefan Eßer assert(a != NULL && b != NULL && a != b); 268850696a6eSStefan Eßer assert(a->num != NULL && b->num != NULL); 268950696a6eSStefan Eßer 2690252884aeSStefan Eßer if (BC_NUM_ZERO(a)) { 2691252884aeSStefan Eßer bc_num_setToZero(b, scale); 2692252884aeSStefan Eßer return; 2693252884aeSStefan Eßer } 2694252884aeSStefan Eßer if (BC_NUM_ONE(a)) { 2695252884aeSStefan Eßer bc_num_one(b); 2696252884aeSStefan Eßer bc_num_extend(b, scale); 2697252884aeSStefan Eßer return; 2698252884aeSStefan Eßer } 2699252884aeSStefan Eßer 2700252884aeSStefan Eßer rdx = BC_NUM_RDX(scale); 270150696a6eSStefan Eßer rdx = BC_MAX(rdx, BC_NUM_RDX_VAL(a)); 2702252884aeSStefan Eßer len = bc_vm_growSize(a->len, rdx); 2703252884aeSStefan Eßer 2704252884aeSStefan Eßer BC_SIG_LOCK; 2705252884aeSStefan Eßer 2706252884aeSStefan Eßer bc_num_init(&num1, len); 2707252884aeSStefan Eßer bc_num_init(&num2, len); 2708252884aeSStefan Eßer bc_num_setup(&half, half_digs, sizeof(half_digs) / sizeof(BcDig)); 2709252884aeSStefan Eßer 2710252884aeSStefan Eßer bc_num_one(&half); 2711252884aeSStefan Eßer half.num[0] = BC_BASE_POW / 2; 2712252884aeSStefan Eßer half.len = 1; 271350696a6eSStefan Eßer BC_NUM_RDX_SET_NP(half, 1); 2714252884aeSStefan Eßer half.scale = 1; 2715252884aeSStefan Eßer 2716252884aeSStefan Eßer bc_num_init(&f, len); 2717252884aeSStefan Eßer bc_num_init(&fprime, len); 2718252884aeSStefan Eßer 2719252884aeSStefan Eßer BC_SETJMP_LOCKED(err); 2720252884aeSStefan Eßer 2721252884aeSStefan Eßer BC_SIG_UNLOCK; 2722252884aeSStefan Eßer 2723252884aeSStefan Eßer x0 = &num1; 2724252884aeSStefan Eßer x1 = &num2; 2725252884aeSStefan Eßer 2726252884aeSStefan Eßer bc_num_one(x0); 2727252884aeSStefan Eßer pow = bc_num_intDigits(a); 2728252884aeSStefan Eßer 2729252884aeSStefan Eßer if (pow) { 2730252884aeSStefan Eßer 2731252884aeSStefan Eßer if (pow & 1) x0->num[0] = 2; 2732252884aeSStefan Eßer else x0->num[0] = 6; 2733252884aeSStefan Eßer 2734252884aeSStefan Eßer pow -= 2 - (pow & 1); 2735252884aeSStefan Eßer bc_num_shiftLeft(x0, pow / 2); 2736252884aeSStefan Eßer } 2737252884aeSStefan Eßer 273850696a6eSStefan Eßer // I can set the rdx here directly because neg should be false. 273910328f8bSStefan Eßer x0->scale = x0->rdx = 0; 2740252884aeSStefan Eßer resscale = (scale + BC_BASE_DIGS) + 2; 2741252884aeSStefan Eßer 2742252884aeSStefan Eßer while (bc_num_cmp(x1, x0)) { 2743252884aeSStefan Eßer 2744252884aeSStefan Eßer assert(BC_NUM_NONZERO(x0)); 2745252884aeSStefan Eßer 2746252884aeSStefan Eßer bc_num_div(a, x0, &f, resscale); 2747252884aeSStefan Eßer bc_num_add(x0, &f, &fprime, resscale); 274850696a6eSStefan Eßer 274950696a6eSStefan Eßer assert(BC_NUM_RDX_VALID_NP(fprime)); 275050696a6eSStefan Eßer assert(BC_NUM_RDX_VALID_NP(half)); 275150696a6eSStefan Eßer 2752252884aeSStefan Eßer bc_num_mul(&fprime, &half, x1, resscale); 2753252884aeSStefan Eßer 2754252884aeSStefan Eßer temp = x0; 2755252884aeSStefan Eßer x0 = x1; 2756252884aeSStefan Eßer x1 = temp; 2757252884aeSStefan Eßer } 2758252884aeSStefan Eßer 2759252884aeSStefan Eßer bc_num_copy(b, x0); 2760252884aeSStefan Eßer if (b->scale > scale) bc_num_truncate(b, b->scale - scale); 2761252884aeSStefan Eßer 276250696a6eSStefan Eßer assert(!BC_NUM_NEG(b) || BC_NUM_NONZERO(b)); 276350696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(b)); 276450696a6eSStefan Eßer assert(BC_NUM_RDX_VAL(b) <= b->len || !b->len); 276550696a6eSStefan Eßer assert(!b->len || b->num[b->len - 1] || BC_NUM_RDX_VAL(b) == b->len); 2766252884aeSStefan Eßer 2767252884aeSStefan Eßer err: 2768252884aeSStefan Eßer BC_SIG_MAYLOCK; 2769252884aeSStefan Eßer bc_num_free(&fprime); 2770252884aeSStefan Eßer bc_num_free(&f); 2771252884aeSStefan Eßer bc_num_free(&num2); 2772252884aeSStefan Eßer bc_num_free(&num1); 2773252884aeSStefan Eßer BC_LONGJMP_CONT; 2774252884aeSStefan Eßer } 2775252884aeSStefan Eßer 2776252884aeSStefan Eßer void bc_num_divmod(BcNum *a, BcNum *b, BcNum *c, BcNum *d, size_t scale) { 2777252884aeSStefan Eßer 2778252884aeSStefan Eßer size_t ts, len; 277950696a6eSStefan Eßer BcNum *ptr_a, num2; 278050696a6eSStefan Eßer bool init = false; 2781252884aeSStefan Eßer 2782252884aeSStefan Eßer ts = BC_MAX(scale + b->scale, a->scale); 2783252884aeSStefan Eßer len = bc_num_mulReq(a, b, ts); 2784252884aeSStefan Eßer 2785252884aeSStefan Eßer assert(a != NULL && b != NULL && c != NULL && d != NULL); 2786252884aeSStefan Eßer assert(c != d && a != d && b != d && b != c); 2787252884aeSStefan Eßer 2788252884aeSStefan Eßer if (c == a) { 2789252884aeSStefan Eßer 2790252884aeSStefan Eßer memcpy(&num2, c, sizeof(BcNum)); 2791252884aeSStefan Eßer ptr_a = &num2; 2792252884aeSStefan Eßer 2793252884aeSStefan Eßer BC_SIG_LOCK; 2794252884aeSStefan Eßer 2795252884aeSStefan Eßer bc_num_init(c, len); 2796252884aeSStefan Eßer 2797252884aeSStefan Eßer init = true; 2798252884aeSStefan Eßer 2799252884aeSStefan Eßer BC_SETJMP_LOCKED(err); 2800252884aeSStefan Eßer 2801252884aeSStefan Eßer BC_SIG_UNLOCK; 2802252884aeSStefan Eßer } 2803252884aeSStefan Eßer else { 2804252884aeSStefan Eßer ptr_a = a; 2805252884aeSStefan Eßer bc_num_expand(c, len); 2806252884aeSStefan Eßer } 2807252884aeSStefan Eßer 280850696a6eSStefan Eßer if (BC_NUM_NONZERO(a) && !BC_NUM_RDX_VAL(a) && 280950696a6eSStefan Eßer !BC_NUM_RDX_VAL(b) && b->len == 1 && !scale) 281050696a6eSStefan Eßer { 2811252884aeSStefan Eßer BcBigDig rem; 2812252884aeSStefan Eßer 2813252884aeSStefan Eßer bc_num_divArray(ptr_a, (BcBigDig) b->num[0], c, &rem); 2814252884aeSStefan Eßer 2815252884aeSStefan Eßer assert(rem < BC_BASE_POW); 2816252884aeSStefan Eßer 2817252884aeSStefan Eßer d->num[0] = (BcDig) rem; 2818252884aeSStefan Eßer d->len = (rem != 0); 2819252884aeSStefan Eßer } 2820252884aeSStefan Eßer else bc_num_r(ptr_a, b, c, d, scale, ts); 2821252884aeSStefan Eßer 282250696a6eSStefan Eßer assert(!BC_NUM_NEG(c) || BC_NUM_NONZERO(c)); 282350696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(c)); 282450696a6eSStefan Eßer assert(BC_NUM_RDX_VAL(c) <= c->len || !c->len); 282550696a6eSStefan Eßer assert(!c->len || c->num[c->len - 1] || BC_NUM_RDX_VAL(c) == c->len); 282650696a6eSStefan Eßer assert(!BC_NUM_NEG(d) || BC_NUM_NONZERO(d)); 282750696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(d)); 282850696a6eSStefan Eßer assert(BC_NUM_RDX_VAL(d) <= d->len || !d->len); 282950696a6eSStefan Eßer assert(!d->len || d->num[d->len - 1] || BC_NUM_RDX_VAL(d) == d->len); 2830252884aeSStefan Eßer 2831252884aeSStefan Eßer err: 2832252884aeSStefan Eßer if (init) { 2833252884aeSStefan Eßer BC_SIG_MAYLOCK; 2834252884aeSStefan Eßer bc_num_free(&num2); 2835252884aeSStefan Eßer BC_LONGJMP_CONT; 2836252884aeSStefan Eßer } 2837252884aeSStefan Eßer } 2838252884aeSStefan Eßer 2839252884aeSStefan Eßer #if DC_ENABLED 2840252884aeSStefan Eßer void bc_num_modexp(BcNum *a, BcNum *b, BcNum *c, BcNum *restrict d) { 2841252884aeSStefan Eßer 2842252884aeSStefan Eßer BcNum base, exp, two, temp; 2843252884aeSStefan Eßer BcDig two_digs[2]; 2844252884aeSStefan Eßer 2845252884aeSStefan Eßer assert(a != NULL && b != NULL && c != NULL && d != NULL); 2846252884aeSStefan Eßer assert(a != d && b != d && c != d); 2847252884aeSStefan Eßer 284850696a6eSStefan Eßer if (BC_ERR(BC_NUM_ZERO(c))) bc_vm_err(BC_ERR_MATH_DIVIDE_BY_ZERO); 284950696a6eSStefan Eßer if (BC_ERR(BC_NUM_NEG(b))) bc_vm_err(BC_ERR_MATH_NEGATIVE); 285050696a6eSStefan Eßer if (BC_ERR(BC_NUM_RDX_VAL(a) || BC_NUM_RDX_VAL(b) || BC_NUM_RDX_VAL(c))) 285150696a6eSStefan Eßer bc_vm_err(BC_ERR_MATH_NON_INTEGER); 2852252884aeSStefan Eßer 2853252884aeSStefan Eßer bc_num_expand(d, c->len); 2854252884aeSStefan Eßer 2855252884aeSStefan Eßer BC_SIG_LOCK; 2856252884aeSStefan Eßer 2857252884aeSStefan Eßer bc_num_init(&base, c->len); 2858252884aeSStefan Eßer bc_num_setup(&two, two_digs, sizeof(two_digs) / sizeof(BcDig)); 2859252884aeSStefan Eßer bc_num_init(&temp, b->len + 1); 2860252884aeSStefan Eßer bc_num_createCopy(&exp, b); 2861252884aeSStefan Eßer 2862252884aeSStefan Eßer BC_SETJMP_LOCKED(err); 2863252884aeSStefan Eßer 2864252884aeSStefan Eßer BC_SIG_UNLOCK; 2865252884aeSStefan Eßer 2866252884aeSStefan Eßer bc_num_one(&two); 2867252884aeSStefan Eßer two.num[0] = 2; 2868252884aeSStefan Eßer bc_num_one(d); 2869252884aeSStefan Eßer 2870252884aeSStefan Eßer // We already checked for 0. 2871252884aeSStefan Eßer bc_num_rem(a, c, &base, 0); 2872252884aeSStefan Eßer 2873252884aeSStefan Eßer while (BC_NUM_NONZERO(&exp)) { 2874252884aeSStefan Eßer 2875252884aeSStefan Eßer // Num two cannot be 0, so no errors. 2876252884aeSStefan Eßer bc_num_divmod(&exp, &two, &exp, &temp, 0); 2877252884aeSStefan Eßer 287850696a6eSStefan Eßer if (BC_NUM_ONE(&temp) && !BC_NUM_NEG_NP(temp)) { 287950696a6eSStefan Eßer 288050696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(d)); 288150696a6eSStefan Eßer assert(BC_NUM_RDX_VALID_NP(base)); 2882252884aeSStefan Eßer 2883252884aeSStefan Eßer bc_num_mul(d, &base, &temp, 0); 2884252884aeSStefan Eßer 2885252884aeSStefan Eßer // We already checked for 0. 2886252884aeSStefan Eßer bc_num_rem(&temp, c, d, 0); 2887252884aeSStefan Eßer } 2888252884aeSStefan Eßer 288950696a6eSStefan Eßer assert(BC_NUM_RDX_VALID_NP(base)); 289050696a6eSStefan Eßer 2891252884aeSStefan Eßer bc_num_mul(&base, &base, &temp, 0); 2892252884aeSStefan Eßer 2893252884aeSStefan Eßer // We already checked for 0. 2894252884aeSStefan Eßer bc_num_rem(&temp, c, &base, 0); 2895252884aeSStefan Eßer } 2896252884aeSStefan Eßer 2897252884aeSStefan Eßer err: 2898252884aeSStefan Eßer BC_SIG_MAYLOCK; 2899252884aeSStefan Eßer bc_num_free(&exp); 2900252884aeSStefan Eßer bc_num_free(&temp); 2901252884aeSStefan Eßer bc_num_free(&base); 2902252884aeSStefan Eßer BC_LONGJMP_CONT; 290350696a6eSStefan Eßer assert(!BC_NUM_NEG(d) || d->len); 290450696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(d)); 290550696a6eSStefan Eßer assert(!d->len || d->num[d->len - 1] || BC_NUM_RDX_VAL(d) == d->len); 2906252884aeSStefan Eßer } 2907252884aeSStefan Eßer #endif // DC_ENABLED 2908252884aeSStefan Eßer 2909252884aeSStefan Eßer #if BC_DEBUG_CODE 2910252884aeSStefan Eßer void bc_num_printDebug(const BcNum *n, const char *name, bool emptyline) { 2911*7e5c51e5SStefan Eßer bc_file_puts(&vm.fout, bc_flush_none, name); 2912*7e5c51e5SStefan Eßer bc_file_puts(&vm.fout, bc_flush_none, ": "); 2913252884aeSStefan Eßer bc_num_printDecimal(n); 2914*7e5c51e5SStefan Eßer bc_file_putchar(&vm.fout, bc_flush_err, '\n'); 2915*7e5c51e5SStefan Eßer if (emptyline) bc_file_putchar(&vm.fout, bc_flush_err, '\n'); 2916252884aeSStefan Eßer vm.nchars = 0; 2917252884aeSStefan Eßer } 2918252884aeSStefan Eßer 2919252884aeSStefan Eßer void bc_num_printDigs(const BcDig *n, size_t len, bool emptyline) { 2920252884aeSStefan Eßer 2921252884aeSStefan Eßer size_t i; 2922252884aeSStefan Eßer 2923252884aeSStefan Eßer for (i = len - 1; i < len; --i) 2924252884aeSStefan Eßer bc_file_printf(&vm.fout, " %lu", (unsigned long) n[i]); 2925252884aeSStefan Eßer 2926*7e5c51e5SStefan Eßer bc_file_putchar(&vm.fout, bc_flush_err, '\n'); 2927*7e5c51e5SStefan Eßer if (emptyline) bc_file_putchar(&vm.fout, bc_flush_err, '\n'); 2928252884aeSStefan Eßer vm.nchars = 0; 2929252884aeSStefan Eßer } 2930252884aeSStefan Eßer 2931252884aeSStefan Eßer void bc_num_printWithDigs(const BcNum *n, const char *name, bool emptyline) { 2932*7e5c51e5SStefan Eßer bc_file_puts(&vm.fout, bc_flush_none, name); 2933252884aeSStefan Eßer bc_file_printf(&vm.fout, " len: %zu, rdx: %zu, scale: %zu\n", 293450696a6eSStefan Eßer name, n->len, BC_NUM_RDX_VAL(n), n->scale); 2935252884aeSStefan Eßer bc_num_printDigs(n->num, n->len, emptyline); 2936252884aeSStefan Eßer } 2937252884aeSStefan Eßer 2938252884aeSStefan Eßer void bc_num_dump(const char *varname, const BcNum *n) { 2939252884aeSStefan Eßer 2940252884aeSStefan Eßer ulong i, scale = n->scale; 2941252884aeSStefan Eßer 2942252884aeSStefan Eßer bc_file_printf(&vm.ferr, "\n%s = %s", varname, 294350696a6eSStefan Eßer n->len ? (BC_NUM_NEG(n) ? "-" : "+") : "0 "); 2944252884aeSStefan Eßer 2945252884aeSStefan Eßer for (i = n->len - 1; i < n->len; --i) { 2946252884aeSStefan Eßer 2947*7e5c51e5SStefan Eßer if (i + 1 == BC_NUM_RDX_VAL(n)) 2948*7e5c51e5SStefan Eßer bc_file_puts(&vm.ferr, bc_flush_none, ". "); 2949252884aeSStefan Eßer 295050696a6eSStefan Eßer if (scale / BC_BASE_DIGS != BC_NUM_RDX_VAL(n) - i - 1) 2951252884aeSStefan Eßer bc_file_printf(&vm.ferr, "%lu ", (unsigned long) n->num[i]); 2952252884aeSStefan Eßer else { 2953252884aeSStefan Eßer 2954252884aeSStefan Eßer int mod = scale % BC_BASE_DIGS; 2955252884aeSStefan Eßer int d = BC_BASE_DIGS - mod; 2956252884aeSStefan Eßer BcDig div; 2957252884aeSStefan Eßer 2958252884aeSStefan Eßer if (mod != 0) { 2959252884aeSStefan Eßer div = n->num[i] / ((BcDig) bc_num_pow10[(ulong) d]); 2960252884aeSStefan Eßer bc_file_printf(&vm.ferr, "%lu", (unsigned long) div); 2961252884aeSStefan Eßer } 2962252884aeSStefan Eßer 2963252884aeSStefan Eßer div = n->num[i] % ((BcDig) bc_num_pow10[(ulong) d]); 2964252884aeSStefan Eßer bc_file_printf(&vm.ferr, " ' %lu ", (unsigned long) div); 2965252884aeSStefan Eßer } 2966252884aeSStefan Eßer } 2967252884aeSStefan Eßer 2968252884aeSStefan Eßer bc_file_printf(&vm.ferr, "(%zu | %zu.%zu / %zu) %lu\n", 296950696a6eSStefan Eßer n->scale, n->len, BC_NUM_RDX_VAL(n), n->cap, 2970252884aeSStefan Eßer (unsigned long) (void*) n->num); 2971*7e5c51e5SStefan Eßer 2972*7e5c51e5SStefan Eßer bc_file_flush(&vm.ferr, bc_flush_err); 2973252884aeSStefan Eßer } 2974252884aeSStefan Eßer #endif // BC_DEBUG_CODE 2975