blob: aac41af321d06523267219e9d9df8a541dee1a66 [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 Howard47137932019-10-10 21:16:43 -060046#include <rand.h>
Gavin Howardd5551672018-09-22 19:52:42 -060047#include <vm.h>
Gavin Howard3eb626f2018-02-14 13:54:35 -070048
Gavin Howard49ea7b62019-04-23 14:46:26 -060049static BcStatus bc_num_m(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale);
50
Gavin Howard56ebcf72019-02-21 16:59:48 -070051static ssize_t bc_num_neg(size_t n, bool neg) {
52 return (((ssize_t) n) ^ -((ssize_t) neg)) + neg;
53}
54
Gavin Howardd2cf06a2019-02-21 17:03:24 -070055ssize_t bc_num_cmpZero(const BcNum *n) {
Gavin Howard56ebcf72019-02-21 16:59:48 -070056 return bc_num_neg((n)->len != 0, (n)->neg);
Gavin Howard9d91b2c2019-02-21 16:55:32 -070057}
58
Gavin Howard7b7d2f32019-02-21 16:46:47 -070059static size_t bc_num_int(const BcNum *n) {
60 return n->len ? n->len - n->rdx : 0;
61}
62
Gavin Howard798508b2019-02-21 16:45:32 -070063static void bc_num_expand(BcNum *restrict n, size_t req) {
Gavin Howardfe9a3022019-06-21 20:40:45 -060064 assert(n != NULL);
Gavin Howard798508b2019-02-21 16:45:32 -070065 req = req >= BC_NUM_DEF_SIZE ? req : BC_NUM_DEF_SIZE;
66 if (req > n->cap) {
Gavin Howard404ece72019-04-26 15:59:15 -060067 n->num = bc_vm_realloc(n->num, BC_NUM_SIZE(req));
Gavin Howard798508b2019-02-21 16:45:32 -070068 n->cap = req;
69 }
70}
71
Gavin Howard1cbfe242019-01-09 17:13:11 -070072static void bc_num_setToZero(BcNum *restrict n, size_t scale) {
Gavin Howardfe9a3022019-06-21 20:40:45 -060073 assert(n != NULL);
Gavin Howardfcf29012019-04-25 20:12:19 -060074 n->scale = scale;
75 n->len = n->rdx = 0;
Gavin Howard97102b12018-11-01 23:37:31 -060076 n->neg = false;
Gavin Howardf8ddb6d2018-10-22 12:58:53 -060077}
78
Gavin Howard1cbfe242019-01-09 17:13:11 -070079static void bc_num_zero(BcNum *restrict n) {
Gavin Howardf8ddb6d2018-10-22 12:58:53 -060080 bc_num_setToZero(n, 0);
Gavin Howard0be26ed2018-08-31 20:21:56 -060081}
82
Gavin Howard3c02ed32018-12-28 18:17:15 -070083void bc_num_one(BcNum *restrict n) {
Stefan Eßera1445f22019-08-01 07:17:00 -060084 bc_num_zero(n);
Gavin Howard63738202018-09-26 15:34:20 -060085 n->len = 1;
86 n->num[0] = 1;
Gavin Howard0be26ed2018-08-31 20:21:56 -060087}
88
Gavin Howardb3f1ee22019-05-07 21:49:15 -060089static void bc_num_clean(BcNum *restrict n) {
Gavin Howard1d871c12019-06-19 21:20:07 -060090 while (BC_NUM_NONZERO(n) && !n->num[n->len - 1]) n->len -= 1;
Gavin Howarda2653e62019-06-19 22:02:27 -060091 if (BC_NUM_ZERO(n)) {
92 n->neg = false;
93 n->rdx = 0;
94 }
Gavin Howardb3f1ee22019-05-07 21:49:15 -060095 else if (n->len < n->rdx) n->len = n->rdx;
96}
97
Gavin Howard1cbfe242019-01-09 17:13:11 -070098static size_t bc_num_log10(size_t i) {
Gavin Howard1ab22d22019-01-03 13:32:17 -070099 size_t len;
Gavin Howard530e3072019-04-23 16:23:36 -0600100 for (len = 1; i; i /= BC_BASE, ++len);
Gavin Howardc849ed22019-05-13 02:01:10 -0600101 assert(len - 1 <= BC_BASE_DIGS + 1);
Gavin Howardc1349922019-04-30 20:48:09 -0600102 return len - 1;
Gavin Howard1ab22d22019-01-03 13:32:17 -0700103}
104
Gavin Howard4007e2f2019-05-08 09:08:00 -0600105static size_t bc_num_zeroDigits(const BcDig *n) {
Gavin Howard92ba4e52019-05-10 20:49:13 -0600106 return BC_BASE_DIGS - bc_num_log10((size_t) *n);
Gavin Howard4007e2f2019-05-08 09:08:00 -0600107}
108
Gavin Howard06fee0c2019-05-09 14:43:20 -0600109static size_t bc_num_intDigits(const BcNum *n) {
Gavin Howard92ba4e52019-05-10 20:49:13 -0600110 size_t digits = bc_num_int(n) * BC_BASE_DIGS;
Gavin Howard3d3c5972019-05-15 10:43:59 -0600111 if (digits > 0) digits -= bc_num_zeroDigits(n->num + n->len - 1);
Gavin Howard445c1892019-04-29 13:50:24 -0600112 return digits;
113}
114
Gavin Howardc1349922019-04-30 20:48:09 -0600115static size_t bc_num_nonzeroLen(const BcNum *restrict n) {
Gavin Howard08f78562019-04-29 07:56:04 -0600116 size_t i, len = n->len;
117 assert(len == n->rdx);
Gavin Howard8fe174f2019-05-15 09:44:28 -0600118 for (i = len - 1; i < len && !n->num[i]; --i);
Gavin Howarda882ac22019-05-14 08:44:29 -0600119 assert(i + 1 > 0);
120 return i + 1;
Gavin Howard08f78562019-04-29 07:56:04 -0600121}
122
Stefan Essera346a182019-07-28 11:28:28 +0200123static BcDig bc_num_addDigits(BcDig a, BcDig b, bool *carry) {
Gavin Howard2e8e9042019-08-01 07:51:46 -0600124
125 assert(((BcBigDig) BC_BASE_POW) * 2 == ((BcDig) BC_BASE_POW) * 2);
Stefan Essera346a182019-07-28 11:28:28 +0200126 assert(a < BC_BASE_POW);
127 assert(b < BC_BASE_POW);
Stefan Essera346a182019-07-28 11:28:28 +0200128
129 a += b + *carry;
Gavin Howard2e8e9042019-08-01 07:51:46 -0600130 *carry = (a >= BC_BASE_POW);
131 if (*carry) a -= BC_BASE_POW;
Stefan Essera346a182019-07-28 11:28:28 +0200132
Gavin Howard2e8e9042019-08-01 07:51:46 -0600133 assert(a >= 0);
Stefan Essera346a182019-07-28 11:28:28 +0200134 assert(a < BC_BASE_POW);
Gavin Howard2e8e9042019-08-01 07:51:46 -0600135
Stefan Essera346a182019-07-28 11:28:28 +0200136 return a;
137}
138
139static BcDig bc_num_subDigits(BcDig a, BcDig b, bool *carry) {
Gavin Howard2e8e9042019-08-01 07:51:46 -0600140
Stefan Essera346a182019-07-28 11:28:28 +0200141 assert(a < BC_BASE_POW);
142 assert(b < BC_BASE_POW);
Stefan Essera346a182019-07-28 11:28:28 +0200143
144 b += *carry;
Gavin Howard2e8e9042019-08-01 07:51:46 -0600145 *carry = (a < b);
146 if (*carry) a += BC_BASE_POW;
Stefan Essera346a182019-07-28 11:28:28 +0200147
Gavin Howard2e8e9042019-08-01 07:51:46 -0600148 assert(a - b >= 0);
Stefan Essera346a182019-07-28 11:28:28 +0200149 assert(a - b < BC_BASE_POW);
Gavin Howard2e8e9042019-08-01 07:51:46 -0600150
Stefan Essera346a182019-07-28 11:28:28 +0200151 return a - b;
Gavin Howard14c354a2019-04-24 14:28:24 -0600152}
153
154static BcStatus bc_num_addArrays(BcDig *restrict a, const BcDig *restrict b,
155 size_t len)
156{
157 size_t i;
Stefan Essera346a182019-07-28 11:28:28 +0200158 bool carry = false;
Gavin Howard14c354a2019-04-24 14:28:24 -0600159
Stefan Essera346a182019-07-28 11:28:28 +0200160 for (i = 0; BC_NO_SIG && i < len; ++i)
161 a[i] = bc_num_addDigits(a[i], b[i], &carry);
Gavin Howard14c354a2019-04-24 14:28:24 -0600162
163 for (; BC_NO_SIG && carry; ++i)
Stefan Essera346a182019-07-28 11:28:28 +0200164 a[i] = bc_num_addDigits(a[i], 0, &carry);
Gavin Howard14c354a2019-04-24 14:28:24 -0600165
166 return BC_SIG ? BC_STATUS_SIGNAL : BC_STATUS_SUCCESS;
167}
168
Gavin Howarddc0ee9e2019-02-16 23:27:08 -0700169static BcStatus bc_num_subArrays(BcDig *restrict a, const BcDig *restrict b,
170 size_t len)
Gavin Howard3c02ed32018-12-28 18:17:15 -0700171{
Stefan Esser94fb4802019-06-06 22:07:20 +0200172 size_t i;
Stefan Esser94fb4802019-06-06 22:07:20 +0200173 bool carry = false;
Gavin Howard14c354a2019-04-24 14:28:24 -0600174
Stefan Essera346a182019-07-28 11:28:28 +0200175 for (i = 0; BC_NO_SIG && i < len; ++i)
176 a[i] = bc_num_subDigits(a[i], b[i], &carry);
Gavin Howard9060bfd2019-05-18 18:03:24 -0600177
Stefan Essera346a182019-07-28 11:28:28 +0200178 for (; BC_NO_SIG && carry; ++i)
179 a[i] = bc_num_subDigits(a[i], 0, &carry);
Gavin Howard14c354a2019-04-24 14:28:24 -0600180
Gavin Howard2d188a52019-02-25 14:19:08 -0700181 return BC_SIG ? BC_STATUS_SIGNAL : BC_STATUS_SUCCESS;
Gavin Howarde1e74942018-03-20 15:51:52 -0600182}
183
Gavin Howard2956a5e2019-05-08 08:04:06 -0600184static BcStatus bc_num_mulArray(const BcNum *restrict a, BcBigDig b,
Gavin Howardb3f1ee22019-05-07 21:49:15 -0600185 BcNum *restrict c)
186{
187 size_t i;
Gavin Howard2956a5e2019-05-08 08:04:06 -0600188 BcBigDig carry = 0;
Gavin Howardb3f1ee22019-05-07 21:49:15 -0600189
Gavin Howard92ba4e52019-05-10 20:49:13 -0600190 assert(b <= BC_BASE_POW);
Gavin Howardf1148822019-05-11 15:29:24 -0600191
192 if (a->len + 1 > c->cap) bc_num_expand(c, a->len + 1);
Gavin Howardb3f1ee22019-05-07 21:49:15 -0600193
194 memset(c->num, 0, BC_NUM_SIZE(c->cap));
195
196 for (i = 0; BC_NO_SIG && i < a->len; ++i) {
Gavin Howard2956a5e2019-05-08 08:04:06 -0600197 BcBigDig in = ((BcBigDig) a->num[i]) * b + carry;
Gavin Howard92ba4e52019-05-10 20:49:13 -0600198 c->num[i] = in % BC_BASE_POW;
199 carry = in / BC_BASE_POW;
Gavin Howardb3f1ee22019-05-07 21:49:15 -0600200 }
201
202 if (BC_NO_SIG) {
Gavin Howard92ba4e52019-05-10 20:49:13 -0600203 assert(carry < BC_BASE_POW);
Gavin Howarde7ae4f42019-05-08 09:08:15 -0600204 c->num[i] = (BcDig) carry;
Gavin Howardb3f1ee22019-05-07 21:49:15 -0600205 c->len = a->len;
206 c->len += (carry != 0);
207 }
208
Gavin Howardead22422019-06-19 22:38:15 -0600209 bc_num_clean(c);
Gavin Howarda2653e62019-06-19 22:02:27 -0600210
Gavin Howard1d871c12019-06-19 21:20:07 -0600211 assert(!c->neg || BC_NUM_NONZERO(c));
212 assert(c->rdx <= c->len || !c->len || BC_SIG);
Gavin Howarda2653e62019-06-19 22:02:27 -0600213 assert(!c->len || c->num[c->len - 1] || c->rdx == c->len);
Gavin Howard1d871c12019-06-19 21:20:07 -0600214
Gavin Howardb3f1ee22019-05-07 21:49:15 -0600215 return BC_SIG ? BC_STATUS_SIGNAL : BC_STATUS_SUCCESS;
216}
217
Gavin Howard2956a5e2019-05-08 08:04:06 -0600218static BcStatus bc_num_divArray(const BcNum *restrict a, BcBigDig b,
219 BcNum *restrict c, BcBigDig *rem)
Gavin Howardb3f1ee22019-05-07 21:49:15 -0600220{
221 size_t i;
Gavin Howard2956a5e2019-05-08 08:04:06 -0600222 BcBigDig carry = 0;
Gavin Howardb3f1ee22019-05-07 21:49:15 -0600223
224 assert(c->cap >= a->len);
225
226 for (i = a->len - 1; BC_NO_SIG && i < a->len; --i) {
Gavin Howard92ba4e52019-05-10 20:49:13 -0600227 BcBigDig in = ((BcBigDig) a->num[i]) + carry * BC_BASE_POW;
Gavin Howarda142c482019-05-15 13:39:31 -0600228 assert(in / b < BC_BASE_POW);
Gavin Howarde7ae4f42019-05-08 09:08:15 -0600229 c->num[i] = (BcDig) (in / b);
Gavin Howardb3f1ee22019-05-07 21:49:15 -0600230 carry = in % b;
231 }
232
233 c->len = a->len;
Gavin Howardead22422019-06-19 22:38:15 -0600234 bc_num_clean(c);
Gavin Howardb3f1ee22019-05-07 21:49:15 -0600235 *rem = carry;
236
Gavin Howard1d871c12019-06-19 21:20:07 -0600237 assert(!c->neg || BC_NUM_NONZERO(c));
238 assert(c->rdx <= c->len || !c->len || BC_SIG);
Gavin Howarda2653e62019-06-19 22:02:27 -0600239 assert(!c->len || c->num[c->len - 1] || c->rdx == c->len);
Gavin Howard1d871c12019-06-19 21:20:07 -0600240
Gavin Howardb3f1ee22019-05-07 21:49:15 -0600241 return BC_SIG ? BC_STATUS_SIGNAL : BC_STATUS_SUCCESS;
242}
243
Gavin Howard2d188a52019-02-25 14:19:08 -0700244static ssize_t bc_num_compare(const BcDig *restrict a, const BcDig *restrict b,
245 size_t len)
Gavin Howard3c02ed32018-12-28 18:17:15 -0700246{
Gavin Howard63738202018-09-26 15:34:20 -0600247 size_t i;
Gavin Howard9a66a832019-05-13 20:20:12 -0600248 BcDig c = 0;
Gavin Howard2d188a52019-02-25 14:19:08 -0700249 for (i = len - 1; BC_NO_SIG && i < len && !(c = a[i] - b[i]); --i);
Stefan Esser94fb4802019-06-06 22:07:20 +0200250 return BC_SIG ? BC_NUM_CMP_SIGNAL_VAL : bc_num_neg(i + 1, c < 0);
Gavin Howard08bf5292018-03-20 14:59:33 -0600251}
252
Gavin Howard3c02ed32018-12-28 18:17:15 -0700253ssize_t bc_num_cmp(const BcNum *a, const BcNum *b) {
Gavin Howard4681d1b2018-03-05 19:49:33 -0700254
Gavin Howard63738202018-09-26 15:34:20 -0600255 size_t i, min, a_int, b_int, diff;
Gavin Howard8a921bd2018-10-11 14:15:32 -0600256 BcDig *max_num, *min_num;
Gavin Howard7fbb4e22018-10-18 11:43:17 -0600257 bool a_max, neg = false;
258 ssize_t cmp;
259
Gavin Howardfe9a3022019-06-21 20:40:45 -0600260 assert(a != NULL && b != NULL);
Gavin Howard4681d1b2018-03-05 19:49:33 -0700261
Gavin Howard8a921bd2018-10-11 14:15:32 -0600262 if (a == b) return 0;
Gavin Howard56ebcf72019-02-21 16:59:48 -0700263 if (BC_NUM_ZERO(a)) return bc_num_neg(b->len != 0, !b->neg);
Gavin Howardd2cf06a2019-02-21 17:03:24 -0700264 if (BC_NUM_ZERO(b)) return bc_num_cmpZero(a);
Gavin Howard890d0c02018-10-30 16:34:50 -0600265 if (a->neg) {
Gavin Howard7fbb4e22018-10-18 11:43:17 -0600266 if (b->neg) neg = true;
Gavin Howard63738202018-09-26 15:34:20 -0600267 else return -1;
268 }
269 else if (b->neg) return 1;
Gavin Howard4681d1b2018-03-05 19:49:33 -0700270
Gavin Howard7b7d2f32019-02-21 16:46:47 -0700271 a_int = bc_num_int(a);
272 b_int = bc_num_int(b);
Gavin Howard890d0c02018-10-30 16:34:50 -0600273 a_int -= b_int;
274 a_max = (a->rdx > b->rdx);
Gavin Howard4681d1b2018-03-05 19:49:33 -0700275
Gavin Howardbb86c3b2019-11-21 20:51:55 -0700276 if (a_int) return neg ? -((ssize_t) a_int) : (ssize_t) a_int;
Gavin Howard4681d1b2018-03-05 19:49:33 -0700277
Gavin Howard890d0c02018-10-30 16:34:50 -0600278 if (a_max) {
Gavin Howard63738202018-09-26 15:34:20 -0600279 min = b->rdx;
280 diff = a->rdx - b->rdx;
281 max_num = a->num + diff;
282 min_num = b->num;
283 }
284 else {
285 min = a->rdx;
286 diff = b->rdx - a->rdx;
287 max_num = b->num + diff;
288 min_num = a->num;
289 }
Gavin Howard4681d1b2018-03-05 19:49:33 -0700290
Gavin Howard890d0c02018-10-30 16:34:50 -0600291 cmp = bc_num_compare(max_num, min_num, b_int + min);
Gavin Howard3378af82019-05-11 08:29:39 -0600292
293#if BC_ENABLE_SIGNALS
Stefan Esser94fb4802019-06-06 22:07:20 +0200294 if (BC_NUM_CMP_SIGNAL(cmp)) return cmp;
Gavin Howard3378af82019-05-11 08:29:39 -0600295#endif // BC_ENABLE_SIGNALS
296
Gavin Howard5eba0e82019-02-21 18:08:33 -0700297 if (cmp) return bc_num_neg((size_t) cmp, !a_max == !neg);
Gavin Howard021150b2018-03-10 15:40:42 -0700298
Gavin Howard2d188a52019-02-25 14:19:08 -0700299 for (max_num -= diff, i = diff - 1; BC_NO_SIG && i < diff; --i) {
Gavin Howard5eba0e82019-02-21 18:08:33 -0700300 if (max_num[i]) return bc_num_neg(1, !a_max == !neg);
Gavin Howard63738202018-09-26 15:34:20 -0600301 }
Gavin Howard6fbdb292018-02-27 15:44:48 -0700302
Stefan Esser94fb4802019-06-06 22:07:20 +0200303 return BC_SIG ? BC_NUM_CMP_SIGNAL_VAL : 0;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700304}
305
Gavin Howard3c02ed32018-12-28 18:17:15 -0700306void bc_num_truncate(BcNum *restrict n, size_t places) {
Gavin Howard6fbdb292018-02-27 15:44:48 -0700307
Gavin Howardb1558662019-04-26 14:32:55 -0600308 size_t places_rdx;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700309
Stefan Esser70f11852019-04-27 10:39:00 +0200310 if (!places) return;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700311
Gavin Howard0737af72019-06-19 22:12:26 -0600312 places_rdx = n->rdx ? n->rdx - BC_NUM_RDX(n->scale - places) : 0;
Gavin Howardb1558662019-04-26 14:32:55 -0600313 assert(places <= n->scale && (BC_NUM_ZERO(n) || places_rdx <= n->len));
314
315 n->scale -= places;
316 n->rdx -= places_rdx;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700317
Gavin Howardddce6d42019-01-03 14:07:06 -0700318 if (BC_NUM_NONZERO(n)) {
Gavin Howardb1558662019-04-26 14:32:55 -0600319
Gavin Howardc9bef2d2019-04-26 14:46:52 -0600320 size_t pow;
321
Gavin Howard92ba4e52019-05-10 20:49:13 -0600322 pow = n->scale % BC_BASE_DIGS;
323 pow = pow ? BC_BASE_DIGS - pow : 0;
Gavin Howardc1542802019-05-08 17:20:51 -0600324 pow = bc_num_pow10[pow];
Gavin Howardb1558662019-04-26 14:32:55 -0600325
326 n->len -= places_rdx;
Gavin Howard404ece72019-04-26 15:59:15 -0600327 memmove(n->num, n->num + places_rdx, BC_NUM_SIZE(n->len));
Gavin Howardb1558662019-04-26 14:32:55 -0600328
329 // Clear the lower part of the last digit.
Gavin Howardda6b5432019-04-26 14:33:43 -0600330 if (BC_NUM_NONZERO(n)) n->num[0] -= n->num[0] % (BcDig) pow;
Gavin Howardb1558662019-04-26 14:32:55 -0600331
Gavin Howard2a366922019-01-16 10:50:20 -0700332 bc_num_clean(n);
Gavin Howard955da852018-10-23 11:08:20 -0600333 }
Gavin Howard6fbdb292018-02-27 15:44:48 -0700334}
335
Stefan Esser70f11852019-04-27 10:39:00 +0200336static void bc_num_extend(BcNum *restrict n, size_t places) {
Gavin Howard6fbdb292018-02-27 15:44:48 -0700337
Gavin Howardb5e6da92019-04-30 19:58:04 -0600338 size_t places_rdx;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700339
Gavin Howard1ab22d22019-01-03 13:32:17 -0700340 if (!places) return;
Gavin Howardfea7e312019-05-14 11:50:43 -0600341 if (BC_NUM_ZERO(n)) {
342 n->scale += places;
343 return;
344 }
Gavin Howard6fbdb292018-02-27 15:44:48 -0700345
Gavin Howardb5e6da92019-04-30 19:58:04 -0600346 places_rdx = BC_NUM_RDX(places + n->scale) - n->rdx;
347
Gavin Howard8721e332019-05-06 08:02:49 -0600348 if (places_rdx) {
349 bc_num_expand(n, bc_vm_growSize(n->len, places_rdx));
350 memmove(n->num + places_rdx, n->num, BC_NUM_SIZE(n->len));
351 memset(n->num, 0, BC_NUM_SIZE(places_rdx));
352 }
Gavin Howardb5e6da92019-04-30 19:58:04 -0600353
Gavin Howarde6f326e2019-04-26 10:13:45 -0600354 n->rdx += places_rdx;
355 n->scale += places;
Gavin Howardb1558662019-04-26 14:32:55 -0600356 n->len += places_rdx;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700357
Gavin Howarde6f326e2019-04-26 10:13:45 -0600358 assert(n->rdx == BC_NUM_RDX(n->scale));
Stefan Esser193f8522019-04-27 01:29:01 +0200359}
360
Gavin Howard2d188a52019-02-25 14:19:08 -0700361static void bc_num_retireMul(BcNum *restrict n, size_t scale,
362 bool neg1, bool neg2)
363{
Gavin Howard971a2672019-04-26 14:32:07 -0600364 if (n->scale < scale) bc_num_extend(n, scale - n->scale);
365 else bc_num_truncate(n, n->scale - scale);
Gavin Howard337fe602018-09-01 15:10:59 -0600366
Gavin Howard305249a2018-10-15 20:24:47 -0600367 bc_num_clean(n);
Gavin Howardddce6d42019-01-03 14:07:06 -0700368 if (BC_NUM_NONZERO(n)) n->neg = (!neg1 != !neg2);
Gavin Howard305249a2018-10-15 20:24:47 -0600369}
370
Gavin Howard2d188a52019-02-25 14:19:08 -0700371static void bc_num_split(const BcNum *restrict n, size_t idx,
372 BcNum *restrict a, BcNum *restrict b)
373{
Gavin Howardca97b402019-08-01 07:11:46 -0600374 assert(BC_NUM_ZERO(a));
375 assert(BC_NUM_ZERO(b));
376
Gavin Howard305249a2018-10-15 20:24:47 -0600377 if (idx < n->len) {
378
Gavin Howard305249a2018-10-15 20:24:47 -0600379 b->len = n->len - idx;
380 a->len = idx;
Gavin Howarda3f260c2019-04-26 15:47:25 -0600381 a->scale = a->rdx = b->scale = b->rdx = 0;
Gavin Howard305249a2018-10-15 20:24:47 -0600382
Gavin Howardca97b402019-08-01 07:11:46 -0600383 assert(a->cap >= a->len);
384 assert(b->cap >= b->len);
385
Gavin Howard404ece72019-04-26 15:59:15 -0600386 memcpy(b->num, n->num + idx, BC_NUM_SIZE(b->len));
387 memcpy(a->num, n->num, BC_NUM_SIZE(idx));
Gavin Howard50c80022019-01-03 15:14:33 -0700388
389 bc_num_clean(b);
Gavin Howard305249a2018-10-15 20:24:47 -0600390 }
Gavin Howard50c80022019-01-03 15:14:33 -0700391 else bc_num_copy(a, n);
Gavin Howard305249a2018-10-15 20:24:47 -0600392
393 bc_num_clean(a);
Gavin Howard305249a2018-10-15 20:24:47 -0600394}
395
Gavin Howard14913fc2019-04-23 14:36:03 -0600396static size_t bc_num_shiftZero(BcNum *restrict n) {
397
398 size_t i;
399
400 assert(!n->rdx || BC_NUM_ZERO(n));
401
402 for (i = 0; i < n->len && !n->num[i]; ++i);
403
404 n->len -= i;
405 n->num += i;
406
407 return i;
408}
409
Gavin Howard2e736de2019-04-27 06:41:58 -0600410static void bc_num_unshiftZero(BcNum *restrict n, size_t places_rdx) {
411 n->len += places_rdx;
412 n->num -= places_rdx;
Gavin Howard14913fc2019-04-23 14:36:03 -0600413}
414
Gavin Howard2956a5e2019-05-08 08:04:06 -0600415static BcStatus bc_num_shift(BcNum *restrict n, BcBigDig dig) {
Gavin Howardbe884592019-04-29 15:07:49 -0600416
417 size_t i, len = n->len;
Gavin Howard2956a5e2019-05-08 08:04:06 -0600418 BcBigDig carry = 0, pow;
Gavin Howardbe884592019-04-29 15:07:49 -0600419 BcDig *ptr = n->num;
Gavin Howard305249a2018-10-15 20:24:47 -0600420
Gavin Howard92ba4e52019-05-10 20:49:13 -0600421 assert(dig < BC_BASE_DIGS);
Gavin Howard305249a2018-10-15 20:24:47 -0600422
Gavin Howardc1542802019-05-08 17:20:51 -0600423 pow = bc_num_pow10[dig];
Gavin Howard92ba4e52019-05-10 20:49:13 -0600424 dig = bc_num_pow10[BC_BASE_DIGS - dig];
Gavin Howardb1558662019-04-26 14:32:55 -0600425
Gavin Howard834fde12019-04-30 08:00:05 -0600426 for (i = len - 1; BC_NO_SIG && i < len; --i) {
Gavin Howard2956a5e2019-05-08 08:04:06 -0600427 BcBigDig in, temp;
428 in = ((BcBigDig) ptr[i]);
Gavin Howard834fde12019-04-30 08:00:05 -0600429 temp = carry * dig;
430 carry = in % pow;
431 ptr[i] = ((BcDig) (in / pow)) + (BcDig) temp;
Gavin Howard305249a2018-10-15 20:24:47 -0600432 }
433
Gavin Howard834fde12019-04-30 08:00:05 -0600434 assert(!carry);
Gavin Howardb1558662019-04-26 14:32:55 -0600435
436 return BC_SIG ? BC_STATUS_SIGNAL : BC_STATUS_SUCCESS;
Gavin Howard337fe602018-09-01 15:10:59 -0600437}
438
Gavin Howardb1558662019-04-26 14:32:55 -0600439static BcStatus bc_num_shiftLeft(BcNum *restrict n, size_t places) {
Gavin Howard7ad5a662019-02-19 14:40:46 -0700440
Gavin Howardb1558662019-04-26 14:32:55 -0600441 BcStatus s = BC_STATUS_SUCCESS;
Gavin Howard2956a5e2019-05-08 08:04:06 -0600442 BcBigDig dig;
Gavin Howard48a0e472019-04-26 15:39:46 -0600443 size_t places_rdx;
Gavin Howardbe884592019-04-29 15:07:49 -0600444 bool shift;
Gavin Howard14913fc2019-04-23 14:36:03 -0600445
Gavin Howardb1558662019-04-26 14:32:55 -0600446 if (!places) return s;
Gavin Howardabe4fdf2019-05-06 08:39:53 -0600447 if (places > n->scale) {
448 size_t size = bc_vm_growSize(BC_NUM_RDX(places - n->scale), n->len);
449 if (size > SIZE_MAX - 1) return bc_vm_err(BC_ERROR_MATH_OVERFLOW);
450 }
Gavin Howard4631f3a2019-04-30 10:48:40 -0600451 if (BC_NUM_ZERO(n)) {
452 if (n->scale >= places) n->scale -= places;
453 else n->scale = 0;
454 return s;
455 }
Gavin Howard14913fc2019-04-23 14:36:03 -0600456
Gavin Howard92ba4e52019-05-10 20:49:13 -0600457 dig = (BcBigDig) (places % BC_BASE_DIGS);
Gavin Howard48a0e472019-04-26 15:39:46 -0600458 shift = (dig != 0);
Gavin Howard22253c72019-05-06 08:03:57 -0600459 places_rdx = BC_NUM_RDX(places);
Gavin Howard48a0e472019-04-26 15:39:46 -0600460
Gavin Howard22253c72019-05-06 08:03:57 -0600461 if (n->scale) {
Gavin Howarda1879192019-04-30 18:55:08 -0600462
463 if (n->rdx >= places_rdx) {
464
Gavin Howard92ba4e52019-05-10 20:49:13 -0600465 size_t mod = n->scale % BC_BASE_DIGS, revdig;
Gavin Howarda1879192019-04-30 18:55:08 -0600466
Gavin Howard92ba4e52019-05-10 20:49:13 -0600467 mod = mod ? mod : BC_BASE_DIGS;
468 revdig = dig ? BC_BASE_DIGS - dig : 0;
Gavin Howarda1879192019-04-30 18:55:08 -0600469
Gavin Howard92ba4e52019-05-10 20:49:13 -0600470 if (mod + revdig > BC_BASE_DIGS) places_rdx = 1;
Gavin Howarda1879192019-04-30 18:55:08 -0600471 else places_rdx = 0;
472 }
473 else places_rdx -= n->rdx;
474 }
Gavin Howard4631f3a2019-04-30 10:48:40 -0600475
476 if (places_rdx) {
Gavin Howarde396dff2019-05-01 07:28:36 -0600477 bc_num_expand(n, bc_vm_growSize(n->len, places_rdx));
Gavin Howard834fde12019-04-30 08:00:05 -0600478 memmove(n->num + places_rdx, n->num, BC_NUM_SIZE(n->len));
479 memset(n->num, 0, BC_NUM_SIZE(places_rdx));
480 n->len += places_rdx;
Gavin Howardb1558662019-04-26 14:32:55 -0600481 }
Gavin Howard4631f3a2019-04-30 10:48:40 -0600482
483 if (places > n->scale) n->scale = n->rdx = 0;
484 else {
485 n->scale -= places;
486 n->rdx = BC_NUM_RDX(n->scale);
487 }
Gavin Howardb1558662019-04-26 14:32:55 -0600488
Gavin Howard92ba4e52019-05-10 20:49:13 -0600489 if (shift) s = bc_num_shift(n, BC_BASE_DIGS - dig);
Gavin Howardb1558662019-04-26 14:32:55 -0600490
491 bc_num_clean(n);
492
493 return BC_SIG && !s ? BC_STATUS_SIGNAL : s;
494}
495
496static BcStatus bc_num_shiftRight(BcNum *restrict n, size_t places) {
497
498 BcStatus s = BC_STATUS_SUCCESS;
Gavin Howard2956a5e2019-05-08 08:04:06 -0600499 BcBigDig dig;
Gavin Howardb5e6da92019-04-30 19:58:04 -0600500 size_t places_rdx, scale, scale_mod, int_len, expand;
501 bool shift;
Gavin Howardb1558662019-04-26 14:32:55 -0600502
503 if (!places) return s;
Gavin Howard14913fc2019-04-23 14:36:03 -0600504 if (BC_NUM_ZERO(n)) {
Gavin Howardb1558662019-04-26 14:32:55 -0600505 n->scale += places;
506 bc_num_expand(n, BC_NUM_RDX(n->scale));
507 return s;
Gavin Howard14913fc2019-04-23 14:36:03 -0600508 }
509
Gavin Howard92ba4e52019-05-10 20:49:13 -0600510 dig = (BcBigDig) (places % BC_BASE_DIGS);
Gavin Howardb5e6da92019-04-30 19:58:04 -0600511 shift = (dig != 0);
Gavin Howard834fde12019-04-30 08:00:05 -0600512 scale = n->scale;
Gavin Howard92ba4e52019-05-10 20:49:13 -0600513 scale_mod = scale % BC_BASE_DIGS;
514 scale_mod = scale_mod ? scale_mod : BC_BASE_DIGS;
Gavin Howard834fde12019-04-30 08:00:05 -0600515 int_len = bc_num_int(n);
Gavin Howardb1558662019-04-26 14:32:55 -0600516 places_rdx = BC_NUM_RDX(places);
Gavin Howard7ad5a662019-02-19 14:40:46 -0700517
Gavin Howard92ba4e52019-05-10 20:49:13 -0600518 if (scale_mod + dig > BC_BASE_DIGS) {
Gavin Howardb5e6da92019-04-30 19:58:04 -0600519 expand = places_rdx - 1;
520 places_rdx = 1;
Gavin Howard834fde12019-04-30 08:00:05 -0600521 }
522 else {
Gavin Howardb5e6da92019-04-30 19:58:04 -0600523 expand = places_rdx;
524 places_rdx = 0;
Gavin Howard7ad5a662019-02-19 14:40:46 -0700525 }
526
Gavin Howardb5e6da92019-04-30 19:58:04 -0600527 if (expand > int_len) expand -= int_len;
528 else expand = 0;
529
Gavin Howard92ba4e52019-05-10 20:49:13 -0600530 bc_num_extend(n, places_rdx * BC_BASE_DIGS);
Gavin Howarde396dff2019-05-01 07:28:36 -0600531 bc_num_expand(n, bc_vm_growSize(expand, n->len));
Gavin Howardb5e6da92019-04-30 19:58:04 -0600532 memset(n->num + n->len, 0, BC_NUM_SIZE(expand));
533 n->len += expand;
Gavin Howard834fde12019-04-30 08:00:05 -0600534 n->scale = n->rdx = 0;
Gavin Howardb1558662019-04-26 14:32:55 -0600535
Gavin Howard834fde12019-04-30 08:00:05 -0600536 if (shift) s = bc_num_shift(n, dig);
537
538 n->scale = scale + places;
539 n->rdx = BC_NUM_RDX(n->scale);
540
541 bc_num_clean(n);
Gavin Howard7ad5a662019-02-19 14:40:46 -0700542
543 assert(n->rdx <= n->len && n->len <= n->cap);
Gavin Howardb1558662019-04-26 14:32:55 -0600544 assert(n->rdx == BC_NUM_RDX(n->scale));
545
546 return BC_SIG && !s ? BC_STATUS_SIGNAL : s;
Gavin Howard7ad5a662019-02-19 14:40:46 -0700547}
Gavin Howard7ad5a662019-02-19 14:40:46 -0700548
Gavin Howard1cbfe242019-01-09 17:13:11 -0700549static BcStatus bc_num_inv(BcNum *a, BcNum *b, size_t scale) {
Gavin Howard9c4358c2018-03-22 20:11:28 -0600550
Gavin Howard63738202018-09-26 15:34:20 -0600551 BcNum one;
Gavin Howardd32d7df2018-10-15 08:42:25 -0600552 BcDig num[2];
Gavin Howard9c4358c2018-03-22 20:11:28 -0600553
Gavin Howard971a2672019-04-26 14:32:07 -0600554 assert(BC_NUM_NONZERO(a));
Gavin Howard1769abc2019-02-21 09:36:14 -0700555
Gavin Howardd0b05f52019-05-15 10:20:54 -0600556 bc_num_setup(&one, num, sizeof(num) / sizeof(BcDig));
Gavin Howard63738202018-09-26 15:34:20 -0600557 bc_num_one(&one);
Gavin Howard9c4358c2018-03-22 20:11:28 -0600558
Gavin Howardd32d7df2018-10-15 08:42:25 -0600559 return bc_num_div(&one, a, b, scale);
Gavin Howard9c4358c2018-03-22 20:11:28 -0600560}
561
Gavin Howard7bda4782018-12-28 09:53:22 -0700562#if BC_ENABLE_EXTRA_MATH
Gavin Howard1cbfe242019-01-09 17:13:11 -0700563static BcStatus bc_num_intop(const BcNum *a, const BcNum *b, BcNum *restrict c,
Gavin Howard2956a5e2019-05-08 08:04:06 -0600564 BcBigDig *v)
Gavin Howard3c02ed32018-12-28 18:17:15 -0700565{
Gavin Howardecafd4f2019-02-23 09:30:45 -0700566 if (BC_ERR(b->rdx)) return bc_vm_err(BC_ERROR_MATH_NON_INTEGER);
Gavin Howardac4d9122018-12-27 23:59:12 -0700567 bc_num_copy(c, a);
Gavin Howard2956a5e2019-05-08 08:04:06 -0600568 return bc_num_bigdig(b, v);
Gavin Howardac4d9122018-12-27 23:59:12 -0700569}
Gavin Howard7bda4782018-12-28 09:53:22 -0700570#endif // BC_ENABLE_EXTRA_MATH
Gavin Howardac4d9122018-12-27 23:59:12 -0700571
Stefan Esser73c0a2a2019-07-31 21:57:00 +0200572static BcStatus bc_num_as(BcNum *a, BcNum *b, BcNum *restrict c, size_t sub) {
573
574 BcDig *ptr_c, *ptr_l, *ptr_r;
Gavin Howard6e4ccc42019-08-01 09:05:18 -0600575 size_t i, min_rdx, max_rdx, diff, a_int, b_int, min_len, max_len, max_int;
576 size_t len_l, len_r;
Stefan Esser73c0a2a2019-07-31 21:57:00 +0200577 bool b_neg, do_sub, do_rev_sub, carry;
578
579 // Because this function doesn't need to use scale (per the bc spec),
580 // I am hijacking it to say whether it's doing an add or a subtract.
581 // Convert substraction to addition of negative second operand.
582
Stefan Esser73c0a2a2019-07-31 21:57:00 +0200583 if (BC_NUM_ZERO(b)) {
584 bc_num_copy(c, a);
585 return BC_STATUS_SUCCESS;
586 }
587 if (BC_NUM_ZERO(a)) {
588 bc_num_copy(c, b);
Gavin Howard6e4ccc42019-08-01 09:05:18 -0600589 c->neg = (b->neg != sub);
Stefan Esser73c0a2a2019-07-31 21:57:00 +0200590 return BC_STATUS_SUCCESS;
591 }
592
Gavin Howard6e4ccc42019-08-01 09:05:18 -0600593 b_neg = (b->neg != sub);
Stefan Esser73c0a2a2019-07-31 21:57:00 +0200594 do_sub = (a->neg != b_neg);
595
596 a_int = bc_num_int(a);
597 b_int = bc_num_int(b);
598 max_int = BC_MAX(a_int, b_int);
599
600 min_rdx = BC_MIN(a->rdx, b->rdx);
601 max_rdx = BC_MAX(a->rdx, b->rdx);
602 diff = max_rdx - min_rdx;
603
604 max_len = max_int + max_rdx;
605
606 if (do_sub) {
Gavin Howard6e4ccc42019-08-01 09:05:18 -0600607 if (a_int != b_int) do_rev_sub = (a_int < b_int);
Stefan Esser73c0a2a2019-07-31 21:57:00 +0200608 else if (a->rdx > b->rdx)
Gavin Howard6e4ccc42019-08-01 09:05:18 -0600609 do_rev_sub = (bc_num_compare(a->num + diff, b->num, b->len) < 0);
Stefan Esser73c0a2a2019-07-31 21:57:00 +0200610 else
Gavin Howard6e4ccc42019-08-01 09:05:18 -0600611 do_rev_sub = (bc_num_compare(a->num, b->num + diff, a->len) <= 0);
Stefan Esser73c0a2a2019-07-31 21:57:00 +0200612 }
613 else {
Gavin Howard6e4ccc42019-08-01 09:05:18 -0600614 max_len += 1;
Stefan Esser73c0a2a2019-07-31 21:57:00 +0200615 do_rev_sub = false;
616 }
Gavin Howard6e4ccc42019-08-01 09:05:18 -0600617
Stefan Esser73c0a2a2019-07-31 21:57:00 +0200618 assert(max_len <= c->cap);
619
620 if (do_rev_sub) {
Gavin Howard6e4ccc42019-08-01 09:05:18 -0600621 ptr_l = b->num;
622 ptr_r = a->num;
623 len_l = b->len;
624 len_r = a->len;
Stefan Esser73c0a2a2019-07-31 21:57:00 +0200625 }
626 else {
Gavin Howard6e4ccc42019-08-01 09:05:18 -0600627 ptr_l = a->num;
628 ptr_r = b->num;
629 len_l = a->len;
630 len_r = b->len;
Stefan Esser73c0a2a2019-07-31 21:57:00 +0200631 }
Stefan Esser73c0a2a2019-07-31 21:57:00 +0200632
Gavin Howard6e4ccc42019-08-01 09:05:18 -0600633 ptr_c = c->num;
Stefan Esser73c0a2a2019-07-31 21:57:00 +0200634 carry = false;
Gavin Howard6e4ccc42019-08-01 09:05:18 -0600635
Stefan Esser73c0a2a2019-07-31 21:57:00 +0200636 if (diff) {
Gavin Howard6e4ccc42019-08-01 09:05:18 -0600637
638 if ((a->rdx > b->rdx) != do_rev_sub) {
Stefan Esser73c0a2a2019-07-31 21:57:00 +0200639 memcpy(ptr_c, ptr_l, BC_NUM_SIZE(diff));
640 ptr_l += diff;
641 len_l -= diff;
642 }
643 else {
Gavin Howard6e4ccc42019-08-01 09:05:18 -0600644
645 if (do_sub) {
646
647 for (i = 0; BC_NO_SIG && i < diff; i++)
648 ptr_c[i] = bc_num_subDigits(0, ptr_r[i], &carry);
649
650 if (BC_SIG) return BC_STATUS_SIGNAL;
651 }
652 else memcpy(ptr_c, ptr_r, BC_NUM_SIZE(diff));
653
Stefan Esser73c0a2a2019-07-31 21:57:00 +0200654 ptr_r += diff;
655 len_r -= diff;
656 }
Gavin Howard6e4ccc42019-08-01 09:05:18 -0600657
Stefan Esser73c0a2a2019-07-31 21:57:00 +0200658 ptr_c += diff;
659 }
Gavin Howard6e4ccc42019-08-01 09:05:18 -0600660
Stefan Esser73c0a2a2019-07-31 21:57:00 +0200661 min_len = BC_MIN(len_l, len_r);
662
Stefan Esser73c0a2a2019-07-31 21:57:00 +0200663 if (do_sub) {
Gavin Howard889a50b2019-11-21 21:34:53 -0700664 for (i = 0; BC_NO_SIG && i < min_len; ++i)
Gavin Howard6e4ccc42019-08-01 09:05:18 -0600665 ptr_c[i] = bc_num_subDigits(ptr_l[i], ptr_r[i], &carry);
Gavin Howard889a50b2019-11-21 21:34:53 -0700666 for (; BC_NO_SIG && i < len_l; ++i)
Gavin Howard6e4ccc42019-08-01 09:05:18 -0600667 ptr_c[i] = bc_num_subDigits(ptr_l[i], 0, &carry);
Gavin Howard889a50b2019-11-21 21:34:53 -0700668 for (; BC_NO_SIG && i < len_r; ++i)
Gavin Howard6e4ccc42019-08-01 09:05:18 -0600669 ptr_c[i] = bc_num_subDigits(0, ptr_r[i], &carry);
Gavin Howard889a50b2019-11-21 21:34:53 -0700670 for (; BC_NO_SIG && i < max_len - diff; ++i)
Gavin Howard6e4ccc42019-08-01 09:05:18 -0600671 ptr_c[i] = bc_num_subDigits(0, 0, &carry);
Stefan Esser73c0a2a2019-07-31 21:57:00 +0200672 }
673 else {
Gavin Howard889a50b2019-11-21 21:34:53 -0700674 for (i = 0; BC_NO_SIG && i < min_len; ++i)
Gavin Howard6e4ccc42019-08-01 09:05:18 -0600675 ptr_c[i] = bc_num_addDigits(ptr_l[i], ptr_r[i], &carry);
Gavin Howard889a50b2019-11-21 21:34:53 -0700676 for (; BC_NO_SIG && i < len_l; ++i)
Gavin Howard6e4ccc42019-08-01 09:05:18 -0600677 ptr_c[i] = bc_num_addDigits(ptr_l[i], 0, &carry);
Gavin Howard889a50b2019-11-21 21:34:53 -0700678 for (; BC_NO_SIG && i < len_r; ++i)
Gavin Howard6e4ccc42019-08-01 09:05:18 -0600679 ptr_c[i] = bc_num_addDigits(0, ptr_r[i], &carry);
Gavin Howard889a50b2019-11-21 21:34:53 -0700680 for (; BC_NO_SIG && i < max_len - diff; ++i)
Gavin Howard6e4ccc42019-08-01 09:05:18 -0600681 ptr_c[i] = bc_num_addDigits(0, 0, &carry);
Stefan Esser73c0a2a2019-07-31 21:57:00 +0200682 }
Stefan Esser73c0a2a2019-07-31 21:57:00 +0200683
Gavin Howard6e4ccc42019-08-01 09:05:18 -0600684 if (BC_NO_SIG) {
Stefan Esser73c0a2a2019-07-31 21:57:00 +0200685
Gavin Howard6e4ccc42019-08-01 09:05:18 -0600686 assert(carry == false);
687
688 // The result has the same sign as a, unless the operation was a
689 // reverse subtraction (b - a).
690 c->neg = (a->neg != do_rev_sub);
691 c->len = max_len;
692 c->rdx = max_rdx;
693 c->scale = BC_MAX(a->scale, b->scale);
694
695 bc_num_clean(c);
696 }
Stefan Esser73c0a2a2019-07-31 21:57:00 +0200697
698 return BC_SIG ? BC_STATUS_SIGNAL : BC_STATUS_SUCCESS;
699}
Stefan Esser73c0a2a2019-07-31 21:57:00 +0200700
Gavin Howarddc4e3712019-04-23 14:56:29 -0600701static BcStatus bc_num_m_simp(const BcNum *a, const BcNum *b, BcNum *restrict c)
702{
Gavin Howard971a2672019-04-26 14:32:07 -0600703 size_t i, alen = a->len, blen = b->len, clen;
Gavin Howard5083aec2019-04-23 18:03:46 -0600704 BcDig *ptr_a = a->num, *ptr_b = b->num, *ptr_c;
Gavin Howard2956a5e2019-05-08 08:04:06 -0600705 BcBigDig sum = 0, carry = 0;
Gavin Howarddc4e3712019-04-23 14:56:29 -0600706
Gavin Howard971a2672019-04-26 14:32:07 -0600707 assert(sizeof(sum) >= sizeof(BcDig) * 2);
Gavin Howardb538d802019-04-23 16:50:55 -0600708 assert(!a->rdx && !b->rdx);
709
Gavin Howard5083aec2019-04-23 18:03:46 -0600710 clen = bc_vm_growSize(alen, blen);
Gavin Howarde396dff2019-05-01 07:28:36 -0600711 bc_num_expand(c, bc_vm_growSize(clen, 1));
Gavin Howarddc4e3712019-04-23 14:56:29 -0600712
713 ptr_c = c->num;
Gavin Howard404ece72019-04-26 15:59:15 -0600714 memset(ptr_c, 0, BC_NUM_SIZE(c->cap));
Gavin Howarddc4e3712019-04-23 14:56:29 -0600715
Gavin Howard5083aec2019-04-23 18:03:46 -0600716 for (i = 0; BC_NO_SIG && i < clen; ++i) {
Gavin Howarddc4e3712019-04-23 14:56:29 -0600717
Gavin Howard5083aec2019-04-23 18:03:46 -0600718 ssize_t sidx = (ssize_t) (i - blen + 1);
719 size_t j = (size_t) BC_MAX(0, sidx), k = BC_MIN(i, blen - 1);
Gavin Howarddc4e3712019-04-23 14:56:29 -0600720
Stefan Essera299ffc2019-04-24 23:28:32 +0200721 for (; BC_NO_SIG && j < alen && k < blen; ++j, --k) {
Stefan Eßer4b111c22019-04-24 17:23:08 -0600722
Gavin Howard2956a5e2019-05-08 08:04:06 -0600723 sum += ((BcBigDig) ptr_a[j]) * ((BcBigDig) ptr_b[k]);
Gavin Howarddc4e3712019-04-23 14:56:29 -0600724
Gavin Howard0ecb2952019-08-01 07:38:28 -0600725 if (sum >= ((BcBigDig) BC_BASE_POW) * BC_BASE_POW) {
Gavin Howard92ba4e52019-05-10 20:49:13 -0600726 carry += sum / BC_BASE_POW;
727 sum %= BC_BASE_POW;
Stefan Essera299ffc2019-04-24 23:28:32 +0200728 }
729 }
Stefan Eßer4b111c22019-04-24 17:23:08 -0600730
Gavin Howard0ecb2952019-08-01 07:38:28 -0600731 if (sum >= BC_BASE_POW) {
732 carry += sum / BC_BASE_POW;
733 sum %= BC_BASE_POW;
734 }
735
Stefan Essera299ffc2019-04-24 23:28:32 +0200736 ptr_c[i] = (BcDig) sum;
Gavin Howard92ba4e52019-05-10 20:49:13 -0600737 assert(ptr_c[i] < BC_BASE_POW);
Stefan Essera299ffc2019-04-24 23:28:32 +0200738 sum = carry;
739 carry = 0;
Gavin Howarddc4e3712019-04-23 14:56:29 -0600740 }
741
Gavin Howard5083aec2019-04-23 18:03:46 -0600742 if (sum) {
Gavin Howard92ba4e52019-05-10 20:49:13 -0600743 assert(sum < BC_BASE_POW);
Gavin Howard12a7cd22019-04-24 07:10:14 -0600744 ptr_c[clen] = (BcDig) sum;
Gavin Howard5083aec2019-04-23 18:03:46 -0600745 clen += 1;
746 }
747
748 c->len = clen;
Gavin Howarda58840c2019-05-07 19:33:53 -0600749
Gavin Howarddc4e3712019-04-23 14:56:29 -0600750 return BC_SIG ? BC_STATUS_SIGNAL : BC_STATUS_SUCCESS;
751}
752
Gavin Howard14c354a2019-04-24 14:28:24 -0600753static BcStatus bc_num_shiftAddSub(BcNum *restrict n, const BcNum *restrict a,
754 size_t shift, BcNumShiftAddOp op)
755{
756 assert(n->len >= shift + a->len);
757 assert(!n->rdx && !a->rdx);
758 return op(n->num + shift, a->num, a->len);
759}
760
761static BcStatus bc_num_k(BcNum *a, BcNum *b, BcNum *restrict c) {
Gavin Howard773c86b2018-11-02 14:07:19 -0600762
Gavin Howard9b801632019-02-16 23:34:06 -0700763 BcStatus s;
Stefan Esser14eff462019-04-25 07:42:03 +0200764 size_t max, max2, total;
Gavin Howard305249a2018-10-15 20:24:47 -0600765 BcNum l1, h1, l2, h2, m2, m1, z0, z1, z2, temp;
Gavin Howard5bede412019-02-23 09:31:28 -0700766 BcDig *digs, *dig_ptr;
Gavin Howard14c354a2019-04-24 14:28:24 -0600767 BcNumShiftAddOp op;
Gavin Howardb2f01a42019-05-10 20:00:32 -0600768 bool aone = BC_NUM_ONE(a);
Gavin Howard337fe602018-09-01 15:10:59 -0600769
Gavin Howard14913fc2019-04-23 14:36:03 -0600770 assert(BC_NUM_ZERO(c));
771
Gavin Howardddce6d42019-01-03 14:07:06 -0700772 // This is here because the function is recursive.
Stefan Esser94fb4802019-06-06 22:07:20 +0200773 if (BC_SIG) return BC_STATUS_SIGNAL;
Gavin Howardf1ae2be2019-05-09 11:58:22 -0600774 if (BC_NUM_ZERO(a) || BC_NUM_ZERO(b)) return BC_STATUS_SUCCESS;
Gavin Howardb2f01a42019-05-10 20:00:32 -0600775 if (aone || BC_NUM_ONE(b)) {
Gavin Howardad0ecfe2018-10-30 01:16:01 -0600776 bc_num_copy(c, aone ? b : a);
Gavin Howardb2f01a42019-05-10 20:00:32 -0600777 if ((aone && a->neg) || b->neg) c->neg = !c->neg;
Gavin Howardad0ecfe2018-10-30 01:16:01 -0600778 return BC_STATUS_SUCCESS;
779 }
Gavin Howardb7417b12019-05-10 18:39:46 -0600780 if (a->len < BC_NUM_KARATSUBA_LEN || b->len < BC_NUM_KARATSUBA_LEN)
Gavin Howarddc4e3712019-04-23 14:56:29 -0600781 return bc_num_m_simp(a, b, c);
Gavin Howard6fbdb292018-02-27 15:44:48 -0700782
Gavin Howarde0432502019-02-26 16:23:45 -0700783 max = BC_MAX(a->len, b->len);
Stefan Essera299ffc2019-04-24 23:28:32 +0200784 max = BC_MAX(max, BC_NUM_DEF_SIZE);
Gavin Howarde0432502019-02-26 16:23:45 -0700785 max2 = (max + 1) / 2;
786
Stefan Esser5fd43a32019-04-25 07:05:24 +0200787 total = bc_vm_arraySize(BC_NUM_KARATSUBA_ALLOCS, max);
Gavin Howard404ece72019-04-26 15:59:15 -0600788 digs = dig_ptr = bc_vm_malloc(BC_NUM_SIZE(total));
Gavin Howard5bede412019-02-23 09:31:28 -0700789
790 bc_num_setup(&l1, dig_ptr, max);
Stefan Essera299ffc2019-04-24 23:28:32 +0200791 dig_ptr += max;
Gavin Howard5bede412019-02-23 09:31:28 -0700792 bc_num_setup(&h1, dig_ptr, max);
Stefan Essera299ffc2019-04-24 23:28:32 +0200793 dig_ptr += max;
Gavin Howard5bede412019-02-23 09:31:28 -0700794 bc_num_setup(&l2, dig_ptr, max);
Stefan Essera299ffc2019-04-24 23:28:32 +0200795 dig_ptr += max;
Gavin Howard5bede412019-02-23 09:31:28 -0700796 bc_num_setup(&h2, dig_ptr, max);
Stefan Essera299ffc2019-04-24 23:28:32 +0200797 dig_ptr += max;
Gavin Howard5bede412019-02-23 09:31:28 -0700798 bc_num_setup(&m1, dig_ptr, max);
Stefan Essera299ffc2019-04-24 23:28:32 +0200799 dig_ptr += max;
Gavin Howard5bede412019-02-23 09:31:28 -0700800 bc_num_setup(&m2, dig_ptr, max);
Gavin Howard57223022019-04-24 08:37:14 -0600801 max = bc_vm_growSize(max, 1);
Gavin Howardad0ecfe2018-10-30 01:16:01 -0600802 bc_num_init(&z0, max);
803 bc_num_init(&z1, max);
804 bc_num_init(&z2, max);
Gavin Howard14c354a2019-04-24 14:28:24 -0600805 max = bc_vm_growSize(max, max) + 1;
806 bc_num_init(&temp, max);
Gavin Howard0dfe2922018-05-22 13:57:02 -0600807
Gavin Howardad0ecfe2018-10-30 01:16:01 -0600808 bc_num_split(a, max2, &l1, &h1);
809 bc_num_split(b, max2, &l2, &h2);
Gavin Howard6fbdb292018-02-27 15:44:48 -0700810
Gavin Howard14c354a2019-04-24 14:28:24 -0600811 bc_num_expand(c, max);
812 c->len = max;
Gavin Howard404ece72019-04-26 15:59:15 -0600813 memset(c->num, 0, BC_NUM_SIZE(c->len));
Gavin Howard305249a2018-10-15 20:24:47 -0600814
Gavin Howard14c354a2019-04-24 14:28:24 -0600815 s = bc_num_sub(&h1, &l1, &m1, 0);
816 if (BC_ERR(s)) goto err;
817 s = bc_num_sub(&l2, &h2, &m2, 0);
818 if (BC_ERR(s)) goto err;
Gavin Howard305249a2018-10-15 20:24:47 -0600819
Gavin Howard971a2672019-04-26 14:32:07 -0600820 if (BC_NUM_NONZERO(&h1) && BC_NUM_NONZERO(&h2)) {
Gavin Howard305249a2018-10-15 20:24:47 -0600821
Gavin Howard14c354a2019-04-24 14:28:24 -0600822 s = bc_num_m(&h1, &h2, &z2, 0);
823 if (BC_ERR(s)) goto err;
824 bc_num_clean(&z2);
825
826 s = bc_num_shiftAddSub(c, &z2, max2 * 2, bc_num_addArrays);
827 if (BC_ERR(s)) goto err;
828 s = bc_num_shiftAddSub(c, &z2, max2, bc_num_addArrays);
829 if (BC_ERR(s)) goto err;
830 }
831
Gavin Howard971a2672019-04-26 14:32:07 -0600832 if (BC_NUM_NONZERO(&l1) && BC_NUM_NONZERO(&l2)) {
Gavin Howard14c354a2019-04-24 14:28:24 -0600833
834 s = bc_num_m(&l1, &l2, &z0, 0);
835 if (BC_ERR(s)) goto err;
836 bc_num_clean(&z0);
837
838 s = bc_num_shiftAddSub(c, &z0, max2, bc_num_addArrays);
839 if (BC_ERR(s)) goto err;
840 s = bc_num_shiftAddSub(c, &z0, 0, bc_num_addArrays);
841 if (BC_ERR(s)) goto err;
842 }
843
Gavin Howard971a2672019-04-26 14:32:07 -0600844 if (BC_NUM_NONZERO(&m1) && BC_NUM_NONZERO(&m2)) {
Gavin Howard14c354a2019-04-24 14:28:24 -0600845
846 s = bc_num_m(&m1, &m2, &z1, 0);
847 if (BC_ERR(s)) goto err;
848 bc_num_clean(&z1);
849
850 op = (m1.neg != m2.neg) ? bc_num_subArrays : bc_num_addArrays;
851 s = bc_num_shiftAddSub(c, &z1, max2, op);
852 if (BC_ERR(s)) goto err;
853 }
Gavin Howard305249a2018-10-15 20:24:47 -0600854
855err:
Gavin Howard5bede412019-02-23 09:31:28 -0700856 free(digs);
Gavin Howard305249a2018-10-15 20:24:47 -0600857 bc_num_free(&temp);
Gavin Howard305249a2018-10-15 20:24:47 -0600858 bc_num_free(&z2);
Gavin Howard305249a2018-10-15 20:24:47 -0600859 bc_num_free(&z1);
Gavin Howard305249a2018-10-15 20:24:47 -0600860 bc_num_free(&z0);
Gavin Howard305249a2018-10-15 20:24:47 -0600861 return s;
862}
863
Gavin Howard1cbfe242019-01-09 17:13:11 -0700864static BcStatus bc_num_m(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale) {
Gavin Howard305249a2018-10-15 20:24:47 -0600865
866 BcStatus s;
867 BcNum cpa, cpb;
Gavin Howard48a0e472019-04-26 15:39:46 -0600868 size_t ascale, bscale, ardx, brdx, azero = 0, bzero = 0, zero, len, rscale;
Gavin Howard305249a2018-10-15 20:24:47 -0600869
Stefan Eßera1445f22019-08-01 07:17:00 -0600870 bc_num_zero(c);
Gavin Howardcf9f2462019-04-27 06:43:57 -0600871 ascale = a->scale;
872 bscale = b->scale;
873 scale = BC_MAX(scale, ascale);
Gavin Howarda9a887a2019-04-29 14:00:56 -0600874 scale = BC_MAX(scale, bscale);
Gavin Howard7cb5f6d2019-04-29 14:01:44 -0600875
Gavin Howard48a0e472019-04-26 15:39:46 -0600876 rscale = ascale + bscale;
Gavin Howard14913fc2019-04-23 14:36:03 -0600877 scale = BC_MIN(rscale, scale);
Gavin Howardb3f1ee22019-05-07 21:49:15 -0600878
Gavin Howardcc2974b2019-05-08 11:07:28 -0600879 if ((a->len == 1 || b->len == 1) && !a->rdx && !b->rdx) {
Gavin Howardb3f1ee22019-05-07 21:49:15 -0600880
881 BcNum *operand;
Gavin Howard2956a5e2019-05-08 08:04:06 -0600882 BcBigDig dig;
Gavin Howardb3f1ee22019-05-07 21:49:15 -0600883
884 if (a->len == 1) {
Gavin Howard2956a5e2019-05-08 08:04:06 -0600885 dig = (BcBigDig) a->num[0];
Gavin Howardb3f1ee22019-05-07 21:49:15 -0600886 operand = b;
887 }
888 else {
Gavin Howard2956a5e2019-05-08 08:04:06 -0600889 dig = (BcBigDig) b->num[0];
Gavin Howardb3f1ee22019-05-07 21:49:15 -0600890 operand = a;
891 }
892
893 s = bc_num_mulArray(operand, dig, c);
Gavin Howard66a93552019-05-09 17:13:59 -0600894 if (BC_ERROR_SIGNAL_ONLY(s)) return s;
Gavin Howardb3f1ee22019-05-07 21:49:15 -0600895
Gavin Howard51152ca2019-05-14 11:50:16 -0600896 if (BC_NUM_NONZERO(c)) c->neg = (a->neg != b->neg);
Gavin Howard15eca212019-05-14 13:08:12 -0600897
Gavin Howard66a93552019-05-09 17:13:59 -0600898 return s;
Gavin Howardb3f1ee22019-05-07 21:49:15 -0600899 }
900
Gavin Howard66a93552019-05-09 17:13:59 -0600901 bc_num_init(&cpa, a->len + a->rdx);
902 bc_num_init(&cpb, b->len + b->rdx);
903 bc_num_copy(&cpa, a);
904 bc_num_copy(&cpb, b);
905
906 cpa.neg = cpb.neg = false;
907
Gavin Howard92ba4e52019-05-10 20:49:13 -0600908 ardx = cpa.rdx * BC_BASE_DIGS;
Gavin Howard48a0e472019-04-26 15:39:46 -0600909 s = bc_num_shiftLeft(&cpa, ardx);
910 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howard14913fc2019-04-23 14:36:03 -0600911 bc_num_clean(&cpa);
912 azero = bc_num_shiftZero(&cpa);
Gavin Howard48a0e472019-04-26 15:39:46 -0600913
Gavin Howard92ba4e52019-05-10 20:49:13 -0600914 brdx = cpb.rdx * BC_BASE_DIGS;
Gavin Howard48a0e472019-04-26 15:39:46 -0600915 s = bc_num_shiftLeft(&cpb, brdx);
916 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howard14913fc2019-04-23 14:36:03 -0600917 bzero = bc_num_shiftZero(&cpb);
918 bc_num_clean(&cpb);
919
Gavin Howard14c354a2019-04-24 14:28:24 -0600920 s = bc_num_k(&cpa, &cpb, c);
Gavin Howardc78e7522019-02-22 13:24:25 -0700921 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howard305249a2018-10-15 20:24:47 -0600922
Gavin Howard14913fc2019-04-23 14:36:03 -0600923 zero = bc_vm_growSize(azero, bzero);
924 len = bc_vm_growSize(c->len, zero);
Gavin Howardad0ecfe2018-10-30 01:16:01 -0600925
Gavin Howard14913fc2019-04-23 14:36:03 -0600926 bc_num_expand(c, len);
Gavin Howard92ba4e52019-05-10 20:49:13 -0600927 s = bc_num_shiftLeft(c, (len - c->len) * BC_BASE_DIGS);
Gavin Howard48a0e472019-04-26 15:39:46 -0600928 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
929 s = bc_num_shiftRight(c, ardx + brdx);
930 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howardb3f1ee22019-05-07 21:49:15 -0600931
Gavin Howardad0ecfe2018-10-30 01:16:01 -0600932 bc_num_retireMul(c, scale, a->neg, b->neg);
Gavin Howardcf9f2462019-04-27 06:43:57 -0600933
Gavin Howard305249a2018-10-15 20:24:47 -0600934err:
Gavin Howard14913fc2019-04-23 14:36:03 -0600935 bc_num_unshiftZero(&cpb, bzero);
Gavin Howard305249a2018-10-15 20:24:47 -0600936 bc_num_free(&cpb);
Gavin Howard14913fc2019-04-23 14:36:03 -0600937 bc_num_unshiftZero(&cpa, azero);
Gavin Howard305249a2018-10-15 20:24:47 -0600938 bc_num_free(&cpa);
939 return s;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700940}
941
Gavin Howard61b7f3f2019-06-23 10:00:28 -0600942static bool bc_num_nonZeroDig(BcDig *restrict a, size_t len) {
Gavin Howardb740ef72019-06-19 07:34:04 -0600943 size_t i;
944 bool nonzero = false;
945 for (i = len - 1; !nonzero && i < len; --i) nonzero = (a[i] != 0);
946 return nonzero;
947}
948
Stefan Esser94fb4802019-06-06 22:07:20 +0200949static ssize_t bc_num_divCmp(const BcDig *a, const BcNum *b, size_t len) {
Stefan Esser45edd422019-06-05 21:04:42 +0200950
Stefan Esser94fb4802019-06-06 22:07:20 +0200951 ssize_t cmp;
952
953 if (b->len > len && a[len]) cmp = bc_num_compare(a, b->num, len + 1);
954 else if (b->len <= len) {
955 if (a[len]) cmp = 1;
956 else cmp = bc_num_compare(a, b->num, len);
Stefan Esser45edd422019-06-05 21:04:42 +0200957 }
Stefan Esser94fb4802019-06-06 22:07:20 +0200958 else cmp = -1;
959
960 return cmp;
Gavin Howard1980e032019-05-08 17:21:17 -0600961}
962
Gavin Howardb740ef72019-06-19 07:34:04 -0600963static BcStatus bc_num_divExtend(BcNum *restrict a, BcNum *restrict b,
964 BcBigDig divisor)
965{
966 BcStatus s;
967 size_t pow;
968
969 pow = BC_BASE_DIGS - bc_num_log10((size_t) divisor);
970
971 s = bc_num_shiftLeft(a, pow);
972 if (BC_ERROR_SIGNAL_ONLY(s)) return s;
973
974 return bc_num_shiftLeft(b, pow);
975}
976
Stefan Esser035ace72019-06-06 08:14:43 +0200977static BcStatus bc_num_d_long(BcNum *restrict a, BcNum *restrict b,
Gavin Howardbd60ec72019-05-10 08:01:22 -0600978 BcNum *restrict c, size_t scale)
Gavin Howardd5b27c82019-05-09 12:13:07 -0600979{
Gavin Howardad0ecfe2018-10-30 01:16:01 -0600980 BcStatus s = BC_STATUS_SUCCESS;
Gavin Howardb740ef72019-06-19 07:34:04 -0600981 BcBigDig divisor;
Gavin Howardbd60ec72019-05-10 08:01:22 -0600982 size_t len, end, i, rdx;
Gavin Howardb740ef72019-06-19 07:34:04 -0600983 BcNum cpb;
984 bool nonzero = false;
Stefan Esser94fb4802019-06-06 22:07:20 +0200985
Gavin Howardb740ef72019-06-19 07:34:04 -0600986 assert(b->len < a->len);
Gavin Howardd5b27c82019-05-09 12:13:07 -0600987 len = b->len;
988 end = a->len - len;
Gavin Howardb740ef72019-06-19 07:34:04 -0600989 assert(len >= 1);
Gavin Howardbd60ec72019-05-10 08:01:22 -0600990
Stefan Esser94fb4802019-06-06 22:07:20 +0200991 bc_num_expand(c, a->len);
Gavin Howardb740ef72019-06-19 07:34:04 -0600992 memset(c->num, 0, c->cap * sizeof(BcDig));
Stefan Esser94fb4802019-06-06 22:07:20 +0200993
Gavin Howardd5b27c82019-05-09 12:13:07 -0600994 c->rdx = a->rdx;
995 c->scale = a->scale;
996 c->len = a->len;
997
Gavin Howardb740ef72019-06-19 07:34:04 -0600998 divisor = (BcBigDig) b->num[len - 1];
999
1000 if (len > 1 && bc_num_nonZeroDig(b->num, len - 1)) {
1001
1002 nonzero = (divisor > 1 << ((10 * BC_BASE_DIGS) / 6 + 1));
1003
1004 if (!nonzero) {
1005
1006 s = bc_num_divExtend(a, b, divisor);
1007 if (BC_ERROR_SIGNAL_ONLY(s)) return s;
1008
1009 len = BC_MAX(a->len, b->len);
1010 bc_num_expand(a, len + 1);
Gavin Howardd27f53e2019-06-22 21:57:31 -06001011
1012 if (len + 1 > a->len) a->len = len + 1;
Gavin Howardb740ef72019-06-19 07:34:04 -06001013
1014 len = b->len;
1015 end = a->len - len;
1016 divisor = (BcBigDig) b->num[len - 1];
1017
1018 nonzero = bc_num_nonZeroDig(b->num, len - 1);
1019 }
1020 }
1021
1022 divisor += nonzero;
1023
1024 bc_num_expand(c, a->len);
Gavin Howard1d871c12019-06-19 21:20:07 -06001025 memset(c->num, 0, BC_NUM_SIZE(c->cap));
Gavin Howardb740ef72019-06-19 07:34:04 -06001026
Gavin Howardbd60ec72019-05-10 08:01:22 -06001027 assert(c->scale >= scale);
1028 rdx = c->rdx - BC_NUM_RDX(scale);
Gavin Howard61349662019-05-08 11:08:45 -06001029
Gavin Howardbd60ec72019-05-10 08:01:22 -06001030 bc_num_init(&cpb, len + 1);
Gavin Howardbd60ec72019-05-10 08:01:22 -06001031
Gavin Howardb740ef72019-06-19 07:34:04 -06001032 i = end - 1;
1033
1034 for (; BC_NO_SIG && i < end && i >= rdx && BC_NUM_NONZERO(a); --i) {
Gavin Howard61349662019-05-08 11:08:45 -06001035
Gavin Howard070a6ce2019-05-08 11:50:25 -06001036 ssize_t cmp;
Gavin Howardb740ef72019-06-19 07:34:04 -06001037 BcDig *n;
1038 BcBigDig q, result;
Gavin Howard070a6ce2019-05-08 11:50:25 -06001039
Gavin Howardd5b27c82019-05-09 12:13:07 -06001040 n = a->num + i;
Gavin Howardb740ef72019-06-19 07:34:04 -06001041 assert(n >= a->num);
1042 result = q = 0;
Gavin Howard070a6ce2019-05-08 11:50:25 -06001043
Stefan Esser94fb4802019-06-06 22:07:20 +02001044 cmp = bc_num_divCmp(n, b, len);
Gavin Howard3378af82019-05-11 08:29:39 -06001045
1046#if BC_ENABLE_SIGNALS
Gavin Howardb740ef72019-06-19 07:34:04 -06001047 if (BC_NUM_CMP_SIGNAL(cmp)) goto err;
Gavin Howard3378af82019-05-11 08:29:39 -06001048#endif // BC_ENABLE_SIGNALS
Gavin Howard070a6ce2019-05-08 11:50:25 -06001049
Gavin Howardb740ef72019-06-19 07:34:04 -06001050 while (cmp >= 0) {
Gavin Howardbd60ec72019-05-10 08:01:22 -06001051
Gavin Howardb740ef72019-06-19 07:34:04 -06001052 BcBigDig n1, dividend;
Gavin Howard1980e032019-05-08 17:21:17 -06001053
1054 n1 = (BcBigDig) n[len];
Gavin Howard92ba4e52019-05-10 20:49:13 -06001055 dividend = n1 * BC_BASE_POW + (BcBigDig) n[len - 1];
Gavin Howardb740ef72019-06-19 07:34:04 -06001056 q = (dividend / divisor);
Stefan Esser94fb4802019-06-06 22:07:20 +02001057
Gavin Howardb740ef72019-06-19 07:34:04 -06001058 if (q <= 1) {
1059 q = 1;
1060 s = bc_num_subArrays(n, b->num, len);
1061 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
1062 }
1063 else {
Stefan Esser94fb4802019-06-06 22:07:20 +02001064
Gavin Howardb740ef72019-06-19 07:34:04 -06001065 assert(q <= BC_BASE_POW);
Gavin Howard1980e032019-05-08 17:21:17 -06001066
Gavin Howardb740ef72019-06-19 07:34:04 -06001067 s = bc_num_mulArray(b, (BcBigDig) q, &cpb);
Gavin Howard1980e032019-05-08 17:21:17 -06001068 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
1069
Gavin Howardb740ef72019-06-19 07:34:04 -06001070 s = bc_num_subArrays(n, cpb.num, cpb.len);
1071 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
1072 }
Gavin Howard1980e032019-05-08 17:21:17 -06001073
Gavin Howardb740ef72019-06-19 07:34:04 -06001074 result += q;
1075 assert(result <= BC_BASE_POW);
Stefan Esser45edd422019-06-05 21:04:42 +02001076
Stefan Esser6cb9f4a2019-06-23 09:58:43 +02001077 if (nonzero) {
Gavin Howardb740ef72019-06-19 07:34:04 -06001078 cmp = bc_num_divCmp(n, b, len);
Stefan Esser94fb4802019-06-06 22:07:20 +02001079#if BC_ENABLE_SIGNALS
1080 if (BC_NUM_CMP_SIGNAL(cmp)) goto err;
1081#endif // BC_ENABLE_SIGNALS
1082 }
Gavin Howardb740ef72019-06-19 07:34:04 -06001083 else cmp = -1;
Gavin Howard1980e032019-05-08 17:21:17 -06001084 }
Stefan Esser595d84e2019-06-06 23:01:19 +02001085
Gavin Howardb740ef72019-06-19 07:34:04 -06001086 assert(result < BC_BASE_POW);
Gavin Howard1980e032019-05-08 17:21:17 -06001087
Gavin Howardb740ef72019-06-19 07:34:04 -06001088 c->num[i] = (BcDig) result;
Gavin Howard61349662019-05-08 11:08:45 -06001089 }
Stefan Esser94fb4802019-06-06 22:07:20 +02001090
Gavin Howard1980e032019-05-08 17:21:17 -06001091err:
Gavin Howardd5b27c82019-05-09 12:13:07 -06001092 if (BC_NO_ERR(!s) && BC_SIG) s = BC_STATUS_SIGNAL;
Gavin Howard66a93552019-05-09 17:13:59 -06001093 bc_num_free(&cpb);
Gavin Howardd5b27c82019-05-09 12:13:07 -06001094 return s;
1095}
1096
Gavin Howardd5b27c82019-05-09 12:13:07 -06001097static BcStatus bc_num_d(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale) {
1098
1099 BcStatus s = BC_STATUS_SUCCESS;
Gavin Howardfcb74462019-05-09 14:42:35 -06001100 size_t len;
1101 BcNum cpa, cpb;
Gavin Howardd5b27c82019-05-09 12:13:07 -06001102
1103 if (BC_NUM_ZERO(b)) return bc_vm_err(BC_ERROR_MATH_DIVIDE_BY_ZERO);
1104 if (BC_NUM_ZERO(a)) {
1105 bc_num_setToZero(c, scale);
1106 return BC_STATUS_SUCCESS;
1107 }
Gavin Howardb2f01a42019-05-10 20:00:32 -06001108 if (BC_NUM_ONE(b)) {
Gavin Howardd5b27c82019-05-09 12:13:07 -06001109 bc_num_copy(c, a);
1110 bc_num_retireMul(c, scale, a->neg, b->neg);
1111 return BC_STATUS_SUCCESS;
1112 }
1113 if (!a->rdx && !b->rdx && b->len == 1 && !scale) {
1114 BcBigDig rem;
1115 s = bc_num_divArray(a, (BcBigDig) b->num[0], c, &rem);
1116 bc_num_retireMul(c, scale, a->neg, b->neg);
1117 return s;
1118 }
1119
1120 len = bc_num_mulReq(a, b, scale);
1121 bc_num_init(&cpa, len);
1122 bc_num_copy(&cpa, a);
Gavin Howardfcb74462019-05-09 14:42:35 -06001123 bc_num_createCopy(&cpb, b);
Gavin Howardd5b27c82019-05-09 12:13:07 -06001124
Gavin Howard03e35dd2019-05-11 06:56:30 -06001125 len = b->len;
1126
Gavin Howardd5b27c82019-05-09 12:13:07 -06001127 if (len > cpa.len) {
1128 bc_num_expand(&cpa, bc_vm_growSize(len, 2));
Gavin Howard92ba4e52019-05-10 20:49:13 -06001129 bc_num_extend(&cpa, (len - cpa.len) * BC_BASE_DIGS);
Gavin Howardd5b27c82019-05-09 12:13:07 -06001130 }
1131
Gavin Howard92ba4e52019-05-10 20:49:13 -06001132 cpa.scale = cpa.rdx * BC_BASE_DIGS;
Gavin Howardd5b27c82019-05-09 12:13:07 -06001133
1134 bc_num_extend(&cpa, b->scale);
1135 cpa.rdx -= BC_NUM_RDX(b->scale);
Gavin Howard92ba4e52019-05-10 20:49:13 -06001136 cpa.scale = cpa.rdx * BC_BASE_DIGS;
Gavin Howard2c2dcb32019-05-23 21:58:06 -06001137
Gavin Howardbd60ec72019-05-10 08:01:22 -06001138 if (scale > cpa.scale) {
1139 bc_num_extend(&cpa, scale);
Gavin Howard92ba4e52019-05-10 20:49:13 -06001140 cpa.scale = cpa.rdx * BC_BASE_DIGS;
Gavin Howardbd60ec72019-05-10 08:01:22 -06001141 }
Gavin Howardd5b27c82019-05-09 12:13:07 -06001142
Gavin Howardd5b27c82019-05-09 12:13:07 -06001143 if (cpa.cap == cpa.len) bc_num_expand(&cpa, bc_vm_growSize(cpa.len, 1));
1144
1145 // We want an extra zero in front to make things simpler.
1146 cpa.num[cpa.len++] = 0;
1147
Gavin Howardfcb74462019-05-09 14:42:35 -06001148 if (cpa.rdx == cpa.len) cpa.len = bc_num_nonzeroLen(&cpa);
1149 if (cpb.rdx == cpb.len) cpb.len = bc_num_nonzeroLen(&cpb);
1150 cpb.scale = cpb.rdx = 0;
Gavin Howardd5b27c82019-05-09 12:13:07 -06001151
Gavin Howardbd60ec72019-05-10 08:01:22 -06001152 s = bc_num_d_long(&cpa, &cpb, c, scale);
Gavin Howardd5b27c82019-05-09 12:13:07 -06001153
1154 if (BC_NO_ERR(!s)) {
Gavin Howardd5b27c82019-05-09 12:13:07 -06001155 if (BC_SIG) s = BC_STATUS_SIGNAL;
Gavin Howard41fc5802019-06-23 09:25:52 -06001156 else bc_num_retireMul(c, scale, a->neg, b->neg);
Gavin Howardd5b27c82019-05-09 12:13:07 -06001157 }
1158
Gavin Howardfcb74462019-05-09 14:42:35 -06001159 bc_num_free(&cpb);
Gavin Howard1980e032019-05-08 17:21:17 -06001160 bc_num_free(&cpa);
Gavin Howardd5b27c82019-05-09 12:13:07 -06001161
Gavin Howard63738202018-09-26 15:34:20 -06001162 return s;
Gavin Howard6fbdb292018-02-27 15:44:48 -07001163}
1164
Gavin Howard1cbfe242019-01-09 17:13:11 -07001165static BcStatus bc_num_r(BcNum *a, BcNum *b, BcNum *restrict c,
1166 BcNum *restrict d, size_t scale, size_t ts)
Gavin Howard18441082018-10-22 10:03:30 -06001167{
Gavin Howard63738202018-09-26 15:34:20 -06001168 BcStatus s;
Gavin Howard18441082018-10-22 10:03:30 -06001169 BcNum temp;
1170 bool neg;
Gavin Howard6fbdb292018-02-27 15:44:48 -07001171
Gavin Howardddce6d42019-01-03 14:07:06 -07001172 if (BC_NUM_ZERO(b)) return bc_vm_err(BC_ERROR_MATH_DIVIDE_BY_ZERO);
1173 if (BC_NUM_ZERO(a)) {
Gavin Howard99ca4992019-01-02 13:48:49 -07001174 bc_num_setToZero(c, ts);
Gavin Howardf8ddb6d2018-10-22 12:58:53 -06001175 bc_num_setToZero(d, ts);
Gavin Howard63738202018-09-26 15:34:20 -06001176 return BC_STATUS_SUCCESS;
1177 }
Gavin Howard8b254872018-03-14 01:13:35 -06001178
Gavin Howardad0ecfe2018-10-30 01:16:01 -06001179 bc_num_init(&temp, d->cap);
Gavin Howard1769abc2019-02-21 09:36:14 -07001180 s = bc_num_d(a, b, c, scale);
1181 assert(!s || s == BC_STATUS_SIGNAL);
Gavin Howardc78e7522019-02-22 13:24:25 -07001182 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howard6fbdb292018-02-27 15:44:48 -07001183
Gavin Howard996fc222019-04-29 13:22:38 -06001184 if (scale) scale = ts + 1;
Gavin Howard3115c012018-10-04 10:53:56 -06001185
Gavin Howard53eba8b2018-10-31 15:14:37 -06001186 s = bc_num_m(c, b, &temp, scale);
Gavin Howardc78e7522019-02-22 13:24:25 -07001187 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howard53eba8b2018-10-31 15:14:37 -06001188 s = bc_num_sub(a, &temp, d, scale);
Gavin Howardc78e7522019-02-22 13:24:25 -07001189 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howard5d149cf2018-09-06 13:46:23 -06001190
Gavin Howard77445432019-04-26 15:59:33 -06001191 if (ts > d->scale && BC_NUM_NONZERO(d)) bc_num_extend(d, ts - d->scale);
Gavin Howard18441082018-10-22 10:03:30 -06001192
1193 neg = d->neg;
Gavin Howardad0ecfe2018-10-30 01:16:01 -06001194 bc_num_retireMul(d, ts, a->neg, b->neg);
Gavin Howard996fc222019-04-29 13:22:38 -06001195 d->neg = BC_NUM_NONZERO(d) ? neg : false;
Gavin Howard6fbdb292018-02-27 15:44:48 -07001196
1197err:
Gavin Howard18441082018-10-22 10:03:30 -06001198 bc_num_free(&temp);
1199 return s;
1200}
1201
Gavin Howard2d188a52019-02-25 14:19:08 -07001202static BcStatus bc_num_rem(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale)
1203{
Gavin Howard18441082018-10-22 10:03:30 -06001204 BcStatus s;
1205 BcNum c1;
Gavin Howard798508b2019-02-21 16:45:32 -07001206 size_t ts;
Gavin Howard18441082018-10-22 10:03:30 -06001207
Gavin Howard77445432019-04-26 15:59:33 -06001208 ts = bc_vm_growSize(scale, b->scale);
1209 ts = BC_MAX(ts, a->scale);
Gavin Howard798508b2019-02-21 16:45:32 -07001210
1211 bc_num_init(&c1, bc_num_mulReq(a, b, ts));
Gavin Howard54b946a2018-10-23 12:06:57 -06001212 s = bc_num_r(a, b, &c1, c, scale, ts);
Gavin Howard63738202018-09-26 15:34:20 -06001213 bc_num_free(&c1);
Gavin Howardd64ce7b2018-10-24 16:20:20 -06001214
Gavin Howard63738202018-09-26 15:34:20 -06001215 return s;
Gavin Howard6fbdb292018-02-27 15:44:48 -07001216}
1217
Gavin Howard1cbfe242019-01-09 17:13:11 -07001218static BcStatus bc_num_p(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale) {
Gavin Howard6fbdb292018-02-27 15:44:48 -07001219
Gavin Howardad0ecfe2018-10-30 01:16:01 -06001220 BcStatus s = BC_STATUS_SUCCESS;
Gavin Howard63738202018-09-26 15:34:20 -06001221 BcNum copy;
Gavin Howard2956a5e2019-05-08 08:04:06 -06001222 BcBigDig pow = 0;
Gavin Howard63738202018-09-26 15:34:20 -06001223 size_t i, powrdx, resrdx;
1224 bool neg, zero;
Gavin Howard6fbdb292018-02-27 15:44:48 -07001225
Gavin Howardecafd4f2019-02-23 09:30:45 -07001226 if (BC_ERR(b->rdx)) return bc_vm_err(BC_ERROR_MATH_NON_INTEGER);
Gavin Howard6fbdb292018-02-27 15:44:48 -07001227
Gavin Howardddce6d42019-01-03 14:07:06 -07001228 if (BC_NUM_ZERO(b)) {
Gavin Howard63738202018-09-26 15:34:20 -06001229 bc_num_one(c);
Gavin Howard61abdbe2018-10-22 13:05:38 -06001230 return BC_STATUS_SUCCESS;
Gavin Howard63738202018-09-26 15:34:20 -06001231 }
Gavin Howardddce6d42019-01-03 14:07:06 -07001232 if (BC_NUM_ZERO(a)) {
Gavin Howard7622bcd2019-06-07 21:41:41 -06001233 if (b->neg) return bc_vm_err(BC_ERROR_MATH_DIVIDE_BY_ZERO);
Gavin Howardf8ddb6d2018-10-22 12:58:53 -06001234 bc_num_setToZero(c, scale);
Gavin Howard63738202018-09-26 15:34:20 -06001235 return BC_STATUS_SUCCESS;
1236 }
Gavin Howardb2f01a42019-05-10 20:00:32 -06001237 if (BC_NUM_ONE(b)) {
Gavin Howardad0ecfe2018-10-30 01:16:01 -06001238 if (!b->neg) bc_num_copy(c, a);
Gavin Howard63738202018-09-26 15:34:20 -06001239 else s = bc_num_inv(a, c, scale);
Gavin Howard63738202018-09-26 15:34:20 -06001240 return s;
1241 }
Gavin Howard6fbdb292018-02-27 15:44:48 -07001242
Gavin Howard63738202018-09-26 15:34:20 -06001243 neg = b->neg;
1244 b->neg = false;
Gavin Howard2956a5e2019-05-08 08:04:06 -06001245 s = bc_num_bigdig(b, &pow);
Gavin Howardfb14efc2018-12-22 14:01:58 -07001246 b->neg = neg;
Gavin Howard53eba8b2018-10-31 15:14:37 -06001247 if (s) return s;
1248
Gavin Howardbaa4f582019-01-24 13:56:35 -07001249 bc_num_createCopy(&copy, a);
Gavin Howard6fbdb292018-02-27 15:44:48 -07001250
Gavin Howardeec07282019-05-10 20:10:18 -06001251 if (!neg) {
1252 size_t max = BC_MAX(scale, a->scale), scalepow = a->scale * pow;
1253 scale = BC_MIN(scalepow, max);
1254 }
Gavin Howardb29674f2018-03-22 22:24:58 -06001255
Gavin Howardd497c232019-04-29 10:32:38 -06001256 for (powrdx = a->scale; BC_NO_SIG && !(pow & 1); pow >>= 1) {
Gavin Howard63738202018-09-26 15:34:20 -06001257 powrdx <<= 1;
Gavin Howard53eba8b2018-10-31 15:14:37 -06001258 s = bc_num_mul(&copy, &copy, &copy, powrdx);
Gavin Howardc78e7522019-02-22 13:24:25 -07001259 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howard63738202018-09-26 15:34:20 -06001260 }
Gavin Howard6fb635f2018-03-03 12:45:42 -07001261
Gavin Howard2d188a52019-02-25 14:19:08 -07001262 if (BC_SIG) goto sig_err;
Gavin Howard6fb635f2018-03-03 12:45:42 -07001263
Gavin Howardad0ecfe2018-10-30 01:16:01 -06001264 bc_num_copy(c, &copy);
Gavin Howard1ab22d22019-01-03 13:32:17 -07001265 resrdx = powrdx;
Gavin Howardad0ecfe2018-10-30 01:16:01 -06001266
Gavin Howard2d188a52019-02-25 14:19:08 -07001267 while (BC_NO_SIG && (pow >>= 1)) {
Gavin Howard53eba8b2018-10-31 15:14:37 -06001268
Gavin Howard4d5daba2019-01-07 14:42:50 -07001269 powrdx <<= 1;
Gavin Howard53eba8b2018-10-31 15:14:37 -06001270 s = bc_num_mul(&copy, &copy, &copy, powrdx);
Gavin Howardc78e7522019-02-22 13:24:25 -07001271 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howard53eba8b2018-10-31 15:14:37 -06001272
Gavin Howard63738202018-09-26 15:34:20 -06001273 if (pow & 1) {
1274 resrdx += powrdx;
Gavin Howard53eba8b2018-10-31 15:14:37 -06001275 s = bc_num_mul(c, &copy, c, resrdx);
Gavin Howardc78e7522019-02-22 13:24:25 -07001276 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howard63738202018-09-26 15:34:20 -06001277 }
1278 }
Gavin Howard6fbdb292018-02-27 15:44:48 -07001279
Gavin Howard2d188a52019-02-25 14:19:08 -07001280 if (BC_SIG) goto sig_err;
Gavin Howard9b801632019-02-16 23:34:06 -07001281 if (neg) {
1282 s = bc_num_inv(c, c, scale);
Gavin Howardc78e7522019-02-22 13:24:25 -07001283 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howard9b801632019-02-16 23:34:06 -07001284 }
1285
Stefan Eßerb5318342019-04-29 10:31:26 -06001286 if (c->scale > scale) bc_num_truncate(c, c->scale - scale);
Gavin Howard1d959152018-03-03 23:33:13 -07001287
Gavin Howard53eba8b2018-10-31 15:14:37 -06001288 // We can't use bc_num_clean() here.
Gavin Howard63738202018-09-26 15:34:20 -06001289 for (zero = true, i = 0; zero && i < c->len; ++i) zero = !c->num[i];
Gavin Howardf8ddb6d2018-10-22 12:58:53 -06001290 if (zero) bc_num_setToZero(c, scale);
Gavin Howard1d959152018-03-03 23:33:13 -07001291
Gavin Howard9b801632019-02-16 23:34:06 -07001292sig_err:
Gavin Howard2d188a52019-02-25 14:19:08 -07001293 if (BC_NO_ERR(!s) && BC_SIG) s = BC_STATUS_SIGNAL;
Gavin Howard6fbdb292018-02-27 15:44:48 -07001294err:
Gavin Howard63738202018-09-26 15:34:20 -06001295 bc_num_free(&copy);
1296 return s;
Gavin Howard6fbdb292018-02-27 15:44:48 -07001297}
1298
Gavin Howard7bda4782018-12-28 09:53:22 -07001299#if BC_ENABLE_EXTRA_MATH
Gavin Howard2d188a52019-02-25 14:19:08 -07001300static BcStatus bc_num_place(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 Howardc9bef2d2019-04-26 14:46:52 -06001311 if (val < c->scale) bc_num_truncate(c, c->scale - val);
1312 else if (val > c->scale) bc_num_extend(c, val - c->scale);
Gavin Howardac4d9122018-12-27 23:59:12 -07001313
1314 return s;
1315}
1316
Gavin Howard2d188a52019-02-25 14:19:08 -07001317static BcStatus bc_num_left(BcNum *a, BcNum *b, BcNum *restrict c, size_t scale)
1318{
Gavin Howardac4d9122018-12-27 23:59:12 -07001319 BcStatus s = BC_STATUS_SUCCESS;
Gavin Howard2956a5e2019-05-08 08:04:06 -06001320 BcBigDig val = 0;
Gavin Howardac4d9122018-12-27 23:59:12 -07001321
1322 BC_UNUSED(scale);
1323
1324 s = bc_num_intop(a, b, c, &val);
Gavin Howardecafd4f2019-02-23 09:30:45 -07001325 if (BC_ERR(s)) return s;
Gavin Howardac4d9122018-12-27 23:59:12 -07001326
Gavin Howardb1558662019-04-26 14:32:55 -06001327 return bc_num_shiftLeft(c, (size_t) val);
Gavin Howardac4d9122018-12-27 23:59:12 -07001328}
1329
Gavin Howard2d188a52019-02-25 14:19:08 -07001330static BcStatus bc_num_right(BcNum *a, BcNum *b, BcNum *restrict c,
1331 size_t scale)
1332{
Gavin Howardac4d9122018-12-27 23:59:12 -07001333 BcStatus s = BC_STATUS_SUCCESS;
Gavin Howard2956a5e2019-05-08 08:04:06 -06001334 BcBigDig val = 0;
Gavin Howardac4d9122018-12-27 23:59:12 -07001335
1336 BC_UNUSED(scale);
1337
1338 s = bc_num_intop(a, b, c, &val);
Gavin Howardecafd4f2019-02-23 09:30:45 -07001339 if (BC_ERR(s)) return s;
Gavin Howardac4d9122018-12-27 23:59:12 -07001340
Gavin Howard2a366922019-01-16 10:50:20 -07001341 if (BC_NUM_ZERO(c)) return s;
1342
Gavin Howardb1558662019-04-26 14:32:55 -06001343 return bc_num_shiftRight(c, (size_t) val);
Gavin Howardac4d9122018-12-27 23:59:12 -07001344}
Gavin Howard7bda4782018-12-28 09:53:22 -07001345#endif // BC_ENABLE_EXTRA_MATH
Gavin Howardac4d9122018-12-27 23:59:12 -07001346
Gavin Howard1cbfe242019-01-09 17:13:11 -07001347static BcStatus bc_num_binary(BcNum *a, BcNum *b, BcNum *c, size_t scale,
1348 BcNumBinaryOp op, size_t req)
Gavin Howard6fbdb292018-02-27 15:44:48 -07001349{
Gavin Howarda1c44392018-09-27 12:41:15 -06001350 BcStatus s;
Gavin Howard63738202018-09-26 15:34:20 -06001351 BcNum num2, *ptr_a, *ptr_b;
1352 bool init = false;
Gavin Howard6fbdb292018-02-27 15:44:48 -07001353
Gavin Howardfe9a3022019-06-21 20:40:45 -06001354 assert(a != NULL && b != NULL && c != NULL && op != NULL);
Gavin Howard6fbdb292018-02-27 15:44:48 -07001355
Gavin Howard890d0c02018-10-30 16:34:50 -06001356 if (c == a) {
Gavin Howard63738202018-09-26 15:34:20 -06001357 ptr_a = &num2;
Gavin Howardba009802018-09-29 04:41:51 -06001358 memcpy(ptr_a, c, sizeof(BcNum));
Gavin Howard890d0c02018-10-30 16:34:50 -06001359 init = true;
Gavin Howard63738202018-09-26 15:34:20 -06001360 }
1361 else ptr_a = a;
Gavin Howard6fbdb292018-02-27 15:44:48 -07001362
Gavin Howard63738202018-09-26 15:34:20 -06001363 if (c == b) {
Gavin Howardba009802018-09-29 04:41:51 -06001364 ptr_b = &num2;
Gavin Howardba009802018-09-29 04:41:51 -06001365 if (c != a) {
1366 memcpy(ptr_b, c, sizeof(BcNum));
Gavin Howard63738202018-09-26 15:34:20 -06001367 init = true;
1368 }
1369 }
1370 else ptr_b = b;
Gavin Howard6fbdb292018-02-27 15:44:48 -07001371
Gavin Howardad0ecfe2018-10-30 01:16:01 -06001372 if (init) bc_num_init(c, req);
1373 else bc_num_expand(c, req);
Gavin Howard6fbdb292018-02-27 15:44:48 -07001374
Gavin Howardad0ecfe2018-10-30 01:16:01 -06001375 s = op(ptr_a, ptr_b, c, scale);
Gavin Howard6fbdb292018-02-27 15:44:48 -07001376
Gavin Howardddce6d42019-01-03 14:07:06 -07001377 assert(!c->neg || BC_NUM_NONZERO(c));
Gavin Howard87c29c72019-03-28 11:22:51 -06001378 assert(c->rdx <= c->len || !c->len || s);
Gavin Howarda2653e62019-06-19 22:02:27 -06001379 assert(!c->len || c->num[c->len - 1] || c->rdx == c->len);
Gavin Howard3cf769e2018-10-11 13:55:11 -06001380
Gavin Howardba009802018-09-29 04:41:51 -06001381 if (init) bc_num_free(&num2);
Gavin Howardad0ecfe2018-10-30 01:16:01 -06001382
Gavin Howardee9d01e2019-02-16 22:48:11 -07001383 return s;
Gavin Howard6fbdb292018-02-27 15:44:48 -07001384}
1385
Gavin Howardc90ed902019-01-02 13:07:49 -07001386#ifndef NDEBUG
Gavin Howard1cbfe242019-01-09 17:13:11 -07001387static bool bc_num_strValid(const char *val) {
Gavin Howard6fbdb292018-02-27 15:44:48 -07001388
Gavin Howardc7d655b2018-12-28 19:45:13 -07001389 bool radix = false;
Gavin Howard63738202018-09-26 15:34:20 -06001390 size_t i, len = strlen(val);
Gavin Howard6fbdb292018-02-27 15:44:48 -07001391
Gavin Howard63738202018-09-26 15:34:20 -06001392 if (!len) return true;
Gavin Howard6fbdb292018-02-27 15:44:48 -07001393
Gavin Howard63738202018-09-26 15:34:20 -06001394 for (i = 0; i < len; ++i) {
Gavin Howard6fbdb292018-02-27 15:44:48 -07001395
Gavin Howard8a921bd2018-10-11 14:15:32 -06001396 BcDig c = val[i];
Gavin Howardf6e3fb32018-08-09 13:48:59 -06001397
Gavin Howard63738202018-09-26 15:34:20 -06001398 if (c == '.') {
Gavin Howard6fbdb292018-02-27 15:44:48 -07001399
Gavin Howard63738202018-09-26 15:34:20 -06001400 if (radix) return false;
Gavin Howard6fbdb292018-02-27 15:44:48 -07001401
Gavin Howard63738202018-09-26 15:34:20 -06001402 radix = true;
1403 continue;
1404 }
Gavin Howard6fbdb292018-02-27 15:44:48 -07001405
Gavin Howardc7d655b2018-12-28 19:45:13 -07001406 if (!(isdigit(c) || isupper(c))) return false;
Gavin Howard63738202018-09-26 15:34:20 -06001407 }
Gavin Howard6fbdb292018-02-27 15:44:48 -07001408
Gavin Howard63738202018-09-26 15:34:20 -06001409 return true;
Gavin Howard6fbdb292018-02-27 15:44:48 -07001410}
Gavin Howardc90ed902019-01-02 13:07:49 -07001411#endif // NDEBUG
Gavin Howard6fbdb292018-02-27 15:44:48 -07001412
Gavin Howard2956a5e2019-05-08 08:04:06 -06001413static BcBigDig bc_num_parseChar(char c, size_t base_t) {
Gavin Howardc7d655b2018-12-28 19:45:13 -07001414
1415 if (isupper(c)) {
1416 c = BC_NUM_NUM_LETTER(c);
Gavin Howard6193aaf2019-01-03 16:44:04 -07001417 c = ((size_t) c) >= base_t ? (char) base_t - 1 : c;
Gavin Howardc7d655b2018-12-28 19:45:13 -07001418 }
1419 else c -= '0';
1420
Gavin Howard2956a5e2019-05-08 08:04:06 -06001421 return (BcBigDig) (uchar) c;
Gavin Howardc7d655b2018-12-28 19:45:13 -07001422}
Gavin Howarde212d542019-06-21 19:25:40 -06001423
Gavin Howard1cbfe242019-01-09 17:13:11 -07001424static void bc_num_parseDecimal(BcNum *restrict n, const char *restrict val) {
Gavin Howard6fbdb292018-02-27 15:44:48 -07001425
Gavin Howard3b60abb2019-04-26 08:49:17 -06001426 size_t len, i, temp, mod;
Gavin Howard63738202018-09-26 15:34:20 -06001427 const char *ptr;
Gavin Howardd43ec372019-04-25 21:13:54 -06001428 bool zero = true, rdx;
Stefan Essera299ffc2019-04-24 23:28:32 +02001429
Gavin Howard63738202018-09-26 15:34:20 -06001430 for (i = 0; val[i] == '0'; ++i);
Gavin Howard6fbdb292018-02-27 15:44:48 -07001431
Gavin Howard63738202018-09-26 15:34:20 -06001432 val += i;
Gavin Howard46a7d8d2019-05-06 15:16:12 -06001433 assert(!val[0] || isalnum(val[0]) || val[0] == '.');
1434
1435 // All 0's. We can just return, since this
1436 // procedure expects a virgin (already 0) BcNum.
1437 if (!val[0]) return;
1438
Gavin Howard63738202018-09-26 15:34:20 -06001439 len = strlen(val);
Gavin Howard6fbdb292018-02-27 15:44:48 -07001440
Gavin Howard63738202018-09-26 15:34:20 -06001441 ptr = strchr(val, '.');
Gavin Howardd43ec372019-04-25 21:13:54 -06001442 rdx = (ptr != NULL);
Gavin Howard6fbdb292018-02-27 15:44:48 -07001443
Gavin Howardd43ec372019-04-25 21:13:54 -06001444 for (i = 0; i < len && (zero = (val[i] == '0' || val[i] == '.')); ++i);
Gavin Howard6fbdb292018-02-27 15:44:48 -07001445
Gavin Howardd43ec372019-04-25 21:13:54 -06001446 n->scale = (size_t) (rdx * ((val + len) - (ptr + 1)));
Gavin Howard1c1697c2019-04-26 10:07:45 -06001447 n->rdx = BC_NUM_RDX(n->scale);
Gavin Howardd43ec372019-04-25 21:13:54 -06001448
Gavin Howard3b60abb2019-04-26 08:49:17 -06001449 i = len - (ptr == val ? 0 : i) - rdx;
Gavin Howard48a0e472019-04-26 15:39:46 -06001450 temp = BC_NUM_ROUND_POW(i);
Gavin Howard92ba4e52019-05-10 20:49:13 -06001451 mod = n->scale % BC_BASE_DIGS;
1452 i = mod ? BC_BASE_DIGS - mod : 0;
1453 n->len = ((temp + i) / BC_BASE_DIGS);
Gavin Howardd43ec372019-04-25 21:13:54 -06001454
1455 bc_num_expand(n, n->len);
Gavin Howard404ece72019-04-26 15:59:15 -06001456 memset(n->num, 0, BC_NUM_SIZE(n->len));
Gavin Howard6fbdb292018-02-27 15:44:48 -07001457
Gavin Howard6076dbb2019-04-26 09:47:50 -06001458 if (zero) n->len = n->rdx = 0;
1459 else {
Gavin Howardfcf29012019-04-25 20:12:19 -06001460
Gavin Howard2956a5e2019-05-08 08:04:06 -06001461 BcBigDig exp, pow;
Gavin Howardd43ec372019-04-25 21:13:54 -06001462
Gavin Howard687cf4d2019-05-11 08:55:36 -06001463 assert(i <= BC_NUM_BIGDIG_MAX);
1464
1465 exp = (BcBigDig) i;
Gavin Howardc1542802019-05-08 17:20:51 -06001466 pow = bc_num_pow10[exp];
Gavin Howardd43ec372019-04-25 21:13:54 -06001467
1468 for (i = len - 1; i < len; --i, ++exp) {
Gavin Howard8dd307e2019-01-08 23:05:19 -07001469
Gavin Howardc7d655b2018-12-28 19:45:13 -07001470 char c = val[i];
Gavin Howard8dd307e2019-01-08 23:05:19 -07001471
Gavin Howardd43ec372019-04-25 21:13:54 -06001472 if (c == '.') exp -= 1;
Gavin Howard8dd307e2019-01-08 23:05:19 -07001473 else {
Gavin Howardd43ec372019-04-25 21:13:54 -06001474
Gavin Howard92ba4e52019-05-10 20:49:13 -06001475 size_t idx = exp / BC_BASE_DIGS;
Gavin Howardd43ec372019-04-25 21:13:54 -06001476
Gavin Howard8dd307e2019-01-08 23:05:19 -07001477 if (isupper(c)) c = '9';
Gavin Howard2956a5e2019-05-08 08:04:06 -06001478 n->num[idx] += (((BcBigDig) c) - '0') * pow;
Gavin Howardd43ec372019-04-25 21:13:54 -06001479
Gavin Howard92ba4e52019-05-10 20:49:13 -06001480 if ((exp + 1) % BC_BASE_DIGS == 0) pow = 1;
Gavin Howardd43ec372019-04-25 21:13:54 -06001481 else pow *= BC_BASE;
Gavin Howard8dd307e2019-01-08 23:05:19 -07001482 }
Gavin Howardc7d655b2018-12-28 19:45:13 -07001483 }
Gavin Howardad0ecfe2018-10-30 01:16:01 -06001484 }
Gavin Howard6fbdb292018-02-27 15:44:48 -07001485}
1486
Gavin Howarddc0ee9e2019-02-16 23:27:08 -07001487static BcStatus bc_num_parseBase(BcNum *restrict n, const char *restrict val,
Gavin Howard2956a5e2019-05-08 08:04:06 -06001488 BcBigDig base)
Gavin Howard1cbfe242019-01-09 17:13:11 -07001489{
1490 BcStatus s = BC_STATUS_SUCCESS;
Gavin Howard4c605692019-05-08 09:09:17 -06001491 BcNum temp, mult1, mult2, result1, result2, *m1, *m2, *ptr;
Gavin Howard77445432019-04-26 15:59:33 -06001492 char c = 0;
Gavin Howard11b9afd2018-10-18 14:23:28 -06001493 bool zero = true;
Gavin Howard2956a5e2019-05-08 08:04:06 -06001494 BcBigDig v;
Gavin Howard1769abc2019-02-21 09:36:14 -07001495 size_t i, digs, len = strlen(val);
Gavin Howardede51f02018-03-02 12:30:00 -07001496
Gavin Howard11b9afd2018-10-18 14:23:28 -06001497 for (i = 0; zero && i < len; ++i) zero = (val[i] == '.' || val[i] == '0');
Gavin Howarddc0ee9e2019-02-16 23:27:08 -07001498 if (zero) return BC_STATUS_SUCCESS;
Gavin Howardede51f02018-03-02 12:30:00 -07001499
Gavin Howard2956a5e2019-05-08 08:04:06 -06001500 bc_num_init(&temp, BC_NUM_BIGDIG_LOG10);
Gavin Howard4c605692019-05-08 09:09:17 -06001501 bc_num_init(&mult1, BC_NUM_BIGDIG_LOG10);
Gavin Howardede51f02018-03-02 12:30:00 -07001502
Gavin Howardc7d655b2018-12-28 19:45:13 -07001503 for (i = 0; i < len && (c = val[i]) && c != '.'; ++i) {
Gavin Howard9b801632019-02-16 23:34:06 -07001504
Gavin Howardb3f1ee22019-05-07 21:49:15 -06001505 v = bc_num_parseChar(c, base);
Gavin Howard9b801632019-02-16 23:34:06 -07001506
Gavin Howard4c605692019-05-08 09:09:17 -06001507 s = bc_num_mulArray(n, base, &mult1);
Gavin Howardc78e7522019-02-22 13:24:25 -07001508 if (BC_ERROR_SIGNAL_ONLY(s)) goto int_err;
Gavin Howard2956a5e2019-05-08 08:04:06 -06001509 bc_num_bigdig2num(&temp, v);
Gavin Howard4c605692019-05-08 09:09:17 -06001510 s = bc_num_add(&mult1, &temp, n, 0);
Gavin Howardc78e7522019-02-22 13:24:25 -07001511 if (BC_ERROR_SIGNAL_ONLY(s)) goto int_err;
Gavin Howard63738202018-09-26 15:34:20 -06001512 }
Gavin Howardede51f02018-03-02 12:30:00 -07001513
Gavin Howarddc0ee9e2019-02-16 23:27:08 -07001514 if (i == len && !(c = val[i])) goto int_err;
Gavin Howard53eba8b2018-10-31 15:14:37 -06001515
Gavin Howard63738202018-09-26 15:34:20 -06001516 assert(c == '.');
Gavin Howard4c605692019-05-08 09:09:17 -06001517 bc_num_init(&mult2, BC_NUM_BIGDIG_LOG10);
1518 bc_num_init(&result1, BC_NUM_DEF_SIZE);
1519 bc_num_init(&result2, BC_NUM_DEF_SIZE);
1520 bc_num_one(&mult1);
1521
1522 m1 = &mult1;
1523 m2 = &mult2;
Gavin Howardede51f02018-03-02 12:30:00 -07001524
Gavin Howard8722e832019-05-08 11:12:16 -06001525 for (i += 1, digs = 0; BC_NO_SIG && i < len && (c = val[i]); ++i, ++digs) {
1526
Gavin Howardb3f1ee22019-05-07 21:49:15 -06001527 v = bc_num_parseChar(c, base);
Gavin Howard9b801632019-02-16 23:34:06 -07001528
Gavin Howard4c605692019-05-08 09:09:17 -06001529 s = bc_num_mulArray(&result1, base, &result2);
Gavin Howardc78e7522019-02-22 13:24:25 -07001530 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howarddc0ee9e2019-02-16 23:27:08 -07001531
Gavin Howard2956a5e2019-05-08 08:04:06 -06001532 bc_num_bigdig2num(&temp, v);
Gavin Howard4c605692019-05-08 09:09:17 -06001533 s = bc_num_add(&result2, &temp, &result1, 0);
Gavin Howardc78e7522019-02-22 13:24:25 -07001534 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howard4c605692019-05-08 09:09:17 -06001535 s = bc_num_mulArray(m1, base, m2);
Gavin Howardc78e7522019-02-22 13:24:25 -07001536 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howard4c605692019-05-08 09:09:17 -06001537
Gavin Howard0737af72019-06-19 22:12:26 -06001538 if (m2->len < m2->rdx) m2->len = m2->rdx;
1539
Gavin Howard4c605692019-05-08 09:09:17 -06001540 ptr = m1;
1541 m1 = m2;
1542 m2 = ptr;
Gavin Howard63738202018-09-26 15:34:20 -06001543 }
Gavin Howardede51f02018-03-02 12:30:00 -07001544
Gavin Howardf2ee6342019-04-09 17:05:20 -06001545 if (BC_SIG) {
1546 s = BC_STATUS_SIGNAL;
1547 goto err;
1548 }
1549
Gavin Howard1769abc2019-02-21 09:36:14 -07001550 // This one cannot be a divide by 0 because mult starts out at 1, then is
1551 // multiplied by base, and base cannot be 0, so mult cannot be 0.
Gavin Howard4c605692019-05-08 09:09:17 -06001552 s = bc_num_div(&result1, m1, &result2, digs * 2);
Gavin Howard1769abc2019-02-21 09:36:14 -07001553 assert(!s || s == BC_STATUS_SIGNAL);
Gavin Howardc78e7522019-02-22 13:24:25 -07001554 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howard4c605692019-05-08 09:09:17 -06001555 bc_num_truncate(&result2, digs);
1556 s = bc_num_add(n, &result2, n, digs);
Gavin Howardc78e7522019-02-22 13:24:25 -07001557 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howardd1417682019-02-15 17:08:42 -07001558
Gavin Howardddce6d42019-01-03 14:07:06 -07001559 if (BC_NUM_NONZERO(n)) {
Gavin Howardb0642be2019-04-30 21:12:22 -06001560 if (n->scale < digs) bc_num_extend(n, digs - n->scale);
Gavin Howard63738202018-09-26 15:34:20 -06001561 }
1562 else bc_num_zero(n);
Gavin Howardede51f02018-03-02 12:30:00 -07001563
Gavin Howardd1417682019-02-15 17:08:42 -07001564err:
Gavin Howard4c605692019-05-08 09:09:17 -06001565 bc_num_free(&result2);
1566 bc_num_free(&result1);
1567 bc_num_free(&mult2);
Gavin Howarddc0ee9e2019-02-16 23:27:08 -07001568int_err:
Gavin Howard4c605692019-05-08 09:09:17 -06001569 bc_num_free(&mult1);
Gavin Howard63738202018-09-26 15:34:20 -06001570 bc_num_free(&temp);
Gavin Howarddc0ee9e2019-02-16 23:27:08 -07001571 return s;
Gavin Howard6fbdb292018-02-27 15:44:48 -07001572}
1573
Gavin Howard549fd022019-06-14 18:53:49 -06001574static void bc_num_printNewline(void) {
Gavin Howard6193aaf2019-01-03 16:44:04 -07001575 if (vm->nchars >= (size_t) (vm->line_len - 1)) {
Gavin Howard48af52e2018-10-30 14:47:38 -06001576 bc_vm_putchar('\\');
1577 bc_vm_putchar('\n');
Gavin Howard63738202018-09-26 15:34:20 -06001578 }
Gavin Howard80977b22018-09-06 14:29:13 -06001579}
1580
Gavin Howardb4a9cdb2019-05-07 15:25:51 -06001581static void bc_num_putchar(int c) {
1582 if (c != '\n') bc_num_printNewline();
1583 bc_vm_putchar(c);
1584}
1585
Gavin Howard40a085f2018-12-03 12:08:59 -07001586#if DC_ENABLED
Gavin Howard1cbfe242019-01-09 17:13:11 -07001587static void bc_num_printChar(size_t n, size_t len, bool rdx) {
Gavin Howard3c02ed32018-12-28 18:17:15 -07001588 BC_UNUSED(rdx);
Gavin Howardeedfb2c2019-05-07 08:00:13 -06001589 BC_UNUSED(len);
1590 assert(len == 1);
Gavin Howard3c02ed32018-12-28 18:17:15 -07001591 bc_vm_putchar((uchar) n);
Gavin Howard83eb8392018-10-09 01:21:19 -06001592}
1593#endif // DC_ENABLED
1594
Gavin Howard1cbfe242019-01-09 17:13:11 -07001595static void bc_num_printDigits(size_t n, size_t len, bool rdx) {
1596
Gavin Howarda84ad992018-12-03 19:11:06 -07001597 size_t exp, pow;
Gavin Howard80977b22018-09-06 14:29:13 -06001598
Gavin Howardb4a9cdb2019-05-07 15:25:51 -06001599 bc_num_putchar(rdx ? '.' : ' ');
Gavin Howard2ed2bfb2018-03-14 02:42:32 -06001600
Gavin Howard530e3072019-04-23 16:23:36 -06001601 for (exp = 0, pow = 1; exp < len - 1; ++exp, pow *= BC_BASE);
Gavin Howard2ed2bfb2018-03-14 02:42:32 -06001602
Gavin Howardeedfb2c2019-05-07 08:00:13 -06001603 for (exp = 0; exp < len; pow /= BC_BASE, ++exp) {
Gavin Howard4b075f22019-05-07 15:27:53 -06001604 size_t dig = n / pow;
Gavin Howard3c02ed32018-12-28 18:17:15 -07001605 n -= dig * pow;
Gavin Howardb4a9cdb2019-05-07 15:25:51 -06001606 bc_num_putchar(((uchar) dig) + '0');
Gavin Howard63738202018-09-26 15:34:20 -06001607 }
Gavin Howard6fbdb292018-02-27 15:44:48 -07001608}
Gavin Howardfe679f02018-02-14 15:50:09 -07001609
Gavin Howard1cbfe242019-01-09 17:13:11 -07001610static void bc_num_printHex(size_t n, size_t len, bool rdx) {
Gavin Howard70e3d602018-12-11 11:00:49 -07001611
Gavin Howardeedfb2c2019-05-07 08:00:13 -06001612 BC_UNUSED(len);
1613
Gavin Howard3c02ed32018-12-28 18:17:15 -07001614 assert(len == 1);
Gavin Howard80977b22018-09-06 14:29:13 -06001615
Gavin Howardb4a9cdb2019-05-07 15:25:51 -06001616 if (rdx) bc_num_putchar('.');
Gavin Howard2682a1f2018-03-03 09:09:13 -07001617
Gavin Howardb4a9cdb2019-05-07 15:25:51 -06001618 bc_num_putchar(bc_num_hex_digits[n]);
Gavin Howard2682a1f2018-03-03 09:09:13 -07001619}
1620
Gavin Howard1cbfe242019-01-09 17:13:11 -07001621static void bc_num_printDecimal(const BcNum *restrict n) {
Gavin Howard32f2beb2018-03-09 11:43:20 -07001622
Gavin Howard51cd7ea2019-04-26 09:48:12 -06001623 size_t i, j, rdx = n->rdx;
1624 bool zero = true;
Gavin Howard92ba4e52019-05-10 20:49:13 -06001625 size_t buffer[BC_BASE_DIGS];
Gavin Howard32f2beb2018-03-09 11:43:20 -07001626
Gavin Howardb4a9cdb2019-05-07 15:25:51 -06001627 if (n->neg) bc_num_putchar('-');
Gavin Howard32f2beb2018-03-09 11:43:20 -07001628
Gavin Howard51cd7ea2019-04-26 09:48:12 -06001629 for (i = n->len - 1; i < n->len; --i) {
1630
1631 BcDig n9 = n->num[i];
1632 size_t temp;
1633 bool irdx = (i == rdx - 1);
1634
1635 zero = (zero & !irdx);
Gavin Howard92ba4e52019-05-10 20:49:13 -06001636 temp = n->scale % BC_BASE_DIGS;
1637 temp = i || !temp ? 0 : BC_BASE_DIGS - temp;
Gavin Howard51cd7ea2019-04-26 09:48:12 -06001638
Gavin Howard92ba4e52019-05-10 20:49:13 -06001639 memset(buffer, 0, BC_BASE_DIGS * sizeof(size_t));
Gavin Howard51cd7ea2019-04-26 09:48:12 -06001640
Gavin Howard92ba4e52019-05-10 20:49:13 -06001641 for (j = 0; n9 && j < BC_BASE_DIGS; ++j) {
Stefan Essera299ffc2019-04-24 23:28:32 +02001642 buffer[j] = n9 % BC_BASE;
1643 n9 /= BC_BASE;
1644 }
Stefan Esser0da17752019-04-25 12:25:15 +02001645
Gavin Howard92ba4e52019-05-10 20:49:13 -06001646 for (j = BC_BASE_DIGS - 1; j < BC_BASE_DIGS && j >= temp; --j) {
1647 bool print_rdx = (irdx & (j == BC_BASE_DIGS - 1));
Gavin Howard51cd7ea2019-04-26 09:48:12 -06001648 zero = (zero && buffer[j] == 0);
1649 if (!zero) bc_num_printHex(buffer[j], 1, print_rdx);
Stefan Essera299ffc2019-04-24 23:28:32 +02001650 }
1651 }
Gavin Howard32f2beb2018-03-09 11:43:20 -07001652}
1653
Gavin Howard7ad5a662019-02-19 14:40:46 -07001654#if BC_ENABLE_EXTRA_MATH
Gavin Howardb1558662019-04-26 14:32:55 -06001655static BcStatus bc_num_printExponent(const BcNum *restrict n, bool eng) {
Gavin Howard7ad5a662019-02-19 14:40:46 -07001656
Gavin Howardb1558662019-04-26 14:32:55 -06001657 BcStatus s = BC_STATUS_SUCCESS;
Gavin Howard7ad5a662019-02-19 14:40:46 -07001658 bool neg = (n->len <= n->rdx);
1659 BcNum temp, exp;
1660 size_t places, mod;
Gavin Howard2956a5e2019-05-08 08:04:06 -06001661 BcDig digs[BC_NUM_BIGDIG_LOG10];
Gavin Howard7ad5a662019-02-19 14:40:46 -07001662
1663 bc_num_createCopy(&temp, n);
1664
1665 if (neg) {
Gavin Howardc1349922019-04-30 20:48:09 -06001666
1667 size_t i, idx = bc_num_nonzeroLen(n) - 1;
1668
1669 places = 1;
1670
Gavin Howard92ba4e52019-05-10 20:49:13 -06001671 for (i = BC_BASE_DIGS - 1; i < BC_BASE_DIGS; --i) {
Gavin Howardc1542802019-05-08 17:20:51 -06001672 if (bc_num_pow10[i] > (BcBigDig) n->num[idx]) places += 1;
Gavin Howardc1349922019-04-30 20:48:09 -06001673 else break;
1674 }
1675
Gavin Howard92ba4e52019-05-10 20:49:13 -06001676 places += (n->rdx - (idx + 1)) * BC_BASE_DIGS;
Gavin Howard7ad5a662019-02-19 14:40:46 -07001677 mod = places % 3;
Gavin Howardc1349922019-04-30 20:48:09 -06001678
Gavin Howard7ad5a662019-02-19 14:40:46 -07001679 if (eng && mod != 0) places += 3 - mod;
Gavin Howardb1558662019-04-26 14:32:55 -06001680 s = bc_num_shiftLeft(&temp, places);
1681 if (BC_ERROR_SIGNAL_ONLY(s)) goto exit;
Gavin Howard7ad5a662019-02-19 14:40:46 -07001682 }
1683 else {
Gavin Howard06fee0c2019-05-09 14:43:20 -06001684 places = bc_num_intDigits(n) - 1;
Gavin Howard7ad5a662019-02-19 14:40:46 -07001685 mod = places % 3;
1686 if (eng && mod != 0) places -= 3 - (3 - mod);
Gavin Howardb1558662019-04-26 14:32:55 -06001687 s = bc_num_shiftRight(&temp, places);
1688 if (BC_ERROR_SIGNAL_ONLY(s)) goto exit;
Gavin Howard7ad5a662019-02-19 14:40:46 -07001689 }
1690
1691 bc_num_printDecimal(&temp);
Gavin Howardb4a9cdb2019-05-07 15:25:51 -06001692 bc_num_putchar('e');
Gavin Howard7ad5a662019-02-19 14:40:46 -07001693
1694 if (!places) {
1695 bc_num_printHex(0, 1, false);
1696 goto exit;
1697 }
1698
Gavin Howardb4a9cdb2019-05-07 15:25:51 -06001699 if (neg) bc_num_putchar('-');
Gavin Howard7ad5a662019-02-19 14:40:46 -07001700
Gavin Howard2956a5e2019-05-08 08:04:06 -06001701 bc_num_setup(&exp, digs, BC_NUM_BIGDIG_LOG10);
1702 bc_num_bigdig2num(&exp, (BcBigDig) places);
Gavin Howard7ad5a662019-02-19 14:40:46 -07001703
1704 bc_num_printDecimal(&exp);
1705
1706exit:
1707 bc_num_free(&temp);
Gavin Howardb1558662019-04-26 14:32:55 -06001708 return BC_SIG ? BC_STATUS_SIGNAL : BC_STATUS_SUCCESS;
Gavin Howard7ad5a662019-02-19 14:40:46 -07001709}
1710#endif // BC_ENABLE_EXTRA_MATH
1711
Gavin Howardad80c522019-05-18 20:11:15 -06001712static BcStatus bc_num_printFixup(BcNum *restrict n, BcBigDig rem,
1713 BcBigDig pow, size_t idx)
1714{
Gavin Howardf5ced072019-05-18 19:46:10 -06001715 size_t i, len = n->len - idx;
Stefan Esserba6a9bb2019-05-17 19:38:59 +02001716 BcBigDig acc;
Gavin Howarda80836d2019-05-18 19:18:04 -06001717 BcDig *a = n->num + idx;
Stefan Esserba6a9bb2019-05-17 19:38:59 +02001718
Gavin Howard2f3eb0b2019-05-18 20:01:35 -06001719 if (len < 2) return BC_STATUS_SUCCESS;
Gavin Howardbe0fb122019-05-18 19:26:38 -06001720
Gavin Howard2f3eb0b2019-05-18 20:01:35 -06001721 for (i = len - 1; BC_NO_SIG && i > 0; --i) {
Gavin Howardbe0fb122019-05-18 19:26:38 -06001722
Gavin Howardad80c522019-05-18 20:11:15 -06001723 acc = ((BcBigDig) a[i]) * rem + ((BcBigDig) a[i - 1]);
1724 a[i - 1] = (BcDig) (acc % pow);
1725 acc /= pow;
Gavin Howardf5ced072019-05-18 19:46:10 -06001726 acc += (BcBigDig) a[i];
Gavin Howardbe0fb122019-05-18 19:26:38 -06001727
Stefan Esserba6a9bb2019-05-17 19:38:59 +02001728 if (acc >= BC_BASE_POW) {
Gavin Howardbe0fb122019-05-18 19:26:38 -06001729
Gavin Howarddd42b4b2019-05-18 18:30:46 -06001730 if (i == len - 1) {
Gavin Howardd0d31972019-05-18 20:13:27 -06001731 len = bc_vm_growSize(len, 1);
1732 bc_num_expand(n, bc_vm_growSize(len, idx));
Gavin Howarda80836d2019-05-18 19:18:04 -06001733 a = n->num + idx;
Gavin Howardd0d31972019-05-18 20:13:27 -06001734 a[len - 1] = 0;
Stefan Esserba6a9bb2019-05-17 19:38:59 +02001735 }
Gavin Howardbe0fb122019-05-18 19:26:38 -06001736
Stefan Esserba6a9bb2019-05-17 19:38:59 +02001737 a[i + 1] += acc / BC_BASE_POW;
1738 acc %= BC_BASE_POW;
1739 }
Gavin Howardbe0fb122019-05-18 19:26:38 -06001740
Gavin Howardf5ced072019-05-18 19:46:10 -06001741 assert(acc < BC_BASE_POW);
1742 a[i] = (BcDig) acc;
Stefan Esserba6a9bb2019-05-17 19:38:59 +02001743 }
Gavin Howardf5ced072019-05-18 19:46:10 -06001744
Gavin Howard2f3eb0b2019-05-18 20:01:35 -06001745 n->len = len + idx;
1746
1747 return BC_SIG ? BC_STATUS_SIGNAL : BC_STATUS_SUCCESS;
Stefan Esserba6a9bb2019-05-17 19:38:59 +02001748}
1749
Gavin Howardad80c522019-05-18 20:11:15 -06001750static BcStatus bc_num_printPrepare(BcNum *restrict n, BcBigDig rem,
1751 BcBigDig pow)
1752{
Gavin Howard2f3eb0b2019-05-18 20:01:35 -06001753 BcStatus s = BC_STATUS_SUCCESS;
Stefan Esserba6a9bb2019-05-17 19:38:59 +02001754 size_t i;
1755
Gavin Howard2f3eb0b2019-05-18 20:01:35 -06001756 for (i = 0; BC_NO_SIG && BC_NO_ERR(!s) && i < n->len; ++i)
Gavin Howardad80c522019-05-18 20:11:15 -06001757 s = bc_num_printFixup(n, rem, pow, i);
Gavin Howardbe0fb122019-05-18 19:26:38 -06001758
Gavin Howard2f3eb0b2019-05-18 20:01:35 -06001759 if (BC_ERR(s)) return s;
1760
1761 for (i = 0; BC_NO_SIG && i < n->len; ++i) {
1762
Gavin Howardad80c522019-05-18 20:11:15 -06001763 assert(pow == ((BcBigDig) ((BcDig) pow)));
Gavin Howardbe0fb122019-05-18 19:26:38 -06001764
Gavin Howardad80c522019-05-18 20:11:15 -06001765 if (n->num[i] >= (BcDig) pow) {
Gavin Howardbe0fb122019-05-18 19:26:38 -06001766
Stefan Esserba6a9bb2019-05-17 19:38:59 +02001767 if (i + 1 == n->len) {
Gavin Howardd0d31972019-05-18 20:13:27 -06001768 n->len = bc_vm_growSize(n->len, 1);
1769 bc_num_expand(n, n->len);
Stefan Esserba6a9bb2019-05-17 19:38:59 +02001770 n->num[i + 1] = 0;
Stefan Esserba6a9bb2019-05-17 19:38:59 +02001771 }
Gavin Howardbe0fb122019-05-18 19:26:38 -06001772
Gavin Howardaf556c32019-05-18 20:14:44 -06001773 assert(pow < BC_BASE_POW);
Gavin Howardad80c522019-05-18 20:11:15 -06001774 n->num[i + 1] += n->num[i] / ((BcDig) pow);
1775 n->num[i] %= (BcDig) pow;
Stefan Esserba6a9bb2019-05-17 19:38:59 +02001776 }
1777 }
Gavin Howard2f3eb0b2019-05-18 20:01:35 -06001778
1779 return BC_NO_ERR(!s) && BC_SIG ? BC_STATUS_SIGNAL : BC_STATUS_SUCCESS;
Stefan Esserba6a9bb2019-05-17 19:38:59 +02001780}
Stefan Esserba6a9bb2019-05-17 19:38:59 +02001781
Gavin Howard2956a5e2019-05-08 08:04:06 -06001782static BcStatus bc_num_printNum(BcNum *restrict n, BcBigDig base,
Gavin Howarddc0ee9e2019-02-16 23:27:08 -07001783 size_t len, BcNumDigitOp print)
Gavin Howardbc7cae82018-03-14 13:43:04 -06001784{
Gavin Howard63738202018-09-26 15:34:20 -06001785 BcStatus s;
1786 BcVec stack;
Gavin Howardcaf4ca12019-05-18 19:49:32 -06001787 BcNum intp, fracp1, fracp2, digit, flen1, flen2, *n1, *n2, *temp;
Gavin Howard2c2dcb32019-05-23 21:58:06 -06001788 BcBigDig dig = 0, *ptr, acc, exp;
Gavin Howardefd84652019-05-18 19:36:33 -06001789 size_t i, j;
Gavin Howard83eb8392018-10-09 01:21:19 -06001790 bool radix;
Gavin Howard1705b982019-05-11 15:18:03 -06001791 BcDig digit_digs[BC_NUM_BIGDIG_LOG10 + 1];
Gavin Howard2682a1f2018-03-03 09:09:13 -07001792
Gavin Howardb3f1ee22019-05-07 21:49:15 -06001793 assert(base > 1);
Gavin Howard3fb24fa2019-02-15 17:18:57 -07001794
Gavin Howardddce6d42019-01-03 14:07:06 -07001795 if (BC_NUM_ZERO(n)) {
1796 print(0, len, false);
Gavin Howarddc0ee9e2019-02-16 23:27:08 -07001797 return BC_STATUS_SUCCESS;
Gavin Howard48af52e2018-10-30 14:47:38 -06001798 }
Gavin Howard9a4b6cd2018-10-23 15:13:30 -06001799
Gavin Howard9eb75b52019-05-25 08:12:57 -06001800 // This function uses an algorithm that Stefan Esser <se@freebsd.org> came
1801 // up with to print the integer part of a number. What it does is convert
1802 // intp into a number of the specified base, but it does it directly,
1803 // instead of just doing a series of divisions and printing the remainders
1804 // in reverse order.
1805 //
1806 // Let me explain in a bit more detail:
1807 //
1808 // The algorithm takes the current least significant digit (after intp has
1809 // been converted to an integer) and the next to least significant digit,
1810 // and it converts the least significant digit into one of the specified
1811 // base, putting any overflow into the next to least significant digit. It
1812 // iterates through the whole number, from least significant to most
1813 // significant, doing this conversion. At the end of that iteration, the
1814 // least significant digit is converted, but the others are not, so it
1815 // iterates again, starting at the next to least significant digit. It keeps
1816 // doing that conversion, skipping one more digit than the last time, until
1817 // all digits have been converted. Then it prints them in reverse order.
1818 //
1819 // That is the gist of the algorithm. It leaves out several things, such as
1820 // the fact that digits are not always converted into the specified base,
Gavin Howard934eab52019-05-25 12:10:22 -06001821 // but into something close, basically a power of the specified base. In
1822 // Stefan's words, "You could consider BcDigs to be of base 10^BC_BASE_DIGS
1823 // in the normal case and obase^N for the largest value of N that satisfies
1824 // obase^N <= 10^BC_BASE_DIGS. [This means that] the result is not in base
1825 // "obase", but in base "obase^N", which happens to be printable as a number
1826 // of base "obase" without consideration for neighbouring BcDigs." This fact
1827 // is what necessitates the existence of the loop later in this function.
1828 //
Gavin Howard227c8d52019-06-11 07:46:37 -06001829 // The conversion happens in bc_num_printPrepare() where the outer loop
1830 // happens and bc_num_printFixup() where the inner loop, or actual
1831 // conversion, happens.
Gavin Howard9eb75b52019-05-25 08:12:57 -06001832
Gavin Howard2956a5e2019-05-08 08:04:06 -06001833 bc_vec_init(&stack, sizeof(BcBigDig), NULL);
Gavin Howarda8c2e582019-05-07 11:33:10 -06001834 bc_num_init(&fracp1, n->rdx);
Gavin Howarda50fc542018-03-29 17:25:38 -06001835
Gavin Howardcaf4ca12019-05-18 19:49:32 -06001836 bc_num_createCopy(&intp, n);
1837 bc_num_truncate(&intp, intp.scale);
Gavin Howardc4e9a082019-05-07 11:12:50 -06001838
Gavin Howardcaf4ca12019-05-18 19:49:32 -06001839 s = bc_num_sub(n, &intp, &fracp1, 0);
Gavin Howardc78e7522019-02-22 13:24:25 -07001840 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howard2682a1f2018-03-03 09:09:13 -07001841
Gavin Howardefd84652019-05-18 19:36:33 -06001842 if (base != vm->last_base) {
Gavin Howardc4e9a082019-05-07 11:12:50 -06001843
Gavin Howardefd84652019-05-18 19:36:33 -06001844 vm->last_pow = 1;
1845 vm->last_exp = 0;
Gavin Howard1769abc2019-02-21 09:36:14 -07001846
Gavin Howardefd84652019-05-18 19:36:33 -06001847 while (vm->last_pow * base <= BC_BASE_POW) {
1848 vm->last_pow *= base;
1849 vm->last_exp += 1;
Gavin Howard30fe4cb2019-05-18 18:33:55 -06001850 }
1851
Gavin Howardefd84652019-05-18 19:36:33 -06001852 vm->last_rem = BC_BASE_POW - vm->last_pow;
1853 vm->last_base = base;
1854 }
Stefan Esserba6a9bb2019-05-17 19:38:59 +02001855
Gavin Howard2f3eb0b2019-05-18 20:01:35 -06001856 exp = vm->last_exp;
Gavin Howardbe0fb122019-05-18 19:26:38 -06001857
Gavin Howard2f3eb0b2019-05-18 20:01:35 -06001858 if (vm->last_rem != 0) {
Gavin Howardad80c522019-05-18 20:11:15 -06001859 s = bc_num_printPrepare(&intp, vm->last_rem, vm->last_pow);
Gavin Howardc78e7522019-02-22 13:24:25 -07001860 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howard2f3eb0b2019-05-18 20:01:35 -06001861 }
Gavin Howard1769abc2019-02-21 09:36:14 -07001862
Gavin Howard2f3eb0b2019-05-18 20:01:35 -06001863 for (i = 0; BC_NO_SIG && i < intp.len; ++i) {
Gavin Howardc4e9a082019-05-07 11:12:50 -06001864
Gavin Howardcaf4ca12019-05-18 19:49:32 -06001865 acc = (BcBigDig) intp.num[i];
Gavin Howardbe0fb122019-05-18 19:26:38 -06001866
Gavin Howard281d1582019-06-11 07:37:48 -06001867 for (j = 0; BC_NO_SIG && j < exp && (i < intp.len - 1 || acc != 0); ++j)
1868 {
Stefan Esserb2c2fe42019-05-20 13:00:48 +02001869 if (j != exp - 1) {
1870 dig = acc % base;
1871 acc /= base;
1872 }
1873 else {
1874 dig = acc;
1875 acc = 0;
1876 }
Gavin Howard281d1582019-06-11 07:37:48 -06001877
Stefan Esserb2c2fe42019-05-20 13:00:48 +02001878 assert(dig < base);
Gavin Howard281d1582019-06-11 07:37:48 -06001879
Gavin Howardefd84652019-05-18 19:36:33 -06001880 bc_vec_push(&stack, &dig);
Stefan Esserba6a9bb2019-05-17 19:38:59 +02001881 }
Gavin Howardefd84652019-05-18 19:36:33 -06001882
Gavin Howard2f3eb0b2019-05-18 20:01:35 -06001883 assert(acc == 0 || BC_SIG);
Gavin Howard63738202018-09-26 15:34:20 -06001884 }
Gavin Howard2682a1f2018-03-03 09:09:13 -07001885
Gavin Howard2d188a52019-02-25 14:19:08 -07001886 if (BC_SIG) goto sig_err;
Gavin Howarddc0ee9e2019-02-16 23:27:08 -07001887
Gavin Howard2d188a52019-02-25 14:19:08 -07001888 for (i = 0; BC_NO_SIG && i < stack.len; ++i) {
Gavin Howard63738202018-09-26 15:34:20 -06001889 ptr = bc_vec_item_rev(&stack, i);
Gavin Howardfe9a3022019-06-21 20:40:45 -06001890 assert(ptr != NULL);
Gavin Howardddce6d42019-01-03 14:07:06 -07001891 print(*ptr, len, false);
Gavin Howard63738202018-09-26 15:34:20 -06001892 }
Gavin Howard2682a1f2018-03-03 09:09:13 -07001893
Gavin Howard2d188a52019-02-25 14:19:08 -07001894 if (BC_SIG) goto sig_err;
Stefan Esser276de8c2019-05-04 20:12:43 +02001895 if (!n->scale) goto err;
Gavin Howard2682a1f2018-03-03 09:09:13 -07001896
Gavin Howardcaf4ca12019-05-18 19:49:32 -06001897 bc_num_init(&fracp2, n->rdx);
1898 bc_num_setup(&digit, digit_digs, sizeof(digit_digs) / sizeof(BcDig));
1899 bc_num_init(&flen1, BC_NUM_BIGDIG_LOG10 + 1);
1900 bc_num_init(&flen2, BC_NUM_BIGDIG_LOG10 + 1);
1901 bc_num_one(&flen1);
1902
Gavin Howardfbae8a52019-05-07 11:15:38 -06001903 radix = true;
Gavin Howarda8c2e582019-05-07 11:33:10 -06001904 n1 = &flen1;
1905 n2 = &flen2;
Gavin Howardfbae8a52019-05-07 11:15:38 -06001906
Gavin Howardead22422019-06-19 22:38:15 -06001907 fracp2.scale = n->scale;
1908 fracp2.rdx = BC_NUM_RDX(fracp2.scale);
1909
Gavin Howard06fee0c2019-05-09 14:43:20 -06001910 while (BC_NO_SIG && bc_num_intDigits(n1) < n->scale + 1) {
Gavin Howarddc0ee9e2019-02-16 23:27:08 -07001911
Gavin Howardb3f1ee22019-05-07 21:49:15 -06001912 bc_num_expand(&fracp2, fracp1.len + 1);
1913 s = bc_num_mulArray(&fracp1, base, &fracp2);
Gavin Howardcaf4ca12019-05-18 19:49:32 -06001914 if (BC_ERROR_SIGNAL_ONLY(s)) goto frac_err;
Gavin Howarda2653e62019-06-19 22:02:27 -06001915 if (fracp2.len < fracp2.rdx) fracp2.len = fracp2.rdx;
Gavin Howard1769abc2019-02-21 09:36:14 -07001916
1917 // Will never fail (except for signals) because fracp is
1918 // guaranteed to be non-negative and small enough.
Gavin Howard2956a5e2019-05-08 08:04:06 -06001919 s = bc_num_bigdig(&fracp2, &dig);
Gavin Howardcaf4ca12019-05-18 19:49:32 -06001920 if (BC_ERROR_SIGNAL_ONLY(s)) goto frac_err;
Gavin Howarddc0ee9e2019-02-16 23:27:08 -07001921
Gavin Howard2956a5e2019-05-08 08:04:06 -06001922 bc_num_bigdig2num(&digit, dig);
Gavin Howard43b6fd72019-05-07 11:39:19 -06001923 s = bc_num_sub(&fracp2, &digit, &fracp1, 0);
Gavin Howardcaf4ca12019-05-18 19:49:32 -06001924 if (BC_ERROR_SIGNAL_ONLY(s)) goto frac_err;
Gavin Howarddc0ee9e2019-02-16 23:27:08 -07001925
Gavin Howardddce6d42019-01-03 14:07:06 -07001926 print(dig, len, radix);
Gavin Howardb3f1ee22019-05-07 21:49:15 -06001927 s = bc_num_mulArray(n1, base, n2);
Gavin Howardcaf4ca12019-05-18 19:49:32 -06001928 if (BC_ERROR_SIGNAL_ONLY(s)) goto frac_err;
Gavin Howardfbae8a52019-05-07 11:15:38 -06001929
1930 radix = false;
Gavin Howarda8c2e582019-05-07 11:33:10 -06001931 temp = n1;
1932 n1 = n2;
1933 n2 = temp;
Gavin Howard63738202018-09-26 15:34:20 -06001934 }
Gavin Howard2682a1f2018-03-03 09:09:13 -07001935
Gavin Howardcaf4ca12019-05-18 19:49:32 -06001936frac_err:
Gavin Howardf1148822019-05-11 15:29:24 -06001937 bc_num_free(&flen2);
1938 bc_num_free(&flen1);
Gavin Howarda8c2e582019-05-07 11:33:10 -06001939 bc_num_free(&fracp2);
Gavin Howardcaf4ca12019-05-18 19:49:32 -06001940sig_err:
1941 if (BC_NO_ERR(!s) && BC_SIG) s = BC_STATUS_SIGNAL;
1942err:
Gavin Howarda8c2e582019-05-07 11:33:10 -06001943 bc_num_free(&fracp1);
Gavin Howardcaf4ca12019-05-18 19:49:32 -06001944 bc_num_free(&intp);
Gavin Howard63738202018-09-26 15:34:20 -06001945 bc_vec_free(&stack);
Gavin Howarddc0ee9e2019-02-16 23:27:08 -07001946 return s;
Gavin Howard2682a1f2018-03-03 09:09:13 -07001947}
1948
Gavin Howard2956a5e2019-05-08 08:04:06 -06001949static BcStatus bc_num_printBase(BcNum *restrict n, BcBigDig base) {
Gavin Howardb3f1ee22019-05-07 21:49:15 -06001950
Gavin Howarddc0ee9e2019-02-16 23:27:08 -07001951 BcStatus s;
Gavin Howard1ab22d22019-01-03 13:32:17 -07001952 size_t width;
Gavin Howard83eb8392018-10-09 01:21:19 -06001953 BcNumDigitOp print;
1954 bool neg = n->neg;
1955
Gavin Howardb4a9cdb2019-05-07 15:25:51 -06001956 if (neg) bc_num_putchar('-');
Gavin Howard83eb8392018-10-09 01:21:19 -06001957
1958 n->neg = false;
1959
Gavin Howardb3f1ee22019-05-07 21:49:15 -06001960 if (base <= BC_NUM_MAX_POSIX_IBASE) {
Gavin Howard83eb8392018-10-09 01:21:19 -06001961 width = 1;
1962 print = bc_num_printHex;
1963 }
1964 else {
Gavin Howardb3f1ee22019-05-07 21:49:15 -06001965 width = bc_num_log10(base - 1);
Gavin Howard83eb8392018-10-09 01:21:19 -06001966 print = bc_num_printDigits;
1967 }
1968
Gavin Howarddc0ee9e2019-02-16 23:27:08 -07001969 s = bc_num_printNum(n, base, width, print);
Gavin Howard83eb8392018-10-09 01:21:19 -06001970 n->neg = neg;
Gavin Howarddc0ee9e2019-02-16 23:27:08 -07001971
1972 return s;
Gavin Howard83eb8392018-10-09 01:21:19 -06001973}
1974
Gavin Howard40a085f2018-12-03 12:08:59 -07001975#if DC_ENABLED
Gavin Howard2956a5e2019-05-08 08:04:06 -06001976BcStatus bc_num_stream(BcNum *restrict n, BcBigDig base) {
Gavin Howarddc0ee9e2019-02-16 23:27:08 -07001977 return bc_num_printNum(n, base, 1, bc_num_printChar);
Gavin Howard83eb8392018-10-09 01:21:19 -06001978}
1979#endif // DC_ENABLED
1980
Gavin Howard3c02ed32018-12-28 18:17:15 -07001981void bc_num_setup(BcNum *restrict n, BcDig *restrict num, size_t cap) {
Gavin Howardfe9a3022019-06-21 20:40:45 -06001982 assert(n != NULL);
Gavin Howardb8a12922018-12-21 21:40:45 -07001983 n->num = num;
1984 n->cap = cap;
Stefan Eßera1445f22019-08-01 07:17:00 -06001985 bc_num_zero(n);
Gavin Howardb8a12922018-12-21 21:40:45 -07001986}
1987
Gavin Howard3c02ed32018-12-28 18:17:15 -07001988void bc_num_init(BcNum *restrict n, size_t req) {
Gavin Howardfe9a3022019-06-21 20:40:45 -06001989 assert(n != NULL);
Gavin Howard53eba8b2018-10-31 15:14:37 -06001990 req = req >= BC_NUM_DEF_SIZE ? req : BC_NUM_DEF_SIZE;
Gavin Howard404ece72019-04-26 15:59:15 -06001991 bc_num_setup(n, bc_vm_malloc(BC_NUM_SIZE(req)), req);
Gavin Howardb5c77212018-02-14 17:12:34 -07001992}
1993
Gavin Howarded392aa2018-02-27 13:09:26 -07001994void bc_num_free(void *num) {
Gavin Howardfe9a3022019-06-21 20:40:45 -06001995 assert(num != NULL);
Gavin Howardad0ecfe2018-10-30 01:16:01 -06001996 free(((BcNum*) num)->num);
Gavin Howardb5c77212018-02-14 17:12:34 -07001997}
1998
Gavin Howard3c02ed32018-12-28 18:17:15 -07001999void bc_num_copy(BcNum *d, const BcNum *s) {
Gavin Howardfe9a3022019-06-21 20:40:45 -06002000 assert(d != NULL && s != NULL);
Gavin Howard7344ba72019-01-03 13:37:27 -07002001 if (d == s) return;
Gavin Howarddcff3c92019-01-09 10:04:56 -07002002 bc_num_expand(d, s->len);
Gavin Howard7344ba72019-01-03 13:37:27 -07002003 d->len = s->len;
2004 d->neg = s->neg;
2005 d->rdx = s->rdx;
Gavin Howardfcf29012019-04-25 20:12:19 -06002006 d->scale = s->scale;
Gavin Howard404ece72019-04-26 15:59:15 -06002007 memcpy(d->num, s->num, BC_NUM_SIZE(d->len));
Gavin Howard5a049c42018-02-15 11:24:11 -07002008}
2009
Gavin Howardbaa4f582019-01-24 13:56:35 -07002010void bc_num_createCopy(BcNum *d, const BcNum *s) {
2011 bc_num_init(d, s->len);
2012 bc_num_copy(d, s);
2013}
2014
Gavin Howard2956a5e2019-05-08 08:04:06 -06002015void bc_num_createFromBigdig(BcNum *n, BcBigDig val) {
Gavin Howard92ba4e52019-05-10 20:49:13 -06002016 bc_num_init(n, (BC_NUM_BIGDIG_LOG10 - 1) / BC_BASE_DIGS + 1);
Gavin Howard2956a5e2019-05-08 08:04:06 -06002017 bc_num_bigdig2num(n, val);
Gavin Howardbaa4f582019-01-24 13:56:35 -07002018}
2019
Gavin Howard7ad5a662019-02-19 14:40:46 -07002020size_t bc_num_scale(const BcNum *restrict n) {
Gavin Howardfcf29012019-04-25 20:12:19 -06002021 return n->scale;
Gavin Howard7ad5a662019-02-19 14:40:46 -07002022}
2023
2024size_t bc_num_len(const BcNum *restrict n) {
2025
Gavin Howard30ed6402019-05-14 09:00:55 -06002026 size_t len = n->len;
Gavin Howardfcf29012019-04-25 20:12:19 -06002027
Gavin Howardb41483b2019-04-27 08:30:10 -06002028 if (BC_NUM_ZERO(n)) return 0;
Gavin Howardfe9a3022019-06-21 20:40:45 -06002029
Gavin Howard7106c252019-05-14 08:54:07 -06002030 if (n->rdx == len) {
Gavin Howard7ad5a662019-02-19 14:40:46 -07002031
Gavin Howard30ed6402019-05-14 09:00:55 -06002032 size_t zero, scale;
2033
Gavin Howard7106c252019-05-14 08:54:07 -06002034 len = bc_num_nonzeroLen(n);
Gavin Howardb41483b2019-04-27 08:30:10 -06002035
Gavin Howard7106c252019-05-14 08:54:07 -06002036 scale = n->scale % BC_BASE_DIGS;
2037 scale = scale ? scale : BC_BASE_DIGS;
Gavin Howard0c593dd2019-05-14 08:50:09 -06002038
Gavin Howard7106c252019-05-14 08:54:07 -06002039 zero = bc_num_zeroDigits(n->num + len - 1);
2040
2041 len = len * BC_BASE_DIGS - zero - (BC_BASE_DIGS - scale);
2042 }
2043 else len = bc_num_intDigits(n) + n->scale;
2044
2045 return len;
Gavin Howard7ad5a662019-02-19 14:40:46 -07002046}
2047
Gavin Howarddc0ee9e2019-02-16 23:27:08 -07002048BcStatus bc_num_parse(BcNum *restrict n, const char *restrict val,
Gavin Howard2956a5e2019-05-08 08:04:06 -06002049 BcBigDig base, bool letter)
Gavin Howard3c02ed32018-12-28 18:17:15 -07002050{
Gavin Howarddc0ee9e2019-02-16 23:27:08 -07002051 BcStatus s = BC_STATUS_SUCCESS;
2052
Gavin Howardfe9a3022019-06-21 20:40:45 -06002053 assert(n != NULL && val != NULL && base);
Gavin Howardbd848202019-05-18 11:50:42 -06002054 assert(base >= BC_NUM_MIN_BASE && base <= vm->maxes[BC_PROG_GLOBALS_IBASE]);
Gavin Howardc90ed902019-01-02 13:07:49 -07002055 assert(bc_num_strValid(val));
Gavin Howard3eb626f2018-02-14 13:54:35 -07002056
Gavin Howard2956a5e2019-05-08 08:04:06 -06002057 if (letter) {
2058 BcBigDig dig = bc_num_parseChar(val[0], BC_NUM_MAX_LBASE);
2059 bc_num_bigdig2num(n, dig);
2060 }
Gavin Howardb3f1ee22019-05-07 21:49:15 -06002061 else if (base == BC_BASE) bc_num_parseDecimal(n, val);
2062 else s = bc_num_parseBase(n, val, base);
Gavin Howarddc0ee9e2019-02-16 23:27:08 -07002063
2064 return s;
Gavin Howard3eb626f2018-02-14 13:54:35 -07002065}
2066
Gavin Howard2956a5e2019-05-08 08:04:06 -06002067BcStatus bc_num_print(BcNum *restrict n, BcBigDig base, bool newline) {
Gavin Howardb3f1ee22019-05-07 21:49:15 -06002068
Gavin Howarddc0ee9e2019-02-16 23:27:08 -07002069 BcStatus s = BC_STATUS_SUCCESS;
2070
Gavin Howardfe9a3022019-06-21 20:40:45 -06002071 assert(n != NULL);
Gavin Howardb3f1ee22019-05-07 21:49:15 -06002072 assert(BC_ENABLE_EXTRA_MATH || base >= BC_NUM_MIN_BASE);
Gavin Howard3eb626f2018-02-14 13:54:35 -07002073
Gavin Howardab5e9632019-01-02 13:32:02 -07002074 bc_num_printNewline();
Gavin Howardbc7cae82018-03-14 13:43:04 -06002075
Gavin Howardddce6d42019-01-03 14:07:06 -07002076 if (BC_NUM_ZERO(n)) bc_num_printHex(0, 1, false);
Gavin Howardb3f1ee22019-05-07 21:49:15 -06002077 else if (base == BC_BASE) bc_num_printDecimal(n);
Gavin Howard7ad5a662019-02-19 14:40:46 -07002078#if BC_ENABLE_EXTRA_MATH
Gavin Howardb3f1ee22019-05-07 21:49:15 -06002079 else if (base == 0 || base == 1)
2080 s = bc_num_printExponent(n, base != 0);
Gavin Howard7ad5a662019-02-19 14:40:46 -07002081#endif // BC_ENABLE_EXTRA_MATH
Gavin Howardb3f1ee22019-05-07 21:49:15 -06002082 else s = bc_num_printBase(n, base);
Gavin Howard3eb626f2018-02-14 13:54:35 -07002083
Gavin Howardb4a9cdb2019-05-07 15:25:51 -06002084 if (BC_NO_ERR(!s) && newline) bc_num_putchar('\n');
Gavin Howarddc0ee9e2019-02-16 23:27:08 -07002085
2086 return s;
Gavin Howard3eb626f2018-02-14 13:54:35 -07002087}
2088
Gavin Howard2956a5e2019-05-08 08:04:06 -06002089BcStatus bc_num_bigdig(const BcNum *restrict n, BcBigDig *result) {
Gavin Howard68b8a5c2018-02-15 11:41:24 -07002090
Gavin Howard63738202018-09-26 15:34:20 -06002091 size_t i;
Gavin Howard2956a5e2019-05-08 08:04:06 -06002092 BcBigDig r;
Gavin Howard68b8a5c2018-02-15 11:41:24 -07002093
Gavin Howardfe9a3022019-06-21 20:40:45 -06002094 assert(n != NULL && result != NULL);
Gavin Howard68b8a5c2018-02-15 11:41:24 -07002095
Gavin Howardecafd4f2019-02-23 09:30:45 -07002096 if (BC_ERR(n->neg)) return bc_vm_err(BC_ERROR_MATH_NEGATIVE);
Gavin Howard68b8a5c2018-02-15 11:41:24 -07002097
Gavin Howard06b6e862019-02-15 11:15:31 -07002098 for (r = 0, i = n->len; i > n->rdx;) {
Gavin Howard5bede412019-02-23 09:31:28 -07002099
Gavin Howard92ba4e52019-05-10 20:49:13 -06002100 BcBigDig prev = r * BC_BASE_POW;
Gavin Howard5bede412019-02-23 09:31:28 -07002101
Gavin Howard4fb37202019-05-11 14:49:13 -06002102 if (BC_ERR(prev / BC_BASE_POW != r))
Gavin Howard5bede412019-02-23 09:31:28 -07002103 return bc_vm_err(BC_ERROR_MATH_OVERFLOW);
2104
Gavin Howard2956a5e2019-05-08 08:04:06 -06002105 r = prev + (BcBigDig) n->num[--i];
Gavin Howard5bede412019-02-23 09:31:28 -07002106
Gavin Howard4fb37202019-05-11 14:49:13 -06002107 if (BC_ERR(r < prev)) return bc_vm_err(BC_ERROR_MATH_OVERFLOW);
Gavin Howard63738202018-09-26 15:34:20 -06002108 }
Gavin Howard68b8a5c2018-02-15 11:41:24 -07002109
Gavin Howard7b557da2018-12-21 10:25:32 -07002110 *result = r;
Gavin Howardd3a1c392018-12-11 12:00:57 -07002111
Gavin Howard63738202018-09-26 15:34:20 -06002112 return BC_STATUS_SUCCESS;
Gavin Howard68b8a5c2018-02-15 11:41:24 -07002113}
2114
Gavin Howard2956a5e2019-05-08 08:04:06 -06002115void bc_num_bigdig2num(BcNum *restrict n, BcBigDig val) {
Gavin Howard8e2cc692018-02-15 17:39:14 -07002116
Gavin Howardc25fd612019-04-26 19:31:31 -06002117 BcDig *ptr;
Gavin Howard2956a5e2019-05-08 08:04:06 -06002118 size_t i;
Gavin Howardc25fd612019-04-26 19:31:31 -06002119
Gavin Howardfe9a3022019-06-21 20:40:45 -06002120 assert(n != NULL);
Gavin Howard8e2cc692018-02-15 17:39:14 -07002121
Gavin Howard63738202018-09-26 15:34:20 -06002122 bc_num_zero(n);
Gavin Howard025d04d2018-02-20 13:53:28 -07002123
Gavin Howard1ab22d22019-01-03 13:32:17 -07002124 if (!val) return;
Gavin Howard8e2cc692018-02-15 17:39:14 -07002125
Gavin Howard2956a5e2019-05-08 08:04:06 -06002126 bc_num_expand(n, BC_NUM_BIGDIG_LOG10);
Gavin Howard05feab02019-04-23 16:24:07 -06002127
Gavin Howard5c2ab902019-05-15 10:12:57 -06002128 for (ptr = n->num, i = 0; val; ++i, val /= BC_BASE_POW)
Gavin Howard92ba4e52019-05-10 20:49:13 -06002129 ptr[i] = val % BC_BASE_POW;
Gavin Howard5c2ab902019-05-15 10:12:57 -06002130
2131 n->len = i;
Gavin Howard025d04d2018-02-20 13:53:28 -07002132}
2133
Gavin Howard47137932019-10-10 21:16:43 -06002134#if BC_ENABLE_EXTRA_MATH
2135BcStatus bc_num_rng(const BcNum *restrict n, BcRNG *rng) {
2136
2137 BcStatus s;
2138 BcNum pow, temp, temp2, intn, frac;
2139 ulong state1, state2, inc1, inc2;
Gavin Howard47137932019-10-10 21:16:43 -06002140 BcDig pow_num[BC_RAND_NUM_SIZE];
Gavin Howard47137932019-10-10 21:16:43 -06002141
Gavin Howard47137932019-10-10 21:16:43 -06002142 bc_num_setup(&pow, pow_num, sizeof(pow_num) / sizeof(BcDig));
Gavin Howard47137932019-10-10 21:16:43 -06002143
2144 bc_num_init(&temp, n->len);
Gavin Howard0b4bf1c2019-10-13 13:14:20 -06002145 bc_num_init(&temp2, n->len);
Gavin Howarda8d93a12019-10-13 13:01:25 -06002146 bc_num_init(&frac, n->rdx);
Gavin Howard0b4bf1c2019-10-13 13:14:20 -06002147 bc_num_init(&intn, bc_num_int(n));
Gavin Howard47137932019-10-10 21:16:43 -06002148
2149 s = bc_num_mul(&rng->max, &rng->max, &pow, 0);
2150 if (BC_ERR(s)) goto err;
2151
Gavin Howarda8d93a12019-10-13 13:01:25 -06002152 memcpy(frac.num, n->num, BC_NUM_SIZE(n->rdx));
Gavin Howard47137932019-10-10 21:16:43 -06002153 frac.len = n->rdx;
2154 frac.rdx = n->rdx;
2155 frac.scale = n->scale;
2156
2157 s = bc_num_mul(&frac, &pow, &temp, 0);
2158 if (s) goto err;
2159
2160 bc_num_truncate(&temp, temp.scale);
2161 bc_num_copy(&frac, &temp);
2162
Gavin Howard0b4bf1c2019-10-13 13:14:20 -06002163 memcpy(intn.num, n->num + n->rdx, BC_NUM_SIZE(bc_num_int(n)));
Gavin Howard47137932019-10-10 21:16:43 -06002164 intn.len = bc_num_int(n);
2165
2166 if (BC_NUM_NONZERO(&frac)) {
2167
2168 s = bc_num_divmod(&frac, &rng->max, &temp, &temp2, 0);
2169 if (BC_ERR(s)) goto err;
2170
2171 s = bc_num_bigdig(&temp2, &state1);
2172 if (BC_ERR(s)) goto err;
2173
2174 s = bc_num_bigdig(&temp, &state2);
2175 if (BC_ERR(s)) goto err;
2176 }
2177 else state1 = state2 = 0;
2178
2179 if (BC_NUM_NONZERO(&intn)) {
2180
2181 s = bc_num_divmod(&intn, &rng->max, &temp, &temp2, 0);
2182 if (BC_ERR(s)) goto err;
2183
2184 s = bc_num_bigdig(&temp2, &inc1);
2185 if (BC_ERR(s)) goto err;
2186
2187 if (bc_num_cmp(&temp, &rng->max) > 0) {
2188 bc_num_copy(&temp2, &temp);
2189 s = bc_num_mod(&temp2, &rng->max, &temp, 0);
2190 if (BC_ERR(s)) goto err;
2191 }
2192
2193 s = bc_num_bigdig(&temp, &inc2);
2194 if (BC_ERR(s)) goto err;
2195 }
2196 else inc1 = inc2 = 0;
2197
2198 bc_rand_seed(rng, state1, state2, inc1, inc2);
2199
2200err:
Gavin Howard0b4bf1c2019-10-13 13:14:20 -06002201 bc_num_free(&intn);
Gavin Howarda8d93a12019-10-13 13:01:25 -06002202 bc_num_free(&frac);
Gavin Howard0b4bf1c2019-10-13 13:14:20 -06002203 bc_num_free(&temp2);
Gavin Howard47137932019-10-10 21:16:43 -06002204 bc_num_free(&temp);
2205 return s;
2206}
2207
2208BcStatus bc_num_createFromRNG(BcNum *restrict n, BcRNG *rng) {
2209
2210 BcStatus s;
2211 ulong s1, s2, i1, i2;
2212 BcNum pow, conv, temp1, temp2, temp3;
2213 BcDig pow_num[BC_RAND_NUM_SIZE];
2214 BcDig temp1_num[BC_RAND_NUM_SIZE], temp2_num[BC_RAND_NUM_SIZE];
2215 BcDig conv_num[BC_NUM_BIGDIG_LOG10];
2216
2217 bc_num_init(n, 2 * BC_RAND_NUM_SIZE);
2218 bc_num_init(&temp3, 2 * BC_RAND_NUM_SIZE);
2219
2220 bc_num_setup(&pow, pow_num, sizeof(pow_num) / sizeof(BcDig));
2221 bc_num_setup(&temp1, temp1_num, sizeof(temp1_num) / sizeof(BcDig));
2222 bc_num_setup(&temp2, temp2_num, sizeof(temp2_num) / sizeof(BcDig));
2223 bc_num_setup(&conv, conv_num, sizeof(conv_num) / sizeof(BcDig));
2224
2225 s = bc_num_mul(&rng->max, &rng->max, &pow, 0);
2226 if (BC_ERR(s)) goto err;
2227
2228 bc_rand_getUlongs(rng, &s1, &s2, &i1, &i2);
2229
2230 bc_num_bigdig2num(&conv, s2);
2231
2232 s = bc_num_mul(&conv, &rng->max, &temp1, 0);
2233 if (BC_ERR(s)) goto err;
2234
2235 bc_num_bigdig2num(&conv, s1);
2236
2237 s = bc_num_add(&conv, &temp1, &temp2, 0);
2238 if (BC_ERR(s)) goto err;
2239
2240 s = bc_num_div(&temp2, &pow, &temp3, BC_RAND_STATE_BITS);
2241 if (BC_ERR(s)) goto err;
2242
2243 bc_num_bigdig2num(&conv, i2);
2244
2245 s = bc_num_mul(&conv, &rng->max, &temp1, 0);
2246 if (BC_ERR(s)) goto err;
2247
2248 bc_num_bigdig2num(&conv, i1);
2249
2250 s = bc_num_add(&conv, &temp1, &temp2, 0);
2251 if (BC_ERR(s)) goto err;
2252
2253 s = bc_num_add(&temp2, &temp3, n, 0);
2254 if (BC_ERR(s)) goto err;
2255
2256err:
2257 bc_num_free(&temp3);
2258 if (BC_ERR(s)) bc_num_free(n);
2259 return s;
2260}
2261
2262BcStatus bc_num_irand(const BcNum *restrict a, BcNum *restrict b,
2263 BcRNG *restrict rng)
2264{
2265 BcStatus s = BC_STATUS_SUCCESS;
2266 BcRand r;
Gavin Howard865dddb2019-10-11 20:34:40 -06002267 BcBigDig modl;
2268 BcNum pow, pow2, cp, cp2, mod, temp1, temp2, rand;
Gavin Howard47137932019-10-10 21:16:43 -06002269 BcNum *p1, *p2, *t1, *t2, *c1, *c2, *tmp;
2270 BcDig rand_num[BC_NUM_BIGDIG_LOG10];
Gavin Howard865dddb2019-10-11 20:34:40 -06002271 bool carry = false, start = true;
2272
2273 assert(a != b);
Gavin Howard47137932019-10-10 21:16:43 -06002274
2275 bc_num_createCopy(&cp, a);
Gavin Howard865dddb2019-10-11 20:34:40 -06002276 bc_num_truncate(&cp, cp.scale);
2277 cp.neg = false;
2278
2279 if (BC_NUM_ZERO(&cp) || BC_NUM_ONE(&cp)) goto early_exit;
2280
Gavin Howard47137932019-10-10 21:16:43 -06002281 bc_num_init(&cp2, cp.len);
Gavin Howard865dddb2019-10-11 20:34:40 -06002282 bc_num_init(&mod, BC_NUM_BIGDIG_LOG10);
Gavin Howard47137932019-10-10 21:16:43 -06002283 bc_num_init(&temp1, BC_NUM_DEF_SIZE);
2284 bc_num_init(&temp2, BC_NUM_DEF_SIZE);
2285 bc_num_init(&pow2, BC_NUM_DEF_SIZE);
2286 bc_num_init(&pow, BC_NUM_DEF_SIZE);
2287 bc_num_one(&pow);
2288 bc_num_setup(&rand, rand_num, sizeof(rand_num) / sizeof(BcDig));
2289
Gavin Howard47137932019-10-10 21:16:43 -06002290 p1 = &pow;
2291 p2 = &pow2;
2292 t1 = &temp1;
2293 t2 = &temp2;
2294 c1 = &cp;
2295 c2 = &cp2;
2296
Gavin Howard865dddb2019-10-11 20:34:40 -06002297 while (BC_NUM_NONZERO(c1)) {
Gavin Howard47137932019-10-10 21:16:43 -06002298
Gavin Howard865dddb2019-10-11 20:34:40 -06002299 s = bc_num_divmod(c1, &rng->max, c2, &mod, 0);
2300 if (BC_ERR(s)) goto err;
2301
2302 s = bc_num_bigdig(&mod, &modl);
2303 if (BC_ERR(s)) goto err;
2304
2305 if (start || carry) carry = !modl;
2306 else modl += 1;
2307
2308 if (!modl) r = bc_rand_int(rng);
2309 else if (modl == 1) r = 0;
2310 else r = bc_rand_bounded(rng, (BcRand) modl);
2311
Gavin Howard47137932019-10-10 21:16:43 -06002312 bc_num_bigdig2num(&rand, r);
2313
2314 s = bc_num_mul(&rand, p1, p2, 0);
2315 if (BC_ERR(s)) goto err;
2316
2317 s = bc_num_add(p2, t1, t2, 0);
2318 if (BC_ERR(s)) goto err;
2319
Gavin Howard865dddb2019-10-11 20:34:40 -06002320 if (BC_NUM_NONZERO(c2)) {
Gavin Howard47137932019-10-10 21:16:43 -06002321
Gavin Howard865dddb2019-10-11 20:34:40 -06002322 s = bc_num_mul(&rng->max, p1, p2, 0);
2323 if (BC_ERR(s)) goto err;
2324
2325 tmp = p1;
2326 p1 = p2;
2327 p2 = tmp;
2328
2329 tmp = c1;
2330 c1 = c2;
2331 c2 = tmp;
2332
2333 start = false;
2334 }
2335 else c1 = c2;
Gavin Howard47137932019-10-10 21:16:43 -06002336
2337 tmp = t1;
2338 t1 = t2;
2339 t2 = tmp;
Gavin Howard47137932019-10-10 21:16:43 -06002340 }
2341
2342 bc_num_copy(b, t1);
Gavin Howard865dddb2019-10-11 20:34:40 -06002343 bc_num_clean(b);
Gavin Howard47137932019-10-10 21:16:43 -06002344
2345err:
2346 bc_num_free(&pow);
2347 bc_num_free(&pow2);
2348 bc_num_free(&temp2);
2349 bc_num_free(&temp1);
Gavin Howard865dddb2019-10-11 20:34:40 -06002350 bc_num_free(&mod);
Gavin Howard47137932019-10-10 21:16:43 -06002351 bc_num_free(&cp2);
Gavin Howard865dddb2019-10-11 20:34:40 -06002352early_exit:
Gavin Howard47137932019-10-10 21:16:43 -06002353 bc_num_free(&cp);
2354 return s;
2355}
2356#endif // BC_ENABLE_EXTRA_MATH
2357
Gavin Howard3ef06272019-06-19 21:19:10 -06002358size_t bc_num_addReq(const BcNum *a, const BcNum *b, size_t scale) {
Gavin Howard1973d612019-02-21 15:37:32 -07002359
2360 size_t aint, bint, ardx, brdx;
2361
Gavin Howardd18fa6d2019-01-24 13:49:35 -07002362 BC_UNUSED(scale);
Gavin Howard1973d612019-02-21 15:37:32 -07002363
2364 ardx = a->rdx;
Gavin Howard7b7d2f32019-02-21 16:46:47 -07002365 aint = bc_num_int(a);
Gavin Howard3ef06272019-06-19 21:19:10 -06002366 assert(aint <= a->len && ardx <= a->len);
2367
2368 brdx = b->rdx;
Gavin Howard7b7d2f32019-02-21 16:46:47 -07002369 bint = bc_num_int(b);
Gavin Howard3ef06272019-06-19 21:19:10 -06002370 assert(bint <= b->len && brdx <= b->len);
2371
Gavin Howard2d188a52019-02-25 14:19:08 -07002372 ardx = BC_MAX(ardx, brdx);
2373 aint = BC_MAX(aint, bint);
Gavin Howard1973d612019-02-21 15:37:32 -07002374
Gavin Howard2d188a52019-02-25 14:19:08 -07002375 return bc_vm_growSize(bc_vm_growSize(ardx, aint), 1);
Gavin Howardd18fa6d2019-01-24 13:49:35 -07002376}
2377
Gavin Howard3ef06272019-06-19 21:19:10 -06002378size_t bc_num_mulReq(const BcNum *a, const BcNum *b, size_t scale) {
Gavin Howard2d188a52019-02-25 14:19:08 -07002379 size_t max, rdx;
2380 rdx = bc_vm_growSize(a->rdx, b->rdx);
Gavin Howardd7883272019-05-10 20:04:23 -06002381 max = BC_NUM_RDX(scale);
2382 max = bc_vm_growSize(BC_MAX(max, rdx), 1);
Gavin Howard2d188a52019-02-25 14:19:08 -07002383 rdx = bc_vm_growSize(bc_vm_growSize(bc_num_int(a), bc_num_int(b)), max);
2384 return rdx;
Gavin Howardd18fa6d2019-01-24 13:49:35 -07002385}
2386
Gavin Howard3ef06272019-06-19 21:19:10 -06002387size_t bc_num_powReq(const BcNum *a, const BcNum *b, size_t scale) {
Gavin Howardd18fa6d2019-01-24 13:49:35 -07002388 BC_UNUSED(scale);
Gavin Howard007afdf2019-02-23 09:45:42 -07002389 return bc_vm_growSize(bc_vm_growSize(a->len, b->len), 1);
Gavin Howardd18fa6d2019-01-24 13:49:35 -07002390}
2391
2392#if BC_ENABLE_EXTRA_MATH
Gavin Howard3ef06272019-06-19 21:19:10 -06002393size_t bc_num_placesReq(const BcNum *a, const BcNum *b, size_t scale) {
Gavin Howardd18fa6d2019-01-24 13:49:35 -07002394 BC_UNUSED(scale);
Gavin Howarde3938372019-05-23 21:57:48 -06002395 return a->len + b->len - a->rdx - b->rdx;
Gavin Howardd18fa6d2019-01-24 13:49:35 -07002396}
2397#endif // BC_ENABLE_EXTRA_MATH
2398
Stefan Esser73c0a2a2019-07-31 21:57:00 +02002399BcStatus bc_num_add(BcNum *a, BcNum *b, BcNum *c, size_t scale) {
Stefan Esser73c0a2a2019-07-31 21:57:00 +02002400 return bc_num_binary(a, b, c, false, bc_num_as, bc_num_addReq(a, b, scale));
2401}
2402
2403BcStatus bc_num_sub(BcNum *a, BcNum *b, BcNum *c, size_t scale) {
Stefan Esser73c0a2a2019-07-31 21:57:00 +02002404 return bc_num_binary(a, b, c, true, bc_num_as, bc_num_addReq(a, b, scale));
2405}
Gavin Howard3eb626f2018-02-14 13:54:35 -07002406
Gavin Howard12fe7812018-09-29 03:45:18 -06002407BcStatus bc_num_mul(BcNum *a, BcNum *b, BcNum *c, size_t scale) {
Gavin Howardd18fa6d2019-01-24 13:49:35 -07002408 return bc_num_binary(a, b, c, scale, bc_num_m, bc_num_mulReq(a, b, scale));
Gavin Howard3eb626f2018-02-14 13:54:35 -07002409}
2410
Gavin Howard12fe7812018-09-29 03:45:18 -06002411BcStatus bc_num_div(BcNum *a, BcNum *b, BcNum *c, size_t scale) {
Gavin Howardd18fa6d2019-01-24 13:49:35 -07002412 return bc_num_binary(a, b, c, scale, bc_num_d, bc_num_mulReq(a, b, scale));
Gavin Howard3eb626f2018-02-14 13:54:35 -07002413}
2414
Gavin Howard54b946a2018-10-23 12:06:57 -06002415BcStatus bc_num_mod(BcNum *a, BcNum *b, BcNum *c, size_t scale) {
Gavin Howard2d188a52019-02-25 14:19:08 -07002416 size_t req = bc_num_mulReq(a, b, scale);
2417 return bc_num_binary(a, b, c, scale, bc_num_rem, req);
Gavin Howard3eb626f2018-02-14 13:54:35 -07002418}
2419
Gavin Howard12fe7812018-09-29 03:45:18 -06002420BcStatus bc_num_pow(BcNum *a, BcNum *b, BcNum *c, size_t scale) {
Gavin Howard6734e1a2019-02-21 17:11:52 -07002421 return bc_num_binary(a, b, c, scale, bc_num_p, bc_num_powReq(a, b, scale));
Gavin Howard3eb626f2018-02-14 13:54:35 -07002422}
2423
Gavin Howard7d8f0382018-12-27 16:15:13 -07002424#if BC_ENABLE_EXTRA_MATH
2425BcStatus bc_num_places(BcNum *a, BcNum *b, BcNum *c, size_t scale) {
Gavin Howard8291ecd2019-05-06 14:34:53 -06002426 size_t req = bc_num_placesReq(a, b, scale);
2427 return bc_num_binary(a, b, c, scale, bc_num_place, req);
Gavin Howard7d8f0382018-12-27 16:15:13 -07002428}
2429
2430BcStatus bc_num_lshift(BcNum *a, BcNum *b, BcNum *c, size_t scale) {
Gavin Howarde3938372019-05-23 21:57:48 -06002431 size_t req = bc_num_placesReq(a, b, scale);
Gavin Howard8291ecd2019-05-06 14:34:53 -06002432 return bc_num_binary(a, b, c, scale, bc_num_left, req);
Gavin Howard7d8f0382018-12-27 16:15:13 -07002433}
2434
2435BcStatus bc_num_rshift(BcNum *a, BcNum *b, BcNum *c, size_t scale) {
Gavin Howarde3938372019-05-23 21:57:48 -06002436 size_t req = bc_num_placesReq(a, b, scale);
Gavin Howard8291ecd2019-05-06 14:34:53 -06002437 return bc_num_binary(a, b, c, scale, bc_num_right, req);
Gavin Howard7d8f0382018-12-27 16:15:13 -07002438}
2439#endif // BC_ENABLE_EXTRA_MATH
2440
Gavin Howard3c02ed32018-12-28 18:17:15 -07002441BcStatus bc_num_sqrt(BcNum *restrict a, BcNum *restrict b, size_t scale) {
Gavin Howard954276b2018-05-16 01:43:58 -06002442
Gavin Howard1cbfe242019-01-09 17:13:11 -07002443 BcStatus s = BC_STATUS_SUCCESS;
Gavin Howard34edd0a2018-10-25 15:51:05 -06002444 BcNum num1, num2, half, f, fprime, *x0, *x1, *temp;
Gavin Howardf6c6d592019-10-25 08:26:42 -06002445 size_t pow, len, rdx, req, digs, digs1, digs2, resscale;
Stefan Eßer4276af42019-04-29 14:04:57 -06002446 BcDig half_digs[1];
Gavin Howard954276b2018-05-16 01:43:58 -06002447
Gavin Howardfe9a3022019-06-21 20:40:45 -06002448 assert(a != NULL && b != NULL && a != b);
Gavin Howard954276b2018-05-16 01:43:58 -06002449
Gavin Howardecafd4f2019-02-23 09:30:45 -07002450 if (BC_ERR(a->neg)) return bc_vm_err(BC_ERROR_MATH_NEGATIVE);
Gavin Howard07fbf012019-02-19 09:25:11 -07002451
Stefan Eßer4276af42019-04-29 14:04:57 -06002452 if (a->scale > scale) scale = a->scale;
Gavin Howard06fee0c2019-05-09 14:43:20 -06002453 len = bc_vm_growSize(bc_num_intDigits(a), 1);
Gavin Howardeec07282019-05-10 20:10:18 -06002454 rdx = BC_NUM_RDX(scale);
2455 req = bc_vm_growSize(BC_MAX(rdx, a->rdx), len >> 1);
Gavin Howard2d188a52019-02-25 14:19:08 -07002456 bc_num_init(b, bc_vm_growSize(req, 1));
Gavin Howard954276b2018-05-16 01:43:58 -06002457
Gavin Howardddce6d42019-01-03 14:07:06 -07002458 if (BC_NUM_ZERO(a)) {
Gavin Howardf8ddb6d2018-10-22 12:58:53 -06002459 bc_num_setToZero(b, scale);
Gavin Howardad0ecfe2018-10-30 01:16:01 -06002460 return BC_STATUS_SUCCESS;
Gavin Howard63738202018-09-26 15:34:20 -06002461 }
Gavin Howardb2f01a42019-05-10 20:00:32 -06002462 if (BC_NUM_ONE(a)) {
Gavin Howard757b66a2018-10-06 04:13:46 -06002463 bc_num_one(b);
Gavin Howardad0ecfe2018-10-30 01:16:01 -06002464 bc_num_extend(b, scale);
2465 return BC_STATUS_SUCCESS;
Gavin Howard63738202018-09-26 15:34:20 -06002466 }
Gavin Howard954276b2018-05-16 01:43:58 -06002467
Gavin Howardeec07282019-05-10 20:10:18 -06002468 rdx = BC_NUM_RDX(scale);
2469 rdx = BC_MAX(rdx, a->rdx);
Stefan Eßer4276af42019-04-29 14:04:57 -06002470 len = bc_vm_growSize(a->len, rdx);
Gavin Howard954276b2018-05-16 01:43:58 -06002471
Gavin Howardad0ecfe2018-10-30 01:16:01 -06002472 bc_num_init(&num1, len);
2473 bc_num_init(&num2, len);
Gavin Howard18f40202018-12-29 21:30:37 -07002474 bc_num_setup(&half, half_digs, sizeof(half_digs) / sizeof(BcDig));
Gavin Howard954276b2018-05-16 01:43:58 -06002475
Gavin Howard63738202018-09-26 15:34:20 -06002476 bc_num_one(&half);
Gavin Howard92ba4e52019-05-10 20:49:13 -06002477 half.num[0] = BC_BASE_POW / 2;
Stefan Eßer4276af42019-04-29 14:04:57 -06002478 half.len = 1;
Gavin Howard63738202018-09-26 15:34:20 -06002479 half.rdx = 1;
Stefan Eßer4276af42019-04-29 14:04:57 -06002480 half.scale = 1;
Gavin Howard954276b2018-05-16 01:43:58 -06002481
Gavin Howardad0ecfe2018-10-30 01:16:01 -06002482 bc_num_init(&f, len);
2483 bc_num_init(&fprime, len);
Gavin Howard954276b2018-05-16 01:43:58 -06002484
Gavin Howard63738202018-09-26 15:34:20 -06002485 x0 = &num1;
2486 x1 = &num2;
Gavin Howard954276b2018-05-16 01:43:58 -06002487
Gavin Howard63738202018-09-26 15:34:20 -06002488 bc_num_one(x0);
Gavin Howard06fee0c2019-05-09 14:43:20 -06002489 pow = bc_num_intDigits(a);
Gavin Howard954276b2018-05-16 01:43:58 -06002490
Gavin Howard53eba8b2018-10-31 15:14:37 -06002491 if (pow) {
Gavin Howard954276b2018-05-16 01:43:58 -06002492
Gavin Howardc39fd492018-10-04 10:07:03 -06002493 if (pow & 1) x0->num[0] = 2;
2494 else x0->num[0] = 6;
Gavin Howard954276b2018-05-16 01:43:58 -06002495
Gavin Howardc39fd492018-10-04 10:07:03 -06002496 pow -= 2 - (pow & 1);
Gavin Howard74704572019-05-07 13:48:48 -06002497 s = bc_num_shiftLeft(x0, pow / 2);
2498 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howard63738202018-09-26 15:34:20 -06002499 }
Gavin Howard954276b2018-05-16 01:43:58 -06002500
Gavin Howardeec07282019-05-10 20:10:18 -06002501 x0->scale = x0->rdx = digs = digs1 = digs2 = 0;
Gavin Howardf6c6d592019-10-25 08:26:42 -06002502 resscale = (scale + BC_BASE_DIGS) + 2;
Stefan Eßer4276af42019-04-29 14:04:57 -06002503
Gavin Howardf6c6d592019-10-25 08:26:42 -06002504 while (BC_NO_SIG && bc_num_cmp(x1, x0)) {
Gavin Howard954276b2018-05-16 01:43:58 -06002505
Gavin Howard971a2672019-04-26 14:32:07 -06002506 assert(BC_NUM_NONZERO(x0));
Gavin Howard1769abc2019-02-21 09:36:14 -07002507
Stefan Eßer4276af42019-04-29 14:04:57 -06002508 s = bc_num_div(a, x0, &f, resscale);
Gavin Howard1769abc2019-02-21 09:36:14 -07002509 assert(!s || s == BC_STATUS_SIGNAL);
Gavin Howardc78e7522019-02-22 13:24:25 -07002510 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Stefan Eßer4276af42019-04-29 14:04:57 -06002511 s = bc_num_add(x0, &f, &fprime, resscale);
Gavin Howardc78e7522019-02-22 13:24:25 -07002512 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Stefan Eßer4276af42019-04-29 14:04:57 -06002513 s = bc_num_mul(&fprime, &half, x1, resscale);
Gavin Howardc78e7522019-02-22 13:24:25 -07002514 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howard954276b2018-05-16 01:43:58 -06002515
Gavin Howard63738202018-09-26 15:34:20 -06002516 temp = x0;
2517 x0 = x1;
2518 x1 = temp;
2519 }
Gavin Howard954276b2018-05-16 01:43:58 -06002520
Gavin Howard2d188a52019-02-25 14:19:08 -07002521 if (BC_SIG) {
Gavin Howard176cfe62019-02-16 23:40:13 -07002522 s = BC_STATUS_SIGNAL;
2523 goto err;
2524 }
Gavin Howard0dfe2922018-05-22 13:57:02 -06002525
Gavin Howardad0ecfe2018-10-30 01:16:01 -06002526 bc_num_copy(b, x0);
Stefan Eßer4276af42019-04-29 14:04:57 -06002527 if (b->scale > scale) bc_num_truncate(b, b->scale - scale);
Gavin Howard954276b2018-05-16 01:43:58 -06002528
Gavin Howard33164932019-06-22 21:07:14 -06002529 assert(!b->neg || BC_NUM_NONZERO(b));
2530 assert(b->rdx <= b->len || !b->len);
2531 assert(!b->len || b->num[b->len - 1] || b->rdx == b->len);
2532
Gavin Howard954276b2018-05-16 01:43:58 -06002533err:
Gavin Howardcf0748d2019-04-09 14:51:47 -06002534 if (BC_ERR(s)) bc_num_free(b);
Gavin Howard63738202018-09-26 15:34:20 -06002535 bc_num_free(&fprime);
Gavin Howard63738202018-09-26 15:34:20 -06002536 bc_num_free(&f);
Gavin Howard63738202018-09-26 15:34:20 -06002537 bc_num_free(&num2);
Gavin Howard63738202018-09-26 15:34:20 -06002538 bc_num_free(&num1);
Gavin Howard63738202018-09-26 15:34:20 -06002539 return s;
Gavin Howard3eb626f2018-02-14 13:54:35 -07002540}
Gavin Howardba009802018-09-29 04:41:51 -06002541
Gavin Howard2cb39612018-10-22 08:46:48 -06002542BcStatus bc_num_divmod(BcNum *a, BcNum *b, BcNum *c, BcNum *d, size_t scale) {
2543
2544 BcStatus s;
2545 BcNum num2, *ptr_a;
Gavin Howard890d0c02018-10-30 16:34:50 -06002546 bool init = false;
Stefan Eßera7fbbf62019-04-29 14:14:30 -06002547 size_t ts, len;
2548
2549 ts = BC_MAX(scale + b->scale, a->scale);
2550 len = bc_num_mulReq(a, b, ts);
Gavin Howard2cb39612018-10-22 08:46:48 -06002551
Gavin Howardfe9a3022019-06-21 20:40:45 -06002552 assert(a != NULL && b != NULL && c != NULL && d != NULL);
Gavin Howardf2ef2f32019-01-16 11:25:16 -07002553 assert(c != d && a != d && b != d && b != c);
Gavin Howard2cb39612018-10-22 08:46:48 -06002554
Gavin Howard890d0c02018-10-30 16:34:50 -06002555 if (c == a) {
Gavin Howard2cb39612018-10-22 08:46:48 -06002556 memcpy(&num2, c, sizeof(BcNum));
2557 ptr_a = &num2;
Gavin Howardad0ecfe2018-10-30 01:16:01 -06002558 bc_num_init(c, len);
Gavin Howard890d0c02018-10-30 16:34:50 -06002559 init = true;
Gavin Howard2cb39612018-10-22 08:46:48 -06002560 }
Gavin Howardf679ad82018-10-29 12:26:30 -06002561 else {
2562 ptr_a = a;
Gavin Howardad0ecfe2018-10-30 01:16:01 -06002563 bc_num_expand(c, len);
Gavin Howardf679ad82018-10-29 12:26:30 -06002564 }
Gavin Howard2cb39612018-10-22 08:46:48 -06002565
Gavin Howardf5961782019-05-09 07:50:46 -06002566 if (BC_NUM_NONZERO(a) && !a->rdx && !b->rdx && b->len == 1 && !scale) {
Gavin Howardc9732f42019-05-18 20:32:31 -06002567
Gavin Howard2956a5e2019-05-08 08:04:06 -06002568 BcBigDig rem;
Gavin Howardc9732f42019-05-18 20:32:31 -06002569
Gavin Howard9d771ba2019-05-08 09:09:25 -06002570 s = bc_num_divArray(ptr_a, (BcBigDig) b->num[0], c, &rem);
Gavin Howardc9732f42019-05-18 20:32:31 -06002571
Gavin Howard92ba4e52019-05-10 20:49:13 -06002572 assert(rem < BC_BASE_POW);
Gavin Howardc9732f42019-05-18 20:32:31 -06002573
Gavin Howardb3f1ee22019-05-07 21:49:15 -06002574 d->num[0] = (BcDig) rem;
Gavin Howardf5961782019-05-09 07:50:46 -06002575 d->len = (rem != 0);
Gavin Howardb3f1ee22019-05-07 21:49:15 -06002576 }
Gavin Howardf5961782019-05-09 07:50:46 -06002577 else s = bc_num_r(ptr_a, b, c, d, scale, ts);
Gavin Howard2cb39612018-10-22 08:46:48 -06002578
Gavin Howardddce6d42019-01-03 14:07:06 -07002579 assert(!c->neg || BC_NUM_NONZERO(c));
Gavin Howardc4bf4c42019-01-09 10:30:15 -07002580 assert(c->rdx <= c->len || !c->len);
Gavin Howarda2653e62019-06-19 22:02:27 -06002581 assert(!c->len || c->num[c->len - 1] || c->rdx == c->len);
Gavin Howardddce6d42019-01-03 14:07:06 -07002582 assert(!d->neg || BC_NUM_NONZERO(d));
Gavin Howardc4bf4c42019-01-09 10:30:15 -07002583 assert(d->rdx <= d->len || !d->len);
Gavin Howarda2653e62019-06-19 22:02:27 -06002584 assert(!d->len || d->num[d->len - 1] || d->rdx == d->len);
Gavin Howard2cb39612018-10-22 08:46:48 -06002585
2586 if (init) bc_num_free(&num2);
2587
2588 return s;
2589}
2590
Gavin Howard40a085f2018-12-03 12:08:59 -07002591#if DC_ENABLED
Gavin Howard34edd0a2018-10-25 15:51:05 -06002592BcStatus bc_num_modexp(BcNum *a, BcNum *b, BcNum *c, BcNum *restrict d) {
Gavin Howardba009802018-09-29 04:41:51 -06002593
2594 BcStatus s;
Gavin Howard34edd0a2018-10-25 15:51:05 -06002595 BcNum base, exp, two, temp;
Gavin Howardd3929832018-12-24 15:13:15 -07002596 BcDig two_digs[2];
Gavin Howardba009802018-09-29 04:41:51 -06002597
Gavin Howardfe9a3022019-06-21 20:40:45 -06002598 assert(a != NULL && b != NULL && c != NULL && d != NULL);
2599 assert(a != d && b != d && c != d);
Gavin Howardba009802018-09-29 04:41:51 -06002600
Gavin Howardecafd4f2019-02-23 09:30:45 -07002601 if (BC_ERR(BC_NUM_ZERO(c)))
2602 return bc_vm_err(BC_ERROR_MATH_DIVIDE_BY_ZERO);
2603 if (BC_ERR(b->neg)) return bc_vm_err(BC_ERROR_MATH_NEGATIVE);
2604 if (BC_ERR(a->rdx || b->rdx || c->rdx))
Gavin Howard7536dcf2018-12-15 19:27:09 -07002605 return bc_vm_err(BC_ERROR_MATH_NON_INTEGER);
Gavin Howardba009802018-09-29 04:41:51 -06002606
Gavin Howardad0ecfe2018-10-30 01:16:01 -06002607 bc_num_expand(d, c->len);
2608 bc_num_init(&base, c->len);
Gavin Howard7fdc30a2018-12-29 21:31:21 -07002609 bc_num_setup(&two, two_digs, sizeof(two_digs) / sizeof(BcDig));
Gavin Howard303572b2019-05-09 07:50:56 -06002610 bc_num_init(&temp, b->len + 1);
Gavin Howardba009802018-09-29 04:41:51 -06002611
2612 bc_num_one(&two);
Gavin Howardba009802018-09-29 04:41:51 -06002613 two.num[0] = 2;
2614 bc_num_one(d);
2615
Gavin Howard1769abc2019-02-21 09:36:14 -07002616 // We already checked for 0.
Gavin Howard53eba8b2018-10-31 15:14:37 -06002617 s = bc_num_rem(a, c, &base, 0);
Gavin Howard1769abc2019-02-21 09:36:14 -07002618 assert(!s || s == BC_STATUS_SIGNAL);
Gavin Howardc78e7522019-02-22 13:24:25 -07002619 if (BC_ERROR_SIGNAL_ONLY(s)) goto rem_err;
Gavin Howardbaa4f582019-01-24 13:56:35 -07002620 bc_num_createCopy(&exp, b);
Gavin Howardba009802018-09-29 04:41:51 -06002621
Gavin Howard2d188a52019-02-25 14:19:08 -07002622 while (BC_NO_SIG && BC_NUM_NONZERO(&exp)) {
Gavin Howardba009802018-09-29 04:41:51 -06002623
Gavin Howard1769abc2019-02-21 09:36:14 -07002624 // Num two cannot be 0, so no errors.
Gavin Howard53eba8b2018-10-31 15:14:37 -06002625 s = bc_num_divmod(&exp, &two, &exp, &temp, 0);
Gavin Howardc78e7522019-02-22 13:24:25 -07002626 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howardba009802018-09-29 04:41:51 -06002627
Gavin Howardb2f01a42019-05-10 20:00:32 -06002628 if (BC_NUM_ONE(&temp) && !temp.neg) {
2629
Gavin Howard53eba8b2018-10-31 15:14:37 -06002630 s = bc_num_mul(d, &base, &temp, 0);
Gavin Howardc78e7522019-02-22 13:24:25 -07002631 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howard1769abc2019-02-21 09:36:14 -07002632
2633 // We already checked for 0.
Gavin Howard53eba8b2018-10-31 15:14:37 -06002634 s = bc_num_rem(&temp, c, d, 0);
Gavin Howard1769abc2019-02-21 09:36:14 -07002635 assert(!s || s == BC_STATUS_SIGNAL);
Gavin Howardc78e7522019-02-22 13:24:25 -07002636 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howardba009802018-09-29 04:41:51 -06002637 }
2638
Gavin Howard53eba8b2018-10-31 15:14:37 -06002639 s = bc_num_mul(&base, &base, &temp, 0);
Gavin Howardc78e7522019-02-22 13:24:25 -07002640 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howard1769abc2019-02-21 09:36:14 -07002641
2642 // We already checked for 0.
Gavin Howard53eba8b2018-10-31 15:14:37 -06002643 s = bc_num_rem(&temp, c, &base, 0);
Gavin Howard1769abc2019-02-21 09:36:14 -07002644 assert(!s || s == BC_STATUS_SIGNAL);
Gavin Howardc78e7522019-02-22 13:24:25 -07002645 if (BC_ERROR_SIGNAL_ONLY(s)) goto err;
Gavin Howardba009802018-09-29 04:41:51 -06002646 }
2647
Gavin Howard2d188a52019-02-25 14:19:08 -07002648 if (BC_NO_ERR(!s) && BC_SIG) s = BC_STATUS_SIGNAL;
Gavin Howardfa4983a2019-02-16 23:43:03 -07002649
Gavin Howardba009802018-09-29 04:41:51 -06002650err:
Gavin Howardba009802018-09-29 04:41:51 -06002651 bc_num_free(&exp);
Gavin Howardd88164d2019-02-21 09:57:31 -07002652rem_err:
2653 bc_num_free(&temp);
Gavin Howardba009802018-09-29 04:41:51 -06002654 bc_num_free(&base);
Gavin Howard3cf769e2018-10-11 13:55:11 -06002655 assert(!d->neg || d->len);
Gavin Howarda2653e62019-06-19 22:02:27 -06002656 assert(!d->len || d->num[d->len - 1] || d->rdx == d->len);
Gavin Howardba009802018-09-29 04:41:51 -06002657 return s;
2658}
Gavin Howarde6e84762018-10-03 11:46:34 -06002659#endif // DC_ENABLED
Gavin Howardefc1e202019-05-06 08:01:07 -06002660
2661#if BC_DEBUG_CODE
2662void bc_num_printDebug(const BcNum *n, const char *name, bool emptyline) {
2663 printf("%s: ", name);
2664 bc_num_printDecimal(n);
2665 printf("\n");
2666 if (emptyline) printf("\n");
Gavin Howarde34f7d82019-05-07 10:32:39 -06002667 vm->nchars = 0;
Gavin Howardefc1e202019-05-06 08:01:07 -06002668}
2669
Gavin Howard2a84f962019-05-08 17:20:09 -06002670void bc_num_printDigs(const BcDig *n, size_t len, bool emptyline) {
Gavin Howardefc1e202019-05-06 08:01:07 -06002671
2672 size_t i;
2673
Gavin Howard92ba4e52019-05-10 20:49:13 -06002674 for (i = len - 1; i < len; --i) printf(" %0*d", BC_BASE_DIGS, n[i]);
Gavin Howardefc1e202019-05-06 08:01:07 -06002675
2676 printf("\n");
2677 if (emptyline) printf("\n");
Gavin Howarde34f7d82019-05-07 10:32:39 -06002678 vm->nchars = 0;
Gavin Howardefc1e202019-05-06 08:01:07 -06002679}
2680
Gavin Howard2a84f962019-05-08 17:20:09 -06002681void bc_num_printWithDigs(const BcNum *n, const char *name, bool emptyline) {
2682 printf("%s len: %zu, rdx: %zu, scale: %zu\n",
2683 name, n->len, n->rdx, n->scale);
2684 bc_num_printDigs(n->num, n->len, emptyline);
2685}
2686
Gavin Howardefc1e202019-05-06 08:01:07 -06002687void bc_num_dump(const char *varname, const BcNum *n) {
2688
Gavin Howarde6476862019-05-23 08:54:15 -06002689 ulong i, scale = n->scale;
Gavin Howardefc1e202019-05-06 08:01:07 -06002690
2691 fprintf(stderr, "\n%s = %s", varname, n->len ? (n->neg ? "-" : "+") : "0 ");
2692
2693 for (i = n->len - 1; i < n->len; --i) {
2694
2695 if (i + 1 == n->rdx) fprintf(stderr, ". ");
2696
Gavin Howard92ba4e52019-05-10 20:49:13 -06002697 if (scale / BC_BASE_DIGS != n->rdx - i - 1)
2698 fprintf(stderr, "%0*d ", BC_BASE_DIGS, n->num[i]);
Gavin Howardefc1e202019-05-06 08:01:07 -06002699 else {
2700
Gavin Howard92ba4e52019-05-10 20:49:13 -06002701 int mod = scale % BC_BASE_DIGS;
2702 int d = BC_BASE_DIGS - mod;
Gavin Howardefc1e202019-05-06 08:01:07 -06002703 BcDig div;
2704
2705 if (mod != 0) {
Gavin Howarde6476862019-05-23 08:54:15 -06002706 div = n->num[i] / ((BcDig) bc_num_pow10[(ulong) d]);
Gavin Howardefc1e202019-05-06 08:01:07 -06002707 fprintf(stderr, "%0*d", (int) mod, div);
2708 }
2709
Gavin Howarde6476862019-05-23 08:54:15 -06002710 div = n->num[i] % ((BcDig) bc_num_pow10[(ulong) d]);
Gavin Howardefc1e202019-05-06 08:01:07 -06002711 fprintf(stderr, " ' %0*d ", d, div);
2712 }
2713 }
2714
2715 fprintf(stderr, "(%zu | %zu.%zu / %zu) %p\n",
2716 n->scale, n->len, n->rdx, n->cap, (void*) n->num);
2717}
2718#endif // BC_DEBUG_CODE