o
    
j                     @   s  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 d dlmZ G dd	 d	ejZG d
d dejZdejdejdejfddZG dd dejjZG dd dejZG dd dejZG dd dejZG dd dejjZG dd dejZG dd dejZG dd  d ejjZG d!d" d"ejjZd#d$ ZG d%d& d&ejZG d'd( d(ejZG d)d* d*ejZ G d+d, d,ejZ!G d-d. d.ejZ"dS )/    N)Coqpit)nn)
functional)sequence_mask)SSIMLoss)	TorchSTFTc                       $   e Zd Z fddZdd Z  ZS )L1LossMaskedc                       t    || _d S Nsuper__init__seq_len_normselfr   	__class__ H/home/kuhnn/.local/lib/python3.10/site-packages/TTS/tts/layers/losses.pyr         

zL1LossMasked.__init__c                 C      d|_ t||ddd }| jrH||jddd }||jd |jd  }|	|}t
j|| || dd	}|||j }|S |	|}t
j|| || d
d	}||  }|S )a  
        Args:
            x: A Variable containing a FloatTensor of size
                (batch, max_len, dim) which contains the
                unnormalized probability for each class.
            target: A Variable containing a LongTensor of size
                (batch, max_len, dim) which contains the index of the true
                class for each corresponding step.
            length: A Variable containing a LongTensor of size (batch,)
                which contains the length of each data in a batch.
        Shapes:
            x: B x T X D
            target: B x T x D
            length: B
        Returns:
            loss: An average loss value in range [0, 1] masked by the length.
        F   sequence_lengthmax_len   Tdimkeepdimr   none	reductionsum)requires_gradr   size	unsqueezefloatr   r#   divshape	expand_asr   l1_lossmultodevicer   xtargetlengthmasknorm_wout_weightslossr   r   r   forward      

zL1LossMasked.forward__name__
__module____qualname__r   r7   __classcell__r   r   r   r   r	          r	   c                       r   )MSELossMaskedc                    r
   r   r   r   r   r   r   r   8   r   zMSELossMasked.__init__c                 C   r   )a  
        Args:
            x: A Variable containing a FloatTensor of size
                (batch, max_len, dim) which contains the
                unnormalized probability for each class.
            target: A Variable containing a LongTensor of size
                (batch, max_len, dim) which contains the index of the true
                class for each corresponding step.
            length: A Variable containing a LongTensor of size (batch,)
                which contains the length of each data in a batch.
        Shapes:
            - x: :math:`[B, T, D]`
            - target: :math:`[B, T, D]`
            - length: :math:`B`
        Returns:
            loss: An average loss value in range [0, 1] masked by the length.
        Fr   r   r   Tr   r   r    r!   r#   )r$   r   r%   r&   r'   r   r#   r(   r)   r*   r   mse_lossr,   r-   r.   r/   r   r   r   r7   <   r8   zMSELossMasked.forwardr9   r   r   r   r   r?   7   r>   r?   r0   r3   returnc                 C   sJ   t j| | dddd}t j| | tjddd}| | || d  S )zMin-Max normalize tensor through first dimension
    Shapes:
        - x: :math:`[B, D1, D2]`
        - m: :math:`[B, D1, 1]`
    r   )r   r   Tr   g:0yE>)torchamaxmasked_fillaminnpinf)r0   r3   maximumminimumr   r   r   sample_wise_min_max^   s   rJ   c                       (   e Zd ZdZ fddZdd Z  ZS )r   zkSSIM loss as (1 - SSIM)
    SSIM is explained here https://en.wikipedia.org/wiki/Structural_similarity
    c                    s   t    t | _d S r   )r   r   	_SSIMLoss	loss_funcr   r   r   r   r   n      
zSSIMLoss.__init__c                 C   s   t ||ddd}t||}t||}| || d|| d}| dkr>td|  d tjd|j	d}| dk rVtd|  d	 tjd|j	d}|S )
a|  
        Args:
            y_hat (tensor): model prediction values.
            y (tensor): target values.
            length (tensor): length of each sample in a batch for masking.

        Shapes:
            y_hat: B x T X D
            y: B x T x D
            length: B

         Returns:
            loss: An average loss value in range [0, 1] masked by the length.
        r   r   r         ?z > SSIM loss is out-of-range z, setting it 1.0)r.           z, setting it 0.0)
