
%% This file contains various low-level utility  
%% predicates

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Unknown predicates will just fail

:- unknown(_,fail).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% OPERATOR DEFINITIONS

%% For segment macros: stating, defining, evaluating

:- op(250,fx,@).          
:- op(200,xfx,:).         
:- op(450,xfx,/).         
:- op(300,xfx,//).        
:- op(850,fx,macro).      
:- op(850,fx,eval).       
:- op(850,xfx,'<@>').     

%% For defining morphemes

:- op(850,fx,morph). 

%% For defining two level rules

:- op(700,xfx,':=').   % name rule
:- op(600,xfx,'=>').   % optional rule
:- op(600,xfx,'<=>').  % oblig rule
:- op(650,xfx,where).  % side conditions
:- op(640,xfx,and).    % for conditions
:- op(600,xfx,in).     % for conditions (infix member)

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% UTILITIES % UTILITIES % UTILITIES % UTILITIES %%
%% UTILITIES % UTILITIES % UTILITIES % UTILITIES %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

A in B :- member(A,B).    % infix version of member

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

set_of(A,B,C):- setof(A,B,C), !.  % Version of setof
set_of(_,_,[]).                   % returning [] when 
                                  % no answers found

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Own version of bagof 
% -- don't need to quantify all variables to stop 
% them being stuck with first instantiated value.

xbagof(Answer,Goal,BagOfAnswers):- 
   abolish(xbagof_store,1), 
   fail_drive((Goal,
               assert(xbagof_store(Answer)))), 
   xbagof_retract_stores(BagOfAnswers), !. 

xbagof_retract_stores([A|As]):- 
   retract(xbagof_store(A)), 
   xbagof_retract_stores(As). 
xbagof_retract_stores([]). 

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% exhaustively pursues goal X, in failure driven 
% loop 

fail_drive(X):- \+ ((X), fail). 

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% verify that goal X can be sucessfully proven. No 
% variable binding side-effects.

verify(X):- call(\+(\+(X))). 

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% make a ground copy of X

ground_copy(X,Y):-
    copy(X,Y), 
    numbervars(Y,0,_). 

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Set value of flag "Name" to have value X. 
% Value stored as statement: flag(Name,X)

set_flag(Name,X):-
    nonvar(Name),
    retract(flag(Name,_)), !, 
    asserta(flag(Name,X)). 
set_flag(Name,X):-
    nonvar(Name),
    asserta(flag(Name,X)). 

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Gives named positive integer sequences. 
% First call with a name gives N = 1. Each subsequent 
% with that name gives N as 1 more than last time. 

new_num(Name,N):-
    nonvar(Name),
    retract(nflag(Name,M)), !, 
    N is M + 1, 
    asserta(nflag(Name,N)). 
new_num(Name,1):-
    nonvar(Name),
    asserta(nflag(Name,1)). 

% Zeros (i.e. restarts) the number sequence Flag. 

zero_num(Flag):- retract(nflag(Flag,_)), !. 
zero_num(_). 

% One particular number sequence "state", used in 
% defining the `lexical FSM'. 

new_state(N):- new_num(state,N).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% formatting predicate: prints N spaces. 

spaces(N):- N =< 0. 
spaces(N):- N > 0, M is N -1, write(' '), spaces(M).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% List Y, like list X, but has (anonymous) variable
% tail. 

make_diff_list(X,Y):- 
    append(X,_,Y). 

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% explode atom "Atom" to give list of character
% atoms. 

explode_atom(Atom,AtomChars):- 
   name(Atom,Xs), 
   explode_atom2(Xs,AtomChars). 

explode_atom2([],[]). 
explode_atom2([X|Xs],[Y|Ys]):- 
   name(Y,[X]), 
   explode_atom2(Xs,Ys). 

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% First arg = embedded list of single char atoms. 
% Second arg = correspondingly embedded list of 
% ASCII codes for those chars

name_list([],[]). 
name_list([A|As],[B|Bs]):- 
   B = [_|_], !,
   name_list(A,B),  
   name_list(As,Bs). 
name_list([A|As],[B|Bs]):- 
   name(A,[B]), 
   name_list(As,Bs). 

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% First arg is a list-of-lists, which are appended
% to give second arg.

appendn([],[]). 
appendn([A|As],Bs):- 
   append(A,Cs,Bs), 
   appendn(As,Cs). 

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Delete any, and all, occurrences of X from list Y
% giving Z. No occurrences of X in Y required for 
% success

delete_any(_,[],[]). 
delete_any(X,[X|Ys],Zs):- !, delete_any(X,Ys,Zs). 
delete_any(X,[Y|Ys],[Y|Zs]):- delete_any(X,Ys,Zs).  

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Delete first occurrence of X in Y giving Z. 
% Fails if are no occurrences of X in Y. 

delete_one(X,[X|Ys],Ys). 
delete_one(X,[Y|Ys],[Y|Zs]):- 
    \+(X = Y), 
    delete_one(X,Ys,Zs).  

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% X is Nth element in list Xs. 

nth(N,X,Xs):- nth(N,1,X,Xs).
nth(N,N,X,[X|_]).
nth(N,M,X,[_|Xs]):-
    M1 is M+1, 
    nth(N,M1,X,Xs).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Atom has Len characters in its name. 

atom_length(Atom,Len):- 
   name(Atom,Chars),
   length(Chars,Len). 

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Given list of atoms AtomList, returns as Max the 
% number of chars for the atom with the longest name. 

max_atom_length(AtomList,Max):- 
   member(MaxAtom,AtomList), 
   name(MaxAtom,MaxChars), 
   length(MaxChars,Max), 
   \+((member(Atom,AtomList),  
      name(Atom,AtomChars), 
      length(AtomChars,Len), 
      Len > Max)), !. 
max_atom_length([],0). 

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%% Unstable utilities %%%%%%%%%%%%%%%%%%%%%

append([],L,L). 
append([H|T],L,[H|R]):- append(T,L,R). 

member(H,[H|_]).
member(H,[_|T]):- member(H,T). 

reverse(Xs,Ys):- reverse(Xs,Ys,[]).
reverse([],Ys,Ys).
reverse([X|Xs],Ys,Zs):- reverse(Xs,Ys,[X|Zs]). 

copy(X,Y):-
   set_flag(copy,X),
   flag(copy,Y).


