
    wgs                        d Z ddlmZmZmZmZ ddlmZmZm	Z	 ddl
mZ ddlmZ ddlmZ ddlmZ ddlmZ d	d
iZ G d d      Z G d d      Z G d d      Z G d d      Z	 	 ddZ edd      	 	 dd       Zy)ad  
This module contains the functionality to arrange the nodes of a
diagram on an abstract grid, and then to produce a graphical
representation of the grid.

The currently supported back-ends are Xy-pic [Xypic].

Layout Algorithm
================

This section provides an overview of the algorithms implemented in
:class:`DiagramGrid` to lay out diagrams.

The first step of the algorithm is the removal composite and identity
morphisms which do not have properties in the supplied diagram.  The
premises and conclusions of the diagram are then merged.

The generic layout algorithm begins with the construction of the
"skeleton" of the diagram.  The skeleton is an undirected graph which
has the objects of the diagram as vertices and has an (undirected)
edge between each pair of objects between which there exist morphisms.
The direction of the morphisms does not matter at this stage.  The
skeleton also includes an edge between each pair of vertices `A` and
`C` such that there exists an object `B` which is connected via
a morphism to `A`, and via a morphism to `C`.

The skeleton constructed in this way has the property that every
object is a vertex of a triangle formed by three edges of the
skeleton.  This property lies at the base of the generic layout
algorithm.

After the skeleton has been constructed, the algorithm lists all
triangles which can be formed.  Note that some triangles will not have
all edges corresponding to morphisms which will actually be drawn.
Triangles which have only one edge or less which will actually be
drawn are immediately discarded.

The list of triangles is sorted according to the number of edges which
correspond to morphisms, then the triangle with the least number of such
edges is selected.  One of such edges is picked and the corresponding
objects are placed horizontally, on a grid.  This edge is recorded to
be in the fringe.  The algorithm then finds a "welding" of a triangle
to the fringe.  A welding is an edge in the fringe where a triangle
could be attached.  If the algorithm succeeds in finding such a
welding, it adds to the grid that vertex of the triangle which was not
yet included in any edge in the fringe and records the two new edges in
the fringe.  This process continues iteratively until all objects of
the diagram has been placed or until no more weldings can be found.

An edge is only removed from the fringe when a welding to this edge
has been found, and there is no room around this edge to place
another vertex.

When no more weldings can be found, but there are still triangles
left, the algorithm searches for a possibility of attaching one of the
remaining triangles to the existing structure by a vertex.  If such a
possibility is found, the corresponding edge of the found triangle is
placed in the found space and the iterative process of welding
triangles restarts.

When logical groups are supplied, each of these groups is laid out
independently.  Then a diagram is constructed in which groups are
objects and any two logical groups between which there exist morphisms
are connected via a morphism.  This diagram is laid out.  Finally,
the grid which includes all objects of the initial diagram is
constructed by replacing the cells which contain logical groups with
the corresponding laid out grids, and by correspondingly expanding the
rows and columns.

The sequential layout algorithm begins by constructing the
underlying undirected graph defined by the morphisms obtained after
simplifying premises and conclusions and merging them (see above).
The vertex with the minimal degree is then picked up and depth-first
search is started from it.  All objects which are located at distance
`n` from the root in the depth-first search tree, are positioned in
the `n`-th column of the resulting grid.  The sequential layout will
therefore attempt to lay the objects out along a line.

References
==========

.. [Xypic] https://xy-pic.sourceforge.net/

    )CompositeMorphismIdentityMorphismNamedMorphismDiagram)DictSymboldefault_sort_key)latex)	FiniteSet)iterable)doctest_depends_on)chain)preview_diagrampygletc                   Z    e Zd ZdZd Zed        Zed        Zd Zd Z	d Z
d Zd	 Zd
 Zy)_GrowableGrida  
    Holds a growable grid of objects.

    Explanation
    ===========

    It is possible to append or prepend a row or a column to the grid
    using the corresponding methods.  Prepending rows or columns has
    the effect of changing the coordinates of the already existing
    elements.

    This class currently represents a naive implementation of the
    functionality with little attempt at optimisation.
    c                     || _         || _        t        |      D cg c]  }t        |      D cg c]  }d  c} c}}| _        y c c}w c c}}w N)_width_heightrange_array)selfwidthheightijs        e/home/mcse/projects/flask/flask-venv/lib/python3.12/site-packages/sympy/categories/diagram_drawing.py__init__z_GrowableGrid.__init__r   s;    =B6]KeEl33K3Ks   A	AAAc                     | j                   S r   )r   r   s    r   r   z_GrowableGrid.widthx   s    {{    c                     | j                   S r   )r   r!   s    r   r   z_GrowableGrid.height|   s    ||r"   c                 0    |\  }}| j                   |   |   S )zZ
        Returns the element located at in the i-th line and j-th
        column.
        r   r   i_jr   r   s       r   __getitem__z_GrowableGrid.__getitem__   s     
 1{{1~a  r"   c                 2    |\  }}|| j                   |   |<   y)zW
        Sets the element located at in the i-th line and j-th
        column.
        Nr%   )r   r'   newvaluer   r   s        r   __setitem__z_GrowableGrid.__setitem__   s    
 1$Aqr"   c                     | xj                   dz  c_         | j                  j                  t        | j                        D cg c]  }d c}       yc c}w )z3
        Appends an empty row to the grid.
           N)r   r   appendr   r   r   r   s     r   
append_rowz_GrowableGrid.append_row   s;     	%*<=QD=>=s   	Ac                     | xj                   dz  c_         t        | j                        D ]   }| j                  |   j	                  d       " y)z6
        Appends an empty column to the grid.
        r-   N)r   r   r   r   r.   r   r   s     r   append_columnz_GrowableGrid.append_column   s@     	qt||$ 	(AKKN!!$'	(r"   c                     | xj                   dz  c_         | j                  j                  dt        | j                        D cg c]  }d c}       yc c}w )z6
        Prepends the grid with an empty row.
        r-   r   N)r   r   insertr   r   r/   s     r   prepend_rowz_GrowableGrid.prepend_row   s=     	1U4;;-?@t@A@s   	A
c                     | xj                   dz  c_         t        | j                        D ]!  }| j                  |   j	                  dd       # y)z9
        Prepends the grid with an empty column.
        r-   r   N)r   r   r   r   r5   r2   s     r   prepend_columnz_GrowableGrid.prepend_column   sB     	qt||$ 	+AKKN!!!T*	+r"   N)__name__
__module____qualname____doc__r   propertyr   r   r(   r+   r0   r3   r6   r8    r"   r   r   r   c   sT    L    !%?(B+r"   r   c                      e Zd ZdZed        Zed        Zed        Zed        Zed        Z	ed        Z
ed        Zed	        Zed
        Zed        Zed        Zed        Zed        Zed        Zed        Zed        Zed        Zed        Zed        Zed        Zed        Zed        Zed        Zed        Zed        Zed        Zd#dZe d        Z!e d        Z"d  Z#e d!        Z$d" Z%y)$DiagramGrida  
    Constructs and holds the fitting of the diagram into a grid.

    Explanation
    ===========

    The mission of this class is to analyse the structure of the
    supplied diagram and to place its objects on a grid such that,
    when the objects and the morphisms are actually drawn, the diagram
    would be "readable", in the sense that there will not be many
    intersections of moprhisms.  This class does not perform any
    actual drawing.  It does strive nevertheless to offer sufficient
    metadata to draw a diagram.

    Consider the following simple diagram.

    >>> from sympy.categories import Object, NamedMorphism
    >>> from sympy.categories import Diagram, DiagramGrid
    >>> from sympy import pprint
    >>> A = Object("A")
    >>> B = Object("B")
    >>> C = Object("C")
    >>> f = NamedMorphism(A, B, "f")
    >>> g = NamedMorphism(B, C, "g")
    >>> diagram = Diagram([f, g])

    The simplest way to have a diagram laid out is the following:

    >>> grid = DiagramGrid(diagram)
    >>> (grid.width, grid.height)
    (2, 2)
    >>> pprint(grid)
    A  B
    <BLANKLINE>
       C

    Sometimes one sees the diagram as consisting of logical groups.
    One can advise ``DiagramGrid`` as to such groups by employing the
    ``groups`` keyword argument.

    Consider the following diagram:

    >>> D = Object("D")
    >>> f = NamedMorphism(A, B, "f")
    >>> g = NamedMorphism(B, C, "g")
    >>> h = NamedMorphism(D, A, "h")
    >>> k = NamedMorphism(D, B, "k")
    >>> diagram = Diagram([f, g, h, k])

    Lay it out with generic layout:

    >>> grid = DiagramGrid(diagram)
    >>> pprint(grid)
    A  B  D
    <BLANKLINE>
       C

    Now, we can group the objects `A` and `D` to have them near one
    another:

    >>> grid = DiagramGrid(diagram, groups=[[A, D], B, C])
    >>> pprint(grid)
    B     C
    <BLANKLINE>
    A  D

    Note how the positioning of the other objects changes.

    Further indications can be supplied to the constructor of
    :class:`DiagramGrid` using keyword arguments.  The currently
    supported hints are explained in the following paragraphs.

    :class:`DiagramGrid` does not automatically guess which layout
    would suit the supplied diagram better.  Consider, for example,
    the following linear diagram:

    >>> E = Object("E")
    >>> f = NamedMorphism(A, B, "f")
    >>> g = NamedMorphism(B, C, "g")
    >>> h = NamedMorphism(C, D, "h")
    >>> i = NamedMorphism(D, E, "i")
    >>> diagram = Diagram([f, g, h, i])

    When laid out with the generic layout, it does not get to look
    linear:

    >>> grid = DiagramGrid(diagram)
    >>> pprint(grid)
    A  B
    <BLANKLINE>
       C  D
    <BLANKLINE>
          E

    To get it laid out in a line, use ``layout="sequential"``:

    >>> grid = DiagramGrid(diagram, layout="sequential")
    >>> pprint(grid)
    A  B  C  D  E

    One may sometimes need to transpose the resulting layout.  While
    this can always be done by hand, :class:`DiagramGrid` provides a
    hint for that purpose:

    >>> grid = DiagramGrid(diagram, layout="sequential", transpose=True)
    >>> pprint(grid)
    A
    <BLANKLINE>
    B
    <BLANKLINE>
    C
    <BLANKLINE>
    D
    <BLANKLINE>
    E

    Separate hints can also be provided for each group.  For an
    example, refer to ``tests/test_drawing.py``, and see the different
    ways in which the five lemma [FiveLemma] can be laid out.

    See Also
    ========

    Diagram

    References
    ==========

    .. [FiveLemma] https://en.wikipedia.org/wiki/Five_lemma
    c                     i }| j                         D ].  \  }}t        |t              r|st        |t              r*|||<   0 |S )a-  
        Given a dictionary mapping morphisms to their properties,
        returns a new dictionary in which there are no morphisms which
        do not have properties, and which are compositions of other
        morphisms included in the dictionary.  Identities are dropped
        as well.
        )items
