src/HOL/Tools/Sledgehammer/sledgehammer_proof_reconstruct.ML
author blanchet
Tue, 17 Aug 2010 14:37:16 +0200
changeset 38713 3abda3c76df9
parent 38506 319c59682c51
child 38715 57de0f12516f
permissions -rw-r--r--
handle E's Skolem constants more gracefully
     1 (*  Title:      HOL/Tools/Sledgehammer/sledgehammer_proof_reconstruct.ML
     2     Author:     Lawrence C. Paulson, Cambridge University Computer Laboratory
     3     Author:     Claire Quigley, Cambridge University Computer Laboratory
     4     Author:     Jasmin Blanchette, TU Muenchen
     5 
     6 Transfer of proofs from external provers.
     7 *)
     8 
     9 signature SLEDGEHAMMER_PROOF_RECONSTRUCT =
    10 sig
    11   type minimize_command = string list -> string
    12 
    13   val metis_line: bool -> int -> int -> string list -> string
    14   val metis_proof_text:
    15     bool * minimize_command * string * string vector * thm * int
    16     -> string * string list
    17   val isar_proof_text:
    18     string Symtab.table * bool * int * Proof.context * int list list
    19     -> bool * minimize_command * string * string vector * thm * int
    20     -> string * string list
    21   val proof_text:
    22     bool -> string Symtab.table * bool * int * Proof.context * int list list
    23     -> bool * minimize_command * string * string vector * thm * int
    24     -> string * string list
    25 end;
    26 
    27 structure Sledgehammer_Proof_Reconstruct : SLEDGEHAMMER_PROOF_RECONSTRUCT =
    28 struct
    29 
    30 open ATP_Problem
    31 open Metis_Clauses
    32 open Sledgehammer_Util
    33 open Sledgehammer_Fact_Filter
    34 open Sledgehammer_Translate
    35 
    36 type minimize_command = string list -> string
    37 
    38 (* Simple simplifications to ensure that sort annotations don't leave a trail of
    39    spurious "True"s. *)
    40 fun s_not @{const False} = @{const True}
    41   | s_not @{const True} = @{const False}
    42   | s_not (@{const Not} $ t) = t
    43   | s_not t = @{const Not} $ t
    44 fun s_conj (@{const True}, t2) = t2
    45   | s_conj (t1, @{const True}) = t1
    46   | s_conj p = HOLogic.mk_conj p
    47 fun s_disj (@{const False}, t2) = t2
    48   | s_disj (t1, @{const False}) = t1
    49   | s_disj p = HOLogic.mk_disj p
    50 fun s_imp (@{const True}, t2) = t2
    51   | s_imp (t1, @{const False}) = s_not t1
    52   | s_imp p = HOLogic.mk_imp p
    53 fun s_iff (@{const True}, t2) = t2
    54   | s_iff (t1, @{const True}) = t1
    55   | s_iff (t1, t2) = HOLogic.eq_const HOLogic.boolT $ t1 $ t2
    56 
    57 fun mk_anot (AConn (ANot, [phi])) = phi
    58   | mk_anot phi = AConn (ANot, [phi])
    59 fun mk_aconn c (phi1, phi2) = AConn (c, [phi1, phi2])
    60 
    61 fun index_in_shape x = find_index (exists (curry (op =) x))
    62 fun is_axiom_number axiom_names num =
    63   num > 0 andalso num <= Vector.length axiom_names andalso
    64   Vector.sub (axiom_names, num - 1) <> ""
    65 fun is_conjecture_number conjecture_shape num =
    66   index_in_shape num conjecture_shape >= 0
    67 
    68 fun negate_term (Const (@{const_name All}, T) $ Abs (s, T', t')) =
    69     Const (@{const_name Ex}, T) $ Abs (s, T', negate_term t')
    70   | negate_term (Const (@{const_name Ex}, T) $ Abs (s, T', t')) =
    71     Const (@{const_name All}, T) $ Abs (s, T', negate_term t')
    72   | negate_term (@{const "op -->"} $ t1 $ t2) =
    73     @{const "op &"} $ t1 $ negate_term t2
    74   | negate_term (@{const "op &"} $ t1 $ t2) =
    75     @{const "op |"} $ negate_term t1 $ negate_term t2
    76   | negate_term (@{const "op |"} $ t1 $ t2) =
    77     @{const "op &"} $ negate_term t1 $ negate_term t2
    78   | negate_term (@{const Not} $ t) = t
    79   | negate_term t = @{const Not} $ t
    80 
    81 datatype ('a, 'b, 'c, 'd, 'e) raw_step =
    82   Definition of 'a * 'b * 'c |
    83   Inference of 'a * 'd * 'e list
    84 
    85 fun raw_step_number (Definition (num, _, _)) = num
    86   | raw_step_number (Inference (num, _, _)) = num
    87 
    88 (**** PARSING OF TSTP FORMAT ****)
    89 
    90 (*Strings enclosed in single quotes, e.g. filenames*)
    91 val scan_quoted = $$ "'" |-- Scan.repeat (~$$ "'") --| $$ "'" >> implode;
    92 
    93 val scan_dollar_name =
    94   Scan.repeat ($$ "$") -- Symbol.scan_id >> (fn (ss, s) => implode ss ^ s)
    95 
    96 fun repair_name _ "$true" = "c_True"
    97   | repair_name _ "$false" = "c_False"
    98   | repair_name _ "$$e" = "c_equal" (* seen in Vampire proofs *)
    99   | repair_name _ "equal" = "c_equal" (* needed by SPASS? *)
   100   | repair_name pool s =
   101     case Symtab.lookup pool s of
   102       SOME s' => s'
   103     | NONE =>
   104       if String.isPrefix "sQ" s andalso String.isSuffix "_eqProxy" s then
   105         "c_equal" (* seen in Vampire proofs *)
   106       else
   107         s
   108 (* Generalized first-order terms, which include file names, numbers, etc. *)
   109 val parse_potential_integer =
   110   (scan_dollar_name || scan_quoted) >> K NONE
   111   || scan_integer >> SOME
   112 fun parse_annotation x =
   113   ((parse_potential_integer ::: Scan.repeat ($$ " " |-- parse_potential_integer)
   114     >> map_filter I) -- Scan.optional parse_annotation []
   115      >> uncurry (union (op =))
   116    || $$ "(" |-- parse_annotations --| $$ ")"
   117    || $$ "[" |-- parse_annotations --| $$ "]") x
   118 and parse_annotations x =
   119   (Scan.optional (parse_annotation
   120                   ::: Scan.repeat ($$ "," |-- parse_annotation)) []
   121    >> (fn numss => fold (union (op =)) numss [])) x
   122 
   123 (* Vampire proof lines sometimes contain needless information such as "(0:3)",
   124    which can be hard to disambiguate from function application in an LL(1)
   125    parser. As a workaround, we extend the TPTP term syntax with such detritus
   126    and ignore it. *)
   127 fun parse_vampire_detritus x =
   128   (scan_integer |-- $$ ":" --| scan_integer >> K []) x
   129 
   130 fun parse_term pool x =
   131   ((scan_dollar_name >> repair_name pool)
   132     -- Scan.optional ($$ "(" |-- (parse_vampire_detritus || parse_terms pool)
   133                       --| $$ ")") []
   134     --| Scan.optional ($$ "(" |-- parse_vampire_detritus --| $$ ")") []
   135    >> ATerm) x
   136 and parse_terms pool x =
   137   (parse_term pool ::: Scan.repeat ($$ "," |-- parse_term pool)) x
   138 
   139 fun parse_atom pool =
   140   parse_term pool -- Scan.option (Scan.option ($$ "!") --| $$ "="
   141                                   -- parse_term pool)
   142   >> (fn (u1, NONE) => AAtom u1
   143        | (u1, SOME (NONE, u2)) => AAtom (ATerm ("c_equal", [u1, u2]))
   144        | (u1, SOME (SOME _, u2)) =>
   145          mk_anot (AAtom (ATerm ("c_equal", [u1, u2]))))
   146 
   147 fun fo_term_head (ATerm (s, _)) = s
   148 
   149 (* TPTP formulas are fully parenthesized, so we don't need to worry about
   150    operator precedence. *)
   151 fun parse_formula pool x =
   152   (($$ "(" |-- parse_formula pool --| $$ ")"
   153     || ($$ "!" >> K AForall || $$ "?" >> K AExists)
   154        --| $$ "[" -- parse_terms pool --| $$ "]" --| $$ ":"
   155        -- parse_formula pool
   156        >> (fn ((q, ts), phi) => AQuant (q, map fo_term_head ts, phi))
   157     || $$ "~" |-- parse_formula pool >> mk_anot
   158     || parse_atom pool)
   159    -- Scan.option ((Scan.this_string "=>" >> K AImplies
   160                     || Scan.this_string "<=>" >> K AIff
   161                     || Scan.this_string "<~>" >> K ANotIff
   162                     || Scan.this_string "<=" >> K AIf
   163                     || $$ "|" >> K AOr || $$ "&" >> K AAnd)
   164                    -- parse_formula pool)
   165    >> (fn (phi1, NONE) => phi1
   166         | (phi1, SOME (c, phi2)) => mk_aconn c (phi1, phi2))) x
   167 
   168 val parse_tstp_extra_arguments =
   169   Scan.optional ($$ "," |-- parse_annotation
   170                  --| Scan.option ($$ "," |-- parse_annotations)) []
   171 
   172 (* Syntax: (fof|cnf)\(<num>, <formula_role>, <formula> <extra_arguments>\).
   173    The <num> could be an identifier, but we assume integers. *)
   174  fun parse_tstp_line pool =
   175    ((Scan.this_string "fof" || Scan.this_string "cnf") -- $$ "(")
   176      |-- scan_integer --| $$ "," -- Symbol.scan_id --| $$ ","
   177      -- parse_formula pool -- parse_tstp_extra_arguments --| $$ ")" --| $$ "."
   178     >> (fn (((num, role), phi), deps) =>
   179            case role of
   180              "definition" =>
   181              (case phi of
   182                 AConn (AIff, [phi1 as AAtom _, phi2]) =>
   183                 Definition (num, phi1, phi2)
   184               | AAtom (ATerm ("c_equal", _)) =>
   185                 Inference (num, phi, deps) (* Vampire's equality proxy axiom *)
   186               | _ => raise Fail "malformed definition")
   187            | _ => Inference (num, phi, deps))
   188 
   189 (**** PARSING OF VAMPIRE OUTPUT ****)
   190 
   191 (* Syntax: <num>. <formula> <annotation> *)
   192 fun parse_vampire_line pool =
   193   scan_integer --| $$ "." -- parse_formula pool -- parse_annotation
   194   >> (fn ((num, phi), deps) => Inference (num, phi, deps))
   195 
   196 (**** PARSING OF SPASS OUTPUT ****)
   197 
   198 (* SPASS returns clause references of the form "x.y". We ignore "y", whose role
   199    is not clear anyway. *)
   200 val parse_dot_name = scan_integer --| $$ "." --| scan_integer
   201 
   202 val parse_spass_annotations =
   203   Scan.optional ($$ ":" |-- Scan.repeat (parse_dot_name
   204                                          --| Scan.option ($$ ","))) []
   205 
   206 (* It is not clear why some literals are followed by sequences of stars and/or
   207    pluses. We ignore them. *)
   208 fun parse_decorated_atom pool =
   209   parse_atom pool --| Scan.repeat ($$ "*" || $$ "+" || $$ " ")
   210 
   211 fun mk_horn ([], []) = AAtom (ATerm ("c_False", []))
   212   | mk_horn ([], pos_lits) = foldr1 (mk_aconn AOr) pos_lits
   213   | mk_horn (neg_lits, []) = mk_anot (foldr1 (mk_aconn AAnd) neg_lits)
   214   | mk_horn (neg_lits, pos_lits) =
   215     mk_aconn AImplies (foldr1 (mk_aconn AAnd) neg_lits,
   216                        foldr1 (mk_aconn AOr) pos_lits)
   217 
   218 fun parse_horn_clause pool =
   219   Scan.repeat (parse_decorated_atom pool) --| $$ "|" --| $$ "|"
   220     -- Scan.repeat (parse_decorated_atom pool) --| $$ "-" --| $$ ">"
   221     -- Scan.repeat (parse_decorated_atom pool)
   222   >> (mk_horn o apfst (op @))
   223 
   224 (* Syntax: <num>[0:<inference><annotations>]
   225    <atoms> || <atoms> -> <atoms>. *)
   226 fun parse_spass_line pool =
   227   scan_integer --| $$ "[" --| $$ "0" --| $$ ":" --| Symbol.scan_id
   228     -- parse_spass_annotations --| $$ "]" -- parse_horn_clause pool --| $$ "."
   229   >> (fn ((num, deps), u) => Inference (num, u, deps))
   230 
   231 fun parse_line pool =
   232   parse_tstp_line pool || parse_vampire_line pool || parse_spass_line pool
   233 fun parse_lines pool = Scan.repeat1 (parse_line pool)
   234 fun parse_proof pool =
   235   fst o Scan.finite Symbol.stopper
   236             (Scan.error (!! (fn _ => raise Fail "unrecognized ATP output")
   237                             (parse_lines pool)))
   238   o explode o strip_spaces
   239 
   240 (**** INTERPRETATION OF TSTP SYNTAX TREES ****)
   241 
   242 exception FO_TERM of string fo_term list
   243 exception FORMULA of (string, string fo_term) formula list
   244 exception SAME of unit
   245 
   246 (* Type variables are given the basic sort "HOL.type". Some will later be
   247    constrained by information from type literals, or by type inference. *)
   248 fun type_from_fo_term tfrees (u as ATerm (a, us)) =
   249   let val Ts = map (type_from_fo_term tfrees) us in
   250     case strip_prefix_and_undo_ascii type_const_prefix a of
   251       SOME b => Type (invert_const b, Ts)
   252     | NONE =>
   253       if not (null us) then
   254         raise FO_TERM [u]  (* only "tconst"s have type arguments *)
   255       else case strip_prefix_and_undo_ascii tfree_prefix a of
   256         SOME b =>
   257         let val s = "'" ^ b in
   258           TFree (s, AList.lookup (op =) tfrees s |> the_default HOLogic.typeS)
   259         end
   260       | NONE =>
   261         case strip_prefix_and_undo_ascii tvar_prefix a of
   262           SOME b => TVar (("'" ^ b, 0), HOLogic.typeS)
   263         | NONE =>
   264           (* Variable from the ATP, say "X1" *)
   265           Type_Infer.param 0 (a, HOLogic.typeS)
   266   end
   267 
   268 (* Type class literal applied to a type. Returns triple of polarity, class,
   269    type. *)
   270 fun type_constraint_from_term pos tfrees (u as ATerm (a, us)) =
   271   case (strip_prefix_and_undo_ascii class_prefix a,
   272         map (type_from_fo_term tfrees) us) of
   273     (SOME b, [T]) => (pos, b, T)
   274   | _ => raise FO_TERM [u]
   275 
   276 (** Accumulate type constraints in a formula: negative type literals **)
   277 fun add_var (key, z)  = Vartab.map_default (key, []) (cons z)
   278 fun add_type_constraint (false, cl, TFree (a ,_)) = add_var ((a, ~1), cl)
   279   | add_type_constraint (false, cl, TVar (ix, _)) = add_var (ix, cl)
   280   | add_type_constraint _ = I
   281 
   282 fun fix_atp_variable_name f s =
   283   let
   284     fun subscript_name s n = s ^ nat_subscript n
   285     val s = String.map f s
   286   in
   287     case space_explode "_" s of
   288       [_] => (case take_suffix Char.isDigit (String.explode s) of
   289                 (cs1 as _ :: _, cs2 as _ :: _) =>
   290                 subscript_name (String.implode cs1)
   291                                (the (Int.fromString (String.implode cs2)))
   292               | (_, _) => s)
   293     | [s1, s2] => (case Int.fromString s2 of
   294                      SOME n => subscript_name s1 n
   295                    | NONE => s)
   296     | _ => s
   297   end
   298 
   299 (* First-order translation. No types are known for variables. "HOLogic.typeT"
   300    should allow them to be inferred. *)
   301 fun raw_term_from_pred thy full_types tfrees =
   302   let
   303     fun aux opt_T extra_us u =
   304       case u of
   305         ATerm ("hBOOL", [u1]) => aux (SOME @{typ bool}) [] u1
   306       | ATerm ("hAPP", [u1, u2]) => aux opt_T (u2 :: extra_us) u1
   307       | ATerm (a, us) =>
   308         if a = type_wrapper_name then
   309           case us of
   310             [typ_u, term_u] =>
   311             aux (SOME (type_from_fo_term tfrees typ_u)) extra_us term_u
   312           | _ => raise FO_TERM us
   313         else case strip_prefix_and_undo_ascii const_prefix a of
   314           SOME "equal" =>
   315           list_comb (Const (@{const_name "op ="}, HOLogic.typeT),
   316                      map (aux NONE []) us)
   317         | SOME b =>
   318           let
   319             val c = invert_const b
   320             val num_type_args = num_type_args thy c
   321             val (type_us, term_us) =
   322               chop (if full_types then 0 else num_type_args) us
   323             (* Extra args from "hAPP" come after any arguments given directly to
   324                the constant. *)
   325             val term_ts = map (aux NONE []) term_us
   326             val extra_ts = map (aux NONE []) extra_us
   327             val t =
   328               Const (c, if full_types then
   329                           case opt_T of
   330                             SOME T => map fastype_of term_ts ---> T
   331                           | NONE =>
   332                             if num_type_args = 0 then
   333                               Sign.const_instance thy (c, [])
   334                             else
   335                               raise Fail ("no type information for " ^ quote c)
   336                         else
   337                           Sign.const_instance thy (c,
   338                               map (type_from_fo_term tfrees) type_us))
   339           in list_comb (t, term_ts @ extra_ts) end
   340         | NONE => (* a free or schematic variable *)
   341           let
   342             val ts = map (aux NONE []) (us @ extra_us)
   343             val T = map fastype_of ts ---> HOLogic.typeT
   344             val t =
   345               case strip_prefix_and_undo_ascii fixed_var_prefix a of
   346                 SOME b => Free (b, T)
   347               | NONE =>
   348                 case strip_prefix_and_undo_ascii schematic_var_prefix a of
   349                   SOME b => Var ((b, 0), T)
   350                 | NONE =>
   351                   if is_tptp_variable a then
   352                     Var ((fix_atp_variable_name Char.toLower a, 0), T)
   353                   else
   354                     (* Skolem constants? *)
   355                     Var ((fix_atp_variable_name Char.toUpper a, 0), T)
   356           in list_comb (t, ts) end
   357   in aux (SOME HOLogic.boolT) [] end
   358 
   359 fun term_from_pred thy full_types tfrees pos (u as ATerm (s, _)) =
   360   if String.isPrefix class_prefix s then
   361     add_type_constraint (type_constraint_from_term pos tfrees u)
   362     #> pair @{const True}
   363   else
   364     pair (raw_term_from_pred thy full_types tfrees u)
   365 
   366 val combinator_table =
   367   [(@{const_name COMBI}, @{thm COMBI_def_raw}),
   368    (@{const_name COMBK}, @{thm COMBK_def_raw}),
   369    (@{const_name COMBB}, @{thm COMBB_def_raw}),
   370    (@{const_name COMBC}, @{thm COMBC_def_raw}),
   371    (@{const_name COMBS}, @{thm COMBS_def_raw})]
   372 
   373 fun uncombine_term (t1 $ t2) = betapply (pairself uncombine_term (t1, t2))
   374   | uncombine_term (Abs (s, T, t')) = Abs (s, T, uncombine_term t')
   375   | uncombine_term (t as Const (x as (s, _))) =
   376     (case AList.lookup (op =) combinator_table s of
   377        SOME thm => thm |> prop_of |> specialize_type @{theory} x |> Logic.dest_equals |> snd
   378      | NONE => t)
   379   | uncombine_term t = t
   380 
   381 (* Update schematic type variables with detected sort constraints. It's not
   382    totally clear when this code is necessary. *)
   383 fun repair_tvar_sorts (t, tvar_tab) =
   384   let
   385     fun do_type (Type (a, Ts)) = Type (a, map do_type Ts)
   386       | do_type (TVar (xi, s)) =
   387         TVar (xi, the_default s (Vartab.lookup tvar_tab xi))
   388       | do_type (TFree z) = TFree z
   389     fun do_term (Const (a, T)) = Const (a, do_type T)
   390       | do_term (Free (a, T)) = Free (a, do_type T)
   391       | do_term (Var (xi, T)) = Var (xi, do_type T)
   392       | do_term (t as Bound _) = t
   393       | do_term (Abs (a, T, t)) = Abs (a, do_type T, do_term t)
   394       | do_term (t1 $ t2) = do_term t1 $ do_term t2
   395   in t |> not (Vartab.is_empty tvar_tab) ? do_term end
   396 
   397 fun quantify_over_free quant_s free_s body_t =
   398   case Term.add_frees body_t [] |> filter (curry (op =) free_s o fst) of
   399     [] => body_t
   400   | frees as (_, free_T) :: _ =>
   401     Abs (free_s, free_T, fold (curry abstract_over) (map Free frees) body_t)
   402 
   403 (* Interpret an ATP formula as a HOL term, extracting sort constraints as they
   404    appear in the formula. *)
   405 fun prop_from_formula thy full_types tfrees phi =
   406   let
   407     fun do_formula pos phi =
   408       case phi of
   409         AQuant (_, [], phi) => do_formula pos phi
   410       | AQuant (q, x :: xs, phi') =>
   411         do_formula pos (AQuant (q, xs, phi'))
   412         #>> quantify_over_free (case q of
   413                                   AForall => @{const_name All}
   414                                 | AExists => @{const_name Ex}) x
   415       | AConn (ANot, [phi']) => do_formula (not pos) phi' #>> s_not
   416       | AConn (c, [phi1, phi2]) =>
   417         do_formula (pos |> c = AImplies ? not) phi1
   418         ##>> do_formula pos phi2
   419         #>> (case c of
   420                AAnd => s_conj
   421              | AOr => s_disj
   422              | AImplies => s_imp
   423              | AIf => s_imp o swap
   424              | AIff => s_iff
   425              | ANotIff => s_not o s_iff)
   426       | AAtom tm => term_from_pred thy full_types tfrees pos tm
   427       | _ => raise FORMULA [phi]
   428   in repair_tvar_sorts (do_formula true phi Vartab.empty) end
   429 
   430 fun check_formula ctxt =
   431   Type_Infer.constrain HOLogic.boolT
   432   #> Syntax.check_term (ProofContext.set_mode ProofContext.mode_schematic ctxt)
   433 
   434 
   435 (**** Translation of TSTP files to Isar Proofs ****)
   436 
   437 fun unvarify_term (Var ((s, 0), T)) = Free (s, T)
   438   | unvarify_term t = raise TERM ("unvarify_term: non-Var", [t])
   439 
   440 fun decode_line full_types tfrees (Definition (num, phi1, phi2)) ctxt =
   441     let
   442       val thy = ProofContext.theory_of ctxt
   443       val t1 = prop_from_formula thy full_types tfrees phi1
   444       val vars = snd (strip_comb t1)
   445       val frees = map unvarify_term vars
   446       val unvarify_args = subst_atomic (vars ~~ frees)
   447       val t2 = prop_from_formula thy full_types tfrees phi2
   448       val (t1, t2) =
   449         HOLogic.eq_const HOLogic.typeT $ t1 $ t2
   450         |> unvarify_args |> uncombine_term |> check_formula ctxt
   451         |> HOLogic.dest_eq
   452     in
   453       (Definition (num, t1, t2),
   454        fold Variable.declare_term (maps OldTerm.term_frees [t1, t2]) ctxt)
   455     end
   456   | decode_line full_types tfrees (Inference (num, u, deps)) ctxt =
   457     let
   458       val thy = ProofContext.theory_of ctxt
   459       val t = u |> prop_from_formula thy full_types tfrees
   460                 |> uncombine_term |> check_formula ctxt
   461     in
   462       (Inference (num, t, deps),
   463        fold Variable.declare_term (OldTerm.term_frees t) ctxt)
   464     end
   465 fun decode_lines ctxt full_types tfrees lines =
   466   fst (fold_map (decode_line full_types tfrees) lines ctxt)
   467 
   468 fun is_same_inference _ (Definition _) = false
   469   | is_same_inference t (Inference (_, t', _)) = t aconv t'
   470 
   471 (* No "real" literals means only type information (tfree_tcs, clsrel, or
   472    clsarity). *)
   473 val is_only_type_information = curry (op aconv) HOLogic.true_const
   474 
   475 fun replace_one_dep (old, new) dep = if dep = old then new else [dep]
   476 fun replace_deps_in_line _ (line as Definition _) = line
   477   | replace_deps_in_line p (Inference (num, t, deps)) =
   478     Inference (num, t, fold (union (op =) o replace_one_dep p) deps [])
   479 
   480 (* Discard axioms; consolidate adjacent lines that prove the same formula, since
   481    they differ only in type information.*)
   482 fun add_line _ _ (line as Definition _) lines = line :: lines
   483   | add_line conjecture_shape axiom_names (Inference (num, t, [])) lines =
   484     (* No dependencies: axiom, conjecture, or (for Vampire) internal axioms or
   485        definitions. *)
   486     if is_axiom_number axiom_names num then
   487       (* Axioms are not proof lines. *)
   488       if is_only_type_information t then
   489         map (replace_deps_in_line (num, [])) lines
   490       (* Is there a repetition? If so, replace later line by earlier one. *)
   491       else case take_prefix (not o is_same_inference t) lines of
   492         (_, []) => lines (*no repetition of proof line*)
   493       | (pre, Inference (num', _, _) :: post) =>
   494         pre @ map (replace_deps_in_line (num', [num])) post
   495     else if is_conjecture_number conjecture_shape num then
   496       Inference (num, negate_term t, []) :: lines
   497     else
   498       map (replace_deps_in_line (num, [])) lines
   499   | add_line _ _ (Inference (num, t, deps)) lines =
   500     (* Type information will be deleted later; skip repetition test. *)
   501     if is_only_type_information t then
   502       Inference (num, t, deps) :: lines
   503     (* Is there a repetition? If so, replace later line by earlier one. *)
   504     else case take_prefix (not o is_same_inference t) lines of
   505       (* FIXME: Doesn't this code risk conflating proofs involving different
   506          types? *)
   507        (_, []) => Inference (num, t, deps) :: lines
   508      | (pre, Inference (num', t', _) :: post) =>
   509        Inference (num, t', deps) ::
   510        pre @ map (replace_deps_in_line (num', [num])) post
   511 
   512 (* Recursively delete empty lines (type information) from the proof. *)
   513 fun add_nontrivial_line (Inference (num, t, [])) lines =
   514     if is_only_type_information t then delete_dep num lines
   515     else Inference (num, t, []) :: lines
   516   | add_nontrivial_line line lines = line :: lines
   517 and delete_dep num lines =
   518   fold_rev add_nontrivial_line (map (replace_deps_in_line (num, [])) lines) []
   519 
   520 (* ATPs sometimes reuse free variable names in the strangest ways. Removing
   521    offending lines often does the trick. *)
   522 fun is_bad_free frees (Free x) = not (member (op =) frees x)
   523   | is_bad_free _ _ = false
   524 
   525 (* Vampire is keen on producing these. *)
   526 fun is_trivial_formula (@{const Not} $ (Const (@{const_name "op ="}, _)
   527                                         $ t1 $ t2)) = (t1 aconv t2)
   528   | is_trivial_formula _ = false
   529 
   530 fun add_desired_line _ _ _ _ (line as Definition (num, _, _)) (j, lines) =
   531     (j, line :: map (replace_deps_in_line (num, [])) lines)
   532   | add_desired_line isar_shrink_factor conjecture_shape axiom_names frees
   533                      (Inference (num, t, deps)) (j, lines) =
   534     (j + 1,
   535      if is_axiom_number axiom_names num orelse
   536         is_conjecture_number conjecture_shape num orelse
   537         (not (is_only_type_information t) andalso
   538          null (Term.add_tvars t []) andalso
   539          not (exists_subterm (is_bad_free frees) t) andalso
   540          not (is_trivial_formula t) andalso
   541          (null lines orelse (* last line must be kept *)
   542           (length deps >= 2 andalso j mod isar_shrink_factor = 0))) then
   543        Inference (num, t, deps) :: lines  (* keep line *)
   544      else
   545        map (replace_deps_in_line (num, deps)) lines)  (* drop line *)
   546 
   547 (** EXTRACTING LEMMAS **)
   548 
   549 (* A list consisting of the first number in each line is returned. For TSTP,
   550    interesting lines have the form "fof(108, axiom, ...)", where the number
   551    (108) is extracted. For SPASS, lines have the form "108[0:Inp] ...", where
   552    the first number (108) is extracted. For Vampire, we look for
   553    "108. ... [input]". *)
   554 fun used_facts_in_atp_proof axiom_names atp_proof =
   555   let
   556     fun axiom_name num =
   557       let val j = Int.fromString num |> the_default ~1 in
   558         if is_axiom_number axiom_names j then
   559           SOME (Vector.sub (axiom_names, j - 1))
   560         else
   561           NONE
   562       end
   563     val tokens_of =
   564       String.tokens (fn c => not (Char.isAlphaNum c) andalso c <> #"_")
   565     val thm_name_list = Vector.foldr (op ::) [] axiom_names
   566     fun do_line ("fof" :: num :: "axiom" :: (rest as _ :: _)) =
   567         (case strip_prefix_and_undo_ascii axiom_prefix (List.last rest) of
   568            SOME name =>
   569            if member (op =) rest "file" then SOME name else axiom_name num
   570          | NONE => axiom_name num)
   571       | do_line (num :: "0" :: "Inp" :: _) = axiom_name num
   572       | do_line (num :: rest) =
   573         (case List.last rest of "input" => axiom_name num | _ => NONE)
   574       | do_line _ = NONE
   575   in atp_proof |> split_lines |> map_filter (do_line o tokens_of) end
   576 
   577 val indent_size = 2
   578 val no_label = ("", ~1)
   579 
   580 val raw_prefix = "X"
   581 val assum_prefix = "A"
   582 val fact_prefix = "F"
   583 
   584 fun string_for_label (s, num) = s ^ string_of_int num
   585 
   586 fun metis_using [] = ""
   587   | metis_using ls =
   588     "using " ^ space_implode " " (map string_for_label ls) ^ " "
   589 fun metis_apply _ 1 = "by "
   590   | metis_apply 1 _ = "apply "
   591   | metis_apply i _ = "prefer " ^ string_of_int i ^ " apply "
   592 fun metis_name full_types = if full_types then "metisFT" else "metis"
   593 fun metis_call full_types [] = metis_name full_types
   594   | metis_call full_types ss =
   595     "(" ^ metis_name full_types ^ " " ^ space_implode " " ss ^ ")"
   596 fun metis_command full_types i n (ls, ss) =
   597   metis_using ls ^ metis_apply i n ^ metis_call full_types ss
   598 fun metis_line full_types i n ss =
   599   "Try this command: " ^
   600   Markup.markup Markup.sendback (metis_command full_types i n ([], ss)) ^ ".\n"
   601 fun minimize_line _ [] = ""
   602   | minimize_line minimize_command facts =
   603     case minimize_command facts of
   604       "" => ""
   605     | command =>
   606       "To minimize the number of lemmas, try this: " ^
   607       Markup.markup Markup.sendback command ^ ".\n"
   608 
   609 val unprefix_chained = perhaps (try (unprefix chained_prefix))
   610 
   611 fun used_facts axiom_names =
   612   used_facts_in_atp_proof axiom_names
   613   #> List.partition (String.isPrefix chained_prefix)
   614   #>> map (unprefix chained_prefix)
   615   #> pairself (sort_distinct string_ord)
   616 
   617 fun metis_proof_text (full_types, minimize_command, atp_proof, axiom_names,
   618                       goal, i) =
   619   let
   620     val (chained_lemmas, other_lemmas) = used_facts axiom_names atp_proof
   621     val lemmas = other_lemmas @ chained_lemmas
   622     val n = Logic.count_prems (prop_of goal)
   623   in
   624     (metis_line full_types i n other_lemmas ^
   625      minimize_line minimize_command lemmas, lemmas)
   626   end
   627 
   628 (** Isar proof construction and manipulation **)
   629 
   630 fun merge_fact_sets (ls1, ss1) (ls2, ss2) =
   631   (union (op =) ls1 ls2, union (op =) ss1 ss2)
   632 
   633 type label = string * int
   634 type facts = label list * string list
   635 
   636 datatype qualifier = Show | Then | Moreover | Ultimately
   637 
   638 datatype step =
   639   Fix of (string * typ) list |
   640   Let of term * term |
   641   Assume of label * term |
   642   Have of qualifier list * label * term * byline
   643 and byline =
   644   ByMetis of facts |
   645   CaseSplit of step list list * facts
   646 
   647 fun smart_case_split [] facts = ByMetis facts
   648   | smart_case_split proofs facts = CaseSplit (proofs, facts)
   649 
   650 fun add_fact_from_dep axiom_names num =
   651   if is_axiom_number axiom_names num then
   652     apsnd (insert (op =) (Vector.sub (axiom_names, num - 1)))
   653   else
   654     apfst (insert (op =) (raw_prefix, num))
   655 
   656 fun forall_of v t = HOLogic.all_const (fastype_of v) $ lambda v t
   657 fun forall_vars t = fold_rev forall_of (map Var (Term.add_vars t [])) t
   658 
   659 fun step_for_line _ _ (Definition (_, t1, t2)) = Let (t1, t2)
   660   | step_for_line _ _ (Inference (num, t, [])) = Assume ((raw_prefix, num), t)
   661   | step_for_line axiom_names j (Inference (num, t, deps)) =
   662     Have (if j = 1 then [Show] else [], (raw_prefix, num),
   663           forall_vars t,
   664           ByMetis (fold (add_fact_from_dep axiom_names) deps ([], [])))
   665 
   666 fun proof_from_atp_proof pool ctxt full_types tfrees isar_shrink_factor
   667                          atp_proof conjecture_shape axiom_names params frees =
   668   let
   669     val lines =
   670       atp_proof ^ "$" (* the $ sign acts as a sentinel (FIXME: needed?) *)
   671       |> parse_proof pool
   672       |> sort (int_ord o pairself raw_step_number)
   673       |> decode_lines ctxt full_types tfrees
   674       |> rpair [] |-> fold_rev (add_line conjecture_shape axiom_names)
   675       |> rpair [] |-> fold_rev add_nontrivial_line
   676       |> rpair (0, []) |-> fold_rev (add_desired_line isar_shrink_factor
   677                                              conjecture_shape axiom_names frees)
   678       |> snd
   679   in
   680     (if null params then [] else [Fix params]) @
   681     map2 (step_for_line axiom_names) (length lines downto 1) lines
   682   end
   683 
   684 (* When redirecting proofs, we keep information about the labels seen so far in
   685    the "backpatches" data structure. The first component indicates which facts
   686    should be associated with forthcoming proof steps. The second component is a
   687    pair ("assum_ls", "drop_ls"), where "assum_ls" are the labels that should
   688    become assumptions and "drop_ls" are the labels that should be dropped in a
   689    case split. *)
   690 type backpatches = (label * facts) list * (label list * label list)
   691 
   692 fun used_labels_of_step (Have (_, _, _, by)) =
   693     (case by of
   694        ByMetis (ls, _) => ls
   695      | CaseSplit (proofs, (ls, _)) =>
   696        fold (union (op =) o used_labels_of) proofs ls)
   697   | used_labels_of_step _ = []
   698 and used_labels_of proof = fold (union (op =) o used_labels_of_step) proof []
   699 
   700 fun new_labels_of_step (Fix _) = []
   701   | new_labels_of_step (Let _) = []
   702   | new_labels_of_step (Assume (l, _)) = [l]
   703   | new_labels_of_step (Have (_, l, _, _)) = [l]
   704 val new_labels_of = maps new_labels_of_step
   705 
   706 val join_proofs =
   707   let
   708     fun aux _ [] = NONE
   709       | aux proof_tail (proofs as (proof1 :: _)) =
   710         if exists null proofs then
   711           NONE
   712         else if forall (curry (op =) (hd proof1) o hd) (tl proofs) then
   713           aux (hd proof1 :: proof_tail) (map tl proofs)
   714         else case hd proof1 of
   715           Have ([], l, t, _) => (* FIXME: should we really ignore the "by"? *)
   716           if forall (fn Have ([], l', t', _) :: _ => (l, t) = (l', t')
   717                       | _ => false) (tl proofs) andalso
   718              not (exists (member (op =) (maps new_labels_of proofs))
   719                          (used_labels_of proof_tail)) then
   720             SOME (l, t, map rev proofs, proof_tail)
   721           else
   722             NONE
   723         | _ => NONE
   724   in aux [] o map rev end
   725 
   726 fun case_split_qualifiers proofs =
   727   case length proofs of
   728     0 => []
   729   | 1 => [Then]
   730   | _ => [Ultimately]
   731 
   732 fun redirect_proof conjecture_shape hyp_ts concl_t proof =
   733   let
   734     (* The first pass outputs those steps that are independent of the negated
   735        conjecture. The second pass flips the proof by contradiction to obtain a
   736        direct proof, introducing case splits when an inference depends on
   737        several facts that depend on the negated conjecture. *)
   738     fun find_hyp num =
   739       nth hyp_ts (index_in_shape num conjecture_shape)
   740       handle Subscript =>
   741              raise Fail ("Cannot find hypothesis " ^ Int.toString num)
   742      val concl_ls = map (pair raw_prefix) (List.last conjecture_shape)
   743      val canonicalize_labels =
   744        map (fn l => if member (op =) concl_ls l then hd concl_ls else l)
   745        #> distinct (op =)
   746      fun first_pass ([], contra) = ([], contra)
   747        | first_pass ((step as Fix _) :: proof, contra) =
   748          first_pass (proof, contra) |>> cons step
   749        | first_pass ((step as Let _) :: proof, contra) =
   750          first_pass (proof, contra) |>> cons step
   751        | first_pass ((step as Assume (l as (_, num), _)) :: proof, contra) =
   752          if member (op =) concl_ls l then
   753            first_pass (proof, contra ||> l = hd concl_ls ? cons step)
   754          else
   755            first_pass (proof, contra) |>> cons (Assume (l, find_hyp num))
   756        | first_pass (Have (qs, l, t, ByMetis (ls, ss)) :: proof, contra) =
   757          let
   758            val ls = canonicalize_labels ls
   759            val step = Have (qs, l, t, ByMetis (ls, ss))
   760          in
   761            if exists (member (op =) (fst contra)) ls then
   762              first_pass (proof, contra |>> cons l ||> cons step)
   763            else
   764              first_pass (proof, contra) |>> cons step
   765          end
   766        | first_pass _ = raise Fail "malformed proof"
   767     val (proof_top, (contra_ls, contra_proof)) =
   768       first_pass (proof, (concl_ls, []))
   769     val backpatch_label = the_default ([], []) oo AList.lookup (op =) o fst
   770     fun backpatch_labels patches ls =
   771       fold merge_fact_sets (map (backpatch_label patches) ls) ([], [])
   772     fun second_pass end_qs ([], assums, patches) =
   773         ([Have (end_qs, no_label, concl_t,
   774                 ByMetis (backpatch_labels patches (map snd assums)))], patches)
   775       | second_pass end_qs (Assume (l, t) :: proof, assums, patches) =
   776         second_pass end_qs (proof, (t, l) :: assums, patches)
   777       | second_pass end_qs (Have (qs, l, t, ByMetis (ls, ss)) :: proof, assums,
   778                             patches) =
   779         if member (op =) (snd (snd patches)) l andalso
   780            not (member (op =) (fst (snd patches)) l) andalso
   781            not (AList.defined (op =) (fst patches) l) then
   782           second_pass end_qs (proof, assums, patches ||> apsnd (append ls))
   783         else
   784           (case List.partition (member (op =) contra_ls) ls of
   785              ([contra_l], co_ls) =>
   786              if member (op =) qs Show then
   787                second_pass end_qs (proof, assums,
   788                                    patches |>> cons (contra_l, (co_ls, ss)))
   789              else
   790                second_pass end_qs
   791                            (proof, assums,
   792                             patches |>> cons (contra_l, (l :: co_ls, ss)))
   793                |>> cons (if member (op =) (fst (snd patches)) l then
   794                            Assume (l, negate_term t)
   795                          else
   796                            Have (qs, l, negate_term t,
   797                                  ByMetis (backpatch_label patches l)))
   798            | (contra_ls as _ :: _, co_ls) =>
   799              let
   800                val proofs =
   801                  map_filter
   802                      (fn l =>
   803                          if member (op =) concl_ls l then
   804                            NONE
   805                          else
   806                            let
   807                              val drop_ls = filter (curry (op <>) l) contra_ls
   808                            in
   809                              second_pass []
   810                                  (proof, assums,
   811                                   patches ||> apfst (insert (op =) l)
   812                                           ||> apsnd (union (op =) drop_ls))
   813                              |> fst |> SOME
   814                            end) contra_ls
   815                val (assumes, facts) =
   816                  if member (op =) (fst (snd patches)) l then
   817                    ([Assume (l, negate_term t)], (l :: co_ls, ss))
   818                  else
   819                    ([], (co_ls, ss))
   820              in
   821                (case join_proofs proofs of
   822                   SOME (l, t, proofs, proof_tail) =>
   823                   Have (case_split_qualifiers proofs @
   824                         (if null proof_tail then end_qs else []), l, t,
   825                         smart_case_split proofs facts) :: proof_tail
   826                 | NONE =>
   827                   [Have (case_split_qualifiers proofs @ end_qs, no_label,
   828                          concl_t, smart_case_split proofs facts)],
   829                 patches)
   830                |>> append assumes
   831              end
   832            | _ => raise Fail "malformed proof")
   833        | second_pass _ _ = raise Fail "malformed proof"
   834     val proof_bottom =
   835       second_pass [Show] (contra_proof, [], ([], ([], []))) |> fst
   836   in proof_top @ proof_bottom end
   837 
   838 val kill_duplicate_assumptions_in_proof =
   839   let
   840     fun relabel_facts subst =
   841       apfst (map (fn l => AList.lookup (op =) subst l |> the_default l))
   842     fun do_step (step as Assume (l, t)) (proof, subst, assums) =
   843         (case AList.lookup (op aconv) assums t of
   844            SOME l' => (proof, (l, l') :: subst, assums)
   845          | NONE => (step :: proof, subst, (t, l) :: assums))
   846       | do_step (Have (qs, l, t, by)) (proof, subst, assums) =
   847         (Have (qs, l, t,
   848                case by of
   849                  ByMetis facts => ByMetis (relabel_facts subst facts)
   850                | CaseSplit (proofs, facts) =>
   851                  CaseSplit (map do_proof proofs, relabel_facts subst facts)) ::
   852          proof, subst, assums)
   853       | do_step step (proof, subst, assums) = (step :: proof, subst, assums)
   854     and do_proof proof = fold do_step proof ([], [], []) |> #1 |> rev
   855   in do_proof end
   856 
   857 val then_chain_proof =
   858   let
   859     fun aux _ [] = []
   860       | aux _ ((step as Assume (l, _)) :: proof) = step :: aux l proof
   861       | aux l' (Have (qs, l, t, by) :: proof) =
   862         (case by of
   863            ByMetis (ls, ss) =>
   864            Have (if member (op =) ls l' then
   865                    (Then :: qs, l, t,
   866                     ByMetis (filter_out (curry (op =) l') ls, ss))
   867                  else
   868                    (qs, l, t, ByMetis (ls, ss)))
   869          | CaseSplit (proofs, facts) =>
   870            Have (qs, l, t, CaseSplit (map (aux no_label) proofs, facts))) ::
   871         aux l proof
   872       | aux _ (step :: proof) = step :: aux no_label proof
   873   in aux no_label end
   874 
   875 fun kill_useless_labels_in_proof proof =
   876   let
   877     val used_ls = used_labels_of proof
   878     fun do_label l = if member (op =) used_ls l then l else no_label
   879     fun do_step (Assume (l, t)) = Assume (do_label l, t)
   880       | do_step (Have (qs, l, t, by)) =
   881         Have (qs, do_label l, t,
   882               case by of
   883                 CaseSplit (proofs, facts) =>
   884                 CaseSplit (map (map do_step) proofs, facts)
   885               | _ => by)
   886       | do_step step = step
   887   in map do_step proof end
   888 
   889 fun prefix_for_depth n = replicate_string (n + 1)
   890 
   891 val relabel_proof =
   892   let
   893     fun aux _ _ _ [] = []
   894       | aux subst depth (next_assum, next_fact) (Assume (l, t) :: proof) =
   895         if l = no_label then
   896           Assume (l, t) :: aux subst depth (next_assum, next_fact) proof
   897         else
   898           let val l' = (prefix_for_depth depth assum_prefix, next_assum) in
   899             Assume (l', t) ::
   900             aux ((l, l') :: subst) depth (next_assum + 1, next_fact) proof
   901           end
   902       | aux subst depth (next_assum, next_fact) (Have (qs, l, t, by) :: proof) =
   903         let
   904           val (l', subst, next_fact) =
   905             if l = no_label then
   906               (l, subst, next_fact)
   907             else
   908               let
   909                 val l' = (prefix_for_depth depth fact_prefix, next_fact)
   910               in (l', (l, l') :: subst, next_fact + 1) end
   911           val relabel_facts =
   912             apfst (map (fn l =>
   913                            case AList.lookup (op =) subst l of
   914                              SOME l' => l'
   915                            | NONE => raise Fail ("unknown label " ^
   916                                                  quote (string_for_label l))))
   917           val by =
   918             case by of
   919               ByMetis facts => ByMetis (relabel_facts facts)
   920             | CaseSplit (proofs, facts) =>
   921               CaseSplit (map (aux subst (depth + 1) (1, 1)) proofs,
   922                          relabel_facts facts)
   923         in
   924           Have (qs, l', t, by) ::
   925           aux subst depth (next_assum, next_fact) proof
   926         end
   927       | aux subst depth nextp (step :: proof) =
   928         step :: aux subst depth nextp proof
   929   in aux [] 0 (1, 1) end
   930 
   931 fun string_for_proof ctxt full_types i n =
   932   let
   933     fun fix_print_mode f x =
   934       setmp_CRITICAL show_no_free_types true
   935           (setmp_CRITICAL show_types true
   936                (Print_Mode.setmp (filter (curry (op =) Symbol.xsymbolsN)
   937                                          (print_mode_value ())) f)) x
   938     fun do_indent ind = replicate_string (ind * indent_size) " "
   939     fun do_free (s, T) =
   940       maybe_quote s ^ " :: " ^
   941       maybe_quote (fix_print_mode (Syntax.string_of_typ ctxt) T)
   942     fun do_label l = if l = no_label then "" else string_for_label l ^ ": "
   943     fun do_have qs =
   944       (if member (op =) qs Moreover then "moreover " else "") ^
   945       (if member (op =) qs Ultimately then "ultimately " else "") ^
   946       (if member (op =) qs Then then
   947          if member (op =) qs Show then "thus" else "hence"
   948        else
   949          if member (op =) qs Show then "show" else "have")
   950     val do_term = maybe_quote o fix_print_mode (Syntax.string_of_term ctxt)
   951     fun do_facts (ls, ss) =
   952       let
   953         val ls = ls |> sort_distinct (prod_ord string_ord int_ord)
   954         val ss = ss |> map unprefix_chained |> sort_distinct string_ord
   955       in metis_command full_types 1 1 (ls, ss) end
   956     and do_step ind (Fix xs) =
   957         do_indent ind ^ "fix " ^ space_implode " and " (map do_free xs) ^ "\n"
   958       | do_step ind (Let (t1, t2)) =
   959         do_indent ind ^ "let " ^ do_term t1 ^ " = " ^ do_term t2 ^ "\n"
   960       | do_step ind (Assume (l, t)) =
   961         do_indent ind ^ "assume " ^ do_label l ^ do_term t ^ "\n"
   962       | do_step ind (Have (qs, l, t, ByMetis facts)) =
   963         do_indent ind ^ do_have qs ^ " " ^
   964         do_label l ^ do_term t ^ " " ^ do_facts facts ^ "\n"
   965       | do_step ind (Have (qs, l, t, CaseSplit (proofs, facts))) =
   966         space_implode (do_indent ind ^ "moreover\n")
   967                       (map (do_block ind) proofs) ^
   968         do_indent ind ^ do_have qs ^ " " ^ do_label l ^ do_term t ^ " " ^
   969         do_facts facts ^ "\n"
   970     and do_steps prefix suffix ind steps =
   971       let val s = implode (map (do_step ind) steps) in
   972         replicate_string (ind * indent_size - size prefix) " " ^ prefix ^
   973         String.extract (s, ind * indent_size,
   974                         SOME (size s - ind * indent_size - 1)) ^
   975         suffix ^ "\n"
   976       end
   977     and do_block ind proof = do_steps "{ " " }" (ind + 1) proof
   978     (* One-step proofs are pointless; better use the Metis one-liner
   979        directly. *)
   980     and do_proof [Have (_, _, _, ByMetis _)] = ""
   981       | do_proof proof =
   982         (if i <> 1 then "prefer " ^ string_of_int i ^ "\n" else "") ^
   983         do_indent 0 ^ "proof -\n" ^
   984         do_steps "" "" 1 proof ^
   985         do_indent 0 ^ (if n <> 1 then "next" else "qed") ^ "\n"
   986   in do_proof end
   987 
   988 fun isar_proof_text (pool, debug, isar_shrink_factor, ctxt, conjecture_shape)
   989                     (other_params as (full_types, _, atp_proof, axiom_names,
   990                                       goal, i)) =
   991   let
   992     val (params, hyp_ts, concl_t) = strip_subgoal goal i
   993     val frees = fold Term.add_frees (concl_t :: hyp_ts) []
   994     val tfrees = fold Term.add_tfrees (concl_t :: hyp_ts) []
   995     val n = Logic.count_prems (prop_of goal)
   996     val (one_line_proof, lemma_names) = metis_proof_text other_params
   997     fun isar_proof_for () =
   998       case proof_from_atp_proof pool ctxt full_types tfrees isar_shrink_factor
   999                                 atp_proof conjecture_shape axiom_names params
  1000                                 frees
  1001            |> redirect_proof conjecture_shape hyp_ts concl_t
  1002            |> kill_duplicate_assumptions_in_proof
  1003            |> then_chain_proof
  1004            |> kill_useless_labels_in_proof
  1005            |> relabel_proof
  1006            |> string_for_proof ctxt full_types i n of
  1007         "" => "No structured proof available.\n"
  1008       | proof => "\nStructured proof:\n" ^ Markup.markup Markup.sendback proof
  1009     val isar_proof =
  1010       if debug then
  1011         isar_proof_for ()
  1012       else
  1013         try isar_proof_for ()
  1014         |> the_default "Warning: The Isar proof construction failed.\n"
  1015   in (one_line_proof ^ isar_proof, lemma_names) end
  1016 
  1017 fun proof_text isar_proof isar_params other_params =
  1018   (if isar_proof then isar_proof_text isar_params else metis_proof_text)
  1019       other_params
  1020 
  1021 end;