o
    پi                     @   s  d dl Z d dlZd dlZd dlZd dlmZ dd e_d dl	m
Z
 d dlmZ d dlmZmZ d dlmZ d d	lmZ d d
lmZ d dlmZmZ d dlmZmZmZ d dlmZ d dlm Z  i dddej!dej!dddddddddddddd d!dd"dd#d d$d%d&d d'dd(dd)dd*Z"d+Z#d,d%d-d.d.d/d0d1d2Z$d\d3d4Z%d5d%d,d,d6d7d8d,dd,d9d7gd:d%dd,d;d7d<d,dd,d=d7gd>d?dd,d@d7dAd?dddBd7gdCd%dd,dDd7dEd%dddFd7d8d?dd,dGd7gdHd%dd,dId7dJd?dd,dKd7dLd,dMddNd7dOdPdQd,dRd7gdSZ&G dTdU dUZ'd]dVdWZ(e)ej*+  p(e  dXG dYdZ dZe Z,e-d[kr>e.  dS dS )^    N)dp_attentionc                   C   s   dS )N    r   r   r   a/home/ubuntu/.local/lib/python3.10/site-packages/sglang/test/attention/test_trtllm_mla_backend.py<lambda>   s    r   )AttentionArch)FlashInferMLAAttnBackend)TRTLLMMLABackendTRTLLMMLADecodeMetadata)get_num_page_per_block_flashmla)RadixAttention)MLATokenToKVPool)ForwardBatchForwardMode)
ServerArgsget_global_server_args$set_global_server_args_for_scheduler)is_flashinfer_available)CustomTestCasedevicecudadtypekv_cache_dtypecontext_leni   max_bs@   	tolerance{Gz?
seed_cache*   seed_qkv{   num_attention_heads   kv_lora_ranki   qk_nope_head_dimqk_rope_head_dim
v_head_dimnum_kv_headsr   layer_idtp_q_head_numtp_k_head_num   )prefill_head_dimprefill_v_head_dimi'      (         ?i   yarndeepseek_yarn)	beta_fast	beta_slowfactormscalemscale_all_dim original_max_position_embeddingstype	rope_typec              	   C   sX   ddl m} |p| d }| dt}|| d | d | d t|d|d}|j||_|S )	Nr   )get_rope_wrapperr   rope_scalingr&   r   F)	head_size
rotary_dimmax_positionbaser=   is_neox_styler   )"sglang.srt.layers.rotary_embeddingr<   getROPE_SCALING_CONFIG	ROPE_BASEcos_sin_cacheto)configr   r<   devr=   rotaryr   r   r   build_rotary_embD   s   	rL   singlezMinimal smoke test)name
batch_sizemax_seq_len	page_sizedescriptionbatchzMedium-scale batchsingle_fp16zSingle FP16 vs reference
batch_fp16zBatch FP16 vs referencepage_32   z32-token pagespage_64z64-token pagesbasiczSingle sequencebasic_different_pagesizezDifferent page sizezBatch shapessingle_sequencezSingle sequence metadatabatch_mixed_lengthszMixed sequence lengthslarge_batch   zLarge batch stress testedge_case_short      zSub-page sequences)basic_functionalityoutput_matchpage_size_consistencyshape_sanity_testsmetadata_testsc                   @   s   e Zd ZdZdd ZdS )MockModelRunnerz2Minimal fake ModelRunner for testing MLA backends.c                    s   d | _  d | _ d | _ d | _t | _tdd d tj d  d	  d
  d  d d d
  d  d  t	 fddd	| _
 d }| j
j}tj|| tj| j d||}tdd||d| _t||  d | j d	  d d| j dd| _d S )Nr   r   r   rQ   ModelConfigr   r   r"   r$   r%   r&   r'   r1   g      ?c                    s    d S )Nr(   r   )_rI   r   r   r      s    z*MockModelRunner.__init__.<locals>.<lambda>)	r   attention_archr"   r$   r%   r&   r'   scalingget_num_kv_headsr   r   r   	TokenPool)sizereq_to_tokenr   F)rp   rQ   r   r$   r&   	layer_numr   enable_memory_saver)r   r   r   rQ   r   server_argsr:   r   MLAstaticmethodmodel_configr   torcharangeint32reshapereq_to_token_poolr   token_to_kv_pool)selfrI   r   max_ctxrq   r   rj   r   __init__   sZ   




