🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: comp.lang.prolog
Article #2497

PROLOG Digest V4 #80

#2497
From: Chuck Restivo, The Moderator
Date: Sun, 30 Nov 1986 20:12
1430 lines
48706 bytes
PROLOG Digest             Monday, 1 Dec 1986       Volume 4 : Issue 80

Today's Topics:
                     LP Library - Utility Update
----------------------------------------------------------------------

Date: 25 Nov 86 07:06:00 EST
From: John Cugini <cugini@nbs-vms.ARPA>
Subject: update of utility library

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
%%     NBS/ICST Prolog Utility Library
%%     version date: Nov. 25, 1986
%%
%%     developed by:
%%
%%     John Cugini <cugini@nbs-vms.arpa>
%%     Institute for Computer Sciences and Technology
%%     National Bureau of Standards
%%
%%     Product of US Government: not subject to copyright
%%
%%  This file contains various utility predicates, some commonly used,
%%  some not.  They deal with lists, structures, I/O, sets, numeric
%%  facilities, and some extensions of logic and control.  This library
%%  is written in and for the C-Prolog dialect of Prolog.
%%
%%  Many of these predicates expect certain of their arguments to be
%%  instantiated upon invocation.  When such restrictions apply it is
%%  usually the leading arguments which are thought of as input (and
%%  hence instantiated), and the trailing arguments as output (and hence
%%  allowed to be uninstantiated).
%%
%%  There is a coding convention: the user-callable version of the
%%  predicate has a plain name.  If this predicate needs sub-predicates,
%%  based on whether certain arguments are instantiated or not, the names
%%  of the sub-predicates are formed by appending a string of c,v, or
%%  x's, where c indicates argument must be constant (instantiated), v
%%  that it must be a variable, and x that it may be either.
%%
%%  Further, each main predicate is preceded by documentation lines,
%%  which describe the declarative meaning of the predicate, and which
%%  arguments must be instantiated.
%%
%%  The overall organization of the library is:
%%
%%      Basic predicates
%%      Lists
%%      Structures
%%      Input/Output
%%      Sets
%%      Numeric
%%      Control
%%      Extended Logic
%%
%%  Each section is prefaced by a header with lots of asterisks

%%  ************ Basic Predicates *************

%%  The following predicates test the type of the term passed
%%  to them, using the terminology of the C-Prolog manual.

term(Term).
simple(Term)   :- atomic(Term); var(Term).
compound(Term) :- not(simple(Term)).
literal(Term)  :- nonvar(Term),
                  functor(Term, Name, _),
                  atom(Name).
float(Term)    :- number(Term), not(integer(Term)).
rational(Term) :- integer(Term); ratio(Term).
ratio(Num/Den) :- integer(Num), integer(Den).

%%  constant_term(Term) iff Term is currently instantiated and all
%%  of its arguments, sub-arguments, etc. are constant.

constant_term(Term) :-
  atomic(Term) -> true;
                  (nonvar(Term),
                   Term =.. [Functor | Args],
                   all(constant_term, Args)).

%%  instantiation(Term, Type) iff Type describes the instantiation state
%%  of Term: constant (completely ground), partial, or var.

instantiation(Term, Type) :-
  var(Term)           -> Type = var;
  constant_term(Term) -> Type = constant;
                         Type = partial.

%%  ************* Lists **************

%%  islist(X) iff X is a list.  (Really just checks for [] or that
%%  main functor is '.').

islist([]) :- !.
islist([_|_]).

%%  is_real_list(X) iff X is a properly formed list.  Does not allow
%%  "dotted" lists, a la [a|b], as does islist.

is_real_list([]) :- !.
is_real_list([_ | Rest]) :- is_real_list(Rest).

%%  non_null_list(List) iff List is a real list containing at least
%%  one element.

non_null_list([_ | Rest]) :- is_real_list(Rest).

%%  member(Elem, List) iff Elem is a member of List.

member(Elem, [Elem | _]).
member(Elem, [_ | Rest_of_list]) :- member(Elem, Rest_of_list).

%%  member_rest(Elem, List, Rest) iff Elem is a member of List and
%%  Rest is the rest of the list following Elem.

member_rest(Elem, [Elem | Rest], Rest).
member_rest(Elem, [_ | Rest], Rest_rest) :-
  member_rest(Elem, Rest, Rest_rest).

%%  append(First_part, Second_part, List) iff List is the
%%  concatenation of the first two arguments.

append([], List, List).
append([Elem | First_part], Second_part, [Elem | List]) :-
  append(First_part, Second_part, List).

%%  append_n(L1, L2) iff L1 is a list of lists, which, if concatenated,
%%  is L2.  When L1 is var, no null lists are generated for it, even
%%  though logically they could appear anywhere.  Thus, this isn't
%%  symmetric, in that L1=[[],[1]] will generate L2=[1], but not
%%  vice-versa.  Note especially that L1=[[]] will generate L2=[], but
%%  not the reverse.  L1 and L2 can be partially instantiated, and this
%%  usually works.  NG if both args are var.

%% avoid generating the null list in arg1.

append_n([List1], List2) :-
  not((var(List1), List2 == [])),
  List1=List2.

append_n([SL1, SL2 | Rest], List) :-
  (var(SL1) -> V=t; V=f),
  append(SL1, Right_part, List),
  not((V=t, SL1=[])),
  append_n([SL2 | Rest], Right_part).

%%  delete(Elem, Old_list, New_list) iff New_list equals Old_list except
%%  for the removal of all occurrences of Elem.  NG if arg2 is var.

delete(Elem, Old, New) :-
   (var(Elem) -> (remove_dupl(Old, Smaller_old),
                  member(Elem, Smaller_old)
                 );
                  Elem=X        %  dummy statement to fill else-part
   ),
   delete_c(Elem, Old, New).

delete_c(_, [], []).
delete_c(Elem1, [Elem2 | Rest_of_old], New_list) :-
   Elem1 = Elem2 -> (delete_c(Elem1, Rest_of_old, New_list)
                    );
                    (New_list = [Elem2 | Rest_of_new],
                     delete_c(Elem1, Rest_of_old, Rest_of_new)
                    ).

%%  delete_all(Del_list, Old_list, New_list) iff New_list equals Old_list
%%  except for the removal of any occurrences of any elements of Del_list.
%%  NG if arg1 or 2 is var.

delete_all([], L, L).
delete_all([E | R_del], Old_list, New_list) :-
   delete(E, Old_list, M),
   delete_all(R_del, M, New_list).

%%  remove_dupl(List, Shriven_list) iff Shriven_list equals List
%%  in same order, sans leading duplicate members.  Ie, only the
%%  rightmost of duplicate members remain.  NG if arg1 is var.

remove_dupl([],[]).
remove_dupl([Elem | Rest_list], Rest_shriven) :-
  member(Elem, Rest_list),
  !,
  remove_dupl(Rest_list, Rest_shriven).
remove_dupl([Elem | Rest_list], [Elem | Rest_shriven]) :-
  remove_dupl(Rest_list, Rest_shriven).

%%  no_dupls(List) iff List is a list with no duplicate elements.
%%  NG if arg1 is var.

