Skip to content

Commit

Permalink
add knife module
Browse files Browse the repository at this point in the history
  • Loading branch information
demiazz committed Dec 8, 2024
1 parent 8c377e1 commit 0bb7e80
Show file tree
Hide file tree
Showing 22 changed files with 253 additions and 37 deletions.
2 changes: 1 addition & 1 deletion .ocamlformat
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
profile = janestreet
module-item-spacing=sparse
version = 0.26.2
version = 0.27.0
2 changes: 1 addition & 1 deletion advent-of-ocaml.opam
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ depends: [
"reason-react" {>= "0.15.0"}
"reason-react-ppx" {>= "0.15.0"}
"opam-check-npm-deps" {build & >= "3.0.1"}
"ocaml-lsp-server" {dev}
"ocaml-lsp-server" {dev & >= "1.20.1"}
"ocamlformat" {dev}
"dot-merlin-reader" {dev}
"utop" {dev}
Expand Down
5 changes: 4 additions & 1 deletion dune-project
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,10 @@
(and
:build
(>= 3.0.1)))
(ocaml-lsp-server :dev)
(ocaml-lsp-server
(and
:dev
(>= 1.20.1)))
(ocamlformat :dev)
(dot-merlin-reader :dev)
(utop :dev)
Expand Down
15 changes: 6 additions & 9 deletions lib/year_2015/day_01.ml
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,13 @@ let parse input =
input |> Parse.chars_of process
;;

let part_one input = input |> parse |> Seq.fold_left ( + ) 0 |> string_of_int
let part_one input = input |> parse |> Knife.Seq.sum |> string_of_int

let part_two input =
let rec fold_until acc idx seq =
match seq () with
| Seq.Nil -> acc
| Seq.Cons (x, next) ->
let acc = acc + x in
let next_idx = idx + 1 in
if acc = -1 then next_idx else fold_until acc next_idx next
let position =
input |> parse |> Seq.scan ( + ) 0 |> Seq.find_index (Stdlib.( = ) (-1))
in
input |> parse |> fold_until 0 0 |> string_of_int
match position with
| Some index -> index |> string_of_int
| None -> failwith "Santa never enter the basement"
;;
4 changes: 1 addition & 3 deletions lib/year_2015/day_02.ml
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,7 @@ let pipe f (l, w, h) = l |> f w |> f h

let sum = pipe ( + )

let sum_of f seq = seq |> Seq.map f |> Seq.fold_left ( + ) 0

let total_of f input = input |> parse |> sum_of f |> string_of_int
let total_of f input = input |> parse |> Seq.map f |> Knife.Seq.sum |> string_of_int

let part_one input =
let min = pipe min in
Expand Down
3 changes: 1 addition & 2 deletions lib/year_2015/day_05.ml
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
module Common = struct
let dispenser_of item_of string =
let length = String.length string in
Seq.ints 0 |> Seq.take length |> Seq.map item_of |> Seq.to_dispenser
string |> Knife.String.indices |> Seq.map item_of |> Seq.to_dispenser
;;

let count_of is_nice input =
Expand Down
2 changes: 1 addition & 1 deletion lib/year_2015/dune
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
(library
(name year_2015)
(libraries point_2d parse reader)
(libraries knife point_2d parse reader)
(modes melange native))
2 changes: 1 addition & 1 deletion lib/year_2024/day_01.ml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ module Part_one = struct
let sum (left, right) =
let diff (l, r) = Stdlib.abs (l - r) in
let pairs = Seq.zip (List.to_seq left) (List.to_seq right) in
pairs |> Seq.map diff |> Seq.fold_left Stdlib.( + ) 0
pairs |> Seq.map diff |> Knife.Seq.sum
;;

let distance input = input |> parse |> split |> sort |> sum |> string_of_int
Expand Down
26 changes: 9 additions & 17 deletions lib/year_2024/day_02.ml
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,12 @@ let parse input =
input |> Parse.lines_of process
;;

let validate_with_bounds min max =
let rec is_valid index = function
| [] -> true
| _ :: [] -> true
| x :: y :: xs ->
let diff = x - y in
if diff >= min && diff <= max then is_valid (index + 1) (y :: xs) else false
let validate_with_bounds min max xs =
let is_invalid (x, y) =
let diff = x - y in
diff < min || diff > max
in
is_valid 0
xs |> List.to_seq |> Knife.Seq.pairs_of |> Knife.Seq.exists ~f:is_invalid |> not
;;

