
    a
ib                       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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mZmZ er	  ej        d
          ZdZdZdZe G d d                      Ze G d d                      Z G d de
          Z  G d de           Z! G d de           Z" e!            Z# e"            Z$eeedd(d!Z%eeedd(d"Z&d)d#Z' G d$ d%e!          Z( G d& d'e"          Z)dS )*zPrompt caching module for LangSmith SDK.

This module provides thread-safe LRU caches with background refresh
for prompt caching. Includes both sync and async implementations.
    )annotationsN)ABC)OrderedDict)	Awaitable)	dataclass)Path)TYPE_CHECKINGAnyCallableOptionalUnionzlangsmith.cachei,  d   <   c                  >    e Zd ZU dZded<   ded<   dZded<   ddZdS )
CacheEntryz4A single cache entry with metadata for TTL tracking.r
   valuefloat
created_atNzOptional[Callable[[], Any]]refresh_functtl_secondsOptional[float]returnboolc                H    |dS t          j                     | j        z
  |k    S )z/Check if entry is past its TTL (needs refresh).NF)timer   )selfr   s     C:\Users\Dell Inspiron 16\Desktop\tws\AgrotaPowerBi\back-agrota-powerbi\mcp-client-agrota\venv\Lib\site-packages\langsmith/prompt_cache.pyis_stalezCacheEntry.is_stale)   s%    5	do-<<    )r   r   r   r   )__name__
__module____qualname____doc____annotations__r   r    r   r   r   r   !   sV         >>JJJ04L4444= = = = = =r   r   c                  |    e Zd ZU dZdZded<   dZded<   dZded<   dZded<   e	dd	            Z
e	dd            ZdS )CacheMetricszCache performance metrics.r   inthitsmisses	refreshesrefresh_errorsr   c                     | j         | j        z   S )z%Total cache requests (hits + misses).)r)   r*   r   s    r   total_requestszCacheMetrics.total_requests9   s     y4;&&r   r   c                4    | j         }|dk    r
| j        |z  ndS )zCache hit rate (0.0 to 1.0).r   g        )r/   r)   )r   totals     r   hit_ratezCacheMetrics.hit_rate>   s&     #$)AIIty5  36r   N)r   r(   )r   r   )r    r!   r"   r#   r)   r$   r*   r+   r,   propertyr/   r2   r%   r   r   r'   r'   0   s         $$DMMMMFOOOOIN' ' ' X' 7 7 7 X7 7 7r   r'   c                      e Zd ZdZg dZeeefd"dZe	d#d            Z
d$dZd%dZd&dZd'dZd$dZd(dZd)dZd*dZd"d Zd!S )+_BasePromptCachezBase class for prompt caches with shared LRU logic.

    Provides thread-safe in-memory LRU cache operations.
    Subclasses implement the background refresh mechanism.
    )_cache_lock	_max_size_ttl_seconds_refresh_interval_metricsmax_sizer(   r   r   refresh_interval_secondsr   r   Nonec                    t                      | _        t          j                    | _        t                      | _        |                     |||           dS )ag  Initialize the base cache.

        Args:
            max_size: Maximum entries in cache (LRU eviction when exceeded).
            ttl_seconds: Time before entry is considered stale. Set to None for
                infinite TTL (entries never expire, no background refresh).
            refresh_interval_seconds: How often to check for stale entries.
        r<   r   r=   N)r   r6   	threadingRLockr7   r'   r;   