no_dupls([]).
no_dupls([Elem | Rest]) :-
   not(member(Elem, Rest)),
   no_dupls(Rest).

%% ordered(List) iff List is a list whose elements are in non-
%% decreasing order.  NG if List is var.

ordered([]).
ordered([Elem]).
ordered([Elem1, Elem2 | Rest]) :-
  Elem1 @=< Elem2,
  !,
  ordered([Elem2 | Rest]).

%%  last(Elem, List) iff Elem is the last element in List.

last(Elem, [Elem]).
last(Elem, [_ | Rest]) :- last(Elem, Rest).

%%  next_to(X,Y,L) iff X and Y are adjacent in list L.

next_to(X, Y, [X,Y | _]).
next_to(X, Y, [_ | Rest]) :- next_to(X, Y, Rest).

%%  reverse(List1, List2) iff List1 is List2 in reverse order.
%%  NG if arg1 is var.

reverse([], []) :- !.
reverse([Head | Tail], List) :-
  reverse(Tail, Liat), append(Liat, [Head], List).

%%  efface(Elem, Old_list, New_list) iff New_list = Old_list
%%  with first occurrence of Elem removed.  NG if more than
%%  one arg is var.

efface(Elem, [Elem | Rest], Rest) :- !.
efface(Elem, [Non_elem | Old_rest], [Non_elem | New_rest]) :-
  not(Elem = Non_elem),
  efface(Elem, Old_rest, New_rest).

%%  insert(Elem, List, Bigger_list) iff Bigger_list = List plus
%%  Elem inserted somewhere.  NG if all args are var.

insert(Elem, List, [Elem | List]).
insert(Elem, [Non_elem | List], [Non_elem | Bigger_list]) :-
   insert(Elem, List, Bigger_list).

%%  subst(Old_elem, Old_list, New_elem, New_list) iff New_list equals
%%  Old_list except for the substitution of New_elem for any
%%  occurrences of Old_elem.  NG for arg1;2;3 var.

subst(_, [], _, []).
subst(Old_elem, [Old_elem | Rest_of_old],
      New_elem, [New_elem | Rest_of_new]) :-
         !, subst(Old_elem, Rest_of_old, New_elem, Rest_of_new).
subst(Old_elem, [Non_elem | Rest_of_old],
      New_elem, [Non_elem | Rest_of_new]) :-
            subst(Old_elem, Rest_of_old, New_elem, Rest_of_new).

%%  prefix(Part, Whole) iff Part is a leading substring of Whole.

prefix([], _).
prefix([Elem | Rest_of_part], [Elem | Rest_of_whole]) :-
  prefix(Rest_of_part, Rest_of_whole).

%%  suffix(Part, Whole) iff Part is a trailing substring of Whole.

suffix(List, List) :- islist(List).
suffix(Part, [Elem | Rest_of_whole]) :- suffix(Part, Rest_of_whole).

%%  trim(List, Elem, Ans) iff Ans is List with all leading and
%%  trailing occurrences of Elem removed.  NG if arg1 or arg2
%%  is var.

trim(List, Elem, Ans) :-
  trim_left(List, Elem, Temp),
  trim_right(Temp, Elem, Ans).

%%  trim_left(List, Elem, Ans) iff Ans is List with all leading
%%  occurrences of Elem removed.  NG if arg1 or arg2 is var.

trim_left([Elem | Rest], Elem, Ans) :-
  trim_left(Rest, Elem, Ans), !.
trim_left(List, _, List).

%%  trim_right(List, Elem, Ans) iff Ans is List with all trailing
%%  occurrences of Elem removed.  NG if arg1 or arg2 is var.

trim_right([],  _, [])  :- !.
trim_right([Elem | Rest], Elem, []) :-
  trim_right(Rest, Elem, []), !.
trim_right([Arb | Rest], Elem, [Arb | Rest_Trim]) :-
  trim_right(Rest, Elem, Rest_Trim).

%%  sublist(List, Start, End, Sublist) iff Sublist is a contiguous
%%  sub-list within List, starting at position Start, and ending at
%%  position End.  Note that [] is a valid sublist, so for
%%  List = [1,2], valid solutions are:
%%
%%         Start  End   Sublist
%%         1      0     []
%%         1      1     [1]
%%         1      2     [1,2]
%%         2      1     []
%%         2      2     [2]
%%         3      2     []
%%
%%  NG if arg1 is var.

sublist(List, Start, End, Sublist) :-
  prefix(Sublist, List),
  Start = 1,
  length(Sublist, End).

sublist([Elem | Rest], Start, End, Sublist) :-
  sublist(Rest, Startx, Endx, Sublist),
  Start is Startx + 1,
  End is Endx + 1.

%%   matchlist(List1, List2, Common, New1, New2) iff List1 and
%%   List2 are sorted instantiated lists (possibly with repetitions)
%%   and Common is a list of their matching elements, and New1 and New2
%%   are List1 and List2 minus the matching elements in Common, eg:
%%   matchlist([1,2,3,3,3,4,5,5,6], [3,3,4,4,5,5], [3,3,4,5,5],
%%             [1,2,3,6],           [4]) is true.  NG if Arg1 or Arg2
%%   is var or unsorted.

matchlist([], List2, [], [], List2) :- !.

matchlist([Elem | Rest], [], [], [Elem | Rest], []) :- !.

matchlist([Elem | Rest1], [Elem | Rest2], [Elem | Com_Rest], New1, New2) :-
  matchlist(Rest1, Rest2, Com_Rest, New1, New2), !.

matchlist([El1 | Rest1], [El2 | Rest2], Com, New1, New2) :-
  (El1 @< El2 ->
     (matchlist(Rest1, [El2 | Rest2], Com, New_Rest1, New2),
                       New1 = [El1 | New_Rest1]);
     (matchlist([El1 | Rest1], Rest2, Com, New1, New_Rest2),
                       New2 = [El2 | New_Rest2])
  ),
  !.


%%  list_length(List, Number) iff List has Number elements.
%%  NG if arg1 is var.  Note that, apparently, C-Prolog has an
%%  undocumented evaluable predicate, length(List, Number).

list_length([], 0).
list_length([Elem | Rest], Number) :-
  list_length(Rest, N_minus),
  Number is N_minus + 1.

%%  position(List, Elem, Number) iff Elem is in position Number
%%  in the List.  NG if arg1 is var.

position([Elem | Rest], Elem, 1).
position([_    | Rest], Elem, Number) :-
   var(Number) -> (position(Rest, Elem, N_minus),
                   Number is N_minus + 1);
                  (N_minus is Number - 1,
                   position(Rest, Elem, N_minus),
                   !).

%%  repeat_list(Elem, Number, List) iff List is a list of Elem
%%  repeated Number times.  No var restrictions.

repeat_list(Elem, Number, List) :-
  (nonvar(Number), var(List)) ->
      repeat_list_xcv(Elem, Number, List);
      repeat_list_xxx(Elem, Number, List).

repeat_list_xcv(Elem, 0, []).
repeat_list_xcv(Elem, N, [Elem | Rest]) :-
  N_minus is N - 1,
  !,
  N_minus > -1,
  repeat_list_xcv(Elem, N_minus, Rest).

