Merge related samples.

    :param file_name: the name of the input file.
    :param out_prefix: the prefix of the output files.
    :param no_status: is there a status column in the file?

    :type file_name: str
    :type out_prefix: str
    :type no_status: boolean

    In the output file, there are a pair of samples per line. Hence, one can
    find related individuals by merging overlapping pairs.

def merge_related_samples(file_name, out_prefix, no_status):
    """Merge related samples.

    :param file_name: the name of the input file.
    :param out_prefix: the prefix of the output files.
    :param no_status: is there a status column in the file?

    :type file_name: str
    :type out_prefix: str
    :type no_status: boolean

    In the output file, there are a pair of samples per line. Hence, one can
    find related individuals by merging overlapping pairs.

    """
    # What we need to save
    status = {}
    samples_sets = []
    open_function = open
    if file_name.endswith(".gz"):
        open_function = gzip.open
    with open_function(file_name, 'rb') as input_file:
        header_index = dict([
            (col_name, i) for i, col_name in
            enumerate(input_file.readline().rstrip("\r\n").split("\t"))
        ])
        for col_name in {"FID1", "IID1", "FID2", "IID2"}:
            if col_name not in header_index:
                msg = "{}: no column named {}".format(file_name, col_name)
                raise ProgramError(msg)
        if not no_status:
            if "status" not in header_index:
                msg = "{}: no column named status".format(file_name)
                raise ProgramError(msg)

        for line in input_file:
            row = line.rstrip("\r\n").split("\t")
            sample_1 = (row[header_index["FID1"]], row[header_index["IID1"]])
            sample_2 = (row[header_index["FID2"]], row[header_index["IID2"]])
            tmp_set = {sample_1, sample_2}
            match = False
            for i in xrange(len(samples_sets)):
                if len(tmp_set & samples_sets[i]) > 0:
                    # We have a match
                    samples_sets[i] |= tmp_set
                    match = True
            if not match:
                # We did not find a match, so we add
                samples_sets.append(tmp_set)

            # Check for the status
            the_status = "None"
            if not no_status:
                the_status = row[header_index["status"]]
            status[(sample_1, sample_2)] = the_status

    # Doing a final check
    final_samples_set = []
    removed = set()
    for i in xrange(len(samples_sets)):
        if i in removed:
            # We removed this group
            continue
        group = samples_sets[i]
        j = i + 1
        while j < len(samples_sets):
            if j in removed:
                j += 1
                continue
            if len(group & samples_sets[j]) > 0:
                # We have a match, we start from the beginning
                group |= samples_sets[j]
                removed.add(j)
                j = i + 1
                continue
            j += 1

        final_samples_set.append(group)

    # Printing the output file
    output_file = None
    try:
        output_file = open(out_prefix + ".merged_related_individuals", 'w')
        to_print = ["index", "FID1", "IID1", "FID2", "IID2"]
        if not no_status:
            to_print.append("status")
        print >>output_file, "\t".join(to_print)
    except IOError:
        msg = "{}: can't write file".format(out_prefix +
                                            ".merged_related_individuals")
        raise ProgramError(msg)

    # Iterating on the groups
    chosen_samples = set()
    remaining_samples = set()
    for i, group in enumerate(final_samples_set):
        index = str(i+1)
        for sample_1, sample_2 in status.iterkeys():
            if (sample_1 in group) and (sample_2 in group):
                to_print = [index, sample_1[0], sample_1[1], sample_2[0],
                            sample_2[1]]
                if not no_status:
                    to_print.append(status[(sample_1, sample_2)])
                print >>output_file, "\t".join(to_print)

        # Choose a random sample from the group
        chosen = random.choice(list(group))
        chosen_samples.add(chosen)
        remaining_samples |= group - {chosen}

    # Printing the files
    try:
        filename = out_prefix + ".chosen_related_individuals"
        with open(filename, "w") as chosen_file:
            for sample_id in chosen_samples:
                print >>chosen_file, "\t".join(sample_id)

        filename = out_prefix + ".discarded_related_individuals"
        with open(filename, "w") as discarded_file:
            for sample_id in remaining_samples:
                print >>discarded_file, "\t".join(sample_id)
    except IOError:
        msg = "{}: can't write files".format(out_prefix + ".*")
        raise ProgramError(msg)

    # Closing the output file
    output_file.close()