--- /dev/null
+from dataclasses import dataclass\r
+from collections import defaultdict\r
+from typing import List, DefaultDict, TextIO\r
+\r
+\r
+@dataclass\r
+class Problem:\r
+    data: DefaultDict[int, int]\r
+\r
+    @staticmethod\r
+    def calc(value: int) -> List[int]:\r
+        if value == 0:\r
+            result = [1]\r
+        elif len(str(value)) % 2 == 0:\r
+            digits = str(value)\r
+            mid = len(digits) // 2\r
+            result = [int(digits[:mid]), int(digits[mid:])]\r
+        else:\r
+            result = [value * 2024]\r
+        return result\r
+\r
+    def run(self, count: int, debug: bool = False) -> int:\r
+        data = self.data\r
+        if debug:\r
+            print(data)\r
+        for _ in range(count):\r
+            newdata: DefaultDict[int, int] = defaultdict(int)\r
+            for value, count in data.items():\r
+                for newval in Problem.calc(value):\r
+                    newdata[newval] += count\r
+            if debug:\r
+                print(newdata)\r
+            data = newdata\r
+        return sum(data.values())\r
+\r
+    @staticmethod\r
+    def from_stream(stream: TextIO) -> 'Problem':\r
+        line = next(stream).strip()  # just one line\r
+        data = defaultdict(int, {int(x): 1 for x in line.split(' ')})\r
+        return Problem(data)\r
 
--- /dev/null
+import sys
+import argparse
+from . import Problem
+
+
+def main(args=None):
+    parser = argparse.ArgumentParser(description="AOC 2024 day 11")
+    parser.add_argument('filename', type=str)
+    parser.add_argument('-d', '--debug', action='store_true')
+    parser.add_argument('-c', '--count', type=int, default=25)
+    options = parser.parse_args(args)
+
+    with open(options.filename) as f:
+        problem = Problem.from_stream(f)
+
+    print(f"result {problem.run(options.count, debug=options.debug)}")
+    return 0
+
+
+if __name__ == '__main__':
+    sys.exit(main(sys.argv[1:]))