a
    _DfZ                     @   sn  d dl Z d dlm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 d dlmZ d dlmZmZmZmZmZmZm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" d
dl#m$Z$m%Z%m&Z& G dd deZ'G dd dZ(G dd dZ)G dd dee*e(f Z+G dd dee*e)f Z,G dd dee*ee*e-f f Z.G dd dZ/dS )    N)defaultdictcopy)Enumauto)	lru_cache)chainproductisnan)AnyDefaultDictDictIterableMappingOptionalSequence)tqdm   )
Annotation)AnnotationSet)open_atomic)BoundingBox)PathLike)	all_equalgroupingmeanc                   @   s   e Zd Ze Ze ZdS )RecallStepsN)__name__
__module____qualname__r   ZELEVENALL r"   r"   ^/nfs/NAS7/SABIOD/METHODE/ermites/ermites_venv/lib/python3.9/site-packages/globox/evaluation.pyr      s   r   c                   @   s   e Zd Zded ed eddddZd d dd	d
Zd d dddZeedddZ	eedddZ
edddZedddZee dddZdd ZddddZdS )PartialEvaluationItemNr   
list[bool]list[float])tpsscoresnposreturnc                 C   st   |pg | _ |pg | _|| _i | _| jdks:J d| dt| j t| jkspJ dt| j  dt| j dd S )Nr   z'npos' (z') should be greater than or equal to 0.zThe number of 'tps' (z-) should be equal to the number of 'scores' (z).)_tps_scores_npos_cachelen)selfr'   r(   r)   r"   r"   r#   __init__   s    


zPartialEvaluationItem.__init__otherr*   c                 C   s<   |  j |j 7  _ |  j|j7  _|  j|j7  _|   | S N)r+   r,   r-   clear_cache)r0   r3   r"   r"   r#   __iadd__+   s
    zPartialEvaluationItem.__iadd__c                 C   s   t | }||7 }|S r4   r   r0   r3   Zcopy_r"   r"   r#   __add__2   s    zPartialEvaluationItem.__add__r*   c                 C   s
   t | jS r4   )r/   r+   r0   r"   r"   r#   ndet7   s    zPartialEvaluationItem.ndetc                 C   s   | j S r4   )r-   r:   r"   r"   r#   r)   ;   s    zPartialEvaluationItem.nposc                 C   s0   | j dt| j}|| j d< || jks,J |S )Ntp)r.   getsumr+   r-   )r0   r<   r"   r"   r#   r<   ?   s    
zPartialEvaluationItem.tpc                 C   s:   | j d}|d ur|S t| j| j| j}|| j d< |S )Nap)r.   r=   COCOEvaluator_compute_apr,   r+   r-   r0   r?   r"   r"   r#   r?   E   s    
zPartialEvaluationItem.apc                 C   sJ   | j d}|d ur|S |  }| jdkr4|| j ntd}|| j d< |S )Narr   nan)r.   r=   r<   r-   float)r0   rC   r<   r"   r"   r#   rC   M   s    
zPartialEvaluationItem.arc                 C   s   | j   d S r4   r.   clearr:   r"   r"   r#   r5   V   s    z!PartialEvaluationItem.clear_cacheEvaluationItemc                 C   s    t |  | j| j|  |  S r4   )rH   r<   r;   r-   r?   rC   r:   r"   r"   r#   evaluateY   s    zPartialEvaluationItem.evaluate)NNr   )r   r   r    r   intr1   r6   r8   propertyr;   r)   r<   rE   r?   rC   r5   rI   r"   r"   r"   r#   r$      s(      	r$   c                   @   s4   e Zd ZdZdZeeeee ee ddddZdS )rH   z)Evaluation of COCO metrics for one label.r<   r;   r)   r?   rC   N)r<   r;   r)   r?   rC   r*   c                 C   s"   || _ || _|| _|| _|| _d S r4   rL   )r0   r<   r;   r)   r?   rC   r"   r"   r#   r1   b   s
    zEvaluationItem.__init__)	r   r   r    __doc__	__slots__rJ   r   rE   r1   r"   r"   r"   r#   rH   ]   s
   rH   c                       s   e Zd ZdZdeeeef  d fddZd d dddZ	d d dd	d
