%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% 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.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

:- 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)).

:- assert((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.
%
:- assert(inserted(dummy)).
:- retract(inserted(dummy)).
:- assert(deleted(dummy)).
:- retract(deleted(dummy)).
:- assert(modifiable(dummy)).
:- retract(modifiable(dummy)). 
:- assert(empty(dummy)).
:- retract(empty(dummy)).

%exists(dummy) :- fail.
%empty(P) :- modifiable(P), not(inserted(P)).
%empty(P) :- not(exists(P)).
%exists(P) :- modifiable(P), inserted(P), not(deleted(P)).

