Theory CoCallImplSafe

theory CoCallImplSafe
imports CoCallAnalysisImpl CoCallAnalysisSpec ArityAnalysisFixProps
begin

locale CoCallImplSafe
begin
sublocale CoCallAnalysisImpl.

lemma ccNeighbors_Int_ccrestr: "(ccNeighbors x G  S) = ccNeighbors x (cc_restr (insert x S) G)  S"
  by transfer auto
  
lemma 
  assumes "x  S" and "y  S"
  shows CCexp_subst: "cc_restr S (CCexp e[y::=x]a) = cc_restr S (CCexp ea)"
    and Aexp_restr_subst: "(Aexp e[y::=x]a) f|` S = (Aexp ea) f|` S"
using assms
proof (nominal_induct e avoiding: x y  arbitrary: a  S rule: exp_strong_induct_rec_set)
  case (Var b v) 
  case 1 show ?case by auto
  case 2 thus ?case by auto
next
  case (App e v)
  case 1
    with App show ?case
    by (auto simp add: Int_insert_left fv_subst_int simp del: join_comm intro: join_mono)
  case 2
    with App show ?case
     by (auto simp add: env_restr_join simp del: fun_meet_simp)
next
  case (Lam v e)
  case 1
    with Lam
    show ?case
      by (auto simp add: CCexp_pre_simps cc_restr_predCC  Diff_Int_distrib2 fv_subst_int env_restr_join env_delete_env_restr_swap[symmetric] simp del: CCexp_simps)
  case 2
    with Lam
    show ?case
      by (auto simp add: env_restr_join env_delete_env_restr_swap[symmetric]  simp del: fun_meet_simp)
