def Func(arg_0):
    """
    Function to remove older files. This is called either in substep 1 or after
    the final substep so that tempfiles are retained for restarting interrupted
    jobs until we're sure they're no longer needed.
    """

    ## remove align-related tmp files
    arg_1 = glob.glob(os.path.join(arg_0.tmpdir, "*.fa"))
    arg_2 = glob.glob(os.path.join(arg_0.tmpdir, "*.npy"))
    for arg_3 in arg_1 + arg_2:
        if os.path.exists(arg_3):
            os.remove(arg_3)

    ## remove cluster related files
    arg_4 = [
        os.path.join(arg_0.dirs.across, arg_0.name+".utemp"),
        os.path.join(arg_0.dirs.across, arg_0.name+".htemp"),
        os.path.join(arg_0.dirs.across, arg_0.name+"_catcons.tmp"),
        os.path.join(arg_0.dirs.across, arg_0.name+"_cathaps.tmp"),
        os.path.join(arg_0.dirs.across, arg_0.name+"_catshuf.tmp"),
        os.path.join(arg_0.dirs.across, arg_0.name+"_catsort.tmp"),
        os.path.join(arg_0.dirs.across, arg_0.name+".tmparrs.h5"),
        os.path.join(arg_0.dirs.across, arg_0.name+".tmp.indels.hdf5"),
        ]
    for arg_5 in arg_4:
        if os.path.exists(arg_5):
            os.remove(arg_5)

    ## remove singlecat related h5 files
    arg_6 = glob.glob(os.path.join(arg_0.dirs.across, '*.tmp.h5'))
    for arg_7 in arg_6:
        if os.path.exists(arg_7):
            os.remove(arg_7)


def Func(arg_0):
    """ cleanup for assembly object """

    ## build s2 results data frame
    arg_0.stats_dfs.s2 = arg_0._build_stat("s2")
    arg_0.stats_files.s2 = os.path.join(arg_0.dirs.edits, 's2_rawedit_stats.txt')

    ## write stats for all samples
    with io.open(arg_0.stats_files.s2, 'w', encoding='utf-8') as outfile:
        arg_0.stats_dfs.s2.fillna(value=0).astype(np.int).to_string(outfile)


def Func(arg_0, arg_1, arg_2, arg_3):
    """
    Filter for samples that are already finished with this step, allow others
    to run, pass them to parallel client function to filter with cutadapt.
    """

    ## create output directories
    arg_0.dirs.edits = os.path.join(os.path.realpath(
                                   arg_0.paramsdict["project_dir"]),
                                   arg_0.name+"_edits")
    if not os.path.exists(arg_0.dirs.edits):
        os.makedirs(arg_0.dirs.edits)

    ## get samples
    arg_6 = choose_samples(arg_1, arg_2)

    ## only allow extra adapters in filters==3,
    ## and add poly repeats if not in list of adapters
    if int(arg_0.paramsdict["filter_adapters"]) == 3:
        if not arg_0._hackersonly["p3_adapters_extra"]:
            for arg_7 in ["A"*8, "T"*8, "C"*8, "G"*8]:
                arg_0._hackersonly["p3_adapters_extra"].append(arg_7)
        if not arg_0._hackersonly["p5_adapters_extra"]:
            for arg_7 in ["A"*8, "T"*8, "C"*8, "G"*8]:
                arg_0._hackersonly["p5_adapters_extra"].append(arg_7)
    else:
        arg_0._hackersonly["p5_adapters_extra"] = []
        arg_0._hackersonly["p3_adapters_extra"] = []

    ## concat is not parallelized (since it's disk limited, generally)
    arg_6 = concat_reads(arg_0, arg_6, arg_3)

    ## cutadapt is parallelized by ncores/2 because cutadapt spawns threads
    arg_9 = arg_3.load_balanced_view(targets=arg_3.ids[::2])
    run_cutadapt(arg_0, arg_6, arg_9)

    ## cleanup is ...
    assembly_cleanup(arg_0)


def Func(arg_0, arg_1, arg_2):
    """ concatenate if multiple input files for a single samples """

    ## concatenate reads if they come from merged assemblies.
    if any([len(arg_3.files.fastqs) > 1 for arg_3 in arg_1]):
        ## run on single engine for now
        arg_4 = time.time()
        arg_5 = " concatenating inputs  | {} | s2 |"
        arg_6 = 0
        arg_7 = {}
        for arg_8 in arg_1:
            if len(arg_8.files.fastqs) > 1:
                arg_7[arg_8.name] = arg_2[0].apply(\
                                       concat_multiple_inputs, *(arg_0, arg_8))
            else:
                arg_8.files.concat = arg_8.files.fastqs

        ## wait for all to finish
        while 1:
            arg_6 = sum([arg_3.ready() for arg_3 in arg_7.values()])
            arg_12 = datetime.timedelta(seconds=int(time.time()-arg_4))
            progressbar(len(arg_7), arg_6, arg_5.format(arg_12), spacer=arg_0._spacer)
            time.sleep(0.1)
            if arg_6 == len(arg_7):
                print("")
                break

        ## collect results, which are concat file handles.
        for arg_13 in arg_7:
            if arg_7[arg_13].successful():
                arg_0.samples[arg_13].files.concat = arg_7[arg_13].result()
            else:
                arg_15 = arg_7[arg_13].result()#exception()
                LOGGER.error("error in step2 concat %s", arg_15)
                raise IPyradWarningExit("error in step2 concat: {}".format(arg_15))
    else:
        for arg_8 in arg_1:
            ## just copy fastqs handles to concat attribute
            arg_8.files.concat = arg_8.files.fastqs

    return arg_1


def Func(arg_0, arg_1, arg_2):
    """
    sends fastq files to cutadapt
    """
    ## choose cutadapt function based on datatype
    arg_3 = time.time()
    arg_4 = " processing reads      | {} | s2 |"
    arg_5 = 0
    arg_6 = {}

    ## sort subsamples so that the biggest files get submitted first
    arg_1.sort(key=lambda x: x.stats.reads_raw, reverse=True)
    LOGGER.info([arg_7.stats.reads_raw for arg_7 in arg_1])

    ## send samples to cutadapt filtering
    if "pair" in arg_0.paramsdict["datatype"]:
        for arg_8 in arg_1:
            arg_6[arg_8.name] = arg_2.apply(cutadaptit_pairs, *(arg_0, arg_8))
    else:
        for arg_8 in arg_1:
            arg_6[arg_8.name] = arg_2.apply(cutadaptit_single, *(arg_0, arg_8))

    ## wait for all to finish
    while 1:
        arg_5 = sum([arg_7.ready() for arg_7 in arg_6.values()])
        arg_10 = datetime.timedelta(seconds=int(time.time()-arg_3))
        progressbar(len(arg_6), arg_5, arg_4.format(arg_10), spacer=arg_0._spacer)
        time.sleep(0.1)
        if arg_5 == len(arg_6):
            print("")
            break

    ## collect results, report failures, and store stats. async = sample.name
    for arg_11 in arg_6:
        if arg_6[arg_11].successful():
            arg_12 = arg_6[arg_11].result()

            ## if single cleanup is easy
            if "pair" not in arg_0.paramsdict["datatype"]:
                parse_single_results(arg_0, arg_0.samples[arg_11], arg_12)
            else:
                parse_pair_results(arg_0, arg_0.samples[arg_11], arg_12)
        else:
            print("  found an error in step2; see ipyrad_log.txt")
            LOGGER.error("error in Func(): %s", arg_6[arg_11].exception())


def Func(arg_0, arg_1):
    """
    If multiple fastq files were appended into the list of fastqs for samples
    then we merge them here before proceeding.
    """

    ## if more than one tuple in fastq list
    if len(arg_1.files.fastqs) > 1:
        ## create a cat command to append them all (doesn't matter if they
        ## are gzipped, cat still works). Grab index 0 of tuples for R1s.
        arg_2 = ["cat"] + [i[0] for i in arg_1.files.fastqs]

        arg_3 = ".gz"
        if not arg_1.files.fastqs[0][0].endswith(".gz"):
            arg_3 = ""

        ## write to new concat handle
        arg_4 = os.path.join(arg_0.dirs.edits, arg_1.name+"_R1_concat.fq{}".format(arg_3))
        with open(arg_4, 'w') as cout1:
            arg_5 = sps.Popen(arg_2, stderr=sps.STDOUT, stdout=cout1, close_fds=True)
            arg_6 = arg_5.communicate()[0]
        if arg_5.returncode:
            raise IPyradWarningExit("error in: {}, {}".format(arg_2, arg_6))

        ## Only set conc2 if R2 actually exists
        arg_7 = 0
        if "pair" in arg_0.paramsdict["datatype"]:
            arg_8 = ["cat"] + [i[1] for i in arg_1.files.fastqs]
            arg_7 = os.path.join(arg_0.dirs.edits, arg_1.name+"_R2_concat.fq{}".format(arg_3))
            with open(arg_7, 'w') as cout2:
                arg_9 = sps.Popen(arg_8, stderr=sps.STDOUT, stdout=cout2, close_fds=True)
                arg_10 = arg_9.communicate()[0]
            if arg_9.returncode:
                raise IPyradWarningExit("Error concatenating fastq files. Make sure all "\
                    + "these files exist: {}\nError message: {}".format(arg_8, arg_9.returncode))

        ## store new file handles
        arg_1.files.concat = [(arg_4, arg_7)]
    return arg_1.files.concat


def Func( arg_0, arg_1 ):
    """ Convert vcf from step6 to .loci format to facilitate downstream format conversion """
    arg_2   =  os.path.join( arg_0.dirs.consens, arg_0.name+".vcf" )
    arg_3  =  os.path.join( arg_0.dirs.outfiles, arg_0.name+".loci" )

    importvcf( arg_2, arg_3 )


def Func( arg_0, arg_1 ):
    """ Function for importing a vcf file into loci format. Arguments
        are the input vcffile and the loci file to write out. """

    try:
        ## Get names of all individuals in the vcf
        with open( invcffile, 'r' ) as invcf:
            for arg_2 in invcf:
                if arg_2.split()[0] == "#CHROM":
                    ## This is maybe a little clever. The names in the vcf are everything after
                    ## the "FORMAT" column, so find that index, then slice everything after it.
                    arg_3 = arg_2.split().index( "FORMAT" ) + 1
                    arg_4 = arg_2.split()[ arg_3:]
                    LOGGER.debug( "Got names - %s", arg_4 )
                    break

            print( "wat" )
        ## Get the column to start reading at
    except Exception:
        print( "wat" )


def Func(arg_0):
    """
    A function to find 2 engines per hostname on the ipyclient.
    We'll assume that the CPUs are hyperthreaded, which is why
    we grab two. If they are not then no foul. Two multi-threaded
    jobs will be run on each of the 2 engines per host.
    """
    ## fill hosts with async[gethostname]
    arg_1 = []
    for arg_2 in arg_0.ids:
        arg_3 = arg_0[arg_2]
        if not arg_3.outstanding:
            arg_1.append(arg_3.apply(socket.gethostname))

    ## capture results of asyncs
    arg_1 = [i.get() for i in arg_1]
    arg_4 = set(arg_1)
    arg_5 = zip(arg_1, arg_0.ids)
    arg_6 = {host: [i[1] for i in arg_5 if i[0] == host] for host in arg_4}
    arg_7 = list(itertools.chain(*[arg_6[i][:2] for i in arg_6]))

    ## return first two engines from each host
    return arg_7


def Func(arg_0, arg_1):
    """
    compute stats for stats file and NHX tree features
    """

    ## get name indices
    arg_2 = arg_0.samples

    ## get majority rule consensus tree of weighted Q bootstrap trees
    if arg_0.params.nboots:

        ## Tree object
        arg_3 = ete3.Tree(arg_0.trees.tree, format=0)
        arg_3.unroot()

        ## only grab as many boots as the last option said was max
        with open(arg_0.trees.boots, 'r') as inboots:
            arg_4 = [ete3.Tree(arg_11.strip(), format=0) for arg_11 in inboots.readlines()]
            arg_5 = [arg_3] + arg_4[-arg_0.params.nboots:]

        ## infer consensus tree and write to file
        arg_6, arg_7 = consensus_tree(arg_5, arg_2=arg_2)
        arg_0.trees.cons = os.path.join(arg_0.dirs, arg_0.name + ".cons")
        with open(arg_0.trees.cons, 'w') as ocons:
            ocons.write(arg_6.write(format=0))
    else:
        arg_6 = ete3.Tree(arg_0.trees.tree, format=0)
        arg_6.unroot()

    ## build stats file and write trees
    arg_0.trees.nhx = os.path.join(arg_0.dirs, arg_0.name + ".nhx")
    with open(arg_0.files.stats, 'w') as ostats:

        ## print Tetrad info
        #ostats.write(STATS_STRING.format(**self.stats))

        ## print bootstrap splits
        if arg_0.params.nboots:
            ostats.write("## splits observed in {} trees\n".format(len(arg_5)))
            for arg_11, arg_12 in enumerate(arg_0.samples):
                ostats.write("{:<3} {}\n".format(arg_11, arg_12))
            ostats.write("\n")
            for arg_13, arg_14 in arg_7:
                if arg_13.count('1') > 1:
                    ostats.write("{}   {:.2f}\n".format(arg_13, round(arg_14, 2)))
            ostats.write("\n")

    ## parallelized this function because it can be slogging
    arg_15 = arg_1.load_balanced_view()

    ## store results in dicts
    arg_16 = {}
    arg_17 = {}
    arg_18 = sum(1 for arg_11 in arg_6.iter_leaves())
    arg_19 = set(arg_6.get_leaf_names())

    ## iterate over node traversal.
    for arg_20 in arg_6.traverse():
        ## this is slow, needs to look at every sampled quartet
        ## so we send it be processed on an engine
        arg_16[arg_20] = arg_15.apply(_get_total, *(arg_18, arg_20))
        arg_17[arg_20] = arg_15.apply(_get_sampled, *(arg_0, arg_19, arg_20))

    ## wait for jobs to finish
    arg_1.wait()

    ## put results into tree
    for arg_20 in arg_6.traverse():
        ## this is fast, just calcs n_choose_k
        arg_21 = arg_16[arg_20].result()
        arg_22 = arg_17[arg_20].result()
        ## store the results to the tree
        arg_20.add_feature("quartets_total", arg_21)
        arg_20.add_feature("quartets_sampled", arg_22)
    arg_23 = ["quartets_total", "quartets_sampled"]

    ## return as NHX format with extra info
    with open(arg_0.trees.nhx, 'w') as outtre:
        outtre.write(arg_6.write(format=0, arg_23=arg_23))


