source: Deliverables/D5.1-5.3/cost-plug-in/plugin/cost_value.ml @ 1679

Last change on this file since 1679 was 1679, checked in by ayache, 9 years ago

Frama-C plug-in (sources+documentation)

File size: 20.8 KB
Line 
1
2(** This module describes the values manipulated by the plug-in. *)
3
4exception Unknown_cost of string
5exception Unknown_prototype of string
6
7
8let string_of_mset to_list sep f mset =
9  let filter (_, occ) = occ <> 0 in
10  let f' (elt, occ) =
11    if occ = 1 then (f elt)
12    else Printf.sprintf "%d*%s" occ (f elt) in
13  Misc.List.to_string sep f' (List.filter filter (to_list mset))
14
15type prototypes = string list Misc.String.Map.t
16
17
18module type S = sig
19
20  type relation
21  val is_large : relation -> bool
22  val has_lower_type : relation -> bool
23
24  type t
25
26  val top : t
27  val of_int : int -> t
28  val of_var : string -> t
29  val add    : t -> t -> t
30  val minus  : t -> t -> t
31  val mul    : t -> t -> t
32  val div    : t -> t -> t
33  val max    : t -> t -> t
34  val cond   : t -> relation -> t -> t -> t -> t
35  val join   : t -> t -> t
36  val widen  : t -> t -> t
37  val narrow : t -> t -> t
38
39  val le : t -> t -> bool
40
41  val replace_vars : t Misc.String.Map.t -> t -> t
42
43  val to_string : t -> string
44  val string_of_relation : relation -> string
45
46  val compare : t -> t -> int
47
48end
49
50
51module Make (S : S) = struct
52
53  let s_add_list = function
54    | [] -> S.of_int 0
55    | e :: l -> List.fold_left S.add e l
56
57  module Args = struct
58
59    type t = S.t list
60
61    let compare = Misc.List.compare S.compare
62
63    let le args1 args2 =
64      if List.length args1 <> List.length args2 then false
65      else
66        let f res arg1 arg2 = res && (S.le arg1 arg2) in
67        List.fold_left2 f true args1 args2
68
69    let replace_vars replacements = List.map (S.replace_vars replacements)
70
71    let to_string = Misc.List.to_string ", " S.to_string
72
73  end
74
75
76  module Externs = struct
77
78    module M = Misc.String.MSet
79    include M
80
81    let add = union
82
83    let le = subset
84
85    let replace_vars _ externs = externs
86
87    let to_string = string_of_mset to_list " + " (fun x -> x)
88
89    let to_ext externs =
90      let f x occ ext = S.add (S.mul (S.of_int occ) (S.of_var x)) ext in
91      fold f externs (S.of_int 0)
92
93  end
94
95
96  module FunCall = struct
97
98    type t =
99        { caller : string ;
100          id : int ;
101          callee : string ;
102          args : Args.t }
103    let compare = Pervasives.compare
104
105    let caller fun_call = fun_call.caller
106    let id fun_call = fun_call.id
107    let callee fun_call = fun_call.callee
108    let args fun_call = fun_call.args
109
110    let make caller id callee args = { caller ; id ; callee ; args }
111
112    let apply f f_caller f_id f_callee f_args fun_call =
113      let caller_res = f_caller (caller fun_call) in
114      let id_res = f_id (id fun_call) in
115      let callee_res = f_callee (callee fun_call) in
116      let args_res = f_args (args fun_call) in
117      f caller_res id_res callee_res args_res
118
119    let apply2 f f_caller f_id f_callee f_args fun_call1 fun_call2 =
120      let caller_res = f_caller (caller fun_call1) (caller fun_call2) in
121      let id_res = f_id (id fun_call1) (id fun_call2) in
122      let callee_res = f_callee (callee fun_call1) (callee fun_call2) in
123      let args_res = f_args (args fun_call1) (args fun_call2) in
124      f caller_res id_res callee_res args_res
125
126    let le =
127      let f b1 b2 b3 b4 = b1 && b2 && b3 && b4 in
128      apply2 f (=) (=) (=) Args.le
129
130    let replace_vars replacement =
131      apply make Misc.id Misc.id Misc.id (Args.replace_vars replacement)
132
133    let reduce
134        to_list is_solved replace_vars of_fun_call
135        prototypes costs fun_call =
136      let callee = callee fun_call in
137      let args = args fun_call in
138      if Misc.String.Map.mem callee prototypes then
139        let formals = Misc.String.Map.find callee prototypes in
140        if List.length formals = List.length args then
141          let replacements =
142            Misc.String.Map.of_list (List.combine formals args) in
143          if Misc.String.Map.mem callee costs then
144            let cost' = Misc.String.Map.find callee costs in
145            if is_solved cost' then to_list (replace_vars replacements cost')
146            else [of_fun_call fun_call]
147          else raise (Unknown_cost callee)
148        else
149          raise
150            (Failure ("FunCall.reduce: formals and actuals for " ^
151                         "function " ^ callee ^ " have different sizes."))
152      else raise (Unknown_prototype callee)
153
154    let to_string fun_call =
155      Printf.sprintf "%s@[%s,%d](%s)"
156        (callee fun_call) (caller fun_call) (id fun_call)
157        (Args.to_string (args fun_call))
158
159  end
160
161  module FunCalls = struct
162
163    module M = Multiset.Make (FunCall)
164    include M
165
166    let singleton_ fun_call = M.add fun_call empty
167
168    let singleton caller id callee args =
169      singleton (FunCall.make caller id callee args)
170
171    let add = union
172
173    let le1 fun_call occ fun_calls =
174      let f fun_call' occ' = (FunCall.le fun_call fun_call') && (occ <= occ') in
175      exists f fun_calls
176
177    let le fun_calls1 fun_calls2 =
178      let f fun_call occ = le1 fun_call occ fun_calls2 in
179      for_all f fun_calls1
180
181    let called_funs fun_calls =
182      let f fun_call _ called_funs =
183        Misc.String.Set.add (FunCall.callee fun_call) called_funs in
184      fold f fun_calls Misc.String.Set.empty
185
186    let replace_vars replacements fun_calls =
187      let f fun_call _ fun_calls =
188        let fun_call = FunCall.replace_vars replacements fun_call in
189        add (singleton_ fun_call) fun_calls in
190      fold f fun_calls empty
191
192    let to_string = string_of_mset to_list " + " FunCall.to_string
193
194  end
195
196
197  module rec LoopCost : sig
198    type t
199    val compare : t -> t -> int
200    val make : string -> int -> S.relation -> S.t -> S.t -> S.t -> Cost.t -> t
201    val le : t -> t -> bool
202    val called_funs : t -> Misc.String.Set.t
203    val replace_vars : S.t Misc.String.Map.t -> t -> t
204    val reduce : prototypes -> Cost.t Misc.String.Map.t -> t -> t
205    val to_string : t -> string
206    val to_ext : t -> S.t
207  end = struct
208
209    type t =
210        { fun_name : string ;
211          id : int ;
212          relation : S.relation ;
213          init_value : S.t ;
214          exit_value : S.t ;
215          increment : S.t ;
216          body_cost : Cost.t }
217
218    let make fun_name id relation init_value exit_value increment body_cost =
219      { fun_name ; id ; relation ; init_value ; exit_value ; increment ;
220        body_cost }
221
222    let fun_name loop_cost = loop_cost.fun_name
223    let id loop_cost = loop_cost.id
224    let relation loop_cost = loop_cost.relation
225    let init_value loop_cost = loop_cost.init_value
226    let exit_value loop_cost = loop_cost.exit_value
227    let increment loop_cost = loop_cost.increment
228    let body_cost loop_cost = loop_cost.body_cost
229
230    let compare = Pervasives.compare
231
232    let apply f
233        f_fun_name f_id f_relation f_init_value f_exit_value f_increment
234        f_body_cost loop_cost =
235      let fun_name_res = f_fun_name (fun_name loop_cost) in
236      let id_res = f_id (id loop_cost) in
237      let relation_res = f_relation (relation loop_cost) in
238      let init_value_res = f_init_value (init_value loop_cost) in
239      let exit_value_res = f_exit_value (exit_value loop_cost) in
240      let increment_res = f_increment (increment loop_cost) in
241      let body_cost_res = f_body_cost (body_cost loop_cost) in
242      f
243        fun_name_res id_res relation_res init_value_res exit_value_res
244        increment_res body_cost_res
245
246    let apply2 f
247        f_fun_name f_id f_relation f_init_value f_exit_value f_increment
248        f_body_cost loop_cost1 loop_cost2 =
249      let fun_name_res =
250        f_fun_name (fun_name loop_cost1) (fun_name loop_cost2) in
251      let id_res = f_id (id loop_cost1) (id loop_cost2) in
252      let relation_res =
253        f_relation (relation loop_cost1) (relation loop_cost2) in
254      let init_value_res =
255        f_init_value (init_value loop_cost1) (init_value loop_cost2) in
256      let exit_value_res =
257        f_exit_value (exit_value loop_cost1) (exit_value loop_cost2) in
258      let increment_res =
259        f_increment (increment loop_cost1) (increment loop_cost2) in
260      let body_cost_res =
261        f_body_cost (body_cost loop_cost1) (body_cost loop_cost2) in
262      f
263        fun_name_res id_res relation_res init_value_res exit_value_res
264        increment_res body_cost_res
265
266    let le =
267      let f b1 b2 b3 b4 b5 b6 b7 = b1 && b2 && b3 && b4 && b5 && b6 && b7 in
268      apply2 f (=) (=) (=) S.le S.le S.le Cost.le
269
270    let called_funs loop_cost = Cost.called_funs (body_cost loop_cost)
271
272    let replace_vars replacements =
273      let arg_replace_vars = S.replace_vars replacements in
274      apply make Misc.id Misc.id Misc.id arg_replace_vars arg_replace_vars
275        arg_replace_vars (Cost.replace_vars replacements)
276
277    let reduce prototypes costs =
278      apply make Misc.id Misc.id Misc.id Misc.id Misc.id Misc.id
279        (Cost.reduce prototypes costs)
280
281    let to_string loop_cost =
282      Printf.sprintf "%s@%d(%s %s %s %s (%s))"
283        (fun_name loop_cost)
284        (id loop_cost)
285        (S.string_of_relation (relation loop_cost))
286        (S.to_string (init_value loop_cost))
287        (S.to_string (exit_value loop_cost))
288        (S.to_string (increment loop_cost))
289        (Cost.to_string (body_cost loop_cost))
290
291    let to_ext loop_cost =
292      let rel = relation loop_cost in
293      let init_value = init_value loop_cost in
294      let exit_value = exit_value loop_cost in
295      let increment = increment loop_cost in
296      let body_cost = body_cost loop_cost in
297      let rel_op = if S.has_lower_type rel then S.minus else S.add in
298      let rel_added = S.of_int (if S.is_large rel then 0 else 1) in
299      let iteration_nb = rel_op increment rel_added in
300      let iteration_nb = S.minus iteration_nb init_value in
301      let iteration_nb = S.add exit_value iteration_nb in
302      let iteration_nb = S.div iteration_nb increment in
303      let body_cost = Cost.to_ext body_cost in
304      let cond_body_cost =
305        S.cond init_value rel exit_value body_cost (S.of_int 0) in
306      S.mul iteration_nb cond_body_cost
307
308  end
309
310
311  and LoopCosts : sig
312    type t
313    val empty : t
314    val singleton :
315      string -> int -> S.relation -> S.t -> S.t -> S.t -> Cost.t -> t
316    val add : t -> t -> t
317    val replace_vars : S.t Misc.String.Map.t -> t -> t
318    val called_funs : t -> Misc.String.Set.t
319    val reduce : prototypes -> Cost.t Misc.String.Map.t -> t -> t
320    val to_string : t -> string
321    val le : t -> t -> bool
322    val to_ext : t -> S.t
323  end = struct
324
325    module M = Multiset.Make (LoopCost)
326    include M
327
328    let singleton_ loop_cost = M.add loop_cost empty
329
330    let singleton
331        fun_name id relation init_value exit_value increment body_cost =
332      let loop_cost =
333        LoopCost.make
334          fun_name id relation init_value exit_value increment body_cost in
335      singleton_ loop_cost
336
337    let add = union
338
339    let le1 loop_cost occ loop_costs =
340      let f loop_cost' occ' res =
341        res || ((LoopCost.le loop_cost loop_cost') && (occ <= occ')) in
342      fold f loop_costs false
343
344    let le loop_costs1 loop_costs2 =
345      let f loop_cost occ res = res && le1 loop_cost occ loop_costs2 in
346      fold f loop_costs1 true
347
348    let called_funs loop_costs =
349      let f loop_cost _ called_funs =
350        Misc.String.Set.union (LoopCost.called_funs loop_cost) called_funs in
351      fold f loop_costs Misc.String.Set.empty
352
353    let replace_vars replacements loop_costs =
354      let f loop_cost _ loop_costs =
355        let loop_cost = LoopCost.replace_vars replacements loop_cost in
356        add (singleton_ loop_cost) loop_costs in
357      fold f loop_costs empty
358
359    let reduce prototypes replacements loop_costs =
360      let f loop_cost occ loop_costs =
361        add_occ (LoopCost.reduce prototypes replacements loop_cost) occ
362          loop_costs in
363      fold f loop_costs empty
364
365    let to_string = string_of_mset to_list " + " LoopCost.to_string
366
367    let to_ext loop_costs =
368      let f loop_cost occ ext =
369        S.add (S.mul (S.of_int occ) (LoopCost.to_ext loop_cost)) ext in
370      fold f loop_costs (S.of_int 0)
371
372  end
373
374
375  and Base : sig
376    type t
377    val compare : t -> t -> int
378    val of_int : int -> t
379    val of_extern : string -> t
380    val of_fun_call_ : FunCall.t -> t
381    val of_fun_call : string -> int -> string -> Args.t -> t
382    val of_loop_cost :
383      string -> int -> S.relation -> S.t -> S.t -> S.t -> Cost.t -> t
384    val add : t -> t -> t
385    val called_funs : t -> Misc.String.Set.t
386    val replace_vars : S.t Misc.String.Map.t -> t -> t
387    val reduce : prototypes -> Cost.t Misc.String.Map.t -> t -> Base.t list
388    val le : t -> t -> bool
389    val to_string : t -> string
390    val to_ext : t -> S.t
391  end = struct
392
393    type t =
394        { constant : int ;
395          externs : Externs.t ;
396          fun_calls : FunCalls.t ;
397          loop_costs : LoopCosts.t }
398
399    let make constant externs fun_calls loop_costs =
400      { constant ; externs ; fun_calls ; loop_costs }
401
402    let compare = Pervasives.compare
403
404    let constant base = base.constant
405    let externs base = base.externs
406    let fun_calls base = base.fun_calls
407    let loop_costs base = base.loop_costs
408
409    let set_fun_calls fun_calls base = { base with fun_calls }
410    let set_loop_costs loop_costs base = { base with loop_costs }
411
412    let to_string base =
413      Printf.sprintf "%d + (%s) + (%s) + (%s)"
414        (constant base)
415        (Externs.to_string (externs base))
416        (FunCalls.to_string (fun_calls base))
417        (LoopCosts.to_string (loop_costs base))
418
419    let of_int i =  make i Externs.empty FunCalls.empty LoopCosts.empty
420
421    let of_extern x =
422      make 0 (Externs.singleton x) FunCalls.empty LoopCosts.empty
423
424    let of_fun_call_ fun_call =
425      make 0 Externs.empty (FunCalls.singleton_ fun_call) LoopCosts.empty
426
427    let of_fun_call caller id callee args =
428      let fun_call = FunCall.make caller id callee args in
429      of_fun_call_ fun_call
430
431    let of_loop_cost
432        fun_name id relation init_value exit_value increment body_cost =
433      let loop_costs =
434        LoopCosts.singleton
435          fun_name id relation init_value exit_value increment body_cost in
436      make 0 Externs.empty FunCalls.empty loop_costs
437
438    let apply f f_constant f_externs f_fun_calls f_loop_costs base =
439      let constant_res = f_constant (constant base) in
440      let externs_res = f_externs (externs base) in
441      let fun_calls_res = f_fun_calls (fun_calls base) in
442      let loop_costs_res = f_loop_costs (loop_costs base) in
443      f constant_res externs_res fun_calls_res loop_costs_res
444
445    let apply2 f f_constant f_externs f_fun_calls f_loop_costs base1 base2 =
446      let constant_res = f_constant (constant base1) (constant base2) in
447      let externs_res = f_externs (externs base1) (externs base2) in
448      let fun_calls_res = f_fun_calls (fun_calls base1) (fun_calls base2) in
449      let loop_costs_res = f_loop_costs (loop_costs base1) (loop_costs base2) in
450      f constant_res externs_res fun_calls_res loop_costs_res
451
452    let add = apply2 make (+) Externs.add FunCalls.add LoopCosts.add
453
454    let le =
455      let f b1 b2 b3 b4 = b1 && b2 && b3 && b4 in
456      apply2 f (<=) Externs.le FunCalls.le LoopCosts.le
457
458    let replace_vars replacements =
459      apply make Misc.id
460        (Externs.replace_vars replacements)
461        (FunCalls.replace_vars replacements)
462        (LoopCosts.replace_vars replacements)
463
464    let called_funs base =
465      Misc.String.Set.union
466        (FunCalls.called_funs (fun_calls base))
467        (LoopCosts.called_funs (loop_costs base))
468
469    let reduce prototypes costs base =
470      let f fun_call occ base_list =
471        let added_bases =
472          FunCall.reduce
473            Cost.to_list Cost.is_solved Cost.replace_vars of_fun_call_
474            prototypes costs fun_call in
475        let added_bases =
476          if added_bases = [] then [of_int 0] else added_bases in
477        let added_bases =
478          let f base = Misc.repeat occ (add base) (of_int 0) in
479          List.map f added_bases in
480        let f_base_list res added_base =
481          res @ (List.map (add added_base) base_list) in
482        List.fold_left f_base_list [] added_bases in
483      let loop_costs = LoopCosts.reduce prototypes costs (loop_costs base) in
484      let base = set_loop_costs loop_costs base in
485      let base' = set_fun_calls FunCalls.empty base in
486      FunCalls.fold f (fun_calls base) [base']
487
488    let to_ext base =
489      let f_fun_calls fun_calls =
490        if not (FunCalls.is_empty fun_calls) then
491          raise (Failure "Base.to_ext: function calls")
492        else S.of_int 0 in
493      let f ext1 ext2 ext3 ext4 = s_add_list [ext1 ; ext2 ; ext3 ; ext4] in
494      apply f S.of_int Externs.to_ext f_fun_calls LoopCosts.to_ext base
495
496  end
497
498
499  and Cost : sig
500    type t
501    val of_int : int -> t
502    val of_extern : string -> t
503    val of_fun_call : string -> int -> string -> Args.t -> t
504    val of_loop_cost :
505      string -> int -> S.relation -> S.t -> S.t -> S.t -> Cost.t -> t
506    val of_base : Base.t -> t
507    val empty : t
508    val add : t -> t -> t
509    val join : t -> t -> t
510    val widen : t -> t -> t
511    val narrow : t -> t -> t
512    val called_funs : t -> Misc.String.Set.t
513    val has_fun_calls : t -> bool
514    val replace_vars : S.t Misc.String.Map.t -> t -> t
515    val reduce : prototypes -> Cost.t Misc.String.Map.t -> t -> t
516    val is_solved : t -> bool
517    val to_list : t -> Base.t list
518    val to_string : t -> string
519    val le : t -> t -> bool
520    val to_ext : t -> S.t
521  end = struct
522
523    module M = Eset.Make (Base)
524    include M
525
526    let to_string cost =
527      if is_empty cost then "0"
528      else Misc.List.to_string " max " Base.to_string (to_list cost)
529
530    let of_base base = singleton base
531
532    let of_int i = of_base (Base.of_int i)
533
534    let of_extern x = of_base (Base.of_extern x)
535
536    let of_fun_call caller id callee args =
537      of_base (Base.of_fun_call caller id callee args)
538
539    let of_loop_cost
540        fun_name loop_id relation init_value exit_value increment body_cost =
541      of_base
542        (Base.of_loop_cost
543           fun_name loop_id relation init_value exit_value increment body_cost)
544
545    let join1 base cost =
546      let f_exists base' = Base.le base base' in
547      if exists f_exists cost then cost
548      else
549        let f_absorb base' = Base.le base' base in
550        M.add base (M.diff cost (M.filter f_absorb cost))
551
552    let add cost1 cost2 =
553      if is_empty cost1 then cost2
554      else
555        if is_empty cost2 then cost1
556        else
557          let f2 base1 base2 = join1 (Base.add base1 base2) in
558          let f1 base1 = fold (f2 base1) cost2 in
559          fold f1 cost1 empty
560
561    let join cost1 cost2 =
562      if is_empty cost1 then cost2
563      else
564        if is_empty cost2 then cost1
565        else fold join1 cost1 cost2
566
567    let widen = join
568
569    let narrow = join (* TODO: improve *)
570
571    let mem base cost =
572      let f base' res = res || (Base.le base base') in
573      fold f cost false
574
575    let le cost1 cost2 =
576      let f base res = res && (mem base cost2) in
577      fold f cost1 true
578
579
580    let called_funs cost =
581      let f base called_funs =
582        Misc.String.Set.union (Base.called_funs base) called_funs in
583      fold f cost Misc.String.Set.empty
584
585    let has_fun_calls cost = not (Misc.String.Set.is_empty (called_funs cost))
586
587    let replace_vars replacements cost =
588      let f base cost = join1 (Base.replace_vars replacements base) cost in
589      fold f cost empty
590
591    let reduce prototypes costs cost =
592      let f base cost =
593        let base_list = Base.reduce prototypes costs base in
594        let f_join cost base = join1 base cost in
595        List.fold_left f_join cost base_list in
596      fold f cost empty
597
598    let is_solved cost = not (has_fun_calls cost)
599
600    let to_ext cost =
601      if is_empty cost then S.of_int 0
602      else
603        let f base ext = S.max (Base.to_ext base) ext in
604        let base = choose cost in
605        let cost = remove base cost in
606        fold f cost (Base.to_ext base)
607
608  end
609
610
611  type t = Top | C of Cost.t
612
613
614  let to_string = function
615    | Top -> "top"
616    | C cost -> Cost.to_string cost
617
618
619  let of_int i = C (Cost.of_int i)
620
621  let of_extern fun_name = C (Cost.of_extern fun_name)
622
623  let of_fun_call caller id callee args =
624    C (Cost.of_fun_call caller id callee args)
625
626  let of_loop_cost
627      fun_name loop_id relation init_value exit_value increment body_cost =
628    C (Cost.of_loop_cost
629         fun_name loop_id relation init_value exit_value increment body_cost)
630
631
632  let is_top = function Top -> true | _ -> false
633
634  let extract = function
635    | Top -> raise (Failure "Cost_value.extract")
636    | C cost -> cost
637
638  let top = Top
639
640  let bot = of_int 0
641
642
643  let top_absorbs f = function
644    | Top -> Top
645    | C cost -> C (f cost)
646
647  let top_absorbs2 f cost1 cost2 = match cost1, cost2 with
648    | Top, _ | _, Top -> Top
649    | C cost1, C cost2 -> C (f cost1 cost2)
650
651
652  let add = top_absorbs2 Cost.add
653
654  let join = top_absorbs2 Cost.join
655
656  let widen = top_absorbs2 Cost.widen
657
658  let narrow cost1 cost2 = match cost1, cost2 with
659    | cost, Top | Top, cost -> cost
660    | C cost1, C cost2 -> C (Cost.narrow cost1 cost2)
661
662  let le cost1 cost2 = match cost1, cost2 with
663    | _, Top -> true
664    | Top, _ -> false
665    | C cost1, C cost2 -> Cost.le cost1 cost2
666
667
668  let reduce_ prototypes costs cost =
669    let called_funs = Cost.called_funs cost in
670    let costs =
671      let f fun_name _ = Misc.String.Set.mem fun_name called_funs in
672      Misc.String.Map.filter f costs in
673    let f fun_name cost costs = match cost, costs with
674      | _, None -> None
675      | Top, _ -> None
676      | C cost, Some costs -> Some (Misc.String.Map.add fun_name cost costs) in
677    match Misc.String.Map.fold f costs (Some Misc.String.Map.empty) with
678      | None -> Top
679      | Some costs -> C (Cost.reduce prototypes costs cost)
680
681  let reduce prototypes costs = function
682    | Top -> Top
683    | C cost -> reduce_ prototypes costs cost
684
685  let reduces prototypes costs =
686    Misc.String.Map.map (reduce prototypes costs) costs
687
688
689  let replace_vars replacements = top_absorbs (Cost.replace_vars replacements)
690
691
692  let has_fun_calls = function
693    | Top -> false
694    | C cost -> Cost.has_fun_calls cost
695
696
697  let is_concrete cost = (not (is_top cost)) && (not (has_fun_calls cost))
698
699  let to_ext = function
700    | Top -> S.top
701    | C cost -> Cost.to_ext cost
702
703
704end
Note: See TracBrowser for help on using the repository browser.