
    )`ik                        d 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ZddlZddl	m	Z	 ddl
mZ ddlmZmZmZmZmZ ddlZddlZddlZdedefdZ eej                            d	d
                    Z eej                            dd                    Zej                            dd          Z eej                            dd                    Z eej                            dd                    Zej                            dd          Z ej                            dd          Z!d e "                    d          D             Z#d e!"                    d          D             Z$ej                            dd
          dk    Z%da&da'i Z(dZ) ej*        d          Z+d Z, e,             defdZ-d Z.d ede/fd!Z0d"ed#eeef         ddfd$Z1d"edeeeef                  fd%Z2d&ej3        defd'Z4d(edefd)Z5d*e6d+e7deeeej3        f         eeef         f         fd,Z8	 dMd-ed ed*e6d+e7d.ee         dee         fd/Z9d0ed1eddfd2Z:d(edefd3Z;d4ed5edee         fd6Z<	 dNd8ed9ed:ed;ede/f
d<Z=	 	 	 	 dOd0ed>e/d?ed@e/dAeeeeef         ef                  defdBZ>dPdCed?ede?fdDZ@dE ZA eA              e.             dQd(edFedGedefdHZBd-ed*e6d+e7de7fdIZCd-ed ed*e6d+e7dFeddfdJZDd ed1edFeddfdKZEdMd-edefdLZFdS )Ra3  
Copyright (c) 2025 by FlashInfer team.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
    N)datetime)Path)AnyCallableDictTupleOptionalpathreturnc                 v    d| v r4|                      dt          t          j                                        S | S )z
    Replace %i with the current process ID in a path.

    This is useful for multi-process/multi-GPU environments where each process
    needs its own log file.
    z%i)replacestrosgetpid)r
   s    j/home/jaya/work/projects/VOICE-AGENT/VIET/agent-env/lib/python3.11/site-packages/flashinfer/api_logging.py_substitute_process_idr   "   s4     t||||D#bikk"2"2333K    FLASHINFER_LOGLEVEL0FLASHINFER_LOGDESTstdoutFLASHINFER_DUMP_DIRflashinfer_dumpsFLASHINFER_DUMP_MAX_SIZE_GB20FLASHINFER_DUMP_MAX_COUNT1000FLASHINFER_DUMP_INCLUDE FLASHINFER_DUMP_EXCLUDEc                 ^    g | ]*}|                                 |                                 +S  strip.0ps     r   
<listcomp>r(   ;   -    SSSS!''))SSSr   ,c                 ^    g | ]*}|                                 |                                 +S r"   r#   r%   s     r   r(   r(   <   r)   r   FLASHINFER_DUMP_SAFETENSORS1Fzflashinfer.apic                     t           dk    rTt                              t          j                               t                              t          j        dz              dS t                              t          j                   t          j        	                                 t          dk    rt          j        t          j                  } nEt          dk    rt          j        t          j                  } nt          j        t          d          } t          j        d          }|                     |           t                              |            d	t          _        dS )
z1Set up the logger based on environment variables.r      Nr   stderra)modez%(message)sF)_API_LOG_LEVEL_logger
addHandlerloggingNullHandlersetLevelCRITICALDEBUGhandlersclear_API_LOG_DESTStreamHandlersysr   r0   FileHandler	FormattersetFormatter	propagate)handler	formatters     r   _setup_loggerrF   K   s   7.00111)A-... W]###    '
33	(	"	"'
33%m#>>> !-00I###wGr   c                  N    t          j                                        d          S )z:Get current timestamp in the format [YYYY-MM-DD HH:MM:SS].z[%Y-%m-%d %H:%M:%S])r   nowstrftimer"   r   r   _get_timestamprJ   m   s    <>>""#8999r   c                  L   t           dk    rt          d           t          d           t          dt                      t          rt          d           t          rt          dt                      t
          rt          dt
                      t          d           dS dS )	z;Warn users about security implications of Level 10 logging.
   P================================================================================a  WARNING: FlashInfer API Logging is set to Level 10 (Tensor Dumping).
This will dump ALL input and outputs including tensors for FlashInfer APIs to disk in
the configured dump directory. Ensure that you are NOT processing sensitive data
or that the dump directory is secure. To disable dumping, unset FLASHINFER_LOGLEVEL or
set it to below 10. For more information, see https://docs.flashinfer.ai/logging.htmlzCurrent dump directory is: u   ⚠️  SAFETENSORS mode enabled: tensor stride/non-contiguity will NOT be preserved.
    Tensors will be saved as contiguous. Use torch.save (default) to preserve strides.zInclude filter: zExclude filter: N)r3   print	_DUMP_DIR_DUMP_SAFETENSORS_DUMP_INCLUDE_PATTERNS_DUMP_EXCLUDE_PATTERNSr"   r   r   
_warn_dumprS   r   s    hd	
 	
 	
 	7I77888 	i   " 	?=%;==>>>! 	?=%;==>>>h' r   	func_namec                      t           r"t           fdt           D                       sdS t          r"t           fdt          D                       rdS dS )aN  
    Check if a function should be dumped based on include/exclude filters.

    Uses fnmatch-style patterns (wildcards: * for any chars, ? for single char).
    Matching is case-sensitive.

    Parameters
    ----------
    func_name : str
        The function name to check. For class methods, this is formatted as
        "ClassName.method_name" (e.g., "BatchDecodeWrapper.run").

    Returns
    -------
    bool
        True if the function should be dumped, False otherwise.

    Filter Logic
    ------------
    1. If FLASHINFER_DUMP_INCLUDE is set:
       - Function must match at least one include pattern
       - If it doesn't match any, return False (skip dump)
    2. If FLASHINFER_DUMP_EXCLUDE is set:
       - If function matches any exclude pattern, return False (skip dump)
    3. Otherwise, return True (dump the function)
    c              3   B   K   | ]}t          j         |          V  d S Nfnmatchr&   patrT   s     r   	<genexpr>z(_should_dump_function.<locals>.<genexpr>   s/      UUs7?9c22UUUUUUr   Fc              3   B   K   | ]}t          j         |          V  d S rW   rX   rZ   s     r   r\   z(_should_dump_function.<locals>.<genexpr>   s/      QQ3wy#..QQQQQQr   T)rQ   anyrR   )rT   s   `r   _should_dump_functionr_      sr    8  UUUU>TUUUUU 	5  QQQQ:PQQQQQ 	54r   filepathrecordc                     t          | d          5 }|                    t          j        |          dz              ddd           dS # 1 swxY w Y   dS )z
    Append a JSON record as a single line to a JSONL file.

    Parameters
    ----------
    filepath : Path
        Path to the JSONL file
    record : Dict[str, Any]
        Record to append (will be serialized as single-line JSON)
    r1   
