File: stbchain.xml

package info (click to toggle)
gap 4.15.1-1
  • links: PTS
  • area: main
  • in suites: forky, sid
  • size: 110,212 kB
  • sloc: ansic: 97,261; xml: 48,343; cpp: 13,946; sh: 4,900; perl: 1,650; javascript: 255; makefile: 252; ruby: 9
file content (992 lines) | stat: -rw-r--r-- 43,100 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
<!-- %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -->
<!-- %% -->
<!-- %W  stbchain.tex              GAP documentation            Heiko Theißen -->
<!-- %% -->
<!-- %% -->
<!-- %Y  Copyright 1997,  Lehrstuhl D für Mathematik,  RWTH Aachen,   Germany -->
<!-- %% -->
<Chapter Label="More about Stabilizer Chains">
<Heading>More about Stabilizer Chains</Heading>

This  chapter contains some rather  technical complements to the material
handled in the chapters&nbsp;<Ref Chap="Permutations"/>
and <Ref Chap="Permutation Groups"/>.


<!-- %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -->
<Section Label="Generalized Conjugation Technique">
<Heading>Generalized Conjugation Technique</Heading>

The command <C>ConjugateGroup( <A>G</A>, <A>p</A> )</C>
(see&nbsp;<Ref Oper="ConjugateGroup"/>)
for a  permutation  group  <A>G</A>  with  stabilizer  chain
equips its result also with a stabilizer chain, namely with the chain  of
<A>G</A> conjugate by <A>p</A>. Conjugating a stabilizer chain by a permutation <A>p</A>
means replacing all the points which appear in the <C>orbit</C> components  by
their images under <A>p</A> and replacing every permutation <A>g</A> which  appears
in a <C>labels</C> or <C>transversal</C> component  by  its  conjugate  <M>g^p</M>.  The
conjugate <M>g^p</M> acts on the mapped points  exactly  as  <A>g</A>  did  on  the
original points, i.e., <M>(pnt.p). g^p = (pnt.g).p</M>. Since the  entries  in
the <C>translabels</C> components are integers pointing to  positions  of  the
<C>labels</C> list, the <C>translabels</C> lists just have to be  permuted  by  <A>p</A>
for the conjugated stabilizer.
Then <C>generators</C> is reconstructed as
<C>labels{ genlabels }</C> and
<C>transversal{ orbit }</C> as
<C>labels{ translabels{ orbit } }</C>.
<P/>
<Index>generalized conjugation technique</Index>
This conjugation technique can be generalized. Instead of mapping  points
and  permutations  under  the  same  permutation  <A>p</A>,  it  is  sometimes
desirable (e.g., in the context of permutation  group  homomorphisms)  to
map the points with an arbitrary mapping <M>map</M> and the permutations  with
a homomorphism <M>hom</M> such that the compatibility of the actions is  still
valid:   <M>map(pnt).hom(g) = map(pnt.g)</M>.   (Of   course    the   ordinary
conjugation is a special case  of  this,  with   <M>map(pnt) = pnt.p</M>   and
<M>hom(g) = g^p</M>.)
<P/>
In  the  generalized  case,  the  <Q>conjugated</Q>  chain  need  not  be  a
stabilizer chain for the image of <M>hom</M>, since the  <Q>preimage</Q>  of  the
stabilizer of <M>map(b)</M> (where <M>b</M> is a base point) need not fix <M>b</M>,  but
only fixes the preimage <M>map^{{-1}}( map(b) )</M> setwise. Therefore the  method
can be applied only to one level and the next stabilizer must be computed
explicitly.
But if <M>map</M> is injective, we have <M>map(b).hom(g) = map(b)</M> if and
only if <M>b.g = b</M>, and if this holds, then <M>g = w(g_1, \ldots, g_n)</M>
is a  word  in  the
generators <M>g_1, \ldots, g_n</M> of the stabilizer of&nbsp;<M>b</M> and
<!-- % replaced \buildrel *\over= by {$*$}{$=$} ... easiest compromise for HTML -->
<M>hom(g) =^* w( hom(g_1), \ldots, hom(g_n) )</M> is in the
<Q>conjugated</Q> stabilizer.
If, more generally, <M>hom</M> is a right inverse to a homomorphism
<M>\varphi</M> (i.e., <M>\varphi(hom(g)) = g</M> for all <M>g</M>),
equality <M>*</M> holds modulo the kernel of <M>\varphi</M>;
in this case the <Q>conjugated</Q> chain can be made into a real stabilizer
chain by extending each level with the generators of the kernel and
appending a proper stabilizer chain of the kernel at the end.
These special cases will occur in the algorithms for permutation group
homomorphisms (see&nbsp;<Ref Chap="Group Homomorphisms"/>).
<P/>
To <Q>conjugate</Q> the points (i.e., <C>orbit</C>) and permutations (i.e.,
<C>labels</C>) of the Schreier tree, a loop is set up over the <C>orbit</C> list
constructed during the orbit algorithm, and  for  each  vertex  <M>b</M>  with
unique edge <M>a(l)b</M> ending at <M>b</M>, the label <M>l</M> is mapped with <M>hom</M> and
<M>b</M> with <M>map</M>. We assume  that  the  <C>orbit</C>  list  was  built  w.r.t.&nbsp;a
certain ordering <M>&lt;</M> of the labels,
where <M>l' &lt; l</M> means that every  point  in
the orbit was mapped with <M>l'</M> before it was mapped with <M>l</M>. This  shape
of the <C>orbit</C> list is guaranteed if the Schreier tree is  extended  only
by <Ref Func="AddGeneratorsExtendSchreierTree"/>,
and it is then also guaranteed  for
the <Q>conjugated</Q> Schreier tree. (The ordering of the labels  cannot  be
read from the Schreier tree, however.)
<P/>
In the generalized case, it can happen that  the  edge  <M>a(l)b</M>  bears  a
label <M>l</M> whose image is <Q>old</Q>, i.e., equal to the image of an  earlier
label <M>l' &lt; l</M>. Because of the compatibility of the actions we  then  have
<M>map(b) = map(a).hom(l)^{{-1}} = map(a).hom(l')^{{-1}} = map(a{{l'}}^{{-1}})</M>,
so <M>map(b)</M> is already equal to the image  of  the  vertex  <M>a{{l'}}^{{-1}}</M>.
This vertex must have been  encountered  before  <M>b = al^{{-1}}</M>  because
<M>l' &lt; l</M>. We conclude that the image of a label can be <Q>old</Q> only if the
vertex at the end of the corresponding edge has an  <Q>old</Q>  image,  too,
but then it need not be <Q>conjugated</Q> at all. A similar  remark  applies
to labels which map under <M>hom</M> to the identity.

</Section>


<!-- %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -->
<Section Label="The General Backtrack Algorithm with Ordered Partitions">
<Heading>The General Backtrack Algorithm with Ordered Partitions</Heading>

Section <Ref Sect="Backtrack"/> describes the basic
functions for a backtrack search. The purpose of
this section  is  to document  how  the general   backtrack algorithm  is
implemented in &GAP; and which  parts you have to modify  if you want to
write your own backtrack routines.

<Subsection Label="Internal representation of ordered partitions">
<Heading>Internal representation of ordered partitions</Heading>

<Index Subkey="internal representation">ordered partitions</Index>
&GAP; represents  an  ordered  partition  as  a  record  with   the   following
components.
<List>
<Mark><C>points</C></Mark>
<Item>
        a list  of all points contained in  the  partition, such that the
        points of each cell from lie consecutively,
</Item>
<Mark><C>cellno</C></Mark>
<Item>
        a list whose <A>i</A>th entry is the number of the cell which contains
        the point <A>i</A>,
</Item>
<Mark><C>firsts</C></Mark>
<Item>
        a list  such that  <C>points[firsts[<A>j</A>]]</C>  is  the first point  in
        <C>points</C> which is in cell <A>j</A>,
</Item>
<Mark><C>lengths</C></Mark>
<Item>
        a list of the  cell lengths.
</Item>
</List>
Some of the information is  redundant, e.g., the  <C>lengths</C> could also be
read off the <C>firsts</C> list,  but since this   need not be increasing,  it
would    require some searching. Similar  for    <C>cellno</C>, which could be
replaced by a systematic search  of <C>points</C>, keeping  track of what cell
is currently being  traversed.
With the above components, the <A>m</A>th cell of a partition <A>P</A>
is expressed as <C><A>P</A>.points{ [ <A>P</A>.firsts[<A>m</A>] ..
<A>P</A>.firsts[<A>m</A>] + <A>P</A>.lengths[<A>m</A>] - 1 ] }</C>.
The   most   important
operations, however, to be performed upon <A>P</A> are the splitting of a cell
and the reuniting  of the two parts. Following  the strategy  of J.&nbsp;Leon,
<!-- explicit citation, please -->
this is done as follows:
<P/>
<List>
<Mark>(1)</Mark>
<Item>
The points which make up the cell that is to be split are sorted so  that
the ones that remain inside occupy positions <C>[ <A>P</A>.firsts[<A>m</A>] .. <A>last</A>
]</C> in the list <C><A>P</A>.points</C> (for a suitable value of <A>last</A>).
</Item>
<Mark>(2)</Mark>
<Item>
The  points  at  positions  <C>[ <A>last</A> + 1 .. <A>P</A>.firsts[<A>m</A>] +
<A>P</A>.lengths[<A>m</A>] - 1 ]</C> will form the additional cell.
For this new  cell
requires additional entries are added to the lists <C><A>P</A>.firsts</C>  (namely,
<C><A>last</A>+1</C>) and <C><A>P</A>.lengths</C> (namely,
<C><A>P</A>.firsts[<A>m</A>] + <A>P</A>.lengths[<A>m</A>] - <A>last</A> - 1</C>).
</Item>
<Mark>(3)</Mark>
<Item>
The entries of the sublist
<C><A>P</A>.cellno{ [ <A>last</A>+1 .. <A>P</A>.firsts[<A>m</A>] +
P.lengths[<A>m</A>]-1 ] }</C> must be set to the number of the new cell.
</Item>
<Mark>(4)</Mark>
<Item>
  The entry <C><A>P</A>.lengths[<A>m</A>]</C> must be reduced to
  <C><A>last</A> - <A>P</A>.firsts[<A>m</A>] + 1</C>.
</Item>
</List>
<P/>
Then reuniting the  two cells requires  only the reversal of steps&nbsp;2 to&nbsp;4
above. The list <C><A>P</A>.points</C> need not be rearranged.

</Subsection>

<Subsection Label="Functions for setting up an R-base">
<Heading>Functions for setting up an R-base</Heading>

This subsection explains
some  &GAP;  functions   which   are   local   to   the   library   file
<F>lib/stbcbckt.gi</F> which contains the code for backtracking in permutation
groups. They are mentioned here because you might find them helpful  when
you want  to  implement  your own  backtracking  function  based  on  the
partition concept. An important argument to most of the functions is  the
R-base <M>R</M>, which you should regard as a black box. We will tell you how
to set it up, how to maintain it and where to pass it as argument, but it
is not necessary for you to know its internal representation. However, if
you insist to learn the whole story: Here are the record components  from
which an R-base is made up:
<P/>
<List>
<Mark><C>domain</C></Mark>
<Item>
    the set <M>\Omega</M> on which the group <M>G</M> operates
</Item>
<Mark><C>base</C></Mark>
<Item>
    the sequence <M>(a_1, \ldots, a_r)</M> of base points
</Item>
<Mark><C>partition</C></Mark>
<Item>
    an  ordered  partition, initially  <M>\Pi_0</M>, this  will be  refined to
    <M>\Pi_1, \ldots, \Pi_r</M> during the backtrack algorithm
</Item>
<Mark><C>where</C></Mark>
<Item>
    a list such that <M>a_i</M> lies in cell number <C>where</C><M>[i]</M> of <M>\Pi_i</M>
</Item>
<Mark><C>rfm</C></Mark>
<Item>
    a    list whose <M>i</M>th entry  is   a  list of   refinements which take
    <M>\Sigma_i</M>  to <M>\Sigma_{{i+1}}</M>;  the    structure of a  refinement  is
    described below
</Item>
<Mark><C>chain</C></Mark>
<Item>
    a (copy of a) stabilizer  chain for <M>G</M> (not  if  <M>G</M> is a  symmetric
    group)
</Item>
<Mark><C>fix</C></Mark>
<Item>
    only if  <M>G</M> is a  symmetric group:  a list whose  <M>i</M> entry contains
    <C>Fixcells( </C><M>\Pi_i</M><C> )</C>
</Item>
<Mark><C>level</C></Mark>
<Item>
    initially equal to <C>chain</C>,  this will be changed  to chains  for the
    stabilizers <M>G_{{a_1 \ldots a_i}}</M> for <M>i = 1, \ldots, r</M>
    during the
    backtrack algorithm; if <M>G</M> is a  symmetric group, only the number of
    moved points is stored for each stabilizer
</Item>
<Mark><C>lev</C></Mark>
<Item>
    a  list   whose  <M>i</M>th  entry   remembers   the  <C>level</C> entry    for
    <M>G_{{a_1 \ldots a_{{i-1}}}}</M>
</Item>
<Mark><C>level2</C>, <C>lev2</C></Mark>
<Item>
    a similar  construction   for a second  group  (used  in intersection
    calculations), <K>false</K> otherwise.  This second group <M>H</M> activated if
    the R-base  is constructed as  <C>EmptyRBase( </C><M>[ G, H ], \Omega,
    \Pi_0</M><C> )</C> (if <M>G = H</M>, &GAP; sets <C>level2 = </C><K>true</K> instead).
</Item>
<Mark><C>nextLevel</C></Mark>
<Item>
    this is described below
</Item>
</List>
<P/>
As  our guiding example, we  present  code for the function
<Ref Oper="Centralizer" Label="for a magma and a submagma"/>
which calculates the centralizer of an element <M>g</M> in the group <M>G</M>.
(The real code is more general and has a few more subtleties.)
<P/>
<Listing><![CDATA[
Pi_0 := TrivialPartition( omega );
R := EmptyRBase( G, omega, Pi_0 );
R.nextLevel := function( Pi, rbase )
local  fix, p, q, where;
NextRBasePoint( Pi, rbase );
fix := Fixcells( Pi );
for p  in fix  do
  q := p ^ g;
  where := IsolatePoint( Pi, q );
  if where <> false  then
    Add( fix, q );
    ProcessFixpoint( R, q );
    AddRefinement( R, "Centralizer", [ Pi.cellno[ p ], q, where ] );
    if Pi.lengths[ where ] = 1  then
      p := FixpointCellNo( Pi, where );
      ProcessFixpoint( R, p );
      AddRefinement( R, "ProcessFixpoint", [ p, where ] );
    fi;
  fi;
od;
end;

return PartitionBacktrack(
  G,
  c -> g ^ c = g,
  false,
  R,
  [ Pi_0, g ],
  L, R );
]]></Listing>
<P/>
The list numbers below refer to the line numbers of the code above.
<P/>
<List>
  <Mark>1.</Mark>
  <Item>
    <C>omega</C> is the set on which <C>G</C> acts and <C>Pi_0</C>
    is the first member  of the decreasing sequence of partitions mentioned
    in <Ref Sect="Backtrack"/>.
    We  set  <C>Pi_0 = omega</C>,  which  is  constructed  as
    <C>TrivialPartition( omega )</C>, but we could have started with  a  finer
    partition, e.g., into unions of <C>g</C>-cycles of the same length.
  </Item>
  <Mark>2.</Mark>
  <Item>
    This statement sets up the R-base in the variable <C>R</C>.
  </Item>
  <Mark>3.-21.</Mark>
  <Item>
    These lines define a function <C>R.nextLevel</C> which  is  called
    whenever an additional member in the sequence
    <C>Pi_0 </C><M>\geq \Pi_1 \geq \ldots</M>
    of partitions is needed. If <M>\Pi_i</M> does  not  yet  contain  enough
    base points in one-point cells, &GAP;  will  call
    <C>R.nextLevel( </C><M>\Pi_i,</M><C> R )</C>,
    and this function will choose a new base point <M>a_{{i+1}}</M>, refine
    <M>\Pi_i</M> to <M>\Pi_{{i+1}}</M>
    (thereby <E>changing</E> the first argument) and  store
    all necessary information in&nbsp;<C>R</C>.
  </Item>
  <Mark>5.</Mark>
  <Item>
    This statement selects a new base point <M>a_{{i+1}}</M>,
    which is not yet in  a one-point cell of <M>\Pi</M> and still moved
    by the stabilizer <M>G_{{a_1 \ldots a_i}}</M> of the earlier base points.
    If certain points  of  <C>omega</C>  should be preferred as base point
    (e.g., because they belong to long cycles  of <C>g</C>),
    a list of points starting with the most wanted ones, can  be  given
    as an optional third argument to <C>NextRBasePoint</C>
    (actually, this is done in the real code for
    <Ref Oper="Centralizer" Label="for a magma and a submagma"/>).
  </Item>
  <Mark>6.</Mark>
  <Item>
    <C>Fixcells( </C><M>\Pi</M><C> )</C> returns the list of  points  in
    one-point  cells  of <M>\Pi</M>
    (ordered as the cells are ordered in <M>\Pi</M>).
  </Item>
  <Mark>7.</Mark>
  <Item>
    For every point <M>p \in fix</M>, if we know the image
    <M>p</M><C>^</C><M>g</M> under  <M>c \in C_G(e)</M>,
    we also know <M>( p</M><C>^</C><M>g )</M><C>^</C><M>c
    = ( p</M><C>^</C><M>c )</M><C>^</C><M>g</M>.
    We therefore want to isolate these extra points in <M>\Pi</M>.
  </Item>
  <Mark>9.</Mark>
  <Item>
    This statement puts point <M>q</M> in a cell of its own,
    returning in  <C>where</C> the number of the cell of <M>\Pi</M>
    from which <M>q</M>  was  taken.
    If  <M>q</M>  was already the only point in its cell,
    <C>where = </C><K>false</K> instead.
  </Item>
  <Mark>12.</Mark>
  <Item>
    This command does the necessary bookkeeping for the extra base point
    <M>q</M>:
    It prescribes <M>q</M> as next base in the stabilizer chain for  <M>G</M>
    (needed, e.g., in line&nbsp;5)
    and  returns  <K>false</K>  if  <M>q</M>  was  already  fixed  the
    stabilizer of the earlier base points
    (and <K>true</K> otherwise; this is  not used here).
    Another call to <C>ProcessFixpoint</C> like  this  was  implicitly
    made by the function <C>NextRBasePoint</C> to register the chosen
    base  point.
    By contrast, the point <M>q</M> was not chosen this way,
    so  <C>ProcessFixpoint</C> must be called explicitly for&nbsp;<M>q</M>.
  </Item>
  <Mark>13.</Mark>
  <Item>
    This statement registers the function  which  will  be  used  during  the
    backtrack search to perform the corresponding refinements on the  <Q>image
    partition</Q> <M>\Sigma_i</M>
    (to  yield  the  refined  <M>\Sigma_{{i+1}}</M>).
    After choosing an image <M>b_{{i+1}}</M> for the base point
    <M>a_{{i+1}}</M>, &GAP; will compute
    <M>\Sigma_i \wedge (\{ b_{{i+1}} \}, \Omega \setminus \{ b_{{i+1}} \})</M>
    and store this partition in <M>I</M><C>.partition</C>,
    where <M>I</M> is a black box similar to <M>R</M>,
    but corresponding to the current <Q>image  partition</Q>
    (hence  it  is  an <Q>R-image</Q> in analogy to the R-base).
    Then &GAP; will call the function
    <C>Refinements.Centralizer( R, I, Pi.cellno[ p ], p, where )</C>,
    with  the  then  current  values  of  <M>R</M>  and  <M>I</M>,
    but   where <M>\Pi</M><C>.cellno</C><M>[ p ]</M>, <M>p</M>,
    <C>where</C> still have the values  they  have  at the time of this
    <C>AddRefinement</C> command.
    This function call will further refine <M>I</M><C>.partition</C>
    to yield <M>\Sigma_{{i+1}}</M> as it  is  programmed  in the function
    <C>Refinements.Centralizer</C>, which is  described  below.
    (The global variable <C>Refinements</C> is a record which contains  all
    refinement functions for all backtracking procedures.)
  </Item>
  <Mark>14.-19.</Mark>
  <Item>
    If the cell from which <M>q</M> was taken out had only two points,
    we now have an additional one-point cell.
    This condition is checked in line&nbsp;13 and if it is true,
    this extra fixpoint <M>p</M> is taken  (line&nbsp;15),
    processed  like <M>q</M> before (line&nbsp;16) and is then (line&nbsp;17)
    passed to  another  refinement function
    <C>Refinements.ProcessFixpoint( R, I, p, where )</C>,
    which is also described below.
  </Item>
  <Mark>23.-29.</Mark>
  <Item>
    This command  starts  the  backtrack  search.  Its  result  will  be  the
    centralizer as a subgroup of <M>G</M>. Its arguments are
  </Item>
  <Mark>24.</Mark>
  <Item>
    the group we want to run through,
  </Item>
  <Mark>25.</Mark>
  <Item>
    the property we want to test, as a &GAP; function,
  </Item>
  <Mark>26.</Mark>
  <Item>
    <K>false</K> if we are looking for a subgroup, <K>true</K> in the case
    of   a  representative  search    (when  the result   would    be one
    representative),
  </Item>
  <Mark>27.</Mark>
  <Item>
    the R-base,
  </Item>
  <Mark>28.</Mark>
  <Item>
    a list  of data, to be stored  in <M>I</M><C>.data</C>, which has
    in position&nbsp;1 the first member <M>\Sigma_0</M>  of the decreasing
    sequence of <Q>image partitions</Q> mentioned in
    <Ref Sect="Backtrack"/>.
    In the centralizer example, position&nbsp;2 contains the
    element that is  to be centralized. In the  case of  a representative
    search,  i.e.,  a conjugacy test  <M>g</M><C>^</C><M>c</M><C> ?= </C><M>h</M>, we
    would  have <M>h</M>   instead of  <M>g</M>   here, and   possibly a <M>\Sigma_0</M>
    different from <M>\Pi_0</M> (e.g., a  partition into unions of  <M>h</M>-cycles
    of same length).
  </Item>
  <Mark>29.</Mark>
  <Item>
    two subgroups <M>L \leq C_G(g)</M> and <M>R \leq C_G(h)</M> known  in
    advance (we have <M>L = R</M> in the centralizer case).
  </Item>
</List>

</Subsection>


<Subsection Label="Refinement functions for the backtrack search">
<Heading>Refinement functions for the backtrack search</Heading>

The last
subsection showed   how the refinement   process leading from  <M>\Pi_i</M> to
<M>\Pi_{{i+1}}</M>  is coded in the  function  <M>R</M><C>.nextLevel</C>, this  has to be
executed once the  base point  <M>a_{{i+1}}</M>.  The analogous refinement  step
from <M>\Sigma_i</M> to <M>\Sigma_{{i+1}}</M> must be performed for each choice of an
image <M>b_{{i+1}}</M> for  <M>a_{{i+1}}</M>, and it will  depend  on the corresponding
value of <M>\Sigma_i \wedge (\{b_{{i+1}}\}, \Omega \setminus \{b_{{i+1}}\})</M>.
But  before
we  can continue  our centralizer example,  we  must,  for the interested
reader, document the record components of the other black box <M>I</M>, as we
did above for the R-base black box <M>R</M>. Most of the components change as
&GAP; walks up and down the levels of the search tree.
<List>
  <Mark><C>data</C></Mark>
  <Item>
    this will be mentioned below
  </Item>
  <Mark><C>depth</C></Mark>
  <Item>
    the level <M>i</M> in the search tree of the current node <M>\Sigma_i</M>
  </Item>
  <Mark><C>bimg</C></Mark>
  <Item>
    a list of images of the points in <M>R</M><C>.base</C>
  </Item>
  <Mark><C>partition</C></Mark>
  <Item>
    the partition <M>\Sigma_i</M> of the current node
  </Item>
  <Mark><C>level</C></Mark>
  <Item>
    the stabilizer chain <M>R</M><C>.lev</C><M>[i]</M> at the current level
  </Item>
  <Mark><C>perm</C></Mark>
  <Item>
    a permutation mapping <C>Fixcells</C><M>( \Pi_i )</M> to
    <C>Fixcells</C><M>( \Sigma_i )</M>;
    this implies mapping <M>(a_1, \ldots, a_i)</M> to
    <M>(b_1, \ldots, b_i)</M>
  </Item>
  <Mark><C>level2</C>, <C>perm2</C></Mark>
  <Item>
    a  similar construction for    the second stabilizer chain,   <K>false</K>
    otherwise (and <K>true</K> if <M>R</M><C>.level2 = </C><K>true</K>)
  </Item>
</List>
<P/>
As declared in the above code for <Ref Oper="Centralizer" Label="for a magma and a submagma"/>,
the refinement is performed by the function
<C>Refinement.Centralizer</C><M>( R, I, \Pi</M><C>.cellno</C><M>[p], p, where )</M>.
The  functions in the  record
<C>Refinement</C> always   take two  additional   arguments  before  the  ones
specified  in  the <C>AddRefinement</C>  call (in  line&nbsp;13 above),  namely the
R-base  <M>R</M> and  the  current  value  <M>I</M>  of the <Q>R-image</Q>.
In our example, <M>p</M> is a fixpoint of
<M>\Pi = \Pi_i \wedge (\{ a_{{i+1}} \}, \Omega \setminus \{ a_{{i+1}} \})</M>
such that <M>where = \Pi</M><C>.cellno</C><M>[ p^g ]</M>.
The <C>Refinement</C> functions must return <K>false</K> if the refinement is
unsuccessful (e.g., because it  leads to <M>\Sigma_{{i+1}}</M> having  different
cell   sizes from  <M>\Pi_{{i+1}}</M>)  and  <K>true</K>  otherwise.
Our particular function looks like this.
<P/>
<Listing><![CDATA[
Refinements.Centralizer := function( R, I, cellno, p, where )
local  Sigma, q;
Sigma := I.partition;
q := FixpointCellNo( Sigma, cellno ) ^ I.data[ 2 ];
return IsolatePoint( Sigma, q ) = where and ProcessFixpoint( I, p, q );
end;
]]></Listing>
<P/>
The list numbers below refer to the line numbers of the code immediately
above.
<P/>
<List>
  <Mark>3.</Mark>
  <Item>
    The current value of
    <M>\Sigma_i \wedge (\{ b_{{i+1}} \}, \Omega \setminus \{ b_{{i+1}} \})</M>
    is always found in <M>I</M><C>.partition</C>.
  </Item>
  <Mark>4.</Mark>
  <Item>
    The image of the only point in cell number
    <M>cellno = \Pi_i</M><C>.cellno</C><M>[ p ]</M> in <M>\Sigma</M>
    under <M>g = I</M><C>.data</C><M>[ 2 ]</M> is calculated.
  </Item>
  <Mark>5.</Mark>
  <Item>
    The function returns <K>true</K> only if the  image  <M>q</M>  has  the
    same  cell number in <M>\Sigma</M> as <M>p</M> had in <M>\Pi</M>
    (i.e., <M>where</M>) and if <M>q</M> can  be prescribed as an  image
    for  <M>p</M>  under  the  coset  of  the  stabilizer
    <M>G_{{a_1 \ldots a_{{i+1}}}}.c</M> where <M>c \in G</M>  is  an
    (already  constructed) element mapping the  earlier  base  points
    <M>a_1, \ldots, a_{{i+1}}</M>  to  the already chosen images
    <M>b_1, \ldots, b_{{i+1}}</M>.
    This  latter  condition  is tested by
    <C>ProcessFixpoint</C><M>( I, p, q )</M> which, if successful,
    also does the necessary bookkeeping in <M>I</M>.
    In analogy to  the  remark  about line&nbsp;12 in the program above,
    the chosen image  <M>b_{{i+1}}</M>  for  the  base point <M>a_{{i+1}}</M>
    has already been processed  implicitly  by  the  function
    <C>PartitionBacktrack</C>,
    and this processing includes the construction of an element
    <M>c \in  G</M>  which  maps  <C>Fixcells</C><M>( \Pi_i )</M>  to
    <C>Fixcells</C><M>( \Sigma_i )</M>  and  <M>a_{{i+1}}</M>  to
    <M>b_{{i+1}}</M>.
    By  contrast,  the  extra fixpoints <M>p</M> and <M>q</M> in
    <M>\Pi_{{i+1}}</M> and <M>\Sigma_{{i+1}}</M> were  not  chosen automatically,
    so they require an  explicit  call  of  <C>ProcessFixpoint</C>,
    which replaces the element <M>c</M> by some <M>c'.c</M>
    (with  <M>c' \in  G_{{a_1 \ldots a_{{i+1}}}}</M>) which in addition maps
    <M>p</M> to <M>q</M>, or returns <K>false</K> if  this is impossible.
  </Item>
</List>
<P/>
You should now be able to  guess what
<C>Refinements.ProcessFixpoint</C><M>( R, I, p, where )</M> does:
it  simply returns
<C>ProcessFixpoint</C><M>( I, p, </M><C>FixpointCellNo</C><M>(
 I</M><C>.partition</C><M>, where ) )</M>.
<P/>
<E>Summary.</E>
<P/>
When you write  your  own backtrack functions using
the  partition technique,  you  have  to  supply  an R-base, including  a
component <C>nextLevel</C>, and   the  functions in the   <C>Refinements</C> record
which  you need. Then  you can start  the backtrack by passing the R-base
and the additional data (for the  <C>data</C> component of the <Q>R-image</Q>) to
<C>PartitionBacktrack</C>.

</Subsection>

<Subsection Label="Functions for meeting ordered partitions">
<Heading>Functions for meeting ordered partitions</Heading>

A   kind  of
refinement that   occurs  in particular  in   the  normalizer calculation
involves computing  the  meet of <M>\Pi</M>  (cf.&nbsp;lines&nbsp;6ff.&nbsp;above) with an
arbitrary other partition  <M>\Lambda</M>, not just  with one point. To do this
efficiently, &GAP; uses the following two functions.
<P/>
<C>StratMeetPartition( </C><M>R</M>, <M>\Pi</M>, <M>\Lambda</M> <C>[</C>, <M>g</M> <C>] )</C>
<P/>
<C>MeetPartitionStrat( </C><M>R</M>, <M>I</M><C>{, </C><M>\Lambda'</M><C>}[, {</C><M>g'</M><C>}]</C>, <M>strat</M> <C>)</C>
<P/>
<Index>meet strategy</Index>
Such a  <C>StratMeetPartition</C>   command would   typically appear in    the
function call <M>R</M><C>.nextLevel</C><M>(  \Pi, R )</M>  (during the refinement of
<M>\Pi_i</M>  to  <M>\Pi_{{i+1}}</M>).
This command replaces <M>\Pi</M> by <M>\Pi \wedge \Lambda</M>
(thereby  <E>changing</E> the  second  argument) and returns a <Q>meet
strategy</Q>  <M>strat</M>. This  is  (for  us) a  black   box which  serves two
purposes:  First, it allows &GAP; to  calculate faster the corresponding
meet <M>\Sigma \wedge \Lambda'</M>,  which must then  appear in a <C>Refinements</C>
function  (during the refinement of  <M>\Sigma_i</M> to <M>\Sigma_{{i+1}}</M>). It is
faster  to compute <M>\Sigma \wedge \Lambda'</M> with  the <Q>meet strategy</Q> of
<M>\Pi \wedge \Lambda</M> because  if the refinement  of <M>\Sigma</M> is successful
at  all, the  intersection  of a  cell from   the left hand  side of  the
<M>\wedge</M> sign  with a cell  from the right hand side  must  have the same
size   in both cases  (and  <M>strat</M>  records these   sizes, so  that only
non-empty intersections must  be calculated for <M>\Sigma \wedge \Lambda'</M>).
Second, if  there  is a discrepancy  between  the behaviour prescribed by
<M>strat</M> and the behaviour observed when refining <M>\Sigma</M>, the refinement
can immediately be abandoned.
<P/>
On  the  other hand, if you  only  want to meet   a  partition <M>\Pi</M> with
<M>\Lambda</M>  for  a one-time  use, without recording   a strategy,  you can
simply type <C>StratMeetPartition</C><M>( \Pi, \Lambda )</M>  as in the following
example, which also demonstrates some other partition-related commands.
<P/>
<!-- % <Example><![CDATA[ -->
<!-- % gap> P := Partition( [[1,2],[3,4,5],[6]] );;  Cells( P ); -->
<!-- % [ [ 1, 2 ], [ 3, 4, 5 ], [ 6 ] ] -->
<!-- % gap> Q := OnPartitions( P, (1,3,6) );;  Cells( Q );   -->
<!-- % [ [ 3, 2 ], [ 6, 4, 5 ], [ 1 ] ]  -->
<!-- % gap> StratMeetPartition( P, Q );  -->
<!-- % [  ]  # the ``meet strategy'' was not recorded, ignore this result -->
<!-- % gap> Cells( P ); -->
<!-- % [ [ 1 ], [ 5, 4 ], [ 6 ], [ 2 ], [ 3 ] ] -->
<!-- % ]]></Example> -->
<!--  -->
<!-- % The preceding (original) example doesn't work because there is no -->
<!-- % function 'OnPartitions'. The following example works, but I don't -->
<!-- % know if it makes sense to have it here at all. (Note, in particular, -->
<!-- % that the function 'Partition' is undocumented.)  VF  14.10.02 -->

<Example><![CDATA[
gap> P := Partition( [[1,2],[3,4,5],[6]] );;  Cells( P );
[ [ 1, 2 ], [ 3, 4, 5 ], [ 6 ] ]
gap> Q := Partition( OnTuplesTuples( last, (1,3,6) ) );;  Cells( Q );
[ [ 3, 2 ], [ 6, 4, 5 ], [ 1 ] ]
gap> StratMeetPartition( P, Q );
[  ]
gap> # The ``meet strategy'' was not recorded, ignore this result.
gap> Cells( P );
[ [ 1 ], [ 5, 4 ], [ 6 ], [ 2 ], [ 3 ] ]
]]></Example>
<P/>
You can even say  <C>StratMeetPartition</C><M>( \Pi, \Delta )</M>  where <M>\Delta</M>
is simply  a subset  of  <M>\Omega</M>, it   will then  be interpreted as  the
partition <M>(\Delta, \Omega \setminus \Delta)</M>.
<P/>
&GAP; makes use   of  the advantages  of   a <Q>meet  strategy</Q>  if  the
refinement   function  in <C>Refinements</C>  contains  a <C>MeetPartitionStrat</C>
command where   <M>strat</M>  is   the    <Q>meet  strategy</Q>  calculated    by
<C>StratMeetPartition</C> before.  Such a command replaces <M>I</M><C>.partition</C> by
its meet with <M>\Lambda'</M>, again changing the argument <M>I</M>. The necessary
reversal of these changes when backtracking from  a node (and prescribing
the next possible image  for a base point) is  automatically done by  the
function <C>PartitionBacktrack</C>.
<P/>
In  all cases, an additional  argument <M>g</M> means that the   meet is to be
taken  not with <M>\Lambda</M>, but instead with <M>\Lambda.{{g^{{-1}}}}</M>,
where
operation  on ordered partitions is  meant cellwise  (and setwise on each
cell). (Analogously for the primed arguments.)
<P/>
<Example><![CDATA[
gap> P := Partition( [[1,2],[3,4,5],[6]] );;
gap> StratMeetPartition( P, P, (1,6,3) );;  Cells( P );
[ [ 1 ], [ 5, 4 ], [ 6 ], [ 2 ], [ 3 ] ]
]]></Example>
Note that <M>P.(1,3,6) = Q</M>.

</Subsection>
</Section>


<!-- %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -->
<Section Label="Stabilizer Chains for Automorphisms Acting on Enumerators">
<Heading>Stabilizer Chains for Automorphisms Acting on Enumerators</Heading>

This section describes a way of representing the automorphism group of a
group as permutation group, following <Cite Key="Sims97"/>. The code however is
not yet included in the &GAP; library.
<P/>
In this section  we present an example in  which objects we  already know
(namely,  automorphisms  of   solvable  groups)   are  equipped  with the
permutation-like operations <C>^</C> and <C>/</C>  for action on positive integers.
To achieve this, we must  define a new  type of objects which behave like
permutations   but are  represented     as automorphisms  acting  on   an
enumerator.  Our  goal is to  generalize  the Schreier-Sims algorithm for
construction of a stabilizer chain to groups of such new automorphisms.


<Subsection Label="An operation domain for automorphisms">
<Heading>An operation domain for automorphisms</Heading>

The idea we describe here is due  to C.&nbsp;Sims.
We consider  a group <M>A</M>  of  automorphisms of a
group <M>G</M>, given by generators,  and we would like  to know its order. Of
course we  could   follow the strategy  of  the   Schreier-Sims algorithm
(described  in <Ref Sect="Stabilizer Chains"/>) for <M>A</M>
acting   on   <M>G</M>. This would    involve   a  call  of
<C>StabChainStrong( EmptyStabChain( [], One( <A>A</A> ) ), GroupGenerators( <A>A</A> ) )</C>  where
<C>StabChainStrong</C>  is a function as the  one described in the pseudo-code
below:
<P/>
<Listing><![CDATA[
StabChainStrong := function( S, newgens )
  Extend the Schreier tree of S with newgens.
  for sch  in  Schreier generators  do
    if not sch in S.stabilizer  then
      StabChainStrong( S.stabilizer, [ sch ] );
    fi;
  od;
end;
]]></Listing>
<P/>
The membership test <M>sch \notin S</M><C>.stabilizer</C> can be performed because
the  stabilizer chain  of <M>S</M><C>.stabilizer</C>  is   already correct at  that
moment. We  even know a base  in advance, namely  any  generating set for
<M>G</M>.
Fix such a generating set <M>(g_1, \ldots, g_d)</M> and observe that this
base  is  generally very   short compared  to   the degree <M>|G|</M>  of  the
operation. The problem with the Schreier-Sims algorithm, however, is then
that the length of the first  basic orbit <M>g_1.A</M>  would already have the
magnitude of <M>|G|</M>,  and the basic orbits at  deeper levels would  not be
much shorter. For the advantage of a short base  we pay the high price of
long basic  orbits, since the  product of  the  (few) basic orbit lengths
must  equal <M>|A|</M>.  Such  long  orbits  make the Schreier-Sims  algorithm
infeasible,   so we have to   look for a  longer base  with shorter basic
orbits.
<P/>
Assume that   <M>G</M> is solvable  and  choose  a  characteristic series with
elementary abelian factors. For the sake of  simplicity we assume that <M>N
&lt; G</M> is an   elementary abelian characteristic subgroup  with elementary
abelian factor group <M>G/N</M>. Since <M>N</M> is characteristic, <M>A</M> also acts as
a group of automorphisms  on the factor  group <M>G/N</M>,  but of course  not
necessarily  faithfully. To retain  a faithful action,  we let <M>A</M> act on
the   disjoint   union   <M>G/N</M>   with   <M>G</M>,   and   choose    as    base
<M>(g_1 N, \ldots, g_d N, g_1, \ldots, g_d)</M>.
Now the first <M>d</M> basic  orbits  lie
inside <M>G/N</M> and can have length at most <M>[G:N]</M>. Since the  base
points <M>g_1 N, \ldots, g_d N</M>  form  a  generating  set  for  <M>G/N</M>,  their
iterated stabilizer <M>A^{(d+1)}</M> acts trivially on the factor group <M>G/N</M>,
i.e., it leaves the cosets <M>g_i N</M> invariant. Accordingly,  the  next  <M>d</M>
basic orbits lie inside <M>g_i N</M> (for <M>i = 1, \ldots, d</M>) and can  have  length
at most&nbsp;<M>|N|</M>.
<P/>
Generalizing this method to a characteristic series
<M>G = N_0 &gt; N_1 &gt; \ldots &gt; N_l = \{ 1 \}</M> of length
<M>l &gt; 2</M>, we  can always find  a base of length <M>l.d</M>
such that each  basic orbit is  contained in a  coset of a characteristic
factor, i.e. in a set of the form <M>g_i N_{{j-1}} / N_j</M>
(where <M>g_i</M> is one of
the generators  of <M>G</M> and <M>1 \leq j \leq l</M>).
In particular, the  length of
the basic  orbits   is  bounded   by  the  size  of    the  corresponding
characteristic factors. To implement a Schreier-Sims algorithm for such a
base, we  must  be   able  to  let   automorphisms  act  on   cosets   of
characteristic  factors <M>g_i N_{{j-1}} / N_j</M>,
for  varying  <M>i</M>  and <M>j</M>.  We
would    like to    translate each such     action  into  an  action   on
<M>\{ 1, \ldots, [ N_{{j-1}}:N_j] \}</M>,
because then we need not enumerate the operation domain,
which is the disjoint union of
<M>G / N_1</M>, <M>G / N_2 \ldots G / N_l</M>,
as a whole. Enumerating it  as a whole would result  in basic orbits like
<C>orbit</C><M> \subseteq \{ 1001, \ldots, 1100 \}</M>
with a  <C>transversal</C> list whose
first 1000 entries would be unbound, but  still require 4&nbsp;bytes of memory
each (see&nbsp;<Ref Sect="Stabilizer Chain Records"/>).
<P/>
Identifying each coset <M>g_i N_{{j-1}} / N_j</M> into
<M>\{ 1, \ldots, [N_{{j-1}}:N_j] \}</M> of course means
that we have  to change the action  of
the automorphisms on     every  level of   the  stabilizer   chain.  Such
flexibility is not   possible with permutations  because their  effect on
positive  integers  is <Q>hardwired</Q>  into them,  but  we can install new
operations for automorphisms.

</Subsection>


<Subsection Label="Enumerators for cosets of characteristic factors">
<Heading>Enumerators for cosets of characteristic factors</Heading>

So far  we
have  not used the  fact that  the characteristic  factors are elementary
abelian, but we will do so from  here on.
Our first task is to implement an enumerator
(see <Ref Attr="AsList"/> and
<Ref Sect="Enumerators"/>)
for a coset of a characteristic factor in a solvable  group  <M>G</M>.
We assume that such a coset <M>g N/M</M> is given by
<P/>
<List>
  <Mark>(1)</Mark>
  <Item>
    a pcgs for  the group  <M>G</M> (see  <Ref Attr="Pcgs"/>),
    let <M>n = </M><C>Length( </C><M>pcgs</M><C> )</C>;
  </Item>
  <Mark>(2)</Mark>
  <Item>
    a range <M>range = [ start .. stop ]</M> indicating that
    <M>N = \langle pcgs\{ [ start .. n ] \} \rangle</M> and
    <M>M = \langle pcgs\{ [ stop + 1 .. n ] \} \rangle</M>,
    i.e., the cosets of <M>pcgs\{ range \}</M> form a base
    for the vector space <M>N/M</M>;
  </Item>
  <Mark>(3)</Mark>
  <Item>
    the representative <M>g</M>.
  </Item>
</List>
<P/>
We   first  define a  new representation  for   such enumerators and then
construct them by simply putting these three pieces of data into a record
object. The  enumerator  should  behave as  a   list of  group   elements
(representing cosets modulo <M>M</M>),   consequently, its family will  be the
family of the <M>pcgs</M> itself.
<P/>
<Log><![CDATA[
DeclareRepresentation( "IsCosetSolvableFactorEnumeratorRep", IsEnumerator,
    [ "pcgs", "range", "representative" ] );

EnumeratorCosetSolvableFactor := function( pcgs, range, g )
    return Objectify( NewType( FamilyObj( pcgs ),
                   IsCosetSolvableFactorEnumeratorRep ),
                   rec( pcgs := pcgs,
                       range := range,
              representative := g ) );
end;
]]></Log>
<P/>
The definition of the operations <Ref Attr="Length"/>,
<Ref Oper="\[\]"/> and <Ref Oper="Position"/>
is now
straightforward. The  code has sometimes  been  abbreviated and is  meant
<Q>cum grano salis</Q>,  e.g.,  the declaration of  the local  variables has
been left out.
<P/>
<Log><![CDATA[
InstallMethod( Length, [ IsCosetSolvableFactorEnumeratorRep ],
    enum -> Product( RelativeOrdersPcgs( enum!.pcgs ){ enum!.range } ) );

InstallMethod( \[\], [ IsCosetSolvableFactorEnumeratorRep,
        IsPosRat and IsInt ],
    function( enum, pos )
    elm := ();
    pos := pos - 1;
    for i  in Reversed( enum!.range )  do
        p := RelativeOrderOfPcElement( enum!.pcgs, i );
        elm := enum!.pcgs[ i ] ^ ( pos mod p ) * elm;
        pos := QuoInt( pos, p );
    od;
    return enum!.representative * elm;
end );

InstallMethod( Position, [ IsCosetSolvableFactorEnumeratorRep,
        IsObject, IsZeroCyc ],
    function( enum, elm, zero )
    exp := ExponentsOfPcElement( enum!.pcgs,
                   LeftQuotient( enum!.representative, elm ) );
    pos := 0;
    for i  in enum!.range  do
        pos := pos * RelativeOrderOfPcElement( pcgs, i ) + exp[ i ];
    od;
    return pos + 1;
end );
]]></Log>

</Subsection>


<Subsection Label="Making automorphisms act on such enumerators">
<Heading>Making automorphisms act on such enumerators</Heading>

Our next task is to make automorphisms of the solvable group
<M>pcgs</M><C>!.group</C> act on
<M>[ 1 .. </M><C>Length</C><M>( enum ) ]</M> for such an enumerator
<M>enum</M>. We achieve this
by  introducing a new  representation of automorphisms on enumerators and
by putting the enumerator together  with the automorphism into an  object
which behaves like  a permutation. Turning  an ordinary automorphism into
such  a special  automorphism requires  then   the construction of  a new
object which has the new type. We provide an operation
<C>PermOnEnumerator( <A>model</A>, <A>aut</A> )</C> which constructs such a new
object having the same type as <A>model</A>,
but representing the  automorphism  <A>aut</A>. So <A>aut</A>  can be
either an ordinary automorphism or one which already has an enumerator in
its type, but perhaps  different from the one  we want (i.e. from the one
in <A>model</A>).
<P/>
<Log><![CDATA[
DeclareCategory( "IsPermOnEnumerator",
    IsMultiplicativeElementWithInverse and IsPerm );

DeclareRepresentation( "IsPermOnEnumeratorDefaultRep",
    IsPermOnEnumerator and IsAttributeStoringRep,
    [ "perm" ] );

DeclareOperation( "PermOnEnumerator",
    [ IsEnumerator, IsObject ] );

InstallMethod( PermOnEnumerator,
    [ IsEnumerator, IsObject ],
    function( enum, a )
    SetFilterObj( a, IsMultiplicativeElementWithInverse );
    a := Objectify( NewKind( PermutationsOnEnumeratorsFamily,
                 IsPermOnEnumeratorDefaultRep ),
                 rec( perm := a ) );
    SetEnumerator( a, enum );
    return a;
end );

InstallMethod( PermOnEnumerator,
    [ IsEnumerator, IsPermOnEnumeratorDefaultRep ],
    function( enum, a )
    a := Objectify( TypeObj( a ), rec( perm := a!.perm ) );
    SetEnumerator( a, enum );
    return a;
end );
]]></Log>
<P/>
Next we  have to install new  methods for the  operations which calculate
the  product of two automorphisms, because   this product must again have
the    right type. We    also have to write  a    function which uses the
enumerators to apply such an automorphism to positive integers.
<P/>
<Log><![CDATA[
InstallMethod( \*, IsIdenticalObj,
    [ IsPermOnEnumeratorDefaultRep, IsPermOnEnumeratorDefaultRep ],
    function( a, b )
    perm := a!.perm * b!.perm;
    SetIsBijective( perm, true );
    return PermOnEnumerator( Enumerator( a ), perm );
end );

InstallMethod( \^,
    [ IsPosRat and IsInt, IsPermOnEnumeratorDefaultRep ],
    function( p, a )
    return PositionCanonical( Enumerator( a ),
                   Enumerator( a )[ p ] ^ a!.perm );
end );
]]></Log>
<P/>
How the corresponding methods for <C><A>p</A> / <A>aut</A></C>
and <C><A>aut</A> ^ <A>n</A></C> look like is obvious.
<P/>
Now we  can  formulate  the recursive procedure   <C>StabChainStrong</C> which
extends  the stabilizer chain by adding  in new  generators <M>newgens</M>. We
content  ourselves again   with pseudo-code, emphasizing  only  the lines
which set the <C>EnumeratorDomainPermutation</C>. We assume that initially <M>S</M>
is a stabilizer chain for the trivial subgroup with a level for each pair
<M>(range,g)</M> characterizing an enumerator  (as  described above). We  also
assume that  the <C>identity</C>  element at each  level already  has the type
corresponding to that level.
<P/>
<Listing><![CDATA[
StabChainStrong := function( S, newgens )
  for i  in [ 1 .. Length( newgens ) ]  do
    newgens[ i ] := AutomorphismOnEnumerator( S.identity, newgens[ i ] );
  od;
  Extend the Schreier tree of S with newgens.
  for sch  in  Schreier generators  do
    if not sch in S.stabilizer  then
      StabChainStrong( S.stabilizer, [ sch ] );
    fi;
  od;
end;
]]></Listing>

</Subsection>
</Section>
</Chapter>


<!-- %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -->
<!-- %% -->
<!-- %E -->