keep track of domain in 0D isl_multi_pw_aff and isl_multi_union_pw_aff
authorSven Verdoolaege <sven.verdoolaege@gmail.com>
Wed, 14 Jun 2017 10:05:26 +0000 (14 12:05 +0200)
committerSven Verdoolaege <sven.verdoolaege@gmail.com>
Sun, 4 Mar 2018 14:39:57 +0000 (4 15:39 +0100)
This ensures that intersect_domain has an effect on objects
of type isl_multi_pw_aff and isl_multi_union_pw_aff.
This in turn makes it possible to round-trip a conversion
from isl_pw_multi_aff to isl_multi_pw_aff and back to isl_pw_multi_aff
for 0D functions.  This truly makes isl_multi_pw_aff a superset of
isl_pw_multi_aff, which it has been claimed to be in the bindings.
This also makes it possible to carry some information
in 0D isl_multi_union_pw_aff objects such that users do not
have to treat this case specially.
In particular, the prefix schedule at a node without an outer band node
now keeps track of the domain, filtered by any outer filter nodes.

The domain is only kept in case of 0D functions since objects
with at least one base expression carry domain information
in the individual base expressions.
The explicit domain gets taken into account on a product
operation such that it wouldn't get lost.

Since the output tuple of a 0D function is empty,
the explicit domain is printed outside of the tuple.
Note that the 0D output space may still have structure,
so it would not be clear where to place the domain
if it were printed inside the tuple.
In case of an isl_multi_union_pw_aff, the tuple is
the outermost construct in the output.
If an explicit domain needs to be printed, the tuple and
the domain are placed inside parentheses to clarify
that the domain belongs to the empty tuple.

It is not immediately obvious whether a pullback of a 0D function
with respect to another function should intersect the domain
with the (shared) domain of the other function, but this is
what this commit does.

This commit attempts to adjust all affected operations,
but it may have missed some.

Signed-off-by: Sven Verdoolaege <sven.verdoolaege@gmail.com>
21 files changed:
Makefile.am
doc/user.pod
isl_aff.c
isl_aff_private.h
isl_input.c
isl_map.c
isl_multi_align_set.c [new file with mode: 0644]
isl_multi_align_templ.c [new file with mode: 0644]
isl_multi_align_union_set.c [new file with mode: 0644]
isl_multi_dims.c
isl_multi_explicit_domain.c [new file with mode: 0644]
isl_multi_intersect.c
isl_multi_no_explicit_domain.c [new file with mode: 0644]
isl_multi_pw_aff_explicit_domain.c [new file with mode: 0644]
isl_multi_templ.c
isl_multi_templ.h
isl_multi_union_pw_aff_explicit_domain.c [new file with mode: 0644]
isl_output.c
isl_test.c
isl_union_map.c
isl_val.c

index 0525e4b..b6f5603 100644 (file)
@@ -344,8 +344,15 @@ EXTRA_DIST = \
        isl_map_lexopt_templ.c \
        isl_maybe_map.h \
        isl_multi_macro.h \
+       isl_multi_explicit_domain.c \
+       isl_multi_pw_aff_explicit_domain.c \
+       isl_multi_union_pw_aff_explicit_domain.c \
+       isl_multi_no_explicit_domain.c \
        isl_multi_templ.c \
        isl_multi_templ.h \
+       isl_multi_align_templ.c \
+       isl_multi_align_set.c \
+       isl_multi_align_union_set.c \
        isl_multi_apply_templ.c \
        isl_multi_apply_set.c \
        isl_multi_apply_union_set.c \
index 30b3ca0..5203ab6 100644 (file)
@@ -270,6 +270,17 @@ be used as an alternative.
 
 =back
 
+=head3 Changes since isl-0.19
+
+=over
+
+=item * Zero-dimensional objects of type C<isl_multi_pw_aff> or
+C<isl_multi_union_pw_aff> can now keep track of an explicit domain.
+This explicit domain, if present, is taken into account
+by various operations that take such objects as input.
+
+=back
+
 =head1 License
 
 C<isl> is released under the MIT license.
@@ -2749,6 +2760,10 @@ Objects of the value type do not have an associated space.
 The space of a multiple value is therefore always a set space.
 Similarly, the space of a multiple union piecewise
 affine expression is always a set space.
+If the base expressions are not total, then
+a corresponding zero-dimensional multiple expression may
+have an explicit domain that keeps track of the domain
+outside of any base expressions.
 
 The multiple expression types defined by C<isl>
 are C<isl_multi_val>, C<isl_multi_aff>, C<isl_multi_pw_aff>,
@@ -2969,10 +2984,7 @@ while there is only a single multiple expression in
 an C<isl_multi_union_pw_aff>, which can therefore only live
 in a single space.  This means that not every
 C<isl_union_pw_multi_aff> can be converted to
-an C<isl_multi_union_pw_aff>.  Conversely, a zero-dimensional
-C<isl_multi_union_pw_aff> carries no information
-about any possible domain and therefore cannot be converted
-to an C<isl_union_pw_multi_aff>.  Moreover, the elements
+an C<isl_multi_union_pw_aff>.  Conversely, the elements
 of an C<isl_multi_union_pw_aff> may be defined over different domains,
 while each multiple expression inside an C<isl_union_pw_multi_aff>
 has a single domain.  The conversion of an C<isl_union_pw_multi_aff>
@@ -4569,8 +4581,9 @@ parameters.
        __isl_give isl_set *isl_pw_aff_params(
                __isl_take isl_pw_aff *pwa);
 
-The function C<isl_multi_union_pw_aff_domain> requires its
-input to have at least one set dimension.
+If no explicit domain was set on a zero-dimensional input to
+C<isl_multi_union_pw_aff_domain>, then this function will
+return a parameter set.
 
        #include <isl/polynomial.h>
        __isl_give isl_qpolynomial *
index 715cf8f..b0a2aa3 100644 (file)
--- a/isl_aff.c
+++ b/isl_aff.c
@@ -3830,6 +3830,7 @@ error:
 #define DOMBASE set
 #define NO_DOMAIN
 
+#include <isl_multi_no_explicit_domain.c>
 #include <isl_multi_templ.c>
 #include <isl_multi_apply_set.c>
 #include <isl_multi_cmp.c>
@@ -6275,14 +6276,32 @@ error:
 #undef DOMBASE
 #define DOMBASE set
 
+#include <isl_multi_explicit_domain.c>
+#include <isl_multi_pw_aff_explicit_domain.c>
 #include <isl_multi_templ.c>
 #include <isl_multi_apply_set.c>
 #include <isl_multi_coalesce.c>
 #include <isl_multi_dims.c>
 #include <isl_multi_gist.c>
 #include <isl_multi_hash.c>
+#include <isl_multi_align_set.c>
 #include <isl_multi_intersect.c>
 
