def filter_genes(data, min_counts=None, min_cells=None, max_counts=None, max_cells=None,
                 min_counts_u=None,  min_cells_u=None, max_counts_u=None, max_cells_u=None,
                 min_shared_counts=None, min_shared_cells=None, copy=False):
    adata = data.copy() if copy else data
    set_initial_size(adata)
    layers = [layer for layer in ['spliced', 'unspliced'] if layer in adata.layers.keys()]
    if min_shared_counts is not None or min_shared_cells is not None: layers.extend(['shared'])
    for layer in layers:
        if layer is 'spliced':
            _min_counts, _min_cells, _max_counts, _max_cells = min_counts, min_cells, max_counts, max_cells
        elif layer is 'unspliced':
            _min_counts, _min_cells, _max_counts, _max_cells = min_counts_u, min_cells_u, max_counts_u, max_cells_u
        else:
            _min_counts, _min_cells, _max_counts, _max_cells = min_shared_counts, min_shared_cells, None, None
        if layer in adata.layers.keys():
            X = adata.layers[layer]
        else:
            Xs, Xu = adata.layers['spliced'], adata.layers['unspliced']
            nonzeros = (Xs > 0).multiply(Xu > 0) if issparse(Xs) else (Xs > 0) * (Xu > 0)
            X = nonzeros.multiply(Xs) + nonzeros.multiply(Xu) if issparse(nonzeros) else nonzeros * (Xs + Xu)
        gene_subset = np.ones(adata.n_vars, dtype=bool)
        if _min_counts is not None or _max_counts is not None:
            gene_subset, _ = filter(X, min_counts=_min_counts, max_counts=_max_counts)
            adata._inplace_subset_var(gene_subset)
        if _min_cells is not None or _max_cells is not None:
            gene_subset, _ = filter(X, min_cells=_min_cells, max_cells=_max_cells)
            adata._inplace_subset_var(gene_subset)
        s = np.sum(~gene_subset)
        if s > 0:
            logg.info('Filtered out {} genes that are detected'.format(s), end=' ')
            if _min_cells is not None or _min_counts is not None:
                logg.info('in less than', str(_min_cells) + ' cells (' + str(layer) + ').' if _min_counts is None
                          else str(_min_counts) + ' counts (' + str(layer) + ').', no_indent=True)
            if max_cells is not None or max_counts is not None:
                logg.info('in more than ', str(_max_cells) + ' cells(' + str(layer) + ').' if _max_counts is None
                          else str(_max_counts) + ' counts (' + str(layer) + ').', no_indent=True)
    return adata if copy else None