New Terminal Output:

root@f3f8d6319494:/app# python3 << 'EOF'
> import struct
>
> with open('/app/frames.tza', 'rb') as f:
>     f.read(4)
>     w, h = struct.unpack('<HH', f.read(4))
>     n_frames, max_bytes = struct.unpack('<II', f.read(8))
>     frames = [f.read(w*h) for _ in range(n_frames)]
>
> def find_runs(changes):
>     runs = []
>     if not changes:
>         return runs
>     run_start = changes[0]
>     run_end = changes[0]
>     for c in changes[1:]:
>         if c == run_end + 1:
>             run_end = c
>         else:
>             runs.append((run_start, run_end))
>             run_start = run_end = c
>     runs.append((run_start, run_end))
>     return runs
>
> # Calculate optimal threshold
> def pos_cost(r, c):
>     return 3 + len(str(r)) + 1 + len(str(c))
>
> # For a row at position r:
> # EL+rewrite: pos_cost(r+1, 1) + 4 + w
> # Partial: sum(pos_cost(r+1, start+1) + run_len for each run)
>
> def cost_erase(r):
>     return pos_cost(r+1, 1) + 4 + w
>
> def cost_partial(r, changes):
>     runs = find_runs(changes)
>     return sum(pos_cost(r+1, start+1) + (end-start+1) for start, end in runs)
>
> # Test with row 2 from frame 7 (30 changes, 2 runs)
> i = 7
> r = 13
> prev_row = frames[i-1][r*w:(r+1)*w]
> curr_row = frames[i][r*w:(r+1)*w]
> changes = [c for c in range(w) if prev_row[c] != curr_row[c]]
> print(f'Row {r}: {len(changes)} changes')
> print(f'  Erase cost: {cost_erase(r)}')
> print(f'  Partial cost: {cost_partial(r, changes)}')
>
> # Find the crossover threshold
> print(f'\nFor row {r}:')
> print(f'  Erase cost: {cost_erase(r)}')
> print(f'  Cost per change position + 1 char: ~{pos_cost(r+1, 10) + 1}')
> print(f'  Crossover at ~{cost_erase(r) // (pos_cost(r+1, 10) + 1)} changes')
> EOF
Row 13: 33 changes
  Erase cost: 91
  Partial cost: 48

For row 13:
  Erase cost: 91
  Cost per change position + 1 char: ~9
  Crossover at ~10 changes
root@f3f8d6319494:/app#
