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