
    PL
j`                        U 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mZ ddl	m
Z
mZmZmZmZ  ej        e          Zdej        defdZdedefd	Zdd
ee         dee         fdZ G d d          ZdZi Zee
eeef         f         ed<    ej                    Zde
defdZ ddZ! G d d          Z" e"            Z#defdZ$ddefdZ%dS )aV  Central registry for all hermes-agent tools.

Each tool file calls ``registry.register()`` at module level to declare its
schema, handler, toolset membership, and availability check.  ``model_tools.py``
queries the registry instead of maintaining its own parallel data structures.

Import chain (circular-import safe):
    tools/registry.py  (no imports from model_tools or tool files)
           ^
    tools/*.py  (import from tools.registry at module level)
           ^
    model_tools.py  (imports tools.registry + all tool modules)
           ^
    run_agent.py, cli.py, batch_runner.py, etc.
    N)Path)CallableDictListOptionalSetnodereturnc                 8   t          | t          j                  rt          | j        t          j                  sdS | j        j        }t          |t          j                  o9|j        dk    o.t          |j        t          j                  o|j        j	        dk    S )zHReturn True when *node* is a ``registry.register(...)`` call expression.Fregisterregistry)

isinstanceastExprvalueCallfunc	AttributeattrNameid)r	   r   s     2/home/kuhnn/.hermes/hermes-agent/tools/registry.py_is_registry_register_callr      s    dCH%% Z
CH-M-M u:?D4'' 	(I#	(tz38,,	( JMZ'	    module_pathc                     	 |                      d          }t          j        |t          |                     }n# t          t
          f$ r Y dS w xY wt          d |j        D                       S )zReturn True when the module contains a top-level ``registry.register(...)`` call.

    Only inspects module-body statements so that helper modules which happen
    to call ``registry.register()`` inside a function are not picked up.
    zutf-8)encoding)filenameFc              3   4   K   | ]}t          |          V  d S N)r   ).0stmts     r   	<genexpr>z*_module_registers_tools.<locals>.<genexpr>6   s+      FFD)$//FFFFFFr   )	read_textr   parsestrOSErrorSyntaxErroranybody)r   sourcetrees      r   _module_registers_toolsr-   *   s    &&&88y#k*:*:;;;[!   uu FFDIFFFFFFs   9< AA	tools_dirc                    | t          |           n*t          t                                                    j        }d t	          |                    d                    D             }g }|D ]_}	 t          j        |           |                    |           -# t          $ r&}t                              d||           Y d}~Xd}~ww xY w|S )zLImport built-in self-registering tool modules and return their module names.Nc                 R    g | ]$}|j         d vt          |          d|j         %S )>   __init__.pymcp_tool.pyregistry.pyztools.)namer-   stem)r!   paths     r   
<listcomp>z*discover_builtin_tools.<locals>.<listcomp><   sK       9III#D)) J 	IIIr   z*.pyz#Could not import tool module %s: %s)r   __file__resolveparentsortedglob	importlibimport_moduleappend	Exceptionloggerwarning)r.   
tools_pathmodule_namesimportedmod_namees         r   discover_builtin_toolsrH   9   s    $-$9itH~~?U?U?W?W?^J :??62233  L H  O O	O#H---OOH%%%% 	O 	O 	ONN@(ANNNNNNNN	OOs   0)B
C
$CC
c                        e Zd ZdZdZ	 ddZdS )	ToolEntryz&Metadata for a single registered tool.r4   toolsetschemahandlercheck_fnrequires_envis_asyncdescriptionemojimax_result_size_charsdynamic_schema_overridesNc                     || _         || _        || _        || _        || _        || _        || _        || _        |	| _        |
| _	        || _
        d S r    rK   )selfr4   rL   rM   rN   rO   rP   rQ   rR   rS   rT   rU   s               r   __init__zToolEntry.__init__V   s^     	 ( &
%:" )A%%%r   )NN)__name__
__module____qualname____doc__	__slots__rX    r   r   rJ   rJ   M   sB        00I GKA A A A A Ar   rJ   g      >@_check_fn_cachefnc                    t          j                    }t          5  t                              |           }|!|\  }}||z
  t
          k     r|cddd           S ddd           n# 1 swxY w Y   	 t           |                       }n# t          $ r d}Y nw xY wt          5  ||ft          | <   ddd           n# 1 swxY w Y   |S )zIReturn bool(fn()), TTL-cached across calls. Swallows exceptions as False.NF)time	monotonic_check_fn_cache_lockr_   get_CHECK_FN_TTL_SECONDSboolr@   )r`   nowcachedtsr   s        r   _check_fn_cachedrk   ~   sq   
