B
      ›\’  ã               @   s   d dl Z d dlZd dl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 d dlmZmZmZmZ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 d d	lm Z  d
d„ Z!G dd„ de"ƒZ#dd„ Z$G dd„ de"ƒZ%dd„ Z&dd„ Z'dd„ Z(dd„ Z)dd„ Z*dS )é    N)Úadd)Útypes)Úinfer_globalÚAbstractTemplate)Ú	signature)Úir_utilsÚirÚutilsÚconfigÚtyping)	Úget_call_tableÚmk_unique_varÚcompile_to_numba_irÚreplace_arg_nodesÚguardÚfind_callnameÚrequireÚ
find_constÚGuardException)Úexec_)ÚOPERATORS_TO_BUILTINSc             C   s   |dkr| | S | S d S )Nr   © )Údim_sizeÚindex_constr   r   ú2lib/python3.7/site-packages/numba/stencilparfor.pyÚ_compute_last_ind   s    r   c               @   sT   e Zd Zdd„ Zdd„ Zdd„ Zdd„ Zd	d
„ Zdd„ Zdd„ Z	dd„ Z
dd„ ZdS )ÚStencilPassc             C   s(   || _ || _|| _|| _|| _|| _d S )N)Úfunc_irÚtypemapÚ	calltypesÚarray_analysisÚ	typingctxÚflags)Úselfr   r   r   r    r!   r"   r   r   r   Ú__init__!   s    zStencilPass.__init__c                s<  ddl m} tˆ jjƒ\}}g }i }xD| ¡ D ]8\}}t|ƒdkr.t|d |ƒr.| |¡ |d ||< q.W |srdS xÂˆ jj ¡ D ]°\}}	x¤t	t
t|	jƒƒƒD ]Œ\}
‰tˆtjƒrætˆjtjƒræˆjjdkræˆjjj|krætˆjjƒ}‡fdd„ttˆjjƒƒD ƒ}ˆjj}t‡ fdd	„|D ƒƒ}x$|D ]}t|tjƒr6td
ƒ‚q6W | d¡}|ˆjjj }t|ˆ j||	j|	j |ˆ j!ˆ j"ƒ\}}}|j# dd¡}ˆ  $|||||ˆj%|||¡	}|	jd|
… | |	j|
d d…  |	_q¢tˆtjƒr¢tˆjtjƒr¢ˆjjdkr¢t&t'ˆ jˆjƒdkr¢t (dˆj ¡ˆ_q¢W q‚W dS )zP Finds all calls to StencilFuncs in the IR and converts them to parfor.
        r   )ÚStencilFuncé   NÚcallc                s   i | ]}ˆ j j| |“qS r   )ÚvalueÚargs)Ú.0Úi)Ústmtr   r   ú
<dictcomp>E   s    z#StencilPass.run.<locals>.<dictcomp>c             3   s   | ]}ˆ j |j V  qd S )N)r   Úname)r*   r+   )r#   r   r   ú	<genexpr>H   s    z"StencilPass.run.<locals>.<genexpr>zITuple parameters not supported for stencil kernels in parallel=True mode.ÚoutÚindex_offsets)ÚstencilÚnumba))Znumba.stencilr%   r   r   ÚblocksÚitemsÚlenÚ
isinstanceÚappendÚreversedÚlistÚ	enumerateÚbodyr   ÚAssignr(   ÚExprÚopÚfuncr.   ÚdictÚkwsÚranger)   Útupler   Z	BaseTupleÚ
ValueErrorÚgetÚget_stencil_irr!   ÚscopeÚlocr   r   ÚoptionsÚ_mk_stencil_parforÚtargetr   r   ÚConst)r#   r%   Z
call_tableÚ_Zstencil_callsZstencil_dictZcall_varnameZ	call_listÚlabelÚblockr+   rB   Ú
input_dictÚin_argsZarg_typemapZarg_typeÚout_arrÚsfÚ
stencil_irZrtÚarg_to_arr_dictr1   Ú	gen_nodesr   )r#   r,   r   Úrun)   sR    
 




