o
    پiݖ                     @   s0  U d Z ddlZddlZddlZddlZddlm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 ddlmZ ddlZddlZdd	lmZ ddlm  m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" ee#Z$da%ee e&d< da'ee e&d< da(ee! e&d< da)ee  e&d< da*ee e&d< da+ee e&d< da,ee e&d< da-ee e&d< edg dZ.de/e0ej1eB f de2e3e2e0ef  e3ej1 f fddZ4i Z5e/e0eg ed f f e&d< dd!d"Z6d#ej1d$e0dej1fd%d&Z7d#ej1d$e0dej1fd'd(Z8da%edB e&d< dZ9edB e&d)< defd*d+Z:d,e3e; d-e;d.e0defd/d0Z<d1eee;  d-e;d.e0d2e0def
d3d4Z=da'edB e&d< defd5d6Z>d7a?d8e@fd9d:ZA			;		<		dd=e;d>e;d?e0d-e;d.e0d@ejBdB dAe;dB fdBdCZCda(edB e&d< de!fdDdEZDda+edB e&d< defdFdGZE									ddHe;dIe;dJee; dKe;dLe;dMe;dNe;dOe;d.ee0 ddfdPdQZFde;fdRdSZGde;fdTdUZHde;fdVdWZIde;fdXdYZJde;fdZd[ZKde;fd\d]ZL				;	dd^e;d_e;d`e@dKe;dLe;dae;d?e0dbe;dB fdcddZMde@fdedfZNdgaOe	dhefdidjZPde;fdkdlZQde;fdmdnZRddodpZSddqe@fdrdsZT	ddteeB due;de3e; fdvdwZU			xddye;d.e0dB dze0defd{d|ZV			xdd}e;d.e0dB dze0defd~dZWdefdd+Z:defdd6Z>dd ZXdd ZYdd ZZdd Z[dd Z\dd Z]dd Z^dd Z_de fddZ`dd Zadd Zbdd Zcdd ZddefddZedd Zfdd ZgdefddGZEdd Zhdd Zidd Zjdd ZkdefddZldd Zmdd Znd,ee; d-e;d.e0defdd0Z<ddf ZNde;d.e0fddZodd Zpde;dOe;d.e0fddZqdddZrddp ZSdS )a  sglang-diffusion distributed state.

It takes over the control of the distributed environment from PyTorch.
The typical workflow is:

- call `init_distributed_environment` to initialize the distributed environment.
- call `initialize_model_parallel` or `ensure_model_parallel_initialized` to
 initialize the model parallel groups.

- any code dealing with the distributed stuff

- call `destroy_model_parallel` to destroy the model parallel groups.
- call `destroy_distributed_environment` to destroy the distributed environment.

If you only need to use the distributed environment without model parallelism,
 you can skip the model parallel initialization and destruction steps.
    N)
namedtuple)Callable)contextmanager)shared_memory)AnyListOptional)patch)ProcessGroup)StatelessProcessGroup)init_logger   )RankGenerator   )GroupCoordinatorPipelineGroupCoordinator SequenceParallelGroupCoordinatorget_local_torch_device_WORLD_TP_SP_PP_CFG_DP_DIT_VAETensorMetadata)devicedtypesizetensor_dictreturnc              	   C   sl   g }g }|   D ])\}}t|tjr*|jj}||t||j|	 f || q|||f q||fS )zSplit the tensor dictionary into two parts:
    1. A list of (key, value) pairs. If the value is a tensor, it is replaced
         by its metadata.
    2. A list of tensors.
    )
items
isinstancetorchTensorr   typeappendr   r   r   )r    metadata_listtensor_listkeyvaluer    r,   l/home/ubuntu/.local/lib/python3.10/site-packages/sglang/multimodal_gen/runtime/distributed/parallel_state.py_split_tensor_dictJ   s   r.   r   _groupsgroupc                 C   s   t | t| j< d S N)weakrefrefr/   unique_namer0   r,   r,   r-   _register_groupg   s   r6   tensor
group_namec                 C   sD   |t v sJ d| dt |  }|d u rtd| d|| S )NzGroup z is not found.z is destroyed.)r/   
ValueError_all_reduce_out_place)r7   r8   r0   r,   r,   r-   
all_reducek   s
   

r;   c                 C   s
   t | S r1   )r$   
