1 % (c) 2009-2024 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(probsrc(bsyntaxtree)).
20 :- use_module(probsrc(btypechecker)).
21 :- use_module(probsrc(preferences)).
22 :- use_module(probsrc(tools)).
23 :- use_module(probsrc(translate)).
24 :- use_module(probsrc(bmachine)).
25 :- use_module(probsrc(b_global_sets), [is_b_global_constant/3,b_global_set_cardinality/2]).
26
27 :- use_module(probsrc(error_manager)).
28 :- use_module(probsrc(self_check)).
29 :- use_module(probsrc(debug),[debug_println/2, formatsilent/2, debug_format/3]).
30
31 :- use_module(probsrc(inf_arith)).
32 :- use_module(kodkodsrc(interval_calc)).
33
34 % for interpreting value(...) components
35 :- use_module(probsrc(store), [empty_state/1]).
36 :- use_module(probsrc(b_interpreter), [b_compute_expression_nowf/6]).
37
38 :- use_module(probsrc(module_information),[module_info/2]).
39 :- module_info(group,kodkod).
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(_)),assertz(debug_constraints(Constraints)),
87 create_initial_information(Nodes,Empty),
88 apply_start_constraints(Constraints,ChangedNodes,Empty,Initial),
89 compute_maximum_iterations(Nodes,MaxIter),
90 start_ms_timer(Timer4),
91 do_analysis(ChangedNodes,2,MaxIter,CTree,Initial,Values),
92 (debug:debug_mode(on) -> stop_ms_timer_with_msg(Timer4,do_analysis(MaxIter)) ; true),
93 start_ms_timer(Timer5),
94 insert_info(Infos,Values),
95 (debug:debug_mode(on) -> stop_ms_timer_with_msg(Timer5,insert_info) ; true).
96
97 create_node_environment(ROIdentifiers,Identifiers,NewIdentifiers,NodeEnv,Infos) :-
98 initial_node_env(InitNodeEnv),
99 create_identifier_nodes2(ROIdentifiers,_,InitNodeEnv,RONodeEnv1,Infos1,([],[])),
100 identifier_nodes_readonly(RONodeEnv1,RONodeEnv),
101 create_identifier_nodes2(Identifiers,NewIdentifiers,RONodeEnv,NodeEnv,Infos2,Infos1),
102 foldl(add_id_basic_constraint(NodeEnv),Identifiers,Infos,Infos2).
103 add_id_basic_constraint(NodeEnv,Identifier,In,Out) :-
104 get_texpr_id(Identifier,Id),
105 identifier_node_lookup(Id,NodeEnv,_NodeId,Nodes,_Mode),
106 add_type_constraints(Identifier,Nodes,In,Out).
107
108 % just a heuristic to avoid a non-terminating run
109 compute_maximum_iterations(Nodes,MaxIter) :-
110 length(Nodes,N), MaxIter is N*10.
111
112 reset_nodecounter :-
113 retractall(nodecounter(_)),assertz(nodecounter(1)),
114 retractall(debug_node(_,_)),
115 retractall(debug_constraints(_)).
116
117 % create a numbered node for each identifier
118 initial_node_env(NodeEnv) :- empty_avl(NodeEnv).
119
120 %create_identifier_nodes(Ids,NIds,Env,IIn,IOut) :-
121 % empty_avl(Start), create_identifier_nodes2(Ids,NIds,Start,Env,IIn,IOut).
122
123 create_identifier_nodes2([],[],Env,Env,Info,Info).
124 create_identifier_nodes2([TId|Rest],[NTId|NRest],In,Out,IIn,IOut) :-
125 create_identifier_nodes3(TId,NTId,In,Inter,IIn,IInter),
126 create_identifier_nodes2(Rest,NRest,Inter,Out,IInter,IOut).
127 create_identifier_nodes3(TId,NTId,In,Out,IIn,IOut) :-
128 get_texpr_id(TId,Id),
129 get_texpr_type(TId,Type),
130 new_node_scope(Type,NodeId,Nodes),
131 avl_store(Id,In,nodes(NodeId,Nodes,rw),Out),
132 texpr_add_node_infos(TId,NodeId,NTId,IIn,IOut).
133
134 % look up the node of an identifier
135 identifier_node_lookup(Id,Env,NodeId,Nodes,Mode) :-
136 (avl_fetch(Id,Env,nodes(NodeId,Nodes,Mode)) -> true
137 ; add_warning(kodkod,'Could not lookup identifier: ',Id),fail).
138
139 % set the complete environment to read-only,
140 % read-only nodes are not used for output of constraint nodes
141 identifier_nodes_readonly(OldEnv,NewEnv) :-
142 avl_map(set_readonly,OldEnv,NewEnv).
143 set_readonly(nodes(Id,Nodes,_Mode),nodes(Id,Nodes,ro)).
144
145 compute_integer_set('INTEGER',-inf,inf).
146 compute_integer_set('NATURAL',0,inf).
147 compute_integer_set('NATURAL1',1,inf).
148 compute_integer_set('INT',Min,Max) :-
149 get_preference(minint,Min),
150 get_preference(maxint,Max).
151 compute_integer_set('NAT',0,Max) :-
152 get_preference(maxint,Max).
153 compute_integer_set('NAT1',1,Max) :-
154 get_preference(maxint,Max).
155
156 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
157 % computation on information
158
159 info_conjunct(irange(X,Y),irange(A,B),Result) :-
160 Max infis max(X,A), Min infis min(Y,B),
161 normalise_irange(irange(Max,Min),Result).
162 info_disjunct(irange(X,Y),irange(A,B),Result) :-
163 ( is_bottom(irange(X,Y)) -> Result = irange(A,B)
164 ; is_bottom(irange(A,B)) -> Result = irange(X,Y)
165 ;
166 Min infis min(X,A), Max infis max(Y,B),
167 normalise_irange(irange(Min,Max),Result)).
168
169 normalise_irange(In,Result) :-
170 (is_bottom(In) -> Result=irange(0,-1) ; 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,AM2),irange(Bm,BM2),irange(Cm,CM2)) :-
213 % Cm infis min(Am,Bm), CM2 infis max(AM2,BM2).
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, assertz(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,'predicate analysis',0).
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 ; nth1(N,NodeList,Elem), nth1(N,NodeListRO,ElemRO)).
530 ignore_readonly_nodes([],[],[]).
531 ignore_readonly_nodes([Mode|MRest],[InNodes|InRest],[OutNodes|OutRest]) :-
532 ignore_readonly_nodes2(Mode,InNodes,OutNodes),
533 ignore_readonly_nodes(MRest,InRest,OutRest).
534 ignore_readonly_nodes2(ro,Nodes,Ignored) :- ignore_readonly_nodes3(Nodes,Ignored).
535 ignore_readonly_nodes2(rw,Nodes,Nodes).
536 ignore_readonly_nodes3([],[]).
537 ignore_readonly_nodes3([Scope:_Node|NRest],[Scope:ignored|IRest]) :-
538 ignore_readonly_nodes3(NRest,IRest).
539
540 nodes_for_general_sum_or_mult(Rule,TId,TPred,TExpr,TOrig,NodeEnv,Nodes,TNew,In,Out) :-
541 identifier_nodes_readonly(NodeEnv,SubEnv1),
542 create_identifier_nodes2([TId],[NTId],SubEnv1,SubEnv,In,Inter1),
543 extract_nodes_l([TId,TPred,TExpr],SubEnv,[IdNodes,_PredNodes,ExprNodes],_Modes,
544 [NTId,NTPred,NTExpr],Inter1,Inter2),
545 get_texpr_info(TOrig,Info),
546 memberchk(interval:ExprNodeId,ExprNodes),
547 memberchk(possible_values:IdId,IdNodes),
548 new_node_scope(integer,NodeId,Nodes),
549 add_node_infos(Info,NodeId,NInfo,Inter2,Inter3),
550 add_all([constraint([possible_values:IdId,interval:ExprNodeId],
551 [interval:NodeId],Rule)],
552 Inter3,Out),
553 get_texpr_expr(TOrig,OrigExpr),functor(OrigExpr,Functor,_),
554 NExpr =.. [Functor,[NTId],NTPred,NTExpr],
555 create_texpr(NExpr,integer,NInfo,TNew).
556
557 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
558 % extract constraints from syntax elements
559 % extract(NExpr,NodeId,InputNodes,OrigOutputNodes,Transition)
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). % ALWAYS FAILS !!!!! SOMETHING IS WRONG HERE; TODO: fix
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). % ALWAYS FAILS !!!!!
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],lte). % was erroneously gte, see test 2410
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 ;
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 ;
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 ; Constraints = []),
743 writetodot(Counter,[ChangedNode|CNRest],Constraints,In),
744 compute_constraints(Constraints,ChangedNodes1,[],In,Inter),
745 list_to_ord_set(ChangedNodes1,ChangedNodes),
746 ord_union(CNRest,ChangedNodes,NewChangedNodes),
747 Counter2 is Counter+1,
748 do_analysis(NewChangedNodes,Counter2,MaxIter,CTree,Inter,Out).
749 do_analysis(_ChangedNodes,Counter,MaxIter,_CTree,Values,Values) :-
750 Counter >= MaxIter.
751 compute_constraints([],Changed,Changed,Values,Values).
752 compute_constraints([Constraint|CRest],Cin,Cout,Vin,Vout) :-
753 compute_constraint(Constraint,Cin,Cinter,Vin,Vinter),
754 compute_constraints(CRest,Cinter,Cout,Vinter,Vout).
755 compute_constraint(constraint(Input,Output,Rule),Cin,Cout,Vin,Vout) :-
756 get_input_values(Input,Vin,InValues),
757 same_length(Output,OutValues),
758 call_rule(Rule,InValues,OutValues),
759 apply_output(Output,OutValues,Cin,Cout,Vin,Vout).
760 get_input_values([],_,[]).
761 get_input_values([Node|NRest],Values,[Value|VRest]) :-
762 avl_fetch(Node,Values,Value),
763 get_input_values(NRest,Values,VRest).
764 call_rule(Rule,InValues,OutValues) :-
765 append_arguments(Rule,InValues,CallRule),
766 if(rule(CallRule,OutValues), true,
767 (add_warning(predicate_analysis,'No rule for: ',CallRule),fail)).
768 apply_output([],[],Changed,Changed,Values,Values).
769 apply_output([Node|NRest],[Value|VRest],Cin,Cout,Vin,Vout) :-
770 ( Node = _:ignored ->
771 Vin = Vinter, Cin=Cinter
772 ;
773 avl_change(Node,Vin,OldValue,Vinter,NewValue),
774 info_conjunct(OldValue,Value,NewValue),
775 ( has_more_information(NewValue,OldValue) ->
776 Cin = [Node|Cinter]
777 ;
778 Cin = Cinter)),
779 apply_output(NRest,VRest,Cinter,Cout,Vinter,Vout).
780
781 append_arguments(Orig,Args,Result) :-
782 Orig =.. FA,
783 append_arguments2(FA,Args,AllArgs),
784 Result =.. AllArgs.
785 append_arguments2([F|FA],Args,AllArgs) :-
786 multi_rule(F),!,
787 append([F|FA],[Args],AllArgs).
788 append_arguments2(FA,Args,AllArgs) :-
789 append(FA,Args,AllArgs).
790
791
792 create_add_list_constraints([],_Result,Constraints) :- !,Constraints=[].
793 create_add_list_constraints([Node],Result,Constraints) :- !,
794 Constraints = [constraint([Node],[Result],id),constraint([Result],[Node],id)].
795 create_add_list_constraints(Nodes,Result,Constraints) :- !,
796 create_add_list_constraints2(Nodes,Result,Constraints,[]).
797 create_add_list_constraints2([A,B],Result) --> !,
798 findall(constraint(I,O,C),addition_constraint(A,B,Result,I,O,C)).
799 create_add_list_constraints2([A|Nodes],Result) -->
800 {new_node_scope(integer,NodeId,_Nodes)},
801 findall(constraint(I,O,C),addition_constraint(A,interval:NodeId,Result,I,O,C)),
802 create_add_list_constraints2(Nodes,interval:NodeId).
803
804
805
806 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
807 % compute a single constraint
808
809 rule(id(Info),[Info]).
810 rule(constant(Info),[Info]).
811 rule(add(A,B),[C]) :- interval_addition(A,B,C).
812 rule(sub(A,B),[C]) :- interval_subtraction(A,B,C).
813 rule(mult(A,B),[C]) :- interval_multiplication(A,B,C).
814 rule(power(Base,Exp),[Result]) :-
815 (power_rule(Base,Exp,Result) -> true ; Result = irange(-inf,inf)).
816 rule(cart_div(irange(Cm,CM2),irange(Am,AM2)),[irange(Bm,BM2)]) :- % propagates C = A * B
817 ( CM2 = inf -> % C can be infinite
818 BM2 = inf
819 ; Am = 0, % the set A can be empty
820 number(Cm),Cm<1 -> % set C can be empty
821 BM2 = inf % B can be arbitrarily larger and still yield an empty A*B
822 ; Am = 0 -> % case: set C cannot be empty, actually Am is 1
823 BM2 = CM2 % B can be as large as the maximum size of C
824 ; Am = inf -> % A is infinite
825 BM2 = 0 % as CM2 is not inf, C cannot be infinite, only solution is B to be empty
826 ;
827 BM2 infis (CM2+Am-1) // Am % e.g., for CM2=3 and Am=2 we obtain 2 ?? Shouldnt we use CM2 // Am ??
828 ),
829 ( number(Cm),Cm<1 -> % set C can be empty
830 Bm = 0 % B can also be empty
831 ; AM2 = 0 -> % A has to be empty; this means C is empty and we have a contradiction
832 Bm is -1 % try force contradiction
833 ; AM2 = inf -> % A can be infinite, but C is definitely not empty
834 Bm = 1 % B must have at least one element
835 ; Cm = inf -> Bm = inf % C has to be infinite, but A is finite
836 ; AM2 > Cm -> Bm = 1 % B must have at least one element, as C is not empty
837 ;
838 Bm infis Cm // AM2). % minimal size of B is C divided by maximal size of A
839 % TO DO: can be improved, e.g., for :kodkod A = {1,2} & avl = {1|->2, 1|->3, 2|->2, 2|->3, 4|->2} & avl = A*B
840 % print(rule_result(cart_div(irange(Cm,CM2),irange(Am,AM2)),[irange(Bm,BM2)])),nl.
841 rule(negate(A),[C]) :- interval_negation(A,C).
842 rule(lt(irange(_,AM2)),[irange(-inf,CM2)]) :- CM2 infis AM2-1.
843 rule(lte(irange(_,AM2)),[irange(-inf,AM2)]).
844 rule(gt(irange(Am,_)),[irange(Cm,inf)]) :- Cm infis Am+1.
845 rule(gte(irange(Am,_)),[irange(Am,inf)]).
846 rule(interval_card(irange(Am,AM2),irange(Bm,BM2)),[irange(Cm,CM2)]) :-
847 Cm infis max(Bm-AM2+1,0), CM2 infis max(BM2-Am+1,0).
848 rule(interval_possibilities(irange(Am,AM2)),[irange(0,M)]) :-
849 M infis max(AM2-Am+1,0).
850 rule(union([A|Rest]),[C]) :- union2(Rest,A,C).
851 rule(union([]),[irange(-inf,inf)]) :- print(empty_kodkod_union_constraint),nl. % TO DO: check if ok !!
852 % happens e.g., for set extensions like { min(0..7) } ; should not occur
853 rule(divrange(irange(Am,AM2),irange(Bm,BM2)),[C]) :- C = irange(MinC,MaxC),
854 div_rule(Am,AM2,Bm,BM2,MinC,MaxC),
855 format('Division propagation: ~w..~w / ~w..~w ---> ~w~n',[Am,AM2,Bm,BM2,C]).
856 rule(concat_index(irange(_,AM2),irange(_,BM2)),[irange(1,CM2)]) :-
857 (number(AM2),number(BM2) -> CM2 is AM2+BM2 ; CM2 = inf).
858
859
860 :- assert_must_succeed((div_rule(1,100,1,2,MinC,MaxC), MinC == 0, MaxC == 100)).
861 :- assert_must_succeed((div_rule(100,200,1,50,MinC,MaxC), MinC == 2, MaxC == 200)).
862 :- assert_must_succeed((div_rule(-2,100,1,50,MinC,MaxC), MinC == -2, MaxC == 100)).
863 :- assert_must_succeed((div_rule(-2,100,0,50,MinC,MaxC), MinC == -2, MaxC == 100)). % with try_find_abort we would get:
864 % :- assert_must_succeed((div_rule(-2,100,0,50,MinC,MaxC), MinC == -inf, MaxC == inf)). % above with try_find_abort
865
866 % propagate division Am..AM2 / Bm..BM2 --> C
867 div_rule(Am,AM2,Bm,BM2,MinC,MaxC) :- number(Bm), Bm >= 0,!, % division by positive number
868 (Am = -inf -> MinC = -inf
869 ; Am < 0, Bm=0 -> MinC = Am % if try_find_abort is true we could set MinC to -inf; here we suppose division by 0 will not occur or not produce a value
870 ; Am < 0 -> MinC is Am // Bm % a possible lowest value is obtained by the largest absolute (neg) value
871 ; BM2=inf -> MinC is 0 % Am is positive ; we can divide Am by an arbitrarily large number to reach 0
872 ; MinC is Am // BM2 % minimum is dividing min of A by maximum of B
873 ),
874 (AM2 = inf -> MaxC = inf
875 ; BM2=inf, AM2<0 -> MinC is 0 % Am is positive ; we can divide Am by an arbitrarily large number to reach 0
876 ; AM2 < 0 -> MaxC is AM2 // BM2 % a possible max value is obtained by the smallest absolute (neg) value
877 ; Bm=0 -> MaxC = AM2 % if try_find_abort is true we could set MaxC to inf
878 ; MaxC is AM2 // Bm % maximum is dividing max of A by min of B
879 ).
880 div_rule(Am,AM2,_Bm,_BM2,MinC,MaxC) :- number(Am),number(AM2), !, % TODO: improve and check
881 M is max(abs(Am),abs(AM2)), % if try_find_abort is true we could divide by 0 and set to -inf .. inf
882 MinC is -M, MaxC = M.
883 div_rule(_,_,_,_,-inf,inf).
884
885 union2([],Acc,R) :- !,Acc=R.
886 union2([H|T],Acc,R) :- info_disjunct(H,Acc,I),union2(T,I,R).
887
888 power_rule(irange(Basem,BaseM2),irange(Expm,ExpM2),irange(Rm,RM2)) :-
889 number(Basem),Basem > 0,
890 number(Expm),Expm >= 0,
891 Rm is floor( Basem ** Expm),
892 ((BaseM2=inf; ExpM2=inf) -> RM2=inf ; RM2 is floor(BaseM2 ** ExpM2)).
893
894 multi_rule(union).
895
896
897 insert_info(Infonodes,Values) :-
898 create_info_lookup_table(Values,ValueTable),
899 insert_info2(Infonodes,ValueTable).
900 insert_info2([],_).
901 insert_info2([infonode(Node,New,Old)|Rest],Values) :-
902 find_all_node_information(Node,Values,Info),
903 add_info(Info,Old,New),
904 insert_info2(Rest,Values).
905
906 :- load_files(library(system), [when(compile_time), imports([environ/2])]).
907 :- if(environ(prob_safe_mode,true)).
908 find_all_node_information(Node,_,_) :- \+ ground(Node), add_internal_error('Non-ground node: ',Node),fail.
909 :- endif.
910 find_all_node_information(Node,Values,Info) :-
911 ( avl_fetch(Node,Values,RawInfos) -> filter_tops(RawInfos,Info) % used to call avl_member; but Node is ground
912 ; Info = []).
913
914 filter_tops([],[]).
915 filter_tops([Scope:RI|RRest],Result) :-
916 get_scope_top(Scope,Top),
917 ( has_more_information(RI,Top) -> Result = [Scope:RI|RestResult]
918 ; Result = RestResult),
919 filter_tops(RRest,RestResult).
920
921 add_info([],Old,Old) :- !.
922 add_info(Info,Old,[analysis(Info)|Old]).
923
924 create_info_lookup_table(Values,Nodes) :-
925 avl_to_list(Values,ValueList),
926 empty_avl(Empty),
927 create_info_lookup_table2(ValueList,Empty,Nodes).
928 create_info_lookup_table2([],Nodes,Nodes).
929 create_info_lookup_table2([(Scope:Node)-I|Rest],In,Out) :-
930 (avl_fetch(Node,In,Old) -> true ; Old = []),
931 avl_store(Node,In,[Scope:I|Old],Inter),
932 create_info_lookup_table2(Rest,Inter,Out).
933
934 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
935 % dot output
936
937 writetodot(Counter,ChangedNodes,ActiveConstraints,Values) :-
938 debugging_enabled,!,
939 debug_constraints(Constraints),
940 writetodot(Counter,Constraints,ChangedNodes,ActiveConstraints,Values).
941 writetodot(_Counter,_ChangedNodes,_ActiveConstraints,_Values).
942
943 writetodot(Counter,Constraints,ChangedNodes,ActiveConstraints,Values) :-
944 printnulls(3,Counter,CStr),
945 ajoin(['debug-constraint-anim/constraints-',CStr,'.dot'],Filename),
946 debug_format(19,'Writing Kodkod constraints to dot file: ~w~n',[Filename]),
947 open(Filename,write,S),
948 write(S,'digraph constraints {\n'),
949 get_nodes(Constraints,INodes,CNodes),
950 write_inodes(INodes,ChangedNodes,Values,S),
951 write_cnodes(CNodes,INodes,ActiveConstraints,S),
952 write(S,'}\n'),
953 close(S).
954 get_nodes(Constraints,INodes,RNodes) :-
955 findall(CN,
956 (member(constraint(I,O,_),Constraints),
957 (member(CN,I);member(CN,O))),
958 Nodes1),
959 sort(Nodes1,Nodes2),
960 number_nodes(Nodes2,1,INodes),
961 number_nodes(Constraints,1,RNodes).
962 number_nodes([],_,[]).
963 number_nodes([N|Rest],Nr,[node(Nr,N)|RRest]) :-
964 Nr2 is Nr+1,
965 number_nodes(Rest,Nr2,RRest).
966 write_inodes([],_,_,_).
967 write_inodes([node(Nr,N)|Rest],ChangedNodes,Values,S) :-
968 write(S,' i'),write(S,Nr),write(S,'[shape=box,'),
969 ( ChangedNodes = [N|_] ->
970 !,write(S,'fillcolor=green,style=filled,')
971 ; member(N,ChangedNodes) ->
972 !,write(S,'fillcolor=yellow,style=filled,')
973 ; true),
974 write(S,'label=\"'),
975 write(S,N),write(S,'\\n'),
976 ( debug_node(N,TExpr) ->
977 translate_bexpression(TExpr,Text)
978 ;
979 Text = ('?')),
980 write(S,Text),write(S,'\\n'),
981 ( avl_fetch(N,Values,Value) ->
982 write(S,Value)
983 ; write(S,'?')),
984 write(S,'\"];\n'),
985 write_inodes(Rest,ChangedNodes,Values,S).
986 write_cnodes([],_,_,_).
987 write_cnodes([node(Nr,constraint(In,Out,R))|Rest],INodes,ActiveConstraints,S) :-
988 write(S,' c'),write(S,Nr),write(S,'[shape=diamond,'),
989 ( member(constraint(In,Out,R),ActiveConstraints) ->
990 !,write(S,'fillcolor=lightblue,style=filled,')
991 ; true),
992 write(S,'label=\"'),
993 write(S,R),write(S,'\"];\n'),
994 write_inputs(In,INodes,Nr,S),
995 write_outputs(Out,INodes,Nr,S),
996 write_cnodes(Rest,INodes,ActiveConstraints,S).
997 write_inputs([],_,_,_).
998 write_inputs([I|Rest],INodes,RNr,S) :-
999 member(node(INr,I),INodes),!,
1000 write(S,' i'),write(S,INr),write(S,' -> '),
1001 write(S,' c'),write(S,RNr),write(S,';\n'),
1002 write_inputs(Rest,INodes,RNr,S).
1003 write_outputs([],_,_,_).
1004 write_outputs([O|Rest],INodes,RNr,S) :-
1005 member(node(ONr,O),INodes),!,
1006 write(S,' c'),write(S,RNr),write(S,' -> '),
1007 write(S,' i'),write(S,ONr),write(S,';\n'),
1008 write_outputs(Rest,INodes,RNr,S).
1009
1010 printnulls(N,Number,Str) :-
1011 number_codes(Number,NCodes),
1012 length(NCodes,L),
1013 R is N-L,
1014 fillnulls(R,NCodes,Codes),
1015 atom_codes(Str,Codes).
1016 fillnulls(R,Codes,Codes) :- R =< 0,!.
1017 fillnulls(R,NCodes,[48|Codes]) :-
1018 R2 is R-1,
1019 fillnulls(R2,NCodes,Codes).
1020
1021
1022 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1023
1024 :- dynamic debug_predicate_analysis/0.
1025 % assert this fact to enable dot visualization using writetodot below
1026
1027 store_debug_info(I,T) :- debug_predicate_analysis,!,store_debug_info2(I,T).
1028 store_debug_info(_,_).
1029
1030 store_debug_info2([],_).
1031 store_debug_info2([Node|Rest],TExpr) :-
1032 assertz(debug_node(Node,TExpr)),
1033 store_debug_info2(Rest,TExpr).
1034
1035
1036 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1037 % test case specification for predicate analysis
1038
1039 :- use_module(probsrc(junit_tests)).
1040
1041 test_predicate_analysis :-
1042 b_machine_has_constants,
1043 b_get_machine_constants(Constants),
1044 b_get_properties_from_machine(Properties),
1045 b_get_static_assertions_from_machine(Assertions),
1046 Assertions = [_|_],!,
1047 test_predicate_analysis2(Constants,Properties,Assertions,Junits),
1048 b_machine_name(MachineName),
1049 print_junit([predicate_analysis,MachineName], Junits).
1050 test_predicate_analysis :-
1051 add_error(predicate_analysis,
1052 'Machine must have constants and assertions to be a valid predicate analysis test case.'),
1053 fail.
1054 test_predicate_analysis2(Constants,Properties,Assertions,Junits) :-
1055 predicate_analysis_with_global_sets(Properties,[],Constants,TaggedConstants,_TaggedProperties),!,
1056 test_predicate_analysis3(Assertions,TaggedConstants,Junits).
1057 test_predicate_analysis2(_,_,_,[Junit]) :-
1058 in_junit_mode,!,
1059 pa_junit(setup,error('Predicate analysis failed'),Junit).
1060 test_predicate_analysis2(_,_,_,_) :-
1061 add_error(predicate_analysis, 'Predicate analysis of properties failed'),
1062 fail.
1063 test_predicate_analysis3([],_Constants,[]).
1064 test_predicate_analysis3([Assertion|Arest],Constants,[Junit|Jrest]) :-
1065 test_predicate_analysis_testcase(Assertion,Constants,Junit),
1066 test_predicate_analysis3(Arest,Constants,Jrest).
1067
1068 test_predicate_analysis_testcase(TAssertion,Constants,Junit) :-
1069 translate_bexpression(TAssertion,Name),
1070 ( catch( ( test_predicate_analysis_testcase1(TAssertion,Constants),
1071 pa_junit(Name,pass,Junit)),
1072 junit(Msg),
1073 handle_error(Name,Msg,Junit)) -> true
1074 ;
1075 pa_junit(Name,error('Test failed.'),Junit)).
1076 handle_error(Name,Msg,Junit) :-
1077 in_junit_mode,!,
1078 pa_junit(Name,error(Msg),Junit).
1079 handle_error(Name,Msg,_Junit) :-
1080 add_error(predicate_analysis, Name, Msg), fail.
1081
1082 test_predicate_analysis_testcase1(TAssertion,Constants) :-
1083 extract_predicate_analysis_testcases(TAssertion,[],Testcases),
1084 maplist(run_testcase(Constants),Testcases).
1085 run_testcase(Constants,pa_testcase(Id,Scope,ExpectedValue)) :-
1086 formatsilent('Predicate analysis testcase: check if "~w" of "~w" is ~w~N', [Scope,Id,ExpectedValue]),
1087 expected_value_is_top(ExpectedValue,Scope,IsTop),
1088 extract_analysis(Id,Constants,Scope,FoundValue,IsTop),
1089 ( FoundValue == ExpectedValue -> true
1090 ;
1091 ajoin(['unexpected information for constant ',Id,': expected ', ExpectedValue,
1092 ', but was ', FoundValue],Msg),
1093 throw(junit(Msg))).
1094 expected_value_is_top(ExpectedValue,Scope,true) :-
1095 ground(ExpectedValue),get_scope_top(Scope,ExpectedValue),!.
1096 expected_value_is_top(_ExpectedValue,_Scope,false).
1097
1098 extract_predicate_analysis_testcases(TExpr,QRefs,Testcases) :-
1099 get_texpr_expr(TExpr,Expr),
1100 extract_predicate_analysis_testcases2(Expr,QRefs,Testcases).
1101 extract_predicate_analysis_testcases2(forall(Ids,TPre,Cond),QRefs,Testcases) :-
1102 get_texpr_expr(TPre,Pre),introduce_qrefs(Pre,Ids,QRefs,SubQRefs),
1103 extract_predicate_analysis_testcases(Cond,SubQRefs,Testcases).
1104 extract_predicate_analysis_testcases2(equal(Ref,Spec),QRefs,[pa_testcase(Constant,Scope,irange(Value,Value))]) :- !,
1105 extract_test_reference(Ref,QRefs,Constant,Scope),
1106 extract_integer(Spec,Value).
1107 extract_predicate_analysis_testcases2(less_equal(Ref,Spec),QRefs,[pa_testcase(Constant,Scope,irange(-inf,Value))]) :- !,
1108 extract_test_reference(Ref,QRefs,Constant,Scope),
1109 extract_integer(Spec,Value).
1110 extract_predicate_analysis_testcases2(greater_equal(Ref,Spec),QRefs,[pa_testcase(Constant,Scope,irange(Value,inf))]) :- !,
1111 extract_test_reference(Ref,QRefs,Constant,Scope),
1112 extract_integer(Spec,Value).
1113 extract_predicate_analysis_testcases2(member(Ref,TSpec),QRefs,[pa_testcase(Constant,Scope,Info)]) :-
1114 get_texpr_expr(TSpec,Spec),
1115 extract_test_reference(Ref,QRefs,Constant,Scope),
1116 integer_interval_membership(Spec,Scope,Info),!.
1117 extract_predicate_analysis_testcases2(not_member(Ref,TSpec),QRefs,[pa_testcase(Constant,Scope,Top)]) :-
1118 get_texpr_expr(TSpec,empty_set),
1119 extract_test_reference(Ref,QRefs,Constant,Scope),
1120 get_scope_top(Scope,Top).
1121 extract_predicate_analysis_testcases2(conjunct(A,B),QRefs,Testcases) :- !,
1122 extract_predicate_analysis_testcases(A,QRefs,TestcasesA),
1123 extract_predicate_analysis_testcases(B,QRefs,TestcasesB),
1124 append(TestcasesA,TestcasesB,Testcases).
1125 extract_predicate_analysis_testcases2(_Expr,_QRefs,_Testcases) :-
1126 throw(junit('Unsupported test case pattern')).
1127
1128 integer_interval_membership(empty_set,Scope,Bottom) :- get_scope_bottom(Scope,Bottom).
1129 integer_interval_membership(interval(Min,Max),_Scope,irange(VMin,VMax)) :-
1130 extract_integer(Min,VMin),extract_integer(Max,VMax).
1131 integer_interval_membership(integer_set(I),_Scope,irange(Min,Max)) :-
1132 compute_integer_set(I,Min,Max).
1133
1134 introduce_qrefs(member(E2,Ref),Ids,QRefs,NewQRefs) :-
1135 lookup_ref(Ref,QRefs,Path,Constant),append(Path,[elem],Path2),
1136 couple_pathes(E2,Path2,Constant,Ids,NewQRefs,QRefs).
1137 introduce_qrefs(equal(E2,Ref),Ids,QRefs,NewQRefs) :-
1138 lookup_ref(Ref,QRefs,Path,Constant),
1139 couple_pathes(E2,Path,Constant,Ids,NewQRefs,QRefs).
1140
1141 couple_pathes(TExpr,Path,Constant,TIdentifiers) -->
1142 {get_texpr_ids(TIdentifiers,Identifiers)},
1143 couple_pathes2(TExpr,Path,Constant,Identifiers).
1144 couple_pathes2(TExpr,Path,Constant,Identifiers) -->
1145 {get_texpr_expr(TExpr,Expr)},couple_pathes3(Expr,TExpr,Path,Constant,Identifiers).
1146 couple_pathes3(identifier(Id),_,Path,Constant,Identifiers) -->
1147 {memberchk(Id,Identifiers),!},[Id-ref(Path,Constant)].
1148 couple_pathes3(couple(A,B),_,Path,Constant,Identifiers) --> !,
1149 {append(Path,[left],PathA),append(Path,[right],PathB)},
1150 couple_pathes2(A,PathA,Constant,Identifiers),
1151 couple_pathes2(B,PathB,Constant,Identifiers).
1152 couple_pathes3(_,TExpr,_Path,_Constant,_Identifiers,_,_) :-
1153 translate_bexpression(TExpr,String),
1154 atom_concat('Expected quantified identifier or couple in test specification, but was: ',
1155 String,Msg),
1156 throw(junit(Msg)).
1157
1158
1159 lookup_ref(Expr,QRefs,Path,Constant) :-
1160 get_texpr_id(Expr,Id),
1161 memberchk(Id-ref(Path1,Constant1),QRefs),!,
1162 Path1=Path,Constant1=Constant.
1163 lookup_ref(Expr,_QRefs,[],Id) :-
1164 get_texpr_id(Expr,Id).
1165
1166 extract_analysis(Id,Constants,Scope,FoundValue,IsTop) :-
1167 get_texpr_id(Constant,Id),
1168 ( memberchk(Constant,Constants) ->
1169 get_texpr_info(Constant,Info),
1170 (memberchk(analysis(Analysis),Info) -> true ; Analysis=[]),
1171 ( memberchk(Scope:FoundValue,Analysis) -> true
1172 ; IsTop == true -> get_scope_top(Scope,FoundValue) /* no information expected */
1173 ;
1174 ajoin(['No analysis information found for constant ',Id,', scope ',Scope],Msg),
1175 print(extract_analysis(Id,Constants,Scope,FoundValue,IsTop)),nl,
1176 throw(junit(Msg)))
1177 ;
1178 ajoin(['Constant ',Id,' not found'],Msg),
1179 throw(junit(Msg))).
1180
1181 extract_test_reference(TRef,QRefs,Id,Scope) :-
1182 get_texpr_expr(TRef,Ref),
1183 extract_test_reference2(Ref,QRefs,BaseScope,Id,ScopeList),
1184 construct_scope(ScopeList,BaseScope,Scope),!.
1185 extract_test_reference(TRef,_QRefs,_Id,_Scope) :-
1186 translate_bexpression(TRef,String),
1187 atom_concat('Unsupported reference on left side: ', String,Msg),
1188 throw(junit(Msg)).
1189 extract_test_reference2(card(TRef),QRefs,card,Id,Scope) :-
1190 !,get_texpr_expr(TRef,Ref),Ref=identifier(_),
1191 extract_test_reference2(Ref,QRefs,_,Id,Scope).
1192 extract_test_reference2(identifier(Id),QRefs,interval,Constant,Scope) :-
1193 memberchk(Id-ref(Scope,Constant),QRefs),!.
1194 extract_test_reference2(identifier(Id),_QRefs,interval,Id,[]) :- !.
1195
1196
1197 :- assert_must_succeed(( construct_scope([elem,right],interval,S),
1198 S==elem(right(interval)) )).
1199 construct_scope([],Scope,Scope).
1200 construct_scope([F|Rest],Base,Scope) :-
1201 functor(Scope,F,1),arg(1,Scope,Subscope),
1202 construct_scope(Rest,Base,Subscope).
1203
1204 extract_integer(Spec,Value) :-
1205 ( get_texpr_expr(Spec,integer(Value)) -> true
1206 ; get_texpr_expr(Spec,unary_minus(Spec2)) ->
1207 extract_integer(Spec2,Value2),Value is -Value2
1208 ;
1209 throw(junit('Integer value expected in right side of assertion'))).
1210
1211 in_junit_mode :-
1212 junit_mode(_),!.
1213
1214 pa_junit(Name,Verdict,Junit) :-
1215 create_junit_result(Name,0,Verdict,Junit).