isinstancer   r   )	morphismsnewmorphismsmorphismpropss       r   _simplify_morphismszDiagramGrid._simplify_morphisms2  sS     (0 	/OHe($56uH&67).X&	/ r"   c                 d    t        t        | j                         |j                                     S )a-  
        Given two dictionaries of morphisms and their properties,
        produces a single dictionary which includes elements from both
        dictionaries.  If a morphism has some properties in premises
        and also in conclusions, the properties in conclusions take
        priority.
        )dictr   rB   )premisesconclusionss     r   _merge_premises_conclusionsz'DiagramGrid._merge_premises_conclusionsE  s&     E(..*K,=,=,?@AAr"   c                 @    | |z  }t        |      dk7  ry| |z
  ||z
  z  S )aO  
        If ``edge1`` and ``edge2`` have precisely one common endpoint,
        returns an edge which would form a triangle with ``edge1`` and
        ``edge2``.

        If ``edge1`` and ``edge2`` do not have a common endpoint,
        returns ``None``.

        If ``edge1`` and ``edge`` are the same edge, returns ``None``.
        r-   Nlen)edge1edge2intersections      r   _juxtapose_edgeszDiagramGrid._juxtapose_edgesP  s4     u}|! $)=>>r"   c                 B    || v r| |   j                  |       y|g| |<   y)a  
        If ``edge`` is not in ``dictionary``, adds ``edge`` to the
        dictionary and sets its value to ``[elem]``.  Otherwise
        appends ``elem`` to the value of existing entry.

        Note that edges are undirected, thus `(A, B) = (B, A)`.
        N)r.   )