(zStencilPass.runc          	   C   s´   x®|  ¡ D ]¢\}}|j}|j}g }x‚|jD ]x}	t|	tjƒr˜| ¡ }
t|
tjƒrht|
j	tj
ƒrh|
j	jdkslt‚| t |
j	j	||¡¡ | t ||¡¡ q*| |	¡ q*W ||_q
W dS )zß
        Find return statements in the IR and replace them with a SetItem
        call of the value "returned" by the kernel into the result array.
        Returns the block labels that contained return statements.
        ÚcastN)r5   rH   rI   r<   r7   r   ÚReturnÚpopr=   r(   r>   r?   ÚAssertionErrorr8   ZJump)r#   r4   Úexit_value_varÚparfor_body_exit_labelrO   rP   rH   rI   Únew_bodyr,   Z	prev_stmtr   r   r   Úreplace_return_with_setitemd   s    z'StencilPass.replace_return_with_setitemc
       F   
   C   s¨  g }
|j }tjdkr4td|||||||ƒ t |¡ |d }| j|j }t || j¡\}}t 	|¡}t 
|||| j| j¡ tjdkr–tdƒ t |¡ t || jj|| j¡ tjdkrÈtdƒ t |¡ | j|j j}|j}|j}g }x:t|ƒD ].}t |tdƒ|¡}tj| j|j< | |¡ qðW |  ||||||	¡\}}tjdkrXtdƒ t |¡ g }| j |¡}| |¡}|t|ƒks„t‚x\t|ƒD ]P}|  || || |
||¡}|   || |
||¡}| t!j" #|| ||d¡¡ qŽW t$| %¡ ƒd }t &||¡||< t |tdƒ|¡}|j'| j|j< g } |dkr:|d }!nPt |td	ƒ|¡}!tj( )tj|¡| j|!j< tj* +||¡}"t ,|"|!|¡}#|  |#¡ tjdkr¨td
ƒ t |¡ t &||¡}$|dkrÎ| j|j }t d¡}%t ||%|¡}&tj* -|d|¡}'tj( )tj|j¡| j|%< |$j. /t ,|'|&|¡g¡ t d¡}(t ||(|¡})d|j0krx|j0d }*|j't1j2 2|*¡krlt3dƒ‚| '|*¡}+n
| 'd¡}+t 4|+|¡},|j'| j|(< |$j. /t ,|,|)|¡g¡ t d¡}-t ||-|¡}t!jj5 6|j'|j|j7¡| j|j< t |tdƒ|¡}.tj8 9t:¡| j|.j< t ;dt:|¡}/t ,|/|.|¡}0|$j. |0¡ tj* -|.|j'j|¡}1t |tdƒ|¡}2tj< =|j'¡| j|2j< t ,|1|2|¡}3|$j. |3¡ t >dt:j?||&|)|2g| j@| j| j¡}4| A||¡ |$j. /|4¡ n¦d|j0krt|j0d }*t1j2 2|*¡}5| j@ B|5|j'¡sd}6t3|6ƒ‚t |tdƒ|¡}7| j@ CtD¡}8|8| j|7j< t ;dtD|¡}9t ,|9|7|¡}:|$j. |:¡ | j@ E|8tjFfd i ¡};tj*jG|7dd|d}<|;| j|<< t |tdƒ|¡}=tjH| j|=j< t ,|<|=|¡}>|$j. |>¡ t 4| '|*¡|¡}?t |tdƒ|¡}@|j'| j|@j< t ,|?|@|¡}A|$j. |A¡ t I|tDddƒ|=|@|¡}B|$j. |B¡ tJtjF| j|j | j|=j | j|j j'ƒ};|;| j|B< |  K|||¡ tjdkr tdƒ t |¡ t L||!||¡}CtJtjF| j|j | j|!j | j|j j'ƒ| j|C< || j. /| ¡ || j. |C¡ || j. t Mdt Ndd¡¡¡ t O|¡}|t$| %¡ ƒ j. P¡  tjdkr^td ƒ t |¡ d!||gf}Dt!j" Q||$|||!||D| jR¡}E|
 |E¡ |
 t ,|||¡¡ |
