def mkp_mst(D, W):
    best_approx = DeliveryPath()

    for count in range(1, len(D)):
        continue_ = False
        for haul in combinations(D, count):
            shortest_path = DeliveryPath(haul)

            if shortest_path.weight > W:
                continue
            continue_ = True

            approx = DeliveryPath(haul)
            if approx > best_approx:
                best_approx = approx
        if not continue_:
            break

    return best_approx


if __name__ == '__main__':
    deliveries = Delivery.generate(20)
    weight_limit = 250
    path = mkp_mst(D=deliveries, W=weight_limit)
    path.plot(all_deliveries=deliveries)
    print(f"best profit: ${path.profit:.2f}")
    print(f"capacity used: {path.weight / weight_limit:.1%}")
def compare_mkp_solutions():
    print("Comparing up to brute force limits (n=8)")
    results = results_init()
    for i in range(10):
        deliveries = Delivery.generate(8)
        weight_limit = 250
        for algo in algorithms:
            t0 = perf_counter()
            path = algo(D=deliveries, W=weight_limit)
            results[algo.__name__]['time'] += perf_counter() - t0
            results[algo.__name__]['profit'] += path.profit
        print(f"Round {i+1}/10")
    pprint(results, width=50)

    print('Compared to brute force:')
    bf_res = results[algorithms[0].__name__]
    for algo in algorithms[1:]:
        n = algo.__name__
        print(f"{n}: "
              f"{results[n]['profit'] / bf_res['profit']:.1%} profit, "
              f"{bf_res['time'] / results[n]['time']:.0f}x faster")

    del algorithms[0]

    print("\nComparing up to combinatorial mst limits (n=16)")
    results = results_init()
    for i in range(10):
        deliveries = Delivery.generate(16)
        weight_limit = 250
        for algo in algorithms:
            t0 = perf_counter()
            path = algo(D=deliveries, W=weight_limit)
            results[algo.__name__]['time'] += perf_counter() - t0
            results[algo.__name__]['profit'] += path.profit
        print(f"Round {i+1}/10")
    pprint(results, width=50)

    print('Compared to combinatorial mst:')
    cmst_res = results[algorithms[0].__name__]
    for algo in algorithms[1:]:
        n = algo.__name__
        print(f"{n}: "
              f"{results[n]['profit'] / cmst_res['profit']:.1%} profit, "
              f"{cmst_res['time'] / results[n]['time']:.0f}x faster")

    del algorithms[0]

    print("\nComparing the two fast approximation algorithms (n=200)")
    results = results_init()
    for i in range(10):
        deliveries = Delivery.generate(250)
        weight_limit = 2000
        for algo in algorithms:
            t0 = perf_counter()
            path = algo(D=deliveries, W=weight_limit)
            results[algo.__name__]['time'] += perf_counter() - t0
            results[algo.__name__]['profit'] += path.profit
        print(f"Round {i+1}/10")
    pprint(results, width=50)

    print('Comparing:')
    for index in (0, 1):
        n1 = algorithms[index].__name__
        n2 = algorithms[not index].__name__
        print(f"{n1}: "
              f"{results[n1]['profit'] / results[n2]['profit']:.1%} profit, "
              f"{results[n2]['time'] / results[n1]['time']:.2f}x faster")

def brute_force_mkp(D, W):
    best_path = DeliveryPath()

    for count in range(len(D)):
        for haul in combinations(D, count + 1):
            new_path = DeliveryPath(haul, make_mst=False)

            if new_path.weight > W:
                continue

            for path in permutations(haul):
                path = DeliveryPath(path, make_mst=False)
                if path > new_path:
                    new_path = path

            if new_path.profit > best_path.profit:
                best_path = new_path

    return best_path


if __name__ == '__main__':
    deliveries = Delivery.generate(10)
    weight_limit = 250
    path = brute_force_mkp(D=deliveries, W=weight_limit)
    path.plot(all_deliveries=deliveries)
    print(f"best profit: ${path.profit:.2f}")
    print(f"capacity used: {path.weight / weight_limit:.1%}")