Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/groups/perm_gps/permgroup_element.pyx
4058 views
1
"""
2
Permutation group elements
3
4
AUTHORS:
5
6
- David Joyner (2006-02)
7
8
- David Joyner (2006-03): word problem method and reorganization
9
10
- Robert Bradshaw (2007-11): convert to Cython
11
12
EXAMPLES: The Rubik's cube group::
13
14
sage: f= [(17,19,24,22),(18,21,23,20),(6,25,43,16),(7,28,42,13),(8,30,41,11)]
15
sage: b=[(33,35,40,38),(34,37,39,36),( 3, 9,46,32),( 2,12,47,29),( 1,14,48,27)]
16
sage: l=[( 9,11,16,14),(10,13,15,12),( 1,17,41,40),( 4,20,44,37),( 6,22,46,35)]
17
sage: r=[(25,27,32,30),(26,29,31,28),( 3,38,43,19),( 5,36,45,21),( 8,33,48,24)]
18
sage: u=[( 1, 3, 8, 6),( 2, 5, 7, 4),( 9,33,25,17),(10,34,26,18),(11,35,27,19)]
19
sage: d=[(41,43,48,46),(42,45,47,44),(14,22,30,38),(15,23,31,39),(16,24,32,40)]
20
sage: cube = PermutationGroup([f,b,l,r,u,d])
21
sage: F=cube.gens()[0]
22
sage: B=cube.gens()[1]
23
sage: L=cube.gens()[2]
24
sage: R=cube.gens()[3]
25
sage: U=cube.gens()[4]
26
sage: D=cube.gens()[5]
27
sage: cube.order()
28
43252003274489856000
29
sage: F.order()
30
4
31
32
The interested user may wish to explore the following commands:
33
move = cube.random_element() and time word_problem([F,B,L,R,U,D],
34
move, False). This typically takes about 5 minutes (on a 2 Ghz
35
machine) and outputs a word ('solving' the cube in the position
36
move) with about 60 terms or so.
37
38
OTHER EXAMPLES: We create element of a permutation group of large
39
degree.
40
41
::
42
43
sage: G = SymmetricGroup(30)
44
sage: s = G(srange(30,0,-1)); s
45
(1,30)(2,29)(3,28)(4,27)(5,26)(6,25)(7,24)(8,23)(9,22)(10,21)(11,20)(12,19)(13,18)(14,17)(15,16)
46
"""
47
48
###########################################################################
49
# Copyright (C) 2006 William Stein <[email protected]>
50
# Copyright (C) 2006 David Joyner
51
#
52
# Distributed under the terms of the GNU General Public License (GPL)
53
# http://www.gnu.org/licenses/
54
###########################################################################
55
56
57
import random
58
59
import sage.groups.group as group
60
61
include "../../ext/stdsage.pxi"
62
include "../../ext/interrupt.pxi"
63
include "../../ext/python_list.pxi"
64
65
from sage.rings.all import ZZ, Integer, is_MPolynomial, is_Polynomial
66
from sage.matrix.all import MatrixSpace
67
from sage.interfaces.all import gap, is_GapElement, is_ExpectElement
68
from sage.sets.finite_enumerated_set import FiniteEnumeratedSet
69
import sage.structure.coerce as coerce
70
71
import operator
72
73
from sage.rings.fast_arith cimport arith_llong
74
cdef arith_llong arith = arith_llong()
75
cdef extern from *:
76
long long LONG_LONG_MAX
77
78
#import permgroup_named
79
80
def make_permgroup_element(G, x):
81
"""
82
Returns a PermutationGroupElement given the permutation group
83
``G`` and the permutation ``x`` in list notation.
84
85
This is function is used when unpickling old (pre-domain) versions
86
of permutation groups and their elements. This now does a bit of
87
processing and calls :func:`make_permgroup_element_v2` which is
88
used in unpickling the current PermutationGroupElements.
89
90
EXAMPLES::
91
92
sage: from sage.groups.perm_gps.permgroup_element import make_permgroup_element
93
sage: S = SymmetricGroup(3)
94
sage: make_permgroup_element(S, [1,3,2])
95
(2,3)
96
"""
97
domain = FiniteEnumeratedSet(range(1, len(x)+1))
98
return make_permgroup_element_v2(G, x, domain)
99
100
def make_permgroup_element_v2(G, x, domain):
101
"""
102
Returns a PermutationGroupElement given the permutation group
103
``G``, the permutation ``x`` in list notation, and the domain
104
``domain`` of the permutation group.
105
106
This is function is used when unpickling permutation groups and
107
their elements.
108
109
EXAMPLES::
110
111
sage: from sage.groups.perm_gps.permgroup_element import make_permgroup_element_v2
112
sage: S = SymmetricGroup(3)
113
sage: make_permgroup_element_v2(S, [1,3,2], S.domain())
114
(2,3)
115
"""
116
# Note that it has to be in-sync with the __init__ method of
117
# PermutationGroup_generic since the elements have to be created
118
# before the PermutationGroup_generic is initialized. The
119
# constructor for PermutationGroupElement requires that
120
# G._domain_to_gap be set.
121
G._domain = domain
122
G._deg = len(domain)
123
G._domain_to_gap = dict([(key, i+1) for i, key in enumerate(domain)])
124
G._domain_from_gap = dict([(i+1, key) for i, key in enumerate(domain)])
125
return G(x, check=False)
126
127
128
def is_PermutationGroupElement(x):
129
"""
130
Returns True if ``x`` is a PermutationGroupElement.
131
132
EXAMPLES::
133
134
sage: p = PermutationGroupElement([(1,2),(3,4,5)])
135
sage: from sage.groups.perm_gps.permgroup_element import is_PermutationGroupElement
136
sage: is_PermutationGroupElement(p)
137
True
138
"""
139
return isinstance(x, PermutationGroupElement)
140
141
def string_to_tuples(g):
142
"""
143
EXAMPLES::
144
145
sage: from sage.groups.perm_gps.permgroup_element import string_to_tuples
146
sage: string_to_tuples('(1,2,3)')
147
[(1, 2, 3)]
148
sage: string_to_tuples('(1,2,3)(4,5)')
149
[(1, 2, 3), (4, 5)]
150
sage: string_to_tuples(' (1,2, 3) (4,5)')
151
[(1, 2, 3), (4, 5)]
152
sage: string_to_tuples('(1,2)(3)')
153
[(1, 2), (3,)]
154
"""
155
from sage.misc.all import sage_eval
156
157
if not isinstance(g, str):
158
raise ValueError, "g (= %s) must be a string"%g
159
elif g == '()':
160
return []
161
g = g.replace('\n','').replace(' ', '').replace(')(', '),(').replace(')', ',)')
162
g = '[' + g + ']'
163
return sage_eval(g, preparse=False)
164
165
def standardize_generator(g, convert_dict=None):
166
"""
167
Standardizes the input for permutation group elements to a list of
168
tuples. This was factored out of the
169
PermutationGroupElement.__init__ since
170
PermutationGroup_generic.__init__ needs to do the same computation
171
in order to compute the domain of a group when it's not explicitly
172
specified.
173
174
INPUT:
175
176
- ``g`` - a list, tuple, string, GapElement,
177
PermuationGroupElement, Permutation
178
179
- ``convert_dict`` - (optional) a dictionary used to convert the
180
points to a number compatible with GAP.
181
182
OUTPUT:
183
184
The permutation in as a list of cycles.
185
186
EXAMPLES::
187
188
sage: from sage.groups.perm_gps.permgroup_element import standardize_generator
189
sage: standardize_generator('(1,2)')
190
[(1, 2)]
191
192
sage: p = PermutationGroupElement([(1,2)])
193
sage: standardize_generator(p)
194
[(1, 2)]
195
sage: standardize_generator(p._gap_())
196
[(1, 2)]
197
sage: standardize_generator((1,2))
198
[(1, 2)]
199
sage: standardize_generator([(1,2)])
200
[(1, 2)]
201
sage: standardize_generator(Permutation([2,1,3]))
202
[(1, 2), (3,)]
203
204
::
205
206
sage: d = {'a': 1, 'b': 2}
207
sage: p = SymmetricGroup(['a', 'b']).gen(0); p
208
('a','b')
209
sage: standardize_generator(p, convert_dict=d)
210
[(1, 2)]
211
sage: standardize_generator(p._gap_(), convert_dict=d)
212
[(1, 2)]
213
sage: standardize_generator(('a','b'), convert_dict=d)
214
[(1, 2)]
215
sage: standardize_generator([('a','b')], convert_dict=d)
216
[(1, 2)]
217
"""
218
from sage.interfaces.gap import GapElement
219
from sage.combinat.permutation import Permutation
220
from sage.libs.pari.gen import gen
221
from sage.combinat.permutation import Permutation_class
222
223
if isinstance(g, gen):
224
g = list(g)
225
226
needs_conversion = True
227
if isinstance(g, GapElement):
228
g = str(g)
229
needs_conversion = False
230
if isinstance(g, Permutation_class):
231
return g.cycle_tuples()
232
if isinstance(g, PermutationGroupElement):
233
g = g.cycle_tuples()
234
if isinstance(g, str):
235
g = string_to_tuples(g)
236
if isinstance(g, tuple) and (len(g) == 0 or not isinstance(g[0], tuple)):
237
g = [g]
238
239
#Get the permutation in list notation
240
if PyList_CheckExact(g) and (len(g) == 0 or not PY_TYPE_CHECK(g[0], tuple)):
241
if convert_dict is not None and needs_conversion:
242
g = [convert_dict[x] for x in g]
243
244
245
#FIXME: Currently, we need to verify that g defines an actual
246
#permutation since the constructor Permutation does not
247
#perform this check. When it does, we should remove this code.
248
#See #8392
249
if set(g) != set(range(1, len(g)+1)):
250
raise ValueError, "Invalid permutation vector: %s"%g
251
return Permutation(g).cycle_tuples()
252
else:
253
if convert_dict is not None and needs_conversion:
254
g = [tuple([convert_dict[x] for x in cycle])for cycle in g]
255
256
return g
257
258
cdef class PermutationGroupElement(MultiplicativeGroupElement):
259
"""
260
An element of a permutation group.
261
262
EXAMPLES::
263
264
sage: G = PermutationGroup(['(1,2,3)(4,5)'])
265
sage: G
266
Permutation Group with generators [(1,2,3)(4,5)]
267
sage: g = G.random_element()
268
sage: g in G
269
True
270
sage: g = G.gen(0); g
271
(1,2,3)(4,5)
272
sage: print g
273
(1,2,3)(4,5)
274
sage: g*g
275
(1,3,2)
276
sage: g**(-1)
277
(1,3,2)(4,5)
278
sage: g**2
279
(1,3,2)
280
sage: G = PermutationGroup([(1,2,3)])
281
sage: g = G.gen(0); g
282
(1,2,3)
283
sage: g.order()
284
3
285
286
This example illustrates how permutations act on multivariate
287
polynomials.
288
289
::
290
291
sage: R = PolynomialRing(RationalField(), 5, ["x","y","z","u","v"])
292
sage: x, y, z, u, v = R.gens()
293
sage: f = x**2 - y**2 + 3*z**2
294
sage: G = PermutationGroup(['(1,2,3)(4,5)', '(1,2,3,4,5)'])
295
sage: sigma = G.gen(0)
296
sage: f * sigma
297
3*x^2 + y^2 - z^2
298
"""
299
def __init__(self, g, parent = None, check = True):
300
r"""
301
Create element of a permutation group.
302
303
There are several ways to define a permutation group element:
304
305
306
- Define a permutation group `G`, then use
307
``G.gens()`` and multiplication \* to construct
308
elements.
309
310
- Define a permutation group `G`, then use e.g.,
311
``G([(1,2),(3,4,5)])`` to construct an element of the
312
group. You could also use ``G('(1,2)(3,4,5)')``
313
314
- Use e.g.,
315
``PermutationGroupElement([(1,2),(3,4,5)])`` or
316
``PermutationGroupElement('(1,2)(3,4,5)')`` to make a
317
permutation group element with parent `S_5`.
318
319
320
INPUT:
321
322
323
- ``g`` - defines element
324
325
- ``parent (optional)`` - defines parent group (g must
326
be in parent if specified, or a TypeError is raised).
327
328
- ``check`` - bool (default: True), if False assumes g
329
is a gap element in parent (if specified).
330
331
332
EXAMPLES: We illustrate construction of permutation using several
333
different methods.
334
335
First we construct elements by multiplying together generators for
336
a group.
337
338
::
339
340
sage: G = PermutationGroup(['(1,2)(3,4)', '(3,4,5,6)'], canonicalize=False)
341
sage: s = G.gens()
342
sage: s[0]
343
(1,2)(3,4)
344
sage: s[1]
345
(3,4,5,6)
346
sage: s[0]*s[1]
347
(1,2)(3,5,6)
348
sage: (s[0]*s[1]).parent()
349
Permutation Group with generators [(1,2)(3,4), (3,4,5,6)]
350
351
Next we illustrate creation of a permutation using coercion into an
352
already-created group.
353
354
::
355
356
sage: g = G([(1,2),(3,5,6)])
357
sage: g
358
(1,2)(3,5,6)
359
sage: g.parent()
360
Permutation Group with generators [(1,2)(3,4), (3,4,5,6)]
361
sage: g == s[0]*s[1]
362
True
363
364
We can also use a string instead of a list to specify the
365
permutation.
366
367
::
368
369
sage: h = G('(1,2)(3,5,6)')
370
sage: g == h
371
True
372
373
We can also make a permutation group element directly using the
374
``PermutationGroupElement`` command. Note that the
375
parent is then the full symmetric group `S_n`, where
376
`n` is the largest integer that is moved by the
377
permutation.
378
379
::
380
381
sage: k = PermutationGroupElement('(1,2)(3,5,6)')
382
sage: k
383
(1,2)(3,5,6)
384
sage: k.parent()
385
Symmetric group of order 6! as a permutation group
386
387
Note the comparison of permutations doesn't require that the parent
388
groups are the same.
389
390
::
391
392
sage: k == g
393
True
394
395
Arithmetic with permutations having different parents is also
396
defined::
397
398
sage: k*g
399
(3,6,5)
400
sage: (k*g).parent()
401
Symmetric group of order 6! as a permutation group
402
403
::
404
405
sage: G = PermutationGroup([[(1,2,3),(4,5)],[(3,4)]])
406
sage: loads(dumps(G.0)) == G.0
407
True
408
409
EXAMPLES::
410
411
sage: k = PermutationGroupElement('(1,2)(3,5,6)')
412
sage: k._gap_()
413
(1,2)(3,5,6)
414
sage: k._gap_().parent()
415
Gap
416
417
List notation::
418
419
sage: PermutationGroupElement([1,2,4,3,5])
420
(3,4)
421
422
TESTS::
423
424
sage: PermutationGroupElement(())
425
()
426
sage: PermutationGroupElement([()])
427
()
428
"""
429
from sage.groups.perm_gps.permgroup_named import SymmetricGroup
430
from sage.groups.perm_gps.permgroup import PermutationGroup_generic
431
from sage.combinat.permutation import from_cycles
432
433
convert_dict = parent._domain_to_gap if parent is not None else None
434
try:
435
v = standardize_generator(g, convert_dict)
436
except KeyError:
437
raise ValueError, "Invalid permutation vector: %s" % g
438
439
440
degree = max([1] + [max(cycle+(1,)) for cycle in v])
441
v = from_cycles(degree, v)
442
443
self.__gap = 'PermList(%s)'%v
444
445
if parent is None:
446
parent = SymmetricGroup(len(v))
447
448
if check and parent.__class__ != SymmetricGroup:
449
if not (parent is None or isinstance(parent, PermutationGroup_generic)):
450
raise TypeError, 'parent must be a permutation group'
451
if parent is not None:
452
P = parent._gap_()
453
if not P.parent()(self.__gap) in P:
454
raise TypeError, 'permutation %s not in %s'%(g, parent)
455
456
Element.__init__(self, parent)
457
458
self.n = max(parent.degree(), 1)
459
460
if self.perm is NULL or self.perm is self.perm_buf:
461
self.perm = <int *>sage_malloc(sizeof(int) * self.n)
462
else:
463
self.perm = <int *>sage_realloc(self.perm, sizeof(int) * self.n)
464
465
466
cdef int i, vn = len(v)
467
assert(vn <= self.n)
468
for i from 0 <= i < vn:
469
self.perm[i] = v[i] - 1
470
for i from vn <= i < self.n:
471
self.perm[i] = i
472
473
# We do this check even if check=False because it's fast
474
# (relative to other things in this function) and the
475
# rest of the code is assumes that self.perm specifies
476
# a valid permutation (else segfaults, infinite loops may occur).
477
if not is_valid_permutation(self.perm, vn):
478
raise ValueError, "Invalid permutation vector: %s" % v
479
480
def __cinit__(self, g = None, parent = None, check = True):
481
self.perm = NULL
482
483
def __dealloc__(self):
484
if self.perm is not NULL and self.perm is not self.perm_buf:
485
sage_free(self.perm)
486
487
def __reduce__(self):
488
"""
489
Returns a function and its arguments needed to create this
490
permutation group element. This is used in pickling.
491
492
EXAMPLES::
493
494
sage: g = PermutationGroupElement([(1,2,3),(4,5)]); g
495
(1,2,3)(4,5)
496
sage: func, args = g.__reduce__()
497
sage: func(*args)
498
(1,2,3)(4,5)
499
"""
500
return make_permgroup_element_v2, (self._parent, self.list(), self._parent.domain())
501
502
cdef PermutationGroupElement _new_c(self):
503
cdef PermutationGroupElement other = PY_NEW_SAME_TYPE(self)
504
if HAS_DICTIONARY(self):
505
other.__class__ = self.__class__
506
other._parent = self._parent
507
other.n = self.n
508
if other.n <= sizeof(other.perm_buf) / sizeof(int):
509
other.perm = other.perm_buf
510
else:
511
other.perm = <int *>sage_malloc(sizeof(int) * other.n)
512
return other
513
514
def _gap_(self, gap=None):
515
"""
516
Returns
517
518
EXAMPLES::
519
520
sage: g = PermutationGroupElement([(1,2,3),(4,5)]); g
521
(1,2,3)(4,5)
522
sage: a = g._gap_(); a
523
(1,2,3)(4,5)
524
sage: g._gap_() is g._gap_()
525
True
526
527
Note that only one GapElement is cached:
528
529
sage: gap2 = Gap()
530
sage: b = g._gap_(gap2)
531
sage: c = g._gap_()
532
sage: a is c
533
False
534
"""
535
if (self._gap_element is None or
536
(gap is not None and self._gap_element._parent is not gap)):
537
if gap is None:
538
from sage.interfaces.gap import gap
539
self._gap_element = gap(self._gap_init_())
540
return self._gap_element
541
542
def _gap_init_(self):
543
"""
544
Returns a GAP string representation for this
545
PermutationGroupElement.
546
547
EXAMPLES::
548
549
sage: g = PermutationGroupElement([(1,2,3),(4,5)])
550
sage: g._gap_init_()
551
'PermList([2, 3, 1, 5, 4])'
552
"""
553
return 'PermList(%s)'%self._gap_list()
554
555
def _repr_(self):
556
"""
557
Return string representation of this permutation.
558
559
EXAMPLES:
560
561
We create the permutation `(1,2,3)(4,5)` and
562
print it. ::
563
564
sage: g = PermutationGroupElement([(1,2,3),(4,5)])
565
sage: g._repr_()
566
'(1,2,3)(4,5)'
567
568
Permutation group elements support renaming them so they print
569
however you want, as illustrate below::
570
571
sage: g.rename('sigma')
572
sage: g
573
sigma
574
sage: g.rename()
575
sage: g
576
(1,2,3)(4,5)
577
"""
578
return self.cycle_string()
579
580
def _latex_(self):
581
r"""
582
Returns a latex representation of this permutation.
583
584
EXAMPLES::
585
586
sage: g = PermutationGroupElement([(1,2,3),(4,5)])
587
sage: latex(g)
588
(1,2,3)(4,5)
589
590
sage: S = SymmetricGroup(['a', 'b'])
591
sage: latex(S.gens())
592
\left[(\verb|a|,\verb|b|)\right]
593
"""
594
from sage.misc.latex import latex
595
return "".join(["(" + ",".join([latex(x) for x in cycle])+")" for cycle in self.cycle_tuples()])
596
597
def __getitem__(self, i):
598
"""
599
Return the ith permutation cycle in the disjoint cycle
600
representation of self.
601
602
INPUT:
603
604
605
- ``i`` - integer
606
607
608
OUTPUT: a permutation group element
609
610
EXAMPLE::
611
612
sage: G = PermutationGroup([[(1,2,3),(4,5)]],5)
613
sage: g = G.gen(0)
614
sage: g[0]
615
(1,2,3)
616
sage: g[1]
617
(4,5)
618
"""
619
return self.cycles()[i]
620
621
def __cmp__(PermutationGroupElement self, PermutationGroupElement right):
622
"""
623
Compare group elements self and right.
624
625
EXAMPLES::
626
627
sage: G = PermutationGroup([[(3,4)], [(1,2,3),(4,5)]])
628
sage: G.gen(0) != G.gen(1)
629
True
630
sage: G.gen(0) == G.gen(1)
631
False
632
633
Permutations are ordered left lexicographically on their
634
associated 'lists'; thus in the symmetric group S(5), the
635
permutation (1,2)(3,4), which corresponds to the list
636
[2,1,4,3,5], is larger than (1,2), which corresponds to the
637
list [2,1,3,4,5].
638
639
::
640
641
sage: S = SymmetricGroup(5)
642
sage: S("(1,2)(3,4)") < S("(1,2)")
643
False
644
sage: S("(1,2)(3,4)") > S("(1,2)")
645
True
646
647
TESTS:
648
649
Verify that we fixed bug #5537::
650
651
sage: h = PermutationGroupElement('(1,3,2)')
652
sage: k = PermutationGroupElement('(1,2,3),(4,5)')
653
sage: k^2 == h, h == k^2
654
(True, True)
655
sage: k^6 == PermutationGroupElement('()')
656
True
657
"""
658
cdef int i
659
cdef int swap = 1
660
if right.n < self.n:
661
self, right = right, self
662
swap = -1
663
for i from 0 <= i < self.n:
664
if self.perm[i] < right.perm[i]:
665
return -swap
666
elif self.perm[i] > right.perm[i]:
667
return swap
668
for i from self.n <= i < right.n:
669
if i < right.perm[i]:
670
return -swap
671
elif i > right.perm[i]:
672
return swap
673
return 0
674
675
def __call__(self, i):
676
"""
677
Returns the image of the integer i under this permutation.
678
Alternately, if i is a list, tuple or string, returns the result of
679
self acting on i.
680
681
EXAMPLE::
682
683
sage: G = PermutationGroup(['(1,2,3)(4,5)'])
684
sage: G
685
Permutation Group with generators [(1,2,3)(4,5)]
686
sage: g = G.gen(0)
687
sage: g(5)
688
4
689
sage: g('abcde')
690
'bcaed'
691
sage: g([0,1,2,3,4])
692
[1, 2, 0, 4, 3]
693
sage: g(('who','what','when','where','why'))
694
('what', 'when', 'who', 'why', 'where')
695
696
::
697
698
sage: g(x)
699
Traceback (most recent call last):
700
...
701
ValueError: Must be in the domain or a list, tuple or string.
702
sage: g(3/2)
703
Traceback (most recent call last):
704
...
705
ValueError: Must be in the domain or a list, tuple or string.
706
"""
707
to_gap = self._parent._domain_to_gap
708
from_gap = self._parent._domain_from_gap
709
cdef int j
710
711
try:
712
i = to_gap[i]
713
except (KeyError, TypeError):
714
# We currently have to include this to maintain the
715
# current behavior where if you pass in an integer which
716
# is not in the domain of the permutation group, then that
717
# integer itself will be returned.
718
if isinstance(i, (long, int, Integer)):
719
return i
720
721
722
if not isinstance(i,(list,tuple,str)):
723
raise ValueError, "Must be in the domain or a list, tuple or string."
724
725
permuted = [i[self.perm[j]] for j from 0 <= j < self.n]
726
if PY_TYPE_CHECK(i, tuple):
727
permuted = tuple(permuted)
728
elif PY_TYPE_CHECK(i, str):
729
permuted = ''.join(permuted)
730
permuted += i[self.n:]
731
return permuted
732
else:
733
j = i
734
if 1 <= j <= self.n:
735
return from_gap[self.perm[j-1]+1]
736
else:
737
return from_gap[i]
738
739
cpdef list _act_on_list_on_position(self, list x):
740
"""
741
Returns the right action of ``self`` on the list ``x``. This is the
742
action on positions.
743
744
EXAMPLES::
745
746
sage: G = PermutationGroup([[(1,2,3,4,5,6)]])
747
sage: p = G.gen()^2; p
748
(1,3,5)(2,4,6)
749
sage: p._act_on_list_on_position([1,2,3,4,5,6])
750
[3, 4, 5, 6, 1, 2]
751
sage: p._act_on_list_on_position(['a','b','c','d','e','f'])
752
['c', 'd', 'e', 'f', 'a', 'b']
753
sage: p._act_on_list_on_position(['c','d','e','f','a','b'])
754
['e', 'f', 'a', 'b', 'c', 'd']
755
sage: p._act_on_list_on_position([])
756
Traceback (most recent call last):
757
...
758
AssertionError: (1,3,5)(2,4,6) and [] should have the same length
759
sage: p._act_on_list_on_position([1,2,3,4,5,6,7])
760
Traceback (most recent call last):
761
...
762
AssertionError: (1,3,5)(2,4,6) and [1, 2, 3, 4, 5, 6, 7] should have the same length
763
"""
764
assert len(x) == self.n, '%s and %s should have the same length'%(self, x)
765
return [ x[self.perm[i]] for i in range(self.n) ]
766
767
cpdef ClonableIntArray _act_on_array_on_position(self, ClonableIntArray x):
768
"""
769
Returns the right action of ``self`` on the ClonableIntArray
770
``x``. This is the action on positions.
771
772
EXAMPLES::
773
774
sage: from sage.structure.list_clone import IncreasingIntArrays
775
sage: v = IncreasingIntArrays()([1,2,3,4])
776
sage: G = PermutationGroup([[(1,2,3,4)]])
777
sage: id = G.identity()
778
sage: id._act_on_array_on_position(v)
779
[1, 2, 3, 4]
780
"""
781
cdef int i
782
cdef ClonableIntArray y
783
cdef int l = self.n
784
assert x._len == l, '%s and %s should have the same length'%(self, x)
785
y = x.clone()
786
for i in range(l):
787
y._list[i] = x._list[self.perm[i]]
788
y.set_immutable()
789
return y
790
791
cpdef _act_on_(self, x, bint self_on_left):
792
"""
793
Return the right action of self on left.
794
795
For example, if f=left is a polynomial, then this function returns
796
f(sigma\*x), which is image of f under the right action of sigma on
797
the indeterminates. This is a right action since the image of
798
f(sigma\*x) under tau is f(sigma\*tau\*x).
799
800
INPUT:
801
802
803
- ``left`` - element of space on which permutations
804
act from the right
805
806
807
EXAMPLES::
808
809
sage: G = PermutationGroup(['(1,2,3)(4,5)', '(1,2,3,4,5)'])
810
sage: R.<x,y,z,u,v> = PolynomialRing(QQ,5)
811
sage: f = x^2 + y^2 - z^2 + 2*u^2
812
sage: sigma, tau = G.gens()
813
sage: f*sigma
814
-x^2 + y^2 + z^2 + 2*v^2
815
sage: f*tau
816
y^2 + z^2 - u^2 + 2*v^2
817
sage: f*(sigma*tau)
818
2*x^2 - y^2 + z^2 + u^2
819
sage: (f*sigma)*tau
820
2*x^2 - y^2 + z^2 + u^2
821
"""
822
if not self_on_left:
823
left = x
824
if is_Polynomial(left):
825
if self != 1:
826
raise ValueError, "%s does not act on %s"%(self, left.parent())
827
return left
828
elif is_MPolynomial(left):
829
R = left.parent()
830
vars = R.gens()
831
try:
832
sigma_x = [vars[self(i+1)-1] for i in range(R.ngens())]
833
except IndexError:
834
raise TypeError, "%s does not act on %s"%(self, left.parent())
835
return left(tuple(sigma_x))
836
837
cpdef MonoidElement _mul_(left, MonoidElement _right):
838
"""
839
EXAMPLES::
840
841
sage: S = SymmetricGroup(['a', 'b'])
842
sage: s = S([('a', 'b')]); s
843
('a','b')
844
sage: s*s
845
()
846
"""
847
cdef PermutationGroupElement prod = left._new_c()
848
cdef PermutationGroupElement right = <PermutationGroupElement>_right
849
cdef int i
850
for i from 0 <= i < left.n:
851
prod.perm[i] = right.perm[left.perm[i]]
852
return prod
853
854
def __invert__(self):
855
"""
856
Return the inverse of this permutation.
857
858
EXAMPLES::
859
860
sage: g = PermutationGroupElement('(1,2,3)(4,5)')
861
sage: ~g
862
(1,3,2)(4,5)
863
sage: (~g) * g
864
()
865
"""
866
cdef PermutationGroupElement inv = self._new_c()
867
cdef int i
868
for i from 0 <= i < self.n:
869
inv.perm[self.perm[i]] = i
870
return inv
871
872
cpdef _gap_list(self):
873
"""
874
Returns this permutation in list notation compatible with the
875
GAP numbering.
876
877
EXAMPLES::
878
879
sage: S = SymmetricGroup(3)
880
sage: s = S.gen(0); s
881
(1,2,3)
882
sage: s._gap_list()
883
[2, 3, 1]
884
885
::
886
887
sage: S = SymmetricGroup(['a', 'b', 'c'])
888
sage: s = S.gen(0); s
889
('a','b','c')
890
sage: s._gap_list()
891
[2, 3, 1]
892
"""
893
cdef int i
894
return [self.perm[i]+1 for i from 0 <= i < self.n]
895
896
def _gap_cycle_string(self):
897
"""
898
Returns a cycle string for this permutation compatible with
899
the GAP numbering.
900
901
EXAMPLES::
902
903
sage: S = SymmetricGroup(3)
904
sage: s = S.gen(0); s
905
(1,2,3)
906
sage: s._gap_cycle_string()
907
'(1,2,3)'
908
909
::
910
911
sage: S = SymmetricGroup(['a', 'b', 'c'])
912
sage: s = S.gen(0); s
913
('a','b','c')
914
sage: s._gap_cycle_string()
915
'(1,2,3)'
916
"""
917
from sage.combinat.permutation import Permutation
918
return Permutation(self._gap_list()).cycle_string()
919
920
cpdef list(self):
921
"""
922
Returns list of the images of the integers from 1 to n under this
923
permutation as a list of Python ints.
924
925
EXAMPLES::
926
927
sage: G = SymmetricGroup(4)
928
sage: x = G([2,1,4,3]); x
929
(1,2)(3,4)
930
sage: v = x.list(); v
931
[2, 1, 4, 3]
932
sage: type(v[0])
933
<type 'int'>
934
sage: x = G([2,1]); x
935
(1,2)
936
sage: x.list()
937
[2, 1, 3, 4]
938
939
TESTS::
940
941
sage: S = SymmetricGroup(0)
942
sage: x = S.one(); x
943
()
944
sage: x.list()
945
[]
946
"""
947
cdef int i
948
949
#We need to do this to handle the case of SymmetricGroup(0)
950
#where the domain is (), but the permutation group element has
951
#an underlying representation of [1]. The 1 doesn't
952
#correspond to anything in the domain
953
if len(self._parent._domain) == 0:
954
return []
955
else:
956
from_gap = self._parent._domain_from_gap
957
return [from_gap[self.perm[i]+1] for i from 0 <= i < self.n]
958
959
def __hash__(self):
960
"""
961
Return hash of this permutation.
962
963
EXAMPLES::
964
965
sage: G = SymmetricGroup(5)
966
sage: s = G([2,1,5,3,4])
967
sage: s.tuple()
968
(2, 1, 5, 3, 4)
969
sage: hash(s)
970
1592966088 # 32-bit
971
2865702456085625800 # 64-bit
972
sage: hash(s.tuple())
973
1592966088 # 32-bit
974
2865702456085625800 # 64-bit
975
"""
976
return hash(tuple(self._gap_list()))
977
978
def tuple(self):
979
"""
980
Return tuple of images of the domain under self.
981
982
EXAMPLES::
983
984
sage: G = SymmetricGroup(5)
985
sage: s = G([2,1,5,3,4])
986
sage: s.tuple()
987
(2, 1, 5, 3, 4)
988
989
sage: S = SymmetricGroup(['a', 'b'])
990
sage: S.gen().tuple()
991
('b', 'a')
992
"""
993
if self.__tuple is None:
994
self.__tuple = tuple(self.list())
995
return self.__tuple
996
997
def dict(self):
998
"""
999
Returns list of the images of the integers from 1 to n under this
1000
permutation as a list of Python ints.
1001
1002
.. note::
1003
1004
:meth:`.list` returns a zero-indexed list. :meth:`.dict`
1005
return the actual mapping of the permutation, which will be
1006
indexed starting with 1.
1007
1008
EXAMPLES::
1009
1010
sage: G = SymmetricGroup(4)
1011
sage: g = G((1,2,3,4)); g
1012
(1,2,3,4)
1013
sage: v = g.dict(); v
1014
{1: 2, 2: 3, 3: 4, 4: 1}
1015
sage: type(v[1])
1016
<type 'int'>
1017
sage: x = G([2,1]); x
1018
(1,2)
1019
sage: x.dict()
1020
{1: 2, 2: 1, 3: 3, 4: 4}
1021
"""
1022
from_gap = self._parent._domain_from_gap
1023
cdef int i
1024
u = {}
1025
for i from 0 <= i < self.n:
1026
u[i+1] = from_gap[self.perm[i]+1]
1027
return u
1028
1029
def order(self):
1030
"""
1031
Return the order of this group element, which is the smallest
1032
positive integer `n` for which `g^n = 1`.
1033
1034
EXAMPLES::
1035
1036
sage: s = PermutationGroupElement('(1,2)(3,5,6)')
1037
sage: s.order()
1038
6
1039
1040
TESTS::
1041
1042
sage: prod(primes(150))
1043
1492182350939279320058875736615841068547583863326864530410
1044
sage: L = [tuple(range(sum(primes(p))+1, sum(primes(p))+1+p)) for p in primes(150)]
1045
sage: PermutationGroupElement(L).order()
1046
1492182350939279320058875736615841068547583863326864530410
1047
"""
1048
order = None
1049
cdef long long order_c = 1
1050
cdef int cycle_len
1051
cdef int i, k
1052
cdef bint* seen = <bint *>sage_malloc(sizeof(bint) * self.n)
1053
for i from 0 <= i < self.n: seen[i] = 0
1054
for i from 0 <= i < self.n:
1055
if seen[i] or self.perm[i] == i:
1056
continue
1057
k = self.perm[i]
1058
cycle_len = 1
1059
while k != i:
1060
seen[k] = 1
1061
k = self.perm[k]
1062
cycle_len += 1
1063
if order is not None:
1064
order = order.lcm(cycle_len)
1065
else:
1066
order_c = (order_c * cycle_len) / arith.c_gcd_longlong(order_c, cycle_len)
1067
if order_c > LONG_LONG_MAX / (self.n - i):
1068
order = Integer(order_c)
1069
sage_free(seen)
1070
return int(order_c) if order is None else order
1071
1072
def inverse(self):
1073
r"""
1074
Returns the inverse permutation.
1075
1076
OUTPUT:
1077
1078
For an element of a permutation group, this method returns the inverse
1079
element, which is both the inverse function and the inverse as an
1080
element of a group.
1081
1082
EXAMPLES::
1083
1084
sage: s = PermutationGroupElement("(1,2,3)(4,5)")
1085
sage: s.inverse()
1086
(1,3,2)(4,5)
1087
1088
sage: A = AlternatingGroup(4)
1089
sage: t = A("(1,2,3)")
1090
sage: t.inverse()
1091
(1,3,2)
1092
1093
There are several ways (syntactically) to get an inverse
1094
of a permutation group element. ::
1095
1096
sage: s = PermutationGroupElement("(1,2,3,4)(6,7,8)")
1097
sage: s.inverse() == s^-1
1098
True
1099
sage: s.inverse() == ~s
1100
True
1101
"""
1102
return ~self
1103
1104
def sign(self):
1105
"""
1106
Returns the sign of self, which is `(-1)^{s}`, where
1107
`s` is the number of swaps.
1108
1109
EXAMPLES::
1110
1111
sage: s = PermutationGroupElement('(1,2)(3,5,6)')
1112
sage: s.sign()
1113
-1
1114
1115
ALGORITHM: Only even cycles contribute to the sign, thus
1116
1117
.. math::
1118
1119
sign(sigma) = (-1)^{\sum_c len(c)-1}
1120
1121
1122
where the sum is over cycles in self.
1123
"""
1124
cdef int cycle_len_sum = 0
1125
cdef int i, k
1126
cdef bint* seen = <bint *>sage_malloc(sizeof(bint) * self.n)
1127
for i from 0 <= i < self.n: seen[i] = 0
1128
for i from 0 <= i < self.n:
1129
if seen[i] or self.perm[i] == i:
1130
continue
1131
k = self.perm[i]
1132
while k != i:
1133
seen[k] = 1
1134
k = self.perm[k]
1135
cycle_len_sum += 1
1136
sage_free(seen)
1137
return 1 - 2*(cycle_len_sum % 2) # == (-1)^cycle_len
1138
1139
1140
def orbit(self, n, bint sorted=True):
1141
"""
1142
Returns the orbit of the integer `n` under this group
1143
element, as a sorted list.
1144
1145
EXAMPLES::
1146
1147
sage: G = PermutationGroup(['(1,2,3)(4,5)'])
1148
sage: g = G.gen(0)
1149
sage: g.orbit(4)
1150
[4, 5]
1151
sage: g.orbit(3)
1152
[1, 2, 3]
1153
sage: g.orbit(10)
1154
[10]
1155
1156
::
1157
1158
sage: s = SymmetricGroup(['a', 'b']).gen(0); s
1159
('a','b')
1160
sage: s.orbit('a')
1161
['a', 'b']
1162
"""
1163
to_gap = self._parent._domain_to_gap
1164
from_gap = self._parent._domain_from_gap
1165
try:
1166
n = to_gap[n]
1167
except KeyError:
1168
return [n]
1169
1170
cdef int i = n
1171
cdef int start = i
1172
if 1 <= i <= self.n:
1173
L = [from_gap[i]]
1174
i = self.perm[i-1]+1
1175
while i != start:
1176
PyList_Append(L,from_gap[i])
1177
i = self.perm[i-1]+1
1178
if sorted:
1179
L.sort()
1180
return L
1181
else:
1182
return from_gap[n]
1183
1184
def cycles(self):
1185
"""
1186
Return self as a list of disjoint cycles.
1187
1188
EXAMPLES::
1189
1190
sage: G = PermutationGroup(['(1,2,3)(4,5,6,7)'])
1191
sage: g = G.0
1192
sage: g.cycles()
1193
[(1,2,3), (4,5,6,7)]
1194
sage: a, b = g.cycles()
1195
sage: a(1), b(1)
1196
(2, 1)
1197
"""
1198
L = []
1199
cdef PermutationGroupElement cycle
1200
cdef int i, j, k, next_k
1201
cdef bint* seen = <bint *>sage_malloc(sizeof(bint) * self.n)
1202
for i from 0 <= i < self.n: seen[i] = 0
1203
for i from 0 <= i < self.n:
1204
if seen[i] or self.perm[i] == i:
1205
continue
1206
cycle = self._new_c()
1207
for j from 0 <= j < self.n: cycle.perm[j] = j
1208
k = cycle.perm[i] = self.perm[i]
1209
while k != i:
1210
seen[k] = 1
1211
next_k = cycle.perm[k] = self.perm[k]
1212
k = next_k
1213
PyList_Append(L, cycle)
1214
sage_free(seen)
1215
return L
1216
1217
def cycle_tuples(self, singletons=False):
1218
"""
1219
Return self as a list of disjoint cycles, represented as tuples
1220
rather than permutation group elements.
1221
1222
INPUT:
1223
1224
- ``singletons`` - boolean (default: False) whether or not consider the
1225
cycle that correspond to fixed point
1226
1227
EXAMPLES::
1228
1229
sage: p = PermutationGroupElement('(2,6)(4,5,1)')
1230
sage: p.cycle_tuples()
1231
[(1, 4, 5), (2, 6)]
1232
sage: p.cycle_tuples(singletons=True)
1233
[(1, 4, 5), (2, 6), (3,)]
1234
1235
EXAMPLES::
1236
1237
sage: S = SymmetricGroup(4)
1238
sage: S.gen(0).cycle_tuples()
1239
[(1, 2, 3, 4)]
1240
1241
::
1242
1243
sage: S = SymmetricGroup(['a','b','c','d'])
1244
sage: S.gen(0).cycle_tuples()
1245
[('a', 'b', 'c', 'd')]
1246
sage: S([('a', 'b'), ('c', 'd')]).cycle_tuples()
1247
[('a', 'b'), ('c', 'd')]
1248
"""
1249
from_gap = self._parent._domain_from_gap
1250
L = []
1251
cdef int i, k
1252
cdef bint* seen = <bint *>sage_malloc(sizeof(bint) * self.n)
1253
for i from 0 <= i < self.n: seen[i] = 0
1254
for i from 0 <= i < self.n:
1255
if seen[i]:
1256
continue
1257
if self.perm[i] == i:
1258
if singletons:
1259
PyList_Append(L, (from_gap[i+1],))
1260
# it is not necessary to put seen[i] to 1 as we will never
1261
# see i again
1262
else:
1263
continue
1264
else:
1265
cycle = [from_gap[i+1]]
1266
k = self.perm[i]
1267
while k != i:
1268
PyList_Append(cycle, from_gap[k+1])
1269
seen[k] = 1
1270
k = self.perm[k]
1271
PyList_Append(L, tuple(cycle))
1272
sage_free(seen)
1273
return L
1274
1275
def cycle_string(self, singletons=False):
1276
"""
1277
Return string representation of this permutation.
1278
1279
EXAMPLES::
1280
1281
sage: g = PermutationGroupElement([(1,2,3),(4,5)])
1282
sage: g.cycle_string()
1283
'(1,2,3)(4,5)'
1284
1285
sage: g = PermutationGroupElement([3,2,1])
1286
sage: g.cycle_string(singletons=True)
1287
'(1,3)(2)'
1288
"""
1289
cycles = self.cycle_tuples(singletons)
1290
if len(cycles) == 0:
1291
return '()'
1292
return ''.join([repr(c) for c in cycles]).replace(', ',',').replace(',)',')')
1293
1294
def has_descent(self, i, side = "right", positive = False):
1295
"""
1296
INPUT:
1297
1298
- ``i``: an element of the index set
1299
- ``side``: "left" or "right" (default: "right")
1300
- ``positive``: a boolean (default: False)
1301
1302
Returns whether ``self`` has a left (resp. right) descent at
1303
position ``i``. If ``positive`` is True, then test for a non
1304
descent instead.
1305
1306
Beware that, since permutations are acting on the right, the
1307
meaning of descents is the reverse of the usual
1308
convention. Hence, ``self`` has a left descent at position
1309
``i`` if ``self(i) > self(i+1)``.
1310
1311
EXAMPLES::
1312
1313
sage: S = SymmetricGroup([1,2,3])
1314
sage: S.one().has_descent(1)
1315
False
1316
sage: S.one().has_descent(2)
1317
False
1318
sage: s = S.simple_reflections()
1319
sage: x = s[1]*s[2]
1320
sage: x.has_descent(1, side = "right")
1321
False
1322
sage: x.has_descent(2, side = "right")
1323
True
1324
sage: x.has_descent(1, side = "left")
1325
True
1326
sage: x.has_descent(2, side = "left")
1327
False
1328
sage: S._test_has_descent()
1329
1330
The symmetric group acting on a set not of the form
1331
`(1,\dots,n)` is also supported::
1332
1333
sage: S = SymmetricGroup([2,4,1])
1334
sage: s = S.simple_reflections()
1335
sage: x = s[2]*s[4]
1336
sage: x.has_descent(4)
1337
True
1338
sage: S._test_has_descent()
1339
"""
1340
to_gap = self._parent._domain_to_gap
1341
from_gap = self._parent._domain_from_gap
1342
if side == "right":
1343
self = ~self
1344
1345
try:
1346
i1 = from_gap[to_gap[i]+1]
1347
except KeyError:
1348
return False
1349
1350
return (to_gap[self(i)] > to_gap[self(i1)]) is not positive
1351
1352
def matrix(self):
1353
"""
1354
Returns deg x deg permutation matrix associated to the permutation
1355
self
1356
1357
EXAMPLES::
1358
1359
sage: G = PermutationGroup(['(1,2,3)(4,5)'])
1360
sage: g = G.gen(0)
1361
sage: g.matrix()
1362
[0 1 0 0 0]
1363
[0 0 1 0 0]
1364
[1 0 0 0 0]
1365
[0 0 0 0 1]
1366
[0 0 0 1 0]
1367
"""
1368
M = MatrixSpace(ZZ, self.n, self.n, sparse=True)
1369
cdef int i
1370
entries = {}
1371
for i from 0 <= i < self.n:
1372
entries[i, self.perm[i]] = 1
1373
return M(entries)
1374
1375
def word_problem(self, words, display=True):
1376
"""
1377
G and H are permutation groups, g in G, H is a subgroup of G
1378
generated by a list (words) of elements of G. If g is in H, return
1379
the expression for g as a word in the elements of (words).
1380
1381
This function does not solve the word problem in Sage. Rather it
1382
pushes it over to GAP, which has optimized algorithms for the word
1383
problem. Essentially, this function is a wrapper for the GAP
1384
functions "EpimorphismFromFreeGroup" and
1385
"PreImagesRepresentative".
1386
1387
EXAMPLE::
1388
1389
sage: G = PermutationGroup([[(1,2,3),(4,5)],[(3,4)]], canonicalize=False)
1390
sage: g1, g2 = G.gens()
1391
sage: h = g1^2*g2*g1
1392
sage: h.word_problem([g1,g2], False)
1393
('x1^2*x2^-1*x1', '(1,2,3)(4,5)^2*(3,4)^-1*(1,2,3)(4,5)')
1394
sage: h.word_problem([g1,g2])
1395
x1^2*x2^-1*x1
1396
[['(1,2,3)(4,5)', 2], ['(3,4)', -1], ['(1,2,3)(4,5)', 1]]
1397
('x1^2*x2^-1*x1', '(1,2,3)(4,5)^2*(3,4)^-1*(1,2,3)(4,5)')
1398
"""
1399
if not self._parent._has_natural_domain():
1400
raise NotImplementedError
1401
1402
import copy
1403
from sage.groups.perm_gps.permgroup import PermutationGroup
1404
from sage.interfaces.all import gap
1405
1406
G = gap(words[0].parent())
1407
g = words[0].parent()(self)
1408
H = gap.Group(words)
1409
ans = G.EpimorphismFromFreeGroup().PreImagesRepresentative(g)
1410
1411
l1 = str(ans)
1412
l2 = copy.copy(l1)
1413
l4 = []
1414
l3 = l1.split("*")
1415
for i in range(1,len(words)+1):
1416
l2 = l2.replace("x"+str(i),str(words[i-1]))
1417
1418
if display:
1419
for i in range(len(l3)): ## parsing the word for display
1420
if len(l3[i].split("^"))==2:
1421
l4.append([l3[i].split("^")[0],int(l3[i].split("^")[1])])
1422
if len(l3[i].split("^"))==1:
1423
l4.append([l3[i].split("^")[0],1])
1424
l5 = copy.copy(l4)
1425
for i in range(len(l4)):
1426
for j in range(1,len(words)+1):
1427
l5[i][0] = l5[i][0].replace("x"+str(j),str(words[j-1]))
1428
print " ",l1
1429
print " ",l5
1430
return l1,l2
1431
1432
1433
1434
1435
cdef bint is_valid_permutation(int* perm, int n):
1436
"""
1437
This is used in the __init__ method.
1438
1439
Returns True iff the first n elements of perm are literally a
1440
permutation of [0, ..., n-1].
1441
1442
TESTS::
1443
1444
sage: S = SymmetricGroup(10)
1445
sage: PermutationGroupElement([2,1],S,check=False)
1446
(1,2)
1447
sage: PermutationGroupElement([1,1],S,check=False)
1448
Traceback (most recent call last):
1449
...
1450
ValueError: Invalid permutation vector: [1, 1]
1451
sage: PermutationGroupElement([1,-1],S,check=False)
1452
Traceback (most recent call last):
1453
...
1454
ValueError: Invalid permutation vector: [1, -1]
1455
sage: PermutationGroupElement([1,2,3,10],S,check=False)
1456
Traceback (most recent call last):
1457
...
1458
ValueError: Invalid permutation vector: [1, 2, 3, 10]
1459
"""
1460
cdef int i, ix
1461
# make everything is in bounds
1462
for i from 0 <= i < n:
1463
if not 0 <= perm[i] < n:
1464
return False
1465
# mark hit points by sign
1466
for i from 0 <= i < n:
1467
ix = -1-perm[i] if perm[i] < 0 else perm[i]
1468
perm[ix] = -1-perm[ix]
1469
# make sure everything is hit once, and reset signs
1470
for i from 0 <= i < n:
1471
if perm[i] >= 0:
1472
return False
1473
perm[i] = -1-perm[i]
1474
1475
return True
1476
1477
1478