%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% This file contains working code for backtrackable updates. 
% It works with many different Prologs, and is the conceptual kernal
% of all my TR prototypes.  If an update log file has been defined, then
% all updates are also recorded there.
%                                                            
% 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.

:- import  logWrite/1 from 'recovery.pl'.


% Check whether atom P has a base predicate symbol.
% If not, issue a warning message, and fail.

basePredicate(P) :- modifiable(P), !.

basePredicate(P) :- write('CTR Warning: The atom '),
                    write(P),
                    write(' does not have a base predicate symbol.'),
                    nl, fail.


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

empty(P) :- basePredicate(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/1
% removes this atom during backtracking.  The predicate logWrite/1
% records the insertion in a log.
%
ins(P) :- basePredicate(P),
          not(inserted(P)),
          assert(inserted(P)),
          logWrite(ins(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/1 puts the atom back during
% backtracking.  The predicate logWrite/1 records the insertion in a
% log.
%
ins(P) :- basePredicate(P),
          deleted(P),
          retract(deleted(P)),
          logWrite(ins(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/1 removes this atom during backtracking.
% The predicate logWrite/1 records the deletion in a log.
%
del(P) :- basePredicate(P),
          inserted(P),
          not(deleted(P)),          
          assert(deleted(P)),
          logWrite(del(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)).



% Declare to the XSB system that some predicates exsist.
%
declarePred(P) :- assert(P), retract(P).

 :- declarePred(modifiable(dummy)).
 :- declarePred(inserted(dummy)).
 :- declarePred(deleted(dummy)).

% :- dynamic(modifiable/1).
% :- dynamic(inserted/1).
% :- dynamic(deleted/1).

%inserted(dummy) :- fail.
%deleted(dummy) :- fail.
%modifiable(dummy) :- fail. 
%empty(dummy) :- fail.

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