repeat_list_xxx(Elem, 0, []).
repeat_list_xxx(Elem, N, [Elem | Rest]) :-
  repeat_list_xxx(Elem, N_minus, Rest),
  N is N_minus + 1.

%%  permute(List1, List2) iff List1 is a permutation of List2.
%%  NG if arg1 is var.

permute(Whole, [Elem | Rest_of_part]) :-
     islist(Whole),
     member(Elem, Whole),
     efface(Elem, Whole, Reduced_whole),
     permute(Reduced_whole, Rest_of_part).

permute([], []).

%%  reduce(Bin_op, List, Ans) iff Ans is the result of applying
%%  Bin_op, left-associatively, to the elements of List.   NG if
%%  arg1 or arg2 is var, or if List contains fewer than two
%%  elements. If ID is an identity element for Bin_op, and the List
%%  may contain only one element, invoke with: reduce(Bin_op, [ID |
%%  List], Ans).   To allow a null list to return ID, invoke
%%  with: reduce(Bin_op, [ID, ID | List], Ans).

reduce(Bin_op, [El1, El2 | Rest], Ans) :-
  Callit =.. [Bin_op, El1, El2, Temp],
  Callit,
  (Rest = [] ->
     Ans = Temp;
     reduce(Bin_op, [Temp | Rest], Ans)
  ).

%%  maplist(Pred, Old, New) iff for each corresponding element in
%%  Old and New, Pred(Old, New) is true.  NG if arg1 is var.

maplist(_, [], []).
maplist(Pred, [Elem_old | Rest_of_old], [Elem_new | Rest_of_new]) :-
  Pred_call =.. [Pred, Elem_old, Elem_new],  %  constructs  predicate
  Pred_call,                         %  invokes constructed predicate
  maplist(Pred, Rest_of_old, Rest_of_new).

%%  maplist_2(Pred, L1, L2, L3) iff L1, L2, and L3 are lists of equal length
%%  and for each element (E1, E2, E3) in corresponding positions in the
%%  lists, Pred(E1, E2, E3) is true.  Thus, E3 should be a function of E1
%%  and E2 - if there is more than one solution, only the first will be
%%  used.  NG if arg1 is var.  Whether arg2 or arg3 can be var depends on
%%  the nature of Pred.

maplist_2(_, [], [], []).
maplist_2(Pred, [Elem1 | Rest1], [Elem2 | Rest2], [Elem3 | Rest3]) :-
  Pred_call =.. [Pred, Elem1, Elem2, Elem3],
  Pred_call,
  !,
  maplist_2(Pred, Rest1, Rest2, Rest3).

%%  for_all(Op_list, Pre_list, Post_list) iff a predicate is
%%  successfully invoked for each member of Op_List.  Each
%%  predicate is formed by pre-pending Pre_list to a member
%%  of Op_List, appending Post_list to it, and then forming
%%  the corresponding functor.
%%
%%  Thus, for_all([a,b,c], [wiggle,x], [y]) will invoke:
%%       wiggle(x, a, y)
%%       wiggle(x, b, y)
%%       wiggle(x, c, y).
%%
%%  and for_all([number, atomic], [], [A]) will invoke:
%%       number(A)
%%       atomic(A).

for_all([], _, _).
for_all([Op | Rest_Ops], Pre_list, Post_list) :-
  append(Pre_list, [Op | Post_list], Pred_list),
  Pred =.. Pred_list,
  !,
  Pred,
  for_all(Rest_Ops, Pre_list, Post_list).

%%  maxlist(List, Max) iff Max is the highest value in a List of numbers.
%%  NG if arg1 is var.

maxlist([Elem], Elem).
maxlist([Elem | Rest], Max) :-
   maxlist(Rest, Rmax),
   (Elem > Rmax -> Max = Elem; Max = Rmax).

%%  minlist(List, Min) iff Min is the lowest value in a List of numbers.
%%  NG if arg1 is var.

minlist([Elem], Elem).
minlist([Elem | Rest], Min) :-
   minlist(Rest, Rmin),
   (Elem < Rmin -> Min = Elem; Min = Rmin).

%%  bestlist(List, Pred, Best) iff Best is the best value in a List,
%%  according to some binary predicate Pred(A,B), which succeeds iff
%%  A is better than B.  NG if arg1 or arg2 is var.

bestlist([Elem], _, Elem).
bestlist([Elem | Rest], Pred, Best) :-
   bestlist(Rest, Pred, Rbest),
   Call =.. [Pred, Elem, Rbest],
   (Call -> Best = Elem; Best = Rbest).

%%  all(Pred, List) iff Pred is a single-place predicate true of
%%  all members of the list.  NG if either arg is var.

all(Pred, []) :- atom(Pred).

all(Pred, [Elem | Rest]) :-
  Callit =.. [Pred, Elem],
  Callit,
  all(Pred, Rest).

%%  some(Pred, List) iff Pred is a single-place predicate true of
%%  at least one member of the list.  NG if either arg is var.

some(Pred, List) :- atom(Pred), some_cx(Pred, List).

some_cx(Pred, [Elem | Rest]) :-
  (Callit =.. [Pred, Elem],
   Callit);
  some_cx(Pred, Rest).

%%  notall(Pred, List) iff Pred is a single-place predicate false of
%%  at least one member of the list.  NG if either arg is var.

notall(Pred, List) :- atom(Pred), not(all(Pred,List)).

%%  none(Pred, List) iff Pred is a single-place predicate true of
%%  none of the members of the list.  NG if either arg is var.

none(Pred, List) :- atom(Pred), not(some(Pred,List)).

%%  string_of(Alphabet, String) iff String is a list composed of
%%  elements of the non-redundant non-null Alphabet.
%%  NG if both args are var.

string_of(Alphabet, String) :-
  var(Alphabet) -> remove_dupl(String, Alphabet);
                   (Alphabet = [_|_],       %  ie non-null
                    no_dupls(Alphabet),
                    string_of_cv(Alphabet, String)
                   ).

string_of_cv(Alphabet, []).
string_of_cv(Alphabet, [Elem | Rest]) :-
  string_of_cv(Alphabet, Rest),
  member(Elem, Alphabet).

%%  sort_all(In_list, Out_list) iff Out_list is a sorting of In_list,
%%  saving duplicates.  The Xing allows the use of keysort, which
%%  doesn't delete duplicates.

sort_all(In, Out) :-
  sort_all_xit(In, Xed_list),
  keysort(Xed_list, Sorted_xed_list),
  sort_all_xit(Out, Sorted_xed_list).

%%  sort_all_xit(List, Xed_list) iff each element of list is matched
%%  by an xed element in Xed_list.

sort_all_xit([],[]).
sort_all_xit([Elem | In_rest], [Elem-x | Out_rest]) :-
  sort_all_xit(In_rest, Out_rest), !.

