#include #include #include "minitest.h" #include "intmath.h" int int_pown(unsigned int base, unsigned int exp) { int res = 1; while (exp > 0) { res *= base; exp--; } return res; } int int_pow10(unsigned int exp) { return int_pown(10, exp); } int int_floor(int x, int precision) { int sign = (x < 0) * -2 + 1; x *= sign; int iterations = 0; while (x > int_pow10(precision)) { x /= 10; iterations++; } return (x + (sign == -1)) * int_pow10(iterations) * sign; } int int_ceil(int x, int precision) { return -int_floor(-x, precision); } int int_min(int a, int b) { return (a < b) * a + (b <= a) * b; } int int_max(int a, int b) { return (a > b) * a + (b >= a) * b; } /* Returns `int x` floored to `unsigned int precision` as an `exp_notated` * value with given `unsigned int base`. * * `unsigned int base` affects the precision. */ exp_notated int_floored_exponent_notation_base ( int x, unsigned int precision, unsigned int base ) { int original = x; int sign = (x < 0) * -2 + 1; x *= sign; int i = 0; while (x >= int_pown(base, precision)) { x /= base; i++; } x += (sign == -1) && (original != -x * int_pown(base, i)); if (x >= int_pown(base, precision)) { x /= base; i++; } x *= sign; exp_notated res = { .mantissa = x, .exponent = i, .base = base }; return res; } exp_notated int_floored_exponent_notation(int x, unsigned int precision) { return int_floored_exponent_notation_base(x, precision, 10); } exp_notated int_ceiled_exponent_notation_base( int x, unsigned int precision, unsigned int base ) { exp_notated res = int_floored_exponent_notation_base(-x, precision, base); res.mantissa = -res.mantissa; return res; } exp_notated int_ceiled_exponent_notation(int x, unsigned int precision) { return int_ceiled_exponent_notation_base(x, precision, 10); } int exp_notated_to_int(exp_notated x) { return x.mantissa * int_pown(x.base, x.exponent); } /* Returns the appropriate binary prefix for `exp_notated x`. `x` must have * `1024` as its base, it should not be greater than `1023*1024^10` and it * should be floored/ceiled (ie. the mantissa should not be further away from * zero than `1023`). Failing to meet these conditions results in the function * returning `NULL`. */ const char* binary_prefix(exp_notated x) { if ( (x.base != 1024) || (x.exponent > 10) || (x.mantissa > 1023) || (x.mantissa < -1023) ) { return NULL; } switch (x.exponent) { case 0: return ""; case 1: return "Ki"; case 2: return "Mi"; case 3: return "Gi"; case 4: return "Ti"; case 5: return "Pi"; case 6: return "Ei"; case 7: return "Zi"; case 8: return "Yi"; case 9: return "Ri"; default: return "Qi"; } } /* Returns the a string describing the argument `int x` floored to the * appropriate multiple of 1024 followed with a binary prefix (Ki, Mi, Gi...). * The string is dynamically allocated and should be free'd. * * Returns NULL on malloc error. */ char* int_floored_with_binary_prefix(int x) { exp_notated x_exp = int_floored_exponent_notation_base(x, 1, 1024); if (x_exp.exponent > 10) /* quebi-=1024^10 greatest defined prefix */ x_exp.mantissa *= int_pow10(x_exp.exponent - 10); const char* prefix = binary_prefix(x_exp); size_t bufsize = 5 + /* len("-1024") */ int_max(0, x_exp.exponent - 10) + /* length of possible zeros */ 4; /* len(" Gi\0") */ char* res = malloc(bufsize); if (res == NULL) return NULL; sprintf(res, "%d", x_exp.mantissa); if (strcmp(prefix, "") != 0) strcat(res, " "); strcat(res, prefix); return res; } char* test_pown() { mt_assert_eq(int_pown(2, 5), 32); mt_assert_eq(int_pown(3, 3), 27); mt_assert_eq(int_pown(9, 0), 1); return 0; } char* test_pow10() { mt_assert_eq(int_pow10(3), 1000); mt_assert_eq(int_pow10(0), 1); return 0; } char* test_floor() { mt_assert_eq(int_floor(34128, 2), 34000); mt_assert_eq(int_floor(-34128, 3), -34200); mt_assert_eq(int_floor(-9999, 3), -10000); mt_assert_eq(int_floor(9999, 3), 9990); mt_assert_eq(int_floor(0, 1), 0); return 0; } char* test_ceil() { mt_assert_eq(int_ceil(44212, 3), 44300); mt_assert_eq(int_ceil(-44212, 3), -44200); mt_assert_eq(int_ceil(-9999, 3), -9990); mt_assert_eq(int_ceil(9999, 3), 10000); mt_assert_eq(int_ceil(0, 1), 0); return 0; } char* test_min() { mt_assert_eq(int_min(2, 6), 2); mt_assert_eq(int_min(4, 1), 1); mt_assert_eq(int_min(10, 10), 1); return 0; } char* test_max() { mt_assert_eq(int_max(3, 7), 7); mt_assert_eq(int_min(4, 1), 4); mt_assert_eq(int_min(10, 10), 10); return 0; } char* test_floored_exponent_notation() { const size_t bufsize = 128; char* msg = malloc(bufsize); exp_notated res = int_floored_exponent_notation(9489345, 3); snprintf(msg, bufsize, "int_floored_exponent_notation(9489345, 3): expected 948*10^4, got %d*%d^%d", res.mantissa, res.base, res.exponent ); mt_assert( res.mantissa == 948 && res.base == 10 && res.exponent == 4 , msg); res = int_floored_exponent_notation(-88, 1); snprintf(msg, bufsize, "int_floored_exponent_notation(-88, 1): expected -9*10^1, got %d*%d^%d", res.mantissa, res.base, res.exponent ); mt_assert( res.mantissa == -9 && res.base == 10 && res.exponent == 1 , msg); res = int_floored_exponent_notation(-99, 1); snprintf(msg, bufsize, "int_floored_exponent_notation(-99, 1): expected -1*10^2, got %d*%d^%d", res.mantissa, res.base, res.exponent ); mt_assert( res.mantissa == -1 && res.base == 10 && res.exponent == 2 , msg); res = int_floored_exponent_notation_base(3145733, 1, 1024); snprintf(msg, bufsize, "int_floored_exponent_notation_base(3145733, 1, 1024): expected 3*1024^2, got %d*%d^%d", res.mantissa, res.base, res.exponent ); mt_assert( res.mantissa == 3 && res.base == 1024 && res.exponent == 2 , msg); res = int_floored_exponent_notation_base(-1022, 1, 1024); snprintf(msg, bufsize, "int_floored_exponent_notation(-1022, 1, 1024): expected -1022*1024^0, got %d*%d^%d", res.mantissa, res.base, res.exponent ); mt_assert( res.mantissa == -1022 && res.base == 1024 && res.exponent == 0 , msg); res = int_floored_exponent_notation_base(-1023, 1, 1024); snprintf(msg, bufsize, "int_floored_exponent_notation(-1023, 1, 1024): expected -1023*1024^0, got %d*%d^%d", res.mantissa, res.base, res.exponent ); mt_assert( res.mantissa == -1023 && res.base == 1024 && res.exponent == 0 , msg); res = int_floored_exponent_notation_base(-1024, 1, 1024); snprintf(msg, bufsize, "int_floored_exponent_notation(-1024, 1, 1024): expected -1*1024^1, got %d*%d^%d", res.mantissa, res.base, res.exponent ); mt_assert( res.mantissa == -1 && res.base == 1024 && res.exponent == 1 , msg); free(msg); return 0; } char* test_ceiled_exponent_notation() { const size_t bufsize = 128; char* msg = malloc(bufsize); exp_notated res = int_ceiled_exponent_notation(9489345, 3); snprintf(msg, bufsize, "int_ceiled_exponent_notation(9489345, 3): expected 949*10^4, got %d*%d^%d", res.mantissa, res.base, res.exponent ); mt_assert( res.mantissa == 949 && res.base == 10 && res.exponent == 4 , msg); res = int_ceiled_exponent_notation(-99, 1); snprintf(msg, bufsize, "int_ceiled_exponent_notation(-99, 1): expected -9*10^1, got %d*%d^%d", res.mantissa, res.base, res.exponent ); mt_assert( res.mantissa == -9 && res.base == 10 && res.exponent == 1 , msg); res = int_ceiled_exponent_notation(-100, 1); snprintf(msg, bufsize, "int_ceiled_exponent_notation(-100, 1): expected -1*10^2, got %d*%d^%d", res.mantissa, res.base, res.exponent ); mt_assert( res.mantissa == -1 && res.base == 10 && res.exponent == 2 , msg); res = int_ceiled_exponent_notation_base(3145733, 1, 1024); snprintf(msg, bufsize, "int_ceiled_exponent_notation_base(3145733, 1, 1024): expected 4*1024^2, got %d*%d^%d", res.mantissa, res.base, res.exponent ); mt_assert( res.mantissa == 4 && res.base == 1024 && res.exponent == 2 , msg); res = int_ceiled_exponent_notation_base(-1023, 1, 1024); snprintf(msg, bufsize, "int_ceiled_exponent_notation(-1023, 1, 1024): expected -1023*1024^0, got %d*%d^%d", res.mantissa, res.base, res.exponent ); mt_assert( res.mantissa == -1023 && res.base == 1024 && res.exponent == 0 , msg); res = int_ceiled_exponent_notation_base(-1024, 1, 1024); snprintf(msg, bufsize, "int_ceiled_exponent_notation(-1024, 1, 1024): expected -1*1024^1, got %d*%d^%d", res.mantissa, res.base, res.exponent ); mt_assert( res.mantissa == -1 && res.base == 1024 && res.exponent == 1 , msg); free(msg); return 0; } char* test_exponent_notated_to_int() { exp_notated x = { .mantissa = 3, .exponent = 2, .base = 10 }; mt_assert_eq(300, exp_notated_to_int(x)); return 0; } char* test_floored_with_binary_prefix() { const size_t bufsize = 64; char* msg = malloc(bufsize); char* res = int_floored_with_binary_prefix(1026); snprintf(msg, bufsize, "1026 yielded `%s` instead of `1 Ki`", res); mt_assert(strcmp( res, "1 Ki" ) == 0, msg); free(res); res = int_floored_with_binary_prefix(-1023); snprintf(msg, bufsize, "-1023 yielded `%s` instead of `-1023`", res); mt_assert(strcmp( res, "-1023" ) == 0, msg); free(res); res = int_floored_with_binary_prefix(-1026); snprintf(msg, bufsize, "-1026 yielded `%s` instead of `-2 Ki`", res); mt_assert(strcmp( res, "-2 Ki" ) == 0, msg); free(res); res = int_floored_with_binary_prefix(1049088); snprintf(msg, bufsize, "1049088 yielded `%s` instead of `1 Mi`", res); mt_assert(strcmp( res, "1 Mi" ) == 0, msg); free(res); res = int_floored_with_binary_prefix(5243392); snprintf(msg, bufsize, "5243392 yielded `%s` instead of `5 Mi`", res); mt_assert(strcmp( res, "5 Mi" ) == 0, msg); free(res); free(msg); return 0; } void intmath_tests() { mt_run_test(test_pown); mt_run_test(test_pow10); mt_run_test(test_floor); mt_run_test(test_ceil); mt_run_test(test_min); mt_run_test(test_max); mt_run_test(test_floored_exponent_notation); mt_run_test(test_ceiled_exponent_notation); mt_run_test(test_exponent_notated_to_int); mt_run_test(test_floored_with_binary_prefix); }