From 38902d9a642be8dd3876d42db8ef9a90b33e45b9 Mon Sep 17 00:00:00 2001 From: Bruno BELANYI Date: Sat, 18 Dec 2021 16:05:30 +0100 Subject: [PATCH 1/4] 2021: d18: ex1: add input --- 2021/d18/ex1/input | 100 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 2021/d18/ex1/input diff --git a/2021/d18/ex1/input b/2021/d18/ex1/input new file mode 100644 index 0000000..1dd0bc9 --- /dev/null +++ b/2021/d18/ex1/input @@ -0,0 +1,100 @@ +[[3,[8,[2,1]]],[[[0,6],[0,2]],3]] +[[[1,[8,5]],[[3,9],0]],2] +[5,[[5,[3,8]],[7,4]]] +[1,[[[0,4],[8,5]],6]] +[[[1,[0,3]],2],[2,[0,[7,9]]]] +[[[4,[4,4]],[[7,2],[7,1]]],9] +[5,[4,4]] +[[0,[[2,6],[8,9]]],[[4,5],2]] +[[[8,2],0],3] +[[9,0],[3,3]] +[[[[5,2],2],5],5] +[[[1,6],[[0,4],[7,7]]],[[1,4],[[6,5],5]]] +[[[[4,1],[4,1]],[2,[5,5]]],[1,[0,[0,6]]]] +[[[[1,5],1],[8,4]],[9,[3,4]]] +[[1,[3,3]],[[[7,4],[8,1]],2]] +[3,[[[2,1],4],[5,4]]] +[6,[[0,[1,9]],[[4,0],8]]] +[5,[7,[7,[8,8]]]] +[[[[6,2],[5,8]],[5,[3,1]]],[[7,9],[[2,0],6]]] +[[[7,[7,9]],[5,7]],[[[9,3],[6,9]],[[1,2],[2,3]]]] +[[[[4,1],2],[1,[6,6]]],[[[2,2],[8,8]],4]] +[[[[3,7],4],8],[6,[[0,2],3]]] +[[[[1,8],2],3],[[9,[1,7]],[[0,0],[6,8]]]] +[[[9,[5,2]],7],[[8,6],[8,[1,2]]]] +[[[7,[1,0]],[[6,0],[8,4]]],[[[7,8],5],[3,[1,2]]]] +[[[[2,5],9],[[8,2],0]],0] +[0,[[[7,5],[4,1]],[5,[6,6]]]] +[[[[3,6],2],[[1,1],[6,6]]],0] +[[[[0,9],[2,5]],[2,[3,2]]],[6,3]] +[3,[[9,[1,4]],[[0,8],[4,6]]]] +[1,[[5,[5,9]],[9,0]]] +[[[6,8],4],[[[6,6],2],[[3,9],2]]] +[5,[[[7,5],[4,8]],0]] +[[9,[6,6]],[9,[[6,8],[6,4]]]] +[[[4,8],[0,[2,8]]],[7,[[4,5],[1,6]]]] +[[[6,[8,6]],2],[[[2,9],[2,4]],[0,2]]] +[[[0,[5,6]],[[3,8],3]],[[3,1],7]] +[[1,[8,1]],[1,[6,[7,1]]]] +[[[5,[9,6]],[3,5]],2] +[[3,7],[[[2,5],[4,1]],[3,[5,6]]]] +[[8,7],[[9,6],3]] +[[[[4,2],[4,8]],[7,[4,5]]],2] +[[[[6,7],6],3],[[[6,7],4],0]] +[[[0,1],[[9,1],[2,9]]],9] +[[[[8,5],[5,8]],[0,7]],[0,[8,[3,2]]]] +[[4,[[6,5],[1,9]]],[[[0,0],1],6]] +[[[[9,5],9],[2,[6,3]]],[[2,9],[6,9]]] +[[[7,[5,0]],1],[7,[[8,7],3]]] +[[[2,4],2],[[[3,0],6],[[0,2],[9,2]]]] +[[1,[[7,3],[4,3]]],[[[3,9],[1,1]],[3,6]]] +[[[[4,7],7],[[7,1],[2,3]]],[1,[[7,6],[5,6]]]] +[[0,[5,2]],0] +[[[[6,6],[4,8]],8],[[0,[7,4]],8]] +[[4,[7,2]],[[[0,8],1],[9,5]]] +[0,0] +[[[[3,7],6],3],[3,[[3,3],1]]] +[[[6,5],7],[[3,5],[[6,4],[4,9]]]] +[[4,[[7,9],9]],9] +[5,[8,[[7,4],1]]] +[[[[2,4],[5,7]],8],[[[7,6],[6,9]],[[3,9],[6,4]]]] +[[[4,8],3],[[[3,9],7],0]] +[0,[8,[[4,2],3]]] +[[[[0,1],[5,8]],[7,2]],[2,4]] +[[6,[8,[1,9]]],[[[6,5],[8,1]],[7,[6,4]]]] +[[9,3],[5,[0,6]]] +[[2,[7,[2,0]]],[[2,1],[5,5]]] +[[[0,[7,0]],[[0,4],[4,9]]],[8,[[6,1],[6,3]]]] +[[[[5,7],[3,2]],[0,[5,0]]],[[0,[1,6]],3]] +[[[[6,3],[9,5]],[9,9]],[[5,[8,3]],[[0,0],[0,3]]]] +[[6,[4,9]],[[[9,9],[8,4]],4]] +[0,[2,5]] +[[[[7,9],[1,2]],[3,3]],[[[7,2],7],[[1,6],0]]] +[[[[8,0],2],8],[[[1,5],9],9]] +[[[0,[6,9]],4],[[[4,8],5],4]] +[[6,[[0,3],4]],[0,[[8,3],1]]] +[[[1,2],[2,[3,3]]],[6,7]] +[[0,[[7,4],5]],[3,[[8,2],0]]] +[[[[0,1],[1,7]],[[2,7],[5,9]]],[[[7,0],0],[8,1]]] +[[6,4],[3,0]] +[[[[6,6],4],[5,1]],[7,3]] +[[[[9,2],3],[8,[4,8]]],7] +[[5,[[2,2],[9,2]]],[[[1,8],0],[8,[6,3]]]] +[2,[[0,0],[0,[9,9]]]] +[[4,4],[[6,5],[6,5]]] +[[[[9,1],2],4],5] +[[[[2,1],[3,1]],[[2,6],9]],5] +[[[9,[0,6]],7],[[8,3],[[8,1],2]]] +[[[6,[0,0]],[2,[0,0]]],[[[0,4],8],3]] +[[[[4,1],[2,9]],[6,5]],3] +[[9,[[9,4],8]],[[[5,5],3],[[3,4],4]]] +[8,[9,[[0,3],1]]] +[9,[[[6,0],4],9]] +[[6,[2,9]],[[[2,7],[5,3]],0]] +[[[4,1],5],[8,[[0,7],4]]] +[[[[2,5],5],[[8,2],[8,9]]],[[9,6],[[0,3],[2,3]]]] +[6,1] +[[1,7],4] +[[8,7],0] +[[[[5,4],7],5],[[[6,1],5],[5,[5,5]]]] +[[[6,[1,5]],[0,[7,0]]],[[[1,5],3],[5,[1,0]]]] From 2be3cf43275408bbcdf7850f48f7ce1cdc5b7921 Mon Sep 17 00:00:00 2001 From: Bruno BELANYI Date: Sat, 18 Dec 2021 16:05:38 +0100 Subject: [PATCH 2/4] 2021: d18: ex1: add solution --- 2021/d18/ex1/ex1.py | 208 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 208 insertions(+) create mode 100755 2021/d18/ex1/ex1.py diff --git a/2021/d18/ex1/ex1.py b/2021/d18/ex1/ex1.py new file mode 100755 index 0000000..7a02746 --- /dev/null +++ b/2021/d18/ex1/ex1.py @@ -0,0 +1,208 @@ +#!/usr/bin/env python + +import functools +import itertools +import sys +from dataclasses import dataclass +from typing import Iterator, List, Optional, Tuple + + +@dataclass +class Tree: + parent: Optional["Pair"] + + +@dataclass +class Pair(Tree): + left: Tree + right: Tree + + +@dataclass +class Num(Tree): + val: int + + +# True means left, False means right +Path = List[bool] + + +def solve(input: List[str]) -> int: + def make_pair(left: Tree, right: Tree, parent: Optional[Pair] = None) -> Pair: + pair = Pair(parent=parent, left=left, right=right) + pair.left.parent = pair + pair.right.parent = pair + return pair + + def make_num(val: int, parent: Optional[Pair] = None) -> Num: + return Num(parent=parent, val=val) + + # FIXME: remove this + def debug(tree: Tree) -> str: + if isinstance(tree, Pair): + return f"[{debug(tree.left)},{debug(tree.right)}]" + assert isinstance(tree, Num) + return str(tree.val) + + def parse() -> List[Tree]: + def parse_snailfish_number(line: str) -> Tree: + def parse_index(input: str, index: int = 0) -> Tuple[int, Tree]: + if input[index] == "[": + left_index, left = parse_index(input, index + 1) + assert input[left_index] == "," # Sanity check + right_index, right = parse_index(input, left_index + 1) + assert input[right_index] == "]" # Sanity check + return right_index + 1, make_pair(left, right) + res = 0 + for i in itertools.count(index): + if i < len(input) and input[i] in set(str(i) for i in range(10)): + res = res * 10 + int(input[i]) + else: + return i, make_num(res) + assert False # Sanity check + + __, res = parse_index(line) + return res + + return [parse_snailfish_number(line) for line in input] + + def explosion_path(number: Tree) -> Optional[Path]: + def dfs(number: Pair, path: Path = []) -> Optional[Path]: + if ( + len(path) >= 4 + and isinstance(number.left, Num) + and isinstance(number.right, Num) + ): + return path + if isinstance(number.left, Pair): + left_path = dfs(number.left, path + [True]) + if left_path is not None: + return left_path + if isinstance(number.right, Pair): + right_path = dfs(number.right, path + [False]) + if right_path is not None: + return right_path + return None + + assert isinstance(number, Pair) # Sanity check + return dfs(number) + + def split_path(number: Tree) -> Optional[Path]: + def dfs(number: Pair, path: Path = []) -> Optional[Path]: + if isinstance(number.left, Num): + if number.left.val >= 10: + return path + [True] + else: + assert isinstance(number.left, Pair) # Sanity check + if (left_path := dfs(number.left, path + [True])) is not None: + return left_path + if isinstance(number.right, Num): + if number.right.val >= 10: + return path + [False] + else: + assert isinstance(number.right, Pair) # Sanity check + if (right_path := dfs(number.right, path + [False])) is not None: + return right_path + return None + + assert isinstance(number, Pair) # Sanity check + return dfs(number) + + def explode(number: Tree, path: Path) -> Tree: + def walk(number: Tree, reverse: bool) -> Iterator[Tree]: + if isinstance(number, Num): + yield number + else: + assert isinstance(number, Pair) # Sanity check + first, second = ( + (number.right, number.left) + if reverse + else (number.left, number.right) + ) + yield from walk(first, reverse) + yield number + yield from walk(second, reverse) + + def next_num(number: Tree, reverse: bool) -> Optional[Num]: + if number.parent is None: + return None + sibling = number.parent.left if reverse else number.parent.right + if number is sibling: + return next_num(number.parent, reverse) + for node in walk(sibling, reverse=reverse): + if isinstance(node, Num): + return node + return None + + assert isinstance(number, Pair) # Sanity check + + if len(path) == 0: + p, n = next_num(number, reverse=True), next_num(number, reverse=False) + if p is not None: + assert isinstance(number.left, Num) # Safety check + p.val += number.left.val + if n is not None: + assert isinstance(number.right, Num) # Safety check + n.val += number.right.val + return make_num(0) + + parent, left, right = number.parent, number.left, number.right + if path[0]: + left = explode(number.left, path[1:]) + else: + right = explode(number.right, path[1:]) + + return make_pair(parent=parent, left=left, right=right) + + def split(number: Tree, path: Path) -> Tree: + def split_int(num: int, parent: Optional[Pair]) -> Tree: + assert num >= 0 # Sanity check + left = num // 2 + right = num - left + res = make_pair(left=make_num(left), right=make_num(right), parent=parent) + return res + + if len(path) == 0: + assert isinstance(number, Num) # Sanity check + return split_int(number.val, number.parent) + + assert isinstance(number, Pair) # Sanity check + + parent, left, right = number.parent, number.left, number.right + if path[0]: + left = split(number.left, path[1:]) + else: + right = split(number.right, path[1:]) + + return make_pair(parent=parent, left=left, right=right) + + def reduce(number: Tree) -> Tree: + path = explosion_path(number) + if path is not None: + return reduce(explode(number, path)) + path = split_path(number) + if path is not None: + return reduce(split(number, path)) + return number + + def add(left: Tree, right: Tree) -> Tree: + return reduce(make_pair(left=left, right=right)) + + def magnitude(number: Tree) -> int: + if isinstance(number, Num): + return number.val + assert isinstance(number, Pair) # Safety check + return 3 * magnitude(number.left) + 2 * magnitude(number.right) + + numbers = parse() + result = functools.reduce(add, numbers) + return magnitude(result) + + +def main() -> None: + input = [line.strip() for line in sys.stdin.readlines()] + print(solve(input)) + + +if __name__ == "__main__": + main() From c0bf6fe213cc23294f877b4327c6344884bc40fb Mon Sep 17 00:00:00 2001 From: Bruno BELANYI Date: Sat, 18 Dec 2021 16:05:48 +0100 Subject: [PATCH 3/4] 2021: d18: ex2: add input --- 2021/d18/ex2/input | 100 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 2021/d18/ex2/input diff --git a/2021/d18/ex2/input b/2021/d18/ex2/input new file mode 100644 index 0000000..1dd0bc9 --- /dev/null +++ b/2021/d18/ex2/input @@ -0,0 +1,100 @@ +[[3,[8,[2,1]]],[[[0,6],[0,2]],3]] +[[[1,[8,5]],[[3,9],0]],2] +[5,[[5,[3,8]],[7,4]]] +[1,[[[0,4],[8,5]],6]] +[[[1,[0,3]],2],[2,[0,[7,9]]]] +[[[4,[4,4]],[[7,2],[7,1]]],9] +[5,[4,4]] +[[0,[[2,6],[8,9]]],[[4,5],2]] +[[[8,2],0],3] +[[9,0],[3,3]] +[[[[5,2],2],5],5] +[[[1,6],[[0,4],[7,7]]],[[1,4],[[6,5],5]]] +[[[[4,1],[4,1]],[2,[5,5]]],[1,[0,[0,6]]]] +[[[[1,5],1],[8,4]],[9,[3,4]]] +[[1,[3,3]],[[[7,4],[8,1]],2]] +[3,[[[2,1],4],[5,4]]] +[6,[[0,[1,9]],[[4,0],8]]] +[5,[7,[7,[8,8]]]] +[[[[6,2],[5,8]],[5,[3,1]]],[[7,9],[[2,0],6]]] +[[[7,[7,9]],[5,7]],[[[9,3],[6,9]],[[1,2],[2,3]]]] +[[[[4,1],2],[1,[6,6]]],[[[2,2],[8,8]],4]] +[[[[3,7],4],8],[6,[[0,2],3]]] +[[[[1,8],2],3],[[9,[1,7]],[[0,0],[6,8]]]] +[[[9,[5,2]],7],[[8,6],[8,[1,2]]]] +[[[7,[1,0]],[[6,0],[8,4]]],[[[7,8],5],[3,[1,2]]]] +[[[[2,5],9],[[8,2],0]],0] +[0,[[[7,5],[4,1]],[5,[6,6]]]] +[[[[3,6],2],[[1,1],[6,6]]],0] +[[[[0,9],[2,5]],[2,[3,2]]],[6,3]] +[3,[[9,[1,4]],[[0,8],[4,6]]]] +[1,[[5,[5,9]],[9,0]]] +[[[6,8],4],[[[6,6],2],[[3,9],2]]] +[5,[[[7,5],[4,8]],0]] +[[9,[6,6]],[9,[[6,8],[6,4]]]] +[[[4,8],[0,[2,8]]],[7,[[4,5],[1,6]]]] +[[[6,[8,6]],2],[[[2,9],[2,4]],[0,2]]] +[[[0,[5,6]],[[3,8],3]],[[3,1],7]] +[[1,[8,1]],[1,[6,[7,1]]]] +[[[5,[9,6]],[3,5]],2] +[[3,7],[[[2,5],[4,1]],[3,[5,6]]]] +[[8,7],[[9,6],3]] +[[[[4,2],[4,8]],[7,[4,5]]],2] +[[[[6,7],6],3],[[[6,7],4],0]] +[[[0,1],[[9,1],[2,9]]],9] +[[[[8,5],[5,8]],[0,7]],[0,[8,[3,2]]]] +[[4,[[6,5],[1,9]]],[[[0,0],1],6]] +[[[[9,5],9],[2,[6,3]]],[[2,9],[6,9]]] +[[[7,[5,0]],1],[7,[[8,7],3]]] +[[[2,4],2],[[[3,0],6],[[0,2],[9,2]]]] +[[1,[[7,3],[4,3]]],[[[3,9],[1,1]],[3,6]]] +[[[[4,7],7],[[7,1],[2,3]]],[1,[[7,6],[5,6]]]] +[[0,[5,2]],0] +[[[[6,6],[4,8]],8],[[0,[7,4]],8]] +[[4,[7,2]],[[[0,8],1],[9,5]]] +[0,0] +[[[[3,7],6],3],[3,[[3,3],1]]] +[[[6,5],7],[[3,5],[[6,4],[4,9]]]] +[[4,[[7,9],9]],9] +[5,[8,[[7,4],1]]] +[[[[2,4],[5,7]],8],[[[7,6],[6,9]],[[3,9],[6,4]]]] +[[[4,8],3],[[[3,9],7],0]] +[0,[8,[[4,2],3]]] +[[[[0,1],[5,8]],[7,2]],[2,4]] +[[6,[8,[1,9]]],[[[6,5],[8,1]],[7,[6,4]]]] +[[9,3],[5,[0,6]]] +[[2,[7,[2,0]]],[[2,1],[5,5]]] +[[[0,[7,0]],[[0,4],[4,9]]],[8,[[6,1],[6,3]]]] +[[[[5,7],[3,2]],[0,[5,0]]],[[0,[1,6]],3]] +[[[[6,3],[9,5]],[9,9]],[[5,[8,3]],[[0,0],[0,3]]]] +[[6,[4,9]],[[[9,9],[8,4]],4]] +[0,[2,5]] +[[[[7,9],[1,2]],[3,3]],[[[7,2],7],[[1,6],0]]] +[[[[8,0],2],8],[[[1,5],9],9]] +[[[0,[6,9]],4],[[[4,8],5],4]] +[[6,[[0,3],4]],[0,[[8,3],1]]] +[[[1,2],[2,[3,3]]],[6,7]] +[[0,[[7,4],5]],[3,[[8,2],0]]] +[[[[0,1],[1,7]],[[2,7],[5,9]]],[[[7,0],0],[8,1]]] +[[6,4],[3,0]] +[[[[6,6],4],[5,1]],[7,3]] +[[[[9,2],3],[8,[4,8]]],7] +[[5,[[2,2],[9,2]]],[[[1,8],0],[8,[6,3]]]] +[2,[[0,0],[0,[9,9]]]] +[[4,4],[[6,5],[6,5]]] +[[[[9,1],2],4],5] +[[[[2,1],[3,1]],[[2,6],9]],5] +[[[9,[0,6]],7],[[8,3],[[8,1],2]]] +[[[6,[0,0]],[2,[0,0]]],[[[0,4],8],3]] +[[[[4,1],[2,9]],[6,5]],3] +[[9,[[9,4],8]],[[[5,5],3],[[3,4],4]]] +[8,[9,[[0,3],1]]] +[9,[[[6,0],4],9]] +[[6,[2,9]],[[[2,7],[5,3]],0]] +[[[4,1],5],[8,[[0,7],4]]] +[[[[2,5],5],[[8,2],[8,9]]],[[9,6],[[0,3],[2,3]]]] +[6,1] +[[1,7],4] +[[8,7],0] +[[[[5,4],7],5],[[[6,1],5],[5,[5,5]]]] +[[[6,[1,5]],[0,[7,0]]],[[[1,5],3],[5,[1,0]]]] From 8bf78654a5ab20e0ba70ffe0de29abd866249c2e Mon Sep 17 00:00:00 2001 From: Bruno BELANYI Date: Sat, 18 Dec 2021 16:05:53 +0100 Subject: [PATCH 4/4] 2021: d18: ex2: add solution --- 2021/d18/ex2/ex2.py | 211 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 211 insertions(+) create mode 100755 2021/d18/ex2/ex2.py diff --git a/2021/d18/ex2/ex2.py b/2021/d18/ex2/ex2.py new file mode 100755 index 0000000..56c8d48 --- /dev/null +++ b/2021/d18/ex2/ex2.py @@ -0,0 +1,211 @@ +#!/usr/bin/env python + +import functools +import itertools +import sys +from copy import deepcopy +from dataclasses import dataclass +from typing import Iterator, List, Optional, Tuple + + +@dataclass +class Tree: + parent: Optional["Pair"] + + +@dataclass +class Pair(Tree): + left: Tree + right: Tree + + +@dataclass +class Num(Tree): + val: int + + +# True means left, False means right +Path = List[bool] + + +def solve(input: List[str]) -> int: + def make_pair(left: Tree, right: Tree, parent: Optional[Pair] = None) -> Pair: + pair = Pair(parent=parent, left=left, right=right) + pair.left.parent = pair + pair.right.parent = pair + return pair + + def make_num(val: int, parent: Optional[Pair] = None) -> Num: + return Num(parent=parent, val=val) + + # FIXME: remove this + def debug(tree: Tree) -> str: + if isinstance(tree, Pair): + return f"[{debug(tree.left)},{debug(tree.right)}]" + assert isinstance(tree, Num) + return str(tree.val) + + def parse() -> List[Tree]: + def parse_snailfish_number(line: str) -> Tree: + def parse_index(input: str, index: int = 0) -> Tuple[int, Tree]: + if input[index] == "[": + left_index, left = parse_index(input, index + 1) + assert input[left_index] == "," # Sanity check + right_index, right = parse_index(input, left_index + 1) + assert input[right_index] == "]" # Sanity check + return right_index + 1, make_pair(left, right) + res = 0 + for i in itertools.count(index): + if i < len(input) and input[i] in set(str(i) for i in range(10)): + res = res * 10 + int(input[i]) + else: + return i, make_num(res) + assert False # Sanity check + + __, res = parse_index(line) + return res + + return [parse_snailfish_number(line) for line in input] + + def explosion_path(number: Tree) -> Optional[Path]: + def dfs(number: Pair, path: Path = []) -> Optional[Path]: + if ( + len(path) >= 4 + and isinstance(number.left, Num) + and isinstance(number.right, Num) + ): + return path + if isinstance(number.left, Pair): + left_path = dfs(number.left, path + [True]) + if left_path is not None: + return left_path + if isinstance(number.right, Pair): + right_path = dfs(number.right, path + [False]) + if right_path is not None: + return right_path + return None + + assert isinstance(number, Pair) # Sanity check + return dfs(number) + + def split_path(number: Tree) -> Optional[Path]: + def dfs(number: Pair, path: Path = []) -> Optional[Path]: + if isinstance(number.left, Num): + if number.left.val >= 10: + return path + [True] + else: + assert isinstance(number.left, Pair) # Sanity check + if (left_path := dfs(number.left, path + [True])) is not None: + return left_path + if isinstance(number.right, Num): + if number.right.val >= 10: + return path + [False] + else: + assert isinstance(number.right, Pair) # Sanity check + if (right_path := dfs(number.right, path + [False])) is not None: + return right_path + return None + + assert isinstance(number, Pair) # Sanity check + return dfs(number) + + def explode(number: Tree, path: Path) -> Tree: + def walk(number: Tree, reverse: bool) -> Iterator[Tree]: + if isinstance(number, Num): + yield number + else: + assert isinstance(number, Pair) # Sanity check + first, second = ( + (number.right, number.left) + if reverse + else (number.left, number.right) + ) + yield from walk(first, reverse) + yield number + yield from walk(second, reverse) + + def next_num(number: Tree, reverse: bool) -> Optional[Num]: + if number.parent is None: + return None + sibling = number.parent.left if reverse else number.parent.right + if number is sibling: + return next_num(number.parent, reverse) + for node in walk(sibling, reverse=reverse): + if isinstance(node, Num): + return node + return None + + assert isinstance(number, Pair) # Sanity check + + if len(path) == 0: + p, n = next_num(number, reverse=True), next_num(number, reverse=False) + if p is not None: + assert isinstance(number.left, Num) # Safety check + p.val += number.left.val + if n is not None: + assert isinstance(number.right, Num) # Safety check + n.val += number.right.val + return make_num(0) + + parent, left, right = number.parent, number.left, number.right + if path[0]: + left = explode(number.left, path[1:]) + else: + right = explode(number.right, path[1:]) + + return make_pair(parent=parent, left=left, right=right) + + def split(number: Tree, path: Path) -> Tree: + def split_int(num: int, parent: Optional[Pair]) -> Tree: + assert num >= 0 # Sanity check + left = num // 2 + right = num - left + res = make_pair(left=make_num(left), right=make_num(right), parent=parent) + return res + + if len(path) == 0: + assert isinstance(number, Num) # Sanity check + return split_int(number.val, number.parent) + + assert isinstance(number, Pair) # Sanity check + + parent, left, right = number.parent, number.left, number.right + if path[0]: + left = split(number.left, path[1:]) + else: + right = split(number.right, path[1:]) + + return make_pair(parent=parent, left=left, right=right) + + def reduce(number: Tree) -> Tree: + path = explosion_path(number) + if path is not None: + return reduce(explode(number, path)) + path = split_path(number) + if path is not None: + return reduce(split(number, path)) + return number + + def add(left: Tree, right: Tree) -> Tree: + return reduce(make_pair(left=left, right=right)) + + def magnitude(number: Tree) -> int: + if isinstance(number, Num): + return number.val + assert isinstance(number, Pair) # Safety check + return 3 * magnitude(number.left) + 2 * magnitude(number.right) + + numbers = parse() + return max( + magnitude(add(deepcopy(l), deepcopy(r))) + for l, r in itertools.permutations(numbers, 2) + ) + + +def main() -> None: + input = [line.strip() for line in sys.stdin.readlines()] + print(solve(input)) + + +if __name__ == "__main__": + main()