
    #jv                        d Z ddlmZ ddl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
mZmZ ddlmZ ddlmZ ddlmZmZm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"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 ed#$            Z/dZ0d%Z1d&Z2dZ3d'Z4e	 G d( d)             Z5efdd*dEd+Z6efdd*dFd,Z7efdd-dGd.Z8dHd/Z9dId0Z:dJd1Z;dKd2Z<dLdMd3Z=dNd4Z>dOd5Z?dPd6Z@dd7d8ZAeee ddd9	 	 	 	 	 	 	 	 	 dQd:ZBedddddd;dddd<ddddddd<dd=	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 dRd>ZCefdddddd;dddddd?ddddddd<dddd@	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 dSdAZDdLdTdBZEg dCZFeGdDk(  r eH eE             y)Uu  dispatch.anu_pickup_driver — task-2721 P0-b user-level systemd path driver.

default DISABLED. activation flag(memory/state/p0b_driver_enabled == "enabled") 부재 시 전면 no-op.
scan 한정: memory/events/task-*.result.json final 만. tmp/partial/다른 marker·jsonl·md → NOOP_NOT_TARGET.
6조건 전부 PASS 시에만 P0-a pickup_once(lock-free) 호출. pickup_once 가 ANU-owned wake argv(dry-run)를 빌드.
실제 cron 발사 0(P0-a dry_run=True/FIRE_NOT_ACTIVATED). ANU key literal 0 — .env.keys 런타임 로드만.
    )annotationsN)	dataclass)datetime	timedeltatimezone)Optional)pickup_once)CANONICAL_ROOTVERDICT_AUTHORITATIVEverify_collector_authoritativezsystemd-pathzmemory/state/p0b_driver_enabledzmemory/eventszmemory/p0b_state/quarantinezmemory/p0b_state/processedz"memory/p0b_state/driver_runs.jsonlztask-*.result.json2   z!memory/state/p0b_activation_epochg       @   g?
WAKE_BUILTPICKUP_SKIP
QUARANTINEFIRE_FAILEDNOOP_DISABLEDNOOP_NOT_TARGETNOOP_NOT_READYNOOP_LEGACY_SKIPNOOP_MAX_FILES_DEFERANUFOREIGNSELFenableddisabled	   )hoursSKIP_TERMINALSKIP_DEDUPEPENDING_OWNER_PROOFc                      e Zd ZU ded<   ded<   ded<   dZded<   dZd	ed
<   dZded<   dZded<   dZded<   dZ	ded<   e
Zded<   eZded<   ddZy)DriverRecordstrtsresult_pathverdictNOptional[str]owner_key_classFboolquarantinedquarantine_reasonfire_cron_idr   intretryerrordriver
activationc                    | j                   | j                  | j                  | j                  | j                  | j
                  | j                  | j                  | j                  | j                  | j                  dS )Nr%   r&   r'   r)   r+   r,   r-   r/   r0   r1   r2   r4   )selfs    M/home/jay/workspace/.worktrees/task-2729p8-dev5/dispatch/anu_pickup_driver.pyto_jsonzDriverRecord.to_json[   sc    ''++||#33++!%!7!7 --ZZZZkk//
 	
    )returndict)__name__
__module____qualname____annotations__r)   r+   r,   r-   r/   r0   DRIVER_NAMEr1   ACTIVATION_DISABLEDr2   r7    r8   r6   r#   r#   M   sg    GL%)O])K'+}+"&L-&E3NE=FC)J)
r8   r#   flag_readerc                  |g	  |       }|yt        |      j                         r*t        |      j                         d   j                         S t        |      j                         S t        j
                  j                  | t              }	 t        |dd      5 }|j                         }ddd       j                         S # t         $ r Y yw xY w# 1 sw Y   (xY w# t        t        f$ r Y yw xY w)u   flag 파일 첫 줄 trim 값 반환. 부재/읽기실패 → "" (=> disabled).
    flag_reader: 테스트 주입용 callable() -> Optional[str] (None 이면 실제 파일 읽기).N r   rutf-8encoding)	Exceptionr$   strip