zMockModelRunner.__init__N)__name__
__module____qualname____doc__r   r   r   r   r   rg      s    rg   c                 C   s  | j |j ksJ d| j  d|j  | j|jks$J d| j d|j t|  r/J dt| r:J dt|  rEJ dt| rPJ d| |  }|  }|	  }tj
| |||d}|std	|d
d|d
d|  | }tj|td| dj}td t|D ]2\}	}
t|
  | j }| |  }||  }td| d|d
d|d
dt|| d
 q|S )z'Compare outputs with detailed analysis.zShape mismatch:  vs zDtype mismatch: zTRTLLM output contains NaNzReference output contains NaNzTRTLLM output contains InfzReference output contains InfrtolatolzComparison failed: max_diff=z.6fz, mean_diff=z, tolerance=   )kzTop 5 differences:z  [z
]: TRTLLM=z, Reference=z, diff=)shaper   rx   isnananyisinfabsmaxitemmeanallcloseprintflattentopkminnumelindices	enumeratenpunravel_indexcpunumpy)
trtllm_outreference_outr   diffmax_diff	mean_diff	all_close	flat_difftop_diff_indicesiidx	idx_tupletrt_valref_valr   r   r   compare_outputs  s>   (r   zCUDA + flashinfer requiredc                   @   s   e Zd ZdZedd Zedd Zdd Zd(d	d
Zd)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 d! Zd"d# Zd$d% Zd&d' ZdS )*TestTRTLLMMLAzATest suite for TRTLLM MLA backend with centralized configuration.c                 C   s   t dd}d|_t| dS )z&Set up global server args for testing.dummy)
model_pathFN)r   enable_dp_attentionr   )clsrt   r   r   r   
setUpClassB  s   
zTestTRTLLMMLA.setUpClassc                 C   s   d S Nr   )r   r   r   r   tearDownClassI  s   zTestTRTLLMMLA.tearDownClassc                 C   s   t  }|| |S )z+Merge test case with default configuration.)DEFAULT_CONFIGcopyupdate)r~   	test_caserI   r   r   r   _merge_configM  s   
zTestTRTLLMMLA._merge_configFc           
   	   C   s   t |}t |}t|}t|}|s|d |d  n|d }|s$|d n|d }t|d ||jj|d |d |d	d
}	|||||	fS )z6Create model runners, backends, and layer for testing.r$   r&   r-   r'   r.   r"   r(   r)   attn_mqa)	num_headshead_dimrl   r(   r)   r'   prefix)rg   r	   r   r   rw   rl   )
r~   rI   
is_prefillmodel_runner_trtllmmodel_runner_referencetrtllm_backendreference_backendr   r'   layerr   r   r   _create_model_componentsS  s2   z&TestTRTLLMMLA._create_model_componentsNc                 C   s   |d }|p	|d }t j||d |d f|d |d}t j||d |d f|d |d}t j||d |d f|d |d}t j||d |d f|d |d}	t j||d |d f|d |d}
||||	|
fS )	aw  Create Q, K, V random tensors for given batch size with separate MLA components.

        Args:
            batch_size: Batch size.
            config: Configuration dict with model dims and device.
            dtype_override: Optional torch dtype to override config["dtype"].

        Returns:
            Tuple of (q_nope, q_rope, k_nope, k_rope, v, cos_sin_cache)
        r   r   r"   r$   rn   r&   r(   r'   )rx   randn)r~   rO   rI   dtype_overrider   target_dtypeq_nopeq_ropek_nopek_ropevr   r   r   _create_qkv_tensorsy  s8   z!TestTRTLLMMLA._create_qkv_tensorsc                 C   s   t |tjdd|df|d dtj||d dt|  tjtj||d d||	 |d	}|j
