
    h}7                     H   d Z ddlmZ ddlZddlmZmZ g dZej                  d        Z
ej                  d        Zd	 Zd
 Zd Zej                  dd       Zej                  dd       Zej                  dd       Z ed       ej                  d      d               Zy)z
Eulerian circuits and graphs.
    )combinationsN   )arbitrary_elementnot_implemented_for)is_eulerianeulerian_circuiteulerizeis_semieulerianhas_eulerian_patheulerian_pathc                       j                         r+t         fd D              xr t        j                         S t        d  j	                         D              xr t        j
                         S )ap  Returns True if and only if `G` is Eulerian.

    A graph is *Eulerian* if it has an Eulerian circuit. An *Eulerian
    circuit* is a closed walk that includes each edge of a graph exactly
    once.

    Graphs with isolated vertices (i.e. vertices with zero degree) are not
    considered to have Eulerian circuits. Therefore, if the graph is not
    connected (or not strongly connected, for directed graphs), this function
    returns False.

    Parameters
    ----------
    G : NetworkX graph
       A graph, either directed or undirected.

    Examples
    --------
    >>> nx.is_eulerian(nx.DiGraph({0: [3], 1: [2], 2: [3], 3: [0, 1]}))
    True
    >>> nx.is_eulerian(nx.complete_graph(5))
    True
    >>> nx.is_eulerian(nx.petersen_graph())
    False

    If you prefer to allow graphs with isolated vertices to have Eulerian circuits,
    you can first remove such vertices and then call `is_eulerian` as below example shows.

    >>> G = nx.Graph([(0, 1), (1, 2), (0, 2)])
    >>> G.add_node(3)
    >>> nx.is_eulerian(G)
    False

    >>> G.remove_nodes_from(list(nx.isolates(G)))
    >>> nx.is_eulerian(G)
    True


    c              3   d   K   | ]'  }j                  |      j                  |      k(   ) y wN	in_degree
out_degree).0nGs     V/var/www/django_project/virt/lib/python3.12/site-packages/networkx/algorithms/euler.py	<genexpr>zis_eulerian.<locals>.<genexpr>A   s+      
23AKKNall1o-
s   -0c              3   2   K   | ]  \  }}|d z  dk(    yw)r   r   N r   vds      r   r   zis_eulerian.<locals>.<genexpr>F   s     1daq1uz1   )is_directedallnxis_strongly_connecteddegreeis_connectedr   s   `r   r   r      se    R 	}}  
78
 
 *&&q)	*
 1ahhj11Hbooa6HH    c                 4    t        |       xr t        |        S )zReturn True iff `G` is semi-Eulerian.

    G is semi-Eulerian if it has an Eulerian path but no Eulerian circuit.

    See Also
    --------
    has_eulerian_path
    is_eulerian
    )r   r   r$   s    r   r
   r
   I   s     Q6A$66r%   c                 6    t               syt               rt               S  j                         r5 fd D        \  }} j	                  |       j                  |      kD  r|S |S  D cg c]  } j                  |      dz  dk7  s| c}d   }|S c c}w )zaReturn a suitable starting vertex for an Eulerian path.

    If no path exists, return None.
    Nc              3   j   K   | ]*  }j                  |      j                  |      k7  s'| , y wr   r   )r   r   r   s     r   r   z#_find_path_start.<locals>.<genexpr>c   s(     D!++a.ALLO"C!Ds   (33r   r   )r   r   r   r   r   r   r"   )r   v1v2r   starts   `    r   _find_path_startr,   W   s    
 Q1~ ##}}DQDB<<akk"o-II 6q!qA!56q9 7s   -B
