Session Conditional_Transfer_Rule

Theory CTR_Tools

(* Title: CTR_Tools/CTR_Tools.thy
   Author: Mihails Milehins
   Copyright 2021 (C) Mihails Milehins

A collection of basic Isabelle/ML functions for the CTR.
*)

section‹Import›
theory CTR_Tools 
  imports Main
begin



subsection‹Standard library extension›

ML_file "More_Library.ML"
ML_file "More_Binding.ML"
ML_file "More_Type.ML"
ML_file "More_Sort.ML"
ML_file "More_Term.ML"
ML_file "More_Variable.ML"
ML_file "More_Logic.ML"
ML_file "More_Thm.ML"
ML_file "More_Simplifier.ML"
ML_file "More_HOLogic.ML"
ML_file "More_Transfer.ML"



subsection‹Specialized functionality›

ML_file "CTR_Utilities.ML"

end

File ‹More_Library.ML›

(* Title: CTR_Tools/More_Library.ML
   Author: Mihails Milehins
   Copyright 2021 (C) Mihails Milehins

An extension of the structure Library from the standard library of 
Isabelle/Pure.
*)

signature LIBRARY =
sig
    
  include LIBRARY
  
  (*pairs and triples*)
  val reroute_ps_sp : ('a * 'b) * 'c -> 'a * ('b * 'c)
  val reroute_sp_ps : 'a * ('b * 'c) -> ('a * 'b) * 'c
  val reroute_ps_triple : ('a * 'b) * 'c -> 'a * 'b * 'c  
  val reroute_sp_triple : 'a * ('b * 'c) -> 'a * 'b * 'c
  val reroute_triple_ps : 'a * 'b * 'c -> ('a * 'b) * 'c  
  val reroute_triple_sp : 'a * 'b * 'c -> 'a * ('b * 'c)

  val dup : 'a -> 'a * 'a
  val apdupr : ('a -> 'b) -> 'a -> 'a * 'b
  val apdupl : ('a -> 'b) -> 'a -> 'b * 'a

  (*lists*)
  val map_slice_l : ('a list -> 'b list) -> ('a * 'c) list -> ('b * 'c) list
  val map_slice_r : ('a list -> 'b list) -> ('c * 'a) list -> ('c * 'b) list
  val map_slice_side_l : 
    ('a list -> 'b list * 'c) -> ('a * 'd) list -> ('b * 'd) list * 'c
  val map_slice_side_r : 
    ('a list -> 'b list * 'c) -> ('d * 'a) list -> ('d * 'b) list * 'c  
  val find_indices : ('a -> bool) -> 'a list -> int list

  (*strings*)
  val tabulate : string -> string
  
  (*option*)
  val mk_opt_id : ('a -> 'b) -> ('a -> 'b option) -> 'a -> 'b

  (*keywords*)
  val kw_bo : string parser
  val kw_bc : string parser
  val kw_col : string parser

end

structure Library: LIBRARY =
struct

open Library;


(** pairs and triples **)

fun reroute_ps_sp ((x, y), z) = (x, (y, z));
fun reroute_sp_ps (x, (y, z)) = ((x, y), z);
fun reroute_ps_triple ((x, y), z) = (x, y, z);
fun reroute_sp_triple (x, (y, z)) = (x, y, z);
fun reroute_triple_ps (x, y, z) = ((x, y), z);
fun reroute_triple_sp (x, y, z) = (x, (y, z));

fun dup x = (x, x);
fun apdupr f x = (x, f x);
fun apdupl f x = (f x, x);


(** lists **)

fun map_slice_l f xs = xs 
  |> split_list
  |>> f
  |> op~~;
fun map_slice_r f xs = xs 
  |> split_list
  ||> f
  |> op~~;
fun map_slice_side_l f xs = xs 
  |> split_list
  |>> f
  |> swap
  |> reroute_sp_ps
  |>> swap  
  |>> op~~;
fun map_slice_side_r f xs = xs 
  |> split_list
  ||> f
  |> reroute_sp_ps
  |>> op~~;

fun find_indices p xs =
  let
    fun find_indices_impl _ _ [] = []
      | find_indices_impl p i (x::xs) =
          if p x 
          then i::find_indices_impl p (i + 1) xs
          else find_indices_impl p (i + 1) xs
  in find_indices_impl p 0 xs end;


(** option **)

fun mk_opt_id g f x = case f x of SOME y => y | NONE => g x;


(** strings **)

val tabulate = split_lines #> map (fn c => "\t" ^ c) #> String.concatWith "\n"


(** shortcuts for common keywords **)

val kw_bo = keyword(;
val kw_bc = keyword);
val kw_col = keyword:;

end

open Library;

File ‹More_Binding.ML›

(* Title: CTR_Tools/More_Binding.ML
   Author: Mihails Milehins
   Copyright 2021 (C) Mihails Milehins

An extension of the structure Binding from the standard library of 
Isabelle/Pure.
*)

signature BINDING =
sig
  include BINDING
  val qualified_name_mandatory : string -> binding
end;

structure Binding: BINDING =
struct

open Binding;

fun qualified_name_mandatory c = fold
  (fn c => fn b => Binding.qualify_name true b c)
  (Long_Name.explode c)
  Binding.empty;

end;

File ‹More_Type.ML›

(* Title: CTR_Tools/More_Type.ML
   Author: Mihails Milehins
   Copyright 2021 (C) Mihails Milehins

An extension of the structure Type from the standard library of 
Isabelle/Pure.
*)

signature TYPE =
sig
  include TYPE
  val typ_matches : tsig -> typ list * typ list -> tyenv -> tyenv
  val default_sorts_of_empty_sorts : theory -> typ -> typ
  val default_sorts : theory -> typ -> typ
end 

structure Type: TYPE  =
struct

open Type;

fun typ_matches tsig (T :: Ts, U :: Us) subs = 
      typ_matches tsig (Ts, Us) (Type.typ_match tsig (T, U) subs)
  | typ_matches _ ([], []) subs = subs
  | typ_matches _ _ _ = raise Type.TYPE_MATCH;

fun default_sorts_of_empty_sorts thy = 
  let val dS = Sign.defaultS thy
  in
    map_atyps
      (
        fn TFree (x, []) => TFree (x, dS)
         | TVar (xi, []) => TVar (xi, dS)
         | TFree (x, S) => TFree (x, S)
         | TVar (xi, S) => TVar (xi, S)
      )
  end;

fun default_sorts thy = 
  let val dS = Sign.defaultS thy
  in
    map_atyps
      (
        fn TFree (x, _) => TFree (x, dS)
         | TVar (xi, _) => TVar (xi, dS)
      )
  end;

end;

File ‹More_Sort.ML›

(* Title: CTR_Tools/More_Sort.ML
   Author: Mihails Milehins
   Copyright 2021 (C) Mihails Milehins

An extension of the structure Sorts from the standard library of 
Isabelle/Pure.
*)

signature SORTS =
sig
  include SORTS
  val params_of_super_classes : theory -> class -> (string * typ) list
  val params_of_sort : theory -> class list -> (string * typ) list
end;

structure Sorts: SORTS  =
struct

open Sorts;

fun params_of_class thy class = 
  try (Axclass.get_info thy #> #params) class |> these;

fun params_of_super_classes thy class =
  class :: Sorts.super_classes (Sign.classes_of thy) class 
  |> maps (params_of_class thy);

fun params_of_sort thy sort = maps (params_of_super_classes thy) sort;

end;

File ‹More_Term.ML›

(* Title: CTR_Tools/More_Term.ML
   Author: Mihails Milehins
   Copyright 2021 (C) Mihails Milehins

An extension of the structure Term from the standard library of 
Isabelle/Pure.
*)

signature TERM =
sig
  include TERM
  val map_sv : (indexname * typ -> term) -> term -> term
  val map_fv : (string * typ -> term) -> term -> term
  val map_const : (string * typ -> term) -> term -> term
  val map_tfree: (string * sort -> typ) -> term -> term
  val has_tfreesT : typ -> bool
  val is_comb : term -> bool
  val could_match_const : (''a * typ) * (''a * typ) -> bool
end;

structure Term: TERM  =
struct

open Term;

fun map_sv f (Var sv_spec) = f sv_spec
  | map_sv f (Abs (c, T, t)) = Abs (c, T, map_sv f t)
  | map_sv f (t $ u) = map_sv f t $ map_sv f u
  | map_sv _ t = t;

fun map_fv f (Free fv_spec) = f fv_spec
  | map_fv f (Abs (c, T, t)) = Abs (c, T, map_fv f t)
  | map_fv f (t $ u) = map_fv f t $ map_fv f u
  | map_fv _ t = t;

fun map_const f (Const const_spec) = f const_spec
  | map_const f (Abs (c, T, t)) = Abs (c, T, map_const f t)
  | map_const f (t $ u) = map_const f t $ map_const f u
  | map_const _ t = t;

fun map_tfree f (Const (c, T)) = Const (c, map_type_tfree f T)
  | map_tfree f (Free (c, T)) = Free (c, map_type_tfree f T)
  | map_tfree f (Var (v, T)) = Var (v, map_type_tfree f T) 
  | map_tfree f (Abs (c, T, t)) = Abs (c, map_type_tfree f T, map_tfree f t)
  | map_tfree f (t $ u) = map_tfree f t $ map_tfree f u
  | map_tfree _ t = t

fun has_tfreesT (TFree _) = true
  | has_tfreesT (TVar _) = false
  | has_tfreesT (Type (_, Ts)) = has_tfreesT_list Ts
and has_tfreesT_list [] = false
  | has_tfreesT_list (T::Ts) = has_tfreesT T orelse has_tfreesT_list Ts;

fun is_comb (_ $ _) = true
  | is_comb _ = false;

fun could_match_const ((c, T), (c', T')) = 
  c = c' andalso Type.could_match (T, T');

end;

File ‹More_Variable.ML›

(* Title: CTR_Tools/More_Variable.ML
   Author: Mihails Milehins
   Copyright 2021 (C) Mihails Milehins

An extension of the structure Variable from the standard library of 
Isabelle/Pure.
*)

signature VARIABLE =
sig
  include VARIABLE
  val fix_new_vars : Proof.context -> string list -> Proof.context
  val variant_name_const : Proof.context -> string list -> string list
end

structure Variable: VARIABLE  =
struct

open Variable;

(*fix new variables*)
fun fix_new_vars ctxt cs = 
  let
    fun fix_new_vars_select c ctxt = 
      if Variable.is_fixed ctxt c 
      then ctxt
      else ctxt |> Variable.add_fixes (single c) |> #2
  in fold_rev fix_new_vars_select cs ctxt end;

(*invent new name variants with respect to the base names of constants*)
fun variant_name_const ctxt names = 
  let
    val {constants=constants, ...} = Consts.dest (Proof_Context.consts_of ctxt)
    val const_names = map (#1 #> Long_Name.base_name) constants
    val name_ctxt = ctxt
      |> Variable.names_of
      |> fold Name.declare const_names
    val names =
      let
        fun folder name (names, name_ctxt) = 
          Name.variant name name_ctxt |>> curry (swap #> op::) names
      in ([], name_ctxt) |> fold folder names |> #1 end
  in names end;

end

File ‹More_Logic.ML›

(* Title: CTR_Tools/More_Logic.ML
   Author: Mihails Milehins
   Copyright 2021 (C) Mihails Milehins

An extension of the structure Logic from the standard library of Isabelle/Pure.
*)

signature LOGIC =
sig
  include LOGIC
  val unvarify_types_local_term : 
    Proof.context -> term -> term * Proof.context
  val unvarify_local_term : Proof.context -> term -> term * Proof.context
  val varifyT_mixed_global : typ -> typ
  val varify_mixed_global : term -> term
  val unoverload_types_term : theory -> term -> term
  val is_equals : term -> bool
end

structure Logic : LOGIC  =
struct

open Logic;

(*unvarify types in a local context*)
fun unvarify_types_local_factt ctxt ts = 
  let
    val stv_specs = ts
      |> map (fn thmt => Term.add_tvars thmt [] |> rev)
      |> flat
      |> distinct op=
    val ctxt' = fold Proof_Context.augment ts ctxt
    val (ftv_specs, _) = Variable.invent_types (map #2 stv_specs) ctxt'
    val ctxt'' = fold Variable.declare_typ (map TFree ftv_specs) ctxt
    val stv_to_ftv = stv_specs ~~ ftv_specs
      |> AList.lookup op= 
      #> the
      #> TFree
    val ts = map (map_types (map_type_tvar stv_to_ftv)) ts
  in (ts, ctxt'') end;

(*unvarify types in a single term in a local context*)
fun unvarify_types_local_term ctxt t = t
  |> single
  |> unvarify_types_local_factt ctxt
  |>> the_single;

(*unvarify terms in a local context*)
fun unvarify_local_factt ctxt ts =
  let
    val (ts, ctxt') = unvarify_types_local_factt ctxt ts
    val sv_specs = map (fn thmt => Term.add_vars thmt [] |> rev) ts
      |> flat 
      |> distinct op=
    val stvcs = sv_specs |> (#1 #> #1 |> map)
      |> Variable.variant_name_const ctxt'
      |> rev
    val ftvcs = Variable.variant_fixes stvcs ctxt' |> #1
    val ctxt'' = ctxt' |> Variable.add_fixes ftvcs |> #2
    val stvc_to_ftvc = (sv_specs ~~ (ftvcs ~~ map #2 sv_specs))
      |> AList.lookup op=  
      #> the
      #> Free
    val ts = map (Term.map_sv stvc_to_ftvc) ts
  in (ts, ctxt'') end;

(*unvarify a single term in a local context*)
fun unvarify_local_term ctxt thm = thm
  |> single
  |> unvarify_local_factt ctxt
  |>> the_single;

local

fun ftvs_stvs_map svtcs stvs = stvs
  |> map (apdupr #1)
  |> map_slice_r (fn cs => Name.variant_list svtcs cs) 
  |> map (K 0 |> apdupr |> apsnd)
  |> map (fn ((c, S), v) => ((c, S), (v, S))) 
  |> map (apsnd TVar)
  |> AList.lookup op= #> the
 
in
 
(*varify a type that may have occurrences of both schematic and fixed 
type variables*)
fun varifyT_mixed_global T = 
  let
    val svtcs = map (#1 #> #1) (Term.add_tvarsT T [])
    val ftvs_stvs_map = ftvs_stvs_map svtcs (Term.add_tfreesT T [])
  in Term.map_type_tfree ftvs_stvs_map T end;

(* varify a term that may have occurrences of both schematic and fixed 
variables and, also, both schematic and fixed type variables *)
fun varify_mixed_global t =
  let
    val svtcs = map (#1 #> #1) (Term.add_tvars t [])
    val ftvs_stvs_map = ftvs_stvs_map svtcs (Term.add_tfrees t [])
    val t = Term.map_tfree ftvs_stvs_map t 
    val svcs = map (#1 #> #1) (Term.add_vars t [])
    val fvs_svs_map = Term.add_frees t []
      |> map (apdupr #1)
      |> map_slice_r (fn cs => Name.variant_list svcs cs) 
      |> map (K 0 |> apdupr |> apsnd)
      |> map (fn ((c, T), v) => ((c, T), (v, T)))
      |> map (apsnd Var)
      |> AList.lookup op= #> the
    val t = Term.map_fv fvs_svs_map t
  in t end;

end;

(*unoverload all types in a term*)
fun unoverload_types_term thy t =
  let
    val sort_consts = Term.add_tvars t []
      |> map #2
      |> map (Sorts.params_of_sort thy)
      |> flat
      |> distinct op=
    val const_map = Term.add_consts t []
      |> distinct op=
      |> filter (member Term.could_match_const sort_consts)
      |> dup
      ||> map (#1 #> Long_Name.base_name)
      ||> Name.variant_list (Term.add_vars t [] |> map #1 |> map #1)
      |> op~~
      |> map (fn ((c, T), d) => ((c, T), Var ((d, 0), T)))
    fun const_map_opt const_spec = case AList.lookup op= const_map const_spec of 
      SOME t => t | 
      NONE => Const const_spec
    val t' = Term.map_const const_map_opt t
    val t'' =
      let                     
        val uctxt = Logic.unconstrainT [] t' |> #1
        (*Ported from Pure/Logic.ML with amendments*)
        val map_atyps = 
          Term.map_atyps (Type.default_sorts thy o (#atyp_map uctxt))
        (*Ported from Pure/Logic.ML with amendments*)
        val t'' = t'
          |> Term.map_types map_atyps
          |> curry Logic.list_implies (map #2 (#constraints uctxt));
        val prems = Logic.strip_imp_prems t'
        val prems = drop (length prems - Logic.count_prems t') prems
      in Logic.list_implies (prems, Logic.strip_imp_concl t'') end
  in t'' end;


(*equality*)
fun is_equals (Const (const_name‹Pure.eq›, _) $ _ $ _) = true
  | is_equals _ = false;

end;

File ‹More_Thm.ML›

(* Title: CTR_Tools/More_Thm.ML
   Author: Mihails Milehins
   Copyright 2021 (C) Mihails Milehins

An extension of the structure Thm from the standard library of 
Isabelle/Pure.
*)

signature THM =
sig
  include THM
  val unvarify_local_fact : 
    Proof.context -> thm list -> thm list * Proof.context
  val unvarify_local_thm : Proof.context -> thm -> thm * Proof.context
  val unvarify_local_thms : 
    Proof.context -> thm list -> thm list * Proof.context
  val tags_rule : (string * string) list -> thm -> thm
  val apply_attributes: 
    attribute list -> thm list -> Context.generic -> thm list * Context.generic 
  val forall_intr_var_order : Proof.context -> int list -> thm -> thm
  val pure_unfold : Proof.context -> thm list -> thm -> thm
end;

structure Thm: THM =
struct

open Thm;

(* 
unvarify a fact in a local context (new (type) variables are 
fixed and declared in the context).
*)
fun unvarify_local_fact ctxt thms =
  let
    val thmts = map Thm.full_prop_of thms
    val stv_specs = thmts
      |> map (fn thmt => Term.add_tvars thmt [] |> rev) 
      |> flat
      |> distinct op=
    val ctxt' = fold Proof_Context.augment thmts ctxt
    val (ftv_specs, ctxt'') = Variable.invent_types (map #2 stv_specs) ctxt'
    val ctxt''' = fold Variable.declare_typ (map TFree ftv_specs) ctxt
    val stv_to_ftv = stv_specs ~~ ftv_specs |> AList.lookup op= #> the
    val instT = map (fn v => (v, v |> stv_to_ftv |> TFree)) stv_specs
    val sv_specs = thmts
      |> map (fn thmt => Term.add_vars thmt [] |> rev) 
      |> flat 
      |> distinct op=
    val ftvcs = 
      let
        val stvcs = sv_specs 
          |> map (#1 #> #1)
          |> Variable.variant_name_const ctxt''
          |> rev
      in Variable.variant_fixes stvcs ctxt'' |> #1 end
    val ctxt'''' = Variable.add_fixes ftvcs ctxt''' |> #2
    val insts = 
      let val stvc_to_ftvc = AList.lookup op= (sv_specs ~~ ftvcs) #> the
      in
        map 
          (
            fn v as ((a, i), T) =>
              let val T' = Term_Subst.instantiateT instT T
              in 
                (
                  ((a, i), T'), 
                  ((stvc_to_ftvc v, T') |> Free |> Thm.cterm_of ctxt'''')
                ) 
              end
          )
          sv_specs
      end
    val ctxt''''' = ctxt''''
      |> fold Variable.declare_term (map (#2 #> Thm.term_of) insts) 
    val thms' = 
      map 
        (
          Drule.instantiate_normalize 
            (map (apsnd (Thm.ctyp_of ctxt)) instT, insts)
        ) 
        thms     
  in (thms', ctxt''''') end;

(*unvarify a single theorem in a local context*)
fun unvarify_local_thm ctxt thm = thm
  |> single
  |> unvarify_local_fact ctxt
  |>> the_single;

(*unvarify a list of theorems in a local context*)
fun unvarify_local_thms ctxt thms =
  let
    fun folder thm (thms, ctxt) = thm
      |> single
      |> unvarify_local_fact ctxt 
      |>> the_single
      |>> curry (swap #> op::) thms;
  in fold_rev folder thms ([], ctxt) end;

(*add multiple tags*)
fun tags_rule tgs thm = fold Thm.tag_rule tgs thm;

(*apply a list of attributes to a fact*)
fun apply_attributes attrs thms context =
  let 
    fun apply_attributes_single attrs thm context = 
      fold 
      (fn attr => fn (thm, context) => Thm.apply_attribute attr thm context) 
      attrs 
      (thm, context);
    fun folder thm (thms, context) = context
      |> apply_attributes_single attrs thm 
      |>> curry (op:: o swap) thms;
  in fold folder thms ([], context) |>> rev end;

(*introduction of the universally quantified variables with respect to a 
pre-defined order on the stvs*)
fun forall_intr_var_order ctxt order thm' =
  let
    val stvs = thm'
      |> Thm.full_prop_of
      |> (fn t => Term.add_vars t [])
      |> rev
    val stv_cts = map (nth stvs #> Var #> Thm.cterm_of ctxt) order 
  in fold Thm.forall_intr (rev stv_cts) thm' end;

(*low level unfold*)
(*Designed based on an algorithm from HOL-Types_To_Sets/unoverload_def.ML*)
fun pure_unfold ctxt thms = ctxt
  |> 
    (
      thms
      |> Conv.rewrs_conv 
      |> Conv.try_conv 
      |> K
      |> Conv.top_conv
    )
  |> Conv.fconv_rule;

end;

File ‹More_Simplifier.ML›

(* Title: CTR_Tools/More_Simplifier.ML
   Author: Mihails Milehins
   Copyright 2021 (C) Mihails Milehins

An extension of the functionality of the simplifier provided as part of
the standard library of Isabelle/Pure.

Notes:
  - The structure More_Simplifier was copied from the file 
  HOL/Types_To_Sets/Examples/Prerequisites.thy (with amendments)
*)

structure More_Simplifier =
struct

fun asm_full_var_simplify ctxt thm =
  let
    val ((_, [thm']), ctxt') = Variable.import false [thm] ctxt
  in
    Simplifier.full_simplify ctxt' thm'
    |> singleton (Variable.export ctxt' ctxt)
    |> Drule.zero_var_indexes
  end;

fun var_simplify_only ctxt ths thm =
  asm_full_var_simplify (Raw_Simplifier.clear_simpset ctxt addsimps ths) thm;

val var_simplified = Attrib.thms >>
  (fn ths => Thm.rule_attribute ths
    (fn context => var_simplify_only (Context.proof_of context) ths));

fun rewrite_simp_opt ctxt simp_spec_opt = case simp_spec_opt of 
    SOME simp_spec => var_simplify_only ctxt simp_spec 
  | NONE => Simplifier.full_simplify ctxt;

end;

File ‹More_HOLogic.ML›

(* Title: CTR_Tools/More_HOLogic.ML
   Author: Mihails Milehins
   Copyright 2021 (C) Mihails Milehins

An extension of the structure HOLogic from the standard library of Isabelle/HOL.
*)

signature HOLOGIC =
sig
  include HOLOGIC
  val mk_binrelT : typ * typ -> typ 
  val is_binrelT : typ -> bool
  val dest_binrelT : typ -> typ * typ
  val mk_rel : string * (typ * typ) -> term
  val is_conj : term -> bool
end;

structure HOLogic: HOLOGIC =
struct

open HOLogic;

fun mk_binrelT (T, U) = T --> U --> HOLogic.boolT;

fun is_binrelT (
  Type (type_name‹fun›, [_, Type (type_name‹fun›, [_, typ‹bool›])])
  ) = true
  | is_binrelT _ = false
 
fun dest_binrelT (Type (type_name‹fun›, [T, Type (type_name‹fun›, [U, _])])) = 
      (T, U)
  | dest_binrelT T = raise TYPE("dest_binrelT", single T, []); 

fun mk_rel (c, (T, U)) = Free (c, mk_binrelT (T, U))

fun is_conj (Const (const_name‹conj›, _) $ _ $ _) = true
  | is_conj _ = false;

end;

File ‹More_Transfer.ML›

(* Title: CTR_Tools/More_Transfer.ML
   Author: Mihails Milehins
   Copyright 2021 (C) Mihails Milehins

An extension of the structure Transfer from the standard library of 
Isabelle/Pure.
*)

signature TRANSFER =
sig
  include TRANSFER
  val is_is_equality : term -> bool
end;

structure Transfer: TRANSFER =
struct

open Transfer;

fun is_is_equality (Const (const_name‹is_equality›, _) $ _) = true
  | is_is_equality _ = false;

end;

File ‹CTR_Utilities.ML›

(* Title: CTR_Tools/CTR_Utilities.ML
   Author: Mihails Milehins
   Copyright 2021 (C) Mihails Milehins

A collection of Isabelle/ML utilities for the CTR.
*)

signature CTR_UTILITIES =
sig
val thm_printer : Proof.context -> bool -> string -> thm list -> unit
val qualified_name_of_const_name : string -> string
end;

structure CTR_Utilities : CTR_UTILITIES =
struct

fun thm_printer ctxt print name thms =
  Proof_Display.print_results
    print
    (Position.thread_data ())
    ctxt
    ((Thm.theoremK, ""), single (name, thms));

fun qualified_name_of_const_name c = 
  if Long_Name.qualification c = 0
  then error "qualified_name_of_const_name: invalid constant name"
  else 
    if Long_Name.qualification c = 1 
    then c 
    else c |> Long_Name.explode |> tl |> Long_Name.implode

end;

Theory IML_UT

(* Title: IML_UT/IML_UT.thy
   Author: Mihails Milehins
   Copyright 2021 (C) Mihails Milehins
*)

sectionIML_UT›
theory IML_UT
  imports "../CTR_Tools/CTR_Tools"
begin

ML_file ‹UT_Test_Suite.ML›

end

File ‹UT_Test_Suite.ML›

(* Title: IML_UT/UT_Test_Suite.ML
   Author: Mihails Milehins
   Copyright 2021 (C) Mihails Milehins

The implementation of a lightweight unit test framework IML_UT 
for Isabelle/ML code.

Notes:
 - The framework IML_UT was developed before the official release of SpecCheck
that serves a similar purpose (https://www.isa-afp.org/entries/SpecCheck.html).
Nonetheless, it is not unlikely that the author would be willing to make
the framework IML_UT obsolete, using SpecCheck in its place, in the future. 
 - To a certain extent, this work was inspired by xUnit (https://xunit.net/). 
*)


signature UT_TEST_SUITE =
sig
type ('x, 'y) test_suite
type ('x, 'y) test_results_suite
val init : 
  string -> 
  ('x -> 'y) -> 
  ('x -> string) -> 
  ('y -> string) -> 
  ('x, 'y) test_suite
val assert_brel : 
  string -> 
  ('y * 'y -> bool) -> 
  'y -> 'x -> 
  ('x, 'y) test_suite -> 
  ('x, 'y) test_suite
val assert_exception : 
  string -> 'x -> exn -> ('x, 'y) test_suite -> ('x, 'y) test_suite
val execute : ('x, 'y) test_suite -> ('x, 'y) test_results_suite
val output_test_results : bool -> ('a, 'b) test_results_suite -> unit
end;


structure UT_Test_Suite: UT_TEST_SUITE =
struct

exception BLANK;



(**** Types ****)

datatype 'y test_output = Exception of exn | Result of 'y;

type 'y assertion_output =
  {
    expected_output : string,
    output : 'y test_output,
    status : bool
  };

type ('x, 'y) test_result =
  {
    test_id : int, 
    name : string, 
    input : 'x, 
    assertion_output : 'y assertion_output
  };

type ('x, 'y) unit_test =
  {
    test_id : int,
    name : string,
    input : 'x,
    assertion : 'x -> 'y assertion_output
  };

type ('x, 'y) test_results_suite = 
  {
    id : string,
    function : 'x -> 'y,
    string_of_input : 'x -> string,
    string_of_output : 'y -> string,
    test_results : ('x, 'y) test_result Queue.T
  };

type ('x, 'y) test_suite = 
  {
    id : string,
    function : 'x -> 'y,
    string_of_input : 'x -> string,
    string_of_output : 'y -> string,
    tests : ('x, 'y) unit_test Queue.T
  };



(**** Initialization ****)

fun init id f string_of_input string_of_output : ('x, 'y) test_suite = 
  {
    id = id,
    function = f,
    string_of_input = string_of_input,
    string_of_output = string_of_output,
    tests = Queue.empty
  };



(**** Miscellaneous utilities ****)

fun get_num_tests tests = length (Queue.content tests);

fun is_empty_test_suite test_suite = Queue.is_empty (#tests test_suite);



(**** Evaluation ****)

(* The following function evolved from the function try from 
Pure/General/basics.ML of Isabelle2021 *)
fun eval f x =
  let
    val y_opt = try f x
    val out = case y_opt of
        SOME y => Result y
      | NONE => Exception
          (
            let val _ = f x in BLANK end
              handle exn => 
                if Exn.is_interrupt exn then Exn.reraise exn else exn
          )
  in out end;



(**** Assertion of a binary relation ****)

local

fun assert_brel_impl 
  string_of_output brel test_id name y f x : ('x, 'y) unit_test =
  let
    fun assertion x : 'y assertion_output = 
      let 
        val output = eval f x
        val status = case output of
            Exception _ => false
          | Result y' => brel (y', y)
      in
       {
         expected_output = string_of_output y,
         output = output, 
         status = status
       } 
      end
  in {test_id = test_id, name = name, input = x, assertion = assertion} end;

in

fun assert_brel name brel y x (test_suite : ('x, 'y) test_suite) =
  let
    val f = #function test_suite
    val string_of_input = #string_of_input test_suite
    val string_of_output = #string_of_output test_suite
    val tests = #tests test_suite
    val test_id = get_num_tests tests
    val test = assert_brel_impl string_of_output brel test_id name y f x
  in
    {
      id = #id test_suite,
      function = f,
      string_of_input = string_of_input,
      string_of_output = string_of_output,
      tests = Queue.enqueue test tests
    } : ('x, 'y) test_suite
  end;

end;



(**** Assertion of an exception ****)

local

fun assert_exception_impl test_id name (exn : exn) f x : ('x, 'y) unit_test =
  let
    fun assertion x : 'y assertion_output = 
      let
        val output = eval f x
        val status = case output of 
            Exception exn' =>
              exnName exn' = exnName exn 
              andalso exnMessage exn' = exnMessage exn
          | Result _ => false
     in 
       {
         expected_output = "exception '" ^ exnMessage exn ^ "'",
         output = output,
         status = status
       } 
     end
  in {test_id = test_id, name = name, input = x, assertion = assertion} end;

in

fun assert_exception name x (exn : exn) (test_suite : ('x, 'y) test_suite) =
  let
    val f = #function test_suite
    val string_of_input = #string_of_input test_suite
    val string_of_output = #string_of_output test_suite
    val tests = #tests test_suite
    val test_id = get_num_tests tests
    val test = assert_exception_impl test_id name exn f x
  in 
    {
      id = #id test_suite,
      function = f,
      string_of_input = string_of_input,
      string_of_output = string_of_output,
      tests = Queue.enqueue test tests
    } : ('x, 'y) test_suite
  end;

end;



(**** Test execution ****)

local 

fun execute_test (test_suite : ('x, 'y) test_suite) : 
  (('x, 'y) test_result * ('x, 'y) test_suite) =
  let
    val {id, function, string_of_input, string_of_output, tests} = test_suite
    val ({test_id, name, input, assertion}, tests') = Queue.dequeue tests
    val assertion_output = assertion input
    val test_result = 
      {
        test_id = test_id, 
        name = name, 
        input = input,
        assertion_output = assertion_output
      }
    val test_suite' = 
      {
        id = id, 
        function = function, 
        string_of_input = string_of_input, 
        string_of_output = string_of_output, 
        tests = tests'
      }
  in (test_result, test_suite') end;

in

fun execute (test_suite : ('x, 'y) test_suite) = 
  let
    fun execute_impl test_results test_suite =
      let
        val (test_result, test_suite') = execute_test test_suite
        val test_results' = Queue.enqueue test_result test_results
      in
        if is_empty_test_suite test_suite'
        then test_results'
        else execute_impl test_results' test_suite'
      end;
    val test_results = 
      {
        id = #id test_suite,
        function = #function test_suite,
        string_of_input = #string_of_input test_suite,
        string_of_output = #string_of_output test_suite,
        test_results = 
          if is_empty_test_suite test_suite 
          then Queue.empty 
          else execute_impl Queue.empty test_suite
      }
  in test_results : ('x, 'y) test_results_suite end;

end;



(**** Output ****)

local

fun mk_message 
  string_of_input string_of_output (test_result : ('x, 'y) test_result) =
  let
    fun string_of_output' (Exception exn) =
        "execution failed with the exception '" ^ exnMessage exn ^ "'"
      | string_of_output' (Result y) = string_of_output y
    val test = "Test " ^ Int.toString (#test_id test_result) ^ "\n"
    val function = "Name: " ^ #name test_result ^ "\n"
    val input = 
      "Input data:\n" ^ 
      tabulate (string_of_input (#input test_result)) ^ 
      "\n"
    val output = 
      "Outcome:\n" ^ 
      tabulate (string_of_output' (#output (#assertion_output test_result))) ^
      "\n"
    val expected_output = 
      "Expected outcome:\n" ^ 
      tabulate (#expected_output (#assertion_output test_result)) ^ 
      "\n"
    val test_pass_flag = #status (#assertion_output test_result)
    val status = "Test result: " ^ (if test_pass_flag then "pass" else "fail") 
    val c = String.concatWith 
      " " [test, function, input, output, expected_output, status] 
  in (test_pass_flag, c) end;

in

fun output_test_results 
  error_flag (test_results_suite : ('x, 'y) test_results_suite) = 
  let

    val
      {
        id = id,
        string_of_input = string_of_input,
        string_of_output = string_of_output,
        test_results = test_results,
        ...
      } = test_results_suite

    fun mk_messages cs test_results =
      if Queue.is_empty test_results
      then rev cs
      else
        let
          val (test_result, test_results') = Queue.dequeue test_results
          val (test_pass_flag, c) = 
            mk_message string_of_input string_of_output test_result        
        in mk_messages ((test_pass_flag, c)::cs) test_results' end

    val test_results = mk_messages [] test_results

    val heading_c = "Test results for the test suite: " ^ id ^ "\n"

    val test_cs = map snd test_results 
    val num_tests = length test_cs
    val test_cs = test_cs
      |> String.concatWith "\n\n"
      |> single

    val tests_failed = test_results 
      |> map fst
      |> map not
      |> find_indices I
    val num_tests_failed = length tests_failed
    val num_tests_passed = num_tests - num_tests_failed
    val success_flag = num_tests_failed = 0

    val num_tests_c = "Number of tests: " ^ Int.toString num_tests 
    val num_tests_passed_c = 
      "Number of tests passed: " ^ Int.toString num_tests_passed 
    val num_tests_failed_c = 
      "Number of tests failed: " ^ Int.toString num_tests_failed 
    val summary_c = 
      "Test outcome: " ^ 
      (if success_flag then "success" else "failure")
    val failed_tests_c = 
      if success_flag 
      then "\n"
      else 
        (
          "Failed tests: " ^
          ML_Syntax.print_list Int.toString tests_failed ^ 
          "\n"
        ) 

    val _ =
      (
        heading_c :: 
        num_tests_c :: 
        num_tests_passed_c :: 
        num_tests_failed_c ::
        summary_c :: 
        failed_tests_c ::
        test_cs
      )         
      |> map Pretty.str
      |> Pretty.chunks
      |> Pretty.writeln

    val _ = (not error_flag orelse success_flag) 
      orelse error ("at least one of the " ^ id ^ " tests failed")

  in () end;

end;

end;

Theory UD

(* Title: UD/UD.thy
   Author: Mihails Milehins
   Copyright 2021 (C) Mihails Milehins

Infrastructure for unoverloading definitions.
*)

section‹UD›
theory UD
  imports "../CTR_Tools/CTR_Tools" Main
  keywords "ud" :: thy_decl
begin



subsection‹Import›

ML_file‹UD_With.ML›
ML_file‹UD_Consts.ML›
ML_file‹UD.ML›



subsectionud_with›

setupUD_With.UDWithData.setup

end

File ‹UD_With.ML›

(* Title: UD/UD_With.ML
   Author: Mihails Milehins
   Copyright 2021 (C) Mihails Milehins

Database for the storage of the theorems that provide a relationship 
between the overloaded constants and the unoverloaded constants. 
*)

signature UD_WITH =
sig

structure UDWithData: NAMED_THMS

end;

structure UD_With : UD_WITH =
struct

structure UDWithData = Named_Thms
  (
    val name = binding‹ud_with›
    val description = "Unoverloaded definitions"
  );

end;

File ‹UD_Consts.ML›

(* Title: UD/UD_Consts.ML
   Author: Mihails Milehins
   Copyright 2021 (C) Mihails Milehins

The following infrastructure allows for the exclusion of arbitrary 
constants from being unoverloaded during the invocation of the
algorithm associated with the UD.
*)

signature UD_CONSTS =
sig

structure ConstsData : THEORY_DATA
val const_of_key : theory -> Symtab.key -> term option
val update_const : Symtab.key -> term -> theory -> theory
val remove_const : Symtab.key -> theory -> theory
val get_keys : theory -> Symtab.key list

end;

structure UD_Consts : UD_CONSTS =
struct

structure ConstsData = Theory_Data 
  (
    type T = term Symtab.table
    val empty = Symtab.empty
    val extend = I
    val merge = Symtab.merge (K true)
  )
val const_of_key = Symtab.lookup o ConstsData.get
fun update_const k v = ConstsData.map (Symtab.update (k, v))
fun remove_const k = ConstsData.map (Symtab.delete k)
val get_keys = Symtab.keys o ConstsData.get

end;

File ‹UD.ML›

(* Title: UD/UD.ML
   Author: Mihails Milehins
   Copyright 2021 (C) Mihails Milehins

The following file provides an implementation of the command ud.

The algorithm associated with the command ud draws inspiration and builds
upon the ideas associated with/expressed in 
  - The function Unoverload_Def.unoverload_def that was written by 
Fabian Immler and is available in the file HOL-Types_To_Sets/unoverload_def.ML 
in Isabelle2020. 
  - The conference proceedings article titled "A Mechanized Translation from 
Higher-Order Logic to Set Theory" written by Alexander Krauss and 
Andreas Schropp [1].

[1] Krauss A, Schropp A. A Mechanized Translation from Higher-Order Logic
to Set Theory. In: Kaufmann M, Paulson LC, editors. Interactive Theorem
Proving. Berlin: Springer; 2010. p. 323–38. 
(Lecture Notes in Computer Science; vol. 6172).
*)

signature UD =
sig
datatype ud_thm_out_type = trivial of thm | nontrivial of thm * thm
val axioms_of_ci : 
  theory -> Defs.T -> string * typ -> (string option * string) list
val das_of_ci : theory -> Defs.T -> string * typ -> thm list
val unoverload_definition : 
  binding * mixfix -> string * typ -> theory -> ud_thm_out_type * theory
end;


structure UD : UD =
struct



(**** Auxiliary ****)

fun mk_msg_unoverload_definition msg = "ud: " ^ msg;



(**** Data ****)

datatype ud_thm_out_type = trivial of thm | nontrivial of thm * thm



(**** Definitional Axioms ****)

(*the implementation of axioms_of_ci and da_of_ci are based on elements of 
the code HOL/Types_To_Sets/unoverloading.ML*)
local

fun match_args (Ts, Us) =
  if Type.could_matches (Ts, Us) 
  then 
    Option.map Envir.subst_type 
      (
        SOME (Type.raw_matches (Ts, Us) Vartab.empty) 
          handle Type.TYPE_MATCH => NONE
      )
  else NONE;

in

fun axioms_of_ci thy defs (c, T) = 
  let
    val const_entry = Theory.const_dep thy (c, T);
    val Uss = Defs.specifications_of defs (fst const_entry);
  in
    Uss
    |> filter (fn spec => is_some (match_args (#lhs spec, snd const_entry)))
    |> map (fn Us => (#def Us, #description Us))
  end;

fun das_of_ci thy defs = axioms_of_ci thy defs
  #> map #1
  #> filter is_some
  #> map (the #> try (Thm.axiom thy))
  #> filter is_some
  #> map (the #> Drule.abs_def);

end;



(**** Main ****)

local

val msg_no_cids = 
  mk_msg_unoverload_definition "no suitable constant-instance definitions";
val msg_ud_ex = 
  mk_msg_unoverload_definition "unoverloaded constant already exists";
val msg_multiple_cids = 
  mk_msg_unoverload_definition "multiple constant-instance definitions";
val msg_extra_type_variables = 
  mk_msg_unoverload_definition "specification depends on extra type variables";

fun map_sorts ctxt map_sortsT thm =
  let
    val Ts = thm
      |> Thm.full_prop_of
      |> (fn t => Term.add_tvars t [])
    val instTs = Ts
      |> map TVar
      |> map (Term.map_atyps (map_sortsT (Proof_Context.theory_of ctxt)))
      |> map (Thm.ctyp_of ctxt)
      |> curry op~~ Ts
  in Drule.instantiate_normalize (instTs, []) thm end;

fun trivial_ud thy b cid' =
  let
    val (with_thm, thy') =
      let 
        val b' = Binding.qualify_name true b "with"
        val attr = single UD_With.UDWithData.add
      in Global_Theory.add_thm ((b', cid'), attr) thy end
    val _ = 
      let val c = Thm.derivation_name with_thm
      in
        with_thm
        |> single
        |> CTR_Utilities.thm_printer (Proof_Context.init_global thy') true c
      end
  in (trivial with_thm, thy') end;

fun nontrivial_ud thy defs (b, mixfix) cid T =
  let

    fun mk_with_thm thm_rhs_ct with_def_thm cid =
      let
        val with_def_thm_rhs_ct = with_def_thm
          |> Thm.cprop_of
          |> Thm.dest_equals_rhs
        val inst = Thm.match (with_def_thm_rhs_ct, thm_rhs_ct)
        val const_with_def = with_def_thm
          |> Drule.instantiate_normalize inst
          |> Thm.symmetric
        val with_thm = Thm.transitive cid const_with_def
      in with_thm end;

    val ctxt = Proof_Context.init_global thy

    val cid' = Thm.unvarify_global thy cid 

    val cid_rhs_ct = cid' |> Thm.cprop_of |> Thm.dest_equals_rhs

    val (cid_rhs_t, ctxt') = cid_rhs_ct
      |> Thm.term_of 
      |> Logic.unoverload_types_term thy
      |> Logic.unvarify_types_local_term ctxt

    val rhs_consts = 
      let
        val sort_const_cs = Term.add_tvarsT T []
          |> map #2
          |> map (Sorts.params_of_sort thy)
          |> flat
          |> distinct op=
          |> map #1
        val consts = Term.add_consts cid_rhs_t []
        val consts_no_cids = consts
          |> map (fn const => (const, axioms_of_ci thy defs const))
          |> filter (fn (_, cid_opt) => null cid_opt)
          |> map fst
        val consts_in_sort = consts
          |> filter (fn (_, T) => Term.has_tfreesT T)
          |> filter (fn (c, _) => member op= sort_const_cs c)
        val elim_consts = thy
          |> UD_Consts.get_keys
          |> map (UD_Consts.const_of_key thy #> the #> dest_Const)
        val consts_out = consts_no_cids @ consts_in_sort 
          |> distinct op=
          |> filter_out (member (swap #> Term.could_match_const) elim_consts)
      in consts_out end

    val (cs, ctxt'') = ctxt'
      |> Variable.variant_fixes (map (#1 #> Long_Name.base_name) rhs_consts) 

    val fv_of_const = 
      (map Const rhs_consts ~~ map Free (cs ~~ map #2 rhs_consts))
      |> AList.lookup op=
      |> mk_opt_id I

    val rhs_const_ts = map Const rhs_consts
    val arg_ts = map fv_of_const rhs_const_ts

    val cid_rhs_t' = map_aterms fv_of_const cid_rhs_t

    fun declare_const_with thy =
      let
        val T = map type_of arg_ts ---> type_of cid_rhs_t'
        val b' = Binding.qualify_name true b "with"
      in Sign.declare_const_global ((b', T), mixfix) thy end

    val (lhst, thy') = declare_const_with thy

    val lhst' = Term.list_comb (lhst, arg_ts)

    val (with_def_thm, thy'') =
      let
        val b' = Binding.qualify_name true b "with_def"
        val def_t = Logic.mk_equals (lhst', cid_rhs_t')
      in 
        Global_Theory.add_defs false [((b', def_t), [])] thy' |>> the_single
          handle ERROR c =>
            let val msg_match = "Specification depends on extra type variables"
            in 
              if String.isSubstring msg_match c
              then error msg_extra_type_variables 
              else error c 
            end
      end

    val _ = 
      let val c = Thm.derivation_name with_def_thm
      in
        with_def_thm
        |> single
        |> CTR_Utilities.thm_printer (Proof_Context.init_global thy'') true c
      end

    val (with_thm, thy''') =
      let
        val b' = Binding.qualify_name true b "with"
        val with_thm = cid' 
          |> mk_with_thm cid_rhs_ct with_def_thm
          |> singleton (Proof_Context.export ctxt'' ctxt)
        val attr = single UD_With.UDWithData.add
      in Global_Theory.add_thm ((b', with_thm), attr) thy'' end

    val _ = 
      let val c = Thm.derivation_name with_thm
      in 
        with_thm
        |> single
        |> CTR_Utilities.thm_printer (Proof_Context.init_global thy''') true c
      end

  in (nontrivial (with_def_thm, with_thm), thy''') end;

in

fun unoverload_definition (b, mixfix) (c, T) thy =
  let


    (*auxiliary*)

    fun get_ud_const lr ud_thms = 
      let 
        val get_ud_const_impl = Thm.cprop_of
          #> lr
          #> Thm.term_of
          #> strip_abs_body
          #> head_of
          #> dest_Const
      in map get_ud_const_impl ud_thms end


    (*main*)

    val ctxt = Proof_Context.init_global thy
    
    val defs = Theory.defs_of thy

    val ud_thms = ctxt
      |> UD_With.UDWithData.get
      |> map (Local_Defs.meta_rewrite_rule ctxt)

    val T' = T
      |> Type.default_sorts_of_empty_sorts thy
      |> Logic.varifyT_mixed_global

    val _ =
      let 
        val ud_lhs_consts = get_ud_const Thm.dest_equals_lhs ud_thms
        val ex = member (swap #> Term.could_match_const) ud_lhs_consts (c, T')
      in not ex orelse error msg_ud_ex end

    val cids = das_of_ci thy defs (c, T')
    
    val cid = 
      if null cids 
      then error msg_no_cids
      else 
        if length cids = 1 
        then the_single cids 
        else error msg_multiple_cids

    val cid' = 
      let
        val lhs_const = cid
          |> Thm.cprop_of
          |> Thm.dest_equals_lhs
          |> Thm.term_of
          |> head_of
        val insts = (lhs_const, Const (c, T'))
          |> apply2 (Thm.cterm_of ctxt)
          |> Thm.first_order_match
      in 
        cid
        |> Drule.instantiate_normalize insts
        |> map_sorts ctxt Type.default_sorts_of_empty_sorts
        |> Local_Defs.unfold ctxt ud_thms
      end

    val cid_rhs_t = cid' 
      |> Thm.cprop_of 
      |> Thm.dest_equals_rhs
      |> Thm.term_of 

    val trivial_flag = 
      let val ud_rhs_consts = get_ud_const Thm.dest_equals_rhs ud_thms
      in
        Term.is_comb cid_rhs_t
        andalso cid_rhs_t |> head_of |> is_Const
        andalso cid_rhs_t
          |> head_of
          |> dest_Const
          |> member (swap #> Term.could_match_const) ud_rhs_consts
      end

    val (ud_thm_out, thy') =
      if trivial_flag
      then trivial_ud thy b cid'
      else nontrivial_ud thy defs (b, mixfix) cid' T'

  in (ud_thm_out, thy') end;

end;



(**** Interface ****)

val msg_not_constant = 
  mk_msg_unoverload_definition "the input term is not a constant";

fun process_ud ((b_opt, t), mixfix) thy = 
  let 
    val t' = Proof_Context.read_term_pattern (Proof_Context.init_global thy) t
    val (c, T) = dest_Const t'
      handle TERM _ => error msg_not_constant 
    val b = case b_opt of
        SOME b => b
      | NONE => Binding.name (Long_Name.base_name c)
  in unoverload_definition (b, mixfix) (c, T) thy |> #2 end;

val parse_ud = Scan.option Parse.binding -- Parse.const -- Parse.opt_mixfix';

val _ = Outer_Syntax.command
  command_keywordud 
  "unoverloading of constant-instance definitions"
  (parse_ud >> (process_ud #> Toplevel.theory));

end;

Theory CTR

(* Title: CTR/CTR.thy
   Author: Mihails Milehins
   Copyright 2021 (C) Mihails Milehins

Infrastructure for the synthesis of the transfer rules and relativized 
constants.
*)


sectionCTR›
theory CTR
  imports 
    "../UD/UD"
    "HOL-Library.Conditional_Parametricity"
  keywords "ctr_relator" :: thy_defn
    and "ctr" :: thy_defn
    and "trp"
    and "synthesis"
    and "relativization"
    and "hybrid"
    and "parametricity"
begin



subsection‹Import›

ML_file "CTR_Relators.ML"
ML_file "CTR_Foundations.ML"
ML_file "CTR_Algorithm.ML"
ML_file "CTR_Conversions.ML"
ML_file "CTR_Relativization.ML"
ML_file "CTR_Parametricity.ML"
ML_file "CTR_Postprocessing.ML"
ML_file "CTR.ML"



subsection‹Rewriting›

named_theorems ctr_blank
named_theorems ctr_simps

lemma ctr_simps_pred_fun_top_eq_range[ctr_simps]: 
  "pred_fun top (λx. x  R) f = (range f  R)"
  by auto
lemma ctr_simps_pred_prod_eq_cart[ctr_simps]: 
  "pred_prod (λx. x  A) (λx. x  B) = (λx. x  A × B)"
  by auto
lemma ctr_simps_pred_fun_eq_image[ctr_simps]: 
  "pred_fun (λx. x  D) (λx. x  R) f  (f ` D  R)"
  by auto
lemma ctr_simps_in_iff[ctr_simps]: "(xA. x  U)  A  U" by auto
lemma ctr_simps_subset_pow_iff[ctr_simps]: "(AS. A  U)  S  Pow U" 
  by auto
lemma ctr_simps_subset_pow_iff'[ctr_simps]: 
  "(A. A  S  A  U)  S  Pow U" 
  by auto
lemma ctr_simps_subset_pow_iff''[ctr_simps]: "(S  {S. S  U})  S  Pow U" 
  by (simp add: subset_eq)
lemma ctr_simps_range_ss_iff[ctr_simps]: "(x. f x  U)  range f  U" 
  by auto
lemma ctr_simps_range_pow_ss_iff[ctr_simps]: "(x. f x  U)  range f  Pow U" 
  by auto
lemma ctr_simps_Ball_def'[ctr_simps]: "(x. x  A  P x)  (x  A. P x)" 
  by auto
lemma ctr_simps_True_imp[ctr_simps]: "(True  A)  A" by simp
lemma ctr_simps_True_conj[ctr_simps]: "(True  A)  A" by simp
lemma ctr_simps_conj_True[ctr_simps]: "(A  True)  A" by simp
lemma ctr_simps_top_True[ctr_simps]: "top A  True" by auto
lemma ctr_simps_Ball_True[ctr_simps]: "(xU. True)  True" by auto
lemma ctr_simps_Ball_UNIV[ctr_simps]: "(nUNIV. A n)  (n. A n)" by simp
lemma ctr_simps_Bex_UNIV[ctr_simps]: "(nUNIV. A n)  (n. A n)" by simp
lemma ctr_simps_subset_Pow[ctr_simps]: "{A. A  U} = Pow U" by blast
lemma ctr_simps_mem_Collect_eq[ctr_simps]: "(a  Collect P) = P a"
  by (rule mem_Collect_eq)
lemma ctr_simps_relation_top_empty_eq[ctr_simps]: "(λx. x  UNIV) = top" 
  by auto
lemma ctr_simps_pred_fun_eq[ctr_simps]: 
  "pred_fun A B = (λf. x. A x  B (f x))" 
  by simp
lemma ctr_simps_subset_eq_sym[ctr_simps]: "(xA. x  B)  (A  B)" by auto
lemma ctr_simps_UNIV_I[ctr_simps]: "x  UNIV" by simp
lemma ctr_simps_UNIV_def[ctr_simps]: "{x. True} = UNIV" by simp
lemma ctr_simps_conj_commute[ctr_simps]: "(P  Q)  (Q  P)" by auto
lemma ctr_simps_conj_absorb[ctr_simps]: "(A  A)  A" by simp
lemma ctr_simps_conj_left_absorb[ctr_simps]: "(A  A  B)  (A  B)" by simp
lemma ctr_simps_inf_idem[ctr_simps]: "inf a a = (a::'a::semilattice_inf)" 
  by simp
lemma ctr_simps_sup_idem[ctr_simps]: "sup a a = (a::'a::semilattice_sup)" 
  by simp
lemma ctr_simps_inf_assoc[ctr_simps]: 
  "inf (inf a b) c = inf (a::'a::semilattice_inf) (inf b c)"
  by (auto simp: semilattice_inf_class.inf.assoc)
lemma ctr_simps_sup_assoc[ctr_simps]:
  "sup (sup a b) c = sup (a::'a::semilattice_sup) (sup b c)"
  by (auto simp: semilattice_sup_class.sup.assoc)
lemma ctr_simps_Collect_mem_eq[ctr_simps]: "{x. x  U} = U" by simp



subsection‹tts relators›

ctr_relator rel_set
ctr_relator rel_filter

end

File ‹CTR_Relators.ML›

(* Title: CTR/CTR_Relators.ML
   Author: Mihails Milehins
   Copyright 2021 (C) Mihails Milehins

Implementation of the functionality associated with the ctr relators,
including the command ctr_relator for registering the ctr relators. 
*)


signature CTR_RELATORS =
sig
structure RelatorData: GENERIC_DATA
val get_relator_data_generic : Context.generic -> RelatorData.T
val get_relator_data_proof : Proof.context -> RelatorData.T
val get_relator_data_global : theory -> RelatorData.T
val relator_of_generic : Context.generic -> Symtab.key -> term option
val relator_of_proof : Proof.context -> Symtab.key -> term option
val relator_of_global : theory -> Symtab.key -> term option
val update_relator : Symtab.key -> term -> local_theory -> local_theory
val process_ctr_relator : string -> Proof.context -> local_theory
val pr_of_typ : Proof.context -> ((string * sort) * term) list -> typ -> term
val bnf_relator_of_type_name : Proof.context -> string -> term option
end;

structure CTR_Relators : CTR_RELATORS =
struct




(**** Data ****)



(*** Data container ***)

structure RelatorData = Generic_Data
  (
    type T = term Symtab.table
    val empty = Symtab.empty
    val extend = I
    val merge = Symtab.merge (K true)
  );
 

(*** Generic operations on the relator data ***)

val get_relator_data_generic = RelatorData.get;
val get_relator_data_proof = Context.Proof #> get_relator_data_generic;
val get_relator_data_global = Context.Theory #> get_relator_data_generic;
fun relator_of_generic context = context 
  |> get_relator_data_generic 
  |> Symtab.lookup
  #> 
    (
      context 
      |> Context.theory_of 
      |> (Morphism.transfer_morphism #> Morphism.term) 
      |> Option.map
    );
val relator_of_proof = Context.Proof #> relator_of_generic;
val relator_of_global = Context.Theory #> relator_of_generic;
fun update_relator k rel = Local_Theory.declaration
  {pervasive=true, syntax=false} 
  (fn phi => (k, Morphism.term phi rel) |> Symtab.update |> RelatorData.map);




(**** User input analysis ****)

fun mk_msg_ctr_relator msg = "ctr_relator: " ^ msg;
val mk_msg_not_const = "the input must be a constant term";
val mk_msg_not_body_bool = "the body of the type of the input must be bool";
val mk_msg_not_binders_2 = 
  "the type of the input must have more than two binders";
val mk_msg_not_binders_binrelT = 
  "all of the binders associated with the type of the input" ^
  "except the last two must be the binary relation types";
val mk_msg_no_dup_binrelT = 
  "the types of the binders of the binary relations associated " ^
  "with the type of the input must have no duplicates";
val mk_msg_not_binders_binrelT_ftv_stv = 
  "the types of the binders of the binary relation types associated " ^
  "with the input type must be either free type variables or " ^
  "schematic type variables";
val mk_msg_not_type_constructor = 
  "the last two binders of the input type must be " ^
  "the results of an application of a type constructor";
val mk_msg_not_identical_type_constructors =
  "the type constructors that are associated with the last two binders " ^
  "of the input type must be identical";
val mk_msg_not_identical_input_types =
  "the sequences of the input types to the type constructors that are " ^
  "associated with the last two binders of the input type must be " ^
  "identical to the sequences of the types formed by concatenating the " ^
  "type variables associated with the left hand side and the right " ^
  "hand side of the binary relation types, respectively";



(**** Command for the registration of ctr relators ****)

fun relator_type_name_of_type T =
  let
  
    val _ = T |> body_type |> curry op= HOLogic.boolT
      orelse error (mk_msg_ctr_relator mk_msg_not_body_bool)

    val binders = binder_types T
    val n = length binders
    val _ = n |> (fn n => n > 2) 
      orelse error (mk_msg_ctr_relator mk_msg_not_binders_2)

    val (relTs, (mainT_lhs, mainT_rhs)) = binders
      |> chop (n - 2)
      ||> chop 1
      ||> apfst the_single
      ||> apsnd the_single

    val _ = relTs |> map HOLogic.is_binrelT |> List.all I
      orelse error (mk_msg_ctr_relator mk_msg_not_binders_binrelT)

    val (lhs_tvars, rhs_tvars) = relTs 
      |> map HOLogic.dest_binrelT
      |> split_list
    val tvars = lhs_tvars @ rhs_tvars

    val _ = tvars |> has_duplicates op= |> not
      orelse error (mk_msg_ctr_relator mk_msg_no_dup_binrelT)
    val _ = tvars |> map (fn T => is_TVar T orelse is_TFree T) |> List.all I
      orelse error (mk_msg_ctr_relator mk_msg_not_binders_binrelT_ftv_stv)
    val _ = is_Type mainT_lhs
      orelse error (mk_msg_ctr_relator mk_msg_not_type_constructor)
    val _ = is_Type mainT_rhs
      orelse error (mk_msg_ctr_relator mk_msg_not_type_constructor)
    
    val mainT_lhs = dest_Type mainT_lhs
    val mainT_rhs = dest_Type mainT_rhs

    val _ = op= (apply2 #1 (mainT_lhs, mainT_rhs))
      orelse error (mk_msg_ctr_relator mk_msg_not_identical_type_constructors)
    val _ = lhs_tvars = #2 mainT_lhs
      orelse error (mk_msg_ctr_relator mk_msg_not_identical_input_types)
    val _ = rhs_tvars = #2 mainT_rhs
      orelse error (mk_msg_ctr_relator mk_msg_not_identical_input_types)

  in #1 mainT_lhs end;

fun process_ctr_relator args ctxt = 
  let
    val t = Syntax.read_term ctxt args
    val _ = is_Const t orelse error (mk_msg_ctr_relator mk_msg_not_const)
    val c = relator_type_name_of_type (type_of t)
  in update_relator c t ctxt end;

val _ = Outer_Syntax.local_theory 
  command_keywordctr_relator 
  "registration of the ctr relators"
  (Parse.const >> process_ctr_relator);




(**** ctr relators combined with the bnf relators ****)

fun bnf_relator_of_type_name ctxt c = 
  let
    fun bnf_relator_of_type_name ctxt c = 
      let 
        val relator_of_bnf = BNF_Def.rel_of_bnf 
          #> strip_comb 
          #> #1
          #> dest_Const
          #> #1
          #> Syntax.read_term ctxt
          #> Logic.varify_global
      in c |> BNF_Def.bnf_of ctxt |> Option.map relator_of_bnf end
  in
    case relator_of_proof ctxt c of
        SOME t => SOME t
      | NONE => bnf_relator_of_type_name ctxt c
  end;




(**** Conversion of a type to a parametricity relation ****)

(* 
The algorithm follows an outline of an algorithm for a similar purpose 
suggested in section 4.1 of the Ph.D. thesis of Ondřej Kunčar titled 
"Types, Abstraction and Parametric Polymorphism in Higher-Order Logic". 
*)
fun pr_of_typ ctxt ftv_spec_relt T =
  let
    fun pr_of_typ _ trel (TFree ftv_spec) = trel ftv_spec
      | pr_of_typ _ _ (Type (c, [])) = 
          Const 
            (
              const_name‹HOL.eq›, 
              HOLogic.mk_binrelT (Type (c, []), Type (c, []))
            )
      | pr_of_typ relator_of_type_name trel (Type (c, Ts)) = 
          let
            val constt = relator_of_type_name c
              handle Option => 
                raise TYPE ("pr_of_typ: no relator", [Type (c, Ts)], []) 
            val constT = type_of constt
            val binders = constT |> binder_types |> take (length Ts)
            val argts = map (pr_of_typ relator_of_type_name trel) Ts
            val argTs = map type_of argts
            val tyenv_match = Type.typ_matches 
              (Proof_Context.tsig_of ctxt) (binders, argTs) Vartab.empty
              handle Type.TYPE_MATCH => 
                raise TYPE ("pr_of_typ: invalid relator", [Type (c, Ts)], [])
            val constt = constt
              |> dest_Const
              ||> K (Envir.subst_type tyenv_match constT)
              |> Const
          in list_comb (constt, argts) end
      | pr_of_typ _ _ T = raise TYPE ("pr_of_typ: type", single T, [])
    val trel = AList.lookup op= ftv_spec_relt #> the
  in pr_of_typ (bnf_relator_of_type_name ctxt #> the) trel T end;

end;

File ‹CTR_Foundations.ML›

(* Title: CTR/CTR_Foundations.ML
   Author: Mihails Milehins
   Copyright 2021 (C) Mihails Milehins

Basic data types for CTR.
*)

signature CTR_FOUNDATIONS =
sig
datatype ctr_algorithm = relativization | parametricity
datatype ctr_alg_out = 
  ALGRelativization of thm | ALGParametricity of thm | ALGFailure
datatype ctr_pp_out = 
    PPRelativization of (thm * thm) * local_theory 
  | PPParametricity of thm * local_theory
  | PPFailure of local_theory;
val apply_alg_out : (thm -> thm) -> ctr_alg_out -> ctr_alg_out
val lthy_of_pp_out : ctr_pp_out -> local_theory 
val string_of_pp_out :  ctr_pp_out -> string
end;

structure CTR_Foundations : CTR_FOUNDATIONS =
struct

datatype ctr_algorithm = relativization | parametricity;
datatype ctr_alg_out = 
  ALGRelativization of thm | ALGParametricity of thm | ALGFailure;
datatype ctr_pp_out = 
    PPRelativization of (thm * thm) * local_theory 
  | PPParametricity of thm * local_theory
  | PPFailure of local_theory;

fun apply_alg_out f (ALGRelativization thm) = ALGRelativization (f thm)
  | apply_alg_out f (ALGParametricity thm) = ALGParametricity (f thm)
  | apply_alg_out _ ALGFailure = ALGFailure;

fun lthy_of_pp_out (PPRelativization (_, lthy)) = lthy
  | lthy_of_pp_out (PPParametricity (_, lthy)) = lthy
  | lthy_of_pp_out (PPFailure lthy) = lthy;

fun string_of_pp_out (PPRelativization ((ow_def_thm, tr_thm), lthy)) = 
      let
        val preamble_c = 
          "PPRelativization ((ow_def_thm, tr_thm), lthy), where"
        val ow_def_thm_c = "ow_def_thm: " ^ Thm.string_of_thm lthy ow_def_thm
        val tr_thm_c = "tr_thm: " ^ Thm.string_of_thm lthy tr_thm
        val lthy_c = "lthy: unknown local theory" 
        val out_c = [preamble_c, ow_def_thm_c, tr_thm_c, lthy_c] 
          |> String.concatWith "\n"
      in out_c end
  | string_of_pp_out (PPParametricity (tr_thm, lthy)) =
      let
        val preamble_c = "PPParametricity (tr_thm, lthy), where"
        val tr_thm_c = "tr_thm" ^ Thm.string_of_thm lthy tr_thm
        val lthy_c = "lthy: unknown local theory" 
        val out_c = [preamble_c, tr_thm_c, lthy_c] 
          |> String.concatWith "\n"    
      in out_c end
  | string_of_pp_out (PPFailure _) = "Failure";

end;

open CTR_Foundations;

File ‹CTR_Algorithm.ML›

(* Title: CTR/CTR_Algorithm.ML
   Author: Mihails Milehins
   Copyright 2021 (C) Mihails Milehins

Abstract interface for the definition of an algorithm for the synthesis
of transfer rules and relativized definitions.
*)

signature CTR_ALGORITHM =
sig

val apply : 
  Proof.context ->
  bool * thm list option ->
  thm list ->
  ((indexname * sort) * term) list ->
  thm -> 
  ctr_alg_out

end;

File ‹CTR_Conversions.ML›

(* Title: CTR/CTR_Conversions.ML
   Author: Mihails Milehins
   Copyright 2021 (C) Mihails Milehins

Conversion of a transfer rule goal to a parametricity property goal.
*)

signature CTR_CONVERSIONS =
sig

val const_of_def : Proof.context -> thm -> string * typ
val trt_of_const : 
  Proof.context -> ((indexname * sort) * term) list -> string * typ -> term
val dest_trt : term -> term * term * term
val prt_of_trt : term -> term

end;

structure CTR_Conversions : CTR_CONVERSIONS =
struct

(*conversion of stvs to ftvs based on type_specs: rhs*)
fun type_specs_rhs_ftv_of_stv type_specs = type_specs 
  |> map (apsnd (type_of #> HOLogic.dest_binrelT #> #2))  
  |> AList.lookup op= #> the;

(*conversion of stvs to ftvs based on type_specs: lhs*)
fun type_specs_lhs_ftv_of_stv type_specs = type_specs
  |> map (apsnd (type_of #> HOLogic.dest_binrelT #> #1))  
  |> AList.lookup op= #> the;

(*extraction of a constant from a definition*)
fun const_of_def ctxt thm = thm 
  |> Local_Defs.meta_rewrite_rule ctxt
  |> Thm.full_prop_of
  |> Logic.dest_equals
  |> #1
  |> strip_comb
  |> #1
  |> dest_Const;

(*transfer rule goal from a constant*)
fun trt_of_const ctxt type_specs const_spec = 
  let

    fun mk_lhs_stv T = (("a", 0), T)

    val rhs_ftv_of_stv = type_specs_rhs_ftv_of_stv type_specs
    val lhs_ftv_of_stv = type_specs_lhs_ftv_of_stv type_specs

    val rhs_const_spec = apsnd (map_type_tvar rhs_ftv_of_stv) const_spec
    val lhs_var_spec = const_spec 
      |> #2 
      |> mk_lhs_stv 
      ||> map_type_tvar lhs_ftv_of_stv

    val prt =                               
      let val type_specs = map (apfst (rhs_ftv_of_stv #> dest_TFree)) type_specs
      in rhs_const_spec |> #2 |> CTR_Relators.pr_of_typ ctxt type_specs end
    
  in prt $ (Var lhs_var_spec) $ (Const rhs_const_spec) end;

(*transfer rule destruction*)
fun dest_trt (pr $ lhst $ rhst) = (pr, lhst, rhst)
  | dest_trt t = raise TERM ("dest_trt", single t);

(*conversion of a transfer rule goal to a parametricity property goal*)
fun prt_of_trt trt =
  let val (pr, lhst, rhst) = dest_trt trt
  in pr $ Const (rhst |> dest_Const |> #1, type_of lhst) $ rhst end;

end;

File ‹CTR_Relativization.ML›

(* Title: CTR/CTR_Relativization.ML
   Author: Mihails Milehins
   Copyright 2021 (C) Mihails Milehins

Implementation of the algorithm CTR II: Relativization.
*)


structure CTR_Relativization : CTR_ALGORITHM =
struct

open More_Simplifier;

fun pp_tac ctxt unfld_thms def_thm i = 
  SELECT_GOAL (Local_Defs.unfold_tac ctxt (def_thm :: unfld_thms)) i
  THEN CONVERSION (Thm.beta_conversion true) i
  THEN CONVERSION Thm.eta_conversion i
  THEN Transfer.transfer_prover_start_tac ctxt i
  THEN (TRYALL (Transfer.transfer_step_tac ctxt))
  THEN (Transfer.transfer_prover_end_tac ctxt i);

fun try_prove ctxt xs asms prop tac = SOME (Goal.prove ctxt xs asms prop tac)
  handle ERROR _  => NONE;

fun try_relativization ctxt simp_spec_opt trt unfld_thms def_thm =
  let
    val tr_thm_opt = pp_tac ctxt unfld_thms def_thm 1
      |> K
      |> try_prove ctxt [] [] (HOLogic.mk_Trueprop trt) 
    val tr_thm_opt = Option.map (rewrite_simp_opt ctxt simp_spec_opt) tr_thm_opt 
  in tr_thm_opt end;

fun apply ctxt (synthesis, simp_spec_opt) assms type_specs def_thm = 
  let
    val is_equality_assms = 
      let val is_is_equality_thm = 
        Thm.full_prop_of 
        #> HOLogic.dest_Trueprop 
        #> Transfer.is_is_equality
      in
        assms
        |> filter is_is_equality_thm
        |> map (@{thm is_equality_def} |> single |> Local_Defs.unfold ctxt)
      end
    val trt = def_thm 
      |> CTR_Conversions.const_of_def ctxt
      |> CTR_Conversions.trt_of_const ctxt type_specs
    val pp_thm_opt =
      let val ppt = CTR_Conversions.prt_of_trt trt
      in
        pp_tac ctxt is_equality_assms def_thm 1
        |> K
        |> try_prove ctxt [] [] (HOLogic.mk_Trueprop ppt) 
      end
    val tr_thm_opt = 
      if pp_thm_opt |> Option.isSome |> not andalso synthesis
      then try_relativization ctxt simp_spec_opt trt is_equality_assms def_thm
      else NONE
    val output = 
      if Option.isSome tr_thm_opt 
      then ALGRelativization (the tr_thm_opt)
      else 
        if Option.isSome pp_thm_opt
        then ALGParametricity (the pp_thm_opt)
        else ALGFailure
  in output end;

end;

File ‹CTR_Parametricity.ML›

(* Title: CTR/CTR_Parametricity.ML
   Author: Mihails Milehins
   Copyright 2021 (C) Mihails Milehins

Implementation of the invocation of the algorithm CTR I: Parametricity.
*)

structure CTR_Parametricity : CTR_ALGORITHM =
struct

fun theorem_format_error ctxt thm =
  let
    val msg_main = "Unexpected format for the definition." 
    val msg = [(Pretty.para msg_main), Thm.pretty_thm ctxt thm]
      |> Pretty.chunks 
      |> Pretty.string_of;
  in error msg end;

fun gen_parametric_constant settings lthy def_thm =
  let
    val eq = Ctr_Sugar_Util.mk_abs_def def_thm 
      handle TERM _ => theorem_format_error lthy def_thm
    val goalt = Conditional_Parametricity.mk_param_goal_from_eq_def lthy eq
    val thm = Conditional_Parametricity.prove_goal settings lthy (SOME eq) goalt
  in thm end;

fun apply ctxt (_, _) _ _ def_thm = 
  let
    val thm = def_thm
      |> gen_parametric_constant Conditional_Parametricity.quiet_settings ctxt
  in ALGParametricity thm end;

end;

File ‹CTR_Postprocessing.ML›

(* Title: CTR/CTR_Postprocessing.ML
   Author: Mihails Milehins
   Copyright 2021 (C) Mihails Milehins

Postprocessing of the output of the algorithms associated with the CTR.
*)


signature CTR_POSTPROCESSING =
sig

val postprocess_parametricity : 
  binding -> thm -> local_theory -> ctr_pp_out
val postprocess_relativization : 
  binding -> mixfix -> thm -> Proof.context -> ctr_pp_out
val postprocess_failure : Proof.context -> ctr_pp_out

end;

structure CTR_Postprocessing : CTR_POSTPROCESSING =
struct

open CTR_Utilities;

(*post-processing of an arbitrary transfer rule*)
fun postprocess_transfer_rule b thm ctxt = 
  let
    val b = ((b |> Binding.path_of |> map fst) @ [Binding.name_of b, "transfer"])
      |> Long_Name.implode
      |> Binding.qualified_name_mandatory
    val lthy = ctxt
      |> Local_Theory.note 
        (
          (b, Transfer.transfer_add |> K |> Attrib.internal |> single), 
          single thm
        )
      |>> #2
      |>> the_single
      |> #2
    val _ =
      let
        val lthy_print = 
          ("Transfer.lifting_syntax" |> single |> Bundle.includes) lthy
      in
        thm 
        |> single
        |> (Local_Theory.full_name lthy_print b |> thm_printer lthy_print true)
      end
  in lthy end;

(*post-processing of a parametricity property*)
fun postprocess_parametricity b thm ctxt = 
  PPParametricity (thm, postprocess_transfer_rule b thm ctxt);

(*post-processing of a relativization*)
fun postprocess_relativization b mf thm ctxt = 
  let
    
    val (_, rhst, _) = thm 
      |> Thm.concl_of
      |> HOLogic.dest_Trueprop 
      |> CTR_Conversions.dest_trt
    val (rhst, ctxt') = Logic.unvarify_local_term ctxt rhst 
    val (absts, rhst) = rhst
      |> Term.strip_abs_eta (rhst |> strip_abs_vars |> length) 
    val argts = rhst
      |> (fn t => Term.add_frees t [])
      |> rev
      |> subtract op= absts
      |> curry (swap #> op@) absts  
      |> map Free
    val (lhst, lthy) = 
      let 
        fun declare_const_with thy = 
          let val T = map type_of argts ---> type_of rhst 
          in Sign.declare_const_global ((b, T), mf) thy end
      in Local_Theory.raw_theory_result declare_const_with ctxt' end
    val lhst = Term.list_comb (lhst, argts)
    fun make_with_def thy = 
      let
        val b =
          ((b |> Binding.path_of |> map fst) @ [Binding.name_of b ^ "_def"])
          |> Long_Name.implode
          |> Binding.qualified_name_mandatory
      in
        Global_Theory.add_defs 
          false (single ((b, Logic.mk_equals (lhst, rhst)), [])) thy
        |>> the_single
      end
    val (ow_def_thm, lthy') = Local_Theory.raw_theory_result make_with_def lthy
    val _ = ow_def_thm 
      |> single
      |> (ow_def_thm |> Thm.derivation_name |> thm_printer lthy' true)
    val thm = Thm.pure_unfold lthy' (ow_def_thm |> Thm.symmetric |> single) thm
  in 
    PPRelativization ((ow_def_thm, thm), postprocess_transfer_rule b thm lthy') 
  end;

(*post-processing of a failed attempt to perform relativization*)
fun postprocess_failure lthy = PPFailure lthy;

end;

File ‹CTR.ML›

(* Title: CTR/CTR.ML
   Author: Mihails Milehins
   Copyright 2021 (C) Mihails Milehins

The implementation of the command ctr.
*)

signature CTR =
sig

datatype alg_input = 
    ALG_PP of ((binding option * (Facts.ref * Token.src list)) * mixfix) list 
  | ALG_RP of
     (
       (
         (
           (string * (Facts.ref * Token.src list) option) option *
           (string, string, Facts.ref) Element.ctxt list
         ) *
         (string * string) list
       ) * ((binding option * (Facts.ref * Token.src list)) * mixfix) list
     )
val ctr_parser : alg_input parser
val process_parametricity :
  (binding option * thm) * mixfix ->
  Proof.context -> 
  ctr_pp_out
val process_relativization : 
  (string * thm list option) option ->
  Element.context list ->
  (string * string) list ->
  (binding option * thm) * mixfix ->
  Proof.context -> 
  ctr_pp_out
val process_ctrs : alg_input -> Proof.context -> Proof.context

end;


structure CTR : CTR =
struct



(**** Data ****)

datatype alg_input = 
    ALG_PP of ((binding option * (Facts.ref * Token.src list)) * mixfix) list 
  | ALG_RP of
     (
       (
         (
           (string * (Facts.ref * Token.src list) option) option *
           (string, string, Facts.ref) Element.ctxt list
         ) * (string * string) list
       ) * ((binding option * (Facts.ref * Token.src list)) * mixfix) list
     );



(**** Parser ****)

local

val algorithm_parser = keywordrelativization || keywordparametricity;

val synthesis_parser = 
  Scan.option (keywordsynthesis -- Scan.option Parse.thm);

val type_specs_parser = 
  Scan.optional
    (
      keywordtrp |-- 
      Parse.and_list (kw_bo |-- Parse.type_var -- Parse.term --| kw_bc)
    ) 
    [];

val thm_specs_parser =
  keywordin |-- 
  Parse.and_list 
    (
      (Scan.option (Parse.binding --| keyword:)) -- 
      Parse.thm -- 
      Parse.opt_mixfix'
    );

fun rel_opt_thm_name s =
  Scan.optional
    (
      (
        Parse.binding -- Parse.opt_attribs || Parse.attribs >> 
          pair Binding.empty
      ) --| Parse.$$$ s
    )
    Binding.empty_atts;

(* 
The function was ported with amendments from the file Parse_Spec.ML in the main 
distribution of Isabelle 2021.
*)
val rel_loc_element =
  keywordfixes |-- Parse.!!! Parse_Spec.locale_fixes >> Element.Fixes ||
  keywordassumes |-- 
    Parse.!!! (Parse.and_list1 (rel_opt_thm_name ":" -- Scan.repeat1 Parse.propp))
    >> Element.Assumes;

(* 
The function was ported with amendments from the file Parse_Spec.ML in the main 
distribution of Isabelle 2021.
*)
val rel_context_element = 
  Parse.group (fn () => "context element") rel_loc_element;

fun parametricity_parser tokens = thm_specs_parser tokens |>> ALG_PP;

fun relativization_parser tokens = tokens
  |> 
    (
      synthesis_parser -- 
      Scan.repeat rel_context_element -- 
      type_specs_parser --
      thm_specs_parser
    ) 
  |>> ALG_RP;

in

val ctr_parser = 
  algorithm_parser :|-- 
  (           
    fn c => case c of
        "relativization" => relativization_parser
      | "parametricity" => parametricity_parser
      | _ => error "relativization or parametricity expected"
  );

end;




(**** User input analysis ****)

fun mk_msg_ctr msg = "ctr: " ^ msg;
fun mk_msg_binrel c = c ^ ": trp must consist of (stv, binary relation) pairs" ;
fun mk_msg_binrel_ftv c = c ^ 
  ": the user-specified binary relations must be defined over type variables";
fun mk_msg_no_dup_stvs c = c ^ ": duplicate stvs";
fun mk_msg_no_dup_binrel_ftvs c = c ^ 
  ": duplicate ftvs in the specification of the binary relations";
fun mk_msg_def ctxt t = Syntax.string_of_term ctxt t ^ " is not a definition";

fun check_def ctxt thm = 
  let
    val t = Thm.full_prop_of thm
    val _ = (ctxt, t) |> #2 |> Logic.is_equals
      orelse error (mk_msg_def ctxt t |> mk_msg_ctr)
  in () end;

fun mk_msg_no_relator c = "no relator found for the type constructor " ^ c
fun mk_msg_invalid_relator c = 
  "the relator found for the type constructor " ^ c ^
  " is not suitable (is there is a mismatch of type variables?)"



(**** Preprocessing ****)



(*** Preprocessing of the type-relation pairs ***)

fun preprocess_type_specs ctxt type_specs =
  let

    fun preprocess_entry ctxt ctxt' (T, relt) = 
      let
        val v = T |> Syntax.parse_typ ctxt' |> dest_TVar |> #1
        val relt = Syntax.read_term ctxt relt
        val T = type_of relt
        val _ = T |> HOLogic.is_binrelT 
          orelse error ("trp" |> mk_msg_binrel |> mk_msg_ctr)
        val (U, V) = HOLogic.dest_binrelT T
        val _ = is_TFree U andalso is_TFree V 
          orelse error ("trp" |> mk_msg_binrel_ftv |> mk_msg_ctr)
        val S = V |> dest_TFree |> #2
      in ((v, S), relt) end

    val type_specs = 
      let val ctxt' = Proof_Context.init_global (Proof_Context.theory_of ctxt)
      in map (preprocess_entry ctxt ctxt') type_specs end

    val _ = type_specs |> map #1 |> has_duplicates op= |> not
      orelse error ("trp" |> mk_msg_no_dup_stvs |> mk_msg_ctr)

    val _ = type_specs 
      |> map #2 
      |> map (fn T => Term.add_tfrees T [])
      |> flat
      |> has_duplicates op=
      |> not
      orelse error ("trp" |> mk_msg_no_dup_binrel_ftvs |> mk_msg_ctr)

    val ctxt' = type_specs
      |> map (#2 #> (fn t => Term.add_frees t []))
      |> flat 
      |> map #1
      |> Variable.fix_new_vars ctxt
      |> fold (Variable.declare_term) (map #2 type_specs)

  in (type_specs, ctxt') end;



(*** Preprocessing of the context elements ***)

fun preprocess_declaration elems lthy = 
  let
    val lthy' = Expression.read_statement elems (Element.Shows []) lthy |> #2
    val assms = Assumption.local_prems_of lthy' lthy
    val (assms', lthy'') = Thm.unvarify_local_fact lthy' assms
  in (assms', lthy'') end;

fun preprocess_definition ctxt thm_spec = 
  let
    val thm_spec' = apfst (apsnd (Local_Defs.meta_rewrite_rule ctxt)) thm_spec
    val thm = thm_spec' |> #1 |> #2
    val _ = check_def ctxt thm
    val b = case thm_spec' |> #1 |> #1 of
        SOME b => b
      | NONE => 
          let
            val c = thm 
              |> Thm.cprop_of 
              |> Thm.dest_equals_lhs 
              |> Thm.term_of
              |> head_of
              |> dest_Const
              |> fst
          in 
            Binding.qualified_name_mandatory 
              (CTR_Utilities.qualified_name_of_const_name c ^ ".transferred") 
          end
  in ((b, thm_spec' |> #1 |> #2), thm_spec' |> #2)  end;



(*** Preprocessing of the theorem specification ***)

fun preprocess_thm_spec ctxt type_specs thm_spec = 
  let

    (*auxiliary*)

    fun gen_indexed_rel_names n c = ((replicate n c), (1 upto n))
      ||> map Int.toString
      |> op~~
      |> map (op^)

    (*theorems*)
    val thm_spec = preprocess_definition ctxt thm_spec

    (*invent a new brel for each ftv not previously associated with a brel*)
    val ftvs = thm_spec
      |> (#1 #> #2 #> Thm.full_prop_of #> (fn t => Term.add_tvars t []))
      |> distinct op=
      |> subtract op= (map #1 type_specs)    
    val (cs, ctxt') =
      Variable.variant_fixes (gen_indexed_rel_names (length ftvs) "A") ctxt
    val Ss = map #2 ftvs
    val (lhsTs, ctxt'') = Variable.invent_types Ss ctxt' |>> map TFree
    val (rhsTs, ctxt''') = Variable.invent_types Ss ctxt'' |>> map TFree
    val ts = map Free (cs ~~ map HOLogic.mk_binrelT (lhsTs ~~ rhsTs))
    val ctxt'''' = fold Variable.declare_term ts ctxt'''
    val type_specs = type_specs @ (ftvs ~~ ts)

  in ((thm_spec, type_specs), ctxt'''') end;


(*** Preprocessing of the keyword synthesis ***)

fun preprocess_synthesis (synthesis : (string * thm list option) option) = 
  case synthesis of
      SOME (_, thm_opt) => 
        (
          case thm_opt of 
              SOME simp_spec => (true, SOME simp_spec)
            | NONE => (true, NONE)
        )
    | NONE => (false, NONE);



(**** Evaluation ****)

fun apply_algorithm ctxt' alg synthesis assms type_specs thm_spec = 
  let
    fun mk_msg_relator f T = T
      |> dest_Type
      |> #1
      |> f 
      |> mk_msg_ctr
    fun mk_error_no_relator T = mk_msg_relator mk_msg_no_relator T
    fun mk_error_invalid_relator T = mk_msg_relator mk_msg_invalid_relator T
    val alg_out = case alg of 
        relativization => 
          (
            CTR_Relativization.apply ctxt' synthesis assms type_specs thm_spec
            handle 
                TYPE ("pr_of_typ: no relator", [T], []) => 
                  error (mk_error_no_relator T) 
              | TYPE ("pr_of_typ: invalid relator", [T], []) => 
                  error (mk_error_invalid_relator T) 
          )
      | parametricity => CTR_Parametricity.apply 
          ctxt' synthesis assms type_specs thm_spec
  in alg_out end;

fun postprocess_alg_out b mf alg_out ctxt = case alg_out of
    ALGRelativization thm => 
      CTR_Postprocessing.postprocess_relativization b mf thm ctxt
  | ALGParametricity thm => 
      CTR_Postprocessing.postprocess_parametricity b thm ctxt
  | ALGFailure => error "ctr: a transfer rule could not be established.";

fun process_definition ctxt' alg synthesis assms type_specs thm_spec ctxt = 
  let
    val alg_out = thm_spec 
      |> #1 
      |> #2 
      |> apply_algorithm ctxt' alg synthesis assms type_specs
      |> apply_alg_out (singleton (Proof_Context.export ctxt' ctxt))
  in postprocess_alg_out (thm_spec |> #1 |> #1) (#2 thm_spec) alg_out ctxt end;



(*** Parametricity ***)

fun process_parametricity thm_spec lthy = 
  let val thm_spec' = preprocess_definition lthy thm_spec 
  in 
    process_definition lthy parametricity (false, NONE) [] [] thm_spec' lthy 
  end;



(*** Relativization ***)

fun preprocess_relativization lthy synthesis elems type_specs thm_spec = 
  let
    val synthesis' = preprocess_synthesis synthesis
    val (assms, lthy') = preprocess_declaration elems lthy
    val (type_specs', lthy'') = preprocess_type_specs lthy' type_specs
    val ((thm_spec', type_specs''), lthy''') = 
      preprocess_thm_spec lthy'' type_specs' thm_spec
  in ((synthesis', assms, type_specs'', thm_spec'), lthy''') end;

fun process_relativization synthesis elems type_specs thm_spec lthy = 
  let
    val ((synthesis, assms, type_specs, thm_spec), lthy') = 
      preprocess_relativization lthy synthesis elems type_specs thm_spec 
    val lthy'' = process_definition
      lthy' relativization synthesis assms type_specs thm_spec lthy
  in lthy'' end;




(**** Interface ****)

fun process_parametricities thm_specs lthy = fold
  (
    fn thm_spec => fn ctxt => 
      process_parametricity thm_spec ctxt |> lthy_of_pp_out
  ) 
  thm_specs
  lthy;

fun process_relativizations synthesis elems type_specs thm_specs lthy =  
  let 
    fun process_relativization' thm_spec ctxt =  
      process_relativization synthesis elems type_specs thm_spec ctxt 
      |> lthy_of_pp_out
  in fold process_relativization' thm_specs lthy end;

fun process_ctrs args lthy = 
  let
    fun preprocess_thm_specs lthy = 
      map (apfst (apsnd (singleton (Attrib.eval_thms lthy))))
    fun process_ctrs_impl (ALG_PP thm_specs) lthy = 
          process_parametricities (preprocess_thm_specs lthy thm_specs) lthy
      | process_ctrs_impl 
          (ALG_RP (((synthesis, elems), type_specs), thm_specs)) lthy =
          let
            val thm_specs' = preprocess_thm_specs lthy thm_specs
            val synthesis' = Option.map 
              (apsnd (Option.map ((single #> Attrib.eval_thms lthy)))) 
              synthesis
          in
            process_relativizations synthesis' elems type_specs thm_specs' lthy 
          end
  in process_ctrs_impl args lthy end;

val _ =
  Outer_Syntax.local_theory 
    command_keywordctr 
    "automated synthesis of transfer rules and relativization of definitions"
    (ctr_parser >> process_ctrs);

end;

Theory UD_Tests

(* Title: UD/Tests/UD_Tests.thy
   Author: Mihails Milehins
   Copyright 2021 (C) Mihails Milehins

A test suite for the framework UD.
*)

section‹Test suite for UD›
theory UD_Tests
  imports
    "../UD"
    "../../IML_UT/IML_UT"
    Complex_Main
begin



subsection‹Background›

(* 
The following two definitions were copied from 
HOL-Analysis.Elementary_Topology with minor amendments
to avoid unnecessary dependencies
*)
definition (in topological_space) islimpt:: "'a  'a set  bool"
  where "islimpt x S  (T. xT  open T  (yS. yT  yx))"
definition closure :: "('a::topological_space) set  'a set" 
  where "closure S = S  {x. islimpt x S}"

ud ‹topological_space.closed›
ud ‹islimpt›

lemma closed_with: "closed  closed.with open"
  unfolding closed_def closed.with_def .

definition closure_with :: "('a set  bool)  'a set  'a set"
  where "closure_with τ  λS. S  {x. islimpt.with τ x S}"

lemma closure_with: "closure  closure_with open"
  unfolding closure_def islimpt.with closure_with_def .



subsection‹Tests›

ML_file‹UD_TEST_UNOVERLOAD_DEFINITION.ML›

MLval ud_test_unoverload_definition_test_results =
  ud_test_unoverload_definition.execute_test_suite_unoverload_definition
    @{theory}
MLval _ = ud_test_unoverload_definition_test_results
  |> UT_Test_Suite.output_test_results true
›

end

File ‹UD_TEST_UNOVERLOAD_DEFINITION.ML›

(* Title: UD/Tests/UD_TEST_UNOVERLOAD_DEFINITION.ML
   Author: Mihails Milehins
   Copyright 2021 (C) Mihails Milehins
*)

signature UD_TEST_UNOVERLOAD_DEFINITION =
sig
type unoverload_definition_in_type
val execute_test_suite_unoverload_definition : 
  theory ->
  (unoverload_definition_in_type, UD.ud_thm_out_type * theory)
    UT_Test_Suite.test_results_suite
end;

structure ud_test_unoverload_definition : UD_TEST_UNOVERLOAD_DEFINITION =
struct




(**** Auxiliary ****)

fun mk_msg_unoverload_definition_error msg = "ud: " ^ msg



(*** Data ***)

type unoverload_definition_in_type = 
  (binding * mixfix) * (string * typ) * theory;



(*** Relation ***)

fun ud_test_eq_thm thy (act_c, exp_c) (act_ud_out, exp_ud_out) =
  let

    fun rel (act_thm, exp_thm) =
      let
        fun replace_const (Const (c, T)) =
            if c = exp_c then Const (act_c, T) else Const (c, T)
          | replace_const (t $ u) = replace_const t $ replace_const u
          | replace_const (Abs (c, T, t)) = Abs (c, T, replace_const t)
          | replace_const x = x
        val act_t = Thm.full_prop_of act_thm
        val exp_t = exp_thm |> Thm.full_prop_of |> replace_const
      in Pattern.equiv thy (act_t, exp_t) end;

    fun ud_test_eq_thm_impl ((UD.trivial act_thm), (UD.trivial exp_thm)) =
          rel (act_thm, exp_thm)
      | ud_test_eq_thm_impl ((UD.nontrivial act_thms), (UD.nontrivial exp_thms)) = 
          let
            val (act_with_def_thm, act_with_thm) = act_thms 
            val (exp_with_def_thm, exp_with_thm) = exp_thms
          in 
            rel (act_with_def_thm, exp_with_def_thm)
            andalso rel (act_with_thm, exp_with_thm) 
          end
     | ud_test_eq_thm_impl (_, _) = false

  in ud_test_eq_thm_impl (fst act_ud_out, fst exp_ud_out) end;




(**** Tests ****)



(*** Valid inputs ***)

fun test_eq_trivial thy  = 
  let
    
    (*input*)
    val act_c = "UD_Tests.closed'.with"
    val exp_c = ""
    val b = Binding.name "closed'"
    val aT = TVar (("'a", 0), sort‹topological_space›)
    val T = HOLogic.mk_setT aT --> HOLogic.boolT
    val closed_c = "Topological_Spaces.topological_space_class.closed"
    val ud_in = ((b, NoSyn), (closed_c, T), thy)
    
    (*output*)
    val ud_out = (UD.trivial (@{thm closed_with}), thy)
    
  in
    UT_Test_Suite.assert_brel 
      "nontrivial output equivalence"
      (ud_test_eq_thm thy (act_c, exp_c)) 
      ud_out
      ud_in
  end;

fun test_eq_nontrivial thy  = 
  let
    
    (*input*)
    val act_c = "UD_Tests.closure.with"
    val exp_c = "UD_Tests.closure_with"
    val b = Binding.name "closure"
    val aT = TVar (("'a", 0), sort‹topological_space›)
    val T = (HOLogic.mk_setT aT --> HOLogic.mk_setT aT)
    val closure_c = "UD_Tests.closure"
    val ud_in = ((b, NoSyn), (closure_c, T), thy)
    
    (*output*)
    val ud_out = 
      (UD.nontrivial (@{thm closure_with_def}, @{thm closure_with}), thy)
    
  in
    UT_Test_Suite.assert_brel 
      "nontrivial output equivalence"
      (ud_test_eq_thm thy (act_c, exp_c)) 
      ud_out
      ud_in
  end;



(*** Exceptions ***)

fun test_exc_extra_type_variables thy = 
  let
    val aT = TVar (("'a", 0), sort‹type›)
    val Sup_class_c = "Complete_Lattices.Sup_class"
    val args : unoverload_definition_in_type =
      (
        (Binding.empty, NoSyn), 
        (Sup_class_c, Term.itselfT aT --> typ‹prop›), 
        thy
      )
    val err_msg = mk_msg_unoverload_definition_error 
      "specification depends on extra type variables"
  in 
    UT_Test_Suite.assert_exception 
      "extra type variables" args (ERROR err_msg)
  end;

fun test_exc_ud_ex thy = 
  let
    val aT = TVar (("'a", 0), sort‹type›)
    val T = 
      (HOLogic.mk_setT aT --> HOLogic.boolT) --> 
      HOLogic.mk_setT aT --> 
      HOLogic.boolT
    val ts_c = "Topological_Spaces.topological_space.closed"
    val args : unoverload_definition_in_type =
      ((Binding.empty, NoSyn), (ts_c, T), thy)
    val err_msg = mk_msg_unoverload_definition_error 
      "unoverloaded constant already exists"
  in 
    UT_Test_Suite.assert_exception 
      "constant already exists" args (ERROR err_msg)
  end;

fun test_exc_no_cids thy = 
  let
    val aT = TVar (("'a", 0), sort‹type›)
    val T = 
      (HOLogic.mk_setT aT --> HOLogic.boolT) --> 
      HOLogic.mk_setT aT --> 
      HOLogic.boolT
    val implies_c = "HOL.implies"
    val args : unoverload_definition_in_type =
      ((Binding.empty, NoSyn), (implies_c, T), thy)
    val err_msg = mk_msg_unoverload_definition_error
      "no suitable constant-instance definitions"
  in 
    UT_Test_Suite.assert_exception 
      "no suitable CIs" args (ERROR err_msg)
  end;




(**** Test suite ****)

local

fun unoverload_definition_string_of_input 
  (unoverload_definition_in : unoverload_definition_in_type) = 
  let
    val ((b, _), (c, T), thy) = unoverload_definition_in
    val b_c = "binding: " ^ Binding.name_of b
    val const_c = 
      "constant: " ^ 
      c ^ 
      " :: " ^ 
      Syntax.string_of_typ (Proof_Context.init_global thy) T
    val thy_c = "thy: unknown theory" 
    val out_c = [b_c, const_c, thy_c] |> String.concatWith "\n"
  in out_c end;

fun unoverload_definition_string_of_output (ud_thm_out, thy) = 
  let
    val ctxt = Proof_Context.init_global thy
    val ud_thm_c = 
      let
        val with_thm_c = "with_thm: "
        val with_def_thm_c = "with_def_thm: "
      in
        case ud_thm_out of 
          UD.trivial with_thm => 
            with_thm_c ^ Thm.string_of_thm ctxt with_thm
        | UD.nontrivial (with_def_thm, with_thm) =>
            with_def_thm_c ^ 
            Thm.string_of_thm ctxt with_def_thm ^ 
            "\n" ^
            with_thm_c ^ 
            Thm.string_of_thm ctxt with_thm
      end
    val thy_c = "thy: unknown theory"
    val out_c = [ud_thm_c, thy_c] |> String.concatWith "\n"
  in out_c end;

fun unoverload_definition 
  (((b, mixfix), (c, T), thy) : unoverload_definition_in_type) = 
  UD.unoverload_definition (b, mixfix) (c, T) thy;

in

fun mk_test_suite_unoverload_definition thy =
  let
    val test_suite_unoverload_definition = UT_Test_Suite.init
      "unoverload_definition"
      unoverload_definition
      unoverload_definition_string_of_input
      unoverload_definition_string_of_output
  in
    test_suite_unoverload_definition
    |> test_eq_trivial thy
    |> test_eq_nontrivial thy
    |> test_exc_extra_type_variables thy
    |> test_exc_ud_ex thy
    |> test_exc_no_cids thy
  end;

end;

fun execute_test_suite_unoverload_definition thy = 
  UT_Test_Suite.execute (mk_test_suite_unoverload_definition thy)

end;

Theory CTR_Tests

(* Title: CTR/Tests/CTR_Tests.thy
   Author: Mihails Milehins
   Copyright 2021 (C) Mihails Milehins

A test suite for the sub-framework CTR.
*)

section‹A test suite for CTR›
theory CTR_Tests
  imports
    "../CTR"
    "../../IML_UT/IML_UT"
    Complex_Main
  keywords "ctr_test" :: thy_defn
begin



subsection‹Background›

MLtype ctr_test_data =
  {
    ctr_type : string,
    synthesis : (string * thm list option) option,
    elems: (string, string, Facts.ref) Element.ctxt list,
    type_specs : (string * string) list,
    thm_specs : ((binding option * thm) * mixfix) list
  };

structure CTRTestData = Generic_Data
  (
    type T = ctr_test_data Symtab.table
    val empty = Symtab.empty
    val extend = I
    val merge = Symtab.merge (K true)
  );

val get_ctr_test_data_generic = CTRTestData.get;
val get_ctr_test_data_proof = Context.Proof #> get_ctr_test_data_generic;
val get_ctr_test_data_global = Context.Theory #> get_ctr_test_data_generic;
fun test_data_of_generic context = context
  |> get_ctr_test_data_generic
  |> Symtab.lookup;
val ctr_test_data_of_proof = Context.Proof #> test_data_of_generic;

(*oversimplified: to be used with care*)
fun update_ctr_test_data k ctr_test_data =
  Local_Theory.declaration
    {pervasive=true, syntax=false}
    (fn _ => (k, ctr_test_data) |> Symtab.update |> CTRTestData.map);

fun process_ctr_test_data (k, args) (lthy : local_theory) =
  let
    fun preprocess_thm_specs lthy =
      map (apfst (apsnd (singleton (Attrib.eval_thms lthy))))
    fun process_ctrs_impl (CTR.ALG_PP _) (lthy : local_theory) = lthy
      | process_ctrs_impl
          (CTR.ALG_RP (((synthesis, elems), type_specs), thm_specs))
          (lthy : local_theory) =
          let
            val thm_specs' = preprocess_thm_specs lthy thm_specs
            val synthesis' = Option.map
              (apsnd (Option.map ((single #> Attrib.eval_thms lthy))))
              synthesis
            val data : ctr_test_data =
              {
                ctr_type = "relativization",
                synthesis = synthesis',
                elems = elems,
                type_specs = type_specs,
                thm_specs = thm_specs'
              }
          in update_ctr_test_data k data lthy end
  in process_ctrs_impl args lthy end;

val ctr_test_parser = Parse.string -- CTR.ctr_parser;

val _ =
  Outer_Syntax.local_theory
    command_keywordctr_test
    "test setup for the command ctr"
    (ctr_test_parser >> process_ctr_test_data);

ud ‹order.mono›
ud mono' ‹mono› 

definition mono_ow :: 
  "'a set  ('b  'b  bool)  ('a  'a  bool)  ('a  'b)  bool"
  where "mono_ow UB leb lea f  xUB. yUB. lea x y  leb (f x) (f y)"

typedef 'a K = {xs::'a list. length xs = 2}
  by (simp add: Ex_list_of_length)

definition KK :: "'a K  'a K  bool" 
  where "KK k1 k2  k1 = k2"

typedef 'a L = {xs::'a list. length xs = 2}
  by (simp add: Ex_list_of_length)

definition LL :: "'a L  'a L  bool" 
  where "LL k1 k2  k1 = k2"

definition rel_L :: 
  "('a::group_add  'b::group_add  bool)  'a::group_add L  'b::group_add L  bool" 
  where "rel_L A b c = True"

ctr_relator rel_L

definition not_binders_binrelT :: 
  "('a  'b  bool)  ('c  bool)  'a  'b  bool"
  where "not_binders_binrelT R1 R2 a b = True"

definition no_dup_binrelT :: 
  "('a  'b  bool)  ('c  'a  bool)  'a  'b  bool"
  where "no_dup_binrelT R1 R2 a b = True"

definition not_binders_binrelT_ftv_stv :: 
  "('a  'b  bool)  (nat  'c  bool)  'a  'b  bool"
  where "not_binders_binrelT_ftv_stv R1 R2 a b = True"

definition not_type_constructor_lhs :: 
  "('a  'b  bool)  ('c  'd  bool)  'a  'a K  bool"
  where "not_type_constructor_lhs R1 R2 a b = True"

definition not_type_constructor_rhs :: 
  "('a  'b  bool)  ('c  'd  bool)  'a K  'e  bool"
  where "not_type_constructor_rhs R1 R2 a b = True"

definition not_identical_type_constructors ::
  "('a  'b  bool)  ('c  'd  bool)  'a K  'e L  bool"
  where "not_identical_type_constructors R1 R2 a b = True"

definition not_identical_type_constructors_lhs ::
  "('a  'b  bool)  ('c  'd  bool)  'a K  'b K  bool"
  where "not_identical_type_constructors_lhs R1 R2 a b = True"

definition not_identical_type_constructors_rhs ::
  "('a  'b  bool)  'a K  'c K  bool"
  where "not_identical_type_constructors_rhs R1 a b = True"



subsection‹Test data›

lemma mono_ow_transfer':
  includes lifting_syntax
  assumes [transfer_domain_rule, transfer_rule]: "Domainp B = (λx. x  UB)" 
    and [transfer_rule]: "right_total B" 
  shows
    "((A ===> A ===> (=)) ===> (B ===> B ===> (=)) ===> (B ===> A) ===> (=))
      (mono_ow UB) mono.with"
  unfolding mono_ow_def mono.with_def
  by (transfer_prover_start, transfer_step+) simp

ctr_test "mono_with" relativization
  synthesis ctr_simps_Collect_mem_eq
  assumes [transfer_domain_rule, transfer_rule]:
    "Domainp (B::'c'dbool) = (λx. x  UB)"
    and [transfer_rule]: "right_total B" 
  trp (?'b A::'a'bbool›) and (?'a B)
  in mono_ow': mono.with_def 

ctr_test "exI" relativization
  in mono_ow'': exI

ctr_test "binrel" relativization
  synthesis ctr_simps_Collect_mem_eq
  assumes [transfer_domain_rule, transfer_rule]:
    "Domainp (B::'c'dbool) = (λx. x  UB)"
    and [transfer_rule]: "right_total B" 
  trp (?'b A) and (?'a B)
  in mono_ow': mono.with_def 

ctr_test "binrel_ftv" relativization
  synthesis ctr_simps_Collect_mem_eq
  assumes [transfer_domain_rule, transfer_rule]:
    "Domainp (B::'c'dbool) = (λx. x  UB)"
    and [transfer_rule]: "right_total B" 
  trp (?'b A::nat'bbool›) and (?'a B)
  in mono_ow': mono.with_def 

ctr_test "dup_stvs" relativization
  synthesis ctr_simps_Collect_mem_eq
  assumes [transfer_domain_rule, transfer_rule]:
    "Domainp (B::'c'dbool) = (λx. x  UB)"
    and [transfer_rule]: "right_total B" 
  trp (?'b A::'a'bbool›) and (?'b B)
  in mono_ow': mono.with_def 

ctr_test "dup_binrel_ftvs" relativization
  synthesis ctr_simps_Collect_mem_eq
  assumes [transfer_domain_rule, transfer_rule]:
    "Domainp (B::'c'dbool) = (λx. x  UB)"
    and [transfer_rule]: "right_total B" 
  trp (?'b A::'a'dbool›) and (?'a B)
  in mono_ow': mono.with_def 

ctr_test "no_relator" relativization
  synthesis ctr_simps_Collect_mem_eq
  assumes [transfer_domain_rule, transfer_rule]:
    "Domainp (B::'c'dbool) = (λx. x  UB)"
    and [transfer_rule]: "right_total B" 
  trp (?'b A::'a'bbool›) and (?'a B) 
  in KK_def

ctr_test "invalid_relator" relativization
  synthesis ctr_simps_Collect_mem_eq
  assumes [transfer_domain_rule, transfer_rule]:
    "Domainp (B::'c'dbool) = (λx. x  UB)"
    and [transfer_rule]: "right_total B" 
  trp (?'b A::'a'bbool›) and (?'a B)
  in LL_def



subsection‹Tests›


subsubsectionprocess_relativization›

ML_file‹CTR_TEST_PROCESS_RELATIVIZATION.ML›

context
  includes lifting_syntax
begin

MLval ctr_test_process_relativization_test_results =
  ctr_test_process_relativization.execute_test_suite_process_relativization
    @{context}
MLval _ = ctr_test_process_relativization_test_results
  |> UT_Test_Suite.output_test_results true
›

end


subsubsectionprocess_ctr_relator›

ML_file‹CTR_TEST_PROCESS_CTR_RELATOR.ML›
MLval ctr_test_process_ctr_relator_test_results =
  ctr_test_process_ctr_relator.execute_test_suite_process_ctr_relator
    @{context}
MLval _ = ctr_test_process_ctr_relator_test_results
  |> UT_Test_Suite.output_test_results true
›

end

File ‹CTR_TEST_PROCESS_RELATIVIZATION.ML›

(* Title: CTR/Tests/CTR_TEST_PROCESS_RELATIVIZATION.ML
   Author: Mihails Milehins
   Copyright 2021 (C) Mihails Milehins
*)

signature CTR_TEST_PROCESS_RELATIVIZATION =
sig
type process_relativization_in_type
val execute_test_suite_process_relativization : 
  Proof.context -> (process_relativization_in_type, ctr_pp_out)
    UT_Test_Suite.test_results_suite
end;

structure ctr_test_process_relativization : CTR_TEST_PROCESS_RELATIVIZATION =
struct




(**** Auxiliary ****)

fun mk_msg_ctr_error msg = "ctr: " ^ msg



(*** Data ***)

type process_relativization_in_type = 
  (
    (string * thm list option) option *
    Element.context list *
    (string * string) list *
    ((binding option * thm) * mixfix) 
  ) * Proof.context;



(*** Relation ***)

local

fun map_const_name (oldc, newc) (Const (c, T)) = 
    if oldc = c then Const (newc, T) else Const (c, T)
  | map_const_name eqc (Abs (c, T, t)) = Abs (c, T, map_const_name eqc t)
  | map_const_name eqc (t $ u) = map_const_name eqc t $ map_const_name eqc u
  | map_const_name _ t = t

in 
 
fun process_relativization_test_eq 
    (PPRelativization args1, PPRelativization args2) = 
      let
        val act_lthy = #2 args1
        val exp_lthy = #2 args2
        val (act_ow_def_t, act_tr_t) = args1
          |> #1
          |>> Local_Defs.meta_rewrite_rule act_lthy 
          |> apply2 Thm.full_prop_of
        val (exp_ow_def_t, exp_tr_t) = args2
          |> #1
          |>> Local_Defs.meta_rewrite_rule act_lthy 
          |> apply2 Thm.full_prop_of
        val act_ow_def_lhst = act_ow_def_t |> Logic.dest_equals |> #1
        val exp_ow_def_lhst = exp_ow_def_t |> Logic.dest_equals |> #1
        val thy = Proof_Context.theory_of exp_lthy
        val mapc = 
          (
            act_ow_def_lhst |> head_of |> dest_Const |> #1, 
            exp_ow_def_lhst |> head_of |> dest_Const |> #1
          )
        val act_ow_def_t' = map_const_name mapc act_ow_def_t
        val act_tr_t' = map_const_name mapc act_tr_t
        val act_ow_def_eq = Pattern.equiv thy (act_ow_def_t', exp_ow_def_t)
        val tr_eq = Pattern.equiv thy (act_tr_t', exp_tr_t)
        val _ = act_ow_def_eq |> Bool.toString |> writeln
      in act_ow_def_eq andalso tr_eq end
  | process_relativization_test_eq 
      (PPParametricity args1, PPParametricity args2) = 
      (*careful: not needed; hence, a usable implementation is not provided*)
      Thm.eq_thm (fst args1, fst args2) 
  | process_relativization_test_eq (_, _) = false;

end;




(**** Tests ****)



(*** Valid inputs ***)

fun test_eq_trivial ctxt = 
  let
    (*input*)
    val {synthesis, elems, type_specs, thm_specs, ...} = 
      ctr_test_data_of_proof ctxt "mono_with" |> the
    val process_relativization_in = 
      ((synthesis, elems, type_specs, the_single thm_specs), ctxt)
    (*output*)
    val process_relativization_out = 
      PPRelativization ((@{thm mono_ow_def}, @{thm mono_ow_transfer'}), ctxt)
  in
    UT_Test_Suite.assert_brel 
      "output equivalence"
      process_relativization_test_eq 
      process_relativization_out
      process_relativization_in
  end;



(*** Exceptions ***)

fun test_exc_def ctxt = 
  let
    val {synthesis, elems, type_specs, thm_specs, ...} = 
      ctr_test_data_of_proof ctxt "exI" |> the
    val args = 
      ((synthesis, elems, type_specs, the_single thm_specs), ctxt)
    val err_msg = mk_msg_ctr_error 
      (
        Syntax.string_of_term ctxt (Thm.full_prop_of @{thm exI}) ^ 
        " is not a definition"
      )
  in UT_Test_Suite.assert_exception "not a definition" args (ERROR err_msg) end;

fun test_exc_binrel ctxt = 
  let
    val {synthesis, elems, type_specs, thm_specs, ...} = 
      ctr_test_data_of_proof ctxt "binrel" |> the
    val args = ((synthesis, elems, type_specs, the_single thm_specs), ctxt)
    val err_msg = mk_msg_ctr_error 
      "trp: trp must consist of (stv, binary relation) pairs"
  in UT_Test_Suite.assert_exception "binary relation" args (ERROR err_msg) end;

fun test_exc_binrel_ftv ctxt = 
  let
    val {synthesis, elems, type_specs, thm_specs, ...} = 
      ctr_test_data_of_proof ctxt "binrel_ftv" |> the
    val args = ((synthesis, elems, type_specs, the_single thm_specs), ctxt)
    val err_msg = mk_msg_ctr_error 
      "trp: the user-specified binary relations must " ^
      "be defined over type variables"
  in 
    UT_Test_Suite.assert_exception "binary relation ftv" args (ERROR err_msg) 
  end;

fun test_exc_dup_stvs ctxt = 
  let
    val {synthesis, elems, type_specs, thm_specs, ...} = 
      ctr_test_data_of_proof ctxt "dup_stvs" |> the
    val args = ((synthesis, elems, type_specs, the_single thm_specs), ctxt)
    val err_msg = mk_msg_ctr_error "trp: duplicate stvs"
  in UT_Test_Suite.assert_exception "duplicate stv" args (ERROR err_msg) end;

fun test_exc_dup_binrel_ftvs ctxt = 
  let
    val {synthesis, elems, type_specs, thm_specs, ...} = 
      ctr_test_data_of_proof ctxt "dup_binrel_ftvs" |> the
    val args = ((synthesis, elems, type_specs, the_single thm_specs), ctxt)
    val err_msg = mk_msg_ctr_error
      "trp: duplicate ftvs in the specification of the binary relations"
  in
    UT_Test_Suite.assert_exception "duplicate binrel ftvs" args (ERROR err_msg) 
  end;

fun test_exc_no_relator ctxt = 
  let
    val {synthesis, elems, type_specs, thm_specs, ...} = 
      ctr_test_data_of_proof ctxt "no_relator" |> the
    val args = ((synthesis, elems, type_specs, the_single thm_specs), ctxt)
    val err_msg = mk_msg_ctr_error
      "no relator found for the type constructor CTR_Tests.K"
  in
    UT_Test_Suite.assert_exception "no relator" args (ERROR err_msg) 
  end;

fun test_exc_invalid_relator ctxt = 
  let
    val {synthesis, elems, type_specs, thm_specs, ...} = 
      ctr_test_data_of_proof ctxt "invalid_relator" |> the
    val args = ((synthesis, elems, type_specs, the_single thm_specs), ctxt)
    val err_msg = mk_msg_ctr_error
      (
        "the relator found for the type constructor CTR_Tests.L " ^
        "is not suitable (is there is a mismatch of type variables?)" 
      )
  in
    UT_Test_Suite.assert_exception "no relator" args (ERROR err_msg) 
  end;




(**** Test suite ****)

local

fun string_of_elem_ctxt_fixes args = "fixes: " ^
  (
    args
    |> map (fn (x, y, _) => (x, y)) 
    |> ML_Syntax.print_list 
      (ML_Syntax.print_pair Binding.print (ML_Syntax.print_option I))
  );
fun string_of_elem_ctxt_assumes ctxt args =
  let
    val string_of_fst = ML_Syntax.print_pair 
      Binding.print 
      (ML_Syntax.print_list (Token.pretty_src ctxt #> Pretty.string_of))
    val string_of_snd = 
      ML_Syntax.print_list (ML_Syntax.print_pair I (ML_Syntax.print_list I))
  in 
    "assumes: " ^ 
    ML_Syntax.print_list (ML_Syntax.print_pair string_of_fst string_of_snd) args
  end;
fun string_of_elem_ctxt_constrains _ = "constrains: unknown constrains";
fun string_of_elem_ctxt_defines _ = "defines: unknown defines";
fun string_of_elem_ctxt_notes _ = "notes: unknown notes";
fun string_of_elem_ctxt_lazy_notes _ = "lazy notes: unknown lazy notes";

fun string_of_elem_ctxt _ (Element.Fixes args : Element.context) = 
      string_of_elem_ctxt_fixes args
  | string_of_elem_ctxt _ (Element.Constrains args) = 
      string_of_elem_ctxt_constrains args
  | string_of_elem_ctxt ctxt (Element.Assumes args) = 
      string_of_elem_ctxt_assumes ctxt args
  | string_of_elem_ctxt _ (Element.Defines args) = 
      string_of_elem_ctxt_defines args
  | string_of_elem_ctxt _ (Element.Notes args) = 
      string_of_elem_ctxt_notes args
  | string_of_elem_ctxt _ (Element.Lazy_Notes args) = 
      string_of_elem_ctxt_lazy_notes args

fun process_relativization_string_of_input ctxt 
  (process_relativization_in : process_relativization_in_type) = 
  let
    val ((synthesis_opt, elems, type_specs, thm_spec), lthy) =
      process_relativization_in
    val synthesis_opt_c =
      let val synthesis_c = "synthesis: "
      in
        case synthesis_opt of 
          SOME synthesis => 
            (
              case #2 synthesis of 
                  SOME _ => synthesis_c ^ "user-defined simpset"
                | NONE => synthesis_c ^ "default simpset"
            )
        | NONE => synthesis_c ^ "none"
      end
    val elems_c = "elements:" ^ 
      (
        if null elems 
        then " none" 
        else "\n" ^
          (
            elems
            |> map (string_of_elem_ctxt ctxt)
            |> map (fn c => "\t" ^ c)
            |> String.concatWith "\n"
          )
      )
    val type_specs_c = "type_specs: " ^
      ML_Syntax.print_list (ML_Syntax.print_pair I I) type_specs
    val thm_spec_c =
      "definition: " ^ (thm_spec |> #1 |> #2 |> Thm.string_of_thm lthy) 
    val lthy_c = "lthy: unknown local theory" 
    val out_c = [synthesis_opt_c, elems_c, type_specs_c, thm_spec_c, lthy_c] 
      |> String.concatWith "\n"
  in out_c end;

fun process_relativization 
  (
    ((synthesis, assms, type_specs, thm_spec), lthy) : 
      process_relativization_in_type
  ) = CTR.process_relativization synthesis assms type_specs thm_spec lthy;

in

fun mk_test_suite_process_relativization ctxt =
  let
    val test_suite_process_relativization = UT_Test_Suite.init
      "process_relativization"
      process_relativization
      (process_relativization_string_of_input ctxt)
      string_of_pp_out
  in
    test_suite_process_relativization
    |> test_eq_trivial ctxt
    |> test_exc_def ctxt
    |> test_exc_binrel ctxt
    |> test_exc_binrel_ftv ctxt
    |> test_exc_dup_stvs ctxt
    |> test_exc_dup_binrel_ftvs ctxt
    |> test_exc_no_relator ctxt
    |> test_exc_invalid_relator ctxt
  end;

end;

fun execute_test_suite_process_relativization ctxt = 
  UT_Test_Suite.execute (mk_test_suite_process_relativization ctxt)

end;

File ‹CTR_TEST_PROCESS_CTR_RELATOR.ML›

(* Title: CTR/Tests/CTR_TEST_PROCESS_CTR_RELATOR.ML
   Author: Mihails Milehins
   Copyright 2021 (C) Mihails Milehins
*)

signature CTR_TEST_PROCESS_CTR_RELATOR =
sig
type process_ctr_relator_in_type
val execute_test_suite_process_ctr_relator : 
  Proof.context -> (process_ctr_relator_in_type, local_theory)
    UT_Test_Suite.test_results_suite
end;

structure ctr_test_process_ctr_relator : CTR_TEST_PROCESS_CTR_RELATOR =
struct



(**** Auxiliary ****)

fun mk_msg_ctr_relator msg = "ctr_relator: " ^ msg;



(*** Data ***)

type process_ctr_relator_in_type = string * Proof.context;




(**** Tests ****)



(*** Exceptions ***)

fun test_exc_not_const ctxt = 
  let
    val c = "a + b"
    val args = (c, ctxt)
    val err_msg = mk_msg_ctr_relator "the input must be a constant term"
  in UT_Test_Suite.assert_exception "not a constant" args (ERROR err_msg) end;

fun test_exc_not_body_bool ctxt = 
  let
    val c = "Cons"
    val args = (c, ctxt)
    val err_msg = mk_msg_ctr_relator 
      "the body of the type of the input must be bool"
  in UT_Test_Suite.assert_exception "not bool body" args (ERROR err_msg) end;

fun test_exc_not_binders_2 ctxt = 
  let
    val c = "Ex"
    val args = (c, ctxt)
    val err_msg = mk_msg_ctr_relator 
      "the type of the input must have more than two binders"
  in UT_Test_Suite.assert_exception "not two binders" args (ERROR err_msg) end;

fun test_exc_not_binders_binrelT ctxt =
  let
    val c = "not_binders_binrelT"
    val args = (c, ctxt)
    val err_msg = mk_msg_ctr_relator
      "all of the binders associated with the type of the input" ^
      "except the last two must be the binary relation types"
  in 
    UT_Test_Suite.assert_exception 
      "not binary relation types" args (ERROR err_msg) 
  end;

fun test_exc_no_dup_binrelT ctxt =
  let
    val c = "no_dup_binrelT"
    val args = (c, ctxt)
    val err_msg = mk_msg_ctr_relator
      "the types of the binders of the binary relations associated " ^
      "with the type of the input must have no duplicates"
  in 
    UT_Test_Suite.assert_exception 
      "no duplicates in the binary relation types" args (ERROR err_msg) 
  end;

fun test_exc_not_binders_binrelT_ftv_stv ctxt =
  let
    val c = "not_binders_binrelT_ftv_stv"
    val args = (c, ctxt)
    val err_msg = mk_msg_ctr_relator
      "the types of the binders of the binary relation types associated " ^
      "with the input type must be either free type variables or " ^
      "schematic type variables"
  in 
    UT_Test_Suite.assert_exception 
      "not binrel type ftv or stv" args (ERROR err_msg) 
  end;

fun test_exc_not_type_constructor_lhs ctxt =
  let
    val c = "not_type_constructor_lhs"
    val args = (c, ctxt)
    val err_msg = mk_msg_ctr_relator
      "the last two binders of the input type must be " ^
      "the results of an application of a type constructor"
  in 
    UT_Test_Suite.assert_exception 
      "not type constructor lhs" args (ERROR err_msg)
  end;

fun test_exc_not_type_constructor_rhs ctxt =
  let
    val c = "not_type_constructor_rhs"
    val args = (c, ctxt)
    val err_msg = mk_msg_ctr_relator
      "the last two binders of the input type must be " ^
      "the results of an application of a type constructor"
  in 
    UT_Test_Suite.assert_exception 
      "not type constructor rhs" args (ERROR err_msg)
  end;

fun test_exc_not_identical_type_constructors_lhs ctxt =
  let
    val c = "not_identical_type_constructors_lhs"
    val args = (c, ctxt)
    val err_msg = mk_msg_ctr_relator
      "the sequences of the input types to the type constructors that are " ^
      "associated with the last two binders of the input type must be " ^
      "identical to the sequences of the types formed by concatenating the " ^
      "type variables associated with the left hand side and the right " ^
      "hand side of the binary relation types, respectively"
  in 
    UT_Test_Suite.assert_exception 
      "not identical type constructors lhs" args (ERROR err_msg)
  end;

fun test_exc_not_identical_type_constructors_rhs ctxt =
  let
    val c = "not_identical_type_constructors_rhs"
    val args = (c, ctxt)
    val err_msg = mk_msg_ctr_relator
      "the sequences of the input types to the type constructors that are " ^
      "associated with the last two binders of the input type must be " ^
      "identical to the sequences of the types formed by concatenating the " ^
      "type variables associated with the left hand side and the right " ^
      "hand side of the binary relation types, respectively"
  in 
    UT_Test_Suite.assert_exception 
      "not identical type constructors rhs" args (ERROR err_msg)
  end;




(**** Test suite ****)

local

fun process_ctr_relator_string_of_input 
   (process_ctr_relator_in : process_ctr_relator_in_type) = 
  let
    val (c, _) = process_ctr_relator_in
    val name_c = "constant name: " ^ c
    val ctxt_c = "lthy: unknown context" 
    val out_c = [name_c, ctxt_c] |> String.concatWith "\n"
  in out_c end;

fun process_ctr_relator ((c, ctxt) : process_ctr_relator_in_type) = 
  CTR_Relators.process_ctr_relator c ctxt;

in

fun mk_test_suite_process_ctr_relator ctxt =
  let
    val test_suite_process_ctr_relator = UT_Test_Suite.init
      "process_ctr_relator"
      process_ctr_relator
      process_ctr_relator_string_of_input
      (fn _ => "lthy: unknown local theory")
  in
    test_suite_process_ctr_relator
    |> test_exc_not_const ctxt
    |> test_exc_not_body_bool ctxt
    |> test_exc_not_binders_2 ctxt
    |> test_exc_not_binders_binrelT ctxt
    |> test_exc_no_dup_binrelT ctxt
    |> test_exc_not_binders_binrelT_ftv_stv ctxt
    |> test_exc_not_type_constructor_lhs ctxt
    |> test_exc_not_type_constructor_rhs ctxt
    |> test_exc_not_identical_type_constructors_lhs ctxt
    |> test_exc_not_identical_type_constructors_rhs ctxt
  end;

end;

fun execute_test_suite_process_ctr_relator ctxt = 
  UT_Test_Suite.execute (mk_test_suite_process_ctr_relator ctxt)

end;

Theory Reference_Prerequisites

(* Title: Reference_Prerequisites.thy
   Author: Mihails Milehins
   Copyright 2021 (C) Mihails Milehins
*)

section‹Reference prerequisites›
theory Reference_Prerequisites
  imports "HOL-Library.LaTeXsugar" 
begin

ML_file ‹~~/src/Doc/antiquote_setup.ML›


(* Copied from Transfer.thy in the main library. *)
notation rel_fun (infixr "===>" 55)
notation map_fun (infixr "--->" 55)

end

File ‹~~/src/Doc/antiquote_setup.ML›

(*  Title:      Doc/antiquote_setup.ML
    Author:     Makarius

Auxiliary antiquotations for the Isabelle manuals.
*)

structure Antiquote_Setup: sig end =
struct

(* misc utils *)

fun translate f = Symbol.explode #> map f #> implode;

val clean_string = translate
  (fn "_" => "\\_"
    | "#" => "\\#"
    | "$" => "\\$"
    | "%" => "\\%"
    | "<" => "$<$"
    | ">" => "$>$"
    | "{" => "\\{"
    | "|" => "$\\mid$"
    | "}" => "\\}"
    | "‐" => "-"
    | c => c);

fun clean_name "…" = "dots"
  | clean_name ".." = "ddot"
  | clean_name "." = "dot"
  | clean_name "_" = "underscore"
  | clean_name "{" = "braceleft"
  | clean_name "}" = "braceright"
  | clean_name s = s |> translate (fn "_" => "-" | "‐" => "-" | c => c);


(* ML text *)

local

fun ml_val (toks1, []) = ML_Lex.read "fn _ => (" @ toks1 @ ML_Lex.read ");"
  | ml_val (toks1, toks2) =
      ML_Lex.read "fn _ => (" @ toks1 @ ML_Lex.read " : " @ toks2 @ ML_Lex.read ");";

fun ml_op (toks1, []) = ML_Lex.read "fn _ => (op " @ toks1 @ ML_Lex.read ");"
  | ml_op (toks1, toks2) =
      ML_Lex.read "fn _ => (op " @ toks1 @ ML_Lex.read " : " @ toks2 @ ML_Lex.read ");";

fun ml_type (toks1, []) = ML_Lex.read "val _ = NONE : (" @ toks1 @ ML_Lex.read ") option;"
  | ml_type (toks1, toks2) =
      ML_Lex.read "val _ = [NONE : (" @ toks1 @ ML_Lex.read ") option, NONE : (" @
        toks2 @ ML_Lex.read ") option];";

fun ml_exception (toks1, []) = ML_Lex.read "fn _ => (" @ toks1 @ ML_Lex.read " : exn);"
  | ml_exception (toks1, toks2) =
      ML_Lex.read "fn _ => (" @ toks1 @ ML_Lex.read " : " @ toks2 @ ML_Lex.read " -> exn);";

fun ml_structure (toks, _) =
  ML_Lex.read "functor XXX() = struct structure XX = " @ toks @ ML_Lex.read " end;";

fun ml_functor (Antiquote.Text tok :: _, _) =
      ML_Lex.read "ML_Env.check_functor " @
      ML_Lex.read (ML_Syntax.print_string (ML_Lex.content_of tok))
  | ml_functor _ = raise Fail "Bad ML functor specification";

val is_name =
  ML_Lex.kind_of #> (fn kind => kind = ML_Lex.Ident orelse kind = ML_Lex.Long_Ident);

fun ml_name txt =
  (case filter is_name (ML_Lex.tokenize txt) of
    toks as [_] => ML_Lex.flatten toks
  | _ => error ("Single ML name expected in input: " ^ quote txt));

fun prep_ml source =
  (#1 (Input.source_content source), ML_Lex.read_source source);

fun index_ml name kind ml = Thy_Output.antiquotation_raw name
  (Scan.lift (Args.text_input -- Scan.option (Args.colon |-- Args.text_input)))
  (fn ctxt => fn (source1, opt_source2) =>
    let
      val (txt1, toks1) = prep_ml source1;
      val (txt2, toks2) =
        (case opt_source2 of
          SOME source => prep_ml source
        | NONE => ("", []));

      val txt =
        if txt2 = "" then txt1
        else if kind = "type" then txt1 ^ " = " ^ txt2
        else if kind = "exception" then txt1 ^ " of " ^ txt2
        else if Symbol_Pos.is_identifier (Long_Name.base_name (ml_name txt1))
        then txt1 ^ ": " ^ txt2
        else txt1 ^ " : " ^ txt2;
      val txt' = if kind = "" then txt else kind ^ " " ^ txt;

      val pos = Input.pos_of source1;
      val _ =
        ML_Context.eval_in (SOME ctxt) ML_Compiler.flags pos (ml (toks1, toks2))
          handle ERROR msg => error (msg ^ Position.here pos);
      val kind' = if kind = "" then "ML" else "ML " ^ kind;
    in
      Latex.block
       [Latex.string ("\\indexdef{}{" ^ kind' ^ "}{" ^ clean_string (ml_name txt1) ^ "}"),
        Thy_Output.verbatim ctxt txt']
    end);

in

val _ =
  Theory.setup
   (index_ml binding‹index_ML› "" ml_val #>
    index_ml binding‹index_ML_op› "infix" ml_op #>
    index_ml binding‹index_ML_type› "type" ml_type #>
    index_ml binding‹index_ML_exception› "exception" ml_exception #>
    index_ml binding‹index_ML_structure› "structure" ml_structure #>
    index_ml binding‹index_ML_functor› "functor" ml_functor);

end;


(* named theorems *)

val _ =
  Theory.setup (Thy_Output.antiquotation_raw binding‹named_thms›
    (Scan.repeat (Attrib.thm -- Scan.lift (Args.parens Args.name)))
    (fn ctxt =>
      map (fn (thm, name) =>
        Output.output
          (Document_Antiquotation.format ctxt
            (Document_Antiquotation.delimit ctxt (Thy_Output.pretty_thm ctxt thm))) ^
        enclose "\\rulename{" "}" (Output.output name))
      #> space_implode "\\par\\smallskip%\n"
      #> Latex.string #> single
      #> Thy_Output.isabelle ctxt));


(* Isabelle/Isar entities (with index) *)

local

fun no_check (_: Proof.context) (name, _: Position.T) = name;

fun check_keyword ctxt (name, pos) =
  if Keyword.is_keyword (Thy_Header.get_keywords' ctxt) name then name
  else error ("Bad outer syntax keyword " ^ quote name ^ Position.here pos);

fun check_system_option ctxt arg =
  (Completion.check_option (Options.default ()) ctxt arg; true)
    handle ERROR _ => false;

val arg = enclose "{" "}" o clean_string;

fun entity check markup binding index =
  Thy_Output.antiquotation_raw
    (binding |> Binding.map_name (fn name => name ^
      (case index of NONE => "" | SOME true => "_def" | SOME false => "_ref")))
    (Scan.lift (Scan.optional (Args.parens Args.name) "" -- Args.name_position))
    (fn ctxt => fn (logic, (name, pos)) =>
      let
        val kind = translate (fn "_" => " " | c => c) (Binding.name_of binding);
        val hyper_name =
          "{" ^ Long_Name.append kind (Long_Name.append logic (clean_name name)) ^ "}";
        val hyper =
          enclose ("\\hyperlink" ^ hyper_name ^ "{") "}" #>
          index = SOME true ? enclose ("\\hypertarget" ^ hyper_name ^ "{") "}";
        val idx =
          (case index of
            NONE => ""
          | SOME is_def =>
              "\\index" ^ (if is_def then "def" else "ref") ^ arg logic ^ arg kind ^ arg name);
        val _ =
          if Context_Position.is_reported ctxt pos then ignore (check ctxt (name, pos)) else ();
        val latex =
          idx ^
          (Output.output name
            |> (if markup = "" then I else enclose ("\\" ^ markup ^ "{") "}")
            |> hyper o enclose "\\mbox{\\isa{" "}}");
      in Latex.string latex end);

fun entity_antiqs check markup kind =
  entity check markup kind NONE #>
  entity check markup kind (SOME true) #>
  entity check markup kind (SOME false);

in

val _ =
  Theory.setup
   (entity_antiqs no_check "" binding‹syntax› #>
    entity_antiqs Outer_Syntax.check_command "isacommand" binding‹command› #>
    entity_antiqs check_keyword "isakeyword" binding‹keyword› #>
    entity_antiqs check_keyword "isakeyword" binding‹element› #>
    entity_antiqs Method.check_name "" binding‹method› #>
    entity_antiqs Attrib.check_name "" binding‹attribute› #>
    entity_antiqs no_check "" binding‹fact› #>
    entity_antiqs no_check "" binding‹variable› #>
    entity_antiqs no_check "" binding‹case› #>
    entity_antiqs Document_Antiquotation.check "" binding‹antiquotation› #>
    entity_antiqs Document_Antiquotation.check_option "" binding‹antiquotation_option› #>
    entity_antiqs Document_Marker.check "" binding‹document_marker› #>
    entity_antiqs no_check "isasystem" binding‹setting› #>
    entity_antiqs check_system_option "isasystem" binding‹system_option› #>
    entity_antiqs no_check "" binding‹inference› #>
    entity_antiqs no_check "isasystem" binding‹executable› #>
    entity_antiqs Isabelle_Tool.check "isatool" binding‹tool› #>
    entity_antiqs ML_Context.check_antiquotation "" binding‹ML_antiquotation› #>
    entity_antiqs (K JEdit.check_action) "isasystem" binding‹action›);

end;

end;

Theory CTR_Introduction

(* Title: CTR_Introduction.thy
   Author: Mihails Milehins
   Copyright 2021 (C) Mihails Milehins
*)

section‹Introduction›
theory CTR_Introduction
  imports Main
begin



subsection‹Background›

text‹
The framework Conditional Transfer Rule› provides several experimental
Isabelle/Isar commands that are aimed at the automation of unoverloading
of definitions and synthesis of conditional transfer rules in Isabelle/HOL.
›



subsection‹Structure and organization›

text‹
The remainder of the reference manual is organized into two explicit sections,
one for each sub-framework of the CTR:
\begin{itemize}
\item Unoverload Definition› (UD): automated elimination of sort
constraints and unoverloading of definitions
\item Conditional Transfer Rule› (CTR): automated synthesis of 
relativized definitions and transfer rules
\end{itemize}
It should be noted that the abbreviation CTR will be used to 
refer both to the general framework and the sub-framework.
›

text‹\newpage›

end

Theory UD_Reference

(* Title: UD/UD_Reference.thy
   Author: Mihails Milehins
   Copyright 2021 (C) Mihails Milehins

Reference manual for the UD.
*)

section‹UD›
theory UD_Reference
  imports 
    UD
    "../Reference_Prerequisites"
begin



subsection‹Introduction›


subsubsection‹Background›

text‹
This section presents a reference manual for the (sub-)framework UD. 
The framework UD can be used for the elimination of sort constraints and
unoverloading of definitions in the object logic Isabelle/HOL of the 
formal proof assistant Isabelle \cite{paulson_natural_1986}. 
The framework UD evolved from the author's work on the extension of the 
framework Types-To-Sets (see \cite{blanchette_types_2016,kuncar_types_2019}, 
\cite{immler_smooth_2019} and \cite{immler_automation_2019} for a 
description of the framework Types-To-Sets, but not the author's extension) 
and builds upon certain ideas expressed in \cite{kaufmann_mechanized_2010}.
›


subsubsection‹Purpose and scope›

text‹
The primary functionality of the framework is available via the Isabelle/Isar 
command @{command ud}. This command automates the processes of the 
elimination of sort constrains and unoverloading of definitions.
Thus, the command @{command ud} allows for the synthesis
of the convenience constants and theorems that are usually needed for the 
application of the derivation step 2 of the original relativization algorithm
of Types-To-Sets (see \cite{blanchette_types_2016}). However, 
it is expected that the command can be useful for other purposes.
›


subsubsection‹Related and previous work›

text‹
The functionality provided by the command @{command ud} shares similarities
with the functionality provided by the algorithms for the elimination of
sort constraints and elimination of overloading that were 
presented in \cite{kaufmann_mechanized_2010}
and with the algorithms associated with the command 
\mbox{\textbf{unoverload\_definition}} that was proposed
in \cite{immler_automation_2019}. 
Nonetheless, technically, unlike \mbox{\textbf{unoverload\_definition}}, 
the command @{command ud} does
not require the additional axiom UO associated with Types-To-Sets for 
its operation (see \cite{blanchette_types_2016}, 
\cite{immler_automation_2019}), it uses the definitional axioms
instead of arbitrary theorems supplied by the user
and can be used for unoverloading almost any overloaded constants, 
not merely the constants overloaded using the Isabelle's type-class 
infrastructure 
\cite{nipkow_type_1991,wenzel_type_1997,altenkirch_constructive_2007}.

It should also be mentioned that the Isabelle/ML code from the main 
distribution of Isabelle was frequently reused during the 
development of the UD.
›



subsection‹Theory\label{sec:ud_theory}›

text‹
It is assumed that there exists a variable $ud_{\mathsf{with}}$ that stores theorems 
of the form $c_{\tau} = c_{\mathsf{with}}\ \bar{*}$, where $c_{\tau}$ and $c_{\mathsf{with}}$ are 
distinct constant-instances and $\bar{*}$ is a finite sequence of unresolvable 
constant-instances, such that, if $c_{\tau}$ depends on a type 
variable $\alpha_{\Upsilon}$, with $\Upsilon$ being a type class that depends 
on the overloaded constants $\bar{*'}$, then $\bar{*}$ contains $\bar{*'}$
as a subsequence. The binary operation $\cup$ is defined 
in a manner such that for any sequences $\bar{*}$ and $\bar{*'}$, 
$\bar{*} \cup \bar{*'}$ is a sequence that consists of all elements of the 
union of the elements of $\bar{*}$ and $\bar{*'}$ without duplication. 
The inputs to the algorithm associated with the command @{command ud}
are assumed to be a constant-instance $c_{\sigma}$ and a well-formed 
definitional theory $D$.
Given the constant-instance $c_{\sigma}$, there exists at most
one definitional axiom $c_{\tau} = \phi_{\tau}\left[\bar{*}\right]$ 
in $D$ such that $c_{\sigma} \leq c_{\tau}$: otherwise the orthogonality 
of $D$ and, therefore, the well-formedness of $D$ are violated 
($\phi$ is assumed to be parameterized by the types that it can have with 
respect to the type substitution operation, and $\bar{*}$ in
$c_{\tau} = \phi_{\tau}\left[\bar{*}\right]$ is a list of all uninterpreted
constant-instances that occur in $\phi_{\tau}\left[\bar{*}\right]$).

If a definitional axiom $c_{\tau}=\phi_{\tau}\left[\bar{*}\right]$ 
such that $c_{\sigma} \leq c_{\tau}$ exists for the constant-instance 
$c_{\sigma}$, then the following derivation is applied to it by the algorithm
\[
\infer[(6)]
{\vdash c_{\sigma} = c_{\mathsf{with}}\ \left(\bar{*} \cup \bar{*'}\right)}
{
\infer[(5)]
{
\vdash c_{\mathsf{with}}\ \left(\bar{*} \cup \bar{*'}\right) = 
\phi_{\mathsf{with}}\left[\bar{*} \cup \bar{*'}\right]
}
{
\infer[(4)]
{\vdash c_{\mathsf{with}}\ ?\bar{f} = \phi_{\mathsf{with}}\left[?\bar{f}\right]}
{
\infer[(3)]
{\vdash c_{\mathsf{with}} = (\lambda \bar{f}.\ \phi_{\mathsf{with}}\left[\bar{f}\right])}
{
\infer[(2)]
{\vdash c_{\sigma}=\phi_{\mathsf{with}}\left[\bar{*} \cup \bar{*'}\right]}
{
\infer[(1)]
{\vdash c_{\sigma}=\phi_{\sigma}\left[\bar{*}\right]}
{\vdash c_{\tau}=\phi_{\tau}\left[\bar{*}\right]}
}
}
}
}
}
\]
In step 1, the previously established property $c_{\sigma} \leq c_{\tau}$ is
used to create the (extended variant of the) type substitution map $\rho$
such that $\sigma = \rho \left( \tau \right)$ (see \cite{kuncar_types_2015}) 
and perform the type substitution in 
$c_{\tau}=\phi_{\tau}\left[\bar{*}\right]$ to obtain
$c_{\sigma}=\phi_{\sigma}\left[\bar{*}\right]$; in step 2, the
collection of theorems $ud_{\mathsf{with}}$ is unfolded, using it as 
a term rewriting system, possibly introducing further uninterpreted constants
$\bar{*'}$; in step 3, the term on the right hand side of the 
theorem is processed by removing the sort constraints from all type variables 
that occur in it, replacing every uninterpreted constant-instance (this 
excludes all built-in constants of Isabelle/HOL) that occurs in 
it by a fresh term variable, 
and applying the abstraction until the resulting term is closed:
this term forms the right hand side of a new definitional axiom 
of a fresh constant $c_{\mathsf{with}}$ (if the conditions associated with the 
definitional principles of Isabelle/HOL \cite{yang_comprehending_2017} are 
satisfied); step 4 is justified by the beta-contraction; step 5 is a 
substitution of the uninterpreted constants $\bar{*} \cup \bar{*'}$; 
step 6 follows trivially from the results of the application of steps 2 and 5. 

Assuming that the definitional axiom for the input to the
algorithm $c_{\sigma}$ does not have any occurrences of uninterpreted 
constants and there is exactly one type-variable $\alpha_{\Upsilon}$ that occurs
in $c_{\sigma}$, with $\Upsilon$ being a type class that depends on the 
overloaded constants $\bar{*}$, the algorithm can be restated as follows:
\[
\infer[(6)]
{\vdash c_{\sigma}\left[?\alpha_{\Upsilon}\right] = c_{\mathsf{with}}\left[?\alpha_{\Upsilon}\right]\ \bar{*}}
{
\infer[(5)]
{
\vdash c_{\mathsf{with}}\left[?\alpha_{\Upsilon}\right]\ \bar{*} = \phi_{\mathsf{with}}\left[?\alpha_{\Upsilon}, \bar{*}\right]
}
{
\infer[(4)]
{\vdash c_{\mathsf{with}}\left[?\alpha\right]\ ?\bar{f}\left[?\alpha\right] = \phi_{\mathsf{with}}\left[?\alpha, ?\bar{f}\right]}
{
\infer[(3)]
{\vdash c_{\mathsf{with}}\left[?\alpha\right] = (\lambda \bar{f}\left[?\alpha\right].\ \phi_{\mathsf{with}}\left[?\alpha, \bar{f}\right])}
{
\infer[(2)]
{\vdash c_{\sigma}\left[?\alpha_{\Upsilon}\right] = \phi_{\mathsf{with}}\left[?\alpha_{\Upsilon}, \bar{*}\right]}
{
\infer[(1)]
{\vdash c_{\sigma}\left[?\alpha_{\Upsilon}\right] = \phi_{\sigma}\left[?\alpha_{\Upsilon}\right]}
{\vdash c_{\tau}\left[?\alpha\right]=\phi_{\tau}\left[?\alpha\right]}
}
}
}
}
}
\]

The implementation of the command @{command ud} closely follows the steps of 
the algorithm outlined above. Thus, at the end of the successful
execution, the command declares the constant $c_{\mathsf{with}}$ and stores the 
constant-instance definition that is obtained at the end of step 3 of
the algorithm UD; furthermore, the command stores the theorem that is 
obtained after the execution of step 6 of the algorithm.

Unlike the classical overloading elimination algorithm, 
the algorithm employed in the implementation
of the command @{command ud} is not recursive. Thus, the users are responsible 
for maintaining an adequate collection of theorems $ud_{\mathsf{with}}$. 
Nonetheless, in this case, the users can provide their own 
unoverloaded constants $c_{\mathsf{with}}$ and the associated theorems 
$c_{\sigma} = c_{\mathsf{with}}\ \bar{*}$ for any constant-instance $c_{\sigma}$. 
From the perspective of the relativization algorithm associated with
Types-To-Sets this can be useful because there is no 
guarantee that the automatically synthesized constants $c_{\mathsf{with}}$ 
will possess desirable parametricity characteristics
(e.g., see \cite{kuncar_types_2015} and \cite{immler_smooth_2019}).
Unfortunately, the implemented algorithm still suffers from the fundamental 
limitation that was already outlined in \cite{kaufmann_mechanized_2010}, 
\cite{blanchette_types_2016} and \cite{kuncar_types_2019}: 
it does not offer a solution for handling the 
constants whose types contain occurrences of the type constructors whose 
type definitions contain occurrences of unresolvable overloading.

›



subsection‹Syntax›

text‹
This subsection presents the syntactic categories that are associated with the 
command @{command ud}. It is important to note that the presentation is 
only approximate.
›

text‹

\begin{matharray}{rcl}
  @{command_def "ud"} & : & theory → theory›\\
\end{matharray}

  

  rail@@{command ud} binding? const mixfix?

   ud (b›) const› (mixfix›) provides access to the algorithm for
the elimination of sort constraints and unoverloading of definitions
that was described in subsection \ref{sec:ud_theory}.
The optional binding b› is used for the specification
of the names of the entities added by the command to the theory and the 
optional argument mixfix› is used for the specification 
of the concrete inner syntax for the constant in the usual manner
(e.g., see \cite{wenzel_isabelle/isar_2019-1}). 
If either b› or mixfix› are not specified by the user, then the command
introduces sensible defaults. Following the specification of the 
definition of the constant, an additional theorem that establishes
the relationship between the newly introduced constant and the 
constant provided by the user as an input is established and added 
to the dynamic fact @{thm [source] ud_with}.
›



subsection‹Examples\label{sec:ud_ex}›

text‹
In this subsection, some of the capabilities of the framework UD are 
demonstrated by example. The examples that are presented in this subsection are 
expected to be sufficient for beginning an independent exploration of the 
framework, but do not cover the entire spectrum of the functionality 
and the problems that one may encounter while using it.
›


subsubsection‹Type classes›

text‹
We begin the exploration of the capabilities of the framework by considering
the constant @{const mono}.
The constant @{const mono} is an overloaded constant that is defined 
in the standard library of Isabelle/HOL \cite{noauthor_isabellehol_2020} as  
\begin{center}
@{thm [names_short = true] mono_def[no_vars]} 
\end{center}
for any @{term [show_sorts] "f::'a::order'b::order"}.
Technically, there exist two distinct constants that are associated with
the definition of the constant @{const mono} 
(see, e.g., \cite{berardi_local_2009} 
for further details): @{const order.mono} that is provided by the 
Isabelle system automatically and the constant @{const mono} itself.
The constants are unoverloaded successively using the command @{command ud}:
›
ud ‹order.mono›
ud mono' ‹mono›
text‹
The invocation of the commands above declares the constant @{const mono.with} 
that is defined as
\begin{center}
@{thm mono.with_def[no_vars]}
\end{center}
and provides the theorem @{thm [source] mono.with} given by
\begin{center}
@{thm mono.with[no_vars]}
\end{center}
and the theorem @{thm [source] mono'.with} given by
\begin{center}
@{thm mono'.with[no_vars]}.
\end{center}
The theorems establish the relationship between the unoverloaded constant
@{const mono.with} and the overloaded constant @{const mono}:
both theorems are automatically added to the dynamic fact 
@{thm [source] ud_with}.
›


subsubsection‹Low-level overloading›

text‹
The following example closely follows Example 5 in section 5.2. in 
\cite{kaufmann_mechanized_2010}. 
›

consts pls :: "'a  'a  'a"

overloading
pls_nat  "pls::nat  nat  nat"
pls_times  "pls::'a × 'b  'a × 'b  'a × 'b"
begin
definition pls_nat :: "nat  nat  nat" where "pls_nat a b = a + b"
definition pls_times :: "'a × 'b  'a × 'b  'a × 'b" 
  where "pls_times  λx y. (pls (fst x) (fst y), pls (snd x) (snd y))"
end

ud pls_nat ‹pls::nat  nat  nat›
ud pls_times ‹pls::'a × 'b  'a × 'b  'a × 'b

text‹
As expected, two new unoverloaded constants are produced via
the invocations of the command @{command ud} above. The first constant,
const‹pls_nat.with›, corresponds to pls::nat ⇒ nat ⇒ nat› and is given by
\begin{center}
@{thm pls_nat.with_def[no_vars]},
\end{center}
the second constant, const‹pls_times.with›, corresponds to
\begin{center}
pls::'a × 'b ⇒ 'a × 'b ⇒ 'a × 'b› 
\end{center}
and is given by 
\begin{center}
@{thm pls_times.with_def[no_vars]}.
\end{center}
The theorems that establish the relationship between the overloaded and
the unoverloaded constants are given by 
\begin{center}
@{thm pls_nat.with} 
\end{center}
and 
\begin{center}
@{thm pls_times.with}.
\end{center}
The definitions of the constants const‹pls_nat.with› and 
const‹pls_times.with› are consistent with the ones suggested in
\cite{kaufmann_mechanized_2010}. Nonetheless, of course, it is
important to keep in mind that the command @{command ud}
has a more restricted scope of applicability than the
algorithm suggested in \cite{kaufmann_mechanized_2010}.
›

text‹\newpage›

end

Theory CTR_Reference

(* Title: CTR/CTR_Reference.thy
   Author: Mihails Milehins
   Copyright 2021 (C) Mihails Milehins

Reference manual for the CTR.
*)

section‹CTR›
theory CTR_Reference
  imports 
    CTR
    "../UD/UD_Reference"
begin



subsection‹Introduction›


subsubsection‹Background›

text‹
This section presents a reference manual for the (sub-)framework CTR. 
The framework CTR can be used for the automated synthesis of transfer rules and
relativization of definitions.
CTR evolved from the frameworks Conditional Parametricity (CP) 
\cite{gilcher_conditional_2017} and Types-To-Sets 
(see \cite{blanchette_types_2016} and \cite{immler_smooth_2019})
that are available as part of the main distribution of Isabelle.
However, it does not require either the axiom LT or the axiom UO
associated with the Types-To-Sets for its operation.
The framework CTR introduces the following Isabelle/Isar commands:
\[
\begin{array}{rcl}
  @{command ctr} & : &  local_theory → local_theory›\\
  @{command ctr_relator} & : &  local_theory → local_theory›\\
\end{array}
\]
›


subsubsection‹Purpose and scope›

text‹
The primary functionality of the CTR is available via the
command @{command ctr}. The command @{command ctr}, given a definition 
of a constant, attempts to provide a conditional transfer rule for this 
constant, possibly under arbitrary user-defined side conditions.
The process of the synthesis of a transfer rule for 
a constant may or may not involve declaration and synthesis of a 
definition of a new (relativized) constant.
The command provides an interface for the application of two 
plausible algorithms for the synthesis of the transfer rules
that have a restricted and overlapping scope of applicability. 
The first algorithm (\textit{CTR I}) was developed and implemented in 
\cite{gilcher_conditional_2017}. 
An outline of the second algorithm (\textit{CTR II}) 
was proposed in \cite{lammich_automatic_2013} and \cite{immler_smooth_2019}:
CTR II relies on the functionality of the 
@{method transfer_prover} 
(see subsection 4.6 in \cite{kuncar_types_2015}). 
More details about CTR II are given in the next subsection.

The command @{command ctr_relator} can be used for the 
registration of the so-called \textit{ctr-relators} that need to be provided 
for every non-nullary type constructor that occurs in the type of the 
constant-instance whose definition is passed as an argument to CTR II. 
However, as a fallback solution, the command @{command ctr} may 
use the relators that are associated with the standard BNF infrastructure
of Isabelle/HOL (e.g., see \cite{traytel_foundational_2012}).
The only necessary condition for the registration of the ctr-relators 
is that they must satisfy the type-constraint 
associated with the action of a BNF on relations (e.g., see 
\cite{traytel_foundational_2012} or \cite{blanchette_bindings_2019}).
›


subsubsection‹Related and previous work›

text‹
As already mentioned, the framework CTR evolved from the framework 
CP that is available as part of the main 
distribution of Isabelle. Furthermore, CTR provides an interface to the main 
functionality associated with the framework CP 
and builds upon many ideas that could be associated with it. 

The primary reason for the development of the command @{command ctr} instead 
of extending the implementation of the existing command
@{command parametric_constant} was the philosophy of non-intervention with the
development version of Isabelle that was adopted at the onset of the design of 
the CTR. Perhaps, in the future, the functionality of the aforementioned 
commands can be integrated.

It should also be mentioned that the Isabelle/ML code from the main 
distribution of Isabelle was frequently reused during the 
development of CTR.
›



subsection‹Theory›

text‹
Assume the existence of an underlying well-formed 
definitional theory $D$ and an associated HOL signature $\Sigma$.
Furthermore, assume the existence of a map $\mathsf{ctr}_{\mathsf{Rel}}$ from a
finite set of non-nullary type constructors to ctr-relators, mapping each 
non-nullary type constructor in its domain to a valid ctr-relator for this 
type constructor. 

The inputs to CTR II are a context $\Gamma$,
a constant-instance definition 
\mbox{$\vdash c_{?\bar{\gamma}\ K} = \phi\left[?\bar{\gamma}\right]$} 
of the constant-instance $c_{?\bar{\gamma}\ K}$ 
and the map $\mathsf{trp}$ from the set of all schematic type variables
in ?$\bar{\gamma}$ to the set of (fixed) binary relations 
$x_{\alpha\rightarrow\beta\rightarrow\mathbb{B}}$ in $\Gamma$
($?\bar{\gamma}$ corresponds to a sequence of all 
distinct type variables that occur in the type $?\bar{\gamma}\ K$, 
while $K$, applied using a postfix notation, contains all information about 
the type constructors of the type $?\bar{\gamma}\ K$,
such that the non-nullary type constructors associated with $K$  
form a subset of the domain of $\mathsf{ctr}_{\mathsf{Rel}}$).
CTR II consists of three parts: 
\textit{synthesis of a parametricity relation},
\textit{synthesis of a transfer rule} and \textit{post-processing}.

\textbf{Synthesis of a parametricity relation}. 
An outline of an algorithm for the conversion of a type to a
parametricity relation is given in subsection 4.1.1 in \cite{kuncar_types_2015}.
Thus, every nullary type constructor in $?\bar{\gamma}\ K$ is replaced 
by the identity relation $=$, every non-nullary type constructor $\kappa$ 
in $?\bar{\gamma}\ K$ is replaced by its corresponding ctr-relator 
$\mathsf{ctr}_{\mathsf{Rel}}\left(\kappa\right)$ and every type variable 
$?\gamma$ in $?\bar{\gamma}\ K$ is replaced by $\mathsf{trp}\left(?\gamma\right)$, 
obtaining the parametricity relation 
$R_{\bar{\alpha}\ K \rightarrow \bar{\beta}\ K \rightarrow\mathbb{B}}$.

\textbf{Synthesis of a transfer rule}. 
First, the goal
$R\ \left(\phi\left[\bar{\alpha}\right]\right)\ \left(\phi\left[\bar{\beta}\right]\right)$
is created in $\Gamma$ and an attempt to prove it is made 
using the algorithm associated with 
the method @{method transfer_prover} (see subsection 4.6 in 
\cite{kuncar_types_2015}).
If the proof is successful, nothing else needs to be done in this part.
Otherwise, an attempt to find a solution for $?a$ in 
$R\ \left(?a_{\bar{\alpha}\ K}\right)\ \left(\phi\left[\bar{\beta}\right]\right)$ 
is made, once again, using the @{method transfer_prover}
(see subsection 4.6 in \cite{kuncar_types_2015}). 
Of course, the success of this part is not guaranteed 
(see \cite{kuncar_types_2015} for more information).
In summary, the result of the successful completion of the synthesis 
is a transfer rule 
$\Gamma \vdash R\ \left(\psi\left[\bar{\alpha},\bar{x}\right]\right)\ \left(\phi\left[\bar{\beta}\right]\right)$,
where $\bar{x}$ is used to denote a sequence of typed variables,
each of which occurs free in the context $\Gamma$. 

\textbf{Post-processing}. If 
$\psi\left[\bar{\alpha},\bar{x}\right] = \phi\left[\bar{\alpha}\right]$
after the successful completion of part 2 of the algorithm, then 
the definitions of the constant-instances $c_{\bar{\alpha}\ K}$
and $c_{\bar{\beta}\ K}$ are folded, resulting in the deduction
\mbox{$\Gamma \vdash R\ \left( c_{\bar{\alpha}\ K}\right)\ \left(c_{\bar{\beta}\ K}\right)$}. Otherwise, if 
\mbox{$\psi\left[\bar{\alpha},\bar{x}\right]\ \neq \phi\left[\bar{\alpha}\right]$},
then a new constant $d$ is declared such that 
\mbox{$\vdash d_{\sigma\left[?\bar{\alpha}\right]} = \left(\lambda \bar{x}.\ \psi\left[?\bar{\alpha},\bar{x}\right]\right)$},
where $\sigma$ is determined uniquely by $\bar{x}$ and $?\bar{\alpha}\ K$. 
In this case, 
\mbox{$\Gamma \vdash R\ \left(\psi\left[\bar{\alpha},\bar{x}\right]\right)\ \left(\phi\left[\bar{\beta}\right]\right)$}
can be restated as 
\mbox{$\Gamma \vdash R\ (d_{\sigma\left[\bar{\alpha}\right]}\ \bar{x})\ \left(c_{\bar{\beta}\ K}\right)$}.
This result can be exported to the global theory context and forms a 
conditional transfer rule for $c_{?\bar{\gamma}\ K}$.
›



subsection‹Syntax›


subsubsection‹Background›

text‹
This subsection presents the syntactic categories that are associated with the 
command @{command ctr} and closely related auxiliary commands and attributes. 
It is important to note that the presentation is only approximate.
›


subsubsectionctr_relator›

text‹
\begin{matharray}{rcl}
  @{command_def ctr_relator} & : &  local_theory → local_theory›
\end{matharray}

  

  rail@@{command ctr_relator} term›

   ctr_relator c› saves the ctr relator› c› to a database of 
ctr relators for future reference. A ctr relator is defined as any constant 
that has the type of the form
\begin{center} 
1⇒β1⇒𝔹)⇒…⇒(αn⇒βn⇒𝔹)⇒(α1…αn)κ⇒(β1…βn)κ⇒𝔹›,
\end{center}
where α1…αn and β1…βn are distinct type variables,
n› is a positive integer and κ› is a type constructor.
›


subsubsectionctr›

(*
Certain elements of the content presented below were copied from the theory 
Doc/Isar_Rel/Spec.thy in the main library of Isabelle. 
*)
text‹
\begin{matharray}{rcl}
  @{command_def ctr} & : &  local_theory → local_theory› \\
\end{matharray}

  

  rail@@{command ctr} (@'parametricity' | @'relativization' rlt) in_def
    ;
    rlt: 
      (synthesis*) 
      (cce*)
      trp
    ;
    synthesis: @'synthesis' (thm*)
    ;
    cce:
      @'fixes' vars |
      @'assumes' (props + @'and')
    ;
    trp: (@'trp' ('(' type_var term ')' + @'and'))?
    ;
    in_def: ((binding @':')? thm mixfix?) + and
  ›

   ctr provides access to two algorithms for the automated synthesis of 
the transfer rules and the relativization of constants based on their 
definitions. 

     @{element "parametricity"} indicates that CTR I needs
to be invoked by the command.

     @{element "relativization"} indicates that CTR II needs to be 
invoked by the command. 

     @{element "synthesis"} thm› indicates that the relativization of the 
inputs needs to be performed and post-processed using the simplifier with 
the collection of rewrite rules stated as the fact thm›. If the optional 
argument thm› is not provided, then the default simpset› is used for 
post-processing. If the keyword @{element "synthesis"} is omitted, 
then no attempt to perform the relativization is made. 
The keyword @{keyword "synthesis"} is relevant only for CTR II.

     @{element "trp"} (?a1 A1)› and …› and (?an An)› indicates 
that the type variable that has the indexname ?ai (1≤i≤n›, 
n› is a non-negative integer) is meant to 
correspond to the binary relation Ai in the transfer relation constructed by 
the command prior to the statement of the transfer rule (for further 
information see subsection 4.1 in \cite{kuncar_types_2015}). This is relevant only
for CTR II.

     @{element "in"} (b:) def_thm (mixfix)› is used for
the specification of the input to the algorithms that are associated with 
the command @{command ctr}. def_thm› is meant to be a fact that 
consists of exactly one theorem of the form
\begin{center}
A a1…an ≃ f a1…an,
\end{center}
where A› is a constant, ≃› is either meta- or object-equality, 
n› is a non-negative integer,
a1…an are schematic variables and f› is a suitable formula in n› 
arguments (however, there are further implicit restrictions). 
If a new constant is introduced by the command, then the optional argument
mixfix› is used for the specification 
of the concrete inner syntax for the constant 
in the usual manner
(e.g. see \cite{wenzel_isabelle/isar_2019-1}). The optional binding b› is
used for the specification of the names of the entities that
are provided after the successful execution of the command. It is hoped
that the algorithm chosen for the specification of the names 
is sufficiently intuitive and does not require further explanation.
If either b› or mixfix› are not specified by the user, then the command
introduces sensible defaults.
Multiple theorems may be provided after the
keyword @{element "in"}, employing the keyword and as a separator.
In this case, the parameterized algorithm associated with the command is
applied repeatedly to each input theorem in the order of their specification
from left to right and the local context is augmented incrementally. 
›



subsection‹Examples\label{sec:ctr_ex}›

text‹
In this subsection, some of the capabilities of the framework CTR are 
demonstrated by example. The examples that are presented in this subsection are 
expected to be sufficient to begin an independent exploration of the framework, 
but do not cover the entire spectrum of the functionality and the problems that 
one may encounter while using it.

The examples that are presented in this subsection continue the example 
of the application of the command @{command ud}
to the definition of the constant @{const mono} that was presented in 
subsection \ref{sec:ud_ex}.
›


subsubsection‹CTR I›

text‹
As already explained, the command @{command ctr} can be used to invoke
the algorithm associated with the command @{command parametric_constant}
for the synthesis of the parametricity relation for a given constant. For
example, the invocation of
› 
ctr parametricity
  in mono: mono.with_def
text‹
generates the transfer rule @{thm [source] mono_transfer}: 
\begin{center}
@{thm [display, mode=IfThenNoBox] mono_transfer[no_vars]}
\end{center}
A detailed explanation of the underlying algorithm can be found in 
\cite{gilcher_conditional_2017}.
›


subsubsection‹CTR II›

text‹
The first example demonstrates how CTR II can be 
used to produce a conditional parametricity property, identical 
to the parametricity property produced by CTR I:
›

ctr relativization
  fixes A1 :: "'a  'b  bool" and A2 :: "'c  'd  bool"
  assumes [transfer_rule]: "bi_total A1" 
  trp (?'a A1) and (?'b A2)
  in mono': mono.with_def

text‹
This produces the theorem @{thm [source] mono'.transfer}:
@{thm [display, mode=IfThenNoBox] mono'.transfer[no_vars]}
which is identical to the theorem @{thm [source] mono_transfer} generated
by CTR I in the previous subsection.
›

text‹
Of course, there is very little value in trying to establish a parametricity
property using CTR II due to the availability of CTR I. However,
it is often the case that the constant is not parametric under a given
set of side conditions. Nonetheless, in this case, it is often possible to
define a new (relativized) constant that is related to the original 
constant under the transfer relation associated with the original constant. 
It is expected that the most common application of CTR II will be the
synthesis of the relativized constants.

For example, suppose one desires to find a conditional transfer rule for
the constant @{const mono.with} such that (using the conventions from the 
previous example) the relation A1› is right total, but not, necessarily, 
left total. While, under such restriction on the involved relations, the 
constant @{const mono.with} is no longer conditionally parametric, its 
relativization exists and can be found using the transfer prover. To automate 
the relativization process, the user merely needs to add the keyword 
@{keyword "synthesis"} immediately after the keyword 
@{keyword "relativization"}:
›

ctr relativization
  synthesis ctr_simps
  fixes A1 :: "'a  'b  bool" and A2 :: "'c  'd  bool"
  assumes [transfer_domain_rule]: "Domainp A1 = (λx. x  U1)"
    and [transfer_rule]: "right_total A1" 
  trp (?'a A1) and (?'b A2)
  in mono_ow: mono.with_def

text‹
This produces the definition @{thm [source] mono_ow_def}:
@{thm [display, mode=IfThenNoBox] mono_ow_def[no_vars]}
and the theorem @{thm [source] mono_ow.transfer}:
@{thm [display, mode=IfThenNoBox] mono_ow.transfer[no_vars]}

It should be noted that, given that the keyword @{keyword "synthesis"} was
followed by the name of the named collection of theorems 
@{thm [source] ctr_simps}, this collection was used in post-processing of
the result of the relativization. The users can omit simplification
entirely by specifying the collection @{thm [source] ctr_blank} instead
of @{thm [source] ctr_simps}.
›

text‹\newpage›

end