1252884aeSStefan Eßer /* 2252884aeSStefan Eßer * ***************************************************************************** 3252884aeSStefan Eßer * 43aa99676SStefan Eßer * SPDX-License-Identifier: BSD-2-Clause 5252884aeSStefan Eßer * 6d101cdd6SStefan Eßer * Copyright (c) 2018-2023 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> 47d101cdd6SStefan Eßer #if BC_ENABLE_LIBRARY 48d101cdd6SStefan Eßer #include <library.h> 49d101cdd6SStefan Eßer #endif // BC_ENABLE_LIBRARY 50252884aeSStefan Eßer 5144d4804dSStefan Eßer // Before you try to understand this code, see the development manual 5244d4804dSStefan Eßer // (manuals/development.md#numbers). 5344d4804dSStefan Eßer 5478bc019dSStefan Eßer static void 5578bc019dSStefan Eßer bc_num_m(BcNum* a, BcNum* b, BcNum* restrict c, size_t scale); 56252884aeSStefan Eßer 5744d4804dSStefan Eßer /** 5844d4804dSStefan Eßer * Multiply two numbers and throw a math error if they overflow. 5944d4804dSStefan Eßer * @param a The first operand. 6044d4804dSStefan Eßer * @param b The second operand. 6144d4804dSStefan Eßer * @return The product of the two operands. 6244d4804dSStefan Eßer */ 6378bc019dSStefan Eßer static inline size_t 6478bc019dSStefan Eßer bc_num_mulOverflow(size_t a, size_t b) 6578bc019dSStefan Eßer { 6644d4804dSStefan Eßer size_t res = a * b; 6744d4804dSStefan Eßer if (BC_ERR(BC_VM_MUL_OVERFLOW(a, b, res))) bc_err(BC_ERR_MATH_OVERFLOW); 6844d4804dSStefan Eßer return res; 6944d4804dSStefan Eßer } 7044d4804dSStefan Eßer 7144d4804dSStefan Eßer /** 7244d4804dSStefan Eßer * Conditionally negate @a n based on @a neg. Algorithm taken from 7344d4804dSStefan Eßer * https://graphics.stanford.edu/~seander/bithacks.html#ConditionalNegate . 7444d4804dSStefan Eßer * @param n The value to turn into a signed value and negate. 7544d4804dSStefan Eßer * @param neg The condition to negate or not. 7644d4804dSStefan Eßer */ 7778bc019dSStefan Eßer static inline ssize_t 7878bc019dSStefan Eßer bc_num_neg(size_t n, bool neg) 7978bc019dSStefan Eßer { 80252884aeSStefan Eßer return (((ssize_t) n) ^ -((ssize_t) neg)) + neg; 81252884aeSStefan Eßer } 82252884aeSStefan Eßer 8344d4804dSStefan Eßer /** 8444d4804dSStefan Eßer * Compare a BcNum against zero. 8544d4804dSStefan Eßer * @param n The number to compare. 8644d4804dSStefan Eßer * @return -1 if the number is less than 0, 1 if greater, and 0 if equal. 8744d4804dSStefan Eßer */ 8878bc019dSStefan Eßer ssize_t 8978bc019dSStefan Eßer bc_num_cmpZero(const BcNum* n) 9078bc019dSStefan Eßer { 9150696a6eSStefan Eßer return bc_num_neg((n)->len != 0, BC_NUM_NEG(n)); 92252884aeSStefan Eßer } 93252884aeSStefan Eßer 9444d4804dSStefan Eßer /** 9544d4804dSStefan Eßer * Return the number of integer limbs in a BcNum. This is the opposite of rdx. 9644d4804dSStefan Eßer * @param n The number to return the amount of integer limbs for. 9744d4804dSStefan Eßer * @return The amount of integer limbs in @a n. 9844d4804dSStefan Eßer */ 9978bc019dSStefan Eßer static inline size_t 10078bc019dSStefan Eßer bc_num_int(const BcNum* n) 10178bc019dSStefan Eßer { 10250696a6eSStefan Eßer return n->len ? n->len - BC_NUM_RDX_VAL(n) : 0; 103252884aeSStefan Eßer } 104252884aeSStefan Eßer 10544d4804dSStefan Eßer /** 10644d4804dSStefan Eßer * Expand a number's allocation capacity to at least req limbs. 10744d4804dSStefan Eßer * @param n The number to expand. 10844d4804dSStefan Eßer * @param req The number limbs to expand the allocation capacity to. 10944d4804dSStefan Eßer */ 11078bc019dSStefan Eßer static void 11178bc019dSStefan Eßer bc_num_expand(BcNum* restrict n, size_t req) 11278bc019dSStefan Eßer { 113252884aeSStefan Eßer assert(n != NULL); 114252884aeSStefan Eßer 115252884aeSStefan Eßer req = req >= BC_NUM_DEF_SIZE ? req : BC_NUM_DEF_SIZE; 116252884aeSStefan Eßer 11778bc019dSStefan Eßer if (req > n->cap) 11878bc019dSStefan Eßer { 119252884aeSStefan Eßer BC_SIG_LOCK; 120252884aeSStefan Eßer 121252884aeSStefan Eßer n->num = bc_vm_realloc(n->num, BC_NUM_SIZE(req)); 122252884aeSStefan Eßer n->cap = req; 123252884aeSStefan Eßer 124252884aeSStefan Eßer BC_SIG_UNLOCK; 125252884aeSStefan Eßer } 126252884aeSStefan Eßer } 127252884aeSStefan Eßer 12844d4804dSStefan Eßer /** 12944d4804dSStefan Eßer * Set a number to 0 with the specified scale. 13044d4804dSStefan Eßer * @param n The number to set to zero. 13144d4804dSStefan Eßer * @param scale The scale to set the number to. 13244d4804dSStefan Eßer */ 133d101cdd6SStefan Eßer static inline void 13478bc019dSStefan Eßer bc_num_setToZero(BcNum* restrict n, size_t scale) 13578bc019dSStefan Eßer { 136252884aeSStefan Eßer assert(n != NULL); 137252884aeSStefan Eßer n->scale = scale; 138252884aeSStefan Eßer n->len = n->rdx = 0; 139252884aeSStefan Eßer } 140252884aeSStefan Eßer 14178bc019dSStefan Eßer void 14278bc019dSStefan Eßer bc_num_zero(BcNum* restrict n) 14378bc019dSStefan Eßer { 144252884aeSStefan Eßer bc_num_setToZero(n, 0); 145252884aeSStefan Eßer } 146252884aeSStefan Eßer 14778bc019dSStefan Eßer void 14878bc019dSStefan Eßer bc_num_one(BcNum* restrict n) 14978bc019dSStefan Eßer { 150252884aeSStefan Eßer bc_num_zero(n); 151252884aeSStefan Eßer n->len = 1; 152252884aeSStefan Eßer n->num[0] = 1; 153252884aeSStefan Eßer } 154252884aeSStefan Eßer 15544d4804dSStefan Eßer /** 15644d4804dSStefan Eßer * "Cleans" a number, which means reducing the length if the most significant 15744d4804dSStefan Eßer * limbs are zero. 15844d4804dSStefan Eßer * @param n The number to clean. 15944d4804dSStefan Eßer */ 16078bc019dSStefan Eßer static void 16178bc019dSStefan Eßer bc_num_clean(BcNum* restrict n) 16278bc019dSStefan Eßer { 16344d4804dSStefan Eßer // Reduce the length. 16478bc019dSStefan Eßer while (BC_NUM_NONZERO(n) && !n->num[n->len - 1]) 16578bc019dSStefan Eßer { 16678bc019dSStefan Eßer n->len -= 1; 16778bc019dSStefan Eßer } 168252884aeSStefan Eßer 16944d4804dSStefan Eßer // Special cases. 17050696a6eSStefan Eßer if (BC_NUM_ZERO(n)) n->rdx = 0; 17178bc019dSStefan Eßer else 17278bc019dSStefan Eßer { 17344d4804dSStefan Eßer // len must be at least as much as rdx. 17450696a6eSStefan Eßer size_t rdx = BC_NUM_RDX_VAL(n); 17550696a6eSStefan Eßer if (n->len < rdx) n->len = rdx; 176252884aeSStefan Eßer } 177252884aeSStefan Eßer } 178252884aeSStefan Eßer 17944d4804dSStefan Eßer /** 18044d4804dSStefan Eßer * Returns the log base 10 of @a i. I could have done this with floating-point 18144d4804dSStefan Eßer * math, and in fact, I originally did. However, that was the only 18244d4804dSStefan Eßer * floating-point code in the entire codebase, and I decided I didn't want any. 18344d4804dSStefan Eßer * This is fast enough. Also, it might handle larger numbers better. 18444d4804dSStefan Eßer * @param i The number to return the log base 10 of. 18544d4804dSStefan Eßer * @return The log base 10 of @a i. 18644d4804dSStefan Eßer */ 18778bc019dSStefan Eßer static size_t 18878bc019dSStefan Eßer bc_num_log10(size_t i) 18978bc019dSStefan Eßer { 190252884aeSStefan Eßer size_t len; 19178bc019dSStefan Eßer 19278bc019dSStefan Eßer for (len = 1; i; i /= BC_BASE, ++len) 19378bc019dSStefan Eßer { 19478bc019dSStefan Eßer continue; 19578bc019dSStefan Eßer } 19678bc019dSStefan Eßer 197252884aeSStefan Eßer assert(len - 1 <= BC_BASE_DIGS + 1); 19878bc019dSStefan Eßer 199252884aeSStefan Eßer return len - 1; 200252884aeSStefan Eßer } 201252884aeSStefan Eßer 20244d4804dSStefan Eßer /** 20344d4804dSStefan Eßer * Returns the number of decimal digits in a limb that are zero starting at the 20444d4804dSStefan Eßer * most significant digits. This basically returns how much of the limb is used. 20544d4804dSStefan Eßer * @param n The number. 20644d4804dSStefan Eßer * @return The number of decimal digits that are 0 starting at the most 20744d4804dSStefan Eßer * significant digits. 20844d4804dSStefan Eßer */ 20978bc019dSStefan Eßer static inline size_t 21078bc019dSStefan Eßer bc_num_zeroDigits(const BcDig* n) 21178bc019dSStefan Eßer { 212252884aeSStefan Eßer assert(*n >= 0); 213252884aeSStefan Eßer assert(((size_t) *n) < BC_BASE_POW); 214252884aeSStefan Eßer return BC_BASE_DIGS - bc_num_log10((size_t) *n); 215252884aeSStefan Eßer } 216252884aeSStefan Eßer 21744d4804dSStefan Eßer /** 21844d4804dSStefan Eßer * Return the total number of integer digits in a number. This is the opposite 21944d4804dSStefan Eßer * of scale, like bc_num_int() is the opposite of rdx. 22044d4804dSStefan Eßer * @param n The number. 22144d4804dSStefan Eßer * @return The number of integer digits in @a n. 22244d4804dSStefan Eßer */ 22378bc019dSStefan Eßer static size_t 22478bc019dSStefan Eßer bc_num_intDigits(const BcNum* n) 22578bc019dSStefan Eßer { 226252884aeSStefan Eßer size_t digits = bc_num_int(n) * BC_BASE_DIGS; 227252884aeSStefan Eßer if (digits > 0) digits -= bc_num_zeroDigits(n->num + n->len - 1); 228252884aeSStefan Eßer return digits; 229252884aeSStefan Eßer } 230252884aeSStefan Eßer 23144d4804dSStefan Eßer /** 23244d4804dSStefan Eßer * Returns the number of limbs of a number that are non-zero starting at the 23344d4804dSStefan Eßer * most significant limbs. This expects that there are *no* integer limbs in the 23444d4804dSStefan Eßer * number because it is specifically to figure out how many zero limbs after the 23544d4804dSStefan Eßer * decimal place to ignore. If there are zero limbs after non-zero limbs, they 23644d4804dSStefan Eßer * are counted as non-zero limbs. 23744d4804dSStefan Eßer * @param n The number. 23844d4804dSStefan Eßer * @return The number of non-zero limbs after the decimal point. 23944d4804dSStefan Eßer */ 24078bc019dSStefan Eßer static size_t 24178bc019dSStefan Eßer bc_num_nonZeroLen(const BcNum* restrict n) 24278bc019dSStefan Eßer { 243252884aeSStefan Eßer size_t i, len = n->len; 24478bc019dSStefan Eßer 24550696a6eSStefan Eßer assert(len == BC_NUM_RDX_VAL(n)); 24678bc019dSStefan Eßer 24778bc019dSStefan Eßer for (i = len - 1; i < len && !n->num[i]; --i) 24878bc019dSStefan Eßer { 24978bc019dSStefan Eßer continue; 25078bc019dSStefan Eßer } 25178bc019dSStefan Eßer 252252884aeSStefan Eßer assert(i + 1 > 0); 25378bc019dSStefan Eßer 254252884aeSStefan Eßer return i + 1; 255252884aeSStefan Eßer } 256252884aeSStefan Eßer 25744d4804dSStefan Eßer /** 25844d4804dSStefan Eßer * Performs a one-limb add with a carry. 25944d4804dSStefan Eßer * @param a The first limb. 26044d4804dSStefan Eßer * @param b The second limb. 26144d4804dSStefan Eßer * @param carry An in/out parameter; the carry in from the previous add and the 26244d4804dSStefan Eßer * carry out from this add. 26344d4804dSStefan Eßer * @return The resulting limb sum. 26444d4804dSStefan Eßer */ 26578bc019dSStefan Eßer static BcDig 26678bc019dSStefan Eßer bc_num_addDigits(BcDig a, BcDig b, bool* carry) 26778bc019dSStefan Eßer { 268252884aeSStefan Eßer assert(((BcBigDig) BC_BASE_POW) * 2 == ((BcDig) BC_BASE_POW) * 2); 269d101cdd6SStefan Eßer assert(a < BC_BASE_POW && a >= 0); 270d101cdd6SStefan Eßer assert(b < BC_BASE_POW && b >= 0); 271252884aeSStefan Eßer 272252884aeSStefan Eßer a += b + *carry; 273252884aeSStefan Eßer *carry = (a >= BC_BASE_POW); 274252884aeSStefan Eßer if (*carry) a -= BC_BASE_POW; 275252884aeSStefan Eßer 276252884aeSStefan Eßer assert(a >= 0); 277252884aeSStefan Eßer assert(a < BC_BASE_POW); 278252884aeSStefan Eßer 279252884aeSStefan Eßer return a; 280252884aeSStefan Eßer } 281252884aeSStefan Eßer 28244d4804dSStefan Eßer /** 28344d4804dSStefan Eßer * Performs a one-limb subtract with a carry. 28444d4804dSStefan Eßer * @param a The first limb. 28544d4804dSStefan Eßer * @param b The second limb. 28644d4804dSStefan Eßer * @param carry An in/out parameter; the carry in from the previous subtract 28744d4804dSStefan Eßer * and the carry out from this subtract. 28844d4804dSStefan Eßer * @return The resulting limb difference. 28944d4804dSStefan Eßer */ 29078bc019dSStefan Eßer static BcDig 29178bc019dSStefan Eßer bc_num_subDigits(BcDig a, BcDig b, bool* carry) 29278bc019dSStefan Eßer { 293d101cdd6SStefan Eßer assert(a < BC_BASE_POW && a >= 0); 294d101cdd6SStefan Eßer assert(b < BC_BASE_POW && b >= 0); 295252884aeSStefan Eßer 296252884aeSStefan Eßer b += *carry; 297252884aeSStefan Eßer *carry = (a < b); 298252884aeSStefan Eßer if (*carry) a += BC_BASE_POW; 299252884aeSStefan Eßer 300252884aeSStefan Eßer assert(a - b >= 0); 301252884aeSStefan Eßer assert(a - b < BC_BASE_POW); 302252884aeSStefan Eßer 303252884aeSStefan Eßer return a - b; 304252884aeSStefan Eßer } 305252884aeSStefan Eßer 30644d4804dSStefan Eßer /** 30744d4804dSStefan Eßer * Add two BcDig arrays and store the result in the first array. 30844d4804dSStefan Eßer * @param a The first operand and out array. 30944d4804dSStefan Eßer * @param b The second operand. 31044d4804dSStefan Eßer * @param len The length of @a b. 31144d4804dSStefan Eßer */ 31278bc019dSStefan Eßer static void 31378bc019dSStefan Eßer bc_num_addArrays(BcDig* restrict a, const BcDig* restrict b, size_t len) 314252884aeSStefan Eßer { 315252884aeSStefan Eßer size_t i; 316252884aeSStefan Eßer bool carry = false; 317252884aeSStefan Eßer 31878bc019dSStefan Eßer for (i = 0; i < len; ++i) 31978bc019dSStefan Eßer { 32078bc019dSStefan Eßer a[i] = bc_num_addDigits(a[i], b[i], &carry); 32178bc019dSStefan Eßer } 322252884aeSStefan Eßer 32344d4804dSStefan Eßer // Take care of the extra limbs in the bigger array. 32478bc019dSStefan Eßer for (; carry; ++i) 32578bc019dSStefan Eßer { 32678bc019dSStefan Eßer a[i] = bc_num_addDigits(a[i], 0, &carry); 32778bc019dSStefan Eßer } 328252884aeSStefan Eßer } 329252884aeSStefan Eßer 33044d4804dSStefan Eßer /** 33144d4804dSStefan Eßer * Subtract two BcDig arrays and store the result in the first array. 33244d4804dSStefan Eßer * @param a The first operand and out array. 33344d4804dSStefan Eßer * @param b The second operand. 33444d4804dSStefan Eßer * @param len The length of @a b. 33544d4804dSStefan Eßer */ 33678bc019dSStefan Eßer static void 33778bc019dSStefan Eßer bc_num_subArrays(BcDig* restrict a, const BcDig* restrict b, size_t len) 338252884aeSStefan Eßer { 339252884aeSStefan Eßer size_t i; 340252884aeSStefan Eßer bool carry = false; 341252884aeSStefan Eßer 34278bc019dSStefan Eßer for (i = 0; i < len; ++i) 34378bc019dSStefan Eßer { 34478bc019dSStefan Eßer a[i] = bc_num_subDigits(a[i], b[i], &carry); 34578bc019dSStefan Eßer } 346252884aeSStefan Eßer 34744d4804dSStefan Eßer // Take care of the extra limbs in the bigger array. 34878bc019dSStefan Eßer for (; carry; ++i) 34978bc019dSStefan Eßer { 35078bc019dSStefan Eßer a[i] = bc_num_subDigits(a[i], 0, &carry); 35178bc019dSStefan Eßer } 352252884aeSStefan Eßer } 353252884aeSStefan Eßer 35444d4804dSStefan Eßer /** 35544d4804dSStefan Eßer * Multiply a BcNum array by a one-limb number. This is a faster version of 35644d4804dSStefan Eßer * multiplication for when we can use it. 35744d4804dSStefan Eßer * @param a The BcNum to multiply by the one-limb number. 35844d4804dSStefan Eßer * @param b The one limb of the one-limb number. 35944d4804dSStefan Eßer * @param c The return parameter. 36044d4804dSStefan Eßer */ 36178bc019dSStefan Eßer static void 36278bc019dSStefan Eßer bc_num_mulArray(const BcNum* restrict a, BcBigDig b, BcNum* restrict c) 363252884aeSStefan Eßer { 364252884aeSStefan Eßer size_t i; 365252884aeSStefan Eßer BcBigDig carry = 0; 366252884aeSStefan Eßer 367252884aeSStefan Eßer assert(b <= BC_BASE_POW); 368252884aeSStefan Eßer 36944d4804dSStefan Eßer // Make sure the return parameter is big enough. 370252884aeSStefan Eßer if (a->len + 1 > c->cap) bc_num_expand(c, a->len + 1); 371252884aeSStefan Eßer 37244d4804dSStefan Eßer // We want the entire return parameter to be zero for cleaning later. 37378bc019dSStefan Eßer // NOLINTNEXTLINE 374252884aeSStefan Eßer memset(c->num, 0, BC_NUM_SIZE(c->cap)); 375252884aeSStefan Eßer 37644d4804dSStefan Eßer // Actual multiplication loop. 37778bc019dSStefan Eßer for (i = 0; i < a->len; ++i) 37878bc019dSStefan Eßer { 379252884aeSStefan Eßer BcBigDig in = ((BcBigDig) a->num[i]) * b + carry; 380252884aeSStefan Eßer c->num[i] = in % BC_BASE_POW; 381252884aeSStefan Eßer carry = in / BC_BASE_POW; 382252884aeSStefan Eßer } 383252884aeSStefan Eßer 384252884aeSStefan Eßer assert(carry < BC_BASE_POW); 38544d4804dSStefan Eßer 38644d4804dSStefan Eßer // Finishing touches. 387252884aeSStefan Eßer c->num[i] = (BcDig) carry; 388d101cdd6SStefan Eßer assert(c->num[i] >= 0 && c->num[i] < BC_BASE_POW); 389252884aeSStefan Eßer c->len = a->len; 390252884aeSStefan Eßer c->len += (carry != 0); 391252884aeSStefan Eßer 392252884aeSStefan Eßer bc_num_clean(c); 393252884aeSStefan Eßer 39444d4804dSStefan Eßer // Postconditions. 39550696a6eSStefan Eßer assert(!BC_NUM_NEG(c) || BC_NUM_NONZERO(c)); 39650696a6eSStefan Eßer assert(BC_NUM_RDX_VAL(c) <= c->len || !c->len); 39750696a6eSStefan Eßer assert(!c->len || c->num[c->len - 1] || BC_NUM_RDX_VAL(c) == c->len); 398252884aeSStefan Eßer } 399252884aeSStefan Eßer 40044d4804dSStefan Eßer /** 40144d4804dSStefan Eßer * Divide a BcNum array by a one-limb number. This is a faster version of divide 40244d4804dSStefan Eßer * for when we can use it. 40344d4804dSStefan Eßer * @param a The BcNum to multiply by the one-limb number. 40444d4804dSStefan Eßer * @param b The one limb of the one-limb number. 40544d4804dSStefan Eßer * @param c The return parameter for the quotient. 40644d4804dSStefan Eßer * @param rem The return parameter for the remainder. 40744d4804dSStefan Eßer */ 40878bc019dSStefan Eßer static void 40978bc019dSStefan Eßer bc_num_divArray(const BcNum* restrict a, BcBigDig b, BcNum* restrict c, 41078bc019dSStefan Eßer BcBigDig* rem) 411252884aeSStefan Eßer { 412252884aeSStefan Eßer size_t i; 413252884aeSStefan Eßer BcBigDig carry = 0; 414252884aeSStefan Eßer 415252884aeSStefan Eßer assert(c->cap >= a->len); 416252884aeSStefan Eßer 41744d4804dSStefan Eßer // Actual division loop. 41878bc019dSStefan Eßer for (i = a->len - 1; i < a->len; --i) 41978bc019dSStefan Eßer { 420252884aeSStefan Eßer BcBigDig in = ((BcBigDig) a->num[i]) + carry * BC_BASE_POW; 421252884aeSStefan Eßer assert(in / b < BC_BASE_POW); 422252884aeSStefan Eßer c->num[i] = (BcDig) (in / b); 423d101cdd6SStefan Eßer assert(c->num[i] >= 0 && c->num[i] < BC_BASE_POW); 424252884aeSStefan Eßer carry = in % b; 425252884aeSStefan Eßer } 426252884aeSStefan Eßer 42744d4804dSStefan Eßer // Finishing touches. 428252884aeSStefan Eßer c->len = a->len; 429252884aeSStefan Eßer bc_num_clean(c); 430252884aeSStefan Eßer *rem = carry; 431252884aeSStefan Eßer 43244d4804dSStefan Eßer // Postconditions. 43350696a6eSStefan Eßer assert(!BC_NUM_NEG(c) || BC_NUM_NONZERO(c)); 43450696a6eSStefan Eßer assert(BC_NUM_RDX_VAL(c) <= c->len || !c->len); 43550696a6eSStefan Eßer assert(!c->len || c->num[c->len - 1] || BC_NUM_RDX_VAL(c) == c->len); 436252884aeSStefan Eßer } 437252884aeSStefan Eßer 43844d4804dSStefan Eßer /** 43944d4804dSStefan Eßer * Compare two BcDig arrays and return >0 if @a b is greater, <0 if @a b is 44044d4804dSStefan Eßer * less, and 0 if equal. Both @a a and @a b must have the same length. 44144d4804dSStefan Eßer * @param a The first array. 44244d4804dSStefan Eßer * @param b The second array. 44344d4804dSStefan Eßer * @param len The minimum length of the arrays. 44444d4804dSStefan Eßer */ 44578bc019dSStefan Eßer static ssize_t 44678bc019dSStefan Eßer bc_num_compare(const BcDig* restrict a, const BcDig* restrict b, size_t len) 447252884aeSStefan Eßer { 448252884aeSStefan Eßer size_t i; 449252884aeSStefan Eßer BcDig c = 0; 45078bc019dSStefan Eßer for (i = len - 1; i < len && !(c = a[i] - b[i]); --i) 45178bc019dSStefan Eßer { 45278bc019dSStefan Eßer continue; 45378bc019dSStefan Eßer } 454252884aeSStefan Eßer return bc_num_neg(i + 1, c < 0); 455252884aeSStefan Eßer } 456252884aeSStefan Eßer 45778bc019dSStefan Eßer ssize_t 45878bc019dSStefan Eßer bc_num_cmp(const BcNum* a, const BcNum* b) 45978bc019dSStefan Eßer { 46050696a6eSStefan Eßer size_t i, min, a_int, b_int, diff, ardx, brdx; 46178bc019dSStefan Eßer BcDig* max_num; 46278bc019dSStefan Eßer BcDig* min_num; 463252884aeSStefan Eßer bool a_max, neg = false; 464252884aeSStefan Eßer ssize_t cmp; 465252884aeSStefan Eßer 466252884aeSStefan Eßer assert(a != NULL && b != NULL); 467252884aeSStefan Eßer 46844d4804dSStefan Eßer // Same num? Equal. 469252884aeSStefan Eßer if (a == b) return 0; 47044d4804dSStefan Eßer 47144d4804dSStefan Eßer // Easy cases. 47250696a6eSStefan Eßer if (BC_NUM_ZERO(a)) return bc_num_neg(b->len != 0, !BC_NUM_NEG(b)); 473252884aeSStefan Eßer if (BC_NUM_ZERO(b)) return bc_num_cmpZero(a); 47478bc019dSStefan Eßer if (BC_NUM_NEG(a)) 47578bc019dSStefan Eßer { 47650696a6eSStefan Eßer if (BC_NUM_NEG(b)) neg = true; 477252884aeSStefan Eßer else return -1; 478252884aeSStefan Eßer } 47950696a6eSStefan Eßer else if (BC_NUM_NEG(b)) return 1; 480252884aeSStefan Eßer 48144d4804dSStefan Eßer // Get the number of int limbs in each number and get the difference. 482252884aeSStefan Eßer a_int = bc_num_int(a); 483252884aeSStefan Eßer b_int = bc_num_int(b); 484252884aeSStefan Eßer a_int -= b_int; 485252884aeSStefan Eßer 48644d4804dSStefan Eßer // If there's a difference, then just return the comparison. 487252884aeSStefan Eßer if (a_int) return neg ? -((ssize_t) a_int) : (ssize_t) a_int; 488252884aeSStefan Eßer 48944d4804dSStefan Eßer // Get the rdx's and figure out the max. 49050696a6eSStefan Eßer ardx = BC_NUM_RDX_VAL(a); 49150696a6eSStefan Eßer brdx = BC_NUM_RDX_VAL(b); 49250696a6eSStefan Eßer a_max = (ardx > brdx); 493252884aeSStefan Eßer 49444d4804dSStefan Eßer // Set variables based on the above. 49578bc019dSStefan Eßer if (a_max) 49678bc019dSStefan Eßer { 49750696a6eSStefan Eßer min = brdx; 49850696a6eSStefan Eßer diff = ardx - brdx; 499252884aeSStefan Eßer max_num = a->num + diff; 500252884aeSStefan Eßer min_num = b->num; 501252884aeSStefan Eßer } 50278bc019dSStefan Eßer else 50378bc019dSStefan Eßer { 50450696a6eSStefan Eßer min = ardx; 50550696a6eSStefan Eßer diff = brdx - ardx; 506252884aeSStefan Eßer max_num = b->num + diff; 507252884aeSStefan Eßer min_num = a->num; 508252884aeSStefan Eßer } 509252884aeSStefan Eßer 51044d4804dSStefan Eßer // Do a full limb-by-limb comparison. 511252884aeSStefan Eßer cmp = bc_num_compare(max_num, min_num, b_int + min); 512252884aeSStefan Eßer 51344d4804dSStefan Eßer // If we found a difference, return it based on state. 514252884aeSStefan Eßer if (cmp) return bc_num_neg((size_t) cmp, !a_max == !neg); 515252884aeSStefan Eßer 51644d4804dSStefan Eßer // If there was no difference, then the final step is to check which number 51744d4804dSStefan Eßer // has greater or lesser limbs beyond the other's. 51878bc019dSStefan Eßer for (max_num -= diff, i = diff - 1; i < diff; --i) 51978bc019dSStefan Eßer { 520252884aeSStefan Eßer if (max_num[i]) return bc_num_neg(1, !a_max == !neg); 521252884aeSStefan Eßer } 522252884aeSStefan Eßer 523252884aeSStefan Eßer return 0; 524252884aeSStefan Eßer } 525252884aeSStefan Eßer 52678bc019dSStefan Eßer void 52778bc019dSStefan Eßer bc_num_truncate(BcNum* restrict n, size_t places) 52878bc019dSStefan Eßer { 52950696a6eSStefan Eßer size_t nrdx, places_rdx; 530252884aeSStefan Eßer 531252884aeSStefan Eßer if (!places) return; 532252884aeSStefan Eßer 53344d4804dSStefan Eßer // Grab these needed values; places_rdx is the rdx equivalent to places like 53444d4804dSStefan Eßer // rdx is to scale. 53550696a6eSStefan Eßer nrdx = BC_NUM_RDX_VAL(n); 53650696a6eSStefan Eßer places_rdx = nrdx ? nrdx - BC_NUM_RDX(n->scale - places) : 0; 53744d4804dSStefan Eßer 53844d4804dSStefan Eßer // We cannot truncate more places than we have. 539252884aeSStefan Eßer assert(places <= n->scale && (BC_NUM_ZERO(n) || places_rdx <= n->len)); 540252884aeSStefan Eßer 541252884aeSStefan Eßer n->scale -= places; 54250696a6eSStefan Eßer BC_NUM_RDX_SET(n, nrdx - places_rdx); 543252884aeSStefan Eßer 54444d4804dSStefan Eßer // Only when the number is nonzero do we need to do the hard stuff. 54578bc019dSStefan Eßer if (BC_NUM_NONZERO(n)) 54678bc019dSStefan Eßer { 547252884aeSStefan Eßer size_t pow; 548252884aeSStefan Eßer 54944d4804dSStefan Eßer // This calculates how many decimal digits are in the least significant 55044d4804dSStefan Eßer // limb. 551252884aeSStefan Eßer pow = n->scale % BC_BASE_DIGS; 552252884aeSStefan Eßer pow = pow ? BC_BASE_DIGS - pow : 0; 553252884aeSStefan Eßer pow = bc_num_pow10[pow]; 554252884aeSStefan Eßer 555252884aeSStefan Eßer n->len -= places_rdx; 55644d4804dSStefan Eßer 55744d4804dSStefan Eßer // We have to move limbs to maintain invariants. The limbs must begin at 55844d4804dSStefan Eßer // the beginning of the BcNum array. 55978bc019dSStefan Eßer // NOLINTNEXTLINE 560252884aeSStefan Eßer memmove(n->num, n->num + places_rdx, BC_NUM_SIZE(n->len)); 561252884aeSStefan Eßer 562252884aeSStefan Eßer // Clear the lower part of the last digit. 563252884aeSStefan Eßer if (BC_NUM_NONZERO(n)) n->num[0] -= n->num[0] % (BcDig) pow; 564252884aeSStefan Eßer 565252884aeSStefan Eßer bc_num_clean(n); 566252884aeSStefan Eßer } 567252884aeSStefan Eßer } 568252884aeSStefan Eßer 56978bc019dSStefan Eßer void 57078bc019dSStefan Eßer bc_num_extend(BcNum* restrict n, size_t places) 57178bc019dSStefan Eßer { 57250696a6eSStefan Eßer size_t nrdx, places_rdx; 573252884aeSStefan Eßer 574252884aeSStefan Eßer if (!places) return; 57544d4804dSStefan Eßer 57644d4804dSStefan Eßer // Easy case with zero; set the scale. 57778bc019dSStefan Eßer if (BC_NUM_ZERO(n)) 57878bc019dSStefan Eßer { 579252884aeSStefan Eßer n->scale += places; 580252884aeSStefan Eßer return; 581252884aeSStefan Eßer } 582252884aeSStefan Eßer 58344d4804dSStefan Eßer // Grab these needed values; places_rdx is the rdx equivalent to places like 58444d4804dSStefan Eßer // rdx is to scale. 58550696a6eSStefan Eßer nrdx = BC_NUM_RDX_VAL(n); 58650696a6eSStefan Eßer places_rdx = BC_NUM_RDX(places + n->scale) - nrdx; 587252884aeSStefan Eßer 58844d4804dSStefan Eßer // This is the hard case. We need to expand the number, move the limbs, and 58944d4804dSStefan Eßer // set the limbs that were just cleared. 59078bc019dSStefan Eßer if (places_rdx) 59178bc019dSStefan Eßer { 592252884aeSStefan Eßer bc_num_expand(n, bc_vm_growSize(n->len, places_rdx)); 59378bc019dSStefan Eßer // NOLINTNEXTLINE 594252884aeSStefan Eßer memmove(n->num + places_rdx, n->num, BC_NUM_SIZE(n->len)); 59578bc019dSStefan Eßer // NOLINTNEXTLINE 596252884aeSStefan Eßer memset(n->num, 0, BC_NUM_SIZE(places_rdx)); 597252884aeSStefan Eßer } 598252884aeSStefan Eßer 59944d4804dSStefan Eßer // Finally, set scale and rdx. 60050696a6eSStefan Eßer BC_NUM_RDX_SET(n, nrdx + places_rdx); 601252884aeSStefan Eßer n->scale += places; 602252884aeSStefan Eßer n->len += places_rdx; 603252884aeSStefan Eßer 60450696a6eSStefan Eßer assert(BC_NUM_RDX_VAL(n) == BC_NUM_RDX(n->scale)); 605252884aeSStefan Eßer } 606252884aeSStefan Eßer 60744d4804dSStefan Eßer /** 60844d4804dSStefan Eßer * Retires (finishes) a multiplication or division operation. 60944d4804dSStefan Eßer */ 61078bc019dSStefan Eßer static void 61178bc019dSStefan Eßer bc_num_retireMul(BcNum* restrict n, size_t scale, bool neg1, bool neg2) 612252884aeSStefan Eßer { 61344d4804dSStefan Eßer // Make sure scale is correct. 614252884aeSStefan Eßer if (n->scale < scale) bc_num_extend(n, scale - n->scale); 615252884aeSStefan Eßer else bc_num_truncate(n, n->scale - scale); 616252884aeSStefan Eßer 617252884aeSStefan Eßer bc_num_clean(n); 61844d4804dSStefan Eßer 61944d4804dSStefan Eßer // We need to ensure rdx is correct. 62050696a6eSStefan Eßer if (BC_NUM_NONZERO(n)) n->rdx = BC_NUM_NEG_VAL(n, !neg1 != !neg2); 621252884aeSStefan Eßer } 622252884aeSStefan Eßer 62344d4804dSStefan Eßer /** 62444d4804dSStefan Eßer * Splits a number into two BcNum's. This is used in Karatsuba. 62544d4804dSStefan Eßer * @param n The number to split. 62644d4804dSStefan Eßer * @param idx The index to split at. 62744d4804dSStefan Eßer * @param a An out parameter; the low part of @a n. 62844d4804dSStefan Eßer * @param b An out parameter; the high part of @a n. 62944d4804dSStefan Eßer */ 63078bc019dSStefan Eßer static void 63178bc019dSStefan Eßer bc_num_split(const BcNum* restrict n, size_t idx, BcNum* restrict a, 63278bc019dSStefan Eßer BcNum* restrict b) 633252884aeSStefan Eßer { 63444d4804dSStefan Eßer // We want a and b to be clear. 635252884aeSStefan Eßer assert(BC_NUM_ZERO(a)); 636252884aeSStefan Eßer assert(BC_NUM_ZERO(b)); 637252884aeSStefan Eßer 63844d4804dSStefan Eßer // The usual case. 63978bc019dSStefan Eßer if (idx < n->len) 64078bc019dSStefan Eßer { 64144d4804dSStefan Eßer // Set the fields first. 642252884aeSStefan Eßer b->len = n->len - idx; 643252884aeSStefan Eßer a->len = idx; 64450696a6eSStefan Eßer a->scale = b->scale = 0; 64550696a6eSStefan Eßer BC_NUM_RDX_SET(a, 0); 64650696a6eSStefan Eßer BC_NUM_RDX_SET(b, 0); 647252884aeSStefan Eßer 648252884aeSStefan Eßer assert(a->cap >= a->len); 649252884aeSStefan Eßer assert(b->cap >= b->len); 650252884aeSStefan Eßer 65144d4804dSStefan Eßer // Copy the arrays. This is not necessary for safety, but it is faster, 65244d4804dSStefan Eßer // for some reason. 65378bc019dSStefan Eßer // NOLINTNEXTLINE 654252884aeSStefan Eßer memcpy(b->num, n->num + idx, BC_NUM_SIZE(b->len)); 65578bc019dSStefan Eßer // NOLINTNEXTLINE 656252884aeSStefan Eßer memcpy(a->num, n->num, BC_NUM_SIZE(idx)); 657252884aeSStefan Eßer 658252884aeSStefan Eßer bc_num_clean(b); 659252884aeSStefan Eßer } 66044d4804dSStefan Eßer // If the index is weird, just skip the split. 661252884aeSStefan Eßer else bc_num_copy(a, n); 662252884aeSStefan Eßer 663252884aeSStefan Eßer bc_num_clean(a); 664252884aeSStefan Eßer } 665252884aeSStefan Eßer 66644d4804dSStefan Eßer /** 66744d4804dSStefan Eßer * Copies a number into another, but shifts the rdx so that the result number 66844d4804dSStefan Eßer * only sees the integer part of the source number. 66944d4804dSStefan Eßer * @param n The number to copy. 67044d4804dSStefan Eßer * @param r The result number with a shifted rdx, len, and num. 67144d4804dSStefan Eßer */ 67278bc019dSStefan Eßer static void 67378bc019dSStefan Eßer bc_num_shiftRdx(const BcNum* restrict n, BcNum* restrict r) 67478bc019dSStefan Eßer { 67544d4804dSStefan Eßer size_t rdx = BC_NUM_RDX_VAL(n); 67644d4804dSStefan Eßer 67744d4804dSStefan Eßer r->len = n->len - rdx; 67844d4804dSStefan Eßer r->cap = n->cap - rdx; 67944d4804dSStefan Eßer r->num = n->num + rdx; 68044d4804dSStefan Eßer 68144d4804dSStefan Eßer BC_NUM_RDX_SET_NEG(r, 0, BC_NUM_NEG(n)); 68244d4804dSStefan Eßer r->scale = 0; 68344d4804dSStefan Eßer } 68444d4804dSStefan Eßer 68544d4804dSStefan Eßer /** 68644d4804dSStefan Eßer * Shifts a number so that all of the least significant limbs of the number are 68744d4804dSStefan Eßer * skipped. This must be undone by bc_num_unshiftZero(). 68844d4804dSStefan Eßer * @param n The number to shift. 68944d4804dSStefan Eßer */ 69078bc019dSStefan Eßer static size_t 69178bc019dSStefan Eßer bc_num_shiftZero(BcNum* restrict n) 69278bc019dSStefan Eßer { 693d101cdd6SStefan Eßer // This is volatile to quiet a GCC warning about longjmp() clobbering. 694d101cdd6SStefan Eßer volatile size_t i; 695252884aeSStefan Eßer 69644d4804dSStefan Eßer // If we don't have an integer, that is a problem, but it's also a bug 69744d4804dSStefan Eßer // because the caller should have set everything up right. 69850696a6eSStefan Eßer assert(!BC_NUM_RDX_VAL(n) || BC_NUM_ZERO(n)); 699252884aeSStefan Eßer 70078bc019dSStefan Eßer for (i = 0; i < n->len && !n->num[i]; ++i) 70178bc019dSStefan Eßer { 70278bc019dSStefan Eßer continue; 70378bc019dSStefan Eßer } 704252884aeSStefan Eßer 705252884aeSStefan Eßer n->len -= i; 706252884aeSStefan Eßer n->num += i; 707252884aeSStefan Eßer 708252884aeSStefan Eßer return i; 709252884aeSStefan Eßer } 710252884aeSStefan Eßer 71144d4804dSStefan Eßer /** 71244d4804dSStefan Eßer * Undo the damage done by bc_num_unshiftZero(). This must be called like all 71344d4804dSStefan Eßer * cleanup functions: after a label used by setjmp() and longjmp(). 71444d4804dSStefan Eßer * @param n The number to unshift. 71544d4804dSStefan Eßer * @param places_rdx The amount the number was originally shift. 71644d4804dSStefan Eßer */ 71778bc019dSStefan Eßer static void 71878bc019dSStefan Eßer bc_num_unshiftZero(BcNum* restrict n, size_t places_rdx) 71978bc019dSStefan Eßer { 720252884aeSStefan Eßer n->len += places_rdx; 721252884aeSStefan Eßer n->num -= places_rdx; 722252884aeSStefan Eßer } 723252884aeSStefan Eßer 72444d4804dSStefan Eßer /** 72544d4804dSStefan Eßer * Shifts the digits right within a number by no more than BC_BASE_DIGS - 1. 72644d4804dSStefan Eßer * This is the final step on shifting numbers with the shift operators. It 72744d4804dSStefan Eßer * depends on the caller to shift the limbs properly because all it does is 72844d4804dSStefan Eßer * ensure that the rdx point is realigned to be between limbs. 72944d4804dSStefan Eßer * @param n The number to shift digits in. 73044d4804dSStefan Eßer * @param dig The number of places to shift right. 73144d4804dSStefan Eßer */ 73278bc019dSStefan Eßer static void 73378bc019dSStefan Eßer bc_num_shift(BcNum* restrict n, BcBigDig dig) 73478bc019dSStefan Eßer { 735252884aeSStefan Eßer size_t i, len = n->len; 736252884aeSStefan Eßer BcBigDig carry = 0, pow; 737252884aeSStefan Eßer BcDig* ptr = n->num; 738252884aeSStefan Eßer 739252884aeSStefan Eßer assert(dig < BC_BASE_DIGS); 740252884aeSStefan Eßer 74144d4804dSStefan Eßer // Figure out the parameters for division. 742252884aeSStefan Eßer pow = bc_num_pow10[dig]; 743252884aeSStefan Eßer dig = bc_num_pow10[BC_BASE_DIGS - dig]; 744252884aeSStefan Eßer 74544d4804dSStefan Eßer // Run a series of divisions and mods with carries across the entire number 74644d4804dSStefan Eßer // array. This effectively shifts everything over. 74778bc019dSStefan Eßer for (i = len - 1; i < len; --i) 74878bc019dSStefan Eßer { 749252884aeSStefan Eßer BcBigDig in, temp; 750252884aeSStefan Eßer in = ((BcBigDig) ptr[i]); 751252884aeSStefan Eßer temp = carry * dig; 752252884aeSStefan Eßer carry = in % pow; 753252884aeSStefan Eßer ptr[i] = ((BcDig) (in / pow)) + (BcDig) temp; 754d101cdd6SStefan Eßer assert(ptr[i] >= 0 && ptr[i] < BC_BASE_POW); 755252884aeSStefan Eßer } 756252884aeSStefan Eßer 757252884aeSStefan Eßer assert(!carry); 758252884aeSStefan Eßer } 759252884aeSStefan Eßer 76044d4804dSStefan Eßer /** 76144d4804dSStefan Eßer * Shift a number left by a certain number of places. This is the workhorse of 76244d4804dSStefan Eßer * the left shift operator. 76344d4804dSStefan Eßer * @param n The number to shift left. 76444d4804dSStefan Eßer * @param places The amount of places to shift @a n left by. 76544d4804dSStefan Eßer */ 76678bc019dSStefan Eßer static void 76778bc019dSStefan Eßer bc_num_shiftLeft(BcNum* restrict n, size_t places) 76878bc019dSStefan Eßer { 769252884aeSStefan Eßer BcBigDig dig; 770252884aeSStefan Eßer size_t places_rdx; 771252884aeSStefan Eßer bool shift; 772252884aeSStefan Eßer 773252884aeSStefan Eßer if (!places) return; 77444d4804dSStefan Eßer 77544d4804dSStefan Eßer // Make sure to grow the number if necessary. 77678bc019dSStefan Eßer if (places > n->scale) 77778bc019dSStefan Eßer { 778252884aeSStefan Eßer size_t size = bc_vm_growSize(BC_NUM_RDX(places - n->scale), n->len); 77944d4804dSStefan Eßer if (size > SIZE_MAX - 1) bc_err(BC_ERR_MATH_OVERFLOW); 780252884aeSStefan Eßer } 78144d4804dSStefan Eßer 78244d4804dSStefan Eßer // If zero, we can just set the scale and bail. 78378bc019dSStefan Eßer if (BC_NUM_ZERO(n)) 78478bc019dSStefan Eßer { 785252884aeSStefan Eßer if (n->scale >= places) n->scale -= places; 786252884aeSStefan Eßer else n->scale = 0; 787252884aeSStefan Eßer return; 788252884aeSStefan Eßer } 789252884aeSStefan Eßer 79044d4804dSStefan Eßer // When I changed bc to have multiple digits per limb, this was the hardest 79144d4804dSStefan Eßer // code to change. This and shift right. Make sure you understand this 79244d4804dSStefan Eßer // before attempting anything. 79344d4804dSStefan Eßer 79444d4804dSStefan Eßer // This is how many limbs we will shift. 795252884aeSStefan Eßer dig = (BcBigDig) (places % BC_BASE_DIGS); 796252884aeSStefan Eßer shift = (dig != 0); 79744d4804dSStefan Eßer 79844d4804dSStefan Eßer // Convert places to a rdx value. 799252884aeSStefan Eßer places_rdx = BC_NUM_RDX(places); 800252884aeSStefan Eßer 80144d4804dSStefan Eßer // If the number is not an integer, we need special care. The reason an 80244d4804dSStefan Eßer // integer doesn't is because left shift would only extend the integer, 80344d4804dSStefan Eßer // whereas a non-integer might have its fractional part eliminated or only 80444d4804dSStefan Eßer // partially eliminated. 80578bc019dSStefan Eßer if (n->scale) 80678bc019dSStefan Eßer { 80750696a6eSStefan Eßer size_t nrdx = BC_NUM_RDX_VAL(n); 80850696a6eSStefan Eßer 80944d4804dSStefan Eßer // If the number's rdx is bigger, that's the hard case. 81078bc019dSStefan Eßer if (nrdx >= places_rdx) 81178bc019dSStefan Eßer { 812252884aeSStefan Eßer size_t mod = n->scale % BC_BASE_DIGS, revdig; 813252884aeSStefan Eßer 81444d4804dSStefan Eßer // We want mod to be in the range [1, BC_BASE_DIGS], not 81544d4804dSStefan Eßer // [0, BC_BASE_DIGS). 816252884aeSStefan Eßer mod = mod ? mod : BC_BASE_DIGS; 81744d4804dSStefan Eßer 81844d4804dSStefan Eßer // We need to reverse dig to get the actual number of digits. 819252884aeSStefan Eßer revdig = dig ? BC_BASE_DIGS - dig : 0; 820252884aeSStefan Eßer 82144d4804dSStefan Eßer // If the two overflow BC_BASE_DIGS, we need to move an extra place. 822252884aeSStefan Eßer if (mod + revdig > BC_BASE_DIGS) places_rdx = 1; 823252884aeSStefan Eßer else places_rdx = 0; 824252884aeSStefan Eßer } 82550696a6eSStefan Eßer else places_rdx -= nrdx; 826252884aeSStefan Eßer } 827252884aeSStefan Eßer 82844d4804dSStefan Eßer // If this is non-zero, we need an extra place, so expand, move, and set. 82978bc019dSStefan Eßer if (places_rdx) 83078bc019dSStefan Eßer { 831252884aeSStefan Eßer bc_num_expand(n, bc_vm_growSize(n->len, places_rdx)); 83278bc019dSStefan Eßer // NOLINTNEXTLINE 833252884aeSStefan Eßer memmove(n->num + places_rdx, n->num, BC_NUM_SIZE(n->len)); 83478bc019dSStefan Eßer // NOLINTNEXTLINE 835252884aeSStefan Eßer memset(n->num, 0, BC_NUM_SIZE(places_rdx)); 836252884aeSStefan Eßer n->len += places_rdx; 837252884aeSStefan Eßer } 838252884aeSStefan Eßer 83944d4804dSStefan Eßer // Set the scale appropriately. 84078bc019dSStefan Eßer if (places > n->scale) 84178bc019dSStefan Eßer { 84250696a6eSStefan Eßer n->scale = 0; 84350696a6eSStefan Eßer BC_NUM_RDX_SET(n, 0); 84450696a6eSStefan Eßer } 84578bc019dSStefan Eßer else 84678bc019dSStefan Eßer { 847252884aeSStefan Eßer n->scale -= places; 84850696a6eSStefan Eßer BC_NUM_RDX_SET(n, BC_NUM_RDX(n->scale)); 849252884aeSStefan Eßer } 850252884aeSStefan Eßer 85144d4804dSStefan Eßer // Finally, shift within limbs. 852252884aeSStefan Eßer if (shift) bc_num_shift(n, BC_BASE_DIGS - dig); 853252884aeSStefan Eßer 854252884aeSStefan Eßer bc_num_clean(n); 855252884aeSStefan Eßer } 856252884aeSStefan Eßer 85778bc019dSStefan Eßer void 85878bc019dSStefan Eßer bc_num_shiftRight(BcNum* restrict n, size_t places) 85978bc019dSStefan Eßer { 860252884aeSStefan Eßer BcBigDig dig; 861252884aeSStefan Eßer size_t places_rdx, scale, scale_mod, int_len, expand; 862252884aeSStefan Eßer bool shift; 863252884aeSStefan Eßer 864252884aeSStefan Eßer if (!places) return; 86544d4804dSStefan Eßer 86644d4804dSStefan Eßer // If zero, we can just set the scale and bail. 86778bc019dSStefan Eßer if (BC_NUM_ZERO(n)) 86878bc019dSStefan Eßer { 869252884aeSStefan Eßer n->scale += places; 870252884aeSStefan Eßer bc_num_expand(n, BC_NUM_RDX(n->scale)); 871252884aeSStefan Eßer return; 872252884aeSStefan Eßer } 873252884aeSStefan Eßer 87444d4804dSStefan Eßer // Amount within a limb we have to shift by. 875252884aeSStefan Eßer dig = (BcBigDig) (places % BC_BASE_DIGS); 876252884aeSStefan Eßer shift = (dig != 0); 87744d4804dSStefan Eßer 878252884aeSStefan Eßer scale = n->scale; 87944d4804dSStefan Eßer 88044d4804dSStefan Eßer // Figure out how the scale is affected. 881252884aeSStefan Eßer scale_mod = scale % BC_BASE_DIGS; 882252884aeSStefan Eßer scale_mod = scale_mod ? scale_mod : BC_BASE_DIGS; 88344d4804dSStefan Eßer 88444d4804dSStefan Eßer // We need to know the int length and rdx for places. 885252884aeSStefan Eßer int_len = bc_num_int(n); 886252884aeSStefan Eßer places_rdx = BC_NUM_RDX(places); 887252884aeSStefan Eßer 88844d4804dSStefan Eßer // If we are going to shift past a limb boundary or not, set accordingly. 88978bc019dSStefan Eßer if (scale_mod + dig > BC_BASE_DIGS) 89078bc019dSStefan Eßer { 891252884aeSStefan Eßer expand = places_rdx - 1; 892252884aeSStefan Eßer places_rdx = 1; 893252884aeSStefan Eßer } 89478bc019dSStefan Eßer else 89578bc019dSStefan Eßer { 896252884aeSStefan Eßer expand = places_rdx; 897252884aeSStefan Eßer places_rdx = 0; 898252884aeSStefan Eßer } 899252884aeSStefan Eßer 90044d4804dSStefan Eßer // Clamp expanding. 901252884aeSStefan Eßer if (expand > int_len) expand -= int_len; 902252884aeSStefan Eßer else expand = 0; 903252884aeSStefan Eßer 90444d4804dSStefan Eßer // Extend, expand, and zero. 905252884aeSStefan Eßer bc_num_extend(n, places_rdx * BC_BASE_DIGS); 906252884aeSStefan Eßer bc_num_expand(n, bc_vm_growSize(expand, n->len)); 90778bc019dSStefan Eßer // NOLINTNEXTLINE 908252884aeSStefan Eßer memset(n->num + n->len, 0, BC_NUM_SIZE(expand)); 90944d4804dSStefan Eßer 91044d4804dSStefan Eßer // Set the fields. 911252884aeSStefan Eßer n->len += expand; 91250696a6eSStefan Eßer n->scale = 0; 91350696a6eSStefan Eßer BC_NUM_RDX_SET(n, 0); 914252884aeSStefan Eßer 91544d4804dSStefan Eßer // Finally, shift within limbs. 916252884aeSStefan Eßer if (shift) bc_num_shift(n, dig); 917252884aeSStefan Eßer 918252884aeSStefan Eßer n->scale = scale + places; 91950696a6eSStefan Eßer BC_NUM_RDX_SET(n, BC_NUM_RDX(n->scale)); 920252884aeSStefan Eßer 921252884aeSStefan Eßer bc_num_clean(n); 922252884aeSStefan Eßer 92350696a6eSStefan Eßer assert(BC_NUM_RDX_VAL(n) <= n->len && n->len <= n->cap); 92450696a6eSStefan Eßer assert(BC_NUM_RDX_VAL(n) == BC_NUM_RDX(n->scale)); 925252884aeSStefan Eßer } 926252884aeSStefan Eßer 92744d4804dSStefan Eßer /** 92844d4804dSStefan Eßer * Tests if a number is a integer with scale or not. Returns true if the number 92944d4804dSStefan Eßer * is not an integer. If it is, its integer shifted form is copied into the 93044d4804dSStefan Eßer * result parameter for use where only integers are allowed. 93144d4804dSStefan Eßer * @param n The integer to test and shift. 93244d4804dSStefan Eßer * @param r The number to store the shifted result into. This number should 93344d4804dSStefan Eßer * *not* be allocated. 93444d4804dSStefan Eßer * @return True if the number is a non-integer, false otherwise. 93544d4804dSStefan Eßer */ 93678bc019dSStefan Eßer static bool 93778bc019dSStefan Eßer bc_num_nonInt(const BcNum* restrict n, BcNum* restrict r) 93878bc019dSStefan Eßer { 93944d4804dSStefan Eßer bool zero; 94044d4804dSStefan Eßer size_t i, rdx = BC_NUM_RDX_VAL(n); 94144d4804dSStefan Eßer 94278bc019dSStefan Eßer if (!rdx) 94378bc019dSStefan Eßer { 94478bc019dSStefan Eßer // NOLINTNEXTLINE 94544d4804dSStefan Eßer memcpy(r, n, sizeof(BcNum)); 94644d4804dSStefan Eßer return false; 94744d4804dSStefan Eßer } 94844d4804dSStefan Eßer 94944d4804dSStefan Eßer zero = true; 95044d4804dSStefan Eßer 95178bc019dSStefan Eßer for (i = 0; zero && i < rdx; ++i) 95278bc019dSStefan Eßer { 95378bc019dSStefan Eßer zero = (n->num[i] == 0); 95478bc019dSStefan Eßer } 95544d4804dSStefan Eßer 95644d4804dSStefan Eßer if (BC_ERR(!zero)) return true; 95744d4804dSStefan Eßer 95844d4804dSStefan Eßer bc_num_shiftRdx(n, r); 95944d4804dSStefan Eßer 96044d4804dSStefan Eßer return false; 961252884aeSStefan Eßer } 962252884aeSStefan Eßer 963252884aeSStefan Eßer #if BC_ENABLE_EXTRA_MATH 96444d4804dSStefan Eßer 96544d4804dSStefan Eßer /** 96644d4804dSStefan Eßer * Execute common code for an operater that needs an integer for the second 96744d4804dSStefan Eßer * operand and return the integer operand as a BcBigDig. 96844d4804dSStefan Eßer * @param a The first operand. 96944d4804dSStefan Eßer * @param b The second operand. 97044d4804dSStefan Eßer * @param c The result operand. 97144d4804dSStefan Eßer * @return The second operand as a hardware integer. 97244d4804dSStefan Eßer */ 97378bc019dSStefan Eßer static BcBigDig 97478bc019dSStefan Eßer bc_num_intop(const BcNum* a, const BcNum* b, BcNum* restrict c) 975252884aeSStefan Eßer { 97644d4804dSStefan Eßer BcNum temp; 97744d4804dSStefan Eßer 978d101cdd6SStefan Eßer #if BC_GCC 979d101cdd6SStefan Eßer temp.len = 0; 980d101cdd6SStefan Eßer temp.rdx = 0; 981d101cdd6SStefan Eßer temp.num = NULL; 982d101cdd6SStefan Eßer #endif // BC_GCC 983d101cdd6SStefan Eßer 98444d4804dSStefan Eßer if (BC_ERR(bc_num_nonInt(b, &temp))) bc_err(BC_ERR_MATH_NON_INTEGER); 98544d4804dSStefan Eßer 986252884aeSStefan Eßer bc_num_copy(c, a); 98744d4804dSStefan Eßer 98844d4804dSStefan Eßer return bc_num_bigdig(&temp); 989252884aeSStefan Eßer } 990252884aeSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH 991252884aeSStefan Eßer 99244d4804dSStefan Eßer /** 99344d4804dSStefan Eßer * This is the actual implementation of add *and* subtract. Since this function 99444d4804dSStefan Eßer * doesn't need to use scale (per the bc spec), I am hijacking it to say whether 99544d4804dSStefan Eßer * it's doing an add or a subtract. And then I convert substraction to addition 99644d4804dSStefan Eßer * of negative second operand. This is a BcNumBinOp function. 99744d4804dSStefan Eßer * @param a The first operand. 99844d4804dSStefan Eßer * @param b The second operand. 99944d4804dSStefan Eßer * @param c The return parameter. 100044d4804dSStefan Eßer * @param sub Non-zero for a subtract, zero for an add. 100144d4804dSStefan Eßer */ 100278bc019dSStefan Eßer static void 100378bc019dSStefan Eßer bc_num_as(BcNum* a, BcNum* b, BcNum* restrict c, size_t sub) 100478bc019dSStefan Eßer { 100578bc019dSStefan Eßer BcDig* ptr_c; 100678bc019dSStefan Eßer BcDig* ptr_l; 100778bc019dSStefan Eßer BcDig* ptr_r; 1008252884aeSStefan Eßer size_t i, min_rdx, max_rdx, diff, a_int, b_int, min_len, max_len, max_int; 100950696a6eSStefan Eßer size_t len_l, len_r, ardx, brdx; 101050696a6eSStefan Eßer bool b_neg, do_sub, do_rev_sub, carry, c_neg; 1011252884aeSStefan Eßer 101278bc019dSStefan Eßer if (BC_NUM_ZERO(b)) 101378bc019dSStefan Eßer { 1014252884aeSStefan Eßer bc_num_copy(c, a); 1015252884aeSStefan Eßer return; 1016252884aeSStefan Eßer } 101744d4804dSStefan Eßer 101878bc019dSStefan Eßer if (BC_NUM_ZERO(a)) 101978bc019dSStefan Eßer { 1020252884aeSStefan Eßer bc_num_copy(c, b); 102150696a6eSStefan Eßer c->rdx = BC_NUM_NEG_VAL(c, BC_NUM_NEG(b) != sub); 1022252884aeSStefan Eßer return; 1023252884aeSStefan Eßer } 1024252884aeSStefan Eßer 1025252884aeSStefan Eßer // Invert sign of b if it is to be subtracted. This operation must 102644d4804dSStefan Eßer // precede the tests for any of the operands being zero. 102750696a6eSStefan Eßer b_neg = (BC_NUM_NEG(b) != sub); 1028252884aeSStefan Eßer 102944d4804dSStefan Eßer // Figure out if we will actually add the numbers if their signs are equal 103044d4804dSStefan Eßer // or subtract. 103150696a6eSStefan Eßer do_sub = (BC_NUM_NEG(a) != b_neg); 1032252884aeSStefan Eßer 1033252884aeSStefan Eßer a_int = bc_num_int(a); 1034252884aeSStefan Eßer b_int = bc_num_int(b); 1035252884aeSStefan Eßer max_int = BC_MAX(a_int, b_int); 1036252884aeSStefan Eßer 103744d4804dSStefan Eßer // Figure out which number will have its last limbs copied (for addition) or 103844d4804dSStefan Eßer // subtracted (for subtraction). 103950696a6eSStefan Eßer ardx = BC_NUM_RDX_VAL(a); 104050696a6eSStefan Eßer brdx = BC_NUM_RDX_VAL(b); 104150696a6eSStefan Eßer min_rdx = BC_MIN(ardx, brdx); 104250696a6eSStefan Eßer max_rdx = BC_MAX(ardx, brdx); 1043252884aeSStefan Eßer diff = max_rdx - min_rdx; 1044252884aeSStefan Eßer 1045252884aeSStefan Eßer max_len = max_int + max_rdx; 1046252884aeSStefan Eßer 104778bc019dSStefan Eßer if (do_sub) 104878bc019dSStefan Eßer { 1049252884aeSStefan Eßer // Check whether b has to be subtracted from a or a from b. 1050252884aeSStefan Eßer if (a_int != b_int) do_rev_sub = (a_int < b_int); 105150696a6eSStefan Eßer else if (ardx > brdx) 105278bc019dSStefan Eßer { 1053252884aeSStefan Eßer do_rev_sub = (bc_num_compare(a->num + diff, b->num, b->len) < 0); 1054252884aeSStefan Eßer } 105578bc019dSStefan Eßer else do_rev_sub = (bc_num_compare(a->num, b->num + diff, a->len) <= 0); 105678bc019dSStefan Eßer } 105778bc019dSStefan Eßer else 105878bc019dSStefan Eßer { 1059252884aeSStefan Eßer // The result array of the addition might come out one element 1060252884aeSStefan Eßer // longer than the bigger of the operand arrays. 1061252884aeSStefan Eßer max_len += 1; 1062252884aeSStefan Eßer do_rev_sub = (a_int < b_int); 1063252884aeSStefan Eßer } 1064252884aeSStefan Eßer 1065252884aeSStefan Eßer assert(max_len <= c->cap); 1066252884aeSStefan Eßer 106744d4804dSStefan Eßer // Cache values for simple code later. 106878bc019dSStefan Eßer if (do_rev_sub) 106978bc019dSStefan Eßer { 1070252884aeSStefan Eßer ptr_l = b->num; 1071252884aeSStefan Eßer ptr_r = a->num; 1072252884aeSStefan Eßer len_l = b->len; 1073252884aeSStefan Eßer len_r = a->len; 1074252884aeSStefan Eßer } 107578bc019dSStefan Eßer else 107678bc019dSStefan Eßer { 1077252884aeSStefan Eßer ptr_l = a->num; 1078252884aeSStefan Eßer ptr_r = b->num; 1079252884aeSStefan Eßer len_l = a->len; 1080252884aeSStefan Eßer len_r = b->len; 1081252884aeSStefan Eßer } 1082252884aeSStefan Eßer 1083252884aeSStefan Eßer ptr_c = c->num; 1084252884aeSStefan Eßer carry = false; 1085252884aeSStefan Eßer 108644d4804dSStefan Eßer // This is true if the numbers have a different number of limbs after the 108744d4804dSStefan Eßer // decimal point. 108878bc019dSStefan Eßer if (diff) 108978bc019dSStefan Eßer { 1090252884aeSStefan Eßer // If the rdx values of the operands do not match, the result will 1091252884aeSStefan Eßer // have low end elements that are the positive or negative trailing 1092252884aeSStefan Eßer // elements of the operand with higher rdx value. 109378bc019dSStefan Eßer if ((ardx > brdx) != do_rev_sub) 109478bc019dSStefan Eßer { 109550696a6eSStefan Eßer // !do_rev_sub && ardx > brdx || do_rev_sub && brdx > ardx 1096252884aeSStefan Eßer // The left operand has BcDig values that need to be copied, 1097252884aeSStefan Eßer // either from a or from b (in case of a reversed subtraction). 109878bc019dSStefan Eßer // NOLINTNEXTLINE 1099252884aeSStefan Eßer memcpy(ptr_c, ptr_l, BC_NUM_SIZE(diff)); 1100252884aeSStefan Eßer ptr_l += diff; 1101252884aeSStefan Eßer len_l -= diff; 1102252884aeSStefan Eßer } 110378bc019dSStefan Eßer else 110478bc019dSStefan Eßer { 1105252884aeSStefan Eßer // The right operand has BcDig values that need to be copied 1106252884aeSStefan Eßer // or subtracted from zero (in case of a subtraction). 110778bc019dSStefan Eßer if (do_sub) 110878bc019dSStefan Eßer { 110950696a6eSStefan Eßer // do_sub (do_rev_sub && ardx > brdx || 111050696a6eSStefan Eßer // !do_rev_sub && brdx > ardx) 1111252884aeSStefan Eßer for (i = 0; i < diff; i++) 111278bc019dSStefan Eßer { 1113252884aeSStefan Eßer ptr_c[i] = bc_num_subDigits(0, ptr_r[i], &carry); 1114252884aeSStefan Eßer } 111578bc019dSStefan Eßer } 111678bc019dSStefan Eßer else 111778bc019dSStefan Eßer { 111850696a6eSStefan Eßer // !do_sub && brdx > ardx 111978bc019dSStefan Eßer // NOLINTNEXTLINE 1120252884aeSStefan Eßer memcpy(ptr_c, ptr_r, BC_NUM_SIZE(diff)); 1121252884aeSStefan Eßer } 1122252884aeSStefan Eßer 112344d4804dSStefan Eßer // Future code needs to ignore the limbs we just did. 1124252884aeSStefan Eßer ptr_r += diff; 1125252884aeSStefan Eßer len_r -= diff; 1126252884aeSStefan Eßer } 1127252884aeSStefan Eßer 112844d4804dSStefan Eßer // The return value pointer needs to ignore what we just did. 1129252884aeSStefan Eßer ptr_c += diff; 1130252884aeSStefan Eßer } 1131252884aeSStefan Eßer 113244d4804dSStefan Eßer // This is the length that can be directly added/subtracted. 1133252884aeSStefan Eßer min_len = BC_MIN(len_l, len_r); 1134252884aeSStefan Eßer 1135252884aeSStefan Eßer // After dealing with possible low array elements that depend on only one 113644d4804dSStefan Eßer // operand above, the actual add or subtract can be performed as if the rdx 113744d4804dSStefan Eßer // of both operands was the same. 113844d4804dSStefan Eßer // 1139252884aeSStefan Eßer // Inlining takes care of eliminating constant zero arguments to 1140252884aeSStefan Eßer // addDigit/subDigit (checked in disassembly of resulting bc binary 1141252884aeSStefan Eßer // compiled with gcc and clang). 114278bc019dSStefan Eßer if (do_sub) 114378bc019dSStefan Eßer { 114444d4804dSStefan Eßer // Actual subtraction. 1145252884aeSStefan Eßer for (i = 0; i < min_len; ++i) 114678bc019dSStefan Eßer { 1147252884aeSStefan Eßer ptr_c[i] = bc_num_subDigits(ptr_l[i], ptr_r[i], &carry); 114878bc019dSStefan Eßer } 114944d4804dSStefan Eßer 115044d4804dSStefan Eßer // Finishing the limbs beyond the direct subtraction. 115178bc019dSStefan Eßer for (; i < len_l; ++i) 115278bc019dSStefan Eßer { 115378bc019dSStefan Eßer ptr_c[i] = bc_num_subDigits(ptr_l[i], 0, &carry); 1154252884aeSStefan Eßer } 115578bc019dSStefan Eßer } 115678bc019dSStefan Eßer else 115778bc019dSStefan Eßer { 115844d4804dSStefan Eßer // Actual addition. 1159252884aeSStefan Eßer for (i = 0; i < min_len; ++i) 116078bc019dSStefan Eßer { 1161252884aeSStefan Eßer ptr_c[i] = bc_num_addDigits(ptr_l[i], ptr_r[i], &carry); 116278bc019dSStefan Eßer } 116344d4804dSStefan Eßer 116444d4804dSStefan Eßer // Finishing the limbs beyond the direct addition. 116578bc019dSStefan Eßer for (; i < len_l; ++i) 116678bc019dSStefan Eßer { 116778bc019dSStefan Eßer ptr_c[i] = bc_num_addDigits(ptr_l[i], 0, &carry); 116878bc019dSStefan Eßer } 116944d4804dSStefan Eßer 117044d4804dSStefan Eßer // Addition can create an extra limb. We take care of that here. 1171252884aeSStefan Eßer ptr_c[i] = bc_num_addDigits(0, 0, &carry); 1172252884aeSStefan Eßer } 1173252884aeSStefan Eßer 1174252884aeSStefan Eßer assert(carry == false); 1175252884aeSStefan Eßer 1176252884aeSStefan Eßer // The result has the same sign as a, unless the operation was a 1177252884aeSStefan Eßer // reverse subtraction (b - a). 117850696a6eSStefan Eßer c_neg = BC_NUM_NEG(a) != (do_sub && do_rev_sub); 117950696a6eSStefan Eßer BC_NUM_RDX_SET_NEG(c, max_rdx, c_neg); 1180252884aeSStefan Eßer c->len = max_len; 1181252884aeSStefan Eßer c->scale = BC_MAX(a->scale, b->scale); 1182252884aeSStefan Eßer 1183252884aeSStefan Eßer bc_num_clean(c); 1184252884aeSStefan Eßer } 1185252884aeSStefan Eßer 118644d4804dSStefan Eßer /** 118744d4804dSStefan Eßer * The simple multiplication that karatsuba dishes out to when the length of the 118844d4804dSStefan Eßer * numbers gets low enough. This doesn't use scale because it treats the 118944d4804dSStefan Eßer * operands as though they are integers. 119044d4804dSStefan Eßer * @param a The first operand. 119144d4804dSStefan Eßer * @param b The second operand. 119244d4804dSStefan Eßer * @param c The return parameter. 119344d4804dSStefan Eßer */ 119478bc019dSStefan Eßer static void 119578bc019dSStefan Eßer bc_num_m_simp(const BcNum* a, const BcNum* b, BcNum* restrict c) 119678bc019dSStefan Eßer { 1197252884aeSStefan Eßer size_t i, alen = a->len, blen = b->len, clen; 119878bc019dSStefan Eßer BcDig* ptr_a = a->num; 119978bc019dSStefan Eßer BcDig* ptr_b = b->num; 120078bc019dSStefan Eßer BcDig* ptr_c; 1201252884aeSStefan Eßer BcBigDig sum = 0, carry = 0; 1202252884aeSStefan Eßer 1203252884aeSStefan Eßer assert(sizeof(sum) >= sizeof(BcDig) * 2); 120450696a6eSStefan Eßer assert(!BC_NUM_RDX_VAL(a) && !BC_NUM_RDX_VAL(b)); 1205252884aeSStefan Eßer 120644d4804dSStefan Eßer // Make sure c is big enough. 1207252884aeSStefan Eßer clen = bc_vm_growSize(alen, blen); 1208252884aeSStefan Eßer bc_num_expand(c, bc_vm_growSize(clen, 1)); 1209252884aeSStefan Eßer 121044d4804dSStefan Eßer // If we don't memset, then we might have uninitialized data use later. 1211252884aeSStefan Eßer ptr_c = c->num; 121278bc019dSStefan Eßer // NOLINTNEXTLINE 1213252884aeSStefan Eßer memset(ptr_c, 0, BC_NUM_SIZE(c->cap)); 1214252884aeSStefan Eßer 121544d4804dSStefan Eßer // This is the actual multiplication loop. It uses the lattice form of long 121644d4804dSStefan Eßer // multiplication (see the explanation on the web page at 121744d4804dSStefan Eßer // https://knilt.arcc.albany.edu/What_is_Lattice_Multiplication or the 121844d4804dSStefan Eßer // explanation at Wikipedia). 121978bc019dSStefan Eßer for (i = 0; i < clen; ++i) 122078bc019dSStefan Eßer { 1221252884aeSStefan Eßer ssize_t sidx = (ssize_t) (i - blen + 1); 122244d4804dSStefan Eßer size_t j, k; 1223252884aeSStefan Eßer 122444d4804dSStefan Eßer // These are the start indices. 122544d4804dSStefan Eßer j = (size_t) BC_MAX(0, sidx); 122644d4804dSStefan Eßer k = BC_MIN(i, blen - 1); 122744d4804dSStefan Eßer 122844d4804dSStefan Eßer // On every iteration of this loop, a multiplication happens, then the 122944d4804dSStefan Eßer // sum is automatically calculated. 123078bc019dSStefan Eßer for (; j < alen && k < blen; ++j, --k) 123178bc019dSStefan Eßer { 1232252884aeSStefan Eßer sum += ((BcBigDig) ptr_a[j]) * ((BcBigDig) ptr_b[k]); 1233252884aeSStefan Eßer 123478bc019dSStefan Eßer if (sum >= ((BcBigDig) BC_BASE_POW) * BC_BASE_POW) 123578bc019dSStefan Eßer { 1236252884aeSStefan Eßer carry += sum / BC_BASE_POW; 1237252884aeSStefan Eßer sum %= BC_BASE_POW; 1238252884aeSStefan Eßer } 1239252884aeSStefan Eßer } 1240252884aeSStefan Eßer 124144d4804dSStefan Eßer // Calculate the carry. 124278bc019dSStefan Eßer if (sum >= BC_BASE_POW) 124378bc019dSStefan Eßer { 1244252884aeSStefan Eßer carry += sum / BC_BASE_POW; 1245252884aeSStefan Eßer sum %= BC_BASE_POW; 1246252884aeSStefan Eßer } 1247252884aeSStefan Eßer 124844d4804dSStefan Eßer // Store and set up for next iteration. 1249252884aeSStefan Eßer ptr_c[i] = (BcDig) sum; 1250252884aeSStefan Eßer assert(ptr_c[i] < BC_BASE_POW); 1251252884aeSStefan Eßer sum = carry; 1252252884aeSStefan Eßer carry = 0; 1253252884aeSStefan Eßer } 1254252884aeSStefan Eßer 1255252884aeSStefan Eßer // This should always be true because there should be no carry on the last 1256252884aeSStefan Eßer // digit; multiplication never goes above the sum of both lengths. 1257252884aeSStefan Eßer assert(!sum); 1258252884aeSStefan Eßer 1259252884aeSStefan Eßer c->len = clen; 1260252884aeSStefan Eßer } 1261252884aeSStefan Eßer 126244d4804dSStefan Eßer /** 126344d4804dSStefan Eßer * Does a shifted add or subtract for Karatsuba below. This calls either 126444d4804dSStefan Eßer * bc_num_addArrays() or bc_num_subArrays(). 126544d4804dSStefan Eßer * @param n An in/out parameter; the first operand and return parameter. 126644d4804dSStefan Eßer * @param a The second operand. 126744d4804dSStefan Eßer * @param shift The amount to shift @a n by when adding/subtracting. 126844d4804dSStefan Eßer * @param op The function to call, either bc_num_addArrays() or 126944d4804dSStefan Eßer * bc_num_subArrays(). 127044d4804dSStefan Eßer */ 127178bc019dSStefan Eßer static void 127278bc019dSStefan Eßer bc_num_shiftAddSub(BcNum* restrict n, const BcNum* restrict a, size_t shift, 127378bc019dSStefan Eßer BcNumShiftAddOp op) 1274252884aeSStefan Eßer { 1275252884aeSStefan Eßer assert(n->len >= shift + a->len); 127650696a6eSStefan Eßer assert(!BC_NUM_RDX_VAL(n) && !BC_NUM_RDX_VAL(a)); 1277252884aeSStefan Eßer op(n->num + shift, a->num, a->len); 1278252884aeSStefan Eßer } 1279252884aeSStefan Eßer 128044d4804dSStefan Eßer /** 128144d4804dSStefan Eßer * Implements the Karatsuba algorithm. 128244d4804dSStefan Eßer */ 128378bc019dSStefan Eßer static void 128478bc019dSStefan Eßer bc_num_k(const BcNum* a, const BcNum* b, BcNum* restrict c) 128578bc019dSStefan Eßer { 1286252884aeSStefan Eßer size_t max, max2, total; 1287252884aeSStefan Eßer BcNum l1, h1, l2, h2, m2, m1, z0, z1, z2, temp; 128878bc019dSStefan Eßer BcDig* digs; 128978bc019dSStefan Eßer BcDig* dig_ptr; 1290252884aeSStefan Eßer BcNumShiftAddOp op; 1291252884aeSStefan Eßer bool aone = BC_NUM_ONE(a); 1292d101cdd6SStefan Eßer #if BC_ENABLE_LIBRARY 1293d101cdd6SStefan Eßer BcVm* vm = bcl_getspecific(); 1294d101cdd6SStefan Eßer #endif // BC_ENABLE_LIBRARY 1295252884aeSStefan Eßer 1296252884aeSStefan Eßer assert(BC_NUM_ZERO(c)); 1297252884aeSStefan Eßer 1298252884aeSStefan Eßer if (BC_NUM_ZERO(a) || BC_NUM_ZERO(b)) return; 129944d4804dSStefan Eßer 130078bc019dSStefan Eßer if (aone || BC_NUM_ONE(b)) 130178bc019dSStefan Eßer { 1302252884aeSStefan Eßer bc_num_copy(c, aone ? b : a); 130350696a6eSStefan Eßer if ((aone && BC_NUM_NEG(a)) || BC_NUM_NEG(b)) BC_NUM_NEG_TGL(c); 1304252884aeSStefan Eßer return; 1305252884aeSStefan Eßer } 130644d4804dSStefan Eßer 130744d4804dSStefan Eßer // Shell out to the simple algorithm with certain conditions. 130878bc019dSStefan Eßer if (a->len < BC_NUM_KARATSUBA_LEN || b->len < BC_NUM_KARATSUBA_LEN) 130978bc019dSStefan Eßer { 1310252884aeSStefan Eßer bc_num_m_simp(a, b, c); 1311252884aeSStefan Eßer return; 1312252884aeSStefan Eßer } 1313252884aeSStefan Eßer 131444d4804dSStefan Eßer // We need to calculate the max size of the numbers that can result from the 131544d4804dSStefan Eßer // operations. 1316252884aeSStefan Eßer max = BC_MAX(a->len, b->len); 1317252884aeSStefan Eßer max = BC_MAX(max, BC_NUM_DEF_SIZE); 1318252884aeSStefan Eßer max2 = (max + 1) / 2; 1319252884aeSStefan Eßer 132044d4804dSStefan Eßer // Calculate the space needed for all of the temporary allocations. We do 132144d4804dSStefan Eßer // this to just allocate once. 1322252884aeSStefan Eßer total = bc_vm_arraySize(BC_NUM_KARATSUBA_ALLOCS, max); 1323252884aeSStefan Eßer 1324252884aeSStefan Eßer BC_SIG_LOCK; 1325252884aeSStefan Eßer 132644d4804dSStefan Eßer // Allocate space for all of the temporaries. 1327252884aeSStefan Eßer digs = dig_ptr = bc_vm_malloc(BC_NUM_SIZE(total)); 1328252884aeSStefan Eßer 132944d4804dSStefan Eßer // Set up the temporaries. 1330252884aeSStefan Eßer bc_num_setup(&l1, dig_ptr, max); 1331252884aeSStefan Eßer dig_ptr += max; 1332252884aeSStefan Eßer bc_num_setup(&h1, dig_ptr, max); 1333252884aeSStefan Eßer dig_ptr += max; 1334252884aeSStefan Eßer bc_num_setup(&l2, dig_ptr, max); 1335252884aeSStefan Eßer dig_ptr += max; 1336252884aeSStefan Eßer bc_num_setup(&h2, dig_ptr, max); 1337252884aeSStefan Eßer dig_ptr += max; 1338252884aeSStefan Eßer bc_num_setup(&m1, dig_ptr, max); 1339252884aeSStefan Eßer dig_ptr += max; 1340252884aeSStefan Eßer bc_num_setup(&m2, dig_ptr, max); 134144d4804dSStefan Eßer 134244d4804dSStefan Eßer // Some temporaries need the ability to grow, so we allocate them 134344d4804dSStefan Eßer // separately. 1344252884aeSStefan Eßer max = bc_vm_growSize(max, 1); 1345252884aeSStefan Eßer bc_num_init(&z0, max); 1346252884aeSStefan Eßer bc_num_init(&z1, max); 1347252884aeSStefan Eßer bc_num_init(&z2, max); 1348252884aeSStefan Eßer max = bc_vm_growSize(max, max) + 1; 1349252884aeSStefan Eßer bc_num_init(&temp, max); 1350252884aeSStefan Eßer 1351d101cdd6SStefan Eßer BC_SETJMP_LOCKED(vm, err); 1352252884aeSStefan Eßer 1353252884aeSStefan Eßer BC_SIG_UNLOCK; 1354252884aeSStefan Eßer 135544d4804dSStefan Eßer // First, set up c. 1356252884aeSStefan Eßer bc_num_expand(c, max); 1357252884aeSStefan Eßer c->len = max; 135878bc019dSStefan Eßer // NOLINTNEXTLINE 1359252884aeSStefan Eßer memset(c->num, 0, BC_NUM_SIZE(c->len)); 1360252884aeSStefan Eßer 136144d4804dSStefan Eßer // Split the parameters. 136244d4804dSStefan Eßer bc_num_split(a, max2, &l1, &h1); 136344d4804dSStefan Eßer bc_num_split(b, max2, &l2, &h2); 136444d4804dSStefan Eßer 136544d4804dSStefan Eßer // Do the subtraction. 1366252884aeSStefan Eßer bc_num_sub(&h1, &l1, &m1, 0); 1367252884aeSStefan Eßer bc_num_sub(&l2, &h2, &m2, 0); 1368252884aeSStefan Eßer 136944d4804dSStefan Eßer // The if statements below are there for efficiency reasons. The best way to 137044d4804dSStefan Eßer // understand them is to understand the Karatsuba algorithm because now that 137144d4804dSStefan Eßer // the ollocations and splits are done, the algorithm is pretty 137244d4804dSStefan Eßer // straightforward. 137344d4804dSStefan Eßer 137478bc019dSStefan Eßer if (BC_NUM_NONZERO(&h1) && BC_NUM_NONZERO(&h2)) 137578bc019dSStefan Eßer { 137650696a6eSStefan Eßer assert(BC_NUM_RDX_VALID_NP(h1)); 137750696a6eSStefan Eßer assert(BC_NUM_RDX_VALID_NP(h2)); 137850696a6eSStefan Eßer 1379252884aeSStefan Eßer bc_num_m(&h1, &h2, &z2, 0); 1380252884aeSStefan Eßer bc_num_clean(&z2); 1381252884aeSStefan Eßer 1382252884aeSStefan Eßer bc_num_shiftAddSub(c, &z2, max2 * 2, bc_num_addArrays); 1383252884aeSStefan Eßer bc_num_shiftAddSub(c, &z2, max2, bc_num_addArrays); 1384252884aeSStefan Eßer } 1385252884aeSStefan Eßer 138678bc019dSStefan Eßer if (BC_NUM_NONZERO(&l1) && BC_NUM_NONZERO(&l2)) 138778bc019dSStefan Eßer { 138850696a6eSStefan Eßer assert(BC_NUM_RDX_VALID_NP(l1)); 138950696a6eSStefan Eßer assert(BC_NUM_RDX_VALID_NP(l2)); 139050696a6eSStefan Eßer 1391252884aeSStefan Eßer bc_num_m(&l1, &l2, &z0, 0); 1392252884aeSStefan Eßer bc_num_clean(&z0); 1393252884aeSStefan Eßer 1394252884aeSStefan Eßer bc_num_shiftAddSub(c, &z0, max2, bc_num_addArrays); 1395252884aeSStefan Eßer bc_num_shiftAddSub(c, &z0, 0, bc_num_addArrays); 1396252884aeSStefan Eßer } 1397252884aeSStefan Eßer 139878bc019dSStefan Eßer if (BC_NUM_NONZERO(&m1) && BC_NUM_NONZERO(&m2)) 139978bc019dSStefan Eßer { 140050696a6eSStefan Eßer assert(BC_NUM_RDX_VALID_NP(m1)); 140150696a6eSStefan Eßer assert(BC_NUM_RDX_VALID_NP(m1)); 140250696a6eSStefan Eßer 1403252884aeSStefan Eßer bc_num_m(&m1, &m2, &z1, 0); 1404252884aeSStefan Eßer bc_num_clean(&z1); 1405252884aeSStefan Eßer 140650696a6eSStefan Eßer op = (BC_NUM_NEG_NP(m1) != BC_NUM_NEG_NP(m2)) ? 140778bc019dSStefan Eßer bc_num_subArrays : 140878bc019dSStefan Eßer bc_num_addArrays; 1409252884aeSStefan Eßer bc_num_shiftAddSub(c, &z1, max2, op); 1410252884aeSStefan Eßer } 1411252884aeSStefan Eßer 1412252884aeSStefan Eßer err: 1413252884aeSStefan Eßer BC_SIG_MAYLOCK; 1414252884aeSStefan Eßer free(digs); 1415252884aeSStefan Eßer bc_num_free(&temp); 1416252884aeSStefan Eßer bc_num_free(&z2); 1417252884aeSStefan Eßer bc_num_free(&z1); 1418252884aeSStefan Eßer bc_num_free(&z0); 1419d101cdd6SStefan Eßer BC_LONGJMP_CONT(vm); 1420252884aeSStefan Eßer } 1421252884aeSStefan Eßer 142244d4804dSStefan Eßer /** 142344d4804dSStefan Eßer * Does checks for Karatsuba. It also changes things to ensure that the 142444d4804dSStefan Eßer * Karatsuba and simple multiplication can treat the numbers as integers. This 142544d4804dSStefan Eßer * is a BcNumBinOp function. 142644d4804dSStefan Eßer * @param a The first operand. 142744d4804dSStefan Eßer * @param b The second operand. 142844d4804dSStefan Eßer * @param c The return parameter. 142944d4804dSStefan Eßer * @param scale The current scale. 143044d4804dSStefan Eßer */ 143178bc019dSStefan Eßer static void 143278bc019dSStefan Eßer bc_num_m(BcNum* a, BcNum* b, BcNum* restrict c, size_t scale) 143378bc019dSStefan Eßer { 1434252884aeSStefan Eßer BcNum cpa, cpb; 1435d101cdd6SStefan Eßer size_t ascale, bscale, ardx, brdx, zero, len, rscale; 1436d101cdd6SStefan Eßer // These are meant to quiet warnings on GCC about longjmp() clobbering. 1437d101cdd6SStefan Eßer // The problem is real here. 1438d101cdd6SStefan Eßer size_t scale1, scale2, realscale; 1439d101cdd6SStefan Eßer // These are meant to quiet the GCC longjmp() clobbering, even though it 1440d101cdd6SStefan Eßer // does not apply here. 1441d101cdd6SStefan Eßer volatile size_t azero; 1442d101cdd6SStefan Eßer volatile size_t bzero; 1443d101cdd6SStefan Eßer #if BC_ENABLE_LIBRARY 1444d101cdd6SStefan Eßer BcVm* vm = bcl_getspecific(); 1445d101cdd6SStefan Eßer #endif // BC_ENABLE_LIBRARY 1446252884aeSStefan Eßer 144750696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(a)); 144850696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(b)); 144950696a6eSStefan Eßer 1450252884aeSStefan Eßer bc_num_zero(c); 145144d4804dSStefan Eßer 1452252884aeSStefan Eßer ascale = a->scale; 1453252884aeSStefan Eßer bscale = b->scale; 145444d4804dSStefan Eßer 145544d4804dSStefan Eßer // This sets the final scale according to the bc spec. 1456d101cdd6SStefan Eßer scale1 = BC_MAX(scale, ascale); 1457d101cdd6SStefan Eßer scale2 = BC_MAX(scale1, bscale); 1458252884aeSStefan Eßer rscale = ascale + bscale; 1459d101cdd6SStefan Eßer realscale = BC_MIN(rscale, scale2); 1460252884aeSStefan Eßer 146144d4804dSStefan Eßer // If this condition is true, we can use bc_num_mulArray(), which would be 146244d4804dSStefan Eßer // much faster. 146378bc019dSStefan Eßer if ((a->len == 1 || b->len == 1) && !a->rdx && !b->rdx) 146478bc019dSStefan Eßer { 1465252884aeSStefan Eßer BcNum* operand; 1466252884aeSStefan Eßer BcBigDig dig; 1467252884aeSStefan Eßer 146844d4804dSStefan Eßer // Set the correct operands. 146978bc019dSStefan Eßer if (a->len == 1) 147078bc019dSStefan Eßer { 1471252884aeSStefan Eßer dig = (BcBigDig) a->num[0]; 1472252884aeSStefan Eßer operand = b; 1473252884aeSStefan Eßer } 147478bc019dSStefan Eßer else 147578bc019dSStefan Eßer { 1476252884aeSStefan Eßer dig = (BcBigDig) b->num[0]; 1477252884aeSStefan Eßer operand = a; 1478252884aeSStefan Eßer } 1479252884aeSStefan Eßer 1480252884aeSStefan Eßer bc_num_mulArray(operand, dig, c); 1481252884aeSStefan Eßer 148244d4804dSStefan Eßer // Need to make sure the sign is correct. 148350696a6eSStefan Eßer if (BC_NUM_NONZERO(c)) 148478bc019dSStefan Eßer { 148550696a6eSStefan Eßer c->rdx = BC_NUM_NEG_VAL(c, BC_NUM_NEG(a) != BC_NUM_NEG(b)); 148678bc019dSStefan Eßer } 1487252884aeSStefan Eßer 1488252884aeSStefan Eßer return; 1489252884aeSStefan Eßer } 1490252884aeSStefan Eßer 149150696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(a)); 149250696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(b)); 149350696a6eSStefan Eßer 1494252884aeSStefan Eßer BC_SIG_LOCK; 1495252884aeSStefan Eßer 149644d4804dSStefan Eßer // We need copies because of all of the mutation needed to make Karatsuba 149744d4804dSStefan Eßer // think the numbers are integers. 149850696a6eSStefan Eßer bc_num_init(&cpa, a->len + BC_NUM_RDX_VAL(a)); 149950696a6eSStefan Eßer bc_num_init(&cpb, b->len + BC_NUM_RDX_VAL(b)); 1500252884aeSStefan Eßer 1501d101cdd6SStefan Eßer BC_SETJMP_LOCKED(vm, init_err); 1502252884aeSStefan Eßer 1503252884aeSStefan Eßer BC_SIG_UNLOCK; 1504252884aeSStefan Eßer 1505252884aeSStefan Eßer bc_num_copy(&cpa, a); 1506252884aeSStefan Eßer bc_num_copy(&cpb, b); 1507252884aeSStefan Eßer 150850696a6eSStefan Eßer assert(BC_NUM_RDX_VALID_NP(cpa)); 150950696a6eSStefan Eßer assert(BC_NUM_RDX_VALID_NP(cpb)); 1510252884aeSStefan Eßer 151150696a6eSStefan Eßer BC_NUM_NEG_CLR_NP(cpa); 151250696a6eSStefan Eßer BC_NUM_NEG_CLR_NP(cpb); 151350696a6eSStefan Eßer 151450696a6eSStefan Eßer assert(BC_NUM_RDX_VALID_NP(cpa)); 151550696a6eSStefan Eßer assert(BC_NUM_RDX_VALID_NP(cpb)); 151650696a6eSStefan Eßer 151744d4804dSStefan Eßer // These are what makes them appear like integers. 151850696a6eSStefan Eßer ardx = BC_NUM_RDX_VAL_NP(cpa) * BC_BASE_DIGS; 1519252884aeSStefan Eßer bc_num_shiftLeft(&cpa, ardx); 1520252884aeSStefan Eßer 152150696a6eSStefan Eßer brdx = BC_NUM_RDX_VAL_NP(cpb) * BC_BASE_DIGS; 1522252884aeSStefan Eßer bc_num_shiftLeft(&cpb, brdx); 1523252884aeSStefan Eßer 1524252884aeSStefan Eßer // We need to reset the jump here because azero and bzero are used in the 1525252884aeSStefan Eßer // cleanup, and local variables are not guaranteed to be the same after a 1526252884aeSStefan Eßer // jump. 1527252884aeSStefan Eßer BC_SIG_LOCK; 1528252884aeSStefan Eßer 1529d101cdd6SStefan Eßer BC_UNSETJMP(vm); 1530252884aeSStefan Eßer 153144d4804dSStefan Eßer // We want to ignore zero limbs. 1532252884aeSStefan Eßer azero = bc_num_shiftZero(&cpa); 1533252884aeSStefan Eßer bzero = bc_num_shiftZero(&cpb); 1534252884aeSStefan Eßer 1535d101cdd6SStefan Eßer BC_SETJMP_LOCKED(vm, err); 1536252884aeSStefan Eßer 1537252884aeSStefan Eßer BC_SIG_UNLOCK; 1538252884aeSStefan Eßer 1539252884aeSStefan Eßer bc_num_clean(&cpa); 1540252884aeSStefan Eßer bc_num_clean(&cpb); 1541252884aeSStefan Eßer 1542252884aeSStefan Eßer bc_num_k(&cpa, &cpb, c); 1543252884aeSStefan Eßer 154444d4804dSStefan Eßer // The return parameter needs to have its scale set. This is the start. It 154544d4804dSStefan Eßer // also needs to be shifted by the same amount as a and b have limbs after 154644d4804dSStefan Eßer // the decimal point. 1547252884aeSStefan Eßer zero = bc_vm_growSize(azero, bzero); 1548252884aeSStefan Eßer len = bc_vm_growSize(c->len, zero); 1549252884aeSStefan Eßer 1550252884aeSStefan Eßer bc_num_expand(c, len); 155144d4804dSStefan Eßer 155244d4804dSStefan Eßer // Shift c based on the limbs after the decimal point in a and b. 1553252884aeSStefan Eßer bc_num_shiftLeft(c, (len - c->len) * BC_BASE_DIGS); 1554252884aeSStefan Eßer bc_num_shiftRight(c, ardx + brdx); 1555252884aeSStefan Eßer 1556d101cdd6SStefan Eßer bc_num_retireMul(c, realscale, BC_NUM_NEG(a), BC_NUM_NEG(b)); 1557252884aeSStefan Eßer 1558252884aeSStefan Eßer err: 1559252884aeSStefan Eßer BC_SIG_MAYLOCK; 1560252884aeSStefan Eßer bc_num_unshiftZero(&cpb, bzero); 1561252884aeSStefan Eßer bc_num_unshiftZero(&cpa, azero); 1562d101cdd6SStefan Eßer init_err: 1563d101cdd6SStefan Eßer BC_SIG_MAYLOCK; 1564d101cdd6SStefan Eßer bc_num_free(&cpb); 1565252884aeSStefan Eßer bc_num_free(&cpa); 1566d101cdd6SStefan Eßer BC_LONGJMP_CONT(vm); 1567252884aeSStefan Eßer } 1568252884aeSStefan Eßer 156944d4804dSStefan Eßer /** 157044d4804dSStefan Eßer * Returns true if the BcDig array has non-zero limbs, false otherwise. 157144d4804dSStefan Eßer * @param a The array to test. 157244d4804dSStefan Eßer * @param len The length of the array. 157344d4804dSStefan Eßer * @return True if @a has any non-zero limbs, false otherwise. 157444d4804dSStefan Eßer */ 157578bc019dSStefan Eßer static bool 157678bc019dSStefan Eßer bc_num_nonZeroDig(BcDig* restrict a, size_t len) 157778bc019dSStefan Eßer { 1578252884aeSStefan Eßer size_t i; 157978bc019dSStefan Eßer 1580d101cdd6SStefan Eßer for (i = len - 1; i < len; --i) 158178bc019dSStefan Eßer { 1582d101cdd6SStefan Eßer if (a[i] != 0) return true; 158378bc019dSStefan Eßer } 158478bc019dSStefan Eßer 1585d101cdd6SStefan Eßer return false; 1586252884aeSStefan Eßer } 1587252884aeSStefan Eßer 158844d4804dSStefan Eßer /** 158944d4804dSStefan Eßer * Compares a BcDig array against a BcNum. This is especially suited for 159044d4804dSStefan Eßer * division. Returns >0 if @a a is greater than @a b, <0 if it is less, and =0 159144d4804dSStefan Eßer * if they are equal. 159244d4804dSStefan Eßer * @param a The array. 159344d4804dSStefan Eßer * @param b The number. 159444d4804dSStefan Eßer * @param len The length to assume the arrays are. This is always less than the 159544d4804dSStefan Eßer * actual length because of how this is implemented. 159644d4804dSStefan Eßer */ 159778bc019dSStefan Eßer static ssize_t 159878bc019dSStefan Eßer bc_num_divCmp(const BcDig* a, const BcNum* b, size_t len) 159978bc019dSStefan Eßer { 1600252884aeSStefan Eßer ssize_t cmp; 1601252884aeSStefan Eßer 1602252884aeSStefan Eßer if (b->len > len && a[len]) cmp = bc_num_compare(a, b->num, len + 1); 160378bc019dSStefan Eßer else if (b->len <= len) 160478bc019dSStefan Eßer { 1605252884aeSStefan Eßer if (a[len]) cmp = 1; 1606252884aeSStefan Eßer else cmp = bc_num_compare(a, b->num, len); 1607252884aeSStefan Eßer } 1608252884aeSStefan Eßer else cmp = -1; 1609252884aeSStefan Eßer 1610252884aeSStefan Eßer return cmp; 1611252884aeSStefan Eßer } 1612252884aeSStefan Eßer 161344d4804dSStefan Eßer /** 161444d4804dSStefan Eßer * Extends the two operands of a division by BC_BASE_DIGS minus the number of 161544d4804dSStefan Eßer * digits in the divisor estimate. In other words, it is shifting the numbers in 161644d4804dSStefan Eßer * order to force the divisor estimate to fill the limb. 161744d4804dSStefan Eßer * @param a The first operand. 161844d4804dSStefan Eßer * @param b The second operand. 161944d4804dSStefan Eßer * @param divisor The divisor estimate. 162044d4804dSStefan Eßer */ 162178bc019dSStefan Eßer static void 162278bc019dSStefan Eßer bc_num_divExtend(BcNum* restrict a, BcNum* restrict b, BcBigDig divisor) 1623252884aeSStefan Eßer { 1624252884aeSStefan Eßer size_t pow; 1625252884aeSStefan Eßer 1626252884aeSStefan Eßer assert(divisor < BC_BASE_POW); 1627252884aeSStefan Eßer 1628252884aeSStefan Eßer pow = BC_BASE_DIGS - bc_num_log10((size_t) divisor); 1629252884aeSStefan Eßer 1630252884aeSStefan Eßer bc_num_shiftLeft(a, pow); 1631252884aeSStefan Eßer bc_num_shiftLeft(b, pow); 1632252884aeSStefan Eßer } 1633252884aeSStefan Eßer 163444d4804dSStefan Eßer /** 163544d4804dSStefan Eßer * Actually does division. This is a rewrite of my original code by Stefan Esser 163644d4804dSStefan Eßer * from FreeBSD. 163744d4804dSStefan Eßer * @param a The first operand. 163844d4804dSStefan Eßer * @param b The second operand. 163944d4804dSStefan Eßer * @param c The return parameter. 164044d4804dSStefan Eßer * @param scale The current scale. 164144d4804dSStefan Eßer */ 164278bc019dSStefan Eßer static void 164378bc019dSStefan Eßer bc_num_d_long(BcNum* restrict a, BcNum* restrict b, BcNum* restrict c, 164478bc019dSStefan Eßer size_t scale) 1645252884aeSStefan Eßer { 1646252884aeSStefan Eßer BcBigDig divisor; 1647d101cdd6SStefan Eßer size_t i, rdx; 1648d101cdd6SStefan Eßer // This is volatile and len 2 and reallen exist to quiet the GCC warning 1649d101cdd6SStefan Eßer // about clobbering on longjmp(). This one is possible, I think. 1650d101cdd6SStefan Eßer volatile size_t len; 1651d101cdd6SStefan Eßer size_t len2, reallen; 1652d101cdd6SStefan Eßer // This is volatile and realend exists to quiet the GCC warning about 1653d101cdd6SStefan Eßer // clobbering on longjmp(). This one is possible, I think. 1654d101cdd6SStefan Eßer volatile size_t end; 1655d101cdd6SStefan Eßer size_t realend; 1656252884aeSStefan Eßer BcNum cpb; 1657d101cdd6SStefan Eßer // This is volatile and realnonzero exists to quiet the GCC warning about 1658d101cdd6SStefan Eßer // clobbering on longjmp(). This one is possible, I think. 1659d101cdd6SStefan Eßer volatile bool nonzero; 1660d101cdd6SStefan Eßer bool realnonzero; 1661d101cdd6SStefan Eßer #if BC_ENABLE_LIBRARY 1662d101cdd6SStefan Eßer BcVm* vm = bcl_getspecific(); 1663d101cdd6SStefan Eßer #endif // BC_ENABLE_LIBRARY 1664252884aeSStefan Eßer 1665252884aeSStefan Eßer assert(b->len < a->len); 166644d4804dSStefan Eßer 1667252884aeSStefan Eßer len = b->len; 1668252884aeSStefan Eßer end = a->len - len; 166944d4804dSStefan Eßer 1670252884aeSStefan Eßer assert(len >= 1); 1671252884aeSStefan Eßer 167244d4804dSStefan Eßer // This is a final time to make sure c is big enough and that its array is 167344d4804dSStefan Eßer // properly zeroed. 1674252884aeSStefan Eßer bc_num_expand(c, a->len); 167578bc019dSStefan Eßer // NOLINTNEXTLINE 1676252884aeSStefan Eßer memset(c->num, 0, c->cap * sizeof(BcDig)); 1677252884aeSStefan Eßer 167844d4804dSStefan Eßer // Setup. 167950696a6eSStefan Eßer BC_NUM_RDX_SET(c, BC_NUM_RDX_VAL(a)); 1680252884aeSStefan Eßer c->scale = a->scale; 1681252884aeSStefan Eßer c->len = a->len; 1682252884aeSStefan Eßer 168344d4804dSStefan Eßer // This is pulling the most significant limb of b in order to establish a 168444d4804dSStefan Eßer // good "estimate" for the actual divisor. 1685252884aeSStefan Eßer divisor = (BcBigDig) b->num[len - 1]; 1686252884aeSStefan Eßer 168744d4804dSStefan Eßer // The entire bit of code in this if statement is to tighten the estimate of 168844d4804dSStefan Eßer // the divisor. The condition asks if b has any other non-zero limbs. 168978bc019dSStefan Eßer if (len > 1 && bc_num_nonZeroDig(b->num, len - 1)) 169078bc019dSStefan Eßer { 169144d4804dSStefan Eßer // This takes a little bit of understanding. The "10*BC_BASE_DIGS/6+1" 169244d4804dSStefan Eßer // results in either 16 for 64-bit 9-digit limbs or 7 for 32-bit 4-digit 169344d4804dSStefan Eßer // limbs. Then it shifts a 1 by that many, which in both cases, puts the 169444d4804dSStefan Eßer // result above *half* of the max value a limb can store. Basically, 169544d4804dSStefan Eßer // this quickly calculates if the divisor is greater than half the max 169644d4804dSStefan Eßer // of a limb. 1697252884aeSStefan Eßer nonzero = (divisor > 1 << ((10 * BC_BASE_DIGS) / 6 + 1)); 1698252884aeSStefan Eßer 169944d4804dSStefan Eßer // If the divisor is *not* greater than half the limb... 170078bc019dSStefan Eßer if (!nonzero) 170178bc019dSStefan Eßer { 170244d4804dSStefan Eßer // Extend the parameters by the number of missing digits in the 170344d4804dSStefan Eßer // divisor. 1704252884aeSStefan Eßer bc_num_divExtend(a, b, divisor); 1705252884aeSStefan Eßer 170644d4804dSStefan Eßer // Check bc_num_d(). In there, we grow a again and again. We do it 170744d4804dSStefan Eßer // again here; we *always* want to be sure it is big enough. 1708d101cdd6SStefan Eßer len2 = BC_MAX(a->len, b->len); 1709d101cdd6SStefan Eßer bc_num_expand(a, len2 + 1); 1710252884aeSStefan Eßer 171144d4804dSStefan Eßer // Make a have a zero most significant limb to match the len. 1712d101cdd6SStefan Eßer if (len2 + 1 > a->len) a->len = len2 + 1; 1713252884aeSStefan Eßer 171444d4804dSStefan Eßer // Grab the new divisor estimate, new because the shift has made it 171544d4804dSStefan Eßer // different. 1716d101cdd6SStefan Eßer reallen = b->len; 1717d101cdd6SStefan Eßer realend = a->len - reallen; 1718d101cdd6SStefan Eßer divisor = (BcBigDig) b->num[reallen - 1]; 1719252884aeSStefan Eßer 1720d101cdd6SStefan Eßer realnonzero = bc_num_nonZeroDig(b->num, reallen - 1); 1721252884aeSStefan Eßer } 1722d101cdd6SStefan Eßer else 1723d101cdd6SStefan Eßer { 1724d101cdd6SStefan Eßer realend = end; 1725d101cdd6SStefan Eßer realnonzero = nonzero; 1726d101cdd6SStefan Eßer } 1727d101cdd6SStefan Eßer } 1728d101cdd6SStefan Eßer else 1729d101cdd6SStefan Eßer { 1730d101cdd6SStefan Eßer realend = end; 1731d101cdd6SStefan Eßer realnonzero = false; 1732252884aeSStefan Eßer } 1733252884aeSStefan Eßer 173444d4804dSStefan Eßer // If b has other nonzero limbs, we want the divisor to be one higher, so 173544d4804dSStefan Eßer // that it is an upper bound. 1736d101cdd6SStefan Eßer divisor += realnonzero; 1737252884aeSStefan Eßer 173844d4804dSStefan Eßer // Make sure c can fit the new length. 1739252884aeSStefan Eßer bc_num_expand(c, a->len); 174078bc019dSStefan Eßer // NOLINTNEXTLINE 1741252884aeSStefan Eßer memset(c->num, 0, BC_NUM_SIZE(c->cap)); 1742252884aeSStefan Eßer 1743252884aeSStefan Eßer assert(c->scale >= scale); 174450696a6eSStefan Eßer rdx = BC_NUM_RDX_VAL(c) - BC_NUM_RDX(scale); 1745252884aeSStefan Eßer 1746252884aeSStefan Eßer BC_SIG_LOCK; 1747252884aeSStefan Eßer 1748252884aeSStefan Eßer bc_num_init(&cpb, len + 1); 1749252884aeSStefan Eßer 1750d101cdd6SStefan Eßer BC_SETJMP_LOCKED(vm, err); 1751252884aeSStefan Eßer 1752252884aeSStefan Eßer BC_SIG_UNLOCK; 1753252884aeSStefan Eßer 175444d4804dSStefan Eßer // This is the actual division loop. 1755d101cdd6SStefan Eßer for (i = realend - 1; i < realend && i >= rdx && BC_NUM_NONZERO(a); --i) 175678bc019dSStefan Eßer { 1757252884aeSStefan Eßer ssize_t cmp; 1758252884aeSStefan Eßer BcDig* n; 1759252884aeSStefan Eßer BcBigDig result; 1760252884aeSStefan Eßer 1761252884aeSStefan Eßer n = a->num + i; 1762252884aeSStefan Eßer assert(n >= a->num); 1763252884aeSStefan Eßer result = 0; 1764252884aeSStefan Eßer 1765252884aeSStefan Eßer cmp = bc_num_divCmp(n, b, len); 1766252884aeSStefan Eßer 176744d4804dSStefan Eßer // This is true if n is greater than b, which means that division can 176844d4804dSStefan Eßer // proceed, so this inner loop is the part that implements one instance 176944d4804dSStefan Eßer // of the division. 177078bc019dSStefan Eßer while (cmp >= 0) 177178bc019dSStefan Eßer { 177244d4804dSStefan Eßer BcBigDig n1, dividend, quotient; 1773252884aeSStefan Eßer 177444d4804dSStefan Eßer // These should be named obviously enough. Just imagine that it's a 177544d4804dSStefan Eßer // division of one limb. Because that's what it is. 1776252884aeSStefan Eßer n1 = (BcBigDig) n[len]; 1777252884aeSStefan Eßer dividend = n1 * BC_BASE_POW + (BcBigDig) n[len - 1]; 177844d4804dSStefan Eßer quotient = (dividend / divisor); 1779252884aeSStefan Eßer 178044d4804dSStefan Eßer // If this is true, then we can just subtract. Remember: setting 178144d4804dSStefan Eßer // quotient to 1 is not bad because we already know that n is 178244d4804dSStefan Eßer // greater than b. 178378bc019dSStefan Eßer if (quotient <= 1) 178478bc019dSStefan Eßer { 178544d4804dSStefan Eßer quotient = 1; 1786252884aeSStefan Eßer bc_num_subArrays(n, b->num, len); 1787252884aeSStefan Eßer } 178878bc019dSStefan Eßer else 178978bc019dSStefan Eßer { 179044d4804dSStefan Eßer assert(quotient <= BC_BASE_POW); 1791252884aeSStefan Eßer 179244d4804dSStefan Eßer // We need to multiply and subtract for a quotient above 1. 179344d4804dSStefan Eßer bc_num_mulArray(b, (BcBigDig) quotient, &cpb); 1794252884aeSStefan Eßer bc_num_subArrays(n, cpb.num, cpb.len); 1795252884aeSStefan Eßer } 1796252884aeSStefan Eßer 179744d4804dSStefan Eßer // The result is the *real* quotient, by the way, but it might take 179844d4804dSStefan Eßer // multiple trips around this loop to get it. 179944d4804dSStefan Eßer result += quotient; 1800252884aeSStefan Eßer assert(result <= BC_BASE_POW); 1801252884aeSStefan Eßer 180244d4804dSStefan Eßer // And here's why it might take multiple trips: n might *still* be 180344d4804dSStefan Eßer // greater than b. So we have to loop again. That's what this is 180444d4804dSStefan Eßer // setting up for: the condition of the while loop. 1805d101cdd6SStefan Eßer if (realnonzero) cmp = bc_num_divCmp(n, b, len); 1806252884aeSStefan Eßer else cmp = -1; 1807252884aeSStefan Eßer } 1808252884aeSStefan Eßer 1809252884aeSStefan Eßer assert(result < BC_BASE_POW); 1810252884aeSStefan Eßer 181144d4804dSStefan Eßer // Store the actual limb quotient. 1812252884aeSStefan Eßer c->num[i] = (BcDig) result; 1813252884aeSStefan Eßer } 1814252884aeSStefan Eßer 1815252884aeSStefan Eßer err: 1816252884aeSStefan Eßer BC_SIG_MAYLOCK; 1817252884aeSStefan Eßer bc_num_free(&cpb); 1818d101cdd6SStefan Eßer BC_LONGJMP_CONT(vm); 1819252884aeSStefan Eßer } 1820252884aeSStefan Eßer 182144d4804dSStefan Eßer /** 182244d4804dSStefan Eßer * Implements division. This is a BcNumBinOp function. 182344d4804dSStefan Eßer * @param a The first operand. 182444d4804dSStefan Eßer * @param b The second operand. 182544d4804dSStefan Eßer * @param c The return parameter. 182644d4804dSStefan Eßer * @param scale The current scale. 182744d4804dSStefan Eßer */ 182878bc019dSStefan Eßer static void 182978bc019dSStefan Eßer bc_num_d(BcNum* a, BcNum* b, BcNum* restrict c, size_t scale) 183078bc019dSStefan Eßer { 183150696a6eSStefan Eßer size_t len, cpardx; 1832252884aeSStefan Eßer BcNum cpa, cpb; 1833d101cdd6SStefan Eßer #if BC_ENABLE_LIBRARY 1834d101cdd6SStefan Eßer BcVm* vm = bcl_getspecific(); 1835d101cdd6SStefan Eßer #endif // BC_ENABLE_LIBRARY 1836252884aeSStefan Eßer 183744d4804dSStefan Eßer if (BC_NUM_ZERO(b)) bc_err(BC_ERR_MATH_DIVIDE_BY_ZERO); 183844d4804dSStefan Eßer 183978bc019dSStefan Eßer if (BC_NUM_ZERO(a)) 184078bc019dSStefan Eßer { 1841252884aeSStefan Eßer bc_num_setToZero(c, scale); 1842252884aeSStefan Eßer return; 1843252884aeSStefan Eßer } 184444d4804dSStefan Eßer 184578bc019dSStefan Eßer if (BC_NUM_ONE(b)) 184678bc019dSStefan Eßer { 1847252884aeSStefan Eßer bc_num_copy(c, a); 184850696a6eSStefan Eßer bc_num_retireMul(c, scale, BC_NUM_NEG(a), BC_NUM_NEG(b)); 1849252884aeSStefan Eßer return; 1850252884aeSStefan Eßer } 185144d4804dSStefan Eßer 185244d4804dSStefan Eßer // If this is true, we can use bc_num_divArray(), which would be faster. 185378bc019dSStefan Eßer if (!BC_NUM_RDX_VAL(a) && !BC_NUM_RDX_VAL(b) && b->len == 1 && !scale) 185478bc019dSStefan Eßer { 1855252884aeSStefan Eßer BcBigDig rem; 1856252884aeSStefan Eßer bc_num_divArray(a, (BcBigDig) b->num[0], c, &rem); 185750696a6eSStefan Eßer bc_num_retireMul(c, scale, BC_NUM_NEG(a), BC_NUM_NEG(b)); 1858252884aeSStefan Eßer return; 1859252884aeSStefan Eßer } 1860252884aeSStefan Eßer 186150696a6eSStefan Eßer len = bc_num_divReq(a, b, scale); 1862252884aeSStefan Eßer 1863252884aeSStefan Eßer BC_SIG_LOCK; 1864252884aeSStefan Eßer 186544d4804dSStefan Eßer // Initialize copies of the parameters. We want the length of the first 186644d4804dSStefan Eßer // operand copy to be as big as the result because of the way the division 186744d4804dSStefan Eßer // is implemented. 1868252884aeSStefan Eßer bc_num_init(&cpa, len); 1869252884aeSStefan Eßer bc_num_copy(&cpa, a); 1870252884aeSStefan Eßer bc_num_createCopy(&cpb, b); 1871252884aeSStefan Eßer 1872d101cdd6SStefan Eßer BC_SETJMP_LOCKED(vm, err); 1873252884aeSStefan Eßer 1874252884aeSStefan Eßer BC_SIG_UNLOCK; 1875252884aeSStefan Eßer 1876252884aeSStefan Eßer len = b->len; 1877252884aeSStefan Eßer 187844d4804dSStefan Eßer // Like the above comment, we want the copy of the first parameter to be 187944d4804dSStefan Eßer // larger than the second parameter. 188078bc019dSStefan Eßer if (len > cpa.len) 188178bc019dSStefan Eßer { 1882252884aeSStefan Eßer bc_num_expand(&cpa, bc_vm_growSize(len, 2)); 1883252884aeSStefan Eßer bc_num_extend(&cpa, (len - cpa.len) * BC_BASE_DIGS); 1884252884aeSStefan Eßer } 1885252884aeSStefan Eßer 188650696a6eSStefan Eßer cpardx = BC_NUM_RDX_VAL_NP(cpa); 188750696a6eSStefan Eßer cpa.scale = cpardx * BC_BASE_DIGS; 1888252884aeSStefan Eßer 188944d4804dSStefan Eßer // This is just setting up the scale in preparation for the division. 1890252884aeSStefan Eßer bc_num_extend(&cpa, b->scale); 189150696a6eSStefan Eßer cpardx = BC_NUM_RDX_VAL_NP(cpa) - BC_NUM_RDX(b->scale); 189250696a6eSStefan Eßer BC_NUM_RDX_SET_NP(cpa, cpardx); 189350696a6eSStefan Eßer cpa.scale = cpardx * BC_BASE_DIGS; 1894252884aeSStefan Eßer 189544d4804dSStefan Eßer // Once again, just setting things up, this time to match scale. 189678bc019dSStefan Eßer if (scale > cpa.scale) 189778bc019dSStefan Eßer { 1898252884aeSStefan Eßer bc_num_extend(&cpa, scale); 189950696a6eSStefan Eßer cpardx = BC_NUM_RDX_VAL_NP(cpa); 190050696a6eSStefan Eßer cpa.scale = cpardx * BC_BASE_DIGS; 1901252884aeSStefan Eßer } 1902252884aeSStefan Eßer 190344d4804dSStefan Eßer // Grow if necessary. 1904252884aeSStefan Eßer if (cpa.cap == cpa.len) bc_num_expand(&cpa, bc_vm_growSize(cpa.len, 1)); 1905252884aeSStefan Eßer 1906252884aeSStefan Eßer // We want an extra zero in front to make things simpler. 1907252884aeSStefan Eßer cpa.num[cpa.len++] = 0; 1908252884aeSStefan Eßer 190944d4804dSStefan Eßer // Still setting things up. Why all of these things are needed is not 191044d4804dSStefan Eßer // something that can be easily explained, but it has to do with making the 191144d4804dSStefan Eßer // actual algorithm easier to understand because it can assume a lot of 191244d4804dSStefan Eßer // things. Thus, you should view all of this setup code as establishing 191344d4804dSStefan Eßer // assumptions for bc_num_d_long(), where the actual division happens. 191444d4804dSStefan Eßer if (cpardx == cpa.len) cpa.len = bc_num_nonZeroLen(&cpa); 191544d4804dSStefan Eßer if (BC_NUM_RDX_VAL_NP(cpb) == cpb.len) cpb.len = bc_num_nonZeroLen(&cpb); 191650696a6eSStefan Eßer cpb.scale = 0; 191750696a6eSStefan Eßer BC_NUM_RDX_SET_NP(cpb, 0); 1918252884aeSStefan Eßer 1919252884aeSStefan Eßer bc_num_d_long(&cpa, &cpb, c, scale); 1920252884aeSStefan Eßer 192150696a6eSStefan Eßer bc_num_retireMul(c, scale, BC_NUM_NEG(a), BC_NUM_NEG(b)); 1922252884aeSStefan Eßer 1923252884aeSStefan Eßer err: 1924252884aeSStefan Eßer BC_SIG_MAYLOCK; 1925252884aeSStefan Eßer bc_num_free(&cpb); 1926252884aeSStefan Eßer bc_num_free(&cpa); 1927d101cdd6SStefan Eßer BC_LONGJMP_CONT(vm); 1928252884aeSStefan Eßer } 1929252884aeSStefan Eßer 193044d4804dSStefan Eßer /** 193144d4804dSStefan Eßer * Implements divmod. This is the actual modulus function; since modulus 193244d4804dSStefan Eßer * requires a division anyway, this returns the quotient and modulus. Either can 193344d4804dSStefan Eßer * be thrown out as desired. 193444d4804dSStefan Eßer * @param a The first operand. 193544d4804dSStefan Eßer * @param b The second operand. 193644d4804dSStefan Eßer * @param c The return parameter for the quotient. 193744d4804dSStefan Eßer * @param d The return parameter for the modulus. 193844d4804dSStefan Eßer * @param scale The current scale. 193944d4804dSStefan Eßer * @param ts The scale that the operation should be done to. Yes, it's not 194044d4804dSStefan Eßer * necessarily the same as scale, per the bc spec. 194144d4804dSStefan Eßer */ 194278bc019dSStefan Eßer static void 194378bc019dSStefan Eßer bc_num_r(BcNum* a, BcNum* b, BcNum* restrict c, BcNum* restrict d, size_t scale, 194478bc019dSStefan Eßer size_t ts) 1945252884aeSStefan Eßer { 1946252884aeSStefan Eßer BcNum temp; 1947d101cdd6SStefan Eßer // realscale is meant to quiet a warning on GCC about longjmp() clobbering. 1948d101cdd6SStefan Eßer // This one is real. 1949d101cdd6SStefan Eßer size_t realscale; 1950252884aeSStefan Eßer bool neg; 1951d101cdd6SStefan Eßer #if BC_ENABLE_LIBRARY 1952d101cdd6SStefan Eßer BcVm* vm = bcl_getspecific(); 1953d101cdd6SStefan Eßer #endif // BC_ENABLE_LIBRARY 1954252884aeSStefan Eßer 195544d4804dSStefan Eßer if (BC_NUM_ZERO(b)) bc_err(BC_ERR_MATH_DIVIDE_BY_ZERO); 195644d4804dSStefan Eßer 195778bc019dSStefan Eßer if (BC_NUM_ZERO(a)) 195878bc019dSStefan Eßer { 1959252884aeSStefan Eßer bc_num_setToZero(c, ts); 1960252884aeSStefan Eßer bc_num_setToZero(d, ts); 1961252884aeSStefan Eßer return; 1962252884aeSStefan Eßer } 1963252884aeSStefan Eßer 1964252884aeSStefan Eßer BC_SIG_LOCK; 1965252884aeSStefan Eßer 1966252884aeSStefan Eßer bc_num_init(&temp, d->cap); 1967252884aeSStefan Eßer 1968d101cdd6SStefan Eßer BC_SETJMP_LOCKED(vm, err); 1969252884aeSStefan Eßer 1970252884aeSStefan Eßer BC_SIG_UNLOCK; 1971252884aeSStefan Eßer 197244d4804dSStefan Eßer // Division. 1973252884aeSStefan Eßer bc_num_d(a, b, c, scale); 1974252884aeSStefan Eßer 197544d4804dSStefan Eßer // We want an extra digit so we can safely truncate. 1976d101cdd6SStefan Eßer if (scale) realscale = ts + 1; 1977d101cdd6SStefan Eßer else realscale = scale; 1978252884aeSStefan Eßer 197950696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(c)); 198050696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(b)); 198150696a6eSStefan Eßer 198244d4804dSStefan Eßer // Implement the rest of the (a - (a / b) * b) formula. 1983d101cdd6SStefan Eßer bc_num_m(c, b, &temp, realscale); 1984d101cdd6SStefan Eßer bc_num_sub(a, &temp, d, realscale); 1985252884aeSStefan Eßer 198644d4804dSStefan Eßer // Extend if necessary. 1987252884aeSStefan Eßer if (ts > d->scale && BC_NUM_NONZERO(d)) bc_num_extend(d, ts - d->scale); 1988252884aeSStefan Eßer 198950696a6eSStefan Eßer neg = BC_NUM_NEG(d); 199050696a6eSStefan Eßer bc_num_retireMul(d, ts, BC_NUM_NEG(a), BC_NUM_NEG(b)); 199150696a6eSStefan Eßer d->rdx = BC_NUM_NEG_VAL(d, BC_NUM_NONZERO(d) ? neg : false); 1992252884aeSStefan Eßer 1993252884aeSStefan Eßer err: 1994252884aeSStefan Eßer BC_SIG_MAYLOCK; 1995252884aeSStefan Eßer bc_num_free(&temp); 1996d101cdd6SStefan Eßer BC_LONGJMP_CONT(vm); 1997252884aeSStefan Eßer } 1998252884aeSStefan Eßer 199944d4804dSStefan Eßer /** 200044d4804dSStefan Eßer * Implements modulus/remainder. (Yes, I know they are different, but not in the 200144d4804dSStefan Eßer * context of bc.) This is a BcNumBinOp function. 200244d4804dSStefan Eßer * @param a The first operand. 200344d4804dSStefan Eßer * @param b The second operand. 200444d4804dSStefan Eßer * @param c The return parameter. 200544d4804dSStefan Eßer * @param scale The current scale. 200644d4804dSStefan Eßer */ 200778bc019dSStefan Eßer static void 200878bc019dSStefan Eßer bc_num_rem(BcNum* a, BcNum* b, BcNum* restrict c, size_t scale) 200978bc019dSStefan Eßer { 2010252884aeSStefan Eßer BcNum c1; 2011252884aeSStefan Eßer size_t ts; 2012d101cdd6SStefan Eßer #if BC_ENABLE_LIBRARY 2013d101cdd6SStefan Eßer BcVm* vm = bcl_getspecific(); 2014d101cdd6SStefan Eßer #endif // BC_ENABLE_LIBRARY 2015252884aeSStefan Eßer 2016252884aeSStefan Eßer ts = bc_vm_growSize(scale, b->scale); 2017252884aeSStefan Eßer ts = BC_MAX(ts, a->scale); 2018252884aeSStefan Eßer 2019252884aeSStefan Eßer BC_SIG_LOCK; 2020252884aeSStefan Eßer 202144d4804dSStefan Eßer // Need a temp for the quotient. 2022252884aeSStefan Eßer bc_num_init(&c1, bc_num_mulReq(a, b, ts)); 2023252884aeSStefan Eßer 2024d101cdd6SStefan Eßer BC_SETJMP_LOCKED(vm, err); 2025252884aeSStefan Eßer 2026252884aeSStefan Eßer BC_SIG_UNLOCK; 2027252884aeSStefan Eßer 2028252884aeSStefan Eßer bc_num_r(a, b, &c1, c, scale, ts); 2029252884aeSStefan Eßer 2030252884aeSStefan Eßer err: 2031252884aeSStefan Eßer BC_SIG_MAYLOCK; 2032252884aeSStefan Eßer bc_num_free(&c1); 2033d101cdd6SStefan Eßer BC_LONGJMP_CONT(vm); 2034252884aeSStefan Eßer } 2035252884aeSStefan Eßer 203644d4804dSStefan Eßer /** 203744d4804dSStefan Eßer * Implements power (exponentiation). This is a BcNumBinOp function. 203844d4804dSStefan Eßer * @param a The first operand. 203944d4804dSStefan Eßer * @param b The second operand. 204044d4804dSStefan Eßer * @param c The return parameter. 204144d4804dSStefan Eßer * @param scale The current scale. 204244d4804dSStefan Eßer */ 204378bc019dSStefan Eßer static void 204478bc019dSStefan Eßer bc_num_p(BcNum* a, BcNum* b, BcNum* restrict c, size_t scale) 204578bc019dSStefan Eßer { 204644d4804dSStefan Eßer BcNum copy, btemp; 204744d4804dSStefan Eßer BcBigDig exp; 2048d101cdd6SStefan Eßer // realscale is meant to quiet a warning on GCC about longjmp() clobbering. 2049d101cdd6SStefan Eßer // This one is real. 2050d101cdd6SStefan Eßer size_t powrdx, resrdx, realscale; 205144d4804dSStefan Eßer bool neg; 2052d101cdd6SStefan Eßer #if BC_ENABLE_LIBRARY 2053d101cdd6SStefan Eßer BcVm* vm = bcl_getspecific(); 2054d101cdd6SStefan Eßer #endif // BC_ENABLE_LIBRARY 2055d101cdd6SStefan Eßer 2056d101cdd6SStefan Eßer // This is here to silence a warning from GCC. 2057d101cdd6SStefan Eßer #if BC_GCC 2058d101cdd6SStefan Eßer btemp.len = 0; 2059d101cdd6SStefan Eßer btemp.rdx = 0; 2060d101cdd6SStefan Eßer btemp.num = NULL; 2061d101cdd6SStefan Eßer #endif // BC_GCC 2062252884aeSStefan Eßer 206344d4804dSStefan Eßer if (BC_ERR(bc_num_nonInt(b, &btemp))) bc_err(BC_ERR_MATH_NON_INTEGER); 2064252884aeSStefan Eßer 2065d101cdd6SStefan Eßer assert(btemp.len == 0 || btemp.num != NULL); 2066d101cdd6SStefan Eßer 206778bc019dSStefan Eßer if (BC_NUM_ZERO(&btemp)) 206878bc019dSStefan Eßer { 2069252884aeSStefan Eßer bc_num_one(c); 2070252884aeSStefan Eßer return; 2071252884aeSStefan Eßer } 207244d4804dSStefan Eßer 207378bc019dSStefan Eßer if (BC_NUM_ZERO(a)) 207478bc019dSStefan Eßer { 207544d4804dSStefan Eßer if (BC_NUM_NEG_NP(btemp)) bc_err(BC_ERR_MATH_DIVIDE_BY_ZERO); 2076252884aeSStefan Eßer bc_num_setToZero(c, scale); 2077252884aeSStefan Eßer return; 2078252884aeSStefan Eßer } 207944d4804dSStefan Eßer 208078bc019dSStefan Eßer if (BC_NUM_ONE(&btemp)) 208178bc019dSStefan Eßer { 208244d4804dSStefan Eßer if (!BC_NUM_NEG_NP(btemp)) bc_num_copy(c, a); 2083252884aeSStefan Eßer else bc_num_inv(a, c, scale); 2084252884aeSStefan Eßer return; 2085252884aeSStefan Eßer } 2086252884aeSStefan Eßer 208744d4804dSStefan Eßer neg = BC_NUM_NEG_NP(btemp); 208844d4804dSStefan Eßer BC_NUM_NEG_CLR_NP(btemp); 2089252884aeSStefan Eßer 209044d4804dSStefan Eßer exp = bc_num_bigdig(&btemp); 209144d4804dSStefan Eßer 209244d4804dSStefan Eßer BC_SIG_LOCK; 2093252884aeSStefan Eßer 2094252884aeSStefan Eßer bc_num_createCopy(©, a); 2095252884aeSStefan Eßer 2096d101cdd6SStefan Eßer BC_SETJMP_LOCKED(vm, err); 2097252884aeSStefan Eßer 2098252884aeSStefan Eßer BC_SIG_UNLOCK; 2099252884aeSStefan Eßer 210044d4804dSStefan Eßer // If this is true, then we do not have to do a division, and we need to 210144d4804dSStefan Eßer // set scale accordingly. 210278bc019dSStefan Eßer if (!neg) 210378bc019dSStefan Eßer { 210444d4804dSStefan Eßer size_t max = BC_MAX(scale, a->scale), scalepow; 210544d4804dSStefan Eßer scalepow = bc_num_mulOverflow(a->scale, exp); 2106d101cdd6SStefan Eßer realscale = BC_MIN(scalepow, max); 2107252884aeSStefan Eßer } 2108d101cdd6SStefan Eßer else realscale = scale; 2109252884aeSStefan Eßer 211044d4804dSStefan Eßer // This is only implementing the first exponentiation by squaring, until it 211144d4804dSStefan Eßer // reaches the first time where the square is actually used. 211278bc019dSStefan Eßer for (powrdx = a->scale; !(exp & 1); exp >>= 1) 211378bc019dSStefan Eßer { 2114252884aeSStefan Eßer powrdx <<= 1; 211550696a6eSStefan Eßer assert(BC_NUM_RDX_VALID_NP(copy)); 2116252884aeSStefan Eßer bc_num_mul(©, ©, ©, powrdx); 2117252884aeSStefan Eßer } 2118252884aeSStefan Eßer 211944d4804dSStefan Eßer // Make c a copy of copy for the purpose of saving the squares that should 212044d4804dSStefan Eßer // be saved. 2121252884aeSStefan Eßer bc_num_copy(c, ©); 2122252884aeSStefan Eßer resrdx = powrdx; 2123252884aeSStefan Eßer 212444d4804dSStefan Eßer // Now finish the exponentiation by squaring, this time saving the squares 212544d4804dSStefan Eßer // as necessary. 212678bc019dSStefan Eßer while (exp >>= 1) 212778bc019dSStefan Eßer { 2128252884aeSStefan Eßer powrdx <<= 1; 212950696a6eSStefan Eßer assert(BC_NUM_RDX_VALID_NP(copy)); 2130252884aeSStefan Eßer bc_num_mul(©, ©, ©, powrdx); 2131252884aeSStefan Eßer 213244d4804dSStefan Eßer // If this is true, we want to save that particular square. This does 213344d4804dSStefan Eßer // that by multiplying c with copy. 213478bc019dSStefan Eßer if (exp & 1) 213578bc019dSStefan Eßer { 2136252884aeSStefan Eßer resrdx += powrdx; 213750696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(c)); 213850696a6eSStefan Eßer assert(BC_NUM_RDX_VALID_NP(copy)); 2139252884aeSStefan Eßer bc_num_mul(c, ©, c, resrdx); 2140252884aeSStefan Eßer } 2141252884aeSStefan Eßer } 2142252884aeSStefan Eßer 214344d4804dSStefan Eßer // Invert if necessary. 2144d101cdd6SStefan Eßer if (neg) bc_num_inv(c, c, realscale); 2145252884aeSStefan Eßer 214644d4804dSStefan Eßer // Truncate if necessary. 2147d101cdd6SStefan Eßer if (c->scale > realscale) bc_num_truncate(c, c->scale - realscale); 2148252884aeSStefan Eßer 214944d4804dSStefan Eßer bc_num_clean(c); 2150252884aeSStefan Eßer 2151252884aeSStefan Eßer err: 2152252884aeSStefan Eßer BC_SIG_MAYLOCK; 2153252884aeSStefan Eßer bc_num_free(©); 2154d101cdd6SStefan Eßer BC_LONGJMP_CONT(vm); 2155252884aeSStefan Eßer } 2156252884aeSStefan Eßer 2157252884aeSStefan Eßer #if BC_ENABLE_EXTRA_MATH 215844d4804dSStefan Eßer /** 215944d4804dSStefan Eßer * Implements the places operator. This is a BcNumBinOp function. 216044d4804dSStefan Eßer * @param a The first operand. 216144d4804dSStefan Eßer * @param b The second operand. 216244d4804dSStefan Eßer * @param c The return parameter. 216344d4804dSStefan Eßer * @param scale The current scale. 216444d4804dSStefan Eßer */ 216578bc019dSStefan Eßer static void 216678bc019dSStefan Eßer bc_num_place(BcNum* a, BcNum* b, BcNum* restrict c, size_t scale) 216778bc019dSStefan Eßer { 216844d4804dSStefan Eßer BcBigDig val; 2169252884aeSStefan Eßer 2170252884aeSStefan Eßer BC_UNUSED(scale); 2171252884aeSStefan Eßer 217244d4804dSStefan Eßer val = bc_num_intop(a, b, c); 2173252884aeSStefan Eßer 217444d4804dSStefan Eßer // Just truncate or extend as appropriate. 2175252884aeSStefan Eßer if (val < c->scale) bc_num_truncate(c, c->scale - val); 2176252884aeSStefan Eßer else if (val > c->scale) bc_num_extend(c, val - c->scale); 2177252884aeSStefan Eßer } 2178252884aeSStefan Eßer 217944d4804dSStefan Eßer /** 218044d4804dSStefan Eßer * Implements the left shift operator. This is a BcNumBinOp function. 218144d4804dSStefan Eßer */ 218278bc019dSStefan Eßer static void 218378bc019dSStefan Eßer bc_num_left(BcNum* a, BcNum* b, BcNum* restrict c, size_t scale) 218478bc019dSStefan Eßer { 218544d4804dSStefan Eßer BcBigDig val; 2186252884aeSStefan Eßer 2187252884aeSStefan Eßer BC_UNUSED(scale); 2188252884aeSStefan Eßer 218944d4804dSStefan Eßer val = bc_num_intop(a, b, c); 2190252884aeSStefan Eßer 2191252884aeSStefan Eßer bc_num_shiftLeft(c, (size_t) val); 2192252884aeSStefan Eßer } 2193252884aeSStefan Eßer 219444d4804dSStefan Eßer /** 219544d4804dSStefan Eßer * Implements the right shift operator. This is a BcNumBinOp function. 219644d4804dSStefan Eßer */ 219778bc019dSStefan Eßer static void 219878bc019dSStefan Eßer bc_num_right(BcNum* a, BcNum* b, BcNum* restrict c, size_t scale) 219978bc019dSStefan Eßer { 220044d4804dSStefan Eßer BcBigDig val; 2201252884aeSStefan Eßer 2202252884aeSStefan Eßer BC_UNUSED(scale); 2203252884aeSStefan Eßer 220444d4804dSStefan Eßer val = bc_num_intop(a, b, c); 2205252884aeSStefan Eßer 2206252884aeSStefan Eßer if (BC_NUM_ZERO(c)) return; 2207252884aeSStefan Eßer 2208252884aeSStefan Eßer bc_num_shiftRight(c, (size_t) val); 2209252884aeSStefan Eßer } 2210252884aeSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH 2211252884aeSStefan Eßer 221244d4804dSStefan Eßer /** 221344d4804dSStefan Eßer * Prepares for, and calls, a binary operator function. This is probably the 221444d4804dSStefan Eßer * most important function in the entire file because it establishes assumptions 221544d4804dSStefan Eßer * that make the rest of the code so easy. Those assumptions include: 221644d4804dSStefan Eßer * 221744d4804dSStefan Eßer * - a is not the same pointer as c. 221844d4804dSStefan Eßer * - b is not the same pointer as c. 221944d4804dSStefan Eßer * - there is enough room in c for the result. 222044d4804dSStefan Eßer * 222144d4804dSStefan Eßer * Without these, this whole function would basically have to be duplicated for 222244d4804dSStefan Eßer * *all* binary operators. 222344d4804dSStefan Eßer * 222444d4804dSStefan Eßer * @param a The first operand. 222544d4804dSStefan Eßer * @param b The second operand. 222644d4804dSStefan Eßer * @param c The return parameter. 222744d4804dSStefan Eßer * @param scale The current scale. 222844d4804dSStefan Eßer * @param req The number of limbs needed to fit the result. 222944d4804dSStefan Eßer */ 223078bc019dSStefan Eßer static void 223178bc019dSStefan Eßer bc_num_binary(BcNum* a, BcNum* b, BcNum* c, size_t scale, BcNumBinOp op, 223278bc019dSStefan Eßer size_t req) 2233252884aeSStefan Eßer { 223478bc019dSStefan Eßer BcNum* ptr_a; 223578bc019dSStefan Eßer BcNum* ptr_b; 223678bc019dSStefan Eßer BcNum num2; 2237d101cdd6SStefan Eßer #if BC_ENABLE_LIBRARY 2238d101cdd6SStefan Eßer BcVm* vm = NULL; 2239d101cdd6SStefan Eßer #endif // BC_ENABLE_LIBRARY 2240252884aeSStefan Eßer 2241252884aeSStefan Eßer assert(a != NULL && b != NULL && c != NULL && op != NULL); 2242252884aeSStefan Eßer 224350696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(a)); 224450696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(b)); 224550696a6eSStefan Eßer 2246252884aeSStefan Eßer BC_SIG_LOCK; 2247252884aeSStefan Eßer 2248d101cdd6SStefan Eßer ptr_a = c == a ? &num2 : a; 2249d101cdd6SStefan Eßer ptr_b = c == b ? &num2 : b; 2250252884aeSStefan Eßer 225144d4804dSStefan Eßer // Actually reallocate. If we don't reallocate, we want to expand at the 225244d4804dSStefan Eßer // very least. 2253d101cdd6SStefan Eßer if (c == a || c == b) 225478bc019dSStefan Eßer { 2255d101cdd6SStefan Eßer #if BC_ENABLE_LIBRARY 2256d101cdd6SStefan Eßer vm = bcl_getspecific(); 2257d101cdd6SStefan Eßer #endif // BC_ENABLE_LIBRARY 2258d101cdd6SStefan Eßer 2259d101cdd6SStefan Eßer // NOLINTNEXTLINE 2260d101cdd6SStefan Eßer memcpy(&num2, c, sizeof(BcNum)); 2261d101cdd6SStefan Eßer 2262252884aeSStefan Eßer bc_num_init(c, req); 2263252884aeSStefan Eßer 226444d4804dSStefan Eßer // Must prepare for cleanup. We want this here so that locals that got 226544d4804dSStefan Eßer // set stay set since a longjmp() is not guaranteed to preserve locals. 2266d101cdd6SStefan Eßer BC_SETJMP_LOCKED(vm, err); 2267252884aeSStefan Eßer BC_SIG_UNLOCK; 2268252884aeSStefan Eßer } 226978bc019dSStefan Eßer else 227078bc019dSStefan Eßer { 2271252884aeSStefan Eßer BC_SIG_UNLOCK; 2272252884aeSStefan Eßer bc_num_expand(c, req); 2273252884aeSStefan Eßer } 2274252884aeSStefan Eßer 227544d4804dSStefan Eßer // It is okay for a and b to be the same. If a binary operator function does 227644d4804dSStefan Eßer // need them to be different, the binary operator function is responsible 227744d4804dSStefan Eßer // for that. 227844d4804dSStefan Eßer 227944d4804dSStefan Eßer // Call the actual binary operator function. 2280252884aeSStefan Eßer op(ptr_a, ptr_b, c, scale); 2281252884aeSStefan Eßer 228250696a6eSStefan Eßer assert(!BC_NUM_NEG(c) || BC_NUM_NONZERO(c)); 228350696a6eSStefan Eßer assert(BC_NUM_RDX_VAL(c) <= c->len || !c->len); 228450696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(c)); 228550696a6eSStefan Eßer assert(!c->len || c->num[c->len - 1] || BC_NUM_RDX_VAL(c) == c->len); 2286252884aeSStefan Eßer 2287252884aeSStefan Eßer err: 228844d4804dSStefan Eßer // Cleanup only needed if we initialized c to a new number. 2289d101cdd6SStefan Eßer if (c == a || c == b) 229078bc019dSStefan Eßer { 2291252884aeSStefan Eßer BC_SIG_MAYLOCK; 2292252884aeSStefan Eßer bc_num_free(&num2); 2293d101cdd6SStefan Eßer BC_LONGJMP_CONT(vm); 2294252884aeSStefan Eßer } 2295252884aeSStefan Eßer } 2296252884aeSStefan Eßer 229744d4804dSStefan Eßer /** 229844d4804dSStefan Eßer * Tests a number string for validity. This function has a history; I originally 229944d4804dSStefan Eßer * wrote it because I did not trust my parser. Over time, however, I came to 230044d4804dSStefan Eßer * trust it, so I was able to relegate this function to debug builds only, and I 230144d4804dSStefan Eßer * used it in assert()'s. But then I created the library, and well, I can't 230244d4804dSStefan Eßer * trust users, so I reused this for yelling at users. 230344d4804dSStefan Eßer * @param val The string to check to see if it's a valid number string. 230444d4804dSStefan Eßer * @return True if the string is a valid number string, false otherwise. 230544d4804dSStefan Eßer */ 230678bc019dSStefan Eßer bool 230778bc019dSStefan Eßer bc_num_strValid(const char* restrict val) 230878bc019dSStefan Eßer { 2309252884aeSStefan Eßer bool radix = false; 2310252884aeSStefan Eßer size_t i, len = strlen(val); 2311252884aeSStefan Eßer 231244d4804dSStefan Eßer // Notice that I don't check if there is a negative sign. That is not part 231344d4804dSStefan Eßer // of a valid number, except in the library. The library-specific code takes 231444d4804dSStefan Eßer // care of that part. 231544d4804dSStefan Eßer 231644d4804dSStefan Eßer // Nothing in the string is okay. 2317252884aeSStefan Eßer if (!len) return true; 2318252884aeSStefan Eßer 231944d4804dSStefan Eßer // Loop through the characters. 232078bc019dSStefan Eßer for (i = 0; i < len; ++i) 232178bc019dSStefan Eßer { 2322252884aeSStefan Eßer BcDig c = val[i]; 2323252884aeSStefan Eßer 232444d4804dSStefan Eßer // If we have found a radix point... 232578bc019dSStefan Eßer if (c == '.') 232678bc019dSStefan Eßer { 232744d4804dSStefan Eßer // We don't allow two radices. 2328252884aeSStefan Eßer if (radix) return false; 2329252884aeSStefan Eßer 2330252884aeSStefan Eßer radix = true; 2331252884aeSStefan Eßer continue; 2332252884aeSStefan Eßer } 2333252884aeSStefan Eßer 233444d4804dSStefan Eßer // We only allow digits and uppercase letters. 2335252884aeSStefan Eßer if (!(isdigit(c) || isupper(c))) return false; 2336252884aeSStefan Eßer } 2337252884aeSStefan Eßer 2338252884aeSStefan Eßer return true; 2339252884aeSStefan Eßer } 2340252884aeSStefan Eßer 234144d4804dSStefan Eßer /** 234244d4804dSStefan Eßer * Parses one character and returns the digit that corresponds to that 234344d4804dSStefan Eßer * character according to the base. 234444d4804dSStefan Eßer * @param c The character to parse. 234544d4804dSStefan Eßer * @param base The base. 234644d4804dSStefan Eßer * @return The character as a digit. 234744d4804dSStefan Eßer */ 234878bc019dSStefan Eßer static BcBigDig 234978bc019dSStefan Eßer bc_num_parseChar(char c, size_t base) 235078bc019dSStefan Eßer { 235144d4804dSStefan Eßer assert(isupper(c) || isdigit(c)); 235244d4804dSStefan Eßer 235344d4804dSStefan Eßer // If a letter... 235478bc019dSStefan Eßer if (isupper(c)) 235578bc019dSStefan Eßer { 2356d101cdd6SStefan Eßer #if BC_ENABLE_LIBRARY 2357d101cdd6SStefan Eßer BcVm* vm = bcl_getspecific(); 2358d101cdd6SStefan Eßer #endif // BC_ENABLE_LIBRARY 2359d101cdd6SStefan Eßer 236044d4804dSStefan Eßer // This returns the digit that directly corresponds with the letter. 2361252884aeSStefan Eßer c = BC_NUM_NUM_LETTER(c); 236244d4804dSStefan Eßer 236344d4804dSStefan Eßer // If the digit is greater than the base, we clamp. 2364d101cdd6SStefan Eßer if (BC_DIGIT_CLAMP) 2365d101cdd6SStefan Eßer { 236644d4804dSStefan Eßer c = ((size_t) c) >= base ? (char) base - 1 : c; 2367252884aeSStefan Eßer } 2368d101cdd6SStefan Eßer } 236944d4804dSStefan Eßer // Straight convert the digit to a number. 2370252884aeSStefan Eßer else c -= '0'; 2371252884aeSStefan Eßer 2372252884aeSStefan Eßer return (BcBigDig) (uchar) c; 2373252884aeSStefan Eßer } 2374252884aeSStefan Eßer 237544d4804dSStefan Eßer /** 237644d4804dSStefan Eßer * Parses a string as a decimal number. This is separate because it's going to 237744d4804dSStefan Eßer * be the most used, and it can be heavily optimized for decimal only. 237844d4804dSStefan Eßer * @param n The number to parse into and return. Must be preallocated. 237944d4804dSStefan Eßer * @param val The string to parse. 238044d4804dSStefan Eßer */ 238178bc019dSStefan Eßer static void 238278bc019dSStefan Eßer bc_num_parseDecimal(BcNum* restrict n, const char* restrict val) 238378bc019dSStefan Eßer { 2384252884aeSStefan Eßer size_t len, i, temp, mod; 2385252884aeSStefan Eßer const char* ptr; 2386252884aeSStefan Eßer bool zero = true, rdx; 2387d101cdd6SStefan Eßer #if BC_ENABLE_LIBRARY 2388d101cdd6SStefan Eßer BcVm* vm = bcl_getspecific(); 2389d101cdd6SStefan Eßer #endif // BC_ENABLE_LIBRARY 2390252884aeSStefan Eßer 239144d4804dSStefan Eßer // Eat leading zeroes. 239278bc019dSStefan Eßer for (i = 0; val[i] == '0'; ++i) 239378bc019dSStefan Eßer { 239478bc019dSStefan Eßer continue; 239578bc019dSStefan Eßer } 2396252884aeSStefan Eßer 2397252884aeSStefan Eßer val += i; 2398252884aeSStefan Eßer assert(!val[0] || isalnum(val[0]) || val[0] == '.'); 2399252884aeSStefan Eßer 240044d4804dSStefan Eßer // All 0's. We can just return, since this procedure expects a virgin 240144d4804dSStefan Eßer // (already 0) BcNum. 2402252884aeSStefan Eßer if (!val[0]) return; 2403252884aeSStefan Eßer 240444d4804dSStefan Eßer // The length of the string is the length of the number, except it might be 240544d4804dSStefan Eßer // one bigger because of a decimal point. 2406252884aeSStefan Eßer len = strlen(val); 2407252884aeSStefan Eßer 240844d4804dSStefan Eßer // Find the location of the decimal point. 2409252884aeSStefan Eßer ptr = strchr(val, '.'); 2410252884aeSStefan Eßer rdx = (ptr != NULL); 2411252884aeSStefan Eßer 241244d4804dSStefan Eßer // We eat leading zeroes again. These leading zeroes are different because 241344d4804dSStefan Eßer // they will come after the decimal point if they exist, and since that's 241444d4804dSStefan Eßer // the case, they must be preserved. 241578bc019dSStefan Eßer for (i = 0; i < len && (zero = (val[i] == '0' || val[i] == '.')); ++i) 241678bc019dSStefan Eßer { 241778bc019dSStefan Eßer continue; 241878bc019dSStefan Eßer } 2419252884aeSStefan Eßer 242044d4804dSStefan Eßer // Set the scale of the number based on the location of the decimal point. 242144d4804dSStefan Eßer // The casts to uintptr_t is to ensure that bc does not hit undefined 242244d4804dSStefan Eßer // behavior when doing math on the values. 242378bc019dSStefan Eßer n->scale = (size_t) (rdx * 242478bc019dSStefan Eßer (((uintptr_t) (val + len)) - (((uintptr_t) ptr) + 1))); 2425252884aeSStefan Eßer 242644d4804dSStefan Eßer // Set rdx. 242750696a6eSStefan Eßer BC_NUM_RDX_SET(n, BC_NUM_RDX(n->scale)); 242844d4804dSStefan Eßer 242944d4804dSStefan Eßer // Calculate length. First, the length of the integer, then the number of 243044d4804dSStefan Eßer // digits in the last limb, then the length. 2431252884aeSStefan Eßer i = len - (ptr == val ? 0 : i) - rdx; 2432252884aeSStefan Eßer temp = BC_NUM_ROUND_POW(i); 2433252884aeSStefan Eßer mod = n->scale % BC_BASE_DIGS; 2434252884aeSStefan Eßer i = mod ? BC_BASE_DIGS - mod : 0; 2435252884aeSStefan Eßer n->len = ((temp + i) / BC_BASE_DIGS); 2436252884aeSStefan Eßer 2437d101cdd6SStefan Eßer // Expand and zero. The plus extra is in case the lack of clamping causes 2438d101cdd6SStefan Eßer // the number to overflow the original bounds. 2439d101cdd6SStefan Eßer bc_num_expand(n, n->len + !BC_DIGIT_CLAMP); 244078bc019dSStefan Eßer // NOLINTNEXTLINE 2441d101cdd6SStefan Eßer memset(n->num, 0, BC_NUM_SIZE(n->len + !BC_DIGIT_CLAMP)); 2442252884aeSStefan Eßer 244378bc019dSStefan Eßer if (zero) 244478bc019dSStefan Eßer { 244550696a6eSStefan Eßer // I think I can set rdx directly to zero here because n should be a 244650696a6eSStefan Eßer // new number with sign set to false. 244750696a6eSStefan Eßer n->len = n->rdx = 0; 244850696a6eSStefan Eßer } 244978bc019dSStefan Eßer else 245078bc019dSStefan Eßer { 245144d4804dSStefan Eßer // There is actually stuff to parse if we make it here. Yay... 2452252884aeSStefan Eßer BcBigDig exp, pow; 2453252884aeSStefan Eßer 2454252884aeSStefan Eßer assert(i <= BC_NUM_BIGDIG_MAX); 2455252884aeSStefan Eßer 245644d4804dSStefan Eßer // The exponent and power. 2457252884aeSStefan Eßer exp = (BcBigDig) i; 2458252884aeSStefan Eßer pow = bc_num_pow10[exp]; 2459252884aeSStefan Eßer 246044d4804dSStefan Eßer // Parse loop. We parse backwards because numbers are stored little 246144d4804dSStefan Eßer // endian. 246278bc019dSStefan Eßer for (i = len - 1; i < len; --i, ++exp) 246378bc019dSStefan Eßer { 2464252884aeSStefan Eßer char c = val[i]; 2465252884aeSStefan Eßer 246644d4804dSStefan Eßer // Skip the decimal point. 2467252884aeSStefan Eßer if (c == '.') exp -= 1; 246878bc019dSStefan Eßer else 246978bc019dSStefan Eßer { 247044d4804dSStefan Eßer // The index of the limb. 2471252884aeSStefan Eßer size_t idx = exp / BC_BASE_DIGS; 2472d101cdd6SStefan Eßer BcBigDig dig; 2473252884aeSStefan Eßer 2474d101cdd6SStefan Eßer if (isupper(c)) 2475d101cdd6SStefan Eßer { 247644d4804dSStefan Eßer // Clamp for the base. 2477d101cdd6SStefan Eßer if (!BC_DIGIT_CLAMP) c = BC_NUM_NUM_LETTER(c); 2478d101cdd6SStefan Eßer else c = 9; 2479d101cdd6SStefan Eßer } 2480d101cdd6SStefan Eßer else c -= '0'; 248144d4804dSStefan Eßer 2482d101cdd6SStefan Eßer // Add the digit to the limb. This takes care of overflow from 2483d101cdd6SStefan Eßer // lack of clamping. 2484d101cdd6SStefan Eßer dig = ((BcBigDig) n->num[idx]) + ((BcBigDig) c) * pow; 2485d101cdd6SStefan Eßer if (dig >= BC_BASE_POW) 2486d101cdd6SStefan Eßer { 2487d101cdd6SStefan Eßer // We cannot go over BC_BASE_POW with clamping. 2488d101cdd6SStefan Eßer assert(!BC_DIGIT_CLAMP); 2489d101cdd6SStefan Eßer 2490d101cdd6SStefan Eßer n->num[idx + 1] = (BcDig) (dig / BC_BASE_POW); 2491d101cdd6SStefan Eßer n->num[idx] = (BcDig) (dig % BC_BASE_POW); 2492d101cdd6SStefan Eßer assert(n->num[idx] >= 0 && n->num[idx] < BC_BASE_POW); 2493d101cdd6SStefan Eßer assert(n->num[idx + 1] >= 0 && 2494d101cdd6SStefan Eßer n->num[idx + 1] < BC_BASE_POW); 2495d101cdd6SStefan Eßer } 2496d101cdd6SStefan Eßer else 2497d101cdd6SStefan Eßer { 2498d101cdd6SStefan Eßer n->num[idx] = (BcDig) dig; 2499d101cdd6SStefan Eßer assert(n->num[idx] >= 0 && n->num[idx] < BC_BASE_POW); 2500d101cdd6SStefan Eßer } 2501252884aeSStefan Eßer 250244d4804dSStefan Eßer // Adjust the power and exponent. 2503252884aeSStefan Eßer if ((exp + 1) % BC_BASE_DIGS == 0) pow = 1; 2504252884aeSStefan Eßer else pow *= BC_BASE; 2505252884aeSStefan Eßer } 2506252884aeSStefan Eßer } 2507252884aeSStefan Eßer } 2508d101cdd6SStefan Eßer 2509d101cdd6SStefan Eßer // Make sure to add one to the length if needed from lack of clamping. 2510d101cdd6SStefan Eßer n->len += (!BC_DIGIT_CLAMP && n->num[n->len] != 0); 2511252884aeSStefan Eßer } 2512252884aeSStefan Eßer 251344d4804dSStefan Eßer /** 251444d4804dSStefan Eßer * Parse a number in any base (besides decimal). 251544d4804dSStefan Eßer * @param n The number to parse into and return. Must be preallocated. 251644d4804dSStefan Eßer * @param val The string to parse. 251744d4804dSStefan Eßer * @param base The base to parse as. 251844d4804dSStefan Eßer */ 251978bc019dSStefan Eßer static void 252078bc019dSStefan Eßer bc_num_parseBase(BcNum* restrict n, const char* restrict val, BcBigDig base) 2521252884aeSStefan Eßer { 252278bc019dSStefan Eßer BcNum temp, mult1, mult2, result1, result2; 252378bc019dSStefan Eßer BcNum* m1; 252478bc019dSStefan Eßer BcNum* m2; 252578bc019dSStefan Eßer BcNum* ptr; 2526252884aeSStefan Eßer char c = 0; 2527252884aeSStefan Eßer bool zero = true; 2528252884aeSStefan Eßer BcBigDig v; 2529d101cdd6SStefan Eßer size_t digs, len = strlen(val); 2530d101cdd6SStefan Eßer // This is volatile to quiet a warning on GCC about longjmp() clobbering. 2531d101cdd6SStefan Eßer volatile size_t i; 2532d101cdd6SStefan Eßer #if BC_ENABLE_LIBRARY 2533d101cdd6SStefan Eßer BcVm* vm = bcl_getspecific(); 2534d101cdd6SStefan Eßer #endif // BC_ENABLE_LIBRARY 2535252884aeSStefan Eßer 253644d4804dSStefan Eßer // If zero, just return because the number should be virgin (already 0). 253778bc019dSStefan Eßer for (i = 0; zero && i < len; ++i) 253878bc019dSStefan Eßer { 253978bc019dSStefan Eßer zero = (val[i] == '.' || val[i] == '0'); 254078bc019dSStefan Eßer } 2541252884aeSStefan Eßer if (zero) return; 2542252884aeSStefan Eßer 2543252884aeSStefan Eßer BC_SIG_LOCK; 2544252884aeSStefan Eßer 2545252884aeSStefan Eßer bc_num_init(&temp, BC_NUM_BIGDIG_LOG10); 2546252884aeSStefan Eßer bc_num_init(&mult1, BC_NUM_BIGDIG_LOG10); 2547252884aeSStefan Eßer 2548d101cdd6SStefan Eßer BC_SETJMP_LOCKED(vm, int_err); 2549252884aeSStefan Eßer 2550252884aeSStefan Eßer BC_SIG_UNLOCK; 2551252884aeSStefan Eßer 255244d4804dSStefan Eßer // We split parsing into parsing the integer and parsing the fractional 255344d4804dSStefan Eßer // part. 255444d4804dSStefan Eßer 255544d4804dSStefan Eßer // Parse the integer part. This is the easy part because we just multiply 255644d4804dSStefan Eßer // the number by the base, then add the digit. 255778bc019dSStefan Eßer for (i = 0; i < len && (c = val[i]) && c != '.'; ++i) 255878bc019dSStefan Eßer { 255944d4804dSStefan Eßer // Convert the character to a digit. 2560252884aeSStefan Eßer v = bc_num_parseChar(c, base); 2561252884aeSStefan Eßer 256244d4804dSStefan Eßer // Multiply the number. 2563252884aeSStefan Eßer bc_num_mulArray(n, base, &mult1); 256444d4804dSStefan Eßer 256544d4804dSStefan Eßer // Convert the digit to a number and add. 2566252884aeSStefan Eßer bc_num_bigdig2num(&temp, v); 2567252884aeSStefan Eßer bc_num_add(&mult1, &temp, n, 0); 2568252884aeSStefan Eßer } 2569252884aeSStefan Eßer 257044d4804dSStefan Eßer // If this condition is true, then we are done. We still need to do cleanup 257144d4804dSStefan Eßer // though. 257210328f8bSStefan Eßer if (i == len && !val[i]) goto int_err; 2573252884aeSStefan Eßer 257444d4804dSStefan Eßer // If we get here, we *must* be at the radix point. 257510328f8bSStefan Eßer assert(val[i] == '.'); 2576252884aeSStefan Eßer 2577252884aeSStefan Eßer BC_SIG_LOCK; 2578252884aeSStefan Eßer 257944d4804dSStefan Eßer // Unset the jump to reset in for these new initializations. 2580d101cdd6SStefan Eßer BC_UNSETJMP(vm); 2581252884aeSStefan Eßer 2582252884aeSStefan Eßer bc_num_init(&mult2, BC_NUM_BIGDIG_LOG10); 2583252884aeSStefan Eßer bc_num_init(&result1, BC_NUM_DEF_SIZE); 2584252884aeSStefan Eßer bc_num_init(&result2, BC_NUM_DEF_SIZE); 2585252884aeSStefan Eßer bc_num_one(&mult1); 2586252884aeSStefan Eßer 2587d101cdd6SStefan Eßer BC_SETJMP_LOCKED(vm, err); 2588252884aeSStefan Eßer 2589252884aeSStefan Eßer BC_SIG_UNLOCK; 2590252884aeSStefan Eßer 259144d4804dSStefan Eßer // Pointers for easy switching. 2592252884aeSStefan Eßer m1 = &mult1; 2593252884aeSStefan Eßer m2 = &mult2; 2594252884aeSStefan Eßer 259544d4804dSStefan Eßer // Parse the fractional part. This is the hard part. 259678bc019dSStefan Eßer for (i += 1, digs = 0; i < len && (c = val[i]); ++i, ++digs) 259778bc019dSStefan Eßer { 259850696a6eSStefan Eßer size_t rdx; 259950696a6eSStefan Eßer 260044d4804dSStefan Eßer // Convert the character to a digit. 2601252884aeSStefan Eßer v = bc_num_parseChar(c, base); 2602252884aeSStefan Eßer 260344d4804dSStefan Eßer // We keep growing result2 according to the base because the more digits 260444d4804dSStefan Eßer // after the radix, the more significant the digits close to the radix 260544d4804dSStefan Eßer // should be. 2606252884aeSStefan Eßer bc_num_mulArray(&result1, base, &result2); 2607252884aeSStefan Eßer 260844d4804dSStefan Eßer // Convert the digit to a number. 2609252884aeSStefan Eßer bc_num_bigdig2num(&temp, v); 261044d4804dSStefan Eßer 261144d4804dSStefan Eßer // Add the digit into the fraction part. 2612252884aeSStefan Eßer bc_num_add(&result2, &temp, &result1, 0); 261344d4804dSStefan Eßer 261444d4804dSStefan Eßer // Keep growing m1 and m2 for use after the loop. 2615252884aeSStefan Eßer bc_num_mulArray(m1, base, m2); 2616252884aeSStefan Eßer 261750696a6eSStefan Eßer rdx = BC_NUM_RDX_VAL(m2); 261850696a6eSStefan Eßer 261950696a6eSStefan Eßer if (m2->len < rdx) m2->len = rdx; 2620252884aeSStefan Eßer 262144d4804dSStefan Eßer // Switch. 2622252884aeSStefan Eßer ptr = m1; 2623252884aeSStefan Eßer m1 = m2; 2624252884aeSStefan Eßer m2 = ptr; 2625252884aeSStefan Eßer } 2626252884aeSStefan Eßer 2627252884aeSStefan Eßer // This one cannot be a divide by 0 because mult starts out at 1, then is 262844d4804dSStefan Eßer // multiplied by base, and base cannot be 0, so mult cannot be 0. And this 262944d4804dSStefan Eßer // is the reason we keep growing m1 and m2; this division is what converts 263044d4804dSStefan Eßer // the parsed fractional part from an integer to a fractional part. 2631252884aeSStefan Eßer bc_num_div(&result1, m1, &result2, digs * 2); 263244d4804dSStefan Eßer 263344d4804dSStefan Eßer // Pretruncate. 2634252884aeSStefan Eßer bc_num_truncate(&result2, digs); 263544d4804dSStefan Eßer 263644d4804dSStefan Eßer // The final add of the integer part to the fractional part. 2637252884aeSStefan Eßer bc_num_add(n, &result2, n, digs); 2638252884aeSStefan Eßer 263944d4804dSStefan Eßer // Basic cleanup. 264078bc019dSStefan Eßer if (BC_NUM_NONZERO(n)) 264178bc019dSStefan Eßer { 2642252884aeSStefan Eßer if (n->scale < digs) bc_num_extend(n, digs - n->scale); 2643252884aeSStefan Eßer } 2644252884aeSStefan Eßer else bc_num_zero(n); 2645252884aeSStefan Eßer 2646252884aeSStefan Eßer err: 2647252884aeSStefan Eßer BC_SIG_MAYLOCK; 2648252884aeSStefan Eßer bc_num_free(&result2); 2649252884aeSStefan Eßer bc_num_free(&result1); 2650252884aeSStefan Eßer bc_num_free(&mult2); 2651252884aeSStefan Eßer int_err: 2652252884aeSStefan Eßer BC_SIG_MAYLOCK; 2653252884aeSStefan Eßer bc_num_free(&mult1); 2654252884aeSStefan Eßer bc_num_free(&temp); 2655d101cdd6SStefan Eßer BC_LONGJMP_CONT(vm); 2656252884aeSStefan Eßer } 2657252884aeSStefan Eßer 265844d4804dSStefan Eßer /** 265944d4804dSStefan Eßer * Prints a backslash+newline combo if the number of characters needs it. This 266044d4804dSStefan Eßer * is really a convenience function. 266144d4804dSStefan Eßer */ 266278bc019dSStefan Eßer static inline void 266378bc019dSStefan Eßer bc_num_printNewline(void) 266478bc019dSStefan Eßer { 266550696a6eSStefan Eßer #if !BC_ENABLE_LIBRARY 2666d101cdd6SStefan Eßer if (vm->nchars >= vm->line_len - 1 && vm->line_len) 266778bc019dSStefan Eßer { 26687e5c51e5SStefan Eßer bc_vm_putchar('\\', bc_flush_none); 26697e5c51e5SStefan Eßer bc_vm_putchar('\n', bc_flush_err); 2670252884aeSStefan Eßer } 267150696a6eSStefan Eßer #endif // !BC_ENABLE_LIBRARY 2672252884aeSStefan Eßer } 2673252884aeSStefan Eßer 267444d4804dSStefan Eßer /** 267544d4804dSStefan Eßer * Prints a character after a backslash+newline, if needed. 267644d4804dSStefan Eßer * @param c The character to print. 267744d4804dSStefan Eßer * @param bslash Whether to print a backslash+newline. 267844d4804dSStefan Eßer */ 267978bc019dSStefan Eßer static void 268078bc019dSStefan Eßer bc_num_putchar(int c, bool bslash) 268178bc019dSStefan Eßer { 268244d4804dSStefan Eßer if (c != '\n' && bslash) bc_num_printNewline(); 26837e5c51e5SStefan Eßer bc_vm_putchar(c, bc_flush_save); 2684252884aeSStefan Eßer } 2685252884aeSStefan Eßer 268644d4804dSStefan Eßer #if !BC_ENABLE_LIBRARY 268744d4804dSStefan Eßer 268844d4804dSStefan Eßer /** 268944d4804dSStefan Eßer * Prints a character for a number's digit. This is for printing for dc's P 269044d4804dSStefan Eßer * command. This function does not need to worry about radix points. This is a 269144d4804dSStefan Eßer * BcNumDigitOp. 269244d4804dSStefan Eßer * @param n The "digit" to print. 269344d4804dSStefan Eßer * @param len The "length" of the digit, or number of characters that will 269444d4804dSStefan Eßer * need to be printed for the digit. 269544d4804dSStefan Eßer * @param rdx True if a decimal (radix) point should be printed. 269644d4804dSStefan Eßer * @param bslash True if a backslash+newline should be printed if the character 269744d4804dSStefan Eßer * limit for the line is reached, false otherwise. 269844d4804dSStefan Eßer */ 269978bc019dSStefan Eßer static void 270078bc019dSStefan Eßer bc_num_printChar(size_t n, size_t len, bool rdx, bool bslash) 270178bc019dSStefan Eßer { 2702252884aeSStefan Eßer BC_UNUSED(rdx); 2703252884aeSStefan Eßer BC_UNUSED(len); 270444d4804dSStefan Eßer BC_UNUSED(bslash); 2705252884aeSStefan Eßer assert(len == 1); 27067e5c51e5SStefan Eßer bc_vm_putchar((uchar) n, bc_flush_save); 2707252884aeSStefan Eßer } 2708252884aeSStefan Eßer 270944d4804dSStefan Eßer #endif // !BC_ENABLE_LIBRARY 271044d4804dSStefan Eßer 271144d4804dSStefan Eßer /** 271244d4804dSStefan Eßer * Prints a series of characters for large bases. This is for printing in bases 271344d4804dSStefan Eßer * above hexadecimal. This is a BcNumDigitOp. 271444d4804dSStefan Eßer * @param n The "digit" to print. 271544d4804dSStefan Eßer * @param len The "length" of the digit, or number of characters that will 271644d4804dSStefan Eßer * need to be printed for the digit. 271744d4804dSStefan Eßer * @param rdx True if a decimal (radix) point should be printed. 271844d4804dSStefan Eßer * @param bslash True if a backslash+newline should be printed if the character 271944d4804dSStefan Eßer * limit for the line is reached, false otherwise. 272044d4804dSStefan Eßer */ 272178bc019dSStefan Eßer static void 272278bc019dSStefan Eßer bc_num_printDigits(size_t n, size_t len, bool rdx, bool bslash) 272378bc019dSStefan Eßer { 2724252884aeSStefan Eßer size_t exp, pow; 2725252884aeSStefan Eßer 272644d4804dSStefan Eßer // If needed, print the radix; otherwise, print a space to separate digits. 272744d4804dSStefan Eßer bc_num_putchar(rdx ? '.' : ' ', true); 2728252884aeSStefan Eßer 272944d4804dSStefan Eßer // Calculate the exponent and power. 273078bc019dSStefan Eßer for (exp = 0, pow = 1; exp < len - 1; ++exp, pow *= BC_BASE) 273178bc019dSStefan Eßer { 273278bc019dSStefan Eßer continue; 273378bc019dSStefan Eßer } 2734252884aeSStefan Eßer 273544d4804dSStefan Eßer // Print each character individually. 273678bc019dSStefan Eßer for (exp = 0; exp < len; pow /= BC_BASE, ++exp) 273778bc019dSStefan Eßer { 273844d4804dSStefan Eßer // The individual subdigit. 2739252884aeSStefan Eßer size_t dig = n / pow; 274044d4804dSStefan Eßer 274144d4804dSStefan Eßer // Take the subdigit away. 2742252884aeSStefan Eßer n -= dig * pow; 274344d4804dSStefan Eßer 274444d4804dSStefan Eßer // Print the subdigit. 274544d4804dSStefan Eßer bc_num_putchar(((uchar) dig) + '0', bslash || exp != len - 1); 2746252884aeSStefan Eßer } 2747252884aeSStefan Eßer } 2748252884aeSStefan Eßer 274944d4804dSStefan Eßer /** 275044d4804dSStefan Eßer * Prints a character for a number's digit. This is for printing in bases for 275144d4804dSStefan Eßer * hexadecimal and below because they always print only one character at a time. 275244d4804dSStefan Eßer * This is a BcNumDigitOp. 275344d4804dSStefan Eßer * @param n The "digit" to print. 275444d4804dSStefan Eßer * @param len The "length" of the digit, or number of characters that will 275544d4804dSStefan Eßer * need to be printed for the digit. 275644d4804dSStefan Eßer * @param rdx True if a decimal (radix) point should be printed. 275744d4804dSStefan Eßer * @param bslash True if a backslash+newline should be printed if the character 275844d4804dSStefan Eßer * limit for the line is reached, false otherwise. 275944d4804dSStefan Eßer */ 276078bc019dSStefan Eßer static void 276178bc019dSStefan Eßer bc_num_printHex(size_t n, size_t len, bool rdx, bool bslash) 276278bc019dSStefan Eßer { 2763252884aeSStefan Eßer BC_UNUSED(len); 276444d4804dSStefan Eßer BC_UNUSED(bslash); 2765252884aeSStefan Eßer 2766252884aeSStefan Eßer assert(len == 1); 2767252884aeSStefan Eßer 276844d4804dSStefan Eßer if (rdx) bc_num_putchar('.', true); 2769252884aeSStefan Eßer 277044d4804dSStefan Eßer bc_num_putchar(bc_num_hex_digits[n], bslash); 2771252884aeSStefan Eßer } 2772252884aeSStefan Eßer 277344d4804dSStefan Eßer /** 277444d4804dSStefan Eßer * Prints a decimal number. This is specially written for optimization since 277544d4804dSStefan Eßer * this will be used the most and because bc's numbers are already in decimal. 277644d4804dSStefan Eßer * @param n The number to print. 277744d4804dSStefan Eßer * @param newline Whether to print backslash+newlines on long enough lines. 277844d4804dSStefan Eßer */ 277978bc019dSStefan Eßer static void 278078bc019dSStefan Eßer bc_num_printDecimal(const BcNum* restrict n, bool newline) 278178bc019dSStefan Eßer { 278250696a6eSStefan Eßer size_t i, j, rdx = BC_NUM_RDX_VAL(n); 2783252884aeSStefan Eßer bool zero = true; 2784252884aeSStefan Eßer size_t buffer[BC_BASE_DIGS]; 2785252884aeSStefan Eßer 278644d4804dSStefan Eßer // Print loop. 278778bc019dSStefan Eßer for (i = n->len - 1; i < n->len; --i) 278878bc019dSStefan Eßer { 2789252884aeSStefan Eßer BcDig n9 = n->num[i]; 2790252884aeSStefan Eßer size_t temp; 2791252884aeSStefan Eßer bool irdx = (i == rdx - 1); 2792252884aeSStefan Eßer 279344d4804dSStefan Eßer // Calculate the number of digits in the limb. 2794252884aeSStefan Eßer zero = (zero & !irdx); 2795252884aeSStefan Eßer temp = n->scale % BC_BASE_DIGS; 2796252884aeSStefan Eßer temp = i || !temp ? 0 : BC_BASE_DIGS - temp; 2797252884aeSStefan Eßer 279878bc019dSStefan Eßer // NOLINTNEXTLINE 2799252884aeSStefan Eßer memset(buffer, 0, BC_BASE_DIGS * sizeof(size_t)); 2800252884aeSStefan Eßer 280144d4804dSStefan Eßer // Fill the buffer with individual digits. 280278bc019dSStefan Eßer for (j = 0; n9 && j < BC_BASE_DIGS; ++j) 280378bc019dSStefan Eßer { 2804d213476dSStefan Eßer buffer[j] = ((size_t) n9) % BC_BASE; 2805252884aeSStefan Eßer n9 /= BC_BASE; 2806252884aeSStefan Eßer } 2807252884aeSStefan Eßer 280844d4804dSStefan Eßer // Print the digits in the buffer. 280978bc019dSStefan Eßer for (j = BC_BASE_DIGS - 1; j < BC_BASE_DIGS && j >= temp; --j) 281078bc019dSStefan Eßer { 281144d4804dSStefan Eßer // Figure out whether to print the decimal point. 2812252884aeSStefan Eßer bool print_rdx = (irdx & (j == BC_BASE_DIGS - 1)); 281344d4804dSStefan Eßer 281444d4804dSStefan Eßer // The zero variable helps us skip leading zero digits in the limb. 2815252884aeSStefan Eßer zero = (zero && buffer[j] == 0); 281644d4804dSStefan Eßer 281778bc019dSStefan Eßer if (!zero) 281878bc019dSStefan Eßer { 281944d4804dSStefan Eßer // While the first three arguments should be self-explanatory, 282044d4804dSStefan Eßer // the last needs explaining. I don't want to print a newline 282144d4804dSStefan Eßer // when the last digit to be printed could take the place of the 282244d4804dSStefan Eßer // backslash rather than being pushed, as a single character, to 282344d4804dSStefan Eßer // the next line. That's what that last argument does for bc. 282444d4804dSStefan Eßer bc_num_printHex(buffer[j], 1, print_rdx, 282544d4804dSStefan Eßer !newline || (j > temp || i != 0)); 282644d4804dSStefan Eßer } 2827252884aeSStefan Eßer } 2828252884aeSStefan Eßer } 2829252884aeSStefan Eßer } 2830252884aeSStefan Eßer 2831252884aeSStefan Eßer #if BC_ENABLE_EXTRA_MATH 2832252884aeSStefan Eßer 283344d4804dSStefan Eßer /** 283444d4804dSStefan Eßer * Prints a number in scientific or engineering format. When doing this, we are 283544d4804dSStefan Eßer * always printing in decimal. 283644d4804dSStefan Eßer * @param n The number to print. 283744d4804dSStefan Eßer * @param eng True if we are in engineering mode. 283844d4804dSStefan Eßer * @param newline Whether to print backslash+newlines on long enough lines. 283944d4804dSStefan Eßer */ 284078bc019dSStefan Eßer static void 284178bc019dSStefan Eßer bc_num_printExponent(const BcNum* restrict n, bool eng, bool newline) 284244d4804dSStefan Eßer { 284350696a6eSStefan Eßer size_t places, mod, nrdx = BC_NUM_RDX_VAL(n); 284450696a6eSStefan Eßer bool neg = (n->len <= nrdx); 2845252884aeSStefan Eßer BcNum temp, exp; 2846252884aeSStefan Eßer BcDig digs[BC_NUM_BIGDIG_LOG10]; 2847d101cdd6SStefan Eßer #if BC_ENABLE_LIBRARY 2848d101cdd6SStefan Eßer BcVm* vm = bcl_getspecific(); 2849d101cdd6SStefan Eßer #endif // BC_ENABLE_LIBRARY 2850252884aeSStefan Eßer 2851252884aeSStefan Eßer BC_SIG_LOCK; 2852252884aeSStefan Eßer 2853252884aeSStefan Eßer bc_num_createCopy(&temp, n); 2854252884aeSStefan Eßer 2855d101cdd6SStefan Eßer BC_SETJMP_LOCKED(vm, exit); 2856252884aeSStefan Eßer 2857252884aeSStefan Eßer BC_SIG_UNLOCK; 2858252884aeSStefan Eßer 285944d4804dSStefan Eßer // We need to calculate the exponents, and they change based on whether the 286044d4804dSStefan Eßer // number is all fractional or not, obviously. 286178bc019dSStefan Eßer if (neg) 286278bc019dSStefan Eßer { 286344d4804dSStefan Eßer // Figure out how many limbs after the decimal point is zero. 286444d4804dSStefan Eßer size_t i, idx = bc_num_nonZeroLen(n) - 1; 2865252884aeSStefan Eßer 2866252884aeSStefan Eßer places = 1; 2867252884aeSStefan Eßer 286844d4804dSStefan Eßer // Figure out how much in the last limb is zero. 286978bc019dSStefan Eßer for (i = BC_BASE_DIGS - 1; i < BC_BASE_DIGS; --i) 287078bc019dSStefan Eßer { 2871252884aeSStefan Eßer if (bc_num_pow10[i] > (BcBigDig) n->num[idx]) places += 1; 2872252884aeSStefan Eßer else break; 2873252884aeSStefan Eßer } 2874252884aeSStefan Eßer 287544d4804dSStefan Eßer // Calculate the combination of zero limbs and zero digits in the last 287644d4804dSStefan Eßer // limb. 287750696a6eSStefan Eßer places += (nrdx - (idx + 1)) * BC_BASE_DIGS; 2878252884aeSStefan Eßer mod = places % 3; 2879252884aeSStefan Eßer 288044d4804dSStefan Eßer // Calculate places if we are in engineering mode. 2881252884aeSStefan Eßer if (eng && mod != 0) places += 3 - mod; 288244d4804dSStefan Eßer 288344d4804dSStefan Eßer // Shift the temp to the right place. 2884252884aeSStefan Eßer bc_num_shiftLeft(&temp, places); 2885252884aeSStefan Eßer } 288678bc019dSStefan Eßer else 288778bc019dSStefan Eßer { 288844d4804dSStefan Eßer // This is the number of digits that we are supposed to put behind the 288944d4804dSStefan Eßer // decimal point. 2890252884aeSStefan Eßer places = bc_num_intDigits(n) - 1; 289144d4804dSStefan Eßer 289244d4804dSStefan Eßer // Calculate the true number based on whether engineering mode is 289344d4804dSStefan Eßer // activated. 2894252884aeSStefan Eßer mod = places % 3; 2895252884aeSStefan Eßer if (eng && mod != 0) places -= 3 - (3 - mod); 289644d4804dSStefan Eßer 289744d4804dSStefan Eßer // Shift the temp to the right place. 2898252884aeSStefan Eßer bc_num_shiftRight(&temp, places); 2899252884aeSStefan Eßer } 2900252884aeSStefan Eßer 290144d4804dSStefan Eßer // Print the shifted number. 290244d4804dSStefan Eßer bc_num_printDecimal(&temp, newline); 2903252884aeSStefan Eßer 290444d4804dSStefan Eßer // Print the e. 290544d4804dSStefan Eßer bc_num_putchar('e', !newline); 290644d4804dSStefan Eßer 290744d4804dSStefan Eßer // Need to explicitly print a zero exponent. 290878bc019dSStefan Eßer if (!places) 290978bc019dSStefan Eßer { 291044d4804dSStefan Eßer bc_num_printHex(0, 1, false, !newline); 2911252884aeSStefan Eßer goto exit; 2912252884aeSStefan Eßer } 2913252884aeSStefan Eßer 291444d4804dSStefan Eßer // Need to print sign for the exponent. 291544d4804dSStefan Eßer if (neg) bc_num_putchar('-', true); 2916252884aeSStefan Eßer 291744d4804dSStefan Eßer // Create a temporary for the exponent... 2918252884aeSStefan Eßer bc_num_setup(&exp, digs, BC_NUM_BIGDIG_LOG10); 2919252884aeSStefan Eßer bc_num_bigdig2num(&exp, (BcBigDig) places); 2920252884aeSStefan Eßer 292144d4804dSStefan Eßer /// ..and print it. 292244d4804dSStefan Eßer bc_num_printDecimal(&exp, newline); 2923252884aeSStefan Eßer 2924252884aeSStefan Eßer exit: 2925252884aeSStefan Eßer BC_SIG_MAYLOCK; 2926252884aeSStefan Eßer bc_num_free(&temp); 2927d101cdd6SStefan Eßer BC_LONGJMP_CONT(vm); 2928252884aeSStefan Eßer } 2929252884aeSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH 2930252884aeSStefan Eßer 293144d4804dSStefan Eßer /** 2932*aa339f1dSStefan Eßer * Takes a number with limbs with base BC_BASE_POW and converts the limb at the 2933*aa339f1dSStefan Eßer * given index to base @a pow, where @a pow is obase^N. 293444d4804dSStefan Eßer * @param n The number to convert. 293544d4804dSStefan Eßer * @param rem BC_BASE_POW - @a pow. 293644d4804dSStefan Eßer * @param pow The power of obase we will convert the number to. 293744d4804dSStefan Eßer * @param idx The index of the number to start converting at. Doing the 293844d4804dSStefan Eßer * conversion is O(n^2); we have to sweep through starting at the 2939*aa339f1dSStefan Eßer * least significant limb. 294044d4804dSStefan Eßer */ 294178bc019dSStefan Eßer static void 294278bc019dSStefan Eßer bc_num_printFixup(BcNum* restrict n, BcBigDig rem, BcBigDig pow, size_t idx) 2943252884aeSStefan Eßer { 2944252884aeSStefan Eßer size_t i, len = n->len - idx; 2945252884aeSStefan Eßer BcBigDig acc; 2946252884aeSStefan Eßer BcDig* a = n->num + idx; 2947252884aeSStefan Eßer 294844d4804dSStefan Eßer // Ignore if there's just one limb left. This is the part that requires the 294944d4804dSStefan Eßer // extra loop after the one calling this function in bc_num_printPrepare(). 2950252884aeSStefan Eßer if (len < 2) return; 2951252884aeSStefan Eßer 295244d4804dSStefan Eßer // Loop through the remaining limbs and convert. We start at the second limb 295344d4804dSStefan Eßer // because we pull the value from the previous one as well. 295478bc019dSStefan Eßer for (i = len - 1; i > 0; --i) 295578bc019dSStefan Eßer { 295644d4804dSStefan Eßer // Get the limb and add it to the previous, along with multiplying by 295744d4804dSStefan Eßer // the remainder because that's the proper overflow. "acc" means 295844d4804dSStefan Eßer // "accumulator," by the way. 2959252884aeSStefan Eßer acc = ((BcBigDig) a[i]) * rem + ((BcBigDig) a[i - 1]); 296044d4804dSStefan Eßer 296144d4804dSStefan Eßer // Store a value in base pow in the previous limb. 2962252884aeSStefan Eßer a[i - 1] = (BcDig) (acc % pow); 296344d4804dSStefan Eßer 296444d4804dSStefan Eßer // Divide by the base and accumulate the remaining value in the limb. 2965252884aeSStefan Eßer acc /= pow; 2966252884aeSStefan Eßer acc += (BcBigDig) a[i]; 2967252884aeSStefan Eßer 296844d4804dSStefan Eßer // If the accumulator is greater than the base... 296978bc019dSStefan Eßer if (acc >= BC_BASE_POW) 297078bc019dSStefan Eßer { 297144d4804dSStefan Eßer // Do we need to grow? 297278bc019dSStefan Eßer if (i == len - 1) 297378bc019dSStefan Eßer { 297444d4804dSStefan Eßer // Grow. 2975252884aeSStefan Eßer len = bc_vm_growSize(len, 1); 2976252884aeSStefan Eßer bc_num_expand(n, bc_vm_growSize(len, idx)); 297744d4804dSStefan Eßer 297844d4804dSStefan Eßer // Update the pointer because it may have moved. 2979252884aeSStefan Eßer a = n->num + idx; 298044d4804dSStefan Eßer 298144d4804dSStefan Eßer // Zero out the last limb. 2982252884aeSStefan Eßer a[len - 1] = 0; 2983252884aeSStefan Eßer } 2984252884aeSStefan Eßer 298544d4804dSStefan Eßer // Overflow into the next limb since we are over the base. 2986252884aeSStefan Eßer a[i + 1] += acc / BC_BASE_POW; 2987252884aeSStefan Eßer acc %= BC_BASE_POW; 2988252884aeSStefan Eßer } 2989252884aeSStefan Eßer 2990252884aeSStefan Eßer assert(acc < BC_BASE_POW); 299144d4804dSStefan Eßer 299244d4804dSStefan Eßer // Set the limb. 2993252884aeSStefan Eßer a[i] = (BcDig) acc; 2994252884aeSStefan Eßer } 2995252884aeSStefan Eßer 299644d4804dSStefan Eßer // We may have grown the number, so adjust the length. 2997252884aeSStefan Eßer n->len = len + idx; 2998252884aeSStefan Eßer } 2999252884aeSStefan Eßer 300044d4804dSStefan Eßer /** 3001*aa339f1dSStefan Eßer * Prepares a number for printing in a base that does not have BC_BASE_POW as a 3002*aa339f1dSStefan Eßer * power. This basically converts the number from having limbs of base 300344d4804dSStefan Eßer * BC_BASE_POW to limbs of pow, where pow is obase^N. 300444d4804dSStefan Eßer * @param n The number to prepare for printing. 300544d4804dSStefan Eßer * @param rem The remainder of BC_BASE_POW when divided by a power of the base. 300644d4804dSStefan Eßer * @param pow The power of the base. 300744d4804dSStefan Eßer */ 300878bc019dSStefan Eßer static void 300978bc019dSStefan Eßer bc_num_printPrepare(BcNum* restrict n, BcBigDig rem, BcBigDig pow) 301078bc019dSStefan Eßer { 3011252884aeSStefan Eßer size_t i; 3012252884aeSStefan Eßer 301344d4804dSStefan Eßer // Loop from the least significant limb to the most significant limb and 301444d4804dSStefan Eßer // convert limbs in each pass. 301578bc019dSStefan Eßer for (i = 0; i < n->len; ++i) 301678bc019dSStefan Eßer { 301778bc019dSStefan Eßer bc_num_printFixup(n, rem, pow, i); 301878bc019dSStefan Eßer } 3019252884aeSStefan Eßer 302044d4804dSStefan Eßer // bc_num_printFixup() does not do everything it is supposed to, so we do 302144d4804dSStefan Eßer // the last bit of cleanup here. That cleanup is to ensure that each limb 302244d4804dSStefan Eßer // is less than pow and to expand the number to fit new limbs as necessary. 302378bc019dSStefan Eßer for (i = 0; i < n->len; ++i) 302478bc019dSStefan Eßer { 3025252884aeSStefan Eßer assert(pow == ((BcBigDig) ((BcDig) pow))); 3026252884aeSStefan Eßer 302744d4804dSStefan Eßer // If the limb needs fixing... 302878bc019dSStefan Eßer if (n->num[i] >= (BcDig) pow) 302978bc019dSStefan Eßer { 303044d4804dSStefan Eßer // Do we need to grow? 303178bc019dSStefan Eßer if (i + 1 == n->len) 303278bc019dSStefan Eßer { 303344d4804dSStefan Eßer // Grow the number. 3034252884aeSStefan Eßer n->len = bc_vm_growSize(n->len, 1); 3035252884aeSStefan Eßer bc_num_expand(n, n->len); 303644d4804dSStefan Eßer 303744d4804dSStefan Eßer // Without this, we might use uninitialized data. 3038252884aeSStefan Eßer n->num[i + 1] = 0; 3039252884aeSStefan Eßer } 3040252884aeSStefan Eßer 3041252884aeSStefan Eßer assert(pow < BC_BASE_POW); 304244d4804dSStefan Eßer 304344d4804dSStefan Eßer // Overflow into the next limb. 3044252884aeSStefan Eßer n->num[i + 1] += n->num[i] / ((BcDig) pow); 3045252884aeSStefan Eßer n->num[i] %= (BcDig) pow; 3046252884aeSStefan Eßer } 3047252884aeSStefan Eßer } 3048252884aeSStefan Eßer } 3049252884aeSStefan Eßer 305078bc019dSStefan Eßer static void 305178bc019dSStefan Eßer bc_num_printNum(BcNum* restrict n, BcBigDig base, size_t len, 305244d4804dSStefan Eßer BcNumDigitOp print, bool newline) 3053252884aeSStefan Eßer { 3054252884aeSStefan Eßer BcVec stack; 305578bc019dSStefan Eßer BcNum intp, fracp1, fracp2, digit, flen1, flen2; 305678bc019dSStefan Eßer BcNum* n1; 305778bc019dSStefan Eßer BcNum* n2; 305878bc019dSStefan Eßer BcNum* temp; 305978bc019dSStefan Eßer BcBigDig dig = 0, acc, exp; 306078bc019dSStefan Eßer BcBigDig* ptr; 306144d4804dSStefan Eßer size_t i, j, nrdx, idigits; 3062252884aeSStefan Eßer bool radix; 3063252884aeSStefan Eßer BcDig digit_digs[BC_NUM_BIGDIG_LOG10 + 1]; 3064d101cdd6SStefan Eßer #if BC_ENABLE_LIBRARY 3065d101cdd6SStefan Eßer BcVm* vm = bcl_getspecific(); 3066d101cdd6SStefan Eßer #endif // BC_ENABLE_LIBRARY 3067252884aeSStefan Eßer 3068252884aeSStefan Eßer assert(base > 1); 3069252884aeSStefan Eßer 307044d4804dSStefan Eßer // Easy case. Even with scale, we just print this. 307178bc019dSStefan Eßer if (BC_NUM_ZERO(n)) 307278bc019dSStefan Eßer { 307344d4804dSStefan Eßer print(0, len, false, !newline); 3074252884aeSStefan Eßer return; 3075252884aeSStefan Eßer } 3076252884aeSStefan Eßer 3077252884aeSStefan Eßer // This function uses an algorithm that Stefan Esser <se@freebsd.org> came 3078252884aeSStefan Eßer // up with to print the integer part of a number. What it does is convert 3079252884aeSStefan Eßer // intp into a number of the specified base, but it does it directly, 3080252884aeSStefan Eßer // instead of just doing a series of divisions and printing the remainders 3081252884aeSStefan Eßer // in reverse order. 3082252884aeSStefan Eßer // 3083252884aeSStefan Eßer // Let me explain in a bit more detail: 3084252884aeSStefan Eßer // 308544d4804dSStefan Eßer // The algorithm takes the current least significant limb (after intp has 308644d4804dSStefan Eßer // been converted to an integer) and the next to least significant limb, and 308744d4804dSStefan Eßer // it converts the least significant limb into one of the specified base, 308844d4804dSStefan Eßer // putting any overflow into the next to least significant limb. It iterates 308944d4804dSStefan Eßer // through the whole number, from least significant to most significant, 309044d4804dSStefan Eßer // doing this conversion. At the end of that iteration, the least 309144d4804dSStefan Eßer // significant limb is converted, but the others are not, so it iterates 309244d4804dSStefan Eßer // again, starting at the next to least significant limb. It keeps doing 309344d4804dSStefan Eßer // that conversion, skipping one more limb than the last time, until all 309444d4804dSStefan Eßer // limbs have been converted. Then it prints them in reverse order. 3095252884aeSStefan Eßer // 3096252884aeSStefan Eßer // That is the gist of the algorithm. It leaves out several things, such as 309744d4804dSStefan Eßer // the fact that limbs are not always converted into the specified base, but 309844d4804dSStefan Eßer // into something close, basically a power of the specified base. In 3099252884aeSStefan Eßer // Stefan's words, "You could consider BcDigs to be of base 10^BC_BASE_DIGS 3100252884aeSStefan Eßer // in the normal case and obase^N for the largest value of N that satisfies 3101252884aeSStefan Eßer // obase^N <= 10^BC_BASE_DIGS. [This means that] the result is not in base 3102252884aeSStefan Eßer // "obase", but in base "obase^N", which happens to be printable as a number 3103252884aeSStefan Eßer // of base "obase" without consideration for neighbouring BcDigs." This fact 3104252884aeSStefan Eßer // is what necessitates the existence of the loop later in this function. 3105252884aeSStefan Eßer // 3106252884aeSStefan Eßer // The conversion happens in bc_num_printPrepare() where the outer loop 3107252884aeSStefan Eßer // happens and bc_num_printFixup() where the inner loop, or actual 310844d4804dSStefan Eßer // conversion, happens. In other words, bc_num_printPrepare() is where the 310944d4804dSStefan Eßer // loop that starts at the least significant limb and goes to the most 311044d4804dSStefan Eßer // significant limb. Then, on every iteration of its loop, it calls 311144d4804dSStefan Eßer // bc_num_printFixup(), which has the inner loop of actually converting 311244d4804dSStefan Eßer // the limbs it passes into limbs of base obase^N rather than base 311344d4804dSStefan Eßer // BC_BASE_POW. 3114252884aeSStefan Eßer 311550696a6eSStefan Eßer nrdx = BC_NUM_RDX_VAL(n); 311650696a6eSStefan Eßer 3117252884aeSStefan Eßer BC_SIG_LOCK; 3118252884aeSStefan Eßer 311944d4804dSStefan Eßer // The stack is what allows us to reverse the digits for printing. 312044d4804dSStefan Eßer bc_vec_init(&stack, sizeof(BcBigDig), BC_DTOR_NONE); 312150696a6eSStefan Eßer bc_num_init(&fracp1, nrdx); 3122252884aeSStefan Eßer 312344d4804dSStefan Eßer // intp will be the "integer part" of the number, so copy it. 3124252884aeSStefan Eßer bc_num_createCopy(&intp, n); 3125252884aeSStefan Eßer 3126d101cdd6SStefan Eßer BC_SETJMP_LOCKED(vm, err); 3127252884aeSStefan Eßer 3128252884aeSStefan Eßer BC_SIG_UNLOCK; 3129252884aeSStefan Eßer 313044d4804dSStefan Eßer // Make intp an integer. 3131252884aeSStefan Eßer bc_num_truncate(&intp, intp.scale); 3132252884aeSStefan Eßer 313344d4804dSStefan Eßer // Get the fractional part out. 3134252884aeSStefan Eßer bc_num_sub(n, &intp, &fracp1, 0); 3135252884aeSStefan Eßer 313644d4804dSStefan Eßer // If the base is not the same as the last base used for printing, we need 313744d4804dSStefan Eßer // to update the cached exponent and power. Yes, we cache the values of the 313844d4804dSStefan Eßer // exponent and power. That is to prevent us from calculating them every 313944d4804dSStefan Eßer // time because printing will probably happen multiple times on the same 314044d4804dSStefan Eßer // base. 3141d101cdd6SStefan Eßer if (base != vm->last_base) 314278bc019dSStefan Eßer { 3143d101cdd6SStefan Eßer vm->last_pow = 1; 3144d101cdd6SStefan Eßer vm->last_exp = 0; 3145252884aeSStefan Eßer 314644d4804dSStefan Eßer // Calculate the exponent and power. 3147d101cdd6SStefan Eßer while (vm->last_pow * base <= BC_BASE_POW) 314878bc019dSStefan Eßer { 3149d101cdd6SStefan Eßer vm->last_pow *= base; 3150d101cdd6SStefan Eßer vm->last_exp += 1; 3151252884aeSStefan Eßer } 3152252884aeSStefan Eßer 315344d4804dSStefan Eßer // Also, the remainder and base itself. 3154d101cdd6SStefan Eßer vm->last_rem = BC_BASE_POW - vm->last_pow; 3155d101cdd6SStefan Eßer vm->last_base = base; 3156252884aeSStefan Eßer } 3157252884aeSStefan Eßer 3158d101cdd6SStefan Eßer exp = vm->last_exp; 3159252884aeSStefan Eßer 3160d101cdd6SStefan Eßer // If vm->last_rem is 0, then the base we are printing in is a divisor of 316144d4804dSStefan Eßer // BC_BASE_POW, which is the easy case because it means that BC_BASE_POW is 316244d4804dSStefan Eßer // a power of obase, and no conversion is needed. If it *is* 0, then we have 316344d4804dSStefan Eßer // the hard case, and we have to prepare the number for the base. 3164d101cdd6SStefan Eßer if (vm->last_rem != 0) 3165d101cdd6SStefan Eßer { 3166d101cdd6SStefan Eßer bc_num_printPrepare(&intp, vm->last_rem, vm->last_pow); 3167d101cdd6SStefan Eßer } 3168252884aeSStefan Eßer 316944d4804dSStefan Eßer // After the conversion comes the surprisingly easy part. From here on out, 317044d4804dSStefan Eßer // this is basically naive code that I wrote, adjusted for the larger bases. 317144d4804dSStefan Eßer 317244d4804dSStefan Eßer // Fill the stack of digits for the integer part. 317378bc019dSStefan Eßer for (i = 0; i < intp.len; ++i) 317478bc019dSStefan Eßer { 317544d4804dSStefan Eßer // Get the limb. 3176252884aeSStefan Eßer acc = (BcBigDig) intp.num[i]; 3177252884aeSStefan Eßer 317844d4804dSStefan Eßer // Turn the limb into digits of base obase. 3179252884aeSStefan Eßer for (j = 0; j < exp && (i < intp.len - 1 || acc != 0); ++j) 3180252884aeSStefan Eßer { 318144d4804dSStefan Eßer // This condition is true if we are not at the last digit. 318278bc019dSStefan Eßer if (j != exp - 1) 318378bc019dSStefan Eßer { 3184252884aeSStefan Eßer dig = acc % base; 3185252884aeSStefan Eßer acc /= base; 3186252884aeSStefan Eßer } 318778bc019dSStefan Eßer else 318878bc019dSStefan Eßer { 3189252884aeSStefan Eßer dig = acc; 3190252884aeSStefan Eßer acc = 0; 3191252884aeSStefan Eßer } 3192252884aeSStefan Eßer 3193252884aeSStefan Eßer assert(dig < base); 3194252884aeSStefan Eßer 319544d4804dSStefan Eßer // Push the digit onto the stack. 3196252884aeSStefan Eßer bc_vec_push(&stack, &dig); 3197252884aeSStefan Eßer } 3198252884aeSStefan Eßer 3199252884aeSStefan Eßer assert(acc == 0); 3200252884aeSStefan Eßer } 3201252884aeSStefan Eßer 320244d4804dSStefan Eßer // Go through the stack backwards and print each digit. 320378bc019dSStefan Eßer for (i = 0; i < stack.len; ++i) 320478bc019dSStefan Eßer { 3205252884aeSStefan Eßer ptr = bc_vec_item_rev(&stack, i); 320644d4804dSStefan Eßer 3207252884aeSStefan Eßer assert(ptr != NULL); 320844d4804dSStefan Eßer 320944d4804dSStefan Eßer // While the first three arguments should be self-explanatory, the last 321044d4804dSStefan Eßer // needs explaining. I don't want to print a newline when the last digit 321144d4804dSStefan Eßer // to be printed could take the place of the backslash rather than being 321244d4804dSStefan Eßer // pushed, as a single character, to the next line. That's what that 321344d4804dSStefan Eßer // last argument does for bc. 321478bc019dSStefan Eßer print(*ptr, len, false, 321578bc019dSStefan Eßer !newline || (n->scale != 0 || i == stack.len - 1)); 3216252884aeSStefan Eßer } 3217252884aeSStefan Eßer 321844d4804dSStefan Eßer // We are done if there is no fractional part. 3219252884aeSStefan Eßer if (!n->scale) goto err; 3220252884aeSStefan Eßer 3221252884aeSStefan Eßer BC_SIG_LOCK; 3222252884aeSStefan Eßer 322344d4804dSStefan Eßer // Reset the jump because some locals are changing. 3224d101cdd6SStefan Eßer BC_UNSETJMP(vm); 3225252884aeSStefan Eßer 322650696a6eSStefan Eßer bc_num_init(&fracp2, nrdx); 3227252884aeSStefan Eßer bc_num_setup(&digit, digit_digs, sizeof(digit_digs) / sizeof(BcDig)); 3228252884aeSStefan Eßer bc_num_init(&flen1, BC_NUM_BIGDIG_LOG10); 3229252884aeSStefan Eßer bc_num_init(&flen2, BC_NUM_BIGDIG_LOG10); 3230252884aeSStefan Eßer 3231d101cdd6SStefan Eßer BC_SETJMP_LOCKED(vm, frac_err); 3232252884aeSStefan Eßer 3233252884aeSStefan Eßer BC_SIG_UNLOCK; 3234252884aeSStefan Eßer 3235252884aeSStefan Eßer bc_num_one(&flen1); 3236252884aeSStefan Eßer 3237252884aeSStefan Eßer radix = true; 323844d4804dSStefan Eßer 323944d4804dSStefan Eßer // Pointers for easy switching. 3240252884aeSStefan Eßer n1 = &flen1; 3241252884aeSStefan Eßer n2 = &flen2; 3242252884aeSStefan Eßer 3243252884aeSStefan Eßer fracp2.scale = n->scale; 324450696a6eSStefan Eßer BC_NUM_RDX_SET_NP(fracp2, BC_NUM_RDX(fracp2.scale)); 3245252884aeSStefan Eßer 324644d4804dSStefan Eßer // As long as we have not reached the scale of the number, keep printing. 324778bc019dSStefan Eßer while ((idigits = bc_num_intDigits(n1)) <= n->scale) 324878bc019dSStefan Eßer { 324944d4804dSStefan Eßer // These numbers will keep growing. 3250252884aeSStefan Eßer bc_num_expand(&fracp2, fracp1.len + 1); 3251252884aeSStefan Eßer bc_num_mulArray(&fracp1, base, &fracp2); 325250696a6eSStefan Eßer 325350696a6eSStefan Eßer nrdx = BC_NUM_RDX_VAL_NP(fracp2); 325450696a6eSStefan Eßer 325544d4804dSStefan Eßer // Ensure an invariant. 325650696a6eSStefan Eßer if (fracp2.len < nrdx) fracp2.len = nrdx; 3257252884aeSStefan Eßer 3258252884aeSStefan Eßer // fracp is guaranteed to be non-negative and small enough. 325944d4804dSStefan Eßer dig = bc_num_bigdig2(&fracp2); 3260252884aeSStefan Eßer 326144d4804dSStefan Eßer // Convert the digit to a number and subtract it from the number. 3262252884aeSStefan Eßer bc_num_bigdig2num(&digit, dig); 3263252884aeSStefan Eßer bc_num_sub(&fracp2, &digit, &fracp1, 0); 3264252884aeSStefan Eßer 326544d4804dSStefan Eßer // While the first three arguments should be self-explanatory, the last 326644d4804dSStefan Eßer // needs explaining. I don't want to print a newline when the last digit 326744d4804dSStefan Eßer // to be printed could take the place of the backslash rather than being 326844d4804dSStefan Eßer // pushed, as a single character, to the next line. That's what that 326944d4804dSStefan Eßer // last argument does for bc. 327044d4804dSStefan Eßer print(dig, len, radix, !newline || idigits != n->scale); 327144d4804dSStefan Eßer 327244d4804dSStefan Eßer // Update the multipliers. 3273252884aeSStefan Eßer bc_num_mulArray(n1, base, n2); 3274252884aeSStefan Eßer 3275252884aeSStefan Eßer radix = false; 327644d4804dSStefan Eßer 327744d4804dSStefan Eßer // Switch. 3278252884aeSStefan Eßer temp = n1; 3279252884aeSStefan Eßer n1 = n2; 3280252884aeSStefan Eßer n2 = temp; 3281252884aeSStefan Eßer } 3282252884aeSStefan Eßer 3283252884aeSStefan Eßer frac_err: 3284252884aeSStefan Eßer BC_SIG_MAYLOCK; 3285252884aeSStefan Eßer bc_num_free(&flen2); 3286252884aeSStefan Eßer bc_num_free(&flen1); 3287252884aeSStefan Eßer bc_num_free(&fracp2); 3288252884aeSStefan Eßer err: 3289252884aeSStefan Eßer BC_SIG_MAYLOCK; 3290252884aeSStefan Eßer bc_num_free(&fracp1); 3291252884aeSStefan Eßer bc_num_free(&intp); 3292252884aeSStefan Eßer bc_vec_free(&stack); 3293d101cdd6SStefan Eßer BC_LONGJMP_CONT(vm); 3294252884aeSStefan Eßer } 3295252884aeSStefan Eßer 329644d4804dSStefan Eßer /** 329744d4804dSStefan Eßer * Prints a number in the specified base, or rather, figures out which function 329844d4804dSStefan Eßer * to call to print the number in the specified base and calls it. 329944d4804dSStefan Eßer * @param n The number to print. 330044d4804dSStefan Eßer * @param base The base to print in. 330144d4804dSStefan Eßer * @param newline Whether to print backslash+newlines on long enough lines. 330244d4804dSStefan Eßer */ 330378bc019dSStefan Eßer static void 330478bc019dSStefan Eßer bc_num_printBase(BcNum* restrict n, BcBigDig base, bool newline) 330578bc019dSStefan Eßer { 3306252884aeSStefan Eßer size_t width; 3307252884aeSStefan Eßer BcNumDigitOp print; 330850696a6eSStefan Eßer bool neg = BC_NUM_NEG(n); 3309252884aeSStefan Eßer 331044d4804dSStefan Eßer // Clear the sign because it makes the actual printing easier when we have 331144d4804dSStefan Eßer // to do math. 331250696a6eSStefan Eßer BC_NUM_NEG_CLR(n); 3313252884aeSStefan Eßer 331444d4804dSStefan Eßer // Bases at hexadecimal and below are printed as one character, larger bases 331544d4804dSStefan Eßer // are printed as a series of digits separated by spaces. 331678bc019dSStefan Eßer if (base <= BC_NUM_MAX_POSIX_IBASE) 331778bc019dSStefan Eßer { 3318252884aeSStefan Eßer width = 1; 3319252884aeSStefan Eßer print = bc_num_printHex; 3320252884aeSStefan Eßer } 332178bc019dSStefan Eßer else 332278bc019dSStefan Eßer { 3323252884aeSStefan Eßer assert(base <= BC_BASE_POW); 3324252884aeSStefan Eßer width = bc_num_log10(base - 1); 3325252884aeSStefan Eßer print = bc_num_printDigits; 3326252884aeSStefan Eßer } 3327252884aeSStefan Eßer 332844d4804dSStefan Eßer // Print. 332944d4804dSStefan Eßer bc_num_printNum(n, base, width, print, newline); 333044d4804dSStefan Eßer 333144d4804dSStefan Eßer // Reset the sign. 333250696a6eSStefan Eßer n->rdx = BC_NUM_NEG_VAL(n, neg); 3333252884aeSStefan Eßer } 3334252884aeSStefan Eßer 333544d4804dSStefan Eßer #if !BC_ENABLE_LIBRARY 333644d4804dSStefan Eßer 333778bc019dSStefan Eßer void 333878bc019dSStefan Eßer bc_num_stream(BcNum* restrict n) 333978bc019dSStefan Eßer { 334044d4804dSStefan Eßer bc_num_printNum(n, BC_NUM_STREAM_BASE, 1, bc_num_printChar, false); 3341252884aeSStefan Eßer } 334244d4804dSStefan Eßer 334344d4804dSStefan Eßer #endif // !BC_ENABLE_LIBRARY 3344252884aeSStefan Eßer 334578bc019dSStefan Eßer void 334678bc019dSStefan Eßer bc_num_setup(BcNum* restrict n, BcDig* restrict num, size_t cap) 334778bc019dSStefan Eßer { 3348252884aeSStefan Eßer assert(n != NULL); 3349252884aeSStefan Eßer n->num = num; 3350252884aeSStefan Eßer n->cap = cap; 3351252884aeSStefan Eßer bc_num_zero(n); 3352252884aeSStefan Eßer } 3353252884aeSStefan Eßer 335478bc019dSStefan Eßer void 335578bc019dSStefan Eßer bc_num_init(BcNum* restrict n, size_t req) 335678bc019dSStefan Eßer { 3357252884aeSStefan Eßer BcDig* num; 3358252884aeSStefan Eßer 3359252884aeSStefan Eßer BC_SIG_ASSERT_LOCKED; 3360252884aeSStefan Eßer 3361252884aeSStefan Eßer assert(n != NULL); 3362252884aeSStefan Eßer 336344d4804dSStefan Eßer // BC_NUM_DEF_SIZE is set to be about the smallest allocation size that 336444d4804dSStefan Eßer // malloc() returns in practice, so just use it. 3365252884aeSStefan Eßer req = req >= BC_NUM_DEF_SIZE ? req : BC_NUM_DEF_SIZE; 3366252884aeSStefan Eßer 336744d4804dSStefan Eßer // If we can't use a temp, allocate. 3368d101cdd6SStefan Eßer if (req != BC_NUM_DEF_SIZE) num = bc_vm_malloc(BC_NUM_SIZE(req)); 3369d101cdd6SStefan Eßer else 337078bc019dSStefan Eßer { 3371d101cdd6SStefan Eßer num = bc_vm_getTemp() == NULL ? bc_vm_malloc(BC_NUM_SIZE(req)) : 3372d101cdd6SStefan Eßer bc_vm_takeTemp(); 337378bc019dSStefan Eßer } 3374252884aeSStefan Eßer 3375252884aeSStefan Eßer bc_num_setup(n, num, req); 3376252884aeSStefan Eßer } 3377252884aeSStefan Eßer 337878bc019dSStefan Eßer void 337978bc019dSStefan Eßer bc_num_clear(BcNum* restrict n) 338078bc019dSStefan Eßer { 3381252884aeSStefan Eßer n->num = NULL; 3382252884aeSStefan Eßer n->cap = 0; 3383252884aeSStefan Eßer } 3384252884aeSStefan Eßer 338578bc019dSStefan Eßer void 338678bc019dSStefan Eßer bc_num_free(void* num) 338778bc019dSStefan Eßer { 3388252884aeSStefan Eßer BcNum* n = (BcNum*) num; 3389252884aeSStefan Eßer 3390252884aeSStefan Eßer BC_SIG_ASSERT_LOCKED; 3391252884aeSStefan Eßer 3392252884aeSStefan Eßer assert(n != NULL); 3393252884aeSStefan Eßer 339444d4804dSStefan Eßer if (n->cap == BC_NUM_DEF_SIZE) bc_vm_addTemp(n->num); 3395252884aeSStefan Eßer else free(n->num); 3396252884aeSStefan Eßer } 3397252884aeSStefan Eßer 339878bc019dSStefan Eßer void 339978bc019dSStefan Eßer bc_num_copy(BcNum* d, const BcNum* s) 340078bc019dSStefan Eßer { 3401252884aeSStefan Eßer assert(d != NULL && s != NULL); 340244d4804dSStefan Eßer 3403252884aeSStefan Eßer if (d == s) return; 340444d4804dSStefan Eßer 3405252884aeSStefan Eßer bc_num_expand(d, s->len); 3406252884aeSStefan Eßer d->len = s->len; 340744d4804dSStefan Eßer 340844d4804dSStefan Eßer // I can just copy directly here because the sign *and* rdx will be 340944d4804dSStefan Eßer // properly preserved. 3410252884aeSStefan Eßer d->rdx = s->rdx; 3411252884aeSStefan Eßer d->scale = s->scale; 341278bc019dSStefan Eßer // NOLINTNEXTLINE 3413252884aeSStefan Eßer memcpy(d->num, s->num, BC_NUM_SIZE(d->len)); 3414252884aeSStefan Eßer } 3415252884aeSStefan Eßer 341678bc019dSStefan Eßer void 341778bc019dSStefan Eßer bc_num_createCopy(BcNum* d, const BcNum* s) 341878bc019dSStefan Eßer { 3419252884aeSStefan Eßer BC_SIG_ASSERT_LOCKED; 3420252884aeSStefan Eßer bc_num_init(d, s->len); 3421252884aeSStefan Eßer bc_num_copy(d, s); 3422252884aeSStefan Eßer } 3423252884aeSStefan Eßer 342478bc019dSStefan Eßer void 342578bc019dSStefan Eßer bc_num_createFromBigdig(BcNum* restrict n, BcBigDig val) 342678bc019dSStefan Eßer { 3427252884aeSStefan Eßer BC_SIG_ASSERT_LOCKED; 342850696a6eSStefan Eßer bc_num_init(n, BC_NUM_BIGDIG_LOG10); 3429252884aeSStefan Eßer bc_num_bigdig2num(n, val); 3430252884aeSStefan Eßer } 3431252884aeSStefan Eßer 343278bc019dSStefan Eßer size_t 343378bc019dSStefan Eßer bc_num_scale(const BcNum* restrict n) 343478bc019dSStefan Eßer { 3435252884aeSStefan Eßer return n->scale; 3436252884aeSStefan Eßer } 3437252884aeSStefan Eßer 343878bc019dSStefan Eßer size_t 343978bc019dSStefan Eßer bc_num_len(const BcNum* restrict n) 344078bc019dSStefan Eßer { 3441252884aeSStefan Eßer size_t len = n->len; 3442252884aeSStefan Eßer 344344d4804dSStefan Eßer // Always return at least 1. 3444028616d0SStefan Eßer if (BC_NUM_ZERO(n)) return n->scale ? n->scale : 1; 3445252884aeSStefan Eßer 344644d4804dSStefan Eßer // If this is true, there is no integer portion of the number. 344778bc019dSStefan Eßer if (BC_NUM_RDX_VAL(n) == len) 344878bc019dSStefan Eßer { 344944d4804dSStefan Eßer // We have to take into account the fact that some of the digits right 345044d4804dSStefan Eßer // after the decimal could be zero. If that is the case, we need to 345144d4804dSStefan Eßer // ignore them until we hit the first non-zero digit. 345244d4804dSStefan Eßer 3453252884aeSStefan Eßer size_t zero, scale; 3454252884aeSStefan Eßer 345544d4804dSStefan Eßer // The number of limbs with non-zero digits. 345644d4804dSStefan Eßer len = bc_num_nonZeroLen(n); 3457252884aeSStefan Eßer 345844d4804dSStefan Eßer // Get the number of digits in the last limb. 3459252884aeSStefan Eßer scale = n->scale % BC_BASE_DIGS; 3460252884aeSStefan Eßer scale = scale ? scale : BC_BASE_DIGS; 3461252884aeSStefan Eßer 346244d4804dSStefan Eßer // Get the number of zero digits. 3463252884aeSStefan Eßer zero = bc_num_zeroDigits(n->num + len - 1); 3464252884aeSStefan Eßer 346544d4804dSStefan Eßer // Calculate the true length. 3466252884aeSStefan Eßer len = len * BC_BASE_DIGS - zero - (BC_BASE_DIGS - scale); 3467252884aeSStefan Eßer } 346844d4804dSStefan Eßer // Otherwise, count the number of int digits and return that plus the scale. 3469252884aeSStefan Eßer else len = bc_num_intDigits(n) + n->scale; 3470252884aeSStefan Eßer 3471252884aeSStefan Eßer return len; 3472252884aeSStefan Eßer } 3473252884aeSStefan Eßer 347478bc019dSStefan Eßer void 347578bc019dSStefan Eßer bc_num_parse(BcNum* restrict n, const char* restrict val, BcBigDig base) 347678bc019dSStefan Eßer { 3477103d7cdfSStefan Eßer #if BC_DEBUG 3478d101cdd6SStefan Eßer #if BC_ENABLE_LIBRARY 3479d101cdd6SStefan Eßer BcVm* vm = bcl_getspecific(); 3480d101cdd6SStefan Eßer #endif // BC_ENABLE_LIBRARY 3481103d7cdfSStefan Eßer #endif // BC_DEBUG 3482d101cdd6SStefan Eßer 3483252884aeSStefan Eßer assert(n != NULL && val != NULL && base); 3484d101cdd6SStefan Eßer assert(base >= BC_NUM_MIN_BASE && base <= vm->maxes[BC_PROG_GLOBALS_IBASE]); 3485252884aeSStefan Eßer assert(bc_num_strValid(val)); 3486252884aeSStefan Eßer 348744d4804dSStefan Eßer // A one character number is *always* parsed as though the base was the 348844d4804dSStefan Eßer // maximum allowed ibase, per the bc spec. 348978bc019dSStefan Eßer if (!val[1]) 349078bc019dSStefan Eßer { 3491252884aeSStefan Eßer BcBigDig dig = bc_num_parseChar(val[0], BC_NUM_MAX_LBASE); 3492252884aeSStefan Eßer bc_num_bigdig2num(n, dig); 3493252884aeSStefan Eßer } 3494252884aeSStefan Eßer else if (base == BC_BASE) bc_num_parseDecimal(n, val); 3495252884aeSStefan Eßer else bc_num_parseBase(n, val, base); 349650696a6eSStefan Eßer 349750696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(n)); 3498252884aeSStefan Eßer } 3499252884aeSStefan Eßer 350078bc019dSStefan Eßer void 350178bc019dSStefan Eßer bc_num_print(BcNum* restrict n, BcBigDig base, bool newline) 350278bc019dSStefan Eßer { 3503252884aeSStefan Eßer assert(n != NULL); 3504252884aeSStefan Eßer assert(BC_ENABLE_EXTRA_MATH || base >= BC_NUM_MIN_BASE); 3505252884aeSStefan Eßer 350644d4804dSStefan Eßer // We may need a newline, just to start. 3507252884aeSStefan Eßer bc_num_printNewline(); 3508252884aeSStefan Eßer 350978bc019dSStefan Eßer if (BC_NUM_NONZERO(n)) 351078bc019dSStefan Eßer { 3511d101cdd6SStefan Eßer #if BC_ENABLE_LIBRARY 3512d101cdd6SStefan Eßer BcVm* vm = bcl_getspecific(); 3513d101cdd6SStefan Eßer #endif // BC_ENABLE_LIBRARY 3514d101cdd6SStefan Eßer 3515d43fa8efSStefan Eßer // Print the sign. 3516d43fa8efSStefan Eßer if (BC_NUM_NEG(n)) bc_num_putchar('-', true); 3517d43fa8efSStefan Eßer 351876238846SStefan Eßer // Print the leading zero if necessary. We don't print when using 351976238846SStefan Eßer // scientific or engineering modes. 352076238846SStefan Eßer if (BC_Z && BC_NUM_RDX_VAL(n) == n->len && base != 0 && base != 1) 352178bc019dSStefan Eßer { 3522d43fa8efSStefan Eßer bc_num_printHex(0, 1, false, !newline); 3523d43fa8efSStefan Eßer } 352478bc019dSStefan Eßer } 3525d43fa8efSStefan Eßer 352644d4804dSStefan Eßer // Short-circuit 0. 352744d4804dSStefan Eßer if (BC_NUM_ZERO(n)) bc_num_printHex(0, 1, false, !newline); 352844d4804dSStefan Eßer else if (base == BC_BASE) bc_num_printDecimal(n, newline); 3529252884aeSStefan Eßer #if BC_ENABLE_EXTRA_MATH 353044d4804dSStefan Eßer else if (base == 0 || base == 1) 353178bc019dSStefan Eßer { 353244d4804dSStefan Eßer bc_num_printExponent(n, base != 0, newline); 353378bc019dSStefan Eßer } 3534252884aeSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH 353544d4804dSStefan Eßer else bc_num_printBase(n, base, newline); 3536252884aeSStefan Eßer 353744d4804dSStefan Eßer if (newline) bc_num_putchar('\n', false); 3538252884aeSStefan Eßer } 3539252884aeSStefan Eßer 354078bc019dSStefan Eßer BcBigDig 354178bc019dSStefan Eßer bc_num_bigdig2(const BcNum* restrict n) 354278bc019dSStefan Eßer { 3543103d7cdfSStefan Eßer #if BC_DEBUG 3544d101cdd6SStefan Eßer #if BC_ENABLE_LIBRARY 3545d101cdd6SStefan Eßer BcVm* vm = bcl_getspecific(); 3546d101cdd6SStefan Eßer #endif // BC_ENABLE_LIBRARY 3547103d7cdfSStefan Eßer #endif // BC_DEBUG 3548d101cdd6SStefan Eßer 3549252884aeSStefan Eßer // This function returns no errors because it's guaranteed to succeed if 355044d4804dSStefan Eßer // its preconditions are met. Those preconditions include both n needs to 3551d101cdd6SStefan Eßer // be non-NULL, n being non-negative, and n being less than vm->max. If all 355244d4804dSStefan Eßer // of that is true, then we can just convert without worrying about negative 355344d4804dSStefan Eßer // errors or overflow. 3554252884aeSStefan Eßer 3555252884aeSStefan Eßer BcBigDig r = 0; 355650696a6eSStefan Eßer size_t nrdx = BC_NUM_RDX_VAL(n); 3557252884aeSStefan Eßer 355844d4804dSStefan Eßer assert(n != NULL); 355950696a6eSStefan Eßer assert(!BC_NUM_NEG(n)); 3560d101cdd6SStefan Eßer assert(bc_num_cmp(n, &vm->max) < 0); 356150696a6eSStefan Eßer assert(n->len - nrdx <= 3); 3562252884aeSStefan Eßer 3563252884aeSStefan Eßer // There is a small speed win from unrolling the loop here, and since it 3564252884aeSStefan Eßer // only adds 53 bytes, I decided that it was worth it. 356578bc019dSStefan Eßer switch (n->len - nrdx) 356678bc019dSStefan Eßer { 3567252884aeSStefan Eßer case 3: 356850696a6eSStefan Eßer { 356950696a6eSStefan Eßer r = (BcBigDig) n->num[nrdx + 2]; 357078bc019dSStefan Eßer 3571252884aeSStefan Eßer // Fallthrough. 357250696a6eSStefan Eßer BC_FALLTHROUGH 357378bc019dSStefan Eßer } 357450696a6eSStefan Eßer 3575252884aeSStefan Eßer case 2: 357650696a6eSStefan Eßer { 357750696a6eSStefan Eßer r = r * BC_BASE_POW + (BcBigDig) n->num[nrdx + 1]; 357878bc019dSStefan Eßer 3579252884aeSStefan Eßer // Fallthrough. 358050696a6eSStefan Eßer BC_FALLTHROUGH 358178bc019dSStefan Eßer } 358250696a6eSStefan Eßer 3583252884aeSStefan Eßer case 1: 358450696a6eSStefan Eßer { 358550696a6eSStefan Eßer r = r * BC_BASE_POW + (BcBigDig) n->num[nrdx]; 358650696a6eSStefan Eßer } 3587252884aeSStefan Eßer } 3588252884aeSStefan Eßer 358944d4804dSStefan Eßer return r; 3590252884aeSStefan Eßer } 3591252884aeSStefan Eßer 359278bc019dSStefan Eßer BcBigDig 359378bc019dSStefan Eßer bc_num_bigdig(const BcNum* restrict n) 359478bc019dSStefan Eßer { 3595d101cdd6SStefan Eßer #if BC_ENABLE_LIBRARY 3596d101cdd6SStefan Eßer BcVm* vm = bcl_getspecific(); 3597d101cdd6SStefan Eßer #endif // BC_ENABLE_LIBRARY 3598d101cdd6SStefan Eßer 359944d4804dSStefan Eßer assert(n != NULL); 3600252884aeSStefan Eßer 360144d4804dSStefan Eßer // This error checking is extremely important, and if you do not have a 360244d4804dSStefan Eßer // guarantee that converting a number will always succeed in a particular 360344d4804dSStefan Eßer // case, you *must* call this function to get these error checks. This 360444d4804dSStefan Eßer // includes all instances of numbers inputted by the user or calculated by 360544d4804dSStefan Eßer // the user. Otherwise, you can call the faster bc_num_bigdig2(). 360644d4804dSStefan Eßer if (BC_ERR(BC_NUM_NEG(n))) bc_err(BC_ERR_MATH_NEGATIVE); 3607d101cdd6SStefan Eßer if (BC_ERR(bc_num_cmp(n, &vm->max) >= 0)) bc_err(BC_ERR_MATH_OVERFLOW); 3608252884aeSStefan Eßer 360944d4804dSStefan Eßer return bc_num_bigdig2(n); 3610252884aeSStefan Eßer } 3611252884aeSStefan Eßer 361278bc019dSStefan Eßer void 361378bc019dSStefan Eßer bc_num_bigdig2num(BcNum* restrict n, BcBigDig val) 361478bc019dSStefan Eßer { 3615252884aeSStefan Eßer BcDig* ptr; 3616252884aeSStefan Eßer size_t i; 3617252884aeSStefan Eßer 3618252884aeSStefan Eßer assert(n != NULL); 3619252884aeSStefan Eßer 3620252884aeSStefan Eßer bc_num_zero(n); 3621252884aeSStefan Eßer 362244d4804dSStefan Eßer // Already 0. 3623252884aeSStefan Eßer if (!val) return; 3624252884aeSStefan Eßer 362544d4804dSStefan Eßer // Expand first. This is the only way this function can fail, and it's a 362644d4804dSStefan Eßer // fatal error. 3627252884aeSStefan Eßer bc_num_expand(n, BC_NUM_BIGDIG_LOG10); 3628252884aeSStefan Eßer 362944d4804dSStefan Eßer // The conversion is easy because numbers are laid out in little-endian 363044d4804dSStefan Eßer // order. 3631252884aeSStefan Eßer for (ptr = n->num, i = 0; val; ++i, val /= BC_BASE_POW) 363278bc019dSStefan Eßer { 3633252884aeSStefan Eßer ptr[i] = val % BC_BASE_POW; 363478bc019dSStefan Eßer } 3635252884aeSStefan Eßer 3636252884aeSStefan Eßer n->len = i; 3637252884aeSStefan Eßer } 3638252884aeSStefan Eßer 363944d4804dSStefan Eßer #if BC_ENABLE_EXTRA_MATH 364044d4804dSStefan Eßer 364178bc019dSStefan Eßer void 364278bc019dSStefan Eßer bc_num_rng(const BcNum* restrict n, BcRNG* rng) 364378bc019dSStefan Eßer { 364450696a6eSStefan Eßer BcNum temp, temp2, intn, frac; 3645252884aeSStefan Eßer BcRand state1, state2, inc1, inc2; 364650696a6eSStefan Eßer size_t nrdx = BC_NUM_RDX_VAL(n); 3647d101cdd6SStefan Eßer #if BC_ENABLE_LIBRARY 3648d101cdd6SStefan Eßer BcVm* vm = bcl_getspecific(); 3649d101cdd6SStefan Eßer #endif // BC_ENABLE_LIBRARY 3650252884aeSStefan Eßer 365144d4804dSStefan Eßer // This function holds the secret of how I interpret a seed number for the 365244d4804dSStefan Eßer // PRNG. Well, it's actually in the development manual 365344d4804dSStefan Eßer // (manuals/development.md#pseudo-random-number-generator), so look there 365444d4804dSStefan Eßer // before you try to understand this. 365544d4804dSStefan Eßer 3656252884aeSStefan Eßer BC_SIG_LOCK; 3657252884aeSStefan Eßer 3658252884aeSStefan Eßer bc_num_init(&temp, n->len); 3659252884aeSStefan Eßer bc_num_init(&temp2, n->len); 366050696a6eSStefan Eßer bc_num_init(&frac, nrdx); 3661252884aeSStefan Eßer bc_num_init(&intn, bc_num_int(n)); 3662252884aeSStefan Eßer 3663d101cdd6SStefan Eßer BC_SETJMP_LOCKED(vm, err); 3664252884aeSStefan Eßer 3665252884aeSStefan Eßer BC_SIG_UNLOCK; 3666252884aeSStefan Eßer 3667d101cdd6SStefan Eßer assert(BC_NUM_RDX_VALID_NP(vm->max)); 3668252884aeSStefan Eßer 366978bc019dSStefan Eßer // NOLINTNEXTLINE 367050696a6eSStefan Eßer memcpy(frac.num, n->num, BC_NUM_SIZE(nrdx)); 367150696a6eSStefan Eßer frac.len = nrdx; 367250696a6eSStefan Eßer BC_NUM_RDX_SET_NP(frac, nrdx); 3673252884aeSStefan Eßer frac.scale = n->scale; 3674252884aeSStefan Eßer 367550696a6eSStefan Eßer assert(BC_NUM_RDX_VALID_NP(frac)); 3676d101cdd6SStefan Eßer assert(BC_NUM_RDX_VALID_NP(vm->max2)); 367750696a6eSStefan Eßer 367844d4804dSStefan Eßer // Multiply the fraction and truncate so that it's an integer. The 367944d4804dSStefan Eßer // truncation is what clamps it, by the way. 3680d101cdd6SStefan Eßer bc_num_mul(&frac, &vm->max2, &temp, 0); 3681252884aeSStefan Eßer bc_num_truncate(&temp, temp.scale); 3682252884aeSStefan Eßer bc_num_copy(&frac, &temp); 3683252884aeSStefan Eßer 368444d4804dSStefan Eßer // Get the integer. 368578bc019dSStefan Eßer // NOLINTNEXTLINE 368650696a6eSStefan Eßer memcpy(intn.num, n->num + nrdx, BC_NUM_SIZE(bc_num_int(n))); 3687252884aeSStefan Eßer intn.len = bc_num_int(n); 3688252884aeSStefan Eßer 3689252884aeSStefan Eßer // This assert is here because it has to be true. It is also here to justify 369044d4804dSStefan Eßer // some optimizations. 3691d101cdd6SStefan Eßer assert(BC_NUM_NONZERO(&vm->max)); 3692252884aeSStefan Eßer 369344d4804dSStefan Eßer // If there *was* a fractional part... 369478bc019dSStefan Eßer if (BC_NUM_NONZERO(&frac)) 369578bc019dSStefan Eßer { 369644d4804dSStefan Eßer // This divmod splits frac into the two state parts. 3697d101cdd6SStefan Eßer bc_num_divmod(&frac, &vm->max, &temp, &temp2, 0); 3698252884aeSStefan Eßer 3699d101cdd6SStefan Eßer // frac is guaranteed to be smaller than vm->max * vm->max (pow). 3700d101cdd6SStefan Eßer // This means that when dividing frac by vm->max, as above, the 3701d101cdd6SStefan Eßer // quotient and remainder are both guaranteed to be less than vm->max, 3702252884aeSStefan Eßer // which means we can use bc_num_bigdig2() here and not worry about 3703252884aeSStefan Eßer // overflow. 370444d4804dSStefan Eßer state1 = (BcRand) bc_num_bigdig2(&temp2); 370544d4804dSStefan Eßer state2 = (BcRand) bc_num_bigdig2(&temp); 3706252884aeSStefan Eßer } 3707252884aeSStefan Eßer else state1 = state2 = 0; 3708252884aeSStefan Eßer 370944d4804dSStefan Eßer // If there *was* an integer part... 371078bc019dSStefan Eßer if (BC_NUM_NONZERO(&intn)) 371178bc019dSStefan Eßer { 371244d4804dSStefan Eßer // This divmod splits intn into the two inc parts. 3713d101cdd6SStefan Eßer bc_num_divmod(&intn, &vm->max, &temp, &temp2, 0); 3714252884aeSStefan Eßer 3715d101cdd6SStefan Eßer // Because temp2 is the mod of vm->max, from above, it is guaranteed 3716252884aeSStefan Eßer // to be small enough to use bc_num_bigdig2(). 371744d4804dSStefan Eßer inc1 = (BcRand) bc_num_bigdig2(&temp2); 3718252884aeSStefan Eßer 371944d4804dSStefan Eßer // Clamp the second inc part. 3720d101cdd6SStefan Eßer if (bc_num_cmp(&temp, &vm->max) >= 0) 372178bc019dSStefan Eßer { 3722252884aeSStefan Eßer bc_num_copy(&temp2, &temp); 3723d101cdd6SStefan Eßer bc_num_mod(&temp2, &vm->max, &temp, 0); 3724252884aeSStefan Eßer } 3725252884aeSStefan Eßer 3726d101cdd6SStefan Eßer // The if statement above ensures that temp is less than vm->max, which 3727252884aeSStefan Eßer // means that we can use bc_num_bigdig2() here. 372844d4804dSStefan Eßer inc2 = (BcRand) bc_num_bigdig2(&temp); 3729252884aeSStefan Eßer } 3730252884aeSStefan Eßer else inc1 = inc2 = 0; 3731252884aeSStefan Eßer 3732252884aeSStefan Eßer bc_rand_seed(rng, state1, state2, inc1, inc2); 3733252884aeSStefan Eßer 3734252884aeSStefan Eßer err: 3735252884aeSStefan Eßer BC_SIG_MAYLOCK; 3736252884aeSStefan Eßer bc_num_free(&intn); 3737252884aeSStefan Eßer bc_num_free(&frac); 3738252884aeSStefan Eßer bc_num_free(&temp2); 3739252884aeSStefan Eßer bc_num_free(&temp); 3740d101cdd6SStefan Eßer BC_LONGJMP_CONT(vm); 3741252884aeSStefan Eßer } 3742252884aeSStefan Eßer 374378bc019dSStefan Eßer void 374478bc019dSStefan Eßer bc_num_createFromRNG(BcNum* restrict n, BcRNG* rng) 374578bc019dSStefan Eßer { 3746252884aeSStefan Eßer BcRand s1, s2, i1, i2; 374750696a6eSStefan Eßer BcNum conv, temp1, temp2, temp3; 3748252884aeSStefan Eßer BcDig temp1_num[BC_RAND_NUM_SIZE], temp2_num[BC_RAND_NUM_SIZE]; 3749252884aeSStefan Eßer BcDig conv_num[BC_NUM_BIGDIG_LOG10]; 3750d101cdd6SStefan Eßer #if BC_ENABLE_LIBRARY 3751d101cdd6SStefan Eßer BcVm* vm = bcl_getspecific(); 3752d101cdd6SStefan Eßer #endif // BC_ENABLE_LIBRARY 3753252884aeSStefan Eßer 3754252884aeSStefan Eßer BC_SIG_LOCK; 3755252884aeSStefan Eßer 3756252884aeSStefan Eßer bc_num_init(&temp3, 2 * BC_RAND_NUM_SIZE); 3757252884aeSStefan Eßer 3758d101cdd6SStefan Eßer BC_SETJMP_LOCKED(vm, err); 3759252884aeSStefan Eßer 3760252884aeSStefan Eßer BC_SIG_UNLOCK; 3761252884aeSStefan Eßer 3762252884aeSStefan Eßer bc_num_setup(&temp1, temp1_num, sizeof(temp1_num) / sizeof(BcDig)); 3763252884aeSStefan Eßer bc_num_setup(&temp2, temp2_num, sizeof(temp2_num) / sizeof(BcDig)); 3764252884aeSStefan Eßer bc_num_setup(&conv, conv_num, sizeof(conv_num) / sizeof(BcDig)); 3765252884aeSStefan Eßer 3766252884aeSStefan Eßer // This assert is here because it has to be true. It is also here to justify 3767d101cdd6SStefan Eßer // the assumption that vm->max is not zero. 3768d101cdd6SStefan Eßer assert(BC_NUM_NONZERO(&vm->max)); 3769252884aeSStefan Eßer 377044d4804dSStefan Eßer // Because this is true, we can just ignore math errors that would happen 377144d4804dSStefan Eßer // otherwise. 3772d101cdd6SStefan Eßer assert(BC_NUM_NONZERO(&vm->max2)); 3773252884aeSStefan Eßer 3774252884aeSStefan Eßer bc_rand_getRands(rng, &s1, &s2, &i1, &i2); 3775252884aeSStefan Eßer 377644d4804dSStefan Eßer // Put the second piece of state into a number. 3777252884aeSStefan Eßer bc_num_bigdig2num(&conv, (BcBigDig) s2); 3778252884aeSStefan Eßer 377950696a6eSStefan Eßer assert(BC_NUM_RDX_VALID_NP(conv)); 378050696a6eSStefan Eßer 378144d4804dSStefan Eßer // Multiply by max to make room for the first piece of state. 3782d101cdd6SStefan Eßer bc_num_mul(&conv, &vm->max, &temp1, 0); 3783252884aeSStefan Eßer 378444d4804dSStefan Eßer // Add in the first piece of state. 3785252884aeSStefan Eßer bc_num_bigdig2num(&conv, (BcBigDig) s1); 3786252884aeSStefan Eßer bc_num_add(&conv, &temp1, &temp2, 0); 3787252884aeSStefan Eßer 378844d4804dSStefan Eßer // Divide to make it an entirely fractional part. 3789d101cdd6SStefan Eßer bc_num_div(&temp2, &vm->max2, &temp3, BC_RAND_STATE_BITS); 3790252884aeSStefan Eßer 379144d4804dSStefan Eßer // Now start on the increment parts. It's the same process without the 379244d4804dSStefan Eßer // divide, so put the second piece of increment into a number. 3793252884aeSStefan Eßer bc_num_bigdig2num(&conv, (BcBigDig) i2); 3794252884aeSStefan Eßer 379550696a6eSStefan Eßer assert(BC_NUM_RDX_VALID_NP(conv)); 379650696a6eSStefan Eßer 379744d4804dSStefan Eßer // Multiply by max to make room for the first piece of increment. 3798d101cdd6SStefan Eßer bc_num_mul(&conv, &vm->max, &temp1, 0); 3799252884aeSStefan Eßer 380044d4804dSStefan Eßer // Add in the first piece of increment. 3801252884aeSStefan Eßer bc_num_bigdig2num(&conv, (BcBigDig) i1); 3802252884aeSStefan Eßer bc_num_add(&conv, &temp1, &temp2, 0); 3803252884aeSStefan Eßer 380444d4804dSStefan Eßer // Now add the two together. 3805252884aeSStefan Eßer bc_num_add(&temp2, &temp3, n, 0); 3806252884aeSStefan Eßer 380750696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(n)); 380850696a6eSStefan Eßer 3809252884aeSStefan Eßer err: 3810252884aeSStefan Eßer BC_SIG_MAYLOCK; 3811252884aeSStefan Eßer bc_num_free(&temp3); 3812d101cdd6SStefan Eßer BC_LONGJMP_CONT(vm); 3813252884aeSStefan Eßer } 3814252884aeSStefan Eßer 381578bc019dSStefan Eßer void 381678bc019dSStefan Eßer bc_num_irand(BcNum* restrict a, BcNum* restrict b, BcRNG* restrict rng) 381778bc019dSStefan Eßer { 381844d4804dSStefan Eßer BcNum atemp; 381976238846SStefan Eßer size_t i; 3820252884aeSStefan Eßer 3821252884aeSStefan Eßer assert(a != b); 3822252884aeSStefan Eßer 382344d4804dSStefan Eßer if (BC_ERR(BC_NUM_NEG(a))) bc_err(BC_ERR_MATH_NEGATIVE); 382444d4804dSStefan Eßer 382544d4804dSStefan Eßer // If either of these are true, then the numbers are integers. 3826252884aeSStefan Eßer if (BC_NUM_ZERO(a) || BC_NUM_ONE(a)) return; 3827252884aeSStefan Eßer 3828d101cdd6SStefan Eßer #if BC_GCC 3829d101cdd6SStefan Eßer // This is here in GCC to quiet the "maybe-uninitialized" warning. 3830d101cdd6SStefan Eßer atemp.num = NULL; 3831d101cdd6SStefan Eßer atemp.len = 0; 3832d101cdd6SStefan Eßer #endif // BC_GCC 3833d101cdd6SStefan Eßer 383444d4804dSStefan Eßer if (BC_ERR(bc_num_nonInt(a, &atemp))) bc_err(BC_ERR_MATH_NON_INTEGER); 3835252884aeSStefan Eßer 3836d101cdd6SStefan Eßer assert(atemp.num != NULL); 383744d4804dSStefan Eßer assert(atemp.len); 3838252884aeSStefan Eßer 383976238846SStefan Eßer if (atemp.len > 2) 384076238846SStefan Eßer { 384176238846SStefan Eßer size_t len; 384276238846SStefan Eßer 384376238846SStefan Eßer len = atemp.len - 2; 3844252884aeSStefan Eßer 384544d4804dSStefan Eßer // Just generate a random number for each limb. 384676238846SStefan Eßer for (i = 0; i < len; i += 2) 384778bc019dSStefan Eßer { 384876238846SStefan Eßer BcRand dig; 384976238846SStefan Eßer 385076238846SStefan Eßer dig = bc_rand_bounded(rng, BC_BASE_RAND_POW); 385176238846SStefan Eßer 385276238846SStefan Eßer b->num[i] = (BcDig) (dig % BC_BASE_POW); 385376238846SStefan Eßer b->num[i + 1] = (BcDig) (dig / BC_BASE_POW); 385476238846SStefan Eßer } 385576238846SStefan Eßer } 385676238846SStefan Eßer else 385776238846SStefan Eßer { 385876238846SStefan Eßer // We need this set. 385976238846SStefan Eßer i = 0; 386078bc019dSStefan Eßer } 3861252884aeSStefan Eßer 386276238846SStefan Eßer // This will be true if there's one full limb after the two limb groups. 386376238846SStefan Eßer if (i == atemp.len - 2) 386476238846SStefan Eßer { 386576238846SStefan Eßer // Increment this for easy use. 386676238846SStefan Eßer i += 1; 386776238846SStefan Eßer 386876238846SStefan Eßer // If the last digit is not one, we need to set a bound for it 386976238846SStefan Eßer // explicitly. Since there's still an empty limb, we need to fill that. 387076238846SStefan Eßer if (atemp.num[i] != 1) 387176238846SStefan Eßer { 387276238846SStefan Eßer BcRand dig; 387376238846SStefan Eßer BcRand bound; 387476238846SStefan Eßer 387576238846SStefan Eßer // Set the bound to the bound of the last limb times the amount 387676238846SStefan Eßer // needed to fill the second-to-last limb as well. 387776238846SStefan Eßer bound = ((BcRand) atemp.num[i]) * BC_BASE_POW; 387876238846SStefan Eßer 387976238846SStefan Eßer dig = bc_rand_bounded(rng, bound); 388076238846SStefan Eßer 388176238846SStefan Eßer // Fill the last two. 388276238846SStefan Eßer b->num[i - 1] = (BcDig) (dig % BC_BASE_POW); 388376238846SStefan Eßer b->num[i] = (BcDig) (dig / BC_BASE_POW); 388476238846SStefan Eßer 388576238846SStefan Eßer // Ensure that the length will be correct. If the last limb is zero, 388676238846SStefan Eßer // then the length needs to be one less than the bound. 388776238846SStefan Eßer b->len = atemp.len - (b->num[i] == 0); 388876238846SStefan Eßer } 388976238846SStefan Eßer // Here the last limb *is* one, which means the last limb does *not* 389076238846SStefan Eßer // need to be filled. Also, the length needs to be one less because the 389176238846SStefan Eßer // last limb is 0. 389276238846SStefan Eßer else 389376238846SStefan Eßer { 389476238846SStefan Eßer b->num[i - 1] = (BcDig) bc_rand_bounded(rng, BC_BASE_POW); 389576238846SStefan Eßer b->len = atemp.len - 1; 389676238846SStefan Eßer } 389776238846SStefan Eßer } 389876238846SStefan Eßer // Here, there is only one limb to fill. 389976238846SStefan Eßer else 390076238846SStefan Eßer { 390176238846SStefan Eßer // See above for how this works. 390278bc019dSStefan Eßer if (atemp.num[i] != 1) 390378bc019dSStefan Eßer { 390444d4804dSStefan Eßer b->num[i] = (BcDig) bc_rand_bounded(rng, (BcRand) atemp.num[i]); 390576238846SStefan Eßer b->len = atemp.len - (b->num[i] == 0); 3906252884aeSStefan Eßer } 390776238846SStefan Eßer else b->len = atemp.len - 1; 390876238846SStefan Eßer } 3909252884aeSStefan Eßer 3910252884aeSStefan Eßer bc_num_clean(b); 3911252884aeSStefan Eßer 391250696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(b)); 3913252884aeSStefan Eßer } 391444d4804dSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH 3915252884aeSStefan Eßer 391678bc019dSStefan Eßer size_t 391778bc019dSStefan Eßer bc_num_addReq(const BcNum* a, const BcNum* b, size_t scale) 391878bc019dSStefan Eßer { 3919252884aeSStefan Eßer size_t aint, bint, ardx, brdx; 3920252884aeSStefan Eßer 392144d4804dSStefan Eßer // Addition and subtraction require the max of the length of the two numbers 392244d4804dSStefan Eßer // plus 1. 392344d4804dSStefan Eßer 3924252884aeSStefan Eßer BC_UNUSED(scale); 3925252884aeSStefan Eßer 392650696a6eSStefan Eßer ardx = BC_NUM_RDX_VAL(a); 3927252884aeSStefan Eßer aint = bc_num_int(a); 3928252884aeSStefan Eßer assert(aint <= a->len && ardx <= a->len); 3929252884aeSStefan Eßer 393050696a6eSStefan Eßer brdx = BC_NUM_RDX_VAL(b); 3931252884aeSStefan Eßer bint = bc_num_int(b); 3932252884aeSStefan Eßer assert(bint <= b->len && brdx <= b->len); 3933252884aeSStefan Eßer 3934252884aeSStefan Eßer ardx = BC_MAX(ardx, brdx); 3935252884aeSStefan Eßer aint = BC_MAX(aint, bint); 3936252884aeSStefan Eßer 3937252884aeSStefan Eßer return bc_vm_growSize(bc_vm_growSize(ardx, aint), 1); 3938252884aeSStefan Eßer } 3939252884aeSStefan Eßer 394078bc019dSStefan Eßer size_t 394178bc019dSStefan Eßer bc_num_mulReq(const BcNum* a, const BcNum* b, size_t scale) 394278bc019dSStefan Eßer { 3943252884aeSStefan Eßer size_t max, rdx; 394444d4804dSStefan Eßer 394544d4804dSStefan Eßer // Multiplication requires the sum of the lengths of the numbers. 394644d4804dSStefan Eßer 394750696a6eSStefan Eßer rdx = bc_vm_growSize(BC_NUM_RDX_VAL(a), BC_NUM_RDX_VAL(b)); 394844d4804dSStefan Eßer 3949252884aeSStefan Eßer max = BC_NUM_RDX(scale); 395044d4804dSStefan Eßer 3951252884aeSStefan Eßer max = bc_vm_growSize(BC_MAX(max, rdx), 1); 3952252884aeSStefan Eßer rdx = bc_vm_growSize(bc_vm_growSize(bc_num_int(a), bc_num_int(b)), max); 395344d4804dSStefan Eßer 3954252884aeSStefan Eßer return rdx; 3955252884aeSStefan Eßer } 3956252884aeSStefan Eßer 395778bc019dSStefan Eßer size_t 395878bc019dSStefan Eßer bc_num_divReq(const BcNum* a, const BcNum* b, size_t scale) 395978bc019dSStefan Eßer { 396050696a6eSStefan Eßer size_t max, rdx; 396144d4804dSStefan Eßer 396244d4804dSStefan Eßer // Division requires the length of the dividend plus the scale. 396344d4804dSStefan Eßer 396450696a6eSStefan Eßer rdx = bc_vm_growSize(BC_NUM_RDX_VAL(a), BC_NUM_RDX_VAL(b)); 396544d4804dSStefan Eßer 396650696a6eSStefan Eßer max = BC_NUM_RDX(scale); 396744d4804dSStefan Eßer 396850696a6eSStefan Eßer max = bc_vm_growSize(BC_MAX(max, rdx), 1); 396950696a6eSStefan Eßer rdx = bc_vm_growSize(bc_num_int(a), max); 397044d4804dSStefan Eßer 397150696a6eSStefan Eßer return rdx; 397250696a6eSStefan Eßer } 397350696a6eSStefan Eßer 397478bc019dSStefan Eßer size_t 397578bc019dSStefan Eßer bc_num_powReq(const BcNum* a, const BcNum* b, size_t scale) 397678bc019dSStefan Eßer { 3977252884aeSStefan Eßer BC_UNUSED(scale); 3978252884aeSStefan Eßer return bc_vm_growSize(bc_vm_growSize(a->len, b->len), 1); 3979252884aeSStefan Eßer } 3980252884aeSStefan Eßer 3981252884aeSStefan Eßer #if BC_ENABLE_EXTRA_MATH 398278bc019dSStefan Eßer size_t 398378bc019dSStefan Eßer bc_num_placesReq(const BcNum* a, const BcNum* b, size_t scale) 398478bc019dSStefan Eßer { 3985252884aeSStefan Eßer BC_UNUSED(scale); 398650696a6eSStefan Eßer return a->len + b->len - BC_NUM_RDX_VAL(a) - BC_NUM_RDX_VAL(b); 3987252884aeSStefan Eßer } 3988252884aeSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH 3989252884aeSStefan Eßer 399078bc019dSStefan Eßer void 399178bc019dSStefan Eßer bc_num_add(BcNum* a, BcNum* b, BcNum* c, size_t scale) 399278bc019dSStefan Eßer { 399350696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(a)); 399450696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(b)); 3995252884aeSStefan Eßer bc_num_binary(a, b, c, false, bc_num_as, bc_num_addReq(a, b, scale)); 3996252884aeSStefan Eßer } 3997252884aeSStefan Eßer 399878bc019dSStefan Eßer void 399978bc019dSStefan Eßer bc_num_sub(BcNum* a, BcNum* b, BcNum* c, size_t scale) 400078bc019dSStefan Eßer { 400150696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(a)); 400250696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(b)); 4003252884aeSStefan Eßer bc_num_binary(a, b, c, true, bc_num_as, bc_num_addReq(a, b, scale)); 4004252884aeSStefan Eßer } 4005252884aeSStefan Eßer 400678bc019dSStefan Eßer void 400778bc019dSStefan Eßer bc_num_mul(BcNum* a, BcNum* b, BcNum* c, size_t scale) 400878bc019dSStefan Eßer { 400950696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(a)); 401050696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(b)); 4011252884aeSStefan Eßer bc_num_binary(a, b, c, scale, bc_num_m, bc_num_mulReq(a, b, scale)); 4012252884aeSStefan Eßer } 4013252884aeSStefan Eßer 401478bc019dSStefan Eßer void 401578bc019dSStefan Eßer bc_num_div(BcNum* a, BcNum* b, BcNum* c, size_t scale) 401678bc019dSStefan Eßer { 401750696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(a)); 401850696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(b)); 401950696a6eSStefan Eßer bc_num_binary(a, b, c, scale, bc_num_d, bc_num_divReq(a, b, scale)); 4020252884aeSStefan Eßer } 4021252884aeSStefan Eßer 402278bc019dSStefan Eßer void 402378bc019dSStefan Eßer bc_num_mod(BcNum* a, BcNum* b, BcNum* c, size_t scale) 402478bc019dSStefan Eßer { 402550696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(a)); 402650696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(b)); 402750696a6eSStefan Eßer bc_num_binary(a, b, c, scale, bc_num_rem, bc_num_divReq(a, b, scale)); 4028252884aeSStefan Eßer } 4029252884aeSStefan Eßer 403078bc019dSStefan Eßer void 403178bc019dSStefan Eßer bc_num_pow(BcNum* a, BcNum* b, BcNum* c, size_t scale) 403278bc019dSStefan Eßer { 403350696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(a)); 403450696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(b)); 4035252884aeSStefan Eßer bc_num_binary(a, b, c, scale, bc_num_p, bc_num_powReq(a, b, scale)); 4036252884aeSStefan Eßer } 4037252884aeSStefan Eßer 4038252884aeSStefan Eßer #if BC_ENABLE_EXTRA_MATH 403978bc019dSStefan Eßer void 404078bc019dSStefan Eßer bc_num_places(BcNum* a, BcNum* b, BcNum* c, size_t scale) 404178bc019dSStefan Eßer { 404250696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(a)); 404350696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(b)); 4044252884aeSStefan Eßer bc_num_binary(a, b, c, scale, bc_num_place, bc_num_placesReq(a, b, scale)); 4045252884aeSStefan Eßer } 4046252884aeSStefan Eßer 404778bc019dSStefan Eßer void 404878bc019dSStefan Eßer bc_num_lshift(BcNum* a, BcNum* b, BcNum* c, size_t scale) 404978bc019dSStefan Eßer { 405050696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(a)); 405150696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(b)); 4052252884aeSStefan Eßer bc_num_binary(a, b, c, scale, bc_num_left, bc_num_placesReq(a, b, scale)); 4053252884aeSStefan Eßer } 4054252884aeSStefan Eßer 405578bc019dSStefan Eßer void 405678bc019dSStefan Eßer bc_num_rshift(BcNum* a, BcNum* b, BcNum* c, size_t scale) 405778bc019dSStefan Eßer { 405850696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(a)); 405950696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(b)); 4060252884aeSStefan Eßer bc_num_binary(a, b, c, scale, bc_num_right, bc_num_placesReq(a, b, scale)); 4061252884aeSStefan Eßer } 4062252884aeSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH 4063252884aeSStefan Eßer 406478bc019dSStefan Eßer void 406578bc019dSStefan Eßer bc_num_sqrt(BcNum* restrict a, BcNum* restrict b, size_t scale) 406678bc019dSStefan Eßer { 406778bc019dSStefan Eßer BcNum num1, num2, half, f, fprime; 406878bc019dSStefan Eßer BcNum* x0; 406978bc019dSStefan Eßer BcNum* x1; 407078bc019dSStefan Eßer BcNum* temp; 4071d101cdd6SStefan Eßer // realscale is meant to quiet a warning on GCC about longjmp() clobbering. 4072d101cdd6SStefan Eßer // This one is real. 4073d101cdd6SStefan Eßer size_t pow, len, rdx, req, resscale, realscale; 4074252884aeSStefan Eßer BcDig half_digs[1]; 4075d101cdd6SStefan Eßer #if BC_ENABLE_LIBRARY 4076d101cdd6SStefan Eßer BcVm* vm = bcl_getspecific(); 4077d101cdd6SStefan Eßer #endif // BC_ENABLE_LIBRARY 4078252884aeSStefan Eßer 4079252884aeSStefan Eßer assert(a != NULL && b != NULL && a != b); 4080252884aeSStefan Eßer 408144d4804dSStefan Eßer if (BC_ERR(BC_NUM_NEG(a))) bc_err(BC_ERR_MATH_NEGATIVE); 4082252884aeSStefan Eßer 408344d4804dSStefan Eßer // We want to calculate to a's scale if it is bigger so that the result will 408444d4804dSStefan Eßer // truncate properly. 4085d101cdd6SStefan Eßer if (a->scale > scale) realscale = a->scale; 4086d101cdd6SStefan Eßer else realscale = scale; 4087252884aeSStefan Eßer 408844d4804dSStefan Eßer // Set parameters for the result. 4089252884aeSStefan Eßer len = bc_vm_growSize(bc_num_intDigits(a), 1); 4090d101cdd6SStefan Eßer rdx = BC_NUM_RDX(realscale); 409144d4804dSStefan Eßer 409244d4804dSStefan Eßer // Square root needs half of the length of the parameter. 409350696a6eSStefan Eßer req = bc_vm_growSize(BC_MAX(rdx, BC_NUM_RDX_VAL(a)), len >> 1); 4094252884aeSStefan Eßer 4095252884aeSStefan Eßer BC_SIG_LOCK; 4096252884aeSStefan Eßer 409744d4804dSStefan Eßer // Unlike the binary operators, this function is the only single parameter 409844d4804dSStefan Eßer // function and is expected to initialize the result. This means that it 409944d4804dSStefan Eßer // expects that b is *NOT* preallocated. We allocate it here. 4100252884aeSStefan Eßer bc_num_init(b, bc_vm_growSize(req, 1)); 4101252884aeSStefan Eßer 4102252884aeSStefan Eßer BC_SIG_UNLOCK; 4103252884aeSStefan Eßer 410450696a6eSStefan Eßer assert(a != NULL && b != NULL && a != b); 410550696a6eSStefan Eßer assert(a->num != NULL && b->num != NULL); 410650696a6eSStefan Eßer 410744d4804dSStefan Eßer // Easy case. 410878bc019dSStefan Eßer if (BC_NUM_ZERO(a)) 410978bc019dSStefan Eßer { 4110d101cdd6SStefan Eßer bc_num_setToZero(b, realscale); 4111252884aeSStefan Eßer return; 4112252884aeSStefan Eßer } 411344d4804dSStefan Eßer 411444d4804dSStefan Eßer // Another easy case. 411578bc019dSStefan Eßer if (BC_NUM_ONE(a)) 411678bc019dSStefan Eßer { 4117252884aeSStefan Eßer bc_num_one(b); 4118d101cdd6SStefan Eßer bc_num_extend(b, realscale); 4119252884aeSStefan Eßer return; 4120252884aeSStefan Eßer } 4121252884aeSStefan Eßer 412244d4804dSStefan Eßer // Set the parameters again. 4123d101cdd6SStefan Eßer rdx = BC_NUM_RDX(realscale); 412450696a6eSStefan Eßer rdx = BC_MAX(rdx, BC_NUM_RDX_VAL(a)); 4125252884aeSStefan Eßer len = bc_vm_growSize(a->len, rdx); 4126252884aeSStefan Eßer 4127252884aeSStefan Eßer BC_SIG_LOCK; 4128252884aeSStefan Eßer 4129252884aeSStefan Eßer bc_num_init(&num1, len); 4130252884aeSStefan Eßer bc_num_init(&num2, len); 4131252884aeSStefan Eßer bc_num_setup(&half, half_digs, sizeof(half_digs) / sizeof(BcDig)); 4132252884aeSStefan Eßer 413344d4804dSStefan Eßer // There is a division by two in the formula. We setup a number that's 1/2 413444d4804dSStefan Eßer // so that we can use multiplication instead of heavy division. 4135252884aeSStefan Eßer bc_num_one(&half); 4136252884aeSStefan Eßer half.num[0] = BC_BASE_POW / 2; 4137252884aeSStefan Eßer half.len = 1; 413850696a6eSStefan Eßer BC_NUM_RDX_SET_NP(half, 1); 4139252884aeSStefan Eßer half.scale = 1; 4140252884aeSStefan Eßer 4141252884aeSStefan Eßer bc_num_init(&f, len); 4142252884aeSStefan Eßer bc_num_init(&fprime, len); 4143252884aeSStefan Eßer 4144d101cdd6SStefan Eßer BC_SETJMP_LOCKED(vm, err); 4145252884aeSStefan Eßer 4146252884aeSStefan Eßer BC_SIG_UNLOCK; 4147252884aeSStefan Eßer 414844d4804dSStefan Eßer // Pointers for easy switching. 4149252884aeSStefan Eßer x0 = &num1; 4150252884aeSStefan Eßer x1 = &num2; 4151252884aeSStefan Eßer 415244d4804dSStefan Eßer // Start with 1. 4153252884aeSStefan Eßer bc_num_one(x0); 415444d4804dSStefan Eßer 415544d4804dSStefan Eßer // The power of the operand is needed for the estimate. 4156252884aeSStefan Eßer pow = bc_num_intDigits(a); 4157252884aeSStefan Eßer 415844d4804dSStefan Eßer // The code in this if statement calculates the initial estimate. First, if 415944d4804dSStefan Eßer // a is less than 0, then 0 is a good estimate. Otherwise, we want something 416044d4804dSStefan Eßer // in the same ballpark. That ballpark is pow. 416178bc019dSStefan Eßer if (pow) 416278bc019dSStefan Eßer { 416344d4804dSStefan Eßer // An odd number is served by starting with 2^((pow-1)/2), and an even 416444d4804dSStefan Eßer // number is served by starting with 6^((pow-2)/2). Why? Because math. 4165252884aeSStefan Eßer if (pow & 1) x0->num[0] = 2; 4166252884aeSStefan Eßer else x0->num[0] = 6; 4167252884aeSStefan Eßer 4168252884aeSStefan Eßer pow -= 2 - (pow & 1); 4169252884aeSStefan Eßer bc_num_shiftLeft(x0, pow / 2); 4170252884aeSStefan Eßer } 4171252884aeSStefan Eßer 417250696a6eSStefan Eßer // I can set the rdx here directly because neg should be false. 417310328f8bSStefan Eßer x0->scale = x0->rdx = 0; 4174d101cdd6SStefan Eßer resscale = (realscale + BC_BASE_DIGS) + 2; 4175252884aeSStefan Eßer 417644d4804dSStefan Eßer // This is the calculation loop. This compare goes to 0 eventually as the 417744d4804dSStefan Eßer // difference between the two numbers gets smaller than resscale. 417878bc019dSStefan Eßer while (bc_num_cmp(x1, x0)) 417978bc019dSStefan Eßer { 4180252884aeSStefan Eßer assert(BC_NUM_NONZERO(x0)); 4181252884aeSStefan Eßer 418244d4804dSStefan Eßer // This loop directly corresponds to the iteration in Newton's method. 418344d4804dSStefan Eßer // If you know the formula, this loop makes sense. Go study the formula. 418444d4804dSStefan Eßer 4185252884aeSStefan Eßer bc_num_div(a, x0, &f, resscale); 4186252884aeSStefan Eßer bc_num_add(x0, &f, &fprime, resscale); 418750696a6eSStefan Eßer 418850696a6eSStefan Eßer assert(BC_NUM_RDX_VALID_NP(fprime)); 418950696a6eSStefan Eßer assert(BC_NUM_RDX_VALID_NP(half)); 419050696a6eSStefan Eßer 4191252884aeSStefan Eßer bc_num_mul(&fprime, &half, x1, resscale); 4192252884aeSStefan Eßer 419344d4804dSStefan Eßer // Switch. 4194252884aeSStefan Eßer temp = x0; 4195252884aeSStefan Eßer x0 = x1; 4196252884aeSStefan Eßer x1 = temp; 4197252884aeSStefan Eßer } 4198252884aeSStefan Eßer 419944d4804dSStefan Eßer // Copy to the result and truncate. 4200252884aeSStefan Eßer bc_num_copy(b, x0); 4201d101cdd6SStefan Eßer if (b->scale > realscale) bc_num_truncate(b, b->scale - realscale); 4202252884aeSStefan Eßer 420350696a6eSStefan Eßer assert(!BC_NUM_NEG(b) || BC_NUM_NONZERO(b)); 420450696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(b)); 420550696a6eSStefan Eßer assert(BC_NUM_RDX_VAL(b) <= b->len || !b->len); 420650696a6eSStefan Eßer assert(!b->len || b->num[b->len - 1] || BC_NUM_RDX_VAL(b) == b->len); 4207252884aeSStefan Eßer 4208252884aeSStefan Eßer err: 4209252884aeSStefan Eßer BC_SIG_MAYLOCK; 4210252884aeSStefan Eßer bc_num_free(&fprime); 4211252884aeSStefan Eßer bc_num_free(&f); 4212252884aeSStefan Eßer bc_num_free(&num2); 4213252884aeSStefan Eßer bc_num_free(&num1); 4214d101cdd6SStefan Eßer BC_LONGJMP_CONT(vm); 4215252884aeSStefan Eßer } 4216252884aeSStefan Eßer 421778bc019dSStefan Eßer void 421878bc019dSStefan Eßer bc_num_divmod(BcNum* a, BcNum* b, BcNum* c, BcNum* d, size_t scale) 421978bc019dSStefan Eßer { 4220252884aeSStefan Eßer size_t ts, len; 422150696a6eSStefan Eßer BcNum *ptr_a, num2; 4222d101cdd6SStefan Eßer // This is volatile to quiet a warning on GCC about clobbering with 4223d101cdd6SStefan Eßer // longjmp(). 4224d101cdd6SStefan Eßer volatile bool init = false; 4225d101cdd6SStefan Eßer #if BC_ENABLE_LIBRARY 4226d101cdd6SStefan Eßer BcVm* vm = bcl_getspecific(); 4227d101cdd6SStefan Eßer #endif // BC_ENABLE_LIBRARY 4228252884aeSStefan Eßer 422944d4804dSStefan Eßer // The bulk of this function is just doing what bc_num_binary() does for the 423044d4804dSStefan Eßer // binary operators. However, it assumes that only c and a can be equal. 423144d4804dSStefan Eßer 423244d4804dSStefan Eßer // Set up the parameters. 4233252884aeSStefan Eßer ts = BC_MAX(scale + b->scale, a->scale); 4234252884aeSStefan Eßer len = bc_num_mulReq(a, b, ts); 4235252884aeSStefan Eßer 4236252884aeSStefan Eßer assert(a != NULL && b != NULL && c != NULL && d != NULL); 4237252884aeSStefan Eßer assert(c != d && a != d && b != d && b != c); 4238252884aeSStefan Eßer 423944d4804dSStefan Eßer // Initialize or expand as necessary. 424078bc019dSStefan Eßer if (c == a) 424178bc019dSStefan Eßer { 424278bc019dSStefan Eßer // NOLINTNEXTLINE 4243252884aeSStefan Eßer memcpy(&num2, c, sizeof(BcNum)); 4244252884aeSStefan Eßer ptr_a = &num2; 4245252884aeSStefan Eßer 4246252884aeSStefan Eßer BC_SIG_LOCK; 4247252884aeSStefan Eßer 4248252884aeSStefan Eßer bc_num_init(c, len); 4249252884aeSStefan Eßer 4250252884aeSStefan Eßer init = true; 4251252884aeSStefan Eßer 4252d101cdd6SStefan Eßer BC_SETJMP_LOCKED(vm, err); 4253252884aeSStefan Eßer 4254252884aeSStefan Eßer BC_SIG_UNLOCK; 4255252884aeSStefan Eßer } 425678bc019dSStefan Eßer else 425778bc019dSStefan Eßer { 4258252884aeSStefan Eßer ptr_a = a; 4259252884aeSStefan Eßer bc_num_expand(c, len); 4260252884aeSStefan Eßer } 4261252884aeSStefan Eßer 426244d4804dSStefan Eßer // Do the quick version if possible. 426378bc019dSStefan Eßer if (BC_NUM_NONZERO(a) && !BC_NUM_RDX_VAL(a) && !BC_NUM_RDX_VAL(b) && 426478bc019dSStefan Eßer b->len == 1 && !scale) 426550696a6eSStefan Eßer { 4266252884aeSStefan Eßer BcBigDig rem; 4267252884aeSStefan Eßer 4268252884aeSStefan Eßer bc_num_divArray(ptr_a, (BcBigDig) b->num[0], c, &rem); 4269252884aeSStefan Eßer 4270252884aeSStefan Eßer assert(rem < BC_BASE_POW); 4271252884aeSStefan Eßer 4272252884aeSStefan Eßer d->num[0] = (BcDig) rem; 4273252884aeSStefan Eßer d->len = (rem != 0); 4274252884aeSStefan Eßer } 427544d4804dSStefan Eßer // Do the slow method. 4276252884aeSStefan Eßer else bc_num_r(ptr_a, b, c, d, scale, ts); 4277252884aeSStefan Eßer 427850696a6eSStefan Eßer assert(!BC_NUM_NEG(c) || BC_NUM_NONZERO(c)); 427950696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(c)); 428050696a6eSStefan Eßer assert(BC_NUM_RDX_VAL(c) <= c->len || !c->len); 428150696a6eSStefan Eßer assert(!c->len || c->num[c->len - 1] || BC_NUM_RDX_VAL(c) == c->len); 428250696a6eSStefan Eßer assert(!BC_NUM_NEG(d) || BC_NUM_NONZERO(d)); 428350696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(d)); 428450696a6eSStefan Eßer assert(BC_NUM_RDX_VAL(d) <= d->len || !d->len); 428550696a6eSStefan Eßer assert(!d->len || d->num[d->len - 1] || BC_NUM_RDX_VAL(d) == d->len); 4286252884aeSStefan Eßer 4287252884aeSStefan Eßer err: 428844d4804dSStefan Eßer // Only cleanup if we initialized. 428978bc019dSStefan Eßer if (init) 429078bc019dSStefan Eßer { 4291252884aeSStefan Eßer BC_SIG_MAYLOCK; 4292252884aeSStefan Eßer bc_num_free(&num2); 4293d101cdd6SStefan Eßer BC_LONGJMP_CONT(vm); 4294252884aeSStefan Eßer } 4295252884aeSStefan Eßer } 4296252884aeSStefan Eßer 429778bc019dSStefan Eßer void 429878bc019dSStefan Eßer bc_num_modexp(BcNum* a, BcNum* b, BcNum* c, BcNum* restrict d) 429978bc019dSStefan Eßer { 430044d4804dSStefan Eßer BcNum base, exp, two, temp, atemp, btemp, ctemp; 4301252884aeSStefan Eßer BcDig two_digs[2]; 4302d101cdd6SStefan Eßer #if BC_ENABLE_LIBRARY 4303d101cdd6SStefan Eßer BcVm* vm = bcl_getspecific(); 4304d101cdd6SStefan Eßer #endif // BC_ENABLE_LIBRARY 4305252884aeSStefan Eßer 4306252884aeSStefan Eßer assert(a != NULL && b != NULL && c != NULL && d != NULL); 4307252884aeSStefan Eßer assert(a != d && b != d && c != d); 4308252884aeSStefan Eßer 430944d4804dSStefan Eßer if (BC_ERR(BC_NUM_ZERO(c))) bc_err(BC_ERR_MATH_DIVIDE_BY_ZERO); 431044d4804dSStefan Eßer if (BC_ERR(BC_NUM_NEG(b))) bc_err(BC_ERR_MATH_NEGATIVE); 431144d4804dSStefan Eßer 4312103d7cdfSStefan Eßer #if BC_DEBUG || BC_GCC 431344d4804dSStefan Eßer // This is entirely for quieting a useless scan-build error. 431444d4804dSStefan Eßer btemp.len = 0; 431544d4804dSStefan Eßer ctemp.len = 0; 4316103d7cdfSStefan Eßer #endif // BC_DEBUG || BC_GCC 431744d4804dSStefan Eßer 431844d4804dSStefan Eßer // Eliminate fractional parts that are zero or error if they are not zero. 431944d4804dSStefan Eßer if (BC_ERR(bc_num_nonInt(a, &atemp) || bc_num_nonInt(b, &btemp) || 432044d4804dSStefan Eßer bc_num_nonInt(c, &ctemp))) 432144d4804dSStefan Eßer { 432244d4804dSStefan Eßer bc_err(BC_ERR_MATH_NON_INTEGER); 432344d4804dSStefan Eßer } 432444d4804dSStefan Eßer 432544d4804dSStefan Eßer bc_num_expand(d, ctemp.len); 4326252884aeSStefan Eßer 4327252884aeSStefan Eßer BC_SIG_LOCK; 4328252884aeSStefan Eßer 432944d4804dSStefan Eßer bc_num_init(&base, ctemp.len); 4330252884aeSStefan Eßer bc_num_setup(&two, two_digs, sizeof(two_digs) / sizeof(BcDig)); 433144d4804dSStefan Eßer bc_num_init(&temp, btemp.len + 1); 433244d4804dSStefan Eßer bc_num_createCopy(&exp, &btemp); 4333252884aeSStefan Eßer 4334d101cdd6SStefan Eßer BC_SETJMP_LOCKED(vm, err); 4335252884aeSStefan Eßer 4336252884aeSStefan Eßer BC_SIG_UNLOCK; 4337252884aeSStefan Eßer 4338252884aeSStefan Eßer bc_num_one(&two); 4339252884aeSStefan Eßer two.num[0] = 2; 4340252884aeSStefan Eßer bc_num_one(d); 4341252884aeSStefan Eßer 4342252884aeSStefan Eßer // We already checked for 0. 434344d4804dSStefan Eßer bc_num_rem(&atemp, &ctemp, &base, 0); 4344252884aeSStefan Eßer 434544d4804dSStefan Eßer // If you know the algorithm I used, the memory-efficient method, then this 434644d4804dSStefan Eßer // loop should be self-explanatory because it is the calculation loop. 434778bc019dSStefan Eßer while (BC_NUM_NONZERO(&exp)) 434878bc019dSStefan Eßer { 4349252884aeSStefan Eßer // Num two cannot be 0, so no errors. 4350252884aeSStefan Eßer bc_num_divmod(&exp, &two, &exp, &temp, 0); 4351252884aeSStefan Eßer 435278bc019dSStefan Eßer if (BC_NUM_ONE(&temp) && !BC_NUM_NEG_NP(temp)) 435378bc019dSStefan Eßer { 435450696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(d)); 435550696a6eSStefan Eßer assert(BC_NUM_RDX_VALID_NP(base)); 4356252884aeSStefan Eßer 4357252884aeSStefan Eßer bc_num_mul(d, &base, &temp, 0); 4358252884aeSStefan Eßer 4359252884aeSStefan Eßer // We already checked for 0. 436044d4804dSStefan Eßer bc_num_rem(&temp, &ctemp, d, 0); 4361252884aeSStefan Eßer } 4362252884aeSStefan Eßer 436350696a6eSStefan Eßer assert(BC_NUM_RDX_VALID_NP(base)); 436450696a6eSStefan Eßer 4365252884aeSStefan Eßer bc_num_mul(&base, &base, &temp, 0); 4366252884aeSStefan Eßer 4367252884aeSStefan Eßer // We already checked for 0. 436844d4804dSStefan Eßer bc_num_rem(&temp, &ctemp, &base, 0); 4369252884aeSStefan Eßer } 4370252884aeSStefan Eßer 4371252884aeSStefan Eßer err: 4372252884aeSStefan Eßer BC_SIG_MAYLOCK; 4373252884aeSStefan Eßer bc_num_free(&exp); 4374252884aeSStefan Eßer bc_num_free(&temp); 4375252884aeSStefan Eßer bc_num_free(&base); 4376d101cdd6SStefan Eßer BC_LONGJMP_CONT(vm); 437750696a6eSStefan Eßer assert(!BC_NUM_NEG(d) || d->len); 437850696a6eSStefan Eßer assert(BC_NUM_RDX_VALID(d)); 437950696a6eSStefan Eßer assert(!d->len || d->num[d->len - 1] || BC_NUM_RDX_VAL(d) == d->len); 4380252884aeSStefan Eßer } 4381252884aeSStefan Eßer 4382252884aeSStefan Eßer #if BC_DEBUG_CODE 438378bc019dSStefan Eßer void 438478bc019dSStefan Eßer bc_num_printDebug(const BcNum* n, const char* name, bool emptyline) 438578bc019dSStefan Eßer { 4386d101cdd6SStefan Eßer bc_file_puts(&vm->fout, bc_flush_none, name); 4387d101cdd6SStefan Eßer bc_file_puts(&vm->fout, bc_flush_none, ": "); 438844d4804dSStefan Eßer bc_num_printDecimal(n, true); 4389d101cdd6SStefan Eßer bc_file_putchar(&vm->fout, bc_flush_err, '\n'); 4390d101cdd6SStefan Eßer if (emptyline) bc_file_putchar(&vm->fout, bc_flush_err, '\n'); 4391d101cdd6SStefan Eßer vm->nchars = 0; 4392252884aeSStefan Eßer } 4393252884aeSStefan Eßer 439478bc019dSStefan Eßer void 439578bc019dSStefan Eßer bc_num_printDigs(const BcDig* n, size_t len, bool emptyline) 439678bc019dSStefan Eßer { 4397252884aeSStefan Eßer size_t i; 4398252884aeSStefan Eßer 4399252884aeSStefan Eßer for (i = len - 1; i < len; --i) 440078bc019dSStefan Eßer { 4401d101cdd6SStefan Eßer bc_file_printf(&vm->fout, " %lu", (unsigned long) n[i]); 440278bc019dSStefan Eßer } 4403252884aeSStefan Eßer 4404d101cdd6SStefan Eßer bc_file_putchar(&vm->fout, bc_flush_err, '\n'); 4405d101cdd6SStefan Eßer if (emptyline) bc_file_putchar(&vm->fout, bc_flush_err, '\n'); 4406d101cdd6SStefan Eßer vm->nchars = 0; 4407252884aeSStefan Eßer } 4408252884aeSStefan Eßer 440978bc019dSStefan Eßer void 441078bc019dSStefan Eßer bc_num_printWithDigs(const BcNum* n, const char* name, bool emptyline) 441178bc019dSStefan Eßer { 4412d101cdd6SStefan Eßer bc_file_puts(&vm->fout, bc_flush_none, name); 4413d101cdd6SStefan Eßer bc_file_printf(&vm->fout, " len: %zu, rdx: %zu, scale: %zu\n", name, n->len, 441478bc019dSStefan Eßer BC_NUM_RDX_VAL(n), n->scale); 4415252884aeSStefan Eßer bc_num_printDigs(n->num, n->len, emptyline); 4416252884aeSStefan Eßer } 4417252884aeSStefan Eßer 441878bc019dSStefan Eßer void 441978bc019dSStefan Eßer bc_num_dump(const char* varname, const BcNum* n) 442078bc019dSStefan Eßer { 4421252884aeSStefan Eßer ulong i, scale = n->scale; 4422252884aeSStefan Eßer 4423d101cdd6SStefan Eßer bc_file_printf(&vm->ferr, "\n%s = %s", varname, 442450696a6eSStefan Eßer n->len ? (BC_NUM_NEG(n) ? "-" : "+") : "0 "); 4425252884aeSStefan Eßer 442678bc019dSStefan Eßer for (i = n->len - 1; i < n->len; --i) 442778bc019dSStefan Eßer { 44287e5c51e5SStefan Eßer if (i + 1 == BC_NUM_RDX_VAL(n)) 442978bc019dSStefan Eßer { 4430d101cdd6SStefan Eßer bc_file_puts(&vm->ferr, bc_flush_none, ". "); 443178bc019dSStefan Eßer } 4432252884aeSStefan Eßer 443350696a6eSStefan Eßer if (scale / BC_BASE_DIGS != BC_NUM_RDX_VAL(n) - i - 1) 443478bc019dSStefan Eßer { 4435d101cdd6SStefan Eßer bc_file_printf(&vm->ferr, "%lu ", (unsigned long) n->num[i]); 443678bc019dSStefan Eßer } 443778bc019dSStefan Eßer else 443878bc019dSStefan Eßer { 4439252884aeSStefan Eßer int mod = scale % BC_BASE_DIGS; 4440252884aeSStefan Eßer int d = BC_BASE_DIGS - mod; 4441252884aeSStefan Eßer BcDig div; 4442252884aeSStefan Eßer 444378bc019dSStefan Eßer if (mod != 0) 444478bc019dSStefan Eßer { 4445252884aeSStefan Eßer div = n->num[i] / ((BcDig) bc_num_pow10[(ulong) d]); 4446d101cdd6SStefan Eßer bc_file_printf(&vm->ferr, "%lu", (unsigned long) div); 4447252884aeSStefan Eßer } 4448252884aeSStefan Eßer 4449252884aeSStefan Eßer div = n->num[i] % ((BcDig) bc_num_pow10[(ulong) d]); 4450d101cdd6SStefan Eßer bc_file_printf(&vm->ferr, " ' %lu ", (unsigned long) div); 4451252884aeSStefan Eßer } 4452252884aeSStefan Eßer } 4453252884aeSStefan Eßer 4454d101cdd6SStefan Eßer bc_file_printf(&vm->ferr, "(%zu | %zu.%zu / %zu) %lu\n", n->scale, n->len, 445578bc019dSStefan Eßer BC_NUM_RDX_VAL(n), n->cap, (unsigned long) (void*) n->num); 44567e5c51e5SStefan Eßer 4457d101cdd6SStefan Eßer bc_file_flush(&vm->ferr, bc_flush_err); 4458252884aeSStefan Eßer } 4459252884aeSStefan Eßer #endif // BC_DEBUG_CODE 4460