S )"z> Converts a set of stencil kernel blocks to a parfor.
        r&   rK   r   z#stencil_blocks after copy_propagatez'stencil_blocks after removing dead codez$parfor_index_varz-stencil_blocks after replace stencil accessesz$parfor_exit_valuez$parfor_index_tuple_varz.stencil_blocks after creating parfor index varNZin_arr_shapeÚshapeZzero_valÚcvalz-cval type does not match stencil return type.Zstencil_outputz	$np_g_varÚnpz$np_attr_attrÚfullz	$py_g_varÚsliceé   r   )r@   r)   rB   rI   z$slice_instz$cval_constz%stencil_blocks after replacing returnZstencilparfor_dummyéÿÿÿÿz#stencil_blocks after adding SetItemr2   )Sr4   r
   ÚDEBUG_ARRAY_OPTÚprintr   Údump_blocksr   r.   Zcopy_propagateÚget_name_var_tableZapply_copy_propagater   Zremove_deadr   Z	arg_namesÚndimrH   rI   rC   r   ÚVarr   r   Úintpr8   Ú_replace_stencil_accessesr    Zget_equiv_setZ	get_shaper6   r\   Ú_get_stencil_last_indÚ_get_stencil_start_indr3   ÚparforZLoopNestÚmaxÚkeysZBlockÚdtypeÚ
containersÚUniTupler>   Úbuild_tupler=   Úgetattrr<   ÚextendrJ   r   ZtypeofrE   rM   ZnpytypesZArrayZlayoutÚmiscZModulerc   ÚGlobalÚ	functionsZNumberClassZgen_np_callrd   r!   Zinsert_equivZcan_convertZresolve_value_typere   Úresolve_function_typeZnoner'   Zslice2_typeÚStaticSetItemr   r`   ÚSetItemrZ   ZLocZsimplify_CFGr[   ZParforr"   )Fr#   rO   rR   rS   rU   r1   rL   Úreturn_typeÚstencil_funcrV   rW   Ústencil_blocksÚin_arrZ
in_arr_typZin_cpsZout_cpsÚname_var_tableÚndimsrH   rI   Úparfor_varsr+   Z
parfor_varÚstart_lengthsÚend_lengthsZ	loopnestsZ	equiv_setZin_arr_dim_sizesÚlast_indZ	start_indr^   r]   Zfor_replacing_retZparfor_ind_varÚ
tuple_callÚtuple_assignZ
init_blockZ
shape_nameZ	shape_varZshape_getattrZ	zero_nameZzero_varrb   Ztemp2Z
full_constZso_nameZdtype_g_np_varZ
dtype_g_npZdtype_g_np_assignZdtype_np_attr_callZdtype_attr_varZdtype_attr_assignZstmtsZcval_tyÚmsgÚ	slice_varZslice_fn_tyZslice_gZslice_assignedZsigZcallexprZslice_inst_varZslice_assignZcval_const_valZcval_const_varZcval_const_assignZsetitemexprZsetitem_callÚpatternrr   r   r   r   rK   }   s^   



































zStencilPass._mk_stencil_parforc             C   s4  |}|dkr0t  |tdƒ|¡}tj| j|j< t|tj	ƒrRt  
t  ||¡||¡}nt  
|||¡}| |¡ t  |tdƒ|¡}tj| j|j< t  |tdƒ|¡}	t t¡}
tj |
¡}|| j|	j< t  d|
|¡}t  
||	|¡}| |¡ t j |	||gd|¡}| | jtjtjgi ¡| j|< t  
|||¡}| |¡ |S )Nr   Zstencil_const_varrŠ   Zcompute_last_ind_varr   r   )r   rm   r   r   rn   r   r.   r7   ÚnumbersÚNumberr=   rM   r8   r3   Znjitr   r}   Z
Dispatcherr|   r>   r'   Zget_call_typer!   r   )r#   r   Z
end_lengthrW   rH   rI   rŠ   r   Úconst_assignZg_varZ
check_funcZfunc_typZg_objZg_assignÚ
index_callÚindex_assignr   r   r   rp   e  s2    





z!StencilPass._get_stencil_last_indc       	      C   sŒ   t |tƒrtt|dƒƒS dd„ }t|i | jtjf| j| j	ƒ}t
|jƒdksNt‚|j ¡ d }t||gƒ ||jd d… 7 }|jd jj}|S )Nr   c             S   s   t t| dƒƒS )Nr   )ÚabsÚmin)Zs_lengthr   r   r   Úget_start_indŠ  s    z9StencilPass._get_stencil_start_ind.<locals>.get_start_indr&   éþÿÿÿ)r7   Úintr•   r–   r   r!   r   rn   r   r   r6   r4   r\   Úpopitemr   r<   r(   )	r#   Zstart_lengthrW   rH   rI   r—   Úf_irrP   Zret_varr   r   r   rq   ‡  s    

