xref: /freebsd/contrib/bc/src/library.c (revision a970610a3af63b3f4df5b69d91c6b4093a00ed8f)
150696a6eSStefan Eßer /*
250696a6eSStefan Eßer  * *****************************************************************************
350696a6eSStefan Eßer  *
450696a6eSStefan Eßer  * SPDX-License-Identifier: BSD-2-Clause
550696a6eSStefan Eßer  *
6*a970610aSStefan Eßer  * Copyright (c) 2018-2024 Gavin D. Howard and contributors.
750696a6eSStefan Eßer  *
850696a6eSStefan Eßer  * Redistribution and use in source and binary forms, with or without
950696a6eSStefan Eßer  * modification, are permitted provided that the following conditions are met:
1050696a6eSStefan Eßer  *
1150696a6eSStefan Eßer  * * Redistributions of source code must retain the above copyright notice, this
1250696a6eSStefan Eßer  *   list of conditions and the following disclaimer.
1350696a6eSStefan Eßer  *
1450696a6eSStefan Eßer  * * Redistributions in binary form must reproduce the above copyright notice,
1550696a6eSStefan Eßer  *   this list of conditions and the following disclaimer in the documentation
1650696a6eSStefan Eßer  *   and/or other materials provided with the distribution.
1750696a6eSStefan Eßer  *
1850696a6eSStefan Eßer  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
1950696a6eSStefan Eßer  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2050696a6eSStefan Eßer  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2150696a6eSStefan Eßer  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
2250696a6eSStefan Eßer  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2350696a6eSStefan Eßer  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2450696a6eSStefan Eßer  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2550696a6eSStefan Eßer  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2650696a6eSStefan Eßer  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2750696a6eSStefan Eßer  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2850696a6eSStefan Eßer  * POSSIBILITY OF SUCH DAMAGE.
2950696a6eSStefan Eßer  *
3050696a6eSStefan Eßer  * *****************************************************************************
3150696a6eSStefan Eßer  *
3250696a6eSStefan Eßer  * The public functions for libbc.
3350696a6eSStefan Eßer  *
3450696a6eSStefan Eßer  */
3550696a6eSStefan Eßer 
3650696a6eSStefan Eßer #if BC_ENABLE_LIBRARY
3750696a6eSStefan Eßer 
3850696a6eSStefan Eßer #include <setjmp.h>
3950696a6eSStefan Eßer #include <string.h>
4050696a6eSStefan Eßer #include <time.h>
4150696a6eSStefan Eßer 
4250696a6eSStefan Eßer #include <bcl.h>
4350696a6eSStefan Eßer 
4450696a6eSStefan Eßer #include <library.h>
4550696a6eSStefan Eßer #include <num.h>
4650696a6eSStefan Eßer #include <vm.h>
4750696a6eSStefan Eßer 
48d101cdd6SStefan Eßer #ifndef _WIN32
49d101cdd6SStefan Eßer #include <pthread.h>
50d101cdd6SStefan Eßer #endif // _WIN32
51d101cdd6SStefan Eßer 
5244d4804dSStefan Eßer // The asserts in this file are important to testing; in many cases, the test
5344d4804dSStefan Eßer // would not work without the asserts, so don't remove them without reason.
5444d4804dSStefan Eßer //
5544d4804dSStefan Eßer // Also, there are many uses of bc_num_clear() here; that is because numbers are
5644d4804dSStefan Eßer // being reused, and a clean slate is required.
5744d4804dSStefan Eßer //
58d101cdd6SStefan Eßer // Also, there are a bunch of BC_UNSETJMP between calls to bc_num_init(). That
59d101cdd6SStefan Eßer // is because locals are being initialized, and unlike bc proper, this code
60d101cdd6SStefan Eßer // cannot assume that allocation failures are fatal. So we have to reset the
61d101cdd6SStefan Eßer // jumps every time to ensure that the locals will be correct after jumping.
6250696a6eSStefan Eßer 
63175a4d10SStefan Eßer #if BC_ENABLE_MEMCHECK
64175a4d10SStefan Eßer 
65175a4d10SStefan Eßer BC_NORETURN void
66175a4d10SStefan Eßer bcl_invalidGeneration(void)
67175a4d10SStefan Eßer {
68175a4d10SStefan Eßer 	abort();
69175a4d10SStefan Eßer }
70175a4d10SStefan Eßer 
71175a4d10SStefan Eßer BC_NORETURN void
72175a4d10SStefan Eßer bcl_nonexistentNum(void)
73175a4d10SStefan Eßer {
74175a4d10SStefan Eßer 	abort();
75175a4d10SStefan Eßer }
76175a4d10SStefan Eßer 
77175a4d10SStefan Eßer BC_NORETURN void
78175a4d10SStefan Eßer bcl_numIdxOutOfRange(void)
79175a4d10SStefan Eßer {
80175a4d10SStefan Eßer 	abort();
81175a4d10SStefan Eßer }
82175a4d10SStefan Eßer 
83175a4d10SStefan Eßer #endif // BC_ENABLE_MEMCHECK
84175a4d10SStefan Eßer 
85d101cdd6SStefan Eßer static BclTls* tls = NULL;
86d101cdd6SStefan Eßer static BclTls tls_real;
87d101cdd6SStefan Eßer 
88d101cdd6SStefan Eßer BclError
89d101cdd6SStefan Eßer bcl_start(void)
9078bc019dSStefan Eßer {
91d101cdd6SStefan Eßer #ifndef _WIN32
9250696a6eSStefan Eßer 
93d101cdd6SStefan Eßer 	int r;
9450696a6eSStefan Eßer 
95d101cdd6SStefan Eßer 	if (tls != NULL) return BCL_ERROR_NONE;
9650696a6eSStefan Eßer 
97d101cdd6SStefan Eßer 	r = pthread_key_create(&tls_real, NULL);
98d101cdd6SStefan Eßer 	if (BC_ERR(r != 0)) return BCL_ERROR_FATAL_ALLOC_ERR;
99d101cdd6SStefan Eßer 
100d101cdd6SStefan Eßer #else // _WIN32
101d101cdd6SStefan Eßer 
102d101cdd6SStefan Eßer 	if (tls != NULL) return BCL_ERROR_NONE;
103d101cdd6SStefan Eßer 
104d101cdd6SStefan Eßer 	tls_real = TlsAlloc();
105d101cdd6SStefan Eßer 	if (BC_ERR(tls_real == TLS_OUT_OF_INDEXES))
106d101cdd6SStefan Eßer 	{
107d101cdd6SStefan Eßer 		return BCL_ERROR_FATAL_ALLOC_ERR;
10850696a6eSStefan Eßer 	}
10950696a6eSStefan Eßer 
110d101cdd6SStefan Eßer #endif // _WIN32
111d101cdd6SStefan Eßer 
112d101cdd6SStefan Eßer 	tls = &tls_real;
113d101cdd6SStefan Eßer 
114d101cdd6SStefan Eßer 	return BCL_ERROR_NONE;
115d101cdd6SStefan Eßer }
116d101cdd6SStefan Eßer 
117d101cdd6SStefan Eßer /**
118d101cdd6SStefan Eßer  * Sets the thread-specific data for the thread.
119d101cdd6SStefan Eßer  * @param vm  The @a BcVm to set as the thread data.
120d101cdd6SStefan Eßer  * @return    An error code, if any.
121d101cdd6SStefan Eßer  */
122d101cdd6SStefan Eßer static BclError
123d101cdd6SStefan Eßer bcl_setspecific(BcVm* vm)
12478bc019dSStefan Eßer {
125d101cdd6SStefan Eßer #ifndef _WIN32
126d101cdd6SStefan Eßer 
127d101cdd6SStefan Eßer 	int r;
128d101cdd6SStefan Eßer 
129d101cdd6SStefan Eßer 	assert(tls != NULL);
130d101cdd6SStefan Eßer 
131d101cdd6SStefan Eßer 	r = pthread_setspecific(tls_real, vm);
132d101cdd6SStefan Eßer 	if (BC_ERR(r != 0)) return BCL_ERROR_FATAL_ALLOC_ERR;
133d101cdd6SStefan Eßer 
134d101cdd6SStefan Eßer #else // _WIN32
135d101cdd6SStefan Eßer 
136d101cdd6SStefan Eßer 	bool r;
137d101cdd6SStefan Eßer 
138d101cdd6SStefan Eßer 	assert(tls != NULL);
139d101cdd6SStefan Eßer 
140d101cdd6SStefan Eßer 	r = TlsSetValue(tls_real, vm);
141d101cdd6SStefan Eßer 	if (BC_ERR(!r)) return BCL_ERROR_FATAL_ALLOC_ERR;
142d101cdd6SStefan Eßer 
143d101cdd6SStefan Eßer #endif // _WIN32
144d101cdd6SStefan Eßer 
145d101cdd6SStefan Eßer 	return BCL_ERROR_NONE;
146d101cdd6SStefan Eßer }
147d101cdd6SStefan Eßer 
148d101cdd6SStefan Eßer BcVm*
149d101cdd6SStefan Eßer bcl_getspecific(void)
150d101cdd6SStefan Eßer {
151d101cdd6SStefan Eßer 	BcVm* vm;
152d101cdd6SStefan Eßer 
153d101cdd6SStefan Eßer #ifndef _WIN32
154d101cdd6SStefan Eßer 
155d101cdd6SStefan Eßer 	vm = pthread_getspecific(tls_real);
156d101cdd6SStefan Eßer 
157d101cdd6SStefan Eßer #else // _WIN32
158d101cdd6SStefan Eßer 
159d101cdd6SStefan Eßer 	vm = TlsGetValue(tls_real);
160d101cdd6SStefan Eßer 
161d101cdd6SStefan Eßer #endif // _WIN32
162d101cdd6SStefan Eßer 
163d101cdd6SStefan Eßer 	return vm;
16450696a6eSStefan Eßer }
16550696a6eSStefan Eßer 
16678bc019dSStefan Eßer BclError
16778bc019dSStefan Eßer bcl_init(void)
16878bc019dSStefan Eßer {
16950696a6eSStefan Eßer 	BclError e = BCL_ERROR_NONE;
170d101cdd6SStefan Eßer 	BcVm* vm;
17150696a6eSStefan Eßer 
172d101cdd6SStefan Eßer 	assert(tls != NULL);
17310041e99SStefan Eßer 
174d101cdd6SStefan Eßer 	vm = bcl_getspecific();
175d101cdd6SStefan Eßer 	if (vm != NULL)
17678bc019dSStefan Eßer 	{
177d101cdd6SStefan Eßer 		assert(vm->refs >= 1);
178d101cdd6SStefan Eßer 
179d101cdd6SStefan Eßer 		vm->refs += 1;
180d101cdd6SStefan Eßer 
18110041e99SStefan Eßer 		return e;
18210041e99SStefan Eßer 	}
18350696a6eSStefan Eßer 
184d101cdd6SStefan Eßer 	vm = bc_vm_malloc(sizeof(BcVm));
185d101cdd6SStefan Eßer 	if (BC_ERR(vm == NULL)) return BCL_ERROR_FATAL_ALLOC_ERR;
186d101cdd6SStefan Eßer 
187d101cdd6SStefan Eßer 	e = bcl_setspecific(vm);
188d101cdd6SStefan Eßer 	if (BC_ERR(e != BCL_ERROR_NONE))
189d101cdd6SStefan Eßer 	{
190d101cdd6SStefan Eßer 		free(vm);
191d101cdd6SStefan Eßer 		return e;
192d101cdd6SStefan Eßer 	}
193d101cdd6SStefan Eßer 
194d101cdd6SStefan Eßer 	memset(vm, 0, sizeof(BcVm));
195d101cdd6SStefan Eßer 
196d101cdd6SStefan Eßer 	vm->refs += 1;
197d101cdd6SStefan Eßer 
198d101cdd6SStefan Eßer 	assert(vm->refs == 1);
199d101cdd6SStefan Eßer 
20044d4804dSStefan Eßer 	// Setting these to NULL ensures that if an error occurs, we only free what
20144d4804dSStefan Eßer 	// is necessary.
202d101cdd6SStefan Eßer 	vm->ctxts.v = NULL;
203d101cdd6SStefan Eßer 	vm->jmp_bufs.v = NULL;
204d101cdd6SStefan Eßer 	vm->out.v = NULL;
20550696a6eSStefan Eßer 
206d101cdd6SStefan Eßer 	vm->abrt = false;
207d101cdd6SStefan Eßer 	vm->leading_zeroes = false;
208d101cdd6SStefan Eßer 	vm->digit_clamp = true;
20950696a6eSStefan Eßer 
21044d4804dSStefan Eßer 	// The jmp_bufs always has to be initialized first.
211d101cdd6SStefan Eßer 	bc_vec_init(&vm->jmp_bufs, sizeof(sigjmp_buf), BC_DTOR_NONE);
21250696a6eSStefan Eßer 
213d101cdd6SStefan Eßer 	BC_FUNC_HEADER(vm, err);
21450696a6eSStefan Eßer 
21550696a6eSStefan Eßer 	bc_vm_init();
21650696a6eSStefan Eßer 
217d101cdd6SStefan Eßer 	bc_vec_init(&vm->ctxts, sizeof(BclContext), BC_DTOR_NONE);
218d101cdd6SStefan Eßer 	bc_vec_init(&vm->out, sizeof(uchar), BC_DTOR_NONE);
21950696a6eSStefan Eßer 
220175a4d10SStefan Eßer #if BC_ENABLE_EXTRA_MATH
221175a4d10SStefan Eßer 
222175a4d10SStefan Eßer 	// We need to seed this in case /dev/random and /dev/urandom don't work.
22350696a6eSStefan Eßer 	srand((unsigned int) time(NULL));
224d101cdd6SStefan Eßer 	bc_rand_init(&vm->rng);
22550696a6eSStefan Eßer 
226175a4d10SStefan Eßer #endif // BC_ENABLE_EXTRA_MATH
227175a4d10SStefan Eßer 
22850696a6eSStefan Eßer err:
229d101cdd6SStefan Eßer 
230103d7cdfSStefan Eßer 	BC_FUNC_FOOTER(vm, e);
231103d7cdfSStefan Eßer 
23244d4804dSStefan Eßer 	// This is why we had to set them to NULL.
233d101cdd6SStefan Eßer 	if (BC_ERR(vm != NULL && vm->err))
23478bc019dSStefan Eßer 	{
235d101cdd6SStefan Eßer 		if (vm->out.v != NULL) bc_vec_free(&vm->out);
236d101cdd6SStefan Eßer 		if (vm->jmp_bufs.v != NULL) bc_vec_free(&vm->jmp_bufs);
237d101cdd6SStefan Eßer 		if (vm->ctxts.v != NULL) bc_vec_free(&vm->ctxts);
238d101cdd6SStefan Eßer 		bcl_setspecific(NULL);
239d101cdd6SStefan Eßer 		free(vm);
24050696a6eSStefan Eßer 	}
24150696a6eSStefan Eßer 
24250696a6eSStefan Eßer 	return e;
24350696a6eSStefan Eßer }
24450696a6eSStefan Eßer 
24578bc019dSStefan Eßer BclError
24678bc019dSStefan Eßer bcl_pushContext(BclContext ctxt)
24778bc019dSStefan Eßer {
24850696a6eSStefan Eßer 	BclError e = BCL_ERROR_NONE;
249d101cdd6SStefan Eßer 	BcVm* vm = bcl_getspecific();
25050696a6eSStefan Eßer 
251d101cdd6SStefan Eßer 	BC_FUNC_HEADER(vm, err);
25250696a6eSStefan Eßer 
253d101cdd6SStefan Eßer 	bc_vec_push(&vm->ctxts, &ctxt);
25450696a6eSStefan Eßer 
25550696a6eSStefan Eßer err:
256175a4d10SStefan Eßer 
257d101cdd6SStefan Eßer 	BC_FUNC_FOOTER(vm, e);
25850696a6eSStefan Eßer 	return e;
25950696a6eSStefan Eßer }
26050696a6eSStefan Eßer 
26178bc019dSStefan Eßer void
26278bc019dSStefan Eßer bcl_popContext(void)
26378bc019dSStefan Eßer {
264d101cdd6SStefan Eßer 	BcVm* vm = bcl_getspecific();
265d101cdd6SStefan Eßer 
266d101cdd6SStefan Eßer 	if (vm->ctxts.len) bc_vec_pop(&vm->ctxts);
267d101cdd6SStefan Eßer }
268d101cdd6SStefan Eßer 
269d101cdd6SStefan Eßer static BclContext
270d101cdd6SStefan Eßer bcl_contextHelper(BcVm* vm)
271d101cdd6SStefan Eßer {
272d101cdd6SStefan Eßer 	if (!vm->ctxts.len) return NULL;
273d101cdd6SStefan Eßer 	return *((BclContext*) bc_vec_top(&vm->ctxts));
27450696a6eSStefan Eßer }
27550696a6eSStefan Eßer 
27678bc019dSStefan Eßer BclContext
27778bc019dSStefan Eßer bcl_context(void)
27878bc019dSStefan Eßer {
279d101cdd6SStefan Eßer 	BcVm* vm = bcl_getspecific();
280d101cdd6SStefan Eßer 	return bcl_contextHelper(vm);
28150696a6eSStefan Eßer }
28250696a6eSStefan Eßer 
28378bc019dSStefan Eßer void
28478bc019dSStefan Eßer bcl_free(void)
28578bc019dSStefan Eßer {
28644d4804dSStefan Eßer 	size_t i;
287d101cdd6SStefan Eßer 	BcVm* vm = bcl_getspecific();
28844d4804dSStefan Eßer 
289d101cdd6SStefan Eßer 	vm->refs -= 1;
290d101cdd6SStefan Eßer 	if (vm->refs) return;
29110041e99SStefan Eßer 
292175a4d10SStefan Eßer #if BC_ENABLE_EXTRA_MATH
293d101cdd6SStefan Eßer 	bc_rand_free(&vm->rng);
294175a4d10SStefan Eßer #endif // BC_ENABLE_EXTRA_MATH
295d101cdd6SStefan Eßer 	bc_vec_free(&vm->out);
29650696a6eSStefan Eßer 
297d101cdd6SStefan Eßer 	for (i = 0; i < vm->ctxts.len; ++i)
29878bc019dSStefan Eßer 	{
299d101cdd6SStefan Eßer 		BclContext ctxt = *((BclContext*) bc_vec_item(&vm->ctxts, i));
30050696a6eSStefan Eßer 		bcl_ctxt_free(ctxt);
30150696a6eSStefan Eßer 	}
30250696a6eSStefan Eßer 
303d101cdd6SStefan Eßer 	bc_vec_free(&vm->ctxts);
30450696a6eSStefan Eßer 
30510328f8bSStefan Eßer 	bc_vm_atexit();
30650696a6eSStefan Eßer 
307d101cdd6SStefan Eßer 	free(vm);
308d101cdd6SStefan Eßer 	bcl_setspecific(NULL);
309d101cdd6SStefan Eßer }
31050696a6eSStefan Eßer 
311d101cdd6SStefan Eßer void
312d101cdd6SStefan Eßer bcl_end(void)
313d101cdd6SStefan Eßer {
314d101cdd6SStefan Eßer #ifndef _WIN32
31550696a6eSStefan Eßer 
316d101cdd6SStefan Eßer 	// We ignore the return value.
317d101cdd6SStefan Eßer 	pthread_key_delete(tls_real);
318d101cdd6SStefan Eßer 
319d101cdd6SStefan Eßer #else // _WIN32
320d101cdd6SStefan Eßer 
321d101cdd6SStefan Eßer 	// We ignore the return value.
322d101cdd6SStefan Eßer 	TlsFree(tls_real);
323d101cdd6SStefan Eßer 
324d101cdd6SStefan Eßer #endif // _WIN32
325d101cdd6SStefan Eßer 
326d101cdd6SStefan Eßer 	tls = NULL;
32750696a6eSStefan Eßer }
32850696a6eSStefan Eßer 
32978bc019dSStefan Eßer void
33078bc019dSStefan Eßer bcl_gc(void)
33178bc019dSStefan Eßer {
33250696a6eSStefan Eßer 	bc_vm_freeTemps();
33350696a6eSStefan Eßer }
33450696a6eSStefan Eßer 
33578bc019dSStefan Eßer bool
33678bc019dSStefan Eßer bcl_abortOnFatalError(void)
33778bc019dSStefan Eßer {
338d101cdd6SStefan Eßer 	BcVm* vm = bcl_getspecific();
339d101cdd6SStefan Eßer 
340d101cdd6SStefan Eßer 	return vm->abrt;
34150696a6eSStefan Eßer }
34250696a6eSStefan Eßer 
34378bc019dSStefan Eßer void
34478bc019dSStefan Eßer bcl_setAbortOnFatalError(bool abrt)
34578bc019dSStefan Eßer {
346d101cdd6SStefan Eßer 	BcVm* vm = bcl_getspecific();
347d101cdd6SStefan Eßer 
348d101cdd6SStefan Eßer 	vm->abrt = abrt;
34950696a6eSStefan Eßer }
35050696a6eSStefan Eßer 
35178bc019dSStefan Eßer bool
35278bc019dSStefan Eßer bcl_leadingZeroes(void)
35378bc019dSStefan Eßer {
354d101cdd6SStefan Eßer 	BcVm* vm = bcl_getspecific();
355d101cdd6SStefan Eßer 
356d101cdd6SStefan Eßer 	return vm->leading_zeroes;
357d43fa8efSStefan Eßer }
358d43fa8efSStefan Eßer 
35978bc019dSStefan Eßer void
36078bc019dSStefan Eßer bcl_setLeadingZeroes(bool leadingZeroes)
36178bc019dSStefan Eßer {
362d101cdd6SStefan Eßer 	BcVm* vm = bcl_getspecific();
363d101cdd6SStefan Eßer 
364d101cdd6SStefan Eßer 	vm->leading_zeroes = leadingZeroes;
365d101cdd6SStefan Eßer }
366d101cdd6SStefan Eßer 
367d101cdd6SStefan Eßer bool
368d101cdd6SStefan Eßer bcl_digitClamp(void)
369d101cdd6SStefan Eßer {
370d101cdd6SStefan Eßer 	BcVm* vm = bcl_getspecific();
371d101cdd6SStefan Eßer 
372d101cdd6SStefan Eßer 	return vm->digit_clamp;
373d101cdd6SStefan Eßer }
374d101cdd6SStefan Eßer 
375d101cdd6SStefan Eßer void
376d101cdd6SStefan Eßer bcl_setDigitClamp(bool digitClamp)
377d101cdd6SStefan Eßer {
378d101cdd6SStefan Eßer 	BcVm* vm = bcl_getspecific();
379d101cdd6SStefan Eßer 
380d101cdd6SStefan Eßer 	vm->digit_clamp = digitClamp;
381d43fa8efSStefan Eßer }
382d43fa8efSStefan Eßer 
38378bc019dSStefan Eßer BclContext
38478bc019dSStefan Eßer bcl_ctxt_create(void)
38578bc019dSStefan Eßer {
386d101cdd6SStefan Eßer 	BcVm* vm = bcl_getspecific();
38750696a6eSStefan Eßer 	BclContext ctxt = NULL;
38850696a6eSStefan Eßer 
389d101cdd6SStefan Eßer 	BC_FUNC_HEADER(vm, err);
39050696a6eSStefan Eßer 
39144d4804dSStefan Eßer 	// We want the context to be free of any interference of other parties, so
39244d4804dSStefan Eßer 	// malloc() is appropriate here.
39350696a6eSStefan Eßer 	ctxt = bc_vm_malloc(sizeof(BclCtxt));
39450696a6eSStefan Eßer 
395175a4d10SStefan Eßer 	bc_vec_init(&ctxt->nums, sizeof(BclNum), BC_DTOR_BCL_NUM);
39644d4804dSStefan Eßer 	bc_vec_init(&ctxt->free_nums, sizeof(BclNumber), BC_DTOR_NONE);
39750696a6eSStefan Eßer 
39850696a6eSStefan Eßer 	ctxt->scale = 0;
39950696a6eSStefan Eßer 	ctxt->ibase = 10;
40050696a6eSStefan Eßer 	ctxt->obase = 10;
40150696a6eSStefan Eßer 
40250696a6eSStefan Eßer err:
403d101cdd6SStefan Eßer 
404d101cdd6SStefan Eßer 	if (BC_ERR(vm->err && ctxt != NULL))
40578bc019dSStefan Eßer 	{
40650696a6eSStefan Eßer 		if (ctxt->nums.v != NULL) bc_vec_free(&ctxt->nums);
40750696a6eSStefan Eßer 		free(ctxt);
40850696a6eSStefan Eßer 		ctxt = NULL;
40950696a6eSStefan Eßer 	}
41050696a6eSStefan Eßer 
411d101cdd6SStefan Eßer 	BC_FUNC_FOOTER_NO_ERR(vm);
41250696a6eSStefan Eßer 
41350696a6eSStefan Eßer 	return ctxt;
41450696a6eSStefan Eßer }
41550696a6eSStefan Eßer 
41678bc019dSStefan Eßer void
41778bc019dSStefan Eßer bcl_ctxt_free(BclContext ctxt)
41878bc019dSStefan Eßer {
41950696a6eSStefan Eßer 	bc_vec_free(&ctxt->free_nums);
42050696a6eSStefan Eßer 	bc_vec_free(&ctxt->nums);
42150696a6eSStefan Eßer 	free(ctxt);
42250696a6eSStefan Eßer }
42350696a6eSStefan Eßer 
42478bc019dSStefan Eßer void
42578bc019dSStefan Eßer bcl_ctxt_freeNums(BclContext ctxt)
42678bc019dSStefan Eßer {
42710328f8bSStefan Eßer 	bc_vec_popAll(&ctxt->nums);
42810328f8bSStefan Eßer 	bc_vec_popAll(&ctxt->free_nums);
42950696a6eSStefan Eßer }
43050696a6eSStefan Eßer 
43178bc019dSStefan Eßer size_t
43278bc019dSStefan Eßer bcl_ctxt_scale(BclContext ctxt)
43378bc019dSStefan Eßer {
43450696a6eSStefan Eßer 	return ctxt->scale;
43550696a6eSStefan Eßer }
43650696a6eSStefan Eßer 
43778bc019dSStefan Eßer void
43878bc019dSStefan Eßer bcl_ctxt_setScale(BclContext ctxt, size_t scale)
43978bc019dSStefan Eßer {
44050696a6eSStefan Eßer 	ctxt->scale = scale;
44150696a6eSStefan Eßer }
44250696a6eSStefan Eßer 
44378bc019dSStefan Eßer size_t
44478bc019dSStefan Eßer bcl_ctxt_ibase(BclContext ctxt)
44578bc019dSStefan Eßer {
44650696a6eSStefan Eßer 	return ctxt->ibase;
44750696a6eSStefan Eßer }
44850696a6eSStefan Eßer 
44978bc019dSStefan Eßer void
45078bc019dSStefan Eßer bcl_ctxt_setIbase(BclContext ctxt, size_t ibase)
45178bc019dSStefan Eßer {
45250696a6eSStefan Eßer 	if (ibase < BC_NUM_MIN_BASE) ibase = BC_NUM_MIN_BASE;
45350696a6eSStefan Eßer 	else if (ibase > BC_NUM_MAX_IBASE) ibase = BC_NUM_MAX_IBASE;
45450696a6eSStefan Eßer 	ctxt->ibase = ibase;
45550696a6eSStefan Eßer }
45650696a6eSStefan Eßer 
45778bc019dSStefan Eßer size_t
45878bc019dSStefan Eßer bcl_ctxt_obase(BclContext ctxt)
45978bc019dSStefan Eßer {
46050696a6eSStefan Eßer 	return ctxt->obase;
46150696a6eSStefan Eßer }
46250696a6eSStefan Eßer 
46378bc019dSStefan Eßer void
46478bc019dSStefan Eßer bcl_ctxt_setObase(BclContext ctxt, size_t obase)
46578bc019dSStefan Eßer {
46650696a6eSStefan Eßer 	ctxt->obase = obase;
46750696a6eSStefan Eßer }
46850696a6eSStefan Eßer 
46978bc019dSStefan Eßer BclError
47078bc019dSStefan Eßer bcl_err(BclNumber n)
47178bc019dSStefan Eßer {
47250696a6eSStefan Eßer 	BclContext ctxt;
473d101cdd6SStefan Eßer 	BcVm* vm = bcl_getspecific();
47450696a6eSStefan Eßer 
475d101cdd6SStefan Eßer 	BC_CHECK_CTXT_ERR(vm, ctxt);
47650696a6eSStefan Eßer 
477175a4d10SStefan Eßer 	// We need to clear the top byte in memcheck mode. We can do this because
478175a4d10SStefan Eßer 	// the parameter is a copy.
479175a4d10SStefan Eßer 	BCL_CLEAR_GEN(n);
480175a4d10SStefan Eßer 
48144d4804dSStefan Eßer 	// Errors are encoded as (0 - error_code). If the index is in that range, it
48244d4804dSStefan Eßer 	// is an encoded error.
48378bc019dSStefan Eßer 	if (n.i >= ctxt->nums.len)
48478bc019dSStefan Eßer 	{
48550696a6eSStefan Eßer 		if (n.i > 0 - (size_t) BCL_ERROR_NELEMS) return (BclError) (0 - n.i);
48650696a6eSStefan Eßer 		else return BCL_ERROR_INVALID_NUM;
48750696a6eSStefan Eßer 	}
48850696a6eSStefan Eßer 	else return BCL_ERROR_NONE;
48950696a6eSStefan Eßer }
49050696a6eSStefan Eßer 
49144d4804dSStefan Eßer /**
49244d4804dSStefan Eßer  * Inserts a BcNum into a context's list of numbers.
49344d4804dSStefan Eßer  * @param ctxt  The context to insert into.
49444d4804dSStefan Eßer  * @param n     The BcNum to insert.
49544d4804dSStefan Eßer  * @return      The resulting BclNumber from the insert.
49644d4804dSStefan Eßer  */
49778bc019dSStefan Eßer static BclNumber
498175a4d10SStefan Eßer bcl_num_insert(BclContext ctxt, BclNum* restrict n)
49978bc019dSStefan Eßer {
50050696a6eSStefan Eßer 	BclNumber idx;
50150696a6eSStefan Eßer 
50244d4804dSStefan Eßer 	// If there is a free spot...
50378bc019dSStefan Eßer 	if (ctxt->free_nums.len)
50478bc019dSStefan Eßer 	{
505175a4d10SStefan Eßer 		BclNum* ptr;
50650696a6eSStefan Eßer 
50744d4804dSStefan Eßer 		// Get the index of the free spot and remove it.
50850696a6eSStefan Eßer 		idx = *((BclNumber*) bc_vec_top(&ctxt->free_nums));
50950696a6eSStefan Eßer 		bc_vec_pop(&ctxt->free_nums);
51050696a6eSStefan Eßer 
51144d4804dSStefan Eßer 		// Copy the number into the spot.
51250696a6eSStefan Eßer 		ptr = bc_vec_item(&ctxt->nums, idx.i);
513175a4d10SStefan Eßer 
514175a4d10SStefan Eßer 		memcpy(BCL_NUM_NUM(ptr), n, sizeof(BcNum));
515175a4d10SStefan Eßer 
516175a4d10SStefan Eßer #if BC_ENABLE_MEMCHECK
517175a4d10SStefan Eßer 
518175a4d10SStefan Eßer 		ptr->gen_idx += 1;
519175a4d10SStefan Eßer 
520175a4d10SStefan Eßer 		if (ptr->gen_idx == UCHAR_MAX)
521175a4d10SStefan Eßer 		{
522175a4d10SStefan Eßer 			ptr->gen_idx = 0;
523175a4d10SStefan Eßer 		}
524175a4d10SStefan Eßer 
525175a4d10SStefan Eßer 		idx.i |= (ptr->gen_idx << ((sizeof(size_t) - 1) * CHAR_BIT));
526175a4d10SStefan Eßer 
527175a4d10SStefan Eßer #endif // BC_ENABLE_MEMCHECK
52850696a6eSStefan Eßer 	}
52978bc019dSStefan Eßer 	else
53078bc019dSStefan Eßer 	{
531175a4d10SStefan Eßer #if BC_ENABLE_MEMCHECK
532175a4d10SStefan Eßer 		n->gen_idx = 0;
533175a4d10SStefan Eßer #endif // BC_ENABLE_MEMCHECK
534175a4d10SStefan Eßer 
535175a4d10SStefan Eßer 		// Just push the number onto the vector because the generation index is
536175a4d10SStefan Eßer 		// 0.
53750696a6eSStefan Eßer 		idx.i = ctxt->nums.len;
53850696a6eSStefan Eßer 		bc_vec_push(&ctxt->nums, n);
53950696a6eSStefan Eßer 	}
54050696a6eSStefan Eßer 
54150696a6eSStefan Eßer 	return idx;
54250696a6eSStefan Eßer }
54350696a6eSStefan Eßer 
54478bc019dSStefan Eßer BclNumber
54578bc019dSStefan Eßer bcl_num_create(void)
54678bc019dSStefan Eßer {
54750696a6eSStefan Eßer 	BclError e = BCL_ERROR_NONE;
548175a4d10SStefan Eßer 	BclNum n;
54950696a6eSStefan Eßer 	BclNumber idx;
55050696a6eSStefan Eßer 	BclContext ctxt;
551d101cdd6SStefan Eßer 	BcVm* vm = bcl_getspecific();
55250696a6eSStefan Eßer 
553d101cdd6SStefan Eßer 	BC_CHECK_CTXT(vm, ctxt);
55450696a6eSStefan Eßer 
555d101cdd6SStefan Eßer 	BC_FUNC_HEADER(vm, err);
55650696a6eSStefan Eßer 
557175a4d10SStefan Eßer 	BCL_GROW_NUMS(ctxt);
55850696a6eSStefan Eßer 
559175a4d10SStefan Eßer 	bc_num_init(BCL_NUM_NUM_NP(n), BC_NUM_DEF_SIZE);
56050696a6eSStefan Eßer 
56150696a6eSStefan Eßer err:
562175a4d10SStefan Eßer 
563d101cdd6SStefan Eßer 	BC_FUNC_FOOTER(vm, e);
56450696a6eSStefan Eßer 	BC_MAYBE_SETUP(ctxt, e, n, idx);
56550696a6eSStefan Eßer 
56650696a6eSStefan Eßer 	return idx;
56750696a6eSStefan Eßer }
56850696a6eSStefan Eßer 
56944d4804dSStefan Eßer /**
57044d4804dSStefan Eßer  * Destructs a number and marks its spot as free.
57144d4804dSStefan Eßer  * @param ctxt  The context.
57244d4804dSStefan Eßer  * @param n     The index of the number.
57344d4804dSStefan Eßer  * @param num   The number to destroy.
57444d4804dSStefan Eßer  */
57578bc019dSStefan Eßer static void
576175a4d10SStefan Eßer bcl_num_dtor(BclContext ctxt, BclNumber n, BclNum* restrict num)
57778bc019dSStefan Eßer {
578175a4d10SStefan Eßer 	assert(num != NULL && BCL_NUM_ARRAY(num) != NULL);
579175a4d10SStefan Eßer 
580175a4d10SStefan Eßer 	BCL_CLEAR_GEN(n);
58150696a6eSStefan Eßer 
58250696a6eSStefan Eßer 	bcl_num_destruct(num);
58350696a6eSStefan Eßer 	bc_vec_push(&ctxt->free_nums, &n);
584175a4d10SStefan Eßer 
585175a4d10SStefan Eßer #if BC_ENABLE_MEMCHECK
586175a4d10SStefan Eßer 	num->n.num = NULL;
587175a4d10SStefan Eßer #endif // BC_ENABLE_MEMCHECK
58850696a6eSStefan Eßer }
58950696a6eSStefan Eßer 
59078bc019dSStefan Eßer void
59178bc019dSStefan Eßer bcl_num_free(BclNumber n)
59278bc019dSStefan Eßer {
593175a4d10SStefan Eßer 	BclNum* num;
59450696a6eSStefan Eßer 	BclContext ctxt;
595d101cdd6SStefan Eßer 	BcVm* vm = bcl_getspecific();
59650696a6eSStefan Eßer 
597d101cdd6SStefan Eßer 	BC_CHECK_CTXT_ASSERT(vm, ctxt);
59850696a6eSStefan Eßer 
599175a4d10SStefan Eßer 	BCL_CHECK_NUM_VALID(ctxt, n);
60050696a6eSStefan Eßer 
601175a4d10SStefan Eßer 	assert(BCL_NO_GEN(n) < ctxt->nums.len);
602175a4d10SStefan Eßer 
603175a4d10SStefan Eßer 	num = BCL_NUM(ctxt, n);
60450696a6eSStefan Eßer 
60550696a6eSStefan Eßer 	bcl_num_dtor(ctxt, n, num);
60650696a6eSStefan Eßer }
60750696a6eSStefan Eßer 
60878bc019dSStefan Eßer BclError
60978bc019dSStefan Eßer bcl_copy(BclNumber d, BclNumber s)
61078bc019dSStefan Eßer {
61150696a6eSStefan Eßer 	BclError e = BCL_ERROR_NONE;
612175a4d10SStefan Eßer 	BclNum* dest;
613175a4d10SStefan Eßer 	BclNum* src;
61450696a6eSStefan Eßer 	BclContext ctxt;
615d101cdd6SStefan Eßer 	BcVm* vm = bcl_getspecific();
61650696a6eSStefan Eßer 
617d101cdd6SStefan Eßer 	BC_CHECK_CTXT_ERR(vm, ctxt);
61850696a6eSStefan Eßer 
619175a4d10SStefan Eßer 	BCL_CHECK_NUM_VALID(ctxt, d);
620175a4d10SStefan Eßer 	BCL_CHECK_NUM_VALID(ctxt, s);
621175a4d10SStefan Eßer 
622d101cdd6SStefan Eßer 	BC_FUNC_HEADER(vm, err);
62350696a6eSStefan Eßer 
624175a4d10SStefan Eßer 	assert(BCL_NO_GEN(d) < ctxt->nums.len);
625175a4d10SStefan Eßer 	assert(BCL_NO_GEN(s) < ctxt->nums.len);
62650696a6eSStefan Eßer 
627175a4d10SStefan Eßer 	dest = BCL_NUM(ctxt, d);
628175a4d10SStefan Eßer 	src = BCL_NUM(ctxt, s);
62950696a6eSStefan Eßer 
63050696a6eSStefan Eßer 	assert(dest != NULL && src != NULL);
631175a4d10SStefan Eßer 	assert(BCL_NUM_ARRAY(dest) != NULL && BCL_NUM_ARRAY(src) != NULL);
63250696a6eSStefan Eßer 
633175a4d10SStefan Eßer 	bc_num_copy(BCL_NUM_NUM(dest), BCL_NUM_NUM(src));
63450696a6eSStefan Eßer 
63550696a6eSStefan Eßer err:
636175a4d10SStefan Eßer 
637d101cdd6SStefan Eßer 	BC_FUNC_FOOTER(vm, e);
63850696a6eSStefan Eßer 
63950696a6eSStefan Eßer 	return e;
64050696a6eSStefan Eßer }
64150696a6eSStefan Eßer 
64278bc019dSStefan Eßer BclNumber
64378bc019dSStefan Eßer bcl_dup(BclNumber s)
64478bc019dSStefan Eßer {
64550696a6eSStefan Eßer 	BclError e = BCL_ERROR_NONE;
646175a4d10SStefan Eßer 	BclNum *src, dest;
64750696a6eSStefan Eßer 	BclNumber idx;
64850696a6eSStefan Eßer 	BclContext ctxt;
649d101cdd6SStefan Eßer 	BcVm* vm = bcl_getspecific();
65050696a6eSStefan Eßer 
651d101cdd6SStefan Eßer 	BC_CHECK_CTXT(vm, ctxt);
65250696a6eSStefan Eßer 
653175a4d10SStefan Eßer 	BCL_CHECK_NUM_VALID(ctxt, s);
654175a4d10SStefan Eßer 
655d101cdd6SStefan Eßer 	BC_FUNC_HEADER(vm, err);
65650696a6eSStefan Eßer 
657175a4d10SStefan Eßer 	BCL_GROW_NUMS(ctxt);
65850696a6eSStefan Eßer 
659175a4d10SStefan Eßer 	assert(BCL_NO_GEN(s) < ctxt->nums.len);
66050696a6eSStefan Eßer 
661175a4d10SStefan Eßer 	src = BCL_NUM(ctxt, s);
66250696a6eSStefan Eßer 
663175a4d10SStefan Eßer 	assert(src != NULL && BCL_NUM_NUM(src) != NULL);
66450696a6eSStefan Eßer 
66544d4804dSStefan Eßer 	// Copy the number.
666175a4d10SStefan Eßer 	bc_num_clear(BCL_NUM_NUM(&dest));
667175a4d10SStefan Eßer 	bc_num_createCopy(BCL_NUM_NUM(&dest), BCL_NUM_NUM(src));
66850696a6eSStefan Eßer 
66950696a6eSStefan Eßer err:
670175a4d10SStefan Eßer 
671d101cdd6SStefan Eßer 	BC_FUNC_FOOTER(vm, e);
67250696a6eSStefan Eßer 	BC_MAYBE_SETUP(ctxt, e, dest, idx);
67350696a6eSStefan Eßer 
67450696a6eSStefan Eßer 	return idx;
67550696a6eSStefan Eßer }
67650696a6eSStefan Eßer 
67778bc019dSStefan Eßer void
67878bc019dSStefan Eßer bcl_num_destruct(void* num)
67978bc019dSStefan Eßer {
680175a4d10SStefan Eßer 	BclNum* n = (BclNum*) num;
68150696a6eSStefan Eßer 
68250696a6eSStefan Eßer 	assert(n != NULL);
68350696a6eSStefan Eßer 
684175a4d10SStefan Eßer 	if (BCL_NUM_ARRAY(n) == NULL) return;
68550696a6eSStefan Eßer 
686175a4d10SStefan Eßer 	bc_num_free(BCL_NUM_NUM(n));
687175a4d10SStefan Eßer 	bc_num_clear(BCL_NUM_NUM(n));
68850696a6eSStefan Eßer }
68950696a6eSStefan Eßer 
69078bc019dSStefan Eßer bool
69178bc019dSStefan Eßer bcl_num_neg(BclNumber n)
69278bc019dSStefan Eßer {
693175a4d10SStefan Eßer 	BclNum* num;
69450696a6eSStefan Eßer 	BclContext ctxt;
695d101cdd6SStefan Eßer 	BcVm* vm = bcl_getspecific();
69650696a6eSStefan Eßer 
697d101cdd6SStefan Eßer 	BC_CHECK_CTXT_ASSERT(vm, ctxt);
69850696a6eSStefan Eßer 
699175a4d10SStefan Eßer 	BCL_CHECK_NUM_VALID(ctxt, n);
70050696a6eSStefan Eßer 
701175a4d10SStefan Eßer 	assert(BCL_NO_GEN(n) < ctxt->nums.len);
70250696a6eSStefan Eßer 
703175a4d10SStefan Eßer 	num = BCL_NUM(ctxt, n);
70450696a6eSStefan Eßer 
705175a4d10SStefan Eßer 	assert(num != NULL && BCL_NUM_ARRAY(num) != NULL);
706175a4d10SStefan Eßer 
707175a4d10SStefan Eßer 	return BC_NUM_NEG(BCL_NUM_NUM(num)) != 0;
70850696a6eSStefan Eßer }
70950696a6eSStefan Eßer 
71078bc019dSStefan Eßer void
71178bc019dSStefan Eßer bcl_num_setNeg(BclNumber n, bool neg)
71278bc019dSStefan Eßer {
713175a4d10SStefan Eßer 	BclNum* num;
71450696a6eSStefan Eßer 	BclContext ctxt;
715d101cdd6SStefan Eßer 	BcVm* vm = bcl_getspecific();
71650696a6eSStefan Eßer 
717d101cdd6SStefan Eßer 	BC_CHECK_CTXT_ASSERT(vm, ctxt);
71850696a6eSStefan Eßer 
719175a4d10SStefan Eßer 	BCL_CHECK_NUM_VALID(ctxt, n);
72050696a6eSStefan Eßer 
721175a4d10SStefan Eßer 	assert(BCL_NO_GEN(n) < ctxt->nums.len);
72250696a6eSStefan Eßer 
723175a4d10SStefan Eßer 	num = BCL_NUM(ctxt, n);
72450696a6eSStefan Eßer 
725175a4d10SStefan Eßer 	assert(num != NULL && BCL_NUM_ARRAY(num) != NULL);
726175a4d10SStefan Eßer 
727175a4d10SStefan Eßer 	BCL_NUM_NUM(num)->rdx = BC_NUM_NEG_VAL(BCL_NUM_NUM(num), neg);
72850696a6eSStefan Eßer }
72950696a6eSStefan Eßer 
73078bc019dSStefan Eßer size_t
73178bc019dSStefan Eßer bcl_num_scale(BclNumber n)
73278bc019dSStefan Eßer {
733175a4d10SStefan Eßer 	BclNum* num;
73450696a6eSStefan Eßer 	BclContext ctxt;
735d101cdd6SStefan Eßer 	BcVm* vm = bcl_getspecific();
73650696a6eSStefan Eßer 
737d101cdd6SStefan Eßer 	BC_CHECK_CTXT_ASSERT(vm, ctxt);
73850696a6eSStefan Eßer 
739175a4d10SStefan Eßer 	BCL_CHECK_NUM_VALID(ctxt, n);
74050696a6eSStefan Eßer 
741175a4d10SStefan Eßer 	assert(BCL_NO_GEN(n) < ctxt->nums.len);
74250696a6eSStefan Eßer 
743175a4d10SStefan Eßer 	num = BCL_NUM(ctxt, n);
74450696a6eSStefan Eßer 
745175a4d10SStefan Eßer 	assert(num != NULL && BCL_NUM_ARRAY(num) != NULL);
746175a4d10SStefan Eßer 
747175a4d10SStefan Eßer 	return bc_num_scale(BCL_NUM_NUM(num));
74850696a6eSStefan Eßer }
74950696a6eSStefan Eßer 
75078bc019dSStefan Eßer BclError
75178bc019dSStefan Eßer bcl_num_setScale(BclNumber n, size_t scale)
75278bc019dSStefan Eßer {
75350696a6eSStefan Eßer 	BclError e = BCL_ERROR_NONE;
754175a4d10SStefan Eßer 	BclNum* nptr;
75550696a6eSStefan Eßer 	BclContext ctxt;
756d101cdd6SStefan Eßer 	BcVm* vm = bcl_getspecific();
75750696a6eSStefan Eßer 
758d101cdd6SStefan Eßer 	BC_CHECK_CTXT_ERR(vm, ctxt);
75950696a6eSStefan Eßer 
76050696a6eSStefan Eßer 	BC_CHECK_NUM_ERR(ctxt, n);
76150696a6eSStefan Eßer 
762175a4d10SStefan Eßer 	BCL_CHECK_NUM_VALID(ctxt, n);
763175a4d10SStefan Eßer 
764d101cdd6SStefan Eßer 	BC_FUNC_HEADER(vm, err);
76550696a6eSStefan Eßer 
766175a4d10SStefan Eßer 	assert(BCL_NO_GEN(n) < ctxt->nums.len);
76750696a6eSStefan Eßer 
768175a4d10SStefan Eßer 	nptr = BCL_NUM(ctxt, n);
76950696a6eSStefan Eßer 
770175a4d10SStefan Eßer 	assert(nptr != NULL && BCL_NUM_ARRAY(nptr) != NULL);
77150696a6eSStefan Eßer 
772175a4d10SStefan Eßer 	if (scale > BCL_NUM_NUM(nptr)->scale)
773175a4d10SStefan Eßer 	{
774175a4d10SStefan Eßer 		bc_num_extend(BCL_NUM_NUM(nptr), scale - BCL_NUM_NUM(nptr)->scale);
775175a4d10SStefan Eßer 	}
776175a4d10SStefan Eßer 	else if (scale < BCL_NUM_NUM(nptr)->scale)
777175a4d10SStefan Eßer 	{
778175a4d10SStefan Eßer 		bc_num_truncate(BCL_NUM_NUM(nptr), BCL_NUM_NUM(nptr)->scale - scale);
779175a4d10SStefan Eßer 	}
78050696a6eSStefan Eßer 
78150696a6eSStefan Eßer err:
782175a4d10SStefan Eßer 
783d101cdd6SStefan Eßer 	BC_FUNC_FOOTER(vm, e);
78450696a6eSStefan Eßer 
78550696a6eSStefan Eßer 	return e;
78650696a6eSStefan Eßer }
78750696a6eSStefan Eßer 
78878bc019dSStefan Eßer size_t
78978bc019dSStefan Eßer bcl_num_len(BclNumber n)
79078bc019dSStefan Eßer {
791175a4d10SStefan Eßer 	BclNum* num;
79250696a6eSStefan Eßer 	BclContext ctxt;
793d101cdd6SStefan Eßer 	BcVm* vm = bcl_getspecific();
79450696a6eSStefan Eßer 
795d101cdd6SStefan Eßer 	BC_CHECK_CTXT_ASSERT(vm, ctxt);
79650696a6eSStefan Eßer 
797175a4d10SStefan Eßer 	BCL_CHECK_NUM_VALID(ctxt, n);
79850696a6eSStefan Eßer 
799175a4d10SStefan Eßer 	assert(BCL_NO_GEN(n) < ctxt->nums.len);
80050696a6eSStefan Eßer 
801175a4d10SStefan Eßer 	num = BCL_NUM(ctxt, n);
80250696a6eSStefan Eßer 
803175a4d10SStefan Eßer 	assert(num != NULL && BCL_NUM_ARRAY(num) != NULL);
804175a4d10SStefan Eßer 
805175a4d10SStefan Eßer 	return bc_num_len(BCL_NUM_NUM(num));
80650696a6eSStefan Eßer }
80750696a6eSStefan Eßer 
808175a4d10SStefan Eßer static BclError
809175a4d10SStefan Eßer bcl_bigdig_helper(BclNumber n, BclBigDig* result, bool destruct)
81078bc019dSStefan Eßer {
81150696a6eSStefan Eßer 	BclError e = BCL_ERROR_NONE;
812175a4d10SStefan Eßer 	BclNum* num;
81350696a6eSStefan Eßer 	BclContext ctxt;
814d101cdd6SStefan Eßer 	BcVm* vm = bcl_getspecific();
81550696a6eSStefan Eßer 
816d101cdd6SStefan Eßer 	BC_CHECK_CTXT_ERR(vm, ctxt);
81750696a6eSStefan Eßer 
818175a4d10SStefan Eßer 	BCL_CHECK_NUM_VALID(ctxt, n);
819175a4d10SStefan Eßer 
820d101cdd6SStefan Eßer 	BC_FUNC_HEADER(vm, err);
82150696a6eSStefan Eßer 
822175a4d10SStefan Eßer 	assert(BCL_NO_GEN(n) < ctxt->nums.len);
82350696a6eSStefan Eßer 	assert(result != NULL);
82450696a6eSStefan Eßer 
825175a4d10SStefan Eßer 	num = BCL_NUM(ctxt, n);
82650696a6eSStefan Eßer 
827175a4d10SStefan Eßer 	assert(num != NULL && BCL_NUM_ARRAY(num) != NULL);
82850696a6eSStefan Eßer 
829175a4d10SStefan Eßer 	*result = bc_num_bigdig(BCL_NUM_NUM(num));
83050696a6eSStefan Eßer 
83150696a6eSStefan Eßer err:
832175a4d10SStefan Eßer 
833175a4d10SStefan Eßer 	if (destruct)
834175a4d10SStefan Eßer 	{
83550696a6eSStefan Eßer 		bcl_num_dtor(ctxt, n, num);
836175a4d10SStefan Eßer 	}
837175a4d10SStefan Eßer 
838d101cdd6SStefan Eßer 	BC_FUNC_FOOTER(vm, e);
83950696a6eSStefan Eßer 
84050696a6eSStefan Eßer 	return e;
84150696a6eSStefan Eßer }
84250696a6eSStefan Eßer 
843175a4d10SStefan Eßer BclError
844175a4d10SStefan Eßer bcl_bigdig(BclNumber n, BclBigDig* result)
845175a4d10SStefan Eßer {
846175a4d10SStefan Eßer 	return bcl_bigdig_helper(n, result, true);
847175a4d10SStefan Eßer }
848175a4d10SStefan Eßer 
849175a4d10SStefan Eßer BclError
850175a4d10SStefan Eßer bcl_bigdig_keep(BclNumber n, BclBigDig* result)
851175a4d10SStefan Eßer {
852175a4d10SStefan Eßer 	return bcl_bigdig_helper(n, result, false);
853175a4d10SStefan Eßer }
854175a4d10SStefan Eßer 
85578bc019dSStefan Eßer BclNumber
85678bc019dSStefan Eßer bcl_bigdig2num(BclBigDig val)
85778bc019dSStefan Eßer {
85850696a6eSStefan Eßer 	BclError e = BCL_ERROR_NONE;
859175a4d10SStefan Eßer 	BclNum n;
86050696a6eSStefan Eßer 	BclNumber idx;
86150696a6eSStefan Eßer 	BclContext ctxt;
862d101cdd6SStefan Eßer 	BcVm* vm = bcl_getspecific();
86350696a6eSStefan Eßer 
864d101cdd6SStefan Eßer 	BC_CHECK_CTXT(vm, ctxt);
86550696a6eSStefan Eßer 
866d101cdd6SStefan Eßer 	BC_FUNC_HEADER(vm, err);
86750696a6eSStefan Eßer 
868175a4d10SStefan Eßer 	BCL_GROW_NUMS(ctxt);
86950696a6eSStefan Eßer 
870175a4d10SStefan Eßer 	bc_num_createFromBigdig(BCL_NUM_NUM_NP(n), val);
87150696a6eSStefan Eßer 
87250696a6eSStefan Eßer err:
873175a4d10SStefan Eßer 
874d101cdd6SStefan Eßer 	BC_FUNC_FOOTER(vm, e);
87550696a6eSStefan Eßer 	BC_MAYBE_SETUP(ctxt, e, n, idx);
87650696a6eSStefan Eßer 
87750696a6eSStefan Eßer 	return idx;
87850696a6eSStefan Eßer }
87950696a6eSStefan Eßer 
88044d4804dSStefan Eßer /**
88144d4804dSStefan Eßer  * Sets up and executes a binary operator operation.
88244d4804dSStefan Eßer  * @param a         The first operand.
88344d4804dSStefan Eßer  * @param b         The second operand.
88444d4804dSStefan Eßer  * @param op        The operation.
885175a4d10SStefan Eßer  * @param req       The function to get the size of the result for
886175a4d10SStefan Eßer  *                  preallocation.
887175a4d10SStefan Eßer  * @param destruct  True if the parameters should be consumed, false otherwise.
88844d4804dSStefan Eßer  * @return          The result of the operation.
88944d4804dSStefan Eßer  */
89078bc019dSStefan Eßer static BclNumber
89178bc019dSStefan Eßer bcl_binary(BclNumber a, BclNumber b, const BcNumBinaryOp op,
892175a4d10SStefan Eßer            const BcNumBinaryOpReq req, bool destruct)
89350696a6eSStefan Eßer {
89450696a6eSStefan Eßer 	BclError e = BCL_ERROR_NONE;
895175a4d10SStefan Eßer 	BclNum* aptr;
896175a4d10SStefan Eßer 	BclNum* bptr;
897175a4d10SStefan Eßer 	BclNum c;
89850696a6eSStefan Eßer 	BclNumber idx;
89950696a6eSStefan Eßer 	BclContext ctxt;
900d101cdd6SStefan Eßer 	BcVm* vm = bcl_getspecific();
90150696a6eSStefan Eßer 
902d101cdd6SStefan Eßer 	BC_CHECK_CTXT(vm, ctxt);
90350696a6eSStefan Eßer 
90450696a6eSStefan Eßer 	BC_CHECK_NUM(ctxt, a);
90550696a6eSStefan Eßer 	BC_CHECK_NUM(ctxt, b);
90650696a6eSStefan Eßer 
907d101cdd6SStefan Eßer 	BC_FUNC_HEADER(vm, err);
90850696a6eSStefan Eßer 
909175a4d10SStefan Eßer 	BCL_GROW_NUMS(ctxt);
91050696a6eSStefan Eßer 
911175a4d10SStefan Eßer 	assert(BCL_NO_GEN(a) < ctxt->nums.len && BCL_NO_GEN(b) < ctxt->nums.len);
91250696a6eSStefan Eßer 
913175a4d10SStefan Eßer 	aptr = BCL_NUM(ctxt, a);
914175a4d10SStefan Eßer 	bptr = BCL_NUM(ctxt, b);
91550696a6eSStefan Eßer 
91650696a6eSStefan Eßer 	assert(aptr != NULL && bptr != NULL);
917175a4d10SStefan Eßer 	assert(BCL_NUM_ARRAY(aptr) != NULL && BCL_NUM_ARRAY(bptr) != NULL);
91850696a6eSStefan Eßer 
91944d4804dSStefan Eßer 	// Clear and initialize the result.
920175a4d10SStefan Eßer 	bc_num_clear(BCL_NUM_NUM_NP(c));
921175a4d10SStefan Eßer 	bc_num_init(BCL_NUM_NUM_NP(c),
922175a4d10SStefan Eßer 	            req(BCL_NUM_NUM(aptr), BCL_NUM_NUM(bptr), ctxt->scale));
92350696a6eSStefan Eßer 
924175a4d10SStefan Eßer 	op(BCL_NUM_NUM(aptr), BCL_NUM_NUM(bptr), BCL_NUM_NUM_NP(c), ctxt->scale);
92550696a6eSStefan Eßer 
92650696a6eSStefan Eßer err:
92744d4804dSStefan Eßer 
928175a4d10SStefan Eßer 	if (destruct)
929175a4d10SStefan Eßer 	{
93044d4804dSStefan Eßer 		// Eat the operands.
93150696a6eSStefan Eßer 		bcl_num_dtor(ctxt, a, aptr);
93250696a6eSStefan Eßer 		if (b.i != a.i) bcl_num_dtor(ctxt, b, bptr);
933175a4d10SStefan Eßer 	}
93444d4804dSStefan Eßer 
935d101cdd6SStefan Eßer 	BC_FUNC_FOOTER(vm, e);
93650696a6eSStefan Eßer 	BC_MAYBE_SETUP(ctxt, e, c, idx);
93750696a6eSStefan Eßer 
93850696a6eSStefan Eßer 	return idx;
93950696a6eSStefan Eßer }
94050696a6eSStefan Eßer 
94178bc019dSStefan Eßer BclNumber
94278bc019dSStefan Eßer bcl_add(BclNumber a, BclNumber b)
94378bc019dSStefan Eßer {
944175a4d10SStefan Eßer 	return bcl_binary(a, b, bc_num_add, bc_num_addReq, true);
945175a4d10SStefan Eßer }
946175a4d10SStefan Eßer 
947175a4d10SStefan Eßer BclNumber
948175a4d10SStefan Eßer bcl_add_keep(BclNumber a, BclNumber b)
949175a4d10SStefan Eßer {
950175a4d10SStefan Eßer 	return bcl_binary(a, b, bc_num_add, bc_num_addReq, false);
95150696a6eSStefan Eßer }
95250696a6eSStefan Eßer 
95378bc019dSStefan Eßer BclNumber
95478bc019dSStefan Eßer bcl_sub(BclNumber a, BclNumber b)
95578bc019dSStefan Eßer {
956175a4d10SStefan Eßer 	return bcl_binary(a, b, bc_num_sub, bc_num_addReq, true);
957175a4d10SStefan Eßer }
958175a4d10SStefan Eßer 
959175a4d10SStefan Eßer BclNumber
960175a4d10SStefan Eßer bcl_sub_keep(BclNumber a, BclNumber b)
961175a4d10SStefan Eßer {
962175a4d10SStefan Eßer 	return bcl_binary(a, b, bc_num_sub, bc_num_addReq, false);
96350696a6eSStefan Eßer }
96450696a6eSStefan Eßer 
96578bc019dSStefan Eßer BclNumber
96678bc019dSStefan Eßer bcl_mul(BclNumber a, BclNumber b)
96778bc019dSStefan Eßer {
968175a4d10SStefan Eßer 	return bcl_binary(a, b, bc_num_mul, bc_num_mulReq, true);
969175a4d10SStefan Eßer }
970175a4d10SStefan Eßer 
971175a4d10SStefan Eßer BclNumber
972175a4d10SStefan Eßer bcl_mul_keep(BclNumber a, BclNumber b)
973175a4d10SStefan Eßer {
974175a4d10SStefan Eßer 	return bcl_binary(a, b, bc_num_mul, bc_num_mulReq, false);
97550696a6eSStefan Eßer }
97650696a6eSStefan Eßer 
97778bc019dSStefan Eßer BclNumber
97878bc019dSStefan Eßer bcl_div(BclNumber a, BclNumber b)
97978bc019dSStefan Eßer {
980175a4d10SStefan Eßer 	return bcl_binary(a, b, bc_num_div, bc_num_divReq, true);
981175a4d10SStefan Eßer }
982175a4d10SStefan Eßer 
983175a4d10SStefan Eßer BclNumber
984175a4d10SStefan Eßer bcl_div_keep(BclNumber a, BclNumber b)
985175a4d10SStefan Eßer {
986175a4d10SStefan Eßer 	return bcl_binary(a, b, bc_num_div, bc_num_divReq, false);
98750696a6eSStefan Eßer }
98850696a6eSStefan Eßer 
98978bc019dSStefan Eßer BclNumber
99078bc019dSStefan Eßer bcl_mod(BclNumber a, BclNumber b)
99178bc019dSStefan Eßer {
992175a4d10SStefan Eßer 	return bcl_binary(a, b, bc_num_mod, bc_num_divReq, true);
993175a4d10SStefan Eßer }
994175a4d10SStefan Eßer 
995175a4d10SStefan Eßer BclNumber
996175a4d10SStefan Eßer bcl_mod_keep(BclNumber a, BclNumber b)
997175a4d10SStefan Eßer {
998175a4d10SStefan Eßer 	return bcl_binary(a, b, bc_num_mod, bc_num_divReq, false);
99950696a6eSStefan Eßer }
100050696a6eSStefan Eßer 
100178bc019dSStefan Eßer BclNumber
100278bc019dSStefan Eßer bcl_pow(BclNumber a, BclNumber b)
100378bc019dSStefan Eßer {
1004175a4d10SStefan Eßer 	return bcl_binary(a, b, bc_num_pow, bc_num_powReq, true);
1005175a4d10SStefan Eßer }
1006175a4d10SStefan Eßer 
1007175a4d10SStefan Eßer BclNumber
1008175a4d10SStefan Eßer bcl_pow_keep(BclNumber a, BclNumber b)
1009175a4d10SStefan Eßer {
1010175a4d10SStefan Eßer 	return bcl_binary(a, b, bc_num_pow, bc_num_powReq, false);
101150696a6eSStefan Eßer }
101250696a6eSStefan Eßer 
101378bc019dSStefan Eßer BclNumber
101478bc019dSStefan Eßer bcl_lshift(BclNumber a, BclNumber b)
101578bc019dSStefan Eßer {
1016175a4d10SStefan Eßer 	return bcl_binary(a, b, bc_num_lshift, bc_num_placesReq, true);
1017175a4d10SStefan Eßer }
1018175a4d10SStefan Eßer 
1019175a4d10SStefan Eßer BclNumber
1020175a4d10SStefan Eßer bcl_lshift_keep(BclNumber a, BclNumber b)
1021175a4d10SStefan Eßer {
1022175a4d10SStefan Eßer 	return bcl_binary(a, b, bc_num_lshift, bc_num_placesReq, false);
102350696a6eSStefan Eßer }
102450696a6eSStefan Eßer 
102578bc019dSStefan Eßer BclNumber
102678bc019dSStefan Eßer bcl_rshift(BclNumber a, BclNumber b)
102778bc019dSStefan Eßer {
1028175a4d10SStefan Eßer 	return bcl_binary(a, b, bc_num_rshift, bc_num_placesReq, true);
102950696a6eSStefan Eßer }
103050696a6eSStefan Eßer 
103178bc019dSStefan Eßer BclNumber
1032175a4d10SStefan Eßer bcl_rshift_keep(BclNumber a, BclNumber b)
1033175a4d10SStefan Eßer {
1034175a4d10SStefan Eßer 	return bcl_binary(a, b, bc_num_rshift, bc_num_placesReq, false);
1035175a4d10SStefan Eßer }
1036175a4d10SStefan Eßer 
1037175a4d10SStefan Eßer static BclNumber
1038175a4d10SStefan Eßer bcl_sqrt_helper(BclNumber a, bool destruct)
103978bc019dSStefan Eßer {
104050696a6eSStefan Eßer 	BclError e = BCL_ERROR_NONE;
1041175a4d10SStefan Eßer 	BclNum* aptr;
1042175a4d10SStefan Eßer 	BclNum b;
104350696a6eSStefan Eßer 	BclNumber idx;
104450696a6eSStefan Eßer 	BclContext ctxt;
1045d101cdd6SStefan Eßer 	BcVm* vm = bcl_getspecific();
104650696a6eSStefan Eßer 
1047d101cdd6SStefan Eßer 	BC_CHECK_CTXT(vm, ctxt);
104850696a6eSStefan Eßer 
104950696a6eSStefan Eßer 	BC_CHECK_NUM(ctxt, a);
105050696a6eSStefan Eßer 
1051d101cdd6SStefan Eßer 	BC_FUNC_HEADER(vm, err);
105250696a6eSStefan Eßer 
1053175a4d10SStefan Eßer 	BCL_GROW_NUMS(ctxt);
105450696a6eSStefan Eßer 
1055175a4d10SStefan Eßer 	assert(BCL_NO_GEN(a) < ctxt->nums.len);
105650696a6eSStefan Eßer 
1057175a4d10SStefan Eßer 	aptr = BCL_NUM(ctxt, a);
105850696a6eSStefan Eßer 
1059175a4d10SStefan Eßer 	bc_num_sqrt(BCL_NUM_NUM(aptr), BCL_NUM_NUM_NP(b), ctxt->scale);
106050696a6eSStefan Eßer 
106150696a6eSStefan Eßer err:
1062175a4d10SStefan Eßer 
1063175a4d10SStefan Eßer 	if (destruct)
1064175a4d10SStefan Eßer 	{
106550696a6eSStefan Eßer 		bcl_num_dtor(ctxt, a, aptr);
1066175a4d10SStefan Eßer 	}
1067175a4d10SStefan Eßer 
1068d101cdd6SStefan Eßer 	BC_FUNC_FOOTER(vm, e);
106950696a6eSStefan Eßer 	BC_MAYBE_SETUP(ctxt, e, b, idx);
107050696a6eSStefan Eßer 
107150696a6eSStefan Eßer 	return idx;
107250696a6eSStefan Eßer }
107350696a6eSStefan Eßer 
1074175a4d10SStefan Eßer BclNumber
1075175a4d10SStefan Eßer bcl_sqrt(BclNumber a)
1076175a4d10SStefan Eßer {
1077175a4d10SStefan Eßer 	return bcl_sqrt_helper(a, true);
1078175a4d10SStefan Eßer }
1079175a4d10SStefan Eßer 
1080175a4d10SStefan Eßer BclNumber
1081175a4d10SStefan Eßer bcl_sqrt_keep(BclNumber a)
1082175a4d10SStefan Eßer {
1083175a4d10SStefan Eßer 	return bcl_sqrt_helper(a, false);
1084175a4d10SStefan Eßer }
1085175a4d10SStefan Eßer 
1086175a4d10SStefan Eßer static BclError
1087175a4d10SStefan Eßer bcl_divmod_helper(BclNumber a, BclNumber b, BclNumber* c, BclNumber* d,
1088175a4d10SStefan Eßer                   bool destruct)
108978bc019dSStefan Eßer {
109050696a6eSStefan Eßer 	BclError e = BCL_ERROR_NONE;
109150696a6eSStefan Eßer 	size_t req;
1092175a4d10SStefan Eßer 	BclNum* aptr;
1093175a4d10SStefan Eßer 	BclNum* bptr;
1094175a4d10SStefan Eßer 	BclNum cnum, dnum;
109550696a6eSStefan Eßer 	BclContext ctxt;
1096d101cdd6SStefan Eßer 	BcVm* vm = bcl_getspecific();
109750696a6eSStefan Eßer 
1098d101cdd6SStefan Eßer 	BC_CHECK_CTXT_ERR(vm, ctxt);
109950696a6eSStefan Eßer 
110050696a6eSStefan Eßer 	BC_CHECK_NUM_ERR(ctxt, a);
110150696a6eSStefan Eßer 	BC_CHECK_NUM_ERR(ctxt, b);
110250696a6eSStefan Eßer 
1103d101cdd6SStefan Eßer 	BC_FUNC_HEADER(vm, err);
110450696a6eSStefan Eßer 
1105175a4d10SStefan Eßer 	BCL_GROW_NUMS(ctxt);
110650696a6eSStefan Eßer 
110750696a6eSStefan Eßer 	assert(c != NULL && d != NULL);
110850696a6eSStefan Eßer 
1109175a4d10SStefan Eßer 	aptr = BCL_NUM(ctxt, a);
1110175a4d10SStefan Eßer 	bptr = BCL_NUM(ctxt, b);
111150696a6eSStefan Eßer 
111250696a6eSStefan Eßer 	assert(aptr != NULL && bptr != NULL);
1113175a4d10SStefan Eßer 	assert(BCL_NUM_ARRAY(aptr) != NULL && BCL_NUM_ARRAY(bptr) != NULL);
111450696a6eSStefan Eßer 
1115175a4d10SStefan Eßer 	bc_num_clear(BCL_NUM_NUM_NP(cnum));
1116175a4d10SStefan Eßer 	bc_num_clear(BCL_NUM_NUM_NP(dnum));
111750696a6eSStefan Eßer 
1118175a4d10SStefan Eßer 	req = bc_num_divReq(BCL_NUM_NUM(aptr), BCL_NUM_NUM(bptr), ctxt->scale);
111950696a6eSStefan Eßer 
112044d4804dSStefan Eßer 	// Initialize the numbers.
1121175a4d10SStefan Eßer 	bc_num_init(BCL_NUM_NUM_NP(cnum), req);
1122d101cdd6SStefan Eßer 	BC_UNSETJMP(vm);
1123d101cdd6SStefan Eßer 	BC_SETJMP(vm, err);
1124175a4d10SStefan Eßer 	bc_num_init(BCL_NUM_NUM_NP(dnum), req);
112550696a6eSStefan Eßer 
1126175a4d10SStefan Eßer 	bc_num_divmod(BCL_NUM_NUM(aptr), BCL_NUM_NUM(bptr), BCL_NUM_NUM_NP(cnum),
1127175a4d10SStefan Eßer 	              BCL_NUM_NUM_NP(dnum), ctxt->scale);
112850696a6eSStefan Eßer 
112950696a6eSStefan Eßer err:
113050696a6eSStefan Eßer 
1131175a4d10SStefan Eßer 	if (destruct)
1132175a4d10SStefan Eßer 	{
113344d4804dSStefan Eßer 		// Eat the operands.
113450696a6eSStefan Eßer 		bcl_num_dtor(ctxt, a, aptr);
113550696a6eSStefan Eßer 		if (b.i != a.i) bcl_num_dtor(ctxt, b, bptr);
1136175a4d10SStefan Eßer 	}
113750696a6eSStefan Eßer 
113844d4804dSStefan Eßer 	// If there was an error...
1139d101cdd6SStefan Eßer 	if (BC_ERR(vm->err))
114078bc019dSStefan Eßer 	{
114144d4804dSStefan Eßer 		// Free the results.
1142175a4d10SStefan Eßer 		if (BCL_NUM_ARRAY_NP(cnum) != NULL) bc_num_free(&cnum);
1143175a4d10SStefan Eßer 		if (BCL_NUM_ARRAY_NP(cnum) != NULL) bc_num_free(&dnum);
114444d4804dSStefan Eßer 
114544d4804dSStefan Eßer 		// Make sure the return values are invalid.
114650696a6eSStefan Eßer 		c->i = 0 - (size_t) BCL_ERROR_INVALID_NUM;
114750696a6eSStefan Eßer 		d->i = c->i;
114844d4804dSStefan Eßer 
1149d101cdd6SStefan Eßer 		BC_FUNC_FOOTER(vm, e);
115050696a6eSStefan Eßer 	}
115178bc019dSStefan Eßer 	else
115278bc019dSStefan Eßer 	{
1153d101cdd6SStefan Eßer 		BC_FUNC_FOOTER(vm, e);
115444d4804dSStefan Eßer 
115544d4804dSStefan Eßer 		// Insert the results into the context.
115650696a6eSStefan Eßer 		*c = bcl_num_insert(ctxt, &cnum);
115750696a6eSStefan Eßer 		*d = bcl_num_insert(ctxt, &dnum);
115850696a6eSStefan Eßer 	}
115950696a6eSStefan Eßer 
116050696a6eSStefan Eßer 	return e;
116150696a6eSStefan Eßer }
116250696a6eSStefan Eßer 
1163175a4d10SStefan Eßer BclError
1164175a4d10SStefan Eßer bcl_divmod(BclNumber a, BclNumber b, BclNumber* c, BclNumber* d)
1165175a4d10SStefan Eßer {
1166175a4d10SStefan Eßer 	return bcl_divmod_helper(a, b, c, d, true);
1167175a4d10SStefan Eßer }
1168175a4d10SStefan Eßer 
1169175a4d10SStefan Eßer BclError
1170175a4d10SStefan Eßer bcl_divmod_keep(BclNumber a, BclNumber b, BclNumber* c, BclNumber* d)
1171175a4d10SStefan Eßer {
1172175a4d10SStefan Eßer 	return bcl_divmod_helper(a, b, c, d, false);
1173175a4d10SStefan Eßer }
1174175a4d10SStefan Eßer 
1175175a4d10SStefan Eßer static BclNumber
1176175a4d10SStefan Eßer bcl_modexp_helper(BclNumber a, BclNumber b, BclNumber c, bool destruct)
117778bc019dSStefan Eßer {
117850696a6eSStefan Eßer 	BclError e = BCL_ERROR_NONE;
117950696a6eSStefan Eßer 	size_t req;
1180175a4d10SStefan Eßer 	BclNum* aptr;
1181175a4d10SStefan Eßer 	BclNum* bptr;
1182175a4d10SStefan Eßer 	BclNum* cptr;
1183175a4d10SStefan Eßer 	BclNum d;
118450696a6eSStefan Eßer 	BclNumber idx;
118550696a6eSStefan Eßer 	BclContext ctxt;
1186d101cdd6SStefan Eßer 	BcVm* vm = bcl_getspecific();
118750696a6eSStefan Eßer 
1188d101cdd6SStefan Eßer 	BC_CHECK_CTXT(vm, ctxt);
118950696a6eSStefan Eßer 
119050696a6eSStefan Eßer 	BC_CHECK_NUM(ctxt, a);
119150696a6eSStefan Eßer 	BC_CHECK_NUM(ctxt, b);
119250696a6eSStefan Eßer 	BC_CHECK_NUM(ctxt, c);
119350696a6eSStefan Eßer 
1194d101cdd6SStefan Eßer 	BC_FUNC_HEADER(vm, err);
119550696a6eSStefan Eßer 
1196175a4d10SStefan Eßer 	BCL_GROW_NUMS(ctxt);
119750696a6eSStefan Eßer 
1198175a4d10SStefan Eßer 	assert(BCL_NO_GEN(a) < ctxt->nums.len && BCL_NO_GEN(b) < ctxt->nums.len);
1199175a4d10SStefan Eßer 	assert(BCL_NO_GEN(c) < ctxt->nums.len);
120050696a6eSStefan Eßer 
1201175a4d10SStefan Eßer 	aptr = BCL_NUM(ctxt, a);
1202175a4d10SStefan Eßer 	bptr = BCL_NUM(ctxt, b);
1203175a4d10SStefan Eßer 	cptr = BCL_NUM(ctxt, c);
120450696a6eSStefan Eßer 
120550696a6eSStefan Eßer 	assert(aptr != NULL && bptr != NULL && cptr != NULL);
1206175a4d10SStefan Eßer 	assert(BCL_NUM_NUM(aptr) != NULL && BCL_NUM_NUM(bptr) != NULL &&
1207175a4d10SStefan Eßer 	       BCL_NUM_NUM(cptr) != NULL);
120850696a6eSStefan Eßer 
120944d4804dSStefan Eßer 	// Prepare the result.
1210175a4d10SStefan Eßer 	bc_num_clear(BCL_NUM_NUM_NP(d));
121150696a6eSStefan Eßer 
1212175a4d10SStefan Eßer 	req = bc_num_divReq(BCL_NUM_NUM(aptr), BCL_NUM_NUM(cptr), 0);
121350696a6eSStefan Eßer 
121444d4804dSStefan Eßer 	// Initialize the result.
1215175a4d10SStefan Eßer 	bc_num_init(BCL_NUM_NUM_NP(d), req);
121650696a6eSStefan Eßer 
1217175a4d10SStefan Eßer 	bc_num_modexp(BCL_NUM_NUM(aptr), BCL_NUM_NUM(bptr), BCL_NUM_NUM(cptr),
1218175a4d10SStefan Eßer 	              BCL_NUM_NUM_NP(d));
121950696a6eSStefan Eßer 
122050696a6eSStefan Eßer err:
122150696a6eSStefan Eßer 
1222175a4d10SStefan Eßer 	if (destruct)
1223175a4d10SStefan Eßer 	{
122444d4804dSStefan Eßer 		// Eat the operands.
122550696a6eSStefan Eßer 		bcl_num_dtor(ctxt, a, aptr);
122650696a6eSStefan Eßer 		if (b.i != a.i) bcl_num_dtor(ctxt, b, bptr);
122750696a6eSStefan Eßer 		if (c.i != a.i && c.i != b.i) bcl_num_dtor(ctxt, c, cptr);
1228175a4d10SStefan Eßer 	}
122950696a6eSStefan Eßer 
1230d101cdd6SStefan Eßer 	BC_FUNC_FOOTER(vm, e);
123150696a6eSStefan Eßer 	BC_MAYBE_SETUP(ctxt, e, d, idx);
123250696a6eSStefan Eßer 
123350696a6eSStefan Eßer 	return idx;
123450696a6eSStefan Eßer }
123550696a6eSStefan Eßer 
1236175a4d10SStefan Eßer BclNumber
1237175a4d10SStefan Eßer bcl_modexp(BclNumber a, BclNumber b, BclNumber c)
1238175a4d10SStefan Eßer {
1239175a4d10SStefan Eßer 	return bcl_modexp_helper(a, b, c, true);
1240175a4d10SStefan Eßer }
1241175a4d10SStefan Eßer 
1242175a4d10SStefan Eßer BclNumber
1243175a4d10SStefan Eßer bcl_modexp_keep(BclNumber a, BclNumber b, BclNumber c)
1244175a4d10SStefan Eßer {
1245175a4d10SStefan Eßer 	return bcl_modexp_helper(a, b, c, false);
1246175a4d10SStefan Eßer }
1247175a4d10SStefan Eßer 
124878bc019dSStefan Eßer ssize_t
124978bc019dSStefan Eßer bcl_cmp(BclNumber a, BclNumber b)
125078bc019dSStefan Eßer {
1251175a4d10SStefan Eßer 	BclNum* aptr;
1252175a4d10SStefan Eßer 	BclNum* bptr;
125350696a6eSStefan Eßer 	BclContext ctxt;
1254d101cdd6SStefan Eßer 	BcVm* vm = bcl_getspecific();
125550696a6eSStefan Eßer 
1256d101cdd6SStefan Eßer 	BC_CHECK_CTXT_ASSERT(vm, ctxt);
125750696a6eSStefan Eßer 
1258175a4d10SStefan Eßer 	BCL_CHECK_NUM_VALID(ctxt, a);
1259175a4d10SStefan Eßer 	BCL_CHECK_NUM_VALID(ctxt, b);
126050696a6eSStefan Eßer 
1261175a4d10SStefan Eßer 	assert(BCL_NO_GEN(a) < ctxt->nums.len && BCL_NO_GEN(b) < ctxt->nums.len);
1262175a4d10SStefan Eßer 
1263175a4d10SStefan Eßer 	aptr = BCL_NUM(ctxt, a);
1264175a4d10SStefan Eßer 	bptr = BCL_NUM(ctxt, b);
126550696a6eSStefan Eßer 
126650696a6eSStefan Eßer 	assert(aptr != NULL && bptr != NULL);
1267175a4d10SStefan Eßer 	assert(BCL_NUM_NUM(aptr) != NULL && BCL_NUM_NUM(bptr));
126850696a6eSStefan Eßer 
1269175a4d10SStefan Eßer 	return bc_num_cmp(BCL_NUM_NUM(aptr), BCL_NUM_NUM(bptr));
127050696a6eSStefan Eßer }
127150696a6eSStefan Eßer 
127278bc019dSStefan Eßer void
127378bc019dSStefan Eßer bcl_zero(BclNumber n)
127478bc019dSStefan Eßer {
1275175a4d10SStefan Eßer 	BclNum* nptr;
127650696a6eSStefan Eßer 	BclContext ctxt;
1277d101cdd6SStefan Eßer 	BcVm* vm = bcl_getspecific();
127850696a6eSStefan Eßer 
1279d101cdd6SStefan Eßer 	BC_CHECK_CTXT_ASSERT(vm, ctxt);
128050696a6eSStefan Eßer 
1281175a4d10SStefan Eßer 	BCL_CHECK_NUM_VALID(ctxt, n);
128250696a6eSStefan Eßer 
1283175a4d10SStefan Eßer 	assert(BCL_NO_GEN(n) < ctxt->nums.len);
128450696a6eSStefan Eßer 
1285175a4d10SStefan Eßer 	nptr = BCL_NUM(ctxt, n);
128650696a6eSStefan Eßer 
1287175a4d10SStefan Eßer 	assert(nptr != NULL && BCL_NUM_NUM(nptr) != NULL);
1288175a4d10SStefan Eßer 
1289175a4d10SStefan Eßer 	bc_num_zero(BCL_NUM_NUM(nptr));
129050696a6eSStefan Eßer }
129150696a6eSStefan Eßer 
129278bc019dSStefan Eßer void
129378bc019dSStefan Eßer bcl_one(BclNumber n)
129478bc019dSStefan Eßer {
1295175a4d10SStefan Eßer 	BclNum* nptr;
129650696a6eSStefan Eßer 	BclContext ctxt;
1297d101cdd6SStefan Eßer 	BcVm* vm = bcl_getspecific();
129850696a6eSStefan Eßer 
1299d101cdd6SStefan Eßer 	BC_CHECK_CTXT_ASSERT(vm, ctxt);
130050696a6eSStefan Eßer 
1301175a4d10SStefan Eßer 	BCL_CHECK_NUM_VALID(ctxt, n);
130250696a6eSStefan Eßer 
1303175a4d10SStefan Eßer 	assert(BCL_NO_GEN(n) < ctxt->nums.len);
130450696a6eSStefan Eßer 
1305175a4d10SStefan Eßer 	nptr = BCL_NUM(ctxt, n);
130650696a6eSStefan Eßer 
1307175a4d10SStefan Eßer 	assert(nptr != NULL && BCL_NUM_NUM(nptr) != NULL);
1308175a4d10SStefan Eßer 
1309175a4d10SStefan Eßer 	bc_num_one(BCL_NUM_NUM(nptr));
131050696a6eSStefan Eßer }
131150696a6eSStefan Eßer 
131278bc019dSStefan Eßer BclNumber
131378bc019dSStefan Eßer bcl_parse(const char* restrict val)
131478bc019dSStefan Eßer {
131550696a6eSStefan Eßer 	BclError e = BCL_ERROR_NONE;
1316175a4d10SStefan Eßer 	BclNum n;
131750696a6eSStefan Eßer 	BclNumber idx;
131850696a6eSStefan Eßer 	BclContext ctxt;
1319d101cdd6SStefan Eßer 	BcVm* vm = bcl_getspecific();
132050696a6eSStefan Eßer 	bool neg;
132150696a6eSStefan Eßer 
1322d101cdd6SStefan Eßer 	BC_CHECK_CTXT(vm, ctxt);
132350696a6eSStefan Eßer 
1324d101cdd6SStefan Eßer 	BC_FUNC_HEADER(vm, err);
132550696a6eSStefan Eßer 
1326175a4d10SStefan Eßer 	BCL_GROW_NUMS(ctxt);
132750696a6eSStefan Eßer 
132850696a6eSStefan Eßer 	assert(val != NULL);
132950696a6eSStefan Eßer 
133044d4804dSStefan Eßer 	// We have to take care of negative here because bc's number parsing does
133144d4804dSStefan Eßer 	// not.
133250696a6eSStefan Eßer 	neg = (val[0] == '-');
133350696a6eSStefan Eßer 
133450696a6eSStefan Eßer 	if (neg) val += 1;
133550696a6eSStefan Eßer 
133678bc019dSStefan Eßer 	if (!bc_num_strValid(val))
133778bc019dSStefan Eßer 	{
1338d101cdd6SStefan Eßer 		vm->err = BCL_ERROR_PARSE_INVALID_STR;
133950696a6eSStefan Eßer 		goto err;
134050696a6eSStefan Eßer 	}
134150696a6eSStefan Eßer 
134244d4804dSStefan Eßer 	// Clear and initialize the number.
1343175a4d10SStefan Eßer 	bc_num_clear(BCL_NUM_NUM_NP(n));
1344175a4d10SStefan Eßer 	bc_num_init(BCL_NUM_NUM_NP(n), BC_NUM_DEF_SIZE);
134550696a6eSStefan Eßer 
1346175a4d10SStefan Eßer 	bc_num_parse(BCL_NUM_NUM_NP(n), val, (BcBigDig) ctxt->ibase);
134750696a6eSStefan Eßer 
134844d4804dSStefan Eßer 	// Set the negative.
1349175a4d10SStefan Eßer #if BC_ENABLE_MEMCHECK
1350175a4d10SStefan Eßer 	n.n.rdx = BC_NUM_NEG_VAL(BCL_NUM_NUM_NP(n), neg);
1351175a4d10SStefan Eßer #else // BC_ENABLE_MEMCHECK
135250696a6eSStefan Eßer 	n.rdx = BC_NUM_NEG_VAL_NP(n, neg);
1353175a4d10SStefan Eßer #endif // BC_ENABLE_MEMCHECK
135450696a6eSStefan Eßer 
135550696a6eSStefan Eßer err:
1356175a4d10SStefan Eßer 
1357d101cdd6SStefan Eßer 	BC_FUNC_FOOTER(vm, e);
135850696a6eSStefan Eßer 	BC_MAYBE_SETUP(ctxt, e, n, idx);
135950696a6eSStefan Eßer 
136050696a6eSStefan Eßer 	return idx;
136150696a6eSStefan Eßer }
136250696a6eSStefan Eßer 
1363175a4d10SStefan Eßer static char*
1364175a4d10SStefan Eßer bcl_string_helper(BclNumber n, bool destruct)
136578bc019dSStefan Eßer {
1366175a4d10SStefan Eßer 	BclNum* nptr;
136750696a6eSStefan Eßer 	char* str = NULL;
136850696a6eSStefan Eßer 	BclContext ctxt;
1369d101cdd6SStefan Eßer 	BcVm* vm = bcl_getspecific();
137050696a6eSStefan Eßer 
1371d101cdd6SStefan Eßer 	BC_CHECK_CTXT_ASSERT(vm, ctxt);
137250696a6eSStefan Eßer 
1373175a4d10SStefan Eßer 	BCL_CHECK_NUM_VALID(ctxt, n);
1374175a4d10SStefan Eßer 
1375175a4d10SStefan Eßer 	if (BC_ERR(BCL_NO_GEN(n) >= ctxt->nums.len)) return str;
137650696a6eSStefan Eßer 
1377d101cdd6SStefan Eßer 	BC_FUNC_HEADER(vm, err);
137850696a6eSStefan Eßer 
1379175a4d10SStefan Eßer 	assert(BCL_NO_GEN(n) < ctxt->nums.len);
138050696a6eSStefan Eßer 
1381175a4d10SStefan Eßer 	nptr = BCL_NUM(ctxt, n);
138250696a6eSStefan Eßer 
1383175a4d10SStefan Eßer 	assert(nptr != NULL && BCL_NUM_NUM(nptr) != NULL);
138450696a6eSStefan Eßer 
138544d4804dSStefan Eßer 	// Clear the buffer.
1386d101cdd6SStefan Eßer 	bc_vec_popAll(&vm->out);
138750696a6eSStefan Eßer 
138844d4804dSStefan Eßer 	// Print to the buffer.
1389175a4d10SStefan Eßer 	bc_num_print(BCL_NUM_NUM(nptr), (BcBigDig) ctxt->obase, false);
1390d101cdd6SStefan Eßer 	bc_vec_pushByte(&vm->out, '\0');
139144d4804dSStefan Eßer 
139244d4804dSStefan Eßer 	// Just dup the string; the caller is responsible for it.
1393d101cdd6SStefan Eßer 	str = bc_vm_strdup(vm->out.v);
139450696a6eSStefan Eßer 
139550696a6eSStefan Eßer err:
139644d4804dSStefan Eßer 
1397175a4d10SStefan Eßer 	if (destruct)
1398175a4d10SStefan Eßer 	{
139944d4804dSStefan Eßer 		// Eat the operand.
140050696a6eSStefan Eßer 		bcl_num_dtor(ctxt, n, nptr);
1401175a4d10SStefan Eßer 	}
140250696a6eSStefan Eßer 
1403d101cdd6SStefan Eßer 	BC_FUNC_FOOTER_NO_ERR(vm);
140450696a6eSStefan Eßer 
140550696a6eSStefan Eßer 	return str;
140650696a6eSStefan Eßer }
140750696a6eSStefan Eßer 
1408175a4d10SStefan Eßer char*
1409175a4d10SStefan Eßer bcl_string(BclNumber n)
1410175a4d10SStefan Eßer {
1411175a4d10SStefan Eßer 	return bcl_string_helper(n, true);
1412175a4d10SStefan Eßer }
1413175a4d10SStefan Eßer 
1414175a4d10SStefan Eßer char*
1415175a4d10SStefan Eßer bcl_string_keep(BclNumber n)
1416175a4d10SStefan Eßer {
1417175a4d10SStefan Eßer 	return bcl_string_helper(n, false);
1418175a4d10SStefan Eßer }
1419175a4d10SStefan Eßer 
1420175a4d10SStefan Eßer #if BC_ENABLE_EXTRA_MATH
1421175a4d10SStefan Eßer 
1422175a4d10SStefan Eßer static BclNumber
1423175a4d10SStefan Eßer bcl_irand_helper(BclNumber a, bool destruct)
142478bc019dSStefan Eßer {
142550696a6eSStefan Eßer 	BclError e = BCL_ERROR_NONE;
1426175a4d10SStefan Eßer 	BclNum* aptr;
1427175a4d10SStefan Eßer 	BclNum b;
142850696a6eSStefan Eßer 	BclNumber idx;
142950696a6eSStefan Eßer 	BclContext ctxt;
1430d101cdd6SStefan Eßer 	BcVm* vm = bcl_getspecific();
143150696a6eSStefan Eßer 
1432d101cdd6SStefan Eßer 	BC_CHECK_CTXT(vm, ctxt);
143350696a6eSStefan Eßer 
143450696a6eSStefan Eßer 	BC_CHECK_NUM(ctxt, a);
143550696a6eSStefan Eßer 
1436d101cdd6SStefan Eßer 	BC_FUNC_HEADER(vm, err);
143750696a6eSStefan Eßer 
1438175a4d10SStefan Eßer 	BCL_GROW_NUMS(ctxt);
143950696a6eSStefan Eßer 
1440175a4d10SStefan Eßer 	assert(BCL_NO_GEN(a) < ctxt->nums.len);
144150696a6eSStefan Eßer 
1442175a4d10SStefan Eßer 	aptr = BCL_NUM(ctxt, a);
144350696a6eSStefan Eßer 
1444175a4d10SStefan Eßer 	assert(aptr != NULL && BCL_NUM_NUM(aptr) != NULL);
144550696a6eSStefan Eßer 
144644d4804dSStefan Eßer 	// Clear and initialize the result.
1447175a4d10SStefan Eßer 	bc_num_clear(BCL_NUM_NUM_NP(b));
1448175a4d10SStefan Eßer 	bc_num_init(BCL_NUM_NUM_NP(b), BC_NUM_DEF_SIZE);
144950696a6eSStefan Eßer 
1450175a4d10SStefan Eßer 	bc_num_irand(BCL_NUM_NUM(aptr), BCL_NUM_NUM_NP(b), &vm->rng);
145150696a6eSStefan Eßer 
145250696a6eSStefan Eßer err:
145344d4804dSStefan Eßer 
1454175a4d10SStefan Eßer 	if (destruct)
1455175a4d10SStefan Eßer 	{
145644d4804dSStefan Eßer 		// Eat the operand.
145750696a6eSStefan Eßer 		bcl_num_dtor(ctxt, a, aptr);
1458175a4d10SStefan Eßer 	}
145944d4804dSStefan Eßer 
1460d101cdd6SStefan Eßer 	BC_FUNC_FOOTER(vm, e);
146150696a6eSStefan Eßer 	BC_MAYBE_SETUP(ctxt, e, b, idx);
146250696a6eSStefan Eßer 
146350696a6eSStefan Eßer 	return idx;
146450696a6eSStefan Eßer }
146550696a6eSStefan Eßer 
1466175a4d10SStefan Eßer BclNumber
1467175a4d10SStefan Eßer bcl_irand(BclNumber a)
1468175a4d10SStefan Eßer {
1469175a4d10SStefan Eßer 	return bcl_irand_helper(a, true);
1470175a4d10SStefan Eßer }
1471175a4d10SStefan Eßer 
1472175a4d10SStefan Eßer BclNumber
1473175a4d10SStefan Eßer bcl_irand_keep(BclNumber a)
1474175a4d10SStefan Eßer {
1475175a4d10SStefan Eßer 	return bcl_irand_helper(a, false);
1476175a4d10SStefan Eßer }
1477175a4d10SStefan Eßer 
147844d4804dSStefan Eßer /**
147944d4804dSStefan Eßer  * Helps bcl_frand(). This is separate because the error handling is easier that
148044d4804dSStefan Eßer  * way. It is also easier to do ifrand that way.
148144d4804dSStefan Eßer  * @param b       The return parameter.
148244d4804dSStefan Eßer  * @param places  The number of decimal places to generate.
148344d4804dSStefan Eßer  */
148478bc019dSStefan Eßer static void
148578bc019dSStefan Eßer bcl_frandHelper(BcNum* restrict b, size_t places)
148678bc019dSStefan Eßer {
148750696a6eSStefan Eßer 	BcNum exp, pow, ten;
148850696a6eSStefan Eßer 	BcDig exp_digs[BC_NUM_BIGDIG_LOG10];
148950696a6eSStefan Eßer 	BcDig ten_digs[BC_NUM_BIGDIG_LOG10];
1490d101cdd6SStefan Eßer 	BcVm* vm = bcl_getspecific();
149150696a6eSStefan Eßer 
149244d4804dSStefan Eßer 	// Set up temporaries.
149350696a6eSStefan Eßer 	bc_num_setup(&exp, exp_digs, BC_NUM_BIGDIG_LOG10);
149450696a6eSStefan Eßer 	bc_num_setup(&ten, ten_digs, BC_NUM_BIGDIG_LOG10);
149550696a6eSStefan Eßer 
149650696a6eSStefan Eßer 	ten.num[0] = 10;
149750696a6eSStefan Eßer 	ten.len = 1;
149850696a6eSStefan Eßer 
149950696a6eSStefan Eßer 	bc_num_bigdig2num(&exp, (BcBigDig) places);
150050696a6eSStefan Eßer 
150144d4804dSStefan Eßer 	// Clear the temporary that might need to grow.
150250696a6eSStefan Eßer 	bc_num_clear(&pow);
150350696a6eSStefan Eßer 
150444d4804dSStefan Eßer 	// Initialize the temporary that might need to grow.
150550696a6eSStefan Eßer 	bc_num_init(&pow, bc_num_powReq(&ten, &exp, 0));
150650696a6eSStefan Eßer 
1507d101cdd6SStefan Eßer 	BC_SETJMP(vm, err);
150850696a6eSStefan Eßer 
150944d4804dSStefan Eßer 	// Generate the number.
151050696a6eSStefan Eßer 	bc_num_pow(&ten, &exp, &pow, 0);
1511d101cdd6SStefan Eßer 	bc_num_irand(&pow, b, &vm->rng);
151250696a6eSStefan Eßer 
151344d4804dSStefan Eßer 	// Make the number entirely fraction.
151450696a6eSStefan Eßer 	bc_num_shiftRight(b, places);
151550696a6eSStefan Eßer 
151650696a6eSStefan Eßer err:
1517175a4d10SStefan Eßer 
151850696a6eSStefan Eßer 	bc_num_free(&pow);
1519d101cdd6SStefan Eßer 	BC_LONGJMP_CONT(vm);
152050696a6eSStefan Eßer }
152150696a6eSStefan Eßer 
152278bc019dSStefan Eßer BclNumber
152378bc019dSStefan Eßer bcl_frand(size_t places)
152478bc019dSStefan Eßer {
152550696a6eSStefan Eßer 	BclError e = BCL_ERROR_NONE;
1526175a4d10SStefan Eßer 	BclNum n;
152750696a6eSStefan Eßer 	BclNumber idx;
152850696a6eSStefan Eßer 	BclContext ctxt;
1529d101cdd6SStefan Eßer 	BcVm* vm = bcl_getspecific();
153050696a6eSStefan Eßer 
1531d101cdd6SStefan Eßer 	BC_CHECK_CTXT(vm, ctxt);
153250696a6eSStefan Eßer 
1533d101cdd6SStefan Eßer 	BC_FUNC_HEADER(vm, err);
153450696a6eSStefan Eßer 
1535175a4d10SStefan Eßer 	BCL_GROW_NUMS(ctxt);
153650696a6eSStefan Eßer 
153744d4804dSStefan Eßer 	// Clear and initialize the number.
1538175a4d10SStefan Eßer 	bc_num_clear(BCL_NUM_NUM_NP(n));
1539175a4d10SStefan Eßer 	bc_num_init(BCL_NUM_NUM_NP(n), BC_NUM_DEF_SIZE);
154050696a6eSStefan Eßer 
1541175a4d10SStefan Eßer 	bcl_frandHelper(BCL_NUM_NUM_NP(n), places);
154250696a6eSStefan Eßer 
154350696a6eSStefan Eßer err:
154444d4804dSStefan Eßer 
1545d101cdd6SStefan Eßer 	BC_FUNC_FOOTER(vm, e);
154650696a6eSStefan Eßer 	BC_MAYBE_SETUP(ctxt, e, n, idx);
154750696a6eSStefan Eßer 
154850696a6eSStefan Eßer 	return idx;
154950696a6eSStefan Eßer }
155050696a6eSStefan Eßer 
155144d4804dSStefan Eßer /**
155244d4804dSStefan Eßer  * Helps bc_ifrand(). This is separate because error handling is easier that
155344d4804dSStefan Eßer  * way.
155444d4804dSStefan Eßer  * @param a       The limit for bc_num_irand().
155544d4804dSStefan Eßer  * @param b       The return parameter.
155644d4804dSStefan Eßer  * @param places  The number of decimal places to generate.
155744d4804dSStefan Eßer  */
155878bc019dSStefan Eßer static void
155978bc019dSStefan Eßer bcl_ifrandHelper(BcNum* restrict a, BcNum* restrict b, size_t places)
156050696a6eSStefan Eßer {
156150696a6eSStefan Eßer 	BcNum ir, fr;
1562d101cdd6SStefan Eßer 	BcVm* vm = bcl_getspecific();
156350696a6eSStefan Eßer 
156444d4804dSStefan Eßer 	// Clear the integer and fractional numbers.
156550696a6eSStefan Eßer 	bc_num_clear(&ir);
156650696a6eSStefan Eßer 	bc_num_clear(&fr);
156750696a6eSStefan Eßer 
156844d4804dSStefan Eßer 	// Initialize the integer and fractional numbers.
156950696a6eSStefan Eßer 	bc_num_init(&ir, BC_NUM_DEF_SIZE);
157050696a6eSStefan Eßer 	bc_num_init(&fr, BC_NUM_DEF_SIZE);
157150696a6eSStefan Eßer 
1572d101cdd6SStefan Eßer 	BC_SETJMP(vm, err);
157344d4804dSStefan Eßer 
1574d101cdd6SStefan Eßer 	bc_num_irand(a, &ir, &vm->rng);
157550696a6eSStefan Eßer 	bcl_frandHelper(&fr, places);
157650696a6eSStefan Eßer 
157750696a6eSStefan Eßer 	bc_num_add(&ir, &fr, b, 0);
157850696a6eSStefan Eßer 
157950696a6eSStefan Eßer err:
1580175a4d10SStefan Eßer 
158150696a6eSStefan Eßer 	bc_num_free(&fr);
158250696a6eSStefan Eßer 	bc_num_free(&ir);
1583d101cdd6SStefan Eßer 	BC_LONGJMP_CONT(vm);
158450696a6eSStefan Eßer }
158550696a6eSStefan Eßer 
1586175a4d10SStefan Eßer static BclNumber
1587175a4d10SStefan Eßer bcl_ifrand_helper(BclNumber a, size_t places, bool destruct)
158878bc019dSStefan Eßer {
158950696a6eSStefan Eßer 	BclError e = BCL_ERROR_NONE;
1590175a4d10SStefan Eßer 	BclNum* aptr;
1591175a4d10SStefan Eßer 	BclNum b;
159250696a6eSStefan Eßer 	BclNumber idx;
159350696a6eSStefan Eßer 	BclContext ctxt;
1594d101cdd6SStefan Eßer 	BcVm* vm = bcl_getspecific();
159550696a6eSStefan Eßer 
1596d101cdd6SStefan Eßer 	BC_CHECK_CTXT(vm, ctxt);
159750696a6eSStefan Eßer 	BC_CHECK_NUM(ctxt, a);
159850696a6eSStefan Eßer 
1599d101cdd6SStefan Eßer 	BC_FUNC_HEADER(vm, err);
160050696a6eSStefan Eßer 
1601175a4d10SStefan Eßer 	BCL_GROW_NUMS(ctxt);
160250696a6eSStefan Eßer 
1603175a4d10SStefan Eßer 	assert(BCL_NO_GEN(a) < ctxt->nums.len);
160450696a6eSStefan Eßer 
1605175a4d10SStefan Eßer 	aptr = BCL_NUM(ctxt, a);
160650696a6eSStefan Eßer 
1607175a4d10SStefan Eßer 	assert(aptr != NULL && BCL_NUM_NUM(aptr) != NULL);
160850696a6eSStefan Eßer 
160944d4804dSStefan Eßer 	// Clear and initialize the number.
1610175a4d10SStefan Eßer 	bc_num_clear(BCL_NUM_NUM_NP(b));
1611175a4d10SStefan Eßer 	bc_num_init(BCL_NUM_NUM_NP(b), BC_NUM_DEF_SIZE);
161250696a6eSStefan Eßer 
1613175a4d10SStefan Eßer 	bcl_ifrandHelper(BCL_NUM_NUM(aptr), BCL_NUM_NUM_NP(b), places);
161450696a6eSStefan Eßer 
161550696a6eSStefan Eßer err:
161644d4804dSStefan Eßer 
1617175a4d10SStefan Eßer 	if (destruct)
1618175a4d10SStefan Eßer 	{
161944d4804dSStefan Eßer 		// Eat the oprand.
162050696a6eSStefan Eßer 		bcl_num_dtor(ctxt, a, aptr);
1621175a4d10SStefan Eßer 	}
162244d4804dSStefan Eßer 
1623d101cdd6SStefan Eßer 	BC_FUNC_FOOTER(vm, e);
162450696a6eSStefan Eßer 	BC_MAYBE_SETUP(ctxt, e, b, idx);
162550696a6eSStefan Eßer 
162650696a6eSStefan Eßer 	return idx;
162750696a6eSStefan Eßer }
162850696a6eSStefan Eßer 
1629175a4d10SStefan Eßer BclNumber
1630175a4d10SStefan Eßer bcl_ifrand(BclNumber a, size_t places)
1631175a4d10SStefan Eßer {
1632175a4d10SStefan Eßer 	return bcl_ifrand_helper(a, places, true);
1633175a4d10SStefan Eßer }
1634175a4d10SStefan Eßer 
1635175a4d10SStefan Eßer BclNumber
1636175a4d10SStefan Eßer bcl_ifrand_keep(BclNumber a, size_t places)
1637175a4d10SStefan Eßer {
1638175a4d10SStefan Eßer 	return bcl_ifrand_helper(a, places, false);
1639175a4d10SStefan Eßer }
1640175a4d10SStefan Eßer 
1641175a4d10SStefan Eßer static BclError
1642175a4d10SStefan Eßer bcl_rand_seedWithNum_helper(BclNumber n, bool destruct)
164378bc019dSStefan Eßer {
164450696a6eSStefan Eßer 	BclError e = BCL_ERROR_NONE;
1645175a4d10SStefan Eßer 	BclNum* nptr;
164650696a6eSStefan Eßer 	BclContext ctxt;
1647d101cdd6SStefan Eßer 	BcVm* vm = bcl_getspecific();
164850696a6eSStefan Eßer 
1649d101cdd6SStefan Eßer 	BC_CHECK_CTXT_ERR(vm, ctxt);
165050696a6eSStefan Eßer 	BC_CHECK_NUM_ERR(ctxt, n);
165150696a6eSStefan Eßer 
1652d101cdd6SStefan Eßer 	BC_FUNC_HEADER(vm, err);
165350696a6eSStefan Eßer 
1654175a4d10SStefan Eßer 	assert(BCL_NO_GEN(n) < ctxt->nums.len);
165550696a6eSStefan Eßer 
1656175a4d10SStefan Eßer 	nptr = BCL_NUM(ctxt, n);
165750696a6eSStefan Eßer 
1658175a4d10SStefan Eßer 	assert(nptr != NULL && BCL_NUM_NUM(nptr) != NULL);
165950696a6eSStefan Eßer 
1660175a4d10SStefan Eßer 	bc_num_rng(BCL_NUM_NUM(nptr), &vm->rng);
166150696a6eSStefan Eßer 
166250696a6eSStefan Eßer err:
1663175a4d10SStefan Eßer 
1664175a4d10SStefan Eßer 	if (destruct)
1665175a4d10SStefan Eßer 	{
1666175a4d10SStefan Eßer 		// Eat the oprand.
1667175a4d10SStefan Eßer 		bcl_num_dtor(ctxt, n, nptr);
1668175a4d10SStefan Eßer 	}
1669175a4d10SStefan Eßer 
1670d101cdd6SStefan Eßer 	BC_FUNC_FOOTER(vm, e);
1671175a4d10SStefan Eßer 
167250696a6eSStefan Eßer 	return e;
167350696a6eSStefan Eßer }
167450696a6eSStefan Eßer 
167578bc019dSStefan Eßer BclError
1676175a4d10SStefan Eßer bcl_rand_seedWithNum(BclNumber n)
1677175a4d10SStefan Eßer {
1678175a4d10SStefan Eßer 	return bcl_rand_seedWithNum_helper(n, true);
1679175a4d10SStefan Eßer }
1680175a4d10SStefan Eßer 
1681175a4d10SStefan Eßer BclError
1682175a4d10SStefan Eßer bcl_rand_seedWithNum_keep(BclNumber n)
1683175a4d10SStefan Eßer {
1684175a4d10SStefan Eßer 	return bcl_rand_seedWithNum_helper(n, false);
1685175a4d10SStefan Eßer }
1686175a4d10SStefan Eßer 
1687175a4d10SStefan Eßer BclError
168878bc019dSStefan Eßer bcl_rand_seed(unsigned char seed[BCL_SEED_SIZE])
168978bc019dSStefan Eßer {
169050696a6eSStefan Eßer 	BclError e = BCL_ERROR_NONE;
169150696a6eSStefan Eßer 	size_t i;
169244d4804dSStefan Eßer 	ulong vals[BCL_SEED_ULONGS];
1693d101cdd6SStefan Eßer 	BcVm* vm = bcl_getspecific();
169450696a6eSStefan Eßer 
1695d101cdd6SStefan Eßer 	BC_FUNC_HEADER(vm, err);
169650696a6eSStefan Eßer 
169744d4804dSStefan Eßer 	// Fill the array.
169878bc019dSStefan Eßer 	for (i = 0; i < BCL_SEED_SIZE; ++i)
169978bc019dSStefan Eßer 	{
170078bc019dSStefan Eßer 		ulong val = ((ulong) seed[i])
170178bc019dSStefan Eßer 		            << (((ulong) CHAR_BIT) * (i % sizeof(ulong)));
170250696a6eSStefan Eßer 		vals[i / sizeof(long)] |= val;
170350696a6eSStefan Eßer 	}
170450696a6eSStefan Eßer 
1705d101cdd6SStefan Eßer 	bc_rand_seed(&vm->rng, vals[0], vals[1], vals[2], vals[3]);
170650696a6eSStefan Eßer 
170750696a6eSStefan Eßer err:
1708175a4d10SStefan Eßer 
1709d101cdd6SStefan Eßer 	BC_FUNC_FOOTER(vm, e);
1710175a4d10SStefan Eßer 
171150696a6eSStefan Eßer 	return e;
171250696a6eSStefan Eßer }
171350696a6eSStefan Eßer 
171478bc019dSStefan Eßer void
171578bc019dSStefan Eßer bcl_rand_reseed(void)
171678bc019dSStefan Eßer {
1717d101cdd6SStefan Eßer 	BcVm* vm = bcl_getspecific();
1718d101cdd6SStefan Eßer 
1719d101cdd6SStefan Eßer 	bc_rand_srand(bc_vec_top(&vm->rng.v));
172050696a6eSStefan Eßer }
172150696a6eSStefan Eßer 
172278bc019dSStefan Eßer BclNumber
172378bc019dSStefan Eßer bcl_rand_seed2num(void)
172478bc019dSStefan Eßer {
172550696a6eSStefan Eßer 	BclError e = BCL_ERROR_NONE;
1726175a4d10SStefan Eßer 	BclNum n;
172750696a6eSStefan Eßer 	BclNumber idx;
172850696a6eSStefan Eßer 	BclContext ctxt;
1729d101cdd6SStefan Eßer 	BcVm* vm = bcl_getspecific();
173050696a6eSStefan Eßer 
1731d101cdd6SStefan Eßer 	BC_CHECK_CTXT(vm, ctxt);
173250696a6eSStefan Eßer 
1733d101cdd6SStefan Eßer 	BC_FUNC_HEADER(vm, err);
173450696a6eSStefan Eßer 
173544d4804dSStefan Eßer 	// Clear and initialize the number.
1736175a4d10SStefan Eßer 	bc_num_clear(BCL_NUM_NUM_NP(n));
1737175a4d10SStefan Eßer 	bc_num_init(BCL_NUM_NUM_NP(n), BC_NUM_DEF_SIZE);
173850696a6eSStefan Eßer 
1739175a4d10SStefan Eßer 	bc_num_createFromRNG(BCL_NUM_NUM_NP(n), &vm->rng);
174050696a6eSStefan Eßer 
174150696a6eSStefan Eßer err:
1742175a4d10SStefan Eßer 
1743d101cdd6SStefan Eßer 	BC_FUNC_FOOTER(vm, e);
174450696a6eSStefan Eßer 	BC_MAYBE_SETUP(ctxt, e, n, idx);
174550696a6eSStefan Eßer 
174650696a6eSStefan Eßer 	return idx;
174750696a6eSStefan Eßer }
174850696a6eSStefan Eßer 
174978bc019dSStefan Eßer BclRandInt
175078bc019dSStefan Eßer bcl_rand_int(void)
175178bc019dSStefan Eßer {
1752d101cdd6SStefan Eßer 	BcVm* vm = bcl_getspecific();
1753d101cdd6SStefan Eßer 
1754d101cdd6SStefan Eßer 	return (BclRandInt) bc_rand_int(&vm->rng);
175550696a6eSStefan Eßer }
175650696a6eSStefan Eßer 
175778bc019dSStefan Eßer BclRandInt
175878bc019dSStefan Eßer bcl_rand_bounded(BclRandInt bound)
175978bc019dSStefan Eßer {
1760d101cdd6SStefan Eßer 	BcVm* vm = bcl_getspecific();
1761d101cdd6SStefan Eßer 
176250696a6eSStefan Eßer 	if (bound <= 1) return 0;
1763d101cdd6SStefan Eßer 	return (BclRandInt) bc_rand_bounded(&vm->rng, (BcRand) bound);
176450696a6eSStefan Eßer }
176550696a6eSStefan Eßer 
1766175a4d10SStefan Eßer #endif // BC_ENABLE_EXTRA_MATH
1767175a4d10SStefan Eßer 
176850696a6eSStefan Eßer #endif // BC_ENABLE_LIBRARY
1769