def Func(arg_0, arg_1):
    """ random sampler for equal_splits func"""
    arg_2 = tuple(arg_0)
    arg_3 = tuple(arg_1)
    arg_4 = random.sample(arg_2, 2)
    arg_5 = random.sample(arg_3, 2)
    return tuple(arg_4+arg_5)


def Func(arg_0, arg_1, arg_2, arg_3):
    """ groups together several numba compiled funcs """

    ## create empty matrices
    #LOGGER.info("tests[0] %s", tests[0])
    #LOGGER.info('seqnon[[tests[0]]] %s', seqnon[[tests[0]]])
    arg_4 = chunk_to_matrices(arg_0, arg_1, arg_2)

    ## empty svdscores for each arrangement of seqchunk
    arg_5 = np.zeros((3, 16), dtype=np.float64)
    arg_6 = np.zeros(3, dtype=np.float64)
    arg_7 = np.zeros(3, dtype=np.float64)

    for arg_8 in range(3):
        ## get svd scores
        arg_5[arg_8] = np.linalg.svd(arg_4[arg_8].astype(np.float64))[1]
        arg_7[arg_8] = np.linalg.matrix_rank(arg_4[arg_8].astype(np.float64))

    ## get minrank, or 11
    arg_9 = int(min(11, arg_7.min()))
    for arg_8 in range(3):
        arg_6[arg_8] = np.sqrt(np.sum(arg_5[arg_8, arg_9:]**2))

    ## sort to find the best qorder
    arg_10 = np.where(arg_6 == arg_6.min())[0]
    #best = qscores[qscores == qscores.min()][0]
    arg_11 = arg_3[arg_10][0]
    arg_12 = count_snps(arg_4[arg_10][0])

    return arg_11, arg_12


def Func(arg_0, arg_1, arg_2):
    """ The workhorse function. Not numba. """

    ## tell engines to limit threads
    #numba.config.NUMBA_DEFAULT_NUM_THREADS = 1

    ## open the seqarray view, the modified array is in bootsarr
    with h5py.File(arg_0.database.input, 'r') as io5:
        arg_3 = io5["bootsarr"][:]
        arg_4 = io5["bootsmap"][:]

    ## create an N-mask array of all seq cols (this isn't really too slow)
    arg_5 = arg_3[:] == 78

    ## tried numba compiling everythign below here, but was not faster
    ## than making nmask w/ axis arg in numpy

    ## get the input arrays ready
    arg_6 = np.zeros((arg_1.shape[0], 4), dtype=np.uint16)
    arg_7 = None
    #rweights = np.ones(smpchunk.shape[0], dtype=np.float64)
    arg_8 = np.zeros((arg_1.shape[0], 4), dtype=np.uint32)

    #times = []
    ## fill arrays with results using numba funcs
    for arg_9 in xrange(arg_1.shape[0]):
        ## get seqchunk for 4 samples (4, ncols)
        arg_10 = arg_1[arg_9]
        arg_11 = arg_3[arg_10]

        ## get N-containing columns in 4-array, and invariant sites.
        arg_12 = np.any(arg_5[arg_10], axis=0)
        arg_12 += np.all(arg_11 == arg_11[0], axis=0)  ## <- do we need this?

        ## get matrices if there are any shared SNPs
        ## returns best-tree index, qscores, and qstats
        #bidx, qscores, qstats = calculate(seqchunk, maparr[:, 0], nmask, tests)
        arg_13, arg_14 = calculate(arg_11, arg_4[:, 0], arg_12, arg_2)

        ## get weights from the three scores sorted.
        ## Only save to file if the quartet has information
        arg_8[arg_9] = arg_14
        arg_6[arg_9] = arg_1[arg_9][arg_13]

    return arg_6, arg_7, arg_8


def Func(arg_0, arg_1, arg_2):
    """ used in bootstrap resampling without a map file """
    for arg_3 in xrange(arg_2.shape[0]):
        arg_1[:, arg_3] = arg_0[:, arg_2[arg_3]]
    return arg_1


def Func(arg_0, arg_1):
    """ get shape of new bootstrap resampled locus array """
    arg_2 = 0
    for arg_3 in xrange(arg_1.shape[0]):
        arg_2 += arg_0[arg_1[arg_3], 1] - arg_0[arg_1[arg_3], 0]
    return arg_2


def Func(arg_0, arg_1, arg_2, arg_3, arg_4):
    """ fills the new bootstrap resampled array """
    ## column index
    arg_5 = 0

    ## resample each locus
    for arg_6 in xrange(arg_4.shape[0]):

        ## grab a random locus's columns
        arg_7 = arg_3[arg_4[arg_6]][0]
        arg_8 = arg_3[arg_4[arg_6]][1]
        arg_9 = arg_0[:, arg_7:arg_8]

        ## randomize columns within colsq
        arg_10 = np.random.choice(arg_9.shape[1], arg_9.shape[1], replace=False)
        arg_11 = arg_9[:, arg_10]

        ## fill bootarr with n columns from seqarr
        ## the required length was already measured
        arg_1[:, arg_5:arg_5+arg_9.shape[1]] = arg_11

        ## fill bootmap with new map info
        arg_2[arg_5: arg_5+arg_9.shape[1], 0] = arg_6+1

        ## advance column index
        arg_5 += arg_9.shape[1]

    ## return the concatenated cols
    return arg_1, arg_2


def Func(arg_0, arg_1=False):
    """
    converts unicode to utf-8 when reading in json files
    """
    if isinstance(arg_0, unicode):
        return arg_0.encode("utf-8")

    if isinstance(arg_0, list):
        return [Func(arg_2, arg_1=True) for arg_2 in arg_0]

    if isinstance(arg_0, dict) and not arg_1:
        return {
            Func(arg_3, arg_1=True): Func(arg_4, arg_1=True)
            for arg_3, arg_4 in arg_0.iteritems()
        }
    return arg_0


def Func(arg_0):
        """ parse sample names from the sequence file"""
        arg_0.samples = []
        with iter(open(arg_0.files.data, 'r')) as infile:
            infile.next().strip().split()
            while 1:
                try:
                    arg_0.samples.append(infile.next().split()[0])
                except StopIteration:
                    break


def Func(arg_0, arg_1):
        """ runs quartet max-cut on a quartets file """

        ## convert to txt file for wQMC
        arg_0._tmp = os.path.join(arg_0.dirs, ".tmpwtre")
        arg_3 = [ip.bins.qmc, "qrtt="+arg_0.files.qdump, "otre="+arg_0._tmp]

        ## run them
        arg_4 = subprocess.Popen(arg_3, stderr=subprocess.STDOUT, stdout=subprocess.PIPE)
        arg_5 = arg_4.communicate()
        if arg_4.returncode:
            #LOGGER.error("Error in QMC: \n({}).".format(res))
            LOGGER.error(arg_5)
            raise IPyradWarningExit(arg_5[1])

        ## read in the tmp files since qmc does not pipe
        with open(arg_0._tmp) as intree:
            ## convert int names back to str names renamer returns a newick str
            #tmp = toytree.tree(intree.read().strip())
            arg_6 = ete3.Tree(intree.read().strip())
            arg_7 = arg_0._renamer(arg_6)#.tree)

        ## save the tree
        if arg_1:
            arg_0.trees.boots = os.path.join(arg_0.dirs, arg_0.name+".boots")
            with open(arg_0.trees.boots, 'a') as outboot:
                outboot.write(arg_7+"\n")
        else:
            arg_0.trees.tree = os.path.join(arg_0.dirs, arg_0.name+".tree")
            with open(arg_0.trees.tree, 'w') as outtree:
                outtree.write(arg_7)

        ## save JSON file checkpoint
        arg_0._save()


def Func(arg_0):
        """
        Makes a reduced array that excludes quartets with no information and
        prints the quartets and weights to a file formatted for wQMC
        """
        ## open the h5 database
        arg_1 = h5py.File(arg_0.database.output, 'r')

        ## create an output file for writing
        arg_0.files.qdump = os.path.join(arg_0.dirs, arg_0.name+".quartets.txt")
        LOGGER.info("qdump file %s", arg_0.files.qdump)
        arg_4 = open(arg_0.files.qdump, 'w')

        ## todo: should pull quarts order in randomly? or doesn't matter?
        for arg_5 in xrange(0, arg_0.params.nquartets, arg_0._chunksize):
            ## get mask of zero weight quartets
            #mask = io5["weights"][idx:idx+self.chunksize] != 0
            #weight = io5["weights"][idx:idx+self.chunksize][mask]
            #LOGGER.info("exluded = %s, mask shape %s",
            #            self._chunksize - mask.shape[0], mask.shape)
            #LOGGER.info('q shape %s', io5["quartets"][idx:idx+self._chunksize].shape)
            arg_6 = arg_1["quartets"][arg_5:arg_5+arg_0._chunksize, :]#[mask, :]
            arg_7 = [list(j) for j in arg_6]

            ## format and print
            #chunk = ["{},{}|{},{}:{}".format(*i+[j]) for i, j \
            #                                        in zip(quarts, weight)]
            arg_8 = ["{},{}|{},{}".format(*i) for i in arg_7]
            arg_4.write("\n".join(arg_8)+"\n")

        ## close output file and h5 database
        arg_4.close()
        arg_1.close()


def Func(arg_0, arg_1):
        """ renames newick from numbers to sample names"""
        ## get the tre with numbered tree tip labels
        arg_2 = arg_1.get_leaves()

        ## replace numbered names with snames
        for arg_3 in arg_2:
            arg_3.name = arg_0.samples[int(arg_3.name)]

        ## return with only topology and leaf labels
        return arg_1.write(format=9)


def Func(arg_0, arg_1):
        """ write final tree files """

        ## print stats file location:
        #print(STATSOUT.format(opr(self.files.stats)))

        ## print finished tree information ---------------------
        print(FINALTREES.format(opr(arg_0.trees.tree)))

        ## print bootstrap information --------------------------
        if arg_0.params.nboots:
            ## get consensus, map values to tree edges, record stats file
            arg_0._compute_tree_stats(arg_1)
            ## print bootstrap info
            print(BOOTTREES.format(opr(arg_0.trees.cons), opr(arg_0.trees.boots)))

        ## print the ASCII tree only if its small
        if len(arg_0.samples) < 20:
            if arg_0.params.nboots:
                arg_2 = ete3.Tree(arg_0.trees.cons, format=0)
                arg_2.ladderize()
                print(arg_2.get_ascii(show_internal=True,
                                      attributes=["dist", "name"]))
                print("")
            else:
                arg_3 = ete3.Tree(arg_0.trees.tree, format=0)
                arg_3.ladderize()
                #qtre = toytree.tree(self.trees.tree, format=0)
                #qtre.tree.unroot()
                print(arg_3.get_ascii())
                print("")

        ## print PDF filename & tips -----------------------------
        arg_4 = "https://toytree.readthedocs.io/"
        arg_5 = "https://ipyrad.readthedocs.io/tetrad.html"
        print(LINKS.format(arg_4, arg_5))


def Func(arg_0):
        """ save a JSON file representation of Tetrad Class for checkpoint"""

        ## save each attribute as dict
        arg_1 = copy.deepcopy(arg_0.__dict__)
        for arg_2, arg_3 in arg_1.items():
            if isinstance(arg_3, Params):
                arg_1[arg_2] = arg_3.__dict__
        arg_4 = json.dumps(arg_1,
                               sort_keys=False,
                               indent=4,
                               separators=(",", ":"),
                               )

        ## save to file, make dir if it wasn't made earlier
        arg_5 = os.path.join(arg_0.dirs, arg_0.name+".tet.json")
        if not os.path.exists(arg_0.dirs):
            os.mkdir(arg_0.dirs)

        ## protect save from interruption
        arg_6 = 0
        while not arg_6:
            try:
                with open(arg_5, 'w') as jout:
                    jout.write(arg_4)
                arg_6 = 1
            except (KeyboardInterrupt, SystemExit):
                print('.')
                continue