%%  tot_sort(In_list, Out_list) iff Out_list is a total sorting
%%  of In_list.  This means that not only are the elements of
%%  In_list sorted, but that any of those elements which are lists
%%  are also transformed by sorting, and so on.  Thus:
%%  tot_sort([1,[a3,a2,a3,a1],2,1], [1,2,[a1,a2,a3]]). Recall that
%%  sort eliminates duplicates, and so Out_list might be shorter
%%  than In_list.  Tot_sort can be thought of as providing a
%%  normalized form for multi-level sets.  NG if arg1 is var.

tot_sort(In, Out) :-
  norm_elem(In, Normalized_list),
  sort(Normalized_list, Out).

%%  norm_elem(In, Out) iff Out is the same list as In, except that
%%  any elements of In which are themselves lists, are transformed
%%  by tot_sorting.  NG if arg1 is var.

norm_elem([],[]).
norm_elem([In_elem | In_rest], [Out_elem | Out_rest]) :-
  (islist(In_elem) -> tot_sort(In_elem, Out_elem); In_elem = Out_elem),
  norm_elem(In_rest, Out_rest),
  !.

%%  ordered_merge(List1, List2, List3) iff List3 is the ordered merging
%%  of List1 and List2.  If more than one arg is var, returns only a
%%  single solution.  To find all decompositions of a list into two
%%  lists, use merge.  NG if all args are var.

ordered_merge([], List, List) :-
  islist(List),
  !.

ordered_merge([Elem1 | Rest1], [], [Elem1 | Rest1]) :- !.

ordered_merge([Elem1 | Rest1], [Elem2 | Rest2], [Elem1 | Rest3]) :-
  (var(Elem2) -> true; Elem1 @< Elem2),
  !,
  ordered_merge(Rest1, [Elem2 | Rest2], Rest3).

ordered_merge(List1, [Elem2 | Rest2], [Elem2 | Rest3]) :-
  ordered_merge(List1, Rest2, Rest3).

%%  merge(List1, List2, List3) iff List3 is a random merge of List1 and
%%  List2, ie, order is preserved within List1 and List2 but not between
%%  them.  This is like a combination: pick 2 of 5.  Eg, for
%%  List1=[1,2,3], and List2=[a,b], List3 can be [1,2,a,3,b],
%%  [a,1,2,b,3],... NG if List3 and (List1 or List2) are var.

merge([], List, List) :- islist(List).

merge([Elem | Rest], [], [Elem | Rest]).

%  pick from List1
merge([Elem1 | Rest1], [Elem2 | Rest2], [Elem1 | Rest3]) :-
  merge(Rest1, [Elem2 | Rest2], Rest3).

%  pick from List2
merge([Elem1 | Rest1], [Elem2 | Rest2], [Elem2 | Rest3]) :-
  merge([Elem1 | Rest1], Rest2, Rest3).

%%  ***************** Structures ****************

%%  full_name(Term, Name) iff Name is the name of Term, which may be
%%  atomic or compound.  NG if both args are var.

full_name(Term, Name) :-
  atomic(Term),
  !,
  name(Term, Name).

full_name(Term, Name) :-
  var(Term),
  telling(Cur_output),
  tell('x.x'),
  print_string(Name),
  print_string("."),
  told,
  tell(Cur_output),
  seeing(Cur_input),
  see('x.x'),
  read(Term),
  seen,
  !,
  see(Cur_input).

full_name(Term, Name) :-
  Term =.. [Functor, Arg1 | Arglist],
  name(Functor, Func_name),
  full_name(Arg1, Arg1_name),
  full_name_list(Arglist, Arglist_name),
  append_n([Func_name, "(", Arg1_name, Arglist_name, ")"], Name).

full_name_list([], []) :- !.
full_name_list([Arg1 | Arg_rest], Arglist_name) :-
  full_name(Arg1, Arg1_name),
  full_name_list(Arg_rest, Arg_rest_name),
  append_n([",", Arg1_name, Arg_rest_name], Arglist_name).

%%   tree_position(Term, Subterm, Location) iff Term is a
%%   non-variable containing Subterm, at the Location, which
%%   is a list of numbers corresponding to position, eg, for
%%   Term = a(b,c,d(e,f),g), the solutions are:
%%
%%    Subterm             Location
%%    -------             --------
%%    a(b,c,d(e,f),b)     []
%%    b                   [1]
%%    c                   [2]
%%    d(e,f)              [3]
%%    e                   [3,1]
%%    f                   [3,2]
%%    g                   [4]

tree_position(Term, Sub, []) :-
  nonvar(Term),
  Term = Sub.
tree_position(Term, Sub, [Number | Sub_pos]) :-
  Term =.. [Func | Args],
  position(Args, Elem, Number),
  tree_position(Elem, Sub, Sub_pos).

%%  contains(Term, Sub) iff Term is a non-variable containing Sub,
%%  either immediately or indirectly.  NG if arg1 is var.

contains(Term, Sub) :-
  nonvar(Term),
  Term = Sub.
contains(Term, Sub) :-
  Term =.. [Func | Args],
  member(Elem, Args),
  contains(Elem, Sub).

%%  compound_contains(Term, Tester, Count) iff Count is the number of
%%  terms, compound or atomic, within Term for which Tester is true.
%%  Tester is either the name of a unary predicate, or a list of predicate
%%  name, followed by arguments.

compound_contains(Term, Tester, Count) :-
  (Tester = [Pred | Args] -> true;
                             (Pred = Tester, Args = [])),
  Test =.. [Pred, Term | Args],
  !,
  (Test -> N=1; N=0),
  (simple(Term) -> Count = N;
                   (Term =.. [Functor | Arglist],
                    compound_contains_list(Arglist, Pred, Args, Subcount),
                    Count is Subcount + N)).

compound_contains_list([], _, _, 0).
compound_contains_list([Elem | Rest], Pred, Args, Subcount) :-
  compound_contains(Elem, [Pred | Args], Elemcount),
  compound_contains_list(Rest, Pred, Args, Restcount),
  Subcount is Elemcount + Restcount.

%%  atomic_contains(Term, Tester, Count) iff Count is the number of
%%  atomic terms or vars within Term for which Tester is true.  Tester is
%%  either the name of a unary predicate, or a list of predicate
%%  name, followed by arguments.

atomic_contains(Term, Tester, Count) :-
  (Tester = [Pred | Args] -> true;
                             (Pred = Tester, Args = [])),
  !,
  (simple(Term) -> (Test =.. [Pred, Term | Args],
                      (Test -> Count=1; Count=0)
                   );
                   (Term =.. Termlist,
                    atomic_contains_list(Termlist, Pred, Args, Count)
                   )).

atomic_contains_list([], _, _, 0).
atomic_contains_list([Elem | Rest], Pred, Args, Count) :-
  atomic_contains(Elem, [Pred | Args], Elemcount),
  atomic_contains_list(Rest, Pred, Args, Restcount),
  Count is Elemcount + Restcount.

%%  ************* Input and Output *************

%%  readline(List) iff user enters characters of list, followed
%%  by a carriage return (whose ASCII code is 10, under VMS).

readline(List) :-
  get0(Char),
  (Char=10 -> List = [];
     (Char=26 -> List = end_of_file;
                (List = [Char | Rest], readline(Rest))
  )  ).

