Theory DFS_Framework.Rec_Impl

section ‹Recursive DFS Implementation›
theory Rec_Impl
imports General_DFS_Structure
begin

locale rec_impl_defs =
  graph_defs G + gen_dfs_defs gds V0
  for G :: "('v, 'more) graph_rec_scheme"
  and gds :: "('v,'s)gen_dfs_struct"
  +
  fixes pending :: "'s  'v rel"
  fixes stack :: "'s  'v list"
  fixes choose_pending :: "'v  'v option  's  's nres"
begin

  definition "gen_step' s  do { ASSERT (gen_rwof s);
    if gds_is_empty_stack gds s then do {
      v0  SPEC (λv0. v0  V0  ¬ gds_is_discovered gds v0 s);
      gds_new_root gds v0 s
    } else do {
      let u = hd (stack s);
      Vs  SELECT (λv. (u,v)pending s);
      s  choose_pending u Vs s;
      case Vs of 
        None  gds_finish gds u s
      | Some v 
         if gds_is_discovered gds v s
         then if gds_is_finished gds v s then gds_cross_edge gds u v s
              else gds_back_edge gds u v s
         else gds_discover gds u v s
    }}"  

  definition "gen_dfs'  gds_init gds  WHILE gen_cond gen_step'"
  abbreviation "gen_rwof'  rwof (gds_init gds) gen_cond gen_step'"

  definition rec_impl where [DFS_code_unfold]:
  "rec_impl  do {
    s  gds_init gds;

    FOREACHci 
      (λit s. 
          gen_rwof' s 
         (¬gds_is_break gds s  gds_is_empty_stack gds s
             V0-it  gen_discovered s))
      V0
      (Not o gds_is_break gds) 
      (λv0 s. do {
        let s0 = GHOST s;
        if gds_is_discovered gds v0 s then
          RETURN s
        else do {
          s  gds_new_root gds v0 s;
          if gds_is_break gds s then
            RETURN s
          else do {
            REC_annot
            (λ(u,s). gen_rwof' s  ¬gds_is_break gds s 
                 (stk. stack s = u#stk) 
                 E  {u}×UNIV  pending s)
            (λ(u,s) s'. 
                  gen_rwof' s' 
                 (¬gds_is_break gds s'  
                    stack s' = tl (stack s) 
                   pending s' = pending s - {u} × UNIV
                   gen_discovered s'  gen_discovered s
                  ))
            (λD (u,s). do {
              s  FOREACHci 
                (λit s'. gen_rwof' s'
                 (¬gds_is_break gds s' 
                    stack s' = stack s 
                   pending s' = (pending s - {u}×(E``{u} - it))
                   gen_discovered s'  gen_discovered s  (E``{u} - it)
                  )) 
                (E``{u}) (λs. ¬gds_is_break gds s) 
                (λv s. do {
                  s  choose_pending u (Some v) s;
                  if gds_is_discovered gds v s then do {
                    if gds_is_finished gds v s then
                      gds_cross_edge gds u v s
                    else
                      gds_back_edge gds u v s
                  } else do {
                    s  gds_discover gds u v s;
                    if gds_is_break gds s then RETURN s else D (v,s) 
                  }
                }) 
                s;
              if gds_is_break gds s then 
                RETURN s
              else do {
                s  choose_pending u (None) s;
                s  gds_finish gds u s;
                RETURN s
              } 
            }) (v0,s)
          }
        }
      }) s
    }"

  definition rec_impl_for_paper where "rec_impl_for_paper  do {
    s  gds_init gds;
    FOREACHc V0 (Not o gds_is_break gds) (λv0 s. do {
      if gds_is_discovered gds v0 s then RETURN s
      else do {
        s  gds_new_root gds v0 s;
        if gds_is_break gds s then RETURN s
        else do {
          REC (λD (u,s). do {
            s  FOREACHc (E``{u}) (λs. ¬gds_is_break gds s) (λv s. do {
                s  choose_pending u (Some v) s;
                if gds_is_discovered gds v s then do {
                  if gds_is_finished gds v s then gds_cross_edge gds u v s
                  else gds_back_edge gds u v s
                } else do {
                  s  gds_discover gds u v s;
                  if gds_is_break gds s then RETURN s else D (v,s) 
                }
              }) 
              s;
            if gds_is_break gds s then RETURN s
            else do {
              s  choose_pending u (None) s;
              gds_finish gds u s
            } 
          }) (v0,s)
        }
      }
    }) s
  }"

