freebsd-src/contrib/bc/tests/bcl.c
Stefan Eßer 175a4d1042 contrib/bc: update to version 6.4.0
This version contains a fix for an issue that can affect complex
bc scripts that use multiple read() functions that receive input from
an interactive user. The same value could be returned multiple times.

MFC after:	2 weeks
2023-03-10 11:33:33 +01:00

383 lines
9 KiB
C

/*
* *****************************************************************************
*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2018-2023 Gavin D. Howard and contributors.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* *****************************************************************************
*
* Tests for bcl(3).
*
*/
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <bcl.h>
/**
* Takes an error code and aborts if it actually is an error.
* @param e The error code.
*/
static void
err(BclError e)
{
if (e != BCL_ERROR_NONE) abort();
}
int
main(void)
{
BclError e;
BclContext ctxt;
size_t scale;
BclNumber n, n2, n3, n4, n5, n6, n7;
char* res;
BclBigDig b = 0;
e = bcl_start();
err(e);
// We do this twice to test the reference counting code.
e = bcl_init();
err(e);
e = bcl_init();
err(e);
// If bcl is set to abort on fatal error, that is a bug because it should
// default to off.
if (bcl_abortOnFatalError()) err(BCL_ERROR_FATAL_UNKNOWN_ERR);
bcl_setAbortOnFatalError(true);
// Now it *should* be set.
if (!bcl_abortOnFatalError()) err(BCL_ERROR_FATAL_UNKNOWN_ERR);
// We do this twice to test the context stack.
ctxt = bcl_ctxt_create();
bcl_pushContext(ctxt);
ctxt = bcl_ctxt_create();
bcl_pushContext(ctxt);
// Ensure that the scale is properly set.
scale = 10;
bcl_ctxt_setScale(ctxt, scale);
scale = bcl_ctxt_scale(ctxt);
if (scale != 10) err(BCL_ERROR_FATAL_UNKNOWN_ERR);
scale = 16;
bcl_ctxt_setIbase(ctxt, scale);
scale = bcl_ctxt_ibase(ctxt);
if (scale != 16) err(BCL_ERROR_FATAL_UNKNOWN_ERR);
// Now the obase.
bcl_ctxt_setObase(ctxt, scale);
scale = bcl_ctxt_obase(ctxt);
if (scale != 16) err(BCL_ERROR_FATAL_UNKNOWN_ERR);
// Set the back for the tests
bcl_ctxt_setIbase(ctxt, 10);
scale = bcl_ctxt_ibase(ctxt);
if (scale != 10) err(BCL_ERROR_FATAL_UNKNOWN_ERR);
bcl_ctxt_setObase(ctxt, 10);
scale = bcl_ctxt_obase(ctxt);
if (scale != 10) err(BCL_ERROR_FATAL_UNKNOWN_ERR);
// Ensure that creating, duping, and copying works.
n = bcl_num_create();
n2 = bcl_dup(n);
bcl_copy(n, n2);
// Ensure that parsing works.
n3 = bcl_parse("2938");
err(bcl_err(n3));
n4 = bcl_parse("-28390.9108273");
err(bcl_err(n4));
// We also want to be sure that negatives work. This is a special case
// because bc and dc generate a negative instruction; they don't actually
// parse numbers as negative.
if (!bcl_num_neg(n4)) err(BCL_ERROR_FATAL_UNKNOWN_ERR);
// Add them and check the result.
n5 = bcl_add_keep(n3, n4);
err(bcl_err(n5));
res = bcl_string(n5);
if (res == NULL) err(BCL_ERROR_FATAL_ALLOC_ERR);
if (strcmp(res, "-25452.9108273")) err(BCL_ERROR_FATAL_UNKNOWN_ERR);
// We want to ensure all memory gets freed because we run this under
// Valgrind.
free(res);
// Add them and check the result.
n3 = bcl_add(n3, n4);
err(bcl_err(n3));
res = bcl_string_keep(n3);
if (res == NULL) err(BCL_ERROR_FATAL_ALLOC_ERR);
if (strcmp(res, "-25452.9108273")) err(BCL_ERROR_FATAL_UNKNOWN_ERR);
// We want to ensure all memory gets freed because we run this under
// Valgrind.
free(res);
// Ensure that divmod, a special case, works.
n4 = bcl_parse("8937458902.2890347");
err(bcl_err(n4));
e = bcl_divmod_keep(n4, n3, &n5, &n6);
err(e);
res = bcl_string(n5);
if (strcmp(res, "-351137.0060159482")) err(BCL_ERROR_FATAL_UNKNOWN_ERR);
free(res);
res = bcl_string(n6);
if (strcmp(res, ".00000152374405414")) err(BCL_ERROR_FATAL_UNKNOWN_ERR);
free(res);
// Ensure that divmod, a special case, works.
n4 = bcl_parse("8937458902.2890347");
err(bcl_err(n4));
e = bcl_divmod(bcl_dup(n4), n3, &n5, &n6);
err(e);
res = bcl_string(n5);
if (strcmp(res, "-351137.0060159482")) err(BCL_ERROR_FATAL_UNKNOWN_ERR);
free(res);
res = bcl_string(n6);
if (strcmp(res, ".00000152374405414")) err(BCL_ERROR_FATAL_UNKNOWN_ERR);
free(res);
// Ensure that sqrt works. This is also a special case. The reason is
// because it is a one-argument function. Since all binary operators go
// through the same code (basically), we can test add and be done. However,
// sqrt does not, so we want to specifically test it.
n4 = bcl_sqrt(n4);
err(bcl_err(n4));
res = bcl_string(bcl_dup(n4));
if (strcmp(res, "94538.1346457028")) err(BCL_ERROR_FATAL_UNKNOWN_ERR);
free(res);
// We want to check that numbers are properly extended...
e = bcl_num_setScale(n4, 20);
err(e);
res = bcl_string(bcl_dup(n4));
if (strcmp(res, "94538.13464570280000000000"))
err(BCL_ERROR_FATAL_UNKNOWN_ERR);
free(res);
// ...and truncated.
e = bcl_num_setScale(n4, 0);
err(e);
res = bcl_string(bcl_dup(n4));
if (strcmp(res, "94538")) err(BCL_ERROR_FATAL_UNKNOWN_ERR);
free(res);
// Check conversion to hardware integers...
e = bcl_bigdig(n4, &b);
err(e);
if (b != 94538) err(BCL_ERROR_FATAL_UNKNOWN_ERR);
// ...and back.
n4 = bcl_bigdig2num(b);
err(bcl_err(n4));
res = bcl_string(bcl_dup(n4));
if (strcmp(res, "94538")) err(BCL_ERROR_FATAL_UNKNOWN_ERR);
free(res);
// Check rand.
n4 = bcl_frand(10);
err(bcl_err(n4));
// Check that no asserts fire in shifting.
n4 = bcl_lshift(n4, bcl_bigdig2num(10));
err(bcl_err(n4));
// Repeat.
n3 = bcl_irand(n4);
err(bcl_err(n3));
// Repeat.
n2 = bcl_ifrand_keep(n3, 10);
err(bcl_err(n2));
// Repeat.
n2 = bcl_ifrand(bcl_dup(n3), 10);
err(bcl_err(n2));
// Still checking asserts.
e = bcl_rand_seedWithNum_keep(n3);
err(e);
// Still checking asserts.
e = bcl_rand_seedWithNum(n3);
err(e);
// Still checking asserts.
n4 = bcl_rand_seed2num();
err(bcl_err(n4));
// Finally, check modexp, yet another special case.
n5 = bcl_parse("10");
err(bcl_err(n5));
n6 = bcl_modexp_keep(n5, n5, n5);
err(bcl_err(n6));
n7 = bcl_modexp(bcl_dup(n5), bcl_dup(n5), bcl_dup(n5));
err(bcl_err(n7));
// Clean up.
bcl_num_free(n);
// Test leading zeroes.
if (bcl_leadingZeroes()) err(BCL_ERROR_FATAL_UNKNOWN_ERR);
n = bcl_parse("0.01");
err(bcl_err(n));
n2 = bcl_parse("-0.01");
err(bcl_err(n2));
n3 = bcl_parse("1.01");
err(bcl_err(n3));
n4 = bcl_parse("-1.01");
err(bcl_err(n4));
res = bcl_string_keep(n);
if (strcmp(res, ".01")) err(BCL_ERROR_FATAL_UNKNOWN_ERR);
free(res);
res = bcl_string(bcl_dup(n));
if (strcmp(res, ".01")) err(BCL_ERROR_FATAL_UNKNOWN_ERR);
free(res);
res = bcl_string(bcl_dup(n2));
if (strcmp(res, "-.01")) err(BCL_ERROR_FATAL_UNKNOWN_ERR);
free(res);
res = bcl_string(bcl_dup(n3));
if (strcmp(res, "1.01")) err(BCL_ERROR_FATAL_UNKNOWN_ERR);
free(res);
res = bcl_string(bcl_dup(n4));
if (strcmp(res, "-1.01")) err(BCL_ERROR_FATAL_UNKNOWN_ERR);
free(res);
bcl_setLeadingZeroes(true);
if (!bcl_leadingZeroes()) err(BCL_ERROR_FATAL_UNKNOWN_ERR);
res = bcl_string(bcl_dup(n));
if (strcmp(res, "0.01")) err(BCL_ERROR_FATAL_UNKNOWN_ERR);
free(res);
res = bcl_string(bcl_dup(n2));
if (strcmp(res, "-0.01")) err(BCL_ERROR_FATAL_UNKNOWN_ERR);
free(res);
res = bcl_string(bcl_dup(n3));
if (strcmp(res, "1.01")) err(BCL_ERROR_FATAL_UNKNOWN_ERR);
free(res);
res = bcl_string(bcl_dup(n4));
if (strcmp(res, "-1.01")) err(BCL_ERROR_FATAL_UNKNOWN_ERR);
free(res);
bcl_setLeadingZeroes(false);
if (bcl_leadingZeroes()) err(BCL_ERROR_FATAL_UNKNOWN_ERR);
res = bcl_string(n);
if (strcmp(res, ".01")) err(BCL_ERROR_FATAL_UNKNOWN_ERR);
free(res);
res = bcl_string(n2);
if (strcmp(res, "-.01")) err(BCL_ERROR_FATAL_UNKNOWN_ERR);
free(res);
res = bcl_string(n3);
if (strcmp(res, "1.01")) err(BCL_ERROR_FATAL_UNKNOWN_ERR);
free(res);
res = bcl_string(n4);
if (strcmp(res, "-1.01")) err(BCL_ERROR_FATAL_UNKNOWN_ERR);
free(res);
bcl_ctxt_freeNums(ctxt);
bcl_gc();
// We need to pop both contexts and free them.
bcl_popContext();
bcl_ctxt_free(ctxt);
ctxt = bcl_context();
bcl_popContext();
bcl_ctxt_free(ctxt);
// Decrement the reference counter to ensure all is freed.
bcl_free();
bcl_free();
bcl_end();
return 0;
}