1 | % (c) 2014-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 | :- module(clpfd_lists,[ | |
6 | lazy_fd_value_check/4, | |
7 | try_in_fd_value_list_check/4, | |
8 | avl_fd_value_check/4, | |
9 | in_fd_value_list_wf/4, in_fd_value_list_wf/3, | |
10 | get_fd_value_list/5, | |
11 | try_get_fd_value_list/4, | |
12 | get_fd_value/3, | |
13 | ||
14 | get_fdset_information/2, get_finite_fdset_information/2, | |
15 | combine_fdset_information/3, | |
16 | assert_fdset_information/2 | |
17 | ]). | |
18 | ||
19 | :- use_module(preferences). | |
20 | :- use_module(error_manager). | |
21 | ||
22 | :- use_module(module_information,[module_info/2]). | |
23 | :- module_info(group,kernel). | |
24 | :- module_info(description,'This module provides a way to call the CLPFD clpfd_inlist and clpfd:element predicates for lists of FD values.'). | |
25 | ||
26 | % ----------------------------------------------------- | |
27 | ||
28 | :- block complete_type(-). | |
29 | % check if we have a complete value type or if we extract only part of the value | |
30 | complete_type(global(_)). | |
31 | complete_type(integer). | |
32 | %complete_type(couple(A,B)) :- fail. % THIS IS NOT COMPLETE !! -> the element checks are done for each component in isolation ! (there is no guarantee that the pair is in the list of pairs) | |
33 | ||
34 | % ----------------------------------------------------- | |
35 | ||
36 | :- block try_in_fd_value_list_check(-,?,?,?). | |
37 | try_in_fd_value_list_check([],_,_,_) :- fail. | |
38 | try_in_fd_value_list_check([H|T],El,Type,WF) :- | |
39 | try_get_fd_value_type(El,H,Type), % print(fd_val_type(El,H,T)),nl, | |
40 | !, | |
41 | get_fd_value_list([H|T],Type,FDList,GroundList,El), | |
42 | get_fd_value(Type,El,ElFD), | |
43 | in_fd_value_list_wf(GroundList,ElFD,FDList,WF). | |
44 | try_in_fd_value_list_check(S,El,Type,WF) :- | |
45 | (S=[_|_] -> true | |
46 | ; add_internal_error('Illegal call: ',try_in_fd_value_list_check(S,El,Type,WF))). | |
47 | ||
48 | :- use_module(kernel_freetypes,[get_freeval_type/3]). | |
49 | ||
50 | % try get fd_value type from two values; one from element one from set | |
51 | try_get_fd_value_type(V1,V2,T) :- var(V1),!, try_get_fd_value(T,V2,_). | |
52 | try_get_fd_value_type((A1,_),(A2,_),T) :- nonvar(T),T=couple_left(TA),!, | |
53 | try_get_fd_value_type(A1,A2,TA). % useful for domain checks | |
54 | try_get_fd_value_type((A1,B1),(A2,B2),Type) :- !, | |
55 | (try_get_fd_value_type(A1,A2,TA) | |
56 | -> (try_get_fd_value_type(B1,B2,TB) | |
57 | -> Type = couple(TA,TB) % we have two values | |
58 | ; %print(failed_right(B1,B2,TB)),nl, | |
59 | Type = couple_left(TA)) | |
60 | ; Type = couple_right(TB), | |
61 | try_get_fd_value_type(B1,B2,TB)). | |
62 | try_get_fd_value_type(freeval(ID,Case,Val1),V2, freeval_case(ID,Case,Type)) :- !, | |
63 | nonvar(Case), ground(ID), | |
64 | ((nonvar(V2),V2 = freeval(ID2,Case2,Val2), (ID,Case) == (ID2,Case2)) | |
65 | -> try_get_fd_value_type(Val1,Val2,Type) | |
66 | ; % TO DO: if the cases do not match and if Val1 is a variable then we may miss FD values | |
67 | %print(case_mismatch(ID,Case,V2)),nl, | |
68 | get_freeval_type(ID,Case,CaseType), | |
69 | print(freeval_case_type(ID,Case,CaseType,Val1)),nl, | |
70 | % TO DO: use CaseType in case Val1 is not instantiated enough | |
71 | try_get_fd_value(Type,Val1,_)). | |
72 | try_get_fd_value_type(V1,V2,T) :- | |
73 | (var(V2) -> try_get_fd_value(T,V1,_) ; try_get_fd_value(T,V2,_)). | |
74 | ||
75 | % TO DO: merge both versions of try_get_fd_value | |
76 | try_get_fd_value(T,V,_) :- var(V),var(T),!,fail. | |
77 | try_get_fd_value(TV,(A,B),FD) :- var(TV),!, | |
78 | (try_get_fd_value(TA,A,FDA) | |
79 | -> (try_get_fd_value(TB,B,FDB) % What if this fails because B is still a variable !? --> ideally, typing info would help | |
80 | -> TV = couple(TA,TB), FD=fd_pair(FDA,FDB) % we have two values | |
81 | ; TV = couple_left(TA), FD=FDA) | |
82 | ; TV = couple_right(TB), | |
83 | try_get_fd_value(TB,B,FD)). | |
84 | try_get_fd_value(couple(TA,TB),(A,B),fd_pair(FDA,FDB)) :- | |
85 | try_get_fd_value(TA,A,FDA),try_get_fd_value(TB,B,FDB). | |
86 | try_get_fd_value(couple_left(T),(A,_),FDA) :- try_get_fd_value(T,A,FDA). % useful for domain checks | |
87 | % to do: try and get both and do multiple in_list/element calls later | |
88 | try_get_fd_value(couple_right(T),(_,A),FDA) :- try_get_fd_value(T,A,FDA). % useful for range checks | |
89 | try_get_fd_value(rec_fields(T),rec(Fields),fd_rec(FD)) :- | |
90 | try_get_field_fd_values(T,Fields,FD), | |
91 | T \= []. | |
92 | try_get_fd_value(global(GS),fd(X,GS),X). | |
93 | try_get_fd_value(integer,int(X),X). | |
94 | ||
95 | try_get_field_fd_values(_,Fields,_) :- var(Fields),!,fail. % we don't know all the fields; better do nothing | |
96 | try_get_field_fd_values(Types,[],FDVals) :- !,Types=[],FDVals=[]. | |
97 | try_get_field_fd_values(_,[FN|_],_) :- var(FN),!,fail. | |
98 | try_get_field_fd_values(_,[field(Name,_)|_],_) :- var(Name),!,fail. | |
99 | try_get_field_fd_values(Types,[field(Name,Value)|TF],FDVals) :- | |
100 | try_get_fd_value(T,Value,FDVal), | |
101 | !, | |
102 | Types = [field(Name,T)|TTypes], | |
103 | FDVals = [FDVal|TFD], | |
104 | try_get_field_fd_values(TTypes,TF,TFD). | |
105 | try_get_field_fd_values([field_no_fd(Name)|Types],[field(Name,_)|TF],FDVals) :- | |
106 | try_get_field_fd_values(Types,TF,FDVals). | |
107 | ||
108 | ||
109 | :- use_module(b_global_sets,[get_global_type_value/3]). | |
110 | ||
111 | get_fd_value(T,V,E) :- var(V),var(T),!, | |
112 | add_internal_error('Variable arguments: ',get_fd_value(V,T,E)),fail. | |
113 | get_fd_value(couple(TA,TB),(A,B),fd_pair(FDA,FDB)) :- | |
114 | get_fd_value(TA,A,FDA), get_fd_value(TB,B,FDB). | |
115 | get_fd_value(couple_left(T),(A,_),FDA) :- get_fd_value(T,A,FDA). | |
116 | get_fd_value(couple_right(T),(_,A),FDA) :- get_fd_value(T,A,FDA). | |
117 | get_fd_value(rec_fields(T),rec(Fields),fd_rec(FD)) :- get_field_fd_values(T,Fields,FD). | |
118 | get_fd_value(freeval_case(ID,Case,T),freeval(ID,Case,A),FDA) :- | |
119 | get_fd_value(T,A,FDA). | |
120 | get_fd_value(global(GS),FD,X) :- get_global_type_value(FD,GS,X). | |
121 | get_fd_value(integer,int(X),X). | |
122 | ||
123 | get_field_fd_values([],[],[]). | |
124 | get_field_fd_values([field_no_fd(Name)|TTypes],[field(Name,_)|TF],FDVals) :- | |
125 | get_field_fd_values(TTypes,TF,FDVals). | |
126 | get_field_fd_values([field(Name,T)|TTypes],[field(Name,Value)|TF],[FDVal|TFD]) :- | |
127 | get_fd_value(T,Value,FDVal), | |
128 | get_field_fd_values(TTypes,TF,TFD). | |
129 | ||
130 | :- use_module(library(lists),[maplist/2]). | |
131 | check_valid_fd_type(Type) :- | |
132 | (valid_type(Type) -> true | |
133 | ; add_internal_error('Illegal FD type:',check_valid_fd_type(Type)),fail). | |
134 | valid_type(couple(TA,TB)) :- valid_type(TA),valid_type(TB). | |
135 | valid_type(couple_left(TA)) :- valid_type(TA). | |
136 | valid_type(couple_right(TA)) :- valid_type(TA). | |
137 | valid_type(global(GS)) :- atomic(GS). | |
138 | valid_type(rec_fields(Fs)) :- maplist(valid_rec_type,Fs). | |
139 | valid_type(freeval_case(_,_,TA)) :- valid_type(TA). | |
140 | valid_type(integer). | |
141 | ||
142 | valid_rec_type(field(_,T)) :- valid_type(T). | |
143 | valid_rec_type(field_no_fd(_)). | |
144 | ||
145 | % ----------------------------------------------------- | |
146 | ||
147 | % extract from a Set (List) representation a list of FDValues which can be used by the clpfd library | |
148 | try_get_fd_value_list(Set,Type,FDList,GroundList) :- | |
149 | try_get_fd_value_list5(Set,Type,FDList,true,GroundList). | |
150 | try_get_fd_value_list5(V,_,_,_,_) :- var(V),!,fail. | |
151 | try_get_fd_value_list5([],_,[],G,G). | |
152 | try_get_fd_value_list5([H|T],Type,FDList,GroundIn,GroundOut) :- | |
153 | try_get_fd_value(Type,H,Value),!, FDH=Value, | |
154 | (GroundIn=true,number(FDH) -> Ground1=true ; Ground1=false), | |
155 | %%(ground(T) -> true ; print(get_fd_val_list(H,T)),nl), %% | |
156 | check_non_var_list(T), % do not instantiate any free variable ! otherwise we get full domain probably anyway ? | |
157 | if(get_fd_value_list5(T,Type,FDT,Ground1,GroundOut,_NoFilterEl,1), | |
158 | FDList = [FDH|FDT], | |
159 | FDList = []). % obtaining FD value triggered inconsistency | |
160 | try_get_fd_value_list5(avl_set(A),_,_,_,_) :- | |
161 | add_internal_error('Not a list: ',try_get_fd_value_list5(avl_set(A),_,_,_,_)),fail. | |
162 | try_get_fd_value_list5(node(A,B,C,D,E),_,_,_,_) :- | |
163 | add_internal_error('Not a list: ',try_get_fd_value_list5(node(A,B,C,D,E),_,_,_,_)),fail. | |
164 | try_get_fd_value_list5(closure(A,B,C),_,_,_,_) :- | |
165 | add_internal_error('Not a list: ',try_get_fd_value_list5(closure(A,B,C),_,_,_,_)),fail. | |
166 | ||
167 | check_non_var_list(V) :- var(V),!,fail. %print(check_non_var_failed),nl,fail. | |
168 | check_non_var_list([]). | |
169 | check_non_var_list([H|T]) :- !, nonvar(H), check_non_var_list(T). | |
170 | ||
171 | % ----------------------------------------------------- | |
172 | ||
173 | % remove from a list of FDValues those that belong to objects that definitely cannot match Element | |
174 | % this improves the precision of a later in_fd_value_list_wf call | |
175 | % cannot_match: quick check to see if the (AVL or List) element could match | |
176 | ||
177 | :- use_module(kernel_tools,[cannot_match/2, cannot_match_aggressive/2]). | |
178 | ||
179 | % gets a (filtered) FD Value list (El used for filtering) | |
180 | get_fd_value_list(Set,Type,FDList,GroundList,El) :- | |
181 | get_fd_value_list5(Set,Type,FDList,true,GroundList,El,0). | |
182 | get_fd_value_list5(V,_,_,_,_,_,_) :- var(V),!,fail. | |
183 | get_fd_value_list5([],_,[],G,G,_,_). | |
184 | get_fd_value_list5([H|T],Type,FDValList,GroundIn,GroundOut,El,NrMatch) :- | |
185 | (NrMatch<2 -> cannot_match_aggressive(H,El) % there is still hope of obtaining something deterministic; try more aggressive version; important for test 34; see also test 985 where too much propagation hurts | |
186 | ; cannot_match(H,El)), | |
187 | !, % we can filter this FD Value out | |
188 | get_fd_value_list5(T,Type,FDValList,GroundIn,GroundOut,El,NrMatch). | |
189 | get_fd_value_list5([H|T],Type,FDValList,GroundIn,GroundOut,El,NrMatch) :- | |
190 | if(get_fd_value(Type,H,FDH), | |
191 | (FDValList = [FDH|FDT], N1 is NrMatch+1, | |
192 | (GroundIn=true,number(FDH) -> Ground1=true ; Ground1=false)), | |
193 | % Failure can happen when a variable gets instantiated (eg. X -> fd(_,t)) | |
194 | % and triggers co-routines which fail (happens for SelfGrandpas.als for scope 3) | |
195 | % TO DO: review that this is the only possibility of failure | |
196 | (debug:debug_println(9,get_fd_value_failed(Type,H,_)), | |
197 | check_valid_fd_type(Type),fail) | |
198 | ), | |
199 | get_fd_value_list5(T,Type,FDT,Ground1,GroundOut,El,N1). | |
200 | ||
201 | % ----------------------------------------------------- | |
202 | ||
203 | :- use_module(library(clpfd)). | |
204 | :- use_module(clpfd_interface). | |
205 | :- use_module(kernel_waitflags,[get_enumeration_finished_wait_flag/2]). | |
206 | ||
207 | in_fd_value_list_wf(Element,List,WF) :- (ground(List) -> Gr=true ; Gr=false), | |
208 | in_fd_value_list_wf(Gr,Element,List,WF). | |
209 | ||
210 | % assert that an FD Value is in a list of FD Values | |
211 | % in_fd_value_list(ListIsGround, FDElement, ListOfFDValues) | |
212 | in_fd_value_list_wf(true,Element,List,_WF) :- clpfd_inlist(Element,List). | |
213 | in_fd_value_list_wf(false,Element,List,WF) :- | |
214 | get_enumeration_finished_wait_flag(WF,EWF), | |
215 | in_non_ground_fd_value_lists(Element,List,EWF,top). | |
216 | ||
217 | in_non_ground_fd_value_lists(FDP,List,EWF,_) :- nonvar(FDP), FDP = fd_pair(A,B), !, | |
218 | split_fd_pair_list(List,ListA,ListB), | |
219 | in_non_ground_fd_value_lists(A,ListA,EWF,left), | |
220 | in_non_ground_fd_value_lists(B,ListB,EWF,right). | |
221 | in_non_ground_fd_value_lists(FDR,List,EWF,_) :- nonvar(FDR), FDR = fd_rec(Fields), !, | |
222 | %print(treat_fd_rec_fields(Fields,List)),nl, | |
223 | treat_fd_rec_fields(Fields,List,EWF). | |
224 | in_non_ground_fd_value_lists(Element,List,_EWF,Path) :- | |
225 | Path \= top, % otherwise we know List not to be ground | |
226 | %print(el(Element,List)),nl, | |
227 | ground(List), %ground_number_list(List,Min,Max,Len), | |
228 | !, | |
229 | clpfd_interface:clpfd_inlist(Element,List). % this is considerably faster; especially in compiled version (see SLOT-PERFORMANCE2) | |
230 | in_non_ground_fd_value_lists(Element,List,_EWF,_Path) :- | |
231 | tools:exact_member(Element,List), % avoid generating choice point for element position below | |
232 | !. | |
233 | in_non_ground_fd_value_lists(Element,List,EWF,_Path) :- % print(element(List,Element)),nl, | |
234 | clpfd:element(Nr,List,Element), | |
235 | label_el_nr(Nr,Element,List,EWF). % asserts that Element is the _Nr-th element of List; in contrast to clpfd_inlist List do not have to be numbers | |
236 | ||
237 | ||
238 | ||
239 | /* using this + Element in Min..Max | |
240 | does not seem to buy anything/ a lot to comparing calling clpfd_inlist for large lists | |
241 | ground_number_list([],0,0,0). | |
242 | ground_number_list([H|T],Min,Max,Len) :- number(H), | |
243 | ground_number_list_acc(T,H,H,1,Min,Max,Len). | |
244 | ground_number_list_acc([],Min,Max,Len,Min,Max,Len). | |
245 | ground_number_list_acc([H|T],Min0,Max0,Len0,Min,Max,Len) :- number(H), | |
246 | (H<Min0 -> Min1 = H ; Min1 = Min0), | |
247 | (H>Max0 -> Max1 = H ; Max1 = Max0), | |
248 | L1 is Len0+1, | |
249 | ground_number_list_acc(T,Min1,Max1,L1,Min,Max,Len). | |
250 | */ | |
251 | ||
252 | treat_fd_rec_fields([],_,_). | |
253 | treat_fd_rec_fields([V|TF],List,EWF) :- | |
254 | get_next_fd_rec_field(List,FieldFDList,RemainingList), | |
255 | %print(check(V,FieldFDList)),nl, | |
256 | in_non_ground_fd_value_lists(V,FieldFDList,EWF,rec), | |
257 | treat_fd_rec_fields(TF,RemainingList,EWF). | |
258 | ||
259 | get_next_fd_rec_field([],FieldList,RemList) :- !, FieldList=[], RemList=[]. | |
260 | get_next_fd_rec_field([fd_rec([FDV|Rem]) | TList], [FDV|TFieldList], [ fd_rec(Rem) | TRemList]) :- !, | |
261 | get_next_fd_rec_field(TList, TFieldList, TRemList). | |
262 | get_next_fd_rec_field(List,FieldList,RemainingList) :- | |
263 | add_internal_error('Illegal call: ',get_next_fd_rec_field(List,FieldList,RemainingList)), | |
264 | FieldList=[], RemainingList=[]. | |
265 | ||
266 | :- block label_el_nr(?,-,?,?), label_el_nr(-,?,?,-). | |
267 | label_el_nr(ElementNr,Element,List,_) :- %print(label(ElementNr,Element,List,_b)),nl, | |
268 | (number(ElementNr) -> true % clpfd:element already has a solution | |
269 | ; exact_member_nr(Element,List,1,Nr) -> ElementNr=Nr | |
270 | ; %translate:print_clpfd_variable(ElementNr),nl, | |
271 | when(ground(List), % we have to wait until List is ground; otherwise labeling ElementNr will instantiate List and we cannot use the cut ! | |
272 | (clpfd:labeling([],[ElementNr]) -> true)) % avoid pending co-routines in case the Element appears multiple times in the list ! | |
273 | % Note: ElementNr is a local variable not used outside of in_non_ground_fd_value_lists | |
274 | ). | |
275 | ||
276 | exact_member_nr(X,[Y|_],Acc,R) :- X==Y, !, Acc=R. | |
277 | exact_member_nr(X,[_|T],Acc,R) :- A1 is Acc+1, exact_member_nr(X,T,A1,R). | |
278 | ||
279 | split_fd_pair_list([],[],[]). | |
280 | split_fd_pair_list([fd_pair(A,B)|T],[A|TA],[B|TB]) :- split_fd_pair_list(T,TA,TB). | |
281 | ||
282 | ||
283 | % ----------------------------------------------------- | |
284 | ||
285 | :- use_module(kernel_objects,[equal_object/2]). | |
286 | ||
287 | % a lazy, blocking version which waits for Set to become sufficiently instantiated and then | |
288 | % calls in_fd_value_list_wf | |
289 | % Trys to transform E:Set into calls to clpfd library: ; | |
290 | % When FullyChecked becomes true, then there is no need to perform other processing (apart from labeling/enumeration of the involved values) | |
291 | % When FullyChecked becomes false | |
292 | lazy_fd_value_check(Set,E,_,FC) :- nonvar(Set), Set=[H|T], T==[], | |
293 | !, % we have a one-element singleton set | |
294 | %tools_printing:print_term_summary(equal_object(H,E)),nl, | |
295 | equal_object(H,E), | |
296 | FC=true. | |
297 | lazy_fd_value_check(Set,E,WF,FullyChecked) :- preferences:preference(use_clpfd_solver,true),!, | |
298 | lazy_in_list_propagation_block(Set,E,WF,FullyChecked). | |
299 | lazy_fd_value_check(_Set,_E,_WF,false). | |
300 | ||
301 | :- block lazy_in_list_propagation_block(-,?,?,?). | |
302 | lazy_in_list_propagation_block([],_,_,_) :- fail. % nothing can be element of empty set | |
303 | lazy_in_list_propagation_block([H|T],E,WF,FullyChecked) :- contains_fd_element(H,Res,WF), | |
304 | lazy_in_list_propagation_block_2(Res,H,T,E,WF,FullyChecked). | |
305 | ||
306 | :- block lazy_in_list_propagation_block_2(-,?,?,?,?,?). | |
307 | lazy_in_list_propagation_block_2(pred_false,_H,_T,_E,_WF,false). % nothing to propagate | |
308 | lazy_in_list_propagation_block_2(pred_true,H,T,E,WF,FullyChecked) :- % print(bound_h(H)),nl, | |
309 | bound_list(T,Bound), | |
310 | lazy_in_list_propagation_block_3(Bound,H,T,E,WF,FullyChecked). | |
311 | ||
312 | :- block lazy_in_list_propagation_block_3(-,?,?,?,?,?). | |
313 | lazy_in_list_propagation_block_3(pred_false,_H,_T,_E,_WF,FC) :- !, FC=false. % nothing to propagate | |
314 | lazy_in_list_propagation_block_3(pred_true,H,T,E,WF,FullyChecked) :- | |
315 | try_get_fd_value_type(E,H,Type), % print(fd_val_type(E,H,Type)),nl, | |
316 | get_fd_value_list([H|T],Type,FDVals,GroundList,E), % try and extract values which can be used for CLPFD | |
317 | get_fd_value(Type,E,EFD),!, | |
318 | FDVals \= [], % if we have the empty list then membership fails | |
319 | (complete_type(Type) -> FullyChecked = true ; FullyChecked = false), | |
320 | % print(lazy_in_element(FullyChecked,GroundList,E,[H|T],WF)),nl, | |
321 | % print(in_fd_value_list_wf(GroundList,EFD,FDVals,WF,FullyChecked)),nl, | |
322 | in_fd_value_list_wf(GroundList,EFD,FDVals,WF). | |
323 | lazy_in_list_propagation_block_3(pred_true,_H,_T,_E,_WF,FullyChecked) :- !, | |
324 | % should only happen for freeval with no fd type inside or for freevalues if element is not instantiated (we don't know which freval case we have) | |
325 | FullyChecked=false. | |
326 | lazy_in_list_propagation_block_3(P,H,T,E,WF,false) :- | |
327 | add_internal_error('Illegal call: ',lazy_in_list_propagation_block_3(P,H,T,E,WF,false)). | |
328 | ||
329 | :- block contains_fd_element(-,?,?). | |
330 | contains_fd_element(int(_),R,_WF) :- !, R=pred_true. | |
331 | contains_fd_element(fd(_,_),R,_WF) :- !, R=pred_true. | |
332 | contains_fd_element((A,B),R,WF) :- !,contains_fd_element(A,RA,WF), contains_fd_element(B,RB,WF), | |
333 | b_interpreter_check:disjoin(RA,RB,R,priority(16384),priority(16384),WF). % TO DO: examine priority to be used | |
334 | contains_fd_element(rec(Fields),R,WF) :- !, contains_field_fd_el(Fields,R,WF). | |
335 | contains_fd_element(freeval(_ID,_Case,_Val),R,_WF) :- !, | |
336 | % We assume that a freetype can contain FD elements; TO DO: really check ! | |
337 | % ID is something of the form 'List'(integer) ; we use get_freeval_type(ID,_Case) from kernel_freetypes to get subtypes | |
338 | % we could look at the value; but we could be unlucky in that this particular Case has no fd subtype but others do | |
339 | % anyway: try_get_fd_value_type will fail if there is no fd type | |
340 | R=pred_true. | |
341 | contains_fd_element(_,pred_false,_). | |
342 | ||
343 | :- block contains_field_fd_el(-,?,?). | |
344 | contains_field_fd_el([],R,_WF) :- !, R=pred_false. | |
345 | contains_field_fd_el([field(_,V)|_],R,WF) :- contains_fd_element(V,R,WF). % TO DO: look at other fields | |
346 | % deal with freetypes: freeval(ID,Case,Value); but here we need a different scheme: we need to find the case of the element and only look at elements in the list with the same constructor | |
347 | % we could also deal with set types {x,y} : { {1,2}, {2,3} } --> x/y : 1..3 | |
348 | % TO DO: maybe filter out non-matching elements also here (see filter_non_matching_elements for quick_propagate) | |
349 | ||
350 | % set output variable as soon as we can determine if we have a bound list | |
351 | :- block bound_list(-,?). | |
352 | bound_list(List,BoundList) :- | |
353 | (List = [] -> BoundList=pred_true | |
354 | ; List = [_|T] -> bound_list(T,BoundList) | |
355 | ; BoundList=pred_false). % we have avl_set or global_set or closure; TO DO: also allow avl_set | |
356 | ||
357 | ||
358 | % ----------------------------------------------------- | |
359 | ||
360 | % generate an in_fd_value_list_wf call for checking that E is a member of an AVL set encoding | |
361 | :- use_module(library(avl),[avl_domain/2]). | |
362 | avl_fd_value_check(AVL,E,WF,FullyChecked) :- | |
363 | AVL = node(TopEl,_True,_,_,_),!, | |
364 | contains_fd_element(TopEl,Res,WF), | |
365 | (Res==pred_true | |
366 | -> avl_domain(AVL,DomainList), | |
367 | DomainList = [H|T], | |
368 | lazy_in_list_propagation_block_3(pred_true,H,T,E,WF,FullyChecked) | |
369 | ; FullyChecked=false). | |
370 | avl_fd_value_check(AVL,E,WF,FullyChecked) :- | |
371 | add_internal_error('Illegal call: ',avl_fd_value_check(AVL,E,WF,FullyChecked)), | |
372 | FullyChecked=false. | |
373 | ||
374 | ||
375 | ||
376 | ||
377 | % ------------------------------------------------------------------------ | |
378 | ||
379 | % alternative utilities for collecting fd_set information, combine it and then use it | |
380 | ||
381 | % FD-SET | |
382 | ||
383 | % succeeds if we can obtain FD information for a B data value | |
384 | get_fdset_information(V,Info) :- var(V),!,Info=no_fdset_info. | |
385 | get_fdset_information(int(V),int_fdset(FDSET)) :- fd_set(V,FDSET). | |
386 | get_fdset_information(fd(V,GS),fd_fdset(FDSET,GS)) :- fd_set(V,FDSET). | |
387 | get_fdset_information(pred_true,single_value(pred_true)). | |
388 | get_fdset_information(pred_false,single_value(pred_false)). | |
389 | get_fdset_information(string(S),single_value(string(S))) :- ground(S). | |
390 | % TO DO: add more: couples, records, ... | |
391 | ||
392 | get_finite_fdset_information(V,_Info) :- var(V),!,fail. | |
393 | get_finite_fdset_information(int(V),int_fdset(FDSET)) :- finite_fd_set(V,FDSET). | |
394 | get_finite_fdset_information(fd(V,GS),fd_fdset(FDSET,GS)) :- finite_fd_set(V,FDSET). | |
395 | get_finite_fdset_information(pred_true,single_value(pred_true)). | |
396 | get_finite_fdset_information(pred_false,single_value(pred_false)). | |
397 | get_finite_fdset_information(string(S),single_value(string(S))) :- ground(S). | |
398 | ||
399 | finite_fd_set(V,FDSET) :- fd_set(V,FDSET), fdset_size(FDSET,N), number(N). %FDSET \= [[inf|sup]]. | |
400 | ||
401 | % will combine (union) two FD infos; have to be of compatible type | |
402 | combine_fdset_information(no_fdset_info,I,R) :- !, R=I. | |
403 | combine_fdset_information(I,no_fdset_info,R) :- !, R=I. | |
404 | combine_fdset_information(int_fdset(A),int_fdset(B),int_fdset(C)) :- !, | |
405 | %print(un(A,B,C)),nl, | |
406 | fdset_union(A,B,C). | |
407 | combine_fdset_information(fd_fdset(A,GS),fd_fdset(B,GS),fd_fdset(C,GS)) :- !, | |
408 | fdset_union(A,B,C). | |
409 | combine_fdset_information(single_value(V),single_value(W),R) :- !, | |
410 | (V==W -> R=single_value(V) ; R=no_fdset_info). | |
411 | combine_fdset_information(A,B,C) :- | |
412 | add_internal_error('Illegal FD info: ',combine_fdset_information(A,B,C)),fail. | |
413 | ||
414 | % assert that a variable/value matches collected FD information | |
415 | assert_fdset_information(int_fdset(FDSet),X) :- !, | |
416 | (ground(X) -> true ; X=int(XX),in_set(XX,FDSet)). | |
417 | assert_fdset_information(fd_fdset(FDSet,GS),X) :- !, | |
418 | (ground(X) -> true ; X=fd(XX,GS),in_set(XX,FDSet)). | |
419 | assert_fdset_information(single_value(V),X) :- !, equal_object(V,X). | |
420 | assert_fdset_information(_,_). | |
421 | ||
422 | ||
423 |