1 /****************************************************************************
2 **
3 ** This file is part of GAP, a system for computational discrete algebra.
4 **
5 ** Copyright of GAP belongs to its developers, whose names are too numerous
6 ** to list here. Please refer to the COPYRIGHT file for details.
7 **
8 ** SPDX-License-Identifier: GPL-2.0-or-later
9 */
10
11 /*****************************************************************************
12 *
13 * A transformation <f> has internal representation as follows:
14 *
15 * [Obj* image set, Obj* flat kernel, Obj* external degree,
16 * entries image list]
17 *
18 * The <internal degree> of <f> is just the length of <entries image
19 * list>, this is accessed here using <DEG_TRANS2> and <DEG_TRANS4>.
20 *
21 * Transformations must always have internal degree greater than or equal
22 * to the largest point in <entries image list>.
23 *
24 * An element of <entries image list> of a transformation in T_TRANS2
25 * must be at most 65535 and be UInt2. Hence the internal and external
26 * degrees of a T_TRANS2 are at most 65536. If <f> is T_TRANS4, then the
27 * elements of <entries image list> must be UInt4.
28 *
29 * The <image set> and <flat kernel> are found relative to the internal
30 * degree of the transformation, and must not be changed after they are
31 * first found.
32 *
33 * The <external degree> is the largest non-negative integer <n> such
34 * that n ^ f != n or i ^ f = n for some i != n, or equivalently the
35 * degree of a transformation is the least non-negative <n> such that [n
36 * + 1, n + 2, .. ] is fixed pointwise by <f>. This value is an invariant
37 * of <f>, in the sense that it does not depend on the internal
38 * representation. In GAP, DegreeOfTransformation(f) returns the external
39 * degree, so that if f = g, then DegreeOfTransformation(f) =
40 * DegreeOfTransformation(g).
41 *
42 * In this file, the external degree of a transformation is accessed
43 * using FuncDegreeOfTransformation(self, f), and the internal degree is
44 * accessed using DEG_TRANS2/4(f).
45 *
46 *****************************************************************************/
47
48 extern "C" {
49
50 #include "trans.h"
51
52 #include "ariths.h"
53 #include "bool.h"
54 #include "error.h"
55 #include "gapstate.h"
56 #include "gvars.h"
57 #include "integer.h"
58 #include "intfuncs.h"
59 #include "listfunc.h"
60 #include "lists.h"
61 #include "modules.h"
62 #include "opers.h"
63 #include "permutat.h"
64 #include "plist.h"
65 #include "saveload.h"
66
67 } // extern "C"
68
69 #include "permutat_intern.hh"
70
71
72 //
73 // convert TNUM to underlying C data type
74 //
75 template <UInt tnum>
76 struct DataType;
77
78 template <>
79 struct DataType<T_TRANS2> {
80 typedef UInt2 type;
81 };
82 template <>
83 struct DataType<T_TRANS4> {
84 typedef UInt4 type;
85 };
86
87
88 //
89 // convert underlying C data type to TNUM
90 //
91 template <typename T>
92 struct T_TRANS {
93 };
94 template <>
95 struct T_TRANS<UInt2> {
96 static const UInt tnum = T_TRANS2;
97 };
98 template <>
99 struct T_TRANS<UInt4> {
100 static const UInt tnum = T_TRANS4;
101 };
102
103
104 //
105 // Various helper functions for partial permutations
106 //
107 template <typename T>
ASSERT_IS_TRANS(Obj f)108 static void ASSERT_IS_TRANS(Obj f)
109 {
110 GAP_ASSERT(TNUM_OBJ(f) == T_TRANS<T>::tnum);
111 }
112
113 template <typename T>
NEW_TRANS(UInt deg)114 static inline Obj NEW_TRANS(UInt deg)
115 {
116 return NewBag(T_TRANS<T>::tnum, deg * sizeof(T) + 3 * sizeof(Obj));
117 }
118
119 template <typename T>
ADDR_TRANS(Obj f)120 static inline T * ADDR_TRANS(Obj f)
121 {
122 ASSERT_IS_TRANS<T>(f);
123 return (T *)(ADDR_OBJ(f) + 3);
124 }
125
126 template <typename T>
CONST_ADDR_TRANS(Obj f)127 static inline const T * CONST_ADDR_TRANS(Obj f)
128 {
129 ASSERT_IS_TRANS<T>(f);
130 return (const T *)(CONST_ADDR_OBJ(f) + 3);
131 }
132
133 template <typename T>
DEG_TRANS(Obj f)134 static inline UInt DEG_TRANS(Obj f)
135 {
136 ASSERT_IS_TRANS<T>(f);
137 return (UInt)(SIZE_OBJ(f) - 3 * sizeof(Obj)) / sizeof(T);
138 }
139
140
141 #define MIN(a, b) (a < b ? a : b)
142 #define MAX(a, b) (a < b ? b : a)
143
144 #define RequireTransformation(funcname, op) \
145 RequireArgumentCondition(funcname, op, IS_TRANS(op), \
146 "must be a transformation")
147
148
149 static ModuleStateOffset TransStateOffset = -1;
150
151 typedef struct {
152 // TmpTrans is essentially the same as TmpPerm
153 Obj TmpTrans;
154 } TransModuleState;
155
GetTmpTrans(void)156 static inline Obj GetTmpTrans(void)
157 {
158 return MODULE_STATE(Trans).TmpTrans;
159 }
160
AddrTmpTrans(void)161 static inline UInt4 * AddrTmpTrans(void)
162 {
163 return ADDR_TRANS4(GetTmpTrans());
164 }
165
166
167 /****************************************************************************
168 **
169 *V IdentityTrans . . . . . . . . . . . . . . . . . identity transformation
170 **
171 ** 'IdentityTrans' is an identity transformation.
172 */
173 /* mp this will become a ReadOnly object? */
174 static Obj IdentityTrans;
175
176 /*******************************************************************************
177 ** Forward declarations
178 *******************************************************************************/
179
180 static Obj FuncIMAGE_SET_TRANS(Obj self, Obj f);
181
182 /*******************************************************************************
183 ** Internal functions for transformations
184 *******************************************************************************/
185
IMG_TRANS(Obj f)186 static inline Obj IMG_TRANS(Obj f)
187 {
188 GAP_ASSERT(IS_TRANS(f));
189 return CONST_ADDR_OBJ(f)[0];
190 }
191
KER_TRANS(Obj f)192 static inline Obj KER_TRANS(Obj f)
193 {
194 GAP_ASSERT(IS_TRANS(f));
195 return CONST_ADDR_OBJ(f)[1];
196 }
197
EXT_TRANS(Obj f)198 static inline Obj EXT_TRANS(Obj f)
199 {
200 GAP_ASSERT(IS_TRANS(f));
201 return CONST_ADDR_OBJ(f)[2];
202 }
203
SET_IMG_TRANS(Obj f,Obj img)204 static inline void SET_IMG_TRANS(Obj f, Obj img)
205 {
206 GAP_ASSERT(IS_TRANS(f));
207 GAP_ASSERT(img == NULL || (IS_PLIST(img) && !IS_PLIST_MUTABLE(img)));
208 ADDR_OBJ(f)[0] = img;
209 }
210
SET_KER_TRANS(Obj f,Obj ker)211 static inline void SET_KER_TRANS(Obj f, Obj ker)
212 {
213 GAP_ASSERT(IS_TRANS(f));
214 GAP_ASSERT(ker == NULL || (IS_PLIST(ker) && !IS_PLIST_MUTABLE(ker) &&
215 LEN_PLIST(ker) == DEG_TRANS(f)));
216 ADDR_OBJ(f)[1] = ker;
217 }
218
SET_EXT_TRANS(Obj f,Obj deg)219 static inline void SET_EXT_TRANS(Obj f, Obj deg)
220 {
221 GAP_ASSERT(IS_TRANS(f));
222 GAP_ASSERT(deg == NULL ||
223 (IS_INTOBJ(deg) && INT_INTOBJ(deg) <= DEG_TRANS(f)));
224 ADDR_OBJ(f)[2] = deg;
225 }
226
ResizeTmpTrans(UInt len)227 static inline void ResizeTmpTrans(UInt len)
228 {
229 Obj tmpTrans = GetTmpTrans();
230 if (tmpTrans == (Obj)0) {
231 MODULE_STATE(Trans).TmpTrans = NewBag(T_TRANS4, len * sizeof(UInt4) + 3 * sizeof(Obj));
232 }
233 else if (SIZE_OBJ(tmpTrans) < len * sizeof(UInt4) + 3 * sizeof(Obj)) {
234 ResizeBag(tmpTrans, len * sizeof(UInt4) + 3 * sizeof(Obj));
235 }
236 }
237
ResizeInitTmpTrans(UInt len)238 static inline UInt4 * ResizeInitTmpTrans(UInt len)
239 {
240 ResizeTmpTrans(len);
241
242 UInt4 * pttmp = AddrTmpTrans();
243 memset(pttmp, 0, len * sizeof(UInt4));
244 return pttmp;
245 }
246
247 // Find the rank, flat kernel, and image set (unsorted) of a transformation of
248 // degree at most 65536.
249
INIT_TRANS2(Obj f)250 static UInt INIT_TRANS2(Obj f)
251 {
252 UInt deg, rank, i, j;
253 const UInt2 * ptf;
254 UInt4 * pttmp;
255 Obj img, ker;
256
257 deg = DEG_TRANS2(f);
258
259 if (deg == 0) {
260 // special case for degree 0
261 img = NewImmutableEmptyPlist();
262 SET_IMG_TRANS(f, img);
263 SET_KER_TRANS(f, img);
264 CHANGED_BAG(f);
265 return 0;
266 }
267
268 img = NEW_PLIST_IMM(T_PLIST_CYC, deg);
269 ker = NEW_PLIST_IMM(T_PLIST_CYC_NSORT, deg);
270 SET_LEN_PLIST(ker, (Int)deg);
271
272 pttmp = ResizeInitTmpTrans(deg);
273 ptf = CONST_ADDR_TRANS2(f);
274
275 rank = 0;
276 for (i = 0; i < deg; i++) {
277 j = ptf[i];
278 if (pttmp[j] == 0) {
279 pttmp[j] = ++rank;
280 SET_ELM_PLIST(img, rank, INTOBJ_INT(j + 1));
281 }
282 SET_ELM_PLIST(ker, i + 1, INTOBJ_INT(pttmp[j]));
283 }
284
285 SHRINK_PLIST(img, (Int)rank);
286 SET_LEN_PLIST(img, (Int)rank);
287
288 SET_IMG_TRANS(f, img);
289 SET_KER_TRANS(f, ker);
290 CHANGED_BAG(f);
291 return rank;
292 }
293
294 // Find the rank, flat kernel, and image set (unsorted) of a transformation of
295 // degree at least 65537.
296
INIT_TRANS4(Obj f)297 static UInt INIT_TRANS4(Obj f)
298 {
299 UInt deg, rank, i, j;
300 const UInt4 * ptf;
301 UInt4 * pttmp;
302 Obj img, ker;
303
304 deg = DEG_TRANS4(f);
305
306 if (deg == 0) {
307 // Special case for degree 0.
308
309 // The only transformation created within this file that is of type
310 // T_TRANS4 and that does not have (internal) degree 65537 or greater
311 // is ID_TRANS4.
312
313 img = NewImmutableEmptyPlist();
314 SET_IMG_TRANS(f, img);
315 SET_KER_TRANS(f, img);
316 CHANGED_BAG(f);
317 return 0;
318 }
319
320 img = NEW_PLIST_IMM(T_PLIST_CYC, deg);
321 ker = NEW_PLIST_IMM(T_PLIST_CYC_NSORT, deg);
322 SET_LEN_PLIST(ker, (Int)deg);
323
324 pttmp = ResizeInitTmpTrans(deg);
325 ptf = CONST_ADDR_TRANS4(f);
326
327 rank = 0;
328 for (i = 0; i < deg; i++) {
329 j = ptf[i];
330 if (pttmp[j] == 0) {
331 pttmp[j] = ++rank;
332 SET_ELM_PLIST(img, rank, INTOBJ_INT(j + 1));
333 }
334 SET_ELM_PLIST(ker, i + 1, INTOBJ_INT(pttmp[j]));
335 }
336
337 SHRINK_PLIST(img, (Int)rank);
338 SET_LEN_PLIST(img, (Int)rank);
339
340 SET_IMG_TRANS(f, img);
341 SET_KER_TRANS(f, ker);
342 CHANGED_BAG(f);
343 return rank;
344 }
345
RANK_TRANS2(Obj f)346 UInt RANK_TRANS2(Obj f)
347 {
348 GAP_ASSERT(TNUM_OBJ(f) == T_TRANS2);
349 return (IMG_TRANS(f) == NULL ? INIT_TRANS2(f) : LEN_PLIST(IMG_TRANS(f)));
350 }
351
RANK_TRANS4(Obj f)352 UInt RANK_TRANS4(Obj f)
353 {
354 GAP_ASSERT(TNUM_OBJ(f) == T_TRANS4);
355 return (IMG_TRANS(f) == NULL ? INIT_TRANS4(f) : LEN_PLIST(IMG_TRANS(f)));
356 }
357
358 // Retyping is the responsibility of the caller, this should only be called
359 // after a call to SortPlistByRawObj.
360
REMOVE_DUPS_PLIST_INTOBJ(Obj res)361 static void REMOVE_DUPS_PLIST_INTOBJ(Obj res)
362 {
363 Obj tmp;
364 UInt i, k, len;
365 Obj *data;
366
367 len = LEN_PLIST(res);
368
369 if (0 < len) {
370 data = ADDR_OBJ(res);
371 tmp = data[1];
372 k = 1;
373 for (i = 2; i <= len; i++) {
374 if (tmp != data[i]) {
375 k++;
376 tmp = data[i];
377 data[k] = tmp;
378 }
379 }
380 if (k < len) {
381 ResizeBag(res, (k + 1) * sizeof(Obj));
382 SET_LEN_PLIST(res, k);
383 }
384 }
385 }
386
387 /*******************************************************************************
388 ** GAP level functions for creating transformations
389 *******************************************************************************/
390
391 // Returns a transformation with list of images <list>, this does not check
392 // that <list> is really a list or that its entries define a transformation.
393
FuncTransformationNC(Obj self,Obj list)394 static Obj FuncTransformationNC(Obj self, Obj list)
395 {
396 UInt i, deg;
397 UInt2 * ptf2;
398 UInt4 * ptf4;
399 Obj f;
400
401 deg = LEN_LIST(list);
402
403 if (deg <= 65536) {
404 f = NEW_TRANS2(deg);
405 ptf2 = ADDR_TRANS2(f);
406 for (i = 0; i < deg; i++) {
407 ptf2[i] = INT_INTOBJ(ELM_LIST(list, i + 1)) - 1;
408 }
409 }
410 else {
411 f = NEW_TRANS4(deg);
412 ptf4 = ADDR_TRANS4(f);
413 for (i = 0; i < deg; i++) {
414 ptf4[i] = INT_INTOBJ(ELM_LIST(list, i + 1)) - 1;
415 }
416 }
417 return f;
418 }
419
420 // Returns a transformation that maps <src> to <ran>, this does not check that
421 // <src> is duplicate-free.
422
FuncTransformationListListNC(Obj self,Obj src,Obj ran)423 static Obj FuncTransformationListListNC(Obj self, Obj src, Obj ran)
424 {
425 Int deg, i, s, r;
426 Obj f;
427 UInt2 * ptf2;
428 UInt4 * ptf4;
429
430 RequireSmallList("TransformationListListNC", src);
431 RequireSmallList("TransformationListListNC", ran);
432 RequireSameLength("TransformationListListNC", src, ran);
433
434 deg = 0;
435 for (i = LEN_LIST(src); 1 <= i; i--) {
436 if (!IS_INTOBJ(ELM_LIST(src, i))) {
437 ErrorQuit("TransformationListListNC: <src>[%d] must be a small "
438 "integer (not a "
439 "%s)",
440 (Int)i, (Int)TNAM_OBJ(ELM_LIST(src, i)));
441 }
442 s = INT_INTOBJ(ELM_LIST(src, i));
443 if (s < 1) {
444 ErrorQuit(
445 "TransformationListListNC: <src>[%d] must be greater than 0",
446 (Int)i, 0L);
447 }
448
449 if (!IS_INTOBJ(ELM_LIST(ran, i))) {
450 ErrorQuit("TransformationListListNC: <ran>[%d] must be a small "
451 "integer (not a "
452 "%s)",
453 (Int)i, (Int)TNAM_OBJ(ELM_LIST(ran, i)));
454 }
455 r = INT_INTOBJ(ELM_LIST(ran, i));
456 if (r < 1) {
457 ErrorQuit(
458 "TransformationListListNC: <ran>[%d] must be greater than 0",
459 (Int)i, 0L);
460 }
461
462 if (s != r) {
463 if (s > deg) {
464 deg = s;
465 }
466 if (r > deg) {
467 deg = r;
468 }
469 }
470 }
471
472 if (deg <= 65536) {
473 f = NEW_TRANS2(deg);
474 ptf2 = ADDR_TRANS2(f);
475 for (i = 0; i < deg; i++) {
476 ptf2[i] = i;
477 }
478 for (i = LEN_LIST(src); 1 <= i; i--) {
479 s = INT_INTOBJ(ELM_LIST(src, i));
480 r = INT_INTOBJ(ELM_LIST(ran, i));
481 // deg may be smaller than s if s = r
482 if (s != r) {
483 ptf2[s - 1] = r - 1;
484 }
485 }
486 }
487 else {
488 f = NEW_TRANS4(deg);
489 ptf4 = ADDR_TRANS4(f);
490 for (i = 0; i < deg; i++) {
491 ptf4[i] = i;
492 }
493 for (i = LEN_LIST(src); 1 <= i; i--) {
494 s = INT_INTOBJ(ELM_LIST(src, i));
495 r = INT_INTOBJ(ELM_LIST(ran, i));
496 if (s != r) {
497 ptf4[s - 1] = r - 1;
498 }
499 }
500 }
501 return f;
502 }
503
504 // Returns a transformation with image <img> and flat kernel <ker> under the
505 // (unchecked) assumption that the arguments are valid and that there is such
506 // a transformation, i.e. that the maximum value in <ker> equals the length
507 // of <img>.
508
FuncTRANS_IMG_KER_NC(Obj self,Obj img,Obj ker)509 static Obj FuncTRANS_IMG_KER_NC(Obj self, Obj img, Obj ker)
510 {
511 Obj f, copy_img, copy_ker;
512 UInt2 * ptf2;
513 UInt4 * ptf4;
514 UInt i, pos, deg;
515
516 copy_img = SHALLOW_COPY_OBJ(img);
517 copy_ker = SHALLOW_COPY_OBJ(ker);
518
519 if (!IS_PLIST(copy_img)) {
520 PLAIN_LIST(copy_img);
521 }
522 if (!IS_PLIST(copy_ker)) {
523 PLAIN_LIST(copy_ker);
524 }
525 MakeImmutableNoRecurse(copy_img);
526 MakeImmutableNoRecurse(copy_ker);
527
528 deg = LEN_LIST(copy_ker);
529
530 if (deg <= 65536) {
531 f = NEW_TRANS2(deg);
532 ptf2 = ADDR_TRANS2(f);
533 for (i = 0; i < deg; i++) {
534 pos = INT_INTOBJ(ELM_PLIST(copy_ker, i + 1));
535 ptf2[i] = INT_INTOBJ(ELM_PLIST(copy_img, pos)) - 1;
536 }
537 }
538 else {
539 f = NEW_TRANS4(deg);
540 ptf4 = ADDR_TRANS4(f);
541 for (i = 0; i < deg; i++) {
542 pos = INT_INTOBJ(ELM_PLIST(copy_ker, i + 1));
543 ptf4[i] = INT_INTOBJ(ELM_PLIST(copy_img, pos)) - 1;
544 }
545 }
546
547 SET_IMG_TRANS(f, copy_img);
548 SET_KER_TRANS(f, copy_ker);
549 CHANGED_BAG(f);
550
551 return f;
552 }
553
554 // Returns an idempotent transformation with image <img> and flat kernel <ker>
555 // under the (unchecked) assumption that the arguments are valid and that
556 // there
557 // is such a transformation, i.e. that the maximum value in <ker> equals the
558 // length of <img>.
559 //
560 // Note that this does not return the same transformation as TRANS_IMG_KER_NC
561 // with the same arguments.
562
FuncIDEM_IMG_KER_NC(Obj self,Obj img,Obj ker)563 static Obj FuncIDEM_IMG_KER_NC(Obj self, Obj img, Obj ker)
564 {
565 Obj f, copy_img, copy_ker;
566 UInt2 * ptf2;
567 UInt4 * ptf4, *pttmp;
568 UInt i, j, deg, rank;
569
570 copy_img = SHALLOW_COPY_OBJ(img);
571 copy_ker = SHALLOW_COPY_OBJ(ker);
572
573 if (!IS_PLIST(copy_img)) {
574 PLAIN_LIST(copy_img);
575 }
576 if (!IS_PLIST(copy_ker)) {
577 PLAIN_LIST(copy_ker);
578 }
579 MakeImmutableNoRecurse(copy_img);
580 MakeImmutableNoRecurse(copy_ker);
581
582 deg = LEN_LIST(copy_ker);
583 rank = LEN_LIST(copy_img);
584 ResizeTmpTrans(deg);
585 pttmp = AddrTmpTrans();
586
587 // setup the lookup table
588 for (i = 0; i < rank; i++) {
589 j = INT_INTOBJ(ELM_PLIST(copy_img, i + 1));
590 pttmp[INT_INTOBJ(ELM_PLIST(copy_ker, j)) - 1] = j - 1;
591 }
592 if (deg <= 65536) {
593 f = NEW_TRANS2(deg);
594 ptf2 = ADDR_TRANS2(f);
595 pttmp = AddrTmpTrans();
596
597 for (i = 0; i < deg; i++) {
598 ptf2[i] = pttmp[INT_INTOBJ(ELM_PLIST(copy_ker, i + 1)) - 1];
599 }
600 }
601 else {
602 f = NEW_TRANS4(deg);
603 ptf4 = ADDR_TRANS4(f);
604 pttmp = AddrTmpTrans();
605
606 for (i = 0; i < deg; i++) {
607 ptf4[i] = pttmp[INT_INTOBJ(ELM_PLIST(copy_ker, i + 1)) - 1];
608 }
609 }
610
611 SET_IMG_TRANS(f, copy_img);
612 SET_KER_TRANS(f, copy_ker);
613 CHANGED_BAG(f);
614 return f;
615 }
616
617 // Returns an idempotent transformation e with ker(e) = ker(f), where <f> is a
618 // transformation.
619
FuncLEFT_ONE_TRANS(Obj self,Obj f)620 static Obj FuncLEFT_ONE_TRANS(Obj self, Obj f)
621 {
622 Obj ker, img;
623 UInt rank, n, i;
624
625 if (TNUM_OBJ(f) == T_TRANS2) {
626 rank = RANK_TRANS2(f);
627 ker = KER_TRANS(f);
628 }
629 else if (TNUM_OBJ(f) == T_TRANS4) {
630 rank = RANK_TRANS4(f);
631 ker = KER_TRANS(f);
632 }
633 else {
634 RequireTransformation("LEFT_ONE_TRANS", f);
635 return 0L;
636 }
637
638 img = NEW_PLIST(T_PLIST_CYC, rank);
639 n = 1;
640
641 for (i = 1; n <= rank; i++) {
642 if ((UInt)INT_INTOBJ(ELM_PLIST(ker, i)) == n) {
643 SET_ELM_PLIST(img, n++, INTOBJ_INT(i));
644 }
645 }
646
647 SET_LEN_PLIST(img, (Int)n - 1);
648 return FuncIDEM_IMG_KER_NC(self, img, ker);
649 }
650
651 // Returns an idempotent transformation e with im(e) = im(f), where <f> is a
652 // transformation.
653
FuncRIGHT_ONE_TRANS(Obj self,Obj f)654 static Obj FuncRIGHT_ONE_TRANS(Obj self, Obj f)
655 {
656 Obj ker, img;
657 UInt deg, len, i, j, n;
658
659 if (TNUM_OBJ(f) == T_TRANS2) {
660 deg = DEG_TRANS2(f);
661 }
662 else if (TNUM_OBJ(f) == T_TRANS4) {
663 deg = DEG_TRANS4(f);
664 }
665 else {
666 RequireTransformation("RIGHT_ONE_TRANS", f);
667 return 0L;
668 }
669
670 img = FuncIMAGE_SET_TRANS(self, f);
671 ker = NEW_PLIST(T_PLIST_CYC, deg);
672 SET_LEN_PLIST(ker, deg);
673 len = LEN_PLIST(img);
674 j = 1;
675 n = 0;
676
677 for (i = 0; i < deg; i++) {
678 if (j < len && i + 1 == (UInt)INT_INTOBJ(ELM_PLIST(img, j + 1))) {
679 j++;
680 }
681 SET_ELM_PLIST(ker, ++n, INTOBJ_INT(j));
682 }
683 return FuncIDEM_IMG_KER_NC(self, img, ker);
684 }
685
686 /*******************************************************************************
687 ** GAP level functions for degree and rank of transformations
688 *******************************************************************************/
689
690 // Returns the degree of the transformation <f>, i.e. the least value <n> such
691 // that <f> fixes [n + 1, n + 2, .. ].
692
FuncDegreeOfTransformation(Obj self,Obj f)693 static Obj FuncDegreeOfTransformation(Obj self, Obj f)
694 {
695 UInt n, i, deg;
696 const UInt2 * ptf2;
697 const UInt4 * ptf4;
698
699 if (TNUM_OBJ(f) == T_TRANS2) {
700 if (EXT_TRANS(f) == NULL) {
701 n = DEG_TRANS2(f);
702 ptf2 = CONST_ADDR_TRANS2(f);
703 if (ptf2[n - 1] != n - 1) {
704 SET_EXT_TRANS(f, INTOBJ_INT(n));
705 }
706 else {
707 deg = 0;
708 for (i = 0; i < n; i++) {
709 if (ptf2[i] > i && ptf2[i] + 1 > deg) {
710 deg = ptf2[i] + 1;
711 }
712 else if (ptf2[i] < i && i + 1 > deg) {
713 deg = i + 1;
714 }
715 }
716 SET_EXT_TRANS(f, INTOBJ_INT(deg));
717 }
718 }
719 return EXT_TRANS(f);
720 }
721 else if (TNUM_OBJ(f) == T_TRANS4) {
722 if (EXT_TRANS(f) == NULL) {
723 n = DEG_TRANS4(f);
724 ptf4 = CONST_ADDR_TRANS4(f);
725 if (ptf4[n - 1] != n - 1) {
726 SET_EXT_TRANS(f, INTOBJ_INT(n));
727 }
728 else {
729 deg = 0;
730 for (i = 0; i < n; i++) {
731 if (ptf4[i] > i && ptf4[i] + 1 > deg) {
732 deg = ptf4[i] + 1;
733 }
734 else if (ptf4[i] < i && i + 1 > deg) {
735 deg = i + 1;
736 }
737 }
738 SET_EXT_TRANS(f, INTOBJ_INT(deg));
739 }
740 }
741 return EXT_TRANS(f);
742 }
743 RequireTransformation("DegreeOfTransformation", f);
744 return 0L;
745 }
746
747 // Returns the rank of transformation, i.e. number of distinct values in
748 // [(1)f .. (n)f] where n = DegreeOfTransformation(f).
749
FuncRANK_TRANS(Obj self,Obj f)750 static Obj FuncRANK_TRANS(Obj self, Obj f)
751 {
752 if (TNUM_OBJ(f) == T_TRANS2) {
753 return SumInt(INTOBJ_INT(RANK_TRANS2(f) - DEG_TRANS2(f)),
754 FuncDegreeOfTransformation(self, f));
755 }
756 else if (TNUM_OBJ(f) == T_TRANS4) {
757 return SumInt(INTOBJ_INT(RANK_TRANS4(f) - DEG_TRANS4(f)),
758 FuncDegreeOfTransformation(self, f));
759 }
760 RequireTransformation("RANK_TRANS", f);
761 return 0L;
762 }
763
764 // Returns the rank of the transformation <f> on [1 .. n], i.e. the number of
765 // distinct values in [(1)f .. (n)f].
766
FuncRANK_TRANS_INT(Obj self,Obj f,Obj n)767 static Obj FuncRANK_TRANS_INT(Obj self, Obj f, Obj n)
768 {
769 UInt rank, i, m;
770 const UInt2 * ptf2;
771 const UInt4 * ptf4;
772 UInt4 * pttmp;
773
774 RequireNonnegativeSmallInt("RANK_TRANS_INT", n);
775
776 m = INT_INTOBJ(n);
777 if (TNUM_OBJ(f) == T_TRANS2) {
778 if (m >= DEG_TRANS2(f)) {
779 return INTOBJ_INT(RANK_TRANS2(f) - DEG_TRANS2(f) + m);
780 }
781 else {
782 pttmp = ResizeInitTmpTrans(DEG_TRANS2(f));
783 ptf2 = CONST_ADDR_TRANS2(f);
784 rank = 0;
785 for (i = 0; i < m; i++) {
786 if (pttmp[ptf2[i]] == 0) {
787 rank++;
788 pttmp[ptf2[i]] = 1;
789 }
790 }
791 return INTOBJ_INT(rank);
792 }
793 }
794 else if (TNUM_OBJ(f) == T_TRANS4) {
795 if (m >= DEG_TRANS4(f)) {
796 return INTOBJ_INT(RANK_TRANS4(f) - DEG_TRANS4(f) + m);
797 }
798 else {
799 pttmp = ResizeInitTmpTrans(DEG_TRANS4(f));
800 ptf4 = CONST_ADDR_TRANS4(f);
801 rank = 0;
802 for (i = 0; i < m; i++) {
803 if (pttmp[ptf4[i]] == 0) {
804 rank++;
805 pttmp[ptf4[i]] = 1;
806 }
807 }
808 return INTOBJ_INT(rank);
809 }
810 }
811 RequireTransformation("RANK_TRANS_INT", f);
812 return 0L;
813 }
814
815 // Returns the rank of the transformation <f> on the <list>, i.e. the number
816 // of
817 // distinct values in [(list[1])f .. (list[n])f], where <list> consists of
818 // positive ints.
819
FuncRANK_TRANS_LIST(Obj self,Obj f,Obj list)820 static Obj FuncRANK_TRANS_LIST(Obj self, Obj f, Obj list)
821 {
822 UInt rank, i, j, len, def;
823 const UInt2 * ptf2;
824 const UInt4 * ptf4;
825 UInt4 * pttmp;
826 Obj pt;
827
828 if (!IS_LIST(list)) {
829 ErrorQuit("RANK_TRANS_LIST: the second argument must be a list "
830 "(not a %s)",
831 (Int)TNAM_OBJ(list), 0L);
832 }
833
834 len = LEN_LIST(list);
835 if (TNUM_OBJ(f) == T_TRANS2) {
836 def = DEG_TRANS2(f);
837 pttmp = ResizeInitTmpTrans(def);
838 ptf2 = CONST_ADDR_TRANS2(f);
839 rank = 0;
840 for (i = 1; i <= len; i++) {
841 pt = ELM_LIST(list, i);
842 if (!IS_POS_INTOBJ(pt)) {
843 ErrorQuit(
844 "RANK_TRANS_LIST: <list> must be a "
845 "list of positive small integers (not a %s)",
846 (Int)TNAM_OBJ(pt), 0L);
847 }
848 j = INT_INTOBJ(pt) - 1;
849 if (j < def) {
850 j = ptf2[j];
851 if (pttmp[j] == 0) {
852 rank++;
853 pttmp[j] = 1;
854 }
855 }
856 else {
857 rank++;
858 }
859 }
860 return INTOBJ_INT(rank);
861 }
862 else if (TNUM_OBJ(f) == T_TRANS4) {
863 def = DEG_TRANS4(f);
864 pttmp = ResizeInitTmpTrans(def);
865 ptf4 = CONST_ADDR_TRANS4(f);
866 rank = 0;
867 for (i = 1; i <= len; i++) {
868 pt = ELM_LIST(list, i);
869 if (!IS_POS_INTOBJ(pt)) {
870 ErrorQuit(
871 "RANK_TRANS_LIST: <list> must be a "
872 "list of positive small integers (not a %s)",
873 (Int)TNAM_OBJ(pt), 0L);
874 }
875 j = INT_INTOBJ(pt) - 1;
876 if (j < def) {
877 j = ptf4[j];
878 if (pttmp[j] == 0) {
879 rank++;
880 pttmp[j] = 1;
881 }
882 }
883 else {
884 rank++;
885 }
886 }
887 return INTOBJ_INT(rank);
888 }
889
890 RequireTransformation("RANK_TRANS_LIST", f);
891 return 0L;
892 }
893
894 /*******************************************************************************
895 ** GAP level functions for the kernel and preimages of a transformation
896 *******************************************************************************/
897
898 // Returns the flat kernel of transformation on
899 // [1 .. DegreeOfTransformation(f)].
900
FuncFLAT_KERNEL_TRANS(Obj self,Obj f)901 static Obj FuncFLAT_KERNEL_TRANS(Obj self, Obj f)
902 {
903
904 if (TNUM_OBJ(f) == T_TRANS2) {
905 if (KER_TRANS(f) == NULL) {
906 INIT_TRANS2(f);
907 }
908 return KER_TRANS(f);
909 }
910 else if (TNUM_OBJ(f) == T_TRANS4) {
911 if (KER_TRANS(f) == NULL) {
912 INIT_TRANS4(f);
913 }
914 return KER_TRANS(f);
915 }
916
917 RequireTransformation("FLAT_KERNEL_TRANS", f);
918 return 0L;
919 }
920
921 // Returns the flat kernel of the transformation <f> on [1 .. n].
922
FuncFLAT_KERNEL_TRANS_INT(Obj self,Obj f,Obj n)923 static Obj FuncFLAT_KERNEL_TRANS_INT(Obj self, Obj f, Obj n)
924 {
925 Obj newObj, *ptnew;
926 const Obj *ptker;
927 UInt deg, m, i;
928
929 RequireNonnegativeSmallInt("FLAT_KERNEL_TRANS_INT", n);
930
931 m = INT_INTOBJ(n);
932 if (TNUM_OBJ(f) == T_TRANS2) {
933 if (KER_TRANS(f) == NULL) {
934 INIT_TRANS2(f);
935 }
936 deg = DEG_TRANS2(f);
937 if (m == deg) {
938 return KER_TRANS(f);
939 }
940 else if (m == 0) {
941 return NewEmptyPlist();
942 }
943 else {
944 newObj = NEW_PLIST(T_PLIST_CYC_NSORT, m);
945 SET_LEN_PLIST(newObj, m);
946
947 ptker = CONST_ADDR_OBJ(KER_TRANS(f)) + 1;
948 ptnew = ADDR_OBJ(newObj) + 1;
949
950 // copy the kernel set up to minimum of m, deg
951 if (m < deg) {
952 for (i = 0; i < m; i++) {
953 *ptnew++ = *ptker++;
954 }
955 }
956 else {
957 // m > deg
958 for (i = 0; i < deg; i++) {
959 *ptnew++ = *ptker++;
960 }
961 // we must now add another (m - deg) points,
962 // starting with the class number (rank + 1)
963 for (i = 1; i <= m - deg; i++) {
964 *ptnew++ = INTOBJ_INT(i + RANK_TRANS2(f));
965 }
966 }
967 return newObj;
968 }
969 }
970 else if (TNUM_OBJ(f) == T_TRANS4) {
971
972 if (KER_TRANS(f) == NULL) {
973 INIT_TRANS4(f);
974 }
975 deg = DEG_TRANS4(f);
976 if (m == deg) {
977 return KER_TRANS(f);
978 }
979 else if (m == 0) {
980 return NewEmptyPlist();
981 }
982 else {
983 newObj = NEW_PLIST(T_PLIST_CYC_NSORT, m);
984 SET_LEN_PLIST(newObj, m);
985
986 ptker = CONST_ADDR_OBJ(KER_TRANS(f)) + 1;
987 ptnew = ADDR_OBJ(newObj) + 1;
988
989 // copy the kernel set up to minimum of m, deg
990 if (m < deg) {
991 for (i = 0; i < m; i++) {
992 *ptnew++ = *ptker++;
993 }
994 }
995 else {
996 // m > deg
997 for (i = 0; i < deg; i++) {
998 *ptnew++ = *ptker++;
999 }
1000 // we must now add another (m - deg) points,
1001 // starting with the class number (rank + 1)
1002 for (i = 1; i <= m - deg; i++) {
1003 *ptnew++ = INTOBJ_INT(i + RANK_TRANS4(f));
1004 }
1005 }
1006 return newObj;
1007 }
1008 }
1009 RequireTransformation("FLAT_KERNEL_TRANS_INT", f);
1010 return 0L;
1011 }
1012
1013 // Returns the kernel of a transformation <f> as a partition of [1 .. n].
1014
FuncKERNEL_TRANS(Obj self,Obj f,Obj n)1015 static Obj FuncKERNEL_TRANS(Obj self, Obj f, Obj n)
1016 {
1017 Obj ker;
1018 UInt i, j, deg, nr, m, rank, min;
1019 UInt4 * pttmp;
1020
1021 RequireNonnegativeSmallInt("KERNEL_TRANS", n);
1022 RequireTransformation("KERNEL_TRANS", f);
1023
1024 m = INT_INTOBJ(n);
1025
1026 // special case for the identity
1027 if (m == 0) {
1028 ker = NewEmptyPlist();
1029 return ker;
1030 }
1031
1032 deg = DEG_TRANS(f);
1033 rank = RANK_TRANS(f);
1034 min = MIN(m, deg);
1035 nr = (min == m ? rank : rank + m - deg); // the number of classes
1036
1037 ker = NEW_PLIST(T_PLIST_HOM_SSORT, nr);
1038 pttmp = ResizeInitTmpTrans(nr);
1039
1040 // RANK_TRANS(f) should install KER_TRANS(f)
1041 assert(KER_TRANS(f) != NULL);
1042
1043 nr = 0;
1044 // read off flat kernel
1045 for (i = 0; i < min; i++) {
1046 j = INT_INTOBJ(ELM_PLIST(KER_TRANS(f), i + 1));
1047 if (pttmp[j - 1] == 0) {
1048 nr++;
1049 SET_ELM_PLIST(ker, j, NEW_PLIST(T_PLIST_CYC_SSORT, 1));
1050 CHANGED_BAG(ker);
1051 pttmp = AddrTmpTrans();
1052 }
1053 AssPlist(ELM_PLIST(ker, j), (Int)++pttmp[j - 1], INTOBJ_INT(i + 1));
1054 pttmp = AddrTmpTrans();
1055 }
1056
1057 // add trailing singletons, if any
1058 for (i = deg; i < m; i++) {
1059 SET_ELM_PLIST(ker, ++nr, NEW_PLIST(T_PLIST_CYC_SSORT, 1));
1060 SET_LEN_PLIST(ELM_PLIST(ker, nr), 1);
1061 SET_ELM_PLIST(ELM_PLIST(ker, nr), 1, INTOBJ_INT(i + 1));
1062 CHANGED_BAG(ker);
1063 }
1064 SET_LEN_PLIST(ker, (Int)nr);
1065 return ker;
1066 }
1067
1068 // Returns the set (pt)f ^ -1.
1069
FuncPREIMAGES_TRANS_INT(Obj self,Obj f,Obj pt)1070 static Obj FuncPREIMAGES_TRANS_INT(Obj self, Obj f, Obj pt)
1071 {
1072 UInt deg, nr, i, j;
1073 Obj out;
1074
1075 RequireTransformation("PREIMAGES_TRANS_INT", f);
1076 i = GetPositiveSmallInt("PREIMAGES_TRANS_INT", pt) - 1;
1077
1078 deg = DEG_TRANS(f);
1079
1080 if (i >= deg) {
1081 out = NEW_PLIST(T_PLIST_CYC, 1);
1082 SET_LEN_PLIST(out, 1);
1083 SET_ELM_PLIST(out, 1, pt);
1084 return out;
1085 }
1086
1087 out = NEW_PLIST(T_PLIST_CYC_SSORT, 0);
1088 nr = 0;
1089
1090 if (TNUM_OBJ(f) == T_TRANS2) {
1091 for (j = 0; j < deg; j++) {
1092 if ((CONST_ADDR_TRANS2(f))[j] == i) {
1093 AssPlist(out, ++nr, INTOBJ_INT(j + 1));
1094 }
1095 }
1096 }
1097 else {
1098 for (j = 0; j < deg; j++) {
1099 if ((CONST_ADDR_TRANS4(f))[j] == i) {
1100 AssPlist(out, ++nr, INTOBJ_INT(j + 1));
1101 }
1102 }
1103 }
1104
1105 if (nr == 0) {
1106 RetypeBag(out, T_PLIST_EMPTY);
1107 SET_LEN_PLIST(out, 0);
1108 }
1109
1110 return out;
1111 }
1112
1113 /*******************************************************************************
1114 ** GAP level functions for the image sets and lists of a transformation
1115 *******************************************************************************/
1116
1117 // Returns the duplicate free list of images of the transformation f on
1118 // [1 .. n] where n = DEG_TRANS(f). Note that this might not be sorted.
1119
FuncUNSORTED_IMAGE_SET_TRANS(Obj self,Obj f)1120 static Obj FuncUNSORTED_IMAGE_SET_TRANS(Obj self, Obj f)
1121 {
1122
1123 if (TNUM_OBJ(f) == T_TRANS2) {
1124 if (IMG_TRANS(f) == NULL) {
1125 INIT_TRANS2(f);
1126 }
1127 return IMG_TRANS(f);
1128 }
1129 else if (TNUM_OBJ(f) == T_TRANS4) {
1130 if (IMG_TRANS(f) == NULL) {
1131 INIT_TRANS4(f);
1132 }
1133 return IMG_TRANS(f);
1134 }
1135 RequireTransformation("UNSORTED_IMAGE_SET_TRANS", f);
1136 return 0L;
1137 }
1138
1139 // Returns the image set of the transformation f on [1 .. n] where n =
1140 // DegreeOfTransformation(f).
1141
FuncIMAGE_SET_TRANS(Obj self,Obj f)1142 static Obj FuncIMAGE_SET_TRANS(Obj self, Obj f)
1143 {
1144
1145 Obj out = FuncUNSORTED_IMAGE_SET_TRANS(self, f);
1146
1147 if (!IS_SSORT_LIST(out)) {
1148 SortPlistByRawObj(out);
1149 RetypeBagSM(out, T_PLIST_CYC_SSORT);
1150 return out;
1151 }
1152 return out;
1153 }
1154
1155 // Returns the image set of the transformation f on [1 .. n].
1156
FuncIMAGE_SET_TRANS_INT(Obj self,Obj f,Obj n)1157 static Obj FuncIMAGE_SET_TRANS_INT(Obj self, Obj f, Obj n)
1158 {
1159 Obj im, newObj;
1160 UInt deg, m, len, i, j, rank;
1161 Obj * ptnew;
1162 const Obj *ptim;
1163 UInt4 * pttmp;
1164 const UInt4 * ptf4;
1165 const UInt2 * ptf2;
1166
1167 RequireNonnegativeSmallInt("IMAGE_SET_TRANS_INT", n);
1168 RequireTransformation("IMAGE_SET_TRANS_INT", f);
1169
1170 m = INT_INTOBJ(n);
1171 deg = DEG_TRANS(f);
1172
1173 if (m == deg) {
1174 return FuncIMAGE_SET_TRANS(self, f);
1175 }
1176 else if (m == 0) {
1177 return NewImmutableEmptyPlist();
1178 }
1179 else if (m < deg) {
1180 newObj = NEW_PLIST_IMM(T_PLIST_CYC, m);
1181 pttmp = ResizeInitTmpTrans(deg);
1182
1183 if (TNUM_OBJ(f) == T_TRANS2) {
1184 ptf2 = CONST_ADDR_TRANS2(f);
1185 rank = 0;
1186 for (i = 0; i < m; i++) {
1187 j = ptf2[i];
1188 if (pttmp[j] == 0) {
1189 pttmp[j] = ++rank;
1190 SET_ELM_PLIST(newObj, rank, INTOBJ_INT(j + 1));
1191 }
1192 }
1193 }
1194 else {
1195 ptf4 = CONST_ADDR_TRANS4(f);
1196 rank = 0;
1197 for (i = 0; i < m; i++) {
1198 j = ptf4[i];
1199 if (pttmp[j] == 0) {
1200 pttmp[j] = ++rank;
1201 SET_ELM_PLIST(newObj, rank, INTOBJ_INT(j + 1));
1202 }
1203 }
1204 }
1205 SHRINK_PLIST(newObj, (Int)rank);
1206 SET_LEN_PLIST(newObj, (Int)rank);
1207 SortPlistByRawObj(newObj);
1208 RetypeBagSM(newObj, T_PLIST_CYC_SSORT);
1209 }
1210 else {
1211 // m > deg and so m is at least 1!
1212 im = FuncIMAGE_SET_TRANS(self, f);
1213 len = LEN_PLIST(im);
1214 newObj = NEW_PLIST(T_PLIST_CYC_SSORT, m - deg + len);
1215 SET_LEN_PLIST(newObj, m - deg + len);
1216
1217 ptnew = ADDR_OBJ(newObj) + 1;
1218 ptim = CONST_ADDR_OBJ(im) + 1;
1219
1220 // copy the image set
1221 for (i = 0; i < len; i++) {
1222 *ptnew++ = *ptim++;
1223 }
1224 // add newObj points
1225 for (i = deg + 1; i <= m; i++) {
1226 *ptnew++ = INTOBJ_INT(i);
1227 }
1228 }
1229 return newObj;
1230 }
1231
1232 // Returns the image list [(1)f .. (n)f] of the transformation f.
1233
FuncIMAGE_LIST_TRANS_INT(Obj self,Obj f,Obj n)1234 static Obj FuncIMAGE_LIST_TRANS_INT(Obj self, Obj f, Obj n)
1235 {
1236 const UInt2 * ptf2;
1237 const UInt4 * ptf4;
1238 UInt i, deg, m;
1239 Obj out;
1240
1241 RequireNonnegativeSmallInt("IMAGE_LIST_TRANS_INT", n);
1242 RequireTransformation("IMAGE_LIST_TRANS_INT", f);
1243
1244 m = INT_INTOBJ(n);
1245
1246 if (m == 0) {
1247 out = NewImmutableEmptyPlist();
1248 return out;
1249 }
1250
1251 out = NEW_PLIST_IMM(T_PLIST_CYC, m);
1252
1253 if (TNUM_OBJ(f) == T_TRANS2) {
1254 ptf2 = CONST_ADDR_TRANS2(f);
1255 deg = MIN(DEG_TRANS2(f), m);
1256 for (i = 0; i < deg; i++) {
1257 SET_ELM_PLIST(out, i + 1, INTOBJ_INT(ptf2[i] + 1));
1258 }
1259 }
1260 else {
1261 ptf4 = CONST_ADDR_TRANS4(f);
1262 deg = MIN(DEG_TRANS4(f), m);
1263 for (i = 0; i < deg; i++) {
1264 SET_ELM_PLIST(out, i + 1, INTOBJ_INT(ptf4[i] + 1));
1265 }
1266 }
1267 for (; i < m; i++)
1268 SET_ELM_PLIST(out, i + 1, INTOBJ_INT(i + 1));
1269 SET_LEN_PLIST(out, (Int)m);
1270 return out;
1271 }
1272
1273 /*******************************************************************************
1274 ** GAP level functions for properties of transformations
1275 *******************************************************************************/
1276
1277 // Test if a transformation is the identity.
1278
FuncIS_ID_TRANS(Obj self,Obj f)1279 static Obj FuncIS_ID_TRANS(Obj self, Obj f)
1280 {
1281 const UInt2 * ptf2;
1282 const UInt4 * ptf4;
1283 UInt deg, i;
1284
1285 if (TNUM_OBJ(f) == T_TRANS2) {
1286 ptf2 = CONST_ADDR_TRANS2(f);
1287 deg = DEG_TRANS2(f);
1288 for (i = 0; i < deg; i++) {
1289 if (ptf2[i] != i) {
1290 return False;
1291 }
1292 }
1293 return True;
1294 }
1295 else if (TNUM_OBJ(f) == T_TRANS4) {
1296 ptf4 = CONST_ADDR_TRANS4(f);
1297 deg = DEG_TRANS4(f);
1298 for (i = 0; i < deg; i++) {
1299 if (ptf4[i] != i) {
1300 return False;
1301 }
1302 }
1303 return True;
1304 }
1305 RequireTransformation("IS_ID_TRANS", f);
1306 return 0L;
1307 }
1308
1309 // Returns true if the transformation <f> is an idempotent and false if it is
1310 // not.
1311
FuncIS_IDEM_TRANS(Obj self,Obj f)1312 static Obj FuncIS_IDEM_TRANS(Obj self, Obj f)
1313 {
1314 const UInt2 * ptf2;
1315 const UInt4 * ptf4;
1316 UInt deg, i;
1317
1318 if (TNUM_OBJ(f) == T_TRANS2) {
1319 deg = DEG_TRANS2(f);
1320 ptf2 = CONST_ADDR_TRANS2(f);
1321 for (i = 0; i < deg; i++) {
1322 if (ptf2[ptf2[i]] != ptf2[i]) {
1323 return False;
1324 }
1325 }
1326 return True;
1327 }
1328 else if (TNUM_OBJ(f) == T_TRANS4) {
1329 deg = DEG_TRANS4(f);
1330 ptf4 = CONST_ADDR_TRANS4(f);
1331 for (i = 0; i < deg; i++) {
1332 if (ptf4[ptf4[i]] != ptf4[i]) {
1333 return False;
1334 }
1335 }
1336 return True;
1337 }
1338 RequireTransformation("IS_IDEM_TRANS", f);
1339 return 0L;
1340 }
1341
1342 /*******************************************************************************
1343 ** GAP level functions for attributes of transformations
1344 *******************************************************************************/
1345
1346 // Returns the least m and r such that f ^ (m + r) = f ^ m, where f is a
1347 // transformation.
1348
FuncIndexPeriodOfTransformation(Obj self,Obj f)1349 static Obj FuncIndexPeriodOfTransformation(Obj self, Obj f)
1350 {
1351 const UInt2 * ptf2;
1352 const UInt4 * ptf4;
1353 UInt4 * seen;
1354 UInt deg, i, pt, dist, pow, len, last_pt;
1355 Obj ord, out;
1356 Int cyc;
1357
1358 RequireTransformation("IndexPeriodOfTransformation", f);
1359
1360 deg = INT_INTOBJ(FuncDegreeOfTransformation(self, f));
1361
1362 if (deg == 0) {
1363 out = NEW_PLIST(T_PLIST_CYC, 2);
1364
1365 SET_LEN_PLIST(out, 2);
1366 SET_ELM_PLIST(out, 1, INTOBJ_INT(1));
1367 SET_ELM_PLIST(out, 2, INTOBJ_INT(1));
1368 return out;
1369 }
1370
1371 // seen[pt] = 0 -> haven't seen pt before
1372 //
1373 // seen[pt] = d where (1 <= d <= deg)
1374 // -> pt belongs to a component we've seen before and (pt)f ^ (d - 1)
1375 // belongs to a cycle
1376 //
1377 // seen[pt] = deg + 1 -> pt belongs to a component not seen before
1378
1379 seen = ResizeInitTmpTrans(deg);
1380
1381 pow = 2;
1382 ord = INTOBJ_INT(1);
1383
1384 if (TNUM_OBJ(f) == T_TRANS2) {
1385 ptf2 = CONST_ADDR_TRANS2(f);
1386 for (i = 0; i < deg; i++) {
1387 if (seen[i] == 0) {
1388 len = 0;
1389 // repeatedly apply f to pt until we see something we've seen
1390 // already
1391 for (pt = i; seen[pt] == 0; pt = ptf2[pt], len++) {
1392 seen[pt] = deg + 1;
1393 }
1394 last_pt = pt;
1395 if (seen[pt] <= deg) {
1396 // pt belongs to a component we've seen before
1397 dist = seen[pt] + len;
1398 // the distance of i from the cycle in its component + 1
1399 }
1400 else {
1401 // pt belongs to a component we've not seen before
1402
1403 for (cyc = 0; seen[pt] == deg + 1; pt = ptf2[pt], cyc++) {
1404 // go around the cycle again and set the value of
1405 // seen,
1406 // and get the length of the cycle
1407 seen[pt] = 1;
1408 }
1409
1410 ord = LcmInt(ord, INTOBJ_INT(cyc));
1411
1412 // the distance of i from the cycle in its component + 1
1413 dist = len - cyc + 1;
1414
1415 // update bag pointers, in case a garbage collection happened
1416 ptf2 = CONST_ADDR_TRANS2(f);
1417 seen = AddrTmpTrans();
1418 }
1419 if (dist > pow) {
1420 pow = dist;
1421 }
1422 // record the distances of the points from the cycle
1423 for (pt = i; pt != last_pt; pt = ptf2[pt]) {
1424 seen[pt] = dist--;
1425 }
1426 }
1427 }
1428 }
1429 else {
1430 ptf4 = CONST_ADDR_TRANS4(f);
1431 for (i = 0; i < deg; i++) {
1432 if (seen[i] == 0) {
1433 len = 0;
1434 // repeatedly apply f to pt until we see something we've seen
1435 // already
1436 for (pt = i; seen[pt] == 0; pt = ptf4[pt], len++) {
1437 seen[pt] = deg + 1;
1438 }
1439 last_pt = pt;
1440 if (seen[pt] <= deg) {
1441 // pt belongs to a component we've seen before
1442 dist = seen[pt] + len;
1443 // the distance of i from the cycle in its component + 1
1444 }
1445 else {
1446 // pt belongs to a component we've not seen before
1447
1448 for (cyc = 0; seen[pt] == deg + 1; pt = ptf4[pt], cyc++) {
1449 // go around the cycle again and set the value of
1450 // seen,
1451 // and get the length of the cycle
1452 seen[pt] = 1;
1453 }
1454
1455 ord = LcmInt(ord, INTOBJ_INT(cyc));
1456
1457 // the distance of i from the cycle in its component + 1
1458 dist = len - cyc + 1;
1459
1460 // update bag pointers, in case a garbage collection happened
1461 ptf4 = CONST_ADDR_TRANS4(f);
1462 seen = AddrTmpTrans();
1463 }
1464 if (dist > pow) {
1465 pow = dist;
1466 }
1467 // record the distances of the points from the cycle
1468 for (pt = i; pt != last_pt; pt = ptf4[pt]) {
1469 seen[pt] = dist--;
1470 }
1471 }
1472 }
1473 }
1474
1475 out = NEW_PLIST(T_PLIST_CYC, 2);
1476
1477 SET_LEN_PLIST(out, 2);
1478 SET_ELM_PLIST(out, 1, INTOBJ_INT(--pow));
1479 SET_ELM_PLIST(out, 2, ord);
1480 return out;
1481 }
1482
1483 // Returns the least integer m such that f ^ m is an idempotent.
1484
FuncSMALLEST_IDEM_POW_TRANS(Obj self,Obj f)1485 static Obj FuncSMALLEST_IDEM_POW_TRANS(Obj self, Obj f)
1486 {
1487 Obj x, ind, per, pow;
1488
1489 x = FuncIndexPeriodOfTransformation(self, f);
1490 ind = ELM_PLIST(x, 1);
1491 per = ELM_PLIST(x, 2);
1492 pow = per;
1493 while (LtInt(pow, ind)) {
1494 pow = SumInt(pow, per);
1495 }
1496 return pow;
1497 }
1498
1499 /*******************************************************************************
1500 ** GAP level functions for regularity of transformations
1501 *******************************************************************************/
1502
1503 // Returns True if the transformation or list <t> is injective on the list
1504 // <l>.
1505
FuncIsInjectiveListTrans(Obj self,Obj l,Obj t)1506 static Obj FuncIsInjectiveListTrans(Obj self, Obj l, Obj t)
1507 {
1508 UInt n, i, j;
1509 const UInt2 * ptt2;
1510 const UInt4 * ptt4;
1511 UInt4 * pttmp = 0L;
1512 Obj val;
1513
1514 if (!IS_LIST(l)) {
1515 ErrorQuit("the first argument must be a list (not a %s)",
1516 (Int)TNAM_OBJ(l), 0L);
1517 }
1518 else if (!IS_TRANS(t) && !IS_LIST(t)) {
1519 ErrorQuit("the second argument must be a transformation or a list "
1520 "(not a %s)",
1521 (Int)TNAM_OBJ(t), 0L);
1522 }
1523 // init buffer
1524 n = (IS_TRANS(t) ? DEG_TRANS(t) : LEN_LIST(t));
1525 pttmp = ResizeInitTmpTrans(n);
1526
1527 if (TNUM_OBJ(t) == T_TRANS2) {
1528 ptt2 = CONST_ADDR_TRANS2(t);
1529 for (i = LEN_LIST(l); i >= 1; i--) {
1530 val = ELM_LIST(l, i);
1531 if (!IS_POS_INTOBJ(val)) {
1532 ErrorQuit(
1533 "the entries of the first argument must be positive "
1534 "integers (not a %s)",
1535 (Int)TNAM_OBJ(val), 0L);
1536 }
1537 j = INT_INTOBJ(val);
1538 if (j <= n) {
1539 if (pttmp[ptt2[j - 1]] != 0) {
1540 return False;
1541 }
1542 pttmp[ptt2[j - 1]] = 1;
1543 }
1544 }
1545 }
1546 else if (TNUM_OBJ(t) == T_TRANS4) {
1547 ptt4 = CONST_ADDR_TRANS4(t);
1548 for (i = LEN_LIST(l); i >= 1; i--) {
1549 val = ELM_LIST(l, i);
1550 if (!IS_POS_INTOBJ(val)) {
1551 ErrorQuit(
1552 "the entries of the first argument must be positive "
1553 "integers (not a %s)",
1554 (Int)TNAM_OBJ(val), 0L);
1555 }
1556 j = INT_INTOBJ(val);
1557 if (j <= n) {
1558 if (pttmp[ptt4[j - 1]] != 0) {
1559 return False;
1560 }
1561 pttmp[ptt4[j - 1]] = 1;
1562 }
1563 }
1564 }
1565 else {
1566 // t is a list, first we check it describes a transformation
1567 for (i = 1; i <= n; i++) {
1568 val = ELM_LIST(t, i);
1569 if (!IS_POS_INTOBJ(val)) {
1570 ErrorQuit(
1571 "the second argument must consist of positive integers "
1572 "(not a %s)",
1573 (Int)TNAM_OBJ(val), 0L);
1574 }
1575 else if (INT_INTOBJ(val) > n) {
1576 ErrorQuit(
1577 "the second argument must consist of positive integers "
1578 "in the range [1 .. %d]",
1579 (Int)n, 0L);
1580 }
1581 }
1582 for (i = LEN_LIST(l); i >= 1; i--) {
1583 val = ELM_LIST(l, i);
1584 if (!IS_POS_INTOBJ(val)) {
1585 ErrorQuit(
1586 "the entries of the first argument must be positive "
1587 "integers (not a %s)",
1588 (Int)TNAM_OBJ(val), 0L);
1589 }
1590 j = INT_INTOBJ(val);
1591 if (j <= n) {
1592 if (pttmp[INT_INTOBJ(ELM_LIST(t, j)) - 1] != 0) {
1593 return False;
1594 }
1595 pttmp[INT_INTOBJ(ELM_LIST(t, j)) - 1] = 1;
1596 }
1597 }
1598 }
1599 return True;
1600 }
1601
1602 // Returns a transformation g such that transformation f * g * f = f and
1603 // g * f * g = g, where f is a transformation.
1604
FuncInverseOfTransformation(Obj self,Obj f)1605 static Obj FuncInverseOfTransformation(Obj self, Obj f)
1606 {
1607 const UInt2 * ptf2;
1608 const UInt4 * ptf4;
1609 UInt2 * ptg2;
1610 UInt4 * ptg4;
1611 UInt deg, i;
1612 Obj g;
1613
1614 RequireTransformation("InverseOfTransformation", f);
1615 if (FuncIS_ID_TRANS(self, f) == True) {
1616 return f;
1617 }
1618
1619 if (TNUM_OBJ(f) == T_TRANS2) {
1620 deg = DEG_TRANS2(f);
1621 g = NEW_TRANS2(deg);
1622 ptf2 = CONST_ADDR_TRANS2(f);
1623 ptg2 = ADDR_TRANS2(g);
1624 for (i = 0; i < deg; i++) {
1625 ptg2[i] = 0;
1626 }
1627 for (i = deg - 1; i > 0; i--) {
1628 ptg2[ptf2[i]] = i;
1629 }
1630 // to ensure that 1 is in the image and so rank of g equals that of f
1631 ptg2[ptf2[0]] = 0;
1632 }
1633 else {
1634 deg = DEG_TRANS4(f);
1635 g = NEW_TRANS4(deg);
1636 ptf4 = CONST_ADDR_TRANS4(f);
1637 ptg4 = ADDR_TRANS4(g);
1638 for (i = 0; i < deg; i++) {
1639 ptg4[i] = 0;
1640 }
1641 for (i = deg - 1; i > 0; i--) {
1642 ptg4[ptf4[i]] = i;
1643 }
1644 // to ensure that 1 is in the image and so rank of g equals that of f
1645 ptg4[ptf4[0]] = 0;
1646 }
1647 return g;
1648 }
1649
1650 /*******************************************************************************
1651 ** GAP level functions for actions of transformations
1652 *******************************************************************************/
1653
1654 // Returns the flat kernel of a transformation obtained by multiplying <f> by
1655 // any transformation with kernel equal to <ker>. If the argument <ker> =
1656 // [0], then the flat kernel of <f> on [1 .. <n>] is returned. Otherwise, the
1657 // argument <n> is redundant.
1658
1659 // FIXME this should just always return a flat kernel of length <n>, the
1660 // special case should be removed, and [0] should be replaced by [1 .. n] in
1661 // the Semigroup package.
1662
FuncON_KERNEL_ANTI_ACTION(Obj self,Obj ker,Obj f,Obj n)1663 static Obj FuncON_KERNEL_ANTI_ACTION(Obj self, Obj ker, Obj f, Obj n)
1664 {
1665 const UInt2 * ptf2;
1666 const UInt4 * ptf4;
1667 UInt4 * pttmp;
1668 UInt deg, i, j, rank, len;
1669 Obj out;
1670
1671 assert(IS_LIST(ker));
1672 assert(IS_INTOBJ(n));
1673
1674 len = LEN_LIST(ker);
1675 if (len == 1 && INT_INTOBJ(ELM_LIST(ker, 1)) == 0) {
1676 return FuncFLAT_KERNEL_TRANS_INT(self, f, n);
1677 }
1678
1679 RequireTransformation("ON_KERNEL_ANTI_ACTION", f);
1680
1681 rank = 1;
1682 deg = INT_INTOBJ(FuncDegreeOfTransformation(self, f));
1683 if (len < deg) {
1684 ErrorQuit("ON_KERNEL_ANTI_ACTION: the length of the first "
1685 "argument must be at least %d",
1686 (Int)deg, 0L);
1687 }
1688
1689 if (len == 0) {
1690 out = NewImmutableEmptyPlist();
1691 return out;
1692 }
1693 out = NEW_PLIST_IMM(T_PLIST_CYC, len);
1694 SET_LEN_PLIST(out, len);
1695 pttmp = ResizeInitTmpTrans(len);
1696
1697 if (TNUM_OBJ(f) == T_TRANS2) {
1698 ptf2 = CONST_ADDR_TRANS2(f);
1699 for (i = 0; i < deg; i++) {
1700 // <f> then <g> with ker(<g>) = <ker>
1701 j = INT_INTOBJ(ELM_LIST(ker, ptf2[i] + 1)) - 1; // f first!
1702 if (pttmp[j] == 0) {
1703 pttmp[j] = rank++;
1704 }
1705 SET_ELM_PLIST(out, i + 1, INTOBJ_INT(pttmp[j]));
1706 }
1707 }
1708 else /* if (TNUM_OBJ(f) == T_TRANS4) */ {
1709 ptf4 = CONST_ADDR_TRANS4(f);
1710 for (i = 0; i < deg; i++) {
1711 // <f> then <g> with ker(<g>) = <ker>
1712 j = INT_INTOBJ(ELM_LIST(ker, ptf4[i] + 1)) - 1; // f first!
1713 if (pttmp[j] == 0) {
1714 pttmp[j] = rank++;
1715 }
1716 SET_ELM_PLIST(out, i + 1, INTOBJ_INT(pttmp[j]));
1717 }
1718 }
1719
1720 i++;
1721 for (; i <= len; i++) {
1722 // just <ker>
1723 j = INT_INTOBJ(ELM_LIST(ker, i)) - 1;
1724 if (pttmp[j] == 0) {
1725 pttmp[j] = rank++;
1726 }
1727 SET_ELM_PLIST(out, i, INTOBJ_INT(pttmp[j]));
1728 }
1729 return out;
1730 }
1731
1732 /*******************************************************************************
1733 ** GAP level functions for changing representation of a permutation to a
1734 ** transformation
1735 *******************************************************************************/
1736
1737 // Returns a transformation <f> such that (i)f = (i)p for all i <= n where <p>
1738 // is a permutation <p> and <n> is a positive integer. Note that the returned
1739 // transformation is not necessarily a permutation (mathematically), when n is
1740 // less than the largest moved point of p.
1741
FuncAS_TRANS_PERM_INT(Obj self,Obj p,Obj deg)1742 static Obj FuncAS_TRANS_PERM_INT(Obj self, Obj p, Obj deg)
1743 {
1744 const UInt2 *ptp2;
1745 UInt2 *ptf2;
1746 const UInt4 *ptp4;
1747 UInt4 *ptf4;
1748 Obj f;
1749 UInt def, dep, i, min, n;
1750
1751 RequireNonnegativeSmallInt("AS_TRANS_PERM_INT", deg);
1752 RequirePermutation("AS_TRANS_PERM_INT", p);
1753
1754 n = INT_INTOBJ(deg);
1755
1756 if (n == 0) {
1757 return IdentityTrans;
1758 }
1759
1760 // find the degree of f
1761 def = n;
1762 dep = (TNUM_OBJ(p) == T_PERM2 ? DEG_PERM2(p) : DEG_PERM4(p));
1763
1764 if (def < dep) {
1765 min = def;
1766 if (TNUM_OBJ(p) == T_PERM2) {
1767 ptp2 = CONST_ADDR_PERM2(p);
1768 for (i = 0; i < n; i++) {
1769 if (ptp2[i] + 1 > def) {
1770 def = ptp2[i] + 1;
1771 }
1772 }
1773 }
1774 else {
1775 ptp4 = CONST_ADDR_PERM4(p);
1776 for (i = 0; i < n; i++) {
1777 if (ptp4[i] + 1 > def) {
1778 def = ptp4[i] + 1;
1779 }
1780 }
1781 }
1782 }
1783 else {
1784 min = dep;
1785 def = dep; // no point in defining <f> to have lots of trailing
1786 // fixed points
1787 }
1788
1789 if (def <= 65536) {
1790 f = NEW_TRANS2(def);
1791 ptf2 = ADDR_TRANS2(f);
1792
1793 if (TNUM_OBJ(p) == T_PERM2) {
1794 ptp2 = CONST_ADDR_PERM2(p);
1795 for (i = 0; i < min; i++) {
1796 ptf2[i] = ptp2[i];
1797 }
1798 }
1799 else { // TNUM_OBJ(p) == T_PERM4
1800 ptp4 = CONST_ADDR_PERM4(p);
1801 for (i = 0; i < min; i++) {
1802 ptf2[i] = ptp4[i];
1803 }
1804 }
1805 for (; i < def; i++) {
1806 ptf2[i] = i;
1807 }
1808 }
1809 else { // dep >= def > 65536
1810 f = NEW_TRANS4(def);
1811 ptf4 = ADDR_TRANS4(f);
1812 assert(TNUM_OBJ(p) == T_PERM4);
1813 ptp4 = CONST_ADDR_PERM4(p);
1814 for (i = 0; i < min; i++) {
1815 ptf4[i] = ptp4[i];
1816 }
1817 for (; i < def; i++) {
1818 ptf4[i] = i;
1819 }
1820 }
1821
1822 return f;
1823 }
1824
1825 // Returns a transformation <f> such that (i)f = (i)p for all i <= n where <p>
1826 // is a permutation <p> and <n> is the largest moved point of <p>.
1827
FuncAS_TRANS_PERM(Obj self,Obj p)1828 static Obj FuncAS_TRANS_PERM(Obj self, Obj p)
1829 {
1830 const UInt2 * ptPerm2;
1831 const UInt4 * ptPerm4;
1832 UInt sup;
1833
1834 RequirePermutation("AS_TRANS_PERM", p);
1835
1836 // find largest moved point
1837 if (TNUM_OBJ(p) == T_PERM2) {
1838 ptPerm2 = CONST_ADDR_PERM2(p);
1839 for (sup = DEG_PERM2(p); 1 <= sup; sup--) {
1840 if (ptPerm2[sup - 1] != sup - 1) {
1841 break;
1842 }
1843 }
1844 return FuncAS_TRANS_PERM_INT(self, p, INTOBJ_INT(sup));
1845 }
1846 else {
1847 ptPerm4 = CONST_ADDR_PERM4(p);
1848 for (sup = DEG_PERM4(p); 1 <= sup; sup--) {
1849 if (ptPerm4[sup - 1] != sup - 1) {
1850 break;
1851 }
1852 }
1853 return FuncAS_TRANS_PERM_INT(self, p, INTOBJ_INT(sup));
1854 }
1855 }
1856
1857 /*******************************************************************************
1858 ** GAP level functions for changing representation of a transformation to a
1859 ** permutation
1860 *******************************************************************************/
1861
1862 // Returns a permutation mathematically equal to the transformation <f> if
1863 // possible, and returns Fail if it is not possible
1864
FuncAS_PERM_TRANS(Obj self,Obj f)1865 static Obj FuncAS_PERM_TRANS(Obj self, Obj f)
1866 {
1867 const UInt2 *ptf2;
1868 const UInt4 *ptf4;
1869 UInt2 *ptp2;
1870 UInt4 *ptp4;
1871 UInt deg, i;
1872 Obj p;
1873
1874 if (TNUM_OBJ(f) == T_TRANS2) {
1875 deg = DEG_TRANS2(f);
1876 if (RANK_TRANS2(f) != deg) {
1877 return Fail;
1878 }
1879
1880 p = NEW_PERM2(deg);
1881 ptp2 = ADDR_PERM2(p);
1882 ptf2 = CONST_ADDR_TRANS2(f);
1883
1884 for (i = 0; i < deg; i++) {
1885 ptp2[i] = ptf2[i];
1886 }
1887 return p;
1888 }
1889 else if (TNUM_OBJ(f) == T_TRANS4) {
1890 deg = DEG_TRANS4(f);
1891 if (RANK_TRANS4(f) != deg) {
1892 return Fail;
1893 }
1894
1895 p = NEW_PERM4(deg);
1896 ptp4 = ADDR_PERM4(p);
1897 ptf4 = CONST_ADDR_TRANS4(f);
1898
1899 for (i = 0; i < deg; i++) {
1900 ptp4[i] = ptf4[i];
1901 }
1902 return p;
1903 }
1904 RequireTransformation("AS_PERM_TRANS", f);
1905 return 0L;
1906 }
1907
1908 // Returns the permutation of the image of the transformation <f> induced by
1909 // <f> if possible, and returns Fail if it is not possible.
1910
FuncPermutationOfImage(Obj self,Obj f)1911 static Obj FuncPermutationOfImage(Obj self, Obj f)
1912 {
1913 const UInt2 *ptf2;
1914 const UInt4 *ptf4;
1915 UInt2 *ptp2;
1916 UInt4 *ptp4, *pttmp;
1917 UInt deg, rank, i, j;
1918 Obj p, img;
1919
1920 if (TNUM_OBJ(f) == T_TRANS2) {
1921 rank = RANK_TRANS2(f);
1922 deg = DEG_TRANS2(f);
1923
1924 p = NEW_PERM2(deg);
1925 ResizeTmpTrans(deg);
1926
1927 pttmp = AddrTmpTrans();
1928 ptp2 = ADDR_PERM2(p);
1929 for (i = 0; i < deg; i++) {
1930 pttmp[i] = 0;
1931 ptp2[i] = i;
1932 }
1933
1934 ptf2 = CONST_ADDR_TRANS2(f);
1935 img = IMG_TRANS(f);
1936 assert(img != NULL); // should be installed by RANK_TRANS2
1937
1938 for (i = 0; i < rank; i++) {
1939 j = INT_INTOBJ(ELM_PLIST(img, i + 1)) - 1;
1940 if (pttmp[ptf2[j]] != 0) {
1941 return Fail;
1942 }
1943 pttmp[ptf2[j]] = 1;
1944 ptp2[j] = ptf2[j];
1945 }
1946 return p;
1947 }
1948 else if (TNUM_OBJ(f) == T_TRANS4) {
1949 rank = RANK_TRANS4(f);
1950 deg = DEG_TRANS4(f);
1951
1952 p = NEW_PERM4(deg);
1953 ResizeTmpTrans(deg);
1954
1955 pttmp = AddrTmpTrans();
1956 ptp4 = ADDR_PERM4(p);
1957 for (i = 0; i < deg; i++) {
1958 pttmp[i] = 0;
1959 ptp4[i] = i;
1960 }
1961
1962 ptf4 = CONST_ADDR_TRANS4(f);
1963 img = IMG_TRANS(f);
1964 assert(img != NULL); // should be installed by RANK_TRANS2
1965
1966 for (i = 0; i < rank; i++) {
1967 j = INT_INTOBJ(ELM_PLIST(img, i + 1)) - 1;
1968 if (pttmp[ptf4[j]] != 0) {
1969 return Fail;
1970 }
1971 pttmp[ptf4[j]] = 1;
1972 ptp4[j] = ptf4[j];
1973 }
1974 return p;
1975 }
1976 RequireTransformation("PermutationOfImage", f);
1977 return 0L;
1978 }
1979
1980 // Returns the permutation of the im(f) induced by f ^ -1 * g under the
1981 // (unchecked) assumption that im(f) = im(g) and ker(f) = ker(g).
1982
FuncPermLeftQuoTransformationNC(Obj self,Obj f,Obj g)1983 static Obj FuncPermLeftQuoTransformationNC(Obj self, Obj f, Obj g)
1984 {
1985 const UInt2 *ptf2, *ptg2;
1986 const UInt4 *ptf4, *ptg4;
1987 UInt4 *ptp;
1988 UInt def, deg, i, min, max;
1989 Obj perm;
1990
1991 RequireTransformation("PermLeftQuoTransformationNC", f);
1992 RequireTransformation("PermLeftQuoTransformationNC", g);
1993
1994 def = DEG_TRANS(f);
1995 deg = DEG_TRANS(g);
1996 min = MIN(def, deg);
1997 max = MAX(def, deg);
1998
1999 // always return a T_PERM4 to reduce the amount of code here.
2000 perm = NEW_PERM4(max);
2001 ptp = ADDR_PERM4(perm);
2002
2003 if (TNUM_OBJ(f) == T_TRANS2 && TNUM_OBJ(g) == T_TRANS2) {
2004 ptf2 = CONST_ADDR_TRANS2(f);
2005 ptg2 = CONST_ADDR_TRANS2(g);
2006
2007 for (i = 0; i < max; i++) {
2008 ptp[i] = i;
2009 }
2010 for (i = 0; i < min; i++) {
2011 ptp[ptf2[i]] = ptg2[i];
2012 }
2013 for (; i < deg; i++) {
2014 ptp[i] = ptg2[i];
2015 }
2016 for (; i < def; i++) {
2017 ptp[ptf2[i]] = i;
2018 }
2019 }
2020 else if (TNUM_OBJ(f) == T_TRANS2 && TNUM_OBJ(g) == T_TRANS4) {
2021 ptf2 = CONST_ADDR_TRANS2(f);
2022 ptg4 = CONST_ADDR_TRANS4(g);
2023
2024 for (i = 0; i < max; i++) {
2025 ptp[i] = i;
2026 }
2027 for (i = 0; i < min; i++) {
2028 ptp[ptf2[i]] = ptg4[i];
2029 }
2030 for (; i < deg; i++) {
2031 ptp[i] = ptg4[i];
2032 }
2033 for (; i < def; i++) {
2034 // The only transformation created within this file that is of
2035 // type
2036 // T_TRANS4 and that does not have (internal) degree 65537 or
2037 // greater
2038 // is ID_TRANS4.
2039 ptp[ptf2[i]] = i;
2040 }
2041 }
2042 else if (TNUM_OBJ(f) == T_TRANS4 && TNUM_OBJ(g) == T_TRANS2) {
2043 ptf4 = CONST_ADDR_TRANS4(f);
2044 ptg2 = CONST_ADDR_TRANS2(g);
2045
2046 for (i = 0; i < max; i++) {
2047 ptp[i] = i;
2048 }
2049 for (i = 0; i < min; i++) {
2050 ptp[ptf4[i]] = ptg2[i];
2051 }
2052 for (; i < deg; i++) {
2053 // The only transformation created within this file that is of
2054 // type
2055 // T_TRANS4 and that does not have (internal) degree 65537 or
2056 // greater
2057 // is ID_TRANS4.
2058 ptp[i] = ptg2[i];
2059 }
2060 for (; i < def; i++) {
2061 ptp[ptf4[i]] = i;
2062 }
2063 }
2064 else if (TNUM_OBJ(f) == T_TRANS4 && TNUM_OBJ(g) == T_TRANS4) {
2065 ptf4 = CONST_ADDR_TRANS4(f);
2066 ptg4 = CONST_ADDR_TRANS4(g);
2067
2068 for (i = 0; i < max; i++) {
2069 ptp[i] = i;
2070 }
2071 for (i = 0; i < min; i++) {
2072 ptp[ptf4[i]] = ptg4[i];
2073 }
2074 for (; i < deg; i++) {
2075 ptp[i] = ptg4[i];
2076 }
2077 for (; i < def; i++) {
2078 ptp[ptf4[i]] = i;
2079 }
2080 }
2081 return perm;
2082 }
2083
2084 /*******************************************************************************
2085 ** GAP level functions for changing representation of a transformation to a
2086 ** transformation
2087 *******************************************************************************/
2088
2089 // Returns a transformation g such that (i)g = (i)f for all i in list, and
2090 // where (i)g = i for every other value of i.
2091
FuncRestrictedTransformation(Obj self,Obj f,Obj list)2092 static Obj FuncRestrictedTransformation(Obj self, Obj f, Obj list)
2093 {
2094 UInt deg, i, k, len;
2095 const UInt2 *ptf2;
2096 const UInt4 *ptf4;
2097 UInt2 *ptg2;
2098 UInt4 *ptg4;
2099 Obj g, j;
2100
2101 if (!IS_LIST(list)) {
2102 ErrorQuit(
2103 "RestrictedTransformation: the second argument must be a list "
2104 "(not a %s)",
2105 (Int)TNAM_OBJ(list), 0L);
2106 }
2107
2108 len = LEN_LIST(list);
2109
2110 if (TNUM_OBJ(f) == T_TRANS2) {
2111 deg = DEG_TRANS2(f);
2112 g = NEW_TRANS2(deg);
2113
2114 ptf2 = CONST_ADDR_TRANS2(f);
2115 ptg2 = ADDR_TRANS2(g);
2116
2117 // g fixes every point
2118 for (i = 0; i < deg; i++) {
2119 ptg2[i] = i;
2120 }
2121
2122 // g acts like f on list * /
2123 for (i = 0; i < len; i++) {
2124 j = ELM_LIST(list, i + 1);
2125 if (!IS_INTOBJ(j) || INT_INTOBJ(j) < 1) {
2126 ErrorQuit(
2127 "RestrictedTransformation: <list>[%d] must be a positive "
2128 " integer (not a %s)",
2129 (Int)i + 1, (Int)TNAM_OBJ(j));
2130 }
2131 k = INT_INTOBJ(j) - 1;
2132 if (k < deg) {
2133 ptg2[k] = ptf2[k];
2134 }
2135 }
2136 return g;
2137 }
2138 else if (TNUM_OBJ(f) == T_TRANS4) {
2139 deg = DEG_TRANS4(f);
2140 g = NEW_TRANS4(deg);
2141
2142 ptf4 = CONST_ADDR_TRANS4(f);
2143 ptg4 = ADDR_TRANS4(g);
2144
2145 // g fixes every point
2146 for (i = 0; i < deg; i++) {
2147 ptg4[i] = i;
2148 }
2149
2150 // g acts like f on list
2151 for (i = 0; i < len; i++) {
2152 j = ELM_LIST(list, i + 1);
2153 if (!IS_INTOBJ(j) || INT_INTOBJ(j) < 1) {
2154 ErrorQuit(
2155 "RestrictedTransformation: <list>[%d] must be a positive "
2156 " integer (not a %s)",
2157 (Int)i + 1, (Int)TNAM_OBJ(j));
2158 }
2159 k = INT_INTOBJ(j) - 1;
2160 if (k < deg) {
2161 ptg4[k] = ptf4[k];
2162 }
2163 }
2164 return g;
2165 }
2166 RequireTransformation("RestrictedTransformation", f);
2167 return 0L;
2168 }
2169
2170 // AsTransformation for a transformation <f> and a pos int <m> either
2171 // restricts
2172 // <f> to [1 .. m] or returns <f> depending on whether m is less than or equal
2173 // DegreeOfTransformation(f) or not.
2174
2175 // In the first form, this is similar to TRIM_TRANS except that a new
2176 // transformation is returned.
2177
FuncAS_TRANS_TRANS(Obj self,Obj f,Obj m)2178 static Obj FuncAS_TRANS_TRANS(Obj self, Obj f, Obj m)
2179 {
2180 const UInt2 *ptf2;
2181 const UInt4 *ptf4;
2182 UInt2 *ptg2;
2183 UInt4 *ptg4;
2184 UInt i, n, def;
2185 Obj g;
2186
2187 RequireNonnegativeSmallInt("AS_TRANS_TRANS", m);
2188
2189 n = INT_INTOBJ(m);
2190
2191 if (TNUM_OBJ(f) == T_TRANS2) {
2192 def = DEG_TRANS2(f);
2193 if (def <= n) {
2194 return f;
2195 }
2196
2197 g = NEW_TRANS2(n);
2198 ptf2 = CONST_ADDR_TRANS2(f);
2199 ptg2 = ADDR_TRANS2(g);
2200 for (i = 0; i < n; i++) {
2201 if (ptf2[i] > n - 1) {
2202 return Fail;
2203 }
2204 ptg2[i] = ptf2[i];
2205 }
2206 return g;
2207 }
2208 else if (TNUM_OBJ(f) == T_TRANS4) {
2209 def = DEG_TRANS4(f);
2210 if (def <= n) {
2211 return f;
2212 }
2213
2214 if (n > 65536) {
2215 // g is T_TRANS4
2216 g = NEW_TRANS4(n);
2217 ptf4 = CONST_ADDR_TRANS4(f);
2218 ptg4 = ADDR_TRANS4(g);
2219 for (i = 0; i < n; i++) {
2220 if (ptf4[i] > n - 1) {
2221 return Fail;
2222 }
2223 ptg4[i] = ptf4[i];
2224 }
2225 }
2226 else {
2227 // f is T_TRANS4 but n <= 65536 < def and so g will be T_TRANS2 *
2228 // /
2229 g = NEW_TRANS2(n);
2230 ptf4 = CONST_ADDR_TRANS4(f);
2231 ptg2 = ADDR_TRANS2(g);
2232 for (i = 0; i < n; i++) {
2233 if (ptf4[i] > n - 1) {
2234 return Fail;
2235 }
2236 ptg2[i] = (UInt2)ptf4[i];
2237 }
2238 }
2239 return g;
2240 }
2241 RequireTransformation("AS_TRANS_TRANS", f);
2242 return 0L;
2243 }
2244
2245 // Changes the transformation <f> in-place to reduce the degree to <m>. It is
2246 // assumed that f is actually a transformation of [1 .. m], i.e. that i ^ f <=
2247 // m for all i in [1 .. m].
2248
FuncTRIM_TRANS(Obj self,Obj f,Obj m)2249 static Obj FuncTRIM_TRANS(Obj self, Obj f, Obj m)
2250 {
2251 UInt deg, i;
2252 UInt4 * ptf;
2253
2254 RequireNonnegativeSmallInt("TRIM_TRANS", m);
2255
2256 deg = INT_INTOBJ(m);
2257
2258 if (TNUM_OBJ(f) == T_TRANS2) {
2259 // output is T_TRANS2
2260 if (deg > DEG_TRANS2(f)) {
2261 return (Obj)0;
2262 }
2263 ResizeBag(f, deg * sizeof(UInt2) + 3 * sizeof(Obj));
2264 SET_IMG_TRANS(f, NULL);
2265 SET_KER_TRANS(f, NULL);
2266 SET_EXT_TRANS(f, NULL);
2267 CHANGED_BAG(f);
2268 return (Obj)0;
2269 }
2270 else if (TNUM_OBJ(f) == T_TRANS4) {
2271 if (deg > DEG_TRANS4(f)) {
2272 return (Obj)0;
2273 }
2274 if (deg > 65536UL) {
2275 // output is T_TRANS4
2276 ResizeBag(f, deg * sizeof(UInt4) + 3 * sizeof(Obj));
2277 }
2278 else {
2279 // output is T_TRANS2
2280 ptf = ADDR_TRANS4(f);
2281 for (i = 0; i < deg; i++) {
2282 ((UInt2 *)ptf)[i] = (UInt2)ptf[i];
2283 }
2284 RetypeBag(f, T_TRANS2);
2285 ResizeBag(f, deg * sizeof(UInt2) + 3 * sizeof(Obj));
2286 }
2287 SET_IMG_TRANS(f, NULL);
2288 SET_KER_TRANS(f, NULL);
2289 SET_EXT_TRANS(f, NULL);
2290 CHANGED_BAG(f);
2291 return (Obj)0;
2292 }
2293
2294 RequireTransformation("TRIM_TRANS", f);
2295 return 0L;
2296 }
2297
2298 /*******************************************************************************
2299 ** GAP level functions for hashing transformations
2300 *******************************************************************************/
2301
2302 // A hash function for transformations.
2303
HashFuncForTrans(Obj f)2304 Int HashFuncForTrans(Obj f)
2305 {
2306 UInt deg;
2307
2308 GAP_ASSERT(TNUM_OBJ(f) == T_TRANS2 || TNUM_OBJ(f) == T_TRANS4);
2309
2310 deg = INT_INTOBJ(FuncDegreeOfTransformation(0, f));
2311
2312 if (TNUM_OBJ(f) == T_TRANS4) {
2313 if (deg <= 65536) {
2314 FuncTRIM_TRANS(0, f, INTOBJ_INT(deg));
2315 }
2316 else {
2317 return HASHKEY_BAG_NC(f, (UInt4)255, 3 * sizeof(Obj), (int)4 * deg);
2318 }
2319 }
2320
2321 return HASHKEY_BAG_NC(f, (UInt4)255, 3 * sizeof(Obj), (int)2 * deg);
2322 }
2323
FuncHASH_FUNC_FOR_TRANS(Obj self,Obj f,Obj data)2324 static Obj FuncHASH_FUNC_FOR_TRANS(Obj self, Obj f, Obj data)
2325 {
2326 return INTOBJ_INT((HashFuncForTrans(f) % INT_INTOBJ(data)) + 1);
2327 }
2328
2329 /*******************************************************************************
2330 ** GAP level functions for moved points (and related) of a transformation
2331 *******************************************************************************/
2332
2333 // Returns the largest value i such that (i)f <> i or 0 if no such i exists.
2334
FuncLARGEST_MOVED_PT_TRANS(Obj self,Obj f)2335 static Obj FuncLARGEST_MOVED_PT_TRANS(Obj self, Obj f)
2336 {
2337 const UInt2 * ptf2;
2338 const UInt4 * ptf4;
2339 UInt i;
2340
2341 if (TNUM_OBJ(f) == T_TRANS2) {
2342 ptf2 = CONST_ADDR_TRANS2(f);
2343 for (i = DEG_TRANS2(f); 1 <= i; i--) {
2344 if (ptf2[i - 1] != i - 1) {
2345 break;
2346 }
2347 }
2348 return INTOBJ_INT(i);
2349 }
2350 else if (TNUM_OBJ(f) == T_TRANS4) {
2351 ptf4 = CONST_ADDR_TRANS4(f);
2352 for (i = DEG_TRANS4(f); 1 <= i; i--) {
2353 if (ptf4[i - 1] != i - 1) {
2354 break;
2355 }
2356 }
2357 return INTOBJ_INT(i);
2358 }
2359 RequireTransformation("LARGEST_MOVED_PT_TRANS", f);
2360 return 0L;
2361 }
2362
2363 // Returns the largest value in [(1)f .. (n)f] where n = LargestMovedPoint(f).
2364
FuncLARGEST_IMAGE_PT(Obj self,Obj f)2365 static Obj FuncLARGEST_IMAGE_PT(Obj self, Obj f)
2366 {
2367 const UInt2 * ptf2;
2368 const UInt4 * ptf4;
2369 UInt i, max, def;
2370
2371 max = 0;
2372 if (TNUM_OBJ(f) == T_TRANS2) {
2373 def = DEG_TRANS2(f);
2374 ptf2 = CONST_ADDR_TRANS2(f);
2375 for (i = DEG_TRANS2(f); 1 <= i; i--) {
2376 if (ptf2[i - 1] != i - 1) {
2377 break;
2378 }
2379 }
2380 for (; 1 <= i; i--) {
2381 if (ptf2[i - 1] + 1 > max) {
2382 max = ptf2[i - 1] + 1;
2383 if (max == def) {
2384 break;
2385 }
2386 }
2387 }
2388 return INTOBJ_INT(max);
2389 }
2390 else if (TNUM_OBJ(f) == T_TRANS4) {
2391 def = DEG_TRANS4(f);
2392 ptf4 = CONST_ADDR_TRANS4(f);
2393 for (i = DEG_TRANS4(f); 1 <= i; i--) {
2394 if (ptf4[i - 1] != i - 1) {
2395 break;
2396 }
2397 }
2398 for (; 1 <= i; i--) {
2399 if (ptf4[i - 1] + 1 > max) {
2400 max = ptf4[i - 1] + 1;
2401 if (max == def)
2402 break;
2403 }
2404 }
2405 return INTOBJ_INT(max);
2406 }
2407 RequireTransformation("LARGEST_IMAGE_PT", f);
2408 return 0L;
2409 }
2410
2411 // Returns the smallest value <i> such that (i)f <> i if it exists, and Fail
2412 // if
2413 // not. Note that this differs from the GAP level function which returns
2414 // infinity if (i)f = i for all i.
2415
FuncSMALLEST_MOVED_PT_TRANS(Obj self,Obj f)2416 static Obj FuncSMALLEST_MOVED_PT_TRANS(Obj self, Obj f)
2417 {
2418 const UInt2 * ptf2;
2419 const UInt4 * ptf4;
2420 UInt i, deg;
2421
2422 RequireTransformation("SMALLEST_MOVED_PTS_TRANS", f);
2423 if (FuncIS_ID_TRANS(self, f) == True) {
2424 return Fail;
2425 }
2426
2427 if (TNUM_OBJ(f) == T_TRANS2) {
2428 ptf2 = CONST_ADDR_TRANS2(f);
2429 deg = DEG_TRANS2(f);
2430 for (i = 1; i <= deg; i++) {
2431 if (ptf2[i - 1] != i - 1) {
2432 break;
2433 }
2434 }
2435 }
2436 else {
2437 ptf4 = CONST_ADDR_TRANS4(f);
2438 deg = DEG_TRANS4(f);
2439 for (i = 1; i <= deg; i++) {
2440 if (ptf4[i - 1] != i - 1) {
2441 break;
2442 }
2443 }
2444 }
2445 return INTOBJ_INT(i);
2446 }
2447
2448 // Returns the smallest value in [SmallestMovedPoint(f) ..
2449 // LargestMovedPoint(f)] ^ f if it exists and Fail if it does not. Note that
2450 // this differs from the GAP level function which returns infinity if (i)f = i
2451 // for all i.
2452
FuncSMALLEST_IMAGE_PT(Obj self,Obj f)2453 static Obj FuncSMALLEST_IMAGE_PT(Obj self, Obj f)
2454 {
2455 const UInt2 * ptf2;
2456 const UInt4 * ptf4;
2457 UInt i, min, deg;
2458
2459 RequireTransformation("SMALLEST_IMAGE_PT", f);
2460 if (FuncIS_ID_TRANS(self, f) == True) {
2461 return Fail;
2462 }
2463
2464 if (TNUM_OBJ(f) == T_TRANS2) {
2465 ptf2 = CONST_ADDR_TRANS2(f);
2466 deg = DEG_TRANS2(f);
2467 min = deg;
2468 for (i = 0; i < deg; i++) {
2469 if (ptf2[i] != i && ptf2[i] < min) {
2470 min = ptf2[i];
2471 }
2472 }
2473 return INTOBJ_INT(min + 1);
2474 }
2475 else {
2476 ptf4 = CONST_ADDR_TRANS4(f);
2477 deg = DEG_TRANS4(f);
2478 min = deg;
2479 for (i = 0; i < deg; i++) {
2480 if (ptf4[i] != i && ptf4[i] < min) {
2481 min = ptf4[i];
2482 }
2483 }
2484 }
2485 return INTOBJ_INT(min + 1);
2486 }
2487
2488 // Returns the number of values <i> in [1 .. n] such that (i)f <> i, where n =
2489 // DegreeOfTransformation(f).
2490
FuncNR_MOVED_PTS_TRANS(Obj self,Obj f)2491 static Obj FuncNR_MOVED_PTS_TRANS(Obj self, Obj f)
2492 {
2493 UInt nr, i, deg;
2494 const UInt2 * ptf2;
2495 const UInt4 * ptf4;
2496
2497 nr = 0;
2498 if (TNUM_OBJ(f) == T_TRANS2) {
2499 ptf2 = CONST_ADDR_TRANS2(f);
2500 deg = DEG_TRANS2(f);
2501 for (i = 0; i < deg; i++) {
2502 if (ptf2[i] != i) {
2503 nr++;
2504 }
2505 }
2506 return INTOBJ_INT(nr);
2507 }
2508 else if (TNUM_OBJ(f) == T_TRANS4) {
2509 ptf4 = CONST_ADDR_TRANS4(f);
2510 deg = DEG_TRANS4(f);
2511 for (i = 0; i < deg; i++) {
2512 if (ptf4[i] != i) {
2513 nr++;
2514 }
2515 }
2516 return INTOBJ_INT(nr);
2517 }
2518 RequireTransformation("NR_MOVED_PTS_TRANS", f);
2519 return 0L;
2520 }
2521
2522 // Returns the set of values <i> in [1 .. n] such that (i)f <> i, where n =
2523 // DegreeOfTransformation(f).
2524
FuncMOVED_PTS_TRANS(Obj self,Obj f)2525 static Obj FuncMOVED_PTS_TRANS(Obj self, Obj f)
2526 {
2527 UInt len, deg, i;
2528 Obj out;
2529 const UInt2 * ptf2;
2530 const UInt4 * ptf4;
2531
2532 RequireTransformation("MOVED_PTS_TRANS", f);
2533
2534 len = 0;
2535 if (TNUM_OBJ(f) == T_TRANS2) {
2536 deg = DEG_TRANS2(f);
2537 out = NEW_PLIST(T_PLIST_CYC_SSORT, 0);
2538 ptf2 = CONST_ADDR_TRANS2(f);
2539 for (i = 0; i < deg; i++) {
2540 if (ptf2[i] != i) {
2541 AssPlist(out, ++len, INTOBJ_INT(i + 1));
2542 ptf2 = CONST_ADDR_TRANS2(f);
2543 }
2544 }
2545 }
2546 else {
2547 deg = DEG_TRANS4(f);
2548 out = NEW_PLIST(T_PLIST_CYC_SSORT, 0);
2549 ptf4 = CONST_ADDR_TRANS4(f);
2550 for (i = 0; i < deg; i++) {
2551 if (ptf4[i] != i) {
2552 AssPlist(out, ++len, INTOBJ_INT(i + 1));
2553 ptf4 = CONST_ADDR_TRANS4(f);
2554 }
2555 }
2556 }
2557
2558 if (LEN_PLIST(out) == 0) {
2559 RetypeBag(out, T_PLIST_EMPTY);
2560 }
2561
2562 return out;
2563 }
2564
2565 /*******************************************************************************
2566 ** GAP level functions for connected components of a transformation
2567 *******************************************************************************/
2568
2569 // Returns the representatives, in the following sense, of the components of
2570 // the transformation <f>. For every i in [1 .. DegreeOfTransformation(<f>)]
2571 // there exists a representative j and a positive integer k such that i ^ (<f>
2572 // ^ k) = j. The least number of representatives is returned and these
2573 // representatives are partitioned according to the component they belong to.
2574
FuncCOMPONENT_REPS_TRANS(Obj self,Obj f)2575 static Obj FuncCOMPONENT_REPS_TRANS(Obj self, Obj f)
2576 {
2577 UInt deg, i, nr, pt, index;
2578 Obj img, out, comp;
2579 const UInt2 * ptf2;
2580 const UInt4 * ptf4;
2581 UInt4 * seen;
2582
2583 RequireTransformation("COMPONENT_REPS_TRANS", f);
2584
2585 deg = INT_INTOBJ(FuncDegreeOfTransformation(self, f));
2586
2587 if (deg == 0) {
2588 out = NewEmptyPlist();
2589 return out;
2590 }
2591
2592 img = FuncUNSORTED_IMAGE_SET_TRANS(self, f);
2593 out = NEW_PLIST(T_PLIST, 1);
2594
2595 seen = ResizeInitTmpTrans(deg);
2596
2597 for (i = 1; i <= (UInt)LEN_PLIST(img); i++) {
2598 seen[INT_INTOBJ(ELM_PLIST(img, i)) - 1] = 1;
2599 }
2600
2601 if (TNUM_OBJ(f) == T_TRANS2) {
2602 ptf2 = CONST_ADDR_TRANS2(f);
2603 nr = 1;
2604 for (i = 0; i < deg; i++) {
2605 if (seen[i] == 0) {
2606 // i belongs to dom(f) but not im(f)
2607 // repeatedly apply f to pt until we see something we've seen
2608 // already
2609 pt = i;
2610 do {
2611 seen[pt] = nr + 1;
2612 pt = ptf2[pt];
2613 } while (seen[pt] == 1);
2614
2615 index = seen[pt];
2616
2617 if (index != nr + 1) {
2618 // pt belongs to a component we've seen before
2619 ptf2 = CONST_ADDR_TRANS2(f);
2620 pt = i;
2621 do {
2622 seen[pt] = index;
2623 pt = ptf2[pt];
2624 } while (seen[pt] == nr + 1);
2625 comp = ELM_PLIST(out, seen[pt] - 1);
2626 AssPlist(comp, LEN_PLIST(comp) + 1, INTOBJ_INT(i + 1));
2627 }
2628 else {
2629 // pt belongs to a component we've not seen before
2630 comp = NEW_PLIST(T_PLIST_CYC, 1);
2631 SET_LEN_PLIST(comp, 1);
2632 SET_ELM_PLIST(comp, 1, INTOBJ_INT(i + 1));
2633 AssPlist(out, nr++, comp);
2634 }
2635 ptf2 = CONST_ADDR_TRANS2(f);
2636 seen = AddrTmpTrans();
2637 }
2638 }
2639 for (i = 0; i < deg; i++) {
2640 if (seen[i] == 1) {
2641 // i belongs to a cycle
2642 for (pt = i; seen[pt] == 1; pt = ptf2[pt]) {
2643 seen[pt] = 0;
2644 }
2645 comp = NEW_PLIST(T_PLIST_CYC, 1);
2646 SET_LEN_PLIST(comp, 1);
2647 SET_ELM_PLIST(comp, 1, INTOBJ_INT(i + 1));
2648 AssPlist(out, nr++, comp);
2649 ptf2 = CONST_ADDR_TRANS2(f);
2650 seen = AddrTmpTrans();
2651 }
2652 }
2653 }
2654 else {
2655 ptf4 = CONST_ADDR_TRANS4(f);
2656 nr = 1;
2657 for (i = 0; i < deg; i++) {
2658 if (seen[i] == 0) {
2659 // i belongs to dom(f) but not im(f)
2660 // repeatedly apply f to pt until we see something we've seen
2661 // already
2662 pt = i;
2663 do {
2664 seen[pt] = nr + 1;
2665 pt = ptf4[pt];
2666 } while (seen[pt] == 1);
2667
2668 index = seen[pt];
2669
2670 if (index != nr + 1) {
2671 // pt belongs to a component we've seen before
2672 pt = i;
2673 do {
2674 seen[pt] = index;
2675 pt = ptf4[pt];
2676 } while (seen[pt] == nr + 1);
2677 comp = ELM_PLIST(out, seen[pt] - 1);
2678 AssPlist(comp, LEN_PLIST(comp) + 1, INTOBJ_INT(i + 1));
2679 }
2680 else {
2681 // pt belongs to a component we've not seen before
2682 comp = NEW_PLIST(T_PLIST_CYC, 1);
2683 SET_LEN_PLIST(comp, 1);
2684 SET_ELM_PLIST(comp, 1, INTOBJ_INT(i + 1));
2685 AssPlist(out, nr++, comp);
2686 }
2687 ptf4 = CONST_ADDR_TRANS4(f);
2688 seen = AddrTmpTrans();
2689 }
2690 }
2691 for (i = 0; i < deg; i++) {
2692 if (seen[i] == 1) {
2693 // i belongs to a cycle
2694 for (pt = i; seen[pt] == 1; pt = ptf4[pt]) {
2695 seen[pt] = 0;
2696 }
2697 comp = NEW_PLIST(T_PLIST_CYC, 1);
2698 SET_LEN_PLIST(comp, 1);
2699 SET_ELM_PLIST(comp, 1, INTOBJ_INT(i + 1));
2700 AssPlist(out, nr++, comp);
2701 ptf4 = CONST_ADDR_TRANS4(f);
2702 seen = AddrTmpTrans();
2703 }
2704 }
2705 }
2706 return out;
2707 }
2708
2709 // Returns the number of connected components of the transformation <f>,
2710 // thought of as a functional digraph with DegreeOfTransformation(f) vertices.
2711
FuncNR_COMPONENTS_TRANS(Obj self,Obj f)2712 static Obj FuncNR_COMPONENTS_TRANS(Obj self, Obj f)
2713 {
2714 UInt nr, m, i, j, deg;
2715 const UInt2 * ptf2;
2716 const UInt4 * ptf4;
2717 UInt4 * ptseen;
2718
2719 RequireTransformation("NR_COMPONENTS_TRANS", f);
2720
2721 deg = INT_INTOBJ(FuncDegreeOfTransformation(self, f));
2722 ptseen = ResizeInitTmpTrans(deg);
2723 nr = 0;
2724 m = 0;
2725
2726 if (TNUM_OBJ(f) == T_TRANS2) {
2727 ptf2 = CONST_ADDR_TRANS2(f);
2728 for (i = 0; i < deg; i++) {
2729 if (ptseen[i] == 0) {
2730 m++;
2731 for (j = i; ptseen[j] == 0; j = ptf2[j]) {
2732 ptseen[j] = m;
2733 }
2734 if (ptseen[j] == m) {
2735 nr++;
2736 }
2737 }
2738 }
2739 }
2740 else {
2741 ptf4 = CONST_ADDR_TRANS4(f);
2742 for (i = 0; i < deg; i++) {
2743 if (ptseen[i] == 0) {
2744 m++;
2745 for (j = i; ptseen[j] == 0; j = ptf4[j]) {
2746 ptseen[j] = m;
2747 }
2748 if (ptseen[j] == m) {
2749 nr++;
2750 }
2751 }
2752 }
2753 }
2754 return INTOBJ_INT(nr);
2755 }
2756
2757 // Returns the connected components of the transformation <f>, thought of as a
2758 // functional digraph with DegreeOfTransformation(f) vertices.
2759
FuncCOMPONENTS_TRANS(Obj self,Obj f)2760 static Obj FuncCOMPONENTS_TRANS(Obj self, Obj f)
2761 {
2762 const UInt2 * ptf2;
2763 const UInt4 * ptf4;
2764 UInt4 * seen;
2765 UInt deg, i, pt, csize, nr, index, pos;
2766 Obj out, comp;
2767
2768 RequireTransformation("COMPONENTS_TRANS", f);
2769
2770 deg = INT_INTOBJ(FuncDegreeOfTransformation(self, f));
2771
2772 if (deg == 0) {
2773 out = NewEmptyPlist();
2774 return out;
2775 }
2776
2777 out = NEW_PLIST(T_PLIST, 1);
2778 seen = ResizeInitTmpTrans(deg);
2779 nr = 0;
2780
2781 if (TNUM_OBJ(f) == T_TRANS2) {
2782 ptf2 = CONST_ADDR_TRANS2(f);
2783 for (i = 0; i < deg; i++) {
2784 if (seen[i] == 0) {
2785 // repeatedly apply f to pt until we see something we've seen
2786 // already
2787 csize = 0;
2788 pt = i;
2789 do {
2790 csize++;
2791 seen[pt] = deg + 1;
2792 pt = ptf2[pt];
2793 } while (seen[pt] == 0);
2794
2795 if (seen[pt] <= deg) {
2796 // pt belongs to a component we've seen before
2797 index = seen[pt];
2798 comp = ELM_PLIST(out, index);
2799 pos = LEN_PLIST(comp) + 1;
2800 GROW_PLIST(comp, LEN_PLIST(comp) + csize);
2801 SET_LEN_PLIST(comp, LEN_PLIST(comp) + csize);
2802 }
2803 else {
2804 // pt belongs to a component we've not seen before
2805 index = ++nr;
2806 pos = 1;
2807
2808 comp = NEW_PLIST(T_PLIST_CYC, csize);
2809 SET_LEN_PLIST(comp, csize);
2810 AssPlist(out, nr, comp);
2811 }
2812 seen = AddrTmpTrans();
2813 ptf2 = CONST_ADDR_TRANS2(f);
2814
2815 pt = i;
2816 while (seen[pt] == deg + 1) {
2817 SET_ELM_PLIST(comp, pos++, INTOBJ_INT(pt + 1));
2818 seen[pt] = index;
2819 pt = ptf2[pt];
2820 }
2821 CHANGED_BAG(out);
2822 }
2823 }
2824 }
2825 else {
2826 ptf4 = CONST_ADDR_TRANS4(f);
2827 for (i = 0; i < deg; i++) {
2828 if (seen[i] == 0) {
2829 // repeatedly apply f to pt until we see something we've seen
2830 // already
2831 csize = 0;
2832 pt = i;
2833 do {
2834 csize++;
2835 seen[pt] = deg + 1;
2836 pt = ptf4[pt];
2837 } while (seen[pt] == 0);
2838
2839 if (seen[pt] <= deg) {
2840 // pt belongs to a component we've seen before
2841 index = seen[pt];
2842 comp = ELM_PLIST(out, index);
2843 pos = LEN_PLIST(comp) + 1;
2844 GROW_PLIST(comp, LEN_PLIST(comp) + csize);
2845 SET_LEN_PLIST(comp, LEN_PLIST(comp) + csize);
2846 }
2847 else {
2848 // pt belongs to a component we've not seen before
2849 index = ++nr;
2850 pos = 1;
2851
2852 comp = NEW_PLIST(T_PLIST_CYC, csize);
2853 SET_LEN_PLIST(comp, csize);
2854 AssPlist(out, nr, comp);
2855 }
2856 seen = AddrTmpTrans();
2857 ptf4 = CONST_ADDR_TRANS4(f);
2858
2859 pt = i;
2860 while (seen[pt] == deg + 1) {
2861 SET_ELM_PLIST(comp, pos++, INTOBJ_INT(pt + 1));
2862 seen[pt] = index;
2863 pt = ptf4[pt];
2864 }
2865 CHANGED_BAG(out);
2866 }
2867 }
2868 }
2869 return out;
2870 }
2871
2872 // Returns the list of distinct values [pt, (pt)f, (pt)f ^ 2, ..] where <f> is
2873 // a transformation and <pt> is a positive integer.
2874
FuncCOMPONENT_TRANS_INT(Obj self,Obj f,Obj pt)2875 static Obj FuncCOMPONENT_TRANS_INT(Obj self, Obj f, Obj pt)
2876 {
2877 UInt deg, cpt, len;
2878 Obj out;
2879 const UInt2 * ptf2;
2880 const UInt4 * ptf4;
2881 UInt4 * ptseen;
2882
2883 RequireTransformation("COMPONENT_TRANS_INT", f);
2884 cpt = GetPositiveSmallInt("COMPONENT_TRANS_INT", pt) - 1;
2885
2886 deg = INT_INTOBJ(FuncDegreeOfTransformation(self, f));
2887
2888 if (cpt >= deg) {
2889 out = NEW_PLIST(T_PLIST_CYC_SSORT, 1);
2890 SET_LEN_PLIST(out, 1);
2891 SET_ELM_PLIST(out, 1, pt);
2892 return out;
2893 }
2894 out = NEW_PLIST(T_PLIST_CYC, 0);
2895 ptseen = ResizeInitTmpTrans(deg);
2896
2897 len = 0;
2898
2899 // install the points
2900 if (TNUM_OBJ(f) == T_TRANS2) {
2901 do {
2902 AssPlist(out, ++len, INTOBJ_INT(cpt + 1));
2903 ptseen = AddrTmpTrans();
2904 ptf2 = CONST_ADDR_TRANS2(f);
2905 ptseen[cpt] = 1;
2906 cpt = ptf2[cpt];
2907 } while (ptseen[cpt] == 0);
2908 }
2909 else {
2910 do {
2911 AssPlist(out, ++len, INTOBJ_INT(cpt + 1));
2912 ptseen = AddrTmpTrans();
2913 ptf4 = CONST_ADDR_TRANS4(f);
2914 ptseen[cpt] = 1;
2915 cpt = ptf4[cpt];
2916 } while (ptseen[cpt] == 0);
2917 }
2918 SET_LEN_PLIST(out, (Int)len);
2919 return out;
2920 }
2921
2922 /*******************************************************************************
2923 ** GAP level functions for cycles of a transformation
2924 *******************************************************************************/
2925
2926 // Returns the cycle contained in the component of the transformation <f>
2927 // containing the positive integer <pt>.
2928
FuncCYCLE_TRANS_INT(Obj self,Obj f,Obj pt)2929 static Obj FuncCYCLE_TRANS_INT(Obj self, Obj f, Obj pt)
2930 {
2931 UInt deg, cpt, len, i;
2932 Obj out;
2933 const UInt2 * ptf2;
2934 const UInt4 * ptf4;
2935 UInt4 * ptseen;
2936
2937 RequireTransformation("CYCLE_TRANS_INT", f);
2938 cpt = GetPositiveSmallInt("CYCLE_TRANS_INT", pt) - 1;
2939
2940 deg = INT_INTOBJ(FuncDegreeOfTransformation(self, f));
2941
2942 if (cpt >= deg) {
2943 out = NEW_PLIST(T_PLIST_CYC_SSORT, 1);
2944 SET_LEN_PLIST(out, 1);
2945 SET_ELM_PLIST(out, 1, pt);
2946 return out;
2947 }
2948
2949 out = NEW_PLIST(T_PLIST_CYC, 0);
2950 ptseen = ResizeInitTmpTrans(deg);
2951 len = 0;
2952
2953 if (TNUM_OBJ(f) == T_TRANS2) {
2954 ptf2 = CONST_ADDR_TRANS2(f);
2955 // find component
2956 do {
2957 ptseen[cpt] = 1;
2958 cpt = ptf2[cpt];
2959 } while (ptseen[cpt] == 0);
2960 // find cycle
2961 i = cpt;
2962 do {
2963 AssPlist(out, ++len, INTOBJ_INT(i + 1));
2964 ptf2 = CONST_ADDR_TRANS2(f);
2965 i = ptf2[i];
2966 } while (i != cpt);
2967 }
2968 else {
2969 ptf4 = CONST_ADDR_TRANS4(f);
2970 // find component
2971 do {
2972 ptseen[cpt] = 1;
2973 cpt = ptf4[cpt];
2974 } while (ptseen[cpt] == 0);
2975 // find cycle
2976 i = cpt;
2977 do {
2978 AssPlist(out, ++len, INTOBJ_INT(i + 1));
2979 ptf4 = CONST_ADDR_TRANS4(f);
2980 i = ptf4[i];
2981 } while (i != cpt);
2982 }
2983 return out;
2984 }
2985
2986 // Returns the cycles of the transformation <f>, thought of as a
2987 // functional digraph with DegreeOfTransformation(f) vertices.
2988
FuncCYCLES_TRANS(Obj self,Obj f)2989 static Obj FuncCYCLES_TRANS(Obj self, Obj f)
2990 {
2991 const UInt2 * ptf2;
2992 const UInt4 * ptf4;
2993 UInt4 * seen;
2994 UInt deg, i, pt, nr;
2995 Obj out, comp;
2996
2997 RequireTransformation("CYCLES_TRANS", f);
2998 deg = INT_INTOBJ(FuncDegreeOfTransformation(self, f));
2999
3000 if (deg == 0) {
3001 out = NewEmptyPlist();
3002 return out;
3003 }
3004
3005 out = NEW_PLIST(T_PLIST, 0);
3006 nr = 0;
3007
3008 seen = ResizeInitTmpTrans(deg);
3009
3010 if (TNUM_OBJ(f) == T_TRANS2) {
3011 ptf2 = CONST_ADDR_TRANS2(f);
3012 for (i = 0; i < deg; i++) {
3013 if (seen[i] == 0) {
3014 // repeatedly apply f to pt until we see something we've seen
3015 // already
3016 for (pt = i; seen[pt] == 0; pt = ptf2[pt]) {
3017 seen[pt] = 1;
3018 }
3019 if (seen[pt] == 1) {
3020 // pt belongs to a component we've not seen before
3021
3022 comp = NEW_PLIST(T_PLIST_CYC, 0);
3023 AssPlist(out, ++nr, comp);
3024
3025 seen = AddrTmpTrans();
3026 ptf2 = CONST_ADDR_TRANS2(f);
3027
3028 for (; seen[pt] == 1; pt = ptf2[pt]) {
3029 seen[pt] = 2;
3030 AssPlist(comp, LEN_PLIST(comp) + 1,
3031 INTOBJ_INT(pt + 1));
3032 seen = AddrTmpTrans();
3033 ptf2 = CONST_ADDR_TRANS2(f);
3034 }
3035 }
3036 for (pt = i; seen[pt] == 1; pt = ptf2[pt]) {
3037 seen[pt] = 2;
3038 }
3039 }
3040 }
3041 }
3042 else {
3043 ptf4 = CONST_ADDR_TRANS4(f);
3044 for (i = 0; i < deg; i++) {
3045 if (seen[i] == 0) {
3046 // repeatedly apply f to pt until we see something we've seen
3047 // already
3048 for (pt = i; seen[pt] == 0; pt = ptf4[pt]) {
3049 seen[pt] = 1;
3050 }
3051 if (seen[pt] == 1) {
3052 // pt belongs to a component we've not seen before
3053
3054 comp = NEW_PLIST(T_PLIST_CYC, 0);
3055 AssPlist(out, ++nr, comp);
3056
3057 seen = AddrTmpTrans();
3058 ptf4 = CONST_ADDR_TRANS4(f);
3059
3060 for (; seen[pt] == 1; pt = ptf4[pt]) {
3061 seen[pt] = 2;
3062 AssPlist(comp, LEN_PLIST(comp) + 1,
3063 INTOBJ_INT(pt + 1));
3064 seen = AddrTmpTrans();
3065 ptf4 = CONST_ADDR_TRANS4(f);
3066 }
3067 }
3068 for (pt = i; seen[pt] == 1; pt = ptf4[pt]) {
3069 seen[pt] = 2;
3070 }
3071 }
3072 }
3073 }
3074 return out;
3075 }
3076
3077 // Returns the cycles of the transformation <f> contained in the components of
3078 // any of the elements in <list>.
3079
FuncCYCLES_TRANS_LIST(Obj self,Obj f,Obj list)3080 static Obj FuncCYCLES_TRANS_LIST(Obj self, Obj f, Obj list)
3081 {
3082 const UInt2 * ptf2;
3083 const UInt4 * ptf4;
3084 UInt4 * seen;
3085 UInt deg, i, j, pt, nr;
3086 Obj out, comp, list_i;
3087
3088 RequireTransformation("CYCLES_TRANS_LIST", f);
3089 if (!IS_LIST(list)) {
3090 ErrorQuit("CYCLES_TRANS_LIST: the second argument must be a list (not a %s)",
3091 (Int)TNAM_OBJ(f), 0L);
3092 }
3093
3094 deg = INT_INTOBJ(FuncDegreeOfTransformation(self, f));
3095
3096 if (LEN_LIST(list) == 0) {
3097 out = NewEmptyPlist();
3098 return out;
3099 }
3100
3101 out = NEW_PLIST(T_PLIST, 0);
3102 nr = 0;
3103
3104 seen = ResizeInitTmpTrans(deg);
3105
3106 if (TNUM_OBJ(f) == T_TRANS2) {
3107 ptf2 = CONST_ADDR_TRANS2(f);
3108 for (i = 1; i <= (UInt)LEN_LIST(list); i++) {
3109 list_i = ELM_LIST(list, i);
3110 if (!IS_POS_INTOBJ(list_i)) {
3111 ErrorQuit("CYCLES_TRANS_LIST: the second argument must be a "
3112 "list of positive integer (not a %s)",
3113 (Int)TNAM_OBJ(list_i), 0L);
3114 }
3115 j = INT_INTOBJ(list_i) - 1;
3116 if (j >= deg) {
3117 comp = NEW_PLIST(T_PLIST_CYC, 1);
3118 SET_LEN_PLIST(comp, 1);
3119 SET_ELM_PLIST(comp, 1, list_i);
3120 AssPlist(out, ++nr, comp);
3121 seen = AddrTmpTrans();
3122 ptf2 = CONST_ADDR_TRANS2(f);
3123 }
3124 else if (seen[j] == 0) {
3125 // repeatedly apply f to pt until we see something we've seen
3126 // already
3127 for (pt = j; seen[pt] == 0; pt = ptf2[pt]) {
3128 seen[pt] = 1;
3129 }
3130 if (seen[pt] == 1) {
3131 // pt belongs to a component we've not seen before
3132
3133 comp = NEW_PLIST(T_PLIST_CYC, 0);
3134 AssPlist(out, ++nr, comp);
3135
3136 seen = AddrTmpTrans();
3137 ptf2 = CONST_ADDR_TRANS2(f);
3138
3139 for (; seen[pt] == 1; pt = ptf2[pt]) {
3140 seen[pt] = 2;
3141 AssPlist(comp, LEN_PLIST(comp) + 1,
3142 INTOBJ_INT(pt + 1));
3143 seen = AddrTmpTrans();
3144 ptf2 = CONST_ADDR_TRANS2(f);
3145 }
3146 }
3147 for (pt = j; seen[pt] == 1; pt = ptf2[pt]) {
3148 seen[pt] = 2;
3149 }
3150 }
3151 }
3152 }
3153 else {
3154 ptf4 = CONST_ADDR_TRANS4(f);
3155 for (i = 1; i <= (UInt)LEN_LIST(list); i++) {
3156 list_i = ELM_LIST(list, i);
3157 if (!IS_POS_INTOBJ(list_i)) {
3158 ErrorQuit("CYCLES_TRANS_LIST: the second argument must be a "
3159 "list of positive integers (not a %s)",
3160 (Int)TNAM_OBJ(list_i), 0L);
3161 }
3162 j = INT_INTOBJ(list_i) - 1;
3163 if (j >= deg) {
3164 comp = NEW_PLIST(T_PLIST_CYC, 1);
3165 SET_LEN_PLIST(comp, 1);
3166 SET_ELM_PLIST(comp, 1, list_i);
3167 AssPlist(out, ++nr, comp);
3168 seen = AddrTmpTrans();
3169 ptf4 = CONST_ADDR_TRANS4(f);
3170 }
3171 else if (seen[j] == 0) {
3172 // repeatedly apply f to pt until we see something we've seen
3173 // already
3174 for (pt = j; seen[pt] == 0; pt = ptf4[pt]) {
3175 seen[pt] = 1;
3176 }
3177 if (seen[pt] == 1) {
3178 // pt belongs to a component we've not seen before
3179
3180 comp = NEW_PLIST(T_PLIST_CYC, 0);
3181 AssPlist(out, ++nr, comp);
3182
3183 seen = AddrTmpTrans();
3184 ptf4 = CONST_ADDR_TRANS4(f);
3185
3186 for (; seen[pt] == 1; pt = ptf4[pt]) {
3187 seen[pt] = 2;
3188 AssPlist(comp, LEN_PLIST(comp) + 1,
3189 INTOBJ_INT(pt + 1));
3190 seen = AddrTmpTrans();
3191 ptf4 = CONST_ADDR_TRANS4(f);
3192 }
3193 }
3194 for (pt = j; seen[pt] == 1; pt = ptf4[pt]) {
3195 seen[pt] = 2;
3196 }
3197 }
3198 }
3199 }
3200 return out;
3201 }
3202
3203 /*******************************************************************************
3204 ** GAP level functions for the Semigroups package
3205 *******************************************************************************/
3206
3207 // Returns a transformation g such that ((i)f)g = i for all i in list,
3208 // it is assumed (and not checked) that the transformation f is injective on
3209 // list.
3210
FuncINV_LIST_TRANS(Obj self,Obj list,Obj f)3211 static Obj FuncINV_LIST_TRANS(Obj self, Obj list, Obj f)
3212 {
3213 const UInt2 *ptf2;
3214 const UInt4 *ptf4;
3215 UInt2 *ptg2;
3216 UInt4 *ptg4;
3217 UInt deg, i, j;
3218 Obj g, k;
3219
3220 RequireDenseList("INV_LIST_TRANS", list);
3221 RequireTransformation("INV_LIST_TRANS", f);
3222
3223 if (TNUM_OBJ(f) == T_TRANS2) {
3224 deg = DEG_TRANS2(f);
3225 g = NEW_TRANS2(deg);
3226 ptf2 = CONST_ADDR_TRANS2(f);
3227 ptg2 = ADDR_TRANS2(g);
3228
3229 for (j = 0; j < deg; j++) {
3230 ptg2[j] = j;
3231 }
3232 for (j = 1; j <= (UInt)LEN_LIST(list); j++) {
3233 k = ELM_LIST(list, j);
3234 if (!IS_POS_INTOBJ(k)) {
3235 ErrorQuit(
3236 "INV_LIST_TRANS: <list>[%d] must be a positive small integer "
3237 "(not a %s)",
3238 (Int)j, (Int)TNAM_OBJ(k));
3239 }
3240 i = INT_INTOBJ(k) - 1;
3241 if (i < deg) {
3242 ptg2[ptf2[i]] = i;
3243 }
3244 }
3245 return g;
3246 }
3247 else if (TNUM_OBJ(f) == T_TRANS4) {
3248 deg = DEG_TRANS4(f);
3249 g = NEW_TRANS4(deg);
3250 ptf4 = CONST_ADDR_TRANS4(f);
3251 ptg4 = ADDR_TRANS4(g);
3252
3253 i = INT_INTOBJ(ELM_LIST(list, 1)) - 1;
3254 for (j = 0; j < deg; j++) {
3255 ptg4[j] = j;
3256 }
3257 for (j = 1; j <= (UInt)LEN_LIST(list); j++) {
3258 k = ELM_LIST(list, j);
3259 if (!IS_POS_INTOBJ(k)) {
3260 ErrorQuit(
3261 "INV_LIST_TRANS: <list>[%d] must be a positive small integer "
3262 "(not a %s)",
3263 (Int)j, (Int)TNAM_OBJ(k));
3264 }
3265 i = INT_INTOBJ(k) - 1;
3266 if (i < deg) {
3267 ptg4[ptf4[i]] = i;
3268 }
3269 }
3270 return g;
3271 }
3272 return 0L;
3273 }
3274
3275 // If ker(f) = ker(g), then TRANS_IMG_CONJ returns the permutation p
3276 // such that i ^ (f ^ -1 * g) = i ^ p for all i in im(f).
3277 //
3278 // The permutation returned is the same as:
3279 //
3280 // MappingPermListList(ImageListOfTransformation(f, n),
3281 // ImageListOfTransformation(g, n));
3282 //
3283 // where n = max{DegreeOfTransformation(f), DegreeOfTransformation(g)}.
3284 // However, this is included to avoid the overhead of producing the image
3285 // lists
3286 // of f and g in the above.
3287
FuncTRANS_IMG_CONJ(Obj self,Obj f,Obj g)3288 static Obj FuncTRANS_IMG_CONJ(Obj self, Obj f, Obj g)
3289 {
3290 Obj perm;
3291 const UInt2 *ptf2, *ptg2;
3292 const UInt4 *ptf4, *ptg4;
3293 UInt4 *ptsrc, *ptdst, *ptp;
3294 UInt def, deg, i, j, max, min;
3295
3296 RequireTransformation("TRANS_IMG_CONJ", f);
3297 RequireTransformation("TRANS_IMG_CONJ", g);
3298
3299 def = DEG_TRANS(f);
3300 deg = DEG_TRANS(g);
3301 max = MAX(def, deg);
3302 min = MIN(def, deg);
3303
3304 // always return a T_PERM4 to reduce the amount of code in this function
3305 perm = NEW_PERM4(max);
3306
3307 ptsrc = ResizeInitTmpTrans(2 * max);
3308 ptdst = ptsrc + max;
3309
3310 ptp = ADDR_PERM4(perm);
3311
3312 if (TNUM_OBJ(f) == T_TRANS2 && TNUM_OBJ(g) == T_TRANS2) {
3313 ptf2 = CONST_ADDR_TRANS2(f);
3314 ptg2 = CONST_ADDR_TRANS2(g);
3315
3316 for (i = 0; i < min; i++) {
3317 ptsrc[ptf2[i]] = 1;
3318 ptdst[ptg2[i]] = 1;
3319 ptp[ptf2[i]] = ptg2[i];
3320 }
3321
3322 // if deg = min, then this isn't executed
3323 for (; i < deg; i++) {
3324 // ptsrc[i] = 1;
3325 ptdst[ptg2[i]] = 1;
3326 ptp[i] = ptg2[i];
3327 }
3328
3329 // if def = min, then this isn't executed
3330 for (; i < def; i++) {
3331 ptsrc[ptf2[i]] = 1;
3332 ptdst[i] = 1;
3333 ptp[ptf2[i]] = i;
3334 }
3335 }
3336 else if (TNUM_OBJ(f) == T_TRANS2 && TNUM_OBJ(g) == T_TRANS4) {
3337 ptf2 = CONST_ADDR_TRANS2(f);
3338 ptg4 = CONST_ADDR_TRANS4(g);
3339
3340 for (i = 0; i < min; i++) {
3341 ptsrc[ptf2[i]] = 1;
3342 ptdst[ptg4[i]] = 1;
3343 ptp[ptf2[i]] = ptg4[i];
3344 }
3345
3346 // if deg = min, then this isn't executed
3347 for (; i < deg; i++) {
3348 // ptsrc[i] = 1;
3349 ptdst[ptg4[i]] = 1;
3350 ptp[i] = ptg4[i];
3351 }
3352
3353 // if def = min, then this isn't executed
3354 for (; i < def; i++) {
3355 // The only transformation created within this file that is of
3356 // type
3357 // T_TRANS4 and that does not have (internal) degree 65537 or
3358 // greater
3359 // is ID_TRANS4.
3360 ptsrc[ptf2[i]] = 1;
3361 ptdst[i] = 1;
3362 ptp[ptf2[i]] = i;
3363 }
3364 }
3365 else if (TNUM_OBJ(f) == T_TRANS4 && TNUM_OBJ(g) == T_TRANS2) {
3366 ptf4 = CONST_ADDR_TRANS4(f);
3367 ptg2 = CONST_ADDR_TRANS2(g);
3368
3369 for (i = 0; i < min; i++) {
3370 ptsrc[ptf4[i]] = 1;
3371 ptdst[ptg2[i]] = 1;
3372 ptp[ptf4[i]] = ptg2[i];
3373 }
3374
3375 // if deg = min, then this isn't executed
3376 for (; i < deg; i++) {
3377 // The only transformation created within this file that is of
3378 // type
3379 // T_TRANS4 and that does not have (internal) degree 65537 or
3380 // greater
3381 // is ID_TRANS4.
3382 // ptsrc[i] = 1;
3383 ptdst[ptg2[i]] = 1;
3384 ptp[i] = ptg2[i];
3385 }
3386
3387 // if def = min, then this isn't executed
3388 for (; i < def; i++) {
3389 ptsrc[ptf4[i]] = 1;
3390 ptdst[i] = 1;
3391 ptp[ptf4[i]] = i;
3392 }
3393 }
3394 else if (TNUM_OBJ(f) == T_TRANS4 && TNUM_OBJ(g) == T_TRANS4) {
3395 ptf4 = CONST_ADDR_TRANS4(f);
3396 ptg4 = CONST_ADDR_TRANS4(g);
3397
3398 for (i = 0; i < min; i++) {
3399 ptsrc[ptf4[i]] = 1;
3400 ptdst[ptg4[i]] = 1;
3401 ptp[ptf4[i]] = ptg4[i];
3402 }
3403
3404 // if deg = min, then this isn't executed
3405 for (; i < deg; i++) {
3406 // ptsrc[i] = 1;
3407 ptdst[ptg4[i]] = 1;
3408 ptp[i] = ptg4[i];
3409 }
3410
3411 // if def = min, then this isn't executed
3412 for (; i < def; i++) {
3413 ptsrc[ptf4[i]] = 1;
3414 ptdst[i] = 1;
3415 ptp[ptf4[i]] = i;
3416 }
3417 }
3418
3419 // complete the permutation
3420 j = 0;
3421 for (i = 0; i < def; i++) {
3422 if (ptsrc[i] == 0) {
3423 while (ptdst[j] != 0) {
3424 j++;
3425 }
3426 ptp[i] = j;
3427 j++;
3428 }
3429 }
3430 return perm;
3431 }
3432
3433 // Returns the flat kernel of <p> ^ -1 * f * <p> where f is any transformation
3434 // such that ker(f) = <ker>, <p> is a permutation and <ker> is itself a flat
3435 // kernel of transformation. This assumes (but doesn't check) that <p> is a
3436 // permutation of [1 .. Length(<ker>)] regardless of its degree.
3437
FuncPOW_KER_PERM(Obj self,Obj ker,Obj p)3438 static Obj FuncPOW_KER_PERM(Obj self, Obj ker, Obj p)
3439 {
3440 UInt len, rank, i, dep;
3441 Obj out;
3442 UInt4 * ptcnj, *ptlkp;
3443 const UInt4 * ptp4;
3444 const UInt2 * ptp2;
3445
3446 len = LEN_LIST(ker);
3447
3448 if (len == 0) {
3449 out = NEW_PLIST_IMM(T_PLIST_EMPTY, len);
3450 SET_LEN_PLIST(out, len);
3451 return out;
3452 }
3453
3454 out = NEW_PLIST_IMM(T_PLIST_CYC, len);
3455 SET_LEN_PLIST(out, len);
3456
3457 ResizeTmpTrans(2 * len);
3458 ptcnj = AddrTmpTrans();
3459
3460 rank = 1;
3461 ptlkp = ptcnj + len;
3462
3463 if (TNUM_OBJ(p) == T_PERM2) {
3464 dep = DEG_PERM2(p);
3465 ptp2 = CONST_ADDR_PERM2(p);
3466
3467 if (dep <= len) {
3468 // form the conjugate in ptcnj and init the lookup
3469 for (i = 0; i < dep; i++) {
3470 // < p ^ - 1 * g * p > then < g > with ker( < g >) = < ker >
3471 ptcnj[ptp2[i]] = ptp2[INT_INTOBJ(ELM_LIST(ker, i + 1)) - 1];
3472 ptlkp[i] = 0;
3473 }
3474 for (; i < len; i++) {
3475 ptcnj[i] = IMAGE((UInt)INT_INTOBJ(ELM_LIST(ker, i + 1)) - 1,
3476 ptp2, dep);
3477 ptlkp[i] = 0;
3478 }
3479 }
3480 else {
3481 // dep > len but p fixes [1..len] setwise
3482
3483 // form the conjugate in ptcnj and init the lookup
3484 for (i = 0; i < len; i++) {
3485 // < p ^ - 1 * g * p > then < g > with ker( < g >) = < ker >
3486 ptcnj[ptp2[i]] = ptp2[INT_INTOBJ(ELM_LIST(ker, i + 1)) - 1];
3487 ptlkp[i] = 0;
3488 }
3489 }
3490
3491 // form the flat kernel
3492 for (i = 0; i < len; i++) {
3493 if (ptlkp[ptcnj[i]] == 0) {
3494 ptlkp[ptcnj[i]] = rank++;
3495 }
3496 SET_ELM_PLIST(out, i + 1, INTOBJ_INT(ptlkp[ptcnj[i]]));
3497 }
3498 return out;
3499 }
3500 else if (TNUM_OBJ(p) == T_PERM4) {
3501 dep = DEG_PERM4(p);
3502 ptp4 = CONST_ADDR_PERM4(p);
3503
3504 if (dep <= len) {
3505 // form the conjugate in ptcnj and init the lookup
3506 for (i = 0; i < dep; i++) {
3507 // < p ^ - 1 * g * p > then < g > with ker( < g >) = < ker >
3508 ptcnj[ptp4[i]] = ptp4[INT_INTOBJ(ELM_LIST(ker, i + 1)) - 1];
3509 ptlkp[i] = 0;
3510 }
3511 for (; i < len; i++) {
3512 ptcnj[i] = IMAGE((UInt)INT_INTOBJ(ELM_LIST(ker, i + 1)) - 1,
3513 ptp4, dep);
3514 ptlkp[i] = 0;
3515 }
3516 }
3517 else {
3518 // dep > len but p fixes [1..len] setwise
3519
3520 // form the conjugate in ptcnj and init the lookup
3521 for (i = 0; i < len; i++) {
3522 // < p ^ - 1 * g * p > then < g > with ker( < g >) = < ker >
3523 ptcnj[ptp4[i]] = ptp4[INT_INTOBJ(ELM_LIST(ker, i + 1)) - 1];
3524 ptlkp[i] = 0;
3525 }
3526 }
3527
3528 // form the flat kernel
3529 for (i = 0; i < len; i++) {
3530 if (ptlkp[ptcnj[i]] == 0) {
3531 ptlkp[ptcnj[i]] = rank++;
3532 }
3533 SET_ELM_PLIST(out, i + 1, INTOBJ_INT(ptlkp[ptcnj[i]]));
3534 }
3535 return out;
3536 }
3537 RequirePermutation("POW_KER_TRANS", p);
3538 return 0L;
3539 }
3540
3541 // If <f> is a transformation and <X> is a flat kernel of a transformation,
3542 // then we denote OnKernelAntiAction(X, f) by f ^ X. Suppose that x is a
3543 // transformation with ker(x) = <X> and ker(<f>x) = f ^ ker(x) has the same
3544 // number of classes as ker(x). Then INV_KER_TRANS(X, f) returns a
3545 // transformation g such that g<f> ^ ker(x) = ker(x) = ker(gfx) and the action
3546 // of g<f> on ker(x) is the identity.
3547 template <typename TF, typename TG>
INV_KER_TRANS(Obj X,Obj f)3548 static Obj INV_KER_TRANS(Obj X, Obj f)
3549 {
3550 Obj g;
3551 const TF * ptf;
3552 TG * ptg;
3553 UInt4 * pttmp;
3554 UInt deg, i, len;
3555
3556 len = LEN_LIST(X);
3557 ResizeTmpTrans(len);
3558
3559 deg = DEG_TRANS<TF>(f);
3560 g = NEW_TRANS<TG>(len);
3561 pttmp = AddrTmpTrans();
3562 ptf = CONST_ADDR_TRANS<TF>(f);
3563 ptg = ADDR_TRANS<TG>(g);
3564 if (deg >= len) {
3565 // calculate a transversal of f ^ ker(x) = ker(fx)
3566 for (i = 0; i < len; i++) {
3567 pttmp[INT_INTOBJ(ELM_LIST(X, ptf[i] + 1)) - 1] = i;
3568 }
3569 }
3570 else {
3571 for (i = 0; i < deg; i++) {
3572 pttmp[INT_INTOBJ(ELM_LIST(X, ptf[i] + 1)) - 1] = i;
3573 }
3574 for (; i < len; i++) {
3575 pttmp[INT_INTOBJ(ELM_LIST(X, i + 1)) - 1] = i;
3576 }
3577 }
3578 for (i = len; i >= 1; i--) {
3579 ptg[i - 1] = pttmp[INT_INTOBJ(ELM_LIST(X, i)) - 1];
3580 }
3581 return g;
3582 }
3583
FuncINV_KER_TRANS(Obj self,Obj X,Obj f)3584 static Obj FuncINV_KER_TRANS(Obj self, Obj X, Obj f)
3585 {
3586 UInt len = LEN_LIST(X);
3587
3588 if (TNUM_OBJ(f) == T_TRANS2) {
3589 if (len <= 65536) {
3590 return INV_KER_TRANS<UInt2, UInt2>(X, f);
3591 }
3592 else {
3593 return INV_KER_TRANS<UInt2, UInt4>(X, f);
3594 }
3595 }
3596 else if (TNUM_OBJ(f) == T_TRANS4) {
3597 if (len <= 65536) {
3598 return INV_KER_TRANS<UInt4, UInt2>(X, f);
3599 }
3600 else {
3601 return INV_KER_TRANS<UInt4, UInt4>(X, f);
3602 }
3603 }
3604 RequireTransformation("INV_KER_TRANS", f);
3605 return 0L;
3606 }
3607
3608 // Returns the same value as OnSets(set, f) except if set = [0], when the
3609 // image
3610 // set of <f> on [1 .. n] is returned instead. If the argument <set> is not
3611 // [0], then the third argument is ignored.
3612
FuncOnPosIntSetsTrans(Obj self,Obj set,Obj f,Obj n)3613 static Obj FuncOnPosIntSetsTrans(Obj self, Obj set, Obj f, Obj n)
3614 {
3615 const UInt2 * ptf2;
3616 const UInt4 * ptf4;
3617 const Obj * ptset;
3618 UInt deg;
3619 Obj * ptres, res;
3620 UInt i, k;
3621
3622 if (LEN_LIST(set) == 0) {
3623 return set;
3624 }
3625
3626 if (LEN_LIST(set) == 1 && INT_INTOBJ(ELM_LIST(set, 1)) == 0) {
3627 return FuncIMAGE_SET_TRANS_INT(self, f, n);
3628 }
3629
3630 PLAIN_LIST(set);
3631
3632 const UInt len = LEN_PLIST(set);
3633
3634 res = NEW_PLIST_WITH_MUTABILITY(IS_PLIST_MUTABLE(set), T_PLIST_CYC_SSORT, len);
3635 SET_LEN_PLIST(res, len);
3636
3637 ptset = CONST_ADDR_OBJ(set) + len;
3638 ptres = ADDR_OBJ(res) + len;
3639
3640 if (TNUM_OBJ(f) == T_TRANS2) {
3641 ptf2 = CONST_ADDR_TRANS2(f);
3642 deg = DEG_TRANS2(f);
3643 for (i = len; 1 <= i; i--, ptset--, ptres--) {
3644 k = INT_INTOBJ(*ptset);
3645 if (k <= deg) {
3646 k = ptf2[k - 1] + 1;
3647 }
3648 *ptres = INTOBJ_INT(k);
3649 }
3650 SortPlistByRawObj(res);
3651 REMOVE_DUPS_PLIST_INTOBJ(res);
3652 return res;
3653 }
3654 else if (TNUM_OBJ(f) == T_TRANS4) {
3655 ptf4 = CONST_ADDR_TRANS4(f);
3656 deg = DEG_TRANS4(f);
3657 for (i = len; 1 <= i; i--, ptset--, ptres--) {
3658 k = INT_INTOBJ(*ptset);
3659 if (k <= deg) {
3660 k = ptf4[k - 1] + 1;
3661 }
3662 *ptres = INTOBJ_INT(k);
3663 }
3664 SortPlistByRawObj(res);
3665 REMOVE_DUPS_PLIST_INTOBJ(res);
3666 return res;
3667 }
3668 RequireTransformation("OnPosIntSetsTrans", f);
3669 return 0L;
3670 }
3671
3672 /*******************************************************************************
3673 *******************************************************************************
3674 * GAP kernel functions for transformations
3675 *******************************************************************************
3676 *******************************************************************************/
3677
3678 // Returns the identity transformation.
3679
OneTrans(Obj f)3680 static Obj OneTrans(Obj f)
3681 {
3682 return IdentityTrans;
3683 }
3684
3685 /*******************************************************************************
3686 ** Equality for transformations
3687 *******************************************************************************/
3688
EqTrans22(Obj opL,Obj opR)3689 static Int EqTrans22(Obj opL, Obj opR)
3690 {
3691 UInt degL = DEG_TRANS2(opL);
3692 UInt degR = DEG_TRANS2(opR);
3693 const UInt2 * ptLstart = CONST_ADDR_TRANS2(opL);
3694 const UInt2 * ptRstart = CONST_ADDR_TRANS2(opR);
3695
3696 const UInt2 * ptL; // pointer to the left operand
3697 const UInt2 * ptR; // pointer to the right operand
3698 UInt p; // loop variable
3699
3700 // if perms/trans are different sizes, check final element as an early
3701 // check
3702
3703 if (degL != degR) {
3704 if (degL < degR) {
3705 if (*(ptRstart + degR - 1) != (degR - 1)) {
3706 return 0L;
3707 }
3708 }
3709 else {
3710 if (*(ptLstart + degL - 1) != (degL - 1)) {
3711 return 0L;
3712 }
3713 }
3714 }
3715
3716 // search for a difference and return False if you find one
3717 if (degL <= degR) {
3718 ptR = ptRstart + degL;
3719 for (p = degL; p < degR; p++) {
3720 if (*(ptR++) != p) {
3721 return 0L;
3722 }
3723 }
3724 if (memcmp(ptLstart, ptRstart, degL * sizeof(UInt2)) != 0) {
3725 return 0L;
3726 }
3727 }
3728 else {
3729 ptL = ptLstart + degR;
3730 for (p = degR; p < degL; p++) {
3731 if (*(ptL++) != p) {
3732 return 0L;
3733 }
3734 }
3735 if (memcmp(ptLstart, ptRstart, degR * sizeof(UInt2)) != 0) {
3736 return 0L;
3737 }
3738 }
3739
3740 // otherwise they must be equal
3741 return 1L;
3742 }
3743
EqTrans44(Obj opL,Obj opR)3744 static Int EqTrans44(Obj opL, Obj opR)
3745 {
3746 UInt degL = DEG_TRANS4(opL);
3747 UInt degR = DEG_TRANS4(opR);
3748 const UInt4 * ptLstart = CONST_ADDR_TRANS4(opL);
3749 const UInt4 * ptRstart = CONST_ADDR_TRANS4(opR);
3750
3751 const UInt4 * ptL; // pointer to the left operand
3752 const UInt4 * ptR; // pointer to the right operand
3753 UInt p; // loop variable
3754
3755 // if perms/trans are different sizes, check final element as an early
3756 // check
3757
3758 if (degL != degR) {
3759 if (degL < degR) {
3760 if (*(ptRstart + degR - 1) != (degR - 1)) {
3761 return 0L;
3762 }
3763 }
3764 else {
3765 if (*(ptLstart + degL - 1) != (degL - 1)) {
3766 return 0L;
3767 }
3768 }
3769 }
3770
3771 // search for a difference and return False if you find one
3772 if (degL <= degR) {
3773 ptR = ptRstart + degL;
3774 for (p = degL; p < degR; p++) {
3775 if (*(ptR++) != p) {
3776 return 0L;
3777 }
3778 }
3779 if (memcmp(ptLstart, ptRstart, degL * sizeof(UInt4)) != 0) {
3780 return 0L;
3781 }
3782 }
3783 else {
3784 ptL = ptLstart + degR;
3785 for (p = degR; p < degL; p++) {
3786 if (*(ptL++) != p) {
3787 return 0L;
3788 }
3789 }
3790 if (memcmp(ptLstart, ptRstart, degR * sizeof(UInt4)) != 0) {
3791 return 0L;
3792 }
3793 }
3794
3795 // otherwise they must be equal
3796 return 1L;
3797 }
3798
EqTrans24(Obj f,Obj g)3799 static Int EqTrans24(Obj f, Obj g)
3800 {
3801 UInt i, def, deg;
3802 const UInt2 * ptf;
3803 const UInt4 * ptg;
3804
3805 ptf = CONST_ADDR_TRANS2(f);
3806 ptg = CONST_ADDR_TRANS4(g);
3807 def = DEG_TRANS2(f);
3808 deg = DEG_TRANS4(g);
3809
3810 if (def <= deg) {
3811 for (i = 0; i < def; i++) {
3812 if (*(ptf++) != *(ptg++)) {
3813 return 0L;
3814 }
3815 }
3816 for (; i < deg; i++) {
3817 if (*(ptg++) != i) {
3818 return 0L;
3819 }
3820 }
3821 }
3822 else {
3823 // The only transformation created within this file that is of type
3824 // T_TRANS4 and that does not have (internal) degree 65537 or greater
3825 // is ID_TRANS4.
3826
3827 for (i = 0; i < deg; i++) {
3828 if (*(ptf++) != *(ptg++)) {
3829 return 0L;
3830 }
3831 }
3832 for (; i < def; i++) {
3833 if (*(ptf++) != i) {
3834 return 0L;
3835 }
3836 }
3837 }
3838
3839 return 1L;
3840 }
3841
EqTrans42(Obj f,Obj g)3842 static Int EqTrans42(Obj f, Obj g)
3843 {
3844 return EqTrans24(g, f);
3845 }
3846
3847 /*******************************************************************************
3848 ** Less than for transformations
3849 *******************************************************************************/
3850
3851 template <typename TF, typename TG>
LtTrans(Obj f,Obj g)3852 static Int LtTrans(Obj f, Obj g)
3853 {
3854 UInt def = DEG_TRANS<TF>(f);
3855 UInt deg = DEG_TRANS<TG>(g);
3856 UInt i;
3857
3858 const TF * ptf = CONST_ADDR_TRANS<TF>(f);
3859 const TG * ptg = CONST_ADDR_TRANS<TG>(g);
3860
3861 if (def <= deg) {
3862 for (i = 0; i < def; i++) {
3863 if (ptf[i] != ptg[i]) {
3864 return ptf[i] < ptg[i];
3865 }
3866 }
3867 for (; i < deg; i++) {
3868 if (ptg[i] != i) {
3869 return i < ptg[i];
3870 }
3871 }
3872 }
3873 else {
3874 for (i = 0; i < deg; i++) {
3875 if (ptf[i] != ptg[i]) {
3876 return ptf[i] < ptg[i];
3877 }
3878 }
3879 for (; i < def; i++) {
3880 if (ptf[i] != i) {
3881 return ptf[i] < i;
3882 }
3883 }
3884 }
3885 return 0;
3886 }
3887
3888
3889 /*******************************************************************************
3890 ** Products for transformations
3891 *******************************************************************************/
3892
3893 template <typename TF, typename TG>
ProdTrans(Obj f,Obj g)3894 static Obj ProdTrans(Obj f, Obj g)
3895 {
3896 typedef typename ResultType<TF, TG>::type Res;
3897
3898 UInt def = DEG_TRANS<TF>(f);
3899 UInt deg = DEG_TRANS<TG>(g);
3900 UInt i;
3901
3902 Obj fg = NEW_TRANS<Res>(MAX(def, deg));
3903
3904 Res * ptfg = ADDR_TRANS<Res>(fg);
3905 const TF * ptf = CONST_ADDR_TRANS<TF>(f);
3906 const TG * ptg = CONST_ADDR_TRANS<TG>(g);
3907
3908 if (def <= deg) {
3909 for (i = 0; i < def; i++) {
3910 *ptfg++ = ptg[*ptf++];
3911 }
3912 for (; i < deg; i++) {
3913 *ptfg++ = ptg[i];
3914 }
3915 }
3916 else {
3917 for (i = 0; i < def; i++) {
3918 *ptfg++ = IMAGE(ptf[i], ptg, deg);
3919 }
3920 }
3921
3922 return fg;
3923 }
3924
3925
3926 /*******************************************************************************
3927 ** Products for a transformation and permutation
3928 *******************************************************************************/
3929
3930 template <typename TF, typename TP>
ProdTransPerm(Obj f,Obj p)3931 static Obj ProdTransPerm(Obj f, Obj p)
3932 {
3933 typedef typename ResultType<TF, TP>::type Res;
3934
3935 UInt dep = DEG_PERM<TP>(p);
3936 UInt def = DEG_TRANS<TF>(f);
3937 UInt i;
3938
3939 Obj fp = NEW_TRANS<Res>(MAX(def, dep));
3940
3941 Res * ptfp = ADDR_TRANS<Res>(fp);
3942 const TF * ptf = CONST_ADDR_TRANS<TF>(f);
3943 const TP * ptp = CONST_ADDR_PERM<TP>(p);
3944
3945 if (def <= dep) {
3946 for (i = 0; i < def; i++) {
3947 *ptfp++ = ptp[*ptf++];
3948 }
3949 for (; i < dep; i++) {
3950 *ptfp++ = ptp[i];
3951 }
3952 }
3953 else {
3954 for (i = 0; i < def; i++) {
3955 *ptfp++ = IMAGE(ptf[i], ptp, dep);
3956 }
3957 }
3958 return fp;
3959 }
3960
3961
3962 /*******************************************************************************
3963 ** Products for a permutation and transformation
3964 *******************************************************************************/
3965
3966 template <typename TP, typename TF>
ProdPermTrans(Obj p,Obj f)3967 static Obj ProdPermTrans(Obj p, Obj f)
3968 {
3969 typedef typename ResultType<TF, TP>::type Res;
3970
3971 UInt dep = DEG_PERM<TP>(p);
3972 UInt def = DEG_TRANS<TF>(f);
3973 UInt i;
3974
3975 Obj pf = NEW_TRANS<Res>(MAX(def, dep));
3976
3977 Res * ptpf = ADDR_TRANS<Res>(pf);
3978 const TF * ptf = CONST_ADDR_TRANS<TF>(f);
3979 const TP * ptp = CONST_ADDR_PERM<TP>(p);
3980
3981 if (dep <= def) {
3982 for (i = 0; i < dep; i++) {
3983 *ptpf++ = ptf[*ptp++];
3984 }
3985 for (; i < def; i++) {
3986 *ptpf++ = ptf[i];
3987 }
3988 }
3989 else {
3990 for (i = 0; i < dep; i++) {
3991 *ptpf++ = IMAGE(ptp[i], ptf, def);
3992 }
3993 }
3994 return pf;
3995 }
3996
3997
3998 /*******************************************************************************
3999 ** Conjugate a transformation f by a permutation p: p ^ -1 * f * p
4000 *******************************************************************************/
4001
4002 template <typename TF, typename TP>
PowTransPerm(Obj f,Obj p)4003 static Obj PowTransPerm(Obj f, Obj p)
4004 {
4005 typedef typename ResultType<TF, TP>::type Res;
4006
4007 UInt dep = DEG_PERM<TP>(p);
4008 UInt def = DEG_TRANS<TF>(f);
4009 UInt decnj = MAX(dep, def);
4010 UInt i;
4011
4012 Obj cnj = NEW_TRANS<Res>(decnj);
4013
4014 Res * ptcnj = ADDR_TRANS<Res>(cnj);
4015 const TF * ptf = CONST_ADDR_TRANS<TF>(f);
4016 const TP * ptp = CONST_ADDR_PERM<TP>(p);
4017
4018 if (def == dep) {
4019 for (i = 0; i < decnj; i++) {
4020 ptcnj[ptp[i]] = ptp[ptf[i]];
4021 }
4022 }
4023 else {
4024 for (i = 0; i < decnj; i++) {
4025 ptcnj[IMAGE(i, ptp, dep)] = IMAGE(IMAGE(i, ptf, def), ptp, dep);
4026 }
4027 }
4028 return cnj;
4029 }
4030
4031
4032 /*******************************************************************************
4033 ** Left quotient a transformation f by a permutation p: p ^ -1 * f
4034 *******************************************************************************/
4035
4036 template <typename TL, typename TR>
LQuoPermTrans(Obj opL,Obj opR)4037 static Obj LQuoPermTrans(Obj opL, Obj opR)
4038 {
4039 typedef typename ResultType<TL, TR>::type Res;
4040
4041 UInt degL = DEG_PERM<TL>(opL);
4042 UInt degR = DEG_TRANS<TR>(opR);
4043 UInt degM = degL < degR ? degR : degL;
4044 UInt p;
4045
4046 Obj mod = NEW_TRANS<Res>(degM);
4047
4048 Res * ptM = ADDR_TRANS<Res>(mod);
4049 const TL * ptL = CONST_ADDR_PERM<TL>(opL);
4050 const TR * ptR = CONST_ADDR_TRANS<TR>(opR);
4051
4052 if (degL <= degR) {
4053 for (p = 0; p < degL; p++) {
4054 ptM[*(ptL++)] = *(ptR++);
4055 }
4056 for (p = degL; p < degR; p++) {
4057 ptM[p] = *(ptR++);
4058 }
4059 }
4060 else {
4061 for (p = 0; p < degR; p++) {
4062 ptM[*(ptL++)] = *(ptR++);
4063 }
4064 for (p = degR; p < degL; p++) {
4065 ptM[*(ptL++)] = p;
4066 }
4067 }
4068
4069 return mod;
4070 }
4071
4072
4073 /*******************************************************************************
4074 ** Apply a transformation to a point
4075 *******************************************************************************/
4076
PowIntTrans2(Obj point,Obj f)4077 static Obj PowIntTrans2(Obj point, Obj f)
4078 {
4079 Int img;
4080
4081 if (TNUM_OBJ(point) == T_INTPOS) {
4082 return point;
4083 }
4084
4085 img = GetPositiveSmallInt("Tran. Operations", point);
4086
4087 if ((UInt)img <= DEG_TRANS2(f)) {
4088 img = (CONST_ADDR_TRANS2(f))[img - 1] + 1;
4089 }
4090
4091 return INTOBJ_INT(img);
4092 }
4093
PowIntTrans4(Obj point,Obj f)4094 static Obj PowIntTrans4(Obj point, Obj f)
4095 {
4096 Int img;
4097
4098 if (TNUM_OBJ(point) == T_INTPOS) {
4099 return point;
4100 }
4101
4102 img = GetPositiveSmallInt("Tran. Operations", point);
4103
4104 if ((UInt)img <= DEG_TRANS4(f)) {
4105 img = (CONST_ADDR_TRANS4(f))[img - 1] + 1;
4106 }
4107
4108 return INTOBJ_INT(img);
4109 }
4110
4111 /****************************************************************************
4112 **
4113 *F OnSetsTrans( <set>, <f> ) . . . . . . . . . operations on sets of points
4114 **
4115 ** 'OnSetsTrans' returns the image of the tuple <set> under the
4116 ** transformation <f>. It is called from 'FuncOnSets'.
4117 **
4118 ** The input <set> must be a non-empty set, i.e., plain, dense and strictly
4119 ** sorted. This is is not verified.
4120 */
OnSetsTrans(Obj set,Obj f)4121 Obj OnSetsTrans(Obj set, Obj f)
4122 {
4123 const UInt2 * ptf2;
4124 const UInt4 * ptf4;
4125 UInt deg;
4126 const Obj * ptset;
4127 Obj * ptres, tmp, res;
4128 UInt i, isint, k;
4129
4130 GAP_ASSERT(IS_PLIST(set));
4131 GAP_ASSERT(LEN_PLIST(set) > 0);
4132
4133 const UInt len = LEN_PLIST(set);
4134
4135 res = NEW_PLIST_WITH_MUTABILITY(IS_PLIST_MUTABLE(set), T_PLIST, len);
4136 SET_LEN_PLIST(res, len);
4137
4138 ptset = CONST_ADDR_OBJ(set) + len;
4139 ptres = ADDR_OBJ(res) + len;
4140 if (TNUM_OBJ(f) == T_TRANS2) {
4141 ptf2 = CONST_ADDR_TRANS2(f);
4142 deg = DEG_TRANS2(f);
4143 // loop over the entries of the tuple
4144 isint = 1;
4145 for (i = len; 1 <= i; i--, ptset--, ptres--) {
4146 if (IS_POS_INTOBJ(*ptset)) {
4147 k = INT_INTOBJ(*ptset);
4148 if (k <= deg) {
4149 k = ptf2[k - 1] + 1;
4150 }
4151 *ptres = INTOBJ_INT(k);
4152 }
4153 else {
4154 isint = 0;
4155 tmp = POW(*ptset, f);
4156 ptset = CONST_ADDR_OBJ(set) + i;
4157 ptres = ADDR_OBJ(res) + i;
4158 ptf2 = CONST_ADDR_TRANS2(f);
4159 *ptres = tmp;
4160 CHANGED_BAG(res);
4161 }
4162 }
4163 }
4164 else {
4165 ptf4 = CONST_ADDR_TRANS4(f);
4166 deg = DEG_TRANS4(f);
4167
4168 // loop over the entries of the tuple
4169 isint = 1;
4170 for (i = len; 1 <= i; i--, ptset--, ptres--) {
4171 if (IS_POS_INTOBJ(*ptset)) {
4172 k = INT_INTOBJ(*ptset);
4173 if (k <= deg) {
4174 k = ptf4[k - 1] + 1;
4175 }
4176 *ptres = INTOBJ_INT(k);
4177 }
4178 else {
4179 isint = 0;
4180 tmp = POW(*ptset, f);
4181 ptset = CONST_ADDR_OBJ(set) + i;
4182 ptres = ADDR_OBJ(res) + i;
4183 ptf4 = CONST_ADDR_TRANS4(f);
4184 *ptres = tmp;
4185 CHANGED_BAG(res);
4186 }
4187 }
4188 }
4189
4190 // sort the result and remove dups
4191 if (isint) {
4192 SortPlistByRawObj(res);
4193 REMOVE_DUPS_PLIST_INTOBJ(res);
4194
4195 RetypeBagSM(res, T_PLIST_CYC_SSORT);
4196 }
4197 else {
4198 SortDensePlist(res);
4199 RemoveDupsDensePlist(res);
4200 }
4201
4202 return res;
4203 }
4204
4205 /****************************************************************************
4206 **
4207 *F OnTuplesTrans( <tup>, <f> ) . . . . . . . operations on tuples of points
4208 **
4209 ** 'OnTuplesTrans' returns the image of the tuple <tup> under the
4210 ** transformation <f>. It is called from 'FuncOnTuples'.
4211 **
4212 ** The input <tup> must be a non-empty and dense plain list. This is is not
4213 ** verified.
4214 */
OnTuplesTrans(Obj tup,Obj f)4215 Obj OnTuplesTrans(Obj tup, Obj f)
4216 {
4217 const UInt2 * ptf2;
4218 const UInt4 * ptf4;
4219 UInt deg, i, k;
4220 const Obj * pttup;
4221 Obj * ptres, res, tmp;
4222
4223 GAP_ASSERT(IS_PLIST(tup));
4224 GAP_ASSERT(LEN_PLIST(tup) > 0);
4225
4226 const UInt len = LEN_PLIST(tup);
4227
4228 res = NEW_PLIST_WITH_MUTABILITY(IS_PLIST_MUTABLE(tup), T_PLIST, len);
4229 SET_LEN_PLIST(res, len);
4230
4231 pttup = CONST_ADDR_OBJ(tup) + len;
4232 ptres = ADDR_OBJ(res) + len;
4233
4234 if (TNUM_OBJ(f) == T_TRANS2) {
4235 ptf2 = CONST_ADDR_TRANS2(f);
4236 deg = DEG_TRANS2(f);
4237
4238 // loop over the entries of the tuple
4239 for (i = len; 1 <= i; i--, pttup--, ptres--) {
4240 if (IS_POS_INTOBJ(*pttup)) {
4241 k = INT_INTOBJ(*pttup);
4242 if (k <= deg) {
4243 k = ptf2[k - 1] + 1;
4244 }
4245 *ptres = INTOBJ_INT(k);
4246 }
4247 else {
4248 if (*pttup == NULL) {
4249 ErrorQuit("OnTuples for transformation: list must not "
4250 "contain holes",
4251 0L, 0L);
4252 }
4253 tmp = POW(*pttup, f);
4254 pttup = CONST_ADDR_OBJ(tup) + i;
4255 ptres = ADDR_OBJ(res) + i;
4256 ptf2 = CONST_ADDR_TRANS2(f);
4257 *ptres = tmp;
4258 CHANGED_BAG(res);
4259 }
4260 }
4261 }
4262 else {
4263 ptf4 = CONST_ADDR_TRANS4(f);
4264 deg = DEG_TRANS4(f);
4265
4266 // loop over the entries of the tuple
4267 for (i = len; 1 <= i; i--, pttup--, ptres--) {
4268 if (IS_POS_INTOBJ(*pttup)) {
4269 k = INT_INTOBJ(*pttup);
4270 if (k <= deg) {
4271 k = ptf4[k - 1] + 1;
4272 }
4273 *ptres = INTOBJ_INT(k);
4274 }
4275 else {
4276 if (*pttup == NULL) {
4277 ErrorQuit("OnTuples for transformation: list must not "
4278 "contain holes",
4279 0L, 0L);
4280 }
4281 tmp = POW(*pttup, f);
4282 pttup = CONST_ADDR_OBJ(tup) + i;
4283 ptres = ADDR_OBJ(res) + i;
4284 ptf4 = CONST_ADDR_TRANS4(f);
4285 *ptres = tmp;
4286 CHANGED_BAG(res);
4287 }
4288 }
4289 }
4290 return res;
4291 }
4292
4293 /*******************************************************************************
4294 ** Save and load workspace, garbage collection, IS_TRANS
4295 *******************************************************************************/
4296
4297 // Save and load
SaveTrans2(Obj f)4298 static void SaveTrans2(Obj f)
4299 {
4300 const UInt2 * ptr;
4301 UInt len, i;
4302 ptr = CONST_ADDR_TRANS2(f); // save the image list
4303 len = DEG_TRANS2(f);
4304 for (i = 0; i < len; i++) {
4305 SaveUInt2(*ptr++);
4306 }
4307 }
4308
LoadTrans2(Obj f)4309 static void LoadTrans2(Obj f)
4310 {
4311 UInt2 * ptr;
4312 UInt len, i;
4313 len = DEG_TRANS2(f);
4314 ptr = ADDR_TRANS2(f);
4315 for (i = 0; i < len; i++) {
4316 *ptr++ = LoadUInt2();
4317 }
4318 }
4319
SaveTrans4(Obj f)4320 static void SaveTrans4(Obj f)
4321 {
4322 const UInt4 * ptr;
4323 UInt len, i;
4324 ptr = CONST_ADDR_TRANS4(f); // save the image list
4325 len = DEG_TRANS4(f);
4326 for (i = 0; i < len; i++) {
4327 SaveUInt4(*ptr++);
4328 }
4329 }
4330
LoadTrans4(Obj f)4331 static void LoadTrans4(Obj f)
4332 {
4333 UInt4 * ptr;
4334 UInt len, i;
4335 len = DEG_TRANS4(f);
4336 ptr = ADDR_TRANS4(f);
4337 for (i = 0; i < len; i++) {
4338 *ptr++ = LoadUInt4();
4339 }
4340 }
4341
4342 static Obj TYPE_TRANS2;
4343
TypeTrans2(Obj f)4344 static Obj TypeTrans2(Obj f)
4345 {
4346 return TYPE_TRANS2;
4347 }
4348
4349 static Obj TYPE_TRANS4;
4350
TypeTrans4(Obj f)4351 static Obj TypeTrans4(Obj f)
4352 {
4353 return TYPE_TRANS4;
4354 }
4355
4356 static Obj IsTransFilt;
4357
FiltIS_TRANS(Obj self,Obj val)4358 static Obj FiltIS_TRANS(Obj self, Obj val)
4359 {
4360 if (TNUM_OBJ(val) == T_TRANS2 || TNUM_OBJ(val) == T_TRANS4) {
4361 return True;
4362 }
4363 else if (TNUM_OBJ(val) < FIRST_EXTERNAL_TNUM) {
4364 return False;
4365 }
4366 else {
4367 return DoFilter(self, val);
4368 }
4369 }
4370
4371 /****************************************************************************
4372 **
4373 *F * * * * * * * * * * * * * initialize module * * * * * * * * * * * * * * *
4374 */
4375
4376 /****************************************************************************
4377 **
4378 *V BagNames . . . . . . . . . . . . . . . . . . . . . . . list of bag names
4379 */
4380 static StructBagNames BagNames[] = {
4381 { T_TRANS2, "transformation (small)" },
4382 { T_TRANS4, "transformation (large)" },
4383 { -1, "" }
4384 };
4385
4386 /****************************************************************************
4387 **
4388 *V GVarFilts . . . . . . . . . . . . . . . . . . . list of filters to export
4389 */
4390 static StructGVarFilt GVarFilts[] = {
4391
4392 GVAR_FILT(IS_TRANS, "obj", &IsTransFilt),
4393 { 0, 0, 0, 0, 0 }
4394
4395 };
4396
4397 /******************************************************************************
4398 *V GVarFuncs . . . . . . . . . . . . . . . . . . list of functions to export
4399 */
4400 static StructGVarFunc GVarFuncs[] = {
4401
4402 GVAR_FUNC(TransformationNC, 1, "list"),
4403 GVAR_FUNC(TransformationListListNC, 2, "src, ran"),
4404 GVAR_FUNC(DegreeOfTransformation, 1, "f"),
4405 GVAR_FUNC(HASH_FUNC_FOR_TRANS, 2, "f, data"),
4406 GVAR_FUNC(RANK_TRANS, 1, "f"),
4407 GVAR_FUNC(RANK_TRANS_INT, 2, "f, n"),
4408 GVAR_FUNC(RANK_TRANS_LIST, 2, "f, list"),
4409 GVAR_FUNC(LARGEST_MOVED_PT_TRANS, 1, "f"),
4410 GVAR_FUNC(LARGEST_IMAGE_PT, 1, "f"),
4411 GVAR_FUNC(SMALLEST_MOVED_PT_TRANS, 1, "f"),
4412 GVAR_FUNC(SMALLEST_IMAGE_PT, 1, "f"),
4413 GVAR_FUNC(NR_MOVED_PTS_TRANS, 1, "f"),
4414 GVAR_FUNC(MOVED_PTS_TRANS, 1, "f"),
4415 GVAR_FUNC(IMAGE_LIST_TRANS_INT, 2, "f, n"),
4416 GVAR_FUNC(FLAT_KERNEL_TRANS, 1, "f"),
4417 GVAR_FUNC(FLAT_KERNEL_TRANS_INT, 2, "f, n"),
4418 GVAR_FUNC(IMAGE_SET_TRANS, 1, "f"),
4419 GVAR_FUNC(UNSORTED_IMAGE_SET_TRANS, 1, "f"),
4420 GVAR_FUNC(IMAGE_SET_TRANS_INT, 2, "f, n"),
4421 GVAR_FUNC(KERNEL_TRANS, 2, "f, n"),
4422 GVAR_FUNC(PREIMAGES_TRANS_INT, 2, "f, pt"),
4423 GVAR_FUNC(AS_TRANS_PERM, 1, "f"),
4424 GVAR_FUNC(AS_TRANS_PERM_INT, 2, "f, n"),
4425 GVAR_FUNC(AS_PERM_TRANS, 1, "f"),
4426 GVAR_FUNC(PermutationOfImage, 1, "f"),
4427 GVAR_FUNC(RestrictedTransformation, 2, "f, list"),
4428 GVAR_FUNC(AS_TRANS_TRANS, 2, "f, m"),
4429 GVAR_FUNC(TRIM_TRANS, 2, "f, m"),
4430 GVAR_FUNC(IsInjectiveListTrans, 2, "t, l"),
4431 GVAR_FUNC(PermLeftQuoTransformationNC, 2, "f, g"),
4432 GVAR_FUNC(TRANS_IMG_KER_NC, 2, "img, ker"),
4433 GVAR_FUNC(IDEM_IMG_KER_NC, 2, "img, ker"),
4434 GVAR_FUNC(InverseOfTransformation, 1, "f"),
4435 GVAR_FUNC(INV_LIST_TRANS, 2, "list, f"),
4436 GVAR_FUNC(TRANS_IMG_CONJ, 2, "f, g"),
4437 GVAR_FUNC(IndexPeriodOfTransformation, 1, "f"),
4438 GVAR_FUNC(SMALLEST_IDEM_POW_TRANS, 1, "f"),
4439 GVAR_FUNC(POW_KER_PERM, 2, "ker, f"),
4440 GVAR_FUNC(ON_KERNEL_ANTI_ACTION, 3, "ker, f, n"),
4441 GVAR_FUNC(INV_KER_TRANS, 2, "ker, f"),
4442 GVAR_FUNC(IS_IDEM_TRANS, 1, "f"),
4443 GVAR_FUNC(IS_ID_TRANS, 1, "f"),
4444 GVAR_FUNC(COMPONENT_REPS_TRANS, 1, "f"),
4445 GVAR_FUNC(NR_COMPONENTS_TRANS, 1, "f"),
4446 GVAR_FUNC(COMPONENTS_TRANS, 1, "f"),
4447 GVAR_FUNC(COMPONENT_TRANS_INT, 2, "f, pt"),
4448 GVAR_FUNC(CYCLE_TRANS_INT, 2, "f, pt"),
4449 GVAR_FUNC(CYCLES_TRANS, 1, "f"),
4450 GVAR_FUNC(CYCLES_TRANS_LIST, 2, "f, pt"),
4451 GVAR_FUNC(LEFT_ONE_TRANS, 1, "f"),
4452 GVAR_FUNC(RIGHT_ONE_TRANS, 1, "f"),
4453 GVAR_FUNC(OnPosIntSetsTrans, 3, "set, f, n"),
4454 { 0, 0, 0, 0, 0 }
4455
4456 };
4457
4458
4459 /******************************************************************************
4460 *F InitKernel( <module> ) . . . . . . . . initialise kernel data structures
4461 */
InitKernel(StructInitInfo * module)4462 static Int InitKernel(StructInitInfo * module)
4463 {
4464 // set the bag type names (for error messages and debugging)
4465 InitBagNamesFromTable( BagNames );
4466
4467 /* install the marking functions */
4468 InitMarkFuncBags(T_TRANS2, MarkThreeSubBags);
4469 InitMarkFuncBags(T_TRANS4, MarkThreeSubBags);
4470
4471 #ifdef HPCGAP
4472 MakeBagTypePublic(T_TRANS2);
4473 MakeBagTypePublic(T_TRANS4);
4474 #endif
4475
4476 /* install the type functions */
4477 ImportGVarFromLibrary("TYPE_TRANS2", &TYPE_TRANS2);
4478 ImportGVarFromLibrary("TYPE_TRANS4", &TYPE_TRANS4);
4479
4480 TypeObjFuncs[T_TRANS2] = TypeTrans2;
4481 TypeObjFuncs[T_TRANS4] = TypeTrans4;
4482
4483 /* init filters and functions */
4484 InitHdlrFiltsFromTable(GVarFilts);
4485 InitHdlrFuncsFromTable(GVarFuncs);
4486
4487 /* make the buffer bag */
4488 #ifndef HPCGAP
4489 InitGlobalBag(&MODULE_STATE(Trans).TmpTrans, "src/trans.c:TmpTrans");
4490 #endif
4491
4492 // make the identity trans
4493 InitGlobalBag(&IdentityTrans, "src/trans.c:IdentityTrans");
4494
4495 /* install the saving functions */
4496 SaveObjFuncs[T_TRANS2] = SaveTrans2;
4497 LoadObjFuncs[T_TRANS2] = LoadTrans2;
4498 SaveObjFuncs[T_TRANS4] = SaveTrans4;
4499 LoadObjFuncs[T_TRANS4] = LoadTrans4;
4500
4501 /* install the comparison methods */
4502 EqFuncs[T_TRANS2][T_TRANS2] = EqTrans22;
4503 EqFuncs[T_TRANS2][T_TRANS4] = EqTrans24;
4504 EqFuncs[T_TRANS4][T_TRANS2] = EqTrans42;
4505 EqFuncs[T_TRANS4][T_TRANS4] = EqTrans44;
4506 LtFuncs[T_TRANS2][T_TRANS2] = LtTrans<UInt2, UInt2>;
4507 LtFuncs[T_TRANS2][T_TRANS4] = LtTrans<UInt2, UInt4>;
4508 LtFuncs[T_TRANS4][T_TRANS2] = LtTrans<UInt4, UInt2>;
4509 LtFuncs[T_TRANS4][T_TRANS4] = LtTrans<UInt4, UInt4>;
4510
4511 /* install the binary operations */
4512 ProdFuncs[T_TRANS2][T_TRANS2] = ProdTrans<UInt2, UInt2>;
4513 ProdFuncs[T_TRANS2][T_TRANS4] = ProdTrans<UInt2, UInt4>;
4514 ProdFuncs[T_TRANS4][T_TRANS2] = ProdTrans<UInt4, UInt2>;
4515 ProdFuncs[T_TRANS4][T_TRANS4] = ProdTrans<UInt4, UInt4>;
4516 ProdFuncs[T_TRANS2][T_PERM2] = ProdTransPerm<UInt2, UInt2>;
4517 ProdFuncs[T_TRANS2][T_PERM4] = ProdTransPerm<UInt2, UInt4>;
4518 ProdFuncs[T_TRANS4][T_PERM2] = ProdTransPerm<UInt4, UInt2>;
4519 ProdFuncs[T_TRANS4][T_PERM4] = ProdTransPerm<UInt4, UInt4>;
4520 ProdFuncs[T_PERM2][T_TRANS2] = ProdPermTrans<UInt2, UInt2>;
4521 ProdFuncs[T_PERM2][T_TRANS4] = ProdPermTrans<UInt2, UInt4>;
4522 ProdFuncs[T_PERM4][T_TRANS2] = ProdPermTrans<UInt4, UInt2>;
4523 ProdFuncs[T_PERM4][T_TRANS4] = ProdPermTrans<UInt4, UInt4>;
4524 PowFuncs[T_TRANS2][T_PERM2] = PowTransPerm<UInt2, UInt2>;
4525 PowFuncs[T_TRANS2][T_PERM4] = PowTransPerm<UInt2, UInt4>;
4526 PowFuncs[T_TRANS4][T_PERM2] = PowTransPerm<UInt4, UInt2>;
4527 PowFuncs[T_TRANS4][T_PERM4] = PowTransPerm<UInt4, UInt4>;
4528 // for quotients of a transformation by a permutation, we rely on the
4529 // default handler 'QuoDefault'; that uses the inverse of the permutation,
4530 // which is cached
4531 LQuoFuncs[T_PERM2][T_TRANS2] = LQuoPermTrans<UInt2, UInt2>;
4532 LQuoFuncs[T_PERM2][T_TRANS4] = LQuoPermTrans<UInt2, UInt4>;
4533 LQuoFuncs[T_PERM4][T_TRANS2] = LQuoPermTrans<UInt4, UInt2>;
4534 LQuoFuncs[T_PERM4][T_TRANS4] = LQuoPermTrans<UInt4, UInt4>;
4535 PowFuncs[T_INT][T_TRANS2] = PowIntTrans2;
4536 PowFuncs[T_INT][T_TRANS4] = PowIntTrans4;
4537 PowFuncs[T_INTPOS][T_TRANS2] = PowIntTrans2;
4538 PowFuncs[T_INTPOS][T_TRANS4] = PowIntTrans4;
4539
4540 /* install the 'ONE' function for transformations */
4541 OneFuncs[T_TRANS2] = OneTrans;
4542 OneMutFuncs[T_TRANS2] = OneTrans;
4543 OneFuncs[T_TRANS4] = OneTrans;
4544 OneMutFuncs[T_TRANS4] = OneTrans;
4545
4546 /* return success */
4547 return 0;
4548 }
4549
4550 /******************************************************************************
4551 *F InitLibrary( <module> ) . . . . . . . initialise library data structures
4552 */
InitLibrary(StructInitInfo * module)4553 static Int InitLibrary(StructInitInfo * module)
4554 {
4555 /* init filters and functions */
4556 InitGVarFuncsFromTable(GVarFuncs);
4557 InitGVarFiltsFromTable(GVarFilts);
4558 IdentityTrans = NEW_TRANS2(0);
4559
4560 // We make the next transformation to allow testing of some parts of the
4561 // code which would not otherwise be accessible, since no other
4562 // transformation created in this file is a T_TRANS4 unless its internal
4563 // degree is > 65536. Such transformation can be created by packages with
4564 // a
4565 // kernel module, and so we introduce the next transformation for testing
4566 // purposes.
4567 Obj ID_TRANS4 = NEW_TRANS4(0);
4568 AssReadOnlyGVar(GVarName("ID_TRANS4"), ID_TRANS4);
4569
4570 /* return success */
4571 return 0;
4572 }
4573
InitModuleState(void)4574 static Int InitModuleState(void)
4575 {
4576 MODULE_STATE(Trans).TmpTrans = 0;
4577
4578 // return success
4579 return 0;
4580 }
4581
4582 /****************************************************************************
4583 **
4584 *F InitInfoTrans() . . . . . . . . . . . . . . . table of init functions
4585 */
4586 static StructInitInfo module = {
4587 /* type = */ MODULE_BUILTIN,
4588 /* name = */ "trans",
4589 /* revision_c = */ 0,
4590 /* revision_h = */ 0,
4591 /* version = */ 0,
4592 /* crc = */ 0,
4593 /* initKernel = */ InitKernel,
4594 /* initLibrary = */ InitLibrary,
4595 /* checkInit = */ 0,
4596 /* preSave = */ 0,
4597 /* postSave = */ 0,
4598 /* postRestore = */ 0,
4599 /* moduleStateSize = */ sizeof(TransModuleState),
4600 /* moduleStateOffsetPtr = */ &TransStateOffset,
4601 /* initModuleState = */ InitModuleState,
4602 /* destroyModuleState = */ 0,
4603 };
4604
InitInfoTrans(void)4605 StructInitInfo * InitInfoTrans(void)
4606 {
4607 return &module;
4608 }
4609