1/*
2 * Copyright 2008-2009 Katholieke Universiteit Leuven
3 * Copyright 2010      INRIA Saclay
4 *
5 * Use of this software is governed by the MIT license
6 *
7 * Written by Sven Verdoolaege, K.U.Leuven, Departement
8 * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium
9 * and INRIA Saclay - Ile-de-France, Parc Club Orsay Universite,
10 * ZAC des vignes, 4 rue Jacques Monod, 91893 Orsay, France
11 */
12
13#include <stdlib.h>
14#include <string.h>
15#include <isl_space_private.h>
16#include <isl_id_private.h>
17#include <isl_reordering.h>
18
19isl_ctx *isl_space_get_ctx(__isl_keep isl_space *dim)
20{
21	return dim ? dim->ctx : NULL;
22}
23
24__isl_give isl_space *isl_space_alloc(isl_ctx *ctx,
25			unsigned nparam, unsigned n_in, unsigned n_out)
26{
27	isl_space *dim;
28
29	dim = isl_alloc_type(ctx, struct isl_space);
30	if (!dim)
31		return NULL;
32
33	dim->ctx = ctx;
34	isl_ctx_ref(ctx);
35	dim->ref = 1;
36	dim->nparam = nparam;
37	dim->n_in = n_in;
38	dim->n_out = n_out;
39
40	dim->tuple_id[0] = NULL;
41	dim->tuple_id[1] = NULL;
42
43	dim->nested[0] = NULL;
44	dim->nested[1] = NULL;
45
46	dim->n_id = 0;
47	dim->ids = NULL;
48
49	return dim;
50}
51
52/* Mark the space as being that of a set, by setting the domain tuple
53 * to isl_id_none.
54 */
55static __isl_give isl_space *mark_as_set(__isl_take isl_space *space)
56{
57	space = isl_space_cow(space);
58	if (!space)
59		return NULL;
60	space = isl_space_set_tuple_id(space, isl_dim_in, &isl_id_none);
61	return space;
62}
63
64/* Is the space that of a set?
65 */
66int isl_space_is_set(__isl_keep isl_space *space)
67{
68	if (!space)
69		return -1;
70	if (space->n_in != 0 || space->nested[0])
71		return 0;
72	if (space->tuple_id[0] != &isl_id_none)
73		return 0;
74	return 1;
75}
76
77/* Is the given space that of a map?
78 */
79int isl_space_is_map(__isl_keep isl_space *space)
80{
81	if (!space)
82		return -1;
83	return space->tuple_id[0] != &isl_id_none &&
84		space->tuple_id[1] != &isl_id_none;
85}
86
87__isl_give isl_space *isl_space_set_alloc(isl_ctx *ctx,
88			unsigned nparam, unsigned dim)
89{
90	isl_space *space;
91	space = isl_space_alloc(ctx, nparam, 0, dim);
92	space = mark_as_set(space);
93	return space;
94}
95
96/* Mark the space as being that of a parameter domain, by setting
97 * both tuples to isl_id_none.
98 */
99static __isl_give isl_space *mark_as_params(isl_space *space)
100{
101	if (!space)
102		return NULL;
103	space = isl_space_set_tuple_id(space, isl_dim_in, &isl_id_none);
104	space = isl_space_set_tuple_id(space, isl_dim_out, &isl_id_none);
105	return space;
106}
107
108/* Is the space that of a parameter domain?
109 */
110int isl_space_is_params(__isl_keep isl_space *space)
111{
112	if (!space)
113		return -1;
114	if (space->n_in != 0 || space->nested[0] ||
115	    space->n_out != 0 || space->nested[1])
116		return 0;
117	if (space->tuple_id[0] != &isl_id_none)
118		return 0;
119	if (space->tuple_id[1] != &isl_id_none)
120		return 0;
121	return 1;
122}
123
124/* Create a space for a parameter domain.
125 */
126__isl_give isl_space *isl_space_params_alloc(isl_ctx *ctx, unsigned nparam)
127{
128	isl_space *space;
129	space = isl_space_alloc(ctx, nparam, 0, 0);
130	space = mark_as_params(space);
131	return space;
132}
133
134static unsigned global_pos(__isl_keep isl_space *dim,
135				 enum isl_dim_type type, unsigned pos)
136{
137	struct isl_ctx *ctx = dim->ctx;
138
139	switch (type) {
140	case isl_dim_param:
141		isl_assert(ctx, pos < dim->nparam,
142			    return isl_space_dim(dim, isl_dim_all));
143		return pos;
144	case isl_dim_in:
145		isl_assert(ctx, pos < dim->n_in,
146			    return isl_space_dim(dim, isl_dim_all));
147		return pos + dim->nparam;
148	case isl_dim_out:
149		isl_assert(ctx, pos < dim->n_out,
150			    return isl_space_dim(dim, isl_dim_all));
151		return pos + dim->nparam + dim->n_in;
152	default:
153		isl_assert(ctx, 0, return isl_space_dim(dim, isl_dim_all));
154	}
155	return isl_space_dim(dim, isl_dim_all);
156}
157
158/* Extend length of ids array to the total number of dimensions.
159 */
160static __isl_give isl_space *extend_ids(__isl_take isl_space *dim)
161{
162	isl_id **ids;
163	int i;
164
165	if (isl_space_dim(dim, isl_dim_all) <= dim->n_id)
166		return dim;
167
168	if (!dim->ids) {
169		dim->ids = isl_calloc_array(dim->ctx,
170				isl_id *, isl_space_dim(dim, isl_dim_all));
171		if (!dim->ids)
172			goto error;
173	} else {
174		ids = isl_realloc_array(dim->ctx, dim->ids,
175				isl_id *, isl_space_dim(dim, isl_dim_all));
176		if (!ids)
177			goto error;
178		dim->ids = ids;
179		for (i = dim->n_id; i < isl_space_dim(dim, isl_dim_all); ++i)
180			dim->ids[i] = NULL;
181	}
182
183	dim->n_id = isl_space_dim(dim, isl_dim_all);
184
185	return dim;
186error:
187	isl_space_free(dim);
188	return NULL;
189}
190
191static __isl_give isl_space *set_id(__isl_take isl_space *dim,
192	enum isl_dim_type type, unsigned pos, __isl_take isl_id *id)
193{
194	dim = isl_space_cow(dim);
195
196	if (!dim)
197		goto error;
198
199	pos = global_pos(dim, type, pos);
200	if (pos == isl_space_dim(dim, isl_dim_all))
201		goto error;
202
203	if (pos >= dim->n_id) {
204		if (!id)
205			return dim;
206		dim = extend_ids(dim);
207		if (!dim)
208			goto error;
209	}
210
211	dim->ids[pos] = id;
212
213	return dim;
214error:
215	isl_id_free(id);
216	isl_space_free(dim);
217	return NULL;
218}
219
220static __isl_keep isl_id *get_id(__isl_keep isl_space *dim,
221				 enum isl_dim_type type, unsigned pos)
222{
223	if (!dim)
224		return NULL;
225
226	pos = global_pos(dim, type, pos);
227	if (pos == isl_space_dim(dim, isl_dim_all))
228		return NULL;
229	if (pos >= dim->n_id)
230		return NULL;
231	return dim->ids[pos];
232}
233
234static unsigned offset(__isl_keep isl_space *dim, enum isl_dim_type type)
235{
236	switch (type) {
237	case isl_dim_param:	return 0;
238	case isl_dim_in:	return dim->nparam;
239	case isl_dim_out:	return dim->nparam + dim->n_in;
240	default:		return 0;
241	}
242}
243
244static unsigned n(__isl_keep isl_space *dim, enum isl_dim_type type)
245{
246	switch (type) {
247	case isl_dim_param:	return dim->nparam;
248	case isl_dim_in:	return dim->n_in;
249	case isl_dim_out:	return dim->n_out;
250	case isl_dim_all:	return dim->nparam + dim->n_in + dim->n_out;
251	default:		return 0;
252	}
253}
254
255unsigned isl_space_dim(__isl_keep isl_space *dim, enum isl_dim_type type)
256{
257	if (!dim)
258		return 0;
259	return n(dim, type);
260}
261
262unsigned isl_space_offset(__isl_keep isl_space *dim, enum isl_dim_type type)
263{
264	if (!dim)
265		return 0;
266	return offset(dim, type);
267}
268
269static __isl_give isl_space *copy_ids(__isl_take isl_space *dst,
270	enum isl_dim_type dst_type, unsigned offset, __isl_keep isl_space *src,
271	enum isl_dim_type src_type)
272{
273	int i;
274	isl_id *id;
275
276	if (!dst)
277		return NULL;
278
279	for (i = 0; i < n(src, src_type); ++i) {
280		id = get_id(src, src_type, i);
281		if (!id)
282			continue;
283		dst = set_id(dst, dst_type, offset + i, isl_id_copy(id));
284		if (!dst)
285			return NULL;
286	}
287	return dst;
288}
289
290__isl_take isl_space *isl_space_dup(__isl_keep isl_space *dim)
291{
292	isl_space *dup;
293	if (!dim)
294		return NULL;
295	dup = isl_space_alloc(dim->ctx, dim->nparam, dim->n_in, dim->n_out);
296	if (!dup)
297		return NULL;
298	if (dim->tuple_id[0] &&
299	    !(dup->tuple_id[0] = isl_id_copy(dim->tuple_id[0])))
300		goto error;
301	if (dim->tuple_id[1] &&
302	    !(dup->tuple_id[1] = isl_id_copy(dim->tuple_id[1])))
303		goto error;
304	if (dim->nested[0] && !(dup->nested[0] = isl_space_copy(dim->nested[0])))
305		goto error;
306	if (dim->nested[1] && !(dup->nested[1] = isl_space_copy(dim->nested[1])))
307		goto error;
308	if (!dim->ids)
309		return dup;
310	dup = copy_ids(dup, isl_dim_param, 0, dim, isl_dim_param);
311	dup = copy_ids(dup, isl_dim_in, 0, dim, isl_dim_in);
312	dup = copy_ids(dup, isl_dim_out, 0, dim, isl_dim_out);
313	return dup;
314error:
315	isl_space_free(dup);
316	return NULL;
317}
318
319__isl_give isl_space *isl_space_cow(__isl_take isl_space *dim)
320{
321	if (!dim)
322		return NULL;
323
324	if (dim->ref == 1)
325		return dim;
326	dim->ref--;
327	return isl_space_dup(dim);
328}
329
330__isl_give isl_space *isl_space_copy(__isl_keep isl_space *dim)
331{
332	if (!dim)
333		return NULL;
334
335	dim->ref++;
336	return dim;
337}
338
339void *isl_space_free(__isl_take isl_space *dim)
340{
341	int i;
342
343	if (!dim)
344		return NULL;
345
346	if (--dim->ref > 0)
347		return NULL;
348
349	isl_id_free(dim->tuple_id[0]);
350	isl_id_free(dim->tuple_id[1]);
351
352	isl_space_free(dim->nested[0]);
353	isl_space_free(dim->nested[1]);
354
355	for (i = 0; i < dim->n_id; ++i)
356		isl_id_free(dim->ids[i]);
357	free(dim->ids);
358	isl_ctx_deref(dim->ctx);
359
360	free(dim);
361
362	return NULL;
363}
364
365/* Check if "s" is a valid dimension or tuple name.
366 * We currently only forbid names that look like a number.
367 *
368 * s is assumed to be non-NULL.
369 */
370static int name_ok(isl_ctx *ctx, const char *s)
371{
372	char *p;
373	long dummy;
374
375	dummy = strtol(s, &p, 0);
376	if (p != s)
377		isl_die(ctx, isl_error_invalid, "name looks like a number",
378			return 0);
379
380	return 1;
381}
382
383/* Is it possible for the given dimension type to have a tuple id?
384 */
385static int space_can_have_id(__isl_keep isl_space *space,
386	enum isl_dim_type type)
387{
388	if (!space)
389		return 0;
390	if (isl_space_is_params(space))
391		isl_die(space->ctx, isl_error_invalid,
392			"parameter spaces don't have tuple ids", return 0);
393	if (isl_space_is_set(space) && type != isl_dim_set)
394		isl_die(space->ctx, isl_error_invalid,
395			"set spaces can only have a set id", return 0);
396	if (type != isl_dim_in && type != isl_dim_out)
397		isl_die(space->ctx, isl_error_invalid,
398			"only input, output and set tuples can have ids",
399			return 0);
400
401	return 1;
402}
403
404/* Does the tuple have an id?
405 */
406int isl_space_has_tuple_id(__isl_keep isl_space *dim, enum isl_dim_type type)
407{
408	if (!space_can_have_id(dim, type))
409		return -1;
410	return dim->tuple_id[type - isl_dim_in] != NULL;
411}
412
413__isl_give isl_id *isl_space_get_tuple_id(__isl_keep isl_space *dim,
414	enum isl_dim_type type)
415{
416	int has_id;
417
418	if (!dim)
419		return NULL;
420	has_id = isl_space_has_tuple_id(dim, type);
421	if (has_id < 0)
422		return NULL;
423	if (!has_id)
424		isl_die(dim->ctx, isl_error_invalid,
425			"tuple has no id", return NULL);
426	return isl_id_copy(dim->tuple_id[type - isl_dim_in]);
427}
428
429__isl_give isl_space *isl_space_set_tuple_id(__isl_take isl_space *dim,
430	enum isl_dim_type type, __isl_take isl_id *id)
431{
432	dim = isl_space_cow(dim);
433	if (!dim || !id)
434		goto error;
435	if (type != isl_dim_in && type != isl_dim_out)
436		isl_die(dim->ctx, isl_error_invalid,
437			"only input, output and set tuples can have names",
438			goto error);
439
440	isl_id_free(dim->tuple_id[type - isl_dim_in]);
441	dim->tuple_id[type - isl_dim_in] = id;
442
443	return dim;
444error:
445	isl_id_free(id);
446	isl_space_free(dim);
447	return NULL;
448}
449
450__isl_give isl_space *isl_space_reset_tuple_id(__isl_take isl_space *dim,
451	enum isl_dim_type type)
452{
453	dim = isl_space_cow(dim);
454	if (!dim)
455		return NULL;
456	if (type != isl_dim_in && type != isl_dim_out)
457		isl_die(dim->ctx, isl_error_invalid,
458			"only input, output and set tuples can have names",
459			goto error);
460
461	isl_id_free(dim->tuple_id[type - isl_dim_in]);
462	dim->tuple_id[type - isl_dim_in] = NULL;
463
464	return dim;
465error:
466	isl_space_free(dim);
467	return NULL;
468}
469
470/* Set the id of the given dimension of "space" to "id".
471 * If the dimension already has an id, then it is replaced.
472 * If the dimension is a parameter, then we need to change it
473 * in the nested spaces (if any) as well.
474 */
475__isl_give isl_space *isl_space_set_dim_id(__isl_take isl_space *space,
476	enum isl_dim_type type, unsigned pos, __isl_take isl_id *id)
477{
478	space = isl_space_cow(space);
479	if (!space || !id)
480		goto error;
481
482	if (type == isl_dim_param) {
483		int i;
484
485		for (i = 0; i < 2; ++i) {
486			if (!space->nested[i])
487				continue;
488			space->nested[i] =
489				isl_space_set_dim_id(space->nested[i],
490						type, pos, isl_id_copy(id));
491			if (!space->nested[i])
492				goto error;
493		}
494	}
495
496	isl_id_free(get_id(space, type, pos));
497	return set_id(space, type, pos, id);
498error:
499	isl_id_free(id);
500	isl_space_free(space);
501	return NULL;
502}
503
504/* Reset the id of the given dimension of "space".
505 * If the dimension already has an id, then it is removed.
506 * If the dimension is a parameter, then we need to reset it
507 * in the nested spaces (if any) as well.
508 */
509__isl_give isl_space *isl_space_reset_dim_id(__isl_take isl_space *space,
510	enum isl_dim_type type, unsigned pos)
511{
512	space = isl_space_cow(space);
513	if (!space)
514		goto error;
515
516	if (type == isl_dim_param) {
517		int i;
518
519		for (i = 0; i < 2; ++i) {
520			if (!space->nested[i])
521				continue;
522			space->nested[i] =
523				isl_space_reset_dim_id(space->nested[i],
524							type, pos);
525			if (!space->nested[i])
526				goto error;
527		}
528	}
529
530	isl_id_free(get_id(space, type, pos));
531	return set_id(space, type, pos, NULL);
532error:
533	isl_space_free(space);
534	return NULL;
535}
536
537int isl_space_has_dim_id(__isl_keep isl_space *dim,
538	enum isl_dim_type type, unsigned pos)
539{
540	if (!dim)
541		return -1;
542	return get_id(dim, type, pos) != NULL;
543}
544
545__isl_give isl_id *isl_space_get_dim_id(__isl_keep isl_space *dim,
546	enum isl_dim_type type, unsigned pos)
547{
548	if (!dim)
549		return NULL;
550	if (!get_id(dim, type, pos))
551		isl_die(dim->ctx, isl_error_invalid,
552			"dim has no id", return NULL);
553	return isl_id_copy(get_id(dim, type, pos));
554}
555
556__isl_give isl_space *isl_space_set_tuple_name(__isl_take isl_space *dim,
557	enum isl_dim_type type, const char *s)
558{
559	isl_id *id;
560
561	if (!dim)
562		return NULL;
563
564	if (!s)
565		return isl_space_reset_tuple_id(dim, type);
566
567	if (!name_ok(dim->ctx, s))
568		goto error;
569
570	id = isl_id_alloc(dim->ctx, s, NULL);
571	return isl_space_set_tuple_id(dim, type, id);
572error:
573	isl_space_free(dim);
574	return NULL;
575}
576
577/* Does the tuple have a name?
578 */
579int isl_space_has_tuple_name(__isl_keep isl_space *space,
580	enum isl_dim_type type)
581{
582	isl_id *id;
583
584	if (!space_can_have_id(space, type))
585		return -1;
586	id = space->tuple_id[type - isl_dim_in];
587	return id && id->name;
588}
589
590const char *isl_space_get_tuple_name(__isl_keep isl_space *dim,
591	 enum isl_dim_type type)
592{
593	isl_id *id;
594	if (!dim)
595		return NULL;
596	if (type != isl_dim_in && type != isl_dim_out)
597		return NULL;
598	id = dim->tuple_id[type - isl_dim_in];
599	return id ? id->name : NULL;
600}
601
602__isl_give isl_space *isl_space_set_dim_name(__isl_take isl_space *dim,
603				 enum isl_dim_type type, unsigned pos,
604				 const char *s)
605{
606	isl_id *id;
607
608	if (!dim)
609		return NULL;
610	if (!s)
611		return isl_space_reset_dim_id(dim, type, pos);
612	if (!name_ok(dim->ctx, s))
613		goto error;
614	id = isl_id_alloc(dim->ctx, s, NULL);
615	return isl_space_set_dim_id(dim, type, pos, id);
616error:
617	isl_space_free(dim);
618	return NULL;
619}
620
621/* Does the given dimension have a name?
622 */
623int isl_space_has_dim_name(__isl_keep isl_space *space,
624	enum isl_dim_type type, unsigned pos)
625{
626	isl_id *id;
627
628	if (!space)
629		return -1;
630	id = get_id(space, type, pos);
631	return id && id->name;
632}
633
634__isl_keep const char *isl_space_get_dim_name(__isl_keep isl_space *dim,
635				 enum isl_dim_type type, unsigned pos)
636{
637	isl_id *id = get_id(dim, type, pos);
638	return id ? id->name : NULL;
639}
640
641int isl_space_find_dim_by_id(__isl_keep isl_space *dim, enum isl_dim_type type,
642	__isl_keep isl_id *id)
643{
644	int i;
645	int offset;
646	int n;
647
648	if (!dim || !id)
649		return -1;
650
651	offset = isl_space_offset(dim, type);
652	n = isl_space_dim(dim, type);
653	for (i = 0; i < n && offset + i < dim->n_id; ++i)
654		if (dim->ids[offset + i] == id)
655			return i;
656
657	return -1;
658}
659
660int isl_space_find_dim_by_name(__isl_keep isl_space *space,
661	enum isl_dim_type type, const char *name)
662{
663	int i;
664	int offset;
665	int n;
666
667	if (!space || !name)
668		return -1;
669
670	offset = isl_space_offset(space, type);
671	n = isl_space_dim(space, type);
672	for (i = 0; i < n && offset + i < space->n_id; ++i)
673		if (space->ids[offset + i]->name &&
674		    !strcmp(space->ids[offset + i]->name, name))
675			return i;
676
677	return -1;
678}
679
680static __isl_keep isl_id *tuple_id(__isl_keep isl_space *dim,
681	enum isl_dim_type type)
682{
683	if (!dim)
684		return NULL;
685	if (type == isl_dim_in)
686		return dim->tuple_id[0];
687	if (type == isl_dim_out)
688		return dim->tuple_id[1];
689	return NULL;
690}
691
692static __isl_keep isl_space *nested(__isl_keep isl_space *dim,
693	enum isl_dim_type type)
694{
695	if (!dim)
696		return NULL;
697	if (type == isl_dim_in)
698		return dim->nested[0];
699	if (type == isl_dim_out)
700		return dim->nested[1];
701	return NULL;
702}
703
704int isl_space_tuple_match(__isl_keep isl_space *dim1, enum isl_dim_type dim1_type,
705			__isl_keep isl_space *dim2, enum isl_dim_type dim2_type)
706{
707	isl_id *id1, *id2;
708	isl_space *nested1, *nested2;
709
710	if (!dim1 || !dim2)
711		return -1;
712
713	if (dim1 == dim2 && dim1_type == dim2_type)
714		return 1;
715
716	if (n(dim1, dim1_type) != n(dim2, dim2_type))
717		return 0;
718	id1 = tuple_id(dim1, dim1_type);
719	id2 = tuple_id(dim2, dim2_type);
720	if (!id1 ^ !id2)
721		return 0;
722	if (id1 && id1 != id2)
723		return 0;
724	nested1 = nested(dim1, dim1_type);
725	nested2 = nested(dim2, dim2_type);
726	if (!nested1 ^ !nested2)
727		return 0;
728	if (nested1 && !isl_space_is_equal(nested1, nested2))
729		return 0;
730	return 1;
731}
732
733static int match(__isl_keep isl_space *dim1, enum isl_dim_type dim1_type,
734	__isl_keep isl_space *dim2, enum isl_dim_type dim2_type)
735{
736	int i;
737
738	if (dim1 == dim2 && dim1_type == dim2_type)
739		return 1;
740
741	if (!isl_space_tuple_match(dim1, dim1_type, dim2, dim2_type))
742		return 0;
743
744	if (!dim1->ids && !dim2->ids)
745		return 1;
746
747	for (i = 0; i < n(dim1, dim1_type); ++i) {
748		if (get_id(dim1, dim1_type, i) != get_id(dim2, dim2_type, i))
749			return 0;
750	}
751	return 1;
752}
753
754int isl_space_match(__isl_keep isl_space *dim1, enum isl_dim_type dim1_type,
755	__isl_keep isl_space *dim2, enum isl_dim_type dim2_type)
756{
757	if (!dim1 || !dim2)
758		return -1;
759
760	return match(dim1, dim1_type, dim2, dim2_type);
761}
762
763static void get_ids(__isl_keep isl_space *dim, enum isl_dim_type type,
764	unsigned first, unsigned n, __isl_keep isl_id **ids)
765{
766	int i;
767
768	for (i = 0; i < n ; ++i)
769		ids[i] = get_id(dim, type, first + i);
770}
771
772__isl_give isl_space *isl_space_extend(__isl_take isl_space *dim,
773			unsigned nparam, unsigned n_in, unsigned n_out)
774{
775	isl_id **ids = NULL;
776
777	if (!dim)
778		return NULL;
779	if (dim->nparam == nparam && dim->n_in == n_in && dim->n_out == n_out)
780		return dim;
781
782	isl_assert(dim->ctx, dim->nparam <= nparam, goto error);
783	isl_assert(dim->ctx, dim->n_in <= n_in, goto error);
784	isl_assert(dim->ctx, dim->n_out <= n_out, goto error);
785
786	dim = isl_space_cow(dim);
787	if (!dim)
788		goto error;
789
790	if (dim->ids) {
791		ids = isl_calloc_array(dim->ctx, isl_id *,
792					 nparam + n_in + n_out);
793		if (!ids)
794			goto error;
795		get_ids(dim, isl_dim_param, 0, dim->nparam, ids);
796		get_ids(dim, isl_dim_in, 0, dim->n_in, ids + nparam);
797		get_ids(dim, isl_dim_out, 0, dim->n_out, ids + nparam + n_in);
798		free(dim->ids);
799		dim->ids = ids;
800		dim->n_id = nparam + n_in + n_out;
801	}
802	dim->nparam = nparam;
803	dim->n_in = n_in;
804	dim->n_out = n_out;
805
806	return dim;
807error:
808	free(ids);
809	isl_space_free(dim);
810	return NULL;
811}
812
813__isl_give isl_space *isl_space_add_dims(__isl_take isl_space *dim,
814	enum isl_dim_type type, unsigned n)
815{
816	if (!dim)
817		return NULL;
818	dim = isl_space_reset(dim, type);
819	switch (type) {
820	case isl_dim_param:
821		dim = isl_space_extend(dim,
822					dim->nparam + n, dim->n_in, dim->n_out);
823		if (dim && dim->nested[0] &&
824		    !(dim->nested[0] = isl_space_add_dims(dim->nested[0],
825						    isl_dim_param, n)))
826			goto error;
827		if (dim && dim->nested[1] &&
828		    !(dim->nested[1] = isl_space_add_dims(dim->nested[1],
829						    isl_dim_param, n)))
830			goto error;
831		return dim;
832	case isl_dim_in:
833		return isl_space_extend(dim,
834					dim->nparam, dim->n_in + n, dim->n_out);
835	case isl_dim_out:
836		return isl_space_extend(dim,
837					dim->nparam, dim->n_in, dim->n_out + n);
838	default:
839		isl_die(dim->ctx, isl_error_invalid,
840			"cannot add dimensions of specified type", goto error);
841	}
842error:
843	isl_space_free(dim);
844	return NULL;
845}
846
847static int valid_dim_type(enum isl_dim_type type)
848{
849	switch (type) {
850	case isl_dim_param:
851	case isl_dim_in:
852	case isl_dim_out:
853		return 1;
854	default:
855		return 0;
856	}
857}
858
859/* Insert "n" dimensions of type "type" at position "pos".
860 * If we are inserting parameters, then they are also inserted in
861 * any nested spaces.
862 */
863__isl_give isl_space *isl_space_insert_dims(__isl_take isl_space *dim,
864	enum isl_dim_type type, unsigned pos, unsigned n)
865{
866	isl_id **ids = NULL;
867
868	if (!dim)
869		return NULL;
870	if (n == 0)
871		return isl_space_reset(dim, type);
872
873	if (!valid_dim_type(type))
874		isl_die(dim->ctx, isl_error_invalid,
875			"cannot insert dimensions of specified type",
876			goto error);
877
878	isl_assert(dim->ctx, pos <= isl_space_dim(dim, type), goto error);
879
880	dim = isl_space_cow(dim);
881	if (!dim)
882		return NULL;
883
884	if (dim->ids) {
885		enum isl_dim_type t, o = isl_dim_param;
886		int off;
887		int s[3];
888		ids = isl_calloc_array(dim->ctx, isl_id *,
889				     dim->nparam + dim->n_in + dim->n_out + n);
890		if (!ids)
891			goto error;
892		off = 0;
893		s[isl_dim_param - o] = dim->nparam;
894		s[isl_dim_in - o] = dim->n_in;
895		s[isl_dim_out - o] = dim->n_out;
896		for (t = isl_dim_param; t <= isl_dim_out; ++t) {
897			if (t != type) {
898				get_ids(dim, t, 0, s[t - o], ids + off);
899				off += s[t - o];
900			} else {
901				get_ids(dim, t, 0, pos, ids + off);
902				off += pos + n;
903				get_ids(dim, t, pos, s[t - o] - pos, ids + off);
904				off += s[t - o] - pos;
905			}
906		}
907		free(dim->ids);
908		dim->ids = ids;
909		dim->n_id = dim->nparam + dim->n_in + dim->n_out + n;
910	}
911	switch (type) {
912	case isl_dim_param:	dim->nparam += n; break;
913	case isl_dim_in:	dim->n_in += n; break;
914	case isl_dim_out:	dim->n_out += n; break;
915	default:		;
916	}
917	dim = isl_space_reset(dim, type);
918
919	if (type == isl_dim_param) {
920		if (dim && dim->nested[0] &&
921		    !(dim->nested[0] = isl_space_insert_dims(dim->nested[0],
922						    isl_dim_param, pos, n)))
923			goto error;
924		if (dim && dim->nested[1] &&
925		    !(dim->nested[1] = isl_space_insert_dims(dim->nested[1],
926						    isl_dim_param, pos, n)))
927			goto error;
928	}
929
930	return dim;
931error:
932	isl_space_free(dim);
933	return NULL;
934}
935
936__isl_give isl_space *isl_space_move_dims(__isl_take isl_space *dim,
937	enum isl_dim_type dst_type, unsigned dst_pos,
938	enum isl_dim_type src_type, unsigned src_pos, unsigned n)
939{
940	int i;
941
942	if (!dim)
943		return NULL;
944	if (n == 0)
945		return dim;
946
947	isl_assert(dim->ctx, src_pos + n <= isl_space_dim(dim, src_type),
948		goto error);
949
950	if (dst_type == src_type && dst_pos == src_pos)
951		return dim;
952
953	isl_assert(dim->ctx, dst_type != src_type, goto error);
954
955	dim = isl_space_reset(dim, src_type);
956	dim = isl_space_reset(dim, dst_type);
957
958	dim = isl_space_cow(dim);
959	if (!dim)
960		return NULL;
961
962	if (dim->ids) {
963		isl_id **ids;
964		enum isl_dim_type t, o = isl_dim_param;
965		int off;
966		int s[3];
967		ids = isl_calloc_array(dim->ctx, isl_id *,
968					 dim->nparam + dim->n_in + dim->n_out);
969		if (!ids)
970			goto error;
971		off = 0;
972		s[isl_dim_param - o] = dim->nparam;
973		s[isl_dim_in - o] = dim->n_in;
974		s[isl_dim_out - o] = dim->n_out;
975		for (t = isl_dim_param; t <= isl_dim_out; ++t) {
976			if (t == dst_type) {
977				get_ids(dim, t, 0, dst_pos, ids + off);
978				off += dst_pos;
979				get_ids(dim, src_type, src_pos, n, ids + off);
980				off += n;
981				get_ids(dim, t, dst_pos, s[t - o] - dst_pos,
982						ids + off);
983				off += s[t - o] - dst_pos;
984			} else if (t == src_type) {
985				get_ids(dim, t, 0, src_pos, ids + off);
986				off += src_pos;
987				get_ids(dim, t, src_pos + n,
988					    s[t - o] - src_pos - n, ids + off);
989				off += s[t - o] - src_pos - n;
990			} else {
991				get_ids(dim, t, 0, s[t - o], ids + off);
992				off += s[t - o];
993			}
994		}
995		free(dim->ids);
996		dim->ids = ids;
997		dim->n_id = dim->nparam + dim->n_in + dim->n_out;
998	}
999
1000	switch (dst_type) {
1001	case isl_dim_param:	dim->nparam += n; break;
1002	case isl_dim_in:	dim->n_in += n; break;
1003	case isl_dim_out:	dim->n_out += n; break;
1004	default:		;
1005	}
1006
1007	switch (src_type) {
1008	case isl_dim_param:	dim->nparam -= n; break;
1009	case isl_dim_in:	dim->n_in -= n; break;
1010	case isl_dim_out:	dim->n_out -= n; break;
1011	default:		;
1012	}
1013
1014	if (dst_type != isl_dim_param && src_type != isl_dim_param)
1015		return dim;
1016
1017	for (i = 0; i < 2; ++i) {
1018		if (!dim->nested[i])
1019			continue;
1020		dim->nested[i] = isl_space_replace(dim->nested[i],
1021						 isl_dim_param, dim);
1022		if (!dim->nested[i])
1023			goto error;
1024	}
1025
1026	return dim;
1027error:
1028	isl_space_free(dim);
1029	return NULL;
1030}
1031
1032__isl_give isl_space *isl_space_join(__isl_take isl_space *left,
1033	__isl_take isl_space *right)
1034{
1035	isl_space *dim;
1036
1037	if (!left || !right)
1038		goto error;
1039
1040	isl_assert(left->ctx, match(left, isl_dim_param, right, isl_dim_param),
1041			goto error);
1042	isl_assert(left->ctx,
1043		isl_space_tuple_match(left, isl_dim_out, right, isl_dim_in),
1044		goto error);
1045
1046	dim = isl_space_alloc(left->ctx, left->nparam, left->n_in, right->n_out);
1047	if (!dim)
1048		goto error;
1049
1050	dim = copy_ids(dim, isl_dim_param, 0, left, isl_dim_param);
1051	dim = copy_ids(dim, isl_dim_in, 0, left, isl_dim_in);
1052	dim = copy_ids(dim, isl_dim_out, 0, right, isl_dim_out);
1053
1054	if (dim && left->tuple_id[0] &&
1055	    !(dim->tuple_id[0] = isl_id_copy(left->tuple_id[0])))
1056		goto error;
1057	if (dim && right->tuple_id[1] &&
1058	    !(dim->tuple_id[1] = isl_id_copy(right->tuple_id[1])))
1059		goto error;
1060	if (dim && left->nested[0] &&
1061	    !(dim->nested[0] = isl_space_copy(left->nested[0])))
1062		goto error;
1063	if (dim && right->nested[1] &&
1064	    !(dim->nested[1] = isl_space_copy(right->nested[1])))
1065		goto error;
1066
1067	isl_space_free(left);
1068	isl_space_free(right);
1069
1070	return dim;
1071error:
1072	isl_space_free(left);
1073	isl_space_free(right);
1074	return NULL;
1075}
1076
1077__isl_give isl_space *isl_space_product(__isl_take isl_space *left,
1078	__isl_take isl_space *right)
1079{
1080	isl_space *dom1, *dom2, *nest1, *nest2;
1081
1082	if (!left || !right)
1083		goto error;
1084
1085	isl_assert(left->ctx, match(left, isl_dim_param, right, isl_dim_param),
1086			goto error);
1087
1088	dom1 = isl_space_domain(isl_space_copy(left));
1089	dom2 = isl_space_domain(isl_space_copy(right));
1090	nest1 = isl_space_wrap(isl_space_join(isl_space_reverse(dom1), dom2));
1091
1092	dom1 = isl_space_range(left);
1093	dom2 = isl_space_range(right);
1094	nest2 = isl_space_wrap(isl_space_join(isl_space_reverse(dom1), dom2));
1095
1096	return isl_space_join(isl_space_reverse(nest1), nest2);
1097error:
1098	isl_space_free(left);
1099	isl_space_free(right);
1100	return NULL;
1101}
1102
1103/* Given two spaces { A -> C } and { B -> C }, construct the space
1104 * { [A -> B] -> C }
1105 */
1106__isl_give isl_space *isl_space_domain_product(__isl_take isl_space *left,
1107	__isl_take isl_space *right)
1108{
1109	isl_space *ran, *dom1, *dom2, *nest;
1110
1111	if (!left || !right)
1112		goto error;
1113
1114	if (!match(left, isl_dim_param, right, isl_dim_param))
1115		isl_die(left->ctx, isl_error_invalid,
1116			"parameters need to match", goto error);
1117	if (!isl_space_tuple_match(left, isl_dim_out, right, isl_dim_out))
1118		isl_die(left->ctx, isl_error_invalid,
1119			"ranges need to match", goto error);
1120
1121	ran = isl_space_range(isl_space_copy(left));
1122
1123	dom1 = isl_space_domain(left);
1124	dom2 = isl_space_domain(right);
1125	nest = isl_space_wrap(isl_space_join(isl_space_reverse(dom1), dom2));
1126
1127	return isl_space_join(isl_space_reverse(nest), ran);
1128error:
1129	isl_space_free(left);
1130	isl_space_free(right);
1131	return NULL;
1132}
1133
1134__isl_give isl_space *isl_space_range_product(__isl_take isl_space *left,
1135	__isl_take isl_space *right)
1136{
1137	isl_space *dom, *ran1, *ran2, *nest;
1138
1139	if (!left || !right)
1140		goto error;
1141
1142	isl_assert(left->ctx, match(left, isl_dim_param, right, isl_dim_param),
1143			goto error);
1144	if (!isl_space_tuple_match(left, isl_dim_in, right, isl_dim_in))
1145		isl_die(left->ctx, isl_error_invalid,
1146			"domains need to match", goto error);
1147
1148	dom = isl_space_domain(isl_space_copy(left));
1149
1150	ran1 = isl_space_range(left);
1151	ran2 = isl_space_range(right);
1152	nest = isl_space_wrap(isl_space_join(isl_space_reverse(ran1), ran2));
1153
1154	return isl_space_join(isl_space_reverse(dom), nest);
1155error:
1156	isl_space_free(left);
1157	isl_space_free(right);
1158	return NULL;
1159}
1160
1161__isl_give isl_space *isl_space_map_from_set(__isl_take isl_space *dim)
1162{
1163	isl_ctx *ctx;
1164	isl_id **ids = NULL;
1165
1166	if (!dim)
1167		return NULL;
1168	ctx = isl_space_get_ctx(dim);
1169	if (!isl_space_is_set(dim))
1170		isl_die(ctx, isl_error_invalid, "not a set space", goto error);
1171	dim = isl_space_cow(dim);
1172	if (!dim)
1173		return NULL;
1174	if (dim->ids) {
1175		ids = isl_calloc_array(dim->ctx, isl_id *,
1176					dim->nparam + dim->n_out + dim->n_out);
1177		if (!ids)
1178			goto error;
1179		get_ids(dim, isl_dim_param, 0, dim->nparam, ids);
1180		get_ids(dim, isl_dim_out, 0, dim->n_out, ids + dim->nparam);
1181	}
1182	dim->n_in = dim->n_out;
1183	if (ids) {
1184		free(dim->ids);
1185		dim->ids = ids;
1186		dim->n_id = dim->nparam + dim->n_out + dim->n_out;
1187		dim = copy_ids(dim, isl_dim_out, 0, dim, isl_dim_in);
1188	}
1189	isl_id_free(dim->tuple_id[0]);
1190	dim->tuple_id[0] = isl_id_copy(dim->tuple_id[1]);
1191	isl_space_free(dim->nested[0]);
1192	dim->nested[0] = isl_space_copy(dim->nested[1]);
1193	return dim;
1194error:
1195	isl_space_free(dim);
1196	return NULL;
1197}
1198
1199__isl_give isl_space *isl_space_map_from_domain_and_range(
1200	__isl_take isl_space *domain, __isl_take isl_space *range)
1201{
1202	if (!domain || !range)
1203		goto error;
1204	if (!isl_space_is_set(domain))
1205		isl_die(isl_space_get_ctx(domain), isl_error_invalid,
1206			"domain is not a set space", goto error);
1207	if (!isl_space_is_set(range))
1208		isl_die(isl_space_get_ctx(range), isl_error_invalid,
1209			"range is not a set space", goto error);
1210	return isl_space_join(isl_space_reverse(domain), range);
1211error:
1212	isl_space_free(domain);
1213	isl_space_free(range);
1214	return NULL;
1215}
1216
1217static __isl_give isl_space *set_ids(__isl_take isl_space *dim,
1218	enum isl_dim_type type,
1219	unsigned first, unsigned n, __isl_take isl_id **ids)
1220{
1221	int i;
1222
1223	for (i = 0; i < n ; ++i)
1224		dim = set_id(dim, type, first + i, ids[i]);
1225
1226	return dim;
1227}
1228
1229__isl_give isl_space *isl_space_reverse(__isl_take isl_space *dim)
1230{
1231	unsigned t;
1232	isl_space *nested;
1233	isl_id **ids = NULL;
1234	isl_id *id;
1235
1236	if (!dim)
1237		return NULL;
1238	if (match(dim, isl_dim_in, dim, isl_dim_out))
1239		return dim;
1240
1241	dim = isl_space_cow(dim);
1242	if (!dim)
1243		return NULL;
1244
1245	id = dim->tuple_id[0];
1246	dim->tuple_id[0] = dim->tuple_id[1];
1247	dim->tuple_id[1] = id;
1248
1249	nested = dim->nested[0];
1250	dim->nested[0] = dim->nested[1];
1251	dim->nested[1] = nested;
1252
1253	if (dim->ids) {
1254		int n_id = dim->n_in + dim->n_out;
1255		ids = isl_alloc_array(dim->ctx, isl_id *, n_id);
1256		if (n_id && !ids)
1257			goto error;
1258		get_ids(dim, isl_dim_in, 0, dim->n_in, ids);
1259		get_ids(dim, isl_dim_out, 0, dim->n_out, ids + dim->n_in);
1260	}
1261
1262	t = dim->n_in;
1263	dim->n_in = dim->n_out;
1264	dim->n_out = t;
1265
1266	if (dim->ids) {
1267		dim = set_ids(dim, isl_dim_out, 0, dim->n_out, ids);
1268		dim = set_ids(dim, isl_dim_in, 0, dim->n_in, ids + dim->n_out);
1269		free(ids);
1270	}
1271
1272	return dim;
1273error:
1274	free(ids);
1275	isl_space_free(dim);
1276	return NULL;
1277}
1278
1279__isl_give isl_space *isl_space_drop_dims(__isl_take isl_space *dim,
1280	enum isl_dim_type type, unsigned first, unsigned num)
1281{
1282	int i;
1283
1284	if (!dim)
1285		return NULL;
1286
1287	if (num == 0)
1288		return isl_space_reset(dim, type);
1289
1290	if (!valid_dim_type(type))
1291		isl_die(dim->ctx, isl_error_invalid,
1292			"cannot drop dimensions of specified type", goto error);
1293
1294	if (first + num > n(dim, type) || first + num < first)
1295		isl_die(isl_space_get_ctx(dim), isl_error_invalid,
1296			"index out of bounds", return isl_space_free(dim));
1297	dim = isl_space_cow(dim);
1298	if (!dim)
1299		goto error;
1300	if (dim->ids) {
1301		dim = extend_ids(dim);
1302		if (!dim)
1303			goto error;
1304		for (i = 0; i < num; ++i)
1305			isl_id_free(get_id(dim, type, first + i));
1306		for (i = first+num; i < n(dim, type); ++i)
1307			set_id(dim, type, i - num, get_id(dim, type, i));
1308		switch (type) {
1309		case isl_dim_param:
1310			get_ids(dim, isl_dim_in, 0, dim->n_in,
1311				dim->ids + offset(dim, isl_dim_in) - num);
1312		case isl_dim_in:
1313			get_ids(dim, isl_dim_out, 0, dim->n_out,
1314				dim->ids + offset(dim, isl_dim_out) - num);
1315		default:
1316			;
1317		}
1318		dim->n_id -= num;
1319	}
1320	switch (type) {
1321	case isl_dim_param:	dim->nparam -= num; break;
1322	case isl_dim_in:	dim->n_in -= num; break;
1323	case isl_dim_out:	dim->n_out -= num; break;
1324	default:		;
1325	}
1326	dim = isl_space_reset(dim, type);
1327	if (type == isl_dim_param) {
1328		if (dim && dim->nested[0] &&
1329		    !(dim->nested[0] = isl_space_drop_dims(dim->nested[0],
1330						    isl_dim_param, first, num)))
1331			goto error;
1332		if (dim && dim->nested[1] &&
1333		    !(dim->nested[1] = isl_space_drop_dims(dim->nested[1],
1334						    isl_dim_param, first, num)))
1335			goto error;
1336	}
1337	return dim;
1338error:
1339	isl_space_free(dim);
1340	return NULL;
1341}
1342
1343__isl_give isl_space *isl_space_drop_inputs(__isl_take isl_space *dim,
1344		unsigned first, unsigned n)
1345{
1346	if (!dim)
1347		return NULL;
1348	return isl_space_drop_dims(dim, isl_dim_in, first, n);
1349}
1350
1351__isl_give isl_space *isl_space_drop_outputs(__isl_take isl_space *dim,
1352		unsigned first, unsigned n)
1353{
1354	if (!dim)
1355		return NULL;
1356	return isl_space_drop_dims(dim, isl_dim_out, first, n);
1357}
1358
1359__isl_give isl_space *isl_space_domain(__isl_take isl_space *dim)
1360{
1361	if (!dim)
1362		return NULL;
1363	dim = isl_space_drop_outputs(dim, 0, dim->n_out);
1364	dim = isl_space_reverse(dim);
1365	dim = mark_as_set(dim);
1366	return dim;
1367}
1368
1369__isl_give isl_space *isl_space_from_domain(__isl_take isl_space *dim)
1370{
1371	if (!dim)
1372		return NULL;
1373	if (!isl_space_is_set(dim))
1374		isl_die(isl_space_get_ctx(dim), isl_error_invalid,
1375			"not a set space", goto error);
1376	dim = isl_space_reverse(dim);
1377	dim = isl_space_reset(dim, isl_dim_out);
1378	return dim;
1379error:
1380	isl_space_free(dim);
1381	return NULL;
1382}
1383
1384__isl_give isl_space *isl_space_range(__isl_take isl_space *dim)
1385{
1386	if (!dim)
1387		return NULL;
1388	dim = isl_space_drop_inputs(dim, 0, dim->n_in);
1389	dim = mark_as_set(dim);
1390	return dim;
1391}
1392
1393__isl_give isl_space *isl_space_from_range(__isl_take isl_space *dim)
1394{
1395	if (!dim)
1396		return NULL;
1397	if (!isl_space_is_set(dim))
1398		isl_die(isl_space_get_ctx(dim), isl_error_invalid,
1399			"not a set space", goto error);
1400	return isl_space_reset(dim, isl_dim_in);
1401error:
1402	isl_space_free(dim);
1403	return NULL;
1404}
1405
1406__isl_give isl_space *isl_space_params(__isl_take isl_space *space)
1407{
1408	if (isl_space_is_params(space))
1409		return space;
1410	space = isl_space_drop_dims(space,
1411			    isl_dim_in, 0, isl_space_dim(space, isl_dim_in));
1412	space = isl_space_drop_dims(space,
1413			    isl_dim_out, 0, isl_space_dim(space, isl_dim_out));
1414	space = mark_as_params(space);
1415	return space;
1416}
1417
1418__isl_give isl_space *isl_space_set_from_params(__isl_take isl_space *space)
1419{
1420	if (!space)
1421		return NULL;
1422	if (!isl_space_is_params(space))
1423		isl_die(isl_space_get_ctx(space), isl_error_invalid,
1424			"not a parameter space", goto error);
1425	return isl_space_reset(space, isl_dim_set);
1426error:
1427	isl_space_free(space);
1428	return NULL;
1429}
1430
1431__isl_give isl_space *isl_space_as_set_space(__isl_take isl_space *dim)
1432{
1433	dim = isl_space_cow(dim);
1434	if (!dim)
1435		return NULL;
1436
1437	dim->n_out += dim->n_in;
1438	dim->n_in = 0;
1439	dim = isl_space_reset(dim, isl_dim_in);
1440	dim = isl_space_reset(dim, isl_dim_out);
1441
1442	return dim;
1443}
1444
1445__isl_give isl_space *isl_space_underlying(__isl_take isl_space *dim,
1446	unsigned n_div)
1447{
1448	int i;
1449
1450	if (!dim)
1451		return NULL;
1452	if (n_div == 0 &&
1453	    dim->nparam == 0 && dim->n_in == 0 && dim->n_id == 0)
1454		return isl_space_reset(isl_space_reset(dim, isl_dim_in), isl_dim_out);
1455	dim = isl_space_cow(dim);
1456	if (!dim)
1457		return NULL;
1458	dim->n_out += dim->nparam + dim->n_in + n_div;
1459	dim->nparam = 0;
1460	dim->n_in = 0;
1461
1462	for (i = 0; i < dim->n_id; ++i)
1463		isl_id_free(get_id(dim, isl_dim_out, i));
1464	dim->n_id = 0;
1465	dim = isl_space_reset(dim, isl_dim_in);
1466	dim = isl_space_reset(dim, isl_dim_out);
1467
1468	return dim;
1469}
1470
1471/* Are the two spaces the same, including positions and names of parameters?
1472 */
1473int isl_space_is_equal(__isl_keep isl_space *dim1, __isl_keep isl_space *dim2)
1474{
1475	if (!dim1 || !dim2)
1476		return -1;
1477	if (dim1 == dim2)
1478		return 1;
1479	return match(dim1, isl_dim_param, dim2, isl_dim_param) &&
1480	       isl_space_tuple_match(dim1, isl_dim_in, dim2, isl_dim_in) &&
1481	       isl_space_tuple_match(dim1, isl_dim_out, dim2, isl_dim_out);
1482}
1483
1484/* Is space1 equal to the domain of space2?
1485 *
1486 * In the internal version we also allow space2 to be the space of a set,
1487 * provided space1 is a parameter space.
1488 */
1489int isl_space_is_domain_internal(__isl_keep isl_space *space1,
1490	__isl_keep isl_space *space2)
1491{
1492	if (!space1 || !space2)
1493		return -1;
1494	if (!isl_space_is_set(space1))
1495		return 0;
1496	return match(space1, isl_dim_param, space2, isl_dim_param) &&
1497	       isl_space_tuple_match(space1, isl_dim_set, space2, isl_dim_in);
1498}
1499
1500/* Is space1 equal to the domain of space2?
1501 */
1502int isl_space_is_domain(__isl_keep isl_space *space1,
1503	__isl_keep isl_space *space2)
1504{
1505	if (!space2)
1506		return -1;
1507	if (!isl_space_is_map(space2))
1508		return 0;
1509	return isl_space_is_domain_internal(space1, space2);
1510}
1511
1512/* Is space1 equal to the range of space2?
1513 *
1514 * In the internal version, space2 is allowed to be the space of a set,
1515 * in which case it should be equal to space1.
1516 */
1517int isl_space_is_range_internal(__isl_keep isl_space *space1,
1518	__isl_keep isl_space *space2)
1519{
1520	if (!space1 || !space2)
1521		return -1;
1522	if (!isl_space_is_set(space1))
1523		return 0;
1524	return match(space1, isl_dim_param, space2, isl_dim_param) &&
1525	       isl_space_tuple_match(space1, isl_dim_set, space2, isl_dim_out);
1526}
1527
1528/* Is space1 equal to the range of space2?
1529 */
1530int isl_space_is_range(__isl_keep isl_space *space1,
1531	__isl_keep isl_space *space2)
1532{
1533	if (!space2)
1534		return -1;
1535	if (!isl_space_is_map(space2))
1536		return 0;
1537	return isl_space_is_range_internal(space1, space2);
1538}
1539
1540int isl_space_compatible(__isl_keep isl_space *dim1,
1541	__isl_keep isl_space *dim2)
1542{
1543	return dim1->nparam == dim2->nparam &&
1544	       dim1->n_in + dim1->n_out == dim2->n_in + dim2->n_out;
1545}
1546
1547static uint32_t isl_hash_dim(uint32_t hash, __isl_keep isl_space *dim)
1548{
1549	int i;
1550	isl_id *id;
1551
1552	if (!dim)
1553		return hash;
1554
1555	isl_hash_byte(hash, dim->nparam % 256);
1556	isl_hash_byte(hash, dim->n_in % 256);
1557	isl_hash_byte(hash, dim->n_out % 256);
1558
1559	for (i = 0; i < dim->nparam; ++i) {
1560		id = get_id(dim, isl_dim_param, i);
1561		hash = isl_hash_id(hash, id);
1562	}
1563
1564	id = tuple_id(dim, isl_dim_in);
1565	hash = isl_hash_id(hash, id);
1566	id = tuple_id(dim, isl_dim_out);
1567	hash = isl_hash_id(hash, id);
1568
1569	hash = isl_hash_dim(hash, dim->nested[0]);
1570	hash = isl_hash_dim(hash, dim->nested[1]);
1571
1572	return hash;
1573}
1574
1575uint32_t isl_space_get_hash(__isl_keep isl_space *dim)
1576{
1577	uint32_t hash;
1578
1579	if (!dim)
1580		return 0;
1581
1582	hash = isl_hash_init();
1583	hash = isl_hash_dim(hash, dim);
1584
1585	return hash;
1586}
1587
1588int isl_space_is_wrapping(__isl_keep isl_space *dim)
1589{
1590	if (!dim)
1591		return -1;
1592
1593	if (!isl_space_is_set(dim))
1594		return 0;
1595
1596	return dim->nested[1] != NULL;
1597}
1598
1599__isl_give isl_space *isl_space_wrap(__isl_take isl_space *dim)
1600{
1601	isl_space *wrap;
1602
1603	if (!dim)
1604		return NULL;
1605
1606	wrap = isl_space_set_alloc(dim->ctx,
1607				    dim->nparam, dim->n_in + dim->n_out);
1608
1609	wrap = copy_ids(wrap, isl_dim_param, 0, dim, isl_dim_param);
1610	wrap = copy_ids(wrap, isl_dim_set, 0, dim, isl_dim_in);
1611	wrap = copy_ids(wrap, isl_dim_set, dim->n_in, dim, isl_dim_out);
1612
1613	if (!wrap)
1614		goto error;
1615
1616	wrap->nested[1] = dim;
1617
1618	return wrap;
1619error:
1620	isl_space_free(dim);
1621	return NULL;
1622}
1623
1624__isl_give isl_space *isl_space_unwrap(__isl_take isl_space *dim)
1625{
1626	isl_space *unwrap;
1627
1628	if (!dim)
1629		return NULL;
1630
1631	if (!isl_space_is_wrapping(dim))
1632		isl_die(dim->ctx, isl_error_invalid, "not a wrapping space",
1633			goto error);
1634
1635	unwrap = isl_space_copy(dim->nested[1]);
1636	isl_space_free(dim);
1637
1638	return unwrap;
1639error:
1640	isl_space_free(dim);
1641	return NULL;
1642}
1643
1644int isl_space_is_named_or_nested(__isl_keep isl_space *dim, enum isl_dim_type type)
1645{
1646	if (type != isl_dim_in && type != isl_dim_out)
1647		return 0;
1648	if (!dim)
1649		return -1;
1650	if (dim->tuple_id[type - isl_dim_in])
1651		return 1;
1652	if (dim->nested[type - isl_dim_in])
1653		return 1;
1654	return 0;
1655}
1656
1657int isl_space_may_be_set(__isl_keep isl_space *dim)
1658{
1659	if (!dim)
1660		return -1;
1661	if (isl_space_is_set(dim))
1662		return 1;
1663	if (isl_space_dim(dim, isl_dim_in) != 0)
1664		return 0;
1665	if (isl_space_is_named_or_nested(dim, isl_dim_in))
1666		return 0;
1667	return 1;
1668}
1669
1670__isl_give isl_space *isl_space_reset(__isl_take isl_space *dim,
1671	enum isl_dim_type type)
1672{
1673	if (!isl_space_is_named_or_nested(dim, type))
1674		return dim;
1675
1676	dim = isl_space_cow(dim);
1677	if (!dim)
1678		return NULL;
1679
1680	isl_id_free(dim->tuple_id[type - isl_dim_in]);
1681	dim->tuple_id[type - isl_dim_in] = NULL;
1682	isl_space_free(dim->nested[type - isl_dim_in]);
1683	dim->nested[type - isl_dim_in] = NULL;
1684
1685	return dim;
1686}
1687
1688__isl_give isl_space *isl_space_flatten(__isl_take isl_space *dim)
1689{
1690	if (!dim)
1691		return NULL;
1692	if (!dim->nested[0] && !dim->nested[1])
1693		return dim;
1694
1695	if (dim->nested[0])
1696		dim = isl_space_reset(dim, isl_dim_in);
1697	if (dim && dim->nested[1])
1698		dim = isl_space_reset(dim, isl_dim_out);
1699
1700	return dim;
1701}
1702
1703__isl_give isl_space *isl_space_flatten_domain(__isl_take isl_space *dim)
1704{
1705	if (!dim)
1706		return NULL;
1707	if (!dim->nested[0])
1708		return dim;
1709
1710	return isl_space_reset(dim, isl_dim_in);
1711}
1712
1713__isl_give isl_space *isl_space_flatten_range(__isl_take isl_space *dim)
1714{
1715	if (!dim)
1716		return NULL;
1717	if (!dim->nested[1])
1718		return dim;
1719
1720	return isl_space_reset(dim, isl_dim_out);
1721}
1722
1723/* Replace the dimensions of the given type of dst by those of src.
1724 */
1725__isl_give isl_space *isl_space_replace(__isl_take isl_space *dst,
1726	enum isl_dim_type type, __isl_keep isl_space *src)
1727{
1728	dst = isl_space_cow(dst);
1729
1730	if (!dst || !src)
1731		goto error;
1732
1733	dst = isl_space_drop_dims(dst, type, 0, isl_space_dim(dst, type));
1734	dst = isl_space_add_dims(dst, type, isl_space_dim(src, type));
1735	dst = copy_ids(dst, type, 0, src, type);
1736
1737	if (dst && type == isl_dim_param) {
1738		int i;
1739		for (i = 0; i <= 1; ++i) {
1740			if (!dst->nested[i])
1741				continue;
1742			dst->nested[i] = isl_space_replace(dst->nested[i],
1743							 type, src);
1744			if (!dst->nested[i])
1745				goto error;
1746		}
1747	}
1748
1749	return dst;
1750error:
1751	isl_space_free(dst);
1752	return NULL;
1753}
1754
1755/* Given a dimension specification "dim" of a set, create a dimension
1756 * specification for the lift of the set.  In particular, the result
1757 * is of the form [dim -> local[..]], with n_local variables in the
1758 * range of the wrapped map.
1759 */
1760__isl_give isl_space *isl_space_lift(__isl_take isl_space *dim, unsigned n_local)
1761{
1762	isl_space *local_dim;
1763
1764	if (!dim)
1765		return NULL;
1766
1767	local_dim = isl_space_dup(dim);
1768	local_dim = isl_space_drop_dims(local_dim, isl_dim_set, 0, dim->n_out);
1769	local_dim = isl_space_add_dims(local_dim, isl_dim_set, n_local);
1770	local_dim = isl_space_set_tuple_name(local_dim, isl_dim_set, "local");
1771	dim = isl_space_join(isl_space_from_domain(dim),
1772			    isl_space_from_range(local_dim));
1773	dim = isl_space_wrap(dim);
1774	dim = isl_space_set_tuple_name(dim, isl_dim_set, "lifted");
1775
1776	return dim;
1777}
1778
1779int isl_space_can_zip(__isl_keep isl_space *dim)
1780{
1781	if (!dim)
1782		return -1;
1783
1784	return dim->nested[0] && dim->nested[1];
1785}
1786
1787__isl_give isl_space *isl_space_zip(__isl_take isl_space *dim)
1788{
1789	isl_space *dom, *ran;
1790	isl_space *dom_dom, *dom_ran, *ran_dom, *ran_ran;
1791
1792	if (!isl_space_can_zip(dim))
1793		isl_die(dim->ctx, isl_error_invalid, "dim cannot be zipped",
1794			goto error);
1795
1796	if (!dim)
1797		return NULL;
1798	dom = isl_space_unwrap(isl_space_domain(isl_space_copy(dim)));
1799	ran = isl_space_unwrap(isl_space_range(dim));
1800	dom_dom = isl_space_domain(isl_space_copy(dom));
1801	dom_ran = isl_space_range(dom);
1802	ran_dom = isl_space_domain(isl_space_copy(ran));
1803	ran_ran = isl_space_range(ran);
1804	dom = isl_space_join(isl_space_from_domain(dom_dom),
1805			   isl_space_from_range(ran_dom));
1806	ran = isl_space_join(isl_space_from_domain(dom_ran),
1807			   isl_space_from_range(ran_ran));
1808	return isl_space_join(isl_space_from_domain(isl_space_wrap(dom)),
1809			    isl_space_from_range(isl_space_wrap(ran)));
1810error:
1811	isl_space_free(dim);
1812	return NULL;
1813}
1814
1815/* Can we apply isl_space_curry to "space"?
1816 * That is, does it have a nested relation in its domain?
1817 */
1818int isl_space_can_curry(__isl_keep isl_space *space)
1819{
1820	if (!space)
1821		return -1;
1822
1823	return !!space->nested[0];
1824}
1825
1826/* Given a space (A -> B) -> C, return the corresponding space
1827 * A -> (B -> C).
1828 */
1829__isl_give isl_space *isl_space_curry(__isl_take isl_space *space)
1830{
1831	isl_space *dom, *ran;
1832	isl_space *dom_dom, *dom_ran;
1833
1834	if (!space)
1835		return NULL;
1836
1837	if (!isl_space_can_curry(space))
1838		isl_die(space->ctx, isl_error_invalid,
1839			"space cannot be curried", goto error);
1840
1841	dom = isl_space_unwrap(isl_space_domain(isl_space_copy(space)));
1842	ran = isl_space_range(space);
1843	dom_dom = isl_space_domain(isl_space_copy(dom));
1844	dom_ran = isl_space_range(dom);
1845	ran = isl_space_join(isl_space_from_domain(dom_ran),
1846			   isl_space_from_range(ran));
1847	return isl_space_join(isl_space_from_domain(dom_dom),
1848			    isl_space_from_range(isl_space_wrap(ran)));
1849error:
1850	isl_space_free(space);
1851	return NULL;
1852}
1853
1854/* Can we apply isl_space_uncurry to "space"?
1855 * That is, does it have a nested relation in its range?
1856 */
1857int isl_space_can_uncurry(__isl_keep isl_space *space)
1858{
1859	if (!space)
1860		return -1;
1861
1862	return !!space->nested[1];
1863}
1864
1865/* Given a space A -> (B -> C), return the corresponding space
1866 * (A -> B) -> C.
1867 */
1868__isl_give isl_space *isl_space_uncurry(__isl_take isl_space *space)
1869{
1870	isl_space *dom, *ran;
1871	isl_space *ran_dom, *ran_ran;
1872
1873	if (!space)
1874		return NULL;
1875
1876	if (!isl_space_can_uncurry(space))
1877		isl_die(space->ctx, isl_error_invalid,
1878			"space cannot be uncurried",
1879			return isl_space_free(space));
1880
1881	dom = isl_space_domain(isl_space_copy(space));
1882	ran = isl_space_unwrap(isl_space_range(space));
1883	ran_dom = isl_space_domain(isl_space_copy(ran));
1884	ran_ran = isl_space_range(ran);
1885	dom = isl_space_join(isl_space_from_domain(dom),
1886			   isl_space_from_range(ran_dom));
1887	return isl_space_join(isl_space_from_domain(isl_space_wrap(dom)),
1888			    isl_space_from_range(ran_ran));
1889}
1890
1891int isl_space_has_named_params(__isl_keep isl_space *dim)
1892{
1893	int i;
1894	unsigned off;
1895
1896	if (!dim)
1897		return -1;
1898	if (dim->nparam == 0)
1899		return 1;
1900	off = isl_space_offset(dim, isl_dim_param);
1901	if (off + dim->nparam > dim->n_id)
1902		return 0;
1903	for (i = 0; i < dim->nparam; ++i)
1904		if (!dim->ids[off + i])
1905			return 0;
1906	return 1;
1907}
1908
1909/* Align the initial parameters of dim1 to match the order in dim2.
1910 */
1911__isl_give isl_space *isl_space_align_params(__isl_take isl_space *dim1,
1912	__isl_take isl_space *dim2)
1913{
1914	isl_reordering *exp;
1915
1916	if (!isl_space_has_named_params(dim1) || !isl_space_has_named_params(dim2))
1917		isl_die(isl_space_get_ctx(dim1), isl_error_invalid,
1918			"parameter alignment requires named parameters",
1919			goto error);
1920
1921	dim2 = isl_space_params(dim2);
1922	exp = isl_parameter_alignment_reordering(dim1, dim2);
1923	exp = isl_reordering_extend_space(exp, dim1);
1924	isl_space_free(dim2);
1925	if (!exp)
1926		return NULL;
1927	dim1 = isl_space_copy(exp->dim);
1928	isl_reordering_free(exp);
1929	return dim1;
1930error:
1931	isl_space_free(dim1);
1932	isl_space_free(dim2);
1933	return NULL;
1934}
1935
1936/* Given the space of set (domain), construct a space for a map
1937 * with as domain the given space and as range the range of "model".
1938 */
1939__isl_give isl_space *isl_space_extend_domain_with_range(
1940	__isl_take isl_space *space, __isl_take isl_space *model)
1941{
1942	if (!model)
1943		goto error;
1944
1945	space = isl_space_from_domain(space);
1946	space = isl_space_add_dims(space, isl_dim_out,
1947				    isl_space_dim(model, isl_dim_out));
1948	if (isl_space_has_tuple_id(model, isl_dim_out))
1949		space = isl_space_set_tuple_id(space, isl_dim_out,
1950				isl_space_get_tuple_id(model, isl_dim_out));
1951	if (!space)
1952		goto error;
1953	if (model->nested[1]) {
1954		isl_space *nested = isl_space_copy(model->nested[1]);
1955		int n_nested, n_space;
1956		nested = isl_space_align_params(nested, isl_space_copy(space));
1957		n_nested = isl_space_dim(nested, isl_dim_param);
1958		n_space = isl_space_dim(space, isl_dim_param);
1959		if (n_nested > n_space)
1960			nested = isl_space_drop_dims(nested, isl_dim_param,
1961						n_space, n_nested - n_space);
1962		if (!nested)
1963			goto error;
1964		space->nested[1] = nested;
1965	}
1966	isl_space_free(model);
1967	return space;
1968error:
1969	isl_space_free(model);
1970	isl_space_free(space);
1971	return NULL;
1972}
1973