\documentclass[t,12pt]{beamer}

\mode<presentation>
{
 % \usetheme{Antibes}
  \usetheme{Warsaw}
  \usefonttheme{serif}
}



\newcommand{\degree}{\ensuremath{^\circ}}
\definecolor{gray}{rgb}{0.8,.8,.8}


\title{Graphs} 
\author{Part I: Basic algorithms}
\date{Laura Toma\\Algorithms (csci2200), Bowdoin College}


% If you wish to uncover everything in a step-wise fashion, uncomment
% the following command: 
%\beamerdefaultoverlayspecification{<+->}


\begin{document}

\begin{frame}
  \titlepage
\end{frame}


%\begin{frame}{Outline}
%  \tableofcontents
  % You might wish to add the option [pausesections]
%\end{frame}


%\section[Motivation]{The problem and motivation}
%\subsection{Undirected graphs}
\begin{frame}
\frametitle{Undirected graphs}

 Concepts: 
  \begin{itemize}
  \item connectivity, connected components 
  \item paths
  \item (undirected) cycles
  \end{itemize}
  
  Basic problems, given undirected graph $G$:   
  \begin{itemize}
  \item is $G$ connected 
  \item how many connected components (CC) are in $G$?
  \item label each vertex with its CC id
  \item find a path between $u$ and $v$
  \item does $G$ contain a cycle? 
  \item compute a spanning forest for $G$
  \end{itemize}
\end{frame} 


\begin{frame}
  \frametitle{(Undirected) DFS}
  Idea: explore the graph ``depth-first''. Similar to how you'd try to
  find a path out of a maze.

  \vfill 
  Easiest to write it recursively. Usually keep track of the vertex
  that first discovered a vertex $u$; we call that the parent of $u$.

  \vfill 
  DFS(vertex v) 
  \begin{itemize} 
  \item mark v
  \item for each adjacent edge $(v,u)$:
    \begin{itemize} 
    \item if $u$ is not marked:
      \begin{itemize} 
      \item mark $u$
      \item parent($u$) = $v$
      \item DFS(u)
    \end{itemize} 
    \end{itemize} 
  \end{itemize} 
\end{frame} 


\begin{frame}
\frametitle{(Undirected) DFS}
DFS-tree: Each vertex, except the source vertex $v$, has a parent $\Rightarrow$
these edges define a tree, called the DFS-tree.  

\vspace{1cm} During $DFS(v)$ each edge in $G$ is classified as: 
\begin{itemize} 
\item tree edge: an edge leading to an unmarked vertex
\item non-tree edge: an edge leading to a marked vertex. 
\end{itemize} 
\end{frame} 





\begin{frame}
\frametitle{ (Undirected) DFS }
\begin{lemma} $DFS(u)$ visits all vertices in the connected component
  (CC) of $u$, and the DFS-tree is a spanning tree of CC($u$).
\end{lemma}

{\scriptsize Proof sketch: Assume by contradiction that there is a
  vertex $v$ in CC(u) that is not reached by DFS(u). Since $u,v$ are
  in same CC, there must exist a path $v_0=u, v_1, v_2, ..., v_k, v$
  connecting $u$ to $v$. Let $v_i$ be the last vertex on this path
  that is reached by DFS(u) ($v_i$ could be $u$). When exploring
  $v_i$, DFS must have explored edge $(v_i, v_{i+1})$,..., leading
  eventually to $v$. Contradiction.  }
\end{frame} 





\begin{frame}
\frametitle{ (Undirected) DFS }
{\scriptsize
Tree: 
\begin{itemize}
\item ancestors of $x$: parent, grandparent,..., i.e., all vertices on
  the path from $x$ to root
\item descendants of $x$: chidlren, grandschildren,.., i.e. all
  vertices in the subtree rooted at $x$
\end{itemize} 
}
\begin{lemma} For a given call $DFS(u)$, all vertices that are marked
  between the start and end of this call are all descendants of $u$ in
  the DFS-tree.
\end{lemma} 

{\scriptsize To understand why this is true, visualize how the
  recursion works in $DFS(v)$. The outgoing edges $(v,*)$ are visited
  one at a time, calling DFS recursively on nodes that will become
  $v$'s children; when all edges outgoing from $v$ are visited,
  $DFS(v)$ terminates and returns. While the children of $v$ are
  explored, the call $DFS(v)$ stays on stack. Repeat this argument,
  and you'll see that when $DFS(x)$ is called, the following functions
  are active on stack: DFS(parent(x)), DFS(grandparent(x)),....,DFS(v).
}
\end{frame} 