|_
|j|_tj||d d|_|S )-Create a forward batch for the given backend.r   d   r   r   r   )	rO   	input_idsout_cache_locseq_lens_sumforward_modereq_pool_indicesseq_lensseq_lens_cpuattn_backend)r   rx   randintry   intsumr   r   DECODEr   r|   r}   	positions)r~   rO   r   backendmodel_runnerrI   fbr   r   r   _create_forward_batch  s   z#TestTRTLLMMLA._create_forward_batchc                 C   s   t |d  |D ][}t |d  t|D ]M}t||  }t|d D ]<}	t jd|d f|d |d d}
t jd|d f|d |d d}|jj||	f }|j	||
d|
d|d q&qq	d	S )
z8Populate KV cache with identical data for both backends.r   r   r$   r   r   rn   r&   r   N)rx   manual_seedranger   r   r   r|   rq   r}   set_mla_kv_buffer	unsqueezesqueeze)r~   rO   r   model_runnersr   rI   r   r   seq_len	token_idxcache_k_nopecache_k_rope	cache_locr   r   r   _populate_kv_cache  s8   

z TestTRTLLMMLA._populate_kv_cachec                 C   s  t d td D ]}| j|d d t d|d  d|d   | |}|d }|d	 }| |\}}}}}|d
krJtj||d
 g|d d}	nt|d  tj|d
 |d |f|d d}	||	d< | 	||	|||}
|
|
 | ||	|g|| t|d  | ||\}}}}}|j||d||
||d}||d |d  f}| |j| | |j|d  | t|  | t|  W d   n1 sw   Y  qdS )z,Test basic functionality with minimal setup.z%
Running basic functionality tests...rb   rN   r   
  Testing : rR   rO   rP      r   r   r   r   r   r    Nr   r   r"   r'   r   )r   
TEST_CASESsubTestr   r   rx   tensorr   r   r   init_forward_metadatar   r   forward_decodeassertEqualr   r   assertFalser   r   r   )r~   r   rI   rO   rP   r   ri   r   r   r   r   r   r   r   r   r   outputexpected_shaper   r   r   test_basic_functionality  sZ   


z&TestTRTLLMMLA.test_basic_functionalityc                 C   st  t d td D ].}| j|d d t d|d  d|d   | |}|d }|d	 }|d
 tjk}| |\}}}}	}
t|d  tjd||f|d d}||d< | 	||
 |||}| 	||
 |	||}|| |	| | ||||g|
| t|d  | ||\}}}}}|
 |
 |
 |
 |
 f\}}}}}|d }i }|rt|}||j||\}}|j|jd}|j}|tj|}|tj|}|tj|}|tj|}|j||d|
|f||d|}|	j||||
|||d}t|||d}| |d|d  d||      W d   n	1 s2w   Y  qdS )zFTest that TRTLLM and FlashInfer MLA backends produce matching outputs.z(
Running decode output matching tests...rc   rN   r   r   r   rR   rO   rP   r   r   r   r   r   r   r    r   )rG   is_neoxNr   r   z>TRTLLM and Reference outputs differ beyond tolerance. Config: , Max diff: )r   r   r   r   rx   float8_e4m3fnr   r   r   r   cloner   r   r   rL   r   rG   rB   r   rH   r   r   
assertTruer   r   r   )r~   r   rI   rO   rP   use_fp8r   r   r   r   r   r   	fb_trtllmfb_reference
q_nope_ref
q_rope_ref
k_nope_ref
k_rope_refv_ref
q_nope_trt
q_rope_trt
k_nope_trt
k_rope_trtv_trtr   
extra_args
rotary_embr   
out_trtllmout_referencecomparison_passedr   r   r   test_decode_output_match"  s   
	
	
	
z&TestTRTLLMMLA.test_decode_output_matchc                 C   s  t d td D ]}| j|d d t d|d  d|d   | |}|d }|d	 }| |\}}}}}t|d
  tjd||f|d d}	||	d< | ||	|||}
|	|
 | 
||	|g|| t|d  | ||\}}}}}|j||d||
||d}||d |d  f}| |j|d|j d|  | t| d | t| d W d   n1 sw   Y  qdS )z4Test output consistency across different page sizes.z'
Running page size consistency tests...rd   rN   r   r   r   rR   rO   rP   r   r   r   r   r   r    Nr   r"   r'   zOutput shape mismatch: r   zOutput contains NaNzOutput contains Inf)r   r   r   r   r   rx   r   r   r   r   r   r   r   r   r   r   r   r   r   )r~   r   rI   rO   rP   r   ri   r   r   r   r   r   r   r   r   r   r   r   r   r   r   test_page_size_consistency  sT   


