def setUp(self): self.random_sorted_binary_tree = SortedBinaryTree() self.inserted_values = set() random.seed(1) for _ in range(NUM_KEYS): key = random.randint(0, RANGE) while key in self.inserted_values: key = random.randint(0, RANGE) self.random_sorted_binary_tree.add(key) self.inserted_values.add(key)
def test_binary_tree_enumeration(self): sorted_binary_tree = SortedBinaryTree() for key in [100, 50, 200, 25, 75, 150, 250]: sorted_binary_tree.add(key, str(key)) pre_order_keys = [100, 50, 25, 75, 200, 150, 250] in_order_keys = [25, 50, 75, 100, 150, 200, 250] post_order_keys = [25, 75, 50, 150, 250, 200, 100] for expected_key, node in zip( pre_order_keys, sorted_binary_tree.enumerate( order=sorted_binary_tree.PRE_ORDER)): self.assertTrue(expected_key == node.key) for expected_key, node in zip( in_order_keys, sorted_binary_tree.enumerate( order=sorted_binary_tree.IN_ORDER)): self.assertTrue(expected_key == node.key) for expected_key, node in zip( post_order_keys, sorted_binary_tree.enumerate( order=sorted_binary_tree.POST_ORDER)): self.assertTrue(expected_key == node.key)
def test_insert_and_contains(self): sorted_binary_tree = SortedBinaryTree() already_inserted = set() random.seed(1) for _ in range(1000): key = random.randint(0, 10000) while key in already_inserted: key = random.randint(0, 10000) sorted_binary_tree.add(key) self.assertTrue(sorted_binary_tree.contains(key)) already_inserted.add(key) for key in already_inserted: self.assertTrue(sorted_binary_tree.contains(key))
def test_sanity(self): """ This test will exercise all functions of the tree by: inserting, looking up, removing keys. We will retain auxillary (and less efficient) data structures to ensure correctness. """ INSERT_NEW = 0 INSERT_EXISTING = 1 LOOKUP_EXISTING = 2 LOOKUP_NONEXISTENT = 4 POP_EXISTING = 8 POP_NONEXISTENT = 16 OPERATIONS = [ INSERT_NEW, INSERT_NEW, INSERT_NEW, INSERT_NEW, INSERT_NEW, INSERT_EXISTING, LOOKUP_EXISTING, LOOKUP_NONEXISTENT, POP_EXISTING, POP_NONEXISTENT, ] random.seed(2) num_iterations = 1000 key_range = 100 * num_iterations sorted_binary_tree = SortedBinaryTree() inserted = set() removed = set() for i in range(num_iterations): # Each case will need one of the below existing_key = random.choice(list(inserted)) if inserted else None new_key = random.randint(0, key_range) while new_key in inserted: new_key = random.randint(0, key_range) OPERATION_TYPE = random.choice(OPERATIONS) while existing_key is None and OPERATION_TYPE in (INSERT_EXISTING, LOOKUP_EXISTING, POP_EXISTING): OPERATION_TYPE = random.choice(OPERATIONS) if OPERATION_TYPE == INSERT_NEW: sorted_binary_tree.add(new_key, str(new_key)) inserted.add(new_key) if new_key in removed: # If we re-inserted a new key, let's make sure to account for it. removed.remove(new_key) elif OPERATION_TYPE == INSERT_EXISTING: try: sorted_binary_tree.add(existing_key) self.assertTrue(False) except: self.assertTrue(True) elif OPERATION_TYPE == LOOKUP_EXISTING: self.assertTrue(sorted_binary_tree.contains(existing_key)) self.assertTrue( sorted_binary_tree.get(existing_key) == str(existing_key)) continue # No need to check invariants for read-only operations elif OPERATION_TYPE == LOOKUP_NONEXISTENT: self.assertFalse(sorted_binary_tree.contains(new_key)) try: sorted_binary_tree.get(new_key) self.assertTrue(False) except: self.assertTrue(True) elif OPERATION_TYPE == POP_EXISTING: self.assertTrue(sorted_binary_tree.contains(existing_key)) self.assertTrue( sorted_binary_tree.pop(existing_key) == str(existing_key)) self.assertFalse(sorted_binary_tree.contains(existing_key)) inserted.remove(existing_key) removed.add(existing_key) elif OPERATION_TYPE == POP_NONEXISTENT: self.assertFalse(sorted_binary_tree.contains(new_key)) try: sorted_binary_tree.get(new_key) self.assertTrue(False) except: self.assertTrue(True) else: assert False # let's validate invariants ## let's make sure that all keys are present for key in inserted: self.assertTrue(sorted_binary_tree.contains(key)) ## let's make sure that all removed keys are not present for key in removed: self.assertFalse(sorted_binary_tree.contains(key)) ## let's make sure that the ordering is maintained if not sorted_binary_tree.is_empty(): for set_key, tree_node in zip(sorted(inserted), sorted_binary_tree.enumerate()): self.assertTrue(set_key == tree_node.key)
def test_pop(self): sorted_binary_tree = SortedBinaryTree() sorted_binary_tree.add(key=1, payload='one') self.assertTrue(sorted_binary_tree.contains(1)) self.assertTrue(sorted_binary_tree.pop(1) == 'one') self.assertFalse(sorted_binary_tree.contains(1)) keys = [i for i in range(10)] keys = sorted(keys, key=lambda x: random.random()) for key in keys: sorted_binary_tree.add(key=key, payload=str(key)) for key in keys: self.assertTrue(sorted_binary_tree.contains(key)) for key in keys: payload = sorted_binary_tree.pop(key) sorted_binary_tree.assert_invariants() self.assertTrue(payload == str(key)) for key in keys: self.assertFalse(sorted_binary_tree.contains(key))
class SortedBinaryTreeTest(unittest.TestCase): def setUp(self): self.random_sorted_binary_tree = SortedBinaryTree() self.inserted_values = set() random.seed(1) for _ in range(NUM_KEYS): key = random.randint(0, RANGE) while key in self.inserted_values: key = random.randint(0, RANGE) self.random_sorted_binary_tree.add(key) self.inserted_values.add(key) def test_insert_and_contains(self): sorted_binary_tree = SortedBinaryTree() already_inserted = set() random.seed(1) for _ in range(1000): key = random.randint(0, 10000) while key in already_inserted: key = random.randint(0, 10000) sorted_binary_tree.add(key) self.assertTrue(sorted_binary_tree.contains(key)) already_inserted.add(key) for key in already_inserted: self.assertTrue(sorted_binary_tree.contains(key)) def test_traversal(self): previous_node = None for node in self.random_sorted_binary_tree.enumerate(): if previous_node is not None: self.assertTrue(node.key >= previous_node.key) previous_node = node def test_pop(self): sorted_binary_tree = SortedBinaryTree() sorted_binary_tree.add(key=1, payload='one') self.assertTrue(sorted_binary_tree.contains(1)) self.assertTrue(sorted_binary_tree.pop(1) == 'one') self.assertFalse(sorted_binary_tree.contains(1)) keys = [i for i in range(10)] keys = sorted(keys, key=lambda x: random.random()) for key in keys: sorted_binary_tree.add(key=key, payload=str(key)) for key in keys: self.assertTrue(sorted_binary_tree.contains(key)) for key in keys: payload = sorted_binary_tree.pop(key) sorted_binary_tree.assert_invariants() self.assertTrue(payload == str(key)) for key in keys: self.assertFalse(sorted_binary_tree.contains(key)) def test_sanity(self): """ This test will exercise all functions of the tree by: inserting, looking up, removing keys. We will retain auxillary (and less efficient) data structures to ensure correctness. """ INSERT_NEW = 0 INSERT_EXISTING = 1 LOOKUP_EXISTING = 2 LOOKUP_NONEXISTENT = 4 POP_EXISTING = 8 POP_NONEXISTENT = 16 OPERATIONS = [ INSERT_NEW, INSERT_NEW, INSERT_NEW, INSERT_NEW, INSERT_NEW, INSERT_EXISTING, LOOKUP_EXISTING, LOOKUP_NONEXISTENT, POP_EXISTING, POP_NONEXISTENT, ] random.seed(2) num_iterations = 1000 key_range = 100 * num_iterations sorted_binary_tree = SortedBinaryTree() inserted = set() removed = set() for i in range(num_iterations): # Each case will need one of the below existing_key = random.choice(list(inserted)) if inserted else None new_key = random.randint(0, key_range) while new_key in inserted: new_key = random.randint(0, key_range) OPERATION_TYPE = random.choice(OPERATIONS) while existing_key is None and OPERATION_TYPE in (INSERT_EXISTING, LOOKUP_EXISTING, POP_EXISTING): OPERATION_TYPE = random.choice(OPERATIONS) if OPERATION_TYPE == INSERT_NEW: sorted_binary_tree.add(new_key, str(new_key)) inserted.add(new_key) if new_key in removed: # If we re-inserted a new key, let's make sure to account for it. removed.remove(new_key) elif OPERATION_TYPE == INSERT_EXISTING: try: sorted_binary_tree.add(existing_key) self.assertTrue(False) except: self.assertTrue(True) elif OPERATION_TYPE == LOOKUP_EXISTING: self.assertTrue(sorted_binary_tree.contains(existing_key)) self.assertTrue( sorted_binary_tree.get(existing_key) == str(existing_key)) continue # No need to check invariants for read-only operations elif OPERATION_TYPE == LOOKUP_NONEXISTENT: self.assertFalse(sorted_binary_tree.contains(new_key)) try: sorted_binary_tree.get(new_key) self.assertTrue(False) except: self.assertTrue(True) elif OPERATION_TYPE == POP_EXISTING: self.assertTrue(sorted_binary_tree.contains(existing_key)) self.assertTrue( sorted_binary_tree.pop(existing_key) == str(existing_key)) self.assertFalse(sorted_binary_tree.contains(existing_key)) inserted.remove(existing_key) removed.add(existing_key) elif OPERATION_TYPE == POP_NONEXISTENT: self.assertFalse(sorted_binary_tree.contains(new_key)) try: sorted_binary_tree.get(new_key) self.assertTrue(False) except: self.assertTrue(True) else: assert False # let's validate invariants ## let's make sure that all keys are present for key in inserted: self.assertTrue(sorted_binary_tree.contains(key)) ## let's make sure that all removed keys are not present for key in removed: self.assertFalse(sorted_binary_tree.contains(key)) ## let's make sure that the ordering is maintained if not sorted_binary_tree.is_empty(): for set_key, tree_node in zip(sorted(inserted), sorted_binary_tree.enumerate()): self.assertTrue(set_key == tree_node.key) @unittest.skip def test_stack_overflow(self): """ Inserts 10000 consecutive nodes which should cause stack overflow in the recursive implementation. """ sorted_binary_tree = SortedBinaryTree() for key in range(5000, 10000): self.assertFalse(sorted_binary_tree.contains(key)) sorted_binary_tree.add(key=key, payload=str(key)) for key in range(5000): self.assertFalse(sorted_binary_tree.contains(key)) sorted_binary_tree.add(key=key, payload=str(key)) for expected_key, node in zip(range(10000), sorted_binary_tree.enumerate()): self.assertTrue(expected_key == node.key) for key in range(5000, 10000): sorted_binary_tree.get(key) sorted_binary_tree.pop(key) self.assertTrue(True) def test_binary_tree_enumeration(self): sorted_binary_tree = SortedBinaryTree() for key in [100, 50, 200, 25, 75, 150, 250]: sorted_binary_tree.add(key, str(key)) pre_order_keys = [100, 50, 25, 75, 200, 150, 250] in_order_keys = [25, 50, 75, 100, 150, 200, 250] post_order_keys = [25, 75, 50, 150, 250, 200, 100] for expected_key, node in zip( pre_order_keys, sorted_binary_tree.enumerate( order=sorted_binary_tree.PRE_ORDER)): self.assertTrue(expected_key == node.key) for expected_key, node in zip( in_order_keys, sorted_binary_tree.enumerate( order=sorted_binary_tree.IN_ORDER)): self.assertTrue(expected_key == node.key) for expected_key, node in zip( post_order_keys, sorted_binary_tree.enumerate( order=sorted_binary_tree.POST_ORDER)): self.assertTrue(expected_key == node.key)
def test_stack_overflow(self): """ Inserts 10000 consecutive nodes which should cause stack overflow in the recursive implementation. """ sorted_binary_tree = SortedBinaryTree() for key in range(5000, 10000): self.assertFalse(sorted_binary_tree.contains(key)) sorted_binary_tree.add(key=key, payload=str(key)) for key in range(5000): self.assertFalse(sorted_binary_tree.contains(key)) sorted_binary_tree.add(key=key, payload=str(key)) for expected_key, node in zip(range(10000), sorted_binary_tree.enumerate()): self.assertTrue(expected_key == node.key) for key in range(5000, 10000): sorted_binary_tree.get(key) sorted_binary_tree.pop(key) self.assertTrue(True)