empty_like)r7   r8   r,   r,   r-   all_reduce_fakes   s   
r=   _NODEc                   C      t d usJ dt S Nzworld group is not initializedr   r,   r,   r,   r-   get_world_group{      rB   ranks
local_rankbackendc                 C   s   t | g||dddS )NTworld)group_ranksrE   torch_distributed_backenduse_device_communicatorr8   r   rD   rE   rF   r,   r,   r-   init_world_group   s   rM   rH   parallel_modec                 K   s`   |dv sJ d| d|dkrt | ||ddS |dkr(td| ||dd|S t| ||d	dS )zA
    Returns a Group Coordinator for the given parallel mode
    )datapipeliner7   sequenceclassifier_free_guidancezparallel_mode z is not supportedrP   pp_group)rH   rE   rI   r8   rQ   sp_group	cfg_groupNr,   )r   r   r   )rH   rE   rF   rN   kwargsr,   r,   r-   init_parallel_group_coordinator   s2   


	rW   c                   C   r?   Nz.tensor model parallel group is not initializedr   r,   r,   r,   r-   get_tp_group   rC   rZ   Tenablec                 C   s   | a d S r1   )_ENABLE_CUSTOM_ALL_REDUCE)r[   r,   r,   r-   set_custom_all_reduce      r]   env://nccl
world_sizerankdistributed_init_method	device_idtimeoutc           
   	   C   s,  ddl m} |dkr| sd}td|j td| ||||| tj	 se|d us/J d|
 s;| s;| r=i nt|d}|d urWtj|d	|d
< td| d tjjd||| |d| |dkrs|dkrqtj}n|}td u rtttj }	t|	||ad S tjtj ksJ dd S )Nr   current_platformr`   glooz"Using gloo backend for %s platformzTworld_size=%d rank=%d local_rank=%d distributed_init_method=%s backend=%s timeout=%szRdistributed_init_method must be provided when initializing distributed environment)rd   )secondsre   zSetting distributed timeout to z seconds)rF   init_methodra   rb   r_   z;world group already initialized with a different world sizer,   )'sglang.multimodal_gen.runtime.platformsrg   is_cuda_alikeloggerinfodevice_namedebugr$   distributedis_initializedis_mpsis_musais_npudictdatetime	timedeltainit_process_groupenvs
LOCAL_RANKr   listrangeget_world_sizerM   ra   )
ra   rb   rc   rE   rF   rd   re   rg   
extra_argsrD   r,   r,   r-   init_distributed_environment   s\   



	
r   c                   C   r?   Nz0pipeline model parallel group is not initialized)r   r,   r,   r,   r-   get_sp_group(  rC   r   c                   C   r?   )Nz&data parallel group is not initializedr   r,   r,   r,   r-   get_dp_group0  rC   r   data_parallel_sizeclassifier_free_guidance_degreesequence_parallel_degreeulysses_degreering_degreetensor_parallel_degreepipeline_parallel_degreevae_parallel_sizec	                 C   s
  |du rddl m}	 |	 }tj sJ tj }
|p#tjt j	}| | | | | }|
|k rIt
d|
 d| d| d| d| d	|  d