def Func(arg_0, arg_1, arg_2):
        """ inputs results from workers into hdf4 array """
        arg_3, arg_4, arg_5 = arg_2
        #qrts, wgts = results
        #print(qrts)

        with h5py.File(arg_0.database.output, 'r+') as arg_7:
            arg_6 = arg_0._chunksize
            arg_7['quartets'][arg_1:arg_1+arg_6] = arg_3
            ##out['weights'][start:start+chunk] = wgts

            ## entered as 0-indexed !
            if arg_0.checkpoint.boots:
                arg_8 = "qboots/b{}".format(arg_0.checkpoint.boots-1)
                arg_7[arg_8][arg_1:arg_1+arg_6] = arg_5
            else:
                arg_7["qstats"][arg_1:arg_1+arg_6] = arg_5


def Func(arg_0, arg_1, arg_2=None):
    """
    Get the row index of samples that are included. If samples are in the
    'excluded' they were already filtered out of 'samples' during _get_samples.
    """
    ## get index from dbsamples
    arg_1 = [i.name for i in arg_1]
    if arg_2:
        arg_3 = [list(arg_0[arg_2]).index(i) for i in arg_1]
    else:
        arg_3 = [list(arg_0).index(i) for i in arg_1]
    arg_3.sort()
    return arg_3


def Func(arg_0):
    """ pads names for loci output """

    ## get longest name
    arg_1 = max(len(i) for i in arg_0)
    ## Padding distance between name and seq.
    arg_2 = 5
    ## add pad to names
    arg_3 = [name + " " * (arg_1 - len(name)+ arg_2) \
              for name in arg_0]
    arg_4 = "//" + " " * (arg_1 - 2 + arg_2)
    return np.array(arg_3), arg_4


def Func(arg_0):
    """
    Function from make_loci to apply to chunks. smask is sample mask.
    """
    ## parse args
    arg_1, arg_2, arg_3, arg_4, arg_5, arg_6, arg_7, arg_8, arg_9 = arg_0

    ## this slice
    arg_10 = [arg_6, arg_6+arg_2]

    ## get filter db info
    arg_11 = h5py.File(arg_1.database, 'r')
    arg_12 = arg_11["filters"][arg_10[0]:arg_10[1], ]
    arg_13 = arg_11["edges"][arg_10[0]:arg_10[1], ]
    arg_14 = arg_11["snps"][arg_10[0]:arg_10[1], ]

    ## get seqs db
    arg_15 = h5py.File(arg_1.clust_database, 'r')
    if arg_9:
        arg_16 = np.char.upper(arg_15["seqs"][arg_10[0]:arg_10[1], ])
    else:
        arg_16 = arg_15["seqs"][arg_10[0]:arg_10[1], ]

    ## which loci passed all filters
    arg_17 = np.where(np.sum(arg_12, axis=1) == 0)[0]
    arg_18 = []

    ## write loci that passed after trimming edges, then write snp string
    for arg_19 in arg_17:
        arg_20 = arg_13[arg_19]
        #LOGGER.info("!!!!!! iloc edg %s, %s", iloc, edg)
        arg_0 = [arg_19, arg_3, arg_4, arg_20, arg_16, arg_14, arg_5, arg_7, arg_8, arg_6]
        if arg_20[4]:
            arg_21, arg_7, arg_8 = enter_pairs(*arg_0)
            arg_18.append(arg_21)
        else:
            arg_21, arg_7, arg_8 = enter_singles(*arg_0)
            arg_18.append(arg_21)

    ## write to file and clear store
    arg_22 = os.path.join(arg_1.dirs.outfiles, arg_1.name+".loci.{}".format(arg_6))
    with open(arg_22, 'w') as tmpout:
        tmpout.write("\n".join(arg_18) + "\n")

    ## close handles
    arg_15.close()
    arg_11.close()

    ## return sample counter
    return arg_7, arg_8, arg_6


def Func(arg_0, arg_1, arg_2, arg_3, arg_4, arg_5, arg_6, arg_7, arg_8, arg_9):
    """ enter funcs for SE or merged data """

    ## grab all seqs between edges
    arg_10 = arg_4[arg_0, :, arg_3[0]:arg_3[1]+1]
    ## snps was created using only the selected samples, and is edge masked.
    ## The mask is for counting snps quickly, but trimming is still needed here
    ## to make the snps line up with the seqs in the snp string.
    arg_11 = arg_5[arg_0, arg_3[0]:arg_3[1]+1, ]

    ## remove rows with all Ns, seq has only selected samples
    arg_12 = np.all(arg_10 == "N", axis=1)

    ## make mask of removed rows and excluded samples. Use the inverse
    ## of this to save the coverage for samples
    arg_13 = arg_12 + arg_6
    arg_7 = arg_7 + np.invert(arg_13).astype(np.int32)
    arg_14 = np.sum(np.invert(arg_13).astype(np.int32))
    arg_8[arg_14] += 1

    ## select the remaining names in order
    arg_10 = arg_10[~arg_13, ]
    arg_15 = arg_1[~arg_13]

    ## save string for printing, excluding names not in samples
    arg_16 = "\n".join(\
        [name + s.tostring() for name, s in zip(arg_15, arg_10)])

    ## get snp string and add to store
    arg_17 = ["-" if arg_11[i, 0] else \
                 "*" if arg_11[i, 1] else \
                 " " for i in range(len(arg_11))]
    arg_16 += "\n" + arg_2 + "".join(arg_17) + "|{}|".format(arg_0+arg_9)
    #LOGGER.info("outstr %s", outstr)
    return arg_16, arg_7, arg_8


def Func(arg_0):
    """
    Create database file for storing final filtered snps data as hdf5 array.
    Copies splits and duplicates info from clust_database to database.
    """

    ## get stats from step6 h5 and create new h5
    arg_1 = h5py.File(arg_0.clust_database, 'r')
    arg_2 = h5py.File(arg_0.database, 'w')

    ## get maxlen and chunk len
    arg_3 = arg_0._hackersonly["max_fragment_length"] + 20
    arg_4 = arg_1["seqs"].attrs["chunksize"][0]
    arg_5 = arg_1["seqs"].shape[0]

    ## make array for snp string, 2 cols, - and *
    arg_6 = arg_2.create_dataset("snps", (arg_5, arg_3, 2),
                              dtype=np.bool,
                              arg_4=(arg_4, arg_3, 2),
                              compression='gzip')
    arg_6.attrs["chunksize"] = arg_4
    arg_6.attrs["names"] = ["-", "*"]

    ## array for filters that will be applied in step7
    arg_8 = arg_2.create_dataset("filters", (arg_5, 6), dtype=np.bool)
    arg_8.attrs["filters"] = ["duplicates", "max_indels",
                                "max_snps", "max_shared_hets",
                                "min_samps", "max_alleles"]

    ## array for edgetrimming
    arg_9 = arg_2.create_dataset("edges", (arg_5, 5),
                               dtype=np.uint16,
                               arg_4=(arg_4, 5),
                               compression="gzip")
    arg_9.attrs["chunksize"] = arg_4
    arg_9.attrs["names"] = ["R1_L", "R1_R", "R2_L", "R2_R", "sep"]

    ## xfer data from clustdb to finaldb
    arg_9[:, 4] = arg_1["splits"][:]
    arg_8[:, 0] = arg_1["duplicates"][:]

    ## close h5s
    arg_2.close()
    arg_1.close()


def Func(arg_0, arg_1):
    """
    Used to count the number of unique bases in a site for snpstring.
    """
    ## iterate over all loci
    for arg_2 in xrange(arg_0.shape[0]):
        for arg_3 in xrange(arg_0.shape[2]):

            ## make new array
            arg_4 = np.zeros(4, dtype=np.int16)

            ## a list for only catgs
            arg_5 = arg_0[arg_2, :, arg_3]
            for arg_6 in range(arg_5.shape[0]):
                if arg_5[arg_6] == 67: #C
                    arg_4[0] += 1
                elif arg_5[arg_6] == 65: #A
                    arg_4[1] += 1
                elif arg_5[arg_6] == 84: #T
                    arg_4[2] += 1
                elif arg_5[arg_6] == 71: #G
                    arg_4[3] += 1
                elif arg_5[arg_6] == 82: #R
                    arg_4[1] += 1        #A
                    arg_4[3] += 1        #G
                elif arg_5[arg_6] == 75: #K
                    arg_4[2] += 1        #T
                    arg_4[3] += 1        #G
                elif arg_5[arg_6] == 83: #S
                    arg_4[0] += 1        #C
                    arg_4[3] += 1        #G
                elif arg_5[arg_6] == 89: #Y
                    arg_4[0] += 1        #C
                    arg_4[2] += 1        #T
                elif arg_5[arg_6] == 87: #W
                    arg_4[1] += 1        #A
                    arg_4[2] += 1        #T
                elif arg_5[arg_6] == 77: #M
                    arg_4[0] += 1        #C
                    arg_4[1] += 1        #A

            ## get second most common site
            arg_4.sort()
            ## if invariant e.g., [0, 0, 0, 9], then nothing (" ")
            if not arg_4[2]:
                pass
            else:
                if arg_4[2] > 1:
                    arg_1[arg_2, arg_3, 1] = True
                else:
                    arg_1[arg_2, arg_3, 0] = True
    return arg_1


def Func(arg_0, arg_1, arg_2):
    """ write the bisnp string """

    ## grab bis data from tmparr
    arg_3 = os.path.join(arg_0.dirs.outfiles, "tmp-{}.h5".format(arg_0.name))
    with h5py.File(arg_3, 'r') as io5:
        arg_4 = io5["bisarr"]

        ## trim to size b/c it was made longer than actual
        arg_5 = np.where(np.all(arg_4[:] == "", axis=0))[0]
        if np.any(arg_5):
            arg_5 = arg_5.min()
        else:
            arg_5 = arg_4.shape[1]

        ## write to usnps file
        with open(arg_0.outfiles.usnpsphy, 'w') as out:
            out.write("{} {}\n".format(arg_4.shape[0], arg_5))
            for arg_6, arg_7 in enumerate(arg_2):
                out.write("{}{}\n".format(arg_7, "".join(arg_4[arg_6, :arg_5])))


def Func(arg_0, arg_1, arg_2):
    """ Write STRUCTURE format for all SNPs and unlinked SNPs """

    ## grab snp and bis data from tmparr
    arg_3 = time.time()
    arg_4 = os.path.join(arg_0.dirs.outfiles, "tmp-{}.h5".format(arg_0.name))
    with h5py.File(arg_4, 'r') as io5:
        arg_5 = io5["snparr"]
        arg_6 = io5["bisarr"]

        ## trim to size b/c it was made longer than actual
        arg_7 = np.where(np.all(arg_6[:] == "", axis=0))[0]
        if np.any(arg_7):
            arg_7 = arg_7.min()
        else:
            arg_7 = arg_6.shape[1]

        arg_8 = np.where(np.all(arg_5[:] == "", axis=0))[0]
        if np.any(arg_8):
            arg_8 = arg_8.min()
        else:
            arg_8 = arg_5.shape[1]

        ## write to str and ustr
        arg_9 = open(arg_0.outfiles.str, 'w')
        arg_10 = open(arg_0.outfiles.ustr, 'w')
        arg_11 = {'A': '0', 'T': '1', 'G': '2', 'C': '3', 'N': '-9', '-': '-9'}
        if arg_0.paramsdict["max_alleles_consens"] > 1:
            for arg_12, arg_13 in enumerate(arg_2):
                arg_9.write("{}\t\t\t\t\t{}\n"\
                    .format(arg_13,
                    "\t".join([arg_11[DUCT[arg_14][0]] for arg_14 in arg_5[arg_12, :arg_8]])))
                arg_9.write("{}\t\t\t\t\t{}\n"\
                    .format(arg_13,
                    "\t".join([arg_11[DUCT[arg_14][1]] for arg_14 in arg_5[arg_12, :arg_8]])))
                arg_10.write("{}\t\t\t\t\t{}\n"\
                    .format(arg_13,
                    "\t".join([arg_11[DUCT[arg_14][0]] for arg_14 in arg_6[arg_12, :arg_7]])))
                arg_10.write("{}\t\t\t\t\t{}\n"\
                    .format(arg_13,
                    "\t".join([arg_11[DUCT[arg_14][1]] for arg_14 in arg_6[arg_12, :arg_7]])))
        else:
            ## haploid output
            for arg_12, arg_13 in enumerate(arg_2):
                arg_9.write("{}\t\t\t\t\t{}\n"\
                    .format(arg_13,
                    "\t".join([arg_11[DUCT[arg_14][0]] for arg_14 in arg_5[arg_12, :arg_8]])))
                arg_10.write("{}\t\t\t\t\t{}\n"\
                    .format(arg_13,
                    "\t".join([arg_11[DUCT[arg_14][0]] for arg_14 in arg_6[arg_12, :arg_7]])))
        arg_9.close()
        arg_10.close()
    LOGGER.debug("finished writing str in: %s", time.time() - arg_3)


