| 1 | /* |
| 2 | * Copyright 2008-2009 Katholieke Universiteit Leuven |
| 3 | * |
| 4 | * Use of this software is governed by the MIT license |
| 5 | * |
| 6 | * Written by Sven Verdoolaege, K.U.Leuven, Departement |
| 7 | * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium |
| 8 | */ |
| 9 | |
| 10 | #include <isl_map_private.h> |
| 11 | #include <isl_seq.h> |
| 12 | #include <isl/set.h> |
| 13 | #include <isl/map.h> |
| 14 | #include "isl_tab.h" |
| 15 | #include <isl_point_private.h> |
| 16 | #include <isl_vec_private.h> |
| 17 | |
| 18 | #include <set_to_map.c> |
| 19 | #include <set_from_map.c> |
| 20 | |
| 21 | /* Expand the constraint "c" into "v". The initial "dim" dimensions |
| 22 | * are the same, but "v" may have more divs than "c" and the divs of "c" |
| 23 | * may appear in different positions in "v". |
| 24 | * The number of divs in "c" is given by "n_div" and the mapping |
| 25 | * of divs in "c" to divs in "v" is given by "div_map". |
| 26 | * |
| 27 | * Although it shouldn't happen in practice, it is theoretically |
| 28 | * possible that two or more divs in "c" are mapped to the same div in "v". |
| 29 | * These divs are then necessarily the same, so we simply add their |
| 30 | * coefficients. |
| 31 | */ |
| 32 | static void expand_constraint(isl_vec *v, unsigned dim, |
| 33 | isl_int *c, int *div_map, unsigned n_div) |
| 34 | { |
| 35 | int i; |
| 36 | |
| 37 | isl_seq_cpy(dst: v->el, src: c, len: 1 + dim); |
| 38 | isl_seq_clr(p: v->el + 1 + dim, len: v->size - (1 + dim)); |
| 39 | |
| 40 | for (i = 0; i < n_div; ++i) { |
| 41 | int pos = 1 + dim + div_map[i]; |
| 42 | isl_int_add(v->el[pos], v->el[pos], c[1 + dim + i]); |
| 43 | } |
| 44 | } |
| 45 | |
| 46 | /* Add all constraints of bmap to tab. The equalities of bmap |
| 47 | * are added as a pair of inequalities. |
| 48 | */ |
| 49 | static isl_stat tab_add_constraints(struct isl_tab *tab, |
| 50 | __isl_keep isl_basic_map *bmap, int *div_map) |
| 51 | { |
| 52 | int i; |
| 53 | unsigned dim; |
| 54 | isl_size tab_total; |
| 55 | isl_size bmap_n_div; |
| 56 | isl_size bmap_total; |
| 57 | isl_vec *v; |
| 58 | |
| 59 | if (!tab || !bmap) |
| 60 | return isl_stat_error; |
| 61 | |
| 62 | tab_total = isl_basic_map_dim(bmap: tab->bmap, type: isl_dim_all); |
| 63 | bmap_total = isl_basic_map_dim(bmap, type: isl_dim_all); |
| 64 | bmap_n_div = isl_basic_map_dim(bmap, type: isl_dim_div); |
| 65 | dim = bmap_total - bmap_n_div; |
| 66 | if (tab_total < 0 || bmap_total < 0 || bmap_n_div < 0) |
| 67 | return isl_stat_error; |
| 68 | |
| 69 | if (isl_tab_extend_cons(tab, n_new: 2 * bmap->n_eq + bmap->n_ineq) < 0) |
| 70 | return isl_stat_error; |
| 71 | |
| 72 | v = isl_vec_alloc(ctx: bmap->ctx, size: 1 + tab_total); |
| 73 | if (!v) |
| 74 | return isl_stat_error; |
| 75 | |
| 76 | for (i = 0; i < bmap->n_eq; ++i) { |
| 77 | expand_constraint(v, dim, c: bmap->eq[i], div_map, n_div: bmap_n_div); |
| 78 | if (isl_tab_add_ineq(tab, ineq: v->el) < 0) |
| 79 | goto error; |
| 80 | isl_seq_neg(dst: bmap->eq[i], src: bmap->eq[i], len: 1 + bmap_total); |
| 81 | expand_constraint(v, dim, c: bmap->eq[i], div_map, n_div: bmap_n_div); |
| 82 | if (isl_tab_add_ineq(tab, ineq: v->el) < 0) |
| 83 | goto error; |
| 84 | isl_seq_neg(dst: bmap->eq[i], src: bmap->eq[i], len: 1 + bmap_total); |
| 85 | if (tab->empty) |
| 86 | break; |
| 87 | } |
| 88 | |
| 89 | for (i = 0; i < bmap->n_ineq; ++i) { |
| 90 | expand_constraint(v, dim, c: bmap->ineq[i], div_map, n_div: bmap_n_div); |
| 91 | if (isl_tab_add_ineq(tab, ineq: v->el) < 0) |
| 92 | goto error; |
| 93 | if (tab->empty) |
| 94 | break; |
| 95 | } |
| 96 | |
| 97 | isl_vec_free(vec: v); |
| 98 | return isl_stat_ok; |
| 99 | error: |
| 100 | isl_vec_free(vec: v); |
| 101 | return isl_stat_error; |
| 102 | } |
| 103 | |
| 104 | /* Add a specific constraint of bmap (or its opposite) to tab. |
| 105 | * The position of the constraint is specified by "c", where |
| 106 | * the equalities of bmap are counted twice, once for the inequality |
| 107 | * that is equal to the equality, and once for its negation. |
| 108 | * |
| 109 | * Each of these constraints has been added to "tab" before by |
| 110 | * tab_add_constraints (and later removed again), so there should |
| 111 | * already be a row available for the constraint. |
| 112 | */ |
| 113 | static isl_stat tab_add_constraint(struct isl_tab *tab, |
| 114 | __isl_keep isl_basic_map *bmap, int *div_map, int c, int oppose) |
| 115 | { |
| 116 | unsigned dim; |
| 117 | isl_size tab_total; |
| 118 | isl_size bmap_n_div; |
| 119 | isl_size bmap_total; |
| 120 | isl_vec *v; |
| 121 | isl_stat r; |
| 122 | |
| 123 | if (!tab || !bmap) |
| 124 | return isl_stat_error; |
| 125 | |
| 126 | tab_total = isl_basic_map_dim(bmap: tab->bmap, type: isl_dim_all); |
| 127 | bmap_total = isl_basic_map_dim(bmap, type: isl_dim_all); |
| 128 | bmap_n_div = isl_basic_map_dim(bmap, type: isl_dim_div); |
| 129 | dim = bmap_total - bmap_n_div; |
| 130 | if (tab_total < 0 || bmap_total < 0 || bmap_n_div < 0) |
| 131 | return isl_stat_error; |
| 132 | |
| 133 | v = isl_vec_alloc(ctx: bmap->ctx, size: 1 + tab_total); |
| 134 | if (!v) |
| 135 | return isl_stat_error; |
| 136 | |
| 137 | if (c < 2 * bmap->n_eq) { |
| 138 | if ((c % 2) != oppose) |
| 139 | isl_seq_neg(dst: bmap->eq[c/2], src: bmap->eq[c/2], |
| 140 | len: 1 + bmap_total); |
| 141 | if (oppose) |
| 142 | isl_int_sub_ui(bmap->eq[c/2][0], bmap->eq[c/2][0], 1); |
| 143 | expand_constraint(v, dim, c: bmap->eq[c/2], div_map, n_div: bmap_n_div); |
| 144 | r = isl_tab_add_ineq(tab, ineq: v->el); |
| 145 | if (oppose) |
| 146 | isl_int_add_ui(bmap->eq[c/2][0], bmap->eq[c/2][0], 1); |
| 147 | if ((c % 2) != oppose) |
| 148 | isl_seq_neg(dst: bmap->eq[c/2], src: bmap->eq[c/2], |
| 149 | len: 1 + bmap_total); |
| 150 | } else { |
| 151 | c -= 2 * bmap->n_eq; |
| 152 | if (oppose) { |
| 153 | isl_seq_neg(dst: bmap->ineq[c], src: bmap->ineq[c], |
| 154 | len: 1 + bmap_total); |
| 155 | isl_int_sub_ui(bmap->ineq[c][0], bmap->ineq[c][0], 1); |
| 156 | } |
| 157 | expand_constraint(v, dim, c: bmap->ineq[c], div_map, n_div: bmap_n_div); |
| 158 | r = isl_tab_add_ineq(tab, ineq: v->el); |
| 159 | if (oppose) { |
| 160 | isl_int_add_ui(bmap->ineq[c][0], bmap->ineq[c][0], 1); |
| 161 | isl_seq_neg(dst: bmap->ineq[c], src: bmap->ineq[c], |
| 162 | len: 1 + bmap_total); |
| 163 | } |
| 164 | } |
| 165 | |
| 166 | isl_vec_free(vec: v); |
| 167 | return r; |
| 168 | } |
| 169 | |
| 170 | static isl_stat tab_add_divs(struct isl_tab *tab, |
| 171 | __isl_keep isl_basic_map *bmap, int **div_map) |
| 172 | { |
| 173 | int i, j; |
| 174 | struct isl_vec *vec; |
| 175 | isl_size total; |
| 176 | unsigned dim; |
| 177 | |
| 178 | if (!bmap) |
| 179 | return isl_stat_error; |
| 180 | if (!bmap->n_div) |
| 181 | return isl_stat_ok; |
| 182 | |
| 183 | if (!*div_map) |
| 184 | *div_map = isl_alloc_array(bmap->ctx, int, bmap->n_div); |
| 185 | if (!*div_map) |
| 186 | return isl_stat_error; |
| 187 | |
| 188 | total = isl_basic_map_dim(bmap: tab->bmap, type: isl_dim_all); |
| 189 | if (total < 0) |
| 190 | return isl_stat_error; |
| 191 | dim = total - tab->bmap->n_div; |
| 192 | vec = isl_vec_alloc(ctx: bmap->ctx, size: 2 + total + bmap->n_div); |
| 193 | if (!vec) |
| 194 | return isl_stat_error; |
| 195 | |
| 196 | for (i = 0; i < bmap->n_div; ++i) { |
| 197 | isl_seq_cpy(dst: vec->el, src: bmap->div[i], len: 2 + dim); |
| 198 | isl_seq_clr(p: vec->el + 2 + dim, len: tab->bmap->n_div); |
| 199 | for (j = 0; j < i; ++j) |
| 200 | isl_int_add(vec->el[2 + dim + (*div_map)[j]], |
| 201 | vec->el[2 + dim + (*div_map)[j]], |
| 202 | bmap->div[i][2 + dim + j]); |
| 203 | for (j = 0; j < tab->bmap->n_div; ++j) |
| 204 | if (isl_seq_eq(p1: tab->bmap->div[j], |
| 205 | p2: vec->el, len: 2 + dim + tab->bmap->n_div)) |
| 206 | break; |
| 207 | (*div_map)[i] = j; |
| 208 | if (j == tab->bmap->n_div) { |
| 209 | vec->size = 2 + dim + tab->bmap->n_div; |
| 210 | if (isl_tab_add_div(tab, div: vec) < 0) |
| 211 | goto error; |
| 212 | } |
| 213 | } |
| 214 | |
| 215 | isl_vec_free(vec); |
| 216 | |
| 217 | return isl_stat_ok; |
| 218 | error: |
| 219 | isl_vec_free(vec); |
| 220 | |
| 221 | return isl_stat_error; |
| 222 | } |
| 223 | |
| 224 | /* Freeze all constraints of tableau tab. |
| 225 | */ |
| 226 | static int tab_freeze_constraints(struct isl_tab *tab) |
| 227 | { |
| 228 | int i; |
| 229 | |
| 230 | for (i = 0; i < tab->n_con; ++i) |
| 231 | if (isl_tab_freeze_constraint(tab, con: i) < 0) |
| 232 | return -1; |
| 233 | |
| 234 | return 0; |
| 235 | } |
| 236 | |
| 237 | /* Check for redundant constraints starting at offset. |
| 238 | * Put the indices of the non-redundant constraints in index |
| 239 | * and return the number of non-redundant constraints. |
| 240 | */ |
| 241 | static int n_non_redundant(isl_ctx *ctx, struct isl_tab *tab, |
| 242 | int offset, int **index) |
| 243 | { |
| 244 | int i, n; |
| 245 | int n_test = tab->n_con - offset; |
| 246 | |
| 247 | if (isl_tab_detect_redundant(tab) < 0) |
| 248 | return -1; |
| 249 | |
| 250 | if (n_test == 0) |
| 251 | return 0; |
| 252 | if (!*index) |
| 253 | *index = isl_alloc_array(ctx, int, n_test); |
| 254 | if (!*index) |
| 255 | return -1; |
| 256 | |
| 257 | for (n = 0, i = 0; i < n_test; ++i) { |
| 258 | int r; |
| 259 | r = isl_tab_is_redundant(tab, con: offset + i); |
| 260 | if (r < 0) |
| 261 | return -1; |
| 262 | if (r) |
| 263 | continue; |
| 264 | (*index)[n++] = i; |
| 265 | } |
| 266 | |
| 267 | return n; |
| 268 | } |
| 269 | |
| 270 | /* basic_map_collect_diff calls add on each of the pieces of |
| 271 | * the set difference between bmap and map until the add method |
| 272 | * return a negative value. |
| 273 | */ |
| 274 | struct isl_diff_collector { |
| 275 | isl_stat (*add)(struct isl_diff_collector *dc, |
| 276 | __isl_take isl_basic_map *bmap); |
| 277 | }; |
| 278 | |
| 279 | /* Compute the set difference between bmap and map and call |
| 280 | * dc->add on each of the piece until this function returns |
| 281 | * a negative value. |
| 282 | * Return 0 on success and -1 on error. dc->add returning |
| 283 | * a negative value is treated as an error, but the calling |
| 284 | * function can interpret the results based on the state of dc. |
| 285 | * |
| 286 | * Assumes that map has known divs. |
| 287 | * |
| 288 | * The difference is computed by a backtracking algorithm. |
| 289 | * Each level corresponds to a basic map in "map". |
| 290 | * When a node in entered for the first time, we check |
| 291 | * if the corresonding basic map intersects the current piece |
| 292 | * of "bmap". If not, we move to the next level. |
| 293 | * Otherwise, we split the current piece into as many |
| 294 | * pieces as there are non-redundant constraints of the current |
| 295 | * basic map in the intersection. Each of these pieces is |
| 296 | * handled by a child of the current node. |
| 297 | * In particular, if there are n non-redundant constraints, |
| 298 | * then for each 0 <= i < n, a piece is cut off by adding |
| 299 | * constraints 0 <= j < i and adding the opposite of constraint i. |
| 300 | * If there are no non-redundant constraints, meaning that the current |
| 301 | * piece is a subset of the current basic map, then we simply backtrack. |
| 302 | * |
| 303 | * In the leaves, we check if the remaining piece has any integer points |
| 304 | * and if so, pass it along to dc->add. As a special case, if nothing |
| 305 | * has been removed when we end up in a leaf, we simply pass along |
| 306 | * the original basic map. |
| 307 | */ |
| 308 | static isl_stat basic_map_collect_diff(__isl_take isl_basic_map *bmap, |
| 309 | __isl_take isl_map *map, struct isl_diff_collector *dc) |
| 310 | { |
| 311 | int i; |
| 312 | int modified; |
| 313 | int level; |
| 314 | int init; |
| 315 | isl_bool empty; |
| 316 | isl_ctx *ctx; |
| 317 | struct isl_tab *tab = NULL; |
| 318 | struct isl_tab_undo **snap = NULL; |
| 319 | int *k = NULL; |
| 320 | int *n = NULL; |
| 321 | int **index = NULL; |
| 322 | int **div_map = NULL; |
| 323 | |
| 324 | empty = isl_basic_map_is_empty(bmap); |
| 325 | if (empty) { |
| 326 | isl_basic_map_free(bmap); |
| 327 | isl_map_free(map); |
| 328 | return empty < 0 ? isl_stat_error : isl_stat_ok; |
| 329 | } |
| 330 | |
| 331 | bmap = isl_basic_map_cow(bmap); |
| 332 | map = isl_map_cow(map); |
| 333 | |
| 334 | if (!bmap || !map) |
| 335 | goto error; |
| 336 | |
| 337 | ctx = map->ctx; |
| 338 | snap = isl_alloc_array(map->ctx, struct isl_tab_undo *, map->n); |
| 339 | k = isl_alloc_array(map->ctx, int, map->n); |
| 340 | n = isl_alloc_array(map->ctx, int, map->n); |
| 341 | index = isl_calloc_array(map->ctx, int *, map->n); |
| 342 | div_map = isl_calloc_array(map->ctx, int *, map->n); |
| 343 | if (!snap || !k || !n || !index || !div_map) |
| 344 | goto error; |
| 345 | |
| 346 | bmap = isl_basic_map_order_divs(bmap); |
| 347 | map = isl_map_order_divs(map); |
| 348 | |
| 349 | tab = isl_tab_from_basic_map(bmap, track: 1); |
| 350 | if (!tab) |
| 351 | goto error; |
| 352 | |
| 353 | modified = 0; |
| 354 | level = 0; |
| 355 | init = 1; |
| 356 | |
| 357 | while (level >= 0) { |
| 358 | if (level >= map->n) { |
| 359 | int empty; |
| 360 | struct isl_basic_map *bm; |
| 361 | if (!modified) { |
| 362 | if (dc->add(dc, isl_basic_map_copy(bmap)) < 0) |
| 363 | goto error; |
| 364 | break; |
| 365 | } |
| 366 | bm = isl_basic_map_copy(bmap: tab->bmap); |
| 367 | bm = isl_basic_map_cow(bmap: bm); |
| 368 | bm = isl_basic_map_update_from_tab(bmap: bm, tab); |
| 369 | bm = isl_basic_map_simplify(bmap: bm); |
| 370 | bm = isl_basic_map_finalize(bmap: bm); |
| 371 | empty = isl_basic_map_is_empty(bmap: bm); |
| 372 | if (empty) |
| 373 | isl_basic_map_free(bmap: bm); |
| 374 | else if (dc->add(dc, bm) < 0) |
| 375 | goto error; |
| 376 | if (empty < 0) |
| 377 | goto error; |
| 378 | level--; |
| 379 | init = 0; |
| 380 | continue; |
| 381 | } |
| 382 | if (init) { |
| 383 | int offset; |
| 384 | struct isl_tab_undo *snap2; |
| 385 | snap2 = isl_tab_snap(tab); |
| 386 | if (tab_add_divs(tab, bmap: map->p[level], |
| 387 | div_map: &div_map[level]) < 0) |
| 388 | goto error; |
| 389 | offset = tab->n_con; |
| 390 | snap[level] = isl_tab_snap(tab); |
| 391 | if (tab_freeze_constraints(tab) < 0) |
| 392 | goto error; |
| 393 | if (tab_add_constraints(tab, bmap: map->p[level], |
| 394 | div_map: div_map[level]) < 0) |
| 395 | goto error; |
| 396 | k[level] = 0; |
| 397 | n[level] = 0; |
| 398 | if (tab->empty) { |
| 399 | if (isl_tab_rollback(tab, snap: snap2) < 0) |
| 400 | goto error; |
| 401 | level++; |
| 402 | continue; |
| 403 | } |
| 404 | modified = 1; |
| 405 | n[level] = n_non_redundant(ctx, tab, offset, |
| 406 | index: &index[level]); |
| 407 | if (n[level] < 0) |
| 408 | goto error; |
| 409 | if (n[level] == 0) { |
| 410 | level--; |
| 411 | init = 0; |
| 412 | continue; |
| 413 | } |
| 414 | if (isl_tab_rollback(tab, snap: snap[level]) < 0) |
| 415 | goto error; |
| 416 | if (tab_add_constraint(tab, bmap: map->p[level], |
| 417 | div_map: div_map[level], c: index[level][0], oppose: 1) < 0) |
| 418 | goto error; |
| 419 | level++; |
| 420 | continue; |
| 421 | } else { |
| 422 | if (k[level] + 1 >= n[level]) { |
| 423 | level--; |
| 424 | continue; |
| 425 | } |
| 426 | if (isl_tab_rollback(tab, snap: snap[level]) < 0) |
| 427 | goto error; |
| 428 | if (tab_add_constraint(tab, bmap: map->p[level], |
| 429 | div_map: div_map[level], |
| 430 | c: index[level][k[level]], oppose: 0) < 0) |
| 431 | goto error; |
| 432 | snap[level] = isl_tab_snap(tab); |
| 433 | k[level]++; |
| 434 | if (tab_add_constraint(tab, bmap: map->p[level], |
| 435 | div_map: div_map[level], |
| 436 | c: index[level][k[level]], oppose: 1) < 0) |
| 437 | goto error; |
| 438 | level++; |
| 439 | init = 1; |
| 440 | continue; |
| 441 | } |
| 442 | } |
| 443 | |
| 444 | isl_tab_free(tab); |
| 445 | free(ptr: snap); |
| 446 | free(ptr: n); |
| 447 | free(ptr: k); |
| 448 | for (i = 0; index && i < map->n; ++i) |
| 449 | free(ptr: index[i]); |
| 450 | free(ptr: index); |
| 451 | for (i = 0; div_map && i < map->n; ++i) |
| 452 | free(ptr: div_map[i]); |
| 453 | free(ptr: div_map); |
| 454 | |
| 455 | isl_basic_map_free(bmap); |
| 456 | isl_map_free(map); |
| 457 | |
| 458 | return isl_stat_ok; |
| 459 | error: |
| 460 | isl_tab_free(tab); |
| 461 | free(ptr: snap); |
| 462 | free(ptr: n); |
| 463 | free(ptr: k); |
| 464 | for (i = 0; index && i < map->n; ++i) |
| 465 | free(ptr: index[i]); |
| 466 | free(ptr: index); |
| 467 | for (i = 0; div_map && i < map->n; ++i) |
| 468 | free(ptr: div_map[i]); |
| 469 | free(ptr: div_map); |
| 470 | isl_basic_map_free(bmap); |
| 471 | isl_map_free(map); |
| 472 | return isl_stat_error; |
| 473 | } |
| 474 | |
| 475 | /* A diff collector that actually collects all parts of the |
| 476 | * set difference in the field diff. |
| 477 | */ |
| 478 | struct isl_subtract_diff_collector { |
| 479 | struct isl_diff_collector dc; |
| 480 | struct isl_map *diff; |
| 481 | }; |
| 482 | |
| 483 | /* isl_subtract_diff_collector callback. |
| 484 | */ |
| 485 | static isl_stat basic_map_subtract_add(struct isl_diff_collector *dc, |
| 486 | __isl_take isl_basic_map *bmap) |
| 487 | { |
| 488 | struct isl_subtract_diff_collector *sdc; |
| 489 | sdc = (struct isl_subtract_diff_collector *)dc; |
| 490 | |
| 491 | sdc->diff = isl_map_union_disjoint(map1: sdc->diff, |
| 492 | map2: isl_map_from_basic_map(bmap)); |
| 493 | |
| 494 | return sdc->diff ? isl_stat_ok : isl_stat_error; |
| 495 | } |
| 496 | |
| 497 | /* Return the set difference between bmap and map. |
| 498 | */ |
| 499 | static __isl_give isl_map *basic_map_subtract(__isl_take isl_basic_map *bmap, |
| 500 | __isl_take isl_map *map) |
| 501 | { |
| 502 | struct isl_subtract_diff_collector sdc; |
| 503 | sdc.dc.add = &basic_map_subtract_add; |
| 504 | sdc.diff = isl_map_empty(space: isl_basic_map_get_space(bmap)); |
| 505 | if (basic_map_collect_diff(bmap, map, dc: &sdc.dc) < 0) { |
| 506 | isl_map_free(map: sdc.diff); |
| 507 | sdc.diff = NULL; |
| 508 | } |
| 509 | return sdc.diff; |
| 510 | } |
| 511 | |
| 512 | /* Return an empty map living in the same space as "map1" and "map2". |
| 513 | */ |
| 514 | static __isl_give isl_map *replace_pair_by_empty( __isl_take isl_map *map1, |
| 515 | __isl_take isl_map *map2) |
| 516 | { |
| 517 | isl_space *space; |
| 518 | |
| 519 | space = isl_map_get_space(map: map1); |
| 520 | isl_map_free(map: map1); |
| 521 | isl_map_free(map: map2); |
| 522 | return isl_map_empty(space); |
| 523 | } |
| 524 | |
| 525 | /* Return the set difference between map1 and map2. |
| 526 | * (U_i A_i) \ (U_j B_j) is computed as U_i (A_i \ (U_j B_j)) |
| 527 | * |
| 528 | * If "map1" and "map2" are obviously equal to each other, |
| 529 | * then return an empty map in the same space. |
| 530 | * |
| 531 | * If "map1" and "map2" are disjoint, then simply return "map1". |
| 532 | */ |
| 533 | __isl_give isl_map *isl_map_subtract( __isl_take isl_map *map1, |
| 534 | __isl_take isl_map *map2) |
| 535 | { |
| 536 | int i; |
| 537 | int equal, disjoint; |
| 538 | struct isl_map *diff; |
| 539 | |
| 540 | if (isl_map_align_params_bin(map1: &map1, map2: &map2) < 0) |
| 541 | goto error; |
| 542 | if (isl_map_check_equal_space(map1, map2) < 0) |
| 543 | goto error; |
| 544 | |
| 545 | equal = isl_map_plain_is_equal(map1, map2); |
| 546 | if (equal < 0) |
| 547 | goto error; |
| 548 | if (equal) |
| 549 | return replace_pair_by_empty(map1, map2); |
| 550 | |
| 551 | disjoint = isl_map_is_disjoint(map1, map2); |
| 552 | if (disjoint < 0) |
| 553 | goto error; |
| 554 | if (disjoint) { |
| 555 | isl_map_free(map: map2); |
| 556 | return map1; |
| 557 | } |
| 558 | |
| 559 | map1 = isl_map_compute_divs(map: map1); |
| 560 | map2 = isl_map_compute_divs(map: map2); |
| 561 | if (!map1 || !map2) |
| 562 | goto error; |
| 563 | |
| 564 | map1 = isl_map_remove_empty_parts(map: map1); |
| 565 | map2 = isl_map_remove_empty_parts(map: map2); |
| 566 | |
| 567 | diff = isl_map_empty(space: isl_map_get_space(map: map1)); |
| 568 | for (i = 0; i < map1->n; ++i) { |
| 569 | struct isl_map *d; |
| 570 | d = basic_map_subtract(bmap: isl_basic_map_copy(bmap: map1->p[i]), |
| 571 | map: isl_map_copy(map: map2)); |
| 572 | if (ISL_F_ISSET(map1, ISL_MAP_DISJOINT)) |
| 573 | diff = isl_map_union_disjoint(map1: diff, map2: d); |
| 574 | else |
| 575 | diff = isl_map_union(map1: diff, map2: d); |
| 576 | } |
| 577 | |
| 578 | isl_map_free(map: map1); |
| 579 | isl_map_free(map: map2); |
| 580 | |
| 581 | return diff; |
| 582 | error: |
| 583 | isl_map_free(map: map1); |
| 584 | isl_map_free(map: map2); |
| 585 | return NULL; |
| 586 | } |
| 587 | |
| 588 | __isl_give isl_set *isl_set_subtract(__isl_take isl_set *set1, |
| 589 | __isl_take isl_set *set2) |
| 590 | { |
| 591 | return set_from_map(isl_map_subtract(map1: set_to_map(set1), |
| 592 | map2: set_to_map(set2))); |
| 593 | } |
| 594 | |
| 595 | /* Remove the elements of "dom" from the domain of "map". |
| 596 | */ |
| 597 | __isl_give isl_map *isl_map_subtract_domain(__isl_take isl_map *map, |
| 598 | __isl_take isl_set *dom) |
| 599 | { |
| 600 | isl_bool ok; |
| 601 | isl_map *ext_dom; |
| 602 | |
| 603 | isl_map_align_params_set(map: &map, set: &dom); |
| 604 | ok = isl_map_compatible_domain(map, set: dom); |
| 605 | if (ok < 0) |
| 606 | goto error; |
| 607 | if (!ok) |
| 608 | isl_die(isl_set_get_ctx(dom), isl_error_invalid, |
| 609 | "incompatible spaces" , goto error); |
| 610 | |
| 611 | ext_dom = isl_map_universe(space: isl_map_get_space(map)); |
| 612 | ext_dom = isl_map_intersect_domain(map: ext_dom, set: dom); |
| 613 | return isl_map_subtract(map1: map, map2: ext_dom); |
| 614 | error: |
| 615 | isl_map_free(map); |
| 616 | isl_set_free(set: dom); |
| 617 | return NULL; |
| 618 | } |
| 619 | |
| 620 | /* Remove the elements of "dom" from the range of "map". |
| 621 | */ |
| 622 | __isl_give isl_map *isl_map_subtract_range(__isl_take isl_map *map, |
| 623 | __isl_take isl_set *dom) |
| 624 | { |
| 625 | isl_bool ok; |
| 626 | isl_map *ext_dom; |
| 627 | |
| 628 | isl_map_align_params_set(map: &map, set: &dom); |
| 629 | ok = isl_map_compatible_range(map, set: dom); |
| 630 | if (ok < 0) |
| 631 | goto error; |
| 632 | if (!ok) |
| 633 | isl_die(isl_set_get_ctx(dom), isl_error_invalid, |
| 634 | "incompatible spaces" , goto error); |
| 635 | |
| 636 | ext_dom = isl_map_universe(space: isl_map_get_space(map)); |
| 637 | ext_dom = isl_map_intersect_range(map: ext_dom, set: dom); |
| 638 | return isl_map_subtract(map1: map, map2: ext_dom); |
| 639 | error: |
| 640 | isl_map_free(map); |
| 641 | isl_set_free(set: dom); |
| 642 | return NULL; |
| 643 | } |
| 644 | |
| 645 | /* A diff collector that aborts as soon as its add function is called, |
| 646 | * setting empty to isl_false. |
| 647 | */ |
| 648 | struct isl_is_empty_diff_collector { |
| 649 | struct isl_diff_collector dc; |
| 650 | isl_bool empty; |
| 651 | }; |
| 652 | |
| 653 | /* isl_is_empty_diff_collector callback. |
| 654 | */ |
| 655 | static isl_stat basic_map_is_empty_add(struct isl_diff_collector *dc, |
| 656 | __isl_take isl_basic_map *bmap) |
| 657 | { |
| 658 | struct isl_is_empty_diff_collector *edc; |
| 659 | edc = (struct isl_is_empty_diff_collector *)dc; |
| 660 | |
| 661 | edc->empty = isl_bool_false; |
| 662 | |
| 663 | isl_basic_map_free(bmap); |
| 664 | return isl_stat_error; |
| 665 | } |
| 666 | |
| 667 | /* Check if bmap \ map is empty by computing this set difference |
| 668 | * and breaking off as soon as the difference is known to be non-empty. |
| 669 | */ |
| 670 | static isl_bool basic_map_diff_is_empty(__isl_keep isl_basic_map *bmap, |
| 671 | __isl_keep isl_map *map) |
| 672 | { |
| 673 | isl_bool empty; |
| 674 | isl_stat r; |
| 675 | struct isl_is_empty_diff_collector edc; |
| 676 | |
| 677 | empty = isl_basic_map_plain_is_empty(bmap); |
| 678 | if (empty) |
| 679 | return empty; |
| 680 | |
| 681 | edc.dc.add = &basic_map_is_empty_add; |
| 682 | edc.empty = isl_bool_true; |
| 683 | r = basic_map_collect_diff(bmap: isl_basic_map_copy(bmap), |
| 684 | map: isl_map_copy(map), dc: &edc.dc); |
| 685 | if (!edc.empty) |
| 686 | return isl_bool_false; |
| 687 | |
| 688 | return r < 0 ? isl_bool_error : isl_bool_true; |
| 689 | } |
| 690 | |
| 691 | /* Check if map1 \ map2 is empty by checking if the set difference is empty |
| 692 | * for each of the basic maps in map1. |
| 693 | */ |
| 694 | static isl_bool map_diff_is_empty(__isl_keep isl_map *map1, |
| 695 | __isl_keep isl_map *map2) |
| 696 | { |
| 697 | int i; |
| 698 | isl_bool is_empty = isl_bool_true; |
| 699 | |
| 700 | if (!map1 || !map2) |
| 701 | return isl_bool_error; |
| 702 | |
| 703 | for (i = 0; i < map1->n; ++i) { |
| 704 | is_empty = basic_map_diff_is_empty(bmap: map1->p[i], map: map2); |
| 705 | if (is_empty < 0 || !is_empty) |
| 706 | break; |
| 707 | } |
| 708 | |
| 709 | return is_empty; |
| 710 | } |
| 711 | |
| 712 | /* Return true if "bmap" contains a single element. |
| 713 | */ |
| 714 | isl_bool isl_basic_map_plain_is_singleton(__isl_keep isl_basic_map *bmap) |
| 715 | { |
| 716 | isl_size total; |
| 717 | |
| 718 | if (!bmap) |
| 719 | return isl_bool_error; |
| 720 | if (bmap->n_div) |
| 721 | return isl_bool_false; |
| 722 | if (bmap->n_ineq) |
| 723 | return isl_bool_false; |
| 724 | total = isl_basic_map_dim(bmap, type: isl_dim_all); |
| 725 | if (total < 0) |
| 726 | return isl_bool_error; |
| 727 | return bmap->n_eq == total; |
| 728 | } |
| 729 | |
| 730 | /* Return true if "map" contains a single element. |
| 731 | */ |
| 732 | isl_bool isl_map_plain_is_singleton(__isl_keep isl_map *map) |
| 733 | { |
| 734 | if (!map) |
| 735 | return isl_bool_error; |
| 736 | if (map->n != 1) |
| 737 | return isl_bool_false; |
| 738 | |
| 739 | return isl_basic_map_plain_is_singleton(bmap: map->p[0]); |
| 740 | } |
| 741 | |
| 742 | /* Given a singleton basic map, extract the single element |
| 743 | * as an isl_point. |
| 744 | */ |
| 745 | static __isl_give isl_point *( |
| 746 | __isl_keep isl_basic_map *bmap) |
| 747 | { |
| 748 | int j; |
| 749 | isl_size dim; |
| 750 | struct isl_vec *point; |
| 751 | isl_int m; |
| 752 | |
| 753 | dim = isl_basic_map_dim(bmap, type: isl_dim_all); |
| 754 | if (dim < 0) |
| 755 | return NULL; |
| 756 | |
| 757 | isl_assert(bmap->ctx, bmap->n_eq == dim, return NULL); |
| 758 | point = isl_vec_alloc(ctx: bmap->ctx, size: 1 + dim); |
| 759 | if (!point) |
| 760 | return NULL; |
| 761 | |
| 762 | isl_int_init(m); |
| 763 | |
| 764 | isl_int_set_si(point->el[0], 1); |
| 765 | for (j = 0; j < bmap->n_eq; ++j) { |
| 766 | int i = dim - 1 - j; |
| 767 | isl_assert(bmap->ctx, |
| 768 | isl_seq_first_non_zero(bmap->eq[j] + 1, i) == -1, |
| 769 | goto error); |
| 770 | isl_assert(bmap->ctx, |
| 771 | isl_int_is_one(bmap->eq[j][1 + i]) || |
| 772 | isl_int_is_negone(bmap->eq[j][1 + i]), |
| 773 | goto error); |
| 774 | isl_assert(bmap->ctx, |
| 775 | isl_seq_first_non_zero(bmap->eq[j]+1+i+1, dim-i-1) == -1, |
| 776 | goto error); |
| 777 | |
| 778 | isl_int_gcd(m, point->el[0], bmap->eq[j][1 + i]); |
| 779 | isl_int_divexact(m, bmap->eq[j][1 + i], m); |
| 780 | isl_int_abs(m, m); |
| 781 | isl_seq_scale(dst: point->el, src: point->el, f: m, len: 1 + i); |
| 782 | isl_int_divexact(m, point->el[0], bmap->eq[j][1 + i]); |
| 783 | isl_int_neg(m, m); |
| 784 | isl_int_mul(point->el[1 + i], m, bmap->eq[j][0]); |
| 785 | } |
| 786 | |
| 787 | isl_int_clear(m); |
| 788 | return isl_point_alloc(space: isl_basic_map_get_space(bmap), vec: point); |
| 789 | error: |
| 790 | isl_int_clear(m); |
| 791 | isl_vec_free(vec: point); |
| 792 | return NULL; |
| 793 | } |
| 794 | |
| 795 | /* Return isl_bool_true if the singleton map "map1" is a subset of "map2", |
| 796 | * i.e., if the single element of "map1" is also an element of "map2". |
| 797 | * Assumes "map2" has known divs. |
| 798 | */ |
| 799 | static isl_bool map_is_singleton_subset(__isl_keep isl_map *map1, |
| 800 | __isl_keep isl_map *map2) |
| 801 | { |
| 802 | int i; |
| 803 | isl_bool is_subset = isl_bool_false; |
| 804 | struct isl_point *point; |
| 805 | |
| 806 | if (!map1 || !map2) |
| 807 | return isl_bool_error; |
| 808 | if (map1->n != 1) |
| 809 | isl_die(isl_map_get_ctx(map1), isl_error_invalid, |
| 810 | "expecting single-disjunct input" , |
| 811 | return isl_bool_error); |
| 812 | |
| 813 | point = singleton_extract_point(bmap: map1->p[0]); |
| 814 | if (!point) |
| 815 | return isl_bool_error; |
| 816 | |
| 817 | for (i = 0; i < map2->n; ++i) { |
| 818 | is_subset = isl_basic_map_contains_point(bmap: map2->p[i], point); |
| 819 | if (is_subset) |
| 820 | break; |
| 821 | } |
| 822 | |
| 823 | isl_point_free(pnt: point); |
| 824 | return is_subset; |
| 825 | } |
| 826 | |
| 827 | static isl_bool map_is_subset(__isl_keep isl_map *map1, |
| 828 | __isl_keep isl_map *map2) |
| 829 | { |
| 830 | isl_bool is_subset = isl_bool_false; |
| 831 | isl_bool empty, single; |
| 832 | isl_bool rat1, rat2; |
| 833 | |
| 834 | if (!map1 || !map2) |
| 835 | return isl_bool_error; |
| 836 | |
| 837 | if (!isl_map_has_equal_space(map1, map2)) |
| 838 | return isl_bool_false; |
| 839 | |
| 840 | empty = isl_map_is_empty(map: map1); |
| 841 | if (empty < 0) |
| 842 | return isl_bool_error; |
| 843 | if (empty) |
| 844 | return isl_bool_true; |
| 845 | |
| 846 | empty = isl_map_is_empty(map: map2); |
| 847 | if (empty < 0) |
| 848 | return isl_bool_error; |
| 849 | if (empty) |
| 850 | return isl_bool_false; |
| 851 | |
| 852 | rat1 = isl_map_has_rational(map: map1); |
| 853 | rat2 = isl_map_has_rational(map: map2); |
| 854 | if (rat1 < 0 || rat2 < 0) |
| 855 | return isl_bool_error; |
| 856 | if (rat1 && !rat2) |
| 857 | return isl_bool_false; |
| 858 | |
| 859 | if (isl_map_plain_is_universe(map: map2)) |
| 860 | return isl_bool_true; |
| 861 | |
| 862 | single = isl_map_plain_is_singleton(map: map1); |
| 863 | if (single < 0) |
| 864 | return isl_bool_error; |
| 865 | map2 = isl_map_compute_divs(map: isl_map_copy(map: map2)); |
| 866 | if (single) { |
| 867 | is_subset = map_is_singleton_subset(map1, map2); |
| 868 | isl_map_free(map: map2); |
| 869 | return is_subset; |
| 870 | } |
| 871 | is_subset = map_diff_is_empty(map1, map2); |
| 872 | isl_map_free(map: map2); |
| 873 | |
| 874 | return is_subset; |
| 875 | } |
| 876 | |
| 877 | isl_bool isl_map_is_subset(__isl_keep isl_map *map1, __isl_keep isl_map *map2) |
| 878 | { |
| 879 | return isl_map_align_params_map_map_and_test(map1, map2, |
| 880 | fn: &map_is_subset); |
| 881 | } |
| 882 | |
| 883 | isl_bool isl_set_is_subset(__isl_keep isl_set *set1, __isl_keep isl_set *set2) |
| 884 | { |
| 885 | return isl_map_is_subset(map1: set_to_map(set1), map2: set_to_map(set2)); |
| 886 | } |
| 887 | |
| 888 | __isl_give isl_map *isl_map_make_disjoint(__isl_take isl_map *map) |
| 889 | { |
| 890 | int i; |
| 891 | struct isl_subtract_diff_collector sdc; |
| 892 | sdc.dc.add = &basic_map_subtract_add; |
| 893 | |
| 894 | if (!map) |
| 895 | return NULL; |
| 896 | if (ISL_F_ISSET(map, ISL_MAP_DISJOINT)) |
| 897 | return map; |
| 898 | if (map->n <= 1) |
| 899 | return map; |
| 900 | |
| 901 | map = isl_map_compute_divs(map); |
| 902 | map = isl_map_remove_empty_parts(map); |
| 903 | |
| 904 | if (!map || map->n <= 1) |
| 905 | return map; |
| 906 | |
| 907 | sdc.diff = isl_map_from_basic_map(bmap: isl_basic_map_copy(bmap: map->p[0])); |
| 908 | |
| 909 | for (i = 1; i < map->n; ++i) { |
| 910 | struct isl_basic_map *bmap = isl_basic_map_copy(bmap: map->p[i]); |
| 911 | struct isl_map *copy = isl_map_copy(map: sdc.diff); |
| 912 | if (basic_map_collect_diff(bmap, map: copy, dc: &sdc.dc) < 0) { |
| 913 | isl_map_free(map: sdc.diff); |
| 914 | sdc.diff = NULL; |
| 915 | break; |
| 916 | } |
| 917 | } |
| 918 | |
| 919 | isl_map_free(map); |
| 920 | |
| 921 | return sdc.diff; |
| 922 | } |
| 923 | |
| 924 | __isl_give isl_set *isl_set_make_disjoint(__isl_take isl_set *set) |
| 925 | { |
| 926 | return set_from_map(isl_map_make_disjoint(map: set_to_map(set))); |
| 927 | } |
| 928 | |
| 929 | __isl_give isl_map *isl_map_complement(__isl_take isl_map *map) |
| 930 | { |
| 931 | isl_map *universe; |
| 932 | |
| 933 | if (!map) |
| 934 | return NULL; |
| 935 | |
| 936 | universe = isl_map_universe(space: isl_map_get_space(map)); |
| 937 | |
| 938 | return isl_map_subtract(map1: universe, map2: map); |
| 939 | } |
| 940 | |
| 941 | __isl_give isl_set *isl_set_complement(__isl_take isl_set *set) |
| 942 | { |
| 943 | return isl_map_complement(map: set); |
| 944 | } |
| 945 | |