N)openwritejsondumps)r`   ra   fs      r   _append_to_jsonlri      s     
h		 +	
6""T)***+ + + + + + + + + + + + + + + + + +s   +A		AAc                     |                                  sdS d}t          | d          5 }|D ]}|                                }|r|}	 ddd           n# 1 swxY w Y   |rt          j        |          S dS )am  
    Read the last record from a JSONL file.

    For metadata.jsonl, this returns the most complete state (completed if available,
    otherwise inputs_saved).

    Parameters
    ----------
    filepath : Path
        Path to the JSONL file

    Returns
    -------
    Optional[Dict[str, Any]]
        The last record, or None if file is empty/doesn't exist
    Nr)existsrd   r$   rf   loads)r`   	last_linerh   lines       r   _read_jsonl_last_recordrp      s    " ?? tI	h		 ! 	! 	!D::<<D ! 		!! ! ! ! ! ! ! ! ! ! ! ! ! ! !  %z)$$$4s   AAAtensorc                 T    |                                  |                                 z  S )z(Calculate the size of a tensor in bytes.)element_sizenelement)rq   s    r   _get_tensor_size_bytesru      s#      6??#4#444r   valuec           
         	 t          | t          j                  rdt          |           dS t          | t          j                  r'dt          |           j         d| j         | j	        dS t          | t          t          t          t          t          d          f          r| S t          | t          t          t          f          r,t          |           j        t          |           dd         dS t          |           j        t          |           dd         dS # t           $ r t          |           j        d	dcY S w xY w)
z
    Convert a non-tensor value to a JSON-serializable format for metadata.

    This function is intended for serializing non-tensor arguments/values
    that are used in API input or output metadata. Tensor arguments are not handled here.
    torch.dtype)typerv   enum.)ry   namerv   Ni  )ry   reprz<not serializable>)
isinstancetorchdtyper   rz   Enumry   __name__r|   rv   intfloatboollisttupledict	Exception)rv   s    r   _serialize_valuer      se   
eU[)) 	 &U   ty)) 	;;/>>%*>>  
 UCtDzzBCC 	LeT233 		U,UETE*   U,E

5D5)    
 
 
KK((
 
 	
 	
 	

s*   +D! A D! /7D! 'AD! 5+D! !!EEargskwargsc                    i }i }t          |           D ]N\  }}d| }t          |t          j                  r|                                ||<   <t          |          ||<   O|                                D ]N\  }}d| }t          |t          j                  r|                                ||<   <t          |          ||<   O||fS )a  
    Extract tensors and non-tensor metadata from function arguments.

    Tensors are moved to CPU but preserve their stride/contiguity information.

    Returns
    -------
    tensors : Dict[str, torch.Tensor]
        Dictionary of tensor arguments with keys like "arg_0", "kwarg_name"
        All tensors are on CPU with original stride preserved.
    metadata : Dict[str, Any]
        Dictionary of non-tensor arguments (serializable to JSON)
    arg_kwarg_)	enumerater~   r   Tensorcpur   items)	r   r   tensorsmetadataiargkeyrv   	kwarg_keys	            r   _extract_tensors_and_metadatar     s      GH D// 2 23Qjjc5<(( 	27799GCLL,S11HSMM llnn : :
U"SNN	eU\** 	:!&GI"25"9"9HYHr   funcself_idc                 h	   t          |          s t                              d| d           dS t          t          k    r(t                              dt           d| d           dS 	 |t          vr
dt          |<   t          |xx         dz  cc<   t          |         }t          j                    	                    d	          dd
         }t          j                    }| d| d| d|d}t          t                    |z  }	|	                    dd           t          ||          \  }
}t!          d |
                                D                       }t$          dz  dz  dz  }t&          |z   |k    r<t                              dt$           d| d           |	                                 dS |
rt*          rk	 ddlm} d |
                                D             } ||t3          |	dz                       nA# t4          $ r t                              d            w xY wt9          j        |
|	dz             |t=          | d          r| j        nd||t          j                    |i tA          |
!                                          g ||dz  di t*          rdnd t=          tD          d!          r!t3          tE          j#        |                     nd"t8          j$        tJ          j&        d#d$d%}|||d&<   |
                                D ]j\  }}tA          |j'                  t3          |j(                  tA          |)                                          t3          |j*                  d'|d(         |<   k	 dd)l&m$} ||d*         d+<   n# tV          $ r d"|d*         d+<   Y nw xY wt3          |	          |d,<   tY          |	d-z  |           t          t                    d.z  }tY          ||           t          dz  at&          |z  at                              d/|	 d0|dz  d1d2t           d3t           d4	           t3          |	          S # tV          $ r[}t                              d5| d6|            ddl-}t                              |.                                           Y d}~dS d}~ww xY w)7a  
    Dump function inputs to disk BEFORE execution (crash-safe).

    This function:
    1. Extracts tensors and metadata from inputs
    2. Creates a timestamped directory
    3. Saves inputs.pt and partial metadata.json
    4. Tracks cumulative size and count limits

    Parameters
    ----------
    func : Callable
        The function being called
    func_name : str
        Name of the function
    args : tuple
        Positional arguments
    kwargs : dict
        Keyword arguments
    self_id : Optional[int]
        The id() of the 'self' object if this is a method call

    Returns
    -------
    Optional[str]
        Path to the dump directory, or None if dump was skipped
    zSkipping dump for z' (filtered by include/exclude patterns)NzDump limit reached (z dumps). Skipping dump for z/. Increase FLASHINFER_DUMP_MAX_COUNT if needed.r   r/   z%Y%m%d_%H%M%S_%f_pid__call04dT)parentsexist_okc              3   4   K   | ]}t          |          V  d S rW   ru   r&   ts     r   r\   z(_dump_function_inputs.<locals>.<genexpr>y  s+      SSq/22SSSSSSr   i   zDump size limit reached (z GB). Skipping dump for z1. Increase FLASHINFER_DUMP_MAX_SIZE_GB if needed.	save_filec                 >    i | ]\  }}||                                 S r"   
