1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
open! Core
open! Import
module Col = struct
type t =
{ desired_width : int
; min_width : int
}
[@@deriving fields ~getters]
let shrink_if_possible t =
let { desired_width; min_width } = t in
match desired_width > min_width with
| true -> This { t with desired_width = desired_width - 1 }
| false -> Null
;;
end
let rec reduce ~excess cols =
match excess <= 0 with
| true -> List.map cols ~f:Col.desired_width
| false ->
let excess = ref excess in
let cols =
let%map.List col = cols in
if !excess > 0
then (
match Col.shrink_if_possible col with
| Null -> col
| This col ->
excess := !excess - 1;
col)
else col
in
reduce ~excess:!excess cols
;;
module Request = struct
type t =
{ label : String.Utf8.t
; desired_width : int
; min_width : int or_null
}
[@@deriving sexp_of]
let min t =
match t.min_width with
| Null -> 1
| This x -> x
;;
end
type constraints =
{ total_width : int
; min_widths : (String.Utf8.t * int) list
}
[@@deriving sexp_of]
exception Impossible_table_constraints of constraints [@@deriving sexp_of]
let compute ~max_width reqs =
let minimum = list_sum reqs ~f:Request.min in
if minimum > max_width
then
raise
(Impossible_table_constraints
{ total_width = max_width
; min_widths =
List.filter_map reqs ~f:(fun { label; min_width; _ } ->
match min_width with
| Null -> None
| This x -> Some (label, x))
});
let n_no_min = List.count reqs ~f:(fun { min_width; _ } -> Or_null.is_null min_width) in
let requested_mins =
list_sum reqs ~f:(fun { min_width; _ } -> Or_null.value ~default:0 min_width)
in
let remaining = max_width - requested_mins in
let per_col_min = if n_no_min > 0 then remaining / n_no_min else 0 in
let cols =
List.map reqs ~f:(fun { desired_width; min_width; _ } ->
match min_width with
| This min_width -> { Col.desired_width = max desired_width min_width; min_width }
| Null -> { Col.desired_width; min_width = per_col_min })
in
let desired = list_sum cols ~f:Col.desired_width in
let excess = desired - max_width in
reduce ~excess cols
;;