1############################################################################
2##
3##  bipart.gi
4##  Copyright (C) 2013-15                                James D. Mitchell
5##
6##  Licensing information can be found in the README file of this package.
7##
8#############################################################################
9##
10
11#############################################################################
12# Family and type.
13#
14# One per degree to avoid lists with bipartitions of different degrees
15# belonging to IsAssociativeElementCollection.
16#############################################################################
17
18BindGlobal("TYPES_BIPART", []);
19BindGlobal("TYPE_BIPART",
20function(n)
21  local fam, type;
22
23  n := n + 1;  # since the degree can be 0
24
25  if IsBound(TYPES_BIPART[n]) then
26    return TYPES_BIPART[n];
27  fi;
28
29  fam := NewFamily(Concatenation("BipartitionFamily", String(n - 1)),
30                   IsBipartition,
31                   CanEasilySortElements,
32                   CanEasilySortElements);
33
34  type := NewType(fam,
35                  IsBipartition and IsInternalRep);
36  TYPES_BIPART[n] := type;
37  return type;
38end);
39
40#############################################################################
41# Pickler
42#############################################################################
43
44InstallMethod(IO_Pickle, "for a bipartition",
45[IsFile, IsBipartition],
46function(file, x)
47  if IO_Write(file, "BIPA") = fail then
48    return IO_Error;
49  fi;
50  return IO_Pickle(file, IntRepOfBipartition(x));
51end);
52
53IO_Unpicklers.BIPA := function(file)
54  local blocks;
55
56  blocks := IO_Unpickle(file);
57  if blocks = IO_Error then
58    return IO_Error;
59  fi;
60  return BIPART_NC(blocks);
61end;
62
63#############################################################################
64# Implications
65#############################################################################
66
67InstallTrueMethod(IsPermBipartition, IsTransBipartition
68                                     and IsDualTransBipartition);
69
70InstallTrueMethod(IsBlockBijection, IsPermBipartition);
71
72#############################################################################
73# GAP level - directly using interface to C/C++ level
74#############################################################################
75
76# Fundamental attributes
77
78InstallMethod(DegreeOfBipartition, "for a bipartition",
79[IsBipartition], BIPART_DEGREE);
80
81InstallMethod(NrBlocks, "for a bipartition",
82[IsBipartition], BIPART_NR_BLOCKS);
83
84InstallMethod(NrLeftBlocks, "for a bipartition",
85[IsBipartition], BIPART_NR_LEFT_BLOCKS);
86
87InstallMethod(RankOfBipartition, "for a bipartition",
88[IsBipartition], x -> BIPART_RANK(x, 0));
89
90# Constructors
91
92InstallGlobalFunction(Bipartition,
93function(classes)
94  local n, copy, i, j;
95
96  if not IsList(classes)
97      or ForAny(classes, x -> not IsHomogeneousList(x)
98                              or not IsDuplicateFree(x)) then
99    ErrorNoReturn("Semigroups: Bipartition: usage,\n",
100                  "the argument <classes> must consist of duplicate-free ",
101                  "homogeneous lists,");
102  fi;
103
104  n := Sum(classes, Length) / 2;
105
106  if n >= 2 ^ 29 then
107    ErrorNoReturn("Semigroups: Bipartition: usage,\n",
108                  "the maximum degree of a bipartition ",
109                  "is 2 ^ 29 - 1,");
110  elif not ForAll(classes, x -> ForAll(x,
111                                       i -> (IsPosInt(i) or IsNegInt(i))
112                                            and AbsInt(i) <= n)) then
113    ErrorNoReturn("Semigroups: Bipartition: usage,\n",
114                  "the argument <classes> must consist of lists of ",
115                  "integers from [-", n, " .. -1, 1 .. ", n, "],");
116  elif not IsEmpty(classes)
117      and Union(classes) <> Concatenation([-n .. -1], [1 .. n]) then
118    ErrorNoReturn("Semigroups: Bipartition: usage,\n",
119                  "the union of the argument <classes> must be ",
120                  "[-", n, " .. -1, 1 .. ", n, "],");
121  fi;
122
123  copy := List(classes, ShallowCopy);
124  for i in [1 .. Length(copy)] do
125    for j in [1 .. Length(copy[i])] do
126      if copy[i][j] < 0 then
127        copy[i][j] := AbsInt(copy[i][j]) + n;
128      fi;
129    od;
130  od;
131
132  Perform(copy, Sort);
133  Sort(copy);
134
135  for i in [1 .. Length(copy)] do
136    for j in [1 .. Length(copy[i])] do
137      if copy[i][j] > n then
138        copy[i][j] := -copy[i][j] + n;
139      fi;
140    od;
141  od;
142  return BIPART_NC(copy);
143end);
144
145InstallMethod(BipartitionByIntRep, "for a list", [IsList],
146function(blocks)
147  local n, next, seen, i;
148
149  n := Length(blocks);
150  if not IsEvenInt(n) then
151    ErrorNoReturn("Semigroups: BipartitionByIntRep: usage,\n",
152                  "the length of the argument <blocks> must be an even ",
153                  "integer,");
154  elif n >= 2 ^ 30 then
155    ErrorNoReturn("Semigroups: BipartitionByIntRep: usage,\n",
156                  "the length of the argument <blocks> must not exceed ",
157                  "2 ^ 30 - 1,");
158  elif not ForAll(blocks, i -> IsPosInt(i) and i <= n) then
159    ErrorNoReturn("Semigroups: BipartitionByIntRep: usage,\n",
160                  "the elements of the argument <blocks> must be positive ",
161                  "integers not exceeding ", n, ",");
162  fi;
163
164  next := 0;
165  seen := BlistList([1 .. Maximum(blocks)], []);
166
167  for i in [1 .. n] do
168    if not seen[blocks[i]] then
169      next := next + 1;
170      if blocks[i] <> next then
171        ErrorNoReturn("Semigroups: BipartitionByIntRep: usage,\n",
172                      "expected ", next, " but found ", blocks[i],
173                      ", in position ", i, ",");
174      fi;
175      seen[blocks[i]] := true;
176    fi;
177  od;
178  return BIPART_NC(blocks);
179end);
180
181InstallMethod(IdentityBipartition, "for zero", [IsZeroCyc],
182function(n)
183  return Bipartition([]);
184end);
185
186InstallMethod(IdentityBipartition, "for a positive integer", [IsPosInt],
187function(n)
188  local blocks, i;
189
190  if n >= 2 ^ 29 then
191    ErrorNoReturn("Semigroups: IdentityBipartition: usage,\n",
192                  "the argument <n> must not exceed 2 ^ 29 - 1,");
193  fi;
194  blocks := EmptyPlist(2 * n);
195
196  for i in [1 .. n] do
197    blocks[i] := i;
198    blocks[i + n] := i;
199  od;
200
201  return BIPART_NC(blocks);
202end);
203
204InstallMethod(RandomBipartition, "for a random source and pos int",
205[IsRandomSource, IsPosInt],
206function(rs, n)
207  local out, nrblocks, vals, j, i;
208
209  if n >= 2 ^ 29 then
210    ErrorNoReturn("Semigroups: RandomBipartition: usage,\n",
211                  "the argument <n> must not exceed 2 ^ 29 - 1,");
212  fi;
213  out := EmptyPlist(2 * n);
214  nrblocks := 0;
215  vals := [1];
216
217  for i in [1 .. 2 * n] do
218    j := Random(rs, vals);
219    if j = nrblocks + 1 then
220      nrblocks := nrblocks + 1;
221      Add(vals, nrblocks + 1);
222    fi;
223    out[i] := j;
224  od;
225
226  return BIPART_NC(out);
227end);
228
229InstallMethod(RandomBipartition, "for a pos int", [IsPosInt],
230function(n)
231  return RandomBipartition(GlobalMersenneTwister, n);
232end);
233
234InstallMethod(RandomBlockBijection, "for a random source and pos int",
235[IsRandomSource, IsPosInt],
236function(rs, n)
237  local out, nrblocks, j, free, i;
238
239  if n >= 2 ^ 29 then
240    ErrorNoReturn("Semigroups: RandomBlockBipartition: usage,\n",
241                  "the argument <n> must not exceed 2 ^ 29 - 1,");
242  fi;
243
244  out := EmptyPlist(2 * n);
245  out[1] := 1;
246  nrblocks := 1;
247
248  for i in [2 .. n] do
249    j := Random(rs, [1 .. nrblocks + 1]);
250    if j = nrblocks + 1 then
251      nrblocks := nrblocks + 1;
252    fi;
253    out[i] := j;
254  od;
255
256  free := [n + 1 .. 2 * n];
257  for i in [1 .. nrblocks] do
258    j := Random(rs, free);
259    out[j] := i;
260    RemoveSet(free, j);
261  od;
262
263  for i in free do
264    out[i] := Random(rs, [1 .. nrblocks]);
265  od;
266
267  return BIPART_NC(out);
268end);
269
270InstallMethod(RandomBlockBijection, "for a pos int", [IsPosInt],
271function(n)
272  return RandomBlockBijection(GlobalMersenneTwister, n);
273end);
274
275# Operators
276
277InstallMethod(PermLeftQuoBipartition, "for a bipartition and bipartition",
278IsIdenticalObj, [IsBipartition, IsBipartition],
279function(x, y)
280
281  if LeftBlocks(x) <> LeftBlocks(y) or RightBlocks(x) <> RightBlocks(y) then
282    ErrorNoReturn("Semigroups: PermLeftQuoBipartition: usage,\n",
283                  "the arguments must have equal left and right blocks,");
284  fi;
285  return BIPART_PERM_LEFT_QUO(x, y);
286end);
287
288# Attributes
289
290InstallMethod(DomainOfBipartition, "for a bipartition", [IsBipartition],
291function(x)
292  local out;
293  out := [];
294  for x in ExtRepOfObj(LeftBlocks(x)) do
295    if IsPosInt(x[1]) then
296      Append(out, x);
297    fi;
298  od;
299  return out;
300end);
301
302InstallMethod(CodomainOfBipartition, "for a bipartition", [IsBipartition],
303function(x)
304  local out;
305  out := [];
306  for x in ExtRepOfObj(RightBlocks(x)) do
307    if IsPosInt(x[1]) then
308      Append(out, -x);
309    fi;
310  od;
311  return out;
312end);
313
314InstallMethod(ExtRepOfObj, "for a bipartition", [IsBipartition],
315BIPART_EXT_REP);
316
317InstallMethod(IntRepOfBipartition, "for a bipartition", [IsBipartition],
318BIPART_INT_REP);
319
320# xx ^ * - linear - 2 * degree
321
322InstallMethod(LeftProjection, "for a bipartition", [IsBipartition],
323BIPART_LEFT_PROJ);
324
325InstallMethod(RightProjection, "for a bipartition", [IsBipartition],
326BIPART_RIGHT_PROJ);
327
328# linear - 2 * degree
329
330InstallMethod(StarOp, "for a bipartition", [IsBipartition], BIPART_STAR);
331
332InstallMethod(ChooseHashFunction, "for a bipartition",
333[IsBipartition, IsInt],
334  function(x, hashlen)
335  return rec(func := BIPART_HASH,
336             data := hashlen);
337end);
338
339#############################################################################
340# GAP level
341#############################################################################
342
343# Attributes
344
345# not a synonym since NrTransverseBlocks also applies to blocks
346
347InstallMethod(NrTransverseBlocks, "for a bipartition", [IsBipartition],
348RankOfBipartition);
349
350InstallMethod(NrRightBlocks, "for a bipartition", [IsBipartition],
351x -> NrBlocks(x) - NrLeftBlocks(x) + NrTransverseBlocks(x));
352
353InstallMethod(OneMutable, "for a bipartition",
354[IsBipartition], x -> IdentityBipartition(DegreeOfBipartition(x)));
355
356InstallMethod(OneMutable, "for a bipartition collection",
357[IsBipartitionCollection], x ->
358IdentityBipartition(DegreeOfBipartitionCollection(x)));
359
360# the Other is to avoid warning on opening GAP
361
362InstallOtherMethod(InverseMutable, "for a bipartition", [IsBipartition],
363function(x)
364  if IsBlockBijection(x) or IsPartialPermBipartition(x) then
365    return Star(x);
366  fi;
367  return fail;
368end);
369
370# Properties
371
372InstallMethod(IsBlockBijection, "for a bipartition",
373[IsBipartition],
374function(x)
375  return NrBlocks(x) = NrLeftBlocks(x) and NrRightBlocks(x) = NrLeftBlocks(x);
376end);
377
378InstallMethod(IsPartialPermBipartition, "for a bipartition",
379[IsBipartition],
380function(x)
381  return NrLeftBlocks(x) = DegreeOfBipartition(x)
382    and NrRightBlocks(x) = DegreeOfBipartition(x);
383end);
384
385# a bipartition is a transformation if and only if the second row is a
386# permutation of [1 .. n], where n is the degree.
387
388InstallMethod(IsTransBipartition, "for a bipartition",
389[IsBipartition],
390function(x)
391  return NrLeftBlocks(x) = NrTransverseBlocks(x)
392   and NrRightBlocks(x) = DegreeOfBipartition(x);
393end);
394
395InstallMethod(IsDualTransBipartition, "for a bipartition", [IsBipartition],
396function(x)
397  return NrRightBlocks(x) = NrTransverseBlocks(x)
398   and NrLeftBlocks(x) = DegreeOfBipartition(x);
399end);
400
401InstallMethod(IsPermBipartition, "for a bipartition",
402[IsBipartition],
403function(x)
404  return IsPartialPermBipartition(x)
405    and NrTransverseBlocks(x) = DegreeOfBipartition(x);
406end);
407
408# Fundamental operators
409
410InstallMethod(\*, "for a bipartition and a perm",
411[IsBipartition, IsPerm],
412function(x, p)
413  if LargestMovedPoint(p) <= DegreeOfBipartition(x) then
414    return x * AsBipartition(p, DegreeOfBipartition(x));
415  fi;
416  ErrorNoReturn("Semigroups: \\* (for a bipartition and perm): usage,\n",
417                "the largest moved point of the perm must not be greater\n",
418                "than the degree of the bipartition,");
419end);
420
421InstallMethod(\*, "for a perm and a bipartition",
422[IsPerm, IsBipartition],
423function(p, x)
424  if LargestMovedPoint(p) <= DegreeOfBipartition(x) then
425    return AsBipartition(p, DegreeOfBipartition(x)) * x;
426  fi;
427  ErrorNoReturn("Semigroups: \\* (for a perm and bipartition): usage,\n",
428                "the largest moved point of the perm must not be greater\n",
429                "than the degree of the bipartition,");
430end);
431
432InstallMethod(\*, "for a bipartition and a transformation",
433[IsBipartition, IsTransformation],
434function(x, f)
435  if DegreeOfTransformation(f) <= DegreeOfBipartition(x) then
436    return x * AsBipartition(f, DegreeOfBipartition(x));
437  fi;
438  ErrorNoReturn("Semigroups: \\* (for a bipartition and transformation): ",
439                "usage,\n",
440                "the degree of the transformation must not be greater\n",
441                "than the degree of the bipartition,");
442end);
443
444InstallMethod(\*, "for a transformation and a bipartition",
445[IsTransformation, IsBipartition],
446function(f, g)
447  if DegreeOfTransformation(f) <= DegreeOfBipartition(g) then
448    return AsBipartition(f, DegreeOfBipartition(g)) * g;
449  fi;
450  ErrorNoReturn("Semigroups: \\* (for a transformation and bipartition): ",
451                "usage,\n",
452                "the degree of the transformation must not be greater\n",
453                "than the degree of the bipartition,");
454end);
455
456InstallMethod(\*, "for a bipartition and a partial perm",
457[IsBipartition, IsPartialPerm],
458function(f, g)
459  local n;
460  n := DegreeOfBipartition(f);
461  if ForAll([1 .. n], i -> i ^ g <= n) then
462    return f * AsBipartition(g, DegreeOfBipartition(f));
463  fi;
464  ErrorNoReturn("Semigroups: \\* (for a bipartition and partial perm): usage,",
465                "\nthe partial perm must map [1 .. ", String(n), "] into\n",
466                "[1 .. ", String(n), "],");
467end);
468
469InstallMethod(\*, "for a partial perm and a bipartition",
470[IsPartialPerm, IsBipartition],
471function(f, g)
472  local n;
473  n := DegreeOfBipartition(g);
474  if ForAll([1 .. n], i -> i ^ f <= n) then
475    return AsBipartition(f, DegreeOfBipartition(g)) * g;
476  fi;
477  ErrorNoReturn("Semigroups: \\* (for a partial perm and a bipartition): ",
478                "usage,\n",
479                "the partial perm must map [1 .. ", String(n), "] into\n",
480                "[1 .. ", String(n), "],");
481end);
482
483InstallMethod(\^, "for a bipartition and permutation",
484[IsBipartition, IsPerm],
485function(f, p)
486  return p ^ -1 * f * p;
487end);
488
489# Other operators
490
491InstallMethod(PartialPermLeqBipartition, "for a bipartition and a bipartition",
492IsIdenticalObj, [IsBipartition, IsBipartition],
493function(x, y)
494
495  if not (IsPartialPermBipartition(x) and IsPartialPermBipartition(y)) then
496    ErrorNoReturn("Semigroups: PartialPermLeqBipartition: usage,\n",
497                  "the arguments must be partial perm bipartitions,");
498  fi;
499
500  return AsPartialPerm(x) < AsPartialPerm(y);
501end);
502
503# Changing representations
504
505InstallMethod(AsBipartition, "for a permutation and zero",
506[IsPerm, IsZeroCyc],
507function(f, n)
508  return Bipartition([]);
509end);
510
511InstallMethod(AsBipartition, "for a permutation",
512[IsPerm], x -> AsBipartition(x, LargestMovedPoint(x)));
513
514InstallMethod(AsBipartition, "for a partial perm",
515[IsPartialPerm],
516function(x)
517  return AsBipartition(x, Maximum(DegreeOfPartialPerm(x),
518                                  CodegreeOfPartialPerm(x)));
519end);
520
521InstallMethod(AsBipartition, "for a partial perm and zero",
522[IsPartialPerm, IsZeroCyc],
523function(f, n)
524  return Bipartition([]);
525end);
526
527InstallMethod(AsBipartition, "for a transformation",
528[IsTransformation], x -> AsBipartition(x, DegreeOfTransformation(x)));
529
530InstallMethod(AsBipartition, "for a transformation and zero",
531[IsTransformation, IsZeroCyc],
532function(f, n)
533  return Bipartition([]);
534end);
535
536InstallMethod(AsBipartition, "for a bipartition", [IsBipartition], IdFunc);
537
538InstallMethod(AsBipartition, "for a bipartition", [IsBipartition, IsZeroCyc],
539function(f, n)
540  return Bipartition([]);
541end);
542
543InstallMethod(AsBipartition, "for a pbr and pos int",
544[IsPBR, IsZeroCyc],
545function(x, deg)
546  return Bipartition([]);
547end);
548
549InstallMethod(AsBipartition, "for a pbr and pos int",
550[IsPBR, IsPosInt],
551function(x, deg)
552  if not IsBipartitionPBR(x) then
553    ErrorNoReturn("Semigroups: AsBipartition (for a pbr): usage,\n",
554                  "the argument does not satisfy 'IsBipartitionPBR',");
555  fi;
556
557  return AsBipartition(AsBipartition(x), deg);
558end);
559
560InstallMethod(AsBipartition, "for a pbr",
561[IsPBR],
562function(x)
563  if not IsBipartitionPBR(x) then
564    ErrorNoReturn("Semigroups: AsBipartition (for a pbr): usage,\n",
565                  "the argument does not satisfy 'IsBipartitionPBR',");
566  fi;
567  return Bipartition(Union(ExtRepOfObj(x)));
568end);
569
570InstallMethod(AsBlockBijection, "for a partial perm",
571[IsPartialPerm],
572function(x)
573  return AsBlockBijection(x, Maximum(DegreeOfPartialPerm(x),
574                                     CodegreeOfPartialPerm(x)) + 1);
575end);
576
577# Viewing, printing etc
578
579InstallMethod(ViewString, "for a bipartition",
580[IsBipartition],
581function(x)
582  local str, ext, i;
583
584  if DegreeOfBipartition(x) = 0 then
585    return "\><empty bipartition>\<";
586  fi;
587
588  if IsBlockBijection(x) then
589    str := "\>\><block bijection:\< ";
590  else
591    str := "\>\><bipartition:\< ";
592  fi;
593
594  ext := ExtRepOfObj(x);
595  Append(str, "\>");
596  Append(str, String(ext[1]));
597  Append(str, "\<");
598
599  for i in [2 .. Length(ext)] do
600    Append(str, ", \>");
601    Append(str, String(ext[i]));
602    Append(str, "\<");
603  od;
604  Append(str, ">\<");
605  return str;
606end);
607
608InstallMethod(String, "for a bipartition", [IsBipartition],
609function(x)
610  return Concatenation("Bipartition(", String(ExtRepOfObj(x)), ")");
611end);
612
613InstallMethod(PrintString, "for a bipartition",
614[IsBipartition],
615function(x)
616  local ext, str, i;
617  if DegreeOfBipartition(x) = 0 then
618    return "\>\>Bipartition(\< \>[]\<)\<";
619  fi;
620  ext := ExtRepOfObj(x);
621  str := Concatenation("\>\>Bipartition(\< \>[ ", PrintString(ext[1]));
622  for i in [2 .. Length(ext)] do
623    Append(str, ",\< \>");
624    Append(str, PrintString(ext[i]));
625  od;
626  Append(str, " \<]");
627  Append(str, " )\<");
628  return str;
629end);
630
631InstallMethod(PrintString, "for a bipartition collection",
632[IsBipartitionCollection],
633function(coll)
634  local str, i;
635
636  if IsGreensClass(coll) or IsSemigroup(coll) then
637    TryNextMethod();
638  fi;
639
640  str := "\>[ ";
641  for i in [1 .. Length(coll)] do
642    if not i = 1 then
643      Append(str, " ");
644    fi;
645    Append(str, "\>");
646    Append(str, PrintString(coll[i]));
647    if not i = Length(coll) then
648      Append(str, ",\<\n");
649    else
650      Append(str, " ]\<\n");
651    fi;
652  od;
653  return str;
654end);
655
656# Bipartition collections
657
658InstallMethod(DegreeOfBipartitionCollection, "for a bipartition semigroup",
659[IsBipartitionSemigroup],
660function(S)
661  return DegreeOfBipartitionSemigroup(S);
662end);
663
664InstallMethod(DegreeOfBipartitionCollection, "for a bipartition collection",
665[IsBipartitionCollection],
666function(coll)
667  return DegreeOfBipartition(coll[1]);
668end);
669
670#############################################################################
671# All of the methods in this section could be done in C/C++
672#############################################################################
673
674# Change representations . . .
675
676InstallMethod(AsBipartition, "for a permutation and pos int",
677[IsPerm, IsPosInt],
678function(x, n)
679  if n >= 2 ^ 29 then
680    ErrorNoReturn("Semigroups: AsBipartition: usage,\n",
681                  "the argument <n> must not exceed 2 ^ 29 - 1,");
682  elif OnSets([1 .. n], x) <> [1 .. n] then
683    ErrorNoReturn("Semigroups: AsBipartition (for a permutation and pos int):",
684                  "\nthe permutation <p> in the 1st argument must permute ",
685                  "[1 .. ", String(n), "],");
686  fi;
687  return BIPART_NC(Concatenation([1 .. n], ListPerm(x ^ -1, n)));
688end);
689
690InstallMethod(AsPartialPerm, "for a bipartition", [IsBipartition],
691function(x)
692  local n, blocks, nrleft, im, out, i;
693
694  if not IsPartialPermBipartition(x) then
695    ErrorNoReturn("Semigroups: AsPartialPerm (for a bipartition):\n",
696                  "the argument does not define a partial perm,");
697  fi;
698
699  n      := DegreeOfBipartition(x);
700  blocks := IntRepOfBipartition(x);
701  nrleft := NrLeftBlocks(x);
702  im     := [1 .. n] * 0;
703
704  for i in [n + 1 .. 2 * n] do
705    if blocks[i] <= nrleft then
706      im[blocks[i]] := i - n;
707    fi;
708  od;
709
710  out := EmptyPlist(n);
711  for i in [1 .. n] do
712    out[i] := im[blocks[i]];
713  od;
714  return PartialPermNC(out);
715end);
716
717InstallMethod(AsPermutation, "for a bipartition", [IsBipartition],
718function(x)
719  local n, blocks, im, out, i;
720
721  if not IsPermBipartition(x) then
722    ErrorNoReturn("Semigroups: AsPermutation (for a bipartition):\n",
723                  "the argument does not define a permutation,");
724  fi;
725
726  n      := DegreeOfBipartition(x);
727  blocks := IntRepOfBipartition(x);
728  im     := EmptyPlist(n);
729
730  for i in [n + 1 .. 2 * n] do
731    im[blocks[i]] := i - n;
732  od;
733
734  out := EmptyPlist(n);
735  for i in [1 .. n] do
736    out[i] := im[blocks[i]];
737  od;
738  return PermList(out);
739end);
740
741InstallMethod(AsTransformation, "for a bipartition", [IsBipartition],
742function(x)
743  local n, blocks, nr, im, out, i;
744
745  if not IsTransBipartition(x) then
746    ErrorNoReturn("Semigroups: AsTransformation (for a bipartition):\n",
747                  "the argument does not define a transformation,");
748  fi;
749
750  n      := DegreeOfBipartition(x);
751  blocks := IntRepOfBipartition(x);
752  nr     := NrLeftBlocks(x);
753  im     := EmptyPlist(n);
754
755  for i in [n + 1 .. 2 * n] do
756    if blocks[i] <= nr then
757      im[blocks[i]] := i - n;
758    fi;
759  od;
760
761  out := EmptyPlist(n);
762  for i in [1 .. n] do
763    out[i] := im[blocks[i]];
764  od;
765  return TransformationNC(out);
766end);
767
768InstallMethod(AsBipartition, "for a partial perm and pos int",
769[IsPartialPerm, IsPosInt],
770function(x, n)
771  local r, out, y, j, i;
772
773  if n >= 2 ^ 29 then
774    ErrorNoReturn("Semigroups: AsBipartition: usage,\n",
775                  "the argument <n> must not exceed 2 ^ 29 - 1,");
776  fi;
777
778  r   := n;
779  out := EmptyPlist(2 * n);
780  y   := x ^ -1;
781
782  for i in [1 .. n] do
783    out[i] := i;
784    j      := i ^ y;
785    if j <> 0 then
786      out[n + i] := j;
787    else
788      r          := r + 1;
789      out[n + i] := r;
790    fi;
791  od;
792  return BIPART_NC(out);
793end);
794
795InstallMethod(AsBipartition, "for a transformation and a positive integer",
796[IsTransformation, IsPosInt],
797function(f, n)
798  local r, ker, out, g, i;
799
800  if n >= 2 ^ 29 then
801    ErrorNoReturn("Semigroups: AsBipartition: usage,\n",
802                  "the argument <n> must not exceed 2 ^ 29 - 1,");
803  fi;
804  if n < DegreeOfTransformation(f) then
805    # Verify f is a transformation on [1 .. n].
806    for i in [1 .. n] do
807      if i ^ f > n then
808        ErrorNoReturn("Semigroups: AsBipartition (for a transformation and ",
809                      "pos int):\n",
810                      "the argument must map [1 .. ", String(n), "] to ",
811                      "itself,");
812      fi;
813    od;
814  fi;
815
816  r := RankOfTransformation(f, n);
817  ker := FlatKernelOfTransformation(f, n);
818
819  out := EmptyPlist(2 * n);
820  g := List([1 .. n], x -> 0);
821
822  # The inverse of f.
823  for i in [1 .. n] do
824    g[i ^ f] := i;
825  od;
826
827  for i in [1 .. n] do
828    out[i] := ker[i];
829    if g[i] <> 0 then
830      out[n + i] := ker[g[i]];
831    else
832      r := r + 1;
833      out[n + i] := r;
834    fi;
835  od;
836  return BIPART_NC(out);
837end);
838
839InstallMethod(AsBipartition, "for a bipartition and pos int",
840[IsBipartition, IsPosInt],
841function(f, n)
842  local deg, blocks, out, nrblocks, nrleft, lookup, j, i;
843
844  if n >= 2 ^ 29 then
845    ErrorNoReturn("Semigroups: AsBipartition: usage,\n",
846                  "the argument <n> must not exceed 2 ^ 29 - 1,");
847  fi;
848  deg := DegreeOfBipartition(f);
849  if n = deg then
850    return f;
851  fi;
852  blocks := IntRepOfBipartition(f);
853  out := [];
854  nrblocks := 0;
855
856  if n < deg then
857    for i in [1 .. n] do
858      out[i] := blocks[i];
859      if out[i] > nrblocks then
860        nrblocks := nrblocks + 1;
861      fi;
862    od;
863    nrleft := nrblocks;
864    lookup := EmptyPlist(NrBlocks(f));
865    for i in [n + 1 .. 2 * n] do
866      j := blocks[i + deg - n];
867      if j > nrleft then
868        if not IsBound(lookup[j]) then
869          nrblocks := nrblocks + 1;
870          lookup[j] := nrblocks;
871        fi;
872        j := lookup[j];
873      fi;
874      out[i] := j;
875    od;
876  else  # n > deg
877    for i in [1 .. deg] do
878      out[i] := blocks[i];
879    od;
880    nrblocks := NrLeftBlocks(f);
881    for i in [deg + 1 .. n] do
882      nrblocks := nrblocks + 1;
883      out[i] := nrblocks;
884    od;
885    nrleft := nrblocks;  # = n - deg + NrLeftBlocks(f)
886    for i in [n + 1 .. n + deg] do
887      if blocks[i - n + deg] <= nrleft - n + deg then  # it's a left block
888        out[i] := blocks[i - n + deg];
889      else
890        out[i] := blocks[i - n + deg] + n - deg;
891      fi;
892    od;
893    nrblocks := NrBlocks(f) + n - deg;
894    for i in [n + deg + 1 .. 2 * n] do
895      nrblocks := nrblocks + 1;
896      out[i] := nrblocks;
897    od;
898  fi;
899  return BIPART_NC(out);
900end);
901
902# same as AsBipartition except that all undefined points are in a single block
903# together with an extra (pair of) points.
904
905InstallMethod(AsBlockBijection, "for a partial perm and pos int",
906[IsPartialPerm, IsPosInt],
907function(f, n)
908  local bigblock, nr, out, i;
909
910  if n >= 2 ^ 29 then
911    ErrorNoReturn("Semigroups: AsBlockBijection: usage,\n",
912                  "the argument <n> must not exceed 2 ^ 29 - 1,");
913  fi;
914
915  if n <= Maximum(DegreeOfPartialPerm(f), CodegreeOfPartialPerm(f)) then
916    ErrorNoReturn("Semigroups: AsBlockBijection (for a partial perm and pos ",
917                  "int):\n",
918                  "the 2nd argument must be strictly greater than the maximum ",
919                  "of the\ndegree and codegree of the 1st argument,");
920  fi;
921
922  nr := 0;
923  out := [1 .. 2 * n] * 0;
924  bigblock := n;
925
926  for i in [1 .. n - 1] do
927    if i ^ f = 0 then
928      if bigblock = n then
929        nr := nr + 1;
930        bigblock := nr;
931      fi;
932      out[i] := bigblock;
933    else
934      nr := nr + 1;
935      out[i] := nr;
936      out[n + i ^ f] := nr;
937    fi;
938  od;
939
940  out[n] := bigblock;
941  out[2 * n] := bigblock;
942
943  for i in [n + 1 .. 2 * n - 1] do
944    if out[i] = 0 then
945      out[i] := bigblock;
946    fi;
947  od;
948
949  return BIPART_NC(out);
950end);
951
952InstallMethod(AsBlockBijection, "for a bipartition and pos int",
953[IsBipartition, IsPosInt],
954function(x, n)
955  if not IsPartialPermBipartition(x) then
956    ErrorNoReturn("Semigroups: AsBlockBijection (for a bipartition and pos ",
957                  "int):\n",
958                  "the argument <x> must be a partial perm bipartition,");
959  fi;
960  return AsBlockBijection(AsPartialPerm(x), n);
961end);
962
963InstallMethod(AsBlockBijection, "for a bipartition",
964[IsBipartition],
965function(x)
966  if not IsPartialPermBipartition(x) then
967    ErrorNoReturn("Semigroups: AsBlockBijection (for a bipartition):\n",
968                  "the argument <x> must be a partial perm bipartition,");
969  fi;
970  return AsBlockBijection(AsPartialPerm(x));
971end);
972
973InstallMethod(NaturalLeqBlockBijection, "for a bipartition and bipartition",
974IsIdenticalObj, [IsBipartition, IsBipartition],
975function(x, y)
976  local xblocks, yblocks, n, lookup, i;
977
978  if not IsBlockBijection(x) or not IsBlockBijection(y) then
979    ErrorNoReturn("Semigroups: NaturalLeqBlockBijection: usage,\n",
980                  "the arguments must be block bijections,");
981  elif NrBlocks(x) > NrBlocks(y) then
982    return false;
983  fi;
984
985  xblocks := IntRepOfBipartition(x);
986  yblocks := IntRepOfBipartition(y);
987  n       := DegreeOfBipartition(x);
988
989  lookup := [];
990  for i in [1 .. n] do
991    if IsBound(lookup[yblocks[i]]) and lookup[yblocks[i]] <> xblocks[i] then
992      return false;
993    else
994      lookup[yblocks[i]] := xblocks[i];
995    fi;
996  od;
997  for i in [n + 1 .. 2 * n] do
998    if lookup[yblocks[i]] <> xblocks[i] then
999      return false;
1000    fi;
1001  od;
1002  return true;
1003end);
1004
1005InstallMethod(NaturalLeqPartialPermBipartition,
1006"for a bipartition and bipartition",
1007IsIdenticalObj, [IsBipartition, IsBipartition],
1008function(x, y)
1009  local n, xblocks, yblocks, val, i;
1010
1011  if not IsPartialPermBipartition(x) or not IsPartialPermBipartition(y) then
1012    ErrorNoReturn("Semigroups: NaturalLeqPartialPermBipartition: usage,\n",
1013                  "the arguments must be partial perm bipartitions,");
1014  fi;
1015
1016  n := DegreeOfBipartition(x);
1017
1018  xblocks := IntRepOfBipartition(x);
1019  yblocks := IntRepOfBipartition(y);
1020
1021  for i in [n + 1 .. 2 * n] do
1022    val := xblocks[i];
1023    if val <= n and val <> yblocks[i] then
1024      return false;
1025    fi;
1026  od;
1027  return true;
1028end);
1029
1030InstallMethod(IsUniformBlockBijection, "for a bipartition",
1031[IsBipartition],
1032function(x)
1033  local blocks, n, sizesleft, sizesright, i;
1034
1035  if not IsBlockBijection(x) then
1036    return false;
1037  fi;
1038
1039  blocks := IntRepOfBipartition(x);
1040  n := DegreeOfBipartition(x);
1041  sizesleft := [1 .. NrBlocks(x)] * 0;
1042  sizesright := [1 .. NrBlocks(x)] * 0;
1043
1044  for i in [1 .. n] do
1045    sizesleft[blocks[i]] := sizesleft[blocks[i]] + 1;
1046  od;
1047  for i in [n + 1 .. 2 * n] do
1048    sizesright[blocks[i]] := sizesright[blocks[i]] + 1;
1049  od;
1050  for i in [1 .. NrBlocks(x)] do
1051    if sizesright[i] <> sizesleft[i] then
1052      return false;
1053    fi;
1054  od;
1055
1056  return true;
1057end);
1058
1059InstallMethod(IndexPeriodOfSemigroupElement, "for a bipartition",
1060[IsBipartition],
1061function(x)
1062  return SEMIGROUPS.IndexPeriodByRank(x, RankOfBipartition);
1063end);
1064