blob: 15a4419f66eea53da89643cfa5d415dd242dba8b [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 Howard4007e2f2019-05-08 09:08:00 -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 Howarda882ac22019-05-14 08:44:29 -0600114 for (i = len - 1; i < n->len && !n->num[i]; --i);
115 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 Howard14c354a2019-04-24 14:28:24 -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 Howarde7ae4f42019-05-08 09:08:15 -0600199 c->num[i] = (BcDig) (in / b);
Gavin Howardb3f1ee22019-05-07 21:49:15 -0600200 carry = in % b;
201 }
202
203 c->len = a->len;
204 bc_num_clean(c);
205 *rem = carry;
206
207 return BC_SIG ? BC_STATUS_SIGNAL : BC_STATUS_SUCCESS;
208}
209
Gavin Howard2d188a52019-02-25 14:19:08 -0700210static ssize_t bc_num_compare(const BcDig *restrict a, const BcDig *restrict b,
211 size_t len)
Gavin Howard3c02ed32018-12-28 18:17:15 -0700212{
Gavin Howard63738202018-09-26 15:34:20 -0600213 size_t i;
Gavin Howard9a66a832019-05-13 20:20:12 -0600214 BcDig c = 0;
Gavin Howard2d188a52019-02-25 14:19:08 -0700215 for (i = len - 1; BC_NO_SIG && i < len && !(c = a[i] - b[i]); --i);
Gavin Howarda36f0312019-05-09 10:23:07 -0600216 return BC_SIG ? BC_NUM_CMP_SIGNAL : bc_num_neg(i + 1, c < 0);
Gavin Howard08bf5292018-03-20 14:59:33 -0600217}
218
Gavin Howard3c02ed32018-12-28 18:17:15 -0700219ssize_t bc_num_cmp(const BcNum *a, const BcNum *b) {
Gavin Howard4681d1b2018-03-05 19:49:33 -0700220
Gavin Howard63738202018-09-26 15:34:20 -0600221 size_t i, min, a_int, b_int, diff;
Gavin Howard8a921bd2018-10-11 14:15:32 -0600222 BcDig *max_num, *min_num;
Gavin Howard7fbb4e22018-10-18 11:43:17 -0600223 bool a_max, neg = false;
224 ssize_t cmp;
225
226 assert(a && b);
Gavin Howard4681d1b2018-03-05 19:49:33 -0700227
Gavin Howard8a921bd2018-10-11 14:15:32 -0600228 if (a == b) return 0;
Gavin Howard56ebcf72019-02-21 16:59:48 -0700229 if (BC_NUM_ZERO(a)) return bc_num_neg(b->len != 0, !b->neg);
Gavin Howardd2cf06a2019-02-21 17:03:24 -0700230 if (BC_NUM_ZERO(b)) return bc_num_cmpZero(a);
Gavin Howard890d0c02018-10-30 16:34:50 -0600231 if (a->neg) {
Gavin Howard7fbb4e22018-10-18 11:43:17 -0600232 if (b->neg) neg = true;
Gavin Howard63738202018-09-26 15:34:20 -0600233 else return -1;
234 }
235 else if (b->neg) return 1;
Gavin Howard4681d1b2018-03-05 19:49:33 -0700236
Gavin Howard7b7d2f32019-02-21 16:46:47 -0700237 a_int = bc_num_int(a);
238 b_int = bc_num_int(b);
Gavin Howard890d0c02018-10-30 16:34:50 -0600239 a_int -= b_int;
240 a_max = (a->rdx > b->rdx);
Gavin Howard4681d1b2018-03-05 19:49:33 -0700241
Gavin Howard1ab22d22019-01-03 13:32:17 -0700242 if (a_int) return (ssize_t) a_int;
Gavin Howard4681d1b2018-03-05 19:49:33 -0700243
Gavin Howard890d0c02018-10-30 16:34:50 -0600244 if (a_max) {
Gavin Howard63738202018-09-26 15:34:20 -0600245 min = b->rdx;
246 diff = a->rdx - b->rdx;
247 max_num = a->num + diff;
248 min_num = b->num;
249 }
250 else {
251 min = a->rdx;
252 diff = b->rdx - a->rdx;
253 max_num = b->num + diff;
254 min_num = a->num;
255 }
Gavin Howard4681d1b2018-03-05 19:49:33 -0700256
Gavin Howard890d0c02018-10-30 16:34:50 -0600257 cmp = bc_num_compare(max_num, min_num, b_int + min);
Gavin Howard3378af82019-05-11 08:29:39 -0600258
259#if BC_ENABLE_SIGNALS
Gavin Howarda36f0312019-05-09 10:23:07 -0600260 if (cmp == BC_NUM_CMP_SIGNAL) return cmp;
Gavin Howard3378af82019-05-11 08:29:39 -0600261#endif // BC_ENABLE_SIGNALS
262
Gavin Howard5eba0e82019-02-21 18:08:33 -0700263 if (cmp) return bc_num_neg((size_t) cmp, !a_max == !neg);
Gavin Howard021150b2018-03-10 15:40:42 -0700264
Gavin Howard2d188a52019-02-25 14:19:08 -0700265 for (max_num -= diff, i = diff - 1; BC_NO_SIG && i < diff; --i) {
Gavin Howard5eba0e82019-02-21 18:08:33 -0700266 if (max_num[i]) return bc_num_neg(1, !a_max == !neg);
Gavin Howard63738202018-09-26 15:34:20 -0600267 }
Gavin Howard6fbdb292018-02-27 15:44:48 -0700268
Gavin Howarda36f0312019-05-09 10:23:07 -0600269 return BC_SIG ? BC_NUM_CMP_SIGNAL : 0;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700270}
271
Gavin Howard3c02ed32018-12-28 18:17:15 -0700272void bc_num_truncate(BcNum *restrict n, size_t places) {
Gavin Howard6fbdb292018-02-27 15:44:48 -0700273
Gavin Howardb1558662019-04-26 14:32:55 -0600274 size_t places_rdx;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700275
Stefan Esser70f11852019-04-27 10:39:00 +0200276 if (!places) return;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700277
Gavin Howardb1558662019-04-26 14:32:55 -0600278 places_rdx = n->rdx - BC_NUM_RDX(n->scale - places);
279 assert(places <= n->scale && (BC_NUM_ZERO(n) || places_rdx <= n->len));
280
281 n->scale -= places;
282 n->rdx -= places_rdx;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700283
Gavin Howardddce6d42019-01-03 14:07:06 -0700284 if (BC_NUM_NONZERO(n)) {
Gavin Howardb1558662019-04-26 14:32:55 -0600285
Gavin Howardc9bef2d2019-04-26 14:46:52 -0600286 size_t pow;
287
Gavin Howard92ba4e52019-05-10 20:49:13 -0600288 pow = n->scale % BC_BASE_DIGS;
289 pow = pow ? BC_BASE_DIGS - pow : 0;
Gavin Howardc1542802019-05-08 17:20:51 -0600290 pow = bc_num_pow10[pow];
Gavin Howardb1558662019-04-26 14:32:55 -0600291
292 n->len -= places_rdx;
Gavin Howard404ece72019-04-26 15:59:15 -0600293 memmove(n->num, n->num + places_rdx, BC_NUM_SIZE(n->len));
Gavin Howardb1558662019-04-26 14:32:55 -0600294
295 // Clear the lower part of the last digit.
Gavin Howardda6b5432019-04-26 14:33:43 -0600296 if (BC_NUM_NONZERO(n)) n->num[0] -= n->num[0] % (BcDig) pow;
Gavin Howardb1558662019-04-26 14:32:55 -0600297
Gavin Howard2a366922019-01-16 10:50:20 -0700298 bc_num_clean(n);
Gavin Howard955da852018-10-23 11:08:20 -0600299 }
Gavin Howard6fbdb292018-02-27 15:44:48 -0700300}
301
Stefan Esser70f11852019-04-27 10:39:00 +0200302static void bc_num_extend(BcNum *restrict n, size_t places) {
Gavin Howard6fbdb292018-02-27 15:44:48 -0700303
Gavin Howardb5e6da92019-04-30 19:58:04 -0600304 size_t places_rdx;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700305
Gavin Howard1ab22d22019-01-03 13:32:17 -0700306 if (!places) return;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700307
Gavin Howardb5e6da92019-04-30 19:58:04 -0600308 places_rdx = BC_NUM_RDX(places + n->scale) - n->rdx;
309
Gavin Howard8721e332019-05-06 08:02:49 -0600310 if (places_rdx) {
311 bc_num_expand(n, bc_vm_growSize(n->len, places_rdx));
312 memmove(n->num + places_rdx, n->num, BC_NUM_SIZE(n->len));
313 memset(n->num, 0, BC_NUM_SIZE(places_rdx));
314 }
Gavin Howardb5e6da92019-04-30 19:58:04 -0600315
Gavin Howarde6f326e2019-04-26 10:13:45 -0600316 n->rdx += places_rdx;
317 n->scale += places;
Gavin Howardb1558662019-04-26 14:32:55 -0600318 n->len += places_rdx;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700319
Gavin Howarde6f326e2019-04-26 10:13:45 -0600320 assert(n->rdx == BC_NUM_RDX(n->scale));
Stefan Esser193f8522019-04-27 01:29:01 +0200321}
322
Gavin Howard2d188a52019-02-25 14:19:08 -0700323static void bc_num_retireMul(BcNum *restrict n, size_t scale,
324 bool neg1, bool neg2)
325{
Gavin Howard971a2672019-04-26 14:32:07 -0600326 if (n->scale < scale) bc_num_extend(n, scale - n->scale);
327 else bc_num_truncate(n, n->scale - scale);
Gavin Howard337fe602018-09-01 15:10:59 -0600328
Gavin Howard305249a2018-10-15 20:24:47 -0600329 bc_num_clean(n);
Gavin Howardddce6d42019-01-03 14:07:06 -0700330 if (BC_NUM_NONZERO(n)) n->neg = (!neg1 != !neg2);
Gavin Howard305249a2018-10-15 20:24:47 -0600331}
332
Gavin Howard2d188a52019-02-25 14:19:08 -0700333static void bc_num_split(const BcNum *restrict n, size_t idx,
334 BcNum *restrict a, BcNum *restrict b)
335{
Gavin Howard305249a2018-10-15 20:24:47 -0600336 if (idx < n->len) {
337
Gavin Howard305249a2018-10-15 20:24:47 -0600338 b->len = n->len - idx;
339 a->len = idx;
Gavin Howarda3f260c2019-04-26 15:47:25 -0600340 a->scale = a->rdx = b->scale = b->rdx = 0;
Gavin Howard305249a2018-10-15 20:24:47 -0600341
Gavin Howard404ece72019-04-26 15:59:15 -0600342 memcpy(b->num, n->num + idx, BC_NUM_SIZE(b->len));
343 memcpy(a->num, n->num, BC_NUM_SIZE(idx));
Gavin Howard50c80022019-01-03 15:14:33 -0700344
345 bc_num_clean(b);
Gavin Howard305249a2018-10-15 20:24:47 -0600346 }
Gavin Howard50c80022019-01-03 15:14:33 -0700347 else bc_num_copy(a, n);
Gavin Howard305249a2018-10-15 20:24:47 -0600348
349 bc_num_clean(a);
Gavin Howard305249a2018-10-15 20:24:47 -0600350}
351
Gavin Howard14913fc2019-04-23 14:36:03 -0600352static size_t bc_num_shiftZero(BcNum *restrict n) {
353
354 size_t i;
355
356 assert(!n->rdx || BC_NUM_ZERO(n));
357
358 for (i = 0; i < n->len && !n->num[i]; ++i);
359
360 n->len -= i;
361 n->num += i;
362
363 return i;
364}
365
Gavin Howard2e736de2019-04-27 06:41:58 -0600366static void bc_num_unshiftZero(BcNum *restrict n, size_t places_rdx) {
367 n->len += places_rdx;
368 n->num -= places_rdx;
Gavin Howard14913fc2019-04-23 14:36:03 -0600369}
370
Gavin Howard2956a5e2019-05-08 08:04:06 -0600371static BcStatus bc_num_shift(BcNum *restrict n, BcBigDig dig) {
Gavin Howardbe884592019-04-29 15:07:49 -0600372
373 size_t i, len = n->len;
Gavin Howard2956a5e2019-05-08 08:04:06 -0600374 BcBigDig carry = 0, pow;
Gavin Howardbe884592019-04-29 15:07:49 -0600375 BcDig *ptr = n->num;
Gavin Howard305249a2018-10-15 20:24:47 -0600376
Gavin Howard92ba4e52019-05-10 20:49:13 -0600377 assert(dig < BC_BASE_DIGS);
Gavin Howard305249a2018-10-15 20:24:47 -0600378
Gavin Howardc1542802019-05-08 17:20:51 -0600379 pow = bc_num_pow10[dig];
Gavin Howard92ba4e52019-05-10 20:49:13 -0600380 dig = bc_num_pow10[BC_BASE_DIGS - dig];
Gavin Howardb1558662019-04-26 14:32:55 -0600381
Gavin Howard834fde12019-04-30 08:00:05 -0600382 for (i = len - 1; BC_NO_SIG && i < len; --i) {
Gavin Howard2956a5e2019-05-08 08:04:06 -0600383 BcBigDig in, temp;
384 in = ((BcBigDig) ptr[i]);
Gavin Howard834fde12019-04-30 08:00:05 -0600385 temp = carry * dig;
386 carry = in % pow;
387 ptr[i] = ((BcDig) (in / pow)) + (BcDig) temp;
Gavin Howard305249a2018-10-15 20:24:47 -0600388 }
389
Gavin Howard834fde12019-04-30 08:00:05 -0600390 assert(!carry);
Gavin Howardb1558662019-04-26 14:32:55 -0600391
392 return BC_SIG ? BC_STATUS_SIGNAL : BC_STATUS_SUCCESS;
Gavin Howard337fe602018-09-01 15:10:59 -0600393}
394
Gavin Howardb1558662019-04-26 14:32:55 -0600395static BcStatus bc_num_shiftLeft(BcNum *restrict n, size_t places) {
Gavin Howard7ad5a662019-02-19 14:40:46 -0700396
Gavin Howardb1558662019-04-26 14:32:55 -0600397 BcStatus s = BC_STATUS_SUCCESS;
Gavin Howard2956a5e2019-05-08 08:04:06 -0600398 BcBigDig dig;
Gavin Howard48a0e472019-04-26 15:39:46 -0600399 size_t places_rdx;
Gavin Howardbe884592019-04-29 15:07:49 -0600400 bool shift;
Gavin Howard14913fc2019-04-23 14:36:03 -0600401
Gavin Howardb1558662019-04-26 14:32:55 -0600402 if (!places) return s;
Gavin Howardabe4fdf2019-05-06 08:39:53 -0600403 if (places > n->scale) {
404 size_t size = bc_vm_growSize(BC_NUM_RDX(places - n->scale), n->len);
405 if (size > SIZE_MAX - 1) return bc_vm_err(BC_ERROR_MATH_OVERFLOW);
406 }
Gavin Howard4631f3a2019-04-30 10:48:40 -0600407 if (BC_NUM_ZERO(n)) {
408 if (n->scale >= places) n->scale -= places;
409 else n->scale = 0;
410 return s;
411 }
Gavin Howard14913fc2019-04-23 14:36:03 -0600412
Gavin Howard92ba4e52019-05-10 20:49:13 -0600413 dig = (BcBigDig) (places % BC_BASE_DIGS);
Gavin Howard48a0e472019-04-26 15:39:46 -0600414 shift = (dig != 0);
Gavin Howard22253c72019-05-06 08:03:57 -0600415 places_rdx = BC_NUM_RDX(places);
Gavin Howard48a0e472019-04-26 15:39:46 -0600416
Gavin Howard22253c72019-05-06 08:03:57 -0600417 if (n->scale) {
Gavin Howarda1879192019-04-30 18:55:08 -0600418
419 if (n->rdx >= places_rdx) {
420
Gavin Howard92ba4e52019-05-10 20:49:13 -0600421 size_t mod = n->scale % BC_BASE_DIGS, revdig;
Gavin Howarda1879192019-04-30 18:55:08 -0600422
Gavin Howard92ba4e52019-05-10 20:49:13 -0600423 mod = mod ? mod : BC_BASE_DIGS;
424 revdig = dig ? BC_BASE_DIGS - dig : 0;
Gavin Howarda1879192019-04-30 18:55:08 -0600425
Gavin Howard92ba4e52019-05-10 20:49:13 -0600426 if (mod + revdig > BC_BASE_DIGS) places_rdx = 1;
Gavin Howarda1879192019-04-30 18:55:08 -0600427 else places_rdx = 0;
428 }
429 else places_rdx -= n->rdx;
430 }
Gavin Howard4631f3a2019-04-30 10:48:40 -0600431
432 if (places_rdx) {
Gavin Howarde396dff2019-05-01 07:28:36 -0600433 bc_num_expand(n, bc_vm_growSize(n->len, places_rdx));
Gavin Howard834fde12019-04-30 08:00:05 -0600434 memmove(n->num + places_rdx, n->num, BC_NUM_SIZE(n->len));
435 memset(n->num, 0, BC_NUM_SIZE(places_rdx));
436 n->len += places_rdx;
Gavin Howardb1558662019-04-26 14:32:55 -0600437 }
Gavin Howard4631f3a2019-04-30 10:48:40 -0600438
439 if (places > n->scale) n->scale = n->rdx = 0;
440 else {
441 n->scale -= places;
442 n->rdx = BC_NUM_RDX(n->scale);
443 }
Gavin Howardb1558662019-04-26 14:32:55 -0600444
Gavin Howard92ba4e52019-05-10 20:49:13 -0600445 if (shift) s = bc_num_shift(n, BC_BASE_DIGS - dig);
Gavin Howardb1558662019-04-26 14:32:55 -0600446
447 bc_num_clean(n);
448
449 return BC_SIG && !s ? BC_STATUS_SIGNAL : s;
450}
451
452static BcStatus bc_num_shiftRight(BcNum *restrict n, size_t places) {
453
454 BcStatus s = BC_STATUS_SUCCESS;
Gavin Howard2956a5e2019-05-08 08:04:06 -0600455 BcBigDig dig;
Gavin Howardb5e6da92019-04-30 19:58:04 -0600456 size_t places_rdx, scale, scale_mod, int_len, expand;
457 bool shift;
Gavin Howardb1558662019-04-26 14:32:55 -0600458
459 if (!places) return s;
Gavin Howard14913fc2019-04-23 14:36:03 -0600460 if (BC_NUM_ZERO(n)) {
Gavin Howardb1558662019-04-26 14:32:55 -0600461 n->scale += places;
462 bc_num_expand(n, BC_NUM_RDX(n->scale));
463 return s;
Gavin Howard14913fc2019-04-23 14:36:03 -0600464 }
465
Gavin Howard92ba4e52019-05-10 20:49:13 -0600466 dig = (BcBigDig) (places % BC_BASE_DIGS);
Gavin Howardb5e6da92019-04-30 19:58:04 -0600467 shift = (dig != 0);
Gavin Howard834fde12019-04-30 08:00:05 -0600468 scale = n->scale;
Gavin Howard92ba4e52019-05-10 20:49:13 -0600469 scale_mod = scale % BC_BASE_DIGS;
470 scale_mod = scale_mod ? scale_mod : BC_BASE_DIGS;
Gavin Howard834fde12019-04-30 08:00:05 -0600471 int_len = bc_num_int(n);
Gavin Howardb1558662019-04-26 14:32:55 -0600472 places_rdx = BC_NUM_RDX(places);
Gavin Howard7ad5a662019-02-19 14:40:46 -0700473
Gavin Howard92ba4e52019-05-10 20:49:13 -0600474 if (scale_mod + dig > BC_BASE_DIGS) {
Gavin Howardb5e6da92019-04-30 19:58:04 -0600475 expand = places_rdx - 1;
476 places_rdx = 1;
Gavin Howard834fde12019-04-30 08:00:05 -0600477 }
478 else {
Gavin Howardb5e6da92019-04-30 19:58:04 -0600479 expand = places_rdx;
480 places_rdx = 0;
Gavin Howard7ad5a662019-02-19 14:40:46 -0700481 }
482
Gavin Howardb5e6da92019-04-30 19:58:04 -0600483 if (expand > int_len) expand -= int_len;
484 else expand = 0;
485
Gavin Howard92ba4e52019-05-10 20:49:13 -0600486 bc_num_extend(n, places_rdx * BC_BASE_DIGS);
Gavin Howarde396dff2019-05-01 07:28:36 -0600487 bc_num_expand(n, bc_vm_growSize(expand, n->len));
Gavin Howardb5e6da92019-04-30 19:58:04 -0600488 memset(n->num + n->len, 0, BC_NUM_SIZE(expand));
489 n->len += expand;
Gavin Howard834fde12019-04-30 08:00:05 -0600490 n->scale = n->rdx = 0;
Gavin Howardb1558662019-04-26 14:32:55 -0600491
Gavin Howard834fde12019-04-30 08:00:05 -0600492 if (shift) s = bc_num_shift(n, dig);
493
494 n->scale = scale + places;
495 n->rdx = BC_NUM_RDX(n->scale);
496
497 bc_num_clean(n);
Gavin Howard7ad5a662019-02-19 14:40:46 -0700498
499 assert(n->rdx <= n->len && n->len <= n->cap);
Gavin Howardb1558662019-04-26 14:32:55 -0600500 assert(n->rdx == BC_NUM_RDX(n->scale));
501
502 return BC_SIG && !s ? BC_STATUS_SIGNAL : s;
Gavin Howard7ad5a662019-02-19 14:40:46 -0700503}
Gavin Howard7ad5a662019-02-19 14:40:46 -0700504
Gavin Howard1cbfe242019-01-09 17:13:11 -0700505static BcStatus bc_num_inv(BcNum *a, BcNum *b, size_t scale) {
Gavin Howard9c4358c2018-03-22 20:11:28 -0600506
Gavin Howard63738202018-09-26 15:34:20 -0600507 BcNum one;
Gavin Howardd32d7df2018-10-15 08:42:25 -0600508 BcDig num[2];
Gavin Howard9c4358c2018-03-22 20:11:28 -0600509
Gavin Howard971a2672019-04-26 14:32:07 -0600510 assert(BC_NUM_NONZERO(a));
Gavin Howard1769abc2019-02-21 09:36:14 -0700511
Gavin Howardd32d7df2018-10-15 08:42:25 -0600512 one.cap = 2;
513 one.num = num;
Gavin Howard63738202018-09-26 15:34:20 -0600514 bc_num_one(&one);
Gavin Howard9c4358c2018-03-22 20:11:28 -0600515
Gavin Howardd32d7df2018-10-15 08:42:25 -0600516 return bc_num_div(&one, a, b, scale);
Gavin Howard9c4358c2018-03-22 20:11:28 -0600517}
518
Gavin Howard7bda4782018-12-28 09:53:22 -0700519#if BC_ENABLE_EXTRA_MATH
Gavin Howard1cbfe242019-01-09 17:13:11 -0700520static BcStatus bc_num_intop(const BcNum *a, const BcNum *b, BcNum *restrict c,
Gavin Howard2956a5e2019-05-08 08:04:06 -0600521 BcBigDig *v)
Gavin Howard3c02ed32018-12-28 18:17:15 -0700522{
Gavin Howardecafd4f2019-02-23 09:30:45 -0700523 if (BC_ERR(b->rdx)) return bc_vm_err(BC_ERROR_MATH_NON_INTEGER);
Gavin Howardac4d9122018-12-27 23:59:12 -0700524 bc_num_copy(c, a);
Gavin Howard2956a5e2019-05-08 08:04:06 -0600525 return bc_num_bigdig(b, v);
Gavin Howardac4d9122018-12-27 23:59:12 -0700526}
Gavin Howard7bda4782018-12-28 09:53:22 -0700527#endif // BC_ENABLE_EXTRA_MATH
Gavin Howardac4d9122018-12-27 23:59:12 -0700528
Gavin Howard1cbfe242019-01-09 17:13:11 -0700529static BcStatus bc_num_a(BcNum *a, BcNum *b, BcNum *restrict c, size_t sub) {
Gavin Howard6fbdb292018-02-27 15:44:48 -0700530
Gavin Howard8a921bd2018-10-11 14:15:32 -0600531 BcDig *ptr, *ptr_a, *ptr_b, *ptr_c;
Gavin Howard63738202018-09-26 15:34:20 -0600532 size_t i, max, min_rdx, min_int, diff, a_int, b_int;
Gavin Howard2956a5e2019-05-08 08:04:06 -0600533 BcBigDig carry;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700534
Gavin Howard63738202018-09-26 15:34:20 -0600535 // Because this function doesn't need to use scale (per the bc spec),
536 // I am hijacking it to say whether it's doing an add or a subtract.
Gavin Howard73cce1a2018-09-06 15:16:09 -0600537
Gavin Howardddce6d42019-01-03 14:07:06 -0700538 if (BC_NUM_ZERO(a)) {
Gavin Howardad0ecfe2018-10-30 01:16:01 -0600539 bc_num_copy(c, b);
Gavin Howardddce6d42019-01-03 14:07:06 -0700540 if (sub && BC_NUM_NONZERO(c)) c->neg = !c->neg;
Gavin Howardad0ecfe2018-10-30 01:16:01 -0600541 return BC_STATUS_SUCCESS;
Gavin Howard63738202018-09-26 15:34:20 -0600542 }
Gavin Howardddce6d42019-01-03 14:07:06 -0700543 if (BC_NUM_ZERO(b)) {
Gavin Howardad0ecfe2018-10-30 01:16:01 -0600544 bc_num_copy(c, a);
545 return BC_STATUS_SUCCESS;
546 }
Gavin Howardf6964a12018-03-14 10:52:31 -0600547
Gavin Howard63738202018-09-26 15:34:20 -0600548 c->neg = a->neg;
Gavin Howard63738202018-09-26 15:34:20 -0600549 c->rdx = BC_MAX(a->rdx, b->rdx);
Gavin Howardfba50a22019-04-26 10:07:21 -0600550 c->scale = BC_MAX(a->scale, b->scale);
Gavin Howard63738202018-09-26 15:34:20 -0600551 min_rdx = BC_MIN(a->rdx, b->rdx);
Gavin Howard6fbdb292018-02-27 15:44:48 -0700552
Gavin Howard63738202018-09-26 15:34:20 -0600553 if (a->rdx > b->rdx) {
554 diff = a->rdx - b->rdx;
555 ptr = a->num;
556 ptr_a = a->num + diff;
557 ptr_b = b->num;
558 }
559 else {
560 diff = b->rdx - a->rdx;
561 ptr = b->num;
562 ptr_a = a->num;
563 ptr_b = b->num + diff;
564 }
Gavin Howard6fbdb292018-02-27 15:44:48 -0700565
Gavin Howardcafcd3e2019-01-22 09:54:24 -0700566 for (ptr_c = c->num, i = 0; i < diff; ++i) ptr_c[i] = ptr[i];
Gavin Howard6fbdb292018-02-27 15:44:48 -0700567
Gavin Howardcafcd3e2019-01-22 09:54:24 -0700568 c->len = diff;
Gavin Howard63738202018-09-26 15:34:20 -0600569 ptr_c += diff;
Gavin Howard7b7d2f32019-02-21 16:46:47 -0700570 a_int = bc_num_int(a);
571 b_int = bc_num_int(b);
Gavin Howard6fbdb292018-02-27 15:44:48 -0700572
Gavin Howard890d0c02018-10-30 16:34:50 -0600573 if (a_int > b_int) {
Gavin Howard63738202018-09-26 15:34:20 -0600574 min_int = b_int;
575 max = a_int;
576 ptr = ptr_a;
577 }
578 else {
579 min_int = a_int;
580 max = b_int;
581 ptr = ptr_b;
582 }
Gavin Howard6fbdb292018-02-27 15:44:48 -0700583
Gavin Howard2d188a52019-02-25 14:19:08 -0700584 for (carry = 0, i = 0; BC_NO_SIG && i < min_rdx + min_int; ++i) {
Gavin Howard2956a5e2019-05-08 08:04:06 -0600585 BcBigDig in = ((BcBigDig) ptr_a[i]) + ((BcBigDig) ptr_b[i]);
Gavin Howarddd7a0fd2019-01-22 09:56:46 -0700586 carry = bc_num_addDigit(ptr_c + i, in, carry);
Gavin Howard63738202018-09-26 15:34:20 -0600587 }
Gavin Howard6fbdb292018-02-27 15:44:48 -0700588
Gavin Howard2d188a52019-02-25 14:19:08 -0700589 for (; BC_NO_SIG && i < max + min_rdx; ++i)
Gavin Howard2956a5e2019-05-08 08:04:06 -0600590 carry = bc_num_addDigit(ptr_c + i, (BcBigDig) ptr[i], carry);
Gavin Howardcafcd3e2019-01-22 09:54:24 -0700591
592 c->len += i;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700593
Gavin Howard1ab22d22019-01-03 13:32:17 -0700594 if (carry) c->num[c->len++] = (BcDig) carry;
Gavin Howard2ea7dc42018-05-22 14:02:02 -0600595
Gavin Howard2d188a52019-02-25 14:19:08 -0700596 return BC_SIG ? BC_STATUS_SIGNAL : BC_STATUS_SUCCESS;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700597}
598
Gavin Howard1cbfe242019-01-09 17:13:11 -0700599static BcStatus bc_num_s(BcNum *a, BcNum *b, BcNum *restrict c, size_t sub) {
Gavin Howard6fbdb292018-02-27 15:44:48 -0700600
Gavin Howarddc0ee9e2019-02-16 23:27:08 -0700601 BcStatus s;
Gavin Howard63738202018-09-26 15:34:20 -0600602 ssize_t cmp;
603 BcNum *minuend, *subtrahend;
604 size_t start;
605 bool aneg, bneg, neg;
Gavin Howarda1c090a2018-03-05 14:20:33 -0700606
Gavin Howard63738202018-09-26 15:34:20 -0600607 // Because this function doesn't need to use scale (per the bc spec),
608 // I am hijacking it to say whether it's doing an add or a subtract.
Gavin Howard6fbdb292018-02-27 15:44:48 -0700609
Gavin Howardddce6d42019-01-03 14:07:06 -0700610 if (BC_NUM_ZERO(a)) {
Gavin Howardad0ecfe2018-10-30 01:16:01 -0600611 bc_num_copy(c, b);
Gavin Howardddce6d42019-01-03 14:07:06 -0700612 if (sub && BC_NUM_NONZERO(c)) c->neg = !c->neg;
Gavin Howardad0ecfe2018-10-30 01:16:01 -0600613 return BC_STATUS_SUCCESS;
Gavin Howard63738202018-09-26 15:34:20 -0600614 }
Gavin Howardddce6d42019-01-03 14:07:06 -0700615 if (BC_NUM_ZERO(b)) {
Gavin Howardad0ecfe2018-10-30 01:16:01 -0600616 bc_num_copy(c, a);
617 return BC_STATUS_SUCCESS;
618 }
Gavin Howard6fbdb292018-02-27 15:44:48 -0700619
Gavin Howard63738202018-09-26 15:34:20 -0600620 aneg = a->neg;
621 bneg = b->neg;
622 a->neg = b->neg = false;
Gavin Howard9a6f7a42018-03-05 14:07:06 -0700623
Gavin Howard63738202018-09-26 15:34:20 -0600624 cmp = bc_num_cmp(a, b);
Gavin Howard3378af82019-05-11 08:29:39 -0600625
626#if BC_ENABLE_SIGNALS
Gavin Howarda36f0312019-05-09 10:23:07 -0600627 if (cmp == BC_NUM_CMP_SIGNAL) return BC_STATUS_SIGNAL;
Gavin Howard3378af82019-05-11 08:29:39 -0600628#endif // BC_ENABLE_SIGNALS
Gavin Howard9a6f7a42018-03-05 14:07:06 -0700629
Gavin Howard63738202018-09-26 15:34:20 -0600630 a->neg = aneg;
631 b->neg = bneg;
Gavin Howard9a6f7a42018-03-05 14:07:06 -0700632
Gavin Howard1ab22d22019-01-03 13:32:17 -0700633 if (!cmp) {
Gavin Howardf8ddb6d2018-10-22 12:58:53 -0600634 bc_num_setToZero(c, BC_MAX(a->rdx, b->rdx));
Gavin Howard63738202018-09-26 15:34:20 -0600635 return BC_STATUS_SUCCESS;
636 }
Gavin Howardddce6d42019-01-03 14:07:06 -0700637
638 if (cmp > 0) {
Gavin Howard63738202018-09-26 15:34:20 -0600639 neg = a->neg;
640 minuend = a;
641 subtrahend = b;
642 }
643 else {
644 neg = b->neg;
645 if (sub) neg = !neg;
646 minuend = b;
647 subtrahend = a;
648 }
Gavin Howard9a6f7a42018-03-05 14:07:06 -0700649
Gavin Howardad0ecfe2018-10-30 01:16:01 -0600650 bc_num_copy(c, minuend);
Gavin Howard63738202018-09-26 15:34:20 -0600651 c->neg = neg;
Gavin Howard9a6f7a42018-03-05 14:07:06 -0700652
Gavin Howarde6f326e2019-04-26 10:13:45 -0600653 if (c->scale < subtrahend->scale) {
654 bc_num_extend(c, subtrahend->scale - c->scale);
Gavin Howard63738202018-09-26 15:34:20 -0600655 start = 0;
656 }
657 else start = c->rdx - subtrahend->rdx;
Gavin Howard9a6f7a42018-03-05 14:07:06 -0700658
Gavin Howarddc0ee9e2019-02-16 23:27:08 -0700659 s = bc_num_subArrays(c->num + start, subtrahend->num, subtrahend->len);
Gavin Howardfa4983a2019-02-16 23:43:03 -0700660
Gavin Howard63738202018-09-26 15:34:20 -0600661 bc_num_clean(c);
Gavin Howard9a6f7a42018-03-05 14:07:06 -0700662
Gavin Howarddc0ee9e2019-02-16 23:27:08 -0700663 return s;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700664}
665
Gavin Howarddc4e3712019-04-23 14:56:29 -0600666static BcStatus bc_num_m_simp(const BcNum *a, const BcNum *b, BcNum *restrict c)
667{
Gavin Howard971a2672019-04-26 14:32:07 -0600668 size_t i, alen = a->len, blen = b->len, clen;
Gavin Howard5083aec2019-04-23 18:03:46 -0600669 BcDig *ptr_a = a->num, *ptr_b = b->num, *ptr_c;
Gavin Howard2956a5e2019-05-08 08:04:06 -0600670 BcBigDig sum = 0, carry = 0;
Gavin Howarddc4e3712019-04-23 14:56:29 -0600671
Gavin Howard971a2672019-04-26 14:32:07 -0600672 assert(sizeof(sum) >= sizeof(BcDig) * 2);
Gavin Howardb538d802019-04-23 16:50:55 -0600673 assert(!a->rdx && !b->rdx);
674
Gavin Howard5083aec2019-04-23 18:03:46 -0600675 clen = bc_vm_growSize(alen, blen);
Gavin Howarde396dff2019-05-01 07:28:36 -0600676 bc_num_expand(c, bc_vm_growSize(clen, 1));
Gavin Howarddc4e3712019-04-23 14:56:29 -0600677
678 ptr_c = c->num;
Gavin Howard404ece72019-04-26 15:59:15 -0600679 memset(ptr_c, 0, BC_NUM_SIZE(c->cap));
Gavin Howarddc4e3712019-04-23 14:56:29 -0600680
Gavin Howard5083aec2019-04-23 18:03:46 -0600681 for (i = 0; BC_NO_SIG && i < clen; ++i) {
Gavin Howarddc4e3712019-04-23 14:56:29 -0600682
Gavin Howard5083aec2019-04-23 18:03:46 -0600683 ssize_t sidx = (ssize_t) (i - blen + 1);
684 size_t j = (size_t) BC_MAX(0, sidx), k = BC_MIN(i, blen - 1);
Gavin Howarddc4e3712019-04-23 14:56:29 -0600685
Stefan Essera299ffc2019-04-24 23:28:32 +0200686 for (; BC_NO_SIG && j < alen && k < blen; ++j, --k) {
Stefan Eßer4b111c22019-04-24 17:23:08 -0600687
Gavin Howard2956a5e2019-05-08 08:04:06 -0600688 sum += ((BcBigDig) ptr_a[j]) * ((BcBigDig) ptr_b[k]);
Gavin Howarddc4e3712019-04-23 14:56:29 -0600689
Gavin Howard92ba4e52019-05-10 20:49:13 -0600690 if (sum >= BC_BASE_POW) {
691 carry += sum / BC_BASE_POW;
692 sum %= BC_BASE_POW;
Stefan Essera299ffc2019-04-24 23:28:32 +0200693 }
694 }
Stefan Eßer4b111c22019-04-24 17:23:08 -0600695
Stefan Essera299ffc2019-04-24 23:28:32 +0200696 ptr_c[i] = (BcDig) sum;
Gavin Howard92ba4e52019-05-10 20:49:13 -0600697 assert(ptr_c[i] < BC_BASE_POW);
Stefan Essera299ffc2019-04-24 23:28:32 +0200698 sum = carry;
699 carry = 0;
Gavin Howarddc4e3712019-04-23 14:56:29 -0600700 }
701
Gavin Howard5083aec2019-04-23 18:03:46 -0600702 if (sum) {
Gavin Howard92ba4e52019-05-10 20:49:13 -0600703 assert(sum < BC_BASE_POW);
Gavin Howard12a7cd22019-04-24 07:10:14 -0600704 ptr_c[clen] = (BcDig) sum;
Gavin Howard5083aec2019-04-23 18:03:46 -0600705 clen += 1;
706 }
707
708 c->len = clen;
Gavin Howarda58840c2019-05-07 19:33:53 -0600709
Gavin Howarddc4e3712019-04-23 14:56:29 -0600710 return BC_SIG ? BC_STATUS_SIGNAL : BC_STATUS_SUCCESS;
711}
712
Gavin Howard14c354a2019-04-24 14:28:24 -0600713static BcStatus bc_num_shiftAddSub(BcNum *restrict n, const BcNum *restrict a,
714 size_t shift, BcNumShiftAddOp op)
715{
716 assert(n->len >= shift + a->len);
717 assert(!n->rdx && !a->rdx);
718 return op(n->num + shift, a->num, a->len);
719}
720
721static BcStatus bc_num_k(BcNum *a, BcNum *b, BcNum *restrict c) {
Gavin Howard773c86b2018-11-02 14:07:19 -0600722
Gavin Howard9b801632019-02-16 23:34:06 -0700723 BcStatus s;
Stefan Esser14eff462019-04-25 07:42:03 +0200724 size_t max, max2, total;
Gavin Howard305249a2018-10-15 20:24:47 -0600725 BcNum l1, h1, l2, h2, m2, m1, z0, z1, z2, temp;
Gavin Howard5bede412019-02-23 09:31:28 -0700726 BcDig *digs, *dig_ptr;
Gavin Howard14c354a2019-04-24 14:28:24 -0600727 BcNumShiftAddOp op;
Gavin Howardb2f01a42019-05-10 20:00:32 -0600728 bool aone = BC_NUM_ONE(a);
Gavin Howard337fe602018-09-01 15:10:59 -0600729
Gavin Howard14913fc2019-04-23 14:36:03 -0600730 assert(BC_NUM_ZERO(c));
731
Gavin Howardddce6d42019-01-03 14:07:06 -0700732 // This is here because the function is recursive.
Gavin Howard5083aec2019-04-23 18:03:46 -0600733 if (BC_SIG) return BC_STATUS_SIGNAL;
Gavin Howardf1ae2be2019-05-09 11:58:22 -0600734 if (BC_NUM_ZERO(a) || BC_NUM_ZERO(b)) return BC_STATUS_SUCCESS;
Gavin Howardb2f01a42019-05-10 20:00:32 -0600735 if (aone || BC_NUM_ONE(b)) {
Gavin Howardad0ecfe2018-10-30 01:16:01 -0600736 bc_num_copy(c, aone ? b : a);
Gavin Howardb2f01a42019-05-10 20:00:32 -0600737 if ((aone && a->neg) || b->neg) c->neg = !c->neg;
Gavin Howardad0ecfe2018-10-30 01:16:01 -0600738 return BC_STATUS_SUCCESS;
739 }
Gavin Howardb7417b12019-05-10 18:39:46 -0600740 if (a->len < BC_NUM_KARATSUBA_LEN || b->len < BC_NUM_KARATSUBA_LEN)
Gavin Howarddc4e3712019-04-23 14:56:29 -0600741 return bc_num_m_simp(a, b, c);
Gavin Howard6fbdb292018-02-27 15:44:48 -0700742
Gavin Howarde0432502019-02-26 16:23:45 -0700743 max = BC_MAX(a->len, b->len);
Stefan Essera299ffc2019-04-24 23:28:32 +0200744 max = BC_MAX(max, BC_NUM_DEF_SIZE);
Gavin Howarde0432502019-02-26 16:23:45 -0700745 max2 = (max + 1) / 2;
746
Stefan Esser5fd43a32019-04-25 07:05:24 +0200747 total = bc_vm_arraySize(BC_NUM_KARATSUBA_ALLOCS, max);
Gavin Howard404ece72019-04-26 15:59:15 -0600748 digs = dig_ptr = bc_vm_malloc(BC_NUM_SIZE(total));
Gavin Howard5bede412019-02-23 09:31:28 -0700749
750 bc_num_setup(&l1, dig_ptr, max);
Stefan Essera299ffc2019-04-24 23:28:32 +0200751 dig_ptr += max;
Gavin Howard5bede412019-02-23 09:31:28 -0700752 bc_num_setup(&h1, dig_ptr, max);
Stefan Essera299ffc2019-04-24 23:28:32 +0200753 dig_ptr += max;
Gavin Howard5bede412019-02-23 09:31:28 -0700754 bc_num_setup(&l2, 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(&h2, 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(&m1, 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(&m2, dig_ptr, max);
Gavin Howard57223022019-04-24 08:37:14 -0600761 max = bc_vm_growSize(max, 1);
Gavin Howardad0ecfe2018-10-30 01:16:01 -0600762 bc_num_init(&z0, max);
763 bc_num_init(&z1, max);
764 bc_num_init(&z2, max);
Gavin Howard14c354a2019-04-24 14:28:24 -0600765 max = bc_vm_growSize(max, max) + 1;
766 bc_num_init(&temp, max);
Gavin Howard0dfe2922018-05-22 13:57:02 -0600767
Gavin Howardad0ecfe2018-10-30 01:16:01 -0600768 bc_num_split(a, max2, &l1, &h1);
Gavin Howard14c354a2019-04-24 14:28:24 -0600769 bc_num_clean(&l1);
770 bc_num_clean(&h1);
Gavin Howardad0ecfe2018-10-30 01:16:01 -0600771 bc_num_split(b, max2, &l2, &h2);
Gavin Howard14c354a2019-04-24 14:28:24 -0600772 bc_num_clean(&l2);
773 bc_num_clean(&h2);
Gavin Howard6fbdb292018-02-27 15:44:48 -0700774
Gavin Howard14c354a2019-04-24 14:28:24 -0600775 bc_num_expand(c, max);
776 c->len = max;
Gavin Howard404ece72019-04-26 15:59:15 -0600777 memset(c->num, 0, BC_NUM_SIZE(c->len));
Gavin Howard305249a2018-10-15 20:24:47 -0600778
Gavin Howard14c354a2019-04-24 14:28:24 -0600779 s = bc_num_sub(&h1, &l1, &m1, 0);
780 if (BC_ERR(s)) goto err;
781 s = bc_num_sub(&l2, &h2, &m2, 0);
782 if (BC_ERR(s)) goto err;
Gavin Howard305249a2018-10-15 20:24:47 -0600783
Gavin Howard971a2672019-04-26 14:32:07 -0600784 if (BC_NUM_NONZERO(&h1) && BC_NUM_NONZERO(&h2)) {
Gavin Howard305249a2018-10-15 20:24:47 -0600785
Gavin Howard14c354a2019-04-24 14:28:24 -0600786 s = bc_num_m(&h1, &h2, &z2, 0);
787 if (BC_ERR(s)) goto err;
788 bc_num_clean(&z2);
789
790 s = bc_num_shiftAddSub(c, &z2, max2 * 2, bc_num_addArrays);
791 if (BC_ERR(s)) goto err;
792 s = bc_num_shiftAddSub(c, &z2, max2, bc_num_addArrays);
793 if (BC_ERR(s)) goto err;
794 }
795
Gavin Howard971a2672019-04-26 14:32:07 -0600796 if (BC_NUM_NONZERO(&l1) && BC_NUM_NONZERO(&l2)) {
Gavin Howard14c354a2019-04-24 14:28:24 -0600797
798 s = bc_num_m(&l1, &l2, &z0, 0);
799 if (BC_ERR(s)) goto err;
800 bc_num_clean(&z0);
801
802 s = bc_num_shiftAddSub(c, &z0, max2, bc_num_addArrays);
803 if (BC_ERR(s)) goto err;
804 s = bc_num_shiftAddSub(c, &z0, 0, bc_num_addArrays);
805 if (BC_ERR(s)) goto err;
806 }
807
Gavin Howard971a2672019-04-26 14:32:07 -0600808 if (BC_NUM_NONZERO(&m1) && BC_NUM_NONZERO(&m2)) {
Gavin Howard14c354a2019-04-24 14:28:24 -0600809
810 s = bc_num_m(&m1, &m2, &z1, 0);
811 if (BC_ERR(s)) goto err;
812 bc_num_clean(&z1);
813
814 op = (m1.neg != m2.neg) ? bc_num_subArrays : bc_num_addArrays;
815 s = bc_num_shiftAddSub(c, &z1, max2, op);
816 if (BC_ERR(s)) goto err;
817 }
Gavin Howard305249a2018-10-15 20:24:47 -0600818
819err:
Gavin Howard5bede412019-02-23 09:31:28 -0700820 free(digs);
Gavin Howard305249a2018-10-15 20:24:47 -0600821 bc_num_free(&temp);
Gavin Howard305249a2018-10-15 20:24:47 -0600822 bc_num_free(&z2);
Gavin Howard305249a2018-10-15 20:24:47 -0600823 bc_num_free(&z1);
Gavin Howard305249a2018-10-15 20:24:47 -0600824 bc_num_free(&z0);
Gavin Howard305249a2018-10-15 20:24:47 -0600825 return s;
826}
827
Gavin Howard1cbfe242019-01-09 17:13:11 -0700828static BcStatus bc_num_m(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale) {
Gavin Howard305249a2018-10-15 20:24:47 -0600829
830 BcStatus s;
831 BcNum cpa, cpb;
Gavin Howard48a0e472019-04-26 15:39:46 -0600832 size_t ascale, bscale, ardx, brdx, azero = 0, bzero = 0, zero, len, rscale;
Gavin Howard305249a2018-10-15 20:24:47 -0600833
Gavin Howard14913fc2019-04-23 14:36:03 -0600834 bc_num_setToZero(c, 0);
Gavin Howardcf9f2462019-04-27 06:43:57 -0600835 ascale = a->scale;
836 bscale = b->scale;
837 scale = BC_MAX(scale, ascale);
Gavin Howarda9a887a2019-04-29 14:00:56 -0600838 scale = BC_MAX(scale, bscale);
Gavin Howard7cb5f6d2019-04-29 14:01:44 -0600839
Gavin Howard48a0e472019-04-26 15:39:46 -0600840 rscale = ascale + bscale;
Gavin Howard14913fc2019-04-23 14:36:03 -0600841 scale = BC_MIN(rscale, scale);
Gavin Howardb3f1ee22019-05-07 21:49:15 -0600842
Gavin Howardcc2974b2019-05-08 11:07:28 -0600843 if ((a->len == 1 || b->len == 1) && !a->rdx && !b->rdx) {
Gavin Howardb3f1ee22019-05-07 21:49:15 -0600844
845 BcNum *operand;
Gavin Howard2956a5e2019-05-08 08:04:06 -0600846 BcBigDig dig;
Gavin Howardb3f1ee22019-05-07 21:49:15 -0600847
848 if (a->len == 1) {
Gavin Howard2956a5e2019-05-08 08:04:06 -0600849 dig = (BcBigDig) a->num[0];
Gavin Howardb3f1ee22019-05-07 21:49:15 -0600850 operand = b;
851 }
852 else {
Gavin Howard2956a5e2019-05-08 08:04:06 -0600853 dig = (BcBigDig) b->num[0];
Gavin Howardb3f1ee22019-05-07 21:49:15 -0600854 operand = a;
855 }
856
857 s = bc_num_mulArray(operand, dig, c);
Gavin Howard66a93552019-05-09 17:13:59 -0600858 if (BC_ERROR_SIGNAL_ONLY(s)) return s;
Gavin Howardb3f1ee22019-05-07 21:49:15 -0600859
860 c->scale = operand->scale;
861 c->rdx = operand->rdx;
862 c->neg = (a->neg != b->neg);
Gavin Howard66a93552019-05-09 17:13:59 -0600863 return s;
Gavin Howardb3f1ee22019-05-07 21:49:15 -0600864 }
865
Gavin Howard66a93552019-05-09 17:13:59 -0600866 bc_num_init(&cpa, a->len + a->rdx);
867 bc_num_init(&cpb, b->len + b->rdx);
868 bc_num_copy(&cpa, a);
869 bc_num_copy(&cpb, b);
870
871 cpa.neg = cpb.neg = false;
872
Gavin Howard92ba4e52019-05-10 20:49:13 -0600873 ardx = cpa.rdx * BC_BASE_DIGS;
Gavin Howard48a0e472019-04-26 15:39:46 -0600874 s = bc_num_shiftLeft(&cpa, ardx);
875 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howard14913fc2019-04-23 14:36:03 -0600876 bc_num_clean(&cpa);
877 azero = bc_num_shiftZero(&cpa);
Gavin Howard48a0e472019-04-26 15:39:46 -0600878
Gavin Howard92ba4e52019-05-10 20:49:13 -0600879 brdx = cpb.rdx * BC_BASE_DIGS;
Gavin Howard48a0e472019-04-26 15:39:46 -0600880 s = bc_num_shiftLeft(&cpb, brdx);
881 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howard14913fc2019-04-23 14:36:03 -0600882 bzero = bc_num_shiftZero(&cpb);
883 bc_num_clean(&cpb);
884
Gavin Howard14c354a2019-04-24 14:28:24 -0600885 s = bc_num_k(&cpa, &cpb, c);
Gavin Howardc78e7522019-02-22 13:24:25 -0700886 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howard305249a2018-10-15 20:24:47 -0600887
Gavin Howard14913fc2019-04-23 14:36:03 -0600888 zero = bc_vm_growSize(azero, bzero);
889 len = bc_vm_growSize(c->len, zero);
Gavin Howardad0ecfe2018-10-30 01:16:01 -0600890
Gavin Howard14913fc2019-04-23 14:36:03 -0600891 bc_num_expand(c, len);
Gavin Howard92ba4e52019-05-10 20:49:13 -0600892 s = bc_num_shiftLeft(c, (len - c->len) * BC_BASE_DIGS);
Gavin Howard48a0e472019-04-26 15:39:46 -0600893 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
894 s = bc_num_shiftRight(c, ardx + brdx);
895 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howardb3f1ee22019-05-07 21:49:15 -0600896
Gavin Howardad0ecfe2018-10-30 01:16:01 -0600897 bc_num_retireMul(c, scale, a->neg, b->neg);
Gavin Howardcf9f2462019-04-27 06:43:57 -0600898
Gavin Howard305249a2018-10-15 20:24:47 -0600899err:
Gavin Howard14913fc2019-04-23 14:36:03 -0600900 bc_num_unshiftZero(&cpb, bzero);
Gavin Howard305249a2018-10-15 20:24:47 -0600901 bc_num_free(&cpb);
Gavin Howard14913fc2019-04-23 14:36:03 -0600902 bc_num_unshiftZero(&cpa, azero);
Gavin Howard305249a2018-10-15 20:24:47 -0600903 bc_num_free(&cpa);
904 return s;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700905}
906
Gavin Howard1980e032019-05-08 17:21:17 -0600907static ssize_t bc_num_divCmp(const BcDig *a, const BcNum *b, size_t len) {
908
909 ssize_t cmp;
910
911 if (b->len > len && a[len]) cmp = bc_num_compare(a, b->num, len + 1);
912 else if (b->len <= len) {
913 if (a[len]) cmp = 1;
914 else cmp = bc_num_compare(a, b->num, len);
915 }
916 else cmp = -1;
917
918 return cmp;
919}
920
Gavin Howardd5b27c82019-05-09 12:13:07 -0600921static BcStatus bc_num_d_long(BcNum *restrict a, const BcNum *restrict b,
Gavin Howardbd60ec72019-05-10 08:01:22 -0600922 BcNum *restrict c, size_t scale)
Gavin Howardd5b27c82019-05-09 12:13:07 -0600923{
Gavin Howardad0ecfe2018-10-30 01:16:01 -0600924 BcStatus s = BC_STATUS_SUCCESS;
Gavin Howard92cf9052019-05-09 08:05:57 -0600925 BcBigDig divisor, q;
Gavin Howardbd60ec72019-05-10 08:01:22 -0600926 size_t len, end, i, rdx;
927 BcNum cpb, sub, temp;
Gavin Howardd5b27c82019-05-09 12:13:07 -0600928 BcDig *n;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700929
Gavin Howardd5b27c82019-05-09 12:13:07 -0600930 len = b->len;
931 end = a->len - len;
932 divisor = (BcBigDig) b->num[len - 1];
Gavin Howard070a6ce2019-05-08 11:50:25 -0600933
Gavin Howardd5b27c82019-05-09 12:13:07 -0600934 bc_num_expand(c, a->len);
935 memset(c->num + end, 0, (c->cap - end) * sizeof(BcDig));
Gavin Howardbd60ec72019-05-10 08:01:22 -0600936
Gavin Howardd5b27c82019-05-09 12:13:07 -0600937 c->rdx = a->rdx;
938 c->scale = a->scale;
939 c->len = a->len;
940
Gavin Howardbd60ec72019-05-10 08:01:22 -0600941 assert(c->scale >= scale);
942 rdx = c->rdx - BC_NUM_RDX(scale);
Gavin Howard61349662019-05-08 11:08:45 -0600943
Gavin Howardbd60ec72019-05-10 08:01:22 -0600944 bc_num_init(&cpb, len + 1);
945 bc_num_init(&sub, len + 1);
946 bc_num_init(&temp, len + 1);
947
948 for (i = end - 1; BC_NO_SIG && BC_NO_ERR(!s) && i < end && i >= rdx; --i) {
Gavin Howard61349662019-05-08 11:08:45 -0600949
Gavin Howard070a6ce2019-05-08 11:50:25 -0600950 ssize_t cmp;
951
Gavin Howardd5b27c82019-05-09 12:13:07 -0600952 n = a->num + i;
Gavin Howard070a6ce2019-05-08 11:50:25 -0600953 q = 0;
954
Gavin Howard1980e032019-05-08 17:21:17 -0600955 cmp = bc_num_divCmp(n, b, len);
Gavin Howard3378af82019-05-11 08:29:39 -0600956
957#if BC_ENABLE_SIGNALS
Gavin Howarda36f0312019-05-09 10:23:07 -0600958 if (cmp == BC_NUM_CMP_SIGNAL) break;
Gavin Howard3378af82019-05-11 08:29:39 -0600959#endif // BC_ENABLE_SIGNALS
Gavin Howard070a6ce2019-05-08 11:50:25 -0600960
Gavin Howardbd60ec72019-05-10 08:01:22 -0600961 if (!cmp) {
962
963 q = 1;
964
965 s = bc_num_mulArray(b, (BcBigDig) q, &cpb);
966 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
967 }
Gavin Howard070a6ce2019-05-08 11:50:25 -0600968 else if (cmp > 0) {
969
Gavin Howard92cf9052019-05-09 08:05:57 -0600970 BcBigDig n1, pow, dividend;
Gavin Howardbd60ec72019-05-10 08:01:22 -0600971 size_t cpblen;
Gavin Howard1980e032019-05-08 17:21:17 -0600972
973 n1 = (BcBigDig) n[len];
Gavin Howard92ba4e52019-05-10 20:49:13 -0600974 dividend = n1 * BC_BASE_POW + (BcBigDig) n[len - 1];
Gavin Howard92cf9052019-05-09 08:05:57 -0600975 q = (dividend / divisor + 1);
Gavin Howard92ba4e52019-05-10 20:49:13 -0600976 q = q > BC_BASE_POW ? BC_BASE_POW : q;
Gavin Howard1980e032019-05-08 17:21:17 -0600977 dividend = ((BcBigDig) bc_num_log10((size_t) q));
978
Gavin Howardc0b8a7c2019-05-13 01:56:30 -0600979 assert(dividend > 0);
980
Gavin Howard1980e032019-05-08 17:21:17 -0600981 pow = bc_num_pow10[dividend - 1];
Gavin Howard1980e032019-05-08 17:21:17 -0600982
Gavin Howard92cf9052019-05-09 08:05:57 -0600983 s = bc_num_mulArray(b, (BcBigDig) q, &cpb);
Gavin Howard1980e032019-05-08 17:21:17 -0600984 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
985
Gavin Howard6945eaf2019-05-09 17:06:54 -0600986 s = bc_num_mulArray(b, pow, &sub);
Gavin Howard1980e032019-05-08 17:21:17 -0600987 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
988
Gavin Howardbd60ec72019-05-10 08:01:22 -0600989 cpblen = cpb.len;
990
Gavin Howard1980e032019-05-08 17:21:17 -0600991 while (BC_NO_SIG && BC_NO_ERR(!s) && pow > 0) {
992
Gavin Howardbd60ec72019-05-10 08:01:22 -0600993 s = bc_num_subArrays(cpb.num, sub.num, sub.len);
Gavin Howard1980e032019-05-08 17:21:17 -0600994 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
995
Gavin Howardbd60ec72019-05-10 08:01:22 -0600996 bc_num_clean(&cpb);
Gavin Howard1980e032019-05-08 17:21:17 -0600997
Gavin Howardbd60ec72019-05-10 08:01:22 -0600998 cmp = bc_num_divCmp(n, &cpb, len);
Gavin Howard3378af82019-05-11 08:29:39 -0600999
1000#if BC_ENABLE_SIGNALS
Gavin Howarda36f0312019-05-09 10:23:07 -06001001 if (cmp == BC_NUM_CMP_SIGNAL) goto err;
Gavin Howard3378af82019-05-11 08:29:39 -06001002#endif // BC_ENABLE_SIGNALS
Gavin Howard1980e032019-05-08 17:21:17 -06001003
1004 while (BC_NO_SIG && BC_NO_ERR(!s) && cmp < 0) {
1005
1006 q -= pow;
1007
1008 s = bc_num_subArrays(cpb.num, sub.num, sub.len);
1009 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
1010
Gavin Howardbd60ec72019-05-10 08:01:22 -06001011 bc_num_clean(&cpb);
Gavin Howard1980e032019-05-08 17:21:17 -06001012
Gavin Howardbd60ec72019-05-10 08:01:22 -06001013 cmp = bc_num_divCmp(n, &cpb, len);
Gavin Howard3378af82019-05-11 08:29:39 -06001014
1015#if BC_ENABLE_SIGNALS
Gavin Howarda36f0312019-05-09 10:23:07 -06001016 if (cmp == BC_NUM_CMP_SIGNAL) goto err;
Gavin Howard3378af82019-05-11 08:29:39 -06001017#endif // BC_ENABLE_SIGNALS
Gavin Howard1980e032019-05-08 17:21:17 -06001018 }
1019
1020 pow /= BC_BASE;
Gavin Howard1980e032019-05-08 17:21:17 -06001021
Gavin Howardbd60ec72019-05-10 08:01:22 -06001022 if (pow) {
1023
Gavin Howard5a684032019-05-10 08:07:19 -06001024 BcBigDig rem;
1025
Gavin Howardbd60ec72019-05-10 08:01:22 -06001026 s = bc_num_addArrays(cpb.num, sub.num, sub.len);
1027 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
1028
1029 cpb.len = cpblen;
1030 bc_num_clean(&cpb);
1031
1032 bc_num_copy(&temp, &sub);
1033 s = bc_num_divArray(&temp, 10, &sub, &rem);
1034 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
1035 assert(rem == 0);
1036 }
Gavin Howard070a6ce2019-05-08 11:50:25 -06001037 }
Gavin Howard1980e032019-05-08 17:21:17 -06001038
1039 q -= 1;
1040 }
1041
Gavin Howard92ba4e52019-05-10 20:49:13 -06001042 assert(q <= BC_BASE_POW);
Gavin Howard1980e032019-05-08 17:21:17 -06001043
1044 if (q) {
1045
Gavin Howard1980e032019-05-08 17:21:17 -06001046 s = bc_num_subArrays(n, cpb.num, len);
1047 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howard070a6ce2019-05-08 11:50:25 -06001048 }
1049
Gavin Howard92cf9052019-05-09 08:05:57 -06001050 c->num[i] = (BcDig) q;
Gavin Howard61349662019-05-08 11:08:45 -06001051 }
1052
Gavin Howard1980e032019-05-08 17:21:17 -06001053err:
Gavin Howardd5b27c82019-05-09 12:13:07 -06001054 if (BC_NO_ERR(!s) && BC_SIG) s = BC_STATUS_SIGNAL;
Gavin Howardbd60ec72019-05-10 08:01:22 -06001055 bc_num_free(&temp);
Gavin Howard66a93552019-05-09 17:13:59 -06001056 bc_num_free(&cpb);
Gavin Howard66a93552019-05-09 17:13:59 -06001057 bc_num_free(&sub);
Gavin Howardd5b27c82019-05-09 12:13:07 -06001058 return s;
1059}
1060
Gavin Howardd5b27c82019-05-09 12:13:07 -06001061static BcStatus bc_num_d(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale) {
1062
1063 BcStatus s = BC_STATUS_SUCCESS;
Gavin Howardfcb74462019-05-09 14:42:35 -06001064 size_t len;
1065 BcNum cpa, cpb;
Gavin Howardd5b27c82019-05-09 12:13:07 -06001066 bool zero = true;
1067
1068 if (BC_NUM_ZERO(b)) return bc_vm_err(BC_ERROR_MATH_DIVIDE_BY_ZERO);
1069 if (BC_NUM_ZERO(a)) {
1070 bc_num_setToZero(c, scale);
1071 return BC_STATUS_SUCCESS;
1072 }
Gavin Howardb2f01a42019-05-10 20:00:32 -06001073 if (BC_NUM_ONE(b)) {
Gavin Howardd5b27c82019-05-09 12:13:07 -06001074 bc_num_copy(c, a);
1075 bc_num_retireMul(c, scale, a->neg, b->neg);
1076 return BC_STATUS_SUCCESS;
1077 }
1078 if (!a->rdx && !b->rdx && b->len == 1 && !scale) {
1079 BcBigDig rem;
1080 s = bc_num_divArray(a, (BcBigDig) b->num[0], c, &rem);
1081 bc_num_retireMul(c, scale, a->neg, b->neg);
1082 return s;
1083 }
1084
1085 len = bc_num_mulReq(a, b, scale);
1086 bc_num_init(&cpa, len);
1087 bc_num_copy(&cpa, a);
Gavin Howardfcb74462019-05-09 14:42:35 -06001088 bc_num_createCopy(&cpb, b);
Gavin Howardd5b27c82019-05-09 12:13:07 -06001089
Gavin Howard03e35dd2019-05-11 06:56:30 -06001090 len = b->len;
1091
Gavin Howardd5b27c82019-05-09 12:13:07 -06001092 if (len > cpa.len) {
1093 bc_num_expand(&cpa, bc_vm_growSize(len, 2));
Gavin Howard92ba4e52019-05-10 20:49:13 -06001094 bc_num_extend(&cpa, (len - cpa.len) * BC_BASE_DIGS);
Gavin Howardd5b27c82019-05-09 12:13:07 -06001095 }
1096
Gavin Howard92ba4e52019-05-10 20:49:13 -06001097 cpa.scale = cpa.rdx * BC_BASE_DIGS;
Gavin Howardd5b27c82019-05-09 12:13:07 -06001098
1099 bc_num_extend(&cpa, b->scale);
1100 cpa.rdx -= BC_NUM_RDX(b->scale);
Gavin Howard92ba4e52019-05-10 20:49:13 -06001101 cpa.scale = cpa.rdx * BC_BASE_DIGS;
Gavin Howardbd60ec72019-05-10 08:01:22 -06001102 if (scale > cpa.scale) {
1103 bc_num_extend(&cpa, scale);
Gavin Howard92ba4e52019-05-10 20:49:13 -06001104 cpa.scale = cpa.rdx * BC_BASE_DIGS;
Gavin Howardbd60ec72019-05-10 08:01:22 -06001105 }
Gavin Howardd5b27c82019-05-09 12:13:07 -06001106
1107 if (b->rdx == b->len) {
Gavin Howardfcb74462019-05-09 14:42:35 -06001108 size_t i;
Gavin Howardd5b27c82019-05-09 12:13:07 -06001109 for (i = 0; zero && i < len; ++i) zero = !b->num[len - i - 1];
1110 assert(i != len || !zero);
1111 len -= i - 1;
1112 }
1113
1114 if (cpa.cap == cpa.len) bc_num_expand(&cpa, bc_vm_growSize(cpa.len, 1));
1115
1116 // We want an extra zero in front to make things simpler.
1117 cpa.num[cpa.len++] = 0;
1118
Gavin Howardfcb74462019-05-09 14:42:35 -06001119 if (cpa.rdx == cpa.len) cpa.len = bc_num_nonzeroLen(&cpa);
1120 if (cpb.rdx == cpb.len) cpb.len = bc_num_nonzeroLen(&cpb);
1121 cpb.scale = cpb.rdx = 0;
Gavin Howardd5b27c82019-05-09 12:13:07 -06001122
Gavin Howardbd60ec72019-05-10 08:01:22 -06001123 s = bc_num_d_long(&cpa, &cpb, c, scale);
Gavin Howardd5b27c82019-05-09 12:13:07 -06001124
1125 if (BC_NO_ERR(!s)) {
1126 bc_num_retireMul(c, scale, a->neg, b->neg);
1127 if (BC_SIG) s = BC_STATUS_SIGNAL;
1128 }
1129
Gavin Howardfcb74462019-05-09 14:42:35 -06001130 bc_num_free(&cpb);
Gavin Howard1980e032019-05-08 17:21:17 -06001131 bc_num_free(&cpa);
Gavin Howardd5b27c82019-05-09 12:13:07 -06001132
Gavin Howard63738202018-09-26 15:34:20 -06001133 return s;
Gavin Howard6fbdb292018-02-27 15:44:48 -07001134}
1135
Gavin Howard1cbfe242019-01-09 17:13:11 -07001136static BcStatus bc_num_r(BcNum *a, BcNum *b, BcNum *restrict c,
1137 BcNum *restrict d, size_t scale, size_t ts)
Gavin Howard18441082018-10-22 10:03:30 -06001138{
Gavin Howard63738202018-09-26 15:34:20 -06001139 BcStatus s;
Gavin Howard18441082018-10-22 10:03:30 -06001140 BcNum temp;
1141 bool neg;
Gavin Howard6fbdb292018-02-27 15:44:48 -07001142
Gavin Howardddce6d42019-01-03 14:07:06 -07001143 if (BC_NUM_ZERO(b)) return bc_vm_err(BC_ERROR_MATH_DIVIDE_BY_ZERO);
1144 if (BC_NUM_ZERO(a)) {
Gavin Howard99ca4992019-01-02 13:48:49 -07001145 bc_num_setToZero(c, ts);
Gavin Howardf8ddb6d2018-10-22 12:58:53 -06001146 bc_num_setToZero(d, ts);
Gavin Howard63738202018-09-26 15:34:20 -06001147 return BC_STATUS_SUCCESS;
1148 }
Gavin Howard8b254872018-03-14 01:13:35 -06001149
Gavin Howardad0ecfe2018-10-30 01:16:01 -06001150 bc_num_init(&temp, d->cap);
Gavin Howard1769abc2019-02-21 09:36:14 -07001151 s = bc_num_d(a, b, c, scale);
1152 assert(!s || s == BC_STATUS_SIGNAL);
Gavin Howardc78e7522019-02-22 13:24:25 -07001153 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howard6fbdb292018-02-27 15:44:48 -07001154
Gavin Howard996fc222019-04-29 13:22:38 -06001155 if (scale) scale = ts + 1;
Gavin Howard3115c012018-10-04 10:53:56 -06001156
Gavin Howard53eba8b2018-10-31 15:14:37 -06001157 s = bc_num_m(c, b, &temp, scale);
Gavin Howardc78e7522019-02-22 13:24:25 -07001158 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howard53eba8b2018-10-31 15:14:37 -06001159 s = bc_num_sub(a, &temp, d, scale);
Gavin Howardc78e7522019-02-22 13:24:25 -07001160 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howard5d149cf2018-09-06 13:46:23 -06001161
Gavin Howard77445432019-04-26 15:59:33 -06001162 if (ts > d->scale && BC_NUM_NONZERO(d)) bc_num_extend(d, ts - d->scale);
Gavin Howard18441082018-10-22 10:03:30 -06001163
1164 neg = d->neg;
Gavin Howardad0ecfe2018-10-30 01:16:01 -06001165 bc_num_retireMul(d, ts, a->neg, b->neg);
Gavin Howard996fc222019-04-29 13:22:38 -06001166 d->neg = BC_NUM_NONZERO(d) ? neg : false;
Gavin Howard6fbdb292018-02-27 15:44:48 -07001167
1168err:
Gavin Howard18441082018-10-22 10:03:30 -06001169 bc_num_free(&temp);
1170 return s;
1171}
1172
Gavin Howard2d188a52019-02-25 14:19:08 -07001173static BcStatus bc_num_rem(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale)
1174{
Gavin Howard18441082018-10-22 10:03:30 -06001175 BcStatus s;
1176 BcNum c1;
Gavin Howard798508b2019-02-21 16:45:32 -07001177 size_t ts;
Gavin Howard18441082018-10-22 10:03:30 -06001178
Gavin Howard77445432019-04-26 15:59:33 -06001179 ts = bc_vm_growSize(scale, b->scale);
1180 ts = BC_MAX(ts, a->scale);
Gavin Howard798508b2019-02-21 16:45:32 -07001181
1182 bc_num_init(&c1, bc_num_mulReq(a, b, ts));
Gavin Howard54b946a2018-10-23 12:06:57 -06001183 s = bc_num_r(a, b, &c1, c, scale, ts);
Gavin Howard63738202018-09-26 15:34:20 -06001184 bc_num_free(&c1);
Gavin Howardd64ce7b2018-10-24 16:20:20 -06001185
Gavin Howard63738202018-09-26 15:34:20 -06001186 return s;
Gavin Howard6fbdb292018-02-27 15:44:48 -07001187}
1188
Gavin Howard1cbfe242019-01-09 17:13:11 -07001189static BcStatus bc_num_p(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale) {
Gavin Howard6fbdb292018-02-27 15:44:48 -07001190
Gavin Howardad0ecfe2018-10-30 01:16:01 -06001191 BcStatus s = BC_STATUS_SUCCESS;
Gavin Howard63738202018-09-26 15:34:20 -06001192 BcNum copy;
Gavin Howard2956a5e2019-05-08 08:04:06 -06001193 BcBigDig pow = 0;
Gavin Howard63738202018-09-26 15:34:20 -06001194 size_t i, powrdx, resrdx;
1195 bool neg, zero;
Gavin Howard6fbdb292018-02-27 15:44:48 -07001196
Gavin Howardecafd4f2019-02-23 09:30:45 -07001197 if (BC_ERR(b->rdx)) return bc_vm_err(BC_ERROR_MATH_NON_INTEGER);
Gavin Howard6fbdb292018-02-27 15:44:48 -07001198
Gavin Howardddce6d42019-01-03 14:07:06 -07001199 if (BC_NUM_ZERO(b)) {
Gavin Howard63738202018-09-26 15:34:20 -06001200 bc_num_one(c);
Gavin Howard61abdbe2018-10-22 13:05:38 -06001201 return BC_STATUS_SUCCESS;
Gavin Howard63738202018-09-26 15:34:20 -06001202 }
Gavin Howardddce6d42019-01-03 14:07:06 -07001203 if (BC_NUM_ZERO(a)) {
Gavin Howardf8ddb6d2018-10-22 12:58:53 -06001204 bc_num_setToZero(c, scale);
Gavin Howard63738202018-09-26 15:34:20 -06001205 return BC_STATUS_SUCCESS;
1206 }
Gavin Howardb2f01a42019-05-10 20:00:32 -06001207 if (BC_NUM_ONE(b)) {
Gavin Howardad0ecfe2018-10-30 01:16:01 -06001208 if (!b->neg) bc_num_copy(c, a);
Gavin Howard63738202018-09-26 15:34:20 -06001209 else s = bc_num_inv(a, c, scale);
Gavin Howard63738202018-09-26 15:34:20 -06001210 return s;
1211 }
Gavin Howard6fbdb292018-02-27 15:44:48 -07001212
Gavin Howard63738202018-09-26 15:34:20 -06001213 neg = b->neg;
1214 b->neg = false;
Gavin Howard2956a5e2019-05-08 08:04:06 -06001215 s = bc_num_bigdig(b, &pow);
Gavin Howardfb14efc2018-12-22 14:01:58 -07001216 b->neg = neg;
Gavin Howard53eba8b2018-10-31 15:14:37 -06001217 if (s) return s;
1218
Gavin Howardbaa4f582019-01-24 13:56:35 -07001219 bc_num_createCopy(&copy, a);
Gavin Howard6fbdb292018-02-27 15:44:48 -07001220
Gavin Howardeec07282019-05-10 20:10:18 -06001221 if (!neg) {
1222 size_t max = BC_MAX(scale, a->scale), scalepow = a->scale * pow;
1223 scale = BC_MIN(scalepow, max);
1224 }
Gavin Howardb29674f2018-03-22 22:24:58 -06001225
Gavin Howardd497c232019-04-29 10:32:38 -06001226 for (powrdx = a->scale; BC_NO_SIG && !(pow & 1); pow >>= 1) {
Gavin Howard63738202018-09-26 15:34:20 -06001227 powrdx <<= 1;
Gavin Howard53eba8b2018-10-31 15:14:37 -06001228 s = bc_num_mul(&copy, &copy, &copy, powrdx);
Gavin Howardc78e7522019-02-22 13:24:25 -07001229 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howard63738202018-09-26 15:34:20 -06001230 }
Gavin Howard6fb635f2018-03-03 12:45:42 -07001231
Gavin Howard2d188a52019-02-25 14:19:08 -07001232 if (BC_SIG) goto sig_err;
Gavin Howard6fb635f2018-03-03 12:45:42 -07001233
Gavin Howardad0ecfe2018-10-30 01:16:01 -06001234 bc_num_copy(c, &copy);
Gavin Howard1ab22d22019-01-03 13:32:17 -07001235 resrdx = powrdx;
Gavin Howardad0ecfe2018-10-30 01:16:01 -06001236
Gavin Howard2d188a52019-02-25 14:19:08 -07001237 while (BC_NO_SIG && (pow >>= 1)) {
Gavin Howard53eba8b2018-10-31 15:14:37 -06001238
Gavin Howard4d5daba2019-01-07 14:42:50 -07001239 powrdx <<= 1;
Gavin Howard53eba8b2018-10-31 15:14:37 -06001240 s = bc_num_mul(&copy, &copy, &copy, powrdx);
Gavin Howardc78e7522019-02-22 13:24:25 -07001241 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howard53eba8b2018-10-31 15:14:37 -06001242
Gavin Howard63738202018-09-26 15:34:20 -06001243 if (pow & 1) {
1244 resrdx += powrdx;
Gavin Howard53eba8b2018-10-31 15:14:37 -06001245 s = bc_num_mul(c, &copy, c, resrdx);
Gavin Howardc78e7522019-02-22 13:24:25 -07001246 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howard63738202018-09-26 15:34:20 -06001247 }
1248 }
Gavin Howard6fbdb292018-02-27 15:44:48 -07001249
Gavin Howard2d188a52019-02-25 14:19:08 -07001250 if (BC_SIG) goto sig_err;
Gavin Howard9b801632019-02-16 23:34:06 -07001251 if (neg) {
1252 s = bc_num_inv(c, c, scale);
Gavin Howardc78e7522019-02-22 13:24:25 -07001253 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howard9b801632019-02-16 23:34:06 -07001254 }
1255
Stefan Eßerb5318342019-04-29 10:31:26 -06001256 if (c->scale > scale) bc_num_truncate(c, c->scale - scale);
Gavin Howard1d959152018-03-03 23:33:13 -07001257
Gavin Howard53eba8b2018-10-31 15:14:37 -06001258 // We can't use bc_num_clean() here.
Gavin Howard63738202018-09-26 15:34:20 -06001259 for (zero = true, i = 0; zero && i < c->len; ++i) zero = !c->num[i];
Gavin Howardf8ddb6d2018-10-22 12:58:53 -06001260 if (zero) bc_num_setToZero(c, scale);
Gavin Howard1d959152018-03-03 23:33:13 -07001261
Gavin Howard9b801632019-02-16 23:34:06 -07001262sig_err:
Gavin Howard2d188a52019-02-25 14:19:08 -07001263 if (BC_NO_ERR(!s) && BC_SIG) s = BC_STATUS_SIGNAL;
Gavin Howard6fbdb292018-02-27 15:44:48 -07001264err:
Gavin Howard63738202018-09-26 15:34:20 -06001265 bc_num_free(&copy);
1266 return s;
Gavin Howard6fbdb292018-02-27 15:44:48 -07001267}
1268
Gavin Howard7bda4782018-12-28 09:53:22 -07001269#if BC_ENABLE_EXTRA_MATH
Gavin Howard2d188a52019-02-25 14:19:08 -07001270static BcStatus bc_num_place(BcNum *a, BcNum *b, BcNum *restrict c,
1271 size_t scale)
1272{
Gavin Howardac4d9122018-12-27 23:59:12 -07001273 BcStatus s = BC_STATUS_SUCCESS;
Gavin Howard2956a5e2019-05-08 08:04:06 -06001274 BcBigDig val = 0;
Gavin Howardac4d9122018-12-27 23:59:12 -07001275
1276 BC_UNUSED(scale);
1277
1278 s = bc_num_intop(a, b, c, &val);
Gavin Howardecafd4f2019-02-23 09:30:45 -07001279 if (BC_ERR(s)) return s;
Gavin Howardac4d9122018-12-27 23:59:12 -07001280
Gavin Howardc9bef2d2019-04-26 14:46:52 -06001281 if (val < c->scale) bc_num_truncate(c, c->scale - val);
1282 else if (val > c->scale) bc_num_extend(c, val - c->scale);
Gavin Howardac4d9122018-12-27 23:59:12 -07001283
1284 return s;
1285}
1286
Gavin Howard2d188a52019-02-25 14:19:08 -07001287static BcStatus bc_num_left(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale)
1288{
Gavin Howardac4d9122018-12-27 23:59:12 -07001289 BcStatus s = BC_STATUS_SUCCESS;
Gavin Howard2956a5e2019-05-08 08:04:06 -06001290 BcBigDig val = 0;
Gavin Howardac4d9122018-12-27 23:59:12 -07001291
1292 BC_UNUSED(scale);
1293
1294 s = bc_num_intop(a, b, c, &val);
Gavin Howardecafd4f2019-02-23 09:30:45 -07001295 if (BC_ERR(s)) return s;
Gavin Howardac4d9122018-12-27 23:59:12 -07001296
Gavin Howardb1558662019-04-26 14:32:55 -06001297 return bc_num_shiftLeft(c, (size_t) val);
Gavin Howardac4d9122018-12-27 23:59:12 -07001298}
1299
Gavin Howard2d188a52019-02-25 14:19:08 -07001300static BcStatus bc_num_right(BcNum *a, BcNum *b, BcNum *restrict c,
1301 size_t scale)
1302{
Gavin Howardac4d9122018-12-27 23:59:12 -07001303 BcStatus s = BC_STATUS_SUCCESS;
Gavin Howard2956a5e2019-05-08 08:04:06 -06001304 BcBigDig val = 0;
Gavin Howardac4d9122018-12-27 23:59:12 -07001305
1306 BC_UNUSED(scale);
1307
1308 s = bc_num_intop(a, b, c, &val);
Gavin Howardecafd4f2019-02-23 09:30:45 -07001309 if (BC_ERR(s)) return s;
Gavin Howardac4d9122018-12-27 23:59:12 -07001310
Gavin Howard2a366922019-01-16 10:50:20 -07001311 if (BC_NUM_ZERO(c)) return s;
1312
Gavin Howardb1558662019-04-26 14:32:55 -06001313 return bc_num_shiftRight(c, (size_t) val);
Gavin Howardac4d9122018-12-27 23:59:12 -07001314}
Gavin Howard7bda4782018-12-28 09:53:22 -07001315#endif // BC_ENABLE_EXTRA_MATH
Gavin Howardac4d9122018-12-27 23:59:12 -07001316
Gavin Howard1cbfe242019-01-09 17:13:11 -07001317static BcStatus bc_num_binary(BcNum *a, BcNum *b, BcNum *c, size_t scale,
1318 BcNumBinaryOp op, size_t req)
Gavin Howard6fbdb292018-02-27 15:44:48 -07001319{
Gavin Howarda1c44392018-09-27 12:41:15 -06001320 BcStatus s;
Gavin Howard63738202018-09-26 15:34:20 -06001321 BcNum num2, *ptr_a, *ptr_b;
1322 bool init = false;
Gavin Howard6fbdb292018-02-27 15:44:48 -07001323
Gavin Howard63738202018-09-26 15:34:20 -06001324 assert(a && b && c && op);
Gavin Howard6fbdb292018-02-27 15:44:48 -07001325
Gavin Howard890d0c02018-10-30 16:34:50 -06001326 if (c == a) {
Gavin Howard63738202018-09-26 15:34:20 -06001327 ptr_a = &num2;
Gavin Howardba009802018-09-29 04:41:51 -06001328 memcpy(ptr_a, c, sizeof(BcNum));
Gavin Howard890d0c02018-10-30 16:34:50 -06001329 init = true;
Gavin Howard63738202018-09-26 15:34:20 -06001330 }
1331 else ptr_a = a;
Gavin Howard6fbdb292018-02-27 15:44:48 -07001332
Gavin Howard63738202018-09-26 15:34:20 -06001333 if (c == b) {
Gavin Howardba009802018-09-29 04:41:51 -06001334 ptr_b = &num2;
Gavin Howardba009802018-09-29 04:41:51 -06001335 if (c != a) {
1336 memcpy(ptr_b, c, sizeof(BcNum));
Gavin Howard63738202018-09-26 15:34:20 -06001337 init = true;
1338 }
1339 }
1340 else ptr_b = b;
Gavin Howard6fbdb292018-02-27 15:44:48 -07001341
Gavin Howardad0ecfe2018-10-30 01:16:01 -06001342 if (init) bc_num_init(c, req);
1343 else bc_num_expand(c, req);
Gavin Howard6fbdb292018-02-27 15:44:48 -07001344
Gavin Howardad0ecfe2018-10-30 01:16:01 -06001345 s = op(ptr_a, ptr_b, c, scale);
Gavin Howard6fbdb292018-02-27 15:44:48 -07001346
Gavin Howardddce6d42019-01-03 14:07:06 -07001347 assert(!c->neg || BC_NUM_NONZERO(c));
Gavin Howard87c29c72019-03-28 11:22:51 -06001348 assert(c->rdx <= c->len || !c->len || s);
Gavin Howard3cf769e2018-10-11 13:55:11 -06001349
Gavin Howardba009802018-09-29 04:41:51 -06001350 if (init) bc_num_free(&num2);
Gavin Howardad0ecfe2018-10-30 01:16:01 -06001351
Gavin Howardee9d01e2019-02-16 22:48:11 -07001352 return s;
Gavin Howard6fbdb292018-02-27 15:44:48 -07001353}
1354
Gavin Howardc90ed902019-01-02 13:07:49 -07001355#ifndef NDEBUG
Gavin Howard1cbfe242019-01-09 17:13:11 -07001356static bool bc_num_strValid(const char *val) {
Gavin Howard6fbdb292018-02-27 15:44:48 -07001357
Gavin Howardc7d655b2018-12-28 19:45:13 -07001358 bool radix = false;
Gavin Howard63738202018-09-26 15:34:20 -06001359 size_t i, len = strlen(val);
Gavin Howard6fbdb292018-02-27 15:44:48 -07001360
Gavin Howard63738202018-09-26 15:34:20 -06001361 if (!len) return true;
Gavin Howard6fbdb292018-02-27 15:44:48 -07001362
Gavin Howard63738202018-09-26 15:34:20 -06001363 for (i = 0; i < len; ++i) {
Gavin Howard6fbdb292018-02-27 15:44:48 -07001364
Gavin Howard8a921bd2018-10-11 14:15:32 -06001365 BcDig c = val[i];
Gavin Howardf6e3fb32018-08-09 13:48:59 -06001366
Gavin Howard63738202018-09-26 15:34:20 -06001367 if (c == '.') {
Gavin Howard6fbdb292018-02-27 15:44:48 -07001368
Gavin Howard63738202018-09-26 15:34:20 -06001369 if (radix) return false;
Gavin Howard6fbdb292018-02-27 15:44:48 -07001370
Gavin Howard63738202018-09-26 15:34:20 -06001371 radix = true;
1372 continue;
1373 }
Gavin Howard6fbdb292018-02-27 15:44:48 -07001374
Gavin Howardc7d655b2018-12-28 19:45:13 -07001375 if (!(isdigit(c) || isupper(c))) return false;
Gavin Howard63738202018-09-26 15:34:20 -06001376 }
Gavin Howard6fbdb292018-02-27 15:44:48 -07001377
Gavin Howard63738202018-09-26 15:34:20 -06001378 return true;
Gavin Howard6fbdb292018-02-27 15:44:48 -07001379}
Gavin Howardc90ed902019-01-02 13:07:49 -07001380#endif // NDEBUG
Gavin Howard6fbdb292018-02-27 15:44:48 -07001381
Gavin Howard2956a5e2019-05-08 08:04:06 -06001382static BcBigDig bc_num_parseChar(char c, size_t base_t) {
Gavin Howardc7d655b2018-12-28 19:45:13 -07001383
1384 if (isupper(c)) {
1385 c = BC_NUM_NUM_LETTER(c);
Gavin Howard6193aaf2019-01-03 16:44:04 -07001386 c = ((size_t) c) >= base_t ? (char) base_t - 1 : c;
Gavin Howardc7d655b2018-12-28 19:45:13 -07001387 }
1388 else c -= '0';
1389
Gavin Howard2956a5e2019-05-08 08:04:06 -06001390 return (BcBigDig) (uchar) c;
Gavin Howardc7d655b2018-12-28 19:45:13 -07001391}
1392
Gavin Howard1cbfe242019-01-09 17:13:11 -07001393static void bc_num_parseDecimal(BcNum *restrict n, const char *restrict val) {
Gavin Howard6fbdb292018-02-27 15:44:48 -07001394
Gavin Howard3b60abb2019-04-26 08:49:17 -06001395 size_t len, i, temp, mod;
Gavin Howard63738202018-09-26 15:34:20 -06001396 const char *ptr;
Gavin Howardd43ec372019-04-25 21:13:54 -06001397 bool zero = true, rdx;
Stefan Essera299ffc2019-04-24 23:28:32 +02001398
Gavin Howard63738202018-09-26 15:34:20 -06001399 for (i = 0; val[i] == '0'; ++i);
Gavin Howard6fbdb292018-02-27 15:44:48 -07001400
Gavin Howard63738202018-09-26 15:34:20 -06001401 val += i;
Gavin Howard46a7d8d2019-05-06 15:16:12 -06001402 assert(!val[0] || isalnum(val[0]) || val[0] == '.');
1403
1404 // All 0's. We can just return, since this
1405 // procedure expects a virgin (already 0) BcNum.
1406 if (!val[0]) return;
1407
Gavin Howard63738202018-09-26 15:34:20 -06001408 len = strlen(val);
Gavin Howard6fbdb292018-02-27 15:44:48 -07001409
Gavin Howard63738202018-09-26 15:34:20 -06001410 ptr = strchr(val, '.');
Gavin Howardd43ec372019-04-25 21:13:54 -06001411 rdx = (ptr != NULL);
Gavin Howard6fbdb292018-02-27 15:44:48 -07001412
Gavin Howardd43ec372019-04-25 21:13:54 -06001413 for (i = 0; i < len && (zero = (val[i] == '0' || val[i] == '.')); ++i);
Gavin Howard6fbdb292018-02-27 15:44:48 -07001414
Gavin Howardd43ec372019-04-25 21:13:54 -06001415 n->scale = (size_t) (rdx * ((val + len) - (ptr + 1)));
Gavin Howard1c1697c2019-04-26 10:07:45 -06001416 n->rdx = BC_NUM_RDX(n->scale);
Gavin Howardd43ec372019-04-25 21:13:54 -06001417
Gavin Howard3b60abb2019-04-26 08:49:17 -06001418 i = len - (ptr == val ? 0 : i) - rdx;
Gavin Howard48a0e472019-04-26 15:39:46 -06001419 temp = BC_NUM_ROUND_POW(i);
Gavin Howard92ba4e52019-05-10 20:49:13 -06001420 mod = n->scale % BC_BASE_DIGS;
1421 i = mod ? BC_BASE_DIGS - mod : 0;
1422 n->len = ((temp + i) / BC_BASE_DIGS);
Gavin Howardd43ec372019-04-25 21:13:54 -06001423
1424 bc_num_expand(n, n->len);
Gavin Howard404ece72019-04-26 15:59:15 -06001425 memset(n->num, 0, BC_NUM_SIZE(n->len));
Gavin Howard6fbdb292018-02-27 15:44:48 -07001426
Gavin Howard6076dbb2019-04-26 09:47:50 -06001427 if (zero) n->len = n->rdx = 0;
1428 else {
Gavin Howardfcf29012019-04-25 20:12:19 -06001429
Gavin Howard2956a5e2019-05-08 08:04:06 -06001430 BcBigDig exp, pow;
Gavin Howardd43ec372019-04-25 21:13:54 -06001431
Gavin Howard687cf4d2019-05-11 08:55:36 -06001432 assert(i <= BC_NUM_BIGDIG_MAX);
1433
1434 exp = (BcBigDig) i;
Gavin Howardc1542802019-05-08 17:20:51 -06001435 pow = bc_num_pow10[exp];
Gavin Howardd43ec372019-04-25 21:13:54 -06001436
1437 for (i = len - 1; i < len; --i, ++exp) {
Gavin Howard8dd307e2019-01-08 23:05:19 -07001438
Gavin Howardc7d655b2018-12-28 19:45:13 -07001439 char c = val[i];
Gavin Howard8dd307e2019-01-08 23:05:19 -07001440
Gavin Howardd43ec372019-04-25 21:13:54 -06001441 if (c == '.') exp -= 1;
Gavin Howard8dd307e2019-01-08 23:05:19 -07001442 else {
Gavin Howardd43ec372019-04-25 21:13:54 -06001443
Gavin Howard92ba4e52019-05-10 20:49:13 -06001444 size_t idx = exp / BC_BASE_DIGS;
Gavin Howardd43ec372019-04-25 21:13:54 -06001445
Gavin Howard8dd307e2019-01-08 23:05:19 -07001446 if (isupper(c)) c = '9';
Gavin Howard2956a5e2019-05-08 08:04:06 -06001447 n->num[idx] += (((BcBigDig) c) - '0') * pow;
Gavin Howardd43ec372019-04-25 21:13:54 -06001448
Gavin Howard92ba4e52019-05-10 20:49:13 -06001449 if ((exp + 1) % BC_BASE_DIGS == 0) pow = 1;
Gavin Howardd43ec372019-04-25 21:13:54 -06001450 else pow *= BC_BASE;
Gavin Howard8dd307e2019-01-08 23:05:19 -07001451 }
Gavin Howardc7d655b2018-12-28 19:45:13 -07001452 }
Gavin Howardad0ecfe2018-10-30 01:16:01 -06001453 }
Gavin Howard6fbdb292018-02-27 15:44:48 -07001454}
1455
Gavin Howarddc0ee9e2019-02-16 23:27:08 -07001456static BcStatus bc_num_parseBase(BcNum *restrict n, const char *restrict val,
Gavin Howard2956a5e2019-05-08 08:04:06 -06001457 BcBigDig base)
Gavin Howard1cbfe242019-01-09 17:13:11 -07001458{
1459 BcStatus s = BC_STATUS_SUCCESS;
Gavin Howard4c605692019-05-08 09:09:17 -06001460 BcNum temp, mult1, mult2, result1, result2, *m1, *m2, *ptr;
Gavin Howard77445432019-04-26 15:59:33 -06001461 char c = 0;
Gavin Howard11b9afd2018-10-18 14:23:28 -06001462 bool zero = true;
Gavin Howard2956a5e2019-05-08 08:04:06 -06001463 BcBigDig v;
Gavin Howard1769abc2019-02-21 09:36:14 -07001464 size_t i, digs, len = strlen(val);
Gavin Howardede51f02018-03-02 12:30:00 -07001465
Gavin Howard11b9afd2018-10-18 14:23:28 -06001466 for (i = 0; zero && i < len; ++i) zero = (val[i] == '.' || val[i] == '0');
Gavin Howarddc0ee9e2019-02-16 23:27:08 -07001467 if (zero) return BC_STATUS_SUCCESS;
Gavin Howardede51f02018-03-02 12:30:00 -07001468
Gavin Howard2956a5e2019-05-08 08:04:06 -06001469 bc_num_init(&temp, BC_NUM_BIGDIG_LOG10);
Gavin Howard4c605692019-05-08 09:09:17 -06001470 bc_num_init(&mult1, BC_NUM_BIGDIG_LOG10);
Gavin Howardede51f02018-03-02 12:30:00 -07001471
Gavin Howardc7d655b2018-12-28 19:45:13 -07001472 for (i = 0; i < len && (c = val[i]) && c != '.'; ++i) {
Gavin Howard9b801632019-02-16 23:34:06 -07001473
Gavin Howardb3f1ee22019-05-07 21:49:15 -06001474 v = bc_num_parseChar(c, base);
Gavin Howard9b801632019-02-16 23:34:06 -07001475
Gavin Howard4c605692019-05-08 09:09:17 -06001476 s = bc_num_mulArray(n, base, &mult1);
Gavin Howardc78e7522019-02-22 13:24:25 -07001477 if (BC_ERROR_SIGNAL_ONLY(s)) goto int_err;
Gavin Howard2956a5e2019-05-08 08:04:06 -06001478 bc_num_bigdig2num(&temp, v);
Gavin Howard4c605692019-05-08 09:09:17 -06001479 s = bc_num_add(&mult1, &temp, n, 0);
Gavin Howardc78e7522019-02-22 13:24:25 -07001480 if (BC_ERROR_SIGNAL_ONLY(s)) goto int_err;
Gavin Howard63738202018-09-26 15:34:20 -06001481 }
Gavin Howardede51f02018-03-02 12:30:00 -07001482
Gavin Howarddc0ee9e2019-02-16 23:27:08 -07001483 if (i == len && !(c = val[i])) goto int_err;
Gavin Howard53eba8b2018-10-31 15:14:37 -06001484
Gavin Howard63738202018-09-26 15:34:20 -06001485 assert(c == '.');
Gavin Howard4c605692019-05-08 09:09:17 -06001486 bc_num_init(&mult2, BC_NUM_BIGDIG_LOG10);
1487 bc_num_init(&result1, BC_NUM_DEF_SIZE);
1488 bc_num_init(&result2, BC_NUM_DEF_SIZE);
1489 bc_num_one(&mult1);
1490
1491 m1 = &mult1;
1492 m2 = &mult2;
Gavin Howardede51f02018-03-02 12:30:00 -07001493
Gavin Howard8722e832019-05-08 11:12:16 -06001494 for (i += 1, digs = 0; BC_NO_SIG && i < len && (c = val[i]); ++i, ++digs) {
1495
Gavin Howardb3f1ee22019-05-07 21:49:15 -06001496 v = bc_num_parseChar(c, base);
Gavin Howard9b801632019-02-16 23:34:06 -07001497
Gavin Howard4c605692019-05-08 09:09:17 -06001498 s = bc_num_mulArray(&result1, base, &result2);
Gavin Howardc78e7522019-02-22 13:24:25 -07001499 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howarddc0ee9e2019-02-16 23:27:08 -07001500
Gavin Howard2956a5e2019-05-08 08:04:06 -06001501 bc_num_bigdig2num(&temp, v);
Gavin Howard4c605692019-05-08 09:09:17 -06001502 s = bc_num_add(&result2, &temp, &result1, 0);
Gavin Howardc78e7522019-02-22 13:24:25 -07001503 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howard4c605692019-05-08 09:09:17 -06001504 s = bc_num_mulArray(m1, base, m2);
Gavin Howardc78e7522019-02-22 13:24:25 -07001505 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howard4c605692019-05-08 09:09:17 -06001506
1507 ptr = m1;
1508 m1 = m2;
1509 m2 = ptr;
Gavin Howard63738202018-09-26 15:34:20 -06001510 }
Gavin Howardede51f02018-03-02 12:30:00 -07001511
Gavin Howardf2ee6342019-04-09 17:05:20 -06001512 if (BC_SIG) {
1513 s = BC_STATUS_SIGNAL;
1514 goto err;
1515 }
1516
Gavin Howard1769abc2019-02-21 09:36:14 -07001517 // This one cannot be a divide by 0 because mult starts out at 1, then is
1518 // multiplied by base, and base cannot be 0, so mult cannot be 0.
Gavin Howard4c605692019-05-08 09:09:17 -06001519 s = bc_num_div(&result1, m1, &result2, digs * 2);
Gavin Howard1769abc2019-02-21 09:36:14 -07001520 assert(!s || s == BC_STATUS_SIGNAL);
Gavin Howardc78e7522019-02-22 13:24:25 -07001521 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howard4c605692019-05-08 09:09:17 -06001522 bc_num_truncate(&result2, digs);
1523 s = bc_num_add(n, &result2, n, digs);
Gavin Howardc78e7522019-02-22 13:24:25 -07001524 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howardd1417682019-02-15 17:08:42 -07001525
Gavin Howardddce6d42019-01-03 14:07:06 -07001526 if (BC_NUM_NONZERO(n)) {
Gavin Howardb0642be2019-04-30 21:12:22 -06001527 if (n->scale < digs) bc_num_extend(n, digs - n->scale);
Gavin Howard63738202018-09-26 15:34:20 -06001528 }
1529 else bc_num_zero(n);
Gavin Howardede51f02018-03-02 12:30:00 -07001530
Gavin Howardd1417682019-02-15 17:08:42 -07001531err:
Gavin Howard4c605692019-05-08 09:09:17 -06001532 bc_num_free(&result2);
1533 bc_num_free(&result1);
1534 bc_num_free(&mult2);
Gavin Howarddc0ee9e2019-02-16 23:27:08 -07001535int_err:
Gavin Howard4c605692019-05-08 09:09:17 -06001536 bc_num_free(&mult1);
Gavin Howard63738202018-09-26 15:34:20 -06001537 bc_num_free(&temp);
Gavin Howarddc0ee9e2019-02-16 23:27:08 -07001538 return s;
Gavin Howard6fbdb292018-02-27 15:44:48 -07001539}
1540
Gavin Howard1cbfe242019-01-09 17:13:11 -07001541static void bc_num_printNewline() {
Gavin Howard6193aaf2019-01-03 16:44:04 -07001542 if (vm->nchars >= (size_t) (vm->line_len - 1)) {
Gavin Howard48af52e2018-10-30 14:47:38 -06001543 bc_vm_putchar('\\');
1544 bc_vm_putchar('\n');
Gavin Howard63738202018-09-26 15:34:20 -06001545 }
Gavin Howard80977b22018-09-06 14:29:13 -06001546}
1547
Gavin Howardb4a9cdb2019-05-07 15:25:51 -06001548static void bc_num_putchar(int c) {
1549 if (c != '\n') bc_num_printNewline();
1550 bc_vm_putchar(c);
1551}
1552
Gavin Howard40a085f2018-12-03 12:08:59 -07001553#if DC_ENABLED
Gavin Howard1cbfe242019-01-09 17:13:11 -07001554static void bc_num_printChar(size_t n, size_t len, bool rdx) {
Gavin Howard3c02ed32018-12-28 18:17:15 -07001555 BC_UNUSED(rdx);
Gavin Howardeedfb2c2019-05-07 08:00:13 -06001556 BC_UNUSED(len);
1557 assert(len == 1);
Gavin Howard3c02ed32018-12-28 18:17:15 -07001558 bc_vm_putchar((uchar) n);
Gavin Howard83eb8392018-10-09 01:21:19 -06001559}
1560#endif // DC_ENABLED
1561
Gavin Howard1cbfe242019-01-09 17:13:11 -07001562static void bc_num_printDigits(size_t n, size_t len, bool rdx) {
1563
Gavin Howarda84ad992018-12-03 19:11:06 -07001564 size_t exp, pow;
Gavin Howard80977b22018-09-06 14:29:13 -06001565
Gavin Howardb4a9cdb2019-05-07 15:25:51 -06001566 bc_num_putchar(rdx ? '.' : ' ');
Gavin Howard2ed2bfb2018-03-14 02:42:32 -06001567
Gavin Howard530e3072019-04-23 16:23:36 -06001568 for (exp = 0, pow = 1; exp < len - 1; ++exp, pow *= BC_BASE);
Gavin Howard2ed2bfb2018-03-14 02:42:32 -06001569
Gavin Howardeedfb2c2019-05-07 08:00:13 -06001570 for (exp = 0; exp < len; pow /= BC_BASE, ++exp) {
Gavin Howard4b075f22019-05-07 15:27:53 -06001571 size_t dig = n / pow;
Gavin Howard3c02ed32018-12-28 18:17:15 -07001572 n -= dig * pow;
Gavin Howardb4a9cdb2019-05-07 15:25:51 -06001573 bc_num_putchar(((uchar) dig) + '0');
Gavin Howard63738202018-09-26 15:34:20 -06001574 }
Gavin Howard6fbdb292018-02-27 15:44:48 -07001575}
Gavin Howardfe679f02018-02-14 15:50:09 -07001576
Gavin Howard1cbfe242019-01-09 17:13:11 -07001577static void bc_num_printHex(size_t n, size_t len, bool rdx) {
Gavin Howard70e3d602018-12-11 11:00:49 -07001578
Gavin Howardeedfb2c2019-05-07 08:00:13 -06001579 BC_UNUSED(len);
1580
Gavin Howard3c02ed32018-12-28 18:17:15 -07001581 assert(len == 1);
Gavin Howard80977b22018-09-06 14:29:13 -06001582
Gavin Howardb4a9cdb2019-05-07 15:25:51 -06001583 if (rdx) bc_num_putchar('.');
Gavin Howard2682a1f2018-03-03 09:09:13 -07001584
Gavin Howardb4a9cdb2019-05-07 15:25:51 -06001585 bc_num_putchar(bc_num_hex_digits[n]);
Gavin Howard2682a1f2018-03-03 09:09:13 -07001586}
1587
Gavin Howard1cbfe242019-01-09 17:13:11 -07001588static void bc_num_printDecimal(const BcNum *restrict n) {
Gavin Howard32f2beb2018-03-09 11:43:20 -07001589
Gavin Howard51cd7ea2019-04-26 09:48:12 -06001590 size_t i, j, rdx = n->rdx;
1591 bool zero = true;
Gavin Howard92ba4e52019-05-10 20:49:13 -06001592 size_t buffer[BC_BASE_DIGS];
Gavin Howard32f2beb2018-03-09 11:43:20 -07001593
Gavin Howardb4a9cdb2019-05-07 15:25:51 -06001594 if (n->neg) bc_num_putchar('-');
Gavin Howard32f2beb2018-03-09 11:43:20 -07001595
Gavin Howard51cd7ea2019-04-26 09:48:12 -06001596 for (i = n->len - 1; i < n->len; --i) {
1597
1598 BcDig n9 = n->num[i];
1599 size_t temp;
1600 bool irdx = (i == rdx - 1);
1601
1602 zero = (zero & !irdx);
Gavin Howard92ba4e52019-05-10 20:49:13 -06001603 temp = n->scale % BC_BASE_DIGS;
1604 temp = i || !temp ? 0 : BC_BASE_DIGS - temp;
Gavin Howard51cd7ea2019-04-26 09:48:12 -06001605
Gavin Howard92ba4e52019-05-10 20:49:13 -06001606 memset(buffer, 0, BC_BASE_DIGS * sizeof(size_t));
Gavin Howard51cd7ea2019-04-26 09:48:12 -06001607
Gavin Howard92ba4e52019-05-10 20:49:13 -06001608 for (j = 0; n9 && j < BC_BASE_DIGS; ++j) {
Stefan Essera299ffc2019-04-24 23:28:32 +02001609 buffer[j] = n9 % BC_BASE;
1610 n9 /= BC_BASE;
1611 }
Stefan Esser0da17752019-04-25 12:25:15 +02001612
Gavin Howard92ba4e52019-05-10 20:49:13 -06001613 for (j = BC_BASE_DIGS - 1; j < BC_BASE_DIGS && j >= temp; --j) {
1614 bool print_rdx = (irdx & (j == BC_BASE_DIGS - 1));
Gavin Howard51cd7ea2019-04-26 09:48:12 -06001615 zero = (zero && buffer[j] == 0);
1616 if (!zero) bc_num_printHex(buffer[j], 1, print_rdx);
Stefan Essera299ffc2019-04-24 23:28:32 +02001617 }
1618 }
Gavin Howard32f2beb2018-03-09 11:43:20 -07001619}
1620
Gavin Howard7ad5a662019-02-19 14:40:46 -07001621#if BC_ENABLE_EXTRA_MATH
Gavin Howardb1558662019-04-26 14:32:55 -06001622static BcStatus bc_num_printExponent(const BcNum *restrict n, bool eng) {
Gavin Howard7ad5a662019-02-19 14:40:46 -07001623
Gavin Howardb1558662019-04-26 14:32:55 -06001624 BcStatus s = BC_STATUS_SUCCESS;
Gavin Howard7ad5a662019-02-19 14:40:46 -07001625 bool neg = (n->len <= n->rdx);
1626 BcNum temp, exp;
1627 size_t places, mod;
Gavin Howard2956a5e2019-05-08 08:04:06 -06001628 BcDig digs[BC_NUM_BIGDIG_LOG10];
Gavin Howard7ad5a662019-02-19 14:40:46 -07001629
1630 bc_num_createCopy(&temp, n);
1631
1632 if (neg) {
Gavin Howardc1349922019-04-30 20:48:09 -06001633
1634 size_t i, idx = bc_num_nonzeroLen(n) - 1;
1635
1636 places = 1;
1637
Gavin Howard92ba4e52019-05-10 20:49:13 -06001638 for (i = BC_BASE_DIGS - 1; i < BC_BASE_DIGS; --i) {
Gavin Howardc1542802019-05-08 17:20:51 -06001639 if (bc_num_pow10[i] > (BcBigDig) n->num[idx]) places += 1;
Gavin Howardc1349922019-04-30 20:48:09 -06001640 else break;
1641 }
1642
Gavin Howard92ba4e52019-05-10 20:49:13 -06001643 places += (n->rdx - (idx + 1)) * BC_BASE_DIGS;
Gavin Howard7ad5a662019-02-19 14:40:46 -07001644 mod = places % 3;
Gavin Howardc1349922019-04-30 20:48:09 -06001645
Gavin Howard7ad5a662019-02-19 14:40:46 -07001646 if (eng && mod != 0) places += 3 - mod;
Gavin Howardb1558662019-04-26 14:32:55 -06001647 s = bc_num_shiftLeft(&temp, places);
1648 if (BC_ERROR_SIGNAL_ONLY(s)) goto exit;
Gavin Howard7ad5a662019-02-19 14:40:46 -07001649 }
1650 else {
Gavin Howard06fee0c2019-05-09 14:43:20 -06001651 places = bc_num_intDigits(n) - 1;
Gavin Howard7ad5a662019-02-19 14:40:46 -07001652 mod = places % 3;
1653 if (eng && mod != 0) places -= 3 - (3 - mod);
Gavin Howardb1558662019-04-26 14:32:55 -06001654 s = bc_num_shiftRight(&temp, places);
1655 if (BC_ERROR_SIGNAL_ONLY(s)) goto exit;
Gavin Howard7ad5a662019-02-19 14:40:46 -07001656 }
1657
1658 bc_num_printDecimal(&temp);
Gavin Howardb4a9cdb2019-05-07 15:25:51 -06001659 bc_num_putchar('e');
Gavin Howard7ad5a662019-02-19 14:40:46 -07001660
1661 if (!places) {
1662 bc_num_printHex(0, 1, false);
1663 goto exit;
1664 }
1665
Gavin Howardb4a9cdb2019-05-07 15:25:51 -06001666 if (neg) bc_num_putchar('-');
Gavin Howard7ad5a662019-02-19 14:40:46 -07001667
Gavin Howard2956a5e2019-05-08 08:04:06 -06001668 bc_num_setup(&exp, digs, BC_NUM_BIGDIG_LOG10);
1669 bc_num_bigdig2num(&exp, (BcBigDig) places);
Gavin Howard7ad5a662019-02-19 14:40:46 -07001670
1671 bc_num_printDecimal(&exp);
1672
1673exit:
1674 bc_num_free(&temp);
Gavin Howardb1558662019-04-26 14:32:55 -06001675 return BC_SIG ? BC_STATUS_SIGNAL : BC_STATUS_SUCCESS;
Gavin Howard7ad5a662019-02-19 14:40:46 -07001676}
1677#endif // BC_ENABLE_EXTRA_MATH
1678
Gavin Howard2956a5e2019-05-08 08:04:06 -06001679static BcStatus bc_num_printNum(BcNum *restrict n, BcBigDig base,
Gavin Howarddc0ee9e2019-02-16 23:27:08 -07001680 size_t len, BcNumDigitOp print)
Gavin Howardbc7cae82018-03-14 13:43:04 -06001681{
Gavin Howard63738202018-09-26 15:34:20 -06001682 BcStatus s;
1683 BcVec stack;
Gavin Howarda8c2e582019-05-07 11:33:10 -06001684 BcNum intp1, intp2, fracp1, fracp2, digit, flen1, flen2, *n1, *n2, *temp;
Gavin Howard2956a5e2019-05-08 08:04:06 -06001685 BcBigDig dig, *ptr;
Gavin Howard83eb8392018-10-09 01:21:19 -06001686 size_t i;
1687 bool radix;
Gavin Howard1705b982019-05-11 15:18:03 -06001688 BcDig digit_digs[BC_NUM_BIGDIG_LOG10 + 1];
Gavin Howard2682a1f2018-03-03 09:09:13 -07001689
Gavin Howardb3f1ee22019-05-07 21:49:15 -06001690 assert(base > 1);
Gavin Howard3fb24fa2019-02-15 17:18:57 -07001691
Gavin Howardddce6d42019-01-03 14:07:06 -07001692 if (BC_NUM_ZERO(n)) {
1693 print(0, len, false);
Gavin Howarddc0ee9e2019-02-16 23:27:08 -07001694 return BC_STATUS_SUCCESS;
Gavin Howard48af52e2018-10-30 14:47:38 -06001695 }
Gavin Howard9a4b6cd2018-10-23 15:13:30 -06001696
Gavin Howard2956a5e2019-05-08 08:04:06 -06001697 bc_vec_init(&stack, sizeof(BcBigDig), NULL);
Gavin Howarda8c2e582019-05-07 11:33:10 -06001698 bc_num_init(&fracp1, n->rdx);
1699 bc_num_init(&fracp2, n->rdx);
Gavin Howardc4e9a082019-05-07 11:12:50 -06001700 bc_num_setup(&digit, digit_digs, sizeof(digit_digs) / sizeof(BcDig));
Gavin Howardf1148822019-05-11 15:29:24 -06001701 bc_num_init(&flen1, BC_NUM_BIGDIG_LOG10 + 1);
1702 bc_num_init(&flen2, BC_NUM_BIGDIG_LOG10 + 1);
Gavin Howarda8c2e582019-05-07 11:33:10 -06001703 bc_num_one(&flen1);
Gavin Howardc4e9a082019-05-07 11:12:50 -06001704 bc_num_createCopy(&intp1, n);
Gavin Howarda50fc542018-03-29 17:25:38 -06001705
Gavin Howardc4e9a082019-05-07 11:12:50 -06001706 bc_num_truncate(&intp1, intp1.scale);
1707 bc_num_init(&intp2, intp1.len);
1708
Gavin Howarda8c2e582019-05-07 11:33:10 -06001709 s = bc_num_sub(n, &intp1, &fracp1, 0);
Gavin Howardc78e7522019-02-22 13:24:25 -07001710 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howard2682a1f2018-03-03 09:09:13 -07001711
Gavin Howarda8c2e582019-05-07 11:33:10 -06001712 n1 = &intp1;
1713 n2 = &intp2;
Gavin Howardc4e9a082019-05-07 11:12:50 -06001714
Gavin Howarda8c2e582019-05-07 11:33:10 -06001715 while (BC_NO_SIG && BC_NUM_NONZERO(n1)) {
Gavin Howard1769abc2019-02-21 09:36:14 -07001716
1717 // Dividing by base cannot be divide by 0 because base cannot be 0.
Gavin Howardb3f1ee22019-05-07 21:49:15 -06001718 s = bc_num_divArray(n1, base, n2, &dig);
Gavin Howardc78e7522019-02-22 13:24:25 -07001719 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howard1769abc2019-02-21 09:36:14 -07001720
Gavin Howardad0ecfe2018-10-30 01:16:01 -06001721 bc_vec_push(&stack, &dig);
Gavin Howardc4e9a082019-05-07 11:12:50 -06001722
Gavin Howarda8c2e582019-05-07 11:33:10 -06001723 temp = n1;
1724 n1 = n2;
1725 n2 = temp;
Gavin Howard63738202018-09-26 15:34:20 -06001726 }
Gavin Howard2682a1f2018-03-03 09:09:13 -07001727
Gavin Howard2d188a52019-02-25 14:19:08 -07001728 if (BC_SIG) goto sig_err;
Gavin Howarddc0ee9e2019-02-16 23:27:08 -07001729
Gavin Howard2d188a52019-02-25 14:19:08 -07001730 for (i = 0; BC_NO_SIG && i < stack.len; ++i) {
Gavin Howard63738202018-09-26 15:34:20 -06001731 ptr = bc_vec_item_rev(&stack, i);
1732 assert(ptr);
Gavin Howardddce6d42019-01-03 14:07:06 -07001733 print(*ptr, len, false);
Gavin Howard63738202018-09-26 15:34:20 -06001734 }
Gavin Howard2682a1f2018-03-03 09:09:13 -07001735
Gavin Howard2d188a52019-02-25 14:19:08 -07001736 if (BC_SIG) goto sig_err;
Stefan Esser276de8c2019-05-04 20:12:43 +02001737 if (!n->scale) goto err;
Gavin Howard2682a1f2018-03-03 09:09:13 -07001738
Gavin Howardfbae8a52019-05-07 11:15:38 -06001739 radix = true;
Gavin Howarda8c2e582019-05-07 11:33:10 -06001740 n1 = &flen1;
1741 n2 = &flen2;
Gavin Howardfbae8a52019-05-07 11:15:38 -06001742
Gavin Howard06fee0c2019-05-09 14:43:20 -06001743 while (BC_NO_SIG && bc_num_intDigits(n1) < n->scale + 1) {
Gavin Howarddc0ee9e2019-02-16 23:27:08 -07001744
Gavin Howardb3f1ee22019-05-07 21:49:15 -06001745 bc_num_expand(&fracp2, fracp1.len + 1);
1746 s = bc_num_mulArray(&fracp1, base, &fracp2);
Gavin Howardc78e7522019-02-22 13:24:25 -07001747 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howardb3f1ee22019-05-07 21:49:15 -06001748 fracp2.scale = n->scale;
1749 fracp2.rdx = BC_NUM_RDX(fracp2.scale);
Gavin Howard1769abc2019-02-21 09:36:14 -07001750
1751 // Will never fail (except for signals) because fracp is
1752 // guaranteed to be non-negative and small enough.
Gavin Howard2956a5e2019-05-08 08:04:06 -06001753 s = bc_num_bigdig(&fracp2, &dig);
Gavin Howardc78e7522019-02-22 13:24:25 -07001754 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howarddc0ee9e2019-02-16 23:27:08 -07001755
Gavin Howard2956a5e2019-05-08 08:04:06 -06001756 bc_num_bigdig2num(&digit, dig);
Gavin Howard43b6fd72019-05-07 11:39:19 -06001757 s = bc_num_sub(&fracp2, &digit, &fracp1, 0);
Gavin Howardc78e7522019-02-22 13:24:25 -07001758 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howarddc0ee9e2019-02-16 23:27:08 -07001759
Gavin Howardddce6d42019-01-03 14:07:06 -07001760 print(dig, len, radix);
Gavin Howardb3f1ee22019-05-07 21:49:15 -06001761 s = bc_num_mulArray(n1, base, n2);
Gavin Howardc78e7522019-02-22 13:24:25 -07001762 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howardfbae8a52019-05-07 11:15:38 -06001763
1764 radix = false;
Gavin Howarda8c2e582019-05-07 11:33:10 -06001765 temp = n1;
1766 n1 = n2;
1767 n2 = temp;
Gavin Howard63738202018-09-26 15:34:20 -06001768 }
Gavin Howard2682a1f2018-03-03 09:09:13 -07001769
Gavin Howarddc0ee9e2019-02-16 23:27:08 -07001770sig_err:
Gavin Howard2d188a52019-02-25 14:19:08 -07001771 if (BC_NO_ERR(!s) && BC_SIG) s = BC_STATUS_SIGNAL;
Gavin Howard2682a1f2018-03-03 09:09:13 -07001772err:
Gavin Howardc4e9a082019-05-07 11:12:50 -06001773 bc_num_free(&intp2);
Gavin Howardea8d5132019-05-07 11:52:04 -06001774 bc_num_free(&intp1);
Gavin Howardf1148822019-05-11 15:29:24 -06001775 bc_num_free(&flen2);
1776 bc_num_free(&flen1);
Gavin Howarda8c2e582019-05-07 11:33:10 -06001777 bc_num_free(&fracp2);
1778 bc_num_free(&fracp1);
Gavin Howard63738202018-09-26 15:34:20 -06001779 bc_vec_free(&stack);
Gavin Howarddc0ee9e2019-02-16 23:27:08 -07001780 return s;
Gavin Howard2682a1f2018-03-03 09:09:13 -07001781}
1782
Gavin Howard2956a5e2019-05-08 08:04:06 -06001783static BcStatus bc_num_printBase(BcNum *restrict n, BcBigDig base) {
Gavin Howardb3f1ee22019-05-07 21:49:15 -06001784
Gavin Howarddc0ee9e2019-02-16 23:27:08 -07001785 BcStatus s;
Gavin Howard1ab22d22019-01-03 13:32:17 -07001786 size_t width;
Gavin Howard83eb8392018-10-09 01:21:19 -06001787 BcNumDigitOp print;
1788 bool neg = n->neg;
1789
Gavin Howardb4a9cdb2019-05-07 15:25:51 -06001790 if (neg) bc_num_putchar('-');
Gavin Howard83eb8392018-10-09 01:21:19 -06001791
1792 n->neg = false;
1793
Gavin Howardb3f1ee22019-05-07 21:49:15 -06001794 if (base <= BC_NUM_MAX_POSIX_IBASE) {
Gavin Howard83eb8392018-10-09 01:21:19 -06001795 width = 1;
1796 print = bc_num_printHex;
1797 }
1798 else {
Gavin Howardb3f1ee22019-05-07 21:49:15 -06001799 width = bc_num_log10(base - 1);
Gavin Howard83eb8392018-10-09 01:21:19 -06001800 print = bc_num_printDigits;
1801 }
1802
Gavin Howarddc0ee9e2019-02-16 23:27:08 -07001803 s = bc_num_printNum(n, base, width, print);
Gavin Howard83eb8392018-10-09 01:21:19 -06001804 n->neg = neg;
Gavin Howarddc0ee9e2019-02-16 23:27:08 -07001805
1806 return s;
Gavin Howard83eb8392018-10-09 01:21:19 -06001807}
1808
Gavin Howard40a085f2018-12-03 12:08:59 -07001809#if DC_ENABLED
Gavin Howard2956a5e2019-05-08 08:04:06 -06001810BcStatus bc_num_stream(BcNum *restrict n, BcBigDig base) {
Gavin Howarddc0ee9e2019-02-16 23:27:08 -07001811 return bc_num_printNum(n, base, 1, bc_num_printChar);
Gavin Howard83eb8392018-10-09 01:21:19 -06001812}
1813#endif // DC_ENABLED
1814
Gavin Howard3c02ed32018-12-28 18:17:15 -07001815void bc_num_setup(BcNum *restrict n, BcDig *restrict num, size_t cap) {
Gavin Howardb8a12922018-12-21 21:40:45 -07001816 assert(n);
1817 n->num = num;
1818 n->cap = cap;
Gavin Howardfcf29012019-04-25 20:12:19 -06001819 n->rdx = n->scale = n->len = 0;
Gavin Howardb8a12922018-12-21 21:40:45 -07001820 n->neg = false;
1821}
1822
Gavin Howard3c02ed32018-12-28 18:17:15 -07001823void bc_num_init(BcNum *restrict n, size_t req) {
Gavin Howard63738202018-09-26 15:34:20 -06001824 assert(n);
Gavin Howard53eba8b2018-10-31 15:14:37 -06001825 req = req >= BC_NUM_DEF_SIZE ? req : BC_NUM_DEF_SIZE;
Gavin Howard404ece72019-04-26 15:59:15 -06001826 bc_num_setup(n, bc_vm_malloc(BC_NUM_SIZE(req)), req);
Gavin Howardb5c77212018-02-14 17:12:34 -07001827}
1828
Gavin Howarded392aa2018-02-27 13:09:26 -07001829void bc_num_free(void *num) {
Gavin Howardad0ecfe2018-10-30 01:16:01 -06001830 assert(num);
1831 free(((BcNum*) num)->num);
Gavin Howardb5c77212018-02-14 17:12:34 -07001832}
1833
Gavin Howard3c02ed32018-12-28 18:17:15 -07001834void bc_num_copy(BcNum *d, const BcNum *s) {
Gavin Howard63738202018-09-26 15:34:20 -06001835 assert(d && s);
Gavin Howard7344ba72019-01-03 13:37:27 -07001836 if (d == s) return;
Gavin Howarddcff3c92019-01-09 10:04:56 -07001837 bc_num_expand(d, s->len);
Gavin Howard7344ba72019-01-03 13:37:27 -07001838 d->len = s->len;
1839 d->neg = s->neg;
1840 d->rdx = s->rdx;
Gavin Howardfcf29012019-04-25 20:12:19 -06001841 d->scale = s->scale;
Gavin Howard404ece72019-04-26 15:59:15 -06001842 memcpy(d->num, s->num, BC_NUM_SIZE(d->len));
Gavin Howard5a049c42018-02-15 11:24:11 -07001843}
1844
Gavin Howardbaa4f582019-01-24 13:56:35 -07001845void bc_num_createCopy(BcNum *d, const BcNum *s) {
1846 bc_num_init(d, s->len);
1847 bc_num_copy(d, s);
1848}
1849
Gavin Howard2956a5e2019-05-08 08:04:06 -06001850void bc_num_createFromBigdig(BcNum *n, BcBigDig val) {
Gavin Howard92ba4e52019-05-10 20:49:13 -06001851 bc_num_init(n, (BC_NUM_BIGDIG_LOG10 - 1) / BC_BASE_DIGS + 1);
Gavin Howard2956a5e2019-05-08 08:04:06 -06001852 bc_num_bigdig2num(n, val);
Gavin Howardbaa4f582019-01-24 13:56:35 -07001853}
1854
Gavin Howard7ad5a662019-02-19 14:40:46 -07001855size_t bc_num_scale(const BcNum *restrict n) {
Gavin Howardfcf29012019-04-25 20:12:19 -06001856 return n->scale;
Gavin Howard7ad5a662019-02-19 14:40:46 -07001857}
1858
1859size_t bc_num_len(const BcNum *restrict n) {
1860
Gavin Howard30ed6402019-05-14 09:00:55 -06001861 size_t len = n->len;
Gavin Howardfcf29012019-04-25 20:12:19 -06001862
Gavin Howardb41483b2019-04-27 08:30:10 -06001863 if (BC_NUM_ZERO(n)) return 0;
Gavin Howard7106c252019-05-14 08:54:07 -06001864 if (n->rdx == len) {
Gavin Howard7ad5a662019-02-19 14:40:46 -07001865
Gavin Howard30ed6402019-05-14 09:00:55 -06001866 size_t zero, scale;
1867
Gavin Howard7106c252019-05-14 08:54:07 -06001868 len = bc_num_nonzeroLen(n);
Gavin Howardb41483b2019-04-27 08:30:10 -06001869
Gavin Howard7106c252019-05-14 08:54:07 -06001870 scale = n->scale % BC_BASE_DIGS;
1871 scale = scale ? scale : BC_BASE_DIGS;
Gavin Howard0c593dd2019-05-14 08:50:09 -06001872
Gavin Howard7106c252019-05-14 08:54:07 -06001873 zero = bc_num_zeroDigits(n->num + len - 1);
1874
1875 len = len * BC_BASE_DIGS - zero - (BC_BASE_DIGS - scale);
1876 }
1877 else len = bc_num_intDigits(n) + n->scale;
1878
1879 return len;
Gavin Howard7ad5a662019-02-19 14:40:46 -07001880}
1881
Gavin Howarddc0ee9e2019-02-16 23:27:08 -07001882BcStatus bc_num_parse(BcNum *restrict n, const char *restrict val,
Gavin Howard2956a5e2019-05-08 08:04:06 -06001883 BcBigDig base, bool letter)
Gavin Howard3c02ed32018-12-28 18:17:15 -07001884{
Gavin Howarddc0ee9e2019-02-16 23:27:08 -07001885 BcStatus s = BC_STATUS_SUCCESS;
1886
Gavin Howard63738202018-09-26 15:34:20 -06001887 assert(n && val && base);
Gavin Howarde97ae922019-02-25 21:47:17 -07001888 assert(BC_ENABLE_EXTRA_MATH ||
Gavin Howardb3f1ee22019-05-07 21:49:15 -06001889 (base >= BC_NUM_MIN_BASE && base <= vm->max_ibase));
Gavin Howardc90ed902019-01-02 13:07:49 -07001890 assert(bc_num_strValid(val));
Gavin Howard3eb626f2018-02-14 13:54:35 -07001891
Gavin Howard2956a5e2019-05-08 08:04:06 -06001892 if (letter) {
1893 BcBigDig dig = bc_num_parseChar(val[0], BC_NUM_MAX_LBASE);
1894 bc_num_bigdig2num(n, dig);
1895 }
Gavin Howardb3f1ee22019-05-07 21:49:15 -06001896 else if (base == BC_BASE) bc_num_parseDecimal(n, val);
1897 else s = bc_num_parseBase(n, val, base);
Gavin Howarddc0ee9e2019-02-16 23:27:08 -07001898
1899 return s;
Gavin Howard3eb626f2018-02-14 13:54:35 -07001900}
1901
Gavin Howard2956a5e2019-05-08 08:04:06 -06001902BcStatus bc_num_print(BcNum *restrict n, BcBigDig base, bool newline) {
Gavin Howardb3f1ee22019-05-07 21:49:15 -06001903
Gavin Howarddc0ee9e2019-02-16 23:27:08 -07001904 BcStatus s = BC_STATUS_SUCCESS;
1905
Gavin Howardb3f1ee22019-05-07 21:49:15 -06001906 assert(n);
1907 assert(BC_ENABLE_EXTRA_MATH || base >= BC_NUM_MIN_BASE);
Gavin Howard3eb626f2018-02-14 13:54:35 -07001908
Gavin Howardab5e9632019-01-02 13:32:02 -07001909 bc_num_printNewline();
Gavin Howardbc7cae82018-03-14 13:43:04 -06001910
Gavin Howardddce6d42019-01-03 14:07:06 -07001911 if (BC_NUM_ZERO(n)) bc_num_printHex(0, 1, false);
Gavin Howardb3f1ee22019-05-07 21:49:15 -06001912 else if (base == BC_BASE) bc_num_printDecimal(n);
Gavin Howard7ad5a662019-02-19 14:40:46 -07001913#if BC_ENABLE_EXTRA_MATH
Gavin Howardb3f1ee22019-05-07 21:49:15 -06001914 else if (base == 0 || base == 1)
1915 s = bc_num_printExponent(n, base != 0);
Gavin Howard7ad5a662019-02-19 14:40:46 -07001916#endif // BC_ENABLE_EXTRA_MATH
Gavin Howardb3f1ee22019-05-07 21:49:15 -06001917 else s = bc_num_printBase(n, base);
Gavin Howard3eb626f2018-02-14 13:54:35 -07001918
Gavin Howardb4a9cdb2019-05-07 15:25:51 -06001919 if (BC_NO_ERR(!s) && newline) bc_num_putchar('\n');
Gavin Howarddc0ee9e2019-02-16 23:27:08 -07001920
1921 return s;
Gavin Howard3eb626f2018-02-14 13:54:35 -07001922}
1923
Gavin Howard2956a5e2019-05-08 08:04:06 -06001924BcStatus bc_num_bigdig(const BcNum *restrict n, BcBigDig *result) {
Gavin Howard68b8a5c2018-02-15 11:41:24 -07001925
Gavin Howard63738202018-09-26 15:34:20 -06001926 size_t i;
Gavin Howard2956a5e2019-05-08 08:04:06 -06001927 BcBigDig r;
Gavin Howard68b8a5c2018-02-15 11:41:24 -07001928
Gavin Howard63738202018-09-26 15:34:20 -06001929 assert(n && result);
Gavin Howard68b8a5c2018-02-15 11:41:24 -07001930
Gavin Howardecafd4f2019-02-23 09:30:45 -07001931 if (BC_ERR(n->neg)) return bc_vm_err(BC_ERROR_MATH_NEGATIVE);
Gavin Howard68b8a5c2018-02-15 11:41:24 -07001932
Gavin Howard06b6e862019-02-15 11:15:31 -07001933 for (r = 0, i = n->len; i > n->rdx;) {
Gavin Howard5bede412019-02-23 09:31:28 -07001934
Gavin Howard92ba4e52019-05-10 20:49:13 -06001935 BcBigDig prev = r * BC_BASE_POW;
Gavin Howard5bede412019-02-23 09:31:28 -07001936
Gavin Howard4fb37202019-05-11 14:49:13 -06001937 if (BC_ERR(prev / BC_BASE_POW != r))
Gavin Howard5bede412019-02-23 09:31:28 -07001938 return bc_vm_err(BC_ERROR_MATH_OVERFLOW);
1939
Gavin Howard2956a5e2019-05-08 08:04:06 -06001940 r = prev + (BcBigDig) n->num[--i];
Gavin Howard5bede412019-02-23 09:31:28 -07001941
Gavin Howard4fb37202019-05-11 14:49:13 -06001942 if (BC_ERR(r < prev)) return bc_vm_err(BC_ERROR_MATH_OVERFLOW);
Gavin Howard63738202018-09-26 15:34:20 -06001943 }
Gavin Howard68b8a5c2018-02-15 11:41:24 -07001944
Gavin Howard7b557da2018-12-21 10:25:32 -07001945 *result = r;
Gavin Howardd3a1c392018-12-11 12:00:57 -07001946
Gavin Howard63738202018-09-26 15:34:20 -06001947 return BC_STATUS_SUCCESS;
Gavin Howard68b8a5c2018-02-15 11:41:24 -07001948}
1949
Gavin Howard2956a5e2019-05-08 08:04:06 -06001950void bc_num_bigdig2num(BcNum *restrict n, BcBigDig val) {
Gavin Howard8e2cc692018-02-15 17:39:14 -07001951
Gavin Howardc25fd612019-04-26 19:31:31 -06001952 BcDig *ptr;
Gavin Howard2956a5e2019-05-08 08:04:06 -06001953 size_t i;
Gavin Howardc25fd612019-04-26 19:31:31 -06001954
Gavin Howard63738202018-09-26 15:34:20 -06001955 assert(n);
Gavin Howard8e2cc692018-02-15 17:39:14 -07001956
Gavin Howard63738202018-09-26 15:34:20 -06001957 bc_num_zero(n);
Gavin Howard025d04d2018-02-20 13:53:28 -07001958
Gavin Howard1ab22d22019-01-03 13:32:17 -07001959 if (!val) return;
Gavin Howard8e2cc692018-02-15 17:39:14 -07001960
Gavin Howard2956a5e2019-05-08 08:04:06 -06001961 bc_num_expand(n, BC_NUM_BIGDIG_LOG10);
Gavin Howard05feab02019-04-23 16:24:07 -06001962
Gavin Howard92ba4e52019-05-10 20:49:13 -06001963 for (ptr = n->num, i = 0; val; ++i, ++n->len, val /= BC_BASE_POW)
1964 ptr[i] = val % BC_BASE_POW;
Gavin Howard025d04d2018-02-20 13:53:28 -07001965}
1966
Gavin Howardd18fa6d2019-01-24 13:49:35 -07001967size_t bc_num_addReq(BcNum *a, BcNum *b, size_t scale) {
Gavin Howard1973d612019-02-21 15:37:32 -07001968
1969 size_t aint, bint, ardx, brdx;
1970
Gavin Howardd18fa6d2019-01-24 13:49:35 -07001971 BC_UNUSED(scale);
Gavin Howard1973d612019-02-21 15:37:32 -07001972
1973 ardx = a->rdx;
1974 brdx = b->rdx;
Gavin Howard7b7d2f32019-02-21 16:46:47 -07001975 aint = bc_num_int(a);
1976 bint = bc_num_int(b);
Gavin Howard2d188a52019-02-25 14:19:08 -07001977 ardx = BC_MAX(ardx, brdx);
1978 aint = BC_MAX(aint, bint);
Gavin Howard1973d612019-02-21 15:37:32 -07001979
Gavin Howard2d188a52019-02-25 14:19:08 -07001980 return bc_vm_growSize(bc_vm_growSize(ardx, aint), 1);
Gavin Howardd18fa6d2019-01-24 13:49:35 -07001981}
1982
1983size_t bc_num_mulReq(BcNum *a, BcNum *b, size_t scale) {
Gavin Howard2d188a52019-02-25 14:19:08 -07001984 size_t max, rdx;
1985 rdx = bc_vm_growSize(a->rdx, b->rdx);
Gavin Howardd7883272019-05-10 20:04:23 -06001986 max = BC_NUM_RDX(scale);
1987 max = bc_vm_growSize(BC_MAX(max, rdx), 1);
Gavin Howard2d188a52019-02-25 14:19:08 -07001988 rdx = bc_vm_growSize(bc_vm_growSize(bc_num_int(a), bc_num_int(b)), max);
1989 return rdx;
Gavin Howardd18fa6d2019-01-24 13:49:35 -07001990}
1991
1992size_t bc_num_powReq(BcNum *a, BcNum *b, size_t scale) {
1993 BC_UNUSED(scale);
Gavin Howard007afdf2019-02-23 09:45:42 -07001994 return bc_vm_growSize(bc_vm_growSize(a->len, b->len), 1);
Gavin Howardd18fa6d2019-01-24 13:49:35 -07001995}
1996
1997#if BC_ENABLE_EXTRA_MATH
Gavin Howard194380e2019-05-06 08:39:30 -06001998size_t bc_num_placesReq(BcNum *a, BcNum *b, size_t scale) {
1999
2000 BcStatus s;
Gavin Howardea1ae3c2019-05-10 18:44:52 -06002001 BcBigDig places = 0;
Gavin Howard194380e2019-05-06 08:39:30 -06002002 size_t rdx;
2003
2004 BC_UNUSED(s);
Gavin Howardd18fa6d2019-01-24 13:49:35 -07002005 BC_UNUSED(scale);
Gavin Howard194380e2019-05-06 08:39:30 -06002006
Gavin Howard194380e2019-05-06 08:39:30 -06002007 // This error will be taken care of later. Ignore.
Gavin Howard2956a5e2019-05-08 08:04:06 -06002008 s = bc_num_bigdig(b, &places);
Gavin Howard194380e2019-05-06 08:39:30 -06002009
2010 if (a->scale <= places) rdx = BC_NUM_RDX(places);
2011 else rdx = BC_NUM_RDX(a->scale - places);
2012
Gavin Howard06fee0c2019-05-09 14:43:20 -06002013 return BC_NUM_RDX(bc_num_intDigits(a)) + rdx;
Gavin Howard194380e2019-05-06 08:39:30 -06002014}
2015
2016size_t bc_num_shiftLeftReq(BcNum *a, BcNum *b, size_t scale) {
2017
2018 BcStatus s;
Gavin Howard687cf4d2019-05-11 08:55:36 -06002019 BcBigDig places = 0;
2020 size_t rdx;
Gavin Howard194380e2019-05-06 08:39:30 -06002021
2022 BC_UNUSED(s);
2023 BC_UNUSED(scale);
2024
Gavin Howard194380e2019-05-06 08:39:30 -06002025 // This error will be taken care of later. Ignore.
Gavin Howard2956a5e2019-05-08 08:04:06 -06002026 s = bc_num_bigdig(b, &places);
Gavin Howard194380e2019-05-06 08:39:30 -06002027
2028 if (a->scale <= places) rdx = BC_NUM_RDX(places) - a->rdx + 1;
2029 else rdx = 0;
2030
2031 return a->len + rdx;
2032}
2033
2034size_t bc_num_shiftRightReq(BcNum *a, BcNum *b, size_t scale) {
2035
2036 BcStatus s;
Gavin Howard687cf4d2019-05-11 08:55:36 -06002037 BcBigDig places = 0;
2038 size_t int_digs, rdx;
Gavin Howard194380e2019-05-06 08:39:30 -06002039
2040 BC_UNUSED(s);
2041 BC_UNUSED(scale);
2042
Gavin Howard194380e2019-05-06 08:39:30 -06002043 // This error will be taken care of later. Ignore.
Gavin Howard2956a5e2019-05-08 08:04:06 -06002044 s = bc_num_bigdig(b, &places);
Gavin Howard194380e2019-05-06 08:39:30 -06002045
Gavin Howard06fee0c2019-05-09 14:43:20 -06002046 int_digs = BC_NUM_RDX(bc_num_intDigits(a));
Gavin Howard194380e2019-05-06 08:39:30 -06002047 rdx = BC_NUM_RDX(places);
2048
2049 if (int_digs <= rdx) rdx -= int_digs;
2050 else rdx = 0;
2051
2052 return a->len + rdx;
Gavin Howardd18fa6d2019-01-24 13:49:35 -07002053}
2054#endif // BC_ENABLE_EXTRA_MATH
2055
Gavin Howard12fe7812018-09-29 03:45:18 -06002056BcStatus bc_num_add(BcNum *a, BcNum *b, BcNum *c, size_t scale) {
Gavin Howard54b946a2018-10-23 12:06:57 -06002057 BcNumBinaryOp op = (!a->neg == !b->neg) ? bc_num_a : bc_num_s;
Gavin Howard41852962018-12-27 17:15:00 -07002058 BC_UNUSED(scale);
Gavin Howardd18fa6d2019-01-24 13:49:35 -07002059 return bc_num_binary(a, b, c, false, op, bc_num_addReq(a, b, scale));
Gavin Howard3eb626f2018-02-14 13:54:35 -07002060}
2061
Gavin Howard12fe7812018-09-29 03:45:18 -06002062BcStatus bc_num_sub(BcNum *a, BcNum *b, BcNum *c, size_t scale) {
Gavin Howard54b946a2018-10-23 12:06:57 -06002063 BcNumBinaryOp op = (!a->neg == !b->neg) ? bc_num_s : bc_num_a;
Gavin Howard41852962018-12-27 17:15:00 -07002064 BC_UNUSED(scale);
Gavin Howardd18fa6d2019-01-24 13:49:35 -07002065 return bc_num_binary(a, b, c, true, op, bc_num_addReq(a, b, scale));
Gavin Howard3eb626f2018-02-14 13:54:35 -07002066}
2067
Gavin Howard12fe7812018-09-29 03:45:18 -06002068BcStatus bc_num_mul(BcNum *a, BcNum *b, BcNum *c, size_t scale) {
Gavin Howardd18fa6d2019-01-24 13:49:35 -07002069 return bc_num_binary(a, b, c, scale, bc_num_m, bc_num_mulReq(a, b, scale));
Gavin Howard3eb626f2018-02-14 13:54:35 -07002070}
2071
Gavin Howard12fe7812018-09-29 03:45:18 -06002072BcStatus bc_num_div(BcNum *a, BcNum *b, BcNum *c, size_t scale) {
Gavin Howardd18fa6d2019-01-24 13:49:35 -07002073 return bc_num_binary(a, b, c, scale, bc_num_d, bc_num_mulReq(a, b, scale));
Gavin Howard3eb626f2018-02-14 13:54:35 -07002074}
2075
Gavin Howard54b946a2018-10-23 12:06:57 -06002076BcStatus bc_num_mod(BcNum *a, BcNum *b, BcNum *c, size_t scale) {
Gavin Howard2d188a52019-02-25 14:19:08 -07002077 size_t req = bc_num_mulReq(a, b, scale);
2078 return bc_num_binary(a, b, c, scale, bc_num_rem, req);
Gavin Howard3eb626f2018-02-14 13:54:35 -07002079}
2080
Gavin Howard12fe7812018-09-29 03:45:18 -06002081BcStatus bc_num_pow(BcNum *a, BcNum *b, BcNum *c, size_t scale) {
Gavin Howard6734e1a2019-02-21 17:11:52 -07002082 return bc_num_binary(a, b, c, scale, bc_num_p, bc_num_powReq(a, b, scale));
Gavin Howard3eb626f2018-02-14 13:54:35 -07002083}
2084
Gavin Howard7d8f0382018-12-27 16:15:13 -07002085#if BC_ENABLE_EXTRA_MATH
2086BcStatus bc_num_places(BcNum *a, BcNum *b, BcNum *c, size_t scale) {
Gavin Howard8291ecd2019-05-06 14:34:53 -06002087 size_t req = bc_num_placesReq(a, b, scale);
2088 return bc_num_binary(a, b, c, scale, bc_num_place, req);
Gavin Howard7d8f0382018-12-27 16:15:13 -07002089}
2090
2091BcStatus bc_num_lshift(BcNum *a, BcNum *b, BcNum *c, size_t scale) {
Gavin Howard8291ecd2019-05-06 14:34:53 -06002092 size_t req = bc_num_shiftLeftReq(a, b, scale);
2093 return bc_num_binary(a, b, c, scale, bc_num_left, req);
Gavin Howard7d8f0382018-12-27 16:15:13 -07002094}
2095
2096BcStatus bc_num_rshift(BcNum *a, BcNum *b, BcNum *c, size_t scale) {
Gavin Howard8291ecd2019-05-06 14:34:53 -06002097 size_t req = bc_num_shiftRightReq(a, b, scale);
2098 return bc_num_binary(a, b, c, scale, bc_num_right, req);
Gavin Howard7d8f0382018-12-27 16:15:13 -07002099}
2100#endif // BC_ENABLE_EXTRA_MATH
2101
Gavin Howard3c02ed32018-12-28 18:17:15 -07002102BcStatus bc_num_sqrt(BcNum *restrict a, BcNum *restrict b, size_t scale) {
Gavin Howard954276b2018-05-16 01:43:58 -06002103
Gavin Howard1cbfe242019-01-09 17:13:11 -07002104 BcStatus s = BC_STATUS_SUCCESS;
Gavin Howard34edd0a2018-10-25 15:51:05 -06002105 BcNum num1, num2, half, f, fprime, *x0, *x1, *temp;
Stefan Eßer4276af42019-04-29 14:04:57 -06002106 size_t pow, len, rdx, req, digs, digs1, digs2, resscale, times = 0;
Gavin Howard9f2395b2018-10-06 05:28:58 -06002107 ssize_t cmp = 1, cmp1 = SSIZE_MAX, cmp2 = SSIZE_MAX;
Stefan Eßer4276af42019-04-29 14:04:57 -06002108 BcDig half_digs[1];
Gavin Howard954276b2018-05-16 01:43:58 -06002109
Gavin Howard34edd0a2018-10-25 15:51:05 -06002110 assert(a && b && a != b);
Gavin Howard954276b2018-05-16 01:43:58 -06002111
Gavin Howardecafd4f2019-02-23 09:30:45 -07002112 if (BC_ERR(a->neg)) return bc_vm_err(BC_ERROR_MATH_NEGATIVE);
Gavin Howard07fbf012019-02-19 09:25:11 -07002113
Stefan Eßer4276af42019-04-29 14:04:57 -06002114 if (a->scale > scale) scale = a->scale;
Gavin Howard06fee0c2019-05-09 14:43:20 -06002115 len = bc_vm_growSize(bc_num_intDigits(a), 1);
Gavin Howardeec07282019-05-10 20:10:18 -06002116 rdx = BC_NUM_RDX(scale);
2117 req = bc_vm_growSize(BC_MAX(rdx, a->rdx), len >> 1);
Gavin Howard2d188a52019-02-25 14:19:08 -07002118 bc_num_init(b, bc_vm_growSize(req, 1));
Gavin Howard954276b2018-05-16 01:43:58 -06002119
Gavin Howardddce6d42019-01-03 14:07:06 -07002120 if (BC_NUM_ZERO(a)) {
Gavin Howardf8ddb6d2018-10-22 12:58:53 -06002121 bc_num_setToZero(b, scale);
Gavin Howardad0ecfe2018-10-30 01:16:01 -06002122 return BC_STATUS_SUCCESS;
Gavin Howard63738202018-09-26 15:34:20 -06002123 }
Gavin Howardb2f01a42019-05-10 20:00:32 -06002124 if (BC_NUM_ONE(a)) {
Gavin Howard757b66a2018-10-06 04:13:46 -06002125 bc_num_one(b);
Gavin Howardad0ecfe2018-10-30 01:16:01 -06002126 bc_num_extend(b, scale);
2127 return BC_STATUS_SUCCESS;
Gavin Howard63738202018-09-26 15:34:20 -06002128 }
Gavin Howard954276b2018-05-16 01:43:58 -06002129
Gavin Howardeec07282019-05-10 20:10:18 -06002130 rdx = BC_NUM_RDX(scale);
2131 rdx = BC_MAX(rdx, a->rdx);
Stefan Eßer4276af42019-04-29 14:04:57 -06002132 len = bc_vm_growSize(a->len, rdx);
Gavin Howard954276b2018-05-16 01:43:58 -06002133
Gavin Howardad0ecfe2018-10-30 01:16:01 -06002134 bc_num_init(&num1, len);
2135 bc_num_init(&num2, len);
Gavin Howard18f40202018-12-29 21:30:37 -07002136 bc_num_setup(&half, half_digs, sizeof(half_digs) / sizeof(BcDig));
Gavin Howard954276b2018-05-16 01:43:58 -06002137
Gavin Howard63738202018-09-26 15:34:20 -06002138 bc_num_one(&half);
Gavin Howard92ba4e52019-05-10 20:49:13 -06002139 half.num[0] = BC_BASE_POW / 2;
Stefan Eßer4276af42019-04-29 14:04:57 -06002140 half.len = 1;
Gavin Howard63738202018-09-26 15:34:20 -06002141 half.rdx = 1;
Stefan Eßer4276af42019-04-29 14:04:57 -06002142 half.scale = 1;
Gavin Howard954276b2018-05-16 01:43:58 -06002143
Gavin Howardad0ecfe2018-10-30 01:16:01 -06002144 bc_num_init(&f, len);
2145 bc_num_init(&fprime, len);
Gavin Howard954276b2018-05-16 01:43:58 -06002146
Gavin Howard63738202018-09-26 15:34:20 -06002147 x0 = &num1;
2148 x1 = &num2;
Gavin Howard954276b2018-05-16 01:43:58 -06002149
Gavin Howard63738202018-09-26 15:34:20 -06002150 bc_num_one(x0);
Gavin Howard06fee0c2019-05-09 14:43:20 -06002151 pow = bc_num_intDigits(a);
Gavin Howard954276b2018-05-16 01:43:58 -06002152
Gavin Howard53eba8b2018-10-31 15:14:37 -06002153 if (pow) {
Gavin Howard954276b2018-05-16 01:43:58 -06002154
Gavin Howardc39fd492018-10-04 10:07:03 -06002155 if (pow & 1) x0->num[0] = 2;
2156 else x0->num[0] = 6;
Gavin Howard954276b2018-05-16 01:43:58 -06002157
Gavin Howardc39fd492018-10-04 10:07:03 -06002158 pow -= 2 - (pow & 1);
Gavin Howard74704572019-05-07 13:48:48 -06002159 s = bc_num_shiftLeft(x0, pow / 2);
2160 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howard63738202018-09-26 15:34:20 -06002161 }
Gavin Howard954276b2018-05-16 01:43:58 -06002162
Gavin Howardeec07282019-05-10 20:10:18 -06002163 x0->scale = x0->rdx = digs = digs1 = digs2 = 0;
Gavin Howard92ba4e52019-05-10 20:49:13 -06002164 resscale = (scale + BC_BASE_DIGS) * 2;
Stefan Eßer4276af42019-04-29 14:04:57 -06002165
Gavin Howard06fee0c2019-05-09 14:43:20 -06002166 len = BC_NUM_RDX(bc_num_intDigits(x0) + resscale - 1);
Gavin Howard954276b2018-05-16 01:43:58 -06002167
Gavin Howard2d188a52019-02-25 14:19:08 -07002168 while (BC_NO_SIG && (cmp || digs < len)) {
Gavin Howard954276b2018-05-16 01:43:58 -06002169
Gavin Howard971a2672019-04-26 14:32:07 -06002170 assert(BC_NUM_NONZERO(x0));
Gavin Howard1769abc2019-02-21 09:36:14 -07002171
Stefan Eßer4276af42019-04-29 14:04:57 -06002172 s = bc_num_div(a, x0, &f, resscale);
Gavin Howard1769abc2019-02-21 09:36:14 -07002173 assert(!s || s == BC_STATUS_SIGNAL);
Gavin Howardc78e7522019-02-22 13:24:25 -07002174 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Stefan Eßer4276af42019-04-29 14:04:57 -06002175 s = bc_num_add(x0, &f, &fprime, resscale);
Gavin Howardc78e7522019-02-22 13:24:25 -07002176 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Stefan Eßer4276af42019-04-29 14:04:57 -06002177 s = bc_num_mul(&fprime, &half, x1, resscale);
Gavin Howardc78e7522019-02-22 13:24:25 -07002178 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howard954276b2018-05-16 01:43:58 -06002179
Gavin Howard53eba8b2018-10-31 15:14:37 -06002180 cmp = bc_num_cmp(x1, x0);
Gavin Howard3378af82019-05-11 08:29:39 -06002181
2182#if BC_ENABLE_SIGNALS
Gavin Howarda36f0312019-05-09 10:23:07 -06002183 if (cmp == BC_NUM_CMP_SIGNAL) {
Gavin Howardf2ee6342019-04-09 17:05:20 -06002184 s = BC_STATUS_SIGNAL;
2185 break;
2186 }
Gavin Howard3378af82019-05-11 08:29:39 -06002187#endif // BC_ENABLE_SIGNALS
Gavin Howardf2ee6342019-04-09 17:05:20 -06002188
Gavin Howard53eba8b2018-10-31 15:14:37 -06002189 digs = x1->len - (unsigned long long) llabs(cmp);
Gavin Howard954276b2018-05-16 01:43:58 -06002190
Gavin Howardb5ec7f32018-10-29 12:32:39 -06002191 if (cmp == cmp2 && digs == digs1) times += 1;
Gavin Howardb11e9e22018-10-06 17:26:02 -06002192 else times = 0;
2193
Gavin Howardeec07282019-05-10 20:10:18 -06002194 resscale += (times > 2);
Gavin Howard9f2395b2018-10-06 05:28:58 -06002195
2196 cmp2 = cmp1;
2197 cmp1 = cmp;
Gavin Howardb5ec7f32018-10-29 12:32:39 -06002198 digs1 = digs;
Gavin Howard9f2395b2018-10-06 05:28:58 -06002199
Gavin Howard63738202018-09-26 15:34:20 -06002200 temp = x0;
2201 x0 = x1;
2202 x1 = temp;
2203 }
Gavin Howard954276b2018-05-16 01:43:58 -06002204
Gavin Howard2d188a52019-02-25 14:19:08 -07002205 if (BC_SIG) {
Gavin Howard176cfe62019-02-16 23:40:13 -07002206 s = BC_STATUS_SIGNAL;
2207 goto err;
2208 }
Gavin Howard0dfe2922018-05-22 13:57:02 -06002209
Gavin Howardad0ecfe2018-10-30 01:16:01 -06002210 bc_num_copy(b, x0);
Stefan Eßer4276af42019-04-29 14:04:57 -06002211 if (b->scale > scale) bc_num_truncate(b, b->scale - scale);
Gavin Howard954276b2018-05-16 01:43:58 -06002212
2213err:
Gavin Howardcf0748d2019-04-09 14:51:47 -06002214 if (BC_ERR(s)) bc_num_free(b);
Gavin Howard63738202018-09-26 15:34:20 -06002215 bc_num_free(&fprime);
Gavin Howard63738202018-09-26 15:34:20 -06002216 bc_num_free(&f);
Gavin Howard63738202018-09-26 15:34:20 -06002217 bc_num_free(&num2);
Gavin Howard63738202018-09-26 15:34:20 -06002218 bc_num_free(&num1);
Gavin Howardddce6d42019-01-03 14:07:06 -07002219 assert(!b->neg || BC_NUM_NONZERO(b));
Gavin Howardc4bf4c42019-01-09 10:30:15 -07002220 assert(b->rdx <= b->len || !b->len);
Gavin Howard63738202018-09-26 15:34:20 -06002221 return s;
Gavin Howard3eb626f2018-02-14 13:54:35 -07002222}
Gavin Howardba009802018-09-29 04:41:51 -06002223
Gavin Howard2cb39612018-10-22 08:46:48 -06002224BcStatus bc_num_divmod(BcNum *a, BcNum *b, BcNum *c, BcNum *d, size_t scale) {
2225
2226 BcStatus s;
2227 BcNum num2, *ptr_a;
Gavin Howard890d0c02018-10-30 16:34:50 -06002228 bool init = false;
Stefan Eßera7fbbf62019-04-29 14:14:30 -06002229 size_t ts, len;
2230
2231 ts = BC_MAX(scale + b->scale, a->scale);
2232 len = bc_num_mulReq(a, b, ts);
Gavin Howard2cb39612018-10-22 08:46:48 -06002233
Gavin Howardf2ef2f32019-01-16 11:25:16 -07002234 assert(c != d && a != d && b != d && b != c);
Gavin Howard2cb39612018-10-22 08:46:48 -06002235
Gavin Howard890d0c02018-10-30 16:34:50 -06002236 if (c == a) {
Gavin Howard2cb39612018-10-22 08:46:48 -06002237 memcpy(&num2, c, sizeof(BcNum));
2238 ptr_a = &num2;
Gavin Howardad0ecfe2018-10-30 01:16:01 -06002239 bc_num_init(c, len);
Gavin Howard890d0c02018-10-30 16:34:50 -06002240 init = true;
Gavin Howard2cb39612018-10-22 08:46:48 -06002241 }
Gavin Howardf679ad82018-10-29 12:26:30 -06002242 else {
2243 ptr_a = a;
Gavin Howardad0ecfe2018-10-30 01:16:01 -06002244 bc_num_expand(c, len);
Gavin Howardf679ad82018-10-29 12:26:30 -06002245 }
Gavin Howard2cb39612018-10-22 08:46:48 -06002246
Gavin Howardf5961782019-05-09 07:50:46 -06002247 if (BC_NUM_NONZERO(a) && !a->rdx && !b->rdx && b->len == 1 && !scale) {
Gavin Howard2956a5e2019-05-08 08:04:06 -06002248 BcBigDig rem;
Gavin Howard9d771ba2019-05-08 09:09:25 -06002249 s = bc_num_divArray(ptr_a, (BcBigDig) b->num[0], c, &rem);
Gavin Howard92ba4e52019-05-10 20:49:13 -06002250 assert(rem < BC_BASE_POW);
Gavin Howardb3f1ee22019-05-07 21:49:15 -06002251 d->num[0] = (BcDig) rem;
Gavin Howardf5961782019-05-09 07:50:46 -06002252 d->len = (rem != 0);
Gavin Howardb3f1ee22019-05-07 21:49:15 -06002253 }
Gavin Howardf5961782019-05-09 07:50:46 -06002254 else s = bc_num_r(ptr_a, b, c, d, scale, ts);
Gavin Howard2cb39612018-10-22 08:46:48 -06002255
Gavin Howardddce6d42019-01-03 14:07:06 -07002256 assert(!c->neg || BC_NUM_NONZERO(c));
Gavin Howardc4bf4c42019-01-09 10:30:15 -07002257 assert(c->rdx <= c->len || !c->len);
Gavin Howardddce6d42019-01-03 14:07:06 -07002258 assert(!d->neg || BC_NUM_NONZERO(d));
Gavin Howardc4bf4c42019-01-09 10:30:15 -07002259 assert(d->rdx <= d->len || !d->len);
Gavin Howard2cb39612018-10-22 08:46:48 -06002260
2261 if (init) bc_num_free(&num2);
2262
2263 return s;
2264}
2265
Gavin Howard40a085f2018-12-03 12:08:59 -07002266#if DC_ENABLED
Gavin Howard34edd0a2018-10-25 15:51:05 -06002267BcStatus bc_num_modexp(BcNum *a, BcNum *b, BcNum *c, BcNum *restrict d) {
Gavin Howardba009802018-09-29 04:41:51 -06002268
2269 BcStatus s;
Gavin Howard34edd0a2018-10-25 15:51:05 -06002270 BcNum base, exp, two, temp;
Gavin Howardd3929832018-12-24 15:13:15 -07002271 BcDig two_digs[2];
Gavin Howardba009802018-09-29 04:41:51 -06002272
Gavin Howard34edd0a2018-10-25 15:51:05 -06002273 assert(a && b && c && d && a != d && b != d && c != d);
Gavin Howardba009802018-09-29 04:41:51 -06002274
Gavin Howardecafd4f2019-02-23 09:30:45 -07002275 if (BC_ERR(BC_NUM_ZERO(c)))
2276 return bc_vm_err(BC_ERROR_MATH_DIVIDE_BY_ZERO);
2277 if (BC_ERR(b->neg)) return bc_vm_err(BC_ERROR_MATH_NEGATIVE);
2278 if (BC_ERR(a->rdx || b->rdx || c->rdx))
Gavin Howard7536dcf2018-12-15 19:27:09 -07002279 return bc_vm_err(BC_ERROR_MATH_NON_INTEGER);
Gavin Howardba009802018-09-29 04:41:51 -06002280
Gavin Howardad0ecfe2018-10-30 01:16:01 -06002281 bc_num_expand(d, c->len);
2282 bc_num_init(&base, c->len);
Gavin Howard7fdc30a2018-12-29 21:31:21 -07002283 bc_num_setup(&two, two_digs, sizeof(two_digs) / sizeof(BcDig));
Gavin Howard303572b2019-05-09 07:50:56 -06002284 bc_num_init(&temp, b->len + 1);
Gavin Howardba009802018-09-29 04:41:51 -06002285
2286 bc_num_one(&two);
Gavin Howardba009802018-09-29 04:41:51 -06002287 two.num[0] = 2;
2288 bc_num_one(d);
2289
Gavin Howard1769abc2019-02-21 09:36:14 -07002290 // We already checked for 0.
Gavin Howard53eba8b2018-10-31 15:14:37 -06002291 s = bc_num_rem(a, c, &base, 0);
Gavin Howard1769abc2019-02-21 09:36:14 -07002292 assert(!s || s == BC_STATUS_SIGNAL);
Gavin Howardc78e7522019-02-22 13:24:25 -07002293 if (BC_ERROR_SIGNAL_ONLY(s)) goto rem_err;
Gavin Howardbaa4f582019-01-24 13:56:35 -07002294 bc_num_createCopy(&exp, b);
Gavin Howardba009802018-09-29 04:41:51 -06002295
Gavin Howard2d188a52019-02-25 14:19:08 -07002296 while (BC_NO_SIG && BC_NUM_NONZERO(&exp)) {
Gavin Howardba009802018-09-29 04:41:51 -06002297
Gavin Howard1769abc2019-02-21 09:36:14 -07002298 // Num two cannot be 0, so no errors.
Gavin Howard53eba8b2018-10-31 15:14:37 -06002299 s = bc_num_divmod(&exp, &two, &exp, &temp, 0);
Gavin Howardc78e7522019-02-22 13:24:25 -07002300 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howardba009802018-09-29 04:41:51 -06002301
Gavin Howardb2f01a42019-05-10 20:00:32 -06002302 if (BC_NUM_ONE(&temp) && !temp.neg) {
2303
Gavin Howard53eba8b2018-10-31 15:14:37 -06002304 s = bc_num_mul(d, &base, &temp, 0);
Gavin Howardc78e7522019-02-22 13:24:25 -07002305 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howard1769abc2019-02-21 09:36:14 -07002306
2307 // We already checked for 0.
Gavin Howard53eba8b2018-10-31 15:14:37 -06002308 s = bc_num_rem(&temp, c, d, 0);
Gavin Howard1769abc2019-02-21 09:36:14 -07002309 assert(!s || s == BC_STATUS_SIGNAL);
Gavin Howardc78e7522019-02-22 13:24:25 -07002310 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howardba009802018-09-29 04:41:51 -06002311 }
2312
Gavin Howard53eba8b2018-10-31 15:14:37 -06002313 s = bc_num_mul(&base, &base, &temp, 0);
Gavin Howardc78e7522019-02-22 13:24:25 -07002314 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howard1769abc2019-02-21 09:36:14 -07002315
2316 // We already checked for 0.
Gavin Howard53eba8b2018-10-31 15:14:37 -06002317 s = bc_num_rem(&temp, c, &base, 0);
Gavin Howard1769abc2019-02-21 09:36:14 -07002318 assert(!s || s == BC_STATUS_SIGNAL);
Gavin Howardc78e7522019-02-22 13:24:25 -07002319 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howardba009802018-09-29 04:41:51 -06002320 }
2321
Gavin Howard2d188a52019-02-25 14:19:08 -07002322 if (BC_NO_ERR(!s) && BC_SIG) s = BC_STATUS_SIGNAL;
Gavin Howardfa4983a2019-02-16 23:43:03 -07002323
Gavin Howardba009802018-09-29 04:41:51 -06002324err:
Gavin Howardba009802018-09-29 04:41:51 -06002325 bc_num_free(&exp);
Gavin Howardd88164d2019-02-21 09:57:31 -07002326rem_err:
2327 bc_num_free(&temp);
Gavin Howardba009802018-09-29 04:41:51 -06002328 bc_num_free(&base);
Gavin Howard3cf769e2018-10-11 13:55:11 -06002329 assert(!d->neg || d->len);
Gavin Howardba009802018-09-29 04:41:51 -06002330 return s;
2331}
Gavin Howarde6e84762018-10-03 11:46:34 -06002332#endif // DC_ENABLED
Gavin Howardefc1e202019-05-06 08:01:07 -06002333
2334#if BC_DEBUG_CODE
2335void bc_num_printDebug(const BcNum *n, const char *name, bool emptyline) {
2336 printf("%s: ", name);
2337 bc_num_printDecimal(n);
2338 printf("\n");
2339 if (emptyline) printf("\n");
Gavin Howarde34f7d82019-05-07 10:32:39 -06002340 vm->nchars = 0;
Gavin Howardefc1e202019-05-06 08:01:07 -06002341}
2342
Gavin Howard2a84f962019-05-08 17:20:09 -06002343void bc_num_printDigs(const BcDig *n, size_t len, bool emptyline) {
Gavin Howardefc1e202019-05-06 08:01:07 -06002344
2345 size_t i;
2346
Gavin Howard92ba4e52019-05-10 20:49:13 -06002347 for (i = len - 1; i < len; --i) printf(" %0*d", BC_BASE_DIGS, n[i]);
Gavin Howardefc1e202019-05-06 08:01:07 -06002348
2349 printf("\n");
2350 if (emptyline) printf("\n");
Gavin Howarde34f7d82019-05-07 10:32:39 -06002351 vm->nchars = 0;
Gavin Howardefc1e202019-05-06 08:01:07 -06002352}
2353
Gavin Howard2a84f962019-05-08 17:20:09 -06002354void bc_num_printWithDigs(const BcNum *n, const char *name, bool emptyline) {
2355 printf("%s len: %zu, rdx: %zu, scale: %zu\n",
2356 name, n->len, n->rdx, n->scale);
2357 bc_num_printDigs(n->num, n->len, emptyline);
2358}
2359
Gavin Howardefc1e202019-05-06 08:01:07 -06002360void bc_num_dump(const char *varname, const BcNum *n) {
2361
2362 unsigned long i, scale = n->scale;
2363
2364 fprintf(stderr, "\n%s = %s", varname, n->len ? (n->neg ? "-" : "+") : "0 ");
2365
2366 for (i = n->len - 1; i < n->len; --i) {
2367
2368 if (i + 1 == n->rdx) fprintf(stderr, ". ");
2369
Gavin Howard92ba4e52019-05-10 20:49:13 -06002370 if (scale / BC_BASE_DIGS != n->rdx - i - 1)
2371 fprintf(stderr, "%0*d ", BC_BASE_DIGS, n->num[i]);
Gavin Howardefc1e202019-05-06 08:01:07 -06002372 else {
2373
Gavin Howard92ba4e52019-05-10 20:49:13 -06002374 int mod = scale % BC_BASE_DIGS;
2375 int d = BC_BASE_DIGS - mod;
Gavin Howardefc1e202019-05-06 08:01:07 -06002376 BcDig div;
2377
2378 if (mod != 0) {
Gavin Howard2a84f962019-05-08 17:20:09 -06002379 div = n->num[i] / ((BcDig) bc_num_pow10[(unsigned long) d]);
Gavin Howardefc1e202019-05-06 08:01:07 -06002380 fprintf(stderr, "%0*d", (int) mod, div);
2381 }
2382
Gavin Howard2a84f962019-05-08 17:20:09 -06002383 div = n->num[i] % ((BcDig) bc_num_pow10[(unsigned long) d]);
Gavin Howardefc1e202019-05-06 08:01:07 -06002384 fprintf(stderr, " ' %0*d ", d, div);
2385 }
2386 }
2387
2388 fprintf(stderr, "(%zu | %zu.%zu / %zu) %p\n",
2389 n->scale, n->len, n->rdx, n->cap, (void*) n->num);
Gavin Howarde34f7d82019-05-07 10:32:39 -06002390 vm->nchars = 0;
Gavin Howardefc1e202019-05-06 08:01:07 -06002391}
2392#endif // BC_DEBUG_CODE