1 % (c) 2009-2019 Lehrstuhl fuer Softwaretechnik und Programmiersprachen,
2 % Heinrich Heine Universitaet Duesseldorf
3 % This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html)
4
5
6 :- module(predicate_analysis,[predicate_analysis/5
7 ,predicate_analysis_with_global_sets/5
8 ,info_conjunct/3
9 ,info_disjunct/3
10 ,test_predicate_analysis/0
11 ,max_type_cardinality/2
12 ]).
13
14 :- use_module(library(lists)).
15 :- use_module(library(avl)).
16 :- use_module(library(ordsets)).
17 :- use_module(library(terms)).
18
19 :- use_module(bsyntaxtree).
20 :- use_module(btypechecker).
21 :- use_module(preferences).
22 :- use_module(tools).
23 :- use_module(translate).
24 :- use_module(bmachine).
25 :- use_module(b_global_sets, [is_b_global_constant/3,b_global_set_cardinality/2]).
26
27 :- use_module(error_manager).
28 :- use_module(self_check).
29 :- use_module(debug,[debug_println/2]).
30
31 :- use_module(inf_arith).
32 :- use_module(interval_calc).
33
34 % for interpreting value(...) components
35 :- use_module(store, [empty_state/1]).
36 :- use_module(b_interpreter, [b_compute_expression_nowf/4]).
37
38 :- use_module(module_information,[module_info/2]).
39 :- module_info(group,analysis).
40 :- module_info(description,'This is a module and plugin to extract information about expressions (e.g. integer bounds (aka intervals), cardinality) by static analysis. Used by Kodkod.').
41
42 :- dynamic debugging_enabled/0.
43 :- load_files(library(system), [when(compile_time), imports([environ/2])]).
44 % avl_apply
45 % similar to avl_fetch_pair: but checks whether we have a function
46 % and whether the function is defined for the key
47 :- if(environ(debug_kodkod,true)).
48 debugging_enabled. % commment in to have dot files written to debug-constraint-anim folder (interval analysis for kodkod)
49 :- endif.
50
51 % the following is called by kodkod:
52 predicate_analysis_with_global_sets(Tree,ROIdentifiers,Identifiers,NewIdentifiers,NewTree) :-
53 findall(X,
54 (bmachine_is_precompiled,
55 b_get_machine_set(Set),
56 b_global_set_cardinality(Set,Card),
57 create_texpr(identifier(Set),set(global(Set)),[analysis([card:irange(Card,Card)])],X)),
58 Sets),
59 findall(X,
60 (bmachine_is_precompiled,
61 is_b_global_constant(Set,_Index,Elem),
62 create_texpr(identifier(Elem),global(Set),[],X)),
63 Elements),
64 append([ROIdentifiers,Sets,Elements],ROIds2),
65 predicate_analysis(Tree,ROIds2,Identifiers,NewIdentifiers,NewTree).
66
67
68 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
69 % static analysis of integers
70
71 :- volatile nodecounter/1, debug_node/2, debug_constraints/1.
72 :- dynamic nodecounter/1, debug_node/2, debug_constraints/1.
73
74 predicate_analysis(Tree,ROIdentifiers,Identifiers,NewIdentifiers,NewTree) :-
75 reset_nodecounter,
76 debug_println(15,starting_predicate_analysis),
77 start_ms_timer(Timer1),
78 create_node_environment(ROIdentifiers,Identifiers,NewIdentifiers,NodeEnv,Infos1),
79 (debug:debug_mode(on) -> stop_ms_timer_with_msg(Timer1,create_node_environment) ; true),
80 start_ms_timer(Timer2),
81 extract_nodes(Tree,NodeEnv,_Node,_Mode,NewTree,(Constraints,Infos),Infos1),
82 (debug:debug_mode(on) -> stop_ms_timer_with_msg(Timer2,extract_nodes) ; true),
83 start_ms_timer(Timer3),
84 create_constraint_tree(Constraints,Nodes,CTree),
85 (debug:debug_mode(on) -> stop_ms_timer_with_msg(Timer3,create_constraint_tree) ; true),
86 retractall(debug_constraints(_)),assert(debug_constraints(Constraints)),
87 create_initial_information(Nodes,Empty),
88 apply_start_constraints(Constraints,ChangedNodes,Empty,Initial),
89 compute_maximum_iterations(Nodes,MaxIter),
90 debug_println(15,predicate_analysis:do_analysis(MaxIter)),
91 start_ms_timer(Timer4),
92 do_analysis(ChangedNodes,2,MaxIter,CTree,Initial,Values),
93 (debug:debug_mode(on) -> stop_ms_timer_with_msg(Timer4,do_analysis) ; true),
94 insert_info(Infos,Values).
95
96 create_node_environment(ROIdentifiers,Identifiers,NewIdentifiers,NodeEnv,Infos) :-
97 initial_node_env(InitNodeEnv),
98 create_identifier_nodes2(ROIdentifiers,_,InitNodeEnv,RONodeEnv1,Infos1,([],[])),
99 identifier_nodes_readonly(RONodeEnv1,RONodeEnv),
100 create_identifier_nodes2(Identifiers,NewIdentifiers,RONodeEnv,NodeEnv,Infos2,Infos1),
101 foldl(add_id_basic_constraint(NodeEnv),Identifiers,Infos,Infos2).
102 add_id_basic_constraint(NodeEnv,Identifier,In,Out) :-
103 get_texpr_id(Identifier,Id),
104 identifier_node_lookup(Id,NodeEnv,_NodeId,Nodes,_Mode),
105 add_type_constraints(Identifier,Nodes,In,Out).
106
107 % just a heuristic to avoid a non-terminating run
108 compute_maximum_iterations(Nodes,MaxIter) :-
109 length(Nodes,N), MaxIter is N*10.
110
111 reset_nodecounter :-
112 retractall(nodecounter(_)),assert(nodecounter(1)),
113 retractall(debug_node(_,_)),
114 retractall(debug_constraints(_)).
115
116 % create a numbered node for each identifier
117 initial_node_env(NodeEnv) :- empty_avl(NodeEnv).
118
119 %create_identifier_nodes(Ids,NIds,Env,IIn,IOut) :-
120 % empty_avl(Start), create_identifier_nodes2(Ids,NIds,Start,Env,IIn,IOut).
121
122 create_identifier_nodes2([],[],Env,Env,Info,Info).
123 create_identifier_nodes2([TId|Rest],[NTId|NRest],In,Out,IIn,IOut) :-
124 create_identifier_nodes3(TId,NTId,In,Inter,IIn,IInter),
125 create_identifier_nodes2(Rest,NRest,Inter,Out,IInter,IOut).
126 create_identifier_nodes3(TId,NTId,In,Out,IIn,IOut) :-
127 get_texpr_id(TId,Id),
128 get_texpr_type(TId,Type),
129 new_node_scope(Type,NodeId,Nodes),
130 avl_store(Id,In,nodes(NodeId,Nodes,rw),Out),
131 texpr_add_node_infos(TId,NodeId,NTId,IIn,IOut).
132
133 % look up the node of an identifier
134 identifier_node_lookup(Id,Env,NodeId,Nodes,Mode) :-
135 (avl_fetch(Id,Env,nodes(NodeId,Nodes,Mode)) -> true
136 ; add_warning(kodkod,'Could not lookup identifier: ',Id),fail).
137
138 % set the complete environment to read-only,
139 % read-only nodes are not used for output of constraint nodes
140 identifier_nodes_readonly(OldEnv,NewEnv) :-
141 avl_map(set_readonly,OldEnv,NewEnv).
142 set_readonly(nodes(Id,Nodes,_Mode),nodes(Id,Nodes,ro)).
143
144 compute_integer_set('INTEGER',-inf,inf).
145 compute_integer_set('NATURAL',0,inf).
146 compute_integer_set('NATURAL1',1,inf).
147 compute_integer_set('INT',Min,Max) :-
148 get_preference(minint,Min),
149 get_preference(maxint,Max).
150 compute_integer_set('NAT',0,Max) :-
151 get_preference(maxint,Max).
152 compute_integer_set('NAT1',1,Max) :-
153 get_preference(maxint,Max).
154
155 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
156 % computation on information
157
158 info_conjunct(irange(X,Y),irange(A,B),Result) :-
159 Max infis max(X,A), Min infis min(Y,B),
160 normalise_irange(irange(Max,Min),Result).
161 info_disjunct(irange(X,Y),irange(A,B),Result) :-
162 ( is_bottom(irange(X,Y)) -> Result = irange(A,B)
163 ; is_bottom(irange(A,B)) -> Result = irange(X,Y)
164 ; otherwise ->
165 Min infis min(X,A), Max infis max(Y,B),
166 normalise_irange(irange(Min,Max),Result)).
167
168 normalise_irange(In,Result) :-
169 ( is_bottom(In) -> Result=irange(0,-1)
170 ; otherwise -> Result=In).
171 is_bottom(irange(A,B)) :- infless(B,A).
172
173
174 has_more_information(irange(X,_Y),irange(A,_B)) :- infless(A,X),!.
175 has_more_information(irange(_X,Y),irange(_A,B)) :- infless(Y,B),!.
176 has_more_information(set(A),elem(B)) :- has_more_information(A,B).
177 has_more_information(left(A),left(B)) :- has_more_information(A,B).
178 has_more_information(right(A),right(B)) :- has_more_information(A,B).
179
180 % what kind of information may be stored in a node of a certain data type
181 get_type_scopes(Type,Scopes) :-
182 findall(S,get_type_scope(Type,S),Scopes1),
183 sort([possible_values|Scopes1],Scopes).
184 get_type_scope(integer,interval).
185 get_type_scope(set(T),elem(S)) :-
186 get_type_scope(T,S).
187 get_type_scope(set(_),card).
188 get_type_scope(seq(S),T) :-
189 get_type_scope(set(couple(integer,S)),T).
190 get_type_scope(couple(A,_),left(S)) :-
191 get_type_scope(A,S).
192 get_type_scope(couple(_,B),right(S)) :-
193 get_type_scope(B,S).
194
195 % get the top element of a scope
196 get_scope_top(interval,irange(-inf,inf)).
197 get_scope_top(possible_values,irange(0,inf)).
198 get_scope_top(elem(S),T) :- get_scope_top(S,T).
199 get_scope_top(left(S),T) :- get_scope_top(S,T).
200 get_scope_top(right(S),T) :- get_scope_top(S,T).
201 get_scope_top(card,irange(0,inf)).
202
203 get_scope_bottom(interval,irange(0,-1)).
204 get_scope_bottom(possible_values,T) :- get_scope_bottom(interval,T).
205 get_scope_bottom(elem(S),T) :- get_scope_bottom(S,T).
206 get_scope_bottom(left(S),T) :- get_scope_bottom(S,T).
207 get_scope_bottom(right(S),T) :- get_scope_bottom(S,T).
208 get_scope_bottom(card,T) :- get_scope_bottom(interval,T).
209
210 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
211 % operations on possible infinite numbers
212 %irange_union(irange(Am,AM),irange(Bm,BM),irange(Cm,CM)) :-
213 % Cm infis min(Am,Bm), CM infis max(AM,BM).
214
215 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
216 % new extract
217
218 new_node_scope(Type,Id,Nodes) :-
219 new_node2(Id),get_type_scopes(Type,Scopes),
220 findall(S:Id,member(S,Scopes),Nodes).
221
222 new_node2(Id):-
223 retract(nodecounter(Id)),
224 Id2 is Id+1, assert(nodecounter(Id2)).
225
226 extract_nodes(TExpr,NodeEnv,Nodes,Mode,NTExpr,In,Out) :-
227 get_texpr_expr(TExpr,Expr),
228 extract_nodes2(Expr,TExpr,NodeEnv,Nodes,Mode,NTExpr,In,Inter),
229 add_type_constraints(TExpr,Nodes,Inter,Out),
230 store_debug_info(Nodes,TExpr).
231 extract_nodes2(identifier(Id),TExpr,NodeEnv,Nodes,Mode,NTExpr,In,Out) :- !,
232 identifier_node_lookup(Id,NodeEnv,NodeId,Nodes,Mode),!,
233 texpr_add_node_infos(TExpr,NodeId,NTExpr,In,Out).
234 extract_nodes2(conjunct(A,B),TExpr,NodeEnv,[],rw,NTExpr,In,Out) :- !,
235 get_texpr_info(TExpr,Infos),
236 create_texpr(conjunct(NA,NB),pred,Infos,NTExpr),
237 extract_nodes(A,NodeEnv,_NodesA,_ModeA,NA,In,Inter),
238 extract_nodes(B,NodeEnv,_NodesB,_ModeB,NB,Inter,Out).
239 extract_nodes2(forall(Ids,Pre,Cond),TExpr,NodeEnv,[],rw,NTExpr,In,Out) :- !,
240 get_texpr_info(TExpr,Info),
241 create_texpr(forall(NIds,NPre,NCond),pred,Info,NTExpr),
242 identifier_nodes_readonly(NodeEnv,SubEnv1),
243 create_identifier_nodes2(Ids,NIds,SubEnv1,SubEnv,In,Inter1),
244 extract_nodes(Pre,SubEnv,_NodesPre,_ModePre,NPre,Inter1,Inter2),
245 identifier_nodes_readonly(SubEnv,ROEnv),
246 extract_nodes(Cond,ROEnv,_NodesCond,_ModeCond,NCond,Inter2,Out).
247 extract_nodes2(exists(Ids,Cond),TExpr,NodeEnv,[],rw,NTExpr,In,Out) :- !,
248 get_texpr_info(TExpr,Info),
249 create_texpr(exists(NIds,NCond),pred,Info,NTExpr),
250 create_identifier_nodes2(Ids,NIds,NodeEnv,SubEnv,In,Inter1),
251 extract_nodes(Cond,SubEnv,_NodesCond,_ModeCond,NCond,Inter1,Out).
252 extract_nodes2(general_sum([TId],TPred,TExpr),TSigma,NodeEnv,Nodes,rw,NTSigma,In,Out) :- !,
253 nodes_for_general_sum_or_mult(mult,TId,TPred,TExpr,TSigma,NodeEnv,Nodes,NTSigma,In,Out).
254 extract_nodes2(general_product([TId],TPred,TExpr),TSigma,NodeEnv,Nodes,rw,NTSigma,In,Out) :- !,
255 nodes_for_general_sum_or_mult(power_rule,TId,TPred,TExpr,TSigma,NodeEnv,Nodes,NTSigma,In,Out).
256 extract_nodes2(comprehension_set(Ids,Cond),TExpr,NodeEnv,Nodes,rw,NTExpr,In,Out) :- !,
257 get_texpr_info(TExpr,Info),
258 get_texpr_type(TExpr,Type),
259 identifier_nodes_readonly(NodeEnv,SubEnv1),
260 create_identifier_nodes2(Ids,NIds,SubEnv1,SubEnv,In,Inter1),
261 extract_nodes(Cond,SubEnv,_NodesCond,_ModeCond,NCond,Inter1,Inter2),
262 new_node_scope(Type,NodeId,Nodes),
263 compset_constraints(Nodes,Ids,SubEnv,Constraints),
264 add_all(Constraints,Inter2,Inter3),
265 add_node_infos(Info,NodeId,NInfo,Inter3,Out),
266 create_texpr(comprehension_set(NIds,NCond),Type,NInfo,NTExpr).
267 extract_nodes2(set_extension(L),TExpr,NodeEnv,LocalNodes,rw,NTExpr,In,Out) :- !,
268 get_texpr_info(TExpr,Info),
269 get_texpr_type(TExpr,Type),
270 new_node_scope(Type,NodeId,LocalNodes),
271 extract_set_extension(L,NodeEnv,LocalNodes,NTSubs,In,Inter1),
272 CardNode = card:_, memberchk(CardNode,LocalNodes),
273 length(L,MaxCard),
274 add_all([constraint([],[CardNode],constant(irange(1,MaxCard)))],
275 Inter1,Inter2),
276 add_node_infos(Info,NodeId,NewInfo,Inter2,Out),
277 create_texpr(set_extension(NTSubs),Type,NewInfo,NTExpr).
278 extract_nodes2(value(Value),TExpr,NodeEnv,LocalNodes,RW,NTExpr,In,Out) :-
279 get_texpr_info(TExpr,Infos), member(was_identifier(ID),Infos),
280 \+ ground(Value),!,
281 %print(treating_value_as_id(Value,ID)),nl,
282 extract_nodes2(identifier(ID),TExpr,NodeEnv,LocalNodes,RW,NTExpr,In,Out).
283 extract_nodes2(value(Value),TExpr,_NodeEnv,LocalNodes,rw,NTExpr,In,Out) :- nonvar(Value),
284 !,
285 get_texpr_info(TExpr,Info),
286 get_texpr_type(TExpr,Type),
287 new_node_scope(Type,NodeId,LocalNodes),
288 findall(C,extract_constraint_for_value(Type,TExpr,LocalNodes,C),Constraints),
289 add_all(Constraints,In,Inter),
290 add_node_infos(Info,NodeId,NewInfo,Inter,Out),
291 create_texpr(value(Value),Type,NewInfo,NTExpr).
292 extract_nodes2(partition(S,Es),TExpr,NodeEnv,[],rw,NTExpr,In,Out) :- !,
293 extract_nodes_l([S|Es],NodeEnv,[SNodes|EsNodeLists],_Modes,[NS|NEs],In,Inter1),
294 % subset constraints
295 findall(constraint(CI,CO,CC),
296 (member(Scope:SId,SNodes),
297 member(NodeList,EsNodeLists),member(Scope:Elem,NodeList),
298 is_subset_extract(Scope:Elem,Scope:SId,CI,CO,CC)),
299 SubsetConstraints),
300 add_all(SubsetConstraints,Inter1,Inter2),
301 % cardinality constraints
302 memberchk(card:SNodeId,SNodes),
303 findall(card:Elem2,(member(NodeList2,EsNodeLists),member(card:Elem2,NodeList2)),
304 CardNodes),
305 create_add_list_constraints(CardNodes,card:SNodeId,CardConstraints),
306 add_all(CardConstraints,Inter2,Inter3),
307 % union constraints
308 findall(constraint(ElemNodes,[elem(Scope3):SId3],union),
309 ( member(elem(Scope3):SId3,SNodes),
310 findall(elem(Scope3):Elem3,
311 (member(NodeList3,EsNodeLists),member(elem(Scope3):Elem3,NodeList3)),
312 ElemNodes)),
313 UnionConstraints),
314 add_all(UnionConstraints,Inter3,Out),
315 get_texpr_info(TExpr,Info),
316 create_texpr(partition(NS,NEs),pred,Info,NTExpr).
317 extract_nodes2(sequence_extension(L),TExpr,NodeEnv,LocalNodes,rw,NTExpr,In,Out) :- !,
318 get_texpr_info(TExpr,Info),
319 get_texpr_type(TExpr,Type),
320 new_node_scope(Type,NodeId,LocalNodes),
321 extract_seq_extension(L,NodeEnv,LocalNodes,NTSubs,In,Inter1),
322 CardNode = card:_, memberchk(CardNode,LocalNodes),
323 DomNode = elem(left(interval)):_, memberchk(DomNode,LocalNodes),
324 length(L,Card),
325 add_all([constraint([],[CardNode],constant(irange(Card,Card))),
326 constraint([],[DomNode], constant(irange(1, Card)))],
327 Inter1,Inter2),
328 add_node_infos(Info,NodeId,NewInfo,Inter2,Out),
329 create_texpr(sequence_extension(NTSubs),Type,NewInfo,NTExpr).
330 extract_nodes2(empty_set,TExpr,_NodeEnv,Nodes,rw,NTExpr,In,Out) :- !,
331 get_texpr_type(TExpr,Type), new_node_scope(Type,NodeId,Nodes),
332 texpr_add_node_infos(TExpr,NodeId,NTExpr,In,Inter),
333 get_type_scopes(Type,Scopes),
334 findall(constraint([],[elem(S):NodeId],constant(Bottom)),
335 (member(elem(S),Scopes),get_scope_bottom(S,Bottom)),
336 Bottoms),
337 add_all([constraint([],[card:NodeId],constant(irange(0,0)))|Bottoms],Inter,Out).
338 extract_nodes2(Expr,TExpr,NodeEnv,Nodes,rw,NTExpr,In,Out) :-
339 same_functor(Expr,Tmp),
340 extract(Tmp,_,_,_,_),!,
341 syntaxtransformation(Expr,Subs,[],NSubs,NExpr),
342 copy_term((NSubs,NExpr),(NSubsRO,NExprRO)),
343 copy_term((NSubs,NExpr),(NewSubs,NewExpr)),
344 extract_nodes_l(Subs,NodeEnv,SubNodeLists,Modes,NewSubs,In,Inter),
345 ignore_readonly_nodes(Modes,SubNodeLists,SubNodeListsRO),
346 get_texpr_info(TExpr,Info),
347 get_texpr_type(TExpr,Type),
348 new_node_scope(Type,NodeId,Nodes),
349 add_node_infos(Info,NodeId,NewInfos,Inter,Inter2),
350 create_texpr(NewExpr,Type,NewInfos,NTExpr),
351 findall(constraint(InputNodes,OutputNodes,Transition),
352 ( extract(NExpr,NodeId,InputNodes,OrigOutputNodes,Transition),
353 copy_term(extract(NExpr,NodeId,InputNodes,OrigOutputNodes,Transition),
354 extract(NExprRO,NodeId,_,OutputNodes,Transition)),
355 choose_one(SubNodeLists,SubNodeListsRO,NSubs,NSubsRO)),
356 Constraints),
357 %error_manager:print_error_span(TExpr),nl,print(Constraints),nl,
358 add_all(Constraints,Inter2,Out).
359 extract_nodes2(Expr,TExpr,NodeEnv,[],rw,NTExpr,In,Out) :-
360 remove_bt(TExpr,_,NExpr,NTExpr),
361 syntaxtransformation(Expr,Subs,Names,NSubs,NExpr),
362 identifier_nodes_readonly(NodeEnv,SubEnv1),
363 create_identifier_nodes2(Names,_NIds,SubEnv1,SubEnv,In,Inter1),!,
364 extract_nodes_l(Subs,SubEnv,_SubNodeLists,_Modes,NSubs,Inter1,Out).
365 extract_nodes2(_,TExpr,_,_,_,_,_,_) :-
366 throw(failed_analysis(TExpr)).
367
368 add_type_constraints(TExpr,Nodes,In,Out) :-
369 get_texpr_type(TExpr,Type),
370 findall(C, type_constraint(Type,Nodes,C), Constraints),
371 add_all(Constraints,In,Out).
372
373 type_constraint(Type,Nodes,constraint([],[possible_values:N],constant(irange(1,Card)))) :-
374 member(possible_values:N,Nodes),
375 max_type_cardinality(Type,Card).
376 type_constraint(set(T),Nodes,constraint([],[card:N],constant(irange(0,Card)))) :-
377 member(card:N,Nodes),
378 max_type_cardinality(T,Card).
379 type_constraint(integer,Nodes,constraint([interval:N],[possible_values:N],interval_possibilities)) :-
380 member(interval:N,Nodes),member(possible_values:N,Nodes).
381
382 max_type_cardinality(global(G),Card) :-
383 % TODO: This should be switched off if we try to determine the cardinality of the deferred sets
384 b_global_set_cardinality(G,Card).
385 max_type_cardinality(boolean,2).
386 max_type_cardinality(set(T),Card) :-
387 max_type_cardinality(T,C1),
388 % catch/3 to avoid overflow errors
389 catch( Card is ceiling(2 ** C1),
390 error(_,_),
391 fail).
392 max_type_cardinality(couple(A,B),Card) :-
393 max_type_cardinality(A,CardA),
394 max_type_cardinality(B,CardB),
395 Card is CardA * CardB.
396
397 add_existing_constraints(Info,NodeId,In,Out) :-
398 memberchk(analysis(Analysis),Info),!,
399 findall( constraint([],[Scope:NodeId],constant(Value)),
400 member(Scope:Value,Analysis),
401 Constraints),
402 add_all(Constraints,In,Out).
403 add_existing_constraints(_Info,_NodeId,In,In).
404
405 extract_constraint_for_value(Type,Value,LocalNodes,constraint([],[NodeType:NodeId],Constant)) :-
406 extract_constraint_for_value2(Type,Value,NodeType,Constant),
407 memberchk(NodeType:NodeId,LocalNodes).
408 extract_constraint_for_value2(set(_Type),Value,card,Constant) :-
409 compute_integer(card(Value),Card),
410 Constant = constant(irange(Card,Card)).
411 extract_constraint_for_value2(set(integer),Value,elem(interval),constant(irange(Min,Max))) :-
412 compute_integer(min(Value),Min),
413 compute_integer(max(Value),Max).
414 extract_constraint_for_value2(set(couple(TA,_)),Value,elem(left(DN)),Constant) :-
415 compute_expression(domain(Value),set(TA),Dom),
416 create_texpr(value(Dom),set(TA),[],BDom),
417 extract_constraint_for_value2(set(TA),BDom,elem(DN),Constant).
418 extract_constraint_for_value2(set(couple(_,TB)),Value,elem(right(RN)),Constant) :-
419 compute_expression(range(Value),set(TB),Ran),
420 create_texpr(value(Ran),set(TB),[],BRan),
421 extract_constraint_for_value2(set(TB),BRan,elem(RN),Constant).
422
423 compute_integer(Expr,Result) :-
424 compute_expression(Expr,integer,int(Result)).
425 compute_expression(Expr,Type,Result) :-
426 empty_state(Empty),
427 create_texpr(Expr,Type,[],BExpr),
428 b_compute_expression_nowf(BExpr,Empty,Empty,Result).
429
430 compset_constraints(SetNodes,Ids,NodeEnv,Constraints) :-
431 lookup_identifier_nodes(Ids,NodeEnv,NodeLists),
432 findall( C,
433 (SetNode=elem(Scope):_SetNodeId, member(SetNode,SetNodes),
434 compset_constraint(Scope,NodeLists,ElemNode),
435 member(C, [constraint([SetNode],[ElemNode],id),
436 constraint([ElemNode],[SetNode],id)])),
437 Constraints).
438 compset_constraint(left(Scope),NodeLists,Node) :- !,
439 last(Begin,_,NodeLists),
440 compset_constraint(Scope,Begin,Node).
441 compset_constraint(right(Scope),NodeLists,Node) :- !,
442 last(_,Last,NodeLists),
443 compset_constraint(Scope,[Last],Node).
444 compset_constraint(Scope,[Nodes],Scope:NodeId) :-
445 memberchk(Scope:NodeId,Nodes).
446
447 lookup_identifier_nodes([],_Env,[]).
448 lookup_identifier_nodes([TId|Irest],Env,[Nodes|Nrest]) :-
449 get_texpr_id(TId,Id),
450 identifier_node_lookup(Id,Env,_NodeId,Nodes,_Mode),
451 lookup_identifier_nodes(Irest,Env,Nrest).
452
453 /*
454 extract_set_extension([L],NodeEnv,Nodes,[NTExpr],In,Out) :- !,
455 extract_nodes(L,NodeEnv,Nodes,_Mode,NTExpr,In,Out).
456 extract_set_extension([Elem|Rest],NodeEnv,TmpNodes,[NTExpr|NTrest],In,Out) :-
457 extract_nodes(Elem,NodeEnv,ElemNodes,_Mode,NTExpr,In,Inter1),
458 extract_set_extension(Rest,NodeEnv,RestNodes,NTrest,Inter1,Inter2),
459 %append([TmpNodes,ElemNodes,RestNodes],Nodes),
460 get_texpr_type(Elem,Type),
461 new_node_scope(Type,NodeId,TmpNodes),
462 add_node_infos([],NodeId,_,Inter2,Inter3),
463 findall(C,( EN = Scope:_,
464 RN = Scope:_,
465 ON = Scope:_,
466 member(EN,ElemNodes),
467 memberchk(RN,RestNodes),
468 memberchk(ON,TmpNodes),
469 member(C,[constraint([EN,RN],[ON],union),
470 constraint([ON],[EN],id),
471 constraint([ON],[RN],id)])),
472 Constraints),
473 add_all(Constraints,Inter3,Out).
474 */
475 extract_set_extension(Elements,NodeEnv,SeqNodes,NElements,In,Out) :-
476 extract_extension_nodes(Elements,NodeEnv,ElemNodes,NElements,In,Inter),
477 findall(n(Scope,elem(Scope):N),member(elem(Scope):N,SeqNodes),Scopes),
478 extract_ext_nodes2(Scopes,ElemNodes,Constraints),
479 add_all(Constraints,Inter,Out).
480
481 extract_seq_extension(Elements,NodeEnv,SeqNodes,NElements,In,Out) :-
482 extract_extension_nodes(Elements,NodeEnv,ElemNodes,NElements,In,Inter),
483 findall(n(Scope,elem(right(Scope)):N),member(elem(right(Scope)):N,SeqNodes),Scopes),
484 extract_ext_nodes2(Scopes,ElemNodes,Constraints),
485 add_all(Constraints,Inter,Out).
486
487 extract_ext_nodes2([],_ElemNodes,[]).
488 extract_ext_nodes2([Scope|Srest],ElemNodes,Constraints) :-
489 extract_ext_nodes3(Scope,ElemNodes,C1),
490 append(C1,C2,Constraints),
491 extract_ext_nodes2(Srest,ElemNodes,C2).
492 extract_ext_nodes3(n(Scope,SeqNode),ElemNodes,[Union|Constraints]) :-
493 findall(Scope:E,
494 ( member(NodeList,ElemNodes), memberchk(Scope:E,NodeList)),
495 AllElemNodes),
496 Union = constraint(AllElemNodes,[SeqNode],union),
497 % for set extensions like { min(0..7) } this generates an empty list AllElemenNodes; TO DO: fix
498 findall(constraint([SeqNode],[E],id), member(E,AllElemNodes), Constraints).
499
500 extract_extension_nodes([],_NodeEnv,[],[],X,X).
501 extract_extension_nodes([Elem|Rest],NodeEnv,[ElemNodes|RestNodes],[NElem|NRest],In,Out) :-
502 extract_nodes(Elem,NodeEnv,ElemNodes,_Mode,NElem,In,Inter),
503 extract_extension_nodes(Rest,NodeEnv,RestNodes,NRest,Inter,Out).
504
505
506 texpr_add_node_infos(TExpr,NodeId,NTExpr,In,Out) :-
507 get_texpr_expr(TExpr,Expr),
508 get_texpr_type(TExpr,Type),
509 get_texpr_info(TExpr,Info),
510 add_node_infos(Info,NodeId,NewInfo,In,Inter),
511 create_texpr(Expr,Type,NewInfo,NTExpr),
512 add_existing_constraints(Info,NodeId,Inter,Out).
513 add_node_infos(OldInfo,NodeId,NewInfos,
514 (Constraints,[infonode(NodeId,NewInfos,OldInfo)|Out]),
515 (Constraints,Out)).
516
517 add_all([],In,In).
518 add_all([E|Rest],([E|In],Infonodes),Out) :-
519 add_all(Rest,(In,Infonodes),Out).
520 extract_nodes_l([],_,[],[],[],In,In).
521 extract_nodes_l([TExpr|ERest],NodeEnv,[Node|NRest],[Mode|MRest],[NTExpr|NTRest],In,Out) :-
522 extract_nodes(TExpr,NodeEnv,Node,Mode,NTExpr,In,Inter),
523 extract_nodes_l(ERest,NodeEnv,NRest,MRest,NTRest,Inter,Out).
524 choose_one([],[],[],[]).
525 choose_one([List1|L1Rest],[List2|L2Rest],[Elem1|E1Rest],[Elem2|E2Rest]) :-
526 choose_one2(List1,List2,Elem1,Elem2),
527 choose_one(L1Rest,L2Rest,E1Rest,E2Rest).
528 choose_one2(NodeList,NodeListRO,Elem,ElemRO) :-
529 ( Elem == (*) -> true
530 ; otherwise -> nth1(N,NodeList,Elem), nth1(N,NodeListRO,ElemRO)).
531 ignore_readonly_nodes([],[],[]).
532 ignore_readonly_nodes([Mode|MRest],[InNodes|InRest],[OutNodes|OutRest]) :-
533 ignore_readonly_nodes2(Mode,InNodes,OutNodes),
534 ignore_readonly_nodes(MRest,InRest,OutRest).
535 ignore_readonly_nodes2(ro,Nodes,Ignored) :- ignore_readonly_nodes3(Nodes,Ignored).
536 ignore_readonly_nodes2(rw,Nodes,Nodes).
537 ignore_readonly_nodes3([],[]).
538 ignore_readonly_nodes3([Scope:_Node|NRest],[Scope:ignored|IRest]) :-
539 ignore_readonly_nodes3(NRest,IRest).
540
541 nodes_for_general_sum_or_mult(Rule,TId,TPred,TExpr,TOrig,NodeEnv,Nodes,TNew,In,Out) :-
542 identifier_nodes_readonly(NodeEnv,SubEnv1),
543 create_identifier_nodes2([TId],[NTId],SubEnv1,SubEnv,In,Inter1),
544 extract_nodes_l([TId,TPred,TExpr],SubEnv,[IdNodes,_PredNodes,ExprNodes],_Modes,
545 [NTId,NTPred,NTExpr],Inter1,Inter2),
546 get_texpr_info(TOrig,Info),
547 memberchk(interval:ExprNodeId,ExprNodes),
548 memberchk(possible_values:IdId,IdNodes),
549 new_node_scope(integer,NodeId,Nodes),
550 add_node_infos(Info,NodeId,NInfo,Inter2,Inter3),
551 add_all([constraint([possible_values:IdId,interval:ExprNodeId],
552 [interval:NodeId],Rule)],
553 Inter3,Out),
554 get_texpr_expr(TOrig,OrigExpr),functor(OrigExpr,Functor,_),
555 NExpr =.. [Functor,[NTId],NTPred,NTExpr],
556 create_texpr(NExpr,integer,NInfo,TNew).
557
558 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
559 % extract constraints from syntax elements
560
561 extract(Expr,R,X,Y,Op) :-
562 extract_symmetric(Expr,R,X1,Y1,Op1),
563 symmetric_constraint(Op1,Op2),
564 ( Op=Op1,X=X1,Y=Y1
565 ; Op=Op2,X=Y1,Y=X1).
566
567 extract(integer(I),R,[],[interval:R],constant(irange(I,I))).
568 extract(value(IV),R,[],[interval:R],constant(irange(I,I))) :- nonvar(IV), IV = int(I). % TO DO: other vals
569 extract(interval(interval:A,interval:B),R,[interval:A,interval:B],[card:R],interval_card).
570 extract(member(S:A,elem(S):B),_,[elem(S):B],[S:A],id).
571 extract(subset(elem(S):A,elem(S):B),_,[elem(S):B],[elem(S):A],id).
572 extract(subset_strict(elem(S):A,elem(S):B),_,[elem(S):B],[elem(S):A],id).
573 extract(add(interval:A,interval:B),R,I,O,C) :- addition_constraint(interval:A,interval:B,interval:R,I,O,C).
574 extract(minus(interval:A,interval:B),R,I,O,C) :- addition_constraint(interval:B,interval:R,interval:A,I,O,C).
575 extract(minus(interval:A,interval:B),R,[interval:R,interval:B],[interval:A],add).
576 extract(minus(interval:A,interval:B),R,[interval:A,interval:R],[interval:B],sub).
577 extract(multiplication(interval:A,interval:B),R,[interval:A,interval:B],[interval:R],mult).
578 extract(div(interval:A,interval:B),R,[interval:A,interval:B],[interval:R],divrange).
579 extract(floored_div(interval:A,interval:B),R,[interval:A,interval:B],[interval:R],divrange). % TO DO: provide proper treatment of floored_div
580 extract(modulo(*,interval:B),R,[interval:B],[interval:R],lt).
581 extract(modulo(*,*),R,[],[interval:R],constant(irange(0,inf))).
582 extract(power_of(interval:Base,interval:Exp),R,[interval:Base,interval:Exp],[interval:R],power).
583 extract(integer_set(Set),R,[],[elem(interval):R],constant(irange(Min,Max))) :-
584 compute_integer_set(Set,Min,Max).
585 extract(bool_set,R,[],[card:R],constant(irange(2,2))).
586 extract(pow1_subset(*),R,[],[elem(card):R],constant(irange(1,inf))).
587 extract(fin_subset(A),R,AL,RL,Op) :- extract(pow_subset(A),R,AL,RL,Op).
588 extract(fin1_subset(A),R,AL,RL,Op) :- extract(pow1_subset(A),R,AL,RL,Op).
589 extract(partial_function(A,B),R,AL,RL,Op) :- extract(relations(A,B),R,AL,RL,Op).
590 extract(total_function(A,B),R,AL,RL,Op) :- extract(relations(A,B),R,AL,RL,Op).
591 extract(partial_injection(A,B),R,AL,RL,Op) :- extract(partial_function(A,B),R,AL,RL,Op).
592 extract(total_injection(A,B),R,AL,RL,Op) :- extract(total_function(A,B),R,AL,RL,Op).
593 extract(partial_surjection(A,B),R,AL,RL,Op) :- extract(partial_function(A,B),R,AL,RL,Op).
594 extract(total_surjection(A,B),R,AL,RL,Op) :- extract(total_function(A,B),R,AL,RL,Op).
595 extract(total_bijection(A,B),R,AL,RL,Op) :- extract(total_function(A,B),R,AL,RL,Op).
596 extract(partial_bijection(A,B),R,AL,RL,Op) :- extract(partial_function(A,B),R,AL,RL,Op).
597 extract(cartesian_product(card:A,card:B),R,[card:A,card:B],[card:R],mult).
598 extract(cartesian_product(card:A,card:B),R,[card:R,card:A],[card:B],cart_div).
599 extract(cartesian_product(card:A,card:B),R,[card:R,card:B],[card:A],cart_div).
600 extract(set_subtraction(elem(S):A,*),R,[elem(S):A],[elem(S):R],id).
601 extract(image(elem(right(S)):A,*),R,[elem(right(S)):A],[elem(S):R],id).
602 extract(intersection(S:A,*),R,I,O,C) :- is_subset_extract(S:R,S:A,I,O,C).
603 extract(intersection(*,S:B),R,I,O,C) :- is_subset_extract(S:R,S:B,I,O,C).
604 extract(union(elem(S):A,elem(S):B),R,[elem(S):A,elem(S):B],[elem(S):R],union).
605 extract(union(S:A,*),R,I,O,C) :- is_subset_extract(S:A,S:R,I,O,C).
606 extract(union(*,S:B),R,I,O,C) :- is_subset_extract(S:B,S:R,I,O,C).
607 extract(domain_restriction(S:A,*),R,I,O,C) :- is_subset_extract(left(S):R,S:A,I,O,C).
608 extract(domain_restriction(*,S:B),R,I,O,C) :- is_subset_extract(S:R,S:B,I,O,C).
609 extract(domain_subtraction(*,S:B),R,I,O,C) :- is_subset_extract(S:R,S:B,I,O,C).
610 extract(range_restriction(*,S:B),R,I,O,C) :- is_subset_extract(right(S):R,S:B,I,O,C).
611 extract(range_restriction(S:A,*),R,I,O,C) :- is_subset_extract(S:R,S:A,I,O,C).
612 extract(range_subtraction(S:A,*),R,I,O,C) :- is_subset_extract(S:R,S:A,I,O,C).
613 extract(closure(elem(S):A),R,[elem(S):A],[elem(S):R],id).
614 extract(composition(elem(left(S)):A,*),R,I,O,C) :- is_subset_extract(elem(left(S)):R,elem(left(S)):A,I,O,C).
615 extract(composition(*,elem(right(S)):B),R,I,O,C) :- is_subset_extract(elem(right(S)):R,elem(right(S)):B,I,O,C).
616 extract(function(elem(right(S)):A,*),R,[elem(right(S)):A],[S:R],id).
617 extract(concat(card:A,card:B),R,I,O,C) :- addition_constraint(card:A,card:B,card:R,I,O,C).
618 extract(concat(Right:A,Right:B),R,[Right:A,Right:B],[Right:R],union) :- Right=elem(right(_)).
619 extract(concat(S:A,*),R,I,O,C) :- S=elem(right(_)),is_subset_extract(S:A,S:R,I,O,C).
620 extract(concat(*,S:B),R,I,O,C) :- S=elem(right(_)),is_subset_extract(S:B,S:R,I,O,C).
621 extract(concat(LI:A,LI:B),R,[LI:A,LI:B],[LI:R],concat_index) :- LI=elem(left(interval)). % TODO: More rules like addition
622 extract(seq(*),R,[],[elem(elem(left(interval))):R],constant(irange(1,inf))).
623 extract(seq1(A),R,I,O,C) :- extract(seq(A),R,I,O,C).
624 extract(seq1(*),R,[],[elem(card):R],constant(irange(1,inf))).
625 extract(size(A),R,I,O,C) :- extract(card(A),R,I,O,C).
626
627 addition_constraint(A,B,R,[A,B],[R],add).
628 addition_constraint(A,B,R,[R,B],[A],sub).
629 addition_constraint(A,B,R,[R,A],[B],sub).
630
631 is_subset_extract(A,B,I,O,C) :-extract(subset(A,B),_,I,O,C).
632
633 extract_symmetric(equal(S:A,S:B),_,[S:A],[S:B],id).
634 extract_symmetric(subset(card:A,card:B),_,[card:A],[card:B],gte).
635 extract_symmetric(subset_strict(card:A,card:B),_,[card:A],[card:B],gt).
636 extract_symmetric(less(interval:A,interval:B),_,[interval:B],[interval:A],lt).
637 extract_symmetric(less_equal(interval:A,interval:B),_,[interval:B],[interval:A],lte).
638 extract_symmetric(greater(interval:A,interval:B),_,[interval:B],[interval:A],gt).
639 extract_symmetric(greater_equal(interval:A,interval:B),_,[interval:B],[interval:A],gte).
640 extract_symmetric(unary_minus(interval:X),R,[interval:X],[interval:R],negate).
641 extract_symmetric(card(card:X),R,[card:X],[interval:R],id).
642 extract_symmetric(cartesian_product(elem(S):A,*),R,[elem(S):A],[elem(left(S)):R],id).
643 extract_symmetric(cartesian_product(*,elem(S):B),R,[elem(S):B],[elem(right(S)):R],id).
644 extract_symmetric(couple(S:A,*),R,[S:A],[left(S):R],id).
645 extract_symmetric(couple(*,S:B),R,[S:B],[right(S):R],id).
646 extract_symmetric(pow_subset(elem(S):A),R,[elem(S):A],[elem(elem(S)):R],id).
647 extract_symmetric(pow_subset(card:A),R,[card:A],[elem(card):R],lte).
648 extract_symmetric(pow1_subset(S),R,[A],[B],C) :- extract_symmetric(pow_subset(S),R,[A],[B],C).
649 extract_symmetric(seq(A),R,[elem(S):A],[elem(elem(right(S))):R],id).
650 extract_symmetric(relations(elem(S):A,*),R,[elem(S):A],[elem(elem(left(S))):R],id).
651 extract_symmetric(relations(*,elem(S):B),R,[elem(S):B],[elem(elem(right(S))):R],id).
652 extract_symmetric(interval(interval:A,interval:_),R,[interval:A],[elem(interval):R],gte).
653 extract_symmetric(interval(interval:_,interval:B),R,[interval:B],[elem(interval):R],lte).
654 extract_symmetric(partial_function(card:A,card:_),R,[card:A],[elem(card):R],lte).
655 extract_symmetric(total_function(card:A,card:_),R,[card:A],[elem(card):R],id).
656 extract_symmetric(partial_injection(card:_,card:B),R,[card:B],[elem(card):R],lte).
657 extract_symmetric(total_injection(card:_,card:B),R,[card:B],[elem(card):R],lte).
658 extract_symmetric(total_surjection(card:_,card:B),R,[card:B],[elem(card):R],gte).
659 extract_symmetric(total_bijection(card:A,card:_),R,[card:A],[elem(card):R],id).
660 extract_symmetric(total_bijection(card:_,card:B),R,[card:B],[elem(card):R],id).
661 extract_symmetric(domain(elem(left(S)):A),R,[elem(left(S)):A],[elem(S):R],id).
662 extract_symmetric(range(elem(right(S)):A),R,[elem(right(S)):A],[elem(S):R],id).
663 extract_symmetric(reverse(elem(left(S)):A),R,[elem(left(S)):A],[elem(right(S)):R],id).
664 extract_symmetric(reverse(elem(right(S)):A),R,[elem(right(S)):A],[elem(left(S)):R],id).
665 extract_symmetric(reverse(card:A),R,[card:A],[card:R],id).
666 extract_symmetric(set_subtraction(card:A,*),R,[card:A],[card:R],gte).
667 extract_symmetric(closure(elem(left(S)):A),R,[elem(left(S)):A],[elem(left(S)):R],id).
668 extract_symmetric(closure(elem(right(S)):A),R,[elem(right(S)):A],[elem(right(S)):R],id).
669 extract_symmetric(closure(card:A),R,[card:A],[card:R],gte).
670 extract_symmetric(image(card:A,*),R,[card:R],[card:A],gte).
671 extract_symmetric(first_of_pair(left(S):A),R,[left(S):A],[S:R],id).
672 extract_symmetric(second_of_pair(right(S):A),R,[right(S):A],[S:R],id).
673
674 symmetric_constraint(A,B) :- symmetric_constraint2(A,B),!.
675 symmetric_constraint(A,B) :- symmetric_constraint2(B,A).
676 symmetric_constraint2(id,id).
677 symmetric_constraint2(lt,gt).
678 symmetric_constraint2(lte,gte).
679 symmetric_constraint2(negate,negate).
680
681
682 % this seems to take up most of the time:
683 create_constraint_tree(Constraints,Nodes,Tree) :-
684 empty_avl(EmptyTree),
685 sort(Constraints,SConstraints), % for SAT_Generator sorting reduces runtime from 22 sec to 18 sec for n=8; TO DO: investigate
686 (debug:debug_mode(on) -> length(Constraints,Len), format('Processing ~w constraints~n',[Len]) ; true),
687 create_constraint_tree1(SConstraints,[],UnsortedNodes,EmptyTree,Tree),
688 (debug:debug_mode(on) -> length(UnsortedNodes,Len2), format('Sorting ~w nodes~n',[Len2]) ; true),
689 sort(UnsortedNodes,Nodes),
690 (debug:debug_mode(on) -> length(Nodes,Len3), format('Done sorting ~w nodes~n',[Len3]) ; true).
691 %,set_prolog_flag(profiling,off),print_profile.
692
693 create_constraint_tree1([],Nodes,Nodes,Tree,Tree).
694 create_constraint_tree1([C|CRest],InNodes,OutNodes,InTree,OutTree) :-
695 create_constraint_tree2(C,InNodes,InterNodes,InTree,InterTree),
696 create_constraint_tree1(CRest,InterNodes,OutNodes,InterTree,OutTree).
697
698 create_constraint_tree2(Constraint,InNodes,OutNodes,InTree,OutTree) :-
699 Constraint = constraint(Input,Output,_Rule),
700 ( all_nodes_ignored(Output) ->
701 InTree = OutTree, InNodes=OutNodes
702 ; %otherwise ->
703 add_all_to_constraint_tree(Input,Constraint,InTree,OutTree),
704 %ord_union(Input,InNodes,Nodes),ord_union(Output,Nodes,OutNodes) % this can be very expensive !
705 append(Input,InNodes,Nodes), append(Output,Nodes,OutNodes) % just append the nodes and sort them later
706 ).
707
708 add_all_to_constraint_tree([],_,Tree,Tree).
709 add_all_to_constraint_tree([Node|NRest],Constraint,InTree,OutTree) :-
710 ( avl_change(Node,InTree,Old,InterTree,[Constraint|Old]) ->
711 true
712 ; %otherwise ->
713 avl_store(Node,InTree,[Constraint],InterTree)),
714 add_all_to_constraint_tree(NRest,Constraint,InterTree,OutTree).
715
716 all_nodes_ignored([]).
717 all_nodes_ignored([_:ignored|Rest]) :-
718 all_nodes_ignored(Rest).
719
720
721 create_initial_information(Nodes,InitEnv) :-
722 empty_avl(Empty),
723 foldl(create_initial_information2,Nodes,Empty,InitEnv).
724 create_initial_information2(Scope:Node,In,Out) :-
725 get_scope_top(Scope,Top),
726 avl_store(Scope:Node,In,Top,Out).
727
728 apply_start_constraints(Constraints,ChangedNodes,In,Out) :-
729 find_start_constraints(Constraints,Start),
730 writetodot(1,[],Start,In),
731 compute_constraints(Start,ChangedNodes1,[],In,Out),
732 list_to_ord_set(ChangedNodes1,ChangedNodes).
733 find_start_constraints(Constraints,Start) :-
734 findall(constraint([],Output,Rule),
735 member(constraint([],Output,Rule),Constraints),
736 Start).
737
738 do_analysis([],Counter,_MaxIter,_CTree,Values,Values) :-
739 !,writetodot(Counter,[],[],Values).
740 do_analysis([ChangedNode|CNRest],Counter,MaxIter,CTree,In,Out) :-
741 Counter < MaxIter,!,
742 ( avl_fetch(ChangedNode,CTree,Constraints) -> true
743 ; otherwise -> Constraints = []),
744 writetodot(Counter,[ChangedNode|CNRest],Constraints,In),
745 compute_constraints(Constraints,ChangedNodes1,[],In,Inter),
746 list_to_ord_set(ChangedNodes1,ChangedNodes),
747 ord_union(CNRest,ChangedNodes,NewChangedNodes),
748 Counter2 is Counter+1,
749 do_analysis(NewChangedNodes,Counter2,MaxIter,CTree,Inter,Out).
750 do_analysis(_ChangedNodes,Counter,MaxIter,_CTree,Values,Values) :-
751 Counter >= MaxIter.
752 compute_constraints([],Changed,Changed,Values,Values).
753 compute_constraints([Constraint|CRest],Cin,Cout,Vin,Vout) :-
754 compute_constraint(Constraint,Cin,Cinter,Vin,Vinter),
755 compute_constraints(CRest,Cinter,Cout,Vinter,Vout).
756 compute_constraint(constraint(Input,Output,Rule),Cin,Cout,Vin,Vout) :-
757 get_input_values(Input,Vin,InValues),
758 same_length(Output,OutValues),
759 call_rule(Rule,InValues,OutValues),
760 apply_output(Output,OutValues,Cin,Cout,Vin,Vout).
761 get_input_values([],_,[]).
762 get_input_values([Node|NRest],Values,[Value|VRest]) :-
763 avl_fetch(Node,Values,Value),
764 get_input_values(NRest,Values,VRest).
765 call_rule(Rule,InValues,OutValues) :-
766 append_arguments(Rule,InValues,CallRule),
767 if(rule(CallRule,OutValues), true,
768 (add_warning(predicate_analysis,'No rule for: ',CallRule),fail)).
769 apply_output([],[],Changed,Changed,Values,Values).
770 apply_output([Node|NRest],[Value|VRest],Cin,Cout,Vin,Vout) :-
771 ( Node = _:ignored ->
772 Vin = Vinter, Cin=Cinter
773 ; otherwise ->
774 avl_change(Node,Vin,OldValue,Vinter,NewValue),
775 info_conjunct(OldValue,Value,NewValue),
776 ( has_more_information(NewValue,OldValue) ->
777 Cin = [Node|Cinter]
778 ; otherwise ->
779 Cin = Cinter)),
780 apply_output(NRest,VRest,Cinter,Cout,Vinter,Vout).
781
782 append_arguments(Orig,Args,Result) :-
783 Orig =.. FA,
784 append_arguments2(FA,Args,AllArgs),
785 Result =.. AllArgs.
786 append_arguments2([F|FA],Args,AllArgs) :-
787 multi_rule(F),!,
788 append([F|FA],[Args],AllArgs).
789 append_arguments2(FA,Args,AllArgs) :-
790 append(FA,Args,AllArgs).
791
792
793 create_add_list_constraints([],_Result,Constraints) :- !,Constraints=[].
794 create_add_list_constraints([Node],Result,Constraints) :- !,
795 Constraints = [constraint([Node],[Result],id),constraint([Result],[Node],id)].
796 create_add_list_constraints(Nodes,Result,Constraints) :- !,
797 create_add_list_constraints2(Nodes,Result,Constraints,[]).
798 create_add_list_constraints2([A,B],Result) --> !,
799 findall(constraint(I,O,C),addition_constraint(A,B,Result,I,O,C)).
800 create_add_list_constraints2([A|Nodes],Result) -->
801 {new_node_scope(integer,NodeId,_Nodes)},
802 findall(constraint(I,O,C),addition_constraint(A,interval:NodeId,Result,I,O,C)),
803 create_add_list_constraints2(Nodes,interval:NodeId).
804
805
806
807 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
808 % compute a single constraint
809
810 rule(id(Info),[Info]).
811 rule(constant(Info),[Info]).
812 rule(add(A,B),[C]) :- interval_addition(A,B,C).
813 rule(sub(A,B),[C]) :- interval_subtraction(A,B,C).
814 rule(mult(A,B),[C]) :- interval_multiplication(A,B,C).
815 rule(power(Base,Exp),[Result]) :-
816 ( power_rule(Base,Exp,Result) -> true
817 ; otherwise -> Result = irange(-inf,inf)).
818 rule(cart_div(irange(Cm,CM),irange(Am,AM)),[irange(Bm,BM)]) :-
819 ( CM = inf ->
820 BM = inf
821 ; Am = 0 ->
822 BM = inf
823 ; otherwise ->
824 BM infis (CM+Am-1) // Am),
825 ( AM = 0 -> Bm = inf
826 ; AM = inf -> Bm = inf
827 ; otherwise ->
828 Bm infis Cm // AM).
829 rule(negate(A),[C]) :- interval_negation(A,C).
830 rule(lt(irange(_,AM)),[irange(-inf,CM)]) :- CM infis AM-1.
831 rule(lte(irange(_,AM)),[irange(-inf,AM)]).
832 rule(gt(irange(Am,_)),[irange(Cm,inf)]) :- Cm infis Am+1.
833 rule(gte(irange(Am,_)),[irange(Am,inf)]).
834 rule(interval_card(irange(Am,AM),irange(Bm,BM)),[irange(Cm,CM)]) :-
835 Cm infis max(Bm-AM+1,0), CM infis max(BM-Am+1,0).
836 rule(interval_possibilities(irange(Am,AM)),[irange(0,M)]) :-
837 M infis max(AM-Am+1,0).
838 rule(union([A|Rest]),[C]) :- union2(Rest,A,C).
839 rule(union([]),[irange(-inf,inf)]) :- print(empty_kodkod_union_constraint),nl. % TO DO: check if ok !!
840 % happens e.g., for set extensions like { min(0..7) } ; should not occur
841 rule(divrange(irange(Am,AM),irange(Bm,_BM)),[C]) :-
842 % rough approximation to division
843 ( number(Bm),Bm>=0 ->
844 C = irange(Am,AM)
845 ; number(Am),number(AM) ->
846 M is max(abs(Am),abs(AM)),
847 C = irange(-M,M)
848 ; otherwise ->
849 C = irange(-inf,inf)).
850 rule(concat_index(irange(_,AM),irange(_,BM)),[irange(1,CM)]) :-
851 ( number(AM),number(BM) -> CM is AM+BM
852 ; otherwise -> CM = inf).
853
854
855 union2([],Acc,R) :- !,Acc=R.
856 union2([H|T],Acc,R) :- info_disjunct(H,Acc,I),union2(T,I,R).
857
858 power_rule(irange(Basem,BaseM),irange(Expm,ExpM),irange(Rm,RM)) :-
859 number(Basem),Basem > 0,
860 number(Expm),Expm >= 0,
861 Rm is floor( Basem ** Expm),
862 ( (BaseM=inf; ExpM=inf) -> RM=inf
863 ; otherwise -> RM is floor(BaseM ** ExpM)).
864
865 multi_rule(union).
866
867
868 insert_info(Infonodes,Values) :-
869 create_info_lookup_table(Values,ValueTable),
870 insert_info2(Infonodes,ValueTable).
871 insert_info2([],_).
872 insert_info2([infonode(Node,New,Old)|Rest],Values) :-
873 find_all_node_information(Node,Values,Info),
874 add_info(Info,Old,New),
875 insert_info2(Rest,Values).
876 find_all_node_information(Node,Values,Info) :-
877 ( avl_member(Node,Values,RawInfos) -> filter_tops(RawInfos,Info)
878 ; otherwise -> Info = []).
879 filter_tops([],[]).
880 filter_tops([Scope:RI|RRest],Result) :-
881 get_scope_top(Scope,Top),
882 ( has_more_information(RI,Top) -> Result = [Scope:RI|RestResult]
883 ; otherwise -> Result = RestResult),
884 filter_tops(RRest,RestResult).
885 add_info([],Old,Old) :- !.
886 add_info(Info,Old,[analysis(Info)|Old]).
887
888 create_info_lookup_table(Values,Nodes) :-
889 avl_to_list(Values,ValueList),
890 empty_avl(Empty),
891 create_info_lookup_table2(ValueList,Empty,Nodes).
892 create_info_lookup_table2([],Nodes,Nodes).
893 create_info_lookup_table2([(Scope:Node)-I|Rest],In,Out) :-
894 ( avl_fetch(Node,In,Old) -> true
895 ; otherwise -> Old = []),
896 avl_store(Node,In,[Scope:I|Old],Inter),
897 create_info_lookup_table2(Rest,Inter,Out).
898
899 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
900 % dot output
901
902 writetodot(Counter,ChangedNodes,ActiveConstraints,Values) :-
903 debugging_enabled,!,
904 debug_constraints(Constraints),
905 writetodot(Counter,Constraints,ChangedNodes,ActiveConstraints,Values).
906 writetodot(_Counter,_ChangedNodes,_ActiveConstraints,_Values).
907
908 writetodot(Counter,Constraints,ChangedNodes,ActiveConstraints,Values) :-
909 printnulls(3,Counter,CStr),
910 ajoin(['debug-constraint-anim/constraints-',CStr,'.dot'],Filename),
911 open(Filename,write,S),
912 write(S,'digraph constraints {\n'),
913 get_nodes(Constraints,INodes,CNodes),
914 write_inodes(INodes,ChangedNodes,Values,S),
915 write_cnodes(CNodes,INodes,ActiveConstraints,S),
916 write(S,'}\n'),
917 close(S).
918 get_nodes(Constraints,INodes,RNodes) :-
919 findall(CN,
920 (member(constraint(I,O,_),Constraints),
921 (member(CN,I);member(CN,O))),
922 Nodes1),
923 sort(Nodes1,Nodes2),
924 number_nodes(Nodes2,1,INodes),
925 number_nodes(Constraints,1,RNodes).
926 number_nodes([],_,[]).
927 number_nodes([N|Rest],Nr,[node(Nr,N)|RRest]) :-
928 Nr2 is Nr+1,
929 number_nodes(Rest,Nr2,RRest).
930 write_inodes([],_,_,_).
931 write_inodes([node(Nr,N)|Rest],ChangedNodes,Values,S) :-
932 write(S,' i'),write(S,Nr),write(S,'[shape=box,'),
933 ( ChangedNodes = [N|_] ->
934 !,write(S,'fillcolor=green,style=filled,')
935 ; member(N,ChangedNodes) ->
936 !,write(S,'fillcolor=yellow,style=filled,')
937 ; otherwise -> true),
938 write(S,'label=\"'),
939 write(S,N),write(S,'\\n'),
940 ( debug_node(N,TExpr) ->
941 translate_bexpression(TExpr,Text)
942 ; otherwise ->
943 Text = ('?')),
944 write(S,Text),write(S,'\\n'),
945 ( avl_fetch(N,Values,Value) ->
946 write(S,Value)
947 ; otherwise -> write(S,'?')),
948 write(S,'\"];\n'),
949 write_inodes(Rest,ChangedNodes,Values,S).
950 write_cnodes([],_,_,_).
951 write_cnodes([node(Nr,constraint(In,Out,R))|Rest],INodes,ActiveConstraints,S) :-
952 write(S,' c'),write(S,Nr),write(S,'[shape=diamond,'),
953 ( member(constraint(In,Out,R),ActiveConstraints) ->
954 !,write(S,'fillcolor=lightblue,style=filled,')
955 ; otherwise -> true),
956 write(S,'label=\"'),
957 write(S,R),write(S,'\"];\n'),
958 write_inputs(In,INodes,Nr,S),
959 write_outputs(Out,INodes,Nr,S),
960 write_cnodes(Rest,INodes,ActiveConstraints,S).
961 write_inputs([],_,_,_).
962 write_inputs([I|Rest],INodes,RNr,S) :-
963 member(node(INr,I),INodes),!,
964 write(S,' i'),write(S,INr),write(S,' -> '),
965 write(S,' c'),write(S,RNr),write(S,';\n'),
966 write_inputs(Rest,INodes,RNr,S).
967 write_outputs([],_,_,_).
968 write_outputs([O|Rest],INodes,RNr,S) :-
969 member(node(ONr,O),INodes),!,
970 write(S,' c'),write(S,RNr),write(S,' -> '),
971 write(S,' i'),write(S,ONr),write(S,';\n'),
972 write_outputs(Rest,INodes,RNr,S).
973
974 printnulls(N,Number,Str) :-
975 number_codes(Number,NCodes),
976 length(NCodes,L),
977 R is N-L,
978 fillnulls(R,NCodes,Codes),
979 atom_codes(Str,Codes).
980 fillnulls(R,Codes,Codes) :- R =< 0,!.
981 fillnulls(R,NCodes,[48|Codes]) :-
982 R2 is R-1,
983 fillnulls(R2,NCodes,Codes).
984
985
986 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
987
988 :- dynamic debug_predicate_analysis/0.
989 % assert this fact to enable dot visualization using writetodot below
990
991 store_debug_info(I,T) :- debug_predicate_analysis,!,store_debug_info2(I,T).
992 store_debug_info(_,_).
993
994 store_debug_info2([],_).
995 store_debug_info2([Node|Rest],TExpr) :-
996 assert(debug_node(Node,TExpr)),
997 store_debug_info2(Rest,TExpr).
998
999
1000 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1001 % test case specification for predicate analysis
1002
1003 :- use_module(junit_tests).
1004
1005 test_predicate_analysis :-
1006 b_machine_has_constants,
1007 b_get_machine_constants(Constants),
1008 b_get_properties_from_machine(Properties),
1009 b_get_static_assertions_from_machine(Assertions),
1010 Assertions = [_|_],!,
1011 test_predicate_analysis2(Constants,Properties,Assertions,Junits),
1012 print_junit(Junits,predicate_analysis).
1013 test_predicate_analysis :-
1014 add_error(predicate_analysis,
1015 'Machine must have constants and assertions to be a valid predicate analysis test case.'),
1016 fail.
1017 test_predicate_analysis2(Constants,Properties,Assertions,Junits) :-
1018 predicate_analysis_with_global_sets(Properties,[],Constants,TaggedConstants,_TaggedProperties),!,
1019 test_predicate_analysis3(Assertions,TaggedConstants,Junits).
1020 test_predicate_analysis2(_,_,_,[Junit]) :-
1021 in_junit_mode,!,
1022 pa_junit(setup,error('Predicate analysis failed'),Junit).
1023 test_predicate_analysis2(_,_,_,_) :-
1024 add_error(predicate_analysis, 'Predicate analysis of properties failed'),
1025 fail.
1026 test_predicate_analysis3([],_Constants,[]).
1027 test_predicate_analysis3([Assertion|Arest],Constants,[Junit|Jrest]) :-
1028 test_predicate_analysis_testcase(Assertion,Constants,Junit),
1029 test_predicate_analysis3(Arest,Constants,Jrest).
1030
1031 test_predicate_analysis_testcase(TAssertion,Constants,Junit) :-
1032 translate_bexpression(TAssertion,Name),
1033 ( catch( ( test_predicate_analysis_testcase1(TAssertion,Constants),
1034 pa_junit(Name,pass,Junit)),
1035 junit(Msg),
1036 handle_error(Name,Msg,Junit)) -> true
1037 ; otherwise ->
1038 pa_junit(Name,error('Test failed.'),Junit)).
1039 handle_error(Name,Msg,Junit) :-
1040 in_junit_mode,!,
1041 pa_junit(Name,error(Msg),Junit).
1042 handle_error(Name,Msg,_Junit) :-
1043 add_error(predicate_analysis, Name, Msg), fail.
1044
1045 test_predicate_analysis_testcase1(TAssertion,Constants) :-
1046 extract_predicate_analysis_testcases(TAssertion,[],Testcases),
1047 maplist(run_testcase(Constants),Testcases).
1048 run_testcase(Constants,pa_testcase(Id,Scope,ExpectedValue)) :-
1049 format('Predicate analysis testcase: check if "~w" of "~w" is ~w~N', [Scope,Id,ExpectedValue]),
1050 expected_value_is_top(ExpectedValue,Scope,IsTop),
1051 extract_analysis(Id,Constants,Scope,FoundValue,IsTop),
1052 ( FoundValue == ExpectedValue -> true
1053 ; otherwise ->
1054 ajoin(['unexpected information for constant ',Id,': expected ', ExpectedValue,
1055 ', but was ', FoundValue],Msg),
1056 throw(junit(Msg))).
1057 expected_value_is_top(ExpectedValue,Scope,true) :-
1058 ground(ExpectedValue),get_scope_top(Scope,ExpectedValue),!.
1059 expected_value_is_top(_ExpectedValue,_Scope,false).
1060
1061 extract_predicate_analysis_testcases(TExpr,QRefs,Testcases) :-
1062 get_texpr_expr(TExpr,Expr),
1063 extract_predicate_analysis_testcases2(Expr,QRefs,Testcases).
1064 extract_predicate_analysis_testcases2(forall(Ids,TPre,Cond),QRefs,Testcases) :-
1065 get_texpr_expr(TPre,Pre),introduce_qrefs(Pre,Ids,QRefs,SubQRefs),
1066 extract_predicate_analysis_testcases(Cond,SubQRefs,Testcases).
1067 extract_predicate_analysis_testcases2(equal(Ref,Spec),QRefs,[pa_testcase(Constant,Scope,irange(Value,Value))]) :- !,
1068 extract_test_reference(Ref,QRefs,Constant,Scope),
1069 extract_integer(Spec,Value).
1070 extract_predicate_analysis_testcases2(less_equal(Ref,Spec),QRefs,[pa_testcase(Constant,Scope,irange(-inf,Value))]) :- !,
1071 extract_test_reference(Ref,QRefs,Constant,Scope),
1072 extract_integer(Spec,Value).
1073 extract_predicate_analysis_testcases2(greater_equal(Ref,Spec),QRefs,[pa_testcase(Constant,Scope,irange(Value,inf))]) :- !,
1074 extract_test_reference(Ref,QRefs,Constant,Scope),
1075 extract_integer(Spec,Value).
1076 extract_predicate_analysis_testcases2(member(Ref,TSpec),QRefs,[pa_testcase(Constant,Scope,Info)]) :-
1077 get_texpr_expr(TSpec,Spec),
1078 extract_test_reference(Ref,QRefs,Constant,Scope),
1079 integer_interval_membership(Spec,Scope,Info),!.
1080 extract_predicate_analysis_testcases2(not_member(Ref,TSpec),QRefs,[pa_testcase(Constant,Scope,Top)]) :-
1081 get_texpr_expr(TSpec,empty_set),
1082 extract_test_reference(Ref,QRefs,Constant,Scope),
1083 get_scope_top(Scope,Top).
1084 extract_predicate_analysis_testcases2(conjunct(A,B),QRefs,Testcases) :- !,
1085 extract_predicate_analysis_testcases(A,QRefs,TestcasesA),
1086 extract_predicate_analysis_testcases(B,QRefs,TestcasesB),
1087 append(TestcasesA,TestcasesB,Testcases).
1088 extract_predicate_analysis_testcases2(_Expr,_QRefs,_Testcases) :-
1089 throw(junit('Unsupported test case pattern')).
1090
1091 integer_interval_membership(empty_set,Scope,Bottom) :- get_scope_bottom(Scope,Bottom).
1092 integer_interval_membership(interval(Min,Max),_Scope,irange(VMin,VMax)) :-
1093 extract_integer(Min,VMin),extract_integer(Max,VMax).
1094 integer_interval_membership(integer_set(I),_Scope,irange(Min,Max)) :-
1095 compute_integer_set(I,Min,Max).
1096
1097 introduce_qrefs(member(E2,Ref),Ids,QRefs,NewQRefs) :-
1098 lookup_ref(Ref,QRefs,Path,Constant),append(Path,[elem],Path2),
1099 couple_pathes(E2,Path2,Constant,Ids,NewQRefs,QRefs).
1100 introduce_qrefs(equal(E2,Ref),Ids,QRefs,NewQRefs) :-
1101 lookup_ref(Ref,QRefs,Path,Constant),
1102 couple_pathes(E2,Path,Constant,Ids,NewQRefs,QRefs).
1103
1104 couple_pathes(TExpr,Path,Constant,TIdentifiers) -->
1105 {get_texpr_ids(TIdentifiers,Identifiers)},
1106 couple_pathes2(TExpr,Path,Constant,Identifiers).
1107 couple_pathes2(TExpr,Path,Constant,Identifiers) -->
1108 {get_texpr_expr(TExpr,Expr)},couple_pathes3(Expr,TExpr,Path,Constant,Identifiers).
1109 couple_pathes3(identifier(Id),_,Path,Constant,Identifiers) -->
1110 {memberchk(Id,Identifiers),!},[Id-ref(Path,Constant)].
1111 couple_pathes3(couple(A,B),_,Path,Constant,Identifiers) --> !,
1112 {append(Path,[left],PathA),append(Path,[right],PathB)},
1113 couple_pathes2(A,PathA,Constant,Identifiers),
1114 couple_pathes2(B,PathB,Constant,Identifiers).
1115 couple_pathes3(_,TExpr,_Path,_Constant,_Identifiers,_,_) :-
1116 translate_bexpression(TExpr,String),
1117 atom_concat('Expected quantified identifier or couple in test specification, but was: ',
1118 String,Msg),
1119 throw(junit(Msg)).
1120
1121
1122 lookup_ref(Expr,QRefs,Path,Constant) :-
1123 get_texpr_id(Expr,Id),
1124 memberchk(Id-ref(Path1,Constant1),QRefs),!,
1125 Path1=Path,Constant1=Constant.
1126 lookup_ref(Expr,_QRefs,[],Id) :-
1127 get_texpr_id(Expr,Id).
1128
1129 extract_analysis(Id,Constants,Scope,FoundValue,IsTop) :-
1130 get_texpr_id(Constant,Id),
1131 ( memberchk(Constant,Constants) ->
1132 get_texpr_info(Constant,Info),
1133 ( memberchk(analysis(Analysis),Info) -> true
1134 ; otherwise -> Analysis=[]),
1135 ( memberchk(Scope:FoundValue,Analysis) -> true
1136 ; IsTop == true -> get_scope_top(Scope,FoundValue) /* no information expected */
1137 ; otherwise ->
1138 ajoin(['No analysis information found for constant ',Id,', scope ',Scope],Msg),
1139 print(extract_analysis(Id,Constants,Scope,FoundValue,IsTop)),nl,
1140 throw(junit(Msg)))
1141 ; otherwise ->
1142 ajoin(['Constant ',Id,' not found'],Msg),
1143 throw(junit(Msg))).
1144
1145 extract_test_reference(TRef,QRefs,Id,Scope) :-
1146 get_texpr_expr(TRef,Ref),
1147 extract_test_reference2(Ref,QRefs,BaseScope,Id,ScopeList),
1148 construct_scope(ScopeList,BaseScope,Scope),!.
1149 extract_test_reference(TRef,_QRefs,_Id,_Scope) :-
1150 translate_bexpression(TRef,String),
1151 atom_concat('Unsupported reference on left side: ', String,Msg),
1152 throw(junit(Msg)).
1153 extract_test_reference2(card(TRef),QRefs,card,Id,Scope) :-
1154 !,get_texpr_expr(TRef,Ref),Ref=identifier(_),
1155 extract_test_reference2(Ref,QRefs,_,Id,Scope).
1156 extract_test_reference2(identifier(Id),QRefs,interval,Constant,Scope) :-
1157 memberchk(Id-ref(Scope,Constant),QRefs),!.
1158 extract_test_reference2(identifier(Id),_QRefs,interval,Id,[]) :- !.
1159
1160
1161 :- assert_must_succeed(( construct_scope([elem,right],interval,S),
1162 S==elem(right(interval)) )).
1163 construct_scope([],Scope,Scope).
1164 construct_scope([F|Rest],Base,Scope) :-
1165 functor(Scope,F,1),arg(1,Scope,Subscope),
1166 construct_scope(Rest,Base,Subscope).
1167
1168 extract_integer(Spec,Value) :-
1169 ( get_texpr_expr(Spec,integer(Value)) -> true
1170 ; get_texpr_expr(Spec,unary_minus(Spec2)) ->
1171 extract_integer(Spec2,Value2),Value is -Value2
1172 ; otherwise ->
1173 throw(junit('Integer value expected in right side of assertion'))).
1174
1175 in_junit_mode :-
1176 junit_mode(_),!.
1177
1178 pa_junit(Name,Verdict,Junit) :-
1179 b_machine_name(MachineName),
1180 create_junit_result(Name,predicate_analysis,MachineName,0,Verdict,Junit).