dictionaryedgeelems      r   _add_edge_appendzDiagramGrid._add_edge_appende  s+     :t##D) $vJtr"   c                     i }| D ]8  }t         j                  |t        |j                  |j                  g      |       : t        |      }|D ],  }|D ]%  }t         j                  ||      }|s||vs!g ||<   ' . |S )a  
        Creates a dictionary which maps edges to corresponding
        morphisms.  Thus for a morphism `f:Aightarrow B`, the edge
        `(A, B)` will be associated with `f`.  This function also adds
        to the list those edges which are formed by juxtaposition of
        two edges already in the list.  These new edges are not
        associated with any morphism and are only added to assure that
        the diagram can be decomposed into triangles.
        )r@   rY   	frozensetdomaincodomainrJ   rT   )rD   edgesrF   edges1wvwvs          r   _build_skeletonzDiagramGrid._build_skeletons  s     ! 	RH((y(//83D3D!EFR	R
 e 	#A # 11!Q7"E/ "E"I#	# r"   c           	          t               }| D ]D  }| D ]=  }t        j                  ||      }|s|| v s!|j                  t	        |||g             ? F |S )z
        Builds the set of triangles formed by the supplied edges.  The
        triangles are arbitrary and need not be commutative.  A
        triangle is a set that contains all three of its sides.
        )setr@   rT   addr[   )r^   	trianglesr`   ra   rb   s        r   _list_triangleszDiagramGrid._list_triangles  sc     E	 	9A 9 11!Q7"+MM)Q2J"789	9 r"   c                     | D cg c]&  }t        |D cg c]
  }||   s	| c}      dk\  r|( c}}S c c}w c c}}w )z
        Returns a list which contains only those triangles who have
        morphisms associated with at least two edges.
           rO   )rg   skeletontries       r   _drop_redundant_trianglesz%DiagramGrid._drop_redundant_triangles  sD      ) >36a(1+671<  > 	>6>s   :
55::c                 N    t        | t              rt        | j                        S y)z
        Returns the length of a morphism.  The length of a morphism is
        the number of components it consists of.  A non-composite
        morphism is of length 1.
        r-   )rC   r   rP   
components)rF   s    r   _morphism_lengthzDiagramGrid._morphism_length  s#     h 12x**++r"   c                 n    i }| D ]-  }d}|D ]  }||   }|s|t        d |D              z  }! |||<   / |S )a  
        Returns a dictionary mapping triangles to their minimal sizes.
        The minimal size of a triangle is the sum of maximal lengths
        of morphisms associated to the sides of the triangle.  The
        length of a morphism is the number of components it consists
        of.  A non-composite morphism is of length 1.

        Sorting triangles by this metric attempts to address two
        aspects of layout.  For triangles with only simple morphisms
        in the edge, this assures that triangles with all three edges
        visible will get typeset after triangles with less visible
        edges, which sometimes minimizes the necessity in diagonal
        arrows.  For triangles with composite morphisms in the edges,
        this assures that objects connected with shorter morphisms
        will be laid out first, resulting the visual proximity of
        those objects which are connected by shorter morphisms.
        r   c              3   F   K   | ]  }t         j                  |        y wr   )r@   rq   ).0ms     r   	<genexpr>z:DiagramGrid._compute_triangle_min_sizes.<locals>.<genexpr>  s#       4$% !, < <Q ?  4s   !)max)rg   r^   triangle_sizestrianglesizerm   rD   s          r   _compute_triangle_min_sizesz'DiagramGrid._compute_triangle_min_sizes  sj    & ! 	,HD 4!!H	C  4)2 4 4 4D4
 (,N8$	, r"   c                 6    t        t        t        |              S )zG
        Given a triangle, returns the objects included in it.
        )r[   r   tuple)ry   s    r   _triangle_objectszDiagramGrid._triangle_objects  s     h011r"   c                 \    t        t        j                  |       t        |      z
        d   S )zh
        Given a triangle and an edge of it, returns the vertex which
        opposes the edge.
        r   )listr@   r~   re   )ry   rW   s     r   _other_vertexzDiagramGrid._other_vertex  s(     K11(;c$iGHKKr"   c                 z    | d   dk  s,| d   dk  s$| d   |j                   k\  s| d   |j                  k\  ry||    du S )zt
        Checks if the cell at coordinates ``pt`` is either empty or
        out of the bounds of the grid.
        r   r-   TNr   r   )ptgrids     r   _empty_pointzDiagramGrid._empty_point  sJ     qEAI2a519qET[[ betzz&9Bx4r"   c                    | \  }}d}|dk(  rM|j                          d}d}t        t        |            D ]!  }||   \  \  }}	\  }
}|dz   |	f|
dz   |ff||<   # n||j                  k(  r|j	                          |dk(  rRd}|d   df}|j                          t        t        |            D ]!  }||   \  \  }}	\  }
}||	dz   f|
|dz   ff||<   # n||j                  k(  r|j                          ||||f<   |S )aP  
        Places an object at the coordinate ``cords`` in ``grid``,
        growing the grid and updating ``fringe``, if necessary.
        Returns (0, 0) if no row or column has been prepended, (1, 0)
        if a row was prepended, (0, 1) if a column was prepended and
        (1, 1) if both a column and a row were prepended.
        r   r   r   )r-   r   r-   )r6   r   rP   r   r0   r8   r   r3   )coordsobjr   fringer   r   offsetki1j1i2j2s               r   _put_objectzDiagramGrid._put_object  s/    A7AF3v;' 9'-ay$"b8B 1fb\BFB<8q	9 $++OO7AQi^F!3v;' 9'-ay$"b8B "q&\BQ<8q	9 $**_ QT
r"   c                     t         j                  | |      }t         j                  ||      }|r*|r(||d      }|j                  t        ||g            r| S |S |r| S |r|S y)z
        Given two points, ``pt1`` and ``pt2``, and the welding edge
        ``edge``, chooses one of the two points to place the opposing
        vertex ``obj`` of the triangle.  If neither of this points
        fits, returns ``None``.
        r   N)r@   r   getr[   )	pt1pt2rW   r   rk   r   	pt1_empty	pt2_emptyAs	            r   _choose_target_cellzDiagramGrid._choose_target_cell  sn      ,,S$7	,,S$7	 T!WA||Iq#h/0

JJr"   c                 d    | D ]+  }|D ]$  \  }}t        ||   ||   g      |v s|||ffc c S  - y)aO  
        Finds, if possible, a triangle and an edge in the ``fringe`` to
        which the triangle could be attached.  Returns the tuple
        containing the triangle and the index of the corresponding
        edge in the ``fringe``.

        This function relies on the fact that objects are unique in
        the diagram.
        N)r[   )rg   r   r   ry   abs         r   _find_triangle_to_weldz"DiagramGrid._find_triangle_to_weld3  sT     " 	.H  .Ad1gtAw/0H<$q!f--.	. r"   c                 0   |\  }}d}t         j                  | ||   ||   f      }t        |d   |d   z
        dk(  rKt        |d   |d   z
        dk(  r4|d   |d   f}||   rB|d   |d   f}||   r2|j                  ||f       y|d   |d   k(  r|d   dz   |d   f}	|d   dz   |d   f}
t         j	                  |	|
||f|||      }|s|d   dz
  |d   f}|d   dz
  |d   f}t         j	                  ||||f|||      }|s|j                  ||f       y|d   |d   k(  r|d   |d   dz   f}|d   |d   dz   f}t         j	                  ||||f|||      }|sL|d   |d   dz
  f}|d   |d   dz
  f}t         j	                  ||||f|||      }|s|j                  ||f       yt         j                  ||||      }|d   |d   z   |d   |d   z   f}|d   |d   z   |d   |d   z   f}|d   |d   z   |d   |d   z   f}|j                  ||f||fg       y)a  
        If possible, welds the triangle ``tri`` to ``fringe`` and
        returns ``False``.  If this method encounters a degenerate
        situation in the fringe and corrects it such that a restart of
        the search is required, it returns ``True`` (which means that
        a restart in finding triangle weldings is required).

        A degenerate situation is a situation when an edge listed in
        the fringe does not belong to the visual boundary of the
        diagram.
        Nr   r-   TF)r@   r   absremover   r   extend)rl   welding_edger   r   rk   r   r   target_cellr   	down_left
down_rightup_leftup_rightright_up
right_downleft_up	left_downr   s                     r   _weld_trianglezDiagramGrid._weld_triangleD  s    1''d1gtAw-?@ !qt!AaD1Q4K(8A(=Q41,KK  tQqTl$ MM1a&)qTQqT\ !q!A$I11Q4J%99:1vsHdDK A$(AaD.Q4!8QqT>)==X1vsHdD # MM1a&)qTQqT\ tQqTAX~H1qtaxJ%99*q!fc8TCK A$!q.aD!A$(N	)==YAXtE # MM1a&) ((c4H #1~q	1"1~q	13qTF1IqtfQi/0qTF1IqtfQi/0;'![)9:; r"   c                 j    t        t        j                  |       t              }||    t        |      fS )z
        Returns a key for the supplied triangle.  It should be the
        same independently of the hash randomisation.
        key)sortedr@   r~   r	   )rl   rx   objectss      r   _triangle_keyzDiagramGrid._triangle_key  s6     ))#.4DFs#%5g%>??r"   c                     | D cg c]  }||   rt        |t               }}t        |t              }t        t        |d   t                    S c c}w )z
        For a given triangle always picks the same root edge.  The
        root edge is the edge that will be placed first on the grid.
        r   r   )r   r	   r}   )rl   rk   rm   
candidatessorted_candidatess        r   _pick_root_edgezDiagramGrid._pick_root_edge  s]      #3hqk Q$45 3
 3":3CD V-a06FGHH3s   Ac                 v    | D cg c])  }|j                  t        j                  |            r(|+ c}S c c}w )z}
        Returns only those triangles whose set of objects is not
        completely included in ``placed_objects``.
        )
issupersetr@   r~   )rg   placed_objectsrl   s      r   _drop_irrelevant_trianglesz&DiagramGrid._drop_irrelevant_triangles  s;      ) 10I0I))#.10 1 	1 1s   )66c                    t        |j                        D ][  }t        |j                        D ]?  }|||f   sfd}| D cg c]  } ||      s| }	}|	s/|	d   }t        |D 
cg c]
  }
||
   s	|
 c}
d       }|D 
cg c]	  }
|
v s|
 }}
|d   }t	        |t        g      z
        d   }|dz
  |f||dz   f|dz   |f||dz
  f|dz
  |dz
  f|dz
  |dz   f|dz   |dz
  f|dz   |dz   fg}|D ]s  }t        j                  ||      st        j                  ||||      }||d   z  }||d   z  }|d   |d   z   |d   |d   z   f}|j                  ||f|f       |c c c S  B ^ yc c}w c c}
w c c}
w )aj  
        Starting from an object in the existing structure on the ``grid``,
        adds an edge to which a triangle from ``triangles`` could be
        welded.  If this method has found a way to do so, it returns
        the object it has just added.

        This method should be applied when ``_weld_triangle`` cannot
        find weldings any more.
        c                 b    t         j                  |       }|v xr |hz
  z  t               k(  S r   )r@   r~   re   )rl   objsr   r   s     r   good_trianglez2DiagramGrid._grow_pseudopod.<locals>.good_triangle  s;    &88=D$; A&$#,735@Ar"   r   c                 .    t        |  j                         S r   )r   sort_key)rm   s    r   <lambda>z-DiagramGrid._grow_pseudopod.<locals>.<lambda>  s    )Q-2H2H2J r"   r   r-   N)
r   r   r   r   r}   r[   r@   r   r   r.   )rg   r   r   rk   r   r   r   r   rl   trisrm   r   r^   rW   	other_obj
neighboursr   r   r   s       `             @r   _grow_pseudopodzDiagramGrid._grow_pseudopod  s    t{{# B	)A4::& A)1a4jA
 (1GM#4FGG 1g
 $$C1x{Q$C(JL
$.;q#(;; Qx!$C5)9"9:1=	
  !1uaj1a!e*q1uaj1a!e* 1ua!enq1ua!enq1ua!enqSTuVWZ[V[n^
 % )B"//D9 "-!8!8	4"9 VAYVAY efQi/A1BC1vrl3(()kA)B	)J k H$ %D;s$   	E8E8.
E=9E=	FFc           	           fd}d }i i t        |t        t        f      r3i }|j                         D ]  \  }} ||      }	|||	<    |||        |}n-g }|D ]$  } ||      }	|j	                  |	        ||	d       & |}g }
|D ]B  }|j
                     }|j                     }||k7  s'|
j	                  t        ||d             D t        t        |
            fdt        j                        D cg c]+  t        fdt        j                        D              - }}t        j                        D cg c]+  t        fdt        j                        D              - }}t        t        |      t        |            }d}d}t        j                        D ]  }t        j                        D ]h  }||f   }|v rL|   }t        |j                        D ].  t        |j                        D ]  |f   ||z   |z   f<    0 n||||f<   |||   z  }j d}|||   z  } |S c c}w c c}w )	a+  
        Given the slightly preprocessed morphisms of the diagram,
        produces a grid laid out according to ``groups``.

        If a group has hints, it is laid out with those hints only,
        without any influence from ``hints``.  Otherwise, it is laid
        out with ``hints``.
        c                     t        | t              rL| D ]  }| |<   	 |rt        j                  |       fi || <   yt        j                  |       fi | <   y| | <   y)a  
            If ``group`` is a set of objects, uses a ``DiagramGrid``
            to lay it out and returns the grid.  Otherwise returns the
            object (i.e., ``group``).  If ``local_hints`` is not
            empty, it is supplied to ``DiagramGrid`` as the dictionary
            of hints.  Otherwise, the ``hints`` argument of
            ``_handle_groups`` is used.
            N)rC   r   r@   subdiagram_from_objects)grouplocal_hintsr   diagramgroups_gridshints
obj_groupss      r   lay_out_groupz1DiagramGrid._handle_groups.<locals>.lay_out_group!  s     %+ ! ,C&+JsO, *577>+OBM+OL' +677>+IBG+IL' %*
5!r"   c                 ,    t        |       rt        |  S | S )zh
            Converts ``group`` to a :class:``FiniteSet`` if it is an
            iterable.
            )r   r   )r   s    r   group_to_finitesetz6DiagramGrid._handle_groups.<locals>.group_to_finiteset:  s    
  %((r"   Ndummyc                 H    | v r|    }|j                   |j                  fS y)z
            For the supplied group (or object, eventually), returns
            the size of the cell that will hold this group (object).
            )r-   r-   r   )r   r   r   s     r   
