
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% This file contains working code for backtrackable updates. 
% It works with many different Prologs, and is the conceptual kernal
% of all my TR prototypes.          
%                                                            
% The code implements strong inserts and strong deletes.  That is, the
% update ins(p) inserts the atom p into the database, but if p is
% already there, then ins(p) fails.  Likewise, the update del(p) deletes
% atom p from the database, but if p is not there to begin with, then
% del(p) fails.
%
% The code is based on insert and delete tags.  The first time that
% ins(p) is executed, it adds the atom inserted(p) to the database.
% This atom remains in the database until ins(p) is backtracked.
% Subsequently, if del(p) is executed, then the atom deleted(p) is added
% to the database; and if ins(p) is executed, then the atom deleted(p)
% is removed from the database.  This technique prevents the Prolog
% database from being corrupted when backtracking through updates.
%
% The atom db(p) determines whether p is true in the current database
% state.  It returns true iff inserted(p) is in the database and
% deleted(p) is not.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


% Atom P is in the database if it has been inserted and not deleted.
%
db(P) :- inserted(P), not(deleted(P)).


% Initial inserts are a special case.  To insert P for the first time.
% add the atom inserted(P) to the database.  The predicate undo_ins1
% removes this atom during backtracking.
%
ins(P) :- not(inserted(P)),
            assert(inserted(P)),
            undo_ins1(P).

% To undo an initial insert, remove the inserted fact.
%
undo_ins1(P).
undo_ins1(P) :- retract(inserted(P)), !, fail.


% For all subsequent inserts, remove the atom deleted(P) from the
% database.  The predicate undo_ins2 puts the atom back during
% backtracking.
%
ins(P) :- deleted(P),
          retract(deleted(P)),
          undo_ins2(P).


% To undo a subsequent insert, put the delete tag back.
%
undo_ins2(P).
undo_ins2(P) :- asserta(deleted(P)), !, fail.



% To delete P, put the atom deleted(P) into the database.  The
% predicate undo_del removes this atom during backtracking.
%
del(P) :- inserted(P),
          not(deleted(P)),
          assert(deleted(P)),
          undo_del(P).

% To undo a deletion, remove the delete tag.
%
undo_del(P).
undo_del(P) :- retract(deleted(P)), !, fail.



% The following dummy rules let Prolog know that the predicates
% inserted/1 and deleted/1 are defined.
%
inserted(dummy) :- fail.
deleted(dummy) :- fail.
