isl_map.c: basic_map_space_reset: handle NULL input
[platform/upstream/isl.git] / isl_test.c
index 6f47057..485d8b4 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Copyright 2008-2009 Katholieke Universiteit Leuven
  *
- * Use of this software is governed by the GNU LGPLv2.1 license
+ * Use of this software is governed by the MIT license
  *
  * Written by Sven Verdoolaege, K.U.Leuven, Departement
  * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium
@@ -22,6 +22,7 @@
 #include <isl/schedule.h>
 #include <isl_options_private.h>
 #include <isl/vertices.h>
+#include <isl/ast_build.h>
 
 #define ARRAY_SIZE(array) (sizeof(array)/sizeof(*array))
 
@@ -200,6 +201,10 @@ int test_parse(struct isl_ctx *ctx)
                                      "{ [i] : i <= 10 }") < 0)
                return -1;
 
+       if (test_parse_map_equal(ctx, "{Sym=[n] [i] : i <= n }",
+                                     "[n] -> { [i] : i <= n }") < 0)
+               return -1;
+
        return 0;
 }
 
@@ -2010,20 +2015,89 @@ void test_lift(isl_ctx *ctx)
        isl_basic_set_free(bset);
 }
 
-void test_subset(isl_ctx *ctx)
+struct {
+       const char *set1;
+       const char *set2;
+       int subset;
+} subset_tests[] = {
+       { "{ [112, 0] }",
+         "{ [i0, i1] : exists (e0 = [(i0 - i1)/16], e1: "
+               "16e0 <= i0 - i1 and 16e0 >= -15 + i0 - i1 and "
+               "16e1 <= i1 and 16e0 >= -i1 and 16e1 >= -i0 + i1) }", 1 },
+       { "{ [65] }",
+         "{ [i] : exists (e0 = [(255i)/256], e1 = [(127i + 65e0)/191], "
+               "e2 = [(3i + 61e1)/65], e3 = [(52i + 12e2)/61], "
+               "e4 = [(2i + e3)/3], e5 = [(4i + e3)/4], e6 = [(8i + e3)/12]: "
+                   "3e4 = 2i + e3 and 4e5 = 4i + e3 and 12e6 = 8i + e3 and "
+                   "i <= 255 and 64e3 >= -45 + 67i and i >= 0 and "
+                   "256e0 <= 255i and 256e0 >= -255 + 255i and "
+                   "191e1 <= 127i + 65e0 and 191e1 >= -190 + 127i + 65e0 and "
+                   "65e2 <= 3i + 61e1 and 65e2 >= -64 + 3i + 61e1 and "
+                   "61e3 <= 52i + 12e2 and 61e3 >= -60 + 52i + 12e2) }", 1 },
+       { "{ [i] : 0 <= i <= 10 }", "{ rat: [i] : 0 <= i <= 10 }", 1 },
+       { "{ rat: [i] : 0 <= i <= 10 }", "{ [i] : 0 <= i <= 10 }", 0 },
+       { "{ rat: [0] }", "{ [i] : 0 <= i <= 10 }", 1 },
+       { "{ rat: [(1)/2] }", "{ [i] : 0 <= i <= 10 }", 0 },
+};
+
+static int test_subset(isl_ctx *ctx)
 {
-       const char *str;
+       int i;
        isl_set *set1, *set2;
+       int subset;
+
+       for (i = 0; i < ARRAY_SIZE(subset_tests); ++i) {
+               set1 = isl_set_read_from_str(ctx, subset_tests[i].set1);
+               set2 = isl_set_read_from_str(ctx, subset_tests[i].set2);
+               subset = isl_set_is_subset(set1, set2);
+               isl_set_free(set1);
+               isl_set_free(set2);
+               if (subset < 0)
+                       return -1;
+               if (subset != subset_tests[i].subset)
+                       isl_die(ctx, isl_error_unknown,
+                               "incorrect subset result", return -1);
+       }
 
-       str = "{ [112, 0] }";
-       set1 = isl_set_read_from_str(ctx, str);
-       str = "{ [i0, i1] : exists (e0 = [(i0 - i1)/16], e1: "
-               "16e0 <= i0 - i1 and 16e0 >= -15 + i0 - i1 and "
-               "16e1 <= i1 and 16e0 >= -i1 and 16e1 >= -i0 + i1) }";
-       set2 = isl_set_read_from_str(ctx, str);
-       assert(isl_set_is_subset(set1, set2));
-       isl_set_free(set1);
-       isl_set_free(set2);
+       return 0;
+}
+
+struct {
+       const char *minuend;
+       const char *subtrahend;
+       const char *difference;
+} subtract_domain_tests[] = {
+       { "{ A[i] -> B[i] }", "{ A[i] }", "{ }" },
+       { "{ A[i] -> B[i] }", "{ B[i] }", "{ A[i] -> B[i] }" },
+       { "{ A[i] -> B[i] }", "{ A[i] : i > 0 }", "{ A[i] -> B[i] : i <= 0 }" },
+};
+
+static int test_subtract(isl_ctx *ctx)
+{
+       int i;
+       isl_union_map *umap1, *umap2;
+       isl_union_set *uset;
+       int equal;
+
+       for (i = 0; i < ARRAY_SIZE(subtract_domain_tests); ++i) {
+               umap1 = isl_union_map_read_from_str(ctx,
+                               subtract_domain_tests[i].minuend);
+               uset = isl_union_set_read_from_str(ctx,
+                               subtract_domain_tests[i].subtrahend);
+               umap2 = isl_union_map_read_from_str(ctx,
+                               subtract_domain_tests[i].difference);
+               umap1 = isl_union_map_subtract_domain(umap1, uset);
+               equal = isl_union_map_is_equal(umap1, umap2);
+               isl_union_map_free(umap1);
+               isl_union_map_free(umap2);
+               if (equal < 0)
+                       return -1;
+               if (!equal)
+                       isl_die(ctx, isl_error_unknown,
+                               "incorrect subtract domain result", return -1);
+       }
+
+       return 0;
 }
 
 int test_factorize(isl_ctx *ctx)
