2022-05-07 15:39:30 +02:00
|
|
|
from avl import AVLTree, _balance_factor
|
|
|
|
|
|
|
|
import logging
|
|
|
|
from typing import List
|
2022-01-30 15:04:08 +01:00
|
|
|
|
|
|
|
import pytest
|
|
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
2022-05-07 15:39:30 +02:00
|
|
|
@pytest.mark.parametrize(
|
|
|
|
("values", "expected_balance_factor"),
|
|
|
|
[
|
|
|
|
([], 0),
|
|
|
|
([1], 0),
|
|
|
|
([1, -1], -1),
|
|
|
|
([1, 2], 1),
|
|
|
|
],
|
|
|
|
)
|
|
|
|
def test_balance_factor(
|
|
|
|
values: List[int], expected_balance_factor: int
|
|
|
|
) -> None:
|
|
|
|
tree = AVLTree()
|
|
|
|
for v in values:
|
|
|
|
tree.insert(v)
|
|
|
|
|
|
|
|
assert _balance_factor(tree.root) == expected_balance_factor
|
|
|
|
|
|
|
|
|
|
|
|
def test_incorrect_avl() -> None:
|
|
|
|
tree = AVLTree()
|
|
|
|
tree.insert(0)
|
|
|
|
tree.root.rank = 1
|
|
|
|
assert not tree.is_correct
|
|
|
|
|
|
|
|
|
2022-01-30 15:04:08 +01:00
|
|
|
def test_empty() -> None:
|
|
|
|
tree = AVLTree()
|
|
|
|
|
|
|
|
assert tree.root is None
|
|
|
|
assert tree.is_correct
|
|
|
|
|
|
|
|
|
|
|
|
def test_one_node() -> None:
|
|
|
|
tree = AVLTree()
|
|
|
|
tree.insert(1)
|
|
|
|
|
|
|
|
assert tree.root is not None
|
|
|
|
assert 1 == tree.root.value
|
|
|
|
assert 0 == tree.root.rank
|
|
|
|
assert tree.root.left is None
|
|
|
|
assert tree.root.right is None
|
|
|
|
|
|
|
|
assert tree.is_correct
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize("values", [[1, 2], [1, 2, 0]])
|
|
|
|
def test_no_rebalance_needed(values):
|
|
|
|
tree = AVLTree()
|
|
|
|
|
|
|
|
for value in values:
|
|
|
|
tree.insert(value)
|
|
|
|
assert tree.is_correct
|
|
|
|
|
|
|
|
|
|
|
|
def test_three_nodes_rebalanced():
|
|
|
|
tree = AVLTree()
|
|
|
|
|
|
|
|
for value in (1, 2, 3):
|
|
|
|
print(tree)
|
|
|
|
tree.insert(value)
|
|
|
|
|
|
|
|
assert tree.is_correct
|
|
|
|
|
|
|
|
|
|
|
|
def test_bigger_tree():
|
|
|
|
tree = AVLTree()
|
|
|
|
|
|
|
|
for i in range(50):
|
|
|
|
tree.insert(i)
|
|
|
|
assert tree.is_correct
|
|
|
|
|
|
|
|
|
|
|
|
def test_bigger_tree_reversed():
|
|
|
|
tree = AVLTree()
|
|
|
|
|
|
|
|
for i in range(50):
|
|
|
|
tree.insert(-i)
|
|
|
|
assert tree.is_correct
|
|
|
|
|
|
|
|
|
|
|
|
def test_promote():
|
|
|
|
tree = AVLTree()
|
|
|
|
|
|
|
|
for value in (0, 1, -1):
|
|
|
|
tree.insert(value)
|
|
|
|
assert tree.is_correct
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"values",
|
|
|
|
[
|
|
|
|
[0, 1, -2, -1, -3],
|
|
|
|
[0, 1, -2, -1, -3, -4, 4, 9, 7],
|
|
|
|
[0, 1, -2, -1, -3, -4, 4, 9, 7, 5, -5, 8],
|
|
|
|
],
|
|
|
|
)
|
|
|
|
def test_rotate(values):
|
|
|
|
tree = AVLTree()
|
|
|
|
|
|
|
|
for value in values:
|
|
|
|
tree.insert(value)
|
|
|
|
assert tree.is_correct
|
|
|
|
|
|
|
|
|
|
|
|
def _report(t_before: str, t_after: str) -> None:
|
|
|
|
with open("before.dot", "w") as before, open("after.dot", "w") as after:
|
|
|
|
print(t_before, file=before)
|
|
|
|
print(t_after, file=after)
|
|
|
|
|
|
|
|
|
|
|
|
# @unittest.skip("Only for replicating hypothesis")
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"values, delete_order",
|
|
|
|
[
|
|
|
|
([4, 2, 5, 0, 3], [5, 0, 4, 2, 3]),
|
|
|
|
([1, 2, 3, 4, 5], [5]),
|
|
|
|
([0, 1, 2, -15, 17, -2, -1], [17, 2, 1, 0, -2, -15, -1]),
|
|
|
|
(
|
|
|
|
[
|
|
|
|
0,
|
|
|
|
1,
|
|
|
|
2,
|
|
|
|
3,
|
|
|
|
4,
|
|
|
|
128,
|
|
|
|
1536,
|
|
|
|
7,
|
|
|
|
25223,
|
|
|
|
1159,
|
|
|
|
16,
|
|
|
|
32915,
|
|
|
|
148,
|
|
|
|
147,
|
|
|
|
24,
|
|
|
|
26,
|
|
|
|
220,
|
|
|
|
-1,
|
|
|
|
-9,
|
|
|
|
-8,
|
|
|
|
-6,
|
|
|
|
-5,
|
|
|
|
-4,
|
|
|
|
-3,
|
|
|
|
-2,
|
|
|
|
],
|
|
|
|
[
|
|
|
|
24,
|
|
|
|
0,
|
|
|
|
-3,
|
|
|
|
-9,
|
|
|
|
1536,
|
|
|
|
128,
|
|
|
|
7,
|
|
|
|
16,
|
|
|
|
-4,
|
|
|
|
-6,
|
|
|
|
4,
|
|
|
|
2,
|
|
|
|
-5,
|
|
|
|
3,
|
|
|
|
-1,
|
|
|
|
32915,
|
|
|
|
-8,
|
|
|
|
1159,
|
|
|
|
-2,
|
|
|
|
26,
|
|
|
|
220,
|
|
|
|
25223,
|
|
|
|
1,
|
|
|
|
147,
|
|
|
|
148,
|
|
|
|
],
|
|
|
|
),
|
|
|
|
],
|
|
|
|
)
|
|
|
|
def test_delete_minimal(values, delete_order):
|
|
|
|
tree = AVLTree()
|
|
|
|
|
|
|
|
for value in values:
|
|
|
|
tree.insert(value)
|
|
|
|
|
|
|
|
for value in delete_order:
|
|
|
|
before = str(tree)
|
|
|
|
tree.delete(value)
|
|
|
|
after = str(tree)
|
|
|
|
|
|
|
|
try:
|
|
|
|
assert tree.is_correct
|
|
|
|
except AssertionError:
|
|
|
|
logger.info(
|
|
|
|
f"[FAIL] Delete {value} from {values} in order {delete_order}"
|
|
|
|
)
|
|
|
|
_report(before, after)
|
|
|
|
logger.info(f"Before:\n{before}")
|
|
|
|
logger.info(f"After:\n{after}")
|
|
|
|
raise
|