passagemath-coxeter3 10.6.38__cp311-cp311-musllinux_1_2_x86_64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1223 @@
1
+ # distutils: language = c++
2
+ # distutils: libraries = coxeter3
3
+ # sage_setup: distribution = sagemath-coxeter3
4
+ # sage.doctest: optional - coxeter3
5
+ """
6
+ Low level part of the interface to Fokko Ducloux's Coxeter 3 library
7
+
8
+ .. TODO::
9
+
10
+ - Write a more efficient method for converting polynomials in
11
+ Coxeter to Sage polynomials.
12
+ """
13
+ # ****************************************************************************
14
+ # Copyright (C) 2009-2013 Mike Hansen <mhansen@gmail.com>
15
+ #
16
+ # Distributed under the terms of the GNU General Public License (GPL)
17
+ # https://www.gnu.org/licenses/
18
+ # ****************************************************************************
19
+
20
+ from sage.libs.coxeter3.decl cimport *
21
+ from cpython.object cimport Py_LT, Py_LE, Py_EQ, Py_NE, Py_GT, Py_GE
22
+ from sage.cpython.string cimport str_to_bytes, bytes_to_str
23
+
24
+ initConstants()
25
+
26
+ from sage.rings.integer import Integer
27
+ from sage.rings.integer_ring import ZZ
28
+ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
29
+
30
+
31
+ cdef class String:
32
+ def __init__(self, s=""):
33
+ """
34
+ Construct a Coxeter string from a Python string.
35
+
36
+ EXAMPLES::
37
+
38
+ sage: from sage.libs.coxeter3.coxeter import String
39
+ sage: s = String("hello"); s
40
+ hello
41
+ sage: del s
42
+ """
43
+ self.x = c_String(str_to_bytes(s))
44
+
45
+ def __repr__(self):
46
+ """
47
+ EXAMPLES::
48
+
49
+ sage: from sage.libs.coxeter3.coxeter import String
50
+ sage: s = String('Hi')
51
+ sage: s
52
+ Hi
53
+ """
54
+ return bytes_to_str(self.x.ptr())
55
+
56
+ def __hash__(self):
57
+ """
58
+ Return the hash of this String.
59
+
60
+ This is the hash of the tuple consisting of the class name and
61
+ the name of this type.
62
+
63
+ EXAMPLES::
64
+
65
+ sage: from sage.libs.coxeter3.coxeter import String
66
+ sage: s = String('hello')
67
+ sage: hash(s) == hash('hello')
68
+ True
69
+ """
70
+ return hash(repr(self))
71
+
72
+ def __richcmp__(String self, other, int op):
73
+ """
74
+ EXAMPLES::
75
+
76
+ sage: from sage.libs.coxeter3.coxeter import String
77
+ sage: ta1 = String('A')
78
+ sage: ta2 = String('A')
79
+ sage: tb = String('b')
80
+ sage: ta1 == ta2
81
+ True
82
+ sage: tb != ta1
83
+ True
84
+ sage: all([ta1 < tb, ta1 <= tb, ta1 <= ta1])
85
+ True
86
+ sage: all([tb > ta1, tb >= ta1, tb >= tb])
87
+ True
88
+ """
89
+ if type(other) is not type(self):
90
+ if op in (Py_LT, Py_LE, Py_GT, Py_GE):
91
+ return NotImplemented
92
+ return op == Py_NE
93
+
94
+ s = repr(self)
95
+ o = repr(other)
96
+
97
+ if op == Py_EQ:
98
+ return s == o
99
+ elif op == Py_NE:
100
+ return s != o
101
+ elif op == Py_LT:
102
+ return s < o
103
+ elif op == Py_LE:
104
+ return s <= o
105
+ elif op == Py_GT:
106
+ return s > o
107
+ elif op == Py_GE:
108
+ return s >= o
109
+
110
+ def __len__(self):
111
+ """
112
+ Return the length of this string.
113
+
114
+ EXAMPLES::
115
+
116
+ sage: from sage.libs.coxeter3.coxeter import String
117
+ sage: s = String('Hi')
118
+ sage: len(s)
119
+ 2
120
+ """
121
+ return self.x.length()
122
+
123
+ def __reduce__(self):
124
+ """
125
+ EXAMPLES::
126
+
127
+ sage: from sage.libs.coxeter3.coxeter import String
128
+ sage: s = String('Hi')
129
+ sage: TestSuite(s).run()
130
+ """
131
+ return (String, (repr(self),) )
132
+
133
+
134
+ cdef class Type:
135
+ def __init__(self, s):
136
+ """
137
+ Construct a Coxeter Type from a Python string.
138
+
139
+ EXAMPLES::
140
+
141
+ sage: from sage.libs.coxeter3.coxeter import Type
142
+ sage: t = Type('A'); t
143
+ A
144
+ sage: del t
145
+ """
146
+ self.x = c_Type(str_to_bytes(s))
147
+
148
+ def __repr__(self):
149
+ """
150
+ EXAMPLES::
151
+
152
+ sage: from sage.libs.coxeter3.coxeter import Type
153
+ sage: t = Type('A'); t
154
+ A
155
+ """
156
+ return bytes_to_str(self.x.name().ptr())
157
+
158
+ def name(self):
159
+ """
160
+ EXAMPLES::
161
+
162
+ sage: from sage.libs.coxeter3.coxeter import Type
163
+ sage: t = Type('A')
164
+ sage: t.name()
165
+ A
166
+ """
167
+ return String(bytes_to_str(self.x.name().ptr()))
168
+
169
+ def __hash__(self):
170
+ """
171
+ Return the hash of this Type.
172
+
173
+ This is the hash of the tuple consisting of the class name and
174
+ the name of this type.
175
+
176
+ EXAMPLES::
177
+
178
+ sage: from sage.libs.coxeter3.coxeter import Type
179
+ sage: a = Type('A')
180
+ sage: b = Type('B')
181
+ sage: hash(a) == hash(b)
182
+ False
183
+ sage: d = {a: 1, b: 2}
184
+ """
185
+ return hash(('Type', self.name()))
186
+
187
+ def __richcmp__(Type self, other, int op):
188
+ """
189
+ EXAMPLES::
190
+
191
+ sage: from sage.libs.coxeter3.coxeter import Type
192
+ sage: ta1 = Type('A')
193
+ sage: ta2 = Type('A')
194
+ sage: tb = Type('b')
195
+ sage: ta1 == ta2
196
+ True
197
+ sage: tb != ta1
198
+ True
199
+ sage: all([ta1 < tb, ta1 <= tb, ta1 <= ta1])
200
+ True
201
+ sage: all([tb > ta1, tb >= ta1, tb >= tb])
202
+ True
203
+ """
204
+ if type(other) is not type(self):
205
+ if op in (Py_LT, Py_LE, Py_GT, Py_GE):
206
+ return NotImplemented
207
+ return op == Py_NE
208
+
209
+ s = repr(self)
210
+ o = repr(other)
211
+
212
+ if op == Py_EQ:
213
+ return s == o
214
+ elif op == Py_NE:
215
+ return s != o
216
+ elif op == Py_LT:
217
+ return s < o
218
+ elif op == Py_LE:
219
+ return s <= o
220
+ elif op == Py_GT:
221
+ return s > o
222
+ elif op == Py_GE:
223
+ return s >= o
224
+
225
+ def __reduce__(self):
226
+ """
227
+ EXAMPLES::
228
+
229
+ sage: from sage.libs.coxeter3.coxeter import Type
230
+ sage: t = Type('A')
231
+ sage: TestSuite(t).run()
232
+ """
233
+ return (Type, (repr(self), ))
234
+
235
+
236
+ cdef class CoxGroup(SageObject):
237
+ def __cinit__(self, cartan_type):
238
+ """
239
+ EXAMPLES::
240
+
241
+ sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup
242
+ sage: W = CoxGroup(['A', 5]); W
243
+ Coxeter group of type A and rank 5
244
+
245
+ Coxeter 3 segfault's on the trivial Coxeter group; so we catch
246
+ this and raise a not implemented error::
247
+
248
+ sage: W = CoxGroup(['A', 0]); W
249
+ Traceback (most recent call last):
250
+ ...
251
+ NotImplementedError: Coxeter group of type ['A',0] using Coxeter 3 not yet implemented
252
+
253
+ Successfully initializes from a relabeled Cartan type::
254
+
255
+ sage: ctype = CartanType(['B', 3]).relabel({1: 3, 2: 2, 3: 1})
256
+ sage: W = CoxGroup(ctype)
257
+ sage: CoxeterMatrix(W.coxeter_matrix(), ctype.index_set()) == CoxeterMatrix(ctype)
258
+ True
259
+ """
260
+ from sage.combinat.root_system.cartan_type import CartanType
261
+ from sage.combinat.root_system.coxeter_matrix import CoxeterMatrix
262
+ self.cartan_type = CartanType(cartan_type)
263
+ ordering = self._ordering_from_cartan_type(self.cartan_type)
264
+
265
+ type, rank = self.cartan_type.type(), self.cartan_type.rank()
266
+ if self.cartan_type.is_affine():
267
+ # Only untwisted affine groups are supported
268
+ try:
269
+ if not self.cartan_type.is_untwisted_affine():
270
+ raise NotImplementedError('twisted affine groups are not supported in coxeter3')
271
+ except AttributeError:
272
+ pass
273
+ type = type.lower()
274
+
275
+ type = 'B' if type == 'C' else type
276
+
277
+ if rank == 0:
278
+ raise NotImplementedError("Coxeter group of type ['A',0] using Coxeter 3 not yet implemented")
279
+ cdef Type t = Type(type)
280
+ cdef c_CoxGroup* c_W = coxeterGroup(t.x, rank)
281
+ self.x = c_W
282
+ self.out_ordering = {i+1: o for i,o in enumerate(ordering)}
283
+ self.in_ordering = {self.out_ordering[a]: a for a in self.out_ordering}
284
+
285
+ # If the Cartan type supplied is relabeled, compose these orderings
286
+ # with the relabelling on the appropriate sides:
287
+ if hasattr(self.cartan_type, '_relabelling'):
288
+ r = self.cartan_type._relabelling
289
+ r_inv = {v: k for (k, v) in r.items()}
290
+ # Pre-compose in_ordering with r
291
+ self.in_ordering = {i: self.in_ordering[r[i]] for i in self.in_ordering}
292
+ # Post-compose out_ordering with r inverse
293
+ self.out_ordering = {i: r_inv[self.out_ordering[i]] for i in self.out_ordering}
294
+
295
+ # Check that the Coxeter matrices match up.
296
+ cox_mat = CoxeterMatrix(self.coxeter_matrix(), self.cartan_type.index_set())
297
+ if cox_mat != CoxeterMatrix(self.cartan_type):
298
+ print("Warning, differing Coxeter matrices")
299
+
300
+ @classmethod
301
+ def _ordering_from_cartan_type(cls, cartan_type):
302
+ """
303
+ Return an ordering of the index set associated to the Cartan type.
304
+
305
+ EXAMPLES::
306
+
307
+ sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup
308
+ sage: W = CoxGroup(['A', 5])
309
+ sage: W._ordering_from_cartan_type(CartanType(['A',5]))
310
+ [1, 2, 3, 4, 5]
311
+ """
312
+ from sage.arith.srange import srange
313
+ t = cartan_type.type()
314
+ r = cartan_type.rank()
315
+ is_affine = cartan_type.is_affine()
316
+
317
+ if t in ['B', 'C', 'D', 'F', 'H']:
318
+ return srange(r-1 if is_affine else r,
319
+ -1 if is_affine else 0, -1)
320
+ elif t in ['A', 'I']:
321
+ return srange(0 if is_affine else 1, r+1)
322
+ elif t in ['G']:
323
+ if is_affine:
324
+ raise NotImplementedError
325
+ else:
326
+ return [Integer(1), Integer(2)]
327
+ elif t in ['E']:
328
+ if is_affine:
329
+ return srange(1, r) + [ZZ.zero()]
330
+ else:
331
+ return srange(1, r+1)
332
+ else:
333
+ raise NotImplementedError
334
+
335
+ def __hash__(self):
336
+ """
337
+ Return the hash of this CoxGroup.
338
+
339
+ This is the hash of the tuple of the class's name, the type,
340
+ and the rank.
341
+
342
+ EXAMPLES::
343
+
344
+ sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup
345
+ sage: A4 = CoxGroup(['A', 4])
346
+ sage: d = {A4: True}
347
+ """
348
+ return hash((self.__class__.__name__, self.type(), self.rank()))
349
+
350
+ def __richcmp__(CoxGroup self, other, int op):
351
+ """
352
+ EXAMPLES::
353
+
354
+ sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup
355
+ sage: A4 = CoxGroup(['A', 4])
356
+ sage: A5 = CoxGroup(['A', 5])
357
+ sage: B4 = CoxGroup(['B', 4])
358
+ sage: A4 == A4
359
+ True
360
+ sage: A4 != B4
361
+ True
362
+ sage: A4 < B4
363
+ True
364
+ sage: A5 > A4
365
+ True
366
+ sage: A4 >= A4
367
+ True
368
+ sage: B4 >= A5
369
+ True
370
+ """
371
+ if type(other) is not type(self):
372
+ if op in (Py_LT, Py_LE, Py_GT, Py_GE):
373
+ return NotImplemented
374
+ return op == Py_NE
375
+
376
+ s_t = self.type()
377
+ o_t = other.type()
378
+ s_r = self.rank()
379
+ o_r = other.rank()
380
+
381
+ if op == Py_EQ:
382
+ return s_t == o_t and s_r == o_r
383
+ elif op == Py_NE:
384
+ return s_t != o_t or s_r != o_r
385
+ elif op == Py_LT:
386
+ return s_t < o_t or (s_t == o_t and s_r < o_r)
387
+ elif op == Py_LE:
388
+ return s_t < o_t or (s_t == o_t and s_r <= o_r)
389
+ elif op == Py_GT:
390
+ return s_t > o_t or (s_t == o_t and s_r > o_r)
391
+ elif op == Py_GE:
392
+ return s_t > o_t or (s_t == o_t and s_r >= o_r)
393
+
394
+ def __reduce__(self):
395
+ """
396
+ EXAMPLES::
397
+
398
+ sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup
399
+ sage: W = CoxGroup(['A', 5])
400
+ sage: TestSuite((W)).run()
401
+ """
402
+ return (CoxGroup, (self.cartan_type,))
403
+
404
+ def __dealloc__(self):
405
+ """
406
+ Deallocate the memory for this CoxGroup.
407
+
408
+ EXAMPLES::
409
+
410
+ sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup
411
+ sage: W = CoxGroup(['A', 5])
412
+ sage: del W
413
+ """
414
+ del self.x
415
+
416
+ def __repr__(self):
417
+ """
418
+ Return a string representation of this Coxeter group.
419
+
420
+ EXAMPLES::
421
+
422
+ sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup
423
+ sage: W = CoxGroup(['A', 5]); W
424
+ Coxeter group of type A and rank 5
425
+ """
426
+ return "Coxeter group of type %s and rank %s" % (self.type(),
427
+ self.rank())
428
+
429
+ def __iter__(self):
430
+ """
431
+ EXAMPLES::
432
+
433
+ sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup
434
+ sage: W = CoxGroup(['A', 2])
435
+ sage: list(iter(W))
436
+ [[], [1], [2], [1, 2], [2, 1], [1, 2, 1]]
437
+ """
438
+ return CoxGroupIterator(self)
439
+
440
+ def bruhat_interval(self, w, v):
441
+ """
442
+ Return the list of the elements in the Bruhat interval between `w` and `v`.
443
+
444
+ EXAMPLES::
445
+
446
+ sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup
447
+ sage: W = CoxGroup(['A', 2])
448
+ sage: W.bruhat_interval([], [1,2])
449
+ [[], [1], [2], [1, 2]]
450
+ """
451
+ cdef CoxGroupElement ww = CoxGroupElement(self, w)
452
+ cdef CoxGroupElement vv = CoxGroupElement(self, v)
453
+ cdef c_List_CoxWord l = c_List_CoxWord(0)
454
+ interval(l, self.x[0], ww.word, vv.word)
455
+ bruhat_interval = []
456
+ cdef CoxGroupElement u
457
+ cdef CoxGroupElement gg = CoxGroupElement(self, [])
458
+ cdef size_t j
459
+ for j in range(l.size()):
460
+ u = gg._new()
461
+ u.word = l[j]
462
+ bruhat_interval.append(u)
463
+
464
+ return bruhat_interval
465
+
466
+ def orderings(self):
467
+ """
468
+ Return two dictionaries specifying the mapping of the labels
469
+ of the Dynkin diagram between Sage and Coxeter3 and the its
470
+ inverse.
471
+
472
+ EXAMPLES::
473
+
474
+ sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup
475
+ sage: W = CoxGroup(['A', 5])
476
+ sage: W.orderings()
477
+ ({1: 1, 2: 2, 3: 3, 4: 4, 5: 5}, {1: 1, 2: 2, 3: 3, 4: 4, 5: 5})
478
+ """
479
+ return self.in_ordering, self.out_ordering
480
+
481
+ def type(self):
482
+ """
483
+ Return the type of this Coxeter group.
484
+
485
+ Note that the type does not include the rank.
486
+
487
+ EXAMPLES::
488
+
489
+ sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup
490
+ sage: W = CoxGroup(['A', 5])
491
+ sage: W.type()
492
+ A
493
+ """
494
+ return Type(bytes_to_str(self.x.type().name().ptr()))
495
+
496
+ def rank(self):
497
+ """
498
+ Return the rank of this Coxeter group.
499
+
500
+ EXAMPLES::
501
+
502
+ sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup
503
+ sage: W = CoxGroup(['A', 5])
504
+ sage: W.rank()
505
+ 5
506
+ """
507
+ return self.x.rank()
508
+
509
+ def order(self):
510
+ """
511
+ Return the order of this Coxeter group.
512
+
513
+ EXAMPLES::
514
+
515
+ sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup
516
+ sage: W = CoxGroup(['A', 5])
517
+ sage: W.order()
518
+ 720
519
+ sage: W = CoxGroup(['A', 3, 1])
520
+ sage: W.order()
521
+ +Infinity
522
+ """
523
+ if self.is_finite():
524
+ return Integer(self.x.order())
525
+ else:
526
+ from sage.rings.infinity import infinity
527
+ return infinity
528
+
529
+ def is_finite(self):
530
+ """
531
+ Return whether this Coxeter group is finite.
532
+
533
+ EXAMPLES::
534
+
535
+ sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup
536
+ sage: W = CoxGroup(['A', 5])
537
+ sage: W.is_finite()
538
+ True
539
+ sage: W = CoxGroup(['A', 3, 1])
540
+ sage: W.is_finite()
541
+ False
542
+ """
543
+ return isFiniteType(self.x)
544
+
545
+ cpdef full_context(self) noexcept:
546
+ """
547
+ Make all of the elements of a finite Coxeter group available.
548
+
549
+ Raises an error if ``self`` is not finite.
550
+
551
+ EXAMPLES::
552
+
553
+ sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup
554
+ sage: W = CoxGroup(['A', 2])
555
+ sage: W.full_context()
556
+ sage: W = CoxGroup(['A', 2,1])
557
+ sage: W.full_context()
558
+ Traceback (most recent call last):
559
+ ...
560
+ TypeError: group needs to be finite
561
+ """
562
+ if not self.is_finite():
563
+ raise TypeError("group needs to be finite")
564
+ cdef c_FiniteCoxGroup* fcoxgroup = <c_FiniteCoxGroup*>(self.x)
565
+ if not fcoxgroup.isFullContext():
566
+ fcoxgroup.fullContext()
567
+
568
+ def long_element(self):
569
+ """
570
+ Return the longest word in a finite Coxeter group.
571
+
572
+ EXAMPLES::
573
+
574
+ sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup
575
+ sage: W = CoxGroup(['A', 5])
576
+ sage: W.long_element()
577
+ [1, 2, 1, 3, 2, 1, 4, 3, 2, 1, 5, 4, 3, 2, 1]
578
+
579
+ sage: W = CoxGroup(['A', 3, 1])
580
+ sage: W.long_element()
581
+ Traceback (most recent call last):
582
+ ...
583
+ TypeError: group needs to be finite
584
+ """
585
+ self.full_context()
586
+ cdef c_FiniteCoxGroup* fcoxgroup = <c_FiniteCoxGroup*>(self.x)
587
+ cdef CoxGroupElement w0 = CoxGroupElement(self, [])
588
+ w0.word = fcoxgroup.longest_coxword()
589
+ return w0
590
+
591
+ def __call__(self, w):
592
+ """
593
+ Return a reduced expression for ``w``.
594
+
595
+ INPUT:
596
+
597
+ - ``w`` -- a word for an element of ``self``, not necessarily reduced
598
+
599
+ OUTPUT: a reduced expression for ``w``
600
+
601
+ EXAMPLES::
602
+
603
+ sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup
604
+ sage: W = CoxGroup(['A', 5])
605
+ sage: w = [1,1,3,5,4,5,4]
606
+ sage: W.__call__(w)
607
+ [3, 4, 5]
608
+ """
609
+ return CoxGroupElement(self, w).reduced()
610
+
611
+ def coxeter_matrix(self):
612
+ """
613
+ Return the Coxeter matrix for this Coxeter group.
614
+
615
+ EXAMPLES::
616
+
617
+ sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup
618
+ sage: W = CoxGroup(['A', 5])
619
+ sage: W.coxeter_matrix()
620
+ [1 3 2 2 2]
621
+ [3 1 3 2 2]
622
+ [2 3 1 3 2]
623
+ [2 2 3 1 3]
624
+ [2 2 2 3 1]
625
+ """
626
+ from sage.matrix.constructor import matrix
627
+ rank = self.rank()
628
+ m = matrix(ZZ, rank, rank)
629
+ for i, ii in enumerate(self.cartan_type.index_set()):
630
+ ii = self.in_ordering[ii]-1
631
+ for j, jj in enumerate(self.cartan_type.index_set()):
632
+ jj = self.in_ordering[jj]-1
633
+ m[i,j] = self.x.M(ii, jj)
634
+ return m
635
+
636
+ def coxeter_graph(self):
637
+ """
638
+ Return the Coxeter graph for this Coxeter group.
639
+
640
+ OUTPUT: a Sage graph
641
+
642
+ .. NOTE::
643
+
644
+ This uses the labels native to Coxeter3. This is useful
645
+ when trying to obtain the mapping between the labels of
646
+ Sage and Coxeter3.
647
+
648
+ EXAMPLES::
649
+
650
+ sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup
651
+ sage: W = CoxGroup(['A', 5])
652
+ sage: W.coxeter_graph()
653
+ Graph on 5 vertices
654
+ sage: W.coxeter_graph().edges(sort=True)
655
+ [(1, 2, None), (2, 3, None), (3, 4, None), (4, 5, None)]
656
+ """
657
+ from sage.graphs.graph import Graph
658
+ g = Graph()
659
+ m = self.coxeter_matrix()
660
+ rank = self.rank()
661
+ for i, row in enumerate(m.rows()):
662
+ for j in range(i+1,rank):
663
+ if row[j] == 3:
664
+ g.add_edge(i+1, j+1)
665
+ elif row[j] > 4:
666
+ g.add_edge(i+1, j+1, row[j])
667
+ return g
668
+
669
+
670
+ cdef class CoxGroupElement:
671
+ def __init__(self, CoxGroup group, w, normal_form=True):
672
+ """
673
+ TESTS::
674
+
675
+ sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup, CoxGroupElement
676
+ sage: W = CoxGroup(['A', 5])
677
+ sage: w = CoxGroupElement(W, [2,1,2,1,1], normal_form=False); w
678
+ [2, 1, 2, 1, 1]
679
+ sage: w = CoxGroupElement(W, [1,1,4,5,4], normal_form=False); w
680
+ [1, 1, 4, 5, 4]
681
+ sage: w = CoxGroupElement(W, [1,1,4,5,4]); w
682
+ [4, 5, 4]
683
+ sage: W = CoxGroup(['A', 4])
684
+ sage: CoxGroupElement(W, [1,2,3,2,3])
685
+ [1, 3, 2]
686
+ sage: W = CoxGroup(['A', 4])
687
+ sage: w = CoxGroupElement(W, [1,2,3,2,3])
688
+ sage: del w
689
+ """
690
+ self.group = (<CoxGroup>group).x
691
+ self._parent_group = group
692
+ self.word.reset()
693
+ for i in w:
694
+ self.word.append(self._parent_group.in_ordering[i])
695
+
696
+ if normal_form:
697
+ self.group.normalForm(self.word)
698
+
699
+ def _coxnumber(self):
700
+ """
701
+ Return the internal integer used by Coxeter3 to represent this element.
702
+
703
+ TESTS::
704
+
705
+ sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup, CoxGroupElement
706
+ sage: W = CoxGroup(['A', 4])
707
+ sage: w = CoxGroupElement(W, [1,2,3,2,3])
708
+ sage: w._coxnumber()
709
+ 7
710
+ """
711
+ return int(self.group.extendContext(self.word))
712
+
713
+ def __reduce__(self):
714
+ """
715
+ EXAMPLES::
716
+
717
+ sage: from sage.libs.coxeter3.coxeter import *
718
+ sage: W = CoxGroup(['A',5])
719
+ sage: w = W([1,2,3])
720
+ sage: TestSuite(w).run()
721
+ """
722
+ return (CoxGroupElement, (self._parent_group, list(self)))
723
+
724
+ def __invert__(self):
725
+ """
726
+ Return the inverse of this element.
727
+
728
+ EXAMPLES::
729
+
730
+ sage: from sage.libs.coxeter3.coxeter import *
731
+ sage: W = CoxGroup(['A',5])
732
+ sage: w = W([1,2,3])
733
+ sage: ~w
734
+ [3, 2, 1]
735
+ """
736
+ return CoxGroupElement(self._parent_group, reversed(self))
737
+
738
+ inverse = __invert__
739
+
740
+ cpdef CoxGroup parent_group(self) noexcept:
741
+ """
742
+ Return the parent Coxeter group for this element.
743
+
744
+ EXAMPLES::
745
+
746
+ sage: from sage.libs.coxeter3.coxeter import *
747
+ sage: W = CoxGroup(['A',5])
748
+ sage: w = W([1,2,3])
749
+ sage: w.parent_group()
750
+ Coxeter group of type A and rank 5
751
+ """
752
+ return self._parent_group
753
+
754
+ def __getitem__(self, i):
755
+ """
756
+ Return the `i`-th entry of this element.
757
+
758
+ EXAMPLES::
759
+
760
+ sage: from sage.libs.coxeter3.coxeter import *
761
+ sage: W = CoxGroup(['A',5])
762
+ sage: w = W([1,2,3])
763
+ sage: w[0]
764
+ 1
765
+ sage: w[2]
766
+ 3
767
+ sage: w[:-2]
768
+ [1]
769
+ sage: w[-2:]
770
+ [2, 3]
771
+ sage: w[3:0:-1]
772
+ [3, 2]
773
+ sage: w[4]
774
+ Traceback (most recent call last):
775
+ ...
776
+ IndexError: The index (4) is out of range.
777
+ """
778
+ if isinstance(i, slice):
779
+ #Get the start, stop, and step from the slice
780
+ return [self[ii] for ii in range(*i.indices(len(self)))]
781
+ if i < 0:
782
+ i += len(self)
783
+ if i >= len(self):
784
+ raise IndexError("The index (%d) is out of range." % i)
785
+
786
+ return self._parent_group.out_ordering[self.word[i]]
787
+
788
+ def __repr__(self):
789
+ """
790
+ Return a string representation of this CoxGroupElement as a list of generators.
791
+
792
+ EXAMPLES::
793
+
794
+ sage: from sage.libs.coxeter3.coxeter import *
795
+ sage: W = CoxGroup(['A',5])
796
+ sage: w = W([1,2,3]); w
797
+ [1, 2, 3]
798
+ """
799
+ return repr(list(self))
800
+
801
+ def __hash__(self):
802
+ """
803
+ Return the hash of this element.
804
+
805
+ This is a hash of the tuple of the class name, the parent, and
806
+ a tuple of the reduced word.
807
+
808
+ EXAMPLES::
809
+
810
+ sage: from sage.libs.coxeter3.coxeter import *
811
+ sage: W = CoxGroup(['A', 5])
812
+ sage: w = W([1,2,3])
813
+ sage: v = W([2,3,4])
814
+ sage: hash(w) == hash(v)
815
+ False
816
+ """
817
+ return hash((self.__class__.__name__, self.parent_group(), tuple(self)))
818
+
819
+ def __richcmp__(CoxGroupElement self, other, int op):
820
+ """
821
+ EXAMPLES::
822
+
823
+ sage: from sage.libs.coxeter3.coxeter import *
824
+ sage: W = CoxGroup(['A', 5])
825
+ sage: V = CoxGroup(['A', 6])
826
+ sage: w1 = W([1,2,3])
827
+ sage: w2 = W([2,3,4])
828
+ sage: v1 = V([1,2,3])
829
+ sage: w1 == w1
830
+ True
831
+ sage: w1 != w2
832
+ True
833
+ sage: all([w1 < w2, w1 <= w2, w1 <= w1])
834
+ True
835
+ sage: all([w2 > w1, w2 >= w1, w2 >= w2])
836
+ True
837
+ sage: w1 == v1
838
+ False
839
+ sage: w1 != v1
840
+ True
841
+ """
842
+ if type(other) is not type(self):
843
+ if op in (Py_LT, Py_LE, Py_GT, Py_GE):
844
+ return NotImplemented
845
+ return op == Py_NE
846
+
847
+ s_p = self.parent_group()
848
+ o_p = other.parent_group()
849
+ s_l = list(self)
850
+ o_l = list(other)
851
+
852
+ if op == Py_EQ:
853
+ return s_p == o_p and s_l == o_l
854
+ elif op == Py_NE:
855
+ return s_p != o_p or s_l != o_l
856
+ elif op == Py_LT:
857
+ return s_p < o_p or (s_p == o_p and s_l < o_l)
858
+ elif op == Py_LE:
859
+ return s_p < o_p or (s_p == o_p and s_l <= o_l)
860
+ elif op == Py_GT:
861
+ return s_p > o_p or (s_p == o_p and s_l > o_l)
862
+ elif op == Py_GE:
863
+ return s_p > o_p or (s_p == o_p and s_l >= o_l)
864
+
865
+ def __iter__(self):
866
+ """
867
+ Return an iterator for the letters in the reduced word for this element.
868
+
869
+ EXAMPLES::
870
+
871
+ sage: from sage.libs.coxeter3.coxeter import *
872
+ sage: W = CoxGroup(['A',5])
873
+ sage: w = W([1,2,3])
874
+ sage: [a for a in w]
875
+ [1, 2, 3]
876
+ """
877
+ return (self[i] for i in range(len(self)))
878
+
879
+ def __len__(self):
880
+ """
881
+ Return the length of this element.
882
+
883
+ EXAMPLES::
884
+
885
+ sage: from sage.libs.coxeter3.coxeter import *
886
+ sage: W = CoxGroup(['A',5])
887
+ sage: w = W([1,2,3])
888
+ sage: len(w)
889
+ 3
890
+ """
891
+ return self.word.length()
892
+
893
+ def left_descents(self):
894
+ """
895
+ Return the left descent set of this element.
896
+
897
+ EXAMPLES::
898
+
899
+ sage: from sage.libs.coxeter3.coxeter import *
900
+ sage: W = CoxGroup(['A',5])
901
+ sage: w = W([1,2,1])
902
+ sage: w.left_descents()
903
+ [1, 2]
904
+ """
905
+ return LFlags_to_list(self._parent_group, self.group.ldescent(self.word))
906
+
907
+ def right_descents(self):
908
+ """
909
+ Return the right descent set of this element.
910
+
911
+ EXAMPLES::
912
+
913
+ sage: from sage.libs.coxeter3.coxeter import *
914
+ sage: W = CoxGroup(['A',5])
915
+ sage: w = W([1,2,1])
916
+ sage: w.right_descents()
917
+ [1, 2]
918
+ """
919
+ return LFlags_to_list(self._parent_group, self.group.rdescent(self.word))
920
+
921
+ def bruhat_le(self, w):
922
+ """
923
+ Return whether u = (self) is less than w in Bruhat order.
924
+
925
+ EXAMPLES::
926
+
927
+ sage: from sage.libs.coxeter3.coxeter import *
928
+ sage: W = CoxGroup(['A',5])
929
+ sage: w = W([1,2,3,4,5,4])
930
+ sage: v = W([1,2,4,5,4])
931
+ sage: v.bruhat_le(w)
932
+ True
933
+ sage: w.bruhat_le(w)
934
+ True
935
+ sage: w.bruhat_le(v)
936
+ False
937
+ """
938
+ cdef CoxGroupElement ww = CoxGroupElement(self._parent_group, w)
939
+ return self.group.inOrder(self.word, ww.word)
940
+
941
+ def is_two_sided_descent(self, s):
942
+ """
943
+ Return whether ``s`` is a two-sided descent of ``self``.
944
+
945
+ EXAMPLES::
946
+
947
+ sage: from sage.libs.coxeter3.coxeter import *
948
+ sage: W = CoxGroup(['A',2])
949
+ sage: x = W([1,2,1])
950
+ sage: x.is_two_sided_descent(1)
951
+ True
952
+ """
953
+ cdef Generator ss = self._parent_group.in_ordering[s]
954
+ return self.group.isDescent(self.word, s)
955
+
956
+ cdef CoxGroupElement _new(self) noexcept:
957
+ """
958
+ Return a new copy of this element.
959
+ """
960
+ cdef CoxGroupElement res = CoxGroupElement(self.parent_group(), [])
961
+ res.word = self.word
962
+ return res
963
+
964
+ def coatoms(self):
965
+ """
966
+ Return the coatoms of this element in Bruhat order.
967
+
968
+ EXAMPLES::
969
+
970
+ sage: from sage.libs.coxeter3.coxeter import *
971
+ sage: W = CoxGroup(['A',2])
972
+ sage: W([1,2,1]).coatoms()
973
+ [[2, 1], [1, 2]]
974
+ sage: W([]).coatoms()
975
+ []
976
+ """
977
+ cdef c_List_CoxWord list = c_List_CoxWord(0)
978
+ self.group.coatoms(list, self.word)
979
+
980
+ coatoms = []
981
+
982
+ cdef Length i = 0
983
+ cdef CoxGroupElement res
984
+ for i in range(list.size()):
985
+ res = self._new()
986
+ res.word = list[i]
987
+ coatoms.append(res)
988
+ return coatoms
989
+
990
+ def normal_form(self):
991
+ """
992
+ Return ``self`` in normal form.
993
+
994
+ This is the lexicographically minimal reduced word for
995
+ ``self``.
996
+
997
+ EXAMPLES::
998
+
999
+ sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup, CoxGroupElement
1000
+ sage: W = CoxGroup(['A', 5])
1001
+ sage: w = CoxGroupElement(W, [2,1,2], normal_form=False); w
1002
+ [2, 1, 2]
1003
+ sage: w.normal_form()
1004
+ [1, 2, 1]
1005
+ """
1006
+ cdef CoxGroupElement res = self._new()
1007
+ self.group.normalForm(res.word)
1008
+ return res
1009
+
1010
+ def reduced(self):
1011
+ """
1012
+ Return a reduced word for this element.
1013
+
1014
+ EXAMPLES::
1015
+
1016
+ sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup, CoxGroupElement
1017
+ sage: W = CoxGroup(['A', 5])
1018
+ sage: w = CoxGroupElement(W, [2,1,2,1,1], normal_form=False); w
1019
+ [2, 1, 2, 1, 1]
1020
+ sage: w.reduced()
1021
+ [1, 2, 1]
1022
+ """
1023
+ cdef CoxGroupElement res = self._new()
1024
+ self.group.reduced(res.word, self.word)
1025
+ return res
1026
+
1027
+ def __mul__(CoxGroupElement self, CoxGroupElement y):
1028
+ """
1029
+ EXAMPLES::
1030
+
1031
+ sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup
1032
+ sage: W = CoxGroup(['A', 5])
1033
+ sage: W([1]) * W([1])
1034
+ []
1035
+ sage: W([1,2]) * W([1])
1036
+ [1, 2, 1]
1037
+ """
1038
+ cdef CoxGroupElement res = self._new()
1039
+ self.group.prod(res.word, y.word)
1040
+ return res
1041
+
1042
+ def poincare_polynomial(self):
1043
+ """
1044
+ Return the Poincaré polynomial associated with the Bruhat
1045
+ interval between the identity element and this one.
1046
+
1047
+ EXAMPLES::
1048
+
1049
+ sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup
1050
+ sage: W = CoxGroup(['A', 5])
1051
+ sage: W([]).poincare_polynomial()
1052
+ 1
1053
+ sage: W([1,2,1]).poincare_polynomial()
1054
+ t^3 + 2*t^2 + 2*t + 1
1055
+ """
1056
+ cdef CoxGroup W = self.parent_group()
1057
+ cdef c_List_CoxWord result = c_List_CoxWord(0)
1058
+ cdef CoxGroupElement id = CoxGroupElement(W, [])
1059
+ cdef CoxGroupElement ww = CoxGroupElement(W, self)
1060
+ interval(result, W.x[0], id.word, ww.word)
1061
+
1062
+ cdef list coefficients = [0]*(len(ww)+1)
1063
+ cdef size_t j
1064
+ for j in range(result.size()):
1065
+ coefficients[result[j].length()] += 1
1066
+ return ZZ['t'](coefficients)
1067
+
1068
+ def kazhdan_lusztig_polynomial(self, v):
1069
+ """
1070
+ Return the Kazhdan-Lusztig polynomial `P_{u,v}` where `u` is ``self``.
1071
+
1072
+ Currently this is a bit inefficient as it constructs the
1073
+ polynomial from its string representation.
1074
+
1075
+ EXAMPLES::
1076
+
1077
+ sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup
1078
+ sage: W = CoxGroup(['A', 2])
1079
+ sage: W([]).kazhdan_lusztig_polynomial([1,2,1])
1080
+ 1
1081
+ sage: W([1,2,1]).kazhdan_lusztig_polynomial([])
1082
+ 0
1083
+ """
1084
+ cdef CoxGroupElement vv
1085
+ if not isinstance(v, CoxGroupElement):
1086
+ vv = CoxGroupElement(self._parent_group, v)
1087
+ else:
1088
+ vv = v
1089
+
1090
+ ZZq = PolynomialRing(ZZ, 'q')
1091
+ if not self.group.inOrder(self.word, vv.word):
1092
+ return ZZq.zero()
1093
+
1094
+ cdef CoxNbr x = self.group.extendContext(self.word)
1095
+ cdef CoxNbr y = self.group.extendContext(vv.word)
1096
+ cdef c_KLPol kl_poly = self.group.klPol(x, y)
1097
+ if kl_poly.isZero():
1098
+ return ZZq.zero()
1099
+ cdef size_t i
1100
+ l = [kl_poly[i] for i in range(kl_poly.deg()+1)]
1101
+ return ZZq(l)
1102
+
1103
+ def mu_coefficient(self, v):
1104
+ r"""
1105
+ Return the mu coefficient `\mu(u,v)` where `u` is this element.
1106
+
1107
+ EXAMPLES::
1108
+
1109
+ sage: from sage.libs.coxeter3.coxeter import *
1110
+ sage: W = CoxGroup(['A',5])
1111
+ sage: w = W([1,2,3,4,5,4])
1112
+ sage: v = W([1,2,4,5,4])
1113
+ sage: w.mu_coefficient(v)
1114
+ 0
1115
+ sage: w.mu_coefficient(w)
1116
+ 0
1117
+ sage: v.mu_coefficient(w)
1118
+ 1
1119
+ """
1120
+ cdef CoxGroupElement vv = CoxGroupElement(self._parent_group, v)
1121
+ cdef CoxNbr x = self.group.extendContext(self.word)
1122
+ cdef CoxNbr y = self.group.extendContext(vv.word)
1123
+ return ZZ(self.group.mu(x,y))
1124
+
1125
+
1126
+ cdef LFlags_to_list(CoxGroup parent, LFlags f) noexcept:
1127
+ """
1128
+ Return the right descent set of this element.
1129
+
1130
+ EXAMPLES::
1131
+
1132
+ sage: from sage.libs.coxeter3.coxeter import *
1133
+ sage: W = CoxGroup(['A',5])
1134
+ sage: w = W([1,2,1])
1135
+ sage: w.right_descents()
1136
+ [1, 2]
1137
+ """
1138
+ cdef Generator s
1139
+ cdef LFlags f1 = f
1140
+ l = []
1141
+ while f1:
1142
+ s = firstBit(f1)
1143
+ l.append(parent.out_ordering[s+1])
1144
+ f1 = f1 & (f1-1)
1145
+ return l
1146
+
1147
+
1148
+ class CoxGroupIterator():
1149
+ def __init__(self, group):
1150
+ """
1151
+ A class used to iterate over all of the elements of a Coxeter group.
1152
+
1153
+ .. NOTE::
1154
+
1155
+ This will construct all of the elements of the group within
1156
+ Coxeter3. For some groups, this may be too large to fit
1157
+ into memory.
1158
+
1159
+ EXAMPLES::
1160
+
1161
+ sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup, CoxGroupIterator
1162
+ sage: W = CoxGroup(['A', 2])
1163
+ sage: it = CoxGroupIterator(W)
1164
+ sage: [next(it) for i in range(W.order())]
1165
+ [[], [1], [2], [1, 2], [2, 1], [1, 2, 1]]
1166
+ """
1167
+ self.group = group
1168
+ self.order = group.order()
1169
+ self.n = 0
1170
+ self.group.full_context()
1171
+
1172
+ def __iter__(self):
1173
+ """
1174
+ Return self, as per the iterator protocol.
1175
+
1176
+ EXAMPLES::
1177
+
1178
+ sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup, CoxGroupIterator
1179
+ sage: W = CoxGroup(['A', 2])
1180
+ sage: it = iter(W)
1181
+ sage: it is iter(it)
1182
+ True
1183
+ """
1184
+ return self
1185
+
1186
+ def __next__(self):
1187
+ """
1188
+ Return the next element in the associated Coxeter group.
1189
+
1190
+ EXAMPLES::
1191
+
1192
+ sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup, CoxGroupIterator
1193
+ sage: W = CoxGroup(['A', 2])
1194
+ sage: it = CoxGroupIterator(W)
1195
+ sage: next(it)
1196
+ []
1197
+ """
1198
+ if self.n >= self.order:
1199
+ raise StopIteration
1200
+ cdef CoxGroupElement w = self.group([])
1201
+
1202
+ (<CoxGroup>self.group).x.prod_nbr(w.word, self.n)
1203
+ self.n += 1
1204
+ return w
1205
+
1206
+ next = __next__
1207
+
1208
+
1209
+ CoxGroup_cache = {}
1210
+
1211
+
1212
+ def get_CoxGroup(cartan_type):
1213
+ """
1214
+ TESTS::
1215
+
1216
+ sage: from sage.libs.coxeter3.coxeter import get_CoxGroup as CoxGroup, CoxGroupIterator
1217
+ sage: W = CoxGroup(['A', 2])
1218
+ """
1219
+ from sage.combinat.root_system.cartan_type import CartanType
1220
+ cartan_type = CartanType(cartan_type)
1221
+ if cartan_type not in CoxGroup_cache:
1222
+ CoxGroup_cache[cartan_type] = CoxGroup(cartan_type)
1223
+ return CoxGroup_cache[cartan_type]