Skip to content

Commit 20493c1

Browse files
committed
Add utils/ directory
Signed-off-by: Christian Lindig <lindig@gmail.com>
1 parent 9568aa6 commit 20493c1

4 files changed

Lines changed: 91 additions & 0 deletions

File tree

Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ clean:
2828
utop:
2929
$(DUNE) utop
3030

31+
%.mli: %.ml
32+
$(DUNE) exec -- ocaml-print-intf $< > $@
33+
3134
format:
3235
$(DUNE) build --auto-promote @fmt
3336
dune format-dune-file dune-project > $$$$ && mv $$$$ dune-project

utils/dji.ml

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
(* We want to represent non-overlapping float intervals with an associated value.
2+
We add intervals to a map; this is rejected, when the new addition would
3+
overlap with an existing interval. Hence, order of insertion matters.
4+
The result is presented in increasing order (not in insertion order).
5+
*)
6+
7+
module M = Map.Make (Float)
8+
9+
(* An interval x,y is represented as a map from x to a ('a, y) tuple. So it is ordered by x *)
10+
11+
type 'a t = float * 'a * float
12+
13+
let empty = M.empty
14+
15+
(* Before adding a new interval (x,y) with a value 'v', we need to check: the interval to
16+
the left ends before x and the interval to the right starts after y.
17+
Otherwise we don't add (x,v,y) *)
18+
let add t (x, v, y) =
19+
assert (x < y);
20+
let before = M.find_first_opt (fun x' -> x' <= x) t in
21+
let after = M.find_first_opt (fun x' -> x' >= x) t in
22+
match (before, after) with
23+
| None, None -> M.add x (v, y) t
24+
| Some (_, (_, y')), None when y' <= x -> M.add x (v, y) t
25+
| None, Some (x', _) when y <= x' -> M.add x (v, y) t
26+
| Some (_, (_, y')), Some (x', _) when y' <= x && y <= x' -> M.add x (v, y) t
27+
| _ -> t
28+
29+
let from_list intervals = List.fold_left add empty intervals
30+
31+
let disjoint intervals =
32+
from_list intervals |> M.bindings |> List.map (fun (x, (v, y)) -> (x, v, y))
33+
34+
let tests =
35+
[
36+
(* 1: No overlaps *)
37+
( [ (1., "a", 3.); (4., "b", 6.); (7., "c", 9.) ]
38+
, [ (1., "a", 3.); (4., "b", 6.); (7., "c", 9.) ] )
39+
; (* 2.: Simple overlap at start *)
40+
( [ (1., "a", 5.); (2., "b", 4.); (6., "c", 8.) ]
41+
, [ (1., "a", 5.); (6., "c", 8.) ] )
42+
; (* 3.: Simple overlap at end *)
43+
( [ (1., "a", 3.); (2., "b", 5.); (6., "c", 8.) ]
44+
, [ (1., "a", 3.); (6., "c", 8.) ] )
45+
; (* 4.: Adjacent intervals, not an overlap *)
46+
( [ (1., "a", 3.); (3., "b", 5.); (6., "c", 8.) ]
47+
, [ (1., "a", 3.); (3., "b", 5.); (6., "c", 8.) ] )
48+
; (* 5.: A sub-interval completely contained within another *)
49+
( [ (1., "a", 10.); (3., "b", 7.); (12., "c", 15.) ]
50+
, [ (1., "a", 10.); (12., "c", 15.) ] )
51+
; (* 6.: Multiple overlapping intervals *)
52+
( [ (1., "a", 5.); (2., "b", 6.); (3., "c", 7.); (8., "d", 9.) ]
53+
, [ (1., "a", 5.); (8., "d", 9.) ] )
54+
; (* 7.: Empty list *)
55+
([], [])
56+
; (* 8.: List with a single element *)
57+
([ (10., "a", 20.) ], [ (10., "a", 20.) ])
58+
; (* 9.: Overlapping intervals that are processed in order *)
59+
( [ (5., "a", 10.); (1., "b", 6.); (11., "c", 15.) ]
60+
, [ (5., "a", 10.); (11., "c", 15.) ] )
61+
; (* 10.: Reverse order of the first case, to check order dependency *)
62+
( [ (7., "a", 9.); (4., "b", 6.); (1., "c", 3.) ]
63+
, [ (1., "c", 3.); (4., "b", 6.); (7., "a", 9.) ] )
64+
]
65+
66+
let test () =
67+
tests
68+
|> List.iter (fun (intervals, expected) ->
69+
assert (expected = disjoint intervals))

utils/dji.mli

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
(* Given (x,y) intervals, compute the non-overlapping intervals from a
2+
list of intervals *)
3+
4+
type 'a t = float * 'a * float
5+
(* An interval (x,y) with x < y and an associated value 'a *)
6+
7+
val disjoint : 'a t list -> 'a t list
8+
(* Given a list of intervals, compute the subset of non-overlapping
9+
intervals and return them in increasing order. An interval from the
10+
argument list is not in the result if it overlaps with another
11+
interval earlier in the list. As such, the order of intervals in the
12+
argument matters. Note that the result does *not* maintain that order
13+
but returns intervals in increasing order. Because intervals in the
14+
result are not overlapping, this order is well defined. *)
15+
16+
val test : unit -> unit
17+
(* Run internal test *)

utils/dune

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
(library
2+
(name utils))

0 commit comments

Comments
 (0)