Ticket #14141: trac_14141-knutson_tao_puzzles-as.patch

File trac_14141-knutson_tao_puzzles-as.patch, 73.8 KB (added by aschilling, 8 years ago)
  • doc/en/reference/combinat/index.rst

    # HG changeset patch
    # User Anne Schilling <anne@math.ucdavis.edu>
    # Date 1366412824 14400
    # Node ID 90c8e21b0db27b21accc92309fe81edec31c6d82
    # Parent 8b0e35a21e1135d03571be4cf14b7a32cf28372b
    #14141: Implementation of Knutson-Tao puzzles
    
    diff --git a/doc/en/reference/combinat/index.rst b/doc/en/reference/combinat/index.rst
    a b Combinatorics 
    6767   root_systems
    6868
    6969   sage/combinat/kazhdan_lusztig
     70   sage/combinat/knutson_tao_puzzles
    7071
    7172   crystals
    7273   posets
  • sage/combinat/all.py

    diff --git a/sage/combinat/all.py b/sage/combinat/all.py
    a b from cyclic_sieving_phenomenon import Cy 
    133133
    134134from sidon_sets import sidon_sets
    135135
     136# Puzzles
     137from knutson_tao_puzzles import KnutsonTaoPuzzleSolver
     138
    136139# Gelfand-Tsetlin patterns
    137140from gelfand_tsetlin_patterns import GelfandTsetlinPattern, GelfandTsetlinPatterns
    138141
  • new file sage/combinat/knutson_tao_puzzles.py

    diff --git a/sage/combinat/knutson_tao_puzzles.py b/sage/combinat/knutson_tao_puzzles.py
    new file mode 100644
    - +  
     1r"""
     2Knutson-Tao Puzzles
     3
     4This module implements a generic algorithm to solve Knutson-Tao puzzles. An
     5instance of this class will be callable: the arguments are the labels of
     6north-east and north-west sides of the puzzle boundary; the output is the list
     7of the fillings of the puzzle with the specified pieces.
     8
     9Acknowledgements
     10----------------
     11
     12This code was written during Sage Days 45 at ICERM with Franco Saliola, Anne Schilling, and Avinash Dalal in discussions with Allen Knutson.
     13The code was tested afterwards by Liz Beazley and Ed Richmond.
     14
     15.. TODO::
     16
     17    Functionality to add:
     18
     19    - plotter will not plot edge labels higher than 2; e.g. in BK puzzles, the labels are
     20      1,..., n and so in 3-step examples, none of the edge labels with 3 appear
     21
     22    - we should also have a 3-step puzzle pieces constructor, taken from p22 of
     23      :arXiv:`math/0610538`
     24
     25    - implement the bijection from puzzles to tableaux; see for example
     26      R. Vakil, A geometric Littlewood-Richardson rule, :arXiv:`math/0302294`
     27      or K. Purbhoo, Puzzles, Tableaux and Mosaics, :arXiv:`0705.1184`.
     28"""
     29#*****************************************************************************
     30#       Copyright (C) 2013 Franco Saliola <saliola@gmail.com>,
     31#                     2013 Allen Knutson,
     32#                     2013 Avinash Dalal,
     33#                     2013 Anne Schilling,
     34#                     2013 Elizabeth Beazley,
     35#                     2013 Ed Richmond
     36#
     37#  Distributed under the terms of the GNU General Public License (GPL)
     38#                  http://www.gnu.org/licenses/
     39#*****************************************************************************
     40from sage.combinat.free_module import CombinatorialFreeModule
     41from sage.plot.graphics import Graphics
     42from sage.plot.polygon import polygon
     43from sage.plot.line import line
     44from sage.plot.text import text
     45from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
     46from sage.rings.finite_rings.integer_mod_ring import Integers
     47from sage.plot.plot import graphics_array
     48from sage.combinat.words.word import Word
     49from sage.misc.cachefunc import cached_method
     50from sage.categories.algebras_with_basis import AlgebrasWithBasis
     51from sage.structure.unique_representation import UniqueRepresentation
     52from sage.combinat.skew_tableau import SkewTableau
     53
     54class PuzzlePiece(object):
     55    r"""
     56    Abstract class for puzzle pieces.
     57
     58    This abstract class contains information on how to test equality of
     59    puzzle pieces, and sets color and plotting options.
     60    """
     61
     62    def __eq__(self, other):
     63        r"""
     64        TESTS::
     65
     66            sage: from sage.combinat.knutson_tao_puzzles import DeltaPiece
     67            sage: delta = DeltaPiece('a','b','c')
     68            sage: delta1 = DeltaPiece('a','b','c')
     69            sage: delta == delta1
     70            True
     71            sage: delta1 = DeltaPiece('A','b','c')
     72            sage: delta == delta1
     73            False
     74        """
     75        if isinstance(other, type(self)):
     76            return self.border() == other.border()
     77        else:
     78            return False
     79
     80    def __hash__(self):
     81        r"""
     82        TESTS::
     83
     84            sage: from sage.combinat.knutson_tao_puzzles import DeltaPiece
     85            sage: delta = DeltaPiece('a','b','c')
     86            sage: hash(delta) == hash(delta)
     87            True
     88        """
     89        return hash((type(self), self.border()))
     90
     91    def border(self):
     92        r"""
     93        Returns the border of ``self``.
     94
     95        EXAMPLES::
     96
     97            sage: from sage.combinat.knutson_tao_puzzles import DeltaPiece
     98            sage: delta = DeltaPiece('a','b','c')
     99            sage: delta.border()
     100            ('a', 'b', 'c')
     101        """
     102        return tuple(self.edge_label(edge) for edge in self.edges())
     103
     104    def color(self):
     105        r"""
     106        Returns the color of ``self``.
     107
     108        EXAMPLES::
     109
     110            sage: from sage.combinat.knutson_tao_puzzles import DeltaPiece
     111            sage: delta = DeltaPiece('a','b','c')
     112            sage: delta.color()
     113            'white'
     114            sage: delta = DeltaPiece('0','0','0')
     115            sage: delta.color()
     116            'red'
     117            sage: delta = DeltaPiece('1','1','1')
     118            sage: delta.color()
     119            'blue'
     120            sage: delta = DeltaPiece('2','2','2')
     121            sage: delta.color()
     122            'green'
     123            sage: delta = DeltaPiece('2','K','2')
     124            sage: delta.color()
     125            'orange'
     126            sage: delta = DeltaPiece('2','T1/2','2')
     127            sage: delta.color()
     128            'yellow'
     129        """
     130        colors = {('0','0','0'): 'red',
     131                  ('1','1','1'): 'blue',
     132                  ('2','2','2'): 'green'}
     133        border = self.border()
     134        if border in colors:
     135            color = colors[border]
     136        elif 'K' in border:
     137            color = 'orange'
     138        elif '10' in border:
     139            color = 'white'
     140        elif any(label.startswith('T') for label in border):
     141            color = 'yellow'
     142        else:
     143            color = 'white'
     144        return color
     145
     146    def _plot_label(self, label, coords, fontcolor=(0.3,0.3,0.3), fontsize=15, rotation=0):
     147        r"""
     148        TESTS::
     149
     150            sage: from sage.combinat.knutson_tao_puzzles import DeltaPiece
     151            sage: delta = DeltaPiece('2','K','2')
     152            sage: delta._plot_label('1',(1,1))    # not tested
     153        """
     154        if label in ('0', '1', '2'):
     155            return text(label, coords, color=fontcolor, fontsize=fontsize, rotation=rotation)
     156        else:
     157            return Graphics()
     158
     159    def _plot_piece(self, coords, border_color=(0.5,0.5,0.5), border_thickness=1, style='fill'):
     160        r"""
     161        TESTS::
     162
     163            sage: from sage.combinat.knutson_tao_puzzles import DeltaPiece
     164            sage: delta = DeltaPiece('2','K','2')
     165            sage: delta._plot_piece([(1,1),(1,2),(2,2)]) # not tested
     166        """
     167        if style == 'fill':
     168            P = polygon(coords, color=self.color())
     169            P += polygon(coords, fill=False, color=border_color, thickness=border_thickness)
     170            return P
     171        elif style == 'edges':
     172            if isinstance(self, DeltaPiece):
     173                edges = ('north_west', 'south', 'north_east')
     174            elif isinstance(self, NablaPiece):
     175                edges = ('south_west', 'north', 'south_east')
     176            else:
     177                edges = self.edges()
     178            P = Graphics()
     179            for (i, edge) in enumerate(edges):
     180                P += line([coords[i], coords[(i+1)%3]], color=self.edge_color(edge), thickness=border_thickness)
     181            return P
     182        else:
     183            return NotImplemented
     184
     185    def edge_color(self, edge):
     186        r"""
     187        Color of the specified edge of ``self`` (to be used when plotting the
     188        piece).
     189
     190        EXAMPLES::
     191
     192            sage: from sage.combinat.knutson_tao_puzzles import DeltaPiece
     193            sage: delta = DeltaPiece('1','0','10')
     194            sage: delta.edge_color('south')
     195            'blue'
     196            sage: delta.edge_color('north_west')
     197            'red'
     198            sage: delta.edge_color('north_east')
     199            'white'
     200        """
     201        edge_label = self.edge_label(edge)
     202        colors = {'1': 'blue', '0': 'red'}
     203        if edge_label in colors:
     204            color = colors[edge_label]
     205        elif 'K' in edge_label:
     206            color = 'orange'
     207        elif edge_label.startswith('T'):
     208            color = 'yellow'
     209        else:
     210            color = 'white'
     211        return color
     212
     213    def edge_label(self, edge):
     214        r"""
     215        Return the edge label of ``edge``.
     216
     217        EXAMPLES::
     218
     219            sage: from sage.combinat.knutson_tao_puzzles import DeltaPiece
     220            sage: delta = DeltaPiece('2','K','2')
     221            sage: delta.edge_label('south')
     222            '2'
     223            sage: delta.edge_label('north_east')
     224            '2'
     225            sage: delta.edge_label('north_west')
     226            'K'
     227        """
     228        return self._edge_labels[edge]
     229
     230    __getitem__ = edge_label
     231
     232class NablaPiece(PuzzlePiece):
     233    r"""
     234    Nabla Piece takes as input three labels, inputted as strings. They label
     235    the North, Southeast and Southwest edges, respectively.
     236
     237    EXAMPLES::
     238
     239        sage: from sage.combinat.knutson_tao_puzzles import NablaPiece
     240        sage: NablaPiece('a','b','c')
     241        c\a/b
     242    """
     243
     244    def __init__(self, north, south_east, south_west):
     245        r"""
     246        INPUT:
     247
     248        - ``north``, ``south_east``, ``south_west`` -- strings, which label the edges
     249
     250        EXAMPLES::
     251
     252            sage: from sage.combinat.knutson_tao_puzzles import NablaPiece
     253            sage: NablaPiece('1','2','3')
     254            3\1/2
     255        """
     256        self._edge_labels = dict(north=north, south_east=south_east, south_west=south_west)
     257
     258    def __repr__(self):
     259        r"""
     260        Prints the labels of the Nabla piece.
     261
     262        EXAMPLES::
     263
     264            sage: from sage.combinat.knutson_tao_puzzles import NablaPiece
     265            sage: NablaPiece('1','2','3')
     266            3\1/2
     267        """
     268        return "%s\%s/%s" % (self['south_west'],
     269                             self['north'],
     270                             self['south_east'])
     271
     272    def clockwise_rotation(self):
     273        r"""
     274        Rotates the Nabla piece by 120 degree clockwise.
     275
     276        OUTPUT:
     277
     278        - Nabla piece
     279
     280        EXAMPLES::
     281
     282            sage: from sage.combinat.knutson_tao_puzzles import NablaPiece
     283            sage: nabla = NablaPiece('1','2','3')
     284            sage: nabla.clockwise_rotation()
     285            2\3/1
     286        """
     287        return NablaPiece(north=self['south_west'],
     288                          south_east=self['north'],
     289                          south_west=self['south_east'])
     290
     291    def half_turn_rotation(self):
     292        r"""
     293        Rotates the Nabla piece by 180 degree.
     294
     295        OUTPUT:
     296
     297        - Delta piece
     298
     299        EXAMPLES::
     300
     301            sage: from sage.combinat.knutson_tao_puzzles import NablaPiece
     302            sage: nabla = NablaPiece('1','2','3')
     303            sage: nabla.half_turn_rotation()
     304            2/1\3
     305        """
     306        return DeltaPiece(south=self['north'],
     307                          north_west=self['south_east'],
     308                          north_east=self['south_west'])
     309
     310    def edges(self):
     311        r"""
     312        Return the tuple of edge names.
     313
     314        EXAMPLES::
     315
     316            sage: from sage.combinat.knutson_tao_puzzles import NablaPiece
     317            sage: nabla = NablaPiece('1','2','3')
     318            sage: nabla.edges()
     319            ('north', 'south_east', 'south_west')
     320        """
     321        return ('north', 'south_east', 'south_west')
     322
     323class DeltaPiece(PuzzlePiece):
     324    r"""
     325    Delta Piece takes as input three labels, inputted as strings. They label
     326    the South, Northwest and Northeast edges, respectively.
     327
     328    EXAMPLES::
     329
     330        sage: from sage.combinat.knutson_tao_puzzles import DeltaPiece
     331        sage: DeltaPiece('a','b','c')
     332        b/a\c
     333    """
     334
     335    def __init__(self, south, north_west, north_east):
     336        r"""
     337        INPUT:
     338
     339        - ``south``, ``north_west``, ``north_east`` -- strings, which label the edges
     340
     341        EXAMPLES::
     342
     343            sage: from sage.combinat.knutson_tao_puzzles import DeltaPiece
     344            sage: DeltaPiece('1','2','3')
     345            2/1\3
     346        """
     347        self._edge_labels = dict(south=south, north_west=north_west, north_east=north_east)
     348
     349    def __repr__(self):
     350        r"""
     351        Prints the labels of the Delta piece.
     352
     353        EXAMPLES::
     354
     355            sage: from sage.combinat.knutson_tao_puzzles import DeltaPiece
     356            sage: DeltaPiece('1','2','3')
     357            2/1\3
     358        """
     359        return "%s/%s\%s" % (self['north_west'],
     360                             self['south'],
     361                             self['north_east'])
     362
     363    def clockwise_rotation(self):
     364        r"""
     365        Rotates the Delta piece by 120 degree clockwise.
     366
     367        OUTPUT:
     368
     369        - Delta piece
     370
     371        EXAMPLES::
     372
     373            sage: from sage.combinat.knutson_tao_puzzles import DeltaPiece
     374            sage: delta = DeltaPiece('1','2','3')
     375            sage: delta.clockwise_rotation()
     376            1/3\2
     377        """
     378        return DeltaPiece(south=self['north_east'],
     379                          north_west=self['south'],
     380                          north_east=self['north_west'])
     381
     382    def half_turn_rotation(self):
     383        r"""
     384        Rotates the Delta piece by 180 degree.
     385
     386        OUTPUT:
     387
     388        - Nabla piece
     389
     390        EXAMPLES::
     391
     392            sage: from sage.combinat.knutson_tao_puzzles import DeltaPiece
     393            sage: delta = DeltaPiece('1','2','3')
     394            sage: delta.half_turn_rotation()
     395            3\1/2
     396        """
     397        return NablaPiece(north=self['south'],
     398                          south_east=self['north_west'],
     399                          south_west=self['north_east'])
     400
     401    def edges(self):
     402        r"""
     403        Return the tuple of edge names.
     404
     405        EXAMPLES::
     406
     407            sage: from sage.combinat.knutson_tao_puzzles import DeltaPiece
     408            sage: delta = DeltaPiece('1','2','3')
     409            sage: delta.edges()
     410            ('south', 'north_west', 'north_east')
     411        """
     412        return ('south', 'north_west', 'north_east')
     413
     414class RhombusPiece(PuzzlePiece):
     415    r"""
     416    Class of rhombi pieces.
     417
     418    To construct a rhombus piece we input a delta and a nabla piece.
     419    The delta and nabla pieces are joined along the south and north edge,
     420    respectively.
     421
     422    EXAMPLES::
     423
     424        sage: from sage.combinat.knutson_tao_puzzles import DeltaPiece, NablaPiece, RhombusPiece
     425        sage: delta = DeltaPiece('1','2','3')
     426        sage: nabla = NablaPiece('4','5','6')
     427        sage: RhombusPiece(delta,nabla)
     428        2/\3  6\/5
     429    """
     430    def __init__(self, north_piece, south_piece):
     431        r"""
     432        EXAMPLES::
     433
     434            sage: from sage.combinat.knutson_tao_puzzles import DeltaPiece, NablaPiece, RhombusPiece
     435            sage: delta = DeltaPiece('1','2','3')
     436            sage: nabla = NablaPiece('4','5','6')
     437            sage: RhombusPiece(delta,nabla)
     438            2/\3  6\/5
     439        """
     440        self._north_piece = north_piece
     441        self._south_piece = south_piece
     442        self._edge_labels = dict(north_west=north_piece['north_west'],
     443                                 north_east=north_piece['north_east'],
     444                                 south_east=south_piece['south_east'],
     445                                 south_west=south_piece['south_west'])
     446
     447    def __iter__(self):
     448        r"""
     449        Return the list of the north and south piece.
     450
     451        EXAMPLES::
     452
     453            sage: from sage.combinat.knutson_tao_puzzles import DeltaPiece, NablaPiece, RhombusPiece
     454            sage: delta = DeltaPiece('1','2','3')
     455            sage: nabla = NablaPiece('4','5','6')
     456            sage: r = RhombusPiece(delta,nabla)
     457            sage: list(r)
     458            [2/1\3, 6\4/5]
     459        """
     460        yield self._north_piece
     461        yield self._south_piece
     462
     463    def north_piece(self):
     464        r"""
     465        Return the north piece.
     466
     467        EXAMPLES::
     468
     469            sage: from sage.combinat.knutson_tao_puzzles import DeltaPiece, NablaPiece, RhombusPiece
     470            sage: delta = DeltaPiece('1','2','3')
     471            sage: nabla = NablaPiece('4','5','6')
     472            sage: r = RhombusPiece(delta,nabla)
     473            sage: r.north_piece()
     474            2/1\3
     475        """
     476        return self._north_piece
     477
     478    def south_piece(self):
     479        r"""
     480        Return the south piece.
     481
     482        EXAMPLES::
     483
     484            sage: from sage.combinat.knutson_tao_puzzles import DeltaPiece, NablaPiece, RhombusPiece
     485            sage: delta = DeltaPiece('1','2','3')
     486            sage: nabla = NablaPiece('4','5','6')
     487            sage: r = RhombusPiece(delta,nabla)
     488            sage: r.south_piece()
     489            6\4/5
     490        """
     491        return self._south_piece
     492
     493    def __repr__(self):
     494        r"""
     495        EXAMPLES::
     496
     497            sage: from sage.combinat.knutson_tao_puzzles import DeltaPiece, NablaPiece, RhombusPiece
     498            sage: delta = DeltaPiece('1','2','3')
     499            sage: nabla = NablaPiece('4','5','6')
     500            sage: RhombusPiece(delta,nabla)
     501            2/\3  6\/5
     502        """
     503        return "%s/\%s  %s\/%s" % (self['north_west'], self['north_east'],
     504                                   self['south_west'], self['south_east'])
     505
     506    def edges(self):
     507        r"""
     508        Return the tuple of edge names.
     509
     510        EXAMPLES::
     511
     512            sage: from sage.combinat.knutson_tao_puzzles import DeltaPiece, NablaPiece, RhombusPiece
     513            sage: delta = DeltaPiece('1','2','3')
     514            sage: nabla = NablaPiece('4','5','6')
     515            sage: RhombusPiece(delta,nabla).edges()
     516            ('north_west', 'north_east', 'south_east', 'south_west')
     517        """
     518        return ('north_west', 'north_east', 'south_east', 'south_west')
     519
     520class PuzzlePieces(object):
     521    r"""
     522    Construct a valid set of puzzle pieces.
     523
     524    This class constructs the set of valid puzzle pieces. It can take a list of
     525    forbidden border labels as input. These labels are forbidden from appearing
     526    on the south edge of a puzzle filling. The user can add valid nabla or
     527    delta pieces and specify which rotations of these pieces are legal. For
     528    example, ``rotations=0`` does not add any additional pieces (only the piece
     529    itself), ``rotations=60`` adds six pieces (the pieces and its rotations by
     530    60, 120, 180, 240, 300), etc..
     531
     532    EXAMPLES::
     533
     534        sage: from sage.combinat.knutson_tao_puzzles import PuzzlePieces, NablaPiece
     535        sage: forbidden_border_labels = ['10']
     536        sage: pieces = PuzzlePieces(forbidden_border_labels)
     537        sage: pieces.add_piece(NablaPiece('0','0','0'), rotations=60)
     538        sage: pieces.add_piece(NablaPiece('1','1','1'), rotations=60)
     539        sage: pieces.add_piece(NablaPiece('1','0','10'), rotations=60)
     540        sage: pieces
     541        Nablas : [0\0/0, 0\10/1, 10\1/0, 1\0/10, 1\1/1]
     542        Deltas : [0/0\0, 0/1\10, 1/10\0, 1/1\1, 10/0\1]
     543
     544    The user can obtain the list of valid rhombi pieces as follows::
     545
     546        sage: sorted([p for p in pieces.rhombus_pieces()], key=str)
     547        [0/\0  0\/0, 0/\0  1\/10, 0/\10  10\/0, 0/\10  1\/1, 1/\0  0\/1,
     548        1/\1  10\/0, 1/\1  1\/1, 10/\1  0\/0, 10/\1  1\/10]
     549    """
     550    def __init__(self, forbidden_border_labels=None):
     551        r"""
     552        INPUT:
     553
     554        - ``forbidden_border_labels`` -- list of forbidden border labels given as strings
     555
     556        TESTS::
     557
     558            sage: from sage.combinat.knutson_tao_puzzles import PuzzlePieces
     559            sage: forbidden_border_labels = ['10']
     560            sage: pieces = PuzzlePieces(forbidden_border_labels)
     561            sage: pieces
     562            Nablas : []
     563            Deltas : []
     564
     565            sage: PuzzlePieces('10')
     566            Traceback (most recent call last):
     567            ...
     568            TypeError: Input must be a list
     569        """
     570        self._nabla_pieces = set([])
     571        self._delta_pieces = set([])
     572        if forbidden_border_labels is None:
     573            forbidden_border_labels = []
     574        if not isinstance(forbidden_border_labels,list):
     575            raise TypeError("Input must be a list")
     576        self._forbidden_border_labels = forbidden_border_labels
     577
     578    def __eq__(self, other):
     579        r"""
     580        TESTS::
     581
     582            sage: from sage.combinat.knutson_tao_puzzles import H_grassmannian_pieces
     583            sage: x = H_grassmannian_pieces()
     584            sage: y = H_grassmannian_pieces()
     585            sage: x == y
     586            True
     587        """
     588        if isinstance(other, type(self)):
     589            return self.__dict__ == other.__dict__
     590        else:
     591            return False
     592
     593    def __hash__(self):
     594        r"""
     595        TESTS::
     596
     597            sage: from sage.combinat.knutson_tao_puzzles import H_grassmannian_pieces
     598            sage: x = H_grassmannian_pieces()
     599            sage: hash(x) == hash(x)
     600            True
     601        """
     602        return hash((type(self), self.__repr__()))
     603
     604    def add_piece(self, piece, rotations=120):
     605        r"""
     606        Adds ``piece`` to the list of pieces.
     607
     608        INPUT:
     609
     610        - ``piece`` -- a nabla piece or a delta piece
     611        - ``rotations`` -- (default: 120) 0, 60, 120, 180
     612
     613        The user can add valid nabla or delta pieces and specify
     614        which rotations of these pieces are legal. For example, ``rotations=0``
     615        does not add any additional pieces (only the piece itself), ``rotations=60`` adds
     616        six pieces (namely three delta and three nabla pieces), while
     617        ``rotations=120`` adds only delta or nabla (depending on which piece ``self`` is).
     618        ``rotations=180`` adds the piece and its 180 degree rotation, i.e. one delta and one
     619        nabla piece.
     620
     621        EXAMPLES::
     622
     623            sage: from sage.combinat.knutson_tao_puzzles import PuzzlePieces, DeltaPiece
     624            sage: delta = DeltaPiece('a','b','c')
     625            sage: pieces = PuzzlePieces()
     626            sage: pieces
     627            Nablas : []
     628            Deltas : []
     629            sage: pieces.add_piece(delta)
     630            sage: pieces
     631            Nablas : []
     632            Deltas : [a/c\b, b/a\c, c/b\a]
     633
     634            sage: pieces = PuzzlePieces()
     635            sage: pieces.add_piece(delta,rotations=0)
     636            sage: pieces
     637            Nablas : []
     638            Deltas : [b/a\c]
     639
     640            sage: pieces = PuzzlePieces()
     641            sage: pieces.add_piece(delta,rotations=60)
     642            sage: pieces
     643            Nablas : [a\b/c, b\c/a, c\a/b]
     644            Deltas : [a/c\b, b/a\c, c/b\a]
     645        """
     646        if isinstance(piece, NablaPiece):
     647            pieces_list = self._nabla_pieces
     648        else:
     649            pieces_list = self._delta_pieces
     650        pieces_list.add(piece)
     651        if rotations == 120:
     652            pieces_list.add(piece.clockwise_rotation())
     653            pieces_list.add(piece.clockwise_rotation().clockwise_rotation())
     654        elif rotations == 180:
     655            self.add_piece(piece.half_turn_rotation(), rotations=0)
     656        elif rotations == 60:
     657            self.add_piece(piece, rotations=120)
     658            self.add_piece(piece.half_turn_rotation(), rotations=120)
     659
     660    def add_forbidden_label(self, label):
     661        r"""
     662        Adds forbidden border labels.
     663
     664        INPUT:
     665
     666        - ``label`` -- string specifying a new forbidden label
     667
     668        EXAMPLES::
     669
     670            sage: from sage.combinat.knutson_tao_puzzles import PuzzlePieces
     671            sage: pieces = PuzzlePieces()
     672            sage: pieces.add_forbidden_label('1')
     673            sage: pieces._forbidden_border_labels
     674            ['1']
     675            sage: pieces.add_forbidden_label('2')
     676            sage: pieces._forbidden_border_labels
     677            ['1', '2']
     678        """
     679        self._forbidden_border_labels.append(label)
     680
     681    def add_T_piece(self, label1, label2):
     682        r"""
     683        Adds a nabla and delta piece with ``label1`` and ``label2``.
     684
     685        This method adds a nabla piece with edges ``label2``\ T``label1``|``label2`` / ``label1``.
     686        and a delta piece with edges ``label1``/ T``label1``|``label2`` \ ``label2``.
     687        It also adds T``label1``|``label2`` to the forbidden list.
     688
     689        EXAMPLES::
     690
     691            sage: from sage.combinat.knutson_tao_puzzles import PuzzlePieces
     692            sage: pieces = PuzzlePieces()
     693            sage: pieces.add_T_piece('1','3')
     694            sage: pieces
     695            Nablas : [3\T1|3/1]
     696            Deltas : [1/T1|3\3]
     697            sage: pieces._forbidden_border_labels
     698            ['T1|3']
     699        """
     700        self.add_forbidden_label('T%s|%s' % (label1, label2))
     701        self.add_piece(NablaPiece('T%s|%s' % (label1, label2), label1, label2), rotations=180)
     702
     703    def __repr__(self):
     704        r"""
     705        TESTS::
     706
     707            sage: from sage.combinat.knutson_tao_puzzles import PuzzlePieces, DeltaPiece
     708            sage: pieces = PuzzlePieces()
     709            sage: delta = DeltaPiece('a','b','c')
     710            sage: pieces.add_piece(delta,rotations=60)
     711            sage: pieces
     712            Nablas : [a\b/c, b\c/a, c\a/b]
     713            Deltas : [a/c\b, b/a\c, c/b\a]
     714        """
     715        s = "Nablas : %s\n" % sorted([p for p in self._nabla_pieces], key=str)
     716        s += "Deltas : %s" % sorted([p for p in self._delta_pieces], key=str)
     717        return s
     718
     719    def delta_pieces(self):
     720        r"""
     721        Returns the delta pieces as a set.
     722
     723        EXAMPLES::
     724
     725            sage: from sage.combinat.knutson_tao_puzzles import PuzzlePieces, DeltaPiece
     726            sage: pieces = PuzzlePieces()
     727            sage: delta = DeltaPiece('a','b','c')
     728            sage: pieces.add_piece(delta,rotations=60)
     729            sage: sorted([p for p in pieces.delta_pieces()], key=str)
     730            [a/c\b, b/a\c, c/b\a]
     731        """
     732        return self._delta_pieces
     733
     734    def nabla_pieces(self):
     735        r"""
     736        Returns the nabla pieces as a set.
     737
     738        EXAMPLES::
     739
     740            sage: from sage.combinat.knutson_tao_puzzles import PuzzlePieces, DeltaPiece
     741            sage: pieces = PuzzlePieces()
     742            sage: delta = DeltaPiece('a','b','c')
     743            sage: pieces.add_piece(delta,rotations=60)
     744            sage: sorted([p for p in pieces.nabla_pieces()], key=str)
     745            [a\b/c, b\c/a, c\a/b]
     746        """
     747        return self._nabla_pieces
     748
     749    def rhombus_pieces(self):
     750        r"""
     751        Returns a list of all allowable rhombus pieces.
     752
     753        Allowable rhombus pieces are those where the south edge of the delta
     754        piece equals the north edge of the nabla piece.
     755
     756        EXAMPLES::
     757
     758            sage: from sage.combinat.knutson_tao_puzzles import PuzzlePieces, DeltaPiece
     759            sage: pieces = PuzzlePieces()
     760            sage: delta = DeltaPiece('a','b','c')
     761            sage: pieces.add_piece(delta,rotations=60)
     762            sage: sorted([p for p in pieces.rhombus_pieces()], key=str)
     763            [a/\b  b\/a, b/\c  c\/b, c/\a  a\/c]
     764        """
     765        rhombi = set([])
     766        for nabla in self._nabla_pieces:
     767            for delta in self._delta_pieces:
     768                if delta['south'] == nabla['north']:
     769                    rhombi.add(RhombusPiece(delta, nabla))
     770        return rhombi
     771
     772    def boundary_deltas(self):
     773        r"""
     774        Returns deltas with south edges not in the forbidden list.
     775
     776        EXAMPLES::
     777
     778            sage: from sage.combinat.knutson_tao_puzzles import PuzzlePieces, DeltaPiece
     779            sage: pieces = PuzzlePieces(['a'])
     780            sage: delta = DeltaPiece('a','b','c')
     781            sage: pieces.add_piece(delta,rotations=60)
     782            sage: sorted([p for p in pieces.boundary_deltas()], key=str)
     783            [a/c\b, c/b\a]
     784        """
     785        return tuple(delta for delta in self.delta_pieces()
     786                    if delta['south'] not in self._forbidden_border_labels)
     787
     788def H_grassmannian_pieces():
     789    r"""
     790    Defines the puzzle pieces used in computing the cohomology of the Grassmannian.
     791
     792    REFERENCES:
     793
     794    .. [KTW] Allen Knutson, Terence Tao, Christopher Woodward,
     795       The honeycomb model of GL(n) tensor products II: Puzzles determine facets of the Littlewood-Richardson cone,
     796       :arXiv:`math/0107011`
     797
     798    EXAMPLES::
     799
     800        sage: from sage.combinat.knutson_tao_puzzles import H_grassmannian_pieces
     801        sage: H_grassmannian_pieces()
     802        Nablas : [0\0/0, 0\10/1, 10\1/0, 1\0/10, 1\1/1]
     803        Deltas : [0/0\0, 0/1\10, 1/10\0, 1/1\1, 10/0\1]
     804    """
     805    forbidden_border_labels = ['10']
     806    pieces = PuzzlePieces(forbidden_border_labels)
     807    pieces.add_piece(NablaPiece('0','0','0'), rotations=60)
     808    pieces.add_piece(NablaPiece('1','1','1'), rotations=60)
     809    pieces.add_piece(NablaPiece('1','0','10'), rotations=60)
     810    return pieces
     811
     812def HT_grassmannian_pieces():
     813    r"""
     814    Defines the puzzle pieces used in computing the torus-equivariant cohomology of the Grassmannian.
     815
     816    REFERENCES:
     817
     818    .. [KT2003] Allen Knutson, Terence Tao, Puzzles and (equivariant) cohomology of Grassmannians,
     819       Duke Math. J. 119 (2003) 221
     820
     821    EXAMPLES::
     822
     823        sage: from sage.combinat.knutson_tao_puzzles import HT_grassmannian_pieces
     824        sage: HT_grassmannian_pieces()
     825        Nablas : [0\0/0, 0\10/1, 10\1/0, 1\0/10, 1\1/1, 1\T0|1/0]
     826        Deltas : [0/0\0, 0/1\10, 0/T0|1\1, 1/10\0, 1/1\1, 10/0\1]
     827    """
     828    pieces = H_grassmannian_pieces()
     829    pieces.add_T_piece('0', '1')
     830    return pieces
     831
     832def K_grassmannian_pieces():
     833    r"""
     834    Defines the puzzle pieces used in computing the K-theory of the Grassmannian.
     835
     836    REFERENCES:
     837
     838    .. [Buch00] A. Buch, A Littlewood-Richardson rule for the K-theory of Grassmannians, :arXiv:`math.AG/0004137`
     839
     840    EXAMPLES::
     841
     842        sage: from sage.combinat.knutson_tao_puzzles import K_grassmannian_pieces
     843        sage: K_grassmannian_pieces()
     844        Nablas : [0\0/0, 0\10/1, 0\K/1, 10\1/0, 1\0/10, 1\0/K, 1\1/1, K\1/0]
     845        Deltas : [0/0\0, 0/1\10, 1/10\0, 1/1\1, 10/0\1, K/K\K]
     846    """
     847    pieces = H_grassmannian_pieces()
     848    pieces.add_forbidden_label('K')
     849    pieces.add_piece(NablaPiece('0','K','1'), rotations=120)
     850    pieces.add_piece(DeltaPiece('K','K','K'), rotations=0)
     851    return pieces
     852
     853def H_two_step_pieces():
     854    r"""
     855    Defines the puzzle pieces used in two step flags.
     856
     857    This rule is currently only conjecturally true. See [BuchKreschTamvakis03]_.
     858
     859    REFERENCES:
     860
     861    .. [BuchKreschTamvakis03] A. Buch, A. Kresch, H. Tamvakis, Gromov-Witten invariants on Grassmannian, :arXiv:`math/0306388`
     862
     863    EXAMPLES::
     864
     865        sage: from sage.combinat.knutson_tao_puzzles import H_two_step_pieces
     866        sage: H_two_step_pieces()
     867        Nablas : [(21)0\21/0, 0\(21)0/21, 0\0/0, 0\10/1, 0\20/2, 10\1/0, 10\2(10)/2, 1\0/10, 1\1/1, 1\21/2,
     868        2(10)\2/10, 20\2/0, 21\0/(21)0, 21\2/1, 2\0/20, 2\1/21, 2\10/2(10), 2\2/2]
     869        Deltas : [(21)0/0\21, 0/0\0, 0/1\10, 0/21\(21)0, 0/2\20, 1/10\0, 1/1\1, 1/2\21, 10/0\1, 10/2\2(10),
     870        2(10)/10\2, 2/2(10)\10, 2/20\0, 2/21\1, 2/2\2, 20/0\2, 21/(21)0\0, 21/1\2]
     871    """
     872    forbidden_border_labels = ['10', '20', '21', '(21)0', '2(10)']
     873    pieces = PuzzlePieces(forbidden_border_labels)
     874    for i in ('0', '1', '2'):
     875        pieces.add_piece(DeltaPiece(i, i, i), rotations=60)
     876    for i, j in (('1','0'), ('2','0'), ('2','1')):
     877        pieces.add_piece(DeltaPiece(i+j, i, j), rotations=60)
     878    pieces.add_piece(DeltaPiece('(21)0','21','0'), rotations=60)
     879    pieces.add_piece(DeltaPiece('2(10)','2','10'), rotations=60)
     880    return pieces
     881
     882def HT_two_step_pieces():
     883    r"""
     884    Defines the puzzle pieces used in computing the equivariant two step puzzle pieces.
     885
     886    For the puzzle pieces, see Figure 26 on page 22 of [CoskunVakil06]_.
     887
     888    REFERENCES:
     889
     890    .. [CoskunVakil06] I. Coskun, R. Vakil, Geometric positivity in the cohomology of homogeneous spaces
     891       and generalized Schubert calculus, :arXiv:`math/0610538`
     892
     893    EXAMPLES::
     894
     895       sage: from sage.combinat.knutson_tao_puzzles import HT_two_step_pieces
     896       sage: HT_two_step_pieces()
     897       Nablas : [(21)0\21/0, 0\(21)0/21, 0\0/0, 0\10/1, 0\20/2, 10\1/0, 10\2(10)/2,
     898       1\0/10, 1\1/1, 1\21/2, 1\T0|1/0, 2(10)\2/10, 20\2/0, 21\0/(21)0, 21\2/1, 21\T0|21/0,
     899       21\T10|21/10, 2\0/20, 2\1/21, 2\10/2(10), 2\2/2, 2\T0|2/0, 2\T10|2/10, 2\T1|2/1]
     900       Deltas : [(21)0/0\21, 0/0\0, 0/1\10, 0/21\(21)0, 0/2\20, 0/T0|1\1, 0/T0|21\21, 0/T0|2\2,
     901       1/10\0, 1/1\1, 1/2\21, 1/T1|2\2, 10/0\1, 10/2\2(10), 10/T10|21\21, 10/T10|2\2, 2(10)/10\2,
     902       2/2(10)\10, 2/20\0, 2/21\1, 2/2\2, 20/0\2, 21/(21)0\0, 21/1\2]
     903    """
     904    pieces = H_two_step_pieces()
     905    for label1, label2 in (('0','1'), ('0','2'), ('1','2'), ('10','2'), ('0','21'), ('10','21')):
     906        pieces.add_T_piece(label1, label2)
     907    return pieces
     908
     909
     910def BK_pieces(max_letter):
     911    r"""
     912    The puzzle pieces used in computing the Belkale-Kumar coefficients for any
     913    partial flag variety in type `A`.
     914
     915    There are two types of puzzle pieces:
     916
     917    - a triangle, with each edge labeled with the same letter;
     918    - a rhombus, with edges labeled `i`, `j`, `i`, `j` in clockwise order with
     919      `i > j`.
     920
     921    Each of these is rotated by 60 degrees, but not reflected.
     922
     923    We model the rhombus pieces as two triangles: a delta piece north-west
     924    label `i`, north-east label `j` and south label `i(j)`; and a nabla piece
     925    with south-east label `i`, south-west label `j` and north label `i(j)`.
     926
     927    INPUT:
     928
     929    - ``max_letter`` -- positive integer specifying the number of steps in the
     930      partial flag variety, equivalently, the number of elements in the
     931      alphabet for the edge labels. The smallest label is `1`.
     932
     933    REFERENCES:
     934
     935    .. [KnutsonPurbhoo10] A. Knutson, K. Purbhoo, Product and puzzle formulae
     936       for `GL_n` Belkale-Kumar coefficients, :arXiv:`1008.4979`
     937
     938    EXAMPLES::
     939
     940        sage: from sage.combinat.knutson_tao_puzzles import BK_pieces
     941        sage: BK_pieces(3)
     942        Nablas : [1\1/1, 1\2(1)/2, 1\3(1)/3, 2(1)\2/1, 2\1/2(1), 2\2/2, 2\3(2)/3, 3(1)\3/1, 3(2)\3/2, 3\1/3(1), 3\2/3(2), 3\3/3]
     943        Deltas : [1/1\1, 1/2\2(1), 1/3\3(1), 2(1)/1\2, 2/2(1)\1, 2/2\2, 2/3\3(2), 3(1)/1\3, 3(2)/2\3, 3/3(1)\1, 3/3(2)\2, 3/3\3]
     944
     945    """
     946    forbidden_border_labels = ['%s(%s)' % (i, j)
     947                                for i in range(1, max_letter + 1)
     948                                for j in range(1, i)]
     949    pieces = PuzzlePieces(forbidden_border_labels)
     950    for i in range(1, max_letter + 1):
     951        piece = DeltaPiece('%s'%i, '%s'%i, '%s'%i)
     952        pieces.add_piece(piece, rotations=60)
     953        for j in range(1, i):
     954            piece = DeltaPiece(north_west='%s'%i, north_east='%s'%j, south='%s(%s)'%(i,j))
     955            pieces.add_piece(piece, rotations=60)
     956    return pieces
     957
     958class PuzzleFilling(object):
     959    r"""
     960    Create partial puzzles and provides methods to build puzzles from them.
     961    """
     962    def __init__(self, north_west_labels, north_east_labels):
     963        r"""
     964        TESTS::
     965
     966            sage: from sage.combinat.knutson_tao_puzzles import PuzzleFilling
     967            sage: P = PuzzleFilling('0101','0101')
     968            sage: P
     969            {}
     970        """
     971        self._nw_labels = tuple(north_west_labels)
     972        self._ne_labels = tuple(north_east_labels)
     973        self._squares = {}
     974        self._n = len(self._nw_labels)
     975        self._kink_coordinates = (1, self._n)
     976
     977    def __getitem__(self, key):
     978        r"""
     979        TESTS::
     980
     981            sage: from sage.combinat.knutson_tao_puzzles import KnutsonTaoPuzzleSolver
     982            sage: ps = KnutsonTaoPuzzleSolver("H")
     983            sage: puzzle = ps('0101','1001')[0]
     984            sage: puzzle
     985            {(1, 2): 1/\1  10\/0, (1, 3): 0/\10  1\/1, (3, 3): 1/1\1, (4, 4): 10/0\1,
     986            (1, 4): 1/\1  10\/0, (1, 1): 0/1\10, (2, 3): 1/\0  0\/1, (2, 2): 0/0\0, (3, 4): 0/\0  1\/10, (2, 4): 0/\0  0\/0}
     987            sage: puzzle[(1,2)]  # indirect doctest
     988            1/\1  10\/0
     989        """
     990        return self._squares[key]
     991
     992    def kink_coordinates(self):
     993        r"""
     994        Provides the coordinates of the kinks.
     995
     996        The kink coordinates are the coordinates up to which the puzzle has already
     997        been built. The kink starts in the north corner and then moves down the diagonals
     998        as the puzzles is built.
     999
     1000        EXAMPLES::
     1001
     1002            sage: from sage.combinat.knutson_tao_puzzles import PuzzleFilling
     1003            sage: P = PuzzleFilling('0101','0101')
     1004            sage: P
     1005            {}
     1006            sage: P.kink_coordinates()
     1007            (1, 4)
     1008        """
     1009        return self._kink_coordinates
     1010
     1011    def is_in_south_edge(self):
     1012        r"""
     1013        Checks whether kink coordinates of partial puzzle is in south corner.
     1014
     1015        EXAMPLES::
     1016
     1017            sage: from sage.combinat.knutson_tao_puzzles import PuzzleFilling
     1018            sage: P = PuzzleFilling('0101','0101')
     1019            sage: P.is_in_south_edge()
     1020            False
     1021        """
     1022        i, j = self.kink_coordinates()
     1023        return i == j
     1024
     1025    def north_west_label_of_kink(self):
     1026        r"""
     1027        Return north-west label of kink.
     1028
     1029        EXAMPLES::
     1030
     1031            sage: from sage.combinat.knutson_tao_puzzles import PuzzleFilling
     1032            sage: P = PuzzleFilling('0101','0101')
     1033            sage: P.north_west_label_of_kink()
     1034            '1'
     1035        """
     1036        (i, j) = self.kink_coordinates()
     1037        if i == 1:
     1038            return self._nw_labels[j-1]
     1039        else:
     1040            return self._squares[i-1,j]['south_east']
     1041
     1042    def north_east_label_of_kink(self):
     1043        r"""
     1044        Return north east label of kink.
     1045
     1046        EXAMPLES::
     1047
     1048            sage: from sage.combinat.knutson_tao_puzzles import PuzzleFilling
     1049            sage: P = PuzzleFilling('0101','0101')
     1050            sage: P.north_east_label_of_kink()
     1051            '0'
     1052        """
     1053        (i, j) = self.kink_coordinates()
     1054        if j == self._n:
     1055            return self._ne_labels[i-1]
     1056        else:
     1057            return self._squares[i,j+1]['south_west']
     1058
     1059    def is_completed(self):
     1060        r"""
     1061        Whether partial puzzle is complete (completely filled) or not.
     1062
     1063        EXAMPLES::
     1064
     1065            sage: from sage.combinat.knutson_tao_puzzles import PuzzleFilling
     1066            sage: P = PuzzleFilling('0101','0101')
     1067            sage: P.is_completed()
     1068            False
     1069
     1070            sage: from sage.combinat.knutson_tao_puzzles import KnutsonTaoPuzzleSolver
     1071            sage: ps = KnutsonTaoPuzzleSolver("H")
     1072            sage: puzzle = ps('0101','1001')[0]
     1073            sage: puzzle.is_completed()
     1074            True
     1075        """
     1076        (i, j) = self.kink_coordinates()
     1077        return i == self._n + 1
     1078
     1079    def south_labels(self):
     1080        r"""
     1081        Return south labels for completed puzzle.
     1082
     1083        EXAMPLES::
     1084
     1085            sage: from sage.combinat.knutson_tao_puzzles import KnutsonTaoPuzzleSolver
     1086            sage: ps = KnutsonTaoPuzzleSolver("H")
     1087            sage: ps('0101','1001')[0].south_labels()
     1088            ('1', '0', '1', '0')
     1089        """
     1090        # TODO: return ''.join(self[i, i]['south'] for i in range(1, self._n + 1))
     1091        return tuple([self[i,i]['south'] for i in range(1, self._n+1)])
     1092
     1093    def add_piece(self, piece):
     1094        r"""
     1095        Adds ``piece`` to partial puzzle.
     1096
     1097        EXAMPLES::
     1098
     1099            sage: from sage.combinat.knutson_tao_puzzles import DeltaPiece, PuzzleFilling
     1100            sage: piece = DeltaPiece('0','1','0')
     1101            sage: P = PuzzleFilling('0101','0101'); P
     1102            {}
     1103            sage: P.add_piece(piece); P
     1104            {(1, 4): 1/0\0}
     1105        """
     1106        (i, j) = self.kink_coordinates()
     1107        self._squares[i, j] = piece
     1108        if isinstance(piece, DeltaPiece):
     1109            i += 1
     1110            j = self._n
     1111        else:
     1112            j -= 1
     1113        self._kink_coordinates = (i, j)
     1114
     1115    def add_pieces(self, pieces):
     1116        r"""
     1117        Adds ``piece`` to partial puzzle.
     1118
     1119        INPUT:
     1120
     1121        - ``pieces`` -- tuple of pieces
     1122
     1123        EXAMPLES::
     1124
     1125            sage: from sage.combinat.knutson_tao_puzzles import DeltaPiece, PuzzleFilling
     1126            sage: P = PuzzleFilling('0101','0101'); P
     1127            {}
     1128            sage: piece = DeltaPiece('0','1','0')
     1129            sage: pieces = [piece,piece]
     1130            sage: P.add_pieces(pieces)
     1131            sage: P
     1132            {(2, 4): 1/0\0, (1, 4): 1/0\0}
     1133        """
     1134        (i, j) = self.kink_coordinates()
     1135        for piece in pieces:
     1136            self._squares[i, j] = piece
     1137            if isinstance(piece, DeltaPiece):
     1138                i += 1
     1139                j = self._n
     1140            else:
     1141                j -= 1
     1142        self._kink_coordinates = (i, j)
     1143
     1144    def copy(self):
     1145        r"""
     1146        Return copy of ``self``.
     1147
     1148        EXAMPLES::
     1149
     1150
     1151            sage: from sage.combinat.knutson_tao_puzzles import DeltaPiece, PuzzleFilling
     1152            sage: piece = DeltaPiece('0','1','0')
     1153            sage: P = PuzzleFilling('0101','0101'); P
     1154            {}
     1155            sage: PP = P.copy()
     1156            sage: P.add_piece(piece); P
     1157            {(1, 4): 1/0\0}
     1158            sage: PP
     1159            {}
     1160        """
     1161        PP = PuzzleFilling(self._nw_labels, self._ne_labels)
     1162        PP._squares = self._squares.copy()
     1163        PP._kink_coordinates = self._kink_coordinates
     1164        PP._n = self._n
     1165        return PP
     1166
     1167    def contribution(self):
     1168        r"""
     1169        Return equivariant contributions from ``self`` in polynomial ring.
     1170
     1171        EXAMPLES::
     1172
     1173            sage: from sage.combinat.knutson_tao_puzzles import KnutsonTaoPuzzleSolver
     1174            sage: ps = KnutsonTaoPuzzleSolver("HT")
     1175            sage: puzzles = ps('0101','1001')
     1176            sage: sorted([p.contribution() for p in puzzles], key=str)
     1177            [1, y1 - y3]
     1178        """
     1179        R = PolynomialRing(Integers(), 'y', self._n+1)
     1180        y = R.gens()
     1181        z = R.one()
     1182        for i in range(1, self._n + 1):
     1183            for j in range(i+1, self._n + 1):
     1184                if self[i, j].north_piece()['south'].startswith('T'):
     1185                    z *= y[i] - y[j]
     1186                if self[i, j].north_piece()['south'].startswith('K'):
     1187                    z *= -1
     1188        return z
     1189
     1190    def __repr__(self):
     1191        r"""
     1192        TESTS::
     1193
     1194            sage: from sage.combinat.knutson_tao_puzzles import H_grassmannian_pieces, PuzzleFilling
     1195            sage: P = PuzzleFilling('0101','0101'); P
     1196            {}
     1197            sage: P.__repr__()
     1198            '{}'
     1199        """
     1200        return str(self._squares)
     1201
     1202    def __iter__(self):
     1203        r"""
     1204        Iterator.
     1205
     1206        TESTS::
     1207
     1208            sage: from sage.combinat.knutson_tao_puzzles import KnutsonTaoPuzzleSolver
     1209            sage: ps = KnutsonTaoPuzzleSolver("H")
     1210            sage: puzzle = ps('0101','1001')[0]
     1211            sage: puzzle
     1212            {(1, 2): 1/\1  10\/0, (1, 3): 0/\10  1\/1, (3, 3): 1/1\1, (4, 4): 10/0\1,
     1213            (1, 4): 1/\1  10\/0, (1, 1): 0/1\10, (2, 3): 1/\0  0\/1, (2, 2): 0/0\0,
     1214            (3, 4): 0/\0  1\/10, (2, 4): 0/\0  0\/0}
     1215            sage: [p for p in puzzle]
     1216            [1/\1  10\/0,
     1217            0/\10  1\/1,
     1218            0/\0  0\/0,
     1219            1/\1  10\/0,
     1220            1/\0  0\/1,
     1221            0/\0  1\/10,
     1222            0/1\10,
     1223            0/0\0,
     1224            1/1\1,
     1225            10/0\1]
     1226        """
     1227        for d in range(self._n):
     1228            for k in range(d + 1):
     1229                yield self[k + 1, self._n - d + k]
     1230
     1231    def plot(self, labels=True, style="fill"):
     1232        r"""
     1233        Plots completed puzzle.
     1234
     1235        EXAMPLES::
     1236
     1237            sage: from sage.combinat.knutson_tao_puzzles import KnutsonTaoPuzzleSolver
     1238            sage: ps = KnutsonTaoPuzzleSolver("H")
     1239            sage: puzzle = ps('0101','1001')[0]
     1240            sage: puzzle.plot()  #not tested
     1241            sage: puzzle.plot(style='fill')  #not tested
     1242            sage: puzzle.plot(style='edges')  #not tested
     1243        """
     1244        P = Graphics()
     1245        coords = [(k, -d) for d in range(self._n) for k in range(-d, d + 1, 2)]
     1246        for ((k, d), piece) in zip(coords, self):
     1247            if isinstance(piece, RhombusPiece):
     1248                for (i, triangle) in enumerate(piece):
     1249                    P += triangle._plot_piece([(k, d - 2*i), (k - 1, d - 1), (k + 1, d - 1)], style=style)
     1250                if labels:
     1251                    P += piece._plot_label(piece['north_west'], (k - 0.5, d - 0.5), rotation=60)
     1252                    P += piece._plot_label(piece['north_east'], (k + 0.5, d - 0.5), rotation=-60)
     1253                    P += piece._plot_label(piece.north_piece()['south'], (k, d - 1))
     1254            else:
     1255                P += piece._plot_piece([(k, d), (k - 1, d - 1), (k + 1, d - 1)], style=style)
     1256                if labels:
     1257                    P += piece._plot_label(piece['north_west'], (k - 0.5, d - 0.5), rotation=60)
     1258                    P += piece._plot_label(piece['north_east'], (k + 0.5, d - 0.5), rotation=-60)
     1259                    P += piece._plot_label(piece['south'], (k, d - 1))
     1260        P.set_aspect_ratio(1.73)
     1261        P.axes(False)
     1262        return P
     1263
     1264    def _latex_(self):
     1265        r"""
     1266        Return latex version of ``self``.
     1267
     1268        Note that you might need to add tikz to the preamble::
     1269
     1270            sage: latex.extra_preamble(r'''\usepackage{tikz}''')
     1271            sage: from sage.combinat.knutson_tao_puzzles import *
     1272
     1273            sage: ps = KnutsonTaoPuzzleSolver(H_grassmannian_pieces())
     1274            sage: solns = ps('0101', '0101')
     1275            sage: view(solns[0], viewer='pdf', tightpage=True)  # not tested
     1276
     1277            sage: ps = KnutsonTaoPuzzleSolver(HT_two_step_pieces())
     1278            sage: solns = ps(list('10212'), list('12012'))
     1279            sage: view(solns[0], viewer='pdf', tightpage=True)  # not tested
     1280
     1281            sage: ps = KnutsonTaoPuzzleSolver(K_grassmannian_pieces())
     1282            sage: solns = ps('0101', '0101')
     1283            sage: view(solns[0], viewer='pdf', tightpage=True)  # not tested
     1284
     1285        """
     1286        from collections import defaultdict
     1287        label_colors = defaultdict(lambda : None)
     1288        label_colors.update({'0': 'red', '1': 'blue', '2': 'green'})
     1289        edge_colors = defaultdict(lambda : None)
     1290        edge_colors.update({'0': 'red', '1': 'blue', '2': 'green', 'K':'orange'})
     1291
     1292        s = r"""\begin{tikzpicture}[yscale=1.73]"""
     1293        coords = [(k, -d) for d in range(self._n) for k in range(-d, d + 1, 2)]
     1294
     1295        def tikztriangle_fill(color, k, d, i, *args):
     1296            s = r"""\path[color=%s, fill=%s!10]""" % (color, color)
     1297            s += r"""(%s, %s) -- (%s, %s)""" % (k, d-2*i, k-1, d-1)
     1298            s += r"""-- (%s, %s)""" % (k+1, d-1)
     1299            s += r"""-- (%s, %s)""" % (k, d-2*i)
     1300            s += ";\n"
     1301            return s
     1302
     1303        def tikztriangle_edges(color, k, d, i, label1, label2, label3):
     1304            s = ""
     1305            if i == 1: return s
     1306            tikzcmd = r"""\draw[color=%s, fill=none] (%s, %s) -- (%s, %s);""" + "\n"
     1307            if edge_colors[label1]:
     1308                s += tikzcmd % (edge_colors[label1], k-1, d-1, k+1, d-1)
     1309            if edge_colors[label2]:
     1310                s += tikzcmd % (edge_colors[label2], k, d-2*i, k-1, d-1)
     1311            if edge_colors[label3]:
     1312                s += tikzcmd % (edge_colors[label3], k+1, d-1, k, d-2*i)
     1313            return s
     1314
     1315        def tikzlabels(color, k, d, i, label1, label2, label3):
     1316            s = r"""\path[] (%s, %s)""" % (k, d-2*i)
     1317            s += r"""-- (%s, %s) """ % (k-1, d-1)
     1318            if label_colors[label2]:
     1319                s += r"""node[midway, color=%s] {$%s$} """ % (label_colors[label2], label2)
     1320            s += r"""-- (%s, %s) """ % (k+1, d-1)
     1321            if label_colors[label1]:
     1322                s += r"""node[midway, color=%s] {$%s$} """ % (label_colors[label1], label1)
     1323            s += r"""-- (%s, %s) """ % (k, d-2*i)
     1324            if label_colors[label3]:
     1325                s += r"""node[midway, color=%s] {$%s$} """ % (label_colors[label3], label3)
     1326            s += ";\n"
     1327            return s
     1328
     1329        for ((k, d), piece) in zip(coords, self):
     1330            for tikzcmd in (tikztriangle_fill, tikztriangle_edges, tikzlabels):
     1331                if isinstance(piece, RhombusPiece):
     1332                    for (i, triangle) in enumerate([piece.north_piece(), piece.south_piece()]):
     1333                        if i == 0:
     1334                            s += tikzcmd(triangle.color(), k, d, i, *triangle.border())
     1335                        else:
     1336                            s += tikzcmd(triangle.color(), k, d, i, "", "", "")
     1337                else:
     1338                    color = piece.color()
     1339                    s += tikzcmd(color, k, d, 0, *piece.border())
     1340
     1341        s += r"""\end{tikzpicture}"""
     1342
     1343        return s
     1344
     1345class KnutsonTaoPuzzleSolver(UniqueRepresentation):
     1346    r"""
     1347    Returns puzzle solver function used to create all puzzles with given boundary conditions.
     1348
     1349    This class implements a generic algorithm to solve Knutson-Tao puzzles.
     1350    An instance of this class will be callable: the arguments are the
     1351    labels of north-east and north-west sides of the puzzle boundary; the
     1352    output is the list of the fillings of the puzzle with the specified
     1353    pieces.
     1354
     1355    INPUT:
     1356
     1357    - ``puzzle_pieces`` -- takes either a collection of puzzle pieces or
     1358      a string indicating a pre-programmed collection of puzzle pieces:
     1359
     1360        - ``H`` -- cohomology of the Grassmannian
     1361        - ``HT`` -- equivariant cohomology of the Grassmannian
     1362        - ``K`` -- K-theory
     1363        - ``H2step`` -- cohomology of the *2-step* Grassmannian
     1364        - ``HT2step`` -- equivariant cohomology of the *2-step* Grassmannian
     1365        - ``BK`` -- Belkale-Kumar puzzle pieces
     1366
     1367    - ``max_letter`` -- (default: None) None or a positive integer. This is
     1368      only required only for Belkale-Kumar puzzles.
     1369
     1370    EXAMPLES:
     1371
     1372    Each puzzle piece is an edge-labelled triangle oriented in such a way
     1373    that it has a south edge (called a *delta* piece) or a north edge
     1374    (called a *nabla* piece). For example, the puzzle pieces corresponding
     1375    to the cohomology of the Grassmannian are the following::
     1376
     1377        sage: from sage.combinat.knutson_tao_puzzles import H_grassmannian_pieces
     1378        sage: H_grassmannian_pieces()
     1379        Nablas : [0\0/0, 0\10/1, 10\1/0, 1\0/10, 1\1/1]
     1380        Deltas : [0/0\0, 0/1\10, 1/10\0, 1/1\1, 10/0\1]
     1381
     1382    In the string representation, the nabla pieces are depicted as
     1383    ``c\a/b``, where `a` is the label of the north edge, `b` is the label
     1384    of the south-east edge, `c` is the label of the south-west edge.
     1385    A similar string representation exists for the delta pieces.
     1386
     1387    To create a puzzle solver, one specifies a collection of puzzle pieces::
     1388
     1389        sage: KnutsonTaoPuzzleSolver(H_grassmannian_pieces())
     1390        Knutson-Tao puzzle solver with pieces:
     1391        Nablas : [0\0/0, 0\10/1, 10\1/0, 1\0/10, 1\1/1]
     1392        Deltas : [0/0\0, 0/1\10, 1/10\0, 1/1\1, 10/0\1]
     1393
     1394    The following shorthand to create the above puzzle solver is also supported::
     1395
     1396        sage: KnutsonTaoPuzzleSolver('H')
     1397        Knutson-Tao puzzle solver with pieces:
     1398        Nablas : [0\0/0, 0\10/1, 10\1/0, 1\0/10, 1\1/1]
     1399        Deltas : [0/0\0, 0/1\10, 1/10\0, 1/1\1, 10/0\1]
     1400
     1401    The solver will compute all fillings of the puzzle with the given
     1402    puzzle pieces. The user specifies the labels of north-east and
     1403    north-west sides of the puzzle boundary and the output is a list of the
     1404    fillings of the puzzle with the specified pieces. For example, there is
     1405    one solution to the puzzle whose north-west and north-east edges are
     1406    both labeled '0'::
     1407
     1408        sage: ps = KnutsonTaoPuzzleSolver('H')
     1409        sage: ps('0', '0')
     1410        [{(1, 1): 0/0\0}]
     1411
     1412    There are two solutions to the puzzle whose north-west and north-east
     1413    edges are both labeled '0101'::
     1414
     1415        sage: ps = KnutsonTaoPuzzleSolver('H')
     1416        sage: solns = ps('0101', '0101')
     1417        sage: len(solns)
     1418        2
     1419        sage: solns.sort(key=str)
     1420        sage: solns
     1421        [{(1, 2): 1/\0  0\/1, (1, 3): 0/\0  0\/0, (3, 3): 1/1\1, (4, 4): 10/0\1, (1, 4): 1/\0  0\/1, (1, 1): 0/0\0, (2, 3): 0/\10  1\/1, (2, 2): 1/1\1, (3, 4): 0/\0  1\/10, (2, 4): 1/\1  10\/0},
     1422        {(1, 2): 1/\1  10\/0, (1, 3): 0/\0  1\/10, (3, 3): 0/0\0, (4, 4): 1/1\1, (1, 4): 1/\0  0\/1, (1, 1): 0/1\10, (2, 3): 10/\1  0\/0, (2, 2): 0/0\0, (3, 4): 1/\0  0\/1, (2, 4): 1/\1  1\/1}]
     1423
     1424    The pieces in a puzzle filling are indexed by pairs of non-negative
     1425    integers `(i, j)` with `1 \leq i \leq j \leq n`, where `n` is the
     1426    length of the word labelling the triangle edge. The pieces indexed by
     1427    `(i, i)` are the triangles along the south edge of the puzzle. ::
     1428
     1429        sage: f = solns[0]
     1430        sage: [f[i, i] for i in range(1,5)]
     1431        [0/0\0, 1/1\1, 1/1\1, 10/0\1]
     1432
     1433    The pieces indexed by `(i, j)` for `j > i` are a pair consisting of
     1434    a delta piece and nabla piece glued together along the south edge and
     1435    north edge, respectively (these pairs are called *rhombi*). ::
     1436
     1437        sage: f = solns[0]
     1438        sage: f[1, 2]
     1439        1/\0  0\/1
     1440
     1441    There are various methods and options to display puzzle solutions.
     1442    A single puzzle can be displayed using the plot method of the puzzle::
     1443
     1444        sage: ps = KnutsonTaoPuzzleSolver("H")
     1445        sage: puzzle = ps('0101','1001')[0]
     1446        sage: puzzle.plot()  #not tested
     1447        sage: puzzle.plot(style='fill')  #not tested
     1448        sage: puzzle.plot(style='edges')  #not tested
     1449
     1450    To plot several puzzle solutions, use the plot method of the puzzle
     1451    solver::
     1452
     1453        sage: ps = KnutsonTaoPuzzleSolver('K')
     1454        sage: solns = ps('0101', '0101')
     1455        sage: ps.plot(solns)        # not tested
     1456
     1457    The code can also generate a PDF of a puzzle (using LaTeX and *tikz*)::
     1458
     1459        sage: latex.extra_preamble(r'''\usepackage{tikz}''')
     1460        sage: ps = KnutsonTaoPuzzleSolver('H')
     1461        sage: solns = ps('0101', '0101')
     1462        sage: view(solns[0], viewer='pdf', tightpage=True)  # not tested
     1463
     1464
     1465    Below are examples of using each of the currently supported puzzles.
     1466
     1467    Cohomology of the Grassmannian::
     1468
     1469        sage: ps = KnutsonTaoPuzzleSolver("H")
     1470        sage: solns = ps('0101', '0101')
     1471        sage: sorted(solns, key=str)
     1472        [{(1, 2): 1/\0  0\/1, (1, 3): 0/\0  0\/0, (3, 3): 1/1\1, (4, 4): 10/0\1, (1, 4): 1/\0  0\/1, (1, 1): 0/0\0, (2, 3): 0/\10  1\/1, (2, 2): 1/1\1, (3, 4): 0/\0  1\/10, (2, 4): 1/\1  10\/0},
     1473        {(1, 2): 1/\1  10\/0, (1, 3): 0/\0  1\/10, (3, 3): 0/0\0, (4, 4): 1/1\1, (1, 4): 1/\0  0\/1, (1, 1): 0/1\10, (2, 3): 10/\1  0\/0, (2, 2): 0/0\0, (3, 4): 1/\0  0\/1, (2, 4): 1/\1  1\/1}]
     1474
     1475    Equivariant puzzles::
     1476
     1477        sage: ps = KnutsonTaoPuzzleSolver("HT")
     1478        sage: solns = ps('0101', '0101')
     1479        sage: sorted(solns, key=str)
     1480        [{(1, 2): 1/\0  0\/1, (1, 3): 0/\0  0\/0, (3, 3): 0/0\0, (4, 4): 1/1\1, (1, 4): 1/\0  0\/1, (1, 1): 0/0\0, (2, 3): 0/\1  1\/0, (2, 2): 1/1\1, (3, 4): 1/\0  0\/1, (2, 4): 1/\1  1\/1},
     1481        {(1, 2): 1/\0  0\/1, (1, 3): 0/\0  0\/0, (3, 3): 1/1\1, (4, 4): 10/0\1, (1, 4): 1/\0  0\/1, (1, 1): 0/0\0, (2, 3): 0/\10  1\/1, (2, 2): 1/1\1, (3, 4): 0/\0  1\/10, (2, 4): 1/\1  10\/0},
     1482        {(1, 2): 1/\1  10\/0, (1, 3): 0/\0  1\/10, (3, 3): 0/0\0, (4, 4): 1/1\1, (1, 4): 1/\0  0\/1, (1, 1): 0/1\10, (2, 3): 10/\1  0\/0, (2, 2): 0/0\0, (3, 4): 1/\0  0\/1, (2, 4): 1/\1  1\/1}]
     1483
     1484    K-Theory puzzles::
     1485
     1486        sage: ps = KnutsonTaoPuzzleSolver("K")
     1487        sage: solns = ps('0101', '0101')
     1488        sage: sorted(solns, key=str)
     1489        [{(1, 2): 1/\0  0\/1, (1, 3): 0/\0  0\/0, (3, 3): 1/1\1, (4, 4): 10/0\1, (1, 4): 1/\0  0\/1, (1, 1): 0/0\0, (2, 3): 0/\10  1\/1, (2, 2): 1/1\1, (3, 4): 0/\0  1\/10, (2, 4): 1/\1  10\/0},
     1490        {(1, 2): 1/\1  10\/0, (1, 3): 0/\0  1\/10, (3, 3): 0/0\0, (4, 4): 1/1\1, (1, 4): 1/\0  0\/1, (1, 1): 0/1\10, (2, 3): 10/\1  0\/0, (2, 2): 0/0\0, (3, 4): 1/\0  0\/1, (2, 4): 1/\1  1\/1},
     1491        {(1, 2): 1/\1  10\/0, (1, 3): 0/\0  1\/K, (3, 3): 1/1\1, (4, 4): 10/0\1, (1, 4): 1/\0  0\/1, (1, 1): 0/1\10, (2, 3): K/\K  0\/1, (2, 2): 0/0\0, (3, 4): 0/\0  1\/10, (2, 4): 1/\1  K\/0}]
     1492
     1493    Two-step puzzles::
     1494
     1495        sage: ps = KnutsonTaoPuzzleSolver("H2step")
     1496        sage: solns = ps('01201', '01021')
     1497        sage: sorted(solns, key=str)
     1498        [{(1, 2): 1/\0  0\/1, (1, 3): 2/\0  0\/2, (3, 3): 1/1\1, (4, 5): 20/\2  1\/10, (4, 4): 1/1\1, (5, 5): 10/0\1, (1, 4): 0/\0  0\/0, (1, 1): 0/0\0, (1, 5): 1/\0  0\/1, (2, 3): 2/\2  21\/1, (2, 2): 1/2\21, (2, 5): 1/\1  10\/0, (3, 4): 21/\2  1\/1, (2, 4): 0/\10  2\/21, (3, 5): 0/\0  2\/20},
     1499        {(1, 2): 1/\1  10\/0, (1, 3): 2/\1  1\/2, (3, 3): 0/0\0, (4, 5): 2(10)/\2  0\/1, (4, 4): 0/0\0, (5, 5): 1/1\1, (1, 4): 0/\0  1\/10, (1, 1): 0/1\10, (1, 5): 1/\0  0\/1, (2, 3): 2/\2  20\/0, (2, 2): 0/2\20, (2, 5): 1/\1  1\/1, (3, 4): 20/\2  0\/0, (2, 4): 10/\1  2\/20, (3, 5): 1/\0  2\/2(10)},
     1500        {(1, 2): 1/\21  20\/0, (1, 3): 2/\2  21\/1, (3, 3): 1/1\1, (4, 5): 21/\2  1\/1, (4, 4): 10/0\1, (5, 5): 1/1\1, (1, 4): 0/\0  2\/20, (1, 1): 0/2\20, (1, 5): 1/\0  0\/1, (2, 3): 1/\0  0\/1, (2, 2): 0/0\0, (2, 5): 1/\1  2\/21, (3, 4): 0/\0  1\/10, (2, 4): 20/\2  0\/0, (3, 5): 21/\0  0\/21}]
     1501
     1502    Two-step equivariant puzzles::
     1503
     1504        sage: ps = KnutsonTaoPuzzleSolver("HT2step")
     1505        sage: solns = ps('10212', '12012')
     1506        sage: sorted(solns, key=str)
     1507        [{(1, 2): 0/\(21)0  1\/2, (1, 3): 2/\1  (21)0\/0, (3, 3): 0/0\0, (4, 5): 1/\1  2\/21, (4, 4): 2/2\2, (5, 5): 21/1\2, (1, 4): 1/\1  1\/1, (1, 1): 1/1\1, (1, 5): 2/\1  1\/2, (2, 3): 0/\2  2\/0, (2, 2): 2/2\2, (2, 5): 2/\2  21\/1, (3, 4): 2/\0  0\/2, (2, 4): 1/\21  2\/2, (3, 5): 1/\0  0\/1},
     1508        {(1, 2): 0/\(21)0  1\/2, (1, 3): 2/\1  (21)0\/0, (3, 3): 0/0\0, (4, 5): 2/\1  1\/2, (4, 4): 1/1\1, (5, 5): 2/2\2, (1, 4): 1/\1  1\/1, (1, 1): 1/1\1, (1, 5): 2/\1  1\/2, (2, 3): 0/\2  2\/0, (2, 2): 2/2\2, (2, 5): 2/\2  2\/2, (3, 4): 1/\0  0\/1, (2, 4): 1/\2  2\/1, (3, 5): 2/\0  0\/2},
     1509        {(1, 2): 0/\(21)0  1\/2, (1, 3): 2/\1  (21)0\/0, (3, 3): 2/2\2, (4, 5): 1/\1  2\/21, (4, 4): 20/0\2, (5, 5): 21/1\2, (1, 4): 1/\1  1\/1, (1, 1): 1/1\1, (1, 5): 2/\1  1\/2, (2, 3): 0/\20  2\/2, (2, 2): 2/2\2, (2, 5): 2/\2  21\/1, (3, 4): 0/\0  2\/20, (2, 4): 1/\21  20\/0, (3, 5): 1/\0  0\/1},
     1510        {(1, 2): 0/\1  1\/0, (1, 3): 2/\1  1\/2, (3, 3): 0/0\0, (4, 5): 1/\1  2\/21, (4, 4): 2/2\2, (5, 5): 21/1\2, (1, 4): 1/\1  1\/1, (1, 1): 1/1\1, (1, 5): 2/\1  1\/2, (2, 3): 2/\2  20\/0, (2, 2): 0/2\20, (2, 5): 2/\2  21\/1, (3, 4): 2/\0  0\/2, (2, 4): 1/\21  2\/2, (3, 5): 1/\0  0\/1},
     1511        {(1, 2): 0/\1  1\/0, (1, 3): 2/\1  1\/2, (3, 3): 0/0\0, (4, 5): 2/\1  1\/2, (4, 4): 1/1\1, (5, 5): 2/2\2, (1, 4): 1/\1  1\/1, (1, 1): 1/1\1, (1, 5): 2/\1  1\/2, (2, 3): 2/\2  20\/0, (2, 2): 0/2\20, (2, 5): 2/\2  2\/2, (3, 4): 1/\0  0\/1, (2, 4): 1/\2  2\/1, (3, 5): 2/\0  0\/2},
     1512        {(1, 2): 0/\10  1\/1, (1, 3): 2/\10  10\/2, (3, 3): 1/1\1, (4, 5): 10/\1  2\/20, (4, 4): 2/2\2, (5, 5): 20/0\2, (1, 4): 1/\1  10\/0, (1, 1): 1/1\1, (1, 5): 2/\1  1\/2, (2, 3): 2/\2  21\/1, (2, 2): 1/2\21, (2, 5): 2/\2  20\/0, (3, 4): 2/\1  1\/2, (2, 4): 0/\20  2\/2, (3, 5): 0/\0  1\/10},
     1513        {(1, 2): 0/\10  1\/1, (1, 3): 2/\10  10\/2, (3, 3): 1/1\1, (4, 5): 2/\1  1\/2, (4, 4): 10/0\1, (5, 5): 2/2\2, (1, 4): 1/\1  10\/0, (1, 1): 1/1\1, (1, 5): 2/\1  1\/2, (2, 3): 2/\2  21\/1, (2, 2): 1/2\21, (2, 5): 2/\2  2\/2, (3, 4): 0/\0  1\/10, (2, 4): 0/\2  2\/0, (3, 5): 2/\0  0\/2},
     1514        {(1, 2): 0/\20  21\/1, (1, 3): 2/\2  20\/0, (3, 3): 0/0\0, (4, 5): 2/\1  1\/2, (4, 4): 1/1\1, (5, 5): 2/2\2, (1, 4): 1/\1  2\/21, (1, 1): 1/2\21, (1, 5): 2/\1  1\/2, (2, 3): 0/\1  1\/0, (2, 2): 1/1\1, (2, 5): 2/\2  2\/2, (3, 4): 1/\0  0\/1, (2, 4): 21/\2  1\/1, (3, 5): 2/\0  0\/2},
     1515        {(1, 2): 0/\20  21\/1, (1, 3): 2/\2  20\/0, (3, 3): 1/1\1, (4, 5): 2/\1  1\/2, (4, 4): 10/0\1, (5, 5): 2/2\2, (1, 4): 1/\1  2\/21, (1, 1): 1/2\21, (1, 5): 2/\1  1\/2, (2, 3): 0/\10  1\/1, (2, 2): 1/1\1, (2, 5): 2/\2  2\/2, (3, 4): 0/\0  1\/10, (2, 4): 21/\2  10\/0, (3, 5): 2/\0  0\/2},
     1516        {(1, 2): 0/\21  21\/0, (1, 3): 2/\2  21\/1, (3, 3): 0/0\0, (4, 5): 2/\1  1\/2, (4, 4): 1/1\1, (5, 5): 2/2\2, (1, 4): 1/\1  2\/21, (1, 1): 1/2\21, (1, 5): 2/\1  1\/2, (2, 3): 1/\1  10\/0, (2, 2): 0/1\10, (2, 5): 2/\2  2\/2, (3, 4): 1/\0  0\/1, (2, 4): 21/\2  1\/1, (3, 5): 2/\0  0\/2}]
     1517
     1518
     1519    Belkale-Kumar puzzles (the following example is Figure 2 of [KnutsonPurbhoo10]_)::
     1520
     1521        sage: ps = KnutsonTaoPuzzleSolver('BK', 3)
     1522        sage: solns = ps('12132', '23112')
     1523        sage: len(solns)
     1524        1
     1525        sage: solns[0].south_labels()
     1526        ('3', '2', '1', '2', '1')
     1527        sage: solns
     1528        [{(1, 2): 2/\3(2)  3(1)\/1, (1, 3): 1/\3(1)  3(2)\/2, (3, 3): 1/1\1, (4, 5): 1/\1  2\/2(1), (4, 4): 2/2\2, (5, 5): 2(1)/1\2, (1, 4): 3/\3  3(1)\/1, (1, 1): 1/3\3(1), (1, 5): 2/\2  3\/3(2), (2, 3): 2/\2  2(1)\/1, (2, 2): 1/2\2(1), (2, 5): 3(2)/\3  2(1)\/1, (3, 4): 2/\1  1\/2, (2, 4): 1/\2(1)  2\/2, (3, 5): 1/\1  1\/1}]
     1529    """
     1530    def __init__(self, puzzle_pieces):
     1531        r"""
     1532        Knutson-Tao puzzle solver.
     1533
     1534        TESTS:
     1535
     1536        Check that UniqueRepresentation works::
     1537
     1538            sage: from sage.combinat.knutson_tao_puzzles import KnutsonTaoPuzzleSolver, H_grassmannian_pieces
     1539            sage: ps = KnutsonTaoPuzzleSolver(H_grassmannian_pieces())
     1540            sage: qs = KnutsonTaoPuzzleSolver("H")
     1541            sage: ps
     1542            Knutson-Tao puzzle solver with pieces:
     1543            Nablas : [0\0/0, 0\10/1, 10\1/0, 1\0/10, 1\1/1]
     1544            Deltas : [0/0\0, 0/1\10, 1/10\0, 1/1\1, 10/0\1]
     1545            sage: qs
     1546            Knutson-Tao puzzle solver with pieces:
     1547            Nablas : [0\0/0, 0\10/1, 10\1/0, 1\0/10, 1\1/1]
     1548            Deltas : [0/0\0, 0/1\10, 1/10\0, 1/1\1, 10/0\1]
     1549            sage: ps == qs
     1550            True
     1551        """
     1552        self._puzzle_pieces = puzzle_pieces
     1553        self._rhombus_pieces = tuple(puzzle_pieces.rhombus_pieces())
     1554        self._bottom_deltas = tuple(puzzle_pieces.boundary_deltas())
     1555
     1556    @staticmethod
     1557    def __classcall_private__(cls, puzzle_pieces, max_letter=None):
     1558        r"""
     1559        TESTS::
     1560
     1561            sage: from sage.combinat.knutson_tao_puzzles import *
     1562            sage: KnutsonTaoPuzzleSolver(H_grassmannian_pieces()) == KnutsonTaoPuzzleSolver("H") # indirect doctest
     1563            True
     1564            sage: KnutsonTaoPuzzleSolver(HT_grassmannian_pieces()) == KnutsonTaoPuzzleSolver("HT")
     1565            True
     1566            sage: KnutsonTaoPuzzleSolver(K_grassmannian_pieces()) == KnutsonTaoPuzzleSolver("K")
     1567            True
     1568            sage: KnutsonTaoPuzzleSolver(H_two_step_pieces()) == KnutsonTaoPuzzleSolver("H2step")
     1569            True
     1570            sage: KnutsonTaoPuzzleSolver(HT_two_step_pieces()) == KnutsonTaoPuzzleSolver("HT2step")
     1571            True
     1572            sage: KnutsonTaoPuzzleSolver(BK_pieces(3)) == KnutsonTaoPuzzleSolver("BK",3)
     1573            True
     1574        """
     1575        if isinstance(puzzle_pieces, str):
     1576            if puzzle_pieces == "H":
     1577                puzzle_pieces = H_grassmannian_pieces()
     1578            elif puzzle_pieces == "HT":
     1579                puzzle_pieces = HT_grassmannian_pieces()
     1580            elif puzzle_pieces == "K":
     1581                puzzle_pieces = K_grassmannian_pieces()
     1582            elif puzzle_pieces == "H2step":
     1583                puzzle_pieces = H_two_step_pieces()
     1584            elif puzzle_pieces == "HT2step":
     1585                puzzle_pieces = HT_two_step_pieces()
     1586            elif puzzle_pieces == "BK":
     1587                if max_letter is not None:
     1588                    puzzle_pieces = BK_pieces(max_letter)
     1589                else:
     1590                    raise ValueError, "max_letter needs to be specified"
     1591        return super(KnutsonTaoPuzzleSolver, cls).__classcall__(cls, puzzle_pieces)
     1592
     1593    def __call__(self, lamda, mu, algorithm='strips'):
     1594        r"""
     1595        TESTS::
     1596
     1597            sage: from sage.combinat.knutson_tao_puzzles import KnutsonTaoPuzzleSolver
     1598            sage: ps = KnutsonTaoPuzzleSolver("H")
     1599            sage: ps('0101','1001')
     1600            [{(1, 2): 1/\1  10\/0, (1, 3): 0/\10  1\/1, (3, 3): 1/1\1, (4, 4): 10/0\1, (1, 4): 1/\1  10\/0,
     1601            (1, 1): 0/1\10, (2, 3): 1/\0  0\/1, (2, 2): 0/0\0, (3, 4): 0/\0  1\/10, (2, 4): 0/\0  0\/0}]
     1602            sage: ps('0101','1001',algorithm='pieces')
     1603            [{(1, 2): 1/\1  10\/0, (1, 3): 0/\10  1\/1, (3, 3): 1/1\1, (4, 4): 10/0\1, (1, 4): 1/\1  10\/0,
     1604            (2, 4): 0/\0  0\/0, (2, 3): 1/\0  0\/1, (2, 2): 0/0\0, (3, 4): 0/\0  1\/10, (1, 1): 0/1\10}]
     1605        """
     1606        lamda, mu = tuple(lamda), tuple(mu)
     1607        if algorithm == 'pieces':
     1608            return list(self._fill_puzzle_by_pieces(lamda, mu))
     1609        elif algorithm == 'strips':
     1610            return list(self._fill_puzzle_by_strips(lamda, mu))
     1611
     1612    solutions = __call__
     1613
     1614    def __repr__(self):
     1615        r"""
     1616        EXAMPLES::
     1617
     1618            sage: from sage.combinat.knutson_tao_puzzles import KnutsonTaoPuzzleSolver
     1619            sage: KnutsonTaoPuzzleSolver('H')
     1620            Knutson-Tao puzzle solver with pieces:
     1621            Nablas : [0\0/0, 0\10/1, 10\1/0, 1\0/10, 1\1/1]
     1622            Deltas : [0/0\0, 0/1\10, 1/10\0, 1/1\1, 10/0\1]
     1623        """
     1624        return "Knutson-Tao puzzle solver with pieces:\n%s" % self._puzzle_pieces
     1625
     1626    def puzzle_pieces(self):
     1627        r"""
     1628        The puzzle pieces used for filling in the puzzles.
     1629
     1630        EXAMPLES::
     1631
     1632            sage: from sage.combinat.knutson_tao_puzzles import KnutsonTaoPuzzleSolver
     1633            sage: ps = KnutsonTaoPuzzleSolver('H')
     1634            sage: ps.puzzle_pieces()
     1635            Nablas : [0\0/0, 0\10/1, 10\1/0, 1\0/10, 1\1/1]
     1636            Deltas : [0/0\0, 0/1\10, 1/10\0, 1/1\1, 10/0\1]
     1637        """
     1638        return self._puzzle_pieces
     1639
     1640    def _fill_piece(self, nw_label, ne_label, pieces):
     1641        r"""
     1642        Fillings of a piece.
     1643
     1644        INPUT:
     1645
     1646        - ``nw_label``, ``nw_label`` -- label
     1647        - ``pieces`` -- puzzle pieces used for the filling
     1648
     1649        OUTPUT:
     1650
     1651        - list of the fillings
     1652
     1653        EXAMPLES::
     1654
     1655            sage: from sage.combinat.knutson_tao_puzzles import KnutsonTaoPuzzleSolver
     1656            sage: ps = KnutsonTaoPuzzleSolver('H')
     1657            sage: ps._fill_piece('0', '0', ps._bottom_deltas)
     1658            [0/0\0]
     1659        """
     1660        output = []
     1661        for piece in pieces:
     1662            if ( piece['north_west'] == nw_label
     1663                    and piece['north_east'] == ne_label ):
     1664                output.append(piece)
     1665        return output
     1666
     1667    @cached_method
     1668    def _fill_strip(self, nw_labels, ne_label, pieces, final_pieces=None):
     1669        r"""
     1670        Fillings of a strip of height 1.
     1671
     1672        INPUT:
     1673
     1674        - ``nw_labels`` -- tuple of labels
     1675        - ``nw_label`` -- label
     1676        - ``pieces`` -- puzzle pieces used for the filling
     1677        - ``final_pieces`` -- pieces used for the last piece to be filled in
     1678
     1679        OUTPUT:
     1680
     1681        - list of lists of the fillings
     1682
     1683        EXAMPLES::
     1684
     1685            sage: from sage.combinat.knutson_tao_puzzles import KnutsonTaoPuzzleSolver
     1686            sage: ps = KnutsonTaoPuzzleSolver('H')
     1687            sage: ps._fill_strip(('0',), '0', ps._rhombus_pieces, ps._bottom_deltas)
     1688            [[0/0\0]]
     1689            sage: ps._fill_strip(('0','0'), '0', ps._rhombus_pieces, ps._bottom_deltas)
     1690            [[0/\0  0\/0, 0/0\0]]
     1691            sage: sorted(ps._fill_strip(('0',), '0', ps._rhombus_pieces), key=str)
     1692            [[0/\0  0\/0], [0/\0  1\/10]]
     1693            sage: sorted(ps._fill_strip(('0','1'), '0', ps._rhombus_pieces), key =str)
     1694            [[1/\0  0\/1, 0/\0  0\/0], [1/\0  0\/1, 0/\0  1\/10]]
     1695
     1696        TESTS::
     1697
     1698            sage: from sage.combinat.knutson_tao_puzzles import KnutsonTaoPuzzleSolver
     1699            sage: ps = KnutsonTaoPuzzleSolver('H')
     1700            sage: ps._fill_strip(('0',), 'goo', ps._rhombus_pieces)
     1701            []
     1702        """
     1703        if final_pieces is None:
     1704            final_pieces = pieces
     1705
     1706        output = []
     1707        if len(nw_labels) == 1:
     1708            X = self._fill_piece(nw_labels[0], ne_label, final_pieces)
     1709            if X:
     1710                output = [[x] for x in X]
     1711        else:
     1712            partial_fillings = self._fill_strip(nw_labels[1:], ne_label, pieces)
     1713            for partial_filling in partial_fillings:
     1714                ne_label = partial_filling[-1]['south_west']
     1715                for piece in self._fill_piece(nw_labels[0], ne_label, final_pieces):
     1716                    output.append(partial_filling + [piece])
     1717        return output
     1718
     1719    def _fill_puzzle_by_pieces(self, lamda, mu):
     1720        r"""
     1721        Fill puzzle pieces for given outer labels ``lambda`` and ``mu``.
     1722
     1723        EXAMPLES::
     1724
     1725            sage: from sage.combinat.knutson_tao_puzzles import KnutsonTaoPuzzleSolver
     1726            sage: ps = KnutsonTaoPuzzleSolver('H')
     1727            sage: list(ps._fill_puzzle_by_pieces('0', '0'))
     1728            [{(1, 1): 0/0\0}]
     1729        """
     1730        queue = [PuzzleFilling(lamda, mu)]
     1731        while queue:
     1732            PP = queue.pop()
     1733            ne_label = PP.north_east_label_of_kink()
     1734            nw_label = PP.north_west_label_of_kink()
     1735            if PP.is_in_south_edge():
     1736                pieces = self._bottom_deltas
     1737            else:
     1738                pieces = self._rhombus_pieces
     1739            for piece in self._fill_piece(nw_label, ne_label, pieces):
     1740                PPcopy = PP.copy()
     1741                PPcopy.add_piece(piece)
     1742                if PPcopy.is_completed():
     1743                    yield PPcopy
     1744                else:
     1745                    queue.append(PPcopy)
     1746
     1747    def _fill_puzzle_by_strips(self, lamda, mu):
     1748        r"""
     1749        Fill puzzle pieces by strips for given outer labels ``lambda`` and ``mu``.
     1750
     1751        EXAMPLES::
     1752
     1753            sage: from sage.combinat.knutson_tao_puzzles import KnutsonTaoPuzzleSolver
     1754            sage: ps = KnutsonTaoPuzzleSolver('H')
     1755            sage: list(ps._fill_puzzle_by_strips('0', '0'))
     1756            [{(1, 1): 0/0\0}]
     1757            sage: list(ps._fill_puzzle_by_strips('01', '01'))
     1758            [{(1, 2): 1/\0  0\/1, (1, 1): 0/0\0, (2, 2): 1/1\1}]
     1759        """
     1760        queue = [PuzzleFilling(lamda, mu)]
     1761        while queue:
     1762            PP = queue.pop()
     1763            (i, j) = PP.kink_coordinates()
     1764
     1765            # grab nw labels
     1766            if i == 1:
     1767                nw_labels = PP._nw_labels
     1768            else:
     1769                nw_labels = tuple(PP._squares[i-1, k]['south_east']
     1770                                  for k in range(i, len(lamda)+1))
     1771
     1772            # grab ne labels
     1773            ne_label = PP._ne_labels[i-1]
     1774
     1775            deltas = self._bottom_deltas
     1776            rhombi = self._rhombus_pieces
     1777            for row in self._fill_strip(nw_labels, ne_label, rhombi, deltas):
     1778                PPcopy = PP.copy()
     1779                PPcopy.add_pieces(row)
     1780                if PPcopy.is_completed():
     1781                    yield PPcopy
     1782                else:
     1783                    queue.append(PPcopy)
     1784
     1785    def plot(self, puzzles):
     1786        r"""
     1787        Return plot of puzzles.
     1788
     1789        INPUT:
     1790
     1791        - ``puzzles`` -- list of puzzles
     1792
     1793        EXAMPLES::
     1794
     1795            sage: from sage.combinat.knutson_tao_puzzles import KnutsonTaoPuzzleSolver
     1796            sage: ps = KnutsonTaoPuzzleSolver('K')
     1797            sage: solns = ps('0101', '0101')
     1798            sage: ps.plot(solns)        # not tested
     1799        """
     1800        g = [p.plot() for p in puzzles]
     1801        m = len([gg.axes(False) for gg in g])
     1802        return graphics_array(g, (m+3)/4, 4)
     1803
     1804    def structure_constants(self, lamda, mu, nu=None):
     1805        r"""
     1806        Compute cohomology structure coefficients from puzzles.
     1807
     1808        INPUT:
     1809
     1810        - ``pieces`` -- puzzle pieces to be used
     1811        - ``lambda``, ``mu`` -- edge labels of puzzle for northwest and north east side
     1812        - ``nu`` -- (default: ``None``) If ``nu`` is not specified a dictionary is returned with
     1813          the structure coefficients corresponding to all south labels; if ``nu`` is given, only
     1814          the coefficients with the specified label is returned.
     1815
     1816        OUTPUT: dictionary
     1817
     1818        EXAMPLES:
     1819
     1820        Note: In order to standardize the output of the following examples,
     1821        we output a sorted list of items from the dictionary instead of the
     1822        dictionary itself.
     1823
     1824        Grassmannian cohomology::
     1825
     1826            sage: ps = KnutsonTaoPuzzleSolver('H')
     1827            sage: cp = ps.structure_constants('0101', '0101')
     1828            sage: sorted(cp.items(), key=str)
     1829            [(('0', '1', '1', '0'), 1), (('1', '0', '0', '1'), 1)]
     1830            sage: ps.structure_constants('001001', '001010', '010100')
     1831            1
     1832
     1833        Equivariant cohomology::
     1834
     1835            sage: ps = KnutsonTaoPuzzleSolver('HT')
     1836            sage: cp = ps.structure_constants('0101', '0101')
     1837            sage: sorted(cp.items(), key=str)
     1838            [(('0', '1', '0', '1'), y2 - y3),
     1839            (('0', '1', '1', '0'), 1),
     1840            (('1', '0', '0', '1'), 1)]
     1841
     1842        K-theory::
     1843
     1844            sage: ps = KnutsonTaoPuzzleSolver('K')
     1845            sage: cp = ps.structure_constants('0101', '0101')
     1846            sage: sorted(cp.items(), key=str)
     1847            [(('0', '1', '1', '0'), 1), (('1', '0', '0', '1'), 1), (('1', '0', '1', '0'), -1)]
     1848
     1849        Two-step::
     1850
     1851            sage: ps = KnutsonTaoPuzzleSolver('H2step')
     1852            sage: cp = ps.structure_constants('01122', '01122')
     1853            sage: sorted(cp.items(), key=str)
     1854            [(('0', '1', '1', '2', '2'), 1)]
     1855            sage: cp = ps.structure_constants('01201', '01021')
     1856            sage: sorted(cp.items(), key=str)
     1857            [(('0', '2', '1', '1', '0'), 1),
     1858             (('1', '2', '0', '0', '1'), 1),
     1859             (('2', '0', '1', '0', '1'), 1)]
     1860
     1861        Two-step equivariant::
     1862
     1863            sage: ps = KnutsonTaoPuzzleSolver('HT2step')
     1864            sage: cp = ps.structure_constants('10212', '12012')
     1865            sage: sorted(cp.items(), key=str)
     1866            [(('1', '2', '0', '1', '2'), y1*y2 - y2*y3 - y1*y4 + y3*y4),
     1867             (('1', '2', '0', '2', '1'), y1 - y3),
     1868             (('1', '2', '1', '0', '2'), y2 - y4),
     1869             (('1', '2', '1', '2', '0'), 1),
     1870             (('1', '2', '2', '0', '1'), 1),
     1871             (('2', '1', '0', '1', '2'), y1 - y3),
     1872             (('2', '1', '1', '0', '2'), 1)]
     1873        """
     1874        from collections import defaultdict
     1875        R = PolynomialRing(Integers(), 'y', len(lamda)+1)
     1876        z = defaultdict(R.zero)
     1877        for p in self(lamda, mu):
     1878            z[p.south_labels()] += p.contribution()
     1879        if nu is None:
     1880            return dict(z)
     1881        else:
     1882            return z[tuple(nu)]