blob: 870d0e2b1fcf8e41daeca1f5035eb3760ceff888 [file] [log] [blame]
Gavin Howard3eb626f2018-02-14 13:54:35 -07001/*
Gavin Howardb5904bf2018-02-20 13:28:18 -07002 * *****************************************************************************
Gavin Howard3eb626f2018-02-14 13:54:35 -07003 *
Gavin Howard7345cb92019-04-08 14:13:43 -06004 * Copyright (c) 2018-2019 Gavin D. Howard and contributors.
Gavin Howard3eb626f2018-02-14 13:54:35 -07005 *
Gavin Howard7345cb92019-04-08 14:13:43 -06006 * All rights reserved.
Gavin Howard3eb626f2018-02-14 13:54:35 -07007 *
Gavin Howard7345cb92019-04-08 14:13:43 -06008 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are met:
10 *
11 * * Redistributions of source code must retain the above copyright notice, this
12 * list of conditions and the following disclaimer.
13 *
14 * * Redistributions in binary form must reproduce the above copyright notice,
15 * this list of conditions and the following disclaimer in the documentation
16 * and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
Gavin Howard3eb626f2018-02-14 13:54:35 -070029 *
Gavin Howardb5904bf2018-02-20 13:28:18 -070030 * *****************************************************************************
Gavin Howard3eb626f2018-02-14 13:54:35 -070031 *
32 * Code for the number type.
33 *
34 */
35
36#include <assert.h>
Gavin Howardc7d655b2018-12-28 19:45:13 -070037#include <ctype.h>
Gavin Howard411f7322018-09-26 17:21:19 -060038#include <stdbool.h>
39#include <stdlib.h>
40#include <string.h>
Gavin Howard8e2cc692018-02-15 17:39:14 -070041
Gavin Howard305249a2018-10-15 20:24:47 -060042#include <limits.h>
43
Gavin Howard29493062018-03-20 19:57:37 -060044#include <status.h>
Gavin Howard3ba6c8d2018-02-15 12:23:35 -070045#include <num.h>
Gavin Howardd5551672018-09-22 19:52:42 -060046#include <vm.h>
Gavin Howard3eb626f2018-02-14 13:54:35 -070047
Gavin Howard49ea7b62019-04-23 14:46:26 -060048static BcStatus bc_num_m(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale);
49
Gavin Howard56ebcf72019-02-21 16:59:48 -070050static ssize_t bc_num_neg(size_t n, bool neg) {
51 return (((ssize_t) n) ^ -((ssize_t) neg)) + neg;
52}
53
Gavin Howardd2cf06a2019-02-21 17:03:24 -070054ssize_t bc_num_cmpZero(const BcNum *n) {
Gavin Howard56ebcf72019-02-21 16:59:48 -070055 return bc_num_neg((n)->len != 0, (n)->neg);
Gavin Howard9d91b2c2019-02-21 16:55:32 -070056}
57
Gavin Howard7b7d2f32019-02-21 16:46:47 -070058static size_t bc_num_int(const BcNum *n) {
59 return n->len ? n->len - n->rdx : 0;
60}
61
Gavin Howard798508b2019-02-21 16:45:32 -070062static void bc_num_expand(BcNum *restrict n, size_t req) {
63 assert(n);
64 req = req >= BC_NUM_DEF_SIZE ? req : BC_NUM_DEF_SIZE;
65 if (req > n->cap) {
Gavin Howard404ece72019-04-26 15:59:15 -060066 n->num = bc_vm_realloc(n->num, BC_NUM_SIZE(req));
Gavin Howard798508b2019-02-21 16:45:32 -070067 n->cap = req;
68 }
69}
70
Gavin Howard1cbfe242019-01-09 17:13:11 -070071static void bc_num_setToZero(BcNum *restrict n, size_t scale) {
Gavin Howarda2514a02018-10-18 14:20:09 -060072 assert(n);
Gavin Howardfcf29012019-04-25 20:12:19 -060073 n->scale = scale;
74 n->len = n->rdx = 0;
Gavin Howard97102b12018-11-01 23:37:31 -060075 n->neg = false;
Gavin Howardf8ddb6d2018-10-22 12:58:53 -060076}
77
Gavin Howard1cbfe242019-01-09 17:13:11 -070078static void bc_num_zero(BcNum *restrict n) {
Gavin Howardf8ddb6d2018-10-22 12:58:53 -060079 bc_num_setToZero(n, 0);
Gavin Howard0be26ed2018-08-31 20:21:56 -060080}
81
Gavin Howard3c02ed32018-12-28 18:17:15 -070082void bc_num_one(BcNum *restrict n) {
Gavin Howardf8ddb6d2018-10-22 12:58:53 -060083 bc_num_setToZero(n, 0);
Gavin Howard63738202018-09-26 15:34:20 -060084 n->len = 1;
85 n->num[0] = 1;
Gavin Howard0be26ed2018-08-31 20:21:56 -060086}
87
Gavin Howardb3f1ee22019-05-07 21:49:15 -060088static void bc_num_clean(BcNum *restrict n) {
89 while (BC_NUM_NONZERO(n) && !n->num[n->len - 1]) --n->len;
90 if (BC_NUM_ZERO(n)) n->neg = false;
91 else if (n->len < n->rdx) n->len = n->rdx;
92}
93
Gavin Howard1cbfe242019-01-09 17:13:11 -070094static size_t bc_num_log10(size_t i) {
Gavin Howard1ab22d22019-01-03 13:32:17 -070095 size_t len;
Gavin Howard530e3072019-04-23 16:23:36 -060096 for (len = 1; i; i /= BC_BASE, ++len);
Gavin Howardc849ed22019-05-13 02:01:10 -060097 assert(len - 1 <= BC_BASE_DIGS + 1);
Gavin Howardc1349922019-04-30 20:48:09 -060098 return len - 1;
Gavin Howard1ab22d22019-01-03 13:32:17 -070099}
100
Gavin Howard4007e2f2019-05-08 09:08:00 -0600101static size_t bc_num_zeroDigits(const BcDig *n) {
Gavin Howard92ba4e52019-05-10 20:49:13 -0600102 return BC_BASE_DIGS - bc_num_log10((size_t) *n);
Gavin Howard4007e2f2019-05-08 09:08:00 -0600103}
104
Gavin Howard06fee0c2019-05-09 14:43:20 -0600105static size_t bc_num_intDigits(const BcNum *n) {
Gavin Howard92ba4e52019-05-10 20:49:13 -0600106 size_t digits = bc_num_int(n) * BC_BASE_DIGS;
Gavin Howard3d3c5972019-05-15 10:43:59 -0600107 if (digits > 0) digits -= bc_num_zeroDigits(n->num + n->len - 1);
Gavin Howard445c1892019-04-29 13:50:24 -0600108 return digits;
109}
110
Gavin Howardc1349922019-04-30 20:48:09 -0600111static size_t bc_num_nonzeroLen(const BcNum *restrict n) {
Gavin Howard08f78562019-04-29 07:56:04 -0600112 size_t i, len = n->len;
113 assert(len == n->rdx);
Gavin Howard8fe174f2019-05-15 09:44:28 -0600114 for (i = len - 1; i < len && !n->num[i]; --i);
Gavin Howarda882ac22019-05-14 08:44:29 -0600115 assert(i + 1 > 0);
116 return i + 1;
Gavin Howard08f78562019-04-29 07:56:04 -0600117}
118
Gavin Howard2956a5e2019-05-08 08:04:06 -0600119static BcBigDig bc_num_addDigit(BcDig *restrict num, BcBigDig d, BcBigDig c) {
Gavin Howard14c354a2019-04-24 14:28:24 -0600120 d += c;
Gavin Howard92ba4e52019-05-10 20:49:13 -0600121 *num = (BcDig) (d % BC_BASE_POW);
122 assert(*num >= 0 && *num < BC_BASE_POW);
123 return d / BC_BASE_POW;
Gavin Howard14c354a2019-04-24 14:28:24 -0600124}
125
126static BcStatus bc_num_addArrays(BcDig *restrict a, const BcDig *restrict b,
127 size_t len)
128{
129 size_t i;
Gavin Howard2956a5e2019-05-08 08:04:06 -0600130 BcBigDig carry = 0;
Gavin Howard14c354a2019-04-24 14:28:24 -0600131
132 for (i = 0; BC_NO_SIG && i < len; ++i) {
Gavin Howard2956a5e2019-05-08 08:04:06 -0600133 BcBigDig in = ((BcBigDig) a[i]) + ((BcBigDig) b[i]);
Gavin Howard14c354a2019-04-24 14:28:24 -0600134 carry = bc_num_addDigit(a + i, in, carry);
135 }
136
137 for (; BC_NO_SIG && carry; ++i)
Gavin Howard2956a5e2019-05-08 08:04:06 -0600138 carry = bc_num_addDigit(a + i, (BcBigDig) a[i], carry);
Gavin Howard14c354a2019-04-24 14:28:24 -0600139
140 return BC_SIG ? BC_STATUS_SIGNAL : BC_STATUS_SUCCESS;
141}
142
Gavin Howarddc0ee9e2019-02-16 23:27:08 -0700143static BcStatus bc_num_subArrays(BcDig *restrict a, const BcDig *restrict b,
144 size_t len)
Gavin Howard3c02ed32018-12-28 18:17:15 -0700145{
Gavin Howard63738202018-09-26 15:34:20 -0600146 size_t i, j;
Gavin Howard14c354a2019-04-24 14:28:24 -0600147
Gavin Howard2d188a52019-02-25 14:19:08 -0700148 for (i = 0; BC_NO_SIG && i < len; ++i) {
Gavin Howard8de31d92019-05-18 18:59:57 -0600149
Gavin Howard2d188a52019-02-25 14:19:08 -0700150 for (a[i] -= b[i], j = 0; BC_NO_SIG && a[i + j] < 0;) {
Gavin Howard92ba4e52019-05-10 20:49:13 -0600151 assert(a[i + j] >= -BC_BASE_POW);
152 a[i + j++] += BC_BASE_POW;
Gavin Howard54b946a2018-10-23 12:06:57 -0600153 a[i + j] -= 1;
Gavin Howard92ba4e52019-05-10 20:49:13 -0600154 assert(a[i + j - 1] >= 0 && a[i + j - 1] < BC_BASE_POW);
Gavin Howard63738202018-09-26 15:34:20 -0600155 }
156 }
Gavin Howard14c354a2019-04-24 14:28:24 -0600157
Gavin Howard2d188a52019-02-25 14:19:08 -0700158 return BC_SIG ? BC_STATUS_SIGNAL : BC_STATUS_SUCCESS;
Gavin Howarde1e74942018-03-20 15:51:52 -0600159}
160
Gavin Howard2956a5e2019-05-08 08:04:06 -0600161static BcStatus bc_num_mulArray(const BcNum *restrict a, BcBigDig b,
Gavin Howardb3f1ee22019-05-07 21:49:15 -0600162 BcNum *restrict c)
163{
164 size_t i;
Gavin Howard2956a5e2019-05-08 08:04:06 -0600165 BcBigDig carry = 0;
Gavin Howardb3f1ee22019-05-07 21:49:15 -0600166
Gavin Howard92ba4e52019-05-10 20:49:13 -0600167 assert(b <= BC_BASE_POW);
Gavin Howardf1148822019-05-11 15:29:24 -0600168
169 if (a->len + 1 > c->cap) bc_num_expand(c, a->len + 1);
Gavin Howardb3f1ee22019-05-07 21:49:15 -0600170
171 memset(c->num, 0, BC_NUM_SIZE(c->cap));
172
173 for (i = 0; BC_NO_SIG && i < a->len; ++i) {
Gavin Howard2956a5e2019-05-08 08:04:06 -0600174 BcBigDig in = ((BcBigDig) a->num[i]) * b + carry;
Gavin Howard92ba4e52019-05-10 20:49:13 -0600175 c->num[i] = in % BC_BASE_POW;
176 carry = in / BC_BASE_POW;
Gavin Howardb3f1ee22019-05-07 21:49:15 -0600177 }
178
179 if (BC_NO_SIG) {
Gavin Howard92ba4e52019-05-10 20:49:13 -0600180 assert(carry < BC_BASE_POW);
Gavin Howarde7ae4f42019-05-08 09:08:15 -0600181 c->num[i] = (BcDig) carry;
Gavin Howardb3f1ee22019-05-07 21:49:15 -0600182 c->len = a->len;
183 c->len += (carry != 0);
184 }
185
186 return BC_SIG ? BC_STATUS_SIGNAL : BC_STATUS_SUCCESS;
187}
188
Gavin Howard2956a5e2019-05-08 08:04:06 -0600189static BcStatus bc_num_divArray(const BcNum *restrict a, BcBigDig b,
190 BcNum *restrict c, BcBigDig *rem)
Gavin Howardb3f1ee22019-05-07 21:49:15 -0600191{
192 size_t i;
Gavin Howard2956a5e2019-05-08 08:04:06 -0600193 BcBigDig carry = 0;
Gavin Howardb3f1ee22019-05-07 21:49:15 -0600194
195 assert(c->cap >= a->len);
196
197 for (i = a->len - 1; BC_NO_SIG && i < a->len; --i) {
Gavin Howard92ba4e52019-05-10 20:49:13 -0600198 BcBigDig in = ((BcBigDig) a->num[i]) + carry * BC_BASE_POW;
Gavin Howarda142c482019-05-15 13:39:31 -0600199 assert(in / b < BC_BASE_POW);
Gavin Howarde7ae4f42019-05-08 09:08:15 -0600200 c->num[i] = (BcDig) (in / b);
Gavin Howardb3f1ee22019-05-07 21:49:15 -0600201 carry = in % b;
202 }
203
204 c->len = a->len;
205 bc_num_clean(c);
206 *rem = carry;
207
208 return BC_SIG ? BC_STATUS_SIGNAL : BC_STATUS_SUCCESS;
209}
210
Gavin Howard2d188a52019-02-25 14:19:08 -0700211static ssize_t bc_num_compare(const BcDig *restrict a, const BcDig *restrict b,
212 size_t len)
Gavin Howard3c02ed32018-12-28 18:17:15 -0700213{
Gavin Howard63738202018-09-26 15:34:20 -0600214 size_t i;
Gavin Howard9a66a832019-05-13 20:20:12 -0600215 BcDig c = 0;
Gavin Howard2d188a52019-02-25 14:19:08 -0700216 for (i = len - 1; BC_NO_SIG && i < len && !(c = a[i] - b[i]); --i);
Gavin Howarda36f0312019-05-09 10:23:07 -0600217 return BC_SIG ? BC_NUM_CMP_SIGNAL : bc_num_neg(i + 1, c < 0);
Gavin Howard08bf5292018-03-20 14:59:33 -0600218}
219
Gavin Howard3c02ed32018-12-28 18:17:15 -0700220ssize_t bc_num_cmp(const BcNum *a, const BcNum *b) {
Gavin Howard4681d1b2018-03-05 19:49:33 -0700221
Gavin Howard63738202018-09-26 15:34:20 -0600222 size_t i, min, a_int, b_int, diff;
Gavin Howard8a921bd2018-10-11 14:15:32 -0600223 BcDig *max_num, *min_num;
Gavin Howard7fbb4e22018-10-18 11:43:17 -0600224 bool a_max, neg = false;
225 ssize_t cmp;
226
227 assert(a && b);
Gavin Howard4681d1b2018-03-05 19:49:33 -0700228
Gavin Howard8a921bd2018-10-11 14:15:32 -0600229 if (a == b) return 0;
Gavin Howard56ebcf72019-02-21 16:59:48 -0700230 if (BC_NUM_ZERO(a)) return bc_num_neg(b->len != 0, !b->neg);
Gavin Howardd2cf06a2019-02-21 17:03:24 -0700231 if (BC_NUM_ZERO(b)) return bc_num_cmpZero(a);
Gavin Howard890d0c02018-10-30 16:34:50 -0600232 if (a->neg) {
Gavin Howard7fbb4e22018-10-18 11:43:17 -0600233 if (b->neg) neg = true;
Gavin Howard63738202018-09-26 15:34:20 -0600234 else return -1;
235 }
236 else if (b->neg) return 1;
Gavin Howard4681d1b2018-03-05 19:49:33 -0700237
Gavin Howard7b7d2f32019-02-21 16:46:47 -0700238 a_int = bc_num_int(a);
239 b_int = bc_num_int(b);
Gavin Howard890d0c02018-10-30 16:34:50 -0600240 a_int -= b_int;
241 a_max = (a->rdx > b->rdx);
Gavin Howard4681d1b2018-03-05 19:49:33 -0700242
Gavin Howard1ab22d22019-01-03 13:32:17 -0700243 if (a_int) return (ssize_t) a_int;
Gavin Howard4681d1b2018-03-05 19:49:33 -0700244
Gavin Howard890d0c02018-10-30 16:34:50 -0600245 if (a_max) {
Gavin Howard63738202018-09-26 15:34:20 -0600246 min = b->rdx;
247 diff = a->rdx - b->rdx;
248 max_num = a->num + diff;
249 min_num = b->num;
250 }
251 else {
252 min = a->rdx;
253 diff = b->rdx - a->rdx;
254 max_num = b->num + diff;
255 min_num = a->num;
256 }
Gavin Howard4681d1b2018-03-05 19:49:33 -0700257
Gavin Howard890d0c02018-10-30 16:34:50 -0600258 cmp = bc_num_compare(max_num, min_num, b_int + min);
Gavin Howard3378af82019-05-11 08:29:39 -0600259
260#if BC_ENABLE_SIGNALS
Gavin Howarda36f0312019-05-09 10:23:07 -0600261 if (cmp == BC_NUM_CMP_SIGNAL) return cmp;
Gavin Howard3378af82019-05-11 08:29:39 -0600262#endif // BC_ENABLE_SIGNALS
263
Gavin Howard5eba0e82019-02-21 18:08:33 -0700264 if (cmp) return bc_num_neg((size_t) cmp, !a_max == !neg);
Gavin Howard021150b2018-03-10 15:40:42 -0700265
Gavin Howard2d188a52019-02-25 14:19:08 -0700266 for (max_num -= diff, i = diff - 1; BC_NO_SIG && i < diff; --i) {
Gavin Howard5eba0e82019-02-21 18:08:33 -0700267 if (max_num[i]) return bc_num_neg(1, !a_max == !neg);
Gavin Howard63738202018-09-26 15:34:20 -0600268 }
Gavin Howard6fbdb292018-02-27 15:44:48 -0700269
Gavin Howarda36f0312019-05-09 10:23:07 -0600270 return BC_SIG ? BC_NUM_CMP_SIGNAL : 0;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700271}
272
Gavin Howard3c02ed32018-12-28 18:17:15 -0700273void bc_num_truncate(BcNum *restrict n, size_t places) {
Gavin Howard6fbdb292018-02-27 15:44:48 -0700274
Gavin Howardb1558662019-04-26 14:32:55 -0600275 size_t places_rdx;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700276
Stefan Esser70f11852019-04-27 10:39:00 +0200277 if (!places) return;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700278
Gavin Howardb1558662019-04-26 14:32:55 -0600279 places_rdx = n->rdx - BC_NUM_RDX(n->scale - places);
280 assert(places <= n->scale && (BC_NUM_ZERO(n) || places_rdx <= n->len));
281
282 n->scale -= places;
283 n->rdx -= places_rdx;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700284
Gavin Howardddce6d42019-01-03 14:07:06 -0700285 if (BC_NUM_NONZERO(n)) {
Gavin Howardb1558662019-04-26 14:32:55 -0600286
Gavin Howardc9bef2d2019-04-26 14:46:52 -0600287 size_t pow;
288
Gavin Howard92ba4e52019-05-10 20:49:13 -0600289 pow = n->scale % BC_BASE_DIGS;
290 pow = pow ? BC_BASE_DIGS - pow : 0;
Gavin Howardc1542802019-05-08 17:20:51 -0600291 pow = bc_num_pow10[pow];
Gavin Howardb1558662019-04-26 14:32:55 -0600292
293 n->len -= places_rdx;
Gavin Howard404ece72019-04-26 15:59:15 -0600294 memmove(n->num, n->num + places_rdx, BC_NUM_SIZE(n->len));
Gavin Howardb1558662019-04-26 14:32:55 -0600295
296 // Clear the lower part of the last digit.
Gavin Howardda6b5432019-04-26 14:33:43 -0600297 if (BC_NUM_NONZERO(n)) n->num[0] -= n->num[0] % (BcDig) pow;
Gavin Howardb1558662019-04-26 14:32:55 -0600298
Gavin Howard2a366922019-01-16 10:50:20 -0700299 bc_num_clean(n);
Gavin Howard955da852018-10-23 11:08:20 -0600300 }
Gavin Howard6fbdb292018-02-27 15:44:48 -0700301}
302
Stefan Esser70f11852019-04-27 10:39:00 +0200303static void bc_num_extend(BcNum *restrict n, size_t places) {
Gavin Howard6fbdb292018-02-27 15:44:48 -0700304
Gavin Howardb5e6da92019-04-30 19:58:04 -0600305 size_t places_rdx;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700306
Gavin Howard1ab22d22019-01-03 13:32:17 -0700307 if (!places) return;
Gavin Howardfea7e312019-05-14 11:50:43 -0600308 if (BC_NUM_ZERO(n)) {
309 n->scale += places;
310 return;
311 }
Gavin Howard6fbdb292018-02-27 15:44:48 -0700312
Gavin Howardb5e6da92019-04-30 19:58:04 -0600313 places_rdx = BC_NUM_RDX(places + n->scale) - n->rdx;
314
Gavin Howard8721e332019-05-06 08:02:49 -0600315 if (places_rdx) {
316 bc_num_expand(n, bc_vm_growSize(n->len, places_rdx));
317 memmove(n->num + places_rdx, n->num, BC_NUM_SIZE(n->len));
318 memset(n->num, 0, BC_NUM_SIZE(places_rdx));
319 }
Gavin Howardb5e6da92019-04-30 19:58:04 -0600320
Gavin Howarde6f326e2019-04-26 10:13:45 -0600321 n->rdx += places_rdx;
322 n->scale += places;
Gavin Howardb1558662019-04-26 14:32:55 -0600323 n->len += places_rdx;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700324
Gavin Howarde6f326e2019-04-26 10:13:45 -0600325 assert(n->rdx == BC_NUM_RDX(n->scale));
Stefan Esser193f8522019-04-27 01:29:01 +0200326}
327
Gavin Howard2d188a52019-02-25 14:19:08 -0700328static void bc_num_retireMul(BcNum *restrict n, size_t scale,
329 bool neg1, bool neg2)
330{
Gavin Howard971a2672019-04-26 14:32:07 -0600331 if (n->scale < scale) bc_num_extend(n, scale - n->scale);
332 else bc_num_truncate(n, n->scale - scale);
Gavin Howard337fe602018-09-01 15:10:59 -0600333
Gavin Howard305249a2018-10-15 20:24:47 -0600334 bc_num_clean(n);
Gavin Howardddce6d42019-01-03 14:07:06 -0700335 if (BC_NUM_NONZERO(n)) n->neg = (!neg1 != !neg2);
Gavin Howard305249a2018-10-15 20:24:47 -0600336}
337
Gavin Howard2d188a52019-02-25 14:19:08 -0700338static void bc_num_split(const BcNum *restrict n, size_t idx,
339 BcNum *restrict a, BcNum *restrict b)
340{
Gavin Howard305249a2018-10-15 20:24:47 -0600341 if (idx < n->len) {
342
Gavin Howard305249a2018-10-15 20:24:47 -0600343 b->len = n->len - idx;
344 a->len = idx;
Gavin Howarda3f260c2019-04-26 15:47:25 -0600345 a->scale = a->rdx = b->scale = b->rdx = 0;
Gavin Howard305249a2018-10-15 20:24:47 -0600346
Gavin Howard404ece72019-04-26 15:59:15 -0600347 memcpy(b->num, n->num + idx, BC_NUM_SIZE(b->len));
348 memcpy(a->num, n->num, BC_NUM_SIZE(idx));
Gavin Howard50c80022019-01-03 15:14:33 -0700349
350 bc_num_clean(b);
Gavin Howard305249a2018-10-15 20:24:47 -0600351 }
Gavin Howard50c80022019-01-03 15:14:33 -0700352 else bc_num_copy(a, n);
Gavin Howard305249a2018-10-15 20:24:47 -0600353
354 bc_num_clean(a);
Gavin Howard305249a2018-10-15 20:24:47 -0600355}
356
Gavin Howard14913fc2019-04-23 14:36:03 -0600357static size_t bc_num_shiftZero(BcNum *restrict n) {
358
359 size_t i;
360
361 assert(!n->rdx || BC_NUM_ZERO(n));
362
363 for (i = 0; i < n->len && !n->num[i]; ++i);
364
365 n->len -= i;
366 n->num += i;
367
368 return i;
369}
370
Gavin Howard2e736de2019-04-27 06:41:58 -0600371static void bc_num_unshiftZero(BcNum *restrict n, size_t places_rdx) {
372 n->len += places_rdx;
373 n->num -= places_rdx;
Gavin Howard14913fc2019-04-23 14:36:03 -0600374}
375
Gavin Howard2956a5e2019-05-08 08:04:06 -0600376static BcStatus bc_num_shift(BcNum *restrict n, BcBigDig dig) {
Gavin Howardbe884592019-04-29 15:07:49 -0600377
378 size_t i, len = n->len;
Gavin Howard2956a5e2019-05-08 08:04:06 -0600379 BcBigDig carry = 0, pow;
Gavin Howardbe884592019-04-29 15:07:49 -0600380 BcDig *ptr = n->num;
Gavin Howard305249a2018-10-15 20:24:47 -0600381
Gavin Howard92ba4e52019-05-10 20:49:13 -0600382 assert(dig < BC_BASE_DIGS);
Gavin Howard305249a2018-10-15 20:24:47 -0600383
Gavin Howardc1542802019-05-08 17:20:51 -0600384 pow = bc_num_pow10[dig];
Gavin Howard92ba4e52019-05-10 20:49:13 -0600385 dig = bc_num_pow10[BC_BASE_DIGS - dig];
Gavin Howardb1558662019-04-26 14:32:55 -0600386
Gavin Howard834fde12019-04-30 08:00:05 -0600387 for (i = len - 1; BC_NO_SIG && i < len; --i) {
Gavin Howard2956a5e2019-05-08 08:04:06 -0600388 BcBigDig in, temp;
389 in = ((BcBigDig) ptr[i]);
Gavin Howard834fde12019-04-30 08:00:05 -0600390 temp = carry * dig;
391 carry = in % pow;
392 ptr[i] = ((BcDig) (in / pow)) + (BcDig) temp;
Gavin Howard305249a2018-10-15 20:24:47 -0600393 }
394
Gavin Howard834fde12019-04-30 08:00:05 -0600395 assert(!carry);
Gavin Howardb1558662019-04-26 14:32:55 -0600396
397 return BC_SIG ? BC_STATUS_SIGNAL : BC_STATUS_SUCCESS;
Gavin Howard337fe602018-09-01 15:10:59 -0600398}
399
Gavin Howardb1558662019-04-26 14:32:55 -0600400static BcStatus bc_num_shiftLeft(BcNum *restrict n, size_t places) {
Gavin Howard7ad5a662019-02-19 14:40:46 -0700401
Gavin Howardb1558662019-04-26 14:32:55 -0600402 BcStatus s = BC_STATUS_SUCCESS;
Gavin Howard2956a5e2019-05-08 08:04:06 -0600403 BcBigDig dig;
Gavin Howard48a0e472019-04-26 15:39:46 -0600404 size_t places_rdx;
Gavin Howardbe884592019-04-29 15:07:49 -0600405 bool shift;
Gavin Howard14913fc2019-04-23 14:36:03 -0600406
Gavin Howardb1558662019-04-26 14:32:55 -0600407 if (!places) return s;
Gavin Howardabe4fdf2019-05-06 08:39:53 -0600408 if (places > n->scale) {
409 size_t size = bc_vm_growSize(BC_NUM_RDX(places - n->scale), n->len);
410 if (size > SIZE_MAX - 1) return bc_vm_err(BC_ERROR_MATH_OVERFLOW);
411 }
Gavin Howard4631f3a2019-04-30 10:48:40 -0600412 if (BC_NUM_ZERO(n)) {
413 if (n->scale >= places) n->scale -= places;
414 else n->scale = 0;
415 return s;
416 }
Gavin Howard14913fc2019-04-23 14:36:03 -0600417
Gavin Howard92ba4e52019-05-10 20:49:13 -0600418 dig = (BcBigDig) (places % BC_BASE_DIGS);
Gavin Howard48a0e472019-04-26 15:39:46 -0600419 shift = (dig != 0);
Gavin Howard22253c72019-05-06 08:03:57 -0600420 places_rdx = BC_NUM_RDX(places);
Gavin Howard48a0e472019-04-26 15:39:46 -0600421
Gavin Howard22253c72019-05-06 08:03:57 -0600422 if (n->scale) {
Gavin Howarda1879192019-04-30 18:55:08 -0600423
424 if (n->rdx >= places_rdx) {
425
Gavin Howard92ba4e52019-05-10 20:49:13 -0600426 size_t mod = n->scale % BC_BASE_DIGS, revdig;
Gavin Howarda1879192019-04-30 18:55:08 -0600427
Gavin Howard92ba4e52019-05-10 20:49:13 -0600428 mod = mod ? mod : BC_BASE_DIGS;
429 revdig = dig ? BC_BASE_DIGS - dig : 0;
Gavin Howarda1879192019-04-30 18:55:08 -0600430
Gavin Howard92ba4e52019-05-10 20:49:13 -0600431 if (mod + revdig > BC_BASE_DIGS) places_rdx = 1;
Gavin Howarda1879192019-04-30 18:55:08 -0600432 else places_rdx = 0;
433 }
434 else places_rdx -= n->rdx;
435 }
Gavin Howard4631f3a2019-04-30 10:48:40 -0600436
437 if (places_rdx) {
Gavin Howarde396dff2019-05-01 07:28:36 -0600438 bc_num_expand(n, bc_vm_growSize(n->len, places_rdx));
Gavin Howard834fde12019-04-30 08:00:05 -0600439 memmove(n->num + places_rdx, n->num, BC_NUM_SIZE(n->len));
440 memset(n->num, 0, BC_NUM_SIZE(places_rdx));
441 n->len += places_rdx;
Gavin Howardb1558662019-04-26 14:32:55 -0600442 }
Gavin Howard4631f3a2019-04-30 10:48:40 -0600443
444 if (places > n->scale) n->scale = n->rdx = 0;
445 else {
446 n->scale -= places;
447 n->rdx = BC_NUM_RDX(n->scale);
448 }
Gavin Howardb1558662019-04-26 14:32:55 -0600449
Gavin Howard92ba4e52019-05-10 20:49:13 -0600450 if (shift) s = bc_num_shift(n, BC_BASE_DIGS - dig);
Gavin Howardb1558662019-04-26 14:32:55 -0600451
452 bc_num_clean(n);
453
454 return BC_SIG && !s ? BC_STATUS_SIGNAL : s;
455}
456
457static BcStatus bc_num_shiftRight(BcNum *restrict n, size_t places) {
458
459 BcStatus s = BC_STATUS_SUCCESS;
Gavin Howard2956a5e2019-05-08 08:04:06 -0600460 BcBigDig dig;
Gavin Howardb5e6da92019-04-30 19:58:04 -0600461 size_t places_rdx, scale, scale_mod, int_len, expand;
462 bool shift;
Gavin Howardb1558662019-04-26 14:32:55 -0600463
464 if (!places) return s;
Gavin Howard14913fc2019-04-23 14:36:03 -0600465 if (BC_NUM_ZERO(n)) {
Gavin Howardb1558662019-04-26 14:32:55 -0600466 n->scale += places;
467 bc_num_expand(n, BC_NUM_RDX(n->scale));
468 return s;
Gavin Howard14913fc2019-04-23 14:36:03 -0600469 }
470
Gavin Howard92ba4e52019-05-10 20:49:13 -0600471 dig = (BcBigDig) (places % BC_BASE_DIGS);
Gavin Howardb5e6da92019-04-30 19:58:04 -0600472 shift = (dig != 0);
Gavin Howard834fde12019-04-30 08:00:05 -0600473 scale = n->scale;
Gavin Howard92ba4e52019-05-10 20:49:13 -0600474 scale_mod = scale % BC_BASE_DIGS;
475 scale_mod = scale_mod ? scale_mod : BC_BASE_DIGS;
Gavin Howard834fde12019-04-30 08:00:05 -0600476 int_len = bc_num_int(n);
Gavin Howardb1558662019-04-26 14:32:55 -0600477 places_rdx = BC_NUM_RDX(places);
Gavin Howard7ad5a662019-02-19 14:40:46 -0700478
Gavin Howard92ba4e52019-05-10 20:49:13 -0600479 if (scale_mod + dig > BC_BASE_DIGS) {
Gavin Howardb5e6da92019-04-30 19:58:04 -0600480 expand = places_rdx - 1;
481 places_rdx = 1;
Gavin Howard834fde12019-04-30 08:00:05 -0600482 }
483 else {
Gavin Howardb5e6da92019-04-30 19:58:04 -0600484 expand = places_rdx;
485 places_rdx = 0;
Gavin Howard7ad5a662019-02-19 14:40:46 -0700486 }
487
Gavin Howardb5e6da92019-04-30 19:58:04 -0600488 if (expand > int_len) expand -= int_len;
489 else expand = 0;
490
Gavin Howard92ba4e52019-05-10 20:49:13 -0600491 bc_num_extend(n, places_rdx * BC_BASE_DIGS);
Gavin Howarde396dff2019-05-01 07:28:36 -0600492 bc_num_expand(n, bc_vm_growSize(expand, n->len));
Gavin Howardb5e6da92019-04-30 19:58:04 -0600493 memset(n->num + n->len, 0, BC_NUM_SIZE(expand));
494 n->len += expand;
Gavin Howard834fde12019-04-30 08:00:05 -0600495 n->scale = n->rdx = 0;
Gavin Howardb1558662019-04-26 14:32:55 -0600496
Gavin Howard834fde12019-04-30 08:00:05 -0600497 if (shift) s = bc_num_shift(n, dig);
498
499 n->scale = scale + places;
500 n->rdx = BC_NUM_RDX(n->scale);
501
502 bc_num_clean(n);
Gavin Howard7ad5a662019-02-19 14:40:46 -0700503
504 assert(n->rdx <= n->len && n->len <= n->cap);
Gavin Howardb1558662019-04-26 14:32:55 -0600505 assert(n->rdx == BC_NUM_RDX(n->scale));
506
507 return BC_SIG && !s ? BC_STATUS_SIGNAL : s;
Gavin Howard7ad5a662019-02-19 14:40:46 -0700508}
Gavin Howard7ad5a662019-02-19 14:40:46 -0700509
Gavin Howard1cbfe242019-01-09 17:13:11 -0700510static BcStatus bc_num_inv(BcNum *a, BcNum *b, size_t scale) {
Gavin Howard9c4358c2018-03-22 20:11:28 -0600511
Gavin Howard63738202018-09-26 15:34:20 -0600512 BcNum one;
Gavin Howardd32d7df2018-10-15 08:42:25 -0600513 BcDig num[2];
Gavin Howard9c4358c2018-03-22 20:11:28 -0600514
Gavin Howard971a2672019-04-26 14:32:07 -0600515 assert(BC_NUM_NONZERO(a));
Gavin Howard1769abc2019-02-21 09:36:14 -0700516
Gavin Howardd0b05f52019-05-15 10:20:54 -0600517 bc_num_setup(&one, num, sizeof(num) / sizeof(BcDig));
Gavin Howard63738202018-09-26 15:34:20 -0600518 bc_num_one(&one);
Gavin Howard9c4358c2018-03-22 20:11:28 -0600519
Gavin Howardd32d7df2018-10-15 08:42:25 -0600520 return bc_num_div(&one, a, b, scale);
Gavin Howard9c4358c2018-03-22 20:11:28 -0600521}
522
Gavin Howard7bda4782018-12-28 09:53:22 -0700523#if BC_ENABLE_EXTRA_MATH
Gavin Howard1cbfe242019-01-09 17:13:11 -0700524static BcStatus bc_num_intop(const BcNum *a, const BcNum *b, BcNum *restrict c,
Gavin Howard2956a5e2019-05-08 08:04:06 -0600525 BcBigDig *v)
Gavin Howard3c02ed32018-12-28 18:17:15 -0700526{
Gavin Howardecafd4f2019-02-23 09:30:45 -0700527 if (BC_ERR(b->rdx)) return bc_vm_err(BC_ERROR_MATH_NON_INTEGER);
Gavin Howardac4d9122018-12-27 23:59:12 -0700528 bc_num_copy(c, a);
Gavin Howard2956a5e2019-05-08 08:04:06 -0600529 return bc_num_bigdig(b, v);
Gavin Howardac4d9122018-12-27 23:59:12 -0700530}
Gavin Howard7bda4782018-12-28 09:53:22 -0700531#endif // BC_ENABLE_EXTRA_MATH
Gavin Howardac4d9122018-12-27 23:59:12 -0700532
Gavin Howard1cbfe242019-01-09 17:13:11 -0700533static BcStatus bc_num_a(BcNum *a, BcNum *b, BcNum *restrict c, size_t sub) {
Gavin Howard6fbdb292018-02-27 15:44:48 -0700534
Gavin Howard8a921bd2018-10-11 14:15:32 -0600535 BcDig *ptr, *ptr_a, *ptr_b, *ptr_c;
Gavin Howard63738202018-09-26 15:34:20 -0600536 size_t i, max, min_rdx, min_int, diff, a_int, b_int;
Gavin Howard2956a5e2019-05-08 08:04:06 -0600537 BcBigDig carry;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700538
Gavin Howard63738202018-09-26 15:34:20 -0600539 // Because this function doesn't need to use scale (per the bc spec),
540 // I am hijacking it to say whether it's doing an add or a subtract.
Gavin Howard73cce1a2018-09-06 15:16:09 -0600541
Gavin Howardddce6d42019-01-03 14:07:06 -0700542 if (BC_NUM_ZERO(a)) {
Gavin Howardad0ecfe2018-10-30 01:16:01 -0600543 bc_num_copy(c, b);
Gavin Howardddce6d42019-01-03 14:07:06 -0700544 if (sub && BC_NUM_NONZERO(c)) c->neg = !c->neg;
Gavin Howardad0ecfe2018-10-30 01:16:01 -0600545 return BC_STATUS_SUCCESS;
Gavin Howard63738202018-09-26 15:34:20 -0600546 }
Gavin Howardddce6d42019-01-03 14:07:06 -0700547 if (BC_NUM_ZERO(b)) {
Gavin Howardad0ecfe2018-10-30 01:16:01 -0600548 bc_num_copy(c, a);
549 return BC_STATUS_SUCCESS;
550 }
Gavin Howardf6964a12018-03-14 10:52:31 -0600551
Gavin Howard63738202018-09-26 15:34:20 -0600552 c->neg = a->neg;
Gavin Howard63738202018-09-26 15:34:20 -0600553 c->rdx = BC_MAX(a->rdx, b->rdx);
Gavin Howardfba50a22019-04-26 10:07:21 -0600554 c->scale = BC_MAX(a->scale, b->scale);
Gavin Howard63738202018-09-26 15:34:20 -0600555 min_rdx = BC_MIN(a->rdx, b->rdx);
Gavin Howard6fbdb292018-02-27 15:44:48 -0700556
Gavin Howard63738202018-09-26 15:34:20 -0600557 if (a->rdx > b->rdx) {
558 diff = a->rdx - b->rdx;
559 ptr = a->num;
560 ptr_a = a->num + diff;
561 ptr_b = b->num;
562 }
563 else {
564 diff = b->rdx - a->rdx;
565 ptr = b->num;
566 ptr_a = a->num;
567 ptr_b = b->num + diff;
568 }
Gavin Howard6fbdb292018-02-27 15:44:48 -0700569
Gavin Howardcafcd3e2019-01-22 09:54:24 -0700570 for (ptr_c = c->num, i = 0; i < diff; ++i) ptr_c[i] = ptr[i];
Gavin Howard6fbdb292018-02-27 15:44:48 -0700571
Gavin Howardcafcd3e2019-01-22 09:54:24 -0700572 c->len = diff;
Gavin Howard63738202018-09-26 15:34:20 -0600573 ptr_c += diff;
Gavin Howard7b7d2f32019-02-21 16:46:47 -0700574 a_int = bc_num_int(a);
575 b_int = bc_num_int(b);
Gavin Howard6fbdb292018-02-27 15:44:48 -0700576
Gavin Howard890d0c02018-10-30 16:34:50 -0600577 if (a_int > b_int) {
Gavin Howard63738202018-09-26 15:34:20 -0600578 min_int = b_int;
579 max = a_int;
580 ptr = ptr_a;
581 }
582 else {
583 min_int = a_int;
584 max = b_int;
585 ptr = ptr_b;
586 }
Gavin Howard6fbdb292018-02-27 15:44:48 -0700587
Gavin Howard2d188a52019-02-25 14:19:08 -0700588 for (carry = 0, i = 0; BC_NO_SIG && i < min_rdx + min_int; ++i) {
Gavin Howard2956a5e2019-05-08 08:04:06 -0600589 BcBigDig in = ((BcBigDig) ptr_a[i]) + ((BcBigDig) ptr_b[i]);
Gavin Howarddd7a0fd2019-01-22 09:56:46 -0700590 carry = bc_num_addDigit(ptr_c + i, in, carry);
Gavin Howard63738202018-09-26 15:34:20 -0600591 }
Gavin Howard6fbdb292018-02-27 15:44:48 -0700592
Gavin Howard2d188a52019-02-25 14:19:08 -0700593 for (; BC_NO_SIG && i < max + min_rdx; ++i)
Gavin Howard2956a5e2019-05-08 08:04:06 -0600594 carry = bc_num_addDigit(ptr_c + i, (BcBigDig) ptr[i], carry);
Gavin Howardcafcd3e2019-01-22 09:54:24 -0700595
596 c->len += i;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700597
Gavin Howard1ab22d22019-01-03 13:32:17 -0700598 if (carry) c->num[c->len++] = (BcDig) carry;
Gavin Howard2ea7dc42018-05-22 14:02:02 -0600599
Gavin Howard2d188a52019-02-25 14:19:08 -0700600 return BC_SIG ? BC_STATUS_SIGNAL : BC_STATUS_SUCCESS;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700601}
602
Gavin Howard1cbfe242019-01-09 17:13:11 -0700603static BcStatus bc_num_s(BcNum *a, BcNum *b, BcNum *restrict c, size_t sub) {
Gavin Howard6fbdb292018-02-27 15:44:48 -0700604
Gavin Howarddc0ee9e2019-02-16 23:27:08 -0700605 BcStatus s;
Gavin Howard63738202018-09-26 15:34:20 -0600606 ssize_t cmp;
607 BcNum *minuend, *subtrahend;
608 size_t start;
609 bool aneg, bneg, neg;
Gavin Howarda1c090a2018-03-05 14:20:33 -0700610
Gavin Howard63738202018-09-26 15:34:20 -0600611 // Because this function doesn't need to use scale (per the bc spec),
612 // I am hijacking it to say whether it's doing an add or a subtract.
Gavin Howard8de31d92019-05-18 18:59:57 -0600613
Gavin Howardddce6d42019-01-03 14:07:06 -0700614 if (BC_NUM_ZERO(a)) {
Gavin Howardad0ecfe2018-10-30 01:16:01 -0600615 bc_num_copy(c, b);
Gavin Howardddce6d42019-01-03 14:07:06 -0700616 if (sub && BC_NUM_NONZERO(c)) c->neg = !c->neg;
Gavin Howardad0ecfe2018-10-30 01:16:01 -0600617 return BC_STATUS_SUCCESS;
Gavin Howard63738202018-09-26 15:34:20 -0600618 }
Gavin Howardddce6d42019-01-03 14:07:06 -0700619 if (BC_NUM_ZERO(b)) {
Gavin Howardad0ecfe2018-10-30 01:16:01 -0600620 bc_num_copy(c, a);
621 return BC_STATUS_SUCCESS;
622 }
Gavin Howard6fbdb292018-02-27 15:44:48 -0700623
Gavin Howard63738202018-09-26 15:34:20 -0600624 aneg = a->neg;
625 bneg = b->neg;
626 a->neg = b->neg = false;
Gavin Howard9a6f7a42018-03-05 14:07:06 -0700627
Gavin Howard63738202018-09-26 15:34:20 -0600628 cmp = bc_num_cmp(a, b);
Gavin Howard3378af82019-05-11 08:29:39 -0600629
630#if BC_ENABLE_SIGNALS
Gavin Howarda36f0312019-05-09 10:23:07 -0600631 if (cmp == BC_NUM_CMP_SIGNAL) return BC_STATUS_SIGNAL;
Gavin Howard3378af82019-05-11 08:29:39 -0600632#endif // BC_ENABLE_SIGNALS
Gavin Howard9a6f7a42018-03-05 14:07:06 -0700633
Gavin Howard63738202018-09-26 15:34:20 -0600634 a->neg = aneg;
635 b->neg = bneg;
Gavin Howard9a6f7a42018-03-05 14:07:06 -0700636
Gavin Howard1ab22d22019-01-03 13:32:17 -0700637 if (!cmp) {
Gavin Howardf8ddb6d2018-10-22 12:58:53 -0600638 bc_num_setToZero(c, BC_MAX(a->rdx, b->rdx));
Gavin Howard63738202018-09-26 15:34:20 -0600639 return BC_STATUS_SUCCESS;
640 }
Gavin Howardddce6d42019-01-03 14:07:06 -0700641
642 if (cmp > 0) {
Gavin Howard63738202018-09-26 15:34:20 -0600643 neg = a->neg;
644 minuend = a;
645 subtrahend = b;
646 }
647 else {
648 neg = b->neg;
649 if (sub) neg = !neg;
650 minuend = b;
651 subtrahend = a;
652 }
Gavin Howard9a6f7a42018-03-05 14:07:06 -0700653
Gavin Howardad0ecfe2018-10-30 01:16:01 -0600654 bc_num_copy(c, minuend);
Gavin Howard63738202018-09-26 15:34:20 -0600655 c->neg = neg;
Gavin Howard9a6f7a42018-03-05 14:07:06 -0700656
Gavin Howarde6f326e2019-04-26 10:13:45 -0600657 if (c->scale < subtrahend->scale) {
658 bc_num_extend(c, subtrahend->scale - c->scale);
Gavin Howard63738202018-09-26 15:34:20 -0600659 start = 0;
660 }
661 else start = c->rdx - subtrahend->rdx;
Gavin Howard9a6f7a42018-03-05 14:07:06 -0700662
Gavin Howarddc0ee9e2019-02-16 23:27:08 -0700663 s = bc_num_subArrays(c->num + start, subtrahend->num, subtrahend->len);
Gavin Howardfa4983a2019-02-16 23:43:03 -0700664
Gavin Howard63738202018-09-26 15:34:20 -0600665 bc_num_clean(c);
Gavin Howard9a6f7a42018-03-05 14:07:06 -0700666
Gavin Howarddc0ee9e2019-02-16 23:27:08 -0700667 return s;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700668}
669
Gavin Howarddc4e3712019-04-23 14:56:29 -0600670static BcStatus bc_num_m_simp(const BcNum *a, const BcNum *b, BcNum *restrict c)
671{
Gavin Howard971a2672019-04-26 14:32:07 -0600672 size_t i, alen = a->len, blen = b->len, clen;
Gavin Howard5083aec2019-04-23 18:03:46 -0600673 BcDig *ptr_a = a->num, *ptr_b = b->num, *ptr_c;
Gavin Howard2956a5e2019-05-08 08:04:06 -0600674 BcBigDig sum = 0, carry = 0;
Gavin Howarddc4e3712019-04-23 14:56:29 -0600675
Gavin Howard971a2672019-04-26 14:32:07 -0600676 assert(sizeof(sum) >= sizeof(BcDig) * 2);
Gavin Howardb538d802019-04-23 16:50:55 -0600677 assert(!a->rdx && !b->rdx);
678
Gavin Howard5083aec2019-04-23 18:03:46 -0600679 clen = bc_vm_growSize(alen, blen);
Gavin Howarde396dff2019-05-01 07:28:36 -0600680 bc_num_expand(c, bc_vm_growSize(clen, 1));
Gavin Howarddc4e3712019-04-23 14:56:29 -0600681
682 ptr_c = c->num;
Gavin Howard404ece72019-04-26 15:59:15 -0600683 memset(ptr_c, 0, BC_NUM_SIZE(c->cap));
Gavin Howarddc4e3712019-04-23 14:56:29 -0600684
Gavin Howard5083aec2019-04-23 18:03:46 -0600685 for (i = 0; BC_NO_SIG && i < clen; ++i) {
Gavin Howarddc4e3712019-04-23 14:56:29 -0600686
Gavin Howard5083aec2019-04-23 18:03:46 -0600687 ssize_t sidx = (ssize_t) (i - blen + 1);
688 size_t j = (size_t) BC_MAX(0, sidx), k = BC_MIN(i, blen - 1);
Gavin Howarddc4e3712019-04-23 14:56:29 -0600689
Stefan Essera299ffc2019-04-24 23:28:32 +0200690 for (; BC_NO_SIG && j < alen && k < blen; ++j, --k) {
Stefan Eßer4b111c22019-04-24 17:23:08 -0600691
Gavin Howard2956a5e2019-05-08 08:04:06 -0600692 sum += ((BcBigDig) ptr_a[j]) * ((BcBigDig) ptr_b[k]);
Gavin Howarddc4e3712019-04-23 14:56:29 -0600693
Gavin Howard92ba4e52019-05-10 20:49:13 -0600694 if (sum >= BC_BASE_POW) {
695 carry += sum / BC_BASE_POW;
696 sum %= BC_BASE_POW;
Stefan Essera299ffc2019-04-24 23:28:32 +0200697 }
698 }
Stefan Eßer4b111c22019-04-24 17:23:08 -0600699
Stefan Essera299ffc2019-04-24 23:28:32 +0200700 ptr_c[i] = (BcDig) sum;
Gavin Howard92ba4e52019-05-10 20:49:13 -0600701 assert(ptr_c[i] < BC_BASE_POW);
Stefan Essera299ffc2019-04-24 23:28:32 +0200702 sum = carry;
703 carry = 0;
Gavin Howarddc4e3712019-04-23 14:56:29 -0600704 }
705
Gavin Howard5083aec2019-04-23 18:03:46 -0600706 if (sum) {
Gavin Howard92ba4e52019-05-10 20:49:13 -0600707 assert(sum < BC_BASE_POW);
Gavin Howard12a7cd22019-04-24 07:10:14 -0600708 ptr_c[clen] = (BcDig) sum;
Gavin Howard5083aec2019-04-23 18:03:46 -0600709 clen += 1;
710 }
711
712 c->len = clen;
Gavin Howarda58840c2019-05-07 19:33:53 -0600713
Gavin Howarddc4e3712019-04-23 14:56:29 -0600714 return BC_SIG ? BC_STATUS_SIGNAL : BC_STATUS_SUCCESS;
715}
716
Gavin Howard14c354a2019-04-24 14:28:24 -0600717static BcStatus bc_num_shiftAddSub(BcNum *restrict n, const BcNum *restrict a,
718 size_t shift, BcNumShiftAddOp op)
719{
720 assert(n->len >= shift + a->len);
721 assert(!n->rdx && !a->rdx);
722 return op(n->num + shift, a->num, a->len);
723}
724
725static BcStatus bc_num_k(BcNum *a, BcNum *b, BcNum *restrict c) {
Gavin Howard773c86b2018-11-02 14:07:19 -0600726
Gavin Howard9b801632019-02-16 23:34:06 -0700727 BcStatus s;
Stefan Esser14eff462019-04-25 07:42:03 +0200728 size_t max, max2, total;
Gavin Howard305249a2018-10-15 20:24:47 -0600729 BcNum l1, h1, l2, h2, m2, m1, z0, z1, z2, temp;
Gavin Howard5bede412019-02-23 09:31:28 -0700730 BcDig *digs, *dig_ptr;
Gavin Howard14c354a2019-04-24 14:28:24 -0600731 BcNumShiftAddOp op;
Gavin Howardb2f01a42019-05-10 20:00:32 -0600732 bool aone = BC_NUM_ONE(a);
Gavin Howard337fe602018-09-01 15:10:59 -0600733
Gavin Howard14913fc2019-04-23 14:36:03 -0600734 assert(BC_NUM_ZERO(c));
735
Gavin Howardddce6d42019-01-03 14:07:06 -0700736 // This is here because the function is recursive.
Gavin Howard5083aec2019-04-23 18:03:46 -0600737 if (BC_SIG) return BC_STATUS_SIGNAL;
Gavin Howardf1ae2be2019-05-09 11:58:22 -0600738 if (BC_NUM_ZERO(a) || BC_NUM_ZERO(b)) return BC_STATUS_SUCCESS;
Gavin Howardb2f01a42019-05-10 20:00:32 -0600739 if (aone || BC_NUM_ONE(b)) {
Gavin Howardad0ecfe2018-10-30 01:16:01 -0600740 bc_num_copy(c, aone ? b : a);
Gavin Howardb2f01a42019-05-10 20:00:32 -0600741 if ((aone && a->neg) || b->neg) c->neg = !c->neg;
Gavin Howardad0ecfe2018-10-30 01:16:01 -0600742 return BC_STATUS_SUCCESS;
743 }
Gavin Howardb7417b12019-05-10 18:39:46 -0600744 if (a->len < BC_NUM_KARATSUBA_LEN || b->len < BC_NUM_KARATSUBA_LEN)
Gavin Howarddc4e3712019-04-23 14:56:29 -0600745 return bc_num_m_simp(a, b, c);
Gavin Howard6fbdb292018-02-27 15:44:48 -0700746
Gavin Howarde0432502019-02-26 16:23:45 -0700747 max = BC_MAX(a->len, b->len);
Stefan Essera299ffc2019-04-24 23:28:32 +0200748 max = BC_MAX(max, BC_NUM_DEF_SIZE);
Gavin Howarde0432502019-02-26 16:23:45 -0700749 max2 = (max + 1) / 2;
750
Stefan Esser5fd43a32019-04-25 07:05:24 +0200751 total = bc_vm_arraySize(BC_NUM_KARATSUBA_ALLOCS, max);
Gavin Howard404ece72019-04-26 15:59:15 -0600752 digs = dig_ptr = bc_vm_malloc(BC_NUM_SIZE(total));
Gavin Howard5bede412019-02-23 09:31:28 -0700753
754 bc_num_setup(&l1, dig_ptr, max);
Stefan Essera299ffc2019-04-24 23:28:32 +0200755 dig_ptr += max;
Gavin Howard5bede412019-02-23 09:31:28 -0700756 bc_num_setup(&h1, dig_ptr, max);
Stefan Essera299ffc2019-04-24 23:28:32 +0200757 dig_ptr += max;
Gavin Howard5bede412019-02-23 09:31:28 -0700758 bc_num_setup(&l2, dig_ptr, max);
Stefan Essera299ffc2019-04-24 23:28:32 +0200759 dig_ptr += max;
Gavin Howard5bede412019-02-23 09:31:28 -0700760 bc_num_setup(&h2, dig_ptr, max);
Stefan Essera299ffc2019-04-24 23:28:32 +0200761 dig_ptr += max;
Gavin Howard5bede412019-02-23 09:31:28 -0700762 bc_num_setup(&m1, dig_ptr, max);
Stefan Essera299ffc2019-04-24 23:28:32 +0200763 dig_ptr += max;
Gavin Howard5bede412019-02-23 09:31:28 -0700764 bc_num_setup(&m2, dig_ptr, max);
Gavin Howard57223022019-04-24 08:37:14 -0600765 max = bc_vm_growSize(max, 1);
Gavin Howardad0ecfe2018-10-30 01:16:01 -0600766 bc_num_init(&z0, max);
767 bc_num_init(&z1, max);
768 bc_num_init(&z2, max);
Gavin Howard14c354a2019-04-24 14:28:24 -0600769 max = bc_vm_growSize(max, max) + 1;
770 bc_num_init(&temp, max);
Gavin Howard0dfe2922018-05-22 13:57:02 -0600771
Gavin Howardad0ecfe2018-10-30 01:16:01 -0600772 bc_num_split(a, max2, &l1, &h1);
Gavin Howard14c354a2019-04-24 14:28:24 -0600773 bc_num_clean(&l1);
774 bc_num_clean(&h1);
Gavin Howardad0ecfe2018-10-30 01:16:01 -0600775 bc_num_split(b, max2, &l2, &h2);
Gavin Howard14c354a2019-04-24 14:28:24 -0600776 bc_num_clean(&l2);
777 bc_num_clean(&h2);
Gavin Howard6fbdb292018-02-27 15:44:48 -0700778
Gavin Howard14c354a2019-04-24 14:28:24 -0600779 bc_num_expand(c, max);
780 c->len = max;
Gavin Howard404ece72019-04-26 15:59:15 -0600781 memset(c->num, 0, BC_NUM_SIZE(c->len));
Gavin Howard305249a2018-10-15 20:24:47 -0600782
Gavin Howard14c354a2019-04-24 14:28:24 -0600783 s = bc_num_sub(&h1, &l1, &m1, 0);
784 if (BC_ERR(s)) goto err;
785 s = bc_num_sub(&l2, &h2, &m2, 0);
786 if (BC_ERR(s)) goto err;
Gavin Howard305249a2018-10-15 20:24:47 -0600787
Gavin Howard971a2672019-04-26 14:32:07 -0600788 if (BC_NUM_NONZERO(&h1) && BC_NUM_NONZERO(&h2)) {
Gavin Howard305249a2018-10-15 20:24:47 -0600789
Gavin Howard14c354a2019-04-24 14:28:24 -0600790 s = bc_num_m(&h1, &h2, &z2, 0);
791 if (BC_ERR(s)) goto err;
792 bc_num_clean(&z2);
793
794 s = bc_num_shiftAddSub(c, &z2, max2 * 2, bc_num_addArrays);
795 if (BC_ERR(s)) goto err;
796 s = bc_num_shiftAddSub(c, &z2, max2, bc_num_addArrays);
797 if (BC_ERR(s)) goto err;
798 }
799
Gavin Howard971a2672019-04-26 14:32:07 -0600800 if (BC_NUM_NONZERO(&l1) && BC_NUM_NONZERO(&l2)) {
Gavin Howard14c354a2019-04-24 14:28:24 -0600801
802 s = bc_num_m(&l1, &l2, &z0, 0);
803 if (BC_ERR(s)) goto err;
804 bc_num_clean(&z0);
805
806 s = bc_num_shiftAddSub(c, &z0, max2, bc_num_addArrays);
807 if (BC_ERR(s)) goto err;
808 s = bc_num_shiftAddSub(c, &z0, 0, bc_num_addArrays);
809 if (BC_ERR(s)) goto err;
810 }
811
Gavin Howard971a2672019-04-26 14:32:07 -0600812 if (BC_NUM_NONZERO(&m1) && BC_NUM_NONZERO(&m2)) {
Gavin Howard14c354a2019-04-24 14:28:24 -0600813
814 s = bc_num_m(&m1, &m2, &z1, 0);
815 if (BC_ERR(s)) goto err;
816 bc_num_clean(&z1);
817
818 op = (m1.neg != m2.neg) ? bc_num_subArrays : bc_num_addArrays;
819 s = bc_num_shiftAddSub(c, &z1, max2, op);
820 if (BC_ERR(s)) goto err;
821 }
Gavin Howard305249a2018-10-15 20:24:47 -0600822
823err:
Gavin Howard5bede412019-02-23 09:31:28 -0700824 free(digs);
Gavin Howard305249a2018-10-15 20:24:47 -0600825 bc_num_free(&temp);
Gavin Howard305249a2018-10-15 20:24:47 -0600826 bc_num_free(&z2);
Gavin Howard305249a2018-10-15 20:24:47 -0600827 bc_num_free(&z1);
Gavin Howard305249a2018-10-15 20:24:47 -0600828 bc_num_free(&z0);
Gavin Howard305249a2018-10-15 20:24:47 -0600829 return s;
830}
831
Gavin Howard1cbfe242019-01-09 17:13:11 -0700832static BcStatus bc_num_m(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale) {
Gavin Howard305249a2018-10-15 20:24:47 -0600833
834 BcStatus s;
835 BcNum cpa, cpb;
Gavin Howard48a0e472019-04-26 15:39:46 -0600836 size_t ascale, bscale, ardx, brdx, azero = 0, bzero = 0, zero, len, rscale;
Gavin Howard305249a2018-10-15 20:24:47 -0600837
Gavin Howard14913fc2019-04-23 14:36:03 -0600838 bc_num_setToZero(c, 0);
Gavin Howardcf9f2462019-04-27 06:43:57 -0600839 ascale = a->scale;
840 bscale = b->scale;
841 scale = BC_MAX(scale, ascale);
Gavin Howarda9a887a2019-04-29 14:00:56 -0600842 scale = BC_MAX(scale, bscale);
Gavin Howard7cb5f6d2019-04-29 14:01:44 -0600843
Gavin Howard48a0e472019-04-26 15:39:46 -0600844 rscale = ascale + bscale;
Gavin Howard14913fc2019-04-23 14:36:03 -0600845 scale = BC_MIN(rscale, scale);
Gavin Howardb3f1ee22019-05-07 21:49:15 -0600846
Gavin Howardcc2974b2019-05-08 11:07:28 -0600847 if ((a->len == 1 || b->len == 1) && !a->rdx && !b->rdx) {
Gavin Howardb3f1ee22019-05-07 21:49:15 -0600848
849 BcNum *operand;
Gavin Howard2956a5e2019-05-08 08:04:06 -0600850 BcBigDig dig;
Gavin Howardb3f1ee22019-05-07 21:49:15 -0600851
852 if (a->len == 1) {
Gavin Howard2956a5e2019-05-08 08:04:06 -0600853 dig = (BcBigDig) a->num[0];
Gavin Howardb3f1ee22019-05-07 21:49:15 -0600854 operand = b;
855 }
856 else {
Gavin Howard2956a5e2019-05-08 08:04:06 -0600857 dig = (BcBigDig) b->num[0];
Gavin Howardb3f1ee22019-05-07 21:49:15 -0600858 operand = a;
859 }
860
861 s = bc_num_mulArray(operand, dig, c);
Gavin Howard66a93552019-05-09 17:13:59 -0600862 if (BC_ERROR_SIGNAL_ONLY(s)) return s;
Gavin Howardb3f1ee22019-05-07 21:49:15 -0600863
Gavin Howard51152ca2019-05-14 11:50:16 -0600864 if (BC_NUM_NONZERO(c)) c->neg = (a->neg != b->neg);
Gavin Howard15eca212019-05-14 13:08:12 -0600865
Gavin Howard66a93552019-05-09 17:13:59 -0600866 return s;
Gavin Howardb3f1ee22019-05-07 21:49:15 -0600867 }
868
Gavin Howard66a93552019-05-09 17:13:59 -0600869 bc_num_init(&cpa, a->len + a->rdx);
870 bc_num_init(&cpb, b->len + b->rdx);
871 bc_num_copy(&cpa, a);
872 bc_num_copy(&cpb, b);
873
874 cpa.neg = cpb.neg = false;
875
Gavin Howard92ba4e52019-05-10 20:49:13 -0600876 ardx = cpa.rdx * BC_BASE_DIGS;
Gavin Howard48a0e472019-04-26 15:39:46 -0600877 s = bc_num_shiftLeft(&cpa, ardx);
878 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howard14913fc2019-04-23 14:36:03 -0600879 bc_num_clean(&cpa);
880 azero = bc_num_shiftZero(&cpa);
Gavin Howard48a0e472019-04-26 15:39:46 -0600881
Gavin Howard92ba4e52019-05-10 20:49:13 -0600882 brdx = cpb.rdx * BC_BASE_DIGS;
Gavin Howard48a0e472019-04-26 15:39:46 -0600883 s = bc_num_shiftLeft(&cpb, brdx);
884 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howard14913fc2019-04-23 14:36:03 -0600885 bzero = bc_num_shiftZero(&cpb);
886 bc_num_clean(&cpb);
887
Gavin Howard14c354a2019-04-24 14:28:24 -0600888 s = bc_num_k(&cpa, &cpb, c);
Gavin Howardc78e7522019-02-22 13:24:25 -0700889 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howard305249a2018-10-15 20:24:47 -0600890
Gavin Howard14913fc2019-04-23 14:36:03 -0600891 zero = bc_vm_growSize(azero, bzero);
892 len = bc_vm_growSize(c->len, zero);
Gavin Howardad0ecfe2018-10-30 01:16:01 -0600893
Gavin Howard14913fc2019-04-23 14:36:03 -0600894 bc_num_expand(c, len);
Gavin Howard92ba4e52019-05-10 20:49:13 -0600895 s = bc_num_shiftLeft(c, (len - c->len) * BC_BASE_DIGS);
Gavin Howard48a0e472019-04-26 15:39:46 -0600896 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
897 s = bc_num_shiftRight(c, ardx + brdx);
898 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howardb3f1ee22019-05-07 21:49:15 -0600899
Gavin Howardad0ecfe2018-10-30 01:16:01 -0600900 bc_num_retireMul(c, scale, a->neg, b->neg);
Gavin Howardcf9f2462019-04-27 06:43:57 -0600901
Gavin Howard305249a2018-10-15 20:24:47 -0600902err:
Gavin Howard14913fc2019-04-23 14:36:03 -0600903 bc_num_unshiftZero(&cpb, bzero);
Gavin Howard305249a2018-10-15 20:24:47 -0600904 bc_num_free(&cpb);
Gavin Howard14913fc2019-04-23 14:36:03 -0600905 bc_num_unshiftZero(&cpa, azero);
Gavin Howard305249a2018-10-15 20:24:47 -0600906 bc_num_free(&cpa);
907 return s;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700908}
909
Gavin Howard1980e032019-05-08 17:21:17 -0600910static ssize_t bc_num_divCmp(const BcDig *a, const BcNum *b, size_t len) {
911
912 ssize_t cmp;
913
914 if (b->len > len && a[len]) cmp = bc_num_compare(a, b->num, len + 1);
915 else if (b->len <= len) {
916 if (a[len]) cmp = 1;
917 else cmp = bc_num_compare(a, b->num, len);
918 }
919 else cmp = -1;
920
921 return cmp;
922}
923
Gavin Howardd5b27c82019-05-09 12:13:07 -0600924static BcStatus bc_num_d_long(BcNum *restrict a, const BcNum *restrict b,
Gavin Howardbd60ec72019-05-10 08:01:22 -0600925 BcNum *restrict c, size_t scale)
Gavin Howardd5b27c82019-05-09 12:13:07 -0600926{
Gavin Howardad0ecfe2018-10-30 01:16:01 -0600927 BcStatus s = BC_STATUS_SUCCESS;
Gavin Howard92cf9052019-05-09 08:05:57 -0600928 BcBigDig divisor, q;
Gavin Howardbd60ec72019-05-10 08:01:22 -0600929 size_t len, end, i, rdx;
930 BcNum cpb, sub, temp;
Gavin Howardd5b27c82019-05-09 12:13:07 -0600931 BcDig *n;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700932
Gavin Howardd5b27c82019-05-09 12:13:07 -0600933 len = b->len;
934 end = a->len - len;
935 divisor = (BcBigDig) b->num[len - 1];
Gavin Howard070a6ce2019-05-08 11:50:25 -0600936
Gavin Howardd5b27c82019-05-09 12:13:07 -0600937 bc_num_expand(c, a->len);
938 memset(c->num + end, 0, (c->cap - end) * sizeof(BcDig));
Gavin Howardbd60ec72019-05-10 08:01:22 -0600939
Gavin Howardd5b27c82019-05-09 12:13:07 -0600940 c->rdx = a->rdx;
941 c->scale = a->scale;
942 c->len = a->len;
943
Gavin Howardbd60ec72019-05-10 08:01:22 -0600944 assert(c->scale >= scale);
945 rdx = c->rdx - BC_NUM_RDX(scale);
Gavin Howard61349662019-05-08 11:08:45 -0600946
Gavin Howardbd60ec72019-05-10 08:01:22 -0600947 bc_num_init(&cpb, len + 1);
948 bc_num_init(&sub, len + 1);
949 bc_num_init(&temp, len + 1);
950
951 for (i = end - 1; BC_NO_SIG && BC_NO_ERR(!s) && i < end && i >= rdx; --i) {
Gavin Howard61349662019-05-08 11:08:45 -0600952
Gavin Howard070a6ce2019-05-08 11:50:25 -0600953 ssize_t cmp;
954
Gavin Howardd5b27c82019-05-09 12:13:07 -0600955 n = a->num + i;
Gavin Howard070a6ce2019-05-08 11:50:25 -0600956 q = 0;
957
Gavin Howard1980e032019-05-08 17:21:17 -0600958 cmp = bc_num_divCmp(n, b, len);
Gavin Howard3378af82019-05-11 08:29:39 -0600959
960#if BC_ENABLE_SIGNALS
Gavin Howarda36f0312019-05-09 10:23:07 -0600961 if (cmp == BC_NUM_CMP_SIGNAL) break;
Gavin Howard3378af82019-05-11 08:29:39 -0600962#endif // BC_ENABLE_SIGNALS
Gavin Howard070a6ce2019-05-08 11:50:25 -0600963
Gavin Howardbd60ec72019-05-10 08:01:22 -0600964 if (!cmp) {
965
966 q = 1;
967
968 s = bc_num_mulArray(b, (BcBigDig) q, &cpb);
969 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
970 }
Gavin Howard070a6ce2019-05-08 11:50:25 -0600971 else if (cmp > 0) {
972
Gavin Howard92cf9052019-05-09 08:05:57 -0600973 BcBigDig n1, pow, dividend;
Gavin Howardbd60ec72019-05-10 08:01:22 -0600974 size_t cpblen;
Gavin Howard1980e032019-05-08 17:21:17 -0600975
976 n1 = (BcBigDig) n[len];
Gavin Howard92ba4e52019-05-10 20:49:13 -0600977 dividend = n1 * BC_BASE_POW + (BcBigDig) n[len - 1];
Gavin Howard92cf9052019-05-09 08:05:57 -0600978 q = (dividend / divisor + 1);
Gavin Howard92ba4e52019-05-10 20:49:13 -0600979 q = q > BC_BASE_POW ? BC_BASE_POW : q;
Gavin Howard1980e032019-05-08 17:21:17 -0600980 dividend = ((BcBigDig) bc_num_log10((size_t) q));
981
Gavin Howardc0b8a7c2019-05-13 01:56:30 -0600982 assert(dividend > 0);
983
Gavin Howard1980e032019-05-08 17:21:17 -0600984 pow = bc_num_pow10[dividend - 1];
Gavin Howard1980e032019-05-08 17:21:17 -0600985
Gavin Howard92cf9052019-05-09 08:05:57 -0600986 s = bc_num_mulArray(b, (BcBigDig) q, &cpb);
Gavin Howard1980e032019-05-08 17:21:17 -0600987 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
988
Gavin Howard6945eaf2019-05-09 17:06:54 -0600989 s = bc_num_mulArray(b, pow, &sub);
Gavin Howard1980e032019-05-08 17:21:17 -0600990 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
991
Gavin Howardbd60ec72019-05-10 08:01:22 -0600992 cpblen = cpb.len;
993
Gavin Howard1980e032019-05-08 17:21:17 -0600994 while (BC_NO_SIG && BC_NO_ERR(!s) && pow > 0) {
995
Gavin Howardbd60ec72019-05-10 08:01:22 -0600996 s = bc_num_subArrays(cpb.num, sub.num, sub.len);
Gavin Howard1980e032019-05-08 17:21:17 -0600997 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
998
Gavin Howardbd60ec72019-05-10 08:01:22 -0600999 bc_num_clean(&cpb);
Gavin Howard1980e032019-05-08 17:21:17 -06001000
Gavin Howardbd60ec72019-05-10 08:01:22 -06001001 cmp = bc_num_divCmp(n, &cpb, len);
Gavin Howard3378af82019-05-11 08:29:39 -06001002
1003#if BC_ENABLE_SIGNALS
Gavin Howarda36f0312019-05-09 10:23:07 -06001004 if (cmp == BC_NUM_CMP_SIGNAL) goto err;
Gavin Howard3378af82019-05-11 08:29:39 -06001005#endif // BC_ENABLE_SIGNALS
Gavin Howard1980e032019-05-08 17:21:17 -06001006
1007 while (BC_NO_SIG && BC_NO_ERR(!s) && cmp < 0) {
1008
1009 q -= pow;
1010
1011 s = bc_num_subArrays(cpb.num, sub.num, sub.len);
1012 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
1013
Gavin Howardbd60ec72019-05-10 08:01:22 -06001014 bc_num_clean(&cpb);
Gavin Howard1980e032019-05-08 17:21:17 -06001015
Gavin Howardbd60ec72019-05-10 08:01:22 -06001016 cmp = bc_num_divCmp(n, &cpb, len);
Gavin Howard3378af82019-05-11 08:29:39 -06001017
1018#if BC_ENABLE_SIGNALS
Gavin Howarda36f0312019-05-09 10:23:07 -06001019 if (cmp == BC_NUM_CMP_SIGNAL) goto err;
Gavin Howard3378af82019-05-11 08:29:39 -06001020#endif // BC_ENABLE_SIGNALS
Gavin Howard1980e032019-05-08 17:21:17 -06001021 }
1022
1023 pow /= BC_BASE;
Gavin Howard1980e032019-05-08 17:21:17 -06001024
Gavin Howardbd60ec72019-05-10 08:01:22 -06001025 if (pow) {
1026
Gavin Howard5a684032019-05-10 08:07:19 -06001027 BcBigDig rem;
1028
Gavin Howardbd60ec72019-05-10 08:01:22 -06001029 s = bc_num_addArrays(cpb.num, sub.num, sub.len);
1030 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
1031
1032 cpb.len = cpblen;
1033 bc_num_clean(&cpb);
1034
1035 bc_num_copy(&temp, &sub);
1036 s = bc_num_divArray(&temp, 10, &sub, &rem);
1037 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
1038 assert(rem == 0);
1039 }
Gavin Howard070a6ce2019-05-08 11:50:25 -06001040 }
Gavin Howard1980e032019-05-08 17:21:17 -06001041
1042 q -= 1;
1043 }
1044
Gavin Howard92ba4e52019-05-10 20:49:13 -06001045 assert(q <= BC_BASE_POW);
Gavin Howard1980e032019-05-08 17:21:17 -06001046
1047 if (q) {
1048
Gavin Howard1980e032019-05-08 17:21:17 -06001049 s = bc_num_subArrays(n, cpb.num, len);
1050 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howard070a6ce2019-05-08 11:50:25 -06001051 }
1052
Gavin Howard92cf9052019-05-09 08:05:57 -06001053 c->num[i] = (BcDig) q;
Gavin Howard61349662019-05-08 11:08:45 -06001054 }
1055
Gavin Howard1980e032019-05-08 17:21:17 -06001056err:
Gavin Howardd5b27c82019-05-09 12:13:07 -06001057 if (BC_NO_ERR(!s) && BC_SIG) s = BC_STATUS_SIGNAL;
Gavin Howardbd60ec72019-05-10 08:01:22 -06001058 bc_num_free(&temp);
Gavin Howard66a93552019-05-09 17:13:59 -06001059 bc_num_free(&cpb);
Gavin Howard66a93552019-05-09 17:13:59 -06001060 bc_num_free(&sub);
Gavin Howardd5b27c82019-05-09 12:13:07 -06001061 return s;
1062}
1063
Gavin Howardd5b27c82019-05-09 12:13:07 -06001064static BcStatus bc_num_d(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale) {
1065
1066 BcStatus s = BC_STATUS_SUCCESS;
Gavin Howardfcb74462019-05-09 14:42:35 -06001067 size_t len;
1068 BcNum cpa, cpb;
Gavin Howardd5b27c82019-05-09 12:13:07 -06001069 bool zero = true;
1070
1071 if (BC_NUM_ZERO(b)) return bc_vm_err(BC_ERROR_MATH_DIVIDE_BY_ZERO);
1072 if (BC_NUM_ZERO(a)) {
1073 bc_num_setToZero(c, scale);
1074 return BC_STATUS_SUCCESS;
1075 }
Gavin Howardb2f01a42019-05-10 20:00:32 -06001076 if (BC_NUM_ONE(b)) {
Gavin Howardd5b27c82019-05-09 12:13:07 -06001077 bc_num_copy(c, a);
1078 bc_num_retireMul(c, scale, a->neg, b->neg);
1079 return BC_STATUS_SUCCESS;
1080 }
1081 if (!a->rdx && !b->rdx && b->len == 1 && !scale) {
1082 BcBigDig rem;
1083 s = bc_num_divArray(a, (BcBigDig) b->num[0], c, &rem);
1084 bc_num_retireMul(c, scale, a->neg, b->neg);
1085 return s;
1086 }
1087
1088 len = bc_num_mulReq(a, b, scale);
1089 bc_num_init(&cpa, len);
1090 bc_num_copy(&cpa, a);
Gavin Howardfcb74462019-05-09 14:42:35 -06001091 bc_num_createCopy(&cpb, b);
Gavin Howardd5b27c82019-05-09 12:13:07 -06001092
Gavin Howard03e35dd2019-05-11 06:56:30 -06001093 len = b->len;
1094
Gavin Howardd5b27c82019-05-09 12:13:07 -06001095 if (len > cpa.len) {
1096 bc_num_expand(&cpa, bc_vm_growSize(len, 2));
Gavin Howard92ba4e52019-05-10 20:49:13 -06001097 bc_num_extend(&cpa, (len - cpa.len) * BC_BASE_DIGS);
Gavin Howardd5b27c82019-05-09 12:13:07 -06001098 }
1099
Gavin Howard92ba4e52019-05-10 20:49:13 -06001100 cpa.scale = cpa.rdx * BC_BASE_DIGS;
Gavin Howardd5b27c82019-05-09 12:13:07 -06001101
1102 bc_num_extend(&cpa, b->scale);
1103 cpa.rdx -= BC_NUM_RDX(b->scale);
Gavin Howard92ba4e52019-05-10 20:49:13 -06001104 cpa.scale = cpa.rdx * BC_BASE_DIGS;
Gavin Howardbd60ec72019-05-10 08:01:22 -06001105 if (scale > cpa.scale) {
1106 bc_num_extend(&cpa, scale);
Gavin Howard92ba4e52019-05-10 20:49:13 -06001107 cpa.scale = cpa.rdx * BC_BASE_DIGS;
Gavin Howardbd60ec72019-05-10 08:01:22 -06001108 }
Gavin Howardd5b27c82019-05-09 12:13:07 -06001109
1110 if (b->rdx == b->len) {
Gavin Howardfcb74462019-05-09 14:42:35 -06001111 size_t i;
Gavin Howardd5b27c82019-05-09 12:13:07 -06001112 for (i = 0; zero && i < len; ++i) zero = !b->num[len - i - 1];
1113 assert(i != len || !zero);
1114 len -= i - 1;
1115 }
1116
1117 if (cpa.cap == cpa.len) bc_num_expand(&cpa, bc_vm_growSize(cpa.len, 1));
1118
1119 // We want an extra zero in front to make things simpler.
1120 cpa.num[cpa.len++] = 0;
1121
Gavin Howardfcb74462019-05-09 14:42:35 -06001122 if (cpa.rdx == cpa.len) cpa.len = bc_num_nonzeroLen(&cpa);
1123 if (cpb.rdx == cpb.len) cpb.len = bc_num_nonzeroLen(&cpb);
1124 cpb.scale = cpb.rdx = 0;
Gavin Howardd5b27c82019-05-09 12:13:07 -06001125
Gavin Howardbd60ec72019-05-10 08:01:22 -06001126 s = bc_num_d_long(&cpa, &cpb, c, scale);
Gavin Howardd5b27c82019-05-09 12:13:07 -06001127
1128 if (BC_NO_ERR(!s)) {
1129 bc_num_retireMul(c, scale, a->neg, b->neg);
1130 if (BC_SIG) s = BC_STATUS_SIGNAL;
1131 }
1132
Gavin Howardfcb74462019-05-09 14:42:35 -06001133 bc_num_free(&cpb);
Gavin Howard1980e032019-05-08 17:21:17 -06001134 bc_num_free(&cpa);
Gavin Howardd5b27c82019-05-09 12:13:07 -06001135
Gavin Howard63738202018-09-26 15:34:20 -06001136 return s;
Gavin Howard6fbdb292018-02-27 15:44:48 -07001137}
1138
Gavin Howard1cbfe242019-01-09 17:13:11 -07001139static BcStatus bc_num_r(BcNum *a, BcNum *b, BcNum *restrict c,
1140 BcNum *restrict d, size_t scale, size_t ts)
Gavin Howard18441082018-10-22 10:03:30 -06001141{
Gavin Howard63738202018-09-26 15:34:20 -06001142 BcStatus s;
Gavin Howard18441082018-10-22 10:03:30 -06001143 BcNum temp;
1144 bool neg;
Gavin Howard6fbdb292018-02-27 15:44:48 -07001145
Gavin Howardddce6d42019-01-03 14:07:06 -07001146 if (BC_NUM_ZERO(b)) return bc_vm_err(BC_ERROR_MATH_DIVIDE_BY_ZERO);
1147 if (BC_NUM_ZERO(a)) {
Gavin Howard99ca4992019-01-02 13:48:49 -07001148 bc_num_setToZero(c, ts);
Gavin Howardf8ddb6d2018-10-22 12:58:53 -06001149 bc_num_setToZero(d, ts);
Gavin Howard63738202018-09-26 15:34:20 -06001150 return BC_STATUS_SUCCESS;
1151 }
Gavin Howard8b254872018-03-14 01:13:35 -06001152
Gavin Howardad0ecfe2018-10-30 01:16:01 -06001153 bc_num_init(&temp, d->cap);
Gavin Howard1769abc2019-02-21 09:36:14 -07001154 s = bc_num_d(a, b, c, scale);
1155 assert(!s || s == BC_STATUS_SIGNAL);
Gavin Howardc78e7522019-02-22 13:24:25 -07001156 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howard6fbdb292018-02-27 15:44:48 -07001157
Gavin Howard996fc222019-04-29 13:22:38 -06001158 if (scale) scale = ts + 1;
Gavin Howard3115c012018-10-04 10:53:56 -06001159
Gavin Howard53eba8b2018-10-31 15:14:37 -06001160 s = bc_num_m(c, b, &temp, scale);
Gavin Howardc78e7522019-02-22 13:24:25 -07001161 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howard53eba8b2018-10-31 15:14:37 -06001162 s = bc_num_sub(a, &temp, d, scale);
Gavin Howardc78e7522019-02-22 13:24:25 -07001163 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howard5d149cf2018-09-06 13:46:23 -06001164
Gavin Howard77445432019-04-26 15:59:33 -06001165 if (ts > d->scale && BC_NUM_NONZERO(d)) bc_num_extend(d, ts - d->scale);
Gavin Howard18441082018-10-22 10:03:30 -06001166
1167 neg = d->neg;
Gavin Howardad0ecfe2018-10-30 01:16:01 -06001168 bc_num_retireMul(d, ts, a->neg, b->neg);
Gavin Howard996fc222019-04-29 13:22:38 -06001169 d->neg = BC_NUM_NONZERO(d) ? neg : false;
Gavin Howard6fbdb292018-02-27 15:44:48 -07001170
1171err:
Gavin Howard18441082018-10-22 10:03:30 -06001172 bc_num_free(&temp);
1173 return s;
1174}
1175
Gavin Howard2d188a52019-02-25 14:19:08 -07001176static BcStatus bc_num_rem(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale)
1177{
Gavin Howard18441082018-10-22 10:03:30 -06001178 BcStatus s;
1179 BcNum c1;
Gavin Howard798508b2019-02-21 16:45:32 -07001180 size_t ts;
Gavin Howard18441082018-10-22 10:03:30 -06001181
Gavin Howard77445432019-04-26 15:59:33 -06001182 ts = bc_vm_growSize(scale, b->scale);
1183 ts = BC_MAX(ts, a->scale);
Gavin Howard798508b2019-02-21 16:45:32 -07001184
1185 bc_num_init(&c1, bc_num_mulReq(a, b, ts));
Gavin Howard54b946a2018-10-23 12:06:57 -06001186 s = bc_num_r(a, b, &c1, c, scale, ts);
Gavin Howard63738202018-09-26 15:34:20 -06001187 bc_num_free(&c1);
Gavin Howardd64ce7b2018-10-24 16:20:20 -06001188
Gavin Howard63738202018-09-26 15:34:20 -06001189 return s;
Gavin Howard6fbdb292018-02-27 15:44:48 -07001190}
1191
Gavin Howard1cbfe242019-01-09 17:13:11 -07001192static BcStatus bc_num_p(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale) {
Gavin Howard6fbdb292018-02-27 15:44:48 -07001193
Gavin Howardad0ecfe2018-10-30 01:16:01 -06001194 BcStatus s = BC_STATUS_SUCCESS;
Gavin Howard63738202018-09-26 15:34:20 -06001195 BcNum copy;
Gavin Howard2956a5e2019-05-08 08:04:06 -06001196 BcBigDig pow = 0;
Gavin Howard63738202018-09-26 15:34:20 -06001197 size_t i, powrdx, resrdx;
1198 bool neg, zero;
Gavin Howard6fbdb292018-02-27 15:44:48 -07001199
Gavin Howardecafd4f2019-02-23 09:30:45 -07001200 if (BC_ERR(b->rdx)) return bc_vm_err(BC_ERROR_MATH_NON_INTEGER);
Gavin Howard6fbdb292018-02-27 15:44:48 -07001201
Gavin Howardddce6d42019-01-03 14:07:06 -07001202 if (BC_NUM_ZERO(b)) {
Gavin Howard63738202018-09-26 15:34:20 -06001203 bc_num_one(c);
Gavin Howard61abdbe2018-10-22 13:05:38 -06001204 return BC_STATUS_SUCCESS;
Gavin Howard63738202018-09-26 15:34:20 -06001205 }
Gavin Howardddce6d42019-01-03 14:07:06 -07001206 if (BC_NUM_ZERO(a)) {
Gavin Howardf8ddb6d2018-10-22 12:58:53 -06001207 bc_num_setToZero(c, scale);
Gavin Howard63738202018-09-26 15:34:20 -06001208 return BC_STATUS_SUCCESS;
1209 }
Gavin Howardb2f01a42019-05-10 20:00:32 -06001210 if (BC_NUM_ONE(b)) {
Gavin Howardad0ecfe2018-10-30 01:16:01 -06001211 if (!b->neg) bc_num_copy(c, a);
Gavin Howard63738202018-09-26 15:34:20 -06001212 else s = bc_num_inv(a, c, scale);
Gavin Howard63738202018-09-26 15:34:20 -06001213 return s;
1214 }
Gavin Howard6fbdb292018-02-27 15:44:48 -07001215
Gavin Howard63738202018-09-26 15:34:20 -06001216 neg = b->neg;
1217 b->neg = false;
Gavin Howard2956a5e2019-05-08 08:04:06 -06001218 s = bc_num_bigdig(b, &pow);
Gavin Howardfb14efc2018-12-22 14:01:58 -07001219 b->neg = neg;
Gavin Howard53eba8b2018-10-31 15:14:37 -06001220 if (s) return s;
1221
Gavin Howardbaa4f582019-01-24 13:56:35 -07001222 bc_num_createCopy(&copy, a);
Gavin Howard6fbdb292018-02-27 15:44:48 -07001223
Gavin Howardeec07282019-05-10 20:10:18 -06001224 if (!neg) {
1225 size_t max = BC_MAX(scale, a->scale), scalepow = a->scale * pow;
1226 scale = BC_MIN(scalepow, max);
1227 }
Gavin Howardb29674f2018-03-22 22:24:58 -06001228
Gavin Howardd497c232019-04-29 10:32:38 -06001229 for (powrdx = a->scale; BC_NO_SIG && !(pow & 1); pow >>= 1) {
Gavin Howard63738202018-09-26 15:34:20 -06001230 powrdx <<= 1;
Gavin Howard53eba8b2018-10-31 15:14:37 -06001231 s = bc_num_mul(&copy, &copy, &copy, powrdx);
Gavin Howardc78e7522019-02-22 13:24:25 -07001232 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howard63738202018-09-26 15:34:20 -06001233 }
Gavin Howard6fb635f2018-03-03 12:45:42 -07001234
Gavin Howard2d188a52019-02-25 14:19:08 -07001235 if (BC_SIG) goto sig_err;
Gavin Howard6fb635f2018-03-03 12:45:42 -07001236
Gavin Howardad0ecfe2018-10-30 01:16:01 -06001237 bc_num_copy(c, &copy);
Gavin Howard1ab22d22019-01-03 13:32:17 -07001238 resrdx = powrdx;
Gavin Howardad0ecfe2018-10-30 01:16:01 -06001239
Gavin Howard2d188a52019-02-25 14:19:08 -07001240 while (BC_NO_SIG && (pow >>= 1)) {
Gavin Howard53eba8b2018-10-31 15:14:37 -06001241
Gavin Howard4d5daba2019-01-07 14:42:50 -07001242 powrdx <<= 1;
Gavin Howard53eba8b2018-10-31 15:14:37 -06001243 s = bc_num_mul(&copy, &copy, &copy, powrdx);
Gavin Howardc78e7522019-02-22 13:24:25 -07001244 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howard53eba8b2018-10-31 15:14:37 -06001245
Gavin Howard63738202018-09-26 15:34:20 -06001246 if (pow & 1) {
1247 resrdx += powrdx;
Gavin Howard53eba8b2018-10-31 15:14:37 -06001248 s = bc_num_mul(c, &copy, c, resrdx);
Gavin Howardc78e7522019-02-22 13:24:25 -07001249 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howard63738202018-09-26 15:34:20 -06001250 }
1251 }
Gavin Howard6fbdb292018-02-27 15:44:48 -07001252
Gavin Howard2d188a52019-02-25 14:19:08 -07001253 if (BC_SIG) goto sig_err;
Gavin Howard9b801632019-02-16 23:34:06 -07001254 if (neg) {
1255 s = bc_num_inv(c, c, scale);
Gavin Howardc78e7522019-02-22 13:24:25 -07001256 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howard9b801632019-02-16 23:34:06 -07001257 }
1258
Stefan Eßerb5318342019-04-29 10:31:26 -06001259 if (c->scale > scale) bc_num_truncate(c, c->scale - scale);
Gavin Howard1d959152018-03-03 23:33:13 -07001260
Gavin Howard53eba8b2018-10-31 15:14:37 -06001261 // We can't use bc_num_clean() here.
Gavin Howard63738202018-09-26 15:34:20 -06001262 for (zero = true, i = 0; zero && i < c->len; ++i) zero = !c->num[i];
Gavin Howardf8ddb6d2018-10-22 12:58:53 -06001263 if (zero) bc_num_setToZero(c, scale);
Gavin Howard1d959152018-03-03 23:33:13 -07001264
Gavin Howard9b801632019-02-16 23:34:06 -07001265sig_err:
Gavin Howard2d188a52019-02-25 14:19:08 -07001266 if (BC_NO_ERR(!s) && BC_SIG) s = BC_STATUS_SIGNAL;
Gavin Howard6fbdb292018-02-27 15:44:48 -07001267err:
Gavin Howard63738202018-09-26 15:34:20 -06001268 bc_num_free(&copy);
1269 return s;
Gavin Howard6fbdb292018-02-27 15:44:48 -07001270}
1271
Gavin Howard7bda4782018-12-28 09:53:22 -07001272#if BC_ENABLE_EXTRA_MATH
Gavin Howard2d188a52019-02-25 14:19:08 -07001273static BcStatus bc_num_place(BcNum *a, BcNum *b, BcNum *restrict c,
1274 size_t scale)
1275{
Gavin Howardac4d9122018-12-27 23:59:12 -07001276 BcStatus s = BC_STATUS_SUCCESS;
Gavin Howard2956a5e2019-05-08 08:04:06 -06001277 BcBigDig val = 0;
Gavin Howardac4d9122018-12-27 23:59:12 -07001278
1279 BC_UNUSED(scale);
1280
1281 s = bc_num_intop(a, b, c, &val);
Gavin Howardecafd4f2019-02-23 09:30:45 -07001282 if (BC_ERR(s)) return s;
Gavin Howardac4d9122018-12-27 23:59:12 -07001283
Gavin Howardc9bef2d2019-04-26 14:46:52 -06001284 if (val < c->scale) bc_num_truncate(c, c->scale - val);
1285 else if (val > c->scale) bc_num_extend(c, val - c->scale);
Gavin Howardac4d9122018-12-27 23:59:12 -07001286
1287 return s;
1288}
1289
Gavin Howard2d188a52019-02-25 14:19:08 -07001290static BcStatus bc_num_left(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale)
1291{
Gavin Howardac4d9122018-12-27 23:59:12 -07001292 BcStatus s = BC_STATUS_SUCCESS;
Gavin Howard2956a5e2019-05-08 08:04:06 -06001293 BcBigDig val = 0;
Gavin Howardac4d9122018-12-27 23:59:12 -07001294
1295 BC_UNUSED(scale);
1296
1297 s = bc_num_intop(a, b, c, &val);
Gavin Howardecafd4f2019-02-23 09:30:45 -07001298 if (BC_ERR(s)) return s;
Gavin Howardac4d9122018-12-27 23:59:12 -07001299
Gavin Howardb1558662019-04-26 14:32:55 -06001300 return bc_num_shiftLeft(c, (size_t) val);
Gavin Howardac4d9122018-12-27 23:59:12 -07001301}
1302
Gavin Howard2d188a52019-02-25 14:19:08 -07001303static BcStatus bc_num_right(BcNum *a, BcNum *b, BcNum *restrict c,
1304 size_t scale)
1305{
Gavin Howardac4d9122018-12-27 23:59:12 -07001306 BcStatus s = BC_STATUS_SUCCESS;
Gavin Howard2956a5e2019-05-08 08:04:06 -06001307 BcBigDig val = 0;
Gavin Howardac4d9122018-12-27 23:59:12 -07001308
1309 BC_UNUSED(scale);
1310
1311 s = bc_num_intop(a, b, c, &val);
Gavin Howardecafd4f2019-02-23 09:30:45 -07001312 if (BC_ERR(s)) return s;
Gavin Howardac4d9122018-12-27 23:59:12 -07001313
Gavin Howard2a366922019-01-16 10:50:20 -07001314 if (BC_NUM_ZERO(c)) return s;
1315
Gavin Howardb1558662019-04-26 14:32:55 -06001316 return bc_num_shiftRight(c, (size_t) val);
Gavin Howardac4d9122018-12-27 23:59:12 -07001317}
Gavin Howard7bda4782018-12-28 09:53:22 -07001318#endif // BC_ENABLE_EXTRA_MATH
Gavin Howardac4d9122018-12-27 23:59:12 -07001319
Gavin Howard1cbfe242019-01-09 17:13:11 -07001320static BcStatus bc_num_binary(BcNum *a, BcNum *b, BcNum *c, size_t scale,
1321 BcNumBinaryOp op, size_t req)
Gavin Howard6fbdb292018-02-27 15:44:48 -07001322{
Gavin Howarda1c44392018-09-27 12:41:15 -06001323 BcStatus s;
Gavin Howard63738202018-09-26 15:34:20 -06001324 BcNum num2, *ptr_a, *ptr_b;
1325 bool init = false;
Gavin Howard6fbdb292018-02-27 15:44:48 -07001326
Gavin Howard63738202018-09-26 15:34:20 -06001327 assert(a && b && c && op);
Gavin Howard6fbdb292018-02-27 15:44:48 -07001328
Gavin Howard890d0c02018-10-30 16:34:50 -06001329 if (c == a) {
Gavin Howard63738202018-09-26 15:34:20 -06001330 ptr_a = &num2;
Gavin Howardba009802018-09-29 04:41:51 -06001331 memcpy(ptr_a, c, sizeof(BcNum));
Gavin Howard890d0c02018-10-30 16:34:50 -06001332 init = true;
Gavin Howard63738202018-09-26 15:34:20 -06001333 }
1334 else ptr_a = a;
Gavin Howard6fbdb292018-02-27 15:44:48 -07001335
Gavin Howard63738202018-09-26 15:34:20 -06001336 if (c == b) {
Gavin Howardba009802018-09-29 04:41:51 -06001337 ptr_b = &num2;
Gavin Howardba009802018-09-29 04:41:51 -06001338 if (c != a) {
1339 memcpy(ptr_b, c, sizeof(BcNum));
Gavin Howard63738202018-09-26 15:34:20 -06001340 init = true;
1341 }
1342 }
1343 else ptr_b = b;
Gavin Howard6fbdb292018-02-27 15:44:48 -07001344
Gavin Howardad0ecfe2018-10-30 01:16:01 -06001345 if (init) bc_num_init(c, req);
1346 else bc_num_expand(c, req);
Gavin Howard6fbdb292018-02-27 15:44:48 -07001347
Gavin Howardad0ecfe2018-10-30 01:16:01 -06001348 s = op(ptr_a, ptr_b, c, scale);
Gavin Howard6fbdb292018-02-27 15:44:48 -07001349
Gavin Howardddce6d42019-01-03 14:07:06 -07001350 assert(!c->neg || BC_NUM_NONZERO(c));
Gavin Howard87c29c72019-03-28 11:22:51 -06001351 assert(c->rdx <= c->len || !c->len || s);
Gavin Howard3cf769e2018-10-11 13:55:11 -06001352
Gavin Howardba009802018-09-29 04:41:51 -06001353 if (init) bc_num_free(&num2);
Gavin Howardad0ecfe2018-10-30 01:16:01 -06001354
Gavin Howardee9d01e2019-02-16 22:48:11 -07001355 return s;
Gavin Howard6fbdb292018-02-27 15:44:48 -07001356}
1357
Gavin Howardc90ed902019-01-02 13:07:49 -07001358#ifndef NDEBUG
Gavin Howard1cbfe242019-01-09 17:13:11 -07001359static bool bc_num_strValid(const char *val) {
Gavin Howard6fbdb292018-02-27 15:44:48 -07001360
Gavin Howardc7d655b2018-12-28 19:45:13 -07001361 bool radix = false;
Gavin Howard63738202018-09-26 15:34:20 -06001362 size_t i, len = strlen(val);
Gavin Howard6fbdb292018-02-27 15:44:48 -07001363
Gavin Howard63738202018-09-26 15:34:20 -06001364 if (!len) return true;
Gavin Howard6fbdb292018-02-27 15:44:48 -07001365
Gavin Howard63738202018-09-26 15:34:20 -06001366 for (i = 0; i < len; ++i) {
Gavin Howard6fbdb292018-02-27 15:44:48 -07001367
Gavin Howard8a921bd2018-10-11 14:15:32 -06001368 BcDig c = val[i];
Gavin Howardf6e3fb32018-08-09 13:48:59 -06001369
Gavin Howard63738202018-09-26 15:34:20 -06001370 if (c == '.') {
Gavin Howard6fbdb292018-02-27 15:44:48 -07001371
Gavin Howard63738202018-09-26 15:34:20 -06001372 if (radix) return false;
Gavin Howard6fbdb292018-02-27 15:44:48 -07001373
Gavin Howard63738202018-09-26 15:34:20 -06001374 radix = true;
1375 continue;
1376 }
Gavin Howard6fbdb292018-02-27 15:44:48 -07001377
Gavin Howardc7d655b2018-12-28 19:45:13 -07001378 if (!(isdigit(c) || isupper(c))) return false;
Gavin Howard63738202018-09-26 15:34:20 -06001379 }
Gavin Howard6fbdb292018-02-27 15:44:48 -07001380
Gavin Howard63738202018-09-26 15:34:20 -06001381 return true;
Gavin Howard6fbdb292018-02-27 15:44:48 -07001382}
Gavin Howardc90ed902019-01-02 13:07:49 -07001383#endif // NDEBUG
Gavin Howard6fbdb292018-02-27 15:44:48 -07001384
Gavin Howard2956a5e2019-05-08 08:04:06 -06001385static BcBigDig bc_num_parseChar(char c, size_t base_t) {
Gavin Howardc7d655b2018-12-28 19:45:13 -07001386
1387 if (isupper(c)) {
1388 c = BC_NUM_NUM_LETTER(c);
Gavin Howard6193aaf2019-01-03 16:44:04 -07001389 c = ((size_t) c) >= base_t ? (char) base_t - 1 : c;
Gavin Howardc7d655b2018-12-28 19:45:13 -07001390 }
1391 else c -= '0';
1392
Gavin Howard2956a5e2019-05-08 08:04:06 -06001393 return (BcBigDig) (uchar) c;
Gavin Howardc7d655b2018-12-28 19:45:13 -07001394}
1395
Gavin Howard1cbfe242019-01-09 17:13:11 -07001396static void bc_num_parseDecimal(BcNum *restrict n, const char *restrict val) {
Gavin Howard6fbdb292018-02-27 15:44:48 -07001397
Gavin Howard3b60abb2019-04-26 08:49:17 -06001398 size_t len, i, temp, mod;
Gavin Howard63738202018-09-26 15:34:20 -06001399 const char *ptr;
Gavin Howardd43ec372019-04-25 21:13:54 -06001400 bool zero = true, rdx;
Stefan Essera299ffc2019-04-24 23:28:32 +02001401
Gavin Howard63738202018-09-26 15:34:20 -06001402 for (i = 0; val[i] == '0'; ++i);
Gavin Howard6fbdb292018-02-27 15:44:48 -07001403
Gavin Howard63738202018-09-26 15:34:20 -06001404 val += i;
Gavin Howard46a7d8d2019-05-06 15:16:12 -06001405 assert(!val[0] || isalnum(val[0]) || val[0] == '.');
1406
1407 // All 0's. We can just return, since this
1408 // procedure expects a virgin (already 0) BcNum.
1409 if (!val[0]) return;
1410
Gavin Howard63738202018-09-26 15:34:20 -06001411 len = strlen(val);
Gavin Howard6fbdb292018-02-27 15:44:48 -07001412
Gavin Howard63738202018-09-26 15:34:20 -06001413 ptr = strchr(val, '.');
Gavin Howardd43ec372019-04-25 21:13:54 -06001414 rdx = (ptr != NULL);
Gavin Howard6fbdb292018-02-27 15:44:48 -07001415
Gavin Howardd43ec372019-04-25 21:13:54 -06001416 for (i = 0; i < len && (zero = (val[i] == '0' || val[i] == '.')); ++i);
Gavin Howard6fbdb292018-02-27 15:44:48 -07001417
Gavin Howardd43ec372019-04-25 21:13:54 -06001418 n->scale = (size_t) (rdx * ((val + len) - (ptr + 1)));
Gavin Howard1c1697c2019-04-26 10:07:45 -06001419 n->rdx = BC_NUM_RDX(n->scale);
Gavin Howardd43ec372019-04-25 21:13:54 -06001420
Gavin Howard3b60abb2019-04-26 08:49:17 -06001421 i = len - (ptr == val ? 0 : i) - rdx;
Gavin Howard48a0e472019-04-26 15:39:46 -06001422 temp = BC_NUM_ROUND_POW(i);
Gavin Howard92ba4e52019-05-10 20:49:13 -06001423 mod = n->scale % BC_BASE_DIGS;
1424 i = mod ? BC_BASE_DIGS - mod : 0;
1425 n->len = ((temp + i) / BC_BASE_DIGS);
Gavin Howardd43ec372019-04-25 21:13:54 -06001426
1427 bc_num_expand(n, n->len);
Gavin Howard404ece72019-04-26 15:59:15 -06001428 memset(n->num, 0, BC_NUM_SIZE(n->len));
Gavin Howard6fbdb292018-02-27 15:44:48 -07001429
Gavin Howard6076dbb2019-04-26 09:47:50 -06001430 if (zero) n->len = n->rdx = 0;
1431 else {
Gavin Howardfcf29012019-04-25 20:12:19 -06001432
Gavin Howard2956a5e2019-05-08 08:04:06 -06001433 BcBigDig exp, pow;
Gavin Howardd43ec372019-04-25 21:13:54 -06001434
Gavin Howard687cf4d2019-05-11 08:55:36 -06001435 assert(i <= BC_NUM_BIGDIG_MAX);
1436
1437 exp = (BcBigDig) i;
Gavin Howardc1542802019-05-08 17:20:51 -06001438 pow = bc_num_pow10[exp];
Gavin Howardd43ec372019-04-25 21:13:54 -06001439
1440 for (i = len - 1; i < len; --i, ++exp) {
Gavin Howard8dd307e2019-01-08 23:05:19 -07001441
Gavin Howardc7d655b2018-12-28 19:45:13 -07001442 char c = val[i];
Gavin Howard8dd307e2019-01-08 23:05:19 -07001443
Gavin Howardd43ec372019-04-25 21:13:54 -06001444 if (c == '.') exp -= 1;
Gavin Howard8dd307e2019-01-08 23:05:19 -07001445 else {
Gavin Howardd43ec372019-04-25 21:13:54 -06001446
Gavin Howard92ba4e52019-05-10 20:49:13 -06001447 size_t idx = exp / BC_BASE_DIGS;
Gavin Howardd43ec372019-04-25 21:13:54 -06001448
Gavin Howard8dd307e2019-01-08 23:05:19 -07001449 if (isupper(c)) c = '9';
Gavin Howard2956a5e2019-05-08 08:04:06 -06001450 n->num[idx] += (((BcBigDig) c) - '0') * pow;
Gavin Howardd43ec372019-04-25 21:13:54 -06001451
Gavin Howard92ba4e52019-05-10 20:49:13 -06001452 if ((exp + 1) % BC_BASE_DIGS == 0) pow = 1;
Gavin Howardd43ec372019-04-25 21:13:54 -06001453 else pow *= BC_BASE;
Gavin Howard8dd307e2019-01-08 23:05:19 -07001454 }
Gavin Howardc7d655b2018-12-28 19:45:13 -07001455 }
Gavin Howardad0ecfe2018-10-30 01:16:01 -06001456 }
Gavin Howard6fbdb292018-02-27 15:44:48 -07001457}
1458
Gavin Howarddc0ee9e2019-02-16 23:27:08 -07001459static BcStatus bc_num_parseBase(BcNum *restrict n, const char *restrict val,
Gavin Howard2956a5e2019-05-08 08:04:06 -06001460 BcBigDig base)
Gavin Howard1cbfe242019-01-09 17:13:11 -07001461{
1462 BcStatus s = BC_STATUS_SUCCESS;
Gavin Howard4c605692019-05-08 09:09:17 -06001463 BcNum temp, mult1, mult2, result1, result2, *m1, *m2, *ptr;
Gavin Howard77445432019-04-26 15:59:33 -06001464 char c = 0;
Gavin Howard11b9afd2018-10-18 14:23:28 -06001465 bool zero = true;
Gavin Howard2956a5e2019-05-08 08:04:06 -06001466 BcBigDig v;
Gavin Howard1769abc2019-02-21 09:36:14 -07001467 size_t i, digs, len = strlen(val);
Gavin Howardede51f02018-03-02 12:30:00 -07001468
Gavin Howard11b9afd2018-10-18 14:23:28 -06001469 for (i = 0; zero && i < len; ++i) zero = (val[i] == '.' || val[i] == '0');
Gavin Howarddc0ee9e2019-02-16 23:27:08 -07001470 if (zero) return BC_STATUS_SUCCESS;
Gavin Howardede51f02018-03-02 12:30:00 -07001471
Gavin Howard2956a5e2019-05-08 08:04:06 -06001472 bc_num_init(&temp, BC_NUM_BIGDIG_LOG10);
Gavin Howard4c605692019-05-08 09:09:17 -06001473 bc_num_init(&mult1, BC_NUM_BIGDIG_LOG10);
Gavin Howardede51f02018-03-02 12:30:00 -07001474
Gavin Howardc7d655b2018-12-28 19:45:13 -07001475 for (i = 0; i < len && (c = val[i]) && c != '.'; ++i) {
Gavin Howard9b801632019-02-16 23:34:06 -07001476
Gavin Howardb3f1ee22019-05-07 21:49:15 -06001477 v = bc_num_parseChar(c, base);
Gavin Howard9b801632019-02-16 23:34:06 -07001478
Gavin Howard4c605692019-05-08 09:09:17 -06001479 s = bc_num_mulArray(n, base, &mult1);
Gavin Howardc78e7522019-02-22 13:24:25 -07001480 if (BC_ERROR_SIGNAL_ONLY(s)) goto int_err;
Gavin Howard2956a5e2019-05-08 08:04:06 -06001481 bc_num_bigdig2num(&temp, v);
Gavin Howard4c605692019-05-08 09:09:17 -06001482 s = bc_num_add(&mult1, &temp, n, 0);
Gavin Howardc78e7522019-02-22 13:24:25 -07001483 if (BC_ERROR_SIGNAL_ONLY(s)) goto int_err;
Gavin Howard63738202018-09-26 15:34:20 -06001484 }
Gavin Howardede51f02018-03-02 12:30:00 -07001485
Gavin Howarddc0ee9e2019-02-16 23:27:08 -07001486 if (i == len && !(c = val[i])) goto int_err;
Gavin Howard53eba8b2018-10-31 15:14:37 -06001487
Gavin Howard63738202018-09-26 15:34:20 -06001488 assert(c == '.');
Gavin Howard4c605692019-05-08 09:09:17 -06001489 bc_num_init(&mult2, BC_NUM_BIGDIG_LOG10);
1490 bc_num_init(&result1, BC_NUM_DEF_SIZE);
1491 bc_num_init(&result2, BC_NUM_DEF_SIZE);
1492 bc_num_one(&mult1);
1493
1494 m1 = &mult1;
1495 m2 = &mult2;
Gavin Howardede51f02018-03-02 12:30:00 -07001496
Gavin Howard8722e832019-05-08 11:12:16 -06001497 for (i += 1, digs = 0; BC_NO_SIG && i < len && (c = val[i]); ++i, ++digs) {
1498
Gavin Howardb3f1ee22019-05-07 21:49:15 -06001499 v = bc_num_parseChar(c, base);
Gavin Howard9b801632019-02-16 23:34:06 -07001500
Gavin Howard4c605692019-05-08 09:09:17 -06001501 s = bc_num_mulArray(&result1, base, &result2);
Gavin Howardc78e7522019-02-22 13:24:25 -07001502 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howarddc0ee9e2019-02-16 23:27:08 -07001503
Gavin Howard2956a5e2019-05-08 08:04:06 -06001504 bc_num_bigdig2num(&temp, v);
Gavin Howard4c605692019-05-08 09:09:17 -06001505 s = bc_num_add(&result2, &temp, &result1, 0);
Gavin Howardc78e7522019-02-22 13:24:25 -07001506 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howard4c605692019-05-08 09:09:17 -06001507 s = bc_num_mulArray(m1, base, m2);
Gavin Howardc78e7522019-02-22 13:24:25 -07001508 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howard4c605692019-05-08 09:09:17 -06001509
1510 ptr = m1;
1511 m1 = m2;
1512 m2 = ptr;
Gavin Howard63738202018-09-26 15:34:20 -06001513 }
Gavin Howardede51f02018-03-02 12:30:00 -07001514
Gavin Howardf2ee6342019-04-09 17:05:20 -06001515 if (BC_SIG) {
1516 s = BC_STATUS_SIGNAL;
1517 goto err;
1518 }
1519
Gavin Howard1769abc2019-02-21 09:36:14 -07001520 // This one cannot be a divide by 0 because mult starts out at 1, then is
1521 // multiplied by base, and base cannot be 0, so mult cannot be 0.
Gavin Howard4c605692019-05-08 09:09:17 -06001522 s = bc_num_div(&result1, m1, &result2, digs * 2);
Gavin Howard1769abc2019-02-21 09:36:14 -07001523 assert(!s || s == BC_STATUS_SIGNAL);
Gavin Howardc78e7522019-02-22 13:24:25 -07001524 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howard4c605692019-05-08 09:09:17 -06001525 bc_num_truncate(&result2, digs);
1526 s = bc_num_add(n, &result2, n, digs);
Gavin Howardc78e7522019-02-22 13:24:25 -07001527 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howardd1417682019-02-15 17:08:42 -07001528
Gavin Howardddce6d42019-01-03 14:07:06 -07001529 if (BC_NUM_NONZERO(n)) {
Gavin Howardb0642be2019-04-30 21:12:22 -06001530 if (n->scale < digs) bc_num_extend(n, digs - n->scale);
Gavin Howard63738202018-09-26 15:34:20 -06001531 }
1532 else bc_num_zero(n);
Gavin Howardede51f02018-03-02 12:30:00 -07001533
Gavin Howardd1417682019-02-15 17:08:42 -07001534err:
Gavin Howard4c605692019-05-08 09:09:17 -06001535 bc_num_free(&result2);
1536 bc_num_free(&result1);
1537 bc_num_free(&mult2);
Gavin Howarddc0ee9e2019-02-16 23:27:08 -07001538int_err:
Gavin Howard4c605692019-05-08 09:09:17 -06001539 bc_num_free(&mult1);
Gavin Howard63738202018-09-26 15:34:20 -06001540 bc_num_free(&temp);
Gavin Howarddc0ee9e2019-02-16 23:27:08 -07001541 return s;
Gavin Howard6fbdb292018-02-27 15:44:48 -07001542}
1543
Gavin Howard1cbfe242019-01-09 17:13:11 -07001544static void bc_num_printNewline() {
Gavin Howard6193aaf2019-01-03 16:44:04 -07001545 if (vm->nchars >= (size_t) (vm->line_len - 1)) {
Gavin Howard48af52e2018-10-30 14:47:38 -06001546 bc_vm_putchar('\\');
1547 bc_vm_putchar('\n');
Gavin Howard63738202018-09-26 15:34:20 -06001548 }
Gavin Howard80977b22018-09-06 14:29:13 -06001549}
1550
Gavin Howardb4a9cdb2019-05-07 15:25:51 -06001551static void bc_num_putchar(int c) {
1552 if (c != '\n') bc_num_printNewline();
1553 bc_vm_putchar(c);
1554}
1555
Gavin Howard40a085f2018-12-03 12:08:59 -07001556#if DC_ENABLED
Gavin Howard1cbfe242019-01-09 17:13:11 -07001557static void bc_num_printChar(size_t n, size_t len, bool rdx) {
Gavin Howard3c02ed32018-12-28 18:17:15 -07001558 BC_UNUSED(rdx);
Gavin Howardeedfb2c2019-05-07 08:00:13 -06001559 BC_UNUSED(len);
1560 assert(len == 1);
Gavin Howard3c02ed32018-12-28 18:17:15 -07001561 bc_vm_putchar((uchar) n);
Gavin Howard83eb8392018-10-09 01:21:19 -06001562}
1563#endif // DC_ENABLED
1564
Gavin Howard1cbfe242019-01-09 17:13:11 -07001565static void bc_num_printDigits(size_t n, size_t len, bool rdx) {
1566
Gavin Howarda84ad992018-12-03 19:11:06 -07001567 size_t exp, pow;
Gavin Howard80977b22018-09-06 14:29:13 -06001568
Gavin Howardb4a9cdb2019-05-07 15:25:51 -06001569 bc_num_putchar(rdx ? '.' : ' ');
Gavin Howard2ed2bfb2018-03-14 02:42:32 -06001570
Gavin Howard530e3072019-04-23 16:23:36 -06001571 for (exp = 0, pow = 1; exp < len - 1; ++exp, pow *= BC_BASE);
Gavin Howard2ed2bfb2018-03-14 02:42:32 -06001572
Gavin Howardeedfb2c2019-05-07 08:00:13 -06001573 for (exp = 0; exp < len; pow /= BC_BASE, ++exp) {
Gavin Howard4b075f22019-05-07 15:27:53 -06001574 size_t dig = n / pow;
Gavin Howard3c02ed32018-12-28 18:17:15 -07001575 n -= dig * pow;
Gavin Howardb4a9cdb2019-05-07 15:25:51 -06001576 bc_num_putchar(((uchar) dig) + '0');
Gavin Howard63738202018-09-26 15:34:20 -06001577 }
Gavin Howard6fbdb292018-02-27 15:44:48 -07001578}
Gavin Howardfe679f02018-02-14 15:50:09 -07001579
Gavin Howard1cbfe242019-01-09 17:13:11 -07001580static void bc_num_printHex(size_t n, size_t len, bool rdx) {
Gavin Howard70e3d602018-12-11 11:00:49 -07001581
Gavin Howardeedfb2c2019-05-07 08:00:13 -06001582 BC_UNUSED(len);
1583
Gavin Howard3c02ed32018-12-28 18:17:15 -07001584 assert(len == 1);
Gavin Howard80977b22018-09-06 14:29:13 -06001585
Gavin Howardb4a9cdb2019-05-07 15:25:51 -06001586 if (rdx) bc_num_putchar('.');
Gavin Howard2682a1f2018-03-03 09:09:13 -07001587
Gavin Howardb4a9cdb2019-05-07 15:25:51 -06001588 bc_num_putchar(bc_num_hex_digits[n]);
Gavin Howard2682a1f2018-03-03 09:09:13 -07001589}
1590
Gavin Howard1cbfe242019-01-09 17:13:11 -07001591static void bc_num_printDecimal(const BcNum *restrict n) {
Gavin Howard32f2beb2018-03-09 11:43:20 -07001592
Gavin Howard51cd7ea2019-04-26 09:48:12 -06001593 size_t i, j, rdx = n->rdx;
1594 bool zero = true;
Gavin Howard92ba4e52019-05-10 20:49:13 -06001595 size_t buffer[BC_BASE_DIGS];
Gavin Howard32f2beb2018-03-09 11:43:20 -07001596
Gavin Howardb4a9cdb2019-05-07 15:25:51 -06001597 if (n->neg) bc_num_putchar('-');
Gavin Howard32f2beb2018-03-09 11:43:20 -07001598
Gavin Howard51cd7ea2019-04-26 09:48:12 -06001599 for (i = n->len - 1; i < n->len; --i) {
1600
1601 BcDig n9 = n->num[i];
1602 size_t temp;
1603 bool irdx = (i == rdx - 1);
1604
1605 zero = (zero & !irdx);
Gavin Howard92ba4e52019-05-10 20:49:13 -06001606 temp = n->scale % BC_BASE_DIGS;
1607 temp = i || !temp ? 0 : BC_BASE_DIGS - temp;
Gavin Howard51cd7ea2019-04-26 09:48:12 -06001608
Gavin Howard92ba4e52019-05-10 20:49:13 -06001609 memset(buffer, 0, BC_BASE_DIGS * sizeof(size_t));
Gavin Howard51cd7ea2019-04-26 09:48:12 -06001610
Gavin Howard92ba4e52019-05-10 20:49:13 -06001611 for (j = 0; n9 && j < BC_BASE_DIGS; ++j) {
Stefan Essera299ffc2019-04-24 23:28:32 +02001612 buffer[j] = n9 % BC_BASE;
1613 n9 /= BC_BASE;
1614 }
Stefan Esser0da17752019-04-25 12:25:15 +02001615
Gavin Howard92ba4e52019-05-10 20:49:13 -06001616 for (j = BC_BASE_DIGS - 1; j < BC_BASE_DIGS && j >= temp; --j) {
1617 bool print_rdx = (irdx & (j == BC_BASE_DIGS - 1));
Gavin Howard51cd7ea2019-04-26 09:48:12 -06001618 zero = (zero && buffer[j] == 0);
1619 if (!zero) bc_num_printHex(buffer[j], 1, print_rdx);
Stefan Essera299ffc2019-04-24 23:28:32 +02001620 }
1621 }
Gavin Howard32f2beb2018-03-09 11:43:20 -07001622}
1623
Gavin Howard7ad5a662019-02-19 14:40:46 -07001624#if BC_ENABLE_EXTRA_MATH
Gavin Howardb1558662019-04-26 14:32:55 -06001625static BcStatus bc_num_printExponent(const BcNum *restrict n, bool eng) {
Gavin Howard7ad5a662019-02-19 14:40:46 -07001626
Gavin Howardb1558662019-04-26 14:32:55 -06001627 BcStatus s = BC_STATUS_SUCCESS;
Gavin Howard7ad5a662019-02-19 14:40:46 -07001628 bool neg = (n->len <= n->rdx);
1629 BcNum temp, exp;
1630 size_t places, mod;
Gavin Howard2956a5e2019-05-08 08:04:06 -06001631 BcDig digs[BC_NUM_BIGDIG_LOG10];
Gavin Howard7ad5a662019-02-19 14:40:46 -07001632
1633 bc_num_createCopy(&temp, n);
1634
1635 if (neg) {
Gavin Howardc1349922019-04-30 20:48:09 -06001636
1637 size_t i, idx = bc_num_nonzeroLen(n) - 1;
1638
1639 places = 1;
1640
Gavin Howard92ba4e52019-05-10 20:49:13 -06001641 for (i = BC_BASE_DIGS - 1; i < BC_BASE_DIGS; --i) {
Gavin Howardc1542802019-05-08 17:20:51 -06001642 if (bc_num_pow10[i] > (BcBigDig) n->num[idx]) places += 1;
Gavin Howardc1349922019-04-30 20:48:09 -06001643 else break;
1644 }
1645
Gavin Howard92ba4e52019-05-10 20:49:13 -06001646 places += (n->rdx - (idx + 1)) * BC_BASE_DIGS;
Gavin Howard7ad5a662019-02-19 14:40:46 -07001647 mod = places % 3;
Gavin Howardc1349922019-04-30 20:48:09 -06001648
Gavin Howard7ad5a662019-02-19 14:40:46 -07001649 if (eng && mod != 0) places += 3 - mod;
Gavin Howardb1558662019-04-26 14:32:55 -06001650 s = bc_num_shiftLeft(&temp, places);
1651 if (BC_ERROR_SIGNAL_ONLY(s)) goto exit;
Gavin Howard7ad5a662019-02-19 14:40:46 -07001652 }
1653 else {
Gavin Howard06fee0c2019-05-09 14:43:20 -06001654 places = bc_num_intDigits(n) - 1;
Gavin Howard7ad5a662019-02-19 14:40:46 -07001655 mod = places % 3;
1656 if (eng && mod != 0) places -= 3 - (3 - mod);
Gavin Howardb1558662019-04-26 14:32:55 -06001657 s = bc_num_shiftRight(&temp, places);
1658 if (BC_ERROR_SIGNAL_ONLY(s)) goto exit;
Gavin Howard7ad5a662019-02-19 14:40:46 -07001659 }
1660
1661 bc_num_printDecimal(&temp);
Gavin Howardb4a9cdb2019-05-07 15:25:51 -06001662 bc_num_putchar('e');
Gavin Howard7ad5a662019-02-19 14:40:46 -07001663
1664 if (!places) {
1665 bc_num_printHex(0, 1, false);
1666 goto exit;
1667 }
1668
Gavin Howardb4a9cdb2019-05-07 15:25:51 -06001669 if (neg) bc_num_putchar('-');
Gavin Howard7ad5a662019-02-19 14:40:46 -07001670
Gavin Howard2956a5e2019-05-08 08:04:06 -06001671 bc_num_setup(&exp, digs, BC_NUM_BIGDIG_LOG10);
1672 bc_num_bigdig2num(&exp, (BcBigDig) places);
Gavin Howard7ad5a662019-02-19 14:40:46 -07001673
1674 bc_num_printDecimal(&exp);
1675
1676exit:
1677 bc_num_free(&temp);
Gavin Howardb1558662019-04-26 14:32:55 -06001678 return BC_SIG ? BC_STATUS_SIGNAL : BC_STATUS_SUCCESS;
Gavin Howard7ad5a662019-02-19 14:40:46 -07001679}
1680#endif // BC_ENABLE_EXTRA_MATH
1681
Gavin Howardad80c522019-05-18 20:11:15 -06001682static BcStatus bc_num_printFixup(BcNum *restrict n, BcBigDig rem,
1683 BcBigDig pow, size_t idx)
1684{
Gavin Howardf5ced072019-05-18 19:46:10 -06001685 size_t i, len = n->len - idx;
Stefan Esserba6a9bb2019-05-17 19:38:59 +02001686 BcBigDig acc;
Gavin Howarda80836d2019-05-18 19:18:04 -06001687 BcDig *a = n->num + idx;
Stefan Esserba6a9bb2019-05-17 19:38:59 +02001688
Gavin Howard2f3eb0b2019-05-18 20:01:35 -06001689 if (len < 2) return BC_STATUS_SUCCESS;
Gavin Howardbe0fb122019-05-18 19:26:38 -06001690
Gavin Howard2f3eb0b2019-05-18 20:01:35 -06001691 for (i = len - 1; BC_NO_SIG && i > 0; --i) {
Gavin Howardbe0fb122019-05-18 19:26:38 -06001692
Gavin Howardad80c522019-05-18 20:11:15 -06001693 acc = ((BcBigDig) a[i]) * rem + ((BcBigDig) a[i - 1]);
1694 a[i - 1] = (BcDig) (acc % pow);
1695 acc /= pow;
Gavin Howardf5ced072019-05-18 19:46:10 -06001696 acc += (BcBigDig) a[i];
Gavin Howardbe0fb122019-05-18 19:26:38 -06001697
Stefan Esserba6a9bb2019-05-17 19:38:59 +02001698 if (acc >= BC_BASE_POW) {
Gavin Howardbe0fb122019-05-18 19:26:38 -06001699
Gavin Howarddd42b4b2019-05-18 18:30:46 -06001700 if (i == len - 1) {
Gavin Howardd0d31972019-05-18 20:13:27 -06001701 len = bc_vm_growSize(len, 1);
1702 bc_num_expand(n, bc_vm_growSize(len, idx));
Gavin Howarda80836d2019-05-18 19:18:04 -06001703 a = n->num + idx;
Gavin Howardd0d31972019-05-18 20:13:27 -06001704 a[len - 1] = 0;
Stefan Esserba6a9bb2019-05-17 19:38:59 +02001705 }
Gavin Howardbe0fb122019-05-18 19:26:38 -06001706
Stefan Esserba6a9bb2019-05-17 19:38:59 +02001707 a[i + 1] += acc / BC_BASE_POW;
1708 acc %= BC_BASE_POW;
1709 }
Gavin Howardbe0fb122019-05-18 19:26:38 -06001710
Gavin Howardf5ced072019-05-18 19:46:10 -06001711 assert(acc < BC_BASE_POW);
1712 a[i] = (BcDig) acc;
Stefan Esserba6a9bb2019-05-17 19:38:59 +02001713 }
Gavin Howardf5ced072019-05-18 19:46:10 -06001714
Gavin Howard2f3eb0b2019-05-18 20:01:35 -06001715 n->len = len + idx;
1716
1717 return BC_SIG ? BC_STATUS_SIGNAL : BC_STATUS_SUCCESS;
Stefan Esserba6a9bb2019-05-17 19:38:59 +02001718}
1719
Gavin Howardad80c522019-05-18 20:11:15 -06001720static BcStatus bc_num_printPrepare(BcNum *restrict n, BcBigDig rem,
1721 BcBigDig pow)
1722{
Gavin Howard2f3eb0b2019-05-18 20:01:35 -06001723 BcStatus s = BC_STATUS_SUCCESS;
Stefan Esserba6a9bb2019-05-17 19:38:59 +02001724 size_t i;
1725
Gavin Howard2f3eb0b2019-05-18 20:01:35 -06001726 for (i = 0; BC_NO_SIG && BC_NO_ERR(!s) && i < n->len; ++i)
Gavin Howardad80c522019-05-18 20:11:15 -06001727 s = bc_num_printFixup(n, rem, pow, i);
Gavin Howardbe0fb122019-05-18 19:26:38 -06001728
Gavin Howard2f3eb0b2019-05-18 20:01:35 -06001729 if (BC_ERR(s)) return s;
1730
1731 for (i = 0; BC_NO_SIG && i < n->len; ++i) {
1732
Gavin Howardad80c522019-05-18 20:11:15 -06001733 assert(pow == ((BcBigDig) ((BcDig) pow)));
Gavin Howardbe0fb122019-05-18 19:26:38 -06001734
Gavin Howardad80c522019-05-18 20:11:15 -06001735 if (n->num[i] >= (BcDig) pow) {
Gavin Howardbe0fb122019-05-18 19:26:38 -06001736
Stefan Esserba6a9bb2019-05-17 19:38:59 +02001737 if (i + 1 == n->len) {
Gavin Howardd0d31972019-05-18 20:13:27 -06001738 n->len = bc_vm_growSize(n->len, 1);
1739 bc_num_expand(n, n->len);
Stefan Esserba6a9bb2019-05-17 19:38:59 +02001740 n->num[i + 1] = 0;
Stefan Esserba6a9bb2019-05-17 19:38:59 +02001741 }
Gavin Howardbe0fb122019-05-18 19:26:38 -06001742
Gavin Howardaf556c32019-05-18 20:14:44 -06001743 assert(pow < BC_BASE_POW);
Gavin Howardad80c522019-05-18 20:11:15 -06001744 n->num[i + 1] += n->num[i] / ((BcDig) pow);
1745 n->num[i] %= (BcDig) pow;
Stefan Esserba6a9bb2019-05-17 19:38:59 +02001746 }
1747 }
Gavin Howard2f3eb0b2019-05-18 20:01:35 -06001748
1749 return BC_NO_ERR(!s) && BC_SIG ? BC_STATUS_SIGNAL : BC_STATUS_SUCCESS;
Stefan Esserba6a9bb2019-05-17 19:38:59 +02001750}
Stefan Esserba6a9bb2019-05-17 19:38:59 +02001751
Gavin Howard2956a5e2019-05-08 08:04:06 -06001752static BcStatus bc_num_printNum(BcNum *restrict n, BcBigDig base,
Gavin Howarddc0ee9e2019-02-16 23:27:08 -07001753 size_t len, BcNumDigitOp print)
Gavin Howardbc7cae82018-03-14 13:43:04 -06001754{
Gavin Howard63738202018-09-26 15:34:20 -06001755 BcStatus s;
1756 BcVec stack;
Gavin Howardcaf4ca12019-05-18 19:49:32 -06001757 BcNum intp, fracp1, fracp2, digit, flen1, flen2, *n1, *n2, *temp;
Gavin Howard2f3eb0b2019-05-18 20:01:35 -06001758 BcBigDig dig, *ptr, acc, exp;
Gavin Howardefd84652019-05-18 19:36:33 -06001759 size_t i, j;
Gavin Howard83eb8392018-10-09 01:21:19 -06001760 bool radix;
Gavin Howard1705b982019-05-11 15:18:03 -06001761 BcDig digit_digs[BC_NUM_BIGDIG_LOG10 + 1];
Gavin Howard2682a1f2018-03-03 09:09:13 -07001762
Gavin Howardb3f1ee22019-05-07 21:49:15 -06001763 assert(base > 1);
Gavin Howard3fb24fa2019-02-15 17:18:57 -07001764
Gavin Howardddce6d42019-01-03 14:07:06 -07001765 if (BC_NUM_ZERO(n)) {
1766 print(0, len, false);
Gavin Howarddc0ee9e2019-02-16 23:27:08 -07001767 return BC_STATUS_SUCCESS;
Gavin Howard48af52e2018-10-30 14:47:38 -06001768 }
Gavin Howard9a4b6cd2018-10-23 15:13:30 -06001769
Gavin Howard2956a5e2019-05-08 08:04:06 -06001770 bc_vec_init(&stack, sizeof(BcBigDig), NULL);
Gavin Howarda8c2e582019-05-07 11:33:10 -06001771 bc_num_init(&fracp1, n->rdx);
Gavin Howard65d278b2019-05-18 19:22:58 -06001772
Gavin Howardcaf4ca12019-05-18 19:49:32 -06001773 bc_num_createCopy(&intp, n);
1774 bc_num_truncate(&intp, intp.scale);
Gavin Howard65d278b2019-05-18 19:22:58 -06001775
Gavin Howardcaf4ca12019-05-18 19:49:32 -06001776 s = bc_num_sub(n, &intp, &fracp1, 0);
Gavin Howard65d278b2019-05-18 19:22:58 -06001777 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
1778
Gavin Howardefd84652019-05-18 19:36:33 -06001779 if (base != vm->last_base) {
Gavin Howardbe0fb122019-05-18 19:26:38 -06001780
Gavin Howardefd84652019-05-18 19:36:33 -06001781 vm->last_pow = 1;
1782 vm->last_exp = 0;
Gavin Howardbe0fb122019-05-18 19:26:38 -06001783
Gavin Howardefd84652019-05-18 19:36:33 -06001784 while (vm->last_pow * base <= BC_BASE_POW) {
1785 vm->last_pow *= base;
1786 vm->last_exp += 1;
Gavin Howard30fe4cb2019-05-18 18:33:55 -06001787 }
1788
Gavin Howardefd84652019-05-18 19:36:33 -06001789 vm->last_rem = BC_BASE_POW - vm->last_pow;
1790 vm->last_base = base;
1791 }
Stefan Esserba6a9bb2019-05-17 19:38:59 +02001792
Gavin Howard2f3eb0b2019-05-18 20:01:35 -06001793 exp = vm->last_exp;
Gavin Howardbe0fb122019-05-18 19:26:38 -06001794
Gavin Howard2f3eb0b2019-05-18 20:01:35 -06001795 if (vm->last_rem != 0) {
Gavin Howardad80c522019-05-18 20:11:15 -06001796 s = bc_num_printPrepare(&intp, vm->last_rem, vm->last_pow);
Gavin Howard2f3eb0b2019-05-18 20:01:35 -06001797 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
1798 }
1799
1800 for (i = 0; BC_NO_SIG && i < intp.len; ++i) {
Gavin Howardbe0fb122019-05-18 19:26:38 -06001801
Gavin Howardcaf4ca12019-05-18 19:49:32 -06001802 acc = (BcBigDig) intp.num[i];
Gavin Howardbe0fb122019-05-18 19:26:38 -06001803
Gavin Howard2f3eb0b2019-05-18 20:01:35 -06001804 for (j = 0; BC_NO_SIG && j < exp && (i < intp.len - 1 || acc != 0); ++j)
1805 {
Gavin Howardefd84652019-05-18 19:36:33 -06001806 dig = acc % base;
1807 acc /= base;
1808 bc_vec_push(&stack, &dig);
Stefan Esserba6a9bb2019-05-17 19:38:59 +02001809 }
Gavin Howardefd84652019-05-18 19:36:33 -06001810
Gavin Howard2f3eb0b2019-05-18 20:01:35 -06001811 assert(acc == 0 || BC_SIG);
Gavin Howard30fe4cb2019-05-18 18:33:55 -06001812 }
Gavin Howard2682a1f2018-03-03 09:09:13 -07001813
Gavin Howard2d188a52019-02-25 14:19:08 -07001814 if (BC_SIG) goto sig_err;
Gavin Howarddc0ee9e2019-02-16 23:27:08 -07001815
Gavin Howard2d188a52019-02-25 14:19:08 -07001816 for (i = 0; BC_NO_SIG && i < stack.len; ++i) {
Gavin Howard63738202018-09-26 15:34:20 -06001817 ptr = bc_vec_item_rev(&stack, i);
1818 assert(ptr);
Gavin Howardddce6d42019-01-03 14:07:06 -07001819 print(*ptr, len, false);
Gavin Howard63738202018-09-26 15:34:20 -06001820 }
Gavin Howard2682a1f2018-03-03 09:09:13 -07001821
Gavin Howard2d188a52019-02-25 14:19:08 -07001822 if (BC_SIG) goto sig_err;
Stefan Esser276de8c2019-05-04 20:12:43 +02001823 if (!n->scale) goto err;
Gavin Howard2682a1f2018-03-03 09:09:13 -07001824
Gavin Howardcaf4ca12019-05-18 19:49:32 -06001825 bc_num_init(&fracp2, n->rdx);
1826 bc_num_setup(&digit, digit_digs, sizeof(digit_digs) / sizeof(BcDig));
1827 bc_num_init(&flen1, BC_NUM_BIGDIG_LOG10 + 1);
1828 bc_num_init(&flen2, BC_NUM_BIGDIG_LOG10 + 1);
1829 bc_num_one(&flen1);
1830
Gavin Howardfbae8a52019-05-07 11:15:38 -06001831 radix = true;
Gavin Howarda8c2e582019-05-07 11:33:10 -06001832 n1 = &flen1;
1833 n2 = &flen2;
Gavin Howardfbae8a52019-05-07 11:15:38 -06001834
Gavin Howard06fee0c2019-05-09 14:43:20 -06001835 while (BC_NO_SIG && bc_num_intDigits(n1) < n->scale + 1) {
Gavin Howarddc0ee9e2019-02-16 23:27:08 -07001836
Gavin Howardb3f1ee22019-05-07 21:49:15 -06001837 bc_num_expand(&fracp2, fracp1.len + 1);
1838 s = bc_num_mulArray(&fracp1, base, &fracp2);
Gavin Howardcaf4ca12019-05-18 19:49:32 -06001839 if (BC_ERROR_SIGNAL_ONLY(s)) goto frac_err;
Gavin Howardb3f1ee22019-05-07 21:49:15 -06001840 fracp2.scale = n->scale;
1841 fracp2.rdx = BC_NUM_RDX(fracp2.scale);
Gavin Howard1769abc2019-02-21 09:36:14 -07001842
1843 // Will never fail (except for signals) because fracp is
1844 // guaranteed to be non-negative and small enough.
Gavin Howard2956a5e2019-05-08 08:04:06 -06001845 s = bc_num_bigdig(&fracp2, &dig);
Gavin Howardcaf4ca12019-05-18 19:49:32 -06001846 if (BC_ERROR_SIGNAL_ONLY(s)) goto frac_err;
Gavin Howarddc0ee9e2019-02-16 23:27:08 -07001847
Gavin Howard2956a5e2019-05-08 08:04:06 -06001848 bc_num_bigdig2num(&digit, dig);
Gavin Howard43b6fd72019-05-07 11:39:19 -06001849 s = bc_num_sub(&fracp2, &digit, &fracp1, 0);
Gavin Howardcaf4ca12019-05-18 19:49:32 -06001850 if (BC_ERROR_SIGNAL_ONLY(s)) goto frac_err;
Gavin Howarddc0ee9e2019-02-16 23:27:08 -07001851
Gavin Howardddce6d42019-01-03 14:07:06 -07001852 print(dig, len, radix);
Gavin Howardb3f1ee22019-05-07 21:49:15 -06001853 s = bc_num_mulArray(n1, base, n2);
Gavin Howardcaf4ca12019-05-18 19:49:32 -06001854 if (BC_ERROR_SIGNAL_ONLY(s)) goto frac_err;
Gavin Howardfbae8a52019-05-07 11:15:38 -06001855
1856 radix = false;
Gavin Howarda8c2e582019-05-07 11:33:10 -06001857 temp = n1;
1858 n1 = n2;
1859 n2 = temp;
Gavin Howard63738202018-09-26 15:34:20 -06001860 }
Gavin Howard2682a1f2018-03-03 09:09:13 -07001861
Gavin Howardcaf4ca12019-05-18 19:49:32 -06001862frac_err:
Gavin Howardf1148822019-05-11 15:29:24 -06001863 bc_num_free(&flen2);
1864 bc_num_free(&flen1);
Gavin Howarda8c2e582019-05-07 11:33:10 -06001865 bc_num_free(&fracp2);
Gavin Howardcaf4ca12019-05-18 19:49:32 -06001866sig_err:
1867 if (BC_NO_ERR(!s) && BC_SIG) s = BC_STATUS_SIGNAL;
1868err:
Gavin Howarda8c2e582019-05-07 11:33:10 -06001869 bc_num_free(&fracp1);
Gavin Howardcaf4ca12019-05-18 19:49:32 -06001870 bc_num_free(&intp);
Gavin Howard63738202018-09-26 15:34:20 -06001871 bc_vec_free(&stack);
Gavin Howarddc0ee9e2019-02-16 23:27:08 -07001872 return s;
Gavin Howard2682a1f2018-03-03 09:09:13 -07001873}
1874
Gavin Howard2956a5e2019-05-08 08:04:06 -06001875static BcStatus bc_num_printBase(BcNum *restrict n, BcBigDig base) {
Gavin Howardb3f1ee22019-05-07 21:49:15 -06001876
Gavin Howarddc0ee9e2019-02-16 23:27:08 -07001877 BcStatus s;
Gavin Howard1ab22d22019-01-03 13:32:17 -07001878 size_t width;
Gavin Howard83eb8392018-10-09 01:21:19 -06001879 BcNumDigitOp print;
1880 bool neg = n->neg;
1881
Gavin Howardb4a9cdb2019-05-07 15:25:51 -06001882 if (neg) bc_num_putchar('-');
Gavin Howard83eb8392018-10-09 01:21:19 -06001883
1884 n->neg = false;
1885
Gavin Howardb3f1ee22019-05-07 21:49:15 -06001886 if (base <= BC_NUM_MAX_POSIX_IBASE) {
Gavin Howard83eb8392018-10-09 01:21:19 -06001887 width = 1;
1888 print = bc_num_printHex;
1889 }
1890 else {
Gavin Howardb3f1ee22019-05-07 21:49:15 -06001891 width = bc_num_log10(base - 1);
Gavin Howard83eb8392018-10-09 01:21:19 -06001892 print = bc_num_printDigits;
1893 }
1894
Gavin Howarddc0ee9e2019-02-16 23:27:08 -07001895 s = bc_num_printNum(n, base, width, print);
Gavin Howard83eb8392018-10-09 01:21:19 -06001896 n->neg = neg;
Gavin Howarddc0ee9e2019-02-16 23:27:08 -07001897
1898 return s;
Gavin Howard83eb8392018-10-09 01:21:19 -06001899}
1900
Gavin Howard40a085f2018-12-03 12:08:59 -07001901#if DC_ENABLED
Gavin Howard2956a5e2019-05-08 08:04:06 -06001902BcStatus bc_num_stream(BcNum *restrict n, BcBigDig base) {
Gavin Howarddc0ee9e2019-02-16 23:27:08 -07001903 return bc_num_printNum(n, base, 1, bc_num_printChar);
Gavin Howard83eb8392018-10-09 01:21:19 -06001904}
1905#endif // DC_ENABLED
1906
Gavin Howard3c02ed32018-12-28 18:17:15 -07001907void bc_num_setup(BcNum *restrict n, BcDig *restrict num, size_t cap) {
Gavin Howardb8a12922018-12-21 21:40:45 -07001908 assert(n);
1909 n->num = num;
1910 n->cap = cap;
Gavin Howardfcf29012019-04-25 20:12:19 -06001911 n->rdx = n->scale = n->len = 0;
Gavin Howardb8a12922018-12-21 21:40:45 -07001912 n->neg = false;
1913}
1914
Gavin Howard3c02ed32018-12-28 18:17:15 -07001915void bc_num_init(BcNum *restrict n, size_t req) {
Gavin Howard63738202018-09-26 15:34:20 -06001916 assert(n);
Gavin Howard53eba8b2018-10-31 15:14:37 -06001917 req = req >= BC_NUM_DEF_SIZE ? req : BC_NUM_DEF_SIZE;
Gavin Howard404ece72019-04-26 15:59:15 -06001918 bc_num_setup(n, bc_vm_malloc(BC_NUM_SIZE(req)), req);
Gavin Howardb5c77212018-02-14 17:12:34 -07001919}
1920
Gavin Howarded392aa2018-02-27 13:09:26 -07001921void bc_num_free(void *num) {
Gavin Howardad0ecfe2018-10-30 01:16:01 -06001922 assert(num);
1923 free(((BcNum*) num)->num);
Gavin Howardb5c77212018-02-14 17:12:34 -07001924}
1925
Gavin Howard3c02ed32018-12-28 18:17:15 -07001926void bc_num_copy(BcNum *d, const BcNum *s) {
Gavin Howard63738202018-09-26 15:34:20 -06001927 assert(d && s);
Gavin Howard7344ba72019-01-03 13:37:27 -07001928 if (d == s) return;
Gavin Howarddcff3c92019-01-09 10:04:56 -07001929 bc_num_expand(d, s->len);
Gavin Howard7344ba72019-01-03 13:37:27 -07001930 d->len = s->len;
1931 d->neg = s->neg;
1932 d->rdx = s->rdx;
Gavin Howardfcf29012019-04-25 20:12:19 -06001933 d->scale = s->scale;
Gavin Howard404ece72019-04-26 15:59:15 -06001934 memcpy(d->num, s->num, BC_NUM_SIZE(d->len));
Gavin Howard5a049c42018-02-15 11:24:11 -07001935}
1936
Gavin Howardbaa4f582019-01-24 13:56:35 -07001937void bc_num_createCopy(BcNum *d, const BcNum *s) {
1938 bc_num_init(d, s->len);
1939 bc_num_copy(d, s);
1940}
1941
Gavin Howard2956a5e2019-05-08 08:04:06 -06001942void bc_num_createFromBigdig(BcNum *n, BcBigDig val) {
Gavin Howard92ba4e52019-05-10 20:49:13 -06001943 bc_num_init(n, (BC_NUM_BIGDIG_LOG10 - 1) / BC_BASE_DIGS + 1);
Gavin Howard2956a5e2019-05-08 08:04:06 -06001944 bc_num_bigdig2num(n, val);
Gavin Howardbaa4f582019-01-24 13:56:35 -07001945}
1946
Gavin Howard7ad5a662019-02-19 14:40:46 -07001947size_t bc_num_scale(const BcNum *restrict n) {
Gavin Howardfcf29012019-04-25 20:12:19 -06001948 return n->scale;
Gavin Howard7ad5a662019-02-19 14:40:46 -07001949}
1950
1951size_t bc_num_len(const BcNum *restrict n) {
1952
Gavin Howard30ed6402019-05-14 09:00:55 -06001953 size_t len = n->len;
Gavin Howardfcf29012019-04-25 20:12:19 -06001954
Gavin Howardb41483b2019-04-27 08:30:10 -06001955 if (BC_NUM_ZERO(n)) return 0;
Gavin Howard7106c252019-05-14 08:54:07 -06001956 if (n->rdx == len) {
Gavin Howard7ad5a662019-02-19 14:40:46 -07001957
Gavin Howard30ed6402019-05-14 09:00:55 -06001958 size_t zero, scale;
1959
Gavin Howard7106c252019-05-14 08:54:07 -06001960 len = bc_num_nonzeroLen(n);
Gavin Howardb41483b2019-04-27 08:30:10 -06001961
Gavin Howard7106c252019-05-14 08:54:07 -06001962 scale = n->scale % BC_BASE_DIGS;
1963 scale = scale ? scale : BC_BASE_DIGS;
Gavin Howard0c593dd2019-05-14 08:50:09 -06001964
Gavin Howard7106c252019-05-14 08:54:07 -06001965 zero = bc_num_zeroDigits(n->num + len - 1);
1966
1967 len = len * BC_BASE_DIGS - zero - (BC_BASE_DIGS - scale);
1968 }
1969 else len = bc_num_intDigits(n) + n->scale;
1970
1971 return len;
Gavin Howard7ad5a662019-02-19 14:40:46 -07001972}
1973
Gavin Howarddc0ee9e2019-02-16 23:27:08 -07001974BcStatus bc_num_parse(BcNum *restrict n, const char *restrict val,
Gavin Howard2956a5e2019-05-08 08:04:06 -06001975 BcBigDig base, bool letter)
Gavin Howard3c02ed32018-12-28 18:17:15 -07001976{
Gavin Howarddc0ee9e2019-02-16 23:27:08 -07001977 BcStatus s = BC_STATUS_SUCCESS;
1978
Gavin Howard63738202018-09-26 15:34:20 -06001979 assert(n && val && base);
Gavin Howard42e4f052019-05-18 19:01:19 -06001980 assert(base >= BC_NUM_MIN_BASE && base <= vm->maxes[BC_PROG_GLOBALS_IBASE]);
Gavin Howardc90ed902019-01-02 13:07:49 -07001981 assert(bc_num_strValid(val));
Gavin Howard3eb626f2018-02-14 13:54:35 -07001982
Gavin Howard2956a5e2019-05-08 08:04:06 -06001983 if (letter) {
1984 BcBigDig dig = bc_num_parseChar(val[0], BC_NUM_MAX_LBASE);
1985 bc_num_bigdig2num(n, dig);
1986 }
Gavin Howardb3f1ee22019-05-07 21:49:15 -06001987 else if (base == BC_BASE) bc_num_parseDecimal(n, val);
1988 else s = bc_num_parseBase(n, val, base);
Gavin Howarddc0ee9e2019-02-16 23:27:08 -07001989
1990 return s;
Gavin Howard3eb626f2018-02-14 13:54:35 -07001991}
1992
Gavin Howard2956a5e2019-05-08 08:04:06 -06001993BcStatus bc_num_print(BcNum *restrict n, BcBigDig base, bool newline) {
Gavin Howardb3f1ee22019-05-07 21:49:15 -06001994
Gavin Howarddc0ee9e2019-02-16 23:27:08 -07001995 BcStatus s = BC_STATUS_SUCCESS;
1996
Gavin Howardb3f1ee22019-05-07 21:49:15 -06001997 assert(n);
1998 assert(BC_ENABLE_EXTRA_MATH || base >= BC_NUM_MIN_BASE);
Gavin Howard3eb626f2018-02-14 13:54:35 -07001999
Gavin Howardab5e9632019-01-02 13:32:02 -07002000 bc_num_printNewline();
Gavin Howardbc7cae82018-03-14 13:43:04 -06002001
Gavin Howardddce6d42019-01-03 14:07:06 -07002002 if (BC_NUM_ZERO(n)) bc_num_printHex(0, 1, false);
Gavin Howardb3f1ee22019-05-07 21:49:15 -06002003 else if (base == BC_BASE) bc_num_printDecimal(n);
Gavin Howard7ad5a662019-02-19 14:40:46 -07002004#if BC_ENABLE_EXTRA_MATH
Gavin Howardb3f1ee22019-05-07 21:49:15 -06002005 else if (base == 0 || base == 1)
2006 s = bc_num_printExponent(n, base != 0);
Gavin Howard7ad5a662019-02-19 14:40:46 -07002007#endif // BC_ENABLE_EXTRA_MATH
Gavin Howardb3f1ee22019-05-07 21:49:15 -06002008 else s = bc_num_printBase(n, base);
Gavin Howard3eb626f2018-02-14 13:54:35 -07002009
Gavin Howardb4a9cdb2019-05-07 15:25:51 -06002010 if (BC_NO_ERR(!s) && newline) bc_num_putchar('\n');
Gavin Howarddc0ee9e2019-02-16 23:27:08 -07002011
2012 return s;
Gavin Howard3eb626f2018-02-14 13:54:35 -07002013}
2014
Gavin Howard2956a5e2019-05-08 08:04:06 -06002015BcStatus bc_num_bigdig(const BcNum *restrict n, BcBigDig *result) {
Gavin Howard68b8a5c2018-02-15 11:41:24 -07002016
Gavin Howard63738202018-09-26 15:34:20 -06002017 size_t i;
Gavin Howard2956a5e2019-05-08 08:04:06 -06002018 BcBigDig r;
Gavin Howard68b8a5c2018-02-15 11:41:24 -07002019
Gavin Howard63738202018-09-26 15:34:20 -06002020 assert(n && result);
Gavin Howard68b8a5c2018-02-15 11:41:24 -07002021
Gavin Howardecafd4f2019-02-23 09:30:45 -07002022 if (BC_ERR(n->neg)) return bc_vm_err(BC_ERROR_MATH_NEGATIVE);
Gavin Howard68b8a5c2018-02-15 11:41:24 -07002023
Gavin Howard06b6e862019-02-15 11:15:31 -07002024 for (r = 0, i = n->len; i > n->rdx;) {
Gavin Howard5bede412019-02-23 09:31:28 -07002025
Gavin Howard92ba4e52019-05-10 20:49:13 -06002026 BcBigDig prev = r * BC_BASE_POW;
Gavin Howard5bede412019-02-23 09:31:28 -07002027
Gavin Howard4fb37202019-05-11 14:49:13 -06002028 if (BC_ERR(prev / BC_BASE_POW != r))
Gavin Howard5bede412019-02-23 09:31:28 -07002029 return bc_vm_err(BC_ERROR_MATH_OVERFLOW);
2030
Gavin Howard2956a5e2019-05-08 08:04:06 -06002031 r = prev + (BcBigDig) n->num[--i];
Gavin Howard5bede412019-02-23 09:31:28 -07002032
Gavin Howard4fb37202019-05-11 14:49:13 -06002033 if (BC_ERR(r < prev)) return bc_vm_err(BC_ERROR_MATH_OVERFLOW);
Gavin Howard63738202018-09-26 15:34:20 -06002034 }
Gavin Howard68b8a5c2018-02-15 11:41:24 -07002035
Gavin Howard7b557da2018-12-21 10:25:32 -07002036 *result = r;
Gavin Howardd3a1c392018-12-11 12:00:57 -07002037
Gavin Howard63738202018-09-26 15:34:20 -06002038 return BC_STATUS_SUCCESS;
Gavin Howard68b8a5c2018-02-15 11:41:24 -07002039}
2040
Gavin Howard2956a5e2019-05-08 08:04:06 -06002041void bc_num_bigdig2num(BcNum *restrict n, BcBigDig val) {
Gavin Howard8e2cc692018-02-15 17:39:14 -07002042
Gavin Howardc25fd612019-04-26 19:31:31 -06002043 BcDig *ptr;
Gavin Howard2956a5e2019-05-08 08:04:06 -06002044 size_t i;
Gavin Howardc25fd612019-04-26 19:31:31 -06002045
Gavin Howard63738202018-09-26 15:34:20 -06002046 assert(n);
Gavin Howard8e2cc692018-02-15 17:39:14 -07002047
Gavin Howard63738202018-09-26 15:34:20 -06002048 bc_num_zero(n);
Gavin Howard025d04d2018-02-20 13:53:28 -07002049
Gavin Howard1ab22d22019-01-03 13:32:17 -07002050 if (!val) return;
Gavin Howard8e2cc692018-02-15 17:39:14 -07002051
Gavin Howard2956a5e2019-05-08 08:04:06 -06002052 bc_num_expand(n, BC_NUM_BIGDIG_LOG10);
Gavin Howard05feab02019-04-23 16:24:07 -06002053
Gavin Howard5c2ab902019-05-15 10:12:57 -06002054 for (ptr = n->num, i = 0; val; ++i, val /= BC_BASE_POW)
Gavin Howard92ba4e52019-05-10 20:49:13 -06002055 ptr[i] = val % BC_BASE_POW;
Gavin Howard5c2ab902019-05-15 10:12:57 -06002056
2057 n->len = i;
Gavin Howard025d04d2018-02-20 13:53:28 -07002058}
2059
Gavin Howardd18fa6d2019-01-24 13:49:35 -07002060size_t bc_num_addReq(BcNum *a, BcNum *b, size_t scale) {
Gavin Howard1973d612019-02-21 15:37:32 -07002061
2062 size_t aint, bint, ardx, brdx;
2063
Gavin Howardd18fa6d2019-01-24 13:49:35 -07002064 BC_UNUSED(scale);
Gavin Howard1973d612019-02-21 15:37:32 -07002065
2066 ardx = a->rdx;
2067 brdx = b->rdx;
Gavin Howard7b7d2f32019-02-21 16:46:47 -07002068 aint = bc_num_int(a);
2069 bint = bc_num_int(b);
Gavin Howard2d188a52019-02-25 14:19:08 -07002070 ardx = BC_MAX(ardx, brdx);
2071 aint = BC_MAX(aint, bint);
Gavin Howard1973d612019-02-21 15:37:32 -07002072
Gavin Howard2d188a52019-02-25 14:19:08 -07002073 return bc_vm_growSize(bc_vm_growSize(ardx, aint), 1);
Gavin Howardd18fa6d2019-01-24 13:49:35 -07002074}
2075
2076size_t bc_num_mulReq(BcNum *a, BcNum *b, size_t scale) {
Gavin Howard2d188a52019-02-25 14:19:08 -07002077 size_t max, rdx;
2078 rdx = bc_vm_growSize(a->rdx, b->rdx);
Gavin Howardd7883272019-05-10 20:04:23 -06002079 max = BC_NUM_RDX(scale);
2080 max = bc_vm_growSize(BC_MAX(max, rdx), 1);
Gavin Howard2d188a52019-02-25 14:19:08 -07002081 rdx = bc_vm_growSize(bc_vm_growSize(bc_num_int(a), bc_num_int(b)), max);
2082 return rdx;
Gavin Howardd18fa6d2019-01-24 13:49:35 -07002083}
2084
2085size_t bc_num_powReq(BcNum *a, BcNum *b, size_t scale) {
2086 BC_UNUSED(scale);
Gavin Howard007afdf2019-02-23 09:45:42 -07002087 return bc_vm_growSize(bc_vm_growSize(a->len, b->len), 1);
Gavin Howardd18fa6d2019-01-24 13:49:35 -07002088}
2089
2090#if BC_ENABLE_EXTRA_MATH
Gavin Howard194380e2019-05-06 08:39:30 -06002091size_t bc_num_placesReq(BcNum *a, BcNum *b, size_t scale) {
2092
2093 BcStatus s;
Gavin Howardea1ae3c2019-05-10 18:44:52 -06002094 BcBigDig places = 0;
Gavin Howard194380e2019-05-06 08:39:30 -06002095 size_t rdx;
2096
2097 BC_UNUSED(s);
Gavin Howardd18fa6d2019-01-24 13:49:35 -07002098 BC_UNUSED(scale);
Gavin Howard194380e2019-05-06 08:39:30 -06002099
Gavin Howard194380e2019-05-06 08:39:30 -06002100 // This error will be taken care of later. Ignore.
Gavin Howard2956a5e2019-05-08 08:04:06 -06002101 s = bc_num_bigdig(b, &places);
Gavin Howard194380e2019-05-06 08:39:30 -06002102
2103 if (a->scale <= places) rdx = BC_NUM_RDX(places);
2104 else rdx = BC_NUM_RDX(a->scale - places);
2105
Gavin Howard06fee0c2019-05-09 14:43:20 -06002106 return BC_NUM_RDX(bc_num_intDigits(a)) + rdx;
Gavin Howard194380e2019-05-06 08:39:30 -06002107}
2108
2109size_t bc_num_shiftLeftReq(BcNum *a, BcNum *b, size_t scale) {
2110
2111 BcStatus s;
Gavin Howard687cf4d2019-05-11 08:55:36 -06002112 BcBigDig places = 0;
2113 size_t rdx;
Gavin Howard194380e2019-05-06 08:39:30 -06002114
2115 BC_UNUSED(s);
2116 BC_UNUSED(scale);
2117
Gavin Howard194380e2019-05-06 08:39:30 -06002118 // This error will be taken care of later. Ignore.
Gavin Howard2956a5e2019-05-08 08:04:06 -06002119 s = bc_num_bigdig(b, &places);
Gavin Howard194380e2019-05-06 08:39:30 -06002120
2121 if (a->scale <= places) rdx = BC_NUM_RDX(places) - a->rdx + 1;
2122 else rdx = 0;
2123
2124 return a->len + rdx;
2125}
2126
2127size_t bc_num_shiftRightReq(BcNum *a, BcNum *b, size_t scale) {
2128
2129 BcStatus s;
Gavin Howard687cf4d2019-05-11 08:55:36 -06002130 BcBigDig places = 0;
2131 size_t int_digs, rdx;
Gavin Howard194380e2019-05-06 08:39:30 -06002132
2133 BC_UNUSED(s);
2134 BC_UNUSED(scale);
2135
Gavin Howard194380e2019-05-06 08:39:30 -06002136 // This error will be taken care of later. Ignore.
Gavin Howard2956a5e2019-05-08 08:04:06 -06002137 s = bc_num_bigdig(b, &places);
Gavin Howard194380e2019-05-06 08:39:30 -06002138
Gavin Howard06fee0c2019-05-09 14:43:20 -06002139 int_digs = BC_NUM_RDX(bc_num_intDigits(a));
Gavin Howard194380e2019-05-06 08:39:30 -06002140 rdx = BC_NUM_RDX(places);
2141
2142 if (int_digs <= rdx) rdx -= int_digs;
2143 else rdx = 0;
2144
2145 return a->len + rdx;
Gavin Howardd18fa6d2019-01-24 13:49:35 -07002146}
2147#endif // BC_ENABLE_EXTRA_MATH
2148
Gavin Howard12fe7812018-09-29 03:45:18 -06002149BcStatus bc_num_add(BcNum *a, BcNum *b, BcNum *c, size_t scale) {
Gavin Howard54b946a2018-10-23 12:06:57 -06002150 BcNumBinaryOp op = (!a->neg == !b->neg) ? bc_num_a : bc_num_s;
Gavin Howard41852962018-12-27 17:15:00 -07002151 BC_UNUSED(scale);
Gavin Howardd18fa6d2019-01-24 13:49:35 -07002152 return bc_num_binary(a, b, c, false, op, bc_num_addReq(a, b, scale));
Gavin Howard3eb626f2018-02-14 13:54:35 -07002153}
2154
Gavin Howard12fe7812018-09-29 03:45:18 -06002155BcStatus bc_num_sub(BcNum *a, BcNum *b, BcNum *c, size_t scale) {
Gavin Howard54b946a2018-10-23 12:06:57 -06002156 BcNumBinaryOp op = (!a->neg == !b->neg) ? bc_num_s : bc_num_a;
Gavin Howard41852962018-12-27 17:15:00 -07002157 BC_UNUSED(scale);
Gavin Howardd18fa6d2019-01-24 13:49:35 -07002158 return bc_num_binary(a, b, c, true, op, bc_num_addReq(a, b, scale));
Gavin Howard3eb626f2018-02-14 13:54:35 -07002159}
2160
Gavin Howard12fe7812018-09-29 03:45:18 -06002161BcStatus bc_num_mul(BcNum *a, BcNum *b, BcNum *c, size_t scale) {
Gavin Howardd18fa6d2019-01-24 13:49:35 -07002162 return bc_num_binary(a, b, c, scale, bc_num_m, bc_num_mulReq(a, b, scale));
Gavin Howard3eb626f2018-02-14 13:54:35 -07002163}
2164
Gavin Howard12fe7812018-09-29 03:45:18 -06002165BcStatus bc_num_div(BcNum *a, BcNum *b, BcNum *c, size_t scale) {
Gavin Howardd18fa6d2019-01-24 13:49:35 -07002166 return bc_num_binary(a, b, c, scale, bc_num_d, bc_num_mulReq(a, b, scale));
Gavin Howard3eb626f2018-02-14 13:54:35 -07002167}
2168
Gavin Howard54b946a2018-10-23 12:06:57 -06002169BcStatus bc_num_mod(BcNum *a, BcNum *b, BcNum *c, size_t scale) {
Gavin Howard2d188a52019-02-25 14:19:08 -07002170 size_t req = bc_num_mulReq(a, b, scale);
2171 return bc_num_binary(a, b, c, scale, bc_num_rem, req);
Gavin Howard3eb626f2018-02-14 13:54:35 -07002172}
2173
Gavin Howard12fe7812018-09-29 03:45:18 -06002174BcStatus bc_num_pow(BcNum *a, BcNum *b, BcNum *c, size_t scale) {
Gavin Howard6734e1a2019-02-21 17:11:52 -07002175 return bc_num_binary(a, b, c, scale, bc_num_p, bc_num_powReq(a, b, scale));
Gavin Howard3eb626f2018-02-14 13:54:35 -07002176}
2177
Gavin Howard7d8f0382018-12-27 16:15:13 -07002178#if BC_ENABLE_EXTRA_MATH
2179BcStatus bc_num_places(BcNum *a, BcNum *b, BcNum *c, size_t scale) {
Gavin Howard8291ecd2019-05-06 14:34:53 -06002180 size_t req = bc_num_placesReq(a, b, scale);
2181 return bc_num_binary(a, b, c, scale, bc_num_place, req);
Gavin Howard7d8f0382018-12-27 16:15:13 -07002182}
2183
2184BcStatus bc_num_lshift(BcNum *a, BcNum *b, BcNum *c, size_t scale) {
Gavin Howard8291ecd2019-05-06 14:34:53 -06002185 size_t req = bc_num_shiftLeftReq(a, b, scale);
2186 return bc_num_binary(a, b, c, scale, bc_num_left, req);
Gavin Howard7d8f0382018-12-27 16:15:13 -07002187}
2188
2189BcStatus bc_num_rshift(BcNum *a, BcNum *b, BcNum *c, size_t scale) {
Gavin Howard8291ecd2019-05-06 14:34:53 -06002190 size_t req = bc_num_shiftRightReq(a, b, scale);
2191 return bc_num_binary(a, b, c, scale, bc_num_right, req);
Gavin Howard7d8f0382018-12-27 16:15:13 -07002192}
2193#endif // BC_ENABLE_EXTRA_MATH
2194
Gavin Howard3c02ed32018-12-28 18:17:15 -07002195BcStatus bc_num_sqrt(BcNum *restrict a, BcNum *restrict b, size_t scale) {
Gavin Howard954276b2018-05-16 01:43:58 -06002196
Gavin Howard1cbfe242019-01-09 17:13:11 -07002197 BcStatus s = BC_STATUS_SUCCESS;
Gavin Howard34edd0a2018-10-25 15:51:05 -06002198 BcNum num1, num2, half, f, fprime, *x0, *x1, *temp;
Stefan Eßer4276af42019-04-29 14:04:57 -06002199 size_t pow, len, rdx, req, digs, digs1, digs2, resscale, times = 0;
Gavin Howard9f2395b2018-10-06 05:28:58 -06002200 ssize_t cmp = 1, cmp1 = SSIZE_MAX, cmp2 = SSIZE_MAX;
Stefan Eßer4276af42019-04-29 14:04:57 -06002201 BcDig half_digs[1];
Gavin Howard954276b2018-05-16 01:43:58 -06002202
Gavin Howard34edd0a2018-10-25 15:51:05 -06002203 assert(a && b && a != b);
Gavin Howard954276b2018-05-16 01:43:58 -06002204
Gavin Howardecafd4f2019-02-23 09:30:45 -07002205 if (BC_ERR(a->neg)) return bc_vm_err(BC_ERROR_MATH_NEGATIVE);
Gavin Howard07fbf012019-02-19 09:25:11 -07002206
Stefan Eßer4276af42019-04-29 14:04:57 -06002207 if (a->scale > scale) scale = a->scale;
Gavin Howard06fee0c2019-05-09 14:43:20 -06002208 len = bc_vm_growSize(bc_num_intDigits(a), 1);
Gavin Howardeec07282019-05-10 20:10:18 -06002209 rdx = BC_NUM_RDX(scale);
2210 req = bc_vm_growSize(BC_MAX(rdx, a->rdx), len >> 1);
Gavin Howard2d188a52019-02-25 14:19:08 -07002211 bc_num_init(b, bc_vm_growSize(req, 1));
Gavin Howard954276b2018-05-16 01:43:58 -06002212
Gavin Howardddce6d42019-01-03 14:07:06 -07002213 if (BC_NUM_ZERO(a)) {
Gavin Howardf8ddb6d2018-10-22 12:58:53 -06002214 bc_num_setToZero(b, scale);
Gavin Howardad0ecfe2018-10-30 01:16:01 -06002215 return BC_STATUS_SUCCESS;
Gavin Howard63738202018-09-26 15:34:20 -06002216 }
Gavin Howardb2f01a42019-05-10 20:00:32 -06002217 if (BC_NUM_ONE(a)) {
Gavin Howard757b66a2018-10-06 04:13:46 -06002218 bc_num_one(b);
Gavin Howardad0ecfe2018-10-30 01:16:01 -06002219 bc_num_extend(b, scale);
2220 return BC_STATUS_SUCCESS;
Gavin Howard63738202018-09-26 15:34:20 -06002221 }
Gavin Howard954276b2018-05-16 01:43:58 -06002222
Gavin Howardeec07282019-05-10 20:10:18 -06002223 rdx = BC_NUM_RDX(scale);
2224 rdx = BC_MAX(rdx, a->rdx);
Stefan Eßer4276af42019-04-29 14:04:57 -06002225 len = bc_vm_growSize(a->len, rdx);
Gavin Howard954276b2018-05-16 01:43:58 -06002226
Gavin Howardad0ecfe2018-10-30 01:16:01 -06002227 bc_num_init(&num1, len);
2228 bc_num_init(&num2, len);
Gavin Howard18f40202018-12-29 21:30:37 -07002229 bc_num_setup(&half, half_digs, sizeof(half_digs) / sizeof(BcDig));
Gavin Howard954276b2018-05-16 01:43:58 -06002230
Gavin Howard63738202018-09-26 15:34:20 -06002231 bc_num_one(&half);
Gavin Howard92ba4e52019-05-10 20:49:13 -06002232 half.num[0] = BC_BASE_POW / 2;
Stefan Eßer4276af42019-04-29 14:04:57 -06002233 half.len = 1;
Gavin Howard63738202018-09-26 15:34:20 -06002234 half.rdx = 1;
Stefan Eßer4276af42019-04-29 14:04:57 -06002235 half.scale = 1;
Gavin Howard954276b2018-05-16 01:43:58 -06002236
Gavin Howardad0ecfe2018-10-30 01:16:01 -06002237 bc_num_init(&f, len);
2238 bc_num_init(&fprime, len);
Gavin Howard954276b2018-05-16 01:43:58 -06002239
Gavin Howard63738202018-09-26 15:34:20 -06002240 x0 = &num1;
2241 x1 = &num2;
Gavin Howard954276b2018-05-16 01:43:58 -06002242
Gavin Howard63738202018-09-26 15:34:20 -06002243 bc_num_one(x0);
Gavin Howard06fee0c2019-05-09 14:43:20 -06002244 pow = bc_num_intDigits(a);
Gavin Howard954276b2018-05-16 01:43:58 -06002245
Gavin Howard53eba8b2018-10-31 15:14:37 -06002246 if (pow) {
Gavin Howard954276b2018-05-16 01:43:58 -06002247
Gavin Howardc39fd492018-10-04 10:07:03 -06002248 if (pow & 1) x0->num[0] = 2;
2249 else x0->num[0] = 6;
Gavin Howard954276b2018-05-16 01:43:58 -06002250
Gavin Howardc39fd492018-10-04 10:07:03 -06002251 pow -= 2 - (pow & 1);
Gavin Howard74704572019-05-07 13:48:48 -06002252 s = bc_num_shiftLeft(x0, pow / 2);
2253 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howard63738202018-09-26 15:34:20 -06002254 }
Gavin Howard954276b2018-05-16 01:43:58 -06002255
Gavin Howardeec07282019-05-10 20:10:18 -06002256 x0->scale = x0->rdx = digs = digs1 = digs2 = 0;
Gavin Howard92ba4e52019-05-10 20:49:13 -06002257 resscale = (scale + BC_BASE_DIGS) * 2;
Stefan Eßer4276af42019-04-29 14:04:57 -06002258
Gavin Howard06fee0c2019-05-09 14:43:20 -06002259 len = BC_NUM_RDX(bc_num_intDigits(x0) + resscale - 1);
Gavin Howard954276b2018-05-16 01:43:58 -06002260
Gavin Howard2d188a52019-02-25 14:19:08 -07002261 while (BC_NO_SIG && (cmp || digs < len)) {
Gavin Howard954276b2018-05-16 01:43:58 -06002262
Gavin Howard971a2672019-04-26 14:32:07 -06002263 assert(BC_NUM_NONZERO(x0));
Gavin Howard1769abc2019-02-21 09:36:14 -07002264
Stefan Eßer4276af42019-04-29 14:04:57 -06002265 s = bc_num_div(a, x0, &f, resscale);
Gavin Howard1769abc2019-02-21 09:36:14 -07002266 assert(!s || s == BC_STATUS_SIGNAL);
Gavin Howardc78e7522019-02-22 13:24:25 -07002267 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Stefan Eßer4276af42019-04-29 14:04:57 -06002268 s = bc_num_add(x0, &f, &fprime, resscale);
Gavin Howardc78e7522019-02-22 13:24:25 -07002269 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Stefan Eßer4276af42019-04-29 14:04:57 -06002270 s = bc_num_mul(&fprime, &half, x1, resscale);
Gavin Howardc78e7522019-02-22 13:24:25 -07002271 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howard954276b2018-05-16 01:43:58 -06002272
Gavin Howard53eba8b2018-10-31 15:14:37 -06002273 cmp = bc_num_cmp(x1, x0);
Gavin Howard3378af82019-05-11 08:29:39 -06002274
2275#if BC_ENABLE_SIGNALS
Gavin Howarda36f0312019-05-09 10:23:07 -06002276 if (cmp == BC_NUM_CMP_SIGNAL) {
Gavin Howardf2ee6342019-04-09 17:05:20 -06002277 s = BC_STATUS_SIGNAL;
2278 break;
2279 }
Gavin Howard3378af82019-05-11 08:29:39 -06002280#endif // BC_ENABLE_SIGNALS
Gavin Howardf2ee6342019-04-09 17:05:20 -06002281
Gavin Howard53eba8b2018-10-31 15:14:37 -06002282 digs = x1->len - (unsigned long long) llabs(cmp);
Gavin Howard954276b2018-05-16 01:43:58 -06002283
Gavin Howardb5ec7f32018-10-29 12:32:39 -06002284 if (cmp == cmp2 && digs == digs1) times += 1;
Gavin Howardb11e9e22018-10-06 17:26:02 -06002285 else times = 0;
2286
Gavin Howardeec07282019-05-10 20:10:18 -06002287 resscale += (times > 2);
Gavin Howard9f2395b2018-10-06 05:28:58 -06002288
2289 cmp2 = cmp1;
2290 cmp1 = cmp;
Gavin Howardb5ec7f32018-10-29 12:32:39 -06002291 digs1 = digs;
Gavin Howard9f2395b2018-10-06 05:28:58 -06002292
Gavin Howard63738202018-09-26 15:34:20 -06002293 temp = x0;
2294 x0 = x1;
2295 x1 = temp;
2296 }
Gavin Howard954276b2018-05-16 01:43:58 -06002297
Gavin Howard2d188a52019-02-25 14:19:08 -07002298 if (BC_SIG) {
Gavin Howard176cfe62019-02-16 23:40:13 -07002299 s = BC_STATUS_SIGNAL;
2300 goto err;
2301 }
Gavin Howard0dfe2922018-05-22 13:57:02 -06002302
Gavin Howardad0ecfe2018-10-30 01:16:01 -06002303 bc_num_copy(b, x0);
Stefan Eßer4276af42019-04-29 14:04:57 -06002304 if (b->scale > scale) bc_num_truncate(b, b->scale - scale);
Gavin Howard954276b2018-05-16 01:43:58 -06002305
2306err:
Gavin Howardcf0748d2019-04-09 14:51:47 -06002307 if (BC_ERR(s)) bc_num_free(b);
Gavin Howard63738202018-09-26 15:34:20 -06002308 bc_num_free(&fprime);
Gavin Howard63738202018-09-26 15:34:20 -06002309 bc_num_free(&f);
Gavin Howard63738202018-09-26 15:34:20 -06002310 bc_num_free(&num2);
Gavin Howard63738202018-09-26 15:34:20 -06002311 bc_num_free(&num1);
Gavin Howardddce6d42019-01-03 14:07:06 -07002312 assert(!b->neg || BC_NUM_NONZERO(b));
Gavin Howardc4bf4c42019-01-09 10:30:15 -07002313 assert(b->rdx <= b->len || !b->len);
Gavin Howard63738202018-09-26 15:34:20 -06002314 return s;
Gavin Howard3eb626f2018-02-14 13:54:35 -07002315}
Gavin Howardba009802018-09-29 04:41:51 -06002316
Gavin Howard2cb39612018-10-22 08:46:48 -06002317BcStatus bc_num_divmod(BcNum *a, BcNum *b, BcNum *c, BcNum *d, size_t scale) {
2318
2319 BcStatus s;
2320 BcNum num2, *ptr_a;
Gavin Howard890d0c02018-10-30 16:34:50 -06002321 bool init = false;
Stefan Eßera7fbbf62019-04-29 14:14:30 -06002322 size_t ts, len;
2323
2324 ts = BC_MAX(scale + b->scale, a->scale);
2325 len = bc_num_mulReq(a, b, ts);
Gavin Howard2cb39612018-10-22 08:46:48 -06002326
Gavin Howardf2ef2f32019-01-16 11:25:16 -07002327 assert(c != d && a != d && b != d && b != c);
Gavin Howard2cb39612018-10-22 08:46:48 -06002328
Gavin Howard890d0c02018-10-30 16:34:50 -06002329 if (c == a) {
Gavin Howard2cb39612018-10-22 08:46:48 -06002330 memcpy(&num2, c, sizeof(BcNum));
2331 ptr_a = &num2;
Gavin Howardad0ecfe2018-10-30 01:16:01 -06002332 bc_num_init(c, len);
Gavin Howard890d0c02018-10-30 16:34:50 -06002333 init = true;
Gavin Howard2cb39612018-10-22 08:46:48 -06002334 }
Gavin Howardf679ad82018-10-29 12:26:30 -06002335 else {
2336 ptr_a = a;
Gavin Howardad0ecfe2018-10-30 01:16:01 -06002337 bc_num_expand(c, len);
Gavin Howardf679ad82018-10-29 12:26:30 -06002338 }
Gavin Howard2cb39612018-10-22 08:46:48 -06002339
Gavin Howardf5961782019-05-09 07:50:46 -06002340 if (BC_NUM_NONZERO(a) && !a->rdx && !b->rdx && b->len == 1 && !scale) {
Gavin Howardc9732f42019-05-18 20:32:31 -06002341
Gavin Howard2956a5e2019-05-08 08:04:06 -06002342 BcBigDig rem;
Gavin Howardc9732f42019-05-18 20:32:31 -06002343
Gavin Howard9d771ba2019-05-08 09:09:25 -06002344 s = bc_num_divArray(ptr_a, (BcBigDig) b->num[0], c, &rem);
Gavin Howardc9732f42019-05-18 20:32:31 -06002345
Gavin Howard92ba4e52019-05-10 20:49:13 -06002346 assert(rem < BC_BASE_POW);
Gavin Howardc9732f42019-05-18 20:32:31 -06002347
Gavin Howardb3f1ee22019-05-07 21:49:15 -06002348 d->num[0] = (BcDig) rem;
Gavin Howardf5961782019-05-09 07:50:46 -06002349 d->len = (rem != 0);
Gavin Howardb3f1ee22019-05-07 21:49:15 -06002350 }
Gavin Howardf5961782019-05-09 07:50:46 -06002351 else s = bc_num_r(ptr_a, b, c, d, scale, ts);
Gavin Howard2cb39612018-10-22 08:46:48 -06002352
Gavin Howardddce6d42019-01-03 14:07:06 -07002353 assert(!c->neg || BC_NUM_NONZERO(c));
Gavin Howardc4bf4c42019-01-09 10:30:15 -07002354 assert(c->rdx <= c->len || !c->len);
Gavin Howardddce6d42019-01-03 14:07:06 -07002355 assert(!d->neg || BC_NUM_NONZERO(d));
Gavin Howardc4bf4c42019-01-09 10:30:15 -07002356 assert(d->rdx <= d->len || !d->len);
Gavin Howard2cb39612018-10-22 08:46:48 -06002357
2358 if (init) bc_num_free(&num2);
2359
2360 return s;
2361}
2362
Gavin Howard40a085f2018-12-03 12:08:59 -07002363#if DC_ENABLED
Gavin Howard34edd0a2018-10-25 15:51:05 -06002364BcStatus bc_num_modexp(BcNum *a, BcNum *b, BcNum *c, BcNum *restrict d) {
Gavin Howardba009802018-09-29 04:41:51 -06002365
2366 BcStatus s;
Gavin Howard34edd0a2018-10-25 15:51:05 -06002367 BcNum base, exp, two, temp;
Gavin Howardd3929832018-12-24 15:13:15 -07002368 BcDig two_digs[2];
Gavin Howardba009802018-09-29 04:41:51 -06002369
Gavin Howard34edd0a2018-10-25 15:51:05 -06002370 assert(a && b && c && d && a != d && b != d && c != d);
Gavin Howardba009802018-09-29 04:41:51 -06002371
Gavin Howardecafd4f2019-02-23 09:30:45 -07002372 if (BC_ERR(BC_NUM_ZERO(c)))
2373 return bc_vm_err(BC_ERROR_MATH_DIVIDE_BY_ZERO);
2374 if (BC_ERR(b->neg)) return bc_vm_err(BC_ERROR_MATH_NEGATIVE);
2375 if (BC_ERR(a->rdx || b->rdx || c->rdx))
Gavin Howard7536dcf2018-12-15 19:27:09 -07002376 return bc_vm_err(BC_ERROR_MATH_NON_INTEGER);
Gavin Howardba009802018-09-29 04:41:51 -06002377
Gavin Howardad0ecfe2018-10-30 01:16:01 -06002378 bc_num_expand(d, c->len);
2379 bc_num_init(&base, c->len);
Gavin Howard7fdc30a2018-12-29 21:31:21 -07002380 bc_num_setup(&two, two_digs, sizeof(two_digs) / sizeof(BcDig));
Gavin Howard303572b2019-05-09 07:50:56 -06002381 bc_num_init(&temp, b->len + 1);
Gavin Howardba009802018-09-29 04:41:51 -06002382
2383 bc_num_one(&two);
Gavin Howardba009802018-09-29 04:41:51 -06002384 two.num[0] = 2;
2385 bc_num_one(d);
2386
Gavin Howard1769abc2019-02-21 09:36:14 -07002387 // We already checked for 0.
Gavin Howard53eba8b2018-10-31 15:14:37 -06002388 s = bc_num_rem(a, c, &base, 0);
Gavin Howard1769abc2019-02-21 09:36:14 -07002389 assert(!s || s == BC_STATUS_SIGNAL);
Gavin Howardc78e7522019-02-22 13:24:25 -07002390 if (BC_ERROR_SIGNAL_ONLY(s)) goto rem_err;
Gavin Howardbaa4f582019-01-24 13:56:35 -07002391 bc_num_createCopy(&exp, b);
Gavin Howardba009802018-09-29 04:41:51 -06002392
Gavin Howard2d188a52019-02-25 14:19:08 -07002393 while (BC_NO_SIG && BC_NUM_NONZERO(&exp)) {
Gavin Howardba009802018-09-29 04:41:51 -06002394
Gavin Howard1769abc2019-02-21 09:36:14 -07002395 // Num two cannot be 0, so no errors.
Gavin Howard53eba8b2018-10-31 15:14:37 -06002396 s = bc_num_divmod(&exp, &two, &exp, &temp, 0);
Gavin Howardc78e7522019-02-22 13:24:25 -07002397 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howardba009802018-09-29 04:41:51 -06002398
Gavin Howardb2f01a42019-05-10 20:00:32 -06002399 if (BC_NUM_ONE(&temp) && !temp.neg) {
2400
Gavin Howard53eba8b2018-10-31 15:14:37 -06002401 s = bc_num_mul(d, &base, &temp, 0);
Gavin Howardc78e7522019-02-22 13:24:25 -07002402 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howard1769abc2019-02-21 09:36:14 -07002403
2404 // We already checked for 0.
Gavin Howard53eba8b2018-10-31 15:14:37 -06002405 s = bc_num_rem(&temp, c, d, 0);
Gavin Howard1769abc2019-02-21 09:36:14 -07002406 assert(!s || s == BC_STATUS_SIGNAL);
Gavin Howardc78e7522019-02-22 13:24:25 -07002407 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howardba009802018-09-29 04:41:51 -06002408 }
2409
Gavin Howard53eba8b2018-10-31 15:14:37 -06002410 s = bc_num_mul(&base, &base, &temp, 0);
Gavin Howardc78e7522019-02-22 13:24:25 -07002411 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howard1769abc2019-02-21 09:36:14 -07002412
2413 // We already checked for 0.
Gavin Howard53eba8b2018-10-31 15:14:37 -06002414 s = bc_num_rem(&temp, c, &base, 0);
Gavin Howard1769abc2019-02-21 09:36:14 -07002415 assert(!s || s == BC_STATUS_SIGNAL);
Gavin Howardc78e7522019-02-22 13:24:25 -07002416 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howardba009802018-09-29 04:41:51 -06002417 }
2418
Gavin Howard2d188a52019-02-25 14:19:08 -07002419 if (BC_NO_ERR(!s) && BC_SIG) s = BC_STATUS_SIGNAL;
Gavin Howardfa4983a2019-02-16 23:43:03 -07002420
Gavin Howardba009802018-09-29 04:41:51 -06002421err:
Gavin Howardba009802018-09-29 04:41:51 -06002422 bc_num_free(&exp);
Gavin Howardd88164d2019-02-21 09:57:31 -07002423rem_err:
2424 bc_num_free(&temp);
Gavin Howardba009802018-09-29 04:41:51 -06002425 bc_num_free(&base);
Gavin Howard3cf769e2018-10-11 13:55:11 -06002426 assert(!d->neg || d->len);
Gavin Howardba009802018-09-29 04:41:51 -06002427 return s;
2428}
Gavin Howarde6e84762018-10-03 11:46:34 -06002429#endif // DC_ENABLED
Gavin Howardefc1e202019-05-06 08:01:07 -06002430
2431#if BC_DEBUG_CODE
2432void bc_num_printDebug(const BcNum *n, const char *name, bool emptyline) {
2433 printf("%s: ", name);
2434 bc_num_printDecimal(n);
2435 printf("\n");
2436 if (emptyline) printf("\n");
Gavin Howarde34f7d82019-05-07 10:32:39 -06002437 vm->nchars = 0;
Gavin Howardefc1e202019-05-06 08:01:07 -06002438}
2439
Gavin Howard2a84f962019-05-08 17:20:09 -06002440void bc_num_printDigs(const BcDig *n, size_t len, bool emptyline) {
Gavin Howardefc1e202019-05-06 08:01:07 -06002441
2442 size_t i;
2443
Gavin Howard92ba4e52019-05-10 20:49:13 -06002444 for (i = len - 1; i < len; --i) printf(" %0*d", BC_BASE_DIGS, n[i]);
Gavin Howardefc1e202019-05-06 08:01:07 -06002445
2446 printf("\n");
2447 if (emptyline) printf("\n");
Gavin Howarde34f7d82019-05-07 10:32:39 -06002448 vm->nchars = 0;
Gavin Howardefc1e202019-05-06 08:01:07 -06002449}
2450
Gavin Howard2a84f962019-05-08 17:20:09 -06002451void bc_num_printWithDigs(const BcNum *n, const char *name, bool emptyline) {
2452 printf("%s len: %zu, rdx: %zu, scale: %zu\n",
2453 name, n->len, n->rdx, n->scale);
2454 bc_num_printDigs(n->num, n->len, emptyline);
2455}
2456
Gavin Howardefc1e202019-05-06 08:01:07 -06002457void bc_num_dump(const char *varname, const BcNum *n) {
2458
2459 unsigned long i, scale = n->scale;
2460
2461 fprintf(stderr, "\n%s = %s", varname, n->len ? (n->neg ? "-" : "+") : "0 ");
2462
2463 for (i = n->len - 1; i < n->len; --i) {
2464
2465 if (i + 1 == n->rdx) fprintf(stderr, ". ");
2466
Gavin Howard92ba4e52019-05-10 20:49:13 -06002467 if (scale / BC_BASE_DIGS != n->rdx - i - 1)
2468 fprintf(stderr, "%0*d ", BC_BASE_DIGS, n->num[i]);
Gavin Howardefc1e202019-05-06 08:01:07 -06002469 else {
2470
Gavin Howard92ba4e52019-05-10 20:49:13 -06002471 int mod = scale % BC_BASE_DIGS;
2472 int d = BC_BASE_DIGS - mod;
Gavin Howardefc1e202019-05-06 08:01:07 -06002473 BcDig div;
2474
2475 if (mod != 0) {
Gavin Howard2a84f962019-05-08 17:20:09 -06002476 div = n->num[i] / ((BcDig) bc_num_pow10[(unsigned long) d]);
Gavin Howardefc1e202019-05-06 08:01:07 -06002477 fprintf(stderr, "%0*d", (int) mod, div);
2478 }
2479
Gavin Howard2a84f962019-05-08 17:20:09 -06002480 div = n->num[i] % ((BcDig) bc_num_pow10[(unsigned long) d]);
Gavin Howardefc1e202019-05-06 08:01:07 -06002481 fprintf(stderr, " ' %0*d ", d, div);
2482 }
2483 }
2484
2485 fprintf(stderr, "(%zu | %zu.%zu / %zu) %p\n",
2486 n->scale, n->len, n->rdx, n->cap, (void*) n->num);
Gavin Howard8de31d92019-05-18 18:59:57 -06002487 vm->nchars = 0;
Gavin Howardefc1e202019-05-06 08:01:07 -06002488}
2489#endif // BC_DEBUG_CODE