#define _ISOC99_SOURCE /* for llabs() */ #include #include #define MAX_UINT31 0x7fffffff #define MAX_UINT32 0xffffffff #define MAX_UINT63 0x7fffffffffffffff #define MAX_UINT64 0xffffffffffffffff /* Might require fixing */ #define abs_int32(x) abs(x) #define abs_int64(x) llabs(x) /* It's asserted that: Format of int32 is "%d" Format of uint32 is "%ud" */ /* if not defined then ... typedef int int32; typedef unsigned int uint32; typedef long long int64; typedef unsigned long long uint64; */ #define assert(condition) #define SET_LR(a,b) \ { left = (int64)a->num*(uint64)b->den; \ right = (int64)b->num*(uint64)a->den; } #define DEF_LR int64 left, right #define DEFSET_LR(a,b) \ int64 left = (int64)a->num*(uint64)b->den, \ right = (int64)b->num*(uint64)a->den #define DEF_GND uint64 g, den; int64 num #define DEF_RAT(r) rational *r = (rational *) palloc(sizeof(rational)) #define CHECK_UNDEF(a,b,res) if (a->den==0 || b->den==0) {res->num=0; res->den=0; return res;} #define RETURN_UNDEF(res) {res->num = 0; res->den = 0; return res;} #define RETURN_ZERO(res) {res->num = 0; res->den = 1; return res;} #define RETURN_NONZERO(n,d,res) {res->num = n; res->den = d; return res;} #define RETURN_RATIONAL(n,d,res) { \ if (abs_int64(n) > MAX_UINT31 || den > MAX_UINT32 || num == 0) \ RETURN_UNDEF(result); \ if (abs_int64(num) == 0) \ RETURN_UNDEF(result); \ RETURN_NONZERO(n,d,res); } typedef struct rational { int32 num; uint32 den; } rational; uint64 gcd (uint64 a, uint64 b) { int64 c; assert (a); assert (b); if (b>a) { c=a; a=b; b=c; } while (b) { c = a % b; a = b; b = c; } return a; } rational *rational_in(char *str) { int i = 0; DEF_GND; DEF_RAT(result); bool sgn = 0; num = 0; if (str[i] == '-') { sgn = 1; i++; } while (str[i] >= '0' && str[i] <= '9') { if (num+str[i] > (MAX_UINT63/10+'0')) RETURN_UNDEF(result); num = 10*num + (str[i++] - '0'); } while (str[i] && !(str[i] >= '0' && str[i] <= '9')) i++; if (!str[i]) { if (abs_int64(num) > MAX_UINT31) RETURN_UNDEF(result); RETURN_NONZERO(num, 1, result); } den = 0; while (str[i] >= '0' && str[i] <= '9') { if ((den+str[i]) > (MAX_UINT64/10+'0')) RETURN_UNDEF(result); den = 10*den + (str[i++] - '0'); } g = gcd (abs_int64(num), den); num /= g; if (sgn) num=-num; den /= g; RETURN_RATIONAL(num,den,result); } char *rational_out(rational *r) { int32 size = snprintf(NULL, 0, "%d/%ud", r->num, r->den); char *result = (char*) palloc (size+1); result[size]=0; snprintf(result, size+1, "%d/%ud", r->num, r->den); return result; } bool rational_eq(rational *a, rational *b) { DEFSET_LR(a,b); return (left == right) && (left != 0); } bool rational_ne(rational *a, rational *b) { DEFSET_LR(a,b); return (left != right) || (left == 0); } bool rational_gt(rational *a, rational *b) { DEFSET_LR(a,b); return left>right; } bool rational_ge(rational *a, rational *b) { DEFSET_LR(a,b); return left>=right; } bool rational_lt(rational *a, rational *b) { DEFSET_LR(a,b); return leftden, b->den); SET_LR(a,b); num = left/g + right/g; den = (uint64)a->den*(uint64)b->den/g; RETURN_RATIONAL (num, den, result); } rational *rational_sub(rational *a, rational *b) { DEF_GND; DEF_LR; DEF_RAT(result); CHECK_UNDEF(a, b, result); g = gcd(a->den, b->den); SET_LR(a,b); num = left/g - right/g; den = (uint64)a->den*(uint64)b->den/g; RETURN_RATIONAL (num, den, result); } rational *rational_mult(rational *a, rational *b) { DEF_GND; DEF_RAT(result); CHECK_UNDEF(a, b, result); num = (uint64)a->num * (uint64)b->num; den = (int64)a->den * (int64)b->den; g = gcd (abs(num), den); num /= g; den /= g; RETURN_RATIONAL (num, den, result); /* num/den can't be zero */ } rational *rational_div(rational *a, rational *b) { DEF_GND; DEF_RAT (result); CHECK_UNDEF(a, b, result); if (b->num == 0) RETURN_UNDEF(result); /* this crap is here to assure that correct conversions will be made half of it is probably not really needed */ if (b->num < 0) { num = -((int64)a->num*(uint64)b->den); den = (uint64)a->den*(uint64)(-(int64)b->num); } else { num = (int64)a->num*(uint64)b->den; den = (uint64)((uint64)a->den*(int64)b->num); } g = gcd (abs(num), den); num /= g; den /= g; RETURN_RATIONAL (num, den, result); }