3
d'                 @   s   d 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

    N)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jt|d V  q2W dxd> |k rjd7 qTW fdd	}td
d | j D }g  d}x|D ]\}}	||krƈ jd  j	||	 q||d kr|d7 } jd  j	||	 q|} jd  j	||  jd  j	||	 qW dk r|d> krt   d kr||d k r jd  j	dgt   d   n j	dgt   d    fddt
dt  dD }
x"|
D ]}tj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).

       $   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   >/tmp/pip-build-7vycvbft/networkx/networkx/readwrite/sparse6.py
<listcomp><   s    z8_generate_sparse6_bytes.<locals>.enc.<locals>.<listcomp>)range)r   )r   )r   r   enc:   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>>   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   r   )bitsr   r   r   Z   s   z+_generate_sparse6_bytes.<locals>.<listcomp>   
Nl       @ )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   sL    



:


r3   c       	         s
  | j dr| dd } | j ds(tddd | dd D }t|\} dxd> |k rfd7 qPW  fd	d
}d}tj }|jt| d}x`| D ]V\}}|dkr|d7 }||ks||krP q||kr|}q|j||rd}|j|| qW |stj	|}|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 sparse6c             S   s   g | ]}|d  qS )r   r   )r   cr   r   r   r      s    z&from_sparse6_bytes.<locals>.<listcomp>r   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 )z6Returns stream of pairs b[i], x[i] for sparse6 format.Nr   r   r   )iternextStopIteration)chunksr1   ZdLenbr   ZxLen)r2   r   r   r   	parseData   s2    
z%from_sparse6_bytes.<locals>.parseDatar   FT)

startswithr   r   nxZ
MultiGraphZadd_nodes_fromr   Zhas_edgeZadd_edgeZGraph)	stringcharsr0   r;   r   r-   Z
multigraphr:   r   r   )r2   r   r   r   h   s6    !


Tc             C   s2   |dk	r| j |} tj| dd} dj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))
    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 ]$}|j  }t|s q
|j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| j |} tj| dd} xt| ||D ]}|j| q.W dS )a  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())
        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())
        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)   )r@   )rB   r=   rC   r3   write)r-   rH   r.   r/   r:   r   r   r   r
   :  s
    8
)NT)NT)__doc__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   Ra
2?