@@ -2204,23 +2278,48 @@ int test_one_schedule(isl_ctx *ctx, const char *d, const char *w,
        return 0;
 }
 
-int test_special_schedule(isl_ctx *ctx, const char *domain,
-       const char *validity, const char *proximity, const char *expected_sched)
+static __isl_give isl_union_map *compute_schedule(isl_ctx *ctx,
+       const char *domain, const char *validity, const char *proximity)
 {
        isl_union_set *dom;
        isl_union_map *dep;
        isl_union_map *prox;
-       isl_union_map *sched1, *sched2;
        isl_schedule *schedule;
-       int equal;
+       isl_union_map *sched;
 
        dom = isl_union_set_read_from_str(ctx, domain);
        dep = isl_union_map_read_from_str(ctx, validity);
        prox = isl_union_map_read_from_str(ctx, proximity);
        schedule = isl_union_set_compute_schedule(dom, dep, prox);
-       sched1 = isl_schedule_get_map(schedule);
+       sched = isl_schedule_get_map(schedule);
        isl_schedule_free(schedule);
 
+       return sched;
+}
+
+/* Check that a schedule can be constructed on the given domain
+ * with the given validity and proximity constraints.
+ */
+static int test_has_schedule(isl_ctx *ctx, const char *domain,
+       const char *validity, const char *proximity)
+{
+       isl_union_map *sched;
+
+       sched = compute_schedule(ctx, domain, validity, proximity);
+       if (!sched)
+               return -1;
+
+       isl_union_map_free(sched);
+       return 0;
+}
+
+int test_special_schedule(isl_ctx *ctx, const char *domain,
+       const char *validity, const char *proximity, const char *expected_sched)
+{
+       isl_union_map *sched1, *sched2;
+       int equal;
+
+       sched1 = compute_schedule(ctx, domain, validity, proximity);
        sched2 = isl_union_map_read_from_str(ctx, expected_sched);
 
        equal = isl_union_map_is_equal(sched1, sched2);
@@ -2240,6 +2339,10 @@ int test_schedule(isl_ctx *ctx)
 {
        const char *D, *W, *R, *V, *P, *S;
 
+       /* Handle resulting schedule with zero bands. */
+       if (test_one_schedule(ctx, "{[]}", "{}", "{}", "{[] -> []}", 0, 0) < 0)
+               return -1;
+
        /* Jacobi */
        D = "[T,N] -> { S1[t,i] : 1 <= t <= T and 2 <= i <= N - 1 }";
        W = "{ S1[t,i] -> a[t,i] }";
@@ -2483,7 +2586,16 @@ int test_schedule(isl_ctx *ctx)
        if (test_special_schedule(ctx, D, V, P, S) < 0)
                return -1;
        ctx->opt->schedule_algorithm = ISL_SCHEDULE_ALGORITHM_ISL;
-       return test_special_schedule(ctx, D, V, P, S);
+       if (test_special_schedule(ctx, D, V, P, S) < 0)
+               return -1;
+       
+       D = "{ A[a]; B[] }";
+       V = "{}";
+       P = "{ A[a] -> B[] }";
+       if (test_has_schedule(ctx, D, V, P) < 0)
+               return -1;
+
+       return 0;
 }
 
 int test_plain_injective(isl_ctx *ctx, const char *str, int injective)
@@ -2857,7 +2969,10 @@ int test_output(isl_ctx *ctx)
        s = isl_printer_get_str(p);
        isl_printer_free(p);
        isl_pw_aff_free(pa);
-       equal = !strcmp(s, "(2 - x + 4*floord(x, 4) >= 0) ? (1) : 2");
+       if (!s)
+               equal = -1;
+       else
+               equal = !strcmp(s, "(2 - x + 4*floord(x, 4) >= 0) ? (1) : 2");
        free(s);
        if (equal < 0)
                return -1;
@@ -2905,6 +3020,8 @@ int test_sample(isl_ctx *ctx)
        subset = isl_basic_set_is_subset(bset2, bset1);
        isl_basic_set_free(bset1);
        isl_basic_set_free(bset2);
+       if (empty < 0 || subset < 0)
+               return -1;
        if (empty)
                isl_die(ctx, isl_error_unknown, "point not found", return -1);
        if (!subset)
@@ -3110,14 +3227,538 @@ static int test_list(isl_ctx *ctx)
        return 0;
 }
 
