
    iU                       d Z ddlmZ g dZddlmZ ddlmZ ddlmZ ddl	m
Z
mZmZmZmZmZmZmZmZ ddlmZmZmZmZ dd	lmZmZmZmZ e
rdd
lmZ d Z G d d      Z  G d d      Z! G d de      Z"ee#ge$f   Z%eee$e#f   eee$e#f   f   Z&ee&df   Z'eee'e#f      Z(ee#ee$e#f   f   Z) G d d      Z*eg df   Z+ G d d      Z,d!dZ-d"dZ.	 d#	 	 	 	 	 d$dZ/d%dZ0 e       Z1d&dZ2d&dZ3d'dZ4d  Z5y)(a  
A collection of utilities for canonicalizing and inspecting graphs.

Among other things, they solve of the problem of deterministic bnode
comparisons.

Warning: the time to canonicalize bnodes may increase exponentially on
degenerate larger graphs. Use with care!

Example of comparing two graphs::

    >>> g1 = Graph().parse(format='n3', data='''
    ...     @prefix : <http://example.org/ns#> .
    ...     <http://example.org> :rel
    ...         <http://example.org/same>,
    ...         [ :label "Same" ],
    ...         <http://example.org/a>,
    ...         [ :label "A" ] .
    ... ''')
    >>> g2 = Graph().parse(format='n3', data='''
    ...     @prefix : <http://example.org/ns#> .
    ...     <http://example.org> :rel
    ...         <http://example.org/same>,
    ...         [ :label "Same" ],
    ...         <http://example.org/b>,
    ...         [ :label "B" ] .
    ... ''')
    >>>
    >>> iso1 = to_isomorphic(g1)
    >>> iso2 = to_isomorphic(g2)

These are not isomorphic::

    >>> iso1 == iso2
    False

Diff the two graphs::

    >>> in_both, in_first, in_second = graph_diff(iso1, iso2)

Present in both::

    >>> def dump_nt_sorted(g):
    ...     for l in sorted(g.serialize(format='nt').splitlines()):
    ...         if l: print(l.decode('ascii'))

    >>> dump_nt_sorted(in_both) #doctest: +SKIP
    <http://example.org>
        <http://example.org/ns#rel> <http://example.org/same> .
    <http://example.org>
        <http://example.org/ns#rel> _:cbcaabaaba17fecbc304a64f8edee4335e .
    _:cbcaabaaba17fecbc304a64f8edee4335e
        <http://example.org/ns#label> "Same" .

Only in first::

    >>> dump_nt_sorted(in_first) #doctest: +SKIP
    <http://example.org>
        <http://example.org/ns#rel> <http://example.org/a> .
    <http://example.org>
        <http://example.org/ns#rel> _:cb124e4c6da0579f810c0ffe4eff485bd9 .
    _:cb124e4c6da0579f810c0ffe4eff485bd9
        <http://example.org/ns#label> "A" .

Only in second::

    >>> dump_nt_sorted(in_second) #doctest: +SKIP
    <http://example.org>
        <http://example.org/ns#rel> <http://example.org/b> .
    <http://example.org>
        <http://example.org/ns#rel> _:cb558f30e21ddfc05ca53108348338ade8 .
    _:cb558f30e21ddfc05ca53108348338ade8
        <http://example.org/ns#label> "B" .
    )annotations)IsomorphicGraphto_isomorphic
isomorphicto_canonical_graph
graph_diffsimilar)defaultdict)datetime)sha256)	TYPE_CHECKINGCallableDictIteratorListOptionalSetTupleUnion)ConjunctiveGraphGraphReadOnlyGraphAggregate_TripleType)BNodeIdentifiedNodeNodeURIRef)HASHc                r    | j                   dz  dz  dz  }|| j                  z  }|| j                  dz  z  }|S )N   <   g    .A)dayssecondsmicroseconds)tdresults     =C:\Projects\mas-dev\.venv\Lib\site-packages\rdflib/compare.py_total_secondsr(   r   s?    WWr\B#F
bjjF
boo	))FM    c                      e Zd Zd Zd Zy)_runtimec                    || _         y Nlabelselfr/   s     r'   __init__z_runtime.__init__z   	    
r)   c                V      j                   j                  dz    _          fd}|S )Nr+   c                     t        j                         } | i |}d|v r7|d   2|d   }t        t        j                         |z
        |j                  <   |S )Nstats)r   nowr(   r/   )argskwargsstartr&   r6   fr1   s        r'   	wrapped_fz$_runtime.__call__.<locals>.wrapped_f   sZ    LLNE''F& VG_%@w$28<<>E3I$Jdjj!Mr)   r/   __name__r1   r;   r<   s   `` r'   __call__z_runtime.__call__}   s*    ::j0DJ	 r)   Nr>   