_configurer   r<   r   r=   s       r   __init__z_BasePromptCache.__init__U   sX     5@MM_&&
$#%= 	 	
 	
 	
 	
 	
r   r'   c                    | j         S )zGet cache performance metrics.)r;   r.   s    r   metricsz_BasePromptCache.metricsl   s     }r   c                ,    t                      | _        dS )zReset all metrics to zero.N)r'   r;   r.   s    r   reset_metricsz_BasePromptCache.reset_metricsq   s    $r   keystrr   Callable[[], Any]Optional[Any]c                J   | j         dk    rdS | j        5  || j        vr#| j        xj        dz  c_        	 ddd           dS | j        |         }||_        | j                            |           | j        xj        dz  c_        |j        cddd           S # 1 swxY w Y   dS )a]  Get a value from cache.

        Args:
            key: The cache key (prompt identifier like "owner/name:hash").
            refresh_func: Function to refresh this cache entry when stale.

        Returns:
            The cached value or None if not found.
            Stale entries are still returned (background refresh handles updates).
        r   N   )	r8   r7   r6   r;   r*   r   move_to_endr)   r   )r   rJ   r   entrys       r   getz_BasePromptCache.getu   s    >Q4Z 	 	$+%%$$)$$	 	 	 	 	 	 	 	
 K$E ".E K##C(((M!#;	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	s    BA	BBBr   r
   c                   | j         dk    rdS | j        5  t          j                    }t          |||          }|| j        vrut          | j                  | j         k    rXt          t          | j                            }| j                            |           t          
                    d|            || j        |<   | j                            |           ddd           dS # 1 swxY w Y   dS )Set a value in the cache.

        Args:
            key: The cache key (prompt identifier).
            value: The value to cache.
            refresh_func: Function to refresh this cache entry when stale.
        r   N)r   r   r   zEvicted oldest cache entry: )r8   r7   r   r   r6   lennextiterpoploggerdebugrP   )r   rJ   r   r   nowrQ   
oldest_keys          r   _setz_BasePromptCache._set   s.    >QFZ 	) 	))++CUsVVVE $+%%#dk*:*:dn*L*L!$t{"3"344

+++HJHHIII$DKK##C(((	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	)s   CC**C.1C.c                |    | j         5  | j                            |d           ddd           dS # 1 swxY w Y   dS )ziRemove a specific entry from cache.

        Args:
            key: The cache key to invalidate.
        N)r7   r6   rX   )r   rJ   s     r   
invalidatez_BasePromptCache.invalidate   s     Z 	' 	'KOOC&&&	' 	' 	' 	' 	' 	' 	' 	' 	' 	' 	' 	' 	' 	' 	' 	' 	' 	's   155c                x    | j         5  | j                                         ddd           dS # 1 swxY w Y   dS )z$Clear all cache entries from memory.N)r7   r6   clearr.   s    r   ra   z_BasePromptCache.clear   s    Z 	  	 K	  	  	  	  	  	  	  	  	  	  	  	  	  	  	  	  	  	 s   /33list[tuple[str, CacheEntry]]c                      j         5   fd j                                        D             cddd           S # 1 swxY w Y   dS )z.Get list of stale cache entries (thread-safe).c                R    g | ]#\  }}|                     j                  ||f$S r%   )r   r9   ).0rJ   rQ   r   s      r   
<listcomp>z7_BasePromptCache._get_stale_entries.<locals>.<listcomp>   sF       C>>$"344e  r   N)r7   r6   itemsr.   s   `r   _get_stale_entriesz#_BasePromptCache._get_stale_entries   s    Z 	 	   "&+"3"3"5"5  	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	s   %;??pathUnion[str, Path]c                x   ddl m} t          |          }|j                            dd           | j        5  i }| j                                        D ]v\  }}t          |j	        |j
                  rKt          |j	        d          r|j	                            d          }n!|j	                                        }n|j	        }|||<   wd|i}d	d	d	           n# 1 swxY w Y   |                    d
          }	 t          |d          5 }	t!          j        ||	d           d	d	d	           n# 1 swxY w Y   |                    |           t&                              dt+          |           d|            d	S # t,          $ r/}
|                                r|                                 |
d	}
~
ww xY w)z{Dump cache contents to a JSON file for offline use.

        Args:
            path: Path to the output JSON file.
        r   schemasT)parentsexist_ok
