#!/usr/bin/env nix-shell (* #!nix-shell --pure -i ocaml -p ocaml *) let input = let rec calc blocks n xs = match xs with | [] -> blocks | block_size :: free_space :: xs -> calc (blocks @ [(Some n, block_size); (None, free_space)]) (n + 1) xs | block_size :: xs -> calc (blocks @ [(Some n, block_size)]) (n + 1) xs in read_line () |> String.to_seq |> Seq.map (String.make 1) |> Seq.map int_of_string |> List.of_seq |> calc [] 0 let rec remove_last lst = let rec aux = function | [] -> (None, []) | (Some block_id, size) :: xs -> (Some (block_id, size), List.rev xs) | (None, _) :: xs -> aux xs in List.rev lst |> aux let rec replace_at n replacement = function | [] -> [] | x :: xs -> if n == 0 then replacement @ xs else x :: replace_at (n - 1) replacement xs let rec fold_res n acc lst = match lst with | [] -> acc | (_, 0) :: xs -> fold_res n acc xs | (None, size) :: xs -> fold_res (n + size) acc xs | (Some block_id, size) :: xs -> fold_res (n + 1) ((block_id * n) + acc) ((Some block_id, size - 1) :: xs) let part1 = let rec reorg curr input = let handle free_space block_id size xs = if free_space > size then (Some block_id, size) :: reorg None ((None, free_space - size) :: xs) else if free_space == size then (Some block_id, size) :: reorg None xs else (Some block_id, free_space) :: reorg (Some (block_id, size - free_space)) xs in match (input, curr) with | [], None -> [] | [], Some (block_id, size) -> [(Some block_id, size)] | (Some block_id, size) :: xs, _ -> (Some block_id, size) :: reorg curr xs | (None, free_space) :: xs, Some (block_id, size) -> handle free_space block_id size xs | (None, free_space) :: xs, None -> ( match remove_last xs with | Some (block_id, size), xs -> handle free_space block_id size xs | None, xs -> reorg curr xs ) in input |> reorg None |> fold_res 0 0 let part2 = let rec find_fitting_free_space required_space to_scan n = match to_scan with | [] -> None | (None, block_size) :: xs -> if block_size >= required_space then Some (n, block_size) else find_fitting_free_space required_space xs (n + 1) | (Some _, _) :: xs -> find_fitting_free_space required_space xs (n + 1) in let rec reorg input = match input with | [] -> [] | (None, size) :: xs -> (None, size) :: reorg xs | (Some block_id, size) :: xs -> ( match find_fitting_free_space size (List.rev xs) 0 with | None -> (Some block_id, size) :: reorg xs | Some (idx, space) -> (None, size) :: replace_at (List.length xs - idx - 1) [(None, space - size); (Some block_id, size)] xs |> reorg ) in input |> List.rev |> reorg |> List.rev |> fold_res 0 0 let _ = print_int part1 let _ = print_endline "" let _ = print_int part2 let _ = print_endline ""