
    i0                       d Z ddlmZ ddlZddlmc mZ ddl	Z	ddl
Z
ddlmZmZmZ e
j                  j!                  dd       ddlZddlmZmZmZ d Zd0d1dZd2d3d	Zd4d
Zd Zd Zd Zd Zd Zd Zd Z d Z!d Z"d Z#d Z$d Z%d Z&d Z'e(dk(  rddl)Z)defdefdefdefdefdefd e fd!e!fd"e"fd#e#fd$e$fd%e%fd&e&fd'e'fgZ*dZ+ e,e*      Z-e*D ]  \  Z.Z/	  e/         e0d(e.        e+d)z  Z+  e0d,e+ d-e- d.       e+e-k(  r	 e0d/       y e
jh                  d)       yy# e1$ r)Z2 e0d*e. d+e2         e)jf                          Y dZ2[2zdZ2[2ww xY w)5u  
Tests for Story 6.07 (Track B): BulkheadGuard — asyncio.gather Exception Isolation

Black Box tests (BB): verify the public contract from the outside —
    run_with_bulkhead isolates exceptions, returns correct results for each
    agent, never raises, handles empty inputs, computes success rates correctly.

White Box tests (WB): verify internal mechanics — asyncio.gather called with
    return_exceptions=True, ColdLedger.write_event called on critical failure,
    no crash when cold_ledger is None, BulkheadResult.error contains the
    exception message string.

Package test:
    PKG1: from core.coherence import BulkheadGuard, BulkheadResult works.

Story: 6.07
File under test: core/coherence/bulkhead.py

ALL tests use async coroutines or mocks — NO real I/O performed.
    )annotationsN)	AsyncMock	MagicMockpatchz/mnt/e/genesis-systemBulkheadGuardBulkheadResultCRITICAL_THRESHOLDc                H    t        j                         j                  |       S )z;Run a coroutine synchronously (no pytest-asyncio required).)asyncioget_event_looprun_until_complete)coros    6/mnt/e/genesis-system/tests/track_b/test_story_6_07.pyrunr   -   s    !!#66t<<    c                   K   | xs ddiS w)z*Coroutine that succeeds and returns value.statusok )values    r   _succeedingr   2   s     $Xt$$s   	c                    K   t        |       w)z#Coroutine that raises RuntimeError.)RuntimeError)messages    r   _failingr   7   s     
w
s   c                 <    t               } t        d      | _        | S )z0Return a mock ColdLedger with async write_event.N)return_value)r   r   write_event)ledgers    r   _make_cold_ledgerr!   <   s    [F"5FMr   c                 h   t               } dt        ddi      fdt        ddi      fdt        d      fdt        dd	i      fg}t        | j	                  |            }t        |      }d
}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  t
              rt        j                  t
              nddt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}|D cg c]  }|j                  s| }	}|D cg c]  }|j                  r| }
}t        |	      }d	}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  t
              rt        j                  t
              nddt        j                         v st        j                  |	      rt        j                  |	      ndt        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}t        |
      }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  t
              rt        j                  t
              nddt        j                         v st        j                  |
      rt        j                  |
      ndt        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}|
d   }|j                  }d}||k(  }|st        j                  d|fd||f      t        j                  |      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}x}}|
d   }|j                   }d}||u}|st        j                  d|fd||f      t        j                  |      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}x}}d}|
d   }|j                   }||v }|st        j                  d|fd||f      t        j                  |      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}x}}yc c}w c c}w )u}   
    BB1: 4 tasks (3 success, 1 exception) →
         3 BulkheadResult(success=True), 1 BulkheadResult(success=False).
    agent-1x   agent-2   
