1 /*
2 * *****************************************************************************
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 *
6 * Copyright (c) 2018-2021 Gavin D. Howard and contributors.
7 *
8 * 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.
29 *
30 * *****************************************************************************
31 *
32 * The public functions for libbc.
33 *
34 */
35
36 #if BC_ENABLE_LIBRARY
37
38 #include <setjmp.h>
39 #include <string.h>
40 #include <time.h>
41
42 #include <bcl.h>
43
44 #include <library.h>
45 #include <num.h>
46 #include <vm.h>
47
48 // The asserts in this file are important to testing; in many cases, the test
49 // would not work without the asserts, so don't remove them without reason.
50 //
51 // Also, there are many uses of bc_num_clear() here; that is because numbers are
52 // being reused, and a clean slate is required.
53 //
54 // Also, there are a bunch of BC_UNSETJMP and BC_SETJMP_LOCKED() between calls
55 // to bc_num_init(). That is because locals are being initialized, and unlike bc
56 // proper, this code cannot assume that allocation failures are fatal. So we
57 // have to reset the jumps every time to ensure that the locals will be correct
58 // after jumping.
59
bcl_handleSignal(void)60 void bcl_handleSignal(void) {
61
62 // Signal already in flight, or bc is not executing.
63 if (vm.sig || !vm.running) return;
64
65 vm.sig = 1;
66
67 assert(vm.jmp_bufs.len);
68
69 if (!vm.sig_lock) BC_JMP;
70 }
71
bcl_running(void)72 bool bcl_running(void) {
73 return vm.running != 0;
74 }
75
bcl_init(void)76 BclError bcl_init(void) {
77
78 BclError e = BCL_ERROR_NONE;
79
80 vm.refs += 1;
81
82 if (vm.refs > 1) return e;
83
84 // Setting these to NULL ensures that if an error occurs, we only free what
85 // is necessary.
86 vm.ctxts.v = NULL;
87 vm.jmp_bufs.v = NULL;
88 vm.out.v = NULL;
89
90 vm.abrt = false;
91
92 BC_SIG_LOCK;
93
94 // The jmp_bufs always has to be initialized first.
95 bc_vec_init(&vm.jmp_bufs, sizeof(sigjmp_buf), BC_DTOR_NONE);
96
97 BC_FUNC_HEADER_INIT(err);
98
99 bc_vm_init();
100
101 bc_vec_init(&vm.ctxts, sizeof(BclContext), BC_DTOR_NONE);
102 bc_vec_init(&vm.out, sizeof(uchar), BC_DTOR_NONE);
103
104 // We need to seed this in case /dev/random and /dev/urandm don't work.
105 srand((unsigned int) time(NULL));
106 bc_rand_init(&vm.rng);
107
108 err:
109 // This is why we had to set them to NULL.
110 if (BC_ERR(vm.err)) {
111 if (vm.out.v != NULL) bc_vec_free(&vm.out);
112 if (vm.jmp_bufs.v != NULL) bc_vec_free(&vm.jmp_bufs);
113 if (vm.ctxts.v != NULL) bc_vec_free(&vm.ctxts);
114 }
115
116 BC_FUNC_FOOTER_UNLOCK(e);
117
118 assert(!vm.running && !vm.sig && !vm.sig_lock);
119
120 return e;
121 }
122
bcl_pushContext(BclContext ctxt)123 BclError bcl_pushContext(BclContext ctxt) {
124
125 BclError e = BCL_ERROR_NONE;
126
127 BC_FUNC_HEADER_LOCK(err);
128
129 bc_vec_push(&vm.ctxts, &ctxt);
130
131 err:
132 BC_FUNC_FOOTER_UNLOCK(e);
133 return e;
134 }
135
bcl_popContext(void)136 void bcl_popContext(void) {
137 if (vm.ctxts.len) bc_vec_pop(&vm.ctxts);
138 }
139
bcl_context(void)140 BclContext bcl_context(void) {
141 if (!vm.ctxts.len) return NULL;
142 return *((BclContext*) bc_vec_top(&vm.ctxts));
143 }
144
bcl_free(void)145 void bcl_free(void) {
146
147 size_t i;
148
149 vm.refs -= 1;
150
151 if (vm.refs) return;
152
153 BC_SIG_LOCK;
154
155 bc_rand_free(&vm.rng);
156 bc_vec_free(&vm.out);
157
158 for (i = 0; i < vm.ctxts.len; ++i) {
159 BclContext ctxt = *((BclContext*) bc_vec_item(&vm.ctxts, i));
160 bcl_ctxt_free(ctxt);
161 }
162
163 bc_vec_free(&vm.ctxts);
164
165 bc_vm_atexit();
166
167 BC_SIG_UNLOCK;
168
169 memset(&vm, 0, sizeof(BcVm));
170
171 assert(!vm.running && !vm.sig && !vm.sig_lock);
172 }
173
bcl_gc(void)174 void bcl_gc(void) {
175 BC_SIG_LOCK;
176 bc_vm_freeTemps();
177 BC_SIG_UNLOCK;
178 }
179
bcl_abortOnFatalError(void)180 bool bcl_abortOnFatalError(void) {
181 return vm.abrt;
182 }
183
bcl_setAbortOnFatalError(bool abrt)184 void bcl_setAbortOnFatalError(bool abrt) {
185 vm.abrt = abrt;
186 }
187
bcl_ctxt_create(void)188 BclContext bcl_ctxt_create(void) {
189
190 BclContext ctxt = NULL;
191
192 BC_FUNC_HEADER_LOCK(err);
193
194 // We want the context to be free of any interference of other parties, so
195 // malloc() is appropriate here.
196 ctxt = bc_vm_malloc(sizeof(BclCtxt));
197
198 bc_vec_init(&ctxt->nums, sizeof(BcNum), BC_DTOR_BCL_NUM);
199 bc_vec_init(&ctxt->free_nums, sizeof(BclNumber), BC_DTOR_NONE);
200
201 ctxt->scale = 0;
202 ctxt->ibase = 10;
203 ctxt->obase= 10;
204
205 err:
206 if (BC_ERR(vm.err && ctxt != NULL)) {
207 if (ctxt->nums.v != NULL) bc_vec_free(&ctxt->nums);
208 free(ctxt);
209 ctxt = NULL;
210 }
211
212 BC_FUNC_FOOTER_NO_ERR;
213
214 assert(!vm.running && !vm.sig && !vm.sig_lock);
215
216 return ctxt;
217 }
218
bcl_ctxt_free(BclContext ctxt)219 void bcl_ctxt_free(BclContext ctxt) {
220 BC_SIG_LOCK;
221 bc_vec_free(&ctxt->free_nums);
222 bc_vec_free(&ctxt->nums);
223 free(ctxt);
224 BC_SIG_UNLOCK;
225 }
226
bcl_ctxt_freeNums(BclContext ctxt)227 void bcl_ctxt_freeNums(BclContext ctxt) {
228 bc_vec_popAll(&ctxt->nums);
229 bc_vec_popAll(&ctxt->free_nums);
230 }
231
bcl_ctxt_scale(BclContext ctxt)232 size_t bcl_ctxt_scale(BclContext ctxt) {
233 return ctxt->scale;
234 }
235
bcl_ctxt_setScale(BclContext ctxt,size_t scale)236 void bcl_ctxt_setScale(BclContext ctxt, size_t scale) {
237 ctxt->scale = scale;
238 }
239
bcl_ctxt_ibase(BclContext ctxt)240 size_t bcl_ctxt_ibase(BclContext ctxt) {
241 return ctxt->ibase;
242 }
243
bcl_ctxt_setIbase(BclContext ctxt,size_t ibase)244 void bcl_ctxt_setIbase(BclContext ctxt, size_t ibase) {
245 if (ibase < BC_NUM_MIN_BASE) ibase = BC_NUM_MIN_BASE;
246 else if (ibase > BC_NUM_MAX_IBASE) ibase = BC_NUM_MAX_IBASE;
247 ctxt->ibase = ibase;
248 }
249
bcl_ctxt_obase(BclContext ctxt)250 size_t bcl_ctxt_obase(BclContext ctxt) {
251 return ctxt->obase;
252 }
253
bcl_ctxt_setObase(BclContext ctxt,size_t obase)254 void bcl_ctxt_setObase(BclContext ctxt, size_t obase) {
255 ctxt->obase = obase;
256 }
257
bcl_err(BclNumber n)258 BclError bcl_err(BclNumber n) {
259
260 BclContext ctxt;
261
262 BC_CHECK_CTXT_ERR(ctxt);
263
264 // Errors are encoded as (0 - error_code). If the index is in that range, it
265 // is an encoded error.
266 if (n.i >= ctxt->nums.len) {
267 if (n.i > 0 - (size_t) BCL_ERROR_NELEMS) return (BclError) (0 - n.i);
268 else return BCL_ERROR_INVALID_NUM;
269 }
270 else return BCL_ERROR_NONE;
271 }
272
273 /**
274 * Inserts a BcNum into a context's list of numbers.
275 * @param ctxt The context to insert into.
276 * @param n The BcNum to insert.
277 * @return The resulting BclNumber from the insert.
278 */
bcl_num_insert(BclContext ctxt,BcNum * restrict n)279 static BclNumber bcl_num_insert(BclContext ctxt, BcNum *restrict n) {
280
281 BclNumber idx;
282
283 // If there is a free spot...
284 if (ctxt->free_nums.len) {
285
286 BcNum *ptr;
287
288 // Get the index of the free spot and remove it.
289 idx = *((BclNumber*) bc_vec_top(&ctxt->free_nums));
290 bc_vec_pop(&ctxt->free_nums);
291
292 // Copy the number into the spot.
293 ptr = bc_vec_item(&ctxt->nums, idx.i);
294 memcpy(ptr, n, sizeof(BcNum));
295 }
296 else {
297 // Just push the number onto the vector.
298 idx.i = ctxt->nums.len;
299 bc_vec_push(&ctxt->nums, n);
300 }
301
302 assert(!vm.running && !vm.sig && !vm.sig_lock);
303
304 return idx;
305 }
306
bcl_num_create(void)307 BclNumber bcl_num_create(void) {
308
309 BclError e = BCL_ERROR_NONE;
310 BcNum n;
311 BclNumber idx;
312 BclContext ctxt;
313
314 BC_CHECK_CTXT(ctxt);
315
316 BC_FUNC_HEADER_LOCK(err);
317
318 bc_vec_grow(&ctxt->nums, 1);
319
320 bc_num_init(&n, BC_NUM_DEF_SIZE);
321
322 err:
323 BC_FUNC_FOOTER_UNLOCK(e);
324 BC_MAYBE_SETUP(ctxt, e, n, idx);
325
326 assert(!vm.running && !vm.sig && !vm.sig_lock);
327
328 return idx;
329 }
330
331 /**
332 * Destructs a number and marks its spot as free.
333 * @param ctxt The context.
334 * @param n The index of the number.
335 * @param num The number to destroy.
336 */
bcl_num_dtor(BclContext ctxt,BclNumber n,BcNum * restrict num)337 static void bcl_num_dtor(BclContext ctxt, BclNumber n, BcNum *restrict num) {
338
339 BC_SIG_ASSERT_LOCKED;
340
341 assert(num != NULL && num->num != NULL);
342
343 bcl_num_destruct(num);
344 bc_vec_push(&ctxt->free_nums, &n);
345 }
346
bcl_num_free(BclNumber n)347 void bcl_num_free(BclNumber n) {
348
349 BcNum *num;
350 BclContext ctxt;
351
352 BC_CHECK_CTXT_ASSERT(ctxt);
353
354 BC_SIG_LOCK;
355
356 assert(n.i < ctxt->nums.len);
357
358 num = BC_NUM(ctxt, n);
359
360 bcl_num_dtor(ctxt, n, num);
361
362 BC_SIG_UNLOCK;
363 }
364
bcl_copy(BclNumber d,BclNumber s)365 BclError bcl_copy(BclNumber d, BclNumber s) {
366
367 BclError e = BCL_ERROR_NONE;
368 BcNum *dest, *src;
369 BclContext ctxt;
370
371 BC_CHECK_CTXT_ERR(ctxt);
372
373 BC_FUNC_HEADER_LOCK(err);
374
375 assert(d.i < ctxt->nums.len && s.i < ctxt->nums.len);
376
377 dest = BC_NUM(ctxt, d);
378 src = BC_NUM(ctxt, s);
379
380 assert(dest != NULL && src != NULL);
381 assert(dest->num != NULL && src->num != NULL);
382
383 bc_num_copy(dest, src);
384
385 err:
386 BC_FUNC_FOOTER_UNLOCK(e);
387
388 assert(!vm.running && !vm.sig && !vm.sig_lock);
389
390 return e;
391 }
392
bcl_dup(BclNumber s)393 BclNumber bcl_dup(BclNumber s) {
394
395 BclError e = BCL_ERROR_NONE;
396 BcNum *src, dest;
397 BclNumber idx;
398 BclContext ctxt;
399
400 BC_CHECK_CTXT(ctxt);
401
402 BC_FUNC_HEADER_LOCK(err);
403
404 bc_vec_grow(&ctxt->nums, 1);
405
406 assert(s.i < ctxt->nums.len);
407
408 src = BC_NUM(ctxt, s);
409
410 assert(src != NULL && src->num != NULL);
411
412 // Copy the number.
413 bc_num_clear(&dest);
414 bc_num_createCopy(&dest, src);
415
416 err:
417 BC_FUNC_FOOTER_UNLOCK(e);
418 BC_MAYBE_SETUP(ctxt, e, dest, idx);
419
420 assert(!vm.running && !vm.sig && !vm.sig_lock);
421
422 return idx;
423 }
424
bcl_num_destruct(void * num)425 void bcl_num_destruct(void *num) {
426
427 BcNum *n = (BcNum*) num;
428
429 assert(n != NULL);
430
431 if (n->num == NULL) return;
432
433 bc_num_free(num);
434 bc_num_clear(num);
435 }
436
bcl_num_neg(BclNumber n)437 bool bcl_num_neg(BclNumber n) {
438
439 BcNum *num;
440 BclContext ctxt;
441
442 BC_CHECK_CTXT_ASSERT(ctxt);
443
444 assert(n.i < ctxt->nums.len);
445
446 num = BC_NUM(ctxt, n);
447
448 assert(num != NULL && num->num != NULL);
449
450 return BC_NUM_NEG(num) != 0;
451 }
452
bcl_num_setNeg(BclNumber n,bool neg)453 void bcl_num_setNeg(BclNumber n, bool neg) {
454
455 BcNum *num;
456 BclContext ctxt;
457
458 BC_CHECK_CTXT_ASSERT(ctxt);
459
460 assert(n.i < ctxt->nums.len);
461
462 num = BC_NUM(ctxt, n);
463
464 assert(num != NULL && num->num != NULL);
465
466 num->rdx = BC_NUM_NEG_VAL(num, neg);
467 }
468
bcl_num_scale(BclNumber n)469 size_t bcl_num_scale(BclNumber n) {
470
471 BcNum *num;
472 BclContext ctxt;
473
474 BC_CHECK_CTXT_ASSERT(ctxt);
475
476 assert(n.i < ctxt->nums.len);
477
478 num = BC_NUM(ctxt, n);
479
480 assert(num != NULL && num->num != NULL);
481
482 return bc_num_scale(num);
483 }
484
bcl_num_setScale(BclNumber n,size_t scale)485 BclError bcl_num_setScale(BclNumber n, size_t scale) {
486
487 BclError e = BCL_ERROR_NONE;
488 BcNum *nptr;
489 BclContext ctxt;
490
491 BC_CHECK_CTXT_ERR(ctxt);
492
493 BC_CHECK_NUM_ERR(ctxt, n);
494
495 BC_FUNC_HEADER(err);
496
497 assert(n.i < ctxt->nums.len);
498
499 nptr = BC_NUM(ctxt, n);
500
501 assert(nptr != NULL && nptr->num != NULL);
502
503 if (scale > nptr->scale) bc_num_extend(nptr, scale - nptr->scale);
504 else if (scale < nptr->scale) bc_num_truncate(nptr, nptr->scale - scale);
505
506 err:
507 BC_SIG_MAYLOCK;
508 BC_FUNC_FOOTER(e);
509
510 assert(!vm.running && !vm.sig && !vm.sig_lock);
511
512 return e;
513 }
514
bcl_num_len(BclNumber n)515 size_t bcl_num_len(BclNumber n) {
516
517 BcNum *num;
518 BclContext ctxt;
519
520 BC_CHECK_CTXT_ASSERT(ctxt);
521
522 assert(n.i < ctxt->nums.len);
523
524 num = BC_NUM(ctxt, n);
525
526 assert(num != NULL && num->num != NULL);
527
528 return bc_num_len(num);
529 }
530
bcl_bigdig(BclNumber n,BclBigDig * result)531 BclError bcl_bigdig(BclNumber n, BclBigDig *result) {
532
533 BclError e = BCL_ERROR_NONE;
534 BcNum *num;
535 BclContext ctxt;
536
537 BC_CHECK_CTXT_ERR(ctxt);
538
539 BC_FUNC_HEADER_LOCK(err);
540
541 assert(n.i < ctxt->nums.len);
542 assert(result != NULL);
543
544 num = BC_NUM(ctxt, n);
545
546 assert(num != NULL && num->num != NULL);
547
548 *result = bc_num_bigdig(num);
549
550 err:
551 bcl_num_dtor(ctxt, n, num);
552 BC_FUNC_FOOTER_UNLOCK(e);
553
554 assert(!vm.running && !vm.sig && !vm.sig_lock);
555
556 return e;
557 }
558
bcl_bigdig2num(BclBigDig val)559 BclNumber bcl_bigdig2num(BclBigDig val) {
560
561 BclError e = BCL_ERROR_NONE;
562 BcNum n;
563 BclNumber idx;
564 BclContext ctxt;
565
566 BC_CHECK_CTXT(ctxt);
567
568 BC_FUNC_HEADER_LOCK(err);
569
570 bc_vec_grow(&ctxt->nums, 1);
571
572 bc_num_createFromBigdig(&n, val);
573
574 err:
575 BC_FUNC_FOOTER_UNLOCK(e);
576 BC_MAYBE_SETUP(ctxt, e, n, idx);
577
578 assert(!vm.running && !vm.sig && !vm.sig_lock);
579
580 return idx;
581 }
582
583 /**
584 * Sets up and executes a binary operator operation.
585 * @param a The first operand.
586 * @param b The second operand.
587 * @param op The operation.
588 * @param req The function to get the size of the result for preallocation.
589 * @return The result of the operation.
590 */
bcl_binary(BclNumber a,BclNumber b,const BcNumBinaryOp op,const BcNumBinaryOpReq req)591 static BclNumber bcl_binary(BclNumber a, BclNumber b, const BcNumBinaryOp op,
592 const BcNumBinaryOpReq req)
593 {
594 BclError e = BCL_ERROR_NONE;
595 BcNum *aptr, *bptr;
596 BcNum c;
597 BclNumber idx;
598 BclContext ctxt;
599
600 BC_CHECK_CTXT(ctxt);
601
602 BC_CHECK_NUM(ctxt, a);
603 BC_CHECK_NUM(ctxt, b);
604
605 BC_FUNC_HEADER_LOCK(err);
606
607 bc_vec_grow(&ctxt->nums, 1);
608
609 assert(a.i < ctxt->nums.len && b.i < ctxt->nums.len);
610
611 aptr = BC_NUM(ctxt, a);
612 bptr = BC_NUM(ctxt, b);
613
614 assert(aptr != NULL && bptr != NULL);
615 assert(aptr->num != NULL && bptr->num != NULL);
616
617 // Clear and initialize the result.
618 bc_num_clear(&c);
619 bc_num_init(&c, req(aptr, bptr, ctxt->scale));
620
621 BC_SIG_UNLOCK;
622
623 op(aptr, bptr, &c, ctxt->scale);
624
625 err:
626
627 BC_SIG_MAYLOCK;
628
629 // Eat the operands.
630 bcl_num_dtor(ctxt, a, aptr);
631 if (b.i != a.i) bcl_num_dtor(ctxt, b, bptr);
632
633 BC_FUNC_FOOTER(e);
634 BC_MAYBE_SETUP(ctxt, e, c, idx);
635
636 assert(!vm.running && !vm.sig && !vm.sig_lock);
637
638 return idx;
639 }
640
bcl_add(BclNumber a,BclNumber b)641 BclNumber bcl_add(BclNumber a, BclNumber b) {
642 return bcl_binary(a, b, bc_num_add, bc_num_addReq);
643 }
644
bcl_sub(BclNumber a,BclNumber b)645 BclNumber bcl_sub(BclNumber a, BclNumber b) {
646 return bcl_binary(a, b, bc_num_sub, bc_num_addReq);
647 }
648
bcl_mul(BclNumber a,BclNumber b)649 BclNumber bcl_mul(BclNumber a, BclNumber b) {
650 return bcl_binary(a, b, bc_num_mul, bc_num_mulReq);
651 }
652
bcl_div(BclNumber a,BclNumber b)653 BclNumber bcl_div(BclNumber a, BclNumber b) {
654 return bcl_binary(a, b, bc_num_div, bc_num_divReq);
655 }
656
bcl_mod(BclNumber a,BclNumber b)657 BclNumber bcl_mod(BclNumber a, BclNumber b) {
658 return bcl_binary(a, b, bc_num_mod, bc_num_divReq);
659 }
660
bcl_pow(BclNumber a,BclNumber b)661 BclNumber bcl_pow(BclNumber a, BclNumber b) {
662 return bcl_binary(a, b, bc_num_pow, bc_num_powReq);
663 }
664
bcl_lshift(BclNumber a,BclNumber b)665 BclNumber bcl_lshift(BclNumber a, BclNumber b) {
666 return bcl_binary(a, b, bc_num_lshift, bc_num_placesReq);
667 }
668
bcl_rshift(BclNumber a,BclNumber b)669 BclNumber bcl_rshift(BclNumber a, BclNumber b) {
670 return bcl_binary(a, b, bc_num_rshift, bc_num_placesReq);
671 }
672
bcl_sqrt(BclNumber a)673 BclNumber bcl_sqrt(BclNumber a) {
674
675 BclError e = BCL_ERROR_NONE;
676 BcNum *aptr;
677 BcNum b;
678 BclNumber idx;
679 BclContext ctxt;
680
681 BC_CHECK_CTXT(ctxt);
682
683 BC_CHECK_NUM(ctxt, a);
684
685 BC_FUNC_HEADER(err);
686
687 bc_vec_grow(&ctxt->nums, 1);
688
689 assert(a.i < ctxt->nums.len);
690
691 aptr = BC_NUM(ctxt, a);
692
693 bc_num_sqrt(aptr, &b, ctxt->scale);
694
695 err:
696 BC_SIG_MAYLOCK;
697 bcl_num_dtor(ctxt, a, aptr);
698 BC_FUNC_FOOTER(e);
699 BC_MAYBE_SETUP(ctxt, e, b, idx);
700
701 assert(!vm.running && !vm.sig && !vm.sig_lock);
702
703 return idx;
704 }
705
bcl_divmod(BclNumber a,BclNumber b,BclNumber * c,BclNumber * d)706 BclError bcl_divmod(BclNumber a, BclNumber b, BclNumber *c, BclNumber *d) {
707
708 BclError e = BCL_ERROR_NONE;
709 size_t req;
710 BcNum *aptr, *bptr;
711 BcNum cnum, dnum;
712 BclContext ctxt;
713
714 BC_CHECK_CTXT_ERR(ctxt);
715
716 BC_CHECK_NUM_ERR(ctxt, a);
717 BC_CHECK_NUM_ERR(ctxt, b);
718
719 BC_FUNC_HEADER_LOCK(err);
720
721 bc_vec_grow(&ctxt->nums, 2);
722
723 assert(c != NULL && d != NULL);
724
725 aptr = BC_NUM(ctxt, a);
726 bptr = BC_NUM(ctxt, b);
727
728 assert(aptr != NULL && bptr != NULL);
729 assert(aptr->num != NULL && bptr->num != NULL);
730
731 bc_num_clear(&cnum);
732 bc_num_clear(&dnum);
733
734 req = bc_num_divReq(aptr, bptr, ctxt->scale);
735
736 // Initialize the numbers.
737 bc_num_init(&cnum, req);
738 BC_UNSETJMP;
739 BC_SETJMP_LOCKED(err);
740 bc_num_init(&dnum, req);
741
742 BC_SIG_UNLOCK;
743
744 bc_num_divmod(aptr, bptr, &cnum, &dnum, ctxt->scale);
745
746 err:
747 BC_SIG_MAYLOCK;
748
749 // Eat the operands.
750 bcl_num_dtor(ctxt, a, aptr);
751 if (b.i != a.i) bcl_num_dtor(ctxt, b, bptr);
752
753 // If there was an error...
754 if (BC_ERR(vm.err)) {
755
756 // Free the results.
757 if (cnum.num != NULL) bc_num_free(&cnum);
758 if (dnum.num != NULL) bc_num_free(&dnum);
759
760 // Make sure the return values are invalid.
761 c->i = 0 - (size_t) BCL_ERROR_INVALID_NUM;
762 d->i = c->i;
763
764 BC_FUNC_FOOTER(e);
765 }
766 else {
767
768 BC_FUNC_FOOTER(e);
769
770 // Insert the results into the context.
771 *c = bcl_num_insert(ctxt, &cnum);
772 *d = bcl_num_insert(ctxt, &dnum);
773 }
774
775 assert(!vm.running && !vm.sig && !vm.sig_lock);
776
777 return e;
778 }
779
bcl_modexp(BclNumber a,BclNumber b,BclNumber c)780 BclNumber bcl_modexp(BclNumber a, BclNumber b, BclNumber c) {
781
782 BclError e = BCL_ERROR_NONE;
783 size_t req;
784 BcNum *aptr, *bptr, *cptr;
785 BcNum d;
786 BclNumber idx;
787 BclContext ctxt;
788
789 BC_CHECK_CTXT(ctxt);
790
791 BC_CHECK_NUM(ctxt, a);
792 BC_CHECK_NUM(ctxt, b);
793 BC_CHECK_NUM(ctxt, c);
794
795 BC_FUNC_HEADER_LOCK(err);
796
797 bc_vec_grow(&ctxt->nums, 1);
798
799 assert(a.i < ctxt->nums.len && b.i < ctxt->nums.len);
800 assert(c.i < ctxt->nums.len);
801
802 aptr = BC_NUM(ctxt, a);
803 bptr = BC_NUM(ctxt, b);
804 cptr = BC_NUM(ctxt, c);
805
806 assert(aptr != NULL && bptr != NULL && cptr != NULL);
807 assert(aptr->num != NULL && bptr->num != NULL && cptr->num != NULL);
808
809 // Prepare the result.
810 bc_num_clear(&d);
811
812 req = bc_num_divReq(aptr, cptr, 0);
813
814 // Initialize the result.
815 bc_num_init(&d, req);
816
817 BC_SIG_UNLOCK;
818
819 bc_num_modexp(aptr, bptr, cptr, &d);
820
821 err:
822 BC_SIG_MAYLOCK;
823
824 // Eat the operands.
825 bcl_num_dtor(ctxt, a, aptr);
826 if (b.i != a.i) bcl_num_dtor(ctxt, b, bptr);
827 if (c.i != a.i && c.i != b.i) bcl_num_dtor(ctxt, c, cptr);
828
829 BC_FUNC_FOOTER(e);
830 BC_MAYBE_SETUP(ctxt, e, d, idx);
831
832 assert(!vm.running && !vm.sig && !vm.sig_lock);
833
834 return idx;
835 }
836
bcl_cmp(BclNumber a,BclNumber b)837 ssize_t bcl_cmp(BclNumber a, BclNumber b) {
838
839 BcNum *aptr, *bptr;
840 BclContext ctxt;
841
842 BC_CHECK_CTXT_ASSERT(ctxt);
843
844 assert(a.i < ctxt->nums.len && b.i < ctxt->nums.len);
845
846 aptr = BC_NUM(ctxt, a);
847 bptr = BC_NUM(ctxt, b);
848
849 assert(aptr != NULL && bptr != NULL);
850 assert(aptr->num != NULL && bptr->num != NULL);
851
852 return bc_num_cmp(aptr, bptr);
853 }
854
bcl_zero(BclNumber n)855 void bcl_zero(BclNumber n) {
856
857 BcNum *nptr;
858 BclContext ctxt;
859
860 BC_CHECK_CTXT_ASSERT(ctxt);
861
862 assert(n.i < ctxt->nums.len);
863
864 nptr = BC_NUM(ctxt, n);
865
866 assert(nptr != NULL && nptr->num != NULL);
867
868 bc_num_zero(nptr);
869 }
870
bcl_one(BclNumber n)871 void bcl_one(BclNumber n) {
872
873 BcNum *nptr;
874 BclContext ctxt;
875
876 BC_CHECK_CTXT_ASSERT(ctxt);
877
878 assert(n.i < ctxt->nums.len);
879
880 nptr = BC_NUM(ctxt, n);
881
882 assert(nptr != NULL && nptr->num != NULL);
883
884 bc_num_one(nptr);
885 }
886
bcl_parse(const char * restrict val)887 BclNumber bcl_parse(const char *restrict val) {
888
889 BclError e = BCL_ERROR_NONE;
890 BcNum n;
891 BclNumber idx;
892 BclContext ctxt;
893 bool neg;
894
895 BC_CHECK_CTXT(ctxt);
896
897 BC_FUNC_HEADER_LOCK(err);
898
899 bc_vec_grow(&ctxt->nums, 1);
900
901 assert(val != NULL);
902
903 // We have to take care of negative here because bc's number parsing does
904 // not.
905 neg = (val[0] == '-');
906
907 if (neg) val += 1;
908
909 if (!bc_num_strValid(val)) {
910 vm.err = BCL_ERROR_PARSE_INVALID_STR;
911 goto err;
912 }
913
914 // Clear and initialize the number.
915 bc_num_clear(&n);
916 bc_num_init(&n, BC_NUM_DEF_SIZE);
917
918 BC_SIG_UNLOCK;
919
920 bc_num_parse(&n, val, (BcBigDig) ctxt->ibase);
921
922 // Set the negative.
923 n.rdx = BC_NUM_NEG_VAL_NP(n, neg);
924
925 err:
926 BC_SIG_MAYLOCK;
927 BC_FUNC_FOOTER(e);
928 BC_MAYBE_SETUP(ctxt, e, n, idx);
929
930 assert(!vm.running && !vm.sig && !vm.sig_lock);
931
932 return idx;
933 }
934
bcl_string(BclNumber n)935 char* bcl_string(BclNumber n) {
936
937 BcNum *nptr;
938 char *str = NULL;
939 BclContext ctxt;
940
941 BC_CHECK_CTXT_ASSERT(ctxt);
942
943 if (BC_ERR(n.i >= ctxt->nums.len)) return str;
944
945 BC_FUNC_HEADER(err);
946
947 assert(n.i < ctxt->nums.len);
948
949 nptr = BC_NUM(ctxt, n);
950
951 assert(nptr != NULL && nptr->num != NULL);
952
953 // Clear the buffer.
954 bc_vec_popAll(&vm.out);
955
956 // Print to the buffer.
957 bc_num_print(nptr, (BcBigDig) ctxt->obase, false);
958 bc_vec_pushByte(&vm.out, '\0');
959
960 BC_SIG_LOCK;
961
962 // Just dup the string; the caller is responsible for it.
963 str = bc_vm_strdup(vm.out.v);
964
965 err:
966
967 // Eat the operand.
968 bcl_num_dtor(ctxt, n, nptr);
969
970 BC_FUNC_FOOTER_NO_ERR;
971
972 assert(!vm.running && !vm.sig && !vm.sig_lock);
973
974 return str;
975 }
976
bcl_irand(BclNumber a)977 BclNumber bcl_irand(BclNumber a) {
978
979 BclError e = BCL_ERROR_NONE;
980 BcNum *aptr;
981 BcNum b;
982 BclNumber idx;
983 BclContext ctxt;
984
985 BC_CHECK_CTXT(ctxt);
986
987 BC_CHECK_NUM(ctxt, a);
988
989 BC_FUNC_HEADER_LOCK(err);
990
991 bc_vec_grow(&ctxt->nums, 1);
992
993 assert(a.i < ctxt->nums.len);
994
995 aptr = BC_NUM(ctxt, a);
996
997 assert(aptr != NULL && aptr->num != NULL);
998
999 // Clear and initialize the result.
1000 bc_num_clear(&b);
1001 bc_num_init(&b, BC_NUM_DEF_SIZE);
1002
1003 BC_SIG_UNLOCK;
1004
1005 bc_num_irand(aptr, &b, &vm.rng);
1006
1007 err:
1008 BC_SIG_MAYLOCK;
1009
1010 // Eat the operand.
1011 bcl_num_dtor(ctxt, a, aptr);
1012
1013 BC_FUNC_FOOTER(e);
1014 BC_MAYBE_SETUP(ctxt, e, b, idx);
1015
1016 assert(!vm.running && !vm.sig && !vm.sig_lock);
1017
1018 return idx;
1019 }
1020
1021 /**
1022 * Helps bcl_frand(). This is separate because the error handling is easier that
1023 * way. It is also easier to do ifrand that way.
1024 * @param b The return parameter.
1025 * @param places The number of decimal places to generate.
1026 */
bcl_frandHelper(BcNum * restrict b,size_t places)1027 static void bcl_frandHelper(BcNum *restrict b, size_t places) {
1028
1029 BcNum exp, pow, ten;
1030 BcDig exp_digs[BC_NUM_BIGDIG_LOG10];
1031 BcDig ten_digs[BC_NUM_BIGDIG_LOG10];
1032
1033 // Set up temporaries.
1034 bc_num_setup(&exp, exp_digs, BC_NUM_BIGDIG_LOG10);
1035 bc_num_setup(&ten, ten_digs, BC_NUM_BIGDIG_LOG10);
1036
1037 ten.num[0] = 10;
1038 ten.len = 1;
1039
1040 bc_num_bigdig2num(&exp, (BcBigDig) places);
1041
1042 // Clear the temporary that might need to grow.
1043 bc_num_clear(&pow);
1044
1045 BC_SIG_LOCK;
1046
1047 // Initialize the temporary that might need to grow.
1048 bc_num_init(&pow, bc_num_powReq(&ten, &exp, 0));
1049
1050 BC_SETJMP_LOCKED(err);
1051
1052 BC_SIG_UNLOCK;
1053
1054 // Generate the number.
1055 bc_num_pow(&ten, &exp, &pow, 0);
1056 bc_num_irand(&pow, b, &vm.rng);
1057
1058 // Make the number entirely fraction.
1059 bc_num_shiftRight(b, places);
1060
1061 err:
1062 BC_SIG_MAYLOCK;
1063 bc_num_free(&pow);
1064 BC_LONGJMP_CONT;
1065 }
1066
bcl_frand(size_t places)1067 BclNumber bcl_frand(size_t places) {
1068
1069 BclError e = BCL_ERROR_NONE;
1070 BcNum n;
1071 BclNumber idx;
1072 BclContext ctxt;
1073
1074 BC_CHECK_CTXT(ctxt);
1075
1076 BC_FUNC_HEADER_LOCK(err);
1077
1078 bc_vec_grow(&ctxt->nums, 1);
1079
1080 // Clear and initialize the number.
1081 bc_num_clear(&n);
1082 bc_num_init(&n, BC_NUM_DEF_SIZE);
1083
1084 BC_SIG_UNLOCK;
1085
1086 bcl_frandHelper(&n, places);
1087
1088 err:
1089 BC_SIG_MAYLOCK;
1090
1091 BC_FUNC_FOOTER(e);
1092 BC_MAYBE_SETUP(ctxt, e, n, idx);
1093
1094 assert(!vm.running && !vm.sig && !vm.sig_lock);
1095
1096 return idx;
1097 }
1098
1099 /**
1100 * Helps bc_ifrand(). This is separate because error handling is easier that
1101 * way.
1102 * @param a The limit for bc_num_irand().
1103 * @param b The return parameter.
1104 * @param places The number of decimal places to generate.
1105 */
bcl_ifrandHelper(BcNum * restrict a,BcNum * restrict b,size_t places)1106 static void bcl_ifrandHelper(BcNum *restrict a, BcNum *restrict b,
1107 size_t places)
1108 {
1109 BcNum ir, fr;
1110
1111 // Clear the integer and fractional numbers.
1112 bc_num_clear(&ir);
1113 bc_num_clear(&fr);
1114
1115 BC_SIG_LOCK;
1116
1117 // Initialize the integer and fractional numbers.
1118 bc_num_init(&ir, BC_NUM_DEF_SIZE);
1119 bc_num_init(&fr, BC_NUM_DEF_SIZE);
1120
1121 BC_SETJMP_LOCKED(err);
1122
1123 BC_SIG_UNLOCK;
1124
1125 bc_num_irand(a, &ir, &vm.rng);
1126 bcl_frandHelper(&fr, places);
1127
1128 bc_num_add(&ir, &fr, b, 0);
1129
1130 err:
1131 BC_SIG_MAYLOCK;
1132 bc_num_free(&fr);
1133 bc_num_free(&ir);
1134 BC_LONGJMP_CONT;
1135 }
1136
bcl_ifrand(BclNumber a,size_t places)1137 BclNumber bcl_ifrand(BclNumber a, size_t places) {
1138
1139 BclError e = BCL_ERROR_NONE;
1140 BcNum *aptr;
1141 BcNum b;
1142 BclNumber idx;
1143 BclContext ctxt;
1144
1145 BC_CHECK_CTXT(ctxt);
1146 BC_CHECK_NUM(ctxt, a);
1147
1148 BC_FUNC_HEADER_LOCK(err);
1149
1150 bc_vec_grow(&ctxt->nums, 1);
1151
1152 assert(a.i < ctxt->nums.len);
1153
1154 aptr = BC_NUM(ctxt, a);
1155
1156 assert(aptr != NULL && aptr->num != NULL);
1157
1158 // Clear and initialize the number.
1159 bc_num_clear(&b);
1160 bc_num_init(&b, BC_NUM_DEF_SIZE);
1161
1162 BC_SIG_UNLOCK;
1163
1164 bcl_ifrandHelper(aptr, &b, places);
1165
1166 err:
1167 BC_SIG_MAYLOCK;
1168
1169 // Eat the oprand.
1170 bcl_num_dtor(ctxt, a, aptr);
1171
1172 BC_FUNC_FOOTER(e);
1173 BC_MAYBE_SETUP(ctxt, e, b, idx);
1174
1175 assert(!vm.running && !vm.sig && !vm.sig_lock);
1176
1177 return idx;
1178 }
1179
bcl_rand_seedWithNum(BclNumber n)1180 BclError bcl_rand_seedWithNum(BclNumber n) {
1181
1182 BclError e = BCL_ERROR_NONE;
1183 BcNum *nptr;
1184 BclContext ctxt;
1185
1186 BC_CHECK_CTXT_ERR(ctxt);
1187 BC_CHECK_NUM_ERR(ctxt, n);
1188
1189 BC_FUNC_HEADER(err);
1190
1191 assert(n.i < ctxt->nums.len);
1192
1193 nptr = BC_NUM(ctxt, n);
1194
1195 assert(nptr != NULL && nptr->num != NULL);
1196
1197 bc_num_rng(nptr, &vm.rng);
1198
1199 err:
1200 BC_SIG_MAYLOCK;
1201 BC_FUNC_FOOTER(e);
1202
1203 assert(!vm.running && !vm.sig && !vm.sig_lock);
1204
1205 return e;
1206 }
1207
bcl_rand_seed(unsigned char seed[BCL_SEED_SIZE])1208 BclError bcl_rand_seed(unsigned char seed[BCL_SEED_SIZE]) {
1209
1210 BclError e = BCL_ERROR_NONE;
1211 size_t i;
1212 ulong vals[BCL_SEED_ULONGS];
1213
1214 BC_FUNC_HEADER(err);
1215
1216 // Fill the array.
1217 for (i = 0; i < BCL_SEED_SIZE; ++i) {
1218 ulong val = ((ulong) seed[i]) << (((ulong) CHAR_BIT) *
1219 (i % sizeof(ulong)));
1220 vals[i / sizeof(long)] |= val;
1221 }
1222
1223 bc_rand_seed(&vm.rng, vals[0], vals[1], vals[2], vals[3]);
1224
1225 err:
1226 BC_SIG_MAYLOCK;
1227 BC_FUNC_FOOTER(e);
1228 return e;
1229 }
1230
bcl_rand_reseed(void)1231 void bcl_rand_reseed(void) {
1232 bc_rand_srand(bc_vec_top(&vm.rng.v));
1233 }
1234
bcl_rand_seed2num(void)1235 BclNumber bcl_rand_seed2num(void) {
1236
1237 BclError e = BCL_ERROR_NONE;
1238 BcNum n;
1239 BclNumber idx;
1240 BclContext ctxt;
1241
1242 BC_CHECK_CTXT(ctxt);
1243
1244 BC_FUNC_HEADER_LOCK(err);
1245
1246 // Clear and initialize the number.
1247 bc_num_clear(&n);
1248 bc_num_init(&n, BC_NUM_DEF_SIZE);
1249
1250 BC_SIG_UNLOCK;
1251
1252 bc_num_createFromRNG(&n, &vm.rng);
1253
1254 err:
1255 BC_SIG_MAYLOCK;
1256 BC_FUNC_FOOTER(e);
1257 BC_MAYBE_SETUP(ctxt, e, n, idx);
1258
1259 assert(!vm.running && !vm.sig && !vm.sig_lock);
1260
1261 return idx;
1262 }
1263
bcl_rand_int(void)1264 BclRandInt bcl_rand_int(void) {
1265 return (BclRandInt) bc_rand_int(&vm.rng);
1266 }
1267
bcl_rand_bounded(BclRandInt bound)1268 BclRandInt bcl_rand_bounded(BclRandInt bound) {
1269 if (bound <= 1) return 0;
1270 return (BclRandInt) bc_rand_bounded(&vm.rng, (BcRand) bound);
1271 }
1272
1273 #endif // BC_ENABLE_LIBRARY
1274