src/HOL/Tools/BNF/bnf_gfp_grec_sugar.ML
changeset 62692 0701f25fac39
child 62699 add334b71e16
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/src/HOL/Tools/BNF/bnf_gfp_grec_sugar.ML	Tue Mar 22 12:39:37 2016 +0100
     1.3 @@ -0,0 +1,2412 @@
     1.4 +(*  Title:      HOL/Tools/BNF/bnf_gfp_grec_sugar.ML
     1.5 +    Author:     Aymeric Bouzy, Ecole polytechnique
     1.6 +    Author:     Jasmin Blanchette, Inria, LORIA, MPII
     1.7 +    Author:     Dmitriy Traytel, ETH Z├╝rich
     1.8 +    Copyright   2015, 2016
     1.9 +
    1.10 +Generalized corecursor sugar ("corec" and friends).
    1.11 +*)
    1.12 +
    1.13 +signature BNF_GFP_GREC_SUGAR =
    1.14 +sig
    1.15 +  datatype corec_option =
    1.16 +    Plugins_Option of Proof.context -> Plugin_Name.filter |
    1.17 +    Friend_Option |
    1.18 +    Transfer_Option
    1.19 +
    1.20 +  val parse_corec_equation: Proof.context -> term list -> term -> term list * term
    1.21 +  val explore_corec_equation: Proof.context -> bool -> bool -> string -> term ->
    1.22 +    BNF_GFP_Grec_Sugar_Util.s_parse_info -> typ -> term list * term -> term list * term
    1.23 +  val build_corecUU_arg_and_goals: bool -> term -> term list * term -> local_theory ->
    1.24 +    (((thm list * thm list * thm list) * term list) * term) * local_theory
    1.25 +  val derive_eq_corecUU: Proof.context -> BNF_GFP_Grec.corec_info -> term -> term -> thm -> thm
    1.26 +  val derive_unique: Proof.context -> morphism -> term -> BNF_GFP_Grec.corec_info -> typ -> thm ->
    1.27 +    thm
    1.28 +
    1.29 +  val corec_cmd: corec_option list -> (binding * string option * mixfix) list * string ->
    1.30 +    local_theory -> local_theory
    1.31 +  val corecursive_cmd: corec_option list -> (binding * string option * mixfix) list * string ->
    1.32 +    local_theory -> Proof.state
    1.33 +  val friend_of_corec_cmd: (string * string option) * string -> local_theory -> Proof.state
    1.34 +  val coinduction_upto_cmd: string * string -> local_theory -> local_theory
    1.35 +end;
    1.36 +
    1.37 +structure BNF_GFP_Grec_Sugar : BNF_GFP_GREC_SUGAR =
    1.38 +struct
    1.39 +
    1.40 +open Ctr_Sugar
    1.41 +open BNF_Util
    1.42 +open BNF_Tactics
    1.43 +open BNF_Def
    1.44 +open BNF_Comp
    1.45 +open BNF_FP_Util
    1.46 +open BNF_FP_Def_Sugar
    1.47 +open BNF_FP_N2M_Sugar
    1.48 +open BNF_GFP_Rec_Sugar
    1.49 +open BNF_GFP_Util
    1.50 +open BNF_GFP_Grec
    1.51 +open BNF_GFP_Grec_Sugar_Util
    1.52 +open BNF_GFP_Grec_Sugar_Tactics
    1.53 +
    1.54 +val cong_N = "cong_";
    1.55 +val baseN = "base";
    1.56 +val reflN = "refl";
    1.57 +val symN = "sym";
    1.58 +val transN = "trans";
    1.59 +val cong_introsN = prefix cong_N "intros";
    1.60 +val cong_intros_friendN = "cong_intros_friend";
    1.61 +val codeN = "code";
    1.62 +val coinductN = "coinduct";
    1.63 +val coinduct_uptoN = "coinduct_upto";
    1.64 +val corecN = "corec";
    1.65 +val ctrN = "ctr";
    1.66 +val discN = "disc";
    1.67 +val disc_iffN = "disc_iff";
    1.68 +val eq_algrhoN = "eq_algrho";
    1.69 +val eq_corecUUN = "eq_corecUU";
    1.70 +val friendN = "friend";
    1.71 +val inner_elimN = "inner_elim";
    1.72 +val inner_inductN = "inner_induct";
    1.73 +val inner_simpN = "inner_simp";
    1.74 +val rhoN = "rho";
    1.75 +val selN = "sel";
    1.76 +val uniqueN = "unique";
    1.77 +
    1.78 +val inner_fp_suffix = "_inner_fp";
    1.79 +
    1.80 +val nitpicksimp_attrs = @{attributes [nitpick_simp]};
    1.81 +val simp_attrs = @{attributes [simp]};
    1.82 +val transfer_rule_attrs = @{attributes [transfer_rule]};
    1.83 +
    1.84 +val unfold_id_thms1 =
    1.85 +  map (fn thm => thm RS eq_reflection) @{thms id_bnf_o o_id_bnf id_apply o_apply} @
    1.86 +  @{thms fst_def[abs_def, symmetric] snd_def[abs_def, symmetric]};
    1.87 +
    1.88 +fun unfold_id_bnf_etc lthy =
    1.89 +  let val thy = Proof_Context.theory_of lthy in
    1.90 +    Raw_Simplifier.rewrite_term thy unfold_id_thms1 []
    1.91 +    #> Raw_Simplifier.rewrite_term thy @{thms BNF_Composition.id_bnf_def} []
    1.92 +  end;
    1.93 +
    1.94 +fun unexpected_corec_call ctxt eqns t =
    1.95 +  error_at ctxt eqns ("Unexpected corecursive call in " ^ quote (Syntax.string_of_term ctxt t));
    1.96 +fun unsupported_case_around_corec_call ctxt eqns t =
    1.97 +  error_at ctxt eqns ("Unsupported corecursive call under case expression " ^
    1.98 +    quote (Syntax.string_of_term ctxt t) ^ "\n(Define " ^
    1.99 +    quote (Syntax.string_of_typ ctxt (domain_type (fastype_of t))) ^
   1.100 +    " with  discriminators and selectors to circumvent this limitation.)");
   1.101 +
   1.102 +datatype corec_option =
   1.103 +  Plugins_Option of Proof.context -> Plugin_Name.filter |
   1.104 +  Friend_Option |
   1.105 +  Transfer_Option;
   1.106 +
   1.107 +val corec_option_parser = Parse.group (K "option")
   1.108 +  (Plugin_Name.parse_filter >> Plugins_Option
   1.109 +   || Parse.reserved "friend" >> K Friend_Option
   1.110 +   || Parse.reserved "transfer" >> K Transfer_Option);
   1.111 +
   1.112 +type codatatype_extra =
   1.113 +  {case_dtor: thm,
   1.114 +   case_trivial: thm,
   1.115 +   abs_rep_transfers: thm list};
   1.116 +
   1.117 +fun morph_codatatype_extra phi ({case_dtor, case_trivial, abs_rep_transfers} : codatatype_extra) =
   1.118 +  {case_dtor = Morphism.thm phi case_dtor, case_trivial = Morphism.thm phi case_trivial,
   1.119 +   abs_rep_transfers = map (Morphism.thm phi) abs_rep_transfers};
   1.120 +
   1.121 +val transfer_codatatype_extra = morph_codatatype_extra o Morphism.transfer_morphism;
   1.122 +
   1.123 +type coinduct_extra =
   1.124 +  {coinduct: thm,
   1.125 +   coinduct_attrs: Token.src list,
   1.126 +   cong_intro_tab: thm list Symtab.table};
   1.127 +
   1.128 +fun morph_coinduct_extra phi ({coinduct, coinduct_attrs, cong_intro_tab} : coinduct_extra) =
   1.129 +  {coinduct = Morphism.thm phi coinduct, coinduct_attrs = coinduct_attrs,
   1.130 +   cong_intro_tab = Symtab.map (K (Morphism.fact phi)) cong_intro_tab};
   1.131 +
   1.132 +val transfer_coinduct_extra = morph_coinduct_extra o Morphism.transfer_morphism;
   1.133 +
   1.134 +type friend_extra =
   1.135 +  {eq_algrhos: thm list,
   1.136 +   algrho_eqs: thm list};
   1.137 +
   1.138 +val empty_friend_extra = {eq_algrhos = [], algrho_eqs = []};
   1.139 +
   1.140 +fun merge_friend_extras ({eq_algrhos = eq_algrhos1, algrho_eqs = algrho_eqs1},
   1.141 +    {eq_algrhos = eq_algrhos2, algrho_eqs = algrho_eqs2}) =
   1.142 +  {eq_algrhos = union Thm.eq_thm_prop eq_algrhos1 eq_algrhos2,
   1.143 +   algrho_eqs = union Thm.eq_thm_prop algrho_eqs1 algrho_eqs2};
   1.144 +
   1.145 +type corec_sugar_data =
   1.146 +  codatatype_extra Symtab.table * coinduct_extra Symtab.table * friend_extra Symtab.table;
   1.147 +
   1.148 +structure Data = Generic_Data
   1.149 +(
   1.150 +  type T = corec_sugar_data;
   1.151 +  val empty = (Symtab.empty, Symtab.empty, Symtab.empty);
   1.152 +  val extend = I;
   1.153 +  fun merge data : T =
   1.154 +    (Symtab.merge (K true) (apply2 #1 data), Symtab.merge (K true) (apply2 #2 data),
   1.155 +     Symtab.join (K merge_friend_extras) (apply2 #3 data));
   1.156 +);
   1.157 +
   1.158 +fun register_codatatype_extra fpT_name extra =
   1.159 +  Local_Theory.declaration {syntax = false, pervasive = true} (fn phi =>
   1.160 +    Data.map (@{apply 3(1)} (Symtab.update (fpT_name, morph_codatatype_extra phi extra))));
   1.161 +
   1.162 +fun codatatype_extra_of ctxt =
   1.163 +  Symtab.lookup (#1 (Data.get (Context.Proof ctxt)))
   1.164 +  #> Option.map (transfer_codatatype_extra (Proof_Context.theory_of ctxt));
   1.165 +
   1.166 +fun all_codatatype_extras_of ctxt =
   1.167 +  Symtab.dest (#1 (Data.get (Context.Proof ctxt)));
   1.168 +
   1.169 +fun register_coinduct_extra fpT_name extra =
   1.170 +  Local_Theory.declaration {syntax = false, pervasive = true} (fn phi =>
   1.171 +    Data.map (@{apply 3(2)} (Symtab.update (fpT_name, morph_coinduct_extra phi extra))));
   1.172 +
   1.173 +fun coinduct_extra_of ctxt =
   1.174 +  Symtab.lookup (#2 (Data.get (Context.Proof ctxt)))
   1.175 +  #> Option.map (transfer_coinduct_extra (Proof_Context.theory_of ctxt));
   1.176 +
   1.177 +fun register_friend_extra fun_name eq_algrho algrho_eq =
   1.178 +  Local_Theory.declaration {syntax = false, pervasive = true} (fn phi =>
   1.179 +    Data.map (@{apply 3(3)} (Symtab.map_default (fun_name, empty_friend_extra)
   1.180 +      (fn {eq_algrhos, algrho_eqs} =>
   1.181 +        {eq_algrhos = Morphism.thm phi eq_algrho :: eq_algrhos,
   1.182 +         algrho_eqs = Morphism.thm phi algrho_eq :: algrho_eqs}))));
   1.183 +
   1.184 +fun all_friend_extras_of ctxt =
   1.185 +  Symtab.dest (#3 (Data.get (Context.Proof ctxt)));
   1.186 +
   1.187 +fun coinduct_extras_of_generic context =
   1.188 +  corec_infos_of_generic context
   1.189 +  #> map (#corecUU #> dest_Const #> fst #> Symtab.lookup (#2 (Data.get context)) #> the
   1.190 +    #> transfer_coinduct_extra (Context.theory_of context));
   1.191 +
   1.192 +fun get_coinduct_uptos fpT_name context =
   1.193 +  coinduct_extras_of_generic context fpT_name |> map #coinduct;
   1.194 +fun get_cong_all_intros fpT_name context =
   1.195 +  coinduct_extras_of_generic context fpT_name |> maps (#cong_intro_tab #> Symtab.dest #> maps snd);
   1.196 +fun get_cong_intros fpT_name name context =
   1.197 +  coinduct_extras_of_generic context fpT_name
   1.198 +  |> maps (#cong_intro_tab #> (fn tab => Symtab.lookup_list tab name));
   1.199 +
   1.200 +fun ctr_names_of_fp_name lthy fpT_name =
   1.201 +  fpT_name |> fp_sugar_of lthy |> the |> #fp_ctr_sugar |> #ctr_sugar |> #ctrs
   1.202 +  |> map (Long_Name.base_name o name_of_ctr);
   1.203 +
   1.204 +fun register_coinduct_dynamic_base fpT_name lthy =
   1.205 +  let val fp_b = Binding.name (Long_Name.base_name fpT_name) in
   1.206 +    lthy
   1.207 +    |> fold Local_Theory.add_thms_dynamic
   1.208 +      ((mk_fp_binding fp_b coinduct_uptoN, get_coinduct_uptos fpT_name) ::
   1.209 +        map (fn N =>
   1.210 +          let val N = cong_N ^ N in
   1.211 +            (mk_fp_binding fp_b N, get_cong_intros fpT_name N)
   1.212 +          end)
   1.213 +        ([baseN, reflN, symN, transN] @ ctr_names_of_fp_name lthy fpT_name))
   1.214 +    |> Local_Theory.add_thms_dynamic
   1.215 +      (mk_fp_binding fp_b cong_introsN, get_cong_all_intros fpT_name)
   1.216 +  end;
   1.217 +
   1.218 +fun register_coinduct_dynamic_friend fpT_name friend_name =
   1.219 +  let
   1.220 +    val fp_b = Binding.name (Long_Name.base_name fpT_name);
   1.221 +    val friend_base_name = cong_N ^ Long_Name.base_name friend_name;
   1.222 +  in
   1.223 +    Local_Theory.add_thms_dynamic
   1.224 +      (mk_fp_binding fp_b friend_base_name, get_cong_intros fpT_name friend_base_name)
   1.225 +  end;
   1.226 +
   1.227 +fun derive_case_dtor ctxt fpT_name =
   1.228 +  let
   1.229 +    val thy = Proof_Context.theory_of ctxt;
   1.230 +
   1.231 +    val SOME ({fp_res_index, fp_res = {dtors, dtor_ctors, ...},
   1.232 +        absT_info = {rep = rep0, abs_inverse, ...},
   1.233 +        fp_ctr_sugar = {ctr_defs, ctr_sugar = {casex, exhaust, case_thms, ...}, ...}, ...}) =
   1.234 +      fp_sugar_of ctxt fpT_name;
   1.235 +
   1.236 +    val (f_Ts, Type (_, [fpT, _])) = strip_fun_type (fastype_of casex);
   1.237 +    val x_Tss = map binder_types f_Ts;
   1.238 +
   1.239 +    val (((u, fs), xss), _) = ctxt
   1.240 +      |> yield_singleton (mk_Frees "y") fpT
   1.241 +      ||>> mk_Frees "f" f_Ts
   1.242 +      ||>> mk_Freess "x" x_Tss;
   1.243 +
   1.244 +    val dtor = nth dtors fp_res_index;
   1.245 +    val u' = dtor $ u;
   1.246 +
   1.247 +    val absT = fastype_of u';
   1.248 +
   1.249 +    val rep = mk_rep absT rep0;
   1.250 +
   1.251 +    val goal = mk_Trueprop_eq (list_comb (casex, fs) $ u,
   1.252 +        mk_case_absumprod absT rep fs xss xss $ u')
   1.253 +      |> Raw_Simplifier.rewrite_term thy @{thms comp_def[THEN eq_reflection]} [];
   1.254 +    val vars = map (fst o dest_Free) (u :: fs);
   1.255 +
   1.256 +    val dtor_ctor = nth dtor_ctors fp_res_index;
   1.257 +  in
   1.258 +    Goal.prove_sorry ctxt vars [] goal (fn {context = ctxt, prems = _} =>
   1.259 +      mk_case_dtor_tac ctxt u abs_inverse dtor_ctor ctr_defs exhaust case_thms)
   1.260 +    |> Thm.close_derivation
   1.261 +  end;
   1.262 +
   1.263 +fun derive_case_trivial ctxt fpT_name =
   1.264 +  let
   1.265 +    val SOME {casex, exhaust, case_thms, ...} = ctr_sugar_of ctxt fpT_name;
   1.266 +
   1.267 +    val fpT0 as Type (_, As0) = domain_type (body_fun_type (fastype_of casex));
   1.268 +
   1.269 +    val (As, _) = ctxt
   1.270 +      |> mk_TFrees (length As0);
   1.271 +    val fpT = Type (fpT_name, As);
   1.272 +
   1.273 +    val (var_name, ()) = singleton (Variable.variant_frees ctxt []) ("x", ());
   1.274 +    val var = Free (var_name, fpT);
   1.275 +    val goal = mk_Trueprop_eq (expand_to_ctr_term ctxt fpT var, var);
   1.276 +
   1.277 +    val exhaust' = Drule.infer_instantiate' ctxt [SOME (Thm.cterm_of ctxt var)] exhaust;
   1.278 +  in
   1.279 +    Goal.prove_sorry ctxt [var_name] [] goal (fn {context = ctxt, prems = _} =>
   1.280 +      HEADGOAL (rtac ctxt exhaust') THEN ALLGOALS (hyp_subst_tac ctxt) THEN
   1.281 +      unfold_thms_tac ctxt case_thms THEN ALLGOALS (rtac ctxt refl))
   1.282 +    |> Thm.close_derivation
   1.283 +  end;
   1.284 +
   1.285 +fun mk_abs_rep_transfers ctxt fpT_name =
   1.286 +  [mk_abs_transfer ctxt fpT_name, mk_rep_transfer ctxt fpT_name]
   1.287 +  handle Fail _ => [];
   1.288 +
   1.289 +fun set_transfer_rule_attrs thms =
   1.290 +  snd o Local_Theory.notes [((Binding.empty, []), [(thms, transfer_rule_attrs)])];
   1.291 +
   1.292 +fun ensure_codatatype_extra fpT_name ctxt =
   1.293 +  (case codatatype_extra_of ctxt fpT_name of
   1.294 +    NONE =>
   1.295 +    let val abs_rep_transfers = mk_abs_rep_transfers ctxt fpT_name in
   1.296 +      ctxt
   1.297 +      |> register_codatatype_extra fpT_name
   1.298 +        {case_dtor = derive_case_dtor ctxt fpT_name,
   1.299 +         case_trivial = derive_case_trivial ctxt fpT_name,
   1.300 +         abs_rep_transfers = abs_rep_transfers}
   1.301 +      |> set_transfer_rule_attrs abs_rep_transfers
   1.302 +    end
   1.303 +  | SOME {abs_rep_transfers, ...} => ctxt |> set_transfer_rule_attrs abs_rep_transfers);
   1.304 +
   1.305 +fun setup_base fpT_name =
   1.306 +  register_coinduct_dynamic_base fpT_name
   1.307 +  #> ensure_codatatype_extra fpT_name;
   1.308 +
   1.309 +(*TODO: Merge with primcorec "case_of"*)
   1.310 +fun case_of ctxt fcT_name =
   1.311 +  (case ctr_sugar_of ctxt fcT_name of
   1.312 +    SOME {casex = Const (s, _), ...} => SOME s
   1.313 +  | _ => NONE);
   1.314 +
   1.315 +fun is_set ctxt (const_name, T) =
   1.316 +  (case T of
   1.317 +    Type (@{type_name fun}, [Type (fpT_name, _), Type (@{type_name set}, [_])]) =>
   1.318 +    (case bnf_of ctxt fpT_name of
   1.319 +      SOME bnf => exists (fn Const (s, _) => s = const_name | _ => false) (sets_of_bnf bnf)
   1.320 +    | NONE => false)
   1.321 +  | _ => false);
   1.322 +
   1.323 +fun case_eq_if_thms_of_term ctxt t =
   1.324 +  let val ctr_sugars = map_filter (ctr_sugar_of_case ctxt o fst) (Term.add_consts t []) in
   1.325 +    maps #case_eq_ifs ctr_sugars
   1.326 +  end;
   1.327 +
   1.328 +fun all_algrho_eqs_of ctxt =
   1.329 +  maps (#algrho_eqs o snd) (all_friend_extras_of ctxt);
   1.330 +
   1.331 +fun derive_code ctxt inner_fp_simps goal
   1.332 +    {sig_fp_sugars, ssig_fp_sugar, eval, eval_simps, all_algLam_algs, corecUU_thm, ...} res_T fun_t
   1.333 +    fun_def =
   1.334 +  let
   1.335 +    val fun_T = fastype_of fun_t;
   1.336 +    val (arg_Ts, Type (fpT_name, _)) = strip_type fun_T;
   1.337 +    val num_args = length arg_Ts;
   1.338 +
   1.339 +    val SOME {pre_bnf, fp_bnf, absT_info, fp_nesting_bnfs, live_nesting_bnfs, fp_ctr_sugar, ...} =
   1.340 +      fp_sugar_of ctxt fpT_name;
   1.341 +    val SOME {case_trivial, ...} = codatatype_extra_of ctxt fpT_name;
   1.342 +
   1.343 +    val ctr_sugar = #ctr_sugar fp_ctr_sugar;
   1.344 +    val pre_map_def = map_def_of_bnf pre_bnf;
   1.345 +    val abs_inverse = #abs_inverse absT_info;
   1.346 +    val ctr_defs = #ctr_defs fp_ctr_sugar;
   1.347 +    val case_eq_ifs = #case_eq_ifs ctr_sugar @ case_eq_if_thms_of_term ctxt goal;
   1.348 +    val all_sig_map_thms = maps (#map_thms o #fp_bnf_sugar) sig_fp_sugars;
   1.349 +
   1.350 +    val fp_map_ident = map_ident_of_bnf fp_bnf;
   1.351 +    val fpsig_nesting_bnfs = fp_nesting_bnfs @ maps #live_nesting_bnfs sig_fp_sugars;
   1.352 +    val fpsig_nesting_T_names = map (fst o dest_Type o T_of_bnf) fpsig_nesting_bnfs;
   1.353 +    val fpsig_nesting_fp_sugars = map_filter (fp_sugar_of ctxt) fpsig_nesting_T_names;
   1.354 +    val fpsig_nesting_fp_bnf_sugars = map #fp_bnf_sugar fpsig_nesting_fp_sugars;
   1.355 +    val ssig_fp_bnf_sugar = #fp_bnf_sugar ssig_fp_sugar;
   1.356 +    val ssig_bnf = #fp_bnf ssig_fp_sugar;
   1.357 +    val ssig_map = map_of_bnf ssig_bnf;
   1.358 +    val fpsig_nesting_maps = map map_of_bnf fpsig_nesting_bnfs;
   1.359 +    val fpsig_nesting_map_ident0s = map map_ident0_of_bnf fpsig_nesting_bnfs;
   1.360 +    val fpsig_nesting_map_comps = map map_comp_of_bnf fpsig_nesting_bnfs;
   1.361 +    val fpsig_nesting_map_thms = maps #map_thms fpsig_nesting_fp_bnf_sugars;
   1.362 +    val live_nesting_map_ident0s = map map_ident0_of_bnf live_nesting_bnfs;
   1.363 +    val ssig_map_thms = #map_thms ssig_fp_bnf_sugar;
   1.364 +    val all_algLam_alg_pointfuls = map (mk_pointful ctxt) all_algLam_algs;
   1.365 +  in
   1.366 +    Variable.add_free_names ctxt goal []
   1.367 +    |> (fn vars => Goal.prove_sorry ctxt vars [] goal (fn {context = ctxt, prems = _} =>
   1.368 +      mk_code_tac ctxt num_args fpsig_nesting_maps ssig_map eval pre_map_def abs_inverse
   1.369 +        fpsig_nesting_map_ident0s fpsig_nesting_map_comps fpsig_nesting_map_thms
   1.370 +        live_nesting_map_ident0s fp_map_ident case_trivial ctr_defs case_eq_ifs corecUU_thm
   1.371 +        all_sig_map_thms ssig_map_thms all_algLam_alg_pointfuls (all_algrho_eqs_of ctxt) eval_simps
   1.372 +        inner_fp_simps fun_def))
   1.373 +    |> Thm.close_derivation
   1.374 +  end;
   1.375 +
   1.376 +fun derive_unique ctxt phi code_goal
   1.377 +    {sig_fp_sugars, ssig_fp_sugar, eval, eval_simps, all_algLam_algs, corecUU_unique, ...}
   1.378 +    (res_T as Type (fpT_name, _)) eq_corecUU =
   1.379 +  let
   1.380 +    val SOME {pre_bnf, fp_bnf, absT_info, fp_nesting_bnfs, live_nesting_bnfs, fp_ctr_sugar, ...} =
   1.381 +      fp_sugar_of ctxt fpT_name;
   1.382 +    val SOME {case_trivial, ...} = codatatype_extra_of ctxt fpT_name;
   1.383 +
   1.384 +    val ctr_sugar = #ctr_sugar fp_ctr_sugar;
   1.385 +    val pre_map_def = map_def_of_bnf pre_bnf;
   1.386 +    val abs_inverse = #abs_inverse absT_info;
   1.387 +    val ctr_defs = #ctr_defs fp_ctr_sugar;
   1.388 +    val case_eq_ifs = #case_eq_ifs ctr_sugar @ case_eq_if_thms_of_term ctxt code_goal;
   1.389 +    val all_sig_map_thms = maps (#map_thms o #fp_bnf_sugar) sig_fp_sugars;
   1.390 +
   1.391 +    val fp_map_ident = map_ident_of_bnf fp_bnf;
   1.392 +    val fpsig_nesting_bnfs = fp_nesting_bnfs @ maps #live_nesting_bnfs sig_fp_sugars;
   1.393 +    val fpsig_nesting_T_names = map (fst o dest_Type o T_of_bnf) fpsig_nesting_bnfs;
   1.394 +    val fpsig_nesting_fp_sugars = map_filter (fp_sugar_of ctxt) fpsig_nesting_T_names;
   1.395 +    val fpsig_nesting_fp_bnf_sugars = map #fp_bnf_sugar fpsig_nesting_fp_sugars;
   1.396 +    val ssig_fp_bnf_sugar = #fp_bnf_sugar ssig_fp_sugar;
   1.397 +    val ssig_bnf = #fp_bnf ssig_fp_sugar;
   1.398 +    val ssig_map = map_of_bnf ssig_bnf;
   1.399 +    val fpsig_nesting_maps = map map_of_bnf fpsig_nesting_bnfs;
   1.400 +    val fpsig_nesting_map_ident0s = map map_ident0_of_bnf fpsig_nesting_bnfs;
   1.401 +    val fpsig_nesting_map_comps = map map_comp_of_bnf fpsig_nesting_bnfs;
   1.402 +    val fpsig_nesting_map_thms = maps #map_thms fpsig_nesting_fp_bnf_sugars;
   1.403 +    val live_nesting_map_ident0s = map map_ident0_of_bnf live_nesting_bnfs;
   1.404 +    val ssig_map_thms = #map_thms ssig_fp_bnf_sugar;
   1.405 +    val all_algLam_alg_pointfuls = map (mk_pointful ctxt) all_algLam_algs;
   1.406 +
   1.407 +    val @{const Trueprop} $ (Const (@{const_name HOL.eq}, _) $ lhs $ rhs) = code_goal;
   1.408 +    val (fun_t, args) = strip_comb lhs;
   1.409 +    val closed_rhs = fold_rev lambda args rhs;
   1.410 +
   1.411 +    val fun_T = fastype_of fun_t;
   1.412 +    val num_args = num_binder_types fun_T;
   1.413 +
   1.414 +    val f = Free (singleton (Variable.variant_frees ctxt []) ("f", fun_T));
   1.415 +
   1.416 +    val is_self_call = curry (op aconv) fun_t;
   1.417 +    val has_self_call = exists_subterm is_self_call;
   1.418 +
   1.419 +    fun fify args (t $ u) = fify (u :: args) t $ fify [] u
   1.420 +      | fify _ (Abs (s, T, t)) = Abs (s, T, fify [] t)
   1.421 +      | fify args t = if t = fun_t andalso not (exists has_self_call args) then f else t;
   1.422 +
   1.423 +    val goal = Logic.mk_implies (mk_Trueprop_eq (f, fify [] closed_rhs), mk_Trueprop_eq (f, fun_t))
   1.424 +      |> Morphism.term phi;
   1.425 +  in
   1.426 +    Goal.prove_sorry ctxt [fst (dest_Free f)] [] goal (fn {context = ctxt, prems = _} =>
   1.427 +      mk_unique_tac ctxt num_args fpsig_nesting_maps ssig_map eval pre_map_def abs_inverse
   1.428 +        fpsig_nesting_map_ident0s fpsig_nesting_map_comps fpsig_nesting_map_thms
   1.429 +        live_nesting_map_ident0s fp_map_ident case_trivial ctr_defs case_eq_ifs all_sig_map_thms
   1.430 +        ssig_map_thms all_algLam_alg_pointfuls (all_algrho_eqs_of ctxt) eval_simps corecUU_unique
   1.431 +        eq_corecUU)
   1.432 +    |> Thm.close_derivation
   1.433 +  end;
   1.434 +
   1.435 +fun derive_last_disc ctxt fcT_name =
   1.436 +  let
   1.437 +    val SOME {T = fcT, discs, exhaust, disc_thmss, ...} = ctr_sugar_of ctxt fcT_name;
   1.438 +
   1.439 +    val (u, _) = ctxt
   1.440 +      |> yield_singleton (mk_Frees "x") fcT;
   1.441 +
   1.442 +    val udiscs = map (rapp u) discs;
   1.443 +    val (not_udiscs, last_udisc) = split_last udiscs
   1.444 +      |>> map HOLogic.mk_not;
   1.445 +
   1.446 +    val goal = mk_Trueprop_eq (last_udisc, foldr1 HOLogic.mk_conj not_udiscs);
   1.447 +  in
   1.448 +    Goal.prove_sorry ctxt [fst (dest_Free u)] [] goal (fn {context = ctxt, prems = _} =>
   1.449 +      mk_last_disc_tac ctxt u exhaust (flat disc_thmss))
   1.450 +    |> Thm.close_derivation
   1.451 +  end;
   1.452 +
   1.453 +fun derive_eq_algrho ctxt {sig_fp_sugars, ssig_fp_sugar, eval, eval_simps, all_algLam_algs,
   1.454 +      corecUU_unique, ...}
   1.455 +    ({algrho = algrho0, dtor_algrho, ...} : friend_info) fun_t k_T code_goal const_transfers rho_def
   1.456 +    eq_corecUU =
   1.457 +  let
   1.458 +    val fun_T = fastype_of fun_t;
   1.459 +    val (arg_Ts, Type (fpT_name, Ts)) = strip_type fun_T;
   1.460 +    val num_args = length arg_Ts;
   1.461 +
   1.462 +    val SOME {fp_res_index, fp_res, pre_bnf, fp_bnf, absT_info, fp_nesting_bnfs, live_nesting_bnfs,
   1.463 +        fp_ctr_sugar, ...} =
   1.464 +      fp_sugar_of ctxt fpT_name;
   1.465 +    val SOME {case_dtor, ...} = codatatype_extra_of ctxt fpT_name;
   1.466 +
   1.467 +    val fp_nesting_Ts = map T_of_bnf fp_nesting_bnfs;
   1.468 +
   1.469 +    fun is_nullary_disc_def (@{const Trueprop} $ (Const (@{const_name HOL.eq}, _) $ _
   1.470 +          $ (Const (@{const_name HOL.eq}, _) $ _ $ _))) = true
   1.471 +      | is_nullary_disc_def (Const (@{const_name Pure.eq}, _) $ _
   1.472 +          $ (Const (@{const_name HOL.eq}, _) $ _ $ _)) = true
   1.473 +      | is_nullary_disc_def _ = false;
   1.474 +
   1.475 +    val dtor_ctor = nth (#dtor_ctors fp_res) fp_res_index;
   1.476 +    val ctor_iff_dtor = #ctor_iff_dtor fp_ctr_sugar;
   1.477 +    val ctr_sugar = #ctr_sugar fp_ctr_sugar;
   1.478 +    val pre_map_def = map_def_of_bnf pre_bnf;
   1.479 +    val abs_inverse = #abs_inverse absT_info;
   1.480 +    val ctr_defs = #ctr_defs fp_ctr_sugar;
   1.481 +    val nullary_disc_defs = filter (is_nullary_disc_def o Thm.prop_of) (#disc_defs ctr_sugar);
   1.482 +    val disc_sel_eq_cases = #disc_eq_cases ctr_sugar @ #sel_defs ctr_sugar;
   1.483 +    val case_eq_ifs = #case_eq_ifs ctr_sugar @ case_eq_if_thms_of_term ctxt code_goal;
   1.484 +    val all_sig_map_thms = maps (#map_thms o #fp_bnf_sugar) sig_fp_sugars;
   1.485 +
   1.486 +    fun add_tnameT (Type (s, Ts)) = insert (op =) s #> fold add_tnameT Ts
   1.487 +      | add_tnameT _ = I;
   1.488 +
   1.489 +    fun map_disc_sels'_of s =
   1.490 +      (case fp_sugar_of ctxt s of
   1.491 +        SOME {fp_bnf_sugar = {map_disc_iffs, map_selss, ...}, ...} =>
   1.492 +        let
   1.493 +          val map_selss' =
   1.494 +            if length map_selss <= 1 then map_selss
   1.495 +            else map (map (unfold_thms ctxt (no_refl [derive_last_disc ctxt s]))) map_selss;
   1.496 +        in
   1.497 +          map_disc_iffs @ flat map_selss'
   1.498 +        end
   1.499 +      | NONE => []);
   1.500 +
   1.501 +    fun mk_const_pointful_natural const_transfer =
   1.502 +      SOME (mk_pointful_natural_from_transfer ctxt const_transfer)
   1.503 +      handle UNNATURAL () => NONE;
   1.504 +
   1.505 +    val const_pointful_natural_opts = map mk_const_pointful_natural const_transfers;
   1.506 +    val const_pointful_naturals = map_filter I const_pointful_natural_opts;
   1.507 +    val fp_nesting_k_T_names = fold add_tnameT (k_T :: fp_nesting_Ts) [];
   1.508 +    val fp_nesting_k_map_disc_sels' = maps map_disc_sels'_of fp_nesting_k_T_names;
   1.509 +
   1.510 +    val fp_map_ident = map_ident_of_bnf fp_bnf;
   1.511 +    val fpsig_nesting_bnfs = fp_nesting_bnfs @ maps #live_nesting_bnfs sig_fp_sugars;
   1.512 +    val fpsig_nesting_T_names = map (fst o dest_Type o T_of_bnf) fpsig_nesting_bnfs;
   1.513 +    val fpsig_nesting_fp_sugars = map_filter (fp_sugar_of ctxt) fpsig_nesting_T_names;
   1.514 +    val fpsig_nesting_fp_bnf_sugars = map #fp_bnf_sugar fpsig_nesting_fp_sugars;
   1.515 +    val ssig_fp_bnf_sugar = #fp_bnf_sugar ssig_fp_sugar;
   1.516 +    val ssig_bnf = #fp_bnf ssig_fp_sugar;
   1.517 +    val ssig_map = map_of_bnf ssig_bnf;
   1.518 +    val fpsig_nesting_maps = map map_of_bnf fpsig_nesting_bnfs;
   1.519 +    val fpsig_nesting_map_ident0s = map map_ident0_of_bnf fpsig_nesting_bnfs;
   1.520 +    val fpsig_nesting_map_comps = map map_comp_of_bnf fpsig_nesting_bnfs;
   1.521 +    val fpsig_nesting_map_thms = maps #map_thms fpsig_nesting_fp_bnf_sugars;
   1.522 +    val live_nesting_map_ident0s = map map_ident0_of_bnf live_nesting_bnfs;
   1.523 +    val ssig_map_thms = #map_thms ssig_fp_bnf_sugar;
   1.524 +    val all_algLam_alg_pointfuls = map (mk_pointful ctxt) all_algLam_algs;
   1.525 +
   1.526 +    val ctor = nth (#ctors fp_res) fp_res_index;
   1.527 +    val abs = #abs absT_info;
   1.528 +    val rep = #rep absT_info;
   1.529 +    val algrho = mk_ctr Ts algrho0;
   1.530 +
   1.531 +    val goal = mk_Trueprop_eq (fun_t, abs_curried_balanced arg_Ts algrho);
   1.532 +
   1.533 +    fun const_of_transfer thm =
   1.534 +      (case Thm.prop_of thm of @{const Trueprop} $ (_ $ cst $ _) => cst);
   1.535 +
   1.536 +    val eq_algrho =
   1.537 +      Goal.prove_sorry (*FIXME*) (*no sorry*) ctxt [] [] goal (fn {context = ctxt, prems = _} =>
   1.538 +        mk_eq_algrho_tac ctxt fpsig_nesting_maps abs rep ctor ssig_map eval pre_map_def abs_inverse
   1.539 +          fpsig_nesting_map_ident0s fpsig_nesting_map_comps fpsig_nesting_map_thms
   1.540 +          live_nesting_map_ident0s fp_map_ident dtor_ctor ctor_iff_dtor ctr_defs nullary_disc_defs
   1.541 +          disc_sel_eq_cases case_dtor case_eq_ifs const_pointful_naturals
   1.542 +          fp_nesting_k_map_disc_sels' rho_def dtor_algrho corecUU_unique eq_corecUU all_sig_map_thms
   1.543 +          ssig_map_thms all_algLam_alg_pointfuls (all_algrho_eqs_of ctxt) eval_simps)
   1.544 +      |> Thm.close_derivation
   1.545 +      handle e as ERROR _ =>
   1.546 +        (case filter (is_none o snd) (const_transfers ~~ const_pointful_natural_opts) of
   1.547 +          [] => Exn.reraise e
   1.548 +        | thm_nones =>
   1.549 +          error ("Failed to state naturality property for " ^
   1.550 +            commas (map (Syntax.string_of_term ctxt o const_of_transfer o fst) thm_nones)));
   1.551 +
   1.552 +    val algrho_eq = eq_algrho RS (mk_curry_uncurryN_balanced ctxt num_args RS iffD2) RS sym;
   1.553 +  in
   1.554 +    (eq_algrho, algrho_eq)
   1.555 +  end;
   1.556 +
   1.557 +fun prime_rho_transfer_goal ctxt fpT_name rho_def goal =
   1.558 +  let
   1.559 +    val thy = Proof_Context.theory_of ctxt;
   1.560 +
   1.561 +    val SOME {pre_bnf, ...} = fp_sugar_of ctxt fpT_name;
   1.562 +    val SOME {abs_rep_transfers, ...} = codatatype_extra_of ctxt fpT_name;
   1.563 +
   1.564 +    val simps = rel_def_of_bnf pre_bnf :: rho_transfer_simps;
   1.565 +    val fold_rho = unfold_thms ctxt [rho_def RS @{thm symmetric}];
   1.566 +
   1.567 +    fun derive_unprimed rho_transfer' =
   1.568 +      Variable.add_free_names ctxt goal []
   1.569 +      |> (fn vars => Goal.prove_sorry ctxt vars [] goal (fn {context = ctxt, prems = _} =>
   1.570 +        unfold_thms_tac ctxt simps THEN HEADGOAL (rtac ctxt rho_transfer')))
   1.571 +      |> Thm.close_derivation;
   1.572 +
   1.573 +    val goal' = Raw_Simplifier.rewrite_term thy simps [] goal;
   1.574 +  in
   1.575 +    if null abs_rep_transfers then (goal', derive_unprimed #> fold_rho)
   1.576 +    else (goal, fold_rho)
   1.577 +  end;
   1.578 +
   1.579 +fun derive_rho_transfer_folded ctxt fpT_name const_transfers rho_def goal =
   1.580 +  let
   1.581 +    val SOME {pre_bnf, ...} = fp_sugar_of ctxt fpT_name;
   1.582 +    val SOME {abs_rep_transfers, ...} = codatatype_extra_of ctxt fpT_name;
   1.583 +  in
   1.584 +    Variable.add_free_names ctxt goal []
   1.585 +    |> (fn vars => Goal.prove_sorry (*FIXME*) (*no sorry*) ctxt vars [] goal (fn {context = ctxt, prems = _} =>
   1.586 +      mk_rho_transfer_tac ctxt (null abs_rep_transfers) (rel_def_of_bnf pre_bnf)
   1.587 +      const_transfers))
   1.588 +    |> unfold_thms ctxt [rho_def RS @{thm symmetric}]
   1.589 +    |> Thm.close_derivation
   1.590 +  end;
   1.591 +
   1.592 +fun mk_cong_intro_ctr_or_friend_goal ctxt fpT Rcong alg =
   1.593 +  let
   1.594 +    val xy_Ts = binder_types (fastype_of alg);
   1.595 +
   1.596 +    val ((xs, ys), _) = ctxt
   1.597 +      |> mk_Frees "x" xy_Ts
   1.598 +      ||>> mk_Frees "y" xy_Ts;
   1.599 +
   1.600 +    fun mk_prem xy_T x y =
   1.601 +      BNF_Def.build_rel [] ctxt [fpT]
   1.602 +        (fn (T, _) => if T = fpT then Rcong else HOLogic.eq_const T) (xy_T, xy_T) $ x $ y;
   1.603 +
   1.604 +    val prems = @{map 3} mk_prem xy_Ts xs ys;
   1.605 +    val concl = Rcong $ list_comb (alg, xs) $ list_comb (alg, ys);
   1.606 +  in
   1.607 +    Logic.list_implies (map HOLogic.mk_Trueprop prems, HOLogic.mk_Trueprop concl)
   1.608 +  end;
   1.609 +
   1.610 +fun derive_cong_ctr_intros ctxt cong_ctor_intro =
   1.611 +  let
   1.612 +    val @{const Pure.imp} $ _ $ (@{const Trueprop} $ ((Rcong as _ $ _) $ _ $ (ctor $ _))) =
   1.613 +      Thm.prop_of cong_ctor_intro;
   1.614 +
   1.615 +    val fpT as Type (fpT_name, fp_argTs) = range_type (fastype_of ctor);
   1.616 +
   1.617 +    val SOME {pre_bnf, absT_info = {abs_inverse, ...},
   1.618 +        fp_ctr_sugar = {ctr_defs, ctr_sugar = {ctrs = ctrs0, ...}, ...}, ...} =
   1.619 +      fp_sugar_of ctxt fpT_name;
   1.620 +
   1.621 +    val ctrs = map (mk_ctr fp_argTs) ctrs0;
   1.622 +    val pre_rel_def = rel_def_of_bnf pre_bnf;
   1.623 +
   1.624 +    fun prove ctr_def goal =
   1.625 +      Variable.add_free_names ctxt goal []
   1.626 +      |> (fn vars => Goal.prove_sorry ctxt vars [] goal (fn {context = ctxt, prems = _} =>
   1.627 +        mk_cong_intro_ctr_or_friend_tac ctxt ctr_def [pre_rel_def, abs_inverse] cong_ctor_intro))
   1.628 +      |> Thm.close_derivation;
   1.629 +
   1.630 +    val goals = map (mk_cong_intro_ctr_or_friend_goal ctxt fpT Rcong) ctrs;
   1.631 +  in
   1.632 +    map2 prove ctr_defs goals
   1.633 +  end;
   1.634 +
   1.635 +fun derive_cong_friend_intro ctxt cong_algrho_intro =
   1.636 +  let
   1.637 +    val @{const Pure.imp} $ _ $ (@{const Trueprop} $ ((Rcong as _ $ _) $ _
   1.638 +        $ ((algrho as Const (algrho_name, _)) $ _))) =
   1.639 +      Thm.prop_of cong_algrho_intro;
   1.640 +
   1.641 +    val fpT as Type (_, fp_argTs) = range_type (fastype_of algrho);
   1.642 +
   1.643 +    fun has_algrho (@{const Trueprop} $ (Const (@{const_name HOL.eq}, _) $ _ $ rhs)) =
   1.644 +      fst (dest_Const (head_of (strip_abs_body rhs))) = algrho_name;
   1.645 +
   1.646 +    val eq_algrho :: _ =
   1.647 +      maps (filter (has_algrho o Thm.prop_of) o #eq_algrhos o snd) (all_friend_extras_of ctxt);
   1.648 +
   1.649 +    val @{const Trueprop} $ (Const (@{const_name HOL.eq}, _) $ friend0 $ _) = Thm.prop_of eq_algrho;
   1.650 +    val friend = mk_ctr fp_argTs friend0;
   1.651 +
   1.652 +    val goal = mk_cong_intro_ctr_or_friend_goal ctxt fpT Rcong friend;
   1.653 +  in
   1.654 +    Variable.add_free_names ctxt goal []
   1.655 +    |> (fn vars => Goal.prove_sorry ctxt vars [] goal (fn {context = ctxt, prems = _} =>
   1.656 +      mk_cong_intro_ctr_or_friend_tac ctxt eq_algrho [] cong_algrho_intro))
   1.657 +    |> Thm.close_derivation
   1.658 +  end;
   1.659 +
   1.660 +fun derive_cong_intros lthy ctr_names friend_names
   1.661 +    ({cong_base, cong_refl, cong_sym, cong_trans, cong_alg_intros, ...} : dtor_coinduct_info) =
   1.662 +  let
   1.663 +    val cong_ctor_intro :: cong_algrho_intros = rev cong_alg_intros;
   1.664 +    val names = map (prefix cong_N) ([baseN, reflN, symN, transN] @ ctr_names @ friend_names);
   1.665 +    val thms = [cong_base, cong_refl, cong_sym, cong_trans] @
   1.666 +      derive_cong_ctr_intros lthy cong_ctor_intro @
   1.667 +      map (derive_cong_friend_intro lthy) cong_algrho_intros;
   1.668 +  in
   1.669 +    Symtab.make_list (names ~~ thms)
   1.670 +  end;
   1.671 +
   1.672 +fun derive_coinduct ctxt (fpT as Type (fpT_name, _)) dtor_coinduct =
   1.673 +  let
   1.674 +    val thy = Proof_Context.theory_of ctxt;
   1.675 +
   1.676 +    val @{const Pure.imp} $ (@{const Trueprop} $ (_ $ Abs (_, _, _ $
   1.677 +        Abs (_, _, @{const implies} $ _ $ (_ $ (cong0 $ _) $ _ $ _))))) $ _ =
   1.678 +      Thm.prop_of dtor_coinduct;
   1.679 +
   1.680 +    val SOME {X as TVar ((X_s, _), _), fp_res = {dtor_ctors, ...}, pre_bnf,
   1.681 +        absT_info = {abs_inverse, ...}, live_nesting_bnfs,
   1.682 +        fp_ctr_sugar = {ctrXs_Tss, ctr_defs,
   1.683 +          ctr_sugar = ctr_sugar0 as {T = T0, ctrs = ctrs0, discs = discs0, ...}, ...}, ...} =
   1.684 +      fp_sugar_of ctxt fpT_name;
   1.685 +
   1.686 +    val n = length ctrXs_Tss;
   1.687 +    val ms = map length ctrXs_Tss;
   1.688 +
   1.689 +    val X' = TVar ((X_s, maxidx_of_typ fpT + 1), @{sort type});
   1.690 +    val As_rho = tvar_subst thy [T0] [fpT];
   1.691 +    val substXAT = Term.typ_subst_TVars As_rho o Tsubst X X';
   1.692 +    val substXA = Term.subst_TVars As_rho o substT X X';
   1.693 +    val phi = Morphism.typ_morphism "BNF" substXAT $> Morphism.term_morphism "BNF" substXA;
   1.694 +
   1.695 +    fun mk_applied_cong arg =
   1.696 +      enforce_type ctxt domain_type (fastype_of arg) cong0 $ arg;
   1.697 +
   1.698 +    val thm = derive_coinduct_thms_for_types false mk_applied_cong [pre_bnf] dtor_coinduct
   1.699 +        dtor_ctors live_nesting_bnfs [fpT] [substXAT X] [map (map substXAT) ctrXs_Tss] [n]
   1.700 +        [abs_inverse] [abs_inverse] I [ctr_defs] [morph_ctr_sugar phi ctr_sugar0] ctxt
   1.701 +      |> map snd |> the_single;
   1.702 +    val (attrs, _) = mk_coinduct_attrs [fpT] [ctrs0] [discs0] [ms];
   1.703 +  in
   1.704 +    (thm, attrs)
   1.705 +  end;
   1.706 +
   1.707 +type explore_parameters =
   1.708 +  {bound_Us: typ list,
   1.709 +   bound_Ts: typ list,
   1.710 +   U: typ,
   1.711 +   T: typ};
   1.712 +
   1.713 +fun update_UT {bound_Us, bound_Ts, ...} U T =
   1.714 +  {bound_Us = bound_Us, bound_Ts = bound_Ts, U = U, T = T};
   1.715 +
   1.716 +fun explore_nested lthy explore {bound_Us, bound_Ts, U, T} t =
   1.717 +  let
   1.718 +    fun build_simple (T, U) =
   1.719 +      if T = U then
   1.720 +        @{term "%y. y"}
   1.721 +      else
   1.722 +        Bound 0
   1.723 +        |> explore {bound_Us = T :: bound_Us, bound_Ts = T :: bound_Ts, U = U, T = T}
   1.724 +        |> (fn t => Abs (Name.uu, T, t));
   1.725 +  in
   1.726 +    betapply (build_map lthy [] build_simple (T, U), t)
   1.727 +  end;
   1.728 +
   1.729 +fun add_boundvar t = betapply (incr_boundvars 1 t, Bound 0);
   1.730 +
   1.731 +fun explore_fun (arg_U :: arg_Us) explore {bound_Us, bound_Ts, U, T} t =
   1.732 +    let val arg_name = the_default Name.uu (try (fn (Abs (s, _, _)) => s) t) in
   1.733 +      add_boundvar t
   1.734 +      |> explore_fun arg_Us explore
   1.735 +        {bound_Us = arg_U :: bound_Us, bound_Ts = domain_type T :: bound_Ts, U = range_type U,
   1.736 +         T = range_type T}
   1.737 +      |> (fn t => Abs (arg_name, arg_U, t))
   1.738 +    end
   1.739 +  | explore_fun [] explore params t = explore params t;
   1.740 +
   1.741 +fun massage_fun explore (params as {T, U, ...}) =
   1.742 +  if can dest_funT T then explore_fun [domain_type U] explore params else explore params;
   1.743 +
   1.744 +fun massage_star massages explore =
   1.745 +  let
   1.746 +    fun after_massage massages' t params t' =
   1.747 +      if t aconv t' then massage_any massages' params t else massage_any massages params t'
   1.748 +    and massage_any [] params t = explore params t
   1.749 +      | massage_any (massage :: massages') params t =
   1.750 +        massage (after_massage massages' t) params t;
   1.751 +  in
   1.752 +    massage_any massages
   1.753 +  end;
   1.754 +
   1.755 +fun massage_let explore params t =
   1.756 +  (case strip_comb t of
   1.757 +    (Const (@{const_name Let}, _), [_, _]) => unfold_lets_splits t
   1.758 +  | _ => t)
   1.759 +  |> explore params;
   1.760 +
   1.761 +fun check_corec_equation ctxt fun_frees (lhs, rhs) =
   1.762 +  let
   1.763 +    val (fun_t, arg_ts) = strip_comb lhs;
   1.764 +
   1.765 +    fun check_fun_name () =
   1.766 +      null fun_frees orelse member (op aconv) fun_frees fun_t orelse
   1.767 +      error (quote (Syntax.string_of_term ctxt fun_t) ^
   1.768 +        " is not the function currently being defined");
   1.769 +
   1.770 +    fun check_args_are_vars () =
   1.771 +      let
   1.772 +        fun is_ok_Free_or_Var (Free (s, _)) = not (String.isSuffix inner_fp_suffix s)
   1.773 +          | is_ok_Free_or_Var (Var _) = true
   1.774 +          | is_ok_Free_or_Var _ = false;
   1.775 +
   1.776 +        fun is_valid arg =
   1.777 +          (is_ok_Free_or_Var arg andalso not (member (op aconv) fun_frees arg)) orelse
   1.778 +          error ("Argument " ^ quote (Syntax.string_of_term ctxt arg) ^ " is not free");
   1.779 +      in
   1.780 +        forall is_valid arg_ts
   1.781 +      end;
   1.782 +
   1.783 +    fun check_no_duplicate_arg () =
   1.784 +      (case duplicates (op =) arg_ts of
   1.785 +        [] => ()
   1.786 +      | arg :: _ => error ("Repeated argument: " ^ quote (Syntax.string_of_term ctxt arg)));
   1.787 +
   1.788 +    fun check_no_other_frees () =
   1.789 +      let
   1.790 +        val known_frees = fun_frees @ arg_ts;
   1.791 +
   1.792 +        fun check_free (t as Free (s, _)) =
   1.793 +            Variable.is_fixed ctxt s orelse member (op aconv) known_frees t orelse
   1.794 +            error ("Unexpected variable: " ^ quote s)
   1.795 +          | check_free _ = false;
   1.796 +      in
   1.797 +        Term.exists_subterm check_free rhs
   1.798 +      end;
   1.799 +  in
   1.800 +    check_no_duplicate_arg ();
   1.801 +    check_fun_name ();
   1.802 +    check_args_are_vars ();
   1.803 +    check_no_other_frees ()
   1.804 +  end;
   1.805 +
   1.806 +fun parse_corec_equation ctxt fun_frees eq =
   1.807 +  let
   1.808 +    val (lhs, rhs) = HOLogic.dest_eq (HOLogic.dest_Trueprop (drop_all eq))
   1.809 +      handle TERM _ => error "Expected equation";
   1.810 +
   1.811 +    val _ = check_corec_equation ctxt fun_frees (lhs, rhs);
   1.812 +
   1.813 +    val (fun_t, arg_ts) = strip_comb lhs;
   1.814 +    val (arg_Ts, _) = strip_type (fastype_of fun_t);
   1.815 +    val added_Ts = drop (length arg_ts) arg_Ts;
   1.816 +    val free_names = mk_names (length added_Ts) "x" ~~ added_Ts;
   1.817 +    val free_args = Variable.variant_frees ctxt [rhs, lhs] free_names |> map Free;
   1.818 +  in
   1.819 +    (arg_ts @ free_args, list_comb (rhs, free_args))
   1.820 +  end;
   1.821 +
   1.822 +fun morph_views phi (code, ctrs, discs, disc_iffs, sels) =
   1.823 +  (Morphism.term phi code, map (Morphism.term phi) ctrs, map (Morphism.term phi) discs,
   1.824 +   map (Morphism.term phi) disc_iffs, map (Morphism.term phi) sels);
   1.825 +
   1.826 +fun generate_views ctxt eq fun_t (lhs_free_args, rhs) =
   1.827 +  let
   1.828 +    val lhs = list_comb (fun_t, lhs_free_args);
   1.829 +    val T as Type (T_name, Ts) = fastype_of rhs;
   1.830 +    val SOME {fp_ctr_sugar = {ctr_sugar = {ctrs = ctrs0, discs = discs0, selss = selss0, ...}, ...},
   1.831 +        ...} =
   1.832 +      fp_sugar_of ctxt T_name;
   1.833 +    val ctrs = map (mk_ctr Ts) ctrs0;
   1.834 +    val discs = map (mk_disc_or_sel Ts) discs0;
   1.835 +    val selss = map (map (mk_disc_or_sel Ts)) selss0;
   1.836 +
   1.837 +    val code_view = drop_all eq;
   1.838 +
   1.839 +    fun can_case_expand t = not (can (dest_ctr ctxt T_name) t);
   1.840 +
   1.841 +    fun generate_raw_views conds t raw_views =
   1.842 +      let
   1.843 +        fun analyse (ctr :: ctrs) (disc :: discs) ctr' =
   1.844 +          if ctr = ctr' then
   1.845 +            (conds, disc, ctr)
   1.846 +          else
   1.847 +            analyse ctrs discs ctr';
   1.848 +      in
   1.849 +        (analyse ctrs discs (fst (strip_comb t))) :: raw_views
   1.850 +      end;
   1.851 +
   1.852 +    fun generate_disc_views raw_views =
   1.853 +      if length discs = 1 then
   1.854 +        ([], [])
   1.855 +      else
   1.856 +        let
   1.857 +          fun collect_condss_disc condss [] _ = condss
   1.858 +            | collect_condss_disc condss ((conds, disc', _) :: raw_views) disc =
   1.859 +              collect_condss_disc (condss |> disc = disc' ? cons conds) raw_views disc;
   1.860 +
   1.861 +          val grouped_disc_views = discs
   1.862 +            |> map (collect_condss_disc [] raw_views)
   1.863 +            |> curry (op ~~) (map (fn disc => disc $ lhs) discs);
   1.864 +
   1.865 +          fun mk_disc_iff_props props [] = props
   1.866 +            | mk_disc_iff_props _ ((lhs, @{const HOL.True}) :: _) = [lhs]
   1.867 +            | mk_disc_iff_props props ((lhs, rhs) :: views) =
   1.868 +              mk_disc_iff_props ((HOLogic.mk_eq (lhs, rhs)) :: props) views;
   1.869 +        in
   1.870 +          (grouped_disc_views
   1.871 +           |> map swap,
   1.872 +           grouped_disc_views
   1.873 +           |> map (apsnd (s_dnf #> mk_conjs))
   1.874 +           |> mk_disc_iff_props []
   1.875 +           |> map (fn eq => ([[]], eq)))
   1.876 +        end;
   1.877 +
   1.878 +    fun generate_ctr_views raw_views =
   1.879 +      let
   1.880 +        fun collect_condss_ctr condss [] _ = condss
   1.881 +          | collect_condss_ctr condss ((conds, _, ctr') :: raw_views) ctr =
   1.882 +            collect_condss_ctr (condss |> ctr = ctr' ? cons conds) raw_views ctr;
   1.883 +
   1.884 +        fun mk_ctr_eq ctr_sels ctr =
   1.885 +          let
   1.886 +            fun extract_arg n sel _(*bound_Ts*) fun_t arg_ts =
   1.887 +              if ctr = fun_t then
   1.888 +                nth arg_ts n
   1.889 +              else
   1.890 +                let val t = list_comb (fun_t, arg_ts) in
   1.891 +                  if can_case_expand t then
   1.892 +                    sel $ t
   1.893 +                  else
   1.894 +                    Term.dummy_pattern (range_type (fastype_of sel))
   1.895 +                end;
   1.896 +          in
   1.897 +            ctr_sels
   1.898 +            |> map_index (uncurry extract_arg)
   1.899 +            |> map (fn extract => massage_corec_code_rhs ctxt extract [] rhs)
   1.900 +            |> curry list_comb ctr
   1.901 +            |> curry HOLogic.mk_eq lhs
   1.902 +          end;
   1.903 +
   1.904 +          fun remove_condss_if_alone [(_, concl)] = [([[]], concl)]
   1.905 +            | remove_condss_if_alone views = views;
   1.906 +      in
   1.907 +        ctrs
   1.908 +        |> `(map (collect_condss_ctr [] raw_views))
   1.909 +        ||> map2 mk_ctr_eq selss
   1.910 +        |> op ~~
   1.911 +        |> filter_out (null o fst)
   1.912 +        |> remove_condss_if_alone
   1.913 +      end;
   1.914 +
   1.915 +    fun generate_sel_views raw_views only_one_ctr =
   1.916 +      let
   1.917 +        fun mk_sel_positions sel =
   1.918 +          let
   1.919 +            fun get_sel_position _ [] = NONE
   1.920 +              | get_sel_position i (sel' :: sels) =
   1.921 +                if sel = sel' then SOME i else get_sel_position (i + 1) sels;
   1.922 +          in
   1.923 +            ctrs ~~ map (get_sel_position 0) selss
   1.924 +            |> map_filter (fn (ctr, pos_opt) =>
   1.925 +              if is_some pos_opt then SOME (ctr, the pos_opt) else NONE)
   1.926 +          end;
   1.927 +
   1.928 +        fun collect_sel_condss0 condss [] _ = condss
   1.929 +          | collect_sel_condss0 condss ((conds, _, ctr) :: raw_views) sel_positions =
   1.930 +            let val condss' = condss |> is_some (AList.lookup (op =) sel_positions ctr) ? cons conds
   1.931 +            in
   1.932 +              collect_sel_condss0 condss' raw_views sel_positions
   1.933 +            end;
   1.934 +
   1.935 +        val collect_sel_condss =
   1.936 +          if only_one_ctr then K [[]] else collect_sel_condss0 [] raw_views;
   1.937 +
   1.938 +        fun mk_sel_rhs sel_positions sel =
   1.939 +          let
   1.940 +            val sel_T = range_type (fastype_of sel);
   1.941 +
   1.942 +            fun extract_sel_value _(*bound_Ts*) fun_t arg_ts =
   1.943 +              (case AList.lookup (op =) sel_positions fun_t of
   1.944 +                SOME n => nth arg_ts n
   1.945 +              | NONE =>
   1.946 +                let val t = list_comb (fun_t, arg_ts) in
   1.947 +                  if can_case_expand t then
   1.948 +                    sel $ t
   1.949 +                  else
   1.950 +                    Term.dummy_pattern sel_T
   1.951 +                end);
   1.952 +          in
   1.953 +            massage_corec_code_rhs ctxt extract_sel_value [] rhs
   1.954 +          end;
   1.955 +
   1.956 +        val ordered_sels = distinct (op =) (flat selss);
   1.957 +        val sel_positionss = map mk_sel_positions ordered_sels;
   1.958 +        val sel_rhss = map2 mk_sel_rhs sel_positionss ordered_sels;
   1.959 +        val sel_lhss = map (rapp lhs o mk_disc_or_sel Ts) ordered_sels;
   1.960 +        val sel_condss = map collect_sel_condss sel_positionss;
   1.961 +
   1.962 +        fun is_undefined (Const (@{const_name undefined}, _)) = true
   1.963 +          | is_undefined _ = false;
   1.964 +      in
   1.965 +        sel_condss ~~ (sel_lhss ~~ sel_rhss)
   1.966 +        |> filter_out (is_undefined o snd o snd)
   1.967 +        |> map (apsnd HOLogic.mk_eq)
   1.968 +      end;
   1.969 +
   1.970 +    fun mk_atomic_prop fun_args (condss, concl) =
   1.971 +      (Logic.list_all (map dest_Free fun_args, abstract_over_list fun_args
   1.972 +        (Logic.list_implies (map HOLogic.mk_Trueprop (s_dnf condss), HOLogic.mk_Trueprop concl))));
   1.973 +
   1.974 +    val raw_views = rhs
   1.975 +      |> massage_let_if_case ctxt (K false) (fn _(*bound_Ts*) => fn t => t
   1.976 +          |> can_case_expand t ? expand_to_ctr_term ctxt T) (K (K ())) (K I) []
   1.977 +      |> (fn expanded_rhs => fold_rev_let_if_case ctxt generate_raw_views [] expanded_rhs [])
   1.978 +      |> rev;
   1.979 +    val (disc_views, disc_iff_views) = generate_disc_views raw_views;
   1.980 +    val ctr_views = generate_ctr_views raw_views;
   1.981 +    val sel_views = generate_sel_views raw_views (length ctr_views = 1);
   1.982 +
   1.983 +    val mk_props = filter_out (null o fst) #> map (mk_atomic_prop lhs_free_args);
   1.984 +  in
   1.985 +    (code_view, mk_props ctr_views, mk_props disc_views, mk_props disc_iff_views,
   1.986 +     mk_props sel_views)
   1.987 +  end;
   1.988 +
   1.989 +fun find_all_associated_types [] _ = []
   1.990 +  | find_all_associated_types ((Type (_, Ts1), Type (_, Ts2)) :: TTs) T =
   1.991 +    find_all_associated_types ((Ts1 ~~ Ts2) @ TTs) T
   1.992 +  | find_all_associated_types ((T1, T2) :: TTs) T =
   1.993 +    find_all_associated_types TTs T |> T1 = T ? cons T2;
   1.994 +
   1.995 +fun as_member_of tab = try dest_Const #> Option.mapPartial (fst #> Symtab.lookup tab);
   1.996 +
   1.997 +fun extract_rho_from_equation
   1.998 +    ({ctr_guards, inner_buffer = {Oper, VLeaf, CLeaf, ctr_wrapper, friends}, ...},
   1.999 +     {pattern_ctrs, discs, sels, it, mk_case})
  1.1000 +    b version Y preT ssig_T friend_tm (lhs_args, rhs) lthy =
  1.1001 +  let
  1.1002 +    val thy = Proof_Context.theory_of lthy;
  1.1003 +
  1.1004 +    val res_T = fastype_of rhs;
  1.1005 +    val YpreT = HOLogic.mk_prodT (Y, preT);
  1.1006 +
  1.1007 +    fun fpT_to new_T T =
  1.1008 +      if T = res_T then
  1.1009 +        new_T
  1.1010 +      else
  1.1011 +        (case T of
  1.1012 +          Type (s, Ts) => Type (s, map (fpT_to new_T) Ts)
  1.1013 +        | _ => T);
  1.1014 +
  1.1015 +    fun build_params bound_Us bound_Ts T =
  1.1016 +      {bound_Us = bound_Us, bound_Ts = bound_Ts, U = T, T = T};
  1.1017 +
  1.1018 +    fun typ_before explore {bound_Us, bound_Ts, ...} t =
  1.1019 +      explore (build_params bound_Us bound_Ts (fastype_of1 (bound_Ts, t))) t;
  1.1020 +
  1.1021 +    val is_self_call = curry (op aconv) friend_tm;
  1.1022 +    val has_self_call = Term.exists_subterm is_self_call;
  1.1023 +
  1.1024 +    fun has_res_T bound_Ts t = fastype_of1 (bound_Ts, t) = res_T;
  1.1025 +
  1.1026 +    fun contains_res_T (Type (s, Ts)) = s = fst (dest_Type res_T) orelse exists contains_res_T Ts
  1.1027 +      | contains_res_T _ = false;
  1.1028 +
  1.1029 +    val is_lhs_arg = member (op =) lhs_args;
  1.1030 +
  1.1031 +    fun is_constant t =
  1.1032 +      not (Term.exists_subterm is_lhs_arg t orelse has_self_call t orelse loose_bvar (t, 0));
  1.1033 +    fun is_nested_type T = T <> res_T andalso T <> YpreT andalso T <> ssig_T;
  1.1034 +
  1.1035 +    val is_valid_case_argumentT = not o member (op =) [Y, ssig_T];
  1.1036 +
  1.1037 +    fun is_same_type_constr (Type (s, _)) (Type (s', _)) = (s = s')
  1.1038 +      | is_same_type_constr _ _ = false;
  1.1039 +
  1.1040 +    exception NO_ENCAPSULATION of unit;
  1.1041 +
  1.1042 +    val parametric_consts = Unsynchronized.ref [];
  1.1043 +
  1.1044 +    (* We are assuming that set functions are marked with "[transfer_rule]" (cf. the "transfer"
  1.1045 +       plugin). Otherwise, the "eq_algrho" tactic might fail. *)
  1.1046 +    fun is_special_parametric_const (x as (s, _)) =
  1.1047 +      s = @{const_name id} orelse is_set lthy x;
  1.1048 +
  1.1049 +    fun add_parametric_const s general_T T U =
  1.1050 +      let
  1.1051 +        fun tupleT_of_funT T =
  1.1052 +          let val (Ts, T) = strip_type T in
  1.1053 +            mk_tupleT_balanced (Ts @ [T])
  1.1054 +          end;
  1.1055 +
  1.1056 +        fun funT_of_tupleT n =
  1.1057 +          dest_tupleT_balanced (n + 1)
  1.1058 +          #> split_last
  1.1059 +          #> op --->;
  1.1060 +
  1.1061 +        val m = num_binder_types general_T;
  1.1062 +        val param1_T = Type_Infer.paramify_vars general_T;
  1.1063 +        val param2_T = Type_Infer.paramify_vars param1_T;
  1.1064 +
  1.1065 +        val deadfixed_T =
  1.1066 +          build_map lthy [] (mk_undefined o op -->) (apply2 tupleT_of_funT (param1_T, param2_T))
  1.1067 +          |> singleton (Type_Infer_Context.infer_types lthy)
  1.1068 +          |> singleton (Type_Infer.fixate lthy)
  1.1069 +          |> type_of
  1.1070 +          |> dest_funT
  1.1071 +          |-> BNF_GFP_Grec_Sugar_Util.generalize_types 1
  1.1072 +          |> funT_of_tupleT m;
  1.1073 +
  1.1074 +        val j = maxidx_of_typ deadfixed_T + 1;
  1.1075 +
  1.1076 +        fun varifyT (Type (s, Ts)) = Type (s, map varifyT Ts)
  1.1077 +          | varifyT (TFree (s, T)) = TVar ((s, j), T)
  1.1078 +          | varifyT T = T;
  1.1079 +
  1.1080 +        val dedvarified_T = varifyT deadfixed_T;
  1.1081 +
  1.1082 +        val new_vars = Sign.typ_match thy (dedvarified_T, T) Vartab.empty
  1.1083 +          |> Vartab.dest
  1.1084 +          |> filter (curry (op =) j o snd o fst)
  1.1085 +          |> Vartab.make;
  1.1086 +
  1.1087 +        val deadinstantiated_T = map_atyps (Type.devar new_vars) dedvarified_T;
  1.1088 +
  1.1089 +        val final_T =
  1.1090 +          if Sign.typ_instance thy (U, deadinstantiated_T) then deadfixed_T else general_T;
  1.1091 +      in
  1.1092 +        parametric_consts := insert (op =) (s, final_T) (!parametric_consts)
  1.1093 +      end;
  1.1094 +
  1.1095 +    fun encapsulate (params as {U, T, ...}) t =
  1.1096 +      if U = T then
  1.1097 +        t
  1.1098 +      else if T = Y then
  1.1099 +        VLeaf $ t
  1.1100 +      else if T = res_T then
  1.1101 +        CLeaf $ t
  1.1102 +      else if T = YpreT then
  1.1103 +        it $ t
  1.1104 +      else if is_nested_type T andalso is_same_type_constr T U then
  1.1105 +        explore_nested lthy encapsulate params t
  1.1106 +      else
  1.1107 +        raise NO_ENCAPSULATION ();
  1.1108 +
  1.1109 +    fun build_function_after_encapsulation fun_t fun_t' (params as {bound_Us, ...}) arg_ts arg_ts' =
  1.1110 +      let
  1.1111 +        val arg_Us' = fst (strip_typeN (length arg_ts) (fastype_of1 (bound_Us, fun_t')));
  1.1112 +
  1.1113 +        fun the_or_error arg NONE =
  1.1114 +            error ("Illegal argument " ^ quote (Syntax.string_of_term lthy arg) ^
  1.1115 +              " to " ^ quote (Syntax.string_of_term lthy fun_t))
  1.1116 +          | the_or_error _ (SOME arg') = arg';
  1.1117 +      in
  1.1118 +        arg_ts'
  1.1119 +        |> `(map (curry fastype_of1 bound_Us))
  1.1120 +        |>> map2 (update_UT params) arg_Us'
  1.1121 +        |> op ~~
  1.1122 +        |> map (try (uncurry encapsulate))
  1.1123 +        |> map2 the_or_error arg_ts
  1.1124 +        |> curry list_comb fun_t'
  1.1125 +      end;
  1.1126 +
  1.1127 +    fun rebuild_function_after_exploration old_fn new_fn explore params arg_ts =
  1.1128 +      arg_ts
  1.1129 +      |> map (typ_before explore params)
  1.1130 +      |> build_function_after_encapsulation old_fn new_fn params arg_ts;
  1.1131 +
  1.1132 +    fun update_case Us U casex =
  1.1133 +      let
  1.1134 +        val Type (T_name, _) = domain_type (snd (strip_fun_type (fastype_of casex)));
  1.1135 +        val SOME {fp_ctr_sugar = {ctr_sugar = {T = Type (_, Ts), casex, ...}, ...}, ...} =
  1.1136 +          fp_sugar_of lthy T_name;
  1.1137 +        val T = body_type (fastype_of casex);
  1.1138 +      in
  1.1139 +        Term.subst_atomic_types ((T :: Ts) ~~ (U :: Us)) casex
  1.1140 +      end;
  1.1141 +
  1.1142 +    fun deduce_according_type default_T [] = default_T
  1.1143 +      | deduce_according_type default_T Ts = (case distinct (op =) Ts of
  1.1144 +          U :: [] => U
  1.1145 +        | _ => fpT_to ssig_T default_T);
  1.1146 +
  1.1147 +    fun massage_if explore_cond explore (params as {bound_Us, bound_Ts, ...}) t =
  1.1148 +      (case strip_comb t of
  1.1149 +        (const as Const (@{const_name If}, _), obj :: (branches as [_, _])) =>
  1.1150 +        (case List.partition Term.is_dummy_pattern (map (explore params) branches) of
  1.1151 +          (dummy_branch' :: _, []) => dummy_branch'
  1.1152 +        | (_, [branch']) => branch'
  1.1153 +        | (_, branches') =>
  1.1154 +          let
  1.1155 +            val brancheUs = map (curry fastype_of1 bound_Us) branches';
  1.1156 +            val U = deduce_according_type (fastype_of1 (bound_Ts, hd branches)) brancheUs;
  1.1157 +            val const_obj' = (If_const U, obj)
  1.1158 +              ||> explore_cond (update_UT params @{typ bool} @{typ bool})
  1.1159 +              |> op $;
  1.1160 +          in
  1.1161 +            build_function_after_encapsulation (const $ obj) const_obj' params branches branches'
  1.1162 +          end)
  1.1163 +      | _ => explore params t);
  1.1164 +
  1.1165 +    fun massage_map explore (params as {bound_Us, bound_Ts, T = Type (T_name, Ts), ...})
  1.1166 +          (t as func $ mapped_arg) =
  1.1167 +        if is_self_call (head_of func) then
  1.1168 +          explore params t
  1.1169 +        else
  1.1170 +          (case try (dest_map lthy T_name) func of
  1.1171 +            SOME (map_tm, fs) =>
  1.1172 +            let
  1.1173 +              val n = length fs;
  1.1174 +              val mapped_arg' = mapped_arg
  1.1175 +                |> `(curry fastype_of1 bound_Ts)
  1.1176 +                |>> build_params bound_Us bound_Ts
  1.1177 +                |-> explore;
  1.1178 +              (* FIXME: This looks suspicious *)
  1.1179 +              val Us = map (fpT_to ssig_T) (snd (dest_Type (fastype_of1 (bound_Us, mapped_arg'))));
  1.1180 +              val temporary_map = map_tm
  1.1181 +                |> mk_map n Us Ts;
  1.1182 +              val map_fn_Ts = fastype_of #> strip_fun_type #> fst;
  1.1183 +              val binder_Uss = map_fn_Ts temporary_map
  1.1184 +                |> map (map (fpT_to ssig_T) o binder_types);
  1.1185 +              val fun_paramss = map_fn_Ts (head_of func)
  1.1186 +                |> map (build_params bound_Us bound_Ts);
  1.1187 +              val fs' = fs |> @{map 4} explore_fun binder_Uss (replicate n explore) fun_paramss;
  1.1188 +              val SOME bnf = bnf_of lthy T_name;
  1.1189 +              val Type (_, bnf_Ts) = T_of_bnf bnf;
  1.1190 +              val typ_alist =
  1.1191 +                lives_of_bnf bnf ~~ map (curry fastype_of1 bound_Us #> range_type) fs';
  1.1192 +              val Us' = map2 the_default Us (map (AList.lookup (op =) typ_alist) bnf_Ts);
  1.1193 +              val map_tm' = map_tm |> mk_map n Us Us';
  1.1194 +            in
  1.1195 +              build_function_after_encapsulation func (list_comb (map_tm', fs')) params [mapped_arg]
  1.1196 +                [mapped_arg']
  1.1197 +            end
  1.1198 +          | NONE => explore params t)
  1.1199 +      | massage_map explore params t = explore params t;
  1.1200 +
  1.1201 +    fun massage_comp explore (params as {bound_Us, ...}) t =
  1.1202 +      (case strip_comb t of
  1.1203 +        (Const (@{const_name comp}, _), f1 :: f2 :: args) =>
  1.1204 +        let
  1.1205 +          val args' = map (typ_before explore params) args;
  1.1206 +          val f2' = typ_before (explore_fun (map (curry fastype_of1 bound_Us) args') explore) params
  1.1207 +            f2;
  1.1208 +          val f1' = typ_before (explore_fun [range_type (fastype_of1 (bound_Us, f2'))] explore)
  1.1209 +            params f1;
  1.1210 +        in
  1.1211 +          betapply (f1', list_comb (f2', args'))
  1.1212 +        end
  1.1213 +      | _ => explore params t);
  1.1214 +
  1.1215 +    fun massage_ctr explore (params as {T = T as Type (s, Ts), bound_Us, ...}) t =
  1.1216 +        if T <> res_T then
  1.1217 +          (case try (dest_ctr lthy s) t of
  1.1218 +            SOME (ctr, args) =>
  1.1219 +            let
  1.1220 +              val args' = map (typ_before explore params) args;
  1.1221 +              val SOME {T = Type (_, ctr_Ts), ...} = ctr_sugar_of lthy s;
  1.1222 +              val temp_ctr = mk_ctr ctr_Ts ctr;
  1.1223 +              val argUs = map (curry fastype_of1 bound_Us) args';
  1.1224 +              val typ_alist = binder_types (fastype_of temp_ctr) ~~ argUs;
  1.1225 +              val Us = ctr_Ts
  1.1226 +                |> map (find_all_associated_types typ_alist)
  1.1227 +                |> map2 deduce_according_type Ts;
  1.1228 +              val ctr' = mk_ctr Us ctr;
  1.1229 +            in
  1.1230 +              build_function_after_encapsulation ctr ctr' params args args'
  1.1231 +            end
  1.1232 +          | NONE => explore params t)
  1.1233 +        else
  1.1234 +          explore params t
  1.1235 +      | massage_ctr explore params t = explore params t;
  1.1236 +
  1.1237 +    fun const_of [] _ = NONE
  1.1238 +      | const_of ((sel as Const (s1, _)) :: r) (const as Const (s2, _)) =
  1.1239 +        if s1 = s2 then SOME sel else const_of r const
  1.1240 +      | const_of _ _ = NONE;
  1.1241 +
  1.1242 +    fun massage_disc explore (params as {T, bound_Us, bound_Ts, ...}) t =
  1.1243 +      (case (strip_comb t, T = @{typ bool}) of
  1.1244 +        ((fun_t, arg :: []), true) =>
  1.1245 +        let val arg_T = fastype_of1 (bound_Ts, arg) in
  1.1246 +          if arg_T <> res_T then
  1.1247 +            (case arg_T |> try (fst o dest_Type) |> Option.mapPartial (ctr_sugar_of lthy) of
  1.1248 +              SOME {discs, T = Type (_, Ts), ...} =>
  1.1249 +              (case const_of discs fun_t of
  1.1250 +                SOME disc =>
  1.1251 +                let
  1.1252 +                  val arg' = arg |> typ_before explore params;
  1.1253 +                  val Type (_, Us) = fastype_of1 (bound_Us, arg');
  1.1254 +                  val disc' = disc |> Term.subst_TVars (map (fst o dest_TVar) Ts ~~ Us);
  1.1255 +                in
  1.1256 +                  disc' $ arg'
  1.1257 +                end
  1.1258 +              | NONE => explore params t)
  1.1259 +            | NONE => explore params t)
  1.1260 +          else
  1.1261 +            explore params t
  1.1262 +        end
  1.1263 +      | _ => explore params t);
  1.1264 +
  1.1265 +    fun massage_sel explore (params as {bound_Us, bound_Ts, ...}) t =
  1.1266 +      let val (fun_t, args) = strip_comb t in
  1.1267 +        if args = [] then
  1.1268 +          explore params t
  1.1269 +        else
  1.1270 +          let val T = fastype_of1 (bound_Ts, hd args) in
  1.1271 +            (case (Option.mapPartial (ctr_sugar_of lthy) (try (fst o dest_Type) T), T <> res_T) of
  1.1272 +              (SOME {selss, T = Type (_, Ts), ...}, true) =>
  1.1273 +              (case const_of (fold (curry op @) selss []) fun_t of
  1.1274 +                SOME sel =>
  1.1275 +                let
  1.1276 +                  val args' = args |> map (typ_before explore params);
  1.1277 +                  val Type (_, Us) = fastype_of1 (bound_Us, hd args');
  1.1278 +                  val sel' = sel |> Term.subst_TVars (map (fst o dest_TVar) Ts ~~ Us);
  1.1279 +                in
  1.1280 +                  build_function_after_encapsulation sel sel' params args args'
  1.1281 +                end
  1.1282 +              | NONE => explore params t)
  1.1283 +            | _ => explore params t)
  1.1284 +          end
  1.1285 +      end;
  1.1286 +
  1.1287 +    fun massage_equality explore (params as {bound_Us, bound_Ts, ...})
  1.1288 +          (t as Const (@{const_name HOL.eq}, _) $ t1 $ t2) =
  1.1289 +        let
  1.1290 +          val check_is_VLeaf =
  1.1291 +            not o (Term.exists_subterm (fn t => t aconv CLeaf orelse t aconv Oper));
  1.1292 +
  1.1293 +          fun try_pattern_matching (fun_t, arg_ts) t =
  1.1294 +            (case as_member_of pattern_ctrs fun_t of
  1.1295 +              SOME (disc, sels) =>
  1.1296 +              let val t' = typ_before explore params t in
  1.1297 +                if fastype_of1 (bound_Us, t') = YpreT then
  1.1298 +                  let
  1.1299 +                    val arg_ts' = map (typ_before explore params) arg_ts;
  1.1300 +                    val sels_t' = map (fn sel => betapply (sel, t')) sels;
  1.1301 +                    val Ts = map (curry fastype_of1 bound_Us) arg_ts';
  1.1302 +                    val Us = map (curry fastype_of1 bound_Us) sels_t';
  1.1303 +                    val arg_ts' = map2 encapsulate (map2 (update_UT params) Us Ts) arg_ts';
  1.1304 +                  in
  1.1305 +                    if forall check_is_VLeaf arg_ts' then
  1.1306 +                      SOME (Library.foldl1 HOLogic.mk_conj
  1.1307 +                        (betapply (disc, t') :: (map HOLogic.mk_eq (arg_ts' ~~ sels_t'))))
  1.1308 +                    else
  1.1309 +                      NONE
  1.1310 +                  end
  1.1311 +                else
  1.1312 +                  NONE
  1.1313 +              end
  1.1314 +            | NONE => NONE);
  1.1315 +        in
  1.1316 +          (case try_pattern_matching (strip_comb t1) t2 of
  1.1317 +            SOME cond => cond
  1.1318 +          | NONE => (case try_pattern_matching (strip_comb t2) t1 of
  1.1319 +              SOME cond => cond
  1.1320 +            | NONE =>
  1.1321 +              let
  1.1322 +                val T = fastype_of1 (bound_Ts, t1);
  1.1323 +                val params' = build_params bound_Us bound_Ts T;
  1.1324 +                val t1' = explore params' t1;
  1.1325 +                val t2' = explore params' t2;
  1.1326 +              in
  1.1327 +                if fastype_of1 (bound_Us, t1') = T andalso fastype_of1 (bound_Us, t2') = T then
  1.1328 +                  HOLogic.mk_eq (t1', t2')
  1.1329 +                else
  1.1330 +                  error ("Unsupported condition: " ^ quote (Syntax.string_of_term lthy t))
  1.1331 +              end))
  1.1332 +        end
  1.1333 +      | massage_equality explore params t = explore params t;
  1.1334 +
  1.1335 +    fun infer_types (TVar _) (TVar _) = []
  1.1336 +      | infer_types (U as TVar _) T = [(U, T)]
  1.1337 +      | infer_types (Type (s', Us)) (Type (s, Ts)) =
  1.1338 +        if s' = s then flat (map2 infer_types Us Ts) else []
  1.1339 +      | infer_types _ _ = [];
  1.1340 +
  1.1341 +    fun group_by_fst associations [] = associations
  1.1342 +      | group_by_fst associations ((a, b) :: r) = group_by_fst (add_association a b associations) r
  1.1343 +    and add_association a b [] = [(a, [b])]
  1.1344 +      | add_association a b ((c, d) :: r) =
  1.1345 +        if a = c then (c, b :: d) :: r
  1.1346 +        else (c, d) :: (add_association a b r);
  1.1347 +
  1.1348 +    fun new_TVar known_TVars =
  1.1349 +      Name.invent_list (map (fst o fst o dest_TVar) known_TVars) "x" 1
  1.1350 +      |> (fn [s] => TVar ((s, 0), []));
  1.1351 +
  1.1352 +    fun instantiate_type inferred_types =
  1.1353 +      Term.typ_subst_TVars (map (apfst (fst o dest_TVar)) inferred_types);
  1.1354 +
  1.1355 +    fun chose_unknown_TVar (T as TVar _) = SOME T
  1.1356 +      | chose_unknown_TVar (Type (_, Ts)) =
  1.1357 +        fold (curry merge_options) (map chose_unknown_TVar Ts) NONE
  1.1358 +      | chose_unknown_TVar _ = NONE;
  1.1359 +
  1.1360 +    (* The function under definition might not be defined yet when this is queried. *)
  1.1361 +    fun maybe_const_type ctxt (s, T) =
  1.1362 +      Sign.const_type (Proof_Context.theory_of ctxt) s |> the_default T;
  1.1363 +
  1.1364 +    fun massage_polymorphic_const explore (params as {bound_Us, ...}) t =
  1.1365 +      let val (fun_t, arg_ts) = strip_comb t in
  1.1366 +        (case fun_t of
  1.1367 +          Const (fun_x as (s, fun_T)) =>
  1.1368 +          let val general_T = maybe_const_type lthy fun_x in
  1.1369 +            if contains_res_T (body_type general_T) orelse is_constant t then
  1.1370 +              explore params t
  1.1371 +            else
  1.1372 +              let
  1.1373 +                val inferred_types = infer_types general_T fun_T;
  1.1374 +
  1.1375 +                fun prepare_skeleton [] _ = []
  1.1376 +                  | prepare_skeleton ((T, U) :: inferred_types) As =
  1.1377 +                    let
  1.1378 +                      fun schematize_res_T U As =
  1.1379 +                        if U = res_T then
  1.1380 +                          let val A = new_TVar As in
  1.1381 +                            (A, A :: As)
  1.1382 +                          end
  1.1383 +                        else
  1.1384 +                          (case U of
  1.1385 +                            Type (s, Us) => fold_map schematize_res_T Us As |>> curry Type s
  1.1386 +                          | _ => (U, As));
  1.1387 +
  1.1388 +                      val (U', As') = schematize_res_T U As;
  1.1389 +                    in
  1.1390 +                      (T, U') :: (prepare_skeleton inferred_types As')
  1.1391 +                    end;
  1.1392 +
  1.1393 +                val inferred_types' = prepare_skeleton inferred_types (map fst inferred_types);
  1.1394 +                val skeleton_T = instantiate_type inferred_types' general_T;
  1.1395 +
  1.1396 +                fun explore_if_possible (exp_arg as (_, true)) _ = exp_arg
  1.1397 +                  | explore_if_possible (exp_arg as (arg, false)) arg_T =
  1.1398 +                    if exists (exists_subtype is_TVar) (binder_types arg_T) then exp_arg
  1.1399 +                    else (typ_before (explore_fun (binder_types arg_T) explore) params arg, true);
  1.1400 +
  1.1401 +                fun collect_inferred_types [] _ = []
  1.1402 +                  | collect_inferred_types ((arg, explored) :: exp_arg_ts) (arg_T :: arg_Ts) =
  1.1403 +                    (if explored then infer_types arg_T (fastype_of1 (bound_Us, arg)) else []) @
  1.1404 +                    collect_inferred_types exp_arg_ts arg_Ts;
  1.1405 +
  1.1406 +                fun propagate exp_arg_ts skeleton_T =
  1.1407 +                  let
  1.1408 +                    val arg_gen_Ts = binder_types skeleton_T;
  1.1409 +                    val exp_arg_ts = map2 explore_if_possible exp_arg_ts arg_gen_Ts;
  1.1410 +                    val inferred_types = collect_inferred_types exp_arg_ts arg_gen_Ts
  1.1411 +                      |> group_by_fst []
  1.1412 +                      |> map (apsnd (deduce_according_type ssig_T));
  1.1413 +                  in
  1.1414 +                    (exp_arg_ts, instantiate_type inferred_types skeleton_T)
  1.1415 +                  end;
  1.1416 +
  1.1417 +                val remaining_to_be_explored = filter_out snd #> length;
  1.1418 +
  1.1419 +                fun try_exploring_args exp_arg_ts skeleton_T =
  1.1420 +                  let
  1.1421 +                    val n = remaining_to_be_explored exp_arg_ts;
  1.1422 +                    val (exp_arg_ts', skeleton_T') = propagate exp_arg_ts skeleton_T;
  1.1423 +                    val n' = remaining_to_be_explored exp_arg_ts';
  1.1424 +
  1.1425 +                    fun try_instantiating A T =
  1.1426 +                      try (try_exploring_args exp_arg_ts') (instantiate_type [(A, T)] skeleton_T');
  1.1427 +                  in
  1.1428 +                    if n' = 0 then
  1.1429 +                      SOME (exp_arg_ts', skeleton_T')
  1.1430 +                    else if n = n' then
  1.1431 +                      if exists_subtype is_TVar skeleton_T' then
  1.1432 +                        let val SOME A = chose_unknown_TVar skeleton_T' in
  1.1433 +                          (case try_instantiating A ssig_T of
  1.1434 +                            SOME result => result
  1.1435 +                          | NONE => (case try_instantiating A YpreT of
  1.1436 +                              SOME result => result
  1.1437 +                            | NONE => (case try_instantiating A res_T of
  1.1438 +                                SOME result => result
  1.1439 +                              | NONE => NONE)))
  1.1440 +                        end
  1.1441 +                      else
  1.1442 +                        NONE
  1.1443 +                    else
  1.1444 +                      try_exploring_args exp_arg_ts' skeleton_T'
  1.1445 +                  end;
  1.1446 +              in
  1.1447 +                (case try_exploring_args (map (fn arg => (arg, false)) arg_ts) skeleton_T of
  1.1448 +                  SOME (exp_arg_ts, fun_U) =>
  1.1449 +                  let
  1.1450 +                    val arg_ts' = map fst exp_arg_ts;
  1.1451 +                    val fun_t' = Const (s, fun_U);
  1.1452 +                    val t' = build_function_after_encapsulation fun_t fun_t' params arg_ts arg_ts';
  1.1453 +                  in
  1.1454 +                    (case try type_of1 (bound_Us, t') of
  1.1455 +                      SOME _ =>
  1.1456 +                      (if fun_T = fun_U orelse is_special_parametric_const (s, fun_T) then ()
  1.1457 +                       else add_parametric_const s general_T fun_T fun_U;
  1.1458 +                       t')
  1.1459 +                    | NONE => explore params t)
  1.1460 +                  end
  1.1461 +                | NONE => explore params t)
  1.1462 +              end
  1.1463 +          end
  1.1464 +        | _ => explore params t)
  1.1465 +      end;
  1.1466 +
  1.1467 +    fun massage_rho explore =
  1.1468 +      massage_star [massage_let, massage_if explore_cond, massage_case, massage_fun, massage_comp,
  1.1469 +          massage_map, massage_ctr, massage_sel, massage_disc, massage_equality,
  1.1470 +          massage_polymorphic_const]
  1.1471 +        explore
  1.1472 +    and massage_case explore (params as {bound_Ts, bound_Us, ...}) t =
  1.1473 +      (case strip_comb t of
  1.1474 +        (casex as Const (case_x as (c, _)), args as _ :: _ :: _) =>
  1.1475 +        (case try strip_fun_type (maybe_const_type lthy case_x) of
  1.1476 +          SOME (gen_branch_Ts, gen_body_fun_T) =>
  1.1477 +          let
  1.1478 +            val gen_branch_ms = map num_binder_types gen_branch_Ts;
  1.1479 +            val n = length gen_branch_ms;
  1.1480 +            val (branches, obj_leftovers) = chop n args;
  1.1481 +          in
  1.1482 +            if n < length args then
  1.1483 +              (case gen_body_fun_T of
  1.1484 +                Type (_, [Type (T_name, _), _]) =>
  1.1485 +                if case_of lthy T_name = SOME c then
  1.1486 +                  let
  1.1487 +                    val brancheTs = binder_fun_types (fastype_of1 (bound_Ts, casex));
  1.1488 +                    val obj_leftover_Ts = map (curry fastype_of1 bound_Ts) obj_leftovers;
  1.1489 +                    val obj_leftovers' =
  1.1490 +                      if is_constant (hd obj_leftovers) then
  1.1491 +                        obj_leftovers
  1.1492 +                      else
  1.1493 +                        (obj_leftover_Ts, obj_leftovers)
  1.1494 +                        |>> map (build_params bound_Us bound_Ts)
  1.1495 +                        |> op ~~
  1.1496 +                        |> map (uncurry explore_inner);
  1.1497 +                    val obj_leftoverUs = obj_leftovers' |> map (curry fastype_of1 bound_Us);
  1.1498 +
  1.1499 +                    val _ = is_valid_case_argumentT (hd obj_leftoverUs) orelse
  1.1500 +                      error (quote (Syntax.string_of_term lthy (hd obj_leftovers)) ^
  1.1501 +                        " is not a valid case argument");
  1.1502 +
  1.1503 +                    val Us = obj_leftoverUs |> hd |> dest_Type |> snd;
  1.1504 +
  1.1505 +                    val branche_binderUss =
  1.1506 +                      (if hd obj_leftoverUs = YpreT then mk_case HOLogic.boolT
  1.1507 +                       else update_case Us HOLogic.boolT casex)
  1.1508 +                      |> fastype_of
  1.1509 +                      |> binder_fun_types
  1.1510 +                      |> map binder_types;
  1.1511 +                    val b_params = map (build_params bound_Us bound_Ts) brancheTs;
  1.1512 +
  1.1513 +                    val branches' = branches
  1.1514 +                      |> @{map 4} explore_fun branche_binderUss (replicate n explore) b_params;
  1.1515 +                    val brancheUs = map (curry fastype_of1 bound_Us) branches';
  1.1516 +                    val U = deduce_according_type (body_type (hd brancheTs))
  1.1517 +                      (map body_type brancheUs);
  1.1518 +                    val casex' =
  1.1519 +                      if hd obj_leftoverUs = YpreT then mk_case U else update_case Us U casex;
  1.1520 +                  in
  1.1521 +                    build_function_after_encapsulation casex casex' params
  1.1522 +                      (branches @ obj_leftovers) (branches' @ obj_leftovers')
  1.1523 +                  end
  1.1524 +                else
  1.1525 +                  explore params t
  1.1526 +              | _ => explore params t)
  1.1527 +            else
  1.1528 +              explore params t
  1.1529 +          end
  1.1530 +        | NONE => explore params t)
  1.1531 +      | _ => explore params t)
  1.1532 +    and explore_cond params t =
  1.1533 +      if has_self_call t then
  1.1534 +        error ("Unallowed corecursive call in condition " ^ quote (Syntax.string_of_term lthy t))
  1.1535 +      else
  1.1536 +        explore_inner params t
  1.1537 +    and explore_inner params t =
  1.1538 +      massage_rho explore_inner_general params t
  1.1539 +    and explore_inner_general (params as {bound_Us, bound_Ts, T, ...}) t =
  1.1540 +      let val (fun_t, arg_ts) = strip_comb t in
  1.1541 +        if is_constant t then
  1.1542 +          t
  1.1543 +        else
  1.1544 +          (case (as_member_of discs fun_t,
  1.1545 +              length arg_ts = 1 andalso has_res_T bound_Ts (the_single arg_ts)) of
  1.1546 +            (SOME disc', true) =>
  1.1547 +            let
  1.1548 +              val arg' = explore_inner params (the_single arg_ts);
  1.1549 +              val arg_U = fastype_of1 (bound_Us, arg');
  1.1550 +            in
  1.1551 +              if arg_U = res_T then
  1.1552 +                fun_t $ arg'
  1.1553 +              else if arg_U = YpreT then
  1.1554 +                disc' $ arg'
  1.1555 +              else
  1.1556 +                error ("Discriminator " ^ quote (Syntax.string_of_term lthy fun_t) ^
  1.1557 +                  " cannot be applied to non-lhs argument " ^
  1.1558 +                  quote (Syntax.string_of_term lthy (hd arg_ts)))
  1.1559 +            end
  1.1560 +          | _ =>
  1.1561 +            (case as_member_of sels fun_t of
  1.1562 +              SOME sel' =>
  1.1563 +              let
  1.1564 +                val arg_ts' = map (explore_inner params) arg_ts;
  1.1565 +                val arg_U = fastype_of1 (bound_Us, hd arg_ts');
  1.1566 +              in
  1.1567 +                if arg_U = res_T then
  1.1568 +                  build_function_after_encapsulation fun_t fun_t params arg_ts arg_ts'
  1.1569 +                else if arg_U = YpreT then
  1.1570 +                  build_function_after_encapsulation fun_t sel' params arg_ts arg_ts'
  1.1571 +                else
  1.1572 +                  error ("Selector " ^ quote (Syntax.string_of_term lthy fun_t) ^
  1.1573 +                    " cannot be applied to non-lhs argument " ^
  1.1574 +                    quote (Syntax.string_of_term lthy (hd arg_ts)))
  1.1575 +              end
  1.1576 +            | NONE =>
  1.1577 +              (case as_member_of friends fun_t of
  1.1578 +                SOME (_, friend') =>
  1.1579 +                rebuild_function_after_exploration fun_t friend' explore_inner params arg_ts
  1.1580 +                |> curry (op $) Oper
  1.1581 +              | NONE =>
  1.1582 +                (case as_member_of ctr_guards fun_t of
  1.1583 +                  SOME ctr_guard' =>
  1.1584 +                  rebuild_function_after_exploration fun_t ctr_guard' explore_inner params arg_ts
  1.1585 +                  |> curry (op $) ctr_wrapper
  1.1586 +                  |> curry (op $) Oper
  1.1587 +                | NONE =>
  1.1588 +                  if is_Bound fun_t then
  1.1589 +                    rebuild_function_after_exploration fun_t fun_t explore_inner params arg_ts
  1.1590 +                  else if is_Free fun_t then
  1.1591 +                    let val fun_t' = map_types (fpT_to YpreT) fun_t in
  1.1592 +                      rebuild_function_after_exploration fun_t fun_t' explore_inner params arg_ts
  1.1593 +                    end
  1.1594 +                  else if T = res_T then
  1.1595 +                    error (quote (Syntax.string_of_term lthy fun_t) ^
  1.1596 +                      " not polymorphic enough to be applied like this and no friend")
  1.1597 +                  else
  1.1598 +                    error (quote (Syntax.string_of_term lthy fun_t) ^
  1.1599 +                      " not polymorphic enough to be applied like this")))))
  1.1600 +      end;
  1.1601 +
  1.1602 +    fun explore_ctr params t =
  1.1603 +      massage_rho explore_ctr_general params t
  1.1604 +    and explore_ctr_general params t =
  1.1605 +      let
  1.1606 +        val (fun_t, arg_ts) = strip_comb t;
  1.1607 +        val ctr_opt = as_member_of ctr_guards fun_t;
  1.1608 +      in
  1.1609 +        if is_some ctr_opt then
  1.1610 +          rebuild_function_after_exploration fun_t (the ctr_opt) explore_inner params arg_ts
  1.1611 +        else
  1.1612 +          error ("Constructor expected on right-hand side, " ^
  1.1613 +            quote (Syntax.string_of_term lthy fun_t) ^ " found instead")
  1.1614 +      end;
  1.1615 +
  1.1616 +    val rho_rhs = rhs
  1.1617 +      |> explore_ctr (build_params [] [] (fastype_of rhs))
  1.1618 +      |> abs_tuple_balanced (map (map_types (fpT_to YpreT)) lhs_args)
  1.1619 +      |> unfold_id_bnf_etc lthy;
  1.1620 +  in
  1.1621 +    lthy
  1.1622 +    |> define_const false b version rhoN rho_rhs
  1.1623 +    |>> pair (!parametric_consts, rho_rhs)
  1.1624 +  end;
  1.1625 +
  1.1626 +fun mk_rho_parametricity_goal ctxt Y Z preT ssig_T dead_pre_rel dead_k_rel dead_ssig_rel rho_rhs =
  1.1627 +  let
  1.1628 +    val YpreT = HOLogic.mk_prodT (Y, preT);
  1.1629 +    val ZpreT = Tsubst Y Z YpreT;
  1.1630 +    val ssigZ_T = Tsubst Y Z ssig_T;
  1.1631 +
  1.1632 +    val dead_pre_rel' = Term.subst_atomic_types [(Y, ssig_T), (Z, ssigZ_T)] dead_pre_rel;
  1.1633 +    val dead_k_rel' = Term.subst_atomic_types [(Y, YpreT), (Z, ZpreT)] dead_k_rel;
  1.1634 +
  1.1635 +    val (R, _) = ctxt
  1.1636 +      |> yield_singleton (mk_Frees "R") (mk_pred2T Y Z);
  1.1637 +
  1.1638 +    val rho_rel = mk_rel_fun (dead_k_rel' $ mk_rel_prod R (dead_pre_rel $ R))
  1.1639 +      (dead_pre_rel' $ (dead_ssig_rel $ R));
  1.1640 +    val rho_rhsZ = substT Y Z rho_rhs;
  1.1641 +  in
  1.1642 +    HOLogic.mk_Trueprop (rho_rel $ rho_rhs $ rho_rhsZ)
  1.1643 +  end;
  1.1644 +
  1.1645 +fun extract_rho_return_transfer_goals fun_b version dead_pre_bnf dead_k_bnf Y Z preT fun_T k_T
  1.1646 +    ssig_T ssig_fp_sugar friend_parse_info fun_t parsed_eq lthy =
  1.1647 +  let
  1.1648 +    val Type (fpT_name, _) = body_type fun_T;
  1.1649 +
  1.1650 +    fun mk_rel T bnf =
  1.1651 +      let
  1.1652 +        val ZT = Tsubst Y Z T;
  1.1653 +        val rel_T = mk_predT [mk_pred2T Y Z, T, ZT];
  1.1654 +      in
  1.1655 +        enforce_type lthy I rel_T (rel_of_bnf bnf)
  1.1656 +      end;
  1.1657 +
  1.1658 +    val ssig_bnf = #fp_bnf ssig_fp_sugar;
  1.1659 +
  1.1660 +    val (dead_ssig_bnf, lthy) = bnf_kill_all_but 1 ssig_bnf lthy;
  1.1661 +
  1.1662 +    val dead_pre_rel = mk_rel preT dead_pre_bnf;
  1.1663 +    val dead_k_rel = mk_rel k_T dead_k_bnf;
  1.1664 +    val dead_ssig_rel = mk_rel ssig_T dead_ssig_bnf;
  1.1665 +
  1.1666 +    val (((parametric_consts, rho_rhs), rho_data), lthy) =
  1.1667 +      extract_rho_from_equation friend_parse_info fun_b version Y preT ssig_T fun_t parsed_eq lthy;
  1.1668 +
  1.1669 +    val const_transfer_goals = map (mk_const_transfer_goal lthy) parametric_consts;
  1.1670 +
  1.1671 +    val rho_transfer_goal =
  1.1672 +      mk_rho_parametricity_goal lthy Y Z preT ssig_T dead_pre_rel dead_k_rel dead_ssig_rel rho_rhs;
  1.1673 +  in
  1.1674 +    ((rho_data, (const_transfer_goals, rho_transfer_goal)), lthy)
  1.1675 +  end;
  1.1676 +
  1.1677 +fun explore_corec_equation ctxt could_be_friend friend fun_name fun_free
  1.1678 +    {outer_buffer, ctr_guards, inner_buffer} res_T (args, rhs) =
  1.1679 +  let
  1.1680 +    val is_self_call = curry (op aconv) fun_free;
  1.1681 +    val has_self_call = Term.exists_subterm is_self_call;
  1.1682 +
  1.1683 +    val outer_ssig_T = body_type (fastype_of (#Oper outer_buffer));
  1.1684 +
  1.1685 +    fun inner_fp_of (Free (s, _)) =
  1.1686 +      Free (s ^ inner_fp_suffix, mk_tupleT_balanced (map fastype_of args) --> outer_ssig_T);
  1.1687 +
  1.1688 +    fun build_params bound_Ts U T =
  1.1689 +      {bound_Us = bound_Ts, bound_Ts = bound_Ts, U = U, T = T};
  1.1690 +
  1.1691 +    fun rebuild_function_after_exploration new_fn explore {bound_Ts, ...} arg_ts =
  1.1692 +      let
  1.1693 +        val binder_types_old_fn = map (curry fastype_of1 bound_Ts) arg_ts;
  1.1694 +        val binder_types_new_fn = new_fn
  1.1695 +          |> binder_types o (curry fastype_of1 bound_Ts)
  1.1696 +          |> take (length binder_types_old_fn);
  1.1697 +        val paramss =
  1.1698 +          map2 (build_params bound_Ts) binder_types_new_fn binder_types_old_fn;
  1.1699 +      in
  1.1700 +        map2 explore paramss arg_ts
  1.1701 +        |> curry list_comb new_fn
  1.1702 +      end;
  1.1703 +
  1.1704 +    fun massage_map_corec explore {bound_Ts, U, T, ...} t =
  1.1705 +      let val explore' = explore ooo build_params in
  1.1706 +        massage_nested_corec_call ctxt has_self_call explore' explore' bound_Ts U T t
  1.1707 +      end;
  1.1708 +
  1.1709 +    fun massage_comp explore params t =
  1.1710 +      (case strip_comb t of
  1.1711 +        (Const (@{const_name comp}, _), f1 :: f2 :: args) =>
  1.1712 +        explore params (betapply (f1, (betapplys (f2, args))))
  1.1713 +      | _ => explore params t);
  1.1714 +
  1.1715 +    fun massage_fun explore (params as {bound_Us, bound_Ts, U, T}) t =
  1.1716 +      if can dest_funT T then
  1.1717 +        let
  1.1718 +          val arg_T = domain_type T;
  1.1719 +          val arg_name = the_default Name.uu (try (fn (Abs (s, _, _)) => s) t);
  1.1720 +        in
  1.1721 +          add_boundvar t
  1.1722 +          |> explore {bound_Us = arg_T :: bound_Us, bound_Ts = arg_T :: bound_Ts,
  1.1723 +             U = range_type U, T = range_type T}
  1.1724 +          |> (fn t => Abs (arg_name, arg_T, t))
  1.1725 +        end
  1.1726 +      else
  1.1727 +        explore params t
  1.1728 +
  1.1729 +    fun massage_let_if_case_corec explore {bound_Ts, U, T, ...} t =
  1.1730 +      massage_let_if_case ctxt has_self_call (fn bound_Ts => explore (build_params bound_Ts U T))
  1.1731 +        (K (unexpected_corec_call ctxt [t])) (K (unsupported_case_around_corec_call ctxt [t]))
  1.1732 +        bound_Ts t;
  1.1733 +
  1.1734 +    val massage_map_let_if_case =
  1.1735 +      massage_star [massage_map_corec, massage_fun, massage_comp, massage_let_if_case_corec];
  1.1736 +
  1.1737 +    fun explore_arg _ t =
  1.1738 +      if has_self_call t then
  1.1739 +        error (quote (Syntax.string_of_term ctxt t) ^ " contains a nested corecursive call" ^
  1.1740 +          (if could_be_friend then " (try specifying \"(friend)\")" else ""))
  1.1741 +      else
  1.1742 +        t;
  1.1743 +
  1.1744 +    fun explore_inner params t =
  1.1745 +      massage_map_let_if_case explore_inner_general params t
  1.1746 +    and explore_inner_general (params as {bound_Ts, T, ...}) t =
  1.1747 +      if T = res_T then
  1.1748 +        let val (f_t, arg_ts) = strip_comb t in
  1.1749 +          if has_self_call t then
  1.1750 +            (case as_member_of (#friends inner_buffer) f_t of
  1.1751 +              SOME (_, friend') =>
  1.1752 +              rebuild_function_after_exploration friend' explore_inner params arg_ts
  1.1753 +              |> curry (op $) (#Oper inner_buffer)
  1.1754 +            | NONE =>
  1.1755 +              (case as_member_of ctr_guards f_t of
  1.1756 +                SOME ctr_guard' =>
  1.1757 +                rebuild_function_after_exploration ctr_guard' explore_inner params arg_ts
  1.1758 +                |> curry (op $) (#ctr_wrapper inner_buffer)
  1.1759 +                |> curry (op $) (#Oper inner_buffer)
  1.1760 +              | NONE =>
  1.1761 +                if is_self_call f_t then
  1.1762 +                  if friend andalso exists has_self_call arg_ts then
  1.1763 +                    (case Symtab.lookup (#friends inner_buffer) fun_name of
  1.1764 +                      SOME (_, friend') =>
  1.1765 +                      rebuild_function_after_exploration friend' explore_inner params arg_ts
  1.1766 +                      |> curry (op $) (#Oper inner_buffer))
  1.1767 +                  else
  1.1768 +                    let val arg_Ts = binder_types (fastype_of1 (bound_Ts, f_t)) in
  1.1769 +                      map2 explore_arg (map2 (update_UT params) arg_Ts arg_Ts) arg_ts
  1.1770 +                      |> mk_tuple1_balanced bound_Ts
  1.1771 +                      |> curry (op $) (#VLeaf inner_buffer)
  1.1772 +                    end
  1.1773 +                else
  1.1774 +                  error (quote (Syntax.string_of_term ctxt f_t) ^ " not registered as friend")))
  1.1775 +          else
  1.1776 +            #CLeaf inner_buffer $ t
  1.1777 +        end
  1.1778 +      else if has_self_call t then
  1.1779 +        error (quote (Syntax.string_of_term ctxt t) ^ " contains a corecursive call but has type " ^
  1.1780 +          quote (Syntax.string_of_typ ctxt T))
  1.1781 +      else
  1.1782 +        explore_nested ctxt explore_inner_general params t;
  1.1783 +
  1.1784 +    fun explore_outer params t =
  1.1785 +      massage_map_let_if_case explore_outer_general params t
  1.1786 +    and explore_outer_general (params as {bound_Ts, T, ...}) t =
  1.1787 +      if T = res_T then
  1.1788 +        let val (f_t, arg_ts) = strip_comb t in
  1.1789 +          (case as_member_of ctr_guards f_t of
  1.1790 +            SOME ctr_guard' =>
  1.1791 +            rebuild_function_after_exploration ctr_guard' explore_inner params arg_ts
  1.1792 +            |> curry (op $) (#VLeaf outer_buffer)
  1.1793 +          | NONE =>
  1.1794 +            if not (has_self_call t) then
  1.1795 +              t
  1.1796 +              |> expand_to_ctr_term ctxt T
  1.1797 +              |> massage_let_if_case_corec explore_outer_general params
  1.1798 +            else
  1.1799 +              (case as_member_of (#friends outer_buffer) f_t of
  1.1800 +                SOME (_, friend') =>
  1.1801 +                rebuild_function_after_exploration friend' explore_outer params arg_ts
  1.1802 +                |> curry (op $) (#Oper outer_buffer)
  1.1803 +              | NONE =>
  1.1804 +                if is_self_call f_t then
  1.1805 +                  let val arg_Ts = binder_types (fastype_of1 (bound_Ts, f_t)) in
  1.1806 +                    map2 explore_arg (map2 (update_UT params) arg_Ts arg_Ts) arg_ts
  1.1807 +                    |> mk_tuple1_balanced bound_Ts
  1.1808 +                    |> curry (op $) (inner_fp_of f_t)
  1.1809 +                  end
  1.1810 +                else
  1.1811 +                  error (quote (Syntax.string_of_term ctxt f_t) ^ " not registered as friend")))
  1.1812 +        end
  1.1813 +      else if has_self_call t then
  1.1814 +        error (quote (Syntax.string_of_term ctxt t) ^ " contains a corecursive call but has type " ^
  1.1815 +          quote (Syntax.string_of_typ ctxt T))
  1.1816 +      else
  1.1817 +        explore_nested ctxt explore_outer_general params t;
  1.1818 +  in
  1.1819 +    (args, rhs
  1.1820 +      |> explore_outer (build_params [] outer_ssig_T res_T)
  1.1821 +      |> abs_tuple_balanced args)
  1.1822 +  end;
  1.1823 +
  1.1824 +fun mk_corec_fun_def_rhs ctxt arg_Ts corecUU0 corecUU_arg =
  1.1825 +  let val corecUU = enforce_type ctxt domain_type (fastype_of corecUU_arg) corecUU0 in
  1.1826 +    abs_curried_balanced arg_Ts (corecUU $ unfold_id_bnf_etc ctxt corecUU_arg)
  1.1827 +  end;
  1.1828 +
  1.1829 +fun get_options ctxt opts =
  1.1830 +  let
  1.1831 +    val plugins = get_first (fn Plugins_Option f => SOME (f ctxt) | _ => NONE) (rev opts)
  1.1832 +      |> the_default Plugin_Name.default_filter;
  1.1833 +    val friend = exists (can (fn Friend_Option => ())) opts;
  1.1834 +    val transfer = exists (can (fn Transfer_Option => ())) opts;
  1.1835 +  in
  1.1836 +    (plugins, friend, transfer)
  1.1837 +  end;
  1.1838 +
  1.1839 +fun add_function name parsed_eq lthy =
  1.1840 +  let
  1.1841 +    fun pat_completeness_auto ctxt =
  1.1842 +      Pat_Completeness.pat_completeness_tac ctxt 1 THEN auto_tac ctxt;
  1.1843 +
  1.1844 +    val ({defname, pelims = [[pelim]], pinducts = [pinduct], psimps = [psimp], ...}, lthy) =
  1.1845 +      Function.add_function [(Binding.concealed (Binding.name name), NONE, NoSyn)]
  1.1846 +        [(apfst Binding.concealed Attrib.empty_binding, parsed_eq)]
  1.1847 +        Function_Common.default_config pat_completeness_auto lthy;
  1.1848 +  in
  1.1849 +    ((defname, (pelim, pinduct, psimp)), lthy)
  1.1850 +  end;
  1.1851 +
  1.1852 +fun build_corecUU_arg_and_goals prove_termin (Free (fun_base_name, _)) (arg_ts, explored_rhs) lthy =
  1.1853 +  let
  1.1854 +    val inner_fp_name0 = fun_base_name ^ inner_fp_suffix;
  1.1855 +    val inner_fp_free = Free (inner_fp_name0, fastype_of explored_rhs);
  1.1856 +  in
  1.1857 +    if Term.exists_subterm (curry (op aconv) inner_fp_free) explored_rhs then
  1.1858 +      let
  1.1859 +        val arg = mk_tuple_balanced arg_ts;
  1.1860 +        val inner_fp_eq =
  1.1861 +          mk_Trueprop_eq (betapply (inner_fp_free, arg), betapply (explored_rhs, arg));
  1.1862 +
  1.1863 +        val ((inner_fp_name, (pelim, pinduct, psimp)), lthy') =
  1.1864 +          add_function inner_fp_name0 inner_fp_eq lthy;
  1.1865 +
  1.1866 +        fun mk_triple elim induct simp = ([elim], [induct], [simp]);
  1.1867 +
  1.1868 +        fun prepare_termin () =
  1.1869 +          let
  1.1870 +            val {goal, ...} = Proof.goal (Function.termination NONE lthy');
  1.1871 +            val termin_goal = goal |> Thm.concl_of |> Logic.unprotect |> Envir.beta_eta_contract;
  1.1872 +          in
  1.1873 +            (lthy', (mk_triple pelim pinduct psimp, [termin_goal]))
  1.1874 +          end;
  1.1875 +
  1.1876 +        val (lthy'', (inner_fp_triple, termin_goals)) =
  1.1877 +          if prove_termin then
  1.1878 +            (case try (Function.prove_termination NONE
  1.1879 +                (Function_Common.termination_prover_tac true lthy')) lthy' of
  1.1880 +              NONE => prepare_termin ()
  1.1881 +            | SOME ({elims = SOME [[elim]], inducts = SOME [induct], simps = SOME [simp], ...},
  1.1882 +                lthy'') =>
  1.1883 +              (lthy'', (mk_triple elim induct simp, [])))
  1.1884 +          else
  1.1885 +            prepare_termin ();
  1.1886 +
  1.1887 +        val inner_fp_const = (inner_fp_name, fastype_of explored_rhs)
  1.1888 +          |>> Proof_Context.read_const {proper = true, strict = false} lthy'
  1.1889 +          |> (fn (Const (s, _), T) => Const (s, T));
  1.1890 +      in
  1.1891 +        (((inner_fp_triple, termin_goals), inner_fp_const), lthy'')
  1.1892 +      end
  1.1893 +    else
  1.1894 +      (((([], [], []), []), explored_rhs), lthy)
  1.1895 +  end;
  1.1896 +
  1.1897 +fun derive_eq_corecUU ctxt {sig_fp_sugars, ssig_fp_sugar, eval, corecUU, eval_simps,
  1.1898 +      all_algLam_algs, corecUU_unique, ...}
  1.1899 +    fun_t corecUU_arg fun_code =
  1.1900 +  let
  1.1901 +    val fun_T = fastype_of fun_t;
  1.1902 +    val (arg_Ts, Type (fpT_name, _)) = strip_type fun_T;
  1.1903 +    val num_args = length arg_Ts;
  1.1904 +
  1.1905 +    val SOME {pre_bnf, fp_bnf, absT_info, fp_nesting_bnfs, live_nesting_bnfs, fp_ctr_sugar, ...} =
  1.1906 +      fp_sugar_of ctxt fpT_name;
  1.1907 +    val SOME {case_trivial, ...} = codatatype_extra_of ctxt fpT_name;
  1.1908 +
  1.1909 +    val ctr_sugar = #ctr_sugar fp_ctr_sugar;
  1.1910 +    val pre_map_def = map_def_of_bnf pre_bnf;
  1.1911 +    val abs_inverse = #abs_inverse absT_info;
  1.1912 +    val ctr_defs = #ctr_defs fp_ctr_sugar;
  1.1913 +    val case_eq_ifs = #case_eq_ifs ctr_sugar @ case_eq_if_thms_of_term ctxt (Thm.prop_of fun_code);
  1.1914 +    val all_sig_map_thms = maps (#map_thms o #fp_bnf_sugar) sig_fp_sugars;
  1.1915 +
  1.1916 +    val fp_map_ident = map_ident_of_bnf fp_bnf;
  1.1917 +    val fpsig_nesting_bnfs = fp_nesting_bnfs @ maps #live_nesting_bnfs sig_fp_sugars;
  1.1918 +    val fpsig_nesting_T_names = map (fst o dest_Type o T_of_bnf) fpsig_nesting_bnfs;
  1.1919 +    val fpsig_nesting_fp_sugars = map_filter (fp_sugar_of ctxt) fpsig_nesting_T_names;
  1.1920 +    val fpsig_nesting_fp_bnf_sugars = map #fp_bnf_sugar fpsig_nesting_fp_sugars;
  1.1921 +    val ssig_fp_bnf_sugar = #fp_bnf_sugar ssig_fp_sugar;
  1.1922 +    val ssig_bnf = #fp_bnf ssig_fp_sugar;
  1.1923 +    val ssig_map = map_of_bnf ssig_bnf;
  1.1924 +    val fpsig_nesting_maps = map map_of_bnf fpsig_nesting_bnfs;
  1.1925 +    val fpsig_nesting_map_ident0s = map map_ident0_of_bnf fpsig_nesting_bnfs;
  1.1926 +    val fpsig_nesting_map_comps = map map_comp_of_bnf fpsig_nesting_bnfs;
  1.1927 +    val fpsig_nesting_map_thms = maps #map_thms fpsig_nesting_fp_bnf_sugars;
  1.1928 +    val live_nesting_map_ident0s = map map_ident0_of_bnf live_nesting_bnfs;
  1.1929 +    val ssig_map_thms = #map_thms ssig_fp_bnf_sugar;
  1.1930 +    val all_algLam_alg_pointfuls = map (mk_pointful ctxt) all_algLam_algs;
  1.1931 +
  1.1932 +    val def_rhs = mk_corec_fun_def_rhs ctxt arg_Ts corecUU corecUU_arg;
  1.1933 +
  1.1934 +    val goal = mk_Trueprop_eq (fun_t, def_rhs);
  1.1935 +  in
  1.1936 +    Goal.prove_sorry ctxt [] [] goal (fn {context = ctxt, prems = _} =>
  1.1937 +      mk_eq_corecUU_tac ctxt num_args fpsig_nesting_maps ssig_map eval pre_map_def abs_inverse
  1.1938 +        fpsig_nesting_map_ident0s fpsig_nesting_map_comps fpsig_nesting_map_thms
  1.1939 +        live_nesting_map_ident0s fp_map_ident case_trivial ctr_defs case_eq_ifs all_sig_map_thms
  1.1940 +        ssig_map_thms all_algLam_alg_pointfuls (all_algrho_eqs_of ctxt) eval_simps corecUU_unique
  1.1941 +        fun_code)
  1.1942 +    |> Thm.close_derivation
  1.1943 +  end;
  1.1944 +
  1.1945 +fun derive_coinduct_cong_intros
  1.1946 +    ({fpT = fpT0 as Type (fpT_name, _), friend_names = friend_names0,
  1.1947 +      corecUU = Const (corecUU_name, _), dtor_coinduct_info as {dtor_coinduct, ...}, ...})
  1.1948 +    lthy =
  1.1949 +  let
  1.1950 +    val thy = Proof_Context.theory_of lthy;
  1.1951 +    val phi = Proof_Context.export_morphism lthy (Local_Theory.target_of lthy);
  1.1952 +
  1.1953 +    val fpT = Morphism.typ phi fpT0;
  1.1954 +    val general_fpT = body_type (Sign.the_const_type thy corecUU_name);
  1.1955 +    val most_general = Sign.typ_instance thy (general_fpT, fpT);
  1.1956 +  in
  1.1957 +    (case (most_general, coinduct_extra_of lthy corecUU_name) of
  1.1958 +      (true, SOME extra) => ((false, extra), lthy)
  1.1959 +    | _ =>
  1.1960 +      let
  1.1961 +        val ctr_names = ctr_names_of_fp_name lthy fpT_name;
  1.1962 +        val friend_names = friend_names0 |> map Long_Name.base_name |> rev;
  1.1963 +        val cong_intro_tab = derive_cong_intros lthy ctr_names friend_names dtor_coinduct_info;
  1.1964 +        val (coinduct, coinduct_attrs) = derive_coinduct lthy fpT0 dtor_coinduct;
  1.1965 +        val ((_, [coinduct]), lthy) = (* TODO check: only if most_general?*)
  1.1966 +          Local_Theory.note ((Binding.empty, coinduct_attrs), [coinduct]) lthy;
  1.1967 +
  1.1968 +        val extra = {coinduct = coinduct, coinduct_attrs = coinduct_attrs,
  1.1969 +          cong_intro_tab = cong_intro_tab};
  1.1970 +      in
  1.1971 +        ((most_general, extra), lthy |> most_general ? register_coinduct_extra corecUU_name extra)
  1.1972 +      end)
  1.1973 +  end;
  1.1974 +
  1.1975 +fun update_coinduct_cong_intross_dynamic fpT_name lthy =
  1.1976 +  let
  1.1977 +    val all_corec_infos = corec_infos_of lthy fpT_name;
  1.1978 +  in
  1.1979 +    lthy
  1.1980 +    |> fold_map (apfst snd oo derive_coinduct_cong_intros) all_corec_infos
  1.1981 +    |> snd
  1.1982 +  end;
  1.1983 +
  1.1984 +fun derive_and_update_coinduct_cong_intross [] = pair (false, [])
  1.1985 +  | derive_and_update_coinduct_cong_intross (corec_infos as {fpT = Type (fpT_name, _), ...} :: _) =
  1.1986 +    fold_map derive_coinduct_cong_intros corec_infos
  1.1987 +    #>> split_list
  1.1988 +    #> (fn ((changeds, extras), lthy) =>
  1.1989 +      if exists I changeds then
  1.1990 +        ((true, extras), lthy |> update_coinduct_cong_intross_dynamic fpT_name)
  1.1991 +      else
  1.1992 +        ((false, extras), lthy));
  1.1993 +
  1.1994 +fun prepare_corec_ursive_cmd long_cmd opts (raw_fixes, raw_eq) lthy =
  1.1995 +  let
  1.1996 +    val _ = can the_single raw_fixes orelse
  1.1997 +      error "Mutually corecursive functions not supported";
  1.1998 +
  1.1999 +    val (plugins, friend, transfer) = get_options lthy opts;
  1.2000 +    val ([((b, fun_T), mx)], [(_, eq)]) =
  1.2001 +      fst (Specification.read_spec raw_fixes [(Attrib.empty_binding, raw_eq)] lthy);
  1.2002 +
  1.2003 +    val _ = Sign.of_sort (Proof_Context.theory_of lthy) (fun_T, @{sort type}) orelse
  1.2004 +      error ("Type of " ^ Binding.print b ^ " contains top sort");
  1.2005 +
  1.2006 +    val (arg_Ts, res_T) = strip_type fun_T;
  1.2007 +    val fpT_name = (case res_T of Type (s, _) => s | _ => not_codatatype lthy res_T);
  1.2008 +    val fun_free = Free (Binding.name_of b, fun_T);
  1.2009 +    val parsed_eq = parse_corec_equation lthy [fun_free] eq;
  1.2010 +
  1.2011 +    val fun_name = Local_Theory.full_name lthy b;
  1.2012 +    val fun_t = Const (fun_name, fun_T);
  1.2013 +      (* FIXME: does this work with locales that fix variables? *)
  1.2014 +
  1.2015 +    val no_base = has_no_corec_info lthy fpT_name;
  1.2016 +    val lthy = lthy |> no_base ? setup_base fpT_name;
  1.2017 +
  1.2018 +    fun extract_rho lthy =
  1.2019 +      let
  1.2020 +        val lthy = lthy |> Variable.declare_typ fun_T;
  1.2021 +        val (prepared as (_, _, version, Y, Z, preT, k_T, ssig_T, dead_pre_bnf, dead_k_bnf, _,
  1.2022 +               ssig_fp_sugar, buffer), lthy) =
  1.2023 +          prepare_friend_corec fun_name fun_T lthy;
  1.2024 +        val friend_parse_info = friend_parse_info_of lthy arg_Ts res_T buffer;
  1.2025 +
  1.2026 +        val parsed_eq' = parsed_eq ||> subst_atomic [(fun_free, fun_t)];
  1.2027 +      in
  1.2028 +        lthy
  1.2029 +        |> extract_rho_return_transfer_goals b version dead_pre_bnf dead_k_bnf Y Z preT fun_T k_T
  1.2030 +          ssig_T ssig_fp_sugar friend_parse_info fun_t parsed_eq'
  1.2031 +        |>> pair prepared
  1.2032 +      end;
  1.2033 +
  1.2034 +    val ((prepareds, (rho_datas, transfer_goal_datas)), lthy) =
  1.2035 +      if friend then extract_rho lthy |>> (apfst single ##> (apfst single #> apsnd single))
  1.2036 +      else (([], ([], [])), lthy);
  1.2037 +
  1.2038 +    val ((buffer, corec_infos), lthy) =
  1.2039 +      if friend then
  1.2040 +        ((#13 (the_single prepareds), []), lthy)
  1.2041 +      else
  1.2042 +        corec_info_of res_T lthy
  1.2043 +        ||> no_base ? update_coinduct_cong_intross_dynamic fpT_name
  1.2044 +        |>> (fn info as {buffer, ...} => (buffer, [info]));
  1.2045 +
  1.2046 +    val corec_parse_info = corec_parse_info_of lthy arg_Ts res_T buffer;
  1.2047 +
  1.2048 +    val explored_eq =
  1.2049 +      explore_corec_equation lthy true friend fun_name fun_free corec_parse_info res_T parsed_eq;
  1.2050 +
  1.2051 +    val (((inner_fp_triple, termin_goals), corecUU_arg), lthy) =
  1.2052 +      build_corecUU_arg_and_goals (not long_cmd) fun_free explored_eq lthy;
  1.2053 +
  1.2054 +    fun def_fun (inner_fp_elims0, inner_fp_inducts0, inner_fp_simps0) const_transfers
  1.2055 +        rho_transfers_foldeds lthy =
  1.2056 +      let
  1.2057 +        fun register_friend lthy =
  1.2058 +          let
  1.2059 +            val [(old_corec_info, fp_b, version, Y, Z, _, k_T, _, _, dead_k_bnf, sig_fp_sugar,
  1.2060 +                  ssig_fp_sugar, _)] = prepareds;
  1.2061 +            val [(rho, rho_def)] = rho_datas;
  1.2062 +            val [(_, rho_transfer_goal)] = transfer_goal_datas;
  1.2063 +            val Type (fpT_name, _) = res_T;
  1.2064 +
  1.2065 +            val rho_transfer_folded =
  1.2066 +              (case rho_transfers_foldeds of
  1.2067 +                [] =>
  1.2068 +                derive_rho_transfer_folded lthy fpT_name const_transfers rho_def rho_transfer_goal
  1.2069 +              | [thm] => thm);
  1.2070 +          in
  1.2071 +            lthy
  1.2072 +            |> register_coinduct_dynamic_friend fpT_name fun_name
  1.2073 +            |> register_friend_corec fun_name fp_b version Y Z k_T dead_k_bnf sig_fp_sugar
  1.2074 +              ssig_fp_sugar fun_t rho rho_transfer_folded old_corec_info
  1.2075 +          end;
  1.2076 +
  1.2077 +        val (friend_infos, lthy) = lthy |> (if friend then register_friend #>> single else pair []);
  1.2078 +        val (corec_info as {corecUU = corecUU0, ...}, lthy) =
  1.2079 +          (case corec_infos of
  1.2080 +            [] => corec_info_of res_T lthy
  1.2081 +          | [info] => (info, lthy));
  1.2082 +
  1.2083 +        val def_rhs = mk_corec_fun_def_rhs lthy arg_Ts corecUU0 corecUU_arg;
  1.2084 +        val def = ((b, mx), ((Binding.concealed (Thm.def_binding b), []), def_rhs));
  1.2085 +
  1.2086 +        val ((fun_t0, (_, fun_def0)), (lthy, lthy_old)) = lthy
  1.2087 +          |> Local_Theory.open_target |> snd
  1.2088 +          |> Local_Theory.define def
  1.2089 +          ||> `Local_Theory.close_target;
  1.2090 +
  1.2091 +        val parsed_eq = parse_corec_equation lthy [fun_free] eq;
  1.2092 +        val views0 = generate_views lthy eq fun_free parsed_eq;
  1.2093 +
  1.2094 +        val lthy' = lthy |> fold Variable.declare_typ (res_T :: arg_Ts);
  1.2095 +        val phi = Proof_Context.export_morphism lthy_old lthy';
  1.2096 +
  1.2097 +        val fun_t = Morphism.term phi fun_t0; (* FIXME: shadows "fun_t" -- identical? *)
  1.2098 +        val fun_def = Morphism.thm phi fun_def0;
  1.2099 +        val inner_fp_elims = map (Morphism.thm phi) inner_fp_elims0;
  1.2100 +        val inner_fp_inducts = map (Morphism.thm phi) inner_fp_inducts0;
  1.2101 +        val inner_fp_simps = map (Morphism.thm phi) inner_fp_simps0;
  1.2102 +        val (code_goal, _, _, _, _) = morph_views phi views0;
  1.2103 +
  1.2104 +        fun derive_and_note_friend_extra_theorems lthy =
  1.2105 +          let
  1.2106 +            val k_T = #7 (the_single prepareds);
  1.2107 +            val rho_def = snd (the_single rho_datas);
  1.2108 +
  1.2109 +            val (eq_algrho, algrho_eq) = derive_eq_algrho lthy corec_info (the_single friend_infos)
  1.2110 +              fun_t k_T code_goal const_transfers rho_def fun_def;
  1.2111 +
  1.2112 +            val notes =
  1.2113 +              (if Config.get lthy bnf_internals then
  1.2114 +                 [(eq_algrhoN, [eq_algrho])]
  1.2115 +               else
  1.2116 +                 [])
  1.2117 +              |> map (fn (thmN, thms) =>
  1.2118 +                ((Binding.qualify true (Binding.name_of b)
  1.2119 +                    (Binding.qualify false friendN (Binding.name thmN)), []),
  1.2120 +                 [(thms, [])]));
  1.2121 +          in
  1.2122 +            lthy
  1.2123 +            |> register_friend_extra fun_name eq_algrho algrho_eq
  1.2124 +            |> Local_Theory.notes notes |> snd
  1.2125 +          end;
  1.2126 +
  1.2127 +        val lthy = lthy |> friend ? derive_and_note_friend_extra_theorems;
  1.2128 +
  1.2129 +        val code_thm = derive_code lthy inner_fp_simps code_goal corec_info res_T fun_t fun_def;
  1.2130 +(* TODO:
  1.2131 +        val ctr_thmss = map mk_thm (#2 views);
  1.2132 +        val disc_thmss = map mk_thm (#3 views);
  1.2133 +        val disc_iff_thmss = map mk_thm (#4 views);
  1.2134 +        val sel_thmss = map mk_thm (#5 views);
  1.2135 +*)
  1.2136 +
  1.2137 +        val uniques =
  1.2138 +          if null inner_fp_simps then [derive_unique lthy phi (#1 views0) corec_info res_T fun_def]
  1.2139 +          else [];
  1.2140 +
  1.2141 +(* TODO:
  1.2142 +        val disc_iff_or_disc_thmss =
  1.2143 +          map2 (fn [] => I | disc_iffs => K disc_iffs) disc_iff_thmss disc_thmss;
  1.2144 +        val simp_thmss = map2 append disc_iff_or_disc_thmss sel_thmss;
  1.2145 +*)
  1.2146 +
  1.2147 +        val ((_, [{cong_intro_tab, coinduct, coinduct_attrs}]), lthy) = lthy
  1.2148 +          |> derive_and_update_coinduct_cong_intross [corec_info];
  1.2149 +        val cong_intros_pairs = Symtab.dest cong_intro_tab;
  1.2150 +
  1.2151 +        val code_attrs = if plugins code_plugin then [Code.add_default_eqn_attrib] else [];
  1.2152 +
  1.2153 +        val anonymous_notes = [];
  1.2154 +(* TODO:
  1.2155 +          [(flat disc_iff_or_disc_thmss, simp_attrs)]
  1.2156 +          |> map (fn (thms, attrs) => ((Binding.empty, attrs), [(thms, [])]));
  1.2157 +*)
  1.2158 +
  1.2159 +        val notes =
  1.2160 +          [(cong_introsN, maps snd cong_intros_pairs, []),
  1.2161 +           (codeN, [code_thm], code_attrs @ nitpicksimp_attrs),
  1.2162 +           (coinductN, [coinduct], coinduct_attrs),
  1.2163 +           (inner_inductN, inner_fp_inducts, []),
  1.2164 +           (uniqueN, uniques, [])] @
  1.2165 +           map (fn (thmN, thms) => (thmN, thms, [])) cong_intros_pairs @
  1.2166 +          (if Config.get lthy bnf_internals then
  1.2167 +             [(inner_elimN, inner_fp_elims, []),
  1.2168 +              (inner_simpN, inner_fp_simps, [])]
  1.2169 +           else
  1.2170 +             [])
  1.2171 +(* TODO:
  1.2172 +           (ctrN, ctr_thms, []),
  1.2173 +           (discN, disc_thms, []),
  1.2174 +           (disc_iffN, disc_iff_thms, []),
  1.2175 +           (selN, sel_thms, simp_attrs),
  1.2176 +           (simpsN, simp_thms, []),
  1.2177 +*)
  1.2178 +          |> map (fn (thmN, thms, attrs) =>
  1.2179 +              ((Binding.qualify true (Binding.name_of b)
  1.2180 +                  (Binding.qualify false corecN (Binding.name thmN)), attrs),
  1.2181 +               [(thms, [])]))
  1.2182 +          |> filter_out (null o fst o hd o snd);
  1.2183 +      in
  1.2184 +        lthy
  1.2185 +(* TODO:
  1.2186 +        |> Spec_Rules.add Spec_Rules.Equational ([fun_t0], flat sel_thmss)
  1.2187 +        |> Spec_Rules.add Spec_Rules.Equational ([fun_t0], flat ctr_thmss)
  1.2188 +*)
  1.2189 +        |> Spec_Rules.add Spec_Rules.Equational ([fun_t0], [code_thm])
  1.2190 +        |> Local_Theory.notes (anonymous_notes @ notes)
  1.2191 +        |> snd
  1.2192 +      end;
  1.2193 +
  1.2194 +    fun prove_transfer_goal ctxt goal =
  1.2195 +      Variable.add_free_names ctxt goal []
  1.2196 +      |> (fn vars => Goal.prove_sorry (*FIXME*) (*no sorry*) ctxt vars [] goal (fn {context = ctxt, prems = _} =>
  1.2197 +        HEADGOAL (Transfer.transfer_prover_tac ctxt)))
  1.2198 +      |> Thm.close_derivation;
  1.2199 +
  1.2200 +    fun maybe_prove_transfer_goal ctxt goal =
  1.2201 +      (case try (prove_transfer_goal ctxt) goal of
  1.2202 +        SOME thm => apfst (cons thm)
  1.2203 +      | NONE => apsnd (cons goal));
  1.2204 +
  1.2205 +    val const_transfer_goals = fold (union (op aconv) o fst) transfer_goal_datas [];
  1.2206 +    val (const_transfers, const_transfer_goals') =
  1.2207 +      if long_cmd then ([], const_transfer_goals)
  1.2208 +      else fold (maybe_prove_transfer_goal lthy) const_transfer_goals ([], []);
  1.2209 +  in
  1.2210 +    ((def_fun, (([res_T], prepareds, rho_datas, map snd transfer_goal_datas),
  1.2211 +        (inner_fp_triple, termin_goals), (const_transfers, const_transfer_goals'))), lthy)
  1.2212 +  end;
  1.2213 +
  1.2214 +fun corec_cmd opts (raw_fixes, raw_eq) lthy =
  1.2215 +  let
  1.2216 +    val ((def_fun, (_, (inner_fp_triple, termin_goals), (const_transfers, const_transfer_goals))),
  1.2217 +         lthy) =
  1.2218 +      prepare_corec_ursive_cmd false opts (raw_fixes, raw_eq) lthy;
  1.2219 +  in
  1.2220 +    if not (null termin_goals) then
  1.2221 +      error ("Termination prover failed (try " ^ quote (#1 @{command_keyword corecursive}) ^
  1.2222 +        " instead of " ^ quote (#1 @{command_keyword corec}) ^ ")")
  1.2223 +    else if not (null const_transfer_goals) then
  1.2224 +      error ("Transfer prover failed (try " ^ quote (#1 @{command_keyword corecursive}) ^
  1.2225 +        " instead of " ^ quote (#1 @{command_keyword corec}) ^ ")")
  1.2226 +    else
  1.2227 +      def_fun inner_fp_triple const_transfers [] lthy
  1.2228 +  end;
  1.2229 +
  1.2230 +fun corecursive_cmd opts (raw_fixes, raw_eq) lthy =
  1.2231 +  let
  1.2232 +    val ((def_fun, (([Type (fpT_name, _)], prepareds, rho_datas, rho_transfer_goals),
  1.2233 +            (inner_fp_triple, termin_goals), (const_transfers, const_transfer_goals))), lthy) =
  1.2234 +      prepare_corec_ursive_cmd true opts (raw_fixes, raw_eq) lthy;
  1.2235 +
  1.2236 +    val (rho_transfer_goals', unprime_rho_transfer_and_folds) =
  1.2237 +      @{map 3} (fn (_, _, _, _, _, _, _, _, _, _, _, _, _) => fn (_, rho_def) =>
  1.2238 +          prime_rho_transfer_goal lthy fpT_name rho_def)
  1.2239 +        prepareds rho_datas rho_transfer_goals
  1.2240 +      |> split_list;
  1.2241 +  in
  1.2242 +    Proof.theorem NONE (fn [termin_thms, const_transfers', rho_transfers'] =>
  1.2243 +      let
  1.2244 +        val remove_domain_condition =
  1.2245 +          full_simplify (put_simpset HOL_basic_ss lthy
  1.2246 +            addsimps (@{thm True_implies_equals} :: termin_thms));
  1.2247 +      in
  1.2248 +        def_fun (@{apply 3} (map remove_domain_condition) inner_fp_triple)
  1.2249 +          (const_transfers @ const_transfers')
  1.2250 +          (map2 (fn f => f) unprime_rho_transfer_and_folds rho_transfers')
  1.2251 +      end)
  1.2252 +      (map (map (rpair [])) [termin_goals, const_transfer_goals, rho_transfer_goals']) lthy
  1.2253 +  end;
  1.2254 +
  1.2255 +fun friend_of_corec_cmd ((raw_fun_name, raw_fun_T_opt), raw_eq) lthy =
  1.2256 +  let
  1.2257 +    val Const (fun_name, default_fun_T0) =
  1.2258 +      Proof_Context.read_const {proper = true, strict = false} lthy raw_fun_name;
  1.2259 +    val fun_T =
  1.2260 +      (case raw_fun_T_opt of
  1.2261 +        SOME raw_T => Syntax.read_typ lthy raw_T
  1.2262 +      | NONE => singleton (freeze_types lthy []) default_fun_T0);
  1.2263 +
  1.2264 +    val fun_t = Const (fun_name, fun_T);
  1.2265 +    val fun_b = Binding.name (Long_Name.base_name fun_name);
  1.2266 +
  1.2267 +    val fake_lthy = lthy |> Proof_Context.add_const_constraint (fun_name, SOME fun_T)
  1.2268 +      handle TYPE (msg, _, _) => error msg;
  1.2269 +
  1.2270 +    val code_goal = Syntax.read_prop fake_lthy raw_eq;
  1.2271 +
  1.2272 +    val (arg_Ts, res_T as Type (fpT_name, _)) = strip_type fun_T;
  1.2273 +
  1.2274 +    val no_base = has_no_corec_info lthy fpT_name;
  1.2275 +    val lthy = lthy |> no_base ? setup_base fpT_name;
  1.2276 +
  1.2277 +    val lthy = lthy |> Variable.declare_typ fun_T;
  1.2278 +    val ((old_corec_info, fp_b, version, Y, Z, preT, k_T, ssig_T, dead_pre_bnf, dead_k_bnf,
  1.2279 +          sig_fp_sugar, ssig_fp_sugar, buffer), lthy) =
  1.2280 +      prepare_friend_corec fun_name fun_T lthy;
  1.2281 +    val friend_parse_info = friend_parse_info_of lthy arg_Ts res_T buffer;
  1.2282 +
  1.2283 +    val parsed_eq = parse_corec_equation lthy [] code_goal;
  1.2284 +
  1.2285 +    val (((rho, rho_def), (const_transfer_goals, rho_transfer_goal)), lthy) =
  1.2286 +      extract_rho_return_transfer_goals fun_b version dead_pre_bnf dead_k_bnf Y Z preT fun_T k_T
  1.2287 +        ssig_T ssig_fp_sugar friend_parse_info fun_t parsed_eq lthy;
  1.2288 +
  1.2289 +    fun register_friend_extra_and_note_thms code_goal code_thm const_transfers k_T friend_info
  1.2290 +        lthy =
  1.2291 +      let
  1.2292 +        val (corec_info, lthy) = corec_info_of res_T lthy;
  1.2293 +
  1.2294 +        val fun_free = Free (Binding.name_of fun_b, fun_T);
  1.2295 +
  1.2296 +        fun freeze_fun (t as Const (s, _)) = if s = fun_name then fun_free else t
  1.2297 +          | freeze_fun t = t;
  1.2298 +
  1.2299 +        val eq = Term.map_aterms freeze_fun code_goal;
  1.2300 +        val parsed_eq = parse_corec_equation lthy [fun_free] eq;
  1.2301 +
  1.2302 +        val corec_parse_info = corec_parse_info_of lthy arg_Ts res_T buffer;
  1.2303 +        val explored_eq = explore_corec_equation lthy false false fun_name fun_free corec_parse_info
  1.2304 +          res_T parsed_eq;
  1.2305 +
  1.2306 +        val ((_, corecUU_arg), _) = build_corecUU_arg_and_goals false fun_free explored_eq lthy;
  1.2307 +
  1.2308 +        val eq_corecUU = derive_eq_corecUU lthy corec_info fun_t corecUU_arg code_thm;
  1.2309 +        val (eq_algrho, algrho_eq) = derive_eq_algrho lthy corec_info friend_info fun_t k_T
  1.2310 +          code_goal const_transfers rho_def eq_corecUU;
  1.2311 +
  1.2312 +        val ((_, [{cong_intro_tab, coinduct, coinduct_attrs}]), lthy) = lthy
  1.2313 +          |> register_friend_extra fun_name eq_algrho algrho_eq
  1.2314 +          |> register_coinduct_dynamic_friend fpT_name fun_name
  1.2315 +          |> derive_and_update_coinduct_cong_intross [corec_info];
  1.2316 +        val cong_intros_pairs = Symtab.dest cong_intro_tab;
  1.2317 +
  1.2318 +        val unique = derive_unique lthy Morphism.identity code_goal corec_info res_T eq_corecUU;
  1.2319 +
  1.2320 +        val notes =
  1.2321 +          [(cong_intros_friendN, maps snd cong_intros_pairs, []),
  1.2322 +           (codeN, [code_thm], []),
  1.2323 +           (coinductN, [coinduct], coinduct_attrs),
  1.2324 +           (uniqueN, [unique], [])] @
  1.2325 +           map (fn (thmN, thms) => (thmN, thms, [])) cong_intros_pairs @
  1.2326 +          (if Config.get lthy bnf_internals then
  1.2327 +             [(eq_algrhoN, [eq_algrho], []),
  1.2328 +              (eq_corecUUN, [eq_corecUU], [])]
  1.2329 +           else
  1.2330 +             [])
  1.2331 +          |> map (fn (thmN, thms, attrs) =>
  1.2332 +            ((Binding.qualify true (Binding.name_of fun_b)
  1.2333 +                (Binding.qualify false friendN (Binding.name thmN)), attrs),
  1.2334 +             [(thms, [])]));
  1.2335 +      in
  1.2336 +        lthy
  1.2337 +        |> Local_Theory.notes notes |> snd
  1.2338 +      end;
  1.2339 +
  1.2340 +    val (rho_transfer_goal', unprime_rho_transfer_and_fold) =
  1.2341 +      prime_rho_transfer_goal lthy fpT_name rho_def rho_transfer_goal;
  1.2342 +  in
  1.2343 +    lthy
  1.2344 +    |> Proof.theorem NONE (fn [[code_thm], const_transfers, [rho_transfer']] =>
  1.2345 +        register_friend_corec fun_name fp_b version Y Z k_T dead_k_bnf sig_fp_sugar ssig_fp_sugar
  1.2346 +          fun_t rho (unprime_rho_transfer_and_fold rho_transfer') old_corec_info
  1.2347 +        #-> register_friend_extra_and_note_thms code_goal code_thm const_transfers k_T)
  1.2348 +      (map (map (rpair [])) [[code_goal], const_transfer_goals, [rho_transfer_goal']])
  1.2349 +    |> Proof.refine_singleton (Method.primitive_text (K I))
  1.2350 +  end;
  1.2351 +
  1.2352 +fun coinduction_upto_cmd (base_name, raw_fpT) lthy =
  1.2353 +  let
  1.2354 +    val fpT as Type (fpT_name, _) = Syntax.read_typ lthy raw_fpT;
  1.2355 +
  1.2356 +    val no_base = has_no_corec_info lthy fpT_name;
  1.2357 +
  1.2358 +    val (corec_info as {version, ...}, lthy) = lthy
  1.2359 +      |> corec_info_of fpT;
  1.2360 +    val lthy = lthy |> no_base ? setup_base fpT_name;
  1.2361 +
  1.2362 +    val ((changed, [{cong_intro_tab, coinduct, coinduct_attrs}]), lthy) = lthy
  1.2363 +      |> derive_and_update_coinduct_cong_intross [corec_info];
  1.2364 +    val lthy = lthy |> (changed orelse no_base) ? update_coinduct_cong_intross_dynamic fpT_name;
  1.2365 +    val cong_intros_pairs = Symtab.dest cong_intro_tab;
  1.2366 +
  1.2367 +    val notes =
  1.2368 +      [(cong_introsN, maps snd cong_intros_pairs, []),
  1.2369 +       (coinduct_uptoN, [coinduct], coinduct_attrs)] @
  1.2370 +      map (fn (thmN, thms) => (thmN, thms, [])) cong_intros_pairs
  1.2371 +      |> map (fn (thmN, thms, attrs) =>
  1.2372 +        (((Binding.qualify true base_name
  1.2373 +            (Binding.qualify false ("v" ^ string_of_int version) (Binding.name thmN))), attrs),
  1.2374 +         [(thms, [])]));
  1.2375 +  in
  1.2376 +    lthy |> Local_Theory.notes notes |> snd
  1.2377 +  end;
  1.2378 +
  1.2379 +fun consolidate lthy =
  1.2380 +  let
  1.2381 +    val corec_infoss = map (corec_infos_of lthy o fst) (all_codatatype_extras_of lthy);
  1.2382 +    val (changeds, lthy) = lthy
  1.2383 +      |> fold_map (apfst fst oo derive_and_update_coinduct_cong_intross) corec_infoss;
  1.2384 +  in
  1.2385 +    if exists I changeds then lthy else raise Same.SAME
  1.2386 +  end;
  1.2387 +
  1.2388 +fun consolidate_global thy =
  1.2389 +  SOME (Named_Target.theory_map consolidate thy)
  1.2390 +  handle Same.SAME => NONE;
  1.2391 +
  1.2392 +val _ = Outer_Syntax.local_theory @{command_keyword corec}
  1.2393 +  "define nonprimitive corecursive functions"
  1.2394 +  ((Scan.optional (@{keyword "("} |-- Parse.!!! (Parse.list1 corec_option_parser)
  1.2395 +      --| @{keyword ")"}) []) -- (Parse.fixes --| Parse.where_ -- Parse.prop)
  1.2396 +   >> uncurry corec_cmd);
  1.2397 +
  1.2398 +val _ = Outer_Syntax.local_theory_to_proof @{command_keyword corecursive}
  1.2399 +  "define nonprimitive corecursive functions"
  1.2400 +  ((Scan.optional (@{keyword "("} |-- Parse.!!! (Parse.list1 corec_option_parser)
  1.2401 +      --| @{keyword ")"}) []) -- (Parse.fixes --| Parse.where_ -- Parse.prop)
  1.2402 +   >> uncurry corecursive_cmd);
  1.2403 +
  1.2404 +val _ = Outer_Syntax.local_theory_to_proof @{command_keyword friend_of_corec}
  1.2405 +  "register a function as a legal context for nonprimitive corecursion"
  1.2406 +  (Parse.const -- Scan.option (Parse.$$$ "::" |-- Parse.typ) --| Parse.where_ -- Parse.prop
  1.2407 +   >> friend_of_corec_cmd);
  1.2408 +
  1.2409 +val _ = Outer_Syntax.local_theory @{command_keyword coinduction_upto}
  1.2410 +  "derive a coinduction up-to principle and a corresponding congruence closure"
  1.2411 +  (Parse.name --| Parse.$$$ ":" -- Parse.typ >> coinduction_upto_cmd);
  1.2412 +
  1.2413 +val _ = Theory.setup (Theory.at_begin consolidate_global);
  1.2414 +
  1.2415 +end;