Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/groups/perm_gps/permgroup_element.pyx
8815 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.old as group
60
61
include "sage/ext/stdsage.pxi"
62
include "sage/ext/interrupt.pxi"
63
from cpython.list cimport *
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
from sage.misc.superseded import deprecated_function_alias
71
72
import operator
73
74
from sage.rings.fast_arith cimport arith_llong
75
cdef arith_llong arith = arith_llong()
76
cdef extern from *:
77
long long LONG_LONG_MAX
78
79
#import permgroup_named
80
81
def make_permgroup_element(G, x):
82
"""
83
Returns a PermutationGroupElement given the permutation group
84
``G`` and the permutation ``x`` in list notation.
85
86
This is function is used when unpickling old (pre-domain) versions
87
of permutation groups and their elements. This now does a bit of
88
processing and calls :func:`make_permgroup_element_v2` which is
89
used in unpickling the current PermutationGroupElements.
90
91
EXAMPLES::
92
93
sage: from sage.groups.perm_gps.permgroup_element import make_permgroup_element
94
sage: S = SymmetricGroup(3)
95
sage: make_permgroup_element(S, [1,3,2])
96
(2,3)
97
"""
98
domain = FiniteEnumeratedSet(range(1, len(x)+1))
99
return make_permgroup_element_v2(G, x, domain)
100
101
def make_permgroup_element_v2(G, x, domain):
102
"""
103
Returns a PermutationGroupElement given the permutation group
104
``G``, the permutation ``x`` in list notation, and the domain
105
``domain`` of the permutation group.
106
107
This is function is used when unpickling permutation groups and
108
their elements.
109
110
EXAMPLES::
111
112
sage: from sage.groups.perm_gps.permgroup_element import make_permgroup_element_v2
113
sage: S = SymmetricGroup(3)
114
sage: make_permgroup_element_v2(S, [1,3,2], S.domain())
115
(2,3)
116
"""
117
# Note that it has to be in-sync with the __init__ method of
118
# PermutationGroup_generic since the elements have to be created
119
# before the PermutationGroup_generic is initialized. The
120
# constructor for PermutationGroupElement requires that
121
# G._domain_to_gap be set.
122
G._domain = domain
123
G._deg = len(domain)
124
G._domain_to_gap = dict([(key, i+1) for i, key in enumerate(domain)])
125
G._domain_from_gap = dict([(i+1, key) for i, key in enumerate(domain)])
126
return G(x, check=False)
127
128
129
def is_PermutationGroupElement(x):
130
"""
131
Returns True if ``x`` is a PermutationGroupElement.
132
133
EXAMPLES::
134
135
sage: p = PermutationGroupElement([(1,2),(3,4,5)])
136
sage: from sage.groups.perm_gps.permgroup_element import is_PermutationGroupElement
137
sage: is_PermutationGroupElement(p)
138
True
139
"""
140
return isinstance(x, PermutationGroupElement)
141
142
def string_to_tuples(g):
143
"""
144
EXAMPLES::
145
146
sage: from sage.groups.perm_gps.permgroup_element import string_to_tuples
147
sage: string_to_tuples('(1,2,3)')
148
[(1, 2, 3)]
149
sage: string_to_tuples('(1,2,3)(4,5)')
150
[(1, 2, 3), (4, 5)]
151
sage: string_to_tuples(' (1,2, 3) (4,5)')
152
[(1, 2, 3), (4, 5)]
153
sage: string_to_tuples('(1,2)(3)')
154
[(1, 2), (3,)]
155
"""
156
from sage.misc.all import sage_eval
157
158
if not isinstance(g, str):
159
raise ValueError, "g (= %s) must be a string"%g
160
elif g == '()':
161
return []
162
g = g.replace('\n','').replace(' ', '').replace(')(', '),(').replace(')', ',)')
163
g = '[' + g + ']'
164
return sage_eval(g, preparse=False)
165
166
def standardize_generator(g, convert_dict=None):
167
"""
168
Standardizes the input for permutation group elements to a list of
169
tuples. This was factored out of the
170
PermutationGroupElement.__init__ since
171
PermutationGroup_generic.__init__ needs to do the same computation
172
in order to compute the domain of a group when it's not explicitly
173
specified.
174
175
INPUT:
176
177
- ``g`` - a list, tuple, string, GapElement,
178
PermutationGroupElement, Permutation
179
180
- ``convert_dict`` - (optional) a dictionary used to convert the
181
points to a number compatible with GAP.
182
183
OUTPUT:
184
185
The permutation in as a list of cycles.
186
187
EXAMPLES::
188
189
sage: from sage.groups.perm_gps.permgroup_element import standardize_generator
190
sage: standardize_generator('(1,2)')
191
[(1, 2)]
192
193
sage: p = PermutationGroupElement([(1,2)])
194
sage: standardize_generator(p)
195
[(1, 2)]
196
sage: standardize_generator(p._gap_())
197
[(1, 2)]
198
sage: standardize_generator((1,2))
199
[(1, 2)]
200
sage: standardize_generator([(1,2)])
201
[(1, 2)]
202
sage: standardize_generator(Permutation([2,1,3]))
203
[(1, 2), (3,)]
204
205
::
206
207
sage: d = {'a': 1, 'b': 2}
208
sage: p = SymmetricGroup(['a', 'b']).gen(0); p
209
('a','b')
210
sage: standardize_generator(p, convert_dict=d)
211
[(1, 2)]
212
sage: standardize_generator(p._gap_(), convert_dict=d)
213
[(1, 2)]
214
sage: standardize_generator(('a','b'), convert_dict=d)
215
[(1, 2)]
216
sage: standardize_generator([('a','b')], convert_dict=d)
217
[(1, 2)]
218
"""
219
from sage.interfaces.gap import GapElement
220
from sage.combinat.permutation import Permutation
221
from sage.libs.pari.all import pari_gen
222
223
if isinstance(g, pari_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):
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.domain(), 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[(\text{\texttt{a}},\text{\texttt{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_demo 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
list = deprecated_function_alias(14319, domain)
921
922
cpdef domain(self):
923
"""
924
Returns the domain of self.
925
926
EXAMPLES::
927
928
sage: G = SymmetricGroup(4)
929
sage: x = G([2,1,4,3]); x
930
(1,2)(3,4)
931
sage: v = x.domain(); v
932
[2, 1, 4, 3]
933
sage: type(v[0])
934
<type 'int'>
935
sage: x = G([2,1]); x
936
(1,2)
937
sage: x.domain()
938
[2, 1, 3, 4]
939
940
TESTS::
941
942
sage: S = SymmetricGroup(0)
943
sage: x = S.one(); x
944
()
945
sage: x.domain()
946
[]
947
"""
948
cdef int i
949
950
#We need to do this to handle the case of SymmetricGroup(0)
951
#where the domain is (), but the permutation group element has
952
#an underlying representation of [1]. The 1 doesn't
953
#correspond to anything in the domain
954
if len(self._parent._domain) == 0:
955
return []
956
else:
957
from_gap = self._parent._domain_from_gap
958
return [from_gap[self.perm[i]+1] for i from 0 <= i < self.n]
959
960
def __hash__(self):
961
"""
962
Return hash of this permutation.
963
964
EXAMPLES::
965
966
sage: G = SymmetricGroup(5)
967
sage: s = G([2,1,5,3,4])
968
sage: s.tuple()
969
(2, 1, 5, 3, 4)
970
sage: hash(s)
971
1592966088 # 32-bit
972
2865702456085625800 # 64-bit
973
sage: hash(s.tuple())
974
1592966088 # 32-bit
975
2865702456085625800 # 64-bit
976
"""
977
return hash(tuple(self._gap_list()))
978
979
def tuple(self):
980
"""
981
Return tuple of images of the domain under self.
982
983
EXAMPLES::
984
985
sage: G = SymmetricGroup(5)
986
sage: s = G([2,1,5,3,4])
987
sage: s.tuple()
988
(2, 1, 5, 3, 4)
989
990
sage: S = SymmetricGroup(['a', 'b'])
991
sage: S.gen().tuple()
992
('b', 'a')
993
"""
994
if self.__tuple is None:
995
self.__tuple = tuple(self.domain())
996
return self.__tuple
997
998
def dict(self):
999
"""
1000
Returns a dictionary associating each element of the domain with its
1001
image.
1002
1003
EXAMPLES::
1004
1005
sage: G = SymmetricGroup(4)
1006
sage: g = G((1,2,3,4)); g
1007
(1,2,3,4)
1008
sage: v = g.dict(); v
1009
{1: 2, 2: 3, 3: 4, 4: 1}
1010
sage: type(v[1])
1011
<type 'int'>
1012
sage: x = G([2,1]); x
1013
(1,2)
1014
sage: x.dict()
1015
{1: 2, 2: 1, 3: 3, 4: 4}
1016
"""
1017
from_gap = self._parent._domain_from_gap
1018
to_gap = self._parent._domain_to_gap
1019
cdef int i
1020
return {e:from_gap[self.perm[i-1]+1] for e,i in to_gap.iteritems()}
1021
1022
def order(self):
1023
"""
1024
Return the order of this group element, which is the smallest
1025
positive integer `n` for which `g^n = 1`.
1026
1027
EXAMPLES::
1028
1029
sage: s = PermutationGroupElement('(1,2)(3,5,6)')
1030
sage: s.order()
1031
6
1032
1033
TESTS::
1034
1035
sage: prod(primes(150))
1036
1492182350939279320058875736615841068547583863326864530410
1037
sage: L = [tuple(range(sum(primes(p))+1, sum(primes(p))+1+p)) for p in primes(150)]
1038
sage: PermutationGroupElement(L).order()
1039
1492182350939279320058875736615841068547583863326864530410
1040
"""
1041
order = None
1042
cdef long long order_c = 1
1043
cdef int cycle_len
1044
cdef int i, k
1045
cdef bint* seen = <bint *>sage_malloc(sizeof(bint) * self.n)
1046
for i from 0 <= i < self.n: seen[i] = 0
1047
for i from 0 <= i < self.n:
1048
if seen[i] or self.perm[i] == i:
1049
continue
1050
k = self.perm[i]
1051
cycle_len = 1
1052
while k != i:
1053
seen[k] = 1
1054
k = self.perm[k]
1055
cycle_len += 1
1056
if order is not None:
1057
order = order.lcm(cycle_len)
1058
else:
1059
order_c = (order_c * cycle_len) / arith.c_gcd_longlong(order_c, cycle_len)
1060
if order_c > LONG_LONG_MAX / (self.n - i):
1061
order = Integer(order_c)
1062
sage_free(seen)
1063
return int(order_c) if order is None else order
1064
1065
def inverse(self):
1066
r"""
1067
Returns the inverse permutation.
1068
1069
OUTPUT:
1070
1071
For an element of a permutation group, this method returns the inverse
1072
element, which is both the inverse function and the inverse as an
1073
element of a group.
1074
1075
EXAMPLES::
1076
1077
sage: s = PermutationGroupElement("(1,2,3)(4,5)")
1078
sage: s.inverse()
1079
(1,3,2)(4,5)
1080
1081
sage: A = AlternatingGroup(4)
1082
sage: t = A("(1,2,3)")
1083
sage: t.inverse()
1084
(1,3,2)
1085
1086
There are several ways (syntactically) to get an inverse
1087
of a permutation group element. ::
1088
1089
sage: s = PermutationGroupElement("(1,2,3,4)(6,7,8)")
1090
sage: s.inverse() == s^-1
1091
True
1092
sage: s.inverse() == ~s
1093
True
1094
"""
1095
return ~self
1096
1097
def sign(self):
1098
"""
1099
Returns the sign of self, which is `(-1)^{s}`, where
1100
`s` is the number of swaps.
1101
1102
EXAMPLES::
1103
1104
sage: s = PermutationGroupElement('(1,2)(3,5,6)')
1105
sage: s.sign()
1106
-1
1107
1108
ALGORITHM: Only even cycles contribute to the sign, thus
1109
1110
.. math::
1111
1112
sign(sigma) = (-1)^{\sum_c len(c)-1}
1113
1114
1115
where the sum is over cycles in self.
1116
"""
1117
cdef int cycle_len_sum = 0
1118
cdef int i, k
1119
cdef bint* seen = <bint *>sage_malloc(sizeof(bint) * self.n)
1120
for i from 0 <= i < self.n: seen[i] = 0
1121
for i from 0 <= i < self.n:
1122
if seen[i] or self.perm[i] == i:
1123
continue
1124
k = self.perm[i]
1125
while k != i:
1126
seen[k] = 1
1127
k = self.perm[k]
1128
cycle_len_sum += 1
1129
sage_free(seen)
1130
return 1 - 2*(cycle_len_sum % 2) # == (-1)^cycle_len
1131
1132
1133
def orbit(self, n, bint sorted=True):
1134
"""
1135
Returns the orbit of the integer `n` under this group
1136
element, as a sorted list.
1137
1138
EXAMPLES::
1139
1140
sage: G = PermutationGroup(['(1,2,3)(4,5)'])
1141
sage: g = G.gen(0)
1142
sage: g.orbit(4)
1143
[4, 5]
1144
sage: g.orbit(3)
1145
[1, 2, 3]
1146
sage: g.orbit(10)
1147
[10]
1148
1149
::
1150
1151
sage: s = SymmetricGroup(['a', 'b']).gen(0); s
1152
('a','b')
1153
sage: s.orbit('a')
1154
['a', 'b']
1155
"""
1156
to_gap = self._parent._domain_to_gap
1157
from_gap = self._parent._domain_from_gap
1158
try:
1159
n = to_gap[n]
1160
except KeyError:
1161
return [n]
1162
1163
cdef int i = n
1164
cdef int start = i
1165
if 1 <= i <= self.n:
1166
L = [from_gap[i]]
1167
i = self.perm[i-1]+1
1168
while i != start:
1169
PyList_Append(L,from_gap[i])
1170
i = self.perm[i-1]+1
1171
if sorted:
1172
L.sort()
1173
return L
1174
else:
1175
return from_gap[n]
1176
1177
def cycles(self):
1178
"""
1179
Return self as a list of disjoint cycles.
1180
1181
EXAMPLES::
1182
1183
sage: G = PermutationGroup(['(1,2,3)(4,5,6,7)'])
1184
sage: g = G.0
1185
sage: g.cycles()
1186
[(1,2,3), (4,5,6,7)]
1187
sage: a, b = g.cycles()
1188
sage: a(1), b(1)
1189
(2, 1)
1190
"""
1191
L = []
1192
cdef PermutationGroupElement cycle
1193
cdef int i, j, k, next_k
1194
cdef bint* seen = <bint *>sage_malloc(sizeof(bint) * self.n)
1195
for i from 0 <= i < self.n: seen[i] = 0
1196
for i from 0 <= i < self.n:
1197
if seen[i] or self.perm[i] == i:
1198
continue
1199
cycle = self._new_c()
1200
for j from 0 <= j < self.n: cycle.perm[j] = j
1201
k = cycle.perm[i] = self.perm[i]
1202
while k != i:
1203
seen[k] = 1
1204
next_k = cycle.perm[k] = self.perm[k]
1205
k = next_k
1206
PyList_Append(L, cycle)
1207
sage_free(seen)
1208
return L
1209
1210
def cycle_tuples(self, singletons=False):
1211
"""
1212
Return self as a list of disjoint cycles, represented as tuples
1213
rather than permutation group elements.
1214
1215
INPUT:
1216
1217
- ``singletons`` - boolean (default: False) whether or not consider the
1218
cycle that correspond to fixed point
1219
1220
EXAMPLES::
1221
1222
sage: p = PermutationGroupElement('(2,6)(4,5,1)')
1223
sage: p.cycle_tuples()
1224
[(1, 4, 5), (2, 6)]
1225
sage: p.cycle_tuples(singletons=True)
1226
[(1, 4, 5), (2, 6), (3,)]
1227
1228
EXAMPLES::
1229
1230
sage: S = SymmetricGroup(4)
1231
sage: S.gen(0).cycle_tuples()
1232
[(1, 2, 3, 4)]
1233
1234
::
1235
1236
sage: S = SymmetricGroup(['a','b','c','d'])
1237
sage: S.gen(0).cycle_tuples()
1238
[('a', 'b', 'c', 'd')]
1239
sage: S([('a', 'b'), ('c', 'd')]).cycle_tuples()
1240
[('a', 'b'), ('c', 'd')]
1241
"""
1242
from_gap = self._parent._domain_from_gap
1243
L = []
1244
cdef int i, k
1245
cdef bint* seen = <bint *>sage_malloc(sizeof(bint) * self.n)
1246
for i from 0 <= i < self.n: seen[i] = 0
1247
for i from 0 <= i < self.n:
1248
if seen[i]:
1249
continue
1250
if self.perm[i] == i:
1251
if singletons:
1252
PyList_Append(L, (from_gap[i+1],))
1253
# it is not necessary to put seen[i] to 1 as we will never
1254
# see i again
1255
else:
1256
continue
1257
else:
1258
cycle = [from_gap[i+1]]
1259
k = self.perm[i]
1260
while k != i:
1261
PyList_Append(cycle, from_gap[k+1])
1262
seen[k] = 1
1263
k = self.perm[k]
1264
PyList_Append(L, tuple(cycle))
1265
sage_free(seen)
1266
return L
1267
1268
def cycle_string(self, singletons=False):
1269
"""
1270
Return string representation of this permutation.
1271
1272
EXAMPLES::
1273
1274
sage: g = PermutationGroupElement([(1,2,3),(4,5)])
1275
sage: g.cycle_string()
1276
'(1,2,3)(4,5)'
1277
1278
sage: g = PermutationGroupElement([3,2,1])
1279
sage: g.cycle_string(singletons=True)
1280
'(1,3)(2)'
1281
"""
1282
cycles = self.cycle_tuples(singletons)
1283
if len(cycles) == 0:
1284
return '()'
1285
return ''.join([repr(c) for c in cycles]).replace(', ',',').replace(',)',')')
1286
1287
def has_descent(self, i, side = "right", positive = False):
1288
"""
1289
INPUT:
1290
1291
- ``i``: an element of the index set
1292
- ``side``: "left" or "right" (default: "right")
1293
- ``positive``: a boolean (default: False)
1294
1295
Returns whether ``self`` has a left (resp. right) descent at
1296
position ``i``. If ``positive`` is True, then test for a non
1297
descent instead.
1298
1299
Beware that, since permutations are acting on the right, the
1300
meaning of descents is the reverse of the usual
1301
convention. Hence, ``self`` has a left descent at position
1302
``i`` if ``self(i) > self(i+1)``.
1303
1304
EXAMPLES::
1305
1306
sage: S = SymmetricGroup([1,2,3])
1307
sage: S.one().has_descent(1)
1308
False
1309
sage: S.one().has_descent(2)
1310
False
1311
sage: s = S.simple_reflections()
1312
sage: x = s[1]*s[2]
1313
sage: x.has_descent(1, side = "right")
1314
False
1315
sage: x.has_descent(2, side = "right")
1316
True
1317
sage: x.has_descent(1, side = "left")
1318
True
1319
sage: x.has_descent(2, side = "left")
1320
False
1321
sage: S._test_has_descent()
1322
1323
The symmetric group acting on a set not of the form
1324
`(1,\dots,n)` is also supported::
1325
1326
sage: S = SymmetricGroup([2,4,1])
1327
sage: s = S.simple_reflections()
1328
sage: x = s[2]*s[4]
1329
sage: x.has_descent(4)
1330
True
1331
sage: S._test_has_descent()
1332
"""
1333
to_gap = self._parent._domain_to_gap
1334
from_gap = self._parent._domain_from_gap
1335
if side == "right":
1336
self = ~self
1337
1338
try:
1339
i1 = from_gap[to_gap[i]+1]
1340
except KeyError:
1341
return False
1342
1343
return (to_gap[self(i)] > to_gap[self(i1)]) is not positive
1344
1345
def matrix(self):
1346
"""
1347
Returns deg x deg permutation matrix associated to the permutation
1348
self
1349
1350
EXAMPLES::
1351
1352
sage: G = PermutationGroup(['(1,2,3)(4,5)'])
1353
sage: g = G.gen(0)
1354
sage: g.matrix()
1355
[0 1 0 0 0]
1356
[0 0 1 0 0]
1357
[1 0 0 0 0]
1358
[0 0 0 0 1]
1359
[0 0 0 1 0]
1360
"""
1361
M = MatrixSpace(ZZ, self.n, self.n, sparse=True)
1362
cdef int i
1363
entries = {}
1364
for i from 0 <= i < self.n:
1365
entries[i, self.perm[i]] = 1
1366
return M(entries)
1367
1368
def word_problem(self, words, display=True):
1369
"""
1370
G and H are permutation groups, g in G, H is a subgroup of G
1371
generated by a list (words) of elements of G. If g is in H, return
1372
the expression for g as a word in the elements of (words).
1373
1374
This function does not solve the word problem in Sage. Rather it
1375
pushes it over to GAP, which has optimized algorithms for the word
1376
problem. Essentially, this function is a wrapper for the GAP
1377
functions "EpimorphismFromFreeGroup" and
1378
"PreImagesRepresentative".
1379
1380
EXAMPLE::
1381
1382
sage: G = PermutationGroup([[(1,2,3),(4,5)],[(3,4)]], canonicalize=False)
1383
sage: g1, g2 = G.gens()
1384
sage: h = g1^2*g2*g1
1385
sage: h.word_problem([g1,g2], False)
1386
('x1^2*x2^-1*x1', '(1,2,3)(4,5)^2*(3,4)^-1*(1,2,3)(4,5)')
1387
sage: h.word_problem([g1,g2])
1388
x1^2*x2^-1*x1
1389
[['(1,2,3)(4,5)', 2], ['(3,4)', -1], ['(1,2,3)(4,5)', 1]]
1390
('x1^2*x2^-1*x1', '(1,2,3)(4,5)^2*(3,4)^-1*(1,2,3)(4,5)')
1391
"""
1392
if not self._parent._has_natural_domain():
1393
raise NotImplementedError
1394
1395
import copy
1396
from sage.groups.perm_gps.permgroup import PermutationGroup
1397
from sage.interfaces.all import gap
1398
1399
G = gap(words[0].parent())
1400
g = words[0].parent()(self)
1401
H = gap.Group(words)
1402
ans = G.EpimorphismFromFreeGroup().PreImagesRepresentative(g)
1403
1404
l1 = str(ans)
1405
l2 = copy.copy(l1)
1406
l4 = []
1407
l3 = l1.split("*")
1408
for i in range(1,len(words)+1):
1409
l2 = l2.replace("x"+str(i),str(words[i-1]))
1410
1411
if display:
1412
for i in range(len(l3)): ## parsing the word for display
1413
if len(l3[i].split("^"))==2:
1414
l4.append([l3[i].split("^")[0],int(l3[i].split("^")[1])])
1415
if len(l3[i].split("^"))==1:
1416
l4.append([l3[i].split("^")[0],1])
1417
l5 = copy.copy(l4)
1418
for i in range(len(l4)):
1419
for j in range(1,len(words)+1):
1420
l5[i][0] = l5[i][0].replace("x"+str(j),str(words[j-1]))
1421
print " ",l1
1422
print " ",l5
1423
return l1,l2
1424
1425
def conjugacy_class(self):
1426
r"""
1427
Return the conjugacy class of ``self``.
1428
1429
EXAMPLES::
1430
1431
sage: D = DihedralGroup(5)
1432
sage: g = D((1,3,5,2,4))
1433
sage: g.conjugacy_class()
1434
Conjugacy class of (1,3,5,2,4) in Dihedral group of order 10 as a permutation group
1435
"""
1436
from sage.groups.conjugacy_classes import ConjugacyClassGAP
1437
return ConjugacyClassGAP(self.parent(), self)
1438
1439
1440
cdef bint is_valid_permutation(int* perm, int n):
1441
"""
1442
This is used in the __init__ method.
1443
1444
Returns True iff the first n elements of perm are literally a
1445
permutation of [0, ..., n-1].
1446
1447
TESTS::
1448
1449
sage: S = SymmetricGroup(10)
1450
sage: PermutationGroupElement([2,1],S,check=False)
1451
(1,2)
1452
sage: PermutationGroupElement([1,1],S,check=False)
1453
Traceback (most recent call last):
1454
...
1455
ValueError: Invalid permutation vector: [1, 1]
1456
sage: PermutationGroupElement([1,-1],S,check=False)
1457
Traceback (most recent call last):
1458
...
1459
ValueError: Invalid permutation vector: [1, -1]
1460
sage: PermutationGroupElement([1,2,3,10],S,check=False)
1461
Traceback (most recent call last):
1462
...
1463
ValueError: Invalid permutation vector: [1, 2, 3, 10]
1464
"""
1465
cdef int i, ix
1466
# make everything is in bounds
1467
for i from 0 <= i < n:
1468
if not 0 <= perm[i] < n:
1469
return False
1470
# mark hit points by sign
1471
for i from 0 <= i < n:
1472
ix = -1-perm[i] if perm[i] < 0 else perm[i]
1473
perm[ix] = -1-perm[ix]
1474
# make sure everything is hit once, and reset signs
1475
for i from 0 <= i < n:
1476
if perm[i] >= 0:
1477
return False
1478
perm[i] = -1-perm[i]
1479
1480
return True
1481
1482
1483