def cauchy_mult_torch(v: torch.Tensor, z: torch.Tensor, w: torch.Tensor,
                      symmetric=True) -> torch.Tensor:
    """
    v: (B, N)
    z: (L)
    w: (B, N)
    symmetric: whether to assume that v and w contain complex conjugate pairs, of the form
    [v_half, v_half.conj()] and [w_half, w_half.conj()]
    """
    if not symmetric:
        return (rearrange(v, 'b n -> b 1 n') / (rearrange(z, 'l -> l 1') - rearrange(w, 'b n -> b 1 n'))).sum(dim=-1)
    else:
        N = v.shape[-1]
        assert N % 2 == 0
        vv = rearrange(v[:, :N // 2], 'b n -> b 1 n')
        zz = rearrange(z, 'l -> l 1')
        ww = rearrange(w[:, :N // 2], 'b n -> b 1 n')
        # return 2 * ((zz * vv.real - vv.real * ww.real - vv.imag * ww.imag)
        #             / (zz * zz - 2 * zz * ww.real + ww.abs().square())).sum(dim=-1)
        return (vv / (zz - ww) + vv.conj() / (zz - ww.conj())).sum(dim=-1)

    v: (B, N)
    z: (L)
    w: (B, N)
    symmetric: whether to assume that v and w contain complex conjugate pairs, of the form
    [v_half, v_half.conj()] and [w_half, w_half.conj()]
    
analyze/convnexts4nd/extensions/kernels/cauchy.py