1(* ========================================================================== *)
2(* FILE          : tttTools.sml                                               *)
3(* DESCRIPTION   : Library of useful functions for TacticToe                  *)
4(* AUTHOR        : (c) Thibault Gauthier, University of Innsbruck             *)
5(* DATE          : 2017                                                       *)
6(* ========================================================================== *)
7
8structure tttTools :> tttTools =
9struct
10
11open HolKernel boolLib Abbrev Dep tttRedirect
12
13val ERR = mk_HOL_ERR "tttTools"
14
15type lbl_t = (string * real * goal * goal list)
16type fea_t = int list
17type feav_t = (lbl_t * fea_t)
18
19(* --------------------------------------------------------------------------
20   Global parameters
21   -------------------------------------------------------------------------- *)
22
23val ttt_tactic_time = ref 0.05
24val ttt_search_time = ref (Time.fromReal 15.0)
25
26(* --------------------------------------------------------------------------
27   Directories
28   -------------------------------------------------------------------------- *)
29
30val tactictoe_dir   = HOLDIR ^ "/src/tactictoe"
31
32val ttt_tacfea_dir       = tactictoe_dir ^ "/fea_tactic"
33val ttt_thmfea_dir  = tactictoe_dir ^ "/fea_theorem"
34val ttt_glfea_dir   = tactictoe_dir ^ "/fea_goallist"
35
36val ttt_code_dir    = tactictoe_dir ^ "/gen_code"
37val ttt_open_dir    = tactictoe_dir ^ "/gen_open"
38
39val ttt_search_dir  = tactictoe_dir ^ "/log_search"
40val ttt_record_dir  = tactictoe_dir ^ "/log_record"
41val ttt_buildheap_dir = tactictoe_dir ^ "/log_buildheap"
42val ttt_unfold_dir    = tactictoe_dir ^ "/log_unfold"
43val ttt_eproof_dir    = tactictoe_dir ^ "/proof_E"
44val ttt_proof_dir     = tactictoe_dir ^ "/proof_ttt"
45
46(* do not use this with parallelism *)
47fun hide_out f x =
48  hide_in_file (ttt_code_dir ^ "/" ^ current_theory () ^ "_hide_out") f x
49
50(* --------------------------------------------------------------------------
51   Commands
52   -------------------------------------------------------------------------- *)
53
54fun mkDir_err dir =
55  OS.FileSys.mkDir dir
56  handle Interrupt => raise Interrupt
57       | _         => ()
58
59fun rmDir_err dir =
60  OS.FileSys.rmDir dir
61  handle Interrupt => raise Interrupt
62       | _         => ()
63
64fun rmDir_rec dir = ignore (OS.Process.system ("rm -r " ^ dir))
65
66fun cleanDir_rec dir =
67  (
68  rmDir_rec dir;
69  mkDir_err dir
70  )
71
72fun all_files dir =
73  let
74    val stream = OS.FileSys.openDir dir
75    fun loop acc stream =
76      case OS.FileSys.readDir stream of
77        NONE => acc
78      | SOME s => loop (s :: acc) stream
79    val l = loop [] stream
80  in
81    OS.FileSys.closeDir stream;
82    l
83  end
84
85fun clean_dir dir =
86  let
87    val _ = mkDir_err dir
88    val l0 = all_files dir
89    val l1 = map (fn x => OS.Path.concat (dir,x)) l0
90  in
91    app OS.FileSys.remove l1
92  end
93
94fun run_cmd cmd = ignore (OS.Process.system cmd)
95
96(* TODO: Use OS to change dir? *)
97fun cmd_in_dir dir cmd = run_cmd ("cd " ^ dir ^ "; " ^ cmd)
98
99fun exists_file file = OS.FileSys.access (file, []);
100
101(* --------------------------------------------------------------------------
102    Dictionaries shortcuts
103   -------------------------------------------------------------------------- *)
104
105fun dfind k m  = Redblackmap.find (m,k)
106fun dfind_err msg x dict = dfind x dict handle _ => raise ERR "dfind" msg
107
108fun drem k m   = fst (Redblackmap.remove (m,k)) handle NotFound => m
109fun dmem k m   = Lib.can (dfind k) m
110fun dadd k v m = Redblackmap.insert (m,k,v)
111fun daddl kvl m = Redblackmap.insertList (m,kvl)
112val dempty     = Redblackmap.mkDict
113val dnew       = Redblackmap.fromList
114val dlist      = Redblackmap.listItems
115val dlength    = Redblackmap.numItems
116val dapp       = Redblackmap.app
117val dmap       = Redblackmap.map
118val dfoldl     = Redblackmap.foldl
119fun dkeys d    = map fst (dlist d)
120
121fun inv_dict cmp d = dnew cmp (map (fn (a,b) => (b,a)) (dlist d))
122
123(* --------------------------------------------------------------------------
124   References
125   -------------------------------------------------------------------------- *)
126
127fun incr x = x := (!x) + 1
128fun decr x = x := (!x) + 1
129
130(* --------------------------------------------------------------------------
131   Reserved tokens
132   -------------------------------------------------------------------------- *)
133
134val reserved_dict =
135  dnew String.compare
136  (map (fn x => (x,()))
137  ["op", "if", "then", "else", "val", "fun",
138   "structure", "signature", "struct", "sig", "open",
139   "infix", "infixl", "infixr", "andalso", "orelse",
140   "and", "datatype", "type", "where", ":", ";" , ":>",
141   "let", "in", "end", "while", "do",
142   "local","=>","case","of","_","|","fn","handle","raise","#",
143   "[","(",",",")","]","{","}","..."])
144
145fun is_string s = String.sub (s,0) = #"\"" handle _ => false
146fun is_number s = Char.isDigit (String.sub (s,0)) handle _ => false
147fun is_chardef s = String.substring (s,0,2) = "#\"" handle _ => false
148
149fun is_reserved s =
150  dmem s reserved_dict orelse
151  is_number s orelse is_string s orelse is_chardef s
152
153(* --------------------------------------------------------------------------
154   List
155   -------------------------------------------------------------------------- *)
156
157fun map_snd f l   = map (fn (a,b) => (a, f b)) l
158fun map_fst f l   = map (fn (a,b) => (f a, b)) l
159fun map_assoc f l = map (fn a => (a, f a)) l
160
161fun cartesian_product l1 l2 =
162  List.concat (map (fn x => map (fn y => (x,y)) l2) l1)
163
164fun findSome f l = case l of
165    [] => NONE
166  | a :: m =>
167    let val r = f a in
168      if isSome r then r else findSome f m
169    end
170
171fun first_n n l =
172  if n <= 0 orelse null l
173  then []
174  else hd l :: first_n (n - 1) (tl l)
175
176fun first_test_n test n l =
177  if n <= 0 orelse null l
178    then []
179  else if test (hd l)
180    then hd l :: first_test_n test (n - 1) (tl l)
181  else first_test_n test n (tl l)
182
183fun part_aux n acc l =
184  if n <= 0 orelse null l
185  then (rev acc,l)
186  else part_aux (n - 1) (hd l :: acc) (tl l)
187
188fun part_n n l = part_aux n [] l
189
190fun number_list start l = case l of
191    []      => []
192  | a :: m  => (start,a) :: number_list (start + 1) m
193
194fun mk_fast_set compare l =
195  let
196    val empty_dict = dempty compare
197    fun f (k,dict) = dadd k () dict
198  in
199    map fst (Redblackmap.listItems (foldl f empty_dict l))
200  end
201
202(* preserve the order of elements and take the first seen element as representant *)
203fun mk_sameorder_set_aux memdict rl l =
204  case l of
205    [] => rev rl
206  | a :: m => if dmem a memdict
207              then mk_sameorder_set_aux memdict rl m
208              else mk_sameorder_set_aux (dadd a () memdict) (a :: rl) m
209
210fun mk_sameorder_set compare l = mk_sameorder_set_aux (dempty compare) [] l
211
212(* Sort elements and preserve the order of equal elements *)
213fun dict_sort compare l =
214  let
215    val newl = number_list 0 l
216    fun newcompare ((n,x),(m,y)) =
217      case compare (x,y) of
218        EQUAL => Int.compare (n,m)
219      | LESS => LESS
220      | GREATER => GREATER
221  in
222    map snd (mk_fast_set newcompare newl)
223  end
224
225fun mk_string_set l = mk_fast_set String.compare l
226
227fun count_dict startdict l =
228  let
229    fun f (k,dict) =
230      let val old_n = dfind k dict handle _ => 0 in
231        dadd k (old_n + 1) dict
232      end
233  in
234    foldl f startdict l
235  end
236
237fun fold_left f l orig = case l of
238    [] => orig
239  | a :: m => let val new_orig = f a orig in fold_left f m new_orig end
240
241fun list_diff l1 l2 = filter (fn x => not (mem x l2)) l1
242
243fun topo_sort graph =
244  let val (topl,downl) = List.partition (fn (x,xl) => null xl) graph in
245    case (topl,downl) of
246    ([],[]) => []
247  | ([],_)  => raise ERR "topo_sort" "loop or missing nodes"
248  | _       =>
249    let
250      val topl' = List.map fst topl
251      val graph' = List.map (fn (x,xl) => (x,list_diff xl topl')) downl
252    in
253      topl' @ topo_sort graph'
254    end
255  end
256
257fun sort_thyl thyl = topo_sort (map (fn x => (x, ancestry x)) thyl)
258
259
260(* ---------------------------------------------------------------------------
261   Reals
262   -------------------------------------------------------------------------- *)
263
264fun sum_real l = case l of [] => 0.0 | a :: m => a + sum_real m
265
266fun list_rmax l = case l of
267    [] => raise ERR "list_max" ""
268  | [a] => a
269  | a :: m => Real.max (a,list_rmax m)
270
271fun list_imax l = case l of
272    [] => raise ERR "list_imax" ""
273  | [a] => a
274  | a :: m => Int.max (a,list_imax m)
275
276fun sum_int l = case l of [] => 0 | a :: m => a + sum_int m
277
278fun average_real l = sum_real l / Real.fromInt (length l)
279
280fun int_div n1 n2 =
281   (if n2 = 0 then 0.0 else Real.fromInt n1 / Real.fromInt n2)
282
283fun pow (x:real) (n:int) =
284  if n <= 0 then 1.0 else x * (pow x (n-1))
285
286fun approx n r =
287  let val mult = pow 10.0 n in
288    Real.fromInt (Real.round (r * mult)) / mult
289  end
290
291
292(* --------------------------------------------------------------------------
293   Terms
294   -------------------------------------------------------------------------- *)
295
296fun rename_bvarl f tm =
297  let
298    val vi = ref 0
299    fun rename_aux tm = case dest_term tm of
300      VAR(Name,Ty)       => tm
301    | CONST{Name,Thy,Ty} => tm
302    | COMB(Rator,Rand)   => mk_comb (rename_aux Rator, rename_aux Rand)
303    | LAMB(Var,Bod)      =>
304      let
305        val vs = f (fst (dest_var Var))
306        val new_tm = rename_bvar ("V" ^ int_to_string (!vi) ^ vs) tm
307        val (v,bod) = dest_abs new_tm
308        val _ = incr vi
309      in
310        mk_abs (v, rename_aux bod)
311      end
312  in
313    rename_aux tm
314  end
315
316fun all_bvar tm =
317  mk_fast_set Term.compare (map (fst o dest_abs) (find_terms is_abs tm))
318
319(* --------------------------------------------------------------------------
320   Goal
321   -------------------------------------------------------------------------- *)
322
323fun string_of_goal (asm,w) =
324  let
325    val mem = !show_types
326    val _   = show_types := false
327    val s   =
328      (if asm = []
329         then "[]"
330         else "[``" ^ String.concatWith "``,``" (map term_to_string asm) ^
331              "``]")
332    val s1 = "(" ^ s ^ "," ^ "``" ^ (term_to_string w) ^ "``)"
333  in
334    show_types := mem;
335    s1
336  end
337
338fun string_of_bool b = if b then "T" else "F"
339
340(* --------------------------------------------------------------------------
341   Comparisons
342   -------------------------------------------------------------------------- *)
343
344fun compare_imax ((_,r2),(_,r1)) = Int.compare (r1,r2)
345fun compare_imin ((_,r1),(_,r2)) = Int.compare (r1,r2)
346
347fun compare_rmax ((_,r2),(_,r1)) = Real.compare (r1,r2)
348fun compare_rmin ((_,r1),(_,r2)) = Real.compare (r1,r2)
349
350fun goal_compare ((asm1,w1), (asm2,w2)) =
351  list_compare Term.compare (w1 :: asm1, w2 :: asm2)
352
353fun cpl_compare cmp1 cmp2 ((a1,a2),(b1,b2)) =
354  let val r = cmp1 (a1,b1) in
355    if r = EQUAL then cmp2 (a2,b2) else r
356  end
357
358fun strict_term_compare (t1,t2) =
359  if Portable.pointer_eq (t1,t2) then EQUAL
360  else if is_var t1 andalso is_var t2 then Term.compare (t1,t2)
361  else if is_var t1 then LESS
362  else if is_var t2 then GREATER
363  else if is_const t1 andalso is_const t2 then Term.compare (t1,t2)
364  else if is_const t1 then LESS
365  else if is_const t2 then GREATER
366  else if is_comb t1 andalso is_comb t2 then
367    cpl_compare strict_term_compare
368      strict_term_compare (dest_comb t1, dest_comb t2)
369  else if is_comb t1 then LESS
370  else if is_comb t2 then GREATER
371  else
372    cpl_compare Term.compare strict_term_compare (dest_abs t1, dest_abs t2)
373
374fun strict_goal_compare ((asm1,w1), (asm2,w2)) =
375  list_compare strict_term_compare (w1 :: asm1, w2 :: asm2)
376
377fun lbl_compare ((stac1,_,g1,_),(stac2,_,g2,_)) =
378  cpl_compare String.compare goal_compare ((stac1,g1),(stac2,g2))
379
380fun feav_compare ((lbl1,_),(lbl2,_)) = lbl_compare (lbl1,lbl2)
381
382(* --------------------------------------------------------------------------
383   I/O
384   -------------------------------------------------------------------------- *)
385
386fun bare_readl path =
387  let
388    val file = TextIO.openIn path
389    fun loop file = case TextIO.inputLine file of
390        SOME line => line :: loop file
391      | NONE => []
392    val l = loop file
393  in
394    (TextIO.closeIn file; l)
395  end
396
397fun readl path =
398  let
399    val file = TextIO.openIn path
400    fun loop file = case TextIO.inputLine file of
401        SOME line => line :: loop file
402      | NONE => []
403    val l1 = loop file
404    fun rm_last_char s = String.substring (s,0,String.size s - 1)
405    fun is_empty s = s = ""
406    val l2 = map rm_last_char l1 (* removing end line *)
407    val l3 = filter (not o is_empty) l2
408  in
409    (TextIO.closeIn file; l3)
410  end
411
412fun readl_empty path =
413  let
414    val file = TextIO.openIn path
415    fun loop file = case TextIO.inputLine file of
416        SOME line => line :: loop file
417      | NONE => []
418    val l1 = loop file
419    fun rm_last_char s = String.substring (s,0,String.size s - 1)
420    val l2 = map rm_last_char l1 (* removing end line *)
421  in
422    (TextIO.closeIn file; l2)
423  end
424
425
426fun write_file file s =
427  let val oc = TextIO.openOut file in
428    TextIO.output (oc,s); TextIO.closeOut oc
429  end
430
431fun erase_file file = write_file file "" handle _ => ()
432
433fun writel file sl =
434  let val oc = TextIO.openOut file in
435    app (fn s => TextIO.output (oc, s ^ "\n")) sl;
436    TextIO.closeOut oc
437  end
438
439fun append_file file s =
440  let val oc = TextIO.openAppend file in
441    TextIO.output (oc,s); TextIO.closeOut oc
442  end
443
444fun append_endline file s = append_file file (s ^ "\n")
445
446(* --------------------------------------------------------------------------
447   Profiling
448   -------------------------------------------------------------------------- *)
449
450fun add_time f x =
451  let
452    val rt = Timer.startRealTimer ()
453    val r = f x
454    val time = Timer.checkRealTimer rt
455  in
456    (r, Time.toReal time)
457  end
458
459fun total_time total f x =
460  let val (r,t) = add_time f x in (total := (!total) + t; r) end
461
462fun print_time s r = print (s ^ ": " ^ Real.toString r ^ "\n")
463
464fun print_endline s = print (s ^ "\n")
465
466(* --------------------------------------------------------------------------
467   Debugging and exporting feature vectors
468   -------------------------------------------------------------------------- *)
469
470(* unfold_dir *)
471val ttt_unfold_cthy = ref "scratch"
472
473fun debug_unfold s =
474  append_endline (ttt_unfold_dir ^ "/" ^ !ttt_unfold_cthy) s
475
476(* search_dir *)
477fun debug s = append_endline (ttt_search_dir ^ "/debug/" ^ current_theory ()) s
478
479fun debug_err s = (debug s; raise ERR "debug_err" s)
480
481fun debug_t s f x =
482  let
483    val _ = debug s
484    val (r,t) = add_time f x
485    val _ = debug (s ^ " " ^ Real.toString t)
486  in
487    r
488  end
489
490val debugsearch_flag = ref false
491
492fun set_debugsearch b = debugsearch_flag := b
493
494fun debug_search s =
495  if !debugsearch_flag
496  then append_endline (ttt_search_dir ^ "/search/" ^ current_theory ()) s
497  else ()
498
499fun debug_proof s =
500  append_endline (ttt_proof_dir ^ "/" ^ current_theory ()) s
501
502fun debug_eproof s =
503  append_endline (ttt_eproof_dir ^ "/" ^ current_theory ()) s
504
505(* record_dir *)
506fun debug_parse s =
507  append_endline (ttt_record_dir ^ "/parse/" ^ current_theory ()) s
508
509fun debug_replay s =
510  append_endline (ttt_record_dir ^ "/replay/" ^ current_theory ()) s
511
512fun debug_record s =
513  append_endline (ttt_record_dir ^ "/record/" ^ current_theory ()) s
514
515(* --------------------------------------------------------------------------
516   Parsing
517   -------------------------------------------------------------------------- *)
518
519fun unquote_string s =
520  if String.sub (s,0) = #"\"" andalso String.sub (s,String.size s - 1) = #"\""
521  then String.substring (s, 1, String.size s - 2)
522  else raise ERR "unquote_string" s
523
524fun drop_sig s =
525  if last (explode s) = #"."
526  then s
527  else last (String.fields (fn x => x = #".") s)
528
529fun rm_last_n_string n s =
530  let
531    val l = explode s
532    val m = length l
533  in
534    implode (first_n (m - n) l)
535  end
536
537fun filename_of s = last (String.tokens (fn x => x = #"/") s)
538  handle _ => raise ERR "filename_of" s
539
540fun split_sl_aux s pl sl = case sl of
541    []     => raise ERR "split_sl_aux" ""
542  | a :: m => if a = s
543              then (rev pl, m)
544              else split_sl_aux s (a :: pl) m
545
546fun split_sl s sl = split_sl_aux s [] sl
547
548fun rpt_split_sl s sl =
549  let val (a,b) = split_sl s sl handle _ => (sl,[])
550  in
551    if null b then [a] else a :: rpt_split_sl s b
552  end
553
554
555fun split_level_aux i s pl sl = case sl of
556    []     => raise ERR "split_level_aux" s
557  | a :: m => if a = s andalso i <= 0
558                then (rev pl, m)
559              else if mem a ["let","local","struct","(","[","{"]
560                then split_level_aux (i + 1) s (a :: pl) m
561              else if mem a ["end",")","]","}"]
562                then split_level_aux (i - 1) s (a :: pl) m
563              else split_level_aux i s (a :: pl) m
564
565fun split_level s sl = split_level_aux 0 s [] sl
566
567fun rpt_split_level s sl =
568  let val (a,b) = split_level s sl handle _ => (sl,[])
569  in
570    if null b then [a] else a :: rpt_split_level s b
571  end
572
573fun split_charl acc buf csm l1 l2 =
574  if csm = [] then (rev acc, l2) else
575  case l2 of
576    []     => raise ERR "split_charl" ""
577  | a :: m => if hd csm = a
578              then split_charl acc (a :: buf) (tl csm) l1 m
579              else split_charl (a :: (buf @ acc)) [] l1 l1 m
580
581fun split_string s1 s2 =
582  let
583    val (l1,l2) = (explode s1, explode s2)
584    val (rl1,rl2) = split_charl [] [] l1 l1 l2
585  in
586    (implode rl1, implode rl2)
587  end
588  handle _ => raise ERR "split_string" (s1 ^ " " ^ s2)
589
590fun rm_prefix s2 s1 =
591  let val (a,b) = split_string s1 s2 in
592    if a = "" then b else raise ERR "rm_prefix" (s2 ^ " " ^ s1)
593  end
594
595fun rm_squote s =
596  if String.sub (s,0) = #"\"" andalso String.sub (s,String.size s - 1) = #"\""
597  then String.substring (s, 1, String.size s - 2)
598  else raise ERR "rm_squote" s
599
600fun rm_space_aux l = case l of
601    [] => []
602  | a :: m => if a = #" " then rm_space_aux m else l
603
604fun rm_space s = implode (rm_space_aux (explode s))
605
606(* --------------------------------------------------------------------------
607   Tactics
608   -------------------------------------------------------------------------- *)
609
610val ttt_tacerr      = ref []
611val ttt_tacfea      = ref (dempty lbl_compare)
612val ttt_tacfea_cthy = ref (dempty lbl_compare)
613val ttt_tacdep      = ref (dempty goal_compare)
614val ttt_taccov      = ref (dempty String.compare)
615
616(* --------------------------------------------------------------------------
617   Theorems
618   -------------------------------------------------------------------------- *)
619
620val ttt_thmfea = ref (dempty goal_compare)
621
622(* Warning: causes a problem if a theory is named namespace_tag *)
623val namespace_tag = "namespace_tag"
624
625fun dbfetch_of_string s =
626  let val (a,b) = split_string "Theory." s in
627    if a = current_theory ()
628      then String.concatWith " " ["DB.fetch",mlquote a,mlquote b]
629    else
630      if a = namespace_tag then b else s
631  end
632
633fun mk_metis_call sl =
634  "metisTools.METIS_TAC " ^
635  "[" ^ String.concatWith " , " (map dbfetch_of_string sl) ^ "]"
636
637(* --------------------------------------------------------------------------
638   Lists of goals
639   -------------------------------------------------------------------------- *)
640
641val ttt_glfea = ref (dempty (list_compare Int.compare))
642val ttt_glfea_cthy = ref (dempty (list_compare Int.compare))
643
644(* --------------------------------------------------------------------------
645   Cleaning tactictoe data (not necessary)
646   -------------------------------------------------------------------------- *)
647
648fun clean_tttdata () =
649  (
650  ttt_tacerr := [];
651  ttt_tacfea := dempty lbl_compare;
652  ttt_tacfea_cthy := dempty lbl_compare;
653  ttt_tacdep := dempty goal_compare;
654  ttt_taccov := dempty String.compare;
655  ttt_thmfea := dempty goal_compare;
656  ttt_glfea := dempty (list_compare Int.compare);
657  ttt_glfea_cthy := dempty (list_compare Int.compare)
658  )
659
660(*----------------------------------------------------------------------------
661   escaping (for ATPs than do not support single quotes)
662  ----------------------------------------------------------------------------*)
663
664fun escape_char c =
665  if Char.isAlphaNum c then Char.toString c
666  else if c = #"_" then "__"
667  else
668    let val hex = Int.fmt StringCvt.HEX (Char.ord c) in
669      StringCvt.padLeft #"_" 3 hex
670    end
671
672fun escape s = String.translate escape_char s;
673
674fun isCapitalHex c =
675  Char.ord #"A" <= Char.ord c andalso Char.ord c <= Char.ord #"F"
676
677fun charhex_to_int c =
678  if Char.isDigit c
679    then Char.ord c - Char.ord #"0"
680  else if isCapitalHex c
681    then Char.ord c - Char.ord #"A" + 10
682  else raise ERR "charhex_to_int" ""
683
684fun unescape_aux l = case l of
685   [] => []
686 | #"_" :: #"_" :: m => #"_" :: unescape_aux m
687 | #"_" :: a :: b :: m =>
688   Char.chr (16 * charhex_to_int a + charhex_to_int b) :: unescape_aux m
689 | a :: m => a :: unescape_aux m
690
691fun unescape s = implode (unescape_aux (explode s))
692
693(*----------------------------------------------------------------------------
694   Theorems
695  ----------------------------------------------------------------------------*)
696
697fun depnumber_of_thm thm =
698  (Dep.depnumber_of o Dep.depid_of o Tag.dep_of o Thm.tag) thm
699  handle HOL_ERR _ => raise ERR "depnumber_of_thm" ""
700
701fun depidl_of_thm thm =
702  (Dep.depidl_of o Tag.dep_of o Thm.tag) thm
703  handle HOL_ERR _ => raise ERR "depidl_of_thm" ""
704
705fun tid_of_did (thy,n) =
706  let fun has_depnumber n (_,thm) = n = depnumber_of_thm thm in
707    case List.find (has_depnumber n) (DB.thms thy) of
708      SOME (name,_) => SOME (thy ^ "Theory." ^ name)
709    | NONE => NONE
710  end
711
712fun exists_did did = isSome (tid_of_did did)
713
714fun depl_of_thm thm =
715  let
716    fun f x = x = NONE
717    val l = map tid_of_did (depidl_of_thm thm)
718    val l' = filter f l
719  in
720    (null l', mapfilter valOf l)
721  end
722
723fun deplPartial_of_sthm s =
724  let val (a,b) = split_string "Theory." s in
725    if a = namespace_tag
726    then []
727    else List.mapPartial tid_of_did (depidl_of_thm (DB.fetch a b))
728  end
729
730fun only_concl x =
731  let val (a,b) = dest_thm x in
732    if null a then b else raise ERR "only_concl" ""
733  end
734
735(*----------------------------------------------------------------------------
736   Parallelism
737  ----------------------------------------------------------------------------*)
738
739datatype 'a result = Res of 'a | Exn of exn;
740
741fun capture f x = Res (f x) handle e => Exn e
742
743fun release (Res y) = y
744  | release (Exn x) = raise x
745
746fun is_res (Res y) = true
747  | is_res (Exn x) = false
748
749fun is_exn (Res y) = false
750  | is_exn (Exn x) = true
751
752fun interruptkill worker =
753   if Thread.isActive worker
754   then
755     (
756     Thread.interrupt worker handle Thread _ => ();
757     if Thread.isActive worker
758       then Thread.kill worker
759       else ()
760     )
761   else ()
762
763fun compare_imin (a,b) = Int.compare (snd a, snd b)
764
765fun parmap_err ncores forg lorg =
766  let
767    (* input *)
768    val sizeorg = length lorg
769    val lin = List.tabulate (ncores,(fn x => (x, ref NONE)))
770    val din = dnew Int.compare lin
771    fun fi xi x = (x,xi)
772    val queue = ref (mapi fi lorg)
773    (* update process inputs *)
774    fun update_from_queue lineref =
775      if null (!queue) then ()
776      else (lineref := SOME (hd (!queue)); queue := tl (!queue))
777    fun is_refnone x = (not o isSome o ! o snd) x
778    fun dispatcher () =
779      app (update_from_queue o snd) (filter is_refnone lin)
780    (* output *)
781    val lout = List.tabulate (ncores,(fn x => (x, ref [])))
782    val dout = dnew Int.compare lout
783    val lcount = List.tabulate (ncores,(fn x => (x, ref 0)))
784    val dcount = dnew Int.compare lcount
785    (* process *)
786    fun process pi =
787      let val inref = dfind pi din in
788        case !inref of
789          NONE => process pi
790        | SOME (x,xi) =>
791          let
792            val oldl = dfind pi dout
793            val oldn = dfind pi dcount
794            val y = capture forg x
795          in
796            oldl := (y,xi) :: (!oldl);
797            incr oldn;
798            inref := NONE;
799            process pi
800          end
801      end
802    fun fork_on pi = Thread.fork (fn () => process pi, [])
803    val threadl = map fork_on (List.tabulate (ncores,I))
804    fun loop () =
805      (
806      dispatcher ();
807      if null (!queue) andalso sum_int (map (! o snd) lcount) >= sizeorg
808      then app interruptkill threadl
809      else loop ()
810      )
811  in
812    loop ();
813    map fst (dict_sort compare_imin (List.concat (map (! o snd) lout)))
814  end
815
816fun parmap ncores f l =
817  map release (parmap_err ncores f l)
818
819fun parapp ncores f l = ignore (parmap ncores f l)
820
821end (* struct *)
822