+ snap = isl_tab_snap(tabs[i]);
+ tabs[i] = isl_tab_relax(tabs[i], n_eq + k);
+ snap2 = isl_tab_snap(tabs[i]);
+ tabs[i] = isl_tab_select_facet(tabs[i], n_eq + k);
+ super = contains(map, j, ineq_j, tabs[i]);
+ if (super) {
+ if (isl_tab_rollback(tabs[i], snap2) < 0)
+ return -1;
+ map->p[i] = isl_basic_map_cow(map->p[i]);
+ if (!map->p[i])
+ return -1;
+ isl_int_add_ui(map->p[i]->ineq[k][0], map->p[i]->ineq[k][0], 1);
+ ISL_F_SET(map->p[i], ISL_BASIC_MAP_FINAL);
+ drop(map, j, tabs);
+ changed = 1;
+ } else
+ if (isl_tab_rollback(tabs[i], snap) < 0)
+ return -1;
+
+ return changed;
+}
+
+/* For each non-redundant constraint in "bmap" (as determined by "tab"),
+ * wrap the constraint around "bound" such that it includes the whole
+ * set "set" and append the resulting constraint to "wraps".
+ * "wraps" is assumed to have been pre-allocated to the appropriate size.
+ * wraps->n_row is the number of actual wrapped constraints that have
+ * been added.
+ * If any of the wrapping problems results in a constraint that is
+ * identical to "bound", then this means that "set" is unbounded in such
+ * way that no wrapping is possible. If this happens then wraps->n_row
+ * is reset to zero.
+ */
+static int add_wraps(__isl_keep isl_mat *wraps, __isl_keep isl_basic_map *bmap,
+ struct isl_tab *tab, isl_int *bound, __isl_keep isl_set *set)
+{
+ int l;
+ int w;
+ unsigned total = isl_basic_map_total_dim(bmap);
+
+ w = wraps->n_row;
+
+ for (l = 0; l < bmap->n_ineq; ++l) {
+ if (isl_seq_is_neg(bound, bmap->ineq[l], 1 + total))
+ continue;
+ if (isl_seq_eq(bound, bmap->ineq[l], 1 + total))
+ continue;
+ if (isl_tab_is_redundant(tab, bmap->n_eq + l))
+ continue;
+
+ isl_seq_cpy(wraps->row[w], bound, 1 + total);
+ if (!isl_set_wrap_facet(set, wraps->row[w], bmap->ineq[l]))
+ return -1;
+ if (isl_seq_eq(wraps->row[w], bound, 1 + total))
+ goto unbounded;
+ ++w;
+ }
+ for (l = 0; l < bmap->n_eq; ++l) {
+ if (isl_seq_is_neg(bound, bmap->eq[l], 1 + total))
+ continue;
+ if (isl_seq_eq(bound, bmap->eq[l], 1 + total))
+ continue;
+
+ isl_seq_cpy(wraps->row[w], bound, 1 + total);
+ isl_seq_neg(wraps->row[w + 1], bmap->eq[l], 1 + total);
+ if (!isl_set_wrap_facet(set, wraps->row[w], wraps->row[w + 1]))
+ return -1;
+ if (isl_seq_eq(wraps->row[w], bound, 1 + total))
+ goto unbounded;
+ ++w;
+
+ isl_seq_cpy(wraps->row[w], bound, 1 + total);
+ if (!isl_set_wrap_facet(set, wraps->row[w], bmap->eq[l]))
+ return -1;
+ if (isl_seq_eq(wraps->row[w], bound, 1 + total))
+ goto unbounded;
+ ++w;
+ }
+
+ wraps->n_row = w;
+ return 0;
+unbounded:
+ wraps->n_row = 0;
+ return 0;
+}
+
+/* Given a basic set i with a constraint k that is adjacent to either the
+ * whole of basic set j or a facet of basic set j, check if we can wrap
+ * both the facet corresponding to k and the facet of j (or the whole of j)
+ * around their ridges to include the other set.
+ * If so, replace the pair of basic sets by their union.
+ *
+ * All constraints of i (except k) are assumed to be valid for j.
+ *
+ * In the case where j has a facet adjacent to i, tab[j] is assumed
+ * to have been restricted to this facet, so that the non-redundant
+ * constraints in tab[j] are the ridges of the facet.
+ * Note that for the purpose of wrapping, it does not matter whether
+ * we wrap the ridges of i around the whole of j or just around
+ * the facet since all the other constraints are assumed to be valid for j.
+ * In practice, we wrap to include the whole of j.
+ * ____ _____
+ * / | / \
+ * / || / |
+ * \ || => \ |
+ * \ || \ |
+ * \___|| \____|
+ *
+ */
+static int can_wrap_in_facet(struct isl_map *map, int i, int j, int k,
+ struct isl_tab **tabs, int *eq_i, int *ineq_i, int *eq_j, int *ineq_j)
+{
+ int changed = 0;
+ struct isl_mat *wraps = NULL;
+ struct isl_set *set_i = NULL;
+ struct isl_set *set_j = NULL;
+ struct isl_vec *bound = NULL;
+ unsigned total = isl_basic_map_total_dim(map->p[i]);
+ struct isl_tab_undo *snap;
+
+ snap = isl_tab_snap(tabs[i]);
+
+ set_i = isl_set_from_basic_set(
+ isl_basic_map_underlying_set(isl_basic_map_copy(map->p[i])));
+ set_j = isl_set_from_basic_set(
+ isl_basic_map_underlying_set(isl_basic_map_copy(map->p[j])));
+ wraps = isl_mat_alloc(map->ctx, 2 * (map->p[i]->n_eq + map->p[j]->n_eq) +
+ map->p[i]->n_ineq + map->p[j]->n_ineq,
+ 1 + total);
+ bound = isl_vec_alloc(map->ctx, 1 + total);
+ if (!set_i || !set_j || !wraps || !bound)
+ goto error;
+
+ isl_seq_cpy(bound->el, map->p[i]->ineq[k], 1 + total);
+ isl_int_add_ui(bound->el[0], bound->el[0], 1);
+
+ isl_seq_cpy(wraps->row[0], bound->el, 1 + total);
+ wraps->n_row = 1;
+
+ if (add_wraps(wraps, map->p[j], tabs[j], bound->el, set_i) < 0)
+ goto error;
+ if (!wraps->n_row)
+ goto unbounded;
+
+ tabs[i] = isl_tab_select_facet(tabs[i], map->p[i]->n_eq + k);
+ if (isl_tab_detect_redundant(tabs[i]) < 0)
+ goto error;
+
+ isl_seq_neg(bound->el, map->p[i]->ineq[k], 1 + total);
+
+ if (add_wraps(wraps, map->p[i], tabs[i], bound->el, set_j) < 0)
+ goto error;
+ if (!wraps->n_row)
+ goto unbounded;
+
+ changed = fuse(map, i, j, tabs, eq_i, ineq_i, eq_j, ineq_j, wraps);
+
+ if (!changed) {
+unbounded:
+ if (isl_tab_rollback(tabs[i], snap) < 0)
+ goto error;
+ }
+
+ isl_mat_free(wraps);
+
+ isl_set_free(set_i);
+ isl_set_free(set_j);
+
+ isl_vec_free(bound);
+
+ return changed;
+error:
+ isl_vec_free(bound);
+ isl_mat_free(wraps);
+ isl_set_free(set_i);
+ isl_set_free(set_j);
+ return -1;
+}
+
+/* Given two basic sets i and j such that i has exactly one cut constraint,
+ * check if we can wrap the corresponding facet around its ridges to include
+ * the other basic set (and nothing else).
+ * If so, replace the pair by their union.
+ *
+ * We first check if j has a facet adjacent to the cut constraint of i.
+ * If so, we try to wrap in the facet.
+ * ____ _____
+ * / ___|_ / \
+ * / | | / |
+ * \ | | => \ |
+ * \|____| \ |
+ * \___| \____/
+ */
+static int can_wrap_in_set(struct isl_map *map, int i, int j,
+ struct isl_tab **tabs, int *ineq_i, int *ineq_j)
+{
+ int changed = 0;
+ int k, l;
+ unsigned total = isl_basic_map_total_dim(map->p[i]);
+ struct isl_tab_undo *snap;
+
+ for (k = 0; k < map->p[i]->n_ineq; ++k)
+ if (ineq_i[k] == STATUS_CUT)
+ break;
+
+ isl_assert(map->ctx, k < map->p[i]->n_ineq, return -1);
+
+ isl_int_add_ui(map->p[i]->ineq[k][0], map->p[i]->ineq[k][0], 1);
+ for (l = 0; l < map->p[j]->n_ineq; ++l) {
+ if (isl_tab_is_redundant(tabs[j], map->p[j]->n_eq + l))
+ continue;
+ if (isl_seq_eq(map->p[i]->ineq[k],
+ map->p[j]->ineq[l], 1 + total))
+ break;
+ }
+ isl_int_sub_ui(map->p[i]->ineq[k][0], map->p[i]->ineq[k][0], 1);
+
+ if (l >= map->p[j]->n_ineq)
+ return 0;
+
+ snap = isl_tab_snap(tabs[j]);
+ tabs[j] = isl_tab_select_facet(tabs[j], map->p[j]->n_eq + l);
+ if (isl_tab_detect_redundant(tabs[j]) < 0)
+ return -1;
+
+ changed = can_wrap_in_facet(map, i, j, k, tabs, NULL, ineq_i, NULL, ineq_j);
+
+ if (!changed && isl_tab_rollback(tabs[j], snap) < 0)
+ return -1;
+
+ return changed;
+}
+
+/* Check if either i or j has a single cut constraint that can
+ * be used to wrap in (a facet of) the other basic set.
+ * if so, replace the pair by their union.
+ */
+static int check_wrap(struct isl_map *map, int i, int j,
+ struct isl_tab **tabs, int *ineq_i, int *ineq_j)
+{
+ int changed = 0;
+
+ if (count(ineq_i, map->p[i]->n_ineq, STATUS_CUT) == 1)
+ changed = can_wrap_in_set(map, i, j, tabs, ineq_i, ineq_j);
+ if (changed)
+ return changed;
+
+ if (count(ineq_j, map->p[j]->n_ineq, STATUS_CUT) == 1)
+ changed = can_wrap_in_set(map, j, i, tabs, ineq_j, ineq_i);
+ return changed;
+}
+
+/* At least one of the basic maps has an equality that is adjacent
+ * to inequality. Make sure that only one of the basic maps has
+ * such an equality and that the other basic map has exactly one
+ * inequality adjacent to an equality.
+ * We call the basic map that has the inequality "i" and the basic
+ * map that has the equality "j".
+ * If "i" has any "cut" inequality, then relaxing the inequality
+ * by one would not result in a basic map that contains the other
+ * basic map.
+ */
+static int check_adj_eq(struct isl_map *map, int i, int j,
+ struct isl_tab **tabs, int *eq_i, int *ineq_i, int *eq_j, int *ineq_j)
+{
+ int changed = 0;
+ int k;
+