Theory Undirected_Graph_Impl

section ‹Implementation of Weighted Undirected Graph by Map›
theory Undirected_Graph_Impl
imports 
  "HOL-Data_Structures.Map_Specs"
  Common
  Undirected_Graph_Specs
begin

subsection ‹Doubleton Set to Pair›
definition "epair e = (if card e = 2 then Some (SOME (u,v). e={u,v}) else None)"

lemma epair_eqD: "epair e = Some (x,y)  (xy  e={x,y})"
  apply (cases "card e = 2")
  unfolding epair_def
  apply simp_all
  apply (clarsimp simp: card_Suc_eq eval_nat_numeral doubleton_eq_iff)
  by (smt case_prodD case_prodI someI)

lemma epair_not_sng[simp]: "epair e  Some (x,x)"  
  by (auto dest: epair_eqD)

lemma epair_None[simp]: "epair {a,b} = None  a=b" 
  unfolding epair_def by (auto simp: card2_eq) 
  
subsection ‹Generic Implementation›  

text ‹
  When instantiated with a map ADT, this locale provides a weighted graph ADT.
›
locale wgraph_by_map = 
  M: Map M_empty M_update M_delete M_lookup M_invar 
  
  for M_empty M_update M_delete 
  and M_lookup :: "'m  'v  (('v×nat) list) option" 
  and M_invar 
begin

definition "αnodes_aux g  dom (M_lookup g)"

definition "αedges_aux g 
   ({(u,v). xs d. M_lookup g u = Some xs  (v,d)set xs })"


definition "αg g  graph (αnodes_aux g) (αedges_aux g)"

definition "αw g e  case epair e of 
  Some (u,v)  (
    case M_lookup g u of 
      None  0 
    | Some xs  the_default 0 (map_of xs v)
    )
| None  0"

definition invar :: "'m  bool" where
  "invar g  
      M_invar g  finite (dom (M_lookup g))
     (u xs. M_lookup g u = Some xs  
          distinct (map fst xs) 
         uset (map fst xs)
         ((v,d)set xs. (u,d)set (the_default [] (M_lookup g v)))
      )"
  

lemma in_the_default_empty_conv[simp]: 
  "xset (the_default [] m)  (xs. m=Some xs  xset xs)"
  by (cases m) auto  
  
lemma αedges_irrefl: "invar g  irrefl (αedges_aux g)" 
  unfolding invar_def irrefl_def αedges_aux_def
  by (force)
  
lemma αedges_sym: "invar g  sym (αedges_aux g)"  
  unfolding invar_def sym_def αedges_aux_def
  by force 
      
lemma αedges_subset: "invar g  αedges_aux g  αnodes_aux g × αnodes_aux g"
  unfolding invar_def αnodes_aux_def αedges_aux_def
  by force
                      
lemma αnodes_finite[simp, intro!]: "invar g  finite (αnodes_aux g)"
  unfolding invar_def αnodes_aux_def by simp
  
lemma αedges_finite[simp, intro!]: "invar g  finite (αedges_aux g)"
  using finite_subset[OF αedges_subset] by blast
      
definition adj :: "'m  'v  ('v×nat) list" where
  "adj g v = the_default [] (M_lookup g v)"
  
definition empty :: "'m" where "empty = M_empty"

definition add_edge1 :: "'v×'v  nat  'm  'm" where
  "add_edge1  λ(u,v) d g. M_update u ((v,d) # the_default [] (M_lookup g u)) g"

definition add_edge :: "'v×'v  nat  'm  'm" where
  "add_edge  λ(u,v) d g. add_edge1 (v,u) d (add_edge1 (u,v) d g)"
  

lemma edges_αg_aux: "invar g  edges (αg g) = αedges_aux g" 
  unfolding αg_def using αedges_sym αedges_irrefl
  by (auto simp: irrefl_def graph_accs)
 
lemma nodes_αg_aux: "invar g  nodes (αg g) = αnodes_aux g"  
  unfolding αg_def using αedges_subset
  by (force simp: graph_accs)

lemma card_doubleton_eq2[simp]: "card {a,b} = 2  ab" by auto  

lemma the_dflt_Z_eq: "the_default 0 m = d  (m=None  d=0  m=Some d)"
  by (cases m) auto

lemma adj_correct_aux:
  "invar g  set (adj g u) = {(v, d). (u, v)  edges (αg g)  αw g {u, v} = d}"  
  apply (simp add: edges_αg_aux)
  apply safe
  subgoal unfolding adj_def αedges_aux_def by auto
  subgoal for a d 
    unfolding adj_def αw_def
    apply (clarsimp split: prod.splits option.splits simp: the_dflt_Z_eq)
    unfolding invar_def
    by (force dest!: epair_eqD simp: doubleton_eq_iff)+
  subgoal for a 
    unfolding adj_def αw_def
    using αedges_irrefl[of g]
    apply (clarsimp split: prod.splits option.splits)
    apply safe
    subgoal by (auto simp: irrefl_def)
    subgoal 
      apply (clarsimp dest!: epair_eqD simp: doubleton_eq_iff)
      unfolding invar_def αedges_aux_def
      by force
    subgoal  
      apply (clarsimp dest!: epair_eqD simp: doubleton_eq_iff)
      unfolding invar_def αedges_aux_def
      apply clarsimp
      by (smt case_prod_conv map_of_is_SomeI the_default.simps(2))
    done      
  done      
    
lemma invar_empty_aux: "invar empty"  
  by (simp add: invar_def empty_def M.map_specs)

lemma dist_fst_the_dflt_aux: "distinct (map fst (the_default [] m)) 
   (xs. m = Some xs  distinct (map fst xs))"  
  by (cases m; auto)

lemma invar_add_edge_aux: 
  "invar g; (u, v)  edges (αg g); u  v  invar (add_edge (u, v) d g)"  
  apply (simp add: edges_αg_aux)
  unfolding add_edge_def add_edge1_def invar_def αedges_aux_def
  by (auto simp:  M.map_specs dist_fst_the_dflt_aux; force)
  
  
    
sublocale adt_wgraph αw αg invar adj empty add_edge 
  apply unfold_locales
  subgoal by (simp add: adj_correct_aux)
  subgoal by (simp add: invar_empty_aux)
  subgoal
    apply (simp 
        add: graph_eq_iff nodes_αg_aux invar_empty_aux edges_αg_aux
        add: αnodes_aux_def αedges_aux_def
        )
    apply (simp add: empty_def M.map_specs)
    done
  subgoal
    unfolding αw_def
    by (auto simp: empty_def M.map_specs fun_eq_iff split: option.splits)    
  subgoal by (simp add: invar_add_edge_aux)
  subgoal for g u v d
    apply (simp add: edges_αg_aux nodes_αg_aux graph_eq_iff invar_add_edge_aux)
    apply (rule conjI)
    subgoal
      unfolding add_edge_def add_edge1_def invar_def αnodes_aux_def
      by (auto simp: M.map_specs)
    subgoal
      unfolding add_edge_def add_edge1_def invar_def αedges_aux_def
      by (fastforce simp: M.map_specs split!: if_splits)
    done      
  subgoal for g u v d
    apply (simp add: edges_αg_aux invar_add_edge_aux)
    unfolding invar_def αw_def add_edge_def add_edge1_def
    by (auto 
      dest: epair_eqD 
      simp: fun_eq_iff M.map_specs 
      split!: prod.splits option.splits if_splits)
  done    
          
        
end
  

end