blob: ee2c8adb2c19637afe400534a3b266b24b8a57d1 [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 Howardb5904bf2018-02-20 13:28:18 -07004 * Copyright 2018 Gavin D. Howard
Gavin Howard3eb626f2018-02-14 13:54:35 -07005 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
10 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11 * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
14 * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15 * PERFORMANCE OF THIS SOFTWARE.
16 *
Gavin Howardb5904bf2018-02-20 13:28:18 -070017 * *****************************************************************************
Gavin Howard3eb626f2018-02-14 13:54:35 -070018 *
19 * Code for the number type.
20 *
21 */
22
23#include <assert.h>
24#include <stdbool.h>
25#include <string.h>
26
Gavin Howard8e2cc692018-02-15 17:39:14 -070027#include <math.h>
28
29#include <limits.h>
30
Gavin Howard29493062018-03-20 19:57:37 -060031#include <status.h>
Gavin Howard3ba6c8d2018-02-15 12:23:35 -070032#include <num.h>
Gavin Howardf507f232018-02-23 21:10:24 -070033#include <vector.h>
Gavin Howard29493062018-03-20 19:57:37 -060034#include <bc.h>
Gavin Howard3eb626f2018-02-14 13:54:35 -070035
Gavin Howard2ea7dc42018-05-22 14:02:02 -060036BcStatus bc_num_subArrays(BcDigit *n1, BcDigit *n2, size_t len) {
Gavin Howarde1e74942018-03-20 15:51:52 -060037 size_t i, j;
Gavin Howard2ea7dc42018-05-22 14:02:02 -060038 for (i = 0; !bcg.signe && i < len; ++i) {
39 for (n1[i] -= n2[i], j = 0; !bcg.signe && n1[i + j] < 0;) {
Gavin Howard9c4358c2018-03-22 20:11:28 -060040 n1[i + j++] += 10;
Gavin Howardcfc5faa2018-03-20 18:22:42 -060041 n1[i + j] -= 1;
Gavin Howarde1e74942018-03-20 15:51:52 -060042 }
43 }
Gavin Howard2ea7dc42018-05-22 14:02:02 -060044 return bcg.signe ? BC_STATUS_EXEC_SIGNAL : BC_STATUS_SUCCESS;
Gavin Howarde1e74942018-03-20 15:51:52 -060045}
46
Gavin Howard6a804cf2018-05-17 16:54:12 -060047ssize_t bc_num_compare(BcDigit *n1, BcDigit *n2, size_t len) {
48 size_t i;
Gavin Howard08bf5292018-03-20 14:59:33 -060049 BcDigit c;
Gavin Howard2ea7dc42018-05-22 14:02:02 -060050 for (c = 0, i = len - 1; !bcg.signe && !(c = n1[i] - n2[i]) && i < len; --i);
Gavin Howard6a804cf2018-05-17 16:54:12 -060051 return (c < 0 ? -1 : 1) * (ssize_t) (i + 1);
Gavin Howard08bf5292018-03-20 14:59:33 -060052}
53
Gavin Howard6a804cf2018-05-17 16:54:12 -060054ssize_t bc_num_cmp(BcNum *a, BcNum *b) {
Gavin Howard4681d1b2018-03-05 19:49:33 -070055
Gavin Howard9c4358c2018-03-22 20:11:28 -060056 size_t i, min, a_int, b_int, diff;
Gavin Howard6b5f5bb2018-03-22 23:16:57 -060057 BcDigit *max_num, *min_num;
Gavin Howard4681d1b2018-03-05 19:49:33 -070058 bool a_max;
Gavin Howardf6e3fb32018-08-09 13:48:59 -060059 ssize_t cmp, neg;
Gavin Howard4681d1b2018-03-05 19:49:33 -070060
Gavin Howard9c4358c2018-03-22 20:11:28 -060061 if (!a) return !b ? 0 : !b->neg * -2 + 1;
62 else if (!b) return a->neg * -2 + 1;
Gavin Howard4681d1b2018-03-05 19:49:33 -070063
Gavin Howard08bf5292018-03-20 14:59:33 -060064 neg = 1;
Gavin Howard4681d1b2018-03-05 19:49:33 -070065
Gavin Howard4e5ee2b2018-03-10 15:10:17 -070066 if (a->neg) {
Gavin Howard08bf5292018-03-20 14:59:33 -060067 if (b->neg) neg = -1;
Gavin Howard4681d1b2018-03-05 19:49:33 -070068 else return -1;
69 }
Gavin Howard4e5ee2b2018-03-10 15:10:17 -070070 else if (b->neg) return 1;
Gavin Howard4681d1b2018-03-05 19:49:33 -070071
Gavin Howard9c4358c2018-03-22 20:11:28 -060072 if (!a->len) return (!b->neg * -2 + 1) * !!b->len;
73 else if (!b->len) return a->neg * -2 + 1;
Gavin Howard4681d1b2018-03-05 19:49:33 -070074
Gavin Howard4e5ee2b2018-03-10 15:10:17 -070075 a_int = a->len - a->rdx;
76 b_int = b->len - b->rdx;
Gavin Howard43ab9332018-03-20 15:02:48 -060077 a_int -= b_int;
Gavin Howard4681d1b2018-03-05 19:49:33 -070078
Gavin Howardf6e3fb32018-08-09 13:48:59 -060079 if (a_int) return (ssize_t) a_int;
Gavin Howard4681d1b2018-03-05 19:49:33 -070080
Gavin Howard4e5ee2b2018-03-10 15:10:17 -070081 a_max = a->rdx > b->rdx;
Gavin Howard4681d1b2018-03-05 19:49:33 -070082
83 if (a_max) {
Gavin Howard4e5ee2b2018-03-10 15:10:17 -070084 min = b->rdx;
Gavin Howard4e5ee2b2018-03-10 15:10:17 -070085 diff = a->rdx - b->rdx;
Gavin Howard4e5ee2b2018-03-10 15:10:17 -070086 max_num = a->num + diff;
87 min_num = b->num;
Gavin Howard4681d1b2018-03-05 19:49:33 -070088 }
89 else {
Gavin Howard4e5ee2b2018-03-10 15:10:17 -070090 min = a->rdx;
Gavin Howard4e5ee2b2018-03-10 15:10:17 -070091 diff = b->rdx - a->rdx;
Gavin Howard4e5ee2b2018-03-10 15:10:17 -070092 max_num = b->num + diff;
93 min_num = a->num;
Gavin Howard4681d1b2018-03-05 19:49:33 -070094 }
95
Gavin Howard6a804cf2018-05-17 16:54:12 -060096 cmp = bc_num_compare(max_num, min_num, b_int + min);
Gavin Howard2ba07112018-03-20 15:40:58 -060097 if (cmp) return cmp * (!a_max * -2 + 1) * neg;
Gavin Howard021150b2018-03-10 15:40:42 -070098
Gavin Howard2ea7dc42018-05-22 14:02:02 -060099 for (max_num -= diff, i = diff - 1; !bcg.signe && i < diff; --i) {
Gavin Howard86f32e92018-03-20 19:21:12 -0600100 if (max_num[i]) return neg * (!a_max * -2 + 1);
Gavin Howard6fbdb292018-02-27 15:44:48 -0700101 }
102
103 return 0;
104}
105
Gavin Howardd9734e52018-03-29 15:54:41 -0600106void bc_num_truncate(BcNum *n, size_t places) {
Gavin Howard6fbdb292018-02-27 15:44:48 -0700107
Gavin Howard021150b2018-03-10 15:40:42 -0700108 BcDigit *ptr;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700109
Gavin Howard27fdfb92018-03-21 07:56:59 -0600110 assert(places <= n->rdx);
Gavin Howard6fbdb292018-02-27 15:44:48 -0700111
Gavin Howardd9734e52018-03-29 15:54:41 -0600112 if (!places) return;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700113
114 ptr = n->num + places;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700115 n->len -= places;
116 n->rdx -= places;
117
Gavin Howard021150b2018-03-10 15:40:42 -0700118 memmove(n->num, ptr, n->len * sizeof(BcDigit));
Gavin Howard021150b2018-03-10 15:40:42 -0700119 memset(n->num + n->len, 0, sizeof(BcDigit) * (n->cap - n->len));
Gavin Howard6fbdb292018-02-27 15:44:48 -0700120}
121
Gavin Howard3f68df72018-03-22 20:30:27 -0600122BcStatus bc_num_extend(BcNum *n, size_t places) {
Gavin Howard6fbdb292018-02-27 15:44:48 -0700123
124 BcStatus status;
Gavin Howard021150b2018-03-10 15:40:42 -0700125 BcDigit *ptr;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700126 size_t len;
127
Gavin Howard9c4358c2018-03-22 20:11:28 -0600128 if (!places) return BC_STATUS_SUCCESS;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700129
130 len = n->len + places;
Gavin Howard9c4358c2018-03-22 20:11:28 -0600131 if (n->cap < len && (status = bc_num_expand(n, len))) return status;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700132
133 ptr = n->num + places;
Gavin Howard021150b2018-03-10 15:40:42 -0700134 memmove(ptr, n->num, sizeof(BcDigit) * n->len);
Gavin Howard021150b2018-03-10 15:40:42 -0700135 memset(n->num, 0, sizeof(BcDigit) * places);
Gavin Howard6fbdb292018-02-27 15:44:48 -0700136
137 n->len += places;
Gavin Howardcde142c2018-02-28 17:33:44 -0700138 n->rdx += places;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700139
140 return BC_STATUS_SUCCESS;
141}
142
Gavin Howard9c4358c2018-03-22 20:11:28 -0600143BcStatus bc_num_inv(BcNum *a, BcNum *b, size_t scale) {
144
145 BcStatus status;
146 BcNum one;
147
148 if ((status = bc_num_init(&one, BC_NUM_DEF_SIZE))) return status;
149
150 bc_num_one(&one);
151 status = bc_num_div(&one, a, b, scale);
152 bc_num_free(&one);
153
154 return status;
155}
156
Gavin Howard3f68df72018-03-22 20:30:27 -0600157BcStatus bc_num_alg_a(BcNum *a, BcNum *b, BcNum *c, size_t scale) {
Gavin Howard6fbdb292018-02-27 15:44:48 -0700158
Gavin Howard6b5f5bb2018-03-22 23:16:57 -0600159 BcDigit *ptr, *ptr_a, *ptr_b, *ptr_c;
Gavin Howard647a8802018-03-22 09:42:46 -0600160 size_t i, max, min_rdx, min_int, diff, a_int, b_int;
Gavin Howard021150b2018-03-10 15:40:42 -0700161 BcDigit carry;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700162
163 (void) scale;
164
Gavin Howardf6964a12018-03-14 10:52:31 -0600165 if (!a->len) return bc_num_copy(c, b);
166 else if (!b->len) return bc_num_copy(c, a);
167
Gavin Howard6fbdb292018-02-27 15:44:48 -0700168 c->neg = a->neg;
Gavin Howard021150b2018-03-10 15:40:42 -0700169 memset(c->num, 0, c->cap * sizeof(BcDigit));
Gavin Howard6fbdb292018-02-27 15:44:48 -0700170
171 c->rdx = BC_MAX(a->rdx, b->rdx);
Gavin Howard647a8802018-03-22 09:42:46 -0600172 min_rdx = BC_MIN(a->rdx, b->rdx);
Gavin Howard6fbdb292018-02-27 15:44:48 -0700173 c->len = 0;
174
175 if (a->rdx > b->rdx) {
Gavin Howard6fbdb292018-02-27 15:44:48 -0700176 diff = a->rdx - b->rdx;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700177 ptr = a->num;
178 ptr_a = a->num + diff;
179 ptr_b = b->num;
180 }
181 else {
Gavin Howard6fbdb292018-02-27 15:44:48 -0700182 diff = b->rdx - a->rdx;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700183 ptr = b->num;
184 ptr_a = a->num;
185 ptr_b = b->num + diff;
186 }
187
Gavin Howard647a8802018-03-22 09:42:46 -0600188 for (ptr_c = c->num, i = 0; i < diff; ++i, ++c->len) ptr_c[i] = ptr[i];
Gavin Howard6fbdb292018-02-27 15:44:48 -0700189
190 ptr_c += diff;
Gavin Howard647a8802018-03-22 09:42:46 -0600191 a_int = a->len - a->rdx;
192 b_int = b->len - b->rdx;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700193
Gavin Howard647a8802018-03-22 09:42:46 -0600194 if (a_int > b_int) {
195 min_int = b_int;
196 max = a_int;
197 ptr = ptr_a;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700198 }
Gavin Howard647a8802018-03-22 09:42:46 -0600199 else {
200 min_int = a_int;
201 max = b_int;
202 ptr = ptr_b;
203 }
Gavin Howard6fbdb292018-02-27 15:44:48 -0700204
Gavin Howard2ea7dc42018-05-22 14:02:02 -0600205 for (carry = 0, i = 0; !bcg.signe && i < min_rdx + min_int; ++i, ++c->len) {
Gavin Howard6fbdb292018-02-27 15:44:48 -0700206 ptr_c[i] = ptr_a[i] + ptr_b[i] + carry;
Gavin Howard647a8802018-03-22 09:42:46 -0600207 carry = ptr_c[i] / 10;
208 ptr_c[i] %= 10;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700209 }
210
Gavin Howard2ea7dc42018-05-22 14:02:02 -0600211 for (; !bcg.signe && i < max + min_rdx; ++i, ++c->len) {
Gavin Howard6fbdb292018-02-27 15:44:48 -0700212 ptr_c[i] += ptr[i] + carry;
Gavin Howard647a8802018-03-22 09:42:46 -0600213 carry = ptr_c[i] / 10;
214 ptr_c[i] %= 10;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700215 }
216
Gavin Howard2ea7dc42018-05-22 14:02:02 -0600217 if (bcg.signe) return BC_STATUS_EXEC_SIGNAL;
218
Gavin Howard647a8802018-03-22 09:42:46 -0600219 if (carry) c->num[c->len++] = carry;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700220
221 return BC_STATUS_SUCCESS;
222}
223
Gavin Howard3f68df72018-03-22 20:30:27 -0600224BcStatus bc_num_alg_s(BcNum *a, BcNum *b, BcNum *c, size_t sub) {
Gavin Howard6fbdb292018-02-27 15:44:48 -0700225
Gavin Howard9a6f7a42018-03-05 14:07:06 -0700226 BcStatus status;
Gavin Howardf6e3fb32018-08-09 13:48:59 -0600227 ssize_t cmp;
Gavin Howard6b5f5bb2018-03-22 23:16:57 -0600228 BcNum *minuend, *subtrahend;
Gavin Howard2de93992018-03-20 15:58:13 -0600229 size_t start;
Gavin Howard9a6f7a42018-03-05 14:07:06 -0700230 bool aneg, bneg, neg;
Gavin Howarda1c090a2018-03-05 14:20:33 -0700231
232 // Because this function doesn't need to use scale (per the bc spec),
233 // I am hijacking it to tell this function whether it is doing an add
234 // or a subtract.
Gavin Howard6fbdb292018-02-27 15:44:48 -0700235
Gavin Howardeb9a8222018-03-13 09:25:56 -0600236 if (!a->len) {
Gavin Howard9a6f7a42018-03-05 14:07:06 -0700237 status = bc_num_copy(c, b);
238 c->neg = !b->neg;
239 return status;
240 }
Gavin Howardeb9a8222018-03-13 09:25:56 -0600241 else if (!b->len) return bc_num_copy(c, a);
Gavin Howard6fbdb292018-02-27 15:44:48 -0700242
Gavin Howard9a6f7a42018-03-05 14:07:06 -0700243 aneg = a->neg;
244 bneg = b->neg;
Gavin Howard9a6f7a42018-03-05 14:07:06 -0700245 a->neg = b->neg = false;
246
Gavin Howard6a804cf2018-05-17 16:54:12 -0600247 cmp = bc_num_cmp(a, b);
Gavin Howard9a6f7a42018-03-05 14:07:06 -0700248
249 a->neg = aneg;
250 b->neg = bneg;
251
252 if (!cmp) {
253 bc_num_zero(c);
254 return BC_STATUS_SUCCESS;
255 }
256 else if (cmp > 0) {
Gavin Howard8694e132018-03-14 13:43:28 -0600257 neg = sub && a->neg;
Gavin Howard9a6f7a42018-03-05 14:07:06 -0700258 minuend = a;
259 subtrahend = b;
260 }
261 else {
Gavin Howard8694e132018-03-14 13:43:28 -0600262 neg = sub && !b->neg;
Gavin Howard9a6f7a42018-03-05 14:07:06 -0700263 minuend = b;
264 subtrahend = a;
265 }
266
Gavin Howard9c4358c2018-03-22 20:11:28 -0600267 if ((status = bc_num_copy(c, minuend))) return status;
Gavin Howard9a6f7a42018-03-05 14:07:06 -0700268 c->neg = neg;
269
270 if (c->rdx < subtrahend->rdx) {
Gavin Howard9c4358c2018-03-22 20:11:28 -0600271 if ((status = bc_num_extend(c, subtrahend->rdx - c->rdx))) return status;
Gavin Howard9a6f7a42018-03-05 14:07:06 -0700272 start = 0;
273 }
Gavin Howardd9f5c8e2018-03-10 14:08:13 -0700274 else start = c->rdx - subtrahend->rdx;
Gavin Howard9a6f7a42018-03-05 14:07:06 -0700275
Gavin Howard2ea7dc42018-05-22 14:02:02 -0600276 status = bc_num_subArrays(c->num + start, subtrahend->num, subtrahend->len);
Gavin Howard9a6f7a42018-03-05 14:07:06 -0700277
Gavin Howard9a6f7a42018-03-05 14:07:06 -0700278 while (c->len > c->rdx && !c->num[c->len - 1]) --c->len;
279
280 return status;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700281}
282
Gavin Howard3f68df72018-03-22 20:30:27 -0600283BcStatus bc_num_alg_m(BcNum *a, BcNum *b, BcNum *c, size_t scale) {
Gavin Howard6fbdb292018-02-27 15:44:48 -0700284
285 BcStatus status;
Gavin Howard021150b2018-03-10 15:40:42 -0700286 BcDigit carry;
Gavin Howard9c4358c2018-03-22 20:11:28 -0600287 size_t i, j, len;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700288
Gavin Howardeb9a8222018-03-13 09:25:56 -0600289 if (!a->len || !b->len) {
Gavin Howard6fbdb292018-02-27 15:44:48 -0700290 bc_num_zero(c);
291 return BC_STATUS_SUCCESS;
292 }
Gavin Howardb11bc8a2018-03-01 17:23:00 -0700293 else if (BC_NUM_ONE(a)) {
Gavin Howard6fbdb292018-02-27 15:44:48 -0700294 status = bc_num_copy(c, b);
295 if (a->neg) c->neg = !c->neg;
296 return status;
297 }
Gavin Howardb11bc8a2018-03-01 17:23:00 -0700298 else if (BC_NUM_ONE(b)) {
Gavin Howard6fbdb292018-02-27 15:44:48 -0700299 status = bc_num_copy(c, a);
300 if (b->neg) c->neg = !c->neg;
301 return status;
302 }
303
304 scale = BC_MAX(scale, a->rdx);
305 scale = BC_MAX(scale, b->rdx);
306 c->rdx = a->rdx + b->rdx;
307
Gavin Howard021150b2018-03-10 15:40:42 -0700308 memset(c->num, 0, sizeof(BcDigit) * c->cap);
Gavin Howard9c4358c2018-03-22 20:11:28 -0600309 c->len = carry = len = 0;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700310
Gavin Howard0dfe2922018-05-22 13:57:02 -0600311 for (i = 0; !bcg.signe && i < b->len; ++i) {
Gavin Howard6fbdb292018-02-27 15:44:48 -0700312
Gavin Howard0dfe2922018-05-22 13:57:02 -0600313 for (j = 0; !bcg.signe && j < a->len; ++j) {
Gavin Howard6fbdb292018-02-27 15:44:48 -0700314 c->num[i + j] += a->num[j] * b->num[i] + carry;
Gavin Howard5f806ee2018-03-03 23:30:31 -0700315 carry = c->num[i + j] / 10;
316 c->num[i + j] %= 10;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700317 }
318
Gavin Howard0dfe2922018-05-22 13:57:02 -0600319 if (bcg.signe) return BC_STATUS_EXEC_SIGNAL;
320
Gavin Howard6fbdb292018-02-27 15:44:48 -0700321 if (carry) {
322 c->num[i + j] += carry;
323 carry = 0;
324 len = BC_MAX(len, i + j + 1);
325 }
326 else len = BC_MAX(len, i + j);
327 }
328
Gavin Howard0dfe2922018-05-22 13:57:02 -0600329 if (bcg.signe) return BC_STATUS_EXEC_SIGNAL;
330
Gavin Howard6fbdb292018-02-27 15:44:48 -0700331 c->len = BC_MAX(len, c->rdx);
Gavin Howard6fbdb292018-02-27 15:44:48 -0700332 c->neg = !a->neg != !b->neg;
333
Gavin Howarda50fc542018-03-29 17:25:38 -0600334 if (scale < c->rdx) bc_num_truncate(c, c->rdx - scale);
Gavin Howardd2e917d2018-02-28 16:03:10 -0700335 while (c->len > c->rdx && !c->num[c->len - 1]) --c->len;
336
Gavin Howard45ff3572018-05-15 16:33:58 -0600337 return BC_STATUS_SUCCESS;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700338}
339
Gavin Howard3f68df72018-03-22 20:30:27 -0600340BcStatus bc_num_alg_d(BcNum *a, BcNum *b, BcNum *c, size_t scale) {
Gavin Howard6fbdb292018-02-27 15:44:48 -0700341
342 BcStatus status;
Gavin Howard6b5f5bb2018-03-22 23:16:57 -0600343 BcDigit *ptr, *bptr, q;
Gavin Howard9c4358c2018-03-22 20:11:28 -0600344 size_t len, end, i;
Gavin Howardb651f1a2018-02-28 17:34:18 -0700345 BcNum copy;
Gavin Howard5b24c3f2018-03-13 23:57:15 -0600346 bool zero;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700347
Gavin Howardeb9a8222018-03-13 09:25:56 -0600348 if (!b->len) return BC_STATUS_MATH_DIVIDE_BY_ZERO;
349 else if (!a->len) {
Gavin Howardb651f1a2018-02-28 17:34:18 -0700350 bc_num_zero(c);
351 return BC_STATUS_SUCCESS;
352 }
Gavin Howardb11bc8a2018-03-01 17:23:00 -0700353 else if (BC_NUM_ONE(b)) {
Gavin Howardae6e7d52018-03-14 10:13:43 -0600354
Gavin Howard9c4358c2018-03-22 20:11:28 -0600355 if ((status = bc_num_copy(c, a))) return status;
Gavin Howardb651f1a2018-02-28 17:34:18 -0700356 if (b->neg) c->neg = !c->neg;
Gavin Howardae6e7d52018-03-14 10:13:43 -0600357
358 if (c->rdx < scale) status = bc_num_extend(c, scale - c->rdx);
Gavin Howarda50fc542018-03-29 17:25:38 -0600359 else bc_num_truncate(c, c->rdx - scale);
Gavin Howardae6e7d52018-03-14 10:13:43 -0600360
Gavin Howardb651f1a2018-02-28 17:34:18 -0700361 return status;
362 }
363
Gavin Howard9c4358c2018-03-22 20:11:28 -0600364 if ((status = bc_num_init(&copy, a->len + b->rdx + scale + 1))) return status;
365 if ((status = bc_num_copy(&copy, a))) goto err;
Gavin Howardb651f1a2018-02-28 17:34:18 -0700366
Gavin Howard45ff3572018-05-15 16:33:58 -0600367 if ((len = b->len) > copy.len) {
Gavin Howard9c4358c2018-03-22 20:11:28 -0600368 if ((status = bc_num_expand(&copy, len + 2))) goto err;
369 if ((status = bc_num_extend(&copy, len - copy.len))) goto err;
Gavin Howard021150b2018-03-10 15:40:42 -0700370 }
371
Gavin Howard9c4358c2018-03-22 20:11:28 -0600372 if (b->rdx > copy.rdx && (status = bc_num_extend(&copy, b->rdx - copy.rdx)))
373 goto err;
Gavin Howardb651f1a2018-02-28 17:34:18 -0700374
375 copy.rdx -= b->rdx;
376
Gavin Howard9c4358c2018-03-22 20:11:28 -0600377 if (scale > copy.rdx && (status = bc_num_extend(&copy, scale - copy.rdx)))
378 goto err;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700379
Gavin Howardc1a6a342018-03-05 12:10:14 -0700380 if (b->rdx == b->len) {
Gavin Howard45ff3572018-05-15 16:33:58 -0600381 bool zero;
382 for (zero = true, i = 0; zero && i < len; ++i) zero = !b->num[len - i - 1];
Gavin Howard8b22bb22018-08-09 10:53:37 -0600383 if (i == len) {
384 status = BC_STATUS_MATH_DIVIDE_BY_ZERO;
385 goto err;
386 }
Gavin Howardc1a6a342018-03-05 12:10:14 -0700387 len -= i - 1;
388 }
389
Gavin Howard45ff3572018-05-15 16:33:58 -0600390 if (copy.cap == copy.len && (status = bc_num_expand(&copy, copy.len + 1)))
391 goto err;
Gavin Howardb651f1a2018-02-28 17:34:18 -0700392
Gavin Howardb651f1a2018-02-28 17:34:18 -0700393 // We want an extra zero in front to make things simpler.
Gavin Howard9c4358c2018-03-22 20:11:28 -0600394 copy.num[copy.len++] = 0;
Gavin Howardac7656d2018-03-01 17:18:40 -0700395 end = copy.len - len;
396
Gavin Howard9c4358c2018-03-22 20:11:28 -0600397 if ((status = bc_num_expand(c, copy.len))) goto err;
Gavin Howardac7656d2018-03-01 17:18:40 -0700398
Gavin Howardb651f1a2018-02-28 17:34:18 -0700399 bc_num_zero(c);
400 c->rdx = copy.rdx;
401 c->len = copy.len;
Gavin Howard021150b2018-03-10 15:40:42 -0700402 bptr = b->num;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700403
Gavin Howard0dfe2922018-05-22 13:57:02 -0600404 for (i = end - 1; !bcg.signe && i < end; --i) {
Gavin Howard6fbdb292018-02-27 15:44:48 -0700405
Gavin Howard021150b2018-03-10 15:40:42 -0700406 ptr = copy.num + i;
Gavin Howardb651f1a2018-02-28 17:34:18 -0700407
Gavin Howard68f84712018-05-22 14:07:13 -0600408 q = 0;
409 for (; (!status && ptr[len]) || bc_num_compare(ptr, bptr, len) >= 0; ++q)
Gavin Howard2ea7dc42018-05-22 14:02:02 -0600410 status = bc_num_subArrays(ptr, bptr, len);
Gavin Howardb651f1a2018-02-28 17:34:18 -0700411
Gavin Howard9c4358c2018-03-22 20:11:28 -0600412 c->num[i] = q;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700413 }
414
Gavin Howard2ea7dc42018-05-22 14:02:02 -0600415 if (status) goto err;
Gavin Howard0dfe2922018-05-22 13:57:02 -0600416
Gavin Howardac7656d2018-03-01 17:18:40 -0700417 c->neg = !a->neg != !b->neg;
Gavin Howardb651f1a2018-02-28 17:34:18 -0700418 while (c->len > c->rdx && !c->num[c->len - 1]) --c->len;
Gavin Howarda50fc542018-03-29 17:25:38 -0600419 if (c->rdx > scale) bc_num_truncate(c, c->rdx - scale);
Gavin Howardb651f1a2018-02-28 17:34:18 -0700420
Gavin Howard5b24c3f2018-03-13 23:57:15 -0600421 for (i = 0, zero = true; zero && i < c->len; ++i) zero = !c->num[i];
422 if (zero) bc_num_zero(c);
423
Gavin Howardb651f1a2018-02-28 17:34:18 -0700424err:
Gavin Howardb651f1a2018-02-28 17:34:18 -0700425 bc_num_free(&copy);
Gavin Howard6fbdb292018-02-27 15:44:48 -0700426 return status;
427}
428
Gavin Howard3f68df72018-03-22 20:30:27 -0600429BcStatus bc_num_alg_mod(BcNum *a, BcNum *b, BcNum *c, size_t scale) {
Gavin Howard6fbdb292018-02-27 15:44:48 -0700430
431 BcStatus status;
Gavin Howard9c4358c2018-03-22 20:11:28 -0600432 BcNum c1, c2;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700433 size_t len;
434
Gavin Howard8b254872018-03-14 01:13:35 -0600435 if (!b->len) return BC_STATUS_MATH_DIVIDE_BY_ZERO;
436
437 if (!a->len) {
438 bc_num_zero(c);
439 return BC_STATUS_SUCCESS;
440 }
441
Gavin Howard6fbdb292018-02-27 15:44:48 -0700442 len = a->len + b->len + scale;
443
Gavin Howard9c4358c2018-03-22 20:11:28 -0600444 if ((status = bc_num_init(&c1, len))) return status;
445 if ((status = bc_num_init(&c2, len))) goto c2_err;
446 if ((status = bc_num_div(a, b, &c1, scale))) goto err;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700447
448 c->rdx = BC_MAX(scale + b->rdx, a->rdx);
Gavin Howard9c4358c2018-03-22 20:11:28 -0600449 if ((status = bc_num_mul(&c1, b, &c2, scale))) goto err;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700450 status = bc_num_sub(a, &c2, c, scale);
451
452err:
Gavin Howard6fbdb292018-02-27 15:44:48 -0700453 bc_num_free(&c2);
Gavin Howard6fbdb292018-02-27 15:44:48 -0700454c2_err:
Gavin Howard6fbdb292018-02-27 15:44:48 -0700455 bc_num_free(&c1);
Gavin Howard6fbdb292018-02-27 15:44:48 -0700456 return status;
457}
458
Gavin Howard3f68df72018-03-22 20:30:27 -0600459BcStatus bc_num_alg_p(BcNum *a, BcNum *b, BcNum *c, size_t scale) {
Gavin Howard6fbdb292018-02-27 15:44:48 -0700460
461 BcStatus status;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700462 BcNum copy;
Gavin Howard9c4358c2018-03-22 20:11:28 -0600463 unsigned long pow;
464 size_t i, powrdx, resrdx;
465 bool neg, zero;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700466
Gavin Howard23958582018-03-03 09:30:20 -0700467 if (b->rdx) return BC_STATUS_MATH_NON_INTEGER;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700468
Gavin Howard9c4358c2018-03-22 20:11:28 -0600469 if (!b->len) {
Gavin Howard6fbdb292018-02-27 15:44:48 -0700470 bc_num_one(c);
471 return BC_STATUS_SUCCESS;
472 }
Gavin Howardeb9a8222018-03-13 09:25:56 -0600473 else if (!a->len) {
Gavin Howard1d959152018-03-03 23:33:13 -0700474 bc_num_zero(c);
475 return BC_STATUS_SUCCESS;
476 }
Gavin Howard9c4358c2018-03-22 20:11:28 -0600477 else if (BC_NUM_ONE(b)) {
Gavin Howard6fbdb292018-02-27 15:44:48 -0700478
Gavin Howard9c4358c2018-03-22 20:11:28 -0600479 if (!b->neg) status = bc_num_copy(c, a);
480 else status = bc_num_inv(a, c, scale);
Gavin Howard5fc40e72018-03-05 12:11:09 -0700481
482 return status;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700483 }
Gavin Howard6fbdb292018-02-27 15:44:48 -0700484
Gavin Howard9c4358c2018-03-22 20:11:28 -0600485 neg = b->neg;
486 b->neg = false;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700487
Gavin Howard9c4358c2018-03-22 20:11:28 -0600488 if ((status = bc_num_ulong(b, &pow))) return status;
489 if ((status = bc_num_init(&copy, a->len))) return status;
490 if ((status = bc_num_copy(&copy, a))) goto err;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700491
Gavin Howardb29674f2018-03-22 22:24:58 -0600492 if (!neg) scale = BC_MIN(a->rdx * pow, BC_MAX(scale, a->rdx));
493
494 b->neg = neg;
495
Gavin Howard0dfe2922018-05-22 13:57:02 -0600496 for (powrdx = a->rdx; !bcg.signe && !(pow & 1); pow >>= 1) {
Gavin Howard1d959152018-03-03 23:33:13 -0700497 powrdx <<= 1;
Gavin Howard9c4358c2018-03-22 20:11:28 -0600498 if ((status = bc_num_mul(&copy, &copy, &copy, powrdx))) goto err;
Gavin Howard1d959152018-03-03 23:33:13 -0700499 }
Gavin Howard6fb635f2018-03-03 12:45:42 -0700500
Gavin Howard9c4358c2018-03-22 20:11:28 -0600501 if ((status = bc_num_copy(c, &copy))) goto err;
Gavin Howard0dfe2922018-05-22 13:57:02 -0600502 if (bcg.signe) {
503 status = BC_STATUS_EXEC_SIGNAL;
504 goto err;
505 }
Gavin Howard6fb635f2018-03-03 12:45:42 -0700506
Gavin Howard1d959152018-03-03 23:33:13 -0700507 resrdx = powrdx;
Gavin Howard6fb635f2018-03-03 12:45:42 -0700508
Gavin Howard0dfe2922018-05-22 13:57:02 -0600509 for (pow >>= 1; !bcg.signe && pow != 0; pow >>= 1) {
Gavin Howard1d959152018-03-03 23:33:13 -0700510
511 powrdx <<= 1;
512
Gavin Howard9c4358c2018-03-22 20:11:28 -0600513 if ((status = bc_num_mul(&copy, &copy, &copy, powrdx))) goto err;
Gavin Howard1d959152018-03-03 23:33:13 -0700514
Gavin Howard9c4358c2018-03-22 20:11:28 -0600515 if (pow & 1) {
Gavin Howard1d959152018-03-03 23:33:13 -0700516 resrdx += powrdx;
Gavin Howard9c4358c2018-03-22 20:11:28 -0600517 if ((status = bc_num_mul(c, &copy, c, resrdx))) goto err;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700518 }
Gavin Howard6fbdb292018-02-27 15:44:48 -0700519 }
520
Gavin Howardb29674f2018-03-22 22:24:58 -0600521 if (neg && (status = bc_num_inv(c, c, scale))) goto err;
Gavin Howard0dfe2922018-05-22 13:57:02 -0600522 if (bcg.signe) {
523 status = BC_STATUS_EXEC_SIGNAL;
524 goto err;
525 }
Gavin Howard6fbdb292018-02-27 15:44:48 -0700526
Gavin Howarda50fc542018-03-29 17:25:38 -0600527 if (c->rdx > scale) bc_num_truncate(c, c->rdx - scale);
Gavin Howard1d959152018-03-03 23:33:13 -0700528
Gavin Howardb29674f2018-03-22 22:24:58 -0600529 for (zero = true, i = 0; zero && i < c->len; ++i) zero = !c->num[i];
Gavin Howard1d959152018-03-03 23:33:13 -0700530 if (zero) bc_num_zero(c);
531
Gavin Howard6fbdb292018-02-27 15:44:48 -0700532err:
Gavin Howard1d959152018-03-03 23:33:13 -0700533 bc_num_free(&copy);
Gavin Howard6fbdb292018-02-27 15:44:48 -0700534 return status;
535}
536
Gavin Howard3f68df72018-03-22 20:30:27 -0600537BcStatus bc_num_binary(BcNum *a, BcNum *b, BcNum *c, size_t scale,
Gavin Howard6e0f3c52018-08-27 17:28:22 -0600538 BcNumBinaryOp op, size_t req)
Gavin Howard6fbdb292018-02-27 15:44:48 -0700539{
540 BcStatus status;
Gavin Howard6b5f5bb2018-03-22 23:16:57 -0600541 BcNum num2, *ptr_a, *ptr_b;
Gavin Howard45ff3572018-05-15 16:33:58 -0600542 bool init = false;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700543
Gavin Howard27fdfb92018-03-21 07:56:59 -0600544 assert(a && b && c && op);
Gavin Howard6fbdb292018-02-27 15:44:48 -0700545
Gavin Howard6fbdb292018-02-27 15:44:48 -0700546 if (c == a) {
Gavin Howard1cc23f32018-02-28 16:09:33 -0700547 memcpy(&num2, c, sizeof(BcNum));
548 ptr_a = &num2;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700549 init = true;
550 }
551 else ptr_a = a;
552
553 if (c == b) {
554
555 if (c == a) {
556 ptr_b = ptr_a;
557 }
558 else {
Gavin Howard1cc23f32018-02-28 16:09:33 -0700559 memcpy(&num2, c, sizeof(BcNum));
560 ptr_b = &num2;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700561 init = true;
562 }
563 }
564 else ptr_b = b;
565
566 if (init) status = bc_num_init(c, req);
567 else status = bc_num_expand(c, req);
568
Gavin Howard9c4358c2018-03-22 20:11:28 -0600569 if (status) goto err;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700570 status = op(ptr_a, ptr_b, c, scale);
571
Gavin Howard9c4358c2018-03-22 20:11:28 -0600572err:
Gavin Howard1cc23f32018-02-28 16:09:33 -0700573 if (c == a || c == b) bc_num_free(&num2);
Gavin Howard6fbdb292018-02-27 15:44:48 -0700574 return status;
575}
576
Gavin Howard3f68df72018-03-22 20:30:27 -0600577bool bc_num_strValid(const char *val, size_t base) {
Gavin Howard6fbdb292018-02-27 15:44:48 -0700578
Gavin Howard9c4358c2018-03-22 20:11:28 -0600579 size_t len, i;
Gavin Howardf6e3fb32018-08-09 13:48:59 -0600580 BcDigit b;
Gavin Howard9c4358c2018-03-22 20:11:28 -0600581 bool small, radix;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700582
583 radix = false;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700584 len = strlen(val);
585
586 if (!len) return true;
587
Gavin Howard9c4358c2018-03-22 20:11:28 -0600588 small = base <= 10;
Gavin Howardf6e3fb32018-08-09 13:48:59 -0600589 b = (BcDigit) (small ? base + '0' : base - 9 + 'A');
Gavin Howard6fbdb292018-02-27 15:44:48 -0700590
Gavin Howard9c4358c2018-03-22 20:11:28 -0600591 for (i = 0; i < len; ++i) {
Gavin Howard6fbdb292018-02-27 15:44:48 -0700592
Gavin Howardf6e3fb32018-08-09 13:48:59 -0600593 BcDigit c;
594
Gavin Howard45ff3572018-05-15 16:33:58 -0600595 if ((c = val[i]) == '.') {
Gavin Howard6fbdb292018-02-27 15:44:48 -0700596
Gavin Howard9c4358c2018-03-22 20:11:28 -0600597 if (radix) return false;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700598
Gavin Howard9c4358c2018-03-22 20:11:28 -0600599 radix = true;
600 continue;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700601 }
Gavin Howard6fbdb292018-02-27 15:44:48 -0700602
Gavin Howard9c4358c2018-03-22 20:11:28 -0600603 if (c < '0' || (small && c >= b) || (c > '9' && (c < 'A' || c >= b)))
604 return false;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700605 }
606
607 return true;
608}
609
Gavin Howard3f68df72018-03-22 20:30:27 -0600610BcStatus bc_num_parseDecimal(BcNum *n, const char *val) {
Gavin Howard6fbdb292018-02-27 15:44:48 -0700611
612 BcStatus status;
Gavin Howard9c4358c2018-03-22 20:11:28 -0600613 size_t len, i;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700614 const char *ptr;
Gavin Howard45ff3572018-05-15 16:33:58 -0600615 bool zero = true;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700616
617 for (i = 0; val[i] == '0'; ++i);
618
619 val += i;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700620 len = strlen(val);
Gavin Howard6fbdb292018-02-27 15:44:48 -0700621 bc_num_zero(n);
622
623 if (len) {
Gavin Howard9c4358c2018-03-22 20:11:28 -0600624 for (i = 0; zero && i < len; ++i) zero = val[i] == '0' || val[i] == '.';
625 if ((status = bc_num_expand(n, len))) return status;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700626 }
Gavin Howard9c4358c2018-03-22 20:11:28 -0600627
628 if (zero) {
Gavin Howard021150b2018-03-10 15:40:42 -0700629 memset(n->num, 0, sizeof(BcDigit) * n->cap);
Gavin Howard6fbdb292018-02-27 15:44:48 -0700630 n->neg = false;
631 return BC_STATUS_SUCCESS;
632 }
633
634 ptr = strchr(val, '.');
635
Gavin Howardb29674f2018-03-22 22:24:58 -0600636 // Explicitly test for NULL here to produce either a 0 or 1.
Gavin Howardf6e3fb32018-08-09 13:48:59 -0600637 n->rdx = (size_t) ((ptr != NULL) * ((val + len) - (ptr + 1)));
Gavin Howard6fbdb292018-02-27 15:44:48 -0700638
Gavin Howard595886d2018-03-28 19:57:37 -0600639 for (i = len - 1; i < len; ++n->len, i -= 1 + (i && val[i - 1] == '.'))
Gavin Howardb29674f2018-03-22 22:24:58 -0600640 n->num[n->len] = val[i] - '0';
Gavin Howard6fbdb292018-02-27 15:44:48 -0700641
642 return BC_STATUS_SUCCESS;
643}
644
Gavin Howard3f68df72018-03-22 20:30:27 -0600645BcStatus bc_num_parseBase(BcNum *n, const char *val, BcNum *base) {
Gavin Howardede51f02018-03-02 12:30:00 -0700646
647 BcStatus status;
Gavin Howard9c4358c2018-03-22 20:11:28 -0600648 BcNum temp, mult, result;
649 size_t i, len, digits;
Gavin Howardf6e3fb32018-08-09 13:48:59 -0600650 BcDigit c = '\0';
Gavin Howardede51f02018-03-02 12:30:00 -0700651 bool zero;
Gavin Howard5b24c3f2018-03-13 23:57:15 -0600652 unsigned long v;
Gavin Howardede51f02018-03-02 12:30:00 -0700653
654 len = strlen(val);
Gavin Howardb29674f2018-03-22 22:24:58 -0600655 bc_num_zero(n);
Gavin Howardede51f02018-03-02 12:30:00 -0700656
Gavin Howard9c4358c2018-03-22 20:11:28 -0600657 for (zero = true, i = 0; zero && i < len; ++i)
658 zero = (val[i] == '.' || val[i] == '0');
Gavin Howardb29674f2018-03-22 22:24:58 -0600659 if (zero) return BC_STATUS_SUCCESS;
Gavin Howardede51f02018-03-02 12:30:00 -0700660
Gavin Howard9c4358c2018-03-22 20:11:28 -0600661 if ((status = bc_num_init(&temp, BC_NUM_DEF_SIZE))) return status;
662 if ((status = bc_num_init(&mult, BC_NUM_DEF_SIZE))) goto mult_err;
Gavin Howardede51f02018-03-02 12:30:00 -0700663
Gavin Howardede51f02018-03-02 12:30:00 -0700664 for (i = 0; i < len && (c = val[i]) != '.'; ++i) {
665
Gavin Howardf6e3fb32018-08-09 13:48:59 -0600666 v = (unsigned long) (c <= '9' ? c - '0' : c - 'A' + 10);
Gavin Howardfcb6ebb2018-03-09 10:41:06 -0700667
Gavin Howard9c4358c2018-03-22 20:11:28 -0600668 if ((status = bc_num_mul(n, base, &mult, 0))) goto int_err;
669 if ((status = bc_num_ulong2num(&temp, v))) goto int_err;
670 if ((status = bc_num_add(&mult, &temp, n, 0))) goto int_err;
Gavin Howardede51f02018-03-02 12:30:00 -0700671 }
672
Gavin Howardb29674f2018-03-22 22:24:58 -0600673 if (i == len && !(c = val[i])) goto int_err;
Gavin Howardede51f02018-03-02 12:30:00 -0700674 assert(c == '.');
Gavin Howard9c4358c2018-03-22 20:11:28 -0600675 if ((status = bc_num_init(&result, base->len))) goto int_err;
Gavin Howardede51f02018-03-02 12:30:00 -0700676
Gavin Howardede51f02018-03-02 12:30:00 -0700677 bc_num_zero(&result);
678 bc_num_one(&mult);
679
Gavin Howardb29674f2018-03-22 22:24:58 -0600680 for (i += 1, digits = 0; i < len && (c = val[i]); ++i, ++digits) {
Gavin Howardede51f02018-03-02 12:30:00 -0700681
Gavin Howardf6e3fb32018-08-09 13:48:59 -0600682 v = (unsigned long) (c <= '9' ? c - '0' : c - 'A' + 10);
Gavin Howardede51f02018-03-02 12:30:00 -0700683
Gavin Howard9c4358c2018-03-22 20:11:28 -0600684 if ((status = bc_num_mul(&result, base, &result, 0))) goto err;
685 if ((status = bc_num_ulong2num(&temp, v))) goto err;
686 if ((status = bc_num_add(&result, &temp, &result, 0))) goto err;
687 if ((status = bc_num_mul(&mult, base, &mult, 0))) goto err;
Gavin Howardede51f02018-03-02 12:30:00 -0700688 }
689
Gavin Howard9c4358c2018-03-22 20:11:28 -0600690 if ((status = bc_num_div(&result, &mult, &result, digits))) goto err;
691 if ((status = bc_num_add(n, &result, n, digits))) goto err;
Gavin Howard5b24c3f2018-03-13 23:57:15 -0600692
693 if (n->len) {
Gavin Howard9c4358c2018-03-22 20:11:28 -0600694 if (n->rdx < digits && n->len) status = bc_num_extend(n, digits - n->rdx);
Gavin Howard5b24c3f2018-03-13 23:57:15 -0600695 }
696 else bc_num_zero(n);
Gavin Howardede51f02018-03-02 12:30:00 -0700697
698err:
Gavin Howardede51f02018-03-02 12:30:00 -0700699 bc_num_free(&result);
Gavin Howardede51f02018-03-02 12:30:00 -0700700int_err:
Gavin Howardede51f02018-03-02 12:30:00 -0700701 bc_num_free(&mult);
Gavin Howardede51f02018-03-02 12:30:00 -0700702mult_err:
Gavin Howardede51f02018-03-02 12:30:00 -0700703 bc_num_free(&temp);
Gavin Howardede51f02018-03-02 12:30:00 -0700704 return status;
Gavin Howard6fbdb292018-02-27 15:44:48 -0700705}
706
Gavin Howard902a16c2018-05-16 01:22:53 -0600707BcStatus bc_num_printDigits(size_t num, size_t width, bool radix,
Gavin Howard0011a3a2018-03-29 23:11:32 -0600708 size_t *nchars, size_t line_len)
Gavin Howard2682a1f2018-03-03 09:09:13 -0700709{
Gavin Howardf6e3fb32018-08-09 13:48:59 -0600710 size_t exp, pow;
Gavin Howard2ed2bfb2018-03-14 02:42:32 -0600711
Gavin Howard0011a3a2018-03-29 23:11:32 -0600712 if (*nchars == line_len - 1) {
Gavin Howarda141a0f2018-03-23 13:16:10 -0600713 if (putchar('\\') == EOF) return BC_STATUS_IO_ERR;
714 if (putchar('\n') == EOF) return BC_STATUS_IO_ERR;
Gavin Howard2682a1f2018-03-03 09:09:13 -0700715 *nchars = 0;
716 }
Gavin Howard2ed2bfb2018-03-14 02:42:32 -0600717
Gavin Howarda141a0f2018-03-23 13:16:10 -0600718 if (*nchars || radix) {
719 if (putchar(radix ? '.' : ' ') == EOF) return BC_STATUS_IO_ERR;
720 ++(*nchars);
721 }
Gavin Howard2ed2bfb2018-03-14 02:42:32 -0600722
723 for (exp = 0, pow = 1; exp < width - 1; ++exp, pow *= 10);
724
Gavin Howarda141a0f2018-03-23 13:16:10 -0600725 for (exp = 0; exp < width; pow /= 10, ++(*nchars), ++exp) {
Gavin Howard2ed2bfb2018-03-14 02:42:32 -0600726
Gavin Howardf6e3fb32018-08-09 13:48:59 -0600727 size_t div;
728
Gavin Howard0011a3a2018-03-29 23:11:32 -0600729 if (*nchars == line_len - 1) {
Gavin Howarda141a0f2018-03-23 13:16:10 -0600730 if (putchar('\\') == EOF) return BC_STATUS_IO_ERR;
731 if (putchar('\n') == EOF) return BC_STATUS_IO_ERR;
Gavin Howard2ed2bfb2018-03-14 02:42:32 -0600732 *nchars = 0;
733 }
734
735 div = num / pow;
Gavin Howard2ed2bfb2018-03-14 02:42:32 -0600736 num -= div * pow;
Gavin Howard9c4358c2018-03-22 20:11:28 -0600737
Gavin Howarda141a0f2018-03-23 13:16:10 -0600738 if (putchar(((char) div) + '0') == EOF) return BC_STATUS_IO_ERR;
Gavin Howard2682a1f2018-03-03 09:09:13 -0700739 }
740
Gavin Howard6fbdb292018-02-27 15:44:48 -0700741 return BC_STATUS_SUCCESS;
742}
Gavin Howardfe679f02018-02-14 15:50:09 -0700743
Gavin Howard902a16c2018-05-16 01:22:53 -0600744BcStatus bc_num_printHex(size_t num, size_t width, bool radix,
Gavin Howard0011a3a2018-03-29 23:11:32 -0600745 size_t *nchars, size_t line_len)
Gavin Howard2682a1f2018-03-03 09:09:13 -0700746{
Gavin Howard881645c2018-03-14 01:44:20 -0600747 width += !!radix;
Gavin Howard0011a3a2018-03-29 23:11:32 -0600748 if (*nchars + width >= line_len) {
Gavin Howarda141a0f2018-03-23 13:16:10 -0600749 if (putchar('\\') == EOF) return BC_STATUS_IO_ERR;
750 if (putchar('\n') == EOF) return BC_STATUS_IO_ERR;
Gavin Howard2682a1f2018-03-03 09:09:13 -0700751 *nchars = 0;
752 }
753
Gavin Howarda141a0f2018-03-23 13:16:10 -0600754 if (radix && putchar('.') == EOF) return BC_STATUS_IO_ERR;
755 if (putchar(bc_num_hex_digits[num]) == EOF) return BC_STATUS_IO_ERR;
Gavin Howard2682a1f2018-03-03 09:09:13 -0700756
757 *nchars = *nchars + width;
758
759 return BC_STATUS_SUCCESS;
760}
761
Gavin Howard6e0f3c52018-08-27 17:28:22 -0600762BcStatus bc_num_printDecimal(BcNum *n, size_t *nchs, size_t len) {
Gavin Howard32f2beb2018-03-09 11:43:20 -0700763
764 BcStatus status;
765 size_t i;
Gavin Howard32f2beb2018-03-09 11:43:20 -0700766
Gavin Howard32f2beb2018-03-09 11:43:20 -0700767 if (n->neg) {
Gavin Howarda141a0f2018-03-23 13:16:10 -0600768 if (putchar('-') == EOF) return BC_STATUS_IO_ERR;
Gavin Howard6e0f3c52018-08-27 17:28:22 -0600769 ++(*nchs);
Gavin Howard32f2beb2018-03-09 11:43:20 -0700770 }
771
772 status = BC_STATUS_SUCCESS;
773
Gavin Howard9c4358c2018-03-22 20:11:28 -0600774 for (i = n->len - 1; !status && i < n->len; --i)
Gavin Howard6e0f3c52018-08-27 17:28:22 -0600775 status = bc_num_printHex((size_t) n->num[i], 1, i == n->rdx - 1, nchs, len);
Gavin Howard32f2beb2018-03-09 11:43:20 -0700776
777 return status;
778}
779
Gavin Howard0011a3a2018-03-29 23:11:32 -0600780BcStatus bc_num_printBase(BcNum *n, BcNum *base, size_t base_t,
781 size_t *nchars, size_t line_len)
Gavin Howardbc7cae82018-03-14 13:43:04 -0600782{
Gavin Howard2682a1f2018-03-03 09:09:13 -0700783 BcStatus status;
784 BcVec stack;
Gavin Howard9c4358c2018-03-22 20:11:28 -0600785 BcNum intp, fracp, digit, frac_len;
786 size_t width, i;
Gavin Howard6e0f3c52018-08-27 17:28:22 -0600787 BcNumDigitOp print;
Gavin Howard6b5f5bb2018-03-22 23:16:57 -0600788 unsigned long dig, *ptr;
Gavin Howard53ed5bb2018-03-14 01:15:59 -0600789 bool neg, radix;
Gavin Howard2682a1f2018-03-03 09:09:13 -0700790
Gavin Howard53ed5bb2018-03-14 01:15:59 -0600791 neg = n->neg;
792 n->neg = false;
Gavin Howard2682a1f2018-03-03 09:09:13 -0700793
Gavin Howard45ff3572018-05-15 16:33:58 -0600794 if (neg && putchar('-') == EOF) return BC_STATUS_IO_ERR;
795 nchars += neg;
Gavin Howard2682a1f2018-03-03 09:09:13 -0700796
Gavin Howardcb3ce022018-03-21 09:13:24 -0600797 if (base_t <= BC_NUM_MAX_INPUT_BASE) {
Gavin Howard2682a1f2018-03-03 09:09:13 -0700798 width = 1;
799 print = bc_num_printHex;
800 }
801 else {
802 width = (size_t) floor(log10((double) (base_t - 1)) + 1.0);
803 print = bc_num_printDigits;
804 }
805
Gavin Howard9c4358c2018-03-22 20:11:28 -0600806 if ((status = bc_vec_init(&stack, sizeof(long), NULL))) return status;
807 if ((status = bc_num_init(&intp, n->len))) goto int_err;
808 if ((status = bc_num_init(&fracp, n->rdx))) goto frac_err;
809 if ((status = bc_num_init(&digit, width))) goto digit_err;
810 if ((status = bc_num_copy(&intp, n))) goto frac_len_err;
Gavin Howarda50fc542018-03-29 17:25:38 -0600811
812 bc_num_truncate(&intp, intp.rdx);
Gavin Howard9c4358c2018-03-22 20:11:28 -0600813 if ((status = bc_num_sub(n, &intp, &fracp, 0))) goto frac_len_err;
Gavin Howard2682a1f2018-03-03 09:09:13 -0700814
Gavin Howard53ed5bb2018-03-14 01:15:59 -0600815 while (intp.len) {
Gavin Howard9c4358c2018-03-22 20:11:28 -0600816 if ((status = bc_num_mod(&intp, base, &digit, 0))) goto frac_len_err;
817 if ((status = bc_num_ulong(&digit, &dig))) goto frac_len_err;
818 if ((status = bc_vec_push(&stack, &dig))) goto frac_len_err;
819 if ((status = bc_num_div(&intp, base, &intp, 0))) goto frac_len_err;
Gavin Howard2682a1f2018-03-03 09:09:13 -0700820 }
821
822 for (i = 0; i < stack.len; ++i) {
Gavin Howard2682a1f2018-03-03 09:09:13 -0700823 ptr = bc_vec_item_rev(&stack, i);
Gavin Howard9c4358c2018-03-22 20:11:28 -0600824 assert(ptr);
Gavin Howard0011a3a2018-03-29 23:11:32 -0600825 status = print(*ptr, width, false, nchars, line_len);
826 if (status) goto frac_len_err;
Gavin Howard2682a1f2018-03-03 09:09:13 -0700827 }
828
Gavin Howard45ff3572018-05-15 16:33:58 -0600829 if (!n->rdx || (status = bc_num_init(&frac_len, n->len - n->rdx)))
830 goto frac_len_err;
Gavin Howard2682a1f2018-03-03 09:09:13 -0700831
832 bc_num_one(&frac_len);
833
Gavin Howard1819ecc2018-03-14 01:15:41 -0600834 for (radix = true; frac_len.len <= n->rdx; radix = false) {
Gavin Howard9c4358c2018-03-22 20:11:28 -0600835 if ((status = bc_num_mul(&fracp, base, &fracp, n->rdx))) goto err;
836 if ((status = bc_num_ulong(&fracp, &dig))) goto err;
837 if ((status = bc_num_ulong2num(&intp, dig))) goto err;
838 if ((status = bc_num_sub(&fracp, &intp, &fracp, 0))) goto err;
Gavin Howard0011a3a2018-03-29 23:11:32 -0600839 if ((status = print(dig, width, radix, nchars, line_len))) goto err;
Gavin Howard9c4358c2018-03-22 20:11:28 -0600840 if ((status = bc_num_mul(&frac_len, base, &frac_len, 0))) goto err;
Gavin Howard2682a1f2018-03-03 09:09:13 -0700841 }
842
843err:
Gavin Howard53ed5bb2018-03-14 01:15:59 -0600844 n->neg = neg;
Gavin Howard2682a1f2018-03-03 09:09:13 -0700845 bc_num_free(&frac_len);
Gavin Howard2682a1f2018-03-03 09:09:13 -0700846frac_len_err:
Gavin Howard2682a1f2018-03-03 09:09:13 -0700847 bc_num_free(&digit);
Gavin Howard2682a1f2018-03-03 09:09:13 -0700848digit_err:
Gavin Howard2682a1f2018-03-03 09:09:13 -0700849 bc_num_free(&fracp);
Gavin Howard2682a1f2018-03-03 09:09:13 -0700850frac_err:
Gavin Howard2682a1f2018-03-03 09:09:13 -0700851 bc_num_free(&intp);
Gavin Howard2682a1f2018-03-03 09:09:13 -0700852int_err:
Gavin Howard2682a1f2018-03-03 09:09:13 -0700853 bc_vec_free(&stack);
Gavin Howard2682a1f2018-03-03 09:09:13 -0700854 return status;
855}
856
Gavin Howard8d1f1db2018-02-23 11:29:41 -0700857BcStatus bc_num_init(BcNum *n, size_t request) {
Gavin Howardb5c77212018-02-14 17:12:34 -0700858
Gavin Howard27fdfb92018-03-21 07:56:59 -0600859 assert(n);
Gavin Howardb5c77212018-02-14 17:12:34 -0700860
Gavin Howard0b465d02018-02-15 11:41:16 -0700861 memset(n, 0, sizeof(BcNum));
Gavin Howardb5c77212018-02-14 17:12:34 -0700862
Gavin Howard5d74e962018-02-26 13:44:13 -0700863 request = request >= BC_NUM_DEF_SIZE ? request : BC_NUM_DEF_SIZE;
Gavin Howard9c4358c2018-03-22 20:11:28 -0600864 if (!(n->num = malloc(request))) return BC_STATUS_MALLOC_FAIL;
Gavin Howardb5c77212018-02-14 17:12:34 -0700865
Gavin Howard6f6dc942018-02-15 16:58:50 -0700866 n->cap = request;
Gavin Howardb5c77212018-02-14 17:12:34 -0700867
868 return BC_STATUS_SUCCESS;
869}
870
Gavin Howard8d1f1db2018-02-23 11:29:41 -0700871BcStatus bc_num_expand(BcNum *n, size_t request) {
Gavin Howardb5c77212018-02-14 17:12:34 -0700872
Gavin Howard9c4358c2018-03-22 20:11:28 -0600873 BcDigit *temp;
874
Gavin Howard27fdfb92018-03-21 07:56:59 -0600875 assert(n && request);
Gavin Howardb5c77212018-02-14 17:12:34 -0700876
Gavin Howard9c4358c2018-03-22 20:11:28 -0600877 if (request <= n->cap) return BC_STATUS_SUCCESS;
Gavin Howard9c4358c2018-03-22 20:11:28 -0600878 if (!(temp = realloc(n->num, request))) return BC_STATUS_MALLOC_FAIL;
Gavin Howardb5c77212018-02-14 17:12:34 -0700879
Gavin Howard9c4358c2018-03-22 20:11:28 -0600880 memset(temp + n->cap, 0, sizeof(char) * (request - n->cap));
Gavin Howard9c4358c2018-03-22 20:11:28 -0600881 n->num = temp;
882 n->cap = request;
Gavin Howardb5c77212018-02-14 17:12:34 -0700883
884 return BC_STATUS_SUCCESS;
885}
886
Gavin Howarded392aa2018-02-27 13:09:26 -0700887void bc_num_free(void *num) {
Gavin Howard9c4358c2018-03-22 20:11:28 -0600888 BcNum *n = (BcNum*) num;
Gavin Howardd9734e52018-03-29 15:54:41 -0600889 if (n && n->num) free(n->num);
Gavin Howardb5c77212018-02-14 17:12:34 -0700890}
891
Gavin Howard9c4358c2018-03-22 20:11:28 -0600892BcStatus bc_num_copy(BcNum *d, BcNum *s) {
Gavin Howard5a049c42018-02-15 11:24:11 -0700893
894 BcStatus status;
Gavin Howard5a049c42018-02-15 11:24:11 -0700895
Gavin Howard9c4358c2018-03-22 20:11:28 -0600896 assert(d && s);
Gavin Howardf23448c2018-02-27 20:36:24 -0700897
Gavin Howard9c4358c2018-03-22 20:11:28 -0600898 if (d == s) return BC_STATUS_SUCCESS;
Gavin Howard9c4358c2018-03-22 20:11:28 -0600899 if ((status = bc_num_expand(d, s->cap))) return status;
Gavin Howard5a049c42018-02-15 11:24:11 -0700900
901 d->len = s->len;
902 d->neg = s->neg;
Gavin Howard8389bb22018-02-15 17:40:34 -0700903 d->rdx = s->rdx;
Gavin Howard5a049c42018-02-15 11:24:11 -0700904
Gavin Howard188b4ac2018-03-24 14:49:19 -0600905 memcpy(d->num, s->num, sizeof(BcDigit) * d->len);
906 memset(d->num + d->len, 0, sizeof(BcDigit) * (d->cap - d->len));
Gavin Howard5a049c42018-02-15 11:24:11 -0700907
908 return BC_STATUS_SUCCESS;
909}
910
Gavin Howard5cea43b2018-03-02 11:32:11 -0700911BcStatus bc_num_parse(BcNum *n, const char *val, BcNum *base, size_t base_t) {
Gavin Howard025d04d2018-02-20 13:53:28 -0700912
Gavin Howard3eb626f2018-02-14 13:54:35 -0700913 BcStatus status;
914
Gavin Howard27fdfb92018-03-21 07:56:59 -0600915 assert(n && val && base && base_t >= BC_NUM_MIN_BASE &&
916 base_t <= BC_NUM_MAX_INPUT_BASE);
Gavin Howard3eb626f2018-02-14 13:54:35 -0700917
Gavin Howardbdb2f922018-03-15 09:25:18 -0600918 if (!bc_num_strValid(val, base_t)) return BC_STATUS_MATH_BAD_STRING;
Gavin Howard3eb626f2018-02-14 13:54:35 -0700919
Gavin Howard5cea43b2018-03-02 11:32:11 -0700920 if (base_t == 10) status = bc_num_parseDecimal(n, val);
Gavin Howardede51f02018-03-02 12:30:00 -0700921 else status = bc_num_parseBase(n, val, base);
Gavin Howard3eb626f2018-02-14 13:54:35 -0700922
923 return status;
924}
925
Gavin Howard0011a3a2018-03-29 23:11:32 -0600926BcStatus bc_num_print(BcNum *n, BcNum *base, size_t base_t, bool newline,
927 size_t *nchars, size_t line_len)
Gavin Howard152f3e82018-03-07 12:33:15 -0700928{
Gavin Howard3eb626f2018-02-14 13:54:35 -0700929 BcStatus status;
930
Gavin Howard9c4358c2018-03-22 20:11:28 -0600931 assert(n && base && nchars && base_t >= BC_NUM_MIN_BASE &&
Gavin Howard0dd566d2018-03-28 19:29:34 -0600932 base_t <= BC_MAX_BASE);
Gavin Howard3eb626f2018-02-14 13:54:35 -0700933
Gavin Howard0011a3a2018-03-29 23:11:32 -0600934 if (*nchars >= line_len) {
Gavin Howarda141a0f2018-03-23 13:16:10 -0600935 if (putchar('\\') == EOF) return BC_STATUS_IO_ERR;
936 if (putchar('\n') == EOF) return BC_STATUS_IO_ERR;
Gavin Howardbc7cae82018-03-14 13:43:04 -0600937 *nchars = 0;
938 }
939
Gavin Howardeb9a8222018-03-13 09:25:56 -0600940 if (!n->len) {
Gavin Howarda141a0f2018-03-23 13:16:10 -0600941 if (putchar('0') == EOF) return BC_STATUS_IO_ERR;
Gavin Howardbc7cae82018-03-14 13:43:04 -0600942 ++(*nchars);
Gavin Howardec8f8f12018-03-03 09:47:19 -0700943 status = BC_STATUS_SUCCESS;
Gavin Howard9576c382018-03-03 08:11:53 -0700944 }
Gavin Howard0011a3a2018-03-29 23:11:32 -0600945 else if (base_t == 10) status = bc_num_printDecimal(n, nchars, line_len);
946 else status = bc_num_printBase(n, base, base_t, nchars, line_len);
Gavin Howard3eb626f2018-02-14 13:54:35 -0700947
Gavin Howard152f3e82018-03-07 12:33:15 -0700948 if (status) return status;
949
950 if (newline) {
Gavin Howarda141a0f2018-03-23 13:16:10 -0600951 if (putchar('\n') == EOF) return BC_STATUS_IO_ERR;
Gavin Howardbc7cae82018-03-14 13:43:04 -0600952 *nchars = 0;
Gavin Howard152f3e82018-03-07 12:33:15 -0700953 }
954
Gavin Howard3eb626f2018-02-14 13:54:35 -0700955 return status;
956}
957
Gavin Howard8d1f1db2018-02-23 11:29:41 -0700958BcStatus bc_num_ulong(BcNum *n, unsigned long *result) {
Gavin Howard68b8a5c2018-02-15 11:41:24 -0700959
960 size_t i;
Gavin Howardf6e3fb32018-08-09 13:48:59 -0600961 unsigned long pow;
Gavin Howard68b8a5c2018-02-15 11:41:24 -0700962
Gavin Howard27fdfb92018-03-21 07:56:59 -0600963 assert(n && result);
Gavin Howard68b8a5c2018-02-15 11:41:24 -0700964
Gavin Howard68b8a5c2018-02-15 11:41:24 -0700965 if (n->neg) return BC_STATUS_MATH_NEGATIVE;
966
Gavin Howard45ff3572018-05-15 16:33:58 -0600967 for (*result = 0, pow = 1, i = n->rdx; i < n->len; ++i) {
Gavin Howard68b8a5c2018-02-15 11:41:24 -0700968
Gavin Howardf6e3fb32018-08-09 13:48:59 -0600969 unsigned long prev = *result;
970 *result += ((unsigned long) n->num[i]) * pow;
Gavin Howard025d04d2018-02-20 13:53:28 -0700971 pow *= 10;
Gavin Howard68b8a5c2018-02-15 11:41:24 -0700972
973 if (*result < prev) return BC_STATUS_MATH_OVERFLOW;
974 }
975
976 return BC_STATUS_SUCCESS;
977}
978
Gavin Howard8d1f1db2018-02-23 11:29:41 -0700979BcStatus bc_num_ulong2num(BcNum *n, unsigned long val) {
Gavin Howard8e2cc692018-02-15 17:39:14 -0700980
981 BcStatus status;
Gavin Howard9c4358c2018-03-22 20:11:28 -0600982 size_t len, i;
Gavin Howard021150b2018-03-10 15:40:42 -0700983 BcDigit *ptr;
Gavin Howard8e2cc692018-02-15 17:39:14 -0700984
Gavin Howard27fdfb92018-03-21 07:56:59 -0600985 assert(n);
Gavin Howard8e2cc692018-02-15 17:39:14 -0700986
Gavin Howard025d04d2018-02-20 13:53:28 -0700987 bc_num_zero(n);
988
Gavin Howard8e2cc692018-02-15 17:39:14 -0700989 if (!val) {
Gavin Howard8e2cc692018-02-15 17:39:14 -0700990 memset(n->num, 0, sizeof(char) * n->cap);
Gavin Howard8e2cc692018-02-15 17:39:14 -0700991 return BC_STATUS_SUCCESS;
992 }
993
Gavin Howardf6e3fb32018-08-09 13:48:59 -0600994 len = (size_t) ceil(log10(((double) ULONG_MAX) + 1.0));
Gavin Howard8e2cc692018-02-15 17:39:14 -0700995
Gavin Howard9c4358c2018-03-22 20:11:28 -0600996 if ((status = bc_num_expand(n, len))) return status;
Gavin Howard8e2cc692018-02-15 17:39:14 -0700997
Gavin Howard9c4358c2018-03-22 20:11:28 -0600998 for (ptr = n->num, i = 0; val; ++i, ++n->len) {
Gavin Howard025d04d2018-02-20 13:53:28 -0700999 ptr[i] = (char) (val % 10);
Gavin Howard8e2cc692018-02-15 17:39:14 -07001000 val /= 10;
Gavin Howard8e2cc692018-02-15 17:39:14 -07001001 }
1002
Gavin Howard025d04d2018-02-20 13:53:28 -07001003 return BC_STATUS_SUCCESS;
1004}
1005
Gavin Howard8d1f1db2018-02-23 11:29:41 -07001006BcStatus bc_num_add(BcNum *a, BcNum *b, BcNum *result, size_t scale) {
Gavin Howarda1c090a2018-03-05 14:20:33 -07001007 (void) scale;
Gavin Howard6e0f3c52018-08-27 17:28:22 -06001008 BcNumBinaryOp op = (!a->neg == !b->neg) ? bc_num_alg_a : bc_num_alg_s;
Gavin Howard00759132018-08-27 15:37:54 -06001009 size_t r = BC_MAX(a->rdx, b->rdx) + BC_MAX(BC_NUM_INT(a), BC_NUM_INT(b)) + 1;
1010 return bc_num_binary(a, b, result, false, op, r);
Gavin Howard3eb626f2018-02-14 13:54:35 -07001011}
1012
Gavin Howard8d1f1db2018-02-23 11:29:41 -07001013BcStatus bc_num_sub(BcNum *a, BcNum *b, BcNum *result, size_t scale) {
Gavin Howarda1c090a2018-03-05 14:20:33 -07001014 (void) scale;
Gavin Howard6e0f3c52018-08-27 17:28:22 -06001015 BcNumBinaryOp op = (!a->neg == !b->neg) ? bc_num_alg_s : bc_num_alg_a;
Gavin Howard00759132018-08-27 15:37:54 -06001016 size_t r = BC_MAX(a->rdx, b->rdx) + BC_MAX(BC_NUM_INT(a), BC_NUM_INT(b)) + 1;
1017 return bc_num_binary(a, b, result, true, op, r);
Gavin Howard3eb626f2018-02-14 13:54:35 -07001018}
1019
Gavin Howard8d1f1db2018-02-23 11:29:41 -07001020BcStatus bc_num_mul(BcNum *a, BcNum *b, BcNum *result, size_t scale) {
Gavin Howard00759132018-08-27 15:37:54 -06001021 size_t r = BC_NUM_INT(a) + BC_NUM_INT(b) + BC_MAX(scale, a->rdx + b->rdx);
1022 return bc_num_binary(a, b, result, scale, bc_num_alg_m, r);
Gavin Howard3eb626f2018-02-14 13:54:35 -07001023}
1024
Gavin Howard8d1f1db2018-02-23 11:29:41 -07001025BcStatus bc_num_div(BcNum *a, BcNum *b, BcNum *result, size_t scale) {
Gavin Howard00759132018-08-27 15:37:54 -06001026 size_t r = BC_NUM_INT(a) + BC_NUM_INT(b) + BC_MAX(scale, a->rdx + b->rdx);
1027 return bc_num_binary(a, b, result, scale, bc_num_alg_d, r);
Gavin Howard3eb626f2018-02-14 13:54:35 -07001028}
1029
Gavin Howard8d1f1db2018-02-23 11:29:41 -07001030BcStatus bc_num_mod(BcNum *a, BcNum *b, BcNum *result, size_t scale) {
Gavin Howard00759132018-08-27 15:37:54 -06001031 size_t r = BC_NUM_INT(a) + BC_NUM_INT(b) + BC_MAX(scale, a->rdx + b->rdx);
1032 return bc_num_binary(a, b, result, scale, bc_num_alg_mod, r);
Gavin Howard3eb626f2018-02-14 13:54:35 -07001033}
1034
Gavin Howard8d1f1db2018-02-23 11:29:41 -07001035BcStatus bc_num_pow(BcNum *a, BcNum *b, BcNum *result, size_t scale) {
Gavin Howard00759132018-08-27 15:37:54 -06001036 return bc_num_binary(a, b, result, scale, bc_num_alg_p, a->len * b->len + 1);
Gavin Howard3eb626f2018-02-14 13:54:35 -07001037}
1038
Gavin Howard8d1f1db2018-02-23 11:29:41 -07001039BcStatus bc_num_sqrt(BcNum *a, BcNum *result, size_t scale) {
Gavin Howard954276b2018-05-16 01:43:58 -06001040
1041 BcStatus status;
1042 BcNum a2, *ptr_a, num1, num2, two, f, fprime, *x0, *x1, *temp;
1043 size_t pow, len, digits, resrdx, req;
Gavin Howardf6e3fb32018-08-09 13:48:59 -06001044 ssize_t cmp;
Gavin Howard954276b2018-05-16 01:43:58 -06001045
1046 assert(a && result);
1047
1048 req = a->rdx + (a->len - a->rdx) * 2 + 1;
1049
1050 if (result == a) {
1051 memcpy(&a2, result, sizeof(BcNum));
1052 ptr_a = &a2;
1053 status = bc_num_init(result, req);
1054 }
1055 else {
1056 ptr_a = a;
1057 status = bc_num_expand(result, req);
1058 }
1059
1060 if (status) goto init_err;
1061
1062 if (!ptr_a->len) {
1063 bc_num_zero(result);
1064 return BC_STATUS_SUCCESS;
1065 }
1066 else if (ptr_a->neg) return BC_STATUS_MATH_NEG_SQRT;
1067 else if (BC_NUM_ONE(a)) {
1068 bc_num_one(result);
1069 return bc_num_extend(result, scale);
1070 }
1071
1072 memset(result->num, 0, result->cap * sizeof(BcDigit));
1073 len = ptr_a->len;
1074
1075 scale = BC_MAX(scale, ptr_a->rdx) + 1;
1076
1077 if ((status = bc_num_init(&num1, len))) return status;
1078 if ((status = bc_num_init(&num2, num1.len))) goto num2_err;
1079 if ((status = bc_num_init(&two, BC_NUM_DEF_SIZE))) goto two_err;
1080
1081 bc_num_one(&two);
1082 two.num[0] = 2;
1083
1084 len += scale;
1085
1086 if ((status = bc_num_init(&f, len))) goto f_err;
1087 if ((status = bc_num_init(&fprime, len + scale))) goto fprime_err;
1088
1089 x0 = &num1;
1090 x1 = &num2;
1091
1092 bc_num_one(x0);
1093
1094 pow = ptr_a->len - ptr_a->rdx;
1095
1096 if (pow) {
1097
1098 if (pow & 1) {
1099 x0->num[0] = 2;
1100 pow -= 1;
1101 }
1102 else {
1103 x0->num[0] = 6;
1104 pow -= 2;
1105 }
1106
1107 if ((status = bc_num_extend(x0, pow))) goto err;
1108 }
1109
1110 cmp = 1;
1111 x0->rdx = digits = 0;
1112 resrdx = scale + 1;
1113 len = (x0->len - x0->rdx) + resrdx;
1114
Gavin Howard0dfe2922018-05-22 13:57:02 -06001115 while (!bcg.signe && cmp && digits <= len) {
Gavin Howard954276b2018-05-16 01:43:58 -06001116
1117 if ((status = bc_num_mul(x0, x0, &f, resrdx))) goto err;
1118 if ((status = bc_num_sub(&f, a, &f, resrdx))) goto err;
1119 if ((status = bc_num_mul(x0, &two, &fprime, resrdx))) goto err;
1120 if ((status = bc_num_div(&f, &fprime, &f, resrdx))) goto err;
1121 if ((status = bc_num_sub(x0, &f, x1, resrdx))) goto err;
1122
Gavin Howard6a804cf2018-05-17 16:54:12 -06001123 cmp = bc_num_cmp(x1, x0);
Gavin Howardf6e3fb32018-08-09 13:48:59 -06001124 digits = x1->len - (unsigned long long) llabs(cmp);
Gavin Howard954276b2018-05-16 01:43:58 -06001125
1126 temp = x0;
1127 x0 = x1;
1128 x1 = temp;
1129 }
1130
Gavin Howard0dfe2922018-05-22 13:57:02 -06001131 if (bcg.signe) {
1132 status = BC_STATUS_EXEC_SIGNAL;
1133 goto err;
1134 }
1135
Gavin Howard954276b2018-05-16 01:43:58 -06001136 if ((status = bc_num_copy(result, x0))) goto err;
1137
Gavin Howard0dfe2922018-05-22 13:57:02 -06001138 if (result->rdx > --scale) bc_num_truncate(result, result->rdx - scale);
Gavin Howard954276b2018-05-16 01:43:58 -06001139 else if (result->rdx < scale)
1140 status = bc_num_extend(result, scale - result->rdx);
1141
1142err:
1143 bc_num_free(&fprime);
1144fprime_err:
1145 bc_num_free(&f);
1146f_err:
1147 bc_num_free(&two);
1148two_err:
1149 bc_num_free(&num2);
1150num2_err:
1151 bc_num_free(&num1);
1152init_err:
1153 if (result == a) bc_num_free(&a2);
1154 return status;
Gavin Howard3eb626f2018-02-14 13:54:35 -07001155}
1156
Gavin Howard43a027f2018-02-26 13:27:10 -07001157void bc_num_zero(BcNum *n) {
Gavin Howard6455db12018-02-15 17:38:20 -07001158 if (!n) return;
Gavin Howard43a027f2018-02-26 13:27:10 -07001159 memset(n->num, 0, n->cap * sizeof(char));
Gavin Howard6455db12018-02-15 17:38:20 -07001160 n->neg = false;
1161 n->len = 0;
Gavin Howard8389bb22018-02-15 17:40:34 -07001162 n->rdx = 0;
Gavin Howard6455db12018-02-15 17:38:20 -07001163}
1164
Gavin Howard43a027f2018-02-26 13:27:10 -07001165void bc_num_one(BcNum *n) {
Gavin Howardf507f232018-02-23 21:10:24 -07001166 if (!n) return;
Gavin Howard5d74e962018-02-26 13:44:13 -07001167 bc_num_zero(n);
Gavin Howardf507f232018-02-23 21:10:24 -07001168 n->len = 1;
Gavin Howardf507f232018-02-23 21:10:24 -07001169 n->num[0] = 1;
1170}
1171
Gavin Howard5d74e962018-02-26 13:44:13 -07001172void bc_num_ten(BcNum *n) {
Gavin Howard5d74e962018-02-26 13:44:13 -07001173 if (!n) return;
Gavin Howard5d74e962018-02-26 13:44:13 -07001174 bc_num_zero(n);
Gavin Howard5d74e962018-02-26 13:44:13 -07001175 n->len = 2;
1176 n->num[0] = 0;
1177 n->num[1] = 1;
1178}