import copy import typing import stdlib def yep(s: str) -> bool: return s.strip().lower().startswith("y") def guess_bisect_repl(lower: int, upper: int) -> int: mid = lower + ((upper - lower) // 2) if yep(input(f"is it {mid}? ")): return mid if yep(input(f"higher than {mid}? ")): return guess_bisect_repl(mid, upper) return guess_bisect_repl(lower, mid) def find_sqrt_ish(n: int) -> int: return int(find_bisect(0, n, gen_sqrt_check(n))) def gen_sqrt_check(n: int) -> typing.Callable[[float], int]: def check(mid: float) -> int: mid_sq: float = mid * mid if mid_sq == n: return 0 if mid_sq < n: return 1 return -1 return check def find_bisect( lower: float, upper: float, check: typing.Callable[[float], int] ) -> float: mid: float = lower + ((upper - lower) / 2) print(f"lower={lower} mid={mid} upper={upper}") if mid == lower or mid == upper or check(mid) == 0: return mid if check(mid) == 1: return find_bisect(mid, upper, check) return find_bisect(lower, mid, check) def cartesian_path(p0: tuple[int, int], p1: tuple[int, int]) -> list[tuple[int, int]]: path: list[tuple[int, int]] = [] if p0 < p1: for i in range(p0[1], p1[1]): path.append((i, p0[0])) for i in range(p0[0], p1[0]): path.append((p1[1], i)) else: for i in range(p0[1], p1[1] - 1, -1): path.append((i, p0[0])) for i in range(p0[0] - 1, p1[0], -1): path.append((p1[1], i)) return path def gen_matrix(width: int, height: int) -> list[list[int]]: return [list(range(width)) for _ in range(height)] class Roman: SIMPLE = { "I": 1, "V": 5, "X": 10, "L": 50, "C": 100, "D": 500, "M": 1000, } SIMPLE_REVERSE = {v: k for k, v in SIMPLE.items()} COMPOUND = { "IV": 4, "IX": 9, "XL": 40, "XC": 90, "CD": 400, "CM": 900, } PREFIXES = {k[0] for k in COMPOUND.keys()} COMPOUND_REVERSE = {v: k for k, v in COMPOUND.items()} ALL = SIMPLE | COMPOUND ALL_REVERSE = {v: k for k, v in ALL.items()} @classmethod def i2r(cls, i: int) -> str: if i > 100_000: raise ValueError(f"{i} is too silly") r: list[str] = [] for int_val, roman_val in sorted(cls.ALL_REVERSE.items(), reverse=True): remainder = i % int_val r += [roman_val] * int((i - remainder) / int_val) i = remainder return "".join(r) @classmethod def r2i(cls, r: str) -> int: total = 0 offset = 0 for i in range(len(r)): if i + offset > len(r) - 1: break c = r[i + offset] if ( c in cls.PREFIXES and (i + offset + 1) < len(r) and c + r[i + offset + 1] in cls.ALL ): total += cls.ALL[c + r[i + offset + 1]] offset += 1 continue total += cls.ALL[c] return total class MinStack: def __init__(self): self._v: list[int] = [] self._min: list[int] = [] def push(self, val: int) -> None: self._v.append(val) self._min.append(min(val, self._min[-1] if self._min else val)) def pop(self) -> None: self._v.pop(-1) self._min.pop(-1) def top(self) -> int: return self._v[-1] def getMin(self) -> int: # no qa return self._min[-1] def linked_list_to_list(head: stdlib.ListNode | None) -> list[int]: seen: set[int] = set() ret: list[int] = [] while head is not None: if hash(head) in seen: return ret seen.add(hash(head)) ret.append(head.val) head = head.next return ret def sort_linked_list(head: stdlib.ListNode | None) -> stdlib.ListNode | None: by_val: list[tuple[int, stdlib.ListNode]] = [] ret: stdlib.ListNode | None = None while head is not None: by_val.append((head.val, head)) head = head.next cur = ret for _, node in sorted(by_val, key=lambda v: v[0]): if cur is None: cur = ret = node continue cur.next = node cur = cur.next if cur is not None: cur.next = None return ret def connect_binary_tree_right( root: stdlib.Node | None, ) -> tuple[stdlib.Node | None, list[int | None]]: if root is None: return None, [] by_level = binary_tree_by_level(copy.deepcopy(root)) serialized: list[int | None] = [] print("") if 0 not in by_level or len(by_level[0]) == 0: return None, [] connected_root = by_level[0][0] for level, nodes in sorted(by_level.items(), key=lambda p: p[0]): for i in range(len(nodes)): serialized.append(nodes[i].val) if len(nodes) > i + 1: print(f"{'-' * level}> connecting {nodes[i].val} -> {nodes[i + 1].val}") nodes[i].next = nodes[i + 1] serialized.append(None) return connected_root, serialized def binary_tree_by_level(root: stdlib.Node) -> dict[int, list[stdlib.Node]]: combined: dict[int, list[stdlib.Node]] = {} for path in collect_binary_tree_levels(0, root): level, node = path combined.setdefault(level, []) combined[level].insert(0, node) return combined def collect_binary_tree_levels( level: int, node: stdlib.Node | None ) -> typing.Iterator[tuple[int, stdlib.Node]]: if node is None: return yield (level, node) yield from collect_binary_tree_levels(level + 1, node.right) yield from collect_binary_tree_levels(level + 1, node.left)