next
  case (Let Γ e x y)
  hence [simp]: "x  domA Γ " "y  domA Γ"
    by (metis (erased, opaque_lifting) bn_subst domA_not_fresh fresh_def fresh_star_at_base fresh_star_def obtain_fresh subst_is_fresh(2))+

  note Let(1,2)[simp]

  from Let(3)
  have "¬ nonrec (Γ[y::h=x])"  by (simp add: nonrec_subst)

  case [simp]: 1
  have "cc_restr (S  domA Γ) (CCfix Γ[y::h=x](Afix Γ[y::h=x](Aexp e[y::=x]a  (λ_. up0) f|` thunks Γ), CCexp e[y::=x]a)) =
        cc_restr (S  domA Γ) (CCfix Γ        (Afix Γ        (Aexp e       a  (λ_. up0) f|` thunks Γ), CCexp e       a))"
    apply (subst CCfix_restr_subst')
      apply (erule Let(4))
      apply auto[5]
    apply (subst CCfix_restr) back
      apply simp
    apply (subst Afix_restr_subst')
      apply (erule Let(5))
      apply auto[5]
    apply (subst Afix_restr) back
      apply simp
    apply (simp only: env_restr_join)
    apply (subst Let(7))
      apply auto[2]
    apply (subst Let(6))
      apply auto[2]
    apply rule
    done
  thus ?case using Let(1,2) ¬ nonrec Γ ¬ nonrec (Γ[y::h=x])
    by (auto simp add: fresh_star_Pair  elim: cc_restr_eq_subset[rotated] )

  case [simp]: 2
  have "Afix Γ[y::h=x](Aexp e[y::=x]a  (λ_. up0) f|` (thunks Γ)) f|` (S  domA Γ) = Afix Γ(Aexp ea  (λ_. up0) f|` (thunks Γ)) f|` (S  domA Γ)"
    apply (subst Afix_restr_subst')
      apply (erule Let(5))
      apply auto[5]
    apply (subst Afix_restr) back
      apply auto[1]
    apply (simp only: env_restr_join)
    apply (subst Let(7))
      apply auto[2]
    apply rule
    done
  thus ?case using Let(1,2)
    using ¬ nonrec Γ ¬ nonrec (Γ[y::h=x])
    by (auto simp add: fresh_star_Pair elim:env_restr_eq_subset[rotated])
next
  case (Let_nonrec x' e exp x y)

  from Let_nonrec(1,2)
  have  "x  x'" "y  x'" by (simp_all add: fresh_at_base)

  note Let_nonrec(1,2)[simp]
  
  from x'  fv e y  x' x  x'
  have [simp]: "x'  fv (e[y::=x])"
    by (auto simp add: fv_subst_eq)

  note x'  fv e[simp] y  x' [simp]x  x'  [simp]

  case [simp]: 1

  have " a. cc_restr {x'} (CCexp exp[y::=x]a) = cc_restr {x'} (CCexp expa)"
   by (rule Let_nonrec(6)) auto
  from arg_cong[where f = "λx.  x'--x'x", OF this]
  have [simp]: "x'--x'CCexp  exp[y::=x]a  x'--x'CCexp expa" by auto

  have [simp]: " a. Aexp e[y::=x]a f|` S = Aexp ea f|` S"
    by (rule Let_nonrec(5)) auto

  have [simp]: " a. fup(Aexp e[y::=x])a f|` S = fup(Aexp e)a f|` S"
    by (case_tac a) auto

  have [simp]: "Aexp  exp[y::=x]a f|` S = Aexp expa f|` S"
    by (rule Let_nonrec(7)) auto

  have "Aexp exp[y::=x]a f|` {x'} = Aexp expa f|` {x'}"
    by (rule Let_nonrec(7)) auto
  from fun_cong[OF this, where x = x']
  have [simp]: "(Aexp exp[y::=x]a) x' = (Aexp expa) x'" by auto

  have [simp]:  " a. cc_restr S (CCexp exp[y::=x]a) = cc_restr S (CCexp expa)"
    by (rule Let_nonrec(6)) auto

  have [simp]:  " a. cc_restr S (CCexp e[y::=x]a) = cc_restr S (CCexp ea)"
    by (rule Let_nonrec(4)) auto

  have [simp]: " a. cc_restr S (fup(CCexp e[y::=x])a) = cc_restr S (fup(CCexp e)a)"
    by (rule fup_ccExp_restr_subst') simp

  have [simp]: "fv e[y::=x]  S = fv e  S"
    by (auto simp add: fv_subst_eq)

  have [simp]:
    "ccNeighbors x' (CCexp exp[y::=x]a)  - {x'}  S = ccNeighbors x' (CCexp expa)   - {x'}  S"
    apply (simp only: Int_assoc)
    apply (subst (1 2) ccNeighbors_Int_ccrestr)
    apply (subst Let_nonrec(6))
      apply auto[2]
    apply rule
    done

  have [simp]:
    "ccNeighbors x' (CCexp exp[y::=x]a)  S = ccNeighbors x' (CCexp expa)  S"
    apply (subst (1 2) ccNeighbors_Int_ccrestr)
    apply (subst Let_nonrec(6))
      apply auto[2]
    apply rule
    done

  show "cc_restr S (CCexp (let x' be e in exp )[y::=x]a) = cc_restr S (CCexp (let x' be e in exp )a)"
    apply (subst subst_let_be)
      apply auto[2]
    apply (subst (1 2) CCexp_simps(6))
      apply fact+
    apply (simp only: cc_restr_cc_delete_twist)
    apply (rule arg_cong) back
    apply (simp add:  Diff_eq ccBind_eq ABind_nonrec_eq)
    done

  show "Aexp (let x' be e in exp )[y::=x]a f|` S = Aexp (let x' be e in exp )a f|` S"
    by (simp add: env_restr_join env_delete_env_restr_swap[symmetric] ABind_nonrec_eq)
next
  case (IfThenElse scrut e1 e2)
  case [simp]: 2
    from IfThenElse
    show "cc_restr S (CCexp (scrut ? e1 : e2)[y::=x]a) = cc_restr S (CCexp (scrut ? e1 : e2)a)"
      by (auto simp del: edom_env env_restr_empty env_restr_empty_iff simp  add: edom_env[symmetric])

    from IfThenElse(2,4,6)
    show "Aexp (scrut ? e1 : e2)[y::=x]a f|` S = Aexp (scrut ? e1 : e2)a f|` S"
       by (auto simp add: env_restr_join simp del: fun_meet_simp)
qed auto
   
sublocale ArityAnalysisSafe Aexp
  by standard (simp_all add:Aexp_restr_subst)


sublocale ArityAnalysisLetSafe Aexp Aheap
proof
  fix Γ e a
  show "edom (Aheap Γ ea)  domA Γ"
    by (cases "nonrec Γ")
       (auto simp add: Aheap_nonrec_simp dest: subsetD[OF edom_esing_subset] elim!: nonrecE)
next
  fix x y :: var and Γ :: heap and e :: exp
  assume assms: "x  domA Γ"  "y  domA Γ"

  from Aexp_restr_subst[OF assms(2,1)]
  have **: " a. Aexp e[x::=y]a f|` domA Γ = Aexp ea f|` domA Γ".

  show "Aheap Γ[x::h=y] e[x::=y] = Aheap Γ e"
  proof(cases "nonrec Γ")
    case [simp]: False

    from assms
    have "atom ` domA Γ ♯* x" and "atom ` domA Γ ♯* y"
      by (auto simp add: fresh_star_at_base image_iff)
    hence [simp]: "¬ nonrec (Γ[x::h=y])"
      by (simp add: nonrec_subst)

    show ?thesis
    apply (rule cfun_eqI)
    apply simp
    apply (subst Afix_restr_subst[OF assms subset_refl])
    apply (subst Afix_restr[OF  subset_refl]) back
    apply (simp add: env_restr_join)
    apply (subst **)
    apply simp
    done
  next
    case True
    
    from assms
    have "atom ` domA Γ ♯* x" and "atom ` domA Γ ♯* y"
      by (auto simp add: fresh_star_at_base image_iff)
    with True
    have *: "nonrec (Γ[x::h=y])" by (simp add: nonrec_subst)

    from True
    obtain x' e' where [simp]: "Γ = [(x',e')]" "x'  fv e'" by (auto elim: nonrecE)

    from * have [simp]: "x'  fv (e'[x::=y])"
      by (auto simp add: nonrec_def)

    from fun_cong[OF **, where x = x']
    have [simp]: " a. (Aexp e[x::=y]a) x' = (Aexp ea) x'" by simp

    from CCexp_subst[OF assms(2,1)]
    have " a.  cc_restr {x'} (CCexp e[x::=y]a) = cc_restr {x'} (CCexp ea)" by simp
    from arg_cong[where f = "λx.  x'--x'x", OF this]
    have [simp]: " a. x'--x'(CCexp e[x::=y]a)  x'--x'(CCexp ea)" by simp
    
    show ?thesis
      apply -
      apply (rule cfun_eqI)
      apply (auto simp add: Aheap_nonrec_simp ABind_nonrec_eq)
      done
  qed
next
  fix Γ e a
  show "ABinds Γ(Aheap Γ ea)  Aexp ea  Aheap Γ ea  Aexp (Let Γ e)a"
  proof(cases "nonrec Γ")
    case False
    thus ?thesis
      by (auto simp add: Aheap_def join_below_iff env_restr_join2 Compl_partition intro:  below_trans[OF _ Afix_above_arg])
  next
    case True
    then obtain x e' where [simp]: "Γ = [(x,e')]" "x  fv e'" by (auto elim: nonrecE)

    hence " a. x  edom (fup(Aexp e')a)"
      by (auto dest:subsetD[OF fup_Aexp_edom])
    hence [simp]: " a. (fup(Aexp e')a) x = " by (simp add: edomIff)

    show ?thesis
      apply (rule env_restr_below_split[where S = "{x}"])
      apply (rule env_restr_belowI2)
      apply (auto simp add:  Aheap_nonrec_simp  join_below_iff env_restr_join env_delete_restr)
      apply (rule ABind_nonrec_above_arg)
      apply (rule below_trans[OF _ join_above2])
      apply (rule below_trans[OF _ join_above2])
      apply (rule below_refl)
      done
  qed
qed

definition ccHeap_nonrec
  where "ccHeap_nonrec x e exp = (Λ n. CCfix_nonrec x e(Aexp expn, CCexp expn))"

lemma ccHeap_nonrec_eq:
   "ccHeap_nonrec x e expn = CCfix_nonrec x e(Aexp expn, CCexp expn)"
unfolding ccHeap_nonrec_def by (rule beta_cfun) (intro cont2cont)

definition ccHeap_rec :: "heap  exp  Arity  CoCalls"
  where "ccHeap_rec Γ e  = (Λ a. CCfix Γ(Afix Γ(Aexp ea  (λ_.up0) f|` (thunks Γ)), CCexp ea))"

lemma ccHeap_rec_eq:
  "ccHeap_rec Γ ea = CCfix Γ(Afix Γ(Aexp ea  (λ_.up0) f|` (thunks Γ)), CCexp ea)"
unfolding ccHeap_rec_def by simp

definition ccHeap :: "heap  exp  Arity  CoCalls"
  where "ccHeap Γ  = (if nonrec Γ then case_prod ccHeap_nonrec (hd Γ) else ccHeap_rec Γ)"

lemma ccHeap_simp1:
  "¬ nonrec Γ  ccHeap Γ ea = CCfix Γ(Afix Γ(Aexp ea  (λ_.up0) f|` (thunks Γ)), CCexp ea)"
  by (simp add: ccHeap_def ccHeap_rec_eq)

lemma ccHeap_simp2:
  "x  fv e  ccHeap [(x,e)] expn = CCfix_nonrec x e(Aexp expn, CCexp expn)"
  by (simp add: ccHeap_def ccHeap_nonrec_eq nonrec_def)


sublocale CoCallAritySafe CCexp Aexp ccHeap Aheap
proof
  fix e a x
  show "CCexp e(inca)  ccProd {x} (insert x (fv e))  CCexp (App e x)a"
    by simp
next
  fix y e n
  show "cc_restr (fv (Lam [y]. e)) (CCexp e(predn))  CCexp (Lam [y]. e)n"
    by (auto simp add: CCexp_pre_simps predCC_eq dest!: subsetD[OF ccField_cc_restr] simp del: CCexp_simps)
next
  fix x y :: var and S e a
  assume "x  S"  and "y  S"
  thus "cc_restr S (CCexp e[y::=x]a)  cc_restr S (CCexp ea)"
    by (rule eq_imp_below[OF CCexp_subst])
next
  fix e
  assume "isVal e"
  thus "CCexp e0 = ccSquare (fv e)"
    by (induction e rule: isVal.induct) (auto simp add: predCC_eq)
next
  fix Γ e a
  show "cc_restr (- domA Γ) (ccHeap Γ ea)  CCexp (Let Γ e)a"
  proof(cases "nonrec Γ")
    case False
    thus "cc_restr (- domA Γ) (ccHeap Γ ea)  CCexp (Let Γ e)a"
      by (simp add: ccHeap_simp1[OF False, symmetric] del: cc_restr_join)
  next
    case True
    thus ?thesis
      by (auto simp add: ccHeap_simp2 Diff_eq elim!: nonrecE simp del: cc_restr_join)
  qed
next
  fix Δ :: heap and e a

  show "CCexp ea  ccHeap Δ ea"
    by (cases "nonrec Δ")
       (auto simp add: ccHeap_simp1 ccHeap_simp2 arg_cong[OF CCfix_unroll, where f = "(⊑) x" for x ] elim!: nonrecE)

  fix x e' a'
  assume "map_of Δ x = Some e'"
  hence [simp]: "x  domA Δ" by (metis domI dom_map_of_conv_domA) 
  assume "(Aheap Δ ea) x = upa'"
  show "CCexp e'a'  ccHeap Δ ea"
  proof(cases "nonrec Δ")
    case False

    from (Aheap Δ ea) x = upa' False
    have "(Afix Δ(Aexp ea  (λ_.up0)f|` (thunks Δ))) x = upa'" 
      by (simp add: Aheap_def)
    hence "CCexp e'a'  ccBind x e'(Afix Δ(Aexp ea  (λ_.up0)f|` (thunks Δ)), CCfix Δ(Afix Δ(Aexp ea  (λ_.up0)f|` (thunks Δ)), CCexp ea))"
      by (auto simp add: ccBind_eq dest: subsetD[OF ccField_CCexp])
    also
    have "ccBind x e'(Afix Δ(Aexp ea  (λ_.up0)f|` (thunks Δ)), CCfix Δ(Afix Δ(Aexp ea  (λ_.up0)f|` (thunks Δ)), CCexp ea))   ccHeap Δ ea"
      using map_of Δ x = Some e' False
      by (fastforce simp add: ccHeap_simp1 ccHeap_rec_eq ccBindsExtra_simp  ccBinds_eq  arg_cong[OF CCfix_unroll, where f = "(⊑) x" for x ]
                  intro: below_trans[OF _ join_above2])
    finally
    show "CCexp e'a'  ccHeap Δ ea" by this simp_all
  next
    case True
    with map_of Δ x = Some e'
    have [simp]: "Δ = [(x,e')]" "x  fv e'" by (auto elim!: nonrecE split: if_splits)

    show ?thesis
    proof(cases "x--xCCexp ea  isVal e'")
      case True
      with (Aheap Δ ea) x = upa'
      have [simp]: "(CoCallArityAnalysis.Aexp cCCexp ea) x = upa'"
        by (auto simp add: Aheap_nonrec_simp ABind_nonrec_eq split: if_splits)

      have "CCexp e'a'  ccSquare (fv e')"
        unfolding below_ccSquare
        by (rule ccField_CCexp)
      then
      show ?thesis using True
        by (auto simp add: ccHeap_simp2 ccBind_eq Aheap_nonrec_simp ABind_nonrec_eq below_trans[OF _ join_above2] simp del: below_ccSquare )
    next
      case False

      from (Aheap Δ ea) x = upa'
      have [simp]: "a' = 0" using  False
        by (auto simp add: Aheap_nonrec_simp ABind_nonrec_eq split: if_splits)

      show ?thesis using False
        by (auto simp add: ccHeap_simp2 ccBind_eq Aheap_nonrec_simp ABind_nonrec_eq simp del: below_ccSquare )
    qed
  qed

  show "ccProd (fv e') (ccNeighbors x (ccHeap Δ ea) - {x}  thunks Δ)  ccHeap Δ ea" 
  proof (cases "nonrec Δ")
    case [simp]: False

    have "ccProd (fv e') (ccNeighbors x (ccHeap Δ ea) - {x}  thunks Δ)  ccProd (fv e') (ccNeighbors x (ccHeap Δ ea))"
      by (rule ccProd_mono2) auto
    also have "  (xe'map_of Δ. ccProd (fv e') (ccNeighbors x (ccHeap Δ ea)))" 
      using map_of Δ x = Some e' by (rule below_lubmapI)
    also have "  ccBindsExtra Δ(Afix Δ(Aexp ea  (λ_.up0)f|` (thunks Δ)), ccHeap Δ ea)"
      by (simp add: ccBindsExtra_simp  below_trans[OF _ join_above2])
    also have "  ccHeap Δ ea"
      by (simp add: ccHeap_simp1  arg_cong[OF CCfix_unroll, where f = "(⊑) x" for x])
    finally
    show ?thesis by this simp_all
  next
    case True
    with map_of Δ x = Some e'
    have [simp]: "Δ = [(x,e')]" "x  fv e'" by (auto elim!: nonrecE split: if_splits)

    have [simp]: "(ccNeighbors x (ccBind x e'(Aexp ea, CCexp ea))) = {}"
     by (auto simp add: ccBind_eq dest!: subsetD[OF ccField_cc_restr] subsetD[OF ccField_fup_CCexp])

    show ?thesis
    proof(cases "isVal e'  x--xCCexp ea")
    case True

    have "ccNeighbors x (ccHeap Δ ea) =
        ccNeighbors x (ccBind x e'(Aheap_nonrec x e'(Aexp ea, CCexp ea), CCexp ea)) 
        ccNeighbors x (ccProd (fv e') (ccNeighbors x (CCexp ea) - (if isVal e' then {} else {x}))) 
        ccNeighbors x (CCexp ea)" by (auto simp add: ccHeap_simp2 )
    also have "ccNeighbors x (ccBind  x e'(Aheap_nonrec x e'(Aexp ea, CCexp ea), CCexp ea)) = {}"
       by (auto simp add: ccBind_eq dest!: subsetD[OF ccField_cc_restr] subsetD[OF ccField_fup_CCexp])
    also have "ccNeighbors x (ccProd (fv e') (ccNeighbors x (CCexp ea) - (if isVal e' then {} else {x})))
       ccNeighbors x (ccProd (fv e') (ccNeighbors x (CCexp ea)))" by (simp add: ccNeighbors_ccProd)
    also have "  fv e'" by (simp add: ccNeighbors_ccProd)
    finally
    have "ccNeighbors x (ccHeap Δ ea) - {x}  thunks Δ  ccNeighbors x (CCexp ea)  fv e'" by auto
    hence "ccProd (fv e') (ccNeighbors x (ccHeap Δ ea) - {x}  thunks Δ)  ccProd (fv e') (ccNeighbors x (CCexp ea)  fv e')" by (rule ccProd_mono2)
    also have "  ccProd (fv e') (ccNeighbors x (CCexp ea))  ccProd (fv e') (fv e')" by simp
    also have "ccProd (fv e') (ccNeighbors x (CCexp ea))  ccHeap Δ ea"
      using map_of Δ x = Some e' (Aheap Δ ea) x = upa' True
      by (auto simp add: ccHeap_simp2  below_trans[OF _ join_above2])
    also have "ccProd (fv e') (fv e') = ccSquare (fv e')" by (simp add: ccSquare_def)
    also have "  ccHeap Δ ea"
      using map_of Δ x = Some e' (Aheap Δ ea) x = upa' True
      by (auto simp add: ccHeap_simp2  ccBind_eq below_trans[OF _ join_above2])
    also note join_self
    finally show ?thesis by this simp_all
  next
    case False
    have "ccNeighbors x (ccHeap Δ ea) =
        ccNeighbors x (ccBind x e'(Aheap_nonrec x e'(Aexp ea, CCexp ea), CCexp ea)) 
        ccNeighbors x (ccProd (fv e') (ccNeighbors x (CCexp ea) - (if isVal e' then {} else {x}))) 
        ccNeighbors x (CCexp ea)" by (auto simp add: ccHeap_simp2 )
    also have "ccNeighbors x (ccBind  x e'(Aheap_nonrec x e'(Aexp ea, CCexp ea), CCexp ea)) = {}"
       by (auto simp add: ccBind_eq dest!: subsetD[OF ccField_cc_restr] subsetD[OF ccField_fup_CCexp])
    also have  "ccNeighbors x (ccProd (fv e') (ccNeighbors x (CCexp ea) - (if isVal e' then {} else {x}) )) 
      = {}" using False by (auto simp add: ccNeighbors_ccProd)
    finally
    have "ccNeighbors x (ccHeap Δ ea)  ccNeighbors x (CCexp ea)" by auto
    hence"ccNeighbors x (ccHeap Δ ea)  - {x}  thunks Δ  ccNeighbors x (CCexp ea)   - {x}  thunks Δ" by auto
    hence "ccProd (fv e') (ccNeighbors x (ccHeap Δ ea) - {x}  thunks Δ )  ccProd (fv e') (ccNeighbors x (CCexp ea)  - {x}  thunks Δ )" by (rule ccProd_mono2)
    also have "  ccHeap Δ ea"
      using map_of Δ x = Some e' (Aheap Δ ea) x = upa' False
      by (auto simp add: ccHeap_simp2  thunks_Cons below_trans[OF _ join_above2])
    finally show ?thesis by this simp_all
   qed
  qed