agent-failz	test boomagent-3      ==z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} == %(py6)slenresultspy0py1py3py6assert %(py8)spy8N	successesfailuresr   z0%(py3)s
{%(py3)s = %(py1)s.agent_id
} == %(py6)sr3   r4   r5   is not)z1%(py3)s
{%(py3)s = %(py1)s.error
} is not %(py6)sinz-%(py1)s in %(py6)s
{%(py6)s = %(py4)s.error
}r3   py4r5   )r   r   r   r   run_with_bulkheadr/   
@pytest_ar_call_reprcompare@py_builtinslocals_should_repr_global_name	_safereprAssertionError_format_explanationsuccessagent_iderror)guardtasksr0   @py_assert2@py_assert5@py_assert4@py_format7@py_format9rr8   r9   @py_assert0@py_assert3s                r   2test_bb1_mixed_tasks_correct_success_failure_splitrY   H   s|   
 OE	Ka)*	Ka)*	x,-	Ka)*	E %))%01Gw<1<1<133ww<1#1qqyy1I1"4a!))4H4y>Q>Q>Q33yy>Qx=A=A=A33xx=AA;/;/</<////<///;//////<///////A;(;(D(D((((D(((;((((((D(((((((+(1++++++;+++++;++++;+++++++++++++++ 24s   1V*V*V/V/c                 .   t               } dt        d      fdt        d      fg}t        | j                  |            }| j                  } ||      }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |       rt        j                  |       ndt        j                  |      d	t        j                         v st        j                  |      rt        j                  |      nd	t        j                  |      t        j                  |      d
z  }dd|iz  }t        t        j                  |            dx}x}x}}d |D        }t        |      }	|	sddt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      t        j                  |	      dz  }
t        t        j                  |
            dx}}	y)uA   
    BB2: All tasks fail → get_success_rate() returns 0.0.
    agent-aerr-aagent-berr-bg        r,   zV%(py5)s
{%(py5)s = %(py2)s
{%(py2)s = %(py0)s.get_success_rate
}(%(py3)s)
} == %(py8)srO   r0   r2   py2r4   py5r7   assert %(py10)spy10Nc              3  6   K   | ]  }|j                      y wNrL   .0rV   s     r   	<genexpr>z?test_bb2_all_tasks_fail_success_rate_is_zero.<locals>.<genexpr>n   s     .199}.s   ,assert %(py4)s
{%(py4)s = %(py0)s(%(py2)s)
}allr2   ra   rB   )r   r   r   rC   get_success_raterD   rE   rF   rG   rH   rI   rJ   rK   rl   rO   rP   r0   @py_assert1rS   @py_assert7@py_assert6rU   @py_format11rX   @py_format5s              r   ,test_bb2_all_tasks_fail_success_rate_is_zeroru   b   sJ    OE	HW%&	HW%&E %))%01G!!1!'*1c1*c1111*c11111151115111!111111'111'111*111c1111111.g..3.........3...3..............r   c                    t               } dt        d      fdt        d      fg}	 t        | j                  |            }t	        |t
              }|sddt        j                         v st        j                  t              rt        j                  t              nddt        j                         v st        j                  |      rt        j                  |      nddt        j                         v st        j                  t
              rt        j                  t
              ndt        j                  |      d	z  }t        t        j                  |            d
}y
# t        $ r"}t        j                  d|        Y d
}~y
d
}~ww xY w)zI
    BB3: run_with_bulkhead does not raise even when all tasks fail.
    agent-xzcatastrophic failureagent-yzanother crash5assert %(py4)s
{%(py4)s = %(py0)s(%(py1)s, %(py2)s)
}
isinstancer0   listr2   r3   ra   rB   Nz'run_with_bulkhead raised unexpectedly: r   r   r   rC   rz   r{   rF   rG   rD   rH   rI   rJ   rK   	ExceptionpytestfailrO   rP   r0   rX   rt   excs         r   'test_bb3_run_with_bulkhead_never_raisesr   q   s     OE	H345	H_-.EEe--e45'4((((((((z(((z(((((('((('((((((4(((4(((((((((( E=cUCDDEs   D:E! !	F*FFc                 R   t               } dt        ddi      fdt        ddi      fdt        ddi      fg}t        | j                  |            }| j                  } ||      }d}||k(  }|st        j                  d	|fd
||f      dt        j                         v st        j                  |       rt        j                  |       ndt        j                  |      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}x}}d |D        }t        |      }	|	sddt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      t        j                  |	      dz  }
t        t        j                  |
            dx}}	y)uD   
    BB4: All tasks succeed → get_success_rate() returns 1.0.
    r#   vr%   r&   r'   r)   r*         ?r,   r_   rO   r0   r`   rc   rd   Nc              3  4   K   | ]  }|j                     y wrf   rg   rh   s     r   rj   zAtest_bb4_all_tasks_succeed_success_rate_is_one.<locals>.<genexpr>   s     *Qqyy*s   rk   rl   rm   )r   r   r   rC   rn   rD   rE   rF   rG   rH   rI   rJ   rK   rl   ro   s              r   .test_bb4_all_tasks_succeed_success_rate_is_oner      sc    OE	Ka)*	Ka)*	Ka)*E
 %))%01G!!1!'*1c1*c1111*c11111151115111!111111'111'111*111c1111111*'**3*********3***3**************r   c                    t               } t        | j                  g             }g }||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            dx}}| j                  } ||      }d}||k(  }|st        j                  d|fd	||f      d