splitlinesospathjoinACTIVATION_FLAG_RELopenreadlineOSError
ValueError)rootrC   val	flag_pathfhfirsts         r6   read_activationrZ   l   s     	-C ;36s8>>3Cs3x""$Q'--/YSIYYT#67I)S73 	"rKKME	" ;;=  			" 	"Z  s;   C C! C.C! 	CCCC! !C32C3c               *    t        | |      t        k(  S )NrB   )rZ   ACTIVATION_ENABLED)rU   rC   s     r6   is_activatedr]      s    4[9=OOOr8   epoch_readerc                  |?	  |       }|y	 t        t        |      j                         d   j	                               S t        j                  j                  | t              }	 t        |dd      5 }|j                         }ddd       	 t        j	                               S # t         $ r Y yw xY w# t
        t        f$ r Y yw xY w# 1 sw Y   GxY w# t        t
        f$ r Y yw xY w# t
        $ r Y yw xY w)u   activation_epoch 마커(memory/state/p0b_activation_epoch) 첫 줄을 unix timestamp(float)로 읽음.
    부재/읽기실패/파싱실패 → None (호출자는 fail-closed 처리). epoch_reader: 테스트 주입 callable()->Optional[str|float].Nr   rF   rG   rH   )rJ   floatr$   rL   rK   rT   
IndexErrorrM   rN   rO   ACTIVATION_EPOCH_RELrQ   rR   rS   )rU   r_   rV   
epoch_pathrX   rY   s         r6   read_activation_epochre      s     	.C ;	S,,.q1779:: d$89J*cG4 	"KKME	"U[[]##  		 J' 			" 	"Z    s^   B( 2B7 'C 5CC C- (	B43B47C	C	CC C*)C*-	C98C9c                    | syt         j                  j                  t        |             }|j	                  d      xr |j                  d      S )u   final task-*.result.json 만 True. basename 이 'task-' 로 시작 + '.result.json' 으로 끝.
    '.result.json.tmp-...'/partial/.md/.jsonl/다른 marker → False.Fztask-z.result.json)rM   rN   basenamer$   
startswithendswith)rN   bases     r6   	is_targetrk      s?     77CI&D??7#En(EEr8   c                ,     |        j                         S N)	isoformat)clocks    r6   _now_kstrp      s    7r8   c                |    	 t        | j                  d            rt        S 	 t        S # t        $ r	 t        cY S w xY w)uO   envelope claim 으로 SELF/FOREIGN 라벨 추정 (판정 아님, 라벨링만).self_key_used)r*   getOKC_SELFAttributeErrorOKC_FOREIGN)envelopes    r6   _envelope_claim_classrx      sA    _-.O /   s   ) ;;c                    t         j                  j                  | |      }t         j                  j                  |      s|S t	        t        t        j                         dz              }t         j                  j                  | | d|       }d}t         j                  j                  |      rM|dz  }t         j                  j                  | | d| d|       }t         j                  j                  |      rM|S )uY   dest_dir/basename. 이미 존재하면 .{ms타임스탬프}[-n] suffix 로 충돌 회피.i  .r      -)rM   rN   rO   existsr$   r.   time)dest_dirrg   destr%   candns         r6   _collision_safe_destr      s    77<<(+D77>>$	St#$	%B77<<XJat"45D	A
''..
	Qww||H
!B4q&<= ''..
 Kr8   c                   |xs$ t         j                  j                  |t              }	 t        j                  |d       t        |t         j                  j                  |             }	 t        j                  | |       y# t        $ r}d| cY d}~S d}~ww xY w# t        $ rG 	 t        j                  | |       Y y# t        t        j                  f$ r}d| cY d}~cY S d}~ww xY ww xY w)u  terminal(WAKE_BUILT/PICKUP_SKIP) result 파일을 watched 밖 processed 디렉토리로
    atomic 이동. 성공 시 None, 실패 시 에러 메시지(str) 반환(fail-safe: 크래시 0).
    os.replace(같은 fs atomic) 우선, 실패 시 shutil.move fallback.Texist_oku   processed move 실패: N)rM   rN   rO   PROCESSED_DIR_RELmakedirsr   rg   rS   replaceshutilmoveError)rN   rU   processed_dirpdirr   excs         r6   _move_processedr      s     ABGGLL/@AD/