group_sizez.DiagramGrid._handle_groups.<locals>.group_sizer  s-    
 $#E*TZZ00r"   c              3   >   K   | ]  } |f         d      yw)r   Nr>   )rt   r   r   r   top_grids     r   rv   z-DiagramGrid._handle_groups.<locals>.<genexpr>}  s+      ;  &hq!tn5a8 ;   c              3   >   K   | ]  } |f         d      ywr-   Nr>   )rt   r   r   r   r   s     r   rv   z-DiagramGrid._handle_groups.<locals>.<genexpr>  s+      >!" (A7: >r   r   )rC   rJ   r   rB   r.   r\   r]   r   r@   r   r   r   rw   r   r   sum)r   groupsmerged_morphismsr   r   r   finiteset_groupsr   r   finiteset_groupnew_morphismsrF   domcodr   row_heightsr   column_widthsr   real_rowreal_columnlogical_rowlogical_columnr   
local_gridr   r   r   r   s   `  `          ` `        @@@@r   _handle_groupszDiagramGrid._handle_groups  s   	*2	 
 ftTl+!&,lln 2"{"4U";4? 1e[12 &F! 5"4U"; ''8ot45 &F( 	GHX__-CX../C cz
 $$]3W%EF	G w}56		 !&hoo 68  ;$)(..$9; ; 8 8 #("79  >&+HOO&<> > 9 9 S/[1AB 1 	1K"'"7 ={N:;,& ".c!2J":#4#45 D!&z'7'7!8 DA3=ad3C !A +a"0 1DD 36D;./}^<<=  KK00H%	1( A89s   0I0Ic                    t        | j                        }t        |      dk(  rt        dd      }t	        |      d   |d<   |S t
        j                  |      }t        dd      }t        |      dk(  r#t        |t              }|d   |d<   |d   |d<   |S t
        j                  |      }t
        j                  ||      }t
        j                  ||      t        |fd      }t
        j                  |d   |      }|\  |d<   |d<   dg}t        |      }	|	|k7  rt
        j                  |||      }
|
rG|
\  }}t
        j                  |||||      }|r@|	j                  t
        j!                  |             n)t
        j#                  |||||	      }|s||	z
  }| j%                  t'        |       }t        |      }|j(                  |j(                  z   }t+        |j,                  |j,                        }t        ||      }t/        |j(                        D ](  }t/        |j,                        D ]  }|||f   |||f<    * |j(                  }t/        |j,                        D ]+  }t/        |j(                        D ]  }|||f   ||||z   f<    - |S |	j1                  |       t
        j3                  ||	      }|	|k7  r|S )	zG
        Produces the generic layout for the supplied diagram.
        r-   r   r   rj   r   r   r-   c                 0    t         j                  |       S r   )r@   r   )rl   rx   s    r   r   z-DiagramGrid._generic_layout.<locals>.<lambda>  s    &44S.I r"   )r   r   )re   r   rP   r   r}   r@   rc   r   r	   rh   rn   r{   r   r   r   updater~   r   r   r   r   rw   r   r   rf   r   )r   r   all_objectsr   rk   r   rg   	root_edger   r   weldingry   r   restart_requirednew_objremaining_objectsremaining_diagramremaining_gridfinal_widthfinal_height
final_gridr   r   start_jrx   s                           @r   _generic_layoutzDiagramGrid._generic_layout  s   
 '//*{q  !A&D{+A.DJK../?@Q"x=A [.>?G DJ DJK//9	99)XN	$@@x! 9 +J K	  //	!hG	!*T
DJ"# Y+!8864)G +2(<#.#=#=lFD($D #%%11(;=
 &55vtX~G  )4n(D%(/(G(G!#45)7%%01B%CN #'**~/C/C"CK#&t{{N4I4I#JL!.{L!IJ"4::. :!&t{{!3 :A/3AqDzJq!t,:: #jjG">#8#89 N!&~';';!< NA9G19MJq'A+~6NN &%""7+#>>>+Ie +j r"   c                 ,   i }| D ]  }g ||<   	 |D ]R  }||j                      j                  |j                         ||j                     j                  |j                          T |j                         D ]  }||   j	                  t
                |S )z
        Given the objects and the relevant morphisms of a diagram,
        returns the adjacency lists of the underlying undirected
        graph.
        r   )r\   r.   r]   keyssortr	   )r   r   adjlistsr   rF   s        r   _get_undirected_graphz!DiagramGrid._get_undirected_graph   s      	CHSM	 ) 	@HX__%,,X->->?X&&'..x?	@ ==? 	5CSM#34	5 r"   c                     | j                   }t        |t              }t        j	                  ||      t        |fd      }t        dd      |d<   |h}fd d|       S )z
        Lays out the diagram in "sequential" layout.  This method
        will attempt to produce a result as close to a line as
        possible.  For linear diagrams, the result will actually be a
        line.
        r   c                      t        |          S r   rO   )xr   s    r   r   z0DiagramGrid._sequential_layout.<locals>.<lambda>%  s    Xa[1A r"   r-   r   c                     | d   | d   dz   f}|       D ]U  }||v rt         j                  ||g        |j                  |       |j                   ||             |d   dz   |d   f}W |S )z
            Does depth-first search in the underlying graph of the
            diagram and places the objects en route.
            r   r-   )r@   r   rf   r   )r   r   new_ptadjacent_objr   r   place_objectss       r   r  z5DiagramGrid._sequential_layout.<locals>.place_objects+  s     eRUQY'F (b 2 	4>1''dBG""<0%%mFN&KL )a-3	4 "!r"   )r   r   r	   r@   r  minr   )	r   r   r   sorted_objectsrootr   r   r   r  s	         @@@r   _sequential_layoutzDiagramGrid._sequential_layout  so     //-=> 44W>NO>'ABQ"T
	"* 	fn-r"   c                 b    | D cg c]  }|j                   |j                  k7  s|  }}|S c c}w )z
        Removes those morphisms which should appear in the diagram,
        but which have no relevance to object layout.

        Currently this removes "loop" morphisms: the non-identity
        morphisms with the same domains and codomains.
        )r\   r]   )r   ru   rD   s      r   _drop_inessential_morphismsz'DiagramGrid._drop_inessential_morphismsD  s2     !1K1AHH

4JQK	K Ls   ,,c                     i | D ]  }d|<   	 t         j                  | |      fdd}D ]  }|   	 ||       |dz  } t        |      D cg c]  }g  }}j                         D ]  \  }}||   j	                  |        g }|D ]r  }i }	|D ](  }
|
j
                  |v s|
j                  |v s!||
   |	|
<   * t        |      dk(  rt               |	t        |d         <   |j	                  t        |	             t |S c c}w )z
        Given a container of morphisms, returns a list of connected
        components formed by these morphisms.  A connected component
        is represented by a diagram consisting of the corresponding
        morphisms.
        Nc                 B    || <   |    D ]  }|   	 ||        y)zq
            Does a depth-first search traversal of the component
            containing ``object``.
            Nr>   )objectcurrent_indexoadjlistcomponent_indextraverse_components      r   r  zADiagramGrid._get_connected_components.<locals>.traverse_component_  s7    
 '4OF#V_ 9"1%-&q-89r"   r   r-   )r@   r  r   rB   r.   r\   r]   rP   r   r   r   )r   r   r  r  r   component_objectsidxcomponent_morphisms	componentcurrent_morphismsru   r  r  r  s              @@@r   _get_connected_componentsz%DiagramGrid._get_connected_componentsP  s[     	&A!%OA	& 33G=MN	9  	#Aq!)"1m4"	# */})=>AR>>%++- 	-FAsc"))!,	- !* 	CI "% ?HH	)

i0G+;A+>%a(? 9~" ENK!"29Q<"@A&&w/@'AB	C #"7 ?s   	DNc                    t         j                  |j                        }t         j                  |j                        }t         j	                  ||      }t         j                  |      }|| _        t         j                  |j                  |      }|r.||j                  k7  rt         j                  ||||      | _
        nt        |      dkD  rg }	t        |t              }|D ]  }
t        |
fi |}|	j                  |       ! t        d |	D              }t!        d |	D              }t#        ||      }d}|	D ]T  }t%        |j&                        D ]+  }t%        |j(                        D ]  }|||f   ||||z   f<    - ||j(                  z  }V || _
        nCd|v r$|d   dk(  r7t         j+                  ||      | _
        nt         j-                  ||      | _
        |j/                  d      rt#        | j                  j&                  | j                  j(                        }t%        | j                  j&                        D ]<  }t%        | j                  j(                        D ]  }| j                  ||f   |||f<    > || _
        y y )	Nr-   r   c              3   4   K   | ]  }|j                     y wr   )r   rt   gs     r   rv   z'DiagramGrid.__init__.<locals>.<genexpr>  s     5!agg5   c              3   4   K   | ]  }|j                     y wr   )r   r  s     r   rv   z'DiagramGrid.__init__.<locals>.<genexpr>  s     7Aqxx7r!  r   layout
sequential	transpose)r@   rH   rK   rL   rM   r  
_morphismsr  r   r   _gridrP   r   r	   r.   r   rw   r   r   r   r   r  r   r   )r   r   r   r   rK   rL   all_merged_morphismsr   rp   gridsr  r   total_widthtotal_heightr   r   r   r   s                     r   r   zDiagramGrid.__init__  si   2273C3CD!55g6I6IJ*FFk #&BB " / ::OO13
 v0$33!15:DJ_q  E  