Z
edddZee dddZdd ZddddZ  ZS )PartialEvaluationz1Do not mutate this excepted with defined methods.N)itemsc                    s,   t  dd  |d ur"| | i | _d S )Nc                   S   s   t  S r4   )r$   r"   r"   r"   r#   <lambda>p       z,PartialEvaluation.__init__.<locals>.<lambda>)superr1   updater.   )r0   rP   	__class__r"   r#   r1   o   s    
zPartialEvaluation.__init__r2   c                 C   s.   |  D ]\}}| |  |7  < q|   | S r4   )rP   r5   )r0   r3   keyvaluer"   r"   r#   r6   u   s    zPartialEvaluation.__iadd__c                 C   s   t | }||7 }|S r4   r   r7   r"   r"   r#   r8   {   s    zPartialEvaluation.__add__r9   c                 C   sF   | j d}|d ur|S tdd dd |  D D }|| j d< |S )Nr?   c                 s   s   | ]}t |s|V  qd S r4   r
   .0ar"   r"   r#   	<genexpr>   rR   z'PartialEvaluation.ap.<locals>.<genexpr>c                 s   s   | ]}|  V  qd S r4   )r?   rZ   evr"   r"   r#   r\      rR   r.   r=   r   valuesrB   r"   r"   r#   r?      s     
zPartialEvaluation.apc                 C   sF   | j d}|d ur|S tdd dd |  D D }|| j d< |S )NrC   c                 s   s   | ]}t |s|V  qd S r4   r
   rY   r"   r"   r#   r\      rR   z'PartialEvaluation.ar.<locals>.<genexpr>c                 s   s   | ]}|  V  qd S r4   )rC   r]   r"   r"   r#   r\      rR   r_   )r0   rC   r"   r"   r#   rC      s     
zPartialEvaluation.arc                 C   s   | j   d S r4   rF   r:   r"   r"   r#   r5      s    zPartialEvaluation.clear_cache
Evaluationc                 C   s   t | S r4   )ra   r:   r"   r"   r#   rI      s    zPartialEvaluation.evaluate)N)r   r   r    rM   r   r   strr$   r1   r6   r8   rE   r?   rC   r5   rI   __classcell__r"   r"   rU   r#   rO   l   s    rO   c                       sD   e Zd ZdZedd fddZedddZedd	d
Z  Z	S )ra   z/Evaluation of COCO metrics for multiple labels.N)
evaluationr*   c                    s:   t    | dd | D  | | _| | _d S )Nc                 S   s   i | ]\}}||  qS r"   rI   )rZ   labelr^   r"   r"   r#   
<dictcomp>   rR   z'Evaluation.__init__.<locals>.<dictcomp>)rS   r1   rT   rP   r?   _aprC   _ar)r0   rd   rU   r"   r#   r1      s    

zEvaluation.__init__r9   c                 C   s   | j S r4   )rh   r:   r"   r"   r#   r?      s    zEvaluation.apc                 C   s   | j S r4   )ri   r:   r"   r"   r#   rC      s    zEvaluation.ar)
r   r   r    rM   rO   r1   rE   r?   rC   rc   r"   r"   rU   r#   ra      s   ra   c                       sD   e Zd ZdZddd fddZeddd	Zedd
dZ  ZS )MultiThresholdEvaluationzY
    Evaluation of COCO metrics for multiple labels and multiple
    IoU thresholds.
    zlist[Evaluation]N)evaluationsr*   c                    sP   t t}|D ]$}| D ]\}}|| | qqt dd | D  d S )Nc                 S   s8   i | ]0\}}|t d d |D t dd |D dqS )c                 s   s   | ]}t |js|jV  qd S r4   )r   r?   r]   r"   r"   r#   r\      rR   z?MultiThresholdEvaluation.__init__.<locals>.<dictcomp>.<genexpr>c                 s   s   | ]}t |js|jV  qd S r4   )r   rC   r]   r"   r"   r#   r\      rR   )r?   rC   )r   )rZ   rf   Zevsr"   r"   r#   rg      s
   z5MultiThresholdEvaluation.__init__.<locals>.<dictcomp>)r   listrP   appendrS   r1   )r0   rk   resultrd   rf   Zev_itemrU   r"   r#   r1      s    z!MultiThresholdEvaluation.__init__r9   c                 C   s   t dd |  D S )Nc                 s   s"   | ]}t |d  s|d  V  qdS )r?   Nr
   r]   r"   r"   r#   r\      rR   z.MultiThresholdEvaluation.ap.<locals>.<genexpr>r   r`   r:   r"   r"   r#   r?      s    zMultiThresholdEvaluation.apc                 C   s   t dd |  D S )Nc                 s   s"   | ]}t |d  s|d  V  qdS )rC   Nr
   r]   r"   r"   r#   r\      rR   z.MultiThresholdEvaluation.ar.<locals>.<genexpr>ro   r:   r"   r"   r#   rC      s    zMultiThresholdEvaluation.ar)	r   r   r    rM   r1   rE   r?   rC   rc   r"   r"   rU   r#   rj      s   rj   c                
   @   s  e Zd ZdZedddZdZdZde	dfZ