model_dumpjson)modeentriesNz.tmpw   )indentzDumped z cache entries to )	langsmithrm   r   parentmkdirr7   r6   rg   
isinstancer   PromptCommithasattrrp   dictwith_suffixopenrq   dumpreplacerY   rZ   rU   	Exceptionexistsunlink)r   ri   
ls_schemasrs   rJ   rQ   
value_datadata	temp_pathfes              r   r   z_BasePromptCache.dump   sW    	433333Dzz$666Z 	( 	(G"k//11 * *
Uek:+BCC -u{L99 8%*[%;%;%;%H%H

%*[%5%5%7%7

 "'J)w'D!	( 	( 	( 	( 	( 	( 	( 	( 	( 	( 	( 	( 	( 	( 	(& $$V,,			i%% -	$!,,,,- - - - - - - - - - - - - - -d###LLI3w<<II4IIJJJJJ 	 	 	!! #  """G		sO   BCC #C =F  D1%F  1D55F  8D59AF   
F9
*F44F9c           	     \   ddl m} t          |          }|                                st                              d|            dS 	 t          |          5 }t          j        |          }ddd           n# 1 swxY w Y   nD# t          j	        t          f$ r+}t                              d| d|            Y d}~dS d}~ww xY w|                    di           }d}t          j                    }| j        5  |                                D ]\  }	}
t!          | j                  | j        k    rt                              d|             n	 t'          |j        d	          r|j                            |
          }n|j                            |
          }t/          ||
          }|| j        |	<   |dz  }# t0          $ r*}t                              d|	 d|            Y d}~d}~ww xY wddd           n# 1 swxY w Y   t                              d| d|            |S )a%  Load cache contents from a JSON file.

        Args:
            path: Path to the JSON file to load.

        Returns:
            Number of entries loaded.

        Loaded entries get a fresh TTL starting from load time.
        If the file doesn't exist or is corrupted, returns 0.
        r   rl   zCache file not found: NzFailed to load cache file : rs   z)Reached max cache size, stopping load at model_validate)r   r   rO   zFailed to load cache entry zLoaded z cache entries from )rw   rm   r   r   rY   rZ   r   rq   loadJSONDecodeErrorOSErrorwarningrR   r   r7   rg   rU   r6   r8   r|   r{   r   	parse_objr   r   )r   ri   r   r   r   r   rs   loadedr[   rJ   r   r   rQ   s                r   r   z_BasePromptCache.load   s    	433333Dzz{{}} 	LL8$889991	d $qy||$ $ $ $ $ $ $ $ $ $ $ $ $ $ $$g. 	 	 	NNCCCCCDDD11111	 ((9b))ikkZ 	 	#*==??  Zt{##t~55LL!UV!U!UVVVEz68HII N * 7 F Fz R R * 7 A A* M M 'UsCCCE',DK$aKFF    NN#K#K#K#K#KLLLHHHH#	 	 	 	 	 	 	 	 	 	 	 	 	 	 	* 	AvAA4AABBBsz   
B A:.B :A>>B A>B C CC<AHA*F>=H>
G2 G-(H-G22HHHc                0    || _         || _        || _        d S )N)r8   r9   r:   rD   s       r   rC   z_BasePromptCache._configure&  s!     "'!9r   Nr<   r(   r   r   r=   r   r   r>   )r   r'   r   r>   )rJ   rK   r   rL   r   rM   rJ   rK   r   r
   r   rL   r   r>   )rJ   rK   r   r>   )r   rb   )ri   rj   r   r>   )ri   rj   r   r(   )r    r!   r"   r#   	__slots__DEFAULT_PROMPT_CACHE_MAX_SIZE DEFAULT_PROMPT_CACHE_TTL_SECONDS-DEFAULT_PROMPT_CACHE_REFRESH_INTERVAL_SECONDSrE   r3   rG   rI   rR   r]   r_   ra   rh   r   r   rC   r%   r   r   r5   r5   E   s)          I 6'G*W	
 
 
 
 