end

(* Recursive implementation of general DFS *)
locale rec_impl =
  fb_graph G + gen_dfs gds V0 + rec_impl_defs G gds pending stack choose_pending
  for G :: "('v, 'more) graph_rec_scheme"
  and gds :: "('v,'s)gen_dfs_struct"
  and pending :: "'s  'v rel"
  and stack :: "'s  'v list"
  and choose_pending :: "'v  'v option  's  's nres"
  +
  assumes [simp]: "gds_is_empty_stack gds s  stack s = []"
  assumes init_spec: 
    "gds_init gds n SPEC (λs. stack s = []  pending s = {})"
  assumes new_root_spec: 
    "pre_new_root v0 s 
       gds_new_root gds v0 s n SPEC (λs'. 
        stack s' = [v0]  pending s' = {v0}×E``{v0} 
        gen_discovered s' = insert v0 (gen_discovered s))"

  assumes get_pending_fmt: " pre_get_pending s   
    do {
      let u = hd (stack s);
      vo  SELECT (λv. (u,v)pending s);
      s  choose_pending u vo s;
      RETURN (u,vo,s)
    } 
   gds_get_pending gds s" (* TODO: ≤n should be enough here! *)

  assumes choose_pending_spec: "pre_get_pending s; u = hd (stack s); 
    case vo of 
      None  pending s `` {u} = {}
    | Some v  vpending s `` {u}
   
    choose_pending u vo s n SPEC (λs'. 
      (case vo of
        None  pending s' = pending s
      | Some v  pending s' = pending s - {(u,v)}) 
      stack s' = stack s 
      (x. gds_is_discovered gds x s' = gds_is_discovered gds x s) 
      ⌦‹∧ gds_is_break gds s' = gds_is_break gds s›
    )"
  assumes finish_spec: "pre_finish u s0 s 
     gds_finish gds u s n SPEC (λs'. 
      pending s' = pending s 
      stack s' = tl (stack s) 
      (x. gds_is_discovered gds x s' = gds_is_discovered gds x s))"
  assumes cross_edge_spec: "pre_cross_edge u v s0 s 
     gds_cross_edge gds u v s n SPEC (λs'. 
      pending s' = pending s  stack s' = stack s 
      (x. gds_is_discovered gds x s' = gds_is_discovered gds x s))"
  assumes back_edge_spec: "pre_back_edge u v s0 s 
     gds_back_edge gds u v s n SPEC (λs'. 
      pending s' = pending s  stack s' = stack s 
      (x. gds_is_discovered gds x s' = gds_is_discovered gds x s))"
  assumes discover_spec: "pre_discover u v s0 s 
     gds_discover gds u v s n SPEC (λs'. 
      pending s' = pending s  ({v} × E``{v})  stack s' = v#stack s 
      gen_discovered s' = insert v (gen_discovered s))"



begin

    
  lemma gen_step'_refine: 
    "gen_rwof s; gen_cond s  gen_step' s  gen_step s"
    apply (simp only: gen_step'_def gen_step_def)
    apply (clarsimp)
    apply (rule order_trans[OF _ bind_mono(1)[OF get_pending_fmt order_refl]])
    apply (simp add: pw_le_iff refine_pw_simps
      split: option.splits if_split)
    apply (simp add: pre_defs gen_cond_def)
    done


  lemma gen_dfs'_refine: "gen_dfs'  gen_dfs"
    unfolding gen_dfs'_def gen_dfs_def WHILE_eq_I_rwof[where f=gen_step]
    apply (rule refine_IdD)    
    apply (refine_rcg)
    by (simp_all add: gen_step'_refine)

  lemma gen_rwof'_imp_rwof:
    assumes NF: "nofail gen_dfs"
    assumes A: "gen_rwof' s"
    shows "gen_rwof s"
    apply (rule rwof_step_refine)
      apply (rule NF[unfolded gen_dfs_def])

      apply fact

      apply (rule leof_lift[OF gen_step'_refine], assumption+) []
    done


  lemma reachable_invar: 
    "gen_rwof' s  set (stack s)  reachable  pending s  E 
       set (stack s)  gen_discovered s  distinct (stack s)
       pending s  set (stack s) × UNIV"
    apply (erule establish_rwof_invar[rotated -1])    
    apply (rule leof_trans[OF init_spec], auto) []
    apply (subst gen_step'_def)
    apply (refine_rcg refine_vcg
      leof_trans[OF new_root_spec]
      SELECT_rule[THEN leof_lift]
      leof_trans[OF choose_pending_spec[THEN leof_strengthen_SPEC]]
      leof_trans[OF finish_spec]
      leof_trans[OF cross_edge_spec]
      leof_trans[OF back_edge_spec]
      leof_trans[OF discover_spec]
      )
    

    apply simp_all
    subgoal by (simp add: pre_defs, simp add: gen_cond_def)
    subgoal by auto    
    subgoal by auto    
    subgoal by auto    
    subgoal by (simp add: pre_defs, simp add: gen_cond_def)
        
        
    apply ((unfold pre_defs, intro conjI); assumption?) []
      subgoal by (clarsimp simp: gen_cond_def)
      subgoal by (clarsimp simp: gen_cond_def)
      subgoal    
        apply (rule pwD2[OF get_pending_fmt])
          subgoal by (simp add: pre_defs gen_cond_def)
          subgoal by (clarsimp simp: refine_pw_simps; blast)
        done      
      subgoal by (force simp: neq_Nil_conv) []
          
          
    subgoal by (clarsimp simp: neq_Nil_conv gen_cond_def, blast) [] 
    subgoal by (clarsimp simp: neq_Nil_conv gen_cond_def; auto)

    apply (unfold pre_defs, intro conjI, assumption) []
      subgoal by (clarsimp_all simp: gen_cond_def)
      subgoal by (clarsimp_all simp: gen_cond_def)
      apply (rule pwD2[OF get_pending_fmt])
        apply (simp add: pre_defs gen_cond_def; fail)
        apply (clarsimp simp: refine_pw_simps select_def, blast; fail)
        apply (simp; fail)
        apply (simp; fail)

    subgoal by auto
    subgoal by fast

    apply (unfold pre_defs, intro conjI, assumption) []
      apply (clarsimp simp: gen_cond_def; fail)
      apply (clarsimp simp: gen_cond_def; fail)
      apply (rule pwD2[OF get_pending_fmt])
        apply (simp add: pre_defs gen_cond_def; fail)
        apply (clarsimp simp: refine_pw_simps select_def, blast; fail)
        apply (simp; fail)

    subgoal  
      apply clarsimp  
      by (meson ImageI SigmaD1 rtrancl_image_unfold_right subset_eq)  

    subgoal  
      apply clarsimp  
      by blast  
        
    apply force
    apply force
    apply fast
    apply (auto simp: pre_defs gen_cond_def; fail)
    apply fast

    apply ((unfold pre_defs, intro conjI); assumption?)
      apply (clarsimp simp: gen_cond_def; fail)
      apply (clarsimp simp: gen_cond_def; fail)
      apply (rule pwD2[OF get_pending_fmt])
        apply (simp add: pre_defs gen_cond_def; fail)
        apply (clarsimp simp: refine_pw_simps; fail)

    apply (auto simp: neq_Nil_conv; fail)
    apply (auto simp: neq_Nil_conv; fail)
    apply (clarsimp simp: neq_Nil_conv; blast) 
    done

  lemma mk_spec_aux: 
    "m n SPEC Φ; mSPEC gen_rwof'   m  SPEC (λs. gen_rwof' s  Φ s)"  
    by (rule SPEC_rule_conj_leofI1)


  definition "post_choose_pending u vo s0 s  
    gen_rwof' s0 
   gen_cond s0
   stack s0  []
   u=hd (stack s0)  
   inres (choose_pending u vo s0) s 
   stack s = stack s0
   (x. gds_is_discovered gds x s = gds_is_discovered gds x s0)
  ⌦‹∧ gds_is_break gds s = gds_is_break gds s0›
   (case vo of
      None  pending s0``{u}={}  pending s = pending s0
    | Some v  v  pending s0``{u}  pending s = pending s0 - {(u,v)})"

  context
    assumes nofail: 
      "nofail (gds_init gds  WHILE gen_cond gen_step')"
    assumes nofail2: 
      "nofail (gen_dfs)"
  begin
    lemma pcp_imp_pgp: 
      "post_choose_pending u vo s0 s  post_get_pending u vo s0 s"
      unfolding post_choose_pending_def pre_defs
      apply (intro conjI)
      apply (simp add: gen_rwof'_imp_rwof[OF nofail2])
      apply simp
      apply (simp add: gen_cond_def)
      apply (rule pwD2[OF get_pending_fmt])
      apply (simp add: pre_defs gen_cond_def 
          gen_rwof'_imp_rwof[OF nofail2])
      apply (auto simp add: refine_pw_simps select_def split: option.splits) []
      done

    schematic_goal gds_init_refine: "?prop"
      apply (rule mk_spec_aux[OF init_spec])
      apply (rule rwof_init[OF nofail])
      done      

    schematic_goal gds_new_root_refine: 
      "pre_new_root v0 s; gen_rwof' s  gds_new_root gds v0 s  SPEC "
      apply (rule mk_spec_aux[OF new_root_spec], assumption)
      apply (rule order_trans[OF _ rwof_step[OF nofail, where s=s]])
      unfolding gen_step'_def pre_new_root_def gen_cond_def
      apply (auto simp: pw_le_iff refine_pw_simps)
      done      

    schematic_goal gds_choose_pending_refine: 
      assumes 1: "pre_get_pending s"
      assumes 2: "gen_rwof' s"
      assumes [simp]: "u=hd (stack s)"
      assumes 3: "case vo of 
          None  pending s `` {u} = {}
        | Some v  v  pending s `` {u}"
      shows "choose_pending u vo s  SPEC (post_choose_pending u vo s)"
    proof -
      from WHILE_nofail_imp_rwof_nofail[OF nofail 2] 1 3 have 
        "nofail (choose_pending u vo s)"
        unfolding pre_defs gen_step'_def gen_cond_def
        by (auto simp: refine_pw_simps select_def 
          split: option.splits if_split_asm)
      also have "choose_pending u vo s n SPEC (post_choose_pending u vo s)"
        apply (rule leof_trans[OF choose_pending_spec[OF 1 _ 3, THEN leof_strengthen_SPEC]])
        apply simp
        apply (rule leof_RES_rule)
        using 1
        apply (simp add: post_choose_pending_def 2 pre_defs gen_cond_def split: option.splits)
        using 3
        apply auto
        done
      finally (leofD) show ?thesis .
    qed

    schematic_goal gds_finish_refine: 
      "pre_finish u s0 s; post_choose_pending u None s0 s  gds_finish gds u s  SPEC "
      apply (rule mk_spec_aux[OF finish_spec], assumption)
      apply (rule order_trans[OF _ rwof_step[OF nofail, where s=s0]])
      unfolding gen_step'_def pre_defs gen_cond_def post_choose_pending_def
      apply (auto simp: pw_le_iff refine_pw_simps split: option.split)  
      done      

    schematic_goal gds_cross_edge_refine: 
      "pre_cross_edge u v s0 s; post_choose_pending u (Some v) s0 s  gds_cross_edge gds u v s  SPEC "
      apply (rule mk_spec_aux[OF cross_edge_spec], assumption)
      apply (rule order_trans[OF _ rwof_step[OF nofail, where s=s0]])
      unfolding gen_step'_def pre_defs gen_cond_def post_choose_pending_def
      apply (simp add: pw_le_iff refine_pw_simps select_def split: option.split, blast) 
      apply simp
      apply blast
      done      

    schematic_goal gds_back_edge_refine: 
      "pre_back_edge u v s0 s; post_choose_pending u (Some v) s0 s  gds_back_edge gds u v s  SPEC "
      apply (rule mk_spec_aux[OF back_edge_spec], assumption)
      apply (rule order_trans[OF _ rwof_step[OF nofail, where s=s0]])
      unfolding gen_step'_def pre_defs gen_cond_def post_choose_pending_def
      apply (simp add: pw_le_iff refine_pw_simps select_def split: option.split, blast) 
      apply simp
      apply blast
      done      

    schematic_goal gds_discover_refine: 
      "pre_discover u v s0 s; post_choose_pending u (Some v) s0 s  gds_discover gds u v s  SPEC "
      apply (rule mk_spec_aux[OF discover_spec], assumption)
      apply (rule order_trans[OF _ rwof_step[OF nofail, where s=s0]])
      unfolding gen_step'_def pre_defs gen_cond_def post_choose_pending_def
      apply (simp add: pw_le_iff refine_pw_simps select_def split: option.split, blast) 
      apply simp
      apply blast
      done      
  end

  
  lemma rec_impl_aux: " xdDomain P   P - {y} × (succ y - ita) - {(y, xd)} - {xd} × UNIV =
           P - insert (y, xd) ({y} × (succ y - ita))"
    apply auto
    done
           

  lemma rec_impl: "rec_impl  gen_dfs"
    apply (rule le_nofailI)
    apply (rule order_trans[OF _ gen_dfs'_refine])
    unfolding gen_dfs'_def
    apply (rule WHILE_refine_rwof)
    unfolding rec_impl_def
    apply (refine_rcg refine_vcg
      order_trans[OF gds_init_refine]
      order_trans[OF gds_choose_pending_refine]
      order_trans[OF gds_new_root_refine]
      order_trans[OF gds_finish_refine]
      order_trans[OF gds_back_edge_refine]
      order_trans[OF gds_cross_edge_refine]
      order_trans[OF gds_discover_refine]
    )
    apply (simp_all split: if_split_asm)

    using [[goals_limit = 1]]

    apply (auto simp add: pre_defs; fail)
    apply (auto simp add: pre_defs gen_rwof'_imp_rwof; fail)
    apply (auto; fail)
    apply (auto dest: reachable_invar; fail)
    apply (auto simp add: pre_defs gen_rwof'_imp_rwof; fail)
    apply (auto; fail)
    apply (auto; fail)

    apply ((drule pcp_imp_pgp, auto simp add: pre_defs gen_rwof'_imp_rwof); fail)

    apply (auto simp: post_choose_pending_def; fail)
    apply (auto simp: post_choose_pending_def; fail)
    apply (auto simp: post_choose_pending_def; fail)

    apply ((drule pcp_imp_pgp, auto simp add: pre_defs gen_rwof'_imp_rwof); fail)

    apply (auto simp: post_choose_pending_def; fail)
    apply (auto simp: post_choose_pending_def; fail)
    apply (auto simp: post_choose_pending_def; fail)

    apply ((drule pcp_imp_pgp, auto simp add: pre_defs gen_rwof'_imp_rwof); fail)

    apply (rule order_trans)
    apply rprems
    apply (auto; fail) []
    subgoal 
      apply (rule SPEC_rule)
      apply (simp add: post_choose_pending_def gen_rwof'_imp_rwof
        split: if_split_asm)
      apply (clarsimp 
        simp: gen_rwof'_imp_rwof Un_Diff
        split: if_split_asm) []
      apply (clarsimp simp: it_step_insert_iff neq_Nil_conv)
      apply (rule conjI)
      subgoal
        apply (rule rec_impl_aux)
        apply (drule reachable_invar)+
        apply (metis Domain.cases SigmaD1 mem_Collect_eq rev_subsetD)
      done
      subgoal
        apply (rule conjI)
        apply auto []
        apply (metis order_trans)
      done
    done

    apply (auto simp add: pre_defs gen_rwof'_imp_rwof; fail)
    apply (auto; fail)
    apply (auto dest: reachable_invar; fail)

    apply ((drule pcp_imp_pgp, auto simp add: pre_defs gen_rwof'_imp_rwof); fail)

    apply (auto simp: post_choose_pending_def; fail)
    apply (auto simp: post_choose_pending_def; fail)
    apply (auto simp: post_choose_pending_def; fail)

    apply (auto; fail)

    apply (auto simp: gen_cond_def; fail)

    apply (auto simp: gen_cond_def; fail)
    done

end

end