import sys

# --- 1. The Universal Node ---
class Node:
    def __init__(self, data):
        self.data = data
        self.left = None
        self.right = None
        self.height = 1  # For AVL
        
    def __repr__(self):
        return f"Node({self.data})"

# --- 2. The Builder (Handles all Input Types) ---
class TreeBuilder:
    @staticmethod
    def bst(values):
        """Builds a BST from a list of values."""
        root = None
        tree = BSTLogic()
        for v in values:
            root = tree.insert(root, v)
        return root

    @staticmethod
    def level_order(values, null_markers={'null', 'N', -1, None}):
        """Builds a tree from level-order array like [1, 2, 'null', 3]."""
        if not values or values[0] in null_markers: return None
        
        # Normalize input: handle user passing strings or ints
        clean_vals = []
        for v in values:
            if str(v) in ['-1'] or str(v) in null_markers: clean_vals.append(None)
            else: clean_vals.append(int(v))
            
        if not clean_vals: return None

        root = Node(clean_vals[0])
        queue = [root]
        i = 1
        while i < len(clean_vals):
            curr = queue.pop(0)
            
            # Left Child
            if i < len(clean_vals) and clean_vals[i] is not None:
                curr.left = Node(clean_vals[i])
                queue.append(curr.left)
            i += 1
            
            # Right Child
            if i < len(clean_vals) and clean_vals[i] is not None:
                curr.right = Node(clean_vals[i])
                queue.append(curr.right)
            i += 1
        return root

    @staticmethod
    def from_pre_in(preorder, inorder):
        """Builds tree from Preorder and Inorder lists."""
        if not preorder or not inorder: return None
        val = preorder[0]
        root = Node(val)
        mid = inorder.index(val)
        root.left = TreeBuilder.from_pre_in(preorder[1:mid+1], inorder[:mid])
        root.right = TreeBuilder.from_pre_in(preorder[mid+1:], inorder[mid+1:])
        return root

# --- 3. The Logic Engines (BST & AVL) ---
class BSTLogic:
    def insert(self, root, data):
        if not root: return Node(data)
        if data < root.data: root.left = self.insert(root.left, data)
        elif data > root.data: root.right = self.insert(root.right, data)
        return root

class AVLLogic(BSTLogic):
    def height(self, n): return n.height if n else 0
    def balance(self, n): return (self.height(n.left) - self.height(n.right)) if n else 0
    def _rot_r(self, y):
        x = y.left; T2 = x.right; x.right = y; y.left = T2
        y.height = 1 + max(self.height(y.left), self.height(y.right))
        x.height = 1 + max(self.height(x.left), self.height(x.right))
        return x
    def _rot_l(self, x):
        y = x.right; T2 = y.left; y.left = x; x.right = T2
        x.height = 1 + max(self.height(x.left), self.height(x.right))
        y.height = 1 + max(self.height(y.left), self.height(y.right))
        return y
    def insert(self, root, data):
        root = super().insert(root, data) # BST insert
        root.height = 1 + max(self.height(root.left), self.height(root.right))
        b = self.balance(root)
        if b > 1 and data < root.left.data: return self._rot_r(root)
        if b < -1 and data > root.right.data: return self._rot_l(root)
        if b > 1 and data > root.left.data: root.left = self._rot_l(root.left); return self._rot_r(root)
        if b < -1 and data < root.right.data: root.right = self._rot_r(root.right); return self._rot_l(root)
        return root

# --- 4. The Generalized Solver (High Level Recursion) ---
class Solver:
    @staticmethod
    def validate_all(root, condition_func):
        """
        Recursively checks if 'condition_func(node)' is True for ALL nodes.
        Boilerplate 'if not root' is handled.
        Usage: Solver.validate_all(root, lambda n: n.left is None)
        """
        if not root: return True
        # If condition fails for current node, return False
        if not condition_func(root): return False
        # Recurse
        return Solver.validate_all(root.left, condition_func) and Solver.validate_all(root.right, condition_func)

    @staticmethod
    def compare(root1, root2, strategy='mirror'):
        """
        Compares two trees (or subtrees) structurally.
        strategy='mirror': Checks if root1 is mirror of root2.
        strategy='same': Checks if root1 is identical to root2.
        """
        if not root1 and not root2: return True
        if not root1 or not root2: return False
        if root1.data != root2.data: return False
        
        if strategy == 'mirror':
            return Solver.compare(root1.left, root2.right, 'mirror') and \
                   Solver.compare(root1.right, root2.left, 'mirror')
        else: # same
            return Solver.compare(root1.left, root2.left, 'same') and \
                   Solver.compare(root1.right, root2.right, 'same')

    @staticmethod
    def traversals(root):
        """Returns a dict with all standard traversals as lists."""
        res = {'in': [], 'pre': [], 'post': []}
        def _dfs(n):
            if not n: return
            res['pre'].append(n.data)
            _dfs(n.left)
            res['in'].append(n.data)
            _dfs(n.right)
            res['post'].append(n.data)
        _dfs(root)
        return res

# --- 5. Backward Compatibility & Utility Algorithms ---
import heapq

class Algo:
    """Static algorithms for Diameter, Huffman, LCA, etc."""
    
    @staticmethod
    def huffman(chars, freqs):
        # Returns codes in PRE-ORDER of the Huffman Tree
        class HNode:
            def __init__(self, c, f, i): self.c, self.f, self.i, self.left, self.right = c, f, i, None, None
            def __lt__(self, o): return self.f < o.f if self.f != o.f else self.i < o.i
            
        pq = [HNode(c, f, i) for i, (c, f) in enumerate(zip(chars, freqs))]
        heapq.heapify(pq)
        
        while len(pq) > 1:
            l, r = heapq.heappop(pq), heapq.heappop(pq)
            m = HNode(None, l.f + r.f, min(l.i, r.i))
            m.left, m.right = l, r
            heapq.heappush(pq, m)
            
        codes = {}
        def dfs(n, s):
            if not n: return
            if n.c: codes[n.c] = s
            dfs(n.left, s+'0'); dfs(n.right, s+'1')
        
        dfs(pq[0], "")
        # Collect codes in PreOrder logic
        res = []
        def pre(n):
            if not n: return
            if n.c: res.append(codes[n.c])
            pre(n.left); pre(n.right)
        pre(pq[0])
        return res

    @staticmethod
    def build_tree(pre_arr, in_arr):
        if not pre_arr: return None
        root = Node(pre_arr[0])
        mid = in_arr.index(pre_arr[0])
        root.left = Algo.build_tree(pre_arr[1:mid+1], in_arr[:mid])
        root.right = Algo.build_tree(pre_arr[mid+1:], in_arr[mid+1:])
        return root

    @staticmethod
    def height(root):
        return 1 + max(Algo.height(root.left), Algo.height(root.right)) if root else 0

    @staticmethod
    def diameter(root):
        ans = 0
        def d(n):
            nonlocal ans
            if not n: return 0
            l, r = d(n.left), d(n.right)
            ans = max(ans, l+r)
            return 1 + max(l, r)
        d(root)
        return ans
    
    @staticmethod
    def lca(root, n1, n2):
        while root:
            if root.data > n1 and root.data > n2: root = root.left
            elif root.data < n1 and root.data < n2: root = root.right
            else: break
        return root.data if root else None

    @staticmethod
    def is_skewed(root):
        if not root: return True
        if root.left and root.right: return False
        return Algo.is_skewed(root.left) and Algo.is_skewed(root.right)

# --- 6. Legacy Compatibility (Deprecated but kept for backward compatibility) ---
BST = BSTLogic
AVL = AVLLogic
