def deleterious_permutation(obs_del,
                            context_counts,
                            context_to_mut,
                            seq_context,
                            gene_seq,
                            num_permutations=10000,
                            stop_criteria=100,
                            pseudo_count=0,
                            max_batch=25000):
    """Performs null-permutations for deleterious mutation statistics
    in a single gene.

    Parameters
    ----------
    context_counts : pd.Series
        number of mutations for each context
    context_to_mut : dict
        dictionary mapping nucleotide context to a list of observed
        somatic base changes.
    seq_context : SequenceContext
        Sequence context for the entire gene sequence (regardless
        of where mutations occur). The nucleotide contexts are
        identified at positions along the gene.
    gene_seq : GeneSequence
        Sequence of gene of interest
    num_permutations : int, default: 10000
        number of permutations to create for null
    pseudo_count : int, default: 0
        Pseudo-count for number of deleterious mutations for each
        permutation of the null distribution. Increasing pseudo_count
        makes the statistical test more stringent.

    Returns
    -------
    del_count_list : list
        list of deleterious mutation counts under the null
    """
    mycontexts = context_counts.index.tolist()
    somatic_base = [base
                    for one_context in mycontexts
                    for base in context_to_mut[one_context]]

    # calculate the # of batches for simulations
    max_batch = min(num_permutations, max_batch)
    num_batches = num_permutations // max_batch
    remainder = num_permutations % max_batch
    batch_sizes = [max_batch] * num_batches
    if remainder:
        batch_sizes += [remainder]

    num_sim = 0
    null_del_ct = 0
    for j, batch_size in enumerate(batch_sizes):
        # stop iterations if reached sufficient precision
        if null_del_ct >= stop_criteria:
            #j = j - 1
            break

        # get random positions determined by sequence context
        tmp_contxt_pos = seq_context.random_pos(context_counts.iteritems(),
                                                batch_size)
        tmp_mut_pos = np.hstack(pos_array for base, pos_array in tmp_contxt_pos)

        # determine result of random positions
        for i, row in enumerate(tmp_mut_pos):
            # get info about mutations
            tmp_mut_info = mc.get_aa_mut_info(row,
                                              somatic_base,
                                              gene_seq)

            # calc deleterious mutation info
            tmp_del_count = cutils.calc_deleterious_info(tmp_mut_info['Reference AA'],
                                                         tmp_mut_info['Somatic AA'],
                                                         tmp_mut_info['Codon Pos'])

            # update empricial null distribution
            if tmp_del_count >= obs_del: null_del_ct += 1

            # stop if reach sufficient precision on p-value
            if null_del_ct >= stop_criteria:
                break
        # update number of simulations
        num_sim += i + 1

    #num_sim = j*max_batch + i+1
    del_pval = float(null_del_ct) / (num_sim)

    return del_pval