<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 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):
 """Filter genes based on number of cells or counts. Keep genes that have at least `min_counts` counts or are expressed in at least `min_cells` cells or have at most `max_counts` counts or are expressed in at most `max_cells` cells. Only provide one of the optional parameters `min_counts`, `min_cells`, `max_counts`, `max_cells` per call. Parameters data : :class:`~anndata.AnnData`, `np.ndarray`, `sp.spmatrix` The (annotated) data matrix of shape `n_obs` × `n_vars`. Rows correspond to cells and columns to genes. min_counts : `int`, optional (default: `None`) Minimum number of counts required for a gene to pass filtering. min_cells : `int`, optional (default: `None`) Minimum number of cells expressed required for a gene to pass filtering. max_counts : `int`, optional (default: `None`) Maximum number of counts required for a gene to pass filtering. max_cells : `int`, optional (default: `None`) Maximum number of cells expressed required for a gene to pass filtering. min_counts_u : `int`, optional (default: `None`) Minimum number of unspliced counts required for a gene to pass filtering. min_cells_u : `int`, optional (default: `None`) Minimum number of unspliced cells expressed required for a gene to pass filtering. max_counts_u : `int`, optional (default: `None`) Maximum number of unspliced counts required for a gene to pass filtering. max_cells_u : `int`, optional (default: `None`) Maximum number of unspliced cells expressed required for a gene to pass filtering. min_shared_counts: `int`, optional (default: `None`) Minimum number of counts (in cells expressed simultaneously in unspliced and spliced) required for a gene. min_shared_cells: `int`, optional (default: `None`) Minimum number of cells required for a gene to be expressed simultaneously in unspliced and spliced. copy : `bool`, optional (default: `False`) Determines whether a copy is returned. Returns ------- Filters the object and adds `n_counts` to `adata.var`. """

    adata = data.copy() if copy else data

    # set initial cell sizes before filtering
    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:  # shared counts/cells
            _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:  # shared counts/cells
            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