# Enumeration and manipulation of fully commutative elements in Coxeter groups

Reported by: Owned by: gh-cemulate minor sage-9.2 combinatorics Coxeter systems, Coxeter groups, fully commutative elements gfeinberg, tscrim, combinat Chase Meadors, Tianyuan Xu Travis Scrimshaw N/A 56d093f 56d093fc63ec8117bf9fdbd1faf6a5fe979b6f7f

The purpose of the enhancement is to enumerate and manipulate the fully commutative (FC) elements in a Coxeter group. In particular, it accomplishes the goal of determining if a group element is FC set in Ticket: 25964.

The enhancement resides in one new file src/sage/combinat/fully_commutative_elements.py, with examples and tests included for every class and method. All other changes in this ticket concern only meta information such as bibliography references and documentation.

The file src/sage/combinat/fully_commutative_elements.py follows the standard parent-element pattern in Sage and contains two classes FullyCommutativeElements and FullyCommutativeElement, both introduced into the global namespace. Instances of FullyCommutativeElements are implemented in the category of enumerated sets and refined to either finite or infinite enumerated sets for groups associated to Cartan types. Each instance of FullyCommutativeElement is realized as a NormalizedClonableList via the Cartier--Foata canonical form of FC elements.

Basic usage for enumeration of FC elements in a Coxeter group.

sage: FCA3 = FullyCommutativeElements(['A', 3]); FCA3
Fully commutative elements in Coxeter system with Cartan type ['A', 3]
sage: FCA3.category()
Category of finite enumerated sets
sage: FCA3.list()
[[],
[1],
[2],
[3],
[2, 1],
[1, 3],
[1, 2],
[3, 2],
[2, 3],
[3, 2, 1],
[2, 1, 3],
[1, 3, 2],
[1, 2, 3],
[2, 1, 3, 2]]

sage: FCAffineA2 = FullyCommutativeElements(['A', 2, 1])
sage: FCAffineA2.category()
Category of infinite enumerated sets
sage: list(FCAffineA2.iterate_to_length(4))
[[], [0], [1], [2], [1, 0], [2, 0], [0, 1], [2, 1], [0, 2], [1,2], [2, 1, 0], [1, 2, 0], [2, 0, 1], [0, 2, 1], [1, 0, 2], [0, 1, 2], [0, 2, 1, 0], [0, 1, 2, 0], [1, 2, 0, 1], [1, 0, 2, 1], [2, 1,0, 2], [2, 0, 1, 2]]

sage: FCB8 = FullyCommutativeElements(['B', 8])
sage: len(FCB8)    # long time (7 seconds)
14299


Elements are verified to be FC upon construction, and FC element are normalized to Cartier--Foata canonical form:

sage: FCA3([1,2,1])
Traceback (most recent call last):
...
ValueError: The input is not a reduced word of a fully commutative element.

sage: FCA3([2, 3, 1, 2])
[2, 1, 3, 2]


In addition to the construction and enumeration of FC elements in a Coxeter group, various methods are developed for each FC element, including tools for studying the heap posets of FC elements and for computing Kazhdan and Lusztig's (generalized) star operations on FC elements. For more details, please see the attached documentation page built by Sage.

### comment:1 Changed 16 months ago by gh-cemulate

• Branch set to u/gh-cemulate/fully_commutatve_elements

### comment:2 Changed 16 months ago by gh-cemulate

• Authors set to Chase Meadors, Tianyuan Xu
• Commit set to f7811f380d55431fc8c197369d43165e1ef2de38
• Component changed from PLEASE CHANGE to combinatorics
• Description modified (diff)
• Keywords fully commutative coxeter CoxeterGroup added
• Priority changed from major to minor
• Type changed from PLEASE CHANGE to enhancement

### comment:3 Changed 16 months ago by gh-cemulate

• Description modified (diff)

### comment:4 Changed 16 months ago by git

• Commit changed from f7811f380d55431fc8c197369d43165e1ef2de38 to df550617f31c70803d26ea2711ae2c9b8cf6326e

Branch pushed to git repo; I updated commit sha1. New commits:

 ​df55061 Remove spaces before code block indicator

### comment:5 Changed 16 months ago by gh-cemulate

• Status changed from new to needs_review

### comment:6 Changed 16 months ago by gh-cemulate

