"""
Diagnostic: PV with V = all ones (64, 128) MN-major.
O[m, d] should be sum_k P[m, k] for all d (all columns identical).
If columns differ, the PV MMA is reading V incorrectly.
Also test: PV with V = single element (d=0, k=0) = 1, rest 0.
O[m, 0] = P[m, 0], all other entries 0.
"""
import torch, cutlass, cutlass.cute as cute, cutlass.utils as utils, cutlass.pipeline as pipeline
from cutlass.cute.nvgpu import cpasync, tcgen05, OperandMajorMode
from cutlass import Float32, BFloat16, Int32, Boolean, const_expr
from cutlass.utils import LayoutEnum
from cutlass.utils.tmem_allocator import find_tmem_tensor_col_offset
import cuda.bindings.driver as cuda
import cutlass.torch as ct

# Reuse the same kernel as test_diag_v_truncid.py but with different V

def run_test(v_data, desc, head_dim=64):
    m, n = 128, 128
    q = torch.randn(m, head_dim, 1, dtype=torch.bfloat16, device='cuda')
    k = torch.randn(n, head_dim, 1, dtype=torch.bfloat16, device='cuda')
    v = v_data.as_strided((head_dim, n), (1, head_dim)).unsqueeze(-1)
    c = torch.zeros(m, head_dim, 1, dtype=torch.bfloat16, device='cuda')

    qf = q[:,:,0].float(); kf = k[:,:,0].float(); vf = v_data.float()
    ref = (qf @ kf.T).bfloat16().float() @ vf.T

    mQ = ct.from_dlpack(q).mark_layout_dynamic(leading_dim=ct.get_leading_dim(q))
    mK = ct.from_dlpack(k).mark_layout_dynamic(leading_dim=ct.get_leading_dim(k))
    mV = ct.from_dlpack(v).mark_layout_dynamic(leading_dim=ct.get_leading_dim(v))
    mC = ct.from_dlpack(c).mark_layout_dynamic(leading_dim=ct.get_leading_dim(c))
    stream = cuda.CUstream(torch.cuda.current_stream().cuda_stream)
    
    # Import the kernel from test_diag_v_truncid
    from test_diag_v_truncid import DiagVTruncIdKernel
    kernel = DiagVTruncIdKernel(mma_tiler_mn=(128, 128), head_dim=head_dim)
    print(f'Compiling {desc}...', flush=True)
    compiled = cute.compile(kernel, mQ, mK, mV, mC, stream)
    print(f'Running {desc}...', flush=True)
    compiled(mQ, mK, mV, mC, stream)
    torch.cuda.synchronize()
    out = c[:,:,0].float()
    cos = torch.nn.functional.cosine_similarity(out.flatten().unsqueeze(0), ref.flatten().unsqueeze(0)).item()
    print(f'{desc}: cosine {cos:.6f} {"PASS" if cos >= 0.99 else "FAIL"}')
    
    if cos < 0.99:
        print(f'  O[0,:5] = {out[0,:5].tolist()}')
        print(f'  ref[0,:5] = {ref[0,:5].tolist()}')
        # Check if columns are all the same (for all-ones V)
        if v_data.abs().sum() > 0:
            col0 = out[:, 0]
            all_same = all(torch.allclose(out[:, d], col0, atol=0.1) for d in range(min(8, head_dim)))
            print(f'  All columns same? {all_same}')

# Test 1: All ones V
v_ones = torch.ones(64, 128, dtype=torch.bfloat16, device='cuda')
run_test(v_ones, "All-ones V")

# Test 2: Single element V
v_single = torch.zeros(64, 128, dtype=torch.bfloat16, device='cuda')
v_single[0, 0] = 1.0
run_test(v_single, "Single-element V")