0@AJ' #	"966T"#
 5u55K777L l;DG #qxx 7A"177^ 7/0AwQ!^,77 177"# DJX,.(;;-/
 %44W>NODJ99[! !2!2DJJ4D4DED4::,,- 2tzz//0 2A!%AqD!1DAJ22 DJ "r"   c                 .    | j                   j                  S )a  
        Returns the number of columns in this diagram layout.

        Examples
        ========

        >>> from sympy.categories import Object, NamedMorphism
        >>> from sympy.categories import Diagram, DiagramGrid
        >>> A = Object("A")
        >>> B = Object("B")
        >>> C = Object("C")
        >>> f = NamedMorphism(A, B, "f")
        >>> g = NamedMorphism(B, C, "g")
        >>> diagram = Diagram([f, g])
        >>> grid = DiagramGrid(diagram)
        >>> grid.width
        2

        )r'  r   r!   s    r   r   zDiagramGrid.width  s    * zzr"   c                 .    | j                   j                  S )a  
        Returns the number of rows in this diagram layout.

        Examples
        ========

        >>> from sympy.categories import Object, NamedMorphism
        >>> from sympy.categories import Diagram, DiagramGrid
        >>> A = Object("A")
        >>> B = Object("B")
        >>> C = Object("C")
        >>> f = NamedMorphism(A, B, "f")
        >>> g = NamedMorphism(B, C, "g")
        >>> diagram = Diagram([f, g])
        >>> grid = DiagramGrid(diagram)
        >>> grid.height
        2

        )r'  r   r!   s    r   r   zDiagramGrid.height  s    * zz   r"   c                 .    |\  }}| j                   ||f   S )a  
        Returns the object placed in the row ``i`` and column ``j``.
        The indices are 0-based.

        Examples
        ========

        >>> from sympy.categories import Object, NamedMorphism
        >>> from sympy.categories import Diagram, DiagramGrid
        >>> A = Object("A")
        >>> B = Object("B")
        >>> C = Object("C")
        >>> f = NamedMorphism(A, B, "f")
        >>> g = NamedMorphism(B, C, "g")
        >>> diagram = Diagram([f, g])
        >>> grid = DiagramGrid(diagram)
        >>> (grid[0, 0], grid[0, 1])
        (Object("A"), Object("B"))
        >>> (grid[1, 0], grid[1, 1])
        (None, Object("C"))

        )r'  r&   s       r   r(   zDiagramGrid.__getitem__  s     . 1zz!Q$r"   c                     | j                   S )a  
        Returns those morphisms (and their properties) which are
        sufficiently meaningful to be drawn.

        Examples
        ========

        >>> from sympy.categories import Object, NamedMorphism
        >>> from sympy.categories import Diagram, DiagramGrid
        >>> A = Object("A")
        >>> B = Object("B")
        >>> C = Object("C")
        >>> f = NamedMorphism(A, B, "f")
        >>> g = NamedMorphism(B, C, "g")
        >>> diagram = Diagram([f, g])
        >>> grid = DiagramGrid(diagram)
        >>> grid.morphisms
        {NamedMorphism(Object("A"), Object("B"), "f"): EmptySet,
        NamedMorphism(Object("B"), Object("C"), "g"): EmptySet}

        )r&  r!   s    r   rD   zDiagramGrid.morphisms  s    . r"   c                 @    t        | j                  j                        S )a  
        Produces a string representation of this class.

        This method returns a string representation of the underlying
        list of lists of objects.

        Examples
        ========

        >>> from sympy.categories import Object, NamedMorphism
        >>> from sympy.categories import Diagram, DiagramGrid
        >>> A = Object("A")
        >>> B = Object("B")
        >>> C = Object("C")
        >>> f = NamedMorphism(A, B, "f")
        >>> g = NamedMorphism(B, C, "g")
        >>> diagram = Diagram([f, g])
        >>> grid = DiagramGrid(diagram)
        >>> print(grid)
        [[Object("A"), Object("B")],
        [None, Object("C")]]

        )reprr'  r   r!   s    r   __str__zDiagramGrid.__str__/  s    0 DJJ%%&&r"   r   )&r9   r:   r;   r<   staticmethodrH   rM   rT   rY   rc   rh   rn   rq   r{   r~   r   r   r   r   r   r   r   r   r   r   r   r   r  r  r  r  r   r=   r   r   r(   rD   r2  r>   r"   r   r@   r@      st   AD  $ B B ? ?( & &  4    > > 	 	  : 2 2 L L      B  :    ^ ^@ @ @ 
I 
I 1 1 O Ob E EN ^ ^@  * + +Z 	 	 ;# ;#z>@    , ! !, 4  0'r"   r@   c                       e Zd ZdZd Zd Zy)ArrowStringDescriptionah  
    Stores the information necessary for producing an Xy-pic
    description of an arrow.

    The principal goal of this class is to abstract away the string
    representation of an arrow and to also provide the functionality
    to produce the actual Xy-pic string.

    ``unit`` sets the unit which will be used to specify the amount of
    curving and other distances.  ``horizontal_direction`` should be a
    string of ``"r"`` or ``"l"`` specifying the horizontal offset of the
    target cell of the arrow relatively to the current one.
    ``vertical_direction`` should  specify the vertical offset using a
    series of either ``"d"`` or ``"u"``.  ``label_position`` should be
    either ``"^"``, ``"_"``,  or ``"|"`` to specify that the label should
    be positioned above the arrow, below the arrow or just over the arrow,
    in a break.  Note that the notions "above" and "below" are relative
    to arrow direction.  ``label`` stores the morphism label.

    This works as follows (disregard the yet unexplained arguments):

    >>> from sympy.categories.diagram_drawing import ArrowStringDescription
    >>> astr = ArrowStringDescription(
    ... unit="mm", curving=None, curving_amount=None,
    ... looping_start=None, looping_end=None, horizontal_direction="d",
    ... vertical_direction="r", label_position="_", label="f")
    >>> print(str(astr))
    \ar[dr]_{f}

    ``curving`` should be one of ``"^"``, ``"_"`` to specify in which
    direction the arrow is going to curve. ``curving_amount`` is a number
    describing how many ``unit``'s the morphism is going to curve:

    >>> astr = ArrowStringDescription(
    ... unit="mm", curving="^", curving_amount=12,
    ... looping_start=None, looping_end=None, horizontal_direction="d",
    ... vertical_direction="r", label_position="_", label="f")
    >>> print(str(astr))
    \ar@/^12mm/[dr]_{f}

    ``looping_start`` and ``looping_end`` are currently only used for
    loop morphisms, those which have the same domain and codomain.
    These two attributes should store a valid Xy-pic direction and
    specify, correspondingly, the direction the arrow gets out into
    and the direction the arrow gets back from:

    >>> astr = ArrowStringDescription(
    ... unit="mm", curving=None, curving_amount=None,
    ... looping_start="u", looping_end="l", horizontal_direction="",
    ... vertical_direction="", label_position="_", label="f")
    >>> print(str(astr))
    \ar@(u,l)[]_{f}

    ``label_displacement`` controls how far the arrow label is from
    the ends of the arrow.  For example, to position the arrow label
    near the arrow head, use ">":

    >>> astr = ArrowStringDescription(
    ... unit="mm", curving="^", curving_amount=12,
    ... looping_start=None, looping_end=None, horizontal_direction="d",
    ... vertical_direction="r", label_position="_", label="f")
    >>> astr.label_displacement = ">"
    >>> print(str(astr))
    \ar@/^12mm/[dr]_>{f}

    Finally, ``arrow_style`` is used to specify the arrow style.  To
    get a dashed arrow, for example, use "{-->}" as arrow style:

    >>> astr = ArrowStringDescription(
    ... unit="mm", curving="^", curving_amount=12,
    ... looping_start=None, looping_end=None, horizontal_direction="d",
    ... vertical_direction="r", label_position="_", label="f")
    >>> astr.arrow_style = "{-->}"
    >>> print(str(astr))
    \ar@/^12mm/@{-->}[dr]_{f}

    Notes
    =====

    Instances of :class:`ArrowStringDescription` will be constructed
    by :class:`XypicDiagramDrawer` and provided for further use in
    formatters.  The user is not expected to construct instances of
    :class:`ArrowStringDescription` themselves.

    To be able to properly utilise this class, the reader is encouraged
    to checkout the Xy-pic user guide, available at [Xypic].

    See Also
    ========

    XypicDiagramDrawer

    References
    ==========

    .. [Xypic] https://xy-pic.sourceforge.net/
    c
                     || _         || _        || _        || _        || _        || _        || _        || _        |	| _        d| _	        d| _
        d| _        y )N F)unitcurvingcurving_amountlooping_startlooping_endhorizontal_directionvertical_directionlabel_positionlabellabel_displacementarrow_styleforced_label_position)
r   r8  r9  r:  r;  r<  r=  r>  r?  r@  s
             r   r   zArrowStringDescription.__init__  sc     	,*&$8!"4,
"$
 &+"r"   c                    | j                   r'd| j                   | j                  | j                  fz  }nd}| j                  r*| j                  rd| j                  d| j                  d}nd}| j
                  rd| j
                  z   }nd}d|||d| j                  | j                  d	| j                  | j                  d
