VOL1                                 francis                                   1HDR1ADVICE.HLP             00010001000100 85045 99364 000000Unix V7             File: Advice.Hlp	Author: R.A.O'Keefe	Updated: 21 August 1984

#source.
    The source code for the Prolog advice package is UTIL:ADVICE.PL.
    This help file is UTIL:ADVICE.HLP.

#needs.
    To get help, HELPER.PL must be compiled.
    To print out the advice using pa/1, PP.PL must be compiled.
    To turn the advice package on, you need flag/3 from FLAGRO.PL.

#purpose.
    The advice package is a debugging tool.  It gives you a way of
    inserting verification and printing code at any or all of the four
    ports of an INTERPRETED predicate without actually modifying the
    code yourself.  The idea is that you say "whenever you are at the
    X port of a goal matching Y do action Z".  Advice cannot be used
    to bind variables as it is failed back over.  Advice at the call
    port can switch tracing on, and advice at the exit or fail ports
    can switch it off.

#commands.
    advise(Head, Port, Action)		-- see advise.
    advised(Head)			-- see advised.
    unadvise(Head), unadvise(Head,Port)	-- see unadvise.
    pa, pa Pattern			-- see pa.

#data,database.
    The advice package uses the data base in three ways.
    First, it uses the flag "advice" (see advice).
    Second, it records a term advice(Port,Goal,Action) under the
    key Goal for each (Goal,Port,Action) combination.  If you want
    to remove one piece of advice from a single predicate this is
    where you look.
    Finally, it renames the clauses for the original predicate.
    It is to be hoped that this will not happen in other Prologs,
    but the other two uses of the data base are likely to be permanent.

#advice.
    Advice is what is recorded, the verb you use to give advice is
    advise with an S.

    The only advice you can actually give is "execute such and such
    a Prolog form at such a Port of such a Goal".  This should
    include spying and mode/type checking, but it currently DOESN'T.

    As an efficiency measure, advice will NOT be checked for unless
    the 'advice' flag is on.  Use
	flag(advice, _, on)		-- to turn advice checking on
	flag(advice, _, off)		-- to turn it off
	flag(advice, on, on)		-- to check if it is on
	flag(advice, Old, Old)		-- to find out what it is.
    THIS FLAG IS NOT ON INITIALLY.  YOU HAVE TO TURN IT ON YOURSELF.
    That is because Dec-10 compiled code cannot have actions in it.
    In other Prologs the flag should be on to start with.

#advise,advise(Goal,Port,Action).
    :- advise(Goal, Port, Action)
    where Goal is a compound term such as prove(X,Y) and NOT a
    pattern such as prove/2, Port is one of call/exit/redo/fail,
    and Action is a Prolog form
    tucks the advice away in the recorded data base and renames the
    clauses.  As a typical example,
	:- advise(prove(X,Y), call, writef('call %t\n', [prove(X,Y)]).
    says to obey the writef form whenever control reaches the call
    port of a prove/2 goal.

#advised,advised(Goal).
    advised(Goal)
    recognises or enumerates Goals which have been advised.  You have
    to give a compound term such as current_predicate/2 or system/1
    wants, not a pattern F/N such as spy or pp wants.

#pa.
    pa				-- print all advice
    pa help			-- display list of topics
    pa Pattern			-- print advice for selected predicates

    This reminds you what advice you have given.  A distinction is made
    between predicates which have never been advised and predicates
    which don't happen to have any advice at the moment.

#unadvise,unadvise(Goal),unadvise(Goal,Port).
    unadvise(Goal, Port)		-- remove some advice for Goal
    unadvise(Goal)			-- remove all advice for Goal

    unadvise(Goal, Port) removes all the advice pertaining to one
    particular Port (call/exit/redo/fail) of the *predicate* specified
    by Goal.  It will remove advice for heads that do not match the
    Goal pattern exactly; only the principal functor matters.

    unadvise(Goal) removes all the advice for the predicate
    specified by Goal, and renames its clauses back to their original
    state.  Removing all the advice for each of the four ports will
    not rename the clauses back.

#end_of_file.
 EOF1ADVICE.HLP             00010001000100 85045 99364 000009Unix V7             HDR1ADVICE.PL              00010002000100 85045 99364 000000Unix V7             %   File   : ADVICE.PL
%   Author : R.A.O'Keefe
%   Updated: 20 August 1984
%   Purpose: Interlisp-like advice package.
%   Needs  : concat/3 and flag/3 from UTIL, isCurrent from PP.PL.

:- public
	advise/3,
	advised/1,
	(pa)/0,
	(pa)/1,
	unadvise/1,
	unadvise/2.

:- mode
	'a$abolish'(+, +),
	'a$call'(+, +),
	'a$fail'(+),
	advise(+, +),
	advise(+, +, +),
	advised(+),	
	advised(+, -, -),
	(pa),
	pa(+),
	pa_explicit(+),
	unadvise(+),
	unadvise(+, +).

:- op(900, fx, pa).

%   This module defines three commands and a predicate:

%	advise(Predicate, Port, Action)
%	unadvise(Predicate)
%	unadvise(Predicate, Port)
%	advised(Prediate)

%   which cause the Action to be performed at that Port (advise),
%   cause no action to be performed at that Port (unadvise), or
%   test whether a predicate is advised.

%   The Predicate argument must be a Prolog term.  a/2 will be taken
%   as referring to the predicate (/)/2, which is probably not what
%   you want.  Write a(_,_).  This is the same convention as that
%   used by current_predicate.  For advise/3 the arguments will be
%   retained and may be used as a further condition; the other two
%   predicates ignore the arguments except to note how many they are.

%   The Port argument may be call, exit, redo, or fail.
%   This corresponds exactly to the ports shown by the debugger.
%   Note that the "neck" port of some debuggers cannot be handled in
%   this indirect fashion.  Advice should be integrated with the
%   debugger so that spy-points are handled this way, perhaps as
%   advise(Goal, call, spy) or some such.

%   Advice is only heeded when the 'advice' flag is on, use the
%   utility flag/3 to switch it on and off
%	flag(advice, Val, Val)	-- returns the current setting
%	flag(advice, _, on)	-- turns it on
%	flag(advice, _, off)	-- turns it off

%   Unlike the InterLISP facility on which this is based, it is
%   not possible to advise built-in predicates.  Also, advice is
%   only heeded in interpreted calls, public compiled predicates
%   may be advised, but calls to them from within compiled code
%   will not see the advice.  This is due to implementation facts
%   beyond my power to change, and may perhaps be different in
%   NIP if/when that gets off the ground.


%----------------------------------------------------------------------------%

%...advised(Goal)
%   is true when Goal is a Prolog goal whose predicate is under advice.
%   The Goal may have some of its arguments filled in, that doesn't 
%   matter.  This predicate may be used to enumerate the predicates
%   being advised, in the spirit of current_predicate.

advised(Goal) :-
	current_predicate(_, Goal),
	clause(Goal, 'a$call'(Goal,_)).



%...advised(Goal, Skel, Call)
%   is a version of advised/1 which returns the general skeleton for
%   goal, and if the goal is ill-formed or not being advised, prints
%   an error message.  It may NOT be used to enumerate goals, but as
%   you are not supposed to know about it, that is acceptable.  As a
%   result of picking up the mapped call from the clause, only advise
%   needs to know how the new name is generated.

advised(Goal, Skel, Call) :-
	functor(Goal, Functor, Arity),
	functor(Skel, Functor, Arity),
	clause(Skel, 'a$call'(Skel,Call)),
	!.
advised(Goal, _, _) :-
	display('! You are not advising '),
	display(Goal),
	ttynl,
	fail.



%----------------------------------------------------------------------------%

%...advise(Goal, Port, Action)
%   makes sure that the Goal is being advised, and then adds an
%   entry to its advice table.  On the Dec-10 and in C-Prolog, we
%   are able to put the advice "on the Goal's property list" by
%   using recorda/z.

advise(Goal, Port, Action) :-
	'a$fail'(Port),
	functor(Goal, Functor, Arity),
	functor(Skel, Functor, Arity),
	advise(Goal, Skel),
	recordz(Skel, advice(Port,Goal,Action), _).


%...a$fail(Port)
%   checks that the Port is a valid port name.

'a$fail'(Var) :-
	var(Var),
	!,
	display('! Variable as port name in advise/unadvise'),
	ttynl,
	fail.
'a$fail'(call) :- !.
'a$fail'(exit) :- !.
'a$fail'(redo) :- !.
'a$fail'(fail) :- !.
'a$fail'(Port) :-
	display('! unknown port name '),
	display(Port),
	ttynl.



%...advise(Goal)
%   takes a skeletal Goal, e.g. f(A,B,C), and makes sure that calls
%   to that goal will be routed to e.g. a$f(A,B,C) via a$call.  If
%   the goal is advised already, nothing is done.  If there are no
%   clauses for this goal, an error is announced (as this usually
%   indicates a typing mistake or a system predicate.  Otherwise
%   all the current clauses are renamed, and a new clause
%	f(A, B, C) :- a$call(f(A,B,C), a$f(A,B,C)).
%   is added.  BEWARE: this will not work if you change the predicate
%   using assert/retract.  It would have to be built in to the system
%   at a much lower level for that to work.

advise(_, Skel) :-				% already advised?
	clause(Skel, 'a$call'(Skel,_)),
	!.
advise(_, Skel) :-				% has clauses?
	current_predicate(Functor, Skel),
	!,
	concat('a$', Functor, Afunctor),
	Skel =.. [Functor |Args],
	Call =.. [Afunctor|Args],
	(   clause(Skel, Body, Ref),
	    assertz((Call :- Body)),
	    erase(Ref),
	    fail
	;   true
	),  !,
	assert((Skel :- 'a$call'(Skel,Call))).
advise(Goal, _) :-
	display('! You have no clauses for '),
	display(Goal),
	ttynl,
	fail.



%----------------------------------------------------------------------------%

%...unadvise(Goal)
%   wipes out all of the advice for the Goal.

unadvise(Goal) :-
	advised(Goal, Skel, Call),
	retract((Skel :- 'a$call'(Skel,Call))),
	(   clause(Call, Body, Ref),
	    assertz((Skel :- Body)),
	    erase(Ref),
	    fail
	;   true
	),  !,
	'a$abolish'(Skel, _).


%...unadvise(Goal, Port)
%   wipes out all the advice for Goal saying what to do at this Port.

unadvise(Goal, Port) :-
	'a$fail'(Port),			% check that the Port is valid.
	advised(Goal, Skel, Call),	% validate the Goal.
	'a$abolish'(Skel, Port).



%...a$abolish(Goal, Port)
%   wipes out all the advice for this port of the goal, given
%   that the goal and port have been validated.

'a$abolish'(Goal, Port) :-
	recorded(Goal, advice(Port,_,_), Ref),
	erase(Ref),
	fail.
'a$abolish'(_, _).



%...a$call(SourceGoal, MappedGoal)
%   routes a call on SourceGoal to call MappedGoal instead, but obeys
%   any advice that may be lying around.

'a$call'(Goal, Call) :-
	flag(advice, off, off),
	!,
	call(Call).
'a$call'(Goal, Call) :-
	(   recorded(Goal, advice(call,Goal,Action), _), call(Action), fail
	;   call(Call)
	;   recorded(Goal, advice(fail,Goal,Action), _), call(Action), fail
	),
	(   recorded(Goal, advice(exit,Goal,Action), _), call(Action), fail
	;   true
	;   recorded(Goal, advice(redo,Goal,Action), _), call(Action), fail
	).


/*----------------------------------------------------------------------+
|									|
|	pa		prints all advice				|
|	pa help		prints help about the advice package		|
|	pa Preds	prints the advice for Preds, where Preds	|
|			is the same sort of specification pp takes.	|
|	To use these commands you must have PP.PL loaded.		|
|									|
+----------------------------------------------------------------------*/

pa :-
	flag(advice, Old, Old),
	write('% The advice flag is '), write(Old), nl,
	advised(Pred),
	functor(Pred, F, N),
	pa_explicit(F/N),
	fail ; true.


pa(help) :- !,
	give_help('advice.hlp').
pa(Pattern) :-
	setof(Predicate, isCurrent(Pattern, pp, Predicate), Predicates),
	pa_explicit(Predicates).


pa_explicit([Head|Tail]) :-
	pa_explicit(Head),
	pa_explicit(Tail).
pa_explicit([]).
pa_explicit(Functor/Arity) :-
	functor(Head, Functor, Arity),
	write('% '), write(Functor/Arity),
	(   clause(Head, 'a$call'(Head,_)), !, nl,
	    (   recorded(Head, advice(Port,Head,Action), _),
		write(:-(advise(Head,Port,Action))), put(46), nl,
		fail
	    ;   true
	    )
	;   write(' is not advised'), nl
	).

 EOF1ADVICE.PL              00010002000100 85045 99364 000016Unix V7             HDR1ANDOR.PL               00010003000100 85045 99364 000000Unix V7             %   File   : andor_debug
%   Author : Dave Bowen (documented by Paul Wilk)
%   Updated: 27 February 1984
%   Purpose: Meta-circular interpreter maintaining extended AND/OR tree
%   Needs  : findall/3 from UTIL:SETOF.PL.


/* MAIN DATA STRUCTURES

A1			=:=	A term instantiated to the current argument
				of the 	current goal being unified.

A2			=:=	A term instantiated to the current argument
				of the current choice being unified.

F			=:= 	The name of the principal functor of the
				current goal or current procedure.	

N			=:= 	The arity of the current goal.

T1			=:= 	A term instantiated to current goal
				being unified.


T2			=:= 	A term instantiated to the current choice
				being unified.

[AndNodes]		=:=	This is a list of "and" goals for the parent
				goal. When AndNodes instantiates to a list, the
				current goal can then be thought of as the
				parent goal.

[AndNode]		=:=	This is a list of one "and" goal and is used
				to catch a parent goal with only one goal in
				its Body.
				
[And | RestA]		=:=	When the parent goal has more than one goal
				in the Body, And instantiates to the first
				goal in the Body and RestA to to the tail.
				And will be used to construct an "or" node
				and RestA will be saved in

Choice			=:=	Choice instantiates to the head of the
				list of or_goals.
				
RestC			=:=	RestC instantiates to the
				tail of the list of or_goals; the choicepoints..

[Clauses]		=:=	The unordered set of all instances of the
				goal-term (Head:-Body); a list.

Cont			=:=	Cont instantiates to the continuation list
				of the parent goal for the current goal.
				Note that this is a list the head of which
				is the term cont/3 the tail is the rest of
				the continuation list.

% [bp(RestC,RestA,Goal,Substs,Cont,ParentFail)|Fail]

Fail			=:=	Holds a list of records, where each record
				contains backtrack information relating to a
				current goal, i.e. [bp/9 | Fail]. Each record
				bp(RestC,RestA,Goal,Substs,Cont,ParentFail)
				

Goals			=:=	Goals consists of a linked list of the goals to
				be satisfied
Head			=:=
OrNodes			=:=	OrNodes is a pointer to a pointer tuple, Or and
				Rest0.
Or			=:=	Or points to "or" nodes. "or" nodes consist
				of a list called Clauses and a list AndNodes.

% cont(RestG, RestO, Parentfail)

RestG			=:= 	continuation list for the parent goal;
				for when all descendant goals have been
				resolved.

RestO			=:= 	list of choice points still to be resolved
				against the current goal; in case the
				current or_goal choice fails.

ParentFail		=:=	Contains the backtracking information for the
				current goal's parent goal; in case the parent
				goal fails.

Substs			=:= 	A list of the bindings made during the
				unification of the current goal.

MidSubsts		=:= 	MidSubsts is used as a local variable in the
				nested call to unify/5 for nested terms.

NewSubsts		=:=	Note that NewSubsts will contain any new
				substitutions made locally in the call to
				unify/5.

Rest			=:= 	The rest of the substitution list.

Tree			=:= 	contains the current state of the search tree.
				The search tree consists of nodes represented
				as terms. There are "and" nodes and "or" nodes
				An "and" term consists of two pointers Goals
				and OrNodes.

Value			=:= 	The value of a unified term; be it an atom
				integer, variable or non-real variable.


% MAIN PROCEDURES

%debug/1

Initialise_Question

	The call to numbervars/3 unifies the variables in the initial question
	with the special term known as $Var; by convention. These variables
	are now referred to as non-real variables. Associated with each
	non-real variable is a unique number. This number is used to
	in the substitution list, to associate a variable with a value.

	Note that Tree is a shared variable occuring as the second and
	seventh argument of and_node. The contents of Tree, the current state
	of the search tree, are built up until a solution is found for the
	initial question, or the question fails. In either case fail/3
	will call ask/1 to see if the user wishes to display the search tree.

%and_node/8.

Exit_Parent

	If the body of the current goal is empty then the second argument
	of and_node, which will usually contain a list of and_goals, will
	be empty. Therefore the parent goalf is proven true; so call
	continue/5.

Reduce_Goal

	If the body of the current goal is NOT empty (i.e. their ARE
	continuation points) then solve the next goal.	

%do_and/8.

Create_AndNode_with_ChoicePoints

	Create an and_node using the current goal, passing forward
	continuation information in the term cont/3:
	copy the continuation list for the
	parent goal; copy the list of choice points which may still
	satisfy the current goal. Go and look for continuation

Create_AndNode_without_ChoicePoints

	Prune at this and_node after satisfying the current goal;
	this is the last continuation for the parent goal.

%do_goal/8.

Cut
	The current goal is a cut therefore commit to the choices made
	since the parent goal was invoked and discard any choices left
	prior to the cut; go to continue to determine if the parent goal
	has any remaining continuation points.

Test_ChoicePoints

	Invariably the choice points  will have been collected together
	and Or will be instantiated to this collection.

Find_ChoicePoints

	Initially,  apart from a cut or system predicate goal, when a new
	current	goal is chosen, the choice points, in the form of
	Clauses	will not yet have been collected together, and Or will be
	uninstantiated.

	Note that Cont instantiates to the continuation list of the
	parent goal for the current goal. Also note the head of the list is a
	is the term cont/3; the tail is the rest of the
	continuation list.

	So, clause(Head, Body) searches the program for a clause whose head
	matches Head. Body  instantiates to the body of this clause.
	findall repeats this for all the instances of the term Head.

	Clauses is a non-empty, unordered list, containing all clause
	instances (Head:-Body) such that clause(Head, Body) is true.

	Note that for the last goal in a parent goal:

	Clauses == [(Head1:-Body1), (Head2:-Body2),...]

	But more generally:

	Clauses ==	[[(Head1:-Body1), (Head1:-Body2),...] |
			[(Head2:-Body1), (Head2:-Body2),...]].

	where each element of the outer list represents a goal to be satisfied;
	collectively they form the current list of and_goals still to be
	satisfied.

	Each element of the inner lists represents an or_goal
	that will match the head of the corresponding and_goal.

	Note that N, the arity of the term is determined here. It is used
	later as a controlling variabl for the unification of terms.

%or_node/8.

Fail_to_MatchClause

	No principal functor of a procedure matched the current goal,
	i.e. the list Clauses is empty, so fail back.

Succeed_to_MatchClause

	A principal functor of a procedure matched the current goal.
		
%do_or/9.

Last_Choice

	There is only one and_goal still to be satisfied in the scope of the
	current goal. We are looking to get a current goal from the list of
	or_goals contained in Clauses.

Not_Last_Choice

	More than one choice left, so Choice instantiates to the current head
	of the list of or_goals and RestC instantiates to the tail.

	We will pass forward all the information relating to choice points
	using the term bp/9 which will instantiate FAIL.

	This information is necessary should the current or_goal choice fail,
	because we wont have to backtrack and look for the next or_goal choice
	as it can be taken from the head of RestC.

	If we backtracked we would loose all the current or_goal choice
	bindings which defeats the purpose of this debugger.

	When the match for and_node is made in do_choice/9 AndNode will
	instantiate to the body of the current or_goal choice.

%do_choice/9.

Unify_Choice

	Now take the chosen or goal apart again.
	If unification against the head of the current_choice is successful
	then this or-goal choice is suitable as an and_goal therefore commit
	to it.

	We won't backtrack looking for any more or_goal choices because we
	collect	ALL of them once the first one succeeds.

	Pass forward the body of the or-goal chosen. This will be used to
	form the next and_node and become the new current goal.

	Consequently, this call to and_node/8  will instantiate And to a
	term pointer which has as its value the head of the
	and_goals for the current or_goal. The current or_goal passed forward
	should now be thought of as the new parent goal.

Unify_Failed

	The or_choice used as the current goal failed to unify so pass
	forward the list of or_goal choices held by Fail (this was
	instantiated in do_or) and the current state of the proof tree
	held by Tree, to fail/3.

	Note that Fail could hold an empty list containing no or_goal
	choicepoints.

%continue/5

Proven_Question

	If continue/5 is called and the list of continuation points
	is empty then a solution has been found. Go and write the
	solution to the question. Ask the user if they require a display of
	the proof tree.

	Note that if another solution is required we
	fail back here and call fail/3.

Find_Continuation

	Usually when continue/5 is called there will be a list containing
	continuation points. The head of the first argument will match with
	cont/3, and the tail, which will be an empty list or contain
	continuation points, will instantiate Cont to be the current
	continuation list. Cont is passed forward to do_and/8.

%fail/3.

Fail
	There were no choice points left for the current and_goal therefore
	Fail consists of an empty list. Ask the user if they want to print
	the proof tree.

Get_ChoicePoint

	We have not exhausted the choicepoints for the current and_goal
	therefore get the head of Fail which contains all the information
	about the next choice point.

	Note that the information describing the unresolved, unordered set of
	choicepoints is contained in the term list bp/6. So, this is passed
	forward to or_goal.

	Also, note that the tail of the list bp becomes the new choice-point
	list, passed forward in Fail.

%unify/4

Test_for_Dereferencing

	The terms to be unified might not be integers or atoms so they may
	need dereferencing. Pass the values to unify_drf/3 for unification.

%deref/3

Found_VariableTerm

	Dereference the non-real varible term; this will be the current goal
	term and the head of the current choice on subsequent calls. Note
	that.

Found_Atom

	The term is an atom or an integer, so simply return its value;
	no dereferencing is required.

%unify_drf/4

Current_Goal_is_RealVariable

	The current goal unifying with the current choice is a
	real variable as yet uninstantiated. So, bind the real variable
	to the variable-number of the non-real variable. Unification of the
	particular term is true. If there are no more terms to be unified in
	the current goal then go to the next and_node. Add this binding to the
	head of the substitution list.

Current_Choice_is_RealVariable

	The current choice made for unifying with the current goal is a
	real variable as yet uninstantiated. So, bind the real variable
	to the number of the non-real variable. Unification of the
	particular term is true. If there are no more terms to be unified in
	the current goal then go to the next and_node. Add this binding
	to the head of the substitution list

Current_Patterns_both_NonVariableTerms

	Both the current goal and current choice or non-variable terms.
	functor/3 returns the arity and name of the principal functor
	of each of the terms. Unification will fail when functor/3 is called
	for the second time if the names of the principal functors
	instantiating the second argument F, are not the same. Or, if they
	are the same, they do not have the same arity.
	If the terms have the same principal functor and arity then we need to
	call unify/5 to see if the
	terms are compound structures, atoms or integers. Note that we
	carry forward the substition list.

%unify/5

Unify_Atom

	The arity of the term is 0 therefore the term is an integer or an
	atom. Or, this  the terminal case of a compound structure as we
	are using the arity returned by functor as a counter for determining
	which current argument we are matching. Note that nothing is added to
	the substitution list if this clause succeeds.

Unify_Structure

	The structures have the same principal functor and arity
	so match the arguments in turn. Note that the arity I
	returned by functor/3 can be used to iterate up the argument lists
	from right to left. arg/3 is used to take the Ith argument
	from the current goal and the current choice. Note that we are
	still carrying the substitution list. Also note that to keep
	the substitutions in the correct order in dealing with the
	further nested structures MidSubsts is used as a local variable
	to this nested call to unify/4. Note that on successful return
	from unify/4, MidSubsts is passed forward. New substitutions
	may or may not have been added. Subsequently, Unify/5 is called
	attempting to match the I-1th argument. Note that NewSubsts
	will contain any new substitutions made, local to the unify/5 call.
	
%derefvar/4

Dereference_Head_of_SubstitutionList

	N is the number of the variable. Unify this with the number of the
	numbered-variable in the head of the substitution list. If
	they unify then we have to find the value of the numbered-variable
	in the head of the substituion list. We call deref/3 to see if
	the binding is to a value or to yet another non-real variable.

Dereference_Tail_of_SubstitutionList

	If the numbered variable in the head of the substitution list fails
	to unify with the number of the current non-real variable then the
	tail of the substitution list is passed to derefvar, referenced by
	Rest. Note that the third argument is used to maintain the completeness
	of the substitution list while searching for the value of the non-real
	variable.

Found_RealVariable

	We have emptied the substitution list therefore the variable is
	unstantiated. So we pass back the value of the last non-real
	variable in the chain as the value to which to bind the
	current	variable term to.
*/

:- public (debug)/1.

:- op(1050,fy,(debug)).


debug Goals :-
	numbervars(Goals,0,N),
	and_node(Goals,Tree,[],[],[],[],Tree,N).



and_node(true,true,Substs,Cont,Fail,ParentFail,Tree,N) :- !,
	continue(Cont,Substs,Fail,Tree,N).
and_node(Goals,and(Goals,OrNodes),Substs,Cont,Fail,ParentFail,Tree,N) :-
	do_and(Goals,OrNodes,Substs,Cont,Fail,ParentFail,Tree,N).


do_and((Goal,RestG),(Or,RestO),Substs,Cont,Fail,ParentFail,Tree,N) :- !,
	do_goal(Goal,Or,Substs,[cont(RestG,RestO,ParentFail)|Cont],
		Fail,ParentFail,Tree,N).
do_and(Goal,Or,Substs,Cont,Fail,ParentFail,Tree,N) :-
	do_goal(Goal,Or,Substs,Cont,Fail,ParentFail,Tree,N).

		
do_goal(!,true,Substs,Cont,Fail,ParentFail,Tree,N) :- !,	% handle cut
	continue(Cont,Substs,ParentFail,Tree,N).
do_goal(Goal,Or,Substs,Cont,Fail,ParentFail,Tree,N) :-
	nonvar(Or), !,						% clauses exist
	or_node(Clauses,Or,Goal,Substs,Cont,Fail,Tree,N).
do_goal(Goal,Or,Substs,Cont,Fail,ParentFail,Tree,N0) :-
	functor(Goal,F,N),					% get clauses
	functor(Head,F,N),
	findall( (Head:-Body),
		 clause(Head,Body),
	         Clauses),
	numbervars(Clauses,N0,N1),
	or_node(Clauses,Or,Goal,Substs,Cont,Fail,Tree,N1).
	
or_node([],fail,Goal,Substs,Cont,Fail,Tree,N) :- !,		% no clauses
	nl, write('Warning: there are no clauses for '),
	write(Goal), nl,
	fail(Fail,Tree,N).
or_node(Clauses,or(Clauses,AndNodes),Goal,Substs,Cont,Fail,Tree,N) :-
	do_or(Clauses,AndNodes,Goal,Substs,Cont,Fail,Fail,Tree,N).

do_or([Choice],[AndNode],Goal,Substs,Cont,Fail,ParentFail,Tree,N) :- !,	% last clause
	do_choice(Choice,AndNode,Goal,Substs,Cont,Fail,ParentFail,Tree,N).
do_or([Choice|RestC],[And|RestA],Goal,Substs,Cont,Fail,ParentFail,Tree,N) :-
	do_choice(Choice,And,Goal,Substs,Cont,
		  [bp(RestC,RestA,Goal,Substs,Cont,ParentFail)|Fail],
		  ParentFail,Tree,N).
	
do_choice((Head:-Body),And,Goal,Substs,Cont,Fail,ParentFail,Tree,N) :-
	unify(Goal,Head,Substs,NewSubsts), !,
	and_node(Body,And,NewSubsts,Cont,Fail,ParentFail,Tree,N).
do_choice(Choice,And,Goal,Substs,Cont,Fail,ParentFail,Tree,N) :-
	fail(Fail,Tree,N).

continue([],Substs,Fail,Tree,N) :- !,
	(   nl, write_substs(Substs,Tree),
	    ask('More'), !,
	    nl, write(yes), nl
	;   fail(Fail,Tree,N)
	).
continue([cont(Goals,OrNodes,ParentFail)|Cont],Substs,Fail,Tree,N) :-
	do_and(Goals,OrNodes,Substs,Cont,Fail,ParentFail,Tree,N).

fail([],Tree,N) :- !,
	nl, write(no), nl,
	( ask('Print tree'), !
	; printm(0,[],Tree)
	).
fail([bp(RestC,RestA,Goal,Substs,Cont,ParentFail)|Fail],Tree,N) :-
	do_or(RestC,RestA,Goal,Substs,Cont,Fail,ParentFail,Tree,N).

unify(T1,T2,Substs,NewSubsts) :-
	deref(T1,Substs,DT1),
	deref(T2,Substs,DT2),
	unify_drf(DT1,DT2,Substs,NewSubsts).

unify_drf(T,'$VAR'(N),Substs,[N=T|Substs]) :- !.
unify_drf('$VAR'(N),T,Substs,[N=T|Substs]) :- !.
unify_drf(T1,T2,Substs,NewSubsts) :-
	functor(T1,F,N),
	functor(T2,F,N),
	unify(N,T1,T2,Substs,NewSubsts).

unify(0,T1,T2,Substs,Substs) :- !.
unify(I,T1,T2,Substs,NewSubsts) :-
	arg(I,T1,A1),
	arg(I,T2,A2),
	unify(A1,A2,Substs,MidSubsts),
	J is I-1,
	unify(J,T1,T2,MidSubsts,NewSubsts).

deref('$VAR'(N),Substs,Value) :- !,
	derefvar(N,Substs,Substs,Value).
deref(X,S,X).

derefvar(N,[N=V1|Rest],Substs,V2) :- !,
	deref(V1,Substs,V2).
derefvar(N,[_|Rest],Substs,V) :- !,
	derefvar(N,Rest,Substs,V).
derefvar(N,[],_,'$VAR'(N)).

/* Outputting routines */

write_substs(Substs,and(Goals,T)) :-
	write('Proved:'), nl,
	printm(0,Substs,Goals),
	( ask('Print tree'), !
	; printm(0,Substs,and(Goals,T))
	).

printm(M,_,V) :- var(V), !,
	write(V).
printm(M,Substs,or(C,A)) :- !,
	N is M+1,
	write('or('), printm_nl(N,Substs,C), put(0',),
	printm_nl(N,Substs,A), put(0')).
printm(M,Substs,and(G,O)) :- !,
	N is M+1,
	write('and('), printm_nl(N,Substs,G), put(0',),
	printm_nl(N,Substs,O), put(0')).
printm(M,Substs,[X|L]) :- !,
	N is M+1,
	write('[ '), printm(N,Substs,X),
	printml(N,Substs,L), write(' ]').
printm(M,Substs,(X,Y)) :- !,
	N is M+1,
	write('( '), printm(N,Substs,X), put(0',),
	printm_nl(N,Substs,Y), write(' )').
printm(M,Substs,X) :-
	dosubsts(X,Substs,V),
	print(V).

printm_nl(M,S,X) :- nl, tab(M*2), printm(M,S,X).

printml(_,_,V) :-
	var(V), !,
	write(' |'), write(V), put(0']).
printml(_,_,[]) :- !.
printml(M,Substs,[X|L]) :-
	put(0',), printm_nl(M,Substs,X),
	printml(M,Substs,L).

dosubsts(Var,S,Var) :- var(Var), !.	% Leave real variable alone
dosubsts('$VAR'(N),S,V) :- !,
	derefvar(N,S,S,T),
	dosubterms(T,S,V).
dosubsts(T,S,V) :-
	functor(T,F,N),
	functor(V,F,N),
	dosubsts(N,T,S,V).

dosubterms('$VAR'(N),_,'$VAR'(N)) :- !.	% unbound variable
dosubterms(T,S,V) :-			% substitute values of vars in T
	dosubsts(T,S,V).

dosubsts(0,T,S,V) :- !.
dosubsts(I,T,S,V) :-
	arg(I,T,Ti),
	arg(I,V,Vi),
	dosubsts(Ti,S,Vi),
	J is I-1,
	dosubsts(J,T,S,V).

/* Ask user question and fail if "y" typed */

ask(Question) :-
	nl, write(Question),
	write(' (y/n)? '),
	ttyflush,
	get0(C),
	skiptonl(C),
	C =\= "y".

skiptonl(31) :- !. % DEC-10
skiptonl(10) :- !. % VAX
skiptonl(_) :- get0(C), skiptonl(C).

 EOF1ANDOR.PL               00010003000100 85045 99364 000038Unix V7             HDR1APPLIC.HLP             00010004000100 85045 99364 000000Unix V7             %   file. APPLIC.PL
%   author. Lawrence Byrd + Richard A. O'Keefe
%   updated. 4 August 1984
%   purpose. Various "function" application routines based on apply/2.
%   needs. append/3 from ListUt.Pl
%commands.
apply(Pred, Args)
	is the key to this whole module.  It is basically a variant of call/1
	(see the Dec-10 Prolog V3.43 manual) where some of the arguments may
	be already in the Pred, and the rest are passed in the list of Args.
	Thus apply(foo, [X,Y]) is the same as call(foo(X,Y)),
	and apply(foo(X), [Y]) is also the same as call(foo(X,Y)).
callable(Term)
		succeeds when Term is something that it would make sense to
		give to call/1 or apply/2, ie. an atom or a compound term.
checkand(Pred, Conjunction)
		succeeds when Pred(Conjunct) succeeds for every Conjunct in the
		Conjunction.
checklist(Pred, List)
		succeeds when Pred(Elem) succeeds for each Elem in the List.
mapand(Rewrite, OldConj, NewConj)
		succeeds when Rewrite is able to rewrite each conjunct of
		OldConj, and combines the results into NewConj.
maplist(Pred, OldList, NewList)
		succeeds when Pred(Old,New) succeeds for each corresponding
		Old in OldList, New in NewList.
convlist(Rewrite, OldList, NewList)
		Each element of NewList is the image under Rewrite of some
		element of OldList.
exclude(Pred, List, SubList)
		succeeds when SubList is the SubList of List containing all the
		elements for which Pred(Elem) is *false*.
some(Pred, List)
somechk(Pred, List)
		succeeds when Pred(Elem) succeeds for some Elem in List.
sublist(Pred, List, SubList)
		succeeds when SubList is the sub-sequence of the List
		containing all the Elems of List for which Pred(Elem) succeeds.
%end_of_help.
EOF1APPLIC.HLP             00010004000100 85045 99364 000004Unix V7             HDR1APPLIC.PL              00010005000100 85045 99364 000000Unix V7             %   File   : APPLIC.PL
%   Author : Lawrence Byrd + Richard A. O'Keefe
%   Updated: 4 August 1984
%   Purpose: Various "function" application routines based on apply/2.
%   Needs  : append/3 from ListUt.Pl

:- public
	apply/2,
	callable/1,
	checkand/2,
	checklist/2,
	convlist/3,
	exclude/3,
	mapand/3,
	maplist/3,
	some/2,
	somechk/2,
	sublist/3.

:- mode
	apply(+, +),
	callable(?),
	checkand(+, +),
	checklist(+, +),
	convlist(+, +, ?),
	exclude(+, +, ?),
	mapand(+, ?, ?),
	maplist(+, ?, ?),
	some(+, ?),
	somechk(+, +),
	sublist(+, +, ?).



%   apply(Pred, Args)
%   is the key to this whole module.  It is basically a variant of call/1
%   (see the Dec-10 Prolog V3.43 manual) where some of the arguments may
%   be already in the Pred, and the rest are passed in the list of Args.
%   Thus apply(foo, [X,Y]) is the same as call(foo(X,Y)),
%   and apply(foo(X), [Y]) is also the same as call(foo(X,Y)).
%   BEWARE: any goal given to apply is handed off to call/1, which is the
%   Prolog *interpreter*, so if you want to apply compiled predicates you
%   MUST have :- public declarations for them.  The ability to pass goals
%   around with some of their (initial) arguments already filled in is
%   what makes apply/2 so useful.  Don't bother compiling anything that
%   uses apply heavily, the compiler won't be able to help much.  LISP
%   has the same problem.  Later Prolog systems may have a simpler link
%   between compiled and interpreted code, or may fuse compilation and
%   interpretation, so apply/2 may come back into its own.  At the moment,
%   apply and the routines based on it are not well thought of.

apply(Pred, Args) :-
	(   atom(Pred),
		Goal =.. [Pred|Args]
	;   %compound(Pred),
		Pred =.. OldList,
		append(OldList, Args, NewList),
		Goal =.. NewList
	),  !,
	call(Goal).



%   callable(Term)
%   succeeds when Term is something that it would make sense to give to
%   call/1 or apply/2.  That is, Term must be an atom or a compound term;
%   variables and integers are out.

callable(Term) :-
	nonvar(Term),
	functor(Term, FunctionSymbol, _),
	atom(FunctionSymbol).



%   checkand(Pred, Conjunction)
%   succeeds when Pred(Conjunct) succeeds for every Conjunct in the
%   Conjunction.  All the *and predicates in this module assume that
%   a&b&c&d is parsed as a&(b&(c&d)), and that the "null" conjunction
%   is 'true'.  It is possible for this predicate, and most of the
%   others, to backtrack and try alternative solutions.  If you do not
%   want that to happen, copying one of these predicates and putting a
%   cut in the suggested place will produce a tail-recursive version.
%   The cuts in the *and predicates are there because "non-and" is
%   defined by exclusion; they cannot be assigned types.

checkand(Pred, true) :- !.
checkand(Pred, A&B)  :- !,
	apply(Pred, [A]),
	checkand(Pred, B).
checkand(Pred, A) :-
	apply(Pred, [A]).



%   checklist(Pred, List)
%   suceeds when Pred(Elem) succeeds for each Elem in the List.
%   In InterLisp, this is EVERY.  It is also MAPC.

checklist(Pred, []).
checklist(Pred, [Head|Tail]) :-
	apply(Pred, [Head]),
	checklist(Pred, Tail).



%   mapand(Rewrite, OldConj, NewConj)
%   succeeds when Rewrite is able to rewrite each conjunct of OldConj,
%   and combines the results into NewConj.

mapand(Pred, true, true) :- !.
mapand(Pred, Old&Olds, New&News) :- !,
	apply(Pred, [Old,New]),
	mapand(Pred, Olds, News).
mapand(Pred, Old, New) :-
	apply(Pred, [Old,New]).



%   maplist(Pred, OldList, NewList)
%   succeeds when Pred(Old,New) succeeds for each corresponding
%   Old in OldList, New in NewList.  In InterLisp, this is MAPCAR. 
%   It is also MAP2C.  Isn't bidirectionality wonderful?

maplist(Pred, [], []).
maplist(Pred, [Old|Olds], [New|News]) :-
	apply(Pred, [Old,New]),
	maplist(Pred, Olds, News).



%   convlist(Rewrite, OldList, NewList)
%   is a sort of hybrid of maplist/3 and sublist/3.
%   Each element of NewList is the image under Rewrite of some
%   element of OldList, and order is preserved, but elements of
%   OldList on which Rewrite is undefined (fails) are not represented.
%   Thus if foo(X,Y) :- integer(X), Y is X+1.
%   then convlist(foo, [1,a,0,joe(99),101], [2,1,102]).

convlist(Pred, [], []).
convlist(Pred, [Old|Olds], NewList) :-
	apply(Pred, [Old,New]),
	!,
	NewList = [New|News],
	convlist(Pred, Olds, News).
convlist(Pred, [_|Olds], News) :-
	convlist(Pred, Olds, News).



%   exclude(Pred, List, SubList)
%   succeeds when SubList is the SubList of List containing all the
%   elements for which Pred(Elem) is *false*.  That is, it removes
%   all the elements satisfying Pred.  Efficiency would be somewhat
%   improved if the List argument came first, but this argument order
%   was copied from the older sublist/3 predicate, and let's face it,
%   apply/2 isn't stupendously efficient itself.  

exclude(Pred, [], []).
exclude(Pred, [Head|List], SubList) :-
	apply(Pred, [Head]),
	!,
	exclude(Pred, List, SubList).
exclude(Pred, [Head|List], [Head|SubList]) :-
	exclude(Pred, List, SubList).



%   some(Pred, List)
%   succeeds when Pred(Elem) succeeds for some Elem in List.  It will
%   try all ways of proving Pred for each Elem, and will try each Elem
%   in the List.  somechk/2 is to some/2 as memberchk/2 is to member/2;
%   you are more likely to want somechk with its single solution.
%   In InterLisp this is SOME.

some(Pred, [Head|_]) :-
	apply(Pred, [Head]).
some(Pred, [_|Tail]) :-
	some(Pred, Tail).



somechk(Pred, [Head|_]) :-
	apply(Pred, [Head]),
	!.
somechk(Pred, [_|Tail]) :-
	somechk(Pred, Tail).



%   sublist(Pred, List, SubList)
%   succeeds when SubList is the sub-sequence of the List containing all
%   the Elems of List for which Pred(Elem) succeeds.

sublist(Pred, [], []).
sublist(Pred, [Head|List], SubList) :-
	apply(Pred, [Head]),
	!,
	SubList = [Head|Rest],
	sublist(Pred, List, Rest).
sublist(Pred, [_|List], SubList) :-
	sublist(Pred, List, SubList).



EOF1APPLIC.PL              00010005000100 85045 99364 000012Unix V7             HDR1ARC3.PL                00010006000100 85045 99364 000000Unix V7             %   File   : ARC3.PL
%   Author : R.A.O'Keefe
%   Updated: 9 February 1984
%   Purpose: Implement Mackworth's AC-3 algorithm.
%   Needs  : Util:Assoc.Pl, Util:ListUt.Pl

/*  It is often stated that blind backtracking is highly inefficient, and
    it is thereby implied that Prolog must be highly inefficient.  In his
    article "Consistency in Networks of Relations" (AIJ 8 (1977) 99-118)
    Mackworth presents a series of algorithms of increasing complexity to
    "remedy the thrashing behaviour that nearly always accompanies back-
    tracking", which applies to problems involving unary and binary
    constraints for a fixed number of variables with modest discrete
    domains.  Of course it can readily be extended to problems with higher
    degree relations, which become unary or binary when enough of their
    arguments are filled in.  His algorithms do not constitute a complete
    problem-solving method, but can be used to plan a backtracking or
    other solution so that it will be more efficient.

    He considers three forms of "consistency".  I have just implemented
    the first two in this file.  The reason is that this level of planning
    can be handled using just sets of values, path consistency requires
    data structures for relations.  (I know how to manipulate such data
    structures, but I'd like to keep this simple.)

    For an explanation of why the algorithms work, read Mackworth's paper.

    We are given
	a set of Nodes
	a set of Arcs, represented as (From->To) pairs
	a fixed "node admissibility" relation
		admissible_node(Node, Value)
	a fixed "arc admissibility" relation
		admissible_arc(FromNode, ToNode, FromValue, ToValue)
    We compute
	a set of (Node=PossibleValues) associations
	which is node consistent and arc consistent, but may well not
	be path consistent.
*/

:- public
	arc_consistency_3/3.

:- mode
	arc_consistency_3(+, +, -),
	make_nodes(+, -, -),
	make_graph(+, +, -, -),
	revise_each_arc(+, +, +, -),
	node_consistent_bindings(+, -),
	normalise_arcs(+, -),
	group_arcs_with_same_to_node(+, +, -),
	group_arcs_with_same_to_node(+, +, -, -),
	revise_arc(+, +, +, +, -),
	queue_arcs(+, +, +, -).


arc_consistency_3(Nodes, Arcs, ArcConsistentBindings) :-
	make_nodes(Nodes, NodeSet, InitialBindings),
	make_graph(NodeSet, Arcs, ArcSet, Graph),
	revise_each_arc(ArcSet, Graph, InitialBindings, FinalBindings),
	assoc_to_list(FinalBindings, ArcConsistentBindings).


/*  make_nodes(NodeList, NodeSet, Bindings)
    is given a representation of the set of nodes as an unordered list
    possibly with duplicates and returns a representation as an ordered
    list without duplicates (make_graph will need this).  It also returns
    an initial set of node-consistent bindings for the nodes.  Now we will
    want to fetch and update random elements of this map, and the simplest
    thing to do is to use the existing ASSOC.PL utilities.  The fact that
    setof fails if the set would be empty is *exactly* what we want here.
*/

make_nodes(NodeList, NodeSet, Bindings) :-
	sort(NodeList, NodeSet),
	node_consistent_bindings(NodeSet, NodeValList),
	list_to_assoc(NodeValList, Bindings).


node_consistent_bindings([], []).
node_consistent_bindings([Node|Nodes], [Node-Possible|Bindings]) :-
	setof(Value, admissible_node(Node, Value), Possible), !,
	node_consistent_bindings(Nodes, Bindings).


/*  We shall want to look up all the arcs leading TO a given node.
    We would like that to be fast.  We would also like to eliminate
    self-loops (X->X).  I think it is safe to assume that the arc
    list does not mention any nodes not in the node list, but we
    may have nodes that no arc leads to.  So what we are going to
    build as a representation of the graph is a binary tree mapping
    nodes to the list of arcs leading to that node.  In other
    contexts we would make that the list of node with arcs leading
    to the node, but here we want the arcs so we can push them back
    onto the stack.  We also want a list of arcs.  Just in case an
    arc appears more than once in the list, we use sort rather than
    keysort.  The code for building the list into a tree is taken
    from ASSOC.PL, avoiding the extra keysort.
*/

make_graph(NodeSet, ArcList, ArcSet, GraphTree) :-
	normalise_arcs(ArcList, PairList),
	sort(PairList, ArcSet),
	group_arcs_with_same_to_node(NodeSet, ArcSet, FinalPairs),
	length(FinalPairs, N),
	list_to_assoc(N, FinalPairs, GraphTree, []).


/*  normalise_arcs maps a list of (From->To) pairs to a list of (To-From)
    pairs, omitting any (X->X) pairs it may find.
*/

normalise_arcs([], []) :- !.
normalise_arcs([(X->X)|ArcList], PairList) :- !,
	normalise_arcs(ArcList, PairList).
normalise_arcs([(From->To)|ArcList], [To-From|PairList]) :-
	normalise_arcs(ArcList, PairList).


/*  group_arcs_with_same_to_node(NodeSet, ArcSet, NodeToArcMap)
    takes a list of Nodes, and for each node puts a (Node-Arcs) pair
    in the NodeToArcMap, where Arcs is the subset of the ArcSet that
    has Node as the To-node.  It exploits the fact that the NodeSet
    and ArcSet are both sorted, and the NodeToArcMap will also be
    sorted on the Node key, ready for building into a tree.
*/

group_arcs_with_same_to_node([], [], []).
group_arcs_with_same_to_node([Node|Nodes], ArcSet, [Node-Arcs|NodeToArcMap]) :-
	group_arcs_with_same_to_node(ArcSet, Node, Arcs, RestArcSet),
	group_arcs_with_same_to_node(Nodes, RestArcSet, NodeToArcMap).

group_arcs_with_same_to_node([Node-To|ArcSet], Node, [Node-To|Arcs], Rest) :- !,
	group_arcs_with_same_to_node(ArcSet, Node, Arcs, Rest).
group_arcs_with_same_to_node(Rest, _, [], Rest).


/*  revise_each_binding implements the heart of Mackworth's AC-3:
	Q <- {(i,j) | (i,j) in arcs(G), i =/= j}
	while Q not empty do begin
	    select and delete any arc (k,m) from Q;
	    if REVISE((k,m)) then Q <- Q U {(i,k) | (i,k) in arcs(G),i/=k,i/=m}
	end;
    the Bindings variables play the role of his D-subscript-i, and the ArcSet
    variables play the role of Q.  We exploit Prolog's success-failure: if
    revise_arc fails we just pop the arc from Q, if it succeeds it returns
    the new binding for node k.  Note that arc (i,j) in Mackworth's notation
    corresponds to J-I in our notation.
*/
revise_each_arc([], _, Bindings, Bindings) :- !.
revise_each_arc([M-K|Arcs], Graph, OldBindings, NewBindings) :-
	get_assoc(M, OldBindings, OldM),
	get_assoc(K, OldBindings, OldK),
	revise_arc(OldK, K, OldM, M, NewK),
	NewK \== OldK,
	!,		%  There was at least one deletion
	put_assoc(K, OldBindings, NewK, MidBindings),
	get_assoc(K, Graph, ArcsToK),
	queue_arcs(ArcsToK, M, Arcs, MidArcs),
	revise_each_arc(MidArcs, Graph, MidBindings, NewBindings).
revise_each_arc([_|Arcs], Graph, OldBindings, NewBindings) :-
	revise_each_arc(Arcs, Graph, OldBindings, NewBindings).


/*  revise_arc(OldK, K, OldM, M, NewK)
    checks each value in OldK to see whether there is at least one value
    in OldM which admissible_arc will accept.  If there is, it includes
    that value from OldK in NewK, otherwise it skips it.  So NewK is the
    subset of bindings for K which is compatible with the current bindings
    for M.
*/

revise_arc([], _, _, _, []).
revise_arc([Kval|OldK], K, OldM, M, [Kval|NewK]) :-
	member(Mval, OldM),
	admissible_arc(K, M, Kval, Mval),
	!,	% at least one combination works
	revise_arc(OldK, K, OldM, M, NewK).
revise_arc([_|OldK], K, OldM, M, NewK) :-
	revise_arc(OldK, K, OldM, M, NewK).	% nothing worked


/*  queue_arcs(Arcs, Exclude, OldQueue, NewQueue)
    adds each (To-From) arc from Arcs whose From is not Exclude to OldQueue,
    forming at last a NewQueue.  On reflection, it wasn't necessary to store
    complete arcs in the Graph after all, and I should go back and change it.
    However, storing complete arcs wins in a structure copying system.
*/

queue_arcs([], _, Queue, Queue).
queue_arcs([_-Exclude|Arcs], Exclude, OldQueue, NewQueue) :- !,
	queue_arcs(Arcs, Exclude, OldQueue, NewQueue).
queue_arcs([Arc|Arcs], Exclude, OldQueue, NewQueue) :-
	queue_arcs(Arcs, Exclude, [Arc|OldQueue], NewQueue).


EOF1ARC3.PL                00010006000100 85045 99364 000016Unix V7             HDR1ARITH.OPS              00010007000100 85045 99364 000000Unix V7             /* ARITH.OPS : Operator declarations for arithmetic expressions
		Now present in UTIL, used by PRESS and others

						UTILITY
						Lawrence
						Updated: 2 August 81
*/

  :- op(500,yfx,[++,--]).
  :- op(400,yfx,[div,mod]).
  :- op(300,xfy,[:,^]).
 EOF1ARITH.OPS              00010007000100 85045 99364 000001Unix V7             HDR1ARITH.PL               00010008000100 85045 99364 000000Unix V7             %   File   : ARITH.PL
%   Author : R.A.O'Keefe
%   Updated: 12 June 1984
%   Purpose: Define the 'plus' family of arithmetic predicates.

:- public
	divide/4,
	ge/2,
	gt/2,
	le/2,
	lt/2,
	plus/3,
	succ/2,
	times/3.

:- mode
	instantiation_fault_(+),
	ge(?, ?),
	gt(?, ?),
	le(?, ?),
	lt(?, ?),
	succ(?, ?),
	plus(?, ?, ?),
	times(?, ?, ?),
	times(+, +, ?, ?, ?),
	divide(?, ?, ?, ?).

/* :- pred
	ge(integer, integer),
	gt(integer, integer),
	le(integer, integer),
	lt(integer, integer),
	succ(integer, integer),
	plus(integer, integer, integer),
	times(integer, integer, integer),
	    times(integer, integer, integer, integer, integer),
	divide(integer, integer, integer, integer).
*/
/*  These predicates are now primitives in C Prolog.  My original
    reason for adding them to C-Prolog was efficiency, as the "is"
    expression interpreter (which has to handle floating point as
    well as integers) is quite complicated.  succ(X, Y) is 1.2ms
    faster than Y is X+1 on a VAX 11-750.  However, I found that
    using succ and plus were clearer, and because of the (limited)
    reversibility of these operations, lived up to Prolog's claim
    to have some relation to logic a little better than programs
    written using the strictly one-way "is".  When I started using
    Prolog I was very taken with "is" and held my nose up at IC-
    Prolog for lacking it.  I now think this was a mistake.

    Of course there are occasions when you want more than the four
    algorithms of antiquity, such as when you want to do bit-wise
    operations, or when you want to use floating-point.  And it is
    undeniable that a single arithmetic expression involving 3 or 4
    operators is a lot clearer than 3 or 4 predicate calls whose
    data flow needs careful tracing.  The style rule I have adopted
    (in addition to using 'is' when these predicates simply can't
    express what I mean) is to use 'is' whenever I can't express
    what I want with a *single* call on one of these predicates.
    Thus to calculate 3X I might use times(3,X,Ans), but to obtain
    3X+2 I would use Ans is 3*X+2.  A further style rule is that
    if a predicate uses 'is', all similar calculations in that
    predicate also use 'is' so you can see what is going on.  Thus
    if I want 3X+2 in one clause and 3X in another, I would use 'is'
    in both so that the reader can easily perceive the similarities
    and differences.  Reversibility is not then an issue.

    This Prolog code looks bulky.  It is bulky.  But don't dismiss
    these operations for such a reason.  The C code which implements
    them in C Prolog is much shorter, and it is much more efficient
    than using "is", as it knows that there is no question of walking
    down trees.  When writing a Prolog compiler, you should consider
    these operations: the compiler can benefit from knowing more
    about the arguments, and if it keeps track of the instantiation
    state of source variables it may be able to generate special-
    purpose code.  (E.g. calling plus(X,Y,Z) when Z is known to be
    unbound should generate "int_chk_push(X), int_chk_push(Y),
    add, bind_new_var(Z)" or something like that.)
*/

%   The general idea is that if there is enough information in a goal
%   to detect that it must fail (e.g. succ(X,a)) we fail, if there is
%   enough information to determine a unique solution, we yield that
%   solution, and otherwise (this can generally only happen when too
%   few arguments are instantiated) we report an instantiation fault.
%   We report a fault even when the non-determinism is bounded, e.g.
%   in times(X, X, 4) there are only two possible solutions.  This is
%   because these operations are primitives, that would be coded in
%   assembler or micro-code, and we don't want to oblige a compiler
%   to generate full frames for them.  

instantiation_fault_(Goal) :-
	nl, write('! instantiation fault in '),
	print(Goal), nl,
	break, abort.



%   {ge|gt|le|lt}(X,Y) <-> integer(X) & integer(Y) & X {>=|>|=<|<} Y
%   Note that there is no eq or ne, = and \= will do fine.

ge(X, Y) :-
	integer(X), integer(Y),
	!,
	X @>= Y.
ge(X, Y) :-
	( var(X) ; integer(X) ),
	( var(Y) ; integer(Y) ),
	!,
	instantiation_fault_(ge(X,Y)).


gt(X, Y) :-
	integer(X), integer(Y),
	!,
	X @> Y.
gt(X, Y) :-
	( var(X) ; integer(X) ),
	( var(Y) ; integer(Y) ),
	!,
	instantiation_fault_(gt(X,Y)).


le(X, Y) :-
	integer(X), integer(Y),
	!,
	X @=< Y.
le(X, Y) :-
	( var(X) ; integer(X) ),
	( var(Y) ; integer(Y) ),
	!,
	instantiation_fault_(le(X,Y)).


lt(X, Y) :-
	integer(X), integer(Y),
	!,
	X @< Y.
lt(X, Y) :-
	( var(X) ; integer(X) ),
	( var(Y) ; integer(Y) ),
	!,
	instantiation_fault_(lt(X,Y)).



%   succ(P, S) <-> integer(P) & integer(S) & P >= 0 & S = P+1
%   given either of P or S we can solve for the other.
%   If either is neither an integer nor a variable the relation
%   must be false.  But succ(P, S) with both arguments unbound
%   has infinitely many solutions.  (You can generate a bounded
%   range of integers using between/3.)

succ(Pred, Succ) :-
	integer(Pred),
	!,
	Pred >= 0,
	Succ is Pred+1.
succ(Pred, Succ) :-
	integer(Succ),
	!,
	Succ > 0,
	Pred is Succ-1.
succ(Pred, Succ) :-
	var(Pred), var(Succ),
	instantiation_fault_(succ(Pred,Succ)).



%   plus(A, B, S) <-> integer(A) & integer(B) & integer(S) & S = A+B.
%   given any two of the arguments, we can solve for the third.
%   If any argument is neither an integer nor a variable, the relation
%   must be false.  If two are variables and the other is variable or
%   integer, there are infinitely many solutions.

plus(A, B, S) :-
	integer(A), integer(B),
	!,
	S is A+B.
plus(A, B, S) :-
	integer(A), integer(S),
	!,
	B is S-A.
plus(A, B, S) :-
	integer(B), integer(S),
	!,
	A is S-B.
plus(A, B, S) :-
	( var(A) ; integer(A) ),
	( var(B) ; integer(B) ),
	( var(S) ; integer(S) ),
	!,	% at most one of A,B,S is integer, the others are vars
	instantiation_fault_(plus(A,B,S)).



%   times(A, B, P) <-> integer(A) & integer(B) & integer(P) & P = A*B.
%   This is trickier than plus.  Given A and B there is a unique solution
%   for P.  Given A(B) and P there is at most one solution for B(A)
%   except in the case when P and A(B) are both 0, in which case there
%   are infinitely many solutions.  Given just A or B there are infinitely
%   many solutions.  Given P there is always a finite number of solutions,
%   but this number always exceeds 1 (even times(X,Y,1) has X,Y=1,1 or -1,-1).
%   So we report an instantiation error in that case two.  Of course if any
%   argument is instantiated to a non-integer the relation must be false.

times(A, B, P) :-
	integer(A), integer(B),
	!,
	P is A*B.
times(A, B, P) :-
	integer(A), integer(P),
	!,
	times(P, A, B, A, B).
times(A, B, P) :-
	integer(B), integer(P),
	!,
	times(P, B, A, A, B).
times(A, B, P) :-
	( var(A) ; integer(A) ),
	( var(B) ; integer(B) ),
	( var(P) ; integer(P) ),
	!,	% at most one of P,A,B is integer, the others are vars
	instantiation_fault_(times(A,B,P)).

times(P, A, B, X, Y) :-
	A \== 0,
	!,
	0 is P mod A,
	B is P  /  A.
times(0, 0, B, X, Y) :-
	instantiation_fault_(times(X,Y,0)).



/*  divide(A, B, Q, R)
    means A, B, Q, and R are all integers,
    A = B*Q + R,
    A*R >= 0, 	(A and R have the same sign, so Q is truncated towards 0)
    0 <= |R/B| < 1	(so B is non-zero)

    This piece of Prolog is to be taken as a specification of the
    predicate, an implementation may proceed differently.
    I assume "is", and that overflow need not be checked for,
    and that X / Y and X / Y are well defined for X >= 0, Y >= 1.

    Cases:
	any one of A, B, Q, R is bound to a non-integer => FAIL
	B is bound to 0 => FAIL
	{These two failures should have associated error messages}

	A and B bound => calculate Q', R' unify Q=Q' R=R'

	A, Q, and R bound => if A*R < 0 then FAIL
			 if Q = 0 then instantiation FAULT
			 unless |Q| divides A-R then FAIL
			 calculate B from divide(A-R,Q,B,0)

	B, Q, and R bound => check conditions
			calculate A = B*Q+R
			check remaining conditions

	otherwise, instantiation FAULT
*/
divide(A, B, Q, R) :-
	( nonvar(A), \+ integer(A)
	; nonvar(B), \+ integer(B)
	; nonvar(Q), \+ integer(Q)
	; nonvar(R), \+ integer(R)
	),
	!,	% bound to fail
	fail.
divide(A, B, Q, R) :-
	nonvar(A),
	nonvar(B),
	!,
	( B > 0, A >= 0, Q1 is A/B
	; B > 0, A <  0, Q1 is -((-A)/B)
	; B < 0, A >= 0, Q1 is -(A/(-B))
	; B < 0, A <  0, Q1 is (-A)/(-B)
	), !,
	Q = Q1,
	R is A-Q1*B.
divide(A, B, Q, R) :-
	nonvar(A),
	nonvar(Q),	
	nonvar(R),
	!,
	( A >= 0, R >= 0
	; A =< 0, R =< 0
	),
	( Q = 0, !, instantiation_fault_(divide(A,B,Q,R))
	; true
	),
	!,
	0 is (A-R) mod Q,
	B is (A-R)  /  Q.
divide(A, B, Q, R) :-
	nonvar(B),
	nonvar(Q),
	nonvar(R),
	!,
	B \== 0,
	A is B*Q+R,
	( R >= 0, A >= 0, (B > R ; -B > R)
	; R =< 0, A =< 0, (B < R ; -B < R)
	),
	!.
divide(A, B, Q, R) :-
	instantiation_fault_(divide(A,B,Q,R)).

EOF1ARITH.PL               00010008000100 85045 99364 000018Unix V7             HDR1ARRAYS.PL              00010009000100 85045 99364 000000Unix V7             %   File   : ARRAYS.PL
%   Author : R.A.O'Keefe
%   Updated: 8 November 1983
%   Purpose: Updatable arrays in Prolog.

/*  These operations are fully described in
	"Updatable Arrays in Prolog", R.A.O'Keefe, DAI Working Paper 150.
    Note that store(Index, Old, Elem, New) sometimes side-effects Old and
    sometimes doesn't; you cannot rely on Old remaining unchanged.  This
    is NOT an example of logic programming.  For a logic programming
    solution (with cost O(lgN) rather O(1)) see Trees.Pl.
*/

:- public
	array_length/1,
	array_to_list/2,
	fetch/3,
	list_to_array/2,
	store/4.

:- mode
	array_length(+, -),
	array_to_list(+, -),
	    un_wrap(+, ?),
	fetch(+, +, ?),
	    get_last(+, ?),
	list_to_array(+, -),
	    wrap_up(+, -),
	store(+, +, +, -),
	    store(+, +, -, +, -).


array_length(Array+Updates, Length) :-
	functor(Array, array, Length).


array_to_list(Array+Updates, List) :-
	Array =.. [array|Wrapped],
	un_wrap(Wrapped, List).

	un_wrap([History|Histories], [Element|Elements]) :-
		get_last(History, Element), !,
		un_wrap(Histories, Elements).
	un_wrap([], []).


fetch(Index, Array+Updates, Element) :-
	arg(Index, Array, History),
	get_last(History, Element).

	get_last([Head|Tail], Element) :-
		var(Tail), !,
		Element = Head.
	get_last([_|Tail], Element) :-
		get_last(Tail, Element).


list_to_array(List, Array+0) :-
	wrap_up(List, Wrapped),
	Array =.. [array|Wrapped].

	wrap_up([Element|Elements], [[Element|_]|Wrapped]) :- !,
		wrap_up(Elements, Wrapped).
	wrap_up([], []).


store(Index, Array+Updates, Element, NewArray+NewUpdates) :-
	functor(Array, array, Length),
	arg(Index, Array, History),
	put_last(History, Element),
	K is Updates+1, !,
	store(Length, K, NewUpdates, Array, NewArray).

	store(N, N, 0, Old, New) :- !,
		Old =.. [array|OldList],
		un_wrap(OldList, MidList), !,
		wrap_up(MidList, NewList),
		New =.. [array|NewList].
	store(_, U, U, Array, Array).

	put_last(History, Element) :-
		var(History), !,
		History = [Element|_].
	put_last([_|History], Element) :-
		put_last(History, Element).

EOF1ARRAYS.PL              00010009000100 85045 99364 000005Unix V7             HDR1ASK.PL                 00010010000100 85045 99364 000000Unix V7             %   File   : ASK.PL
%   Author : R.A.O'Keefe
%   Updated: 25 November 1983
%   Purpose: ask questions that have a one-character answer.

%   Conversion note: end of line is 10 in C-Prolog, 31 in Dec-10 Prolog.

%   Note: the public predicates with two underscores in their names are
%   public just so that call/1 can see them.  They are not meant to be
%   called by your programs.  The other public predicates are meant to
%   be so called.

:- public
	ask/2,		ask__1/2,
	ask/3,		ask__2/3,
	ask_default_character/2,
	talk_to_user_while/1,
	yesno/1,	yesno__1/1,
	yesno/2,	yesno__2/2.

:- mode
	ask(+, ?),
	ask__1(+, ?),
	ask__1(+, +, ?),
	ask(+, +, ?),
	ask__2(+, +, ?),
	ask__2(+, +, +, ?),
	ask_default_character(+, -),
	talk_to_user_while(+),
	yesno(+),
	yesno__1(+),
	yesno__1(+, +),
	yesno(+, +),
	yesno__2(+, +),
	yesno__2(+, +, +).



%   The method of redirecting input-output in Dec-10 Prolog, C-Prolog,
%   PDP-11 Prolog, and PopLog is rather clumsy.  The following
%   version of "call" manages to ensure that input and output are
%   redirected to 'user' while Goal is running, and restored to their
%   original values while it is not, even if Goal should backtrack or
%   fail.  This is no mean achievement, I can tell you.  To avoid the
%   need for this nonsense, Dec-10 Prolog has a number of commands
%   ttyX that do X to 'user'.  There isn't any ttywrite, but display
%   comes close.  However, this file has to run under C-Prolog as well,
%   where display writes on the current output stream, and the ttyX
%   predicates are not primitives, but have to do their own switching.


talk_to_user_while(Goal) :-
	seeing(Seeing),
	telling(Telling),
	(   see(user), tell(user)		%  CALL port
	;   see(Seeing), tell(Telling), fail	%  FAIL port
	),
	call(Goal),
	(   see(Seeing), tell(Telling)		%  EXIT port
	;   see(user), tell(user), fail		%  REDO port
	).



%   ask_default_character(Spec, Char)
%   lets the programmer specify the default character in whatever way
%   s/he finds convenient, either as an integer, as a string, or as a
%   Prolog atom.  The case of the character is preserved.

ask_default_character(Spec, Spec) :-
	integer(Spec), !.
ask_default_character([Spec|_], Spec) :- !.
ask_default_character(Spec, Char) :-
	atom(Spec),
	name(Spec, [Char|_]).



%   ask(Question, Answer)
%   displays the Question on the  terminal  and  reads  a  one-character
%   answer  from the terminal.  But because you normally have to type "X
%   <CR>" to get the computer to attend to you, it skips to the  end  of
%   the  line.   All the juggling with see and tell is to make sure that
%   i/o is done to the terminal even if your program is doing  something
%   else.   The  character  returned  will  have Ascii code in the range
%   33..126 (that is, it won't be a space or a control character).


ask(Question, Answer) :-
	talk_to_user_while(ask__1(Question, Answer)).


ask__1(Question, Answer) :-
	write(Question),
	write('? '),
	ttyflush,
	get0(Char),
	ask__1(Char, Question, Answer).
	

ask__1(Char, _, Answer) :-
	Char > 32, Char < 127, !,
	skip(31),
	Answer = Char.
ask__1(31, Question, Answer) :- !,
	ask__1(Question, Answer).
ask__1(_, Question, Answer) :-
	skip(31),
	ask__1(Question, Answer).



%   ask(Question, Default, Answer)
%   is like ask(Question, Answer) except thast if the user types a newline
%   the Default will be taken as the Answer.

ask(Question, Default, Answer) :-
	ask_default_character(Default, DefChar),
	talk_to_user_while(ask__2(Question, DefChar, Answer)).


ask__2(Question, Default, Answer) :-
	write(Question),
	write(' ['),
	put(Default),
	write(']? '),
	ttyflush,
	get0(Char),
	ask__2(Char, Question, Default, Answer).
	

ask__2(Char, _, _, Answer) :-
	Char > 32, Char < 127, !,
	skip(31),
	Answer = Char.
ask__2(31, _, Default, Answer) :- !,
	Answer = Default.
ask__2(_, Question, Default, Answer) :-
	skip(31),
	ask__2(Question, Default, Answer).


%   yesno(Question)
%   asks the question, and succeeds if the answer is y or Y, fails if
%   the answer is n or N, and repeats the question if it is anything else.

yesno(Question) :-
	talk_to_user_while(yesno__1(Question)).


yesno__1(Question) :-
	write(Question),
	write('? '),
	ttyflush,
	get0(Char),
	Answer is Char\/32,	%   force lower case
	(   Char = 31		%   end of line
	;   skip(31)		%   skip if it isn't
	),  !,
	yesno__1(Answer, Question).

yesno__1(121/*y*/, _) :- !.
yesno__1(110/*n*/, _) :- !, fail.
yesno__1(_, Question) :-
	write('Please answer Yes or No.'), nl,
	yesno__1(Question).



%   yesno(Question, Default)
%   is like yesno(Question), except that if the user types a newline
%   without a Y or N the default will be used.  It should of course
%   be y or n itself.

yesno(Question, Default) :-
	ask_default_character(Default, DefChar),
	talk_to_user_while(yesno__2(Question, DefChar)).

yesno__2(Question, Default) :-
	write(Question),
	write(' ['),
	put(Default),
	write(']? '),
	ttyflush,
	get0(Char),
	(   Char = 31, Answer = Default		% end of line
	;   skip(31), Answer is Char\/32	% skip to eol
	),  !,
	yesno__2(Answer, Question, Default).

yesno__2(121/*y*/, _, _) :- !.
yesno__2(110/*n*/, _, _) :- !, fail.
yesno__2(_, Question, Default) :-
	write('Please answer Yes or No.'), nl,
	yesno__2(Question, Default).


EOF1ASK.PL                 00010010000100 85045 99364 000011Unix V7             HDR1ASSOC.PL               00010011000100 85045 99364 000000Unix V7             %   File   : ASSOC.PL
%   Author : R.A.O'Keefe
%   Updated: 9 November 1983
%   Purpose: Binary tree implementation of "association lists".

%   Note   : the keys should be ground, the associated values need not be.

:- public
	assoc_to_list/2,
	gen_assoc/3,
	get_assoc/3,
	list_to_assoc/2,
	map_assoc/3,
	put_assoc/4.

:- mode
	assoc_to_list(+, -),
	    assoc_to_list(+, -, +),
	gen_assoc(+, ?, ?),
	get_assoc(+, +, ?),
	    get_assoc(+, +, +, +, +, ?),
	list_to_assoc(+, -),
	    list_to_assoc(+, +, -, +),
	map_assoc(+, +, -),
	put_assoc(+, +, +, -),
	    put_assoc(+, +, +,+,+,+, +, -).


assoc_to_list(Assoc, List) :-
	assoc_to_list(Assoc, List, []).


	assoc_to_list(t(Key,Val,L,R), List, Rest) :-
		assoc_to_list(L, List, [Key-Val|More]),
		assoc_to_list(R, More, Rest).
	assoc_to_list(t, List, List).



gen_assoc(t(_,_,L,_), Key, Val) :-
	gen_assoc(L, Key, Val).
gen_assoc(t(Key,Val,_,_), Key, Val).
gen_assoc(t(_,_,_,R), Key, Val) :-
	gen_assoc(R, Key, Val).



get_assoc(Key, t(K,V,L,R), Val) :-
	compare(Rel, Key, K),
	get_assoc(Rel, Key, V, L, R, Val).


	get_assoc(=, _, Val, _, _, Val).
	get_assoc(<, Key, _, Tree, _, Val) :-
		get_assoc(Key, Tree, Val).
	get_assoc(>, Key, _, _, Tree, Val) :-
		get_assoc(Key, Tree, Val).



list_to_assoc(List, Assoc) :-
	keysort(List, Keys),
	length(Keys, N),
	list_to_assoc(N, Keys, Assoc, []).


	list_to_assoc(0, List, t, List).
	list_to_assoc(N, List, t(Key,Val,L,R), Rest) :-
		A is (N-1)/2,
		Z is (N-1)-A,
		list_to_assoc(A, List, L, [Key-Val|More]),
		list_to_assoc(Z, More, R, Rest).



map_assoc(Pred, t(Key,Val,L0,R0), t(Key,Ans,L1,R1)) :- !,
	map_assoc(Pred, L0, L1),
	apply(Pred, [Val,Ans]),
	map_assoc(Pred, R0, R1).
map_assoc(_, t, t).



put_assoc(Key, t(K,V,L,R), Val, New) :-
	compare(Rel, Key, K),
	put_assoc(Rel, Key, K, V, L, R, Val, New).
put_assoc(Key, t, Val, t(Key,Val,t,t)).


	put_assoc(=, Key, _, _, L, R, Val, t(Key,Val,L,R)).
	put_assoc(<, Key, K, V, L, R, Val, t(K,V,Tree,R)) :-
		put_assoc(Key, L, Val, Tree).
	put_assoc(>, Key, K, V, L, R, Val, t(K,V,L,Tree)) :-
		put_assoc(Key, R, Val, Tree).


 EOF1ASSOC.PL               00010011000100 85045 99364 000005Unix V7             HDR1BACKUP.PL              00010012000100 85045 99364 000000Unix V7             %   File   : BACKUP.PL
%   Author : R.A.O'Keefe
%   Updated: 19 February 1984
%   Purpose: Rename a file according to common "back up" convention.
%   Beware : this is Tops-10-specific.

%   This file MUST be compiled with TrySee.Pl, since it uses the routines
%	parse_file(-Device, -Name, -Extension, +String, +[])
%	pack_file_title(+Device, +Name, +Extension, -String)
%	normalise_file_component(+String, +Length, -Truncated)
%   which are defined in that file.  Herein is defined one predicate:
%	backup(FileName, BackUpExtn)
%   which checks whether a file named by FileName exists, and if so
%   tries to back it up.  The BackUpExtn is the extension to be merged
%   with the extension of the file name, e.g. if it is "Q" and the file
%   was "Foo.Pl" the backup will be "Foo.Ql".  Spaces at the end are
%   significant.  For convenience, backup/1 is also defined.  Note that
%   both these routines will succeed even if the file didn't exist.


:- public
	backup/1,				%  backup(F) -> F.BAK
	backup/2.				%  backup(F,Ext) for e.g. Q convention

:- mode
	backup(+),
	    backup(+, +),
		backup_name(+, +, -),
		    merge_extensions(+, +, -).


backup(FileName) :-
	backup(FileName, "BAK").


backup(FileName, BackUpExtn) :-
	atom(BackUpExtn),
	name(BackUpExtn, BackUpString), !,
	backup(FileName, BackUpString).
backup(FileName, BackUpExtn) :-
	\+ atom(FileName), !,
	ttynl, display('**error: '),
	display(backup(FileName,'_')), ttynl,
	fail.
backup(FileName, BackUpExtn) :-
	seeing(OldFile),
	nofileerrors,
	see(FileName), !,
	fileerrors,
	seeing(NewFile),
	backup_name(NewFile, BackUpExtn, BackFile),
	rename(NewFile, BackFile),
	see(OldFile).
backup(FileName, BackUpExtn) :-
	fileerrors.


	backup_name(OldFile, BackExtn, NewFile) :-
		name(OldFile, OldName),
		parse_file(Device, Name, Extension, OldName, []),
		normalise_file_component(BackExtn, 3, BackupExtension),
		merge_extensions(Extension, BackupExtension, NewExtension),
		pack_file_title(Device, Name, NewExtension, NewName),
		name(NewFile, NewName).
	

		merge_extensions([_|OldT], [NewH|NewT], [NewH|AnsT]) :- !,
			merge_extensions(OldT, NewT, AnsT).
		merge_extensions([], New, New).

EOF1BACKUP.PL              00010012000100 85045 99364 000005Unix V7             HDR1BAGUTL.HLP             00010013000100 85045 99364 000000Unix V7             


SRC Interactive Computing Facility                       Prolog Program LibrarySRC Interactive Computing Facility                       Prolog Program LibrarySRC Interactive Computing Facility                       Prolog Program Library
SIG Artificial Intelligence                                              BAGUTLSIG Artificial Intelligence                                              BAGUTLSIG Artificial Intelligence                                              BAGUTL



                     Department of Artificial Intelligence                     Department of Artificial Intelligence                     Department of Artificial Intelligence
                            University of Edinburgh                            University of Edinburgh                            University of Edinburgh


                       BAG MANIPULATION UTILITY ROUTINES                       BAG MANIPULATION UTILITY ROUTINES                       BAG MANIPULATION UTILITY ROUTINES


                               Source:    R. A. O'Keefe
                       Program Issued:    23 September 81
                        Documentation:    25 September 1981


1. Description1. Description1. Description

Bags  are  a  generalisation  of  sets, in which a given element may be present
several times.  Just as a set may be represented by its characteristic function
(a mapping from some class to truth values), so may a bag be represented by its
characteristic function, whose range  is  the  non-negative  integers.    These
routines   manipulate   Prolog   data-structures   encoding   bags  as  tabular
characteristic functions.

X is an encoded bag if

   - it is the term 'bag', representing the empty bag.

   - it is the term bag(Element, Count, RestOfBag) where  RestOfBag  is  a
     term  representing a bag, Count is a (strictly) positive integer, and
     Element is any Prolog term.  To make these representations canonical,
     Element must precede all the other elements of the bag, in the  sense
     of '@<'.

For  example,  the  bag  {a,b,c,d,c,a,d,c,a,e,d,c}  would be represented by the
Prolog term bag(a,3,bag(b,1,bag(c,4,bag(d,3,bag(e,1,bag))))).


2. How to Use the Program2. How to Use the Program2. How to Use the Program

This library may already  be  loaded  into  UTIL.    To  see  if  it  is,  type
'listing(is_bag)'  to  UTIL.  If it shows you any clauses, all these predicates
should be available at once.  Otherwise, you may either compile or consult  the
file 'Util:BagUtl.Pl'.  The following predicates are then available.

bag_inter(+Bag1, +Bag2, -Inter)
                takes the intersection of two bags.  The count of an element in
                the result is the minimum of its count in Bag1 and its count in
                Bag2.

bag_to_list(+Bag, -List)
                converts  a Bag to a List.  Each element of the Bag will appear
                in the List as many times as it occurs in {the  abstract  value
                of} the Bag.  E.g. [% a:2, b:3, c:1 %] => [a,a, b,b,b, c].

BAGUTL                               - 2 -


bag_to_set(+Bag, -SetList)
                converts  a  Bag to a Set, i.e. to a List in which each element
                of the Bag occurs exactly once.

bag_union(+Bag1, +Bag2, -Union)
                takes the union of two bags.  The count of an  element  in  the
                result is the sum of its count in Bag1 and its count in Bag2.

bagmax(+Bag, ?Elem)
                unifies  Elem  with  that element of Bag which has the greatest
                                    not                                                                            not                                                        count.  NB: this is not an ordering on the elements themselves,
                but the ordinary  arithmetic  ordering  on  their  frequencies.
                Predicates to select the alphabetically (@<) least and greatest
                elements  could  be  supplied  if  anyone  wanted them.  bagmax
                returns the commonest one.

bagmin(+Bag, ?Elem)
                unifies Elem with that element  of  Bag  which  has  the  least
                count.   In other words, with the rarest object actually in the
                Bag.

checkbag(+Pred, +Bag)
                is  an  analogue  of  checklist  for  bags.    It  succeeds  if
                Pred(Elem,Count)  is  true for every element of the Bag and its
                associated Count.

is_bag(+Bag)    succeeds if Bag is a well-formed bag representation.   Not  all
                terms which resemble bags are bags: bag(1,a,bag) is not {'a' is
                not  a  positive integer} and bag(b,1,bag(a,1,bag)) is not {'b'
                is not alphabetically less than 'a}.

length(+Bag, -Total, -Distinct)
                unifies Distinct with the number of distinct  elements  in  the
                Bag  and  Total  with  the sum of their counts.  Hence Total >=
                Distinct.  This name was chosen to agree with the notation  for
                lists (sets).

list_to_bag(+List, -Bag)
                converts a List to a Bag.  The elements of the list do not need
                to be in any special order.

make_sub_bag(+Bag, -SubBag)
                A  sub_bag  predicate  would have two uses: testing whether one
                already existing bag is a sub-bag of  another,  and  generating
                                                                 ____                          the sub-bags of a given bag.  Since bags have so many sub-bags,
                this second use is likely to be rare, and has been split out as
                make_sub_bag.  Given a Bag, make_sub_bag will backtrack through
                all its SubBags.

mapbag(+Pred, +BagIn, -BagOut)
                is  analogous to maplist.  It applies Pred(Element,Transformed)
                to each element of the Bag, generating a transformed bag.   The
                counts  are  not  given to Pred, but are preserved.  If several
                elements are mapped to  the  same  transformed  element,  their
                counts  will  be  added,  so the result will always be a proper
                bag.  For the same reason, the order of results in  the  answer

                                     - 3 -                               BAGUTL


                will  be  alphabetic,  rather than the order of the elements in
                the input bag.

member(?Elem, -Count, +Bag)
                can be used to backtrack through all the members of a bag or to
                test whether some specific object is in a bag.  In either  case
                Count  is  set  to  the  element's count.  NB if Elem is not an
                                                not                                                                            not                                            element of the bag, member will not unify Count with 0, it will
                fail                 fail                 fail.

portray_bag(+Bag)
                These predicates assume that people will  never  want  to  type
                bags,  but  will always create them using bag utilities.  Hence
                the internal representation is meant for efficiency rather than
                readability.  If you add the clause

                    portray(Bag) :- portray_bag(Bag).

                to your program you will get a prettier 'print'ed form (but not
                a 'write'n form, alas).  A bag  is  printed  between  '[%'  and
                '%]',  with  elements  followed by a colon and their count, and
                separated by commas.  For example,

                    ?- list_to_bag([a,c,d,e,f,a,f,d,c,d,e,f,s], B).
                    B = [% a:2, c:2, d:3, e:2, f:3,  s:1 %]

test_sub_bag(+SubBag, +Bag)
                tests whether SubBag is a sub bag of Bag.  This  is  redundant,
                as

                    test_sub_bag_2(Sb, Bg) :-
                            bag_inter(Sb, Bg, In),
                            In = Sb.

                It is cheaper and clearer to use test_sub_bag.


3. Program Requirements3. Program Requirements3. Program Requirements

checkbag  and mapbag require the utility apply/2.  No other utilities are used,
but BagUtl cannot be used with versions of Prolog prior  to  Version  3.    The
database is not affected.

The compiled code occupies about 2k.
 EOF1BAGUTL.HLP             00010013000100 85045 99364 000017Unix V7             HDR1BAGUTL.PL              00010014000100 85045 99364 000000Unix V7             %   File   : BAGUTL.PL
%   Author : R.A.O'Keefe
%   Updated: 18 February 1984
%   Purpose: Bag Utilities
/*
    A bag B is a function from a set dom(B) to the non-negative integers.
For the purposes of this module, a bag is constructed from two functions:
	
	bag		- creates an empty bag
	bag(E, M, B)	- extends the bag B with a new (NB!) element E
			  which occurs with multiplicity M, and which
			  precedes all elements of B in Prolog's order.

A bag is represented by a Prolog term mirroring its construction.  There
is one snag with this: what are we to make of
	bag(f(a,Y), 1, bag(f(X,b), 1, bag))	?
As a term it has two distinct elements, but f(a,b) will be reported as
occurring in it twice.  But according to the definition above,
	bag(f(a,b), 1, bag(f(a,b), 1, bag))
is not the representation of any bag, that bag is represented by
	bag(f(a,b), 2, bag)
alone.  We are apparently stuck with a scheme which is only guaranteed
to work for "sufficiently instantiated" terms, but then, that's true of 
a lot of Prolog code.

    The reason for insisting on the order is to make union and 
intersection linear in the sizes of their arguments.

*/

:- public
	bag_inter/3,
	bag_to_list/2,
	bag_to_set/2,
	bag_union/3,
	bagmax/2,
	bagmin/2,
	checkbag/2,
	is_bag/1,
	length/3,
	list_to_bag/2,
	make_sub_bag/2,
	mapbag/3,
	member/3,
	member/3,
	portray_bag/1,
	test_sub_bag/2.

:- mode
	addkeys(+, -),
	bag_inter(+, +, -),
	    bag_inter(+, +, +, +, +, +, +, -),	
	bag_scan(+, +, +, -, +),
	bag_to_list(+, -),
	    bag_to_list(+, +, -, -),
	bag_to_set(+, -),
	bag_union(+, +, -),
	    bag_union(+, +, +, +, +, +, +, -),
	bagform(+, -),
	    bagform(?, +, -, +, -),
	bagmax(+, -),
	bagmin(+, -),
	checkbag(+, +),
	countdown(+, -),
	is_bag(+),
	    is_bag(+, +),
	length(+, -, -),
	    length(+, +, -, +, -),
	list_to_bag(+, -),
	make_sub_bag(+, -),
	mapbag(+, +, -),
	    mapbaglist(+, +, -),
	member(?, ?, +),
	memberchk(+, ?, +),
	portray_bag(+),
	    portray_bag(+, +, +),
		portray_bag(+, +),
	test_sub_bag(+, +),
	    test_sub_bag(+, +, +, +, +, +, +).



is_bag(bag).
is_bag(bag(E,M,B)) :-
	integer(M), M > 0,
	is_bag(B, E).

	is_bag(bag, _).
	is_bag(bag(E,M,B), P) :-
		E @> P,
		integer(M), M > 0,
		is_bag(B, E).



portray_bag(bag(E,M,B)) :-
	write('[% '), portray_bag(E, M, B), write(' %]').
portray_bag(bag) :-
	write('[% '), write(' %]').

	portray_bag(E, M, B) :-
		var(B), !,
		portray_bag(E, M), write(' | '), write(B).
	portray_bag(E, M, bag(F,N,B)) :- !,
		portray_bag(E, M), write(', '),
		portray_bag(F, N, B).
	portray_bag(E, M, bag) :- !,
		portray_bag(E, M).
	portray_bag(E, M, B) :-
		portray_bag(E, M), write(' | '), write(B).

		portray_bag(E, M) :-
			print(E), write(':'), write(M).


%   If bags are to be as useful as lists, we should provide mapping
%   predicates similar to those for lists.  Hence
%	checkbag(Pred, Bag)		- applies Pred(Element, Count)
%	mapbag(Pred, BagIn, BagOut)	- applies Pred(Element, Answer)
%   Note that mapbag does NOT give the Count to Pred, but preserves it.
%   It wouldn't be hard to apply Pred to four arguments if it wants them.



checkbag(Pred, bag).
checkbag(Pred, bag(E,M,B)) :-
	apply(Pred, [E, M]),
	checkbag(Pred, B).


mapbag(Pred, BagIn, BagOut) :-
	mapbaglist(Pred, BagIn, Listed),
	keysort(Listed, Sorted),
	bagform(Sorted, BagOut).

	mapbaglist(Pred, bag, []).
	mapbaglist(Pred, bag(E,M,B), [R-M|L]) :-
		apply(Pred, [E, R]),
		mapbaglist(Pred, B, L).



bag_to_list(bag, []).
bag_to_list(bag(E,M,B), R) :-
	bag_to_list(M, E, L, R),
	bag_to_list(B, L).

	bag_to_list(0, _, L, L) :- !.
	bag_to_list(M, E, L, [E|R]) :-
		N is M-1,
		bag_to_list(N, E, L, R).



list_to_bag(L, B) :-
	addkeys(L, K),
	keysort(K, S),
	bagform(S, B).

	addkeys([], []).
	addkeys([Head|Tail], [Head-1|Rest]) :-
		addkeys(Tail, Rest).

	bagform([], bag) :- !.
	bagform(List, bag(E,M,B)) :-
		bagform(E, List, Rest, 0, M), !,
		bagform(Rest, B).

		bagform(Head, [Head-N|Tail], Rest, K, M) :-!,
			L is K+N,
			bagform(Head, Tail, Rest, L, M).
		bagform(Head, Rest, Rest, M, M).



bag_to_set(bag, []).
bag_to_set(bag(E,_,B), [E|S]) :-
	bag_to_set(B, S).


/*  There are two versions of the routines member, bagmax, and bagmin.
    The slow versions, which are commented out, try to allow for the
    possibility that distinct elements in the bag might unify, while
    the faster routines assume that all elements are ground terms.


member(E, M, bag(E,K,B)) :-
	member(B, E, K, M).
member(E, M, bag(_,_,B)) :-
	member(E, M, B).

	member(bag(E,L,B), E, K, M) :- !,
		N is K+L,
		member(B, E, N, M).
	member(bag(_,_,B), E, K, M) :-
		member(B, E, K, M).
	member(bag,	   E, M, M).

%  These routines are correct, but Oh, so costly!

bagmax(B, E) :-
	member(E, M, B),
	\+ (member(F, N, B), N > M).

bagmin(B, E) :-
	member(E, M, B),
	\+ (member(F, N, B), N < M).

*//*	The faster versions follow    */


member(Element, Multiplicity, bag(Element,Multiplicity,_)).
member(Element, Multiplicity, bag(_,_,Bag)) :-
	member(Element, Multiplicity, Bag).


memberchk(Element, Multiplicity, bag(Element,Multiplicity,_)) :- !.
memberchk(Element, Multiplicity, bag(_,_,Bag)) :-
	memberchk(Element, Multiplicity, Bag).



bagmax(bag(E,M,B), Emax) :-
	bag_scan(B, E, M, Emax, >).

bagmin(bag(E,M,B), Emin) :-
	bag_scan(B, E, M, Emin, <).

	bag_scan(bag(Eb,Mb,B), Ei, Mi, Eo, C) :-
		compare(C, Mb, Mi), !,
		bag_scan(B, Eb, Mb, Eo, C).
	bag_scan(bag(Eb,Mb,B), Ei, Mi, Eo, C) :-
		bag_scan(B, Ei, Mi, Eo, C).
/*	bag_scan(bag(Eb,Mb,B), Ei, Mi, Eo, C) :-
		bag_scan(B, Eb, Mb, Eo, C).	%  for all extrema
*/	bag_scan(bag,	       Ei, Mi, Ei, C).




length(B, BL, SL) :-
	length(B, 0, BL, 0, SL).

	length(bag,	   BL, BL, SL, SL).
	length(bag(_,M,B), BA, BL, SA, SL) :-
		BB is BA+M, SB is SA+1,
		length(B, BB, BL, SB, SL).


%  sub_bag, if it existed, could be used two ways: to test whether one bag
%  is a sub_bag of another, or to generate all the sub_bags.  The two uses
%  need different implementations.


make_sub_bag(bag, bag).
make_sub_bag(bag(E,M,B), bag(E,N,C)) :-
	countdown(M, N),
	make_sub_bag(B, C).
make_sub_bag(bag(_,_,B), C) :-
	make_sub_bag(B, C).

	countdown(M, M).
	countdown(M, N) :-
		M > 1, K is M-1,
		countdown(K, N).



test_sub_bag(bag, _).
test_sub_bag(bag(E1,M1,B1), bag(E2,M2,B2)) :-
	compare(C, E1, E2),
	test_sub_bag(C, E1, M1, B1, E2, M2, B2).

	test_sub_bag(>, E1, M1, B1, E2, M2, B2) :-
		test_sub_bag(bag(E1, M1, B1), B2).
	test_sub_bag(=, E1, M1, B1, E1, M2, B2) :-
		M1 =< M2,
		test_sub_bag(B1, B2).


bag_union(bag(E1,M1,B1), bag(E2,M2,B2), B3) :-
	compare(C, E1, E2), !,
	bag_union(C, E1, M1, B1, E2, M2, B2, B3).
bag_union(bag, Bag, Bag) :- !.
bag_union(Bag, bag, Bag).

	bag_union(<, E1, M1, B1, E2, M2, B2, bag(E1,M1,B3)) :-
		bag_union(B1, bag(E2, M2, B2), B3).
	bag_union(>, E1, M1, B1, E2, M2, B2, bag(E2,M2,B3)) :-
		bag_union(bag(E1, M1, B1), B2, B3).
	bag_union(=, E1, M1, B1, E1, M2, B2, bag(E1,M3,B3)) :-
		M3 is M1+M2,
		bag_union(B1, B2, B3).



bag_inter(bag(E1,M1,B1), bag(E2,M2,B2), B3) :-
	compare(C, E1, E2), !,
	bag_inter(C, E1, M1, B1, E2, M2, B2, B3).
bag_inter(_, _, bag).

	bag_inter(<, E1, M1, B1, E2, M2, B2, B3) :-
		bag_inter(B1, bag(E2,M2,B2), B3).
	bag_inter(>, E1, M1, B1, E2, M2, B2, B3) :-
		bag_inter(bag(E1,M1,B1), B2, B3).
	bag_inter(=, E1, M1, B1, E1, M2, B2, bag(E1, M3, B3)) :-
		(   M1 < M2, M3 = M1  ;  M3 = M2   ), !,
		bag_inter(B1, B2, B3).


 EOF1BAGUTL.PL              00010014000100 85045 99364 000015Unix V7             HDR1BETWEE.PL              00010015000100 85045 99364 000000Unix V7             %   File   : BETWEEN.PL
%   Author : R.A.O'Keefe
%   Updated: 4 October 1984
%   Purpose: Generate integers.

:- public
	between/3,
	gen_arg/3,
	gen_int/1,
	gen_nat/1,
	repeat/1.

:- mode
	between(+, +, ?),
	between1(+, +, -),
	gen_arg(?, +, ?),
	gen_int(?),
	gen_nat(?),
	gen_nat(+, -),
	repeat(+).


between(L, U, N) :-
	nonvar(N),
	!,
	integer(L), integer(U), integer(N),
	L =< N, N =< U.
between(L, U, N) :-
	integer(L), integer(U), L =< U,
	between1(L, U, N).


between1(L, _, L).
between1(L, U, N) :-
	L < U,
	M is L+1,
	between1(M, U, N).



%   gen_arg(N, Term, Arg)
%   is exactly like arg(N, Term, Arg), except that it will generate
%   solutions for N by backtracking (will work when N is a variable).

gen_arg(N, Term, Arg) :-
	functor(Term, _, Arity),
	between(1, Arity, N),
	arg(N, Term, Arg).



gen_nat(N) :-			% gen-erate nat-ural
	nonvar(N),		% if we aren't to generate it
	!,			% demand that it is an integer
	integer(N), N >= 0.	% and non-negative.
gen_nat(N) :-			% otherwise, generate an
	gen_nat(0, N).		% integer >= 0
 
 
gen_nat(L, L).
gen_nat(L, N) :-		% generate natural > L
	M is L+1,
	gen_nat(M, N).		% generate natural >= M
 
 

gen_int(I) :-			% gen-erate int-eger
	nonvar(I),		% if we aren't to generate it
	!,			% demand that it is an integer.
	integer(I).
gen_int(0).			% generate 0
gen_int(I) :-			% generate +/- N for N > 0
	gen_nat(1, N),
	(   I = N
	;   I is -N
	).



repeat(N) :-
	telling(Old), tell(user),
	write('It is pointlessly stupid to use repeat/1.'), nl,
	write('Why don''t you use between/3 instead, fathead?'), nl,
	tell(Old),
	between(1, N, _).

EOF1BETWEE.PL              00010015000100 85045 99364 000004Unix V7             HDR1BUNDLE.PL              00010016000100 85045 99364 000000Unix V7             %   File   : BUNDLE.PL
%   Author : R.A.O'Keefe
%   Updated: 22 September 1984
%   Purpose: Bundle and Unbundle files.
%   Needs  : append/3 and reverse/2 from LISTUT.PL (lib(lists)).

/*  This file defines two commands:

	<output> bundle [<label1> = <in1>, ..., <labelK> = <inK>].

	unbundle <input>.

    The former writes, for each of the <inI> files in turn,
	%?BEGIN <labelI> -- <inI>
	{contents of file <inI>}
	%?ENDOF <labelI> -- <inI>
    There is no special mark at the beginning or end of the file.

    The latter reads the <input>, and writes every %?BEGIN..%?ENDOF
    section to a file whose name is the given label.

    If you omit a <label>=, BUNDLE will generate one for you.  In
    any case, it will derive a label from what you give it rather
    than taking it as it stands.  Labels end up as FORTRAN 6-letter
    variable names, with - turned to 8 and _ turned to 9.  These
    seem to be acceptable file names on most file systems.

    The code below does assume that the end of file character is 26.
    In some Prologs this is -1.  That's the only use made of 26.
    The program accepts either ^_ (31, Dec-10 Prolog's mapping of
    CRLF) or ^J (LF) as end of line in input and generates whatever
    nl generates in output.  You should only have to change this if
    your Prolog uses CR (change 31 to 13).

    If your Prolog doesn't do TRO you will have a tough time with
    'bundle', as it recurses all over the place.  If asked nicely I
    might do something about that, but 'repeat' loops are alien to
    Prolog as the 'unbundle' mess shows.  C Prolog should be able to  
    unbundle things all right, but not to bundle them, but you would
    be better advised to write a shell script to bundle things on UNIX.

    It is recommended that you include files called UNIX, TOPS10,
    TOPS20, VMS, or whatever, as appropriate, which can be run as
    commands by the operating system of that name, to rename the files
    to a good place.  E.g. if this file were part of a bundle, we might
    have
	UNIX   = mv BUNDLE /usr/lib/prolog/bundle.pl
	       : rm TOPS10 UNIX
	TOPS10 = rename UTIL:BUNDLE.PL=BUNDLE.
	       : delete TOPS10.,UNIX.
    Even if someone has an operating system not in your list, one of
    these files might be a good place to start editing.
*/

:- public
	bundle/1,
	bundle/2,
	help/0,
	unbundle/1.

:- op(100, xfx, bundle).
:- op(100,  fx, bundle).
:- op(100,  fx, unbundle).

:- mode
	bundle(+),
	bundle(+, +),
	bundle1(+, +),
	bundle2(+, +),
	copy_to_bundle(+, +),
	get_one_line(-),
	get_one_line(+, -),
	keep_letters(+, +, -),
	unbundle(+),
	unbundle1(+),
	unbundle2(+, -),
	unbundle3(+, -),
	write_one_line(+).



help :-
	write('Output bundle Files wraps up a list of files into a bundle
	and writes them to Output.  Files is a list of Label=File pairs.
unbundle File unwraps a bundle of files into its components.
').


Output bundle Files :-
	tell(Output),
	bundle Files,
	told.

bundle [].
bundle [Label=File|Files] :- !,
	bundle1(File, Label),
	bundle Files.
bundle [File|Files] :-
	bundle1(File, File),
	bundle Files.


bundle1(File, Label) :-
	atom(Label),
	name(Label, Chars),
	reverse(Chars, Rev),
	(   append(Hrev, [0':|_], Rev)		%  Tops-10
	;   append(Hrev, [0'>|_], Rev)		%  Tops-20
	;   append(Hrev, [0'/|_], Rev)		%  UNIX
	;   append(Hrev, [0']|_], Rev)		%  VMS
	;   Hrev = Rev				%  no device/directory
	),  !,
	reverse(Hrev, Head),
	keep_letters(Head, 6, Letters),
	name(ShortLabel, Letters),
	ShortLabel \== '',
	bundle2(File, ShortLabel).
bundle1(File, Label) :-
	telling(Bundle),
	tell(user),
	write('Can''t generate a label from '), write(Label),
	write(', '), write(File),
	write(' not written to '), write(Bundle), nl,
	tell(Bundle).


keep_letters([Char|Chars], N, [Keep|Keeps]) :-
	N > 0, M is N-1,
	(   0'A =< Char, Char =< 0'Z, Keep = Char
	;   0'a =< Char, Char =< 0'z, Keep is Char-32
	;   0'0 =< Char, Char =< 0'9, Keep = Char
	;   0'- == Char,	      Keep = 0'8	% for VMS
	;   0'_ == Char,	      Keep = 0'9	% for VMS
	),  !,
	keep_letters(Chars, M, Keeps).
keep_letters(_, _, []).


bundle2(File, Label) :-
	nofileerrors,
	see(File),
	!,
	fileerrors,
	write('%?BEGIN '), write(Label), write(' -- '), write(File), nl,
	get0(C),
	copy_to_bundle(C, yes),
	seen,
	write('%?ENDOF '), write(Label), write(' -- '), write(File), nl.
bundle2(File, Label) :-
	fileerrors,
	telling(Bundle),
	tell(user),
	write('Can''t open '), write(File), write(', '),
	write(Label), write(' not written to '), write(Bundle), nl,
	tell(Bundle).


%   copy_to_bundle(Char, Flag)
%   loops around reading characters and writing them to the bundle file.
%   The Flag indicates whether the last character written was a newline.
%   This is so that we can be sure that the %?BEGIN and %?ENDOF marks
%   always start at the beginning of a line.  Which in turn means that
%   we can read a bundle file a line at a time without worrying about
%   end of file in the middle of a line.  Char is the next character to
%   be copied.

copy_to_bundle(26, yes) :- !.		% 26 => -1 in new standard
copy_to_bundle(26, no) :- !,
	nl.
copy_to_bundle(31, _) :- !,		% NB: we don't copy a 31 or 10
	nl, get0(C),			% literally, this is to convert
	copy_to_bundle(C, yes).		% between line terminators if
copy_to_bundle(10, _) :- !,		% that is necessary.  You ought
	nl, get0(C),			% to be able to run this in any
	copy_to_bundle(C, yes).		% current version of Dec-10 or
copy_to_bundle(D, Flag) :-		% C Prolog WITH NO CHANGES.
	put(D), get0(C),
	copy_to_bundle(C, Flag).



/*  unbundle is a wee bit tricky.
    I'm afraid I do most of my character I/O in Prolog thinking in terms
    of Finite-State-Automata rather than in terms of logic.  If files
    were lists I could use DCGs...
    The FSM I have in mind here works on a stream of lines, and has 3
    states.
	state 1: end of file => enter state 3
		 input is %?ENDOF ... => error message, return to state 1
		 input is %?BEGIN ... => open file, enter state 2
	         input is something else => forget it and return to state 1
	state 2: end of file => close output, print error message, state 3
	  	 input is %?ENDOF ... => close output, enter state 1
		 input is %?BEGIN ... => close output, print error message,
				  	return to new state 2
		 input is something else => print it, return to state 2
	state 3: halt
    At least, that's what I had in mind when I started.  It dawned on me
    when I was half way through that Prologs without TRO and GC would have
    a hard time with N,000 characters on the stack, so I had to hack it
    into 'repeat' (ugh, chunder) form.  TRO and GC are VITAL for practical
    programming, VITAL!  Sorry about the mess, before I hacked all the
    repeats in here I thought I understood it, now I know I don't.  But it
    seems to work.
*/

unbundle File :-
	see(File),
	repeat,
	    get_one_line(Line),
	    unbundle1(Line),		% succeeds at end of file
	!,
	seen.	


unbundle1(end_of_file) :- !.
unbundle1(Line) :-
	append("%?BEGIN ", Rest, Line),
	!,
	unbundle2(Rest, Last),
	Last = end_of_file.
unbundle1(Line) :-
	append("%?ENDOF ", Rest, Line),
	write('? Unexpected '), write_one_line(Line),
	!, fail.


unbundle2(Rest, Last) :-
	nofileerrors,
	append(LabelChars, [0' ,0'-,0'-,0' |_], Rest),
	name(Label, LabelChars), 
	atom(Label),
	tell(Label),
	fileerrors,
	!,
	repeat,
	    get_one_line(Line),
	    unbundle3(Line, Last),
	!.
unbundle2(Rest, Rest) :-
	fileerrors.


unbundle3(end_of_file, end_of_file) :- !,
	told,
	write('? unexpected end of file'), nl.
unbundle3(Line, Last) :-
	append("%?BEGIN ", Rest, Line),
	!,
	told,
	write('? Unexpected '), write_one_line(Line),
	unbundle2(Rest, Last).
unbundle3(Line, Line) :-
	append("%?ENDOF ", _, Line),
	told,
	!.
unbundle3(Line, _) :-
	write_one_line(Line),
	fail.


get_one_line(Line) :-
	get0(C),
	(   C = 26, Line = end_of_file
	;   get_one_line(C, Line)
	),  !.

get_one_line(31, []) :- !.
get_one_line(10, []) :- !.
get_one_line(C, [C|Cs]) :-
	get0(D),
	get_one_line(D, Cs).


write_one_line([]) :-
	nl.
write_one_line([C|Cs]) :-
	put(C),
	write_one_line(Cs).

 EOF1BUNDLE.PL              00010016000100 85045 99364 000016Unix V7             HDR1BYRD.HLP               00010017000100 85045 99364 000000Unix V7             UTIL:UTIL.HLP					Updated: 22 October 81


This directory contains the Prolog utilities used by the Mecho Project.

On the ERCC DEC10 in Edinburgh all this stuff is contained in [140,143,UTIL].
The contents of this directory is now being thrown onto outgoing tapes
containing the latest DEC10 Prolog system, on the offchance they might be
of some use elsewhere.

The following files are present:

	UTIL.MIC
	MUTIL.MIC	These are MIC command files for loading two standard
			Utilities packages (a full one and a minimal one).
			The EXE files produced are stored elsewhere in the
			Mecho library ([400,444]). These MIC files are rather
			hairy, their main purpose being to support an
			automatic reloading hack I use (they will undoubtably
			be useless elsewhere).  The interesting work is done in
			the following files:

	UTIL
	MUTIL		These are Prolog files which contain the commands to
			load (by compiling/consulting) the various sources
			which make up the above packages. Either of these
			files just needs consulting to do the loading.

	UTIL.TXT	This file contains a list of all the predicates
			provided by the Utilities package. The predicates
			are listed first by module (source file) and then
			alphabetically.

	WRITEF.*	A short documentary note on the formatted write
			utility (see WRITEF.PL).

	UTIL.MSS	A rather old start to some documentation, It is
			horribly incomplete and uses as yet undefined
			SCRIBE macros so there is only the source form.

	UTIL.OPS
	ARITH.OPS	These files declare the (syntactic) operators used by
			the packages.

	*.PL		These are all the Prolog source files for the Utility
			packages.


Unfortunately there is no decent documentation for the utilities apart from
the list of predicates in UTIL.TXT. This is a matter which I have been meaning
to deal with for years. However most of the routines are pretty short and
straightforward. I have not yet brought them all up to my current commenting
standards though. Sorry about the mess.

The real goody that you may enjoy is the rational arithmetic package which can
be found in LONG.PL.  This provides all the standard arithmetic operations over
arbitrary precision rational numbers, plus some more things like logs, square
roots, and a poor mans trig function hack.  There is also a symbolic
simplifier/evaluator which makes use of LONG in TIDY.PL.  Both these files are
fairly substantial but they both contain documentation on whats going on.  The
rational arithmetic package is pretty fast considing this was not a deliberate
intention.  It needs compiling of course - try it and see!

I hope these may be of some use to you,


					Lawrence Byrd
					Artificial Intelligence
					Hope Park Square
					University of Edinburgh
					Edinburgh
					SCOTLAND	UK


	Network mail etc,

		BYRD on the ERCC DEC10 (Edinburgh) (ppn = [400,441])
			(If thats where you are, or you can get through)

		BYRD@MIT-AI
			(Regular ARPANET mailing address)
EOF1BYRD.HLP               00010017000100 85045 99364 000006Unix V7             HDR1CC.PL                  00010018000100 85045 99364 000000Unix V7             %   File   : CC.PL
%   Author : R.A.O'Keefe
%   Updated: 25 August 1984
%   Purpose: "Conditional Compilation".

/*  The purpose of this file is to provide a "conditional compilation"
    feature in Prolog somewhat like #+ and #- in Mac/Franz/Zeta Lisp.
    The idea is that if you have something which must be different for
    Dec-10 Prolog, C Prolog, Prolog-X, &c, or perhaps for different
    operating systems, you can put
	#+ dialect(dec10), os(tops10).
	-- Dec-10 Prolog Tops-10 version- .
	#+ dialect(c), os(unix).
	-- C Prolog UNIX version- .
	#+ dialect(c), os(vms).
	-- C Prolog VMS version- .
    And so on.  (#+ features.) will throw away the next term if the
    test fails.  (n #+ features.) will throw away the next n terms.
    #- x is the same as #+ \+ (X).  This wants to be built into the
    reader, so that one doesn't have to write :- #+ ...
*/

:- public
	(#+)/1,		%    #+ features. clause.
	(#+)/2,		%  n #+ features. c1. ... cn.
	(#-)/1,		%    #- features. clause.
	(#-)/2,		%  n #- features. c1. ... cn.
	dialect/1,
	os/1.

:- op(1199,  fx, [(#+),(#-)]).
:- op(1199, xfx, [(#+),(#-)]).

#+ Test :-
	1 #+ Test.

N #+ Test :-
	call(Test),
	!.
N #+ Test :-
	skip_read(N).


#- Test :-
	1 #- Test.

N #- Test :-
	call(Test),
	!,
	skip_read(N).
N #- Test.


skip_read(0) :- !.
skip_read(N) :-
	read(_),
	M is N-1,
	skip_read(M).


dialect(dec10).
os(tops10).







EOF1CC.PL                  00010018000100 85045 99364 000003Unix V7             HDR1CLAUSE.PL              00010019000100 85045 99364 000000Unix V7             %   File   : CLAUSE
%   Author : R.A.O'Keefe
%   Updated: 10 March 1984
%   Purpose: Convert a formula in FOPC to clausal form.
%   Needs  : ord_union/3 from UTIL:ORDSET.PL.

/*----------------------------------------------------------------------

    This module has three entry points:
	clausal_form(Formula, Clauses)
	clausal_form_of_negation(Formula, Clauses)
	units_separated(Clauses, PosUnits, NegUnits, NonUnits)

    The Formula is an <expr>, where
	<expr> ::= all(<variable>, <expr>)
		|  some(<variable>, <expr>)
		|  <expr> => <expr>
		|  <expr> <=> <expr>
		|  if(<expr>,<expr>,<expr>)
		|  <expr> and <expr>
		|  <expr> or <expr>
		|  ~ <expr>
		|  <atom>

	<atom> ::= <predicate>(<term>,...,<term>)

	<term> ::= <variable>
		|  <constant>
		|  <functor>(<term>,...,<term>)

    The Clauses are a sentence, where
	<sentence> ::= []			(true)
		|  <clause> . <sentence>	(and)

	<clause> ::= clause(<atoms>, <atoms>)
	<atoms> ::= [] | <atom> . <atoms>

    Note that this representation of a clause is not quite the
    usual one.  clause([a,b,c], [d,e,f]) represents
	a v b v c <- d & e & f
    or, if you don't like "Kowalski form",
	a v b v c v ~d v ~e v ~f

    The reason for the two entry points is that the formula may
    contain free variables, these are to be understood as being
    universally quantified, and the negation of the universal
    closure of a formula is not at all the same thing as the
    universal closure of the negation!

    units_separated takes a list of clauses such as the other two predicates
    might produce, and separates them into a list of positive unit clauses
    (represented just by <atom>s), a list of negative unit clauses (also
    represented by their single <atom>s), and a list of non-unit clauses.
    Some theorem provers might find this separation advantageous, but it is
    not buillt into clausal_form becauses some provers would not benefit.

----------------------------------------------------------------------*/

:- public
	clausal_form/2,
	clausal_form_of_negation/2,
	units_seaparated/4.

:- mode
	clausal_form(+, -),
	clausal_form_of_negation(+, -),
	    pass_one(+, -),
		pass_one(+, +, -),
		pass_one(+, -, +, +, -),
		term_one(+, +, +, -),
		    term_one(+, +, +, +, -),
	    pass_two(+, -),
		pass_two_pos(+, -),
		pass_two_pos(+, -, +, +),
		    term_two(+, -, +),
			term_var(+, +, -),
			term_two(+, +, +, +),
		pass_two_neg(+, -, +, +),
		    sent_and(+, +, -),
		    sent_or(+, +, -),
	units_separated(+, -, -, -),
	contains(+, ?),
	literally_contains(+, +),
	does_not_literally_contain(+, ?).


:- op(700, xfx, [contains,literally_contains,does_not_literally_contain]).
:- op(910,  fy, ~).
:- op(920, xfy, and).
:- op(930, xfy, or).
:- op(940, xfx, [=>, <=>]).


units_separated([], [], [], []).
units_separated([clause([],[Neg])|Clauses], PosL, [Neg|NegL], NonL) :- !,
	units_separated(Clauses, PosL, NegL, NonL).
units_separated([clause([Pos],[])|Clauses], [Pos|PosL], NegL, NonL) :- !,
	units_separated(Clauses, PosL, NegL, NonL).
units_separated([Clause|Clauses], PosL, NegL, [Clause|NonL]) :-
	units_separated(Clauses, PosL, NegL, NonL).


clausal_form(Formula, Clauses) :-
	pass_one(Formula, ClosedAndImplicationFree),
	pass_two(ClosedAndImplicationFree, Clauses).


clausal_form_of_negation(Formula, Clauses) :-
	pass_one(Formula, ClosedAndImplicationFree),
	pass_two(~ClosedAndImplicationFree, Clauses).


/*----------------------------------------------------------------------

    The first pass over the formula does two things.
    1a. It locates the free variables of the formula.
    2.  It applies the rules
	    A => B	--> B v ~A
	    A <=> B	--> (B v ~A) /\ (A v ~B)
	    if(A,B,C)	--> (B v ~A) /\ (A v C)
	to eliminate implications.  Even in a non-clausal
	theorem prover this can be a good idea, eliminating
	<=> and if is essential if each subformula is to
	have a definite parity, and that in turn is vital
	if we are going to replace existential quantifiers
	by Skolem functions.
    1b. It adds explicit quantifiers for the free variables.
    The predicate which does all this is pass_one/5:
	pass_one(+Formula,		% The original formula
		 -Translation,		% its implication-free equivalent
		 +Bound,		% The binding environment
		 +Free0,		% The variables known to be free
		 -Free)			% Free0 union Formula's free variables
    The binding environment just tells us which variables occur in quantifiers
    dominating this subformula, it doesn't matter yet whether they're
    universal or existential.

    The translated formula is still an <expr>, although there are practical
    advantages to be gained by adopting a slightly different representation,
    but the neatness of being able to say that
	pass_one(F, G) --> pass_one(G, G)
    outweighs them.

----------------------------------------------------------------------*/

pass_one(Formula, ClosedAndImplicationFree) :-
	pass_one(Formula, ImplicationFree, [], [], FreeVariables),
	pass_one(FreeVariables, ImplicationFree, ClosedAndImplicationFree).


pass_one([], Formula, Formula).
pass_one([Var|Vars], Formula, all(Var,Closure)) :-
	pass_one(Vars, Formula, Closure).


pass_one(all(Var,B), all(Var,D), Bound, Free0, Free) :- !,
	pass_one(B, D, [Var|Bound], Free0, Free).
pass_one(some(Var,B), some(Var,D), Bound, Free0, Free) :- !,
	pass_one(B, D, [Var|Bound], Free0, Free).
pass_one(A and B, C and D, Bound, Free0, Free) :- !,
	pass_one(A, C, Bound, Free0, Free1),
	pass_one(B, D, Bound, Free1, Free).
pass_one(A or B, C or D, Bound, Free0, Free) :- !,
	pass_one(A, C, Bound, Free0, Free1),
	pass_one(B, D, Bound, Free1, Free).
pass_one(A => B, D or ~C, Bound, Free0, Free) :- !,
	pass_one(A, C, Bound, Free0, Free1),
	pass_one(B, D, Bound, Free1, Free).
pass_one(A <=> B, (D or ~C) and (C or ~D), Bound, Free0, Free) :- !,
	pass_one(A, C, Bound, Free0, Free1),
	pass_one(B, D, Bound, Free1, Free).
pass_one(if(T,A,B), (C or ~U) and (D or U), Bound, Free0, Free) :- !,
	pass_one(T, U, Bound, Free0, Free1),
	pass_one(A, C, Bound, Free1, Free2),
	pass_one(B, D, Bound, Free2, Free).
pass_one(~A, ~C, Bound, Free0, Free) :- !,
	pass_one(A, C, Bound, Free0, Free).
pass_one(Atom, Atom, Bound, Free0, Free) :-
	%   An Atom is "anything else".  If Atoms were explicitly flagged,
	%   say by being written as +Atom, we wouldn't need those wretched
	%   cuts all over the place.  The same is true of pass_two.
	term_one(Atom, Bound, Free0, Free).


%   term_one/4 scans a term which occurs in a context where some
%   variables are Bound by quantifiers and some free variables (Free0)
%   have already been discovered.  Free is returned as the union of the
%   free variables in this term with Free0.  Note that though we call
%   does_not_literally_contain twice, it is doing two different things.
%   The first call determines that the variable is free.  The second
%   call is part of adding an element to a set, which could perhaps have
%   been a binary tree or some other data structure.

term_one(Term, Bound, Free0, Free) :-
	nonvar(Term),
	functor(Term, _, Arity),
	!,
	term_one(Arity, Term, Bound, Free0, Free).
term_one(Var, Bound, Free0, [Var|Free0]) :-
	Bound does_not_literally_contain Var,
	Free0 does_not_literally_contain Var,
	!.
term_one(_, _, Free0, Free0).

term_one(0, _, _, Free0, Free0) :- !.
term_one(N, Term, Bound, Free0, Free) :-
	arg(N, Term, Arg),
	term_one(Arg, Bound, Free0, Free1),
	M is N-1, !,
	term_one(M, Term, Bound, Free1, Free).


/*----------------------------------------------------------------------

    pass_two does the following in one grand sweep:
    1.  The original formula might have used the same variable in any
        number of quantifiers.  In the output, each quantifier gets a
	different variable.
    2.  But existentally quantified variables are replaced by new Skolem
	functions, not by new variables.  As a result, we can simply drop
	all the quantifiers, every remaining variable is universally
	quantified.
    3.  The rules
	~ all(V, F)	--> some(V, ~F)
	~ some(V, F)	--> all(V, ~F)
	~ (A and B)	--> ~A or ~B
	~ (A or B)	--> ~A and ~B
	~ ~ A		--> A
	are applied to move negations down in front of atoms.
    4.  The rules
	A or A		--> A
	A or ~A		--> true
	A or true	--> true
	A or false	--> A
	(A or B) or C	--> A or (B or C)
	(A and B) or C	--> (A or C) and (B or C)
	A or (B and C)	--> (A or B) and (A or C)
	A and true	--> A
	A and false	--> false
	(A and B) and C	--> A and (B and C)
	are applied to the clauses which we build as we work our
	way back up the tree.  The rules
	A and A		--> A
	A and ~A	--> false
	A and (~A or B)	--> A and B
	are NOT applied.  This is best done, if at all, after all the
	clauses have been generated.  The last two rules are special
	cases of resolution, so it is doubtful whether it is worth
	doing them at all.

    The main predicate is pass_two_pos/4:
	pass_two_pos(+Formula,		% The formula to translate
		     -Translation,	% its translation
		     +Univ,		% universal quantifiers in scope
		     +Rename)		% how to rename variables
    Rename is var | var(Old,New,Rename), where Old is a source variable,
    and New is either a new variable (for universal quantifiers) or a
    Skolem function applied to the preceding new variables (for existential
    quantifiers).  Univ is those New elements of the Rename argument which
    are variables.  pass_two_neg produces the translation of its Formula's
    *negation*, this saves building the negation and then handling it.

----------------------------------------------------------------------*/

pass_two(ClosedAndImplicationFree, ClausalForm) :-
	pass_two_pos(ClosedAndImplicationFree, PreClausalForm, [], var),
	pass_two_pos(PreClausalForm, ClausalForm).


%   pass_two_pos/2 does two things.  First, if there was only one clause,
%   pass_two_pos/4 wouldn't have wrapped it up in a list.  This we do here.
%   Second, if one of the clauses is "false", we return that as the only
%   clause.  This would be the place to apply A & A --> A.

pass_two_pos(clause(P,N), [clause(P,N)]) :- !.
pass_two_pos(Sentence, [clause([],[])]) :-
	Sentence contains clause([],[]),
	!.
pass_two_pos(Sentence, Sentence).


pass_two_pos(all(Var,B), Translation, Univ, Rename) :- !,
	pass_two_pos(B, Translation, [New|Univ], var(Var,New,Rename)).
pass_two_pos(some(Var,B), Translation, Univ, Rename) :- !,
	gensym('f-', SkolemFunction),
	SkolemTerm =.. [SkolemFunction|Univ],
	pass_two_pos(B, Translation, Univ, var(Var,SkolemTerm,Rename)).
pass_two_pos(A and B, Translation, Univ, Rename) :- !,
	pass_two_pos(A, C, Univ, Rename),
	pass_two_pos(B, D, Univ, Rename),
	sent_and(C, D, Translation).
pass_two_pos(A or B, Translation, Univ, Rename) :- !,
	pass_two_pos(A, C, Univ, Rename),
	pass_two_pos(B, D, Univ, Rename),
	sent_or(C, D, Translation).
pass_two_pos(~A, Translation, Univ, Rename) :- !,
	pass_two_neg(A, Translation, Univ, Rename).
pass_two_pos(true, [], _, _) :- !.
pass_two_pos(false, clause([],[]), _, _) :- !.
pass_two_pos(Atom, clause([Renamed],[]), _, Rename) :-
	%   An Atom is "anything else", hence the cuts above.
	term_two(Atom, Renamed, Rename).


pass_two_neg(all(Var,B), Translation, Univ, Rename) :- !,
	gensym('g-', SkolemFunction),
	SkolemTerm =.. [SkolemFunction|Univ],
	pass_two_neg(B, Translation, Univ, var(Var,SkolemTerm,Rename)).
pass_two_neg(some(Var,B), Translation, Univ, Rename) :- !,
	pass_two_neg(B, Translation, [New|Univ], var(Var,New,Rename)).
pass_two_neg(A and B, Translation, Univ, Rename) :- !,
	pass_two_neg(A, C, Univ, Rename),
	pass_two_neg(B, D, Univ, Rename),
	sent_or(C, D, Translation).
pass_two_neg(A or B, Translation, Univ, Rename) :- !,
	pass_two_neg(A, C, Univ, Rename),
	pass_two_neg(B, D, Univ, Rename),
	sent_and(C, D, Translation).
pass_two_neg(~A, Translation, Univ, Rename) :- !,
	pass_two_pos(A, Translation, Univ, Rename).
pass_two_neg(true, clause([],[]), _, _) :- !.
pass_two_neg(false, [], _, _) :- !.
pass_two_neg(Atom, clause([],[Renamed]), _, Rename) :-
	%   An Atom is "anything else", hence the cuts above.
	term_two(Atom, Renamed, Rename).



term_two(OldTerm, NewTerm, Rename) :-
	nonvar(OldTerm),
	functor(OldTerm, FunctionSymbol, Arity),
	functor(NewTerm, FunctionSymbol, Arity),
	!,
	term_two(Arity, OldTerm, NewTerm, Rename).
term_two(OldVar, NewTerm, Rename) :-
	term_var(Rename, OldVar, NewTerm).


term_var(var(Old,New,_), Var, New) :-
	Old == Var,
	!.
term_var(var(_,_,Rest), Var, New) :-
	term_var(Rest, Var, New).


term_two(0, _, _, _) :- !.
term_two(N, OldTerm, NewTerm, Rename) :-
	arg(N, OldTerm, OldArg),
	term_two(OldArg, NewArg, Rename),
	arg(N, NewTerm, NewArg),
	M is N-1, !,
	term_two(M, OldTerm, NewTerm, Rename).


/*----------------------------------------------------------------------

	sent_and(S1, S2, "S1 and S2")
	sent_or(S1, S2, "S1 or S2")
    perform the indicated logical operations on clauses or sets of
    clauses (sentences), using a fair bit of propositional reasoning
    (hence our use of "literally" to avoid binding variables) to try
    to keep the results simple.  There are several rules concerning
    conjunction which are *not* applied, but even checking for
	A and A --> A
    would require us to recognise alphabetic variants of A rather
    than literal identity.  So far the naivety abount conjunction
    has not proved to be a practical problem.

----------------------------------------------------------------------*/

sent_or(clause(P1,_), clause(_,N2), []) :-
	P1 contains Atom,
	N2 literally_contains Atom,
	!.
sent_or(clause(_,N1), clause(P2,_), []) :-
	N1 contains Atom,
	P2 literally_contains Atom,
	!.
sent_or(clause(P1,N1), clause(P2,N2), clause(P3,N3)) :- !,
	ord_union(P1, P2, P3),
	ord_union(N1, N2, N3).
sent_or([], _, []) :- !.
sent_or(_, [], []) :- !.
sent_or([Clause|Clauses], Sentence, Answer) :- !,
	sent_or(Sentence, Clause, X),
	sent_or(Clauses, Sentence, Y),
	sent_and(X, Y, Answer).
sent_or(Sentence, [Clause|Clauses], Answer) :- !,
	sent_or(Sentence, Clause, X),
	sent_or(Clauses, Sentence, Y),
	sent_and(X, Y, Answer).


sent_and([], Sentence, Sentence) :- !.
sent_and(Sentence, [], Sentence) :- !.
sent_and([H1|T1], [H2|T2], [H1,H2|T3]) :- !,
	sent_and(T1, T2, T3).
sent_and([H1|T1], Clause, [Clause,H1|T1]) :- !.
sent_and(Clause, [H2|T2], [Clause,H2|T2]) :- !.
sent_and(Clause1, Clause2, [Clause1,Clause2]).


[Head|_] contains Head.
[_|Tail] contains Something :-
	Tail contains Something.


[Head|_] literally_contains Something :-
	Head == Something,
	!.
[_|Tail] literally_contains Something :-
	Tail literally_contains Something.


[] does_not_literally_contain Anything.
[Head|Tail] does_not_literally_contain Something :-
	Head \== Something,
	Tail does_not_literally_contain Something.


/*----------------------------------------------------------------------
    Debugging kit.
	portray_sentence(ListOfClauses)
	    displays a list of clauses, one per line.
	portray_clause(Clause)
	    displays a single clause in "Kowalski notation"
	t(Formula)
	    translates a formula and prints the result.

:- public
	t1/0,t9/0,t/1.


portray_sentence([Clause]) :- !,
	portray_clause(Clause),
	nl.
portray_sentence([Clause|Clauses]) :-
	portray_clause(Clause),
	write(' AND'), nl,
	portray_sentence(Clauses).
portray_sentence([]) :-
	write('TRUE'), nl.


portray_clause(clause(PosAtoms, NegAtoms)) :-
	numbervars(PosAtoms, 0, N),
	numbervars(NegAtoms, N, _),
	portray_clause(PosAtoms, ' v '),
	write(' <- '),
	portray_clause(NegAtoms, ' & '),
	fail.
portray_clause(_).


portray_clause([Atom], _) :- !,
	print(Atom).
portray_clause([Atom|Atoms], Separator) :-
	print(Atom), write(Separator),
	portray_clause(Atoms, Separator).
portray_clause([], _) :-
	write([]).


t(X) :-
	clausal_form(X, Y),
	portray_sentence(Y).

t1 :- t((a=>b) and (b=>c) and (c=>d) and (d=>a) => (a<=>d)).

t2 :- t(continuous(F,X) <=> all(Epsilon, Epsilon > 0 =>
	    some(Delta, Delta > 0 and all(Y,
		abs(Y-X) < Delta => abs(val(F,Y)-val(F,X)) < Epsilon
	)))).

t3 :- clausal_form_of_negation(
	( subset(S1,S2) <=> all(X, member(X,S1) => member(X,S2) )) =>
	( subset(T1,T2) and subset(T2,T3) => subset(T1,T3) )	,Y),
	portray_sentence(Y).

t4 :- t(subset(T1,T2) and subset(T2,T3) => subset(T1,T3)).


t5 :- t((a=>b) and (b=>c)).

t6 :- t(~(a and b)).

t7 :- t((a and b) or c).

t8 :- t((a and b) or (a and ~b) or (~a and b) or (~a and ~b)).

t9 :- t(
	(true(P) <=> t(w0,P)) and
	(t(W1,P1 and P2) <=> t(W1,P1) and t(W1,P2)) and
	(t(W1,P1 or P2) <=> t(W1,P1) or t(W1,P2)) and
	(t(W1,P1 => P2) <=> (t(W1,P1) => t(W1,P2))) and
	(t(W1,P1 <=> P2) <=> (t(W1,P1) <=> t(W1,P2))) and
	(t(W1,~P1) <=> ~t(W1,P1)) and
	(t(W1,know(A1,P1)) <=> all(W2,k(A1,W1,W2)=>t(W2,P1))) and
	k(A1,W1,W1) and
	(k(A1,W1,W2) => (k(A1,W2,W3) => k(A1,W1,W3))) and
	(k(A1,W1,W2) => (k(A1,W1,W3) => k(A1,W2,W3))) and
	(t(W1,know(A,P)) <=> all(W2,k(A,W1,W2) => t(W2,P)))
	).

----------------------------------------------------------------------*/
 EOF1CLAUSE.PL              00010019000100 85045 99364 000033Unix V7             HDR1COUNT.HLP              00010020000100 85045 99364 000000Unix V7             File: Mec:Count.Hlp	Author: R.A.O'Keefe	Updated: 8 September 82

#purpose.

	The COUNT program is a utility for counting the number of clauses
and predicates in a Prolog source file or program.  It has two commands:
?-	help.	%   starts you on the help system.
?-	count.	%   enters a loading loop like XREF.
?-	halt.	%   is the ordinary Prolog command.  ^Z will do as well.

	In response to 'count', the program will prompt 'Next file: '.
Your reply to this is a Dec-10 file name terminated by a carriage return,
or an empty line (just carriage return).

	If a file consults, reconsults, or compiles other files, Count
will inspect them and report on them too.  The counts of clauses and
predicates for a file include the counts for all its constituent files.
When you enter an empty line, the grand totals are printed and cleared,
and Count returns to command level.

#files.

Count expects to be given Dec-10 file names, but Prolog cannot accept
a PPN or path.  Thus what you type in response to the 'Next file: '
prompt is
	[Device :] FileName [. Extension]
where Device and FileName are identifiers of up to 6 characters, and
Extension is an identifier of up to 3 characters.  (The identifiers
will be truncated if necessary.)  If the extension is empty, .PL, or
.CPL it may be omitted.  The device may be omitted too.

Note that the file name should NOT have quotes around it or an extra
period at the end.  Typing ^Z will not work; you have to type just a
carriage return to stop the 'go' loop.

#data_base.

Count leaves a '$seen'(Pred,Arity) and a '$defn'(Pred,Arity,File) fact
in the data base for each predicate that it sees, unless such facts
are already known.  This is enough to support some pp/ixref features,
notably "from(File)" and pretty-printing things that aren't loaded.
They may be useful for other things too.  How's your imagination?

EOF1COUNT.HLP              00010020000100 85045 99364 000004Unix V7             HDR1COUNT.PL               00010021000100 85045 99364 000000Unix V7             %   File   : COUNT.PL
%   Author : Richard A. O'Keefe
%   Updated: 4 August 1984
%   Purpose: Find out how big a file or program is.
%   Needs  : getfile/1 from UTIL:GETFIL.PL, give_help from UTIL:HELPER.PL,
%	     and try_hard_to_see/3 from UTIL:TRYSEE.PL.

%   This program is a re-implementation of Chris Mellish's clause counter,
%   which uses my 'try_hard_to_see' and 'help' mechanisms.  For information
%   on how to use the program, run it and type 'help' or .Help Util:Count
%   It has now been rehacked to live in ToolKit, so 'help' is redundant.

:- public
/*	help/0,			%  displays Mec:Count.Hlp		*/
	count/0.		%  top level

:- mode
	count_command(+,-),
	count_command(+,-,-),
	count,
	count(+,-,+,-),
	count(+,+,-,+,-),
/*	help,								*/
	load_goals(+,-,-),
	load_list(+,-,-),
	proceed(+,-,-),
	proceed(+,+,-,+,-),
	read_and_expand(-).


/*  help :- 								*/
/*	give_help(count, count).					*/


count :-
/*	abolish(noticed, 2),						*/
	count(0, C, 0, P),
	writef('%20L%5R clauses%4R predicates.\n',
		['Grand total:', C, P]).

count(Cold, Cnew, Pold, Pnew) :-
	getfile(File), !,
	count(File, Cold, Cnew, Pold, Pnew).
 
count('',   Cfin, Cfin, Pfin, Pfin) :- !.
count('?',  Cold, Cnew, Pold, Pnew) :-
	give_help(count, files), !,
	count(Cold, Cnew, Pold, Pnew).
count(File, Cold, Cnew, Pold, Pnew) :-
	proceed(File, C, P),
	Cmid is Cold+C,
	Pmid is Pold+P, !,
	count(Cmid, Cnew, Pmid, Pnew).


proceed(File, C, P) :-
	seeing(OldFile),
	try_hard_to_see(File, ['press','mec','util'], ['pl', 'cpl']), !,
	read_and_expand(Term),
	proceed(Term, 0, C, 0, P),
	seeing(FileName),
	seen,
	see(OldFile), !,
	writef('%20L%5R clauses%4R predicates.\n', [FileName, C, P]).
proceed(File, 0).


read_and_expand(Term) :-
	read(Read),
	(   var(Read), Term = true, !
	;   expand_term(Read, Term)
	).
 
 
proceed(end_of_file, Cfin, Cfin, Pfin, Pfin) :- !.
proceed(Term, Cold, Cnew, Pold, Pnew) :-
	count_command(Term, C, P),
	Cmid is Cold+C,
	Pmid is Pold+P,
	read_and_expand(Next), !,
	proceed(Next, Cmid, Cnew, Pmid, Pnew).
 
 
count_command(( :- Goals ), C, P) :- !,
	load_goals(Goals, C, P).
count_command(( ?- Goals ), C, P) :- !,
	load_goals(Goals, C, P).
count_command((Head:-Body), 1, P) :- !,
	count_command(Head, P).
count_command(Head, 1, P) :- !,
	count_command(Head, P).

count_command(Head, 1) :-
	functor(Head, F, N),
	\+ '$seen'(F, N), !,
	seeing(File),
	assertz('$seen'(F, N)),
	assertz('$defn'(F, N, File)).
count_command(Head, 0).
 
 
load_goals((G1,G2), C, P) :- !,
	load_goals(G1, C1, P1),
	load_goals(G2, C2, P2),
	C is C1+C2,
	P is P1+P2.
load_goals(compile(L), C, P) :- !,
	load_list(L, C, P).
load_goals([A|B], C, P) :- !,
	load_list([A|B], C, P).
load_goals(consult(L), C, P) :- !,
	load_list(L, C, P).
load_goals(reconsult(L), C, P) :- !,	% not quite right
	load_list(L, C, P).
load_goals(load(L), C, P) :- !,
	load_list(L, C, P).
load_goals(op(P,T,A), 0, 0) :- !,
	op(P, T, A).
load_goals(_, 0, 0).

 
load_list([File|Rest], C, P) :- !,
	load_list(File, C1, P1),
	load_list(Rest, C2, P2),
	C is C1+C2,
	P is P1+P2.
load_list([], 0, 0) :- !.
load_list(-File, C, P) :- !,	% not quite right
	proceed(File, C, P).
load_list(File, C, P) :-
	atom(File), !,
	proceed(File, C, P).
load_list(_, 0, 0).
 
EOF1COUNT.PL               00010021000100 85045 99364 000007Unix V7             HDR1CTYPES.PL              00010022000100 85045 99364 000000Unix V7             %   File   : CTYPES.PL
%   Author : Richard A. O'Keefe
%   Updated: 9 September 1984
%   Purpose: Character classification

/*  The predicates to_lower, to_upper, is_alnum, is_alpha, is_cntrl,
    is_digit, is_graph, is_lower, is_upper, is_print, is_punct, and
    is_space are taken directly from the April 84 draft of the C
    standard.  The remaining ones are of my own invention, but are
    reasonably useful.  If you want to make your programs portable
    between different operating systems, use is_endline and is_endfile
    instead of the literal constants 31 and 26 or 10 and -1.
*/
:- public
	is_alnum/1,
	is_alpha/1,
	is_cntrl/1,
	is_digit/1,
	is_endfile/1,
	is_graph/1,
	is_lower/1,
	is_newline/1,
	is_newpage/1,
	is_paren/1,
	is_period/1,
	is_print/1,
	is_punct/1,
	is_quote/1,
	is_space/1,
	is_upper/1,
	to_lower/2,
	to_upper/2.

:- mode
	'_ul_hack'(+, +, +, +, ?).


is_alnum(C) :-
	is_alpha(C).
is_alnum(C) :-
	is_digit(C).


is_alpha(C) :-
	is_lower(C).
is_alpha(C) :-
	is_upper(C).
is_alpha(0'_).


is_cntrl(127).
is_cntrl(C) :-
	between(0, 31, C).


is_digit(C) :-
	between(0'0, 0'9, C).


is_endfile(26).			% is -1 in the draft standard


is_graph(C) :-
	between(33, 126, C).


is_lower(C) :-
	between(0'a, 0'z, C).


is_newline(31).			% may be 10 or 13 (O/S-dependent)


is_newpage(12).			% may fail (O/S-dependent)


is_paren(0'(, 0')).
is_paren(0'[, 0']).
is_paren(0'{, 0'}).
is_paren(0'<, 0'>).		% should this be in?


is_period(0'.).			% a period is anything that ends a sentence
is_period(0'?).			% (also known as a period).  . is a full stop.
is_period(0'!).


is_print(C) :-
	between(32, 126, C).


is_punct(C) :-			% between space and digits
	between(33, 47, C).
is_punct(C) :-			% between digits and uppers
	between(58, 64, C).
is_punct(C) :-			% between uppers and lowers
	between(91, 96, C), C \== 0'_.
is_punct(C) :-			% between lowers and delete
	between(123, 126, C).


is_quote(0'').
is_quote(0'").
is_quote(0'`).


is_space(32).			% ` `
is_space(31).			% `\n` in Dec-10 Prolog
is_space( 9).			% `\t`
is_space(10).			% -maybe- `\n`
is_space(11).			% `\v`
is_space(12).			% `\f`
is_space(13).			% `\r`


is_upper(C) :-
	between(0'A, 0'Z, C).


%   to_lower and to_upper are complicated by the fact that the
%   Dec-10 compiler doesn't handle if->then;else.  _ul_hack would not
%   be necessary otherwise.

to_lower(U, L) :-
	between(0, 127, U),
	'_ul_hack'(U, 0'A, 0'Z, 32, L).


to_upper(L, U) :-
	between(0, 127, L),
	'_ul_hack'(L, 0'a, 0'z, -32, U).


'_ul_hack'(X, A, Z, D, Y) :-
	A =< X, X =< Z, !,
	Y is X+D.
'_ul_hack'(X, _, _, _, Y).


EOF1CTYPES.PL              00010022000100 85045 99364 000006Unix V7             HDR1DCSG.EX                00010023000100 85045 99364 000000Unix V7             %   VOCABULARY

noun(frog).
noun(lake).
noun(water).
noun(man).
noun(woman).
noun(cat).
noun(mouse).
noun(fish).

proper(fish).	%  come now!
proper(tom).
proper(jerry).
proper(jim).
proper(fred).
proper(john).

transitive(drinks).
transitive(owns).
transitive(likes).
transitive(loves).
transitive(chased).

intransitive(swims).
intransitive(jumps).
intransitive(lives).
intransitive(squeaks).

preposition(into).
preposition(near).
preposition(in).
preposition(to).

adjective(green).
adjective(blue).
adjective(big).
adjective(small).


%   EXAMPLE	1

s --> np, vp.
s --> Alpha^[np,pP], np, vp/Alpha.
% s --> np, np, vp/np.
% s --> pP, np, vp/pP.

np --> [the], adjs, noun, rel.
np --> [W], {proper(W)}.

adjs --> [W], {adjective(W)}, adjs.
adjs --> [].

noun --> [W], {noun(W)}.

rel --> [that], s/np.
rel --> [].

vp --> [W], {transitive(W)}, np, pPs.
vp --> [W], {intransitive(W)}, pPs.

pPs --> pP, pPs.
pPs --> [].

pP --> [W], {preposition(W)}, np.

t1(N) :- t1(N,S), s(0,0,S,[]).
t1(1, [jim,owns,the,frog]).
t1(2, [the,frog,that,jim,owns,swims]).
t1(3, [jim,swims,in,the,lake,that,the,frog,likes]).
t1(4, [jim,owns,the,frog,that,likes,jim]).
t1(5, [in,the,lake,fred,swims]).
t1(6, [the,green,frog,likes,the,lake,that,fred,swims,in]).
t1(7, [into,the,blue,lake,that,fred,swims,in,the,frog,jumps]).
t1(8, [jim,owns,the,lake,that,the,frog,jumps,into]).


% EXAMPLE	2
% This is Fernando Pereira's XG example.

sentence --> noun_phrase, verb_phrase.
sentence --> Alpha^[noun_phrase,prep_phrase], noun_phrase, verb_phrase/Alpha.

noun_phrase --> [W], {proper(W)}.
noun_phrase --> determiner, noun, ( relative | prep_phrase | [] ).

determiner --> [the].
determiner --> [a].
determiner --> [an].

noun --> [W], {noun(W)}.


verb_phrase --> [W], {transitive(W)}, noun_phrase.
verb_phrase --> [W], {intransitive(W)}, (prep_phrase | []).


relative --> [that], sentence/noun_phrase.

prep_phrase --> [W], {preposition(W)}, noun_phrase.

t2(N) :- t2(N,S), dcsg_phrase(sentence, S).

t2(1, [the,mouse,squeaks]).
t2(2, [the,cat,likes,fish]).
t2(3, [the,cat,chased,the,mouse]).
t2(4, [the,mouse,that,the,cat,that,chased,likes,fish,squeaks]).
% 4: rejected
t2(5, [the,mouse,that,the,cat,that,likes,fish,chased,squeaks]).
t2(6, [to,the,fish,tom,chased,the,mouse]).
t2(7, [to,the,fish,tom,chased,the,mouse,that,squeaks]).
% 7: rejected.  (In fact ungrammatical according to this grammar.)
% if accepted would be (tom) chased (the mouse (that squeaks (to the fish)))
t2(8, [jerry,the,cat,that,likes,fish,chased]). 


%   EXAMPLE	3
%   This is the example in the Dec-10 Prolog manual.

sentence(P) -->
	noun_phrase(X, P1, P),
	verb_phrase(X, P1).
sentence(P) -->
	noun_phrase(Y, Q1, Q),
	noun_phrase(X, P1, P),
	verb_phrase(X, P1)/noun_phrase(Y, Q1, Q).

noun_phrase(X, P, P) -->
	[X], {proper(X)}, !.
noun_phrase(X, P1, P) -->
	determiner(X, P2, P1, P),
	noun(X, P3),
	rel_clause(X, P3, P2).

rel_clause(X, P1, (P1,P2)) -->
	[that], verb_phrase(X, P2).
rel_clause(X, P, P) -->
	[].

determiner(X, P1, P2, each(X,P1,P2)) --> [every].
determiner(X, P1, P2, some(X,P1,P2)) --> [a] | [some].

noun(X, P) --> [W], {noun(W)}, {P =.. [W,X]}.

verb_phrase(X, P) -->
	[W], {transitive(W)},
	noun_phrase(Y, P1, P),
	{P1 =.. [W,X,Y]}.
verb_phrase(X, P) -->
	[W], {intransitive(W)},
	{P =.. [W,X]}.

t3(N) :-
	t3(N,S),
	dcsg_phrase(sentence(P), S),
	numbervars(P, 0, _), 
	write(P).

t3(1, [every,man,that,lives,loves,a,woman]).
t3(2, [some,woman,some,man,that,lives,loves]).
t3(3, [a,woman,lives,that,loves,jim]).
% 3 is not grammatical according to the grammar above
t3(4, [a,woman,that,loves,jim,likes,every,frog,that,swims]).
 EOF1DCSG.EX                00010023000100 85045 99364 000008Unix V7             HDR1DCSG.HLP               00010024000100 85045 99364 000000Unix V7             Definite Clause Slash Grammars are a variation of DCGs which allow
Gazdar-style slash catgories and a limited form of rule schemas.

DCSG.PL		is a Prolog source file which defines a predicate
		dcsg(Files) for loading files containing dcsgs.
DCSG.EX		is a collection of small examples of dcsgs.
 EOF1DCSG.HLP               00010024000100 85045 99364 000001Unix V7             HDR1DCSG.PL                00010025000100 85045 99364 000000Unix V7             %   File   : DCSG.PL
%   Author : R.A.O'Keefe
%   Updated: 3 December 1983
%   Purpose: Preprocessor for Definite Clause Slash Grammars
%   Needs  : append/3, member/2, memberchk/2.

/*  Definite Clause Slash Gammars are a special kind of DCG which
    allows slash categories, e.g. s/np.
    A nonterminal with N arguments is translated into a predicate
    with N+4 arguments, 
	nt(X1,...,Xn) => nt(X1,...,Xn,T0,T,S0,S)
    where T0 is the "trace" before and T that after parsing an nt,
    and S0 is the string before and S that after parsing an nt.
    The translator produces a clause

	non_terminal(nt(X1,...,Xn), T0, T, S0, S) :-
		nt(X1,...,Xn, T0, T, S0, S).

    for each non-terminal.  This was meant for variables standing
    in non-terminal positions.  You can't write

	s --> Alpha, s/Alpha.

    but you can write

	s -->
		Alpha^[np,pp],
		s/Alpha.

    which turns into

	s(T0, T, S0, S) :-
		member(Alpha, [np, pp]),
		non_terminal(Alpha, T0, T, S0, S1),
		s(Alpha, 0, S1, S).

    Most of the complexity in this translator comes from the decision
    to allow schematic rules, where a nonterminal or an extraposed item
    can be a variable, followed by ^[list of possibilities].  I am not
    sure that it was worth while, as I don't actually have any use for
    this case.

    Note: you can block extraposition out of a particular place by using
    /0.  That is, if nonterm/0 appears in a rule, nothing can be
    extraposed out of nonterm.  You can block a particular kind of movement
    by writing e.g.
	vp_obj/Alpha --> vp/Alpha, {Alpha\=np(subj,_,_)}.
    which will let anything out that vp will, except a subject np, and
    then you can call vp_obj instead of vp in appropriate places.

    If you haven't heard of Gazdar, you probably won't know what
    slash categories are for.  Generalised Phrase Structure Grammar
    augments context-free grammars with
	features	(function-free DCG arguments)
	slash categories(as here)
	rule schemas	(as done badly here)
	meta-rules	(sort of like transformations, but not really)
    in such a way that only the notational convenience is increased,
    the grammars still have only context-free power.  As I understand
    it, Gazdar doesn't claim that English *is* a context-free language,
    only that the arguments to date that it is *not* are about as sound
    as a 3-dollar note.  I have to draw a distinction between DCSGs
    (which are just logic programs, and are at least as powerful as
    GPSGs) and the particular parser obtained by using Prolog as the
    parser.  When we use Prolog to parse DCSGs, we run into problems
    with left-recursion and so on that have nothing to do with the
    formalism.  Henry Thompson, DAI Edinburgh, has a full implementation
    of GPSGs in Lisp, including meta-rules, which is a chart parser.
    This file is intended as a cheap way of exploring some of the ideas
    of GPSG, no more.  A serious tool for people who can't hack Prolog
    would be very much bigger.
*/


:- public
	dcsg/1,
	dcsg_phrase/2.

:- mode
	dcsg(+),			%   Files ->
	dcsg_load(+),			%   Files ->
	dcsg_hack(+),			%   Term ->
	dcsg_head(+, +, -),		%   NT x Rhs -> Clause
	dcsg_note(+, +),		%   {clause|dcsg} x Goal ->
	dcsg_args(+, ?, ?, ?, ?, -),
	dcsg_choices(?, +, -),		%   Var x NT-list -> Goal-list
	dcsg_head(+, -, +, -, ?, ?),
	dcsg_body(+, ?, ?, ?, ?, -),
	dcsg_and(+, +, -),
	dcsg_or(+, ?, ?, ?, ?, -),
	dcsg_list(+, ?, ?, -),
	dcsg_choices(+, -),
	dcsg_choice(+, -),
	non_terminal(+, ?, ?, ?, ?, +),
	dcsg_phrase(+, +).


%   dscg(Files)
%   consults a list of Files which are expected to contain grammar
%   rules.  It first of all wipes out all the existing grammar rules,
%   so acts as a sort of reconsult.  I haven't really made up my
%   mind about that.  In any case, I've only the one file to load.

dcsg(_) :-
	clause(non_terminal(_,_,_,_,_), NT),
	functor(NT, NonTerm, Arity),
	abolish(NonTerm, Arity),
	fail.
dcsg(_) :-
	non_dcsg(Functor, Arity),
	abolish(Functor, Arity),
	fail.
dcsg(Files) :-
	abolish(non_dcsg, 2),
	abolish(non_terminal, 5),
	dcsg_load(Files).


dcsg_load([]) :- !.
dcsg_load([File|Files]) :- !,
	dcsg_load(File),
	dcsg_load(Files).
dcsg_load(File) :-
	atom(File),
	nofileerrors,
	see(File),
	fileerrors,
	repeat,
	    read(Term),
	    dcsg_hack(Term),
	    Term = end_of_file,
	!,
	seeing(FullName),
	seen,
	write(FullName), write(' loaded.'), nl.
dcsg_load(File) :-
	write('! can''t load '), write(File), nl.



%   dcsg_hack(Term)
%   processes each term as it is read.  Grammar rules are checked a bit,
%   but ordinary clauses (basically dictionary information) are not, though
%   it does check that a predicate doesn't have grammar rules and ordinary
%   clauses both.

dcsg_hack(end_of_file) :- !.
dcsg_hack((:-Command)) :- !,
	call(Command).			%  operator declarations
dcsg_hack((Head-->Body)) :- !,
	(   dcsg_head(Head, Body, Clause),
	    assertz(Clause)
	;   X=(Head-->Body), numbervars(X,0,_),
	    print(X), nl
	), !.
dcsg_hack((Head:-Body)) :- !,
	dcsg_note(clause, Head),
	assertz((Head :- Body)).
dcsg_hack(Head) :-
	dcsg_note(clause, Head),
	assertz(Head).



%   dcsg_note(Type, Head) 
%   is responsible for checking that a predicate doesn't have rules
%   and clauses both.  The main point of the dcsg_note predicate is
%   really to keep track of nonterminals so that variable nonterminals
%   and dcsg_phrase can work easily.

dcsg_note(clause, Head) :- !,
	nonvar(Head),
	functor(Head, Functor, Arity),
	functor(Term, Functor, Arity),
	(   non_dcsg(Functor, Arity)
	;   assert(non_dcsg(Functor, Arity)),
	    (   clause(non_terminal(Term, _, _, _, _), _),
		write('! Rules and clauses for '),
		write(Functor/Arity), nl
	    ;   true
	    )
	), !.
dcsg_note(dcsg, Head/Missing) :- !,
	dcsg_note(dcsg, Head).
dcsg_note(dcsg, Head) :-
	functor(Head, NonTerm, Arity),
	functor(Term, NonTerm, Arity),
	dcsg_args(Term, T0, T, S0, S, Goal),
	(   clause(non_terminal(Term, T0, T, S0, S), Goal)
	;   assertz(( non_terminal(Term,T0,T,S0,S) :- Goal )),
	    (   non_dcsg(NonTerm, Arity),
		write('! Rules and clauses for '),
		write(NonTerm/Arity), nl
	    ;   true
	    ),
	    T0 = Term, T = 0, S0 = S,
	    assertz(( Goal ))
	), !.



%   dcsg_args(NonTerm, T0, T, S0, S, Goal)
%   adds the extra arguments to the nonterminal to form a proper
%   Prolog goal.  Variables and slash categories are handled elsewhere.

dcsg_args(NonT, T0, T, S0, S, Goal) :-
	NonT =.. [NT|Args],
	append(Args, [T0, T, S0, S], Full),
	Goal =.. [NT|Full], !.


%   dcsg_head(NonTerm, Rhs, Clause),
%   processes the head of a grammar rule, which takes the form
%   <nonterminal> [/ <extraposition> [^ <restriction>]] .
%   The non-terminal itself may not be a variable, but the other
%   bit may be.  It then hands the Rhs on to dcsg_body to process.

dcsg_head(Dud, _, _) :-
	(   var(Dud)
	;   Dud = Var/_, var(Var)
	;   Dud = Var^_
	;   Dud = Var^_/_
	),
	!,
	write('! Rule head may not be a variable.'), nl, fail.
dcsg_head(NonT/Missing^Restriction, Rhs,
		(Head:-member(Missing,Choices),Body)) :-
	nonvar(Restriction), !,
	dcsg_choices(Missing, Restriction, Choices),
	dcsg_head(NonT, Head, Rhs, Body, Missing, 0).
dcsg_head(NonT/Missing, Rhs, (Head:-Body)) :- !,
	dcsg_head(NonT, Head, Rhs, Body, Missing, 0).
dcsg_head(NonT, Rhs, (Head:-Body)) :-
	dcsg_head(NonT, Head, Rhs, Body, _, _).


dcsg_head(NonT, Head, Rhs, Body, T0, T) :-
	dcsg_note(dcsg, NonT),
	dcsg_args(NonT, T0, T, S0, S, Head),
	dcsg_body(Rhs,  T0, T, S0, S, Body).



%   dcsg_body(RHS, <Context>, Translation)
%   translates the RHS of a DCSG grammar rule into Prolog.
%   The method is all but identical to that for DCGs or XGs.

dcsg_body(Var, T0, T, S0, S, non_terminal(Var,T0,T,S0,S,0)) :-
	var(Var),
	!.
dcsg_body(Var^Poss, T0, T, S0, S, non_terminal(Var,T0,T,S0,S,Choices)) :- !,
	dcsg_choices(Var, Poss, Choices).
dcsg_body((A,B), T0, T, S0, S, Trans) :- !,
	dcsg_body(A, T0, T1, S0, S1, A1),
	dcsg_body(B, T1, T,  S1, S,  B1),
	dcsg_and(A1, B1, Trans).
dcsg_body((A;B), T0, T, S0, S, (A1;B1)) :- !,
	dcsg_or(A, T0, T, S0, S, A1),
	dcsg_or(B, T0, T, S0, S, B1).
dcsg_body(!, T, T, S, S, !) :- !.
dcsg_body({Goals}, T, T, S, S, Goals) :- !.
dcsg_body([], T, T, S, S, true) :- !.
dcsg_body([Term|Terms], T, T, S0, S, Trans) :-
	dcsg_list([Term|Terms], S0, S, Trans).
dcsg_body(NonTerm/Missing^Restriction, T, T, S0, S,
		(Goal,member(Restriction,Choices))) :-
	nonvar(Missing), !,
	dcsg_choices(Missing, Restriction, Choices),
	dcsg_args(NonTerm, Missing, 0, S0, S, Goal).
dcsg_body(NonTerm/Missing, T, T, S0, S, Goal) :- !,
	dcsg_args(NonTerm, Missing, 0, S0, S, Goal).
dcsg_body(NonTerm, T0, T, S0, S, Goal) :-
	dcsg_args(NonTerm, T0, T, S0, S, Goal).


dcsg_list([Term], S0, S, 'C'(S0, Term, S)) :- !.
dcsg_list([Term|Terms], S0, S, ('C'(S0,Term,S1),Trans)) :-
	dcsg_list(Terms, S1, S, Trans).


dcsg_or(A, T0, T, S0, S, A3) :-
	dcsg_body(A, T1, T, S1, S, A1),
	(   S \== S1, !, S0 = S1, A2 = A1 ; dcsg_and(S0=S1, A1, A2)   ),
	(   T \== T1, !, T0 = T1, A3 = A2 ; dcsg_and(T0=T1, A2, A3)   ).


dcsg_and(true, X, X) :- !.
dcsg_and(X, true, X) :- !.
dcsg_and((X,Y), Z, (X,W)) :- !,
	dcsg_and(Y, Z, W).
dcsg_and(X, Y, (X,Y)).



%   dcsg_choices([list of nt or nt/arity or goal], [list of goal])
%   turns e.g. [a, b/3, c(X,Y), d(X), e/1] into e.g.
%   [a, b(_,_,_), c(X,Y), d(X), e(_)].  These lists are used to
%   restrict the range of nonterminal variables.
%   dcsg_choices(Var, Restriction, Choices)
%   does the translation, after first checking that the restricted thing
%   is in fact a Prolog variable.  Note that the "existential quantifier"
%   meaning of "^" is still available inside {escape-to-Prolog} braces.


dcsg_choices(Var, Restriction, Choices) :-
	var(Var), !,
	dcsg_choices(Restriction, Choices).
dcsg_choices(_, _, _) :-
	write('! Restriction on non-variable.'), nl, fail.


dcsg_choices([], []) :- !.
dcsg_choices([Head|Tail], [This|Rest]) :-
	dcsg_choice(Head, This),
	dcsg_choices(Tail, Rest).

dcsg_choice(Atom, Atom) :-
	atom(Atom), !.
dcsg_choice(Functor/Arity, Term) :- !,
	atom(Functor), integer(Arity), Arity >= 0,
	functor(Term, Functor, Arity).
dcsg_choice(Term, Term) :-
	nonvar(Term),
	functor(Term, _, Arity),
	Arity > 0.		%  rule out numbers



%   Variables appearing at the top level of a grammar rule are
%   turned into calls on nonterminal/6.
%	Var	=> non_terminal(Var, T0,T, S0,S, 0).
%	Var/Poss=> non_terminal(Var, T0,T, S0,S, Choices).
%   non_terminal/6 checks or ensures that the variable is suitably
%   bound, and then dispatches through non_terminal/5.  Note that
%   Var/Poss may backtrack through different possibilities.

non_terminal(Var, T0, T, S0, S, 0) :-
	var(Var), !,
	write('! variable non-terminal at run-time'), nl,
	fail.		%  we want a better error signal
non_terminal(Var, T0, T, S0, S, 0) :-
	non_terminal(Var, T0, T, S0, S).
non_terminal(Var, T0, T, S0, S, Choices) :-
	nonvar(Var), !,
	memberchk(Var, Choices),
	non_terminal(Var, T0, T, S0, S).
non_terminal(Var, T0, T, S0, S, Choices) :-
	member(Var, Choices),
	non_terminal(Var, T0, T, S0, S).



%   dcsg_phrase(Nonterminal, Sentence)
%   is the dcsg analogue of the standard predicate phrase/2,
%   can the Sentence be completely parsed as a Nonterminal
%   with nothing missing?

dcsg_phrase(NonTerm, Sentence) :-
	non_terminal(NonTerm, 0, 0, Sentence, []).


/*	For debugging DCSG.PL in bare Prolog:
append([],L,L).
append([H|T],L,[H|R]):-append(T,L,R).
member(X,[X|_]).		memberchk(X,[X|_]):-!.
member(X,[_|T]):-member(X,T).	memberchk(X,[_|T]):-memberchk(X,T).
/**/
/*	For debugging DCSG.PL in ToolKit:	*/
ppg :-
	non_dcsg(F,N),
	pp(F/N),
	fail
    ;	clause(non_terminal(_,_,_,_,_), Goal),
	functor(Goal, F, N),
	pp(F/N),
	fail
    ;   true.


EOF1DCSG.PL                00010025000100 85045 99364 000023Unix V7             HDR1DEC10.PL               00010026000100 85045 99364 000000Unix V7             %   File   : /usr/lib/prolog/dec10 (OLD VERSION -- RAOK)
%   Author : Paul F. Wilk
%   Purpose: Dec-10 compatibility file for C Prolog v1.4a

%   RAOK: not necessarily suitable for C Prolog v1.5a.

%   This file defines all the Dec-10 evaluable predicates not already
%   part of C Prolog.  It was written by P.F.Wilk.  R.A.O'Keefe
%   rewrote it so that predicates which cannot be emulated appear as
%   !  <goal> and debug or abort, and the rest appear as
%   ;  <goal> and either succeed or fail as appropriate.

ancestors(A) :-
	write('! '), write(ancestors(A)), nl,
	trace, break, abort.

compile(L) :-
	write('; '), write(compile(L)), nl,
	compile1(L).

compile1([]).
compile1([H|T]) :-
	reconsult(H),
	compile1(T).

depth(D) :-
	write('! '), write(depth(D)), nl,
	trace, break, abort.

gc :-
	write('; '), write(gc), nl.

gcguide(A,B,C) :-
	write('; '), write(gcguide(A,B,C)), nl.

incore(X) :-		% SHOULD NEVER APPEAR; USE call/1
	write('; '), write(incore(X)), nl,
	call(X).

log :-
	write('; '), write(log), nl.

maxdepth(D) :-
	write('; '), write(maxdepth(D)), nl.

nogc :-
	write('; '), write(nogc), nl.

nolog :-
	write('; '), write(nolog), nl.

plsys(S) :-
	write('! '), write(plsys(S)), nl,
	trace, break, abort.

reinitialise :-
	write('! '), write(reinitialise), nl,
	trace, break, abort.

restore(F) :-
	write('! '), write(restore(F)), nl,
	fail.

revive(A,B)  :-
	write('! '), write(revive(A,B)), nl,
	trace, break, abort.

statistics(A,B) :-
	write('! '), write(statistics(A,B)), nl,
	trace, break, abort.

subgoal_of(G)  :-
	write('! '), write(subgoal_of(G)), nl,
	trace, break, abort.

trimcore :-
	write('; '), write(trimcore), nl.

version :-
	write('; '), write(version), nl.

version(V) :-
	write('; '), write(version(V)), nl.

 EOF1DEC10.PL               00010026000100 85045 99364 000004Unix V7             HDR1DECONS.PL              00010027000100 85045 99364 000000Unix V7             %   File   : DECONS.PL
%   Author : R.A.O'Keefe
%   Updated: 23 July 1984
%   Purpose: Construct and take apart Prolog control structures.

:- public
	prolog_bounded_quantification/3,
	prolog_clause/3,
	prolog_conjunction/2,
	prolog_disjunction/2,
	prolog_if_branch/3,
	prolog_negation/2.

:- mode
	prolog_bounded_quantification(?, ?, ?),
	prolog_clause/3,
	prolog_conjunction(?, ?),
	prolog_disjunction(?, ?),
	prolog_if_branch(?, ?, ?),
	prolog_negation(?, ?),
	pl_explode(+, +, +, -),
	pl_explode(+, +, +, -, ?),
	pl_implode(+, +, +, -),
	pl_implode2(+, +, +, -).



%   prolog_bounded_quantification(Form, Generator, Test)
%   handles the syntax of forall(Gen, Test).

prolog_bounded_quantification(forall(G,T), G, T) :- !.
prolog_bounded_quantification(\+ (G, \+ T), G, T) :- !.
prolog_bounded_quantification((G, (T->fail;true) -> fail; true), G, T).



%   prolog_clause(Clause, Head, Body)
%   handles the syntax of clauses.  Note that it is not used to
%   recognise whether a term is a clause or not; almost any Prolog
%   term can serve as a (unit) clause.  If is for building a clause
%   given the head and body, or for taking something known to be a
%   clause apart.

prolog_clause(Head, Head, true) :-
	Head \= (_ :- _), !.
prolog_clause((Head:-Body), Head, Body).



%   prolog_if_branch(Branch, Hypothesis, Conclusion)
%   handles the syntax of individual arms of if-then-elses.

prolog_if_branch((H->C), H, C).



%   prolog_negation(NegatedForm, PositiveForm) recognises
%   and/or generates negations.

prolog_negation(\+X, X) :- !.
prolog_negation((X->fail;true), X).



%   prolog_conjunction(Conjunction, ListOfConjuncts)
%   handles the syntax of conjuncts.  This code wraps call(_) around
%   variables, flattens conjunctions to (A;(B;(C;(D;E)))) form, and
%   drops "true" conjuncts.

prolog_conjunction(Conjunction, ListOfConjuncts) :-
	nonvar(Conjunction),
	!,
	functor(Conjunction, ',', 2),
	pl_explode(Conjunction, ',', 'true', L, []),
	ListOfConjuncts = L.
prolog_conjunction(Conjunction, ListOfConjuncts) :-
	pl_explode(ListOfConjuncts, ',', 'true', L0),
	pl_implode(L0, ',', 'true', Conjunction).



%   prolog_disjunction(Disjunction, ListOfDisjuncts)
%   handles the syntax of disjuncts.  This code wraps call(_) around
%   variables, flattens disjunctions to (A,(B,(C,(D,E)))) form, and
%   drops "false" disjuncts.

prolog_disjunction(Disjunction, ListOfDisjuncts) :-
	nonvar(Disjunction),
	!,
	functor(Disjunction, ';', 2),
	pl_explode(Disjunction, ';', 'fail', L, []),
	ListOfDisjuncts = L.
prolog_disjunction(Disjunction, ListOfDisjuncts) :-
	pl_explode(ListOfDisjuncts, ';', 'fail', L0),
	pl_implode(L0, ';', 'fail', Disjunction).



%   pl_explode(Form, Op, Zero, L0, L)
%   flattens a binary tree built using Op into a list between L0 and L,
%   eliminating Zero nodes, and wrapping call(_) around variable nodes.

pl_explode(V, _, _, [call(V)|L], L) :-
	var(V),
	!.
pl_explode(Z, _, Z, L, L) :- !.
pl_explode(F, O, Z, L0, L) :-
	F =.. [O,A,B],
	!,
	pl_explode(A, O, Z, L0, L1),
	pl_explode(B, O, Z, L1, L).
pl_explode(F, _, _, [F|L], L).



%   pl_explode(List, Op, Zero, L)
%   flattens each of the elements of List using pl_explode/5
%   and forms the result into a big list L.

pl_explode([], _, _, []) :- !.
pl_explode([H|T], O, Z, L0) :-
	pl_explode(H, O, Z, L0, L),
	pl_explode(T, O, Z, L).



%   pl_implode(List, Op, Zero, Tree)
%   forms the list [F1,...,Fn] into the tree Op(F1,Op(...Op(_,Fn))).

pl_implode([], _, Z, Z).
pl_implode([H|T], O, Z, Form) :-
	pl_implode2(T, Z, H, Form).

pl_implode2([], _, F, F).
pl_implode2([H|T], O, X, F) :-
	F =.. [O,X,Y],
	pl_implode2(T, O, H, Y).


EOF1DECONS.PL              00010027000100 85045 99364 000008Unix V7             HDR1DEPTH.PL               00010028000100 85045 99364 000000Unix V7             %   File   : DEPTH.PL
%   Author : R.A.O'Keefe
%   Updated: 12 March 1984
%   Purpose: Find or check the depth of a term.

/*  Many resolution-based theorem provers impose a Depth Bound on the
    terms they create.  Not the least of the reasons for this is to
    stop infinite loops.  This module provides two entry points:

	depth_of_term(Term, Depth)
	depth_bound(Term, Bound)

    depth_of_term calculates the depth of the term, using the definition
	depth(Var) = 0
	dpeth(Const) = 0
	depth(F(T1,...,Tn)) = 1+max(depth(T1),...,depth(Tn))

    Mostly, we couldn't care less what the depth of a term is, provided
    it is below some fixed bound.  depth_bound checks that the depth of
    the given term is below the bound (which is assumed to be an integer
    >= 1), without ever finding out what the depth actually is.
*/

:- public
	depth_bound/2,
	depth_of_term/2.

:- mode
	depth_bound(+, +),
	    depth_bound(+, +, +),
	depth_of_term(+, -),
	    depth_of_term(+, +, +, -).


depth_bound(Compound, Bound) :-
	nonvar(Compound),
	functor(Compound, _, Arity),
	Arity > 0,
	!,
	Bound > 0,		% this is the test!
	Limit is Bound-1,
	depth_bound(Arity, Compound, Limit).
depth_bound(_, _).


depth_bound(0, _, _) :- !.
depth_bound(N, Compound, Limit) :-
	arg(N, Compound, Arg),
	depth_bound(Arg, Limit),
	M is N-1, !,
	depth_bound(M, Compound, Limit).



depth_of_term(Compound, Depth) :-
	nonvar(Compound),
	functor(Compound, _, Arity),
	Arity > 0,
	!,
	depth_of_term(Arity, Compound, 0, ArgDepth),
	Depth is ArgDepth+1.
depth_of_term(_, 0).

depth_of_term(0, _, Depth, Depth) :- !.
depth_of_term(N, Compound, SoFar, Depth) :-
	arg(N, Compound, Arg),
	depth_of_term(Arg, ArgDepth),
	ArgDepth > SoFar,
	M is N-1,
	!,
	depth_of_term(M, Compound, ArgDepth, Depth).
depth_of_term(N, Compound, SoFar, Depth) :-
	M is N-1,
	depth_of_term(M, Compound, SoFar, Depth).

 EOF1DEPTH.PL               00010028000100 85045 99364 000004Unix V7             HDR1DISTFI.EX              00010029000100 85045 99364 000000Unix V7             %   File   : DISTFI.EX
%   Author : R.A.O'Keefe
%   Updated: 10 May 1984
%   Purpose: Load Util:Distfix.Pl and define some examples.

:- compile(['util:rdtok.pl', 'util:distfi.pl']).

:- distfixop(850, fx, [append,A,to,Z,giving,L], append(A,Z,L)),
   distfixop(850, fx, [remove,A,from,L,giving,Z], append(A,Z,L)),
   distfixop(700, xfy, [S,is,the,set,of,X,such,that,P], setof(X,P,S)),
   distfixop(700, xfy, [B,is,the,bag,of,X,such,that,P], bagof(X,P,B)),
   distfixop(850, fx, [apply,P,to,Args], apply(P,Args)),
   distfixop(850, fx, [compare,X,with,Y,giving,R], compare(R,X,Y)),
   distfixop(850, fx, [the,principal,functor,of,T,is,F,with,arity,N],
				functor(T,F,N)),
   distfixop(850, fx, [number,the,variables,of,X,starting,from,S,
			up,to,N], numbervars(X,S,N)),
   distfixop(850, fx, [make,X,ground], numbervars(X,0,_)),
   distfixop(850, fx, [unify,X,with,Y], X = Y),
   distfixop(700, xfx, [X,unifies,with,Y], X = Y),
   distfixop(700, xfx, [X,does,not,unify,with,Y], \=(X,Y)),
   distfixop(850, fx, [select,Elem,from,List,leaving,Rest],
			select(Elem,List,Rest)),
   distfixop(999, fy, [if,Test,then,True,else,False], (Test->True;False)),
   distfixop(999, fy, [if,Test,then,True], (Test->True;true)),
   distfixop(850, yfx, [X,for,all,Y], forall(Y,X)).


dconsult(File) :-
	(File == user ; exists(File)),
	seeing(Old),
	see(File),
	repeat,
	    read(Foo, Vars),
	    expand_term(Foo, Baz),
	    dconsult(Baz, Vars),
	!,
	seen,
	see(Old).

dconsult(end_of_file, _) :- !.
dconsult(:-(Cmd), _) :-
	(call(Cmd) ; ttyput(63), ttynl),
	!, fail.
dconsult(?-(Ques), []) :-
	(call(Ques), display(yes) ; display(no)),
	!, ttynl, fail.
dconsult(?-(Ques), Vars) :-
	(call(Ques), dvars(Vars) ; display(no), ttynl),
	!, fail.
dconsult(:-(H,B), _) :-
	assertz(:-(H,B)),
	!, fail.
dconsult(Ques, Vars) :-
	seeing(user), !,
	dconsult(?-(Ques), Vars).
dconsult(H, _) :-
	assertz(H),
	!, fail.

dvars([V=T|Vars]) :-
	display(V), display(' = '), print(T), nl, !,
	dvars(Vars).
dvars([]) :-
	display('more (y/n)? '), ttyflush,
	get0(C), ttyskip(31),
	C\/32 =\= "y".



 EOF1DISTFI.EX              00010029000100 85045 99364 000005Unix V7             HDR1DISTFI.PL              00010030000100 85045 99364 000000Unix V7             %   File   : DISTFI.PL
%   Author : R.A.O'Keefe
%   Updated: 10 May 1984
%   Purpose: Read Prolog terms (with extended syntax).

/*  Modified by Alan Mycroft to regularise the functor modes.
    This is both easier to understand (there are no more '?'s),
    and also fixes bugs concerning the curious interaction of cut with
    the state of parameter instantiation.

    Since this file doesn't provide "metaread", it is considerably
    simplified.  The token list format has been changed somewhat, see
    the comments in the RDTOK file.

    I have added the rule X(...) -> apply(X,[...]) for Alan Mycroft.

    Distfix operators have finally been added.  They are declared by
	distfixop(Priority, Type, Pattern, Term)
    where Priority is as usual, Type is currently only fx or fy (if
    the Pattern doesn't specify a right argument one of the types must
    still be specified but it doesn't matter which), Term is what the
    reader is to return when it sees something matching the pattern,
    and the Pattern is a list of atoms and variables whose first
    elements is an atom, and in which no two variables appear side by
    side without an intervening atom.  To avoid ambiguities, the first
    atom following each variable should NOT be an infix or postfix
    operator, but the code below does not check for that, as you could
    declare such an operator after declaring the distfix form.
    Examples:
	distfixop(950, fy, [for,each,X,show,that,Y], forall(X,Y))
	distfixop(1105, fx, [try,Goal,reverting,to,Alternative,on,failure],
				(Goal;Alternative))
	distfixop(999, fy, [there,is,a,clause,with,head,H,and,body,B],
				clause(H,B))
	distfixop(999, fy, [there,is,a,clause,with,head,H], clause(H,_))
    Infix forms are also available.  These have the side effect of
    declaring the head keyword as an infix operator; anything that did
    not do this would be significantly harder to patch into the old parser.
    Examples:
	distfixop(700, xfy, [S,is,the,set,of,X,such,that,P], setof(X,P,S))
	distfixop(700, xfy, [B,is,the,bag,of,X,such,that,P], bagof(X,P,S)),
	distfixop(700, xfy, [X,is,to,Y,as,A,is,to,B], X*B =:= A*Y),
	distfixop(700, xfx, [X,had,N,variables], numbervars(X,0,N))
*/

:- public
	distfixop/4,
	read/2.

:- mode
	after_prefix_op(+, +, +, +, +, -, -),
	ambigop(+, -, -, -, -, -),
	cant_follow_expr(+, -),
	distfixop(?, ?, ?, ?),
	distfix_head(+, +, +, -, -),
	distfix_head(+, +, +, +, -, -),
	distfix_keys(+, ?),
	distfix_pass(+, +, -),
	distfix_pattern(+, +, -),
	distfix_read(+, +, -),
	expect(+, +, -),
	exprtl(+, +, +, +, -, -),
	exprtl0(+, +, +, -, -),
	infixop(+, -, -, -),
	postfixop(+, -, -),
	prefixop(+, -, -),
	prefix_is_atom(+, +),
	read(?, ?),
	read(+, +, -, -),
	read(+, +, +, -, -),
	read_args(+, -, -),
	read_list(+, -, -),
	syntax_error(+),
	syntax_error(+, +).


%   read(?Answer, ?Variables)
%   reads a term from the current input stream and unifies it with
%   Answer.  Variables is bound to a list of [Atom=Variable] pairs.

read(Answer, Variables) :-
	repeat,
	    read_tokens(Tokens, Variables),
	    (   read(Tokens, 1200, Term, Leftover), all_read(Leftover)
	    |   syntax_error(Tokens)
	    ),
	!,
	Answer = Term.


%   all_read(+Tokens)
%   checks that there are no unparsed tokens left over.

all_read([]) :- !.
all_read(S) :-
	syntax_error([operator,expected,after,expression], S).


%   expect(Token, TokensIn, TokensOut)
%   reads the next token, checking that it is the one expected, and
%   giving an error message if it is not.  It is used to look for
%   right brackets of various sorts, as they're all we can be sure of.

expect(Token, [Token|Rest], Rest) :- !.
expect(Token, S0, _) :-
	syntax_error([Token,or,operator,expected], S0).


%   I want to experiment with having the operator information held as
%   ordinary Prolog facts.  For the moment the following predicates
%   remain as interfaces to current_op.
%   prefixop(O -> Self, Rarg)
%   postfixop(O -> Larg, Self)
%   infixop(O -> Larg, Self, Rarg)


prefixop(Op, Prec, Prec) :-
	current_op(Prec, fy, Op), !.
prefixop(Op, Prec, Less) :-
	current_op(Prec, fx, Op), !,
	Less is Prec-1.


postfixop(Op, Prec, Prec) :-
	current_op(Prec, yf, Op), !.
postfixop(Op, Less, Prec) :-
	current_op(Prec, xf, Op), !, Less is Prec-1.


infixop(Op, Less, Prec, Less) :-
	current_op(Prec, xfx, Op), !, Less is Prec-1.
infixop(Op, Less, Prec, Prec) :-
	current_op(Prec, xfy, Op), !, Less is Prec-1.
infixop(Op, Prec, Prec, Less) :-
	current_op(Prec, yfx, Op), !, Less is Prec-1.


ambigop(F, L1, O1, R1, L2, O2) :-
	postfixop(F, L2, O2),
	infixop(F, L1, O1, R1), !.


%   read(+TokenList, +Precedence, -Term, -LeftOver)
%   parses a Token List in a context of given Precedence,
%   returning a Term and the unread Left Over tokens.

read([Token|RestTokens], Precedence, Term, LeftOver) :-
	read(Token, RestTokens, Precedence, Term, LeftOver).
read([], _, _, _) :-
	syntax_error([expression,expected], []).


%   read(+Token, +RestTokens, +Precedence, -Term, -LeftOver)

read(var(Variable,_), ['('|S1], Precedence, Answer, S) :- !,
	read(S1, 999, Arg1, S2),
	read_args(S2, RestArgs, S3), !,
	exprtl0(S3, apply(Variable,[Arg1|RestArgs]), Precedence, Answer, S).

read(var(Variable,_), S0, Precedence, Answer, S) :- !,
	exprtl0(S0, Variable, Precedence, Answer, S).

read(atom(-), [integer(Integer)|S1], Precedence, Answer, S) :-
	Negative is -Integer, !,
	exprtl0(S1, Negative, Precedence, Answer, S).

read(atom(Functor), ['('|S1], Precedence, Answer, S) :- !,
	read(S1, 999, Arg1, S2),
	read_args(S2, RestArgs, S3),
	Term =.. [Functor,Arg1|RestArgs], !,
	exprtl0(S3, Term, Precedence, Answer, S).

read(atom(Keyword), S0, Precedence, Answer, S) :-
	is_distprefix_op(Keyword, Prec, Keys, Pattern, Term),
	Precedence >= Prec,
	distfix_pass(Keys, S0, S1),
	distfix_read(Pattern, S1, S2),
	!,
	exprtl(S2, Prec, Term, Precedence, Answer, S).

read(atom(Functor), S0, Precedence, Answer, S) :-
	prefixop(Functor, Prec, Right), !,
	after_prefix_op(Functor, Prec, Right, S0, Precedence, Answer, S).

read(atom(Atom), S0, Precedence, Answer, S) :- !,
	exprtl0(S0, Atom, Precedence, Answer, S).

read(integer(Integer), S0, Precedence, Answer, S) :- !,
	exprtl0(S0, Integer, Precedence, Answer, S).

read('[', [']'|S1], Precedence, Answer, S) :- !,
	exprtl0(S1, [], Precedence, Answer, S).

read('[', S1, Precedence, Answer, S) :- !,
	read(S1, 999, Arg1, S2),
	read_list(S2, RestArgs, S3), !,
	exprtl0(S3, [Arg1|RestArgs], Precedence, Answer, S).

read('(', S1, Precedence, Answer, S) :- !,
	read(S1, 1200, Term, S2),
	expect(')', S2, S3), !,
	exprtl0(S3, Term, Precedence, Answer, S).

read(' (', S1, Precedence, Answer, S) :- !,
	read(S1, 1200, Term, S2),
	expect(')', S2, S3), !,
	exprtl0(S3, Term, Precedence, Answer, S).

read('{', ['}'|S1], Precedence, Answer, S) :- !,
	exprtl0(S1, '{}', Precedence, Answer, S).

read('{', S1, Precedence, Answer, S) :- !,
	read(S1, 1200, Term, S2),
	expect('}', S2, S3), !,
	exprtl0(S3, '{}'(Term), Precedence, Answer, S).

read(string(List), S0, Precedence, Answer, S) :- !,
	exprtl0(S0, List, Precedence, Answer, S).

read(Token, S0, _, _, _) :-
	syntax_error([Token,cannot,start,an,expression], S0).


%   read_args(+Tokens, -TermList, -LeftOver)
%   parses {',' expr(999)} ')' and returns a list of terms.

read_args([','|S1], [Term|Rest], S) :- !,
	read(S1, 999, Term, S2), !,
	read_args(S2, Rest, S).
read_args([')'|S], [], S) :- !.
read_args(S, _, _) :-
	syntax_error([', or )',expected,in,arguments], S).


%   read_list(+Tokens, -TermList, -LeftOver)
%   parses {',' expr(999)} ['|' expr(999)] ']' and returns a list of terms.

read_list([','|S1], [Term|Rest], S) :- !,
	read(S1, 999, Term, S2), !,
	read_list(S2, Rest, S).
read_list(['|'|S1], Rest, S) :- !,
	read(S1, 999, Rest, S2), !,
	expect(']', S2, S).
read_list([']'|S], [], S) :- !.
read_list(S, _, _) :-
	syntax_error([', | or ]',expected,in,list], S).


%   after_prefix_op(+Op, +Prec, +ArgPrec, +Rest, +Precedence, -Ans, -LeftOver)

after_prefix_op(Op, Oprec, _, S0, Precedence, _, _) :-
	Precedence < Oprec, !,
	syntax_error([prefix,operator,Op,in,context,
		with,precedence,Precedence], S0).

after_prefix_op(Op, Oprec, _, S0, Precedence, Answer, S) :-
	peepop(S0, S1),
	prefix_is_atom(S1, Oprec), % can't cut but would like to
	exprtl(S1, Oprec, Op, Precedence, Answer, S).

after_prefix_op(Op, Oprec, Aprec, S1, Precedence, Answer, S) :-
	read(S1, Aprec, Arg, S2),
	Term =.. [Op,Arg], !,
	exprtl(S2, Oprec, Term, Precedence, Answer, S).


%   The next clause fixes a bug concerning "mop dop(1,2)" where
%   mop is monadic and dop dyadic with higher Prolog priority.

peepop([atom(F),'('|S1], [atom(F),'('|S1]) :- !.
peepop([atom(F)|S1], [infixop(F,L,P,R)|S1]) :- infixop(F, L, P, R).
peepop([atom(F)|S1], [postfixop(F,L,P)|S1]) :- postfixop(F, L, P).
peepop(S0, S0).


%   prefix_is_atom(+TokenList, +Precedence)
%   is true when the right context TokenList of a prefix operator
%   of result precedence Precedence forces it to be treated as an
%   atom, e.g. (- = X), p(-), [+], and so on.

prefix_is_atom([Token|_], Precedence) :-
	prefix_is_atom(Token, Precedence).

prefix_is_atom(infixop(_,L,_,_), P) :- L >= P.
prefix_is_atom(postfixop(_,L,_), P) :- L >= P.
prefix_is_atom(')', _).
prefix_is_atom(']', _).
prefix_is_atom('}', _).
prefix_is_atom('|', P) :- 1100 >= P.
prefix_is_atom(',', P) :- 1000 >= P.
prefix_is_atom([],  _).


%   exprtl0(+Tokens, +Term, +Prec, -Answer, -LeftOver)
%   is called by read/4 after it has read a primary (the Term).
%   It checks for following postfix or infix operators.

exprtl0([atom(F)|S1], Term, Precedence, Answer, S) :-
	ambigop(F, L1, O1, R1, L2, O2), !,
	(   exprtl([infixop(F,L1,O1,R1)|S1], 0, Term, Precedence, Answer, S)
	|   exprtl([postfixop(F,L2,O2) |S1], 0, Term, Precedence, Answer, S)
	).
exprtl0([atom(F)|S1], Term, Precedence, Answer, S) :-
	infixop(F, L1, O1, R1), !,
	exprtl([infixop(F,L1,O1,R1)|S1], 0, Term, Precedence, Answer, S).
exprtl0([atom(F)|S1], Term, Precedence, Answer, S) :-
	postfixop(F, L2, O2), !,
	exprtl([postfixop(F,L2,O2) |S1], 0, Term, Precedence, Answer, S).

exprtl0([','|S1], Term, Precedence, Answer, S) :-
	Precedence >= 1000, !,
	read(S1, 1000, Next, S2), !,
	exprtl(S2, 1000, (Term,Next), Precedence, Answer, S).

exprtl0(['|'|S1], Term, Precedence, Answer, S) :-
	Precedence >= 1100, !,
	read(S1, 1100, Next, S2), !,
	exprtl(S2, 1100, (Term;Next), Precedence, Answer, S).

exprtl0([Thing|S1], _, _, _, _) :-
	cant_follow_expr(Thing, Culprit), !,
	syntax_error([Culprit,follows,expression], [Thing|S1]).

exprtl0(S, Term, _, Term, S).


cant_follow_expr(atom(_),	atom).
cant_follow_expr(var(_,_),	variable).
cant_follow_expr(integer(_),	integer).
cant_follow_expr(string(_),	string).
cant_follow_expr(' (',		bracket).
cant_follow_expr('(',		bracket).
cant_follow_expr('[',		bracket).
cant_follow_expr('{',		bracket).



exprtl([infixop(F,L,O,_)|S1], C, Term, Precedence, Answer, S) :-
	Precedence >= 0, C =< L,
	is_distinfix_op(F, Keys, Term, Pattern, Expr),
	distfix_pass(Keys, S1, S2),
	distfix_read(Pattern, S2, S3),
	!,	% do we want this?
	exprtl(S3, O, Expr, Precedence, Answer, S).

exprtl([infixop(F,L,O,R)|S1], C, Term, Precedence, Answer, S) :-
	Precedence >= O, C =< L, !,
	read(S1, R, Other, S2),
	Expr =.. [F,Term,Other], /*!,*/
	exprtl(S2, O, Expr, Precedence, Answer, S).

exprtl([postfixop(F,L,O)|S1], C, Term, Precedence, Answer, S) :-
	Precedence >= O, C =< L, !,
	Expr =.. [F,Term],
	peepop(S1, S2),
	exprtl(S2, O, Expr, Precedence, Answer, S).

exprtl([','|S1], C, Term, Precedence, Answer, S) :-
	Precedence >= 1000, C < 1000, !,
	read(S1, 1000, Next, S2), /*!,*/
	exprtl(S2, 1000, (Term,Next), Precedence, Answer, S).

exprtl(['|'|S1], C, Term, Precedence, Answer, S) :-
	Precedence >= 1100, C < 1100, !,
	read(S1, 1100, Next, S2), /*!,*/
	exprtl(S2, 1100, (Term;Next), Precedence, Answer, S).

exprtl(S, _, Term, _, Term, S).


%   This business of syntax errors is tricky.  When an error is detected,
%   we have to write out a message.  We also have to note how far it was
%   to the end of the input, and for this we are obliged to use the data-
%   base.  Then we fail all the way back to read(), and that prints the
%   input list with a marker where the error was noticed.  If subgoal_of
%   were available in compiled code we could use that to find the input
%   list without hacking the data base.  The really hairy thing is that
%   the original code noted a possible error and backtracked on, so that
%   what looked at first sight like an error sometimes turned out to be
%   a wrong decision by the parser.  This version of the parser makes
%   fewer wrong decisions, and my goal was to get it to do no backtracking
%   at all.  This goal has not yet been met, and it will still occasionally
%   report an error message and then decide that it is happy with the input
%   after all.  Sorry about that.


syntax_error(Message, List) :-
	ttynl, display('**'),
	display_list(Message),
	length(List, Length),
	recorda(syntax_error, length(Length), _), !,
	fail.

display_list([Head|Tail]) :-
	ttyput(32),
	display_token(Head), !,
	display_list(Tail).
display_list([]) :-
	ttynl.

syntax_error(List) :-
	recorded(syntax_error, length(AfterError), Ref),
	erase(Ref),
	length(List, Length),
	BeforeError is Length-AfterError,
	display_list(List, BeforeError), !,
	fail.

display_list(X, 0) :-
	display('<<here>> '), !,
	display_list(X, 99999).
display_list([Head|Tail], BeforeError) :-
	display_token(Head),
	ttyput(32),
	Left is BeforeError-1, !,
	display_list(Tail, Left).
display_list([], _) :-
	ttynl.

display_token(atom(X))	  :- !,	display(X).
display_token(var(_,X))	  :- !,	display(X).
display_token(integer(X)) :- !,	display(X).
display_token(string(X))  :- !,	display(X).
display_token(X)	  :-	display(X).

%  From here down is new stuff to handle distfix operators.

distfixop(Priority, Type, Pattern, Template) :-
	integer(Priority),
	Priority > 0,
	Priority =< 1200,
	atom(Type),
	(   (   Type = fx,	Right is Priority-1
	    ;   Type = fy,	Right = Priority
	    ),
	    distfix_keys(Pattern, [Atom|Keys], RestPattern),
	    distfix_pattern(RestPattern, Right, P_form),
	    !,
	    assert(is_distprefix_op(Atom, Priority, Keys, P_form, Template))
	;   (	Type = xfx,	Right is Priority-1
	    ;	Type = xfy,	Right = Priority
	    ;	Type = yfx,	Right is Priority-1
	    ),
	    distfix_pattern(Pattern, Right, P_form_0),
	    P_form_0 = p(Lhs,[Atom|Keys],P_form),
	    !,
	    op(Priority, Type, Atom),
	    assert(is_distinfix_op(Atom, Keys, Lhs, P_form, Template))
	).
distfixop(P, T, Pn, Te) :-
	telling(Old), tell(user),
	nl, write('! error: '),
	write(distfixop(P,T,Pn,Te)), nl,
	tell(Old),
	!, fail.

/*  A distfix pattern is one of
	p		  -- standing for the end of the pattern
	p(Var,Prio)	  -- standing for a right argument of that priority
	p(Var,Keys,Rest)  -- standing for Var Keyword... Restofpattern
    e.g. p(X,[by],p(Y,[giving,quotient],p(Q,[and,remainder],p(R,99)))).
    distfix_pattern(List, Prio, P_form) turns a human-readable list into
    this compact form.
*/

distfix_pattern([], _, p) :- !.
distfix_pattern([Var], Prio, p(Var,Prio)) :- !,
	var(Var).		%   
distfix_pattern([Var|List], Prio, p(Var,Keys,Rest)) :-
	var(Var),
	distfix_keys(List, Keys, RestList),
	Keys \== [], !,
	distfix_pattern(RestList, Prio, Rest).

/*  distfix_keys picks off all the atoms at the front of the list.
*/
distfix_keys([Key|List], [Key|Keys], RestList) :-
	atom(Key), !,
	distfix_keys(List, Keys, RestList).
distfix_keys(List, [], List).


distfix_read(p, S0, S) :-
	peepop(S0, S).
distfix_read(p(Variable,Priority), S0, S) :-
	read(S0, Priority, Variable, S).
distfix_read(p(Variable,Keywords,RestPattern), S0, S) :- 
	distfix_head(S0, [], Keywords, Tokens, S1),
	%  This may backtrack over ever longer Token lists
	read(Tokens, 1200, Variable, T),
	T = [],
	!,	%  not sure if I want this cut or not
	distfix_read(RestPattern, S1, S).


/*  Distfix_pass(Keys, S0, S)
    is basically append(Keys, S, S0), but Keys is a list of atoms,
    and the prefix of S0 must be atom(K1),...,atom(Kn)
*/
distfix_pass([], S, S) :- !.
distfix_pass([Key|Keys], [atom(Key)|S0], S) :-
	distfix_pass(Keys, S0, S).

/*  Distfix_head(S0, Stack, Keys, Tokens, S)
    matches S0 against Tokens & Keys & S, where Tokens is balanced
    with respect to () [] {}.  It uses the Stack to keep track of
    what brackets need balancing.
*/

distfix_head(S0, [], Keys, [], S) :-
	distfix_pass(Keys, S0, S).
distfix_head([Token|S0], Stack, Keys, [Token|Tokens], S) :-
	distfix_head(Token, Stack, S0, Keys, Tokens, S).

distfix_head('(', Stack, S0, Keys, Tokens, S) :- !,
	distfix_head(S0, [')'|Stack], Keys, Tokens, S).
distfix_head(' (',Stack, S0, Keys, Tokens, S) :- !,
	distfix_head(S0, [')'|Stack], Keys, Tokens, S).
distfix_head('[', Stack, S0, Keys, Tokens, S) :- !,
	distfix_head(S0, [']'|Stack], Keys, Tokens, S).
distfix_head('{', Stack, S0, Keys, Tokens, S) :- !,
	distfix_head(S0, ['}'|Stack], Keys, Tokens, S).
distfix_head(Token, [Token|Stack], S0, Keys, Tokens, S) :- !,
	distfix_head(S0, Stack, Keys, Tokens, S).
distfix_head(Token, _, _, _, _, _) :-
	atom(Token),
	(Token = ')' ; Token = ']' ; Token = '}'),
	!, fail.
distfix_head(_, Stack, S0, Keys, Tokens, S) :-
	distfix_head(S0, Stack, Keys, Tokens, S).

 EOF1DISTFI.PL              00010030000100 85045 99364 000034Unix V7             HDR1EDIT.PL                00010031000100 85045 99364 000000Unix V7             %   File   : EDIT.PL
%   Author : R.A.O'Keefe + Lawrence Byrd
%   Updated: 22 August 1984
%   Purpose: Get from Prolog to TOP and back again.
%   Needs  : append/3 from LISTUT.PL, and latest version of TOP.

%   This module can be compiled or interpreted, whichever you prefer.
%   Luis Jenkins and Bernard Silver added "top" and "top File".
%   At Luis Jenkins' suggestion, editor/1 is now user-defined.
%   Luis Jenkins changed the priority of ':' to 100.

%   Six predicates are defined:
%	edit File	call Top to edit File and return
%	edit		edit the last file mentioned in an edit or redo
%	redo File	call Top to edit File and reconsult the result
%	redo		redo the last file mentioned in an edit or redo
%	top  File	analogous to redo File. 
%	top		analogous to redo. 
%	
%   I have copied Luis Jenkins' idea of making : and . operators so that
%   you can specify a file name without the quotes.

:- public			% This must always be first in a file!
	(edit)/0,
	(edit)/1,
	(redo)/0,
	(redo)/1, 
	(top)/1, 
	(top)/0,
	file_term/2.

:- mode
	edit_file(+),
	file_term(+, -).


:- op(900, fx, [edit,redo,top]).
:- op(600, xfy, .).
:- op(100, xfy, :).



edit :-
	'last file'(File), !,
	edit_file(File).
edit :-
	display('! What file?'), ttynl.


edit(File) :-
	file_term(File, FileAtom),
	abolish('last file', 1),
	assert('last file'(FileAtom)),
	edit_file(FileAtom).



redo :-
	'last file'(File), !,
	edit_file(File),
	reconsult(File).
redo :-
	display('! What file?'), ttynl.


redo(File) :-
	file_term(File, FileAtom),
	abolish('last file', 1),
	assert('last file'(FileAtom)),
	edit_file(FileAtom),
	reconsult(FileAtom).


top File :-
	redo File. 


top :-
	redo. 


%   file_term takes a file name which may be specified using the : and .
%   operators, and returns an atom.  The lib(_) command should use it as
%   well.  file_term does not test that the result is a well-formed file
%   name, nor does it truncate components to 6 letters.  Maybe some code
%   from try_hard_to_see could be adapted.

file_term(Device:FileName.Extension, FileAtom) :- !,
	name(Extension, E),
	name(FileName,  F),
	name(Device,    D),
	append(F, [46|E], FE),
	append(D, [58|FE], DFE),
	name(FileAtom, DFE).
file_term(Device:FileName, FileAtom) :- !,
	name(FileName,  F),
	name(Device,    D),
	append(D, [58|F], DF),
	name(FileAtom, DF).
file_term(FileName.Extension, FileAtom) :- !,
	name(Extension, E),
	name(FileName,  F),
	append(F, [46|E], FE),
	name(FileAtom, FE).
file_term(FileName, FileName) :-
	atom(FileName), !.
file_term(Bogus, _) :-
	display('! Bad file name: '), display(Bogus), ttynl.


%   edit_file has to save and restore Prolog's state, and it has to
%   tell TOP what to do.  There used to be a special hack in TOP to
%   talk to Prolog, but now it uses a system-wide hack: TMP:EDT.
%   A TMP:EDT file contains the following:
%	S <file name> <fn delimiter> [ <program name> <pn delimiter> ]
%   where <fn delimiter> ::= <CR> | <ESC> | }
%   and   <pn delimiter> ::= !
%   What we write is therefore S<filename><ESC>MEC:PROLOG! .  The fact
%   that the editor involved is TOP is, thanks to our use of this Dec-
%   10 convention, immaterial.   To get another editor, say FINE, just
%   change the editor(_) fact.  The name PROLOG.BIN can *not*  be made
%   different, it is built into Prolog itself.

edit_file(File) :-
	(   save('PROLOG.BIN', 1)
	;   name(File, FileName),
	    append([83|FileName], [27|"MEC:PROLOG!"], Command),
	    plsys(tmpcor(tell,edt,Command)),
	    (   editor(Editor)
	    ;   Editor = 'MEC:TOP'	% This is the default
	    ),
    	    plsys(run(Editor, 1))
	), !,
	see('PROLOG.BIN'),
	rename('PROLOG.BIN', []).


EOF1EDIT.PL                00010031000100 85045 99364 000008Unix V7             HDR1EXPAND.PL              00010032000100 85045 99364 000000Unix V7             %   File   : /usr/lib/prolog/expand, UTIL:EXPAND.PL
%   Author : R.A.O'Keefe
%   Updated: Wednesday January 18th, 1984, 0:09:43 am
%   Purpose: Simple macro expansion for Prolog

:- public
	expand/2.

:- mode
	expand(+, +),
	    expand_macros(+),
		expand_macros(+, -),
		    get_rid_of_extra_true(+, +, -).

	
expand(OldFile, NewFile) :-
	file_exists(OldFile),
	seeing(OldInput),
	telling(OldOutput),
	see(OldFile),
	tell(NewFile),
	repeat,
		read(Term),
		expand_term(Term, Expanded),	%  Prolog built-in predicate
		expand_macros(Expanded),
	!,
	seen,
	told.


expand_macros(end_of_file).
expand_macros((Head :- OldBody)) :-
	expand_macros(OldBody, NewBody),
	writeq((Head :- NewBody)), put(46), nl,
	!, fail.		%  drive the repeat loop
expand_macros((:- OldBody)) :-
	expand_macros(OldBody, NewBody),
	writeq((:- NewBody)), put(46), nl,
	!, fail.		%  drive the repeat loop
expand_macros((?- OldBody)) :-
	expand_macros(OldBody, NewBody),
	writeq((?- NewBody)), put(46), nl,
	!, fail.		%  drive the repeat loop
expand_macros(Head) :-
	writeq(Head), put(46), nl,
	!, fail.


expand_macros(Var, call(Var)) :-
	var(Var), !.
expand_macros((OldA,OldB), Answer) :- !,
	expand_macros(OldA, NewA),
	expand_macros(OldB, NewB),
	get_rid_of_extra_true(NewA, NewB, Answer).
expand_macros((OldA;OldB), (NewA;NewB)) :- !,
	expand_macros(OldA, NewA),
	expand_macros(OldB, NewB).
expand_macros((OldA->OldB), (NewA->NewB)) :- !,
	expand_macros(OldA, NewA),
	expand_macros(OldB, NewB).
expand_macros(forall(OldA,OldB), forall(NewA,NewB)) :- !,
	expand_macros(OldA, NewA),
	expand_macros(OldB, NewB).
expand_macros(not(Old), \+(New)) :- !,
	expand_macros(Old, New).
expand_macros(\+(Old), \+(New)) :- !,
	expand_macros(Old, New).
expand_macros(Old, New) :-
	macro(Old, New),
	!.			% FORCE a unique expansion.
expand_macros(Old, Old).	% Not a macro.


%   Macros very often turn into 'true', and clauses that come out
%   looking like ... :- true,true,true,true,true. would be silly.
%   So if one member of a conjunction is 'true' we discard it.  A
%   final 'true' as in "p :- true" does no harm as Prolog will do
%   any removal necessary.  We **can't** remove 'true' disjuncts,
%   though we could in logic, as that would change the behaviour
%   of the program by backtracking a different number of times.

get_rid_of_extra_true(true, X, X) :- !.
get_rid_of_extra_true(X, true, X) :- !.


/* -------------------- EXAMPLE MACROS

macro(cons(H, T, [H|T]),	true).
macro(head([H|T], H),		true).
macro(tail([H|T], T),		true).
macro(empty([]),		true).
macro(positive(X),		X>0).

/* --------------------	EXAMPLE OF THEIR USE

append(Prefix, Suffix, Answer) :-
	head(Prefix, Head),
	tail(Prefix, Tail),
	cons(Head, Rest, Answer),
	append(Tail, Suffix, Rest).
append(Prefix, Answer, Answer) :-
	empty(Prefix).


member(Element, List) :-
	head(List, Element).
member(Element, List) :-
	tail(List, Rest),
	member(Element, Rest).

bug(Var) :-			% bug([2|_]) is meant to succeed
	nonvar(Var), !,		% bug(X) is meant to bind X to 'var'
	head(Var, 2).		% bug(any other non-variable) is
bug(var).			% meant to fail


greater(X, Y) :-
	Z is Y-Z,
	positive(Z).

/* -------------------- END EXAMPLES */
 EOF1EXPAND.PL              00010032000100 85045 99364 000007Unix V7             HDR1FEACH.PL               00010033000100 85045 99364 000000Unix V7             %   File   : FEACH.PL
%   Author : R.A.O'Keefe
%   Updated: 13 June 1984
%   Purpose: Redefine the foreach/5 predicate I deleted.

/*  foreach(Generator, Extractor, Combiner, Argument, Total)
    is a variation on findall, a sort of generalised summation.
    Generator enumerates interesting situations (by backtracking).
    For each situation found by the Generator, the Extractor is
    called once.  If the Extractor fails for any situation, the
    entire call to foreach fails, and any junk that may have been
    put into the data base is removed.  The Extractor is expected
    to bind the Argument, which plays the role of the Template in
    findall.  The code doesn't check that the Argument is bound
    to a non-variable, perhaps it should.  When the Generator runs
    out of situations to enumerate, the Combiner is used to form a
    tree of the arguments.  The Combiner may be a single atom, in
    which case there had better be at least one situtation, or it
    may be an [Op,Default] pair, in which case the default is used
    if there are no situations.  If the arguments found are A1,...,An
    the tree which is returned as the Total is op(A1,op(A2,...,op(_,An)))
    e.g. a+(b+(c+(d+e))).

    This predicate is OBSOLETE.  It is ONLY for an obscure part of MECHO.
    It cannot be assigned a type, as the type of the result depends on 
    the value of the 3rd argument in a strange way.  It would in general
    be much cleaner to use findall and then map down the list.  DANGER!
    BEWARE!  UNCLEAN! UNCLEAN!  Reading this may damage your mental health!
*/

:- public
	foreach/5.

:- mode
	foreach(+, +, +, -, ?),
	    foreach(+, -),
	    foreach(+, +, ?).


foreach(Generator, Extractor, Com, Argument, Tot) :-
	recorda(., -, _),	
	call(Generator),
	foreach(Extractor, Argument),
	write('! extractor failed in '),
	print(foreach(Generator,Extractor,Com,Argument,Tot)),
	!, fail.
foreach(_, _, [Op,Default], _, Total) :-
	atom(Op), nonvar(Default),
	recorded(., Term, Ref), erase(Ref),
	!,
	(   Term = -, Total = Default
	;   Term = -X, foreach(X, Op, Total)
	).
foreach(_, _, Op, _, Total) :-
	atom(Op),
	recorded(., Term, Ref), erase(Ref),
	!,
	Term = -X,
	foreach(X, Op, Total).
foreach(Gen, Ext, Combiner, Arg, Tot) :-
	write('! bad combiner in '),
	print(foreach(Gen,Ext,Combiner,Arg,Tot)), nl,
	recorded(., Term, Ref), erase(Ref),
	Term = -,
	!, fail.



%   foreach(Accumulator, Operator, Total)
%   picks up further situation arguments out of the data base and
%   sticks them onto the right of the accumulator.

foreach(SoFar, Op, Total) :-
	recorded(., Term, Ref), erase(Ref),
	!,
	(   Term = -, Total = SoFar
	;   Term = -X, Next =.. [Op,X,SoFar], foreach(Next, Op, Total)
	).



%   foreach(Extractor, Argument)
%   calls the Extractor and records the first solution Argument.
%   Note the cut: we'd really like to make it an error for there
%   to be more than one extraction for a given situation, but all
%   we can do is force there to be at most one.  If there aren't
%   any, we clean out the stack and *succeed*, this will cause the
%   first clause of foreach/5 to be resumed which will print an
%   error message and fail.

foreach(Extractor, Argument) :-
	call(Extractor),
	!,
	recorda(., -Argument, _),
	fail.
foreach(_, _) :-
	recorded(., Term, Ref), erase(Ref),
	Term = - .

EOF1FEACH.PL               00010033000100 85045 99364 000007Unix V7             HDR1FILE-LIST              00010034000100 85045 99364 000000Unix V7             total 845
-rw-rw-r--  1 root         4129 Feb 15 11:01 ADVICE.HLP
-rw-rw-r--  1 root         7827 Feb 15 11:01 ADVICE.PL
-rw-rw-r--  1 root        19087 Feb 15 11:01 ANDOR.PL
-rw-rw-r--  1 root         1678 Feb 15 11:03 APPLIC.HLP
-rw-rw-r--  1 root         5932 Feb 15 10:58 APPLIC.PL
-rw-rw-r--  1 root         8090 Feb 15 10:58 ARC3.PL
-rw-rw-r--  1 root          251 Feb 15 10:58 ARITH.OPS
-rw-rw-r--  1 root         8900 Feb 15 10:58 ARITH.PL
-rw-rw-r--  1 root         2058 Feb 15 10:58 ARRAYS.PL
-rw-rw-r--  1 root         5266 Feb 15 10:58 ASK.PL
-rw-rw-r--  1 root         2081 Feb 15 10:58 ASSOC.PL
-rw-rw-r--  1 root         2160 Feb 15 10:58 BACKUP.PL
-rw-rw-r--  1 root         8443 Feb 15 10:58 BAGUTL.HLP
-rw-rw-r--  1 root         7361 Feb 15 10:58 BAGUTL.PL
-rw-rw-r--  1 root         1598 Feb 15 11:02 BETWEE.PL
-rw-rw-r--  1 root         8065 Feb 15 11:02 BUNDLE.PL
-rw-rw-r--  1 root         2954 Feb 15 10:58 BYRD.HLP
-rw-rw-r--  1 root         1388 Feb 15 11:01 CC.PL
-rw-rw-r--  1 root        16857 Feb 15 10:58 CLAUSE.PL
-rw-rw-r--  1 root         1858 Feb 15 10:58 COUNT.HLP
-rw-rw-r--  1 root         3220 Feb 15 11:02 COUNT.PL
-rw-rw-r--  1 root         2582 Feb 15 11:02 CTYPES.PL
-rw-rw-r--  1 root         3609 Feb 15 10:58 DCSG.EX
-rw-rw-r--  1 root          295 Feb 15 10:58 DCSG.HLP
-rw-rw-r--  1 root        11688 Feb 15 10:59 DCSG.PL
-rw-rw-r--  1 root         1753 Feb 15 11:02 DEC10.PL
-rw-rw-r--  1 root         3640 Feb 15 10:59 DECONS.PL
-rw-rw-r--  1 root         1863 Feb 15 10:59 DEPTH.PL
-rw-rw-r--  1 root         2061 Feb 15 11:01 DISTFI.EX
-rw-rw-r--  1 root        17257 Feb 15 11:01 DISTFI.PL
-rw-rw-r--  1 root         3642 Feb 15 11:01 EDIT.PL
-rw-rw-r--  1 root         3169 Feb 15 10:59 EXPAND.PL
-rw-rw-r--  1 root         3330 Feb 15 10:59 FEACH.PL
-rw-rw-r--  1 root            0 Feb 15 11:04 FILE-LIST
-rw-rw-r--  1 root          718 Feb 15 11:02 FILES.
-rw-rw-r--  1 root         5139 Feb 15 11:02 FILES.PL
-rw-rw-r--  1 root         1522 Feb 15 10:59 FINE.PL
-rw-rw-r--  1 root         1760 Feb 15 10:59 FLAGRO.PL
-rw-rw-r--  1 root          244 Feb 15 10:59 FLALL.
-rw-rw-r--  1 root         5209 Feb 15 10:59 FLAT.PL
-rw-rw-r--  1 root          216 Feb 15 10:59 FLS.
-rw-rw-r--  1 root         2057 Feb 15 11:01 GELRAN.PL
-rw-rw-r--  1 root         1856 Feb 15 11:03 GENSYM.PL
-rw-rw-r--  1 root         1337 Feb 15 10:59 GETFIL.PL
-rw-rw-r--  1 root         9992 Feb 15 10:59 GRAPHS.PL
-rw-rw-r--  1 root         8090 Feb 15 10:59 HEAPS.PL
-rw-rw-r--  1 root         5491 Feb 15 10:59 HELP.PL
-rw-rw-r--  1 root         5496 Feb 15 10:59 HELP2.PL
-rw-rw-r--  1 root         5328 Feb 15 10:59 HELPER.HLP
-rw-rw-r--  1 root         3302 Feb 15 10:59 HELPER.PL
-rw-rw-r--  1 root         1386 Feb 15 11:02 IDBACK.DEF
-rw-rw-r--  1 root         7071 Feb 15 11:02 IDBACK.PL
-rw-rw-r--  1 root         1069 Feb 15 10:59 IMISCE.PL
-rw-rw-r--  1 root         1765 Feb 15 10:59 INVOCA.PL
-rw-rw-r--  1 root         9732 Feb 15 11:01 IXREF.DEF
-rw-rw-r--  1 root         1848 Feb 15 10:59 IXREF.HLP
-rw-rw-r--  1 root        11473 Feb 15 11:03 IXREF.PL
-rw-rw-r--  1 root         1766 Feb 15 11:01 LAZY.PL
-rw-rw-r--  1 root          503 Feb 15 11:01 LIB.PL
-rw-rw-r--  1 root         5219 Feb 15 10:59 LIB2.PL
-rw-rw-r--  1 root        10833 Feb 15 11:02 LISTUT.BAK
-rw-rw-r--  1 root         2409 Feb 15 11:03 LISTUT.HLP
-rw-rw-r--  1 root        11209 Feb 15 11:03 LISTUT.PL
-rw-rw-r--  1 root         5191 Feb 15 11:02 LOGARR.PL
-rw-rw-r--  1 root        35017 Feb 15 10:59 LONG.PL
-rw-rw-r--  1 root        14120 Feb 15 10:59 MAP.PL
-rw-rw-r--  1 root         8843 Feb 15 10:59 MEDIC.PL
-rw-rw-r--  1 root         4074 Feb 15 11:02 METUTL.PL
-rw-rw-r--  1 root          184 Feb 15 11:02 MODULE.MIC
-rw-rw-r--  1 root        16176 Feb 15 11:02 MODULE.PL
-rw-rw-r--  1 root         3937 Feb 15 10:59 MULTIL.PL
-rw-rw-r--  1 root         1360 Feb 15 11:00 MUTIL.
-rw-rw-r--  1 root          785 Feb 15 11:00 MUTIL.MIC
-rw-rw-r--  1 root         1158 Feb 15 11:00 NOT.HLP
-rw-rw-r--  1 root         3981 Feb 15 11:00 NOT.PL
-rw-rw-r--  1 root         4183 Feb 15 11:00 NUTIL.HLP
-rw-rw-r--  1 root        10678 Feb 15 11:00 NUTIL2.HLP
-rw-rw-r--  1 root         3986 Feb 15 11:00 OCCUR.PL
-rw-rw-r--  1 root         3063 Feb 15 11:00 OKHELP.PL
-rw-rw-r--  1 root         3108 Feb 15 11:00 ORDER.PL
-rw-rw-r--  1 root         7657 Feb 15 11:00 ORDSET.PL
-rw-rw-r--  1 root         5523 Feb 15 11:03 OXREF.EXE
-rw-rw-r--  1 root         1151 Feb 15 11:01 PORSTR.PL
-rw-rw-r--  1 root         6891 Feb 15 11:01 PP.HLP
-rw-rw-r--  1 root        13614 Feb 15 11:00 PP.PL
-rw-rw-r--  1 root        31448 Feb 15 11:01 PREDS.HLP
-rw-rw-r--  1 root         2621 Feb 15 11:00 PROJEC.PL
-rw-rw-r--  1 root         5982 Feb 15 11:00 PROLOG.TYP
-rw-rw-r--  1 root         1317 Feb 15 11:00 PUTIL.MIC
-rw-rw-r--  1 root          856 Feb 15 11:00 PUTSTR.PL
-rw-rw-r--  1 root         5589 Feb 15 11:00 QUEUES.PL
-rw-rw-r--  1 root         1362 Feb 15 11:02 RANDOM.PL
-rw-rw-r--  1 root         8466 Feb 15 11:00 RDSENT.PL
-rw-rw-r--  1 root        12850 Feb 15 11:00 RDTOK.GEN
-rw-rw-r--  1 root         3188 Feb 15 11:00 RDTOK.PL
-rw-rw-r--  1 root        11480 Feb 15 11:00 READ.PL
-rw-rw-r--  1 root         2357 Feb 15 11:00 READIN.PL
-rw-rw-r--  1 root         1281 Feb 15 11:02 RECON.PL
-rw-rw-r--  1 root          970 Feb 15 11:00 SAMSOR.PL
-rw-rw-r--  1 root         8590 Feb 15 11:00 SETOF.PL
-rw-rw-r--  1 root         8775 Feb 15 11:00 SETUTL.PL
-rw-rw-r--  1 root         2163 Feb 15 11:00 SORTS.PL
-rw-rw-r--  1 root         2382 Feb 15 11:02 STRIO.PL
-rw-rw-r--  1 root         7762 Feb 15 11:00 STRUCT.OLD
-rw-rw-r--  1 root         7038 Feb 15 11:02 STRUCT.PL
-rw-rw-r--  1 root         4180 Feb 15 11:00 SYSTEM.PL
-rw-rw-r--  1 root         3148 Feb 15 11:02 TERMIN.PL
-rw-rw-r--  1 root         1487 Feb 15 11:00 TEST.PL
-rw-rw-r--  1 root        12167 Feb 15 11:00 TIDY.OLD
-rw-rw-r--  1 root        12991 Feb 15 11:01 TIDY.PL
-rw-rw-r--  1 root          858 Feb 15 11:00 TIMING.PL
-rw-rw-r--  1 root         2433 Feb 15 11:01 TIMING.POP
-rw-rw-r--  1 root          558 Feb 15 11:00 TOOLKI.HLP
-rw-rw-r--  1 root         1397 Feb 15 11:00 TOOLKI.MIC
-rw-rw-r--  1 root        12536 Feb 15 11:02 TOPLEV.PL
-rw-rw-r--  1 root         8024 Feb 15 11:00 TOPLEV.STD
-rw-rw-r--  1 root         2109 Feb 15 11:01 TRACE.PL
-rw-rw-r--  1 root         4743 Feb 15 11:01 TREES.PL
-rw-rw-r--  1 root         3988 Feb 15 11:01 TRYSEE.PL
-rw-rw-r--  1 root         1009 Feb 15 11:01 TYPE.PL
-rw-rw-r--  1 root        13287 Feb 15 11:02 TYPECH.PL
-rw-rw-r--  1 root         7122 Feb 15 11:01 UNFOLD.PL
-rw-rw-r--  1 root         3115 Feb 15 11:01 UPDATE.PL
-rw-rw-r--  1 root         1720 Feb 15 11:01 UTIL.
-rw-rw-r--  1 root        11653 Feb 15 11:03 UTIL.CNG
-rw-rw-r--  1 root         7263 Feb 15 11:01 UTIL.DEF
-rw-rw-r--  1 root        12632 Feb 15 11:01 UTIL.HLP
-rw-rw-r--  1 root          762 Feb 15 11:01 UTIL.MIC
-rw-rw-r--  1 root          650 Feb 15 11:01 UTIL.OPS
-rw-rw-r--  1 root         5391 Feb 15 11:01 UTIL.TXT
-rw-rw-r--  1 root         6008 Feb 15 11:03 UTIL2.HLP
-rw-rw-r--  1 root         3740 Feb 15 11:01 UTIL2.OLD
-rw-rw-r--  1 root        11463 Feb 15 11:03 UTIL3.HLP
-rw-rw-r--  1 root          806 Feb 15 11:01 VCHECK.HLP
-rw-rw-r--  1 root         2181 Feb 15 11:01 VCHECK.PL
-rw-rw-r--  1 root        12968 Feb 15 11:02 WRITE.PL
-rw-rw-r--  1 root         5023 Feb 15 11:02 WRITEF.HLP
-rw-rw-r--  1 root        10898 Feb 15 11:02 WRITEF.PL
-rw-rw-r--  1 root         4364 Feb 15 11:02 XGPROC.PL
-rw-rw-r--  1 root           93 Feb 15 11:03 XREF.
-rw-rw-r--  1 root         2267 Feb 15 11:03 XREF.BUG
-rw-rw-r--  1 root           87 Feb 15 11:03 XREF.CCL
-rw-rw-r--  1 root         2878 Feb 15 11:03 XREF.DEF
-rw-rw-r--  1 root         5770 Feb 15 11:03 XREF.EXE
-rw-rw-r--  1 root        10090 Feb 15 11:03 XREF.HLP
-rw-rw-r--  1 root          951 Feb 15 11:03 XREF.MIC
-rw-rw-r--  1 root         6057 Feb 15 11:03 XRF.PL
-rw-rw-r--  1 root         3579 Feb 15 11:03 XRFCOL.PL
-rw-rw-r--  1 root         4673 Feb 15 11:03 XRFDEF.PL
-rw-rw-r--  1 root         5783 Feb 15 11:03 XRFMOD.PL
-rw-rw-r--  1 root         6384 Feb 15 11:03 XRFOUT.PL
-rw-rw-r--  1 root           47 Feb 15 11:03 XRFTST.BAR
-rw-rw-r--  1 root          135 Feb 15 11:03 XRFTST.FOO
-rw-rw-r--  1 root         4071 Feb 15 11:03 XRFTTY.PL
EOF1FILE-LIST              00010034000100 85045 99364 000017Unix V7             HDR1FILES.                 00010035000100 85045 99364 000000Unix V7             Advice.Pl
Andor.Pl
Applic.Pl
Arc3.Pl
Arith.Pl
Arrays.Pl
Ask.Pl
Assoc.Pl
Backup.Pl
BagUtl.Pl
Betwee.Pl
Bundle.Pl
Clause.Pl
Count.Pl
Dcsg.Pl
Decons.Pl
Depth.Pl
Edit.Pl
Expand.Pl
Feach.Pl
Files.Pl
Fine.Pl
Flagro.Pl
Flat.Pl
Gensym.Pl
GetFil.Pl
Graphs.Pl
Heaps.Pl
Help.Pl
Help2.Pl
Helper.Pl
Imisce.Pl
Invoca.Pl
Ixref.Pl
Lib.Pl
Lib2.Pl
ListUt.Pl
LogArr.Pl
Long.Pl
Map.Pl
Medic.Pl
MetUtl.Pl
Multil.Pl
Not.Pl
Occur.Pl
OkHelp.Pl
Order.Pl
OrdSet.Pl
PorStr.Pl
Pp.Pl
Projec.Pl
PutStr.Pl
Queues.Pl
RdSent.Pl
RdTok.Pl
Read.Pl
Readin.Pl
Recon.Pl
Samsor.Pl
Setof.Pl
SetUtl.Pl
Sorts.Pl
Struct.Pl
System.Pl
Test.Pl
Tidy.Pl
Timing.Pl
Trace.Pl
Trees.Pl
Trysee.Pl
Type.Pl
Typech.Pl
Update.Pl
Util.Pl
Vcheck.Pl
Write.Pl
Writef.Pl
XGProc.Pl
EOF1FILES.                 00010035000100 85045 99364 000002Unix V7             HDR1FILES.PL               00010036000100 85045 99364 000000Unix V7             %   File   : FILES.PL
%   Author : Lawrence Byrd + Richard A. O'Keefe
%   Updated: 21 August 1984
%   Purpose: Routines for playing with files.

:- public
	append/1,
	check_exists/1,
	file_exists/1,
	open/1,
	open/2,
	open_file/1,
	open_file/2,
	open_file/3,
	close/2,
	delete/1.

:- mode
	append(+),
	check_exists(+),
	file_exists(+),
	open(+),
	open(?, +),
	'open file'(+, +),
	close(+, +),
	delete(+).




			% Check to see if a file exists and produce
			%  an error message if it doesn't.

check_exists(File) :-
	file_exists(File),
	!.
check_exists(File) :-
	telling(OldTell), tell(user),
	nl, write('! File: '), write(File),
	write(' cannot be opened for input.'), nl,
	tell(OldTell),
	!, fail.



			% Succeed if a file exists, otherwise fail

file_exists(File) :-
	atom(File),
	seeing(OldSee),
	(   nofileerrors, see(File), !, fileerrors, seen, see(OldSee)
	;   fileerrors, fail
	).



			% Open a file, checking that it exists

open(File) :-
	check_exists(File),
	see(File).



			% Open a file and return current file
			% This is seeing/2 in C Prolog.

open(Old, File) :-
	seeing(Old),
	open(File).



			% Close file and see old file again

close(File, Old) :-
	close(File),
	see(Old).



			% Delete a file (note that rename requires that
			%  the file be open, in Dec-10 Prolog)

delete(File) :-
	open(Old, File),
	rename(File, []),
	see(Old).



/*  append(File)
    is supposed to open the file in "append" mode, that is for output,
    with the new output going at the end of the old contents instead of
    replacing them entirely as tell/1 would.  However, Bottoms-10 does
    not provide this facility the way UNIX does, so the simplest way of
    implementing the operation is to rename the old file File.EXT to
    File.BAK, to copy the contents of File.BAK into a new version of
    File.EXT, and to leave this new file open.

    As far as Prolog is concerned, you can use this predicate exactly
    as you use append/1 in C Prolog, that is, you can use it to open
    the file instead of tell and thereafter use tell to switch to it.
    (The other pattern which C Prolog permits, which is using append/1
    all the time instead of tell/1, will NOT work.)  However, as far
    as the operating system is concernd they are not equivalent, as
    the File.BAK will be left lying around which we don't really want,
    and in some cases involving path names Bottoms-10 won't get the
    rename right.  Also, any existing File.BAK will be deleted.
*/
append(File) :-
	seeing(Old),
	see(File),
	name(File, Chars),
	(   append(Prefix, [0'.|Suffix], Chars)
	;   Prefix = Chars
	),  !,
	append(Prefix, ".BAK", BackupChars),
	name(Backup, BackupChars),
	nofileerrors,
	(   see(Backup), rename(Backup, [])
	;   true		%  Delete the backup file
	),  !,			%  if it already exists.
	fileerrors,
	see(File),
	rename(File, Backup),
	see(Backup),
	tell(File),
	repeat,
	    get0(Ch),
	    ( Ch = 26 ; put(Ch), fail ),
	!,
	seen,
	see(Old).	


%   File   : /usr/lib/prolog/open
%   Author : R. A. O'Keefe
%   Updated: 17 October 1984
%   Purpose: open files with error messages and failure.
%   Needs  : lib(writef), lib(sets).

/*  The problem with nofileerrors mode is that when a see or tell or
    whatever fails, it does so quietly, without an intimation that
    this has happened.  The trouble with fileerrors mode is that if
    see or tell fails, it aborts, and the program cannot recover.

    Dec-10 Prolog I/O, which C Prolog copies with only minor changes,
    is generally admitted to be an unsatisfactory makeshift.  It has
    not been improved because the feeling is that it needs to be
    replaced (revolution not evolution) and in the mean-time one can
    actually program around most of its problems.  You have to be
    subtle, though.

    This file defines three commands:

	open_file(File, Mode, Action)

		tries to open File in (read, write, append) Mode.
		If it can't, it will print an error message, and
		call Action.  The error message will always go to
		the terminal.

	open_file(File, Mode)

		is shorthand for open(File, Mode, fail)

	open_file(File)

		is shorthand for open(File, read, fail)

    For printing error messages, it uses the library predicate
    fwritef (see lib(writef), there is also a documentation file
    for it).  C Prolog doesn't currently understand \ escape codes
    in strings, this is actually hacked by fwritef.
*/

open_file(File) :-
	open_file(File, read, fail).


open_file(File, Mode) :-
	open_file(File, Mode, fail).


open_file(File, Mode, Action) :-
	\+ atom(File),
	!,
	fwritef(user, '! Bad file name %t in call to open_file.\n', [File]),
	call(Action).
open_file(File, Mode, Action) :-
	\+ (atom(Mode), memberchk(Mode, [read,write,append])),
	!,
	fwritef(user, '! Bad mode %t in call to open_file.\n', [Mode]),
	call(Action).
open_file(File, Mode, Action) :-
	nofileerrors,
	(   'open file'(Mode, File), !, fileerrors
	;   fileerrors,
	    fwritef(user, '! Can''t open file %t in %t mode.\n', [File,Mode]),
	    call(Action)
	).

'open file'(read, File) :-
	see(File).
'open file'(write, File) :-
	tell(File).
'open file'(append, File) :-
	append(File).

 EOF1FILES.PL               00010036000100 85045 99364 000011Unix V7             HDR1FINE.PL                00010037000100 85045 99364 000000Unix V7             /* EDIT.PL : Get to an editor and back again

						UTILITY
						Lawrence
						Updated: 18 February 82
*/

	%%% Run this module interpreted
	%%% EDIT requires no other modules


 % 18 February 82
 % 
 % 	Changed for v 3.43 ie run, tmpcor bottled into plsys calls
 % 
 % (Earlier)
 %
 %	 This relies on the new evaluable predicates which arrived in
 % 	recent Prolog versions. It has been updated to the latest
 % 	(3.31 onwards) versions using save/2, tmpcor/3, run/2.
 %
 % 	edit currently runs the FINE editor and returns by running the
 % 	version of Prolog in mec:
 %


			% Redo a file by editing it and reconsulting it

redo(File)
     :-	edit(File),
	reconsult(File),
	!.



			% Edit a file
			%  Build the FINE CCL string
			%  Save state into prolog.bin
			%  Run FINE
			%  On return - delete prolog.bin
			% Note that the command string to Fine is very
			%  delicate! All the newlines, "!"'s etc are
			%  important.

edit(File)
     :-	name(File,Chars),
	edit_chars(Chars,0,Command,"
mec:prolog!"),
	( save('prolog.bin',1)
		;  plsys( tmpcor(tell,edt,[32|Command]) ),
		   plsys( run('sys:fine',1) )
	),
	!,
	see('prolog.bin'),
	rename('prolog.bin',[]).

		

			% Add a dot to the filename if not already there
			%  and append on the rest of the CCL string

edit_chars([C|Cs],K0,[C|R],T)
     :-	edit_dot(K0,C,K),
	edit_chars(Cs,K,R,T).

edit_chars([],0,[46|T],T) :- !.

edit_chars([],1,T,T).


edit_dot(1,_,1).		% Already found

edit_dot(0,46,1) :- !.		% Found the dot

edit_dot(0,_,0).		% Not found yet
EOF1FINE.PL                00010037000100 85045 99364 000003Unix V7             HDR1FLAGRO.PL              00010038000100 85045 99364 000000Unix V7             %   File   : FLAGRO.PL
%   Author : Lawrence Byrd + R.A.O'Keefe.
%   Updated: 31 October 1983
%   Purpose: Flag (global variable) handling.
%   Needs  : no other files.

:- public
	flag/2,			%  initialise a flag.
	flag/3.			%  change a flag.

:- mode
	check_valid_flag_name(+),
	flag(+, +),
	flag(+, ?, ?).

/*  Flags are stored in the data base keyed under the Flag itself with
    the information packaged into a compound term as follows:

	Flag -->		'$flag'(Flag, CurrentValue)

    If you only access flags through these routines there will be at
    most one such record per flag.  The flag/2 predicate will clear
    out any records it may find.  The flag/3 predicate maintains the
    flags returning the previous value as Old and updating the flag
    to New.  The code actually checks to see if this updating really
    has to change the data base.  For compatibility with old code, if
    you call flag/3 on a flag which has no record, an old value of 0
    is assumed.  For compatibility with C-Prolog, flags may not be
    integers, but only atoms or compound terms.
*/

check_valid_flag_name(Flag) :-
	nonvar(Flag),
	functor(Flag, Atom, _),
	atom(Atom).
%   There should be a clause to print an error message here.


flag(Flag, InitialValue) :-
	check_valid_flag_name(Flag),
	( recorded(Flag, '$flag'(Flag,_), Ref), erase(Ref), fail ; true ),
	recorda(Flag, '$flag'(Flag,InitialValue), _).


flag(Flag, OldValue, NewValue) :-
	check_valid_flag_name(Flag),
	(   recorded(Flag, '$flag'(Flag, Old), Ref)  ;  Old = 0   ),
	!,				%   there should be only one record
	OldValue = Old,			%   pattern match, may fail
	(   OldValue == NewValue	%   no change needed
	;   (   var(Ref)  ;  erase(Ref)   ),
	    recorda(Flag, '$flag'(Flag,NewValue), _)
	),  !.
EOF1FLAGRO.PL              00010038000100 85045 99364 000004Unix V7             HDR1FLALL.                 00010039000100 85045 99364 000000Unix V7             applic.pl
backup.pl
bagutl.pl
cmisce.pl
edit.pl
feach.pl
files.pl
flagro.pl
imisce.pl
invoca.pl
listut.pl
helper.pl
long.pl
medic.pl
metutl.pl
multil.pl
not.pl
occur.pl
ordset.pl
readin.pl
setutl.pl
struct.pl
test.pl
tidy.pl
trace.pl
writef.pl
EOF1FLALL.                 00010039000100 85045 99364 000001Unix V7             HDR1FLAT.PL                00010040000100 85045 99364 000000Unix V7             %   File   : FLAT.PL
%   Author : R.A.O'Keefe
%   Updated: 5 April 1984
%   Purpose: Flatten various binary trees to lists and convert back.

/*  This file was originally for PRESS, where you often want to take
    a tree such as 1+x+0+(u*v+9)+(x^2+2) and flatten it to a list
    such as [1,x,u*v,9,x^2,2] so that you can easily pick out all the
    constants or all the terms involving x or something, without having
    to write N different sets of predicates to handle N different
    binary operators.  It can be useful for other things as well.

    The <operator>_to_list predicates take a binary tree (where leaf
    nodes are anything not labelled by the operator) and flatten it
    to a list.  They also omit "units" of that operator, that is, if
    the operator is & {| + *} the constant true {false 0 1} will not
    appear in the list.  The predicate
	binary_to_list(Tree, Operator, Unit, Before, After)
    enables you to make your own versions.  Note that the answer is
    accumulated in the differnce Before-After.
	binary_to_list(Tree, Operator, Before, After)
    lets you convert trees where the operator has no unit.

    The well known and not often useful predicate "flatten" is a not
    very interesting special case of binary_to_list/5.

    The list_to_<operator> predicates take a list and turn it back
    into a tree.  Now there is an interesting question here: is
    [a,b,c] to be turned into f(a,f(b,c)) or into f(f(a,b),c)?  The
    former is a good idea for & | and '.', while the latter is a
    good idea for + and *.  My solution was to have the top-level
    predicate check whether the Operator is a yfx operator (such as
    + and * are) and if so to generate f(f(a,b),c).  In all other
    cases (xfy,xfx, or no operator declaration) f(a,f(b,c)) is
    generated.
	list_to_binary(List, Operator, Unit, Tree)
    lets you make your own versions.  If the list is [] the Unit will
    be returned, that is the only use of the Unit.
	list_to_binary(List, Operator, Tree)
    should be used when the Operator has no Unit, if given an empty
    list it will fail.
*/

:- public
	and_to_list/2,		%  conjunction -> list of conjuncts
	list_to_and/2,		%  list of conjuncts -> conjunction

	or_to_list/2,		%  disjunction -> list of disjuncts
	list_to_or/2,		%  list of disjuncts -> disjunction

	plus_to_list/2,		%  sum -> list of terms
	list_to_plus/2,		%  list of terms -> sum

	times_to_list/2,	%  product -> list of factors
	list_to_times/2,	%  list of factors -> product

	flatten/2,		%  list of lists -> list

	binary_to_list/4,	%  Term,Operator -> DifferenceList
	binary_to_list/5,	%  Term,Operator,Unit -> DifferenceList

	list_to_binary/3,	%  List,Operator -> Term
	list_to_binary/4.	%  List,Operator,Unit -> Term


:- mode
	and_to_list(+, -),
	binary_to_list(+, +, -, ?),
	binary_to_list(+, +, +, -, ?),
	flatten(+, -),
	list_to_and(+, -),
	list_to_binary(+, +, -),
	list_to_binary(+, +, +, -),
	list_to_binaryL(+, +, +, -),
	list_to_binaryR(+, +, -),
	list_to_or(+, -),
	list_to_plus(+, -),
	list_to_times(+, -),
	or_to_list(+, -),
	plus_to_list(+, -),
	times_to_list(+, -).



and_to_list(Conjunction, List) :-
	binary_to_list(Conjunction, &, true, List, []).


list_to_and(List, Conjunction) :-
	list_to_binary(List, &, true, Conjunction).



or_to_list(Disjunction, List) :-
	binary_to_list(Disjunction, (';'), false, List, []).


list_to_or(List, Disjunction) :-
	list_to_binary(List, (';'), false, Disjunction).



plus_to_list(Sum, List) :-
	binary_to_list(Sum, +, 0, List, []).


list_to_plus(List, Sum) :-
	list_to_binary(List, +, 0, Sum).



times_to_list(Product, List) :-
	binary_to_list(Product, *, 1, List, []).


list_to_times(List, Product) :-
	list_to_binary(List, *, 1, Product).



flatten(List, Flat) :-
	binary_to_list(List, ., [], Flat, []).



binary_to_list(Unit, _, Unit, List, List) :- !.
binary_to_list(Term, Operator, Unit, Before, After) :-
	Term =.. [Operator,Lhs,Rhs],	% Term can't be a variable
	!,
	binary_to_list(Lhs, Operator, Unit, Before, Middle),
	binary_to_list(Rhs, Operator, Unit, Middle, After).
binary_to_list(Term, _, _, [Term|After], After).


binary_to_list(Term, Operator, Before, After) :-
	nonvar(Term),
	Term =.. [Operator,Lhs,Rhs],
	!,
	binary_to_list(Lhs, Operator, Before, Middle),
	binary_to_list(Rhs, Operator, Middle, After).
binary_to_list(Term, _, [Term|After], After).



list_to_binary([], _, Unit, Unit) :- !.
list_to_binary([Head|Tail], Operator, _, Answer) :-
	current_op(_, yfx, Operator),
	!,
	list_to_binaryL(Tail, Operator, Head, Answer).
list_to_binary(List, Operator, _, Answer) :-
	list_to_binaryR(List, Operator, Answer).


list_to_binary([Head|Tail], Operator, Answer) :-
	current_op(_, yfx, Operator),
	!,
	list_to_binaryL(Tail, Operator, Head, Answer).
list_to_binary(List, Operator, Answer) :-
	list_to_binaryR(List, Operator, Answer).


list_to_binaryL([], _, Answer, Answer) :- !.
list_to_binaryL([Head|Tail], Operator, Sofar, Answer) :-
	Next =.. [Operator,Sofar,Head],
	list_to_binaryL(Tail, Operator, Next, Answer).


list_to_binaryR([Term], _, Term) :- !.
list_to_binaryR([Head|Tail], Operator, Answer) :-
	Answer =.. [Operator,Head,Rest], !,
	list_to_binaryR(Tail, Operator, Rest).


 EOF1FLAT.PL                00010040000100 85045 99364 000011Unix V7             HDR1FLS.                   00010041000100 85045 99364 000000Unix V7             /noheader edit.pl,-
files.pl,-
writef.pl,-
trace.pl,-
readin.pl,-
listut.pl,-
setutl.pl,-
bagutl.pl,-
invoca.pl,-
applic.pl,-
multil.pl,-
flagro.pl,-
cmisce.pl,-
imisce.pl,-
struct.pl,-
metutl.pl,-
tidy.pl,-
long.pl
EOF1FLS.                   00010041000100 85045 99364 000001Unix V7             HDR1GELRAN.PL              00010042000100 85045 99364 000000Unix V7             %   File   : RANDOM.PL
%   Author : Allen Van Gelder, Stanford
%   Updated: 21 February 1984
%   Purpose: Random number package.

% rannum produces a random non-negative integer whose low bits are not
% all that random, so it should be scaled to a smaller range in general.
% The integer is in the range 0 .. 2^w - 1,
% where w is the word size less the sign bit, e.g., 17 for DEC-10,
% and 15 or 31 for VAX and most IBM.
%
% ranunif produces a uniformly distributed non-negative random integer over
% a caller-specified range.  If range is R, the result is in 0 .. R-1.
%
% ranstart must be called before the first use of rannum or ranunif,
% and may be called later to redefine the seed.
% ranstart/0 causes a built-in seed to be used.
% ranstart(N), N an integer, varies this, but the same N always
% produces the same sequence of numbers.
%
% According to my reading of Knuth, Vol. 2, this generator has period
% 2^(w+1) and potency (w+1)/2, i.e., 8, 9, or 16 in practice.  Knuth says
% potency should be at least 5, so this looks more than adequate.
% Its drawback is the lack of randomness of low-order bits.

:- public
	ranstart/0,
	ranstart/1,
	rannum/1,
	ranunif/2.

:- mode
	ranstart(+),
	rannum(-),
	ranunif(+, -).

ranstart :-
	ranstart(245).


ranstart(N) :-
	Wsize is 17,			% bits available other than sign-bit
	Incr is (108 << (Wsize-8)) + 1,	% per Knuth, v.2 p.78
	Mult is 1965,			% OK for 15-17 Wsize
	Prev is Mult * (8 * N + 5) + Incr,
	recorda(ranState, ranState(Mult,Prev,Wsize,Incr), _).


rannum(Raw) :-
	recorded(ranState, ranState(Mult,Prev,Wsize,Incr), Ref),
	Curr is Mult * Prev + Incr,
	erase(Ref),
	recorda(ranState, ranState(Mult,Curr,Wsize,Incr), _),
	(   Curr > 0, Raw = Curr
	|   Curr < 0, Raw is Curr + (1<<Wsize)
	),  !.


ranunif(Range, Unif) :-
	Range > 0,
	recorded(ranState, ranState(Mult,Prev,Wsize,Incr), Ref),
	Curr is Mult * Prev + Incr,
	erase(Ref),
	recorda(ranState, ranState(Mult,Curr,Wsize,Incr), _),
	(   Curr > 0, Raw = Curr
	|   Curr < 0, Raw is Curr + (1<<Wsize)
	),  !,
	Unif is (Raw * Range) >> Wsize.


 EOF1GELRAN.PL              00010042000100 85045 99364 000005Unix V7             HDR1GENSYM.PL              00010043000100 85045 99364 000000Unix V7             %   File   : GENSYM.PL
%   Author : Lawrence Byrd?
%   Updated: 12 February 1985
%   Purpose: create new atoms
%   Needs  : append/3.

:- public
	cgensym/2,
	concat/3,
	gensym/2,
	reset_gensym/0,
	reset_gensym/1.

:- mode
	cgensym(+, ?),
	concat(+, +, ?),
	gensym(+, ?),
	reset_gensym(+).


%   gensym(Prefix, V)
%   binds V to a new atom whose name begins with Prefix and ends with a
%   number.  E.g. gensym(a,X), gensym(a,Y), gensym(a,Z) might bind
%   X to a1, Y to a2, Z to a3.  It only succeeds once per call, to get
%   another binding for V you have to call it again.

gensym(Prefix, V) :-
	var(V),
	atomic(Prefix),
	(   retract(flag(gensym(Prefix), M))
	|   M = 0
	),
	N is M+1,
	asserta(flag(gensym(Prefix), N)),
	concat(Prefix, N, V),
	!.


%   cgensym(Prefix, V)
%   binds V to a new atom unless it is already bound.  Thus
%   cgensym(a, fred) would succeed, but cgensym(a, X) would bind
%   X to a new atom, maybe a4.  "c" standard for "conditional".

cgensym(Prefix, V) :-
	nonvar(V), !,
	atomic(V),
	atomic(Prefix).
cgensym(Prefix, V) :-
	gensym(Prefix, V).


%   concat(Name1, Name2, Name3)
%   is like append on atoms.  That is, it appends the name of Name1 and
%   the name of Name2, and binds Name3 to the atom named by the result.
%   Unlike append, it will only work one way round.  Examples:
%   concat(a, b, ab), concat(10, 23, 1023), concat(gs, 46, gs46).
%   concat(04, 05, 405)*??*

concat(N1, N2, N3) :-
	name(N1, Ls1),
	name(N2, Ls2),
	append(Ls1, Ls2, Ls3),
	name(N3, Ls3).

%
%  This procedure will retract all occurances
%  of the flags set up by gensym. i.e. "flag(gensym(_),_)".
%

reset_gensym :-

	retract(flag(gensym(_),_)),
	reset_gensym.

reset_gensym:-!.


%
%  This procedure retracts the flag set up by
%  gensym for a particular symbol.
%

reset_gensym(Sym) :-

	retract(flag(gensym(Sym),_)).

reset_gensym(_):-!.
EOF1GENSYM.PL              00010043000100 85045 99364 000004Unix V7             HDR1GETFIL.PL              00010044000100 85045 99364 000000Unix V7             %   File   : GETFIL.PL
%   Author : Richard A. O'Keefe
%   Updated: 1 June 84
%   Purpose: read a file name from the terminal

:- public
	getfile/1,
	read_a_line/2.

/*  This is for use in tools for analysing Prolog (or other) files.
    getfile(File) prompts Next file: and reads a file name without
    any extraneous punctuation.  The file name is returned as an
    atom.  System-dependent processing should be done here.  The
    current Dec-10 version does that wrongly.  If you have a "search
    list", e.g. something like the $PATH environment variable for
    UNIX programs, this is a good place to put it in.

    read_a_line is similar to the read_line predicate in read_sent,
    but (a) it prompts, and (b) most of the tools that load this
    file don't wany anything else from read_sent.  But if your
    system wants do do case conversion on file names, you'll find
    the predicate you want there.  Oh well.
*/

:- mode
	getfile(-),
	read_a_line(+, -),
	rest_of_a_line(+, -).


getfile(File) :-
	read_a_line('Next file: ', Chars),
	name(File, Chars).


read_a_line(Prompt, Chars) :-
	write(Prompt), ttyflush,
	get0(Char),
	rest_of_a_line(Char, Chars).
			

rest_of_a_line(31, []) :- !.	%  <NL>	  = <CR><LF>
rest_of_a_line(27, []) :- !.	%  <ESC>
rest_of_a_line(Ch, [Ch|Rest]) :-
	get0(C2),
	rest_of_a_line(C2, Rest).


 EOF1GETFIL.PL              00010044000100 85045 99364 000003Unix V7             HDR1GRAPHS.PL              00010045000100 85045 99364 000000Unix V7             %   File   : GRAPHS.PL
%   Author : R.A.O'Keefe
%   Updated: 20 March 1984
%   Purpose: Graph-processing utilities.

/*  The P-representation of a graph is a list of (from-to) vertex
    pairs, where the pairs can be in any old order.  This form is
    convenient for input/output.

    The S-representation of a graph is a list of (vertex-neighbours)
    pairs, where the pairs are in standard order (as produced by
    keysort) and the neighbours of each vertex are also in standard
    order (as produced by sort).  This form is convenient for many
    calculations.

    p_to_s_graph(Pform, Sform) converts a P- to an S- representation.
    s_to_p_graph(Sform, Pform) converts an S- to a P- representation.

    warshall(Graph, Closure) takes the transitive closure of a graph
    in S-form.  (NB: this is not the reflexive transitive closure).

    s_to_p_trans(Sform, Pform) converts Sform to Pform, transposed.

    p_transpose transposes a graph in P-form, cost O(|E|).
    s_transpose transposes a graph in S-form, cost O(|V|^2).
*/
:- public
	p_to_s_graph/2,
	s_to_p_graph/2,
	s_to_p_trans/2,
	p_member/3,
	s_member/3,
	p_transpose/2,
	s_transpose/2,
	compose/3,
	top_sort/2,
	vertices/2,
	warshall/2.

:- mode
	vertices(+, -),
	p_to_s_graph(+, -),
	    p_to_s_vertices(+, -),
	    p_to_s_group(+, +, -),
		p_to_s_group(+, +, -, -),
	s_to_p_graph(+, -),
	    s_to_p_graph(+, +, -, -),
	s_to_p_trans(+, -),
	    s_to_p_trans(+, +, -, -),
	p_member(?, ?, +),
	s_member(?, ?, +),
	p_transpose(+, -),
	s_transpose(+, -),
	    s_transpose(+, -, ?, -),
		transpose_s(+, +, +, -),
	compose(+, +, -),
	    compose(+, +, +, -),
		compose1(+, +, +, -),
		    compose1(+, +, +, +, +, +, +, -),
	top_sort(+, -),
	    vertices_and_zeros(+, -, ?),
	    count_edges(+, +, +, -),
		incr_list(+, +, +, -),
	    select_zeros(+, +, -),
	    top_sort(+, -, +, +, +),
		decr_list(+, +, +, -, +, -),
	warshall(+, -),
	    warshall(+, +, -),
		warshall(+, +, +, -).



%   vertices(S_Graph,  Vertices)
%   strips off the neighbours lists of an S-representation to produce
%   a list of the vertices of the graph.  (It is a characteristic of
%   S-representations that *every* vertex appears, even if it has no
%   neighbours.)

vertices([], []) :- !.
vertices([Vertex-Neighbours|Graph], [Vertex|Vertices]) :-
	vertices(Graph, Vertices).



p_to_s_graph(P_Graph, S_Graph) :-
	sort(P_Graph, EdgeSet),
	p_to_s_vertices(EdgeSet, VertexBag),
	sort(VertexBag, VertexSet),
	p_to_s_group(VertexSet, EdgeSet, S_Graph).


p_to_s_vertices([], []) :- !.
p_to_s_vertices([A-Z|Edges], [A,Z|Vertices]) :-
	p_to_s_vertices(Edges, Vertices).


p_to_s_group([], _, []) :- !.
p_to_s_group([Vertex|Vertices], EdgeSet, [Vertex-Neibs|G]) :-
	p_to_s_group(EdgeSet, Vertex, Neibs, RestEdges),
	p_to_s_group(Vertices, RestEdges, G).


p_to_s_group([V-X|Edges], V, [X|Neibs], RestEdges) :- !,
	p_to_s_group(Edges, V, Neibs, RestEdges).
p_to_s_group(Edges, _, [], Edges).



s_to_p_graph([], []) :- !.
s_to_p_graph([Vertex-Neibs|G], P_Graph) :-
	s_to_p_graph(Neibs, Vertex, P_Graph, Rest_P_Graph),
	s_to_p_graph(G, Rest_P_Graph).


s_to_p_graph([], _, P_Graph, P_Graph) :- !.
s_to_p_graph([Neib|Neibs], Vertex, [Vertex-Neib|P], Rest_P) :-
	s_to_p_graph(Neibs, Vertex, P, Rest_P).



s_to_p_trans([], []) :- !.
s_to_p_trans([Vertex-Neibs|G], P_Graph) :-
	s_to_p_trans(Neibs, Vertex, P_Graph, Rest_P_Graph),
	s_to_p_trans(G, Rest_P_Graph).


s_to_p_trans([], _, P_Graph, P_Graph) :- !.
s_to_p_trans([Neib|Neibs], Vertex, [Neib-Vertex|P], Rest_P) :-
	s_to_p_trans(Neibs, Vertex, P, Rest_P).



warshall(Graph, Closure) :-
	warshall(Graph, Graph, Closure).


warshall([], Closure, Closure) :- !.
warshall([V-_|G], E, Closure) :-
	memberchk(V-Y, E),	%  Y := E(v)
	warshall(E, V, Y, NewE),
	warshall(G, NewE, Closure).


warshall([X-Neibs|G], V, Y, [X-NewNeibs|NewG]) :-
	memberchk(V, Neibs),
	!,
	ord_union(Neibs, Y, NewNeibs),
	warshall(G, V, Y, NewG).
warshall([X-Neibs|G], V, Y, [X-Neibs|NewG]) :- !,
	warshall(G, V, Y, NewG).
warshall([], _, _, []).



p_transpose([], []) :- !.
p_transpose([From-To|Edges], [To-From|Transpose]) :-
	p_transpose(Edges, Transpose).



s_transpose(S_Graph, Transpose) :-
	s_transpose(S_Graph, Base, Base, Transpose).

s_transpose([], [], Base, Base) :- !.
s_transpose([Vertex-Neibs|Graph], [Vertex-[]|RestBase], Base, Transpose) :-
	s_transpose(Graph, RestBase, Base, SoFar),
	transpose_s(SoFar, Neibs, Vertex, Transpose).

transpose_s([Neib-Trans|SoFar], [Neib|Neibs], Vertex,
		[Neib-[Vertex|Trans]|Transpose]) :- !,
	transpose_s(SoFar, Neibs, Vertex, Transpose).
transpose_s([Head|SoFar], Neibs, Vertex, [Head|Transpose]) :- !,
	transpose_s(SoFar, Neibs, Vertex, Transpose).
transpose_s([], [], _, []).



%   p_member(X, Y, P_Graph)
%   tests whether the edge (X,Y) occurs in the graph.  This always
%   costs O(|E|) time.  Here, as in all the operations in this file,
%   vertex labels are assumed to be ground terms, or at least to be
%   sufficiently instantiated that no two of them have a common instance.

p_member(X, Y, P_Graph) :-
	nonvar(X), nonvar(Y), !,
	memberchk(X-Y, P_Graph).
p_member(X, Y, P_Graph) :-
	member(X-Y, P_Graph).

%   s_member(X, Y, S_Graph)
%   tests whether the edge (X,Y) occurs in the graph.  If either
%   X or Y is instantiated, the check is order |V| rather than
%   order |E|.

s_member(X, Y, S_Graph) :-
	var(X), var(Y), !,
	member(X-Neibs, S_Graph),
	member(Y, Neibs).
s_member(X, Y, S_Graph) :-
	var(X), !,
	member(X-Neibs, S_Graph),
	memberchk(Y, Neibs).
s_member(X, Y, S_Graph) :-
	var(Y), !,
	memberchk(X-Neibs, S_Graph),
	member(Y, Neibs).
s_member(X, Y, S_Graph) :-
	memberchk(X-Neibs, S_Graph),
	memberchk(Y, Neibs).


%   compose(G1, G2, Composition)
%   calculates the composition of two S-form graphs, which need not
%   have the same set of vertices.

compose(G1, G2, Composition) :-
	vertices(G1, V1),
	vertices(G2, V2),
	ord_union(V1, V2, V),
	compose(V, G1, G2, Composition).


compose([], _, _, []) :- !.
compose([Vertex|Vertices], [Vertex-Neibs|G1], G2, [Vertex-Comp|Composition]) :- !,
	compose1(Neibs, G2, [], Comp),
	compose(Vertices, G1, G2, Composition).
compose([Vertex|Vertices], G1, G2, [Vertex-[]|Composition]) :-
	compose(Vertices, G1, G2, Composition).


compose1([V1|Vs1], [V2-N2|G2], SoFar, Comp) :-
	compare(Rel, V1, V2), !,
	compose1(Rel, V1, Vs1, V2, N2, G2, SoFar, Comp).
compose1(_, _, Comp, Comp).


compose1(<, _, Vs1, V2, N2, G2, SoFar, Comp) :- !,
	compose1(Vs1, [V2-N2|G2], SoFar, Comp).
compose1(>, V1, Vs1, _, _, G2, SoFar, Comp) :- !,
	compose1([V1|Vs1], G2, SoFar, Comp).
compose1(=, V1, Vs1, V1, N2, G2, SoFar, Comp) :-
	ord_union(N2, SoFar, Next),
	compose1(Vs1, G2, Next, Comp).


/*  NOT USED AFTER ALL
%   raakau(Vertices, InitialValue, Tree)
%   takes an *ordered* list of verticies and an initial value, and
%   makes a very special sort of tree out of them, which represents
%   a function sending each vertex to the initial value.  Note that
%   in the third clause for raakau/6 Z can never be 0, this means
%   that it doesn't matter *what* "greatest member" is reported for
%   empty trees.

raakau(Vertices, InitialValue, Tree) :-
	length(Vertices, N),
	raakau(N, Vertices, _, _, InitialValue, Tree).


raakau(0, Vs, Vs, 0, I, t) :- !.
raakau(1, [V|Vs], Vs, V, I, t(V,I)) :- !.
raakau(N, Vi, Vo, W, I, t(V,W,I,L,R)) :-
	A is (N-1)/2,
	Z is (N-1)-A,	%  Z >= 1
	raakau(A, Vi, [V|Vm], _, I, L),
	raakau(Z, Vm, Vo, W, I, R).


%   incdec(OldTree, Labels, Incr, NewTree)
%   adds Incr to the value associated with each element of Labels
%   in OldTree, producing a new tree.  OldTree must have been produced
%   either by raakau or by incdec, Labels must be in ascedning order,
%   and must be a subset of the labels of the tree.

incdec(OldTree, Labels, Incr, NewTree) :-
	incdec(OldTree, NewTree, Labels, _, Incr).


incdec(t(V,M), t(V,N), [V|L], L, I) :- !,
	N is M+I.
incdec(t(V,W,M,L1,R1), t(V,W,N,L2,R2), Li, Lo, I) :-
	(   Li = [Hi|_], Hi @< V, !,
		incdec(L1, L2, Li, Lm, I)
	;   L2 = L1, Lm = Li
	),
	(   Lm = [V|Lr], !,
		N is M+I
	;   Lr = Lm, N = M
	),
	(   Lr = [Hr|_], Hr @=< W, !,
		incdec(R1, R2, Lr, Lo, I)
	;   R2 = R1, Lo = Lr
	).
/*  END UNUSED CODE */



top_sort(Graph, Sorted) :-
	vertices_and_zeros(Graph, Vertices, Counts0),
	count_edges(Graph, Vertices, Counts0, Counts1),
	select_zeros(Counts1, Vertices, Zeros),
	top_sort(Zeros, Sorted, Graph, Vertices, Counts1).


vertices_and_zeros([], [], []) :- !.
vertices_and_zeros([Vertex-Neibs|Graph], [Vertex|Vertices], [0|Zeros]) :-
	vertices_and_zeros(Graph, Vertices, Zeros).


count_edges([], _, Counts, Counts) :- !.
count_edges([Vertex-Neibs|Graph], Vertices, Counts0, Counts2) :-
	incr_list(Neibs, Vertices, Counts0, Counts1),
	count_edges(Graph, Vertices, Counts1, Counts2).


incr_list([], _, Counts, Counts) :- !.
incr_list([V|Neibs], [V|Vertices], [M|Counts0], [N|Counts1]) :- !,
	N is M+1,
	incr_list(Neibs, Vertices, Counts0, Counts1).
incr_list(Neibs, [_|Vertices], [N|Counts0], [N|Counts1]) :-
	incr_list(Neibs, Vertices, Counts0, Counts1).


select_zeros([], [], []) :- !.
select_zeros([0|Counts], [Vertex|Vertices], [Vertex|Zeros]) :- !,
	select_zeros(Counts, Vertices, Zeros).
select_zeros([_|Counts], [_|Vertices], Zeros) :-
	select_zeros(Counts, Vertices, Zeros).



top_sort([], [], Graph, _, Counts) :- !,
	vertices_and_zeros(Graph, _, Counts).
top_sort([Zero|Zeros], [Zero|Sorted], Graph, Vertices, Counts1) :-
	memberchk(Zero-Neibs, Graph),
	decr_list(Neibs, Vertices, Counts1, Counts2, Zeros, NewZeros),
	top_sort(NewZeros, Sorted, Graph, Vertices, Counts2).


decr_list([], _, Counts, Counts, Zeros, Zeros) :- !.
decr_list([V|Neibs], [V|Vertices], [1|Counts1], [0|Counts2], Zi, Zo) :- !,
	decr_list(Neibs, Vertices, Counts1, Counts2, [V|Zi], Zo).
decr_list([V|Neibs], [V|Vertices], [N|Counts1], [M|Counts2], Zi, Zo) :- !,
	M is N-1,
	decr_list(Neibs, Vertices, Counts1, Counts2, Zi, Zo).
decr_list(Neibs, [_|Vertices], [N|Counts1], [N|Counts2], Zi, Zo) :-
	decr_list(Neibs, Vertices, Counts1, Counts2, Zi, Zo).


EOF1GRAPHS.PL              00010045000100 85045 99364 000020Unix V7             HDR1HEAPS.PL               00010046000100 85045 99364 000000Unix V7             %   File   : HEAPS.PL
%   Author : R.A.O'Keefe
%   Updated: 29 November 1983
%   Purpose: Implement heaps in Prolog.

/*  A heap is a labelled binary tree where the key of each node is less
    than or equal to the keys of its sons.  The point of a heap is that
    we can keep on adding new elements to the heap and we can keep on
    taking out the minimum element.  If there are N elements total, the
    total time is O(NlgN).  If you know all the elements in advance, you
    are better off doing a merge-sort, but this file is for when you
    want to do say a best-first search, and have no idea when you start
    how many elements there will be, let alone what they are.

    A heap is represented as a triple t(N, Free, Tree) where N is the
    number of elements in the tree, Free is a list of integers which
    specifies unused positions in the tree, and Tree is a tree made of
	t			terms for empty subtrees and
	t(Key,Datum,Lson,Rson)	terms for the rest
    The nodes of the tree are notionally numbered like this:
				    1
		     2				    3
             4               6               5               7
         8      12      10     14       9       13      11     15
      ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..  ..
    The idea is that if the maximum number of elements that have been in
    the heap so far is M, and the tree currently has K elements, the tree
    is some subtreee of the tree of this form having exactly M elements,
    and the Free list is a list of K-M integers saying which of the 
    positions in the M-element tree are currently unoccupied.  This free
    list is needed to ensure that the cost of passing N elements through
    the heap is O(NlgM) instead of O(NlgN).  For M say 100 and N say 10^4
    this means a factor of two.  The cost of the free list is slight.
    The storage cost of a heap in a copying Prolog (which Dec-10 Prolog is
    not) is 2K+3M words.



*/

:- public
	add_to_heap/4,		%   Heap x Key x Datum -> Heap
	get_from_heap/4,	%   Heap -> Key x Datum x Heap
	heap_size/2,		%   Heap -> Size
	heap_to_list/2,		%   Heap -> List
	list_to_heap/2,		%   List -> Heap
	min_of_heap/3,		%   Heap -> Key x Datum
	min_of_heap/5.		%   Heap -> (Key x Datum) x (Key x Datum)

:- mode
	add_to_heap(+, +, +, -),
	    add_to_heap(+, +, +, +, -),
		add_to_heap(+, +, +, +, +, +, -, -),
		sort2(+, +, +, +, -, -, -, -),
	get_from_heap(+, ?, ?, -),
	    repair_heap(+, +, +, -),
	heap_size(+, ?),
	heap_to_list(+, -),
	    heap_tree_to_list(+, -),
		heap_tree_to_list(+, +, -),
	list_to_heap(+, -),
	    list_to_heap(+, +, +, -),
	min_of_heap(+, ?, ?),
	min_of_heap(+, ?, ?, ?, ?),
	    min_of_heap(+, +, ?, ?).


%   add_to_heap(OldHeap, Key, Datum, NewHeap)
%   inserts the new Key-Datum pair into the heap.  The insertion is
%   not stable, that is, if you insert several pairs with the same
%   Key it is not defined which of them will come out first, and it
%   is possible for any of them to come out first depending on the 
%   history of the heap.  If you need a stable heap, you could add
%   a counter to the heap and include the counter at the time of
%   insertion in the key.  If the free list is empty, the tree will
%   be grown, otherwise one of the empty slots will be re-used.  (I
%   use imperative programming language, but the heap code is as 
%   pure as the trees code, you can create any number of variants
%   starting from the same heap, and they will share what common
%   structure they can without interfering with each other.)

add_to_heap(t(M,[],OldTree), Key, Datum, t(N,[],NewTree)) :- !,
	N is M+1,
	add_to_heap(N, Key, Datum, OldTree, NewTree).
add_to_heap(t(M,[H|T],OldTree), Key, Datum, t(N,T,NewTree)) :-
	N is M+1,
	add_to_heap(H, Key, Datum, OldTree, NewTree).


add_to_heap(1, Key, Datum, _, t(Key,Datum,t,t)) :- !.
add_to_heap(N, Key, Datum, t(K1,D1,L1,R1), t(K2,D2,L2,R2)) :-
	E is N mod 2,
	M is N/2,
    %   M > 0,		%  only called from list_to_heap/4,add_to_heap/4
	sort2(Key, Datum, K1, D1, K2, D2, K3, D3),
	add_to_heap(E, M, K3, D3, L1, R1, L2, R2).


add_to_heap(0, N, Key, Datum, L1, R, L2, R) :- !,
	add_to_heap(N, Key, Datum, L1, L2).
add_to_heap(1, N, Key, Datum, L, R1, L, R2) :- !,
	add_to_heap(N, Key, Datum, R1, R2).


sort2(Key1, Datum1, Key2, Datum2, Key1, Datum1, Key2, Datum2) :-
	Key1 @< Key2,
	!.
sort2(Key1, Datum1, Key2, Datum2, Key2, Datum2, Key1, Datum1).



%   get_from_heap(OldHeap, Key, Datum, NewHeap)
%   returns the Key-Datum pair in OldHeap with the smallest Key, and
%   also a New Heap which is the Old Heap with that pair deleted.
%   The easy part is picking off the smallest element.  The hard part
%   is repairing the heap structure.  repair_heap/4 takes a pair of
%   heaps and returns a new heap built from their elements, and the
%   position number of the gap in the new tree.  Note that repair_heap
%   is *not* tail-recursive.

get_from_heap(t(N,Free,t(Key,Datum,L,R)), Key, Datum, t(M,[Hole|Free],Tree)) :-
	M is N-1,
	repair_heap(L, R, Tree, Hole).


repair_heap(t(K1,D1,L1,R1), t(K2,D2,L2,R2), t(K2,D2,t(K1,D1,L1,R1),R3), N) :-
	K2 @< K1,
	!,
	repair_heap(L2, R2, R3, M),
	N is 2*M+1.
repair_heap(t(K1,D1,L1,R1), t(K2,D2,L2,R2), t(K1,D1,L3,t(K2,D2,L2,R2)), N) :- !,
	repair_heap(L1, R1, L3, M),
	N is 2*M.
repair_heap(t(K1,D1,L1,R1), t, t(K1,D1,L3,t), N) :- !,
	repair_heap(L1, R1, L3, M),
	N is 2*M.
repair_heap(t, t(K2,D2,L2,R2), t(K2,D2,t,R3), N) :- !,
	repair_heap(L2, R2, R3, M),
	N is 2*M+1.
repair_heap(t, t, t, 1) :- !.



%   heap_size(Heap, Size)
%   reports the number of elements currently in the heap.

heap_size(t(Size,_,_), Size).



%   heap_to_list(Heap, List)
%   returns the current set of Key-Datum pairs in the Heap as a
%   List, sorted into ascending order of Keys.  This is included
%   simply because I think every data structure foo ought to have
%   a foo_to_list and list_to_foo relation (where, of course, it
%   makes sense!) so that conversion between arbitrary data
%   structures is as easy as possible.  This predicate is basically
%   just a merge sort, where we can exploit the fact that the tops
%   of the subtrees are smaller than their descendants.

heap_to_list(t(_,_,Tree), List) :-
	heap_tree_to_list(Tree, List).


heap_tree_to_list(t, []) :- !.
heap_tree_to_list(t(Key,Datum,Lson,Rson), [Key-Datum|Merged]) :-
	heap_tree_to_list(Lson, Llist),
	heap_tree_to_list(Rson, Rlist),
	heap_tree_to_list(Llist, Rlist, Merged).


heap_tree_to_list([H1|T1], [H2|T2], [H2|T3]) :-
	H2 @< H1,
	!,
	heap_tree_to_list([H1|T1], T2, T3).
heap_tree_to_list([H1|T1], T2, [H1|T3]) :- !,
	heap_tree_to_list(T1, T2, T3).
heap_tree_to_list([], T, T) :- !.
heap_tree_to_list(T, [], T).



%   list_to_heap(List, Heap)
%   takes a list of Key-Datum pairs (such as keysort could be used to
%   sort) and forms them into a heap.  We could do that a wee bit
%   faster by keysorting the list and building the tree directly, but
%   this algorithm makes it obvious that the result is a heap, and
%   could be adapted for use when the ordering predicate is not @<
%   and hence keysort is inapplicable.

list_to_heap(List, Heap) :-
	list_to_heap(List, 0, t, Heap).


list_to_heap([], N, Tree, t(N,[],Tree)) :- !.
list_to_heap([Key-Datum|Rest], M, OldTree, Heap) :-
	N is M+1,
	add_to_heap(N, Key, Datum, OldTree, MidTree),
	list_to_heap(Rest, N, MidTree, Heap).



%   min_of_heap(Heap, Key, Datum)
%   returns the Key-Datum pair at the top of the heap (which is of
%   course the pair with the smallest Key), but does not remove it
%   from the heap.  It fails if the heap is empty.

%   min_of_heap(Heap, Key1, Datum1, Key2, Datum2)
%   returns the smallest (Key1) and second smallest (Key2) pairs in
%   the heap, without deleting them.  It fails if the heap does not
%   have at least two elements.

min_of_heap(t(_,_,t(Key,Datum,_,_)), Key, Datum).


min_of_heap(t(_,_,t(Key1,Datum1,Lson,Rson)), Key1, Datum1, Key2, Datum2) :-
	min_of_heap(Lson, Rson, Key2, Datum2).


min_of_heap(t(Ka,Da,_,_), t(Kb,Db,_,_), Kb, Db) :-
	Kb @< Ka, !.
min_of_heap(t(Ka,Da,_,_), _, Ka, Da).
min_of_heap(t, t(Kb,Db,_,_), Kb, Db).

EOF1HEAPS.PL               00010046000100 85045 99364 000016Unix V7             HDR1HELP.PL                00010047000100 85045 99364 000000Unix V7             %   File   : HELP.PL
%   Author : L. Hardman
%   Updated: 6 April 1984
%   Purpose: Extract predicate names and descriptions from files.



:- public
	help/0,
	help/1,
	help/2.

:- mode
	help(+),
	help(+,+),
	search_help_file(+,+,-),
	search_source_file(+,+),
		look_for_predicate(+,+),
		print_if_not_end_of_comment(+),
	parse_help_line(+,+,-,+,?),
		has_paren(+,-,+,?),
		get_file(-,+,?),
		get_arity(+,?,+,?),
			get_arity(+,+,-,+,?),
	parse_source_line(+,+,+,?),
	read_a_line(-),
	skip_string(+,+,?),
	skip_string(+,+,+,?),
	skip_spaces(+,?).


%#  help
%^  Prints out information on how to use the help facility.
%$

help :-
	write('Type "help(predicate_name)" or "help(predicate_name, arity)"'),nl,
	write('to get help on a particular predicate.'), nl.


%#  help(Predicate)
%^  Prints out the labelled comments for Predicate.
%$

help(Predicate) :-
	help(Predicate, _).

%#  help(Functor, Arity)
%^  Prints out the labelled comments for Functor/Arity.
%$

help(Functor, Arity) :-
	nofileerrors,
	seeing(OldFile),
	(   see('Util:Util.Hlp'),
	    search_help_file(Functor, Arity, File),	% Look for "File"
							% containing "Functor"
	    seen,
	    fileerrors
	;
	    fileerrors,
	    fail
	),
	nofileerrors,
	(   see(File),
	    search_source_file(Functor, Arity),		% Find and print out
	    						% comment
	    seen,
	    fileerrors
	;
	    fileerrors,
	    fail
	),
	see(OldFile).


%   search_help_file(Functor, Arity, File)
%   looks through the input stream line by line.
%   It succeeds when it finds a line with the correct predicate in it
%   and returns the name of the File containing Functor/Arity.

search_help_file(FunctorAtom, Arity, FileAtom) :-
	name(FunctorAtom, Functor),
	repeat,
	read_a_line(Line),
	parse_help_line(Functor, Arity, File, Line, []),
	name(FileAtom, File).


%   search_source_file(Functor, Arity)
%   looks through the input stream line by line.
%   It succeeds after the comments on Functor/Arity have been printed out.

search_source_file(Functor, Arity) :-
	look_for_predicate(Functor, Arity),
	look_for_comment,
	print_comment.

look_for_predicate(FunctorAtom, Arity) :-
	name(FunctorAtom, Functor),
	repeat,
	read_a_line(Line),
	parse_source_line(Functor, Arity, Line, []),
	skip_string(37, 35, Line, Rest_of_line), %  Find "%" followed by "#"
	writef(' %s',[Rest_of_line]), nl.

look_for_comment :-
	repeat,
	read_a_line(Line),
	skip_string(37, 94, Line, Rest_of_line), %  Find "%" followed by "^"
	writef(' %s',[Rest_of_line]), nl.

print_comment :-				 %  Failure driven
	repeat,
	read_a_line(Line),
	print_if_not_end_of_comment(Line).

print_if_not_end_of_comment(Line) :-
	skip_string(37, 36, Line, _),		 %  Find "%" followed by "$"
	!.						%  Finished printing
print_if_not_end_of_comment(Line) :-
	skip_string(37, Line, Rest_of_line),		%  Scrap initial "%"
	writef('%s',[Rest_of_line]), nl,
	!,
	fail.



%   parse_help_line(Pred, Arity, File) has to parse a help line.
%   There are no leading spaces or tabs on one of these lines.
%   The predicate symbol is everything up to the first "(" or
%   layout character.  If there is no "(" the arity is 0.  If
%   there is a "(", the arity is the number of commas up to
%   the matching ")" plus 1.  In either case we then have to
%   skip to the "%", and the File is everything after that.

parse_help_line(Predicate, Arity, File) -->
	has_paren(Predicate, HadParen),
	{   Predicate \== []   },
	get_arity(HadParen, Arity),
	skip_string(37),
	get_file(File).


has_paren([], no, [], []) :- !.
has_paren([], yes) -->
	"(", !.
has_paren([], no) -->
	[C], {C =< 32}, !.
has_paren([C|Cs], HadParen) -->
	[C],
	has_paren(Cs, HadParen).


get_file([C|Cs]) -->
	[C], {C > 32}, !,
	get_file(Cs).
get_file([]) --> [].


get_arity(no, 0) --> !.
get_arity(yes, N) -->
	get_arity(0, 1, N).

get_arity(Depth, SoFar, Arity) -->
	(   "'", !, skip_string(39), get_arity(Depth, SoFar, Arity)
	|   """",!, skip_string(34), get_arity(Depth, SoFar, Arity)
	|   "(", !, {E is Depth+1},  get_arity(E, SoFar, Arity)
	|   ")", {Depth = 0}, !, {Arity = SoFar}
	|   ")", !, {E is Depth-1},  get_arity(E, SoFar, Arity)
	|   ",", {Depth = 0}, !, {Next is SoFar+1}, get_arity(Depth, Next, Arity)
	|   [_], get_arity(Depth, SoFar, Arity)
	).

%   parse_source_line(Pred, Arity) has to parse a
%   line in the source file.

parse_source_line(Predicate, Arity) -->
	skip_string(37,35),		%  Find "%" followed by "$"
	skip_spaces,
	has_paren(Predicate, HadParen),
	{   Predicate \== []   },
	get_arity(HadParen, Arity).


%   read_a_line(Line)
%   reads the next line from the current input stream.
%   If there IS a next line, Line is unified with the characters
%   up to but excluding the closing newline (31) character.
%   If there is NOT a next line, Line is unified with 'end_of_file'.

read_a_line(Line) :-
	get0(Char),
	read_a_line(Char, Line).

read_a_line(26, end_of_file) :- !.
read_a_line(Other, Line) :-
	rest_of_line(Other, Line).

rest_of_line(31, []) :- !.
rest_of_line(Char, [Char|Line]) :-
	get0(NextChar),
	rest_of_line(NextChar, Line).


%   skip_string(Char, Line, Rest_of_line) and
%   skip_string(Char1, Char2, Line, Rest_of_line)
%   both scan the line until they find the relevant characters.

skip_string(C) --> [C], !.
skip_string(C) --> [_], skip_string(C).

skip_string(ThisChar, NextChar) -->
	skip_string(ThisChar),
	[NextChar],!.


%   skip_spaces skip over any number of spaces and tabs.

skip_spaces --> [32], !, skip_spaces.
skip_spaces --> [ 9], !, skip_spaces.
skip_spaces --> [].
 EOF1HELP.PL                00010047000100 85045 99364 000011Unix V7             HDR1HELP2.PL               00010048000100 85045 99364 000000Unix V7             %   File   : HELP.PL
%   Author : L. Hardman
%   Updated: 6 April 1984
%   Purpose: Extract predicate names and descriptions from files.



:- public
	help/0,
	help/1,
	help/2.

:- mode
	help(+),
	help(+,+),
	search_help_file(+,+,-),
	search_source_file(+,+),
		look_for_predicate(+,+),
		print_if_not_end_of_comment(+),
	parse_help_line(+,+,-,+,?),
		has_paren(+,-,+,?),
		get_file(-,+,?),
		get_arity(+,?,+,?),
			get_arity(+,+,-,+,?),
	parse_source_line(+,+,+,?),
	read_a_line(-),
	skip_string(+,+,?),
	skip_string(+,+,+,?),
	skip_spaces(+,?).


%#  help
%^  Prints out information on how to use the help facility.
%$

help :-
	write('Type "help(predicate_name)" or "help(predicate_name, arity)"'),nl,
	write('to get help on a particular predicate.'), nl.


%#  help(Predicate)
%^  Prints out the labelled comments for Predicate.
%$

help(Predicate) :-
	help(Predicate, _).

%#  help(Functor, Arity)
%^  Prints out the labelled comments for Functor/Arity.
%$

help(Functor, Arity) :-
	seeing(OldFile),
	see_if_poss('Util:Util.Hlp'),
	search_help_file(Functor, Arity, File),		% Look for "File"
							% containing "Functor"
	seen,
	see_if_poss(File),
	search_source_file(Functor, Arity),		% Find and print out
    							% comment
	seen,
	see(OldFile).

see_if_poss(File) :-
	nofileerrors,
	see(File),
	fileerrors.
see_if_poss(File) :-
	writef('**ERROR in opening %t',[File]), nl,
	fail.



%   search_help_file(Functor, Arity, File)
%   looks through the input stream line by line.
%   It succeeds when it finds a line with the correct predicate in it
%   and returns the name of the File containing Functor/Arity.

search_help_file(FunctorAtom, Arity, FileAtom) :-
	name(FunctorAtom, Functor),
	repeat,
	read_a_line(Line),
	parse_help_line(Functor, Arity, File, Line, []),
	name(FileAtom, File).


%   search_source_file(Functor, Arity)
%   looks through the input stream line by line.
%   It succeeds after the comments on Functor/Arity have been printed out.

search_source_file(Functor, Arity) :-
	look_for_predicate(Functor, Arity),
	look_for_comment,
	print_comment.

look_for_predicate(FunctorAtom, Arity) :-
	name(FunctorAtom, Functor),
	repeat,
	read_a_line(Line),
	parse_source_line(Functor, Arity, Line, []),
	skip_string(37, 35, Line, Rest_of_line), %  Find "%" followed by "#"
	!,
	writef(' %s',[Rest_of_line]), nl.

look_for_comment :-
	repeat,
	read_a_line(Line),
	skip_string(37, 94, Line, Rest_of_line), %  Find "%" followed by "^"
	!,
	writef(' %s',[Rest_of_line]), nl.

print_comment :-				 %  Failure driven
	repeat,
	read_a_line(Line),
	print_if_not_end_of_comment(Line),
	!.

print_if_not_end_of_comment(Line) :-
	skip_string(37, 36, Line, _),		 %  Find "%" followed by "$"
	!.						%  Finished printing
print_if_not_end_of_comment(Line) :-
	skip_string(37, Line, Rest_of_line),		%  Scrap initial "%"
	writef('%s',[Rest_of_line]), nl,
	fail.



%   parse_help_line(Pred, Arity, File) has to parse a help line.
%   There are no leading spaces or tabs on one of these lines.
%   The predicate symbol is everything up to the first "(" or
%   layout character.  If there is no "(" the arity is 0.  If
%   there is a "(", the arity is the number of commas up to
%   the matching ")" plus 1.  In either case we then have to
%   skip to the "%", and the File is everything after that.

parse_help_line(Predicate, Arity, File) -->
	has_paren(Predicate, HadParen),
	{   Predicate \== []   },
	get_arity(HadParen, Arity),
	skip_string(37),
	get_file(File).


has_paren([], no, [], []) :- !.
has_paren([], yes) -->
	"(", !.
has_paren([], no) -->
	[C], {C =< 32}, !.
has_paren([C|Cs], HadParen) -->
	[C],
	has_paren(Cs, HadParen).


get_file([C|Cs]) -->
	[C], {C > 32}, !,
	get_file(Cs).
get_file([]) --> [].


get_arity(no, 0) --> !.
get_arity(yes, N) -->
	get_arity(0, 1, N).

get_arity(Depth, SoFar, Arity) -->
	(   "'", !, skip_string(39), get_arity(Depth, SoFar, Arity)
	|   """",!, skip_string(34), get_arity(Depth, SoFar, Arity)
	|   "(", !, {E is Depth+1},  get_arity(E, SoFar, Arity)
	|   ")", {Depth = 0}, !, {Arity = SoFar}
	|   ")", !, {E is Depth-1},  get_arity(E, SoFar, Arity)
	|   ",", {Depth = 0}, !, {Next is SoFar+1}, get_arity(Depth, Next, Arity)
	|   [_], get_arity(Depth, SoFar, Arity)
	).

%   parse_source_line(Pred, Arity) has to parse a
%   line in the source file.

parse_source_line(Predicate, Arity) -->
	skip_string(37,35),		%  Find "%" followed by "$"
	skip_spaces,
	has_paren(Predicate, HadParen),
	{   Predicate \== []   },
	get_arity(HadParen, Arity).


%   read_a_line(Line)
%   reads the next line from the current input stream.
%   If there IS a next line, Line is unified with the characters
%   up to but excluding the closing newline (31) character.
%   If there is NOT a next line, Line is unified with 'end_of_file'.

read_a_line(Line) :-
	get0(Char),
	read_a_line(Char, Line).

read_a_line(26, end_of_file) :- !.
read_a_line(Other, Line) :-
	rest_of_line(Other, Line).

rest_of_line(31, []) :- !.
rest_of_line(Char, [Char|Line]) :-
	get0(NextChar),
	rest_of_line(NextChar, Line).


%   skip_string(Char, Line, Rest_of_line) and
%   skip_string(Char1, Char2, Line, Rest_of_line)
%   both scan the line until they find the relevant characters.

skip_string(C) --> [C], !.
skip_string(C) --> [_], skip_string(C).

skip_string(ThisChar, NextChar) -->
	skip_string(ThisChar),
	[NextChar].


%   skip_spaces skip over any number of spaces and tabs.

skip_spaces --> [32], !, skip_spaces.
skip_spaces --> [ 9], !, skip_spaces.
skip_spaces --> [].
EOF1HELP2.PL               00010048000100 85045 99364 000011Unix V7             HDR1HELPER.HLP             00010049000100 85045 99364 000000Unix V7             File: Mec:Helper.Hlp	    Author: R.A.O'Keefe		Updated: 27 July 82

#help.
    Call give_help(helper) for a list of topics.

#source.
    The source file for Helper is Mec:Helper.Pl.
    It should be compiled, as typing out the help will be slowish if it
    is not.  The only utility used is append(+,+,-), and it (re)defines
    that itself.  This help file is Mec:Helper.Hlp.

#predicates,commands.
    Helper defines four predicates: give_help/0, give_help/1, give_help/2,
    and try_hard_to_see.  Some of the other kit might be useful: have a look.
    It requires you to define one predicate: help_file.
    'give_help' and 'try_hard_to_see' are topics in Helper's help file.

#give_help.

    There are three versions of this predicate/command.

give_help 
	lists the Areas for which help is available (i.e. those Areas
	for which help_file(Area, File, Delim) is known) and also tells
	you about give_help/1 and give_help/2.

give_help(Area)
	reads through the help file for a given Area and lists the Topics
	in it.  (These are the terms following the delimiter characters.)
	You may also call give_help(Area, _) for the same effect.

give_help(Area, Topic)
	looks for a topic in Area's help file whose heading (the Prolog
	term following the Delimiter) unifies with Topic.  As a special
	case, if Topic is a variable, all the headings are listed, with
	no text.  Normally, the first note whose heading unifies with
	Topic is selected, its heading is ignored, and all its text is
	displayed.

#help_file.
    You must supply at least one fact of the form

	help_file(Area, FileName, Delimiter).

    if you are to use Helper.  For example, there should be a clause

	help_file(helper, 'mec:helper.hlp', 35).	% 35=

    if you want to get help about Helper itself.  Helper uses its own
    predicate try_hard_to_see/3 to find help files, so if the device is
    one of Dsk:, Mec:, Util:, or Pll: you may omit it, and if the extension
    is empty, .Hlp, or .Txt you may omit that too.  Dec-10 Prolog cannot
    handle ppns, or paths, so Dev:Name.Ext is as much as you can say.

    (As a special convenience, help_file(Atom,Atom,35) :- atom(Atom) is
    virtually present.  Thus a Prolog program FooBaz.Pl can call give_help
    as if help_file(foobaz, foobaz, 35) were present, which will find the
    help file or the source file itself.)

    The point of the Area argument is to let you have several help files;
    if you want help about Helper, Util, Mbase, and something you have built
    using Mbase, you can have a help file for each.

    A Help File consists of a sequence of Notes, possibly preceded by a header
    and time stamp which is ignored.  A Note begins with the Delimiter (a
    single character), a Prolog term, and a full stop.  Since this term will
    be read(), the full stop must be followed by a space or new line.  The Note
    is terminated by the next Delimiter character or by the end of the file.
    The Delimiter character may not appear in the body of a Note, but there is
    nothing to stop it being a strange character such as ^Q.

    A note about 'end_of_file' will terminate a help file as far as Helper
    is concerned.  No test beyond that point will be examined.  See the
    'prolog' topic in this file for an example of its use.  Note that the
    word 'end_of_file' must appear on its own; 'get0,get,end_of_file' will
    not terminate the file.  This may possibly be useful.

#prolog.
    A good way of providing help for a Prolog program is to include the help
    information in the source file.  This is quite a help to someone reading
    the program, as well as to someone running it.  The suggested "standard
    header" is as follows (where @ is the Delimiter):

	/*  File: Dev:FooBaz.Pl   Author: Bickerstaff   Updated: 30 Feb 82

	@purpose.
		<describe what the program is for>
	@source.
		<where the program lives and why>
	@needs.
		<the files predicates &c the program needs>
	@predicates.
		<the predicates defined by the program>
	@commands.
		<the user-level commands supplied by the program>
	@database.
		<the facts entered/needed in the data base and what they mean>
	@predicate-1. ...
	@command-1. ...
	@database-1. ...
		<help about specific items>
	@end_of_file.

	@fixes.		%  this isn't real help information
	[31 Nov 81	Johnson]		
		<description of a correction/update> ...

	------------------------------------------------------------*/
	:- op ...
	:- public ...
	:- mode ...

#try_hard_to_see.
    try_hard_to_see(FileName, DeviceDefaults, ExtensionDefaults) takes
	- a string or atom FileName, being intnded as the name of a file
	- a possibly empty list of atoms or strings DeviceDefaults, being
	  the device names to be tried if the FileName contains no device
	  (note that they should NOT have a colon at the end)
	- a possibly empty list of atoms or strings ExtensionDefaults,
	being the extensions to be tried if the FileName contains no
	extension (note that they should NOT start with a period).
    It backtracks through all combinations of the file name as given and the
    defaults (where applicable), and succeeds as soon as it manages to 'see'
    the file.  Once it has succeeded, it will not backtrack any further.
    For example, Helper calls
	try_hard_to_see(HelpFile, [mec,util,pll], [hlp,txt])
    to open a Help File.
EOF1HELPER.HLP             00010049000100 85045 99364 000011Unix V7             HDR1HELPER.PL              00010050000100 85045 99364 000000Unix V7             %   File   : HELPER.PL
%   Author : R.A.O'Keefe
%   Updated: 16 December 1983
%   Purpose: Print extracts from Help files.
%   Needs  : try_hard_to_see/4 from Util:TrySee.Pl

%   give_help(Area, Topic)
%	-- looks for an assertion help_file(Area, FileName, Delimiter)
%	-- which you must supply.  It then tries hard to open the file
%	-- with default extensions "", "HLP", and "PL" and with device
%	-- defaults "DSK", "MEC", "UTIL", and "PLL".  If the file can't
%	-- be found, or if there is no help_file assertion, it gives an
%	-- apology.  Otherwise it searches the file for the sequence
%	-- Delimiter Topic ".", e.g. #help.


:- public
	give_help/0,				% ->
	give_help/1,				% Area ->
	give_help/2.				% Area x Topic ->

:- mode
	give_help,				%  list of areas.
	give_help(+),				%  list of topics in an area.
	give_help(+, +),			%  help on a specific topic.
	    find_help(+, +),			%  find and type a topic of list
		read_after_delimiter(+),	%  find "#" or return end_of_file
		find_help(+, +, +),	    	%  check a list of topics
		    topic_is_among(+, +),	%  member on commas instead of dots
		    type_until_delimiter(+).	%  display body of a Note.



give_help :-
	write('Help is available in the following areas:'), nl,
	help_file(Area, _, _),
	tab(4), writeq(Area), nl,
	fail.
give_help :-
	write('Call give_help(Area) for a list of topics in an Area.'), nl,
	write('Call give_help('), write('Area'),
	write(',Topic) for help about a specific topic.'), nl.


give_help(Area) :-
	write('The topics in '), writeq(Area),
	write(' for which help is available are:'), nl,
	give_help(Area, Topic),
	write('Call give_help('), writeq(Area),
	write(',Topic) for help about a specific topic.'), nl.


give_help(Area, Topic) :-
	(   help_file(Area, HelpName, Delimiter),
		call(Delim is Delimiter)
	;   atom(Area),
		Delim is "#", HelpName = Area
	),
	(   try_hard_to_see(HelpName, [mec,util,pll], [hlp,pl], HelpFile), !,
		seeing(Old),
		see(HelpFile),
		find_help(Delim, Topic),
		seen,
		see(Old)
	;    write('** No help is available on '), writeq(Topic),
		write(' in '), writeq(Area), nl
	),  !.


	find_help(Delimiter, Topic) :-
		var(Topic), !,
		read_after_delimiter(Delimiter, Topics),
		(   Topics = end_of_file
		;   tab(4), write(Topics), nl, fail
		).
	find_help(Delimiter, Topic) :-
		read_after_delimiter(Delimiter, Topics),
		find_help(Topics, Topic, Delimiter).

		find_help(end_of_file, Topic, _) :- !,
			seeing(HelpFile),
			write('** No help is available on '), writeq(Topic),
			write(' in file '), writeq(HelpFile), nl.
		find_help(Topics, Topic, Delimiter) :-
			topic_is_among(Topic, Topics), !,
			type_until_delimiter(Delimiter).


		topic_is_among(Topic, (Topics1,Topics2)) :- !,
			(   topic_is_among(Topic, Topics1)
			;   topic_is_among(Topic, Topics2)
			).
		topic_is_among(Topic, (Topics1;Topics2)) :- !,
			(   topic_is_among(Topic, Topics1)
			;   topic_is_among(Topic, Topics2)
			).
		topic_is_among(Topic, Topic).


		read_after_delimiter(Delimiter, Topics) :-
			repeat,
				get0(Character),
				(   Character = 26, !, Topics = end_of_file
				;   Character = Delimiter, read(Topics)
				).


		type_until_delimiter(Delimiter) :-
			get0(C),
			C =\= 26, C =\= Delimiter, 
			put(C), !,
			type_until_delimiter(Delimiter).
		type_until_delimiter(Delimiter).
	

EOF1HELPER.PL              00010050000100 85045 99364 000007Unix V7             HDR1IDBACK.DEF             00010051000100 85045 99364 000000Unix V7             %   File   : IDBACK.DEF
%   Author : R.A.O'Keefe
%   Updated: 8 October 1984
%   Purpose: "unit" interface clauses for IDBACK.PL
%   Compile this file if and only if you compile your other
%   unit/3 clauses.  I've swapped the arguments around from
%   the way Pereira & Porto had them, for efficiency.

unit((V1-T1)  <  (V2-T2), V1  <  V2, [T1,T2]).
unit((V1-T1)  >  (V2-T2), V1  >  V2, [T1,T2]).
unit((V1-T1) =:= (V2-T2), V1 =:= V2, [T1,T2]).
unit((V1-T1) =\= (V2-T2), V1 =\= V2, [T1,T2]).
unit((V1-T1)  >= (V2-T2), V1  >= V2, [T1,T2]).
unit((V1-T1) =<  (V2-T2), V1 =<  V2, [T1,T2]).

unit((V1-T1) @<  (V2-T2), V1 @<  V2, [T1,T2]).
unit((V1-T1) @>  (V2-T2), V1 @>  V2, [T1,T2]).
unit((V1-T1)  == (V2-T2), V1  == V2, [T1,T2]).
unit((V1-T1) \== (V2-T2), V1 \== V2, [T1,T2]).
unit((V1-T1) @>= (V2-T2), V1 @>= V2, [T1,T2]).
unit((V1-T1) @=< (V2-T2), V1 @=< V2, [T1,T2]).

unit(nonvar(V1-T1),   nonvar(V1),   [T1]).
unit(var(V1-T1),      var(V1),      [T1]).
unit(atomic(V1-T1),   atomic(V1),   [T1]).
unit(atom(V1-T1),     atom(V1),     [T1]).
unit(integer(V1-T1),  integer(V1),  [T1]).
unit(compound(V1-T1), compound(V1), [T1]).

unit(writeq(V1-T1),   writeq(V1),   [T1]).
unit(tab(V1-T1),      tab(V1),      [T1]).
unit(write(V1-T1),    write(V1),    [T1]).
unit(print(V1-T1),    print(V1),    [T1]).
unit(display(V1-T1),  display(V1),  [T1]).
unit(nl,              nl,           []).

EOF1IDBACK.DEF             00010051000100 85045 99364 000003Unix V7             HDR1IDBACK.PL              00010052000100 85045 99364 000000Unix V7             %   File   : IDBACK.PL
%   Authors: Luis Moniz Pereira & Antonio Porto.
%   Updated: 8 October 1984
%   Purpose: Intelligent Backtracking DataBase interpreter.

/*  This code comes from the paper
	An Interpreter of Logic Programs Using Selective Backtracking; 
	Luis Moniz Pereira & Antonio Porto,
	Lisbon University report 3/80 CIUNL July 1980
	presented at the Debrecen Logic Programming Workshop 1980.
    That paper presents two interpreters.  One is for full Prolog
    complete with cuts and so on.  This is the one from Appendix 2,
    which is specialised for "data base" working.

    The requirements are these:
    [1] There must be no cuts in the data base or in the question.
    [2] The heads of non-unit clauses may only contain variables.
	Non-unit clauses are stored in a special way.  Head :- Body
	is stored as non_unit(Head, Body).  Read these as definitions
	of "virtual relations".
    [3] Unit clauses and the question contain only variables or ground
	terms as arguments.  In fact, regard this whole thing as a way
	of handling function-free code.  Unit clauses are stored as
	ordinary Prolog, but for each such relation a special linkage
	clause is needed.  For predicate p/n, this clause has the form
	unit(p(A1-N1,...,An-Nn), p(A1,...,An),  [N1,...,Nn]).
	Such a linkage clause should also be provided for such Prolog
	system predicates as are used; see IDBACK.DEF for a suitable
	set to start with.  These predicates should not bind any
	variables.  So <, atom, and so on are ok, but append isn't.
    [4] There are no multiple occurrences of variables in the head of
	any clause, unless all identical occurrences will match a
	ground term.
    [5] Unit clauses are assumed to come before non-unit clauses for
	the same predicate.  Indeed, it would be better if predicates
	were all unit clauses or all non-unit clauses.
    [6] Backtracking into the database query may only be used for
	finding alternative solutions,  not with the intention of
	exhaustively exploring a subspace (e.g. for certain types of
	side-effects).  {I'm not sure what this means.}

    Using the interpreter.
    You can leave your unit clauses as they stand.  Non-unit clauses
    will have to be converted to non_unit/2 clauses.  (non_unit is a
    heterological predicate (:-).)   Instead of call(Q),  use
    data_base_call(Q).  Q may be a simple goal, or a conjunction or
    a disjunction.
*/

:- public
	data_base_call/1.

:- mode
	data_base_query(+),
	    execute_query(+, +, -, +),
	    new_query(+, -, ?),
		new_query(+, +, +, ?), 
		    query_var(+, +, -),
	    no_backtrack_goal_until(+),
	    not_a_backtrack_goal(+),
	    old_query(+, +),
		old_query(+, +, +),
	    select(?),
	    select_goals(+),
	    select_all_modifying_goals_for(+),
	    set_goal_tags(+, +).

/* data
	backtrack_goal(integer).	*/
	%  backtrack_goal(N) can only exist while goal N is somewhere
	%  on the stack, and means that there may be some point in
	%  looking for alternative solutions to that goal.


data_base_call(Query) :-
	new_query(Query, Tagged, Vars),
	abolish(backtrack_goal, 1),
	execute_query(Tagged, 1, _, 0),
	(   old_query(Query, Tagged)
	;   abolish(backtrack_goal, 1),
	    select_all_modifying_goals_for(Vars),
	    fail		%   look for another solution.
	).


%   In this version of new_query, I have combined Pereira & Porto's
%   "copy" and "new_query" predicates.  For queries with few variables
%   (almost any reasonable query), this is much more efficient than
%   putting things into the data base and pulling them out again.
%   The point of this is to make of copy of the query with two changes:
%   all the variables are replaced by new ones, and all the ground
%   arguments are tagged with "t".
%   I've also combined it, in effect, with select_all_modifying_goals_for,
%   so that we can find all the tags of the query at once with no search
%   and no repetition.

new_query((Old1,Old2), (New1,New2), Vars) :- !,
	new_query(Old1, New1, Vars),
	new_query(Old2, New2, Vars).
new_query((Old1;Old2), (New1;New2), Vars) :- !,
	new_query(Old1, New1, Vars),
	new_query(Old2, New2, Vars).
new_query(Old, New, Vars) :-
	functor(Old, F, N),
	functor(New, F, N),
	new_query(N, Old, New, Vars).

new_query(0, _, _, _) :- !.
new_query(N, Old, New, Vars) :-
	M is N-1,
	arg(N, Old, OldArg),
	(   var(OldArg), query_var(Vars, OldArg, NewArg)
	;   NewArg = OldArg-t
	), 
	arg(N, New, NewArg),
	!,
	new_query(M, Old, New, Vars).

query_var(End, Old, New) :-
	var(End), !,
	End = l(Old,New,_).
query_var(l(Var,New,_), Old, New) :-
	Var == Old, !.
query_var(l(_,_,Vars), Old, New) :-
	query_var(Vars, Old, New).


%   old_query takes the instantiated form of the tagged query and unifies
%   it with the original query, stripping the tags as it goes.  There is
%   no need to refer back to the Vars table constructed by new_query as
%   we are unifying with an existing term, not building a new copy.

old_query((Old1,Old2), (New1,New2)) :- !,
	old_query(Old1, New1),
	old_query(Old2, New2).
old_query((Old1;Old2), (New1;New2)) :- !,
	old_query(Old1, New1),
	old_query(Old2, New2).
old_query(Old, New) :-
	functor(Old, F, N),
	functor(New, F, N),
	old_query(N, Old, New).

old_query(0, _, _) :- !.
old_query(N, Old, New) :-
	M is N-1,
	arg(N, New, Arg-_),
	arg(N, Old, Arg), !,
	old_query(M, Old, New).



execute_query((G1,G2), N1, Nn, Parent) :- !,
	execute_query(G1, N1, Nk, Parent),
	(   execute_query(G2, Nk, Nn, Parent)
	;   no_backtrack_goal_until(N1), !, fail
	).
execute_query((G1;G2), N1, Nn, Parent) :- !,
	(   execute_query(G1, N1, Nn, Parent)
	;   execute_query(G2, N1, Nn, Parent)
	).
execute_query(G, N1, Nn, Parent) :-
	unit(G, Goal, Tags),
	!,
	(   call(Goal),
	    (   set_goal_tags(Tags, N1), Nn is N1+1
	    ;   not_a_backtrack_goal(N1), !, fail
	    )
	;   select(Parent), select_goals(Tags), !, fail
	).
execute_query(G, N1, Nn, Parent) :-
	(   non_unit(G, Body),
	    N2 is N1+1, 
	    (   execute_query(Body, N2, Nn, N1)
	    ;   not_a_backtrack_goal(N1), !, fail
	    )
	;   select(Parent), !, fail
	).


set_goal_tags([Tag|Tags], Tag) :- !,
	set_goal_tags(Tags, Tag).
set_goal_tags([_|Tags], Tag) :- !,
	set_goal_tags(Tags, Tag).
set_goal_tags([], _).


%   Goal selection.
%   In the original code this section was devoid of cuts.  The purity
%   was more apparent than real, however, as select or select_goals
%   or whatever was always followed by a cut, or possibly cut,fail.

select(t) :- !.
select(0) :- !.
select(Tag) :-
	backtrack_goal(Tag),
	!.			%   This one has already been selected.
select(Tag) :-
	asserta(backtrack_goal(Tag)).


select_goals([]) :- !.
select_goals([Tag|Tags]) :-
	select(Tag),
	select_goals(Tags).


select_all_modifying_goals_for(End) :-
	var(End), !.
select_all_modifying_goals_for(l(_,_-Tag,Vars)) :-
	select(Tag),
	select_all_modifying_goals_for(Vars).


%   Backtracking control.

no_backtrack_goal_until(N) :-
	%   \+ (backtrack_goal(X), X >= N)
	backtrack_goal(X), X >= N, !, fail ; true.


not_a_backtrack_goal(N) :-
	%   \+ retract(backtrack_goal(N))
	retract(backtrack_goal(N)), !, fail ; true.

 EOF1IDBACK.PL              00010052000100 85045 99364 000014Unix V7             HDR1IMISCE.PL              00010053000100 85045 99364 000000Unix V7             %   File   : IMISCE.PL
%   Author : Lawrence Byrd, L. Hardman
%   Updated: 4 April 1984
%   Purpose: Miscellaneous routines (interpreted)

%   The contents of this module should be redistributed.  The only thing
%   which has to be interpreted rather than compiled is subgoal, and that
%   is incomplete and obsolete.  gcc needs once from INVOCA.PL.

:- public
	(\=)/2,
	casserta/1,
	cassertz/1,
	clean/0,
	continue/0.


continue.			%  This is one of the actions for error/3


\=(X, X) :- !,
	fail.
\=(X, Y) :- !.


casserta(X) :-
	clause(X, true),
	!.
casserta(X) :-
	asserta(X).


cassertz(X) :-
	clause(X, true),
	!.
cassertz(X) :-
	assertz(X).


clean :-
	nolog,
	seeing(OldInput),
	see('prolog.log'),
	rename('prolog.log', []),
	seen,
	see(OldInput),
	log.


diff(X, X) :- !,
	fail.
diff(X, Y).


gcc(Goal) :-
	once(Goal),
	asserta('$gcc'(Goal)),
	fail.
gcc(Goal) :-
	retract('$gcc'(Answer)),
	!,		%  This cut is needed for nested 
	Goal = Answer.


l(X) :- listing(X).			% Make listing easier.


subgoal(exact, L) :-
	\+ \+ (numbervars(L, 0, _), subgoal_of(L) ).
 EOF1IMISCE.PL              00010053000100 85045 99364 000003Unix V7             HDR1INVOCA.PL              00010054000100 85045 99364 000000Unix V7             %   File   : INVOCA.PL
%   Author : Lawrence
%   Updated: 20 July 1983
%   Purpose: Fancy control structures ("invocation" routines).


%   Most of these predicates are best forgotten.
%   The exceptions are &/2, forall/2, once/1, not/1.

	%%%  Run this module interpreted
	%%%  INVOCA requires no other modules

:- public			% /--- begin junk
	(\\)/2,			% |    a\\b = once((a;b))
	nobt/1,			% |    nobt(X) = once(X)
	(thnot)/1,		% |    thnot(X) = not(X) = \+ X
	any/1,			% |    any([G1,...,Gn]) = G1;...;Gn
	binding/2,		% |    nobody knows
	for/2,			% |    for(N,G) = G,...,G (with N Gs)
	findall/3,		% \--- end junk
	(&)/2,
	forall/2,
	once/1,
	(not)/1.

&(A, B) :-
	call(A),
	call(B).

		

\\(A, B) :-
	(call(A) ; call(B)), !.



any([]) :- !,
	fail.			%   catch lists with unbound tails
any([Goal|Goals]) :-
	call(Goal).
any([_|Goals]) :-
	any(Goals).



binding(N, Goal) :-
	asserta('$bind'(N)),
	N > 0,
	call(Goal),
	'$retr'(N2),
	N3 is N2-1,
	(   N3 =< 0
	;   asserta('$bind'(N3)), fail
	),  !.
binding(_, _) :-
	'$retr'(_),
	fail.


'$retr'(N) :-
	retract('$bind'(N)), !.



			% Findall Xs such that P.  This is a funny version
			%  which finds the FIRST solution that bagof would
			%  find, but returns the empty list if there are no
			%  solutions. (Logically dubious.)  A better version
			%  of this is available elsewhere.

findall(X, P, List) :-
	bagof(X, P, List), !.
findall(X, P, []).



for(0, Goal) :- !.
for(N, Goal) :-
	N > 0,
	call(Goal),
	M is N-1,
	for(M, Goal),
	!.			%   this cuts Goal as well



forall(Generator, Test) :-
	Generator,
	\+ Test,
	!, fail.
forall(_, _).



once(Goal) :-
	call(Goal), !.


nobt(Goal) :-
	call(Goal), !.



not(Goal) :- call(Goal), !, fail.
not(Goal).



thnot(Goal) :- call(Goal), !, fail.
thnot(Goal).

 EOF1INVOCA.PL              00010054000100 85045 99364 000004Unix V7             HDR1IXREF.DEF              00010055000100 85045 99364 000000Unix V7             %   File   : IXREF.DEF
%   Author : Richard A. O'Keefe.
%   Updated: 14 August 1984
%   Purpose: XREF definitions for IXREF (part of PP)


system([_|_]).
system(abolish(_,_)).
system(revive(_,_)).
system(incore(_)).
system(asserta(_,_)).
system(asserta(_)).
system(assertz(_,_)).
system(assertz(_)).
system(retract(_)).
system(clause(_,_,_)).
system(clause(_,_)).
system(recorda(_,_,_)).
system(recordz(_,_,_)).
system(recorded(_,_,_)).
system(instance(_,_)).
system(erase(_)).
system(true).
system(length(_,_)).
system(name(_,_)).
system(op(_,_,_)).
system(var(_)).
system(atom(_)).
system(!).
system(statistics).
system(statistics(_,_)).
system(functor(_,_,_)).
system((A,B)).			%  , ; and \+
	applies((A,B), A).	%  have to be defined for when
	applies((A,B), B).	%  they are "call"ed.
system((A;B)).
	applies((A;B), A).
	applies((A;B), B).
system(\+(A)).
	applies(\+(A), A).
system(call(_)).
	applies(call(Goal), Goal).
system(expand_term(_,_)).
system(debug).
system(debugging).
system(display(_)).
system(get(_)).
system(get0(_)).
system(leash(_)).
system(nl).
system(nodebug).
system(print(_)).
system(put(_)).
system(skip(_)).
system(tab(_)).
system(trace).
system(ttyflush).
system(ttyget(_)).
system(ttyget0(_)).
system(ttynl).
system(ttyput(_)).
system(ttyskip(_)).
system(ttytab(_)).
system(write(_)).
system(writeq(_)).
system(ancestors(_)).
system(depth(_)).
system(maxdepth(_)).
system(subgoal_of(_)).
system(abort).
system(arg(_,_,_)).
system(assert(_)).
system(atomic(_)).
system(bagof(_,_,_)).
	applies(bagof(Vars,Test,Ans), Test).
system(break).
system(close(_)).
system(compare(_,_,_)).
system(compile(_)).
system(consult(_)).
system(current_atom(_)).
system(current_functor(_,_)).
system(current_predicate(_,_)).
system(current_op(_,_,_)).
system(fail).
system(fileerrors).
system(gc).
system(gcguide(_)).
system(halt).
system(integer(_)).
system(keysort(_,_)).
system(listing).
system(listing(_)).
system(log).
system(nofileerrors).
system(nogc).
system(nolog).
system(nonvar(_)).
system(numbervars(_,_,_)).
system(phrase(_,_)).
system(prompt(_,_)).
system(read(_)).
system(reconsult(_)).
system(rename(_,_)).
system(repeat).
system(restore(_)).
system(save(_)).
system(see(_)).
system(seeing(_)).
system(seen).
system(setof(_,_,_)).
	applies(setof(Vars,Test,Ans), Test).
system(sort(_,_)).
system(tell(_)).
system(telling(_)).
system(told).
system(trimcore).
system(plsys(_)).
system('LC').
system('NOLC').
system(spy _).
system(nospy _).
system(\+_).
	applies(\+ Goal, Goal).
system((If->Then)).
	applies((If->Then), If).
	applies((If->Then), Then).
system(_=_).
system(_ is _).
system(_==_).
system(_\==_).
system(_=.._).
system(_<_).
system(_>_).
system(_=<_).
system(_>=_).
system(_@<_).
system(_@=<_).
system(_@>=_).
system(_@>_).
system(_^_).
system(_=\=_).
system(_=:=_).

%	From here on belong to UTIL.

% operators from UTIL.OPS

op(1100, xfy, (\\)).
op( 950, xfy, #).
op( 850, xfy, &).
op( 710,  fy, [not,thnot]).
op( 700, xfx, \=).

op( 300,  fx, edit).
op( 300,  fx, redo).
op( 300,  fx, tlim).
op( 300,  fx, ton).
op( 300,  fx, toff).

% operators from ARITH.OPS

op( 500, yfx, [++,--]).
op( 400, yfx, [div,mod]).
op( 300, xfy, [:,^]).


% UTIL procedures

known(     &(Goal1,Goal2),					utility ).
  applies( &(Goal1,Goal2), Goal1 ).
  applies( &(Goal1,Goal2), Goal2 ).
known(     \=(X,Y),						utility ).
known(     \\(Goal1,Goal2),					utility ).
  applies( \\(Goal1,Goal2), Goal1 ).
  applies( \\(Goal1,Goal2), Goal2 ).
known(     add_element(Elem,S1,S2),				utility	).
known(     any(Goallist),					utility ).
  % Hairy applies...
known(     append(File),					utility ).
known(     append(List1,List2,List3),				utility ).
known(     apply(Pred,Args),					utility ).
  % Hairy applies...
known(     binding(N,Goal),					utility ).
  applies( binding(N,Goal), Goal ).
known(     callable(Term),					utility	).
known(     casserta(X),						utility ).
known(     cassertz(X),						utility ).
known(     cgensym(Prefix,PossVar),				utility ).
known(     check_exists(File),					utility ).
known(     checkand(Pred,Conj),					utility ).
  applies( checkand(Pred,Conj), Pred+1 ).
known(     checklist(Pred,List),				utility ).
  applies( checklist(Pred,List), Pred+1 ).
known(     clean,						utility ).
known(     close(File,Old),					utility ).
known(     concat(Atom1,Atom2,Atom3),				utility ).
known(     contains(Kernel,Expr),				utility	).
known(     continue,						utility ).
known(     convlist(Pred,List1,List2),				utility ).
  applies( convlist(Pred,List1,List2), Pred+2 ).
known(     correspond(X,Xlist,Y,Ylist),				utility	).
known(     del_element(Elem,S1,S2),				utility	).
known(     delete(File),					utility ).
known(     delete(List,X,Rest),					utility	).
known(     diff(X,Y),						utility ).
known(     disjoint(Sets),					utility ).
known(     disjoint(S1,S2),					utility	).
known(     edit(File),						utility ).
known(     error(Format,List,Action),				utility ).
  applies( error(Format,List,Action), Action ).
known(	   eval(Command),					utility ).
known(	   eval(Expr,Ans),					utility ).
known(     file_exists(File),					utility ).
known(     findall(Var,Goal,List),				utility ).
  applies( findall(Var,Goal,List), Goal ).
known(     flag(Flag,Old,New),					utility ).
known(     for(N,Goal),						utility ).
  applies( for(N,Goal), Goal ).
known(     forall(Goal1,Goal2),					utility ).
  applies( forall(Goal1,Goal2), Goal1 ).
  applies( forall(Goal1,Goal2), Goal2 ).
known(     freeof(Kernel,Expr),					utility	).
known(     fwritef(File,Format),				utility	).
known(     fwritef(File,Format,List),				utility	).
known(     gcc(Goal),						utility ).
  applies( gcc(Goal), Goal ).
known(     gensym(Prefix,Var),					utility ).
known(     intersect(S1,S2),					utility	).
known(     intersect(Set1,Set2,ISet),				utility ).
known(     keys_and_values(Pairs,Keys,Values),			utility	).
known(     last(Element,List),					utility ).
known(     listtoset(List,Set),					utility ).
known(     mapand(Pred,Conj1,Conj2),				utility ).
  applies( mapand(Pred,Conj1,Conj2), Pred+2 ).
known(     maplist(Pred,List1,List2),				utility ).
  applies( maplist(Pred,List1,List2), Pred+2 ).
known(     member(Element,Set),					utility ).
known(     memberchk(Element,Set),				utility ).
known(     mlmaplist(Pred,Lists),				utility ).
  applies( mlmaplist(Pred,Lists), Pred+1 ).
known(     mlmaplist(Pred,Lists,Vin,Vout),			utility ).
  applies( mlmaplist(Pred,Lists,Vin,Vout), Pred+3 ).
known(     mlmaplist(Pred,Lists,V),				utility ).
  applies( mlmaplist(Pred,Lists,V), Pred+2 ).
known(     mlmember(Elements,Lists),				utility ).
known(     mlselect(Elements,Lists,Rests),			utility ).
known(     modify(OldPat,Xform,NewPat),				utility ).
  applies( modify(OldPat,Xform,NewPat), OldPat ).
  applies( modify(OldPat,Xform,NewPat), Xform  ).
known(     nextto(X,Y,List),					utility ).
known(     nmember(Element,Set,N),				utility ).
known(     nobt(Goal),						utility ).
  applies( nobt(Goal), Goal ).
known(     nonmember(Elem,Set),					utility	).
known(     not(Goal),						utility ).
  applies( not(Goal), Goal ).
known(     number(N),						utility ).
known(     numlist(L,U,List),					utility ).
known(     occ(X,Term,N),					utility ).
known(     open(File),						utility ).
known(     open(Old,File),					utility ).
known(     pairfrom(List,A,B,Rest),				utility ).
known(     patharg(Path,Expr,Arg),				utility	).
known(     perm(List1,List2),					utility ).
known(     perm2(X,Y,A,B),					utility ).
known(     portray_number(N),					utility ).
known(     position(Term,Expr,Path),				utility	).
known(     prconj(Conj),					utility ).
known(     prexpr(Expr),					utility ).
known(     prlist(List),					utility ).
known(     project(List1,N,List2),				utility	).
known(     read_in(Sentence),					utility ).
known(     redo(File),						utility ).
known(     remove_dups(List,Set),				utility ).
known(     replace(Path,Expr1,Arg,Expr2),			utility	).
known(     rev(List1,List2),					utility ).
known(     reverse(List,Rev),					utility	).
known(     same_length(List1,List2),				utility	).
known(     select(Element,List,Rest),				utility ).
known(     seteq(Set1,Set2),					utility ).
known(     shorter_list(Short,Long),				utility	).
known(     some(Pred,List),					utility ).
  applies( some(Pred,List), Pred+1 ).
known(     somechk(Pred,List),					utility ).
  applies( somechk(Pred,List), Pred+1 ).
known(     subgoal(Exact,Goal),					utility ).
known(     sublist(Pred,List1,List2),				utility ).
  applies( sublist(Pred,List1,List2), Pred+1 ).
known(     subseq(List,List1,List2),				utility	).
known(     subseq0(List1,List2),				utility	).
known(     subseq1(List1,List2),				utility	).
known(     subset(Subset,Superset),				utility ).
known(     subst(Substitution,Old,New),				utility ).
known(     subtract(Set1,Set2,Subset),				utility ).
known(     sumlist(NumList,Sum),				utility ).
known(     symdiff(Set1,Set2,Diff),				utility	).
known(     thnot(Goal),						utility ).
  applies( thnot(Goal), Goal ).
known(	   tidy(Expr,TidiedExpr),				utility ).
known(	   tidy_withvars(Expr,TidiedExpr),			utility ).
known(     tlim(Tlimit),					utility ).
known(     ton(Name),						utility ).
known(     toff,						utility ).
known(     toff(Name),						utility ).
known(     trace(Format,Condition),				utility ).
known(     trace(Format,List,Condition),			utility ).
known(     ttyprint(X),						utility ).
known(     union(Set1,Set2,USet),				utility ).
known(     update(Template,Generator),				utility ).
  applies( update(Template,Generator), Template  ).
  applies( update(Template,Generator), Generator ).
known(     variables(Term,VarSet),				utility ).
known(     writef(Format),					utility ).
known(     writef(Format,List),					utility ).
EOF1IXREF.DEF              00010055000100 85045 99364 000020Unix V7             HDR1IXREF.HLP              00010056000100 85045 99364 000000Unix V7             File: Mec:Ixref.Hlp	Author: R.A.O'Keefe	Updated: 10 May 1983

#purpose.
ixref is an interactive cross-reference utility.  You can use it to
tell you whether you are redefining any system predicates, to find
out which predicates are used but not defined, to discover which
predicates call what other predicates, and so on.

#sources.
The Prolog interactive Cross-reference program no longer exists as
a separate entity.  Instead, there are three programs
	mec:helper.pl		-- general help utility
	mec:pp.pl		-- pretty-printer (needs helper)
	mec:ixref.pl		-- needs pp and helper

#commands.
ixref is part of ToolKit.  It extends the TermPatterns of pp.pl
(see Mec:PP.Hlp, or give_help(pp) for details) with patterns

	from(File)		-- predicates defined in File
	from(-)			-- predicates from nowhere (undefined)
	>(Patt)			-- calling Patt
	<(Patt)			-- called by Patt
	@>(Patt)		-- calling Patt, even indirectly
	@<(Patt)		-- called by Patt, even indirectly.
E.g. if p :- q.  q :- r. then p > q > r and p @> r, but not p > r.
Ixref also provides two new commands:
	sf			-- show file names ixreffed
	sf "StrPatt"		-- show selected file names
	sf TermPatt		-- show files defining any Patt
	sp From-To		-- show call paths between From@>To.
and of course
	ixref(Files)		-- parse files & collect call/define info

#database.
The data extracted by ixref(Files) consists of
	$seen(File)		-- File has been ixreffed
	$seen(Fn,Ar)		-- Fn/Ar has been defined OR used
	$defn(Fn,Ar,File)	-- Fn/Ar is defined in File
	$call(Goal,Arg,Inc)	-- same as XREF's applies/2.
	$call(F,A, G,B)		-- F/A calls G/B

#misc.
The storage required is about 2/3 of the storage used by the original
clauses, e.g. PRESS needs about 35k.
	Mec:ToolKit.Exe
is a version of UTIL with Helper, Pp, Ixref, and Vcheck compiled.
It can be used as an interactive cross-referencer.

#end_of_file.
EOF1IXREF.HLP              00010056000100 85045 99364 000004Unix V7             HDR1IXREF.PL               00010057000100 85045 99364 000000Unix V7             %   File   : IXREF.PL
%   Author : R.A.O'Keefe
%   Updated: 28 May 1984
%   Purpose: Interactive Cross-Reference module for PP.

%   Note: this program is completely parasitic on PP.Pl.  It only provides
%   a way of building the database and a few methods for accessing it; the
%   general pattern-matching stuff comes from PP, and the access method is
%   called from it.  It also depends on Helper for try_hard_to_see.

:- public
	ixref/1,	%  inspect some files
	ixref_path/3,	%  for 'setof'
	(ct)/0, (ct)/1,	%  calling tree
	(sf)/0, (sf)/1, (sf)/2,
	(sp)/1, (sp)/2.	%  show paths

:- mode
    ixref(+),		%  read a list of files and update the database
	get_from(+,+),
	    start_ixref(+,+),
	    ixref_process(+,+,+),
		ixref_command(+),
		    ixref_declaration(+),
		ixref_head(+,+,+,-,-),
		ixref_goal(+,-,-),
	ixref_Current(+, -, -),
	ixref_Pattern(+),
    cassert(+),		%  put something in DataBase if not already there
    ct, ct(+),
	ct(+,+,+),
	    ct(+,+,+,+,-,+,-),
		ct_prefix(+,-,+,+,+),
		ct(+,+,+,-,+,-),
    sf, sf(+), sf(+,-),
    sp(+), sp(+,-),
	ixref_path(+, +, -),
	    ixref_path(+, ?, -, +),
		memberchk(+, +).
    
:-	op(900,  fx, [ct,sf,sp]).


ixref(Files) :-
	nofileerrors,
	(   call('$seen'('util:ixref.def'))
	;   get_from('util:ixref.def', +)
	),  !,
	get_from(Files, +),
	fileerrors.


get_from([Head|Tail], Flag) :-
	!, get_from(Head, Flag),
	!, get_from(Tail, Flag).
get_from([],	      Flag) :- !.
get_from(erase(File), Flag) :- !,
	start_ixref(File, erase).
get_from(-File,       Flag) :- !,
	get_from(File, -).
get_from(File, Flag) :-
	seeing(OldFile),
	try_hard_to_see(File, [press,extras,mec,util,pll], [pl,def]),
	seeing(NewFile),
	start_ixref(NewFile, Flag),
	repeat,
	    read(Term),
	    expand_term(Term, Form),
	    ixref_process(Form, NewFile, Flag),
	    Form = end_of_file,
	!,
	seen,
	see(OldFile).


	start_ixref(File, Flag) :-
		retract('$seen'(File)),			%   File has been seen before
		retract('$defn'(Fn,Ar, File)),		%   Fn/Ar is defined in File
		retract('$call'(Fn,Ar, _,_)),		%   forget all its calls
		retract('$call'(Fn,Ar, _)),		%   forget what it applies
		fail.					%   failure-driven LOOP
	start_ixref(File, erase) :- !.
	start_ixref(File, Flag) :-
		assertz('$seen'(File)).


	ixref_process(end_of_file,    File, Flag) :- !.
	ixref_process((Head :- Body), File, Flag) :- !,
		ixref_head(Head, File, Flag, HeadFn,HeadAr),
		ixref_goal(Body, GoalFn,GoalAr),
		cassert('$seen'(GoalFn,GoalAr)),
		cassert('$call'(HeadFn,HeadAr, GoalFn,GoalAr)).
	ixref_process((:- Commands),  File, Flag) :- !,
		ixref_command(Commands).
	ixref_process((?- Question),  File, Flag) :- !.
	ixref_process(system(Head),	File, Flag) :- !,
		ixref_head(Head, utility, Flag, HeadFn,HeadAr).
	ixref_process(known(Head, F), File, Flag) :- !,
		ixref_head(Head, F, Flag, HeadFn,HeadAr).
	ixref_process(op(P, T, O),	File, Flag) :- !,
		op(P, T, O).
	ixref_process(applies(G, A),	File, Flag) :-
		var(A), !,
		cassert('$call'(G, A, 0)).
	ixref_process(applies(G, A+N),File, Flag) :- !.
		cassert('$call'(G, A, N)).
	ixref_process(Fact,	 	File, Flag) :- !,
		ixref_head(Fact, File, Flag, HeadFn,HeadAr).


	ixref_command((A,B)) :-
		ixref_command(A), !,
		ixref_command(B).
	ixref_command(op(P, T, O)) :- !,
		op(P, T, O).
	ixref_command([X|Y]) :- !,
		get_from([X|Y], +).
	ixref_command(consult(Files)) :- !,
		get_from(Files, +).
	ixref_command(reconsult(Files)) :- !,
		get_from(Files, -).
	ixref_command(compile(Files)) :- !,
		get_from(Files, -).
	ixref_command((public Public)) :- !,
		ixref_declaration(Public).
	ixref_command((mode Mode)) :- !,
		ixref_declaration(Mode).
	ixref_command(_).

	%   handle :- public and :- mode declarations.  The information
	%   should be stored somewhere for the sake of MEDIC, but until
	%   all these tools are properly fitted together it doesn't matter.

		ixref_declaration((A,B)) :-
			ixref_declaration(A), !,
			ixref_declaration(B).
		ixref_declaration(Functor/Arity) :- !,
			cassert('$seen'(Functor, Arity)).
		ixref_declaration(Term) :-
			functor(Term, Functor, Arity),
			cassert('$seen'(Functor, Arity)).


	ixref_head(Head, File, Flag, Functor, Arity) :-
		functor(Head, Functor, Arity),
		call('$defn'(Functor, Arity, File)), !.
	ixref_head(Head, File, Flag, Functor, Arity) :-
		functor(Head, Functor, Arity),
		(   call('$defn'(Functor, Arity, OtherFile)),
			OtherFile \== File,
			(Flag == - ; OtherFile = utility),
			display('** '), display(File),
			display(' redefines '), display(Functor),
			display(/), display(Arity),
			display(' which belongs to '),
			display(OtherFile), ttynl
		;   true
		),  !,
		cassert('$seen'(Functor, Arity)),
		cassert('$defn'(Functor, Arity, File)).


	ixref_goal(Goal,    Fn,Ar) :-
		var(Goal), !,
		fail.
	ixref_goal((G1,G2), Fn,Ar) :-
		ixref_goal(G1, Fn,Ar).
	ixref_goal((G1,G2), Fn,Ar) :- !,
		ixref_goal(G2, Fn,Ar).
	ixref_goal((G1;G2), Fn,Ar) :-
		ixref_goal(G1, Fn,Ar).
	ixref_goal((G1;G2), Fn,Ar) :- !,
		ixref_goal(G2, Fn,Ar).
	ixref_goal(Goal,    Fn,Ar) :-
		call('$call'(Goal, Argument, Extra)),
		nonvar(Argument),
		functor(Argument, Fn, Small),
		Ar is Small+Extra.
	ixref_goal(Goal,    Fn,Ar) :-
		functor(Goal, Fn,Ar),
		call('$defn'(Fn,Ar, utility)),
		!, fail.
	ixref_goal(Goal,    Fn,Ar) :-
		functor(Goal, Fn,Ar).


cassert(Fact) :-
	call(Fact), !.
cassert(Fact) :-
	assertz(Fact).


%   The following predicate accesses the IXREF data-base.
%	from(-)		-- called but not defined
%	from(F)		-- defined in file F
%	tops(F)		-- defined in file F, not called in file F
%	>(-)		-- defined but calling nothing
%	>(Pattern)	-- calling something matching Pattern
%	<(-)		-- defined but not called
%	<(Pattern)	-- called by something matching Pattern
%	@>(Pattern)	-- calling Pattern = closure of >
%	@<(Pattern)	-- called by Pattern = closure of <

ixref_Pattern(from(_)).
ixref_Pattern(tops(_)).
ixref_Pattern(>(_)).
ixref_Pattern(<(_)).
ixref_Pattern(@>(_)).
ixref_Pattern(@<(_)).


ixref_Current(from(-), Functor, Arity) :- !,
	call('$seen'(Functor, Arity)),
	\+ call('$defn'(Functor, Arity, File)).
ixref_Current(from(File), Functor, Arity) :- !,
	call('$defn'(Functor, Arity, File)).
ixref_Current(tops(File), Functor, Arity) :- !,
	call('$defn'(Functor, Arity, File)),
	File \== utility,
	\+ ( '$call'(F, N, Functor, Arity), '$defn'(F, N, File) ).
ixref_Current(>(-), Functor, Arity) :- !,
	call('$defn'(Functor, Arity, _)),
	\+ call('$call'(Functor, Arity, _, _)).
ixref_Current(>(Pattern), Functor, Arity) :- !,
	isCurrent(Pattern, sp, G/B),
	call('$call'(Functor, Arity, G, B)).
ixref_Current(<(-), Functor, Arity) :- !,
	call('$defn'(Functor, Arity, File)),
	File \== utility,
	\+ call('$call'(_, _, Functor, Arity)).
ixref_Current(<(Pattern), Functor, Arity) :- !,
	isCurrent(Pattern, sp, G/B),
	call('$call'(G, B, Functor, Arity)).
ixref_Current(@>(Pattern), Functor, Arity) :- !,
	ixref_path(Functor/Arity, Pattern, _).
ixref_Current(@<(Pattern), Functor, Arity) :- !,
	ixref_path(Pattern, Functor/Arity, _).


/*----------------------------------------------------------------------+
|									|
|			   Seen File ?					|
|									|
|   The predicates provided to the user are				|
|	sf(Pattern, Files)		-- return selected filenames	|
|	sf(Pattern)			-- display selected filenames	|
|	sf				-- display all file names	|
|									|
|   Once again, there are two sorts of patterns, and keeping them apart	|
|   is confusing.  If the pattern is a string, the user is told which	|
|   files have been seen whose names match the pattern.  Otherwise, he	|
|   is told which files have been seen that defined predicates matching	|
|   the pattern.  E.g. sf "fre*" might locate a file 'fred.pl', while	|
|   sf ["fre*"] will locate files defining predicates fred...		|
|									|
+----------------------------------------------------------------------*/

sf :-
	sf("*").

sf(Pattern) :-
	sf(Pattern, Files),
	answer_List(Files, 32).

sf([Head|Tail], Files) :-
	integer(Head), !,
	setof(File, ('$seen'(File), isCurrent([Head|Tail], File)), Files).
sf(Pattern, Files) :-
	setof(File, ('$defn'(F,A,File), isCurrent(Pattern, cf, F/A)), Files).


/*----------------------------------------------------------------------+	
|									|
|			      Show Paths				|
|									|
|   The predicates provided for the user are				|
|	sp(Limits, Paths)		-- return paths			|
|	sp(Limits)			-- display paths		|
|   Note that there is no sp/0, as the complete list of paths is as	|
|   long as it is boring.						|
|	A path is a list [F0/N0, ..., Fk/Nk] where each entry names	|
|   a predicate, and Fi/Ni calls Fi+1/Ni+1, and no entry appears more	|
|   than once.  It describes in detail how F0/N0 may call Fk/Nk.  For	|
|   my convenience, this is the scheme used to implement @> and @<.	|
|   The Limits are							|
|	FirstCaller - LastCalled					|
|	- LastCalled							|
|	FirstCaller							|
|   where FirstCaller, LastCalled are TermPatterns.			|
|									|
+----------------------------------------------------------------------*/

sp(Limits) :-
	sp(Limits, Paths),
	answer_List(Paths, 31).

sp(FirstCaller-LastCalled, Paths) :- !,
	setof(Path, ixref_path(FirstCaller, LastCalled, Path), Paths).
sp(-LastCalled, Paths) :- !,
	sp("*"-LastCalled, Paths).
sp(FirstCaller, Paths) :-
	sp(FirstCaller-"*", Paths).


ixref_path(First, Last, [FirstSpec|Path]) :-
	isCurrent(First, sp, FirstSpec),
	ixref_path(FirstSpec, LastSpec, Path, [FirstSpec]),
	isCurrent(Last, sp, LastSpec).

		ixref_path(F/A, G/B, [H/C|Path], Forbidden) :-
			call('$call'(F, A, H, C)),
			\+ memberchk(H/C, Forbidden),
			ixref_path(H/C, G/B, Path, [H/C|Forbidden]).
		ixref_path(F/A, F/A, [], _).

			memberchk(H, [H|_]) :- !.
			memberchk(X, [_|T]) :- memberchk(X, T).


/*----------------------------------------------------------------------+	
|									|
|			      Call Tree					|
|									|
|   The predicates provided for the user are				|
|	ct				-- call tree for all top preds	|
|	ct(Pattern)			-- call tree for each match	|
|   A call tree is a way of displaying who calls whom in a compact and	|
|   readable table.  DOCUMENT THIS FURTHER.				|
|									|
+----------------------------------------------------------------------*/


ct :-
	ct(tops(_)).


ct(Pattern) :-
	cf(Pattern, Predicates),
	ct(Predicates, 0, []).


ct([], _, _) :- !.
ct([Functor/Arity|Predicates], LinesSoFar, UsedSoFar) :-
	call('$defn'(Functor, Arity, File)),
	File \== utility,
	!,
	ct(Functor, Arity, 0, LinesSoFar, Lines, UsedSoFar, Used),
	nl,
	ct(Predicates, Lines, Used).
ct([_|Predicates], Lines, Used) :-
	ct(Predicates, Lines, Used).


ct_prefix(Line0, Line, Depth, Functor, Arity) :-
	Line is Line0+1,
	(   Line < 10,  put(32), put(32)
	;   Line < 100, put(32)
	;   true
	),  !,
	write(Line), put(32),
	tab(Depth),
	write(Functor), put(47), write(Arity).


ct(Functor, Arity, Depth, L0, L, Used, Used) :-
	memberchk(f(Functor,Arity,Line), Used),
	!,
	ct_prefix(L0, L, Depth, Functor, Arity),
	write('  % see '), write(Line), nl.
ct(Functor, Arity, Depth, L0, L, U0, Used) :-
	'$defn'(Functor, Arity, File),
	!,
	ct_prefix(L0, L1, Depth, Functor, Arity),
	write('  % from '), write(File), nl,
	NewDepth is Depth+3,
	findall(F/A, '$call'(Functor,Arity,F,A), Children),
	ct(Children, NewDepth, L1, L, [f(Functor,Arity,L1)|U0], Used).
ct(Functor, Arity, Depth, L0, L, Used, Used) :-
	ct_prefix(L0, L, Depth, Functor, Arity),
	write('  % UNDEFINED'), nl.


ct([], _, L, L, U, U) :- !.
ct([F/A|Ch], D, L0, L, U0, U) :-
	ct(F, A, D, L0, L1, U0, U1),
	ct(Ch, D, L1, L, U1, U).

 EOF1IXREF.PL               00010057000100 85045 99364 000023Unix V7             HDR1LAZY.PL                00010058000100 85045 99364 000000Unix V7             %   File   : LAZY.PL
%   Author : R.A.O'Keefe
%   Updated: 30 October 1983
%   Purpose: Lazy lists in Prolog.
%   Needs  : apply/2 from APPLIC.PL.

%   Note: this code is "pure" only in the sense that it has no side-
%   effects.  It does rely on the 'var' metalogical predicate and cuts.
%   The lists are a little bit too eager to really be called lazy, but
%   if you look at N elements it is true that only N+1 will be computed.
%   Really lazy lists would compute N.  If you backtrack, the computed
%   elements will be undone just like other Prolog data structures, a
%   Prolog system with "intelligent backtracking" might not do that.

/*
:- type
	lazy_list(T) --> list(T)/void(T,T).

:- pred
	make_lazy(T, void(T,T), lazy_list(T)),
	head_lazy(lazy_list(T), T),
	tail_lazy(lazy_list(T), lazy_list(T)),
	member_check_lazy(T, lazy_list(T)).
*/
:- public
	make_lazy/3,
	head_lazy/2,
	tail_lazy/2,
	member_check_lazy/2.

:- mode
	make_lazy(+, +, -),
	head_lazy(+, ?),
	tail_lazy(+, -),
	member_check_lazy(+, +).


%   A lazy list is a pair consisting of a normal Prolog list (usually
%   ending with an unbound variable) and a goal which may be used to
%   generate new elements.  The idea is that [X0,X1,X2,...]/R should
%   satisfy X0 R X1, X1 R X2, ...  These objects should only be used
%   as arguments to these predicates.

make_lazy(First, Step, [First|_]/Step).


head_lazy([Head|_]/_, Head).


tail_lazy([_|Tail]/Step, Tail/Step) :-
	nonvar(Tail), !.	%  delete this clause to get logic
tail_lazy([Head|Tail]/Step, Tail/Step) :-
	apply(Step, [Head,Next]),
	Tail = [Next|_].


member_check_lazy(Thing, LazyList) :-
	head_lazy(LazyList, Thing), !.
member_check_lazy(Thing, LazyList) :-
	tail_lazy(LazyList, LazyTail),
	member_check_lazy(Thing, LazyTail).

EOF1LAZY.PL                00010058000100 85045 99364 000004Unix V7             HDR1LIB.PL                 00010059000100 85045 99364 000000Unix V7             %   File   : LIB.PL
%   Author : R.A.O'Keefe
%   Updated: 18 February 1984
%   Purpose: A Bottoms-10 version of the VAX "lib" predicate.

%   NOTE: this file MUST NOT BE COMPILED.
:-  true ; true.
%   There, I TOLD you not to compile this file!

%   The following directories could have been specified as
%	libdirectory(mec).
%	libdirectory(util).
%   except that lib(foo) would then try mec:foo as well as mec:foo.pl.

libdirectory('mec:?.pl').
libdirectory('util:?.pl').

:- compile('util:lib2.pl').

 EOF1LIB.PL                 00010059000100 85045 99364 000001Unix V7             HDR1LIB2.PL                00010060000100 85045 99364 000000Unix V7             %   File   : LIB2.PL
%   Author : R.A.O'Keefe
%   Updated: 18 February 1984
%   Purpose: A Bottoms-10 version of the VAX "lib" predicate.
%   Needs  : append/3 and memberchk/2

/*  Note: LIB.PL and LIB2.PL are two parts of one module.  They are
    separate because the user must be able to change the 'libdirectory'/1
    table with assert and retract, and therefore that file must not be
    compiled, while this one may be.  If you have an interpreter, or if
    'assert' *is* your compiler, you may merge the two files, as they are
    merged on the VAX.
*/

:- public
	lib/1,			%  load a file
	lib/2,			%  find a file
	note_lib/1,		%  note public predicates of file
	note_lib/2.		%  general case of note_lib/1

:- mode
	lib(+),
	lib(+, -),
	lib_(+),
	lib_(+, -),
	lib_(+, +, -),
	note_lib(+),
	note_lib(+, +),
	note_lib(+, +, +).



%   lib(File)
%   looks in all the likely places for File, and when it has found it,
%   it compiles the file.  In an interpreted Prolog, it should reconsult
%   the file instead.
%   lib(Pred/Arity) uses the clauses left behind by note_lib to compile
%   (or reconsult) the file defining that predicate.

lib(Symbol/Arity) :- !,
	functor(Head, Symbol, Arity),
	(   clause(Head, (Load,Head)), !, call(Load)
	;   true	% it is already defined
	).
lib(File) :-
	lib(File, Found),
	compile(Found).			%  Dec-10 Prolog
%	reconsult(Found).		%  Vax-11 Prolog
	

%   lib(File, Found)
%   looks in all the likely places for File, and when it has
%   found it, it returns the name of the file it Found.

lib(File, Found) :-
	nofileerrors,		%   so 'see' will fail instead of aborting
	seeing(OldFile),	%   'see' redirects the input stream
	(   lib_(File, Guess),	%   GENERATE guesses
	    see(Guess),		%   this succeeds if the guess is right
	    !,			%   and such a file exists.  We want 1 only.
	    seeing(Found),	%   pick up the name after possible normalisation
	    seen,		%   close the file (POSSIBLE BUG!)
	    fileerrors,		%   make see abort instead of failing, again.
	    see(OldFile)	%   make the input stream what it used to be
	;   fileerrors,		%   if the file can't be found, we have to
	    fail		%   fileerrors, but at least input wasn't
	).			%   redirected.


%   lib_(File, Guess)
%   uses the 'libdirectory'/1 table to enumerate likely places.
%   The first thing to try, however, is the file name as given.
%   The entries in 'libdirectory' are of two sorts:
%	atoms containing a question mark <prefix>?<suffix>
%	    The guess is <prefix><File><suffix>.
%	    Any special punctuation must already be in the atom.
%	atoms not containing a question mark (device names)
%	    Two guesses are made: <device>:<File> and <device>:<File>.pl
%   A guess is immediately rejected if it contains two colons, two
%   full stops, or a full stop followed by a colon.

lib_(File, File).
lib_(File, Guess) :-
	name(File, FileName),
	libdirectory(Dir),			% enumerate directories
	name(Dir, DirName),
	lib_(FileName, DirName, FullName),	% construct full name
	lib_(FullName),				% check full name
	name(Guess, FullName).

lib_(FileName, DirName, FullName) :-		%  P?S -> PFS
	append(Prefix, [63|Suffix], DirName),
	!,
	append(FileName, Suffix, L),
	append(Prefix, L, FullName).
lib_(FileName, DirName, FullName) :-		%  D -> D:F
	append(DirName, [58|FileName], FullName).
lib_(FileName, DirName, FullName) :-		%  D -> D:F.pl
	append(FileName, ".pl", Extended),
	append(DirName, [58|Extended], FullName).


%   Check that the string does not contain two colons, two
%   full stops, or a full stop followed by a colon.  Let's face it,
%   THIS PREDICATE IS OPERATING-SYSTEM-SPECIFIC.
%   There ought to be a valid_file_name(Atom) predicate in each
%   Prolog implementation, but there isn't.

lib_(S) :-
	\+  (	append(_, [58|T], S), memberchk(58, T)
	    ;   append(_, [46|T], S),(memberchk(46, T) ; memberchk(58, T))
	    ).



%   note_lib(File, Method)
%   reads the first clause in the File, which should be a public declaration.
%   If it is, it notes for each predicate mentioned in that declaration that
%   the predicate may be defined by calling Method(File).  Method should be
%   'reconsult' or 'compile', 'consult' will NOT do as it has to wipe out the
%   clauses which note_lib creates.  If you have some fancy preprocessor, you
%   can name it as the method to be used, provided it acts like reconsult
%   rather than consult.
%   note_lib(File)
%   supplies a default Method to note_lib/2, the default being compile for
%   those Prologs which have it, and reconsult for the others.

note_lib(File) :-
	note_lib(File, compile).	%  Dec-10 Prolog
%	note_lib(File, reconsult).	%  Vax-11 Prolog

note_lib(File, Method) :-
	nofileerrors,
	see(File),
	read(FirstClause),
	seen,
	fileerrors,
	FirstClause = (:- public Exports),
	!,
	note_lib(Exports, File, Method).
note_lib(File, Method) :-
	fileerrors,
	write('! note_lib: '), write(File),
	write(' is missing or lacks a :- public declaration.'),
	nl.

note_lib((A,B), File, Method) :- !,
	note_lib(A, File, Method),
	note_lib(B, File, Method).
note_lib(Symbol/Arity, File, Method) :-
	functor(Head, Symbol, Arity),
	Load =.. [Method,File],
	(   clause(Head, _)		%  it's already defined
	;   assert((Head :- Load, Head))
	),  !.


 EOF1LIB2.PL                00010060000100 85045 99364 000011Unix V7             HDR1LISTUT.BAK             00010061000100 85045 99364 000000Unix V7             %   File   : LISTUT.PL
%   Author : Bob Welham, Lawrence Byrd, and R.A.O'Keefe
%   Updated: 1 October 1984
%   Purpose: list processing utilities

%   This module requires
%	select/3	(from SetUtl.Pl) for perm/2
%	listtoset/2	(from SetUtl.Pl) for remove_dups/2
%   If you don't want those routines, it can be used on its own.
%   I am not sure how much of the original code was by Bob Welham
%   and how much by Lawrence Byrd.  The layout and comments are by
%   R.A.O'Keefe, as are nth*, same_length, shorter_list, and subseq*.
%   Keys_and_values has moved to PROJEC.PL.

:- public
	append/3,			%   List x List -> List
	correspond/4,			%   Elem <- List x List -> Elem
	delete/3,			%   List x Elem -> List
	last/2,				%   List -> Elem
	nextto/3,			%   Elem, Elem <- List
	nmember/3,			%   Elem <- Set -> Integer
	nth0/3,				%   Integer x List -> Elem
	nth0/4,				%   Integer x List -> Elem x List
	nth1/3,				%   Integer x List -> Elem
	nth1/4,				%   Integer x List -> Elem x List
	numlist/3,			%   Integer x Integer -> List
	perm/2,				%   List -> List
	perm2/4,			%   Elem x Elem -> Elem x Elem
	remove_dups/2,			%   List -> Set
	rev/2,				%   List -> List
	reverse/2,			%   List -> List
	same_length/2,			%   List x List ->
	select/4,			%   Elem x List x Elem -> List
	shorter_list/2,			%   List x List ->
	subseq/3,			%   List -> List x List
	subseq0/2,			%   List -> List
	subseq1/2,			%   List -> List
	sumlist/2.			%   List -> Integer

:- mode
	append(?, ?, ?),
	correspond(?, +, +, ?),
	delete(+, +, -),
	last(?, ?),
	nextto(?, ?, ?),
	nmember(?, +, ?),
	nth0(+, +, ?),
	nth0(+, ?, ?, ?),
	nth1(+, +, ?),
	nth1(+, ?, ?, ?),
	numlist(+, +, ?),
	perm(?, ?),
	perm2(?,?, ?,?),
	remove_dups(+, ?),
	rev(?, ?),
	reverse(?, ?),
	reverse(?, +, ?),
	same_length(?, ?),
	select(?, ?, ?, ?),
	shorter_list(?, +),
	subseq(?, ?, ?),
	subseq0(+, ?),
	subseq1(+, ?),
	sumlist(+, ?),
	sumlist(+, +, ?).


%   append(Prefix, Suffix, Combined)
%   is true when all three arguments are lists, and the members of Combined
%   are the members of Prefix followed by the members of Suffix.  It may be
%   used to form Combined from a given Prefix and Suffix, or to take a given
%   Combined apart.  E.g. we could define member/2 (from SetUtl.Pl) as
%	member(X, L) :- append(_, [X|_], L).

append([], L, L).
append([H|T], L, [H|R]) :-
	append(T, L, R).



%   correspond(X, Xlist, Ylist, Y)
%   is true when Xlist and Ylist are lists, X is an element of Xlist, Y is
%   an element of Ylist, and X and Y are in similar places in their lists.

correspond(X, [X|_], [Y|_], Y) :- !.
correspond(X, [_|T], [_|U], Y) :-
	correspond(X, T, U, Y).



%   delete(List, Elem, Residue)
%   is true when List is a list, in which Elem may or may not occur, and
%   Residue is a copy of List with all elements equal to Elem deleted.

delete([], _, []) :- !.
delete([Kill|Tail], Kill, Rest) :- !,
	delete(Tail, Kill, Rest).
delete([Head|Tail], Kill, [Head|Rest]) :- !,
	delete(Tail, Kill, Rest).



%   last(Last, List)
%   is true when List is a List and Last is its last element.  This could
%   be defined as last(X,L) :- append(_, [X], L).

last(Last, [Last]) :- !.
last(Last, [_|List]) :-
	last(Last, List).



%   nextto(X, Y, List)
%   is true when X and Y appear side-by-side in List.  It could be written as
%	nextto(X, Y, List) :- append(_, [X,Y], List).
%   It may be used to enumerate successive pairs from the list.

nextto(X,Y, [X,Y|_]).
nextto(X,Y, [_|List]) :-
	nextto(X,Y, List).



%   nmember(Elem, List, Index)
%   is true when Elem is the Indexth member of List.  Could be written as
%	nmember(X, L, N) :- append(B, [X|_], L), length(B, M), N is M+1.
%   It may be used to select a particular element, or to find where some
%   given element occurs, or to enumerate the elements and indices together.

nmember(Elem, [Elem|_], 1).
nmember(Elem, [_|List], N) :-
	nmember(Elem, List, M),
	N is M+1.



%   nth0(N, List, Elem) is true when Elem is the Nth member of List,
%   counting the first as element 0.  (That is, throw away the first
%   N elements and unify Elem with the next.)  It can only be used to
%   select a particular element given the list and index.  For that
%   task it is more efficient than nmember.
%   nth1(N, List, Elem) is the same as nth0, except that it counts from
%   1, that is nth(1, [H|_], H).

nth0(0, [Head|Tail], Head) :- !.
nth0(N, [Head|Tail], Elem) :-
	M is N-1,			% should be succ(M, N)
	nth0(M, Tail, Elem).


nth1(1, [Head|Tail], Head) :- !.
nth1(N, [Head|Tail], Elem) :-
	M is N-1,			% should be succ(M, N)
	nth1(M, Tail, Elem).



%   nth0(N, List, Elem, Rest) unifies Elem with the Nth element of List,
%   counting from 0, and Rest with the other elements.  It can be used
%   to select the Nth element of List (yielding Elem and Rest), or to 
%   insert Elem before the Nth (counting from 1) element of Rest, when
%   it yields List, e.g. nth0(2, List, c, [a,b,d,e]) unifies List with
%   [a,b,c,d,e].  nth1 is the same except that it counts from 1.  nth1
%   can be used to insert Elem after the Nth element of Rest.

nth0(0, [Head|Tail], Head, Tail) :- !.
nth0(N, [Head|Tail], Elem, [Head|Rest]) :-
	M is N-1,		% succ(M, N); should fail if N < 1
	nth0(M, Tail, Elem, Rest).


nth1(1, [Head|Tail], Head, Tail) :- !.
nth1(N, [Head|Tail], Elem, [Head|Rest]) :-
	M is N-1,		% succ(M, N); should fail if N < 1
	nth1(M, Tail, Elem, Rest).



%   numlist(Lower, Upper, List)
%   is true when List is [Lower, ..., Upper]
%   Note that Lower and Upper must be integers, not expressions, and
%   that if Upper < Lower numlist will FAIL rather than producing an
%   empty list.

numlist(Upper, Upper, [Upper]) :- !.
numlist(Lower, Upper, [Lower|Rest]) :-
	Lower < Upper,
	Next is Lower+1,
	numlist(Next, Upper, Rest).



%   perm(List, Perm)
%   is true when List and Perm are permutations of each other.  Of course,
%   if you just want to test that, the best way is to keysort/2 the two
%   lists and see if the results are the same.  Or you could use list_to_bag
%   (from BagUtl.Pl) to see if they convert to the same bag.  The point of
%   perm is to generate permutations.  The arguments may be either way round,
%   the only effect will be the order in which the permutations are tried.
%   Be careful: this is quite efficient, but the number of permutations of an
%   N-element list is N!, even for a 7-element list that is 5040.

perm([], []).
perm(List, [First|Perm]) :-
	select(First, List, Rest),	%  tries each List element in turn
	perm(Rest, Perm).



%   perm2(A,B, C,D)
%   is true when {A,B} = {C,D}.  It is very useful for writing pattern
%   matchers over commutative operators.  It is used more than perm is.

perm2(X,Y, X,Y).
perm2(X,Y, Y,X).



%   remove_dups(List, Pruned)
%   removes duplicated elements from List.  Beware: if the List has
%   non-ground elements, the result may surprise you.

remove_dups(List, Pruned) :-
	sort(List, Pruned).



%   reverse(List, Reversed)
%   is true when List and Reversed are lists with the same elements
%   but in opposite orders.  rev/2 is a synonym for reverse/2.

rev(List, Reversed) :-
	reverse(List, [], Reversed).

reverse(List, Reversed) :-
	reverse(List, [], Reversed).

reverse([], Reversed, Reversed).
reverse([Head|Tail], Sofar, Reversed) :-
	reverse(Tail, [Head|Sofar], Reversed).



%   same_length(List1, List2)
%   is true when List1 and List2 are both lists and have the same number
%   of elements.  No relation between the values of their elements is
%   implied.  It may be used to generate either list given the other,
%   or indeed to generate two lists of the same length, in which case
%   the arguments will be bound to lists of length 0, 1, 2, ... 

same_length([], []).
same_length([_|List1], [_|List2]) :-
	same_length(List1, List2).



%   select(X, Xlist, Y, Ylist)
%   is true when X is the Kth member of Xlist and Y the Kth element of Ylist
%   for some K, and apart from that Xlist and Ylist are the same.  You can
%   use it to replace X by Y or vice versa.

select(X, [X|Tail], Y, [Y|Tail]).
select(X, [Head|Xlist], Y, [Head|Ylist]) :-
	select(X, Xlist, Y, Ylist).



%   shorter_list(Short, Long)
%   is true when Short is a list is strictly shorter than Long.  Long
%   doesn't have to be a proper list provided it is long enough.  This
%   can be used to generate lists shorter than Long, lengths 0, 1, 2...
%   will be tried, but backtracking will terminate with a list that is
%   one element shorter than Long.  It cannot be used to generate lists
%   longer than Short, because it doesn't look at all the elements of the
%   longer list.

shorter_list([], [_|_]).
shorter_list([_|Short], [_|Long]) :-
	shorter_list(Short, Long).
	


%   subseq(Sequence, SubSequence, Complement)
%   is true when SubSequence and Complement are both subsequences of the
%   list Sequence (the order of corresponding elements being preserved)
%   and every element of Sequence which is not in SubSequence is in the
%   Complement and vice versa.  That is,
%   length(Sequence) = length(SubSequence)+length(Complement), e.g.
%   subseq([1,2,3,4], [1,3,4], [2]).  This was written to generate subsets
%   and their complements together, but can also be used to interleave two
%   lists in all possible ways.  Note that if S1 is a subset of S2, it will
%   be generated *before S2 as a SubSequence and *after it as a Complement.

subseq([], [], []).
subseq([Head|Tail], Sbsq, [Head|Cmpl]) :-
	subseq(Tail, Sbsq, Cmpl).
subseq([Head|Tail], [Head|Sbsq], Cmpl) :-
	subseq(Tail, Sbsq, Cmpl).



%   subseq0(Sequence, SubSequence)
%   is true when SubSequence is a subsequence of Sequence, but may
%   be Sequence itself.   Thus subseq0([a,b], [a,b]) is true as well
%   as subseq0([a,b], [a]).

%   subseq1(Sequence, SubSequence)
%   is true when SubSequence is a proper subsequence of Sequence,
%   that is it contains at least one element less.

%   ?- setof(X, subseq0([a,b,c],X), Xs).
%   Xs = [[],[a],[a,b],[a,b,c],[a,c],[b],[b,c],[c]] 
%   ?- bagof(X, subseq0([a,b,c,d],X), Xs).
%   Xs = [[a,b,c,d],[b,c,d],[c,d],[d],[],[c],[b,d],[b],[b,c],[a,c,d],
%	  [a,d],[a],[a,c],[a,b,d],[a,b],[a,b,c]] 

subseq0(List, List).
subseq0(List, Rest) :-
	subseq1(List, Rest).


subseq1([Head|Tail], Rest) :-
	subseq0(Tail, Rest).
subseq1([Head|Tail], [Head|Rest]) :-
	subseq1(Tail, Rest).



%   sumlist(Numbers, Total)
%   is true when Numbers is a list of integers, and Total is their sum.
%   Note that in Dec-10 compiled Prolog this will only work as stated;
%   interpreters will almost certainly accept integer expressions.  Also
%   note here as elsewhere in Prolog arithmetic that machine arithmetic
%   wraps round on the Dec-10: (2^17 - 1)+1 = -2^17 .

sumlist(Numbers, Total) :-
	sumlist(Numbers, 0, Total).

sumlist([], Total, Total).
sumlist([Head|Tail], Sofar, Total) :-
	Next is Sofar+Head,
	sumlist(Tail, Next, Total).


 EOF1LISTUT.BAK             00010061000100 85045 99364 000022Unix V7             HDR1LISTUT.HLP             00010062000100 85045 99364 000000Unix V7             #file. LISTUT.HLP

#updated. 1 October 1984

#purpose.
Provides list processing utilities

#datatype.
Operates on standard Prolog lists, of form
		[Head|Tail]
or
		[Item, Item, Item .....]

#needs.
This module requires
	select/3	(from SetUtl.Pl) for perm/2
	listtoset/2	(from SetUtl.Pl) for remove_dups/2
If you don't want those routines, it can be used on its own.

#author.
I am not sure how much of the original code was by Bob Welham
and how much by Lawrence Byrd.  The layout and comments are by
R.A.O'Keefe, as are nth*, same_length, shorter_list, and subseq*.
Keys_and_values has moved to PROJEC.PL.

#commands.
append(Prefix, Suffix, Combined)	Append list Sufffix to list Prefix
					giving Combined.
correspond(X, Xlist, Y,Ylist)		Is X in same position in Xlist as
					as Y is in Ylist?
delete(List, Elem, Residue)		Delete Elem from List leaving Residue
last(Last, List)			Is Last the last element in List?
nextto(X, Y, List)			Do X and Y appear side-by-side in List?
nth0(N, List, Elem)			Unify Elem with N'th element of List
					Head of list is element number 0
nth0(N, List, Elem, Rest)		Unify Elem with N'th element of List
					and Rest with the other elements.
					Head of list is element number 0
nth1(N, List, Elem)			As nth0. Head of list is element number
					1
nth1(N, List, Elem, Rest)		As nth0. Head of list is element number
					1
nmember(Elem, List, Index)		Is Elem the Index'th member of List?
numlist(Lower, Upper, List)		Does list consist of integers
					[Lower, ..., Upper] ?
perm(List, Perm)			Generates permutations of List
perm2(A, B, C, D)			Is {A,B} = {C, D} ?
remove_dups(List, Pruned)		Pruned is List with no duplicate
					elements.
rev(List, Reversed)			As reverse.
reverse(List, Reversed)			Reversed is reverse of List
same_length(List1, List2)		Is List1 the same length as List2?
select(X, Xlist, Y, Ylist)		Xlist and Ylist are identical apart
					from K'th elements (X and Y).
shorter-list(Short, Long)		Is list Short shorter than list Long?
subseq(Seq, Subseq, Complement)		If Subseq is a subsequence of Seq then
					Complement is the other elements of Seq
subseq0(Sequence, Subsequence)		Is Subsequence a sub-sequence of or
					the same sequence as Sequence?
subseq1(Sequence, Subsequence)		Is Subsequence a subsequence of
					Sequence?
sumlist(Numbers, Total)			Total is sum of integers in list
					Numbers

#user_keys.
list, listut, sequence, sequences.
 EOF1LISTUT.HLP             00010062000100 85045 99364 000005Unix V7             HDR1LISTUT.PL              00010063000100 85045 99364 000000Unix V7             %   File   : LISTUT.PL
%   Author : Bob Welham, Lawrence Byrd, and R.A.O'Keefe
%   Updated: 12 February 1985
%   Purpose: list processing utilities

%   This module requires
%	select/3	(from SetUtl.Pl) for perm/2
%	listtoset/2	(from SetUtl.Pl) for remove_dups/2
%   If you don't want those routines, it can be used on its own.
%   I am not sure how much of the original code was by Bob Welham
%   and how much by Lawrence Byrd.  The layout and comments are by
%   R.A.O'Keefe, as are nth*, same_length, shorter_list, and subseq*.
%   Keys_and_values has moved to PROJEC.PL.

:- public
	append/3,			%   List x List -> List
	correspond/4,			%   Elem <- List x List -> Elem
	delete/3,			%   List x Elem -> List
	last/2,				%   List -> Elem
	nextto/3,			%   Elem, Elem <- List
	nmember/3,			%   Elem <- Set -> Integer
	nmembers/3,			%   List x Set -> Set
	nth0/3,				%   Integer x List -> Elem
	nth0/4,				%   Integer x List -> Elem x List
	nth1/3,				%   Integer x List -> Elem
	nth1/4,				%   Integer x List -> Elem x List
	numlist/3,			%   Integer x Integer -> List
	perm/2,				%   List -> List
	perm2/4,			%   Elem x Elem -> Elem x Elem
	remove_dups/2,			%   List -> Set
	rev/2,				%   List -> List
	reverse/2,			%   List -> List
	same_length/2,			%   List x List ->
	select/4,			%   Elem x List x Elem -> List
	shorter_list/2,			%   List x List ->
	subseq/3,			%   List -> List x List
	subseq0/2,			%   List -> List
	subseq1/2,			%   List -> List
	sumlist/2.			%   List -> Integer

:- mode
	append(?, ?, ?),
	correspond(?, +, +, ?),
	delete(+, +, -),
	last(?, ?),
	nextto(?, ?, ?),
	nmember(?, +, ?),
	nmembers(+, +, -),
	nth0(+, +, ?),
	nth0(+, ?, ?, ?),
	nth1(+, +, ?),
	nth1(+, ?, ?, ?),
	numlist(+, +, ?),
	perm(?, ?),
	perm2(?,?, ?,?),
	remove_dups(+, ?),
	rev(?, ?),
	reverse(?, ?),
	reverse(?, +, ?),
	same_length(?, ?),
	select(?, ?, ?, ?),
	shorter_list(?, +),
	subseq(?, ?, ?),
	subseq0(+, ?),
	subseq1(+, ?),
	sumlist(+, ?),
	sumlist(+, +, ?).


%   append(Prefix, Suffix, Combined)
%   is true when all three arguments are lists, and the members of Combined
%   are the members of Prefix followed by the members of Suffix.  It may be
%   used to form Combined from a given Prefix and Suffix, or to take a given
%   Combined apart.  E.g. we could define member/2 (from SetUtl.Pl) as
%	member(X, L) :- append(_, [X|_], L).

append([], L, L).
append([H|T], L, [H|R]) :-
	append(T, L, R).



%   correspond(X, Xlist, Ylist, Y)
%   is true when Xlist and Ylist are lists, X is an element of Xlist, Y is
%   an element of Ylist, and X and Y are in similar places in their lists.

correspond(X, [X|_], [Y|_], Y) :- !.
correspond(X, [_|T], [_|U], Y) :-
	correspond(X, T, U, Y).



%   delete(List, Elem, Residue)
%   is true when List is a list, in which Elem may or may not occur, and
%   Residue is a copy of List with all elements equal to Elem deleted.

delete([], _, []) :- !.
delete([Kill|Tail], Kill, Rest) :- !,
	delete(Tail, Kill, Rest).
delete([Head|Tail], Kill, [Head|Rest]) :- !,
	delete(Tail, Kill, Rest).



%   last(Last, List)
%   is true when List is a List and Last is its last element.  This could
%   be defined as last(X,L) :- append(_, [X], L).

last(Last, [Last]) :- !.
last(Last, [_|List]) :-
	last(Last, List).



%   nextto(X, Y, List)
%   is true when X and Y appear side-by-side in List.  It could be written as
%	nextto(X, Y, List) :- append(_, [X,Y], List).
%   It may be used to enumerate successive pairs from the list.

nextto(X,Y, [X,Y|_]).
nextto(X,Y, [_|List]) :-
	nextto(X,Y, List).



%   nmember(Elem, List, Index)
%   is true when Elem is the Indexth member of List.  Could be written as
%	nmember(X, L, N) :- append(B, [X|_], L), length(B, M), N is M+1.
%   It may be used to select a particular element, or to find where some
%   given element occurs, or to enumerate the elements and indices together.

nmember(Elem, [Elem|_], 1).
nmember(Elem, [_|List], N) :-
	nmember(Elem, List, M),
	N is M+1.


% nmembers(Indices, Answers, Ans)
% Like nmember/3 except that it looks for a list of arguments in a list
% of positions.
% eg.   nmembers([3,5,1], [a,b,c,d,e,f,g,h], [c,e,a]) is true

nmembers([], _, []).
nmembers([N|Rest], Answers, [Ans|RestAns]) :-
	nmember(Ans, Answers, N),
	nmembers(Rest, Answers, RestAns).




%   nth0(N, List, Elem) is true when Elem is the Nth member of List,
%   counting the first as element 0.  (That is, throw away the first
%   N elements and unify Elem with the next.)  It can only be used to
%   select a particular element given the list and index.  For that
%   task it is more efficient than nmember.
%   nth1(N, List, Elem) is the same as nth0, except that it counts from
%   1, that is nth(1, [H|_], H).

nth0(0, [Head|Tail], Head) :- !.
nth0(N, [Head|Tail], Elem) :-
	M is N-1,			% should be succ(M, N)
	nth0(M, Tail, Elem).


nth1(1, [Head|Tail], Head) :- !.
nth1(N, [Head|Tail], Elem) :-
	M is N-1,			% should be succ(M, N)
	nth1(M, Tail, Elem).



%   nth0(N, List, Elem, Rest) unifies Elem with the Nth element of List,
%   counting from 0, and Rest with the other elements.  It can be used
%   to select the Nth element of List (yielding Elem and Rest), or to 
%   insert Elem before the Nth (counting from 1) element of Rest, when
%   it yields List, e.g. nth0(2, List, c, [a,b,d,e]) unifies List with
%   [a,b,c,d,e].  nth1 is the same except that it counts from 1.  nth1
%   can be used to insert Elem after the Nth element of Rest.

nth0(0, [Head|Tail], Head, Tail) :- !.
nth0(N, [Head|Tail], Elem, [Head|Rest]) :-
	M is N-1,		% succ(M, N); should fail if N < 1
	nth0(M, Tail, Elem, Rest).


nth1(1, [Head|Tail], Head, Tail) :- !.
nth1(N, [Head|Tail], Elem, [Head|Rest]) :-
	M is N-1,		% succ(M, N); should fail if N < 1
	nth1(M, Tail, Elem, Rest).



%   numlist(Lower, Upper, List)
%   is true when List is [Lower, ..., Upper]
%   Note that Lower and Upper must be integers, not expressions, and
%   that if Upper < Lower numlist will FAIL rather than producing an
%   empty list.

numlist(Upper, Upper, [Upper]) :- !.
numlist(Lower, Upper, [Lower|Rest]) :-
	Lower < Upper,
	Next is Lower+1,
	numlist(Next, Upper, Rest).



%   perm(List, Perm)
%   is true when List and Perm are permutations of each other.  Of course,
%   if you just want to test that, the best way is to keysort/2 the two
%   lists and see if the results are the same.  Or you could use list_to_bag
%   (from BagUtl.Pl) to see if they convert to the same bag.  The point of
%   perm is to generate permutations.  The arguments may be either way round,
%   the only effect will be the order in which the permutations are tried.
%   Be careful: this is quite efficient, but the number of permutations of an
%   N-element list is N!, even for a 7-element list that is 5040.

perm([], []).
perm(List, [First|Perm]) :-
	select(First, List, Rest),	%  tries each List element in turn
	perm(Rest, Perm).



%   perm2(A,B, C,D)
%   is true when {A,B} = {C,D}.  It is very useful for writing pattern
%   matchers over commutative operators.  It is used more than perm is.

perm2(X,Y, X,Y).
perm2(X,Y, Y,X).



%   remove_dups(List, Pruned)
%   removes duplicated elements from List.  Beware: if the List has
%   non-ground elements, the result may surprise you.

remove_dups(List, Pruned) :-
	sort(List, Pruned).



%   reverse(List, Reversed)
%   is true when List and Reversed are lists with the same elements
%   but in opposite orders.  rev/2 is a synonym for reverse/2.

rev(List, Reversed) :-
	reverse(List, [], Reversed).

reverse(List, Reversed) :-
	reverse(List, [], Reversed).

reverse([], Reversed, Reversed).
reverse([Head|Tail], Sofar, Reversed) :-
	reverse(Tail, [Head|Sofar], Reversed).



%   same_length(List1, List2)
%   is true when List1 and List2 are both lists and have the same number
%   of elements.  No relation between the values of their elements is
%   implied.  It may be used to generate either list given the other,
%   or indeed to generate two lists of the same length, in which case
%   the arguments will be bound to lists of length 0, 1, 2, ... 

same_length([], []).
same_length([_|List1], [_|List2]) :-
	same_length(List1, List2).



%   select(X, Xlist, Y, Ylist)
%   is true when X is the Kth member of Xlist and Y the Kth element of Ylist
%   for some K, and apart from that Xlist and Ylist are the same.  You can
%   use it to replace X by Y or vice versa.

select(X, [X|Tail], Y, [Y|Tail]).
select(X, [Head|Xlist], Y, [Head|Ylist]) :-
	select(X, Xlist, Y, Ylist).



%   shorter_list(Short, Long)
%   is true when Short is a list is strictly shorter than Long.  Long
%   doesn't have to be a proper list provided it is long enough.  This
%   can be used to generate lists shorter than Long, lengths 0, 1, 2...
%   will be tried, but backtracking will terminate with a list that is
%   one element shorter than Long.  It cannot be used to generate lists
%   longer than Short, because it doesn't look at all the elements of the
%   longer list.

shorter_list([], [_|_]).
shorter_list([_|Short], [_|Long]) :-
	shorter_list(Short, Long).
	


%   subseq(Sequence, SubSequence, Complement)
%   is true when SubSequence and Complement are both subsequences of the
%   list Sequence (the order of corresponding elements being preserved)
%   and every element of Sequence which is not in SubSequence is in the
%   Complement and vice versa.  That is,
%   length(Sequence) = length(SubSequence)+length(Complement), e.g.
%   subseq([1,2,3,4], [1,3,4], [2]).  This was written to generate subsets
%   and their complements together, but can also be used to interleave two
%   lists in all possible ways.  Note that if S1 is a subset of S2, it will
%   be generated *before S2 as a SubSequence and *after it as a Complement.

subseq([], [], []).
subseq([Head|Tail], Sbsq, [Head|Cmpl]) :-
	subseq(Tail, Sbsq, Cmpl).
subseq([Head|Tail], [Head|Sbsq], Cmpl) :-
	subseq(Tail, Sbsq, Cmpl).



%   subseq0(Sequence, SubSequence)
%   is true when SubSequence is a subsequence of Sequence, but may
%   be Sequence itself.   Thus subseq0([a,b], [a,b]) is true as well
%   as subseq0([a,b], [a]).

%   subseq1(Sequence, SubSequence)
%   is true when SubSequence is a proper subsequence of Sequence,
%   that is it contains at least one element less.

%   ?- setof(X, subseq0([a,b,c],X), Xs).
%   Xs = [[],[a],[a,b],[a,b,c],[a,c],[b],[b,c],[c]] 
%   ?- bagof(X, subseq0([a,b,c,d],X), Xs).
%   Xs = [[a,b,c,d],[b,c,d],[c,d],[d],[],[c],[b,d],[b],[b,c],[a,c,d],
%	  [a,d],[a],[a,c],[a,b,d],[a,b],[a,b,c]] 

subseq0(List, List).
subseq0(List, Rest) :-
	subseq1(List, Rest).


subseq1([Head|Tail], Rest) :-
	subseq0(Tail, Rest).
subseq1([Head|Tail], [Head|Rest]) :-
	subseq1(Tail, Rest).



%   sumlist(Numbers, Total)
%   is true when Numbers is a list of integers, and Total is their sum.
%   Note that in Dec-10 compiled Prolog this will only work as stated;
%   interpreters will almost certainly accept integer expressions.  Also
%   note here as elsewhere in Prolog arithmetic that machine arithmetic
%   wraps round on the Dec-10: (2^17 - 1)+1 = -2^17 .

sumlist(Numbers, Total) :-
	sumlist(Numbers, 0, Total).

sumlist([], Total, Total).
sumlist([Head|Tail], Sofar, Total) :-
	Next is Sofar+Head,
	sumlist(Tail, Next, Total).


 EOF1LISTUT.PL              00010063000100 85045 99364 000022Unix V7             HDR1LOGARR.PL              00010064000100 85045 99364 000000Unix V7             %   File   : LOGARR.PL
%   Author : Mostly David H.D.Warren, some changes by Fernando Pereira
%   Updated: 24 September 1984
%   Purpose: Extendable arrays with logarithmic access time.

/*  LOGARITHMIC ARRAYS.

    An array extends from 0 to 2**Size - 1, where Size is a multiple of 2.
    Note that 2**Size = 1<<Size.

    External interface.

    new_array(A) returns a new empty array A.

    is_array(A) checks whether A is an array.

    aref(Index,Array,Element) unifies Element with Array[Index],
	or fails if Array[Index] has not been set.

    arefa(Index,Array,Element) is as aref/3, except that it unifies
	Element with a new array if Array[Index] is undefined.
	This is useful for multidimensional arrays implemented
	as arrays of arrays.

    arefl(Index,Array,Element) is as aref/3, except that Element
	appears as '[]' for undefined cells.

    aset(Index,Array,Element,NewArray) unifies NewArray with the
	result of setting Array[Index] to Element.

    array_to_list(Array,List) returns a list of pairs Index-Element
	of all the elements of Array that have been set.

	In the interests of uniformity, R.A.O'K used the Prolog source
	code tidier on this file; this is not its original layout.  He
	made no algorithmic changes, however.
*/

:- public
	new_array/1,
	is_array/1,
	aref/3,
	arefa/3,
	arefl/3,
	aset/4,
	array_to_list/2.

:- mode
	aref(+, +, ?),
	arefa(+, +, ?),
	arefl(+, +, ?),
	array_to_list(+, -),
	aset(+, +, +, -),
	array_item(+, +, +, +, ?),
	is_array(+),
	new_array(-),
	not_undef(+),
	subarray(+, +, ?),
	subarray_to_list(+, +, +, +, ?, ?),
	update_subarray(+, +, ?, ?, -).


new_array(array($($,$,$,$),2)).


is_array(array(_,_)).


aref(Index, array(Array,Size), Item) :-
	check_int(Index),
	Index < 1<<Size,
	N is Size-2,
	Subindex is Index>>N /\ 3,
	array_item(Subindex, N, Index, Array, Item).


array_to_list(array($(A0,A1,A2,A3),Size), L0) :-
	N is Size-2,
	subarray_to_list(0, N, 0, A0, L0, L1),
	subarray_to_list(1, N, 0, A1, L1, L2),
	subarray_to_list(2, N, 0, A2, L2, L3),
	subarray_to_list(3, N, 0, A3, L3, []).


arefa(Index, array(Array,Size), Item) :-
	check_int(Index),
	Index < 1<<Size,
	N is Size-2,
	Subindex is Index>>N /\ 3,
	array_item(Subindex, N, Index, Array, Item), !.
arefa(_, _, Item) :-
	new_array(Item).


arefl(Index, array(Array,Size), Item) :-
	check_int(Index),
	Index < 1<<Size,
	N is Size-2,
	Subindex is Index>>N /\ 3,
	array_item(Subindex, N, Index, Array, Item), !.
arefl(_, _, []).


aset(Index, array(Array0,Size0), Item, array(Array,Size)) :-
	check_int(Index),
	enlarge_array(Index, Size0, Array0, Size, Array1),
	update_array_item(Size, Index, Array1, Item, Array).


check_int(I) :-
	integer(I), !.
check_int(X) :-
	write('Array index not integer: '), write(X), nl,
	trace, fail.

% Guts

enlarge_array(I, Size, Array, Size, Array) :-
	I < 1<<Size, !.
enlarge_array(I, Size0, Array0, Size, Array) :-
	Size1 is Size0 + 2,
	Array1 = $(Array0,$,$,$),
	enlarge_array(I, Size1, Array1, Size, Array).


array_item(0, 0, Index, $(Item,_,_,_), Item) :- !,
	not_undef(Item).
array_item(0, N, Index, $(Array,_,_,_), Item) :-
	N1 is N-2,
	Subindex is Index >> N1 /\ 3,
	array_item(Subindex, N1, Index, Array, Item).
array_item(1, 0, Index, $(_,Item,_,_), Item) :- !,
	not_undef(Item).
array_item(1, N, Index, $(_,Array,_,_), Item) :-
	N1 is N-2,
	Subindex is Index >> N1 /\ 3,
	array_item(Subindex, N1, Index, Array, Item).
array_item(2, 0, Index, $(_,_,Item,_), Item) :- !,
	not_undef(Item).
array_item(2, N, Index, $(_,_,Array,_), Item) :-
	N1 is N-2,
	Subindex is Index >> N1 /\ 3,
	array_item(Subindex, N1, Index, Array, Item).
array_item(3, 0, Index, $(_,_,_,Item), Item) :- !,
	not_undef(Item).
array_item(3, N, Index, $(_,_,_,Array), Item) :-
	N1 is N-2,
	Subindex is Index >> N1 /\ 3,
	array_item(Subindex, N1, Index, Array, Item).


not_undef($) :- !,
	fail.
not_undef(_).


%% [BEFORE OPEN-CODING 'subarray']
%%
%% array_item(0,Index,Item,Item) :- !,
%%	not_undef(Item).
%% array_item(N,Index,Array,Item) :-
%%	N1 is N-2,
%%	Subindex is Index >> N1 /\ 3,
%%	subarray(Subindex,Array,Array1),
%%	array_item(N1,Index,Array1,Item).
%%
%% subarray(0,$(X,_,_,_),X).
%% subarray(1,$(_,X,_,_),X).
%% subarray(2,$(_,_,X,_),X).
%% subarray(3,$(_,_,_,X),X).

update_array_item(0, Index, Item, NewItem, NewItem) :- !.
update_array_item(N, Index, Array, NewItem, NewArray) :-
	N1 is N-2,
	Subindex is Index >> N1 /\ 3,
	update_subarray(Subindex, Array, Array1, NewArray1, NewArray),
	update_array_item(N1, Index, Array1, NewItem, NewArray1).


update_subarray(I, $, X, X1, Array) :- !,
	update_subarray(I, $($,$,$,$), X, X1, Array).
update_subarray(0, $(W,X,Y,Z), W, W1, $(W1,X,Y,Z)).
update_subarray(1, $(W,X,Y,Z), X, X1, $(W,X1,Y,Z)).
update_subarray(2, $(W,X,Y,Z), Y, Y1, $(W,X,Y1,Z)).
update_subarray(3, $(W,X,Y,Z), Z, Z1, $(W,X,Y,Z1)).


subarray_to_list(K, 0, M, Item, [N-Item|L], L) :-
	not_undef(Item), !,
	N is K+M.
subarray_to_list(K, N, M, $(A0,A1,A2,A3), L0, L) :-
	N > 0, !,
	N1 is N-2,
	M1 is (K+M) << 2,
	subarray_to_list(0, N1, M1, A0, L0, L1),
	subarray_to_list(1, N1, M1, A1, L1, L2),
	subarray_to_list(2, N1, M1, A2, L2, L3),
	subarray_to_list(3, N1, M1, A3, L3, L).
subarray_to_list(_, _, _, _, L, L).
 EOF1LOGARR.PL              00010064000100 85045 99364 000011Unix V7             HDR1LONG.PL                00010065000100 85045 99364 000000Unix V7             /* LONG.PL : Arbitrary precision rational arithmetic package.

Copyright (C) 1981 - R.A.O'Keefe.		Updated: 30 August 82

	Designed and written by Richard O'Keefe.
	Scenery by Lawrence Byrd.

	This package provides arithmetic for arbitrary precision rational
	numbers.  The normal domain of prolog 'integers' is extended to
	full rational 'numbers'.  This domain includes all Prolog integers.
	The predicate:

			number(N)

	will recognise any number in this extended domain.
	Rational numbers are produced by using the predicates

			eval(Command)

			eval(Expression,Answer)

	Expression can involve any form of rational number, whether such
	numbers can be represented by Prolog integer or not.  Any form of
	number produced as output by "eval" is acceptable as input to it.

	For convenience the Answer produced by eval is normalised as follows:

	a) Integers X (where |X| <= 99999) are represented as Prolog integers;

	b) 1/0, 0/0, -1/0 are represented as infinity, undefined, neginfinity;

	c) All other numbers are represented as full rationals in reduced form
	   i.e. numerator and denominator are relatively prime.

	In the current representation, one normalised number will unify with
	another (including an integer) iff the two numbers are equal.  But it
	is better to test for equality between arbitrary numbers by calling

			eval(N1=:=N2)

	which also handles infinity & undefined, and is guaranteed to work.
	Once created, representations of rational numbers can be passed round
	your program, used with eval, or printed.  The predicate

			portray_number(Number)

	will pretty-print arbitrary numbers, and will fail for anything
	else.  In particular, it will not evaluate an expression.  (But
	eval(write(Expr)) combines evaluation and printing if you want.)
	If this is connected up to your general "portray" mechanism, you
	will never have to see the internal representation of rationals.
	It is ill-advised to write procedures which assume knowledge of
	this internal representation as it is subject to change (rarely),
	not to mention that such activities are against all the principles
	of abstraction and structured programming.

   NB	Note that eval/1 and eval/2 will only evaluate fully numeric
	expressions. If there is some garbage in the expression (such
	as an atom) then no evaluation at all occurs and the whole
	input expression is returned untouched. If you want to evaluate
	mixed symbolic and numeric expressions then use tidy/2 (from
	TIDY.PL) which is designed for this purpose.

FIXES

[3 April 81]

	Added the functions numer(_), denom(_) and sign(_) to the 
	evaluator (ie eva2).

[8 April 81]

	removed choice-points from comq, and corrected sign(.).
	replaced the log routine completely.

[14 April 81]

	changed all XXXr routines to XXXn (for Natural or zero)
	changed all XXXs routines to XXXm (for Modified (Natural routines))
	changed all XXXl routines to XXXz (for the ring Z of integers)
	replaced "digits" by "conn" as I've meant to for some time.
	removed experimental 'xwd' code which doesn't work compiled.  Eheu.
	changed estq,chkq,gest to estd,chkd,estg (estimate division digit,
	check digit, estimate Gcd) to avoid confusion; they don't use rationals.
	rewrote norm_number and renamed it to standardise.
	laid the trig routines out in MY style not Lawrence's.
	Increased the radix from 10,000 to 100,000 after fixing addn to use
	unsigned numbers.

[21 April 81]

	Continued tidying things up.
	made 0^(1/N) = 0; this was an oversight.
	added new xwd(,) code in eva2, and beautified portray_number.

[8 July 81]

	fixed mode error bug in eva2(abs(_),_).  Foolish oversight.

[9 Sept 81]

	fixed negative number bugs in arccos and arcsin.  How long have
	these been around without anyone (except Bernard) noticing?
	Also shunted some cuts around in the same general area.

[13 Sept 81]

	corrected typo {da=>Da} in gcdq/4.

[2 Dec 81]

	corrected a benign bug in number/5 (100000 had been written
	where R should have been), and some minor cosmetic changes.
	Unified error reporting into long_error[_message].  Added a
	few mode declarations for trig functions.

[9 Dec 81]

	when writing eval up for EuroSam, discovered that logs aren't
	handled properly.  Rewrote absq and logq to return 'undefined'
	in more cases, instead of failing.

[27 July 82]

	changed prnq/3 to portray_number/3 and laid it out properly.
	changed prin/1 to putn/1 (put Natural) to avoid conflict elsewhere.	
	Made this stuff call put/1 where it made sense, and used ASCII
	codes instead of strings.  Don't know if it matters, really.
	Also rewrote arctaneval completely, so that it should succeed in a
	few more cases.  Really, the trig stuff is PITIFUL.  Please, will
	someone do a proper job of it (preferrably someone PAID to do it).

[30 August 82]

	fixed bug in gcdq/4 so that gcd(1/2,1/4) = 1/4, not 1/2!

[12 July 1983]

	arctaneval used to call addn/4, and there isn't any such
	predicate.  Made it call addn/5.
*/


:-  public
	number/1,		%  number(N) <=> N is a number
	eval/1,			%  eval(E) => E/rational-eval is true
	eval/2,			%  eval(E,A) => (A is E)/rational-eval
	portray_number/1,	%  writes rational assumed radix 100000.
%				%  Lawrence's Low Level TIDY interface
	add/3,			%  add(A,B,C) => (C is A+B)/rational-eval
	multiply/3,		%  similar for *.  NB A,B must be numbers
	power/3.		%  similar for ^.  NOT general terms.


/* OPERATORS */

:- op(300, xfx, div).		%  integer quotient A div B = fix(A/B).


/* MODES and types */

%   The comments at the right give the argument types for each predicate.
%   The predicates can of course be called with any arguments, but these
%   are the only types they are supposed to work on or deliver.  
%   ? = any Prolog term, possibly including variables.
%   E = an arithmetic Expression, a term.
%   A = a Prolog atom (but not an integer).
%   I = a Prolog integer.  Generally positive, but not always.
%   T = a Truth-value, 'true' or 'false'.
%   S = a Sign, '+' or '-'.  {Sometimes can be 0 or *.}
%   R = a Relational operator, {<, =, >; sometimes =<, >=, =/=}
%   N = a long positive (Natural) number.
%   Q = a rational number.

:- mode

%% Top Level %%

    number(+),					% Q
    eval(+),					% E
    eval(+, -),					% E Q
	eva2(+, -),				% E Q
	    relational_op(+, -, -),		% R R T
	    combine_ops(+, +, +, -),		% R R T T
    portray_number(+),				% Q
	portray_number(+, +, +), 		% S N N
	    putn(+),				% N

%% Conversions %%

    number(+, +, ?, ?, ?),			% Q I S N N
	binrad(+, +, -),			% I I N
    standardise(+, ?),				% Q Q

%% Low Level %%

    add(+, +, ?),				% Q Q Q
    multiply(+, +, ?),				% Q Q Q
    power(+, +, ?),				% Q Q Q

%% Rational Arithmetic %%

    mod2(+, ?),					% Q I
    intq(+,    +, -),				% Q   I Q
    gcdq(+, +, +, -),				% Q Q I Q
/*  invq(+,    +, -),				% Q   I Q  */
    mulq(+, +, +, -),				% Q Q I Q
    divq(+, +, +, -),				% Q Q I Q
    divo(+, +, +, -, -),			% Q Q I Q Q
    powq(+, +, +, -),				% Q Q I Q
    negq(+,    +, -),				% Q   I Q
    addq(+, +, +, -),				% Q Q I Q
    subq(+, +, +, -),				% Q Q I Q
    comq(+, +, +, ?),				% Q Q I R
    nthq(+, +, +, -),				% I Q I Q
	nthn(+, +, +, -),			% I N I N
	    newton(+, +, +, +, -),		% I N N I N
		newton(+, +, +, +, +, -),	% R I N N I N

  %% Long Arithmetic %%

    addz(+,+, +,+, +, -,-),			% S N S N I S N
	addn(+, +, +, +, -),			% N N I I N
	    add1(+, +, -),			% N I N
    comz(+,+, +,+, ?),				% S N S N R
	comn(+, +, +, ?),			% N N R R
	    com1(+, +, +, -),			% I I R R
    subz(+,+, +,+, +, -,-),			% S N S N I S N
	subn(+, +, +, -,-),			% N N I S N
	    subn(+, +, +, +, -,-),		% R N N I S N
		prune(+, -),			% N N
		subp(+, +, +, +, -),		% N N I I N
		    sub1(+, +, -),		% N I N
    sign(+, +, -),				% S S S
/*  mulz(+,+, +,+, +, -,-),			% S N S N I S N  */
	muln(+, +, +, -),			% N N I N
	    muln(+, +, +, +, -),		% N N N I N
		mul1(+, +, +, -),		% N I I N
		    mul1(+, +, +, +, -),	% N I I I N
    powz(+,+, +, +, -,-),			% S N I I S N
	pown(+, +, +, +, -),			% I N N I N
    divz(+,+, +,+, +, -,-, -,-),		% S N S N I S N S N
	divn(+, +, +, -, -),			% N N I N N
	    conn(?, ?, ?),			% I N N
	%   both +, +, - and -, -, + are used.
	    div1(+, +, +, -, -),		% N I I N N
	    divm(+, +, +, -, -),		% N N I N N
		div2(+, +, +, -, -),		% N N I N N
		    estd(+, +, +, -),		% N N I I
		    chkd(+, +, +, +, +, -, -),	% N N I I I I N
/*  gcdz(+,+, +,+, +, -, -,-, -,-),		% S N S N I N S N S N  */
	gcdn(+, +, +, -, -, -),			% N N I N N N
	    gcdn(+, +, +, -),			% N N I N
		gcdn(+, +, +, +, -),		% R N N I N
		    estg(+, +, +, -),		% N N I I

%% Logarithms %%

    logq(+, +, +, -),				%  Q Q I Q
	logq(+,+, +,+,+,-),			%  R R Q Q I Q
	absq(+, -, -),				%  Q S Q
	    logq(+, -,-),			%  S S N
	    oneq(+, -, -),			%  Q R Q
	    ratlog(+, +, +, -),			%  Q Q I Q
		ratlog(+,+, +,+,+, -),		%  S S Q Q I Q
		    lograt(+,+,+, -,-),		%  Q Q I N N
			loop(+, +, +, -),	%  N N I N
			    loop(+,+,+,+,-),	%  N N N I N
			logn(+,+,+,+,-),	%  Q I Q Q I I 

%% Trigonometry %%

    sineval(+, -),				%  Q Q
    coseval(+, -),				%  Q Q
    taneval(+, -),				%  Q Q
    arcsineval(+, -),				%  Q Q
    arccoseval(+, -),				%  Q Q
    arctaneval(+, -),				%  Q Q
	arctaneval(+, +, -, -),			%  N N N N
	sineval1(?, ?),				%  Q Q

%% Error handing %%

    long_error(+, ?),				%  A ?
	long_error_message(+, -).		%  A A


/* Implementation

	The internal representation for rationals is of the form:

		number(Sign, Numerator, Denominator)

		    where
			Sign is in {+,-}
			Numerator is a list of (Prolog) integers
			Denominator is a list of (Prolog) integers

	The lists of Prolog integers represent arbitrary precision unsigned
	long integers

		eg [n0,n1,....,nz]

		    is n0+R*(n1+R*(....R*nz)...)

		    where R is the Radix.

	The Radix used in the current version is 100000. Most of the code
	in this module is completely independent of the radix - it all
	uses the value passed in by the top level procedures. However the
	printing routine currently assumes that the radix is a power of
	10 as this makes things easier. In general the radix must be such
	that both:

			Radix^2 - 1
		   and	Radix*2 + 1

				are representable as Prolog integers (which
	are 18 bit quantities on the DEC10). This is a little restrictive,
	however, and this implementation only assumes that Radix^2 - 1 is
	"obtainable" as an intermediate during Prolog arithmetic. On the
	DEC10 intermediate results can be 36 bit quantities and so 100000
	becomes a suitable radix.

	The code actually unpacks the number terms into their separate
	bits for all the low level operations. At this stage the following
	additional number forms are appropriately converted

		<integer>   -	(Prolog integers)
		infinity    -	represented as +1/0
		neginfinity -	represented as -1/0
		undefined   -	represented as  0/0

	The treatment of these strange things is not supposed to be
	mathematically beautiful, but sensible things happen using
	this representation. They are strictly an extension to the
	rationals and could be removed (with eval failing should 0
	denominator numbers ever get produced) if desired.

	Results from eval are normalised before being returned.
	This operation reverses the above transformation except that
	only integers within the range -99999 to +99999 are turned
	back into Prolog integers.
*/



%% TOP LEVEL PREDICATES %%



			% Number recognition predicate

number(N)      :- integer(N), !.
number(number(S,N,D))	:- !.
number(infinity)	:- !.
number(neginfinity)	:- !.
number(undefined)	:- !.



			% Simple eval interpreter with various features.

eval(Var)      :- var(Var), !, long_error(eval, Var).
eval(B is Y)   :- !, eval(Y, B).
eval(write(Y)) :- !, eval(Y, B), print(B).
eval(even(X))  :- !, eva2(X, A), !, mod2(A, 0).
eval(odd( X))  :- !, eva2(X, A), !, mod2(A, 1).
eval(compare(R,A,B)) :-
		  !, eva2(X, A), eva2(Y, B), comq(A, B, 100000, S), !, R=S.
eval(Term)     :- Term =.. [F,X,Y], relational_op(F, R, Flag),
		  !, eva2(X, A), eva2(Y, B), comq(A, B, 100000, S), !,
		  combine_ops(R, S, Flag, true).

	mod2(number(_,_,[]),    M) :- !, fail.
	mod2(number(_,[],_),    0).
	mod2(number(_,[L|_],_), M) :- M is L mod 2.


			% General evaluation of rational expressions

eval(Exp, Ans) :-	%    Hope for the best
	eva2(Exp, N),
	standardise(N, A), !,
	Ans = A.
eval(Exp, Exp).		%    Cannot evaluate so leave alone
%	ttynl, display('[Couldn''t evaluate: '),
%	print(Exp), ttyput("]"), ttynl, ttynl.



eva2(Var, C)     :- var(Var), !, long_error(eva2, Var).
eva2(X+Y, C)     :- !, eva2(X, A), eva2(Y, B), addq(A, B, 100000, C).
eva2(X-Y, C)     :- !, eva2(X, A), eva2(Y, B), subq(A, B, 100000, C).
eva2( -Y, C)     :- !,             eva2(Y, B), negq(   B, 100000, C).
eva2(X*Y, C)     :- !, eva2(X, A), eva2(Y, B), mulq(A, B, 100000, C).
eva2(X/Y, C)     :- !, eva2(X, A), eva2(Y, B), divq(A, B, 100000, C).
eva2(X div Y, C) :- !, eva2(X, A), eva2(Y, B), divo(A, B, 100000, C, _).
eva2(X mod Y, C) :- !, eva2(X, A), eva2(Y, B), divo(A, B, 100000, _, C).
eva2(X++Y, C)	 :- !, eva2((X+Y) mod 360, C).
eva2(X--Y, C)	 :- !, eva2((X-Y) mod 360, C).
eva2(X^Y, C)     :- !, eva2(X, A), eva2(Y, B), powq(A, B, 100000, C).
eva2(sqrt(Y), C) :- !,		   eva2(Y, B), nthq(2, B, 100000, C).
eva2(pi,number(+,[355],[113])) :- !.
eva2(log(X,Y),C) :- !, eva2(X, A), eva2(Y, B), logq(A, B, 100000, C).
eva2(gcd(X,Y),C) :- !, eva2(X, A), eva2(Y, B), gcdq(A, B, 100000, C).
eva2(fix(X), C)  :- !, eva2(X, A), 	       intq(A,    100000, C).
eva2(sin(X), C)  :- !, eva2(X, A), sineval(A, C).
eva2(cos(X), C)  :- !, eva2(X, A), coseval(A, C).
eva2(tan(X), C)  :- !, eva2(X, A), taneval(A, C).
eva2(arcsin(X),C):- !, eva2(X, A), arcsineval(A, C).
eva2(arccos(X),C):- !, eva2(X, A), arccoseval(A, C).
eva2(arctan(X),C):- !, eva2(X, A), arctaneval(A, C).
eva2(abs(X),   number(+,N, D )) :- !, eva2(X, A), A = number(_,N,D).
eva2(numer(X), number(+,N,[1])) :- !, eva2(X, A), A = number(_,N,_).
eva2(denom(X), number(+,D,[1])) :- !, eva2(X, A), A = number(_,_,D).
eva2(sign(X),  number(S,B,[1])) :- !, eva2(X, A), A = number(S,N,_),
				      (N=[], B=[]; B=[1]), !.
eva2(xwd(X,Y),C) :- !, U is !(Y)>>9, V is !(Y)/\511,
		    eva2((X*512+U)*512+V, C).
eva2(X,       C) :- number(X, 100000, S, N, D), !, C = number(S, N, D).
eva2(Term,    C) :- Term =.. [F,X,Y], relational_op(F,R, Flag),
	            !, eva2(X, A), eva2(Y, B), comq(A, B, 100000, S), !,
	            combine_ops(R, S, Flag, C).


	relational_op(  =, =, true).
	relational_op( \=, =, false).
	relational_op(  <, <, true).
	relational_op( >=, <, false).
	relational_op(  >, >, true).
	relational_op( =<, >, false).
	relational_op(=:=, =, true).
	relational_op(=\=, =, false).

	combine_ops(Sign, Sign, Flag,  Ans) :- !, Ans = Flag.
	combine_ops(Sign, Diff, true,false) :- !.
	combine_ops(Sign, Diff, false,true) :- !.



			% Pretty-Print a number.
			%  This now always forces parentheses. When a
			%  proper general portray handler is written
			%  this could be made cleverer (as it once was).
			%  The magic numbers are 40 = "(", 41 = ")",
			%  45 = "-", 47 = "/", 48 = "0" {ASCII codes}.

portray_number(A) :-
	number(A, 100000, S, N, D),	!,
	portray_number(S, N, D).

	portray_number(_,[],  []) :- !,		%  0/0 = undefined
		write(undefined).
	portray_number(+, N,  []) :- !,		% +N/0 = +infinity
		write(infinity).
	portray_number(-, N,  []) :- !,		% -N/0 = -infinity
		write(neginfinity).
	portray_number(+, N, [1]) :- !,		% +N/1 = a +ve integer
		putn(N).
	portray_number(-, N, [1]) :- !,		% -N/1 = a -ve integer
		put(45), putn(N).
	portray_number(+, N,  D ) :- !,		% +N/D = a +ve rational
		put(40), putn(N), put(47), putn(D), put(41).
	portray_number(-, N,  D ) :- !,		% -N/D = a -ve rational
		put(40), put(45), putn(N), put(47), putn(D), put(41).

		putn([]   ) :- !, put(48).
		putn([D]  ) :- !, write(D).
		putn([D|T]) :- 
			putn(T),
			D4 is (D/10000)	      +48, put(D4),	% D4*10^4 +
			D3 is (D/1000) mod 10 +48, put(D3),	% D3*10^3 +
			D2 is (D/100) mod 10  +48, put(D2),	% D2*10^2 +
			D1 is (D/10) mod 10   +48, put(D1),	% D1*10^1 +
			D0 is (D) mod 10      +48, put(D0).	% D0*10^0 = D.


%% INTERFACE CONVERSIONS %%

			% Conversion of a number, of any form, to its
			%  essential bits.

number(infinity,        R, +,[1], []) :- !.
number(neginfinity,     R, -,[1], []) :- !.
number(undefined,	R, +, [], []) :- !.
number(number(S, N, D), R, S,  N,  D) :- !.
number(N, R, +, L, [1]) :- integer(N), N >= 0, !,          binrad(N, R, L).
number(N, R, -, L, [1]) :- integer(N), N  < 0, !, M is -N, binrad(M, R, L).

	binrad(0, R, [])    :- !.
	binrad(N, R, [M|T]) :- K is N/R, M is N mod R, !, binrad(K, R, T).



			% Normalise a number

standardise(number(S,[N],[1]), Ans) :- !,
	(   S = '+', Ans = N
	;   S = '-', Ans is -N
	),  !.
standardise(number(_, [],[1]),  0 ) :- !.
standardise(number(S,  N, []), Ans) :- !,
	(   N =  [], Ans = undefined
	;   S = '+', Ans = infinity
	;   S = '-', Ans = neginfinity
	),  !.
standardise(Number,         Number).




%% LOW LEVEL INTERFACE %%



			% These routines provide a low level interface
			%  for procedures which want to operate directly
			%  on pairs of numbers.
			% Only currently used by TIDY (27/2/81),
			%  so only those necessary are provided.

add(A, B, C) :-		% eval(C is A+B).
	addq(A, B, 100000, X),
	standardise(X, C).

multiply(A, B, C) :-	% eval(C is A*B).
	mulq(A, B, 100000, X),
	standardise(X, C).

power(A, N, C) :-	% eval(C is A^B).
	powq(A, N, 100000, X),
	standardise(X, C).


%% BASIC ARITHMETIC OVER RATIONALS %%


			% Integer part of a rational

intq(A, R, number(S, Q, [1])) :-
	number(A, R, S, N, D),
	divn(N, D, R, Q, _).



			%   The greatest common divisor of two numbers is
			%   defined for all pairs of non-zero rationals.
			%   gcd(X,Y) = Z iff Z > 0 and there are integers
			%   M,N relatively prime for which X=MZ & Y=NZ.

gcdq(A, B, R, number(+,Nd,Dd)) :-
	number(A, R, _, Na, Da),
	number(B, R, _, Nb, Db),
	gcdn(Da, Db, R, _, Ga, Gb),
	muln(Gb, Na, R, Ma),
	muln(Ga, Nb, R, Mb),
	gcdn(Ma, Mb, R, Nd),
	muln(Gb, Da, R, Dd).

/*	The above seems to be right, but I'm not sure.  This IS right.
gcdq(A, B, R, number(+,Nd,Dd)) :-
	number(A, R, _, Na, Da),	%  |A| = Na/Da
	number(B, R, _, Nb, Db),	%  |B| = Nb/Db
	muln(Na, Db, R, N1),		%  N1 = Na.Db
	muln(Nb, Da, R, N2),		%  N2 = Nb.Da
	gcdn(N1, N2, R, Nc),		%  Nc = gcd(Na.Db, Nb.Da)
	muln(Da, Db, R, Dc),		%  Dc = Da.Db
	gcdn(Nc, Dc, R, _, Nd, Dd).	%  Nd/Dd = Nc/Dc in standard form
*/

/*			% Take the inverse of a rational

invq(A, R, number(S, D, N)) :-
	number(A, R, S, N, D).

*/

			% Multiplication of two rationals

mulq(A, B, R, number(Sc, Nc, Dc)) :-
	number(A, R, Sa, Na, Da),
	number(B, R, Sb, Nb, Db),
	sign(Sa, Sb, Sc),
	gcdn(Na, Db, R, _, Na1, Db1),
	gcdn(Da, Nb, R, _, Da1, Nb1),
	muln(Na1, Nb1, R, Nc),
	muln(Da1, Db1, R, Dc).



			% Division of two rationals

divq(A, B, R, number(Sc, Nc, Dc)) :-
	number(A, R, Sa, Na, Da),
	number(B, R, Sb, Nb, Db),
	sign(Sa, Sb, Sc),
	gcdn(Na, Nb, R, _, Na1, Nb1),
	gcdn(Da, Db, R, _, Da1, Db1),
	muln(Na1, Db1, R, Nc),
	muln(Da1, Nb1, R, Dc).



			% Quotient and remainder of two rationals

divo(A, B, R, number(Sq,Nq,[1]), number(Sx,Nx,Dx)) :-
	number(A, R, Sa, Na, Da),	%  A = Sa.Na/Da
	number(B, R, Sb, Nb, Db),	%  B = Sb.Nb/Db
	muln(Na, Db, R, N1),		%  A/B = (Sa.Na.Db)/(Sb.Nb.Da)
	muln(Nb, Da, R, D1),		%      = (Sa.N1)/(Sb.D1)
	divz(Sa,N1, Sb,D1, R, Sq,Nq, Sx,Ny),
	muln(Da, Db, R, Dy),		%  A/B = Q + (Sx.Ny)/(Sb.Nb.Da)
	gcdn(Ny, Dy, R, _, Nx, Dx).	%  A = Q.B + (Sx.Ny)/Dy



			% Exponentiation of rationals
			%  This is always defined for (positive or
			%   negative) integer powers, however there
			%   is a current implementation restiction that
			%   the power be between -99999 and +99999 (ie
			%   within the current Radix).
			%  This may be defined for some rational powers
			%   but since there are results from this which are
			%   not representable as rationals it will fail
			%   in such cases.  The code for rational powers
			%   relies on numerator and denominator being
			%   relatively prime, which is standard.

powq(A, B, R, C) :-
	number(B, R, S, N, [1]), !,
	powq(S, N, A, R, C).
powq(A, B, R, C) :-
	number(B, R, S, N, [D]),
	nthq(D, A, R, X), !,
	powq(S, N, X, R, C).

	powq(S, [], A, R, number(+,[1],[1])) :- !.
	powq(+,[N], A, R, number(Sc, Nc, Dc)) :- !,
		number(A, R, Sa, Na, Da),
		powz(Sa, Na, N, R, Sc, Nc),
		pown(N,  Da,[1],R,     Dc).
	powq(-,[N], A, R, number(Sc, Nc, Dc)) :- !,
		number(A, R, Sa, Na, Da),
		powz(Sa, Da, N, R, Sc, Nc),
		pown(N,  Na,[1],R,     Dc).



			% Negate a rational

negq(A, R, number(Sc, Nc, Dc)) :-
	number(A, R, Sa, Nc, Dc),
	(   Nc = [], Dc = [], Sc = +		%  -undefined=undefined
	;   sign(Sa, -, Sc)			%  -0 = -(0) now.
	),  !.



			% Addition of two rationals

addq(A, B, R, number(Sc, Nc, Dc)) :-
	number(A, R, Sa, Na, Da),
	number(B, R, Sb, Nb, Db),
	muln(Na, Db, R, Xa),
	muln(Nb, Da, R, Xb),
	addz(Sa,Xa, Sb,Xb, R, Sc,Xc),
	gcdn(Xc, Da, R, _, Nx, Ya),
	gcdn(Nx, Db, R, _, Nc, Yb),
	muln(Ya, Yb, R, Dc), /*Q'*/ Nc/Dc\==[]/[], !.
addq(A, B, R, number(Sc, Nc, [])) :- /*Q'*/
	number(A, R, Sa, Na, Da),
	number(B, R, Sb, Nb, Db),
	(   Na\==[], Nb\==[], Sa==Sb, Sc=Sa, Nc=[1]
	;   Sc= +, Nc=[]
	),  !.



			% Subtraction of two rationals

subq(A, B, R, number(Sc, Nc, Dc)) :-
	number(A, R, Sa, Na, Da),
	number(B, R, Sb, Nb, Db),
	muln(Na, Db, R, Xa),
	muln(Nb, Da, R, Xb),
	subz(Sa,Xa, Sb,Xb, R, Sc,Xc),
	gcdn(Xc, Da, R, _, Nx, Ya),
	gcdn(Nx, Db, R, _, Nc, Yb),
	muln(Ya, Yb, R, Dc), /*Q'*/ Nc/Dc\==[]/[], !.
subq(A, B, R, number(Sc, Nc, [])) :- /*Q'*/
	number(A, R, Sa, Na, Da),
	number(B, R, Sb, Nb, Db),
	(   Na\==[], Nb\==[], Sa\==Sb, Sc=Sa, Nc=[1]
	;   Sc= +, Nc=[]
	),  !.



			% Comparison of two rationals

comq(A, B, R, S) :-
	number(A, R, Sa, Na, Da), /*Q'*/ Na/Da \== []/[],
	number(B, R, Sb, Nb, Db), /*Q'*/ Nb/Db \== []/[],
	muln(Na, Db, R, Xa),
	muln(Nb, Da, R, Xb),	!,
	comz(Sa, Xa, Sb, Xb, S).



			% Try to find Nth root
			%  This will fail in cases where the solution is
			%  not representable as a rational

nthq(N, A, R, number(+, Nr, Dr)) :-
	number(A, R, +, Na, Da), !,
	nthn(N, Na, R, Nr),
	nthn(N, Da, R, Dr).
nthq(N, A, R, number(-, Nr, Dr)) :-
	number(A, R, -, Na, Da), !,
	1 is N mod 2,
	nthn(N, Na, R, Nr),
	nthn(N, Da, R, Dr).

	nthn(N,  [], R,  []) :- !.
	nthn(N, [1], R, [1]) :- !.
	nthn(N,   A, R,   S) :-
		newton(N, A, A, R, S), !,
		pown(N, S, [1], R, B), !, B=A.	% check that S^N=A !

		newton(N, A, E, R, S) :-
			M is N-1,
			pown(M, E, [1], R, E1),	% E1=E^(N-1)
			mul1(E1,N, R, D2),	% D2=N.E^(N-1)
			muln(E, E1,R, E2),	% E2=E^N
			mul1(E2,M, R, N1),	% N1=(N-1).E^N
			addn(N1,A, 0, R, N2),	% N2=(N-1).E^N+A
			divn(N2,D2,R, F, _),	% F = {(N-1).E^N+A}div{N.E^(N-1)}
			comn(F, E, =,  Z), !,	% F Z E
			newton(Z, N, A, F, R, S).

			newton(<, N, A, F, R, S) :- !, newton(N, A, F, R, S).
			newton(=, N, A, F, R, F) :- !.



			% Take the logarithm of a rational to a rational base.
			% This can be expected to fail for almost every pair
			% of rational numbers.  To keep the search space within


%   logq(B, X, R, L) is true iff
%	B, X, and L are rationals such that B^L = X.
%   This does its best for strange mixtures, like log(-3,-27) = 3.

logq(B, X, R, L) :-
	absq(B, S, C),	%   B S 0 & |B| = C
	absq(X, T, Y),	%   X T 0 & |X| = Y
	logq(S, T, C, Y, R, L).

	%   absq(A, R, S, B) is true iff
	%	A and B are rationals, |A| = B, and
	%	S = {+,-,0,*} as A {<,=,>} 0 or is undefined.

	absq(number(Sa,[],[]),  *, number(+,[],[]))  :- !.
	absq(number(Sa,[],Da),  0, number(+,[],[1])) :- !.
	absq(number(Sa,Na,Da), Sa, number(+,Na,Da)).

	%   logq(S, T, ...) is just a case analysis of logq.

	logq(+, +, B, X, R, L) :- !,
		ratlog(B, X, R, L).
	logq(-, +, B, X, R, L) :- !,
		ratlog(B, X, R, L),
		mod2(L, 0).		%  L must be "even"
	logq(-, -, B, X, R, L) :- !,
		ratlog(B, X, R, L), !,
		mod2(L, 1).		%  L must be "odd"
	logq(+, -, _, _, _, number(+,[],[])) :- !.
	logq(*, _, _, _, _, number(+,[],[])) :- !.
	logq(_, *, _, _, _, number(+,[],[])) :- !.
	logq(0, _, _, _, _, number(+,[],[])) :- !.
	logq(_, 0, B, X, R, number(Z, N,[])) :- !,
		oneq(B, S, _),
		logq(S, Z,N).

		logq(+, -,[1]) :- !.	%  log(B,0) = -inf for 1<B<inf
		logq(-, +,[1]) :- !.	%  log(B,0) = +inf for 0<B<1
		logq(_, +,[]).		%  log(B,0) = ???? otherwise

		%  oneq(A, S, B) is true when A and B are positive
		%  defined rationals, |log A| = log B, and S = sign(log A).

		oneq(number(_, _,[]), *, number(+,[1],[])) :- !.
		oneq(number(_,Na,Na), 0, number(+,Na,Na)) :- !.
		oneq(number(_,Na,Da), +, number(+,Na,Da)) :-
			comn(Na, Da, =, >), !.
		oneq(number(_,Na,Da), -, number(+,Da,Na)).


		%   ratlog(B, X, R, L) is true iff
		%	B, X > 0 and B^L = X.

		ratlog(B, X, R, L) :-
			oneq(B, S, C),	%  B S 1 & |log B| = log C
			oneq(X, T, Y),!,%  X T 1 & |log X| = log Y
			ratlog(S, T, C, Y, R, L).

			%  ratlog(S,T, ...) is just a case analysis

			ratlog(+, +, B, X, R, number(+,N,D)) :- !,
				lograt(B, X, R, N, D).
			ratlog(+, -, B, X, R, number(-,N,D)) :- !,
				lograt(B, X, R, N, D).
			ratlog(-, +, B, X, R, number(-,N,D)) :- !,
				lograt(B, X, R, N, D).
			ratlog(-, -, B, X, R, number(+,N,D)) :- !,
				lograt(B, X, R, N, D).
			ratlog(0, _, _, _, _, number(+,[], [])) :- !.
			ratlog(_, 0, _, _, _, number(+,[],[1])) :- !.
			ratlog(+, *, _, _, _, number(+,[1],[])) :- !.
			ratlog(-, *, _, _, _, number(-,[1],[])) :- !.
			ratlog(_, *, _, _, _, number(+,[], [])) :- !.
			ratlog(*, _, _, _, _, number(+,[], [])) :- !.

%   lograt(B, X, R, N, D) is true iff
%	B > 1, X > 1 are rationals, B^N = X^D, and gcd(N,D) = 1.

lograt(number(+,Nb,Db), number(+,Nx,Dx), R, [N], [D] ) :-
	gcdn(Db, Nx, R, U), !, U = [1],		%  Db co-prime Nx
	gcdn(Nb, Dx, R, V), !, V = [1],		%  Nb co-prime Dx
	loop(Nb, Nx, R, G), !,
	logn(G, 1, G, Nb, R, D), !,		%  D=log(G,Nb)
	logn(G, 1, G, Nx, R, N), !,		%  N=log(G,Nx)
	pown(N, Db, [1], R, K1),
	pown(D, Dx, [1], R, K2), !,
	K1 = K2.				%  Db^N = Dx^D

	loop(A, B, R, G) :-
		comn(A, B, =, S), !,
		loop(S, A, B, R, G).

		loop(=, A, B, R, A) :- !.
		loop(<, A, B, R, G) :-
			divn(B, A, R, Q, X), X = [], !,
			loop(A, Q, R, G).
		loop(>, A, B, R, G) :-
			divn(A, B, R, Q, X), X = [], !,
			loop(Q, B, R, G).

	%   logn(B, N, P, X, R, L) is true iff
	%	X >= B > 1, P = B^N, and X = B^L.

	logn(B, N, X, X, R, N) :- !.
	logn(B, N, P, X, R, L) :-
		comn(P, X, =, <),
		muln(B, P, R, Q),
		M is N+1, !,
		logn(B, M, Q, X, R, L).


%% BASIC ARITHMETIC OVER LONG INTEGERS %%


			% Addition of two long integers

addz(+,A, +,B, R, +,C) :- !, addn(A, B, 0, R, C).
addz(+,A, -,B, R, S,C) :- !, subn(A, B, R, S, C).
addz(-,A, +,B, R, S,C) :- !, subn(B, A, R, S, C).
addz(-,A, -,B, R, -,C) :- !, addn(B, A, 0, R, C).

	addn([D1|T1], [D2|T2], Cin, R, [D3|T3]) :-
	        Sum is D1+D2+Cin,
		(   !(Sum) >= R, Cout = 1, D3 is !(Sum)-R
		;   !(Sum) <  R, Cout = 0, D3 =  Sum
		),  !,
		addn(T1, T2, Cout, R, T3).
	addn([], L, 0, R, L) :- !.
	addn([], L, 1, R, M) :- !, add1(L, R, M).
	addn(L, [], 0, R, L) :- !.
	addn(L, [], 1, R, M) :- !, add1(L, R, M).

		add1([M|T], R, [N|T]) :- N is M+1, N < R, !.
		add1([M|T], R, [0|S]) :- R is M+1, !, add1(T, R, S).
		add1([],    R, [1]).



			% Comparison of two long integers

comz(_,[],_,[],S) :- !, S = '='.	% -0 = 0 now, alas.
comz(+,A, +,B, S) :- !, comn(A, B, =, S).
comz(+,A, -,B, >).
comz(-,A, +,B, <).
comz(-,A, -,B, S) :- !, comn(B, A, =, S).

	comn([D1|T1], [D2|T2], D, S) :-
	        com1(D1, D2, D, N), !,
	        comn(T1, T2, N, S).
	comn([],      [],      D, S) :- !, S = D.
	comn([],      L,       D, <) :- !.
	comn(L,       [],      D, >) :- !.

		com1(X, X, D, D) :- !.
		com1(X, Y, D, <) :- X < Y, !.
		com1(X, Y, D, >) :- X > Y, !.



			% Subtraction of two long integers

subz(+,A, +,B, R, S,C) :- !, subn(A, B, R, S, C).
subz(+,A, -,B, R, +,C) :- !, addn(A, B, 0, R, C).
subz(-,A, +,B, R, -,C) :- !, addn(B, A, 0, R, C).
subz(-,A, -,B, R, S,C) :- !, subn(B, A, R, S, C).

	subn(A, B, R, S, C) :-
		comn(A, B, =, O), !,  %  Oh for Ordering
		subn(O, A, B, R, S, C).

		subn(<, A, B, R, -, C) :- !, subp(B, A, 0, R, D), prune(D, C).
		subn(>, A, B, R, +, C) :- !, subp(A, B, 0, R, D), prune(D, C).
		subn(=, A, B, R, +,[]) :- !.

			prune([0|L], M ) :- !,
			        prune(L, T),
			        (T = [], M = []; M = [0|T]).
			prune([D|L], [D|M]) :- !,
			        prune(L, M).
			prune([],    []) :- !.

		subp([D1|T1], [D2|T2], Bin, R, [D3|T3]) :-
			S is D1-D2-Bin,
			(   S >= 0, Bout = 0, D3 =  S
			;   S <  0, Bout = 1, D3 is S+R
			),  !,
			subp(T1, T2, Bout, R, T3).
		subp(L, [], 0, R, L) :- !.
		subp(L, [], 1, R, M) :- !, sub1(L, R, M).

			sub1([0|T], R, [K|S]) :- !, K is R-1, sub1(T, R, S).
			sub1([N|T], R, [M|T]) :- M is N-1.



			% Multiplication of Signs

sign(S, S, +) :- !.
sign(S, T, -) :- !.



			% Multiplication of two long integers
/*
mulz(S,A, T,B, R, U,C) :-
	sign(S, T, U), !,
	muln(A, B, R, C).
*/
	muln([], B, R, []) :- !.
	muln(A, [], R, []) :- !.
	muln(A,  B, R,  C) :- !, muln(A, B, [], R, C).

	muln([D1|T1], N2, Ac, R, [D3|Pr]) :-
	        mul1(N2, D1, R, P2),
	        addn(Ac, P2, 0, R, Sm),
		conn(D3, An, Sm), !,
	        muln(T1, N2, An,R, Pr).
	muln([],      N2, Ac, R, Ac) :- !.

		mul1(A, 0, R, []) :- !.
		mul1(A, M, R, Pr) :- !,
		        mul1(A, M, 0, R, Pr).

		        mul1([], M, 0, R, []) :- !.
		        mul1([], M, C, R, [C]) :- !.
		        mul1([D1|T1], M, C, R, [D2|T2]) :-
		                D2 is (D1*M+C) mod R,
		                Co is (D1*M+C)  /  R,
				mul1(T1, M, Co, R, T2).



			% Exponentiation of a long integer to a short
			%  (Prolog) integer. Note that this means the
			%  power must be less than 100000 (current radix).
			%  This code should always be called with positive
			%  powers.

powz(-,A, N, R, -,C) :-
	N mod 2 =:= 1, !,
	pown(N, A, [1], R, C).
powz(S,A, N, R, +,C) :- !,
	pown(N, A, [1], R, C).

	pown(0, A, M, R, M) :- !.
	pown(1, A, M, R, P) :- !,
	        muln(A, M, R, P).
	pown(N, A, M, R, P) :-
	        N1 is N/2,
		(   N mod 2 =:= 0, M1 = M
		;   N mod 2 =:= 1, muln(A, M, R, M1)
		),
		muln(A, A, R, A1), !,
	        pown(N1, A1, M1, R, P).



			% Division of two long integers

divz(S,A, T,B, R, U,Q, S,X) :-
	sign(S, T, U), !,
	divn(A, B, R, Q, X).

	divn(A, [], R, _, _) :- !, fail. % division by 0 is undefined
	divn(A,[1], R, A,[]) :- !.	 % a very common special case
	divn(A,[B], R, Q, X) :- !,	 % nearly as common a case
		div1(A, B, R, Q, Y),
		conn(Y, [], X).
	divn(A,  B, R, Q, X) :-
		comn(A, B, =, S),
		(   S = '<', Q =  [], X = A
		;   S = '=', Q = [1], X = []
		), !.
	divn(A,  B, R, Q, X) :- !,
		divm(A, B, R, Q, X).

		conn(0, [],   []) :- !.
		conn(D, T, [D|T]).

		div1([D1|T1], B1, R, Q1, X1) :- !,
			div1(T1, B1, R, Q2, X2),
			D2 is (X2*R+D1)  /  B1,
			X1 is (X2*R+D1) mod B1,
			conn(D2, Q2, Q1).
		div1([],      B1, R, [],  0).

% divm(A, B, R, Q, X) is called with A > B > R

		divm([D1|T1], B, R, Q1, X1) :- !,
			divm(T1, B, R, Q2, X2),
			conn(D1, X2, T2),
			div2(T2, B, R, D2, X1),
			conn(D2, Q2, Q1).
		divm([],      B, R, [], []).

			div2(A, B, R, Q, X) :-
				estd(A, B, R, E), !,
				chkd(A, B, R, E, 0, Q, P), !,
				subn(A, P, R, S, X).   %  S=+
			div2(A, B, R, _, _) :-
				long_error(divq, A/B).

				estd([A0,A1,A2], [B0,B1], R, E) :-
					B1 >= R/2, !,
					E is (A2*R+A1)/B1.
				estd([A0,A1,A2], [B0,B1], R, E) :- !,
					L is (A2*R+A1)/(B1+1),
					mul1([B0,B1],    L, R, P),
					subn([A0,A1,A2], P, R, S, N), !, %S=+
					estd(N, [B0,B1], R, M),    !,
					E is L+M.
				estd([A0,A1],    [B0,B1], R, E) :- !,
					E is (A1*R+A0+1)/(B1*R+B0).
				estd([A0],       _,       R, 0) :- !.
				estd([A0|Ar],    [B0|Br], R, E) :- !,
					estd(Ar, Br, R, E).
				estd([],	 _,	  R, 0) :- !.
	
				chkd(A, B, R, E, 3, _, _) :-	   !,
					long_error(divq, A/B).
				chkd(A, B, R, E, K, E, P) :-
					mul1(B, E, R, P),
					comn(P, A, <, <), !.
				chkd(A, B, R, E, K, Q, P) :-
					L is K+1, F is E-1, !,
					chkd(A, B, R, F, L, Q, P).



			% GCD of two long integers
/*
gcdz(S,A, T,B, R, D, S,M, T,N) :- !,
	gcdn(A, B, R, D, M, N).
*/
        gcdn([], [], R, [1],  [],  []) :- !.
        gcdn([],  B, R,   B,  [], [1]) :- !.
        gcdn( A, [], R,   A, [1],  []) :- !.
   	gcdn([1], B, R, [1], [1],   B) :- !.	%  common case
	gcdn( A,[1], R, [1],   A, [1]) :- !.	%  common case
	gcdn( A,  B, R,   D,   M,   N) :-	%  A, B > 1
                gcdn(A, B, R, D),
                divn(A, D, R, M, _),
                divn(B, D, R, N, _).

		gcdn(A, B, R, D) :-		%  A, B >= 1  !!
			comn(A, B, =, S), !,
			gcdn(S, A, B, R, D).

			gcdn(<,[], B, R, B) :- !.
			gcdn(<, A, B, R, D) :-
				estg(B, A, R, E),
				muln(E, A, R, P),
				subn(B, P, R, _, M), !,
				gcdn(A, M, R, D).
			gcdn(>, A,[], R, A) :- !.
			gcdn(>, A, B, R, D) :-
				estg(A, B, R, E),
				muln(E, B, R, P),
				subn(A, P, R, _, M), !,
				gcdn(M, B, R, D).
			gcdn(=, A, B, R, A).

				estg(    A,   [B], R, E) :- !,
					div1(A, B, R, Q, X),
					(   X*2 =< B, E = Q
					;   add1(Q, R, E)
					),  !.
				estg([_|A], [_|B], R, E) :- !,
					estg(A, B, R, E).


%% TRIGONOMETRIC EVALUATION %%

	% This stuff needs some work done on it, and the mode
	% declarations haven't been written yet.  Taihoa.
	% To do:
	%	Since at this stage all the argumentss are known to be
	%	numbers we shouldn't waste time using the general eval.
	%	Approximations should be used so that the routines work
	%	for ANY argument.  Care is needed, since little is known
	%	about rational approximations, lest the numbers explode.




sineval(X, S) :-
	eval(X < 0),	!,
	eva2(-X, Y),
	sineval(Y, T),
	eva2(-T, S).
sineval(X, S) :-
	eval(X > 90),	!,
	eva2(180-X, Y),
	sineval(Y, S).
sineval(X, S) :-	% 0 <= X <= 90
	sineval1(X, S).


	sineval1(number(+,[],[1]),   number(+,[],[1])).
	sineval1(number(+,[30],[1]), number(+,[1],[2])).
	sineval1(number(+,[45],[1]), number(+,[99],[140])).
	sineval1(number(+,[60],[1]), number(+,[45],[52])).
	sineval1(number(+,[90],[1]), number(+,[1],[1])).



coseval(X, C) :-
	eva2(90-X, Y), !,
	sineval(Y, C).


taneval(X, T) :-
	sineval(X, S),
	coseval(X, C), !,
	eva2(S/C, T).


arcsineval(S, X) :-
	eval(S >= 0), !,
	sineval1(X, S).
arcsineval(S, X) :-
	eval(S < 0),
	eva2(-S, T), !,
	sineval1(Y, T),
	eva2(-Y, X).


arccoseval(C, X) :-
	arcsineval(C, Y), !,
	eva2(90-Y, X).


arctaneval(number(S,N,D), number(S,M,C)) :-
	arctaneval(N, D, M, C).
	
arctaneval([],X, [], X) :- !.		%  arctan(0) = 0, arctan(undef) = undef
arctaneval(X, X, [45], [1]) :- !.	%  arctan(1) = 45`
arctaneval(X,[], [90], [1]) :- !.	%  arctan(inf) = 90`
arctaneval(N, D, M, C) :-
	R = 100000,			%  the common radix
	muln(N, N, R, Nsq),
	muln(D, D, R, Dsq),
	addn(Nsq, Dsq, 0, R, Sq), !,
	nthn(2, Sq, R, Den), !,
	arcsineval(number(+,N,Den), S),
	S = number(+,M,C).



%% ERROR HANDLING %%

long_error(Culprit, Expression) :-
	long_error_message(Culprit, Message),
	display('** '), display(Message), display(': '),
	print(Expression), ttynl,
	break, fail.

long_error_message(eval, 'EVAL given a variable').
long_error_message(eva2, 'EVAL given an expression containing a variable').
long_error_message(divq, 'Unexpected rational division problem').

 EOF1LONG.PL                00010065000100 85045 99364 000069Unix V7             HDR1MAP.PL                 00010066000100 85045 99364 000000Unix V7             %   File   : MAP.PL
%   Author : R.A.O'Keefe
%   Updated: 7 June 1984
%   Purpose: Implement finite maps.
%   Needs  : list_to_assoc from ASSOC.PL, ord_disjoint from ORDSET.PL

/*  A finite map is a function from terms to terms with a finite
    domain.  This definition actually implies that its domain
    consists of ground terms, and the code below assumes that.
    The representation is similar to the representation for bags
    (indeed a bag could be regarded as a map from keys to integers),
    that is, the empty map is 'map' and any other map is
	map(Key,Val,Map)
    where Map is a finite map and Key is @< than every key in Map.
*/

:- public
	is_map/1,		%  map ->
	list_to_map/2,		%  list -> map
	map_agree/2,		%  map x map ->
	map_compose/3,		%  map x map -> map
	map_disjoint/2,		%  map x map ->
	map_domain/2,		%  map -> ordset
	map_exclude/3,		%  map x ordset -> map
	map_include/3,		%  map x ordset -> map
	map_invert/2,		%  map -> map
	map_map/3,		%  relation x map -> map
	map_range/2,		%  map -> ordset
	map_to_assoc/2,		%  map -> tree
	map_union/3,		%  map x map -> map
	map_update/3,		%  map x map -> map
	map_update/4,		%  map x key x val -> map
	map_value/3,		%  map x dom -> rng
	portray_map/1.		%  map ->


:- mode
	is_map(+),
	    is_map(+, +),
	list_to_map(+, ?),
	    list_to_map_(+, ?),
	map_agree(+, +),
	    map_agree(+, +, +, +, +, +, +),
	map_compose(+, +, ?),
	    map_compose_(+, +, ?),
		map_compose_(+, +, +, +, +, +, +, ?),
	map_disjoint(+, +),
	map_domain(+, ?),
	map_exclude(+, +, ?),
	    map_exclude(+, +, +, +, +, +, ?),
	map_include(+, +, ?),
	    map_include(+, +, +, +, +, +, ?),
	map_invert(+, ?),
	    map_invert_(+, -),
	map_map(+, +, ?),
	map_range(+, ?),
	    map_range_(+, -),
	map_to_assoc(+, ?),
	map_to_list(+, ?),
	map_union(+, +, ?),
	    map_union(+, +, +, +, +, +, +, ?),
	map_update(+, +, ?),
	    map_update(+, +, +, +, +, +, +, ?),
	map_update(+, +, +, ?),
	    map_update(+, +, +, +, +, +, ?),
	map_value(+, +, ?),
	    map_value(+, +, +, +, ?),
	portray_map(+),
	    portray_map(+, +).

/*
:- type map(Key,Val) --> map | map(Key,Val,map(Key,Val)).

:- pred
	is_map(map(_,_)),
	    is_map(map(K,_), K),
	list_to_map(list(pair(K,V)), map(K,V)),
	    list_to_map_(list(pair(K,V)), map(K,V)),
	map_agree(map(K,V), map(K,V)),
	    map_agree(order, K, V, map(K,V), K, V, map(K,V)),
	map_compose(map(K,M), map(M,V), map(K,V)),
	    map_compose_(list(pair(M,K)), map(M,V), list(pair(K,V))),
		map_compose_(order, M, K, list(pair(M,K)), M, V, map(M,V), list(pair(K,V))),
	map_disjoint(map(K,V), map(K,V)),
	map_domain(map(K,V), list(K)),
	map_exclude(map(K,V), list(K), map(K,V)),
	    map_exclude(order, K, V, map(K,V), K, list(K), map(K,V)),
	map_include(map(K,V), list(K), map(K,V)),
	    map_include(order, K, V, map(K,V), K, list(K), map(K,V)),
	map_invert(map(K,V), map(V,K)),
	    map_invert_(map(K,V), list(pair(V,K))),
	map_map(any, map(K,V1), map(K,V2)),
%	map_map(void(V1,V2), map(K,V1), map(K,V2)), it should be.
	map_range(map(K,V), list(V)),
	    map_range_(map(K,V), list(V)),
	map_to_assoc(map(K,V), tree(K,V)),
	map_to_list(map(K,V), list(pair(K,V))),
	map_union(map(K,V), map(K,V), map(K,V)),
	    map_union(order, K, V, map(K,V), K, V, map(K,V), map(K,V)),
	map_update(map(K,V), map(K,V), map(K,V)),
	    map_update(order, K, V, map(K,V), K, V, map(K,V), map(K,V)),
	map_update(map(K,V), K, V, map(K,V)),
	    map_update(order, K, V, map(K,V), K, V, map(K,V)),
	map_value(map(K,V), K, V),
	    map_value(order, V, map(K,V), K, V),
	portray_map(map(_,_)),
	    portray_map(map(_,_), atom).
*/


%   is_map(Thing)
%   is true when Thing is a map.  If you use the predicates in this
%   file, you have no way of constructing a map with an unbound tail,
%   so such structures are NOT recognised as bags (this avoids a
%   possible infinite loop.

is_map(map).
is_map(map(Key,_,Map)) :-
	nonvar(Map),
	is_map(Map, Key).

is_map(map, _).
is_map(map(Key,_,Map), PreviousKey) :-
	nonvar(Map),
	PreviousKey @< Key,
	is_map(Map, Key).



%   list_to_map(+KeyValList, ?Map)
%   takes a list of Key-Value pairs and orders them to form a representation
%   of a finite map.  The list may not have two elements with the same Key.

list_to_map(List, Map) :-
	keysort(List, Sorted),
	list_to_map_(Sorted, Map).

list_to_map_([], map).
list_to_map_([Key-Val|List], map(Key,Val,Map)) :-
	list_to_map_(List, Map).



%   map_agree(+Map1, Map2)
%   is true if whenever Map1 and Map2 have a key in common, they
%   agree on its value.  If they have no keys in common they agree.

map_agree(_, map) :- !.
map_agree(map, _).
map_agree(map(Key1,Val1,Map1), map(Key2,Val2,Map2)) :-
	compare(R, Key1, Key2),
	map_agree(R, Key1, Val1, Map1, Key2, Val2, Map2).

map_agree(<, _, _, Map1, Key2, Val2, Map2) :-
	map_agree(Map1, map(Key2,Val2,Map2)).
map_agree(>, Key1, Val1, Map1, _, _, Map2) :-
	map_agree(map(Key1,Val1,Map1), Map2).
map_agree(=, _, Val, Map1, _, Val, Map2) :-
	map_agree(Map1, Map2).



%   map_compose(Map1, Map2, Composition)
%   constructs Map1 o Map2.  That is, for each K-T in Map1 such that
%   there is a T-V in Map2, K-V is in Composition.  The way this is
%   done requires the range of Map1 to be ground as well as the domains
%   of both maps, but then any fast composition has the same problem.

map_compose(Map1, Map2, Composition) :-
	map_invert_(Map1, Inv0),
	keysort(Inv0, Inv1),
	map_compose_(Inv1, Map2, Mid0),
	keysort(Mid0, Mid1),
	list_to_map_(Mid1, Composition).

map_compose_(_, map, []) :- !.
map_compose_([], _, []).
map_compose_([Val1-Key1|Map1], map(Key2,Val2,Map2), Composition) :-
	compare(R, Val1, Key2),
	map_compose_(R, Val1, Key1, Map1, Key2, Val2, Map2, Composition).

map_compose_(<, _, _, Map1, Key2, Val2, Map2, Composition) :-
	map_compose_(Map1, map(Key2,Val2,Map2), Composition).
map_compose_(>, Val1, Key1, Map1, _, _, Map2, Composition) :-
	map_compose_([Val1-Key1|Map1], Map2, Composition).
map_compose_(=, Com, Key1, Map1, Com, Val2, Map2, [Key1-Val2|Composition]) :-
	map_compose_(Map1, map(Com,Val2,Map2), Composition).



%   map_disjoint(+Map1, +Map2)
%   is true when the two maps have no domain elements in common.
%   That is, if K-V1 is in Map1, there is no K-V2 in Map2 and conversely.
%   This implementation assumes you have loaded the ordered sets package.

map_disjoint(Map1, Map2) :-
	map_domain(Map1, Dom1),
	map_domain(Map2, Dom2),
	ord_disjoint(Dom1, Dom2).



%   map_domain(+Map, ?Domain)
%   unifies Domain with the ordered set representation of the domain
%   of the finite map Map.  As the keys (domain elements) of Map are
%   in ascending order and there are no duplicates, this is trivial.

map_domain(map, []).
map_domain(map(Key,_,Map), [Key|Domain]) :-
	map_domain(Map, Domain).



%   map_exclude(+Map, +Set, ?Restricted)
%   constructs a restriction of the Map by dropping members of the Set
%   from the Restricted map's domain.  That is, Restricted and Map agree,
%   but domain(Restricted) = domain(Map)\Set.
%   Set must be an *ordered* set.

map_exclude(Map, [], Map) :- !.
map_exclude(map, _, map).
map_exclude(map(Key,Val,Map), [Elt|Set], Restricted) :-
	compare(R, Key, Elt),
	map_exclude(R, Key, Val, Map, Elt, Set, Restricted).

map_exclude(<, Key, Val, Map, Elt, Set, map(Key,Val,Restricted)) :-
	map_exclude(Map, [Elt|Set], Restricted).
map_exclude(>, Key, Val, Map, _, Set, Restricted) :-
	map_exclude(map(Key,Val,Map), Set, Restricted).
map_exclude(=, _, _, Map, _, Set, Restricted) :-
	map_exclude(Map, Set, Restricted).



%   map_include(+Map, +Set, ?Restricted)
%   constructs a restriction of the Map by dropping everything which is
%   NOT a member of Set from the restricted map's domain.  That is, the
%   Restricted and original Map agree, but
%   domain(Restricted) = domain(Map) intersection Set.
%   Set must be an *ordered* set.

map_include(Map, [], Map) :- !.
map_include(map, _, map).
map_include(map(Key,Val,Map), [Elt|Set], Restricted) :-
	compare(R, Key, Elt),
	map_include(R, Key, Val, Map, Elt, Set, Restricted).

map_include(<, _, _, Map, Elt, Set, Restricted) :-
	map_include(Map, [Elt|Set], Restricted).
map_include(>, Key, Val, Map, _, Set, Restricted) :-
	map_include(map(Key,Val,Map), Set, Restricted).
map_include(=, Key, Val, Map, _, Set, map(Key,Val,Restricted)) :-
	map_include(Map, Set, Restricted).



%   map_invert(+Map, ?Inverse)
%   unifies Inverse with the inverse of a finite invertible map.
%   All we do is swap the pairs round, sort, and check that the
%   result is indeed a map.  

map_invert(Map, Inverse) :-
	map_invert_(Map, Inv0),
	keysort(Inv0, Inv1),
	list_to_map_(Inv1, Inverse).

%   map_invert_ takes a list of key-value pairs and swaps the pairs around.

map_invert_(map, []).
map_invert_(map(Key,Val,Map), [Val-Key|Inv]) :-
	map_invert_(Map, Inv).



%   map_map(+Predicate, +Map1, ?Map2)
%   composes Map1 with the Predicate, so that K-V2 is in Map2 if
%   K-V1 is in Map1 and Predicate(V1,V2).  Really, the predicate
%   should come second, but there is this convention that the
%   predicate being mapped always comes first.  It doesn't do
%   marvels for Dec-10 Prolog's indexing either.

map_map(_, map, map).
map_map(Pred, map(K,V1,Map1), map(K,V2,Map2)) :-
	apply(Pred, [V1,V2]),
	map_map(Pred, Map1, Map2).



%   map_range(+Map, ?Range)
%   unifies Range with the ordered set representation of the range of the
%   finite map Map.  Note that the cardinality (length) of the domain and
%   the range are seldom equal, except of course for invertible maps.

map_range(Map, Range) :-
	map_range_(Map, Random),
	sort(Random, Range).

map_range_(map, []).
map_range_(map(_,Val,Map), [Val|Range]) :-
	map_range_(Map, Range).



%   map_to_assoc(+Map, ?Assoc)
%   converts a finite map held as an ordered list of Key-Val pairs to
%   an ordered binary tree such as the library file ASSOC works on.
%   This predicate calls an internal routine of that file, so both
%   must be compiled or both interpreted.  Eventually the two files
%   should be combined.

map_to_assoc(Map, Assoc) :-
	map_to_list(Map, List),
	length(List, N),
	list_to_assoc(N, List, Assoc, []).



%   map_to_list(+Map, ?KeyValList)
%   converts a map from its compact form to a list of Key-Val pairs
%   such as keysort yields or list_to_assoc wants.

map_to_list(map, []).
map_to_list(map(Key,Val,Map), [Key-Val|List]) :-
	map_to_list(Map, List).



%   map_union(+Map1, +Map2, ?Union)
%   forms the union of the two given maps.  That is Union(X) =
%   Map1(X) if it is defined, or Map2(X) if that is defined.
%   But when both are defined, both must agree.  (See map_update
%   for a version where Map2 overrides Map1.)

map_union(Map, map, Map) :- !.
map_union(map, Map, Map).
map_union(map(Key1,Val1,Map1), map(Key2,Val2,Map2), Union) :-
	compare(R, Key1, Key2),
	map_union(R, Key1, Val1, Map1, Key2, Val2, Map2, Union).

map_union(<, Key1, Val1, Map1, Key2, Val2, Map2, map(Key1,Val1,Union)) :-
	map_union(Map1, map(Key2,Val2,Map2), Union).
map_union(>, Key1, Val1, Map1, Key2, Val2, Map2, map(Key2,Val2,Union)) :-
	map_union(map(Key1,Val1,Map1), Map2, Union).
map_union(=, Key, Val, Map1, Key, Val, Map2, map(Key,Val,Union)) :-
	map_union(Map1, Map2, Union).



%   map_update(+Base, +Overlay, ?Updated)
%   combines the finite maps Base and Overlay as map_union does,
%   except that when both define values for the same key, the
%   Overlay value is taken regardless of the Base value.  This
%   is useful for changing maps (you may know it as the "mu" function).

map_update(Map, map, Map) :- !.
map_update(map, Map, Map).
map_update(map(Key1,Val1,Map1), map(Key2,Val2,Map2), Updated) :-
	compare(R, Key1, Key2),
	map_update(R, Key1, Val1, Map1, Key2, Val2, Map2, Updated).

map_update(<, Key1, Val1, Map1, Key2, Val2, Map2, map(Key1,Val1,Updated)) :-
	map_update(Map1, map(Key2,Val2,Map2), Updated).
map_update(>, Key1, Val1, Map1, Key2, Val2, Map2, map(Key2,Val2,Updated)) :-
	map_update(map(Key1,Val1,Map1), Map2, Updated).
map_update(=, _, _, Map1, Key, Val, Map2, map(Key,Val,Updated)) :-
	map_update(Map1, Map2, Updated).



%   map_update(+Map, +Key, +Val, ?Updated)
%   computes an Updated map which is the same as Map except that the
%   image of Key is Val, rather than the image it had under Map if any.
%   This is an O(N) operation, not O(1).  By using trees we could get
%   O(lgN).  Eventually this package should be merged with ASSOC.PL.

map_update(map, Key, Val, map(Key,Val,map)).
map_update(map(Key1,Val1,Map), Key, Val, Updated) :-
	compare(R, Key1, Key),
	map_update(R, Key1, Val1, Map, Key, Val, Updated).

map_update(<, Key1, Val1, Map, Key, Val, map(Key1,Val1,Updated)) :-
	map_update(Map, Key, Val, Updated).
map_update(=, _, _, Map, Key, Val, map(Key,Val,Map)).
map_update(>, Key1, Val1, Map, Key, Val, map(Key,Val,map(Key1,Val1,Map))).



%   map_value(+Map, +Arg, ?Result)
%   applies the finite map Map to an argument, and unifies Result with
%   the answer.  It fails if Arg is not in the domain of Map, or if the
%   value does not unify with Result.  Note that this operation is O(N)
%   like all the others; this package is really meant for working on
%   maps as wholes.  We can achieve O(lgN) by using trees (as in ASSOC),
%   and eventually MAP and ASSOC should be merged.  In the mean time,
%   use map_to_assoc to convert a map to a tree for faster lookup.

map_value(map(Key,Val,Map), Arg, Result) :-
	compare(R, Key, Arg),
	map_value(R, Val, Map, Arg, Result).

map_value(<, _, Map, Arg, Result) :- !,
	map_value(Map, Arg, Result).
map_value(=, Result, _, _, Result).



%   portray_map(+Map)
%   writes a finite Map to the current output stream in a pretty
%   form so that you can easily see what it is.  Note that a map
%   written out this way can NOT be read back in.  The point of
%   this predicate is that you can add a clause
%	portray(X) :- is_map(X), !, portray_map(X).
%   to get maps displayed nicely by print/1.

portray_map(map) :- !,
	write('map{'), write('}').
portray_map(Map) :-
	portray_map(Map, 'map{').

portray_map(map, _) :-
	write('}').
portray_map(map(Key,Val,Map), Prefix) :-
	write(Prefix),
	print(Key), write('->'), print(Val),
	!,
	portray_map(Map, ', ').

EOF1MAP.PL                 00010066000100 85045 99364 000028Unix V7             HDR1MEDIC.PL               00010067000100 85045 99364 000000Unix V7             /*----------------------------------------------------------------------------

	Mode Error Diagnosis in Interpreted Code
	----------------------------------------

		A Prolog Debugging Aid.


    This little package is supposed to help a Prolog programmer find mode
errors in his program.  It provides a new consulting routine called "medic"
which reads an unmodified file just like compile, consult, or reconsult.
The new evaluable predicate "expand_term" provided in version 3 Prolog permits
the use of MEDIC with DCGs and any future extensions to Prolog made in that
way.  A procedure whose mode is being checked may be spied and traced like
an ordinary Prolog procedure, because it IS an ordinary Prolog procedure.

    To use the package, type ".ru medic" instead of ".ru util".  MEDIC is
in the Mecho library area.  It may be used exactly as you would use UTIL.
Then just write "medic(File)" instead of "compile(File)".  The effect of
this is similar to the effect of "reconsult(File)", NOT "consult(File)".

    When a mode violation is detected, an error message will be printed and
you will be put in a break.  If you simply exit from the break with ^Z the
program will continue just as if nothing was wrong.  This will happen EACH
time the error is detected;  you can disable/enable it, however.  First, an
example.  Suppose you said   medic(tom)  where
tom:
	:- mode dick(+, -).
	dick(f(X, Y), Z) :-
		... .
and then called  dick(U, 1+1).  The message will be
	! Mode error: dick(+,-) called by
	dick(_1763,1+1)
	--- break ---

    If you have worked out why some mode is wrong, but would like to keep on
debugging, you can disable the checking by calling
	well(Functor, Arity).
There is still a certain amount of overhead associated with the procedure,
but at least you won't get the error messages any more.  If you change your
mind and want to see error messages again for that procedure, call
	sick(Functor, Arity).

    NB: medic doesn't understand about spy-points.  It will not preserve them,
and neither will "sick" and "well".  Nor is "medication" transparent to spying.
If you want a procedure to be checked and spied, you will have to spy on it
again every time medic does something to it.

----------------------------------------------------------------------------*/

:- public
	medic/1,
	well/2,
	sick/2,
	'med$check'/2.

:- mode
	medic(?),	%  playing safe, should be +
	    rest(+, +),
		handle(+, +, -),
		    modes(+),
			compare(+, +),
			genname(+, -),
		    define(+, +, +, -),
			change(+, +, -),
			passed(+, +, +, -),
			    passed(+, +, +),
		    others(+),
	sick(?, ?), well(?, ?),    %  playing safe again
	    genterms(+, +, +, +, -, -),
	'med$check'(+, +),
	    check_args(+, +).


medic(File) :-
	atom(File),
	seeing(OldFile),
	see(File), !,
	read(Term),
	rest(Term, none),
	seen,
	seeing(OldFile),
	write(File), write(' mediconsulted.'), nl.

%   rest is given two things: the next term to be processed, and a table
%   of the procedures seen so far in this file.  The table is
%	none				- none read yet
%	read(Functor, Arity, Rest)	- Functor/Arity and the Rest

rest(end_of_file, _) :- !.
rest(Other, Read) :-
	expand_term(Other, Term),
	handle(Term, Read, Seen),
	read(Next), !,
	rest(Next, Seen).

%   handle must cope with six cases:
%	:- public -,.. , - .				{ignore}
%	:- mode   -,.. , - .				{translate & store}
%	:- op     -,.. , - .				{obey}
%	:- reconsult(File).				{recur}
%	:- question.  or ?- question.			{ignore? obey?}
%	assertion.					{translate & assert}

handle(':-'(public(_)),   Read, Read) :- !.
handle(':-'(mode(Modes)), Read, Read) :- !,
	modes(Modes).
handle(':-'(Others),	  Read, Read) :- !,
	others(Others).
handle(':-'(Head, Body),  Read, Seen) :- !,
	define(Head, Tete,Read, Seen),
	assertz(( Tete :- Body )).
handle(Head,       	  Read, Seen) :- !,
	define(Head, Tete, Read, Seen),
	assertz(( Tete :- true )).

%   define(Head, Tete, Read, Seen)  checks whether the goal Head defines
%   some procedure whose mode is to be checked.  If it is, then a stub
%   has already been generated, and the functor is to be renamed, producing
%   a new goal Tete.  If the new goal is the first of its sort in this file
%   then any existing definitions of it should be abolished.

define(Head, Tete, Read, Seen) :-
	functor(Head, OldFunc, Arity),
	change(OldFunc, Arity, NewFunc),
	passed(Read, NewFunc, Arity, Seen),
	Head =.. [OldFunc|Args],
	Tete =.. [NewFunc|Args].

	change(OldFunc, Arity, NewFunc) :-
		'med$mode'(OldFunc, Arity, NewFunc, Template), !.
	change(OldFunc, Arity, OldFunc).

%   passed(Read, Functor, Arity, Seen) checks whether Functor/Arity is in
%   Read, in which case Seen=Read {nothing new}, or whether it is not, in
%   which case it is added to Read to form Seen, and any previous version
%   of the procedure is abolished.

passed(Read, Functor, Arity, Read) :-
	passed(Read, Functor, Arity), !.
passed(Read, Functor, Arity, read(Functor, Arity, Read)) :-
	abolish(Functor, Arity).

	passed(read(Functor, Arity, Read), Functor, Arity) :- !.
	passed(read(_,       _,     Read), Functor, Arity) :- !,
		passed(Read, Functor, Arity).

'med$check'(Template, Call) :-
	Template =.. [Functor|ArgModes],
	Call	 =.. [NewFunc|Actuals],
	(   check_args(ArgModes, Actuals)
	;   write('! Mode error: '), write(Template),
	    write(' called by'), nl,
	    Term =.. [Functor|Actuals], write(Term), nl,
	    break
	),  !,
	call(Call).

	check_args([+|Rest], [A|More]) :- !,
		nonvar(A), !,
		check_args(Rest, More).
	check_args([-|Rest], [A|More]) :- !,
		var(A),    !,
		check_args(Rest, More).
	check_args([?|Rest], [A|More]) :- !,
		check_args(Rest, More).
	check_args([],	     []      ).

%   sick(Functor, Arity) asserts that Functor/Arity is to be checked.
%   It generates a stub, e.g.  :- mode dick(+,-)
%   =>	dick(A,B) :- med$check(dick(+,-), med$dick(A,B)).

sick(Functor, Arity) :-
	atom(Functor), integer(Arity), Arity >= 0,
	'med$mode'(Functor, Arity, NewFunc, Template), !,
	abolish(Functor, Arity),  %  remove old stub or code
	genterms(Functor, NewFunc, Arity, [], Head, Call),
	assert((  Head :- 'med$check'(Template, Call)  )),
	!.
sick(Functor, Arity) :-
	write('! MEDIC hasn''t been consulted about '),
	write(Functor/Arity), nl,
	!.

%   well(Functor, Arity)  asserts that Functor/Arity is no longer to be
%   checked.  So it changes the stub to a direct call, e.g.
%	dick(A,B) :- med$dick(A,B).
%   This is easily changed back by "sick".

well(Functor, Arity) :-
	atom(Functor), integer(Arity), Arity >= 0,
	'med$mode'(Functor, Arity, NewFunc, Template), !,
	abolish(Functor, Arity),  %  remove old stub
	genterms(Functor, NewFunc, Arity, [], Head, Call),
	assert((  Head :- Call  )),
	!.
well(Functor, Arity) :-
	write('! MEDIC hasn''t been consulted about '),
	write(Functor/Arity), nl,
	!.

%   genterms(F1, F2, N, [], T1, T2)
%   binds T1 to F1(A,...,Z) and T2 to F2(A,...,Z).

genterms(F1, F2, 0, Args, T1, T2) :- !,
	T1 =.. [F1|Args],
	T2 =.. [F2|Args].
genterms(F1, F2, N, Args, T1, T2) :-
	M is N-1, !,
	genterms(F1, F2, M, [Arg|Args], T1, T2).

%   modes(Modes) is given a comma-list of mode-declarations, which I call
%   "templates" here.  For each template, it checks that the new template
%   doesn't conflict with a previous template for the same procedure.  In
%   any case it creates an entry in the table med$mode and then says that
%   the procedure is "sick".  E.g. given :- mode dick(+,-) it stores
%	med$mode(dick, 2, med$dick, dick(+,-)).
%   and creates the stub
%	dick(A, B) :- med$check(dick(+,-), med$dick(A,B)).
%   MEDIC is free to create any new name in place of med$dick; only this
%   section of the package knows what that name is.  And only "sick/well"
%   know how the run-time checking is done.

modes(','(A,B)) :- !,
	modes(A),
	modes(B).
modes(Template) :-
	functor(Template, Functor, Arity),
	(   retract('med$mode'(Functor, Arity, NewFunc, OldTemp)),
		compare(OldTemp, Template)
	;   genname(Functor, NewFunc)
	),
	assert('med$mode'(Functor, Arity, NewFunc, Template)), !,
	sick(Functor, Arity).

%   compare(Old_template, New_template) checks that the new description
%   doesn't conflict with the old.  At the moment this is a simple = test,
%   but some more complex test might be justifiable.  Might.

compare(Same, Same) :- !.
compare(Old,  New ) :-
	write('! New mode declaration '), write(New),
	write(' conflicts with '), write(Old), nl,
	write('  New declaration accepted.'), nl.

genname(OldAtom, NewAtom) :-
	name(OldAtom, OldName),
	append("med$", OldName, NewName),
	name(NewAtom, NewName).

%   others handles miscellaneous things like "op", "reconsult".
%   perhaps other commands should be obeyed too?

others(','(A,B)) :- !,
	others(A),  !,
	others(B).
others(op(A,B,C)) :- !,
	op(A,B,C).
others(reconsult(A)) :- !,
	medic(A).
others([-A]) :- !,
	medic(A).
others(_).			%   ignore them.

 EOF1MEDIC.PL               00010067000100 85045 99364 000018Unix V7             HDR1METUTL.PL              00010068000100 85045 99364 000000Unix V7             %   File   : METUTL.PL
%   Author : R.A.O'Keefe
%   Updated: 15 September 1984
%   Purpose: meta-logical operations as described in my note

:- public
	compound/1,
	copy/2,
	ground/1,
	occurs_check/2,
	occurs_in/2,
	simple/1,
	subsumes/2,
	subsumes_chk/2,
	subterm/2,
	unify/2,
	variables_of/2,
	variant/2,
	var_member_chk/2.
 
:- mode
	copy(+, ?),
	ground(+),
	    ground(+, +),
	occurs_check(+, ?),
	    occurs_check(+, +, ?),
	occurs_in(+, +),
	    occurs_in(+, +, +),
	subterm(+, ?),
	    subterm(+, +, ?),
	subsumes(+, +),
	    subsumes(+, +, +),
		subsumes(+, +, +, +),
	subsumes_chk(+, +),
	unify(+, +),
	    unify(+, +, +),
	var_member_chk(+, +),
	variables_of(+, -),
	    variables_of(+, +, -),
		variables_of(+, +, +, -),
	variant(+, +).



compound(Term) :-
	nonvar(Term),		%  not a variable
	functor(Term, _, Arity),
	Arity > 0.		%  not atomic
 

simple(Term) :-
	var(Term), !.			%  is a variable
simple(Term) :-				%  -or-
	functor(Term, Term, 0), !.	%  is atomic
simple(Term) :-				%  rationals should be atomic
	number(Term).			%  but aren't so we need this hack.

ground(Term) :-
	nonvar(Term),
	functor(Term, _, N),
	ground(N, Term).
 
ground(0, _) :-
	!.
ground(N, Term) :-
	arg(N, Term, Arg),
	ground(Arg),
	M is N-1, !,
	ground(M, Term).
 

occurs_in(Var, Term) :-
	var(Term),
	!,
	Var == Term.
occurs_in(Var, Term) :-
	functor(Term, _, N),
	occurs_in(N, Var, Term).
 
occurs_in(N, Var, Term) :-
	arg(N, Term, Arg),
	occurs_in(Var, Arg),
	!.
occurs_in(N, Var, Term) :-
	N > 1,
	M is N-1,
	occurs_in(M, Var, Term).
 

subterm(Term, Term).
subterm(SubTerm, Term) :-
	nonvar(Term),
	functor(Term, _, N),
	subterm(N, SubTerm, Term).
 
subterm(N, SubTerm, Term) :-
	arg(N, Term, Arg),
	subterm(SubTerm, Arg).
subterm(N, SubTerm, Term) :-
	N > 1,
	M is N-1,
	subterm(M, SubTerm, Term).


copy(Old, New) :-
	asserta(copy(Old)),
	retract(copy(Mid)), !,
	New = Mid.

occurs_check(Term, Var) :-
	var(Term), !,
	Term \== Var.
occurs_check(Term, Var) :-
	functor(Term, _, Arity),
	occurs_check(Arity, Term, Var).

occurs_check(0, _, _) :- !.
occurs_check(N, Term, Var) :-
	arg(N, Term, Arg),
	occurs_check(Arg, Var),
	M is N-1, !,
	occurs_check(M, Term, Var).

unify(X, Y) :-
	var(X), var(Y),
	!,
	X = Y.		%  want unify(X,X)
unify(X, Y) :-
	var(X),
	!,
	occurs_check(Y, X),		%  X is not in Y
	X = Y.
unify(X, Y) :-
	var(Y),
	!,
	occurs_check(X, Y),		%  Y is not in X
	X = Y.
unify(X, Y) :-
	atomic(X),
	!,
	X = Y.
unify(X, Y) :-
	functor(X, F, N),
	functor(Y, F, N),
	unify(N, X, Y).
	
unify(0, X, Y) :- !.
unify(N, X, Y) :-
	arg(N, X, Xn),
	arg(N, Y, Yn),
	unify(Xn, Yn),
	M is N-1, !,
	unify(M, X, Y).


subsumes_chk(General, Specific) :-
	\+  (	numbervars(Specific, 0, _),
		\+ General = Specific
	    ).

var_member_chk(Var, [Head|_]) :-
	Head == Var,
	!.
var_member_chk(Var, [_|Tail]) :-
	var_member_chk(Var, Tail).


variables_of(Term, Vars) :-
	variables_of(Term, [], Vars).

variables_of(Term, Sofar, Sofar) :-
	var(Term),
	var_member_chk(Term, Sofar),
	!.
variables_of(Term, Sofar, [Term|Sofar]) :-
	var(Term),
	!.
variables_of(Term, Sofar, Vars) :-
	functor(Term, _, N),
	variables_of(N, Term, Sofar, Vars).

variables_of(0, _, Vars, Vars) :- !.
variables_of(N, Term, Sofar, Vars) :-
	arg(N, Term, Arg),
	variables_of(Arg, Sofar, Mid),
	M is N-1, !,
	variables_of(M, Term, Mid, Vars).


subsumes(General, Specific) :-
	variables_of(Specific, Vars),
	subsumes(General, Specific, Vars).

subsumes(General, Specific, Vars) :-
	var(General),
	var_member_chk(General, Vars),
	!,
	General == Specific.
subsumes(General, Specific, Vars) :-
	var(General),
	!,
	General = Specific.	%  binds
subsumes(General, Specific, Vars) :-
	nonvar(Specific),	%  mustn't bind it
	functor(General,  FunctionSymbol, Arity),
	functor(Specific, FunctionSymbol, Arity),
	subsumes(Arity, General, Specific, Vars).

subsumes(0, _, _, _) :- !.
subsumes(N, General, Specific, Vars) :-
	arg(N, General,  GenArg),
	arg(N, Specific, SpeArg),
	subsumes(GenArg, SpeArg, Vars),
	M is N-1, !,
	subsumes(M, General, Specific, Vars).


variant(A, B) :-
	subsumes_chk(A, B),
	subsumes_chk(B, A).

EOF1METUTL.PL              00010068000100 85045 99364 000008Unix V7             HDR1MODULE.MIC             00010069000100 85045 99364 000000Unix V7             .util
*:- compile([
*	''util:write.pl'',
*	''util:module.pl''
*   ]).
*:- plsys(core_image),
*	write(''Use :- translate(mod) to rewrite mod.MPL as mod.PL''), nl.
.save mec:module.exe

EOF1MODULE.MIC             00010069000100 85045 99364 000001Unix V7             HDR1MODULE.PL              00010070000100 85045 99364 000000Unix V7             %   File   : MODULE.PL
%   Author : R.A.O'Keefe
%   Updated: 16 October 1984
%   Purpose: Elementary module system for Dec-10 Prolog.
%   Needs  : append/3 from LISTUT.PL, writef/2 & fwritef/3 from WRITEF.PL,
%   	     and portable_writeq/1 from WRITE.PL (should be portray_clause)

/*  ASSUMING that the user's program has no atoms with colons
    in their names (other than the standard predicate =:=),
    this file simulates a module facility in Dec-10 Prolog
    by renaming predicates.  A predicate foo/5 belonging to
    module baz will be called 'baz:foo'/5.

    This is NOT meant to be a spectacularly good module
    system.  It is a sort of "least common denominator".
    The BSI standard <<MAY>> include something like this as
    "level 1" of its module specifications (level 0 will of
    course be no modules at all).  I have no idea what level
    2 will look like.  Any reasonable module system should be
    able to imitate this, and this will do until a real module
    system comes along.  The main thing it buys you is that it
    greatly reduces the likelihood of name collision.

    A module is identified with a file.
    You say :- translate(module), where module is a Prolog atom,	
    and it translates a "module.MPL" file to an 'equvialent'
    "module.PL" file.  "module" will be taken as the name of the
    module, unless it starts with

	:- module ModuleName.

    There are a number of declarations which are understood:

	:- export p1/n1, ..., pk/nk.

	Declares that these predicates are to be exported.
	This means
	(a) that there MUST be at least one clause for each of
	    these predicates in the module.
	(b) that these predicates MAY not be system or imported.
	(c) that any goal referring to any of them will keep
	    its current name.
	(d) there should be a :- public declaration.

	This information is coded as
		predicate(Symbol, Arity, export, Symbol).

	For backwards compatibility, :- public is treated as :- export.

	:- import p1/n1, ..., pk/nk.

	Declares that these predicates are to be imported.
	This means
	(a) that there MAY NOT be any clauses for any of these predicates.
	(b) that these predicates may not be exported.
	(c) that any goal referring to any of them will keep
	    its current name.

	This information is coded as
		predicate(Symbol, Arity, import, Symbol).

	The module may manipulate data base entries.  Level 1 says that
	these belong to the global name pool.  So they must either be
	imports or exports.  Now if you have compiled clauses for a
	predicate in Dec-10 Prolog, you can't get at them with 'retract'
	or 'clause', so we'd like something that warns the Dec-10 Prolog
	programmer that he has made a mistake (or rather has run into a
	Dec-10 Prolog problem) when he has explicit clauses for something
	he means to hit with assert and retract.  Import fits the bill.
	To make this less than totally obscure,  :- data is allowed as a
	synonym for :- import.  Ideally, the module preprocessor should be
	smart enough to check any explilcit asserts and retracts to ensure
	that they have been declared as data.  Note that there is nothing
	at all to stop someone saying ":- data 'mydb:hack'/3."

	System predicates are held in the table
		system(Goal)
	which is copied into predicate/4 when we start to translate
	a file.  All system predicates are automatically imported.

	Internal predicates are coded as
		predicate(Symbol, Arity, hidden, NewSymbol)
	where e.g. internal predicate foo/5 in module baz would be
		predicate(foo, 5, hidden, 'foo:baz').

	:- mode declarations are renamed, and written out.

	:- op(...) declarations are obeyed and written out.

	Clauses are renamed and written out.  They should really go
	through the pretty-printer, but portray_clause hasn't yet
	been brought back from C Prolog to Dec-10 Prolog, and I have
	no time to do it now.  The source code reformatter will do a
	reasonable job, at Edinburgh it's the TOP edit macro COMMA.EM,
	an EMACS version is in preparation in California.

	Note that the renaming assumes that any predicate not
	known to be exported, imported, or internal is internal.
	If an import or export declaration comes along later
	and says otherwise, that is a mistake.

	One peculiarity is that some fairly basic predicates in
	Dec-10 Prolog actually go through the interpreter, so
	:- public declarations may be needed.  These are not to
	be confused with :- export declarations; if foo/5 in module
	baz will be called by the interpreter, there will be a
	:- public 'foo:baz'/5. declaration.

	There is a table applies(pred(Args), Decl).  Decl is
	'direct', meaning that no :- public declaration is needed,
	or 'called', meaning that a :- public declaration is needed.
	For now, there is no way for the user to add to this table.
	The arguments to pred() are - meaning that the argument is
	not called, or N (>= 0) meaning that it is called with N
	extra arguments.

	The table defined(Symbol, Arity) means that a clause for
	Symbol/Arity (its name in the source file, not in the
	translation) has been seen.

    To run this program:
	compile(['util:listut.pl','util:writef.pl','util:write.pl',
		 'UTIL:MODULE.PL']).
    Then you can use translate/1.  Note that this is not a module
    itself, because it does not live with the programs it translates.
    No doubt this should change, but for now it will do.
*/


:- public
	translate/1.

/* import
	writef/2,
	fwritef/3,
	portable_writeq/1.
*/
:- mode
	applies(+, +, -),
	dec10_correction(+, +, +),
	enter_module(+),
	leave_module,
	leave_module(+, +, +),
	portray_clause(+),		% SORRY THIS ISN'T IN DEC-10 LIBRARY
	process(+),
	process_export(+),
	process_head(+),
	process_import(+),
	process_mode(+),
	process_op(+),
	rewrite_all(+, +, -, +),
	rewrite_all(+, +, +, +, +),
	rewrite_args(+, +, +, +, +),
	rewrite_one(+, -),
	rewrite_one(+, +, +),
	system(-),
	translate(+).

/* data
	defined/2,
	module_name/2,
	predicate/4.
*/
:- op(1199, fx, (module)).
:- op(1199, fx, (export)).
:- op(1199, fx, (import)).
:- op(1199, fx, (data)).


translate(Module) :-
	name(Module, Chars),
	append(Chars, ".MPL", InChars),
	append(Chars, ".PL", ExChars),
	name(InFile, InChars),
	see(InFile),
	name(ExFile, ExChars),
	tell(ExFile),
	enter_module(Module),
	repeat,
	    read(Term),
	    expand_term(Term, Clause),
	    (   Clause = end_of_file
	    ;   process(Clause), fail
	    ),
	!,
	seen,
	told,
	leave_module.


process(:-(public(Preds))) :- !,
	write(':- public'), nl,
	process_export(Preds),
	write(.), nl, nl.
process(:-(export(Preds))) :- !,
	write(':- public'), nl,
	process_export(Preds),
	write(.), nl, nl.
process(:-(import(Preds))) :- !,
	process_import(Preds).
process(:-(data(Preds))) :- !,
	process_import(Preds).
process(:-(module(Module))) :- !,
	leave_module,
	enter_module(Module).
process(:-(mode(Modes))) :- !,
	write(':- mode'), nl,
	process_mode(Modes),
	write(.), nl, nl.
process(:-(Ops)) :- !,
	process_op(Ops).
process(:-(Head,Body)) :- !,
	process_head(Head),
	rewrite_all(0, :-(Head,Body), Clause, direct),
	portray_clause(Clause).
process(Head) :-
	process_head(Head),
	rewrite_one(Head, Clause),
	portray_clause(Clause).


portray_clause(Clause) :-		% THIS SHOULD BE COPIED FROM
	numbervars(Clause, 0, _),	% C PROLOG, WHERE IT IS BASED
	portable_writeq(Clause),	% ON PP.PL.  I've used portable_
	write(.), nl.			% writeq to avoid the ',' problem.


process_head(Head) :-
	nonvar(Head),
	functor(Head, Symbol, Arity),
	atom(Symbol),
	(   predicate(Symbol, Arity, (import), _),
	    fwritef(user,
		'! Imported predicate %t has a clause.\n',
		[Symbol/Arity])
	;   defined(Symbol, Arity)
	;   assert(defined(Symbol, Arity)), nl
	), !.
process_head(Head) :-
	fwritef(user, '! Strange clause head: %t.\n', [Head]),
	fail.


process_export((Pred,Preds)) :-
	process_export(Pred),
	write(','), nl,
	process_export(Preds).
process_export(Symbol/Arity) :-
	put(9), writeq(Symbol/Arity),
	(   predicate(Symbol, Arity, hidden, _),
	    fwritef(user,
		'! :- export %t comes too late -- already assumed hidden.\n',
		[Symbol/Arity])
	;   predicate(Symbol, Arity, (import), _),
	    fwritef(user,
		'! :- export %t impossible -- it is system or imported.\n',
		[Symbol/Arity])
	;   predicate(Symbol, Arity, (export), _)
	;   assert(predicate(Symbol, Arity, (export), Symbol))
	),  !.


process_import((Pred,Preds)) :-
	process_import(Pred),
	process_import(Preds).
process_import(Symbol/Arity) :-
	(   predicate(Symbol, Arity, hidden, _),
	    fwritef(user,
		'! :- import %t comes too late -- already assumed hidden.\n',
		[Symbol/Arity])
	;   predicate(Symbol, Arity, (export), _),
	    fwritef(user,
		'! :- import %t impossible -- it is exported.\n',
		[Symbol/Arity])
	;   predicate(Symbol, Arity, (import), _)
	;   assert(predicate(Symbol, Arity, (import), Symbol))
	),  !.


process_mode((Mode,Modes)) :- !,
	process_mode(Mode),
	write(','), nl,
	process_mode(Modes).
process_mode(Head) :-
	rewrite_one(Head, Copy),
	put(9), writeq(Copy).


process_op((Op,Ops)) :-
	process_op(Op),
	process_op(Ops).
process_op(op(P,T,A)) :-
	write(':-  '), portable_writeq(op(P,T,A)), write(.), nl,
	op(P, T, A).

enter_module(ModuleName) :-
	abolish(predicate, 4),
	abolish(defined, 2),
	abolish(module_name, 2),
	(   system(Goal),
	    functor(Goal, Symbol, Arity),
	    assert(predicate(Symbol, Arity, (import), Symbol)),
	    assert(defined(Symbol, Arity)),
	    fail
	;   true
	),
	name(ModuleName, Chars),
	append(Chars, [0':|Tail], Appended),
	assert(module_name(Tail, Appended)).


%   leave_module is called when we have closed the output file and
%   are writing to the terminal again.  That's why it uses writef
%   instead of fwritef.  It's a failure-driven loop: check that each
%   predicate is defined.

leave_module :-
	(   predicate(Symbol, Arity, Key, _),
	    leave_module(Symbol, Arity, Key),
	    fail
	;   true
	).


leave_module(Symbol, Arity, _) :-
	defined(Symbol, Arity),
	!.
leave_module(Symbol, Arity, (export)) :- !,
	writef('! Predicate %t/%t is %t but not defined.\n',
		[Symbol, Arity, exported]).
leave_module(Symbol, Arity, hidden) :- !,
	writef('! Predicate %t/%t is %t but not defined.\n',
		[Symbol, Arity, used]).
	%  I don't think there are any other cases.


/*  rewrite_one(OldTerm, NewTerm)
    rewrites a single goal to use its new predicate symbol.
    For simplicity, it does this even when it doesn't need
    rewriting.  The arguments are just copied across.
*/

rewrite_one(OldTerm, NewTerm) :-
	nonvar(OldTerm),
	functor(OldTerm, OldSymbol, Arity),
	atom(OldSymbol),
	!,
	predicate(OldSymbol, Arity, NewSymbol),
	functor(NewTerm, NewSymbol, Arity),
	rewrite_one(Arity, OldTerm, NewTerm).
rewrite_one(Term, Term).


rewrite_one(0, _, _) :- !.
rewrite_one(N, OldTerm, NewTerm) :-
	arg(N, OldTerm, Arg),
	arg(N, NewTerm, Arg),
	M is N-1,
	rewrite_one(M, OldTerm, NewTerm).


predicate(Symbol, Arity, NewSymbol) :-
	predicate(Symbol, Arity, _, NewSymbol),
	!.
predicate(Symbol, Arity, NewSymbol) :-
	name(Symbol, Chars),
	module_name(Chars, NewChars),
	name(NewSymbol, NewChars),
	assert(predicate(Symbol, Arity, hidden, NewSymbol)).


/*  The basic idea in handling clauses is that we are rewriting a
    term that will be called with N extra arguments.  (At the top
    level, N is 0.)  We want to rewrite all levels of goals in
    this term, hence the name.
*/

rewrite_all(-, Term, Term, _) :- !.
rewrite_all(ExtraArgs, OldTerm, NewTerm, Called) :-
	nonvar(OldTerm),
	functor(OldTerm, OldSymbol, Arity),
	atom(OldSymbol),
	!,			% compound(OldTerm)
	FullArity is Arity+ExtraArgs,
	predicate(OldSymbol, FullArity, NewSymbol),
	dec10_correction(Called, NewSymbol, FullArity),
	functor(NewTerm, NewSymbol, Arity),
	functor(Template, NewSymbol, FullArity),
	rewrite_args(Template, OldTerm, NewTerm, Arity, Called).
rewrite_all(ExtraArgs, OldTerm, fail, _) :-
	integer(OldTerm),
	!,
	fwritef(user, '! %t/%t will be called as a goal!!\n',
		[OldTerm,ExtraArgs]).
rewrite_all(_, Term, Term, _).


rewrite_args(Template, OldTerm, NewTerm, Arity, OldCalled) :-
	applies(Template, OldCalled, Called),
	!,
	rewrite_all(Arity, OldTerm, NewTerm, Template, Called).
rewrite_args(_, OldTerm, NewTerm, Arity, _) :-
	rewrite_one(Arity, OldTerm, NewTerm).


%   dec10_correction makes sure that a predicate which is supposed 
%   to be internal to the module but is given to the interpreter has
%   a :- public declaration.  There is no need to do this for 'import'
%   and 'export' predicates.  We split 'hidden' into two groups; the
%   'hidden' predicates proper, and the 'called' predicates.  This
%   ensures that each public declaration gets generated just the once.

dec10_correction(direct, _, _) :- !.
dec10_correction(called, NewSymbol, Arity) :-
	retract(predicate(Symbol, Arity, hidden, NewSymbol)),
	!,
	write(':- public '), writeq(NewSymbol/Arity),
	write('.  % for "call"'), nl,
	assert(predicate(symbol, Arity, called, NewSymbol)).
dec10_correction(_, _, _).


rewrite_all(0, _, _, _, _) :- !.
rewrite_all(N, OldTerm, NewTerm, Template, Called) :-
	arg(N, OldTerm, OldArg),
	arg(N, Template, ExtraArgs),
	rewrite_all(ExtraArgs, OldArg, NewArg, Called),
	arg(N, NewTerm, NewArg),
	M is N-1,
	rewrite_all(M, OldTerm, NewTerm, Template, Called).


/*----------------------------------------------------------------------+
|									|
|	The following tables describe Dec-10 Prolog.			|
|	system/1 should be a built in predicate, as in C Prolog.	|
|									|
+----------------------------------------------------------------------*/

system(abolish(_,_)).
system(revive(_,_)).
system(asserta(_,_)).
system(asserta(_)).
system(assertz(_,_)).
system(assertz(_)).
system(retract(_)).
system(clause(_,_,_)).
system(clause(_,_)).
system(recorda(_,_,_)).
system(recordz(_,_,_)).
system(recorded(_,_,_)).
system(instance(_,_)).
system(erase(_)).
system(true).
system(length(_,_)).
system(name(_,_)).
system(op(_,_,_)).
system(var(_)).
system(atom(_)).
system(!).
system(statistics).
system(statistics(_,_)).
system(functor(_,_,_)).
system(call(_)).
system(expand_term(_,_)).
system(debug).
system(debugging).
system(display(_)).
system(get(_)).
system(get0(_)).
system(leash(_)).
system(nl).
system(nodebug).
system(print(_)).
system(put(_)).
system(skip(_)).
system(tab(_)).
system(trace).
system(ttyflush).
system(ttyget(_)).
system(ttyget0(_)).
system(ttynl).
system(ttyput(_)).
system(ttyskip(_)).
system(ttytab(_)).
system(write(_)).
system(writeq(_)).
system(ancestors(_)).
system(depth(_)).
system(maxdepth(_)).
system(subgoal_of(_)).
system(abort).
system(arg(_,_,_)).
system(assert(_)).
system(atomic(_)).
system(bagof(_,_,_)).
system(break).
system(close(_)).
system(compare(_,_,_)).
system(compile(_)).
system(consult(_)).
system(current_atom(_)).
system(current_functor(_,_)).
system(current_predicate(_,_)).
system(current_op(_,_,_)).
system(fail).
system(fileerrors).
system(gc).
system(gcguide(_)).
system(halt).
system(integer(_)).
system(keysort(_,_)).
system(listing).
system(listing(_)).
system(log).
system(nofileerrors).
system(nogc).
system(nolog).
system(nonvar(_)).
system(numbervars(_,_,_)).
system('C'(_,_,_)).		% generated in grammar rules
system(phrase(_,_)).
system(prompt(_,_)).
system(read(_)).
system(reconsult(_)).
system(rename(_,_)).
system(repeat).
system(restore(_)).
system(save(_)).
system(save(_,_)).
system(see(_)).
system(seeing(_)).
system(seen).
system(setof(_,_,_)).
system(sort(_,_)).
system(tell(_)).
system(telling(_)).
system(told).
system(trimcore).
system(plsys(_)).
system(end_of_file).
system('LC').
system('NOLC').
system(_^_).
system((_:-_)).
system((_,_)).
system((_;_)).
system(\+_).
system((_->_)).
system(spy _).
system(nospy _).
system(_=_).
system(_ is _).
system(_==_).
system(_\==_).
system(_=.._).
system(_<_).
system(_>_).
system(_=<_).
system(_>=_).
system(_@<_).
system(_@=<_).
system(_@>=_).
system(_@>_).
system(_=\=_).
system(_=:=_).

applies((0:-0),		X, X).
applies((0,0),		X, X).
applies((0;0),		X, X).
applies(call(0),	X, called).
applies(once(0),	X, called).
applies(phrase(2,-),	X, called).
applies(bagof(-,0,-),	X, called).
applies(setof(-,0,-),	X, called).
applies((0->0),		X, called).	% NB: (a->b;c) doesn't work!
applies(X^0,		X, direct).
applies(\+0,		X, called).
EOF1MODULE.PL              00010070000100 85045 99364 000032Unix V7             HDR1MULTIL.PL              00010071000100 85045 99364 000000Unix V7             %   File   : MULTIL.PL
%   Author : Lawrence Byrd
%   Updated: 18 May 1983
%   Purpose: Multiple-list routines

%   This module runs compiled.  It needs some things from Util:Applic.Pl
%   The style of programming which would find these routines useful is
%   now frowned upon.  However, you may find their structure instructive.

:- public
	mlmaplist/2,
	mlmaplist/3,
	mlmaplist/4,
	mlmember/2,
	mlselect/3.

:- mode
	mlmaplist(+, +), 
	mlmaplist(+, +, ?),
	mlmaplist(+, +, ?, ?),
	mlmember(?, +),
	mlmember(+, +, ?),
	mlselect(?, +, ?),
	mlselect(+, +, ?, ?),
	ml_putback(+, ?, ?),
	ml_taketop(+, -, -),
	ml_allempty(+).



%   ml_taketop(Lists, Heads, Tails)
%   is true when Lists is a list of non-empty lists, Heads is a list
%   whose elements are the heads of the elements of Lists, and Tails
%   is a list whose elements are the tails of Lists.

ml_taketop([], [], []).
ml_taketop([[Head|Tail]|Lists], [Head|Heads], [Tail|Tails]) :-
	ml_taketop(Lists, Heads, Tails).



%   ml_allempty(Lists)
%   is true when Lists is a list, all of whose elements are nil ([]).
%   It is used to test whether all the lists being mapped over have
%   come to an end at once.  Since ml_taketop will succeed precisely
%   when all the lists have at least one member, we could produce a
%   set of routines that terminate when any list runs out by simply
%   omitting this test.  As it is, the ml* routines demand that all
%   the lists be the same length.

ml_allempty([]).
ml_allempty([[]|Tail]) :-
	ml_allempty(Tail).



%   mlmaplist(Pred, Lists)
%   applies Pred to argument tuples which are successive slices of the Lists.
%   Thus mlmaplist(tidy, [Untidy,Tidied]) would apply tidy([U,T]) to each
%   successive [U,T] pair from Untidy and Tidied.  It isn't tail-recursive,
%   because Pred (and hence apply) may backtrack.

mlmaplist(Pred, Lists) :-
	ml_taketop(Lists, Heads, Tails),
	apply(Pred, [Heads]),
	mlmaplist(Pred, Tails).
mlmaplist(_, Lists) :-
	ml_allempty(Lists).



%   mlmaplist(Pred, Lists, Extra)
%   is like mlmaplist/2, but passes the Extra argument to Pred as well
%   as the slices from the Lists.

mlmaplist(Pred, Lists, Extra) :-
	ml_taketop(Lists, Heads, Tails), !,
	apply(Pred, [Heads, Extra]),
	mlmaplist(Pred, Tails, Extra).
mlmaplist(_, Lists, _) :-
	ml_allempty(Lists).



%   mlmaplist(Pred, Lists, Init, Final)
%   is like mlmaplist/2, but has an extra accumulator feature.  Init is
%   the initial value of the accumulator, and Final is the final result.
%   Pred(Slice, AccIn, AccOut) is called to update the accumulator.

mlmaplist(Pred, Lists, AccOld, AccNew) :-
	ml_taketop(Lists, Heads, Tails), !,
	apply(Pred, [Heads, AccOld, AccMid]),
	mlmaplist(Pred, Tails, AccMid, AccNew).
mlmaplist(_, Lists, Accum, Accum) :-
	ml_allempty(Lists).



%   mlmember(Elems, Lists) and mlselect(Elems, Lists, Residues)
%   are the multi-list analogues of member and select.  The definition
%   of mlselect is difficult to blieve; it is almost certainly utterly
%   useless.  But it is retained, as that is how it has always been.

mlmember(Elems, Lists) :-
	ml_taketop(Lists, Heads, Tails), !,
	mlmember(Heads, Tails, Elems).

mlmember(Heads, _, Heads).
mlmember(_, Tails, Elems) :-
	mlmember(Elems, Tails).



mlselect(Elems, Lists, Residues) :-
	ml_taketop(Lists, Heads, Tails), !,
	mlselect(Heads, Tails, Elems, Residues).

mlselect(Heads, Tails, Heads, Tails).
mlselect(Heads, Tails, Elems, Residues) :-
	ml_putback(Heads, Rests, Residues), !,
	mlselect(Elems, Tails, Rests).


%   ml_putback(+Heads, ?Tails, ?Lists)
%   is true when ml_taketop(Lists, Heads, Tails) is true, but is
%   rearranged for efficiency with different calling pattern.  It
%   only exists for the benefit of mlselect, and as the bug in the
%   latter went unnnoticed for 3 years, I) don't suppose it matters
%   much.

ml_putback([], [], []).
ml_putback([Head|Heads], [Tail|Tails], [[Head|Tail]|Lists]) :-
	ml_putback(Heads, Tails, Lists).



 EOF1MULTIL.PL              00010071000100 85045 99364 000008Unix V7             HDR1MUTIL.                 00010072000100 85045 99364 000000Unix V7             %   File   : MUTIL
%   Author : Lawrence Byrd
%   Updated: 17 May 1983
%   Purpose: Load a minimal Utilities Package


%% See MUTIL.MIC which calls this and then sets up a core image %%

% The logical name "util:" is assumed to point to the right area, if you
% are not using TOPS10 version 7.01, or don't understand logical names,
% then just edit them all out.

% The following files, found in UTIL, have been ommited for MUTIL to
% make it smaller:
%			LONG.PL
%			TIDY.PL
%			READIN.PL
%			BAGUTL.PL
%			MULTIL.PL
%
% Ie mainly the rational arithmetic package, plus a couple of less useful
%    bits.


:- [
		'util:util.ops',	% General operator declarations
		'util:arith.ops'	% Arithmetic operator declarations
   ].


:- compile([
		'util:files.pl',	% Manipulate files
		'util:writef.pl',	% Formatted write (writef)
		'util:trace.pl',	% Tracing routines
		'util:listut.pl',	% List routines
		'util:setutl.pl',	% Set routines
		'util:applic.pl',	% Application routines
		'util:flagro.pl',	% Flag handling
		'util:struct.pl',	% Structure crunching
		'util:metutl.pl',	% More structure crunching
		'util:gensym.pl'	% Generate symbols
	    ]).

:- [
		'util:edit.pl',		% Jump to FINE and back
		'util:invoca.pl',	% Invocation routines
		'util:imisce.pl'	% Miscellaneous
   ].


%% Temporary addition (added 18 February 82)

core_image :- plsys(core_image).
EOF1MUTIL.                 00010072000100 85045 99364 000003Unix V7             HDR1MUTIL.MIC              00010073000100 85045 99364 000000Unix V7             ; MUTIL.MIC  -  Load Mutil	'<silence>
;
;	This junk allows for automatic loading believe it or not
;
;	Call as:	/mutil		- to load mutil (normal use)
;			/mutil auto	- used by MAKSYS
;
.on error:backto death
.error ?
.on operator:backto death
.operator !
.goto cont
death::
*^C
*^C
.if ($a = "auto") .let e1 = "error"
! MUTIL.MIC HALTED
.mic return
cont::
.let y = $date.["-",20], d = $date.[1,"-"]+" "+$y.[1,"-"]+" "+$y.["-",4]
.if ($d.[1] = "0") .let d = $d.[2,20]
;
;				 Use latest version of Prolog
.run prolog[400,444]  '<revive>
* :- [''util:mutil''].
* :- version(''Minimal Utilities Package  ('d)
*Copyright (C) 1982 Dept. Artificial Intelligence. Edinburgh'').
* :- core_image,
*    display(''Minimal Utilities Package  ('d)''), ttynl,
*    reinitialise.
.save mutil[400,444]
 EOF1MUTIL.MIC              00010073000100 85045 99364 000002Unix V7             HDR1NOT.HLP                00010074000100 85045 99364 000000Unix V7             File: Util:Not.Hlp	Author: R.A.O'Keefe	Updated: 17 August 1983

#source.
The simple-minded not/1 lives in Util:Invoca.Pl.
Whenever you could use it, you are strongly recommended to use '\+'/1.
The suspicious not/1 lives in Util:Not.Pl.

#purpose.
The simple-minded not/1 was for compatibility with a long-dead version
of Dec-10 Prolog.  It has been retained because some other Prologs use
it.  However, it is always better to use \+ in Dec-10 Prolog, as not/1
is not part of the bare Prolog system, and \+ is no worse in C Prolog.

There are problems with negated goals containing universally quantified
variables.  For example, if you write
	bachelor(X) :- \+married(X), male(X).
you will not be able to enumerate bachelors.  To help you detect such
errors in your code, there is a suspicious version of not/1 which will
report any negated goals containing unbound variables.  

#commands.
The source only defines one public predicate: not/1.

If it detects an error, not/1 will switch on tracing and enter a
break.

not/1 understands the existential quantifier ^ .  See the description
of bagof and setof in the Prolog manual to find out what that means.

EOF1NOT.HLP                00010074000100 85045 99364 000003Unix V7             HDR1NOT.PL                 00010075000100 85045 99364 000000Unix V7             %   File   : NOT.PL
%   Author : R.A.O'Keefe
%   Updated: 17 November 1983
%   Purpose: "suspicious" negation 

/*  This file defines a version of 'not' which checks that there are
    no free variables in the goal it is given to "disprove".  Bound
    variables introduced by the existential quantifier ^ or set/bag
    dummy variables are accepted.  If any free variables are found, 
    a message is printed on the terminal and a break level entered.

    It is intended purely as a debugging aid, though it shouldn't slow
    interpreted code down much.  There are several other debugging
    aids that you might want to use as well, particularly
	unknown(_, trace)
    which will detect calls to undefined predicates (as opposed to
    predicates which have clauses that don't happen to match).

    The predicate free_variables/4 defined in this files is also used
    by the set_of/bag_of code.

    Note: in Dec-10 Prolog you should normally use "\+ Goal" instead
    of "not(Goal)".  In C-Prolog you can use either, and would have to
    do some surgery on pl/init to install this version of "not".  The
    reason that I have called this predicate "not" is so that people
    can choose whether to use the library predicate not/1 (in Invoca.Pl)
    or this debugging one, not because I like the name.
*/

:- public
	(not)/1.		%   new checking denial

:- mode
	explicit_binding(+,+,-,-),
	free_variables(+,+,+,-),
	    free_variables(+,+,+,+,-),
	list_is_free_of(+,+),
	not(+),
	term_is_free_of(+,+),
	    term_is_free_of(+,+,+).


not(Goal) :-
	free_variables(Goal, [], [], Vars),
	Vars \== [], !,
	telling(Old), tell(user),
	nl, write('** '), write(not(Goal)),
	nl, write('-- free variables '), write(Vars),
	nl, break,
	tell(Old), !,
	call(Goal),
	!, fail.
not(Goal) :-
	call(Goal),
	!, fail.
not(_).


%   In order to handle variables properly, we have to find all the 
%   universally quantified variables in the Generator.  All variables
%   as yet unbound are universally quantified, unless
%	a)  they occur in the template
%	b)  they are bound by X^P, setof, or bagof
%   free_variables(Generator, Template, OldList, NewList)
%   finds this set, using OldList as an accumulator.

free_variables(Term, Bound, VarList, [Term|VarList]) :-
	var(Term),
	term_is_free_of(Bound, Term),
	list_is_free_of(VarList, Term),
	!.
free_variables(Term, Bound, VarList, VarList) :-
	var(Term),
	!.
free_variables(Term, Bound, OldList, NewList) :-
	explicit_binding(Term, Bound, NewTerm, NewBound),
	!,
	free_variables(NewTerm, NewBound, OldList, NewList).
free_variables(Term, Bound, OldList, NewList) :-
	functor(Term, _, N),
	free_variables(N, Term, Bound, OldList, NewList).

free_variables(0, Term, Bound, VarList, VarList) :- !.
free_variables(N, Term, Bound, OldList, NewList) :-
	arg(N, Term, Argument),
	free_variables(Argument, Bound, OldList, MidList),
	M is N-1, !,
	free_variables(M, Term, Bound, MidList, NewList).

%   explicit_binding checks for goals known to existentially quantify
%   one or more variables.  In particular \+ is quite common.

explicit_binding(\+ Goal,	       Bound, fail,	Bound      ) :- !.
explicit_binding(not(Goal),	       Bound, fail,	Bound	   ) :- !.
explicit_binding(Var^Goal,	       Bound, Goal,	Bound+Var) :- !.
explicit_binding(setof(Var,Goal,Set),  Bound, Goal-Set, Bound+Var) :- !.
explicit_binding(bagof(Var,Goal,Bag),  Bound, Goal-Bag, Bound+Var) :- !.
explicit_binding(set_of(Var,Goal,Set), Bound, Goal-Set, Bound+Var) :- !.
explicit_binding(bag_of(Var,Goal,Bag), Bound, Goal-Bag, Bound+Var) :- !.


term_is_free_of(Term, Var) :-
	var(Term), !,
	Term \== Var.
term_is_free_of(Term, Var) :-
	functor(Term, _, N),
	term_is_free_of(N, Term, Var).

term_is_free_of(0, Term, Var) :- !.
term_is_free_of(N, Term, Var) :-
	arg(N, Term, Argument),
	term_is_free_of(Argument, Var),
	M is N-1, !,
	term_is_free_of(M, Term, Var).


list_is_free_of([Head|Tail], Var) :-
	Head \== Var,
	!,
	list_is_free_of(Tail, Var).
list_is_free_of([], _).


 EOF1NOT.PL                 00010075000100 85045 99364 000008Unix V7             HDR1NUTIL.HLP              00010076000100 85045 99364 000000Unix V7             ADVICE.PL     Interlisp-like advice package	Needs concat/3, flag/3
APPLIC.PL     contains apply-like routines	Needs append/3
ARC3.PL       Mackworth's "arc consistency"
ARITH.OPS     declarations for PRESS's extra arithmetic operators
ARITH.PL      defines plus/3,times/3,divide/4,succ/2 and so on
ARRAYS.PL     is an updateable arrays facility
ASK.PL        asks questions with one-character answers
ASSOC.PL      contains a binary tree implementation of "association lists"
BACKUP.PL     backs up a file with the extension specified
BAGUTL.PL     contains bag-manipulating utilities
BETWEE.PL     integer enumeration
CLAUSE.PL     convert arbitrary logical form to clauses
COUNT.PL      counts predicates and clauses in a program and its subfiles
DCSG.PL       Definite Clause Slash Grammar preprocessor
DECONS.PL     construct/deconstruct Prolog special forms
EDIT.PL       edits a file then runs mec:prolog
EXPAND.PL     expands simple macros
FILES.PL      contains file-manipulating routines
FLAGRO.PL     manipulates global variables (flags)
FLAT.PL       flatten binary trees to lists, and conversely
FLALL         list of files in UTIL:, suitable for SUBFIL
FLS           list of files in UTIL:, suitable for PRINT
GENSYM.PL     contains the compiled routines cgensym, concat, gensym
GETFIL.PL     read a line, prompt for a file name
GRAPHS.PL     processes graphs (including transitive closure)
HEAPS.PL      contains (mergable) heap-manipulating routines
HELPER.PL     prints extracts from help files
IMISCE.PL     contains \=, casserta, cassertz, clean, diff, gcc
INVOCA.PL     contains forall, once, not
IXREF.PL      Interactive cross-referencer (needs PP.PL)
LIB.PL        looks through directories for a file, then reconsults the file
LIB2.PL       the compiled part of LIB
LISTUT.PL     contains list-manipulating utilities
LONG.PL       contains routines for arithmetic manipulation of rational numbers
MAP.PL        implements functions of (small) finite domain
MEDIC.PL      helps to diagnose mode errors
METUTL.PL     contains meta-logical operations
MULTIL.PL     contains multiple-list routines
MUTIL         is the root for the Minimal UTILities collection
MUTIL.MIC     is used by MAKSYS to build MUTIL.EXE 
NOT.PL        negates "suspiciously"
OCCUR.PL      checks number/place of occurrence, replace at specific position
ORDER.PL      tell when a list is in order (can use any test)
ORDSET.PL     contains ordered-set-manipulating routines
PP.PL         Prolog pretty-printer, easy interface to current_predicate &c
PROJEC.PL     selects Kth argument of each element of a list
PROLOG.TYP    declares types for built-in predicates and minimal utilities
PUTSTR.PL     prints a section of a file (part of new unwritten help system)
QUEUES.PL     defines queue operations
RDSENT.PL     reads a single sentence as a list of words.
RDTOK.PL      is an interface to the Prolog tokeniser
READ.PL       is a Prolog parser written in Prolog (needs RdTok.Pl)
READIN.PL     reads an English paragraph as a list of words.
SETOF.PL      defines set_of, bag_of, findall
SETUTL.PL     contains set-manipulating utilities
STRUCT.PL     defines substitution and other things that should be in METUTL
TEST.PL       tests compiled routines by interpreting them
TIDY.PL       simplifies algebraic expressions, but not much (for PRESS)
TIMING.PL     use with TIMING.POP to get dynamic call counts
TRACE.PL      contains trace routines
TREES.PL      contains routines for updating binary trees
TRYSEE.PL     searches several directories/extensions for a file
TYPE.PL       types file(s) to the screen
TYPECH.PL     checks Prolog types
UPDATE.PL     updates "data-base" relations
UTIL          Root file for the Utilities package.
UTIL.DEF      contains definitions of utilities for XREF
UTIL.MIC      is used by MAKSYS to build the UTILities package
UTIL.OPS      declares operators used by UTIL not already in PROLOG
UTIL.TXT      "Utility is_defined_in File" sorted on both keys (needs updating)
VCHECK.PL     reads each clause of a file and reports unique variables
WRITE.PL      portable versions of display,print,write,writeq
WRITEF.PL     contains formatted output routines
 EOF1NUTIL.HLP              00010076000100 85045 99364 000009Unix V7             HDR1NUTIL2.HLP             00010077000100 85045 99364 000000Unix V7             %   File   : UTIL2.HLP
%   Author : L. Hardman
%   Updated: 7 March 1984
%   Purpose: Maintain an up-to-date list of predicates in [140,143,UTIL].
%   Needs  : No other files.



\=(Item1, Item2)						%IMISCE.PL

add(Rational1, Rational2, Rational1+Rational2)			%LONG.PL
add_element(Element, Set, Set+Element)				%SETUTL.PL
add_to_heap(Heap, Key, Item, NewHeap)				%HEAPS.PL
advise(Predicate, Port, Action)					%ADVICE.PL
advised(Predicate)						%ADVICE.PL
append(List1, List2, List1List2)				%LISTUT.PL
apply(Predicate, Arguments)					%APPLIC.PL
array_length(Array+Updates, Length)				%ARRAYS.PL
array_to_list(Array+Updates, List)				%ARRAYS.PL
ask(Question, Answer)						%ASK.PL
ask(Question, Default, Answer)					%ASK.PL
ask_default_character(Specification, Character)			%ASK.PL
assoc_to_list(Association, List)				%ASSOC.PL

backup(Filename)						%BACKUP.PL
backup(Filename, BackUpExtension)				%BACKUP.PL
bag_inter(Bag1, Bag2, Intersection)				%BAGUTL.PL
bag_of(Template, Generator, Bag)				%SETOF.PL
bag_to_list(Bag, List)						%BAGUTL.PL
bag_to_set(Bag, Set_List)					%BAGUTL.PL
bag_union(Bag1, Bag2, Union)					%BAGUTL.PL
bagmax(Bag, Most_Frequent_Element)				%BAGUTL.PL
bagmin(Bag, Least_Frequent_Element)				%BAGUTL.PL

ca								%PP.PL
ca(AtomPattern)							%PP.PL
ca(AtomPattern, AnswerList)					%PP.PL
case_shift(MixedCases, LowerCase)				%RDSENT.PL
casserta(Clause)						%IMISCE.PL
cassertz(Clause)						%IMISCE.PL
cf								%PP.PL
cf(TermPattern)							%PP.PL
cf(TermPattern, AnswerList)					%PP.PL
cgensym(Prefix, NewAtom)					%GENSYM.PL
chars_to_words(Characters, Words)				%RDSENT.PL
checkand(Predicate, Conjunction)				%APPLIC.PL
checkbag(Predicate, Bag)					%BAGUTL.PL
checklist(Predicate, List)					%APPLIC.PL
clean								%IMISCE.PL
close(File, OldFile)						%FILES.PL
co								%PP.PL
co(OperatorPattern)						%PP.PL
co(OperatorPattern, AnswerList)					%PP.PL
co(Priority, Type, Atom)					%PP.PL
compound(Term)							%METUTL.PL
concat(Const1, Const2, Const1Const2)				%GENSYM.PL
contains(Term, Expression)					%OCCUR.PL
convlist(Rewrite, List, NewList)				%APPLIC.PL
copy(Old, New)							%METUTL.PL
copy_ground(Term, Copy, Substitution)				%STRUCT.PL
correspond(Element1, List1, List2, Element2)			%LISTUT.PL
count								%COUNT.PL
cp								%PP.PL
cp(TermPattern)							%PP.PL
cp(TermPattern, AnswerList)					%PP.PL

del_element(Element, Set, Set_-_Element)			%SETUTL.PL
delete(List, Element, List_-_Element)				%LISTUT.PL
delete(OpenFile)						%FILES.PL
disjoint(Set)							%SETUTL.PL
disjoint(Set1, Set2)						%SETUTL.PL

edit(File)							%EDIT.PL
empty_queue(Queue)						%QUEUES.PL
error(Format, List, Action)					%TRACE.PL
eval(Expression)						%LONG.PL
eval(Expression, Answer)					%LONG.PL
exclude(Predicate, List, SubList)				%APPLIC.PL
expand(File, NewFile)						%EXPAND.PL

fetch(Index, Array, History)					%ARRAYS.PL
file_exists(File)						%FILES.PL
findall(Template, Generator, List)				%SETOF.PL
findall(Template, Generator, SoFar, List)			%SETOF.PL
flag(Flag, InitialValue)					%FLAGRO.PL
flag(Flag, Value, NewValue)					%FLAGRO.PL
forall(Generator, Test)						%INVOCA.PL
freeof(Term, Expression)					%OCCUR.PL
fwritef(File, Format)						%WRITEF.PL
fwritef(File, Format, List)					%WRITEF.PL

gcc(Item)							%IMISCE.PL
gen_assoc(Association, Key, Value)				%ASSOC.PL
gensym(Prefix, Symbol)						%GENSYM.PL
get_assoc(Key, Association , Value)				%ASSOC.PL
get_from_heap(Heap, Key, Item, NewHeap)				%HEAPS.PL
get_label(Index, Tree, Label)					%TREES.PL
give_help							%HELPER.PL
give_help(Area)							%HELPER.PL
give_help(Area, Topic)						%HELPER.PL
ground(Term)							%METUTL.PL

head_queue(Queue, Head)						%QUEUES.PL
heap_size(Heap, Number_of_Elements_in_Heap)			%HEAPS.PL
heap_to_list(Heap, List)					%HEAPS.PL

intersect(Set1, Set2)						%SETUTL.PL
intersect(Set1, Set2, Intersection)				%SETUTL.PL
is_bag(Bag)							%BAGUTL.PL
is_digit(Char_Digit)						%RDSENT.PL
is_endfile(Char_^Z)						%RDSENT.PL
is_layout(Char_Tab_NewLine_^S_etc)				%RDSENT.PL
is_letter(Char_LowerCase+UpperCase)				%RDSENT.PL
is_lower(Char_LowerCase)					%RDSENT.PL
is_newline(Char_NewLine)					%RDSENT.PL
is_paren(Left_'([{<', Right_'>}]) ')				%RDSENT.PL
is_period(Char_'.?!')						%RDSENT.PL
is_punct(Char_',;:')						%RDSENT.PL
is_upper(Char_UpperCase)					%RDSENT.PL
ixref(Files)							%IXREF.PL

join_queue(Element, Queue, NewQueue)				%QUEUES.PL
jump_queue(Element, Queue, NewQueue)				%QUEUES.PL

last(LastElement, List)						%LISTUT.PL
length(Bag, Total_Number_of_Elts, Number_of_Distinct_Elts)	%BAGUTL.PL
length_queue(Queue, Number_of_Elements_in_Queue)		%QUEUES.PL
lib(File)							%LIB2.PL
lib(File, Found)						%LIB2.PL
list_join_queue(List, Queue, NewQueue)				%QUEUES.PL
list_jump_queue(List, Queue, NewQueue)				%QUEUES.PL
list_to_array(List, Array)					%ARRAYS.PL
list_to_assoc(List, Association)				%ASSOC.PL
list_to_bag(List, Bag)						%BAGUTL.PL
list_to_heap(List, Heap)					%HEAPS.PL
list_to_ord_set(List, OrderedSet)				%ORDSET.PL
list_to_queue(List, Queue)					%QUEUES.PL
list_to_tree(List, Tree)					%TREES.PL
listtoset(List, Set)						%SETUTL.PL
load(Files)							%TYPECH.PL

make_queue(Queue)						%QUEUES.PL
make_sub_bag(Bag, SubBag)					%BAGUTL.PL
map_assoc(Predicate, Association, Association)			%ASSOC.PL
map_tree(Predicate, Tree, NewTree)				%TREES.PL
mapand(Rewrite, Conjunction, NewConjunction)			%APPLIC.PL
mapbag(Predicate, Bag, NewBag)					%BAGUTL.PL
maplist(Predicate, List, NewList)				%APPLIC.PL
medic(File)							%MEDIC.PL
member(Element, Multiplicity, Bag)				%BAGUTL.PL
member(Element, Set)						%SETUTL.PL
memberchk(Element, Set)						%SETUTL.PL
merge(List1, List2, MergedList)					%ORDSET.PL
min_of_heap(Heap, SmallestKey, Item)				%HEAPS.PL
min_of_heap(Heap, SmallestKey1, Item1, NextSmallestKey2, Item2)	%HEAPS.PL
multiply(Rational1, Rational2, Rational1*Rational2)		%LONG.PL

nextto(Element_K, Element_K+1, List)				%LISTUT.PL
nmember(Element_K, List, K)					%LISTUT.PL
not(Goal)							%INVOCA.PL
not(Goal)							%NOT.PL
note_lib(File)							%LIB2.PL
note_lib(File, Reconsult_or_Compile)				%LIB2.PL
number(Rational_Number)						%LONG.PL
numlist(LowInteger, HighInteger, List_of_Integers)		%LISTUT.PL

occ(Subterm, Term, Number_of_Occurrences)			%STRUCT.PL
occurs_check(Term, Variable)					%METUTL.PL
occurs_in(Variable, Term)					%METUTL.PL
on(Command, File)						%PP.PL
once(Goal)							%INVOCA.PL
open(File)							%FILES.PL
open(OldFile, File)						%FILES.PL
ord_disjoint(Set1, Set2)					%ORDSET.PL
ord_intersect(Set1, Set2)					%ORDSET.PL
ord_intersect(Set1, Set2, Intersection)				%ORDSET.PL
ord_seteq(Set1, Set2)						%ORDSET.PL
ord_subset(SubSet, Set)						%ORDSET.PL
ord_subtract(Set1, Set2, Set1-Set2)				%ORDSET.PL
ord_symdiff(Set1, Set2, Set1-Set2_+_Set2-Set1)			%ORDSET.PL
ord_union(Set1, Set2, Union)					%ORDSET.PL

p_to_s_graph(P_Graph, S_Graph)					%GRAPHS.PL
pairfrom(Set, Element1, Element2, Residue)			%SETUTL.PL
patharg(Path, Expression, Term)					%OCCUR.PL
perm(List, PermutedList)					%LISTUT.PL
perm2(Item1, Item2, Item1_or_2, Item2_or_1)			%LISTUT.PL
plus(Integer1, Integer2, Integer1+Integer2)			%SUCC.PL
portray_bag(Bag)						%BAGUTL.PL
portray_number(Rational_Number)					%LONG.PL
position(Term, Expression, Path)				%OCCUR.PL
power(Rational1, Rational2, Rational1^Rational2)		%LONG.PL
pp								%PP.PL
pp(TermPattern_or_help)						%PP.PL
prconj(Conjunction)						%WRITEF.PL
prexpr(Expression)						%WRITEF.PL
prlist(List)							%WRITEF.PL
project(Structures, K, List_of_Kth_Arguments)			%PROJEC.PL
put_assoc(Key, Association, New)				%ASSOC.PL
put_label(Index, Tree, Label, NewTree)				%TREES.PL

queue_to_list(Queue, List)					%QUEUES.PL

read(Answer, Variables)						%READ.PL
read_in(Words)							%READIN.PL
read_line(Characters)						%RDSENT.PL
read_sent(Words)						%RDSENT.PL
read_tokens(TokenList, VariableList)				%RDTOK.PL
read_until(Delimiters, Answer)					%RDSENT.PL
redo(File)							%EDIT.PL
remove_dups(List, PrunedList)					%LISTUT.PL
replace(Path, Expression, SubTerm, NewExpression)		%OCCUR.PL
reverse(List, ReversedList)					%LISTUT.PL

s_to_p_graph(S_Graph, P_Graph)					%GRAPHS.PL
s_to_p_trans(S_Graph, Transposed_P_Graph)			%GRAPHS.PL
select(Element, Set, Residue)					%SETUTL.PL
serve_queue(Queue, Head, NewQueue)				%QUEUES.PL
set_of(Template, Generator, Set)				%SETOF.PL
seteq(Set1, Set2)						%SETUTL.PL
sf								%IXREF.PL
sf(Pattern)							%IXREF.PL
sf(Pattern, Files)						%IXREF.PL
sick(Functor, Arity)						%MEDIC.PL
simple(Term)							%METUTL.PL
simple(Term)							%STRUCT.PL
some(Predicate, List)						%APPLIC.PL
sp(Limits)							%IXREF.PL
sp(Limits, Paths)						%IXREF.PL
store(Index, Array+Updates, Element, NewArray+NewUpdates)	%ARRAYS.PL
sublist(Predicate, List, SubList)				%APPLIC.PL
subseq0(Sequence, SubSequence)					%LISTUT.PL
subseq1(Sequence, ProperSubSequence)				%LISTUT.PL
subset(Subset, Set)						%SETUTL.PL
subst(Substitution, Term, Result)				%STRUCT.PL
subsumes(General, Specific)					%METUTL.PL
subsumes_chk(General, Specific)					%METUTL.PL
subterm(SubTerm, Term)						%METUTL.PL
subtract(Set1, Set2, Set1-Set2)					%SETUTL.PL
succ(Predecessors, Successor)					%SUCC.PL
sumlist(List_of_Integers, Sum_of_Integers)			%LISTUT.PL
symdiff(Set1, Set2, Set1-Set2_+_Set2-Set1)			%SETUTL.PL

talk_to_user_while(Goal)					%ASK.PL
test_sub_bag(SubBag, Bag)					%BAGUTL.PL
tidy(MessyExpression, TidyExpression)				%TIDY.PL
tidy_withvars(MessyExpression, TidyExpression)			%TIDY.PL
tlim(TracingLevel)						%TRACE.PL
toff								%TRACE.PL
toff(Name)							%TRACE.PL
ton(Name)							%TRACE.PL
trace(Format, List, Name)					%TRACE.PL
trace(Format, TracingLevel)					%TRACE.PL
transpose(S_Graph, Transposed_S_Graph)				%GRAPHS.PL
tree_size(Tree, Number_Elements_in_Tree)			%TREES.PL
tree_to_list(Tree, List)					%TREES.PL
trim_blanks(RawInput, Cleaned)					%RDSENT.PL
try_hard_to_see(Title, DeviceDflts, ExtensionDflts)		%TRYSEE.PL
try_hard_to_see(Title, DeviceDflts, ExtensionDflts, FileFound)	%TRYSEE.PL
ttyprint(Term)							%WRITEF.PL
ty(FileList)							%TYPE.PL
type(FileList)							%TYPE.PL
type_check(Given, Pruned)					%TYPECH.PL

unadvise(Predicate, Port)					%ADVICE.PL
unify(Var1, Var2)						%METUTL.PL
union(Set1, Set2, Union)					%SETUTL.PL
update(Template, Generator)					%UPDATE.PL

var_member_chk(Variable, List)					%METUTL.PL
variables(Term, VariableList)					%STRUCT.PL
variables_of(Term, Variables)					%METUTL.PL
variant(Term1, Term2)						%METUTL.PL
vcheck								%VCHECK.PL
vcheck(File)							%VCHECK.PL

warshall(S_Graph, TransitiveClosure)				%GRAPHS.PL
well(Functor, Arity)						%MEDIC.PL
writef(Format)							%WRITEF.PL
writef(Format, Item)						%WRITEF.PL

yesno(Question)							%ASK.PL
yesno(Question, Default)					%ASK.PL
EOF1NUTIL2.HLP             00010077000100 85045 99364 000021Unix V7             HDR1OCCUR.PL               00010078000100 85045 99364 000000Unix V7             %   File   : OCCUR.PL
%   Author : R.A.O'Keefe
%   Updated: 22 May 1983
%   Purpose: routines for checking number/place of occurrence 

%   Some of the things in METUTL.PL may also be relevant, particularly
%   subterm/2.  Maybe that should go here?  occ/3 in STRUCT.PL too.

:- public
	contains/2,			%   Term x Term ->
	freeof/2,			%   Term x Term ->
	patharg/3,			%   Path x Term -> Term
	position/3,			%   Term x Term -> Path
	replace/4.			%   Path x Term x Term -> Term

:- mode
	contains(+, +),
	copy_all_but_one_arg(+, +, +, +),
	freeof(+, +),
	    freeof(+, +, -),
	patharg(+, +, ?),
	position(?, +, ?),
	position(+, ?, +, ?),
	replace(+, +, +, -).



%   contains(Kernel, Expression)
%   is true when the given Kernel occurs somewhere in the Expression.
%   It be only be used as a test; to generate subterms use subterm/2.

contains(Kernel, Expression) :-
	\+ freeof(Kernel, Expression).


%   freeof(Kernel, Expression)
%   is true when the given Kernel does not occur anywhere in the
%   Expression.  NB: if the Expression contains an unbound variable,
%   this must fail, as the Kernel might occur there.  Since there are
%   infinitely many Kernels not contained in any Expression, and als
%   infinitely many Expressions not containing any Kernel, it doesn't
%   make sense to use this except as a test.

freeof(Kernel, Kernel) :- !,
	fail.
freeof(Kernel, Expression) :-
	functor(Expression, _, Arity),		%  can't be a variable!
	freeof(Arity, Kernel, Expression).

freeof(0, Kernel, Expression) :- !.
freeof(N, Kernel, Expression) :-
	arg(N, Expression, Argument),
	freeof(Kernel, Argument),
	M is N-1, !,
	freeof(M, Kernel, Expression).



%   patharg(Path, Exp, Term)
%   unifies Term with the subterm of Exp found by following Path.
%   It may be viewed as a generalisation of arg/3.  It cannot be
%   used to discover a path to a known Term; use position/3 for that.

patharg([Head|Tail], Exp, Term) :-
	arg(Head, Exp, Arg),
	patharg(Tail, Arg, Term).
patharg([], Term, Term).



%   position(Term, Exp, Path)
%   is true when Term occurs in Exp at the position defined by Path.
%   It may be at other places too, so the predicate is prepared to
%   generate them all.  The path is a generalised Dewey number, as usual.
%   position(x, 2*x^2+2*x+1=0, [1, 1, 2, 2]) {2*x} and
%   position(x, 2*x^2+2*x+1=0, [1, 1, 1, 2, 1]) {x^2} are both examples.

position(Term, Term, []).
position(Term, Exp, Path) :-
	nonvar(Exp),
	functor(Exp, _, N),
	position(N, Term, Exp, Path).

position(0, Term, Exp, Path) :- !, fail.
position(N, Term, Exp, [N|Path]) :-
	arg(N, Exp, Arg),
	position(Term, Arg, Path).
position(N, Term, Exp, Path) :-
	M is N-1, !,
	position(M, Term, Exp, Path).



%   replace(Path, OldExpr, SubTerm, NewExpr)
%   is true when OldExpr and NewExpr are identical except at the position
%   identified by Path, where NewExpr has SubTerm.  There is a bug in the
%   Dec-10 compiler, which is why the second 'arg' call follows the replace
%   recursion.  If it weren't for that bug, replace would be tail recursive.
%   replace([1,1,2,2], 2*x^2+2*x+1=0, y, 2*x^2+2*y+1=0) is an example.
 
replace([M|Path], OldExpr, SubTerm, NewExpr) :- !,
	arg(M, OldExpr, OldArg),
	functor(OldExpr, F, N),
	functor(NewExpr, F, N),
	copy_all_but_one_arg(N, M, OldExpr, NewExpr),
	replace(Path, OldArg, SubTerm, NewArg),
	arg(M, NewExpr, NewArg).
replace([], _, SubTerm, SubTerm).


copy_all_but_one_arg(0, _, _, _) :- !.
copy_all_but_one_arg(M, M, OldExpr, NewExpr) :- !,
	L is M-1,
	copy_all_but_one_arg(L, M, OldExpr, NewExpr).
copy_all_but_one_arg(N, M, OldExpr, NewExpr) :-
	arg(N, OldExpr, Arg),
	arg(N, NewExpr, Arg),
	L is N-1,
	copy_all_but_one_arg(L, M, OldExpr, NewExpr).


/*  Suppose you have a set of rewrite rules Lhs -> Rhs which you
    want exhaustively applied to a term.  You would write

	waterfall(Expr, Final) :-
		Lhs -> Rhs,
		position(Expr, Lhs, Path),
		replace(Path, Expr, Rhs, Modified),
		!,
		waterfall(Modified, Final).
	waterfall(Expr, Expr).

*/
EOF1OCCUR.PL               00010078000100 85045 99364 000008Unix V7             HDR1OKHELP.PL              00010079000100 85045 99364 000000Unix V7             go :-
	see('util:util.hlp'),
	repeat,
	    read_a_line(Line),
	    parse_line(Line),
	seen.


%   read_a_line(Line)
%   reads the next line from the current input stream.
%   If there IS a next line, Line is unified with the characters
%   froim up, up to but excluding the closing newline (31) character.
%   If there is NOT a next line, Line is unified with 'end_of_file'.

read_a_line(Line) :-
	get0(Char),
	read_a_line(Char, Line).

read_a_line(26, end_of_file) :- !.
read_a_line(Other, Line) :-
	rest_of_line(Other, Line).

rest_of_line(31, []) :- !.
rest_of_line(Char, [Char|Line]) :-
	get0(NextChar),
	rest_of_line(NextChar, Line).


%   parse_line(Line)
%   takes a list of characters or the constant end_of_file and does
%   one of three things with it.
%   1.  If it is 'end_of_file', it succeeds.  In the final program
%   it will have to apologise for not having found the predicate you
%   want help for.
%   2.  If the line can be parsed as
%	<predicate name>[(<arguments>)] %<file>
%   it prints the predicate name, its arity (the number of commas plus
%   1 if there are parentheses, otherwise 0), and the file name.  In the
%   final program this will be used to decide whether it is the
%   predicate we are looking for, and if so what file to visit.
%   3.  Otherwise it is a blank line or a header line or some other bit of
%   junk which we ignore.  In cases 2 and 3 we FAIL, because it is used in 
%   a failure-driven loop.

parse_line(end_of_file) :- !.
parse_line(Line) :-
	help_line(Pred, Arity, File, Line, _),
	!,
	name(PredAtom, Pred),
	name(FileAtom, File),
	writef('%t/%t comes from %t.\n', [PredAtom,Arity,FileAtom]),
	fail.
parse_line(_) :-
	nl,
	fail.

%   help_line(Pred, Arity, File) has to parse a help line.
%   There are no leading spaces or tabs on one of these lines.
%   The predicate symbol is everything up to the first "(" or
%   layout character.  If there is no "(" the arity is 0.  If
%   there is a "(", the arity is the number of commas up to
%   the matching ")" plus 1.  In either case we then have to
%   skip to the "%", and the File is everything after that.

help_line(Pred, Arity, File) -->
	help_pred(Pred, HadPren),
	{   Pred \== []   },
	help_arity(HadPren, Arity),
	skip_string(37),
	help_file(File).


help_pred([], yes) -->
	"(", !.
help_pred([], no) -->
	[C], {C =< 32}, !.
help_pred([C|Cs], HadPren) -->
	[C],
	help_pred(Cs, HadPren).


help_file([C|Cs]) -->
	[C], {C > 32}, !,
	help_file(Cs).
help_file([]) --> [].


help_arity(no, 0) --> !.
help_arity(yes, N) -->
	help_arity(0, 1, N).

help_arity(Depth, SoFar, Arity) -->
	(   "'", !, skip_string(39), help_arity(Depth, SoFar, Arity)
	|   """",!, skip_string(34), help_arity(Depth, SoFar, Arity)
	|   "(", !, {E is Depth+1},  help_arity(E, SoFar, Arity)
	|   ")", {Depth = 0}, !, {Arity = SoFar}
	|   ")", !, {E is Depth-1},  help_arity(E, SoFar, Arity)
	|   ",", {Depth = 0}, !, {Next is SoFar+1}, help_arity(Depth, Next, Arity)
	|   [_], help_arity(Depth, SoFar, Arity)
	).

skip_string(C) --> [C], !.
skip_string(C) --> [_], skip_string(C).

 EOF1OKHELP.PL              00010079000100 85045 99364 000006Unix V7             HDR1ORDER.PL               00010080000100 85045 99364 000000Unix V7             %   File   : ORDER.PL
%   Author : R.A.O'Keefe
%   Updated: 12 June 1984
%   Purpose: Define the "ordered" predicates.

:- public
	len/2,
	ordered/1,
	ordered/2.

:- mode
	ordered(+),
	    ordered_(+, +),
	ordered(+, +),
	    ordered_(+, +, +),
	len(?, ?),
	    len_(+, ?),
	    len_(+, +, -).

/*  Warning: thanks to the use of apply/2, these predicates cannot
    be assigned types.  The type checker will shortly be extended to
    handle such predicates, provided they use call/N (N > 1) instead.
    Till then, you'll have to trust me for these types:

:- pred
	ordered(list(T)),
	    ordered_(list(T), T),
	ordered(void(T,T), list(T)),
	    ordered_(list(T), T, void(T,T)),
	len(list(_), integer),
	    len_(integer, list(_)),
	    len_(list(_), integer, integer).
*/

%   ordered(List)
%   is true when List is a list of terms [T1,T2,...,Tn] such that
%   for all k in 2..n Tk-1 @=< Tk, i.e. T1 @=< T2 @=< T3 ...
%   The output of keysort/2 is always ordered, and so is that of
%   sort/2.  Beware: just because a list is ordered does not mean
%   that it is the representation of an ordered set; it might contain
%   duplicates.  E.g. L = [1,2,2,3] & sort(L,M) => ordered(L) & M\=L.

ordered([]).
ordered([Head|Tail]) :-
	ordered_(Tail, Head).

ordered_([], _).
ordered_([Head|Tail], Left) :-
	Left @=< Head,
	ordered_(Tail, Head).



%   ordered(P, [T1,T2,...,Tn]) means P(T1,T2) & P(T2,T3) & ...
%   i.e. that the second argument is ordered if you regard the
%   first argument as =<.  This is good for generating prefixes
%   of sequences, e.g. L = [1,_,_,_,_] & ordered(times(2),L) yields
%   L = [1,2,4,8,16].

ordered(Relation, []).
ordered(Relation, [Head|Tail]) :-
	ordered_(Tail, Head, Relation).

ordered_([], _, _).
ordered_([Head|Tail], Left, Relation) :-
	apply(Relation, [Left,Head]),
	ordered_(Tail, Head, Relation).



%   To exploit ordered/2 fully, we need a way of generating lists of
%   a given length.  I trust that a Prolog Standard will demand that
%   length/2 be reversible.  Until then, here is a reversible length.
%   len_/2 generates a list of a given length.  len_/3 measures the
%   length of a given list.  It reports an error if you give it a
%   variable or a list with a variable tail because then it would
%   backtrack forever trying ever longer lists if there was a
%   failure upstream, and this is generally not a useful thing to do.
%   Note: this code is really hacky, that's because of the error
%   detection.  Making len_/3 fail for variables so that len/2 can
%   report the error on the original list, faugh!

len(List, Length) :-
	nonvar(Length),
	!,
	integer(Length),
	len_(Length, List).
len(List, Length) :-
	nonvar(List),		% we know that var(Length)
	len_(List, 0, Length),	% so len_/3 will work for proper lists
	!.			% and fail for vars and non-lists
len(List, Length) :-
	nl, write('! bad arguments in '),
	write(len(List,Length)), nl,
	break, abort.


len_(0, []).
len_(N, [_|Tail]) :-
	N > 0,
	M is N-1,
	len_(M, Tail).


len_([], Length, Length).
len_([_|Tail], SoFar, Length) :-
	nonvar(Tail),
	Next is SoFar+1,
	len(Tail, Next, Length).


EOF1ORDER.PL               00010080000100 85045 99364 000007Unix V7             HDR1ORDSET.PL              00010081000100 85045 99364 000000Unix V7             %   File   : ORDSET.PL
%   Author : R.A.O'Keefe
%   Updated: 22 May 1983
%   Purpose: Ordered set manipulation utilities

%   In this module, sets are represented by ordered lists with no
%   duplicates.  Thus {c,r,a,f,t} would be [a,c,f,r,t].  The ordering
%   is defined by the @< family of term comparison predicates, which
%   is the ordering used by sort/2 and setof/3.

%   The benefit of the ordered representation is that the elementary
%   set operations can be done in time proportional to the Sum of the
%   argument sizes rather than their Product.  Some of the unordered
%   set routines, such as member/2, length/2,, select/3 can be used
%   unchanged.  The main difficulty with the ordered representation is
%   remembering to use it!

:- public
	list_to_ord_set/2,	%  List -> Set
	merge/3,		%  OrdList x OrdList -> OrdList
	ord_disjoint/2,		%  Set x Set ->
	ord_insert/3,		%  Set x Elem -> Set
	ord_intersect/2,	%  Set x Set ->
	ord_intersect/3,	%  Set x Set -> Set
	ord_seteq/2,		%  Set x Set ->
	ord_subset/2,		%  Set x Set ->
	ord_subtract/3,		%  Set x Set -> Set
	ord_symdiff/3,		%  Set x Set -> Set
	ord_union/3.		%  Set x Set -> Set

:- mode
	list_to_ord_set(+, ?),
	merge(+, +, -),
	ord_disjoint(+, +),
	    ord_disjoint(+, +, +, +, +),
	ord_insert(+, +, ?),
	    ord_insert(+, +, +, +, ?),
	ord_intersect(+, +),
	    ord_intersect(+, +, +, +, +),
	ord_intersect(+, +, ?),
	    ord_intersect(+, +, +, +, +, ?),
	ord_seteq(+, +),
	ord_subset(+, +),
	    ord_subset(+, +, +, +, +),
	ord_subtract(+, +, ?), 
	    ord_subtract(+, +, +, +, +, ?),
	ord_symdiff(+, +, ?),
	    ord_symdiff(+, +, +, +, +, ?),
	ord_union(+, +, ?),
	    ord_union(+, +, +, +, +, ?).



%   list_to_ord_set(+List, ?Set)
%   is true when Set is the ordered representation of the set represented
%   by the unordered representation List.  The only reason for giving it
%   a name at all is that you may not have realised that sort/2 could be
%   used this way.

list_to_ord_set(List, Set) :-
	sort(List, Set).


%   merge(+List1, +List2, -Merged)
%   is true when Merged is the stable merge of the two given lists.
%   If the two lists are not ordered, the merge doesn't mean a great
%   deal.  Merging is perfectly well defined when the inputs contain
%   duplicates, and all copies of an element are preserved in the
%   output, e.g. merge("122357", "34568", "12233455678").  Study this
%   routine carefully, as it is the basis for all the rest.

merge([Head1|Tail1], [Head2|Tail2], [Head2|Merged]) :-
	Head1 @> Head2, !,
	merge([Head1|Tail1], Tail2, Merged).
merge([Head1|Tail1], List2, [Head1|Merged]) :-
	List2 \== [], !,
	merge(Tail1, List2, Merged).
merge([], List2, List2) :- !.
merge(List1, [], List1).



%   ord_disjoint(+Set1, +Set2)
%   is true when the two ordered sets have no element in common.  If the
%   arguments are not ordered, I have no idea what happens.

ord_disjoint([], _) :- !.
ord_disjoint(_, []) :- !.
ord_disjoint([Head1|Tail1], [Head2|Tail2]) :-
	compare(Order, Head1, Head2),
	ord_disjoint(Order, Head1, Tail1, Head2, Tail2).

ord_disjoint(<, _, Tail1, Head2, Tail2) :-
	ord_disjoint(Tail1, [Head2|Tail2]).
ord_disjoint(>, Head1, Tail1, _, Tail2) :-
	ord_disjoint([Head1|Tail1], Tail2).



%   ord_insert(+Set1, +Element, ?Set2)
%   is the equivalent of add_element for ordered sets.  It should give
%   exactly the same result as merge(Set1, [Element], Set2), but a bit
%   faster, and certainly more clearly.

ord_insert([], Element, [Element]).
ord_insert([Head|Tail], Element, Set) :-
	compare(Order, Head, Element),
	ord_insert(Order, Head, Tail, Element, Set).


ord_insert(<, Head, Tail, Element, [Head|Set]) :-
	ord_insert(Tail, Element, Set).
ord_insert(=, Head, Tail, _, [Head|Tail]).
ord_insert(>, Head, Tail, Element, [Element,Head|Tail]).



%   ord_intersect(+Set1, +Set2)
%   is true when the two ordered sets have at least one element in common.
%   Note that the test is == rather than = .

ord_intersect([Head1|Tail1], [Head2|Tail2]) :-
	compare(Order, Head1, Head2),
	ord_intersect(Order, Head1, Tail1, Head2, Tail2).

ord_intersect(=, _, _, _, _).
ord_intersect(<, _, Tail1, Head2, Tail2) :-
	ord_intersect(Tail1, [Head2|Tail2]).
ord_intersect(>, Head1, Tail1, _, Tail2) :-
	ord_intersect([Head1|Tail1], Tail2).



%   ord_intersect(+Set1, +Set2, ?Intersection)
%   is true when Intersection is the ordered representation of Set1
%   and Set2, provided that Set1 and Set2 are ordered sets.

ord_intersect(_, [], []) :- !.
ord_intersect([], _, []) :- !.
ord_intersect([Head1|Tail1], [Head2|Tail2], Intersection) :-
	compare(Order, Head1, Head2),
	ord_intersect(Order, Head1, Tail1, Head2, Tail2, Intersection).

ord_intersect(=, Head,  Tail1, _,     Tail2, [Head|Intersection]) :-
	ord_intersect(Tail1, Tail2, Intersection).
ord_intersect(<, _,     Tail1, Head2, Tail2, Intersection) :-
	ord_intersect(Tail1, [Head2|Tail2], Intersection).
ord_intersect(>, Head1, Tail1, _,     Tail2, Intersection) :-
	ord_intersect([Head1|Tail1], Tail2, Intersection).



%   ord_seteq(+Set1, +Set2)
%   is true when the two arguments represent the same set.  Since they
%   are assumed to be ordered representations, they must be identical.


ord_seteq(Set1, Set2) :-
	Set1 == Set2.



%   ord_subset(+Set1, +Set2)
%   is true when every element of the ordered set Set1 appears in the
%   ordered set Set2.

ord_subset([], _) :- !.
ord_subset([Head1|Tail1], [Head2|Tail2]) :-
	compare(Order, Head1, Head2),
	ord_subset(Order, Head1, Tail1, Head2, Tail2).

ord_subset(=, _, Tail1, _, Tail2) :-
	ord_subset(Tail1, Tail2).
ord_subset(>, Head1, Tail1, _, Tail2) :-
	ord_subset([Head1|Tail1], Tail2).



%   ord_subtract(+Set1, +Set2, ?Difference)
%   is true when Difference contains all and only the elements of Set1
%   which are not also in Set2.


ord_subtract(Set1, [], Set1) :- !.
ord_subtract([], _, []) :- !.
ord_subtract([Head1|Tail1], [Head2|Tail2], Difference) :-
	compare(Order, Head1, Head2),
	ord_subtract(Order, Head1, Tail1, Head2, Tail2, Difference).

ord_subtract(=, _,     Tail1, _,     Tail2, Difference) :-
	ord_subtract(Tail1, Tail2, Difference).
ord_subtract(<, Head1, Tail1, Head2, Tail2, [Head1|Difference]) :-
	ord_subtract(Tail1, [Head2|Tail2], Difference).
ord_subtract(>, Head1, Tail1, _,     Tail2, Difference) :-
	ord_subtract([Head1|Tail1], Tail2, Difference).



%   ord_symdiff(+Set1, +Set2, ?Difference)
%   is true when Difference is the symmetric difference of Set1 and Set2.

ord_symdiff(Set1, [], Set1) :- !.
ord_symdiff([], Set2, Set2) :- !.
ord_symdiff([Head1|Tail1], [Head2|Tail2], Difference) :-
	compare(Order, Head1, Head2),
	ord_symdiff(Order, Head1, Tail1, Head2, Tail2, Difference).

ord_symdiff(=, _,     Tail1, _,     Tail2, Difference) :-
	ord_symdiff(Tail1, Tail2, Difference).
ord_symdiff(<, Head1, Tail1, Head2, Tail2, [Head1|Difference]) :-
	ord_symdiff(Tail1, [Head2|Tail2], Difference).
ord_symdiff(>, Head1, Tail1, Head2, Tail2, [Head2|Difference]) :-
	ord_symdiff([Head1|Tail1], Tail2, Difference).



%   ord_union(+Set1, +Set2, ?Union)
%   is true when Union is the union of Set1 and Set2.  Note that when
%   something occurs in both sets, we want to retain only one copy.

ord_union(Set1, [], Set1) :- !.
ord_union([], Set2, Set2) :- !.
ord_union([Head1|Tail1], [Head2|Tail2], Union) :-
	compare(Order, Head1, Head2),
	ord_union(Order, Head1, Tail1, Head2, Tail2, Union).

ord_union(=, Head,  Tail1, _,     Tail2, [Head|Union]) :-
	ord_union(Tail1, Tail2, Union).
ord_union(<, Head1, Tail1, Head2, Tail2, [Head1|Union]) :-
	ord_union(Tail1, [Head2|Tail2], Union).
ord_union(>, Head1, Tail1, Head2, Tail2, [Head2|Union]) :-
	ord_