z"StencilPass._get_stencil_start_indc          	      sÂ  ˆj }|d }dd„ |D ƒ}	d|jkrdx"|jd D ]}
|
ˆ kr2tdƒ‚q2W ‡ fdd„|jd D ƒ}ng }|j|krztdƒ‚ˆj|j j}|j}|j}|jdk}|r¸|dg }|dg }n d	d„ |jD ƒ}d
d„ |jD ƒ}t	 
|¡}d}x¼| ¡ D ]®\}}g }x˜|jD ]Œ}t|tjƒrLt|jtjƒrL|jjdkrL|jjj|	ksvt|tjƒsht|tjƒr~|jj|	kr~tdƒ‚t|tjƒrŒt|jtjƒrŒ|jjdkrŒ|jjj|	krŒ|jjj|krŒ|jj}|dkrä|g}n"t|dƒr|j|kr||j }t	 |¡ˆ_‡‡fdd„|D ƒ}|rBˆ |t|ƒ|||¡}|r–t|tjƒsjtdd„ |D ƒƒrrtdƒ‚ttt||ƒƒ}ttt ||ƒƒ}d}ˆ |t|ƒ|||¡}|dkrÀ|d }nPt |t!dƒ|¡}t"j# $t"j%|¡ˆj|j< tj &||¡}t |||¡}| '|¡ t(‡fdd„|D ƒƒr<ˆj|jjj j)}nˆj|jjj }tj *|jj||¡}t+|ˆj|jjj ˆj|j ƒˆj,|< ||_| '|¡ q
W ||_qòW |rº|sºtdƒ‚||fS )zÄ Convert relative indexing in the stencil kernel to standard indexing
            by adding the loop index variables to the corresponding dimensions
            of the array index tuples.
        r   c             S   s   g | ]
}|j ‘qS r   )r.   )r*   Úxr   r   r   ú
<listcomp>  s    z9StencilPass._replace_stencil_accesses.<locals>.<listcomp>Zstandard_indexingz[Standard indexing requested for an array name not present in the stencil kernel definition.c                s   g | ]}ˆ | ‘qS r   r   )r*   rœ   )rV   r   r   r   ¤  s    zYThe first argument to a stencil kernel must use relative indexing, not standard indexing.Nc             S   s   g | ]}|d  ‘qS )r   r   )r*   rœ   r   r   r   r   »  s    c             S   s   g | ]}|d  ‘qS )r&   r   )r*   rœ   r   r   r   r   ¼  s    F)ÚsetitemZstatic_setitemz?Assignments to arrays passed to stencil kernels is not allowed.)Zstatic_getitemÚgetitemr&   r.   c                s   g | ]}t ˆˆ j|ƒ‘qS r   )Ú_get_const_index_exprr   )r*   Úv)r#   rU   r   r   r   æ  s   c             S   s   g | ]}t |tƒ ‘qS r   )r7   r™   )r*   r¡   r   r   r   r   ñ  s    z<Variable stencil index only possible with known neighborhoodTz$parfor_index_ind_varc                s   g | ]}ˆ j |j tjk‘qS r   )r   r.   r   rn   )r*   r¡   )r#   r   r   r   
  s   z=Stencil kernel with no accesses to relatively indexed arrays.)-r4   rJ   rE   r.   r   rl   rH   rI   Zneighborhoodr   Zget_tuple_tabler5   r<   r7   r   r=   r(   r>   r?   r€   r   rL   ÚindexÚhasattrZbuild_definitionsZ_definitionsÚ_add_index_offsetsr:   rm   ÚanyÚmapr–   rs   r   r   rv   rw   rn   rx   r8   Úallru   rŸ   r   r   )r#   rU   r‡   rR   r1   r‚   rV   rƒ   r„   Zin_arg_namesrœ   Zstandard_indexedr†   rH   rI   Zneed_to_calc_kernelrˆ   r‰   Ztuple_tableZfound_relative_indexrO   rP   r_   r,   Ú
