# Cartier--Foata (CF) canonical form:
# The CF canonical form of a reduced word w of an FC element is w=w_1w_2...w_k where each w_i is the product of the elements in the left descent set of (w_iw_{i+1}...w_k), with the elements written in increasing numerical order. For example, the CF form for 4153765 in the group A7 is just 1473565. A useful fact is that if w is the reduced word of an FC element, then a generator s in w is in the left descent if and only if all generators to the left of that s commute with s, equivalently, if and only if no neighbor of s in the Coxeter graph appears to its left in w. We'll assume for now that the Coxeter graph is a straight line like in types A and B, so neighbors of s are exactly s-1 and s+1, if they exist. This simplification allows us to omit specifyin the Coxeter matrix.
def takeaway(t,x):
"""
Return the tuple obtained from l by removing the first occurence of x.
EXAMPLES:
sage: takeaway([1,2,5,2],2)
sage: [1,5,2]
"""
i = t.index(x)
return t[:i]+t[i+1:]
def is_left_descent(w, s, M):
"""
Determine if s is a left descent of a fc element w.
"""
if s not in w:
return False
else:
i = w.index(s)
return all(t not in w[:i] for t in neighbors_of(s,M))
def CartierFoata_form(w,M):
"""
Return the Cartier--Foata canonical form of an FC reduced word w.
EXAMPLES:
sage: CartierFoata_form((3,1,5,4))
sage: (1,3,5,4)
sage: CartierFoata_form((4,3,1,5))
sage: (1,4,3,5)
sage: CartierFoata_form((4,1,5,3,7,6,5))
sage: (1,4,7,3,5,6,5)
..NOTE:
The code will also work if w is input as a list (the output will still be a tuple).
"""
l = list(w)
cf_form=[]
while l != []:
ll = l
checked = set()
cf_factor=[]
for s in l:
if s not in checked:
ind = l.index(s)
if is_left_descent(w,s,M): # check if s is 'blocked'
cf_factor.append(s)
checked.add(s)
ll = takeaway(ll,s)
cf_form = cf_form + sorted(cf_factor)
l = ll
return tuple(cf_form)