contiguousr&   kvs      r   
<dictcomp>z)_dump_function_inputs.<locals>.<dictcomp>  s3     * * *.2a1<<>>* * *r   inputs.safetensorszHsafetensors package not installed. Install with: pip install safetensors	inputs.pt
__module__z	<unknown>   )input_tensor_keysoutput_tensor_keysinput_size_bytesinput_size_mbsafetensorsr   	signaturez<unavailable>)r   pythoninputs_saved)function_namemodulecall_sequence	timestamp
process_idinput_metadataoutput_metadatatensor_infotensor_detailstensor_formatfunction_signatureversionsexecution_statusr   shaper   stridedevicer   __version__r   
flashinferdump_dirmetadata.jsonlsession.jsonlzDumped inputs to: z (size: .2fz MB, total: /z dumps)zFailed to dump function call : )/r_   r4   debug_dump_count_DUMP_MAX_COUNTwarning_dump_call_counterr   rH   rI   r   r   r   rO   mkdirr   sumvalues_DUMP_MAX_SIZE_GB_dump_total_size_bytesrmdirrP   safetensors.torchr   r   r   ImportErrorerrorr   savehasattrr   r   keysinspectr   r   r?   versionr   r   r   r   r   ri   	traceback
format_exc)r   rT   r   r   r   call_seqr   pid	dump_namer   input_tensorsr   
input_sizemax_size_bytesr   tensors_contiguousr   r   rq   flashinfer_versionsession_jsonl_pather   s                          r   _dump_function_inputsr   3  s   J !++ SSSS	
 	
 	
 to%%=? = =y = = =	
 	
 	
 tB...,-y)9%%%*%%%%i0 LNN++,>??RC
	 ikk JJcJJIJJHJJJ		??Y.td333 )FdF(S(S%~ SSM<P<P<R<RSSSSS
 +T1D84?!J.??OOC,= C CW` C C C  
 NN4  	B  B;;;;;;* *6C6I6I6K6K* * *& I0#hAU6U2V2VWWWW"   MM@    
=([*@AAA ')0|)D)DUdoo+%")++,!%)-*<*<*>*>%?%?&($.!+{!;	  !.?L]]Ww,,#!#g&7&=&=">">"> *+  !//$
 $
6 ")HY )..00 	 	KCfl++V\**v}}//fm,,	/ /H%&s++	ABBBBBB1CHZ .. 	A 	A 	A1@HZ ...	A  #8}} 	$44h??? ")__>+X666 	q*,= = = K0;= =!= =$3= = =	
 	
 	
 8}}   FiFF1FFGGGi**,,---ttttts_   )EQ 3	Q =A G> =Q >&H$$EQ ;N Q N%"Q $N%%B&Q 
R1AR,,R1r   resultc           	      Z   	 t          |           }|                                st                              d|             dS i }i }t	          |t
          j                  r|                                |d<   nt	          |t                    r`t          |          D ]O\  }}t	          |t
          j                  r|                                |d| <   :t          |          |d| <   Pnt          |          |d<   t          d |                                D                       }|r`t          rAddlm} d |                                D             }	 ||	t#          |d	z                       nt          j        ||d
z             |dz  }
t'          |
          }|||d<   t)          |                                          |d         d<   ||d         d<   |dz  |d         d<   |d         d         |z   |d         d<   |d         d         dz  |d         d<   d|d<   d|vri |d<   |                                D ]j\  }}t)          |j                  t#          |j                  t)          |                                          t#          |j                  d|d         |<   kt5          |
|           t          t6                    dz  }t5          ||           t8          |z  at                              d|  d|dz  dd|d         d         dd           dS t                              d|             dS # t<          $ r[}t                              d |  d!|            ddl}t                              |                                            Y d}~dS d}~ww xY w)"al  
    Add function outputs to an existing dump directory (crash-safe).

    This function is called AFTER successful execution to append outputs
    to the dump that was created before execution.

    Parameters
    ----------
    dump_dir : str
        Path to the dump directory created by _dump_function_inputs
    result : Any
        Function return value
    Dump directory not found: Nr   result_c              3   4   K   | ]}t          |          V  d S rW   r   r   s     r   r\   z)_dump_function_outputs.<locals>.<genexpr>  s+      UU033UUUUUUr   r   r   c                 >    i | ]\  }}||                                 S r"   r   r   s      r   r   z*_dump_function_outputs.<locals>.<dictcomp>  s3     & & &*.!QAq||~~& & &r   outputs.safetensors
outputs.ptr   r   r   r   output_size_bytesr   output_size_mbr   total_size_bytestotal_size_mb	completedr   r   r   r   zDumped outputs to: z (output size: r   z MB, total dump size: z MB)z%metadata.jsonl not found or empty in zFailed to dump outputs to r   )!r   rl   r4   r   r~   r   r   r   r   r   r   r   r   rP   r   r   r   r   r   rp   r   r   r   r   r   r   ri   rO   r   r   r   r   r   )r   r   	dump_pathoutput_tensorsr   r   itemoutput_sizer   r   metadata_jsonl_pathr   r   rq   r   r   r   s                    r   _dump_function_outputsr
    sv    X.NN	!! 	MMAxAABBBF fel++ 		A'-zz||N8$$&& 	A$V,, L L4dEL11 L48HHJJN=Q==115Ed5K5KOMaMM22	L )9(@(@OH% UU^=R=R=T=TUUUUU  	E  
E777777& &2@2F2F2H2H& & &" 	,c)>S2S.T.TUUUU 
>9|+CDDD (*::*+>??*9H&'<@ATATAVAV<W<WH]#$89;FH]#$788C{8SH]#$45'(:;kI ]#$67 8@7N"88H]#O4 ,7H'(  x//-/)*-3355  V!&,// .."6==??33!&-00	3 3)*3// 0(;;; "&i?!B/::: #k1"MMWh W W!,!<GW W$,]$;O$LVW W W     MML(LLMMMMM . . .B8BBqBBCCCi**,,---------	.s&   A M K M &M 
N*AN%%N*c                 L   t          | t                    r|                     d          }|dk    rq|                     dd          }|                    dd          }	 t	          t
          |          S # t          $ r" t                              d|            | cY S w xY w| S | S )z
    Reconstruct special types from metadata format.

    Handles:
    - torch.dtype objects
    - enum.Enum objects (future)
    - Other serialized types
    ry   rx   rv   r   ztorch.zCould not reconstruct dtype: )	r~   r   getr   getattrr   AttributeErrorr4   r   )rv   
value_type	dtype_str
dtype_names       r   _reconstruct_valuer  U  s     % YYv&&
&&		'2..I"**8R88Juj111!    K	 K KLLL
 Ls   A3 3)BBmodule_namer   c           	          	 t          j        |           }|                    d          }|}|D ]}t          ||          }t	          |          sdS |S # t
          $ r.}t                              d|  d| d|            Y d}~dS d}~ww xY w)z6Resolve a function from module name and function name.r{   NzCould not resolve function r   )	importlibimport_modulesplitr  callabler   r4   r   )r  r   r   partsobjpartr   s          r   _resolve_functionr  r  s    (55##C(( 	% 	%D#t$$CC}} 	4
   L+LLLLLL	
 	
 	
 ttttt	s   AA A 
B#BBMbP?actualexpectedrtolatolc                     t           t          j                  rt          t          j                  r j        j        k    r,t                              d j         dj                    dS  j        j        k    r,t                              d j         dj                    dS t          j                   sZ z
                                  	                                
                                }t                              d|            dS dS t           t          t          f          rt          t          t          f          rt                     t                    k    r<t                              dt                      dt                                dS t          fd	t           d
          D                       S t           t                     rt          t                     r                                                                 k    rFt                              d                                  d                                            dS t           fd D                       S  k    r"t                              d  d            dS dS )z&Recursively compare execution results.zShape mismatch: actual z vs expected FzDtype mismatch: actual )r   r!  zValue mismatch: max diff TzLength mismatch: actual c              3   B   K   | ]\  }}t          ||          V  d S rW   _compare_results)r&   r1   r   r!  r   s      r   r\   z#_compare_results.<locals>.<genexpr>  sI       
 
1 Q4..
 
 
 
 
 
r   )strictzKey mismatch: actual c              3   T   K   | ]"}t          |         |                   V  #d S rW   r$  )r&   r   r  r!  r  r   s     r   r\   z#_compare_results.<locals>.<genexpr>  s:      XXA#F1Ix{D$GGXXXXXXr   zValue mismatch: actual )r~   r   r   r   r4   r   r   allcloseabsmaxr  r   r   lenallzipr   r   )r  r  r   r!  diffs   ```` r   r%  r%    s   
 &%,'' /Jx,N,N /<8>))OOU&,UUX^UU   5<8>))OOU&,UUX^UU   5 ~fhTEEE 	X%**,,00227799DOO>>>???5t 
FT5M	*	* z(T5M/R/R v;;#h--''OOT3v;;TTS]]TT   5 
 
 
 
 
FHT:::
 
 
 
 
 	
 
FD	!	! j4&@&@ ;;==HMMOO++OOUUUHMMOOUU   5XXXXXXXQWXXXXXX XOOUfUU8UUVVV5tr   cudacompare_outputsr   runobject_registryc           	         t          |           }|                                st          d|            |dz  }|                                st          d|            t          |          }|t	          d|            |d         }|dz  }	|dz  }
|	                                r$t          j        t          |	          d	
          }nf|
                                r@	 ddlm	}  |t          |
          d	          }n0# t          $ r t          d          dw xY wt          d|            |                                D ]\  }}|                    |          ||<   g }i }|                    di           }d}|                                D ]O}|                    d          r8t!          |                    d          d                   }t%          ||          }P|                                D ]O}|                    d          r8t!          |                    d          d                   }t%          ||          }Pt'          |dz             D ]}d| }||v r|                    ||                    '||v r)|                    t+          ||                              Tt,                              d| d           |                    d           |                                D ]8}|                    d          r!|                    dd          }||         ||<   9|                                D ]I}|                    d          r2|                    dd          }||vrt+          ||                   ||<   Jt,                              d| d|             t,                              dt5          |           dt7          |                                                      |||d}i }i }|r|dz  }|dz  }|                                r$t          j        t          |          d	
          }nT|                                r@	 ddlm	}  |t          |          d	          }n# t          $ r t          d          dw xY w|                                D ]\  }}|                    |          ||<   |                    d i           }||d!<   ||d"<   |r|                    d#          }|                    d$          }|                    d%          }d}d}|||f} |                    d&          r|                    d'          d(         }!t;          ||!          }"|"rt=          |"          rt5          |          dk    r
|dd         ng }#	 t,                              d)|! d*| d+| d,            |"|#i |}|||| <   d}$|$|d-<   |rd.|d/<   |S # t>          $ r>}%t,                               d0|! d1|%            t          |%          |d2<   |cY d}%~%S d}%~%ww xY wn|| |v r||          }|                    d'          d         }&tC          ||&          r0tE          ||&          }t5          |          dk    r
|dd         ng }nBt,                              d3| d4|&            n!t,                              d5| d+| d6           |t;          ||          }|r	 t,                              d7| d'| d8            ||i |}$|$|d-<   |ri }'tG          |$t
          j$                  r|$|'d9<   nktG          |$tJ          t6          f          r8tM          |$          D ]'\  }}(tG          |(t
          j$                  r|(|'d:| <   (ntG          |$tN                    r|$}'d.})|rtQ          |'|          })|)|d/<   |)rt,                              d;           nt,                              d<           n# t>          $ ri}%t,                               d=|%            ddl)}*t,                               |**                                           t          |%          |d2<   Y d}%~%nEd}%~%ww xY wt,                              d>| d'|            n|st,                              d?           |S )@a  
    Replay a function call from a dumped directory.

    This function:
    1. Loads metadata.jsonl to get function info
    2. Loads inputs.pt to get input tensors
    3. Moves tensors to specified device (default: cuda)
    4. Reconstructs the function call
    5. Optionally executes the function (if run=True)
    6. Optionally compares with saved outputs

    Parameters
    ----------
    dump_dir : str
        Path to the dump directory
    compare_outputs : bool
        If True, load and compare with saved outputs
    device : str
        Target device for tensors. Options:
        - "cuda" (default): Load to cuda:0
        - "cpu": Load to CPU
        - "cuda:N": Load to specific CUDA device
    run : bool
        If True, try to resolve and execute the function
    object_registry : Optional[Dict[Tuple[int, int], Any]]
        Registry of stateful objects mapped by (process_id, self_id) tuple.
        This composite key ensures objects from different processes don't collide
        in multi-GPU environments where different processes may have objects
        at the same memory address.

    Returns
    -------
    result : dict
        Dictionary containing:
        - 'args': Positional arguments (tensors on specified device)
        - 'kwargs': Keyword arguments (tensors on specified device)
        - 'metadata': Full metadata
        - 'execution_result': Result of execution (if run=True)
        - 'comparison_match': Boolean indicating if result matched expected (if run=True and compare_outputs=True)
        If compare_outputs=True, also includes:
        - 'expected_tensors': Expected output tensors
        - 'expected_metadata': Expected output metadata
    r   r   zmetadata.jsonl not found in Nzmetadata.jsonl is empty in r   r   r   r   )map_locationr   )	load_file)r   z`Dump was saved with safetensors but package not installed. Install with: pip install safetensorsz2Neither inputs.pt nor inputs.safetensors found in r   r   r   r/   zMissing argument z	 in dump.r   r   z
