1 #include <assert.h>
2 #include <stdlib.h>
3 
4 #include <functional>
5 #include <iostream>
6 #include <sstream>
7 #include <string>
8 #include <type_traits>
9 #include <utility>
10 #include <vector>
11 
12 #include <isl/cpp.h>
13 
14 /* A binary isl function that appears in the C++ bindings
15  * as a unary method in a class T, taking an extra argument
16  * of type A1 and returning an object of type R.
17  */
18 template <typename A1, typename R, typename T>
19 using binary_fn = R (T::*)(A1) const;
20 
21 /* A function for selecting an overload of a pointer to a unary C++ method
22  * based on the single argument type.
23  * The object type and the return type are meant to be deduced.
24  */
25 template <typename A1, typename R, typename T>
arg(const binary_fn<A1,R,T> & fn)26 static binary_fn<A1, R, T> const arg(const binary_fn<A1, R, T> &fn)
27 {
28 	return fn;
29 }
30 
31 /* A description of the inputs and the output of a binary operation.
32  */
33 struct binary {
34 	const char *arg1;
35 	const char *arg2;
36 	const char *res;
37 };
38 
39 /* A template function for checking whether two objects
40  * of the same (isl) type are (obviously) equal.
41  * The spelling depends on the isl type and
42  * in particular on whether an equality method is available or
43  * whether only obvious equality can be tested.
44  */
45 template <typename T, typename std::decay<decltype(
46 	std::declval<T>().is_equal(std::declval<T>()))>::type = true>
is_equal(const T & a,const T & b)47 static bool is_equal(const T &a, const T &b)
48 {
49 	return a.is_equal(b);
50 }
51 template <typename T, typename std::decay<decltype(
52 	std::declval<T>().plain_is_equal(std::declval<T>()))>::type = true>
is_equal(const T & a,const T & b)53 static bool is_equal(const T &a, const T &b)
54 {
55 	return a.plain_is_equal(b);
56 }
57 
58 /* A helper macro for throwing an isl::exception_invalid with message "msg".
59  */
60 #define THROW_INVALID(msg) \
61 	isl::exception::throw_error(isl_error_invalid, msg, __FILE__, __LINE__)
62 
63 /* Run a sequence of tests of method "fn" with stringification "name" and
64  * with inputs and output described by "test",
65  * throwing an exception when an unexpected result is produced.
66  */
67 template <typename R, typename T, typename A1>
test(isl::ctx ctx,R (T::* fn)(A1)const,const std::string & name,const std::vector<binary> & tests)68 static void test(isl::ctx ctx, R (T::*fn)(A1) const, const std::string &name,
69 	const std::vector<binary> &tests)
70 {
71 	for (const auto &test : tests) {
72 		T obj(ctx, test.arg1);
73 		A1 arg1(ctx, test.arg2);
74 		R expected(ctx, test.res);
75 		const auto &res = (obj.*fn)(arg1);
76 		std::ostringstream ss;
77 
78 		if (is_equal(expected, res))
79 			continue;
80 
81 		ss << name << "(" << test.arg1 << ", " << test.arg2 << ") =\n"
82 		   << res << "\n"
83 		   << "expecting:\n"
84 		   << test.res;
85 		THROW_INVALID(ss.str().c_str());
86 	}
87 }
88 
89 /* A helper macro that calls test with as implicit initial argument "ctx" and
90  * as extra argument a stringification of "FN".
91  */
92 #define C(FN, ...) test(ctx, FN, #FN, __VA_ARGS__)
93 
94 /* Perform some basic preimage tests.
95  */
test_preimage(isl::ctx ctx)96 static void test_preimage(isl::ctx ctx)
97 {
98 	C(arg<isl::multi_aff>(&isl::set::preimage), {
99 	{ "{ B[i,j] : 0 <= i < 10 and 0 <= j < 100 }",
100 	  "{ A[j,i] -> B[i,j] }",
101 	  "{ A[j,i] : 0 <= i < 10 and 0 <= j < 100 }" },
102 	{ "{ rat: B[i,j] : 0 <= i, j and 3 i + 5 j <= 100 }",
103 	  "{ A[a,b] -> B[a/2,b/6] }",
104 	  "{ rat: A[a,b] : 0 <= a, b and 9 a + 5 b <= 600 }" },
105 	{ "{ B[i,j] : 0 <= i, j and 3 i + 5 j <= 100 }",
106 	  "{ A[a,b] -> B[a/2,b/6] }",
107 	  "{ A[a,b] : 0 <= a, b and 9 a + 5 b <= 600 and "
108 		    "exists i,j : a = 2 i and b = 6 j }" },
109 	{ "[n] -> { S[i] : 0 <= i <= 100 }", "[n] -> { S[n] }",
110 	  "[n] -> { : 0 <= n <= 100 }" },
111 	{ "{ B[i] : 0 <= i < 100 and exists a : i = 4 a }",
112 	  "{ A[a] -> B[2a] }",
113 	  "{ A[a] : 0 <= a < 50 and exists b : a = 2 b }" },
114 	{ "{ B[i] : 0 <= i < 100 and exists a : i = 4 a }",
115 	  "{ A[a] -> B[([a/2])] }",
116 	  "{ A[a] : 0 <= a < 200 and exists b : [a/2] = 4 b }" },
117 	{ "{ B[i,j,k] : 0 <= i,j,k <= 100 }",
118 	  "{ A[a] -> B[a,a,a/3] }",
119 	  "{ A[a] : 0 <= a <= 100 and exists b : a = 3 b }" },
120 	{ "{ B[i,j] : j = [(i)/2] } ", "{ A[i,j] -> B[i/3,j] }",
121 	  "{ A[i,j] : j = [(i)/6] and exists a : i = 3 a }" },
122 	});
123 
124 	C(arg<isl::multi_aff>(&isl::union_map::preimage_domain), {
125 	{ "{ B[i,j] -> C[2i + 3j] : 0 <= i < 10 and 0 <= j < 100 }",
126 	  "{ A[j,i] -> B[i,j] }",
127 	  "{ A[j,i] -> C[2i + 3j] : 0 <= i < 10 and 0 <= j < 100 }" },
128 	{ "{ B[i] -> C[i]; D[i] -> E[i] }",
129 	  "{ A[i] -> B[i + 1] }",
130 	  "{ A[i] -> C[i + 1] }" },
131 	{ "{ B[i] -> C[i]; B[i] -> E[i] }",
132 	  "{ A[i] -> B[i + 1] }",
133 	  "{ A[i] -> C[i + 1]; A[i] -> E[i + 1] }" },
134 	{ "{ B[i] -> C[([i/2])] }",
135 	  "{ A[i] -> B[2i] }",
136 	  "{ A[i] -> C[i] }" },
137 	{ "{ B[i,j] -> C[([i/2]), ([(i+j)/3])] }",
138 	  "{ A[i] -> B[([i/5]), ([i/7])] }",
139 	  "{ A[i] -> C[([([i/5])/2]), ([(([i/5])+([i/7]))/3])] }" },
140 	{ "[N] -> { B[i] -> C[([N/2]), i, ([N/3])] }",
141 	  "[N] -> { A[] -> B[([N/5])] }",
142 	  "[N] -> { A[] -> C[([N/2]), ([N/5]), ([N/3])] }" },
143 	{ "{ B[i] -> C[i] : exists a : i = 5 a }",
144 	  "{ A[i] -> B[2i] }",
145 	  "{ A[i] -> C[2i] : exists a : 2i = 5 a }" },
146 	{ "{ B[i] -> C[i] : exists a : i = 2 a; "
147 	    "B[i] -> D[i] : exists a : i = 2 a + 1 }",
148 	  "{ A[i] -> B[2i] }",
149 	  "{ A[i] -> C[2i] }" },
150 	{ "{ A[i] -> B[i] }", "{ C[i] -> A[(i + floor(i/3))/2] }",
151 	  "{ C[i] -> B[j] : 2j = i + floor(i/3) }" },
152 	});
153 
154 	C(arg<isl::multi_aff>(&isl::union_map::preimage_range), {
155 	{ "[M] -> { A[a] -> B[a] }", "[M] -> { C[] -> B[floor(M/2)] }",
156 	  "[M] -> { A[floor(M/2)] -> C[] }" },
157 	});
158 }
159 
160 /* The list of tests to perform.
161  */
162 static std::vector<std::pair<const char *, void (*)(isl::ctx)>> tests =
163 {
164 	{ "preimage", &test_preimage },
165 };
166 
167 /* Perform some basic checks by means of the C++ bindings.
168  */
main(int argc,char ** argv)169 int main(int argc, char **argv)
170 {
171 	int ret = EXIT_SUCCESS;
172 	struct isl_ctx *ctx;
173 	struct isl_options *options;
174 
175 	options = isl_options_new_with_defaults();
176 	assert(options);
177 	argc = isl_options_parse(options, argc, argv, ISL_ARG_ALL);
178 	ctx = isl_ctx_alloc_with_options(&isl_options_args, options);
179 
180 	try {
181 		for (const auto &f : tests) {
182 			std::cout << f.first << "\n";
183 			f.second(ctx);
184 		}
185 	} catch (const isl::exception &e) {
186 		std::cerr << e.what() << "\n";
187 		ret = EXIT_FAILURE;
188 	}
189 
190 	isl_ctx_free(ctx);
191 	return ret;
192 }
193