|
| 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)) |
0 commit comments