Replaying z from z  Args: z
, Kwargs: )r   r   r   r   r   r   expected_tensorsexpected_metadatar   r   r   z	.__init__r{   zInstantiating z (PID: z, ID: z)...execution_resultTcomparison_matchzFailed to instantiate r   execution_errorzObject z has no method zObject (PID: z) not found in registry.z
Executing ...r   r   zReplay comparison passed!zReplay comparison FAILED.zExecution failed: z&Skipping execution: could not resolve z\Automatic function resolution disabled. Pass run=True to execute, or manually call function.)+r   rl   FileNotFoundErrorrp   
ValueErrorr   loadr   r   r5  r   r   tor  r   
startswithr   r  r*  rangeappendr  r4   r   r   infor+  r   endswithr  r  r   r   r   r  r~   r   r   r   r   r%  r   r   )+r   r0  r   r1  r2  r  r	  r   rT   inputs_pt_pathinputs_safetensors_pathr   r5  r   rq   r   r   r   max_arg_idxidxr   
kwarg_nameresult_dictexpected_outputsr   outputs_pt_pathoutputs_safetensors_pathr  r   r   r   r  registry_key
class_namecls_obj	real_argsr:  r   method_nameactual_outputsr  matchr   s+                                              r   replay_from_dumprW    s
   d XI I GX G GHHH $&66%%'' K Ix I IJJJ&':;;HAxAABBB)I ,N'*>> 