__module____qualname__r2   r@    r)   r'   r+   r+   y       r)   r+   c                      e Zd Zd Zd Zy)_call_countc                    || _         y r-   r.   r0   s     r'   r2   z_call_count.__init__   r3   r)   c                V      j                   j                  dz    _          fd}|S )Nr+   c                     d|v r>|d   9|d   }j                   |vrd|j                   <   |j                   xx   dz  cc<    | i |S )Nr6   r      r.   )r8   r9   r6   r;   r1   s      r'   r<   z'_call_count.__call__.<locals>.wrapped_f   s[    & VG_%@w::U*()E$**%djj!Q&!d%f%%r)   r=   r?   s   `` r'   r@   z_call_count.__call__   s*    ::j0DJ	& r)   NrA   rD   r)   r'   rG   rG      rE   r)   rG   c                  H     e Zd ZdZ fdZd Zd Z fdZddZddZ	 xZ
S )	r   a%  An implementation of the RGDA1 graph digest algorithm.

    An implementation of RGDA1 (publication below),
    a combination of Sayers & Karp's graph digest algorithm using
    sum and SHA-256 <http://www.hpl.hp.com/techreports/2003/HPL-2003-235R1.pdf>
    and traces <http://pallini.di.uniroma1.it>, an average case
    polynomial time algorithm for graph canonicalization.

    McCusker, J. P. (2015). WebSig: A Digital Signature Framework for the Web.
    Rensselaer Polytechnic Institute, Troy, NY.
    http://gradworks.umi.com/3727015.pdf
    c                ,    t        t        | 
  di | y )NrD   )superr   r2   )r1   r9   	__class__s     r'   r2   zIsomorphicGraph.__init__   s    ot-77r)   c                    t        |t              syt        |       t        |      k7  ry| j                         |j                         k(  S )zGraph isomorphism testing.F)
isinstancer   leninternal_hashr1   others     r'   __eq__zIsomorphicGraph.__eq__   s?    %1Y#e*$!!#u':':'<<<r)   c                &    | j                  |       S )z#Negative graph isomorphism testing.)rV   rT   s     r'   __ne__zIsomorphicGraph.__ne__   s    ;;u%%%r)   c                (    t         t        |          S r-   )rN   r   __hash__)r1   rO   s    r'   rZ   zIsomorphicGraph.__hash__   s    _d466r)   c                &    | j                  |      S )z*Synonym for IsomorphicGraph.internal_hash.r6   )rS   r1   r6   s     r'   graph_digestzIsomorphicGraph.graph_digest   s    !!!..r)   c                8    t        |       j                  |      S )z
        This is defined instead of __hash__ to avoid a circular recursion
        scenario with the Memory store for rdflib which requires a hash lookup
        in order to return a generator of triples.
        r\   _TripleCanonicalizerto_hashr]   s     r'   rS   zIsomorphicGraph.internal_hash   s     $D)111>>r)   r-   )r>   rB   rC   __doc__r2   rV   rX   rZ   r^   rS   __classcell__)rO   s   @r'   r   r      s&    8=&7/?r)   r   .c                  R    e Zd Z	 	 d		 	 	 	 	 	 	 d
dZd Zd ZdddZddZd Zd Z	y)ColorNc                R    |i }|| _         || _        || _        || _        d | _        y r-   )_hash_cachecolornodeshashfunc_hash_color)r1   rj   rk   ri   
hash_caches        r'   r2   zColor.__init__   s4     J%

 r)   c                :    | j                         \  }}d|d|dS )NzColor z (z nodes)key)r1   rj   ri   s      r'   __str__zColor.__str__   s    xxzu(-u55r)   c                L    t        | j                        | j                         fS r-   )rR   rj   
