Solution (source code)

= Solution

Use the ordinary fraction identities, returning fresh records rather than changing either argument:

``
PROCEDURE addRat(x,y: rational): rational;
BEGIN
  RETURN mkRat(x.num*y.den + y.num*x.den, x.den*y.den)
END addRat;

PROCEDURE mulRat(x,y: rational): rational;
BEGIN
  RETURN mkRat(x.num*y.num, x.den*y.den)
END mulRat;

PROCEDURE negRat(x: rational): rational;
BEGIN
  RETURN mkRat(-x.num,x.den)
END negRat;
``

Every result is reduced by `mkRat`. This preserves the <representation invariant> even if an input was initially unreduced, provided its denominator is nonzero. <Aliasing> between the arguments causes no problem because their fields are only read.