3~#6#6UKKK	 	'	'	)	) 
	333333%Ic*A&B&B5QQQMM 	 	 	8  	  KKK
 
 	

 %**,, / /V#YYv..c DF\\"2B77N K!!## 0 0>>&!! 	0ciinnQ'((Ck3//K""$$ 0 0>>&!! 	0ciinnQ'((Ck3//K ;?## 	 	Qjj-KKc*++++N""KK*>#+>??@@@@ OO<<<<===KK !!## 4 4>>(## 	4Xr22J!.s!3F:""$$ M M>>(## 	MXr22J''%7s8K%L%Lz"LL9i99x99:::LLFCIIFFfkkmm1D1DFFGGG+/6x"X"XK O ;#l2#,/D#D !!## 	$z#o*>*>USSS%,,.. 	
777777#,9011%$ $ $      !<   ,1133 	6 	6KC$*IIf$5$5S!!",,'8"==*:&'+:'(
 r
ll8,,,,y))\\,//
 &0L!!+.. 2 '__S11
 ,KDD +x00 + -0IIMMQRRrI+_Z__
__RY___   &gy;F;;*6<?OL9+/(:J$67 +  $ ((:;  +*$ + + +&Pz&P&PQ&P&PQQQ9<Q$56*******+ #.<?3R3R),7C"+//#"6"6r":KsK00 U&sK88+.t99q==tABBxxb(S#(S(Sk(S(STTTTOO[
[['[[[   <$[)<<D '	"8F+FF	FFFGGG#'4#8#8#8 2B./" E%'N!"2ELAA :3Cx00#$4udmDD :'01A'B'B E EGAt)$== E@D}}} =E $$4d;; :)9 !E' S 0AQ R R6;K 23 E%@AAAA(CDDD 8 8 86166777    i224455514Q-......8 OORRRyRR     
C	
 	
 	

 sQ   # D D Q? ?R-AW0 0
