open Printf;; let rec list_of_lines in_file = try let line = input_line in_file in line :: list_of_lines(in_file) with End_of_file -> close_in in_file; [] let encode_computer_id str = Int.logor (Char.code str.[0]) (Int.shift_left (Char.code str.[1]) 8) let decode_computer_id id = [Int.logand id 0xff; Int.shift_right id 8] |> List.map Char.chr |> List.to_seq |> String.of_seq let computer_id_starts_with char id = Int.logand id 0xff = Char.code char let parse_computer_pair str = (encode_computer_id (String.sub str 0 2), encode_computer_id (String.sub str 3 2)) module IntSet = Set.Make(Int) let make_graph list_of_edges = let vertices = Hashtbl.create 512 in let add_association a b = match Hashtbl.find_opt vertices a with | Some set -> Hashtbl.replace vertices a (IntSet.add b set) | None -> Hashtbl.add vertices a ([b] |> IntSet.of_list) in List.iter (fun (a, b) -> add_association a b; add_association b a) list_of_edges; vertices let get_sets_of_three graph = graph |> Hashtbl.to_seq |> Seq.fold_left (fun ret_list (a, b_set) -> b_set |> IntSet.to_seq |> Seq.filter (fun b -> b > a) |> Seq.map (fun b -> b_set |> IntSet.to_seq |> Seq.filter (fun c -> c > b) |> Seq.filter (fun c -> Hashtbl.find graph b |> IntSet.find_opt c |> Option.is_some) |> Seq.map (fun c -> (a, b, c)) |> List.of_seq) |> Seq.fold_left List.append ret_list ) [] let has_computer_starting_with char (a, b, c) = computer_id_starts_with char a || computer_id_starts_with char b || computer_id_starts_with char c let triple_to_list (a, b, c) = [a; b; c] let rec compare_prefix lst prefix = match prefix with | a :: prefix_tail -> begin match lst with | b :: lst_tail -> let compare = Stdlib.compare b a in if compare <> 0 then compare else compare_prefix lst_tail prefix_tail | _ -> -1 end | _ -> 0 let rec search_ahead_starts_with prefix ordered_lists = match ordered_lists with | lst :: tail -> let compare = compare_prefix lst prefix in if compare > 0 then [] else if compare < 0 then search_ahead_starts_with prefix tail else begin lst :: search_ahead_starts_with prefix tail end | _ -> [] let rec contains_remaining_combinations lower_cardinality_sets begin_list middle_elem end_list = match end_list with | elem :: end_list_tail -> if search_ahead_starts_with (List.append begin_list end_list) lower_cardinality_sets |> List.is_empty then false else contains_remaining_combinations lower_cardinality_sets (List.append begin_list [middle_elem]) elem end_list_tail | _ -> true and merge_fully_connected_sets lower_cardinality_sets acc_sets = match lower_cardinality_sets with | lst1 :: tail -> begin match lst1 with | elem1 :: lst1_tail -> let other_lists = search_ahead_starts_with lst1_tail tail in let new_acc = other_lists |> List.fold_left (fun new_sets other -> match other with | elem2 :: other_tail -> if contains_remaining_combinations lower_cardinality_sets [elem1] elem2 other_tail then begin (elem1 :: other) :: new_sets end else new_sets | _ -> raise (Invalid_argument "Need sets of at least 2 cardinality") ) acc_sets in merge_fully_connected_sets tail new_acc | _ -> raise (Invalid_argument "Need sets of at least 2 cardinality") end | _ -> acc_sets |> List.rev let rec find_biggest_clique sorted_list_of_lists = if List.length sorted_list_of_lists = 1 then List.hd sorted_list_of_lists else let bigger_sets = merge_fully_connected_sets sorted_list_of_lists [] in if List.length bigger_sets = 0 then List.hd sorted_list_of_lists else find_biggest_clique bigger_sets let () = let f = open_in "input.txt" in let computer_pairs = list_of_lines f |> List.map parse_computer_pair in let graph = make_graph computer_pairs in let sets = get_sets_of_three graph |> List.sort Stdlib.compare in let sets_filtered = List.filter (has_computer_starting_with 't') sets in let result = List.length sets_filtered in let result_clique = find_biggest_clique (List.map triple_to_list sets) |> List.map decode_computer_id |> List.sort Stdlib.compare in let result2 = String.concat "," result_clique in printf "%d\n%s\n" result result2