hash_colorr1   s    r'   rp   z	Color.key   s    DJJ!233r)   c                D   || j                   }|| j                  v r| j                  |   S d }t        |t              r ||      S d}|D ]9  }|| j	                  dj                  |D cg c]
  } ||       c}            z  }; d|z  }|| j                  |<   |S c c}w )Nc                X    t        | t              r| j                         S t        |       S r-   )rQ   r   n3strxs    r'   	stringifyz#Color.hash_color.<locals>.stringify   s     !T"ttv1vr)   r    %x)ri   rh   rQ   r   rk   join)r1   ri   r{   valuetriplerz   vals          r'   rs   zColor.hash_color   s    =JJED$$$##E**	 eT"U##FT]]3886,J6aYq\6,J#KLLE %<"%
 -Ks   )Bc                x   i }| j                   D ]  }t        | j                        }|j                   D ]~  }||j                  |d |f      D 	cg c]  \  }}}	d||j	                         f c}	}}z  }||j                  |d |f      D 	cg c]  \  }}}	|j	                         |df c}	}}z  } t        |      }| j	                  |      }
|
|vr(t        g | j                  || j                        }|||
<   ||
   j                   j                  |        |j                         S c c}	}}w c c}	}}w )NrK      rm   )rj   listri   triplesrs   tuplerf   rk   rh   appendvalues)r1   Wgraphcolorsn	new_colornodesponew_hash_colorcs               r'   distinguishzColor.distinguish   s4   #%A/3DJJ/?I:?--DRV:X:Xwq!QQ1<<>*:X 	 :?--tUV:X:Xwq!QQ\\^Q*:X 		   i(I!__Y7NV+"dmmY4CSCST)*~&>"((//2   }}s   D.D5c                2    t        | j                        dk(  S )NrK   )rR   rj   rt   s    r'   discretezColor.discrete  s    4::!##r)   c                v    t        | j                  d d  | j                  | j                  | j                        S Nr   )rf   rj   rk   ri   rh   rt   s    r'   copyz
Color.copy  s.    JJqM4==$**AQAQ
 	
r)   )rD   N)rj   zList[IdentifiedNode]rk   HashFuncri   ColorItemTuplerm   	HashCacher-   )ri   zOptional[Tuple[ColorItem, ...]]returnrx   )r   rf   r   r   )
r>   rB   rC   r2   rq   rp   rs   r   r   r   rD   r)   r'   rf   rf      sS    
 !# $ #    	 
  64*($
r)   rf   r   c                      e Zd ZefddZddZddZd ZddZddZ	 e
d      ddd	       Zdd
Z	 d	 	 	 	 	 ddZ ed      ddgf	 	 	 	 	 	 	 dd       ZdddZ	 	 	 	 ddZy)ra   c                <    || _         dfd}i | _        || _        y )Nc                            }|j                  t        |       j                  d             t        |j	                         d      S )Nutf8   )updaterx   encodeint	hexdigest)r   hrk   s     r'   	_hashfuncz0_TripleCanonicalizer.__init__.<locals>._hashfunc#  s6    
AHHSV]]6*+q{{}b))r)   )r   rx   )r   rh   rk   )r1   r   rk   r   s     ` r'   r2   z_TripleCanonicalizer.__init__   s     
	*
 ')!r)   c                f    t        |D cg c]  }|j                         r| c}      dk(  S c c}w Nr   )rR   r   )r1   coloringr   s      r'   	_discretez_TripleCanonicalizer._discrete+  s,    x<x!qzz|Ax<=BB<s   ..c           
     l   t               }t               }t        t               | _        | j                  D ]  \  }}}t        |||g      }t        |D cg c]  }t	        |t
              s| c}      }t        |      dkD  sO|||z
  z  }||z  }t	        |t
              r| j                  |   j                  |       t	        |t
              r| j                  |   j                  |       t	        |t
              s| j                  |   j                  |       | j                  |   j                  |        t        |      dkD  r_t        t        |      | j                  | j                        g|D cg c]&  }t        |g| j                  || j                        ( c}z   S g S c c}w c c}w )a\  Finds an initial color for the graph.

        Finds an initial color of the graph by finding all blank nodes and
        non-blank nodes that are adjacent. Nodes that are not adjacent to blank
        nodes are not included, as they are a) already colored (by URI or literal)
        and b) do not factor into the color of any blank node.
        r   r   )setr
   
_neighborsr   rQ   r   rR   addrf   r   rk   rh   )	r1   bnodesothersr   r   r   rj   rz   bs	            r'   _initial_colorz#_TripleCanonicalizer._initial_color.  s}    !U%c*zzGAq!Aq	NE>1Au)=Q>?A1vz%!)#!a'OOA&**1-a'OOA&**1-a'OOA&**1-OOA&**1- " v;?$v,$BRBRST  	X  A qc4==!8H8HI	X   I' ?Xs   F,
*F,
:+F1c                
   t        |j                        }|j                  t        |j                        f       |j                  j                  |       t        |g| j                  t        |      | j                        }|S r   )
r   ri   r   rR   rj   removerf   rk   r   rh   )r1   ri   
individualr   r   s        r'   _individuatez!_TripleCanonicalizer._individuateP  sg    %	#ekk*,-:&L$--y)9dFVFV
 r)   c              #     K   |D cg c]  }|j                         r| c}D ]  }|j                  D ]  }||f 
  y c c}w wr-   )r   rj   )r1   r   r   r   s       r'   _get_candidatesz$_TripleCanonicalizer._get_candidatesZ  sA     %:XQZZ\!X:AAg   ;:s   AAA%Ac                $   t        |d d      }|d d  }t        |      dkD  r| j                  |      s|j                         }|d d  D ]  }t        |j                        dkD  st        |j                  d   t              s9t        |j                  || j                        d d      }|j                  |       |j                  |       	 |j                  |      }|d | |z   ||dz   d  z   } t        |      dkD  r| j                  |      sg }t               }|D ]U  }	|	j                         }
|
|v r)||
   j                  j                  |	j                         @|j                  |	       |	||
<   W |S # t        $ r |dd  |z   }Y Ew xY w)Nc                "    | j                         S r-   ro   ry   s    r'   <lambda>z._TripleCanonicalizer._refine.<locals>.<lambda>`  s
    !%%'r)   T)rp   reverser   rK   c                "    | j                         S r-   ro   ry   s    r'   r   z._TripleCanonicalizer._refine.<locals>.<lambda>h  s
    aeegr)   )sortedrR   r   poprj   rQ   r   r   r   r   extendindex
