/*---------------------------------------------------------------------------*/ /* KNOWLEDGE BASE TO REPRESENT DYNAMICS OF LEGAL PROVISIONS */ /*---------------------------------------------------------------------------*/ /*This is a prototype program implemented in SWI-Prolog/XPCE according to the description given in the paper of J. Martinek and J. Cybulka: Dynamics of Legal Provisions and its Representation, ICAIL'05, Bologna, p.20-24.*/ /*---------------------------------------------------------------------------*/ /* INTRODUCTORY COMMENTS */ /*---------------------------------------------------------------------------*/ /* FUNCTIONAL PROPERTIES OF THE KNOWLEDGE BASE The program creates a dialog environment, which helps to perform the following tasks: -- DISPLAYING OF THE FORTHCOMING EVENTS – the current list of forthcoming (future) events is shown, -- ADDING OF THE FUTURE EVENT – to the list of forthcoming events a new event, with the specified identifier and the time of its occurring, is added; then a new list of forthcoming events is displayed (chronologically ordered), -- EVENT OCCURRENCE FORCING – from the list of forthcoming events the chronologically first event is selected and it is removed from the list; information about the event occurrence is introduced to the data base; all actions conditioned by the event occurrence are executed; information about the event occurrence is displayed, -- DISPLAYING OF THE PROVISIONS FOR THE GIVEN DATE – an auxiliary window is opened in which the browsing of basic prescription short names is possible; only names actual for the given date are displayed; the user may select one of the displayed names and then the actual text of the selected prescription is displayed; also the specifying provisions are shown. PROVISION IDENTIFIERS The SUBSTANTIAL PROVISION identified by a term of the form: p(StatuteNr,ArticleNr,SectionNr,ProvisionNr) in which 'StatuteNr' is always the main statute number, regardless of the provision origin which may be in the main or in the amending statute. 'ArticleNr' represents the number of the article which is the provision source, 'SectionNr' constitutes the section number or zero if the article is not divided into sections, and 'ProvisionNr' is the number of the provision (the scope of provision numbers proceeds within an article or a section). The META-PROVISION is identified by a term of the form: p(StatuteNr,ArticleNr,SectionNr,ProvisionNr) in which 'StatuteNr' is the number of the statute containing the provision, and the other arguments have the same meaning as above. TEXT IDENTIFIERS Provisions have identifiers, to which the information about provisions textual contents ('Text') is assigned by means of the term (a non-constant fact): text(ProvisionId,TextId), and additionally by means of the clause: full_text(TextId,Text). The text identifier 'TextId' is a term of the form: t(StatuteNr1,StatuteNr2,ArticleNr,SectionNr,ProvisionNr), where 'StatuteNr1' points to the statute in which the text appears, while the remaining arguments identify the provision with which the text is actually concerned. For example, a text which amends some provision of the main statute will obtain the identifier which points at first to the number of the amending statute and the remaining arguments will identify the amended provision. Through this method of text identification it is possible to sort text identifiers to obtain a sequence of them which corresponds to the proper order of provisions in the main statute after amendments. ACTION EXECUTION To execute all the actions which are conditioned by the occurrence of some event it is necessary to find all the meta-norms which are conditioned by that event and to execute all the actions defined in these meta-norms. It concerns both the instantaneous and the durative actions. This task is realised by the procedure 'execute_actions(Event)'. When a durative action concerns the retroactive provision enactment then the limitary event starting the period of retroactivity is forced. In the case of other durative actions the limitary event is added to list of forthcoming events. In both cases the 'starting' actions provided for the execution of the durative action are done in a way similar to the execution of an instantaneous action. The execution of an instantaneous action (conditioned by an occurrence of some definite event) consists in the introduction of relevant formulae 'initiates' and 'terminates' to the data base. They describe changes caused by the action execution and are attached to the conditioning event. This is realised by the procedure 'execute(Action, Event)'. PROVISION REPEALING In the model it was assumed that the repealing of a provision cancels all its structural relations. In the case of the amendment of a provision these relations do not change, so a basic provision is still basic and a specifying one still specifies the same basic provision. When the amendment introduces additional provisions, some new structural relations arise. It appeared however, that when some provision P is repealed, it is inconvenient to cancel all its structural relations, so a special fact 's_fact(P)' is initiated. Moreover the procedure 'terminates_fact(Event,Fact)' looks for that special fact in the following way: terminates_fact(Ev,F):- terminates(Ev,FL), (member(F,FL); covered(F,FL)). The procedure 'covered(Fact,FactList)' tests if 'Fact' is covered by 'FactList' containing the corresponding special fact. covered(F,[F1|_]):- corresponds_to(F,F1),!. covered(F,[_|FL]):- covered(F,FL). corresponds_to(basic(P),s_fact(P)). corresponds_to(specifies(P,_),s_fact(P)). corresponds_to(specifies(_,P),s_fact(P)). corresponds_to(interprets(P,_),s_fact(P)). corresponds_to(interprets(_,P),s_fact(P)). corresponds_to(generalises(P,_),s_fact(P)). corresponds_to(generalises(_,P),s_fact(P)). STATUTE REPEALING When an event occurs which repeals a whole statute, then the all being in force and not prolonged provisions of the statute should be repealed. This task is realised by the procedure 'repeal_provisions(StatueNr,Event)' with the following definition: repeal_provisions(StatuteNr,Ev):- happens(Ev,T), T1 is T-1, generate_provision(T1,p(StatuteNr,A,U,P)), execute(provision_repealing(p(StatuteNr,A,U,P)),Ev), fail. repeal_provisions(_,_). Provisions that should be repealed are generated by means of the procedure: generate_provision(T,Provision):- holds_at(text(Provision,X),T), not(X = repealed), not(holds_at(prolongated(Provision),T)). PROVISION TEXT AMENDING At some point of time only one text (the current text) may correspond to one provision. The event, which initiates the assignment of a new text to the provision, terminates the fact expressing the connection with the former text. To control this change the method proposed by Kesim and Sergot is used.(A Logic Programming Framework for Modelling Temporal Objects. IEEE Transactions on Knowledge and Data Engineering, 8,6,1996,p.724-741). So the full definition of the procedure 'terminates_fact' is as follows: terminates_fact(Ev,text(X,_)):- initiates_fact(Ev,text(X,_)),!. terminates_fact(Ev,F):- terminates(Ev,FL), (member(F,FL); covered(F,FL)). HOW TO START THE PROGRAM The program is initiated by means of the procedure 'start'. In same cases it may be convenient to start the program with the state of the data base which was obtained as a result of some sequence of events. In such a case, it is possible to declare 'user_output', as the output stream and to use data base commands (concerning introduction of forthcoming events or forcing of events) in the file with the specification of provisions (concerning meta-norms, texts etc.). The declaration is of the form: ':-to_terminal'. Please remember that the following meta-norms should be added to every specification of prescriptions: mn(nil,statute_temporal_force_ends(StatuteNr), statute_repealing(StatuteNr)). mn(nil,provision_prolongation_ends(ProvisionId), prolonged_provision_repealing(ProvisionId)). mn(nil,provision_suspension_ends(ProvisionId,Text,NewStructFacts), suspended_provision_enactment(ProvisionId,Text,NewStructFacts)). mn(nil,retroactivity_period_starts(ProvisionId), retroactivity_recording(ProvisionId)). mn(nil,retroactivity_period_starts(ProvisionId), provision_enactment(ProvisionId)). */ /*----------------------------------------------------------------------------*/ /* A FILE SEARCHING WINDOW */ /*----------------------------------------------------------------------------*/ :- pce_autoload(finder,library(find_file)). :- pce_global(@finder,new(finder)). /*----------------------------------------------------------------------------*/ /* DECLARATIONS */ /*----------------------------------------------------------------------------*/ %dynamic predicates which may change during the program execution :- dynamic events/1, happens/2, initiates/2, terminates/2, mn/3, mn/5, full_text/2, index/2. %clauses of the following predicates may appear in different places %of the specification :- discontiguous mn/3,mn/5,full_text/2,index/2. /*------------------------------------------------------------------------*/ /* CREATION OF THE DIALOG ENVIRONMENT */ /*------------------------------------------------------------------------*/ start:- new(@frame,frame('Provisions')), send(@frame,append,new(D1,dialog)), send(new(@view1,view),below,D1), send(new(@view2,view),below,@view1), send(@view1,font,font(win,system)), send(@view1,wrap,word), send(@view2,font,font(win,system)), send(@view2,wrap,word), send(D1,append,new(MB,menu_bar)), send(MB,append,new(File,popup(file))), send(MB,append,new(Operation,popup(operation))), send_list(File,append, [menu_item(open,message(@prolog,open_file)), menu_item(save,message(@prolog,save_file)), menu_item('clear 1',message(@prolog,clear_view1)), menu_item('clear 2',message(@prolog,clear_view2)), menu_item(quit,message(@prolog,quit))]), send_list(Operation,append, [menu_item('show events', message(@prolog,events_writing)), menu_item('add to events', message(@prolog,add_to_events)), menu_item('event forcing', message(@prolog,event_forcing)), menu_item('show index', message(@prolog,show_index))]), open_stream, send(@frame,open), send(@frame,status,full_screen). open_file:- get(@finder,file,@on,txt,File_to_open), send(@view1,load,File_to_open), reass(opened_file(File_to_open)). save_file:- get(@finder,file,@off,txt,File_to_save), send(@view1,save,File_to_save), reass(opened_file(File_to_save)). clear_view1:- send(@view1,clear). clear_view2:- send(@view2,clear). quit:- close_stream, send(@frame,destroy). open_stream:- send(@view2,clear), pce_open(@view2,append,H), reass(output_stream(H)). close_stream:- output_stream(H), close(H). reass(opened_file(X)):- retract(opened_file(_)),!, assert(opened_file(X)). reass(opened_file(X)):- assert(opened_file(X)). reass(output_stream(X)):- retract(output_stream(_)),!, assert(output_stream(X)). reass(output_stream(X)):- assert(output_stream(X)). /*------------------------------------------------------------------------*/ /* OPERATIONS ON THE FUTURE EVENTS LIST */ /*------------------------------------------------------------------------*/ %future events list (empty at the beginning) events([]). %forcing of the first event event_forcing:- output_stream(H), event_forcing(H). event_forcing(H):- events([]), write(H,'the list of events is empty'), nl(H). event_forcing(H):- retract(events(EvL1)),!, EvL1=[[Ev,EvT]|EvL], assert(events(EvL)), assert(happens(Ev,EvT)), write(H,happens(Ev,EvT)), nl(H), write(H,events(EvL)), nl(H), execute_actions(Ev). %the event is added to the future events list; in an auxiliary dialog %window the event identifier and the event time should be declared add_to_events:- new(D,dialog('define arguments')), send_list(D,append,[new(A1,text_item('event id')), new(A2,int_item(time)), button(cancel,message(D,destroy)), button(enter,and(message(@prolog, adding_continuation,A1?selection,A2?selection), message(D,destroy)))]), send(D,default_button,enter), send(D,open). adding_continuation(Ev,EvT):- atom_to_term(Ev,Ev1,_), adding_to_events(Ev1,EvT). %the event Ev and its time EvT is directly added to the future events list adding_to_events(Ev,EvT):- retract(events(EvL)),!, events_sorting([[Ev,EvT]|EvL],EvL1), assert(events(EvL1)), output_stream(H), write(H,events(EvL1)), nl(H). %the events list is sorted events_sorting(L,S):- append(X,[[A1,B1],[A2,B2]|Y],L), order(B2,B1),!, append(X,[[A2,B2],[A1,B1]|Y],M), events_sorting(M,S). events_sorting(L,L). order(B2,B1):- B2 < B1. %future events writing events_writing:- output_stream(H), events_writing(H). events_writing(H):- events(EvL), write(H,events(EvL)), nl(H). /*-------------------------------------------------------------------------*/ /* DISPLAYING OF PROVISIONS FOR THE GIVEN TIME */ /*-------------------------------------------------------------------------*/ %In an auxiliary dialog window the considered time should be defined; %in a separate window a list of basic provisions short names will be %shown and the user should select one of them; %the content of the selected provision together with its specifying %provisions will appear in an editing window. show_index:- new(D,dialog('define argument')), send_list(D,append, [new(A,int_item(time)), button(cancel,message(D,destroy)), button(enter,and( message(@prolog,ps_continuation,A?selection), message(D,destroy)))]), send(D,default_button,enter), send(D,open). ps_continuation(T):- date(T),!, new(F,frame(index)), send(F,append,new(B,browser)), send(new(D,dialog),below(B)), send_list(D,append, [button(select,message(@prolog,ps_continuation1,B?selection?label, T)), button(quit,message(F,destroy))]), send(D,default_button,select), findall(Name,searching(Name,T),L), send(B,font,font(win,system)), send(B,size,size(60,15)), send(F,open), ps_continuation2(L,B). ps_continuation(_). ps_continuation2([],B):-!, send(B,members,['no choice']). ps_continuation2(L,B):- reverse(L,L1), send(B,members,L1). date(T):- 19000101 < T, T < 21000101. searching(Name,T):- holds_at(basic(P),T), index(P,Name). ps_continuation1(Name,T):- index(P,Name), holds_at(text(P,TextId),T), full_text(TextId,Text),!, pce_open(@view1,write,H), tab(H,15), write(H,'Basic provision'), nl(H),nl(H), li_writing(H,Text), nl(H),nl(H), ps_continuation3(H,P,T), close(H). ps_continuation1(_). ps_continuation3(H,P,T):- findall(TextId,spec_searching(P,T,TextId),L), reverse(L,L1), sorting(L1,L2), ps_continuation4(H,L2). ps_continuation4(_,[]):-!. ps_continuation4(H,L):- tab(H,15), write(H,'Specifying provisions'), nl(H),nl(H), spec_writing(H,L). spec_searching(P,T,TextId):- holds_at(specifies(P1,P),T), holds_at(text(P1,TextId),T). spec_writing(_,[]):-!. spec_writing(H,[T|R]):- full_text(T,Text), li_writing(H,Text), nl(H),nl(H), spec_writing(H,R). /*------------------------------------------------------------------------*/ /* AUXILIARY ACTIONS */ /*------------------------------------------------------------------------*/ initiates1(Zd,FL):- retract(initiates(Zd,FL)),!. initiates1(_,[]). terminates1(Zd,FL):- retract(terminates(Zd,FL)),!. terminates1(_,[]). events1(EvL):- retract(events(EvL)),!. events1([]). %time_change(EvL,LimEv,NewTime,NewEvL) - %an auxiliary procedure changing the limitary event time time_change([],Ev,T,[[Ev,T]]):-!. time_change([[Ev,_]|EvL],Ev,T,[[Ev,T]|EvL]):-!. time_change([X|EvL],Ev,T,[X|EvL1]):- time_change(EvL,Ev,T,EvL1). sorting([],[]):-!. sorting([X|L],M):- sorting(L,N), insert(X,N,M). insert(X,[A|L],[A|M]):- order1(A,X),!, insert(X,L,M). insert(X,L,[X|L]). order1(t(_,A1,_,_,_),t(_,A2,_,_,_)):- ord(A1,A2),!. order1(t(_,A1,B1,_,_),t(_,A2,B2,_,_)):- A1 = A2, ord(B1,B2),!. order1(t(_,A1,B1,C1,_),t(_,A2,B2,C2,_)):- A1 = A2, B1 = B2, ord(C1,C2),!. order1(t(_,A1,B1,C1,D1),t(_,A2,B2,C2,D2)):- A1 = A2, B1 = B2, C1 = C2, ord(D1,D2). ord(A,B):- integer(A), integer(B),!, A < B. ord(A,B):- integer(A),!, int_to_atom(A,A1), A1 @< B. ord(A,B):- integer(B),!, int_to_atom(B,B1), A @< B1. ord(A,B):- A @< B. li_writing(_,[]):-!. li_writing(H,[A|R]):- put(H,A), li_writing(H,R). to_terminal:- reass(output_stream(user_output)). /*------------------------------------------------------------------------*/ /* STATUTE PROVISIONS REPEALING */ /*------------------------------------------------------------------------*/ %statute provisions are repealed by the event Ev; %the statute number is StatuteNr repeal_provisions(StatuteNr,Ev):- happens(Ev,T),T1 is T-1, generate_provision(T1,p(StatuteNr,A,U,P)), execute(provision_repealing(p(StatuteNr,A,U,P)),Ev), fail. repeal_provisions(_,_). %provisions to repeal are generated generate_provision(T,Provision):- holds_at(text(Provision,X),T), not(X = repealed), not(holds_at(prolongated(Provision),T)). /*------------------------------------------------------------------------*/ /* EXECUTION OF ALL ACTIONS CONDITIONED BY THE EVENT */ /*------------------------------------------------------------------------*/ %execution of all actions conditioned by the event Ev execute_actions(Ev):- mn(_,Ev,Action), %execution of the instantaneous action execute(Action,Ev), fail. execute_actions(Ev):- mn(_,Ev,Action,LimEv,LimEvTime), %execution of the durative action execute(Action,Ev,LimEv,LimEvTime), fail. execute_actions(_). %execution of one instantaneous action conditioned by the event Ev execute(provision_enactment(P,Text,NewStructFacts), Ev):-!, initiates1(Ev,FL), append(NewStructFacts,[text(P,Text)|FL],FL1), assert(initiates(Ev,FL1)). execute(suspended_provision_enactment(P,Text,NewStructFacts), Ev):-!, terminates1(Ev,FL), append([suspended(P)],FL,FL1), assert(terminates(Ev,FL1)), execute(provision_enactment(P,Text,NewStructFacts),Ev). execute(provision_change(P,Text),Ev):-!, initiates1(Ev,FL), append([text(P,Text)],FL,FL1), assert(initiates(Ev,FL1)). execute(statute_repealing(StatuteNr),Ev):-!, repeal_prvisions(StatuteNr,Ev). execute(provision_repealing(P),Ev):-!, initiates1(Ev,FL), append([text(P,repealed)],FL,FL1), assert(initiates(Ev,FL1)), terminates1(Ev,FL2), append([s_fact(P)],FL2,FL3), assert(terminates(Ev,FL3)). execute(prolonged_provision_repealing(P),Ev):-!, terminates1(Ev,FL), append([prolonged(P)],FL,FL1), assert(terminates(Ev,FL1)), execute(provision_repealing(P),Ev). execute(limitary_event_time_change(LimEv,NewT),_):-!, events1(EvL), time_change(EvL,LimEv,NewT,EvL1), events_sorting(EvL1,EvL2), assert(events(EvL2)). execute(retroactivity_recording(P),Ev):-!, initiates1(Ev,FL), append([retroactive(P)],FL,FL1), assert(initiates(Ev,FL1)). execute(temporary_holding_in_time(_),_):-!. execute(provision_prolongation(P),Ev):-!, initiates1(Ev,FL), append([prolonged(P)],FL,FL1), assert(initiates(Ev,FL1)). execute(provision_suspension(P,_,_),Ev):-!, initiates1(Ev,FL), append([suspended(P)],FL,FL1), assert(initiates(Ev,FL1)). execute(retroactive_provision_enactment(P),Ev):-!, terminates1(Ev,FL), append([retroactive(P)],FL,FL1), assert(terminates(Ev,FL1)). execute(vacatio_legis(_),_):-!. %execution of one durative action conditioned by the event Ev; %the limitary event LimEv (which happens at LimEvTime) is attached %to the action execute(retroactive_provision_enactment(P),Ev,LimEv,LimEvTime):-!, retro_event_forcing(LimEv,LimEvTime), execute(retroactive_provision_enactment(P),Ev). execute(Action,Ev,LimEv,LimEvTime):- adding_to_events(LimEv,LimEvTime), execute(Action,Ev). retro_event_forcing(LimEv,LimEvTime):- assert(happens(LimEv,LimEvTime)), output_stream(H), write(H,happens(LimEv,LimEvTime)), nl(H), execute_actions(LimEv). /*---------------------------------------------------------------------------*/ /* CHECKING IF THE FACT HOLDS AT THE GIVEN TIME */ /*---------------------------------------------------------------------------*/ %The fact F holds at the time T holds_at(F,T):- happens(Ev,TI), TI =< T, initiates_fact(Ev,F), not(broken(F,TI,T)). %the event Ev initiates the fact F initiates_fact(Ev,F):- initiates(Ev,FL), member(F,FL). %the fact F was broken between TI and T broken(F,TI,T):- happens(Ev,EvT), TI < EvT, EvT =< T, terminates_fact(Ev,F). %the event Ev terminates the fact F terminates_fact(Ev,text(X,_)):- initiates_fact(Ev,text(X,_)),!. terminates_fact(Ev,F):- terminates(Ev,FL), (member(F,FL); covered(F,FL)). %covered(F,FL) – the structural fact F is covered by the fact list FL covered(F,[F1|_]):- corresponds_to(F,F1),!. covered(F,[_|FL]):- covered(F,FL). %the structural fact for the provision P corresponds to the special %fact for this provision corresponds_to(basic(P),s_fact(P)). corresponds_to(specifies(P,_),s_fact(P)). corresponds_to(specifies(_,P),s_fact(P)). corresponds_to(interprets(P,_),s_fact(P)). corresponds_to(interprets(_,P),s_fact(P)). corresponds_to(generalises(P,_),s_fact(P)). corresponds_to(generalises(_,P),s_fact(P)). /*------------------------------------------------------------------------*/ /* CALENDRICAL CALCULATIONS */ /*------------------------------------------------------------------------*/ /* The procedure distant_date(T1,D,T) calculates the date T which falls D days after the date T1. The arguments T1 and D must be given. The procedure is based on the algorithm of Dershowitz and Reingold (Software-Practice and Experience, 1990, p. 899-928) which transforms the date to the absolute date. Dates (non-absolute) are represented by integers in the following way: Date = Year*10000 + Month*100 + Day. */ distant_date(T1,D,T):- absolute_date(T1,AD1), AD is AD1+D, date_from_absolute(AD,T). absolute_date(Date,AbsoluteDate):- extract_ymd(Date,Year,Month,Day), absolute_date(Year,Month,Day,AbsoluteDate). extract_ymd(Date,Year,Month,Day):- Year is round(Date/10000), X is Date-Year*10000, Month is round(X/100), Day is X-Month*100. absolute_date(Year,Month,Day,AbsoluteDate):- Month1 is Month-1, prior_months_days(Year,Month1,PMDays), prior_years_days(Year,PYDays), prior_years_leap_days(Year,LDays), AbsoluteDate is LDays+PYDays+PMDays+Day. prior_months_days(_,0,0):-!. prior_months_days(Year,Month,Days):- month_last_day(Year,Month,Last), X is Month-1, prior_months_days(Year,X,Y), Days is Last+Y. month_last_day(Year,Month,Last):- (Month = 2, X is Year mod 4, X = 0, Y is Year mod 400, not(member(Y,[100,200,300])), Last = 29,!); nth1(Month,[31,28,31,30,31,30,31,31,30,31,30,31],Last). prior_years_days(Year,Days):- Years is Year-1, Days is Years*365. prior_years_leap_days(Year,Days):- Years is Year-1, Q1 is round(Years/4), Q2 is round(Years/100), Q3 is round(Years/400), Days is Q1-Q2+Q3. date_from_absolute(AbsoluteDate,Date):- year(AbsoluteDate,Year), month(AbsoluteDate,Year,Month), day(AbsoluteDate,Year,Month,Day), date_code(Year,Month,Day,Date). year(AbsoluteDate,Year):- Approx is round(AbsoluteDate/366), year_search(Approx,AbsoluteDate,Year). year_search(Approx,AbsoluteDate,Year):- X is Approx+1, absolute_date(X,1,1,Date), (Date>AbsoluteDate, Year=Approx,!; year_search(X,AbsoluteDate,Year)). month(AbsoluteDate,Year,Month):- month_search(1,AbsoluteDate,Year,Month). month_search(Approx,AbsoluteDate,Year,Month):- month_last_day(Year,Approx,Last), absolute_date(Year,Approx,Last,Date), (Date>=AbsoluteDate,Month=Approx,!; X is Approx+1, month_search(X,AbsoluteDate,Year,Month)). day(AbsoluteDate,Year,Month,Day):- absolute_date(Year,Month,1,Date), X is Date-1, Day is AbsoluteDate-X. date_code(Year,Month,Day,Date):- D1 is Year*10000, D2 is Month*100, Date is D1+D2+Day.