z(TestTRTLLMMLA.test_page_size_consistencyc                 C   s4  t d td D ]}| j|d d t d|d  d|d   | |}|d }|d	 }| |\}}}}}t|d
  tjd||f|d d}	||	d< | ||	|||}
|	|
 t|d  tj
||d |d f|d |d d}tj
||d |d f|d |d d}tj
||d |d f|d |d d}tj
||d |d f|d |d d}d}|j|||||
||d}||d |d  f}| |j|d|d   | |j|d  | |jjd | t| d|d   | t| d|d   W d   n	1 sw   Y  qdS )z0Smoke test decode across several configurations.z
Running shape sanity tests...re   rN   r   r   r   rR   rO   rP   r   r   r   r   r   r    r"   r$   r   rn   r(   r&   Nr   r'   zOutput shape mismatch for r   zOutput contains NaN for zOutput contains Inf for )r   r   r   r   r   rx   r   r   r   r   r   r   r   r   r   r   r:   r   r   r   r   )r~   r   rI   rO   rP   r   ri   r   r   r   r   r   r   r   r   r   r   r   r   r   r   test_shape_sanity  s   


	zTestTRTLLMMLA.test_shape_sanityc              	   C   s  t d td D ]}| j|d d t d|d  d|d   | |}|d }|d	 }| |\}}}}}t|d
  |dkrNtj|g|d d}	ntjt	d|d |d |f|d d}	||	d< | 
||	|||}
||
 | |j | |jt |j}| |jd | |jjjd | |jjtj | |jjd | | |jdk d W d   n1 sw   Y  qdS )z6Test TRTLLM MLA metadata initialization and structure.z)
Running metadata initialization tests...rf   rN   r   r   r   rR   rO   rP   r   r   r   r   r`   r   z"Block KV indices should be createdr   z6All block indices should be >= -1 (with -1 as padding)N)r   r   r   r   r   rx   r   r   r   r   r   r   assertIsNotNoneforward_decode_metadataassertIsInstancer
   block_kv_indicesr   r   r:   r   rz   r   r  all)r~   r   rI   rO   rP   r   ri   r   r   r   r   metadatar   r   r   test_metadata_initialization/  sT   


z*TestTRTLLMMLA.test_metadata_initializationc                 C   sF  t d ddddddddddddddd	ddd
ddg}|D ]}| j|do | d|d |d d}| |\}}}}}||d }| ||d d| d|d  d ||d  }| ||d d| d|d  d d|d  }	t|d }
t|	|
}| 	|| dd| d W d   n1 sw   Y  q!dS )z#Test block count calculation logic.z,
Running metadata block calculation tests...   r/   r   )r   rQ   expected_min_blocks!   r   r#   r`   r   )scenarior   rQ   rO   rP   rQ   r!  zCalculated blocks (z!) should be >= minimum required ()zTotal tokens (z ) should cover sequence length (r   z6Block count should be multiple of LCM of constraints (N)
r   r   r   r   _calc_padded_blocksassertGreaterEqualr   mathlcmr   )r~   test_scenariosr#  rI   r   ri   r   calculated_blockstotal_tokenstrtllm_constrainttriton_constraintconstraint_lcmr   r   r   test_metadata_block_calculationl  sR   





z-TestTRTLLMMLA.test_metadata_block_calculationc                 C   s  t d td dd D ]}| j|d d t d|d  d|d	   | |}|d
 }|d }| |\}}}}}t|d  |dkrRtj|g|d d}	ntj|d |d |f|d d}	| 	||	|||}
| 
||	|g|| ||
 |j}|j}t|D ]X}|	|  }||}|| dk  }||d  d |d  }| ||d| d| d|  || || dk }t|dkr|jj|d  }| ||k  d|  qW d   n1 sw   Y  qdS )z)Test KV indices creation and correctness.z(
Running KV indices correctness tests...rf   Nr   rN   r   r   r   rR   rO   rP   r   r   r   r   r   rQ   z	Sequence z should have at least z valid blocks, got zAll block indices should be < )r   r   r   r   r   rx   r   r   r   r   r   r   r  r  r   r   r&  r   r'  lenr}   rp   r  r  )r~   r   rI   rO   rP   r   ri   r   r   r   r   r  r  r   r   expected_blocksvalid_indicesmin_required_blocksvalid_block_indicesmax_possible_blocksr   r   r   $test_metadata_kv_indices_correctness  sn   