ValueErrordictrs   r   )r1   r   sequencer   r   r   sicombined_colorscombined_color_mapri   
color_hashs              r'   _refinez_TripleCanonicalizer._refine_  s   ((94HA;(max(@Aa[qww<!#z!''!*e'D#a4- $F
 OOA&OOF+9%^^A.#+CR=6#9HR!VX<N#N ! (max(@  (*/3vE))+J//":.44;;EKKH&&u-16":.   & 9#)!":#89s   "E::FFto_hash_runtimeNc                    d}| j                  |      D ]A  }|| j                  dj                  |D cg c]  }|j                          c}            z  }C |d|z  |d<   |S c c}w )Nr   r\   r|   r}   r^   )canonical_triplesrk   r~   rw   )r1   r6   r&   r   rz   s        r'   rb   z_TripleCanonicalizer.to_hash~  so    ,,5,9FdmmCHHf-Efaddff-E$FGGF :$(6ME.! .Fs   A%c                l   |D cg c]  }|j                          }}| j                  |      s{|D cg c]  }|j                         r| c}d   }|j                  d   }| j	                  ||      }|j                  |       | j                  ||g      }| j                  |      s{|S c c}w c c}w r   )r   r   r   rj   r   r   r   )r1   r   r   rz   ri   r   r   s          r'   _experimental_pathz'_TripleCanonicalizer._experimental_path  s    &./hAFFHh/..* (=1

Q=a@E;;q>D))%6IOOI&||Hyk:H ..*  0=s   B,B1	B1c                    |st        t              }t        | D ]C  }t        |D cg c]  }|j                  d    c}      }|D ]
  }|||   z  } |D ]  }|||<   	 E |S c c}w r   )r
   r   ziprj   )r1   	colorings	groupingsgroupr   gr   s          r'   _create_generatorz&_TripleCanonicalizer._create_generator  sx    
 #C(I)_E/AQWWQZ/0AYq\!  	! 	 %  0s   A"
