commit 1cbfd2ea0c1ff6fe0a1f9922ec2236eb4555efde Author: John Regan Date: Sun Mar 19 17:46:36 2023 -0400 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b52e0eb --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +/coverage +*.o +*.exe +/utest-byte +/utest-byte-single +/utest-byte-static +/utest-short +/utest-short-single +/utest-short-static +/utest-word +/utest-word-single +/utest-word-static +/utest-quad +/utest-quad-single +/utest-quad-static diff --git a/README.md b/README.md new file mode 100644 index 0000000..0a42e25 --- /dev/null +++ b/README.md @@ -0,0 +1,211 @@ +# bigint.h + +A single-file C library for performing big-integer arithmetic, +including functions for converting from/to strings. + +This isn't meant to be the fastest thing in the world, I did +this mostly as a learning exercise. + +## Building + +In one source file, define `BIGINT_IMPLEMENTATION`, then include +`bigint.h`. + +```c +#define BIGINT_IMPLEMENTATION +#include "bigint.h" +``` + +By default, all math operations use 32-bit, unsigned integers +with 64-bit unsigned integers for temporary additions, carries, +etc. + +If for some reason you don't have a larger type available for +multiplication, you can define `BIGINT_SINGLE_WORD_ONLY`, and +the library will fall back to cross-product multiplication. + +You can customize the width by setting `BIGINT_WORD_WIDTH` to any of: + +* `1` (uses `uint8_t`) +* `2` (uses `uint16_t)`) +* `4` (uses `uint32_t`) +* `8` (uses `uint64_t`) + +In the case of using a 64-bit word width, the library will +try to perform 128-bit multiplications if support is detected via +C macros, and fall back to cross-product multiplication otherwise. + +If you need some other type you can define `BIGINT_WORD_TYPE`. You +still need to also define the type width, in bytes. You can also +specify a double-width type, `BIGINT_DWORD_TYPE`. + +By default, all bigint objects are limited to using 4096 bytes of +memory, this can be customized per-object by setting the `limit` +field. The intention is for you to not, say, run out of memory while +parsing a never-ending string of data. You can also set the default +by defining `BIGINT_DEFAULT_LIMIT`. + +Finally, if you want to have everything be statically-allocated, you +can define `BIGINT_NO_MALLOC`. This will cause all bigints to use +a fixed-size number of bytes (set to `BIGINT_DEFAULT_LIMIT`). + +If you define any of: + +* `BIGINT_WORD_WIDTH` +* `BIGINT_WORD_TYPE` +* `BIGINT_DEFAULT_LIMIT` +* `BIGINT_NO_MALLOC` + +They will need to be defined before any `#include` of the library, +since those values will affect things like function signatures and +the structure definition. + +## Usage + +bigint objects can be allocated however you wish, then initialized +with either `bigint_init()` or using the defined `BIGINT_INIT`, ie: + +```c +bigint a = BIGINT_INIT; /* static initialization */ +bigint *b = malloc(sizeof(bigint)); +bigint_init(b); /* initialize with function */ +``` + +When no longer used, you'll need to use `bigint_free()` to free used +memory. + +By default, bigint objects are initialized to represent 0. + +You can set a starting value from various integer types: + +```c +bigint_from_u8(&b,5); +bigint_from_i32(&b,-2147483648); +``` + +Or parse a string, provide the string and the base to use (2, 8, 10, 16, or 0 for auto): + +```c +bigint_from_cstring(&b,"12345",10); +bigint_from_cstring(&b,"-12345",10); +bigint_from_cstring(&b,"0x100",16); +bigint_from_cstring(&b,"-0x100",16); +bigint_from_cstring(&b,"-0b100",2); + +/* bigint_from_cstring is a wrapper around bigint_from_string - bigint_from_string +takes a length parameter, bigint_from_cstring just uses strlen() */ +bigint_from_string(&b,"12345",5,10); +bigint_from_string(&b,"-12345",6,10); +bigint_from_string(&b,"0x100",5,16); +bigint_from_string(&b,"-0x100",6,16); +bigint_from_string(&b,"-0b100",6,2); +``` + +You can copy bigints: + +```c +bigint_copy(&dup,&b); /* dup now has the same data as b */ +``` + +You can increment/decrement a bigint: + + +Functions do not modify your input arguments. + +```c +/* equivalent to c = b + 1 */ +bigint_inc(&c, &b); + +/* equivalent to c = b - 1 */ +bigint_dec(&c, &b); + +/* equivalent to c = a + b */ +bigint_add(&c, &a, &b); + +/* equivalent to c = a - b */ +bigint_sub(&c, &a, &b); + +/* equivalent to c = a * b */ +bigint_mul(&c, &a, &b); + +/* returns the quotient and remainder of an operation at once, + similar to: + q = a / b + r = a % b +*/ +bigint_div_mod(&q, &r, &a, &b); +``` + +There's also left-shifting and right-shifting: + +```c +bigint_lshift(&res, &a, 128); /* equivalent to res = a << 128 +bigint_rshift(&res, &a, 128); /* equivalent to res = a >> 128 +``` + +All functions work on temporary values and perform a copy at the end, +meaning it's safe to do things like: + +```c +bigint_add(&b, &b, &a); /* equivalent to b += a +``` + +In some cases there's faster versions of math operations if +overwriting the original is OK (for example, left-shifting and +right-shifting). Those functions are: + +```c +bigint_lshift_overwrite(bigint *b, size_t bits); /* equivalent to b <<= bits */ +bigint_rshift_overwrite(bigint *b, size_t bits); /* equivalent to b >>= bits */ + +/* this one takes bigint_words as parameters (default uint32_t), it's a much faster division, +equivalent to: + remainder = numerator % denominator + numerator /= denominator +*/ +bigint_div_mod_word(bigint *numerator, bigint_word* remainder, bigint_word denominator); +``` + +Finally there's a function to write the bigint out as a string. It accepts +a buffer, the size of the buffer, and the base to use. Notably, it does NOT write out a NULL +character. It *does* include a prefix based on the base: + +* base 2 - prefix with 'b' +* base 8 - prefix with '0' +* base 16 - prefix with '0x' + +```c +char buffer[101]; +/* always pass size-1 to leave room for null terminator */ +buffer[bigint_to_string(buffer,100,&b,10)] = '\0'; +``` + +If you need a length estimate you can pass `NULL` as the buffer: + +```c +size_t needed = bigint_to_string(NULL,0, &b, 10); +if(!needed) return some_kind_of_error; + +/* add extra byte for '\0' terminator */ +char *buffer = malloc(needed+1); +buffer[bigint_to_string(buffer,needed,&b,10)] = '\0'; +``` + +All functions (besides `bigint_to_string`) return an integer, +with 0 meaning success, or one of the following error codes: + +* `BIGINT_ENOMEM` - an attempt to resize a bigint ran out of memory. +* `BIGINT_ELIMIT` - an attempt to resize a bigint would exceed the `limit` field. +* `BIGINT_EINVAL` - returned when parsing a string with invalid data. + +`bigint_to_string` returns the number of characters required/written, +returning `0` indicates some kind of error. + +## LICENSE + +BSD Zero Clause (see the `LICENSE` file). + +Some files are third-party and have their own licensing: + +* `utest.h`: Public Domain / Unlicense, see file for details. + * Note, `utest.h` is not required for library usage, this is for running automated tests. diff --git a/bigint.h b/bigint.h new file mode 100644 index 0000000..eebb44f --- /dev/null +++ b/bigint.h @@ -0,0 +1,1612 @@ +#ifndef BIGINTH +#define BIGINTH + +#if !defined(BIGINT_API) + #ifdef _WIN32 + #define BIGINT_DLL_IMPORT __declspec(dllimport) + #define BIGINT_DLL_EXPORT __declspec(dllexport) + #else + #if defined(__GNUC__) && __GNUC__ >= 4 + #define BIGINT_DLL_IMPORT __attribute__((visibility("default"))) + #define BIGINT_DLL_EXPORT __attribute__((visibility("default"))) + #else + #define BIGINT_DLL_IMPORT + #define BIGINT_DLL_EXPORT + #endif + #endif + + #ifdef BIGINT_IMPLEMENTATION + #define BIGINT_API BIGINT_DLL_EXPORT + #else + #define BIGINT_API BIGINT_DLL_IMPORT + #endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include +#include +#include +#include + +#define BIGINT_ENOMEM 1 +#define BIGINT_ELIMIT 2 +#define BIGINT_EINVAL 4 + +/* if you override any of: + * BIGINT_WORD_WIDTH + * BIGINT_WORD_TYPE + * BIGINT_DEFAULT_LIMIT + * BIGINT_NO_MALLOC + * + * They needs to be defined before *every* #include + * (not just the implementation include) since that + * affects function signatures, the BIGINT_INIT + * initializer, and (for NO_MALLOC) the struct itself. */ + +#ifndef BIGINT_DEFAULT_LIMIT +#define BIGINT_DEFAULT_LIMIT 4096 +#endif + +#ifndef BIGINT_WORD_WIDTH +#define BIGINT_WORD_WIDTH 4 +#endif + +#ifndef BIGINT_WORD_TYPE +#if BIGINT_WORD_WIDTH == 1 +#define BIGINT_WORD_TYPE uint8_t +#elif BIGINT_WORD_WIDTH == 2 +#define BIGINT_WORD_TYPE uint16_t +#elif BIGINT_WORD_WIDTH == 4 +#define BIGINT_WORD_TYPE uint32_t +#elif BIGINT_WORD_WIDTH == 8 +#define BIGINT_WORD_TYPE uint64_t +#else +#error unknown_type +#endif +#endif + +typedef BIGINT_WORD_TYPE bigint_word; + +typedef struct bigint { + size_t size; + size_t alloc; + size_t limit; + size_t sign; +#ifdef BIGINT_NO_MALLOC + bigint_word words[(BIGINT_DEFAULT_LIMIT/BIGINT_WORD_WIDTH)]; + /* TODO: + * compiling with BIGINT_NO_MALLOC defined and the word type set to uint8_t + * generates the following warning: + * bigint.h:409:24: warning: writing 1 byte into a region of size 0 [-Wstringop-overflow=] + * 409 | b->words[old_size] = val; + * | ~~~~~~~~~~~~~~~~~~~^~~~~ + * bigint.h: In function ‘bigint_from_string_base10’: + * bigint.h:82:17: note: at offset -1 into destination object ‘words’ of size 4096 + * 82 | bigint_word words[(BIGINT_DEFAULT_LIMIT/BIGINT_WORD_WIDTH)]; + * .... why? */ +#else + bigint_word *words; +#endif +} bigint; + +#define BIGINT_WORD_SIZE sizeof(bigint_word) +#define BIGINT_WORD_BIT (BIGINT_WORD_SIZE * CHAR_BIT) +#define BIGINT_WORD_MASK ((bigint_word)-1) + +#define BIGINT_HALF_WORD_BIT (BIGINT_WORD_BIT/2) +#define BIGINT_HALF_WORD_MASK (BIGINT_WORD_MASK >> BIGINT_HALF_WORD_BIT) + +#ifdef BIGINT_NO_MALLOC +#define BIGINT_INIT { .size = 0, .alloc = 0, .sign = 0, .limit = BIGINT_DEFAULT_LIMIT } +#else +#define BIGINT_INIT { .size = 0, .alloc = 0, .sign = 0, .limit = BIGINT_DEFAULT_LIMIT, .words = NULL } +#endif +extern const bigint* BIGINT_ZERO; + +BIGINT_API +void bigint_init(bigint* b); + +BIGINT_API +void bigint_free(bigint* b); + +BIGINT_API +int bigint_copy(bigint* b, const bigint* a); + +BIGINT_API +int bigint_from_word(bigint* b, bigint_word val); + +BIGINT_API +int bigint_from_string(bigint* b, const char* str, size_t len, unsigned int base); + +/* convenience function for NULL-terminated strings */ +BIGINT_API +int bigint_from_cstring(bigint* b, const char* str, unsigned int base); + +BIGINT_API +size_t bigint_to_string(char* str, size_t len, const bigint* b, unsigned int base); + +BIGINT_API +int bigint_inc(bigint* c, const bigint *a); + +BIGINT_API +int bigint_dec(bigint* c, const bigint *b); + +BIGINT_API +int bigint_add(bigint* c, const bigint* a, const bigint* b); + +BIGINT_API +int bigint_sub(bigint* c, const bigint* a, const bigint* b); + +BIGINT_API +int bigint_mul(bigint* c, const bigint* a, const bigint* b); + +BIGINT_API +int bigint_div_mod(bigint* quotient, bigint* remainder, const bigint* numerator, const bigint* denominator); + +/* faster div_mod for when you're dividing by a regular, positive word type - note that this + * overwrites numerator! roughly equivalent to: + * remainder = numerator % denominator + * numerator /= denominator */ +BIGINT_API +void bigint_div_mod_word(bigint* numerator, bigint_word* remainder, bigint_word denominator); + +BIGINT_API +int bigint_lshift(bigint* c, const bigint* a, size_t bits); + +BIGINT_API +int bigint_rshift(bigint* c, const bigint* a, size_t bits); + +BIGINT_API +int bigint_lshift_overwrite(bigint* a, size_t bits); + +BIGINT_API +int bigint_rshift_overwrite(bigint* a, size_t bits); + +#ifdef __cplusplus +} +#endif + +#endif + +#ifdef BIGINT_IMPLEMENTATION + +#define BIGINT_BLOCK_SIZE 8 + +#if __GNUC__ > 4 || \ + (__GNUC__ == 4 && __GNUC_MINOR__ >= 5) +#define BIGINT_UNREACHABLE __builtin_unreachable() +#define BIGINT_UNREACHABLE_RETURN(val) __builtin_unreachable() +#endif + +#if __GNUC__ > 3 || \ + (__GNUC__ == 3 && __GNUC_MINOR__ >= 3) +#define BIGINT_NONNULL1 __attribute__((__nonnull__(1))) +#endif + +#if _MSC_VER >= 1310 +#define BIGINT_UNREACHABLE __assume(0) +#define BIGINT_UNREACHABLE_RETURN(val) __builtin_unreachable() +#endif + +#ifndef BIGINT_UNREACHABLE +#define BIGINT_UNREACHABLE assert(0) +#endif + +#ifndef BIGINT_NONNULL1 +#define BIGINT_NONNULL1 +#endif + +#ifndef BIGINT_UNREACHABLE_RETURN +#define BIGINT_UNREACHABLE_RETURN(val) return val +#endif + +#ifndef BIGINT_SINGLE_WORD_ONLY +#ifndef BIGINT_DWORD_TYPE +#if BIGINT_WORD_WIDTH == 8 +#if defined(__SIZEOF_INT128__) +#define BIGINT_DWORD_TYPE unsigned __int128 +#endif +#elif BIGINT_WORD_WIDTH == 4 +#define BIGINT_DWORD_TYPE uint64_t +#elif BIGINT_WORD_WIDTH == 2 +#define BIGINT_DWORD_TYPE uint32_t +#elif BIGINT_WORD_WIDTH == 1 +#define BIGINT_DWORD_TYPE uint16_t +#endif +#endif /* BIGINT_DWORD_TYPE */ +#endif /* !SINGLE_WORD_ONLY */ + +#ifdef _WIN32 +#include +#if _MSC_VER +#if BIGINT_WORD_WIDTH == 8 && defined(_WIN64) +#pragma intrinsic(_BitScanReverse64) +#pragma intrinsic(_umul128) +#elif BIGINT_WORD_WIDTH <= 4 +#pragma intrinsic(_BitScanReverse) +#endif +#endif +#endif + + +#ifdef BIGINT_NO_MALLOC +static const bigint BIGINT_ZERO_STORAGE = { .size = 0, .alloc = 0, .sign = 0, .limit = BIGINT_DEFAULT_LIMIT }; +#else +static const bigint BIGINT_ZERO_STORAGE = { .words = NULL, .size = 0, .alloc = 0, .sign = 0, .limit = BIGINT_DEFAULT_LIMIT }; +#endif +const bigint* BIGINT_ZERO = &BIGINT_ZERO_STORAGE; + +static void bigint_reset(bigint* b); +static int bigint_resize(bigint* b, size_t size); +static void bigint_truncate(bigint* b); +static int bigint_append(bigint* b, bigint_word val); + +static int bigint_add_word(bigint* b, bigint_word val); + +static size_t bigint_word_bitlength(bigint_word a); +static int bigint_word_add(bigint_word *a, bigint_word b); +static int bigint_word_sub(bigint_word *a, bigint_word b); +static bigint_word bigint_word_mul(bigint_word *a, bigint_word b); + +static int bigint_add_unsigned(bigint* a, const bigint* b); +static int bigint_sub_unsigned(bigint* a, const bigint* b); + +static int bigint_add_signed(bigint* c, const bigint* a, size_t a_sign, const bigint* b, size_t b_sign); + +static int bigint_cmp_abs(const bigint* a, const bigint* b); + +static int bigint_mul_long(bigint* c, const bigint* a, const bigint* b); + +static size_t bigint_bitlength(const bigint* a); +static int bigint_get_bit(const bigint* a, size_t b); +static int bigint_set_bit(bigint* a, size_t b, uint8_t val); +static int bigint_from_string_base16(bigint* b, const char* str, size_t len); +static int bigint_from_string_base10(bigint* b, const char* str, size_t len); +static int bigint_from_string_base8(bigint* b, const char* str, size_t len); +static int bigint_from_string_base2(bigint* b, const char* str, size_t len); +static int bigint_from_string_base0(bigint* b, const char* str, size_t len); + +/* returns the length of a string for the given base */ +static size_t bigint_len_string_base16(const bigint* b); +static size_t bigint_len_string_base10(const bigint* b); +static size_t bigint_len_string_base8(const bigint* b); +static size_t bigint_len_string_base2(const bigint* b); + +BIGINT_NONNULL1 +static size_t bigint_to_string_base16(char* str, size_t len, bigint* b); +BIGINT_NONNULL1 +static size_t bigint_to_string_base10(char* str, size_t len, bigint* b); +BIGINT_NONNULL1 +static size_t bigint_to_string_base8(char* str, size_t len, bigint* b); +BIGINT_NONNULL1 +static size_t bigint_to_string_base2(char* str, size_t len, bigint* b); + +static +void bigint_reset(bigint* b) { + b->size = 0; + b->sign = 0; +#ifndef BIGINT_NO_MALLOC + if(b->alloc) +#endif + b->words[0] = 0; +} + +static +int bigint_resize(bigint* b, size_t size) { +#ifdef BIGINT_NO_MALLOC + size_t alloc = size; + if(alloc * BIGINT_WORD_SIZE > BIGINT_DEFAULT_LIMIT) return BIGINT_ENOMEM; +#else + size_t alloc = ((size + BIGINT_BLOCK_SIZE-1)) & -BIGINT_BLOCK_SIZE; +#endif + + if(alloc * BIGINT_WORD_SIZE > b->limit) return BIGINT_ELIMIT; + +#ifndef BIGINT_NO_MALLOC + if(b->alloc < alloc) { + if(b->words != NULL) { + b->words = (bigint_word *)realloc(b->words,alloc * BIGINT_WORD_SIZE); + } else { + b->words = (bigint_word *)malloc(alloc * BIGINT_WORD_SIZE); + } + if(b->words == NULL) return BIGINT_ENOMEM; + memset(&b->words[b->alloc],0,(alloc - b->alloc) * BIGINT_WORD_SIZE); + b->alloc = alloc; + } +#endif + + if(b->size > size) { /* shrinking */ + b->words[size] = 0; + } else if(b->size < size) { /* growing */ + b->words[b->size] = 0; + } + + b->size = size; + return 0; +} + +static +void bigint_truncate(bigint* b) { + while(b->size && b->words[b->size - 1] == 0) b->size--; + b->sign = b->size && b->sign; +} + +static inline +size_t bigint_word_bitlength(bigint_word a) { +#if defined(_WIN32) && BIGINT_WORD_WIDTH <= 4 + unsigned long index; + unsigned long m = (unsigned long)a; + _BitScanReverse(&index,m); + return (size_t)++index; +#elif defined(_WIN64) && BIGINT_WORD_WIDTH == 8 + unsigned long index; + unsigned long m = (unsigned long)a; + _BitScanReverse64(&index,a); + return (size_t)++index; +#elif __GNUC__ > 4 || \ + (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) + assert(a); +#if BIGINT_WORD_WIDTH == 8 + return (size_t) ((sizeof(unsigned long long) * CHAR_BIT) - __builtin_clzll((unsigned long long)a)); +#else + return (size_t) ((sizeof(unsigned long) * CHAR_BIT) - __builtin_clzl((unsigned long)a)); +#endif /* BIGINT_WORD_WIDTH == 8 */ + +#else /* all other platforms */ + size_t i = BIGINT_WORD_BIT; + + assert(a); + while(i-- > 0) { + if(a >> i & 1) return i+1; + } + + BIGINT_UNREACHABLE_RETURN(0); +#endif +} + +static inline +int bigint_get_bit(const bigint* a, size_t b) { + size_t word = b / BIGINT_WORD_BIT; + size_t bit = b % BIGINT_WORD_BIT; + if(word > a->size) return 0; + return (a->words[word] >> bit) & 1; +} + +static inline +int bigint_set_bit(bigint* a, size_t b, uint8_t val) { + size_t word = b / BIGINT_WORD_BIT; + size_t bit = b % BIGINT_WORD_BIT; + int r; + + if(a->size <= word) { + if( (r = bigint_resize(a,word+1)) != 0) return r; + } + +#if 0 /* this is the obvious way, for reference */ + if(val) { + a->words[word] |= ((bigint_word)1) << bit; + } else { + a->words[word] &= ~(((bigint_word)1) << bit); + } +#else + /* adopted from https://graphics.stanford.edu/~seander/bithacks.html#ConditionalSetOrClearBitsWithoutBranching */ + a->words[word] ^= (-val ^ a->words[word]) & (((bigint_word)1) << bit); +#endif + return 0; +} + +static +size_t bigint_bitlength(const bigint* a) { + size_t l; + if(a->size == 0) return 0; + l = a->size - 1; + return bigint_word_bitlength(a->words[l]) + (l * BIGINT_WORD_BIT); +} + +static +int bigint_append(bigint* b, bigint_word val) { + int r; + size_t old_size = b->size; + r = bigint_resize(b,b->size+1); + if(r) return r; + b->words[old_size] = val; + return 0; +} + +static int bigint_word_add(bigint_word *a, bigint_word b) { + return (*a += b) < b; +} + +static int bigint_word_sub(bigint_word *a, bigint_word b) { + bigint_word tmp = *a; + return (*a -= b) > tmp; +} + +/* performs *a *= b and returns the carry, using + * only the single-word data type */ +static inline +bigint_word bigint_word_mul_cross(bigint_word *a, bigint_word b) { + /* assuming a 32-bit word size - + * using cross-products we can say: + * (64-bit result) = + * a_lo * bi_lo + * + (a_hi * b_lo << 16) + * + (a_lo * b_hi << 16) + * + (a_hi * b_hi << 32) + * + * when doing additions, we need to mind overflow + * and carry into res_hi. + * It's way easier and faster to just use a double-width + * type, this variant was mostly a learning exercise in how + * to multiply numbers with carry/overflow */ + + bigint_word a_hi, b_hi, a_lo, b_lo, tmp, res_lo, res_hi; + a_hi = (*a) >> BIGINT_HALF_WORD_BIT; + a_lo = (*a) & BIGINT_HALF_WORD_MASK; + b_hi = b >> BIGINT_HALF_WORD_BIT; + b_lo = b & BIGINT_HALF_WORD_MASK; + + res_lo = a_lo * b_lo; + res_hi = a_hi * b_hi; + + tmp = a_hi * b_lo; + res_hi += bigint_word_add(&res_lo,tmp << BIGINT_HALF_WORD_BIT); + res_hi += (tmp >> BIGINT_HALF_WORD_BIT); + + tmp = a_lo * b_hi; + res_hi += bigint_word_add(&res_lo,tmp << BIGINT_HALF_WORD_BIT); + res_hi += (tmp >> BIGINT_HALF_WORD_BIT); + + *a = res_lo; + return res_hi; +} + +#ifndef BIGINT_SINGLE_WORD_ONLY +/* attempt to find method for multiplication with a larger type */ + +#ifdef BIGINT_DWORD_TYPE +static inline +bigint_word bigint_word_mul_double(bigint_word *a, bigint_word b) { + BIGINT_DWORD_TYPE res; + res = *a; + res *= b; + *a = res & BIGINT_WORD_MASK; + res >>= BIGINT_WORD_BIT; + return res; +} +#define BIGINT_WORD_MUL_DOUBLE +#elif BIGINT_WORD_WIDTH == 8 && defined(_WIN64) +static inline +bigint_word bigint_word_mul_double(bigint_word *a, bigint_word b) { + uint64_t lowProduct; + uint64_t highProduct; + + lowProduct = _umul128(*a,b,&highProduct); + + *a = lowProduct; + return highProduct; +} +#define BIGINT_WORD_MUL_DOUBLE +#endif +#endif + +/* performs *a *= b and returns the carry */ +static bigint_word bigint_word_mul(bigint_word *a, bigint_word b) { +#if defined(BIGINT_SINGLE_WORD_ONLY) || !defined(BIGINT_WORD_MUL_DOUBLE) + return bigint_word_mul_cross(a,b); +#else /* !SINGLE_WORD_ONLY */ + return bigint_word_mul_double(a,b); +#endif +} + +#if defined(BIGINT_SINGLE_WORD_ONLY) || !defined(BIGINT_WORD_MUL_DOUBLE) +#define BIGINT_WORD_MUL_CONST(x) \ +static inline bigint_word bigint_word_mul_const_ ## x (bigint_word *a) { \ + bigint_word a_hi, b_hi, a_lo, b_lo, tmp, res_lo, res_hi; \ + a_hi = (*a) >> BIGINT_HALF_WORD_BIT; \ + a_lo = (*a) & BIGINT_HALF_WORD_MASK; \ + b_hi = ((bigint_word)x) >> BIGINT_HALF_WORD_BIT; \ + b_lo = ((bigint_word)x) & BIGINT_HALF_WORD_MASK; \ + res_lo = a_lo * b_lo; \ + res_hi = a_hi * b_hi; \ + tmp = a_hi * b_lo; \ + res_hi += bigint_word_add(&res_lo,tmp << BIGINT_HALF_WORD_BIT); \ + res_hi += (tmp >> BIGINT_HALF_WORD_BIT); \ + tmp = a_lo * b_hi; \ + res_hi += bigint_word_add(&res_lo,tmp << BIGINT_HALF_WORD_BIT); \ + res_hi += (tmp >> BIGINT_HALF_WORD_BIT); \ + *a = res_lo; \ + return res_hi; \ +} +#elif defined(BIGINT_DWORD_TYPE) +#define BIGINT_WORD_MUL_CONST(x) \ +static inline bigint_word bigint_word_mul_const_ ## x (bigint_word *a) { \ + BIGINT_DWORD_TYPE res = *a; \ + res *= x; \ + *a = res & BIGINT_WORD_MASK; \ + res >>= BIGINT_WORD_BIT; \ + return res; \ +} +#else +#define BIGINT_WORD_MUL_CONST(x) \ +static inline bigint_word bigint_word_mul_const_ ## x (bigint_word *a) { \ + return bigint_word_mul_double(a,x); +} +#endif + +BIGINT_WORD_MUL_CONST(10) + +static int bigint_add_word(bigint* a, bigint_word val) { + int r; + size_t i = 0; + bigint_word carry = 0; + + if(!val) return 0; /* adding 0 */ + if(!a->size) { + if( (r = bigint_resize(a,1)) != 0) return r; + } + + carry = bigint_word_add(&a->words[0],val); + + i = 1; + while(i < a->size && carry) { + carry = bigint_word_add(&a->words[i],carry); + i++; + } + if(carry) { + if( (r = bigint_append(a, carry)) != 0) return r; + } + + bigint_truncate(a); + return 0; +} + +static int bigint_add_unsigned(bigint* a, const bigint* b) { + int r; + size_t i = 0; + size_t a_size, b_size, max_size; + bigint_word carry = 0; + + if(b->size == 0) return 0; + + a_size = a->size; + b_size = b->size; + max_size = a_size > b_size ? a_size : b_size; + + if( (r = bigint_resize(a,max_size)) != 0) return r; + + i = 0; + while(i < b->size) { + carry = bigint_word_add(&a->words[i],carry); + carry += bigint_word_add(&a->words[i],b->words[i]); + i++; + } + while(i < max_size && carry) { + carry = bigint_word_add(&a->words[i],carry); + i++; + } + if(carry) { + if( (r = bigint_append(a, carry)) != 0) return r; + } + + bigint_truncate(a); + return 0; +} + +static int bigint_sub_unsigned(bigint* a, const bigint* b) { + size_t i; + bigint_word carry = 0; + + if(b->size == 0) return 0; + + i = 0; + while(i < b->size) { + carry = bigint_word_sub(&a->words[i],carry); + carry += bigint_word_sub(&a->words[i],b->words[i]); + i++; + } + while(i < a->size && carry) { + carry = bigint_word_sub(&a->words[i],carry); + i++; + } + bigint_truncate(a); + return 0; +} + +static +int bigint_cmp_abs(const bigint* a, const bigint* b) { + size_t i; + if(a->size != b->size) return a->size < b->size ? -1 : 1; + i = a->size; + while(i--) { + if(a->words[i] != b->words[i]) + return a->words[i] < b->words[i] ? -1 : 1; + } + return 0; +} + +BIGINT_API +int bigint_lshift_overwrite(bigint* c, size_t bits) { + int r; + size_t words, expanded, i; + bigint_word lo, hi; + + if(c->size == 0) return 0; + + words = bits / BIGINT_WORD_BIT; + bits %= BIGINT_WORD_BIT; + + expanded = c->size + words + (bits != 0); + + if( (r = bigint_resize(c,expanded)) != 0) return r; + + if(bits == 0) { + i = expanded; + while(i-- > words) { + c->words[i] = c->words[i - words]; + } + } else { + lo = hi = 0; + i = expanded; + while(--i > words) { + lo = c->words[i - words - 1]; + c->words[i] = (hi << bits) | (lo >> (BIGINT_WORD_BIT - bits)); + hi = lo; + } + c->words[words] = hi << bits; + } + i = 0; + while(iwords[i] = 0; + i++; + } + bigint_truncate(c); + return 0; +} + +BIGINT_API +int bigint_lshift(bigint* c, const bigint* a, size_t bits) { + int r; + + if( (r = bigint_copy(c,a)) != 0) return r; + return bigint_lshift_overwrite(c,bits); +} + + +BIGINT_API +int bigint_rshift_overwrite(bigint* c, size_t bits) { + int r; + size_t words, i; + bigint_word lo, hi; + + words = bits / BIGINT_WORD_BIT; + if(words >= c->size) { /* right-shiting by more words than we actually have */ + c->size = 0; + c->sign = 0; + return 0; + } + + bits %= BIGINT_WORD_BIT; + i = 0; + + if(bits == 0) { + while(i < c->size - words) { + c->words[i] = c->words[i + words]; + i++; + } + } else { + lo = hi = c->words[words]; + while(i < c->size - words - 1) { + hi = c->words[i + words + 1]; + c->words[i] = (hi << (BIGINT_WORD_BIT - bits)) | lo >> bits; + lo = hi; + i++; + } + c->words[c->size - words - 1] = lo >> bits; + } + if( (r = bigint_resize(c, c->size - words)) != 0) return r; + bigint_truncate(c); + return 0; +} + +#define BIGINT_RSHIFT_OVERWRITE_SMALL(x) \ +static inline void bigint_rshift_overwrite_small_ ## x (bigint* c) { \ + size_t i; \ + bigint_word lo, hi; \ + i = 0; \ + lo = hi = c->words[0]; \ + while(i < c->size - 1) { \ + hi = c->words[i + 1]; \ + c->words[i] = (hi << (BIGINT_WORD_BIT - x)) | lo >> x; \ + lo = hi; \ + i++; \ + } \ + c->words[i] = lo >> x; \ + bigint_truncate(c); \ +} + +BIGINT_RSHIFT_OVERWRITE_SMALL(4) +BIGINT_RSHIFT_OVERWRITE_SMALL(3) +BIGINT_RSHIFT_OVERWRITE_SMALL(1) + +BIGINT_API +int bigint_rshift(bigint* c, const bigint* a, size_t bits) { + int r; + if( (r = bigint_copy(c,a)) != 0) return r; + return bigint_rshift_overwrite(c,bits); +} + +static +int bigint_mul_long(bigint* c, const bigint* a, const bigint* b) { + int r; + size_t expanded; + size_t ia, ib; + bigint_word carry; +#if defined(BIGINT_SINGLE_WORD_ONLY) || !defined(BIGINT_DWORD_TYPE) + bigint_word lo, hi; +#else + BIGINT_DWORD_TYPE res; +#endif + + expanded = a->size + b->size + 1; + + if( (r = bigint_resize(c, expanded)) != 0) return r; + c->sign = a->sign ^ b->sign; + + for(ib = 0; ib < b->size; ib++) { + carry = 0; + for(ia = 0; ia < a->size; ia++) { +#if defined(BIGINT_SINGLE_WORD_ONLY) || !defined(BIGINT_DWORD_TYPE) + lo = a->words[ia]; + hi = bigint_word_mul(&lo,b->words[ib]); + hi += bigint_word_add(&lo,c->words[ia+ib]); + hi += bigint_word_add(&lo,carry); + c->words[ia + ib] = lo; + carry = hi; +#else + res = a->words[ia]; + res *= b->words[ib]; + res += c->words[ia + ib]; + res += carry; + c->words[ia + ib] = res & BIGINT_WORD_MASK; + carry = res >> BIGINT_WORD_BIT; +#endif + } + c->words[ib + a->size] = carry; + } + + bigint_truncate(c); + return 0; +} + +static int bigint_mul_word(bigint* a, bigint_word val) { + int r; + size_t i = 0; + bigint_word carry = 0; + bigint_word c = 0; + + if(!val) { + a->size = 0; + a->sign = 0; + return 0; + } + + carry = 0; + for(i=0;isize;i++) { + c = bigint_word_mul(&a->words[i],val); + c += bigint_word_add(&a->words[i],carry); + carry = c; + } + if(carry) { + if( (r = bigint_append(a, carry)) != 0) return r; + } + + bigint_truncate(a); + return 0; +} + +#define BIGINT_MUL_CONST(x) \ +static inline int bigint_mul_const_ ## x (bigint* a) { \ + int r; \ + size_t i = 0; \ + bigint_word carry = 0; \ + bigint_word c = 0; \ + for(i=0;isize;i++) { \ + c = bigint_word_mul_const_ ## x (&a->words[i]); \ + c += bigint_word_add(&a->words[i],carry); \ + carry = c; \ + } \ + if(carry) { \ + if( (r = bigint_append(a, carry)) != 0) return r; \ + } \ + bigint_truncate(a); \ + return 0; \ +} + +BIGINT_MUL_CONST(10) + +/* does NOT accept a string with a leading 0x - that's handled in + * bigint_from_string. does NOT parse a leading +/-, also handled + * in bigint_from_string */ +static int bigint_from_string_base16(bigint* b, const char* str, size_t len) { + bigint_word word; + size_t i = 0; + int r; + + while(i < len && str[i]) { + if( (r = bigint_lshift_overwrite(b,4)) != 0) return r; + switch(str[i]) { + case '0': word = 0; break; + case '1': word = 1; break; + case '2': word = 2; break; + case '3': word = 3; break; + case '4': word = 4; break; + case '5': word = 5; break; + case '6': word = 6; break; + case '7': word = 7; break; + case '8': word = 8; break; + case '9': word = 9; break; + case 'A': word = 10; break; + case 'a': word = 10; break; + case 'B': word = 11; break; + case 'b': word = 11; break; + case 'C': word = 12; break; + case 'c': word = 12; break; + case 'D': word = 13; break; + case 'd': word = 13; break; + case 'E': word = 14; break; + case 'e': word = 14; break; + case 'F': word = 15; break; + case 'f': word = 15; break; + default: return BIGINT_EINVAL; + } + if( (r = bigint_add_word(b,word)) != 0) return r; + i++; + } + + return 0; +} + +/* does NOT parse a leading +/-, handled in bigint_from_string */ +static int bigint_from_string_base10(bigint* b, const char* str, size_t len) { + bigint_word word; + size_t i = 0; + int r; + + while(i < len && str[i]) { + if( (r = bigint_mul_const_10(b)) != 0) return r; + switch(str[i]) { + case '0': word = 0; break; + case '1': word = 1; break; + case '2': word = 2; break; + case '3': word = 3; break; + case '4': word = 4; break; + case '5': word = 5; break; + case '6': word = 6; break; + case '7': word = 7; break; + case '8': word = 8; break; + case '9': word = 9; break; + default: return BIGINT_EINVAL; + } + if( (r = bigint_add_word(b,word)) != 0) return r; + i++; + } + + return 0; +} + +/* does NOT parse a leading +/-, handled in bigint_from_string */ +static int bigint_from_string_base8(bigint* b, const char* str, size_t len) { + bigint_word word; + size_t i = 0; + int r; + + while(i < len && str[i]) { + if( (r = bigint_lshift_overwrite(b,3)) != 0) return r; + switch(str[i]) { + case '0': word = 0; break; + case '1': word = 1; break; + case '2': word = 2; break; + case '3': word = 3; break; + case '4': word = 4; break; + case '5': word = 5; break; + case '6': word = 6; break; + case '7': word = 7; break; + default: return BIGINT_EINVAL; + } + if( (r = bigint_add_word(b,word)) != 0) return r; + i++; + } + + return 0; +} + +/* does NOT parse a leading +/-, handled in bigint_from_string */ +static int bigint_from_string_base2(bigint* b, const char* str, size_t len) { + bigint_word word; + size_t i = 0; + int r; + + while(i < len && str[i]) { + if( (r = bigint_lshift_overwrite(b,1)) != 0) return r; + switch(str[i]) { + case '0': word = 0; break; + case '1': word = 1; break; + default: return BIGINT_EINVAL; + } + if( (r = bigint_add_word(b,word)) != 0) return r; + i++; + } + + return 0; +} + +static int bigint_from_string_base0(bigint* b, const char* str, size_t len) { + if(str[0] == '0') { + str++; + len--; + if(len == 0) { + bigint_reset(b); + return 0; + } + + if(str[0] == 'x' || str[0] == 'X') { + str++; + len--; + if(len == 0) return BIGINT_EINVAL; + return bigint_from_string_base16(b,str,len); + } + + if(str[0] == 'b' || str[0] == 'B') { + str++; + len--; + if(len == 0) return BIGINT_EINVAL; + return bigint_from_string_base2(b,str,len); + } + + return bigint_from_string_base8(b,str,len); + } + + if(len > 1 && str[0] == 'x') { + str++; + len--; + return bigint_from_string_base16(b,str,len); + } + + if(len > 1 && str[0] == 'b') { + str++; + len--; + return bigint_from_string_base2(b,str,len); + } + + return bigint_from_string_base10(b,str,len); +} + +/* allows strings like: + * -b010101 + * -0b010101 + * 01231 (octal) + * 1234 (base10) + * x1234 (base16) + * 0x1234 (base16) + */ + +BIGINT_API +int bigint_from_string(bigint* b, const char* str, size_t len, unsigned int base) { + int r = 0; + size_t sign = 0; + + bigint_reset(b); + + if(len == 0) return BIGINT_EINVAL; + + if(str[0] == '-') { + sign = 1; + str++; + len--; + if(len == 0) return BIGINT_EINVAL; + } + + switch(base) { + case 2: { + if(len > 1 && (str[0] == 'b')) { + str++; + len--; + if(len == 0) return BIGINT_EINVAL; + } + else if(len > 2 && str[0] == '0' && (str[1] == 'b' || str[1] == 'B')) { + str += 2; + len -= 2; + } + r = bigint_from_string_base2(b,str,len); break; + } + case 8: { + r = bigint_from_string_base8(b,str,len); break; + } + case 10: r = bigint_from_string_base10(b,str,len); break; + case 16: { + if(len > 1 && str[0] == 'x') { + str++; + len--; + if(len == 0) return BIGINT_EINVAL; + } + else if(len > 2 && str[0] == '0' && (str[1] == 'X' || str[1] == 'x')) { + str += 2; + len -= 2; + } + r = bigint_from_string_base16(b,str,len); break; + } + case 0: r = bigint_from_string_base0(b,str,len); break; + default: return BIGINT_EINVAL; + } + if(r == 0) { + b->sign = sign; + } + return r; +} + +BIGINT_API +int bigint_from_cstring(bigint* b, const char* str, unsigned int base) { + return bigint_from_string(b,str,strlen(str),base); +} + +BIGINT_API +void bigint_init(bigint* b) { + *b = *BIGINT_ZERO; + b->limit = BIGINT_DEFAULT_LIMIT; +} + +BIGINT_API +void bigint_free(bigint* b) { + bigint_reset(b); +#ifndef BIGINT_NO_MALLOC + if(b->alloc != 0) { + free(b->words); + b->words = NULL; + b->alloc = 0; + } +#endif +} + +BIGINT_API +int bigint_copy(bigint *dest, const bigint *src) { + int r; + size_t limit; + if(src == dest) return 0; + limit = dest->limit; + dest->limit = src->limit; + if(src->size != dest->size) { + r = bigint_resize(dest,src->size); + if(r) { + dest->limit = limit; + return r; + } + } + + /* we may be copying a zero-length */ + if(dest->size == 0) { + dest->sign = 0; + return 0; + } + + assert(dest->words != NULL); + memcpy(dest->words,src->words,dest->size * BIGINT_WORD_SIZE); + dest->sign = src->sign; + return 0; +} + +BIGINT_API +int bigint_from_word(bigint* b, bigint_word val) { + bigint_reset(b); + return bigint_append(b,val); +} + +BIGINT_API +int bigint_from_u8(bigint* b, uint8_t val) { + bigint_reset(b); + return bigint_append(b, (bigint_word)(val & BIGINT_WORD_MASK) ); +} + +BIGINT_API +int bigint_from_u16(bigint* b, uint16_t val) { + int r; + bigint_reset(b); +#if BIGINT_WORD_WIDTH < 2 + while(val) { + r = bigint_append(b, (bigint_word)(val & BIGINT_WORD_MASK)); + if(r) return r; + val >>= BIGINT_WORD_BIT; + } +#else + r = bigint_append(b, (bigint_word)(val & BIGINT_WORD_MASK)); +#endif + return r; +} + +BIGINT_API +int bigint_from_u32(bigint* b, uint32_t val) { + int r; + bigint_reset(b); +#if BIGINT_WORD_WIDTH < 4 + while(val) { + r = bigint_append(b, (bigint_word)(val & BIGINT_WORD_MASK)); + if(r) return r; + val >>= BIGINT_WORD_BIT; + } +#else + r = bigint_append(b, (bigint_word)(val & BIGINT_WORD_MASK)); +#endif + return r; +} + +BIGINT_API +int bigint_from_u64(bigint* b, uint64_t val) { + int r; + bigint_reset(b); +#if BIGINT_WORD_WIDTH < 8 + while(val) { + r = bigint_append(b, (bigint_word)(val & BIGINT_WORD_MASK)); + if(r) return r; + val >>= BIGINT_WORD_BIT; + } +#else + r = bigint_append(b, (bigint_word)(val & BIGINT_WORD_MASK)); +#endif + return r; +} + +BIGINT_API +int bigint_from_i8(bigint* b, int8_t val) { + int r; + uint8_t uval = val < 0 ? -val : val; + + r = bigint_from_u8(b, uval); + if(r) return r; + b->sign = val < 0; + return 0; +} + +BIGINT_API +int bigint_from_i16(bigint* b, int16_t val) { + int r; + uint16_t uval = val < 0 ? -val : val; + + r = bigint_from_u16(b, uval); + if(r) return r; + b->sign = val < 0; + return 0; +} + +BIGINT_API +int bigint_from_i32(bigint* b, int32_t val) { + int r; + uint32_t uval = val < 0 ? -val : val; + + r = bigint_from_u32(b, uval); + if(r) return r; + b->sign = val < 0; + return 0; +} + +BIGINT_API +int bigint_from_i64(bigint* b, int64_t val) { + int r; + uint64_t uval = val < 0 ? -val : val; + + r = bigint_from_u64(b, uval); + if(r) return r; + b->sign = val < 0; + return 0; +} + +static +int bigint_add_signed(bigint* c, const bigint* a, size_t a_sign, const bigint* b, size_t b_sign) { + int r; + int cmp; + + if(a_sign == b_sign) { + if( (r = bigint_copy(c, a)) != 0) return r; + if( (r = bigint_add_unsigned(c, b)) != 0) return r; + c->sign = a_sign; + return 0; + } + + cmp = bigint_cmp_abs(a,b); + + if(cmp == 0) { + /* adding positive and negative numbers with the same + * value, short-circuit to returning zero */ + c->size = 0; + c->sign = 0; + return 0; + } + + if(cmp > 0) { + if( (r = bigint_copy(c, a)) != 0) return r; + if( (r = bigint_sub_unsigned(c, b)) != 0) return r; + c->sign = a_sign; + return 0; + } + + if( (r = bigint_copy(c, b)) != 0) return r; + if( (r = bigint_sub_unsigned(c, a)) != 0) return r; + c->sign = b_sign; + return 0; +} + +BIGINT_API +int bigint_add(bigint* c, const bigint* a, const bigint* b) { + int r; + bigint tmp = BIGINT_INIT; + + if( (r = bigint_copy(&tmp,c)) != 0) return r; + if( (r = bigint_add_signed(&tmp, a, a->sign, b, b->sign)) != 0) goto cleanup; + if( (r = bigint_copy(c,&tmp)) != 0) goto cleanup; + + cleanup: + bigint_free(&tmp); + return r; +} + +BIGINT_API +int bigint_sub(bigint* c, const bigint* a, const bigint* b) { + int r; + bigint tmp = BIGINT_INIT; + + if( (r = bigint_copy(&tmp,c)) != 0) return r; + if( (r = bigint_add_signed(&tmp, a, a->sign, b, !b->sign)) != 0) goto cleanup; + if( (r = bigint_copy(c,&tmp)) != 0) goto cleanup; + + cleanup: + bigint_free(&tmp); + return r; +} + +BIGINT_API +int bigint_mul(bigint* c, const bigint* a, const bigint* b) { + int r; + bigint tmp = BIGINT_INIT; + + if( (r = bigint_copy(&tmp,c)) != 0) return r; + if( (r = bigint_mul_long(&tmp, a, b)) != 0) goto cleanup; + if( (r = bigint_copy(c,&tmp)) != 0) goto cleanup; + + cleanup: + bigint_free(&tmp); + return r; +} + +BIGINT_API +void bigint_div_mod_word(bigint* numerator, bigint_word* remainder, bigint_word denominator) { + size_t i; + size_t j; + + bigint_word dst_word; + bigint_word src_word; + bigint_word parts[2]; + bigint_word r = 0; + + bigint_word div_word; + bigint_word mod_word; + + i = numerator->size; + + while(i-- > 0) { + dst_word = 0; + src_word = numerator->words[i]; + + parts[0] = src_word >> BIGINT_HALF_WORD_BIT; + parts[1] = src_word & BIGINT_HALF_WORD_MASK; + + for(j=0;j<2;j++) { + r <<= BIGINT_HALF_WORD_BIT; + r |= parts[j]; + + div_word = r / denominator; + mod_word = r % denominator; + r = mod_word; + + dst_word <<= BIGINT_HALF_WORD_BIT; + dst_word |= div_word; + } + + numerator->words[i] = dst_word; + } + + bigint_truncate(numerator); + *remainder = r; +} + +/* div_mod_word hard-coded to some value to allow compiler-time optimizations */ +#define BIGINT_DIV_MOD_WORD(x) \ +static inline \ +void bigint_div_mod_ ## x (bigint* numerator, bigint_word* remainder) { \ + size_t i; \ + size_t j; \ + bigint_word dst_word; \ + bigint_word src_word; \ + bigint_word parts[2]; \ + bigint_word r = 0; \ + bigint_word div_word; \ + bigint_word mod_word; \ + i = numerator->size; \ + while(i-- > 0) { \ + dst_word = 0; \ + src_word = numerator->words[i]; \ + parts[0] = src_word >> BIGINT_HALF_WORD_BIT; \ + parts[1] = src_word & BIGINT_HALF_WORD_MASK; \ + for(j=0;j<2;j++) { \ + r <<= BIGINT_HALF_WORD_BIT; \ + r |= parts[j]; \ + div_word = r / x; \ + mod_word = r % x; \ + r = mod_word; \ + dst_word <<= BIGINT_HALF_WORD_BIT; \ + dst_word |= div_word; \ + } \ + numerator->words[i] = dst_word; \ + } \ + bigint_truncate(numerator); \ + *remainder = r; \ +} + +BIGINT_DIV_MOD_WORD(10) + +BIGINT_API +int bigint_div_mod(bigint* quotient, bigint* remainder, const bigint* numerator, const bigint* denominator) { + int r; + size_t n; + + bigint quo = BIGINT_INIT; + bigint rem = BIGINT_INIT; + bigint den = BIGINT_INIT; + + if( (r = bigint_copy(&rem, numerator)) != 0) goto cleanup; + if( (r = bigint_copy(&den, denominator)) != 0) goto cleanup; + + if(bigint_cmp_abs(&rem,denominator) >= 0) { + n = bigint_bitlength(numerator) - bigint_bitlength(denominator); + if( (r = bigint_lshift(&den, denominator, n)) != 0) goto cleanup; + n++; /* ensure we have at least 1 iteration */ + while(n--) { + if(bigint_cmp_abs(&rem,&den) >= 0) { + if( (r = bigint_sub_unsigned(&rem, &den)) != 0) goto cleanup; + if( (r = bigint_set_bit(&quo, (size_t)n, 1)) != 0) goto cleanup; + } + if( (r = bigint_rshift_overwrite(&den,1)) != 0) goto cleanup; + } + } + + quo.sign = numerator->sign ^ denominator->sign; + rem.sign = numerator->sign; + + if( (r = bigint_copy(quotient, &quo)) != 0) goto cleanup; + if( (r = bigint_copy(remainder, &rem)) != 0) goto cleanup; + + cleanup: + bigint_free(&quo); + bigint_free(&rem); + bigint_free(&den); + return r; +} + +static const bigint_word BIGINT_ONE_VALUE = 1; +#ifdef BIGINT_NO_MALLOC +static const bigint BIGINT_ONE_STORAGE = { .words[0] = BIGINT_ONE_VALUE, .size = 1, .alloc = 0, .sign = 0, .limit = BIGINT_DEFAULT_LIMIT }; +#else +static const bigint BIGINT_ONE_STORAGE = { .words = (bigint_word*)&BIGINT_ONE_VALUE, .size = 1, .alloc = 0, .sign = 0, .limit = BIGINT_DEFAULT_LIMIT }; +#endif +static const bigint* BIGINT_ONE = &BIGINT_ONE_STORAGE; + +BIGINT_API +int bigint_inc(bigint* c, const bigint *a) { + return bigint_add(c, a, BIGINT_ONE); +} + +BIGINT_API +int bigint_dec(bigint* c, const bigint *a) { + return bigint_sub(c, a, BIGINT_ONE); +} + +/* we don't take the sign into account on these functions, + * that's handled in the bigint_to_string function */ +static size_t bigint_len_string_base16(const bigint* b) { + size_t blen = bigint_bitlength(b); + + return (blen >> 2) + (!!(blen & 0x03)); +} + +/* pre-computed 1.0 / log2(10.0); */ +static const double base10_scale = 0x1.34413509f79ffp-2; +static size_t bigint_len_string_base10(const bigint* b) { + size_t blen = (size_t)(((double)bigint_bitlength(b)) * base10_scale); + + return ++blen; +} + +static size_t bigint_len_string_base8(const bigint* b) { + size_t blen = bigint_bitlength(b); + + return (blen / 3) + (!!(blen % 3)); +} + +static size_t bigint_len_string_base2(const bigint* b) { + return bigint_bitlength(b); +} + +static const char* const bigint_alphabet = "0123456789abcdef"; + +BIGINT_NONNULL1 +static size_t bigint_to_string_base16(char* str, size_t len, bigint* b) { + size_t u = 0; + + if(b->size == 0) { + str[0] = '0'; + return 1; + } + + while( (u = bigint_len_string_base16(b)) > len) { + bigint_rshift_overwrite_small_4(b); + } + len = u; + + while(b->size) { + str[--u] = bigint_alphabet[b->words[0] & 0x0F]; + bigint_rshift_overwrite_small_4(b); + } + + return len; +} + +BIGINT_NONNULL1 +static size_t bigint_to_string_base10(char* str, size_t len, bigint* b) { + bigint_word rem; + size_t u = 0; + + if(b->size == 0) { + str[0] = '0'; + return 1; + } + + while( (u = bigint_len_string_base10(b)) > len) { + bigint_div_mod_10(b,&rem); + } + len = u; + + while(b->size) { + bigint_div_mod_10(b,&rem); + str[--u] = bigint_alphabet[rem]; + } + + if(u) { + memmove(&str[0],&str[u],len-u); + len -= u; + } + + return len; +} + +BIGINT_NONNULL1 +static size_t bigint_to_string_base8(char* str, size_t len, bigint* b) { + size_t u = 0; + + if(b->size == 0) { + str[0] = '0'; + return 1; + } + + while( (u = bigint_len_string_base8(b)) > len) { + bigint_rshift_overwrite_small_3(b); + } + len = u; + + while(b->size) { + str[--u] = bigint_alphabet[b->words[0] & 0x07]; + bigint_rshift_overwrite_small_3(b); + } + + return len; +} + +BIGINT_NONNULL1 +static size_t bigint_to_string_base2(char* str, size_t len, bigint* b) { + size_t u = 0; + + if(b->size == 0) { + str[0] = '0'; + return 1; + } + + while( (u = bigint_bitlength(b)) > len) { + bigint_rshift_overwrite_small_1(b); + } + len = u; + + while(b->size) { + str[--u] = bigint_alphabet[b->words[0] & 0x01]; + bigint_rshift_overwrite_small_1(b); + } + + return len; +} + +BIGINT_API +size_t bigint_to_string(char* str, size_t len, const bigint* b, unsigned int base) { + bigint tmp = BIGINT_INIT; + int r; + size_t u = 0; + size_t res = 0; + + if(str == NULL) { + switch(base) { + case 2: return 2 + b->sign + bigint_len_string_base2(b); + case 8: return 1 + b->sign + bigint_len_string_base8(b); + case 0: /* fall-through */ + case 10: return b->sign + bigint_len_string_base10(b); + case 16: return 2 + b->sign + bigint_len_string_base16(b); + default: break; + } + return 0; + } + + if(len == 0) return 0; + + if(b->sign) { + *str = '-'; + str++; + len--; + u++; + } + if(len == 0) return u; + + if(base == 2) { + *str = '0'; + str++; + len--; + u++; + if(len == 0) return u; + *str = 'b'; + str++; + len--; + u++; + } else if(base == 8) { + *str = '0'; + str++; + len--; + u++; + } else if(base == 16) { + *str = '0'; + str++; + len--; + u++; + if(len == 0) return u; + *str = 'x'; + str++; + len--; + u++; + } + if(len == 0) return u; + + /* TODO can this copy be avoided? */ + if( (r = bigint_copy(&tmp,b)) != 0) return 0; + switch(base) { + case 2: res = bigint_to_string_base2(str,len,&tmp); break; + case 8: res = bigint_to_string_base8(str,len,&tmp); break; + case 0: /* fall-through */ + case 10: res = bigint_to_string_base10(str,len,&tmp); break; + case 16: res = bigint_to_string_base16(str,len,&tmp); break; + default: break; + } + bigint_free(&tmp); + + if(res != 0) res += u; + return res; +} + +#endif diff --git a/utest.c b/utest.c new file mode 100644 index 0000000..a8d7d07 --- /dev/null +++ b/utest.c @@ -0,0 +1,2610 @@ +#define BIGINT_IMPLEMENTATION +#include "bigint.h" +#include "utest.h" + +#include +#include +#include + +#define BIG_STRING_HEX "80000000002f2a884000000000235fe63" +#define BIG_STRING_DEC "2722258935368420033567525629681967955555" +#define BIG_STRING_OCT "40000000000000571250410000000000000215377143" +#define BIG_STRING_BIN "100000000000000000000000000000000000000000101111001010101000100001000000000000000000000000000000000000000010001101011111111001100011" + +#define BIG_STRING_HEX_PREFIX "0x80000000002f2a884000000000235fe63" +#define BIG_STRING_OCT_PREFIX "040000000000000571250410000000000000215377143" +#define BIG_STRING_BIN_PREFIX "0b100000000000000000000000000000000000000000101111001010101000100001000000000000000000000000000000000000000010001101011111111001100011" + +#define BIG_STRING_HEX_PREFIX_ALT "x80000000002f2a884000000000235fe63" +#define BIG_STRING_BIN_PREFIX_ALT "b100000000000000000000000000000000000000000101111001010101000100001000000000000000000000000000000000000000010001101011111111001100011" + +#define BIG_STRING_HEX_NEGATIVE "-80000000002f2a884000000000235fe63" +#define BIG_STRING_DEC_NEGATIVE "-2722258935368420033567525629681967955555" +#define BIG_STRING_OCT_NEGATIVE "-40000000000000571250410000000000000215377143" +#define BIG_STRING_BIN_NEGATIVE "-100000000000000000000000000000000000000000101111001010101000100001000000000000000000000000000000000000000010001101011111111001100011" + +#define BIG_STRING_HEX_NEGATIVE_PREFIX "-0x80000000002f2a884000000000235fe63" +#define BIG_STRING_OCT_NEGATIVE_PREFIX "-040000000000000571250410000000000000215377143" +#define BIG_STRING_BIN_NEGATIVE_PREFIX "-0b100000000000000000000000000000000000000000101111001010101000100001000000000000000000000000000000000000000010001101011111111001100011" + +#define BIG_STRING_HEX_TRUNC "80000000002f2a884000000000235" +#define BIG_STRING_DEC_TRUNC "272225893536842003356752562968196795" +#define BIG_STRING_OCT_TRUNC "4000000000000057125041000000000000021537" +#define BIG_STRING_BIN_TRUNC "10000000000000000000000000000000000000000010111100101010100010000100000000000000000000000000000000000000001000110101111111100110" + +#define ASSERT_WEQ(a,b) ASSERT_EQ((bigint_word)a,(bigint_word)b) + +#if BIGINT_WORD_WIDTH == 1 + static uint8_t BIG_STRING_B00 = 0x63; + static uint8_t BIG_STRING_B01 = 0xfe; + static uint8_t BIG_STRING_B02 = 0x35; + static uint8_t BIG_STRING_B03 = 0x02; + static uint8_t BIG_STRING_B04 = 0x00; + static uint8_t BIG_STRING_B05 = 0x00; + static uint8_t BIG_STRING_B06 = 0x00; + static uint8_t BIG_STRING_B07 = 0x00; + static uint8_t BIG_STRING_B08 = 0x84; + static uint8_t BIG_STRING_B09 = 0xa8; + static uint8_t BIG_STRING_B10 = 0xf2; + static uint8_t BIG_STRING_B11 = 0x02; + static uint8_t BIG_STRING_B12 = 0x00; + static uint8_t BIG_STRING_B13 = 0x00; + static uint8_t BIG_STRING_B14 = 0x00; + static uint8_t BIG_STRING_B15 = 0x00; + static uint8_t BIG_STRING_B16 = 0x08; + + +#define INIT_BIGSTRING(x) \ + ASSERT_EQ(bigint_resize(&(x),17),0); \ + x.words[0] = BIG_STRING_B00; \ + x.words[1] = BIG_STRING_B01; \ + x.words[2] = BIG_STRING_B02; \ + x.words[3] = BIG_STRING_B03; \ + x.words[4] = BIG_STRING_B04; \ + x.words[5] = BIG_STRING_B05; \ + x.words[6] = BIG_STRING_B06; \ + x.words[7] = BIG_STRING_B07; \ + x.words[8] = BIG_STRING_B08; \ + x.words[9] = BIG_STRING_B09; \ + x.words[10] = BIG_STRING_B10; \ + x.words[11] = BIG_STRING_B11; \ + x.words[12] = BIG_STRING_B12; \ + x.words[13] = BIG_STRING_B13; \ + x.words[14] = BIG_STRING_B14; \ + x.words[15] = BIG_STRING_B15; \ + x.words[16] = BIG_STRING_B16; + +#define ASSERT_BIGSTRING(x) \ + ASSERT_WEQ(a.size,17); \ + ASSERT_WEQ(x.words[0],BIG_STRING_B00); \ + ASSERT_WEQ(x.words[1],BIG_STRING_B01); \ + ASSERT_WEQ(x.words[2],BIG_STRING_B02); \ + ASSERT_WEQ(x.words[3],BIG_STRING_B03); \ + ASSERT_WEQ(x.words[4],BIG_STRING_B04); \ + ASSERT_WEQ(x.words[5],BIG_STRING_B05); \ + ASSERT_WEQ(x.words[6],BIG_STRING_B06); \ + ASSERT_WEQ(x.words[7],BIG_STRING_B07); \ + ASSERT_WEQ(x.words[8],BIG_STRING_B08); \ + ASSERT_WEQ(x.words[9],BIG_STRING_B09); \ + ASSERT_WEQ(x.words[10],BIG_STRING_B10); \ + ASSERT_WEQ(x.words[11],BIG_STRING_B11); \ + ASSERT_WEQ(x.words[12],BIG_STRING_B12); \ + ASSERT_WEQ(x.words[13],BIG_STRING_B13); \ + ASSERT_WEQ(x.words[14],BIG_STRING_B14); \ + ASSERT_WEQ(x.words[15],BIG_STRING_B15); \ + ASSERT_WEQ(x.words[16],BIG_STRING_B16); +#elif BIGINT_WORD_WIDTH == 2 + static uint16_t BIG_STRING_S0 = 0xfe63; + static uint16_t BIG_STRING_S1 = 0x0235; + static uint16_t BIG_STRING_S2 = 0x0000; + static uint16_t BIG_STRING_S3 = 0x0000; + static uint16_t BIG_STRING_S4 = 0xa884; + static uint16_t BIG_STRING_S5 = 0x02f2; + static uint16_t BIG_STRING_S6 = 0x0000; + static uint16_t BIG_STRING_S7 = 0x0000; + static uint16_t BIG_STRING_S8 = 0x0008; + +#define INIT_BIGSTRING(x) \ + ASSERT_EQ(bigint_resize(&(x),9),0); \ + x.words[0] = BIG_STRING_S0; \ + x.words[1] = BIG_STRING_S1; \ + x.words[2] = BIG_STRING_S2; \ + x.words[3] = BIG_STRING_S3; \ + x.words[4] = BIG_STRING_S4; \ + x.words[5] = BIG_STRING_S5; \ + x.words[6] = BIG_STRING_S6; \ + x.words[7] = BIG_STRING_S7; \ + x.words[8] = BIG_STRING_S8; + +#define ASSERT_BIGSTRING(x) \ + ASSERT_WEQ(a.size,9); \ + ASSERT_WEQ(x.words[0],BIG_STRING_S0); \ + ASSERT_WEQ(x.words[1],BIG_STRING_S1); \ + ASSERT_WEQ(x.words[2],BIG_STRING_S2); \ + ASSERT_WEQ(x.words[3],BIG_STRING_S3); \ + ASSERT_WEQ(x.words[4],BIG_STRING_S4); \ + ASSERT_WEQ(x.words[5],BIG_STRING_S5); \ + ASSERT_WEQ(x.words[6],BIG_STRING_S6); \ + ASSERT_WEQ(x.words[7],BIG_STRING_S7); \ + ASSERT_WEQ(x.words[8],BIG_STRING_S8); +#elif BIGINT_WORD_WIDTH == 4 + + static uint32_t BIG_STRING_W0 = 0x0235fe63; + static uint32_t BIG_STRING_W1 = 0x00000000; + static uint32_t BIG_STRING_W2 = 0x02f2a884; + static uint32_t BIG_STRING_W3 = 0x00000000; + static uint32_t BIG_STRING_W4 = 0x00000008; + +#define INIT_BIGSTRING(x) \ + ASSERT_EQ(bigint_resize(&(x),5),0); \ + x.words[0] = BIG_STRING_W0; \ + x.words[1] = BIG_STRING_W1; \ + x.words[2] = BIG_STRING_W2; \ + x.words[3] = BIG_STRING_W3; \ + x.words[4] = BIG_STRING_W4; + +#define ASSERT_BIGSTRING(x) \ + ASSERT_WEQ(a.size,5); \ + ASSERT_WEQ(x.words[0],BIG_STRING_W0); \ + ASSERT_WEQ(x.words[1],BIG_STRING_W1); \ + ASSERT_WEQ(x.words[2],BIG_STRING_W2); \ + ASSERT_WEQ(x.words[3],BIG_STRING_W3); \ + ASSERT_WEQ(x.words[4],BIG_STRING_W4); +#elif BIGINT_WORD_WIDTH == 8 + + static uint64_t BIG_STRING_Q0 = 0x000000000235fe63; + static uint64_t BIG_STRING_Q1 = 0x0000000002f2a884; + static uint64_t BIG_STRING_Q2 = 0x0000000000000008; + +#define INIT_BIGSTRING(x) \ + ASSERT_EQ(bigint_resize(&(x),3),0); \ + x.words[0] = BIG_STRING_Q0; \ + x.words[1] = BIG_STRING_Q1; \ + x.words[2] = BIG_STRING_Q2; + +#define ASSERT_BIGSTRING(x) \ + ASSERT_WEQ(a.size,3); \ + ASSERT_WEQ(x.words[0],BIG_STRING_Q0); \ + ASSERT_WEQ(x.words[1],BIG_STRING_Q1); \ + ASSERT_WEQ(x.words[2],BIG_STRING_Q2); +#else +#error unknownwidth +#endif + +#define PREAMBLE \ + bigint a = BIGINT_INIT; \ + bigint b = BIGINT_INIT; \ + bigint c = BIGINT_INIT; \ + bigint d = BIGINT_INIT; + +#define CLEANUP \ + bigint_free(&a); \ + bigint_free(&b); \ + bigint_free(&c); \ + bigint_free(&d); + +void dump_uint32_spaced(uint32_t val, int first) { + uint32_t hi, lo; + hi = val >> 16; + lo = val & 0xFFFF; + if(first) { + if(hi) { + printf("%x%04x", hi, lo); + } else if(lo) { + printf("%x",lo); + } + } else { + printf("%04x%04x", hi, lo); + } +} + +void dump_bigint(const bigint* b) { + size_t i; + printf("bits: %lu, sign: %lu, size: %lu, alloc: %lu, value: ", + bigint_bitlength(b),b->sign,b->size,b->alloc); + i = b->size; + if(i == 0) { + printf("empty"); + } else { + printf("0x"); + while(i--) { + dump_uint32_spaced(b->words[i], i == b->size - 1); + } + } + printf("\n"); + fflush(stdout); +} + + +UTEST(bigint,size) { +#if BIGINT_WORD_WIDTH == 1 + ASSERT_EQ(sizeof(bigint_word),sizeof(uint8_t)); +#elif BIGINT_WORD_WIDTH == 2 + ASSERT_EQ(sizeof(bigint_word),sizeof(uint16_t)); +#elif BIGINT_WORD_WIDTH == 4 + ASSERT_EQ(sizeof(bigint_word),sizeof(uint32_t)); +#elif BIGINT_WORD_WIDTH == 8 + ASSERT_EQ(sizeof(bigint_word),sizeof(uint64_t)); +#else + abort(); +#endif +} + +UTEST(bigint,init) { + bigint a; + bigint_init(&a); +#ifndef BIGINT_NO_MALLOC + ASSERT_EQ(a.words,NULL); +#endif + ASSERT_EQ(a.size,(size_t)0); + ASSERT_EQ(a.alloc,(size_t)0); + + bigint_free(&a); +} + +UTEST(bigint,limit) { + bigint a; + bigint_init(&a); + + a.limit = 8; + ASSERT_EQ(bigint_resize(&a,9),BIGINT_ELIMIT); + + bigint_free(&a); +} + +UTEST(bigint,from_string_base2) { + bigint a = BIGINT_INIT; + + ASSERT_EQ(bigint_from_string_base2(&a,"garbage",7),BIGINT_EINVAL); + ASSERT_EQ(bigint_from_string_base2(&a,BIG_STRING_BIN,strlen(BIG_STRING_BIN)),0); + ASSERT_BIGSTRING(a); + + bigint_free(&a); +} + +UTEST(bigint,from_string_base8) { + bigint a = BIGINT_INIT; + + ASSERT_EQ(bigint_from_string_base8(&a,"garbage",7),BIGINT_EINVAL); + ASSERT_EQ(bigint_from_string_base8(&a,BIG_STRING_OCT,strlen(BIG_STRING_OCT)),0); + ASSERT_BIGSTRING(a); + + bigint_free(&a); +} + +UTEST(bigint,from_string_base10) { + bigint a = BIGINT_INIT; + + ASSERT_EQ(bigint_from_string_base10(&a,"garbage",7),BIGINT_EINVAL); + ASSERT_EQ(bigint_from_string_base10(&a,BIG_STRING_DEC,strlen(BIG_STRING_DEC)),0); + ASSERT_BIGSTRING(a); + + bigint_free(&a); +} + +UTEST(bigint,from_string_base16) { + bigint a = BIGINT_INIT; + + ASSERT_EQ(bigint_from_string_base16(&a,"garbage",7),BIGINT_EINVAL); + ASSERT_EQ(bigint_from_string_base16(&a,BIG_STRING_HEX,strlen(BIG_STRING_HEX)),0); + ASSERT_BIGSTRING(a); + + bigint_free(&a); +} + +UTEST(bigint,from_string_einval) { + bigint a = BIGINT_INIT; + + ASSERT_EQ(bigint_from_string(&a,"garbage",7,0),BIGINT_EINVAL); + ASSERT_EQ(bigint_from_string(&a,"garbage",7,2),BIGINT_EINVAL); + ASSERT_EQ(bigint_from_string(&a,"garbage",7,8),BIGINT_EINVAL); + ASSERT_EQ(bigint_from_string(&a,"garbage",7,16),BIGINT_EINVAL); + ASSERT_EQ(bigint_from_string(&a,"garbage",7,1),BIGINT_EINVAL); + + ASSERT_EQ(bigint_from_cstring(&a,"garbage",0),BIGINT_EINVAL); + ASSERT_EQ(bigint_from_cstring(&a,"garbage",2),BIGINT_EINVAL); + ASSERT_EQ(bigint_from_cstring(&a,"garbage",8),BIGINT_EINVAL); + ASSERT_EQ(bigint_from_cstring(&a,"garbage",16),BIGINT_EINVAL); + ASSERT_EQ(bigint_from_cstring(&a,"garbage",1),BIGINT_EINVAL); + + ASSERT_EQ(bigint_from_string(&a,BIG_STRING_BIN_PREFIX,strlen(BIG_STRING_BIN_PREFIX),8),BIGINT_EINVAL); + ASSERT_EQ(bigint_from_string(&a,BIG_STRING_BIN_PREFIX,strlen(BIG_STRING_BIN_PREFIX),10),BIGINT_EINVAL); + + ASSERT_EQ(bigint_from_string(&a,BIG_STRING_OCT_PREFIX,strlen(BIG_STRING_OCT_PREFIX),2),BIGINT_EINVAL); + + ASSERT_EQ(bigint_from_string(&a,BIG_STRING_HEX_PREFIX,strlen(BIG_STRING_HEX_PREFIX),2),BIGINT_EINVAL); + ASSERT_EQ(bigint_from_string(&a,BIG_STRING_HEX_PREFIX,strlen(BIG_STRING_HEX_PREFIX),8),BIGINT_EINVAL); + ASSERT_EQ(bigint_from_string(&a,BIG_STRING_HEX_PREFIX,strlen(BIG_STRING_HEX_PREFIX),10),BIGINT_EINVAL); + + ASSERT_EQ(bigint_from_string(&a,"0x",2,16),BIGINT_EINVAL); + + bigint_free(&a); +} + +UTEST(bigint,from_string_zero) { + bigint a = BIGINT_INIT; + + ASSERT_EQ(bigint_from_string(&a,"0",1,0),0); + ASSERT_WEQ(a.size,0); + + ASSERT_EQ(bigint_from_string(&a,"0",1,2),0); + ASSERT_WEQ(a.size,0); + + ASSERT_EQ(bigint_from_string(&a,"0",1,8),0); + ASSERT_WEQ(a.size,0); + + ASSERT_EQ(bigint_from_string(&a,"0",1,10),0); + ASSERT_WEQ(a.size,0); + + ASSERT_EQ(bigint_from_string(&a,"0",1,16),0); + ASSERT_WEQ(a.size,0); + + ASSERT_EQ(bigint_from_string(&a,"b0",2,0),0); + ASSERT_WEQ(a.size,0); + + ASSERT_EQ(bigint_from_string(&a,"b0",2,2),0); + ASSERT_WEQ(a.size,0); + + ASSERT_EQ(bigint_from_string(&a,"0x0",3,16),0); + ASSERT_WEQ(a.size,0); + + bigint_free(&a); +} + +UTEST(bigint,from_string_2) { + bigint a = BIGINT_INIT; + + ASSERT_EQ(bigint_from_string(&a,BIG_STRING_BIN,strlen(BIG_STRING_BIN),2),0); + ASSERT_BIGSTRING(a); + + ASSERT_EQ(bigint_from_string(&a,BIG_STRING_BIN_PREFIX,strlen(BIG_STRING_BIN_PREFIX),2),0); + ASSERT_BIGSTRING(a); + + ASSERT_EQ(bigint_from_string(&a,BIG_STRING_BIN_PREFIX_ALT,strlen(BIG_STRING_BIN_PREFIX_ALT),2),0); + ASSERT_BIGSTRING(a); + + ASSERT_EQ(bigint_from_string(&a,BIG_STRING_BIN_PREFIX,strlen(BIG_STRING_BIN_PREFIX),0),0); + ASSERT_BIGSTRING(a); + + ASSERT_EQ(bigint_from_string(&a,BIG_STRING_BIN_PREFIX_ALT,strlen(BIG_STRING_BIN_PREFIX_ALT),0),0); + ASSERT_BIGSTRING(a); + + ASSERT_EQ(bigint_from_string(&a,BIG_STRING_BIN_NEGATIVE,strlen(BIG_STRING_BIN_NEGATIVE),2),0); + ASSERT_BIGSTRING(a); + ASSERT_EQ(a.sign,(size_t)1); + + ASSERT_EQ(bigint_from_string(&a,BIG_STRING_BIN_NEGATIVE_PREFIX,strlen(BIG_STRING_BIN_NEGATIVE_PREFIX),0),0); + ASSERT_BIGSTRING(a); + ASSERT_EQ(a.sign,(size_t)1); + + ASSERT_EQ(bigint_from_string(&a,BIG_STRING_BIN_NEGATIVE_PREFIX,strlen(BIG_STRING_BIN_NEGATIVE_PREFIX),2),0); + ASSERT_BIGSTRING(a); + ASSERT_EQ(a.sign,(size_t)1); + + bigint_free(&a); +} + +UTEST(bigint,from_string_8) { + bigint a = BIGINT_INIT; + + ASSERT_EQ(bigint_from_string(&a,BIG_STRING_OCT,strlen(BIG_STRING_OCT),8),0); + ASSERT_BIGSTRING(a); + + ASSERT_EQ(bigint_from_string(&a,BIG_STRING_OCT_PREFIX,strlen(BIG_STRING_OCT_PREFIX),8),0); + ASSERT_BIGSTRING(a); + + ASSERT_EQ(bigint_from_string(&a,BIG_STRING_OCT_PREFIX,strlen(BIG_STRING_OCT_PREFIX),0),0); + ASSERT_BIGSTRING(a); + + ASSERT_EQ(bigint_from_string(&a,BIG_STRING_OCT_NEGATIVE,strlen(BIG_STRING_OCT_NEGATIVE),8),0); + ASSERT_BIGSTRING(a); + ASSERT_EQ(a.sign,(size_t)1); + + ASSERT_EQ(bigint_from_string(&a,BIG_STRING_OCT_NEGATIVE_PREFIX,strlen(BIG_STRING_OCT_NEGATIVE_PREFIX),8),0); + ASSERT_BIGSTRING(a); + ASSERT_EQ(a.sign,(size_t)1); + + ASSERT_EQ(bigint_from_string(&a,BIG_STRING_OCT_NEGATIVE_PREFIX,strlen(BIG_STRING_OCT_NEGATIVE_PREFIX),0),0); + ASSERT_BIGSTRING(a); + ASSERT_EQ(a.sign,(size_t)1); + + bigint_free(&a); +} + +UTEST(bigint,from_string_10) { + bigint a = BIGINT_INIT; + + ASSERT_EQ(bigint_from_string(&a,BIG_STRING_DEC,strlen(BIG_STRING_DEC),10),0); + ASSERT_BIGSTRING(a); + + ASSERT_EQ(bigint_from_string(&a,BIG_STRING_DEC,strlen(BIG_STRING_DEC),0),0); + ASSERT_BIGSTRING(a); + + ASSERT_EQ(bigint_from_string(&a,BIG_STRING_DEC_NEGATIVE,strlen(BIG_STRING_DEC_NEGATIVE),10),0); + ASSERT_BIGSTRING(a); + ASSERT_EQ(a.sign,(size_t)1); + + ASSERT_EQ(bigint_from_string(&a,BIG_STRING_DEC_NEGATIVE,strlen(BIG_STRING_DEC_NEGATIVE),0),0); + ASSERT_BIGSTRING(a); + ASSERT_EQ(a.sign,(size_t)1); + + bigint_free(&a); +} + +UTEST(bigint,from_string_16) { + bigint a = BIGINT_INIT; + + ASSERT_EQ(bigint_from_string(&a,BIG_STRING_HEX,strlen(BIG_STRING_HEX),16),0); + ASSERT_BIGSTRING(a); + + ASSERT_EQ(bigint_from_string(&a,BIG_STRING_HEX_NEGATIVE,strlen(BIG_STRING_HEX_NEGATIVE),16),0); + ASSERT_BIGSTRING(a); + ASSERT_EQ(a.sign,(size_t)1); + + ASSERT_EQ(bigint_from_string(&a,BIG_STRING_HEX_PREFIX,strlen(BIG_STRING_HEX_PREFIX),16),0); + ASSERT_BIGSTRING(a); + + ASSERT_EQ(bigint_from_string(&a,BIG_STRING_HEX_PREFIX_ALT,strlen(BIG_STRING_HEX_PREFIX_ALT),16),0); + ASSERT_BIGSTRING(a); + + ASSERT_EQ(bigint_from_string(&a,BIG_STRING_HEX_PREFIX,strlen(BIG_STRING_HEX_PREFIX),0),0); + ASSERT_BIGSTRING(a); + + ASSERT_EQ(bigint_from_string(&a,BIG_STRING_HEX_PREFIX_ALT,strlen(BIG_STRING_HEX_PREFIX_ALT),0),0); + ASSERT_BIGSTRING(a); + + ASSERT_EQ(bigint_from_string(&a,BIG_STRING_HEX_NEGATIVE_PREFIX,strlen(BIG_STRING_HEX_NEGATIVE_PREFIX),16),0); + ASSERT_BIGSTRING(a); + ASSERT_EQ(a.sign,(size_t)1); + + ASSERT_EQ(bigint_from_string(&a,BIG_STRING_HEX_NEGATIVE_PREFIX,strlen(BIG_STRING_HEX_NEGATIVE_PREFIX),0),0); + ASSERT_BIGSTRING(a); + ASSERT_EQ(a.sign,(size_t)1); + + + bigint_free(&a); +} + +UTEST(bigint,from_strings) { + bigint a = BIGINT_INIT; + + ASSERT_EQ(bigint_from_string_base16(&a,"deadbeefc0123456789",strlen("deadbeefc0123456789")),0); +#if BIGINT_WORD_WIDTH == 1 + ASSERT_WEQ(a.size,10); + ASSERT_WEQ(a.words[0],0x89); + ASSERT_WEQ(a.words[1],0x67); + ASSERT_WEQ(a.words[2],0x45); + ASSERT_WEQ(a.words[3],0x23); + ASSERT_WEQ(a.words[4],0x01); + ASSERT_WEQ(a.words[5],0xfc); + ASSERT_WEQ(a.words[6],0xee); + ASSERT_WEQ(a.words[7],0xdb); + ASSERT_WEQ(a.words[8],0xea); + ASSERT_WEQ(a.words[9],0x0d); +#elif BIGINT_WORD_WIDTH == 2 + ASSERT_WEQ(a.size,5); + ASSERT_WEQ(a.words[0],0x6789); + ASSERT_WEQ(a.words[1],0x2345); + ASSERT_WEQ(a.words[2],0xfc01); + ASSERT_WEQ(a.words[3],0xdbee); + ASSERT_WEQ(a.words[4],0x0dea); +#elif BIGINT_WORD_WIDTH == 4 + ASSERT_WEQ(a.size,3); + ASSERT_WEQ(a.words[0],0x23456789); + ASSERT_WEQ(a.words[1],0xdbeefc01); + ASSERT_WEQ(a.words[2],0x00000dea); +#elif BIGINT_WORD_WIDTH == 8 + ASSERT_WEQ(a.size,2); + ASSERT_WEQ(a.words[0],0xdbeefc0123456789); + ASSERT_WEQ(a.words[1],0x0000000000000dea); +#endif + + a.size = 0; + ASSERT_EQ(bigint_from_string_base16(&a,"DEADBEEFC0123456789",strlen("DEADBEEFC0123456789")),0); +#if BIGINT_WORD_WIDTH == 1 + ASSERT_WEQ(a.size,10); + ASSERT_WEQ(a.words[0],0x89); + ASSERT_WEQ(a.words[1],0x67); + ASSERT_WEQ(a.words[2],0x45); + ASSERT_WEQ(a.words[3],0x23); + ASSERT_WEQ(a.words[4],0x01); + ASSERT_WEQ(a.words[5],0xfc); + ASSERT_WEQ(a.words[6],0xee); + ASSERT_WEQ(a.words[7],0xdb); + ASSERT_WEQ(a.words[8],0xea); + ASSERT_WEQ(a.words[9],0x0d); +#elif BIGINT_WORD_WIDTH == 2 + ASSERT_WEQ(a.size,5); + ASSERT_WEQ(a.words[0],0x6789); + ASSERT_WEQ(a.words[1],0x2345); + ASSERT_WEQ(a.words[2],0xfc01); + ASSERT_WEQ(a.words[3],0xdbee); + ASSERT_WEQ(a.words[4],0x0dea); +#elif BIGINT_WORD_WIDTH == 4 + ASSERT_WEQ(a.size,3); + ASSERT_WEQ(a.words[0],0x23456789); + ASSERT_WEQ(a.words[1],0xdbeefc01); + ASSERT_WEQ(a.words[2],0x00000dea); +#elif BIGINT_WORD_WIDTH == 8 + ASSERT_WEQ(a.size,2); + ASSERT_WEQ(a.words[0],0xdbeefc0123456789); + ASSERT_WEQ(a.words[1],0x0000000000000dea); +#endif + + a.size = 0; + ASSERT_EQ(bigint_from_string_base8(&a,"07123456123456712",strlen("07123456123456712")),0); +#if BIGINT_WORD_WIDTH == 1 + ASSERT_WEQ(a.size,6); + ASSERT_WEQ(a.words[0],0xca); + ASSERT_WEQ(a.words[1],0x5d); + ASSERT_WEQ(a.words[2],0x4e); + ASSERT_WEQ(a.words[3],0x71); + ASSERT_WEQ(a.words[4],0x39); + ASSERT_WEQ(a.words[5],0xe5); +#elif BIGINT_WORD_WIDTH == 2 + ASSERT_WEQ(a.size,3); + ASSERT_WEQ(a.words[0],0x5dca); + ASSERT_WEQ(a.words[1],0x714e); + ASSERT_WEQ(a.words[2],0xe539); +#elif BIGINT_WORD_WIDTH == 4 + ASSERT_WEQ(a.size,2); + ASSERT_WEQ(a.words[0],0x714e5dca); + ASSERT_WEQ(a.words[1],0x0000e539); +#elif BIGINT_WORD_WIDTH == 8 + ASSERT_WEQ(a.size,1); + ASSERT_WEQ(a.words[0],0x0000e539714e5dca); +#endif + + bigint_free(&a); +} + +UTEST(bigint,to_strings) { + char buffer[100]; + bigint a = BIGINT_INIT; + +#if BIGINT_WORD_WIDTH == 1 + ASSERT_EQ(bigint_resize(&a,10),0); + a.words[0] = 0x89; + a.words[1] = 0x67; + a.words[2] = 0x45; + a.words[3] = 0x23; + a.words[4] = 0x01; + a.words[5] = 0xfc; + a.words[6] = 0xee; + a.words[7] = 0xdb; + a.words[8] = 0xea; + a.words[9] = 0x0d; +#elif BIGINT_WORD_WIDTH == 2 + ASSERT_EQ(bigint_resize(&a,5),0); + a.words[0] = 0x6789; + a.words[1] = 0x2345; + a.words[2] = 0xfc01; + a.words[3] = 0xdbee; + a.words[4] = 0x0dea; +#elif BIGINT_WORD_WIDTH == 4 + ASSERT_EQ(bigint_resize(&a,3),0); + a.words[0] = 0x23456789; + a.words[1] = 0xdbeefc01; + a.words[2] = 0x00000dea; +#elif BIGINT_WORD_WIDTH == 8 + ASSERT_EQ(bigint_resize(&a,2),0); + a.words[0] = 0xdbeefc0123456789L; + a.words[1] = 0x0000000000000deaL; +#endif + + ASSERT_WEQ(bigint_to_string_base16(buffer,100,&a),19); + + buffer[19] = '\0'; + ASSERT_STREQ(buffer,"deadbeefc0123456789"); + + bigint_free(&a); +} + +UTEST(bigint_zero,to_string_base16) { + char buffer[2]; + + bigint a = BIGINT_INIT; + + ASSERT_WEQ(bigint_to_string_base16(buffer,2,&a),1); + buffer[1] = '\0'; + ASSERT_STREQ(buffer,"0"); +} + +UTEST(bigint,to_string_base16) { + char buffer[34]; + bigint a = BIGINT_INIT; + + INIT_BIGSTRING(a) + + ASSERT_WEQ(bigint_to_string_base16(buffer,34,&a),33); + buffer[33] = '\0'; + ASSERT_STREQ(buffer,BIG_STRING_HEX); + bigint_free(&a); +} + +UTEST(bigint,to_string_base16_trunc) { + char buffer[30]; + bigint a = BIGINT_INIT; + + INIT_BIGSTRING(a) + + ASSERT_WEQ(bigint_to_string_base16(buffer,29,&a),29); + buffer[29] = '\0'; + ASSERT_STREQ(buffer,BIG_STRING_HEX_TRUNC); + bigint_free(&a); +} + +UTEST(bigint,to_string_base16_odd) { + char buffer[35]; + bigint a = BIGINT_INIT; + + INIT_BIGSTRING(a) + + a.words[a.size-1] |= 0x10; + + ASSERT_WEQ(bigint_to_string_base16(buffer,35,&a),34); + bigint_free(&a); +} + +UTEST(bigint,to_string_badbase) { + char buffer[35]; + bigint a = BIGINT_INIT; + + INIT_BIGSTRING(a) + + a.words[a.size-1] |= 0x10; + + ASSERT_WEQ(bigint_to_string(NULL,0,&a,9),0); + ASSERT_WEQ(bigint_to_string(buffer,35,&a,9),0); + + bigint_free(&a); +} + +UTEST(bigint,to_string_base16_null) { + bigint a = BIGINT_INIT; + + INIT_BIGSTRING(a) + + a.words[a.size-1] |= 0x10; + + ASSERT_WEQ(bigint_to_string(NULL,0,&a,16),36); + + bigint_free(&a); +} + +UTEST(bigint,to_string_base16_len) { + bigint a = BIGINT_INIT; + size_t len; + + INIT_BIGSTRING(a) + + len = 2 + (bigint_bitlength(&a) / 4); + + ASSERT_WEQ(bigint_to_string(NULL,0,&a,16),len); + + bigint_free(&a); +} + +UTEST(bigint,positive_to_string_base16) { + char buffer[36]; + bigint a = BIGINT_INIT; + + INIT_BIGSTRING(a) + + ASSERT_WEQ(bigint_to_string(buffer,36,&a,16),35); + buffer[35] = '\0'; + ASSERT_STREQ(buffer, BIG_STRING_HEX_PREFIX); + bigint_free(&a); +} + +UTEST(bigint,negative_to_string_base16) { + char buffer[37]; + bigint a = BIGINT_INIT; + + INIT_BIGSTRING(a) + a.sign = 1; + + ASSERT_WEQ(bigint_to_string(buffer,37,&a,16),36); + buffer[36] = '\0'; + ASSERT_STREQ(buffer,BIG_STRING_HEX_NEGATIVE_PREFIX); +} + +UTEST(bigint_zero,to_string_base10) { + char buffer[2]; + + bigint a = BIGINT_INIT; + + ASSERT_WEQ(bigint_to_string_base10(buffer,2,&a),1); + buffer[1] = '\0'; + ASSERT_STREQ(buffer,"0"); +} + +UTEST(bigint_zero,to_string_base8) { + char buffer[2]; + + bigint a = BIGINT_INIT; + + ASSERT_WEQ(bigint_to_string_base8(buffer,2,&a),1); + buffer[1] = '\0'; + ASSERT_STREQ(buffer,"0"); +} + +UTEST(bigint_zero,to_string_base2) { + char buffer[2]; + + bigint a = BIGINT_INIT; + + ASSERT_WEQ(bigint_to_string_base2(buffer,2,&a),1); + buffer[1] = '\0'; + ASSERT_STREQ(buffer,"0"); +} + +UTEST(bigint,to_string_base10) { + char buffer[41]; + bigint a = BIGINT_INIT; + + INIT_BIGSTRING(a) + + ASSERT_WEQ(bigint_to_string_base10(buffer,41,&a),40); + buffer[40] = '\0'; + ASSERT_STREQ(buffer,BIG_STRING_DEC); + bigint_free(&a); +} + +UTEST(bigint,to_string_base10_trunc) { + char buffer[37]; + bigint a = BIGINT_INIT; + + INIT_BIGSTRING(a) + + ASSERT_WEQ(bigint_to_string_base10(buffer,36,&a),36); + buffer[36] = '\0'; + ASSERT_STREQ(buffer,BIG_STRING_DEC_TRUNC); + bigint_free(&a); +} + +UTEST(bigint,to_string_base10_mem) { + /* the to_length function for base10 will return + * 2, even though the actual tostring only needs 1 */ + char buffer[3]; + bigint a = BIGINT_INIT; + + ASSERT_EQ(bigint_resize(&a,1),0); + a.words[0] = 0x09; + + ASSERT_WEQ(bigint_to_string_base10(buffer,2,&a),1); + buffer[1] = '\0'; + ASSERT_STREQ(buffer,"9"); + bigint_free(&a); +} + +UTEST(bigint,to_string_base10_null) { + bigint a = BIGINT_INIT; + + INIT_BIGSTRING(a) + + ASSERT_WEQ(bigint_to_string(NULL,0,&a,10),40); + bigint_free(&a); +} + +UTEST(bigint,to_string_base10_length) { + bigint a = BIGINT_INIT; + + ASSERT_EQ(bigint_resize(&a,1),0); + + a.words[0] = 0x07; + ASSERT_WEQ(bigint_to_string(NULL,0,&a,10),1); + + a.words[0] = 0x08; + ASSERT_WEQ(bigint_to_string(NULL,0,&a,10),2); + + a.words[0] = 0x40; + ASSERT_WEQ(bigint_to_string(NULL,0,&a,10),3); + + bigint_free(&a); +} + +UTEST(bigint,positive_to_string_base10) { + char buffer[41]; + bigint a = BIGINT_INIT; + + INIT_BIGSTRING(a) + + ASSERT_WEQ(bigint_to_string(buffer,41,&a,10),40); + buffer[40] = '\0'; + ASSERT_STREQ(buffer,BIG_STRING_DEC); + bigint_free(&a); +} + +UTEST(bigint,negative_to_string_base10) { + char buffer[42]; + bigint a = BIGINT_INIT; + + INIT_BIGSTRING(a) + a.sign = 1; + + ASSERT_WEQ(bigint_to_string(buffer,42,&a,10),41); + buffer[41] = '\0'; + ASSERT_STREQ(buffer,BIG_STRING_DEC_NEGATIVE); + bigint_free(&a); +} + +UTEST(bigint,to_string_base8) { + char buffer[45]; + bigint a = BIGINT_INIT; + + INIT_BIGSTRING(a) + + ASSERT_WEQ(bigint_to_string_base8(buffer,45,&a),44); + buffer[44] = '\0'; + ASSERT_STREQ(buffer,BIG_STRING_OCT); + bigint_free(&a); +} + +UTEST(bigint,to_string_base8_trunc) { + char buffer[41]; + bigint a = BIGINT_INIT; + + INIT_BIGSTRING(a) + + ASSERT_WEQ(bigint_to_string_base8(buffer,40,&a),40); + buffer[40] = '\0'; + ASSERT_STREQ(buffer,BIG_STRING_OCT_TRUNC); + bigint_free(&a); +} + +UTEST(bigint,to_string_base8_null) { + bigint a = BIGINT_INIT; + + INIT_BIGSTRING(a) + + ASSERT_WEQ(bigint_to_string(NULL,0,&a,8),45); + bigint_free(&a); +} + +UTEST(bigint,to_string_base8_len) { + bigint a = BIGINT_INIT; + size_t len; + + INIT_BIGSTRING(a) + a.words[a.size - 1] |= 0x10; + + len = 1 + ( (bigint_bitlength(&a) / 3) + (bigint_bitlength(&a) % 3 != 0) ); + + ASSERT_WEQ(bigint_to_string(NULL,0,&a,8),len); + + bigint_free(&a); +} + +UTEST(bigint,to_string_base8_len2) { + bigint a = BIGINT_INIT; + size_t len; + + INIT_BIGSTRING(a) + a.words[a.size - 1] |= 0x30; + + len = 1 + ( (bigint_bitlength(&a) / 3) + (bigint_bitlength(&a) % 3 != 0) ); + + ASSERT_WEQ(bigint_to_string(NULL,0,&a,8),len); + + bigint_free(&a); +} + +UTEST(bigint,to_string_base8_len3) { + bigint a = BIGINT_INIT; + size_t len; + + INIT_BIGSTRING(a) + a.words[a.size - 1] |= 0x70; + + len = 1 + ((bigint_bitlength(&a) / 3) + (bigint_bitlength(&a) % 3 != 0)); + + ASSERT_WEQ(bigint_to_string(NULL,0,&a,8),len); + + bigint_free(&a); +} + +UTEST(bigint,to_string_base8_len4) { + bigint a = BIGINT_INIT; + size_t len; + + INIT_BIGSTRING(a) + a.words[a.size - 1] |= 0x80; + + len = 1 + ((bigint_bitlength(&a) / 3) + (bigint_bitlength(&a) % 3 != 0) ); + + ASSERT_WEQ(bigint_to_string(NULL,0,&a,8),len); + + bigint_free(&a); +} + +UTEST(bigint,positive_to_string_base8) { + char buffer[46]; + bigint a = BIGINT_INIT; + + INIT_BIGSTRING(a) + + ASSERT_WEQ(bigint_to_string(buffer,46,&a,8),45); + buffer[45] = '\0'; + ASSERT_STREQ(buffer,BIG_STRING_OCT_PREFIX); + bigint_free(&a); +} + +UTEST(bigint,negative_to_string_base8) { + char buffer[47]; + bigint a = BIGINT_INIT; + + INIT_BIGSTRING(a) + a.sign = 1; + + ASSERT_WEQ(bigint_to_string(buffer,47,&a,8),46); + buffer[46] = '\0'; + ASSERT_STREQ(buffer,BIG_STRING_OCT_NEGATIVE_PREFIX); + bigint_free(&a); +} + +UTEST(bigint,to_string_base2) { + char buffer[133]; + bigint a = BIGINT_INIT; + + INIT_BIGSTRING(a) + + ASSERT_WEQ(bigint_to_string_base2(buffer,133,&a),132); + buffer[132] = '\0'; + ASSERT_STREQ(buffer,BIG_STRING_BIN); + bigint_free(&a); +} + +UTEST(bigint,to_string_base2_trunc) { + char buffer[129]; + bigint a = BIGINT_INIT; + + INIT_BIGSTRING(a) + + ASSERT_WEQ(bigint_to_string_base2(buffer,128,&a),128); + buffer[128] = '\0'; + ASSERT_STREQ(buffer,BIG_STRING_BIN_TRUNC); + bigint_free(&a); +} + +UTEST(bigint,to_string_base2_null) { + bigint a = BIGINT_INIT; + + INIT_BIGSTRING(a) + + ASSERT_WEQ(bigint_to_string(NULL,0,&a,2),134); + + bigint_free(&a); +} + +UTEST(bigint,to_string_base2_len) { + bigint a = BIGINT_INIT; + size_t len; + + INIT_BIGSTRING(a) + + len = 2 + bigint_bitlength(&a); + + ASSERT_WEQ(bigint_to_string(NULL,0,&a,2),len); + + bigint_free(&a); +} + + +UTEST(bigint,positive_to_string_base2) { + char buffer[135]; + bigint a = BIGINT_INIT; + + INIT_BIGSTRING(a) + + ASSERT_WEQ(bigint_to_string(buffer,135,&a,2),134); + buffer[134] = '\0'; + ASSERT_STREQ(buffer,BIG_STRING_BIN_PREFIX); +} + +UTEST(bigint,negative_to_string_base2) { + char buffer[136]; + bigint a = BIGINT_INIT; + + INIT_BIGSTRING(a); + a.sign = 1; + + ASSERT_WEQ(bigint_to_string(buffer,136,&a,2),135); + buffer[135] = '\0'; + ASSERT_STREQ(buffer,BIG_STRING_BIN_NEGATIVE_PREFIX); +} + +UTEST(bigint,positive_to_string_base0) { + char buffer[41]; + bigint a = BIGINT_INIT; + + INIT_BIGSTRING(a) + + ASSERT_WEQ(bigint_to_string(buffer,41,&a,0),40); + buffer[40] = '\0'; + ASSERT_STREQ(buffer,BIG_STRING_DEC); + bigint_free(&a); +} + +UTEST(bigint,negative_to_string_base0) { + char buffer[42]; + bigint a = BIGINT_INIT; + + INIT_BIGSTRING(a) + a.sign = 1; + + ASSERT_WEQ(bigint_to_string(buffer,42,&a,0),41); + buffer[41] = '\0'; + ASSERT_STREQ(buffer,BIG_STRING_DEC_NEGATIVE); + bigint_free(&a); +} + +UTEST(bigint_word,add) { + bigint_word a, b; + a = (bigint_word)-1; + b = 1; + ASSERT_EQ(bigint_word_add(&a,b),1); + ASSERT_WEQ(a,0); + ASSERT_EQ(bigint_word_add(&a,(bigint_word)-1),0); + ASSERT_WEQ(a,-1); +} + +UTEST(bigint_word,sub) { + bigint_word a, b; + a = 0; + b = 1; + ASSERT_EQ(bigint_word_sub(&a,b),1); + ASSERT_EQ(a,(bigint_word)-1); + ASSERT_EQ(bigint_word_sub(&a,(bigint_word)-1),0); + ASSERT_EQ(a,(bigint_word)0); +} + +UTEST(bigint_word,mul) { + bigint_word a, b, c, carry; + a = (bigint_word)-1; + b = 2; + + c = a << 1; + + carry = bigint_word_mul(&a,b); + ASSERT_WEQ(carry,1); + ASSERT_WEQ(a,c); + + a = (bigint_word)-1; + b = 4; + + c = a << 2; + + carry = bigint_word_mul(&a,b); + ASSERT_WEQ(carry,0x03); + ASSERT_WEQ(a,c); + + a = (bigint_word)-1; + b = (bigint_word)-1; + + carry = bigint_word_mul(&a,b); + ASSERT_WEQ(a,0x00000001UL); + ASSERT_WEQ(carry,-2); +} + +UTEST(bigint_word,bitlength) { + bigint_word a; + + a = (bigint_word)-1; + + ASSERT_WEQ(bigint_word_bitlength(a),sizeof(bigint_word) * CHAR_BIT); + ASSERT_WEQ(bigint_word_bitlength(1),1); +} + +UTEST(bigint,from_word) { + bigint a; + bigint_init(&a); + bigint_from_word(&a,1); + ASSERT_NE(a.words,NULL); + ASSERT_EQ(a.words[0],(bigint_word)1); + ASSERT_EQ(a.size,(size_t)1); + ASSERT_EQ(a.sign,(size_t)0); + + bigint_from_word(&a,(bigint_word)-1); + ASSERT_NE(a.words,NULL); + ASSERT_EQ(a.words[0],(bigint_word)-1); + ASSERT_EQ(a.size,(size_t)1); + ASSERT_EQ(a.sign,(size_t)0); + + bigint_free(&a); +} + +UTEST(bigint,add_word) { + bigint a; + bigint_init(&a); + bigint_add_word(&a,1); + ASSERT_NE(a.words,NULL); + ASSERT_EQ(a.size,(size_t)1); + ASSERT_WEQ(a.words[0],1); + ASSERT_EQ(a.sign,(size_t)0); + + bigint_add_word(&a,(bigint_word)-1); + ASSERT_NE(a.words,NULL); + ASSERT_EQ(a.size,(size_t)2); + ASSERT_WEQ(a.words[0],0); + ASSERT_WEQ(a.words[1],1); + ASSERT_EQ(a.sign,(size_t)0); + + bigint_free(&a); +} + +UTEST(bigint,mul_word_zero) { + bigint a = BIGINT_INIT; + + ASSERT_EQ(bigint_resize(&a,1),0); + + a.words[0] = (bigint_word)-1; + a.size = 1; + + ASSERT_EQ(bigint_mul_word(&a,0),0); + ASSERT_WEQ(a.size,0); + + bigint_free(&a); +} + +UTEST(bigint,mul_word_one) { + bigint a = BIGINT_INIT; + + ASSERT_EQ(bigint_resize(&a,1),0); + + a.words[0] = (bigint_word)-1; + a.size = 1; + + ASSERT_EQ(bigint_mul_word(&a,1),0); + ASSERT_WEQ(a.size,1); + ASSERT_WEQ(a.words[0],(bigint_word)-1); + + bigint_free(&a); +} + +UTEST(bigint,mul_word_two) { + bigint a = BIGINT_INIT; + bigint_word res; + + ASSERT_EQ(bigint_resize(&a,1),0); + + a.words[0] = (bigint_word)-1; + res = a.words[0] << 1; + a.size = 1; + + ASSERT_EQ(bigint_mul_word(&a,2),0); + + ASSERT_WEQ(a.size,2); + ASSERT_WEQ(a.words[0],res); + ASSERT_WEQ(a.words[1],0x01); + + bigint_free(&a); +} + +UTEST(bigint,mul_word_four) { + bigint a = BIGINT_INIT; + bigint_word res; + + ASSERT_EQ(bigint_resize(&a,1),0); + + a.words[0] = (bigint_word)-1; + res = a.words[0] << 2; + a.size = 1; + + ASSERT_EQ(bigint_mul_word(&a,4),0); + ASSERT_WEQ(a.size,2); + ASSERT_WEQ(a.words[0],res); + ASSERT_WEQ(a.words[1],0x03); + + bigint_free(&a); +} + +UTEST(bigint,realloc) { + bigint a = BIGINT_INIT; + + ASSERT_EQ(bigint_add_word(&a,(bigint_word)-1),0); + ASSERT_EQ(a.size,(size_t)1); + ASSERT_WEQ(a.words[0],(bigint_word)-1); + +#ifndef BIGINT_NO_MALLOC + a.size = a.alloc; +#else + a.size = 8; +#endif + memset(a.words,0xFF,a.size * sizeof(bigint_word)); + + /* this will trigger a realloc */ + ASSERT_EQ(bigint_add_word(&a,1),0); + ASSERT_EQ(a.size,(size_t)9); + ASSERT_WEQ(a.words[0],0); + ASSERT_WEQ(a.words[1],0); + ASSERT_WEQ(a.words[2],0); + ASSERT_WEQ(a.words[3],0); + ASSERT_WEQ(a.words[4],0); + ASSERT_WEQ(a.words[5],0); + ASSERT_WEQ(a.words[6],0); + ASSERT_WEQ(a.words[7],0); + ASSERT_WEQ(a.words[8],1); + + bigint_free(&a); +} + +UTEST(bigint,add_unsigned) { + bigint a; + bigint b; + + bigint_init(&a); + bigint_init(&b); + + bigint_from_word(&a,(bigint_word)-1); + ASSERT_NE(a.words,NULL); + ASSERT_EQ(a.size,(size_t)1); + ASSERT_WEQ(a.words[0],-1); + ASSERT_EQ(a.sign,(size_t)0); + + bigint_from_word(&b,(bigint_word)1); + ASSERT_NE(b.words,NULL); + ASSERT_EQ(b.size,(size_t)1); + ASSERT_WEQ(b.words[0],1); + ASSERT_EQ(a.sign,(size_t)0); + + ASSERT_EQ(bigint_add_unsigned(&a,&b),0); + + ASSERT_NE(a.words,NULL); + ASSERT_EQ(a.size,(size_t)2); + ASSERT_WEQ(a.words[0],0); + ASSERT_WEQ(a.words[1],1); + + bigint_reset(&b); + ASSERT_EQ(bigint_add_unsigned(&a,&b),0); + + ASSERT_NE(a.words,NULL); + ASSERT_EQ(a.size,(size_t)2); + ASSERT_WEQ(a.words[0],0); + ASSERT_WEQ(a.words[1],1); + + a.words[0] = (bigint_word)-1; + a.words[1] = (bigint_word)-1; + + ASSERT_EQ(bigint_from_word(&b,1),0); + ASSERT_EQ(bigint_add_unsigned(&a,&b),0); + ASSERT_NE(a.words,NULL); + ASSERT_EQ(a.size,(size_t)3); + ASSERT_WEQ(a.words[0],0); + ASSERT_WEQ(a.words[1],0); + ASSERT_WEQ(a.words[2],1); + + bigint_free(&a); + bigint_free(&b); +} + +UTEST(bigint,sub_unsigned) { + bigint a; + bigint b; + + bigint_init(&a); + bigint_init(&b); + + bigint_from_word(&a,(bigint_word)-1); + ASSERT_NE(a.words,NULL); + ASSERT_EQ(a.words[0],(bigint_word)-1); + ASSERT_EQ(a.size,(size_t)1); + ASSERT_EQ(a.sign,(size_t)0); + + bigint_from_word(&b,(bigint_word)-1); + ASSERT_NE(b.words,NULL); + ASSERT_EQ(b.size,(size_t)1); + ASSERT_EQ(b.words[0],(bigint_word)-1); + ASSERT_EQ(a.sign,(size_t)0); + + ASSERT_EQ(bigint_sub_unsigned(&a,&b),0); + + ASSERT_EQ(a.size,(size_t)0); + + bigint_from_word(&a,(bigint_word)-1); + bigint_from_word(&b,1); + ASSERT_EQ(bigint_sub_unsigned(&a,&b),0); + ASSERT_NE(a.words,NULL); + ASSERT_EQ(a.size,(size_t)1); + ASSERT_EQ(a.words[0],((bigint_word)-1) - 1); + + bigint_reset(&b); + ASSERT_EQ(bigint_sub_unsigned(&a,&b),0); + ASSERT_NE(a.words,NULL); + ASSERT_EQ(a.size,(size_t)1); + ASSERT_EQ(a.words[0],((bigint_word)-1) - 1); + + bigint_from_word(&b,2); + ASSERT_EQ(bigint_add_unsigned(&a,&b),0); + ASSERT_NE(a.words,NULL); + ASSERT_EQ(a.size,(size_t)2); + ASSERT_EQ(a.words[0],(bigint_word)0); + ASSERT_EQ(a.words[1],(bigint_word)1); + + bigint_from_word(&b,1); + ASSERT_EQ(bigint_sub_unsigned(&a,&b),0); + ASSERT_NE(a.words,NULL); + ASSERT_EQ(a.size,(size_t)1); + ASSERT_EQ(a.words[0],((bigint_word)-1)); + + bigint_free(&a); + bigint_free(&b); +} + +UTEST(bigint_positive,bitlength) { + PREAMBLE + + ASSERT_EQ(bigint_resize(&a,2),0); + + a.words[0] = 0; + a.words[1] = 1; + + ASSERT_WEQ(bigint_bitlength(&a),(sizeof(bigint_word) * CHAR_BIT) + 1); + + CLEANUP +} + +UTEST(bigint_positive,get_set_bit) { + PREAMBLE + size_t i; + + ASSERT_EQ(bigint_resize(&a,2),0); + + a.words[0] = 0; + a.words[1] = 1; + + ASSERT_EQ(bigint_get_bit(&a,sizeof(bigint_word) * CHAR_BIT),1); + ASSERT_EQ(bigint_get_bit(&a,(sizeof(bigint_word) * CHAR_BIT) + 1),0); + ASSERT_EQ(bigint_get_bit(&a,(sizeof(bigint_word) * CHAR_BIT) - 1),0); + + for(i=0;i> 1)); + ASSERT_EQ(d.sign,(size_t)0); + + CLEANUP +} + +UTEST(bigint_positive,div_positive_carry) { + PREAMBLE + + ASSERT_EQ(bigint_resize(&a,2),0); + ASSERT_EQ(bigint_resize(&b,1),0); + a.words[0] = 0; + a.words[1] = 1; + b.words[0] = 2; + + ASSERT_EQ(bigint_div_mod(&d,&c,&a,&b),0); + + ASSERT_EQ(c.size,(size_t)0); + ASSERT_EQ(c.sign,(size_t)0); + + ASSERT_EQ(d.size,(size_t)1); + ASSERT_WEQ(d.words[0], (( 1LL << ((sizeof(bigint_word) * CHAR_BIT) - 1)))); + ASSERT_EQ(d.sign,(size_t)0); + + CLEANUP +} + +UTEST(bigint_positive,div_negative) { + PREAMBLE + + ASSERT_EQ(bigint_resize(&a,1),0); + ASSERT_EQ(bigint_resize(&b,1),0); + a.words[0] = ((bigint_word)-1); + b.words[0] = 2; + b.sign = 1; + + ASSERT_EQ(bigint_div_mod(&d,&c,&a,&b),0); + + ASSERT_EQ(c.size,(size_t)1); + ASSERT_WEQ(c.words[0],1); + ASSERT_EQ(c.sign,(size_t)0); + + ASSERT_EQ(d.size,(size_t)1); + ASSERT_WEQ(d.words[0], (((bigint_word)-1) >> 1)); + ASSERT_EQ(d.sign,(size_t)1); + + CLEANUP +} + +UTEST(bigint_positive,div_negative_carry) { + PREAMBLE + + ASSERT_EQ(bigint_resize(&a,2),0); + ASSERT_EQ(bigint_resize(&b,1),0); + a.words[0] = 0; + a.words[1] = 1; + b.words[0] = 2; + b.sign = 1; + + ASSERT_EQ(bigint_div_mod(&d,&c,&a,&b),0); + + ASSERT_EQ(c.size,(size_t)0); + ASSERT_EQ(c.sign,(size_t)0); + + ASSERT_EQ(d.size,(size_t)1); + ASSERT_WEQ(d.words[0], (( 1LL << ((sizeof(bigint_word) * CHAR_BIT) - 1)))); + ASSERT_EQ(d.sign,(size_t)1); + + CLEANUP +} + +UTEST(bigint_positive,lshift_whole_word) { + PREAMBLE; + + ASSERT_EQ(bigint_resize(&a,1),0); + a.words[0] = 1; + + ASSERT_EQ(bigint_lshift(&c,&a,sizeof(bigint_word) * CHAR_BIT),0); + ASSERT_EQ(c.size,(size_t)2); + ASSERT_EQ(c.sign,(size_t)0); + ASSERT_WEQ(c.words[0],0); + ASSERT_WEQ(c.words[1],1); + + CLEANUP +} + +UTEST(bigint_positive,lshift_half_word) { + PREAMBLE; + + ASSERT_EQ(bigint_resize(&a,1),0); + a.words[0] = 1; + + ASSERT_EQ(bigint_lshift(&c,&a,(sizeof(bigint_word) * CHAR_BIT) / 2),0); + ASSERT_EQ(c.size,(size_t)1); + ASSERT_EQ(c.sign,(size_t)0); + ASSERT_WEQ(c.words[0], (1LL << ((sizeof(bigint_word) * CHAR_BIT) / 2))); + + CLEANUP +} + +UTEST(bigint_positive,rshift_whole_word) { + PREAMBLE; + + ASSERT_EQ(bigint_resize(&a,2),0); + a.words[0] = 0; + a.words[1] = 1; + + ASSERT_EQ(bigint_rshift(&c,&a,sizeof(bigint_word) * CHAR_BIT),0); + ASSERT_EQ(c.size,(size_t)1); + ASSERT_EQ(c.sign,(size_t)0); + ASSERT_WEQ(c.words[0],1); + + CLEANUP +} + +UTEST(bigint_positive,rshift_half_word) { + PREAMBLE; + + ASSERT_EQ(bigint_resize(&a,2),0); + a.words[0] = 0; + a.words[1] = 1LL << (sizeof(bigint_word) * CHAR_BIT)/2; + + ASSERT_EQ(bigint_rshift(&c,&a,(sizeof(bigint_word) * CHAR_BIT)/2),0); + ASSERT_EQ(c.size,(size_t)2); + ASSERT_EQ(c.sign,(size_t)0); + ASSERT_WEQ(c.words[0],0); + ASSERT_WEQ(c.words[1],1); + + CLEANUP +} + +UTEST(bigint_positive,div_mod_word) { + PREAMBLE; + bigint_word rem; + + ASSERT_EQ(bigint_resize(&a,1),0); + a.words[0] = 255; + + bigint_div_mod_word(&a, &rem, 2); + ASSERT_EQ(a.size,(size_t)1); + ASSERT_EQ(a.sign,(size_t)0); + ASSERT_WEQ(a.words[0],127); + ASSERT_WEQ(rem,1); + + CLEANUP +} + +UTEST(bigint_negative,add_zero) { + PREAMBLE + + ASSERT_EQ(bigint_from_word(&a,(bigint_word)-1),0); + ASSERT_EQ(bigint_from_word(&b,0),0); + a.sign = 1; + ASSERT_EQ(bigint_add(&c,&a,&b),0); + ASSERT_NE(c.words,NULL); + ASSERT_EQ(c.size,(size_t)1); + ASSERT_EQ(c.sign,(size_t)1); + ASSERT_WEQ(c.words[0],-1); + + CLEANUP +} + +UTEST(bigint_negative,add_positive_nocarry_to_zero) { + PREAMBLE + + ASSERT_EQ(bigint_from_word(&a,1),0); + ASSERT_EQ(bigint_from_word(&b,1),0); + a.sign = 1; + + ASSERT_EQ(bigint_add(&c,&a,&b),0); + ASSERT_EQ(c.size,(size_t)0); + ASSERT_EQ(c.sign,(size_t)0); + + CLEANUP +} + +UTEST(bigint_negative,add_positive_carry_to_zero) { + PREAMBLE + + ASSERT_EQ(bigint_resize(&a,2),0); + ASSERT_EQ(bigint_resize(&b,2),0); + + a.words[0] = 0; + a.words[1] = 1; + a.sign = 1; + + b.words[0] = 0; + b.words[1] = 1; + b.sign = 0; + + ASSERT_EQ(bigint_add(&c,&a,&b),0); + ASSERT_EQ(c.size,(size_t)0); + ASSERT_EQ(c.sign,(size_t)0); + + CLEANUP +} + +UTEST(bigint_negative,add_positive_nocarry) { + PREAMBLE + + ASSERT_EQ(bigint_from_word(&a,(bigint_word)-1),0); + ASSERT_EQ(bigint_from_word(&b,1),0); + a.sign = 1; + + ASSERT_EQ(bigint_add(&c,&a,&b),0); + ASSERT_NE(c.words,NULL); + ASSERT_EQ(c.size,(size_t)1); + ASSERT_EQ(c.sign,(size_t)1); + ASSERT_WEQ(c.words[0],((bigint_word)-1)-1); + + CLEANUP +} + +UTEST(bigint_negative,add_positive_carry) { + PREAMBLE + + ASSERT_EQ(bigint_resize(&a,2),0); + ASSERT_EQ(bigint_from_word(&b,1),0); + + ASSERT_EQ(a.size,(size_t)2); + a.words[0] = 0; + a.words[1] = 1; + a.sign = 1; + + ASSERT_EQ(bigint_add(&c,&a,&b),0); + ASSERT_NE(c.words,NULL); + ASSERT_EQ(c.size,(size_t)1); + ASSERT_EQ(c.sign,(size_t)1); + ASSERT_WEQ(c.words[0],((bigint_word)-1)); + + CLEANUP +} + +UTEST(bigint_negative,add_positive_nocarry_signflip) { + PREAMBLE + + ASSERT_EQ(bigint_resize(&a,1),0); + ASSERT_EQ(bigint_from_word(&b,2),0); + + ASSERT_EQ(a.size,(size_t)1); + a.words[0] = 1; + a.sign = 1; + + ASSERT_EQ(bigint_add(&c,&a,&b),0); + ASSERT_NE(c.words,NULL); + ASSERT_EQ(c.size,(size_t)1); + ASSERT_EQ(c.sign,(size_t)0); + ASSERT_WEQ(c.words[0],1); + + CLEANUP +} + +UTEST(bigint_negative,add_positive_carry_signflip) { + PREAMBLE + + ASSERT_EQ(bigint_resize(&a,2),0); + ASSERT_EQ(bigint_resize(&b,2),0); + + ASSERT_EQ(a.size,(size_t)2); + ASSERT_EQ(b.size,(size_t)2); + a.words[0] = 0; + a.words[1] = 1; + a.sign = 1; + b.words[0] = 1; + b.words[1] = 1; + b.sign = 0; + + ASSERT_EQ(bigint_add(&c,&a,&b),0); + ASSERT_NE(c.words,NULL); + ASSERT_EQ(c.size,(size_t)1); + ASSERT_EQ(c.sign,(size_t)0); + ASSERT_WEQ(c.words[0],1); + + CLEANUP +} + +UTEST(bigint_negative,sub_positive_nocarry) { + PREAMBLE + + ASSERT_EQ(bigint_resize(&a,1),0); + ASSERT_EQ(bigint_resize(&b,1),0); + + a.sign = 1; + b.sign = 0; + + a.words[0] = 1; + b.words[0] = 1; + + ASSERT_EQ(bigint_sub(&c,&a,&b),0); + ASSERT_NE(c.words,NULL); + ASSERT_EQ(c.size,(size_t)1); + ASSERT_EQ(c.sign,(size_t)1); + ASSERT_WEQ(c.words[0],2); + + CLEANUP +} + +UTEST(bigint_negative,sub_positive_carry) { + PREAMBLE + + ASSERT_EQ(bigint_resize(&a,1),0); + ASSERT_EQ(bigint_resize(&b,1),0); + + a.sign = 1; + b.sign = 0; + + a.words[0] = (bigint_word)-1; + b.words[0] = 1; + + ASSERT_EQ(bigint_sub(&c,&a,&b),0); + ASSERT_NE(c.words,NULL); + ASSERT_EQ(c.size,(size_t)2); + ASSERT_EQ(c.sign,(size_t)1); + ASSERT_WEQ(c.words[0],0); + ASSERT_WEQ(c.words[1],1); + + CLEANUP +} + +UTEST(bigint_negative,sub_positive_carry_fromzero) { + PREAMBLE + + ASSERT_EQ(bigint_resize(&b,1),0); + + b.sign = 0; + b.words[0] = 1; + + ASSERT_EQ(bigint_sub(&c,&a,&b),0); + ASSERT_NE(c.words,NULL); + ASSERT_EQ(c.size,(size_t)1); + ASSERT_EQ(c.sign,(size_t)1); + ASSERT_WEQ(c.words[0],1); + + CLEANUP +} + +UTEST(bigint_negative,add_negative_nocarry) { + PREAMBLE + + ASSERT_EQ(bigint_resize(&a,1),0); + ASSERT_EQ(bigint_resize(&b,1),0); + a.sign = 1; + b.sign = 1; + a.words[0] = 1; + b.words[0] = 1; + + ASSERT_EQ(bigint_add(&c,&a,&b),0); + ASSERT_NE(c.words,NULL); + ASSERT_EQ(c.size,(size_t)1); + ASSERT_WEQ(c.words[0],2); + + CLEANUP +} + +UTEST(bigint_negative,add_negative_carry) { + PREAMBLE + + ASSERT_EQ(bigint_resize(&a,1),0); + ASSERT_EQ(bigint_resize(&b,1),0); + a.sign = 1; + b.sign = 1; + a.words[0] = (bigint_word)-1; + b.words[0] = 1; + + ASSERT_EQ(bigint_add(&c,&a,&b),0); + ASSERT_NE(c.words,NULL); + ASSERT_EQ(c.size,(size_t)2); + ASSERT_WEQ(c.words[0],0); + ASSERT_WEQ(c.words[1],1); + + CLEANUP +} + +UTEST(bigint_negative,mul_long_zero) { + PREAMBLE + + ASSERT_EQ(bigint_resize(&a,1),0); + a.sign = 1; + a.words[0] = 1; + + ASSERT_EQ(bigint_mul_long(&c,&a,&b),0); + ASSERT_EQ(c.size,(size_t)0); + ASSERT_EQ(c.sign,(size_t)0); + + CLEANUP +} + +UTEST(bigint_negative,mul_long_positive_nocarry) { + PREAMBLE + + ASSERT_EQ(bigint_resize(&a,1),0); + ASSERT_EQ(bigint_resize(&b,1),0); + a.sign = 1; + a.words[0] = 1; + b.words[0] = 2; + + ASSERT_EQ(bigint_mul_long(&c,&a,&b),0); + ASSERT_EQ(c.size,(size_t)1); + ASSERT_WEQ(c.words[0],2); + ASSERT_EQ(c.sign,(size_t)1); + + CLEANUP +} + +UTEST(bigint_negative,mul_long_positive_carry) { + PREAMBLE + + size_t bits = sizeof(bigint_word) * CHAR_BIT; + bits /= 2; + + ASSERT_EQ(bigint_resize(&a,1),0); + ASSERT_EQ(bigint_resize(&b,1),0); + a.sign = 1; + a.words[0] = 1; + b.words[0] = 1; + while(bits--) { + a.words[0] <<= 1; + b.words[0] <<= 1; + } + + ASSERT_EQ(bigint_mul_long(&c,&a,&b),0); + ASSERT_EQ(c.size,(size_t)2); + ASSERT_WEQ(c.words[0],0); + ASSERT_WEQ(c.words[1],1); + ASSERT_EQ(c.sign,(size_t)1); + + CLEANUP +} + +UTEST(bigint_negative,mul_long_negative_nocarry) { + PREAMBLE + + ASSERT_EQ(bigint_resize(&a,1),0); + ASSERT_EQ(bigint_resize(&b,1),0); + a.words[0] = 1; + b.words[0] = 2; + a.sign = 1; + b.sign = 1; + + ASSERT_EQ(bigint_mul_long(&c,&a,&b),0); + ASSERT_EQ(c.size,(size_t)1); + ASSERT_WEQ(c.words[0],2); + ASSERT_EQ(c.sign,(size_t)0); + + CLEANUP +} + +UTEST(bigint_negative,mul_long_negative_carry) { + PREAMBLE + + size_t bits = sizeof(bigint_word) * CHAR_BIT; + bits /= 2; + + ASSERT_EQ(bigint_resize(&a,1),0); + ASSERT_EQ(bigint_resize(&b,1),0); + a.words[0] = 1; + b.words[0] = 1; + while(bits--) { + a.words[0] <<= 1; + b.words[0] <<= 1; + } + a.sign = 1; + b.sign = 1; + + ASSERT_EQ(bigint_mul_long(&c,&a,&b),0); + ASSERT_EQ(c.size,(size_t)2); + ASSERT_WEQ(c.words[0],0); + ASSERT_WEQ(c.words[1],1); + ASSERT_EQ(c.sign,(size_t)0); + + CLEANUP +} + +UTEST(bigint_negative,mul_zero) { + PREAMBLE + + ASSERT_EQ(bigint_resize(&a,1),0); + a.words[0] = 1; + a.sign = 1; + + ASSERT_EQ(bigint_mul(&c,&a,&b),0); + ASSERT_EQ(c.size,(size_t)0); + ASSERT_EQ(c.sign,(size_t)0); + + CLEANUP +} + +UTEST(bigint_negative,mul_positive_nocarry) { + PREAMBLE + + ASSERT_EQ(bigint_resize(&a,1),0); + ASSERT_EQ(bigint_resize(&b,1),0); + a.words[0] = 1; + b.words[0] = 2; + a.sign = 1; + + ASSERT_EQ(bigint_mul(&c,&a,&b),0); + ASSERT_EQ(c.size,(size_t)1); + ASSERT_WEQ(c.words[0],2); + ASSERT_EQ(c.sign,(size_t)1); + + CLEANUP +} + +UTEST(bigint_negative,mul_positive_carry) { + PREAMBLE + + size_t bits = sizeof(bigint_word) * CHAR_BIT; + bits /= 2; + + ASSERT_EQ(bigint_resize(&a,1),0); + ASSERT_EQ(bigint_resize(&b,1),0); + a.words[0] = 1; + b.words[0] = 1; + while(bits--) { + a.words[0] <<= 1; + b.words[0] <<= 1; + } + a.sign = 1; + + ASSERT_EQ(bigint_mul(&c,&a,&b),0); + ASSERT_EQ(c.size,(size_t)2); + ASSERT_WEQ(c.words[0],0); + ASSERT_WEQ(c.words[1],1); + ASSERT_EQ(c.sign,(size_t)1); + + CLEANUP +} + +UTEST(bigint_negative,mul_negative_nocarry) { + PREAMBLE + + ASSERT_EQ(bigint_resize(&a,1),0); + ASSERT_EQ(bigint_resize(&b,1),0); + a.words[0] = 1; + b.words[0] = 2; + a.sign = 1; + b.sign = 1; + + ASSERT_EQ(bigint_mul(&c,&a,&b),0); + ASSERT_EQ(c.size,(size_t)1); + ASSERT_WEQ(c.words[0],2); + ASSERT_EQ(c.sign,(size_t)0); + + CLEANUP +} + +UTEST(bigint_negative,mul_negative_carry) { + PREAMBLE + + size_t bits = sizeof(bigint_word) * CHAR_BIT; + bits /= 2; + + ASSERT_EQ(bigint_resize(&a,1),0); + ASSERT_EQ(bigint_resize(&b,1),0); + a.words[0] = 1; + b.words[0] = 1; + while(bits--) { + a.words[0] <<= 1; + b.words[0] <<= 1; + } + a.sign = 1; + b.sign = 1; + + ASSERT_EQ(bigint_mul(&c,&a,&b),0); + ASSERT_EQ(c.size,(size_t)2); + ASSERT_WEQ(c.words[0],0); + ASSERT_WEQ(c.words[1],1); + ASSERT_EQ(c.sign,(size_t)0); + + CLEANUP +} + +UTEST(bigint_zero,mul_long_positive) { + PREAMBLE + + ASSERT_EQ(bigint_resize(&b,1),0); + b.words[0] = 1; + + ASSERT_EQ(bigint_mul_long(&c,&a,&b),0); + ASSERT_EQ(c.size,(size_t)0); + ASSERT_EQ(c.sign,(size_t)0); + + CLEANUP +} + +UTEST(bigint_zero,mul_long_negative) { + PREAMBLE + + ASSERT_EQ(bigint_resize(&b,1),0); + b.words[0] = 1; + b.sign = 1; + + ASSERT_EQ(bigint_mul_long(&c,&a,&b),0); + ASSERT_EQ(c.size,(size_t)0); + ASSERT_EQ(c.sign,(size_t)0); + + CLEANUP +} + +UTEST(bigint_zero,mul_long) { + PREAMBLE + + ASSERT_EQ(bigint_mul_long(&c,&a,&b),0); + ASSERT_EQ(c.size,(size_t)0); + ASSERT_EQ(c.sign,(size_t)0); + + CLEANUP +} + +UTEST(bigint_zero,rshift) { + PREAMBLE; + + ASSERT_EQ(bigint_rshift(&c,&a,1),0); + ASSERT_EQ(c.size,(size_t)0); + ASSERT_EQ(c.sign,(size_t)0); + + CLEANUP +} + +UTEST(bigint_zero,inc) { + PREAMBLE + + ASSERT_EQ(bigint_inc(&a,&a),0); + ASSERT_EQ(a.size,(size_t)1); + ASSERT_EQ(a.sign,(size_t)0); + ASSERT_WEQ(a.words[0],1); + + CLEANUP +} + +UTEST(bigint_zero,dec) { + PREAMBLE + + ASSERT_EQ(bigint_dec(&a,&a),0); + ASSERT_EQ(a.size,(size_t)1); + ASSERT_EQ(a.sign,(size_t)1); + ASSERT_WEQ(a.words[0],1); + + CLEANUP +} + +UTEST(bigint,from_int8) { + bigint a = BIGINT_INIT; + + ASSERT_EQ(bigint_from_u8(&a,128),0); + ASSERT_EQ(a.sign,(size_t)0); + ASSERT_WEQ(a.size,1); + ASSERT_WEQ(a.words[0],0x80); + + ASSERT_EQ(bigint_from_i8(&a,-128),0); + ASSERT_EQ(a.sign,(size_t)1); + ASSERT_WEQ(a.size,1); + ASSERT_WEQ(a.words[0],0x80); + + bigint_free(&a); +} + +UTEST(bigint,from_int16) { + bigint a = BIGINT_INIT; + + ASSERT_EQ(bigint_from_u16(&a,32768),0); + ASSERT_EQ(a.sign,(size_t)0); +#if BIGINT_WORD_WIDTH == 1 + ASSERT_WEQ(a.size,2); + ASSERT_WEQ(a.words[0],0x00); + ASSERT_WEQ(a.words[1],0x80); +#else + ASSERT_WEQ(a.size,1); + ASSERT_WEQ(a.words[0],0x8000); +#endif + + ASSERT_EQ(bigint_from_i16(&a,-32768),0); + ASSERT_EQ(a.sign,(size_t)1); +#if BIGINT_WORD_WIDTH == 1 + ASSERT_WEQ(a.size,2); + ASSERT_WEQ(a.words[0],0x00); + ASSERT_WEQ(a.words[1],0x80); +#else + ASSERT_WEQ(a.size,1); + ASSERT_WEQ(a.words[0],0x8000); +#endif + + bigint_free(&a); +} + +UTEST(bigint,from_int32) { + bigint a = BIGINT_INIT; + + ASSERT_EQ(bigint_from_u32(&a,((uint32_t)INT32_MAX) + 1),0); + ASSERT_EQ(a.sign,(size_t)0); +#if BIGINT_WORD_WIDTH == 1 + ASSERT_WEQ(a.size,4); + ASSERT_WEQ(a.words[0],0x00); + ASSERT_WEQ(a.words[1],0x00); + ASSERT_WEQ(a.words[2],0x00); + ASSERT_WEQ(a.words[3],0x80); +#elif BIGINT_WORD_WIDTH == 2 + ASSERT_WEQ(a.size,2); + ASSERT_WEQ(a.words[0],0x0000); + ASSERT_WEQ(a.words[1],0x8000); +#else + ASSERT_WEQ(a.size,1); + ASSERT_WEQ(a.words[0],0x80000000); +#endif + + ASSERT_EQ(bigint_from_i32(&a,INT32_MIN),0); + ASSERT_EQ(a.sign,(size_t)1); +#if BIGINT_WORD_WIDTH == 1 + ASSERT_WEQ(a.size,4); + ASSERT_WEQ(a.words[0],0x00); + ASSERT_WEQ(a.words[1],0x00); + ASSERT_WEQ(a.words[2],0x00); + ASSERT_WEQ(a.words[3],0x80); +#elif BIGINT_WORD_WIDTH == 2 + ASSERT_WEQ(a.size,2); + ASSERT_WEQ(a.words[0],0x0000); + ASSERT_WEQ(a.words[1],0x8000); +#else + ASSERT_WEQ(a.size,1); + ASSERT_WEQ(a.words[0],0x80000000); +#endif + + bigint_free(&a); +} + +UTEST(bigint,from_int64) { + bigint a = BIGINT_INIT; + + ASSERT_EQ(bigint_from_u64(&a,((uint64_t)INT64_MAX) + 1), 0); + ASSERT_EQ(a.sign,(size_t)0); +#if BIGINT_WORD_WIDTH == 1 + ASSERT_WEQ(a.size,8); + ASSERT_WEQ(a.words[0],0x00); + ASSERT_WEQ(a.words[1],0x00); + ASSERT_WEQ(a.words[2],0x00); + ASSERT_WEQ(a.words[3],0x00); + ASSERT_WEQ(a.words[4],0x00); + ASSERT_WEQ(a.words[5],0x00); + ASSERT_WEQ(a.words[6],0x00); + ASSERT_WEQ(a.words[7],0x80); +#elif BIGINT_WORD_WIDTH == 2 + ASSERT_WEQ(a.size,4); + ASSERT_WEQ(a.words[0],0x0000); + ASSERT_WEQ(a.words[1],0x0000); + ASSERT_WEQ(a.words[2],0x0000); + ASSERT_WEQ(a.words[3],0x8000); +#elif BIGINT_WORD_WIDTH == 4 + ASSERT_WEQ(a.size,2); + ASSERT_WEQ(a.words[0],0x00000000); + ASSERT_WEQ(a.words[1],0x80000000); +#else + ASSERT_WEQ(a.size,1); + ASSERT_WEQ(a.words[0],0x8000000000000000); +#endif + + ASSERT_EQ(bigint_from_i64(&a,INT64_MIN),0); + ASSERT_EQ(a.sign,(size_t)1); +#if BIGINT_WORD_WIDTH == 1 + ASSERT_WEQ(a.size,8); + ASSERT_WEQ(a.words[0],0x00); + ASSERT_WEQ(a.words[1],0x00); + ASSERT_WEQ(a.words[2],0x00); + ASSERT_WEQ(a.words[3],0x00); + ASSERT_WEQ(a.words[4],0x00); + ASSERT_WEQ(a.words[5],0x00); + ASSERT_WEQ(a.words[6],0x00); + ASSERT_WEQ(a.words[7],0x80); +#elif BIGINT_WORD_WIDTH == 2 + ASSERT_WEQ(a.size,4); + ASSERT_WEQ(a.words[0],0x0000); + ASSERT_WEQ(a.words[1],0x0000); + ASSERT_WEQ(a.words[2],0x0000); + ASSERT_WEQ(a.words[3],0x8000); +#elif BIGINT_WORD_WIDTH == 4 + ASSERT_WEQ(a.size,2); + ASSERT_WEQ(a.words[0],0x00000000); + ASSERT_WEQ(a.words[1],0x80000000); +#else + ASSERT_WEQ(a.size,1); + ASSERT_WEQ(a.words[0],0x8000000000000000); +#endif + + bigint_free(&a); +} +UTEST_MAIN(); diff --git a/utest.h b/utest.h new file mode 100644 index 0000000..b499a7c --- /dev/null +++ b/utest.h @@ -0,0 +1,1431 @@ +/* + The latest version of this library is available on GitHub; + https://github.com/sheredom/utest.h +*/ + +/* + This is free and unencumbered software released into the public domain. + + Anyone is free to copy, modify, publish, use, compile, sell, or + distribute this software, either in source code form or as a compiled + binary, for any purpose, commercial or non-commercial, and by any + means. + + In jurisdictions that recognize copyright laws, the author or authors + of this software dedicate any and all copyright interest in the + software to the public domain. We make this dedication for the benefit + of the public at large and to the detriment of our heirs and + successors. We intend this dedication to be an overt act of + relinquishment in perpetuity of all present and future rights to this + software under copyright law. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + + For more information, please refer to +*/ + +#ifndef SHEREDOM_UTEST_H_INCLUDED +#define SHEREDOM_UTEST_H_INCLUDED + +#ifdef _MSC_VER +/* + Disable warning about not inlining 'inline' functions. +*/ +#pragma warning(disable : 4710) + +/* + Disable warning about inlining functions that are not marked 'inline'. +*/ +#pragma warning(disable : 4711) + +/* + Disable warning for alignment padding added +*/ +#pragma warning(disable : 4820) + +#if _MSC_VER > 1900 +/* + Disable warning about preprocessor macros not being defined in MSVC headers. +*/ +#pragma warning(disable : 4668) + +/* + Disable warning about no function prototype given in MSVC headers. +*/ +#pragma warning(disable : 4255) + +/* + Disable warning about pointer or reference to potentially throwing function. +*/ +#pragma warning(disable : 5039) +#endif + +#pragma warning(push, 1) +#endif + +#if defined(_MSC_VER) && (_MSC_VER < 1920) +typedef __int64 utest_int64_t; +typedef unsigned __int64 utest_uint64_t; +typedef unsigned __int32 utest_uint32_t; +#else +#include +typedef int64_t utest_int64_t; +typedef uint64_t utest_uint64_t; +typedef uint32_t utest_uint32_t; +#endif + +#include +#include +#include +#include +#include + +#if defined(__cplusplus) +#include +#endif + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + +#if defined(__cplusplus) +#define UTEST_C_FUNC extern "C" +#else +#define UTEST_C_FUNC +#endif + +#define UTEST_TEST_PASSED (0) +#define UTEST_TEST_FAILURE (1) +#define UTEST_TEST_SKIPPED (2) + +#if defined(_MSC_VER) || defined(__MINGW64__) || defined(__MINGW32__) + +#if defined(__MINGW64__) || defined(__MINGW32__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpragmas" +#pragma GCC diagnostic ignored "-Wunknown-pragmas" +#endif + +// define UTEST_USE_OLD_QPC before #include "utest.h" to use old +// QueryPerformanceCounter +#ifndef UTEST_USE_OLD_QPC +#pragma warning(push, 0) +#include +#pragma warning(pop) + +typedef LARGE_INTEGER utest_large_integer; +#else +// use old QueryPerformanceCounter definitions (not sure is this needed in some +// edge cases or not) on Win7 with VS2015 these extern declaration cause "second +// C linkage of overloaded function not allowed" error +typedef union { + struct { + unsigned long LowPart; + long HighPart; + } DUMMYSTRUCTNAME; + struct { + unsigned long LowPart; + long HighPart; + } u; + utest_int64_t QuadPart; +} utest_large_integer; + +UTEST_C_FUNC __declspec(dllimport) int __stdcall QueryPerformanceCounter( + utest_large_integer *); +UTEST_C_FUNC __declspec(dllimport) int __stdcall QueryPerformanceFrequency( + utest_large_integer *); + +#if defined(__MINGW64__) || defined(__MINGW32__) +#pragma GCC diagnostic pop +#endif +#endif + +#elif defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || \ + defined(__NetBSD__) || defined(__DragonFly__) || defined(__sun__) || \ + defined(__HAIKU__) +/* + slightly obscure include here - we need to include glibc's features.h, but + we don't want to just include a header that might not be defined for other + c libraries like musl. Instead we include limits.h, which we know on all + glibc distributions includes features.h +*/ +#include + +#if defined(__GLIBC__) && defined(__GLIBC_MINOR__) +#include + +#if ((2 < __GLIBC__) || ((2 == __GLIBC__) && (17 <= __GLIBC_MINOR__))) +/* glibc is version 2.17 or above, so we can just use clock_gettime */ +#define UTEST_USE_CLOCKGETTIME +#else +#include +#include +#endif +#else // Other libc implementations +#include +#define UTEST_USE_CLOCKGETTIME +#endif + +#elif defined(__APPLE__) +#include +#endif + +#if defined(_MSC_VER) && (_MSC_VER < 1920) +#define UTEST_PRId64 "I64d" +#define UTEST_PRIu64 "I64u" +#else +#include + +#define UTEST_PRId64 PRId64 +#define UTEST_PRIu64 PRIu64 +#endif + +#if defined(__cplusplus) +#define UTEST_INLINE inline + +#if defined(__clang__) +#define UTEST_INITIALIZER_BEGIN_DISABLE_WARNINGS \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wglobal-constructors\"") + +#define UTEST_INITIALIZER_END_DISABLE_WARNINGS _Pragma("clang diagnostic pop") +#else +#define UTEST_INITIALIZER_BEGIN_DISABLE_WARNINGS +#define UTEST_INITIALIZER_END_DISABLE_WARNINGS +#endif + +#define UTEST_INITIALIZER(f) \ + struct f##_cpp_struct { \ + f##_cpp_struct(); \ + }; \ + UTEST_INITIALIZER_BEGIN_DISABLE_WARNINGS static f##_cpp_struct \ + f##_cpp_global UTEST_INITIALIZER_END_DISABLE_WARNINGS; \ + f##_cpp_struct::f##_cpp_struct() +#elif defined(_MSC_VER) +#define UTEST_INLINE __forceinline + +#if defined(_WIN64) +#define UTEST_SYMBOL_PREFIX +#else +#define UTEST_SYMBOL_PREFIX "_" +#endif + +#if defined(__clang__) +#define UTEST_INITIALIZER_BEGIN_DISABLE_WARNINGS \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wmissing-variable-declarations\"") + +#define UTEST_INITIALIZER_END_DISABLE_WARNINGS _Pragma("clang diagnostic pop") +#else +#define UTEST_INITIALIZER_BEGIN_DISABLE_WARNINGS +#define UTEST_INITIALIZER_END_DISABLE_WARNINGS +#endif + +#pragma section(".CRT$XCU", read) +#define UTEST_INITIALIZER(f) \ + static void __cdecl f(void); \ + UTEST_INITIALIZER_BEGIN_DISABLE_WARNINGS \ + __pragma(comment(linker, "/include:" UTEST_SYMBOL_PREFIX #f "_")) \ + UTEST_C_FUNC __declspec(allocate(".CRT$XCU")) void(__cdecl * \ + f##_)(void) = f; \ + UTEST_INITIALIZER_END_DISABLE_WARNINGS \ + static void __cdecl f(void) +#else +#if defined(__linux__) +#if defined(__clang__) +#if __has_warning("-Wreserved-id-macro") +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreserved-id-macro" +#endif +#endif + +#define __STDC_FORMAT_MACROS 1 + +#if defined(__clang__) +#if __has_warning("-Wreserved-id-macro") +#pragma clang diagnostic pop +#endif +#endif +#endif + +#define UTEST_INLINE inline + +#define UTEST_INITIALIZER(f) \ + static void f(void) __attribute__((constructor)); \ + static void f(void) +#endif + +#if defined(__cplusplus) +#define UTEST_CAST(type, x) static_cast(x) +#define UTEST_PTR_CAST(type, x) reinterpret_cast(x) +#define UTEST_EXTERN extern "C" +#define UTEST_NULL NULL +#else +#define UTEST_CAST(type, x) ((type)(x)) +#define UTEST_PTR_CAST(type, x) ((type)(x)) +#define UTEST_EXTERN extern +#define UTEST_NULL 0 +#endif + +#ifdef _MSC_VER +/* + io.h contains definitions for some structures with natural padding. This is + uninteresting, but for some reason MSVC's behaviour is to warn about + including this system header. That *is* interesting +*/ +#pragma warning(disable : 4820) +#pragma warning(push, 1) +#include +#pragma warning(pop) +#define UTEST_COLOUR_OUTPUT() (_isatty(_fileno(stdout))) +#else +#if defined(__EMSCRIPTEN__) +#include +#define UTEST_COLOUR_OUTPUT() false +#else +#include +#define UTEST_COLOUR_OUTPUT() (isatty(STDOUT_FILENO)) +#endif +#endif + +static UTEST_INLINE void *utest_realloc(void *const pointer, size_t new_size) { + void *const new_pointer = realloc(pointer, new_size); + + if (UTEST_NULL == new_pointer) { + free(new_pointer); + } + + return new_pointer; +} + +static UTEST_INLINE utest_int64_t utest_ns(void) { +#if defined(_MSC_VER) || defined(__MINGW64__) || defined(__MINGW32__) + utest_large_integer counter; + utest_large_integer frequency; + QueryPerformanceCounter(&counter); + QueryPerformanceFrequency(&frequency); + return UTEST_CAST(utest_int64_t, + (counter.QuadPart * 1000000000) / frequency.QuadPart); +#elif defined(__linux__) && defined(__STRICT_ANSI__) + return UTEST_CAST(utest_int64_t, clock()) * 1000000000 / CLOCKS_PER_SEC; +#elif defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || \ + defined(__NetBSD__) || defined(__DragonFly__) || defined(__sun__) || \ + defined(__HAIKU__) + struct timespec ts; +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) && \ + !defined(__HAIKU__) + timespec_get(&ts, TIME_UTC); +#else + const clockid_t cid = CLOCK_REALTIME; +#if defined(UTEST_USE_CLOCKGETTIME) + clock_gettime(cid, &ts); +#else + syscall(SYS_clock_gettime, cid, &ts); +#endif +#endif + return UTEST_CAST(utest_int64_t, ts.tv_sec) * 1000 * 1000 * 1000 + ts.tv_nsec; +#elif __APPLE__ + return UTEST_CAST(utest_int64_t, mach_absolute_time()); +#elif __EMSCRIPTEN__ + return emscripten_performance_now() * 1000000.0; +#else +#error Unsupported platform! +#endif +} + +typedef void (*utest_testcase_t)(int *, size_t); + +struct utest_test_state_s { + utest_testcase_t func; + size_t index; + char *name; +}; + +struct utest_state_s { + struct utest_test_state_s *tests; + size_t tests_length; + FILE *output; +}; + +/* extern to the global state utest needs to execute */ +UTEST_EXTERN struct utest_state_s utest_state; + +#if defined(_MSC_VER) +#define UTEST_WEAK __forceinline +#else +#define UTEST_WEAK __attribute__((weak)) +#endif + +#if defined(_MSC_VER) +#define UTEST_UNUSED +#else +#define UTEST_UNUSED __attribute__((unused)) +#endif + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wvariadic-macros" +#pragma clang diagnostic ignored "-Wc++98-compat-pedantic" +#endif +#define UTEST_PRINTF(...) \ + if (utest_state.output) { \ + fprintf(utest_state.output, __VA_ARGS__); \ + } \ + printf(__VA_ARGS__) +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wvariadic-macros" +#pragma clang diagnostic ignored "-Wc++98-compat-pedantic" +#endif + +#ifdef _MSC_VER +#define UTEST_SNPRINTF(BUFFER, N, ...) _snprintf_s(BUFFER, N, N, __VA_ARGS__) +#else +#define UTEST_SNPRINTF(...) snprintf(__VA_ARGS__) +#endif + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#if defined(__cplusplus) +/* if we are using c++ we can use overloaded methods (its in the language) */ +#define UTEST_OVERLOADABLE +#elif defined(__clang__) +/* otherwise, if we are using clang with c - use the overloadable attribute */ +#define UTEST_OVERLOADABLE __attribute__((overloadable)) +#endif + +#if defined(UTEST_OVERLOADABLE) +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(float f); +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(float f) { + UTEST_PRINTF("%f", UTEST_CAST(double, f)); +} + +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(double d); +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(double d) { + UTEST_PRINTF("%f", d); +} + +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long double d); +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long double d) { + UTEST_PRINTF("%Lf", d); +} + +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(int i); +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(int i) { + UTEST_PRINTF("%d", i); +} + +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(unsigned int i); +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(unsigned int i) { + UTEST_PRINTF("%u", i); +} + +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long int i); +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long int i) { + UTEST_PRINTF("%ld", i); +} + +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long unsigned int i); +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long unsigned int i) { + UTEST_PRINTF("%lu", i); +} + +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(const void *p); +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(const void *p) { + UTEST_PRINTF("%p", p); +} + +/* + long long is a c++11 extension +*/ +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) || \ + defined(__cplusplus) && (__cplusplus >= 201103L) + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wc++98-compat-pedantic" +#endif + +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long long int i); +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long long int i) { + UTEST_PRINTF("%lld", i); +} + +UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long long unsigned int i); +UTEST_WEAK UTEST_OVERLOADABLE void +utest_type_printer(long long unsigned int i) { + UTEST_PRINTF("%llu", i); +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#endif +#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#define utest_type_printer(val) \ + UTEST_PRINTF(_Generic((val), signed char \ + : "%d", unsigned char \ + : "%u", short \ + : "%d", unsigned short \ + : "%u", int \ + : "%d", long \ + : "%ld", long long \ + : "%lld", unsigned \ + : "%u", unsigned long \ + : "%lu", unsigned long long \ + : "%llu", float \ + : "%f", double \ + : "%f", long double \ + : "%Lf", default \ + : _Generic((val - val), ptrdiff_t \ + : "%p", default \ + : "undef")), \ + (val)) +#else +/* + we don't have the ability to print the values we got, so we create a macro + to tell our users we can't do anything fancy +*/ +#define utest_type_printer(...) UTEST_PRINTF("undef") +#endif + +#ifdef _MSC_VER +#define UTEST_SURPRESS_WARNING_BEGIN \ + __pragma(warning(push)) __pragma(warning(disable : 4127)) \ + __pragma(warning(disable : 4571)) __pragma(warning(disable : 4130)) +#define UTEST_SURPRESS_WARNING_END __pragma(warning(pop)) +#else +#define UTEST_SURPRESS_WARNING_BEGIN +#define UTEST_SURPRESS_WARNING_END +#endif + +#if defined(__cplusplus) && (__cplusplus >= 201103L) +#define UTEST_AUTO(x) auto +#elif !defined(__cplusplus) + +#if defined(__clang__) +/* clang-format off */ +/* had to disable clang-format here because it malforms the pragmas */ +#define UTEST_AUTO(x) \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wgnu-auto-type\"") __auto_type \ + _Pragma("clang diagnostic pop") +/* clang-format on */ +#else +#define UTEST_AUTO(x) __typeof__(x + 0) +#endif + +#else +#define UTEST_AUTO(x) typeof(x + 0) +#endif + +#if defined(__clang__) +#define UTEST_STRNCMP(x, y, size) \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wdisabled-macro-expansion\"") \ + strncmp(x, y, size) _Pragma("clang diagnostic pop") +#else +#define UTEST_STRNCMP(x, y, size) strncmp(x, y, size) +#endif + +#define UTEST_SKIP(msg) \ + do { \ + UTEST_PRINTF(" Skipped : '%s'\n", (msg)); \ + *utest_result = UTEST_TEST_SKIPPED; \ + return; \ + } while (0) + +#if defined(__clang__) +#define UTEST_EXPECT(x, y, cond) \ + UTEST_SURPRESS_WARNING_BEGIN do { \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wlanguage-extension-token\"") \ + _Pragma("clang diagnostic ignored \"-Wc++98-compat-pedantic\"") \ + _Pragma("clang diagnostic ignored \"-Wfloat-equal\"") \ + UTEST_AUTO(x) xEval = (x); \ + UTEST_AUTO(y) yEval = (y); \ + if (!((xEval)cond(yEval))) { \ + _Pragma("clang diagnostic pop") \ + UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ + UTEST_PRINTF(" Expected : ("); \ + UTEST_PRINTF(#x ") " #cond " (" #y); \ + UTEST_PRINTF(")\n"); \ + UTEST_PRINTF(" Actual : "); \ + utest_type_printer(xEval); \ + UTEST_PRINTF(" vs "); \ + utest_type_printer(yEval); \ + UTEST_PRINTF("\n"); \ + *utest_result = UTEST_TEST_FAILURE; \ + } \ + } \ + while (0) \ + UTEST_SURPRESS_WARNING_END +#elif defined(__GNUC__) +#define UTEST_EXPECT(x, y, cond) \ + UTEST_SURPRESS_WARNING_BEGIN do { \ + UTEST_AUTO(x) xEval = (x); \ + UTEST_AUTO(y) yEval = (y); \ + if (!((xEval)cond(yEval))) { \ + UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ + UTEST_PRINTF(" Expected : ("); \ + UTEST_PRINTF(#x ") " #cond " (" #y); \ + UTEST_PRINTF(")\n"); \ + UTEST_PRINTF(" Actual : "); \ + utest_type_printer(xEval); \ + UTEST_PRINTF(" vs "); \ + utest_type_printer(yEval); \ + UTEST_PRINTF("\n"); \ + *utest_result = UTEST_TEST_FAILURE; \ + } \ + } \ + while (0) \ + UTEST_SURPRESS_WARNING_END +#else +#define UTEST_EXPECT(x, y, cond) \ + UTEST_SURPRESS_WARNING_BEGIN do { \ + if (!((x)cond(y))) { \ + UTEST_PRINTF("%s:%u: Failure (Expected " #cond " Actual)\n", __FILE__, \ + __LINE__); \ + *utest_result = UTEST_TEST_FAILURE; \ + } \ + } \ + while (0) \ + UTEST_SURPRESS_WARNING_END +#endif + +#define EXPECT_TRUE(x) \ + UTEST_SURPRESS_WARNING_BEGIN do { \ + if (!(x)) { \ + UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ + UTEST_PRINTF(" Expected : true\n"); \ + UTEST_PRINTF(" Actual : %s\n", (x) ? "true" : "false"); \ + *utest_result = UTEST_TEST_FAILURE; \ + } \ + } \ + while (0) \ + UTEST_SURPRESS_WARNING_END + +#define EXPECT_FALSE(x) \ + UTEST_SURPRESS_WARNING_BEGIN do { \ + if (x) { \ + UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ + UTEST_PRINTF(" Expected : false\n"); \ + UTEST_PRINTF(" Actual : %s\n", (x) ? "true" : "false"); \ + *utest_result = UTEST_TEST_FAILURE; \ + } \ + } \ + while (0) \ + UTEST_SURPRESS_WARNING_END + +#define EXPECT_EQ(x, y) UTEST_EXPECT(x, y, ==) +#define EXPECT_NE(x, y) UTEST_EXPECT(x, y, !=) +#define EXPECT_LT(x, y) UTEST_EXPECT(x, y, <) +#define EXPECT_LE(x, y) UTEST_EXPECT(x, y, <=) +#define EXPECT_GT(x, y) UTEST_EXPECT(x, y, >) +#define EXPECT_GE(x, y) UTEST_EXPECT(x, y, >=) + +#define EXPECT_STREQ(x, y) \ + UTEST_SURPRESS_WARNING_BEGIN do { \ + if (UTEST_NULL == x || UTEST_NULL == y || 0 != strcmp(x, y)) { \ + UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ + UTEST_PRINTF(" Expected : \"%s\"\n", x); \ + UTEST_PRINTF(" Actual : \"%s\"\n", y); \ + *utest_result = UTEST_TEST_FAILURE; \ + } \ + } \ + while (0) \ + UTEST_SURPRESS_WARNING_END + +#define EXPECT_STRNE(x, y) \ + UTEST_SURPRESS_WARNING_BEGIN do { \ + if (UTEST_NULL == x || UTEST_NULL == y || 0 == strcmp(x, y)) { \ + UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ + UTEST_PRINTF(" Expected : \"%s\"\n", x); \ + UTEST_PRINTF(" Actual : \"%s\"\n", y); \ + *utest_result = UTEST_TEST_FAILURE; \ + } \ + } \ + while (0) \ + UTEST_SURPRESS_WARNING_END + +#define EXPECT_STRNEQ(x, y, n) \ + UTEST_SURPRESS_WARNING_BEGIN do { \ + if (UTEST_NULL == x || UTEST_NULL == y || 0 != UTEST_STRNCMP(x, y, n)) { \ + UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ + UTEST_PRINTF(" Expected : \"%.*s\"\n", UTEST_CAST(int, n), x); \ + UTEST_PRINTF(" Actual : \"%.*s\"\n", UTEST_CAST(int, n), y); \ + *utest_result = UTEST_TEST_FAILURE; \ + } \ + } \ + while (0) \ + UTEST_SURPRESS_WARNING_END + +#define EXPECT_STRNNE(x, y, n) \ + UTEST_SURPRESS_WARNING_BEGIN do { \ + if (UTEST_NULL == x || UTEST_NULL == y || 0 == UTEST_STRNCMP(x, y, n)) { \ + UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ + UTEST_PRINTF(" Expected : \"%.*s\"\n", UTEST_CAST(int, n), x); \ + UTEST_PRINTF(" Actual : \"%.*s\"\n", UTEST_CAST(int, n), y); \ + *utest_result = UTEST_TEST_FAILURE; \ + } \ + } \ + while (0) \ + UTEST_SURPRESS_WARNING_END + +#define EXPECT_NEAR(x, y, epsilon) \ + UTEST_SURPRESS_WARNING_BEGIN do { \ + const double diff = \ + utest_fabs(UTEST_CAST(double, x) - UTEST_CAST(double, y)); \ + if (diff > UTEST_CAST(double, epsilon) || utest_isnan(diff)) { \ + UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ + UTEST_PRINTF(" Expected : %f\n", UTEST_CAST(double, x)); \ + UTEST_PRINTF(" Actual : %f\n", UTEST_CAST(double, y)); \ + *utest_result = UTEST_TEST_FAILURE; \ + } \ + } \ + while (0) \ + UTEST_SURPRESS_WARNING_END + +#if defined(__cplusplus) +#define EXPECT_EXCEPTION(x, exception_type) \ + UTEST_SURPRESS_WARNING_BEGIN do { \ + int exception_caught = 0; \ + try { \ + x; \ + } catch (const exception_type &) { \ + exception_caught = 1; \ + } catch (...) { \ + exception_caught = 2; \ + } \ + if (exception_caught != 1) { \ + UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ + UTEST_PRINTF(" Expected : %s exception\n", #exception_type); \ + UTEST_PRINTF(" Actual : %s\n", (exception_caught == 2) \ + ? "Unexpected exception" \ + : "No exception"); \ + *utest_result = UTEST_TEST_FAILURE; \ + } \ + } \ + while (0) \ + UTEST_SURPRESS_WARNING_END +#endif + +#if defined(__clang__) +#define UTEST_ASSERT(x, y, cond) \ + UTEST_SURPRESS_WARNING_BEGIN do { \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wlanguage-extension-token\"") \ + _Pragma("clang diagnostic ignored \"-Wc++98-compat-pedantic\"") \ + _Pragma("clang diagnostic ignored \"-Wfloat-equal\"") \ + UTEST_AUTO(x) xEval = (x); \ + UTEST_AUTO(y) yEval = (y); \ + if (!((xEval)cond(yEval))) { \ + _Pragma("clang diagnostic pop") \ + UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ + UTEST_PRINTF(" Expected : ("); \ + UTEST_PRINTF(#x ") " #cond " (" #y); \ + UTEST_PRINTF(")\n"); \ + UTEST_PRINTF(" Actual : "); \ + utest_type_printer(xEval); \ + UTEST_PRINTF(" vs "); \ + utest_type_printer(yEval); \ + UTEST_PRINTF("\n"); \ + *utest_result = UTEST_TEST_FAILURE; \ + return; \ + } \ + } \ + while (0) \ + UTEST_SURPRESS_WARNING_END +#elif defined(__GNUC__) +#define UTEST_ASSERT(x, y, cond) \ + UTEST_SURPRESS_WARNING_BEGIN do { \ + UTEST_AUTO(x) xEval = (x); \ + UTEST_AUTO(y) yEval = (y); \ + if (!((xEval)cond(yEval))) { \ + UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ + UTEST_PRINTF(" Expected : ("); \ + UTEST_PRINTF(#x ") " #cond " (" #y); \ + UTEST_PRINTF(")\n"); \ + UTEST_PRINTF(" Actual : "); \ + utest_type_printer(xEval); \ + UTEST_PRINTF(" vs "); \ + utest_type_printer(yEval); \ + UTEST_PRINTF("\n"); \ + *utest_result = UTEST_TEST_FAILURE; \ + return; \ + } \ + } \ + while (0) \ + UTEST_SURPRESS_WARNING_END +#else +#define UTEST_ASSERT(x, y, cond) \ + UTEST_SURPRESS_WARNING_BEGIN do { \ + if (!((x)cond(y))) { \ + UTEST_PRINTF("%s:%u: Failure (Expected " #cond " Actual)\n", __FILE__, \ + __LINE__); \ + *utest_result = UTEST_TEST_FAILURE; \ + return; \ + } \ + } \ + while (0) \ + UTEST_SURPRESS_WARNING_END +#endif + +#define ASSERT_TRUE(x) \ + UTEST_SURPRESS_WARNING_BEGIN do { \ + if (!(x)) { \ + UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ + UTEST_PRINTF(" Expected : true\n"); \ + UTEST_PRINTF(" Actual : %s\n", (x) ? "true" : "false"); \ + *utest_result = UTEST_TEST_FAILURE; \ + return; \ + } \ + } \ + while (0) \ + UTEST_SURPRESS_WARNING_END + +#define ASSERT_FALSE(x) \ + UTEST_SURPRESS_WARNING_BEGIN do { \ + if (x) { \ + UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ + UTEST_PRINTF(" Expected : false\n"); \ + UTEST_PRINTF(" Actual : %s\n", (x) ? "true" : "false"); \ + *utest_result = UTEST_TEST_FAILURE; \ + return; \ + } \ + } \ + while (0) \ + UTEST_SURPRESS_WARNING_END + +#define ASSERT_EQ(x, y) UTEST_ASSERT(x, y, ==) +#define ASSERT_NE(x, y) UTEST_ASSERT(x, y, !=) +#define ASSERT_LT(x, y) UTEST_ASSERT(x, y, <) +#define ASSERT_LE(x, y) UTEST_ASSERT(x, y, <=) +#define ASSERT_GT(x, y) UTEST_ASSERT(x, y, >) +#define ASSERT_GE(x, y) UTEST_ASSERT(x, y, >=) + +#define ASSERT_STREQ(x, y) \ + UTEST_SURPRESS_WARNING_BEGIN do { \ + if (UTEST_NULL == x || UTEST_NULL == y || 0 != strcmp(x, y)) { \ + UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ + UTEST_PRINTF(" Expected : \"%s\"\n", x); \ + UTEST_PRINTF(" Actual : \"%s\"\n", y); \ + *utest_result = UTEST_TEST_FAILURE; \ + return; \ + } \ + } \ + while (0) \ + UTEST_SURPRESS_WARNING_END + +#define ASSERT_STRNE(x, y) \ + UTEST_SURPRESS_WARNING_BEGIN do { \ + if (UTEST_NULL == x || UTEST_NULL == y || 0 == strcmp(x, y)) { \ + UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ + UTEST_PRINTF(" Expected : \"%s\"\n", x); \ + UTEST_PRINTF(" Actual : \"%s\"\n", y); \ + *utest_result = UTEST_TEST_FAILURE; \ + return; \ + } \ + } \ + while (0) \ + UTEST_SURPRESS_WARNING_END + +#define ASSERT_STRNEQ(x, y, n) \ + UTEST_SURPRESS_WARNING_BEGIN do { \ + if (UTEST_NULL == x || UTEST_NULL == y || 0 != UTEST_STRNCMP(x, y, n)) { \ + UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ + UTEST_PRINTF(" Expected : \"%.*s\"\n", UTEST_CAST(int, n), x); \ + UTEST_PRINTF(" Actual : \"%.*s\"\n", UTEST_CAST(int, n), y); \ + *utest_result = UTEST_TEST_FAILURE; \ + return; \ + } \ + } \ + while (0) \ + UTEST_SURPRESS_WARNING_END + +#define ASSERT_STRNNE(x, y, n) \ + UTEST_SURPRESS_WARNING_BEGIN do { \ + if (UTEST_NULL == x || UTEST_NULL == y || 0 == UTEST_STRNCMP(x, y, n)) { \ + UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ + UTEST_PRINTF(" Expected : \"%.*s\"\n", UTEST_CAST(int, n), x); \ + UTEST_PRINTF(" Actual : \"%.*s\"\n", UTEST_CAST(int, n), y); \ + *utest_result = UTEST_TEST_FAILURE; \ + return; \ + } \ + } \ + while (0) \ + UTEST_SURPRESS_WARNING_END + +#define ASSERT_NEAR(x, y, epsilon) \ + UTEST_SURPRESS_WARNING_BEGIN do { \ + const double diff = \ + utest_fabs(UTEST_CAST(double, x) - UTEST_CAST(double, y)); \ + if (diff > UTEST_CAST(double, epsilon) || utest_isnan(diff)) { \ + UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ + UTEST_PRINTF(" Expected : %f\n", UTEST_CAST(double, x)); \ + UTEST_PRINTF(" Actual : %f\n", UTEST_CAST(double, y)); \ + *utest_result = UTEST_TEST_FAILURE; \ + return; \ + } \ + } \ + while (0) \ + UTEST_SURPRESS_WARNING_END + +#if defined(__cplusplus) +#define ASSERT_EXCEPTION(x, exception_type) \ + UTEST_SURPRESS_WARNING_BEGIN do { \ + int exception_caught = 0; \ + try { \ + x; \ + } catch (const exception_type &) { \ + exception_caught = 1; \ + } catch (...) { \ + exception_caught = 2; \ + } \ + if (exception_caught != 1) { \ + UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ + UTEST_PRINTF(" Expected : %s exception\n", #exception_type); \ + UTEST_PRINTF(" Actual : %s\n", (exception_caught == 2) \ + ? "Unexpected exception" \ + : "No exception"); \ + *utest_result = UTEST_TEST_FAILURE; \ + return; \ + } \ + } \ + while (0) \ + UTEST_SURPRESS_WARNING_END +#endif + +#define UTEST(SET, NAME) \ + UTEST_EXTERN struct utest_state_s utest_state; \ + static void utest_run_##SET##_##NAME(int *utest_result); \ + static void utest_##SET##_##NAME(int *utest_result, size_t utest_index) { \ + (void)utest_index; \ + utest_run_##SET##_##NAME(utest_result); \ + } \ + UTEST_INITIALIZER(utest_register_##SET##_##NAME) { \ + const size_t index = utest_state.tests_length++; \ + const char *name_part = #SET "." #NAME; \ + const size_t name_size = strlen(name_part) + 1; \ + char *name = UTEST_PTR_CAST(char *, malloc(name_size)); \ + utest_state.tests = UTEST_PTR_CAST( \ + struct utest_test_state_s *, \ + utest_realloc(UTEST_PTR_CAST(void *, utest_state.tests), \ + sizeof(struct utest_test_state_s) * \ + utest_state.tests_length)); \ + if (utest_state.tests) { \ + utest_state.tests[index].func = &utest_##SET##_##NAME; \ + utest_state.tests[index].name = name; \ + utest_state.tests[index].index = 0; \ + } \ + UTEST_SNPRINTF(name, name_size, "%s", name_part); \ + } \ + void utest_run_##SET##_##NAME(int *utest_result) + +#define UTEST_F_SETUP(FIXTURE) \ + static void utest_f_setup_##FIXTURE(int *utest_result, \ + struct FIXTURE *utest_fixture) + +#define UTEST_F_TEARDOWN(FIXTURE) \ + static void utest_f_teardown_##FIXTURE(int *utest_result, \ + struct FIXTURE *utest_fixture) + +#if defined(__GNUC__) && __GNUC__ >= 8 && defined(__cplusplus) +#define UTEST_FIXTURE_SURPRESS_WARNINGS_BEGIN \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wclass-memaccess\"") +#define UTEST_FIXTURE_SURPRESS_WARNINGS_END _Pragma("GCC diagnostic pop") +#else +#define UTEST_FIXTURE_SURPRESS_WARNINGS_BEGIN +#define UTEST_FIXTURE_SURPRESS_WARNINGS_END +#endif + +#define UTEST_F(FIXTURE, NAME) \ + UTEST_FIXTURE_SURPRESS_WARNINGS_BEGIN \ + UTEST_EXTERN struct utest_state_s utest_state; \ + static void utest_f_setup_##FIXTURE(int *, struct FIXTURE *); \ + static void utest_f_teardown_##FIXTURE(int *, struct FIXTURE *); \ + static void utest_run_##FIXTURE##_##NAME(int *, struct FIXTURE *); \ + static void utest_f_##FIXTURE##_##NAME(int *utest_result, \ + size_t utest_index) { \ + struct FIXTURE fixture; \ + (void)utest_index; \ + memset(&fixture, 0, sizeof(fixture)); \ + utest_f_setup_##FIXTURE(utest_result, &fixture); \ + if (UTEST_TEST_PASSED != *utest_result) { \ + return; \ + } \ + utest_run_##FIXTURE##_##NAME(utest_result, &fixture); \ + utest_f_teardown_##FIXTURE(utest_result, &fixture); \ + } \ + UTEST_INITIALIZER(utest_register_##FIXTURE##_##NAME) { \ + const size_t index = utest_state.tests_length++; \ + const char *name_part = #FIXTURE "." #NAME; \ + const size_t name_size = strlen(name_part) + 1; \ + char *name = UTEST_PTR_CAST(char *, malloc(name_size)); \ + utest_state.tests = UTEST_PTR_CAST( \ + struct utest_test_state_s *, \ + utest_realloc(UTEST_PTR_CAST(void *, utest_state.tests), \ + sizeof(struct utest_test_state_s) * \ + utest_state.tests_length)); \ + utest_state.tests[index].func = &utest_f_##FIXTURE##_##NAME; \ + utest_state.tests[index].name = name; \ + UTEST_SNPRINTF(name, name_size, "%s", name_part); \ + } \ + UTEST_FIXTURE_SURPRESS_WARNINGS_END \ + void utest_run_##FIXTURE##_##NAME(int *utest_result, \ + struct FIXTURE *utest_fixture) + +#define UTEST_I_SETUP(FIXTURE) \ + static void utest_i_setup_##FIXTURE( \ + int *utest_result, struct FIXTURE *utest_fixture, size_t utest_index) + +#define UTEST_I_TEARDOWN(FIXTURE) \ + static void utest_i_teardown_##FIXTURE( \ + int *utest_result, struct FIXTURE *utest_fixture, size_t utest_index) + +#define UTEST_I(FIXTURE, NAME, INDEX) \ + UTEST_EXTERN struct utest_state_s utest_state; \ + static void utest_run_##FIXTURE##_##NAME##_##INDEX(int *, struct FIXTURE *); \ + static void utest_i_##FIXTURE##_##NAME##_##INDEX(int *utest_result, \ + size_t index) { \ + struct FIXTURE fixture; \ + memset(&fixture, 0, sizeof(fixture)); \ + utest_i_setup_##FIXTURE(utest_result, &fixture, index); \ + if (UTEST_TEST_PASSED != *utest_result) { \ + return; \ + } \ + utest_run_##FIXTURE##_##NAME##_##INDEX(utest_result, &fixture); \ + utest_i_teardown_##FIXTURE(utest_result, &fixture, index); \ + } \ + UTEST_INITIALIZER(utest_register_##FIXTURE##_##NAME##_##INDEX) { \ + size_t i; \ + utest_uint64_t iUp; \ + for (i = 0; i < (INDEX); i++) { \ + const size_t index = utest_state.tests_length++; \ + const char *name_part = #FIXTURE "." #NAME; \ + const size_t name_size = strlen(name_part) + 32; \ + char *name = UTEST_PTR_CAST(char *, malloc(name_size)); \ + utest_state.tests = UTEST_PTR_CAST( \ + struct utest_test_state_s *, \ + utest_realloc(UTEST_PTR_CAST(void *, utest_state.tests), \ + sizeof(struct utest_test_state_s) * \ + utest_state.tests_length)); \ + utest_state.tests[index].func = &utest_i_##FIXTURE##_##NAME##_##INDEX; \ + utest_state.tests[index].index = i; \ + utest_state.tests[index].name = name; \ + iUp = UTEST_CAST(utest_uint64_t, i); \ + UTEST_SNPRINTF(name, name_size, "%s/%" UTEST_PRIu64, name_part, iUp); \ + } \ + } \ + void utest_run_##FIXTURE##_##NAME##_##INDEX(int *utest_result, \ + struct FIXTURE *utest_fixture) + +UTEST_WEAK +double utest_fabs(double d); +UTEST_WEAK +double utest_fabs(double d) { + union { + double d; + utest_uint64_t u; + } both; + both.d = d; + both.u &= 0x7fffffffffffffffu; + return both.d; +} + +UTEST_WEAK +int utest_isnan(double d); +UTEST_WEAK +int utest_isnan(double d) { + union { + double d; + utest_uint64_t u; + } both; + both.d = d; + both.u &= 0x7fffffffffffffffu; + return both.u > 0x7ff0000000000000u; +} + +UTEST_WEAK +int utest_should_filter_test(const char *filter, const char *testcase); +UTEST_WEAK int utest_should_filter_test(const char *filter, + const char *testcase) { + if (filter) { + const char *filter_cur = filter; + const char *testcase_cur = testcase; + const char *filter_wildcard = UTEST_NULL; + + while (('\0' != *filter_cur) && ('\0' != *testcase_cur)) { + if ('*' == *filter_cur) { + /* store the position of the wildcard */ + filter_wildcard = filter_cur; + + /* skip the wildcard character */ + filter_cur++; + + while (('\0' != *filter_cur) && ('\0' != *testcase_cur)) { + if ('*' == *filter_cur) { + /* + we found another wildcard (filter is something like *foo*) so we + exit the current loop, and return to the parent loop to handle + the wildcard case + */ + break; + } else if (*filter_cur != *testcase_cur) { + /* otherwise our filter didn't match, so reset it */ + filter_cur = filter_wildcard; + } + + /* move testcase along */ + testcase_cur++; + + /* move filter along */ + filter_cur++; + } + + if (('\0' == *filter_cur) && ('\0' == *testcase_cur)) { + return 0; + } + + /* if the testcase has been exhausted, we don't have a match! */ + if ('\0' == *testcase_cur) { + return 1; + } + } else { + if (*testcase_cur != *filter_cur) { + /* test case doesn't match filter */ + return 1; + } else { + /* move our filter and testcase forward */ + testcase_cur++; + filter_cur++; + } + } + } + + if (('\0' != *filter_cur) || + (('\0' != *testcase_cur) && + ((filter == filter_cur) || ('*' != filter_cur[-1])))) { + /* we have a mismatch! */ + return 1; + } + } + + return 0; +} + +static UTEST_INLINE FILE *utest_fopen(const char *filename, const char *mode) { +#ifdef _MSC_VER + FILE *file; + if (0 == fopen_s(&file, filename, mode)) { + return file; + } else { + return UTEST_NULL; + } +#else + return fopen(filename, mode); +#endif +} + +static UTEST_INLINE int utest_main(int argc, const char *const argv[]); +int utest_main(int argc, const char *const argv[]) { + utest_uint64_t failed = 0; + utest_uint64_t skipped = 0; + size_t index = 0; + size_t *failed_testcases = UTEST_NULL; + size_t failed_testcases_length = 0; + size_t *skipped_testcases = UTEST_NULL; + size_t skipped_testcases_length = 0; + const char *filter = UTEST_NULL; + utest_uint64_t ran_tests = 0; + int enable_mixed_units = 0; + int random_order = 0; + utest_uint32_t seed = 0; + + enum colours { RESET, GREEN, RED, YELLOW }; + + const int use_colours = UTEST_COLOUR_OUTPUT(); + const char *colours[] = {"\033[0m", "\033[32m", "\033[31m", "\033[33m"}; + + if (!use_colours) { + for (index = 0; index < sizeof colours / sizeof colours[0]; index++) { + colours[index] = ""; + } + } + /* loop through all arguments looking for our options */ + for (index = 1; index < UTEST_CAST(size_t, argc); index++) { + /* Informational switches */ + const char help_str[] = "--help"; + const char list_str[] = "--list-tests"; + /* Test config switches */ + const char filter_str[] = "--filter="; + const char output_str[] = "--output="; + const char enable_mixed_units_str[] = "--enable-mixed-units"; + const char random_order_str[] = "--random-order"; + const char random_order_with_seed_str[] = "--random-order="; + + if (0 == UTEST_STRNCMP(argv[index], help_str, strlen(help_str))) { + printf("utest.h - the single file unit testing solution for C/C++!\n" + "Command line Options:\n" + " --help Show this message and exit.\n" + " --filter= Filter the test cases to run (EG. " + "MyTest*.a would run MyTestCase.a but not MyTestCase.b).\n" + " --list-tests List testnames, one per line. Output " + "names can be passed to --filter.\n"); + printf(" --output= Output an xunit XML file to the file " + "specified in .\n" + " --enable-mixed-units Enable the per-test output to contain " + "mixed units (s/ms/us/ns).\n" + " --random-order[=] Randomize the order that the tests are " + "ran in. If the optional argument is not provided, then a " + "random starting seed is used.\n"); + goto cleanup; + } else if (0 == + UTEST_STRNCMP(argv[index], filter_str, strlen(filter_str))) { + /* user wants to filter what test cases run! */ + filter = argv[index] + strlen(filter_str); + } else if (0 == + UTEST_STRNCMP(argv[index], output_str, strlen(output_str))) { + utest_state.output = utest_fopen(argv[index] + strlen(output_str), "w+"); + } else if (0 == UTEST_STRNCMP(argv[index], list_str, strlen(list_str))) { + for (index = 0; index < utest_state.tests_length; index++) { + UTEST_PRINTF("%s\n", utest_state.tests[index].name); + } + /* when printing the test list, don't actually run the tests */ + return 0; + } else if (0 == UTEST_STRNCMP(argv[index], enable_mixed_units_str, + strlen(enable_mixed_units_str))) { + enable_mixed_units = 1; + } else if (0 == UTEST_STRNCMP(argv[index], random_order_with_seed_str, + strlen(random_order_with_seed_str))) { + seed = + UTEST_CAST(utest_uint32_t, + strtoul(argv[index] + strlen(random_order_with_seed_str), + UTEST_NULL, 10)); + random_order = 1; + } else if (0 == UTEST_STRNCMP(argv[index], random_order_str, + strlen(random_order_str))) { + const utest_int64_t ns = utest_ns(); + + // Some really poor pseudo-random using the current time. I do this + // because I really want to avoid using C's rand() because that'd mean our + // random would be affected by any srand() usage by the user (which I + // don't want). + seed = UTEST_CAST(utest_uint32_t, ns >> 32) * 31 + + UTEST_CAST(utest_uint32_t, ns & 0xffffffff); + random_order = 1; + } + } + + if (random_order) { + // Use Fisher-Yates with the Durstenfield's version to randomly re-order the + // tests. + for (index = utest_state.tests_length; index > 1; index--) { + // For the random order we'll use PCG. + const utest_uint32_t state = seed; + const utest_uint32_t word = + ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u; + const utest_uint32_t next = + ((word >> 22u) ^ word) % UTEST_CAST(utest_uint32_t, index); + + // Swap the randomly chosen element into the last location. + const struct utest_test_state_s copy = utest_state.tests[index - 1]; + utest_state.tests[index - 1] = utest_state.tests[next]; + utest_state.tests[next] = copy; + + // Move the seed onwards. + seed = seed * 747796405u + 2891336453u; + } + } + + for (index = 0; index < utest_state.tests_length; index++) { + if (utest_should_filter_test(filter, utest_state.tests[index].name)) { + continue; + } + + ran_tests++; + } + + printf("%s[==========]%s Running %" UTEST_PRIu64 " test cases.\n", + colours[GREEN], colours[RESET], UTEST_CAST(utest_uint64_t, ran_tests)); + + if (utest_state.output) { + fprintf(utest_state.output, "\n"); + fprintf(utest_state.output, + "\n", + UTEST_CAST(utest_uint64_t, ran_tests)); + fprintf(utest_state.output, + "\n", + UTEST_CAST(utest_uint64_t, ran_tests)); + } + + for (index = 0; index < utest_state.tests_length; index++) { + int result = UTEST_TEST_PASSED; + utest_int64_t ns = 0; + + if (utest_should_filter_test(filter, utest_state.tests[index].name)) { + continue; + } + + printf("%s[ RUN ]%s %s\n", colours[GREEN], colours[RESET], + utest_state.tests[index].name); + + if (utest_state.output) { + fprintf(utest_state.output, "", + utest_state.tests[index].name); + } + + ns = utest_ns(); + errno = 0; +#if defined(__cplusplus) + UTEST_SURPRESS_WARNING_BEGIN + try { + utest_state.tests[index].func(&result, utest_state.tests[index].index); + } catch (const std::exception &err) { + printf(" Exception : %s\n", err.what()); + result = UTEST_TEST_FAILURE; + } catch (...) { + printf(" Exception : Unknown\n"); + result = UTEST_TEST_FAILURE; + } + UTEST_SURPRESS_WARNING_END +#else + utest_state.tests[index].func(&result, utest_state.tests[index].index); +#endif + ns = utest_ns() - ns; + + if (utest_state.output) { + fprintf(utest_state.output, "\n"); + } + + // Record the failing test. + if (UTEST_TEST_FAILURE == result) { + const size_t failed_testcase_index = failed_testcases_length++; + failed_testcases = UTEST_PTR_CAST( + size_t *, utest_realloc(UTEST_PTR_CAST(void *, failed_testcases), + sizeof(size_t) * failed_testcases_length)); + if (UTEST_NULL != failed_testcases) { + failed_testcases[failed_testcase_index] = index; + } + failed++; + } else if (UTEST_TEST_SKIPPED == result) { + const size_t skipped_testcase_index = skipped_testcases_length++; + skipped_testcases = UTEST_PTR_CAST( + size_t *, utest_realloc(UTEST_PTR_CAST(void *, skipped_testcases), + sizeof(size_t) * skipped_testcases_length)); + if (UTEST_NULL != skipped_testcases) { + skipped_testcases[skipped_testcase_index] = index; + } + skipped++; + } + + { + const char *const units[] = {"ns", "us", "ms", "s", UTEST_NULL}; + unsigned int unit_index = 0; + utest_int64_t time = ns; + + if (enable_mixed_units) { + for (unit_index = 0; UTEST_NULL != units[unit_index]; unit_index++) { + if (10000 > time) { + break; + } + + time /= 1000; + } + } + + if (UTEST_TEST_FAILURE == result) { + printf("%s[ FAILED ]%s %s (%" UTEST_PRId64 "%s)\n", colours[RED], + colours[RESET], utest_state.tests[index].name, time, + units[unit_index]); + } else if (UTEST_TEST_SKIPPED == result) { + printf("%s[ SKIPPED ]%s %s (%" UTEST_PRId64 "%s)\n", colours[YELLOW], + colours[RESET], utest_state.tests[index].name, time, + units[unit_index]); + } else { + printf("%s[ OK ]%s %s (%" UTEST_PRId64 "%s)\n", colours[GREEN], + colours[RESET], utest_state.tests[index].name, time, + units[unit_index]); + } + } + } + + printf("%s[==========]%s %" UTEST_PRIu64 " test cases ran.\n", colours[GREEN], + colours[RESET], ran_tests); + printf("%s[ PASSED ]%s %" UTEST_PRIu64 " tests.\n", colours[GREEN], + colours[RESET], ran_tests - failed - skipped); + + if (0 != skipped) { + printf("%s[ SKIPPED ]%s %" UTEST_PRIu64 " tests, listed below:\n", + colours[YELLOW], colours[RESET], skipped); + for (index = 0; index < skipped_testcases_length; index++) { + printf("%s[ SKIPPED ]%s %s\n", colours[YELLOW], colours[RESET], + utest_state.tests[skipped_testcases[index]].name); + } + } + + if (0 != failed) { + printf("%s[ FAILED ]%s %" UTEST_PRIu64 " tests, listed below:\n", + colours[RED], colours[RESET], failed); + for (index = 0; index < failed_testcases_length; index++) { + printf("%s[ FAILED ]%s %s\n", colours[RED], colours[RESET], + utest_state.tests[failed_testcases[index]].name); + } + } + + if (utest_state.output) { + fprintf(utest_state.output, "\n\n"); + } + +cleanup: + for (index = 0; index < utest_state.tests_length; index++) { + free(UTEST_PTR_CAST(void *, utest_state.tests[index].name)); + } + + free(UTEST_PTR_CAST(void *, skipped_testcases)); + free(UTEST_PTR_CAST(void *, failed_testcases)); + free(UTEST_PTR_CAST(void *, utest_state.tests)); + + if (utest_state.output) { + fclose(utest_state.output); + } + + return UTEST_CAST(int, failed); +} + +/* + we need, in exactly one source file, define the global struct that will hold + the data we need to run utest. This macro allows the user to declare the + data without having to use the UTEST_MAIN macro, thus allowing them to write + their own main() function. +*/ +#define UTEST_STATE() struct utest_state_s utest_state = {0, 0, 0} + +/* + define a main() function to call into utest.h and start executing tests! A + user can optionally not use this macro, and instead define their own main() + function and manually call utest_main. The user must, in exactly one source + file, use the UTEST_STATE macro to declare a global struct variable that + utest requires. +*/ +#define UTEST_MAIN() \ + UTEST_STATE(); \ + int main(int argc, const char *const argv[]) { \ + return utest_main(argc, argv); \ + } + +#endif /* SHEREDOM_UTEST_H_INCLUDED */