t||||| d}tdu sZJ dt|dt j|ddatdu soJ dt|dt j|ddatdu sJ dt|dt j|ddatdu sJ dzddlm} ddlm} W n ty   d}G dd d}| }Y nw |d}|||t j|d |}t|dt j|d|j|jdatdu sJ d t|d!t j|d"da|dkrt||| t|| dS )#a  
    Initialize model parallel groups.

    Arguments:
        data_parallel_size: number of data parallelism groups.
        classifier_free_guidance_degree: number of GPUs used for Classifier Free Guidance (CFG)
        sequence_parallel_degree: number of GPUs used for sequence parallelism. sequence_parallel_degree = ulysses_degree * ring_degree
        ulysses_degree: number of GPUs used for ulysses sequence parallelism.
        ring_degree: number of GPUs used for ring sequence parallelism.
        tensor_parallel_degree: number of GPUs used for tensor parallelism.
        pipeline_parallel_degree: number of GPUs used for pipeline parallelism.
        backend: distributed backend of pytorch collective comm.

    Let's say we have a total of 16 GPUs denoted by g0 ... g15 and we
    use 2 groups to parallelize the batch dim(dp), 2 groups to parallelize
    split batch caused by CFG, and 2 GPUs to parallelize sequence.

    dp_degree (2) * cfg_degree (2) * sp_degree (2) * pp_degree (2) = 16.

    The present function will create 8 data-parallel groups,
    8 CFG group, 8 pipeline-parallel group, and
    8 sequence-parallel groups:
        8 data-parallel groups:
            [g0, g8], [g1, g9], [g2, g10], [g3, g11],
            [g4, g12], [g5, g13], [g6, g14], [g7, g15]
        8 CFG-parallel groups:
            [g0, g4], [g1, g5], [g2, g6], [g3, g7],
            [g8, g12], [g9, g13], [g10, g14], [g11, g15]
        8 sequence-parallel groups:
            [g0, g1], [g2, g3], [g4, g5], [g6, g7],
            [g8, g9], [g10, g11], [g12, g13], [g14, g15]
        8 pipeline-parallel groups:
            [g0, g2], [g4, g6], [g8, g10], [g12, g14],
            [g1, g3], [g5, g7], [g9, g11], [g13, g15]
    Note that for efficiency, the caller should make sure adjacent ranks
    are on the same DGX box. For example if we are using 2 DGX-1 boxes
    with a total of 16 GPUs, rank 0 to 7 belong to the first box and
    ranks 8 to 15 belong to the second box.
    Nr   rf   zworld_size (z') is less than tensor_parallel_degree (z) x pipeline_parallel_degree (z) xsequence_parallel_degree (z$) xclassifier_free_guidance_degree (z) xdata_parallel_degree ()ztp-sp-pp-cfg-dpz*data parallel group is already initializeddprO   )rH   rE   rF   rN   z5classifier_free_guidance group is already initializedcfgrR   z4pipeline model parallel group is already initializedpprP   z.sequence parallel group is already initializedr   )PROCESS_GROUP) set_seq_parallel_pg_by_sp_groupsc                   @   s    e Zd ZejjjZejjjZdS )z5initialize_model_parallel.<locals>._DummyProcessGroupN)	__name__
__module____qualname__r$   rr   r0   WORLD
ULYSSES_PGRING_PGr,   r,   r,   r-   _DummyProcessGroup  s    
r   sp)sp_ulysses_degreesp_ring_degreerb   	sp_groupsrQ   )rH   rE   rF   rN   ulysses_group
ring_groupz,Tensor parallel group is already initializedtpr7   )rl   rg   !get_torch_distributed_backend_strr$   rr   rs   r   get_backendrB   device_groupRuntimeErrorr   r   rW   	get_ranksrE   r   r   r   parallel_groupsr   r   ImportErrorrb   r   r   r   init_vae_groupinit_dit_group)r   r   r   r   r   r   r   r   rF   rg   ra   dit_parallel_sizerank_generator_YC_PROCESS_GROUP!_set_seq_parallel_pg_by_sp_groupsr   r   r   r,   r,   r-   initialize_model_parallel6  s   3

	


r   c                   C      t  jS )z8Return world size for the sequence model parallel group.r   ra   r,   r,   r,   r-   get_sp_world_size-  r^   r   c                   C   r   )z5Return my rank for the sequence model parallel group.r   rank_in_groupr,   r,   r,   r-   get_sp_parallel_rank2  r^   r   c                   C   r   )z&Return world size for the world group.)rB   ra   r,   r,   r,   r-   r   7  r^   r   c                   C   r   )z#Return my rank for the world group.)rB   rb   r,   r,   r,   r-   get_world_rank<  r^   r   c                   C   r   z.Return world size for the data parallel group.r   ra   r,   r,   r,   r-   get_dp_world_sizeA  r^   r   c                   C   r   z+Return my rank for the data parallel group.r   r   r,   r,   r,   r-   get_dp_rankF  r^   r   tp_sizesp_sizeenable_cfg_paralleldp_sizedist_timeoutc              	   C   s  ddl m} td ur/t r/t | ksJ d|  dt  t |ks-J d| dt  d S ttj	dd}	ttj	dd}
ttj	dd}t
 }tjd	|
