def _remove_node(tt: TourneyTree, pos: Position) -> int:
    children = tt.get_children(pos)
    val = tt[pos]

    if len(children) == 0:
        tt[pos] = -1
        return -1

    if len(children) == 1:
        new_val = _remove_node(tt, children[0])

        tt[pos] = new_val
        return new_val

    same_child = tt.get_same_child(pos)
    other_child = tt.get_sibling(same_child)
    other_val = tt[other_child]

    new_same_val = _remove_node(tt, same_child)
    if new_same_val == -1:
        tt[pos] = other_val
        return other_val

    tt[pos] = new_same_val if COMPARE(new_same_val, other_val) else other_val
    return tt[pos]
def _guess_k_smallest_3(tt: TourneyTree, k: int) -> [int]:
    # item: (row on, row of partner)
    row_scores = dict()
    row_scores[tt[tt.top()]] = (tt.height - 1, tt.height - 1)

    sort_by = dict()
    sort_by[tt[tt.top()]] = 2 * (tt.height - 1)

    for row in range(tt.height - 2, -1, -1):
        for pos in tt.iter_row(row):
            if tt[pos] not in row_scores:
                row_scores[tt[pos]] = (row,
                                       row_scores[tt[tt.get_sibling(pos)]][0])
                # you can weight this 0.001 constant differently
                # doesn't seem fruitful though
                sort_by[tt[pos]] = row + 0.001 * row_scores[tt[tt.get_sibling(
                    pos)]][0]

    return sorted(row_scores, key=sort_by.__getitem__, reverse=True)[:k]
def _guess_k_smallest_2(tt: TourneyTree, k: int) -> [int]:
    # item: (row on, row of partner)
    row_scores = dict()
    row_scores[tt[tt.top()]] = (tt.height - 1, tt.height - 1)

    for row in range(tt.height - 2, -1, -1):
        for pos in tt.iter_row(row):
            if tt[pos] not in row_scores:
                row_scores[tt[pos]] = (row,
                                       row_scores[tt[tt.get_sibling(pos)]][0])

        if len(row_scores) >= k:
            return sorted(row_scores, key=row_scores.__getitem__,
                          reverse=True)[:k]

    return sorted(row_scores, key=row_scores.__getitem__, reverse=True)[:k]
def _evaluate_contender(tt: TourneyTree, sc: SmartCompare, val: int,
                        contenders: [int], k: int):
    pos = _get_element_positions(tt, [val])[val]

    for child in tt.iter_same_child(pos):
        sibling = tt.get_sibling(child)
        sibling_val = tt[sibling]

        if sibling_val < 0:
            continue

        if sibling_val in contenders:
            continue

        sc.set_greater_than(sibling_val, val)

        sc.compare(sibling_val, contenders[-1])
        index = smart_binary_search(contenders, sc, sibling_val,
                                    contenders.index(val) + 1, len(contenders))
        contenders.insert(index, sibling_val)

        if len(contenders) > k:
            contenders.pop()