Theory FiniteListGraph

theory FiniteListGraph
imports 
  FiniteGraph
  "Transitive-Closure.Transitive_Closure_List_Impl"
begin

section ‹Specification of a finite graph, implemented by lists›

text‹A graph G=(V,E)› consits of a list of vertices @{term V}, also called nodes, 
       and a list of edges @{term E}. The edges are tuples of vertices.
       Using lists instead of sets, code can be easily created.›

  record 'v list_graph =
    nodesL :: "'v list"
    edgesL :: "('v ×'v) list"

text‹Correspondence the FiniteGraph›
  definition list_graph_to_graph :: "'v list_graph  'v graph" where 
    "list_graph_to_graph G =  nodes = set (nodesL G), edges = set (edgesL G) "


  definition wf_list_graph_axioms :: "'v list_graph  bool" where
    "wf_list_graph_axioms G  fst` set (edgesL G)  set (nodesL G)  snd` set (edgesL G)  set (nodesL G)"


  lemma wf_list_graph_iff_wf_graph: "wf_graph (list_graph_to_graph G)  wf_list_graph_axioms G"
  unfolding list_graph_to_graph_def wf_graph_def wf_list_graph_axioms_def
  by simp

  text‹We say a @{typ "'v list_graph"} is valid if it fulfills the graph axioms and its lists are distinct›
  definition wf_list_graph::"('v) list_graph  bool" where
   "wf_list_graph G = (distinct (nodesL G)  distinct (edgesL G)  wf_list_graph_axioms G)"


section‹FiniteListGraph operations›

  text ‹Adds a node to a graph.›
  definition add_node :: "'v  'v list_graph  'v list_graph" where 
    "add_node v G =  nodesL = (if v  set (nodesL G) then nodesL G else v#nodesL G), edgesL=edgesL G "

  text ‹Adds an edge to a graph.›
  definition add_edge :: "'v  'v  'v list_graph  'v list_graph" where 
    "add_edge v v' G = (add_node v (add_node v' G)) edgesL := (if (v, v')  set (edgesL G) then edgesL G else (v, v')#edgesL G) "

  text ‹Deletes a node from a graph. Also deletes all adjacent edges.›
  definition delete_node :: "'v  'v list_graph  'v list_graph" where 
  "delete_node v G =  
    nodesL = remove1 v (nodesL G), edgesL = [(e1,e2)  (edgesL G). e1  v  e2  v]
    "

  text ‹Deletes an edge from a graph.›
  definition delete_edge :: "'v  'v  'v list_graph  'v list_graph" where 
    "delete_edge v v' G = nodesL = nodesL G, edgesL = [(e1,e2)  edgesL G. e1  v  e2  v'] "

  
  fun delete_edges::"'v list_graph  ('v × 'v) list  'v list_graph" where 
    "delete_edges G [] = G"|
    "delete_edges G ((v,v')#es) = delete_edges (delete_edge v v' G) es"



text ‹extended graph operations›
   text ‹Reflexive transitive successors of a node. Or: All reachable nodes for v including v.›
    definition succ_rtran :: "'v list_graph  'v  'v list" where
      "succ_rtran G v = rtrancl_list_impl (edgesL G) [v]"

   text ‹Transitive successors of a node. Or: All reachable nodes for v.›
    definition succ_tran :: "'v list_graph  'v  'v list" where
      "succ_tran G v = trancl_list_impl (edgesL G) [v]"
  
   text ‹The number of reachable nodes from v›
    definition num_reachable :: "'v list_graph  'v  nat" where
      "num_reachable G v = length (succ_tran G v)"


    definition num_reachable_norefl :: "'v list_graph  'v  nat" where
      "num_reachable_norefl G v = length ([ x  succ_tran G v. x  v])"


subsection‹undirected graph simulation›
  text ‹Create undirected graph from directed graph by adding backward links›
  fun backlinks :: "('v × 'v) list  ('v × 'v) list" where
    "backlinks [] = []" |
    "backlinks ((e1, e2)#es) = (e2, e1)#(backlinks es)"

  definition undirected :: "'v list_graph  'v list_graph"
    where "undirected G   nodesL = nodesL G, edgesL = remdups (edgesL G @ backlinks (edgesL G)) "

