B
    ,[)                 @   s   d Z ddlmZ ddlZddlZddlZddlmZ ddl	m
Z
mZ ddlmZmZ ddd	d
gZdd Zdd Zddd	Ze
ddddd Zede
dddddd
ZdS )aO  Functions for reading and writing graphs in the *sparse6* format.

The *sparse6* file format is a space-efficient format for large sparse
graphs. For small graphs or large dense graphs, use the *graph6* file
format.

For more information, see the `sparse6`_ homepage.

.. _sparse6: http://users.cecs.anu.edu.au/~bdm/data/formats.html

    )chainN)NetworkXError)	open_filenot_implemented_for)	data_to_n	n_to_datafrom_sparse6_bytesread_sparse6to_sparse6_byteswrite_sparse6c             #   s  t | }|dkrtd|r"dV  dV  x$t|D ]}tt|d V  q2W dxd> |k rjd7 qTW fdd}td	d
 |  D }g  d}x|D ]\}}	||krƈ d  	||	 q||d kr|d7 } d  	||	 q|} d  	||  d  	||	 qW dk r|d> krt   d kr||d k r d  	dgt   d   n 	dgt   d    fddt
dt  dD }
x"|
D ]}tt|d V  qW dV  dS )a%  Yield bytes in the sparse6 encoding of a graph.

    `G` is an undirected simple graph. `nodes` is the list of nodes for
    which the node-induced subgraph will be encoded; if `nodes` is the
    list of all nodes in the graph, the entire graph will be
    encoded. `header` is a Boolean that specifies whether to generate
    the header ``b'>>sparse6<<'`` before the remaining data.

    This function generates `bytes` objects in the following order:

    1. the header (if requested),
    2. the encoding of the number of nodes,
    3. each character, one-at-a-time, in the encoding of the requested
       node-induced subgraph,
    4. a newline character.

    This function raises :exc:`ValueError` if the graph is too large for
    the graph6 format (that is, greater than ``2 ** 36`` nodes).

    l       @ z?sparse6 is only defined if number of nodes is less than 2 ** 36s   >>sparse6<<   :?      c                s    fddt D S )zBig endian k-bit encoding of xc                s(   g | ] }d  d  | > @ r d ndqS )r   r    ).0i)kxr   9lib/python3.7/site-packages/networkx/readwrite/sparse6.py
<listcomp>J   s    z8_generate_sparse6_bytes.<locals>.enc.<locals>.<listcomp>)range)r   )r   )r   r   encH   s    z$_generate_sparse6_bytes.<locals>.encc             s   s&   | ]\}}t ||t||fV  qd S )N)maxmin)r   uvr   r   r   	<genexpr>L   s    z*_generate_sparse6_bytes.<locals>.<genexpr>r      c                sl   g | ]d} |d   d>  |d  d>   |d  d>   |d  d>   |d  d>   |d  d >  qS )r      r            r   )r   r   )bitsr   r   r   g   s   z+_generate_sparse6_bytes.<locals>.<listcomp>   
N)len
ValueErrorr   strencodechrsortededgesappendextendr   )Gnodesheaderndr   r*   Zcurvr   r   datar   )r"   r   r   _generate_sparse6_bytes%   sJ    



:


r3   c       	         s.  |  dr| dd } |  ds(tdtjdk rJdd | d	d D }nd
d | d	d D }t|\} d	xd	> |k rd	7 qrW  fdd}d}t }|t| d}xb| D ]X\}}|d	kr|d	7 }||ks||krP q||kr|}q|	||r
d}|
|| qW |s*t|}|S )aU  Read an undirected graph in sparse6 format from string.

    Parameters
    ----------
    string : string
       Data in sparse6 format

    Returns
    -------
    G : Graph

    Raises
    ------
    NetworkXError
        If the string is unable to be parsed in sparse6 format

    Examples
    --------
    >>> G = nx.from_sparse6_bytes(b':A_')
    >>> sorted(G.edges())
    [(0, 1), (0, 1), (0, 1)]

    See Also
    --------
    read_sparse6, write_sparse6

    References
    ----------
    .. [1] Sparse6 specification
           <http://users.cecs.anu.edu.au/~bdm/data/formats.html>

    s   >>sparse6<<   Nr   z!Expected leading colon in sparse6)r!   c             S   s   g | ]}t |d  qS )r   )ord)r   cr   r   r   r      s    z&from_sparse6_bytes.<locals>.<listcomp>r   c             S   s   g | ]}|d  qS )r   r   )r   r6   r   r   r   r      s    c              3   s   t  } d}d}x|dk r@yt| }W n tk
r:   dS X d}|d8 }||? d@ }|d|> d @ }|}xF|k ryt| }W n tk
r   dS X d}|d> | }|d7 }qjW || ? }| }||fV  qW dS )z5Return stream of pairs b[i], x[i] for sparse6 format.Nr   r   r   )iternextStopIteration)Zchunksr1   ZdLenbr   ZxLen)r2   r   r   r   	parseData   s2    
z%from_sparse6_bytes.<locals>.parseDatar   FT)
startswithr   sysversion_infor   nxZ
MultiGraphZadd_nodes_fromr   Zhas_edgeZadd_edgeZGraph)	stringcharsr0   r;   r   r-   Z
multigraphr:   r   r   )r2   r   r   r   o   s:    !



