isl_map_transitive_closure: break early if input map doesn't compose with itself
[platform/upstream/isl.git] / isl_transitive_closure.c
index d989f45..ba7153b 100644 (file)
 #include "isl_map_private.h"
 #include "isl_seq.h"
  
+/* Given a map that represents a path with the length of the path
+ * encoded as the difference between the last output coordindate
+ * and the last input coordinate, set this length to either
+ * exactly "length" (if "exactly" is set) or at least "length"
+ * (if "exactly" is not set).
+ */
+static __isl_give isl_map *set_path_length(__isl_take isl_map *map,
+       int exactly, int length)
+{
+       struct isl_dim *dim;
+       struct isl_basic_map *bmap;
+       unsigned d;
+       unsigned nparam;
+       int k;
+       isl_int *c;
+
+       if (!map)
+               return NULL;
+
+       dim = isl_map_get_dim(map);
+       d = isl_dim_size(dim, isl_dim_in);
+       nparam = isl_dim_size(dim, isl_dim_param);
+       bmap = isl_basic_map_alloc_dim(dim, 0, 1, 1);
+       if (exactly) {
+               k = isl_basic_map_alloc_equality(bmap);
+               c = bmap->eq[k];
+       } else {
+               k = isl_basic_map_alloc_inequality(bmap);
+               c = bmap->ineq[k];
+       }
+       if (k < 0)
+               goto error;
+       isl_seq_clr(c, 1 + isl_basic_map_total_dim(bmap));
+       isl_int_set_si(c[0], -length);
+       isl_int_set_si(c[1 + nparam + d - 1], -1);
+       isl_int_set_si(c[1 + nparam + d + d - 1], 1);
+
+       bmap = isl_basic_map_finalize(bmap);
+       map = isl_map_intersect(map, isl_map_from_basic_map(bmap));
+
+       return map;
+error:
+       isl_basic_map_free(bmap);
+       isl_map_free(map);
+       return NULL;
+}
+
+/* Check whether the overapproximation of the power of "map" is exactly
+ * the power of "map".  Let R be "map" and A_k the overapproximation.
+ * The approximation is exact if
+ *
+ *     A_1 = R
+ *     A_k = A_{k-1} \circ R                   k >= 2
+ *
+ * Since A_k is known to be an overapproximation, we only need to check
+ *
+ *     A_1 \subset R
+ *     A_k \subset A_{k-1} \circ R             k >= 2
+ *
+ * In practice, "app" has an extra input and output coordinate
+ * to encode the length of the path.  So, we first need to add
+ * this coordinate to "map" and set the length of the path to
+ * one.
+ */
+static int check_power_exactness(__isl_take isl_map *map,
+       __isl_take isl_map *app)
+{
+       int exact;
+       isl_map *app_1;
+       isl_map *app_2;
+
+       map = isl_map_add(map, isl_dim_in, 1);
+       map = isl_map_add(map, isl_dim_out, 1);
+       map = set_path_length(map, 1, 1);
+
+       app_1 = set_path_length(isl_map_copy(app), 1, 1);
+
+       exact = isl_map_is_subset(app_1, map);
+       isl_map_free(app_1);
+
+       if (!exact || exact < 0) {
+               isl_map_free(app);
+               isl_map_free(map);
+               return exact;
+       }
+
+       app_1 = set_path_length(isl_map_copy(app), 0, 1);
+       app_2 = set_path_length(app, 0, 2);
+       app_1 = isl_map_apply_range(map, app_1);
+
+       exact = isl_map_is_subset(app_2, app_1);
+
+       isl_map_free(app_1);
+       isl_map_free(app_2);
+
+       return exact;
+}
+
+/* Check whether the overapproximation of the power of "map" is exactly
+ * the power of "map", possibly after projecting out the power (if "project"
+ * is set).
+ *
+ * If "project" is set and if "steps" can only result in acyclic paths,
+ * then we check
+ *
+ *     A = R \cup (A \circ R)
+ *
+ * where A is the overapproximation with the power projected out, i.e.,
+ * an overapproximation of the transitive closure.
+ * More specifically, since A is known to be an overapproximation, we check
+ *
+ *     A \subset R \cup (A \circ R)
+ *
+ * Otherwise, we check if the power is exact.
+ *
+ * Note that "app" has an extra input and output coordinate to encode
+ * the length of the part.  If we are only interested in the transitive
+ * closure, then we can simply project out these coordinates first.
+ */
+static int check_exactness(__isl_take isl_map *map, __isl_take isl_map *app,
+       int project)
+{
+       isl_map *test;
+       int exact;
+       unsigned d;
+
+       if (!project)
+               return check_power_exactness(map, app);
+
+       d = isl_map_dim(map, isl_dim_in);
+       app = set_path_length(app, 0, 1);
+       app = isl_map_project_out(app, isl_dim_in, d, 1);
+       app = isl_map_project_out(app, isl_dim_out, d, 1);
+
+       test = isl_map_apply_range(isl_map_copy(map), isl_map_copy(app));
+       test = isl_map_union(test, isl_map_copy(map));
+
+       exact = isl_map_is_subset(app, test);
+
+       isl_map_free(app);
+       isl_map_free(test);
+
+       isl_map_free(map);
+
+       return exact;
+error:
+       isl_map_free(app);
+       isl_map_free(map);
+       return -1;
+}
+
 /*
  * The transitive closure implementation is based on the paper
  * "Computing the Transitive Closure of a Union of Affine Integer
@@ -95,16 +246,22 @@ error:
 #define IMPURE         0
 #define PURE_PARAM     1
 #define PURE_VAR       2
+#define MIXED          3
 
 /* Return PURE_PARAM if only the coefficients of the parameters are non-zero.
  * Return PURE_VAR if only the coefficients of the set variables are non-zero.
+ * Return MIXED if only the coefficients of the parameters and the set
+ *     variables are non-zero and if moreover the parametric constant
+ *     can never attain positive values.
  * Return IMPURE otherwise.
  */