let validate min max xs =
Expand All @@ -27,20 +24,15 @@ let validate min max xs =
;;

let to_tolerated xs =
let remove target = List.filteri (fun index _ -> index <> target) xs in
Seq.ints 0 |> Seq.take (List.length xs) |> Seq.map remove
let remove target = Knife.List.remove_at ~index:target xs in
xs |> Knife.List.indices |> Seq.map remove
;;

let check_without_tolerate min max xs = validate min max xs

let check_with_tolerate min max xs =
match validate min max xs with
| true -> true
| false ->
let some xxs = Seq.find (fun xs -> validate min max xs) xxs in
(match to_tolerated xs |> some with
| Some _ -> true
| None -> false)
let check = check_without_tolerate min max in
check xs || to_tolerated xs |> Knife.Seq.exists ~f:check
;;

let count min max tolerate input =
Expand Down
2 changes: 1 addition & 1 deletion lib/year_2024/dune
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
(library
(name year_2024)
(libraries parse reader)
(libraries knife parse reader)
(modes melange native))
3 changes: 3 additions & 0 deletions test/tools/knife/dune
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
(test
(name test_knife)
(libraries alcotest knife))
154 changes: 154 additions & 0 deletions test/tools/knife/test_knife.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
module List_suite = struct
let test_indices name xs expected =
let run () =
Alcotest.(check (list int))
"are equal"
expected
(xs |> Knife.List.indices |> List.of_seq)
in
Alcotest.test_case name `Quick run
;;

let indices_suite =
( "List.indices"
, [ test_indices "empty list" [] []
; test_indices "non empty list" [ 1; 2; 3 ] [ 0; 1; 2 ]
] )
;;

let test_remove_at name index xs expected =
let run () =
Alcotest.(check (list int)) "are equal" expected (Knife.List.remove_at ~index xs)
in
Alcotest.test_case name `Quick run
;;

let remove_at_suite =
( "List.remove_at"
, [ test_remove_at "empty list" 10 [] []
; test_remove_at "out of range" 10 [ 1; 2; 3 ] [ 1; 2; 3 ]
; test_remove_at "negative index" (-1) [ 1; 2; 3 ] [ 1; 2; 3 ]
; test_remove_at "in range" 3 [ 0; 10; 20; 30; 40; 50 ] [ 0; 10; 20; 40; 50 ]
] )
;;

let suites = [ indices_suite; remove_at_suite ]
end

module Seq_suite = struct
let test_exists name f s expected =
let run () = Alcotest.(check bool) "are equal" expected (Knife.Seq.exists ~f s) in
Alcotest.test_case name `Quick run
;;

let exists_suite =
let s = Seq.ints 0 |> Seq.take 5 in
( "Seq.exists"
, [ test_exists "has no matched element" (Stdlib.( = ) 10) s false
; test_exists "has matched element" (Stdlib.( = ) 3) s true
; test_exists "has matched elements" (Stdlib.( > ) 3) s true
] )
;;

let test_indices name s expected =
let run () =
Alcotest.(check (list int))
"are equal"
expected
(s |> Knife.Seq.indices |> List.of_seq)
in
Alcotest.test_case name `Quick run
;;

let indices_suite =
( "Seq.indices"
, [ test_indices "empty sequence" Seq.empty []
; test_indices "non empty sequence" (Seq.ints 0 |> Seq.take 3) [ 0; 1; 2 ]
] )
;;

let test_sum name s expected =
let run () = Alcotest.(check int) "are equal" expected (Knife.Seq.sum s) in
Alcotest.test_case name `Quick run
;;

let sum_suite =
( "Seq.sum"
, [ test_sum "empty sequence" Seq.empty 0
; test_sum "non empty sequence" (Seq.ints 0 |> Seq.take 4) 6
] )
;;

let test_pairs_of name s expected =
let run () =
Alcotest.(check (list (pair int int)))
"are equal"
expected
(s |> Knife.Seq.pairs_of |> List.of_seq)
in
Alcotest.test_case name `Quick run
;;