index_listÚ
index_varsZind_varr‹   rŒ   Zgetitem_return_typZgetitem_callr   )rV   r#   rU   r   ro   •  s¬    












z%StencilPass._replace_stencil_accessesc             C   sB  t |ƒt |ƒkst‚tdd„ || D ƒƒr:ttt||ƒƒS g }g }xîtt |ƒƒD ]Ü}|| }	t|	tƒr®t	 
|tdƒ|¡}	tj| j|	j< t	 t	 || |¡|	|¡}
| |
¡ || }t|tƒrt	 
|tdƒ|¡}tj| j|j< t	 t	 || |¡||¡}
| |
¡ t|	tƒs*t| j|	j tjjƒr`| j|j tjksBt‚|  |	||||¡}| |¡ qRt|tƒs„t| j|j tjjƒrº| j|	j tjksœt‚|  ||	|||¡}| |¡ qRt	 
|tdƒ|¡}tj| j|j< t	j tj|	||¡}| j tjtjtjfi ¡| j|< t	 |||¡}| |¡ | |¡ qRW | |¡ |S )zw Does the actual work of adding loop index variables to the
            relative index constants or variables.
        c             S   s   g | ]}t |tƒ‘qS r   )r7   r™   )r*   r¡   r   r   r   r   *  s    z2StencilPass._add_index_offsets.<locals>.<listcomp>Úold_index_varÚ
offset_varZoffset_stencil_index)r6   r\   r§   r:   r¦   r   rC   r7   r™   r   rm   r   r   rn   r   r.   r=   rM   r8   re   r{   Z	SliceTypeÚ_add_offset_to_slicer>   ÚbinopÚoperatorr!   r~   r   rz   )r#   r¨   r1   r_   rH   rI   Ú	out_nodesr©   r+   rª   r’   r«   Ú	index_varr“   r”   r   r   r   r¤   "  sd    








zStencilPass._add_index_offsetsc             C   sÊ   t |tƒrBd |j|j¡}i }t|i |ƒ |d }|g}tjf}	n&dd„ }||g}| j|j	 }
|
tjf}	| j
jjj}t||| j|	| j| jƒ}|j ¡ \}}t||ƒ |jd jj}| |jd d… ¡ |S )NzRdef f(offset):
                return slice({} + offset, {} + offset)
            Úfc             S   s   t | j| | j| ƒS )N)re   ÚstartÚstop)Z	old_sliceÚoffsetr   r   r   r±   r  s    z+StencilPass._add_offset_to_slice.<locals>.fr˜   )r7   re   Úformatr²   r³   r   r   rn   r   r.   r   Zfunc_idr@   Ú__globals__r   r!   r   r4   rš   r   r<   r(   rz   )r#   rŽ   r«   r¯   rH   rI   Zf_textr±   r)   Zarg_typsZ
slice_typeZ_globalsr›   rN   rP   Z	new_indexr   r   r   r¬   f  s&    



z StencilPass._add_offset_to_sliceN)Ú__name__Ú
__module__Ú__qualname__r$   rX   r`   rK   rp   rq   ro   r¤   r¬   r   r   r   r   r       s   ; i" Dr   c             C   s°  ddl m} ddlm}	 ddlm}
 ddlm} | j 	¡ }t	 
|j¡}||_t |j¡}d|krhtdƒ‚||ƒ}|	 ||¡p t||||ƒ}tjj d||j¡ ||j|j|jd	ƒ\|_|_|_|
j|j|j|jd
d	|j|jtjjd W d	Q R X t |t ¡ ¡}t | !¡ ƒ}t"| !¡ ƒ}|t_#tj$dkr>t%dƒ t &|¡ i }x<|j '¡ D ].\}}t( )|t*|ƒ|¡}|||< |||j+< qNW t ,||¡ tj$dkrªt%dƒ t &|¡ x |j '¡ D ]\}}|||< q¶W i }x’| -¡ D ]†}x~|j.D ]t}t/|t(j0ƒræt/|j1t(j2ƒrætj$dkr4t%d||j1j3|j1j+|j1j3|kƒ ||j1j3 j+||j1j+< ||j1j3 |_1qæW qÚW tj$dkrŒt%d|ƒ t%dƒ t &|¡ t 4|¡ ||_||  5|¡d |fS )z'get typed IR from stencil bytecode
    r   )Ú