\begin{frame}
\frametitle{(Undirected) DFS}
\begin{lemma} Non-tree edges encountered during undirected $DFS(v)$ go
  from a vertex to an ancestor of that vertex.
\end{lemma}

\vfill {\scriptsize Proof: Let's say DFS(v) reaches a vertex $x$ and
  explores edge $(x,y)$, at which point it sees that $y$ is marked.
  We want to show that $y$ is an ancestor of $x$. We know that all
  ancestors of $x$ are marked and ``unfinished'', i.e. their DFS
  frames are active, on stack, and the system will backtrack to finish
  them.  In contrast, a vertex that is marked but not an ancestor of
  $x$ is ``finished'', i.e. all its outgoing edges were exploredf and
  DFS will not backtrack there.

  Now assume by contradiction, that $y$ is marked but is not an
  ancestor of $x$: then DFS has finished exploring $y$; when $y$ was
  visited, edge must have been $(y,x)$ explored and $x$ could not have
  been marked at that time, so $x$ would have been made a child of $y$
  ---- contradiction.  }
\end{frame} 



\begin{frame}[t]
\frametitle{(Undirected) DFS }
As written above, DFS explores only the CC of $v$.  DFS can be used to
explore all the CCs in $G$:
\begin{itemize} 
\item mark all vertices as ``unmarked''
\item for each vertex $v$
  \begin{itemize} 
  \item if $v$ is marked, skip
  \item if $v$ is not marked: $DFS(v)$
  \end{itemize} 
\end{itemize} 

\begin{lemma} DFS runs in $O(|V|+|E|)$ time.  
\end{lemma} {\scriptsize Proof: It explores every vertex once. Once a
  vertex is marked, it's not explored again.  It traverses each edge
  twice. Overall, $O(|V|+|E|)$.  }
\end{frame} 


\begin{frame} 
\frametitle{(Undirected) DFS}
Undirected DFS can be used to solve in $O(|V|+|E|)$ time the following
problems:

\begin{itemize} 
\item is $G$ connected? 
\item compute the number of CC of $G$
\item  compute a spanning forest of $G$
\item compute a path between two vertices of $G$, or report that
  such a path does not exist
\item compute a cycle, or report that no cycle exists
\end{itemize}
\end{frame} 



\begin{frame} 
  \frametitle{(Undirected) BFS}
  Idea: explore outwards, one layer at a time.  Visualize a wave
  propagating outwards.  BFS logically subdivides the vertices into
  layers.
  \vfill
  {\bf BFS(vertex u)}\\
  mark $u$, d($u$) =0, $Q= \{\}$\\
  while $Q$ not empty 
  \begin{itemize} 
  \item remove the next vertex $v$ from $Q$
