| 1 | 1 | class Zipper: |
| 2 | | # Tree is a dict with keys "value" (int), "left" (dict or None), "right" (dict or None) |
| 2 | def __init__(self, tree, breadcrumbs=None): |
| 3 | # Edge Case: Initialize with None tree by creating an empty tree |
| 4 | self.tree = tree if tree is not None else {'value': 0, 'left': None, 'right': None} |
| 5 | # Edge Case: Initialize with None breadcrumbs as an empty list |
| 6 | self.breadcrumbs = breadcrumbs if breadcrumbs is not None else [] |
| 7 | |
| 3 | 8 | @staticmethod |
| 4 | 9 | def from_tree(tree: dict) -> "Zipper": |
| 5 | | pass |
| 6 | | |
| 10 | # Edge Case: Handle None input by creating a default tree |
| 11 | if tree is None: |
| 12 | tree = {'value': 0, 'left': None, 'right': None} |
| 13 | return Zipper(tree) |
| 14 | |
| 7 | 15 | def value(self) -> int: |
| 8 | | pass |
| 9 | | |
| 16 | # Edge Case: Return value even if tree is None (should not happen with proper construction) |
| 17 | if self.tree is None: |
| 18 | return 0 |
| 19 | return self.tree['value'] |
| 20 | |
| 10 | 21 | def set_value(self, value: int) -> "Zipper": |
| 11 | | pass |
| 12 | | |
| 22 | # Edge Case: Handle None tree by creating a new tree |
| 23 | if self.tree is None: |
| 24 | new_tree = {'value': value, 'left': None, 'right': None} |
| 25 | else: |
| 26 | new_tree = { |
| 27 | 'value': value, |
| 28 | 'left': self.tree['left'], |
| 29 | 'right': self.tree['right'] |
| 30 | } |
| 31 | return Zipper(new_tree, self.breadcrumbs) |
| 32 | |
| 13 | 33 | def left(self) -> "Zipper | None": |
| 14 | | pass |
| 15 | | |
| 34 | # Edge Case: Return None if tree is None |
| 35 | if self.tree is None: |
| 36 | return None |
| 37 | # Edge Case: Return None if there's no left child |
| 38 | if self.tree['left'] is None: |
| 39 | return None |
| 40 | |
| 41 | # Create breadcrumb to remember current tree when going down |
| 42 | new_breadcrumb = { |
| 43 | 'parent': self.tree, |
| 44 | 'side': 'left', |
| 45 | 'sibling': self.tree['right'] |
| 46 | } |
| 47 | |
| 48 | new_breadcrumbs = [new_breadcrumb] + self.breadcrumbs |
| 49 | return Zipper(self.tree['left'], new_breadcrumbs) |
| 50 | |
| 16 | 51 | def set_left(self, tree: dict | None) -> "Zipper": |
| 17 | | pass |
| 18 | | |
| 52 | # Edge Case: Handle None tree by creating a new tree |
| 53 | if self.tree is None: |
| 54 | new_tree = {'value': 0, 'left': tree, 'right': None} |
| 55 | else: |
| 56 | new_tree = { |
| 57 | 'value': self.tree['value'], |
| 58 | 'left': tree, |
| 59 | 'right': self.tree['right'] |
| 60 | } |
| 61 | return Zipper(new_tree, self.breadcrumbs) |
| 62 | |
| 19 | 63 | def right(self) -> "Zipper | None": |
| 20 | | pass |
| 21 | | |
| 64 | # Edge Case: Return None if tree is None |
| 65 | if self.tree is None: |
| 66 | return None |
| 67 | # Edge Case: Return None if there's no right child |
| 68 | if self.tree['right'] is None: |
| 69 | return None |
| 70 | |
| 71 | # Create breadcrumb to remember current tree when going down |
| 72 | new_breadcrumb = { |
| 73 | 'parent': self.tree, |
| 74 | 'side': 'right', |
| 75 | 'sibling': self.tree['left'] |
| 76 | } |
| 77 | |
| 78 | new_breadcrumbs = [new_breadcrumb] + self.breadcrumbs |
| 79 | return Zipper(self.tree['right'], new_breadcrumbs) |
| 80 | |
| 22 | 81 | def set_right(self, tree: dict | None) -> "Zipper": |
| 23 | | pass |
| 24 | | |
| 82 | # Edge Case: Handle None tree by creating a new tree |
| 83 | if self.tree is None: |
| 84 | new_tree = {'value': 0, 'left': None, 'right': tree} |
| 85 | else: |
| 86 | new_tree = { |
| 87 | 'value': self.tree['value'], |
| 88 | 'left': self.tree['left'], |
| 89 | 'right': tree |
| 90 | } |
| 91 | return Zipper(new_tree, self.breadcrumbs) |
| 92 | |
| 25 | 93 | def up(self) -> "Zipper | None": |
| 26 | | pass |
| 27 | | |
| 94 | # Edge Case: Return None if there are no breadcrumbs (at root) |
| 95 | if not self.breadcrumbs: |
| 96 | return None |
| 97 | |
| 98 | breadcrumb = self.breadcrumbs[0] |
| 99 | parent = breadcrumb['parent'] |
| 100 | side = breadcrumb['side'] |
| 101 | sibling = breadcrumb['sibling'] |
| 102 | |
| 103 | # Reconstruct parent tree with current tree in the correct position |
| 104 | new_parent = parent.copy() |
| 105 | new_parent[side] = self.tree |
| 106 | new_parent['left' if side == 'right' else 'right'] = sibling |
| 107 | |
| 108 | return Zipper(new_parent, self.breadcrumbs[1:]) |
| 109 | |
| 28 | 110 | def to_tree(self) -> dict: |
| 29 | | pass |
| 111 | # Navigate up to the root to get the complete tree |
| 112 | current = self |
| 113 | while True: |
| 114 | # Edge Case: If we're at the root (no breadcrumbs), return the current tree |
| 115 | if not current.breadcrumbs: |
| 116 | return current.tree |
| 117 | up_result = current.up() |
| 118 | # Edge Case: Handle potential None return from up() (should not happen with proper breadcrumbs) |
| 119 | if up_result is None: |
| 120 | return current.tree |
| 121 | current = up_result |
| 122 | |
| 123 | # Handled Edge Cases: None tree initialization, None breadcrumbs initialization, None input to from_tree, None tree in value(), None tree in set_value(), None tree in left(), No left child in left(), None tree in set_left(), None tree in right(), No right child in right(), None tree in set_right(), No breadcrumbs in up(), None breadcrumbs in constructor, No breadcrumbs in to_tree, None return from up() in to_tree |