9a35856b97e04737559986168e8f547385c92f56
[platform/upstream/isl.git] / isl_dim.c
1 /*
2  * Copyright 2008-2009 Katholieke Universiteit Leuven
3  *
4  * Use of this software is governed by the GNU LGPLv2.1 license
5  *
6  * Written by Sven Verdoolaege, K.U.Leuven, Departement
7  * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium
8  */
9
10 #include <stdlib.h>
11 #include <isl_dim.h>
12 #include "isl_name.h"
13
14 struct isl_dim *isl_dim_alloc(struct isl_ctx *ctx,
15                         unsigned nparam, unsigned n_in, unsigned n_out)
16 {
17         struct isl_dim *dim;
18
19         dim = isl_alloc_type(ctx, struct isl_dim);
20         if (!dim)
21                 return NULL;
22
23         dim->ctx = ctx;
24         isl_ctx_ref(ctx);
25         dim->ref = 1;
26         dim->nparam = nparam;
27         dim->n_in = n_in;
28         dim->n_out = n_out;
29
30         dim->n_name = 0;
31         dim->names = NULL;
32
33         return dim;
34 }
35
36 struct isl_dim *isl_dim_set_alloc(struct isl_ctx *ctx,
37                         unsigned nparam, unsigned dim)
38 {
39         return isl_dim_alloc(ctx, nparam, 0, dim);
40 }
41
42 static unsigned global_pos(struct isl_dim *dim,
43                                  enum isl_dim_type type, unsigned pos)
44 {
45         struct isl_ctx *ctx = dim->ctx;
46
47         switch (type) {
48         case isl_dim_param:
49                 isl_assert(ctx, pos < dim->nparam, return isl_dim_total(dim));
50                 return pos;
51         case isl_dim_in:
52                 isl_assert(ctx, pos < dim->n_in, return isl_dim_total(dim));
53                 return pos + dim->nparam;
54         case isl_dim_out:
55                 isl_assert(ctx, pos < dim->n_out, return isl_dim_total(dim));
56                 return pos + dim->nparam + dim->n_in;
57         default:
58                 isl_assert(ctx, 0, return isl_dim_total(dim));
59         }
60         return isl_dim_total(dim);
61 }
62
63 /* Extend length of names array to the total number of dimensions.
64  */
65 static __isl_give isl_dim *extend_names(__isl_take isl_dim *dim)
66 {
67         struct isl_name **names;
68         int i;
69
70         if (isl_dim_total(dim) <= dim->n_name)
71                 return dim;
72
73         if (!dim->names) {
74                 dim->names = isl_calloc_array(dim->ctx,
75                                 struct isl_name *, isl_dim_total(dim));
76                 if (!dim->names)
77                         goto error;
78         } else {
79                 names = isl_realloc_array(dim->ctx, dim->names,
80                                 struct isl_name *, isl_dim_total(dim));
81                 if (!names)
82                         goto error;
83                 dim->names = names;
84                 for (i = dim->n_name; i < isl_dim_total(dim); ++i)
85                         dim->names[i] = NULL;
86         }
87
88         dim->n_name = isl_dim_total(dim);
89
90         return dim;
91 error:
92         isl_dim_free(dim);
93         return NULL;
94 }
95
96 static struct isl_dim *set_name(struct isl_dim *dim,
97                                  enum isl_dim_type type, unsigned pos,
98                                  struct isl_name *name)
99 {
100         struct isl_ctx *ctx = dim->ctx;
101         dim = isl_dim_cow(dim);
102
103         if (!dim)
104                 goto error;
105
106         pos = global_pos(dim, type, pos);
107         isl_assert(ctx, pos != isl_dim_total(dim), goto error);
108
109         if (pos >= dim->n_name) {
110                 if (!name)
111                         return dim;
112                 dim = extend_names(dim);
113                 if (!dim)
114                         goto error;
115         }
116
117         dim->names[pos] = name;
118
119         return dim;
120 error:
121         isl_name_free(ctx, name);
122         isl_dim_free(dim);
123         return NULL;
124 }
125
126 static struct isl_name *get_name(struct isl_dim *dim,
127                                  enum isl_dim_type type, unsigned pos)
128 {
129         if (!dim)
130                 return NULL;
131
132         pos = global_pos(dim, type, pos);
133         if (pos == isl_dim_total(dim))
134                 return NULL;
135         if (pos >= dim->n_name)
136                 return NULL;
137         return dim->names[pos];
138 }
139
140 static unsigned offset(struct isl_dim *dim, enum isl_dim_type type)
141 {
142         switch (type) {
143         case isl_dim_param:     return 0;
144         case isl_dim_in:        return dim->nparam;
145         case isl_dim_out:       return dim->nparam + dim->n_in;
146         default:                return 0;
147         }
148 }
149
150 static unsigned n(struct isl_dim *dim, enum isl_dim_type type)
151 {
152         switch (type) {
153         case isl_dim_param:     return dim->nparam;
154         case isl_dim_in:        return dim->n_in;
155         case isl_dim_out:       return dim->n_out;
156         default:                return 0;
157         }
158 }
159
160 unsigned isl_dim_size(struct isl_dim *dim, enum isl_dim_type type)
161 {
162         if (!dim)
163                 return 0;
164         return n(dim, type);
165 }
166
167 unsigned isl_dim_offset(__isl_keep isl_dim *dim, enum isl_dim_type type)
168 {
169         if (!dim)
170                 return 0;
171         return offset(dim, type);
172 }
173
174 static struct isl_dim *copy_names(struct isl_dim *dst,
175         enum isl_dim_type dst_type, unsigned offset, struct isl_dim *src,
176         enum isl_dim_type src_type)
177 {
178         int i;
179         struct isl_name *name;
180
181         if (!dst)
182                 return NULL;
183
184         for (i = 0; i < n(src, src_type); ++i) {
185                 name = get_name(src, src_type, i);
186                 if (!name)
187                         continue;
188                 dst = set_name(dst, dst_type, offset + i,
189                                         isl_name_copy(dst->ctx, name));
190                 if (!dst)
191                         return NULL;
192         }
193         return dst;
194 }
195
196 struct isl_dim *isl_dim_dup(struct isl_dim *dim)
197 {
198         struct isl_dim *dup;
199         if (!dim)
200                 return NULL;
201         dup = isl_dim_alloc(dim->ctx, dim->nparam, dim->n_in, dim->n_out);
202         if (!dim->names)
203                 return dup;
204         dup = copy_names(dup, isl_dim_param, 0, dim, isl_dim_param);
205         dup = copy_names(dup, isl_dim_in, 0, dim, isl_dim_in);
206         dup = copy_names(dup, isl_dim_out, 0, dim, isl_dim_out);
207         return dup;
208 }
209
210 struct isl_dim *isl_dim_cow(struct isl_dim *dim)
211 {
212         if (!dim)
213                 return NULL;
214
215         if (dim->ref == 1)
216                 return dim;
217         dim->ref--;
218         return isl_dim_dup(dim);
219 }
220
221 struct isl_dim *isl_dim_copy(struct isl_dim *dim)
222 {
223         if (!dim)
224                 return NULL;
225
226         dim->ref++;
227         return dim;
228 }
229
230 void isl_dim_free(struct isl_dim *dim)
231 {
232         int i;
233
234         if (!dim)
235                 return;
236
237         if (--dim->ref > 0)
238                 return;
239
240         for (i = 0; i < dim->n_name; ++i)
241                 isl_name_free(dim->ctx, dim->names[i]);
242         free(dim->names);
243         isl_ctx_deref(dim->ctx);
244         
245         free(dim);
246 }
247
248 struct isl_dim *isl_dim_set_name(struct isl_dim *dim,
249                                  enum isl_dim_type type, unsigned pos,
250                                  const char *s)
251 {
252         struct isl_name *name;
253         char *p;
254         long dummy;
255
256         if (!dim)
257                 return NULL;
258         dummy = strtol(s, &p, 0);
259         if (p != s)
260                 isl_die(dim->ctx, isl_error_invalid, "name looks like a number",
261                         goto error);
262         name = isl_name_get(dim->ctx, s);
263         if (!name)
264                 goto error;
265         return set_name(dim, type, pos, name);
266 error:
267         isl_dim_free(dim);
268         return NULL;
269 }
270
271 const char *isl_dim_get_name(struct isl_dim *dim,
272                                  enum isl_dim_type type, unsigned pos)
273 {
274         struct isl_name *name = get_name(dim, type, pos);
275         return name ? name->name : NULL;
276 }
277
278 static int match(struct isl_dim *dim1, enum isl_dim_type dim1_type,
279                 struct isl_dim *dim2, enum isl_dim_type dim2_type)
280 {
281         int i;
282
283         if (n(dim1, dim1_type) != n(dim2, dim2_type))
284                 return 0;
285
286         if (!dim1->names && !dim2->names)
287                 return 1;
288
289         for (i = 0; i < n(dim1, dim1_type); ++i) {
290                 if (get_name(dim1, dim1_type, i) !=
291                     get_name(dim2, dim2_type, i))
292                         return 0;
293         }
294         return 1;
295 }
296
297 int isl_dim_match(struct isl_dim *dim1, enum isl_dim_type dim1_type,
298                 struct isl_dim *dim2, enum isl_dim_type dim2_type)
299 {
300         return match(dim1, dim1_type, dim2, dim2_type);
301 }
302
303 static void get_names(struct isl_dim *dim, enum isl_dim_type type,
304         unsigned first, unsigned n, struct isl_name **names)
305 {
306         int i;
307
308         for (i = 0; i < n ; ++i)
309                 names[i] = get_name(dim, type, first+i);
310 }
311
312 struct isl_dim *isl_dim_extend(struct isl_dim *dim,
313                         unsigned nparam, unsigned n_in, unsigned n_out)
314 {
315         struct isl_name **names = NULL;
316
317         if (!dim)
318                 return NULL;
319         if (dim->nparam == nparam && dim->n_in == n_in && dim->n_out == n_out)
320                 return dim;
321
322         isl_assert(dim->ctx, dim->nparam <= nparam, goto error);
323         isl_assert(dim->ctx, dim->n_in <= n_in, goto error);
324         isl_assert(dim->ctx, dim->n_out <= n_out, goto error);
325
326         dim = isl_dim_cow(dim);
327
328         if (dim->names) {
329                 names = isl_calloc_array(dim->ctx, struct isl_name *,
330                                          nparam + n_in + n_out);
331                 if (!names)
332                         goto error;
333                 get_names(dim, isl_dim_param, 0, dim->nparam, names);
334                 get_names(dim, isl_dim_in, 0, dim->n_in, names + nparam);
335                 get_names(dim, isl_dim_out, 0, dim->n_out,
336                                 names + nparam + n_in);
337                 free(dim->names);
338                 dim->names = names;
339                 dim->n_name = nparam + n_in + n_out;
340         }
341         dim->nparam = nparam;
342         dim->n_in = n_in;
343         dim->n_out = n_out;
344
345         return dim;
346 error:
347         free(names);
348         isl_dim_free(dim);
349         return NULL;
350 }
351
352 struct isl_dim *isl_dim_add(struct isl_dim *dim, enum isl_dim_type type,
353         unsigned n)
354 {
355         switch (type) {
356         case isl_dim_param:
357                 return isl_dim_extend(dim,
358                                         dim->nparam + n, dim->n_in, dim->n_out);
359         case isl_dim_in:
360                 return isl_dim_extend(dim,
361                                         dim->nparam, dim->n_in + n, dim->n_out);
362         case isl_dim_out:
363                 return isl_dim_extend(dim,
364                                         dim->nparam, dim->n_in, dim->n_out + n);
365         }
366         return dim;
367 }
368
369 __isl_give isl_dim *isl_dim_insert(__isl_take isl_dim *dim,
370         enum isl_dim_type type, unsigned pos, unsigned n)
371 {
372         struct isl_name **names = NULL;
373
374         if (!dim)
375                 return NULL;
376         if (n == 0)
377                 return dim;
378
379         isl_assert(dim->ctx, pos <= isl_dim_size(dim, type), goto error);
380
381         dim = isl_dim_cow(dim);
382         if (!dim)
383                 return NULL;
384
385         if (dim->names) {
386                 enum isl_dim_type t;
387                 int off;
388                 int size[3];
389                 names = isl_calloc_array(dim->ctx, struct isl_name *,
390                                      dim->nparam + dim->n_in + dim->n_out + n);
391                 if (!names)
392                         goto error;
393                 off = 0;
394                 size[isl_dim_param] = dim->nparam;
395                 size[isl_dim_in] = dim->n_in;
396                 size[isl_dim_out] = dim->n_out;
397                 for (t = isl_dim_param; t <= isl_dim_out; ++t) {
398                         if (t != type) {
399                                 get_names(dim, t, 0, size[t], names + off);
400                                 off += size[t];
401                         } else {
402                                 get_names(dim, t, 0, pos, names + off);
403                                 off += pos + n;
404                                 get_names(dim, t, pos, size[t]-pos, names+off);
405                                 off += size[t] - pos;
406                         }
407                 }
408                 free(dim->names);
409                 dim->names = names;
410                 dim->n_name = dim->nparam + dim->n_in + dim->n_out + n;
411         }
412         switch (type) {
413         case isl_dim_param:     dim->nparam += n; break;
414         case isl_dim_in:        dim->n_in += n; break;
415         case isl_dim_out:       dim->n_out += n; break;
416         }
417
418         return dim;
419 error:
420         isl_dim_free(dim);
421         return NULL;
422 }
423
424 __isl_give isl_dim *isl_dim_move(__isl_take isl_dim *dim,
425         enum isl_dim_type dst_type, unsigned dst_pos,
426         enum isl_dim_type src_type, unsigned src_pos, unsigned n)
427 {
428         if (!dim)
429                 return NULL;
430         if (n == 0)
431                 return dim;
432
433         isl_assert(dim->ctx, src_pos + n <= isl_dim_size(dim, src_type),
434                 goto error);
435
436         if (dst_type == src_type && dst_pos == src_pos)
437                 return dim;
438
439         isl_assert(dim->ctx, dst_type != src_type, goto error);
440
441         dim = isl_dim_cow(dim);
442         if (!dim)
443                 return NULL;
444
445         if (dim->names) {
446                 struct isl_name **names;
447                 enum isl_dim_type t;
448                 int off;
449                 int size[3];
450                 names = isl_calloc_array(dim->ctx, struct isl_name *,
451                                          dim->nparam + dim->n_in + dim->n_out);
452                 if (!names)
453                         goto error;
454                 off = 0;
455                 size[isl_dim_param] = dim->nparam;
456                 size[isl_dim_in] = dim->n_in;
457                 size[isl_dim_out] = dim->n_out;
458                 for (t = isl_dim_param; t <= isl_dim_out; ++t) {
459                         if (t == dst_type) {
460                                 get_names(dim, t, 0, dst_pos, names + off);
461                                 off += dst_pos;
462                                 get_names(dim, src_type, src_pos, n, names+off);
463                                 off += n;
464                                 get_names(dim, t, dst_pos, size[t] - dst_pos,
465                                                 names + off);
466                                 off += size[t] - dst_pos;
467                         } else if (t == src_type) {
468                                 get_names(dim, t, 0, src_pos, names + off);
469                                 off += src_pos;
470                                 get_names(dim, t, src_pos + n,
471                                             size[t] - src_pos - n, names + off);
472                                 off += size[t] - src_pos - n;
473                         } else {
474                                 get_names(dim, t, 0, size[t], names + off);
475                                 off += size[t];
476                         }
477                 }
478                 free(dim->names);
479                 dim->names = names;
480                 dim->n_name = dim->nparam + dim->n_in + dim->n_out;
481         }
482
483         switch (dst_type) {
484         case isl_dim_param:     dim->nparam += n; break;
485         case isl_dim_in:        dim->n_in += n; break;
486         case isl_dim_out:       dim->n_out += n; break;
487         }
488
489         switch (src_type) {
490         case isl_dim_param:     dim->nparam -= n; break;
491         case isl_dim_in:        dim->n_in -= n; break;
492         case isl_dim_out:       dim->n_out -= n; break;
493         }
494
495         return dim;
496 error:
497         isl_dim_free(dim);
498         return NULL;
499 }
500
501 struct isl_dim *isl_dim_join(struct isl_dim *left, struct isl_dim *right)
502 {
503         struct isl_dim *dim;
504
505         if (!left || !right)
506                 goto error;
507
508         isl_assert(left->ctx, match(left, isl_dim_param, right, isl_dim_param),
509                         goto error);
510         isl_assert(left->ctx, n(left, isl_dim_out) == n(right, isl_dim_in),
511                         goto error);
512
513         dim = isl_dim_alloc(left->ctx, left->nparam, left->n_in, right->n_out);
514         if (!dim)
515                 goto error;
516
517         dim = copy_names(dim, isl_dim_param, 0, left, isl_dim_param);
518         dim = copy_names(dim, isl_dim_in, 0, left, isl_dim_in);
519         dim = copy_names(dim, isl_dim_out, 0, right, isl_dim_out);
520
521         isl_dim_free(left);
522         isl_dim_free(right);
523
524         return dim;
525 error:
526         isl_dim_free(left);
527         isl_dim_free(right);
528         return NULL;
529 }
530
531 struct isl_dim *isl_dim_product(struct isl_dim *left, struct isl_dim *right)
532 {
533         struct isl_dim *dim;
534
535         if (!left || !right)
536                 goto error;
537
538         isl_assert(left->ctx, match(left, isl_dim_param, right, isl_dim_param),
539                         goto error);
540
541         dim = isl_dim_alloc(left->ctx, left->nparam,
542                         left->n_in + right->n_in, left->n_out + right->n_out);
543         if (!dim)
544                 goto error;
545
546         dim = copy_names(dim, isl_dim_param, 0, left, isl_dim_param);
547         dim = copy_names(dim, isl_dim_in, 0, left, isl_dim_in);
548         dim = copy_names(dim, isl_dim_in, left->n_in, right, isl_dim_in);
549         dim = copy_names(dim, isl_dim_out, 0, left, isl_dim_out);
550         dim = copy_names(dim, isl_dim_out, left->n_out, right, isl_dim_out);
551
552         isl_dim_free(left);
553         isl_dim_free(right);
554
555         return dim;
556 error:
557         isl_dim_free(left);
558         isl_dim_free(right);
559         return NULL;
560 }
561
562 struct isl_dim *isl_dim_map(struct isl_dim *dim)
563 {
564         struct isl_name **names = NULL;
565
566         if (!dim)
567                 return NULL;
568         isl_assert(dim->ctx, dim->n_in == 0, goto error);
569         if (dim->n_out == 0)
570                 return dim;
571         dim = isl_dim_cow(dim);
572         if (!dim)
573                 return NULL;
574         if (dim->names) {
575                 names = isl_calloc_array(dim->ctx, struct isl_name *,
576                                         dim->nparam + dim->n_out + dim->n_out);
577                 if (!names)
578                         goto error;
579                 get_names(dim, isl_dim_param, 0, dim->nparam, names);
580                 get_names(dim, isl_dim_out, 0, dim->n_out, names + dim->nparam);
581         }
582         dim->n_in = dim->n_out;
583         if (names) {
584                 free(dim->names);
585                 dim->names = names;
586                 dim->n_name = dim->nparam + dim->n_out + dim->n_out;
587                 dim = copy_names(dim, isl_dim_out, 0, dim, isl_dim_in);
588         }
589         return dim;
590 error:
591         isl_dim_free(dim);
592         return NULL;
593 }
594
595 static struct isl_dim *set_names(struct isl_dim *dim, enum isl_dim_type type,
596         unsigned first, unsigned n, struct isl_name **names)
597 {
598         int i;
599
600         for (i = 0; i < n ; ++i)
601                 dim = set_name(dim, type, first+i, names[i]);
602
603         return dim;
604 }
605
606 struct isl_dim *isl_dim_reverse(struct isl_dim *dim)
607 {
608         unsigned t;
609         struct isl_name **names = NULL;
610
611         if (!dim)
612                 return NULL;
613         if (match(dim, isl_dim_in, dim, isl_dim_out))
614                 return dim;
615
616         dim = isl_dim_cow(dim);
617         if (!dim)
618                 return NULL;
619
620         if (dim->names) {
621                 names = isl_alloc_array(dim->ctx, struct isl_name *,
622                                         dim->n_in + dim->n_out);
623                 if (!names)
624                         goto error;
625                 get_names(dim, isl_dim_in, 0, dim->n_in, names);
626                 get_names(dim, isl_dim_out, 0, dim->n_out, names + dim->n_in);
627         }
628
629         t = dim->n_in;
630         dim->n_in = dim->n_out;
631         dim->n_out = t;
632
633         if (dim->names) {
634                 dim = set_names(dim, isl_dim_out, 0, dim->n_out, names);
635                 dim = set_names(dim, isl_dim_in, 0, dim->n_in, names + dim->n_out);
636                 free(names);
637         }
638
639         return dim;
640 error:
641         free(names);
642         isl_dim_free(dim);
643         return NULL;
644 }
645
646 struct isl_dim *isl_dim_drop(struct isl_dim *dim, enum isl_dim_type type,
647                 unsigned first, unsigned num)
648 {
649         int i;
650
651         if (!dim)
652                 return NULL;
653
654         if (n == 0)
655                 return dim;
656
657         isl_assert(dim->ctx, first + num <= n(dim, type), goto error);
658         dim = isl_dim_cow(dim);
659         if (!dim)
660                 goto error;
661         if (dim->names) {
662                 dim = extend_names(dim);
663                 if (!dim)
664                         goto error;
665                 for (i = 0; i < num; ++i)
666                         isl_name_free(dim->ctx, get_name(dim, type, first+i));
667                 for (i = first+num; i < n(dim, type); ++i)
668                         set_name(dim, type, i - num, get_name(dim, type, i));
669                 switch (type) {
670                 case isl_dim_param:
671                         get_names(dim, isl_dim_in, 0, dim->n_in,
672                                 dim->names + offset(dim, isl_dim_in) - num);
673                 case isl_dim_in:
674                         get_names(dim, isl_dim_out, 0, dim->n_out,
675                                 dim->names + offset(dim, isl_dim_out) - num);
676                 case isl_dim_out:
677                         ;
678                 }
679                 dim->n_name -= num;
680         }
681         switch (type) {
682         case isl_dim_param:     dim->nparam -= num; break;
683         case isl_dim_in:        dim->n_in -= num; break;
684         case isl_dim_out:       dim->n_out -= num; break;
685         }
686         return dim;
687 error:
688         isl_dim_free(dim);
689         return NULL;
690 }
691
692 struct isl_dim *isl_dim_drop_inputs(struct isl_dim *dim,
693                 unsigned first, unsigned n)
694 {
695         return isl_dim_drop(dim, isl_dim_in, first, n);
696 }
697
698 struct isl_dim *isl_dim_drop_outputs(struct isl_dim *dim,
699                 unsigned first, unsigned n)
700 {
701         return isl_dim_drop(dim, isl_dim_out, first, n);
702 }
703
704 struct isl_dim *isl_dim_domain(struct isl_dim *dim)
705 {
706         if (!dim)
707                 return NULL;
708         dim = isl_dim_drop_outputs(dim, 0, dim->n_out);
709         return isl_dim_reverse(dim);
710 }
711
712 struct isl_dim *isl_dim_range(struct isl_dim *dim)
713 {
714         if (!dim)
715                 return NULL;
716         return isl_dim_drop_inputs(dim, 0, dim->n_in);
717 }
718
719 struct isl_dim *isl_dim_underlying(struct isl_dim *dim, unsigned n_div)
720 {
721         int i;
722
723         if (!dim)
724                 return NULL;
725         if (n_div == 0 &&
726             dim->nparam == 0 && dim->n_in == 0 && dim->n_name == 0)
727                 return dim;
728         dim = isl_dim_cow(dim);
729         if (!dim)
730                 return NULL;
731         dim->n_out += dim->nparam + dim->n_in + n_div;
732         dim->nparam = 0;
733         dim->n_in = 0;
734
735         for (i = 0; i < dim->n_name; ++i)
736                 isl_name_free(dim->ctx, get_name(dim, isl_dim_out, i));
737         dim->n_name = 0;
738
739         return dim;
740 }
741
742 unsigned isl_dim_total(struct isl_dim *dim)
743 {
744         return dim->nparam + dim->n_in + dim->n_out;
745 }
746
747 int isl_dim_equal(struct isl_dim *dim1, struct isl_dim *dim2)
748 {
749         return match(dim1, isl_dim_param, dim2, isl_dim_param) &&
750                n(dim1, isl_dim_in) == n(dim2, isl_dim_in) &&
751                n(dim1, isl_dim_out) == n(dim2, isl_dim_out);
752 }
753
754 int isl_dim_compatible(struct isl_dim *dim1, struct isl_dim *dim2)
755 {
756         return dim1->nparam == dim2->nparam &&
757                dim1->n_in + dim1->n_out == dim2->n_in + dim2->n_out;
758 }