def Func(arg_0, arg_1, arg_2):
    """
    Sorts, concatenates, and gzips VCF chunks. Also cleans up chunks.
    """
    ## open handle and write headers
    if not arg_2:
        arg_3 = open(arg_0.outfiles.vcf, 'w')
    else:
        arg_3 = gzip.open(arg_0.outfiles.VCF, 'w')
    vcfheader(arg_0, arg_1, arg_3)
    arg_3.close()

    ## get vcf chunks
    arg_4 = glob.glob(arg_0.outfiles.vcf+".*")
    arg_4.sort(key=lambda x: int(x.rsplit(".")[-1]))

    ## concatenate
    if not arg_2:
        arg_3 = open(arg_0.outfiles.vcf, 'a')
    else:
        arg_3 = gzip.open(arg_0.outfiles.VCF, 'a')

    ## what order do users want? The order in the original ref file?
    ## Sorted by the size of chroms? that is the order in faidx.
    ## If reference mapping then it's nice to sort the vcf data by
    ## CHROM and POS. This is doing a very naive sort right now, so the
    ## CHROM will be ordered, but not the pos within each chrom.
    if arg_0.paramsdict["assembly_method"] in ["reference", "denovo+reference"]:
        ## Some unix sorting magic to get POS sorted within CHROM
        ## First you sort by POS (-k 2,2), then you do a `stable` sort
        ## by CHROM. You end up with POS ordered and grouped correctly by CHROM
        ## but relatively unordered CHROMs (locus105 will be before locus11).
        arg_5 = ["cat"] + arg_4 + [" | sort -k 2,2 -n | sort -k 1,1 -s"]
        arg_5 = " ".join(arg_5)
        arg_6 = sps.Popen(arg_5, shell=True, stderr=sps.STDOUT, stdout=arg_3, close_fds=True)
    else:
        arg_6 = sps.Popen(["cat"] + arg_4, stderr=sps.STDOUT, stdout=arg_3, close_fds=True)

    arg_7 = arg_6.communicate()[0]
    if arg_6.returncode:
        raise IPyradWarningExit("err in Func: %s", arg_7)
    arg_3.close()

    for arg_8 in arg_4:
        os.remove(arg_8)


def Func(arg_0, arg_1):
    """ Returns the most common base at each site in order. """

    arg_2 = np.zeros((arg_0.shape[1], 4), dtype=np.uint8)
    arg_2[:, 1] = 46

    for arg_3 in xrange(arg_0.shape[1]):
        ## expand colums with ambigs and remove N-
        arg_4 = np.zeros(111, dtype=np.int64)
        arg_5 = np.bincount(arg_0[:, arg_3])#, minlength=90)
        arg_4[:arg_5.shape[0]] = arg_5
        ## set N and - to zero, wish numba supported minlen arg
        arg_4[78] = 0
        arg_4[45] = 0
        ## add ambig counts to true bases
        for arg_7 in xrange(arg_1.shape[0]):
            arg_8 = arg_4[arg_1[arg_7, 0]]
            for arg_9 in xrange(arg_8):
                arg_4[arg_1[arg_7, 1]] += 1
                arg_4[arg_1[arg_7, 2]] += 1
            arg_4[arg_1[arg_7, 0]] = 0

        ## now get counts from the modified counts arr
        arg_10 = np.argmax(arg_4)
        arg_2[arg_3, 0] = arg_10
        arg_4[arg_10] = 0

        ## if an alt allele fill over the "." placeholder
        arg_10 = np.argmax(arg_4)
        if arg_10:
            arg_2[arg_3, 1] = arg_10
            arg_4[arg_10] = 0

            ## if 3rd or 4th alleles observed then add to arr
            arg_10 = np.argmax(arg_4)
            arg_2[arg_3, 2] = arg_10
            arg_4[arg_10] = 0

            ## if 3rd or 4th alleles observed then add to arr
            arg_10 = np.argmax(arg_4)
            arg_2[arg_3, 3] = arg_10

    return arg_2


def Func(arg_0, arg_1):
    """ collapse outgroup in ete Tree for easier viewing """
    ## check that all tests have the same outgroup
    arg_2 = arg_1[0]["p4"]
    if not all([arg_3["p4"] == arg_2 for arg_3 in arg_1]):
        raise Exception("no good")

    ## prune tree, keep only one sample from outgroup
    arg_4 = ete.Tree(arg_0.write(format=1)) #tree.copy(method="deepcopy")
    arg_5 = [arg_3 for arg_3 in arg_4.get_leaf_names() if arg_3 not in arg_2]
    arg_5 += [arg_2[0]]
    arg_4.prune(arg_5)
    arg_4.search_nodes(arg_7=arg_2[0])[0].name = "outgroup"
    arg_4.ladderize()

    ## remove other ougroups from taxdicts
    arg_8 = copy.deepcopy(arg_1)
    arg_9 = []
    for arg_10 in arg_8:
        #test["p4"] = [outg[0]]
        arg_10["p4"] = ["outgroup"]
        arg_9.append(arg_10)

    return arg_4, arg_9


def Func(
        arg_0,
        arg_1=True,
        arg_2=False,
        arg_3=False,
        arg_4="right",
        arg_5=False,
        *arg_6,
        **arg_7):
        """
        plot the tree using toyplot.graph.

        Parameters:
        -----------
            show_tip_labels: bool
                Show tip names from tree.
            use_edge_lengths: bool
                Use edge lengths from newick tree.
            show_node_support: bool
                Show support values at nodes using a set of default
                options.

            ...
        """
        ## re-decompose tree for new orient and edges args
        arg_0._decompose_tree(arg_4=arg_4, arg_3=arg_3)

        ## update kwargs with entered args and all other kwargs
        arg_8 = {}
        arg_8["show_tip_labels"] = arg_1
        arg_8["show_node_support"] = arg_2
        arg_8.update(arg_7)

        ## pass to panel plotter
        arg_9, arg_10, arg_11 = tree_panel_plot(arg_0, arg_5, **arg_8)
        return arg_9, arg_10, arg_11


def Func(arg_0, arg_1):
    """ iterate over clustS files to get data """

    ## use existing sample cluster path if it exists, since this
    ## func can be used in step 4 and that can occur after merging
    ## assemblies after step3, and if we then referenced by data.dirs.clusts
    ## the path would be broken.
    ##
    ## If branching at step 3 to test different clust thresholds, the
    ## branched samples will retain the samples.files.clusters of the
    ## parent (which have the clust_threshold value of the parent), so
    ## it will look like nothing has changed. If we call this func
    ## from step 3 then it indicates we are in a branch and should
    ## reset the sample.files.clusters handle to point to the correct
    ## data.dirs.clusts directory. See issue #229.
    ## Easier to just always trust that samples.files.clusters is right,
    ## no matter what step?
    #if sample.files.clusters and not sample.stats.state == 3:
    #    pass
    #else:
    #    ## set cluster file handles
    arg_1.files.clusters = os.path.join(
         arg_0.dirs.clusts, arg_1.name+".clustS.gz")

    ## get new clustered loci
    arg_4 = arg_0.samples[arg_1.name].files.clusters
    arg_3 = gzip.open(arg_4, 'r')
    arg_5 = itertools.izip(*[iter(arg_3)]*2)

    ## storage
    arg_6 = []
    arg_7 = []

    ## start with cluster 0
    arg_8 = 0
    arg_9 = 0

    ## iterate until empty
    while 1:
        ## grab next
        try:
            arg_10, arg_11 = arg_5.next()
        except StopIteration:
            break

        ## if not the end of a cluster
        #print name.strip(), seq.strip()
        if arg_10.strip() == arg_11.strip():
            arg_6.append(arg_8)
            arg_7.append(arg_9)
            arg_9 = 0
            arg_8 = 0

        else:
            arg_8 += int(arg_10.split(";")[-2][5:])
            arg_9 = len(arg_11)

    ## return
    arg_3.close()
    return np.array(arg_7), np.array(arg_6)


def Func(arg_0, arg_1):
    """ checks for too many internal indels in muscle aligned clusters """

    ## make into list
    arg_2 = arg_0.split()

    ## paired or not
    try:
        arg_3 = [i.split("nnnn")[0] for i in arg_2[1::2]]
        arg_4 = [i.split("nnnn")[1] for i in arg_2[1::2]]
        arg_5 = [i.rstrip("-").lstrip("-").count("-") for i in arg_3]
        arg_6 = [i.rstrip("-").lstrip("-").count("-") for i in arg_4]
        arg_7 = arg_5 + arg_6
        if max(arg_7) > arg_1:
            return 1

    except IndexError:
        arg_3 = arg_2[1::2]
        arg_7 = [i.rstrip("-").lstrip("-").count("-") for i in arg_3]
        if max(arg_7) > arg_1:
            return 1

    return 0


def Func(arg_0):
    """ sets up directories for step3 data """
    ## make output folder for clusters
    arg_1 = os.path.realpath(arg_0.paramsdict["project_dir"])
    arg_0.dirs.clusts = os.path.join(arg_1, "{}_clust_{}"\
                       .format(arg_0.name, arg_0.paramsdict["clust_threshold"]))
    if not os.path.exists(arg_0.dirs.clusts):
        os.mkdir(arg_0.dirs.clusts)

    ## make a tmpdir for align files
    arg_0.tmpdir = os.path.abspath(os.path.expanduser(
        os.path.join(arg_1, arg_0.name+'-tmpalign')))
    if not os.path.exists(arg_0.tmpdir):
        os.mkdir(arg_0.tmpdir)

    ## If ref mapping, init samples and make the refmapping output directory.
    if not arg_0.paramsdict["assembly_method"] == "denovo":
        ## make output directory for read mapping process
        arg_0.dirs.refmapping = os.path.join(arg_1, "{}_refmapping".format(arg_0.name))
        if not os.path.exists(arg_0.dirs.refmapping):
            os.mkdir(arg_0.dirs.refmapping)


def Func(arg_0, arg_1):
    """
    build a directed acyclic graph describing jobs to be run in order.
    """

    ## Create DAGs for the assembly method being used, store jobs in nodes
    arg_2 = [i.name for i in arg_1]
    arg_3 = nx.DiGraph()

    ## get list of pre-align jobs from globals based on assembly method
    arg_4 = JOBORDER[arg_0.paramsdict["assembly_method"]]

    ## WHICH JOBS TO RUN: iterate over the sample names
    for arg_5 in arg_2:
        ## append pre-align job for each sample to nodes list
        for arg_6 in arg_4:
            arg_3.add_node("{}-{}-{}".format(arg_6, 0, arg_5))

        ## append align func jobs, each will have max 10
        for arg_7 in xrange(10):
            arg_3.add_node("{}-{}-{}".format("muscle_align", arg_7, arg_5))

        ## append final reconcat jobs
        arg_3.add_node("{}-{}-{}".format("reconcat", 0, arg_5))

    ## ORDER OF JOBS: add edges/dependency between jobs: (first-this, then-that)
    for arg_5 in arg_2:
        for arg_8 in arg_2:
            ## enforce that clust/map cannot start until derep is done for ALL
            ## samples. This is b/c...
            arg_3.add_edge("{}-{}-{}".format(arg_4[0], 0, arg_8),
                         "{}-{}-{}".format(arg_4[1], 0, arg_5))

        ## add remaining pre-align jobs
        for arg_9 in xrange(2, len(arg_4)):
            arg_3.add_edge("{}-{}-{}".format(arg_4[arg_9-1], 0, arg_5),
                         "{}-{}-{}".format(arg_4[arg_9], 0, arg_5))

        ## Add 10 align jobs, none of which can start until all chunker jobs
        ## are finished. Similarly, reconcat jobs cannot start until all align
        ## jobs are finished.
        for arg_8 in arg_2:
            for arg_7 in range(10):
                arg_3.add_edge("{}-{}-{}".format("muscle_chunker", 0, arg_8),
                             "{}-{}-{}".format("muscle_align", arg_7, arg_5))
                ## add that the final reconcat job can't start until after
                ## each chunk of its own sample has finished aligning.
                arg_3.add_edge("{}-{}-{}".format("muscle_align", arg_7, arg_5),
                             "{}-{}-{}".format("reconcat", 0, arg_5))
    ## return the dag
    return arg_3, arg_4


def Func(arg_0, arg_1, arg_2):
    """
    makes plot to help visualize the DAG setup. For developers only.
    """
    try:
        import matplotlib.pyplot as plt
        from matplotlib.dates import date2num
        from matplotlib.cm import gist_rainbow

        ## first figure is dag layout
        plt.figure("dag_layout", figsize=(10, 10))
        nx.draw(arg_0,
                arg_3=nx.spring_layout(arg_0),
                node_color='pink',
                with_labels=True)
        plt.savefig("./dag_layout.png", bbox_inches='tight', dpi=200)

        ## second figure is times for steps
        arg_3 = {}
        arg_4 = {}

        for arg_5 in arg_0:
            #jobkey = "{}-{}".format(node, sample)
            arg_6 = arg_1[arg_5].metadata
            arg_7 = date2num(arg_6.started)
            #runtime = date2num(md.completed)# - start
            ## sample id to separate samples on x-axis
            arg_8, arg_8, arg_9 = arg_5.split("-", 2)
            arg_10 = arg_2.index(arg_9)
            ## 1e6 to separate on y-axis
            arg_3[arg_5] = (arg_7+arg_10, arg_7*1e6)
            arg_4[arg_5] = arg_6.engine_id

        ## x just spaces out samples;
        ## y is start time of each job with edge leading to next job
        ## color is the engine that ran the job
        ## all jobs were submitted as 3 second wait times
        plt.figure("dag_starttimes", figsize=(10, 16))
        nx.draw(arg_0, arg_3,
                node_list=arg_4.keys(),
                node_color=arg_4.values(),
                cmap=gist_rainbow,
                with_labels=True)
        plt.savefig("./dag_starttimes.png", bbox_inches='tight', dpi=200)

    except Exception as inst:
        LOGGER.warning(inst)