let pairs_of_suite =
( "Seq.pairs_of"
, [ test_pairs_of "empty sequence" Seq.empty []
; test_pairs_of "single item sequence" (Seq.return 1) []
; test_pairs_of
"multiple items sequence"
(Seq.ints 0 |> Seq.take 5)
[ 0, 1; 1, 2; 2, 3; 3, 4 ]
] )
;;

let test_triples_of name s expected =
let run () =
Alcotest.(check (list (triple int int int)))
"are equal"
expected
(s |> Knife.Seq.triples_of |> List.of_seq)
in
Alcotest.test_case name `Quick run
;;

let triples_of_suite =
( "Seq.triples_of"
, [ test_triples_of "empty sequence" Seq.empty []
; test_triples_of "single item sequence" (Seq.return 1) []
; test_triples_of "double items sequence" (Seq.ints 0 |> Seq.take 2) []
; test_triples_of
"multiple items sequence"
(Seq.ints 0 |> Seq.take 5)
[ 0, 1, 2; 1, 2, 3; 2, 3, 4 ]
] )
;;

let suites =
[ exists_suite; indices_suite; sum_suite; pairs_of_suite; triples_of_suite ]
;;
end

module String_suite = struct
let test_indices name xs expected =
let run () =
Alcotest.(check (list int))
"are equal"
expected
(xs |> Knife.String.indices |> List.of_seq)
in
Alcotest.test_case name `Quick run
;;

let indices_suite =
( "String.indices"
, [ test_indices "empty string" "" []
; test_indices "non empty string" "abc" [ 0; 1; 2 ]
] )
;;

let suites = [ indices_suite ]
end

let () =
let open Alcotest in
run "Knife" (List_suite.suites @ Seq_suite.suites @ String_suite.suites)
;;
3 changes: 3 additions & 0 deletions tools/knife/dune
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
(library
(name knife)
(modes melange native))
3 changes: 3 additions & 0 deletions tools/knife/knife.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module List = List
module Seq = Seq
module String = String
8 changes: 8 additions & 0 deletions tools/knife/list.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
type 'a t = 'a list

let indices xs = Utils.indices_of Stdlib.List.length xs

let remove_at ~index =
let remove it _ = it <> index in
Stdlib.List.filteri remove
;;
5 changes: 5 additions & 0 deletions tools/knife/list.mli
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
type 'a t = 'a list

val indices : 'a list -> int Seq.t

val remove_at : index:int -> 'a list -> 'a list
18 changes: 18 additions & 0 deletions tools/knife/seq.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
type 'a t = 'a Stdlib.Seq.t

let exists ~f = Stdlib.Seq.exists f

let indices s = Stdlib.Seq.mapi Utils.index s

let sum = Stdlib.Seq.fold_left ( + ) 0

let pairs_of s =
let open Stdlib.Seq in
zip s (drop 1 s)
;;

let triples_of s =
let open Stdlib.Seq in
let triple_of (x, y) z = x, y, z in
map2 triple_of (pairs_of s) (drop 2 s)
;;
11 changes: 11 additions & 0 deletions tools/knife/seq.mli
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
type 'a t = 'a Stdlib.Seq.t

val exists : f:('a -> bool) -> 'a t -> bool

val indices : 'a t -> int t

val sum : int t -> int

val pairs_of : 'a t -> ('a * 'a) t

val triples_of : 'a t -> ('a * 'a * 'a) t
3 changes: 3 additions & 0 deletions tools/knife/string.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
type t = string

let indices s = Utils.indices_of Stdlib.String.length s
3 changes: 3 additions & 0 deletions tools/knife/string.mli
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
type t = string

val indices : t -> int Seq.t
11 changes: 11 additions & 0 deletions tools/knife/utils.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
let bool_of_option = function
| Some _ -> true
| None -> false
;;

let indices_of length_of xs =
let open Stdlib.Seq in
ints 0 |> take (length_of xs)
;;

let index index _ = index
5 changes: 5 additions & 0 deletions tools/knife/utils.mli
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
val bool_of_option : 'a option -> bool

val indices_of : ('a -> int) -> 'a -> int Stdlib.Seq.t

val index : int -> 'a -> int

0 comments on commit 0bb7e80

Please sign in to comment.