section‹Correctness lemmata›

  ― ‹add node›
  lemma add_node_wf: "wf_list_graph G  wf_list_graph (add_node v G)"
  unfolding wf_list_graph_def wf_list_graph_axioms_def add_node_def
  by auto

  lemma add_node_set_nodes: "set (nodesL (add_node v G)) = set (nodesL G)  {v}"
  unfolding add_node_def
  by auto

  lemma add_node_set_edges: "set (edgesL (add_node v G)) = set (edgesL G)"
  unfolding add_node_def
  by auto

  lemma add_node_correct: "FiniteGraph.add_node v (list_graph_to_graph G) = list_graph_to_graph (add_node v G)"
  unfolding FiniteGraph.add_node_def list_graph_to_graph_def
  by (simp add: add_node_set_edges add_node_set_nodes)

  lemma add_node_wf2: "wf_graph (list_graph_to_graph G)  wf_graph (list_graph_to_graph (add_node v G))"
  by (subst add_node_correct[symmetric]) simp

  ― ‹add edge›
  lemma add_edge_wf: "wf_list_graph G  wf_list_graph (add_edge v v' G)"
  unfolding wf_list_graph_def add_edge_def add_node_def wf_list_graph_axioms_def
  by auto

  lemma add_edge_set_nodes: "set (nodesL (add_edge v v' G)) = set (nodesL G)  {v,v'}"
  unfolding add_edge_def add_node_def
  by auto

  lemma add_edge_set_edges: "set (edgesL (add_edge v v' G)) = set (edgesL G)  {(v,v')}"
  unfolding add_edge_def add_node_def
  by auto

  lemma add_edge_correct: "FiniteGraph.add_edge v v' (list_graph_to_graph G) = list_graph_to_graph (add_edge v v' G)"
  unfolding FiniteGraph.add_edge_def add_edge_def list_graph_to_graph_def
  by (auto simp: add_node_set_nodes)

  lemma add_edge_wf2: "wf_graph (list_graph_to_graph G)  wf_graph (list_graph_to_graph (add_edge v v' G))"
  by (subst add_edge_correct[symmetric]) simp

  ― ‹delete node›
  lemma delete_node_wf: "wf_list_graph G  wf_list_graph (delete_node v G)"
  unfolding wf_list_graph_def delete_node_def wf_list_graph_axioms_def
  by auto

  lemma delete_node_set_edges:
    "set (edgesL (delete_node v G)) = {(a,b). (a, b)  set (edgesL G)  a  v  b  v}"
  unfolding delete_node_def
  by auto

  lemma delete_node_correct:
    assumes "wf_list_graph G"
    shows "FiniteGraph.delete_node v (list_graph_to_graph G) = list_graph_to_graph (delete_node v G)"
  using assms
  unfolding FiniteGraph.delete_node_def delete_node_def list_graph_to_graph_def wf_list_graph_def
  by auto

  ― ‹delete edge›
  lemma delete_edge_set_nodes: "set (nodesL (delete_edge v v' G)) = set (nodesL G)"
  unfolding delete_edge_def
  by simp

  lemma delete_edge_set_edges:
    "set (edgesL (delete_edge v v' G)) = {(a,b). (a,b)  set (edgesL G)  (a,b)  (v,v')}"
  unfolding delete_edge_def
  by auto

  lemma delete_edge_set_edges2:
    "set (edgesL (delete_edge v v' G)) = set (edgesL G) - {(v,v')}"
  by (auto simp:delete_edge_set_edges)

  lemma delete_edge_wf: "wf_list_graph G  wf_list_graph (delete_edge v v' G)"
  unfolding wf_list_graph_def delete_edge_def wf_list_graph_axioms_def
  by auto
    
  lemma delete_edge_length: "length (edgesL (delete_edge v v' G))  length (edgesL G)"
  unfolding delete_edge_def
  by simp

  lemma delete_edge_commute: "delete_edge a1 a2 (delete_edge b1 b2 G) = delete_edge b1 b2 (delete_edge a1 a2 G)"
  unfolding delete_edge_def
  by simp metis (* auto doesn't seem to like filter_cong *)

  lemma delete_edge_correct: "FiniteGraph.delete_edge v v' (list_graph_to_graph G) = list_graph_to_graph (delete_edge v v' G)"
  unfolding FiniteGraph.delete_edge_def delete_edge_def list_graph_to_graph_def
  by auto

  lemma delete_edge_wf2: "wf_graph (list_graph_to_graph G)  wf_graph (list_graph_to_graph (delete_edge v v' G))"
  by (subst delete_edge_correct[symmetric]) simp

  ― ‹delete edges›
  lemma delete_edges_wf: "wf_list_graph G  wf_list_graph (delete_edges G E)"
  by (induction E arbitrary: G) (auto simp: delete_edge_wf)

  lemma delete_edges_set_nodes: "set (nodesL (delete_edges G E)) = set (nodesL G)"
  by (induction E arbitrary: G) (auto simp: delete_edge_set_nodes)

  lemma delete_edges_nodes: "nodesL (delete_edges G es) = nodesL G"
  by (induction es arbitrary: G) (auto simp: delete_edge_def)

  lemma delete_edges_set_edges: "set (edgesL (delete_edges G E)) = set (edgesL G) - set E"
  by (induction E arbitrary: G) (auto simp: delete_edge_def delete_edge_set_nodes)

  lemma delete_edges_set_edges2:
    "set (edgesL (delete_edges G E)) = {(a,b). (a,b)  set (edgesL G)  (a,b)  set E}"
  by (auto simp: delete_edges_set_edges)

  lemma delete_edges_length: "length (edgesL (delete_edges G f))  length (edgesL G)"
  proof (induction f arbitrary:G)
    case (Cons f fs)
    thus ?case
      apply (cases f, hypsubst)
      apply (subst delete_edges.simps(2))
      apply (metis delete_edge_length le_trans)
      done
  qed simp

  lemma delete_edges_chain: "delete_edges G (as @ bs) = delete_edges (delete_edges G as) bs"
  proof (induction as arbitrary: bs G)
    case (Cons f fs)
    thus ?case
      by (cases f) auto
  qed simp

  lemma delete_edges_delete_edge_commute:
    "delete_edges (delete_edge a1 a2 G) as = delete_edge a1 a2 (delete_edges G as)"
  proof (induction as arbitrary: G a1 a2)
    case (Cons f fs)
    thus ?case
      by (cases f) (simp add: delete_edge_commute)
  qed simp

  lemma delete_edges_commute:
    "delete_edges (delete_edges G as) bs = delete_edges (delete_edges G bs) as"
  proof (induction as arbitrary: bs G)
    case (Cons f fs)
    thus ?case
      by (cases f) (simp add: delete_edges_delete_edge_commute)
  qed simp

  lemma delete_edges_as_filter:
    "delete_edges G l =  nodesL = nodesL G,  edgesL = [x  edgesL G. x  set l] "
  proof (induction l)
    case (Cons f fs)
    thus ?case
      apply (cases f)
      apply (simp add: delete_edges_delete_edge_commute)
      apply (simp add: delete_edge_def)
      apply (metis (lifting, full_types) prod.exhaust case_prodI split_conv)
      done
  qed simp

  declare delete_edges.simps[simp del] (*do not automatically expand definition*)

  lemma delete_edges_correct:
    "FiniteGraph.delete_edges (list_graph_to_graph G) (set E) = list_graph_to_graph (delete_edges G E)"
  unfolding list_graph_to_graph_def FiniteGraph.delete_edges_def
  by (auto simp add: delete_edges_as_filter )
  
  lemma delete_edges_wf2:
    "wf_graph (list_graph_to_graph G)  wf_graph (list_graph_to_graph (delete_edges G E))"
  by (subst delete_edges_correct[symmetric]) simp

  ― ‹helper about reflexive transitive closure impl›
  lemma distinct_relpow_impl:
    "distinct L  distinct new  distinct have  distinct (new@have)  
     distinct (relpow_impl (λas. remdups (map snd [(a, b)L . a  set as])) (λxs ys. [xxs . x  set ys] @ ys) (λx xs. x  set xs) new have M)"
  proof (induction M arbitrary: "new" "have")
    case Suc
    hence
      "distinct ([xnew . x  set have] @ have)"
      "set ([nremdups (map snd [(a, b)L . a  set new]) . (n  set new  n  set have)  n  set have])  set ([xnew . x  set have] @ have) = {}"
      by auto

    with Suc show ?case
      by auto
  qed auto

  lemma distinct_rtrancl_list_impl: "distinct L  distinct ls  distinct (rtrancl_list_impl L ls)"
  unfolding rtrancl_list_impl_def rtrancl_impl_def
  by (simp add:distinct_relpow_impl)

  lemma distinct_trancl_list_impl: "distinct L  distinct ls  distinct (trancl_list_impl L ls)"
  unfolding trancl_list_impl_def trancl_impl_def
  by (simp add:distinct_relpow_impl)

  ― ‹succ rtran›
  value "succ_rtran  nodesL = [1::nat,2,3,4,8,9,10], edgesL = [(1,2), (2,3), (3,4), (8,9),(9,8)]  1"

  lemma succ_rtran_correct: "FiniteGraph.succ_rtran (list_graph_to_graph G) v = set (succ_rtran G v)"
  unfolding FiniteGraph.succ_rtran_def succ_rtran_def list_graph_to_graph_def
  by (simp add: rtrancl_list_impl)

  lemma distinct_succ_rtran: "wf_list_graph G  distinct (succ_rtran G v)"
  unfolding succ_rtran_def wf_list_graph_def
  by (auto intro: distinct_rtrancl_list_impl)

  lemma succ_rtran_set: "set (succ_rtran G v) = {e2. (v,e2)  (set (edgesL G))*}"
  unfolding succ_rtran_def
  by (simp add: rtrancl_list_impl)

  ― ‹succ tran›
  lemma distinct_succ_tran: "wf_list_graph G  distinct (succ_tran G v)"
  unfolding succ_tran_def wf_list_graph_def
  by (auto intro: distinct_trancl_list_impl)

  lemma succ_tran_set: "set (succ_tran G v) = {e2. (v,e2)  (set (edgesL G))+}"
  unfolding succ_tran_def
  by (simp add: trancl_list_impl)

  value "succ_tran  nodesL = [1::nat,2,3,4,8,9,10], edgesL = [(1,2), (2,3), (3,4), (8,9),(9,8)]  1"

  lemma succ_tran_correct: "FiniteGraph.succ_tran (list_graph_to_graph G) v = set (succ_tran G v)"
  unfolding FiniteGraph.succ_tran_def succ_tran_def list_graph_to_graph_def
  by (simp add:trancl_list_impl)
  
  ― ‹num_reachable›
  lemma num_reachable_correct:
    "wf_list_graph G  FiniteGraph.num_reachable (list_graph_to_graph G) v = num_reachable G v"
  unfolding num_reachable_def FiniteGraph.num_reachable_def
  by (metis List.distinct_card distinct_succ_tran succ_tran_correct)

  ― ‹num_reachable_norefl›
  lemma num_reachable_norefl_correct:
    "wf_list_graph G  
     FiniteGraph.num_reachable_norefl (list_graph_to_graph G) v = num_reachable_norefl G v"
 unfolding num_reachable_norefl_def FiniteGraph.num_reachable_norefl_def
 by (metis (full_types) List.distinct_card distinct_filter distinct_succ_tran set_minus_filter_out succ_tran_correct)

  ― ‹backlinks, i.e. backflows in formal def›
  lemma backlinks_alt: "backlinks E = [(snd e, fst e). e  E]"
  by (induction E) auto

  lemma backlinks_set: "set (backlinks E) = {(e2, e1). (e1, e2)  set E}"
  by (induction E) auto

  lemma undirected_nodes_set: "set (edgesL (undirected G)) = set (edgesL G)  {(e2, e1). (e1, e2)  set (edgesL G)}"
  unfolding undirected_def
  by (simp add: backlinks_set)

  lemma undirected_succ_tran_set: "set (succ_tran (undirected G) v) = {e2. (v,e2)  (set (edgesL (undirected G)))+}"
  by (fact succ_tran_set)

  lemma backlinks_in_nodes_G: " fst ` set (edgesL G)  set (nodesL G); snd ` set (edgesL G)  set (nodesL G)   
    fst` set (edgesL (undirected G))  set (nodesL (undirected G))  snd` set (edgesL (undirected G))  set (nodesL (undirected G))"
  unfolding undirected_def
  by(auto simp: backlinks_set)

  lemma backlinks_distinct: "distinct E  distinct (backlinks E)"
  by (induction E) (auto simp: backlinks_alt)

  lemma backlinks_subset: "set (backlinks X)  set (backlinks Y)  set X  set Y"
  by (auto simp: backlinks_set)

  lemma backlinks_correct: "FiniteGraph.backflows (set E) = set (backlinks E)"
  unfolding backflows_def
  by(simp add: backlinks_set)

  ― ‹undirected›
  lemma undirected_wf: "wf_list_graph G  wf_list_graph (undirected G)"
  unfolding wf_list_graph_def wf_list_graph_axioms_def
  by (simp add:backlinks_in_nodes_G) (simp add: undirected_def)

  lemma undirected_correct: 
    "FiniteGraph.undirected (list_graph_to_graph G) = list_graph_to_graph (undirected G)"
  unfolding FiniteGraph.undirected_def undirected_def list_graph_to_graph_def
  by (simp add: backlinks_set)
      
lemmas wf_list_graph_wf =
  add_node_wf
  add_edge_wf
  delete_node_wf
  delete_edge_wf
  delete_edges_wf
  undirected_wf

lemmas list_graph_correct =
  add_node_correct
  add_edge_correct
  delete_node_correct
  delete_edge_correct
  delete_edges_correct
  succ_rtran_correct
  succ_tran_correct
  num_reachable_correct
  undirected_correct


end