individuationsr   c                   |	d|vrd|d<   |dxx   dz  cc<   | j                  |      }g }d }d }d }t        t              }	t               }
|D ]u  \  }}||	v r(|	|   |
z  }t        |      dkD  r|
j	                  |       3|
j	                  |       g }d }|D ]+  }|j                         }|j                  |       ||k(  s*|}- | j                  ||      }|j                  |       | j                  ||g      }t        |D cg c]  }|j                          c}      }| j                  |      }t        |D cg c]  }|j                          c}      }|r| j                  ||g|	      }	|}|||k  r	|g}|}|}5||kD  r|>|dxx   dz  cc<   M||k7  r|j                  |       e|i|dxx   dz  cc<   x |D cg c]  }| j                  |      s| }}t        |      dk(  rcd }d }|D ]U  }|d   g}| j                  |||      }t        D cg c]  }|j                          c}      }|||kD  sL|g}|}|d   }W ||d<   |d   S c c}w c c}w c c}w c c}w )Npruningsr   rK   r6   depth)r   r
   r   rR   r   r   r   r   r   r   rp   r   r   r   _traces)r1   r   r6   r   
candidatesbest
best_scorebest_experimental_scorelast_coloring	generatorvisited	candidateri   vcoloring_copy
color_copyr   c_copyr   refined_coloringcolor_scoreexperimentalexperimental_scorerz   r   
best_depthds                              r'   r   z_TripleCanonicalizer._traces  s    5!8 !E*aA))(3
"$
"&+6s+;	 U *IuI%i(72q6A:KK	*KK	")+MJ$$V,:!'J	 
 ))*i@I  +#||MI;G2B C2BQ2B CDK22=AL!$|%D|!aeeg|%D!E 22"L19	 )M!Z+%=()(
*<'k)$*%*%#'>>,- $*%*%M !+N 37&L$Q$..:Kq$&Lx=AJJ 1XJ LLaLH	#6F$G6FQUUW6F$GH%z)A ){H!,J!"1J ! "E!H{G !D%D( 'M %Hs   I
<I"

I'!I'!I,
c              #    K   |t        j                         }| j                         }|3t        | j                        |d<   t        dt        |      dz
        |d<   | j                  ||d d        }|1t        t        j                         z
        |d<   t        |      |d<   | j                  |      s"dg}| j                  |||      }||d   |d<   n|
d|d	<   d|d<   |t        |      |d
<   t        |D cg c]!  }|j                  d   |j                         f# c}      }|#t        t        j                         z
        |d<   | j                  D ]!  }t        | j                  ||            }| # y c c}w w)Ntriple_countr   rK   adjacent_nodesinitial_coloring_runtimeinitial_color_countr   
tree_depthr   color_countcanonicalize_triples_runtime)r   r7   r   rR   r   maxr   r(   r   r   r   rj   rs   r   _canonicalize_bnodes)	r1   r6   start_coloringr   r   r   bnode_labelsr   r&   s	            r'   r   z&_TripleCanonicalizer.canonical_triples  s    %\\^N&&($'

OE.!&)!S]Q->&?E"#<<(1+60>/1E,- ,/x=E'(~~h'CE||HE|GH &+Ahl#&'E"#"#E,#&x=E- (,3;<8aaggaj!,,.)8<)
 4B/5E01 jjF444V\JKFL ! =s   C<F>&F$A"Fc              #  l   K   |D ]+  }t        |t              rt        d||   z         (| - y w)Nzcb%s)r   )rQ   r   )r1   r   labelsterms       r'   r   z)_TripleCanonicalizer._canonicalize_bnodes  s5     
 D$&&6$<"788
	 s   24)r   r   rk   _HashT)r   List[Color]r   bool)r   r  )r   r  r   zIterator[Tuple[Node, Color]])r   r  r   r  r   r  r-   )r6   Optional[Stats])r   r  r   r  )r   zList[List[Color]]r   zOptional[Dict[Node, Set[Node]]]r   zDict[Node, Set[Node]])r   r  r6   r  r   z	List[int]r   r  )r   r   r   zDict[Node, str])r>   rB   rC   r   r2   r   r   r   r   r   r+   rb   r   r   rG   r   r   r   rD   r)   r'   ra   ra     s    8> 	"C D
>   ! 6:$ 3 
	 !" "&3	DD D 	D
 