r   r%   r&   rJ   rM   itemprintrB   tensorr.   )r   y_hatyr2   r3   y_norm
y_hat_norm	ssim_lossr   r   r   r7   r   s   

 zSSIMLoss.forwardr:   r;   r<   __doc__r   r7   r=   r   r   r   r   r   i   s    r   c                   @   s   e Zd Zdd ZdS )AttentionEntropyLossc                 C   s.   t jj|d }|t|jd   }|S )zc
        Forces attention to be more decisive by penalizing
        soft attention weights
        )probsr   )rB   distributionsCategoricalentropyrF   logr)   mean)r   alignr`   r6   r   r   r   r7      s   zAttentionEntropyLoss.forwardNr:   r;   r<   r7   r   r   r   r   r\      s    r\   c                       s0   e Zd ZdZddef fddZdd Z  ZS )	BCELossMaskedzBCE loss with masking.

    Used mainly for stopnet in autoregressive models.

    Args:
        pos_weight (float): weight for positive samples. If set < 1, penalize early stopping. Defaults to None.
    N
pos_weightc                    s"   t    | dt|g d S )Nrf   )r   r   register_bufferrB   rT   )r   rf   r   r   r   r      s   
zBCELossMasked.__init__c                 C   s   d|_ |dur)t||dd}| }tj||||| j|j	dd}ntj||| j|j	dd}t
|}|| }|S )a  
        Args:
            x: A Variable containing a FloatTensor of size
                (batch, max_len) which contains the
                unnormalized probability for each class.
            target: A Variable containing a LongTensor of size
                (batch, max_len) which contains the index of the true
                class for each corresponding step.
            length: A Variable containing a LongTensor of size (batch,)
                which contains the length of each data in a batch.
        Shapes:
            x: B x T
            target: B x T
            length: B
        Returns:
            loss: An average loss value in range [0, 1] masked by the length.
        FNr   r   r#   )rf   r"   )r$   r   r%   r#   r    binary_cross_entropy_with_logitsmasked_selectrf   r-   r.   rB   numel)r   r0   r1   r2   r3   	num_itemsr6   r   r   r   r7      s    
zBCELossMasked.forwardr   )r:   r;   r<   r[   r'   r   r7   r=   r   r   r   r   re      s    re   c                       s*   e Zd ZdZ fddZdddZ  ZS )DifferentialSpectralLosszUDifferential Spectral Loss
    https://arxiv.org/ftp/arxiv/papers/1909/1909.10302.pdfc                    r
   r   )r   r   rM   )r   rM   r   r   r   r      r   z!DifferentialSpectralLoss.__init__Nc                 C   sv   |ddddf |ddddf  }|ddddf |ddddf  }|du r2|  ||S |  |||d S )z
         Shapes:
            x: B x T
            target: B x T
            length: B
        Returns:
            loss: An average loss value in range [0, 1] masked by the length.
        Nr   rM   )r   r0   r1   r2   x_difftarget_diffr   r   r   r7      s
   (	(z DifferentialSpectralLoss.forwardr   rZ   r   r   r   r   rl      s    rl   c                       sF   e Zd Zd fdd	Zdd Zdd Zedd	 Zed
d Z  Z	S )GuidedAttentionLoss皙?c                    r
   r   )r   r   sigma)r   rs   r   r   r   r      r   zGuidedAttentionLoss.__init__c           
      C   sj   t |}t|}t|}t|||f}tt||D ]\}\}}	| ||	| j||d |	d |f< q|S r   )lenmaxrB   zeros	enumeratezip_make_ga_maskrs   )
r   ilensolensBmax_ilenmax_olenga_masksidxilenolenr   r   r   _make_ga_masks   s   $z"GuidedAttentionLoss._make_ga_masksc                 C   sD   |  |||j}| |||j}|| }t||}|S r   )r   r-   r.   _make_masksrB   rb   ri   )r   att_wsrz   r{   r   	seq_maskslossesr6   r   r   r   r7      s
   zGuidedAttentionLoss.forwardc                 C   sf   t t ||t | | \}}| | }}dt ||  ||  d  d|d    S )NrP   r   )rB   meshgridaranger-   r'   exp)r   r   rs   grid_xgrid_yr   r   r   ry      s   (,z!GuidedAttentionLoss._make_ga_maskc                 C   s$   t | }t |}|d|d@ S )Nrm   )r   r&   )rz   r{   in_masks	out_masksr   r   r   r     s   zGuidedAttentionLoss._make_masksrr   )
r:   r;   r<   r   r   r7   staticmethodry   r   r=   r   r   r   r   rq      s    	
rq   c                   @   s   e Zd ZdddZdS )HuberNc                 C   s@   t ||ddd }tjjj|| || dd|  S )zY
        Shapes:
            x: B x T
            y: B x T
            length: B
        r   r   r   r#   r!   )	r   r%   r&   r'   rB   r   r   smooth_l1_lossr#   )r   r0   rV   r2   r3   r   r   r   r7     s   $zHuber.forwardr   rd   r   r   r   r   r   
  s    r   c                       s&   e Zd Zd fdd	Zdd Z  ZS )ForwardSumLossrm   c                    s4   t    tjjdd| _tjjdd| _|| _d S )N   r   T)zero_infinity)	r   r   rB   r   
LogSoftmaxlog_softmaxCTCLossctc_lossblank_logprob)r   r   r   r   r   r     s   

zForwardSumLoss.__init__c              	   C   s   |}|}t jjj|d| jd}d}t|jd D ]K}t d|| d d}	|| 	dddd || d d d || d f }
| 
|
d  d }
| j|
|	|||d  |||d  d}|| }q||jd  }|S )N)r   r   )inputpadvaluerQ   r   r   r   )input_lengthstarget_lengths)rB   r   r   r   r   ranger)   r   r&   permuter   r   )r   attn_logprobin_lensout_lenskey_lens
query_lensattn_logprob_padded
total_lossbid
target_seqcurr_logprobr6   r   r   r   r7     s"   4
zForwardSumLoss.forward)rm   r9   r   r   r   r   r     s    r   c                       s*   e Zd ZdZd fdd	Zdd Z  ZS )TacotronLossz7Collection of Tacotron set-up based on provided config.rr   c                    s:  t    |j| _|j| _| jr|jj| _|jj| _|j| _|j	| _	|j
| _
|j| _|j| _|j| _|j| _|| _|jrM|jdv rFt|jnt|j| _n|jdv rVt nt | _|jdkrft|d| _|j
dksp|j	dkrwt| jd| _|jdks|jdkrt | _ |j!rt"t#$| jdnd | _%tjdd| _&d S )N)Tacotronr   )rs   rn   )rf   r#   r!   )'r   r   stopnet_pos_weightuse_capacitron_vaecapacitron_vaecapacitron_capacitycapacitron_VAE_loss_alphacapacitron_vae_loss_alphaga_alphadecoder_diff_spec_alphapostnet_diff_spec_alphadecoder_loss_alphadecoder_alphapostnet_loss_alphapostnet_alphadecoder_ssim_alphapostnet_ssim_alphaconfigloss_maskingmodelr	   r   r?   	criterionr   L1LossMSELossrq   criterion_garl   criterion_diff_specr   criterion_ssimstopnetre   rB   rT   criterion_st(criterion_capacitron_reconstruction_loss)r   cga_sigmar   r   r   r   =  s2   


"
zTacotronLoss.__init__c           '      C   s  | j j dv r
|n|}i }| j jsd }	| j jr1| jdkr$| |||	}| jdkr0| |||	}n| jdkr<| ||}| jdkrG| ||}| j| | j|  }||d< ||d< | jr|\}}}t	tj
||}|| j }tjj|d }| | }| \}}}| j jr|	 | }| |||d | |d< |||  }|| j }t||  }||7 }||d< ||d< ||d< ||d	< | j jr| |||ntd
}||7 }||d< | j jr| j jr| tj|
dd||	}n| tj|
dd|}tjjtj|
dd|} || j||   7 }||d< | |d< | j jrO| j jr0| |
||	}n| |
|}tjj||}!|| j||!  7 }||d< |!|d< | j jdkrh| |||}"||"| j 7 }|"|d< | j jdkr|  |||	}#||#| j 7 }|#|d< | j j!dkr|  |||	}$||$| j! 7 }|$|d< | j j"dkr| #|||	}%||%| j$ 7 }|%|d< | j j$dkr| #|||	}&||&| j$ 7 }|&|d< ||d< |S )N)tacotronr   decoder_losspostnet_losscapaciton_reconstruction_losscapacitron_vae_losscapacitron_vae_beta_losscapacitron_vae_kl_termcapacitron_betar   stopnet_loss)r   )dimsdecoder_b_lossdecoder_c_lossdecoder_coarse_lossdecoder_ddc_lossga_lossdecoder_diff_spec_losspostnet_diff_spec_lossdecoder_ssim_losspostnet_ssim_lossr6   )%r   r   lowerr   r   r   r   r   rB   rb   r^   kl_divergencer   r   r   softplusdetachr%   r#   r   r   negativer   r   rv   bidirectional_decoderflipr+   double_decoder_consistencyr   r   r   r   r   r   r   r   )'r   postnet_outputdecoder_output	mel_inputlinear_inputstopnet_outputstopnet_targetstop_target_lengthcapacitron_vae_outputsoutput_lensdecoder_b_output
alignmentsalignment_lensalignments_backwards
input_lenspostnet_targetreturn_dictr   r   r6   posterior_distributionprior_distributionbetakl_termkl_capacityr   r|   TD	beta_loss	stop_lossr   r   attention_c_lossr   r   r   r   r   r   r   r   r7   b  s   





	



zTacotronLoss.forwardr   rZ   r   r   r   r   r   :  s    %r   c                       r   )GlowTTSLossc                    s$   t    dtdtj  | _d S )N      ?r   )r   r   mathra   piconstant_factorrN   r   r   r   r     s   
zGlowTTSLoss.__init__c	                 C   s   i }	t |dt t d| || d    }
| j|
t | t ||jd    }t || d t | }|| |	d< ||	d< ||	d< |	 D ]\}}t |r^td| dqM|	S )	Nr  r   r   r6   log_mleloss_durz [!] NaN loss with .)rB   r#   r   r  r)   itemsisnanRuntimeError)r   zmeansscaleslog_det	y_lengths	o_dur_log
o_attn_dur	x_lengthsr   pzr  r  keyr6   r   r   r   r7     s   .(
zGlowTTSLoss.forwardr9   r   r   r   r   r    r>   r  c                 C   s"   t | |\}}t jj||dS )z\MSE loss using the torch back-end without reduction.
    It uses less VRAM than the raw coder   )rB   broadcast_tensors_C_nnr@   )r0   rV   
expanded_x
expanded_yr   r   r   mse_loss_custom  s   r  c                   @   s   e Zd ZdZdd ZdS )MDNLosszUMixture of Density Network Loss as described in https://arxiv.org/pdf/2003.01950.pdf.c              	   C   s  |j \}}}||||d }|ddddf |ddddf< td|D ]H}tj|dddd|d |f tj|dddd|d |f dddgdd}	tj|	d	 dd|dddd|f  |dddd|f< q$|t||d |d f }
|
	  | }|S )
zt
        Shapes:
            mu: [B, D, T]
            log_sigma: [B, D, T]
            mel_spec: [B, D, T]
        g     Nr   r   )r   r   r   rm   )r   rm   r   g-C6?)
r)   new_onesr   rB   catr   r   	logsumexpr   rb   )r   logptext_lengthsmel_lengthsr|   T_seqT_mel	log_alphat	prev_step
alpha_lastmdn_lossr   r   r   r7     s    F<zMDNLoss.forwardN)r:   r;   r<   r[   r7   r   r   r   r   r    s    r  c                       rK   )AlignTTSLossa@  Modified AlignTTS Loss.
    Computes
        - L1 and SSIM losses from output spectrograms.
        - Huber loss for duration predictor.
        - MDNLoss for Mixture of Density Network.

    All loss values are aggregated by a weighted sum of the alpha values.

    Args:
        c (dict): TTS model configuration.
    c                    sR   t    t | _td| _t | _td| _|j	| _	|j
| _
|j| _|j| _d S )NF)r   r   r  r*  r?   	spec_lossr   ssimdur_loss
ssim_alphadur_loss_alphaspec_loss_alpha	mdn_alphar   r   r   r   r   r   A  s   


zAlignTTSLoss.__init__c	                 C   s  d\}	}
}}|dkr|  |||}na|dkr%| |||}	| |||}
nN|dkr?|  |||}| |||}	| |||}
n4|dkrQ| |d|d|}n"|  |||}| |||}	| |||}
| |d|d|}| j|	 | j|
  | j|  | j	|  }||	|
||dS )N)r   r   r   r   r   r   r   r   )r6   loss_l1	loss_ssimr  r*  )
r*  r,  r-  
spec_lossXr.  r&   r1  r/  r0  r2  )r   r!  r   decoder_targetdecoder_output_lens
dur_output
dur_targetr   phaser,  rY   r.  r*  r6   r   r   r   r7   M  s2   zAlignTTSLoss.forwardrZ   r   r   r   r   r+  4  s    r+  c                       sb   e Zd Zdef fddZedd Zedd Zedd	 Zed
d Z				dddZ
  ZS )VitsGeneratorLossr   c                    sv   t    |j| _|j| _|j| _|j| _|j| _|j| _t	|j
j|j
j|j
j|j
j|j
j|j
j|j
jddd	| _d S )NT)sample_ratemel_fminmel_fmaxn_melsuse_meldo_amp_to_db)r   r   kl_loss_alphagen_loss_alphafeat_loss_alphar0  mel_loss_alphaspeaker_encoder_loss_alphaspk_encoder_loss_alphar   audiofft_size
hop_length
win_lengthr=  r>  r?  num_melsstftr3  r   r   r   r   l  s$   
zVitsGeneratorLoss.__init__c              	   C   s`   d}t | |D ]$\}}t ||D ]\}}|  }| }|tt|| 7 }qq|d S )Nr   r   )rx   r'   r   rB   rb   abs)
feats_realfeats_generatedr6   drdgrlglr   r   r   feature_loss  s   zVitsGeneratorLoss.feature_lossc                 C   sF   d}g }| D ]}|  }td| d }|| ||7 }q||fS Nr   r   r   )r'   rB   rb   append)scores_faker6   
gen_lossesrS  lr   r   r   generator_loss  s   