+/* Does "mpa" have a non-trivial explicit domain?
+ *
+ * The explicit domain, if present, is trivial if it represents
+ * an (obviously) universe set.
+ */
+isl_bool isl_multi_pw_aff_has_non_trivial_domain(
+       __isl_keep isl_multi_pw_aff *mpa)
+{
+       if (!mpa)
+               return isl_bool_error;
+       if (!isl_multi_pw_aff_has_explicit_domain(mpa))
+               return isl_bool_false;
+       return isl_bool_not(isl_set_plain_is_universe(mpa->u.dom));
+}
+
 /* Scale the elements of "pma" by the corresponding elements of "mv".
  */
 __isl_give isl_pw_multi_aff *isl_pw_multi_aff_scale_multi_val(
@@ -6493,18 +6512,22 @@ error:
  * on the shared domain of the piecewise affine expressions,
  * in the special case of a 0D multi piecewise affine expression.
  *
- * A 0D multi piecewise affine expression does not carry any domain
- * information, so create a piecewise multi affine expression
- * with a universe domain.
+ * Create a piecewise multi affine expression with the explicit domain of
+ * the 0D multi piecewise affine expression as domain.
  */
 static __isl_give isl_pw_multi_aff *isl_pw_multi_aff_from_multi_pw_aff_0D(
        __isl_take isl_multi_pw_aff *mpa)
 {
        isl_space *space;
+       isl_set *dom;
+       isl_multi_aff *ma;
 
        space = isl_multi_pw_aff_get_space(mpa);
+       dom = isl_multi_pw_aff_get_explicit_domain(mpa);
        isl_multi_pw_aff_free(mpa);
-       return isl_pw_multi_aff_zero(space);
+
+       ma = isl_multi_aff_zero(space);
+       return isl_pw_multi_aff_alloc(dom, ma);
 }
 
 /* Construct and return a piecewise multi affine expression
@@ -6571,6 +6594,10 @@ __isl_give isl_multi_pw_aff *isl_multi_pw_aff_from_multi_aff(
 
 /* Construct and return a multi piecewise affine expression
  * that is equal to the given piecewise multi affine expression.
+ *
+ * If the resulting multi piecewise affine expression has
+ * an explicit domain, then assign it the domain of the input.
+ * In other cases, the domain is stored in the individual elements.
  */
 __isl_give isl_multi_pw_aff *isl_multi_pw_aff_from_pw_multi_aff(
        __isl_take isl_pw_multi_aff *pma)
@@ -6592,6 +6619,12 @@ __isl_give isl_multi_pw_aff *isl_multi_pw_aff_from_pw_multi_aff(
                pa = isl_pw_multi_aff_get_pw_aff(pma, i);
                mpa = isl_multi_pw_aff_set_pw_aff(mpa, i, pa);
        }
+       if (isl_multi_pw_aff_has_explicit_domain(mpa)) {
+               isl_set *dom;
+
+               dom = isl_pw_multi_aff_domain(isl_pw_multi_aff_copy(pma));
+               mpa = isl_multi_pw_aff_intersect_domain(mpa, dom);
+       }
 
        isl_pw_multi_aff_free(pma);
        return mpa;
@@ -6723,6 +6756,9 @@ isl_bool isl_pw_multi_aff_is_equal(__isl_keep isl_pw_multi_aff *pma1,
  * In other words, plug in "ma" in "mpa".
  *
  * The parameters of "mpa" and "ma" are assumed to have been aligned.
+ *
+ * If "mpa" has an explicit domain, then it is this domain
+ * that needs to undergo a pullback, i.e., a preimage.
  */
 static __isl_give isl_multi_pw_aff *isl_multi_pw_aff_pullback_multi_aff_aligned(
        __isl_take isl_multi_pw_aff *mpa, __isl_take isl_multi_aff *ma)
@@ -6745,6 +6781,12 @@ static __isl_give isl_multi_pw_aff *isl_multi_pw_aff_pullback_multi_aff_aligned(
                if (!mpa->u.p[i])
                        goto error;
        }
+       if (isl_multi_pw_aff_has_explicit_domain(mpa)) {
+               mpa->u.dom = isl_set_preimage_multi_aff(mpa->u.dom,
+                                                       isl_multi_aff_copy(ma));
+               if (!mpa->u.dom)
+                       goto error;
+       }
 
        isl_multi_aff_free(ma);
        isl_space_free(mpa->space);
@@ -6785,6 +6827,9 @@ error:
  * In other words, plug in "pma" in "mpa".
  *
  * The parameters of "mpa" and "mpa" are assumed to have been aligned.
+ *
+ * If "mpa" has an explicit domain, then it is this domain
+ * that needs to undergo a pullback, i.e., a preimage.
  */
 static __isl_give isl_multi_pw_aff *
 isl_multi_pw_aff_pullback_pw_multi_aff_aligned(
@@ -6806,6 +6851,12 @@ isl_multi_pw_aff_pullback_pw_multi_aff_aligned(
                if (!mpa->u.p[i])
                        goto error;
        }
+       if (isl_multi_pw_aff_has_explicit_domain(mpa)) {
+               mpa->u.dom = isl_set_preimage_pw_multi_aff(mpa->u.dom,
+                                                   isl_pw_multi_aff_copy(pma));
+               if (!mpa->u.dom)
+                       goto error;
+       }
 
        isl_pw_multi_aff_free(pma);
        isl_space_free(mpa->space);
@@ -7036,6 +7087,9 @@ __isl_give isl_pw_aff *isl_pw_aff_pullback_multi_pw_aff(
  * The parameters of "mpa1" and "mpa2" are assumed to have been aligned.
  *
  * We pullback each member of "mpa1" in turn.
+ *
+ * If "mpa1" has an explicit domain, then it is this domain
+ * that needs to undergo a pullback instead, i.e., a preimage.
  */
 static __isl_give isl_multi_pw_aff *
 isl_multi_pw_aff_pullback_multi_pw_aff_aligned(
@@ -7058,6 +7112,12 @@ isl_multi_pw_aff_pullback_multi_pw_aff_aligned(
                        goto error;
        }
 
+       if (isl_multi_pw_aff_has_explicit_domain(mpa1)) {
+               mpa1->u.dom = isl_set_preimage_multi_pw_aff(mpa1->u.dom,
+                                               isl_multi_pw_aff_copy(mpa2));
+               if (!mpa1->u.dom)
+                       goto error;
+       }
        mpa1 = isl_multi_pw_aff_reset_space(mpa1, space);
 
        isl_multi_pw_aff_free(mpa2);
@@ -8089,14 +8149,42 @@ error:
 #define NO_IDENTITY
 #define NO_GIST
 
+#include <isl_multi_explicit_domain.c>
+#include <isl_multi_union_pw_aff_explicit_domain.c>
 #include <isl_multi_templ.c>
 #include <isl_multi_apply_set.c>
 #include <isl_multi_apply_union_set.c>
 #include <isl_multi_coalesce.c>
 #include <isl_multi_floor.c>
 #include <isl_multi_gist.c>
+#include <isl_multi_align_set.c>
+#include <isl_multi_align_union_set.c>
 #include <isl_multi_intersect.c>
 
+/* Does "mupa" have a non-trivial explicit domain?
+ *
+ * The explicit domain, if present, is trivial if it represents
+ * an (obviously) universe parameter set.
+ */
+isl_bool isl_multi_union_pw_aff_has_non_trivial_domain(
+       __isl_keep isl_multi_union_pw_aff *mupa)
+{
+       isl_bool is_params, trivial;
+       isl_set *set;
+
+       if (!mupa)
+               return isl_bool_error;
+       if (!isl_multi_union_pw_aff_has_explicit_domain(mupa))
+               return isl_bool_false;
+       is_params = isl_union_set_is_params(mupa->u.dom);
+       if (is_params < 0 || !is_params)
+               return isl_bool_not(is_params);
+       set = isl_set_from_union_set(isl_union_set_copy(mupa->u.dom));
+       trivial = isl_set_plain_is_universe(set);
+       isl_set_free(set);
+       return isl_bool_not(trivial);
+}
+
 /* Construct a multiple union piecewise affine expression
  * in the given space with value zero in each of the output dimensions.
  *
@@ -8136,15 +8224,70 @@ error:
  * the defined expression on the symmetric difference of the domains.
  *
  * We simply iterate over the elements in both arguments and
- * call isl_union_pw_aff_union_add on each of them.
+ * call isl_union_pw_aff_union_add on each of them, if there is
+ * at least one element.
+ *
+ * Otherwise, the two expressions have an explicit domain and
+ * the union of these explicit domains is computed.
+ * This assumes that the explicit domains are either both in terms
+ * of specific domains elements or both in terms of parameters.
+ * However, if one of the expressions does not have any constraints
+ * on its explicit domain, then this is allowed as well and the result
+ * is the expression with no constraints on its explicit domain.
  */
 static __isl_give isl_multi_union_pw_aff *
 isl_multi_union_pw_aff_union_add_aligned(
        __isl_take isl_multi_union_pw_aff *mupa1,
        __isl_take isl_multi_union_pw_aff *mupa2)
 {
-       return isl_multi_union_pw_aff_bin_op(mupa1, mupa2,
+       isl_bool has_domain, is_params1, is_params2;
+
+       if (isl_multi_union_pw_aff_check_equal_space(mupa1, mupa2) < 0)
+               goto error;
+       if (mupa1->n > 0)
+               return isl_multi_union_pw_aff_bin_op(mupa1, mupa2,
                                            &isl_union_pw_aff_union_add);
+       if (isl_multi_union_pw_aff_check_has_explicit_domain(mupa1) < 0 ||
+           isl_multi_union_pw_aff_check_has_explicit_domain(mupa2) < 0)
+               goto error;
+
+       has_domain = isl_multi_union_pw_aff_has_non_trivial_domain(mupa1);
+       if (has_domain < 0)
+               goto error;
+       if (!has_domain) {
+               isl_multi_union_pw_aff_free(mupa2);
+               return mupa1;
+       }
+       has_domain = isl_multi_union_pw_aff_has_non_trivial_domain(mupa2);
+       if (has_domain < 0)
+               goto error;
+       if (!has_domain) {
+               isl_multi_union_pw_aff_free(mupa1);
+               return mupa2;
+       }
+
+       is_params1 = isl_union_set_is_params(mupa1->u.dom);
+       is_params2 = isl_union_set_is_params(mupa2->u.dom);
+       if (is_params1 < 0 || is_params2 < 0)
+               goto error;
+       if (is_params1 != is_params2)
+               isl_die(isl_multi_union_pw_aff_get_ctx(mupa1),
+                       isl_error_invalid,
+                       "cannot compute union of concrete domain and "
+                       "parameter constraints", goto error);
+       mupa1 = isl_multi_union_pw_aff_cow(mupa1);
+       if (!mupa1)
+               goto error;
+       mupa1->u.dom = isl_union_set_union(mupa1->u.dom,
+                                           isl_union_set_copy(mupa2->u.dom));
+       if (!mupa1->u.dom)
+               goto error;
+       isl_multi_union_pw_aff_free(mupa2);
+       return mupa1;
+error:
+       isl_multi_union_pw_aff_free(mupa1);
+       isl_multi_union_pw_aff_free(mupa2);
+       return NULL;
 }
 
 /* Compute the sum of "mupa1" and "mupa2" on the union of their domains,
@@ -8239,6 +8382,10 @@ static isl_stat extract_space(__isl_take isl_pw_multi_aff *pma, void *user)
  *
  * In order to be able to perform the conversion, the input
  * needs to be non-empty and may only involve a single range space.
+ *
+ * If the resulting multi union piecewise affine expression has
+ * an explicit domain, then assign it the domain of the input.
+ * In other cases, the domain is stored in the individual elements.
  */
 __isl_give isl_multi_union_pw_aff *
 isl_multi_union_pw_aff_from_union_pw_multi_aff(
@@ -8270,6 +8417,14 @@ isl_multi_union_pw_aff_from_union_pw_multi_aff(
                upa = isl_union_pw_multi_aff_get_union_pw_aff(upma, i);
                mupa = isl_multi_union_pw_aff_set_union_pw_aff(mupa, i, upa);
        }
+       if (isl_multi_union_pw_aff_has_explicit_domain(mupa)) {
+               isl_union_set *dom;
+               isl_union_pw_multi_aff *copy;
+
+               copy = isl_union_pw_multi_aff_copy(upma);
+               dom = isl_union_pw_multi_aff_domain(copy);
+               mupa = isl_multi_union_pw_aff_intersect_domain(mupa, dom);
+       }
 
        isl_union_pw_multi_aff_free(upma);
        return mupa;
@@ -8372,6 +8527,10 @@ __isl_give isl_multi_union_pw_aff *isl_multi_union_pw_aff_multi_aff_on_domain(
 /* Return a multiple union piecewise affine expression
  * that is equal to "pma" on "domain", assuming "domain" and "pma"
  * have been aligned.
+ *
+ * If the resulting multi union piecewise affine expression has
+ * an explicit domain, then assign it the input domain.
+ * In other cases, the domain is stored in the individual elements.
  */
 static __isl_give isl_multi_union_pw_aff *
 isl_multi_union_pw_aff_pw_multi_aff_on_domain_aligned(
@@ -8396,6 +8555,9 @@ isl_multi_union_pw_aff_pw_multi_aff_on_domain_aligned(
                                            isl_union_set_copy(domain), pa);
                mupa = isl_multi_union_pw_aff_set_union_pw_aff(mupa, i, upa);
        }
+       if (isl_multi_union_pw_aff_has_explicit_domain(mupa))
+               mupa = isl_multi_union_pw_aff_intersect_domain(mupa,
+                                                   isl_union_set_copy(domain));
 
        isl_union_set_free(domain);
        isl_pw_multi_aff_free(pma);
@@ -8437,6 +8599,8 @@ error:
 
 /* Return a union set containing those elements in the domains
  * of the elements of "mupa" where they are all zero.
+ *
+ * If there are no elements, then simply return the entire domain.
  */
 __isl_give isl_union_set *isl_multi_union_pw_aff_zero_union_set(
        __isl_take isl_multi_union_pw_aff *mupa)
@@ -8450,9 +8614,7 @@ __isl_give isl_union_set *isl_multi_union_pw_aff_zero_union_set(
 
        n = isl_multi_union_pw_aff_dim(mupa, isl_dim_set);
        if (n == 0)
-               isl_die(isl_multi_union_pw_aff_get_ctx(mupa), isl_error_invalid,
-                       "cannot determine zero set "
-                       "of zero-dimensional function", goto error);
+               return isl_multi_union_pw_aff_domain(mupa);
 
        upa = isl_multi_union_pw_aff_get_union_pw_aff(mupa, 0);
        zero = isl_union_pw_aff_zero_union_set(upa);
@@ -8468,27 +8630,37 @@ __isl_give isl_union_set *isl_multi_union_pw_aff_zero_union_set(
 
        isl_multi_union_pw_aff_free(mupa);
        return zero;
-error:
-       isl_multi_union_pw_aff_free(mupa);
-       return NULL;
 }
 
 /* Construct a union map mapping the shared domain
  * of the union piecewise affine expressions to the range of "mupa"
  * in the special case of a 0D multi union piecewise affine expression.
  *
- * There is no way to extract a domain from a zero-dimensional
- * isl_multi_union_pw_aff.
+ * Construct a map between the explicit domain of "mupa" and
+ * the range space.
+ * Note that this assumes that the domain consists of explicit elements.
  */
 static __isl_give isl_union_map *isl_union_map_from_multi_union_pw_aff_0D(
        __isl_take isl_multi_union_pw_aff *mupa)
 {
-       isl_die(isl_multi_union_pw_aff_get_ctx(mupa), isl_error_invalid,
-               "cannot determine domain of zero-dimensional "
-               "isl_multi_union_pw_aff", goto error);
-error:
-       isl_multi_union_pw_aff_free(mupa);
-       return NULL;
+       isl_bool is_params;
+       isl_space *space;
+       isl_union_set *dom, *ran;
+
+       space = isl_multi_union_pw_aff_get_space(mupa);
+       dom = isl_multi_union_pw_aff_domain(mupa);
+       ran = isl_union_set_from_set(isl_set_universe(space));
+
+       is_params = isl_union_set_is_params(dom);
+       if (is_params < 0)
+               dom = isl_union_set_free(dom);
+       else if (is_params)
+               isl_die(isl_union_set_get_ctx(dom), isl_error_invalid,
+                       "cannot create union map from expression without "
+                       "explicit domain elements",
+                       dom = isl_union_set_free(dom));
+
+       return isl_union_map_from_domain_and_range(dom, ran);
 }
 
 /* Construct a union map mapping the shared domain
@@ -8496,8 +8668,8 @@ error:
  * with each dimension in the range equated to the
  * corresponding union piecewise affine expression.
  *
- * The input cannot be zero-dimensional as there is
- * no way to extract a domain from a zero-dimensional isl_multi_union_pw_aff.
+ * If the input is zero-dimensional, then construct a mapping
+ * from its explicit domain.
  */
 __isl_give isl_union_map *isl_union_map_from_multi_union_pw_aff(
        __isl_take isl_multi_union_pw_aff *mupa)
@@ -8592,26 +8764,29 @@ isl_union_pw_multi_aff_reset_range_space(
  * that is equal to the given multi union piecewise affine expression,
  * in the special case of a 0D multi union piecewise affine expression.
  *
- * There is no way to extract a domain from a zero-dimensional
- * isl_multi_union_pw_aff.
+ * Construct a union piecewise multi affine expression
+ * on top of the explicit domain of the input.
  */
 __isl_give isl_union_pw_multi_aff *
 isl_union_pw_multi_aff_from_multi_union_pw_aff_0D(
        __isl_take isl_multi_union_pw_aff *mupa)
 {
-       isl_die(isl_multi_union_pw_aff_get_ctx(mupa), isl_error_invalid,
-               "cannot determine domain of zero-dimensional "
-               "isl_multi_union_pw_aff", goto error);
-error:
-       isl_multi_union_pw_aff_free(mupa);
-       return NULL;
+       isl_space *space;
+       isl_multi_val *mv;
+       isl_union_set *domain;
+
+       space = isl_multi_union_pw_aff_get_space(mupa);
+       mv = isl_multi_val_zero(space);
+       domain = isl_multi_union_pw_aff_domain(mupa);
+       return isl_union_pw_multi_aff_multi_val_on_domain(domain, mv);
 }
 
 /* Construct and return a union piecewise multi affine expression
  * that is equal to the given multi union piecewise affine expression.
  *
- * In order to be able to perform the conversion, the input
- * needs to have a least one output dimension.
+ * If the input is zero-dimensional, then
+ * construct a union piecewise multi affine expression
+ * on top of the explicit domain of the input.
  */
 __isl_give isl_union_pw_multi_aff *
 isl_union_pw_multi_aff_from_multi_union_pw_aff(
@@ -8650,19 +8825,15 @@ isl_union_pw_multi_aff_from_multi_union_pw_aff(
 /* Intersect the range of "mupa" with "range",
  * in the special case where "mupa" is 0D.
  *
- * A 0D isl_multi_union_pw_aff does not carry any domain information,
- * so the information in "range" cannot be included.
+ * Intersect the domain of "mupa" with the constraints on the parameters
+ * of "range".
  */
 static __isl_give isl_multi_union_pw_aff *mupa_intersect_range_0D(
        __isl_take isl_multi_union_pw_aff *mupa, __isl_take isl_set *range)
 {
-       isl_die(isl_multi_union_pw_aff_get_ctx(mupa), isl_error_invalid,
-               "cannot intersect range of zero-dimensional "
-               "isl_multi_union_pw_aff", goto error);
-error:
-       isl_multi_union_pw_aff_free(mupa);
-       isl_set_free(range);
-       return NULL;
+       range = isl_set_params(range);
+       mupa = isl_multi_union_pw_aff_intersect_params(mupa, range);
+       return mupa;
 }
 
 /* Intersect the range of "mupa" with "range".
@@ -8710,20 +8881,25 @@ error:
 /* Return the shared domain of the elements of "mupa",
  * in the special case where "mupa" is zero-dimensional.
  *
- * There is no way to extract a domain from a zero-dimensional
- * isl_multi_union_pw_aff.
+ * Return the explicit domain of "mupa".
+ * Note that this domain may be a parameter set, either
+ * because "mupa" is meant to live in a set space or
+ * because no explicit domain has been set.
  */
 __isl_give isl_union_set *isl_multi_union_pw_aff_domain_0D(
        __isl_take isl_multi_union_pw_aff *mupa)
 {
-       isl_die(isl_multi_union_pw_aff_get_ctx(mupa), isl_error_invalid,
-               "cannot determine domain", goto error);
-error:
+       isl_union_set *dom;
+
+       dom = isl_multi_union_pw_aff_get_explicit_domain(mupa);
        isl_multi_union_pw_aff_free(mupa);
-       return NULL;
+
+       return dom;
 }
 
 /* Return the shared domain of the elements of "mupa".
+ *
+ * If "mupa" is zero-dimensional, then return its explicit domain.
  */
 __isl_give isl_union_set *isl_multi_union_pw_aff_domain(
        __isl_take isl_multi_union_pw_aff *mupa)
@@ -8860,26 +9036,22 @@ error:
 /* Apply "ma" to "mupa", in the special case where "mupa" is 0D.
  * The space of "mupa" is known to be compatible with the domain of "ma".
  *
- * This function is currently only called when the number of output
- * dimensions of "ma" is greater than zero, in which case no result
- * can be computed because a 0D isl_multi_union_pw_aff
- * does not carry any domain information.
+ * Construct an isl_multi_union_pw_aff that is equal to "ma"
+ * on the domain of "mupa".
  */
 static __isl_give isl_multi_union_pw_aff *mupa_apply_multi_aff_0D(
        __isl_take isl_multi_union_pw_aff *mupa, __isl_take isl_multi_aff *ma)
 {
-       isl_die(isl_multi_aff_get_ctx(ma), isl_error_invalid,
-               "cannot determine domains", goto error);
-error:
-       isl_multi_union_pw_aff_free(mupa);
-       isl_multi_aff_free(ma);
-       return NULL;
+       isl_union_set *dom;
+
+       dom = isl_multi_union_pw_aff_domain(mupa);
+       ma = isl_multi_aff_project_domain_on_params(ma);
+
+       return isl_multi_union_pw_aff_multi_aff_on_domain(dom, ma);
 }
 
 /* Apply "ma" to "mupa".  The space of "mupa" needs to be compatible
  * with the domain of "ma".
- * Furthermore, the dimension of this space needs to be greater than zero,
- * unless the dimension of the target space of "ma" is also zero.
  * The result is defined over the shared domain of the elements of "mupa"
  */
 __isl_give isl_multi_union_pw_aff *isl_multi_union_pw_aff_apply_multi_aff(
@@ -8908,7 +9080,7 @@ __isl_give isl_multi_union_pw_aff *isl_multi_union_pw_aff_apply_multi_aff(
                isl_die(isl_multi_aff_get_ctx(ma), isl_error_invalid,
                        "spaces don't match", goto error);
        n_out = isl_multi_aff_dim(ma, isl_dim_out);
-       if (isl_multi_aff_dim(ma, isl_dim_in) == 0 && n_out != 0)
+       if (isl_multi_aff_dim(ma, isl_dim_in) == 0)
                return mupa_apply_multi_aff_0D(mupa, ma);
 
        space1 = isl_space_range(isl_multi_aff_get_space(ma));
@@ -8936,18 +9108,18 @@ error:
 /* Apply "pa" to "mupa", in the special case where "mupa" is 0D.
  * The space of "mupa" is known to be compatible with the domain of "pa".
  *
- * The result cannot be computed because a 0D isl_multi_union_pw_aff
- * does not carry any domain information.
+ * Construct an isl_multi_union_pw_aff that is equal to "pa"
+ * on the domain of "mupa".
  */
 static __isl_give isl_union_pw_aff *isl_multi_union_pw_aff_apply_pw_aff_0D(
        __isl_take isl_multi_union_pw_aff *mupa, __isl_take isl_pw_aff *pa)
 {
-       isl_die(isl_pw_aff_get_ctx(pa), isl_error_invalid,
-               "cannot determine domains", goto error);
-error:
-       isl_multi_union_pw_aff_free(mupa);
-       isl_pw_aff_free(pa);
-       return NULL;
+       isl_union_set *dom;
+
+       dom = isl_multi_union_pw_aff_domain(mupa);
+       pa = isl_pw_aff_project_domain_on_params(pa);
+
+       return isl_union_pw_aff_pw_aff_on_domain(dom, pa);
 }
 
 /* Apply "pa" to "mupa".  The space of "mupa" needs to be compatible
@@ -9012,27 +9184,23 @@ error:
 /* Apply "pma" to "mupa", in the special case where "mupa" is 0D.
  * The space of "mupa" is known to be compatible with the domain of "pma".
  *
- * This function is currently only called when the number of output
- * dimensions of "pma" is greater than zero, in which case no result
- * can be computed because a 0D isl_multi_union_pw_aff
- * does not carry any domain information.
+ * Construct an isl_multi_union_pw_aff that is equal to "pma"
+ * on the domain of "mupa".
  */
 static __isl_give isl_multi_union_pw_aff *mupa_apply_pw_multi_aff_0D(
        __isl_take isl_multi_union_pw_aff *mupa,
        __isl_take isl_pw_multi_aff *pma)
 {
-       isl_die(isl_pw_multi_aff_get_ctx(pma), isl_error_invalid,
-               "cannot determine domains", goto error);
-error:
-       isl_multi_union_pw_aff_free(mupa);
-       isl_pw_multi_aff_free(pma);
-       return NULL;
+       isl_union_set *dom;
+
+       dom = isl_multi_union_pw_aff_domain(mupa);
+       pma = isl_pw_multi_aff_project_domain_on_params(pma);
+
+       return isl_multi_union_pw_aff_pw_multi_aff_on_domain(dom, pma);
 }
 
 /* Apply "pma" to "mupa".  The space of "mupa" needs to be compatible
  * with the domain of "pma".
- * Furthermore, the dimension of this space needs to be greater than zero,
- * unless the dimension of the target space of "pma" is also zero.
  * The result is defined over the shared domain of the elements of "mupa"
  */
 __isl_give isl_multi_union_pw_aff *isl_multi_union_pw_aff_apply_pw_multi_aff(
@@ -9062,7 +9230,7 @@ __isl_give isl_multi_union_pw_aff *isl_multi_union_pw_aff_apply_pw_multi_aff(
                isl_die(isl_pw_multi_aff_get_ctx(pma), isl_error_invalid,
                        "spaces don't match", goto error);
        n_out = isl_pw_multi_aff_dim(pma, isl_dim_out);
-       if (isl_pw_multi_aff_dim(pma, isl_dim_in) == 0 && n_out != 0)
+       if (isl_pw_multi_aff_dim(pma, isl_dim_in) == 0)
                return mupa_apply_pw_multi_aff_0D(mupa, pma);
 
        space1 = isl_space_range(isl_pw_multi_aff_get_space(pma));
@@ -9087,11 +9255,47 @@ error:
        return NULL;
 }
 
+/* Replace the explicit domain of "mupa" by its preimage under "upma".
+ * If the explicit domain only keeps track of constraints on the parameters,
+ * then only update those constraints.
+ */
+static __isl_give isl_multi_union_pw_aff *preimage_explicit_domain(
+       __isl_take isl_multi_union_pw_aff *mupa,
+       __isl_keep isl_union_pw_multi_aff *upma)
+{
+       isl_bool is_params;
+
+       if (isl_multi_union_pw_aff_check_has_explicit_domain(mupa) < 0)
+               return isl_multi_union_pw_aff_free(mupa);
+
+       mupa = isl_multi_union_pw_aff_cow(mupa);
+       if (!mupa)
+               return NULL;
+
+       is_params = isl_union_set_is_params(mupa->u.dom);
+       if (is_params < 0)
+               return isl_multi_union_pw_aff_free(mupa);
+
+       upma = isl_union_pw_multi_aff_copy(upma);
+       if (is_params)
+               mupa->u.dom = isl_union_set_intersect_params(mupa->u.dom,
+                   isl_union_set_params(isl_union_pw_multi_aff_domain(upma)));
+       else
+               mupa->u.dom = isl_union_set_preimage_union_pw_multi_aff(
+                                                           mupa->u.dom, upma);
+       if (!mupa->u.dom)
+               return isl_multi_union_pw_aff_free(mupa);
+       return mupa;
+}
+
 /* Compute the pullback of "mupa" by the function represented by "upma".
  * In other words, plug in "upma" in "mupa".  The result contains
  * expressions defined over the domain space of "upma".
  *
  * Run over all elements of "mupa" and plug in "upma" in each of them.
+ *
+ * If "mupa" has an explicit domain, then it is this domain
+ * that needs to undergo a pullback instead, i.e., a preimage.
  */
 __isl_give isl_multi_union_pw_aff *
 isl_multi_union_pw_aff_pullback_union_pw_multi_aff(
@@ -9104,6 +9308,7 @@ isl_multi_union_pw_aff_pullback_union_pw_multi_aff(
                                    isl_union_pw_multi_aff_get_space(upma));
        upma = isl_union_pw_multi_aff_align_params(upma,
                                    isl_multi_union_pw_aff_get_space(mupa));
+       mupa = isl_multi_union_pw_aff_cow(mupa);
        if (!mupa || !upma)
                goto error;
 
@@ -9117,6 +9322,9 @@ isl_multi_union_pw_aff_pullback_union_pw_multi_aff(
                mupa = isl_multi_union_pw_aff_set_union_pw_aff(mupa, i, upa);
        }
 
+       if (isl_multi_union_pw_aff_has_explicit_domain(mupa))
+               mupa = preimage_explicit_domain(mupa, upma);
+
        isl_union_pw_multi_aff_free(upma);
        return mupa;
 error:
index 8ee5b00..09a14d8 100644 (file)
@@ -159,9 +159,14 @@ __isl_give isl_basic_set *isl_aff_pos_basic_set(__isl_take isl_aff *aff);
 
 #undef BASE
 #define BASE pw_aff
+#undef DOMBASE
+#define DOMBASE set
+#define EXPLICIT_DOMAIN
 
 #include <isl_multi_templ.h>
 
+#undef EXPLICIT_DOMAIN
+
 #undef EL
 #define EL isl_union_pw_aff
 
@@ -169,9 +174,14 @@ __isl_give isl_basic_set *isl_aff_pos_basic_set(__isl_take isl_aff *aff);
 
 #undef BASE
 #define BASE union_pw_aff
+#undef DOMBASE
+#define DOMBASE union_set
+#define EXPLICIT_DOMAIN
 
 #include <isl_multi_templ.h>
 
+#undef EXPLICIT_DOMAIN
+
 #undef EL
 #define EL isl_union_pw_multi_aff
 
index a6ba79e..ea0fa71 100644 (file)
@@ -3723,6 +3723,9 @@ __isl_give isl_multi_aff *isl_multi_aff_read_from_str(isl_ctx *ctx,
  * The input format is similar to that of map, except that any conditions
  * on the domains should be specified inside the tuple since each
  * piecewise affine expression may have a different domain.
+ * However, additional, shared conditions can also be specified.
+ * This is especially useful for setting the explicit domain
+ * of a zero-dimensional isl_multi_pw_aff.
  *
  * Since we do not know in advance if the isl_multi_pw_aff lives
  * in a set or a map space, we first read the first tuple and check
@@ -3765,6 +3768,9 @@ __isl_give isl_multi_pw_aff *isl_stream_read_multi_pw_aff(
                        goto error;
        }
 
+       if (isl_stream_eat_if_available(s, ':'))
+               dom = read_formula(s, v, dom, 0);
+
        if (isl_stream_eat(s, '}'))
                goto error;
 
@@ -3970,8 +3976,9 @@ static int next_is_param_tuple(__isl_keep isl_stream *s)
        return is_tuple;
 }
 
-/* Read the body of an isl_multi_union_pw_aff from "s",
- * i.e., everything except the parameter specification.
+/* Read the core of a body of an isl_multi_union_pw_aff from "s",
+ * i.e., everything except the parameter specification and
+ * without shared domain constraints.
  * "v" contains a description of the identifiers parsed so far.
  * The parameters, if any, are specified by "space".
  *
@@ -3983,7 +3990,7 @@ static int next_is_param_tuple(__isl_keep isl_stream *s)
  * elements in a list and construct the result from the tuple space and
  * the list.
  */
-static __isl_give isl_multi_union_pw_aff *read_multi_union_pw_aff_body(
+static __isl_give isl_multi_union_pw_aff *read_multi_union_pw_aff_body_core(
        __isl_keep isl_stream *s, struct vars *v, __isl_take isl_space *space)
 {
        isl_union_pw_aff_list *list;
@@ -3997,6 +4004,78 @@ static __isl_give isl_multi_union_pw_aff *read_multi_union_pw_aff_body(
        return mupa;
 }
 
+/* Read the body of an isl_union_set from "s",
+ * i.e., everything except the parameter specification.
+ * "v" contains a description of the identifiers parsed so far.
+ * The parameters, if any, are specified by "space".
+ *
+ * First read a generic disjunction of object bodies and then try and extract
+ * an isl_union_set from that.
+ */
+static __isl_give isl_union_set *read_union_set_body(__isl_keep isl_stream *s,
+       struct vars *v, __isl_take isl_space *space)
+{
+       struct isl_obj obj = { isl_obj_set, NULL };
+       isl_map *map;
+
+       map = isl_set_universe(space);
+       if (isl_stream_eat(s, '{') < 0)
+               goto error;
+       obj = obj_read_disjuncts(s, v, map);
+       if (isl_stream_eat(s, '}') < 0)
+               goto error;
+       isl_map_free(map);
+
+       return extract_union_set(s->ctx, obj);
+error:
+       obj.type->free(obj.v);
+       isl_map_free(map);
+       return NULL;
+}
+
+/* Read the body of an isl_multi_union_pw_aff from "s",
+ * i.e., everything except the parameter specification.
+ * "v" contains a description of the identifiers parsed so far.
+ * The parameters, if any, are specified by "space".
+ *
+ * In particular, handle the special case with shared domain constraints.
+ * These are specified as
+ *
+ *     ([...] : ...)
+ *
+ * and are especially useful for setting the explicit domain
+ * of a zero-dimensional isl_multi_union_pw_aff.
+ * The core isl_multi_union_pw_aff body ([...]) is read by
+ * read_multi_union_pw_aff_body_core.
+ */
+static __isl_give isl_multi_union_pw_aff *read_multi_union_pw_aff_body(
+       __isl_keep isl_stream *s, struct vars *v, __isl_take isl_space *space)
+{
+       isl_multi_union_pw_aff *mupa;
+
+       if (!isl_stream_next_token_is(s, '('))
+               return read_multi_union_pw_aff_body_core(s, v, space);
+
+       if (isl_stream_eat(s, '(') < 0)
+               goto error;
+       mupa = read_multi_union_pw_aff_body_core(s, v, isl_space_copy(space));
+       if (isl_stream_eat_if_available(s, ':')) {
+               isl_union_set *dom;
+
+               dom = read_union_set_body(s, v, space);
+               mupa = isl_multi_union_pw_aff_intersect_domain(mupa, dom);
+       } else {
+               isl_space_free(space);
+       }
+       if (isl_stream_eat(s, ')') < 0)
+               return isl_multi_union_pw_aff_free(mupa);
+
+       return mupa;
+error:
+       isl_space_free(space);
+       return NULL;
+}
+
 /* Read an isl_multi_union_pw_aff from "s".
  *
  * The input has the form
@@ -4007,11 +4086,22 @@ static __isl_give isl_multi_union_pw_aff *read_multi_union_pw_aff_body(
  *
  *     [..] -> [{ [..] : ... ; [..] : ... }, { [..] : ... ; [..] : ... }]
  *
+ * Additionally, a shared domain may be specified as
+ *
+ *     ([..] : ...)
+ *
+ * or
+ *
+ *     [..] -> ([..] : ...)
+ *
+ * The first case is handled by the caller, the second case
+ * is handled by read_multi_union_pw_aff_body.
+ *
  * We first check for the special case of an empty tuple "[]".
  * Then we check if there are any parameters.
  * Finally, read the tuple and construct the result.
  */
-__isl_give isl_multi_union_pw_aff *isl_stream_read_multi_union_pw_aff(
+static __isl_give isl_multi_union_pw_aff *read_multi_union_pw_aff_core(
        __isl_keep isl_stream *s)
 {
        struct vars *v;
@@ -4052,6 +4142,40 @@ error:
        return NULL;
 }
 
+/* Read an isl_multi_union_pw_aff from "s".
+ *
+ * In particular, handle the special case with shared domain constraints.
+ * These are specified as
+ *
+ *     ([...] : ...)
+ *
+ * and are especially useful for setting the explicit domain
+ * of a zero-dimensional isl_multi_union_pw_aff.
+ * The core isl_multi_union_pw_aff ([...]) is read by
+ * read_multi_union_pw_aff_core.
+ */
+__isl_give isl_multi_union_pw_aff *isl_stream_read_multi_union_pw_aff(
+       __isl_keep isl_stream *s)
+{
+       isl_multi_union_pw_aff *mupa;
+
+       if (!isl_stream_next_token_is(s, '('))
+               return read_multi_union_pw_aff_core(s);
+
+       if (isl_stream_eat(s, '(') < 0)
+               return NULL;
+       mupa = read_multi_union_pw_aff_core(s);
+       if (isl_stream_eat_if_available(s, ':')) {
+               isl_union_set *dom;
+
+               dom = isl_stream_read_union_set(s);
+               mupa = isl_multi_union_pw_aff_intersect_domain(mupa, dom);
+       }
+       if (isl_stream_eat(s, ')') < 0)
+               return isl_multi_union_pw_aff_free(mupa);
+       return mupa;
+}
+
 /* Read an isl_multi_union_pw_aff from "str".
  */
 __isl_give isl_multi_union_pw_aff *isl_multi_union_pw_aff_read_from_str(
index 5b3e5c8..136144c 100644 (file)
--- a/isl_map.c
+++ b/isl_map.c
@@ -13399,18 +13399,26 @@ __isl_give isl_map *isl_map_preimage_range_pw_multi_aff(
  * We create a separate isl_multi_aff to effectuate this change
  * in order to avoid spurious splitting of the map along the pieces
  * of "mpa".
+ * If "mpa" has a non-trivial explicit domain, however,
+ * then the full substitution should be performed.
  */
 __isl_give isl_map *isl_map_preimage_multi_pw_aff(__isl_take isl_map *map,
        enum isl_dim_type type, __isl_take isl_multi_pw_aff *mpa)
 {
        int n;
+       isl_bool full;
        isl_pw_multi_aff *pma;
 
        if (!map || !mpa)
                goto error;
 
        n = isl_map_dim(map, type);
-       if (!isl_map_involves_dims(map, type, 0, n)) {
+       full = isl_map_involves_dims(map, type, 0, n);
+       if (full >= 0 && !full)
+               full = isl_multi_pw_aff_has_non_trivial_domain(mpa);
+       if (full < 0)
+               goto error;
+       if (!full) {
                isl_space *space;
                isl_multi_aff *ma;
 
diff --git a/isl_multi_align_set.c b/isl_multi_align_set.c
new file mode 100644 (file)
index 0000000..d7ef608
--- /dev/null
@@ -0,0 +1,7 @@
+#define ALIGN_DOMBASE set
+#define ALIGN_DOM isl_set
+
+#include <isl_multi_align_templ.c>
+
+#undef ALIGN_DOMBASE
+#undef ALIGN_DOM
diff --git a/isl_multi_align_templ.c b/isl_multi_align_templ.c
new file mode 100644 (file)
index 0000000..f4604f0
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2017      Sven Verdoolaege
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege,
+ */
+
+/* Align the parameters of "multi" and "domain" (if needed) and
+ * call "fn".
+ */
+static __isl_give MULTI(BASE) *FN(FN(MULTI(BASE),align_params),ALIGN_DOMBASE)(
+       __isl_take MULTI(BASE) *multi, __isl_take ALIGN_DOM *domain,
+       __isl_give MULTI(BASE) *fn(__isl_take MULTI(BASE) *multi,
+               __isl_take ALIGN_DOM *domain))
+{
+       isl_bool aligned;
+       isl_bool named;
+       isl_space *dom_space;
+
+       aligned = FN(ALIGN_DOM,space_has_equal_params)(domain, multi->space);
+       if (aligned < 0)
+               goto error;
+       if (aligned)
+               return fn(multi, domain);
+
+       dom_space = FN(ALIGN_DOM,peek_space)(domain);
+       named = isl_space_has_named_params(multi->space);
+       if (named >= 0 && named)
+               named = isl_space_has_named_params(dom_space);
+       if (named < 0)
+               goto error;
+       if (!named)
+               isl_die(FN(MULTI(BASE),get_ctx)(multi), isl_error_invalid,
+                       "unaligned unnamed parameters", goto error);
+       multi = FN(MULTI(BASE),align_params)(multi,
+                                           FN(ALIGN_DOM,get_space)(domain));
+       domain = FN(ALIGN_DOM,align_params)(domain,
+                                           FN(MULTI(BASE),get_space)(multi));
+       return fn(multi, domain);
+error:
+       FN(MULTI(BASE),free)(multi);
+       FN(ALIGN_DOM,free)(domain);
+       return NULL;
+}
diff --git a/isl_multi_align_union_set.c b/isl_multi_align_union_set.c
new file mode 100644 (file)
index 0000000..545a887
--- /dev/null
@@ -0,0 +1,7 @@
+#define ALIGN_DOMBASE union_set
+#define ALIGN_DOM isl_union_set
+
+#include <isl_multi_align_templ.c>
+
+#undef ALIGN_DOMBASE
+#undef ALIGN_DOM
index 7bfa386..766625e 100644 (file)
@@ -23,7 +23,7 @@ isl_bool FN(MULTI(BASE),involves_dims)(__isl_keep MULTI(BASE) *multi,
 
        if (!multi)
                return isl_bool_error;
-       if (multi->n == 0 || n == 0)
+       if (n == 0)
                return isl_bool_false;
 
        for (i = 0; i < multi->n; ++i) {
@@ -34,6 +34,10 @@ isl_bool FN(MULTI(BASE),involves_dims)(__isl_keep MULTI(BASE) *multi,
                        return involves;
        }
 
+       if (FN(MULTI(BASE),has_explicit_domain)(multi))
+               return FN(MULTI(BASE),involves_explicit_domain_dims)(multi,
+                                                               type, first, n);
+
        return isl_bool_false;
 }
 
@@ -59,6 +63,11 @@ __isl_give MULTI(BASE) *FN(MULTI(BASE),insert_dims)(
        multi->space = isl_space_insert_dims(multi->space, type, first, n);
        if (!multi->space)
                return FN(MULTI(BASE),free)(multi);
+       if (FN(MULTI(BASE),has_explicit_domain)(multi))
+               multi = FN(MULTI(BASE),insert_explicit_domain_dims)(multi,
+                                                               type, first, n);
+       if (!multi)
+               return NULL;
 
        for (i = 0; i < multi->n; ++i) {
                multi->u.p[i] = FN(EL,insert_dims)(multi->u.p[i],
diff --git a/isl_multi_explicit_domain.c b/isl_multi_explicit_domain.c
new file mode 100644 (file)
index 0000000..1ab769b
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2017      Sven Verdoolaege
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege.
+ */
+
+/* These versions of the explicit domain functions are used
+ * when the multi expression may have an explicit domain.
+ */
+
+#include <isl_multi_macro.h>
+
+__isl_give MULTI(BASE) *FN(MULTI(BASE),cow)(__isl_take MULTI(BASE) *multi);
+
+/* Does "multi" have an explicit domain?
+ *
+ * An explicit domain is only available if "multi" is zero-dimensional.
+ */
+static int FN(MULTI(BASE),has_explicit_domain)(__isl_keep MULTI(BASE) *multi)
+{
+       return multi && multi->n == 0;
+}
+
+/* Check that "multi" has an explicit domain.
+ */
+static isl_stat FN(MULTI(BASE),check_has_explicit_domain)(
+       __isl_keep MULTI(BASE) *multi)
+{
+       if (!multi)
+               return isl_stat_error;
+       if (!FN(MULTI(BASE),has_explicit_domain)(multi))
+               isl_die(FN(MULTI(BASE),get_ctx)(multi), isl_error_internal,
+                       "expression does not have an explicit domain",
+                       return isl_stat_error);
+       return isl_stat_ok;
+}
+
+/* Return the explicit domain of "multi", assuming it has one.
+ */
+static __isl_give DOM *FN(MULTI(BASE),get_explicit_domain)(
+       __isl_keep MULTI(BASE) *multi)
+{
+       if (FN(MULTI(BASE),check_has_explicit_domain)(multi) < 0)
+               return NULL;
+       return FN(DOM,copy)(multi->u.dom);
+}
+
+/* Replace the explicit domain of "multi" by "dom", assuming it has one.
+ */
+static __isl_give MULTI(BASE) *FN(MULTI(BASE),set_explicit_domain)(
+       __isl_take MULTI(BASE) *multi, __isl_take DOM *dom)
+{
+       if (FN(MULTI(BASE),check_has_explicit_domain)(multi) < 0)
+               goto error;
+       multi = FN(MULTI(BASE),cow)(multi);
+       if (!multi || !dom)
+               goto error;
+       FN(DOM,free)(multi->u.dom);
+       multi->u.dom = dom;
+       if (!multi->u.dom)
+               return FN(MULTI(BASE),free)(multi);
+       return multi;
+error:
+       FN(MULTI(BASE),free)(multi);
+       FN(DOM,free)(dom);
+       return NULL;
+}
+
+/* Intersect the domain of "dst" with the explicit domain of "src".
+ */
+static __isl_give MULTI(BASE) *FN(MULTI(BASE),intersect_explicit_domain)(
+       __isl_take MULTI(BASE) *dst, __isl_keep MULTI(BASE) *src)
+{
+       DOM *dom;
+
+       dom = FN(MULTI(BASE),get_explicit_domain)(src);
+       dst = FN(MULTI(BASE),intersect_domain)(dst, dom);
+
+       return dst;
+}
+
+/* Set the explicit domain of "dst" to that of "src".
+ */
+static __isl_give MULTI(BASE) *FN(MULTI(BASE),copy_explicit_domain)(
+       __isl_take MULTI(BASE) *dst, __isl_keep MULTI(BASE) *src)
+{
+       DOM *dom;
+
+       dom = FN(MULTI(BASE),get_explicit_domain)(src);
+       dst = FN(MULTI(BASE),set_explicit_domain)(dst, dom);
+
+       return dst;
+}
+
+/* Align the parameters of the explicit domain of "multi" to those of "space".
+ */
+static __isl_give MULTI(BASE) *FN(MULTI(BASE),align_explicit_domain_params)(
+       __isl_take MULTI(BASE) *multi, __isl_take isl_space *space)
+{
+       DOM *dom;
+
+       dom = FN(MULTI(BASE),get_explicit_domain)(multi);
+       dom = FN(DOM,align_params)(dom, space);
+       multi = FN(MULTI(BASE),set_explicit_domain)(multi, dom);
+
+       return multi;
+}
+
+/* Replace the space of the explicit domain of "multi" by "space",
+ * without modifying its dimension.
+ */
+static __isl_give MULTI(BASE) *FN(MULTI(BASE),reset_explicit_domain_space)(
+       __isl_take MULTI(BASE) *multi, __isl_take isl_space *space)
+{
+       DOM *dom;
+
+       dom = FN(MULTI(BASE),get_explicit_domain)(multi);
+       dom = FN(DOM,reset_equal_dim_space)(dom, space);
+       multi = FN(MULTI(BASE),set_explicit_domain)(multi, dom);
+
+       return multi;
+}
+
+/* Free the explicit domain of "multi".
+ */
+static void FN(MULTI(BASE),free_explicit_domain)(__isl_keep MULTI(BASE) *multi)
+{
+       if (FN(MULTI(BASE),check_has_explicit_domain)(multi) < 0)
+               return;
+       FN(DOM,free)(multi->u.dom);
+}
+
+/* Do "multi1" and "multi2" have the same explicit domain?
+ */
+static isl_bool FN(MULTI(BASE),equal_explicit_domain)(
+       __isl_keep MULTI(BASE) *multi1, __isl_keep MULTI(BASE) *multi2)
+{
+       DOM *dom1, *dom2;
+       isl_bool equal;
+
+       if (FN(MULTI(BASE),check_has_explicit_domain)(multi1) < 0 ||
+           FN(MULTI(BASE),check_has_explicit_domain)(multi2) < 0)
+               return isl_bool_error;
+       dom1 = FN(MULTI(BASE),get_explicit_domain)(multi1);
+       dom2 = FN(MULTI(BASE),get_explicit_domain)(multi2);
+       equal = FN(DOM,is_equal)(dom1, dom2);
+       FN(DOM,free)(dom1);
+       FN(DOM,free)(dom2);
+
+       return equal;
+}
+
+static isl_stat FN(MULTI(BASE),check_explicit_domain)(
+       __isl_keep MULTI(BASE) *multi) __attribute__ ((unused));
+
+/* Debugging function to check that the explicit domain of "multi"
+ * has the correct space.
+ */
+isl_stat FN(MULTI(BASE),check_explicit_domain)(__isl_keep MULTI(BASE) *multi)
+{
+       isl_space *space1, *space2;
+       isl_bool equal;
+
+       if (FN(MULTI(BASE),check_has_explicit_domain)(multi) < 0)
+               return isl_stat_error;
+       space1 = isl_space_domain(isl_space_copy(multi->space));
+       space2 = FN(DOM,get_space)(multi->u.dom);
+       equal = isl_space_is_equal(space1, space2);
+       isl_space_free(space1);
+       isl_space_free(space2);
+       if (equal < 0)
+               return isl_stat_error;
+       if (!equal)
+               isl_die(FN(MULTI(BASE),get_ctx)(multi), isl_error_internal,
+                       "check failed", return isl_stat_error);
+       return isl_stat_ok;
+}
index b934102..a147ec8 100644 (file)
 
 #include <isl_multi_macro.h>
 
+/* Does the space of "domain" correspond to that of the domain of "multi"?
+ * The parameters do not need to be aligned.
+ */
+static isl_bool FN(MULTI(BASE),compatible_domain)(
+       __isl_keep MULTI(BASE) *multi, __isl_keep DOM *domain)
+{
+       isl_bool ok;
+       isl_space *space, *domain_space;
+
+       domain_space = FN(DOM,get_space)(domain);
+       space = FN(MULTI(BASE),get_space)(multi);
+       ok = isl_space_has_domain_tuples(domain_space, space);
+       isl_space_free(space);
+       isl_space_free(domain_space);
+
+       return ok;
+}
+
+/* Check that the space of "domain" corresponds to
+ * that of the domain of "multi", ignoring parameters.
+ */
+static isl_stat FN(MULTI(BASE),check_compatible_domain)(
+       __isl_keep MULTI(BASE) *multi, __isl_keep DOM *domain)
+{
+       isl_bool ok;
+
+       ok = FN(MULTI(BASE),compatible_domain)(multi, domain);
+       if (ok < 0)
+               return isl_stat_error;
+       if (!ok)
+               isl_die(FN(DOM,get_ctx)(domain), isl_error_invalid,
+                       "incompatible spaces", return isl_stat_error);
+
+       return isl_stat_ok;
+}
+
+/* Intersect the explicit domain of "multi" with "domain".
+ *
+ * The parameters of "multi" and "domain" are assumed to have been aligned.
+ *
+ * In the case of an isl_multi_union_pw_aff object, the explicit domain
+ * is allowed to have only constraints on the parameters, while
+ * "domain" contains actual domain elements.  In this case,
+ * "domain" is intersected with those parameter constraints and
+ * then used as the explicit domain of "multi".
+ */
+static __isl_give MULTI(BASE) *FN(MULTI(BASE),domain_intersect_aligned)(
+       __isl_take MULTI(BASE) *multi, __isl_take DOM *domain)
+{
+       isl_bool is_params;
+       DOM *multi_dom;
+
+       if (FN(MULTI(BASE),check_compatible_domain)(multi, domain) < 0)
+               goto error;
+       if (FN(MULTI(BASE),check_has_explicit_domain)(multi) < 0)
+               goto error;
+       is_params = FN(DOM,is_params)(multi->u.dom);
+       if (is_params < 0)
+               goto error;
+       multi_dom = FN(MULTI(BASE),get_explicit_domain)(multi);
+       if (!is_params) {
+               domain = FN(DOM,intersect)(multi_dom, domain);
+       } else {
+               isl_set *params;
+
+               params = FN(DOM,params)(multi_dom);
+               domain = FN(DOM,intersect_params)(domain, params);
+       }
+       multi = FN(MULTI(BASE),set_explicit_domain)(multi, domain);
+       return multi;
+error:
+       FN(MULTI(BASE),free)(multi);
+       FN(DOM,free)(domain);
+       return NULL;
+}
+
+/* Intersect the explicit domain of "multi" with "domain".
+ * First align the parameters, if needed.
+ */
+static __isl_give MULTI(BASE) *FN(MULTI(BASE),domain_intersect)(
+       __isl_take MULTI(BASE) *multi, __isl_take DOM *domain)
+{
+       return FN(FN(MULTI(BASE),align_params),DOMBASE)(multi, domain,
+                                   FN(MULTI(BASE),domain_intersect_aligned));
+}
+
 /* Intersect the domain of "multi" with "domain".
+ *
+ * If "multi" has an explicit domain, then only this domain
+ * needs to be intersected.
  */
 __isl_give MULTI(BASE) *FN(MULTI(BASE),intersect_domain)(
        __isl_take MULTI(BASE) *multi, __isl_take DOM *domain)
 {
+       if (FN(MULTI(BASE),has_explicit_domain)(multi))
+               return FN(MULTI(BASE),domain_intersect)(multi, domain);
        return FN(FN(MULTI(BASE),apply),DOMBASE)(multi, domain,
                                        &FN(EL,intersect_domain));
 }
 
+/* Intersect the parameter domain of the explicit domain of "multi"
+ * with "domain".
+ */
+static __isl_give MULTI(BASE) *FN(MULTI(BASE),domain_intersect_params_aligned)(
+       __isl_take MULTI(BASE) *multi, __isl_take isl_set *domain)
+{
+       DOM *multi_dom;
+
+       multi_dom = FN(MULTI(BASE),get_explicit_domain)(multi);
+       multi_dom = FN(DOM,intersect_params)(multi_dom, domain);
+       multi = FN(MULTI(BASE),set_explicit_domain)(multi, multi_dom);
+
+       return multi;
+}
+
+/* Intersect the parameter domain of the explicit domain of "multi"
+ * with "domain".
+ * First align the parameters, if needed.
+ */
+static __isl_give MULTI(BASE) *FN(MULTI(BASE),domain_intersect_params)(
+       __isl_take MULTI(BASE) *multi, __isl_take isl_set *domain)
+{
+       return FN(FN(MULTI(BASE),align_params),set)(multi, domain,
+                           FN(MULTI(BASE),domain_intersect_params_aligned));
+}
+
 /* Intersect the parameter domain of "multi" with "domain".
+ *
+ * If "multi" has an explicit domain, then only this domain
+ * needs to be intersected.
  */
 __isl_give MULTI(BASE) *FN(MULTI(BASE),intersect_params)(
        __isl_take MULTI(BASE) *multi, __isl_take isl_set *domain)
 {
+       if (FN(MULTI(BASE),has_explicit_domain)(multi))
+               return FN(MULTI(BASE),domain_intersect_params)(multi, domain);
        return FN(MULTI(BASE),apply_set)(multi, domain,
                                        &FN(EL,intersect_params));
 }
diff --git a/isl_multi_no_explicit_domain.c b/isl_multi_no_explicit_domain.c
new file mode 100644 (file)
index 0000000..13c5de8
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * Copyright 2017      Sven Verdoolaege
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege.
+ */
+
+/* These versions of the explicit domain functions are used
+ * when the multi expression cannot have an explicit domain.
+ */
+
+#include <isl_multi_macro.h>
+
+/* Does "multi" have an explicit domain?
+ *
+ * No.
+ */
+static int FN(MULTI(BASE),has_explicit_domain)(__isl_keep MULTI(BASE) *multi)
+{
+       return 0;
+}
+
+/* Initialize the explicit domain of "multi".
+ * "multi" cannot have an explicit domain, so this function is never called.
+ */
+static __isl_give MULTI(BASE) *FN(MULTI(BASE),init_explicit_domain)(
+       __isl_take MULTI(BASE) *multi)
+{
+       return multi;
+}
+
+/* Intersect the domain of "dst" with the explicit domain of "src".
+ * "src" cannot have an explicit domain, so this function is never called.
+ */
+static __isl_give MULTI(BASE) *FN(MULTI(BASE),intersect_explicit_domain)(
+       __isl_take MULTI(BASE) *dst, __isl_keep MULTI(BASE) *src)
+{
+       return dst;
+}
+
+/* Set the explicit domain of "dst" to that of "src".
+ * "src" and "dst" cannot have an explicit domain,
+ * so this function is never called.
+ */
+static __isl_give MULTI(BASE) *FN(MULTI(BASE),copy_explicit_domain)(
+       __isl_take MULTI(BASE) *dst, __isl_keep MULTI(BASE) *src)
+{
+       return dst;
+}
+
+/* Intersect the domain of "dst" with the domain product
+ * of the explicit domains of "src1" and "src2".
+ * This function is only called if at least one of "src1" or "src2"
+ * has an explicit domain.
+ * "src1", "src2" and "dst" cannot have an explicit domain,
+ * so this function is never called.
+ */
+static __isl_give MULTI(BASE) *
+FN(MULTI(BASE),intersect_explicit_domain_product)(
+       __isl_take MULTI(BASE) *dst, __isl_keep MULTI(BASE) *src1,
+       __isl_keep MULTI(BASE) *src2)
+{
+       return dst;
+}
+
+/* Align the parameters of the explicit domain of "multi" to those of "space".
+ * "multi" cannot have an explicit domain, so this function is never called.
+ */
+static __isl_give MULTI(BASE) *FN(MULTI(BASE),align_explicit_domain_params)(
+       __isl_take MULTI(BASE) *multi, __isl_take isl_space *space)
+{
+       isl_space_free(space);
+       return multi;
+}
+
+/* Replace the space of the explicit domain of "multi" by "space",
+ * without modifying its dimension.
+ * "multi" cannot have an explicit domain, so this function is never called.
+ */
+static __isl_give MULTI(BASE) *FN(MULTI(BASE),reset_explicit_domain_space)(
+       __isl_take MULTI(BASE) *multi, __isl_take isl_space *space)
+{
+       isl_space_free(space);
+       return multi;
+}
+
+/* Check whether the explicit domain of "multi" has non-zero coefficients
+ * for any dimension in the given range or if any of these dimensions appear
+ * with non-zero coefficients in any of the integer divisions involved.
+ * "multi" cannot have an explicit domain, so this function is never called.
+ */
+isl_bool FN(MULTI(BASE),involves_explicit_domain_dims)(
+       __isl_keep MULTI(BASE) *multi,
+       enum isl_dim_type type, unsigned pos, unsigned n)
+{
+       return isl_bool_false;
+}
+
+/* Insert "n" dimensions of type "type" at position "pos"
+ * of the explicit domain of "multi".
+ * "multi" cannot have an explicit domain, so this function is never called.
+ */
+__isl_give MULTI(BASE) *FN(MULTI(BASE),insert_explicit_domain_dims)(
+       __isl_take MULTI(BASE) *multi,
+       enum isl_dim_type type, unsigned pos, unsigned n)
+{
+       return multi;
+}
+
+/* Drop the "n" dimensions of type "type" starting at position "pos"
+ * of the explicit domain of "multi".
+ * "multi" cannot have an explicit domain, so this function is never called.
+ */
+__isl_give MULTI(BASE) *FN(MULTI(BASE),drop_explicit_domain_dims)(
+       __isl_take MULTI(BASE) *multi,
+       enum isl_dim_type type, unsigned pos, unsigned n)
+{
+       return multi;
+}
+
+/* Move the "n" dimensions of "src_type" starting at "src_pos" of
+ * of the explicit domain of "multi" to dimensions of "dst_type" at "dst_pos".
+ * "multi" cannot have an explicit domain, so this function is never called.
+ */
+__isl_give MULTI(BASE) *FN(MULTI(BASE),move_explicit_domain_dims)(
+       __isl_take MULTI(BASE) *multi,
+       enum isl_dim_type dst_type, unsigned dst_pos,
+       enum isl_dim_type src_type, unsigned src_pos, unsigned n)
+{
+       return multi;
+}
+
+/* Free the explicit domain of "multi".
+ * "multi" cannot have an explicit domain, so this function is never called.
+ */
+static void FN(MULTI(BASE),free_explicit_domain)(__isl_keep MULTI(BASE) *multi)
+{
+}
+
+/* Do "multi1" and "multi2" have the same explicit domain?
+ * "multi1" and "multi2" cannot have an explicit domain,
+ * so this function is never called.
+ */
+static isl_bool FN(MULTI(BASE),equal_explicit_domain)(
+       __isl_keep MULTI(BASE) *multi1, __isl_keep MULTI(BASE) *multi2)
+{
+       return isl_bool_true;
+}
+
+static isl_stat FN(MULTI(BASE),check_explicit_domain)(
+       __isl_keep MULTI(BASE) *multi) __attribute__ ((unused));
+
+/* Debugging function to check that the explicit domain of "multi"
+ * has the correct space.
+ * "multi" cannot have an explicit domain,
+ * so this function should never be called.
+ */
+static isl_stat FN(MULTI(BASE),check_explicit_domain)(
+       __isl_keep MULTI(BASE) *multi)
+{
+       return isl_stat_ok;
+}
diff --git a/isl_multi_pw_aff_explicit_domain.c b/isl_multi_pw_aff_explicit_domain.c
new file mode 100644 (file)
index 0000000..e9efea0
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2017      Sven Verdoolaege
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege.
+ */
+
+/* Initialize the explicit domain of "mpa".
+ *
+ * The explicit domain is initialized to a universe set
+ * in the domain space.
+ */
+static __isl_give isl_multi_pw_aff *isl_multi_pw_aff_init_explicit_domain(
+       __isl_take isl_multi_pw_aff *mpa)
+{
+       if (isl_multi_pw_aff_check_has_explicit_domain(mpa) < 0)
+               return isl_multi_pw_aff_free(mpa);
+       mpa->u.dom = isl_set_universe(isl_multi_pw_aff_get_domain_space(mpa));
+       if (!mpa->u.dom)
+               return isl_multi_pw_aff_free(mpa);
+       return mpa;
+}
+
+/* Intersect the domain of "dst" with the domain product
+ * of the explicit domains of "src1" and "src2".
+ * This function is only called if at least one of "src1" or "src2"
+ * has an explicit domain.
+ */
+static __isl_give isl_multi_pw_aff *
+isl_multi_pw_aff_intersect_explicit_domain_product(
+       __isl_take isl_multi_pw_aff *dst, __isl_keep isl_multi_pw_aff *src1,
+       __isl_keep isl_multi_pw_aff *src2)
+{
+       isl_space *space;
+       isl_set *dom;
+       isl_map *map;
+
+       if (!src1 || !src2)
+               return FN(isl_multi_pw_aff,free)(dst);
+       space = isl_multi_pw_aff_get_domain_space(dst);
+       dom = isl_set_universe(space);
+       map = isl_set_unwrap(dom);
+       if (isl_multi_pw_aff_has_explicit_domain(src1)) {
+               dom = isl_set_copy(src1->u.dom);
+               map = isl_map_intersect_domain(map, dom);
+       }
+       if (isl_multi_pw_aff_has_explicit_domain(src2)) {
+               dom = isl_set_copy(src2->u.dom);
+               map = isl_map_intersect_range(map, dom);
+       }
+       dom = isl_map_wrap(map);
+       dst = isl_multi_pw_aff_intersect_domain(dst, dom);
+       return dst;
+}
+
+/* Check whether the explicit domain of "mpa" has non-zero coefficients
+ * for any dimension in the given range or if any of these dimensions appear
+ * with non-zero coefficients in any of the integer divisions involved.
+ */
+isl_bool isl_multi_pw_aff_involves_explicit_domain_dims(
+       __isl_keep isl_multi_pw_aff *mpa,
+       enum isl_dim_type type, unsigned pos, unsigned n)
+{
+       if (isl_multi_pw_aff_check_has_explicit_domain(mpa) < 0)
+               return isl_bool_error;
+       if (type == isl_dim_in)
+               type = isl_dim_set;
+       return isl_set_involves_dims(mpa->u.dom, type, pos, n);
+}
+
+/* Insert "n" dimensions of type "type" at position "pos"
+ * of the explicit domain of "mpa".
+ */
+static __isl_give isl_multi_pw_aff *
+isl_multi_pw_aff_insert_explicit_domain_dims(__isl_take isl_multi_pw_aff *mpa,
+       enum isl_dim_type type, unsigned pos, unsigned n)
+{
+       if (isl_multi_pw_aff_check_has_explicit_domain(mpa) < 0)
+               return isl_multi_pw_aff_free(mpa);
+       mpa = isl_multi_pw_aff_cow(mpa);
+       if (!mpa)
+               return NULL;
+       if (type == isl_dim_in)
+               type = isl_dim_set;
+       mpa->u.dom = isl_set_insert_dims(mpa->u.dom, type, pos, n);
+       if (!mpa->u.dom)
+               return isl_multi_pw_aff_free(mpa);
+       return mpa;
+}
+
+/* Drop the "n" dimensions of type "type" starting at position "pos"
+ * of the explicit domain of "mpa".
+ */
+static __isl_give isl_multi_pw_aff *
+isl_multi_pw_aff_drop_explicit_domain_dims(__isl_take isl_multi_pw_aff *mpa,
+       enum isl_dim_type type, unsigned pos, unsigned n)
+{
+       if (isl_multi_pw_aff_check_has_explicit_domain(mpa) < 0)
+               return isl_multi_pw_aff_free(mpa);
+       mpa = isl_multi_pw_aff_cow(mpa);
+       if (!mpa)
+               return NULL;
+       if (type == isl_dim_in)
+               type = isl_dim_set;
+       mpa->u.dom = isl_set_drop(mpa->u.dom, type, pos, n);
+       if (!mpa->u.dom)
+               return isl_multi_pw_aff_free(mpa);
+       return mpa;
+}
+
+/* Move the "n" dimensions of "src_type" starting at "src_pos" of
+ * of the explicit domain of "mpa" to dimensions of "dst_type" at "dst_pos".
+ */
+static __isl_give isl_multi_pw_aff *isl_multi_pw_aff_move_explicit_domain_dims(
+       __isl_take isl_multi_pw_aff *mpa,
+       enum isl_dim_type dst_type, unsigned dst_pos,
+       enum isl_dim_type src_type, unsigned src_pos, unsigned n)
+{
+       if (isl_multi_pw_aff_check_has_explicit_domain(mpa) < 0)
+               return isl_multi_pw_aff_free(mpa);
+       mpa = isl_multi_pw_aff_cow(mpa);
+       if (!mpa)
+               return NULL;
+       if (dst_type == isl_dim_in)
+               dst_type = isl_dim_set;
+       if (src_type == isl_dim_in)
+               src_type = isl_dim_set;
+       mpa->u.dom = isl_set_move_dims(mpa->u.dom, dst_type, dst_pos,
+                               src_type, src_pos, n);
+       if (!mpa->u.dom)
+               return isl_multi_pw_aff_free(mpa);
+       return mpa;
+}
index 53b5563..bc14033 100644 (file)
@@ -46,6 +46,12 @@ __isl_give isl_space *FN(MULTI(BASE),get_domain_space)(
        return multi ? isl_space_domain(isl_space_copy(multi->space)) : NULL;
 }
 
+/* Allocate a multi expression living in "space".
+ *
+ * If the number of base expressions is zero, then make sure
+ * there is enough room in the structure for the explicit domain,
+ * in case the type supports such an explicit domain.
+ */
 __isl_give MULTI(BASE) *FN(MULTI(BASE),alloc)(__isl_take isl_space *space)
 {
        isl_ctx *ctx;
@@ -57,14 +63,19 @@ __isl_give MULTI(BASE) *FN(MULTI(BASE),alloc)(__isl_take isl_space *space)
 
        ctx = isl_space_get_ctx(space);
        n = isl_space_dim(space, isl_dim_out);
-       multi = isl_calloc(ctx, MULTI(BASE),
+       if (n > 0)
+               multi = isl_calloc(ctx, MULTI(BASE),
                         sizeof(MULTI(BASE)) + (n - 1) * sizeof(struct EL *));
+       else
+               multi = isl_calloc(ctx, MULTI(BASE), sizeof(MULTI(BASE)));
        if (!multi)
                goto error;
 
        multi->space = space;
        multi->n = n;
        multi->ref = 1;
+       if (FN(MULTI(BASE),has_explicit_domain)(multi))
+               multi = FN(MULTI(BASE),init_explicit_domain)(multi);
        return multi;
 error:
        isl_space_free(space);
@@ -86,6 +97,8 @@ __isl_give MULTI(BASE) *FN(MULTI(BASE),dup)(__isl_keep MULTI(BASE) *multi)
        for (i = 0; i < multi->n; ++i)
                dup = FN(FN(MULTI(BASE),set),BASE)(dup, i,
                                                    FN(EL,copy)(multi->u.p[i]));
+       if (FN(MULTI(BASE),has_explicit_domain)(multi))
+               dup = FN(MULTI(BASE),copy_explicit_domain)(dup, multi);
 
        return dup;
 }
@@ -124,6 +137,8 @@ __isl_null MULTI(BASE) *FN(MULTI(BASE),free)(__isl_take MULTI(BASE) *multi)
        isl_space_free(multi->space);
        for (i = 0; i < multi->n; ++i)
                FN(EL,free)(multi->u.p[i]);
+       if (FN(MULTI(BASE),has_explicit_domain)(multi))
+               FN(MULTI(BASE),free_explicit_domain)(multi);
        free(multi);
 
        return NULL;
@@ -267,6 +282,10 @@ error:
  * directly or through its domain.  It therefore passes along both,
  * which we pass along to the element function since we don't know how
  * that is represented either.
+ *
+ * If "multi" has an explicit domain, then the caller is expected
+ * to make sure that any modification that would change the dimensions
+ * of the explicit domain has bee applied before this function is called.
  */
 __isl_give MULTI(BASE) *FN(MULTI(BASE),reset_space_and_domain)(
        __isl_take MULTI(BASE) *multi, __isl_take isl_space *space,
@@ -284,6 +303,12 @@ __isl_give MULTI(BASE) *FN(MULTI(BASE),reset_space_and_domain)(
                if (!multi->u.p[i])
                        goto error;
        }
+       if (FN(MULTI(BASE),has_explicit_domain)(multi)) {
+               multi = FN(MULTI(BASE),reset_explicit_domain_space)(multi,
+                                                       isl_space_copy(domain));
+               if (!multi)
+                       goto error;
+       }
        isl_space_free(domain);
        isl_space_free(multi->space);
        multi->space = space;
@@ -436,6 +461,9 @@ error:
 }
 
 /* Align the parameters of "multi" to those of "model".
+ *
+ * If "multi" has an explicit domain, then align the parameters
+ * of the domain first.
  */
 __isl_give MULTI(BASE) *FN(MULTI(BASE),align_params)(
        __isl_take MULTI(BASE) *multi, __isl_take isl_space *model)
@@ -463,6 +491,12 @@ __isl_give MULTI(BASE) *FN(MULTI(BASE),align_params)(
                isl_die(ctx, isl_error_invalid,
                        "input has unnamed parameters", goto error);
 
+       if (FN(MULTI(BASE),has_explicit_domain)(multi)) {
+               multi = FN(MULTI(BASE),align_explicit_domain_params)(multi,
+                                                       isl_space_copy(model));
+               if (!multi)
+                       goto error;
+       }
        model = isl_space_params(model);
        exp = isl_parameter_alignment_reordering(multi->space, model);
        exp = isl_reordering_extend_space(exp,
@@ -649,10 +683,18 @@ __isl_give MULTI(BASE) *FN(MULTI(BASE),drop_dims)(
                for (i = first; i + n < multi->n; ++i)
                        multi->u.p[i] = multi->u.p[i + n];
                multi->n -= n;
+               if (n > 0 && FN(MULTI(BASE),has_explicit_domain)(multi))
+                       multi = FN(MULTI(BASE),init_explicit_domain)(multi);
 
                return multi;
        }
 
+       if (FN(MULTI(BASE),has_explicit_domain)(multi))
+               multi = FN(MULTI(BASE),drop_explicit_domain_dims)(multi,
+                                                               type, first, n);
+       if (!multi)
+               return NULL;
+
        for (i = 0; i < multi->n; ++i) {
                multi->u.p[i] = FN(EL,drop_dims)(multi->u.p[i], type, first, n);
                if (!multi->u.p[i])
@@ -699,6 +741,9 @@ error:
  * construct a MULTI(BASE) (A * C) -> [B -> D].
  *
  * The parameters are assumed to have been aligned.
+ *
+ * If "multi1" and/or "multi2" has an explicit domain, then
+ * intersect the domain of the result with these explicit domains.
  */
 static __isl_give MULTI(BASE) *FN(MULTI(BASE),range_product_aligned)(
        __isl_take MULTI(BASE) *multi1, __isl_take MULTI(BASE) *multi2)
@@ -728,6 +773,11 @@ static __isl_give MULTI(BASE) *FN(MULTI(BASE),range_product_aligned)(
                res = FN(FN(MULTI(BASE),set),BASE)(res, n1 + i, el);
        }
 
+       if (FN(MULTI(BASE),has_explicit_domain)(multi1))
+               res = FN(MULTI(BASE),intersect_explicit_domain)(res, multi1);
+       if (FN(MULTI(BASE),has_explicit_domain)(multi2))
+               res = FN(MULTI(BASE),intersect_explicit_domain)(res, multi2);
+
        FN(MULTI(BASE),free)(multi1);
        FN(MULTI(BASE),free)(multi2);
        return res;
@@ -836,6 +886,9 @@ __isl_give MULTI(BASE) *FN(MULTI(BASE),factor_range)(
  * construct a MULTI(BASE) [A -> C] -> [B -> D].
  *
  * The parameters are assumed to have been aligned.
+ *
+ * If "multi1" and/or "multi2" has an explicit domain, then
+ * intersect the domain of the result with these explicit domains.
  */
 __isl_give MULTI(BASE) *FN(MULTI(BASE),product_aligned)(
        __isl_take MULTI(BASE) *multi1, __isl_take MULTI(BASE) *multi2)
@@ -869,6 +922,11 @@ __isl_give MULTI(BASE) *FN(MULTI(BASE),product_aligned)(
                res = FN(FN(MULTI(BASE),set),BASE)(res, out1 + i, el);
        }
 
+       if (FN(MULTI(BASE),has_explicit_domain)(multi1) ||
+           FN(MULTI(BASE),has_explicit_domain)(multi2))
+               res = FN(MULTI(BASE),intersect_explicit_domain_product)(res,
+                                                               multi1, multi2);
+
        isl_space_free(space);
        FN(MULTI(BASE),free)(multi1);
        FN(MULTI(BASE),free)(multi2);
@@ -1044,6 +1102,9 @@ static __isl_give MULTI(BASE) *FN(MULTI(BASE),bin_op)(
 
 /* Pairwise perform "fn" to the elements of "multi1" and "multi2" and
  * return the result.
+ *
+ * If "multi2" has an explicit domain, then
+ * intersect the domain of the result with this explicit domain.
  */
 static __isl_give MULTI(BASE) *FN(MULTI(BASE),bin_op)(
        __isl_take MULTI(BASE) *multi1, __isl_take MULTI(BASE) *multi2,
@@ -1062,6 +1123,10 @@ static __isl_give MULTI(BASE) *FN(MULTI(BASE),bin_op)(
                        goto error;
        }
 
+       if (FN(MULTI(BASE),has_explicit_domain)(multi2))
+               multi1 = FN(MULTI(BASE),intersect_explicit_domain)(multi1,
+                                                                   multi2);
+
        FN(MULTI(BASE),free)(multi2);
        return multi1;
 error:
@@ -1338,6 +1403,11 @@ __isl_give MULTI(BASE) *FN(MULTI(BASE),move_dims)(__isl_take MULTI(BASE) *multi,
                                                src_type, src_pos, n);
        if (!multi->space)
                return FN(MULTI(BASE),free)(multi);
+       if (FN(MULTI(BASE),has_explicit_domain)(multi))
+               multi = FN(MULTI(BASE),move_explicit_domain_dims)(multi,
+                               dst_type, dst_pos, src_type, src_pos, n);
+       if (!multi)
+               return NULL;
 
        for (i = 0; i < multi->n; ++i) {
                multi->u.p[i] = FN(EL,move_dims)(multi->u.p[i],
@@ -1395,6 +1465,13 @@ isl_bool FN(MULTI(BASE),plain_is_equal)(__isl_keep MULTI(BASE) *multi1,
                        return equal;
        }
 
+       if (FN(MULTI(BASE),has_explicit_domain)(multi1) ||
+           FN(MULTI(BASE),has_explicit_domain)(multi2)) {
+               equal = FN(MULTI(BASE),equal_explicit_domain)(multi1, multi2);
+               if (equal < 0 || !equal)
+                       return equal;
+       }
+
        return isl_bool_true;
 }
 
@@ -1420,6 +1497,8 @@ isl_bool FN(MULTI(BASE),involves_nan)(__isl_keep MULTI(BASE) *multi)
 
 #ifndef NO_DOMAIN
 /* Return the shared domain of the elements of "multi".
+ *
+ * If "multi" has an explicit domain, then return this domain.
  */
 __isl_give isl_set *FN(MULTI(BASE),domain)(__isl_take MULTI(BASE) *multi)
 {
@@ -1429,6 +1508,12 @@ __isl_give isl_set *FN(MULTI(BASE),domain)(__isl_take MULTI(BASE) *multi)
        if (!multi)
                return NULL;
 
+       if (FN(MULTI(BASE),has_explicit_domain)(multi)) {
+               dom = FN(MULTI(BASE),get_explicit_domain)(multi);
+               FN(MULTI(BASE),free)(multi);
+               return dom;
+       }
+
        dom = isl_set_universe(FN(MULTI(BASE),get_domain_space)(multi));
        for (i = 0; i < multi->n; ++i) {
                isl_set *dom_i;
index e528b60..e82f057 100644 (file)
@@ -9,6 +9,8 @@
  * to the output or set dimension of "space".
  * "p" is an array of size "n" of base expressions.
  * The array is only accessible when n > 0.
+ * "dom" is the explicit domain, if present
+ * The explicit domain is only accessible when n == 0.
  */
 struct MULTI(BASE) {
        int ref;
@@ -16,8 +18,16 @@ struct MULTI(BASE) {
 
        int n;
        struct {
+#ifdef EXPLICIT_DOMAIN
+               DOM *dom;
+#endif
                EL *p[1];
        } u;
 };
 
 __isl_give MULTI(BASE) *CAT(MULTI(BASE),_alloc)(__isl_take isl_space *space);
+
+#ifdef EXPLICIT_DOMAIN
+isl_bool CAT(MULTI(BASE),_has_non_trivial_domain)(
+       __isl_keep MULTI(BASE) *multi);
+#endif
diff --git a/isl_multi_union_pw_aff_explicit_domain.c b/isl_multi_union_pw_aff_explicit_domain.c
new file mode 100644 (file)
index 0000000..7ff9b12
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2017      Sven Verdoolaege
+ *
+ * Use of this software is governed by the MIT license
+ *
+ * Written by Sven Verdoolaege.
+ */
+
+/* Initialize the explicit domain of "mupa".
+ *
+ * The explicit domain is initialized to a universe parameter set.
+ * It may later be specialized with constraints on the parameter or
+ * specific domain instances.
+ */
+static __isl_give isl_multi_union_pw_aff *
+isl_multi_union_pw_aff_init_explicit_domain(
+       __isl_take isl_multi_union_pw_aff *mupa)
+{
+       isl_space *space;
+
+       if (isl_multi_union_pw_aff_check_has_explicit_domain(mupa) < 0)
+               return isl_multi_union_pw_aff_free(mupa);
+       space = isl_space_params(isl_multi_union_pw_aff_get_space(mupa));
+       mupa->u.dom = isl_union_set_from_set(isl_set_universe(space));
+       if (!mupa->u.dom)
+               return isl_multi_union_pw_aff_free(mupa);
+       return mupa;
+}
+
+/* Drop the "n" dimensions of type "type" starting at position "pos"
+ * of the explicit domain of "mupa".
+ */
+static __isl_give isl_multi_union_pw_aff *
+isl_multi_union_pw_aff_drop_explicit_domain_dims(
+       __isl_take isl_multi_union_pw_aff *mupa,
+       enum isl_dim_type type, unsigned pos, unsigned n)
+{
+       if (isl_multi_union_pw_aff_check_has_explicit_domain(mupa) < 0)
+               return isl_multi_union_pw_aff_free(mupa);
+       if (type != isl_dim_param)
+               isl_die(isl_multi_union_pw_aff_get_ctx(mupa), isl_error_invalid,
+                       "can only drop parameters",
+                       return isl_multi_union_pw_aff_free(mupa));
+       mupa = isl_multi_union_pw_aff_cow(mupa);
+       if (!mupa)
+               return NULL;
+       mupa->u.dom = isl_union_set_project_out(mupa->u.dom, type, pos, n);
+       if (!mupa->u.dom)
+               return isl_multi_union_pw_aff_free(mupa);
+       return mupa;
+}
index 2831f9f..2f5b95d 100644 (file)
@@ -23,6 +23,7 @@
 #include <isl_space_private.h>
 #include <isl_mat_private.h>
 #include <isl_vec_private.h>
+#include <isl/union_set.h>
 #include <isl/union_map.h>
 #include <isl/constraint.h>
 #include <isl_local_space_private.h>
@@ -1175,6 +1176,16 @@ static __isl_give isl_printer *print_disjuncts_map(__isl_keep isl_map *map,
                return print_disjuncts(map, space, p, latex);
 }
 
+/* Print the disjuncts of a set.
+ * The names of the variables are taken from "space".
+ * "latex" is set if the constraints should be printed in LaTeX format.
+ */
+static __isl_give isl_printer *print_disjuncts_set(__isl_keep isl_set *set,
+       __isl_keep isl_space *space, __isl_take isl_printer *p, int latex)
+{
+       return print_disjuncts_map(set_to_map(set), space, p, latex);
+}
+
 struct isl_aff_split {
        isl_basic_map *aff;
        isl_map *map;
@@ -1584,6 +1595,15 @@ static __isl_give isl_printer *isl_printer_print_union_map_isl_body(
        return p;
 }
 
+/* Print the body of "uset" (everything except the parameter declarations)
+ * to "p" in isl format.
+ */
+static __isl_give isl_printer *isl_printer_print_union_set_isl_body(
+       __isl_take isl_printer *p, __isl_keep isl_union_set *uset)
+{
+       return isl_printer_print_union_map_isl_body(p, uset_to_umap(uset));
+}
+
 /* Print the isl_union_map "umap" to "p" in isl format.
  */
 static __isl_give isl_printer *isl_union_map_print_isl(
@@ -3169,11 +3189,15 @@ static __isl_give isl_printer *print_dim_mpa(__isl_take isl_printer *p,
 }
 
 /* Print "mpa" to "p" in isl format.
+ *
+ * If "mpa" is zero-dimensional and has a non-trivial explicit domain,
+ * then it is printed after the tuple of affine expressions.
  */
 static __isl_give isl_printer *print_multi_pw_aff_isl(__isl_take isl_printer *p,
        __isl_keep isl_multi_pw_aff *mpa)
 {
        struct isl_print_space_data data = { 0 };
+       isl_bool has_domain;
 
        if (!mpa)
                return isl_printer_free(p);
@@ -3183,6 +3207,16 @@ static __isl_give isl_printer *print_multi_pw_aff_isl(__isl_take isl_printer *p,
        data.print_dim = &print_dim_mpa;
        data.user = mpa;
        p = isl_print_space(mpa->space, p, 0, &data);
+       has_domain = isl_multi_pw_aff_has_non_trivial_domain(mpa);
+       if (has_domain < 0)
+               return isl_printer_free(p);
+       if (has_domain) {
+               isl_space *space;
+
+               space = isl_space_domain(isl_space_copy(mpa->space));
+               p = print_disjuncts_set(mpa->u.dom, space, p, 0);
+               isl_space_free(space);
+       }
        p = isl_printer_print_str(p, " }");
        return p;
 }
@@ -3274,22 +3308,45 @@ static __isl_give isl_printer *print_union_pw_aff_dim(__isl_take isl_printer *p,
 }
 
 /* Print the isl_multi_union_pw_aff "mupa" to "p" in isl format.
+ *
+ * If "mupa" is zero-dimensional and has a non-trivial explicit domain,
+ * then it is printed after the tuple of affine expressions.
+ * In order to clarify that this domain belongs to the expression,
+ * the tuple along with the domain are placed inside parentheses.
+ * If "mupa" has any parameters, then the opening parenthesis
+ * appears after the parameter declarations.
  */
 static __isl_give isl_printer *print_multi_union_pw_aff_isl(
        __isl_take isl_printer *p, __isl_keep isl_multi_union_pw_aff *mupa)
 {
        struct isl_print_space_data data = { 0 };
+       isl_bool has_domain;
        isl_space *space;
 
+       if (!mupa)
+               return isl_printer_free(p);
+       has_domain = isl_multi_union_pw_aff_has_non_trivial_domain(mupa);
+       if (has_domain < 0)
+               return isl_printer_free(p);
+
        space = isl_multi_union_pw_aff_get_space(mupa);
        p = print_param_tuple(p, space, &data);
 
+       if (has_domain)
+               p = isl_printer_print_str(p, "(");
+
        data.print_dim = &print_union_pw_aff_dim;
        data.user = mupa;
 
        p = isl_print_space(space, p, 0, &data);
        isl_space_free(space);
 
+       if (has_domain) {
+               p = isl_printer_print_str(p, " : ");
+               p = isl_printer_print_union_set_isl_body(p, mupa->u.dom);
+               p = isl_printer_print_str(p, ")");
+       }
+
        return p;
 }
 
index b7c70b7..3865f7c 100644 (file)
@@ -119,6 +119,200 @@ static int test_parse_multi_val(isl_ctx *ctx, const char *str)
        return mv ? 0 : -1;
 }
 
+/* Check that printing "mpa" and parsing the output results
+ * in the same expression.
+ */
+static isl_stat check_reparse_mpa(isl_ctx *ctx,
+       __isl_take isl_multi_pw_aff *mpa)
+{
+       char *str;
+       isl_bool equal;
+       isl_multi_pw_aff *mpa2;
+
+       str = isl_multi_pw_aff_to_str(mpa);
+       mpa2 = isl_multi_pw_aff_read_from_str(ctx, str);
+       free(str);
+       equal = isl_multi_pw_aff_plain_is_equal(mpa, mpa2);
+       isl_multi_pw_aff_free(mpa);
+       isl_multi_pw_aff_free(mpa2);
+       if (equal < 0)
+               return isl_stat_error;
+       if (!equal)
+               isl_die(ctx, isl_error_unknown,
+                       "parsed function not equal to original",
+                       return isl_stat_error);
+
+       return isl_stat_ok;
+}
+
+/* String descriptions of multi piecewise affine expressions
+ * that are used for testing printing and parsing.
+ */
+const char *parse_multi_mpa_tests[] = {
+       "{ A[x, y] -> [] : x + y >= 0 }",
+       "{ A[x, y] -> B[] : x + y >= 0 }",
+       "{ A[x, y] -> [x] : x + y >= 0 }",
+       "[N] -> { A[x, y] -> [x] : x + y <= N }",
+       "{ A[x, y] -> [x, y] : x + y >= 0 }",
+       "{ A[x, y] -> [(x : x >= 0), (y : y >= 0)] : x + y >= 0 }",
+       "[N] -> { [] : N >= 0 }",
+       "[N] -> { [] : N >= 0 }",
+       "[N] -> { [N] : N >= 0 }",
+       "[N] -> { [N, N + 1] : N >= 0 }",
+       "[N, M] -> { [(N : N >= 0), (M : M >= 0)] : N + M >= 0 }",
+};
+
+/* Test parsing of multi piecewise affine expressions by printing
+ * the expressions and checking that parsing the output results
+ * in the same expression.
+ * Do this for a couple of manually constructed expressions and
+ * a set of expressions parsed from strings.
+ */
+static int test_parse_mpa(isl_ctx *ctx)
+{
+       int i;
+       isl_space *space;
+       isl_set *dom;
+       isl_multi_pw_aff *mpa;
+       isl_stat r;
+
+       space = isl_space_set_alloc(ctx, 0, 0);
+       space = isl_space_set_tuple_name(space, isl_dim_set, "A");
+       mpa = isl_multi_pw_aff_zero(space);
+       r = check_reparse_mpa(ctx, mpa);
+       if (r < 0)
+               return -1;
+
+       space = isl_space_set_alloc(ctx, 1, 0);
+       space = isl_space_set_dim_name(space, isl_dim_param, 0, "N");
+       space = isl_space_set_tuple_name(space, isl_dim_set, "A");
+       dom = isl_set_universe(isl_space_params(isl_space_copy(space)));
+       dom = isl_set_lower_bound_si(dom, isl_dim_param, 0, 5);
+       mpa = isl_multi_pw_aff_zero(space);
+       mpa = isl_multi_pw_aff_intersect_domain(mpa, dom);
+       r = check_reparse_mpa(ctx, mpa);
+       if (r < 0)
+               return -1;
+
+       for (i = 0; i < ARRAY_SIZE(parse_multi_mpa_tests); ++i) {
+               const char *str;
+
+               str = parse_multi_mpa_tests[i];
+               mpa = isl_multi_pw_aff_read_from_str(ctx, str);
+               r = check_reparse_mpa(ctx, mpa);
+               if (r < 0)
+                       return -1;
+       }
+
+       return 0;
+}
+
+/* Check that printing "mupa" and parsing the output results
+ * in the same expression.
+ */
+static isl_stat check_reparse_mupa(isl_ctx *ctx,
+       __isl_take isl_multi_union_pw_aff *mupa)
+{
+       char *str;
+       isl_bool equal;
+       isl_multi_union_pw_aff *mupa2;
+
+       str = isl_multi_union_pw_aff_to_str(mupa);
+       mupa2 = isl_multi_union_pw_aff_read_from_str(ctx, str);
+       free(str);
+       equal = isl_multi_union_pw_aff_plain_is_equal(mupa, mupa2);
+       isl_multi_union_pw_aff_free(mupa);
+       isl_multi_union_pw_aff_free(mupa2);
+       if (equal < 0)
+               return isl_stat_error;
+       if (!equal)
+               isl_die(ctx, isl_error_unknown,
+                       "parsed function not equal to original",
+                       return isl_stat_error);
+
+       return isl_stat_ok;
+}
+
+/* String descriptions of multi union piecewise affine expressions
+ * that are used for testing printing and parsing.
+ */
+const char *parse_multi_mupa_tests[] = {
+       "[]",
+       "A[]",
+       "A[B[] -> C[]]",
+       "(A[] : { S[x] : x > 0; T[y] : y >= 0 })",
+       "(A[] : { })",
+       "[N] -> (A[] : { })",
+       "[N] -> (A[] : { : N >= 0 })",
+       "[N] -> (A[] : { S[x] : x > N; T[y] : y >= 0 })",
+       "(A[] : [N] -> { S[x] : x > N; T[y] : y >= 0 })",
+       "A[{ S[x] -> [x + 1]; T[x] -> [x] }]",
+       "(A[{ S[x] -> [x + 1]; T[x] -> [x] }] : "
+               "{ S[x] : x > 0; T[y] : y >= 0 })",
+};
+
+/* Test parsing of multi union piecewise affine expressions by printing
+ * the expressions and checking that parsing the output results
+ * in the same expression.
+ * Do this for a couple of manually constructed expressions and
+ * a set of expressions parsed from strings.
+ */
+static int test_parse_mupa(isl_ctx *ctx)
+{
+       int i;
+       isl_space *space;
+       isl_multi_union_pw_aff *mupa;
+       isl_set *dom;
+       isl_union_set *uset;
+       isl_stat r;
+
+       space = isl_space_set_alloc(ctx, 0, 0);
+       space = isl_space_set_tuple_name(space, isl_dim_set, "A");
+       mupa = isl_multi_union_pw_aff_zero(space);
+       r = check_reparse_mupa(ctx, mupa);
+       if (r < 0)
+               return -1;
+
+       space = isl_space_set_alloc(ctx, 1, 0);
+       space = isl_space_set_dim_name(space, isl_dim_param, 0, "N");
+       space = isl_space_set_tuple_name(space, isl_dim_set, "A");
+       dom = isl_set_universe(space);
+       dom = isl_set_lower_bound_si(dom, isl_dim_param, 0, 5);
+       uset = isl_union_set_from_set(dom);
+       space = isl_space_set_alloc(ctx, 1, 0);
+       space = isl_space_set_dim_name(space, isl_dim_param, 0, "N");
+       space = isl_space_set_tuple_name(space, isl_dim_set, "B");
+       mupa = isl_multi_union_pw_aff_zero(space);
+       mupa = isl_multi_union_pw_aff_intersect_domain(mupa, uset);
+       r = check_reparse_mupa(ctx, mupa);
+       if (r < 0)
+               return -1;
+
+       for (i = 0; i < ARRAY_SIZE(parse_multi_mupa_tests); ++i) {
+               const char *str;
+
+               str = parse_multi_mupa_tests[i];
+               mupa = isl_multi_union_pw_aff_read_from_str(ctx, str);
+               r = check_reparse_mupa(ctx, mupa);
+               if (r < 0)
+                       return -1;
+       }
+
+       return 0;
+}
+
+/* Test parsing of multi expressions.
+ */
+static int test_parse_multi(isl_ctx *ctx)
+{
+       if (test_parse_mpa(ctx) < 0)
+               return -1;
+       if (test_parse_mupa(ctx) < 0)
+               return -1;
+
+       return 0;
+}
+
 /* Pairs of binary relation representations that should represent
  * the same binary relations.
  */
@@ -206,6 +400,8 @@ int test_parse(struct isl_ctx *ctx)
                return -1;
        if (test_parse_multi_val(ctx, "{ A[4, infty, NaN, -1/2, 2/3] }") < 0)
                return -1;
+       if (test_parse_multi(ctx) < 0)
+               return -1;
 
        str = "{ [i] -> [-i] }";
        map = isl_map_read_from_str(ctx, str);
@@ -4879,6 +5075,15 @@ struct {
        { &isl_multi_pw_aff_factor_range,
          "{ [B[] -> C[]] }",
          "{ C[] }" },
+       { &isl_multi_pw_aff_range_factor_domain,
+         "{ A[x] -> [B[] -> C[]] : x >= 0 }",
+         "{ A[x] -> B[] : x >= 0 }" },
+       { &isl_multi_pw_aff_range_factor_range,
+         "{ A[x] -> [B[] -> C[]] : x >= 0 }",
+         "{ A[y] -> C[] : y >= 0 }" },
+       { &isl_multi_pw_aff_factor_range,
+         "[N] -> { [B[] -> C[]] : N >= 0 }",
+         "[N] -> { C[] : N >= 0 }" },
 };
 
 /* Perform some basic tests of unary operations on isl_multi_pw_aff objects.
@@ -4924,16 +5129,56 @@ struct {
        { &isl_multi_pw_aff_add, "{ A[x] -> [(1 : x >= 5)] }",
          "{ A[x] -> [(x : x <= 10)] }",
          "{ A[x] -> [(1 + x : 5 <= x <= 10)] }" },
+       { &isl_multi_pw_aff_add, "{ A[x] -> [] : x >= 5 }",
+         "{ A[x] -> [] : x <= 10 }",
+         "{ A[x] -> [] : 5 <= x <= 10 }" },
+       { &isl_multi_pw_aff_add, "{ A[x] -> [] : x >= 5 }",
+         "[N] -> { A[x] -> [] : x <= N }",
+         "[N] -> { A[x] -> [] : 5 <= x <= N }" },
+       { &isl_multi_pw_aff_add,
+         "[N] -> { A[x] -> [] : x <= N }",
+         "{ A[x] -> [] : x >= 5 }",
+         "[N] -> { A[x] -> [] : 5 <= x <= N }" },
        { &isl_multi_pw_aff_range_product, "{ A[x] -> B[(1 : x >= 5)] }",
          "{ A[y] -> C[(2 : y <= 10)] }",
          "{ A[x] -> [B[(1 : x >= 5)] -> C[(2 : x <= 10)]] }" },
+       { &isl_multi_pw_aff_range_product, "{ A[x] -> B[1] : x >= 5 }",
+         "{ A[y] -> C[2] : y <= 10 }",
+         "{ A[x] -> [B[(1 : x >= 5)] -> C[(2 : x <= 10)]] }" },
+       { &isl_multi_pw_aff_range_product, "{ A[x] -> B[1] : x >= 5 }",
+         "[N] -> { A[y] -> C[2] : y <= N }",
+         "[N] -> { A[x] -> [B[(1 : x >= 5)] -> C[(2 : x <= N)]] }" },
+       { &isl_multi_pw_aff_range_product, "[N] -> { A[x] -> B[1] : x >= N }",
+         "{ A[y] -> C[2] : y <= 10 }",
+         "[N] -> { A[x] -> [B[(1 : x >= N)] -> C[(2 : x <= 10)]] }" },
        { &isl_multi_pw_aff_range_product, "{ A[] -> B[1] }", "{ A[] -> C[2] }",
          "{ A[] -> [B[1] -> C[2]] }" },
        { &isl_multi_pw_aff_range_product, "{ A[] -> B[] }", "{ A[] -> C[] }",
          "{ A[] -> [B[] -> C[]] }" },
+       { &isl_multi_pw_aff_range_product, "{ A[x] -> B[(1 : x >= 5)] }",
+         "{ A[y] -> C[] : y <= 10 }",
+         "{ A[x] -> [B[(1 : x >= 5)] -> C[]] : x <= 10 }" },
+       { &isl_multi_pw_aff_range_product, "{ A[y] -> C[] : y <= 10 }",
+         "{ A[x] -> B[(1 : x >= 5)] }",
+         "{ A[x] -> [C[] -> B[(1 : x >= 5)]] : x <= 10 }" },
        { &isl_multi_pw_aff_product, "{ A[x] -> B[(1 : x >= 5)] }",
          "{ A[y] -> C[(2 : y <= 10)] }",
          "{ [A[x] -> A[y]] -> [B[(1 : x >= 5)] -> C[(2 : y <= 10)]] }" },
+       { &isl_multi_pw_aff_product, "{ A[x] -> B[(1 : x >= 5)] }",
+         "{ A[y] -> C[] : y <= 10 }",
+         "{ [A[x] -> A[y]] -> [B[(1 : x >= 5)] -> C[]] : y <= 10 }" },
+       { &isl_multi_pw_aff_product, "{ A[y] -> C[] : y <= 10 }",
+         "{ A[x] -> B[(1 : x >= 5)] }",
+         "{ [A[y] -> A[x]] -> [C[] -> B[(1 : x >= 5)]] : y <= 10 }" },
+       { &isl_multi_pw_aff_product, "{ A[x] -> B[(1 : x >= 5)] }",
+         "[N] -> { A[y] -> C[] : y <= N }",
+         "[N] -> { [A[x] -> A[y]] -> [B[(1 : x >= 5)] -> C[]] : y <= N }" },
+       { &isl_multi_pw_aff_product, "[N] -> { A[y] -> C[] : y <= N }",
+         "{ A[x] -> B[(1 : x >= 5)] }",
+         "[N] -> { [A[y] -> A[x]] -> [C[] -> B[(1 : x >= 5)]] : y <= N }" },
+       { &isl_multi_pw_aff_product, "{ A[x] -> B[] : x >= 5 }",
+         "{ A[y] -> C[] : y <= 10 }",
+         "{ [A[x] -> A[y]] -> [B[] -> C[]] : x >= 5 and y <= 10 }" },
        { &isl_multi_pw_aff_product, "{ A[] -> B[1] }", "{ A[] -> C[2] }",
          "{ [A[] -> A[]] -> [B[1] -> C[2]] }" },
        { &isl_multi_pw_aff_product, "{ A[] -> B[] }", "{ A[] -> C[] }",
@@ -4949,6 +5194,26 @@ struct {
          "{ B[i,j] -> C[(i + 2j : j > 4)] }",
          "{ A[a,b] -> B[(b : b > a),(a : b > a)] }",
          "{ A[a,b] -> C[(b + 2a : b > a > 4)] }" },
+       { &isl_multi_pw_aff_pullback_multi_pw_aff,
+         "{ B[i,j] -> C[] }",
+         "{ A[a,b] -> B[(b : b > a),(a : b > a)] }",
+         "{ A[a,b] -> C[] }" },
+       { &isl_multi_pw_aff_pullback_multi_pw_aff,
+         "{ B[i,j] -> C[] : i > j }",
+         "{ A[a,b] -> B[b,a] }",
+         "{ A[a,b] -> C[] : b > a }" },
+       { &isl_multi_pw_aff_pullback_multi_pw_aff,
+         "{ B[i,j] -> C[] : j > 5 }",
+         "{ A[a,b] -> B[(b : b > a),(a : b > a)] }",
+         "{ A[a,b] -> C[] : b > a > 5 }" },
+       { &isl_multi_pw_aff_pullback_multi_pw_aff,
+         "[N] -> { B[i,j] -> C[] : j > N }",
+         "{ A[a,b] -> B[(b : b > a),(a : b > a)] }",
+         "[N] -> { A[a,b] -> C[] : b > a > N }" },
+       { &isl_multi_pw_aff_pullback_multi_pw_aff,
+         "[M,N] -> { B[] -> C[] : N > 5 }",
+         "[M,N] -> { A[] -> B[] : M > N }",
+         "[M,N] -> { A[] -> C[] : M > N > 5 }" },
 };
 
 /* Perform some basic tests of binary operations on isl_multi_pw_aff objects.
@@ -5004,6 +5269,15 @@ struct {
        { &isl_multi_union_pw_aff_factor_range,
          "[B[] -> C[]]",
          "C[]" },
+       { &isl_multi_union_pw_aff_factor_range,
+         "([B[] -> C[]] : { A[x] : x >= 0 })",
+         "(C[] : { A[x] : x >= 0 })" },
+       { &isl_multi_union_pw_aff_factor_range,
+         "[N] -> ([B[] -> C[]] : { A[x] : x <= N })",
+         "[N] -> (C[] : { A[x] : x <= N })" },
+       { &isl_multi_union_pw_aff_factor_range,
+         "[N] -> ([B[] -> C[]] : { : N >= 0 })",
+         "[N] -> (C[] : { : N >= 0 })" },
 };
 
 /* Perform some basic tests of unary operations on
@@ -5063,15 +5337,66 @@ struct {
        { &isl_multi_union_pw_aff_add, "[{ A[x] -> [(1)] : x >= 5 }]",
          "[{ A[x] -> [(x)] : x <= 10 }]",
          "[{ A[x] -> [(1 + x)] : 5 <= x <= 10 }]" },
+       { &isl_multi_union_pw_aff_add, "([] : { A[x] : x >= 5 })",
+         "([] : { A[x] : x <= 10 })",
+         "([] : { A[x] : 5 <= x <= 10 })" },
+       { &isl_multi_union_pw_aff_add, "([] : { A[x] : x >= 5 })",
+         "[N] -> ([] : { A[x] : x <= N })",
+         "[N] -> ([] : { A[x] : 5 <= x <= N })" },
+       { &isl_multi_union_pw_aff_add, "[N] -> ([] : { A[x] : x >= N })",
+         "([] : { A[x] : x <= 10 })",
+         "[N] -> ([] : { A[x] : N <= x <= 10 })" },
        { &isl_multi_union_pw_aff_union_add, "[{ A[x] -> [(1)] : x >= 5 }]",
          "[{ A[x] -> [(x)] : x <= 10 }]",
          "[{ A[x] -> [(1 + x)] : 5 <= x <= 10; "
             "A[x] -> [(1)] : x > 10; A[x] -> [(x)] : x < 5 }]" },
+       { &isl_multi_union_pw_aff_union_add, "([] : { A[x] : x >= 5 })",
+         "([] : { A[x] : x <= 10 })",
+         "([] : { A[x] })" },
+       { &isl_multi_union_pw_aff_union_add, "([] : { A[x] : x >= 0 })",
+         "[N] -> ([] : { A[x] : x >= N })",
+         "[N] -> ([] : { A[x] : x >= 0 or x >= N })" },
+       { &isl_multi_union_pw_aff_union_add,
+         "[N] -> ([] : { A[] : N >= 0})",
+         "[N] -> ([] : { A[] : N <= 0})",
+         "[N] -> ([] : { A[] })" },
+       { &isl_multi_union_pw_aff_union_add,
+         "[N] -> ([] : { A[] })",
+         "[N] -> ([] : { : })",
+         "[N] -> ([] : { : })" },
+       { &isl_multi_union_pw_aff_union_add,
+         "[N] -> ([] : { : })",
+         "[N] -> ([] : { A[] })",
+         "[N] -> ([] : { : })" },
+       { &isl_multi_union_pw_aff_union_add,
+         "[N] -> ([] : { : N >= 0})",
+         "[N] -> ([] : { : N <= 0})",
+         "[N] -> ([] : { : })" },
        { &isl_multi_union_pw_aff_range_product,
          "B[{ A[] -> [1] }]",
          "C[{ A[] -> [2] }]",
          "[B[{ A[] -> [1] }] -> C[{ A[] -> [2] }]]" },
        { &isl_multi_union_pw_aff_range_product,
+         "(B[] : { A[x] : x >= 5 })",
+         "(C[] : { A[x] : x <= 10 })",
+         "([B[] -> C[]] : { A[x] : 5 <= x <= 10 })" },
+       { &isl_multi_union_pw_aff_range_product,
+         "B[{ A[x] -> [x + 1] : x >= 5 }]",
+         "(C[] : { A[x] : x <= 10 })",
+         "[B[{ A[x] -> [x + 1] : 5 <= x <= 10 }] -> C[]]" },
+       { &isl_multi_union_pw_aff_range_product,
+         "(C[] : { A[x] : x <= 10 })",
+         "B[{ A[x] -> [x + 1] : x >= 5 }]",
+         "[C[] -> B[{ A[x] -> [x + 1] : 5 <= x <= 10 }]]" },
+       { &isl_multi_union_pw_aff_range_product,
+         "B[{ A[x] -> [x + 1] : x >= 5 }]",
+         "[N] -> (C[] : { A[x] : x <= N })",
+         "[N] -> [B[{ A[x] -> [x + 1] : 5 <= x <= N }] -> C[]]" },
+       { &isl_multi_union_pw_aff_range_product,
+         "[N] -> (C[] : { A[x] : x <= N })",
+         "B[{ A[x] -> [x + 1] : x >= 5 }]",
+         "[N] -> [C[] -> B[{ A[x] -> [x + 1] : 5 <= x <= N }]]" },
+       { &isl_multi_union_pw_aff_range_product,
          "B[{ A[] -> [1]; D[] -> [3] }]",
          "C[{ A[] -> [2] }]",
          "[B[{ A[] -> [1]; D[] -> [3] }] -> C[{ A[] -> [2] }]]" },
@@ -5133,6 +5458,32 @@ struct {
        { &isl_multi_union_pw_aff_intersect_range,
          "C[{ B[i,j] -> [i + 2j] }]", "[N] -> { C[x] : N >= 0 }",
          "[N] -> C[{ B[i,j] -> [i + 2j] : N >= 0 }]" },
+       { &isl_multi_union_pw_aff_intersect_range,
+         "C[]", "{ C[] }", "C[]" },
+       { &isl_multi_union_pw_aff_intersect_range,
+         "[N] -> (C[] : { : N >= 0 })",
+         "{ C[] }",
+         "[N] -> (C[] : { : N >= 0 })" },
+       { &isl_multi_union_pw_aff_intersect_range,
+         "(C[] : { A[a,b] })",
+         "{ C[] }",
+         "(C[] : { A[a,b] })" },
+       { &isl_multi_union_pw_aff_intersect_range,
+         "[N] -> (C[] : { A[a,b] : a,b <= N })",
+         "{ C[] }",
+         "[N] -> (C[] : { A[a,b] : a,b <= N })" },
+       { &isl_multi_union_pw_aff_intersect_range,
+         "C[]",
+         "[N] -> { C[] : N >= 0 }",
+         "[N] -> (C[] : { : N >= 0 })" },
+       { &isl_multi_union_pw_aff_intersect_range,
+         "(C[] : { A[a,b] })",
+         "[N] -> { C[] : N >= 0 }",
+         "[N] -> (C[] : { A[a,b] : N >= 0 })" },
+       { &isl_multi_union_pw_aff_intersect_range,
+         "[N] -> (C[] : { : N >= 0 })",
+         "[N] -> { C[] : N < 1024 }",
+         "[N] -> (C[] : { : 0 <= N < 1024 })" },
        { &isl_multi_union_pw_aff_intersect_params,
          "C[{ B[i,j] -> [i + 2j] }]", "[N] -> { : N >= 0 }",
          "[N] -> C[{ B[i,j] -> [i + 2j] : N >= 0}]" },
@@ -5142,6 +5493,18 @@ struct {
        { &isl_multi_union_pw_aff_intersect_params,
          "[N] -> C[{ B[i,j] -> [i + 2j] : N <= 256 }]", "{ : }",
          "[N] -> C[{ B[i,j] -> [i + 2j] : N <= 256 }]" },
+       { &isl_multi_union_pw_aff_intersect_params,
+         "C[]", "[N] -> { : N >= 0 }",
+         "[N] -> (C[] : { : N >= 0 })" },
+       { &isl_multi_union_pw_aff_intersect_params,
+         "(C[] : { A[a,b] })", "[N] -> { : N >= 0 }",
+         "[N] -> (C[] : { A[a,b] : N >= 0 })" },
+       { &isl_multi_union_pw_aff_intersect_params,
+         "[N] -> (C[] : { A[a,N] })", "{ : }",
+         "[N] -> (C[] : { A[a,N] })" },
+       { &isl_multi_union_pw_aff_intersect_params,
+         "[N] -> (C[] : { A[a,b] : N <= 256 })", "[N] -> { : N >= 0 }",
+         "[N] -> (C[] : { A[a,b] : 0 <= N <= 256 })" },
 };
 
 /* Perform some basic tests of binary operations on
@@ -5191,6 +5554,18 @@ struct {
        { &isl_multi_union_pw_aff_intersect_domain,
          "C[{ B[i,j] -> [i + 2j] }]", "{ B[i,i] }",
          "C[{ B[i,i] -> [3i] }]" },
+       { &isl_multi_union_pw_aff_intersect_domain,
+         "(C[] : { B[i,j] })", "{ B[i,i] }",
+         "(C[] : { B[i,i] })" },
+       { &isl_multi_union_pw_aff_intersect_domain,
+         "(C[] : { B[i,j] })", "[N] -> { B[N,N] }",
+         "[N] -> (C[] : { B[N,N] })" },
+       { &isl_multi_union_pw_aff_intersect_domain,
+         "C[]", "{ B[i,i] }",
+         "(C[] : { B[i,i] })" },
+       { &isl_multi_union_pw_aff_intersect_domain,
+         "[N] -> (C[] : { : N >= 0 })", "{ B[i,i] }",
+         "[N] -> (C[] : { B[i,i] : N >= 0 })" },
 };
 
 /* Perform some basic tests of binary operations on
@@ -5258,6 +5633,26 @@ struct {
          "C[]",
          "{ C[] -> D[] }",
          "D[]" },
+       { &isl_multi_union_pw_aff_apply_multi_aff,
+         "[N] -> (C[] : { : N >= 0 })",
+         "{ C[] -> D[] }",
+         "[N] -> (D[] : { : N >= 0 })" },
+       { &isl_multi_union_pw_aff_apply_multi_aff,
+         "C[]",
+         "[N] -> { C[] -> D[N] }",
+         "[N] -> D[{ [N] }]" },
+       { &isl_multi_union_pw_aff_apply_multi_aff,
+         "(C[] : { A[i,j] : i >= j })",
+         "{ C[] -> D[] }",
+         "(D[] : { A[i,j] : i >= j })" },
+       { &isl_multi_union_pw_aff_apply_multi_aff,
+         "[N] -> (C[] : { A[i,j] : N >= 0 })",
+         "{ C[] -> D[] }",
+         "[N] -> (D[] : { A[i,j] : N >= 0 })" },
+       { &isl_multi_union_pw_aff_apply_multi_aff,
+         "(C[] : { A[i,j] : i >= j })",
+         "[N] -> { C[] -> D[N] }",
+         "[N] -> (D[{ A[i,j] -> [N] : i >= j }])" },
 };
 
 /* Perform some basic tests of binary operations on
@@ -5313,6 +5708,34 @@ struct {
          "{ C[a] -> [a] : a >= 0; C[a] -> [-a] : a < 0 }",
          "{ A[i,j] -> [i] : i >= 0; A[i,j] -> [-i] : i < 0; "
            "B[i,j] -> [j] : j >= 0; B[i,j] -> [-j] : j < 0 }" },
+       { &isl_multi_union_pw_aff_apply_pw_aff,
+         "C[]",
+         "[N] -> { C[] -> [N] }",
+         "[N] -> { [N] }" },
+       { &isl_multi_union_pw_aff_apply_pw_aff,
+         "C[]",
+         "[N] -> { C[] -> [N] : N >= 0; C[] -> [-N] : N < 0 }",
+         "[N] -> { [N] : N >= 0; [-N] : N < 0 }" },
+       { &isl_multi_union_pw_aff_apply_pw_aff,
+         "[N] -> (C[] : { : N >= 0 })",
+         "[N] -> { C[] -> [N] }",
+         "[N] -> { [N] : N >= 0 }" },
+       { &isl_multi_union_pw_aff_apply_pw_aff,
+         "[N] -> (C[] : { : N >= 0 })",
+         "[N] -> { C[] -> [N] : N >= 0; C[] -> [-N] : N < 0 }",
+         "[N] -> { [N] : N >= 0 }" },
+       { &isl_multi_union_pw_aff_apply_pw_aff,
+         "[N] -> (C[] : { : N >= 0 })",
+         "{ C[] -> [0] }",
+         "[N] -> { [0] : N >= 0 }" },
+       { &isl_multi_union_pw_aff_apply_pw_aff,
+         "(C[] : { A[i,j] : i >= j })",
+         "[N] -> { C[] -> [N] }",
+         "[N] -> { A[i,j] -> [N] : i >= j }" },
+       { &isl_multi_union_pw_aff_apply_pw_aff,
+         "(C[] : { A[i,j] : i >= j })",
+         "[N] -> { C[] -> [N] : N >= 0 }",
+         "[N] -> { A[i,j] -> [N] : i >= j and N >= 0 }" },
 };
 
 /* Perform some basic tests of binary operations on
@@ -5393,6 +5816,46 @@ struct {
          "C[]",
          "{ C[] -> D[] }",
          "D[]" },
+       { &isl_multi_union_pw_aff_apply_pw_multi_aff,
+         "[N] -> (C[] : { : N >= 0 })",
+         "{ C[] -> D[] }",
+         "[N] -> (D[] : { : N >= 0 })" },
+       { &isl_multi_union_pw_aff_apply_pw_multi_aff,
+         "C[]",
+         "[N] -> { C[] -> D[N] }",
+         "[N] -> D[{ [N] }]" },
+       { &isl_multi_union_pw_aff_apply_pw_multi_aff,
+         "(C[] : { A[i,j] : i >= j })",
+         "{ C[] -> D[] }",
+         "(D[] : { A[i,j] : i >= j })" },
+       { &isl_multi_union_pw_aff_apply_pw_multi_aff,
+         "[N] -> (C[] : { A[i,j] : N >= 0 })",
+         "{ C[] -> D[] }",
+         "[N] -> (D[] : { A[i,j] : N >= 0 })" },
+       { &isl_multi_union_pw_aff_apply_pw_multi_aff,
+         "(C[] : { A[i,j] : i >= j })",
+         "[N] -> { C[] -> D[N] }",
+         "[N] -> (D[{ A[i,j] -> [N] : i >= j }])" },
+       { &isl_multi_union_pw_aff_apply_pw_multi_aff,
+         "C[]",
+         "[N] -> { C[] -> D[N] : N >= 0; C[] -> D[-N] : N < 0 }",
+         "[N] -> D[{ [N] : N >= 0; [-N] : N < 0 }]" },
+       { &isl_multi_union_pw_aff_apply_pw_multi_aff,
+         "[N] -> (C[] : { : N >= 0 })",
+         "[N] -> { C[] -> D[N] }",
+         "[N] -> D[{ [N] : N >= 0 }]" },
+       { &isl_multi_union_pw_aff_apply_pw_multi_aff,
+         "[N] -> (C[] : { : N >= 0 })",
+         "[N] -> { C[] -> D[N] : N >= 0; C[] -> D[-N] : N < 0 }",
+         "[N] -> D[{ [N] : N >= 0 }]" },
+       { &isl_multi_union_pw_aff_apply_pw_multi_aff,
+         "[N] -> (C[] : { : N >= 0 })",
+         "{ C[] -> D[0] }",
+         "[N] -> D[{ [0] : N >= 0 }]" },
+       { &isl_multi_union_pw_aff_apply_pw_multi_aff,
+         "(C[] : { A[i,j] : i >= j })",
+         "[N] -> { C[] -> D[N] : N >= 0 }",
+         "[N] -> D[{ A[i,j] -> [N] : i >= j and N >= 0 }]" },
 };
 
 /* Perform some basic tests of binary operations on
@@ -5455,6 +5918,42 @@ struct {
          "C[{ B[i,j] -> [i + 2j] }]",
          "{ A[a,b] -> B[b,a] : a > b; A[a,b] -> B[a,b] : a <= b }",
          "C[{ A[a,b] -> [b + 2a] : a > b; A[a,b] -> [a + 2b] : a <= b }]" },
+       { &isl_multi_union_pw_aff_pullback_union_pw_multi_aff,
+         "(C[] : { B[a,b] })",
+         "{ A[a,b] -> B[b,a] }",
+         "(C[] : { A[a,b] })" },
+       { &isl_multi_union_pw_aff_pullback_union_pw_multi_aff,
+         "(C[] : { B[a,b] })",
+         "{ B[a,b] -> A[b,a] }",
+         "(C[] : { })" },
+       { &isl_multi_union_pw_aff_pullback_union_pw_multi_aff,
+         "(C[] : { B[a,b] })",
+         "{ A[a,b] -> B[b,a] : a > b }",
+         "(C[] : { A[a,b] : a > b })" },
+       { &isl_multi_union_pw_aff_pullback_union_pw_multi_aff,
+         "(C[] : { B[a,b] : a > b })",
+         "{ A[a,b] -> B[b,a] }",
+         "(C[] : { A[a,b] : b > a })" },
+       { &isl_multi_union_pw_aff_pullback_union_pw_multi_aff,
+         "[N] -> (C[] : { B[a,b] : a > N })",
+         "{ A[a,b] -> B[b,a] : a > b }",
+         "[N] -> (C[] : { A[a,b] : a > b > N })" },
+       { &isl_multi_union_pw_aff_pullback_union_pw_multi_aff,
+         "(C[] : { B[a,b] : a > b })",
+         "[N] -> { A[a,b] -> B[b,a] : a > N }",
+         "[N] -> (C[] : { A[a,b] : b > a > N })" },
+       { &isl_multi_union_pw_aff_pullback_union_pw_multi_aff,
+         "C[]",
+         "{ A[a,b] -> B[b,a] }",
+         "C[]" },
+       { &isl_multi_union_pw_aff_pullback_union_pw_multi_aff,
+         "[N] -> (C[] : { : N >= 0 })",
+         "{ A[a,b] -> B[b,a] }",
+         "[N] -> (C[] : { : N >= 0 })" },
+       { &isl_multi_union_pw_aff_pullback_union_pw_multi_aff,
+         "C[]",
+         "[N] -> { A[a,b] -> B[b,a] : N >= 0 }",
+         "[N] -> (C[] : { : N >= 0 })" },
 };
 
 /* Perform some basic tests of binary operations on
@@ -6098,6 +6597,9 @@ struct {
        { &isl_multi_union_pw_aff_zero_union_set,
          "F[{ A[i,j] -> [i-j]; B[i,j] -> [i-j] : i >= 0 }]",
          "{ A[i,i]; B[i,i] : i >= 0 }" },
+       { &isl_multi_union_pw_aff_zero_union_set,
+         "(F[] : { A[i,j]; B[i,i] : i >= 0 })",
+         "{ A[i,j]; B[i,i] : i >= 0 }" },
 };
 
 /* Perform some basic tests of functions that select
@@ -6174,6 +6676,26 @@ struct {
          "F[{ A[i,j] -> [i]; B[i,j] -> [i] }, "
            "{ A[i,j] -> [j]; B[i,j] -> [j] }]",
          "{ A[i,j] -> B[i',j'] : i,j >> i',j' }" },
+       { &isl_union_map_eq_at_multi_union_pw_aff,
+         "{ A[i,j] -> B[i',j']; A[i,j] -> C[i',j'] }",
+         "(F[] : { A[i,j]; B[i,j] })",
+         "{ A[i,j] -> B[i',j'] }" },
+       { &isl_union_map_eq_at_multi_union_pw_aff,
+         "{ A[i,j] -> B[i',j'] }",
+         "(F[] : { A[i,j] : i > j; B[i,j] : i < j })",
+         "{ A[i,j] -> B[i',j'] : i > j and i' < j' }" },
+       { &isl_union_map_eq_at_multi_union_pw_aff,
+         "[N] -> { A[i,j] -> B[i',j'] : i,i' <= N }",
+         "(F[] : { A[i,j] : i > j; B[i,j] : i < j })",
+         "[N] -> { A[i,j] -> B[i',j'] : i > j and i' < j' and i,i' <= N }" },
+       { &isl_union_map_eq_at_multi_union_pw_aff,
+         "{ A[i,j] -> B[i',j'] }",
+         "[N] -> (F[] : { A[i,j] : i < N; B[i,j] : i < N })",
+         "[N] -> { A[i,j] -> B[i',j'] : i,i' < N }" },
+       { &isl_union_map_eq_at_multi_union_pw_aff,
+         "{ A[i,j] -> B[i',j'] }",
+         "[N] -> (F[] : { : N >= 0 })",
+         "[N] -> { A[i,j] -> B[i',j'] : N >= 0 }" },
 };
 
 /* Perform some basic tests of functions that select
@@ -6697,6 +7219,7 @@ const char *mpa_conversion_tests[] = {
        "{ [x] -> A[x] : x >= 0; [x] -> A[-x] : x < 0 }",
        "{ [x] -> A[x, x + 1] }",
        "{ [x] -> A[] }",
+       "{ [x] -> A[] : x >= 0 }",
 };
 
 /* Check that conversion from isl_pw_multi_aff to isl_multi_pw_aff and
@@ -6743,6 +7266,7 @@ const char *umap_mupa_conversion_tests[] = {
            "5e0 = -a - b + c and c >= -a and c <= 4 - a) }",
        "{ [a, b] -> [c] : exists d : 18 * d = -3 - a + 2c and 1 <= c <= 3 }",
        "{ A[] -> B[0]; C[] -> B[1] }",
+       "{ A[] -> B[]; C[] -> B[] }",
 };
 
 /* Check that converting from isl_union_map to isl_multi_union_pw_aff and back
@@ -7518,7 +8042,7 @@ static int test_pw_multi_aff(isl_ctx *ctx)
 /* Check that we can properly parse multi piecewise affine expressions
  * where the piecewise affine expressions have different domains.
  */
-static int test_multi_pw_aff(isl_ctx *ctx)
+static int test_multi_pw_aff_1(isl_ctx *ctx)
 {
        const char *str;
        isl_set *dom, *dom2;
@@ -7563,6 +8087,61 @@ static int test_multi_pw_aff(isl_ctx *ctx)
        return 0;
 }
 
+/* Check that the dimensions in the explicit domain
+ * of a multi piecewise affine expression are properly
+ * taken into account.
+ */
+static int test_multi_pw_aff_2(isl_ctx *ctx)
+{
+       const char *str;
+       isl_bool involves1, involves2, involves3, equal;
+       isl_multi_pw_aff *mpa, *mpa1, *mpa2;
+
+       str = "{ A[x,y] -> B[] : x >= y }";
+       mpa = isl_multi_pw_aff_read_from_str(ctx, str);
+       involves1 = isl_multi_pw_aff_involves_dims(mpa, isl_dim_in, 0, 2);
+       mpa1 = isl_multi_pw_aff_copy(mpa);
+
+       mpa = isl_multi_pw_aff_insert_dims(mpa, isl_dim_in, 0, 1);
+       involves2 = isl_multi_pw_aff_involves_dims(mpa, isl_dim_in, 0, 1);
+       involves3 = isl_multi_pw_aff_involves_dims(mpa, isl_dim_in, 1, 2);
+       str = "{ [a,x,y] -> B[] : x >= y }";
+       mpa2 = isl_multi_pw_aff_read_from_str(ctx, str);
+       equal = isl_multi_pw_aff_plain_is_equal(mpa, mpa2);
+       isl_multi_pw_aff_free(mpa2);
+
+       mpa = isl_multi_pw_aff_drop_dims(mpa, isl_dim_in, 0, 1);
+       mpa = isl_multi_pw_aff_set_tuple_name(mpa, isl_dim_in, "A");
+       if (equal >= 0 && equal)
+               equal = isl_multi_pw_aff_plain_is_equal(mpa, mpa1);
+       isl_multi_pw_aff_free(mpa1);
+       isl_multi_pw_aff_free(mpa);
+
+       if (involves1 < 0 || involves2 < 0 || involves3 < 0 || equal < 0)
+               return -1;
+       if (!equal)
+               isl_die(ctx, isl_error_unknown,
+                       "incorrect result of dimension insertion/removal",
+                       return isl_stat_error);
+       if (!involves1 || involves2 || !involves3)
+               isl_die(ctx, isl_error_unknown,
+                       "incorrect characterization of involved dimensions",
+                       return isl_stat_error);
+
+       return 0;
+}
+
+/* Perform some tests on multi piecewise affine expressions.
+ */
+static int test_multi_pw_aff(isl_ctx *ctx)
+{
+       if (test_multi_pw_aff_1(ctx) < 0)
+               return -1;
+       if (test_multi_pw_aff_2(ctx) < 0)
+               return -1;
+       return 0;
+}
+
 /* This is a regression test for a bug where isl_basic_map_simplify
  * would end up in an infinite loop.  In particular, we construct
  * an empty basic set that is not obviously empty.
@@ -7705,6 +8284,50 @@ static int test_schedule_tree(isl_ctx *ctx)
        return 0;
 }
 
+/* Check that a zero-dimensional prefix schedule keeps track
+ * of the domain and outer filters.
+ */
+static int test_schedule_tree_prefix(isl_ctx *ctx)
+{
+       const char *str;
+       isl_bool equal;
+       isl_union_set *uset;
+       isl_union_set_list *filters;
+       isl_multi_union_pw_aff *mupa, *mupa2;
+       isl_schedule_node *node;
+
+       str = "{ S1[i,j] : 0 <= i,j < 10; S2[i,j] : 0 <= i,j < 10 }";
+       uset = isl_union_set_read_from_str(ctx, str);
+       node = isl_schedule_node_from_domain(uset);
+       node = isl_schedule_node_child(node, 0);
+
+       str = "{ S1[i,j] : i > j }";
+       uset = isl_union_set_read_from_str(ctx, str);
+       filters = isl_union_set_list_from_union_set(uset);
+       str = "{ S1[i,j] : i <= j; S2[i,j] }";
+       uset = isl_union_set_read_from_str(ctx, str);
+       filters = isl_union_set_list_add(filters, uset);
+       node = isl_schedule_node_insert_sequence(node, filters);
+
+       node = isl_schedule_node_child(node, 0);
+       node = isl_schedule_node_child(node, 0);
+       mupa = isl_schedule_node_get_prefix_schedule_multi_union_pw_aff(node);
+       str = "([] : { S1[i,j] : i > j })";
+       mupa2 = isl_multi_union_pw_aff_read_from_str(ctx, str);
+       equal = isl_multi_union_pw_aff_plain_is_equal(mupa, mupa2);
+       isl_multi_union_pw_aff_free(mupa2);
+       isl_multi_union_pw_aff_free(mupa);
+       isl_schedule_node_free(node);
+
+       if (equal < 0)
+               return -1;
+       if (!equal)
+               isl_die(ctx, isl_error_unknown, "unexpected prefix schedule",
+                       return -1);
+
+       return 0;
+}
+
 /* Check that the reaching domain elements and the prefix schedule
  * at a leaf node are the same before and after grouping.
  */
@@ -8226,6 +8849,7 @@ struct {
        { "schedule (whole component)", &test_schedule_whole },
        { "schedule (incremental)", &test_schedule_incremental },
        { "schedule tree", &test_schedule_tree },
+       { "schedule tree prefix", &test_schedule_tree_prefix },
        { "schedule tree grouping", &test_schedule_tree_group },
        { "tile", &test_tile },
        { "union_pw", &test_union_pw },
index 3ee8ec3..2563e24 100644 (file)
@@ -18,7 +18,7 @@
 #include <isl_union_map_private.h>
 #include <isl/ctx.h>
 #include <isl/hash.h>
-#include <isl/aff.h>
+#include <isl_aff_private.h>
 #include <isl/map.h>
 #include <isl/set.h>
 #include <isl_space_private.h>
@@ -3907,6 +3907,41 @@ static isl_stat order_at(__isl_take isl_map *map, void *user)
        return data->res ? isl_stat_ok : isl_stat_error;
 }
 
+/* If "mupa" has a non-trivial explicit domain, then intersect
+ * domain and range of "umap" with this explicit domain.
+ * If the explicit domain only describes constraints on the parameters,
+ * then the intersection only needs to be performed once.
+ */
+static __isl_give isl_union_map *intersect_explicit_domain(
+       __isl_take isl_union_map *umap, __isl_keep isl_multi_union_pw_aff *mupa)
+{
+       isl_bool non_trivial, is_params;
+       isl_union_set *dom;
+
+       non_trivial = isl_multi_union_pw_aff_has_non_trivial_domain(mupa);
+       if (non_trivial < 0)
+               return isl_union_map_free(umap);
+       if (!non_trivial)
+               return umap;
+       mupa = isl_multi_union_pw_aff_copy(mupa);
+       dom = isl_multi_union_pw_aff_domain(mupa);
+       is_params = isl_union_set_is_params(dom);
+       if (is_params < 0) {
+               isl_union_set_free(dom);
+               return isl_union_map_free(umap);
+       }
+       if (is_params) {
+               isl_set *set;
+
+               set = isl_union_set_params(dom);
+               umap = isl_union_map_intersect_params(umap, set);
+               return umap;
+       }
+       umap = isl_union_map_intersect_domain(umap, isl_union_set_copy(dom));
+       umap = isl_union_map_intersect_range(umap, dom);
+       return umap;
+}
+
 /* Intersect each map in "umap" with the result of calling "order"
  * on the functions is "mupa" that apply to the domain and the range
  * of the map.
@@ -3922,6 +3957,7 @@ static __isl_give isl_union_map *isl_union_map_order_at_multi_union_pw_aff(
                                isl_multi_union_pw_aff_get_space(mupa));
        mupa = isl_multi_union_pw_aff_align_params(mupa,
                                isl_union_map_get_space(umap));
+       umap = intersect_explicit_domain(umap, mupa);
        data.mupa = mupa;
        data.order = order;
        data.res = isl_union_map_empty(isl_union_map_get_space(umap));
index a8be9ac..3f85604 100644 (file)
--- a/isl_val.c
+++ b/isl_val.c
@@ -1640,6 +1640,7 @@ isl_stat isl_val_check_match_domain_space(__isl_keep isl_val *v,
 #define NO_IDENTITY
 #define NO_FROM_BASE
 #define NO_MOVE_DIMS
+#include <isl_multi_no_explicit_domain.c>
 #include <isl_multi_templ.c>
 #include <isl_multi_dims.c>