z2TestTRTLLMMLA.test_metadata_kv_indices_correctnessc              
   C   s&  t d | dddd}| |\}}}}}|d }|j||d | d | |j tj|f|d |d	 d
}tj||d	 d
}|j	||||dt
jdd | ||j |j| }	| |	j tj|d d |d d |f|d	 d
}
|j|||
|
  dt
jd|
 d |j}| | dS )z;Test metadata compatibility with CUDA graph capture/replay.z*
Running CUDA graph compatibility tests...r`   r   r/   r$  rO   rP   )r   max_num_tokensr   r   N)bs
num_tokensr   r   encoder_lensr   	spec_infor   r   )r9  r   r   r   r;  r   r<  r   )r   r   r   init_cuda_graph_stater  decode_cuda_graph_kv_indicesrx   fullry   (init_forward_metadata_capture_cuda_graphr   r   assertIndecode_cuda_graph_metadatar  r   'init_forward_metadata_replay_cuda_graphr   r   r   r  )r~   rI   r   ri   r   r   rO   r   r   capture_metadatanew_seq_lensreplay_metadatar   r   r   &test_metadata_cuda_graph_compatibility  sX   




z4TestTRTLLMMLA.test_metadata_cuda_graph_compatibilityc                 C   s  t d | dddd}| |\}}}}}tjddg|d d}| |d	 ||||}|| |j}tjddg|d d}	| |d	 |	|||}
||
 |j}| |j	j
|j	j
 tjd
dg|d d}| |d	 ||||}|| |j}| |j	 | |j	j
d |d	  dS )z8Test metadata consistency across multiple forward calls.z&
Running metadata consistency tests...r   r   r/   r$  0   r   r   rO   ra   r   N)r   r   r   rx   r   r   r   r  r   r  r   r  )r~   rI   r   ri   r   r   
seq_lens_1fb_1
metadata_1
seq_lens_2fb_2
metadata_2
seq_lens_3fb_3
metadata_3r   r   r   &test_metadata_consistency_across_calls,  s8   



z4TestTRTLLMMLA.test_metadata_consistency_across_callsc              
   C   s  t d td dd D ]}| j|d d t d|d  d|d	   | |}|d
 }|d }| j|dd\}}}}}	tj|f||d d}
dd }|||
 tj||d tj	d|||}|||
 tj||d tj	d|||}|
| |
| t|d  	ddd}||||\}}}|||||	|dd|	j|	j }|||||	|d}|dd}t|||d}| |d|d  d||      W d   n1 sw   Y  qdS )zCTest prefill (forward) behavior of TRTLLM MLA backend vs reference.z 
Running prefill output tests...rc   Nr   rN   r   zPrefill Testing r   rR   rO   rP   T)r   r   r   c                 S   s   t | tjdd| df|d dtj| |d dt|  ||   ||    t	j
tj| |d d|| dd|d}|j|_|j|_tj| |d d|_|S )r   r   r   r   r   r   F)rO   r   r   r   extend_prefix_lensextend_prefix_lens_cpuextend_seq_lens_cpur   r   r   r   attn_attend_prefix_cachemha_return_lser   )r   rx   r   ry   r   r   r   r   tolistr   EXTENDr|   r}   r   )rO   r   rS  r   r   rI   r   r   r   r   _create_forward_batch_prefills  s6   
z]TestTRTLLMMLA.test_prefill_output_match_self_attention.<locals>._create_forward_batch_prefillr   r   r    c                 S   s   |d }|p	|d }| | }|d }|d }|d }	|d }
t j|||	 f||d}t j|||	 f||d}t j|||
 f||d}|d||	}|d||	}|d||
}|||fS )	zKCreate Q, K, V tensors for prefill, using config for head_num and head_dim.r   r   r*   r+   r-   r.   rn   r  )rx   r   view)rO   r   rI   r   r   r   r,  r*   r+   r   r'   qr   r   r   r   r   _create_qkv_tensors_prefill  s4   



z[TestTRTLLMMLA.test_prefill_output_match_self_attention.<locals>._create_qkv_tensors_prefillFr  r   r   r   zFTRTLLM and Reference prefill outputs differ beyond tolerance. Config: r   r   )r   r   r   r   r   rx   r?  r  zerosrz   r   r   forward_extendr\  r*   r'   rD   r   r  r   r   r   )r~   r   rI   rO   rP   r   r   r   r   r   r   rZ  r  r  r^  r]  r   r   r  r  r   r  r   r   r   (test_prefill_output_match_self_attentionW  s   
	*



% z6TestTRTLLMMLA.test_prefill_output_match_self_attentionc                 C   s<  ddl m}m} tjfdd}tjfdd}| jdd d	}d
}d}d}|| ||||\}	}
}}d}|| f}|| |	|
|||||||d	 t|D ]O}||  }t|D ] }||  }|| }|	| }|
||f }tjj	||ddd qWt||D ]}|
||f }| 
t|t|d| d| d q}qKW d   n1 sw   Y  | jddc d	}d
}d}d}|| ||||\}}}}d}|| f}|| |||||||||d	 t|D ]+}||  }||  }t|D ]}|||f }|||  }tjj	||ddd qqW d   dS 1 sw   Y  dS )zcTest TRTLLM MLA Triton kernels: pad_draft_extend_query_kernel and unpad_draft_extend_output_kernel.r   )pad_draft_extend_query_kernel unpad_draft_extend_output_kernelc                 S   s   t d}t jd|d |f|t jd}t j|d |t jd}t j|dd|dd< |d  }	t j|	||||d}
t j||||||d}|
|||fS )z$Create test data for kernel testing.r   r   r[  r   dimNr  )rx   r   r   rz   r_  cumsumr   r   )r~   rO   rP   r   r   r   r   r   cum_seq_lensr,  q_inputpadded_qr   r   r   _create_test_data  s   

zTTestTRTLLMMLA.test_draft_extend_padding_unpadding_kernels.<locals>._create_test_datac                 S   s   t d}t jd|d |f|t jd}t j|d |t jd}t j|dd|dd< t j||||||d}	|d  }
t j|
||||d}|	|||fS )z*Create test data for unpad kernel testing.r   r   r[  r   rd  Nr  )	rx   r   r   rz   r_  rf  r   r   empty)r~   rO   token_per_batchr*   r'   r   r   accept_lengthscum_accept_lengthsraw_outr,  r   r   r   r   _create_test_output_data  s*   
	

z[TestTRTLLMMLA.test_draft_extend_padding_unpadding_kernels.<locals>._create_test_output_datapad_kernel_basic)testr`   rW   ra   r   )	q_ptrpadded_q_ptrseq_lens_q_ptr
cumsum_ptrrO   rP   r   r   
BLOCK_SIZEgh㈵>gư>r   z	Position z
 in batch z should be zeroNunpad_kernel_basic)	raw_out_ptr
