92 lines
2.2 KiB
C
92 lines
2.2 KiB
C
#include <assert.h>
|
|
#include <inttypes.h>
|
|
#include <stddef.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include "meri_object.h"
|
|
#include "meri_bigint.h"
|
|
|
|
#include "tests.h"
|
|
|
|
// Ensures that addition of two i32 values gives the same result, regardless of
|
|
// whether we use bigints or native 64-bit arithmetic
|
|
static void test_addition(int32_t a, int32_t b) {
|
|
struct meri_bigint *a_bigint = meri_bigint_from_i32(a);
|
|
struct meri_bigint *b_bigint = meri_bigint_from_i32(b);
|
|
struct meri_bigint *result = meri_bigint_add(a_bigint, b_bigint);
|
|
|
|
assert(result->nlimbs == 1 || result->nlimbs == 2);
|
|
|
|
uint32_t low = result->limbs[0];
|
|
uint32_t high = (int32_t)low >> 31; // Default to sign extending
|
|
if (result->nlimbs > 1)
|
|
high = result->limbs[1];
|
|
|
|
int64_t result_i64 = (uint64_t)high << 32 | low;
|
|
|
|
int64_t expected = (int64_t)a + (int64_t)b;
|
|
|
|
assert(result_i64 == expected);
|
|
|
|
RELEASE(result);
|
|
}
|
|
|
|
#define TEST(...) {\
|
|
printf("%u", ++test);\
|
|
fflush(stdout);\
|
|
{ __VA_ARGS__; }\
|
|
printf(".");\
|
|
}
|
|
|
|
static int32_t interesting_ints[] = {
|
|
1,
|
|
2,
|
|
(int32_t)((1ul<<31) - 1), // The most positive i32
|
|
-1,
|
|
-2,
|
|
(int32_t)(1ul<<31), // The most negative i32
|
|
0
|
|
};
|
|
|
|
#define NELEMS(x) ((sizeof (x)) / (sizeof (x)[0]))
|
|
|
|
void test_bigint(void) {
|
|
unsigned int test = 0;
|
|
for (size_t i = 0; i < NELEMS(interesting_ints); i++) {
|
|
for (size_t j = 0; j < NELEMS(interesting_ints); j++) {
|
|
TEST(test_addition(interesting_ints[i], interesting_ints[j]));
|
|
}
|
|
}
|
|
|
|
printf("."); // Mark a new section of tests
|
|
|
|
struct meri_bigint *a, *b, *result;
|
|
|
|
// If first addend is moved, it is reused for the result
|
|
TEST(
|
|
a = meri_bigint_from_i32(2);
|
|
b = meri_bigint_from_i32(3);
|
|
result = meri_bigint_add(/* move */ a, COPY(b));
|
|
assert(result == a);
|
|
assert(result->header.refcount == 1);
|
|
assert(result != b);
|
|
assert(b->header.refcount == 1);
|
|
RELEASE(result);
|
|
)
|
|
|
|
// If second addend is moved but first is not, it is reused for the result
|
|
TEST(
|
|
a = meri_bigint_from_i32(2);
|
|
// b is still live from previous test
|
|
result = meri_bigint_add(COPY(a), /* move */ b);
|
|
assert(result != a);
|
|
assert(a->header.refcount == 1);
|
|
assert(result == b);
|
|
assert(result->header.refcount == 1);
|
|
RELEASE(a);
|
|
RELEASE(result);
|
|
)
|
|
|
|
// TODO: Test that all allocations are dead
|
|
}
|