def Func(arg_0, arg_1, arg_2):
    """
    Blocks and prints progress for just the func being requested from a list
    of submitted engine jobs. Returns whether any of the jobs failed.

    func = str
    results = dict of asyncs
    """

    ## TODO: try to insert a better way to break on KBD here.
    LOGGER.info("inside Func of %s", arg_0)

    ## get just the jobs from results that are relevant to this func
    arg_3 = [(i, arg_1[i]) for i in arg_1 if i.split("-", 2)[0] == arg_0]

    ## progress bar
    arg_4 = time.time()
    while 1:
        ## how many of this func have finished so far
        arg_5 = [i[1].ready() for i in arg_3]
        arg_6 = datetime.timedelta(seconds=int(time.time()-arg_4))
        arg_7 = " {}    | {} | s3 |".format(PRINTSTR[arg_0], arg_6)
        progressbar(len(arg_5), sum(arg_5), arg_7, arg_2=arg_2)
        time.sleep(0.1)
        if len(arg_5) == sum(arg_5):
            print("")
            break

    arg_8 = []
    arg_9 = []
    for arg_10 in arg_3:
        if not arg_10[1].successful():
            arg_8.append(arg_10[0])
            arg_9.append(arg_10[1].result())

    return arg_0, arg_8, arg_9


def Func(arg_0, arg_1):
    """
    if multiple fastq files were appended into the list of fastqs for samples
    then we merge them here before proceeding.
    """

    ## if more than one tuple in fastq list
    if len(arg_1.files.edits) > 1:
        ## create a cat command to append them all (doesn't matter if they
        ## are gzipped, cat still works). Grab index 0 of tuples for R1s.
        arg_2 = ["cat"] + [i[0] for i in arg_1.files.edits]

        ## write to new concat handle
        arg_3 = os.path.join(arg_0.dirs.edits, arg_1.name+"_R1_concatedit.fq.gz")
        with open(arg_3, 'w') as cout1:
            arg_4 = sps.Popen(arg_2, stderr=sps.STDOUT, stdout=cout1, close_fds=True)
            arg_5 = arg_4.communicate()[0]
        if arg_4.returncode:
            raise IPyradWarningExit("error in: %s, %s", arg_2, arg_5)

        ## Only set conc2 if R2 actually exists
        arg_6 = 0
        if os.path.exists(str(arg_1.files.edits[0][1])):
            arg_7 = ["cat"] + [i[1] for i in arg_1.files.edits]
            arg_6 = os.path.join(arg_0.dirs.edits, arg_1.name+"_R2_concatedit.fq.gz")
            with gzip.open(arg_6, 'w') as cout2:
                arg_8 = sps.Popen(arg_7, stderr=sps.STDOUT, stdout=cout2, close_fds=True)
                arg_9 = arg_8.communicate()[0]
            if arg_8.returncode:
                raise IPyradWarningExit("error in: %s, %s", arg_7, arg_9)

        ## store new file handles
        arg_1.files.edits = [(arg_3, arg_6)]
    return arg_1.files.edits


def Func(arg_0, arg_1, arg_2, arg_3):
    """
    Calls vsearch for Funcing. cov varies by data type, values were chosen
    based on experience, but could be edited by users
    """

    ## get the dereplicated reads
    if "reference" in arg_0.paramsdict["assembly_method"]:
        arg_4 = os.path.join(arg_0.dirs.edits, arg_1.name+"-refmap_derep.fastq")
        ## In the event all reads for all samples map successfully then Funcing
        ## the unmapped reads makes no sense, so just bail out.
        if not os.stat(arg_4).st_size:
            ## In this case you do have to create empty, dummy vsearch output
            ## files so building_Funcs will not fail.
            arg_5 = os.path.join(arg_0.dirs.clusts, arg_1.name+".utemp")
            arg_6 = os.path.join(arg_0.dirs.clusts, arg_1.name+".utemp.sort")
            arg_7 = os.path.join(arg_0.dirs.clusts, arg_1.name+".htemp")
            for arg_8 in [arg_5, arg_6, arg_7]:
                open(arg_8, 'a').close()
            return
    else:
        arg_4 = os.path.join(arg_0.dirs.edits, arg_1.name+"_derep.fastq")

    ## create handles for the outfiles
    arg_5 = os.path.join(arg_0.dirs.clusts, arg_1.name+".utemp")
    arg_9 = os.path.join(arg_0.dirs.clusts, arg_1.name+".htemp")

    ## If derep file doesn't exist then bail out
    if not os.path.isfile(arg_4):
        LOGGER.warn("Bad derephandle - {}".format(arg_4))
        raise IPyradError("Input file for Funcing doesn't exist - {}"\
                        .format(arg_4))

    ## testing one sample fail
    #if sample.name == "1C_0":
    #    x

    ## datatype specific optimization
    ## minsl: the percentage of the seed that must be matched
    ##    smaller values for RAD/ddRAD where we might want to combine, say 50bp
    ##    reads and 100bp reads in the same analysis.
    ## query_cov: the percentage of the query sequence that must match seed
    ##    smaller values are needed for gbs where only the tips might overlap
    ##    larger values for pairgbs where they should overlap near completely
    ##    small minsl and high query cov allows trimmed reads to match to untrim
    ##    seed for rad/ddrad/pairddrad.
    arg_10 = "plus"
    arg_11 = 0.75
    arg_12 = 0.5
    if arg_0.paramsdict["datatype"] in ["gbs", "2brad"]:
        arg_10 = "both"
        arg_11 = 0.5
        arg_12 = 0.5
    elif arg_0.paramsdict["datatype"] == 'pairgbs':
        arg_10 = "both"
        arg_11 = 0.75
        arg_12 = 0.75

    ## If this value is not null (which is the default) then override query cov
    if arg_0._hackersonly["query_cov"]:
        arg_11 = str(arg_0._hackersonly["query_cov"])
        assert float(arg_11) <= 1, "query_cov must be <= 1.0"

    ## get call string
    arg_13 = [ipyrad.bins.vsearch,
           "-Func_smallmem", arg_4,
           "-strand", arg_10,
           "-query_cov", str(arg_11),
           "-id", str(arg_0.paramsdict["clust_threshold"]),
           "-minsl", str(arg_12),
           "-userout", arg_5,
           "-userfields", "query+target+id+gaps+qstrand+qcov",
           "-maxaccepts", "1",
           "-maxrejects", "0",
           "-threads", str(arg_2),
           "-notmatched", arg_9,
           "-fasta_width", "0",
           "-fastq_qmax", "100",
           "-fulldp",
           "-usersort"]

    ## not sure what the benefit of this option is exactly, needs testing,
    ## might improve indel detection on left side, but we don't want to enforce
    ## aligning on left side if not necessarily, since quality trimmed reads
    ## might lose bases on left side in step2 and no longer align.
    #if data.paramsdict["datatype"] in ["rad", "ddrad", "pairddrad"]:
    #    cmd += ["-leftjust"]

    ## run vsearch
    LOGGER.debug("%s", arg_13)
    arg_14 = sps.Popen(arg_13, stderr=sps.STDOUT, stdout=sps.PIPE, close_fds=True)

    ## This is long running so we wrap it to make sure we can kill it
    try:
        arg_15 = arg_14.communicate()[0]
    except KeyboardInterrupt:
        arg_14.kill()
        raise KeyboardInterrupt

    ## check for errors
    if arg_14.returncode:
        LOGGER.error("error %s: %s", arg_13, arg_15)
        raise IPyradWarningExit("cmd {}: {}".format(arg_13, arg_15))


def Func(arg_0, arg_1, arg_2, arg_3):
    """
    Running on remote Engine. Refmaps, then merges, then dereplicates,
    then denovo clusters reads.
    """

    ## report location for debugging
    LOGGER.info("INSIDE derep %s", arg_1.name)

    ## MERGED ASSEMBIES ONLY:
    ## concatenate edits files within Samples. Returns a new sample.files.edits
    ## with the concat file. No change if not merged Assembly.
    arg_4 = os.path.join(arg_0.dirs.edits, arg_1.name+"_merged_.fastq")
    if not arg_3:
        if not os.path.exists(arg_4):
            arg_1.files.edits = concat_multiple_edits(arg_0, arg_1)
        else:
            LOGGER.info("skipped concat_multiple_edits: {} exists"\
                        .format(arg_4))
    else:
        arg_1.files.edits = concat_multiple_edits(arg_0, arg_1)

    ## PAIRED DATA ONLY:
    ## Denovo: merge or concat fastq pairs [sample.files.pairs]
    ## Reference: only concat fastq pairs  []
    ## Denovo + Reference: ...
    if 'pair' in arg_0.paramsdict['datatype']:
        ## the output file handle for merged reads

        ## modify behavior of merging vs concating if reference
        if "reference" in arg_0.paramsdict["assembly_method"]:
            arg_7 = merge_pairs(arg_0, arg_1.files.edits, arg_4, 0, 0)
        else:
            arg_7 = merge_pairs(arg_0, arg_1.files.edits, arg_4, 1, 1)

        ## store results
        arg_1.files.edits = [(arg_4, )]
        arg_1.stats.reads_merged = arg_7

    ## 3rad uses random adapters to identify pcr duplicates. We will
    ## remove pcr dupes here. Basically append the radom adapter to
    ## each sequence, do a regular old vsearch derep, then trim
    ## off the adapter, and push it down the pipeline. This will
    ## remove all identical seqs with identical random i5 adapters.
    if "3rad" in arg_0.paramsdict["datatype"]:
        declone_3rad(arg_0, arg_1)
        derep_and_sort(arg_0,
                os.path.join(arg_0.dirs.edits, arg_1.name+"_declone.fastq"),
                os.path.join(arg_0.dirs.edits, arg_1.name+"_derep.fastq"),
                arg_2)
    else:
        ## convert fastq to fasta, then derep and sort reads by their size.
        ## we pass in only one file b/c paired should be merged by now.
        derep_and_sort(arg_0,
                arg_1.files.edits[0][0],
                os.path.join(arg_0.dirs.edits, arg_1.name+"_derep.fastq"),
                arg_2)


def Func(arg_0, arg_1):
    """
    loads assembly or creates a new one and set its params from
    parsedict. Does not launch ipcluster.
    """

    ## Creating an assembly with a full path in the name will "work"
    ## but it is potentially dangerous, so here we have assembly_name
    ## and assembly_file, name is used for creating new in cwd, file is
    ## used for loading existing.
    ##
    ## Be nice if the user includes the extension.
    #project_dir = ip.core.assembly._expander(parsedict['1'])
    #assembly_name = parsedict['0']
    arg_2 = ip.core.assembly._expander(arg_1['project_dir'])
    arg_3 = arg_1['assembly_name']
    arg_4 = os.path.join(arg_2, arg_3)

    ## Assembly creation will handle error checking  on
    ## the format of the assembly_name

    ## make sure the working directory exists.
    if not os.path.exists(arg_2):
        os.mkdir(arg_2)

    try:
        ## If 1 and force then go ahead and create a new assembly
        if ('1' in arg_0.steps) and arg_0.force:
            arg_5 = ip.Assembly(arg_3, cli=True)
        else:
            arg_5 = ip.load_json(arg_4, cli=True)
            arg_5._cli = True

    except IPyradWarningExit as _:
        ## if no assembly is found then go ahead and make one
        if '1' not in arg_0.steps:
            raise IPyradWarningExit(\
                "  Error: You must first run step 1 on the assembly: {}"\
                .format(arg_4))
        else:
            ## create a new assembly object
            arg_5 = ip.Assembly(arg_3, cli=True)

    ## for entering some params...
    for arg_7 in arg_1:

        ## trap assignment of assembly_name since it is immutable.
        if arg_7 == "assembly_name":
            ## Raise error if user tried to change assembly name
            if arg_1[arg_7] != arg_5.name:
                arg_5.set_params(arg_7, arg_1[arg_7])
        else:
            ## all other params should be handled by set_params
            try:
                arg_5.set_params(arg_7, arg_1[arg_7])
            except IndexError as _:
                print("  Malformed params file: {}".format(arg_0.params))
                print("  Bad parameter {} - {}".format(arg_7, arg_1[arg_7]))
                sys.exit(-1)
    return arg_5


def Func(arg_0, arg_1, arg_2, arg_3):
    """
    return probability of base call
    """

    arg_4 = (1. - arg_3) / 2.
    arg_5 = arg_3

    ## calculate probs
    arg_6 = arg_0 + arg_1
    arg_7 = scipy.misc.comb(arg_6, arg_0)/(2. **(arg_6))
    arg_8 = scipy.stats.binom.pmf(arg_1, arg_6, arg_2)
    arg_9 = scipy.stats.binom.pmf(arg_0, arg_6, arg_2)

    ## calculate probs
    arg_7 *= arg_5
    arg_8 *= arg_4
    arg_9 *= arg_4

    ## final
    arg_10 = [arg_8, arg_9, arg_7]
    arg_11 = max(arg_10)/float(sum(arg_10))

    ## return
    if arg_7 > arg_8:
        return True, arg_11
    else:
        return False, arg_11


def Func(arg_0, arg_1, arg_2):
    """ store phased allele data for diploids """
    ## find the first hetero site and choose the priority base
    ## example, if W: then priority base in A and not T. PRIORITY=(order: CATG)
    arg_3 = PRIORITY[arg_0[arg_1[0]]]

    ## find which allele has priority based on bigbase
    arg_4 = [i for i in arg_2 if i[0] == arg_3][0]

    ## uplow other bases relative to this one and the priority list
    ## e.g., if there are two hetero sites (WY) and the two alleles are
    ## AT and TC, then since bigbase of (W) is A second hetero site should
    ## be stored as y, since the ordering is swapped in this case; the priority
    ## base (C versus T) is C, but C goes with the minor base at h site 1.
    #consens = list(consens)
    for arg_5, arg_6 in zip(arg_1[1:], arg_4[1:]):
        if PRIORITY[arg_0[arg_5]] != arg_6:
            arg_0[arg_5] = arg_0[arg_5].lower()

    ## return consens
    return arg_0