z VitsGeneratorLoss.generator_lossc                 C   sv   |   } |  }|  }|  }|  }|| d }|d| | d  td|  7 }t|| }|t| }|S )zK
        z_p, logs_q: [b, h, t_t]
        m_p, logs_p: [b, h, t_t]
        r  r   g       )r'   rB   r   r#   )z_plogs_qm_plogs_pz_maskklr[  r   r   r   kl_loss  s   "zVitsGeneratorLoss.kl_lossc                 C   s   t jj| |  S r   )rB   r   r   cosine_similarityrb   )
gt_spk_embsyn_spk_embr   r   r   cosine_similarity_loss  s   z(VitsGeneratorLoss.cosine_similarity_lossFNc                 C   s   d}i }t | }| j|||||dd| j }| j|
|	d| j }| j|dd | j }t	j
j||| j }t	| | j }|| | | | }|ra| ||| j }|| }||d< ||d< ||d	< ||d
< ||d< ||d< ||d< |S )a  
        Shapes:
            - mel_slice : :math:`[B, 1, T]`
            - mel_slice_hat: :math:`[B, 1, T]`
            - z_p: :math:`[B, C, T]`
            - logs_q: :math:`[B, C, T]`
            - m_p: :math:`[B, C, T]`
            - logs_p: :math:`[B, C, T]`
            - z_len: :math:`[B]`
            - scores_disc_fake[i]: :math:`[B, C]`
            - feats_disc_fake[i][j]: :math:`[B, C, T', P]`
            - feats_disc_real[i][j]: :math:`[B, C, T', P]`
        rQ   r   )r]  r^  r_  r`  ra  )rP  rQ  )rY  r   loss_spk_encoderloss_genloss_kl	loss_featloss_melloss_durationr6   )r   r'   rc  r&   rC  rV  rE  r\  rD  rB   r   r   r+   rF  r#   r0  rg  rH  )r   	mel_slicemel_slice_hatr]  r^  r_  r`  z_lenscores_disc_fakefeats_disc_fakefeats_disc_realrm  use_speaker_encoder_as_lossre  rf  r6   r   ra  rj  rk  ri  rl  loss_ser   r   r   r7     s0   zVitsGeneratorLoss.forward)FNN)r:   r;   r<   r   r   r   rV  r\  rc  rg  r7   r=   r   r   r   r   r<  k  s    
	


