type t = { vals : int list; opts : Options.t } let default = { vals = []; opts = Options.default } let plot v c = { c with vals = v } let with_options v c = { c with opts = v } let pp formatter { vals : int list; opts : Options.t } = let open Utils in let height = opts.height in let min_val = list_min vals in let max_val = list_max vals in let range = max_val - min_val in let scale v = (v - min_val) * height / range in let new_vals = List.map scale vals in let rec pad left right s = if left = 0 then s :: repeat right space else space :: pad (left - 1) right s in let new_vals' = zip new_vals (List.tl new_vals) in let make_marker prefix left right s = prefix :: (if left = 0 then s :: repeat right space else space :: pad (left - 1) right s) in let make_connector s e = match Stdlib.compare s e with | 0 -> make_marker axis_spc s (height - s) "-" | x -> let ltr_chr, rtl_chr = match x with 1 -> ("└", "┐") | _ -> ("┘", "┌") in let st, en = Stdlib.(min s e, max s e) in let l = en - st |> saturating_sub 1 in let left, right = (repeat st space, repeat (height - l - st) space) in List.flatten [ [ axis_spc ]; left; [ ltr_chr ]; repeat l "│"; [ rtl_chr ]; right; ] in let m = Format.asprintf "%a" Marker.pp opts.marker in let rec make_graph = function | [] -> [] | [ (s, e) ] -> [ make_marker axis s (height - s) m; make_connector s e; make_marker axis e (height - e) m; ] | (s, e) :: tl -> make_marker axis s (height - s) m :: make_connector s e :: make_graph tl in make_graph new_vals' |> rot_left |> List.map (String.concat "") |> String.concat "\n" |> Format.fprintf formatter "%s"