Tc             C   s2   |dk	r|  |} tj| dd} dt| ||S )a  Convert an undirected graph to bytes in sparse6 format.

    Parameters
    ----------
    G : Graph (undirected)

    nodes: list or iterable
       Nodes are labeled 0...n-1 in the order provided.  If None the ordering
       given by ``G.nodes()`` is used.

    header: bool
       If True add '>>sparse6<<' bytes to head of data.

    Raises
    ------
    NetworkXNotImplemented
        If the graph is directed.

    ValueError
        If the graph has at least ``2 ** 36`` nodes; the sparse6 format
        is only defined for graphs of order less than ``2 ** 36``.

    Examples
    --------
    >>> nx.to_sparse6_bytes(nx.path_graph(2))  # doctest: +SKIP
    b'>>sparse6<<:An\n'

    See Also
    --------
    to_sparse6_bytes, read_sparse6, write_sparse6_bytes

    Notes
    -----
    The returned bytes end with a newline character.

    The format does not support edge or node labels.

    References
    ----------
    .. [1] Graph6 specification
           <http://users.cecs.anu.edu.au/~bdm/data/formats.html>

    Nr)   )ordering    )subgraphr?   convert_node_labels_to_integersjoinr3   )r-   r.   r/   r   r   r   r
      s    ,
rb)modec             C   sN   g }x,| D ]$}|  }t|s q
|t| q
W t|dkrF|d S |S dS )a  Read an undirected graph in sparse6 format from path.

    Parameters
    ----------
    path : file or string
       File or filename to write.

    Returns
    -------
    G : Graph/Multigraph or list of Graphs/MultiGraphs
       If the file contains multiple lines then a list of graphs is returned

    Raises
    ------
    NetworkXError
        If the string is unable to be parsed in sparse6 format

    Examples
    --------
    You can read a sparse6 file by giving the path to the file::

        >>> import tempfile
        >>> with tempfile.NamedTemporaryFile() as f:
        ...     _ = f.write(b'>>sparse6<<:An\n')
        ...     _ = f.seek(0)
        ...     G = nx.read_sparse6(f.name)
        >>> list(G.edges())
        [(0, 1)]

    You can also read a sparse6 file by giving an open file-like object::

        >>> import tempfile
        >>> with tempfile.NamedTemporaryFile() as f:
        ...     _ = f.write(b'>>sparse6<<:An\n')
        ...     _ = f.seek(0)
        ...     G = nx.read_sparse6(f)
        >>> list(G.edges())
        [(0, 1)]

    See Also
    --------
    read_sparse6, from_sparse6_bytes

    References
    ----------
    .. [1] Sparse6 specification
           <http://users.cecs.anu.edu.au/~bdm/data/formats.html>

    r   r   N)stripr$   r+   r   )pathZglistliner   r   r   r	     s    3
Zdirectedr   wbc             C   sD   |dk	r|  |} tj| dd} xt| ||D ]}|| q.W dS )a3  Write graph G to given path in sparse6 format.

    Parameters
    ----------
    G : Graph (undirected)

    path : file or string
       File or filename to write

    nodes: list or iterable
       Nodes are labeled 0...n-1 in the order provided.  If None the ordering
       given by G.nodes() is used.

    header: bool
       If True add '>>sparse6<<' string to head of data

    Raises
    ------
    NetworkXError
        If the graph is directed

    Examples
    --------
    You can write a sparse6 file by giving the path to the file::

        >>> import tempfile
        >>> with tempfile.NamedTemporaryFile() as f:
        ...     nx.write_sparse6(nx.path_graph(2), f.name)
        ...     print(f.read())  # doctest: +SKIP
        b'>>sparse6<<:An\n'

    You can also write a sparse6 file by giving an open file-like object::

        >>> with tempfile.NamedTemporaryFile() as f:
        ...     nx.write_sparse6(nx.path_graph(2), f)
        ...     _ = f.seek(0)
        ...     print(f.read())  # doctest: +SKIP
        b'>>sparse6<<:An\n'

    See Also
    --------
    read_sparse6, from_sparse6_bytes

    Notes
    -----
    The format does not support edge or node labels.

    References
    ----------
    .. [1] Sparse6 specification
           <http://users.cecs.anu.edu.au/~bdm/data/formats.html>

    Nr)   )rB   )rD   r?   rE   r3   write)r-   rJ   r.   r/   r:   r   r   r   r   D  s
    8
)NT)NT)__doc__	itertoolsr   Zmathr=   Znetworkxr?   Znetworkx.exceptionr   Znetworkx.utilsr   r   Znetworkx.readwrite.graph6r   r   __all__r3   r   r
   r	   r   r   r   r   r   <module>   s    Jd
2?
