1 /*
2 * *****************************************************************************
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 *
6 * Copyright (c) 2018-2023 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 #ifndef _WIN32
49 #include <pthread.h>
50 #endif // _WIN32
51
52 // The asserts in this file are important to testing; in many cases, the test
53 // would not work without the asserts, so don't remove them without reason.
54 //
55 // Also, there are many uses of bc_num_clear() here; that is because numbers are
56 // being reused, and a clean slate is required.
57 //
58 // Also, there are a bunch of BC_UNSETJMP between calls to bc_num_init(). That
59 // is because locals are being initialized, and unlike bc proper, this code
60 // cannot assume that allocation failures are fatal. So we have to reset the
61 // jumps every time to ensure that the locals will be correct after jumping.
62
63 #if BC_ENABLE_MEMCHECK
64
65 BC_NORETURN void
bcl_invalidGeneration(void)66 bcl_invalidGeneration(void)
67 {
68 abort();
69 }
70
71 BC_NORETURN void
bcl_nonexistentNum(void)72 bcl_nonexistentNum(void)
73 {
74 abort();
75 }
76
77 BC_NORETURN void
bcl_numIdxOutOfRange(void)78 bcl_numIdxOutOfRange(void)
79 {
80 abort();
81 }
82
83 #endif // BC_ENABLE_MEMCHECK
84
85 static BclTls* tls = NULL;
86 static BclTls tls_real;
87
88 BclError
bcl_start(void)89 bcl_start(void)
90 {
91 #ifndef _WIN32
92
93 int r;
94
95 if (tls != NULL) return BCL_ERROR_NONE;
96
97 r = pthread_key_create(&tls_real, NULL);
98 if (BC_ERR(r != 0)) return BCL_ERROR_FATAL_ALLOC_ERR;
99
100 #else // _WIN32
101
102 if (tls != NULL) return BCL_ERROR_NONE;
103
104 tls_real = TlsAlloc();
105 if (BC_ERR(tls_real == TLS_OUT_OF_INDEXES))
106 {
107 return BCL_ERROR_FATAL_ALLOC_ERR;
108 }
109
110 #endif // _WIN32
111
112 tls = &tls_real;
113
114 return BCL_ERROR_NONE;
115 }
116
117 /**
118 * Sets the thread-specific data for the thread.
119 * @param vm The @a BcVm to set as the thread data.
120 * @return An error code, if any.
121 */
122 static BclError
bcl_setspecific(BcVm * vm)123 bcl_setspecific(BcVm* vm)
124 {
125 #ifndef _WIN32
126
127 int r;
128
129 assert(tls != NULL);
130
131 r = pthread_setspecific(tls_real, vm);
132 if (BC_ERR(r != 0)) return BCL_ERROR_FATAL_ALLOC_ERR;
133
134 #else // _WIN32
135
136 bool r;
137
138 assert(tls != NULL);
139
140 r = TlsSetValue(tls_real, vm);
141 if (BC_ERR(!r)) return BCL_ERROR_FATAL_ALLOC_ERR;
142
143 #endif // _WIN32
144
145 return BCL_ERROR_NONE;
146 }
147
148 BcVm*
bcl_getspecific(void)149 bcl_getspecific(void)
150 {
151 BcVm* vm;
152
153 #ifndef _WIN32
154
155 vm = pthread_getspecific(tls_real);
156
157 #else // _WIN32
158
159 vm = TlsGetValue(tls_real);
160
161 #endif // _WIN32
162
163 return vm;
164 }
165
166 BclError
bcl_init(void)167 bcl_init(void)
168 {
169 BclError e = BCL_ERROR_NONE;
170 BcVm* vm;
171
172 assert(tls != NULL);
173
174 vm = bcl_getspecific();
175 if (vm != NULL)
176 {
177 assert(vm->refs >= 1);
178
179 vm->refs += 1;
180
181 return e;
182 }
183
184 vm = bc_vm_malloc(sizeof(BcVm));
185 if (BC_ERR(vm == NULL)) return BCL_ERROR_FATAL_ALLOC_ERR;
186
187 e = bcl_setspecific(vm);
188 if (BC_ERR(e != BCL_ERROR_NONE))
189 {
190 free(vm);
191 return e;
192 }
193
194 memset(vm, 0, sizeof(BcVm));
195
196 vm->refs += 1;
197
198 assert(vm->refs == 1);
199
200 // Setting these to NULL ensures that if an error occurs, we only free what
201 // is necessary.
202 vm->ctxts.v = NULL;
203 vm->jmp_bufs.v = NULL;
204 vm->out.v = NULL;
205
206 vm->abrt = false;
207 vm->leading_zeroes = false;
208 vm->digit_clamp = true;
209
210 // The jmp_bufs always has to be initialized first.
211 bc_vec_init(&vm->jmp_bufs, sizeof(sigjmp_buf), BC_DTOR_NONE);
212
213 BC_FUNC_HEADER(vm, err);
214
215 bc_vm_init();
216
217 bc_vec_init(&vm->ctxts, sizeof(BclContext), BC_DTOR_NONE);
218 bc_vec_init(&vm->out, sizeof(uchar), BC_DTOR_NONE);
219
220 #if BC_ENABLE_EXTRA_MATH
221
222 // We need to seed this in case /dev/random and /dev/urandom don't work.
223 srand((unsigned int) time(NULL));
224 bc_rand_init(&vm->rng);
225
226 #endif // BC_ENABLE_EXTRA_MATH
227
228 err:
229
230 BC_FUNC_FOOTER(vm, e);
231
232 // This is why we had to set them to NULL.
233 if (BC_ERR(vm != NULL && vm->err))
234 {
235 if (vm->out.v != NULL) bc_vec_free(&vm->out);
236 if (vm->jmp_bufs.v != NULL) bc_vec_free(&vm->jmp_bufs);
237 if (vm->ctxts.v != NULL) bc_vec_free(&vm->ctxts);
238 bcl_setspecific(NULL);
239 free(vm);
240 }
241
242 return e;
243 }
244
245 BclError
bcl_pushContext(BclContext ctxt)246 bcl_pushContext(BclContext ctxt)
247 {
248 BclError e = BCL_ERROR_NONE;
249 BcVm* vm = bcl_getspecific();
250
251 BC_FUNC_HEADER(vm, err);
252
253 bc_vec_push(&vm->ctxts, &ctxt);
254
255 err:
256
257 BC_FUNC_FOOTER(vm, e);
258 return e;
259 }
260
261 void
bcl_popContext(void)262 bcl_popContext(void)
263 {
264 BcVm* vm = bcl_getspecific();
265
266 if (vm->ctxts.len) bc_vec_pop(&vm->ctxts);
267 }
268
269 static BclContext
bcl_contextHelper(BcVm * vm)270 bcl_contextHelper(BcVm* vm)
271 {
272 if (!vm->ctxts.len) return NULL;
273 return *((BclContext*) bc_vec_top(&vm->ctxts));
274 }
275
276 BclContext
bcl_context(void)277 bcl_context(void)
278 {
279 BcVm* vm = bcl_getspecific();
280 return bcl_contextHelper(vm);
281 }
282
283 void
bcl_free(void)284 bcl_free(void)
285 {
286 size_t i;
287 BcVm* vm = bcl_getspecific();
288
289 vm->refs -= 1;
290 if (vm->refs) return;
291
292 #if BC_ENABLE_EXTRA_MATH
293 bc_rand_free(&vm->rng);
294 #endif // BC_ENABLE_EXTRA_MATH
295 bc_vec_free(&vm->out);
296
297 for (i = 0; i < vm->ctxts.len; ++i)
298 {
299 BclContext ctxt = *((BclContext*) bc_vec_item(&vm->ctxts, i));
300 bcl_ctxt_free(ctxt);
301 }
302
303 bc_vec_free(&vm->ctxts);
304
305 bc_vm_atexit();
306
307 free(vm);
308 bcl_setspecific(NULL);
309 }
310
311 void
bcl_end(void)312 bcl_end(void)
313 {
314 #ifndef _WIN32
315
316 // We ignore the return value.
317 pthread_key_delete(tls_real);
318
319 #else // _WIN32
320
321 // We ignore the return value.
322 TlsFree(tls_real);
323
324 #endif // _WIN32
325
326 tls = NULL;
327 }
328
329 void
bcl_gc(void)330 bcl_gc(void)
331 {
332 bc_vm_freeTemps();
333 }
334
335 bool
bcl_abortOnFatalError(void)336 bcl_abortOnFatalError(void)
337 {
338 BcVm* vm = bcl_getspecific();
339
340 return vm->abrt;
341 }
342
343 void
bcl_setAbortOnFatalError(bool abrt)344 bcl_setAbortOnFatalError(bool abrt)
345 {
346 BcVm* vm = bcl_getspecific();
347
348 vm->abrt = abrt;
349 }
350
351 bool
bcl_leadingZeroes(void)352 bcl_leadingZeroes(void)
353 {
354 BcVm* vm = bcl_getspecific();
355
356 return vm->leading_zeroes;
357 }
358
359 void
bcl_setLeadingZeroes(bool leadingZeroes)360 bcl_setLeadingZeroes(bool leadingZeroes)
361 {
362 BcVm* vm = bcl_getspecific();
363
364 vm->leading_zeroes = leadingZeroes;
365 }
366
367 bool
bcl_digitClamp(void)368 bcl_digitClamp(void)
369 {
370 BcVm* vm = bcl_getspecific();
371
372 return vm->digit_clamp;
373 }
374
375 void
bcl_setDigitClamp(bool digitClamp)376 bcl_setDigitClamp(bool digitClamp)
377 {
378 BcVm* vm = bcl_getspecific();
379
380 vm->digit_clamp = digitClamp;
381 }
382
383 BclContext
bcl_ctxt_create(void)384 bcl_ctxt_create(void)
385 {
386 BcVm* vm = bcl_getspecific();
387 BclContext ctxt = NULL;
388
389 BC_FUNC_HEADER(vm, err);
390
391 // We want the context to be free of any interference of other parties, so
392 // malloc() is appropriate here.
393 ctxt = bc_vm_malloc(sizeof(BclCtxt));
394
395 bc_vec_init(&ctxt->nums, sizeof(BclNum), BC_DTOR_BCL_NUM);
396 bc_vec_init(&ctxt->free_nums, sizeof(BclNumber), BC_DTOR_NONE);
397
398 ctxt->scale = 0;
399 ctxt->ibase = 10;
400 ctxt->obase = 10;
401
402 err:
403
404 if (BC_ERR(vm->err && ctxt != NULL))
405 {
406 if (ctxt->nums.v != NULL) bc_vec_free(&ctxt->nums);
407 free(ctxt);
408 ctxt = NULL;
409 }
410
411 BC_FUNC_FOOTER_NO_ERR(vm);
412
413 return ctxt;
414 }
415
416 void
bcl_ctxt_free(BclContext ctxt)417 bcl_ctxt_free(BclContext ctxt)
418 {
419 bc_vec_free(&ctxt->free_nums);
420 bc_vec_free(&ctxt->nums);
421 free(ctxt);
422 }
423
424 void
bcl_ctxt_freeNums(BclContext ctxt)425 bcl_ctxt_freeNums(BclContext ctxt)
426 {
427 bc_vec_popAll(&ctxt->nums);
428 bc_vec_popAll(&ctxt->free_nums);
429 }
430
431 size_t
bcl_ctxt_scale(BclContext ctxt)432 bcl_ctxt_scale(BclContext ctxt)
433 {
434 return ctxt->scale;
435 }
436
437 void
bcl_ctxt_setScale(BclContext ctxt,size_t scale)438 bcl_ctxt_setScale(BclContext ctxt, size_t scale)
439 {
440 ctxt->scale = scale;
441 }
442
443 size_t
bcl_ctxt_ibase(BclContext ctxt)444 bcl_ctxt_ibase(BclContext ctxt)
445 {
446 return ctxt->ibase;
447 }
448
449 void
bcl_ctxt_setIbase(BclContext ctxt,size_t ibase)450 bcl_ctxt_setIbase(BclContext ctxt, size_t ibase)
451 {
452 if (ibase < BC_NUM_MIN_BASE) ibase = BC_NUM_MIN_BASE;
453 else if (ibase > BC_NUM_MAX_IBASE) ibase = BC_NUM_MAX_IBASE;
454 ctxt->ibase = ibase;
455 }
456
457 size_t
bcl_ctxt_obase(BclContext ctxt)458 bcl_ctxt_obase(BclContext ctxt)
459 {
460 return ctxt->obase;
461 }
462
463 void
bcl_ctxt_setObase(BclContext ctxt,size_t obase)464 bcl_ctxt_setObase(BclContext ctxt, size_t obase)
465 {
466 ctxt->obase = obase;
467 }
468
469 BclError
bcl_err(BclNumber n)470 bcl_err(BclNumber n)
471 {
472 BclContext ctxt;
473 BcVm* vm = bcl_getspecific();
474
475 BC_CHECK_CTXT_ERR(vm, ctxt);
476
477 // We need to clear the top byte in memcheck mode. We can do this because
478 // the parameter is a copy.
479 BCL_CLEAR_GEN(n);
480
481 // Errors are encoded as (0 - error_code). If the index is in that range, it
482 // is an encoded error.
483 if (n.i >= ctxt->nums.len)
484 {
485 if (n.i > 0 - (size_t) BCL_ERROR_NELEMS) return (BclError) (0 - n.i);
486 else return BCL_ERROR_INVALID_NUM;
487 }
488 else return BCL_ERROR_NONE;
489 }
490
491 /**
492 * Inserts a BcNum into a context's list of numbers.
493 * @param ctxt The context to insert into.
494 * @param n The BcNum to insert.
495 * @return The resulting BclNumber from the insert.
496 */
497 static BclNumber
bcl_num_insert(BclContext ctxt,BclNum * restrict n)498 bcl_num_insert(BclContext ctxt, BclNum* restrict n)
499 {
500 BclNumber idx;
501
502 // If there is a free spot...
503 if (ctxt->free_nums.len)
504 {
505 BclNum* ptr;
506
507 // Get the index of the free spot and remove it.
508 idx = *((BclNumber*) bc_vec_top(&ctxt->free_nums));
509 bc_vec_pop(&ctxt->free_nums);
510
511 // Copy the number into the spot.
512 ptr = bc_vec_item(&ctxt->nums, idx.i);
513
514 memcpy(BCL_NUM_NUM(ptr), n, sizeof(BcNum));
515
516 #if BC_ENABLE_MEMCHECK
517
518 ptr->gen_idx += 1;
519
520 if (ptr->gen_idx == UCHAR_MAX)
521 {
522 ptr->gen_idx = 0;
523 }
524
525 idx.i |= (ptr->gen_idx << ((sizeof(size_t) - 1) * CHAR_BIT));
526
527 #endif // BC_ENABLE_MEMCHECK
528 }
529 else
530 {
531 #if BC_ENABLE_MEMCHECK
532 n->gen_idx = 0;
533 #endif // BC_ENABLE_MEMCHECK
534
535 // Just push the number onto the vector because the generation index is
536 // 0.
537 idx.i = ctxt->nums.len;
538 bc_vec_push(&ctxt->nums, n);
539 }
540
541 return idx;
542 }
543
544 BclNumber
bcl_num_create(void)545 bcl_num_create(void)
546 {
547 BclError e = BCL_ERROR_NONE;
548 BclNum n;
549 BclNumber idx;
550 BclContext ctxt;
551 BcVm* vm = bcl_getspecific();
552
553 BC_CHECK_CTXT(vm, ctxt);
554
555 BC_FUNC_HEADER(vm, err);
556
557 BCL_GROW_NUMS(ctxt);
558
559 bc_num_init(BCL_NUM_NUM_NP(n), BC_NUM_DEF_SIZE);
560
561 err:
562
563 BC_FUNC_FOOTER(vm, e);
564 BC_MAYBE_SETUP(ctxt, e, n, idx);
565
566 return idx;
567 }
568
569 /**
570 * Destructs a number and marks its spot as free.
571 * @param ctxt The context.
572 * @param n The index of the number.
573 * @param num The number to destroy.
574 */
575 static void
bcl_num_dtor(BclContext ctxt,BclNumber n,BclNum * restrict num)576 bcl_num_dtor(BclContext ctxt, BclNumber n, BclNum* restrict num)
577 {
578 assert(num != NULL && BCL_NUM_ARRAY(num) != NULL);
579
580 BCL_CLEAR_GEN(n);
581
582 bcl_num_destruct(num);
583 bc_vec_push(&ctxt->free_nums, &n);
584
585 #if BC_ENABLE_MEMCHECK
586 num->n.num = NULL;
587 #endif // BC_ENABLE_MEMCHECK
588 }
589
590 void
bcl_num_free(BclNumber n)591 bcl_num_free(BclNumber n)
592 {
593 BclNum* num;
594 BclContext ctxt;
595 BcVm* vm = bcl_getspecific();
596
597 BC_CHECK_CTXT_ASSERT(vm, ctxt);
598
599 BCL_CHECK_NUM_VALID(ctxt, n);
600
601 assert(BCL_NO_GEN(n) < ctxt->nums.len);
602
603 num = BCL_NUM(ctxt, n);
604
605 bcl_num_dtor(ctxt, n, num);
606 }
607
608 BclError
bcl_copy(BclNumber d,BclNumber s)609 bcl_copy(BclNumber d, BclNumber s)
610 {
611 BclError e = BCL_ERROR_NONE;
612 BclNum* dest;
613 BclNum* src;
614 BclContext ctxt;
615 BcVm* vm = bcl_getspecific();
616
617 BC_CHECK_CTXT_ERR(vm, ctxt);
618
619 BCL_CHECK_NUM_VALID(ctxt, d);
620 BCL_CHECK_NUM_VALID(ctxt, s);
621
622 BC_FUNC_HEADER(vm, err);
623
624 assert(BCL_NO_GEN(d) < ctxt->nums.len);
625 assert(BCL_NO_GEN(s) < ctxt->nums.len);
626
627 dest = BCL_NUM(ctxt, d);
628 src = BCL_NUM(ctxt, s);
629
630 assert(dest != NULL && src != NULL);
631 assert(BCL_NUM_ARRAY(dest) != NULL && BCL_NUM_ARRAY(src) != NULL);
632
633 bc_num_copy(BCL_NUM_NUM(dest), BCL_NUM_NUM(src));
634
635 err:
636
637 BC_FUNC_FOOTER(vm, e);
638
639 return e;
640 }
641
642 BclNumber
bcl_dup(BclNumber s)643 bcl_dup(BclNumber s)
644 {
645 BclError e = BCL_ERROR_NONE;
646 BclNum *src, dest;
647 BclNumber idx;
648 BclContext ctxt;
649 BcVm* vm = bcl_getspecific();
650
651 BC_CHECK_CTXT(vm, ctxt);
652
653 BCL_CHECK_NUM_VALID(ctxt, s);
654
655 BC_FUNC_HEADER(vm, err);
656
657 BCL_GROW_NUMS(ctxt);
658
659 assert(BCL_NO_GEN(s) < ctxt->nums.len);
660
661 src = BCL_NUM(ctxt, s);
662
663 assert(src != NULL && BCL_NUM_NUM(src) != NULL);
664
665 // Copy the number.
666 bc_num_clear(BCL_NUM_NUM(&dest));
667 bc_num_createCopy(BCL_NUM_NUM(&dest), BCL_NUM_NUM(src));
668
669 err:
670
671 BC_FUNC_FOOTER(vm, e);
672 BC_MAYBE_SETUP(ctxt, e, dest, idx);
673
674 return idx;
675 }
676
677 void
bcl_num_destruct(void * num)678 bcl_num_destruct(void* num)
679 {
680 BclNum* n = (BclNum*) num;
681
682 assert(n != NULL);
683
684 if (BCL_NUM_ARRAY(n) == NULL) return;
685
686 bc_num_free(BCL_NUM_NUM(n));
687 bc_num_clear(BCL_NUM_NUM(n));
688 }
689
690 bool
bcl_num_neg(BclNumber n)691 bcl_num_neg(BclNumber n)
692 {
693 BclNum* num;
694 BclContext ctxt;
695 BcVm* vm = bcl_getspecific();
696
697 BC_CHECK_CTXT_ASSERT(vm, ctxt);
698
699 BCL_CHECK_NUM_VALID(ctxt, n);
700
701 assert(BCL_NO_GEN(n) < ctxt->nums.len);
702
703 num = BCL_NUM(ctxt, n);
704
705 assert(num != NULL && BCL_NUM_ARRAY(num) != NULL);
706
707 return BC_NUM_NEG(BCL_NUM_NUM(num)) != 0;
708 }
709
710 void
bcl_num_setNeg(BclNumber n,bool neg)711 bcl_num_setNeg(BclNumber n, bool neg)
712 {
713 BclNum* num;
714 BclContext ctxt;
715 BcVm* vm = bcl_getspecific();
716
717 BC_CHECK_CTXT_ASSERT(vm, ctxt);
718
719 BCL_CHECK_NUM_VALID(ctxt, n);
720
721 assert(BCL_NO_GEN(n) < ctxt->nums.len);
722
723 num = BCL_NUM(ctxt, n);
724
725 assert(num != NULL && BCL_NUM_ARRAY(num) != NULL);
726
727 BCL_NUM_NUM(num)->rdx = BC_NUM_NEG_VAL(BCL_NUM_NUM(num), neg);
728 }
729
730 size_t
bcl_num_scale(BclNumber n)731 bcl_num_scale(BclNumber n)
732 {
733 BclNum* num;
734 BclContext ctxt;
735 BcVm* vm = bcl_getspecific();
736
737 BC_CHECK_CTXT_ASSERT(vm, ctxt);
738
739 BCL_CHECK_NUM_VALID(ctxt, n);
740
741 assert(BCL_NO_GEN(n) < ctxt->nums.len);
742
743 num = BCL_NUM(ctxt, n);
744
745 assert(num != NULL && BCL_NUM_ARRAY(num) != NULL);
746
747 return bc_num_scale(BCL_NUM_NUM(num));
748 }
749
750 BclError
bcl_num_setScale(BclNumber n,size_t scale)751 bcl_num_setScale(BclNumber n, size_t scale)
752 {
753 BclError e = BCL_ERROR_NONE;
754 BclNum* nptr;
755 BclContext ctxt;
756 BcVm* vm = bcl_getspecific();
757
758 BC_CHECK_CTXT_ERR(vm, ctxt);
759
760 BC_CHECK_NUM_ERR(ctxt, n);
761
762 BCL_CHECK_NUM_VALID(ctxt, n);
763
764 BC_FUNC_HEADER(vm, err);
765
766 assert(BCL_NO_GEN(n) < ctxt->nums.len);
767
768 nptr = BCL_NUM(ctxt, n);
769
770 assert(nptr != NULL && BCL_NUM_ARRAY(nptr) != NULL);
771
772 if (scale > BCL_NUM_NUM(nptr)->scale)
773 {
774 bc_num_extend(BCL_NUM_NUM(nptr), scale - BCL_NUM_NUM(nptr)->scale);
775 }
776 else if (scale < BCL_NUM_NUM(nptr)->scale)
777 {
778 bc_num_truncate(BCL_NUM_NUM(nptr), BCL_NUM_NUM(nptr)->scale - scale);
779 }
780
781 err:
782
783 BC_FUNC_FOOTER(vm, e);
784
785 return e;
786 }
787
788 size_t
bcl_num_len(BclNumber n)789 bcl_num_len(BclNumber n)
790 {
791 BclNum* num;
792 BclContext ctxt;
793 BcVm* vm = bcl_getspecific();
794
795 BC_CHECK_CTXT_ASSERT(vm, ctxt);
796
797 BCL_CHECK_NUM_VALID(ctxt, n);
798
799 assert(BCL_NO_GEN(n) < ctxt->nums.len);
800
801 num = BCL_NUM(ctxt, n);
802
803 assert(num != NULL && BCL_NUM_ARRAY(num) != NULL);
804
805 return bc_num_len(BCL_NUM_NUM(num));
806 }
807
808 static BclError
bcl_bigdig_helper(BclNumber n,BclBigDig * result,bool destruct)809 bcl_bigdig_helper(BclNumber n, BclBigDig* result, bool destruct)
810 {
811 BclError e = BCL_ERROR_NONE;
812 BclNum* num;
813 BclContext ctxt;
814 BcVm* vm = bcl_getspecific();
815
816 BC_CHECK_CTXT_ERR(vm, ctxt);
817
818 BCL_CHECK_NUM_VALID(ctxt, n);
819
820 BC_FUNC_HEADER(vm, err);
821
822 assert(BCL_NO_GEN(n) < ctxt->nums.len);
823 assert(result != NULL);
824
825 num = BCL_NUM(ctxt, n);
826
827 assert(num != NULL && BCL_NUM_ARRAY(num) != NULL);
828
829 *result = bc_num_bigdig(BCL_NUM_NUM(num));
830
831 err:
832
833 if (destruct)
834 {
835 bcl_num_dtor(ctxt, n, num);
836 }
837
838 BC_FUNC_FOOTER(vm, e);
839
840 return e;
841 }
842
843 BclError
bcl_bigdig(BclNumber n,BclBigDig * result)844 bcl_bigdig(BclNumber n, BclBigDig* result)
845 {
846 return bcl_bigdig_helper(n, result, true);
847 }
848
849 BclError
bcl_bigdig_keep(BclNumber n,BclBigDig * result)850 bcl_bigdig_keep(BclNumber n, BclBigDig* result)
851 {
852 return bcl_bigdig_helper(n, result, false);
853 }
854
855 BclNumber
bcl_bigdig2num(BclBigDig val)856 bcl_bigdig2num(BclBigDig val)
857 {
858 BclError e = BCL_ERROR_NONE;
859 BclNum n;
860 BclNumber idx;
861 BclContext ctxt;
862 BcVm* vm = bcl_getspecific();
863
864 BC_CHECK_CTXT(vm, ctxt);
865
866 BC_FUNC_HEADER(vm, err);
867
868 BCL_GROW_NUMS(ctxt);
869
870 bc_num_createFromBigdig(BCL_NUM_NUM_NP(n), val);
871
872 err:
873
874 BC_FUNC_FOOTER(vm, e);
875 BC_MAYBE_SETUP(ctxt, e, n, idx);
876
877 return idx;
878 }
879
880 /**
881 * Sets up and executes a binary operator operation.
882 * @param a The first operand.
883 * @param b The second operand.
884 * @param op The operation.
885 * @param req The function to get the size of the result for
886 * preallocation.
887 * @param destruct True if the parameters should be consumed, false otherwise.
888 * @return The result of the operation.
889 */
890 static BclNumber
bcl_binary(BclNumber a,BclNumber b,const BcNumBinaryOp op,const BcNumBinaryOpReq req,bool destruct)891 bcl_binary(BclNumber a, BclNumber b, const BcNumBinaryOp op,
892 const BcNumBinaryOpReq req, bool destruct)
893 {
894 BclError e = BCL_ERROR_NONE;
895 BclNum* aptr;
896 BclNum* bptr;
897 BclNum c;
898 BclNumber idx;
899 BclContext ctxt;
900 BcVm* vm = bcl_getspecific();
901
902 BC_CHECK_CTXT(vm, ctxt);
903
904 BC_CHECK_NUM(ctxt, a);
905 BC_CHECK_NUM(ctxt, b);
906
907 BC_FUNC_HEADER(vm, err);
908
909 BCL_GROW_NUMS(ctxt);
910
911 assert(BCL_NO_GEN(a) < ctxt->nums.len && BCL_NO_GEN(b) < ctxt->nums.len);
912
913 aptr = BCL_NUM(ctxt, a);
914 bptr = BCL_NUM(ctxt, b);
915
916 assert(aptr != NULL && bptr != NULL);
917 assert(BCL_NUM_ARRAY(aptr) != NULL && BCL_NUM_ARRAY(bptr) != NULL);
918
919 // Clear and initialize the result.
920 bc_num_clear(BCL_NUM_NUM_NP(c));
921 bc_num_init(BCL_NUM_NUM_NP(c),
922 req(BCL_NUM_NUM(aptr), BCL_NUM_NUM(bptr), ctxt->scale));
923
924 op(BCL_NUM_NUM(aptr), BCL_NUM_NUM(bptr), BCL_NUM_NUM_NP(c), ctxt->scale);
925
926 err:
927
928 if (destruct)
929 {
930 // Eat the operands.
931 bcl_num_dtor(ctxt, a, aptr);
932 if (b.i != a.i) bcl_num_dtor(ctxt, b, bptr);
933 }
934
935 BC_FUNC_FOOTER(vm, e);
936 BC_MAYBE_SETUP(ctxt, e, c, idx);
937
938 return idx;
939 }
940
941 BclNumber
bcl_add(BclNumber a,BclNumber b)942 bcl_add(BclNumber a, BclNumber b)
943 {
944 return bcl_binary(a, b, bc_num_add, bc_num_addReq, true);
945 }
946
947 BclNumber
bcl_add_keep(BclNumber a,BclNumber b)948 bcl_add_keep(BclNumber a, BclNumber b)
949 {
950 return bcl_binary(a, b, bc_num_add, bc_num_addReq, false);
951 }
952
953 BclNumber
bcl_sub(BclNumber a,BclNumber b)954 bcl_sub(BclNumber a, BclNumber b)
955 {
956 return bcl_binary(a, b, bc_num_sub, bc_num_addReq, true);
957 }
958
959 BclNumber
bcl_sub_keep(BclNumber a,BclNumber b)960 bcl_sub_keep(BclNumber a, BclNumber b)
961 {
962 return bcl_binary(a, b, bc_num_sub, bc_num_addReq, false);
963 }
964
965 BclNumber
bcl_mul(BclNumber a,BclNumber b)966 bcl_mul(BclNumber a, BclNumber b)
967 {
968 return bcl_binary(a, b, bc_num_mul, bc_num_mulReq, true);
969 }
970
971 BclNumber
bcl_mul_keep(BclNumber a,BclNumber b)972 bcl_mul_keep(BclNumber a, BclNumber b)
973 {
974 return bcl_binary(a, b, bc_num_mul, bc_num_mulReq, false);
975 }
976
977 BclNumber
bcl_div(BclNumber a,BclNumber b)978 bcl_div(BclNumber a, BclNumber b)
979 {
980 return bcl_binary(a, b, bc_num_div, bc_num_divReq, true);
981 }
982
983 BclNumber
bcl_div_keep(BclNumber a,BclNumber b)984 bcl_div_keep(BclNumber a, BclNumber b)
985 {
986 return bcl_binary(a, b, bc_num_div, bc_num_divReq, false);
987 }
988
989 BclNumber
bcl_mod(BclNumber a,BclNumber b)990 bcl_mod(BclNumber a, BclNumber b)
991 {
992 return bcl_binary(a, b, bc_num_mod, bc_num_divReq, true);
993 }
994
995 BclNumber
bcl_mod_keep(BclNumber a,BclNumber b)996 bcl_mod_keep(BclNumber a, BclNumber b)
997 {
998 return bcl_binary(a, b, bc_num_mod, bc_num_divReq, false);
999 }
1000
1001 BclNumber
bcl_pow(BclNumber a,BclNumber b)1002 bcl_pow(BclNumber a, BclNumber b)
1003 {
1004 return bcl_binary(a, b, bc_num_pow, bc_num_powReq, true);
1005 }
1006
1007 BclNumber
bcl_pow_keep(BclNumber a,BclNumber b)1008 bcl_pow_keep(BclNumber a, BclNumber b)
1009 {
1010 return bcl_binary(a, b, bc_num_pow, bc_num_powReq, false);
1011 }
1012
1013 BclNumber
bcl_lshift(BclNumber a,BclNumber b)1014 bcl_lshift(BclNumber a, BclNumber b)
1015 {
1016 return bcl_binary(a, b, bc_num_lshift, bc_num_placesReq, true);
1017 }
1018
1019 BclNumber
bcl_lshift_keep(BclNumber a,BclNumber b)1020 bcl_lshift_keep(BclNumber a, BclNumber b)
1021 {
1022 return bcl_binary(a, b, bc_num_lshift, bc_num_placesReq, false);
1023 }
1024
1025 BclNumber
bcl_rshift(BclNumber a,BclNumber b)1026 bcl_rshift(BclNumber a, BclNumber b)
1027 {
1028 return bcl_binary(a, b, bc_num_rshift, bc_num_placesReq, true);
1029 }
1030
1031 BclNumber
bcl_rshift_keep(BclNumber a,BclNumber b)1032 bcl_rshift_keep(BclNumber a, BclNumber b)
1033 {
1034 return bcl_binary(a, b, bc_num_rshift, bc_num_placesReq, false);
1035 }
1036
1037 static BclNumber
bcl_sqrt_helper(BclNumber a,bool destruct)1038 bcl_sqrt_helper(BclNumber a, bool destruct)
1039 {
1040 BclError e = BCL_ERROR_NONE;
1041 BclNum* aptr;
1042 BclNum b;
1043 BclNumber idx;
1044 BclContext ctxt;
1045 BcVm* vm = bcl_getspecific();
1046
1047 BC_CHECK_CTXT(vm, ctxt);
1048
1049 BC_CHECK_NUM(ctxt, a);
1050
1051 BC_FUNC_HEADER(vm, err);
1052
1053 BCL_GROW_NUMS(ctxt);
1054
1055 assert(BCL_NO_GEN(a) < ctxt->nums.len);
1056
1057 aptr = BCL_NUM(ctxt, a);
1058
1059 bc_num_sqrt(BCL_NUM_NUM(aptr), BCL_NUM_NUM_NP(b), ctxt->scale);
1060
1061 err:
1062
1063 if (destruct)
1064 {
1065 bcl_num_dtor(ctxt, a, aptr);
1066 }
1067
1068 BC_FUNC_FOOTER(vm, e);
1069 BC_MAYBE_SETUP(ctxt, e, b, idx);
1070
1071 return idx;
1072 }
1073
1074 BclNumber
bcl_sqrt(BclNumber a)1075 bcl_sqrt(BclNumber a)
1076 {
1077 return bcl_sqrt_helper(a, true);
1078 }
1079
1080 BclNumber
bcl_sqrt_keep(BclNumber a)1081 bcl_sqrt_keep(BclNumber a)
1082 {
1083 return bcl_sqrt_helper(a, false);
1084 }
1085
1086 static BclError
bcl_divmod_helper(BclNumber a,BclNumber b,BclNumber * c,BclNumber * d,bool destruct)1087 bcl_divmod_helper(BclNumber a, BclNumber b, BclNumber* c, BclNumber* d,
1088 bool destruct)
1089 {
1090 BclError e = BCL_ERROR_NONE;
1091 size_t req;
1092 BclNum* aptr;
1093 BclNum* bptr;
1094 BclNum cnum, dnum;
1095 BclContext ctxt;
1096 BcVm* vm = bcl_getspecific();
1097
1098 BC_CHECK_CTXT_ERR(vm, ctxt);
1099
1100 BC_CHECK_NUM_ERR(ctxt, a);
1101 BC_CHECK_NUM_ERR(ctxt, b);
1102
1103 BC_FUNC_HEADER(vm, err);
1104
1105 BCL_GROW_NUMS(ctxt);
1106
1107 assert(c != NULL && d != NULL);
1108
1109 aptr = BCL_NUM(ctxt, a);
1110 bptr = BCL_NUM(ctxt, b);
1111
1112 assert(aptr != NULL && bptr != NULL);
1113 assert(BCL_NUM_ARRAY(aptr) != NULL && BCL_NUM_ARRAY(bptr) != NULL);
1114
1115 bc_num_clear(BCL_NUM_NUM_NP(cnum));
1116 bc_num_clear(BCL_NUM_NUM_NP(dnum));
1117
1118 req = bc_num_divReq(BCL_NUM_NUM(aptr), BCL_NUM_NUM(bptr), ctxt->scale);
1119
1120 // Initialize the numbers.
1121 bc_num_init(BCL_NUM_NUM_NP(cnum), req);
1122 BC_UNSETJMP(vm);
1123 BC_SETJMP(vm, err);
1124 bc_num_init(BCL_NUM_NUM_NP(dnum), req);
1125
1126 bc_num_divmod(BCL_NUM_NUM(aptr), BCL_NUM_NUM(bptr), BCL_NUM_NUM_NP(cnum),
1127 BCL_NUM_NUM_NP(dnum), ctxt->scale);
1128
1129 err:
1130
1131 if (destruct)
1132 {
1133 // Eat the operands.
1134 bcl_num_dtor(ctxt, a, aptr);
1135 if (b.i != a.i) bcl_num_dtor(ctxt, b, bptr);
1136 }
1137
1138 // If there was an error...
1139 if (BC_ERR(vm->err))
1140 {
1141 // Free the results.
1142 if (BCL_NUM_ARRAY_NP(cnum) != NULL) bc_num_free(&cnum);
1143 if (BCL_NUM_ARRAY_NP(cnum) != NULL) bc_num_free(&dnum);
1144
1145 // Make sure the return values are invalid.
1146 c->i = 0 - (size_t) BCL_ERROR_INVALID_NUM;
1147 d->i = c->i;
1148
1149 BC_FUNC_FOOTER(vm, e);
1150 }
1151 else
1152 {
1153 BC_FUNC_FOOTER(vm, e);
1154
1155 // Insert the results into the context.
1156 *c = bcl_num_insert(ctxt, &cnum);
1157 *d = bcl_num_insert(ctxt, &dnum);
1158 }
1159
1160 return e;
1161 }
1162
1163 BclError
bcl_divmod(BclNumber a,BclNumber b,BclNumber * c,BclNumber * d)1164 bcl_divmod(BclNumber a, BclNumber b, BclNumber* c, BclNumber* d)
1165 {
1166 return bcl_divmod_helper(a, b, c, d, true);
1167 }
1168
1169 BclError
bcl_divmod_keep(BclNumber a,BclNumber b,BclNumber * c,BclNumber * d)1170 bcl_divmod_keep(BclNumber a, BclNumber b, BclNumber* c, BclNumber* d)
1171 {
1172 return bcl_divmod_helper(a, b, c, d, false);
1173 }
1174
1175 static BclNumber
bcl_modexp_helper(BclNumber a,BclNumber b,BclNumber c,bool destruct)1176 bcl_modexp_helper(BclNumber a, BclNumber b, BclNumber c, bool destruct)
1177 {
1178 BclError e = BCL_ERROR_NONE;
1179 size_t req;
1180 BclNum* aptr;
1181 BclNum* bptr;
1182 BclNum* cptr;
1183 BclNum d;
1184 BclNumber idx;
1185 BclContext ctxt;
1186 BcVm* vm = bcl_getspecific();
1187
1188 BC_CHECK_CTXT(vm, ctxt);
1189
1190 BC_CHECK_NUM(ctxt, a);
1191 BC_CHECK_NUM(ctxt, b);
1192 BC_CHECK_NUM(ctxt, c);
1193
1194 BC_FUNC_HEADER(vm, err);
1195
1196 BCL_GROW_NUMS(ctxt);
1197
1198 assert(BCL_NO_GEN(a) < ctxt->nums.len && BCL_NO_GEN(b) < ctxt->nums.len);
1199 assert(BCL_NO_GEN(c) < ctxt->nums.len);
1200
1201 aptr = BCL_NUM(ctxt, a);
1202 bptr = BCL_NUM(ctxt, b);
1203 cptr = BCL_NUM(ctxt, c);
1204
1205 assert(aptr != NULL && bptr != NULL && cptr != NULL);
1206 assert(BCL_NUM_NUM(aptr) != NULL && BCL_NUM_NUM(bptr) != NULL &&
1207 BCL_NUM_NUM(cptr) != NULL);
1208
1209 // Prepare the result.
1210 bc_num_clear(BCL_NUM_NUM_NP(d));
1211
1212 req = bc_num_divReq(BCL_NUM_NUM(aptr), BCL_NUM_NUM(cptr), 0);
1213
1214 // Initialize the result.
1215 bc_num_init(BCL_NUM_NUM_NP(d), req);
1216
1217 bc_num_modexp(BCL_NUM_NUM(aptr), BCL_NUM_NUM(bptr), BCL_NUM_NUM(cptr),
1218 BCL_NUM_NUM_NP(d));
1219
1220 err:
1221
1222 if (destruct)
1223 {
1224 // Eat the operands.
1225 bcl_num_dtor(ctxt, a, aptr);
1226 if (b.i != a.i) bcl_num_dtor(ctxt, b, bptr);
1227 if (c.i != a.i && c.i != b.i) bcl_num_dtor(ctxt, c, cptr);
1228 }
1229
1230 BC_FUNC_FOOTER(vm, e);
1231 BC_MAYBE_SETUP(ctxt, e, d, idx);
1232
1233 return idx;
1234 }
1235
1236 BclNumber
bcl_modexp(BclNumber a,BclNumber b,BclNumber c)1237 bcl_modexp(BclNumber a, BclNumber b, BclNumber c)
1238 {
1239 return bcl_modexp_helper(a, b, c, true);
1240 }
1241
1242 BclNumber
bcl_modexp_keep(BclNumber a,BclNumber b,BclNumber c)1243 bcl_modexp_keep(BclNumber a, BclNumber b, BclNumber c)
1244 {
1245 return bcl_modexp_helper(a, b, c, false);
1246 }
1247
1248 ssize_t
bcl_cmp(BclNumber a,BclNumber b)1249 bcl_cmp(BclNumber a, BclNumber b)
1250 {
1251 BclNum* aptr;
1252 BclNum* bptr;
1253 BclContext ctxt;
1254 BcVm* vm = bcl_getspecific();
1255
1256 BC_CHECK_CTXT_ASSERT(vm, ctxt);
1257
1258 BCL_CHECK_NUM_VALID(ctxt, a);
1259 BCL_CHECK_NUM_VALID(ctxt, b);
1260
1261 assert(BCL_NO_GEN(a) < ctxt->nums.len && BCL_NO_GEN(b) < ctxt->nums.len);
1262
1263 aptr = BCL_NUM(ctxt, a);
1264 bptr = BCL_NUM(ctxt, b);
1265
1266 assert(aptr != NULL && bptr != NULL);
1267 assert(BCL_NUM_NUM(aptr) != NULL && BCL_NUM_NUM(bptr));
1268
1269 return bc_num_cmp(BCL_NUM_NUM(aptr), BCL_NUM_NUM(bptr));
1270 }
1271
1272 void
bcl_zero(BclNumber n)1273 bcl_zero(BclNumber n)
1274 {
1275 BclNum* nptr;
1276 BclContext ctxt;
1277 BcVm* vm = bcl_getspecific();
1278
1279 BC_CHECK_CTXT_ASSERT(vm, ctxt);
1280
1281 BCL_CHECK_NUM_VALID(ctxt, n);
1282
1283 assert(BCL_NO_GEN(n) < ctxt->nums.len);
1284
1285 nptr = BCL_NUM(ctxt, n);
1286
1287 assert(nptr != NULL && BCL_NUM_NUM(nptr) != NULL);
1288
1289 bc_num_zero(BCL_NUM_NUM(nptr));
1290 }
1291
1292 void
bcl_one(BclNumber n)1293 bcl_one(BclNumber n)
1294 {
1295 BclNum* nptr;
1296 BclContext ctxt;
1297 BcVm* vm = bcl_getspecific();
1298
1299 BC_CHECK_CTXT_ASSERT(vm, ctxt);
1300
1301 BCL_CHECK_NUM_VALID(ctxt, n);
1302
1303 assert(BCL_NO_GEN(n) < ctxt->nums.len);
1304
1305 nptr = BCL_NUM(ctxt, n);
1306
1307 assert(nptr != NULL && BCL_NUM_NUM(nptr) != NULL);
1308
1309 bc_num_one(BCL_NUM_NUM(nptr));
1310 }
1311
1312 BclNumber
bcl_parse(const char * restrict val)1313 bcl_parse(const char* restrict val)
1314 {
1315 BclError e = BCL_ERROR_NONE;
1316 BclNum n;
1317 BclNumber idx;
1318 BclContext ctxt;
1319 BcVm* vm = bcl_getspecific();
1320 bool neg;
1321
1322 BC_CHECK_CTXT(vm, ctxt);
1323
1324 BC_FUNC_HEADER(vm, err);
1325
1326 BCL_GROW_NUMS(ctxt);
1327
1328 assert(val != NULL);
1329
1330 // We have to take care of negative here because bc's number parsing does
1331 // not.
1332 neg = (val[0] == '-');
1333
1334 if (neg) val += 1;
1335
1336 if (!bc_num_strValid(val))
1337 {
1338 vm->err = BCL_ERROR_PARSE_INVALID_STR;
1339 goto err;
1340 }
1341
1342 // Clear and initialize the number.
1343 bc_num_clear(BCL_NUM_NUM_NP(n));
1344 bc_num_init(BCL_NUM_NUM_NP(n), BC_NUM_DEF_SIZE);
1345
1346 bc_num_parse(BCL_NUM_NUM_NP(n), val, (BcBigDig) ctxt->ibase);
1347
1348 // Set the negative.
1349 #if BC_ENABLE_MEMCHECK
1350 n.n.rdx = BC_NUM_NEG_VAL(BCL_NUM_NUM_NP(n), neg);
1351 #else // BC_ENABLE_MEMCHECK
1352 n.rdx = BC_NUM_NEG_VAL_NP(n, neg);
1353 #endif // BC_ENABLE_MEMCHECK
1354
1355 err:
1356
1357 BC_FUNC_FOOTER(vm, e);
1358 BC_MAYBE_SETUP(ctxt, e, n, idx);
1359
1360 return idx;
1361 }
1362
1363 static char*
bcl_string_helper(BclNumber n,bool destruct)1364 bcl_string_helper(BclNumber n, bool destruct)
1365 {
1366 BclNum* nptr;
1367 char* str = NULL;
1368 BclContext ctxt;
1369 BcVm* vm = bcl_getspecific();
1370
1371 BC_CHECK_CTXT_ASSERT(vm, ctxt);
1372
1373 BCL_CHECK_NUM_VALID(ctxt, n);
1374
1375 if (BC_ERR(BCL_NO_GEN(n) >= ctxt->nums.len)) return str;
1376
1377 BC_FUNC_HEADER(vm, err);
1378
1379 assert(BCL_NO_GEN(n) < ctxt->nums.len);
1380
1381 nptr = BCL_NUM(ctxt, n);
1382
1383 assert(nptr != NULL && BCL_NUM_NUM(nptr) != NULL);
1384
1385 // Clear the buffer.
1386 bc_vec_popAll(&vm->out);
1387
1388 // Print to the buffer.
1389 bc_num_print(BCL_NUM_NUM(nptr), (BcBigDig) ctxt->obase, false);
1390 bc_vec_pushByte(&vm->out, '\0');
1391
1392 // Just dup the string; the caller is responsible for it.
1393 str = bc_vm_strdup(vm->out.v);
1394
1395 err:
1396
1397 if (destruct)
1398 {
1399 // Eat the operand.
1400 bcl_num_dtor(ctxt, n, nptr);
1401 }
1402
1403 BC_FUNC_FOOTER_NO_ERR(vm);
1404
1405 return str;
1406 }
1407
1408 char*
bcl_string(BclNumber n)1409 bcl_string(BclNumber n)
1410 {
1411 return bcl_string_helper(n, true);
1412 }
1413
1414 char*
bcl_string_keep(BclNumber n)1415 bcl_string_keep(BclNumber n)
1416 {
1417 return bcl_string_helper(n, false);
1418 }
1419
1420 #if BC_ENABLE_EXTRA_MATH
1421
1422 static BclNumber
bcl_irand_helper(BclNumber a,bool destruct)1423 bcl_irand_helper(BclNumber a, bool destruct)
1424 {
1425 BclError e = BCL_ERROR_NONE;
1426 BclNum* aptr;
1427 BclNum b;
1428 BclNumber idx;
1429 BclContext ctxt;
1430 BcVm* vm = bcl_getspecific();
1431
1432 BC_CHECK_CTXT(vm, ctxt);
1433
1434 BC_CHECK_NUM(ctxt, a);
1435
1436 BC_FUNC_HEADER(vm, err);
1437
1438 BCL_GROW_NUMS(ctxt);
1439
1440 assert(BCL_NO_GEN(a) < ctxt->nums.len);
1441
1442 aptr = BCL_NUM(ctxt, a);
1443
1444 assert(aptr != NULL && BCL_NUM_NUM(aptr) != NULL);
1445
1446 // Clear and initialize the result.
1447 bc_num_clear(BCL_NUM_NUM_NP(b));
1448 bc_num_init(BCL_NUM_NUM_NP(b), BC_NUM_DEF_SIZE);
1449
1450 bc_num_irand(BCL_NUM_NUM(aptr), BCL_NUM_NUM_NP(b), &vm->rng);
1451
1452 err:
1453
1454 if (destruct)
1455 {
1456 // Eat the operand.
1457 bcl_num_dtor(ctxt, a, aptr);
1458 }
1459
1460 BC_FUNC_FOOTER(vm, e);
1461 BC_MAYBE_SETUP(ctxt, e, b, idx);
1462
1463 return idx;
1464 }
1465
1466 BclNumber
bcl_irand(BclNumber a)1467 bcl_irand(BclNumber a)
1468 {
1469 return bcl_irand_helper(a, true);
1470 }
1471
1472 BclNumber
bcl_irand_keep(BclNumber a)1473 bcl_irand_keep(BclNumber a)
1474 {
1475 return bcl_irand_helper(a, false);
1476 }
1477
1478 /**
1479 * Helps bcl_frand(). This is separate because the error handling is easier that
1480 * way. It is also easier to do ifrand that way.
1481 * @param b The return parameter.
1482 * @param places The number of decimal places to generate.
1483 */
1484 static void
bcl_frandHelper(BcNum * restrict b,size_t places)1485 bcl_frandHelper(BcNum* restrict b, size_t places)
1486 {
1487 BcNum exp, pow, ten;
1488 BcDig exp_digs[BC_NUM_BIGDIG_LOG10];
1489 BcDig ten_digs[BC_NUM_BIGDIG_LOG10];
1490 BcVm* vm = bcl_getspecific();
1491
1492 // Set up temporaries.
1493 bc_num_setup(&exp, exp_digs, BC_NUM_BIGDIG_LOG10);
1494 bc_num_setup(&ten, ten_digs, BC_NUM_BIGDIG_LOG10);
1495
1496 ten.num[0] = 10;
1497 ten.len = 1;
1498
1499 bc_num_bigdig2num(&exp, (BcBigDig) places);
1500
1501 // Clear the temporary that might need to grow.
1502 bc_num_clear(&pow);
1503
1504 // Initialize the temporary that might need to grow.
1505 bc_num_init(&pow, bc_num_powReq(&ten, &exp, 0));
1506
1507 BC_SETJMP(vm, err);
1508
1509 // Generate the number.
1510 bc_num_pow(&ten, &exp, &pow, 0);
1511 bc_num_irand(&pow, b, &vm->rng);
1512
1513 // Make the number entirely fraction.
1514 bc_num_shiftRight(b, places);
1515
1516 err:
1517
1518 bc_num_free(&pow);
1519 BC_LONGJMP_CONT(vm);
1520 }
1521
1522 BclNumber
bcl_frand(size_t places)1523 bcl_frand(size_t places)
1524 {
1525 BclError e = BCL_ERROR_NONE;
1526 BclNum n;
1527 BclNumber idx;
1528 BclContext ctxt;
1529 BcVm* vm = bcl_getspecific();
1530
1531 BC_CHECK_CTXT(vm, ctxt);
1532
1533 BC_FUNC_HEADER(vm, err);
1534
1535 BCL_GROW_NUMS(ctxt);
1536
1537 // Clear and initialize the number.
1538 bc_num_clear(BCL_NUM_NUM_NP(n));
1539 bc_num_init(BCL_NUM_NUM_NP(n), BC_NUM_DEF_SIZE);
1540
1541 bcl_frandHelper(BCL_NUM_NUM_NP(n), places);
1542
1543 err:
1544
1545 BC_FUNC_FOOTER(vm, e);
1546 BC_MAYBE_SETUP(ctxt, e, n, idx);
1547
1548 return idx;
1549 }
1550
1551 /**
1552 * Helps bc_ifrand(). This is separate because error handling is easier that
1553 * way.
1554 * @param a The limit for bc_num_irand().
1555 * @param b The return parameter.
1556 * @param places The number of decimal places to generate.
1557 */
1558 static void
bcl_ifrandHelper(BcNum * restrict a,BcNum * restrict b,size_t places)1559 bcl_ifrandHelper(BcNum* restrict a, BcNum* restrict b, size_t places)
1560 {
1561 BcNum ir, fr;
1562 BcVm* vm = bcl_getspecific();
1563
1564 // Clear the integer and fractional numbers.
1565 bc_num_clear(&ir);
1566 bc_num_clear(&fr);
1567
1568 // Initialize the integer and fractional numbers.
1569 bc_num_init(&ir, BC_NUM_DEF_SIZE);
1570 bc_num_init(&fr, BC_NUM_DEF_SIZE);
1571
1572 BC_SETJMP(vm, err);
1573
1574 bc_num_irand(a, &ir, &vm->rng);
1575 bcl_frandHelper(&fr, places);
1576
1577 bc_num_add(&ir, &fr, b, 0);
1578
1579 err:
1580
1581 bc_num_free(&fr);
1582 bc_num_free(&ir);
1583 BC_LONGJMP_CONT(vm);
1584 }
1585
1586 static BclNumber
bcl_ifrand_helper(BclNumber a,size_t places,bool destruct)1587 bcl_ifrand_helper(BclNumber a, size_t places, bool destruct)
1588 {
1589 BclError e = BCL_ERROR_NONE;
1590 BclNum* aptr;
1591 BclNum b;
1592 BclNumber idx;
1593 BclContext ctxt;
1594 BcVm* vm = bcl_getspecific();
1595
1596 BC_CHECK_CTXT(vm, ctxt);
1597 BC_CHECK_NUM(ctxt, a);
1598
1599 BC_FUNC_HEADER(vm, err);
1600
1601 BCL_GROW_NUMS(ctxt);
1602
1603 assert(BCL_NO_GEN(a) < ctxt->nums.len);
1604
1605 aptr = BCL_NUM(ctxt, a);
1606
1607 assert(aptr != NULL && BCL_NUM_NUM(aptr) != NULL);
1608
1609 // Clear and initialize the number.
1610 bc_num_clear(BCL_NUM_NUM_NP(b));
1611 bc_num_init(BCL_NUM_NUM_NP(b), BC_NUM_DEF_SIZE);
1612
1613 bcl_ifrandHelper(BCL_NUM_NUM(aptr), BCL_NUM_NUM_NP(b), places);
1614
1615 err:
1616
1617 if (destruct)
1618 {
1619 // Eat the oprand.
1620 bcl_num_dtor(ctxt, a, aptr);
1621 }
1622
1623 BC_FUNC_FOOTER(vm, e);
1624 BC_MAYBE_SETUP(ctxt, e, b, idx);
1625
1626 return idx;
1627 }
1628
1629 BclNumber
bcl_ifrand(BclNumber a,size_t places)1630 bcl_ifrand(BclNumber a, size_t places)
1631 {
1632 return bcl_ifrand_helper(a, places, true);
1633 }
1634
1635 BclNumber
bcl_ifrand_keep(BclNumber a,size_t places)1636 bcl_ifrand_keep(BclNumber a, size_t places)
1637 {
1638 return bcl_ifrand_helper(a, places, false);
1639 }
1640
1641 static BclError
bcl_rand_seedWithNum_helper(BclNumber n,bool destruct)1642 bcl_rand_seedWithNum_helper(BclNumber n, bool destruct)
1643 {
1644 BclError e = BCL_ERROR_NONE;
1645 BclNum* nptr;
1646 BclContext ctxt;
1647 BcVm* vm = bcl_getspecific();
1648
1649 BC_CHECK_CTXT_ERR(vm, ctxt);
1650 BC_CHECK_NUM_ERR(ctxt, n);
1651
1652 BC_FUNC_HEADER(vm, err);
1653
1654 assert(BCL_NO_GEN(n) < ctxt->nums.len);
1655
1656 nptr = BCL_NUM(ctxt, n);
1657
1658 assert(nptr != NULL && BCL_NUM_NUM(nptr) != NULL);
1659
1660 bc_num_rng(BCL_NUM_NUM(nptr), &vm->rng);
1661
1662 err:
1663
1664 if (destruct)
1665 {
1666 // Eat the oprand.
1667 bcl_num_dtor(ctxt, n, nptr);
1668 }
1669
1670 BC_FUNC_FOOTER(vm, e);
1671
1672 return e;
1673 }
1674
1675 BclError
bcl_rand_seedWithNum(BclNumber n)1676 bcl_rand_seedWithNum(BclNumber n)
1677 {
1678 return bcl_rand_seedWithNum_helper(n, true);
1679 }
1680
1681 BclError
bcl_rand_seedWithNum_keep(BclNumber n)1682 bcl_rand_seedWithNum_keep(BclNumber n)
1683 {
1684 return bcl_rand_seedWithNum_helper(n, false);
1685 }
1686
1687 BclError
bcl_rand_seed(unsigned char seed[BCL_SEED_SIZE])1688 bcl_rand_seed(unsigned char seed[BCL_SEED_SIZE])
1689 {
1690 BclError e = BCL_ERROR_NONE;
1691 size_t i;
1692 ulong vals[BCL_SEED_ULONGS];
1693 BcVm* vm = bcl_getspecific();
1694
1695 BC_FUNC_HEADER(vm, err);
1696
1697 // Fill the array.
1698 for (i = 0; i < BCL_SEED_SIZE; ++i)
1699 {
1700 ulong val = ((ulong) seed[i])
1701 << (((ulong) CHAR_BIT) * (i % sizeof(ulong)));
1702 vals[i / sizeof(long)] |= val;
1703 }
1704
1705 bc_rand_seed(&vm->rng, vals[0], vals[1], vals[2], vals[3]);
1706
1707 err:
1708
1709 BC_FUNC_FOOTER(vm, e);
1710
1711 return e;
1712 }
1713
1714 void
bcl_rand_reseed(void)1715 bcl_rand_reseed(void)
1716 {
1717 BcVm* vm = bcl_getspecific();
1718
1719 bc_rand_srand(bc_vec_top(&vm->rng.v));
1720 }
1721
1722 BclNumber
bcl_rand_seed2num(void)1723 bcl_rand_seed2num(void)
1724 {
1725 BclError e = BCL_ERROR_NONE;
1726 BclNum n;
1727 BclNumber idx;
1728 BclContext ctxt;
1729 BcVm* vm = bcl_getspecific();
1730
1731 BC_CHECK_CTXT(vm, ctxt);
1732
1733 BC_FUNC_HEADER(vm, err);
1734
1735 // Clear and initialize the number.
1736 bc_num_clear(BCL_NUM_NUM_NP(n));
1737 bc_num_init(BCL_NUM_NUM_NP(n), BC_NUM_DEF_SIZE);
1738
1739 bc_num_createFromRNG(BCL_NUM_NUM_NP(n), &vm->rng);
1740
1741 err:
1742
1743 BC_FUNC_FOOTER(vm, e);
1744 BC_MAYBE_SETUP(ctxt, e, n, idx);
1745
1746 return idx;
1747 }
1748
1749 BclRandInt
bcl_rand_int(void)1750 bcl_rand_int(void)
1751 {
1752 BcVm* vm = bcl_getspecific();
1753
1754 return (BclRandInt) bc_rand_int(&vm->rng);
1755 }
1756
1757 BclRandInt
bcl_rand_bounded(BclRandInt bound)1758 bcl_rand_bounded(BclRandInt bound)
1759 {
1760 BcVm* vm = bcl_getspecific();
1761
1762 if (bound <= 1) return 0;
1763 return (BclRandInt) bc_rand_bounded(&vm->rng, (BcRand) bound);
1764 }
1765
1766 #endif // BC_ENABLE_EXTRA_MATH
1767
1768 #endif // BC_ENABLE_LIBRARY
1769