def Func(arg_0, arg_1, arg_2, arg_3):
    """ checks if the sample should be Func and passes the args """
    ## prepare dirs
    arg_0.dirs.consens = os.path.join(arg_0.dirs.project, arg_0.name+"_consens")
    if not os.path.exists(arg_0.dirs.consens):
        os.mkdir(arg_0.dirs.consens)

    ## zap any tmp files that might be leftover
    arg_6 = glob.glob(os.path.join(arg_0.dirs.consens, "*_tmpcons.*"))
    arg_7 = glob.glob(os.path.join(arg_0.dirs.consens, "*_tmpcats.*"))
    for arg_8 in arg_6+arg_7:
        os.remove(arg_8)

    ## filter through samples for those ready
    arg_1 = get_subsamples(arg_0, arg_1, arg_2)

    ## set up parallel client: how many cores?
    arg_9 = arg_3.load_balanced_view()
    arg_0.cpus = arg_0._ipcluster["cores"]
    if not arg_0.cpus:
        arg_0.cpus = len(arg_3.ids)

    ## wrap everything to ensure destruction of temp files
    arg_11 = ""
    try:
        ## calculate depths, if they changed.
        arg_1 = calculate_depths(arg_0, arg_1, arg_9)

        ## chunk clusters into bits for parallel processing
        arg_12 = make_chunks(arg_0, arg_1, arg_9)

        ## process chunks and cleanup
        process_chunks(arg_0, arg_1, arg_12, arg_9)

    except KeyboardInterrupt as arg_11:
        raise arg_11

    finally:
        ## if process failed at any point delete tmp files
        arg_6 = glob.glob(os.path.join(arg_0.dirs.clusts, "tmp_*.[0-9]*"))
        arg_6 += glob.glob(os.path.join(arg_0.dirs.consens, "*_tmpcons.*"))
        arg_6 += glob.glob(os.path.join(arg_0.dirs.consens, "*_tmpcats.*"))
        for arg_13 in arg_6:
            os.remove(arg_13)

        ## Finished step 5. Set step 6 checkpoint to 0 to force
        ## re-Funcning from scratch.
        arg_0._checkpoint = 0


def Func(arg_0, arg_1, arg_2):
    """
    check whether mindepth has changed, and thus whether clusters_hidepth
    needs to be recalculated, and get new maxlen for new highdepth clusts.
    if mindepth not changed then nothing changes.
    """

    ## send jobs to be processed on engines
    arg_3 = time.time()
    arg_4 = " calculating depths    | {} | s5 |"
    arg_5 = {}
    arg_6 = []
    for arg_7 in arg_1:
        arg_5[arg_7.name] = arg_2.apply(recal_hidepth, *(arg_0, arg_7))

    ## block until finished
    while 1:
        arg_9 = [i.ready() for i in arg_5.values()]
        arg_10 = datetime.timedelta(seconds=int(time.time()-arg_3))
        progressbar(len(arg_9), sum(arg_9), arg_4.format(arg_10), spacer=arg_0._spacer)
        time.sleep(0.1)
        if len(arg_9) == sum(arg_9):
            print("")
            break

    ## check for failures and collect results
    arg_11 = []
    for arg_7 in arg_1:
        if not arg_5[arg_7.name].successful():
            LOGGER.error("  sample %s failed: %s", arg_7.name, arg_5[arg_7.name].exception())
        else:
            arg_12, arg_13, arg_14, arg_13, arg_13 = arg_5[arg_7.name].result()
            arg_11.append(arg_12)
            arg_6.append(arg_14)

    ## reset global maxlen if something changed
    arg_0._hackersonly["max_fragment_length"] = int(max(arg_6)) + 4

    return arg_1


def Func(arg_0, arg_1, arg_2):
    """
    calls chunk_clusters and tracks progress.
    """
    ## first progress bar
    arg_3 = time.time()
    arg_4 = " chunking clusters     | {} | s5 |"
    arg_5 = datetime.timedelta(seconds=int(time.time()-arg_3))
    progressbar(10, 0, arg_4.format(arg_5), spacer=arg_0._spacer)

    ## send off samples to be chunked
    arg_6 = {}
    for arg_7 in arg_1:
        arg_6[arg_7.name] = arg_2.apply(chunk_clusters, *(arg_0, arg_7))

    ## block until finished
    while 1:
        arg_9 = [i.ready() for i in arg_6.values()]
        arg_5 = datetime.timedelta(seconds=int(time.time()-arg_3))
        progressbar(len(arg_9), sum(arg_9), arg_4.format(arg_5), spacer=arg_0._spacer)
        time.sleep(0.1)
        if len(arg_9) == sum(arg_9):
            print("")
            break

    ## check for failures
    for arg_7 in arg_1:
        if not arg_6[arg_7.name].successful():
            LOGGER.error("  sample %s failed: %s", arg_7.name,
                        arg_6[arg_7.name].exception())

    return arg_6


def Func(arg_0, arg_1=""):
    """ reports host and engine info for an ipyclient """
    ## get engine data, skips busy engines.
    arg_2 = []
    for arg_3 in arg_0.ids:
        arg_4 = arg_0[arg_3]
        if not arg_4.outstanding:
            arg_2.append(arg_4.apply(_socket.gethostname))

    ## report it
    arg_2 = [i.get() for i in arg_2]
    arg_5 = []
    for arg_6 in set(arg_2):
        arg_5.append("{}host compute node: [{} cores] on {}"\
            .format(arg_1, arg_2.count(arg_6), arg_6))
    print "\n".join(arg_5)


def Func(arg_0):
    """ set the debug dict """

    _lconfig.dictConfig({
    'version': 1,
    'disable_existing_loggers': False,

    'formatters': {
        'standard': {
            'format': "%(asctime)s \t"\
                     +"pid=%(process)d \t"\
                     +"[%(filename)s]\t"\
                     +"%(levelname)s \t"\
                     +"%(message)s"
        },
    },
    'handlers': {
        __name__: {
            'level':arg_0,
            'class':'logging.FileHandler',
            'filename':__debugfile__,
            'formatter':"standard",
            'mode':'a+'
        }
    },
    'loggers':{
        __name__: {
            'handlers': [__name__],
            'level': arg_0,
            'propogate': True
        }
    }
    })


def Func():
    """ gets the right version of vsearch, muscle, and smalt
    depending on linux vs osx """

    # Return error if system is 32-bit arch.
    # This is straight from the python docs:
    # https://docs.python.org/2/library/platform.html#cross-platform
    if not _sys.maxsize > 2**32:
        _sys.exit("ipyrad requires 64bit architecture")

    ## get platform mac or linux
    arg_0 = _sys.platform

    ## get current location
    if 'VIRTUAL_ENV' in _os.environ:
        arg_1 = _os.environ['VIRTUAL_ENV']
    else:
        arg_2 = _os.path.abspath(_os.path.dirname(__file__))
        arg_1 = _os.path.dirname(arg_2)

    ## find bin directory
    arg_1 = _os.path.dirname(arg_2)
    arg_3 = _os.path.join(arg_1, "bin")

    ## get the correct binaries
    if 'linux' in arg_0:
        arg_4 = _os.path.join(
                       _os.path.abspath(arg_3),
                       "vsearch-linux-x86_64")
        arg_5 = _os.path.join(
                       _os.path.abspath(arg_3),
                       "muscle-linux-x86_64")
        arg_6 = _os.path.join(
                       _os.path.abspath(arg_3),
                       "smalt-linux-x86_64")
        arg_7 = _os.path.join(
                       _os.path.abspath(arg_3),
                       "bwa-linux-x86_64")
        arg_8 = _os.path.join(
                       _os.path.abspath(arg_3),
                       "samtools-linux-x86_64")
        arg_9 = _os.path.join(
                       _os.path.abspath(arg_3),
                       "bedtools-linux-x86_64")
        arg_10 = _os.path.join(
                       _os.path.abspath(arg_3),
                       "QMC-linux-x86_64")
    else:
        arg_4 = _os.path.join(
                       _os.path.abspath(arg_3),
                       "vsearch-osx-x86_64")
        arg_5 = _os.path.join(
                       _os.path.abspath(arg_3),
                       "muscle-osx-x86_64")
        arg_6 = _os.path.join(
                       _os.path.abspath(arg_3),
                       "smalt-osx-x86_64")
        arg_7 = _os.path.join(
                       _os.path.abspath(arg_3),
                       "bwa-osx-x86_64")
        arg_8 = _os.path.join(
                       _os.path.abspath(arg_3),
                       "samtools-osx-x86_64")
        arg_9 = _os.path.join(
                       _os.path.abspath(arg_3),
                       "bedtools-osx-x86_64")
        ## only one compiled version available, works for all?
        arg_10 = _os.path.join(
                       _os.path.abspath(arg_3),
                       "QMC-osx-x86_64")

    # Test for existence of binaries
    assert _cmd_exists(arg_5), "muscle not found here: "+arg_5
    assert _cmd_exists(arg_4), "vsearch not found here: "+arg_4
    assert _cmd_exists(arg_6), "smalt not found here: "+arg_6
    assert _cmd_exists(arg_7), "bwa not found here: "+arg_7
    assert _cmd_exists(arg_8), "samtools not found here: "+arg_8
    assert _cmd_exists(arg_9), "bedtools not found here: "+arg_9
    #assert _cmd_exists(qmc), "wQMC not found here: "+qmc
    return arg_4, arg_5, arg_6, arg_7, arg_8, arg_9, arg_10


def Func(arg_0, arg_1, arg_2):
    """
    Returns nsets unique random quartet sets sampled from
    n-choose-k without replacement combinations.
    """
    arg_3 = set()
    while len(arg_3) < arg_0:
        arg_4 = tuple(sorted(np.random.choice(arg_1, arg_2, replace=False)))
        arg_3.add(arg_4)
    return tuple(arg_3)


def Func(arg_0):
    """
    set mkl thread limit and return old value so we can reset
    when finished.
    """
    if "linux" in sys.platform:
        arg_1 = ctypes.CDLL('libmkl_rt.so')
    else:
        arg_1 = ctypes.CDLL('libmkl_rt.dylib')
    arg_2 = arg_1.mkl_get_max_threads()
    arg_1.mkl_set_num_threads(ctypes.byref(ctypes.c_int(arg_0)))
    return arg_2


def Func(arg_0, arg_1):
    """ get total number of quartets possible for a split"""
    if (arg_1.is_leaf() or arg_1.is_root()):
        return 0
    else:
        ## Get counts on down edges.
        ## How to treat polytomies here?
        if len(arg_1.children) > 2:
            arg_2 = arg_1.children[0]
            arg_3 = arg_1.children[1]
            for arg_4 in arg_1.children[2:]:
                arg_3 += arg_4
        else:
            arg_2, arg_3 = arg_1.children
        arg_5 = sum(1 for i in arg_2.iter_leaves())
        arg_6 = sum(1 for i in arg_3.iter_leaves())

        ## get count on up edge sister
        arg_7 = arg_1.get_sisters()[0]
        arg_8 = sum(1 for i in arg_7.iter_leaves())

        ## everyone else
        arg_9 = arg_0 - (arg_5 + arg_6 + arg_8)

        ## return product
        return arg_5 * arg_6 * arg_8 * arg_9


def Func(arg_0, arg_1, arg_2):
    """ get total number of quartets sampled for a split"""
    ## convert tip names to ints
    arg_3 = sorted(arg_1)
    arg_4 = {name: arg_13 for arg_13, name in enumerate(arg_3)}

    ## skip some nodes
    if (arg_2.is_leaf() or arg_2.is_root()):
        return 0
    else:
        ## get counts on down edges
        if len(arg_2.children) > 2:
            arg_5 = arg_2.children[0]
            arg_6 = arg_2.children[1]
            for arg_7 in arg_2.children[2:]:
                arg_6 += arg_7
        else:
            arg_5, arg_6 = arg_2.children

        arg_8 = set(arg_4[arg_19] for arg_19 in arg_5.get_leaf_names())
        arg_9 = set(arg_4[arg_19] for arg_19 in arg_6.get_leaf_names())

        ## get count on up edge sister
        arg_10 = arg_2.get_sisters()[0]
        arg_11 = set(arg_4[arg_19] for arg_19 in arg_10.get_leaf_names())

        ## everyone else
        arg_12 = set(arg_4[arg_19] for arg_19 in arg_1) - set.union(arg_8, arg_9, arg_11)

    arg_13 = 0
    arg_14 = 0
    with h5py.File(arg_0.database.output, 'r') as io5:
        arg_15 = io5["quartets"].shape[0]
        while 1:
            ## break condition
            if arg_13 >= arg_15:
                break

            ## counts matches
            arg_16 = io5["quartets"][arg_13:arg_13+arg_0._chunksize]
            for arg_17 in arg_16:
                arg_18 = set(arg_17)
                if all([arg_18.intersection(arg_19) for arg_19 in [arg_8, arg_9, arg_11, arg_12]]):
                    arg_14 += 1

            ## increase span
            arg_13 += arg_0._chunksize
    return arg_14