+const char *set_conversion_tests[] = {
+       "[N] -> { [i] : N - 1 <= 2 i <= N }",
+       "[N] -> { [i] : exists a : i = 4 a and N - 1 <= i <= N }",
+       "[N] -> { [i,j] : exists a : i = 4 a and N - 1 <= i, 2j <= N }",
+       "[N] -> { [[i]->[j]] : exists a : i = 4 a and N - 1 <= i, 2j <= N }",
+};
+
+/* Check that converting from isl_set to isl_pw_multi_aff and back
+ * to isl_set produces the original isl_set.
+ */
+static int test_set_conversion(isl_ctx *ctx)
+{
+       int i;
+       const char *str;
+       isl_set *set1, *set2;
+       isl_pw_multi_aff *pma;
+       int equal;
+
+       for (i = 0; i < ARRAY_SIZE(set_conversion_tests); ++i) {
+               str = set_conversion_tests[i];
+               set1 = isl_set_read_from_str(ctx, str);
+               pma = isl_pw_multi_aff_from_set(isl_set_copy(set1));
+               set2 = isl_set_from_pw_multi_aff(pma);
+               equal = isl_set_is_equal(set1, set2);
+               isl_set_free(set1);
+               isl_set_free(set2);
+
+               if (equal < 0)
+                       return -1;
+               if (!equal)
+                       isl_die(ctx, isl_error_unknown, "bad conversion",
+                               return -1);
+       }
+
+       return 0;
+}
+
+/* Check that converting from isl_map to isl_pw_multi_aff and back
+ * to isl_map produces the original isl_map.
+ */
+static int test_map_conversion(isl_ctx *ctx)
+{
+       const char *str;
+       isl_map *map1, *map2;
+       isl_pw_multi_aff *pma;
+       int equal;
+
+       str = "{ [a, b, c, d] -> s0[a, b, e, f] : "
+               "exists (e0 = [(a - 2c)/3], e1 = [(-4 + b - 5d)/9], "
+               "e2 = [(-d + f)/9]: 3e0 = a - 2c and 9e1 = -4 + b - 5d and "
+               "9e2 = -d + f and f >= 0 and f <= 8 and 9e >= -5 - 2a and "
+               "9e <= -2 - 2a) }";
+       map1 = isl_map_read_from_str(ctx, str);
+       pma = isl_pw_multi_aff_from_map(isl_map_copy(map1));
+       map2 = isl_map_from_pw_multi_aff(pma);
+       equal = isl_map_is_equal(map1, map2);
+       isl_map_free(map1);
+       isl_map_free(map2);
+
+       if (equal < 0)
+               return -1;
+       if (!equal)
+               isl_die(ctx, isl_error_unknown, "bad conversion", return -1);
+
+       return 0;
+}
+
+static int test_conversion(isl_ctx *ctx)
+{
+       if (test_set_conversion(ctx) < 0)
+               return -1;
+       if (test_map_conversion(ctx) < 0)
+               return -1;
+       return 0;
+}
+
+struct {
+       const char *set;
+       const char *ma;
+       const char *res;
+} preimage_tests[] = {
+       { "{ B[i,j] : 0 <= i < 10 and 0 <= j < 100 }",
+         "{ A[j,i] -> B[i,j] }",
+         "{ A[j,i] : 0 <= i < 10 and 0 <= j < 100 }" },
+       { "{ rat: B[i,j] : 0 <= i, j and 3 i + 5 j <= 100 }",
+         "{ A[a,b] -> B[a/2,b/6] }",
+         "{ rat: A[a,b] : 0 <= a, b and 9 a + 5 b <= 600 }" },
+       { "{ B[i,j] : 0 <= i, j and 3 i + 5 j <= 100 }",
+         "{ A[a,b] -> B[a/2,b/6] }",
+         "{ A[a,b] : 0 <= a, b and 9 a + 5 b <= 600 and "
+                   "exists i,j : a = 2 i and b = 6 j }" },
+       { "[n] -> { S[i] : 0 <= i <= 100 }", "[n] -> { S[n] }",
+         "[n] -> { : 0 <= n <= 100 }" },
+       { "{ B[i] : 0 <= i < 100 and exists a : i = 4 a }",
+         "{ A[a] -> B[2a] }",
+         "{ A[a] : 0 <= a < 50 and exists b : a = 2 b }" },
+       { "{ B[i] : 0 <= i < 100 and exists a : i = 4 a }",
+         "{ A[a] -> B[([a/2])] }",
+         "{ A[a] : 0 <= a < 200 and exists b : [a/2] = 4 b }" },
+       { "{ B[i,j,k] : 0 <= i,j,k <= 100 }",
+         "{ A[a] -> B[a,a,a/3] }",
+         "{ A[a] : 0 <= a <= 100 and exists b : a = 3 b }" },
+       { "{ B[i,j] : j = [(i)/2] } ", "{ A[i,j] -> B[i/3,j] }",
+         "{ A[i,j] : j = [(i)/6] and exists a : i = 3 a }" },
+};
+
+int test_preimage(isl_ctx *ctx)
+{
+       int i;
+       isl_basic_set *bset1, *bset2;
+       isl_multi_aff *ma;
+       int equal;
+
+       for (i = 0; i < ARRAY_SIZE(preimage_tests); ++i) {
+               bset1 = isl_basic_set_read_from_str(ctx, preimage_tests[i].set);
+               ma = isl_multi_aff_read_from_str(ctx, preimage_tests[i].ma);
+               bset2 = isl_basic_set_read_from_str(ctx, preimage_tests[i].res);
+               bset1 = isl_basic_set_preimage_multi_aff(bset1, ma);
+               equal = isl_basic_set_is_equal(bset1, bset2);
+               isl_basic_set_free(bset1);
+               isl_basic_set_free(bset2);
+               if (equal < 0)
+                       return -1;
+               if (!equal)
+                       isl_die(ctx, isl_error_unknown, "bad preimage",
+                               return -1);
+       }
+
+       return 0;
+}
+
+struct {
+       const char *ma1;
+       const char *ma;
+       const char *res;
+} pullback_tests[] = {
+       { "{ B[i,j] -> C[i + 2j] }" , "{ A[a,b] -> B[b,a] }",
+         "{ A[a,b] -> C[b + 2a] }" },
+       { "{ B[i] -> C[2i] }", "{ A[a] -> B[(a)/2] }", "{ A[a] -> C[a] }" },
+       { "{ B[i] -> C[(i)/2] }", "{ A[a] -> B[2a] }", "{ A[a] -> C[a] }" },
+       { "{ B[i] -> C[(i)/2] }", "{ A[a] -> B[(a)/3] }",
+         "{ A[a] -> C[(a)/6] }" },
+       { "{ B[i] -> C[2i] }", "{ A[a] -> B[5a] }", "{ A[a] -> C[10a] }" },
+       { "{ B[i] -> C[2i] }", "{ A[a] -> B[(a)/3] }",
+         "{ A[a] -> C[(2a)/3] }" },
+       { "{ B[i,j] -> C[i + j] }", "{ A[a] -> B[a,a] }", "{ A[a] -> C[2a] }"},
+       { "{ B[a] -> C[a,a] }", "{ A[i,j] -> B[i + j] }",
+         "{ A[i,j] -> C[i + j, i + j] }"},
+       { "{ B[i] -> C[([i/2])] }", "{ B[5] }", "{ C[2] }" },
+       { "[n] -> { B[i,j] -> C[([i/2]) + 2j] }",
+         "[n] -> { B[n,[n/3]] }", "[n] -> { C[([n/2]) + 2*[n/3]] }", },
+};
+
+static int test_pullback(isl_ctx *ctx)
+{
+       int i;
+       isl_multi_aff *ma1, *ma2;
+       isl_multi_aff *ma;
+       int equal;
+
+       for (i = 0; i < ARRAY_SIZE(pullback_tests); ++i) {
+               ma1 = isl_multi_aff_read_from_str(ctx, pullback_tests[i].ma1);
+               ma = isl_multi_aff_read_from_str(ctx, pullback_tests[i].ma);
+               ma2 = isl_multi_aff_read_from_str(ctx, pullback_tests[i].res);
+               ma1 = isl_multi_aff_pullback_multi_aff(ma1, ma);
+               equal = isl_multi_aff_plain_is_equal(ma1, ma2);
+               isl_multi_aff_free(ma1);
+               isl_multi_aff_free(ma2);
+               if (equal < 0)
+                       return -1;
+               if (!equal)
+                       isl_die(ctx, isl_error_unknown, "bad pullback",
+                               return -1);
+       }
+
+       return 0;
+}
+
+/* Check that negation is printed correctly.
+ */
+static int test_ast(isl_ctx *ctx)
+{
+       isl_ast_expr *expr, *expr1, *expr2, *expr3;
+       char *str;
+       int ok;
+
+       expr1 = isl_ast_expr_from_id(isl_id_alloc(ctx, "A", NULL));
+       expr2 = isl_ast_expr_from_id(isl_id_alloc(ctx, "B", NULL));
+       expr = isl_ast_expr_add(expr1, expr2);
+       expr = isl_ast_expr_neg(expr);
+       str = isl_ast_expr_to_str(expr);
+       ok = !strcmp(str, "-(A + B)");
+       free(str);
+       isl_ast_expr_free(expr);
+
+       if (!ok)
+               isl_die(ctx, isl_error_unknown,
+                       "isl_ast_expr printed incorrectly", return -1);
+
+       expr1 = isl_ast_expr_from_id(isl_id_alloc(ctx, "A", NULL));
+       expr2 = isl_ast_expr_from_id(isl_id_alloc(ctx, "B", NULL));
+       expr = isl_ast_expr_add(expr1, expr2);
+       expr3 = isl_ast_expr_from_id(isl_id_alloc(ctx, "C", NULL));
+       expr = isl_ast_expr_sub(expr3, expr);
+       str = isl_ast_expr_to_str(expr);
+       ok = !strcmp(str, "C - (A + B)");
+       free(str);
+       isl_ast_expr_free(expr);
+
+       if (!ok)
+               isl_die(ctx, isl_error_unknown,
+                       "isl_ast_expr printed incorrectly", return -1);
+
+       return 0;
+}
+
+/* Internal data structure for before_for and after_for callbacks.
+ *
+ * depth is the current depth
+ * before is the number of times before_for has been called
+ * after is the number of times after_for has been called
+ */
+struct isl_test_codegen_data {
+       int depth;
+       int before;
+       int after;
+};
+
+/* This function is called before each for loop in the AST generated
+ * from test_ast_gen1.
+ *
+ * Increment the number of calls and the depth.
+ * Check that the space returned by isl_ast_build_get_schedule_space
+ * matches the target space of the schedule returned by
+ * isl_ast_build_get_schedule.
+ * Return an isl_id that is checked by the corresponding call
+ * to after_for.
+ */
+static __isl_give isl_id *before_for(__isl_keep isl_ast_build *build,
+       void *user)
+{
+       struct isl_test_codegen_data *data = user;
+       isl_ctx *ctx;
+       isl_space *space;
+       isl_union_map *schedule;
+       isl_union_set *uset;
+       isl_set *set;
+       int empty;
+       char name[] = "d0";
+
+       ctx = isl_ast_build_get_ctx(build);
+
+       if (data->before >= 3)
+               isl_die(ctx, isl_error_unknown,
+                       "unexpected number of for nodes", return NULL);
+       if (data->depth >= 2)
+               isl_die(ctx, isl_error_unknown,
+                       "unexpected depth", return NULL);
+
+       snprintf(name, sizeof(name), "d%d", data->depth);
+       data->before++;
+       data->depth++;
+
+       schedule = isl_ast_build_get_schedule(build);
+       uset = isl_union_map_range(schedule);
+       if (!uset)
+               return NULL;
+       if (isl_union_set_n_set(uset) != 1) {
+               isl_union_set_free(uset);
+               isl_die(ctx, isl_error_unknown,
+                       "expecting single range space", return NULL);
+       }
+
+       space = isl_ast_build_get_schedule_space(build);
+       set = isl_union_set_extract_set(uset, space);
+       isl_union_set_free(uset);
+       empty = isl_set_is_empty(set);
+       isl_set_free(set);
+
+       if (empty < 0)
+               return NULL;
+       if (empty)
+               isl_die(ctx, isl_error_unknown,
+                       "spaces don't match", return NULL);
+
+       return isl_id_alloc(ctx, name, NULL);
+}
+
+/* This function is called after each for loop in the AST generated
+ * from test_ast_gen1.
+ *
+ * Increment the number of calls and decrement the depth.
+ * Check that the annotation attached to the node matches
+ * the isl_id returned by the corresponding call to before_for.
+ */
+static __isl_give isl_ast_node *after_for(__isl_take isl_ast_node *node,
+       __isl_keep isl_ast_build *build, void *user)
+{
+       struct isl_test_codegen_data *data = user;
+       isl_id *id;
+       const char *name;
+       int valid;
+
+       data->after++;
+       data->depth--;
+
+       if (data->after > data->before)
+               isl_die(isl_ast_node_get_ctx(node), isl_error_unknown,
+                       "mismatch in number of for nodes",
+                       return isl_ast_node_free(node));
+
+       id = isl_ast_node_get_annotation(node);
+       if (!id)
+               isl_die(isl_ast_node_get_ctx(node), isl_error_unknown,
+                       "missing annotation", return isl_ast_node_free(node));
+
+       name = isl_id_get_name(id);
+       valid = name && atoi(name + 1) == data->depth;
+       isl_id_free(id);
+
+       if (!valid)
+               isl_die(isl_ast_node_get_ctx(node), isl_error_unknown,
+                       "wrong annotation", return isl_ast_node_free(node));
+
+       return node;
+}
+
+/* Check that the before_each_for and after_each_for callbacks
+ * are called for each for loop in the generated code,
+ * that they are called in the right order and that the isl_id
+ * returned from the before_each_for callback is attached to
+ * the isl_ast_node passed to the corresponding after_each_for call.
+ */
+static int test_ast_gen1(isl_ctx *ctx)
+{
+       const char *str;
+       isl_set *set;
+       isl_union_map *schedule;
+       isl_ast_build *build;
+       isl_ast_node *tree;
+       struct isl_test_codegen_data data;
+
+       str = "[N] -> { : N >= 10 }";
+       set = isl_set_read_from_str(ctx, str);
+       str = "[N] -> { A[i,j] -> S[8,i,3,j] : 0 <= i,j <= N; "
+                   "B[i,j] -> S[8,j,9,i] : 0 <= i,j <= N }";
+       schedule = isl_union_map_read_from_str(ctx, str);
+
+       data.before = 0;
+       data.after = 0;
+       data.depth = 0;
+       build = isl_ast_build_from_context(set);
+       build = isl_ast_build_set_before_each_for(build,
+                       &before_for, &data);
+       build = isl_ast_build_set_after_each_for(build,
+                       &after_for, &data);
+       tree = isl_ast_build_ast_from_schedule(build, schedule);
+       isl_ast_build_free(build);
+       if (!tree)
+               return -1;
+
+       isl_ast_node_free(tree);
+
+       if (data.before != 3 || data.after != 3)
+               isl_die(ctx, isl_error_unknown,
+                       "unexpected number of for nodes", return -1);
+
+       return 0;
+}
+
+/* Check that the AST generator handles domains that are integrally disjoint
+ * but not ratinoally disjoint.
+ */
+static int test_ast_gen2(isl_ctx *ctx)
+{
+       const char *str;
+       isl_set *set;
+       isl_union_map *schedule;
+       isl_union_map *options;
+       isl_ast_build *build;
+       isl_ast_node *tree;
+
+       str = "{ A[i,j] -> [i,j] : 0 <= i,j <= 1 }";
+       schedule = isl_union_map_read_from_str(ctx, str);
+       set = isl_set_universe(isl_space_params_alloc(ctx, 0));
+       build = isl_ast_build_from_context(set);
+
+       str = "{ [i,j] -> atomic[1] : i + j = 1; [i,j] -> unroll[1] : i = j }";
+       options = isl_union_map_read_from_str(ctx, str);
+       build = isl_ast_build_set_options(build, options);
+       tree = isl_ast_build_ast_from_schedule(build, schedule);
+       isl_ast_build_free(build);
+       if (!tree)
+               return -1;
+       isl_ast_node_free(tree);
+
+       return 0;
+}
+
+/* Increment *user on each call.
+ */
+static __isl_give isl_ast_node *count_domains(__isl_take isl_ast_node *node,
+       __isl_keep isl_ast_build *build, void *user)
+{
+       int *n = user;
+
+       (*n)++;
+
+       return node;
+}
+
+/* Test that unrolling tries to minimize the number of instances.
+ * In particular, for the schedule given below, make sure it generates
+ * 3 nodes (rather than 101).
+ */
+static int test_ast_gen3(isl_ctx *ctx)
+{
+       const char *str;
+       isl_set *set;
+       isl_union_map *schedule;
+       isl_union_map *options;
+       isl_ast_build *build;
+       isl_ast_node *tree;
+       int n_domain = 0;
+
+       str = "[n] -> { A[i] -> [i] : 0 <= i <= 100 and n <= i <= n + 2 }";
+       schedule = isl_union_map_read_from_str(ctx, str);
+       set = isl_set_universe(isl_space_params_alloc(ctx, 0));
+
+       str = "{ [i] -> unroll[0] }";
+       options = isl_union_map_read_from_str(ctx, str);
+
+       build = isl_ast_build_from_context(set);
+       build = isl_ast_build_set_options(build, options);
+       build = isl_ast_build_set_at_each_domain(build,
+                       &count_domains, &n_domain);
+       tree = isl_ast_build_ast_from_schedule(build, schedule);
+       isl_ast_build_free(build);
+       if (!tree)
+               return -1;
+
+       isl_ast_node_free(tree);
+
+       if (n_domain != 3)
+               isl_die(ctx, isl_error_unknown,
+                       "unexpected number of for nodes", return -1);
+
+       return 0;
+}
+
+/* Check that if the ast_build_exploit_nested_bounds options is set,
+ * we do not get an outer if node in the generated AST,
+ * while we do get such an outer if node if the options is not set.
+ */
+static int test_ast_gen4(isl_ctx *ctx)
+{
+       const char *str;
+       isl_set *set;
+       isl_union_map *schedule;
+       isl_ast_build *build;
+       isl_ast_node *tree;
+       enum isl_ast_node_type type;
+       int enb;
+
+       enb = isl_options_get_ast_build_exploit_nested_bounds(ctx);
+       str = "[N,M] -> { A[i,j] -> [i,j] : 0 <= i <= N and 0 <= j <= M }";
+
+       isl_options_set_ast_build_exploit_nested_bounds(ctx, 1);
+
+       schedule = isl_union_map_read_from_str(ctx, str);
+       set = isl_set_universe(isl_space_params_alloc(ctx, 0));
+       build = isl_ast_build_from_context(set);
+       tree = isl_ast_build_ast_from_schedule(build, schedule);
+       isl_ast_build_free(build);
+       if (!tree)
+               return -1;
+
+       type = isl_ast_node_get_type(tree);
+       isl_ast_node_free(tree);
+
+       if (type == isl_ast_node_if)
+               isl_die(ctx, isl_error_unknown,
+                       "not expecting if node", return -1);
+
+       isl_options_set_ast_build_exploit_nested_bounds(ctx, 0);
+
+       schedule = isl_union_map_read_from_str(ctx, str);
+       set = isl_set_universe(isl_space_params_alloc(ctx, 0));
+       build = isl_ast_build_from_context(set);
+       tree = isl_ast_build_ast_from_schedule(build, schedule);
+       isl_ast_build_free(build);
+       if (!tree)
+               return -1;
+
+       type = isl_ast_node_get_type(tree);
+       isl_ast_node_free(tree);
+
+       if (type != isl_ast_node_if)
+               isl_die(ctx, isl_error_unknown,
+                       "expecting if node", return -1);
+
+       isl_options_set_ast_build_exploit_nested_bounds(ctx, enb);
+
+       return 0;
+}
+
+static int test_ast_gen(isl_ctx *ctx)
+{
+       if (test_ast_gen1(ctx) < 0)
+               return -1;
+       if (test_ast_gen2(ctx) < 0)
+               return -1;
+       if (test_ast_gen3(ctx) < 0)
+               return -1;
+       if (test_ast_gen4(ctx) < 0)
+               return -1;
+       return 0;
+}
+
 struct {
        const char *name;
        int (*fn)(isl_ctx *ctx);
 } tests [] = {
+       { "conversion", &test_conversion },
        { "list", &test_list },
        { "align parameters", &test_align_parameters },
+       { "preimage", &test_preimage },
+       { "pullback", &test_pullback },
+       { "AST", &test_ast },
+       { "AST generation", &test_ast_gen },
        { "eliminate", &test_eliminate },
-       { "reisdue class", &test_residue_class },
+       { "residue class", &test_residue_class },
        { "div", &test_div },
        { "slice", &test_slice },
        { "fixed power", &test_fixed_power },
@@ -3137,6 +3778,8 @@ struct {
        { "affine hull", &test_affine_hull },
        { "coalesce", &test_coalesce },
        { "factorize", &test_factorize },
+       { "subset", &test_subset },
+       { "subtract", &test_subtract },
 };
 
 int main()
@@ -3153,7 +3796,6 @@ int main()
                if (tests[i].fn(ctx) < 0)
                        goto error;
        }
-       test_subset(ctx);
        test_lift(ctx);
        test_bound(ctx);
        test_union(ctx);