d	e	dfZed	d
dZdZddeeeee  ddddZddde	eed edddZddde	eed edddZe	dddZe	dddZe	ddd Ze	dd!d"Ze	dd#d$Ze	dd%d&Ze	dd'd(Ze	dd)d*Ze	dd+d,Ze	dd-d.Z e	dd/d0Z!e	dd1d2Z"e#dd3d4Z$edd5d6Z%edd7d8Z&e#dd9d:Z'e#dd;d<Z(e#dd=d>Z)e#dd?d@Z*e#ddAdBZ+e#ddCdDZ,de#dEdFdGZ-ee#dHdIdJZ.e/dheee	edee0e  e1dKdLdMZ2e/die3e3e	edeee  e1dNdOdPZ4e/dQdQe	ede5dRdSdTZ6e7e	eddUdVdWZ8dXdY Z9dZd[e:d[d\d]Z;dZd[e:d[d^d_Z<dZd[e=e:d`dadbZ>e/dcddee	dedfdgZ?dS )jr@   a  
    COCO metrics evaluator.

    Once instantiated, the standard COCO metrics can be queried using the dedicated methods:

    * `COCOEvaluator.ap()`,
    * `COCOEvaluator.ap_50()`,
    * `COCOEvaluator.ar_medium()`,
    * etc.

    Custom evaluations can be performed with `COCOEvaluator.evaluate()` and the standard
    COCO metrics can be printed to the console with `COCOEvaluator.show_summary()`.
          ?gffffff?
   )              @)rs         @rt   infrr         ?e   )rf   AP 50:95AP 50AP 75AP SAP MAP LAR 1AR 10AR 100AR SAR MAR LN)labels)ground_truthspredictionsr   r*   c                C   s<   || _ || _|du rg | _n
t|| _tdd| j| _dS )a  
        Intantiate a `COCOEvaluator` with the to-be-evaluated dataset and the ground truth
        reference one.

        All the bounding box annotations from the prediction dataset must have a confidence score
        and all the ones from the ground truth one must not.

        If labels are provided, only those will be evaluated, else every label will.

        The evaluated datasets must not be modified during the lifetime of the `COCOEvaluator` or
        the results will be wrong.
        N   )maxsize)_predictions_ground_truthsr   rl   r   _COCOEvaluator__evaluate_cached_evaluate)r0   r   r   r   r"   r"   r#   r1      s    
zCOCOEvaluator.__init__d   )max_detections
size_rangeztuple[float, float])iou_thresholdr   r   r*   c                C   s   | j |||dS )a  
        Evaluate COCO metrics for custom parameters.

        Parameters:

        * `iou_threshold`: the bounding box iou threshold to consider a ground-truth to
        detection association valid.
        * `max_detections`: the maximum number of detections taken into account (sorted by
        descreasing confidence).
        * `size_range`: the range of size (bounding box area) to consider.
        r   r   r   )r   r0   r   r   r   r"   r"   r#   rI     s
    zCOCOEvaluator.evaluatec                C   s:   |d u rt j}| ||| | | j| j|||| j S r4   )r@   	ALL_RANGE_assert_paramsevaluate_annotationsr   r   r   rI   r   r"   r"   r#   Z
