mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-17 10:23:46 +00:00
compiler/intermod.m:
We put analysis results into both .opt and .trans_opt files,
but a large chunk of the code that did this was duplicated.
Factor out the commonalities.
Separate the code that gathers the analysis results, and the code
that writes out those results. This allows us to avoid passing
the I/O state through large parts of this module.
Delete the code in this module that wrote out the results of the
termination2 analysis; use the existing code in parse_tree_out_pragma.m
instead.
We put both declarations and definitions of predicates into .opt files.
Call the definitions "definitions" and not "clauses", since the definitions
of predicates consist not of clauses, but of foreign_procs or promises.
Put the declarations before the definitions of predicates in
intermod_infos, since this is the usual order in Mercury programs.
Print foreign_import_module items just after Mercury import or use items,
since they do similar jobs.
Give some predicates better names.
compiler/parse_tree_out.m:
Print foreign_import_module items just after Mercury import or use items,
since they do similar jobs.
Put blank lines between blocks containing different kinds of analysis
results in .opt and .trans_opt files, to match the corresponding output
generated by intermod.m.
Add a new auxiliary predicate for this, and export it to intermod.m.
compiler/parse_tree_out_pragma.m:
Simplify the writing out of many kinds of pragmas using string.format
and io.format.
compiler/rat.m:
Replace write_rat with to_rat_string, which is strictly more flexible.
Rename to_string to to_arith_string, since the job it does is
quite different from to_rat_string's job.
Use a consistent set of variable names.
Delete unneeded module qualifications.
compiler/lp_rational.m:
Simplify code that outputs things.
compiler/term_errors.m:
Improve the style of some comments.
221 lines
5.5 KiB
Mathematica
221 lines
5.5 KiB
Mathematica
%-----------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%-----------------------------------------------------------------------------%
|
|
% Copyright (C) 1997-1998, 2003, 2005-2006, 2010-2011 The University of Melbourne.
|
|
% This file may only be copied under the terms of the GNU Library General
|
|
% Public License - see the file COPYING.LIB in the Mercury distribution.
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% File: rat.m.
|
|
% Authors: vjteag, juliensf.
|
|
%
|
|
% Implements a rational number type using fixed precision integers.
|
|
% The functionality here is limited to that which is used in the
|
|
% lp_rational module.
|
|
%
|
|
% NOTE: if you actually want a general purpose rational number type,
|
|
% then use the rational module in the standard library. This module
|
|
% is pretty heavily geared towards a few specific tasks that are part of
|
|
% the termination analysis.
|
|
%
|
|
% TODO:
|
|
% - overflow checking would be nice
|
|
%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- module libs.rat.
|
|
:- interface.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- type rat.
|
|
|
|
:- func one = rat.
|
|
:- func zero = rat.
|
|
|
|
:- pred '<'(rat::in, rat::in) is semidet.
|
|
:- pred '>'(rat::in, rat::in) is semidet.
|
|
:- pred '=<'(rat::in, rat::in) is semidet.
|
|
:- pred '>='(rat::in, rat::in) is semidet.
|
|
|
|
:- func rat(int) = rat.
|
|
:- func rat(int, int) = rat.
|
|
|
|
:- func '+'(rat) = rat.
|
|
:- func '-'(rat) = rat.
|
|
:- func rat + rat = rat.
|
|
:- func rat - rat = rat.
|
|
:- func rat * rat = rat.
|
|
:- func rat / rat = rat.
|
|
|
|
:- func numer(rat) = int.
|
|
:- func denom(rat) = int.
|
|
|
|
:- func abs(rat) = rat.
|
|
|
|
% Convert a rational to a string of the form "(<Num>/<Denom>).
|
|
% in the general case, or to strings of the form "<Num>" if possible.
|
|
%
|
|
:- func to_arith_string(rat) = string.
|
|
|
|
% Convert a rational to a string of the form "r(<Num>, <Denom>).
|
|
%
|
|
:- func to_rat_string(rat) = string.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module int.
|
|
:- import_module list.
|
|
:- import_module require.
|
|
:- import_module string.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% The normal form of a rat number has the following properties:
|
|
%
|
|
% - numerator and denominator have no common factors.
|
|
% - denominator is positive.
|
|
% - denominator is not zero.
|
|
% - if numerator is zero, then denominator is one.
|
|
%
|
|
% These invariants must be preserved by any rat number
|
|
% constructed using this module since the equality predicate
|
|
% on rats is simply Mercury's default unification
|
|
% predicate =/2. If the invariants were not maintained,
|
|
% we would have pathologies like r(-1,2) \= r(1,-2).
|
|
%
|
|
% The rat_norm/2 function generates rationals in this normal form.
|
|
%
|
|
:- type rat
|
|
---> r(int, int).
|
|
|
|
one = r(1, 1).
|
|
|
|
zero = r(0, 1).
|
|
|
|
'<'(X, Y) :- cmp(X, Y) = (<).
|
|
|
|
'>'(X, Y) :- cmp(X, Y) = (>).
|
|
|
|
'=<'(X, Y) :- cmp(X, Y) \= (>).
|
|
|
|
'>='(X, Y) :- cmp(X, Y) \= (<).
|
|
|
|
rat(Int) = r(Int, 1).
|
|
|
|
rat(Num, Denom) = rat_norm(Num, Denom).
|
|
|
|
'+'(Rat) = Rat.
|
|
|
|
'-'(r(Num, Denom)) = r(-Num, Denom).
|
|
|
|
r(An, Ad) + r(Bn, Bd) = rat_norm(Num, Denom) :-
|
|
LCM = lcm(Ad, Bd),
|
|
CA = LCM // Ad,
|
|
CB = LCM // Bd,
|
|
Num = An * CA + Bn * CB,
|
|
Denom = LCM.
|
|
|
|
X - Y = X + (-Y).
|
|
|
|
% XXX: need we call rat_norm here?
|
|
r(An, Ad) * r(Bn, Bd) = rat_norm(Num, Denom) :-
|
|
G1 = gcd(An, Bd),
|
|
G2 = gcd(Ad, Bn),
|
|
Num = (An // G1) * (Bn // G2),
|
|
Denom = (Ad // G2) * (Bd // G1).
|
|
|
|
X / Y = X * rat.reciprocal(Y).
|
|
|
|
:- func reciprocal(rat) = rat.
|
|
|
|
reciprocal(r(Num, Denom)) =
|
|
( if Num = 0 then
|
|
unexpected($pred, "division by zero")
|
|
else
|
|
r(signum(Num) * Denom, int.abs(Num))
|
|
).
|
|
|
|
numer(r(Num, _)) = Num.
|
|
|
|
denom(r(_, Denom)) = Denom.
|
|
|
|
abs(r(Num, Denom)) = r(int.abs(Num), Denom).
|
|
|
|
:- func rat_norm(int, int) = rat.
|
|
|
|
rat_norm(Num, Denom) = Rat :-
|
|
( if Denom = 0 then
|
|
unexpected($pred, "division by zero")
|
|
else if Num = 0 then
|
|
Rat = r(0, 1)
|
|
else
|
|
G = gcd(Num, Denom),
|
|
Rat = r((Num * signum(Denom)) // G, int.abs(Denom) // G)
|
|
).
|
|
|
|
:- func gcd(int, int) = int.
|
|
|
|
gcd(A, B) = gcd_loop(int.abs(A), int.abs(B)).
|
|
|
|
:- func gcd_loop(int, int) = int.
|
|
|
|
gcd_loop(A, B) = ( if B = 0 then A else gcd_loop(B, A rem B) ).
|
|
|
|
:- func lcm(int, int) = int.
|
|
|
|
lcm(A, B) =
|
|
( if A = 0 then
|
|
0
|
|
else if B = 0 then
|
|
0
|
|
else
|
|
int.abs((A // gcd(A, B)) * B)
|
|
).
|
|
|
|
:- func signum(int) = int.
|
|
|
|
signum(N) = ( if N = 0 then 0 else if N < 0 then -1 else 1 ).
|
|
|
|
% Builtin comparison does not give a natural ordering on rats.
|
|
%
|
|
:- func cmp(rat, rat) = comparison_result.
|
|
|
|
cmp(X, Y) = Cmp :-
|
|
Diff = X - Y,
|
|
( if is_zero(Diff) then
|
|
Cmp = (=)
|
|
else if is_negative(Diff) then
|
|
Cmp = (<)
|
|
else
|
|
Cmp = (>)
|
|
).
|
|
|
|
:- pred is_zero(rat::in) is semidet.
|
|
|
|
is_zero(r(0, _)).
|
|
|
|
:- pred is_negative(rat::in) is semidet.
|
|
|
|
is_negative(r(Num, _)) :-
|
|
Num < 0.
|
|
|
|
to_arith_string(r(Num, Denom)) =
|
|
( if Num = 0 then
|
|
"0"
|
|
else if Denom = 1 then
|
|
string.format("%d", [i(Num)])
|
|
else
|
|
string.format("%d/%d", [i(Num), i(Denom)])
|
|
).
|
|
|
|
to_rat_string(r(Num, Denom)) =
|
|
string.format("r(%d, %d)", [i(Num), i(Denom)]).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
:- end_module libs.rat.
|
|
%-----------------------------------------------------------------------------%
|