%% options copyright owner = Dirk Krause copyright year = 2014 license = bsd %% header /** @file dk3maul.h Mathematical operations on unsigned long, unsigned long long, uintmax_t and dk3_um_t. */ #include #include #if DK3_HAVE_SYS_TYPES_H #include #endif #if DK3_HAVE_STDINT #include #endif #if DK3_HAVE_INTTYPES_H #include #endif #if DK3_HAVE_LIMITS_H #include #endif #if DK3_HAVE_MATH_H #include #endif #if DK3_HAVE_FLOAT_H #include #endif #ifdef __cplusplus extern "C" { #endif /** Addition. @param a Left operand. @param b Right operand. @param ec Pointer to error code variable, may be NULL. The variable may be set to DK3_ERROR_MATH_OVERFLOW when returning. @return Summary of a and b. */ unsigned long dk3ma_ul_add_ok(unsigned long a, unsigned long b, int *ec); /** Substraction. @param a Left operand. @param b Right operand. @param ec Pointer to error code variable, may be NULL. The variable may be set to DK3_ERROR_MATH_OVERFLOW when returning. @return Difference of a and b. */ unsigned long dk3ma_ul_sub_ok(unsigned long a, unsigned long b, int *ec); /** Multiplication. @param a Left operand. @param b Right operand. @param ec Pointer to error code variable, may be NULL. The variable may be set to DK3_ERROR_MATH_OVERFLOW when returning. @return Product of a and b. */ unsigned long dk3ma_ul_mul_ok(unsigned long a, unsigned long b, int *ec); /** Division. @param a Left operand (nominator). @param b Right operand (denominator). @param ec Pointer to error code variable, may be NULL. The variable may be set to DK3_ERROR_MATH_OVERFLOW or DK3_ERROR_MATH_DIVZERO when returning. @return Fraction of a and b. */ unsigned long dk3ma_ul_div_ok(unsigned long a, unsigned long b, int *ec); /** Greatest common divisor. @param a Left operand. @param b Right operand. The variable may be set to DK3_ERROR_MATH_OVERFLOW when returning. @return Greatest common divisor of a and b. */ unsigned long dk3ma_ul_gcd(unsigned long a, unsigned long b); #if DK3_HAVE_LONG_LONG /** Addition. @param a Left operand. @param b Right operand. @param ec Pointer to error code variable, may be NULL. The variable may be set to DK3_ERROR_MATH_OVERFLOW when returning. @return Summary of a and b. */ unsigned long long dk3ma_ull_add_ok(unsigned long long a, unsigned long long b, int *ec); /** Substraction. @param a Left operand. @param b Right operand. @param ec Pointer to error code variable, may be NULL. The variable may be set to DK3_ERROR_MATH_OVERFLOW when returning. @return Difference of a and b. */ unsigned long long dk3ma_ull_sub_ok(unsigned long long a, unsigned long long b, int *ec); /** Multiplication. @param a Left operand. @param b Right operand. @param ec Pointer to error code variable, may be NULL. The variable may be set to DK3_ERROR_MATH_OVERFLOW when returning. @return Product of a and b. */ unsigned long long dk3ma_ull_mul_ok(unsigned long long a, unsigned long long b, int *ec); /** Division. @param a Left operand (nominator). @param b Right operand (denominator). @param ec Pointer to error code variable, may be NULL. The variable may be set to DK3_ERROR_MATH_OVERFLOW or DK3_ERROR_MATH_DIVZERO when returning. @return Fraction of a and b. */ unsigned long long dk3ma_ull_div_ok(unsigned long long a, unsigned long long b, int *ec); /** Greatest common divisor. @param a Left operand. @param b Right operand. The variable may be set to DK3_ERROR_MATH_OVERFLOW when returning. @return Greatest common divisor of a and b. */ unsigned long long dk3ma_ull_gcd(unsigned long long a, unsigned long long b); #endif #if DK3_HAVE_INTMAX_T /** Addition. @param a Left operand. @param b Right operand. @param ec Pointer to error code variable, may be NULL. The variable may be set to DK3_ERROR_MATH_OVERFLOW when returning. @return Summary of a and b. */ uintmax_t dk3ma_uintmax_t_add_ok(uintmax_t a, uintmax_t b, int *ec); /** Substraction. @param a Left operand. @param b Right operand. @param ec Pointer to error code variable, may be NULL. The variable may be set to DK3_ERROR_MATH_OVERFLOW when returning. @return Difference of a and b. */ uintmax_t dk3ma_uintmax_t_sub_ok(uintmax_t a, uintmax_t b, int *ec); /** Multiplication. @param a Left operand. @param b Right operand. @param ec Pointer to error code variable, may be NULL. The variable may be set to DK3_ERROR_MATH_OVERFLOW when returning. @return Product of a and b. */ uintmax_t dk3ma_uintmax_t_mul_ok(uintmax_t a, uintmax_t b, int *ec); /** Division. @param a Left operand (nominator). @param b Right operand (denominator). @param ec Pointer to error code variable, may be NULL. The variable may be set to DK3_ERROR_MATH_OVERFLOW or DK3_ERROR_MATH_DIVZERO when returning. @return Fraction of a and b. */ uintmax_t dk3ma_uintmax_t_div_ok(uintmax_t a, uintmax_t b, int *ec); /** Greatest common divisor. @param a Left operand. @param b Right operand. The variable may be set to DK3_ERROR_MATH_OVERFLOW when returning. @return Greatest common divisor of a and b. */ uintmax_t dk3ma_uintmax_t_gcd(uintmax_t a, uintmax_t b); #endif /** Addition. @param a Left operand. @param b Right operand. @param ec Pointer to error code variable, may be NULL. The variable may be set to DK3_ERROR_MATH_OVERFLOW when returning. @return Summary of a and b. */ dk3_um_t dk3ma_um_add_ok(dk3_um_t a, dk3_um_t b, int *ec); /** Substraction. @param a Left operand. @param b Right operand. @param ec Pointer to error code variable, may be NULL. The variable may be set to DK3_ERROR_MATH_OVERFLOW when returning. @return Difference of a and b. */ dk3_um_t dk3ma_um_sub_ok(dk3_um_t a, dk3_um_t b, int *ec); /** Multiplication. @param a Left operand. @param b Right operand. @param ec Pointer to error code variable, may be NULL. The variable may be set to DK3_ERROR_MATH_OVERFLOW when returning. @return Product of a and b. */ dk3_um_t dk3ma_um_mul_ok(dk3_um_t a, dk3_um_t b, int *ec); /** Division. @param a Left operand (nominator). @param b Right operand (denominator). @param ec Pointer to error code variable, may be NULL. The variable may be set to DK3_ERROR_MATH_OVERFLOW or DK3_ERROR_MATH_DIVZERO when returning. @return Fraction of a and b. */ dk3_um_t dk3ma_um_div_ok(dk3_um_t a, dk3_um_t b, int *ec); /** Greatest common divisor. @param a Left operand. @param b Right operand. The variable may be set to DK3_ERROR_MATH_OVERFLOW when returning. @return Greatest common divisor of a and b. */ dk3_um_t dk3ma_um_gcd(dk3_um_t a, dk3_um_t b); /** Convert dk3_um_t to size_t. @param um Positive dk3_um_t value to convert. @param ec Pointer to error code variable, may be NULL. Set to DK3_ERROR_MATH_OVERFLOW on conversion errors. @return Positive number on success, 0 on error (significant bits lost in conversion). */ size_t dk3ma_um_to_sz(dk3_um_t um, int *ec); #ifdef __cplusplus } #endif %% module #include "dk3ma.h" $!trace-include unsigned long dk3ma_ul_add_ok(unsigned long a, unsigned long b, int *ec) { $? "+ dk3ma_ul_add_ok %lu %lu", a, b if (NULL != ec) { $? "! overflow" if ((DK3_UL_MAX - a) < b) { *ec = DK3_ERROR_MATH_OVERFLOW; } } $? "- dk3ma_ul_add_ok %lu", (a + b) return (a + b); } unsigned long dk3ma_ul_sub_ok(unsigned long a, unsigned long b, int *ec) { $? "+ dk3ma_ul_sub_ok %lu %lu", a, b if (NULL != ec) { if (b > a) { $? "! overflow" *ec = DK3_ERROR_MATH_OVERFLOW; } } return (a - b); } unsigned long dk3ma_ul_mul_ok(unsigned long a, unsigned long b, int *ec) { $? "+ dk3ma_ul_mul_ok %lu %lu", a, b if (NULL != ec) { if (0UL != a) { if ((DK3_UL_MAX / a) < b) { $? "! overflow" *ec = DK3_ERROR_MATH_OVERFLOW; } } } $? "- dk3ma_ul_mul_ok %lu", (a * b) return (a * b); } unsigned long dk3ma_ul_div_ok(unsigned long a, unsigned long b, int *ec) { unsigned long back = (unsigned long)0; $? "+ dk3ma_ul_div_ok %lu %lu", a, b if (0UL != b) { back = a / b; } else { if (NULL != ec) { $? "! division by zero" *ec = DK3_ERROR_MATH_DIVZERO; } back = DK3_UL_MAX; } $? "- dk3ma_ul_div_ok %lu", back return back; } unsigned long dk3ma_ul_gcd(unsigned long a, unsigned long b) { unsigned long h; $? "+ dk3ma_ul_gcd %lu %lu", a, b while (0UL < b) { h = a % b; a = b; b = h; } if (0UL == a) { a = 1UL; } $? "- dk3ma_ul_gcd %lu", a return a; } #if DK3_HAVE_LONG_LONG unsigned long long dk3ma_ull_add_ok(unsigned long long a, unsigned long long b, int *ec) { #if DK3_ON_WINDOWS $? "+ dk3ma_ull_add_ok %I64u %I64u", a, b #else $? "+ dk3ma_ull_add_ok %llu %llu", a, b #endif if (NULL != ec) { if ((DK3_ULL_MAX - a) < b) { $? "! overflow" *ec = DK3_ERROR_MATH_OVERFLOW; } } #if DK3_ON_WINDOWS $? "- dk3ma_ull_add_ok %I64u", (a + b) #else $? "- dk3ma_ull_add_ok %llu", (a + b) #endif return (a + b); } unsigned long long dk3ma_ull_sub_ok(unsigned long long a, unsigned long long b, int *ec) { #if DK3_ON_WINDOWS $? "+ dk3ma_ull_sub_ok %I64u %I64u", a, b #else $? "+ dk3ma_ull_sub_ok %llu %llu", a, b #endif if (NULL != ec) { if (b > a) { $? "! overflow" *ec = DK3_ERROR_MATH_OVERFLOW; } } #if DK3_ON_WINDOWS $? "- dk3ma_ull_sub_ok %I64u", (a - b) #else $? "- dk3ma_ull_sub_ok %llu", (a - b) #endif return (a - b); } unsigned long long dk3ma_ull_mul_ok(unsigned long long a, unsigned long long b, int *ec) { #if DK3_ON_WINDOWS $? "+ dk3ma_ull_mul_ok %I64u %I64u", a, b #else $? "+ dk3ma_ull_mul_ok %llu %llu", a, b #endif if (NULL != ec) { if (0ULL != a) { $? "! overflow" if ((DK3_ULL_MAX / a) < b) { *ec = DK3_ERROR_MATH_OVERFLOW; } } } #if DK3_ON_WINDOWS $? "- dk3ma_ull_mul_ok %I64u", (a * b) #else $? "- dk3ma_ull_mul_ok %llu", (a * b) #endif return (a * b); } unsigned long long dk3ma_ull_div_ok(unsigned long long a, unsigned long long b, int *ec) { unsigned long long back = (unsigned long long)0; #if DK3_ON_WINDOWS $? "+ dk3ma_ull_div_ok %I64u %I64u", a, b #else $? "+ dk3ma_ull_div_ok %llu %llu", a, b #endif if (0ULL != b) { back = a / b; } else { if (NULL != ec) { $? "! division by zero" *ec = DK3_ERROR_MATH_DIVZERO; } back = DK3_ULL_MAX; } #if DK3_ON_WINDOWS $? "- dk3ma_ull_div_ok %I64u", back #else $? "- dk3ma_ull_div_ok %llu", back #endif return back; } unsigned long long dk3ma_ull_gcd(unsigned long long a, unsigned long long b) { unsigned long long h; #if DK3_ON_WINDOWS $? "+ dk3ma_ull_gcd_ok %I64u %I64u", a, b #else $? "+ dk3ma_ull_gcd_ok %llu %llu", a, b #endif while (0ULL < b) { h = a % b; a = b; b = h; } if (0ULL == a) { a = 1ULL; } #if DK3_ON_WINDOWS $? "- dk3ma_ull_add_ok %I64u", a #else $? "- dk3ma_ull_add_ok %llu", a #endif return a; } #endif #if DK3_HAVE_INTMAX_T uintmax_t dk3ma_uintmax_t_add_ok(uintmax_t a, uintmax_t b, int *ec) { #if DK3_ON_WINDOWS $? "+ dk3ma_uintmax_t_add_ok %I64u %I64u", a, b #else $? "+ dk3ma_uintmax_t_add_ok %ju %ju", a, b #endif if (NULL != ec) { if ((DK3_UINTMAX_T_MAX - a) < b) { $? "! overflow" *ec = DK3_ERROR_MATH_OVERFLOW; } } #if DK3_ON_WINDOWS $? "- dk3ma_uintmax_t_add_ok %I64u", (a + b) #else $? "- dk3ma_uintmax_t_add_ok %ju", (a + b) #endif return (a + b); } uintmax_t dk3ma_uintmax_t_sub_ok(uintmax_t a, uintmax_t b, int *ec) { #if DK3_ON_WINDOWS $? "+ dk3ma_uintmax_t_sub_ok %I64u %I64u", a, b #else $? "+ dk3ma_uintmax_t_sub_ok %ju %ju", a, b #endif if (NULL != ec) { if (b > a) { $? "! overflow" *ec = DK3_ERROR_MATH_OVERFLOW; } } #if DK3_ON_WINDOWS $? "- dk3ma_uintmax_t_sub_ok %I64u", (a - b) #else $? "- dk3ma_uintmax_t_sub_ok %ju", (a - b) #endif return (a - b); } uintmax_t dk3ma_uintmax_t_mul_ok(uintmax_t a, uintmax_t b, int *ec) { #if DK3_ON_WINDOWS $? "+ dk3ma_uintmax_t_mul_ok %I64u %I64u", a, b #else $? "+ dk3ma_uintmax_t_mul_ok %ju %ju", a, b #endif if (NULL != ec) { if (DK3_UINTMAX_T_0 != a) { if ((DK3_UINTMAX_T_MAX / a) < b) { $? "! overflow" *ec = DK3_ERROR_MATH_OVERFLOW; } } } #if DK3_ON_WINDOWS $? "- dk3ma_uintmax_t_mul_ok %I64u", (a * b) #else $? "- dk3ma_uintmax_t_mul_ok %ju", (a * b) #endif return (a * b); } uintmax_t dk3ma_uintmax_t_div_ok(uintmax_t a, uintmax_t b, int *ec) { uintmax_t back = (uintmax_t)0; #if DK3_ON_WINDOWS $? "+ dk3ma_uintmax_t_div_ok %I64u %I64u", a, b #else $? "+ dk3ma_uintmax_t_div_ok %ju %ju", a, b #endif if (DK3_UINTMAX_T_0 != b) { back = a / b; } else { if (NULL != ec) { $? "! division by zero" *ec = DK3_ERROR_MATH_DIVZERO; } back = DK3_UINTMAX_T_MAX; } #if DK3_ON_WINDOWS $? "- dk3ma_uintmax_t_div_ok %I64u", back #else $? "- dk3ma_uintmax_t_div_ok %ju", back #endif return back; } uintmax_t dk3ma_uintmax_t_gcd(uintmax_t a, uintmax_t b) { uintmax_t h; #if DK3_ON_WINDOWS $? "+ dk3ma_uintmax_t_gcd_ok %I64u %I64u", a, b #else $? "+ dk3ma_uintmax_t_gcd_ok %ju %ju", a, b #endif while (DK3_UINTMAX_T_0 < b) { h = a % b; a = b; b = h; } if (DK3_UINTMAX_T_0 == a) { a = DK3_UINTMAX_T_1; } #if DK3_ON_WINDOWS $? "- dk3ma_uintmax_t_gcd_ok %I64u", a #else $? "- dk3ma_uintmax_t_gcd_ok %ju", a #endif return a; } #endif dk3_um_t dk3ma_um_add_ok(dk3_um_t a, dk3_um_t b, int *ec) { #if DK3_HAVE_INTMAX_T return (dk3ma_uintmax_t_add_ok(a, b, ec)); #else #if DK3_HAVE_LONG_LONG return (dk3ma_ull_add_ok(a, b, ec)); #else return (dk3ma_ul_add_ok(a, b, ec)); #endif #endif } dk3_um_t dk3ma_um_sub_ok(dk3_um_t a, dk3_um_t b, int *ec) { #if DK3_HAVE_INTMAX_T return (dk3ma_uintmax_t_sub_ok(a, b, ec)); #else #if DK3_HAVE_LONG_LONG return (dk3ma_ull_sub_ok(a, b, ec)); #else return (dk3ma_ul_sub_ok(a, b, ec)); #endif #endif } dk3_um_t dk3ma_um_mul_ok(dk3_um_t a, dk3_um_t b, int *ec) { #if DK3_HAVE_INTMAX_T return (dk3ma_uintmax_t_mul_ok(a, b, ec)); #else #if DK3_HAVE_LONG_LONG return (dk3ma_ull_mul_ok(a, b, ec)); #else return (dk3ma_ul_mul_ok(a, b, ec)); #endif #endif } dk3_um_t dk3ma_um_div_ok(dk3_um_t a, dk3_um_t b, int *ec) { #if DK3_HAVE_INTMAX_T return (dk3ma_uintmax_t_div_ok(a, b, ec)); #else #if DK3_HAVE_LONG_LONG return (dk3ma_ull_div_ok(a, b, ec)); #else return (dk3ma_ul_div_ok(a, b, ec)); #endif #endif } dk3_um_t dk3ma_um_gcd(dk3_um_t a, dk3_um_t b) { #if DK3_HAVE_INTMAX_T return (dk3ma_uintmax_t_gcd(a, b)); #else #if DK3_HAVE_LONG_LONG return (dk3ma_ull_gcd(a, b)); #else return (dk3ma_ul_gcd(a, b)); #endif #endif } size_t dk3ma_um_to_sz(dk3_um_t um, int *ec) { size_t back = 0; #if DK3_SIZEOF_SIZE_T >= DK3_SIZEOF_UM back = (size_t)um; #else if ((dk3_um_t)DK3_SIZE_T_MAX >= um) { back = (size_t)um; } else { if (NULL != ec) { *ec = DK3_ERROR_MATH_OVERFLOW; } } #endif return back; }