'Performs XNOR GEMM
C = A * B
Arguments:
A (Tensor): left-hand side operand.
B (Tensor): right-hand side operand.
C (Tensor): output operand'

def xnor_compound_dot(self, A, B, C, beta=0.0, bsum=None):

    assert (A.dtype == B.dtype == C.dtype)
    assert (A.shape[0] == C.shape[0])
    assert (B.shape[1] == C.shape[1])
    assert (A.shape[1] == B.shape[0])
    assert ((A.shape[1] % 32) == 0)
    blocks = (lambda x: max(int(math.ceil(x)), 1))
    pack_rows_kernel = binary.pack_rows()
    PACK_ROW_BLOCK_SIZE = 64
    Ab = self.empty((A.shape[0], int((A.shape[1] / 32))), dtype=np.uint32)
    pack_rows_params = [(blocks(((Ab.shape[0] * Ab.shape[1]) / PACK_ROW_BLOCK_SIZE)), 1), (PACK_ROW_BLOCK_SIZE, 1, 1), self.stream, A.gpudata, Ab.gpudata, (Ab.shape[0] * Ab.shape[1])]
    pack_rows_kernel.prepared_async_call(*pack_rows_params)
    pack_cols_kernel = binary.pack_cols()
    PACK_COL_BLOCK_SIZE = 32
    Bb = self.empty((int((B.shape[0] / 32)), B.shape[1]), dtype=np.uint32)
    pack_cols_params = [(blocks((B.shape[1] / PACK_COL_BLOCK_SIZE)), blocks((B.shape[0] / PACK_COL_BLOCK_SIZE)), 1), (PACK_COL_BLOCK_SIZE, PACK_COL_BLOCK_SIZE, 1), self.stream, B.gpudata, Bb.gpudata, B.shape[0], B.shape[1]]
    pack_cols_kernel.prepared_async_call(*pack_cols_params)
    xnor_kernel = binary.XNOR_gemm()
    XNOR_BLOCK_SIZE = 16
    xnor_params = [(blocks((B.shape[1] / XNOR_BLOCK_SIZE)), blocks((A.shape[0] / XNOR_BLOCK_SIZE)), 1), (XNOR_BLOCK_SIZE, XNOR_BLOCK_SIZE, 1), self.stream, Ab.gpudata, Bb.gpudata, C.gpudata, A.shape[0], Ab.shape[1], B.shape[1]]
    xnor_kernel.prepared_async_call(*xnor_params)
    if (bsum is not None):
        bsum[:] = self.sum(C, 1)
    return C