next
  fix x Γ e a
  assume [simp]: "¬ nonrec Γ"
  assume "x  thunks Γ"
  hence [simp]: "x  domA Γ" by (rule subsetD[OF thunks_domA])
  assume "x  edom (Aheap Γ ea)"

  from x  thunks Γ
  have "(Afix Γ(Aexp ea  (λ_.up0)f|` (thunks Γ))) x = up0" 
    by (subst Afix_unroll) simp

  thus "(Aheap Γ ea) x = up0" by simp
next
  fix x Γ e a
  assume "nonrec Γ"
  then obtain x' e' where [simp]: "Γ = [(x',e')]" "x'  fv e'" by (auto elim: nonrecE)
  assume "x  thunks Γ"
  hence [simp]: "x = x'" "¬ isVal e'" by (auto simp add: thunks_Cons split: if_splits)

  assume "x--x  CCexp ea"
  hence [simp]: "x'--x' CCexp  ea" by simp

  from x  thunks Γ
  have "(Afix Γ(Aexp ea  (λ_.up0)f|` (thunks Γ))) x = up0" 
    by (subst Afix_unroll) simp

  show "(Aheap Γ ea) x = up0" by (auto simp add: Aheap_nonrec_simp ABind_nonrec_eq)
next
  fix scrut e1 a e2
  show "CCexp scrut0  (CCexp e1a  CCexp e2a)  ccProd (edom (Aexp scrut0)) (edom (Aexp e1a)  edom (Aexp e2a))  CCexp (scrut ? e1 : e2)a"
    by simp
qed
end

end