||d
d t|
||	||| |d t||rjdnd| |||d | rtd|	 }tj| d S d S )Nr   rf   z=You are trying to initialize model parallel groups with size z-, but they are already initialized with size r|   
WORLD_SIZEr   RANKzNInitializing distributed environment with world_size=%d, device=%s, timeout=%sF)main_process_only)ra   rb   rE   rc   rd   rF   re   r   )r   r   r   r   r   r   zcuda:)rl   rg   r   model_parallel_is_initializedget_tp_world_sizer   intosenvirongetr   rn   ro   r   r   r   rm   r$   r   cuda
set_device)r   r   r   r   r   r   rc   r   rg   rE   ra   rb   r   r,   r,   r-   5maybe_init_distributed_environment_and_model_parallelK  sR   
	

r   c                   C   s    t duotduotduotduS )z:Check if tensor, sequence parallel groups are initialized.N)r   r   r   r   r,   r,   r,   r-   r     s    r   Ftp_groupc                 c   s<    t rJ dda t }| az
dV  W da |adS da |aw )zPatch the tp group temporarily until this function ends.

    This method is for draft workers of speculative decoding to run draft model
    with different tp degree from that of target model workers.

    z)Should not call when it's already patchedTNF)_TP_STATE_PATCHEDrZ   r   )r   old_tp_groupr,   r,   r-   patch_tensor_parallel_group  s   	r   c                   C   r   z6Return world size for the tensor model parallel group.rZ   ra   r,   r,   r,   r-   r     r^   r   c                   C   r   z3Return my rank for the tensor model parallel group.rZ   r   r,   r,   r,   r-   get_tp_rank  r^   r   c                   C   ,   t rt   d a tj rtj  d S d S r1   r   destroyr$   rr   rs   destroy_process_groupr,   r,   r,   r-   destroy_distributed_environment     
r   shutdown_rayc                 C   s\   t   t  tt tj  W d    n1 sw   Y  | r,dd l}|	  d S d S )Nr   )
destroy_model_parallelr   
contextlibsuppressAssertionErrorr$   rr   r   rayshutdown)r   r   r,   r,   r-   cleanup_dist_env_and_memory  s   r   pgsource_rankc              
   C   s  t | tr)tj| tjjjksJ dtjj| d}tjj| d}tj	| }n| j
}| j}tt|}tjdg| tjd}d}d}zztt ||krtjddd	}||jdt|< t | trstjj|jg|| | d
 n| j|j|d d||< nKt | trdg}tjj||| | d
 |d }	n| jd|d}	tddd  tj|	d}W d   n1 sw   Y  |jdt| |krd||< W d   n1 sw   Y  W n ty }
 ztd|
 W Y d}
~
nd}
~
ww W |r|  n	|r|  w w t | trtjj| d n|   tt ||kr)|r)|   W d   n	1 s4w   Y  t | trJtjj!|| d |}nt"|}t|D ]}| j||d}||7 }qSdd |# D S )z
    This is a collective operation that returns if each rank is in the same node
    as the source rank. It tests if processes are attached to the same
    memory system (shared access to shared memory).
    z;in_the_same_node_as should be tested with a non-NCCL group.r5   r   )r   s   magic_messageNT   )creater   )srcr0   )r   r   z)multiprocessing.resource_tracker.registerc                  _   s   d S r1   r,   )argsrV   r,   r,   r-   <lambda>  s    z%is_the_same_node_as.<locals>.<lambda>)namez(Error ignored in is_in_the_same_node: %sc                 S   s   g | ]}|d kqS )r   r,   ).0xr,   r,   r-   
<listcomp>  s    z'is_the_same_node_as.<locals>.<listcomp>)$r#   r
   r$   rr   r   BackendNCCLget_rankr   get_process_group_ranksrb   ra   r}   r~   r7   int32r   r   OSErrorr   SharedMemorybuflenbroadcast_object_listr   broadcast_objr	   	Exceptionrn   errorclosebarrierunlinkr;   
zeros_liketolist)r   r   rb   ra   rD   is_in_the_same_nodemagic_messageshmrecvr   eaggregated_datai	rank_datar,   r,   r-   is_the_same_node_as  s   





 

r
   tensor_model_parallel_sizegroup_name_suffixc           
      C   s   t j sJ t j }|pt jt j}||  dks'J d| d|  d||  }g }t|D ]}tt||  |d |  }|	| q1|rMd| nd}t
