/* Fraction calculator 29 September 2023, by Ruud Harmsen Input, stdin, on a line of text: Four integers, separated by whitespace, except that between the first and second pair, there must be (optionally surrounded by white space) one of the characters in [*x/:]. The first two mean multiplication, the second ones indicate division. On return/enter, the result is shown, also a fraction, the smallest one possible. Then on the next line the same in cents (1200 cents = 1 octave). If the first pair of numbers is missing, the previous result is used, which initially is 1:1. Leaving out the operator, that is, just typing 4 numbers, is also allowed, in which case the default is division. The program loop can be stopped by typing a letter q (quit) or e (exit) at the start of the line. 2 October 2023: Program now also supports addition and subtraction. */ #include #include #include #include "../../rhmusicfuncs/rhmusicfuncs.h" LLI opA_numer, opA_denom, opB_numer, opB_denom; LLI w_opA_numer, w_opA_denom, w_opB_numer, w_opB_denom; LLI res_numer, res_denom; char operat[2] = "/"; void Calculate (void); void CalculutePlMn (void); void ReportResults (void); int main (void) { char line[81]; int ret = 0; opA_numer = opA_denom = opB_numer = opB_denom = res_numer = res_denom = 1; while (fgets(line, sizeof line, stdin)) { ret = sscanf(line, " %1[*x/:+-] %lld %lld ", operat, &opB_numer, &opB_denom); if (ret != 3) ret = sscanf(line, " %lld %lld %1[*x/:+-] %lld %lld ", &opA_numer, &opA_denom, operat, &opB_numer, &opB_denom); if (ret != 5) { ret = sscanf(line, " %lld %lld %lld %lld ", &opA_numer, &opA_denom,&opB_numer, &opB_denom); if (ret == 4) operat[0] = '/'; } if (ret != 4 && (line[0] == 'q' || line[0] == 'e')) break; Calculate(); ReportResults(); /* Set default operand A: the previous result unless otherwise specified. */ opA_numer = res_numer; opA_denom = res_denom; } return 0; } void Calculate (void) { LLI thisGcd; if (*operat == ':') *operat = '/'; if (*operat == 'x') *operat = '*'; if (*operat == '+' || *operat == '-') { CalculutePlMn(); } if (*operat != '/' && *operat != '*') return; /* Silently refuse to do anything. */ /* Division is equivalent to multiplication by the inverse value */ w_opB_numer = *operat == '*' ? opB_numer : opB_denom; w_opB_denom = *operat == '*' ? opB_denom : opB_numer; res_numer = opA_numer * w_opB_numer; res_denom = opA_denom * w_opB_denom; /* Safety first, no division by zero */ if (res_numer == 0) res_numer = 1; if (res_denom == 0) res_denom = 1; thisGcd = gcd(res_numer, res_denom); res_numer /= thisGcd; res_denom /= thisGcd; } void CalculutePlMn (void) { LLI thisGcd; thisGcd = gcd(opA_denom, opB_denom); w_opA_denom = w_opB_denom = opA_denom * opB_denom / thisGcd; w_opA_numer = opA_numer * w_opA_denom / opA_denom; w_opB_numer = opB_numer * w_opB_denom / opB_denom; if (*operat == '+') res_numer = w_opA_numer + w_opB_numer; else /* minus */ res_numer = w_opA_numer - w_opB_numer; res_denom = w_opA_denom; thisGcd = gcd(llabs(res_numer), res_denom); res_numer /= thisGcd; res_denom /= thisGcd; } void ReportResults (void) { if (*operat == '*' || *operat == '/') { /* Report result */ printf("%3lld:%-3lld %s %3lld:%-3lld = %3lld:%-3lld\n", opA_numer, opA_denom, operat, opB_numer, opB_denom, res_numer, res_denom); printf("%6.1f %c %6.1f = %6.1f\n", rats2cents(opA_numer, opA_denom), *operat == '/' ? '-' : '+', rats2cents(opB_numer, opB_denom), rats2cents(res_numer, res_denom)); } else { printf("%3lld:%-3lld %s %3lld:%-3lld = %3lld:%-3lld %s %3lld:%-3lld = %3lld:%-3lld\n", opA_numer, opA_denom, operat, opB_numer, opB_denom, w_opA_numer, w_opA_denom, operat, w_opB_numer, w_opB_denom, res_numer, res_denom); } }