.    X' ' ' '   >) ) ) )4' ' ' '       
   ( ( ( (T5 5 5 5n: : : : : :r   r5   c                  x     e Zd ZdZddgZeeedd fdZddZ	ddZ
ddZddZddZddZeeedddZ xZS )PromptCachea  Thread-safe LRU cache with background thread refresh.

    For use with the synchronous Client.

    Features:
    - In-memory LRU cache with configurable max size
    - Background thread for refreshing stale entries
    - Stale-while-revalidate: returns stale data while refresh happens
    - Thread-safe for concurrent access

    Example:
        >>> def fetch_prompt(key: str) -> PromptCommit:
        ...     return client._fetch_prompt_from_api(key)
        >>> cache = PromptCache(
        ...     max_size=100,
        ...     ttl_seconds=3600,
        ...     fetch_func=fetch_prompt,
        ... )
        >>> cache.set("my-prompt:latest", prompt_commit)
        >>> cached = cache.get("my-prompt:latest")
        >>> cache.shutdown()
    _shutdown_event_refresh_threadr@   r<   r(   r   r   r=   r   r   r>   c                   t                                          |||           t          j                    | _        d| _        dS )a  Initialize the sync prompt cache.

        Args:
            max_size: Maximum entries in cache (LRU eviction when exceeded).
            ttl_seconds: Time before entry is considered stale. Set to None for
                infinite TTL (offline mode - entries never expire).
                Default: 300 (5 minutes).
            refresh_interval_seconds: How often to check for stale entries.
        r@   N)superrE   rA   Eventr   r   r   r<   r   r=   	__class__s       r   rE   zPromptCache.__init__K  sO      	#%= 	 	
 	
 	

  )00;?r   rJ   rK   r   r
   r   rL   c                j    | j         |                                  |                     |||           dS )rT   N)r   _start_refresh_threadr]   r   rJ   r   r   s       r   setzPromptCache.sete  s;     '&&(((		#ul+++++r   c                .    |                                   dS )gStop background refresh thread.

        Should be called when the client is being cleaned up.
        N)shutdownr.   s    r   stopzPromptCache.stopr  s    
 	r   c                    | j         | j                                          | j        $| j                            d           d| _        dS dS )r   Ng      @)timeout)r   r   r   joinr.   s    r   r   zPromptCache.shutdowny  s[    
 + $$&&&+ %%c%222#'D    ,+r   c                    | j         o| j                                         t          j        | j        dd          | _        | j                                         t          	                    d           dS dS )z5Start background thread for refreshing stale entries.NTzPromptCache-refresh)targetdaemonnamezStarted cache refresh thread)
r9   r   ra   rA   Thread_refresh_loopr   startrY   rZ   r.   s    r   r   z!PromptCache._start_refresh_thread  s~    ( &&(((#,#3)*$ $ $D 
  &&(((LL788888 )(r   c                   | j                             | j                  sk	 |                                  n4# t          $ r'}t
                              d|            Y d}~nd}~ww xY w| j                             | j                  idS dS )z)Background loop to refresh stale entries.z(Unexpected error in cache refresh loop: N)r   waitr:   _refresh_stale_entriesr   rY   	exceptionr   r   s     r   r   zPromptCache._refresh_loop  s    &++D,BCC 	QQ++---- Q Q Q  !OA!O!OPPPPPPPPQ &++D,BCC 	Q 	Q 	Q 	Q 	Qs   6 
A' A""A'c                B   |                                  }|sdS t                              dt          |           d           |D ]\  }}| j                                        r dS |j        	 |                                }|                     |||j                   | j        xj	        dz  c_	        t                              d|            # t          $ r?}| j        xj        dz  c_        t                              d| d|            Y d}~d}~ww xY wdS )z)Check for stale entries and refresh them.NzRefreshing  stale cache entriesrO   zRefreshed cache entry: zFailed to refresh cache entry r   )rh   rY   rZ   rU   r   is_setr   r   r;   r+   r   r,   r   r   stale_entriesrJ   rQ   	new_valuer   s         r   r   z"PromptCache._refresh_stale_entries  sb   //11 	FK3}#5#5KKKLLL' 	P 	PJC#**,, !-P % 2 2 4 4IHHS)U-?@@@M++q0++LL!@3!@!@AAAA  P P PM00A500NN#NC#N#N1#N#NOOOOOOOOP .	P 	Ps   /A"C
D5DDc               ^    |                                   |                     |||           dS )  Reconfigure the cache parameters.

        Args:
            max_size: Maximum entries in cache (LRU eviction when exceeded).
            ttl_seconds: Time before entry is considered stale.
            refresh_interval_seconds: How often to check for stale entries.
        r@   Nr   rC   rD   s       r   	configurezPromptCache.configure  s?     			#%= 	 	
 	
 	
 	
 	
r   r   r   r   )r    r!   r"   r#   r   r   r   r   rE   r   r   r   r   r   r   r   __classcell__r   s   @r   r   r   1  s        . #$56I
 6'G*W@ @ @ @ @ @ @ @4, , , ,   	( 	( 	( 	(
9 
9 
9 
9Q Q Q QP P P P4 6'G*W
 
 
 
 
 
 
 
 
 