def Func(arg_0, arg_1):
        """
        Runs quartet max-cut QMC on the quartets qdump file.
        """

        ## build command
        arg_0._tmp = os.path.join(arg_0.dirs, ".tmptre")
        arg_3 = [ip.bins.qmc, "qrtt="+arg_0.files.qdump, "otre="+arg_0._tmp]

        ## run it
        arg_4 = subprocess.Popen(arg_3, stderr=subprocess.STDOUT, stdout=subprocess.PIPE)
        arg_5 = arg_4.communicate()
        if arg_4.returncode:
            raise IPyradWarningExit(arg_5[1])

        ## parse tmp file written by qmc into a tree and rename it
        with open(arg_0._tmp, 'r') as intree:
            arg_6 = ete3.Tree(intree.read().strip())
            arg_7 = arg_6.get_leaves()
            for arg_8 in arg_7:
                arg_8.name = arg_0.samples[int(arg_8.name)]
            arg_9 = arg_6.write(format=9)

        ## save the tree to file
        if arg_1:
            arg_0.trees.boots = os.path.join(arg_0.dirs, arg_0.name+".boots")
            with open(arg_0.trees.boots, 'a') as outboot:
                outboot.write(arg_9+"\n")
        else:
            arg_0.trees.tree = os.path.join(arg_0.dirs, arg_0.name+".tree")
            with open(arg_0.trees.tree, 'w') as outtree:
                outtree.write(arg_9)

        ## save the file
        arg_0._save()


def Func(arg_0, arg_1, arg_2):
        """
        Enters results arrays into the HDF5 database.
        """

        ## two result arrs
        arg_3 = arg_0._chunksize
        arg_4, arg_5 = arg_2

        ## enter into db
        with h5py.File(arg_0.database.output, 'r+') as arg_6:
            arg_6['quartets'][arg_1:arg_1+arg_3] = arg_4

            ## entered as 0-indexed !
            if arg_0.params.save_invariants:
                if arg_0.checkpoint.boots:
                    arg_7 = "invariants/boot{}".format(arg_0.checkpoint.boots)
                    arg_6[arg_7][arg_1:arg_1+arg_3] = arg_5
                else:
                    arg_6["invariants/boot0"][arg_1:arg_1+arg_3] = arg_5


def Func(arg_0, arg_1, arg_2, arg_3, arg_4, arg_5, arg_6, **arg_7):
    """
    Creates a client to view ipcluster engines for a given profile and
    returns it with at least one engine spun up and ready to go. If no
    engines are found after nwait amount of time then an error is raised.
    If engines==MPI it waits a bit longer to find engines. If the number
    of engines is set then it waits even longer to try to find that number
    of engines.
    """

    ## save stds for later, we're gonna hide them to prevent external printing
    arg_8 = arg_10.stdout
    arg_9 = arg_10.stderr
    arg_10.stdout = cStringIO.StringIO()
    arg_10.stderr = cStringIO.StringIO()

    ## get cluster_info print string
    arg_13 = "{}establishing parallel connection:".format(arg_6)

    ## wrapped search for ipcluster
    try:
        ## are we looking for a running ipcluster instance?
        if arg_1 not in [None, "default"]:
            arg_14 = {'profile': arg_1, "timeout": arg_3}
        else:
            arg_15 = [arg_0, arg_1, arg_3]
            arg_16 = ["cluster_id", "profile", "timeout"]
            arg_14 = {key:value for key, value in zip(arg_16, arg_15)}

        ## get connection within timeout window of wait time and hide messages
        arg_17 = ipp.Client(**arg_14)
        arg_10.stdout = arg_8
        arg_10.stderr = arg_9

        ## check that all engines have connected
        if (arg_2 == "MPI") or ("ipyrad-cli-" in arg_0):
            if not arg_5:
                print(arg_13)

        for arg_18 in range(6000):
            arg_19 = len(arg_17)
            time.sleep(0.01)
            ## If MPI then wait for all engines to start so we can report
            ## how many cores are on each host. If Local then only wait for
            ## one engine to be ready and then just go.
            if (arg_2 == "MPI") or ("ipyrad-cli-" in arg_0):
                ## wait for cores to be connected
                if arg_4:
                    time.sleep(0.1)
                    if arg_19 == arg_4:
                        break
                if arg_19:
                    time.sleep(3)
                    if len(arg_17) == arg_19:
                        break
            else:
                if arg_4:
                    if arg_19 == arg_4:
                        break
                else:
                    if arg_19:
                        break

    except KeyboardInterrupt as inst:
        ## ensure stdout is reset even if Exception was raised
        arg_10.stdout = arg_8
        arg_10.stderr = arg_9
        raise inst

    ## This is raised if ipcluster is not running ------------
    except IOError as inst:
        ## ensure stdout is reset even if Exception was raised
        arg_10.stdout = arg_8
        arg_10.stderr = arg_9
        if "ipyrad-cli-" in arg_0:
            raise IPyradWarningExit(NO_IPCLUSTER_CLI)
        else:
            raise IPyradWarningExit(NO_IPCLUSTER_API)

    except (ipp.TimeoutError, ipp.NoEnginesRegistered) as inst:
        ## raised by ipp if no connection file is found for 'nwait' seconds
        arg_10.stdout = arg_8
        arg_10.stderr = arg_9
        raise inst

    except Exception as inst:
        ## if any other exceptions were missed...
        arg_10.stdout = arg_8
        arg_10.stderr = arg_9
        raise inst

    finally:
        ## ensure that no matter what we reset the stds
        arg_10.stdout = arg_8
        arg_10.stderr = arg_9

    return arg_17


def Func(arg_0):
    """ Memoization decorator for a function taking one or more arguments. """
    class Memodict(dict):
        """ just a dict"""
        def __getitem__(arg_1, *arg_2):
            return dict.__getitem__(arg_1, arg_2)

        def __missing__(arg_1, arg_2):
            """ this makes it faster """
            arg_3 = arg_1[arg_2] = arg_0(*arg_2)
            return arg_3

    return Memodict().__getitem__


def Func(arg_0):
    """
    Returns both resolutions of a cut site that has an ambiguous base in
    it, else the single cut site
    """
    arg_1 = []
    if any([arg_2 in list("RKSYWM") for arg_2 in arg_0]):
        for arg_3 in list("RKSYWM"):
            if arg_3 in arg_0:
                arg_1.append(arg_0.replace(arg_3, AMBIGS[arg_3][0]))
                arg_1.append(arg_0.replace(arg_3, AMBIGS[arg_3][1]))
        return arg_1
    else:
        return [arg_0, ""]


def Func(arg_0):
    """ takes diploid consensus alleles with phase data stored as a mixture
    of upper and lower case characters and splits it into 2 alleles """

    ## store two alleles, allele1 will start with bigbase
    arg_1 = list(arg_0)
    arg_2 = list(arg_0)
    arg_3 = [i for (i, j) in enumerate(arg_0) if j in "RKSWYMrkswym"]

    ## do remaining h sites
    for arg_4 in arg_3:
        arg_5 = arg_0[arg_4]
        if arg_5.isupper():
            arg_1[arg_4] = PRIORITY[arg_5]
            arg_2[arg_4] = MINOR[arg_5]
        else:
            arg_1[arg_4] = MINOR[arg_5.upper()]
            arg_2[arg_4] = PRIORITY[arg_5.upper()]

    ## convert back to strings
    arg_1 = "".join(arg_1)
    arg_2 = "".join(arg_2)

    return arg_1, arg_2


def Func(arg_0):
    """ returns a seq with Funclement. Preserves little n's for splitters."""
    ## makes base to its small Funclement then makes upper
    return arg_0.replace("A", 't')\
              .replace('T', 'a')\
              .replace('C', 'g')\
              .replace('G', 'c')\
              .replace('n', 'Z')\
              .upper()\
              .replace("Z", "n")


def Func(arg_0):
    "returns reverse complement of a string"
    arg_0 = arg_0[::-1].strip()\
                             .replace("A", "t")\
                             .replace("T", "a")\
                             .replace("C", "g")\
                             .replace("G", "c").upper()
    return arg_0


def Func(arg_0, arg_1, arg_2="", arg_3="  "):
    """ prints a progress bar """
    if arg_0:
        arg_4 = 100*(arg_1 / float(arg_0))
    else:
        arg_4 = 100

    arg_5 = '#'*int(arg_4/5.)
    arg_6 = ' '*int(20-len(arg_5))
    if not ipyrad.__interactive__:
        arg_2 = arg_2.rsplit("|", 2)[0]

    arg_7 = [arg_3, arg_5+arg_6, int(arg_4), arg_2]
    print("\r{}[{}] {:>3}% {} ".format(*arg_7), end="")
    sys.stdout.flush()


def Func(arg_0, arg_1=True):
    """ gets optimum threaded view of ids given the host setup """
    ## engine ids
    ## e.g., [0, 1, 2, 3, 4, 5, 6, 7, 8]
    arg_2 = arg_0.ids

    ## get host names
    ## e.g., ['a', 'a', 'b', 'b', 'a', 'c', 'c', 'c', 'c']
    arg_3 = arg_0.direct_view()
    arg_4 = arg_3.apply_sync(socket.gethostname)

    ## group ids into a dict by their hostnames
    ## e.g., {a: [0, 1, 4], b: [2, 3], c: [5, 6, 7, 8]}
    arg_5 = defaultdict(list)
    for arg_6, arg_7 in zip(arg_4, arg_2):
        arg_5[arg_6].append(arg_7)

    ## Now split threads on the same host into separate proc if there are many
    arg_8 = arg_5.keys()
    for arg_9 in arg_8:
        arg_10 = arg_5[arg_9]
        arg_11 = len(arg_10)
        if len(arg_10) >= 4:
            arg_11 = 2
        ## if 4 nodes and 4 ppn, put one sample per host
        if (len(arg_10) == 4) and (len(arg_4) >= 4):
            arg_11 = 4
        if len(arg_10) >= 6:
            arg_11 = 3
        if len(arg_10) >= 8:
            arg_11 = 4
        if len(arg_10) >= 16:
            arg_11 = 4
        ## split ids into groups of maxt
        arg_12 = [arg_10[i:i+arg_11] for i in xrange(0, len(arg_10), arg_11)]
        arg_13 = len(arg_12)
        ## if anything was split (lth>1) update hostdict with new proc
        if arg_13 > 1:
            arg_5.pop(arg_9)
            for arg_14 in range(arg_13):
                arg_5[arg_15(arg_9)+"_"+arg_15(arg_14)] = arg_12[arg_14]

    ## make sure split numbering is correct
    #threaded = hostdict.values()
    #assert len(ipyclient.ids) <= len(list(itertools.chain(*threaded)))
    LOGGER.info("threaded_view: %s", dict(arg_5))
    return arg_5


def Func():
    """
    Detects the number of CPUs on a system. This is better than asking
    ipyparallel since ipp has to wait for Engines to spin up.
    """
    # Linux, Unix and MacOS:
    if hasattr(os, "sysconf"):
        if os.sysconf_names.has_key("SC_NPROCESSORS_ONLN"):
            # Linux & Unix:
            arg_0 = os.sysconf("SC_NPROCESSORS_ONLN")
            if isinstance(arg_0, int) and arg_0 > 0:
                return arg_0
        else: # OSX:
            return int(os.popen2("sysctl -n hw.ncpu")[1].read())
    # Windows:
    if os.environ.has_key("NUMBER_OF_PROCESSORS"):
        arg_0 = int(os.environ["NUMBER_OF_PROCESSORS"])
        if arg_0 > 0:
            return arg_0
    return 1


def Func(arg_0, arg_1, arg_2, arg_3, arg_4, arg_5, arg_6, arg_7, arg_8, arg_9):
    """ make the subprocess call to structure """
    ## create call string
    arg_10 = os.path.join(arg_4, "{}-K-{}-rep-{}".format(arg_3, arg_8, arg_9))

    arg_11 = ["structure",
           "-m", arg_0,
           "-e", arg_1,
           "-K", str(arg_8),
           "-D", str(arg_5),
           "-N", str(arg_6),
           "-L", str(arg_7),
           "-i", arg_2,
           "-o", arg_10]

    ## call the shell function
    arg_12 = subprocess.Popen(arg_11,
                            stdout=subprocess.PIPE,
                            stderr=subprocess.STDOUT)
    arg_13 = arg_12.communicate()

    ## cleanup
    arg_14 = [arg_0, arg_1, arg_2]
    for arg_15 in arg_14:
        if os.path.exists(arg_15):
            os.remove(arg_15)
    return arg_13