%%   user_pick(List, Selection) iff Selection is a member of the list
%%   chosen by the user.  NG if arg1 is var.

user_pick(List, Selection) :-
  List = [E | R],
  nl, print('Select one of the following: '),
  user_pick_1(List, 1),
  nl,
  print('Enter the number of your preferred entry, '),
  print('terminated by a period.'),
  nl,
  print('Anything besides a valid number will select none of the above.'),
  nl,
  read(Num),
  !,
  integer(Num),
  position(List, Selection, Num).

user_pick_1([], _).
user_pick_1([Elem | Rest], N) :-
  nl, print('  '),
  (N < 10 -> print(' '); true),
  print(N),
  print(' - '),
  print(Elem),
  N1 is N+1,
  user_pick_1(Rest, N1).

%%  user_yes_no(Prompt) iff the user responds affirmatively to the Prompt.
%%  The Prompt should be a yes or no type question, suitable for printing,
%%  user_yes_no('Do you wish to continue?').  The current input stream
%%  is assumed to be set correctly.

user_yes_no(Prompt) :-
  nl, print(Prompt),
  print('  (respond with "y." or "n.")'), nl,
  read(Ans),
  !,
  (Ans = y -> true;
    (Ans = n -> (!,fail);
      (user_yes_no(Prompt)
  ) ) ).

%%  print_string(S) succeeds if S is a list of printable integers, and
%%  it prints the string as a side-effect to the current output stream.

print_string([]).
print_string([Elem | Rest]) :-
  integer(Elem),
  Elem > 31,
  Elem < 127,
  put(Elem),
  print_string(Rest).

%%  tree_print(Term) prints term in tree-fashion, indented by two.

tree_print(Term) :-
  nl,
  tree_print_sub(Term,0,'  ').

tree_print_sub(Term, Depth, Prefix) :-
  print_prefix(Depth, Prefix),
  (var(Term) -> (Name = Term, Arglist = []);
                Term =.. [Name | Arglist]),
  print(Name), nl,
  Depth_plus is Depth+1,
  print_args(Arglist, Depth_plus, Prefix).

print_prefix(0,_).
print_prefix(Depth, Prefix) :-
  Depth > 0,
  print(Prefix),
  Depth_minus is Depth-1,
  print_prefix(Depth_minus, Prefix).

print_args([],             Depth, _).
print_args([First | Rest], Depth, Prefix) :-
  tree_print_sub(First, Depth, Prefix),
  print_args(Rest, Depth, Prefix).

%%  tree_list_print(Term) prints Term just like tree_print, except that
%%  it keeps lists flat (at the same level), rather than indenting.
%%  Further it uses '[' and ']' to delimit the lists instead of the
%%  true internal functor '.'.

tree_list_print(Term) :-
  nl,
  tree_list_print_sub(Term,0,'  ').

tree_list_print_sub(Term, Depth, Prefix) :-
  print_prefix(Depth, Prefix),
  (var(Term) -> (Name = Term, Arglist = []);
     (non_null_list(Term) ->
         (Name = '[', Term = Arglist);
         Term =.. [Name | Arglist]
  )  ),
  print(Name), nl,
  D_plus is Depth+1,
  list_print_args(Arglist, D_plus, Prefix),
  (Name == '[' -> (print_prefix(Depth, Prefix), print(']'), nl); true).

list_print_args([],             Depth, _).
list_print_args([First | Rest], Depth, Prefix) :-
  tree_list_print_sub(First, Depth, Prefix),
  list_print_args(Rest, Depth, Prefix).

%%  *************** Sets ****************

%%  Following predicates treat lists as sets.  Note that only
%%  outer brackets are interpreted as set-constructors.  Any inner
%%  nested brackets are interpreted as ordered lists - thus these
%%  sets are "flat"; they do not contain other sets.  Eg, the
%%  set: [a,b,[1,2,1]] has a list as its 3rd element and is
%%  distinct from: [a,b,[2,1]], but *not* from [b,b,[1,2,1],a,a],
%%  since duplication and order at the highest (set) level are
%%  insignificant.
%%
%%  To treat lists as multi-level sets, perform tot_sort on them, eg
%%  both [a,b,[1,2,1]] and [a,b,[2,1]] map to: [a,b,[1,2]], which
%%  may be thought of as a normalized form for multi-level sets.
%%
%%  Thus, the only easy choice is to treat all inner brackets as
%%  list-constructors (default) or as set-constructors (using
%%  tot_sort).  To explicitly distinguish and therefore allow
%%  both kinds, a structure must be set up, something like:
%%  set(List) to be interpreted as a set.  Then, eg:
%%
%%  set([1,2,[d,d,c,a]])      : 3rd element is list
%%
%%  set([1,2,set([d,d,c,a])]) : 3rd element is set
%%
%%  [1,2,1]                   : (ordered) list of 3 integers
%%
%%  set([1,2,1])              : (unordered) set of 2 integers (= set([1,2]))
%%
%%  Set structure would accept unordered/duplicate elements, but
%%  assume their insignificance.  Conversion predicate might be:
%%
%%  set_list(Set, List) :- Set =.. [set, List], is_real_list(List).

%%  subset(Part, Whole) iff Part is an subset of Whole.
%%  NG if both args are var.

subset(Part, Whole) :-
  var(Whole) -> (var(Part) -> fail;
                              subset_cv(Part, Whole)
                );
                (remove_dupl(Whole, Shorn_whole),
                 (var(Part) -> subset_vc(Part, Shorn_whole);
                               subset_cc(Part, Shorn_whole))
                ).

subset_vc([Elem | Rest_part], [Elem | Rest_whole]) :-
  subset_vc(Rest_part, Rest_whole).
subset_vc(        Rest_part,  [Elem | Rest_whole]) :-
  subset_vc(Rest_part, Rest_whole).
subset_vc([],[]).

subset_cv(Part, Whole) :- remove_dupl(Part, Whole).

subset_cc([], Whole) :- islist(Whole).
subset_cc([Elem | Rest_of_part], Whole) :-
  member(Elem, Whole),
  subset_cc(Rest_of_part, Whole).

%%  intersection(S1, S2, Ans) iff Ans is the intersection of S1 and S2.
%%  NG if arg1;2 is var.

intersection(S1, S2, Ans) :-
   remove_dupl(S1, Better_S1),
   (var(Ans) -> intersection_1(Better_S1, S2, Ans);
                intersection_2(Better_S1, S2, Ans)
   ).

intersection_1([], S2, []).
intersection_1([E | R1], S2, Ans) :-
   member(E, S2) -> (Ans = [E | RA],
                     intersection_1(R1, S2, RA)
                    );
                     intersection_1(R1, S2, Ans).

intersection_2(S1, S2, Ans) :-
   intersection_1(S1, S2, X),
   set_equal(X, Ans).

%%  union(S1, S2, Ans) iff Ans is the union of S1 and S2.
%%  NG if arg1;2 is var.

union(S1, S2, Ans) :-
   var(Ans) -> union_1(S1, S2, Ans);
               union_2(S1, S2, Ans).

union_1(S1, S2, Ans)  :- append(S1, S2, X),
                         remove_dupl(X, Ans).