r   r   c                  v     e Zd ZdZdgZeeedd fdZddZ	ddZ
ddZddZddZddZeeedddZ xZS )AsyncPromptCachea  Thread-safe LRU cache with asyncio task refresh.

    For use with the asynchronous AsyncClient.

    Features:
    - In-memory LRU cache with configurable max size
    - Asyncio task for refreshing stale entries
    - Stale-while-revalidate: returns stale data while refresh happens
    - Thread-safe for concurrent access

    Example:
        >>> async def fetch_prompt(key: str) -> PromptCommit:
        ...     return await client._afetch_prompt_from_api(key)
        >>> cache = AsyncPromptCache(
        ...     max_size=100,
        ...     ttl_seconds=3600,
        ...     fetch_func=fetch_prompt,
        ... )
        >>> await cache.start()
        >>> cache.set("my-prompt:latest", prompt_commit)
        >>> cached = cache.get("my-prompt:latest")
        >>> await cache.stop()
    _refresh_taskr@   r<   r(   r   r   r=   r   r   r>   c               ^    t                                          |||           d| _        dS )ag  Initialize the async prompt cache.

        Args:
            max_size: Maximum entries in cache (LRU eviction when exceeded).
            ttl_seconds: Time before entry is considered stale. Set to None for
                infinite TTL (offline mode - entries never expire).
            refresh_interval_seconds: How often to check for stale entries.
        r@   N)r   rE   r   r   s       r   rE   zAsyncPromptCache.__init__  s@     	#%= 	 	
 	
 	

 <@r   rJ   rK   r   r
   r   Callable[[], Awaitable[Any]]c                z   K   | j         |                                  d{V  |                     |||           dS )zSet a value in the cache.

        Args:
            key: The cache key (prompt identifier).
            value: The value to cache.
            refresh_func: Async function to refresh this cache entry when stale.
        N)r   r   r]   r   s       r   asetzAsyncPromptCache.aset  sM       %**,,		#ul+++++r   c                   K   | j         dS | j        dS t          j        |                                 d          | _        t
                              d           dS )zStart async background refresh loop.

        Must be called from an async context. Creates an asyncio task that
        periodically checks for stale entries and refreshes them.
        Does nothing if ttl_seconds is None (infinite TTL mode).
        NzAsyncPromptCache-refresh)r   z Started async cache refresh task)r9   r   asynciocreate_taskr   rY   rZ   r.   s    r   r   zAsyncPromptCache.start  sk       $F)F$0  +
 
 
 	788888r   c                X    | j         "| j                                          d| _         dS dS )zStop background refresh task.

        Synchronous wrapper that cancels the refresh task.
        For proper cleanup in async context, use stop() instead.
        N)r   cancelr.   s    r   r   zAsyncPromptCache.shutdown  s8     )%%'''!%D *)r   c                   K   | j         dS | j                                          	 | j          d{V  n# t          j        $ r Y nw xY wd| _         t                              d           dS )zlStop async background refresh loop.

        Cancels the refresh task and waits for it to complete.
        Nz Stopped async cache refresh task)r   r   r   CancelledErrorrY   rZ   r.   s    r   r   zAsyncPromptCache.stop$  s      
 %F!!###	$$$$$$$$$% 	 	 	D	!788888s   4 AAc                  K   	 	 t          j        | j                   d{V  |                                  d{V  nC# t           j        $ r  t
          $ r'}t                              d|            Y d}~nd}~ww xY w)z/Async background loop to refresh stale entries.TNz.Unexpected error in async cache refresh loop: )r   sleepr:   r   r   r   rY   r   r   s     r   r   zAsyncPromptCache._refresh_loop4  s      	WWmD$:;;;;;;;;;113333333333)    W W W  !URS!U!UVVVVVVVVW	Ws   9? A?A::A?c                &  K   |                                  }|sdS t                              dt          |           d           |D ]\  }}|j        	 |                                 d{V }|                     |||j                   d{V  | j        xj        dz  c_        t                              d|            |# t          $ r?}| j        xj	        dz  c_	        t          
                    d| d|            Y d}~d}~ww xY wdS )z8Check for stale entries and refresh them asynchronously.NzAsync refreshing r   rO   zAsync refreshed cache entry: z$Failed to async refresh cache entry r   )rh   rY   rZ   rU   r   r   r;   r+   r   r,   r   r   s         r   r   z'AsyncPromptCache._refresh_stale_entries@  sm     //11 	FQ]););QQQRRR' 
	V 
	VJC!-V&+&8&8&:&: : : : : : :I))CE4FGGGGGGGGGM++q0++LL!F!F!FGGGG  V V VM00A500NN#T##T#TQR#T#TUUUUUUUUV .
	V 
	Vs   A.C
D5DDc               l   K   |                                   d{V  |                     |||           dS )r   Nr   rD   s       r   r   zAsyncPromptCache.configureU  sE       iikk+/GHHHHHr   r   )rJ   rK   r   r
   r   r   r   r>   r   )r    r!   r"   r#   r   r   r   r   rE   r   r   r   r   r   r   r   r   r   s   @r   r   r     s!        0 !!I
 6'G*W@ @ @ @ @ @ @ @,, , , ,9 9 9 9(& & & &9 9 9 9 
W 
W 
W 
WV V V V0 6'G*WI I I I I I I I I Ir   r   r@   r<   r(   r   r   r=   r   r   r>   c                @    t                               | ||           dS a  Configure the global prompt cache.

    This should be called before any cache instances are created or used.

    Args:
        max_size: Maximum entries in cache (LRU eviction when exceeded).
        ttl_seconds: Time before entry is considered stale.
        refresh_interval_seconds: How often to check for stale entries.

    Example:
        >>> from langsmith import configure_global_prompt_cache
        >>> configure_global_prompt_cache(max_size=200, ttl_seconds=7200)
    r@   N)prompt_cache_singletonr   r@   s      r   configure_global_prompt_cacher   l  s4    & $$!9 %     r   c                P   K   t                               | ||           d{V  dS r   )async_prompt_cache_singletonr   r@   s      r   #configure_global_async_prompt_cacher     sV      & '
0
0!9 1           r   c                 >    t          j        dt          d           d S )NzcThe 'Cache' class is deprecated and will be removed in a future version. Use 'PromptCache' instead.   )
stacklevel)warningswarnDeprecationWarningr%   r   r   _deprecated_cache_class_warningr     s0    M	%	     r   c                  .     e Zd ZdZeeedd fdZ xZS )Cachez:Deprecated alias for PromptCache. Use PromptCache instead.r@   r<   r(   r   r   r=   r   r   r>   c               l    t                       t                                          |||           dS )a  Initialize the deprecated Cache class.

        Args:
            max_size: Maximum entries in cache (LRU eviction when exceeded).
            ttl_seconds: Time before entry is considered stale.
            refresh_interval_seconds: How often to check for stale entries.
        r@   Nr   r   rE   r   s       r   rE   zCache.__init__  E     	()))#%= 	 	
 	
 	
 	
 	
r   r   	r    r!   r"   r#   r   r   r   rE   r   r   s   @r   r   r     sX        DD
 6'G*W
 
 
 
 
 
 
 
 
 
 
 
r   r   c                  .     e Zd ZdZeeedd fdZ xZS )
AsyncCachezDDeprecated alias for AsyncPromptCache. Use AsyncPromptCache instead.r@   r<   r(   r   r   r=   r   r   r>   c               l    t                       t                                          |||           dS )a  Initialize the deprecated AsyncCache class.

        Args:
            max_size: Maximum entries in cache (LRU eviction when exceeded).
            ttl_seconds: Time before entry is considered stale.
            refresh_interval_seconds: How often to check for stale entries.
        r@   Nr   r   s       r   rE   zAsyncCache.__init__  r   r   r   r   r   s   @r   r   r     sX        NN
 6'G*W
 
 
 
 
 
 
 
 
 
 
 
r   r   r   r   )*r#   
__future__r   r   rq   loggingrA   r   r   abcr   collectionsr   collections.abcr   dataclassesr   pathlibr   typingr	   r
   r   r   r   	getLoggerrY   r   r   r   r   r'   r5   r   r   r   r   r   r   r   r   r   r%   r   r   <module>r      s    # " " " " "                # # # # # # % % % % % % ! ! ! ! ! !       @ @ @ @ @ @ @ @ @ @ @ @ @ @ 			,	-	- $*   # 02 - = = = = = = = = 7 7 7 7 7 7 7 7(i: i: i: i: i:s i: i: i:XR
 R
 R
 R
 R
" R
 R
 R
j^I ^I ^I ^I ^I' ^I ^I ^ID % //11 
 2#C&S	     8 2#C&S	     :   
 
 
 
 
K 
 
 
2
 
 
 
 
! 
 
 
 
 
r   