.

C	   $$R((IBRx///                      RRTT

   	 + +"El+ + + + + + + + + + + + + + +Ls5   1A%%A),A)1B	 	BB"B;;B?B?c                  x    t           5  t                                           ddd           dS # 1 swxY w Y   dS )zDrop all cached ``check_fn`` results. Call after config changes that
    affect tool availability (e.g. ``hermes tools enable``).N)rd   r_   clearr^   r   r   invalidate_check_fn_cachern      s     
                                          /33c                      e Zd ZdZd Zdeee         ee	e
f         f         fdZdee         fdZdee	e
f         fdZde	de
d	z  defd
Zde	dee         fdZdee	         fdZde	dee	         fdZde	de	dd	fdZdee	e	f         fdZde	dee	         fdZ	 	 	 	 	 	 	 	 d3de	de	dede
de
dedede	de	deez  d	z  de
defdZde	dd	fd Zd4d!ee	         d"edee         fd#Zde	d$ede	fd%Zd5de	d&eez  d	z  deez  fd'Z dee	         fd(Z!de	dee         fd)Z"de	dee	         fd*Z#d6de	d&e	de	fd,Z$dee	e	f         fd-Z%de	defd.Z&dee	ef         fd/Z'dee	ef         fd0Z(dee	ef         fd1Z)d4d"efd2Z*d	S )7ToolRegistryzISingleton registry that collects tool schemas + handlers from tool files.c                 n    i | _         i | _        i | _        t          j                    | _        d| _        d S )Nr   )_tools_toolset_checks_toolset_aliases	threadingRLock_lock_generationrW   s    r   rX   zToolRegistry.__init__   s:    ,.4602 _&&
 !"r   r
   c                     | j         5  t          | j                                                  t	          | j                  fcddd           S # 1 swxY w Y   dS )zBReturn a coherent snapshot of registry entries and toolset checks.N)rx   listrs   valuesdictrt   rz   s    r   _snapshot_statezToolRegistry._snapshot_state   s    Z 	J 	J**,,--tD4H/I/II	J 	J 	J 	J 	J 	J 	J 	J 	J 	J 	J 	J 	J 	J 	J 	J 	J 	Js   :AAAc                 6    |                                  d         S )z4Return a stable snapshot of registered tool entries.r   r   rz   s    r   _snapshot_entrieszToolRegistry._snapshot_entries       ##%%a((r   c                 6    |                                  d         S )z8Return a stable snapshot of toolset availability checks.   r   rz   s    r   _snapshot_toolset_checksz%ToolRegistry._snapshot_toolset_checks   r   r   rL   checkNc                     |sdS 	 t           |                      S # t          $ r t                              d|           Y dS w xY w)zQRun a toolset check, treating missing or failing checks as unavailable/available.Tz,Toolset %s check raised; marking unavailableF)rg   r@   rA   debugrW   rL   r   s      r   _evaluate_toolset_checkz$ToolRegistry._evaluate_toolset_check   s]     	4	==  	 	 	LLGQQQ55	s    %AAr4   c                 x    | j         5  | j                            |          cddd           S # 1 swxY w Y   dS )z0Return a registered tool entry by name, or None.N)rx   rs   re   )rW   r4   s     r   	get_entryzToolRegistry.get_entry   s    Z 	) 	);??4((	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	) 	)ro   c                 X    t          d |                                 D                       S )z;Return sorted unique toolset names present in the registry.c                     h | ]	}|j         
S r^   rL   r!   entrys     r   	<setcomp>z<ToolRegistry.get_registered_toolset_names.<locals>.<setcomp>   s    KKKu}KKKr   r;   r   rz   s    r   get_registered_toolset_namesz)ToolRegistry.get_registered_toolset_names   s+    KK$2H2H2J2JKKKLLLr   c                 ^    t          fd|                                 D                       S )z:Return sorted tool names registered under a given toolset.c              3   <   K   | ]}|j         k    |j        V  d S r    rL   r4   )r!   r   rL   s     r   r#   z:ToolRegistry.get_tool_names_for_toolset.<locals>.<genexpr>   s>       
 
 }'' J''''
 
r   r   )rW   rL   s    `r   get_tool_names_for_toolsetz'ToolRegistry.get_tool_names_for_toolset   sG     
 
 
 
$($:$:$<$<
 
 
 
 
 	
r   aliasc                     | j         5  | j                            |          }|r#||k    rt                              d|||           || j        |<   | xj        dz  c_        ddd           dS # 1 swxY w Y   dS )z8Register an explicit alias for a canonical toolset name.z4Toolset alias collision: '%s' (%s) overwritten by %sr   N)rx   ru   re   rA   rB   ry   )rW   r   rL   existings       r   register_toolset_aliasz#ToolRegistry.register_toolset_alias   s    Z 	" 	",0077H H//J8W   ,3D!%(!	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	"s   AA//A36A3c                 l    | j         5  t          | j                  cddd           S # 1 swxY w Y   dS )z=Return a snapshot of ``{alias: canonical_toolset}`` mappings.N)rx   r~   ru   rz   s    r   get_registered_toolset_aliasesz+ToolRegistry.get_registered_toolset_aliases   s|    Z 	/ 	/-..	/ 	/ 	/ 	/ 	/ 	/ 	/ 	/ 	/ 	/ 	/ 	/ 	/ 	/ 	/ 	/ 	/ 	/s   )--c                 x    | j         5  | j                            |          cddd           S # 1 swxY w Y   dS )z8Return the canonical toolset name for an alias, or None.N)rx   ru   re   )rW   r   s     r   get_toolset_alias_targetz%ToolRegistry.get_toolset_alias_target   s    Z 	4 	4(,,U33	4 	4 	4 	4 	4 	4 	4 	4 	4 	4 	4 	4 	4 	4 	4 	4 	4 	4ro   F rM   rN   rO   rP   rQ   rR   rS   rT   rU   overridec                    | j         5  | j                            |          }|r|j        |k    r|j                            d          o|                    d          }|r#t
                              d|||j                   nU|r#t
                              d|||j                   n0t
                              d|||j                   	 ddd           dS t          ||||||pg ||p|                    dd          |	|
|          | j        |<   |r|| j
        vr
|| j
        |<   | xj        d	z  c_        ddd           dS # 1 swxY w Y   dS )
a  Register a tool.  Called at module-import time by each tool file.

        ``override=True`` is an explicit opt-in for plugins that intend to
        replace an existing built-in tool implementation (e.g. swap the
        default browser tool for a headed-Chrome CDP backend). Without it,
        registrations that would shadow an existing tool from a different
        toolset are rejected to prevent accidental overwrites.
        zmcp-z8Tool '%s': MCP toolset '%s' overwriting MCP toolset '%s'zOTool '%s': toolset '%s' overriding existing toolset '%s' (override=True opt-in)zTool registration REJECTED: '%s' (toolset '%s') would shadow existing tool from toolset '%s'. Pass override=True to register() if the replacement is intentional, or deregister the existing tool first.NrR   r   rK   r   )rx   rs   re   rL   
startswithrA   r   infoerrorrJ   rt   ry   )rW   r4   rL   rM   rN   rO   rP   rQ   rR   rS   rT   rU   r   r   both_mcps                  r   r   zToolRegistry.register   s   . Z 0	" 0	"{t,,H H,77 $//77 3**622   LLRgx'7      KK1gx'7    LLN gx'7   A0	" 0	" 0	" 0	" 0	" 0	" 0	" 0	"B !*!)/R!'H6::mR+H+H&;)A! ! !DK  9G4+???08$W-!a0	" 0	" 0	" 0	" 0	" 0	" 0	" 0	" 0	" 0	" 0	" 0	" 0	" 0	" 0	" 0	" 0	" 0	"s   CEAEE	E	c                    | j         5  | j                            |d          	 ddd           dS t          fd| j                                        D                       }|sJ| j                            j        d           fd| j                                        D             | _        | xj	        dz  c_	        ddd           n# 1 swxY w Y   t                              d|           dS )a  Remove a tool from the registry.

        Also cleans up the toolset check if no other tools remain in the
        same toolset.  Used by MCP dynamic tool discovery to nuke-and-repave
        when a server sends ``notifications/tools/list_changed``.
        Nc              3   8   K   | ]}|j         j         k    V  d S r    r   )r!   rG   r   s     r   r#   z*ToolRegistry.deregister.<locals>.<genexpr>@  s=       ' '/0	U]*' ' ' ' ' 'r   c                 2    i | ]\  }}|j         k    ||S r^   r   )r!   r   targetr   s      r   
<dictcomp>z+ToolRegistry.deregister.<locals>.<dictcomp>E  s6     ) ) )%v.. 6...r   r   zDeregistered tool: %s)rx   rs   popr)   r}   rt   rL   ru   itemsry   rA   r   )rW   r4   toolset_still_existsr   s      @r   
deregisterzToolRegistry.deregister3  s    Z 	" 	"KOOD$//E}	" 	" 	" 	" 	" 	" 	" 	" $' ' ' ' '48K4F4F4H4H' ' ' $ $  ( $((===) ) ) ))-)>)D)D)F)F) ) )%
 !!	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	"" 	,d33333s   CBCCC
tool_namesquietc                    g }i }d |                                  D             }t          |          D ]}|                    |          }|s|j        rP|j        |vrt	          |j                  ||j        <   ||j                 s|st
                              d|           ri |j        d|j        i}|j	        s	 |	                                }	t          |	t                    r|                    |	           n3# t          $ r&}
t
                              d||
           Y d}
~
nd}
~
ww xY w|                    d|d           |S )a"  Return OpenAI-format tool schemas for the requested tool names.

        Only tools whose ``check_fn()`` returns True (or have no check_fn)
        are included. ``check_fn()`` results are cached for ~30 s via
        :func:`_check_fn_cached` to amortize repeat probes (check_terminal_
        requirements probes modal/docker, browser checks probe playwright,
        etc.); TTL chosen so env-var changes (``hermes tools enable foo``)
        still take effect in near-real-time without forcing a full cache
        flush on every call.
        c                     i | ]
}|j         |S r^   r4   r   s     r   r   z0ToolRegistry.get_definitions.<locals>.<dictcomp>a  s    SSS5:uSSSr   z"Tool %s unavailable (check failed)r4   NzCdynamic_schema_overrides for tool %s raised %s; using static schemafunction)typer   )r   r;   re   rO   rk   rA   r   rM   r4   rU   r   r~   updater@   rB   r?   )rW   r   r   resultcheck_resultsentries_by_namer4   r   schema_with_name	overridesexcs              r   get_definitionszToolRegistry.get_definitionsQ  s     /1SS$:P:P:R:RSSS:&& 	N 	ND#''--E ~ >664DU^4T4TM%.1$U^4   Q%I4PPPC%,C
CC -9	 % > > @ @I!)T22 ;(//	:::    NN.c        MM:;KLLMMMMs   >>C==
D-D((D-argsc                    |                      |          }|st          j        dd| i          S 	 |j        rddlm}  | |j        |fi |          S  |j        |fi |S # t          $ r{}t          	                    d||           dt          |          j         d| }	 ddlm}  ||          }	n# t          $ r |}	Y nw xY wt          j        d|	i          cY d	}~S d	}~ww xY w)
zExecute a tool handler by name.

        * Async handlers are bridged automatically via ``_run_async()``.
        * All exceptions are caught and returned as ``{"error": "..."}``
          for consistent error format.
        r   zUnknown tool: r   )
_run_asynczTool %s dispatch error: %szTool execution failed: z: )_sanitize_tool_errorN)r   jsondumpsrQ   model_toolsr   rN   r@   rA   	exceptionr   rY   r   )
rW   r4   r   kwargsr   r   rG   rawr   	sanitizeds
             r   dispatchzToolRegistry.dispatch  sf    t$$ 	B:w(?(?(?@AAA	4~ A222222!z-%-"?"?"?"?@@@ 5=00000 	4 	4 	494CCC DDGG,<CCCCC <<<<<<0055		      			 :w	233333333	4sG   #A$ A$ $
C).6C$%B76C$7CC$CC$C)$C)defaultc                 d    |                      |          }|r|j        |j        S ||S ddlm} |S )zBReturn per-tool max result size, or *default* (or global default).Nr   )DEFAULT_RESULT_SIZE_CHARS)r   rT   tools.budget_configr   )rW   r4   r   r   r   s        r   get_max_result_sizez ToolRegistry.get_max_result_size  sO    t$$ 	/U0<..NAAAAAA((r   c                 X    t          d |                                 D                       S )z0Return sorted list of all registered tool names.c              3   $   K   | ]}|j         V  d S r    r   r   s     r   r#   z2ToolRegistry.get_all_tool_names.<locals>.<genexpr>  s$      GGUejGGGGGGr   r   rz   s    r   get_all_tool_nameszToolRegistry.get_all_tool_names  s+    GGd.D.D.F.FGGGGGGr   c                 B    |                      |          }|r|j        ndS )u   Return a tool's raw schema dict, bypassing check_fn filtering.

        Useful for token estimation and introspection where availability
        doesn't matter — only the schema content does.
        N)r   rM   rW   r4   r   s      r   
get_schemazToolRegistry.get_schema  s&     t$$$.u||$.r   c                 B    |                      |          }|r|j        ndS )z.Return the toolset a tool belongs to, or None.N)r   rL   r   s      r   get_toolset_for_toolz!ToolRegistry.get_toolset_for_tool  s$    t$$ %/u}}4/r      ⚡c                 P    |                      |          }|r|j        r|j        n|S )z3Return the emoji for a tool, or *default* if unset.)r   rS   )rW   r4   r   r   s       r   	get_emojizToolRegistry.get_emoji  s+    t$$$AA'Br   c                 >    d |                                  D             S )z?Return ``{tool_name: toolset_name}`` for every registered tool.c                 (    i | ]}|j         |j        S r^   )r4   rL   r   s     r   r   z8ToolRegistry.get_tool_to_toolset_map.<locals>.<dictcomp>  s    PPPe
EMPPPr   )r   rz   s    r   get_tool_to_toolset_mapz$ToolRegistry.get_tool_to_toolset_map  s"    PPt7M7M7O7OPPPPr   c                     | j         5  | j                            |          }ddd           n# 1 swxY w Y   |                     ||          S )zCheck if a toolset's requirements are met.

        Returns False (rather than crashing) when the check function raises
        an unexpected exception (e.g. network error, missing import, bad config).
        N)rx   rt   re   r   r   s      r   is_toolset_availablez!ToolRegistry.is_toolset_available  s     Z 	6 	6(,,W55E	6 	6 	6 	6 	6 	6 	6 	6 	6 	6 	6 	6 	6 	6 	6++GU;;;s   /33c                                                        \  }t          d |D                       } fd|D             S )z7Return ``{toolset: available_bool}`` for every toolset.c                     h | ]	}|j         
S r^   r   r   s     r   r   z:ToolRegistry.check_toolset_requirements.<locals>.<setcomp>  s    >>>U5=>>>r   c           	      d    i | ],}|                     |                    |                    -S r^   )r   re   )r!   rL   rW   toolset_checkss     r   r   z;ToolRegistry.check_toolset_requirements.<locals>.<dictcomp>  sI     
 
 
 T11'>;M;Mg;V;VWW
 
 
r   )r   r;   )rW   entriestoolsetsr   s   `  @r   check_toolset_requirementsz'ToolRegistry.check_toolset_requirements  sf    "&"6"6"8"8>>g>>>??
 
 
 
 
#
 
 
 	
r   c                    i }|                                  \  }}|D ]}|j        }||vr1|                     ||                    |                    g dg d||<   ||         d                             |j                   |j        r;|j        D ]3}|||         d         vr!||         d                             |           4|S )z'Return toolset metadata for UI display.r   )	availabletoolsrR   requirementsr   r   )r   rL   r   re   r?   r4   rP   )rW   r   r   r   r   rj   envs          r   get_available_toolsetsz#ToolRegistry.get_available_toolsets  s    $&"&"6"6"8"8 	A 	AEB!!!%!=!=N..r22" "  #%$&    RL!((444! A - A AC(2,~">>> ^4;;C@@@r   c                    i }|                                  \  }}|D ]}|j        }||vr|g |                    |          dg d||<   |j        ||         d         vr&||         d                             |j                   |j        D ]3}|||         d         vr!||         d                             |           4|S )zABuild a TOOLSET_REQUIREMENTS-compatible dict for backward compat.N)r4   env_varsrO   	setup_urlr   r   r   )r   rL   re   r4   r?   rP   )rW   r   r   r   r   rj   r   s          r   get_toolset_requirementsz%ToolRegistry.get_toolset_requirements  s    "$"&"6"6"8"8 	7 	7EB " . 2 22 6 6!% r
 zG!444r
7#**5:666) 7 7fRj4442Jz*11#6667 r   c                 |   g }g }t                      }|                                 \  }}|D ]}|j        |v r|                               |                     |                                        r|                               b|                    |j        fd|D             d           ||fS )zDReturn (available_toolsets, unavailable_info) like the old function.c                 4    g | ]}|j         k    |j        S r^   r   )r!   rG   rj   s     r   r7   z8ToolRegistry.check_tool_availability.<locals>.<listcomp>  s"    IIIbafr   )r4   r   r   )setr   rL   addr   re   r?   rP   )	rW   r   r   unavailableseenr   r   r   rj   s	           @r   check_tool_availabilityz$ToolRegistry.check_tool_availability	  s    	uu"&"6"6"8"8 	 	EBTzzHHRLLL++B0B0B20F0FGG   $$$$"" % 2IIIIgIII$ $    
 +%%r   )NNFr   r   NNF)Fr    )r   )+rY   rZ   r[   r\   rX   tupler   rJ   r   r&   r   r   r   r   rg   r   r   r   r   r   r   r   r   r~   r|   intfloatr   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r^   r   r   rq   rq      s       SS" " "JtIS(]8K'K!L J J J J
)4	? ) ) ) ))$sH}*= ) ) ) )s 8d? t    )c )hy&9 ) ) ) )
Md3i M M M M
# 
$s) 
 
 
 

"C 
"# 
"$ 
" 
" 
" 
"/S#X / / / /
4c 4hsm 4 4 4 4 "!48-1G" G"G" G" 	G"
 G" G" G" G" G" G"  #U{T1G" #+G" G" G" G" G"R4s 4t 4 4 4 4</ /#c( /4 /DQUJ / / / /j4S 4 43 4 4 4 4@) ) )cEkD6H )TWZ_T_ ) ) ) )HDI H H H H/s /x~ / / / /0 0# 0 0 0 0
C Cc CC CC C C C C
Qc3h Q Q Q Q<C <D < < < <
DdO 
 
 
 
S$Y    ,$sDy/    *& &T & & & & & &r   rq   c                 ~    dt          |           i}|r|                    |           t          j        |d          S )zReturn a JSON error string for tool handlers.

    >>> tool_error("file not found")
    '{"error": "file not found"}'
    >>> tool_error("bad input", success=False)
    '{"error": "bad input", "success": false}'
    r   Fensure_ascii)r&   r   r   r   )messageextrar   s      r   
tool_errorr  3  sC     s7||$F e:f51111r   c                 ^    | t          j        | d          S t          j        |d          S )a  Return a JSON result string for tool handlers.

    Accepts a dict positional arg *or* keyword arguments (not both):

    >>> tool_result(success=True, count=42)
    '{"success": true, "count": 42}'
    >>> tool_result({"key": "value"})
    '{"key": "value"}'
    NFr   )r   r   )datar   s     r   tool_resultr  A  s4     z$U3333:f51111r   r    )r
   N)&r\   r   r=   r   loggingrv   rb   pathlibr   typingr   r   r   r   r   	getLoggerrY   rA   ASTrg   r   r-   r&   rH   rJ   rf   r_   r   r   __annotations__Lockrd   rk   rn   rq   r   r  r  r^   r   r   <module>r     s@      


                  6 6 6 6 6 6 6 6 6 6 6 6 6 6		8	$	$
SW 
 
 
 
 
G G$ G G G G htn S	    (A A A A A A A AX  68heTk 223 8 8 8%y~''  d    $       E& E& E& E& E& E& E& E&R <>>&2C 2 2 2 22 2 2 2 2 2 2 2r   