% Library of frequently used predicates

:- op( 900, fy, not).

% not Goal): negation as failure; 
%   Note: This is often available as a built-in predicate,
%   often written as prefix operator "\+", e.g. \+ likes(mary,snakes)

not Goal  :-
  Goal, !, fail
  ; 
  true.

% once( Goal):
%   Produce one solution of Goal only (only the first solution)

once( Goal)  :-
  Goal, !.

% member( X, List): X is a member of List

member(X,[X | _]).                 % X is head of list

member( X, [_ | Rest])  :-         
  member( X, Rest).                % X is in body of list

%  conc(L1,L2,L3): list L3 is th econcatenation of lists L1 and L2

conc( [], L, L).

conc( [X | L1], L2, [X | L3])  :-
  conc( L1, L2, L3).

% del(X,L0,L): List L is equal to list L0 with X deleted
%   Note: Only one occurrence of X is deleted

del( X, [X | Rest], Rest).        % Delete the head

del( X, [Y | Rest0], [Y | Rest])  :-
  del( X, Rest0, Rest).

%  subset( Set, Subset):  list Set contains all the elements of list Subset 
%    Note: The elements of Subset appear in Set in the same order as in Subset

subset( [], []).

subset( [First | Rest], [First | Sub])  :-       % Retain First in subset
  subset( Rest, Sub).

subset( [First | Rest], Sub)  :-                 % Remove First
  subset( Rest, Sub).

%  set_difference( Set1, Set2, Set3):  Set3 is the list representing 
%    the difference of sets represented by lists Set1 and Set2

set_difference( [], _, []).

set_difference( [X | S1], S2, S3)  :-
  member( X, S2),  !,                            % X in set S2
  set_difference( S1, S2, S3).

set_difference( [X | S1], S2, [X | S3])  :-      % X not in S2
  set_difference( S1, S2, S3).

%  length( List, Length): Lentgh is the length of List
%    Note: Often provided as built-in predicate
%    The definition below is tail-recursive 
%    It can also be used to generate efficiently list of given length

length( L, N)  :-
  length( L, 0, N).

length( [], N, N).

length( [_ | L], N0, N)  :-
  N1 is N0 + 1,
  length( L, N1, N).



%  max( X, Y, Max): Max = max(X,Y)

max( X, Y, Max)  :-
  X >= Y, !, Max = X
  ;
  Max = Y.

%  min( X, Y, Min): Min = min(X,Y) 

min( X, Y, Min)  :-
  X =< Y, !, Min = X
  ;
  Min = Y.


% copy_term( T1, T2): T2 is equal to T1 with variables renamed
% This is often available as a built-in predicate
% Procedure below assumes that copy_term is called with T2 uninstantiated

copy_term( Term, Copy)  :-
  asserta( term_to_copy( Term)),
  retract( term_to_copy( Copy)), !.