• Summary changed from fully-commutatve-elements to Support for the enumeration and manipulation of fully commutative elements in a Coxeter system

### comment:7 Changed 16 months ago by git

• Commit changed from df550617f31c70803d26ea2711ae2c9b8cf6326e to f711d9a0d45761d80271668868c737a5457f3190

Branch pushed to git repo; I updated commit sha1. New commits:

 ​f711d9a Change error messages to match Python convention.

### comment:8 Changed 16 months ago by gh-cemulate

• Description modified (diff)

### comment:9 Changed 16 months ago by gh-TianyuanX

• Cc gfeinberg tscrim combinat added
• Description modified (diff)
• Keywords Coxeter systems Coxeter groups elements added; coxeter CoxeterGroup removed
• Summary changed from Support for the enumeration and manipulation of fully commutative elements in a Coxeter system to Enumeration and manipulation of fully commutative elements in Coxeter groups

### comment:10 Changed 16 months ago by git

• Commit changed from f711d9a0d45761d80271668868c737a5457f3190 to 42fb3ba13c74147b1b2944c50e84ac6833724771

Branch pushed to git repo; I updated commit sha1. New commits:

 ​40614a1 Remove unused variable ​455428f Rearrange some misplaced docs ​9044364 Have .check() not return anything ​191b7ab Refactor .check() and .is_fully_commutative() to be more semantically correct. ​dd94864 Add _repr_ for parent class ​42fb3ba Achieve 100% test coverage

### comment:11 Changed 16 months ago by gh-cemulate

• Description modified (diff)

### Changed 16 months ago by gh-cemulate

Built documentation page

### comment:12 Changed 16 months ago by tscrim

The test for an element of a Coxeter group to be fully commutative should also be available for the elements of a Coxeter group (probably in the category of CoxeterGroups).

FullyCommutativeElements should also be a UniqueRepresentation. So you will also need to implement

@staticmethod
def __classcall_private__(cls, data):
if isinstance(data, CoxeterMatrix):
matrix = data
else:
try:
t = CartanType(data)
except (TypeError, ValueError):
raise ValueError('input must be a CoxeterMatrix or data describing a Cartan/Coxeter type')
matrix = t.coxeter_matrix()
return super(cls, FullyCommutativeElements).__classcall__(cls, matrix)

def __init__(self, matrix):
self._matrix = matrix
# Rest of the initialization


I would also avoid the big try-except block in the __init__ to handle more cases more explicitly. (This will make it easier to maintain the code later on.) I think you should also see if the data can be passed to a CoxeterType, possibly first before CartanType.

### comment:13 Changed 16 months ago by tscrim