def Func(arg_0, arg_1, arg_2, arg_3):
    """ private function to clumpp results"""

    ## concat results for k=x
    arg_4, arg_5 = _concat_reps(arg_0, arg_1, arg_2, arg_3)
    if arg_4:
        arg_6 = arg_4[0].inds
        arg_7 = len(arg_4)
    else:
        arg_6 = arg_7 = 0
    if not arg_4:
        return "no result files found"

    arg_8 = os.path.join(arg_0.workdir, "tmp.clumppparams.txt")
    arg_0.clumppparams.kpop = arg_1
    arg_0.clumppparams.c = arg_6
    arg_0.clumppparams.r = arg_7
    with open(arg_8, 'w') as tmp_c:
        tmp_c.write(arg_0.clumppparams._asfile())

    ## create CLUMPP args string
    arg_12 = os.path.join(arg_0.workdir,
                "{}-K-{}.outfile".format(arg_0.name, arg_1))
    arg_13 = os.path.join(arg_0.workdir,
                "{}-K-{}.indfile".format(arg_0.name, arg_1))
    arg_14 = os.path.join(arg_0.workdir,
                "{}-K-{}.miscfile".format(arg_0.name, arg_1))
    arg_15 = ["CLUMPP", arg_8,
           "-i", arg_13,
           "-o", arg_12,
           "-j", arg_14,
           "-r", str(arg_7),
           "-c", str(arg_6),
           "-k", str(arg_1)]

    ## call clumpp
    arg_16 = subprocess.Popen(arg_15,
                            stderr=subprocess.STDOUT,
                            stdout=subprocess.PIPE)
    arg_17 = arg_16.communicate()

    ## cleanup
    for arg_18 in [arg_13, arg_14]:
        if os.path.exists(arg_18):
            os.remove(arg_18)

    ## parse clumpp results file
    arg_19 = os.path.join(arg_0.workdir, "{}-K-{}.outfile".format(arg_0.name, arg_1))
    if os.path.exists(arg_19):
        arg_20 = pd.read_csv(arg_19, delim_whitespace=True, header=None)
        arg_21 = arg_20.loc[:, 5:]

        ## apply names to cols and rows
        arg_21.columns = range(arg_21.shape[1])
        arg_21.index = arg_0.labels
        if not arg_3:
            sys.stderr.write(
                "[K{}] {}/{} results permuted across replicates (max_var={}).\n"\
                .format(arg_1, arg_7, arg_7+arg_5, arg_2))
        return arg_21

    else:
        sys.stderr.write("No files ready for {}-K-{} in {}\n"\
                         .format(arg_0.name, arg_1, arg_0.workdir))
        return


def Func(arg_0, arg_1, arg_2, arg_3):
    """
    Calculates Evanno method K value scores for a series
    of permuted clumpp results.
    """

    ## iterate across k-vals
    arg_1 = sorted(arg_1)
    arg_4 = []

    for arg_5 in arg_1:
        ## concat results for k=x
        arg_6, arg_7 = _concat_reps(arg_0, arg_5, arg_2, arg_3)

        ## report if some results were excluded
        if arg_7:
            if not arg_3:
                sys.stderr.write(
                "[K{}] {} reps excluded (not converged) see 'max_var_multiple'.\n"\
                .format(arg_5, arg_7))

        if arg_6:
            arg_8 = arg_6[0].inds
            arg_9 = len(arg_6)
        else:
            arg_8 = arg_9 = 0
        if not arg_6:
            print "no result files found"

        ## all we really need is the lnlik
        arg_4.append([arg_10.est_lnlik for arg_10 in arg_6])

    ## compare lnlik and var of results
    if len(arg_4) > 1:
        arg_11 = [np.mean(arg_10) for arg_10 in arg_4]
        arg_12 = [np.std(arg_10, ddof=1) for arg_10 in arg_4]
    else:
        arg_11 = arg_4
        arg_12 = np.nan

    arg_13 = pd.DataFrame(
        index=arg_1,
        data={
            "Nreps": [len(arg_10) for arg_10 in arg_4],
            "lnPK": [0] * len(arg_1),
            "lnPPK": [0] * len(arg_1),
            "deltaK": [0] * len(arg_1),
            "estLnProbMean": arg_11,
            "estLnProbStdev": arg_12,
        }
        )

    ## calculate Evanno's
    for arg_5 in arg_1[1:]:
        arg_13.loc[arg_5, "lnPK"] = arg_13.loc[arg_5, "estLnProbMean"] \
                              - arg_13.loc[arg_5-1, "estLnProbMean"]

    for arg_5 in arg_1[1:-1]:
        arg_13.loc[arg_5, "lnPPK"] = abs(arg_13.loc[arg_5+1, "lnPK"]
                                     - arg_13.loc[arg_5, "lnPK"])
        arg_13.loc[arg_5, "deltaK"] = (abs(
                                    arg_13.loc[arg_5+1, "estLnProbMean"] - \
                                    2.0 * arg_13.loc[arg_5, "estLnProbMean"] + \
                                    arg_13.loc[arg_5-1, "estLnProbMean"]) / \
                                   arg_13.loc[arg_5, "estLnProbStdev"])

    ## return table
    return arg_13


def Func(arg_0, arg_1, arg_2=0, arg_3=False):
        """
        Calculates the Evanno table from results files for tests with
        K-values in the input list kvalues. The values lnPK, lnPPK,
        and deltaK are calculated. The max_var_multiplier arg can be used
        to exclude results files based on variance of the likelihood as a
        proxy for convergence.

        Parameters:
        -----------
        kvalues : list
            The list of K-values for which structure was run for this object.
            e.g., kvalues = [3, 4, 5]

        max_var_multiple: int
            A multiplier value to use as a filter for convergence of runs.
            Default=0=no filtering. As an example, if 10 replicates
            were run then the variance of the run with the minimum variance is
            used as a benchmark. If other runs have a variance that is N times
            greater then that run will be excluded. Remember, if replicate runs
            sampled different distributions of SNPs then it is not unexpected that
            they will have very different variances. However, you may still want
            to exclude runs with very high variance since they likely have
            not converged.

        quiet: bool
            Suppresses printed messages about convergence.

        Returns:
        --------
        table : pandas.DataFrame
            A data frame with LPK, LNPPK, and delta K. The latter is typically
            used to find the best fitting value of K. But be wary of over
            interpreting a single best K value.
        """
        ## do not allow bad vals
        if arg_2:
            if arg_2 < 1:
                raise ValueError('max_variance_multiplier must be >1')

        arg_4 = _Func(arg_0, arg_1, arg_2, arg_3)
        return arg_4


def Func(arg_0, arg_1, arg_2):
        """ Func an _f structure output file """
        arg_3 = ""
        with open(arg_0.repfile) as orep:
            arg_4 = orep.readlines()
            for arg_5 in arg_4:
                ## stat lines
                if "Estimated Ln Prob of Data" in arg_5:
                    arg_0.est_lnlik = float(arg_5.split()[-1])
                if "Mean value of ln likelihood" in arg_5:
                    arg_0.mean_lnlik = float(arg_5.split()[-1])
                if "Variance of ln likelihood" in arg_5:
                    arg_0.var_lnlik = float(arg_5.split()[-1])
                if "Mean value of alpha" in arg_5:
                    arg_0.alpha = float(arg_5.split()[-1])

                ## matrix lines
                arg_10 = arg_1.search(arg_5)
                arg_11 = arg_2.search(arg_5)

                #if ")   :  " in line:
                if arg_10:
                    ## check if sample is supervised...
                    arg_12 = arg_5.strip().split()
                    arg_13 = "{}{}{}".format(
                        " ".join([arg_12[0], arg_12[0], arg_12[2],
                                  arg_12[0].split('.')[0]]),
                        " :  ",
                        " ".join(arg_12[4:])
                    )
                    arg_0.inds += 1
                    arg_3 += arg_13+"\n"

                elif arg_11:
                    ## check if sample is supervised...
                    arg_12 = arg_5.strip().split()
                    arg_14 = ["0.000"] * arg_0.kpop
                    arg_15 = int(arg_12[3]) - 1
                    arg_14[arg_15] = "1.000"
                    arg_13 = "{}{}{}".format(
                        " ".join([arg_12[0], arg_12[0], arg_12[2],
                                  arg_12[0].split('.')[0]]),
                        " :  ",
                        " ".join(arg_14)
                    )
                    arg_0.inds += 1
                    arg_3 += arg_13+"\n"

            arg_3 += "\n"
        return arg_3


def Func(arg_0):
    """ call the command as sps """
    arg_1 = subprocess.Popen(
        arg_0,
        stderr=subprocess.STDOUT,
        stdout=subprocess.PIPE
        )
    arg_2 = arg_1.communicate()
    return arg_2


def Func(arg_0,
        arg_1=None,
        arg_2=False,
        arg_3=False,
        arg_4=False,
        ):
        """
        Submits raxml job to Func. If no ipyclient object is provided then
        the function will block until the raxml Func is finished. If an ipyclient
        is provided then the job is sent to a remote engine and an asynchronous
        result object is returned which can be queried or awaited until it finishes.

        Parameters
        -----------
        ipyclient:
            Not yet supported...
        quiet:
            suppress print statements
        force:
            overwrite existing results files with this job name.
        block:
            will block progress in notebook until job finishes, even if job
            is Funcning on a remote ipyclient.
        """

        ## stop before trying in raxml
        if arg_3:
            for arg_5, arg_6 in arg_0.trees:
                if os.path.exists(arg_6):
                    os.remove(arg_6)
        if os.path.exists(arg_0.trees.info):
            print("Error: set a new name for this job or use Force flag.\nFile exists: {}"\
                  .format(arg_0.trees.info))
            return

        ## TODO: add a progress bar tracker here. It could even read it from
        ## the info file that is being written.
        ## submit it
        if not arg_1:
            arg_7 = _call_raxml(arg_0._command_list)
            arg_0.stdout = arg_7[0]
            arg_0.stderr = arg_7[1]
        else:
            ## find all hosts and submit job to the host with most available engines
            arg_10 = arg_1.load_balanced_view()
            arg_0.async = arg_10.apply(_call_raxml, arg_0._command_list)

        ## initiate random seed
        if not arg_2:
            if not arg_1:
                ## look for errors
                if "Overall execution time" not in arg_0.stdout:
                    print("Error in raxml Func\n" + arg_0.stdout)
                else:
                    print("job {} finished successfully".format(arg_0.params.n))
            else:
                print("job {} submitted to cluster".format(arg_0.params.n))


def Func(arg_0):
        """ find binaries available"""

        ## check for binary
        arg_1 = ["raxmlHPC-PTHREADS", "raxmlHPC-PTHREADS-SSE3"]

        ## check user binary first, then backups
        for arg_2 in [arg_0.params.binary] + arg_1:
            arg_3 = subprocess.Popen(["which", arg_0.params.binary],
                    stdout=subprocess.PIPE,
                    stderr=subprocess.STDOUT).communicate()
            ## update the binary
            if arg_3:
                arg_0.params.binary = arg_2

        ## if none then raise error
        if not arg_3[0]:
            raise Exception(BINARY_ERROR.format(arg_0.params.binary))


def Func(arg_0, arg_1):
    """
    return array of bootstrap D-stats
    """
    ## hold results (nboots, [dstat, ])
    arg_2 = np.zeros((arg_1,))

    ## iterate to fill boots
    for arg_3 in xrange(arg_1):
        ## sample with replacement
        arg_4 = np.random.randint(0, arg_0.shape[0], arg_0.shape[0])
        arg_5 = arg_0[arg_4]
        arg_6, arg_6, arg_7 = _prop_dstat(arg_5)
        arg_2[arg_3] = arg_7

    ## return bootarr
    return arg_2


def Func(arg_0):
    """
    returns a fields argument formatted as a list of strings.
    and doesn't allow zero.
    """
    ## make sure fields will work
    if isinstance(arg_0, int):
        arg_0 = str(arg_0)
    if isinstance(arg_0, str):
        if "," in arg_0:
            arg_0 = [str(i) for i in arg_0.split(",")]
        else:
            arg_0 = [str(arg_0)]
    elif isinstance(arg_0, (tuple, list)):
        arg_0 = [str(i) for i in arg_0]
    else:
        raise IPyradWarningExit("fields not properly formatted")

    ## do not allow zero in fields
    arg_0 = [i for i in arg_0 if i != '0']

    return arg_0


def Func(arg_0, *arg_1: arg_2, **arg_3: arg_4):
        """Get a player information

        Parameters
        ----------
        \*tags: str
            Valid player tags. Minimum length: 3
            Valid characters: 0289PYLQGRJCUV
        \*\*keys: Optional[list] = None
            Filter which keys should be included in the
            response
        \*\*exclude: Optional[list] = None
            Filter which keys should be excluded from the
            response
        \*\*timeout: Optional[int] = None
            Custom timeout that overwrites Client.timeout
        """
        arg_5 = arg_0.api.PLAYER + '/' + ','.join(arg_1)
        return arg_0._get_model(arg_5, FullPlayer, **arg_3)


def Func(arg_0, *arg_1: arg_2, **arg_3: arg_4):
        """Get a clan information

        Parameters
        ----------
        \*tags: str
            Valid clan tags. Minimum length: 3
            Valid characters: 0289PYLQGRJCUV
        \*\*keys: Optional[list] = None
            Filter which keys should be included in the
            response
        \*\*exclude: Optional[list] = None
            Filter which keys should be excluded from the
            response
        \*\*timeout: Optional[int] = None
            Custom timeout that overwrites Client.timeout
        """
        arg_5 = arg_0.api.CLAN + '/' + ','.join(arg_1)
        return arg_0._get_model(arg_5, FullClan, **arg_3)


def Func(arg_0, **arg_1: arg_2):
        """Search for a tournament

        Parameters
        ----------
        name: str
            The name of the tournament
        \*\*keys: Optional[list] = None
            Filter which keys should be included in the
            response
        \*\*exclude: Optional[list] = None
            Filter which keys should be excluded from the
            response
        \*\*max: Optional[int] = None
            Limit the number of items returned in the response
        \*\*page: Optional[int] = None
            Works with max, the zero-based page of the
            items
        \*\*timeout: Optional[int] = None
            Custom timeout that overwrites Client.timeout
        """
        arg_3 = arg_0.api.TOURNAMENT + '/search'
        return arg_0._get_model(arg_3, PartialClan, **arg_1)