D #DL"H		  	r)   ra   c                    t        | t              r| S t               }t        | d      rt        | j                        }|| z  }|S )N
identifier)r  )rQ   r   hasattrr  )r   r&   s     r'   r   r     sA    %)Ful# E,<,<=
eOFMr)   c                p    t        |       j                         }t        |      j                         }||k(  S )a  Compare graph for equality.

    Uses an algorithm to compute unique hashes which takes bnodes into account.

    Examples::

        >>> g1 = Graph().parse(format='n3', data='''
        ...     @prefix : <http://example.org/ns#> .
        ...     <http://example.org> :rel <http://example.org/a> .
        ...     <http://example.org> :rel <http://example.org/b> .
        ...     <http://example.org> :rel [ :label "A bnode." ] .
        ... ''')
        >>> g2 = Graph().parse(format='n3', data='''
        ...     @prefix ns: <http://example.org/ns#> .
        ...     <http://example.org> ns:rel [ ns:label "A bnode." ] .
        ...     <http://example.org> ns:rel <http://example.org/b>,
        ...             <http://example.org/a> .
        ... ''')
        >>> isomorphic(g1, g2)
        True

        >>> g3 = Graph().parse(format='n3', data='''
        ...     @prefix : <http://example.org/ns#> .
        ...     <http://example.org> :rel <http://example.org/a> .
        ...     <http://example.org> :rel <http://example.org/b> .
        ...     <http://example.org> :rel <http://example.org/c> .
        ... ''')
        >>> isomorphic(g1, g3)
        False
    r`   )graph1graph2gd1gd2s       r'   r   r   !  s4    > v
&
.
.
0C
v
&
.
.
0C#:r)   Nc                j    t               }|t        |       j                  |      z  }t        |g      S )zCreates a canonical, read-only graph.

    Creates a canonical, read-only graph where all bnode id:s are based on
    deterministical SHA-256 checksums, correlated with the graph contents.
    r\   )r   ra   r   r   )g1r6   r   s      r'   r   r   E  s6     GE	!"%77e7DDE!5'**r)   c                V    t        |       }t        |      }||z  }||z
  }||z
  }|||fS )zEReturns three sets of triples: "in both", "in first" and "in second".)r   )r  g2cg1cg2in_bothin_first	in_seconds          r'   r   r   R  s@     R
 C
R
 CCiGSyHc	IXy))r)   c                :    t        d t        | |      D              S )a@  Checks if the two graphs are "similar".

    Checks if the two graphs are "similar", by comparing sorted triples where
    all bnodes have been replaced by a singular mock bnode (the
    ``_MOCK_BNODE``).

    This is a much cheaper, but less reliable, alternative to the comparison
    algorithm in ``isomorphic``.
    c              3  ,   K   | ]  \  }}||k(    y wr-   rD   ).0t1t2s      r'   	<genexpr>zsimilar.<locals>.<genexpr>j  s     I(HHRrRx(Hs   )all_squashed_graphs_triples)r  r  s     r'   r	   r	   `  s     I(@R(HIIIr)   c              #     K   t        t        t        |             t        t        |                  D ]  \  }}||f  y wr-   )r   r   _squash_graph)r  r  r  r  s       r'   r  r  m  s9     f]2./b8I1JKB"f Ls   A Ac                    d | D        S )Nc              3  2   K   | ]  }t        |        y wr-   )_squash_bnodes)r  r   s     r'   r  z _squash_graph.<locals>.<genexpr>s  s     7vN6"s   rD   )r   s    r'   r  r  r  s    777r)   c                &    t        d | D              S )Nc              3  T   K   | ]   }t        |t              xr t        xs | " y wr-   )rQ   r   _MOCK_BNODE)r  ts     r'   r  z!_squash_bnodes.<locals>.<genexpr>w  s%     Mf*Q&6;<1<fs   &()r   )r   s    r'   r!  r!  v  s    MfMMMr)   )r   r   r   r   )r  r   r	  r   r   r  r-   )r  r   r6   r  r   r   )r  r   r  r   r   zTuple[Graph, Graph, Graph])r  r   r  r   )r   r   )6rc   
__future__r   __all__collectionsr
   r   hashlibr   typingr   r   r   r   r   r   r   r   r   rdflib.graphr   r   r   r   rdflib.termr   r   r   r   _hashlibr   r(   r+   rG   r   rx   r   r   	ColorItemr   r   Statsrf   r   ra   r   r   r   r   r$  r	   r  r  r!  rD   r)   r'   <module>r0     sU  IV # $  
 
 
 V U ; ; & &*?& *?Z SE3J%S/65c?:;	y#~&T.#-./	S%S/!"F
 F
R 
"f*	u up!J )-
+
+%
+
+* g
J
8Nr)   