-static int purity(__isl_keep isl_basic_set *bset, isl_int *c)
+static int purity(__isl_keep isl_basic_set *bset, isl_int *c, int eq)
 {
        unsigned d;
        unsigned n_div;
        unsigned nparam;
+       int k;
+       int empty;
 
        n_div = isl_basic_set_dim(bset, isl_dim_div);
        d = isl_basic_set_dim(bset, isl_dim_set);
@@ -116,7 +273,25 @@ static int purity(__isl_keep isl_basic_set *bset, isl_int *c)
                return PURE_VAR;
        if (isl_seq_first_non_zero(c + 1 + nparam, d) == -1)
                return PURE_PARAM;
-       return IMPURE;
+       if (eq)
+               return IMPURE;
+
+       bset = isl_basic_set_copy(bset);
+       bset = isl_basic_set_cow(bset);
+       bset = isl_basic_set_extend_constraints(bset, 0, 1);
+       k = isl_basic_set_alloc_inequality(bset);
+       if (k < 0)
+               goto error;
+       isl_seq_clr(bset->ineq[k], 1 + isl_basic_set_total_dim(bset));
+       isl_seq_cpy(bset->ineq[k], c, 1 + nparam);
+       isl_int_sub_ui(bset->ineq[k][0], bset->ineq[k][0], 1);
+       empty = isl_basic_set_is_empty(bset);
+       isl_basic_set_free(bset);
+
+       return empty < 0 ? -1 : empty ? MIXED : IMPURE;
+error:
+       isl_basic_set_free(bset);
+       return -1;
 }
 
 /* Given a set of offsets "delta", construct a relation of the
@@ -136,12 +311,16 @@ static int purity(__isl_keep isl_basic_set *bset, isl_int *c)
  * In particular, let delta be defined as
  *
  *     \delta = [p] -> { [x] : A x + a >= and B p + b >= 0 and
- *                             C x + C'p + c >= 0 }
+ *                             C x + C'p + c >= 0 and
+ *                             D x + D'p + d >= 0 }
  *
- * then the relation is constructed as
+ * where the constraints C x + C'p + c >= 0 are such that the parametric
+ * constant term of each constraint j, "C_j x + C'_j p + c_j",
+ * can never attain positive values, then the relation is constructed as
  *
  *     { [x] -> [y] : exists [f, k] \in Z^{n+1} : y = x + f and
- *                     A f + k a >= 0 and B p + b >= 0 and k >= 1 }
+ *                     A f + k a >= 0 and B p + b >= 0 and
+ *                     C f + C'p + c >= 0 and k >= 1 }
  *     union { [x] -> [x] }
  *
  * Existentially quantified variables in \delta are currently ignored.
@@ -184,7 +363,9 @@ static __isl_give isl_map *path_along_delta(__isl_take isl_dim *dim,
        }
 
        for (i = 0; i < delta->n_eq; ++i) {
-               int p = purity(delta, delta->eq[i]);
+               int p = purity(delta, delta->eq[i], 1);
+               if (p < 0)
+                       goto error;
                if (p == IMPURE)
                        continue;
                k = isl_basic_map_alloc_equality(path);
@@ -200,7 +381,9 @@ static __isl_give isl_map *path_along_delta(__isl_take isl_dim *dim,
        }
 
        for (i = 0; i < delta->n_ineq; ++i) {
-               int p = purity(delta, delta->ineq[i]);
+               int p = purity(delta, delta->ineq[i], 0);
+               if (p < 0)
+                       goto error;
                if (p == IMPURE)
                        continue;
                k = isl_basic_map_alloc_inequality(path);
@@ -211,8 +394,13 @@ static __isl_give isl_map *path_along_delta(__isl_take isl_dim *dim,
                        isl_seq_cpy(path->ineq[k] + off,
                                    delta->ineq[i] + 1 + nparam, d);
                        isl_int_set(path->ineq[k][off + d], delta->ineq[i][0]);
-               } else
+               } else if (p == PURE_PARAM) {
                        isl_seq_cpy(path->ineq[k], delta->ineq[i], 1 + nparam);
+               } else {
+                       isl_seq_cpy(path->ineq[k] + off,
+                                   delta->ineq[i] + 1 + nparam, d);
+                       isl_seq_cpy(path->ineq[k], delta->ineq[i], 1 + nparam);
+               }
        }
 
        k = isl_basic_map_alloc_inequality(path);
@@ -368,6 +556,7 @@ static __isl_give isl_map *construct_extended_path(__isl_take isl_dim *dim,
                if (j < d) {
                        path = isl_map_apply_range(path,
                                path_along_delta(isl_dim_copy(dim), delta));
+                       path = isl_map_coalesce(path);
                } else {
                        isl_basic_set_free(delta);
                        ++n;
@@ -398,7 +587,8 @@ error:
 
 /* Given a union of basic maps R = \cup_i R_i \subseteq D \times D
  * and a dimension specification (Z^{n+1} -> Z^{n+1}),
- * construct a map that is an overapproximation of the map
+ * construct a map that is the union of the identity map and
+ * an overapproximation of the map
  * that takes an element from the dom R \times Z to an
  * element from ran R \times Z, such that the first n coordinates of the
  * difference between them is a sum of differences between images
@@ -412,13 +602,15 @@ error:
  *
  *     { (x) -> (x + d) | \exists k_i >= 0, \delta_i \in \Delta_i :
  *                             d = (\sum_i k_i \delta_i, \sum_i k_i) and
- *                             x in dom R and x + d in ran R}
+ *                             x in dom R and x + d in ran R } union
+ *     { (x) -> (x) }
  */