r<  c                       s6   e Zd Zdef fddZedd Zdd Z  ZS )VitsDiscriminatorLossr   c                    s   t    |j| _d S r   )r   r   disc_loss_alphar3  r   r   r   r     rO   zVitsDiscriminatorLoss.__init__c           	      C   s   d}g }g }t | |D ]0\}}| }| }td| d }t|d }||| 7 }||  ||  q|||fS rW  )rx   r'   rB   rb   rX  rR   )	scores_realrY  r6   real_lossesfake_lossesrR  rS  	real_loss	fake_lossr   r   r   discriminator_loss  s   
z(VitsDiscriminatorLoss.discriminator_lossc           
      C   sb   d}i }| j ||d\}}}|| j |d< ||d  }||d< t|D ]\}}	|	|d| < q#|S )NrQ   )rx  rY  	loss_discr6   loss_disc_real_)r}  rw  rw   )
r   scores_disc_realrq  r6   r   r~  loss_disc_real_ildrr   r   r   r7     s   zVitsDiscriminatorLoss.forward)	r:   r;   r<   r   r   r   r}  r7   r=   r   r   r   r   rv    s
    
rv  c                       s>   e Zd ZdZ fddZedd Z				d	ddZ  ZS )
ForwardTTSLossz%Generic configurable ForwardTTS loss.c                    s  t    |jdkrtd| _n|jdkrtd| _ntd|j|jdkr.td| _	n|jdkr9td| _	n|jdkrCt
 | _	ntd|j|jjrWt | _|j| _|jjrdtd| _|j| _|jjrqtd| _|j| _|jr|jrzt nd | _|j| _|j| _|j| _|j| _d S )NmseFl1z [!] Unknown spec_loss_type {}huberz" [!] Unknown duration_loss_type {})r   r   spec_loss_typer?   r,  r	   
