diff: rat - rational numbers module.

Vanessa Joy TEAGUE vjteag at students.cs.mu.OZ.AU
Thu Mar 26 11:26:16 AEDT 1998


Hello,

Here is the revised version of rat.m with most of the changes suggested.

Estimated hours taken: 3.

Some minor changes to various functions.

compiler/rat.m:
	Implement an int2rat function which takes an integer i and
	returns the rational r(i,1).

	Change the '=' function so that '='(r(An, Ad), r(Bn, Bd)) 
	is evaluated by checking An*Bd = Ad*Bn.

	Simplify the gcd function by using 'mod' instead of repeated
	subtraction.

	Change the '+' and '-' functions so that the numerator and
	denominator of the sum are divided through by the g.c.d. of 
	the denominators of the summands before adding.  This reduces
	the chances of exceeding MAXINT during calculations. 


%-----------------------------------------------------------------------------%
% Copyright (C) 1997-1998 The University of Melbourne.
% This file may only be copied under the terms of the GNU General
% Public License - see the file COPYING in the Mercury distribution.
%-----------------------------------------------------------------------------%
%
% file: rat.m
% authors: conway, vjteag Jan 1998. 
% 
% Implements a rational number type and a set of basic operations on 
% rational numbers.

:- module rat.

:- interface.

:- import_module float, int.

:- type rat.

:- pred rat:'<'(rat, rat).
:- mode rat:'<'(in, in) is semidet.

:- pred rat:'>'(rat, rat).
:- mode rat:'>'(in, in) is semidet.

:- pred rat:'=<'(rat, rat).
:- mode rat:'=<'(in, in) is semidet.

:- pred rat:'>='(rat, rat).
:- mode rat:'>='(in, in) is semidet.

:- pred rat:'='(rat, rat).
:- mode rat:'='(in, in) is semidet.


:- func rat(int, int) = rat.

:- func float(rat) = float.

:- func rat:'+'(rat) = rat.

:- func rat:'-'(rat) = rat.

:- func rat:'+'(rat, rat) = rat.

:- func rat:'-'(rat, rat) = rat.

:- func rat:'*'(rat, rat) = rat.

:- func rat:'/'(rat, rat) = rat.

:- func rat:numer(rat) = int.

:- func rat:denom(rat) = int.

:- func rat:abs(rat) = rat.

:- func one = rat.

:- func zero = rat.

:- func int2rat(int) = rat.


:- implementation.

:- import_module require.

:- type rat	--->	r(int, int).

/* compare/3 doesn't behave correctly on rationals */ 
rat:'<'(r(An, Ad), r(Bn, Bd)) :-
	An*Bd < Bn*Ad.

rat:'>'(r(An, Ad), r(Bn, Bd)) :-
	An*Bd > Bn*Ad.

rat:'=<'(r(An, Ad), r(Bn, Bd)) :-
	An*Bd =< Bn*Ad.

rat:'>='(r(An, Ad), r(Bn, Bd)) :-
	An*Bd >= Bn*Ad.

rat:'='(r(An, Ad), r(Bn, Bd)) :-
	An*Bd = Ad*Bn.

:- func ratnorm(rat) = rat.

rat(Num, Den) = ratnorm(r(Num, Den)).


rat:float(r(Num, Den)) = float:'/'(float:float(Num), float:float(Den)).

one = r(1, 1).

zero = r(0, 1).

int2rat(Num) = r(Num, 1).

rat:'+'(Rat) = Rat.

rat:'-'(r(Num, Den)) = r(-Num, Den).

/* '+' and '-' divide through numerator and denominator of the  */
/* sum by the g.c.d. of Ad and Bd before adding.  This reduces  */
/* the chance of exceeding MAXINT during calculations.  It is   */
/* still necessary to normalize after adding/subtracting 	*/
/* (e.g. 1/6 + 1/3 = 3/6) 					*/ 
rat:'+'(r(An, Ad), r(Bn, Bd)) 
	= ratnorm(r(int:'+'(An*(Bd//Gcd), Bn*(Ad//Gcd)), Ad*(Bd//Gcd))) :-
	Gcd = gcd(Ad, Bd).

rat:'-'(r(An, Ad), r(Bn, Bd)) 
	= ratnorm(r(int:'-'(An*(Bd//Gcd), Bn*(Ad//Gcd)), Ad*(Bd//Gcd))) :-
	Gcd = gcd(Ad, Bd).

rat:'*'(r(An, Ad), r(Bn, Bd)) = ratnorm(r(An*Bn, Ad*Bd)).

rat:'/'(r(An, Ad), r(Bn, Bd)) = Rat :-
	( Bn \= 0 ->
		Rat = ratnorm(r(An*Bd, Ad*Bn))
	;
		error("divide by zero")
	).

rat:numer(Rat) = Num :-
	ratnorm(Rat) = r(Num,_).

rat:denom(Rat) = Den :-
	ratnorm(Rat) = r(_,Den).

rat:abs(Rat) = ( Rat < zero -> rat:'-'(Rat) ; Rat ).

ratnorm(r(Num, Den)) = r(Sgn*iabs(Num)//Gcd, iabs(Den)//Gcd) :-
	(
		Num > 0,
		Den > 0
	->
		Sgn = 1
	;
		Num > 0
	->
		Sgn = -1
	;
		Den > 0
	->
		Sgn = -1
	;
		Sgn = 1
	),
	( Den = 0 -> error("zero denominator!") ; true),
	Gcd0 = gcd(iabs(Num), iabs(Den)),
	( Gcd0 = 0 -> error("zero gcd!") ; Gcd = Gcd0 ).

:- func iabs(int) = int.

iabs(X) = ( X < 0 -> -X ; X ).

:- func gcd(int, int) = int.

gcd(A, B) = Res :-
	( A < 0 ->
		error("A < 0")
	; B < 0 ->
		error("B < 0")
	;
		Res = gcd0(A,B)
	).
	

:- func gcd0(int, int) = int.

gcd0(A, B) = Res :-
	( B = 0 ->		/* This case is used as the base case for    */
		Res = A		/* recursion. Also, because it makes sense   */
				/* to regard all integers as divisors of 0   */
				/* (since 0 = 0*n for all n), the greatest   */
				/* common divisor of zero and any integer m  */ 
				/* is m. 				     */
	;
		Res = gcd0(B, A mod B)
	).	



More information about the developers mailing list