| j                  dS )Nz	@/%s%d%s/r7  z@(,)@z\ar[]{})r9  r:  r8  r;  r<  rB  r=  r>  r?  rA  r@  )r   curving_strlooping_str	style_strs       r   r2  zArrowStringDescription.__str__  s    <<%t7J7J)-)4 4K K$"2"2(,(:(:D<L<LMKKd...II [)T5N5N'')<)<''5 	5r"   N)r9   r:   r;   r<   r   r2  r>   r"   r   r5  r5  J  s    `B+*5r"   r5  c                       e Zd ZdZd Zed        Zed        Zed        Zd Z	ed        Z
ed        Zed	        Zd
 Zed        Zed        ZddZy)XypicDiagramDrawera  
    Given a :class:`~.Diagram` and the corresponding
    :class:`DiagramGrid`, produces the Xy-pic representation of the
    diagram.

    The most important method in this class is ``draw``.  Consider the
    following triangle diagram:

    >>> from sympy.categories import Object, NamedMorphism, Diagram
    >>> from sympy.categories import DiagramGrid, XypicDiagramDrawer
    >>> A = Object("A")
    >>> B = Object("B")
    >>> C = Object("C")
    >>> f = NamedMorphism(A, B, "f")
    >>> g = NamedMorphism(B, C, "g")
    >>> diagram = Diagram([f, g], {g * f: "unique"})

    To draw this diagram, its objects need to be laid out with a
    :class:`DiagramGrid`::

    >>> grid = DiagramGrid(diagram)

    Finally, the drawing:

    >>> drawer = XypicDiagramDrawer()
    >>> print(drawer.draw(diagram, grid))
    \xymatrix{
    A \ar[d]_{g\circ f} \ar[r]^{f} & B \ar[ld]^{g} \\
    C &
    }

    For further details see the docstring of this method.

    To control the appearance of the arrows, formatters are used.  The
    dictionary ``arrow_formatters`` maps morphisms to formatter
    functions.  A formatter is accepts an
    :class:`ArrowStringDescription` and is allowed to modify any of
    the arrow properties exposed thereby.  For example, to have all
    morphisms with the property ``unique`` appear as dashed arrows,
    and to have their names prepended with `\exists !`, the following
    should be done:

    >>> def formatter(astr):
    ...   astr.label = r"\exists !" + astr.label
    ...   astr.arrow_style = "{-->}"
    >>> drawer.arrow_formatters["unique"] = formatter
    >>> print(drawer.draw(diagram, grid))
    \xymatrix{
    A \ar@{-->}[d]_{\exists !g\circ f} \ar[r]^{f} & B \ar[ld]^{g} \\
    C &
    }

    To modify the appearance of all arrows in the diagram, set
    ``default_arrow_formatter``.  For example, to place all morphism
    labels a little bit farther from the arrow head so that they look
    more centred, do as follows:

    >>> def default_formatter(astr):
    ...   astr.label_displacement = "(0.45)"
    >>> drawer.default_arrow_formatter = default_formatter
    >>> print(drawer.draw(diagram, grid))
    \xymatrix{
    A \ar@{-->}[d]_(0.45){\exists !g\circ f} \ar[r]^(0.45){f} & B \ar[ld]^(0.45){g} \\
    C &
    }

    In some diagrams some morphisms are drawn as curved arrows.
    Consider the following diagram:

    >>> D = Object("D")
    >>> E = Object("E")
    >>> h = NamedMorphism(D, A, "h")
    >>> k = NamedMorphism(D, B, "k")
    >>> diagram = Diagram([f, g, h, k])
    >>> grid = DiagramGrid(diagram)
    >>> drawer = XypicDiagramDrawer()
    >>> print(drawer.draw(diagram, grid))
    \xymatrix{
    A \ar[r]_{f} & B \ar[d]^{g} & D \ar[l]^{k} \ar@/_3mm/[ll]_{h} \\
    & C &
    }

    To control how far the morphisms are curved by default, one can
    use the ``unit`` and ``default_curving_amount`` attributes:

    >>> drawer.unit = "cm"
    >>> drawer.default_curving_amount = 1
    >>> print(drawer.draw(diagram, grid))
    \xymatrix{
    A \ar[r]_{f} & B \ar[d]^{g} & D \ar[l]^{k} \ar@/_1cm/[ll]_{h} \\
    & C &
    }

    In some diagrams, there are multiple curved morphisms between the
    same two objects.  To control by how much the curving changes
    between two such successive morphisms, use
    ``default_curving_step``:

    >>> drawer.default_curving_step = 1
    >>> h1 = NamedMorphism(A, D, "h1")
    >>> diagram = Diagram([f, g, h, k, h1])
    >>> grid = DiagramGrid(diagram)
    >>> print(drawer.draw(diagram, grid))
    \xymatrix{
    A \ar[r]_{f} \ar@/^1cm/[rr]^{h_{1}} & B \ar[d]^{g} & D \ar[l]^{k} \ar@/_2cm/[ll]_{h} \\
    & C &
    }

    The default value of ``default_curving_step`` is 4 units.

    See Also
    ========

    draw, ArrowStringDescription
    c                 J    d| _         d| _        d| _        i | _        d | _        y )Nmm      )r8  default_curving_amountdefault_curving_steparrow_formattersdefault_arrow_formatterr!   s    r   r   zXypicDiagramDrawer.__init__M  s-    	&'#$%! !# (,$r"   c                    d}d}d}d}g d}	|| |f   }
|j                         D ]3  \  }}|j                  |
k(  r{|j                  |
k(  rl|j                  |j                  }}||fdk(  r|	dxx   dz  cc<   n>||fdk(  r|	dxx   dz  cc<   n)||fdk(  r|	d	xx   dz  cc<   n||fd
k(  r|	dxx   dz  cc<   |j                  |
k(  r||j                     \  }}d}n%|j                  |
k(  r||j                     \  }}d}n|| z
  }||z
  }|j
                  }|dk7  rm|dk7  rh|dkD  r|dkD  r|	dxx   dz  cc<   |dkD  r|dk  r|	dxx   dz  cc<   ,|dk  r|dk  r|	d	xx   dz  cc<   E|dk  sL|dkD  sS|	dxx   dz  cc<   b|dk(  rd|dkD  r|rd}d}nd}d}n|rd	}d}nd}d	}|r*|dk(  r|	|xx   dz  cc<   |dk(  s|	|xx   dz  cc<   |	|xx   dz  cc<   |	|xx   dz  cc<   |dk(  s|dk  r|rd}d}nd}d}n|rd}d	}nd	}d}|r*|dk(  r|	|xx   dz  cc<   |dk(  s|	|xx   dz  cc<   |	|xx   dz  cc<   |	|xx   dz  cc<   6 d}t        d      D ]  } |	|    |	|   k  s| } g d|   \  }}||||fS )z
        Produces the information required for constructing the string
        representation of a loop morphism.  This function is invoked
        from ``_process_morphism``.

        See Also
        ========

        _process_morphism
        r7  ^)r   r   r   r   rur   r-   r]  lr_  drj   ra  r\  rS  TF_rT  )r[  r^  r`  rb  )rB   r\   r]   r;  r<  r9  r   )r   r   r   morphisms_str_infoobject_coordsr9  	label_posr;  r<  quadrantr   ru   
m_str_infol_sl_eend_iend_jgoes_outd_id_j	m_curvingupper_quadrantlower_quadrantleft_quadrantright_quadrantfreest_quadrants                             r   _process_loop_morphismz)XypicDiagramDrawer._process_loop_morphismZ  s@    	  1a4j/557 d	2MAzCajjC&7 )66
8N8Nc:+QK1$K3Z:-QK1$K3Z:-QK1$K3Z:-QK1$Kxx3!.qzz!:s"!.qxx!8 !)C!)C"**Iqsax !G#'QK1$KAgC!GQK1$KAgC!GQK1$KAgC!GQK1$K
 7)*)*)*)*)*)*)*)* C' 0A50"c) 0A50 ^,1,^,1,
 7())*())*())*())* C' /14/"c) 0A50 ]+q0+^,1,Id	2N q 	$A{Xo66"#	$
(44C(E$ M;??r"   c                 8   d}|}|}||k  r||}}d}g }	g }
g }t        |dz   |      D ]  }|| |f   }|s|D ]  }|j                  |k(  r||j                     \  }}n#|j                  |k(  r||j                     \  }}nH|| kD  r|
j                  |       _|| k  r|	j                  |       v||   j                  r|j                  |         t        |	      t        |
      k  rZ|rd}d}nd}d}|D ]F  }||j                     \  }}||j                     \  }}||   }||k  rd|_        nd|_        d|_        H ||fS |rd}d}nd}d}|D ]F  }||j                     \  }}||j                     \  }}||   }||k  rd|_        nd|_        d|_        H ||fS )z
        Produces the information required for constructing the string
        representation of a horizontal morphism.  This function is
        invoked from ``_process_morphism``.

        See Also
        ========

        _process_morphism
        FTr-   rc  rZ  r   r\   r]   r.   r9  rP   r?  rC  )r   r   target_jr   rd  re  	backwardsstartendupdownstraight_horizontalr   r   ru   rk  rl  r9  rf  r   r   r   r   rh  s                           r   _process_horizontal_morphismz/XypicDiagramDrawer._process_horizontal_morphism  s     	;CUI4  uqy#& 	2Aq!t*C' 288s?%21::%>NUEZZ3&%2188%<NUE19KKNQYIIaL+A.66 (..q12	2, r7SY 		 ) 8(2R(4R/2