X8:3X3-X83X8=D` 
a>Aa99a>root_dirc           
         t          |           }|                                st          d|            g }|                                D ]B}|                                r,|dz                                  r|                    |           C|                    d            g }t          |          }t          	                    d| d|             i }t          |          D ]\  }}	t          	                    d|dz    d	| d
|	j         d           	 t          t          |	          d|d|          }
t          |	          |
d<   |                    |
           {# t          $ ra}t                              d|	j         d|            |                    t          |          t          |	          d           Y d}~d}~ww xY w|S )a  
    Replay a sequence of API calls from a root dump directory.

    This function iterates through all dump directories in the root directory,
    sorted by timestamp/sequence number, and replays them in order.

    Parameters
    ----------
    root_dir : str
        Path to the root directory containing dump subdirectories
    device : str
        Target device for execution (default: "cuda")

    Returns
    -------
    list
        List of results from replay_from_dump calls
    zRoot dump directory not found: r   c                     | j         S rW   )r|   )xs    r   <lambda>z!replay_sequence.<locals>.<lambda>  s     r   )r   zFound z dumps to replay from [r/   r   z] Replaying r=  T)r0  r   r1  r2  r   zFailed to replay r   )r   r   N)r   rl   r>  iterdiris_dirrD  sortr+  r4   rE  r   r|   rW  r   r   r   )rX  r   	root_path	dump_dirsr  resultstotalr2  r   r   resr   s               r   replay_sequencerf    s   & XI N L( L LMMM I!!## # #;;== 	#d%55==?? 	#T""" NN''N(((G	NNELLA%AAxAABBB 35O ++ I I8FQFFFFHMFFFGGG	I #H $ /  C "(mmC
ONN3 	I 	I 	IMMBhmBBqBBCCCNNSVVXGGHHHHHHHH	I
 Ns   AE##
G-AG		Gc            
      .   t           dk    rdS g } |                     d           |                     t                       d           |                     d           	 	 ddlm} |                     d|            n%# t
          $ r |                     d           Y nw xY wt          j        j        }|r|                     d	|            n|                     d
           	 t          j        j	        
                                rTt          j        j	                                        }|r|                     d|            n+|                     d           n|                     d           n0# t
          $ r#}|                     d| d           Y d}~nd}~ww xY wt          j        
                                rt          j                                        }|                     d|            t          |          D ]}	 t          j                            |          }t          j                            |          }|d         dz  |d         z   }	|                     d| d|            |                     d|d          d|d          d|	 d           # t
          $ r&}|                     d| d| d           Y d}~d}~ww xY wn|                     d           |                     dt          j                    n/# t
          $ r"}|                     d|            Y d}~nd}~ww xY w|                     d           |                     d           t                              d                    |                      dS )z5Log system information once at module initialization.r   NrM   z, FlashInfer API Logging - System Informationr/   r   zFlashInfer version: z!FlashInfer version: <unavailable>zCUDA toolkit version: z5CUDA toolkit version: <unavailable - CPU-only build?>zcuDNN version: zcuDNN version: <unavailable>zcuDNN version: <not available>zcuDNN version: <error: >zNumber of GPUs: rL   z  GPU r   z    Compute capability: r{   z (SM)z
: <error: z#CUDA: Not available (CPU-only mode)zPyTorch version: z$Error gathering system information: r   rc   )r3   rD  rJ   r   r   r   r   r/  backendscudnnis_availabledevice_countrC  get_device_nameget_device_capabilityr4   r   join)
linesr   cuda_versioncudnn_versionr   rm  r   gpu_name
capabilitysm_archs
             r   _log_system_inforw    s   E	LL	LLN$$RRRSSS	LL5A	>BBBBBBLLD0BDDEEEE 	> 	> 	>LL<=====	> }) 	RLL@,@@AAAALLPQQQ
	9~#0022 ? % 4 < < > >  ALL!B=!B!BCCCCLL!?@@@@=>>> 	9 	9 	9LL7177788888888	9 :""$$ 	@ :2244LLL:L::;;; <(( 
= 
=	=$z99!<<H!&!A!A!!D!DJ(mb0:a=@GLL!9!!9!9x!9!9:::LL`:a=``:a=``V]```    ! = = =LL!;!!;!;q!;!;!;<<<<<<<<=
= LL>??? 	<):<<==== A A A?A??@@@@@@@@A 
LL	LLMM$))E""#####s    A? >L ?B!L  B!!AL &BE3 2L 3
F =FL F  A)L 
BJ#"L #
K-K	L K;L 
L;L66L;levelindentc                 t   d|z  }| | dS t          | t          j                  r!| | j        j         d| j         d| j         dS t          | t          j                  r|dk    r| dS | d	g}|	                    | d
t          | j                              |	                    | dt          |                                                       |	                    | d| j                    |	                    | d| j                    |	                    | d| j                    |	                    | d|                                             |dk    r	 d}| j        rit%          t          j        d          rOt)          j        t,                    5  t          j                                        }ddd           n# 1 swxY w Y   |r|	                    | d           n|                                 dk    r| j        t          j        t          j        t          j        t          j        t          j        t          j        fv rw|                                 }|	                    | d|                                 !                                d           |	                    | d|"                                !                                d           |	                    | d|#                                !                                d           t          j$        |          %                                !                                }|	                    | d|            t          j&        |          %                                !                                }|	                    | d|            n| j        t          j'        t          j(        t          j)        t          j*        t          j+        fv r|	                    | d|                                  !                                            |	                    | d| "                                !                                            |	                    | d|                                 #                                !                                d           n2# t,          $ r%}	|	                    | d|	 d           Y d}	~	nd}	~	ww xY w|	                    | d           d,                    |          S t%          | d          r| j        j        dk    r|dk    r| d S | d!g}|	                    | d"t[          | j.        ||dz                         |	                    | d#t[          | j/        ||dz                         |	                    | d$| j0                    t%          | d%          r&| j1        |	                    | d&| j1                    |	                    | d           d,                    |          S t          | td                    rtg          |           dk    r| d'S |dk    r| d(tg          |            d)S | d*g}ti          |           D ]4\  }
}|	                    | d+|
 d,t[          |||dz                         5|	                    | d           d,                    |          S t          | t                    rtg          |           dk    r| d-S |dk    r| d.tg          |            d/S | d0g}ti          |           D ]4\  }
}|	                    | d+|
 d,t[          |||dz                         5|	                    | d           d,                    |          S t          | tj                    rtg          |           dk    r| d1S |dk    r| d2tg          |            d3S | d4g}| 6                                D ]A\  }}|	                    | dto          |           d5t[          |||dz                         B|	                    | d6           d,                    |          S t          | tp          t>          tr          tt          f          r| |  S t          | tv                    r| to          |            S 	 | to          |            S # t,          $ r | d7ty          |           j         d8cY S w xY w)9aX  
    Format a value for logging based on the log level.

    Parameters
    ----------
    value : Any
        The value to format
    level : int
        The logging level (1, 2, or 3)
    indent : int
        The indentation level for nested structures
    Returns
    -------
    str
        Formatted string representation of the value
      NNoner{   z (value=ri  r/   zTensor(...)zTensor(z  shape=z	  stride=z  dtype=z	  device=z  requires_grad=z  is_contiguous=   Fis_current_stream_capturingz6  [statistics skipped: CUDA graph capture in progress]r   z  min=z.6fz  max=z  mean=z  nan_count=z  inf_count=z  [statistics error: ]rc   	__class__	FP4TensorzFP4Tensor(...)z
FP4Tensor(z  data=z  scale=z  scale_start_index=original_shapez  original_shape=z[]z[list with z items]r]  z  []: z()z(tuple with z items)(z{}z{dict with z keys}{r   }<z object>)=r~   rz   r   r  r   r|   rv   r   r   rD  r   r   r   r   r   requires_gradis_contiguousis_cudar   r/  
contextlibsuppressr   r~  numelfloat16float32float64bfloat16float8_e4m3fnfloat8_e5m2r   minr  r*  meanisnanr   isinfint8int16int32int64uint8rp  _format_valuedatascalescale_start_indexr  r   r+  r   r   r   r}   r   r   complexr   ry   )rv   rx  ry  
indent_strrq  is_capturing	val_float	nan_count	inf_countr   r   r  r   vals                 r   r  r  h  s
   " J }"""" %## 
 X5?3XXejXX%+XXX	

 %&& ? A:: ---- '''(
@@E%+,>,>@@AAA
DDU5<<>>-B-BDDEEE
99EK99:::
;;U\;;<<<
IIE4GIIJJJ
KKE4G4G4I4IKKLLL A::-G  %= PWUZ9V%W%W P#,Y77 P P',z'M'M'O'OP P P P P P P P P P P P P P P   #LL%]]]    [[]]Q&&{+)'   %*KKMM	
%V%V)--//:N:N:P:P%V%V%VWWW
%V%V)--//:N:N:P:P%V%V%VWWW)OO)..2B2B2G2G2I2IOOO   %*K	$:$:$>$>$@$@$E$E$G$G	
%K%K	%K%KLLL$)K	$:$:$>$>$@$@$E$E$G$G	
%K%K	%K%KLLLL
)   
%N%N%))++:J:J:L:L%N%NOOO
%N%N%))++:J:J:L:L%N%NOOO)SS%++--2D2D2F2F2K2K2M2MSSS    G G G
EEEEEFFFFFFFFG 	
%%%&&&yy uk""  u'?;'N'NA:: 0000***+PP-
E6A:"N"NPP	
 	
 	
 	RR=eVaZ#P#PRR	
 	
 	
 	
QQ8OQQRRR5*++ 	Q0D0PLLJOO9MOOPPP
%%%&&&yy %  u::?? $$$$A:: @@SZZ@@@@!!!" '' 	 	GAtLLPP!PPdE6A:(N(NPP    	
%%%&&&yy %  u::?? $$$$A:: AAc%jjAAAA!!!" '' 	 	GAtLLPP!PPdE6A:(N(NPP    	
%%%&&&yy %  u::?? &&&&A:: AAc%jjAAAA"""# 	 	HCLLUUcUUmCQR
.S.SUU    	
&&&'''yy %#udG455 &%e%%% % ,+d5kk+++>+d5kk+++ > > >==tE{{3======>sO   *<S" &GS" GS" GLS" "
T,TT=d $d76d7c                 6   	 t          j        |           }i }t          |j                                                  D ]H\  }\  }}|j        t           j        j        u r!d}|t          |          k     s||v rd}|s
|j        ||<   I|S # t          $ r i cY S w xY w)a  
    Extract parameters that have default values but were not explicitly provided.

    Parameters
    ----------
    func : Callable
        The function being called
    args : tuple
        Positional arguments that were provided
    kwargs : dict
        Keyword arguments that were provided
    Returns
    -------
    dict
        Dictionary of parameter names to default values for parameters that were not provided
    FT)
r   r   r   
parametersr   default	Parameteremptyr+  r   )	r   r   r   sigdefault_paramsr   
param_nameparamprovideds	            r   _get_default_paramsr    s    "%%&/0D0D0F0F&G&G 	; 	;"A"
E} 1 777 H 3t99}}
f 4 4  ;-2]z*   			s   BB	 	BBc                    g }|                     d           |                     t                       d|            |                     d           |s|r|rh|                     d           t          |          D ]C\  }}|                     d| d           |                     t          ||d                     D|rm|                     d	           |                                D ]C\  }}	|                     d
| d           |                     t          |	|d                     Dn|                     d           t          | ||          }
|
rm|                     d           |
                                D ]C\  }}|                     d
| d           |                     t          ||d                     Dt                              d                    |                     dS )a  
    Log function inputs BEFORE execution for crash safety.

    This ensures inputs are captured even if the function crashes with a CUDA error.

    Parameters
    ----------
    func : Callable
        The function being called (needed to extract default parameters)
    func_name : str
        Name of the function being called
    args : tuple
        Positional arguments
    kwargs : dict
        Keyword arguments
    level : int
        Logging level (3 or 5)
    rM    FlashInfer API Call: zP--------------------------------------------------------------------------------zPositional input arguments:z  arg[z]:   ry  zKeyword input arguments:r{  =z(No explicit arguments)z-Default parameters (not explicitly provided):z= [DEFAULT]rc   N)	rD  rJ   r   r  r   r  r4   r   rp  )r   rT   r   r   rx  rq  r   r   r   rv   r  r  default_values                r   _log_function_inputsr  A  s   * E	LL	LLN$$GGIGGHHH	LL  0v 0 	BLL6777#D// B B3^a^^^,,,]3a@@@AAAA 	DLL3444$llnn D D
U[#[[[)))]5%BBBCCCC.///(tV<<N HDEEE)7)=)=)?)? 	H 	H%JLL5j555666LL}eAFFFGGGGMM$))E""#####r   c                 ,   g }|                     d           |                     t          ||d                     |                     d           |                     d           t                              d                    |                     dS )z
    Log function outputs AFTER successful execution.
    Parameters
    ----------
    func_name : str
        Name of the function
    result : Any
        Function return value
    level : int
        Logging level (3 or 5)
    zOutput value:r/   r  rM   r   rc   N)rD  r  r4   r   rp  )rT   r   rx  rq  s       r   _log_function_outputsr  u  s     E	LL!!!	LLvuQ777888	LL	LLMM$))E""#####r   c                 h    t           dk    r| d S | S dt          dt          fd}| |S  ||           S )a#  
    Decorator to FlashInfer's APIs.

    .. warning::
        This API logging feature is experimental and may change in future versions.

    Currently logs input and output values of the function using Python's logging library.
    This decorator integrates with Python's standard logging infrastructure while
    maintaining zero overhead when disabled (FLASHINFER_LOGLEVEL=0).

    Environment Variables
    ---------------------
    FLASHINFER_LOGLEVEL : int (default: 0)
        - 0: No logging (zero overhead - decorator returns original function)
        - 1: Log function name only (logged BEFORE execution - crash-safe)
        - 3: Log function name + inputs/outputs with metadata (inputs logged BEFORE execution - crash-safe)
        - 5: Log function name + inputs/outputs with metadata + tensor statistics (inputs logged BEFORE execution - crash-safe)
        - 10: Level 5 logging + dump metadata and input/output tensors to disk for reproducibility (preserves stride/contiguity)

    FLASHINFER_LOGDEST : str (default: "stdout")
        - "stdout": Log to standard output
        - "stderr": Log to standard error
        - <path>: Log to specified file path
        - Use %i in path for process ID substitution (e.g., "log_%i.txt" -> "log_12345.txt")

    Level 10 Tensor Dumping (additional variables):
    FLASHINFER_DUMP_DIR : str (default: "flashinfer_dumps")
        - Directory where tensor dumps are saved

    FLASHINFER_DUMP_MAX_SIZE_GB : float (default: 20)
        - Maximum total size of dumps in GB

    FLASHINFER_DUMP_MAX_COUNT : int (default: 1000)
        - Maximum number of function call dumps

    FLASHINFER_DUMP_SAFETENSORS : int (default: 0)
        - 0: Use torch.save format (preserves stride/contiguity)
        - 1: Use safetensors format (no pickle, but loses stride info)

    FLASHINFER_DUMP_INCLUDE : str (default: "")
        - Comma-separated list of patterns to include for dumping (fnmatch-style)

    FLASHINFER_DUMP_EXCLUDE : str (default: "")
        - Comma-separated list of patterns to exclude for dumping (fnmatch-style)

    Examples
    --------
    Basic usage:

    >>> @flashinfer_api
    ... def my_function(x, y):
    ...     return x + y

    Notes
    -----
    - Key header lines include a timestamp in the format: [YYYY-MM-DD HH:MM:SS]
      (e.g., "FlashInfer API Call: function_name", "FlashInfer API Logging - System Information")
    - When FLASHINFER_LOGLEVEL=0, the decorator has truly zero overhead
      as it returns the original function unchanged.
    - Function names and inputs are logged BEFORE execution:
      - Level 1: Function name only
      - Levels 3-5: Function name + inputs with metadata
      This means critical debugging information is preserved even if the function
      crashes (e.g., CUDA illegal memory access, out-of-bounds, etc.).
    - Outputs are logged AFTER successful execution for levels 3 and 5.
    - **CUDA Graph Compatibility**: At level 5, tensor statistics (min/max/mean/nan_count)
      are automatically skipped during CUDA graph capture to avoid synchronization issues.
      The message "[statistics skipped: CUDA graph capture in progress]" will be logged.
    - The %i pattern is automatically replaced with the process ID for multi-process environments.
    - The logger does not propagate to the root logger to avoid duplicate logs.
    r   Nc                     | S rW   r"   )rh   s    r   r\  z flashinfer_api.<locals>.<lambda>  s    Q r   rh   r   c                 F     t          j                    fd            }|S )Nc                     	j         }d }| r^t          | d         d          rH	 | d         j        j         }d|v s|dv r| d| }t          | d                   }n# t          $ r Y nw xY wd }t
          dk    rl	 t          	|| ||          }|rt                              d|            n7# t          $ r*}t          	                    d	| d
|            Y d }~nd }~ww xY w	 t
          dk    r,t                              t                       d|            n3t
          dk    r(t          t
          d          }t          	|| ||           n7# t          $ r*}t          	                    d| d|            Y d }~nd }~ww xY w 	| i |}	 t
          dk    r&t          t
          d          }t          |||           n7# t          $ r*}t          	                    d| d|            Y d }~nd }~ww xY wt
          dk    rh|rf	 t          ||           t                              d|            n7# t          $ r*}t          	                    d| d
|            Y d }~nd }~ww xY w|S )Nr   r  Wrapper)BatchMLAPagedAttentionWrapperr{   rL   )r   zInputs dumped to: z[DUMP ERROR (inputs) in r  r/   r     r}  z[LOGGING ERROR in z (pre-execution)]: z (outputs)]: zOutputs dumped to: z[DUMP ERROR (outputs) in )r   r   r  idr   r3   r   r4   r   r   rJ   r  r  r  r
  rE  )
r   r   rT   r   rQ  r   r   effective_levelr   rh   s
            r   wrapperz2flashinfer_api.<locals>.decorator.<locals>.wrapper  se    
IG 	Q55 	!%a!2!;J J..* A 3 3 (2$?$?I$?$?	"$T!W++    D H##P49dFG     H   G&E8&E&EFFF  P P PMM"NY"N"N1"N"NOOOOOOOOPV!Q&&MM)++NN9NN    $q(( '*.!&<&<O(ItV_UUU V V VT9TTQRTTUUUUUUUUV Q'''FP!Q&&&).!&<&<O))V_MMM P P PN9NN1NNOOOOOOOOP ###Q*8V<<<LL!Ax!A!ABBBB  Q Q QMM"Oi"O"OA"O"OPPPPPPPPQ Msr   6A 
A('A(93B- -
C!7 CC!%A*E 
F E??F1G 
G6 G11G6-H5 5
I)? I$$I))	functoolswraps)rh   r  s   ` r   	decoratorz!flashinfer_api.<locals>.decorator  s9    			>	 >	 >	 >	 
	>	@ r   )r3   r   )r   r  s     r   flashinfer_apir    sf    R <;BX B( B B B BH |9T??r   rW   )r  r  )Fr/  FN)r/  )r   )G__doc__rz   rY   r  r   rf   r6   r   r?   r   pathlibr   typingr   r   r   r   r	   r  r  r   r   r   r   environr  r3   r=   rO   r   r   r   _DUMP_INCLUDE_DUMP_EXCLUDEr  rQ   rR   rP   r   r   r   _session_jsonl_initialized	getLoggerr4   rF   rJ   rS   r   r_   ri   rp   r   ru   r   r   r   r   r   r
  r  r  r%  rW  r   rf  rw  r  r  r  r  r  r"   r   r   <module>r     s              				 



             7 7 7 7 7 7 7 7 7 7 7 7 7 7         	 	 	 	 	 	 RZ^^$93??@@&&rz~~6JH'U'UVV JNN02DEE	E"*..)FMMNN #bjnn%@&IIJJ 
8"==
8"==SS]-@-@-E-ESSS SS]-@-@-E-ESSS  JNN#@#FF#M    "  '
,
-
-  > : : : : :
  0%S %T % % % %P+t +T#s(^ + + + + +d xS#X/G    @55< 5C 5 5 5 5
$
C $
C $
 $
 $
 $
N#
##
4U\!"DcN23# # # #V "t t
tt t 	t
 c]t c]t t t tnh.S h.# h.$ h. h. h. h.Vc c    :3 s x?Q    ( CG4 444&+4:?4	4 4 4 4r "<@\ \\\ \ 
	\
 d5c?C#789\ 	\ \ \ \~> >c >3 >D > > > >BD$ D$ D$P      

k> k> k>S k># k>c k> k> k> k>\(h (e (T (d ( ( ( (V1$
1$"1$*/1$9=1$FI1$	1$ 1$ 1$ 1$h$S $# $c $d $ $ $ $.T T TX T T T T T Tr   