Actually, rather than the Coxeter matrix, it should have the Coxeter group as the input. That way it can immediately output the actual group elements (which can depend on your group's implementation) rather than converting from the reduced words. It just saves a little bit of time for the user, and I think that is more natural anyways.

Finally, I would add a hook in the category of Coxeter groups of

def fully_commutative_elements(self):
from sage.combinat.fully_commutative_elements import FullyCommutativeElements
return FullyCommutativeElements(self)


That way it is easy to access and more natural than having something in the global namespace (which I might even say should be removed).

### comment:14 Changed 16 months ago by gh-cemulate

I like the idea of exposing everything as CoxeterGroup.fully_commutative_elements(). And implementing UniqueRepresentation? should be fine as well. I'll get this done shortly.

The reason we differentiated the element class from the elements of a CoxeterGroup in the first place is that we're treating all of our operations and manipulations on elements as pure combinatorics on reduced words; depending on the implementation, calling .(from_)reduced_word() all the time can be quite expensive. Furthermore, we replace methods on CoxeterGroup elements such as has_descent and co. to provide specialized criterion that work on fully commutative reduced words, and add various other methods as well. This is why we ended up inventing a lightweight element class that simply represents "a canonical reduced word of a fully commutative element".

I agree it might be nice to be able to go seamlessly to an element of the ambient CoxeterGroup (if we're going to initialize with a CoxeterGroup as the "core data" instead of a matrix, which I like), but perhaps we could just have something like .element() that forgets the full commutivity and gives you a regular element of the CoxeterGroup?

### comment:15 Changed 16 months ago by git

• Commit changed from 42fb3ba13c74147b1b2944c50e84ac6833724771 to dfae2719f9e432fecd4a3e1bcc48347e184c8f82

Branch pushed to git repo; I updated commit sha1. New commits:

 ​da4d47d Add .type() accessor to CoxeterType ​a3d02fd Implement unique representation and redesign initialization a bit. ​403fca9 Remove FullyCommutativeElements from global namespace and expose through CoxeterGroup. ​dfae271 Fix tests to reflect new usage method.

### comment:16 Changed 16 months ago by git

• Commit changed from dfae2719f9e432fecd4a3e1bcc48347e184c8f82 to b1e3db538a332c1b8470d2176629e663b5e05ebb

Branch pushed to git repo; I updated commit sha1. New commits:

 ​b1e3db5 Add .group_element() method on fully commutative elements.

### comment:17 Changed 16 months ago by git

• Commit changed from b1e3db538a332c1b8470d2176629e663b5e05ebb to d4f53de6ef04cdc572aacad4e8dcc8bcb4bedca9

Branch pushed to git repo; I updated commit sha1. New commits:

 ​552fa0c Remove superfluous accessors ​1319113 Typo in rank for FC-finite classification. ​d4f53de Improve comments on category refinement.

### comment:18 Changed 16 months ago by git

• Commit changed from d4f53de6ef04cdc572aacad4e8dcc8bcb4bedca9 to 5cbdca30bbbc372c29b5f310f95acb15dac4d17a

Branch pushed to git repo; I updated commit sha1. New commits:

 ​5cbdca3 Improve unique representation test.

### comment:19 Changed 16 months ago by gh-cemulate

Alright, we've taken care of most of your suggestions:

• In implementing unique representation, we've gone ahead and moved to your idea of a CoxeterGroup being the "core data" that a FullyCommutativeElements is initialized from. In doing so, we've restructured the initialization and category refinement steps to be a little more straightforward. To do so we had to add a .type() accessor to CoxeterType? in combinat/root_system/coxeter_type.py, which was missing; hopefully that's no big deal. With this change we are now able to use CoxeterType throughout and not mention CartanType at all.
• We've removed FullyCommutativeElements from the global namespace; usage is now via CoxeterGroup(...).fully_commutative_elements(). All docs and tests have been changed to reflect this.
• We've added a .group_element() to get the plain Coxeter group element from a FullyCommutativeElement. This is in the spirit of your earlier suggestion that we should be able to "output the actual group element", although maybe not the "immediately" part: if w is an instance of FullyCommutativeElement, then w itself is not to be viewed as an element of a Coxeter group in the sense of categories/coxeter_group.py; as such, ElementMethods like cover_reflections() from sage.categories.coxeter_groups make sense for w.group_element() but not for w, while methods like .cartier_foata_form() or .star_operation() from fully_commutative_elements.py are defined for w but not for w.group_element(). The methods in coxeter_groups.py and fully_commutative_elements.py seem to overlap only in some code about descents (though coset_representative from the former and coset_decomposition from the latter are also related), so maybe it's fine to keep the two worlds somewhat separate? We are open to other schemes with more thorough integration. Please let us know what you think.

### comment:20 Changed 16 months ago by tscrim

Sorry for the delay in responding; last week was super busy for me.

Hmm.. I that is a fair point with wanting to abstract away the group and just consider reduced words themselves. I like this current implementation that takes the best of both words: the abstraction and the link with a specific implementation of the group.

Minor point, but can you use the standard copyright for Sage:

# This program is free software: you can redistribute it and/or modify
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
# ****************************************************************************


This is better in terms of the internal structure:

-class FullyCommutativeElements(Parent, UniqueRepresentation):
+class FullyCommutativeElements(UniqueRepresentation, Parent):


This check is not correct:

if not ctype.is_affine() or (family == 'F' and rank == 5) or (family == 'E' and rank == 9):


This says an indefinite type will have finitely many FC elements. This does not seem to match what is in the comment. I also don't like relying on is_affine() failing to check for indefinite type. That feels fragile.

The __iter__ can be simplified a bit:

         # To make the iterator deterministic, use a dictionary rather than a
# set, for the keys are then ordered by default by Python 3.7+:
recent_words = {empty_word: True}
yield empty_word
-        length = 1
-        while True:
+        while recent_words:
new_words = {}
-            for w in recent_words.keys():
+            for w in recent_words:
for s in letters:
if w.still_reduced_fc_after_prepending(s):
sw = self.element_class(
self, [s] + list(w), check=False)
# "Add" sw to the "set"
new_words[sw] = True
-            if len(new_words) == 0:
return
-            for w in new_words.keys():
+            for w in new_words:
yield w
recent_words = new_words
-            length += 1


It might also be good to say in the docstring that it iterates by increasing length.

In iterate_to_length, I would remove the assert length >= 0 since it doesn't match your docstring. Also

         INPUT:

-        - length -- integer; maximum length of element to generate.
+        - length -- integer; maximum length of element to generate


The .. PLOT:: is causing the docbuild to fail. I think you need to do this

         .. PLOT::
+            :width: 400 px

-            FC = FullyCommutativeElements(['B', 5])
+            FC = CoxeterGroup(['B', 5]).fully_commutative_elements()
g = FC([3,2,4,3,1]).plot_heap()
sphinx_plot(g)


### comment:21 Changed 16 months ago by git

• Commit changed from 5cbdca30bbbc372c29b5f310f95acb15dac4d17a to a50966505c8dab8063b07aef349c9b7b856c09ce

Branch pushed to git repo; I updated commit sha1. New commits:

 ​d0c4648 Fix docs ​a91e1ca Clean up iterator ​a509665 Standardize class inheritance; remove length assertion.

### comment:22 Changed 16 months ago by gh-cemulate

No problem, thanks for the comments. I've taken care of all the issues you mentioned and verified that the docs build successfully now.

### comment:23 Changed 16 months ago by git

• Commit changed from a50966505c8dab8063b07aef349c9b7b856c09ce to b174c03c6cccc39e46b316c132db286af78df461

Branch pushed to git repo; I updated commit sha1. New commits:

 ​b174c03 Patch up FC-finite/infinite detection.

### comment:24 Changed 16 months ago by gh-cemulate

We had forgotten to address your comment about the FC-finite/infinite check, but we think it's more sound now. We're not entirely sure what you mean by "indefinite type", but now we do not mention affine at all, as it shouldn't be necessary.

### comment:25 Changed 16 months ago by tscrim

An indefinite type is something that is not finite nor affine.

However, there are still issues with reducible types:

sage: CT = CoxeterType('F4xA2')
sage: CT.is_finite()
True
sage: CT.type()
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
...


An is_reducible method is missing from CoxeterType as well, but the output of type() will be 'reducible'.

You also forgot to fix the copyright statement.

Also, one docstring fix:

         def fully_commutative_elements(self):
r"""
-            Return the combinatorial class of fully commutative elements in this
-            Coxeter group. See
-            :class:~sage.combinat.fully_commutative_elements.FullyCommutativeElements
-            for details.
+            Return the set of fully commutative elements in this Coxeter group.
+
+            .. SEEALSO::
+
+                :class:~sage.combinat.fully_commutative_elements.FullyCommutativeElements


### comment:26 Changed 16 months ago by git

• Commit changed from b174c03c6cccc39e46b316c132db286af78df461 to 64f22b2cff0e12dde96e35a209b6ca13b197997b

Branch pushed to git repo; I updated commit sha1. New commits:

 ​fee70cb Fix docs ​64f22b2 Use standard Sage copyright

### comment:27 Changed 16 months ago by git

• Commit changed from 64f22b2cff0e12dde96e35a209b6ca13b197997b to b974c6ebdabf601a5e1c57cfc6eed1497489875f

Branch pushed to git repo; I updated commit sha1. New commits:

 ​b974c6e Redesign FC-finite/infinite detection

### comment:28 Changed 16 months ago by gh-cemulate

I've taken another stab at the category refinement step. I believe now it correctly refines to .Finite() or Infinite() in the finite or affine case, while not bothering / doing nothing in all remaining cases, which includes indefinite types and reducible types.

I hope I understood your previous comment correctly; the only issue I took away from that is that we may incorrectly report the finite/infinite status of reducible or indefinite types. I was able to construct the FC elements for various product types and manipulate them just fine without any exceptions being thrown (the exception in your example session does not happen for me, as the addition of CoxeterType.type() resolves that).

### comment:29 Changed 16 months ago by tscrim

I don't see why you cannot handle reducible types. I believe the FC elements in a reducible type is just the product of each of the FC elements in each irreducible component. So a reducible type is finite if and only if all of its irreducible components are finite. So how I would do it would be like this:

is_finite = True
if ctype.is_reducible():
ctype = ctype.component_types()
else:
ctype = [ctype]
for ct in ctype:
if not is_a_finite_FC_type(ct):
is_finite = False
break
if is_finite:
category = category.Finite()
else:
category = category.Infinite()


You would need to add a few more little features to CoxeterType that I should have already added (but didn't think about as I didn't need it for my application at that time). However, they are all basically just redirects.

### comment:30 Changed 16 months ago by git

• Commit changed from b974c6ebdabf601a5e1c57cfc6eed1497489875f to 46e732a9ad77f7275cbf857616091a7bfcc4a37e

Branch pushed to git repo; I updated commit sha1. New commits:

 ​0bd7911 Add more methods to CoxeterType ​46e732a Redesign FC-finite/infinite detection.

### comment:31 Changed 16 months ago by gh-cemulate

Ah, I see your point; agreed.

I've redone it again, this time I think it's more straightforward. I was also able to do away with the try-catch block, as CoxeterGroup.coxeter_type() will be a CoxeterMatrix (which inherits from CoxeterType) in the indefinite case, and a CoxeterType when it is able to be detected as such. I believe this is a sound approach but let me know if I've made any faulty assumptions.

### comment:32 Changed 16 months ago by tscrim

Thank you, it is looking a lot better. Now one last bit is you can simplify the checking for the infinite case by making it a positive statement:

if (not ctype.is_finite() and
not ((family == 'F' and rank == 5) or (family == 'E' and rank == 9))):
is_finite = False
break


IMO, this makes the code easier to read.

### comment:33 Changed 16 months ago by git

• Commit changed from 46e732a9ad77f7275cbf857616091a7bfcc4a37e to 8263261b82f6c436ccf09fe0ba0d936a9aa31c50

Branch pushed to git repo; I updated commit sha1. New commits:

 ​2bd2cb6 Make FC-finite/infinite detection more straightforward. ​8263261 Add tests for cardinality detection.

### comment:34 Changed 16 months ago by git

• Commit changed from 8263261b82f6c436ccf09fe0ba0d936a9aa31c50 to dd642fe4e334d98dba1c8ea37cf2e9802add78f4

Branch pushed to git repo; I updated commit sha1. New commits:

 ​dd642fe Remove extra comment.

### comment:35 Changed 16 months ago by gh-cemulate

OK, I've made it more straightforward and added some tests for the cardinality detection as well.

### comment:36 Changed 16 months ago by tscrim

Thank you. The finite detection looks great now. We are almost there.

I would just simply have normalize = cartier_foata_form rather than an explicit redirect. This becomes moot, but why is normalize even returning something when the cartier_foata_form returns nothing? Likewise moot, but you don't need to explicitly state the redirects.

I would hide still_reduced_fc_after_prepending by renaming it _still_reduced_fc_after_prepending as the user doesn't really need that method.

Instead of \mathbb{N}, you can use \NN, which is better for standardization within Sage. (This applies to anything similar, such at \ZZ and \QQ.)

Remove OPTIONAL ARGUMENTS:; just include them in the INPUT: block. Also, input items should not end with a period/full-stop. Additionally, please format as 'left'.

You probably want this:

             (3) t is left descent of the word u_3  obtained by
removing the leftmost s from u_2;
+
...
+
(m-1) the appropriate element in \{s, t\} is a left descent
of the word u_{m-1} obtained by removing the leftmost letter
required to be a descent in Condition (m-2) from u_{m-2}.


I think this is much more understandable on 1 line (and therefore worth breaking the 80 char/line guideline):

-            new_string = [other] + \
-                cur_string if side == 'left' else cur_string + [other]
+            new_string = [other] + cur_string if side == 'left' else cur_string + [other]


In the FullyCommutativeElements.__init__, I would make the doctests:

    def __init__(self, coxeter_group):
r"""
Initialize self.

EXAMPLES::

sage: from sage.combinat.fully_commutative_elements import FullyCommutativeElements
sage: FC = FullyCommutativeElements(CoxeterGroup(['H', 4]))
sage: TestSuite(FC).run()
"""


{{{#diff

sage: list(FCAffineA2.iterate_to_length(4))

• [[], [0], [1], [2], [1, 0], [2, 0], [0, 1], [2, 1], [0, 2], [1,
• 2], [2, 1, 0], [1, 2, 0], [2, 0, 1], [0, 2, 1], [1, 0, 2], [0, 1,
• 2], [0, 2, 1, 0], [0, 1, 2, 0], [1, 2, 0, 1], [1, 0, 2, 1], [2, 1,
• 0, 2], [2, 0, 1, 2]]

+ [[], [0], [1], [2], [1, 0], [2, 0], [0, 1], [2, 1], [0, 2], + [1, 2], [2, 1, 0], [1, 2, 0], [2, 0, 1], [0, 2, 1], [1, 0, 2], + [0, 1, 2], [0, 2, 1, 0], [0, 1, 2, 0], [1, 2, 0, 1], + [1, 0, 2, 1], [2, 1, 0, 2], [2, 0, 1, 2]] }}}

### comment:37 Changed 16 months ago by git

• Commit changed from dd642fe4e334d98dba1c8ea37cf2e9802add78f4 to 89705b006cf8a6e9ae3ee3d95fdca63c7bd17b97

Branch pushed to git repo; I updated commit sha1. New commits:

 ​a6e00aa Standardize TeX in docstrings ​a1b56af Make still_reduced_fc_after_prepending private. ​d4b00b6 Clean up docstrings ​84c3b00 Remove periods from input items in docstrings. ​4f37586 Don't break lines in ternary expressions. ​bc2c7db Use TestSuite for __init__ doctests. ​34e3d02 Clean up formatting on some long-output doctests ​df500f4 More string literals that should be in code delimiters. ​89705b0 Eliminate cartier_foata_form and just use normalize, instead of redirecting.

### comment:38 Changed 16 months ago by gh-cemulate

Alright, I believe I've addressed all your concerns. I did take care to fix other instances that were similar to the cases you pointed out when possible, and verified the docs build and look OK.

Per the first suggestion, I eliminated cartier_foata_form and simply kept normalize; I think we were concerned that "normalize" isn't very descriptive, but given that the docstring says exactly how we are normalizing, I don't think it's a problem.

### comment:39 Changed 16 months ago by tscrim

Thank you. Last tidbits:

The docstring of normalize is not correct because you are not returning anything. I would say

Mutate self into Cartier-Foata normal form.


(the single hyphen is because this doesn't convert it to latex, sadly).

I missed this when reading the docstring, but in coset_decomposition, I would change l to \ell so there is no possible confusion with 1 or I (plus IMO, the latex version is prettier).

### comment:40 Changed 16 months ago by git

• Commit changed from 89705b006cf8a6e9ae3ee3d95fdca63c7bd17b97 to 5d3a953a8cc268bf22dbd2eff18dfd8afce8ffa4

Branch pushed to git repo; I updated commit sha1. New commits:

 ​5d3a953 More docstring tweaks

### comment:41 Changed 16 months ago by gh-cemulate

I agree; taken care of.

### comment:42 Changed 16 months ago by tscrim

• Reviewers set to Travis Scrimshaw
• Status changed from needs_review to positive_review

Thank you.

### comment:43 Changed 16 months ago by gh-cemulate

No problem, thanks a lot for your time and effort; we appreciate the feedback! This will help inform us on code style and Sage convention for any future submissions.

### comment:44 Changed 15 months ago by git

• Commit changed from 5d3a953a8cc268bf22dbd2eff18dfd8afce8ffa4 to 56d093fc63ec8117bf9fdbd1faf6a5fe979b6f7f
• Status changed from positive_review to needs_review

Branch pushed to git repo; I updated commit sha1 and set ticket back to needs_review. New commits:

 ​56d093f Fix pyflakes failure

### comment:45 Changed 15 months ago by gh-cemulate

I checked the test failures and fixed a pyflakes error.

### comment:46 Changed 15 months ago by tscrim

• Status changed from needs_review to positive_review

### comment:47 Changed 15 months ago by vbraun

• Branch changed from u/gh-cemulate/fully_commutatve_elements to 56d093fc63ec8117bf9fdbd1faf6a5fe979b6f7f
• Resolution set to fixed
• Status changed from positive_review to closed
Note: See TracTickets for help on using tickets.