703J-03J- 48
08N ##/ 		 ) 8(2R(4R/2
703J-03J- 48
08 ##r"   c                 8   d}| }|}||k  r||}}d}g }	g }
g }t        |dz   |      D ]  }|||f   }|s|D ]  }|j                  |k(  r||j                     \  }}n#|j                  |k(  r||j                     \  }}nH||kD  r|
j                  |       _||k  r|	j                  |       v||   j                  r|j                  |         t        |	      t        |
      k  rZ|rd}d}nd}d}|D ]F  }||j                     \  }}||j                     \  }}||   }||k  rd|_        nd|_        d|_        H ||fS |rd}d}nd}d}|D ]F  }||j                     \  }}||j                     \  }}||   }||k  rd|_        nd|_        d|_        H ||fS )z
        Produces the information required for constructing the string
        representation of a vertical morphism.  This function is
        invoked from ``_process_morphism``.

        See Also
        ========

        _process_morphism
        FTr-   rZ  rc  rx  )r   r   target_ir   rd  re  rz  r{  r|  leftrightstraight_verticalr   r   ru   rk  rl  r9  rf  r   r   r   r   rh  s                           r   _process_vertical_morphismz-XypicDiagramDrawer._process_vertical_morphismf  s     	;CUI uqy#& 	0Aq!t*C' 088s?%21::%>NUEZZ3&%2188%<NUE19LLOQYKKN+A.66 &,,Q/0	0, t9s5z! 		 ' 8(2R(4R/2
703J-03J- 48
08N ##/ 		 ' 8(2R(4R/2
703J-03J- 48
08 ##r"   c                    d }fd}fd}	||j                      \  }
}||j                     \  }}||
z
  }||z
  } ||dd      } ||dd      }d}d	}d}d}|d
k(  r$|d
k(  rt        j                  |
|||      \  }}}}ng|d
k(  r/t	        ||z
        dkD  rt        j                  |
||||      \  }}n3|d
k(  r.t	        |
|z
        dkD  rt        j                  |
||||      \  }} ||j                   |j                        }d}|r| j                  || j                  z  z   }n>|r<d	} |	|j                   |j                  |      }| j                  || j                  z  z   }d}t        |t              rdt        ||
|f         z   }nt        |t              rW|j                  D cg c]   }t        t        |j                              " }}|j!                          dj#                  |      }n.t        |t$              rt        t        |j                              }t'        | j(                  ||||||||	      S c c}w )zm
        Given the required information, produces the string
        representation of ``morphism``.
        c                 "    | dkD  r|| z  S ||  z  S )z
            If ``times > 0``, repeats ``str_gt`` ``times`` times.
            Otherwise, repeats ``str_lt`` ``-times`` times.
            r   r>   )timesstr_gtstr_lts      r   repeat_string_condz@XypicDiagramDrawer._process_morphism.<locals>.repeat_string_cond  s"    
 qy~%%((r"   c                 z    t        D cg c]!  }|j                  |j                  h| |hk(  r|# c}      S c c}w )zu
            Counts how many processed morphisms there are between the
            two supplied objects.
            )rP   r\   r]   )r   Bru   rd  s      r   count_morphisms_undirectedzHXypicDiagramDrawer._process_morphism.<locals>.count_morphisms_undirected  sE    
 #5 =aHHajj1aV;  = > > =s   &8c           	          t        j                         D cg c]3  \  }}|j                  |j                  f| |fk(  r|j                  |k(  r|5 c}}      S c c}}w )z
            Counts the processed morphisms which go out of ``dom``
            into ``cod`` with curving ``curving``.
            )rP   rB   r\   r]   r9  )r   r   r9  ru   rh  rd  s        r   count_morphisms_filteredzFXypicDiagramDrawer._process_morphism.<locals>.count_morphisms_filtered  s`    
 /A/G/G/I 9maHHajj1c3Z?#++w6  9 : : 9s   8A
ra  r]  r\  r_  r7  rZ  r   r-   zid_{%s}z\circ )r\   r]   rP  rv  r   r  r  rU  rV  rC   r   r
   r   rp   r   namereversejoinr   r5  r8  )r   r   r   rF   re  rD   rd  r  r  r  r   r   r  ry  delta_idelta_jr>  r=  r9  rf  r;  r<  countr:  filtered_morphismsmorphism_namer  component_namess         `                     r   _process_morphismz$XypicDiagramDrawer._process_morphism  s   	)	>	: x/A,X->->?8 Q,Q,/03S:1'25s < 	qLw!| /EEAt/@WilQ\!2Q!6#5#R#R1h&8-$I WilQ\!2Q!6#5#P#P1h&8-$I Wi +8??H<M<MN!885))<* *N
 G!9!2!2G"=!88"))**N
 h 01%d1a4j(99M"34,4,?,?A(  %VINN%;< AO A##%%NN?;M-0!&"78M%IIw-/A}& 	&As   )%H=c                      ||k  r||}}d}n||}}d} dk(  rd}n"t         fdt        ||dz         D              } j                  dz
  k(  rd}n#t         fdt        ||dz         D               }|||fS )z
        For a horizontal morphism, checks whether there is free space
        (i.e., space not occupied by any objects) above the morphism
        or below it.
        FTr   c              3   2   K   | ]  }d z
  |f     ywr   r>   rt   r   dom_ir   s     r   rv   zBXypicDiagramDrawer._check_free_space_horizontal.<locals>.<genexpr>F  s"      1$uqy!|, 1   r-   c              3   2   K   | ]  }d z   |f     ywr   r>   r  s     r   rv   zBXypicDiagramDrawer._check_free_space_horizontal.<locals>.<genexpr>M  s"       7qUQY\ 2  7r  )allr   r   any)	r  dom_jcod_jr   r{  r|  rz  free_up	free_downs	   `  `     r   _check_free_space_horizontalz/XypicDiagramDrawer._check_free_space_horizontal4  s     5=!5CUI!5CUI A:G 1sQw/1 1G DKK!O#I  7 %eS1W 5 7 7 7I I..r"   c                     | |k  r| |}}d}n|| }}d}dk(  rd}n#t        fdt        ||dz         D               }j                  dz
  k(  rd}n#t        fdt        ||dz         D               }|||fS )z
        For a vertical morphism, checks whether there is free space
        (i.e., space not occupied by any objects) to the left of the
        morphism or to the right of it.
        FTr   c              3   2   K   | ]  }|d z
  f     ywr   r>   rt   r   r  r   s     r   rv   z@XypicDiagramDrawer._check_free_space_vertical.<locals>.<genexpr>d  s"       7qQ	\ 2  7r  r-   c              3   2   K   | ]  }|d z   f     ywr   r>   r  s     r   rv   z@XypicDiagramDrawer._check_free_space_vertical.<locals>.<genexpr>j  s"      !8al!3 !8r  )r  r   r   )	r  cod_ir  r   r{  r|  rz  	free_left
free_rights	     ``     r   _check_free_space_verticalz-XypicDiagramDrawer._check_free_space_verticalR  s     5=!5CUI!5CUI A:I  7 %eS1W 5 7 7 7I DJJN"J  !8!&ucAg!6!8 8 8J :y11r"   c                    d }| |k  r||k  r| |}}||}	}d}
n| |kD  r||kD  r
||}}| |}	}d}
| |k  r||kD  r| |}}||}	}d}
n| |kD  r||k  r
||}}| |}	}d}
t        z
        	z
  z  }d}d} |||      D ]  }|s|s n} |||	      D ]o  }|s|s ||f||fk(  r||k(  rd}nt        ||z
        ||z
  z  }|||f   s7|dk(  st        |      t        |      kD  rd}Vt        |      t        |      k  snd}q  ||
fS )z
        For a diagonal morphism, checks whether there is free space
        (i.e., space not occupied by any objects) above the morphism
        or below it.
        c                 H    | |k  rt        | |dz         S t        || dz         S )Nr-   )r   )r{  r|  s     r   
abs_xrangezAXypicDiagramDrawer._check_free_space_diagonal.<locals>.abs_xrangev  s+    s{UC!G,,S%!),,r"   FTinf)floatr   )r  r  r  r  r   r  start_ir   rk  rl  rz  alphar  r  r   r   alpha1s                    r   _check_free_space_diagonalz-XypicDiagramDrawer._check_free_space_diagonalo  s   	- 5=UU] #(gW#UEUIU]uu} #(gW#UEUI5=UU] #(gW#UEUIU]uu} #(gW#UEUI6 ego&8	GU+ 	(A9/ (yq6gw//<"F"1w;/W=F1:%S[3u:-E$)	Vs5z1"'!(		(, I..r"   c           	         d }|j                         D ]  \  }}|j                  s|j                  r|j                  |j                  k(  r9||j                     \  }}||j                     \  }	}
||	k(  r*t
        j                  |||
|      \  }}} |||dd||       ||
k(  r*t
        j                  ||	||      \  }}} |||dd||       t
        j                  ||	||
|      \  }}} |||dd||        y)z
        For all straight morphisms which form the visual boundary of
        the laid out diagram, puts their labels on their outer sides.
        c                 B    |r||}}| r