union_2(S1, S2, Ans) :- union_1(S1, S2, X),
                        set_equal(X, Ans).

%%  set_diff(S1, S2, Ans) iff Ans is the set difference S1 - S2.
%%  NG if arg1;2 is var.

set_diff(S1, S2, Ans) :-
   remove_dupl(S1, Better_S1),
   (var(Ans) -> set_diff_1(Better_S1, S2, Ans);
                set_diff_2(Better_S1, S2, Ans)
   ).

set_diff_1(S1, S2, Ans) :- delete_all(S2, S1, Ans).

set_diff_2(S1, S2, Ans) :- set_diff_1(S1, S2, X),
                           set_equal(X, Ans).

%%  set_equal(A,B) iff A and B contain the same elements (set equality).

set_equal(A, B) :-
  nonvar(A), nonvar(B), set_equal_cc(A, B);
     var(A),    var(B), A = B;
     var(A), nonvar(B), remove_dupl(B, A);
  nonvar(A),    var(B), remove_dupl(A, B).

set_equal_cc(A, B) :- subset(A,B), subset(B,A).

%%   disjoint(A,B) iff A and B have no common element.
%%   NG if arg1;2 is var.

disjoint(A,B) :- not(joint(A,B)).

%%   joint(A,B) iff A and B have at least one common element.
%%   NG if arg1;2 is var.

joint(A,B) :- member(E,A), member(E,B).

%%  set_plus(S1, S2, Both) iff S1 and S2 are disjoint, and their
%%  union equals Both.  NG if Both and (S1 or S2) are var.

set_plus(S1, S2, Both) :-
  nonvar(S1) -> (nonvar(S2) -> set_plus_ccx(S1, S2, Both);
                               set_plus_vxc(S2, S1, Both)
                );
                set_plus_vxc(S1, S2, Both).

set_plus_ccx(S1, S2, Both) :- disjoint(S1, S2), union(S1, S2, Both).

set_plus_vxc(S1, S2, Both) :-
  nonvar(Both),
  subset(S2, Both),
  set_diff(Both, S2, S1).

%%  powerset(Set, Power) iff Power is the power set of Set, ie, a list
%%  of all subsets of Set.  NG if arg1 is var.

powerset(Set, Power) :-
  remove_dupl(Set, Shrunk_set),
  powerset_xx(Shrunk_set, Power).

powerset_xx([], [[]]).
powerset_xx([Elem | Rest], Power) :-
  powerset_xx(Rest, Sub_Power),
  double_list(Elem, Sub_Power, Power).

double_list(New_elem, [Single], [Single, [New_elem | Single]]) :- !.
double_list(New_elem, [Elem | Rest], [Elem, [New_elem | Elem] | List]) :-
  double_list(New_elem, Rest, List).

%%  Here's an alternative version of powerset, perhaps a bit less
%%  efficient, but easier to understand.  It is deliberately
%%  commented out, so as not to conflict with the above.
%%  NG if arg1 is var.
%%
%%  powerset(Set, Power) :- setof(Sub, subset(Sub, Set), Power).

%%  partition(S1, S2) iff S2 is a partition of S1, ie S2 is a set of
%%  non-null pairwise disjoint sets, whose union = S1.
%%  NG if both args are var.

partition(S1, S2) :-
  nonvar(S1), nonvar(S2),        partition_cc(S1,      S2);
     var(S1), nonvar(S2),        partition_vc(S1,      S2);
  nonvar(S1),    var(S2),
       remove_dupl(S1, Slim_S1), partition_cv(Slim_S1, S2).

partition_vc(S1, S2) :-
  not(member([], S2)),      %  partition members must be non-null
  append_n(S2, S1),         %  take all elements of all members
  no_dupls(S1).             %  ensure pairwise disjoint

partition_cc(S1, S2) :-
  partition_vc(Test, S2),
  set_equal(Test, S1).

partition_cv([Elem], [[Elem]]).
partition_cv([Elem | Rest], S2) :-
  partition_cv(Rest, Sub_S2),      %  take a partition of set minus elem
  ( S2 = [[Elem] | Sub_S2];        %  either add a new singleton member
                                   %  containing elem
    (insert(Sub_S2_Elem, Sub_S2_Less_1, Sub_S2),
                                   %  or take one of the old members
     S2 = [[Elem | Sub_S2_Elem] | Sub_S2_Less_1]
    )                              %  and add elem to it
  ).

%%  closure2(List1, Pred2, List2) iff List2 is the closure of List1
%%  according to the binary predicate Pred2, for which the first
%%  operand is the "old" element and the second is the "new" or
%%  generated element.  NG if arg1 or arg2 is var.

closure2(List1, Pred2, List2) :-
  sort(List1, Sorted_list1),
  (setof(Arg2, Arg1^Test^(member(Arg1, Sorted_list1),
                          Test =.. [Pred2, Arg1, Arg2],
                          Test),
         New_list)
    ->
     (append(Sorted_list1, New_list, Combined_list),
      sort(Combined_list, Sorted_combined_list),
      (Sorted_combined_list = Sorted_list1
           -> List2 = Sorted_combined_list;
              closure2(Sorted_combined_list, Pred2, List2)
     ));
     List2 = Sorted_list1
  ).

%%  closure3(List1, Pred3, List2) iff List2 is the closure of List1
%%  according to the ternary predicate Pred3, for which the first 2
%%  operands are the "old" elements and the third is the "new" or
%%  generated element.  NG if arg1 or arg2 is var.

closure3(List1, Pred3, List2) :-
  sort(List1, Sorted_list1),
  (setof(Arg3, Arg1^Arg2^Test^(member(Arg1, Sorted_list1),
                               member(Arg2, Sorted_list1),
                               Test =.. [Pred3, Arg1, Arg2, Arg3],
                               Test),
         New_list)
     ->
       (append(Sorted_list1, New_list, Combined_list),
        sort(Combined_list, Sorted_combined_list),
        (Sorted_combined_list = Sorted_list1
             -> List2 = Sorted_combined_list;
                closure3(Sorted_combined_list, Pred3, List2)
       ));
       List2 = Sorted_list1
  ).

%%  **************** Numeric ****************

%%  numvar(X, Limit) iff X and Limit are non-negative integers,
%%  with X =< Limit.  In generative mode, solutions are produced
%%  from zero to higher values.  Either, both, or neither argument
%%  may be instantiated.  NG if arg1 is fraction and arg2 var.

numvar(X,Limit) :- var(Limit),    natural(Limit), numvar(X, Limit).
numvar(X,Limit) :- nonvar(Limit), natural(X), ((X>Limit, !, fail); true).

%%  natural(X) iff X is a non-negative integer.

natural(X) :- nonvar(X), integer(X), X>=0;
              var(X), gen_integer(X, 0).

%%  gen_integer(X,Seed) generates integers, incrementing from Seed.
%%  NG if arg1 is nonvar or arg2 is var.

gen_integer(X, Seed) :-
  var(X),
  integer(Seed),
  gen_integer_vc(X, Seed).

