diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/chart.ml | 113 | ||||
-rw-r--r-- | src/chart.mli | 5 | ||||
-rw-r--r-- | src/marker.ml | 45 | ||||
-rw-r--r-- | src/marker.mli | 4 | ||||
-rw-r--r-- | src/options.ml | 15 | ||||
-rw-r--r-- | src/options.mli | 4 | ||||
-rw-r--r-- | src/utils.ml | 32 |
7 files changed, 103 insertions, 115 deletions
diff --git a/src/chart.ml b/src/chart.ml index 0826092..de79082 100644 --- a/src/chart.ml +++ b/src/chart.ml | |||
@@ -1,61 +1,66 @@ | |||
1 | let to_string c = String.make 1 c | 1 | let to_string c = String.make 1 c |
2 | |||
2 | let space = to_string ' ' | 3 | let space = to_string ' ' |
4 | |||
3 | let axis = "┬" | 5 | let axis = "┬" |
6 | |||
4 | let axis_spc = "─" | 7 | let axis_spc = "─" |
5 | 8 | ||
6 | type t = { vals: int list; opts: Options.t } | 9 | type t = { vals : int list; opts : Options.t } |
10 | |||
7 | let default = { vals = []; opts = Options.default } | 11 | let default = { vals = []; opts = Options.default } |
8 | let plot v c = { c with vals = v; } | 12 | |
9 | let with_options v c = { v with opts = c; } | 13 | let plot v c = { c with vals = v } |
10 | let pp channel {vals: int list; opts: Options.t } = | 14 | |
11 | let open Utils in | 15 | let with_options v c = { c with opts = v } |
12 | let height = opts.height in | 16 | |
13 | let min_val = list_min vals in | 17 | let pp formatter { vals : int list; opts : Options.t } = |
14 | let max_val = list_max vals in | 18 | let open Utils in |
15 | let range = max_val - min_val in | 19 | let height = opts.height in |
16 | let scale v = ((v - min_val) * height) / range in | 20 | let min_val = list_min vals in |
17 | let new_vals = List.map scale vals in | 21 | let max_val = list_max vals in |
18 | let rec pad left right s = | 22 | let range = max_val - min_val in |
19 | if left = 0 | 23 | let scale v = (v - min_val) * height / range in |
20 | then s :: repeat right space | 24 | let new_vals = List.map scale vals in |
21 | else space :: pad (left - 1) right s | 25 | let rec pad left right s = |
22 | in | 26 | if left = 0 then s :: repeat right space |
23 | let new_vals' = zip new_vals (List.tl new_vals) in | 27 | else space :: pad (left - 1) right s |
24 | let make_marker prefix left right s = | 28 | in |
25 | prefix :: | 29 | let new_vals' = zip new_vals (List.tl new_vals) in |
26 | (if left = 0 | 30 | let make_marker prefix left right s = |
27 | then s :: repeat right space | 31 | prefix |
28 | else space :: pad (left - 1) right s) | 32 | :: |
29 | in | 33 | (if left = 0 then s :: repeat right space |
30 | let make_connector s e = | 34 | else space :: pad (left - 1) right s) |
31 | match Stdlib.compare s e with | 35 | in |
32 | | 0 -> make_marker axis_spc s (height - s) "-" | 36 | let make_connector s e = |
33 | | x -> | 37 | match Stdlib.compare s e with |
34 | let (ltr_chr, rtl_chr) = | 38 | | 0 -> make_marker axis_spc s (height - s) "-" |
35 | match x with | 39 | | x -> |
36 | | 1 -> ("└", "┐") | 40 | let ltr_chr, rtl_chr = |
37 | | _ -> ("┘", "┌") | 41 | match x with 1 -> ("└", "┐") | _ -> ("┘", "┌") |
38 | in | ||
39 | let (st, en) = Stdlib.((min s e, max s e)) in | ||
40 | let l = en - st |> saturating_sub 1 in | ||
41 | let (left, right) = (repeat st space, repeat (height - l - st) space) in | ||
42 | List.flatten [[axis_spc]; left; [ltr_chr]; repeat l "│"; [rtl_chr]; right] | ||
43 | in | 42 | in |
44 | let m = Printf.sprintf opts.marker in | 43 | let st, en = Stdlib.(min s e, max s e) in |
45 | let rec make_graph = function | 44 | let l = en - st |> saturating_sub 1 in |
46 | | [] -> [] | 45 | let left, right = (repeat st space, repeat (height - l - st) space) in |
47 | | (s, e) :: [] -> | 46 | List.flatten |
48 | make_marker axis s (height - s) m | 47 | [ |
49 | :: make_connector s e | 48 | [ axis_spc ]; left; [ ltr_chr ]; repeat l "│"; [ rtl_chr ]; right; |
50 | :: make_marker axis e (height - e) m | 49 | ] |
51 | :: [] | 50 | in |
52 | | (s, e) :: tl -> | 51 | let m = Format.asprintf "%a" Marker.pp opts.marker in |
53 | make_marker axis s (height - s) m | 52 | let rec make_graph = function |
54 | :: make_connector s e | 53 | | [] -> [] |
55 | :: make_graph tl | 54 | | [ (s, e) ] -> |
56 | in | 55 | [ |
57 | make_graph new_vals' | 56 | make_marker axis s (height - s) m; |
58 | |> rot_left | 57 | make_connector s e; |
59 | |> List.map (String.concat "") | 58 | make_marker axis e (height - e) m; |
60 | |> String.concat "\n" | 59 | ] |
61 | |> Printf.fprintf channel "%s" | 60 | | (s, e) :: tl -> |
61 | make_marker axis s (height - s) m :: make_connector s e :: make_graph tl | ||
62 | in | ||
63 | make_graph new_vals' |> rot_left | ||
64 | |> List.map (String.concat "") | ||
65 | |> String.concat "\n" | ||
66 | |> Format.fprintf formatter "%s" | ||
diff --git a/src/chart.mli b/src/chart.mli deleted file mode 100644 index 3c758c4..0000000 --- a/src/chart.mli +++ /dev/null | |||
@@ -1,5 +0,0 @@ | |||
1 | type t | ||
2 | val default : t | ||
3 | val plot : int list -> t -> t | ||
4 | val with_options : Options.t -> t -> t | ||
5 | val pp : out_channel -> t -> unit | ||
diff --git a/src/marker.ml b/src/marker.ml index 0c59804..7d37e2a 100644 --- a/src/marker.ml +++ b/src/marker.ml | |||
@@ -1,27 +1,26 @@ | |||
1 | type t = Point | 1 | type t = |
2 | | Pixel | 2 | | Point |
3 | | Circle | 3 | | Pixel |
4 | | TriangleDown | 4 | | Circle |
5 | | TriangleUp | 5 | | TriangleDown |
6 | | TriangleLeft | 6 | | TriangleUp |
7 | | TriangleRight | 7 | | TriangleLeft |
8 | | Custom of string | 8 | | TriangleRight |
9 | | Custom of string | ||
9 | 10 | ||
10 | exception Invalid_marker_length | 11 | exception Invalid_marker_length |
11 | let of_string s = | ||
12 | if String.length s <> 1 | ||
13 | then raise Invalid_marker_length | ||
14 | else Custom(s) | ||
15 | 12 | ||
16 | let pp channel n = | 13 | let of_string s = |
17 | (match n with | 14 | if String.length s <> 1 then raise Invalid_marker_length else Custom s |
18 | | Point -> "." | ||
19 | | Pixel -> "," | ||
20 | | Circle -> "o" | ||
21 | | TriangleDown -> "v" | ||
22 | | TriangleUp -> "^" | ||
23 | | TriangleLeft -> ">" | ||
24 | | TriangleRight -> "<" | ||
25 | | Custom(s) -> s) | ||
26 | |> Printf.fprintf channel "%s" | ||
27 | 15 | ||
16 | let pp formatter n = | ||
17 | (match n with | ||
18 | | Point -> "." | ||
19 | | Pixel -> "," | ||
20 | | Circle -> "o" | ||
21 | | TriangleDown -> "v" | ||
22 | | TriangleUp -> "^" | ||
23 | | TriangleLeft -> ">" | ||
24 | | TriangleRight -> "<" | ||
25 | | Custom s -> s) | ||
26 | |> Format.fprintf formatter "%s" | ||
diff --git a/src/marker.mli b/src/marker.mli deleted file mode 100644 index 391916b..0000000 --- a/src/marker.mli +++ /dev/null | |||
@@ -1,4 +0,0 @@ | |||
1 | type t | ||
2 | val of_string : string -> t | ||
3 | val pp : out_channel -> t -> unit | ||
4 | |||
diff --git a/src/options.ml b/src/options.ml index a57c28b..341c0bb 100644 --- a/src/options.ml +++ b/src/options.ml | |||
@@ -1,8 +1,7 @@ | |||
1 | type t = { height: int; ascii: bool; marker: Marker.t} | 1 | type t = { height : int; ascii : bool; marker : Marker.t } |
2 | let default = | 2 | |
3 | { height = 14 | 3 | let default = { height = 14; ascii = false; marker = Marker.Circle } |
4 | ; ascii = false | 4 | |
5 | ; marker = Marker.Point | 5 | let with_height v c = { c with height = v } |
6 | } | 6 | |
7 | let with_height v c = { c with height = v; } | 7 | let with_marker v c = { c with marker = v } |
8 | let with_marker v c = { c with marker = v; } | ||
diff --git a/src/options.mli b/src/options.mli deleted file mode 100644 index 64bee51..0000000 --- a/src/options.mli +++ /dev/null | |||
@@ -1,4 +0,0 @@ | |||
1 | type t | ||
2 | val default : t | ||
3 | val with_height : int -> t -> t | ||
4 | val with_marker : Marker.t -> t -> t | ||
diff --git a/src/utils.ml b/src/utils.ml index 62ae3a5..833c972 100644 --- a/src/utils.ml +++ b/src/utils.ml | |||
@@ -1,33 +1,31 @@ | |||
1 | exception Empty_list | 1 | exception Empty_list |
2 | |||
2 | let fold fn = function | 3 | let fold fn = function |
3 | | [] -> raise Empty_list | 4 | | [] -> raise Empty_list |
4 | | head :: tail -> List.fold_left fn head tail | 5 | | head :: tail -> List.fold_left fn head tail |
5 | 6 | ||
6 | let list_min ls = fold Stdlib.min ls | 7 | let list_min ls = fold Stdlib.min ls |
8 | |||
7 | let list_max ls = fold Stdlib.max ls | 9 | let list_max ls = fold Stdlib.max ls |
8 | 10 | ||
9 | let rec repeat n a = | 11 | let rec repeat n a = if n <= 0 then [] else a :: repeat (n - 1) a |
10 | if n <= 0 | ||
11 | then [] | ||
12 | else a :: repeat (n - 1) a | ||
13 | 12 | ||
14 | let empty ls = (ls = []) | 13 | let empty ls = ls = [] |
15 | 14 | ||
16 | let rec zip l1 l2 = | 15 | let rec zip l1 l2 = |
17 | match (l1, l2) with | 16 | match (l1, l2) with |
18 | | ([], _) -> [] | 17 | | [], _ -> [] |
19 | | (_, []) -> [] | 18 | | _, [] -> [] |
20 | | (h1 :: t1, h2 :: t2) -> (h1, h2) :: zip t1 t2 | 19 | | h1 :: t1, h2 :: t2 -> (h1, h2) :: zip t1 t2 |
21 | 20 | ||
22 | let rec transpose = function | 21 | let rec transpose = function |
23 | | [] :: _ -> [] | 22 | | [] :: _ -> [] |
24 | | ls -> List.(map hd ls) :: transpose List.(map tl ls) | 23 | | ls -> List.(map hd ls) :: transpose List.(map tl ls) |
25 | 24 | ||
26 | let flip f a b = f b a | 25 | let flip f a b = f b a |
27 | 26 | ||
28 | let rot_right ls = List.(rev ls |> transpose) | 27 | let rot_right ls = List.(rev ls |> transpose) |
29 | let rot_left ls = List.(transpose ls |> rev) | ||
30 | 28 | ||
31 | let saturating_sub b a = | 29 | let rot_left ls = List.(transpose ls |> rev) |
32 | if a < b then 0 | 30 | |
33 | else a - b | 31 | let saturating_sub b a = if a < b then 0 else a - b |