D4(#D"''*:*:4*@A3


4	  /(../
  3	3KKd#& 	3,SE22	3	3sT   A B +B 	BBBB	C-'B??C)C$C)C-$C))C-c                X   |xs$ t         j                  j                  |t              }	 t        j                  |d       t        |t         j                  j                  |             }t        j                  | |       y# t        t        j                  f$ r}d| cY d}~S d}~ww xY w)ud   result 파일을 quarantine 디렉토리로 이동. 실패 시 예외 메시지 반환(None=성공).Tr   Nu   quarantine move 실패: )rM   rN   rO   QUARANTINE_DIR_RELr   r   rg   r   r   rS   r   )rN   rU   quarantine_dirqdirr   r   s         r6   _quarantine_mover      s    CRWW\\$0BCD0
D4(#D"''*:*:4*@AD$V\\" 0)#//0s   AB B)B$B)$B)c                   |xs$ t         j                  j                  |t              }	 t        j                  t         j                  j                  |      d       t        |dd      5 }|j                  t        j                  | j                         d      dz          |j                          t        j                  |j                                d	d	d	       y	# 1 sw Y   y	xY w# t        $ r Y y	w xY w)
uM   DriverRecord.to_json() 한 줄 JSON append. ANU key literal 절대 미기록.Tr   arG   rH   F)ensure_ascii
N)rM   rN   rO   EVIDENCE_JSONL_RELr   dirnamerQ   writejsondumpsr7   flushfsyncfilenorS   )recordrU   evidence_pathevrX   s        r6   _append_evidencer      s    		@"'',,t-?@B
BGGOOB'$7"cG, 	"HHTZZ 0uELMHHJHHRYY[!	" 	" 	"  s1   AC, ,A+C C,  C)%C, )C, ,	C87C8c                   |xs" t         j                  j                  |ddd      }t         j                  j                  |      sy	 t	        |dd      5 }|D ]o  }|j                         }|s	 t        j                  |      }t        |t              s=|j                  d      d	k(  sR|j                  d
      | k(  sg ddd       y 	 ddd       y# t        t        f$ r Y w xY w# 1 sw Y   yxY w# t        t        f$ r Y yw xY w)uL   dedupe ledger 에 동일 task_id 의 PICKUP_WAKE_BUILT 항목 존재 여부.memoryeventszcallback_4tuple_index.jsonlFrF   rG   rH   eventPICKUP_WAKE_BUILTtask_idNT)rM   rN   rO   isfilerQ   rK   r   loadsrT   	TypeError
isinstancer:   rs   rS   )r   ledger_pathrU   ledgerrX   lineentrys          r6   _dedupe_hitr      s    BGGLLh"?F 77>>&!&#0 	 B  zz| JJt,E ud+		'*.AA		),7	  	  	 "  #I. 	 "  Z  sr   C6 C*0CC*C*+C* C*C6 
C*C6 C'$C*&C''C**C3/C6 3C6 6DD)stat_fnc                   |y|xs t         j                  }	  ||       }|j                  |k  ryy# t        $ r Y yw xY w)u  legacy cutoff 판정. skip 해야 하면 reason(str) 반환, 정상 진행이면 None.
    - activation_epoch is None (부재/불명확) → fail-open 금지 → skip("epoch_absent").
    - stat 실패 → skip("legacy_stat_fail") (보수적 skip — canonical 무수정).
    - result mtime < activation_epoch → skip("pre_activation_epoch") (legacy).
    - mtime >= activation_epoch → None (post-activation, 정상 decision path).Nepoch_absentlegacy_stat_failpre_activation_epoch)rM   statrS   st_mtime)rN   activation_epochr   sts       r6   _legacy_cutoff_checkr     sR      G"T] 
{{%%%	  "!"s   2 	>>)
stable_secretriesintervalsleep_fnr   c                  |xs t         j                  }|xs t        j                  }g }t	        t        d|            D ]^  }	  ||       }	|j                  |	j                  |	j                  f       t        |      dk\  r|d   |d   k(  r n||dz
  k  sW ||       ` |d   d   }
 |       j                         |
z
  }||k  ryt        |      dk\  r|d   |d   k7  ryy# t        $ r Y  yw xY w)	u  write race 방어 readiness 판정. (ready: bool, reason: str) 반환.

    판정 규칙:
    - 파일 stat 실패(존재X 등) → (False, "stat_fail"): DEFER, 다음 트리거 재평가.
    - mtime 이 now - stable_sec 이내(최근 생성/수정) → (False, "recent_mtime"): writer 미완 가능 → DEFER.
    - size/mtime 안정성: 짧은 간격으로 최대 retries 회 stat 하여 (size, mtime) 불변 확인.
      마지막 두 샘플이 다르면 (False, "unstable"): 아직 쓰는 중 → DEFER.
    - 위 모두 통과(aged + stable) → (True, "ready").
    단순 무한 sleep 없음: 총 window = (retries-1) * interval ≤ 약 0.6s.
    r{   )F	stat_fail   )Frecent_mtime)Funstable)Tready)rM   r   r~   sleeprangemaxrS   appendst_sizer   len	timestamp)rN   ro   r   r   r   r   r   samplesattemptr   mtimeages               r6   _check_readinessr   !  s    (  G%4::HGQ) 
	(B 	

BKK01w<1!;Wq[ X
 BKNE
'



%C
Z&
7|qWR[GBK7"  	('	(s   C""	C/.C/rE   F)rU   	pickup_fnlauncher_fn	verify_fnprobero   executor_keyr   r   r   write_evidencer   r   readiness_retriesreadiness_intervalr   r   legacy_cutoffr   c               *   	./ |xs t         }|xs t        }|xs d }t        |      /d7fd.	 	 d8	 	 	 d9. 	/fd}t               s .t	        / t
        t                    S |r/t         ||      }| .t	        / t        d|t                    S t         ||t        n||t        n||t        n|||	      \  }}|s .t	        / t        d|t                    S 	 t        j                  j!                         }|dk  r |d
      S 	 t%         d      5 }|j'                         }ddd       dv r |d      S 	 t)        j*                  |j-                  d            }t3        |t4              s |d      S |j7                  d      }|j7                  d      }t3        |t8              r|j;                         s |d      S t3        |t8              r|j;                         s |d      S |j;                         }t        j                  j=                  |      |k7  sd|v sd|v sd|v r |d      S |j7                  d      }t3        |t4              s |dt>              S |j7                  d      xs |j7                  d      } 	  ||||||  |             }!tE        |!d#d      tF        k7  r |d$tC        |            S tH        }"t        j                  jK                         }#t        j                  jM                  |#| d%      }$t        j                  jM                  |#| d&      }%t        j                  jO                  |$      s,t        j                  jO                  |%      stQ        ||      r-tS         |
      }& .t	        / tT        |"d|&t        '            S 	  | ||(      }'tE        |'d#d      }(|(tX        k(  rd})d}*|/	  |tE        |'d+d      |tE        |'d,d-      .      }+tE        |+d/d      })tS         |
      }&d1jM                  d2 |*|&fD              xs d}, .t	        / tZ        tH        |)|,t        3            S |(t\        t^        fv r,tS         |
      }& .t	        / tT        |"|&t        *            S |(t`        tb        fv r$ |d4t9        |(      je                         z   |"      S tE        |'d5d      }-t3        |-tf              r|-rd1jM                  |-      nd6|( }, .t	        / tV        |"|,t        *            S # t"        $ r} |d
d|       cY d}~S d}~ww xY w# 1 sw Y   xY w# t"        $ r} |dd|       cY d}~S d}~ww xY w# t.        t0        f$ r} |dd|       cY d}~S d}~ww xY w# t@        $ r!} |d tC        |      d!| "      cY d}~S d}~ww xY w# t@        $ r,} .t	        / tV        |"d)| t        *            cY d}~S d}~ww xY w# t@        $ r}d0| }*Y d}~d}~ww xY w):up   단일 result.json 처리. activation 은 호출자(scan_once)가 이미 보장 — 여기선 target/6조건만.c                 4    t        j                  t              S rm   r   nowKSTrA   r8   r6   <lambda>zprocess_one.<locals>.<lambda>i      hll3/ r8   c                &    rt        |        | S rm   )r   )recr   rU   r   s    r6   _emitzprocess_one.<locals>._emitm  s    S$6
r8   Nc                    t              }|}|r|r|dz   |z   n|} t        	t        |d| |t                    S )N; T)r%   r&   r'   r)   r+   r,   r0   r2   )r   r#   VERDICT_QUARANTINEr\   )
reasonowner_class	extra_errmove_errerrr   rN   r   rU   r%   s
        r6   _quarantinez process_one.<locals>._quarantiner  sW    #D$?-03:(hC\&'$)	
 	 		r8   r%   r&   r'   r2   )r   r   Fr%   r&   r'   r+   r,   r2   )ro   r   r   r   r   r   size0u   getsize 실패: )r   r   rb
