type half = L | H type bounds = {lo: int; hi: int} type result = Exact of int | Between of bounds let avg lo hi = (lo + hi) / 2 let rec find' ?(lo=0) ~hi = function | [] -> if lo = hi then Exact hi else Between {lo; hi} | L::hs -> find' ~lo ~hi:(avg lo hi) hs | H::hs -> find' ~lo:(avg (lo+1) hi) ~hi hs exception Inexact of bounds * int option [@warn_on_literal_pattern] let find ?index ?lo ~hi halves = match find' ?lo ~hi halves with | Exact x -> x | Between p -> raise (Inexact (p, index)) type path = {rows: half list; cols: half list} exception Unknown_char of char * int option let path_of_string ?index str = let revs {rows; cols} = List.{rows = rev rows; cols = rev cols} in let put acc = function | 'F' -> {acc with rows = L :: acc.rows} | 'B' -> {acc with rows = H :: acc.rows} | 'L' -> {acc with cols = L :: acc.cols} | 'R' -> {acc with cols = H :: acc.cols} | c -> raise (Unknown_char (c, index)) in String.to_seq str |> Seq.fold_left put {rows = []; cols = []} |> revs type seat = {row: int; col: int} let seat_of_path ?index {rows; cols} = {row = find ?index ~hi:127 rows; col = find ?index ~hi:7 cols} let seat_id {row; col} = (row * 8) + col let seat_id_of_string str = path_of_string str |> seat_of_path |> seat_id let%test_module _ = (module struct let str = "FBFBBFFRLR" let rows = [L;H;L;H;H;L;L] let row = 44 let cols = [H;L;H] let col = 5 let path = {rows; cols} let seat = {row; col} let id = 357 let%test _ = find' ~hi:1 [L] = Exact 0 let%test _ = find' ~hi:1 [H] = Exact 1 let%test _ = find' ~hi:127 [] = Between {lo = 0; hi = 127} let%test _ = find' ~hi:127 rows = Exact row let%test _ = find' ~hi:7 cols = Exact col let%test _ = find ~hi:127 rows = row let%test _ = find ~hi:7 cols = col let%test_unit _ = try ignore (find ~hi:127 []); failwith "didn't raise" with Inexact _ -> () let%test _ = path_of_string str = path let%test _ = seat_of_path path = seat let%test _ = seat_id seat = id end) let infile_seats ~of_seq input = Bracket.infile_lines input ~line:seat_id_of_string ~of_seq let main1 input = infile_seats input ~of_seq:(Seq.fold_left max 0) |> Format.printf "max id: %d\n" let%expect_test _ = main1 "../../data/day5"; [%expect{| max id: 855 |}] let make_list cmp seq = List.(fast_sort cmp (of_seq seq)) let rec find_pair f = function | [] | [_] -> None | x::y::zs -> match f x y with | Some z -> Some z | None -> find_pair f (y::zs) let find_gap lst = let find_gap1 x y = if x + 1 <> y then Some (x + 1) else None in find_pair find_gap1 lst let main2 input = match find_gap (infile_seats input ~of_seq:(make_list Int.compare)) with | Some x -> Format.printf "seat id: %d\n" x | None -> raise Not_found let%expect_test _ = main2 "../../data/day5"; [%expect{| seat id: 552 |}] let main = Misc.main 5 [|main1; main2|]