Bc              #   h  K   | j                         r| j                  }| j                  }n| j                  }| j                  }|g}d }|rf|d   } ||      dk(  r|||f |}|j                          n7t         ||            \  }}|j                  |       | j                  ||       |rey y w)Nr   	r   r   	out_edgesr"   edgespopr   appendremove_edge)	r   sourcer"   r1   vertex_stacklast_vertexcurrent_vertex_next_vertexs	            r   _simplegraph_eulerian_circuitr;   p   s     }}8LK
%b).!Q&&"N33(K.u^/DENA{,MM.+6 s   B-B20B2c              #     K   | j                         r| j                  }| j                  }n| j                  }| j                  }|d fg}d }d }|rt|d   \  }} ||      dk(  r||||f ||}}|j                          n?t         ||d            }	|	\  }
}}|j                  ||f       | j                  |||       |rsy y w)Nr.   r   T)keysr/   )r   r5   r"   r1   r6   r7   last_keyr8   current_keytripler9   r:   next_keys                r   _multigraph_eulerian_circuitrB      s     }}TN#LKH
&22&6#.!Q&&"NH==$2KK&u^$'GHF'-$A{Hh 78MM.+x@ s   B?CCc              #   `  K   t        |       st        j                  d      | j                         r| j	                         } n| j                         } |t        |       }| j                         r&t        | |      D ]  \  }}}|r|||f ||f  yt        | |      E d{    y7 w)a.  Returns an iterator over the edges of an Eulerian circuit in `G`.

    An *Eulerian circuit* is a closed walk that includes each edge of a
    graph exactly once.

    Parameters
    ----------
    G : NetworkX graph
       A graph, either directed or undirected.

    source : node, optional
       Starting node for circuit.

    keys : bool
       If False, edges generated by this function will be of the form
       ``(u, v)``. Otherwise, edges will be of the form ``(u, v, k)``.
       This option is ignored unless `G` is a multigraph.

    Returns
    -------
    edges : iterator
       An iterator over edges in the Eulerian circuit.

    Raises
    ------
    NetworkXError
       If the graph is not Eulerian.

    See Also
    --------
    is_eulerian

    Notes
    -----
    This is a linear time implementation of an algorithm adapted from [1]_.

    For general information about Euler tours, see [2]_.

    References
    ----------
    .. [1] J. Edmonds, E. L. Johnson.
       Matching, Euler tours and the Chinese postman.
       Mathematical programming, Volume 5, Issue 1 (1973), 111-114.
    .. [2] https://en.wikipedia.org/wiki/Eulerian_path

    Examples
    --------
    To get an Eulerian circuit in an undirected graph::

        >>> G = nx.complete_graph(3)
        >>> list(nx.eulerian_circuit(G))
        [(0, 2), (2, 1), (1, 0)]
        >>> list(nx.eulerian_circuit(G, source=1))
        [(1, 2), (2, 0), (0, 1)]

    To get the sequence of vertices in an Eulerian circuit::

        >>> [u for u, v in nx.eulerian_circuit(G)]
        [0, 2, 1]

    zG is not Eulerian.N)
r   r    NetworkXErrorr   reversecopyr   is_multigraphrB   r;   r   r5   r=   ur   ks         r   r   r      s     ~ q>344}}IIKFFH~"1%3Av> 	GAq!Agd
		 1F;;;s   B$B.&B,'B.c                 
   t        j                  |       ry| j                         r| j                  }| j                  }|||   ||   z
  dk7  ryd}d}| D ]7  }||   ||   z
  dk(  r|dz  }||   ||   z
  dk(  r|dz  }+||   ||   k7  s7 y |dk  xr |dk  xr t        j
                  |       S || j                  |   dz  dk7  ryt        d | j                         D              dk(  xr t        j                  |       S )a  Return True iff `G` has an Eulerian path.

    An Eulerian path is a path in a graph which uses each edge of a graph
    exactly once. If `source` is specified, then this function checks
    whether an Eulerian path that starts at node `source` exists.

    A directed graph has an Eulerian path iff:
        - at most one vertex has out_degree - in_degree = 1,
        - at most one vertex has in_degree - out_degree = 1,
        - every other vertex has equal in_degree and out_degree,
        - and all of its vertices belong to a single connected
          component of the underlying undirected graph.

    If `source` is not None, an Eulerian path starting at `source` exists if no
    other node has out_degree - in_degree = 1. This is equivalent to either
    there exists an Eulerian circuit or `source` has out_degree - in_degree = 1
    and the conditions above hold.

    An undirected graph has an Eulerian path iff:
        - exactly zero or two vertices have odd degree,
        - and all of its vertices belong to a single connected component.

    If `source` is not None, an Eulerian path starting at `source` exists if
    either there exists an Eulerian circuit or `source` has an odd degree and the
    conditions above hold.

    Graphs with isolated vertices (i.e. vertices with zero degree) are not considered
    to have an Eulerian path. Therefore, if the graph is not connected (or not strongly
    connected, for directed graphs), this function returns False.

    Parameters
    ----------
    G : NetworkX Graph
        The graph to find an euler path in.

    source : node, optional
        Starting node for path.

    Returns
    -------
    Bool : True if G has an Eulerian path.

    Examples
    --------
    If you prefer to allow graphs with isolated vertices to have Eulerian path,
    you can first remove such vertices and then call `has_eulerian_path` as below example shows.

    >>> G = nx.Graph([(0, 1), (1, 2), (0, 2)])
    >>> G.add_node(3)
    >>> nx.has_eulerian_path(G)
    False

    >>> G.remove_nodes_from(list(nx.isolates(G)))
    >>> nx.has_eulerian_path(G)
    True

    See Also
    --------
    is_eulerian
    eulerian_path
    T   Fr   r   c              3   2   K   | ]  \  }}|d z  dk(    yw)r   rL   Nr   r   s      r   r   z$has_eulerian_path.<locals>.<genexpr>K  s     5$!Q1q5A:5r   )	r    r   r   r   r   is_weakly_connectedr"   sumr#   )r   r5   insoutsunbalanced_insunbalanced_outsr   s          r   r   r      s,   ~ 