parse_failzread:     	null_byterG   zparse: schema_failr   completion_signalz../\collector_envelopeowner_unprovable)r   schedule_id)r   rw   r   r   r   r   owner_proof_erroru   verify_fn 예외: )r   r   r'   owner_proof_failz.pickup.donez.pickup.acked)r%   r&   r'   r)   r+   r0   r2   )r   r   u   pickup_fn 예외: )r%   r&   r'   r)   r0   r2   argvsha256rE   )r   r  decisionu   launcher 예외: r   c              3  &   K   | ]	  }|s|  y wrm   rA   ).0xs     r6   	<genexpr>zprocess_one.<locals>.<genexpr>+  s     ?aQ?s   )r%   r&   r'   r)   r-   r0   r2   pickup_reasonszpickup verdict=)r   r#   r9   r#   )NN)r   r$   r   r(   r   r(   r9   r#   )4r	   r   rp   rk   r#   VERDICT_NOOP_NOT_TARGETr\   r   VERDICT_NOOP_LEGACY_SKIPr   
STABLE_SECSTABILITY_RETRIESSTABILITY_INTERVAL_SECVERDICT_NOOP_NOT_READYrM   rN   getsizerS   rQ   readr   r   decoderT   UnicodeDecodeErrorr   r:   rs   r$   rK   rg   rv   rJ   rx   getattrr   OKC_ANUr   rO   r}   r   r   VERDICT_PICKUP_SKIPVERDICT_FIRE_FAILED_PICKUP_WAKE_BUILTVERDICT_WAKE_BUILT_PICKUP_SKIP_TERMINAL_PICKUP_SKIP_DEDUPE_PICKUP_QUARANTINE_PICKUP_PENDINGlowerlist)0rN   rU   r   r   r   r   ro   r   r   r   r   r   r   r   r   r   r   r   r   r   r   skip_reasonr   ready_reasonsizer   rX   rawresultr   r   rw   r   vr   
result_dir	done_path
acked_pathr   respvr-   
launch_errlrr   r  r   r%   s0   ``       ` ``                                 @@r6   process_oner/  O  sd   0 ([I;;I0/E	%B
 ?C/3,8D $ T?\+)	
  	 *4BR\cd" 0!"--   +!+!3:%6%>!DU+=+E'K]E< \**)
  	Hwwt$ qy7##C$ 	'')C	 #~;''DCJJw/0
 fd#=))jj#G

#67w$=))(#.3D3J3J3L=))mmoG
 	!W,7?'>7?=)) zz./Hh%-;GG,,}-JM1JKA%#
 q)T"&;;-'<X'FH 	H K &JZG9L)ABIjWI]*CDJ
y!77>>*%wT2"4}=\'')
  	
<[Q 
i	&B	 
"7 C.#"3"5
  'r:t< #4}=ii?J#9??G4\&#%)
  	 
#%899"4}=\'')
  	 
 /229s2w}}6KPP c9d+G *7D 9g$))G
_]_\`KaC##%  a  H70@.FGGH	 	 C<VC5>BBC *+ D<WSE?CCDP  A.'<X'F'9#%?A 	AAD  \''&se,)
  	0  706
7s   .T U 'T;8U $U+ V 3W .W9 	T8!T3-T83T8;U U 	U(U#U(#U(+V:VVV	V>V93V>9V>	W6
!W1+W61W69	XXXT)r   r   r   r   ro   r   r   r   r   pathsrC   r   r   r   r   r   r   	max_filesr   r   r_   r   c          
        |xs d }t        | |      s2t        t        |      dt        t              }|rt        || |       |gS |
Ht        j                  j                  | t        t              }t        t        j                  |            }nt        |
      }|}|r|t        | |      }|t        n|}g }|t!        |      |kD  r
||d }|d| }g }|D ]V  }t#        |fi d| d|d	|d
|d|d|d|d|d|d|	d|d|d|d|d|d|d|d|d|}|j%                  |       X |rNt        t        |      dt&        ddt!        |       t(              }|rt        || |       |j%                  |       |S )u   진입점. (1) activation 재확인: disabled → [NOOP_DISABLED] 1건 + evidence + pickup 미호출.
    (2) enabled → 후보 경로 결정 → 각 path process_one → record 목록 반환. 각 record evidence append.c                 4    t        j                  t              S rm   r   rA   r8   r6   r   zscan_once.<locals>.<lambda>l  r   r8   rB   rE   r   Nr^   rU   r   r   r   r   ro   r   r   r   r   r   r   r   r   r   r   r   r   r   Fzmax_files_defer:r   )r]   r#   rp   VERDICT_NOOP_DISABLEDr@   r   rM   rN   rO   EVENTS_DIR_RELRESULT_GLOBsortedglobr!  re   	MAX_FILESr   r/  r   VERDICT_NOOP_MAX_FILES_DEFERr\   ) rU   r   r   r   r   ro   r   r   r   r   r0  rC   r   r   r   r   r   r   r1  r   r   r_   r   r   pattern
candidatesepochlimitdeferredrecordsp	defer_recs                                    r6   	scan_oncerC  P  s   8 0/E +6)*	
 S$6u }'',,t^[ADIIg./
%[
 E%dF #*I	EHS_u4ef%'
G 

  
 $	

  
 
 
 &
 $
 *
 (
 *
 (
 "
 0
   2!
" #
$ #%
& ('
( )
, 	s/2  0 0X@)
	 Ym<y!Nr8   c                &    t        t        d       y)NT)r   r   )rC  r
   )r  s    r6   mainrE    s    nD1r8   )%r?   rP   r5  r   r   r   r6  r9  rc   r  r  r  r  r  r   r  r4  r  r  r  r:  r  rv   rt   r\   r@   r   r#   rZ   r]   re   rk   r   r   r/  rC  rE  __main__)rU   r$   r9   r$   )rU   r$   r9   r*   )rU   r$   r9   Optional[float])rN   r$   r9   r*   )r9   r$   )rw   r:   r9   r$   )r   r$   rg   r$   r9   r$   rm   )rN   r$   rU   r$   r   r(   r9   r(   )rN   r$   rU   r$   r   r(   r9   r(   )r   r#   rU   r$   r   r(   r9   None)r   r$   r   r(   rU   r$   r9   r*   )
rN   r$   r   ra   r   r.   r   ra   r9   tuple)rN   r$   rU   r$   r   r$   r   r(   r   r(   r   r(   r   r*   r   r(   r   rG  r   Optional[int]r   rG  r   rG  r   r*   r9   r#   )rU   r$   r   r$   r   r(   r   r(   r   r(   r0  zOptional[list]r   r*   r   r(   r   rG  r   rJ  r   rG  r1  rJ  r   r*   r   rG  r9   r!  )r9   r.   )I__doc__
__future__r   r8  r   rM   r   r~   dataclassesr   r   r   r   typingr   !dispatch.anu_result_pickup_runnerr	   'dispatch.anu_owned_callback_enforcementr
   r   r   r?   rP   r5  r   r   r   r6  r9  rc   r  r  r  r  r  r   r  r4  r  r  r  r:  r  rv   rt   r\   r@   r   r  r  r  r  r  r#   rZ   r]   re   rk   rp   rx   r   r   r   r   r   r   r   r/  rC  rE  __all__r;   
SystemExitrA   r8   r6   <module>rS     s   #   	   ! 2 2  :  7  2 0 9 "	: 
 
   " # ! # ' + ) - 5      yq!" " ' # ! ' 
 
 
< !/ t ( , PD P '5 d 6F3*	0
8 =A , #$,*
* 	*
 * * *b 

!%$(#' #'"&'+*.(,+}
} } } } "} !} } !}  }  %!}" (#}& &'}( )}, -}D c 

!%$(#' #'"&'+*.#(,1c
c c c "c !c c c !c   !c" %#c$ (%c( )c* +c, &-c2 
3cN
&P z
TV
 r8   