Theory Anti_Unification

section ‹Anti-Unification›
theory Anti_Unification
imports Refine_Util
begin
  text ‹We implement a simple anti-unification.
    Currently, we do not handle lambdas, nor sorts, and avoid
    higher-order variables, i.e.,
    f x and g x will be unified to ?v, not ?v x, and existing higher-order
    patterns will be collapsed, e.g.: ?f x and ?f x will become ?v.
›

ML signature ANTI_UNIFICATION = sig
    type typ_env
    type term_env
    type env = typ_env * term_env

    val empty_typ: typ_env
    val empty_term: term_env
    val empty: env

    val anti_unifyT: typ * typ -> typ_env -> typ * typ_env
    val anti_unify_env: term * term -> env -> term * env
    val anti_unify: term * term -> term
    val anti_unify_list: term list -> term

    val specialize_tac: Proof.context -> thm list -> tactic'
    val specialize_net_tac: Proof.context -> (int * thm) Net.net -> tactic'
  end


  structure Anti_Unification :ANTI_UNIFICATION = struct
    structure Term2tab = Table (
      type key = term * term
      val ord = prod_ord Term_Ord.fast_term_ord Term_Ord.fast_term_ord
    );

    structure Typ2tab = Table (
      type key = typ * typ
      val ord = prod_ord Term_Ord.typ_ord Term_Ord.typ_ord
    );

    type typ_env = (typ Typ2tab.table * int)
    type term_env = (term Term2tab.table * int)
    type env = typ_env * term_env

    val empty_typ = (Typ2tab.empty,0)
    val empty_term = (Term2tab.empty,0)
    val empty = (empty_typ,empty_term)

    fun tvar_pair p (vtab,idx) =
      case Typ2tab.lookup vtab p of
        NONE => let
          val tv = TVar (("T",idx),[])
          val vtab = Typ2tab.update (p,tv) vtab
        in
          (tv,(vtab,idx+1))
        end
      | SOME tv => (tv,(vtab,idx))

    fun anti_unifyT (TFree v1, TFree v2) dt =
        if v1 = v2 then (TFree v1,dt)
        else tvar_pair (TFree v1, TFree v2) dt
      | anti_unifyT (p as (Type (n1,l1), Type (n2,l2))) dt =
        if n1 = n2 andalso (length l1 = length l2) then
          let
            val (l,dt) = fold_map anti_unifyT (l1~~l2) dt
          in (Type (n1,l),dt) end
        else
          tvar_pair p dt
      | anti_unifyT p dt = tvar_pair p dt

    fun var_pair p (tdt,(vtab,idx)) =
      case Term2tab.lookup vtab p of
        NONE => let
          val (T,tdt) = anti_unifyT (apply2 fastype_of p) tdt
          val v = Var (("v",idx),T)
          val vtab = Term2tab.update (p,v) vtab
        in
          (v,(tdt,(vtab,idx+1)))
        end
      | SOME v => (v,(tdt,(vtab,idx)))

    fun anti_unify_env (p as (Free (n1,T1), Free (n2,T2))) (tdt,dt) =
        if n1 = n2 then
          let
            val (T,tdt) = anti_unifyT (T1,T2) tdt
          in (Free (n1,T),(tdt,dt)) end
        else
          var_pair p (tdt,dt)
      | anti_unify_env (p as (Const (n1,T1), Const (n2,T2))) (tdt,dt) =
        if n1 = n2 then
          let
            val (T,tdt) = anti_unifyT (T1,T2) tdt
          in (Const (n1,T),(tdt,dt)) end
        else
          var_pair p (tdt,dt)
      | anti_unify_env (p as (f1$x1,f2$x2)) dtp =
        let
          val (f,dtp) = anti_unify_env (f1,f2) dtp
        in
          if is_Var f then
            var_pair p dtp
          else let
            val (x,dtp) = anti_unify_env (x1,x2) dtp
          in (f$x,dtp) end
        end
      | anti_unify_env p dtp = var_pair p dtp

    fun anti_unify p = anti_unify_env p empty |> #1

    fun anti_unify_list l = let
      fun f [] _ = raise TERM ("anti_unify_list: Empty",[])
        | f [t] dt = (t,dt)
        | f (t1::t2::ts) dt = let
            val (t,dt) = anti_unify_env (t1,t2) dt
          in f (t::ts) dt end
    in
      f l empty |> #1
    end

    local
      fun specialize_aux_tac ctxt get_candidates i st = let
        val maxidx = Thm.maxidx_of st
        val concl = Logic.concl_of_goal (Thm.prop_of st) i
        val pre_candidates = get_candidates concl
          |> map (fn thm =>
               let
                 val tidx = Thm.maxidx_of thm
                 val t = Thm.incr_indexes (maxidx + 1) thm |> Thm.concl_of
               in (maxidx + tidx + 1,t) end)

        fun unifies (idx,t)
          = can (Pattern.unify (Context.Proof ctxt) (t, concl)) (Envir.empty idx)

        val candidates = filter unifies pre_candidates |> map #2
      in
        case candidates of
          [] => Seq.single st
        | _ => let
            val pattern = anti_unify_list candidates
              |> Thm.cterm_of ctxt |> Thm.trivial
          in
            resolve_tac ctxt [pattern] i st
          end
      end
    in
      fun specialize_tac ctxt thms = let
        fun get_candidates concl =
          filter (fn thm => Term.could_unify (Thm.concl_of thm, concl)) thms
      in
        specialize_aux_tac ctxt get_candidates
      end

      fun specialize_net_tac ctxt net = let
        fun get_candidates concl = Net.unify_term net concl |> map #2
      in
        specialize_aux_tac ctxt get_candidates
      end
    end



  end

end