t        j                         v st        j                  |       rt        j                  |       nd
t        j                  |      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }	dd|	iz  }
t        t        j                  |
            dx}x}x}}y)uL   
    BB5: Empty task list → returns [], get_success_rate returns 1.0.
    r,   z%(py0)s == %(py3)sr0   r2   r4   assert %(py5)srb   Nr   r_   rO   r`   rc   rd   )r   r   rC   rD   rE   rF   rG   rH   rI   rJ   rK   rn   )rO   r0   rQ   rp   @py_format4@py_format6rS   rq   rr   rU   rs   s              r   3test_bb5_empty_task_list_returns_empty_and_rate_oner      s     OE%))"-.G7b=7b77b!!1!'*1c1*c1111*c11111151115111!111111'111'111*111c1111111r   c                   
 t               } dt               fdt               fg}t        j                  i 

fd}t        d|      5  t        | j                  |             ddd       
j                  }d} ||      }d}||u }|st        j                  d	|fd
||f      dt        j                         v st        j                  
      rt        j                  
      ndt        j                  |      t        j                  |      t        j                  |      t        j                  |      dz  }t        j                  d      dz   d|iz  }	t!        t        j"                  |	            dx}x}x}x}}y# 1 sw Y   xY w)z}
    WB1: asyncio.gather is called with return_exceptions=True so that
         exceptions are collected, not re-raised.
    r[   r]   c                 R   K   j                  |        | i | d {   S 7 wrf   )update)coroskwargscaptured_kwargsoriginal_gathers     r   
spy_gatherzFtest_wb1_gather_called_with_return_exceptions_true.<locals>.spy_gather   s+     v&$e6v6666s   '%'z&core.coherence.bulkhead.asyncio.gather)side_effectNreturn_exceptionsTis)zI%(py6)s
{%(py6)s = %(py2)s
{%(py2)s = %(py0)s.get
}(%(py4)s)
} is %(py9)sr   )r2   ra   rB   r5   py9z9asyncio.gather must be called with return_exceptions=Truez
>assert %(py11)spy11)r   r   r   r   gatherr   r   rC   getrD   rE   rF   rG   rH   rI   _format_assertmsgrJ   rK   )rO   rP   r   rp   rX   rR   @py_assert8rq   @py_format10@py_format12r   r   s             @@r   2test_wb1_gather_called_with_return_exceptions_truer      so   
 OE	KM"	HJE
 nnOO7 
7Z	P ,E##E*+,  2 23 t 3t;  3t                   3    4    8<    	D     , ,s   
FFc                    t               } t        |       }dt        d      fdt        d      fg}t        |j	                  |             | j
                  j                          | j
                  j                  }|\  }}|j                  d      xs	 |r|d   nd}d	}||k(  }|st        j                  d
|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }	dd|	iz  }
t        t        j                   |
            dx}}y)zt
    WB2: success_rate < 0.5 triggers ColdLedger.write_event with
         event_type='swarm_critical_failure'.
    cold_ledgerr[   r\   r]   r^   
event_typer   Nswarm_critical_failurer,   r   r   r   rb   )r!   r   r   r   rC   r   assert_awaited_once	call_argsr   rD   rE   rF   rG   rH   rI   rJ   rK   )r    rO   rP   call_kwargsargsr   r   rQ   rp   r   r   s              r   @test_wb2_success_rate_below_threshold_triggers_cold_ledger_writer      s    
  Ff-E 
HW%&	HW%&E &'
**,$$..KLD&L)Hd1g4J11:11111:1111111:111:11111111111r   c                 "   t        d      } dt        d      fdt        d      fg}	 t        | j                  |            }t	        |t
              }|sddt        j                         v st        j                  t              rt        j                  t              ndd	t        j                         v st        j                  |      rt        j                  |      nd	d
t        j                         v st        j                  t
              rt        j                  t
              nd
t        j                  |      dz  }t        t        j                  |            d}y# t        $ r"}t        j                  d|        Y d}~yd}~ww xY w)u   
    WB3: cold_ledger=None → no crash when success_rate < CRITICAL_THRESHOLD.
         BulkheadGuard gracefully skips ledger write.
    Nr   rw   zcrash-xrx   zcrash-yry   rz   r0   r{   r|   z*BulkheadGuard crashed without ColdLedger: r}   r   s         r   4test_wb3_no_cold_ledger_no_crash_on_critical_failurer      s    
 d+E	HY'(	HY'(EHe--e45'4((((((((z(((z(((((('((('((((((4(((4(((((((((( H@FGGHs   D:E# #	F,F		Fc                    t               } d}dt        |      fg}t        | j                  |            }t	        |      }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  t              rt        j                  t              nddt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }d	d
|iz  }t        t        j                  |            dx}x}}|d   }	|	j                  }
d}|
|u }|st        j                  d|fd|
|f      dt        j                         v st        j                  |	      rt        j                  |	      ndt        j                  |
      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}
x}}|	j                  }
d}|
|u}|st        j                  d|fd|
|f      dt        j                         v st        j                  |	      rt        j                  |	      ndt        j                  |
      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}
x}}|	j                  }||v }
|
st        j                  d|
fd||f      dt        j                         v st        j                  |      rt        j                  |      nddt        j                         v st        j                  |	      rt        j                  |	      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            dx}
}y)zt
    WB4: BulkheadResult.error for a failing task contains the str(exception)
         of the raised exception.
    zspecific error payloadz	agent-errr%   r,   r.   r/   r0   r1   r6   r7   Nr   Fr   )z/%(py2)s
{%(py2)s = %(py0)s.success
} is %(py5)srV   )r2   ra   rb   assert %(py7)spy7r<   )z1%(py2)s
{%(py2)s = %(py0)s.error
} is not %(py5)sr>   )z-%(py0)s in %(py4)s
{%(py4)s = %(py2)s.error
}error_messagerm   zassert %(py6)sr5   )r   r   r   rC   r/   rD   rE   rF   rG   rH   rI   rJ   rK   rL   rN   )rO   r   rP   r0   rQ   rR   rS   rT   rU   rV   rp   rX   r   @py_format8rt   s                  r   9test_wb4_bulkhead_result_error_contains_exception_messager      s"   
 OE,M8M234E%))%01Gw<1<1<133ww<1
A999911977$7$7$117$GG#=G####=G######=###=######A###A###G#######r   c                    ddl m} m}m} | t        u }|st	        j
                  d|fd| t        f      dt        j                         v st	        j                  |       rt	        j                  |       nddt        j                         v st	        j                  t              rt	        j                  t              nddz  }dd	|iz  }t        t	        j                  |            d
}|t        u }|st	        j
                  d|fd|t        f      dt        j                         v st	        j                  |      rt	        j                  |      nddt        j                         v st	        j                  t              rt	        j                  t              nddz  }dd	|iz  }t        t	        j                  |            d
}d}||k(  }|st	        j
                  d|fd||f      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      dz  }dd|iz  }t        t	        j                  |            d
x}}y
)zEPKG1: from core.coherence import BulkheadGuard, BulkheadResult works.r   r   r   )z%(py0)s is %(py2)sBGr   )r2   ra   zassert %(py4)srB   NBRr	   g      ?r,   r   CTr   r   rb   )core.coherencer   r	   r
   rD   rE   rF   rG   rH   rI   rJ   rK   )	r   r   r   rp   @py_format3rt   rQ   r   r   s	            r   3test_pkg1_package_exports_bulkhead_guard_and_resultr      s0   bb22222229222r   c                     t               } t        |       }dt               fdt        d      fg}t	        |j                  |             | j                  j                          y)u   
    Edge: success_rate == CRITICAL_THRESHOLD (0.5) does NOT trigger critical
    event — threshold is strictly less-than.
    2 tasks, 1 success = 0.5 exactly → no ledger write.
    r   zagent-okr(   halfN)r!   r   r   r   r   rC   r   assert_not_awaited)r    rO   rP   s      r   @test_bb_success_rate_exactly_at_threshold_does_not_emit_criticalr   
  s]      Ff-E 
[]#	x'(E &'
))+r   c                 l   t               } dt        ddi      fdt        d      fdt        ddi      fg}t        | j	                  |            }|d   }|j
                  }d}||k(  }|st        j                  d	|fd
||f      t        j                  |      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}x}}|d   }|j
                  }d}||k(  }|st        j                  d	|fd
||f      t        j                  |      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}x}}|d   }|j
                  }d}||k(  }|st        j                  d	|fd
||f      t        j                  |      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}x}}y)zF
    Edge: results are returned in the same order as input tasks.
    zagent-firstnr%   zagent-secondoopszagent-thirdr*   r   r,   r:   r;   r6   r7   Nr'   )r   r   r   r   rC   rM   rD   rE   rI   rJ   rK   )	rO   rP   r0   rW   rQ   rR   rS   rT   rU   s	            r   test_bb_results_preserve_orderr     s    OE	S!H-.	&)*	S!H-.E
 %))%01G1:/:/-/-////-///://////-///////1:0:0.0.0000.000:000000.00000001:/:/-/-////-///://////-///////r   c                    t               } ddi}t        | j                  dt        |      fg            }t	        |      }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  t              rt        j                  t              nddt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      d	z  }d
d|iz  }t        t        j                  |            dx}x}}|d   }|j                  }d}||u }|st        j                  d|fd||f      t        j                  |      t        j                  |      t        j                  |      dz  }d
d|iz  }t        t        j                  |            dx}x}x}}|d   }|j                  }||k(  }|st        j                  d|fd||f      t        j                  |      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }	dd|	iz  }
t        t        j                  |
            dx}x}}|d   }|j                  }d}||u }|st        j                  d|fd||f      t        j                  |      t        j                  |      t        j                  |      dz  }d
d|iz  }t        t        j                  |            dx}x}x}}y)uC   Single task that succeeds → success=True, result set, error=None.keyr   solor%   r,   r.   r/   r0   r1   r6   r7   Nr   Tr   z/%(py3)s
{%(py3)s = %(py1)s.success
} is %(py6)sr;   )z.%(py3)s
{%(py3)s = %(py1)s.result
} == %(py5)spayload)r3   r4   rb   r   r   )z-%(py3)s
{%(py3)s = %(py1)s.error
} is %(py6)s)r   r   rC   r   r/   rD   rE   rF   rG   rH   rI   rJ   rK   rL   resultrN   )rO   r   r0   rQ   rR   rS   rT   rU   rW   r   r   s              r   test_bb_single_task_successr   .  s   OEgG%))FK4H+I*JKLGw<1<1<133ww<11:%:%%%%%%%%%:%%%%%%%%%%%%%1:':'''''''':'''''''''''''''''''1:#:#t#t####t###:######t#######r   c                 J   t               } t        | j                  dt        d      fg            }t	        |      }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  t              rt        j                  t              nddt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }d	d
|iz  }t        t        j                  |            dx}x}}|d   }|j                  }d}||u }|st        j                  d|fd||f      t        j                  |      t        j                  |      t        j                  |      dz  }d	d
|iz  }t        t        j                  |            dx}x}x}}|d   }|j                  }d}||u }|st        j                  d|fd||f      t        j                  |      t        j                  |      t        j                  |      dz  }d	d
|iz  }t        t        j                  |            dx}x}x}}d}|d   }|j                  }||v }|st        j                  d|fd||f      t        j                  |      t        j                  |      t        j                  |      dz  }d	d
|iz  }t        t        j                  |            dx}x}x}}y)uA   Single task that fails → success=False, result=None, error set.r   downr%   r,   r.   r/   r0   r1   r6   r7   Nr   Fr   r   r;   )z.%(py3)s
{%(py3)s = %(py1)s.result
} is %(py6)sr>   r@   rA   )r   r   rC   r   r/   rD   rE   rF   rG   rH   rI   rJ   rK   rL   r   rN   )	rO   r0   rQ   rR   rS   rT   rU   rW   rX   s	            r   test_bb_single_task_failurer   :  s   OE%))FHV4D+E*FGHGw<1<1<133ww<11:&:&&&&&&&&&:&&&&&&&&&&&&&1:$:$$$$$$$$$:$$$$$$$$$$$$$%WQZ%Z%%%6%%%%%6%%%%6%%%Z%%%%%%%%%%%r   __main__u+   BB1: 3 success, 1 failure → correct splitu%   BB2: All fail → success_rate == 0.0z#BB3: run_with_bulkhead never raisesu(   BB4: All succeed → success_rate == 1.0u    BB5: Empty list → [], rate 1.0z.WB1: gather called with return_exceptions=Truez/WB2: rate < 0.5 triggers ColdLedger write_eventz2WB3: cold_ledger=None no crash on critical failurez4WB4: BulkheadResult.error contains exception messagezGPKG1: Package exports BulkheadGuard, BulkheadResult, CRITICAL_THRESHOLDz(EDGE: rate == 0.5 does NOT emit criticalz"EDGE: results preserve input orderzEDGE: single successzEDGE: single failurez	  [PASS] r%   z	  [FAIL] z: 
/z tests passedz(ALL TESTS PASSED -- Story 6.07 (Track B)rf   )r   dict | Nonereturnr   )boom)r   strr   dict)r   r   )5__doc__
__future__r   builtinsrF   _pytest.assertion.rewrite	assertionrewriterD   r   sysunittest.mockr   r   r   pathinsertr   core.coherence.bulkheadr   r	   r
   r   r   r   r!   rY   ru   r   r   r   r   r   r   r   r   r   r   r   r   __name__	tracebacktestspassedr/   totalnamefnprintr~   r   	print_excexitr   r   r   <module>r      s  * #    
 5 5 * +  =
%
 
,4/E"+ 2 62.H"$.,&0"	$& z 
78jk	02^_	.0WX	35cd	+-`a	9;mn	:<|}	=?st	?Az{	R  UH  	I	35uv	-/MN	!<=	!<=E" FJE "b	"DIdV$%aKF	" 
Bvhawm
,-89G 6  	"IdV2cU+,I!!	"s   DE$EE