drop isl_div abstraction
[platform/upstream/isl.git] / isl_local_space.c
1 /*
2  * Copyright 2011      INRIA Saclay
3  *
4  * Use of this software is governed by the GNU LGPLv2.1 license
5  *
6  * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France,
7  * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod,
8  * 91893 Orsay, France
9  */
10
11 #include <isl_ctx_private.h>
12 #include <isl_map_private.h>
13 #include <isl_local_space_private.h>
14 #include <isl_space_private.h>
15 #include <isl_mat_private.h>
16 #include <isl_aff_private.h>
17 #include <isl/seq.h>
18
19 isl_ctx *isl_local_space_get_ctx(__isl_keep isl_local_space *ls)
20 {
21         return ls ? ls->dim->ctx : NULL;
22 }
23
24 __isl_give isl_local_space *isl_local_space_alloc_div(__isl_take isl_space *dim,
25         __isl_take isl_mat *div)
26 {
27         isl_ctx *ctx;
28         isl_local_space *ls = NULL;
29
30         if (!dim)
31                 goto error;
32
33         ctx = isl_space_get_ctx(dim);
34         ls = isl_calloc_type(ctx, struct isl_local_space);
35         if (!ls)
36                 goto error;
37
38         ls->ref = 1;
39         ls->dim = dim;
40         ls->div = div;
41
42         return ls;
43 error:
44         isl_space_free(dim);
45         isl_local_space_free(ls);
46         return NULL;
47 }
48
49 __isl_give isl_local_space *isl_local_space_alloc(__isl_take isl_space *dim,
50         unsigned n_div)
51 {
52         isl_ctx *ctx;
53         isl_mat *div;
54         unsigned total;
55
56         if (!dim)
57                 return NULL;
58
59         total = isl_space_dim(dim, isl_dim_all);
60
61         ctx = isl_space_get_ctx(dim);
62         div = isl_mat_alloc(ctx, n_div, 1 + 1 + total + n_div);
63         return isl_local_space_alloc_div(dim, div);
64 }
65
66 __isl_give isl_local_space *isl_local_space_from_space(__isl_take isl_space *dim)
67 {
68         return isl_local_space_alloc(dim, 0);
69 }
70
71 __isl_give isl_local_space *isl_local_space_copy(__isl_keep isl_local_space *ls)
72 {
73         if (!ls)
74                 return NULL;
75
76         ls->ref++;
77         return ls;
78 }
79
80 __isl_give isl_local_space *isl_local_space_dup(__isl_keep isl_local_space *ls)
81 {
82         if (!ls)
83                 return NULL;
84
85         return isl_local_space_alloc_div(isl_space_copy(ls->dim),
86                                          isl_mat_copy(ls->div));
87
88 }
89
90 __isl_give isl_local_space *isl_local_space_cow(__isl_take isl_local_space *ls)
91 {
92         if (!ls)
93                 return NULL;
94
95         if (ls->ref == 1)
96                 return ls;
97         ls->ref--;
98         return isl_local_space_dup(ls);
99 }
100
101 void *isl_local_space_free(__isl_take isl_local_space *ls)
102 {
103         if (!ls)
104                 return NULL;
105
106         if (--ls->ref > 0)
107                 return NULL;
108
109         isl_space_free(ls->dim);
110         isl_mat_free(ls->div);
111
112         free(ls);
113
114         return NULL;
115 }
116
117 /* Is the local space that of a set?
118  */
119 int isl_local_space_is_set(__isl_keep isl_local_space *ls)
120 {
121         return ls ? isl_space_is_set(ls->dim) : -1;
122 }
123
124 /* Return true if the two local spaces are identical, with identical
125  * expressions for the integer divisions.
126  */
127 int isl_local_space_is_equal(__isl_keep isl_local_space *ls1,
128         __isl_keep isl_local_space *ls2)
129 {
130         int equal;
131
132         if (!ls1 || !ls2)
133                 return -1;
134
135         equal = isl_space_is_equal(ls1->dim, ls2->dim);
136         if (equal < 0 || !equal)
137                 return equal;
138
139         if (!isl_local_space_divs_known(ls1))
140                 return 0;
141         if (!isl_local_space_divs_known(ls2))
142                 return 0;
143
144         return isl_mat_is_equal(ls1->div, ls2->div);
145 }
146
147 int isl_local_space_dim(__isl_keep isl_local_space *ls,
148         enum isl_dim_type type)
149 {
150         if (!ls)
151                 return 0;
152         if (type == isl_dim_div)
153                 return ls->div->n_row;
154         if (type == isl_dim_all)
155                 return isl_space_dim(ls->dim, isl_dim_all) + ls->div->n_row;
156         return isl_space_dim(ls->dim, type);
157 }
158
159 unsigned isl_local_space_offset(__isl_keep isl_local_space *ls,
160         enum isl_dim_type type)
161 {
162         isl_space *dim;
163
164         if (!ls)
165                 return 0;
166
167         dim = ls->dim;
168         switch (type) {
169         case isl_dim_cst:       return 0;
170         case isl_dim_param:     return 1;
171         case isl_dim_in:        return 1 + dim->nparam;
172         case isl_dim_out:       return 1 + dim->nparam + dim->n_in;
173         case isl_dim_div:       return 1 + dim->nparam + dim->n_in + dim->n_out;
174         default:                return 0;
175         }
176 }
177
178 const char *isl_local_space_get_dim_name(__isl_keep isl_local_space *ls,
179         enum isl_dim_type type, unsigned pos)
180 {
181         return ls ? isl_space_get_dim_name(ls->dim, type, pos) : NULL;
182 }
183
184 __isl_give isl_aff *isl_local_space_get_div(__isl_keep isl_local_space *ls,
185         int pos)
186 {
187         isl_aff *aff;
188
189         if (!ls)
190                 return NULL;
191
192         if (pos < 0 || pos >= ls->div->n_row)
193                 isl_die(isl_local_space_get_ctx(ls), isl_error_invalid,
194                         "index out of bounds", return NULL);
195
196         if (isl_int_is_zero(ls->div->row[pos][0]))
197                 isl_die(isl_local_space_get_ctx(ls), isl_error_invalid,
198                         "expression of div unknown", return NULL);
199
200         aff = isl_aff_alloc(isl_local_space_copy(ls));
201         if (!aff)
202                 return NULL;
203         isl_seq_cpy(aff->v->el, ls->div->row[pos], aff->v->size);
204         return aff;
205 }
206
207 __isl_give isl_space *isl_local_space_get_space(__isl_keep isl_local_space *ls)
208 {
209         if (!ls)
210                 return NULL;
211
212         return isl_space_copy(ls->dim);
213 }
214
215 __isl_give isl_local_space *isl_local_space_set_dim_name(
216         __isl_take isl_local_space *ls,
217         enum isl_dim_type type, unsigned pos, const char *s)
218 {
219         ls = isl_local_space_cow(ls);
220         if (!ls)
221                 return NULL;
222         ls->dim = isl_space_set_dim_name(ls->dim, type, pos, s);
223         if (!ls->dim)
224                 return isl_local_space_free(ls);
225
226         return ls;
227 }
228
229 __isl_give isl_local_space *isl_local_space_reset_space(
230         __isl_take isl_local_space *ls, __isl_take isl_space *dim)
231 {
232         ls = isl_local_space_cow(ls);
233         if (!ls || !dim)
234                 goto error;
235
236         isl_space_free(ls->dim);
237         ls->dim = dim;
238
239         return ls;
240 error:
241         isl_local_space_free(ls);
242         isl_space_free(dim);
243         return NULL;
244 }
245
246 /* Reorder the columns of the given div definitions according to the
247  * given reordering.
248  * The order of the divs themselves is assumed not to change.
249  */
250 static __isl_give isl_mat *reorder_divs(__isl_take isl_mat *div,
251         __isl_take isl_reordering *r)
252 {
253         int i, j;
254         isl_mat *mat;
255         int extra;
256
257         if (!div || !r)
258                 goto error;
259
260         extra = isl_space_dim(r->dim, isl_dim_all) + div->n_row - r->len;
261         mat = isl_mat_alloc(div->ctx, div->n_row, div->n_col + extra);
262         if (!mat)
263                 goto error;
264
265         for (i = 0; i < div->n_row; ++i) {
266                 isl_seq_cpy(mat->row[i], div->row[i], 2);
267                 isl_seq_clr(mat->row[i] + 2, mat->n_col - 2);
268                 for (j = 0; j < r->len; ++j)
269                         isl_int_set(mat->row[i][2 + r->pos[j]],
270                                     div->row[i][2 + j]);
271         }
272
273         isl_reordering_free(r);
274         isl_mat_free(div);
275         return mat;
276 error:
277         isl_reordering_free(r);
278         isl_mat_free(div);
279         return NULL;
280 }
281
282 /* Reorder the dimensions of "ls" according to the given reordering.
283  * The reordering r is assumed to have been extended with the local
284  * variables, leaving them in the same order.
285  */
286 __isl_give isl_local_space *isl_local_space_realign(
287         __isl_take isl_local_space *ls, __isl_take isl_reordering *r)
288 {
289         ls = isl_local_space_cow(ls);
290         if (!ls || !r)
291                 goto error;
292
293         ls->div = reorder_divs(ls->div, isl_reordering_copy(r));
294         if (!ls->div)
295                 goto error;
296
297         ls = isl_local_space_reset_space(ls, isl_space_copy(r->dim));
298
299         isl_reordering_free(r);
300         return ls;
301 error:
302         isl_local_space_free(ls);
303         isl_reordering_free(r);
304         return NULL;
305 }
306
307 __isl_give isl_local_space *isl_local_space_add_div(
308         __isl_take isl_local_space *ls, __isl_take isl_vec *div)
309 {
310         ls = isl_local_space_cow(ls);
311         if (!ls || !div)
312                 goto error;
313
314         if (ls->div->n_col != div->size)
315                 isl_die(isl_local_space_get_ctx(ls), isl_error_invalid,
316                         "incompatible dimensions", goto error);
317
318         ls->div = isl_mat_add_zero_cols(ls->div, 1);
319         ls->div = isl_mat_add_rows(ls->div, 1);
320         if (!ls->div)
321                 goto error;
322
323         isl_seq_cpy(ls->div->row[ls->div->n_row - 1], div->el, div->size);
324         isl_int_set_si(ls->div->row[ls->div->n_row - 1][div->size], 0);
325
326         isl_vec_free(div);
327         return ls;
328 error:
329         isl_local_space_free(ls);
330         isl_vec_free(div);
331         return NULL;
332 }
333
334 __isl_give isl_local_space *isl_local_space_replace_divs(
335         __isl_take isl_local_space *ls, __isl_take isl_mat *div)
336 {
337         ls = isl_local_space_cow(ls);
338
339         if (!ls || !div)
340                 goto error;
341
342         isl_mat_free(ls->div);
343         ls->div = div;
344         return ls;
345 error:
346         isl_mat_free(div);
347         isl_local_space_free(ls);
348         return NULL;
349 }
350
351 /* Copy row "s" of "src" to row "d" of "dst", applying the expansion
352  * defined by "exp".
353  */
354 static void expand_row(__isl_keep isl_mat *dst, int d,
355         __isl_keep isl_mat *src, int s, int *exp)
356 {
357         int i;
358         unsigned c = src->n_col - src->n_row;
359
360         isl_seq_cpy(dst->row[d], src->row[s], c);
361         isl_seq_clr(dst->row[d] + c, dst->n_col - c);
362
363         for (i = 0; i < s; ++i)
364                 isl_int_set(dst->row[d][c + exp[i]], src->row[s][c + i]);
365 }
366
367 /* Compare (known) divs.
368  * Return non-zero if at least one of the two divs is unknown.
369  */
370 static int cmp_row(__isl_keep isl_mat *div, int i, int j)
371 {
372         int li, lj;
373
374         if (isl_int_is_zero(div->row[j][0]))
375                 return -1;
376         if (isl_int_is_zero(div->row[i][0]))
377                 return 1;
378
379         li = isl_seq_last_non_zero(div->row[i], div->n_col);
380         lj = isl_seq_last_non_zero(div->row[j], div->n_col);
381
382         if (li != lj)
383                 return li - lj;
384
385         return isl_seq_cmp(div->row[i], div->row[j], div->n_col);
386 }
387
388 /* Combine the two lists of divs into a single list.
389  * For each row i in div1, exp1[i] is set to the position of the corresponding
390  * row in the result.  Similarly for div2 and exp2.
391  * This function guarantees
392  *      exp1[i] >= i
393  *      exp1[i+1] > exp1[i]
394  * For optimal merging, the two input list should have been sorted.
395  */
396 __isl_give isl_mat *isl_merge_divs(__isl_keep isl_mat *div1,
397         __isl_keep isl_mat *div2, int *exp1, int *exp2)
398 {
399         int i, j, k;
400         isl_mat *div = NULL;
401         unsigned d = div1->n_col - div1->n_row;
402
403         div = isl_mat_alloc(div1->ctx, 1 + div1->n_row + div2->n_row,
404                                 d + div1->n_row + div2->n_row);
405         if (!div)
406                 return NULL;
407
408         for (i = 0, j = 0, k = 0; i < div1->n_row && j < div2->n_row; ++k) {
409                 int cmp;
410
411                 expand_row(div, k, div1, i, exp1);
412                 expand_row(div, k + 1, div2, j, exp2);
413
414                 cmp = cmp_row(div, k, k + 1);
415                 if (cmp == 0) {
416                         exp1[i++] = k;
417                         exp2[j++] = k;
418                 } else if (cmp < 0) {
419                         exp1[i++] = k;
420                 } else {
421                         exp2[j++] = k;
422                         isl_seq_cpy(div->row[k], div->row[k + 1], div->n_col);
423                 }
424         }
425         for (; i < div1->n_row; ++i, ++k) {
426                 expand_row(div, k, div1, i, exp1);
427                 exp1[i] = k;
428         }
429         for (; j < div2->n_row; ++j, ++k) {
430                 expand_row(div, k, div2, j, exp2);
431                 exp2[j] = k;
432         }
433
434         div->n_row = k;
435         div->n_col = d + k;
436
437         return div;
438 }
439
440 int isl_local_space_divs_known(__isl_keep isl_local_space *ls)
441 {
442         int i;
443
444         if (!ls)
445                 return -1;
446
447         for (i = 0; i < ls->div->n_row; ++i)
448                 if (isl_int_is_zero(ls->div->row[i][0]))
449                         return 0;
450
451         return 1;
452 }
453
454 __isl_give isl_local_space *isl_local_space_domain(
455         __isl_take isl_local_space *ls)
456 {
457         ls = isl_local_space_drop_dims(ls, isl_dim_out,
458                                         0, isl_local_space_dim(ls, isl_dim_out));
459         ls = isl_local_space_cow(ls);
460         if (!ls)
461                 return NULL;
462         ls->dim = isl_space_domain(ls->dim);
463         if (!ls->dim)
464                 return isl_local_space_free(ls);
465         return ls;
466 }
467
468 /* Construct a local space for a map that has the given local
469  * space as domain and that has a zero-dimensional range.
470  */
471 __isl_give isl_local_space *isl_local_space_from_domain(
472         __isl_take isl_local_space *ls)
473 {
474         ls = isl_local_space_cow(ls);
475         if (!ls)
476                 return NULL;
477         ls->dim = isl_space_from_domain(ls->dim);
478         if (!ls->dim)
479                 return isl_local_space_free(ls);
480         return ls;
481 }
482
483 __isl_give isl_local_space *isl_local_space_add_dims(
484         __isl_take isl_local_space *ls, enum isl_dim_type type, unsigned n)
485 {
486         int pos;
487
488         if (!ls)
489                 return NULL;
490         pos = isl_local_space_dim(ls, type);
491         return isl_local_space_insert_dims(ls, type, pos, n);
492 }
493
494 /* Remove common factor of non-constant terms and denominator.
495  */
496 static void normalize_div(__isl_keep isl_local_space *ls, int div)
497 {
498         isl_ctx *ctx = ls->div->ctx;
499         unsigned total = ls->div->n_col - 2;
500
501         isl_seq_gcd(ls->div->row[div] + 2, total, &ctx->normalize_gcd);
502         isl_int_gcd(ctx->normalize_gcd,
503                     ctx->normalize_gcd, ls->div->row[div][0]);
504         if (isl_int_is_one(ctx->normalize_gcd))
505                 return;
506
507         isl_seq_scale_down(ls->div->row[div] + 2, ls->div->row[div] + 2,
508                             ctx->normalize_gcd, total);
509         isl_int_divexact(ls->div->row[div][0], ls->div->row[div][0],
510                             ctx->normalize_gcd);
511         isl_int_fdiv_q(ls->div->row[div][1], ls->div->row[div][1],
512                             ctx->normalize_gcd);
513 }
514
515 /* Exploit the equalities in "eq" to simplify the expressions of
516  * the integer divisions in "ls".
517  * The integer divisions in "ls" are assumed to appear as regular
518  * dimensions in "eq".
519  */
520 __isl_give isl_local_space *isl_local_space_substitute_equalities(
521         __isl_take isl_local_space *ls, __isl_take isl_basic_set *eq)
522 {
523         int i, j, k;
524         unsigned total;
525         unsigned n_div;
526
527         ls = isl_local_space_cow(ls);
528         if (!ls || !eq)
529                 goto error;
530
531         total = isl_space_dim(eq->dim, isl_dim_all);
532         if (isl_local_space_dim(ls, isl_dim_all) != total)
533                 isl_die(isl_local_space_get_ctx(ls), isl_error_invalid,
534                         "dimensions don't match", goto error);
535         total++;
536         n_div = eq->n_div;
537         for (i = 0; i < eq->n_eq; ++i) {
538                 j = isl_seq_last_non_zero(eq->eq[i], total + n_div);
539                 if (j < 0 || j == 0 || j >= total)
540                         continue;
541
542                 for (k = 0; k < ls->div->n_row; ++k) {
543                         if (isl_int_is_zero(ls->div->row[k][1 + j]))
544                                 continue;
545                         isl_seq_elim(ls->div->row[k] + 1, eq->eq[i], j, total,
546                                         &ls->div->row[k][0]);
547                         normalize_div(ls, k);
548                 }
549         }
550
551         isl_basic_set_free(eq);
552         return ls;
553 error:
554         isl_basic_set_free(eq);
555         isl_local_space_free(ls);
556         return NULL;
557 }
558
559 int isl_local_space_is_named_or_nested(__isl_keep isl_local_space *ls,
560         enum isl_dim_type type)
561 {
562         if (!ls)
563                 return -1;
564         return isl_space_is_named_or_nested(ls->dim, type);
565 }
566
567 __isl_give isl_local_space *isl_local_space_drop_dims(
568         __isl_take isl_local_space *ls,
569         enum isl_dim_type type, unsigned first, unsigned n)
570 {
571         isl_ctx *ctx;
572
573         if (!ls)
574                 return NULL;
575         if (n == 0 && !isl_local_space_is_named_or_nested(ls, type))
576                 return ls;
577
578         ctx = isl_local_space_get_ctx(ls);
579         if (first + n > isl_local_space_dim(ls, type))
580                 isl_die(ctx, isl_error_invalid, "range out of bounds",
581                         return isl_local_space_free(ls));
582
583         ls = isl_local_space_cow(ls);
584         if (!ls)
585                 return NULL;
586
587         if (type == isl_dim_div) {
588                 ls->div = isl_mat_drop_rows(ls->div, first, n);
589         } else {
590                 ls->dim = isl_space_drop_dims(ls->dim, type, first, n);
591                 if (!ls->dim)
592                         return isl_local_space_free(ls);
593         }
594
595         first += 1 + isl_local_space_offset(ls, type);
596         ls->div = isl_mat_drop_cols(ls->div, first, n);
597         if (!ls->div)
598                 return isl_local_space_free(ls);
599
600         return ls;
601 }
602
603 __isl_give isl_local_space *isl_local_space_insert_dims(
604         __isl_take isl_local_space *ls,
605         enum isl_dim_type type, unsigned first, unsigned n)
606 {
607         isl_ctx *ctx;
608
609         if (!ls)
610                 return NULL;
611         if (n == 0 && !isl_local_space_is_named_or_nested(ls, type))
612                 return ls;
613
614         ctx = isl_local_space_get_ctx(ls);
615         if (first > isl_local_space_dim(ls, type))
616                 isl_die(ctx, isl_error_invalid, "position out of bounds",
617                         return isl_local_space_free(ls));
618
619         ls = isl_local_space_cow(ls);
620         if (!ls)
621                 return NULL;
622
623         if (type == isl_dim_div) {
624                 ls->div = isl_mat_insert_zero_rows(ls->div, first, n);
625         } else {
626                 ls->dim = isl_space_insert_dims(ls->dim, type, first, n);
627                 if (!ls->dim)
628                         return isl_local_space_free(ls);
629         }
630
631         first += 1 + isl_local_space_offset(ls, type);
632         ls->div = isl_mat_insert_zero_cols(ls->div, first, n);
633         if (!ls->div)
634                 return isl_local_space_free(ls);
635
636         return ls;
637 }
638
639 /* Check if the constraints pointed to by "constraint" is a div
640  * constraint corresponding to div "div" in "ls".
641  *
642  * That is, if div = floor(f/m), then check if the constraint is
643  *
644  *              f - m d >= 0
645  * or
646  *              -(f-(m-1)) + m d >= 0
647  */
648 int isl_local_space_is_div_constraint(__isl_keep isl_local_space *ls,
649         isl_int *constraint, unsigned div)
650 {
651         unsigned pos;
652
653         if (!ls)
654                 return -1;
655
656         if (isl_int_is_zero(ls->div->row[div][0]))
657                 return 0;
658
659         pos = isl_local_space_offset(ls, isl_dim_div) + div;
660
661         if (isl_int_eq(constraint[pos], ls->div->row[div][0])) {
662                 int neg;
663                 isl_int_sub(ls->div->row[div][1],
664                                 ls->div->row[div][1], ls->div->row[div][0]);
665                 isl_int_add_ui(ls->div->row[div][1], ls->div->row[div][1], 1);
666                 neg = isl_seq_is_neg(constraint, ls->div->row[div]+1, pos);
667                 isl_int_sub_ui(ls->div->row[div][1], ls->div->row[div][1], 1);
668                 isl_int_add(ls->div->row[div][1],
669                                 ls->div->row[div][1], ls->div->row[div][0]);
670                 if (!neg)
671                         return 0;
672                 if (isl_seq_first_non_zero(constraint+pos+1,
673                                             ls->div->n_row-div-1) != -1)
674                         return 0;
675         } else if (isl_int_abs_eq(constraint[pos], ls->div->row[div][0])) {
676                 if (!isl_seq_eq(constraint, ls->div->row[div]+1, pos))
677                         return 0;
678                 if (isl_seq_first_non_zero(constraint+pos+1,
679                                             ls->div->n_row-div-1) != -1)
680                         return 0;
681         } else
682                 return 0;
683
684         return 1;
685 }
686
687 /*
688  * Set active[i] to 1 if the dimension at position i is involved
689  * in the linear expression l.
690  */
691 int *isl_local_space_get_active(__isl_keep isl_local_space *ls, isl_int *l)
692 {
693         int i, j;
694         isl_ctx *ctx;
695         int *active = NULL;
696         unsigned total;
697         unsigned offset;
698
699         ctx = isl_local_space_get_ctx(ls);
700         total = isl_local_space_dim(ls, isl_dim_all);
701         active = isl_calloc_array(ctx, int, total);
702         if (!active)
703                 return NULL;
704
705         for (i = 0; i < total; ++i)
706                 active[i] = !isl_int_is_zero(l[i]);
707
708         offset = isl_local_space_offset(ls, isl_dim_div) - 1;
709         for (i = ls->div->n_row - 1; i >= 0; --i) {
710                 if (!active[offset + i])
711                         continue;
712                 for (j = 0; j < total; ++j)
713                         active[j] |= !isl_int_is_zero(ls->div->row[i][2 + j]);
714         }
715
716         return active;
717 }