CPUContext)Ú
cpu_target)Útype_annotations)Útype_inference_stager0   z6Cannot use the reserved word 'out' in stencil kernels.zbefore-inferenceNr   )r   r   r   ZliftedZlifted_fromr)   r   Zhtml_outputr&   zInitial stencil_blockszAfter replace_varsrQ   rV   zAfter replace arg with arr)6Znumba.targets.cpurº   Znumba.targets.registryr»   Znumba.annotationsr¼   Znumba.compilerr½   Z	kernel_irÚcopyÚdeepcopyr4   r   rk   rE   Znested_contextÚDummyPipeliner3   ZrewritesZrewrite_registryZapplyr   r!   r)   r   r   r   ZTypeAnnotationr
   ZHTMLZadd_offset_to_labelsZ
next_labelr–   rt   rs   Z
_max_labelrh   ri   rj   r5   r   rm   r   r.   Zreplace_varsÚvaluesr<   r7   r=   r(   ZArgr¢   Zremove_delsZget_return_type)rT   r!   r)   rH   rI   rQ   r   r   rº   r»   r¼   r½   Zstencil_func_irrƒ   r…   Ú	targetctxÚtpZ	min_labelZ	max_labelZvar_dictr¡   ÚtypZnew_varr'   Zcall_typrV   rP   r,   r   r   r   rG   €  sz    






rG   c               @   s   e Zd Zdd„ ZdS )rÀ   c             C   s.   || _ || _|| _|| _d | _d | _d | _d S )N)r!   rÂ   r)   r   r   r   r   )r#   r!   rÂ   r)   r›   r   r   r   r$   Ø  s    zDummyPipeline.__init__N)r·   r¸   r¹   r$   r   r   r   r   rÀ   ×  s   rÀ   c             C   s   t t| ||ƒ}|dk	r|S |S )z²
    infer index_var as constant if it is of a expression form like c-1 where c
    is a constant in the outer function.
    index_var is assumed to be inside stencil kernel
    N)r   Ú_get_const_index_expr_inner)rU   r   r°   Ú	const_valr   r   r   r    â  s
    r    c             C   sr   t t|tjƒƒ tt| ||ƒ}|dk	r*|S t | |¡}tt| ||ƒ}|dk	rP|S tt	| ||ƒ}|dk	rj|S t
‚dS )zWinner constant inference function that calls constant, unary and binary
    cases.
    N)r   r7   r   rm   r   Ú_get_const_two_irsr   Zget_definitionÚ_get_const_unary_exprÚ_get_const_binary_exprr   )rU   r   r°   Ú	var_constÚ	index_defr   r   r   rÅ   î  s    rÅ   c             C   s8   t t| |ƒ}|dk	r|S t t||ƒ}|dk	r0|S t‚dS )zWget constant in either of two IRs if available
    otherwise, throw GuardException
    N)r   r   r   )Zir1Zir2ÚvarrÊ   r   r   r   rÇ     s    rÇ   c             C   sF   t t|tjƒo|jdkƒ |j}t| ||ƒ}t|j }t	d 
||¡ƒS )zQevaluate constant unary expr if possible
    otherwise, raise GuardException
    Zunaryz{}{})r   r7   r   r>   r?   r(   rÅ   r   ÚfnÚevalrµ   )rU   r   rË   Z	inner_varrÆ   r?   r   r   r   rÈ     s
    
rÈ   c             C   sR   t t|tjƒo|jdkƒ t| ||jƒ}t| ||jƒ}t|j	 }t
d |||¡ƒS )zRevaluate constant binary expr if possible
    otherwise, raise GuardException
    r­   z{}{}{})r   r7   r   r>   r?   rÅ   ZlhsZrhsr   rÍ   rÎ   rµ   )rU   r   rË   Zarg1Zarg2r?   r   r   r   rÉ     s
    
rÉ   )+r   r¾   r   Zpytypesr®   r   Znumpyrc   r3   Znumba.typing.templatesr   r   Znumba.typingr   r   r   r	   r
   r   Znumba.ir_utilsr   r   r   r   r   r   r   r   r   Z	numba.sixr   Znumba.utilsr   r   Úobjectr   rG   rÀ   r    rÅ   rÇ   rÈ   rÉ   r   r   r   r   Ú<module>   s4   ,    dW