71 lines
2.3 KiB
OCaml
71 lines
2.3 KiB
OCaml
let ints_of_file fname =
|
|
Aoc.strings_of_file fname |> List.map (Aoc.ints_of_string ~sep:"[: ]+")
|
|
|
|
(** [log10i i] returns the integer part of [log10 i]. [i] must be greater than
|
|
zero. *)
|
|
let log10i i =
|
|
let rec impl acc = function 0 -> acc | x -> impl (acc + 1) (x / 10) in
|
|
assert (i > 0);
|
|
impl ~-1 i
|
|
|
|
(** [pow10 n] returns [10] raised to the [n]th power. [n] must be non-negative.
|
|
*)
|
|
let pow10 n =
|
|
let rec impl acc = function 0 -> acc | x -> impl (acc * 10) (x - 1) in
|
|
assert (n >= 0);
|
|
impl 1 n
|
|
|
|
(** [is_valid_target1 tgt nums] checks to see if we can reach [tgt] from [nums]
|
|
using multiply and addition only. [nums] should be in reverse order to that
|
|
given in the problem. *)
|
|
let rec is_valid_target1 tgt nums =
|
|
(* We work backwards from the target and note that:
|
|
- if we ever go negative then the route we have taken is invalid
|
|
- we can only multiply if dividing the tgt by a number results in no
|
|
remainder.
|
|
*)
|
|
if tgt < 0 then false
|
|
else
|
|
match nums with
|
|
| [] -> tgt = 0
|
|
| h :: t ->
|
|
let sum_valid = is_valid_target1 (tgt - h) t in
|
|
if tgt mod h = 0 then sum_valid || is_valid_target1 (tgt / h) t
|
|
else sum_valid
|
|
|
|
(** [is_valid_target2 tgt nums] checks to see if we can reach [tgt] from [nums]
|
|
using multiply, addition and "merger" only. [nums] should be in reverse
|
|
order to that given in the problem. *)
|
|
let rec is_valid_target2 tgt nums =
|
|
(* Same principle as [is_valid_target1], but also noting that merger op is
|
|
only possible if the last digits of the current [tgt] are the same as the
|
|
number being examined. *)
|
|
if tgt < 0 then false
|
|
else
|
|
match nums with
|
|
| [] -> tgt = 0
|
|
| h :: t ->
|
|
let sum_valid = is_valid_target2 (tgt - h) t in
|
|
let p = pow10 (1 + log10i h) in
|
|
let merge_valid =
|
|
if tgt mod p = h then is_valid_target2 (tgt / p) t else false
|
|
in
|
|
if tgt mod h = 0 then
|
|
sum_valid || merge_valid || is_valid_target2 (tgt / h) t
|
|
else sum_valid || merge_valid
|
|
|
|
let check_target checker = function
|
|
| [] -> false
|
|
| h :: t -> checker h (List.rev t)
|
|
|
|
let part checker lst =
|
|
List.filter (check_target checker) lst
|
|
|> List.map List.hd |> List.fold_left ( + ) 0
|
|
|
|
let _ =
|
|
Aoc.main ints_of_file
|
|
[
|
|
(string_of_int, part is_valid_target1);
|
|
(string_of_int, part is_valid_target2);
|
|
]
|