~~a}}kk||$v,V"<"A 	A1vQ1$!#a3q6!Q&1$Q47"	 aVOq$8VR=S=STU=V	

 !((6"2Q"6!"; 5!((*55:Qrq?QQr%   c           
   #   @  K   t        | |      st        j                  d      | j                         r| j	                         } |t        j
                  |       du rt        |       }| j                         r&t        | |      D ]  \  }}}|r|||f ||f  yt        | |      E d{    y| j                         } |t        |       }| j                         ro|r7t        t        | |      D cg c]  \  }}}|||f c}}}      E d{    yt        t        | |      D cg c]
  \  }}}||f c}}}      E d{    yt        t        | |      D cg c]	  \  }}||f c}}      E d{    y7 c c}}}w 7 wc c}}}w 7 Jc c}}w 7 w)a  Return an iterator over the edges of an Eulerian path in `G`.

    Parameters
    ----------
    G : NetworkX Graph
        The graph in which to look for an eulerian path.
    source : node or None (default: None)
        The node at which to start the search. None means search over all
        starting nodes.
    keys : Bool (default: False)
        Indicates whether to yield edge 3-tuples (u, v, edge_key).
        The default yields edge 2-tuples

    Yields
    ------
    Edge tuples along the eulerian path.

    Warning: If `source` provided is not the start node of an Euler path
    will raise error even if an Euler Path exists.
    zGraph has no Eulerian paths.NF)r   r    rD   r   rE   r   r,   rG   rB   r;   rF   reversedrH   s         r   r   r   N  s    , Q'=>>}}IIK>R^^A.%7%a(F??76B 1aQ'MQ$J	 5Q???FFH>%a(F??#.J1f.UVV71aaAYV   $+G6+RSS1aaVS    $A!V$LMDAq!QM   @ W
 T
 Nsm   B+F-F.A	F7FFFF.F=F	F
F#F
1F<F=FFFFdirectedT)returns_graphc                    | j                         dk(  rt        j                  d      t        j                  |       st        j                  d      | j                         D cg c]  \  }}|dz  dk(  s| }}}t        j                  |       } t        |      dk(  r| S t        |d      D cg c]!  \  }}||t        j                  | ||      if# }}}t        |       dz   }t        j                         }|D ]D  \  }}|j                         D ],  \  }}	||k7  s|j                  |||t        |	      z
  |	       . F t        j                  t        t        j                  |                  }
|
j                         D ]>  \  }}||   |   d   }| j!                  t        j"                  j%                  |             @ | S c c}}w c c}}w )	a  Transforms a graph into an Eulerian graph.

    If `G` is Eulerian the result is `G` as a MultiGraph, otherwise the result is a smallest
    (in terms of the number of edges) multigraph whose underlying simple graph is `G`.

    Parameters
    ----------
    G : NetworkX graph
       An undirected graph

    Returns
    -------
    G : NetworkX multigraph

    Raises
    ------
    NetworkXError
       If the graph is not connected.

    See Also
    --------
    is_eulerian
    eulerian_circuit

    References
    ----------
    .. [1] J. Edmonds, E. L. Johnson.
       Matching, Euler tours and the Chinese postman.
       Mathematical programming, Volume 5, Issue 1 (1973), 111-114.
    .. [2] https://en.wikipedia.org/wiki/Eulerian_path
    .. [3] http://web.math.princeton.edu/math_alive/5/Notes1.pdf

    Examples
    --------
        >>> G = nx.complete_graph(10)
        >>> H = nx.eulerize(G)
        >>> nx.is_eulerian(H)
        True

    r   zCannot Eulerize null graphzG is not connectedr   rL   )r5   target)weightpathr[   )orderr    NetworkXPointlessConceptr#   rD   r"   
MultiGraphlenr   shortest_pathGraphitemsadd_edgelistmax_weight_matchingr1   add_edges_fromutilspairwise)r   r   r   odd_degree_nodesmodd_deg_pairs_pathsupper_bound_on_max_path_lengthGpPsPbest_matchingr[   s               r   r	   r	     s   V 	wwyA~))*FGG??1344&'hhj?daAEQJ??
aA
!
 !!115Aq 
Q  1Q789  &)VaZ" 
B$ 2HHJ 	DAqAvq!?#a&!Hq  	 HHT""8"8"<=>M ##% 21!uQx	**4012 HE @s   &G
7G
2&G)NFr   )__doc__	itertoolsr   networkxr    rg   r   r   __all___dispatchabler   r
   r,   r;   rB   r   r   r   r	   r   r%   r   <module>rv      s    #  : 0I 0If 
7 
727,A0 M< M<` [R [R| 3 3l Z %O & !Or%   