|s||_         y|r| s||_         yyy)a  
            Given the information about room available to one side and
            to the other side of a morphism (``free1`` and ``free2``),
            sets the position of the morphism label in such a way that
            it is on the freer side.  This latter operations involves
            choice between ``pos1`` and ``pos2``, taking ``backwards``
            in consideration.

            Thus this function will do nothing if either both ``free1
            == True`` and ``free2 == True`` or both ``free1 == False``
            and ``free2 == False``.  In either case, choosing one side
            over the other presents no advantage.
            N)r?  )free1free2pos1pos2rz  rh  s         r   set_label_positionz?XypicDiagramDrawer._push_labels_out.<locals>.set_label_position  s1      $dtU,0
)u,0
)  %r"   rZ  rc  N)	rB   r9  rC  r\   r]   rP  r  r  r  )r   rd  r   re  r  ru   rh  r  r  r  r  r  r  rz  r  r  s                   r   _push_labels_outz#XypicDiagramDrawer._push_labels_out  s9   
	1, 0557 &	:MAz!!Z%E%E xx1::% *1884NUE*1::6NUE~ 1MME5$0) #7IsC#,j:% 1KKE5$0J #9j#s#,j:
 1KKE5%7) #7IsC#,j:K&	:r"   c                 0   || j                      \  }}|| j                     \  }}| j                   | j                  k(  rddt        |       fS ||k(  rdt        ||z
        t        |       fS ||k(  rdt        ||z
        t        |       fS ddt        |       fS )a  
        Provides a morphism sorting key such that horizontal or
        vertical morphisms between neighbouring objects come
        first, then horizontal or vertical morphisms between more
        far away objects, and finally, all other morphisms.
        rS  r   r-   rj   )r\   r]   r	   r   )rF   re  r   r   r  ry  s         r   _morphism_sort_keyz%XypicDiagramDrawer._morphism_sort_key	  s     x/A,X->->?8??h/// q*8455q=s8a<(*:8*DEEq=s8a<(*:8*DEE 1&x011r"   c                    i }| j                   D ]  }g ||<   	 |D ]   }||j                     j                  |       " d|z  }t        |j                        D ]  }	t        |j
                        D ]T  }
||	|
f   }|r1|t        |      dz   z  }||   }|D ]  }|t        ||         dz   z  } |
|j
                  dz
  k  sP|dz  }V |	|j                  dz
  k  r|dz  }|dz  } |dz  }|S )z
        Given a collection of :class:`ArrowStringDescription`
        describing the morphisms of a diagram and the object layout
        information of a diagram, produces the final Xy-pic picture.
        z\xymatrix%s{
 r-   z& z\\
z}
)r   r\   r.   r   r   r   r
   str)r   r   rD   rd  diagram_formatobject_morphismsr   rF   resultr   r   morphisms_to_draws               r   _build_xypic_stringz&XypicDiagramDrawer._build_xypic_string&	  s3    ?? 	'C$&S!	'! 	?HX__-44X>	? #^3t{{# 	A4::& #1a4jeCj3..F(8(=%$5 J#&8&B"Cc"IIJ tzzA~%dNF# 4;;?"& dNF#	& 	%r"   Nc           	         |s|j                   }n.i }|j                   j                         D ]  \  }}||v r|||<    i t        |j                        D ]0  }t        |j                        D ]  }	|||	f   s||	f|||	f   <    2 t        |fd      }
i }|
D ]~  }| j                  ||||
|      }| j                  r| j                  |       ||   D ]<  }|j                  | j                  v s| j                  |j                     } ||       > |||<    | j                  ||       t        j                  |||
||      S )a  
        Returns the Xy-pic representation of ``diagram`` laid out in
        ``grid``.

        Consider the following simple triangle diagram.

        >>> from sympy.categories import Object, NamedMorphism, Diagram
        >>> from sympy.categories import DiagramGrid, XypicDiagramDrawer
        >>> A = Object("A")
        >>> B = Object("B")
        >>> C = Object("C")
        >>> f = NamedMorphism(A, B, "f")
        >>> g = NamedMorphism(B, C, "g")
        >>> diagram = Diagram([f, g], {g * f: "unique"})

        To draw this diagram, its objects need to be laid out with a
        :class:`DiagramGrid`::

        >>> grid = DiagramGrid(diagram)

        Finally, the drawing:

        >>> drawer = XypicDiagramDrawer()
        >>> print(drawer.draw(diagram, grid))
        \xymatrix{
        A \ar[d]_{g\circ f} \ar[r]^{f} & B \ar[ld]^{g} \\
        C &
        }

        The argument ``masked`` can be used to skip morphisms in the
        presentation of the diagram:

        >>> print(drawer.draw(diagram, grid, masked=[g * f]))
        \xymatrix{
        A \ar[r]^{f} & B \ar[ld]^{g} \\
        C &
        }

        Finally, the ``diagram_format`` argument can be used to
        specify the format string of the diagram.  For example, to
        increase the spacing by 1 cm, proceeding as follows:

        >>> print(drawer.draw(diagram, grid, diagram_format="@+1cm"))
        \xymatrix@+1cm{
        A \ar[d]_{g\circ f} \ar[r]^{f} & B \ar[ld]^{g} \\
        C &
        }

        c                 0    t         j                  |       S r   )rP  r  )ru   re  s    r   r   z)XypicDiagramDrawer.draw.<locals>.<lambda>	  s    );)N)N -*1 r"   r   )rD   rB   r   r   r   r   r  rX  r  rW  r  rP  r  )r   r   r   maskedr  morphisms_propsru   rG   r   r   rD   rd  rF   string_descriptionprop	formatterre  s                   @r   drawzXypicDiagramDrawer.drawO	  s   ` "nnO O NN002 +5;%*"+ t{{# 	7A4::& 71:12AM$q!t*-7	7
 ? 12	  ! 	>H!%!7!7x	""$ ++,,-?@'1 299 5 55 $ 5 5dii @I01	2 ,>x(	>" 	0$F!55T9&8.J 	Jr"   )Nr7  )r9   r:   r;   r<   r   r3  rv  r  r  r  r  r  r  r  r  r  r  r>   r"   r   rP  rP    s    rf, L@ L@\ z$ z$x h$ h$Ta&F / /: 2 28 W/ W/rA:F 2 22 & &P|Jr"   rP  Nc                 X    t        | |fi |}t               }|j                  | |||      S )aa  
    Provides a shortcut combining :class:`DiagramGrid` and
    :class:`XypicDiagramDrawer`.  Returns an Xy-pic presentation of
    ``diagram``.  The argument ``masked`` is a list of morphisms which
    will be not be drawn.  The argument ``diagram_format`` is the
    format string inserted after "\xymatrix".  ``groups`` should be a
    set of logical groups.  The ``hints`` will be passed directly to
    the constructor of :class:`DiagramGrid`.

    For more information about the arguments, see the docstrings of
    :class:`DiagramGrid` and ``XypicDiagramDrawer.draw``.

    Examples
    ========

    >>> from sympy.categories import Object, NamedMorphism, Diagram
    >>> from sympy.categories import xypic_draw_diagram
    >>> A = Object("A")
    >>> B = Object("B")
    >>> C = Object("C")
    >>> f = NamedMorphism(A, B, "f")
    >>> g = NamedMorphism(B, C, "g")
    >>> diagram = Diagram([f, g], {g * f: "unique"})
    >>> print(xypic_draw_diagram(diagram))
    \xymatrix{
    A \ar[d]_{g\circ f} \ar[r]^{f} & B \ar[ld]^{g} \\
    C &
    }

    See Also
    ========

    XypicDiagramDrawer, DiagramGrid
    )r@   rP  r  )r   r  r  r   r   r   drawers          r   xypic_draw_diagramr  	  s3    H w0%0D!F;;wfn==r"   )r
   dvipng)r   )exemodulesc                 F    ddl m} t        | |||fi |}	 ||	|||d       y)a  
    Combines the functionality of ``xypic_draw_diagram`` and
    ``sympy.printing.preview``.  The arguments ``masked``,
    ``diagram_format``, ``groups``, and ``hints`` are passed to
    ``xypic_draw_diagram``, while ``output``, ``viewer, and ``euler``
    are passed to ``preview``.

    Examples
    ========

    >>> from sympy.categories import Object, NamedMorphism, Diagram
    >>> from sympy.categories import preview_diagram
    >>> A = Object("A")
    >>> B = Object("B")
    >>> C = Object("C")
    >>> f = NamedMorphism(A, B, "f")
    >>> g = NamedMorphism(B, C, "g")
    >>> d = Diagram([f, g], {g * f: "unique"})
    >>> preview_diagram(d)

    See Also
    ========

    XypicDiagramDrawer
    r   )preview)xypicN)sympy.printingr  r  )
r   r  r  r   outputviewereulerr   r  latex_outputs
             r   r   r   	  s2    8 '%gv~&,7057LL&&%<r"   )Nr7  N)Nr7  NpngNT)r<   sympy.categoriesr   r   r   r   
sympy.corer   r   r	   sympy.printing.latexr
   
sympy.setsr   sympy.utilities.iterablesr   sympy.utilities.decoratorr   	itertoolsr   __doctest_requires__r   r@   r5  rP  r  r   r>   r"   r   <module>r     s   Sh6 6 5 5 &   . 8  -h7 I+ I+XX' X'v$L5 L5^rJ rJj =?"&>R +[ADH59= B=r"   