%
%    This file contains working code for backtrackable updates
%     It works with many different Prologs, and is the conceptual kernel
%     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.
%


:- export ins/1, asrt/1, del/1, db/1, empty/1, updat/1.
% Atom P is in the database if it has been inserted and not deleted.
%
db(P) :- modifiable(P), inserted(P), not(deleted(P)).

empty(P) :- modifiable(P), not(db(P)).

updat(P) :- modifiable(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) :- modifiable(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) :- modifiable(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) :- modifiable(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.
asrt(P) :- assert(modifiable(P)).
% The following dummy rules let Prolog know that the predicates
% inserted/1 and deleted/1 are defined.
%
inserted(dummy) :- fail.
deleted(dummy) :- fail.
modifiable(dummy) :- fail.
empty(dummy) :- fail.