gen_integer_vc(X, Seed) :-
  X is Seed;
  (New_seed is Seed+1,
   gen_integer_vc(X, New_seed)).

%%  random(Max, N) instantiates N to a random integer between
%%  1 and Max.  NG if arg1 is var.

seed(13).

random(R,N) :-
        retract(seed(S)),
        N is (S mod R) +1,
        NewSeed is (125*S+1) mod 4096,
        asserta(seed(NewSeed)),!.

%%  rationalize(Exprs, R_Exprs) iff Exprs is a numeric expression,
%%  and R_Exprs is a mostly evaluated form of the expression.
%%  Rational numbers are preserved and reduced to lowest terms,
%%  or integers if possible.  If no floating-point numbers are in
%%  the expression, the results are exact, except for raising to a
%%  fractional power.  NG if Exprs is not a fully instantiated
%%  numeric expression.  Routines for handling exponentiation
%%  override some C-Prolog defaults.  X^0 always equals 1, even for
%%  0^0.  A negative base is allowed with an integer power, so
%%  (-2)^4 = 16, and (-2)^5 = -32.  Negative powers are handled
%%  correctly, eg, 2^(-3) = 1/8, (-2)^(-3) = -1/8, (2/3)^(-3) =
%%  27/8. A negative base to a fractional power fails, as does zero
%%  to a negative power.

rationalize(Exprs, Exprs) :- number(Exprs), !.

%%  handle plus and minus unary ops

rationalize(-(Exprs), R_Exprs) :- rationalize(Exprs*(-1), R_Exprs).

rationalize(+(Exprs), R_Exprs) :- rationalize(Exprs, R_Exprs).

%%  all other unary ops to be handled by regular evaluation.

rationalize(Exprs, R_Exprs) :-
  Exprs =.. [Un_op, Opnd1],
  Un_op \== '+',
  Un_op \== '-',
  rationalize(Opnd1, R_Opnd1),
  Eval =.. [Un_op, R_Opnd1],
  R_Exprs is Eval.

%%  handle binary ops

rationalize(Exprs, R_Exprs) :-
  Exprs =.. [Bin_op, Opnd1, Opnd2],
  rationalize(Opnd1, R_Opnd1),
  rationalize(Opnd2, R_Opnd2),
  (plain_eval(Bin_op, R_Opnd1, R_Opnd2)
        -> (Eval =.. [Bin_op, R_Opnd1, R_Opnd2], R_Exprs is Eval, !);
           (rationalize_1(Bin_op, R_Opnd1, R_Opnd2, R_Exprs), !)
  ).

%%  plain_eval is true if the expression can be evaluated directly,
%%  either because: 1) the result will be exact (eg integer subtraction),
%%  or 2) the result is (probably) not rational anyway (eg, when
%%  a floating-point number is an operand, or raising to a fractional
%%  power).

plain_eval(Bin_op, R_Opnd1, R_Opnd2) :-
  not(member(Bin_op, [+,-,*,/,^])).

plain_eval(Bin_op, R_Opnd1, R_Opnd2) :-
  Bin_op \== '^',
  (float(R_Opnd1); float(R_Opnd2)).

plain_eval(Bin_op, R_Opnd1, R_Opnd2) :-
  integer(R_Opnd1), integer(R_Opnd2),
  member(Bin_op, [+,-,*]).

plain_eval(^, Base, Power) :-
  Base > 0,
  (not(integer(Power));
   Power =:= 0;
   float(Base);
   (number(Base), Power > 0)
  ).

rationalize_1(^, 0, Power, _) :-
  Power < 0,
  nl, print('Error: raising zero to negative power.'), nl,
  !, fail.

rationalize_1(^, 0, Power, 0) :-
  Power > 0.

rationalize_1(^, _, 0, 1).

rationalize_1(^, Base, Power, R_Exprs) :-
  Base < 0,
  not(integer(Power)),
  nl, print('Error: raising negative to fractional power.'), nl,
  !, fail.

rationalize_1(^, Base, Power, R_Exprs) :-
  Base < 0,
  float(Base),
  Mag is (-Base) ^ Power, !,
  (Power mod 2 =:= 0 -> R_Exprs is  Mag;
                        R_Exprs is -Mag).

rationalize_1(Bin_op, R_Opnd1, R_Opnd2, R_Exprs) :-
  (integer(R_Opnd1) -> (Num1 = R_Opnd1, Den1 = 1);
                        Num1/Den1 = R_Opnd1),
  (integer(R_Opnd2) -> (Num2 = R_Opnd2, Den2 = 1);
                        Num2/Den2 = R_Opnd2),
  rationalize_2(Bin_op, Num1, Den1, Num2, Den2, Num, Den),
  (Den =:= 0 -> (nl, print('Error: divide by zero'), nl,
                 !, fail
                );
                reduced(Num/Den, R_Exprs)
  ),
  !.

rationalize_2(+, Num1, Den1, Num2, Den2, Num, Den) :-
  Num is Num1*Den2 + Num2*Den1,
  Den is Den1*Den2.

rationalize_2(-, Num1, Den1, Num2, Den2, Num, Den) :-
  Num is Num1*Den2 - Num2*Den1,
  Den is Den1*Den2.

rationalize_2(*, Num1, Den1, Num2, Den2, Num, Den) :-
  Num is Num1*Num2,
  Den is Den1*Den2.

rationalize_2(/, Num1, Den1, Num2, Den2, Num, Den) :-
  Num is Num1*Den2,
  Den is Den1*Num2.

rationalize_2(^, Num1, 1, Num2, 1, Num, Den) :-
  abs(Num1, Base),
  abs(Num2, Power),
  Mag is Base^Power,
  ((Num1 >= 0; Power mod 2 =:= 0) -> Sign = 1; Sign = -1),
  (Num2 < 0 -> (Num = Sign,      Den = Mag);
               (Num is Sign*Mag, Den = 1)).

rationalize_2(^, Num1, Den1, Num2, 1, Num, Den) :-
  rationalize_2(^, Num1, 1, Num2, 1, NumNum, NumDen),
  rationalize_2(^, Den1, 1, Num2, 1, DenNum, DenDen),
  Num is NumNum*DenDen,
  Den is NumDen*DenNum,
  !.

%%   reduced(Exprs, R_Exprs) iff R_Exprs is Exprs reduced to
%%   lowest terms.  R_Exprs is an integer if the denominator
%%   would be 1.  NG if arg1 is var.

reduced(Exprs,Exprs) :- integer(Exprs), !.
reduced(Num/Den, Ans) :-
  Num mod Den =:= 0,
  Ans is Num/Den, !.
reduced(Num_In/Den_In, Num_Out/Den_Out) :-
  gcd(Num_In, Den_In, GCD),
  Num_Temp is Num_In/GCD,
  Den_Temp is Den_In/GCD,
  (Den_Temp < 0 -> (Num_Out is -Num_Temp, Den_Out is -Den_Temp);
                   (Num_Out is  Num_Temp, Den_Out is  Den_Temp)
  ),
  !.

%%  gcd(X, Y, Ans) iff Ans is the greatest common divisor of the
%%  integers X and Y.  NG if arg1 or arg2 is var.