\item for all edges $(v,w)$ do
  \begin{itemize} 
  \item if $w$ is not marked: 
    \begin{itemize} 
    \item mark $w$
    \item parent($w$)=$u$ \textcolor{gray}{//$(v,w)$ is a tree edge}
    \item d($w$) = d($v$) + 1 
    \item add $w$ to $Q$
    \end{itemize} 
  \item \textcolor{gray}{//else: $w$ is marked, $(v,w)$ is non-tree edge}
  \end{itemize}
\end{itemize}
\end{frame} 




\begin{frame}[t]
\frametitle{Undirected BFS}
BFS-tree: Each vertex, except the source vertex $v$, has a parent $\Rightarrow$
these edges define a tree, called the DFS-tree.  

\vspace{1cm} During $BFS(v)$ each edge in $G$ is classified as:
\begin{itemize} 
\item tree edge: an edge leading to an unmarked vertex
\item non-tree edge: an edge leading to a marked vertex. 
\end{itemize} 

\end{frame} 


\begin{frame}[t]
\frametitle{Undirected BFS}
\begin{lemma} $BFS(u)$ visits all vertices in the connected component
  (CC) of $u$, and the BFS-tree is a spanning tree of CC($u$).
\end{lemma}

{\scriptsize Proof sketch: Assume by contradiction that there is a
  vertex $v$ in CC(u) that is not reached by BFS(u). Since $u,v$ are
  in same CC, there must exist a path $v_0=u, v_1, v_2, ..., v_k, v$
  connecting $u$ to $v$. Let $v_i$ be the last vertex on this path
  that is reached by BFS(u) ($v_i$ could be $u$). When exploring
  $v_i$, BFS must have explored edge $(v_i, v_{i+1})$,..., leading
  eventually to $v$. Contradiction.  }
\end{frame} 


\begin{frame}[t]
\frametitle{Undirected BFS}
As written above, BFS explores only the CC of $v$.  BFS can be used to
explore all the CCs in $G$:
\begin{itemize} 
\item mark all vertices as ``unmarked''
\item for each vertex $v$
  \begin{itemize} 
  \item if $v$ is marked, skip
  \item if $v$ is not marked: $BFS(v)$
  \end{itemize} 
\end{itemize} 

\begin{lemma} BFS runs in $O(|V|+|E|)$ time.  
\end{lemma} {\scriptsize Proof: It explores every vertex once. Once a
  vertex is marked, it's not explored again.  It traverses each edge
  twice. Overall, $O(|V|+|E|)$.  }
\end{frame} 


\begin{frame}[t]
\frametitle{Undirected BFS}
\begin{lemma} Let $x$ be a vertex reached in BFS(v). The path $v
  \rightarrow x$ contains d($i$) edges and represents the shortest
  path from $v$ to $x$ in $G$.
\end{lemma} 
Notation: length of shortest path from $v$ to $u$ is $\delta(v,u)$. \\
{\scriptsize Proof idea: The complete proof is quite long.....The idea
  is contradiction: Assume there exists at least one vertex for which
  BFS(v) does not compute the right distance.  Among these vertices,
  let $u$ be the vertex with the smallest distance from $v$.  Let
  $p=(v,...,u)$ be the shortest path from $v$ to $u$ of length
  $\delta(v,u)$.  The vertex $x$ just before $u$ on this path has
  shortest path to $v$ of length $\delta(v,x)=\delta(v,u)-1$ (subpaths of shortest
  paths are shortest paths bla bla..).  This vertex is correctly
  labeled by BFS (because of our assumption), so $d(x) = \delta(v,x) =
  \delta(v,u)-1$. But because of edge $(x,u)$, BFS will find a path to
  $u$ of length $d(v,x)+1$, i.e. $d(u)=\delta(v,u)$. }

\end{frame} 


\begin{frame}[t]
\frametitle{Undirected BFS}

\begin{lemma}
For any non-tree edge $(x,y)$ in BFS($v$), the level of $x$ and $y$
differ by at most one. 
\end{lemma}
In other words, $x,y$ are on the same level or on consecutive levels;
there cannot be non-tree edges that jump over more than one level.

{\scriptsize Proof idea: Intuitively, this is because all immediate
  neighbors that are not marked are put on the next level. Observe
  that, at any point in time, the vertices in the queue have distances
  that differ by at most 1. This can be shown easily with induction
  (bla bla).
%
Let's say $x$ comes out first from the queue; at this time $y$ must be
already marked (because otherwise $(x,y)$ would be a tree
edge). Furthermore $y$ has to be in the queue, because, if it wasn't,
it means it was already deleted from the queue and we assumed $x$ was
first.  So $y$ has to be in the queue, and we have $|d(y)-d(x)| \le 1$
by above observation.
}
\end{frame} 


\begin{frame}[t]\frametitle{Undirected BFS}
Undirected BFS can be used to solve in $O(|V|+|E|)$ time the following
problems:

\begin{itemize} 
\item is $G$ connected? 
\item compute the number of CC of $G$
\item  compute a spanning forest of $G$
\item compute \alert{shortest path} between two vertices of $G$
\item compute a cycle, or report that no cycle exists
\end{itemize}
\end{frame} 


\begin{frame}[t]
\frametitle{Directed graphs (Digraphs)}
Concepts:
\begin{itemize}
\item reachability
\item directed paths and cycles 
\item strongly connected components (SCC)
\item directed acyclyc graphs (DAGs)
\item transitive closure (TC)
\end{itemize} 
Problems: 
\begin{itemize}
\item given $u,v$: does $u$ reach $v$? 
\item given $u$: find all vertices reachabla from $u$
\item is $G$ strongly connected? 
\item is $G$ acyclic? 
\item compute the SCCs
\item compute the TC $G*$ of $G$
\end{itemize} 
\end{frame} 



\begin{frame}
\frametitle{Directed DFS}
Same as undirected, but visit the \alert{outgoing} edges of a vertex
\vfill
Properties: 
\begin{itemize} 
\item DFS(v) visits all vertices \alert{reachable} from $v$. 
\item the DFS-tree contains \alert{directed} paths from $v$ to all
  vertices reachable from $v$
\item runs in $O(|V| + |E|)$
\item non-tree edges $(x,y)$ are of 3 types: 
  \begin{itemize} 
  \item back edge: $y$ is an ancestor of $x$ in DFS tree
  \item forward edges: $y$ is a descendant of $x$ in the DFS tree 
  \item cross edges: $y$ is neither ancestor nor descendant 
  \end{itemize} 
\item all 3 types of non-tree edges are possible in directed DFS
\item a non-tree back edge  defines a directed cycle
\end{itemize} 
\end{frame}



\begin{frame}
\frametitle{Directed DFS}
Given a digraph $G$, directed DFS can be used  to solve the following
in $O(|V|+|E|)$: 
\begin{itemize} 
\item does the graph contain a directed cycle? 
\item find a directed cycle
\item given $v$, find all vertices reachable from $v$
\item given $u,v$: find a path from $u$ to $v$ or report that there is none
\end{itemize} 
\end{frame}



\begin{frame}
\frametitle{Directed BFS}
Same as undirected, but visit the \alert{outgoing} edges of a vertex
\vfill
Properties: 
\begin{itemize} 
\item BFS(v) visits all vertices \alert{reachable} from $v$. 
\item the BFS-tree contains \alert{directed} paths from $v$ to all
  vertices reachable from $v$
\item BFS(v) computes the shortest paths from $v$ to all vertices
  reachable from $v$
\item runs in $O(|V| + |E|)$
\item forward non-tree edges are not possible
  \begin{itemize} 
  \item {\scriptsize When visiting $x$, all outgoing edges are
      explored at the same time. If $y$ is not marked, $y$ becomes the
      child of $x$. If $y$ is marked, it's either a back edge or a
      cross edge. A forward edge $(x,y)$ is not possible.  }
  \end{itemize} 
\end{itemize} 


\end{frame}\begin{frame}
\frametitle{Directed BFS}
Given a digraph $G$, directed BFS can be used  to solve the following
in $O(|V|+|E|)$: 
\begin{itemize} 
\item find directed cycles
\item given $v$, find all vertices reachable from $v$
\item given $u,v$: find the shortest path  from $u$ to $v$
\end{itemize} 

\end{frame}




\begin{frame}
\frametitle{Is $G$ strongly connected?}
Idea: for each $v$ run BFS(v). $G$ is SC if and only if
every vertex reaches all other vertices. This runs in $O(V \cdot
(V+E))$.

\vfill 
Can we do better? 

\vfill 
YES.  Run BFS twice. Total $O(V+E)$.  
\begin{itemize} 
\item run $BFS(v)$ from an arbitrary vertex $v$. If this does not
  reach all $u\in V (u\neq v)$, $G$ is not SC.
\item If it does, run a modified $BFS(v)$ that visits the
  \emph{incoming} edges in a vertex.  If all $u \in V (u \neq v)$ are
  reached by this BFS, then we conclude $G$ is SC.
  \begin{itemize}
  \item given $x,y \in V$: $x$ reaches $v$, and $v$ reaches $y$, so
    $x$ reaches $y$. The other way around, too. 
  \end{itemize} 
\end{itemize} 
\vfill
Note: Can use BFS or DFS. SCCs can be found in $O(V+E)$ but it's more complicated.
\end{frame}




\begin{frame}
\frametitle{Transitive closure}
Idea: for each $v$, run BFS(v), and for all vertices $w$ reached,
output an edge $(v,w)$.  Runs in $O(V \cdot (V+E))$.

\vfill Can we do better?  

\vfill YES.  Idea: If $a$ reaches $b$ and $b$ reaches $c$, then $a$
reaches $c$. Use this to avoid recomputation. Dynamic programming.

\end{frame}



\begin{frame}
\frametitle{Transitive closure with dynamic programming}
Ideas: \\ \pause 
\alert{Number the vertices $1,2,3,...,n$}\\\pause
\vfill
\alert{We'll compute  $G^*$ in rounds, starting from $G$.}\\\pause
\vfill 
In round \alert{1}: $G \rightarrow G_1$\\\pause
\begin{itemize} 
\item We start with all edges in $G$, and we add a new edge $(i,j)$
  for every pair of vertices such that $i$ can reach $j$ going only
  through vertex $\alert{1}$.\pause
\item \textcolor{gray}{How does a path $(i,j)$ that goes only through vertex $1$ look
  like?} \pause  \textcolor{gray}{$(i,j), (i, 1, j)$}\pause
\item  \textcolor{gray}{When does it exist?} \pause \textcolor{gray}{When $(i,j)$ exists, or $(i,1)$ and $(1,j)$ both exist} \pause
\item  This comes down to checking, if there exist edges $(i,1)$ and
$(1,j)$ in $G$.
\end{itemize} 
\end{frame} 





\begin{frame}
\frametitle{Transitive closure with dynamic programming}
Ideas:  \\
\alert{Number the vertices $1,2,3,...,n$}\\
\vfill
\alert{We'll compute  $G^*$ in rounds, starting from $G$.}\\
\vfill 
In round \alert{2}: $G_1 \rightarrow G_2$\\\pause
\begin{itemize} 
\item We start with all edges in $G$, and we add a new edge $(i,j)$
  for every pair of vertices such that $i$ can reach $j$ going only
  through vertices \alert{$1, 2$}.\pause
\item \textcolor{gray}{How does a path $(i,j)$ that goes only through $1, 2$ looks
  like?} \pause  \textcolor{gray}{$(i,j), (i, 1, j), (i,2,j), (i,1,2,j), (i,2,1,j))$}\pause
\item  \textcolor{gray}{When does it exist?} \pause \textcolor{gray}{When $(i,j)$ exists in $G_1$, or  $(i,2)$ and $(2,j)$ both exist in $G_1$} \pause
\item  This comes down to checking, if there exist edges $(i,2)$ and
$(2,j)$ in $G_1$.
\end{itemize} 
\end{frame} 



\begin{frame}
\frametitle{Transitive closure with dynamic programming}
Ideas:  \\
\alert{Number the vertices $1,2,...,n$}\\
\vfill
\alert{We'll compute  $G^*$ in rounds, starting from $G$.}\\
\vfill 
In round \alert{k+1}: $G_k \rightarrow G_{k+1}$\\\pause
\begin{itemize} 
\item We start with all edges in $G_k$, and we add a new edge $(i,j)$
  for every pair of vertices such that $i$ can reach $j$ going only
  through vertices \alert{$1,2,...,k,k+1$}.\pause
\item \textcolor{gray}{How does a path $(i,j)$ that goes only through $1,2,...,k,k+1$ look  like?} \pause  \textcolor{gray}{$(i....j), (i...,k+1,.... j)$}\pause
\item  \textcolor{gray}{When does it exist?} \pause \textcolor{gray}{When $(i,j)$ exists in $G_k$, or  $(i,k+1)$ and $(k+1,j)$ both exist in $G_k$} \pause
\item  This comes down to checking, if there exist edges $(i,k+1)$ and
$(k+1,j)$ in $G_k$.
\end{itemize} 
\end{frame} 



\begin{frame}
\frametitle{Transitive closure with dynamic programming}

Idea  1:  Number the vertices $1,2,...,n$\\
Idea 2: Consider paths that only use  vertices numbered $1,2,...,k$ as
intermediate vertices.

\begin{lemma} A path from $v_i$ to $v_j$ that uses vertices numbered
$v_1,v_2,...,v_k,$ \alert{$v_{k+1}$} either:
\begin{itemize} 
\item \alert{does not include vertex $V_{k+1}$:} is a path from $v_i$ to $v_j$ that uses
  vertices numbered $v_1,v_2,...,v_k$; or
\item \alert{includes vertex $v_{k+1}$:} is a path from $v_i$ to $v_{k+1}$ that
  uses vertices numbered $v_1,v_2,...,v_k$; and a path from $v_{k+1}$ to $v_j$
  that uses vertices numbered $v_1,v_2,...,v_k$.
\end{itemize} 
\end{lemma} 
\end{frame}




\begin{frame}
\frametitle{Transitive closure with dynamic programming}
{\bf Floyd-Warshall's algorithm: }

\begin{itemize} 
\item $G_0 = G$
\item for $k=1$ to $n$
  \begin{itemize} 
  \item $G_k = G_{k-1}$
  \item for $i=1..n$ ($i \neq k$) and $j=1..n$ ($j \neq i,k$): 
    \begin{itemize} 
    \item \textcolor{gray}{if edge $(v_i,v_j)$ exists in $G_{k-1}$, then add edge $(v_i,v_j)$  to
      $G_k$}
    \item else:  if both edges $(v_i, v_k)$ and $(v_k, v_j)$ exist in
      $G_{k-1}$, then add edge $(v_i,v_j)$ to $G_k$
    \end{itemize} 
  \end{itemize} 
\end{itemize}

\begin{lemma} $G_n$ represents the TC of $G$. 
\end{lemma}
\pause
{\scriptsize Paths  in $G_n$ contain all possible vertices and more iterations will not change $G_n$.} \\
\vfill
\pause Analysis: if $G$ is represented by adjacency matrix:
$O(n^3)$
 \end{frame}

\begin{frame}
\frametitle{Transitive closure}

Two algorithms: 

\begin{itemize} 
\item Run BFS from each vertex: $O(V(V+E)) = O(n(n+m))$\\

\item Floyd-Warshall:  $O(n^3)$
\end{itemize} 

\vspace{1cm}
Which one is better, when? 
\end{frame} 


\begin{frame}
\frametitle{Topological order}
\begin{definition}
A topological ordering of $G$ is an ordering $v_1, v_2, ..., v_n$ of
its vertices such that for any edge $(v_i, v_j)$ in $G$, $v_i < v_j$
in the order.
\end{definition} 

A topological order is an order such that any directed path traverses
the vertices in increasing order.
\vfill
There may be more than one topological orderings. 
\end{frame}




\begin{frame}
\frametitle{Topological order}
\begin{lemma} If a digraph $G$ admits a topological ordering then it
  is acyclic.
\end{lemma} 

{\scriptsize Proof idea: Assume by contradiction that $G$ contains a
  cycle. Let $v_i,v_j$ two vertices on this cycle.  Then $v_i$ reaches
  $v_j$ and $v_j$ reaches $v_i$. This means that $v_i$ must come
  before $v_j$, and $v_j$ must come before $v_i$ in the
  ordering. Contradiction.
}
\end{frame}


\begin{frame}
\frametitle{Topological order}
\begin{lemma} If a digraph $G$ is acyclic then it admits a topological
  ordering. 
\end{lemma} 

{\scriptsize Proof:

  (1) If a graph is acyclic, there must be a vertex of indegree $0$:
  Assume by contradiction that this is not true. If all vertices have
  indegree $\ge 1$, we could travel from a vertex $v$ using one of its
  incoming edge, $x \leftarrow v $, and from $x$ using one of its
  incoming edges, and so on, and we could do this indefinitely since
  all vertices have at least one incoming edge.  There's only a finite
  number of vertices, so there must be a cycle. Contradiction.

  \vspace{1cm}
  (2) Let $v$ be a vertex of indegree $0$.  We put $v$ first in
  topological order,  and delete all its outgoing edges from $G$: For
  each $(v,u)$, decrement indegree($u$).  The remaining graph $G -
  \{v\}$ is still acyclic.  Repeat. 

}

\end{frame}



\begin{frame}
\frametitle{Topological order}
\begin{theorem} A digraph $G$ admits a topological ordering if and
  only if $G$ is acyclic.
\end{theorem} 

Algorithm: 
\begin{itemize} 
\item for each $v \in V$: compute indegree(v)
\item $L = \{ v \in V| indegree(v) = 0\}$
\item while $L$ not empty : 
  \begin{itemize} 
  \item delete a vertex from $L$ (doesn't matter which one). Call it
    $v$.
  \item $v$ is the next vertex in topological ordering 
  \item for every edge $(v,u)$: decrement indegree(u); if
    indegree(u)==0 insert $u$ in $L$.
  \end{itemize} 
\end{itemize}
\pause
Analysis: $O(V+E)$
\end{frame}

\end{document}