output_ptraccept_length_ptrrv  rO   rl  r*   r'   rw  ).sglang.srt.layers.attention.trtllm_mla_backendrb  rc  rx   float32r   r   r   testingassert_closer  r   
zeros_like)r~   rb  rc  rj  rp  rO   rP   r   r   rh  ri  r   rg  rw  gridr   r   posinput_start	input_pos
input_dataoutput_datarl  r*   r'   ro  r   rm  rn  
accept_lenoutput_startr   r   r   +test_draft_extend_padding_unpadding_kernels  s   
"
)

4


$z9TestTRTLLMMLA.test_draft_extend_padding_unpadding_kernels)Fr   )r   r   r   r   classmethodr   r   r   r   r   r   r   r   r  r  r  r  r0  r7  rG  rR  ra  r  r   r   r   r   r   ;  s0    



&/"A :R=6J@+ r   __main__r   )r   )/r(  unittestr   r   rx   sglang.srt.layersr   _dp_attnget_attention_tp_sizesglang.srt.configs.model_configr   2sglang.srt.layers.attention.flashinfer_mla_backendr   r|  r	   r
   !sglang.srt.layers.attention.utilsr   !sglang.srt.layers.radix_attentionr    sglang.srt.mem_cache.memory_poolr   ,sglang.srt.model_executor.forward_batch_infor   r   sglang.srt.server_argsr   r   r   sglang.srt.utilsr   sglang.test.test_utilsr   bfloat16r   rF   rE   rL   r   rg   r   skipIfr   is_availabler   r   mainr   r   r   r   <module>   sN   


%|
:.        
d