Fraction Math in Algol68

Published on 01 February 2023 (Updated: 01 February 2023)

Welcome to the Fraction Math in Algol68 page! Here, you'll find the source code for this program as well as a description of how the program works.

Current Solution

MODE PARSEINT_RESULT = STRUCT(BOOL valid, INT value, STRING leftover);

PROC parse int = (REF STRING s) PARSEINT_RESULT:
(
    BOOL valid := FALSE;
    REAL r := 0.0;
    INT n := 0;
    STRING leftover;

    # Associate string with a file #
    FILE f;
    associate(f, s);

    # On end of input, exit if valid number not seen. Otherwise ignore it #
    on logical file end(f, (REF FILE dummy) BOOL:
        (
            IF NOT valid THEN done FI;
            TRUE
        )
    );

    # Exit if value error #
    on value error(f, (REF FILE dummy) BOOL: done);

    # Convert string to real number #
    get(f, r);

    # If real number is in range of an integer, convert to integer. Indicate integer is valid if same as real #
    IF ABS r <= max int
    THEN
        n := ENTIER(r);
        valid := (n = r)
    FI;

    # Get leftover string #
    get(f, leftover);

done:
    close(f);
    PARSEINT_RESULT(valid, n, leftover)
);

PROC usage = VOID: printf(($gl$, "Usage: ./fraction-math operand1 operator operand2"));

# Fraction structure and operators to extract numerator and demoninator #
MODE FRACTION = STRUCT(INT num, INT den);
OP NUM = (FRACTION f) INT: num OF f;
OP DEN = (FRACTION f) INT: den OF f;

COMMENT
Greatest common denominator using Euclidean algorithm
Source: https://en.wikipedia.org/wiki/Euclidean_algorithm#Implementations

NOTE: This uses tail recursion
COMMENT
OP GCD = (INT a, INT b) INT:
(
    IF b = 0
    THEN
        ABS a
    ELSE
        b GCD (a MOD b)
    FI
);
PRIO GCD = 8;

# Fraction reduction #
PROC reduce = (INT n, INT d) FRACTION:
(
    IF d = 0
    THEN
        put(stand error, ("Division by 0", newline));
        stop
    FI;

    COMMENT
    Reduce by dividing numerator and denominator by greatest common denominator,
    and adjust sign of numerator and denominator as follows:

    n  d  sign n  sign d
    +  +     +       +
    +  -     -       +
    -  +     -       +
    -  -     +       +
    COMMENT
    INT g = n GCD d;
    FRACTION(SIGN d * n OVER g, ABS d OVER g)
);

# Fraction addition and subtraction #
# n1/d1 +/- n2/d2 = (n1*d2 +/- n2*d1) / (d1*d2) #
OP + = (FRACTION f1, FRACTION f2) FRACTION: (
    reduce(NUM f1 * DEN f2 + NUM f2 * DEN f1, DEN f1 * DEN f2)
);
OP - = (FRACTION f1, FRACTION f2) FRACTION: (
    reduce(NUM f1 * DEN f2 - NUM f2 * DEN f1, DEN f1 * DEN f2)
);

# Fraction multiplication #
# n1/d1 * n2/d2 = (n1*n2) / (d1*d2) #
OP * = (FRACTION f1, FRACTION f2) FRACTION: (
    reduce(NUM f1 * NUM f2, DEN f1 * DEN f2)
);

# Fraction division #
# (n1/d1) / (n2/d2) = (n1*d2) / (n2*d1) #
OP / = (FRACTION f1, FRACTION f2) FRACTION: (
    reduce(NUM f1 * DEN f2, NUM f2 * DEN f1)
);

# Fraction comparison #
# n1/d1 OP n2/d2 = n1*d2 OP n2*d1 #
# where: OP is some comparision operation #
OP CMP = (FRACTION f1, FRACTION f2) INT: (
    NUM f1 * DEN f2 - NUM f2 * DEN f1
);
PRIO CMP = 8;

OP > = (FRACTION f1, FRACTION f2) BOOL: f1 CMP f2 > 0;
OP >= = (FRACTION f1, FRACTION f2) BOOL: f1 CMP f2 >= 0;
OP < = (FRACTION f1, FRACTION f2) BOOL: f1 CMP f2 < 0;
OP <= = (FRACTION f1, FRACTION f2) BOOL: f1 CMP f2 <= 0;
OP = = (FRACTION f1, FRACTION f2) BOOL: f1 CMP f2 = 0;
OP /= = (FRACTION f1, FRACTION f2) BOOL: f1 CMP f2 /= 0;

# Parse fraction #
MODE PARSEFRACTION_RESULT = STRUCT(FRACTION value, BOOL valid);
PROC parse fraction = (REF STRING s) PARSEFRACTION_RESULT:
(
    # Parse numerator #
    PARSEINT_RESULT result := parse int(s);
    BOOL valid := valid OF result;
    INT n := value OF result;
    STRING leftover := leftover OF result;

    # Assume denominator is 1 #
    INT d := 1;

    # If numerator is valid and leftover starts with "/", parse denominator #
    IF valid AND (leftover /= "" ANDF leftover[1] = "/")
    THEN
        leftover := leftover[2:];
        result := parse int(leftover);
        valid := valid OF result;
        d := value OF result;
        leftover := leftover OF result
    FI;

    # Invalid if leftover string is not empty #
    IF leftover /= ""
    THEN
        valid := FALSE
    FI;

    PARSEFRACTION_RESULT(FRACTION(n, d), valid)
);

# Do fraction math #
MODE FRACTIONRESULT = UNION(FRACTION, BOOL);
PROC fraction math = (FRACTION f1, STRING op, FRACTION f2) FRACTIONRESULT:
(
    IF op = "+" THEN f1 + f2
    ELIF op = "-" THEN f1 - f2
    ELIF op = "*" THEN f1 * f2
    ELIF op = "/" THEN f1 / f2
    ELIF op = ">" THEN f1 > f2
    ELIF op = ">=" THEN f1 >= f2
    ELIF op = "<" THEN f1 < f2
    ELIF op = "<=" THEN f1 <= f2
    ELIF op = "==" THEN f1 = f2
    ELIF op = "!=" THEN f1 /= f2
    ELSE
        put(stand error, ("Invalid operator ", op, newline));
        stop
    FI
);

# Show fraction result #
PROC show fraction result = (FRACTIONRESULT result) VOID:
(
    CASE result IN
        (FRACTION f): printf(($g"/"gl$, whole(NUM f, 0), whole(DEN f, 0))),
        (BOOL b): printf(($gl$, (b | "1" | "0")))
    ESAC
);

# Parse 1st and 3rd command-line argument #
[2]FRACTION fractions;
STRING s;
[2]INT arg nums := (4, 6);
FOR k TO 2
DO
    s := argv(arg nums[k]);
    PARSEFRACTION_RESULT result := parse fraction(s);
    fractions[k] := value OF result;

    # If invalid, extra characters, exit #
    IF NOT (valid OF result)
    THEN
        usage;
        stop
    FI
OD;

# Get 2nd command-line argument. If empty, exit #
STRING op := argv(5);
IF UPB op = 0
THEN
    usage;
    stop
FI;

# DO fraction math and show result #
FRACTIONRESULT fraction result := fraction math(fractions[1], op, fractions[2]);
show fraction result(fraction result)

Fraction Math in Algol68 was written by:

If you see anything you'd like to change or update, please consider contributing.

How to Implement the Solution

No 'How to Implement the Solution' section available. Please consider contributing.

How to Run the Solution

No 'How to Run the Solution' section available. Please consider contributing.