<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def zip_ll(data, means, M):
 """ Calculates the zero-inflated Poisson log-likelihood. Args: data (array):
 genes x cells means (array):
 genes x k M (array):
 genes x k - this is the zero-inflation parameter. Returns: cells x k array of log-likelihood for each cell/cluster pair. """

    genes, cells = data.shape
    clusters = means.shape[1]
    ll = np.zeros((cells, clusters))
    d0 = (data==0)
    d1 = (data>0)
    for i in range(clusters):
        means_i = np.tile(means[:,i], (cells, 1))
        means_i = means_i.transpose()
        L_i = np.tile(M[:,i], (cells, 1))
        L_i = L_i.transpose()
        ll_0 = np.log(L_i + (1 - L_i)*np.exp(-means_i))
        ll_0 = np.where((L_i==0) & (means_i==0), -means_i, ll_0)
        # not including constant factors
        ll_1 = np.log(1 - L_i) + xlogy(data, means_i) -  means_i
        ll_0 = np.where(d0, ll_0, 0.0)
        ll_1 = np.where(d1, ll_1, 0.0)
        ll[:,i] = np.sum(ll_0 + ll_1, 0)
    return ll