1 /*
2  * This file is part of the Yices SMT Solver.
3  * Copyright (C) 2017 SRI International.
4  *
5  * Yices is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * Yices is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with Yices.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 /*
20  * Computation of the free variables of a term.
21  */
22 
23 #include <assert.h>
24 
25 #include "terms/free_var_collector.h"
26 
27 
28 /*
29  * Initialization
30  */
init_fvar_collector(fvar_collector_t * collect,term_table_t * ttbl)31 void init_fvar_collector(fvar_collector_t *collect, term_table_t *ttbl) {
32   collect->terms = ttbl;
33   init_ptr_hmap(&collect->map, 0);         // default size
34   init_harray_store(&collect->store);
35   init_pstack(&collect->stack);
36 }
37 
38 
39 /*
40  * Delete the whole thing
41  */
delete_fvar_collector(fvar_collector_t * collect)42 void delete_fvar_collector(fvar_collector_t *collect) {
43   delete_ptr_hmap(&collect->map);
44   delete_harray_store(&collect->store);
45   delete_pstack(&collect->stack);
46 }
47 
48 
49 /*
50  * Reset: empty everything
51  */
reset_fvar_collector(fvar_collector_t * collect)52 void reset_fvar_collector(fvar_collector_t *collect) {
53   ptr_hmap_reset(&collect->map);
54   reset_harray_store(&collect->store);
55   reset_pstack(&collect->stack);
56 }
57 
58 
59 
60 /*
61  * For debugging: check that a is sorted and does not contain duplicates
62  * and that all elements of a are variables.
63  */
64 #ifndef NDEBUG
good_var_set(term_table_t * terms,harray_t * a)65 static bool good_var_set(term_table_t *terms, harray_t *a) {
66   uint32_t i, n;
67   term_t x;
68 
69   n = a->nelems;
70   x = NULL_TERM; // -1
71   for (i = 0; i<n; i++) {
72     if (a->data[i] <= x) return false; // not sorted or duplicate
73     x = a->data[i];
74     if (term_kind(terms, x) != VARIABLE) return false;
75   }
76 
77   return true;
78 }
79 
80 #endif
81 
82 
83 /*
84  * Empty set of variables
85  */
empty_fvar_set(fvar_collector_t * collect)86 static inline harray_t *empty_fvar_set(fvar_collector_t *collect) {
87   return empty_harray(&collect->store);
88 }
89 
90 
91 /*
92  * Singleton set: x = unique element
93  */
singleton_fvar_set(fvar_collector_t * collect,term_t x)94 static inline harray_t *singleton_fvar_set(fvar_collector_t *collect, term_t x) {
95   return singleton_harray(&collect->store, x);
96 }
97 
98 
99 
100 /*
101  * Check whether the free var set for term index i is in the hash map
102  * - return NULL if it's not
103  */
lookup_free_vars(fvar_collector_t * collect,int32_t i)104 static harray_t *lookup_free_vars(fvar_collector_t *collect, int32_t i) {
105   ptr_hmap_pair_t *p;
106   harray_t *a;
107 
108   a = NULL;
109   p = ptr_hmap_find(&collect->map, i);
110   if (p != NULL) {
111     a = p->val;
112     assert(a != NULL && good_var_set(collect->terms, a));
113   }
114   return a;
115 }
116 
117 
118 /*
119  * Store the free var set of i into the hash map
120  * - i: term index
121  * - a: set of variables of i
122  * There must not be anything mapped to i the the map yet.
123  */
cache_free_vars(fvar_collector_t * collect,int32_t i,harray_t * a)124 static void cache_free_vars(fvar_collector_t *collect, int32_t i, harray_t *a) {
125   ptr_hmap_pair_t *p;
126 
127   p = ptr_hmap_get(&collect->map, i);
128   assert(p->val == NULL);
129   p->val = a;
130 }
131 
132 
133 /*
134  * Free variables in composite terms (other than forall)
135  */
free_vars_of_composite(fvar_collector_t * collect,composite_term_t * c)136 static harray_t *free_vars_of_composite(fvar_collector_t *collect, composite_term_t *c) {
137   harray_t **a;
138   harray_t *result;
139   uint32_t i, n;
140 
141   n = c->arity;
142   a = (harray_t **) alloc_pstack_array(&collect->stack, n);
143   for (i=0; i<n; i++) {
144     a[i] = get_free_vars_of_term(collect, c->arg[i]);
145   }
146   result = merge_harrays(&collect->store, a, n);
147   free_pstack_array(&collect->stack, (void **) a);
148 
149   return result;
150 }
151 
free_vars_of_pprod(fvar_collector_t * collect,pprod_t * p)152 static harray_t *free_vars_of_pprod(fvar_collector_t *collect, pprod_t *p) {
153   harray_t **a;
154   harray_t *result;
155   uint32_t i, n;
156 
157   n = p->len;
158   a = (harray_t **) alloc_pstack_array(&collect->stack, n);
159   for (i=0; i<n; i++) {
160     a[i] = get_free_vars_of_term(collect, p->prod[i].var);
161   }
162   result = merge_harrays(&collect->store, a, n);
163   free_pstack_array(&collect->stack, (void **) a);
164 
165   return result;
166 }
167 
free_vars_of_poly(fvar_collector_t * collect,polynomial_t * p)168 static harray_t *free_vars_of_poly(fvar_collector_t *collect, polynomial_t *p) {
169   harray_t **a;
170   harray_t *result;
171   monomial_t *mono;
172   uint32_t i, n;
173 
174   n = p->nterms;
175   mono = p->mono;
176   // skip the constant term if any
177   if (mono[0].var == const_idx) {
178     n --;
179     mono ++;
180   }
181 
182   a = (harray_t **) alloc_pstack_array(&collect->stack, n);
183   for (i=0; i<n; i++) {
184     a[i] = get_free_vars_of_term(collect, mono[i].var);
185   }
186   result = merge_harrays(&collect->store, a, n);
187   free_pstack_array(&collect->stack, (void **) a);
188 
189   return result;
190 }
191 
free_vars_of_bvpoly64(fvar_collector_t * collect,bvpoly64_t * p)192 static harray_t *free_vars_of_bvpoly64(fvar_collector_t *collect, bvpoly64_t *p) {
193   harray_t **a;
194   harray_t *result;
195   bvmono64_t *mono;
196   uint32_t i, n;
197 
198   n = p->nterms;
199   mono = p->mono;
200   if (mono[0].var == const_idx) {
201     n--;
202     mono ++;
203   }
204   a = (harray_t **) alloc_pstack_array(&collect->stack, n);
205   for (i=0; i<n; i++) {
206     a[i] = get_free_vars_of_term(collect, mono[i].var);
207   }
208   result = merge_harrays(&collect->store, a, n);
209   free_pstack_array(&collect->stack, (void **) a);
210 
211   return result;
212 }
213 
free_vars_of_bvpoly(fvar_collector_t * collect,bvpoly_t * p)214 static harray_t *free_vars_of_bvpoly(fvar_collector_t *collect, bvpoly_t *p) {
215   harray_t **a;
216   harray_t *result;
217   bvmono_t *mono;
218   uint32_t i, n;
219 
220   n = p->nterms;
221   mono = p->mono;
222   if (mono[0].var == const_idx) {
223     n--;
224     mono ++;
225   }
226 
227   a = (harray_t **) alloc_pstack_array(&collect->stack, n);
228   for (i=0; i<n; i++) {
229     a[i] = get_free_vars_of_term(collect, mono[i].var);
230   }
231   result = merge_harrays(&collect->store, a, n);
232   free_pstack_array(&collect->stack, (void **) a);
233 
234   return result;
235 }
236 
237 
238 /*
239  * Free variables in (FORALL x1, ..., xn: P) and (LAMBDA x1 ... x_n : t)
240  */
free_vars_of_binding(fvar_collector_t * collect,composite_term_t * p)241 static harray_t *free_vars_of_binding(fvar_collector_t *collect, composite_term_t *p) {
242   harray_t *a;
243   uint32_t n;
244 
245   n = p->arity;
246   /*
247    * The bound variables are p->arg[0] to p->arg[n-2]
248    * The body is p->arg[n-1]
249    */
250   assert(n >= 2);
251   a = get_free_vars_of_term(collect, p->arg[n-1]);
252   a = harray_remove_elem(&collect->store, a, n-1, p->arg);
253 
254   return a;
255 }
256 
257 
258 /*
259  * Free variables in a root atom r:
260  */
free_vars_of_root_atom(fvar_collector_t * collect,root_atom_t * r)261 static harray_t *free_vars_of_root_atom(fvar_collector_t *collect, root_atom_t *r) {
262   harray_t *a;
263   term_t x;
264 
265   x = r->x;
266   a  = get_free_vars_of_term(collect, r->p);  // p = polynomial
267   a = harray_remove_elem(&collect->store, a, 1, &x); // x = bound variable in p(x, ...)
268 
269   return a;
270 }
271 
272 
273 
274 /*
275  * Compute the set of free variables in term t:
276  * - t must be defined in collect->terms
277  * - the set is returned as a harray structure a (cf. int_array_hsets)
278  *   a->nelems = size of the set = n
279  *   a->data[0 ... n-1] = variables of t listed in increasing order
280  */
get_free_vars_of_term(fvar_collector_t * collect,term_t t)281 harray_t *get_free_vars_of_term(fvar_collector_t *collect, term_t t) {
282   term_table_t *terms;
283   harray_t *result;
284   int32_t i;
285 
286   terms = collect->terms;
287   i = index_of(t);
288   switch (kind_for_idx(terms, i)) {
289   case CONSTANT_TERM:
290   case ARITH_CONSTANT:
291   case BV64_CONSTANT:
292   case BV_CONSTANT:
293   case UNINTERPRETED_TERM:
294     result = empty_fvar_set(collect);
295     break;
296 
297   case VARIABLE:
298     // we use pos_term(i) rather than t since t could be a negative term
299     // (i.e., neg_term(i) that represents (not v) for some Boolean variable v)
300     result = singleton_fvar_set(collect, pos_term(i));
301     break;
302 
303   case ARITH_EQ_ATOM:
304   case ARITH_GE_ATOM:
305   case ARITH_IS_INT_ATOM:
306   case ARITH_FLOOR:
307   case ARITH_CEIL:
308   case ARITH_ABS:
309     result = get_free_vars_of_term(collect, integer_value_for_idx(terms, i));
310     break;
311 
312   case ARITH_ROOT_ATOM:
313     result = lookup_free_vars(collect, i);
314     if (result == NULL) {
315       result = free_vars_of_root_atom(collect, root_atom_for_idx(terms, i));
316       cache_free_vars(collect, i, result);
317     }
318     break;
319 
320   case ITE_TERM:
321   case ITE_SPECIAL:
322   case APP_TERM:
323   case UPDATE_TERM:
324   case TUPLE_TERM:
325   case EQ_TERM:
326   case DISTINCT_TERM:
327   case OR_TERM:
328   case XOR_TERM:
329   case ARITH_BINEQ_ATOM:
330   case ARITH_RDIV:
331   case ARITH_IDIV:
332   case ARITH_MOD:
333   case ARITH_DIVIDES_ATOM:
334   case BV_ARRAY:
335   case BV_DIV:
336   case BV_REM:
337   case BV_SDIV:
338   case BV_SREM:
339   case BV_SMOD:
340   case BV_SHL:
341   case BV_LSHR:
342   case BV_ASHR:
343   case BV_EQ_ATOM:
344   case BV_GE_ATOM:
345   case BV_SGE_ATOM:
346     result = lookup_free_vars(collect, i);
347     if (result == NULL) {
348       result = free_vars_of_composite(collect, composite_for_idx(terms, i));
349       cache_free_vars(collect, i, result);
350     }
351     break;
352 
353   case FORALL_TERM:
354   case LAMBDA_TERM:
355     result = lookup_free_vars(collect, i);
356     if (result == NULL) {
357       result = free_vars_of_binding(collect, composite_for_idx(terms, i));
358       cache_free_vars(collect, i, result);
359     }
360     break;
361 
362   case SELECT_TERM:
363   case BIT_TERM:
364     result = get_free_vars_of_term(collect, select_for_idx(terms, i)->arg);
365     break;
366 
367   case POWER_PRODUCT:
368     result = lookup_free_vars(collect, i);
369     if (result == NULL) {
370       result = free_vars_of_pprod(collect, pprod_for_idx(terms, i));
371       cache_free_vars(collect, i, result);
372     }
373     break;
374 
375   case ARITH_POLY:
376     result = lookup_free_vars(collect, i);
377     if (result == NULL) {
378       result = free_vars_of_poly(collect, polynomial_for_idx(terms, i));
379       cache_free_vars(collect, i, result);
380     }
381     break;
382 
383   case BV64_POLY:
384     result = lookup_free_vars(collect, i);
385     if (result == NULL) {
386       result = free_vars_of_bvpoly64(collect, bvpoly64_for_idx(terms, i));
387       cache_free_vars(collect, i, result);
388     }
389     break;
390 
391   case BV_POLY:
392     result = lookup_free_vars(collect, i);
393     if (result == NULL) {
394       result = free_vars_of_bvpoly(collect, bvpoly_for_idx(terms, i));
395       cache_free_vars(collect, i, result);
396     }
397     break;
398 
399   default:
400     assert(false);
401     result = NULL;
402     break;
403   }
404 
405   return result;
406 }
407 
408 
409 
410 /*
411  * Check whether t is a ground term:
412  * - side effect: this computes the set of free variables of t
413  */
term_is_ground(fvar_collector_t * collect,term_t t)414 bool term_is_ground(fvar_collector_t *collect, term_t t) {
415   harray_t *a;
416 
417   a = get_free_vars_of_term(collect, t);
418   return a->nelems == 0;
419 }
420 
421 
422 /*
423  * Filter for ptr_hmap: r is a pair <term, ptr>
424  * - r must be deleted if the term is dead
425  * - aux is a pointer to the term table
426  */
fvar_dead_hmap_pair(void * aux,const ptr_hmap_pair_t * r)427 static bool fvar_dead_hmap_pair(void *aux, const ptr_hmap_pair_t *r) {
428   return !live_term(aux, r->key);
429 }
430 
431 
432 /*
433  * Filter for the store:
434  * - a is a harray structure
435  * - aux is a pointer to the term table
436  * - a must be deleted if it contains a dead term
437  */
fvar_dead_harray(void * aux,const harray_t * a)438 static bool fvar_dead_harray(void *aux, const harray_t *a) {
439   uint32_t i, n;
440 
441   n = a->nelems;
442   for (i=0; i<n; i++) {
443     if (!live_term(aux, a->data[i])) {
444       return true;
445     }
446   }
447 
448   return false;
449 }
450 
451 
452 
453 /*
454  * Cleanup: remove any references to deleted terms
455  * - this must be called after terms are removed from collect->terms
456  */
cleanup_fvar_collector(fvar_collector_t * collect)457 void cleanup_fvar_collector(fvar_collector_t *collect) {
458   // must delete records in the map first
459   ptr_hmap_remove_records(&collect->map, collect->terms, fvar_dead_hmap_pair);
460   harray_store_remove_arrays(&collect->store, collect->terms, fvar_dead_harray);
461 }
462