%% options copyright owner = Dirk Krause copyright year = 2014 license = bsd %% header /** @file dk3mal.h Mathematical operations on long, long long, intmax_t and dk3_im_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 /** Absolute value. @param a Original value. @param ec Pointer to error code variable, may be NULL. @return Non-negative value on success, DK3_L_MAX and ec = DK3_ERROR_MATH_OVERFLOW on error. */ long dk3ma_l_abs_ok(long a, int *ec); /** 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. */ long dk3ma_l_add_ok(long a, 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. */ long dk3ma_l_sub_ok(long a, 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. */ long dk3ma_l_mul_ok(long a, 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. */ long dk3ma_l_div_ok(long a, long b, int *ec); /** Greatest common divisor. @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 Greatest common divisor of a and b on success, 0L and ec = DK3_ERROR_MATH_OVERFLOW on error. */ long dk3ma_l_gcd_ok(long a, long b, int *ec); #if DK3_HAVE_LONG_LONG /** Absolute value. @param a Original value. @param ec Pointer to error code variable, may be NULL. @return Non-negative value on success, DK3_L_MAX and ec = DK3_ERROR_MATH_OVERFLOW on error. */ long long dk3ma_ll_abs_ok(long long a, int *ec); /** 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. */ long long dk3ma_ll_add_ok(long long a, 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. */ long long dk3ma_ll_sub_ok(long long a, 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. */ long long dk3ma_ll_mul_ok(long long a, 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. */ long long dk3ma_ll_div_ok(long long a, long long b, int *ec); /** Greatest common divisor. @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 Greatest common divisor of a and b on success, 0L and ec = DK3_ERROR_MATH_OVERFLOW on error. */ long long dk3ma_ll_gcd_ok(long long a, long long b, int *ec); #endif /* if DK3_HAVE_LONG_LONG */ #if DK3_HAVE_INTMAX_T /** Absolute value. @param a Original value. @param ec Pointer to error code variable, may be NULL. @return Non-negative value on success, DK3_L_MAX and ec = DK3_ERROR_MATH_OVERFLOW on error. */ intmax_t dk3ma_intmax_t_abs_ok(intmax_t a, int *ec); /** 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. */ intmax_t dk3ma_intmax_t_add_ok(intmax_t a, intmax_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. */ intmax_t dk3ma_intmax_t_sub_ok(intmax_t a, intmax_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. */ intmax_t dk3ma_intmax_t_mul_ok(intmax_t a, intmax_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. */ intmax_t dk3ma_intmax_t_div_ok(intmax_t a, intmax_t b, int *ec); /** Greatest common divisor. @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 Greatest common divisor of a and b on success, 0L and ec = DK3_ERROR_MATH_OVERFLOW on error. */ intmax_t dk3ma_intmax_t_gcd_ok(intmax_t a, intmax_t b, int *ec); #endif /** Absolute value. @param a Original value. @param ec Pointer to error code variable, may be NULL. @return Non-negative value on success, DK3_L_MAX and ec = DK3_ERROR_MATH_OVERFLOW on error. */ dk3_im_t dk3ma_im_abs_ok(dk3_im_t a, int *ec); /** 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_im_t dk3ma_im_add_ok(dk3_im_t a, dk3_im_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_im_t dk3ma_im_sub_ok(dk3_im_t a, dk3_im_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_im_t dk3ma_im_mul_ok(dk3_im_t a, dk3_im_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_im_t dk3ma_im_div_ok(dk3_im_t a, dk3_im_t b, int *ec); /** Greatest common divisor. @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 Greatest common divisor of a and b on success, 0L and ec = DK3_ERROR_MATH_OVERFLOW on error. */ dk3_im_t dk3ma_im_gcd_ok(dk3_im_t a, dk3_im_t b, int *ec); #ifdef __cplusplus } #endif %% module #include "dk3ma.h" $!trace-include long dk3ma_l_abs_ok(long a, int *ec) { if (NULL != ec) { if(DK3_L_MIN == a) { *ec = DK3_ERROR_MATH_OVERFLOW; } } return ((0L <= a) ? a : (0L - a)); } long dk3ma_l_add_ok(long a, long b, int *ec) { if (NULL != ec) { if ((0L < a) && (0L < b)) { if((DK3_L_MAX - a) < b) { *ec = DK3_ERROR_MATH_OVERFLOW; } } else { if ((0L > a) && (0L > b)) { if (DK3_L_MIN == a) { *ec = DK3_ERROR_MATH_OVERFLOW; } else { if ((DK3_L_MIN - a) > b) { *ec = DK3_ERROR_MATH_OVERFLOW; } } } } } return (a + b); } long dk3ma_l_sub_ok(long a, long b, int *ec) { if (NULL != ec) { if ((0L < a) && (0L > b)) { if((DK3_L_MAX + b) < a) { *ec = DK3_ERROR_MATH_OVERFLOW; } } else { if ((0L > a) && (0L < b)) { if((DK3_L_MIN + b) > a) { *ec = DK3_ERROR_MATH_OVERFLOW; } } } } return (a - b); } long dk3ma_l_mul_ok(long a, long b, int *ec) { if (NULL != ec) { if ((0L != a) && (0L != b)) { if ((DK3_L_MIN == a) && (1L != b)) { *ec = DK3_ERROR_MATH_OVERFLOW; goto checksfinished; } if ((DK3_L_MIN == b) && (1L != a)) { *ec = DK3_ERROR_MATH_OVERFLOW; goto checksfinished; } if ((DK3_L_MAX / dk3ma_l_abs_ok(a, ec)) < dk3ma_l_abs_ok(b, ec)) { *ec = DK3_ERROR_MATH_OVERFLOW; /* goto checksfinished; -- when adding further tests -- */ } } } checksfinished: return (a * b); } long dk3ma_l_div_ok(long a, long b, int *ec) { long back = 0L; if (0L != b) { if ((DK3_L_MIN == a) && (-1L == b)) { back = DK3_L_MAX; *ec = DK3_ERROR_MATH_OVERFLOW; } else { back = a / b; } } else { if (NULL != ec) { *ec = DK3_ERROR_MATH_DIVZERO; } if (0L <= a) { back = DK3_L_MAX; } else { back = DK3_L_MIN; } } return back; } long dk3ma_l_gcd_ok(long a, long b, int *ec) { long h; if (0L > a) { if (DK3_L_MIN == a) { a = 0L; if (NULL != ec) { *ec = DK3_ERROR_MATH_OVERFLOW; } goto finished; } else { a = 0L - a; } } if (0L > b) { if (DK3_L_MIN == b) { a = 0L; if (NULL != ec) { *ec = DK3_ERROR_MATH_OVERFLOW; } goto finished; } else { b = 0L - b; } } while (0L < b) { h = a % b; a = b; b = h; } if (0L == a) { a = 1L; } finished: return a; } #if DK3_HAVE_LONG_LONG long long dk3ma_ll_abs_ok(long long a, int *ec) { if (NULL != ec) { if(DK3_LL_MIN == a) { *ec = DK3_ERROR_MATH_OVERFLOW; } } return ((0LL <= a) ? a : (0L - a)); } long long dk3ma_ll_add_ok(long long a, long long b, int *ec) { if (NULL != ec) { if ((0LL < a) && (0LL < b)) { if((DK3_LL_MAX - a) < b) { *ec = DK3_ERROR_MATH_OVERFLOW; } } else { if ((0LL > a) && (0LL > b)) { if (DK3_LL_MIN == a) { *ec = DK3_ERROR_MATH_OVERFLOW; } else { if ((DK3_LL_MIN - a) > b) { *ec = DK3_ERROR_MATH_OVERFLOW; } } } } } return (a + b); } long long dk3ma_ll_sub_ok(long long a, long long b, int *ec) { if (NULL != ec) { if ((0LL < a) && (0LL > b)) { if((DK3_LL_MAX + b) < a) { *ec = DK3_ERROR_MATH_OVERFLOW; } } else { if ((0LL > a) && (0LL < b)) { if((DK3_LL_MIN + b) > a) { *ec = DK3_ERROR_MATH_OVERFLOW; } } } } return (a - b); } long long dk3ma_ll_mul_ok(long long a, long long b, int *ec) { if (NULL != ec) { if ((0LL != a) && (0LL != b)) { if ((DK3_LL_MIN == a) && (1LL != b)) { *ec = DK3_ERROR_MATH_OVERFLOW; goto checksfinished; } if ((DK3_LL_MIN == b) && (1LL != a)) { *ec = DK3_ERROR_MATH_OVERFLOW; goto checksfinished; } if ((DK3_LL_MAX / dk3ma_ll_abs_ok(a, ec)) < dk3ma_ll_abs_ok(b, ec)) { *ec = DK3_ERROR_MATH_OVERFLOW; /* goto checksfinished; -- when adding further tests -- */ } } } checksfinished: return (a * b); } long long dk3ma_ll_div_ok(long long a, long long b, int *ec) { long long back = 0L; if (0LL != b) { if ((DK3_LL_MIN == a) && (-1LL == b)) { back = DK3_LL_MAX; *ec = DK3_ERROR_MATH_OVERFLOW; } else { back = a / b; } } else { if (NULL != ec) { *ec = DK3_ERROR_MATH_DIVZERO; } if (0LL <= a) { back = DK3_LL_MAX; } else { back = DK3_LL_MIN; } } return back; } long long dk3ma_ll_gcd_ok(long long a, long long b, int *ec) { long long h; if (0LL > a) { if (DK3_LL_MIN == a) { a = 0LL; if (NULL != ec) { *ec = DK3_ERROR_MATH_OVERFLOW; } goto finished; } else { a = 0LL - a; } } if (0LL > b) { if (DK3_LL_MIN == b) { a = 0LL; if (NULL != ec) { *ec = DK3_ERROR_MATH_OVERFLOW; } goto finished; } else { b = 0LL - b; } } while (0LL < b) { h = a % b; a = b; b = h; } if (0LL == a) { a = 1LL; } finished: return a; } #endif #if DK3_HAVE_INTMAX_T intmax_t dk3ma_intmax_t_abs_ok(intmax_t a, int *ec) { if (ec) { if (DK3_INTMAX_T_MIN == a) { *ec = DK3_ERROR_MATH_OVERFLOW; } } return ((DK3_INTMAX_T_0 <= a) ? a : (DK3_INTMAX_T_0 - a)); } intmax_t dk3ma_intmax_t_add_ok(intmax_t a, intmax_t b, int *ec) { if (NULL != ec) { if ((DK3_INTMAX_T_0 < a) && (DK3_INTMAX_T_0 < b)) { if ((DK3_INTMAX_T_MAX - a) < b) { *ec = DK3_ERROR_MATH_OVERFLOW; } } else { if ((DK3_INTMAX_T_0 > a) && (DK3_INTMAX_T_0 > b)) { if (DK3_INTMAX_T_MIN == a) { *ec = DK3_ERROR_MATH_OVERFLOW; } else { if ((DK3_INTMAX_T_MIN - a) > b) { *ec = DK3_ERROR_MATH_OVERFLOW; } } } } } return (a + b); } intmax_t dk3ma_intmax_t_sub_ok(intmax_t a, intmax_t b, int *ec) { if (NULL != ec) { if((DK3_INTMAX_T_0 < a) && (DK3_INTMAX_T_0 > b)) { if((DK3_INTMAX_T_MAX + b) < a) { *ec = DK3_ERROR_MATH_OVERFLOW; } } else { if((DK3_INTMAX_T_0 > a) && (DK3_INTMAX_T_0 < b)) { if((DK3_INTMAX_T_MIN + b) > a) { *ec = DK3_ERROR_MATH_OVERFLOW; } } } } return (a - b); } intmax_t dk3ma_intmax_t_mul_ok(intmax_t a, intmax_t b, int *ec) { if (NULL != ec) { if ((DK3_INTMAX_T_0 != a) && (DK3_INTMAX_T_0 != b)) { if ((DK3_INTMAX_T_MIN == a) && (DK3_INTMAX_T_1 != b)) { *ec = DK3_ERROR_MATH_OVERFLOW; goto checksfinished; } if ((DK3_INTMAX_T_MIN == b) && (DK3_INTMAX_T_1 != a)) { *ec = DK3_ERROR_MATH_OVERFLOW; goto checksfinished; } if ( (DK3_INTMAX_T_MAX / dk3ma_intmax_t_abs_ok(a, ec)) < dk3ma_intmax_t_abs_ok(b, ec) ) { *ec = DK3_ERROR_MATH_OVERFLOW; /* goto checksfinished; -- when adding further tests -- */ } } } checksfinished: return (a * b); } intmax_t dk3ma_intmax_t_div_ok(intmax_t a, intmax_t b, int *ec) { intmax_t back = DK3_INTMAX_T_0; if (DK3_INTMAX_T_0 != b) { if ((DK3_INTMAX_T_MIN == a) && ((-DK3_INTMAX_T_1) == b)) { back = DK3_INTMAX_T_MAX; *ec = DK3_ERROR_MATH_OVERFLOW; } else { back = (a / b); } } else { if (NULL != ec) { *ec = DK3_ERROR_MATH_DIVZERO; } if (DK3_INTMAX_T_0 <= a) { back = DK3_INTMAX_T_MAX; } else { back = DK3_INTMAX_T_MIN; } } return back; } intmax_t dk3ma_intmax_t_gcd_ok(intmax_t a, intmax_t b, int *ec) { intmax_t h; if (DK3_INTMAX_T_0 > a) { if (DK3_INTMAX_T_MIN == a) { a = DK3_INTMAX_T_0; if (NULL != ec) { *ec = DK3_ERROR_MATH_OVERFLOW; } goto finished; } else { a = DK3_INTMAX_T_0 - a; } } if (DK3_INTMAX_T_0 > b) { if (DK3_INTMAX_T_MIN == b) { a = DK3_INTMAX_T_0; if (NULL != ec) { *ec = DK3_ERROR_MATH_OVERFLOW; } goto finished; } else { b = DK3_INTMAX_T_0 - b; } } while (DK3_INTMAX_T_0 < b) { h = a % b; a = b; b = h; } if (DK3_INTMAX_T_0 == a) { a = DK3_INTMAX_T_1 ; } finished: return a; } #endif dk3_im_t dk3ma_im_abs_ok(dk3_im_t a, int *ec) { #if DK3_HAVE_INTMAX_T return (dk3ma_intmax_t_abs_ok(a, ec)); #else #if DK3_HAVE_LONG_LONG return (dk3ma_ll_abs_ok(a, ec)); #else return (dk3ma_l_abs_ok(a, ec)); #endif #endif } dk3_im_t dk3ma_im_add_ok(dk3_im_t a, dk3_im_t b, int *ec) { #if DK3_HAVE_INTMAX_T return (dk3ma_intmax_t_add_ok(a, b, ec)); #else #if DK3_HAVE_LONG_LONG return (dk3ma_ll_add_ok(a, b, ec)); #else return (dk3ma_l_add_ok(a, b, ec)); #endif #endif } dk3_im_t dk3ma_im_sub_ok(dk3_im_t a, dk3_im_t b, int *ec) { #if DK3_HAVE_INTMAX_T return (dk3ma_intmax_t_sub_ok(a, b, ec)); #else #if DK3_HAVE_LONG_LONG return (dk3ma_ll_sub_ok(a, b, ec)); #else return (dk3ma_l_sub_ok(a, b, ec)); #endif #endif } dk3_im_t dk3ma_im_mul_ok(dk3_im_t a, dk3_im_t b, int *ec) { #if DK3_HAVE_INTMAX_T return (dk3ma_intmax_t_mul_ok(a, b, ec)); #else #if DK3_HAVE_LONG_LONG return (dk3ma_ll_mul_ok(a, b, ec)); #else return (dk3ma_l_mul_ok(a, b, ec)); #endif #endif } dk3_im_t dk3ma_im_div_ok(dk3_im_t a, dk3_im_t b, int *ec) { #if DK3_HAVE_INTMAX_T return (dk3ma_intmax_t_div_ok(a, b, ec)); #else #if DK3_HAVE_LONG_LONG return (dk3ma_ll_div_ok(a, b, ec)); #else return (dk3ma_l_div_ok(a, b, ec)); #endif #endif } dk3_im_t dk3ma_im_gcd_ok(dk3_im_t a, dk3_im_t b, int *ec) { #if DK3_HAVE_INTMAX_T return (dk3ma_intmax_t_gcd_ok(a, b, ec)); #else #if DK3_HAVE_LONG_LONG return (dk3ma_ll_gcd_ok(a, b, ec)); #else return (dk3ma_l_gcd_ok(a, b, ec)); #endif #endif }