__evaluate,  s    zCOCOEvaluator.__evaluater9   c                 C   s   |    S r4   )ap_evaluationr?   r:   r"   r"   r#   r?   A  s    zCOCOEvaluator.apc                 C   s   |    S r4   )ap_50_evaluationr?   r:   r"   r"   r#   ap_50D  s    zCOCOEvaluator.ap_50c                 C   s   |    S r4   )ap_75_evaluationr?   r:   r"   r"   r#   ap_75G  s    zCOCOEvaluator.ap_75c                 C   s   |    S r4   )small_evaluationr?   r:   r"   r"   r#   ap_smallJ  s    zCOCOEvaluator.ap_smallc                 C   s   |    S r4   )medium_evaluationr?   r:   r"   r"   r#   	ap_mediumM  s    zCOCOEvaluator.ap_mediumc                 C   s   |    S r4   )large_evaluationr?   r:   r"   r"   r#   ap_largeP  s    zCOCOEvaluator.ap_largec                 C   s   |    S r4   )ndets_1_evaluationrC   r:   r"   r"   r#   ar_1S  s    zCOCOEvaluator.ar_1c                 C   s   |    S r4   )ndets_10_evaluationrC   r:   r"   r"   r#   ar_10V  s    zCOCOEvaluator.ar_10c                 C   s   |    S r4   )ndets_100_evaluationrC   r:   r"   r"   r#   ar_100Y  s    zCOCOEvaluator.ar_100c                 C   s   |    S r4   )r   rC   r:   r"   r"   r#   ar_small\  s    zCOCOEvaluator.ar_smallc                 C   s   |    S r4   )r   rC   r:   r"   r"   r#   	ar_medium_  s    zCOCOEvaluator.ar_mediumc                 C   s   |    S r4   )r   rC   r:   r"   r"   r#   ar_largeb  s    zCOCOEvaluator.ar_largec                    s    fdd j D }t|S )Nc                    s   g | ]} j |d  jdqS r   r   rI   r   rZ   tr:   r"   r#   
<listcomp>f  s   z/COCOEvaluator.ap_evaluation.<locals>.<listcomp>AP_THRESHOLDSrj   )r0   rk   r"   r:   r#   r   e  s    
zCOCOEvaluator.ap_evaluationc                 C   s   | j dd| jdS )Nrp   r   r   r   r:   r"   r"   r#   r   o  s    zCOCOEvaluator.ap_50_evaluationc                 C   s   | j dd| jdS )Ng      ?r   r   r   r:   r"   r"   r#   r   t  s    zCOCOEvaluator.ap_75_evaluationc                 C   s   |  | jS r4   )_range_evalationSMALL_RANGEr:   r"   r"   r#   r   y  s    zCOCOEvaluator.small_evaluationc                 C   s   |  | jS r4   )r   MEDIUM_RANGEr:   r"   r"   r#   r   |  s    zCOCOEvaluator.medium_evaluationc                 C   s   |  | jS r4   )r   LARGE_RANGEr:   r"   r"   r#   r     s    zCOCOEvaluator.large_evaluationc                 C   s
   |  dS )Nr   _ndets_evaluationr:   r"   r"   r#   r     s    z COCOEvaluator.ndets_1_evaluationc                 C   s
   |  dS )Nrq   r   r:   r"   r"   r#   r     s    z!COCOEvaluator.ndets_10_evaluationc                 C   s
   |  dS )Nr   r   r:   r"   r"   r#   r     s    z"COCOEvaluator.ndets_100_evaluation)range_r*   c                    s    fddj D }t|S )Nc                    s   g | ]}j |d  dqS r   re   r   r   r0   r"   r#   r     s   z2COCOEvaluator._range_evalation.<locals>.<listcomp>r   )r0   r   rk   r"   r   r#   r     s    zCOCOEvaluator._range_evalation)max_detsr*   c                    s    fddj D }t|S )Nc                    s   g | ]}j | jd qS )r   r   r   r   r0   r"   r#   r     s   z3COCOEvaluator._ndets_evaluation.<locals>.<listcomp>r   )r0   r   rk   r"   r   r#   r     s    zCOCOEvaluator._ndets_evaluation)r   r   r   r   r   r   r*   c              
   C   s`   |j |j B }t }t|D ]@}	||	p.t|	}
||	p@t|	}|| ||
||||7 }q|S r4   )	image_idsrO   sortedr=   r   evaluate_annotation)clsr   r   r   r   r   r   r   rd   image_idgtpredr"   r"   r#   r     s    
z"COCOEvaluator.evaluate_annotations)
predictionground_truthr   r   r   r   r*   c                 C   s   |j |j ks&J d|j  d|j  dt|jdd }t|jdd }|p^t| | }t }	|D ]:}
||
g }||
g }|	|
  | |||||7  < qj|	S )NzThe prediction image id 'z3' should be the same as the ground truth image id 'z'.c                 S   s   | j S r4   rf   Zboxr"   r"   r#   rQ     rR   z3COCOEvaluator.evaluate_annotation.<locals>.<lambda>c                 S   s   | j S r4   r   r   r"   r"   r#   rQ     rR   )	r   r   ZboxessetkeysunionrO   r=   evaluate_boxes)r   r   r   r   r   r   r   predsrefsrd   rf   detsgtsr"   r"   r#   r     s    


z!COCOEvaluator.evaluate_annotationzlist[BoundingBox])r   r   r   r   r   r*   c                    s  t dd |D sJ dt dd |D s4J dtdd |D sNJ dtdd |D shJ d	| || t|d
d dd}|d | }t|fddd}fdd|D i }i t|D ]\}	}
d}d}t|D ]T\}}||v rq|dkr| s| r q8|
|}||k r.q|}|}q|dks||k rLq||	< |	||< qʇfddt|D   fddt|D } fddtt|D }tfddtt|D }t	|||S )Nc                 s   s   | ]}|j V  qd S r4   )Zis_detectionrZ   pr"   r"   r#   r\     s   z/COCOEvaluator.evaluate_boxes.<locals>.<genexpr>zCPrediction annotations should not contain ground truth annotations.c                 s   s   | ]}|j V  qd S r4   )Zis_ground_truthrZ   gr"   r"   r#   r\     s   zCGround truth annotations should not contain prediction annotations.c                 s   s   | ]}|j V  qd S r4   r   r   r"   r"   r#   r\     s   z0The prediction boxes should have the same label.c                 s   s   | ]}|j V  qd S r4   r   r   r"   r"   r#   r\     s   z2The ground truth boxes should have the same label.c                 S   s   | j S r4   Z_confidencer   r"   r"   r#   rQ     rR   z.COCOEvaluator.evaluate_boxes.<locals>.<lambda>T)rW   reversec                    s   |    S r4   Z_area_inr   r   r"   r#   rQ     rR   )rW   c                    s   g | ]}|   qS r"   r   r   r   r"   r#   r     rR   z0COCOEvaluator.evaluate_boxes.<locals>.<listcomp>rr   c                    s0   g | ](\}}| v r  |  n
|  qS r"   r   rZ   id)
dt_matches	gt_ignorer   r"   r#   r     s   c                    s   g | ]\}} | s|j qS r"   r   r   )	dt_ignorer"   r#   r     rR   c                    s   g | ]} | s|v qS r"   r"   rZ   r   )r   r   r"   r#   r     rR   c                 3   s   | ]} | sd V  qdS )r   Nr"   r   )r   r"   r#   r\     rR   )
allr   r   r   	enumerateiouranger/   r>   r$   )r   r   r   r   r   r   r   r   Z
gt_matchesZidx_dtZdetZbest_iouZidx_bestZidx_gtr   r   r(   matchesr)   r"   )r   r   r   r   r#   r     sf    	






zCOCOEvaluator.evaluate_boxesr   c                 C   sp   d|   krdks&n J d|  d|dks>J d| d|\}}|dkrV||kslJ d| d	| d
d S )Nrr   rv   zthe IoU threshold (z) should be between 0 and 1.r   z"The maximum number of detections (z) should be positive.zThe size range '(z, zM)' should be positive and non-empty, i.e. 0 < size_range[0] <= size_range[1].r"   )r   r   r   lowhighr"   r"   r#   r     s"    


zCOCOEvaluator._assert_paramsc                 C   s   | j   d S r4   )r   cache_clearr:   r"   r"   r#   r5   '  s    zCOCOEvaluator.clear_cacheFverbosec                C   sd   t t| jd| j| j| j| jft| jd| jf}t|dd| dD ]\}}}| j|||d qDd S )N)r   )r   rq   ra   <   )desctotaldisabler   )	r   r	   r   r   r   r   r   r   rI   )r0   r   paramsr   r   rr"   r"   r#   _evaluate_all*  s    	zCOCOEvaluator._evaluate_allc                C   s  ddl m} ddlm} | j|d |ddd}|jdd	d
 |  |  |  | 	 | 
 |  |  |  |  |  |  |  d}| D ]\}}|j|d|dd q| jpt|   }|D ]}	|  |	 d }
|  |	 j}|  |	 j}|  |	 d }|  |	 d }|  |	 d }|  |	 d }|  |	 d }|  |	 d }|  |	 d }|  |	 d }|  |	 d }||	|
d|d|d|d|d|d|d|d|d|d|d|d qd|_ d|_!ddg|_"|j#dd D ]}d|_$d|_ d|_!q|j#dd D ]}d|_$d|_ d|_!q.|j#dd D ]}d|_$d|_ d|_!qV|j#dd D ]}d|_$d|_ d|_!q~|| dS )z+Compute and show the standard COCO metrics.r   )print)Tabler   zCOCO EvaluationT)titleZshow_footerZLabelZTotal)footer)rx   ry   rz   r{   r|   r}   r~   r   r   r   r   r   rightz.2%)Zjustifyr   r?   rC   ZboldnoneZdimr      Zred   Zmagentarq   Zblue   ZgreenN)%Zrichr   Z
rich.tabler   r   Z
add_columnr?   r   r   r   r   r   r   r   r   r   r   r   rP   r   r   r   r   r   r   r   r   r   r   r   r   Zadd_rowZheader_styleZfooter_styleZ
row_stylescolumnsstyle)r0   r   pprintr   tableZmetricsZmetric_nameZmetricr   rf   r?   r   r   ap_sap_map_lr   r   r   ar_sar_mar_lcr"   r"   r#   show_summary7  s    





zCOCOEvaluator.show_summary)pathr   c                C   sX  | j |d | jp t|   }t|d}t|}|t	j
 |D ]}|  | d }|  | j}|  | j}	|  | d }
|  | d }|  | d }|  | d }|  | d }|  | d }|  | d }|  | d }|  | d }||||	|
||||||||f}|| qJW d    n1 sJ0    Y  d S )Nr   wr?   rC   )r   r   r   r   r   r   csvwriterwriterowr@   CSV_HEADERSr   r?   r   r   r   r   r   r   r   )r0   r  r   r   fr  rf   r?   r   r   r   r   r   r   r   r   r   r   r  rowr"   r"   r#   save_csv  sB    
zCOCOEvaluator.save_csvr&   r%   )r(   matchedNPr*   c                 C   s   |dkrt dS tj|t d}tj|td}tj| dd}|| }|| }t|}|| }|td|jd  }	tj	|	ddd ddd }
tj
|| jd	d
}|
|||
jk    }||j S )aO  
        This curve tracing method has some quirks that do not appear when only unique confidence
        thresholds are used (i.e. Scikit-learn's implementation), however, in order to be
        consistent, the COCO's method is reproduced.

        Copyrights: https://github.com/rafaelpadilla/review_object_detection_metrics
        r   rD   )ZdtypeZstable)kindr   Nr   left)Zside)rE   nparrayboolZargsortZcumsumZarangesizemaximum
accumulateZsearchsortedRECALL_STEPSr>   )r   r(   r  r  Zscores_Zmatched_Zindsr<   rcprZi_prZrec_idxZsum_r"   r"   r#   rA     s    
 zCOCOEvaluator._compute_ap)N)N)@r   r   r    rM   r  Zlinspacer   r   r   rE   r   r   r  r	  r   r   r   rb   r1   rJ   ra   rI   r   r?   r   r   r   r   r   r   r   r   r   r   r   rj   r   r   r   r   r   r   r   r   r   r   r   classmethodr   rO   r   r   r   r$   r   staticmethodr   r5   r  r   r  r   r  rA   r"   r"   r"   r#   r@      s   
!


 
 
CY+r@   )0r  collectionsr   r   enumr   r   	functoolsr   	itertoolsr   r	   mathr   typingr   r   r   r   r   r   r   numpyr  r   
annotationr   Zannotationsetr   Zatomicr   Zboundingboxr   Z
file_utilsr   utilsr   r   r   r   r$   rH   rb   rO   ra   rE   rj   r@   r"   r"   r"   r#   <module>   s,   $C+ 