|t j|d|d	}	|	S )
a9  Initialize a tensor parallel group for a specific model.

    This function creates a tensor parallel group that can be used with the
    patch_tensor_parallel_group context manager. It allows different models
    to use different tensor parallelism configurations.

    Arguments:
        tensor_model_parallel_size: number of GPUs used for tensor model parallelism.
        backend: communication backend to use.
        group_name_suffix: optional suffix to make the group name unique.

    Returns:
        A GroupCoordinator for tensor parallelism that can be used with
        the patch_tensor_parallel_group context manager.

    Example usage:
        ```python
        # Initialize tensor parallel group for model1
        tp_group_model1 = initialize_tensor_parallel_group(
            tensor_model_parallel_size=4,
            group_name_suffix="model1"
        )

        # Use tensor parallelism for model1
        with patch_tensor_parallel_group(tp_group_model1):
            # Run model1 with tensor parallelism
            output1 = model1(input1)
        ```
    r   World size (z3) must be divisible by tensor_model_parallel_size (r   r   tp_r   T)use_message_queue_broadcasterr8   r$   rr   rs   r   r   rB   r   r~   r}   r'   rW   rE   )
r  rF   r  ra    num_tensor_model_parallel_groupstp_group_ranksr  rD   r8   r   r,   r,   r-    initialize_tensor_parallel_group  s,   #
r  sequence_model_parallel_sizec           
      C   s   t j sJ t j }|pt jt j}||  dks'J d| d|  d||  }g }t|D ]}tt||  |d |  }|	| q1|rMd| nd}t
|t j||d}	|	S )	aU  Initialize a sequence parallel group for a specific model.

    This function creates a sequence parallel group that can be used with the
    patch_sequence_parallel_group context manager. It allows different models
    to use different sequence parallelism configurations.

    Arguments:
        sequence_model_parallel_size: number of GPUs used for sequence model parallelism.
        backend: communication backend to use.
        group_name_suffix: optional suffix to make the group name unique.

    Returns:
        A GroupCoordinator for sequence parallelism that can be used with
        the patch_sequence_parallel_group context manager.

    Example usage:
        ```python
        # Initialize sequence parallel group for model2
        sp_group_model2 = initialize_sequence_parallel_group(
            sequence_model_parallel_size=2,
            group_name_suffix="model2"
        )

        # Use sequence parallelism for model2
        with patch_sequence_parallel_group(sp_group_model2):
            # Run model2 with sequence parallelism
            output2 = model2(input2)
        ```
    r   r  z5) must be divisible by sequence_model_parallel_size (r   r   sp_r   )r8   r  )
r  rF   r  ra   "num_sequence_model_parallel_groupssp_group_ranksr  rD   r8   rT   r,   r,   r-   "initialize_sequence_parallel_group]  s(   #
r  c                   C   r?   r@   rA   r,   r,   r,   r-   rB     rC   c                   C   r?   rX   rY   r,   r,   r,   r-   rZ     rC   c                   C   r   r   r   r,   r,   r,   r-   $get_tensor_model_parallel_world_size  r^   r  c                   C   r   r   r   r,   r,   r,   r-   get_tensor_model_parallel_rank  r^   r  c                   C   r   )z2Return world size for the sequence parallel group.r   r,   r,   r,   r-    get_sequence_parallel_world_size  r^   r  c                   C   r   )z/Return my rank for the sequence parallel group.r   r,   r,   r,   r-   get_sequence_parallel_rank  r^   r  c                   C   r   r1   )r   ulysses_world_sizer,   r,   r,   r-   get_ulysses_parallel_world_size     r  c                   C   r   r1   )r   ulysses_rankr,   r,   r,   r-   get_ulysses_parallel_rank  r   r"  c                   C   r   r1   )r   ring_world_sizer,   r,   r,   r-   get_ring_parallel_world_size  r   r$  c                   C   r   r1   )r   	ring_rankr,   r,   r,   r-   get_ring_parallel_rank  r   r&  c                   C   r?   r   )r   r,   r,   r,   r-   get_pp_group  rC   r'  c                   C   r   )z8Return world size for the pipeline model parallel group.)r'  ra   r,   r,   r,   r-    get_pipeline_parallel_world_size  r^   r(  c                   C   r   )z5Return my rank for the pipeline model parallel group.)r'  r   r,   r,   r,   r-   get_pipeline_parallel_rank  r^   r)  c                   C   s
   t  dkS )zKReturn True if in the first pipeline model parallel stage, False otherwise.r   )r)  r,   r,   r,   r-   is_pipeline_first_stage  s   