gcd(X, 0, X) :- !.
gcd(0, X, X) :- !.
gcd(X, Y, Ans) :-
  L is Y mod X,
  gcd(L, X, Ans), !.

%%  abs(X, Y) iff Y is the absolute value of X.  Ng if arg1 is var.

abs(X,Y) :-
  X < 0 -> Y is -X; Y is X.

%%  ***************** Control ****************

%%  loop(Pred) repeatedly invokes Pred until it fails.  Loop always
%%  fails and so is executed only for side-effects.  If Pred has
%%  several clauses, enclose in parens.  A typical use might be:
%%     loop((pred(X,Y), write(X), write('  '), write(Y), nl)).
%%  to write out all solutions.

loop(Pred) :-
  repeat,
  (Pred; (!, fail)),     %   Pred succeeds, or kill loop
  fail.                  %   Force repetition.

%%  invoke(Pred_list) creates a predication from Pred_list and
%%  invokes it.  Pred_list must be instantiated.  The reason for
%%  the 2nd arg is that otherwise invoke will quit after first
%%  success with all nonvars.  Even if all args are nonvar,
%%  users may wish to re-invoke by re-instantiating Pred_list.

invoke(Pred_list, Pred_call) :- Pred_call=..Pred_list, Pred_call.

%%  ************* Extended Logic **************

%%  counter_eg(If, Then) iff there exists some instantiation of If
%%  and Then such that If is true and Then is false.  If and Then
%%  must be predicates.  Their arg-lists may contain vars, but the
%%  predicates themselves must be specified.  Eg:
%%  counter_eg((gender(X,male), gender(Y,female)), taller(X,Y)).
%%  may be understood as "Are there any counter-examples to the rule
%%  that if X is male and Y is female, then X is taller than Y?"

counter_eg(If, Then) :- If, not(Then).

%%  implies(If, Then) iff If and Then form a true implication for
%%  this DB, ie there are no counter-examples.

implies(If, Then) :- not(counter_eg(If, Then)).

%%  The following stuff is meant to provide some capability for expressing
%%  disjunction and negation.  The three predicates visible at the user
%%  level are:
%%
%%    or(L) - to ask if this is a true disjunction; defined herein, but
%%            invoked by the user.
%%
%%    ground_or(L) - to tell the system that this is a true disjunction,
%%            ie, at least one disjunct is true.  Defined by the user
%%            as part of the DB.
%%
%%    false(P) - says that P is definitely false (not just not provable,
%%            a la "not" in normal Prolog).  Defined by the user as part
%%            of the DB.
%%
%%    start_or. - the user has to invoke this to fire up the system,
%%            before he starts asking things.
%%
%%  eg, if the user, in the DB, says:
%%
%%    ground_or([e,f,g,h,i]).
%%    false(f).
%%    false(g).
%%    ground_or([a,b,c]) :- d.
%%    d.
%%    false(b).
%%    false(a) :- w; d.
%%
%%  and then invokes start_or, the system will be able to conclude
%%  that the following succeed:
%%
%%    c.                    (because (a or b or c) is true and a and b are
%%                           both false)
%%    or([x1,d,x2]).        (because d is a true disjunct)
%%    or([f,g,t,h,r,e,i]).  (because it contains a true disjunct: [e,f,g,h,i]).
%%    or([e,h,i])           (because its residue from [e,f,g,h,i] is [f,g] all
%%                           of which are false.)
%%
%%  It's probably not wise for "ground_or" itself to depend on "or".


%%  or(L) iff L is a list of disjuncts, at least one of which is true.
%%  NG if L is var.

or(L) :- nonvar(L), L = [_|_], (or_1(L); or_2(L); or_3(L)).

%%  or_1 tries to find a true individual disjunct.
%%  or_2 tries to find a subset of L already known to be true.
%%  or_3 tries to find a superset of L already known to be true,
%%       and then show the rest are false.

or_1([E | R]) :- E; or_1(R).
or_2(L1)      :- ground_or(L2), subset(L2, L1).
or_3(L)       :- ground_or(Ground_list),
                 set_plus(L, Residue, Ground_list),
                 false_list(Residue).

%%  false_list(L) iff all members of L are provably false.

false_list([]).
false_list([E1 | Rest]) :- false(E1), false_list(Rest).

%%  start_or always fails, but in the meantime, it builds clauses
%%  for each disjunct of the ground_or's.

start_or :-
  clause(ground_or(Disjuncts), Antecedents),
  set_plus([One_disjunct], Rest, Disjuncts),
  assertz((One_disjunct :- Antecedents, false_list(Rest))),
  fail.

%%  Some meta-logical facilities coming up.

%%  kb_object(Type, Head, Tail) iff there is a clause "Head :- Tail."
%%  in the current program.  If the Tail=true, and the Head is
%%  composed of all constants, Type = fact, otherwise Type = rule.

kb_object(Type, Head, Tail) :-
  current_predicate(_, Head),
  clause(Head, Tail),
  ((Tail = true, constant_term(Head)) -> Type = fact;
                                         Type = rule).

%%  assert1(Term) iff there is exactly one matching instance of Term
%%  asserted in the DB.  Term should be at least partially instantiated.

assert1(Term) :-
  repeat,
  (retract(Term) -> fail;
                    assert(Term)
  ),
  !.

%%  ed allows for some very primitive run-time program modification.
%%  The user can enter terms, facts or rules, without the "extra"
%%  parentheses needed by assert, and ed then asserts and writes out
%%  the term for later editing into the permanent program.

ed :-
   nl, print('Enter fact or rule ([] to quit).'), nl,
   read(T),
   (T = [] -> true; (ed_1(T), !, ed)).

ed_1(T) :-
  assert(T),
  tell('x.tmp'),
  nl, writeq(T), put(46), nl,
  tell(user).

%%   End of Prolog Utilities
%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

------------------------------

End of PROLOG Digest
********************
Technical Details
Message-ID: <8612010845.AA23915@ucbvax.Berkeley.EDU>
Path: arc1-ca.pugleaf.net!utzoo!mnetor!seismo!lll-crg!ames!ucbcad!ucbvax!SU-SCORE.ARPA!PROLOG-REQUEST

Show Full Headers
Complete Article Headers
Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP
Path: arc1-ca.pugleaf.net!utzoo!mnetor!seismo!lll-crg!ames!ucbcad!ucbvax!SU-SCORE.ARPA!PROLOG-REQUEST
From: PROLOG-REQUEST@SU-SCORE.ARPA (Chuck Restivo, The Moderator)
Newsgroups: comp.lang.prolog
Subject: PROLOG Digest   V4 #80
Message-ID: <8612010845.AA23915@ucbvax.Berkeley.EDU>
Date: Sun, 30-Nov-86 15:12:00 EST
Article-I.D.: ucbvax.8612010845.AA23915
Posted: Sun Nov 30 15:12:00 1986
Date-Received: Mon, 1-Dec-86 07:19:08 EST
Sender: daemon@ucbvax.BERKELEY.EDU
Reply-To: PROLOG@SU-SCORE.ARPA
Organization: The ARPA Internet
Lines: 1430