]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/bc/src/library.c
zfs: merge openzfs/zfs@2e6b3c4d9
[FreeBSD/FreeBSD.git] / contrib / bc / src / library.c
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
66 bcl_invalidGeneration(void)
67 {
68         abort();
69 }
70
71 BC_NORETURN void
72 bcl_nonexistentNum(void)
73 {
74         abort();
75 }
76
77 BC_NORETURN 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
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
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*
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
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
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
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
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
277 bcl_context(void)
278 {
279         BcVm* vm = bcl_getspecific();
280         return bcl_contextHelper(vm);
281 }
282
283 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
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
330 bcl_gc(void)
331 {
332         bc_vm_freeTemps();
333 }
334
335 bool
336 bcl_abortOnFatalError(void)
337 {
338         BcVm* vm = bcl_getspecific();
339
340         return vm->abrt;
341 }
342
343 void
344 bcl_setAbortOnFatalError(bool abrt)
345 {
346         BcVm* vm = bcl_getspecific();
347
348         vm->abrt = abrt;
349 }
350
351 bool
352 bcl_leadingZeroes(void)
353 {
354         BcVm* vm = bcl_getspecific();
355
356         return vm->leading_zeroes;
357 }
358
359 void
360 bcl_setLeadingZeroes(bool leadingZeroes)
361 {
362         BcVm* vm = bcl_getspecific();
363
364         vm->leading_zeroes = leadingZeroes;
365 }
366
367 bool
368 bcl_digitClamp(void)
369 {
370         BcVm* vm = bcl_getspecific();
371
372         return vm->digit_clamp;
373 }
374
375 void
376 bcl_setDigitClamp(bool digitClamp)
377 {
378         BcVm* vm = bcl_getspecific();
379
380         vm->digit_clamp = digitClamp;
381 }
382
383 BclContext
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
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
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
432 bcl_ctxt_scale(BclContext ctxt)
433 {
434         return ctxt->scale;
435 }
436
437 void
438 bcl_ctxt_setScale(BclContext ctxt, size_t scale)
439 {
440         ctxt->scale = scale;
441 }
442
443 size_t
444 bcl_ctxt_ibase(BclContext ctxt)
445 {
446         return ctxt->ibase;
447 }
448
449 void
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
458 bcl_ctxt_obase(BclContext ctxt)
459 {
460         return ctxt->obase;
461 }
462
463 void
464 bcl_ctxt_setObase(BclContext ctxt, size_t obase)
465 {
466         ctxt->obase = obase;
467 }
468
469 BclError
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
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
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
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
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
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
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
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
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
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
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
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
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
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
844 bcl_bigdig(BclNumber n, BclBigDig* result)
845 {
846         return bcl_bigdig_helper(n, result, true);
847 }
848
849 BclError
850 bcl_bigdig_keep(BclNumber n, BclBigDig* result)
851 {
852         return bcl_bigdig_helper(n, result, false);
853 }
854
855 BclNumber
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
1075 bcl_sqrt(BclNumber a)
1076 {
1077         return bcl_sqrt_helper(a, true);
1078 }
1079
1080 BclNumber
1081 bcl_sqrt_keep(BclNumber a)
1082 {
1083         return bcl_sqrt_helper(a, false);
1084 }
1085
1086 static BclError
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
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
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
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
1237 bcl_modexp(BclNumber a, BclNumber b, BclNumber c)
1238 {
1239         return bcl_modexp_helper(a, b, c, true);
1240 }
1241
1242 BclNumber
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
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
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
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
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*
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*
1409 bcl_string(BclNumber n)
1410 {
1411         return bcl_string_helper(n, true);
1412 }
1413
1414 char*
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
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
1467 bcl_irand(BclNumber a)
1468 {
1469         return bcl_irand_helper(a, true);
1470 }
1471
1472 BclNumber
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
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
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
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
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
1630 bcl_ifrand(BclNumber a, size_t places)
1631 {
1632         return bcl_ifrand_helper(a, places, true);
1633 }
1634
1635 BclNumber
1636 bcl_ifrand_keep(BclNumber a, size_t places)
1637 {
1638         return bcl_ifrand_helper(a, places, false);
1639 }
1640
1641 static BclError
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
1676 bcl_rand_seedWithNum(BclNumber n)
1677 {
1678         return bcl_rand_seedWithNum_helper(n, true);
1679 }
1680
1681 BclError
1682 bcl_rand_seedWithNum_keep(BclNumber n)
1683 {
1684         return bcl_rand_seedWithNum_helper(n, false);
1685 }
1686
1687 BclError
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
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
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
1750 bcl_rand_int(void)
1751 {
1752         BcVm* vm = bcl_getspecific();
1753
1754         return (BclRandInt) bc_rand_int(&vm->rng);
1755 }
1756
1757 BclRandInt
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