Theory Nested_DFS

section ‹Nested DFS›
theory Nested_DFS
imports DFS_Find_Path
begin
text ‹Nested DFS is a standard method for Buchi-Automaton emptiness check.›

subsection ‹Auxiliary Lemmas›

lemma closed_restrict_aux:
  assumes CL: "E``F  F  S"
  assumes NR: "E*``U  S = {}"
  assumes SS: "U  F"
  shows "E*``U  F"
  ― ‹Auxiliary lemma to show that nodes reachable from a finished node must  
      be finished if, additionally, no stack node is reachable›
proof clarify
  fix u v
  assume A: "(u,v)E*" "uU"
  hence M: "E*``{u}  S = {}" "uF" using NR SS by blast+

  from A(1) M show "vF"
    apply (induct rule: converse_rtrancl_induct) 
    using CL apply (auto dest: rtrancl_Image_advance_ss) 
    done

qed


subsection ‹Instantiation of the Framework›
record 'v blue_dfs_state = "'v state" +
  lasso :: "('v list × 'v list) option" (* pr × pl *)
  red  :: "'v set"

type_synonym 'v blue_dfs_param = "('v, ('v,unit) blue_dfs_state_ext) parameterization"

lemma lasso_more_cong[cong]:"state.more s = state.more s'  lasso s = lasso s'"
  by (cases s, cases s') simp
lemma red_more_cong[cong]: "state.more s = state.more s'  red s = red s'"
  by (cases s, cases s') simp

lemma [simp]: "s state.more :=  lasso = foo, red = bar   = s  lasso := foo, red := bar "
  by (cases s) simp


abbreviation "dropWhileNot v  dropWhile ((≠) v)"
abbreviation "takeWhileNot v  takeWhile ((≠) v)"

locale BlueDFS_defs = graph_defs G
  for G :: "('v, 'more) graph_rec_scheme"  +
  fixes accpt :: "'v  bool"
begin

  definition "blue s  dom (finished s) - red s"
  definition "cyan s  set (stack s)"
  definition "white s  - dom (discovered s)"

  abbreviation "red_dfs R ss x  find_path1_restr_spec (G  g_V0 := {x} ) ss R"

  definition mk_blue_witness 
    :: "'v blue_dfs_state  'v fpr_result  ('v,unit) blue_dfs_state_ext"
    where
    "mk_blue_witness s redS  case redS of
                 Inl R'   lasso = None, red = (R' ⌦‹∪ red s›) 
               | Inr (vs, v)  let rs = rev (stack s) in 
                              lasso = Some (rs, vs@dropWhileNot v rs), red = red s"

  definition run_red_dfs 
    :: "'v  'v blue_dfs_state  ('v,unit) blue_dfs_state_ext nres" 
    where
    "run_red_dfs u s  case lasso s of None  do {
             redS  red_dfs (red s) (λx. x = u  x  cyan s) u;
             RETURN (mk_blue_witness s redS)
           }
          | _  NOOP s"

  text ‹ Schwoon-Esparza extension ›
  definition "se_back_edge u v s  case lasso s of
    None  
      ― ‹it's a back edge, so u› and v› are both on stack›
      ― ‹we differentiate whether u› or v› is the 'culprit'›
      ― ‹to generate a better counter example›
      if accpt u then
         let rs = rev (tl (stack s));
             ur = rs;
             ul = u#dropWhileNot v rs
         in RETURN lasso = Some (ur,ul), red = red s
      else if accpt v then
         let rs = rev (stack s);
             vr = takeWhileNot v rs;
             vl = dropWhileNot v rs
         in RETURN lasso = Some (vr,vl), red = red s
      else NOOP s
  | _  NOOP s"

  definition blue_dfs_params :: "'v blue_dfs_param"
    where "blue_dfs_params = 
    on_init = RETURN  lasso = None, red = {} ,
    on_new_root = λv0 s. NOOP s,
    on_discover = λu v s. NOOP s,
    on_finish = λu s. if accpt u then run_red_dfs u s else NOOP s,
    on_back_edge = se_back_edge,
    on_cross_edge = λu v s. NOOP s,
    is_break = λs. lasso s  None "
  
  schematic_goal blue_dfs_params_simps[simp]:
    "on_init blue_dfs_params = ?OI"
    "on_new_root blue_dfs_params = ?ONR"
    "on_discover blue_dfs_params = ?OD"
    "on_finish blue_dfs_params = ?OF"
    "on_back_edge blue_dfs_params = ?OBE"
    "on_cross_edge blue_dfs_params = ?OCE"
    "is_break blue_dfs_params = ?IB"
    unfolding blue_dfs_params_def gen_parameterization.simps
    by (rule refl)+


  sublocale param_DFS_defs G blue_dfs_params 
    by unfold_locales

end

locale BlueDFS = BlueDFS_defs G accpt + param_DFS G blue_dfs_params
  for G :: "('v, 'more) graph_rec_scheme" and accpt :: "'v  bool"

lemma BlueDFSI: 
  assumes "fb_graph G" 
  shows "BlueDFS G"
proof -
  interpret fb_graph G by fact
  show ?thesis by unfold_locales
qed

locale BlueDFS_invar = BlueDFS +
  DFS_invar where param = blue_dfs_params

context BlueDFS_defs begin

lemma BlueDFS_invar_eq[simp]:
  shows "DFS_invar G blue_dfs_params s  BlueDFS_invar G accpt s" 
proof
  assume "DFS_invar G blue_dfs_params s"
  interpret DFS_invar G blue_dfs_params s by fact
  show "BlueDFS_invar G accpt s" by unfold_locales
next
  assume "BlueDFS_invar G accpt s"
  then interpret BlueDFS_invar G accpt s .
  show "DFS_invar G blue_dfs_params s" by unfold_locales
qed

end

subsection ‹Correctness Proof›
context BlueDFS begin

  definition "blue_basic_invar s  
    case lasso s of
      None  restr_invar E (red s) (λx. xset (stack s)) 
         red s  dom (finished s)
    | Some l  True"

  lemma (in BlueDFS_invar) red_DFS_precond_aux:
    assumes BI: "blue_basic_invar s"
    assumes [simp]: "lasso s = None"
    assumes SNE: "stack s  []"
    shows
      "fb_graph (G  g_V0 := {hd (stack s)} )"
    and "fb_graph (G  g_E := E  UNIV × - red s, g_V0 := {hd (stack s)} )" 
    and "restr_invar E (red s) (λx. x  set (stack s))"
    using stack_reachable stack s  [] 
    apply (rule_tac fb_graph_subset, auto) []
    using stack_reachable stack s  [] 
    apply (rule_tac fb_graph_subset, auto) []

    using BI apply (simp add: blue_basic_invar_def)
    done

  lemma (in BlueDFS_invar) red_dfs_pres_bbi:
    assumes BI: "blue_basic_invar s"
    assumes [simp]: "lasso s = None" and SNE: "stack s  []"
    assumes "pending s `` {hd (stack s)} = {}"
    shows "run_red_dfs (hd (stack s)) (finish (hd (stack s)) s) n
      SPEC (λe. 
        DFS_invar G blue_dfs_params (finish (hd (stack s)) sstate.more := e) 
         blue_basic_invar (finish (hd (stack s)) sstate.more := e))"
  proof -
    have [simp]: "(λx. x = hd (stack s)  x  cyan (finish (hd (stack s)) s)) = 
      (λx. xset (stack s))"
      using stack s  []
      unfolding finish_def cyan_def by (auto simp: neq_Nil_conv)

    show ?thesis
      unfolding run_red_dfs_def
      apply simp
      apply (refine_vcg)
      apply simp
    proof -
      fix fp1
      define s' where "s' = finish (hd (stack s)) s"
      assume FP_spec: 
        "find_path1_restr_pred (G  g_V0 := {hd (stack s)} ) (λx. x  set (stack s)) (red s) fp1"
      assume "BlueDFS_invar G accpt (s'state.more := mk_blue_witness s' fp1)"      
      then interpret i: BlueDFS_invar G accpt "(s'state.more := mk_blue_witness s' fp1)"
        by simp
      
      have [simp]: 
        "red s' = red s" 
        "discovered s' = discovered s" 
        "dom (finished s') = insert (hd (stack s)) (dom (finished s))"
        unfolding s'_def finish_def by auto

      {
        fix R'
        assume [simp]: "fp1 = Inl R'"
        from FP_spec[unfolded find_path1_restr_pred_def, simplified] 
        have 
          R'FMT: "R' = red s  E+ `` {hd (stack s)}" 
          and RI: "restr_invar E R' (λx. x  set (stack s))" 
          by auto

        from BI have "red s  dom (finished s)"
          unfolding blue_basic_invar_def by auto
        also have "E+ `` {hd (stack s)}  dom (finished s)"
        proof (intro subsetI, elim ImageE, simp)
          fix v
          assume "(hd (stack s),v)E+"

          then obtain u where "(hd (stack s),u)E" and "(u,v)E*" 
            by (auto simp: trancl_unfold_left)

          from RI have NR: "E+ `` {hd (stack s)}  set (stack s) = {}"
            unfolding restr_invar_def by (auto simp: R'FMT)

          with (hd (stack s),u)E have "uset (stack s)" by auto
          with i.finished_closed[simplified] (hd (stack s),u)E 
          have UID: "udom (finished s)" by (auto simp: stack_set_def)

          from NR (hd (stack s),u)E have NR': "E*``{u}  set (stack s) = {}"
            by (auto simp: trancl_unfold_left)

          have CL: "E `` dom (finished s)  dom (finished s)  set (stack s)"
            using finished_closed discovered_eq_finished_un_stack
            by simp

          from closed_restrict_aux[OF CL NR'] UID 
          have "E* `` {u}  dom (finished s)" by simp
          with (u,v)E*  show "v  dom (finished s)" by auto
        qed
        finally (sup_least) 
        have "R'  dom (finished s)  red s  dom (finished s)" 
          by (simp add: R'FMT)
      } note aux1 = this

      show "blue_basic_invar (s'state.more := mk_blue_witness s' fp1)"
        unfolding blue_basic_invar_def mk_blue_witness_def
        apply (simp split: option.splits sum.splits)
        apply (intro allI conjI impI)

        using FP_spec SNE
        apply (auto 
          simp: s'_def blue_basic_invar_def find_path1_restr_pred_def
          simp: restr_invar_def
          simp: neq_Nil_conv) []

        apply (auto dest!: aux1) []
        done
    qed
  qed

  lemma blue_basic_invar: "is_invar blue_basic_invar"
  proof (induct rule: establish_invarI)
    case (finish s) then interpret BlueDFS_invar where s=s by simp

    have [simp]: "(λx. x = hd (stack s)  x  cyan (finish (hd (stack s)) s)) = 
      (λx. xset (stack s))"
      using stack s  []
      unfolding finish_def cyan_def by (auto simp: neq_Nil_conv)

    from finish show ?case 
      apply (simp)
      apply (intro conjI impI)
      apply (rule leof_trans[OF red_dfs_pres_bbi], assumption+, simp)

      apply (auto simp: restr_invar_def blue_basic_invar_def neq_Nil_conv) []
      done
  qed (auto simp: blue_basic_invar_def cond_def se_back_edge_def
            simp: restr_invar_def empty_state_def pred_defs
            simp: DFS_invar.discovered_eq_finished_un_stack
            simp del: BlueDFS_invar_eq
            split: option.splits)

  lemmas (in BlueDFS_invar) s_blue_basic_invar 
    = blue_basic_invar[THEN make_invar_thm]

  lemmas (in BlueDFS_invar) red_DFS_precond 
    = red_DFS_precond_aux[OF s_blue_basic_invar]

  sublocale DFS G blue_dfs_params
    apply unfold_locales
    
    apply (clarsimp_all 
      simp:  se_back_edge_def run_red_dfs_def refine_pw_simps pre_on_defs
      split: option.splits)
    
    unfolding nofail_SPEC_iff
    apply (refine_vcg)
      apply (erule BlueDFS_invar.red_DFS_precond, auto) []

      apply (simp add: cyan_def finish_def)
      apply (erule BlueDFS_invar.red_DFS_precond, auto) []

      apply (rule TrueI)
    done

end

context BlueDFS_invar
begin

  context assumes [simp]: "lasso s = None"
  begin
    lemma red_closed:
      "E `` red s  red s"
      using s_blue_basic_invar
      unfolding blue_basic_invar_def restr_invar_def
      by simp
  
    lemma red_stack_disjoint:
      "set (stack s)  red s = {}"
      using s_blue_basic_invar
      unfolding blue_basic_invar_def restr_invar_def
      by auto
    
    lemma red_finished: "red s  dom (finished s)"
      using s_blue_basic_invar
      unfolding blue_basic_invar_def
      by auto

    (* Play of Colors *)
    lemma all_nodes_colored: "white s  blue s  cyan s  red s = UNIV "  
      unfolding white_def blue_def cyan_def
      by (auto simp: stack_set_def)

    lemma colors_disjoint:
      "white s  (blue s  cyan s  red s) = {}"
      "blue s  (white s  cyan s  red s) = {}"
      "cyan s  (white s  blue s  red s) = {}"
      "red s  (white s  blue s  cyan s) = {}"
      unfolding white_def blue_def cyan_def
      using finished_discovered red_finished
      unfolding stack_set_def
      by blast+
  
  end

  lemma (in BlueDFS) i_no_accpt_cyle_in_finish:
    "is_invar (λs. lasso s = None  (x. accpt x  x  dom (finished s)  (x,x)  E+))"
  proof (induct rule: establish_invarI)
    case (finish s s' u) then interpret BlueDFS_invar where s=s by simp

    let ?onstack = "λx. xset (stack s)"
    let ?rE = "rel_restrict E (red s)"

    from finish obtain sh st where [simp]: "stack s = sh#st" 
      by (auto simp: neq_Nil_conv)

    have 1: "g_E (G  g_V0 := {hd (stack s)} ) = E" by simp

    { (* TODO/FIXME: Ughly proof structure! *)
      fix R'::"'v set"
      let ?R' = "R' ⌦‹∪ red s›"
      let ?s = "s' lasso := None, red := ?R'"

      assume "v. (hd (stack s), v)  ?rE+  ¬ ?onstack v"
      and accpt: "accpt u"
      and NL[simp]: "lasso s = None"
      hence no_hd_cycle: "(hd (stack s), hd (stack s))  ?rE+"
        by auto

      from finish have "stack s  []" by simp
      from hd_in_set[OF this] have "hd (stack s)  red s" 
        using red_stack_disjoint
        by auto
      hence "(hd (stack s),hd (stack s))  E+"
        using no_hd_cycle rel_restrict_tranclI red_closed[OF NL]
        by metis 
      with accpt finish have 
        "x. accpt x  x  dom (finished ?s)  (x,x)  E+"
        by auto
    } with finish have
      "red_dfs (red s) ?onstack (hd (stack s))
          SPEC (λx. R. x = Inl R 
             DFS_invar G blue_dfs_params (lasso_update Map.empty s'red := R ⌦‹∪ red s›) 
             (x. accpt x  xdom (finished s')  (x, x)  E+))"
      apply -
      apply (rule find_path1_restr_spec_rule, intro conjI)

      apply (rule red_DFS_precond, simp_all) []
      unfolding 1
      apply (rule red_DFS_precond, simp_all) []

      apply (auto simp: find_path1_restr_pred_def restr_invar_def) 
      done
    note aux = leof_trans[OF this[simplified,THEN leof_lift]]

    note [refine_vcg del] = find_path1_restr_spec_rule

    from finish show ?case
      apply simp
      apply (intro conjI impI)
        unfolding run_red_dfs_def mk_blue_witness_def cyan_def
        apply clarsimp
        apply (refine_vcg aux)
        apply (auto split: sum.splits)
      done
  next
    case back_edge thus ?case
      by (simp add: se_back_edge_def split: option.split)
  qed simp_all

  lemma no_accpt_cycle_in_finish:
    "lasso s = None; accpt v; v  dom (finished s)  (v,v)  E+"
    using i_no_accpt_cyle_in_finish[THEN make_invar_thm]
    by blast

end

context BlueDFS
begin
  definition lasso_inv where
    "lasso_inv s  pr pl. lasso s = Some (pr,pl)  
                                      pl  []
                                     (v0V0. path E v0 pr (hd pl)) 
                                     accpt (hd pl) 
                                     path E (hd pl) pl (hd pl)"

  lemma (in BlueDFS_invar) se_back_edge_lasso_inv:
    assumes b_inv: "lasso_inv s"
    and ne: "stack s  []"
    and R: "lasso s = None"
    and p:"(hd (stack s), v)  pending s"
    and v: "v  dom (discovered s)" "v  dom (finished s)"
    and s': "s' = back_edge (hd (stack s)) v (spending := pending s - {(u,v)})"
    shows "se_back_edge (hd (stack s)) v s'
                 SPEC (λe. DFS_invar G blue_dfs_params (s'state.more := e) 
                            lasso_inv (s'state.more := e))"
  proof -

    from v stack_set_def have v_in: "v  set (stack s)" by simp
    from p have uv_edg: "(hd (stack s), v)  E" by (auto dest: pendingD)
  
    {
      assume accpt: "accpt (hd (stack s))"
      let ?ur = "rev (tl (stack s))"
      let ?ul = "hd (stack s)#dropWhileNot v (rev (tl (stack s)))"
      let ?s = "s'lasso := Some (?ur, ?ul), red := red s"

      assume "DFS_invar G blue_dfs_params ?s"

      have [simp]: "stack ?s = stack s"
        by (simp add: s')

      have hd_ul[simp]: "hd ?ul = hd (stack s)" by simp

      have "?ul  []" by simp

      moreover have P:"v0V0. path E v0 ?ur (hd ?ul)"
        using stack_is_path[OF ne]
        by auto
      moreover
      from accpt have "accpt (hd ?ul)" by simp

      moreover have "path E (hd ?ul) ?ul (hd ?ul)"
      proof (cases "v = hd (stack s)")
        case True 
        with distinct_hd_tl stack_distinct have ul: "?ul = [hd (stack s)]" 
          by force
        from True uv_edg show ?thesis
          by (subst ul)+ (simp add: path1)
      next
        case False with v_in ne have "v  set ?ur"
          by (auto simp add: neq_Nil_conv)
        with P show ?thesis
          by (fastforce intro: path_prepend 
                           dropWhileNot_path[where p="?ur"]
                           uv_edg)
      qed

      ultimately have "lasso_inv ?s" by (simp add: lasso_inv_def)
    }

    moreover
    {
      assume accpt: "accpt v"
      let ?vr = "takeWhileNot v (rev (stack s))"
      let ?vl = "dropWhileNot v (rev (stack s))"
      let ?s = "s'lasso := Some(?vr, ?vl), red := red s"

      assume "DFS_invar G blue_dfs_params ?s"
      
      have [simp]: "stack ?s = stack s"
        by (simp add: s')
      
      from ne v_in have hd_vl[simp]: "hd ?vl = v"
        by (induct ("stack s") rule: rev_nonempty_induct) auto
      
      from v_in have "?vl  []" by simp
      
      moreover from hd_succ_stack_is_path[OF ne] uv_edg have 
        P: "v0V0. path E v0 (rev (stack s)) v" 
        by auto
      with ne v_in have "v0V0. path E v0 ?vr (hd ?vl)"
        by (force intro: takeWhileNot_path)

      moreover from accpt have "accpt (hd ?vl)" by simp

      moreover from P ne v_in have "path E (hd ?vl) ?vl (hd ?vl)"
        by (force intro: dropWhileNot_path)

      ultimately have "lasso_inv ?s" by (simp add: lasso_inv_def)
    }

    moreover
    {
      assume "¬ accpt (hd (stack s))" "¬ accpt v"
      let ?s = "s'state.more := state.more s'"
      
      assume "DFS_invar G blue_dfs_params ?s"
      
      from assms have "lasso_inv ?s"
        by (auto simp add: lasso_inv_def)
    }

    (* TODO: Clean up this proof, separate logical arguments from framework 
      boilerplate! *)
    ultimately show ?thesis
      using R s'
      unfolding se_back_edge_def
      by (auto split: option.splits)
  qed


  lemma lasso_inv:
    "is_invar lasso_inv"
  proof (induct rule: establish_invarI)
    case (finish s s' u) then interpret BlueDFS_invar where s=s by simp
    (* TODO/FIXME: Ughly proof structure *)
    let ?onstack = "λx. x  set (stack s)"
    let ?rE = "rel_restrict E (red s)"
    let ?revs = "rev (tl (stack s))"
    
    note ne = stack s  []

    note [simp] = u=hd (stack s)

    from finish have [simp]: 
      "x. x = hd (stack s)  x  set (stack s')  xset (stack s)"
      "red s' = red s"
      "lasso s' = lasso s"
      by (auto simp: neq_Nil_conv)


    {
      fix v vs
      let ?cyc = "vs @ dropWhileNot v ?revs"
      let ?s = "s'lasso := Some (?revs, ?cyc), red := red s"
      
      assume "DFS_invar G blue_dfs_params ?s"
        and vs: "vs  []" "path ?rE (hd (stack s)) vs v"
        and v: "?onstack v"
        and accpt: "accpt (hd (stack s))"
      from vs have P: "path E (hd (stack s)) vs v"
        by (metis path_mono rel_restrict_sub)
      
      have hds[simp]: "hd vs = hd (stack s)" "hd ?cyc = hd (stack s)"
        using vs path_hd
        by simp_all
      
      from vs have "?cyc  []" by simp
      
      moreover have P0: "v0V0. path E v0 ?revs (hd ?cyc)"
        using stack_is_path[OF ne]
        by auto

      moreover from accpt have "accpt (hd ?cyc)" by simp

      moreover have "path E (hd ?cyc) ?cyc (hd ?cyc)"
      proof (cases "tl (stack s) = []")
        case True with ne last_stack_in_V0 obtain v0 where "v0  V0"
          and [simp]: "stack s = [v0]" 
          by (auto simp: neq_Nil_conv)
        with v True finish have [simp]: "v = v0" by simp
        
        from True P show ?thesis by simp
      next
        case False note tl_ne = this

        show ?thesis
        proof (cases "v = hd (stack s)")
          case True hence "v  set ?revs"
            using ne stack_distinct by (auto simp: neq_Nil_conv)
          hence "?cyc = vs" by fastforce
          with P True show ?thesis by (simp del: dropWhile_eq_Nil_conv)
        next
          case False with finish v have "v  set ?revs" 
            by (auto simp: neq_Nil_conv)
          with tl_ne False P0  show ?thesis 
            by (force intro: path_conc[OF P] 
                dropWhileNot_path[where p="?revs"])
        qed
      qed
      
      ultimately have "lasso_inv ?s" by (simp add: lasso_inv_def)
    }
    hence "accpt (hd (stack s))  lasso s = None 
            red_dfs (red s) ?onstack (hd (stack s))  SPEC (λrs. vs v. 
                rs = Inr (vs,v) 
                  DFS_invar G blue_dfs_params (s'lasso := Some (?revs, vs @ dropWhileNot v ?revs), red:= red s) 
                   lasso_inv (s'lasso := Some (?revs, vs @ dropWhileNot v ?revs), red:=red s))"
      apply clarsimp
      apply (rule find_path1_restr_spec_rule, intro conjI)
      apply (rule red_DFS_precond, simp_all add: ne) []
      apply (simp, rule red_DFS_precond, simp_all add: ne) []

      using red_stack_disjoint ne

      apply clarsimp
      apply rprems
      apply (simp_all add: find_path1_restr_pred_def restr_invar_def)
      apply (fastforce intro: path_restrict_tl rel_restrictI)
      done
    note aux1 = this[rule_format,THEN leof_lift]

    show ?case
      apply simp
      unfolding run_red_dfs_def mk_blue_witness_def cyan_def

      apply (simp 
        add: run_red_dfs_def mk_blue_witness_def cyan_def
        split: option.splits)
      apply (intro conjI impI)
      apply (refine_vcg leof_trans[OF aux1])
      using finish
      apply (auto simp add: lasso_inv_def split: sum.split)
      done
  next
    case (back_edge s s' u v) then interpret BlueDFS_invar where s=s by simp
    
    from back_edge se_back_edge_lasso_inv[THEN leof_lift] show ?case
      by auto
  qed (simp_all add: lasso_inv_def empty_state_def)
end

context BlueDFS_invar
begin
  lemmas s_lasso_inv = lasso_inv[THEN make_invar_thm]

  lemma 
    assumes "lasso s = Some (pr,pl)"
    shows loop_nonempty: "pl  []"
    and accpt_loop: "accpt (hd pl)"
    and loop_is_path: "path E (hd pl) pl (hd pl)"
    and loop_reachable: "v0V0. path E v0 pr (hd pl)"
    using assms s_lasso_inv
    by (simp_all add: lasso_inv_def)

  lemma blue_dfs_correct:
    assumes NC: "¬ cond s"
    shows "case lasso s of
      None  ¬(v0V0. v. (v0,v)  E*  accpt v  (v,v)  E+)
    | Some (pr,pl)  (v0V0. v. 
        path E v0 pr v  accpt v  pl[]  path E v pl v)"
  proof (cases "lasso s")
    case None
    moreover
    {
      fix v v0
      assume "v0  V0" "(v0,v)  E*" "accpt v" "(v,v)  E+"
      moreover
      hence "v  reachable" by (auto)
      with nc_finished_eq_reachable NC None have "v  dom (finished s)"
        by simp
      moreover note no_accpt_cycle_in_finish None
      ultimately have False by blast
    }
    ultimately show ?thesis by auto
  next
    case (Some prpl) with s_lasso_inv show ?thesis
      by (cases prpl) 
         (auto intro: path_is_rtrancl path_is_trancl simp: lasso_inv_def)
  qed

end

subsection ‹Interface›

interpretation BlueDFS_defs for G accpt .

definition "nested_dfs_spec G accpt  λr. case r of
  None  ¬(v0g_V0 G. v. (v0,v)  (g_E G)*  accpt v  (v,v)  (g_E G)+)
| Some (pr,pl)  (v0g_V0 G. v. 
    path (g_E G) v0 pr v  accpt v  pl[]  path (g_E G) v pl v)"

definition "nested_dfs G accpt  do {
  ASSERT (fb_graph G);
  s  it_dfs TYPE('a) G accpt;
  RETURN (lasso s)
}"

theorem nested_dfs_correct: 
  assumes "fb_graph G"
  shows "nested_dfs G accpt  SPEC (nested_dfs_spec G accpt)"
proof -
  interpret fb_graph G by fact
  interpret BlueDFS G accpt by unfold_locales
  
  show ?thesis 
    unfolding nested_dfs_def
    apply (refine_rcg refine_vcg)
    apply fact
    apply (rule weaken_SPEC[OF it_dfs_correct])
    apply clarsimp
  proof -
    fix s
    assume "BlueDFS_invar G accpt s"
    then interpret BlueDFS_invar G accpt s .
    
    assume "¬cond TYPE('b) G accpt s"
    from blue_dfs_correct[OF this] show "nested_dfs_spec G accpt (lasso s)"
      unfolding nested_dfs_spec_def by simp
  qed
qed

subsection ‹Implementation›

record 'v bdfs_state_impl = "'v simple_state" +
  lasso_impl :: "('v list × 'v list) option"
  red_impl :: "'v set"

definition "bdfs_erel  {(lasso_impl=li,red_impl=ri,lasso=l, red=r) 
  | li ri l r. li=l  ri=r}"

abbreviation "bdfs_rel  bdfs_erelsimple_state_rel"

definition mk_blue_witness_impl
  :: "'v bdfs_state_impl  'v fpr_result  ('v,unit) bdfs_state_impl_ext"
  where
  "mk_blue_witness_impl s redS  
    case redS of
      Inl R'   lasso_impl = None, red_impl = (R' ⌦‹∪ red_impl s›) 
    | Inr (vs, v)  let 
        rs = rev (map fst (CAST (ss_stack s))) 
      in  
        lasso_impl = Some (rs, vs@dropWhileNot v rs), 
        red_impl = red_impl s"

lemma mk_blue_witness_impl[refine]:
  " (si,s)bdfs_rel; (ri,r)Id, Idlist_rel ×r Idsum_rel  
   (mk_blue_witness_impl si ri, mk_blue_witness s r)bdfs_erel"
  unfolding mk_blue_witness_impl_def mk_blue_witness_def
  apply parametricity
  apply (cases si, cases s)
  apply (auto simp: bdfs_erel_def simple_state_rel_def) []
  apply (rule introR[where R="Idlist_rel"])
  apply (cases si, cases s)
  apply (auto simp: bdfs_erel_def simple_state_rel_def comp_def) []
  apply (cases si, cases s)
  apply (auto simp: bdfs_erel_def simple_state_rel_def) []
  done

definition "cyan_impl s  on_stack s"
lemma cyan_impl[refine]: "(si,s)bdfs_rel  (cyan_impl si, cyan s)Id"
  unfolding cyan_impl_def cyan_def
  by (auto simp: bdfs_erel_def simple_state_rel_def)

definition run_red_dfs_impl 
  :: "('v, 'more) graph_rec_scheme  'v  'v bdfs_state_impl  ('v,unit) bdfs_state_impl_ext nres" 
  where
  "run_red_dfs_impl G u s  case lasso_impl s of None  do {
           redS  red_dfs TYPE('more) G (red_impl s) (λx. x = u  x  cyan_impl s) u;
           RETURN (mk_blue_witness_impl s redS)
         }
        | _  RETURN (simple_state.more s)"

  lemma run_red_dfs_impl[refine]: "(Gi,G)Id; (ui,u)Id; (si,s)bdfs_rel 
     run_red_dfs_impl Gi ui si bdfs_erel (run_red_dfs TYPE('a) G u s)"
    unfolding run_red_dfs_impl_def run_red_dfs_def
    apply refine_rcg
    apply refine_dref_type
    apply (cases si, cases s, auto simp: bdfs_erel_def simple_state_rel_def) []
    apply (cases si, cases s, 
      auto simp: bdfs_erel_def simple_state_rel_def cyan_impl_def cyan_def) []
    apply (auto simp: bdfs_erel_def simple_state_rel_def) [2]
    done

  definition "se_back_edge_impl accpt u v s  case lasso_impl s of
    None  
      if accpt u then
         let rs = rev (map fst (tl (CAST (ss_stack s))));
             ur = rs;
             ul = u#dropWhileNot v rs
         in RETURN lasso_impl = Some (ur,ul), red_impl = red_impl s
      else if accpt v then
         let rs = rev (map fst (CAST (ss_stack s)));
             vr = takeWhileNot v rs;
             vl = dropWhileNot v rs
         in RETURN lasso_impl = Some (vr,vl), red_impl = red_impl s
      else RETURN (simple_state.more s)
  | _  RETURN (simple_state.more s)"

  lemma se_back_edge_impl[refine]: " (accpti,accpt)Id; (ui,u)Id; (vi,v)Id; (si,s)bdfs_rel  
     se_back_edge_impl accpt ui vi si bdfs_erel (se_back_edge accpt u v s)"
    unfolding se_back_edge_impl_def se_back_edge_def
    apply refine_rcg
    apply refine_dref_type  
    apply simp_all
    apply (simp_all add: bdfs_erel_def simple_state_rel_def)
    apply (cases si, cases s, (auto) [])
    apply (cases si, cases s, (auto simp: map_tl comp_def) [])
    apply (cases si, cases s, (auto simp: comp_def) [])
    done


  lemma NOOP_impl: "(si, s)  bdfs_rel 
     RETURN (simple_state.more si)   bdfs_erel (NOOP s)"
    apply (simp add: pw_le_iff refine_pw_simps)
    apply (auto simp: simple_state_rel_def)
    done
    

  definition bdfs_params_impl 
    :: "('v, 'more) graph_rec_scheme  ('v  bool)  ('v,'v bdfs_state_impl,('v,unit)bdfs_state_impl_ext) gen_parameterization"
  where "bdfs_params_impl G accpt  
    on_init = RETURN lasso_impl = None, red_impl = {},
    on_new_root = λv0 s. RETURN (simple_state.more s),
    on_discover = λu v s. RETURN (simple_state.more s),
    on_finish = λu s. 
      if accpt u then run_red_dfs_impl G u s else RETURN (simple_state.more s),
    on_back_edge = se_back_edge_impl accpt,
    on_cross_edge = λu v s. RETURN (simple_state.more s),
    is_break = λs. lasso_impl s  None "

  lemmas bdfs_params_impl_simps[simp, DFS_code_unfold] = 
    gen_parameterization.simps[mk_record_simp, OF bdfs_params_impl_def]  


  interpretation impl: simple_impl_defs G "bdfs_params_impl G accpt" "blue_dfs_params TYPE('a) G accpt"
    for G accpt .

context BlueDFS begin

  sublocale impl: simple_impl G blue_dfs_params "bdfs_params_impl G accpt" bdfs_erel
    apply unfold_locales

    apply (simp_all 
      add: bdfs_params_impl_def run_red_dfs_impl se_back_edge_impl NOOP_impl)
    apply parametricity
    apply (clarsimp_all simp: pw_le_iff refine_pw_simps bdfs_erel_def simple_state_rel_def)
    apply (rename_tac si s x y, case_tac si, case_tac s)
    apply (auto simp add: bdfs_erel_def simple_state_rel_def) []
    done

  lemmas impl = impl.simple_tailrec_refine
end

definition "nested_dfs_impl G accpt  do {
  ASSERT (fb_graph G);
  s  impl.tailrec_impl TYPE('a) G accpt;
  RETURN (lasso_impl s)
}"

lemma nested_dfs_impl[refine]: 
  assumes "(Gi,G)Id"
  assumes "(accpti,accpt)Id"
  shows "nested_dfs_impl Gi accpti (Idlist_rel ×r Idlist_reloption_rel) 
    (nested_dfs G accpt)"
  using assms
  unfolding nested_dfs_impl_def nested_dfs_def
  apply refine_rcg
  apply simp_all
  apply (rule intro_prgR[where R=bdfs_rel])
  defer
  apply (rename_tac si s)
  apply (case_tac si, case_tac s)
  apply (auto simp: bdfs_erel_def simple_state_rel_def) []
proof -
  assume "fb_graph G"
  then interpret fb_graph G .
  interpret BlueDFS G by unfold_locales

  from impl show "impl.tailrec_impl TYPE('b) G accpt bdfs_rel (it_dfs TYPE('b) G accpt)" .
qed 

subsection ‹Synthesis of Executable Code›
(* Straightforward autoref implementation *)
record ('v,'si,'nsi)bdfs_state_impl' = "('si,'nsi)simple_state_impl" +
  lasso_impl' :: "('v list × 'v list) option"
  red_impl' :: 'nsi

definition [to_relAPP]: "bdfs_state_erel' Vi  {
  (lasso_impl' = li, red_impl'=ri,lasso_impl = l, red_impl = r) | li ri l r.
    (li,l)Vilist_rel ×r Vilist_reloption_rel  (ri,r)Vidflt_ahs_rel}"

consts 
  i_bdfs_state_ext :: "interface  interface"

lemmas [autoref_rel_intf] = REL_INTFI[of bdfs_state_erel' i_bdfs_state_ext]

lemma [autoref_rules]:
  fixes ns_rel vis_rel Vi
  defines "R  ns_rel,vis_rel,Vibdfs_state_erel'ss_impl_rel"
  shows 
    "(bdfs_state_impl'_ext, bdfs_state_impl_ext) 
       Vilist_rel ×r Vilist_reloption_rel  Vidflt_ahs_rel  unit_rel  Vibdfs_state_erel'"
    "(lasso_impl', lasso_impl)  R  Vilist_rel ×r Vilist_reloption_rel"
    "(red_impl', red_impl)  R  Vidflt_ahs_rel"
  unfolding bdfs_state_erel'_def ss_impl_rel_def R_def
  by auto

schematic_goal nested_dfs_code:
  assumes Vid: "V = (Id :: ('v::hashable × 'v) set)"
  assumes [unfolded Vid, autoref_rules]:
    "(Gi, G)  Rm, Vg_impl_rel_ext"
    "(accpti,accpt)  (V  bool_rel)"
  notes [unfolded Vid, autoref_tyrel] = 
    TYRELI[where R="Vdflt_ahs_rel"]
    TYRELI[where R="Vras_rel"]
  shows "(nres_of ?c, nested_dfs_impl G accpt) 
     Vlist_rel ×r Vlist_reloption_relnres_rel"
  unfolding nested_dfs_impl_def[abs_def] Vid 
    se_back_edge_impl_def run_red_dfs_impl_def mk_blue_witness_impl_def
    cyan_impl_def
    DFS_code_unfold
  (*apply (subst aux1)*)
  using [[autoref_trace_failed_id]]
  apply (autoref_monadic (trace))
  done

concrete_definition nested_dfs_code uses nested_dfs_code

export_code nested_dfs_code checking SML

subsection ‹Conclusion›
text ‹
  We have implemented an efficiently executable nested DFS algorithm.
  The following theorem declares this implementation to the Autoref tool,
  such that it uses it to synthesize efficient code for @{const nested_dfs}.
  Note that you will need the lemma @{thm [source] nested_dfs_correct} to link
  @{const nested_dfs} to an abstract specification, which is usually done in 
  a previous refinement step.
›
theorem nested_dfs_autoref[autoref_rules]:
  assumes "PREFER_id V"
  shows "(λ G accpt. nres_of (nested_dfs_code G accpt),nested_dfs) 
    Rm, Vg_impl_rel_ext  (V  bool_rel) 
    Vlist_rel ×r Vlist_reloption_relnres_rel"
proof -
  from assms have Vid: "V=Id" by simp
  note nested_dfs_code.refine[OF Vid,param_fo, THEN nres_relD]
  also note nested_dfs_impl
  finally show ?thesis by (fastforce intro: nres_relI)
qed


end