# Path: unicycler/misc.py
# def weighted_average(num_1, num_2, weight_1, weight_2):
#     """
#     A simple weighted mean of two numbers.
#     """
#     weight_sum = weight_1 + weight_2
#     if weight_sum == 0:
#         return (num_1 + num_2) / 2.0
#     return num_1 * (weight_1 / weight_sum) + num_2 * (weight_2 / weight_sum)
# 
# def reverse_complement(seq):
#     """
#     Given a DNA sequences, this function returns the reverse complement sequence.
#     """
#     return ''.join([complement_base(x) for x in seq][::-1])
# 
# def get_num_agreement(num_1, num_2):
#     """
#     Returns a value between 0.0 and 1.0 describing how well the numbers agree.
#     1.0 is perfect agreement and 0.0 is the worst.
#     """
#     if num_1 == 0.0 and num_2 == 0.0:
#         return 1.0
#     if num_1 < 0.0 and num_2 < 0.0:
#         num_1 *= -1
#         num_2 *= -1
#     if num_1 * num_2 < 0.0:
#         return 0.0
#     return min(num_1, num_2) / max(num_1, num_2)


import sys
from collections import defaultdict
from .misc import weighted_average, reverse_complement, get_num_agreement
from . import settings
    from .cpp_wrappers import fully_global_alignment, path_alignment

        paths_and_scores.append((path, raw_score, length_discrepancy, scaled_score))

    # Sort the paths from highest to lowest quality.
    paths_and_scores = sorted(paths_and_scores, key=lambda x: (-x[1], x[2], -x[3]))

    # Don't bother keeping paths which are much worse than the best.
    if paths_and_scores:
        best_scaled_score = paths_and_scores[0][3]
        min_scaled_score = best_scaled_score * 0.95
        paths_and_scores = [x for x in paths_and_scores if x[3] >= min_scaled_score]

    return paths_and_scores, progressive_path_search


def all_paths(graph, start, end, min_length, max_length):
    """
    Returns a list of all paths which connect the starting segment to the ending segment and
    are within the length bounds. The start and end segments are not themselves included in the
    paths. Returns an empty list if no paths exist.
    Loops in the graph (especially loops of short segments which don't add much to the path
    length) can result in very large numbers of potential paths in complex areas. To somewhat
    manage this, we exclude paths which include too many copies of a segment. 'Too many copies'
    is defined as double the copy depth count or the double the depth over start/end depth.
    """
    if start not in graph.forward_links:
        return []

    start_seg = graph.segments[abs(start)]
    end_seg = graph.segments[abs(end)]

# Path: unicycler/misc.py
# def weighted_average(num_1, num_2, weight_1, weight_2):
#     """
#     A simple weighted mean of two numbers.
#     """
#     weight_sum = weight_1 + weight_2
#     if weight_sum == 0:
#         return (num_1 + num_2) / 2.0
#     return num_1 * (weight_1 / weight_sum) + num_2 * (weight_2 / weight_sum)
# 
# def reverse_complement(seq):
#     """
#     Given a DNA sequences, this function returns the reverse complement sequence.
#     """
#     return ''.join([complement_base(x) for x in seq][::-1])
# 
# def get_num_agreement(num_1, num_2):
#     """
#     Returns a value between 0.0 and 1.0 describing how well the numbers agree.
#     1.0 is perfect agreement and 0.0 is the worst.
#     """
#     if num_1 == 0.0 and num_2 == 0.0:
#         return 1.0
#     if num_1 < 0.0 and num_2 < 0.0:
#         num_1 *= -1
#         num_2 *= -1
#     if num_1 * num_2 < 0.0:
#         return 0.0
#     return min(num_1, num_2) / max(num_1, num_2)


# Path: unicycler/path_finding.py
import sys
from collections import defaultdict
from .misc import weighted_average, reverse_complement, get_num_agreement
from . import settings
    from .cpp_wrappers import fully_global_alignment, path_alignment


        paths_and_scores.append((path, raw_score, length_discrepancy, scaled_score))

    # Sort the paths from highest to lowest quality.
    paths_and_scores = sorted(paths_and_scores, key=lambda x: (-x[1], x[2], -x[3]))

    # Don't bother keeping paths which are much worse than the best.
    if paths_and_scores:
        best_scaled_score = paths_and_scores[0][3]
        min_scaled_score = best_scaled_score * 0.95
        paths_and_scores = [x for x in paths_and_scores if x[3] >= min_scaled_score]

    return paths_and_scores, progressive_path_search


def all_paths(graph, start, end, min_length, max_length):
    """
    Returns a list of all paths which connect the starting segment to the ending segment and
    are within the length bounds. The start and end segments are not themselves included in the
    paths. Returns an empty list if no paths exist.
    Loops in the graph (especially loops of short segments which don't add much to the path
    length) can result in very large numbers of potential paths in complex areas. To somewhat
    manage this, we exclude paths which include too many copies of a segment. 'Too many copies'
    is defined as double the copy depth count or the double the depth over start/end depth.
    """
    if start not in graph.forward_links:
        return []

    start_seg = graph.segments[abs(start)]
    end_seg = graph.segments[abs(end)]

    start_end_depth = weighted_average(start_seg.depth, end_seg.depth,