ValueErrorformatduration_loss_typer.  r   
model_argsuse_alignerr   aligner_lossaligner_loss_alpha	use_pitch
pitch_losspitch_loss_alpha
use_energyenergy_lossenergy_loss_alphause_ssim_lossr   r-  ssim_loss_alphar1  r0  binary_align_loss_alphabinary_alignment_loss_alphar3  r   r   r   r     s8   








zForwardTTSLoss.__init__c                 C   s.   t t j|| dk dd }| |   S )zBinary loss that forces soft alignments to match the hard alignments as
        explained in `https://arxiv.org/pdf/2108.10447.pdf`.
        r   g-q=)min)rB   ra   clampr#   )alignment_hardalignment_softlog_sumr   r   r   _binary_alignment_loss4  s    z%ForwardTTSLoss._binary_alignment_lossNc                 C   s"  d}i }t | dr#| jdkr#| |||}|| j|  }| j| |d< | jdkr=| |||}|| j|  }| j| |d< | jdkrrt| d }| 	|d d d d d f |d d d d d f |
}|| j|  }| j| |d< t | dr| j
dkr| |dd|dd|
}|| j
|  }| j
| |d	< t | d
r| jdkr| |dd|	dd|
}|| j|  }| j| |d< t | dr| jdkr| ||
|}|| j|  }| j| |d< | jdkr|d ur| ||}|| j|  }|r| j| | |d< n| j| |d< ||d< |S )Nr   rY   r5  	loss_specr   r  r  r   
loss_pitchr  loss_energyr  loss_alignerloss_binary_alignmentr6   )hasattrr  r-  r1  r,  r0  rB   ra   r'   r.  r  r  	transposer  r  r  r  r  r  )r   r   r7  r8  r9  r:  pitch_outputpitch_targetenergy_outputenergy_targetr   alignment_logprobr  r  binary_loss_weightr6   r   rY   r,  log_dur_tgtr.  r  r  r  binary_alignment_lossr   r   r   r7   <  sH   

2zForwardTTSLoss.forward)NNNN)	r:   r;   r<   r[   r   r   r  r7   r=   r   r   r   r   r    s    &
r  )#r  numpyrF   rB   coqpitr   r   torch.nnr   TTS.tts.utils.helpersr   TTS.tts.utils.ssimr   rL    TTS.utils.audio.torch_transformsr   Moduler	   r?   TensorrJ   r\   re   rl   rq   r   r   r   r  r  r  r+  r<  rv  r  r   r   r   r   <module>   s8    ''(3"# C7}#