-static __isl_give isl_map *construct_extended_power(__isl_take isl_dim *dim,
-       __isl_keep isl_map *map, int *project)
+static __isl_give isl_map *construct_component(__isl_take isl_dim *dim,
+       __isl_keep isl_map *map, int *exact, int project)
 {
        struct isl_set *domain = NULL;
        struct isl_set *range = NULL;
+       struct isl_set *overlap;
        struct isl_map *app = NULL;
        struct isl_map *path = NULL;
 
@@ -426,207 +618,345 @@ static __isl_give isl_map *construct_extended_power(__isl_take isl_dim *dim,
        domain = isl_set_coalesce(domain);
        range = isl_map_range(isl_map_copy(map));
        range = isl_set_coalesce(range);
+       overlap = isl_set_intersect(isl_set_copy(domain), isl_set_copy(range));
+       if (isl_set_is_empty(overlap) == 1) {
+               isl_set_free(domain);
+               isl_set_free(range);
+               isl_set_free(overlap);
+               isl_dim_free(dim);
+
+               map = isl_map_copy(map);
+               map = isl_map_add(map, isl_dim_in, 1);
+               map = isl_map_add(map, isl_dim_out, 1);
+               map = set_path_length(map, 1, 1);
+               return map;
+       }
+       isl_set_free(overlap);
        app = isl_map_from_domain_and_range(domain, range);
        app = isl_map_add(app, isl_dim_in, 1);
        app = isl_map_add(app, isl_dim_out, 1);
 
-       path = construct_extended_path(dim, map, project);
+       path = construct_extended_path(isl_dim_copy(dim), map,
+                                       exact && *exact ? &project : NULL);
        app = isl_map_intersect(app, path);
 
-       return app;
+       if (exact && *exact &&
+           (*exact = check_exactness(isl_map_copy(map), isl_map_copy(app),
+                                     project)) < 0)
+               goto error;
+
+       return isl_map_union(app, isl_map_identity(isl_dim_domain(dim)));
+error:
+       isl_dim_free(dim);
+       isl_map_free(app);
+       return NULL;
 }
 
-/* Given a union of basic maps R = \cup_i R_i \subseteq D \times D,
- * construct a map that is an overapproximation of the map
- * that takes an element from the space D to another
- * element from the same space, such that the difference between
- * them is a strictly positive sum of differences between images
- * and pre-images in one of the R_i.
- * The number of differences in the sum is equated to parameter "param".
- * That is, let
+/* Structure for representing the nodes in the graph being traversed
+ * using Tarjan's algorithm.
+ * index represents the order in which nodes are visited.
+ * min_index is the index of the root of a (sub)component.
+ * on_stack indicates whether the node is currently on the stack.
+ */
+struct basic_map_sort_node {
+       int index;
+       int min_index;
+       int on_stack;
+};
+/* Structure for representing the graph being traversed
+ * using Tarjan's algorithm.
+ * len is the number of nodes
+ * node is an array of nodes
+ * stack contains the nodes on the path from the root to the current node
+ * sp is the stack pointer
+ * index is the index of the last node visited
+ * order contains the elements of the components separated by -1
+ * op represents the current position in order
+ */
+struct basic_map_sort {
+       int len;
+       struct basic_map_sort_node *node;
+       int *stack;
+       int sp;
+       int index;
+       int *order;
+       int op;
+};
+
+static void basic_map_sort_free(struct basic_map_sort *s)
+{
+       if (!s)
+               return;
+       free(s->node);
+       free(s->stack);
+       free(s->order);
+       free(s);
+}
+
+static struct basic_map_sort *basic_map_sort_alloc(struct isl_ctx *ctx, int len)
+{
+       struct basic_map_sort *s;
+       int i;
+
+       s = isl_calloc_type(ctx, struct basic_map_sort);
+       if (!s)
+               return NULL;
+       s->len = len;
+       s->node = isl_alloc_array(ctx, struct basic_map_sort_node, len);
+       if (!s->node)
+               goto error;
+       for (i = 0; i < len; ++i)
+               s->node[i].index = -1;
+       s->stack = isl_alloc_array(ctx, int, len);
+       if (!s->stack)
+               goto error;
+       s->order = isl_alloc_array(ctx, int, 2 * len);
+       if (!s->order)
+               goto error;
+
+       s->sp = 0;
+       s->index = 0;
+       s->op = 0;
+
+       return s;
+error:
+       basic_map_sort_free(s);
+       return NULL;
+}
+
+/* Check whether in the computation of the transitive closure
+ * "bmap1" (R_1) should follow (or be part of the same component as)
+ * "bmap2" (R_2).
  *
- *     \Delta_i = { y - x | (x, y) in R_i }
+ * That is check whether
  *
- * then the constructed map is an overapproximation of
+ *     R_1 \circ R_2
  *
- *     { (x) -> (x + d) | \exists k_i >= 0, \delta_i \in \Delta_i :
- *                             d = \sum_i k_i \delta_i and k = \sum_i k_i > 0 }
+ * is a subset of
  *
- * We first construct an extended mapping with an extra coordinate
- * that indicates the number of steps taken.  In particular,
- * the difference in the last coordinate is equal to the number
- * of steps taken to move from a domain element to the corresponding
- * image element(s).
- * In the final step, this difference is equated to the parameter "param"
- * and made positive.  The extra coordinates are subsequently projected out.
+ *     R_2 \circ R_1
+ *
+ * If so, then there is no reason for R_1 to immediately follow R_2
+ * in any path.
  */
-static __isl_give isl_map *construct_power(__isl_keep isl_map *map,
-       unsigned param, int *project)
+static int basic_map_follows(__isl_keep isl_basic_map *bmap1,
+       __isl_keep isl_basic_map *bmap2)
 {
-       struct isl_map *app = NULL;
-       struct isl_map *diff;
-       struct isl_dim *dim = NULL;
-       unsigned d;
+       struct isl_map *map12 = NULL;
+       struct isl_map *map21 = NULL;
+       int subset;
+
+       map21 = isl_map_from_basic_map(
+                       isl_basic_map_apply_range(
+                               isl_basic_map_copy(bmap2),
+                               isl_basic_map_copy(bmap1)));
+       subset = isl_map_is_empty(map21);
+       if (subset < 0)
+               goto error;
+       if (subset) {
+               isl_map_free(map21);
+               return 0;
+       }
 
-       if (!map)
-               return NULL;
+       map12 = isl_map_from_basic_map(
+                       isl_basic_map_apply_range(
+                               isl_basic_map_copy(bmap1),
+                               isl_basic_map_copy(bmap2)));
 
-       dim = isl_map_get_dim(map);
+       subset = isl_map_is_subset(map21, map12);
 
-       d = isl_dim_size(dim, isl_dim_in);
-       dim = isl_dim_add(dim, isl_dim_in, 1);
-       dim = isl_dim_add(dim, isl_dim_out, 1);
-
-       app = construct_extended_power(isl_dim_copy(dim), map, project);
+       isl_map_free(map12);
+       isl_map_free(map21);
 
-       diff = equate_parameter_to_length(dim, param);
-       app = isl_map_intersect(app, diff);
-       app = isl_map_project_out(app, isl_dim_in, d, 1);
-       app = isl_map_project_out(app, isl_dim_out, d, 1);
-
-       return app;
+       return subset < 0 ? -1 : !subset;
+error:
+       isl_map_free(map21);
+       return -1;
 }
 
-/* Shift variable at position "pos" up by one.
- * That is, replace the corresponding variable v by v - 1.
+/* Perform Tarjan's algorithm for computing the strongly connected components
+ * in the graph with the disjuncts of "map" as vertices and with an
+ * edge between any pair of disjuncts such that the first has
+ * to be applied after the second.
  */
-static __isl_give isl_basic_map *basic_map_shift_pos(
-       __isl_take isl_basic_map *bmap, unsigned pos)
+static int power_components_tarjan(struct basic_map_sort *s,
+       __isl_keep isl_map *map, int i)
 {
-       int i;
+       int j;
 
-       bmap = isl_basic_map_cow(bmap);
-       if (!bmap)
-               return NULL;
+       s->node[i].index = s->index;
+       s->node[i].min_index = s->index;
+       s->node[i].on_stack = 1;
+       s->index++;
+       s->stack[s->sp++] = i;
 
-       for (i = 0; i < bmap->n_eq; ++i)
-               isl_int_sub(bmap->eq[i][0], bmap->eq[i][0], bmap->eq[i][pos]);
+       for (j = s->len - 1; j >= 0; --j) {
+               int f;
 
-       for (i = 0; i < bmap->n_ineq; ++i)
-               isl_int_sub(bmap->ineq[i][0],
-                               bmap->ineq[i][0], bmap->ineq[i][pos]);
+               if (j == i)
+                       continue;
+               if (s->node[j].index >= 0 &&
+                       (!s->node[j].on_stack ||
+                        s->node[j].index > s->node[i].min_index))
+                       continue;
 
-       for (i = 0; i < bmap->n_div; ++i) {
-               if (isl_int_is_zero(bmap->div[i][0]))
+               f = basic_map_follows(map->p[i], map->p[j]);
+               if (f < 0)
+                       return -1;
+               if (!f)
                        continue;
-               isl_int_sub(bmap->div[i][1],
-                               bmap->div[i][1], bmap->div[i][1 + pos]);
+
+               if (s->node[j].index < 0) {
+                       power_components_tarjan(s, map, j);
+                       if (s->node[j].min_index < s->node[i].min_index)
+                               s->node[i].min_index = s->node[j].min_index;
+               } else if (s->node[j].index < s->node[i].min_index)
+                       s->node[i].min_index = s->node[j].index;
        }
 
-       return bmap;
-}
+       if (s->node[i].index != s->node[i].min_index)
+               return 0;
 
-/* Shift variable at position "pos" up by one.
- * That is, replace the corresponding variable v by v - 1.
- */
-static __isl_give isl_map *map_shift_pos(__isl_take isl_map *map, unsigned pos)
-{
-       int i;
+       do {
+               j = s->stack[--s->sp];
+               s->node[j].on_stack = 0;
+               s->order[s->op++] = j;
+       } while (j != i);
+       s->order[s->op++] = -1;
 
-       map = isl_map_cow(map);
-       if (!map)
-               return NULL;
-
-       for (i = 0; i < map->n; ++i) {
-               map->p[i] = basic_map_shift_pos(map->p[i], pos);
-               if (!map->p[i])
-                       goto error;
-       }
-       ISL_F_CLR(map, ISL_MAP_NORMALIZED);
-       return map;
-error:
-       isl_map_free(map);
-       return NULL;
+       return 0;
 }
 
-/* Check whether the overapproximation of the power of "map" is exactly
- * the power of "map".  Let R be "map" and A_k the overapproximation.
- * The approximation is exact if
+/* Given a union of basic maps R = \cup_i R_i \subseteq D \times D
+ * and a dimension specification (Z^{n+1} -> Z^{n+1}),
+ * construct a map that is the union of the identity map and
+ * an overapproximation of the map
+ * that takes an element from the dom R \times Z to an
+ * element from ran R \times Z, such that the first n coordinates of the
+ * difference between them is a sum of differences between images
+ * and pre-images in one of the R_i and such that the last coordinate
+ * is equal to the number of steps taken.
+ * That is, let
  *
- *     A_1 = R
- *     A_k = A_{k-1} \circ R                   k >= 2
+ *     \Delta_i = { y - x | (x, y) in R_i }
  *
- * Since A_k is known to be an overapproximation, we only need to check
+ * then the constructed map is an overapproximation of
  *
- *     A_1 \subset R
- *     A_k \subset A_{k-1} \circ R             k >= 2
+ *     { (x) -> (x + d) | \exists k_i >= 0, \delta_i \in \Delta_i :
+ *                             d = (\sum_i k_i \delta_i, \sum_i k_i) and
+ *                             x in dom R and x + d in ran R } union
+ *     { (x) -> (x) }
  *
+ * We first split the map into strongly connected components, perform
+ * the above on each component and the join the results in the correct
+ * order.  The power of each of the components needs to be extended
+ * with the identity map because a path in the global result need
+ * not go through every component.
+ * The final result will then also contain the identity map, but
+ * this part will be removed when the length of the path is forced
+ * to be strictly positive.
  */
-static int check_power_exactness(__isl_take isl_map *map,
-       __isl_take isl_map *app, unsigned param)
+static __isl_give isl_map *construct_power_components(__isl_take isl_dim *dim,
+       __isl_keep isl_map *map, int *exact, int project)
 {
-       int exact;
-       isl_map *app_1;
-       isl_map *app_2;
-
-       app_1 = isl_map_fix_si(isl_map_copy(app), isl_dim_param, param, 1);
+       int i, n;
+       struct isl_map *path = NULL;
+       struct basic_map_sort *s = NULL;
 
-       exact = isl_map_is_subset(app_1, map);
-       isl_map_free(app_1);
+       if (!map)
+               goto error;
+       if (map->n <= 1)
+               return construct_component(dim, map, exact, project);
 
-       if (!exact || exact < 0) {
-               isl_map_free(app);
-               isl_map_free(map);
-               return exact;
+       s = basic_map_sort_alloc(map->ctx, map->n);
+       if (!s)
+               goto error;
+       for (i = map->n - 1; i >= 0; --i) {
+               if (s->node[i].index >= 0)
+                       continue;
+               if (power_components_tarjan(s, map, i) < 0)
+                       goto error;
        }
 
-       app_2 = isl_map_lower_bound_si(isl_map_copy(app),
-                                       isl_dim_param, param, 2);
-       app_1 = map_shift_pos(app, 1 + param);
-       app_1 = isl_map_apply_range(map, app_1);
-
-       exact = isl_map_is_subset(app_2, app_1);
+       i = 0;
+       n = map->n;
+       path = isl_map_identity(isl_dim_domain(isl_dim_copy(dim)));
+       while (n) {
+               struct isl_map *comp;
+               comp = isl_map_alloc_dim(isl_map_get_dim(map), n, 0);
+               while (s->order[i] != -1) {
+                       comp = isl_map_add_basic_map(comp,
+                                   isl_basic_map_copy(map->p[s->order[i]]));
+                       --n;
+                       ++i;
+               }
+               path = isl_map_apply_range(path,
+                           construct_component(isl_dim_copy(dim), comp,
+                                               exact, project));
+               isl_map_free(comp);
+               ++i;
+       }
 
-       isl_map_free(app_1);
-       isl_map_free(app_2);
+       basic_map_sort_free(s);
+       isl_dim_free(dim);
 
-       return exact;
+       return path;
+error:
+       basic_map_sort_free(s);
+       isl_dim_free(dim);
+       return NULL;
 }
 
-/* Check whether the overapproximation of the power of "map" is exactly
- * the power of "map", possibly after projecting out the power (if "project"
- * is set).
- *
- * If "project" is set and if "steps" can only result in acyclic paths,
- * then we check
+/* Given a union of basic maps R = \cup_i R_i \subseteq D \times D,
+ * construct a map that is an overapproximation of the map
+ * that takes an element from the space D to another
+ * element from the same space, such that the difference between
+ * them is a strictly positive sum of differences between images
+ * and pre-images in one of the R_i.
+ * The number of differences in the sum is equated to parameter "param".
+ * That is, let
  *
- *     A = R \cup (A \circ R)
+ *     \Delta_i = { y - x | (x, y) in R_i }
  *
- * where A is the overapproximation with the power projected out, i.e.,
- * an overapproximation of the transitive closure.
- * More specifically, since A is known to be an overapproximation, we check
+ * then the constructed map is an overapproximation of
  *
- *     A \subset R \cup (A \circ R)
+ *     { (x) -> (x + d) | \exists k_i >= 0, \delta_i \in \Delta_i :
+ *                             d = \sum_i k_i \delta_i and k = \sum_i k_i > 0 }
  *
- * Otherwise, we check if the power is exact.
+ * We first construct an extended mapping with an extra coordinate
+ * that indicates the number of steps taken.  In particular,
+ * the difference in the last coordinate is equal to the number
+ * of steps taken to move from a domain element to the corresponding
+ * image element(s).
+ * In the final step, this difference is equated to the parameter "param"
+ * and made positive.  The extra coordinates are subsequently projected out.
  */
-static int check_exactness(__isl_take isl_map *map, __isl_take isl_map *app,
-       unsigned param, int project)
+static __isl_give isl_map *construct_power(__isl_keep isl_map *map,
+       unsigned param, int *exact, int project)
 {
-       isl_map *test;
-       int exact;
-
-       if (!project)
-               return check_power_exactness(map, app, param);
+       struct isl_map *app = NULL;
+       struct isl_map *diff;
+       struct isl_dim *dim = NULL;
+       unsigned d;
 
-       map = isl_map_project_out(map, isl_dim_param, param, 1);
-       app = isl_map_project_out(app, isl_dim_param, param, 1);
+       if (!map)
+               return NULL;
 
-       test = isl_map_apply_range(isl_map_copy(map), isl_map_copy(app));
-       test = isl_map_union(test, isl_map_copy(map));
+       dim = isl_map_get_dim(map);
 
-       exact = isl_map_is_subset(app, test);
+       d = isl_dim_size(dim, isl_dim_in);
+       dim = isl_dim_add(dim, isl_dim_in, 1);
+       dim = isl_dim_add(dim, isl_dim_out, 1);
 
-       isl_map_free(app);
-       isl_map_free(test);
+       app = construct_power_components(isl_dim_copy(dim), map,
+                                       exact, project);
 
-       isl_map_free(map);
+       diff = equate_parameter_to_length(dim, param);
+       app = isl_map_intersect(app, diff);
+       app = isl_map_project_out(app, isl_dim_in, d, 1);
+       app = isl_map_project_out(app, isl_dim_out, d, 1);
 
-       return exact;
-error:
-       isl_map_free(app);
-       isl_map_free(map);
-       return -1;
+       return app;
 }
 
 /* Compute the positive powers of "map", or an overapproximation.
@@ -655,12 +985,7 @@ static __isl_give isl_map *map_power(__isl_take isl_map *map, unsigned param,
                isl_map_dim(map, isl_dim_in) == isl_map_dim(map, isl_dim_out),
                goto error);
 
-       app = construct_power(map, param, exact ? &project : NULL);
-
-       if (exact &&
-           (*exact = check_exactness(isl_map_copy(map), isl_map_copy(app),
-                                     param, project)) < 0)
-               goto error;
+       app = construct_power(map, param, exact, project);
 
        isl_map_free(map);
        return app;