r*  c                   C   s   t  t d kS )zJReturn True if in the last pipeline model parallel stage, False otherwise.r   )r)  r(  r,   r,   r,   r-   is_pipeline_last_stage  s   r+  c                   C   r?   )Nz:classifier_free_guidance parallel group is not initialized)r   r,   r,   r,   r-   get_cfg_group  s   
r,  c                   C   r   )zBReturn world size for the classifier_free_guidance parallel group.)r,  ra   r,   r,   r,   r-   'get_classifier_free_guidance_world_size  r^   r-  c                   C   r   )z?Return my rank for the classifier_free_guidance parallel group.)r,  r   r,   r,   r,   r-   !get_classifier_free_guidance_rank  r^   r.  c                   C   r?   r   r   r,   r,   r,   r-   r     rC   c                   C   r   r   r   r,   r,   r,   r-   get_data_parallel_world_size  r^   r/  c                   C   r   r   r   r,   r,   r,   r-   get_data_parallel_rank  r^   r0  c                   C   s0   t  t d kot t d kot t d kS )z@Return True if in the last data parallel group, False otherwise.r   )r  r  r.  r-  r)  r(  r,   r,   r,   r-   is_dp_last_group  s   r1  c                   C   s   t  t  t  t  t  S )z4Return world size for the DiT model (excluding VAE).)r/  r-  r  r(  r  r,   r,   r,   r-   get_dit_world_size  s   r2  c                   C   r?   )Nz%VAE parallel group is not initialized)r   r,   r,   r,   r-   get_vae_parallel_group!  rC   r3  c                   C   r   )z-Return world size for the VAE parallel group.)r3  ra   r,   r,   r,   r-   get_vae_parallel_world_size&  r^   r4  c                   C   r   )z*Return my rank for the VAE parallel group.)r3  r   r,   r,   r,   r-   get_vae_parallel_rank+  r^   r5  c                 C   s   t | g||dS )N)rH   rE   rI   rK   rL   r,   r,   r-   rM   3  s
   c                   C   s(   t duotduotduotduotduS )z=Check if tensor and pipeline parallel groups are initialized.N)r   r   r   r   r   r,   r,   r,   r-   r   =  s   r   c                 C   s   t jjtt| |dad S )NrD   rF   )r$   rr   	new_groupr}   r~   r   )r   rF   r,   r,   r-   r   H  s   
r   c                   C   r?   )NzDIT group is not initialized)r   r,   r,   r,   r-   get_dit_groupR  rC   r8  c                 C   s6   t d u sJ dtt| | | }tjj||da d S )Nz)VAE parallel group is already initializedr6  )r   r}   r~   r$   rr   r7  )r   r   rF   	vae_ranksr,   r,   r-   r   W  s   r   c                   C   s4   t rt   da trt  datrt  dadS )z(Set the groups to none and destroy them.N)r   r   r   r   r,   r,   r,   r-   r   c  s   r   c                   C   r   r1   r   r,   r,   r,   r-   r     r   )r0   r   r!   N)r   r   r_   r   r`   NN)	r   r   Nr   r   r   r   r   N)r   r   r   r_   N)r!   N)F)r   )r   Nr  )s__doc__r   rx   r   r2   collectionsr   collections.abcr   r   multiprocessingr   typingr   r   r   unittest.mockr	   r$   torch.distributedr
   sglang.multimodal_gen.envsmultimodal_genr{   /sglang.multimodal_gen.runtime.distributed.utilsr   1sglang.multimodal_gen.runtime.utils.logging_utilsr   utils.distributedr   group_coordinatorr   r   r   r   r   rn   r   __annotations__r   r   r   r   r   r   r   r   rw   strr%   tupler}   r.   r/   r6   r;   r=   r>   rB   r   rM   rW   rZ   r\   boolr]   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r
  r  r  r  r  r  r  r  r"  r$  r&  r'  r(  r)  r*  r+  r,  r-  r.  r/  r0  r1  r2  r3  r4  r5  r   r8  r   r   r,   r,   r,   r-   <module>   s  
 



;
M	

 x	
9
	
[
C
C






4