From 6d8fb621d1e93b83786890d6d9cb6f0d8defa8ae Mon Sep 17 00:00:00 2001 From: DJ O'Leary Date: Fri, 26 Jun 2026 18:04:09 +0200 Subject: refactor!: invert repo directory structure now is language first --- ocaml/lib/domain/d01p01.ml | 76 +++++++++++++++++ ocaml/lib/domain/d01p02.ml | 95 +++++++++++++++++++++ ocaml/lib/domain/d02p01.ml | 33 ++++++++ ocaml/lib/domain/d02p01.mli | 36 ++++++++ ocaml/lib/domain/d02p02.ml | 29 +++++++ ocaml/lib/domain/d02p02.mli | 30 +++++++ ocaml/lib/domain/d03p01.ml | 114 ++++++++++++++++++++++++++ ocaml/lib/domain/d03p02.ml | 116 ++++++++++++++++++++++++++ ocaml/lib/domain/dune | 13 +++ ocaml/lib/domain/matrix.ml | 196 ++++++++++++++++++++++++++++++++++++++++++++ ocaml/lib/domain/range.ml | 67 +++++++++++++++ 11 files changed, 805 insertions(+) create mode 100644 ocaml/lib/domain/d01p01.ml create mode 100644 ocaml/lib/domain/d01p02.ml create mode 100644 ocaml/lib/domain/d02p01.ml create mode 100644 ocaml/lib/domain/d02p01.mli create mode 100644 ocaml/lib/domain/d02p02.ml create mode 100644 ocaml/lib/domain/d02p02.mli create mode 100644 ocaml/lib/domain/d03p01.ml create mode 100644 ocaml/lib/domain/d03p02.ml create mode 100644 ocaml/lib/domain/dune create mode 100644 ocaml/lib/domain/matrix.ml create mode 100644 ocaml/lib/domain/range.ml (limited to 'ocaml/lib/domain') diff --git a/ocaml/lib/domain/d01p01.ml b/ocaml/lib/domain/d01p01.ml new file mode 100644 index 0000000..6b531f4 --- /dev/null +++ b/ocaml/lib/domain/d01p01.ml @@ -0,0 +1,76 @@ +module Dial : sig + (** [t] is a dial between 0 and 99 inclusive *) + type t + + (** [direction] is a direction the dial can be turned *) + type direction = Left of int | Right of int + + val start : t + (** [start] is the initial state of the dial *) + + val dial_out_of_range : string + (** [dial_out_of_range] is the error message when the dial is out of range *) + + val turn : t -> direction -> t + (** [turn current direction] is the new state of the dial after turning [direction] from [current] *) + + val to_string : t -> string + (** [to_string t] is the string representation of [t] *) + + val to_int : t -> int + (** [to_int t] is the integer representation of [t] *) + + val pp_direction : direction -> string +end = struct + (** [t] is an int between 0 and 99 inclusive *) + type t = int + + type direction = Left of int | Right of int + + let start = 50 + + let dial_out_of_range = "dial out of range" + + (** [next n] is the new state of the dial after turning [Right]. + Raises: [dial_out_of_range] if [n] is greater than 99. *) + let next = function + | n when n < 99 -> + n + 1 + | 99 -> + 0 + | _ -> + failwith dial_out_of_range + + (** [prev n] is the new state of the dial after turning [Left]. + Raises: [dial_out_of_range] if [n] is less than 0. *) + let prev = function + | n when n > 0 -> + n - 1 + | 0 -> + 99 + | _ -> + failwith dial_out_of_range + + (** [turn current direction] is the new state of the dial after turning [direction] from [current]. + Raises: [dial_out_of_range] if the dial is moved to an invalid state. *) + let rec turn current direction = + match direction with + | Left 0 -> + current + | Left n -> + turn (prev current) (Left (n - 1)) + | Right 0 -> + current + | Right n -> + turn (next current) (Right (n - 1)) + + let to_string = string_of_int + + let to_int t = t + + let pp_direction = function + | Left n -> + "Left " ^ string_of_int n + | Right n -> + "Right " ^ string_of_int n +end diff --git a/ocaml/lib/domain/d01p02.ml b/ocaml/lib/domain/d01p02.ml new file mode 100644 index 0000000..f71e965 --- /dev/null +++ b/ocaml/lib/domain/d01p02.ml @@ -0,0 +1,95 @@ +module Dial : sig + (** [t] is a dial between 0 and 99 inclusive *) + type t + + (** [direction] is a direction the dial can be turned *) + type direction = Left of int | Right of int + + val start : t + (** [start] is the initial state of the dial *) + + val dial_out_of_range : string + (** [dial_out_of_range] is the error message when the dial is out of range *) + + val prev : t -> t + + val next : t -> t + + val is_zero : t -> bool + + val to_string : t -> string + (** [to_string t] is the string representation of [t] *) + + val to_int : t -> int + (** [to_int t] is the integer representation of [t] *) + + val pp_direction : direction -> string +end = struct + (** [t] is an int between 0 and 99 inclusive *) + type t = int + + type direction = Left of int | Right of int + + let start = 50 + + let dial_out_of_range = "dial out of range" + + (** [next n] is the new state of the dial after turning [Right]. + Raises: [dial_out_of_range] if [n] is greater than 99. *) + let next = function + | n when n < 99 -> + n + 1 + | 99 -> + 0 + | _ -> + failwith dial_out_of_range + + (** [prev n] is the new state of the dial after turning [Left]. + Raises: [dial_out_of_range] if [n] is less than 0. *) + let prev = function + | n when n > 0 -> + n - 1 + | 0 -> + 99 + | _ -> + failwith dial_out_of_range + + let is_zero = function 0 -> true | _ -> false + + let to_string = string_of_int + + let to_int t = t + + let pp_direction = function + | Left n -> + "Left " ^ string_of_int n + | Right n -> + "Right " ^ string_of_int n +end + +module Turner : sig + val turn : Dial.t -> int -> Dial.direction -> Dial.t * int + (** [turn dial direction] is the new state of the dial after turning [direction] on [dial] *) +end = struct + (** [turn dial direction] is the new state of the dial after turning [direction] on [dial]. + Raises: [dial_out_of_range] if the dial is moved to an invalid state. *) + let rec turn dial zero_count direction = + let open Dial in + match direction with + | Left 0 -> + (dial, zero_count) + | Left n -> + let dial' = prev dial in + let zero_count' = + if Dial.is_zero dial' then zero_count + 1 else zero_count + in + turn dial' zero_count' (Left (n - 1)) + | Right 0 -> + (dial, zero_count) + | Right n -> + let dial' = next dial in + let zero_count' = + if Dial.is_zero dial' then zero_count + 1 else zero_count + in + turn dial' zero_count' (Right (n - 1)) +end diff --git a/ocaml/lib/domain/d02p01.ml b/ocaml/lib/domain/d02p01.ml new file mode 100644 index 0000000..725d4b1 --- /dev/null +++ b/ocaml/lib/domain/d02p01.ml @@ -0,0 +1,33 @@ +module ID = struct + (** *) + type t = int + + let create num = num + + let validate rules id = List.fold_left (fun acc r -> acc && r id) true rules + + let incr = ( + ) 1 + + let to_int id = id + + let to_string = string_of_int + + let pp id = "ID=" ^ string_of_int id +end + +module Range = struct + type t = ID.t * ID.t + + let create (id1, id2) = (ID.create id1, ID.create id2) + + let rec collect range = collect_range_aux [] range + + and collect_range_aux lst (id1, id2) = + match id1 <= id2 with + | false -> + lst + | true -> + collect_range_aux (id1 :: lst) (ID.incr id1, id2) + + let pp (id1, id2) = "(L" ^ ID.pp id1 ^ ", R" ^ ID.pp id2 ^ ")" +end diff --git a/ocaml/lib/domain/d02p01.mli b/ocaml/lib/domain/d02p01.mli new file mode 100644 index 0000000..55665fe --- /dev/null +++ b/ocaml/lib/domain/d02p01.mli @@ -0,0 +1,36 @@ +module ID : sig + (** [t] is a ID within a range *) + type t + + val create : int -> t + (** [create num] is an ID [t] *) + + val validate : (t -> bool) list -> t -> bool + (** [validate rules id] is [true] if the [id] passes all rule checks *) + + val incr : t -> t + (** [incr id] is the next [id] in the sequence *) + + val to_int : t -> int + (** [to_int id] is an integer representation of [id] *) + + val to_string : t -> string + (** [to_string id] is an string representation of [id] *) + + val pp : t -> string + (** [pp id] is a pretty string representing [id] *) +end + +module Range : sig + (** [t] is a range of values *) + type t + + val create : int * int -> t + (** [create (s, e)] is a range starting at [s] and ending at [e]. *) + + val collect : t -> ID.t list + (** [collect r] is a list containing all elements within the range (inclusive). *) + + val pp : t -> string + (** [pp r] is a pretty string representing [r] *) +end diff --git a/ocaml/lib/domain/d02p02.ml b/ocaml/lib/domain/d02p02.ml new file mode 100644 index 0000000..b2b55f1 --- /dev/null +++ b/ocaml/lib/domain/d02p02.ml @@ -0,0 +1,29 @@ +module ID = struct + (** *) + type t = int + + let create num = num + + let incr = ( + ) 1 + + let to_string = string_of_int + + let pp id = "ID=" ^ string_of_int id +end + +module Range = struct + type t = ID.t * ID.t + + let create (id1, id2) = (ID.create id1, ID.create id2) + + let rec collect range = collect_range_aux [] range + + and collect_range_aux lst (id1, id2) = + match id1 <= id2 with + | false -> + lst + | true -> + collect_range_aux (id1 :: lst) (ID.incr id1, id2) + + let pp (id1, id2) = "(L" ^ ID.pp id1 ^ ", R" ^ ID.pp id2 ^ ")" +end diff --git a/ocaml/lib/domain/d02p02.mli b/ocaml/lib/domain/d02p02.mli new file mode 100644 index 0000000..f918ae1 --- /dev/null +++ b/ocaml/lib/domain/d02p02.mli @@ -0,0 +1,30 @@ +module ID : sig + (** [t] is a ID within a range *) + type t + + val create : int -> t + (** [create num] is an ID [t] *) + + val incr : t -> t + (** [incr id] is the next [id] in the sequence *) + + val to_string : t -> string + (** [to_string id] is an string representation of [id] *) + + val pp : t -> string + (** [pp id] is a pretty string representing [id] *) +end + +module Range : sig + (** [t] is a range of values *) + type t + + val create : int * int -> t + (** [create (s, e)] is a range starting at [s] and ending at [e]. *) + + val collect : t -> ID.t list + (** [collect r] is a list containing all elements within the range (inclusive). *) + + val pp : t -> string + (** [pp r] is a pretty string representing [r] *) +end diff --git a/ocaml/lib/domain/d03p01.ml b/ocaml/lib/domain/d03p01.ml new file mode 100644 index 0000000..1367712 --- /dev/null +++ b/ocaml/lib/domain/d03p01.ml @@ -0,0 +1,114 @@ +module Battery : sig + type t + + val of_int : int -> t + + val to_int : t -> int + + val to_string : t -> string + + val compare : t -> t -> int + + val pp : t -> string +end = struct + type t = int + + let of_int i = i + + let to_int b = b + + let to_string = string_of_int + + let compare = Int.compare + + let pp = string_of_int +end + +module BatteryBank : sig + type t + + val highest_joltage : t -> int + (** [highest_joltage bb] is an integer created from the two highest + digits in [bb]. + Raises: [Assert_failure] if [bb] is empty. *) + + val of_string : string -> t + + val of_list : Battery.t list -> t + + val to_list : t -> Battery.t list + + val pp : t -> string +end = struct + type t = Battery.t list + + let rec highest_joltage bb = + assert (List.length bb > 0) ; + match highest_joltage_aux (None, None) bb with + | None, None -> + 0 + | Some x, Some y -> + int_of_string (Battery.to_string x ^ Battery.to_string y) + | _ -> + failwith "highest_joltage must be two digits" + + and highest_joltage_aux acc = function + | [] -> + acc + | [elt] -> ( + match acc with + | None, None -> + (Some (Battery.of_int 0), Some elt) + | Some first, None -> + (Some first, Some elt) + | None, Some second -> + (Some second, Some elt) + | Some first, Some second -> + if elt > second then (Some first, Some elt) + else (Some first, Some second) ) + | h :: t -> + highest_joltage_aux + ( match acc with + | None, None -> + (Some h, None) + | Some first, None -> + let second = Battery.of_int 0 in + swap_if_higher first second h + | Some first, Some second -> + swap_if_higher first second h + | _ -> + failwith "this branch should be impossible" ) + t + + and swap_if_higher first second considering = + assert (first >= second) ; + let c_gt_f = Battery.compare considering first = 1 in + let c_gt_s = Battery.compare considering second = 1 in + match (c_gt_f, c_gt_s) with + | true, true -> + (Some considering, None) + | false, true -> + (Some first, Some considering) + | true, false -> + failwith "[second] should never be greater than [first]" + | false, false -> + (Some first, Some second) + + let of_string s = + s |> String.to_seq + |> Seq.map (fun c -> Char.escaped c |> int_of_string |> Battery.of_int) + |> List.of_seq + + let of_list lst = lst + + let to_list bb = bb + + let pp bb = + "[ " + ^ ( bb |> List.map Battery.pp + |> List.fold_left + (fun acc b -> + if acc = "" then b ^ ";" else Printf.sprintf "%s %s;" acc b ) + "" ) + ^ " ]" +end diff --git a/ocaml/lib/domain/d03p02.ml b/ocaml/lib/domain/d03p02.ml new file mode 100644 index 0000000..88257d0 --- /dev/null +++ b/ocaml/lib/domain/d03p02.ml @@ -0,0 +1,116 @@ +module Battery : sig + type t + + val of_int : int -> t + + val to_int : t -> int + + val to_string : t -> string + + val compare : t -> t -> int + + val pp : t -> string +end = struct + type t = int + + let of_int i = i + + let to_int b = b + + let to_string = string_of_int + + let compare = Int.compare + + let pp = string_of_int +end + +module BatteryBank : sig + type t + + val highest_joltage : t -> int + (** [highest_joltage bb] is an integer created from the two highest + digits in [bb]. + Raises: [Assert_failure] if [bb] is empty. *) + + val of_string : string -> t + + val of_list : Battery.t list -> t + + val to_list : t -> Battery.t list + + val pp : t -> string +end = struct + type t = Battery.t list + + type highest = {one: Battery.t option; two: Battery.t option} + + let rec highest_joltage bb = + assert (List.length bb > 0) ; + match highest_joltage_aux {one= None; two= None} bb with + | {one= None; two= None} -> + 0 + | {one= Some x; two= Some y} -> + int_of_string (Battery.to_string x ^ Battery.to_string y) + | _ -> + failwith "highest_joltage must be two digits" + + and highest_joltage_aux acc = function + | [] -> + acc + | [elt] -> ( + match acc with + | {one= None; two= None} -> + {one= Some (Battery.of_int 0); two= Some elt} + | {one= Some first; two= None} -> + {one= Some first; two= Some elt} + | {one= None; two= Some second} -> + {one= Some second; two= Some elt} + | {one= Some first; two= Some second} -> + if elt > second then {one= Some first; two= Some elt} + else {one= Some first; two= Some second} ) + | h :: t -> + highest_joltage_aux + ( match acc with + | {one= None; two= None} -> + {one= Some h; two= None} + | {one= Some first; two= None} -> + let second = Battery.of_int 0 in + swap_if_higher first second h + | {one= Some first; two= Some second} -> + swap_if_higher first second h + | _ -> + failwith "this branch should be impossible" ) + t + + and swap_if_higher first second considering = + assert (first >= second) ; + let c_gt_f = Battery.compare considering first = 1 in + let c_gt_s = Battery.compare considering second = 1 in + match (c_gt_f, c_gt_s) with + | true, true -> + {one= Some considering; two= None} + | false, true -> + {one= Some first; two= Some considering} + | true, false -> + failwith "[second] should never be greater than [first]" + | false, false -> + {one= Some first; two= Some second} + + let of_string s = + s |> String.to_seq + |> Seq.map (fun c -> Char.escaped c |> int_of_string |> Battery.of_int) + |> List.of_seq + + let of_list lst = lst + + let to_list bb = bb + + let pp bb = + "[ " + ^ ( bb |> List.map Battery.pp + |> List.fold_left + (fun acc b -> + if acc = "" then b ^ ";" else Printf.sprintf "%s %s;" acc b ) + "" ) + ^ " ]" +end diff --git a/ocaml/lib/domain/dune b/ocaml/lib/domain/dune new file mode 100644 index 0000000..5b13d31 --- /dev/null +++ b/ocaml/lib/domain/dune @@ -0,0 +1,13 @@ +(library + (name domain) + (inline_tests) + (libraries + fmt + logs) + (preprocess + (pps + ppx_deriving.show + ppx_deriving.eq + ppx_deriving.ord + ppx_inline_test + ppx_expect))) diff --git a/ocaml/lib/domain/matrix.ml b/ocaml/lib/domain/matrix.ml new file mode 100644 index 0000000..fdf476f --- /dev/null +++ b/ocaml/lib/domain/matrix.ml @@ -0,0 +1,196 @@ +module M : sig + type 'a t = 'a array array + + val empty : int -> int -> 'a -> 'a t + (** [empty rows columns initial] is an empty 2D array of size [rows] * [columns] + where each element in the array is initialized to [initial]. *) + + val of_list : 'a list list -> 'a t + (** [of_list lst] is a 2D array where every element in the array is an + element corresponds to an element of the [lst]. *) + + val to_list : 'a t -> 'a list list + (** [to_list matrix] is a 2D list where every element in the list is an + element corresponds to an element of the [matrix]. *) + + val of_string : string -> string t + (** [of_string str] is a 2D array where every character is an element. *) + + val get : 'a t -> int -> int -> 'a + (** [get matrix x y] is an alias for [matrix.(y).(x)]. + Requires: [x] and [y] are positive integers or 0. *) + + val set : 'a t -> int -> int -> 'a -> unit + (** [set matrix x y v] is an alias for [matrix.(y).(x) <- v]. + Requires: [x] and [y] are positive integers or 0. *) + + val get_valid_neighbours : 'a t -> int -> int -> (int * int) list + (** [get_valid_neighbours matrix x y] is a list of pairs in the format + (x * y) that represent the valid surrounding positions of [matrix.(y).(x)]. + Requires: [x] and [y] are positive integers or 0. *) + + val pp : ('a -> string) -> 'a t -> string + (** [pp matrix string_of_elt] is the string representation of the [matrix] + where [string_of_elt] is called on each element. *) +end = struct + type 'a t = 'a array array + + let empty cols rows initial = Array.make_matrix cols rows initial + + let of_list lst = Array.of_list @@ List.map Array.of_list lst + + let to_list matrix = Array.to_list @@ Array.map Array.to_list matrix + + let of_string str = + let lines = + str |> String.split_on_char '\n' + |> List.filter_map (fun line -> + let trimmed = String.trim line in + if trimmed <> "" then Some trimmed else None ) + in + let char_lst = + List.map + (fun s -> s |> String.to_seq |> List.of_seq |> List.map Char.escaped) + lines + in + of_list char_lst + + let get matrix x y = matrix.(y).(x) + + let set matrix x y v = matrix.(y).(x) <- v + + let get_valid_neighbours matrix x y = + let potential_neighbours = + [ (x - 1, y - 1) + ; (x, y - 1) + ; (x + 1, y - 1) + ; (x - 1, y) + ; (x + 1, y) + ; (x - 1, y + 1) + ; (x, y + 1) + ; (x + 1, y + 1) ] + in + let min_x = 0 in + let max_x = Array.length matrix.(y) - 1 in + let min_y = 0 in + let max_y = Array.length matrix - 1 in + List.filter + (fun (x, y) -> min_x <= x && x <= max_x && min_y <= y && y <= max_y) + potential_neighbours + + let pp string_of_elt matrix = + "[" + ^ Array.fold_left + (fun col_acc arr -> + let row = + "[" + ^ Array.fold_left + (fun row_acc elt -> row_acc ^ string_of_elt elt ^ "; ") + " " arr + ^ "]" + in + col_acc ^ " " ^ row ^ "\n" ) + "\n" matrix + ^ "]" +end + +let%expect_test "matrix created from input is pretty-printed as the same matrix" + = + let example = + {|..@@.@@@@. +@@@.@.@.@@ +@@@@@.@.@@ +@.@@@@..@. +@@.@@@@.@@ +.@@@@@@@.@ +.@.@.@.@@@ +@.@@@.@@@@ +.@@@@@@@@. +@.@.@@@.@.|} + in + print_string @@ M.pp (fun s -> s) @@ M.of_string example ; + [%expect + {| + [ + [ .; .; @; @; .; @; @; @; @; .; ] + [ @; @; @; .; @; .; @; .; @; @; ] + [ @; @; @; @; @; .; @; .; @; @; ] + [ @; .; @; @; @; @; .; .; @; .; ] + [ @; @; .; @; @; @; @; .; @; @; ] + [ .; @; @; @; @; @; @; @; .; @; ] + [ .; @; .; @; .; @; .; @; @; @; ] + [ @; .; @; @; @; .; @; @; @; @; ] + [ .; @; @; @; @; @; @; @; @; .; ] + [ @; .; @; .; @; @; @; .; @; .; ] + ] + |}] + +let%expect_test "direct access of the matrix is in form matrix.(y).(x)" = + let example = + {|..@@.@@@@. +@@@.@.@.@@ +@@@@@.@.@@ +@.@@@@..@. +@@.@@@@.@@ +.@@@@@@@.@ +.@.@.@.@@@ +@.@@@.@@@@ +.@@@@@@@@. +@.@.@@@.@.|} + in + let matrix = M.of_string example in + print_string matrix.(0).(0) ; + [%expect "."] ; + print_string matrix.(0).(1) ; + [%expect "."] ; + print_string matrix.(0).(2) ; + [%expect "@"] ; + print_string matrix.(0).(3) ; + [%expect "@"] ; + print_string matrix.(0).(4) ; + [%expect "."] ; + print_string matrix.(0).(5) ; + [%expect "@"] ; + print_string matrix.(0).(6) ; + [%expect "@"] ; + print_string matrix.(0).(7) ; + [%expect "@"] ; + print_string matrix.(0).(8) ; + [%expect "@"] ; + print_string matrix.(0).(9) ; + [%expect "."] + +let%expect_test "[M.get matrix x y] is an alias for [matrix.(y).(x)]" = + let example = + {|..@@.@@@@. +@@@.@.@.@@ +@@@@@.@.@@ +@.@@@@..@. +@@.@@@@.@@ +.@@@@@@@.@ +.@.@.@.@@@ +@.@@@.@@@@ +.@@@@@@@@. +@.@.@@@.@.|} + in + let matrix = M.of_string example in + print_string @@ M.get matrix 0 0 ; + [%expect "."] ; + print_string @@ M.get matrix 1 0 ; + [%expect "."] ; + print_string @@ M.get matrix 2 0 ; + [%expect "@"] ; + print_string @@ M.get matrix 3 0 ; + [%expect "@"] ; + print_string @@ M.get matrix 4 0 ; + [%expect "."] ; + print_string @@ M.get matrix 5 0 ; + [%expect "@"] ; + print_string @@ M.get matrix 6 0 ; + [%expect "@"] ; + print_string @@ M.get matrix 7 0 ; + [%expect "@"] ; + print_string @@ M.get matrix 8 0 ; + [%expect "@"] ; + print_string @@ M.get matrix 9 0 ; + [%expect "."] diff --git a/ocaml/lib/domain/range.ml b/ocaml/lib/domain/range.ml new file mode 100644 index 0000000..a1bdc00 --- /dev/null +++ b/ocaml/lib/domain/range.ml @@ -0,0 +1,67 @@ +module M : sig + type t + + val of_string : string -> t option + (** [of_string s] is a range of integers if [s] is a string in the form ["%d-%d"] + or [None] otherwise. *) + + val collect : t -> int list + (** [collect range] is a fully realised range as a list. + Example: ["1-5" |> of_string |> collect] is the list [\[1;2;3;4;5\]]. + Example: ["5-1" |> of_string |> collect] is the list [\[5;4;3;2;1\]].*) + + val pp : t -> string + (** [pp range] is a pretty-printed version of the range. *) +end = struct + type t = int * int + + let of_string s = + Scanf.sscanf_opt s "%d-%d" (fun start finish -> (start, finish)) + + let rec collect (start, finish) = + let collected = + match Int.compare start finish with + | -1 -> + collect_range_aux Int.succ ( > ) [] (start, finish) + | 0 -> + [start] + | 1 -> + collect_range_aux Int.pred ( < ) [] (start, finish) + | _ -> + assert false + in + List.rev collected + + and collect_range_aux fn cmp lst (start, finish) = + if cmp start finish then lst + else collect_range_aux fn cmp (start :: lst) (fn start, finish) + + let pp (start, finish) = string_of_int start ^ "-" ^ string_of_int finish +end + +let%expect_test "incrementing range" = + match M.of_string "1-5" with + | None -> + [%expect.unreachable] + | Some range -> + let collected = M.collect range in + List.iter print_int collected ; + [%expect "12345"] + +let%expect_test "decrementing range" = + match M.of_string "5-1" with + | None -> + [%expect.unreachable] + | Some range -> + let collected = M.collect range in + List.iter print_int collected ; + [%expect "54321"] + +let%expect_test "one element range" = + match M.of_string "1-1" with + | None -> + [%expect.unreachable] + | Some range -> + let collected = M.collect range in + List.iter print_int collected ; + [%expect "1"] -- cgit v1.2.3