\(\\\) \(\\\)

This project is part of my PhD dissertation project. The network has already been assembled.

Let’s first load the required packages and the network file: \(\\\)

Loading libraries….

rr library(igraph)

\(\\\)

Descriptive Network Analysis

rr # tbnet <- read_graph(‘~/Documents/sna/graphs_old/CAnet.graphml’, format = ‘graphml’) tbnet <- read_graph(‘./graphs/TBnet.graphml’, format = ‘graphml’)

\(\\\)

Get a summary of the network:

rr summary(tbnet)

IGRAPH UN-- 173 1937 -- 
+ attr: affil (v/c), numPub (v/n), place (v/c), country (v/c), name (v/c), timesCited (v/n), id (v/c), key (e/n),
| subject (e/c), year (e/n), wosid (e/c), journal (e/c), title (e/c), timesCited (e/n), doi (e/c)

Interpretation: Our co-authorship network is an undirected multigraph (parallel edges) with 173 authors and 1937 scientific collaborations. Each node (author) in the network has 7 attributes: name, country, place, affil, numPub, timesCited and id. Each edge has 8 attributes: key, subject, abstract, year, wosid (Web of science Identification number), journal, title and doi.

Computing Node Centrality Measures: We compute centrality measures such as degree (number of ties to a given author), betweenness (number of shortest paths between alters that go through a particular author), closeness (number of steps required for a particular author to access every other author in the network) and eigenvectors (degree to which an author is connected to other well connected authors in the network), brokerage (degree to which an actor occupies a brokerage position across all pairs of alters)

Degree centrality Let’s first compute the degree and strength of the nodes in the network:

rr d.tbnet <- degree(tbnet) s.tbnet <- graph.strength(simplify(tbnet)) # for weighted graph

Now, let’s plot a histogram of the degree and strength distributions:

Interpretation: While there is a substantial number of nodes of quite low degree, there are also a non-trivial number of nodes with higher order of degree magnitudes. Given the nature of this distribution, a log-log scale is more effective at summarizing the degree information.

From here, the processing requires our network to be a simple graph. We would therefore change our multigraph to a graph object.

Visualization

Now, let’s now visualize our co-authorship graph network tbnet2 using the Kamada and Kawai layout:

rr # l.tbnet <- layout.kamada.kawai(tbnet) # l.tbnet2 <- layout.kamada.kawai(tbnet2) l.tbnet2 <- layout.kamada.kawai(tbnet2)

rr # plot(tbnet, layout=l.tbnet, vertex.label=NA, vertex.size=2) # plot(tbnet2, layout=l.tbnet2, vertex.label=NA, vertex.size=2, vertex.color=‘red’) plot(tbnet2, layout=l.tbnet2, vertex.label=NA, vertex.size=2, vertex.color=‘blue’) # We use l.tbnet2 as layout

rr d.tbnet <- degree(tbnet2) # Degree of simple graph tbnet2 V(tbnet2)\(degree <- d.tbnet dd.tbnet <- degree.distribution(tbnet2) # Compute degree distribution d <- 1:max(d.tbnet)-1 ind <- (dd.tbnet != 0) a.nn.deg.tbnet <- graph.knn(tbnet2, V(tbnet2))\)knn mean(d.tbnet)

[1] 17.36416

Now, ploting degree distribution of the simplified graph.

Interpretation: From the plot on the right, the degrees of the node no clear distribution. Let’s investigate the manner in which nodes of different degrees are linked with each other in the coauthorship network. For this purpose, we bring in the notion of the average degree of the neighbors of a given node. We then plot the average neighbor degree against node degree.

Interpretation: The plot above suggests that while there is a tendency of nodes of higher degrees to link with similar nodes, nodes of lower degree tend to link with nodes of both lower and higher degrees. In other words, while prominent authors with important collaborations tend to collaborate with similar authors, young or less prolific authors tend to collaborate with both prolific and authors with very few collaborations.

Let’s compute the other 3 node centrality measures:

rr A <- get.adjacency(tbnet2, sparse = FALSE) g <- network::as.network.matrix(A)

Closeness centrality: captures the notion that a node is central if it close to many other nodes Considering a network \(G=(V,E)\) where \(V\) is the set of nodes and \(E\), the set of edges, the closeness centrality \(c_{Cl}(v)\) of a node \(v\) is defined as: \[c_{Cl}(v)=\frac{1}{\sum_{u\in V}dist(v,u)}\] \(dist(v,u)\) is defined as the geodesic distance between the nodes \(u,v \in V\).

rr cl.tbnet <- closeness(tbnet2) V(tbnet2)$closeness <- cl.tbnet summary(cl.tbnet)

     Min.   1st Qu.    Median      Mean   3rd Qu.      Max. 
0.0000368 0.0003116 0.0003175 0.0002907 0.0003211 0.0003280 

Betweeness centrality: summarizes the extent to which a node is located between other pairs of nodes. It relates to the to the perspective that importance relates to where a node is located with respect to the paths in the network graph. According to Freeman, it is defined as: \[c_{B}(v)=\frac{\sigma (s,t|v)}{\sum_{s \neq t \neq v \in V}\sigma (s,t)}\] where \(\sigma(s,t|v)\) is the total number of shortest paths between \(s\) and \(t\) that pass through \(v\), and \(\sigma (s,t)\) is the total number of shortest paths between \(s\) and \(t\) regardless of whether or not they pass through \(v\).

rr bw.tbnet <- betweenness(tbnet2) V(tbnet2)$betweenness <- bw.tbnet summary(bw.tbnet)

     Min.   1st Qu.    Median      Mean   3rd Qu.      Max. 
   0.0000    0.8075   12.4900   99.0600   30.5600 3077.0000 

Eigenvector centrality: seeks to capture the idea that the more central the neighbors of a node are, the more central that node itsel is. According to Bonacich and Katz [Page 48, book Kolaczyk and Csardi, 2nd edition, 2009], the Eigenvector centrality measure is defined as: \[c_{E_i}(v)=\alpha \sum_{\{u,v\}\in E}c_{E_i}(u)\]

Where the vector \(\mathbf{c}_{E_i}=(c_{E_i}(1),\dots ,c_{E_i}(N_v))^T\) is the solution to the eigenvalue problem \(\mathbf{Ac}_{E_i}=\alpha^{-1}\mathbf{c}_{E_i}\), where \(\mathbf{A}\) is the adjacency matrix for the network \(G\). According to Bonacich, an optimal choice of \(\alpha^{-1}\) is the largest eigenvalue of \(\mathbf{A}\).

rr ev.tbnet <- evcent(tbnet2)\(vector V(tbnet2)\)eigenv <- ev.tbnet summary(ev.tbnet)

   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
0.00000 0.04589 0.08708 0.13830 0.15230 1.00000 

Visualization highliting Hubs and Authorities

rr V(tbnet2)\(hubScore <- hub.score(tbnet2)\)vector V(tbnet2)\(authScore <- authority.score(tbnet2)\)vector summary(tbnet2)

IGRAPH UNW- 173 1502 -- 
+ attr: affil (v/c), numPub (v/n), place (v/c), country (v/c), name (v/c), timesCited (v/n), id (v/c), degree (v/n),
| closeness (v/n), betweenness (v/n), eigenv (v/n), hubScore (v/n), authScore (v/n), key (e/x), subject (e/x), year
| (e/x), wosid (e/x), journal (e/x), title (e/x), timesCited (e/n), doi (e/x), weight (e/n)

Note that the Hub scores and Authority scores are exactly equal to the eigenvectors of the nodes in the network. An exploration of the auth_data frame below helps the reader notice the remark.

rr auth_data <- data.frame(id=as.numeric(V(tbnet2)\(id),name=V(tbnet2)\)name,numPub=as.numeric(V(tbnet2)\(numPub),timesCited=as.numeric(V(tbnet2)\)timesCited),degree=V(tbnet2)\(degree,closeness=V(tbnet2)\)closeness,betweenness=V(tbnet2)\(betweenness,eigenV=V(tbnet2)\)eigenv,hubScore=V(tbnet2)\(hubScore,authScore=V(tbnet2)\)authScore) auth_data\(affiliation<-V(tbnet2)\)place auth_data\(city<-V(tbnet2)\)affil auth_data\(country<-V(tbnet2)\)country auth_data\(city[which(auth_data\)city==‘LONDON WC1E 7HT’)]<-‘LONDON’ auth_data\(city[which(auth_data\)city==‘LONDON WC1’)]<-‘LONDON’ auth_data\(city[which(auth_data\)city==‘LONDON NW3 2QG’)]<-‘LONDON’ auth_data\(country[which(auth_data\)city==‘COTONOU’ & auth_data\(country=='FRANCE')]<-'BENIN' auth_data\)country[which(V(tbnet2)$country==‘MA USA’)]<-‘USA’ auth_data\(city[which(auth_data\)city==‘BOBO DIOULASSO 01’)]<-‘BOBO DIOULASSO’ # Obtaining coordinates # library(ggmap) # cord<-unlist(geocode(paste(auth_data\(city[1],auth_data\)country[1],sep=‘,’))) # longlat<-data.frame(id=auth_data\(id, name=auth_data\)name, long=coord[1:1792],lat=coord[1793:3584]) # write.csv(longlat,file = ‘~/Documents/sna/graphs/auth_coordinates.csv’) # Getting node information # longlat<-read.csv(‘./graphs/auth_coordinates.csv’,header = T) # longlat<-subset(longlat, select = -c(X)) # auth_data\(long<-longlat\)long # auth_data\(lat<-longlat\)lat # Getting edge list and related information # eList<-get.edgelist(tbnet2,names = T) # colnames(eList)<-c(‘source’,‘target’) # eList<-as.data.frame(eList) # new1<-eList # new2<-eList # new1[]<-auth_data\(long[match(unlist(eList),auth_data\)name)] # new2[]<-auth_data\(lat[match(unlist(eList),auth_data\)name)] # edges<-data.frame(id=1:length(E(tbnet2)),source=eList\(source,target=eList\)target,weight=E(tbnet2)\(weight,timesCited=E(tbnet2)\)timesCited,long_source=new1\(source,lat_source=new2\)source,long_target=new1\(target,lat_target=new2\)target)

Characterizing Edges: Edge betweenness centrality extends from the notion of vertex centrality by assigning to each edge a value reflecting the number of shortest paths traversing that edge. We compute edge betweenness to assess which co-authorship collaborations are important for the flow of information. We then present the 10 most important collaborations in our tuberculosis co-authorship network.

rr eb <- edge.betweenness(tbnet2) E(simplify(tbnet))[order(eb, decreasing=T)[1:10]]

+ 10/1502 edges (vertex names):
 [1] ODOUN MATHIEU  --GNINAFON MARTIN   FAIHUN FRANK   --DE JONG BOUKE C   ODOUN MATHIEU  --TREBUCQ ARNAUD   
 [4] ZELLWEGER J P  --GNINAFON MARTIN   TREBUCQ ARNAUD --ADJONOU CHRISTINE ODOUN MATHIEU  --WACHINOU PRUDENCE
 [7] AFFOLABI DISSOU--BAHSOW OUMOU      AFFOLABI DISSOU--TOUNDOH N         AFFOLABI DISSOU--BEKOU W          
[10] AFFOLABI DISSOU--MAKPENON A       

Characterizing Network cohesion:

In this section, we are going to assess the extent to which subsets of authors are cohesive with the respect to their relation in the co-authorship network. Specifically, we aim at determining if collaborators (co-authors) of a given author tend to collaborate as well. What subset of collaborating authors tend to be more productive in our network? While there are many techniques to determine network cohesion, we choose to investigate local triads and global giant components, cliques detection as well as clustering or communities detection in our tuberculosis co-authorship network.

Cliques: According to Kolaczyk and Csardi (2009), cliques are defined as complete subgraphs such that all nodes within the subset are connected by edges. We compute the number of cliques in our tuberculosis co-authorship network, then compute the number and size of the maximal cliques.

rr clique.number(tbnet2)

[1] 28

Our tuberculosis co-authorship network contains 28 cliques.

rr table(sapply(maximal.cliques(tbnet2), length))


 3  4  5  6  7  8  9 11 12 13 15 16 19 21 28 
 1  5  7  5  5 10  4  4  1  1  1  2  1  1  1 

Density and related notions of relative frequency: Defined as the frequency of realized edges relative to potential edges, the density of a subgraph \(H\) in \(G\) provides a measure of how close \(H\) is to be a clique in \(G\). Density values varie between 0 and 1: \[den(H)=\frac{|E_H|}{|V_H|(V_H-1)/2}\] Here we compute the general density of our tuberculosis co-authorship network.

rr graph.density(tbnet2)

[1] 0.1009544

We assess the relative frequency of \(G\) by computing its transitivity defined as: \[cl_T = \frac{3\tau_\Delta (G)}{\tau_3 (G)}\] where \(\tau_\Delta (G)\) is the number of triangles in \(G\), and \(\tau_3 (G)\) is the number of connected triples (sometimes referred to as 2-star). This measure is also referred to as the fraction of transitive triples. It represents a measure of global clustering of \(G\) summarizing the relative frequency with which connected triples close to form triangles.

rr transitivity(tbnet2)

[1] 0.6305286

Another analogue of this measure is the local transitivity defined as: \[cl(v)=\tau_\Delta (v)/\tau_3 (v)\] where \(\tau_\Delta (v)\) denotes the number of triangles in \(G\) into which \(v \in V\) falls and \(\tau_3 (v)\) is the number of connected triples in \(G\) for which the two edges are both incident to \(v\). Here we compute the local transitivity for all the nodes in our tuberculosis co-authorship network.

rr tr<-transitivity(tbnet2,‘local’,vids = 1:length(V(tbnet2))) V(tbnet2)\(transitivity<-tr auth_data\)transitivity<-tr

Connectivity, Cuts, and Flows: In this section, we measure how close our tuberculosis co-authorship is close to separate into distincts subgraphs. We are also interested in assessing how well information flows in the network. We first start with the concept of connectedness. Since our network is an undirected graph, we do not consider the idea of weak and strong connectivity. A graph \(G\) is said to be connected if every node in \(G\) is reachable from every other node.

rr is.connected(tbnet2)

[1] FALSE

From the output above, we clearly conclude that our co-authorship network is not connected.

Often time, one of the connected components can dominate the others, hence the idea of giant component. Let’s then census our co-authorship:

rr comps<-decompose.graph(tbnet2) table(sapply(comps,vcount))


 16 157 
  1   1 

From the output of the census of all connected components of the network above, we can see that there are 2 main components containing respectively 16 and 157 nodes. There is a giant component containing \(157/173\approx 90.8\%\) of all the vertices in the network.

We further devote closer attention to this giant component.

rr tbnet2.gc <- decompose.graph(tbnet2)[[1]] summary(tbnet2.gc)

IGRAPH UNW- 157 1382 -- 
+ attr: affil (v/c), numPub (v/n), place (v/c), country (v/c), name (v/c), timesCited (v/n), id (v/c), degree (v/n),
| closeness (v/n), betweenness (v/n), eigenv (v/n), hubScore (v/n), authScore (v/n), transitivity (v/n), key (e/x),
| subject (e/x), year (e/x), wosid (e/x), journal (e/x), title (e/x), timesCited (e/n), doi (e/x), weight (e/n)

Let’s plot this giant component:

rr plot(tbnet2.gc, layout=layout.kamada.kawai(tbnet2.gc), vertex.label=NA, vertex.size=2, edge.width=0.08, vertex.color=‘lightblue’)

One important characteristic observed in giant component is the so-called small-world property which refers to the situation wherein the shortest-path distance between pairs of nodes is generally small and the clustering is relatively high. For our tuberculosis co-authorship network, let’s compute the average path length

rr average.path.length(tbnet2.gc)

[1] 2.126164

and the longest of paths

rr diameter(tbnet2.gc)

[1] 5

Let’s assess the transitivity of our giant component:

rr transitivity(tbnet2.gc)

[1] 0.6140667

Interpretation: We can see that the average path length of the giant component of our co-authorship network is small and the longest of paths is not much bigger. Hence our giant component has all the characteristics of a small-world. In addition, the clustering in this network is high indicating that 61% of the connected triples are close to form triangles.

We investigate the concepts of vertex and edge cuts derived from the concept of vertex(edge) connectivity. The vertex (edge) connectivity of a graph \(G\) is the largest integer such that \(G\) is k-vertex- (edge-) connected.

rr vertex.connectivity(tbnet2.gc)

[1] 1

rr edge.connectivity(tbnet2.gc)

[1] 2

In the case of the giant component of our co-authorship network, the vertex connectivity is equal to 1 while the edge connectivity is equal to 2, thus requires the removal of only a single well-chosen node (author) or 2 collaboration ties in order to break this subgraph into additional components.

A set of nodes (edges) that disconnects the graph is called a vertex cut (edge cut). A single node (author) that disconnects of such vertices is called a cut vertex and can provide a sense of where a network is vulnerable. Let’s identify such weak points in our co-authorship network:

rr tbnet2.cut.vertices <- articulation.points(tbnet2.gc) tbnet2.cut.vertices

+ 1/157 vertex, named:
[1] WACHINOU PRUDENCE

The above listed author constitutes the only weak articulation point of our co-authorship network but also the most important nodes of our network.

rr length(tbnet2.cut.vertices)

[1] 1

In our tuberculosis co-authorship network, less than 1% of the nodes are cut vertices meaning that the vulnerability of the network is dependant on a very small set of authors in the co-authorship network.

Graph Partitioning: Regularly framed as community detection problem, graph partitioning is an unsupervized method used in the analysis of network data to find subsets of nodes that demonstrate a ‘cohesiveness’ with respect to thei underlying relational patterns. Cohesive subsets of nodes generally are well connected among themselves and are well separated from the other nodes in the graph. Here, we perform two well established methods of graph partitioning: Hierarchical clustering and Spectral clustering.

Hierarchical Clustering: Hierarchical clustering methods are of two kinds: - agglomerative: “based on the successive coarsening of partitions through the process of merging”, it uses modularity as metrics. - divisive: “based on the successive refinement of partitions through the process of splitting”

Here, we apply the agglomerative method on our tuberculosis co-authorship network:

rr com.tbnet2 <- fastgreedy.community(tbnet2) V(tbnet2)\(community <- com.tbnet2\)membership length(com.tbnet2)

[1] 6

The agglomerative hierarchical clustering identifies 6 communities in our co-authorship network.

rr sizes(com.tbnet2)

Community sizes
 1  2  3  4  5  6 
58 25 16 31 14 29 

The largest community contains 58 authors. Medium size communities contain between 14 and 29 authors.

Let’s now visualize the communities:

rr plot(com.tbnet2, tbnet2,vertex.label=’’, layout=l.tbnet2, mark.groups = NULL, # vertex.size = 3, # edge.color = memb.tbnet2, edge.width = 0.08, vertex.size=1+4 * sqrt(bw.tbnet/1000) )

rr gc.com <- fastgreedy.community(tbnet2.gc) length(gc.com)

[1] 9

There are 9 communities in the giant component of our network.

rr sizes(gc.com)

Community sizes
 1  2  3  4  5  6  7  8  9 
49 25 12 12  5  4 14  5 31 

The 9 communities contain between 5 and 49 authors.

Let’s now visualize the 9 communities in the giant component:

rr l.gc<-layout.kamada.kawai(tbnet2.gc) bw.gc<-betweenness(tbnet2.gc) V(tbnet2.gc)$community <- membership(gc.com)

rr plot(gc.com, tbnet2.gc,vertex.label=’’, # layout=l.tbnet2, layout = l.gc, mark.groups = NULL, # vertex.size = 3, # edge.color = memb.tbnet2, edge.width = 0.06, vertex.size=1+4 * sqrt(bw.gc/1000) )

Let’s plot the 9 communities separately:

rr par(cex.main=3) par(mfrow=c(5,2)) # Plot Original giant component graph plot(gc.com, tbnet2.gc,vertex.label=‘’, main = ’Main component’, # layout=l.tbnet2, layout = l.gc, mark.groups = NULL, # vertex.size = 3, # edge.color = memb.tbnet2, edge.width = 0.05, vertex.size=1+4 * sqrt(bw.gc/1000) ) for(i in 1:9){ g <- which(V(tbnet2.gc)\(community==i) G.group <- subgraph(tbnet2.gc, g) plot(G.group,vertex.label='', main = paste('Community/Partition ',i), # layout=l.tbnet2, layout = l.gc[g,], mark.groups = NULL, vertex.color = gc.com\)membership[g], # vertex.size = 3, # edge.color = memb.tbnet2, edge.width = 0.06, vertex.size=1+4 * sqrt(bw.gc/1000) ) }

At structural_properties.c:1945 :igraph_subgraph is deprecated from igraph 0.6, use igraph_induced_subgraph instead

We finally save all our generated R objects for later use.

rr save(tbnet, file = ‘./Rdata/TBnet.rda’) save(tbnet2, file = ‘./Rdata/TBnet2.rda’) save(auth_data, file = ‘./Rdata/TBauth_data.rda’) save(edges, file = ‘./Rdata/TBedges.rda’) save(tbnet2.gc, file = ‘./Rdata/TBnet2.gc.rda’)

rr # source(‘plotly_map.R’)

\(\\\)

\(\\\) \(\\\)

NEXT TUTORIAL: Mathematical Modeling for Network Graphs

LS0tCnRpdGxlOiAiU29jaWFsIE5ldHdvcmsgQW5hbHlzaXMgb2YgdGhlIFR1YmVyY3Vsb3NpcyBDby1hdXRob3JzaGlwIG5ldHdvcmsgZnJvbSBCZW5pbiIKYXV0aG9yOiAiUm9zZXJpYyBBem9uZGVrb24iCm91dHB1dDoKICBodG1sX25vdGVib29rOiBkZWZhdWx0CiAgd29yZF9kb2N1bWVudDogZGVmYXVsdAotLS0KJFxcJAokXFwkCgpUaGlzIHByb2plY3QgaXMgcGFydCBvZiBteSBQaEQgZGlzc2VydGF0aW9uIHByb2plY3QuIFRoZSBuZXR3b3JrIGhhcyBhbHJlYWR5IGJlZW4gYXNzZW1ibGVkLgoKTGV0J3MgZmlyc3QgbG9hZCB0aGUgcmVxdWlyZWQgcGFja2FnZXMgYW5kIHRoZSBuZXR3b3JrIGZpbGU6CiRcXCQKCkxvYWRpbmcgbGlicmFyaWVzLi4uLgpgYGB7cix3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KGlncmFwaCkKYGBgCiRcXCQKCioqRGVzY3JpcHRpdmUgTmV0d29yayBBbmFseXNpcyoqCmBgYHtyfQojIHRibmV0IDwtIHJlYWRfZ3JhcGgoJ34vRG9jdW1lbnRzL3NuYS9ncmFwaHNfb2xkL0NBbmV0LmdyYXBobWwnLCBmb3JtYXQgPSAnZ3JhcGhtbCcpCnRibmV0IDwtIHJlYWRfZ3JhcGgoJy4vZ3JhcGhzL1RCbmV0LmdyYXBobWwnLCBmb3JtYXQgPSAnZ3JhcGhtbCcpCmBgYAokXFwkCgpHZXQgYSBzdW1tYXJ5IG9mIHRoZSBuZXR3b3JrOgpgYGB7cn0Kc3VtbWFyeSh0Ym5ldCkKYGBgCgoqSW50ZXJwcmV0YXRpb246KiBPdXIgY28tYXV0aG9yc2hpcCBuZXR3b3JrIGlzIGFuIHVuZGlyZWN0ZWQgbXVsdGlncmFwaCAocGFyYWxsZWwgZWRnZXMpIHdpdGggKioxNzMgYXV0aG9ycyoqIGFuZCAqKjE5Mzcgc2NpZW50aWZpYyBjb2xsYWJvcmF0aW9ucyoqLiBFYWNoIG5vZGUgKGF1dGhvcikgaW4gdGhlIG5ldHdvcmsgaGFzIDcgYXR0cmlidXRlczogbmFtZSwgY291bnRyeSwgcGxhY2UsIGFmZmlsLCBudW1QdWIsIHRpbWVzQ2l0ZWQgYW5kIGlkLiBFYWNoIGVkZ2UgaGFzIDggYXR0cmlidXRlczoga2V5LCBzdWJqZWN0LCBhYnN0cmFjdCwgeWVhciwgd29zaWQgKFdlYiBvZiBzY2llbmNlIElkZW50aWZpY2F0aW9uIG51bWJlciksIGpvdXJuYWwsIHRpdGxlIGFuZCBkb2kuCgoKKkNvbXB1dGluZyBOb2RlIENlbnRyYWxpdHkgTWVhc3VyZXM6KgpXZSBjb21wdXRlIGNlbnRyYWxpdHkgbWVhc3VyZXMgc3VjaCBhcyBkZWdyZWUgKG51bWJlciBvZiB0aWVzIHRvIGEgZ2l2ZW4gYXV0aG9yKSwgYmV0d2Vlbm5lc3MgKG51bWJlciBvZiBzaG9ydGVzdCBwYXRocyBiZXR3ZWVuIGFsdGVycyB0aGF0IGdvIHRocm91Z2ggYSBwYXJ0aWN1bGFyIGF1dGhvciksIGNsb3NlbmVzcyAobnVtYmVyIG9mIHN0ZXBzIHJlcXVpcmVkIGZvciBhIHBhcnRpY3VsYXIgYXV0aG9yIHRvIGFjY2VzcyBldmVyeSBvdGhlciBhdXRob3IgaW4gdGhlIG5ldHdvcmspIGFuZCBlaWdlbnZlY3RvcnMgKGRlZ3JlZSB0byB3aGljaCBhbiBhdXRob3IgaXMgY29ubmVjdGVkIHRvIG90aGVyIHdlbGwgY29ubmVjdGVkIGF1dGhvcnMgaW4gdGhlIG5ldHdvcmspLCBicm9rZXJhZ2UgKGRlZ3JlZSB0byB3aGljaCBhbiBhY3RvciBvY2N1cGllcyBhIGJyb2tlcmFnZSBwb3NpdGlvbiBhY3Jvc3MgYWxsIHBhaXJzIG9mIGFsdGVycykgCgoqRGVncmVlIGNlbnRyYWxpdHkqCkxldCdzIGZpcnN0IGNvbXB1dGUgdGhlIGRlZ3JlZSBhbmQgc3RyZW5ndGggb2YgdGhlIG5vZGVzIGluIHRoZSBuZXR3b3JrOgpgYGB7cn0KZC50Ym5ldCA8LSBkZWdyZWUodGJuZXQpCnMudGJuZXQgPC0gZ3JhcGguc3RyZW5ndGgoc2ltcGxpZnkodGJuZXQpKSAjIGZvciB3ZWlnaHRlZCBncmFwaApgYGAKIApOb3csIGxldCdzIHBsb3QgYSBoaXN0b2dyYW0gb2YgdGhlIGRlZ3JlZSBhbmQgc3RyZW5ndGggZGlzdHJpYnV0aW9uczoKYGBge3IsZmlnLndpZHRoPTh9CnBhcihtZnJvdz1jKDEsMikpCmhpc3QoZC50Ym5ldCwgY29sPSJsaWdodGJsdWUiLCB4bGFiPSJWZXJ0ZXggRGVncmVlIiwgeWxhYj0iRnJlcXVlbmN5IiwgbWFpbj0iRGVncmVlIGRpc3RyaWJ1dGlvbiIpCmhpc3Qocy50Ym5ldCwgY29sPSJwaW5rIiwgeGxhYj0iVmVydGV4IFN0cmVuZ3RoIiwgeWxhYj0iRnJlcXVlbmN5IiwgbWFpbj0iU3RyZW5ndGggZGlzdHJpYnV0aW9uIikKYGBgCipJbnRlcnByZXRhdGlvbjoqIFdoaWxlIHRoZXJlIGlzIGEgc3Vic3RhbnRpYWwgbnVtYmVyIG9mIG5vZGVzIG9mIHF1aXRlIGxvdyBkZWdyZWUsIHRoZXJlIGFyZSBhbHNvIGEgbm9uLXRyaXZpYWwgbnVtYmVyIG9mIG5vZGVzIHdpdGggaGlnaGVyIG9yZGVyIG9mIGRlZ3JlZSBtYWduaXR1ZGVzLiBHaXZlbiB0aGUgbmF0dXJlIG9mIHRoaXMgZGlzdHJpYnV0aW9uLCBhIGxvZy1sb2cgc2NhbGUgaXMgbW9yZSBlZmZlY3RpdmUgYXQgc3VtbWFyaXppbmcgdGhlIGRlZ3JlZSBpbmZvcm1hdGlvbi4KCkZyb20gaGVyZSwgdGhlIHByb2Nlc3NpbmcgcmVxdWlyZXMgb3VyIG5ldHdvcmsgdG8gYmUgYSBzaW1wbGUgZ3JhcGguIFdlIHdvdWxkIHRoZXJlZm9yZSBjaGFuZ2Ugb3VyIG11bHRpZ3JhcGggdG8gYSBncmFwaCBvYmplY3QuCmBgYHtyfQpFKHRibmV0KSR3ZWlnaHQgPC0gMQp0Ym5ldDIgPC0gc2ltcGxpZnkodGJuZXQsIGVkZ2UuYXR0ci5jb21iID0gbGlzdCh3ZWlnaHQ9InN1bSIsdGltZXNDaXRlZD0ic3VtIixudW1QdWI9InN1bSIsa2V5PSJjb25jYXQiLHN1YmplY3Q9ImNvbmNhdCIseWVhcj0iY29uY2F0Iix3b3NpZD0iY29uY2F0Iixqb3VybmFsPSJjb25jYXQiLHRpdGxlPSJjb25jYXQiLGRvaT0iY29uY2F0IikpCnN1bW1hcnkodGJuZXQyKQpgYGAKCjwhLS0gIFRoZSBuZXdseSBjb21wdXRlZCB1bmRpcmVjdGVkIGdyYXBoIGNvbnRhaW5zICoxNzkyIG5vZGVzKiBhbmQgKjk1NzA3IGVkZ2VzKi4gTm90ZSB0aGF0IHRoZSBzaW1wbGlmeSBmdW5jdGlvbiBpbiBSIGRvZXMgbm90IGNhcHR1cmUgdGhlIG51bWJlciBvZiBtdWx0aXBsZSBjb2xsYWJvcmF0aW9ucyBiZXR3ZWVuIGF1dGhvcnMuIEluIGEgcHJldmlvdXMgdHV0b3JpYWwsIHdlIGhhdmUgY29tcHV0ZWQgYSBzaW1wbGUgdmVyc2lvbiBvZiB0aGUgdGJuZXQgbmV0d29yay4gTGV0J3MgaW5zdGVhZCBvZiB1c2luZyB0aGUgKnNpbXBsaWZ5KCkqIGZ1bmN0aW9uLCBsb2FkIHRibmV0MiBmcm9tIGl0cyBwcmV2aW91c2x5IGNvbXB1dGVkIC5ncmFwaG1sIGZvcm1hdDogLS0+Cgo8IS0tIGBgYHtyfSAtLT4KPCEtLSAjIHRibmV0MyA8LSByZWFkX2dyYXBoKCcuL2dyYXBoc19vbGQvQ0FuZXRfZ3JhcGguZ3JhcGhtbCcsIGZvcm1hdCA9ICdncmFwaG1sJykgLS0+CjwhLS0gdGJuZXQzIDwtIHJlYWRfZ3JhcGgoJy4vZ3JhcGhzL0NBbmV0X3dlaWdodC5ncmFwaG1sJywgZm9ybWF0ID0gJ2dyYXBobWwnKSAtLT4KPCEtLSBWKHRibmV0MykkbmFtZSA8LSBWKHRibmV0MikkbmFtZSAtLT4KPCEtLSBzdW1tYXJ5KHRibmV0MykgLS0+CjwhLS0gYGBgIC0tPgo8IS0tICpJbnRlcnByZXRhdGlvbjoqIE5vdGUgaGVyZSB0aGF0IHRibmV0MyBoYXMgdGhlIGV4YWN0IG51bWJlciBvZiBub2RlcyBhbmQgZWRnZXMgYXMgdGJuZXQyLiBUaGUgb25seSBkaWZmZXJlbmNlIGlzIHRoZXJlIGlzIGEgd2VpZ2h0IGF0dHJpYnV0ZSBvbiBldmVyeSBlZGdlLiBXZSBoYXZlIHJlbW92ZWQgdGhlIG5hbWUgYXR0cmlidXRlIG9uIGVhY2ggbm9kZSBmb3IgY29udmVuaWVuY2UgaXNzdWUuIC0tPgoKKlZpc3VhbGl6YXRpb24qCgpOb3csIGxldCdzIG5vdyB2aXN1YWxpemUgb3VyIGNvLWF1dGhvcnNoaXAgZ3JhcGggbmV0d29yayB0Ym5ldDIgdXNpbmcgdGhlIEthbWFkYSBhbmQgS2F3YWkgbGF5b3V0OgpgYGB7cn0KIyBsLnRibmV0IDwtIGxheW91dC5rYW1hZGEua2F3YWkodGJuZXQpCiMgbC50Ym5ldDIgPC0gbGF5b3V0LmthbWFkYS5rYXdhaSh0Ym5ldDIpCmwudGJuZXQyIDwtIGxheW91dC5rYW1hZGEua2F3YWkodGJuZXQyKQpgYGAKCgpgYGB7cixmaWcuaGVpZ2h0PTE1LGZpZy53aWR0aD0xNX0KIyBwbG90KHRibmV0LCBsYXlvdXQ9bC50Ym5ldCwgdmVydGV4LmxhYmVsPU5BLCB2ZXJ0ZXguc2l6ZT0yKQojIHBsb3QodGJuZXQyLCBsYXlvdXQ9bC50Ym5ldDIsIHZlcnRleC5sYWJlbD1OQSwgdmVydGV4LnNpemU9MiwgdmVydGV4LmNvbG9yPSdyZWQnKQpwbG90KHRibmV0MiwgbGF5b3V0PWwudGJuZXQyLCB2ZXJ0ZXgubGFiZWw9TkEsIHZlcnRleC5zaXplPTIsIHZlcnRleC5jb2xvcj0nYmx1ZScpICMgV2UgdXNlIGwudGJuZXQyIGFzIGxheW91dApgYGAKCmBgYHtyfQpkLnRibmV0IDwtIGRlZ3JlZSh0Ym5ldDIpICMgRGVncmVlIG9mIHNpbXBsZSBncmFwaCB0Ym5ldDIKVih0Ym5ldDIpJGRlZ3JlZSA8LSBkLnRibmV0CmRkLnRibmV0IDwtIGRlZ3JlZS5kaXN0cmlidXRpb24odGJuZXQyKSAjIENvbXB1dGUgZGVncmVlIGRpc3RyaWJ1dGlvbgpkIDwtIDE6bWF4KGQudGJuZXQpLTEKaW5kIDwtIChkZC50Ym5ldCAhPSAwKQphLm5uLmRlZy50Ym5ldCA8LSBncmFwaC5rbm4odGJuZXQyLCBWKHRibmV0MikpJGtubgptZWFuKGQudGJuZXQpCmBgYAoKTm93LCBwbG90aW5nIGRlZ3JlZSBkaXN0cmlidXRpb24gb2YgdGhlIHNpbXBsaWZpZWQgZ3JhcGguCmBgYHtyLGZpZy53aWR0aD04fQpwYXIobWZyb3c9YygxLDIpKQpoaXN0KGQudGJuZXQsIGNvbD0ibGlnaHRibHVlIiwgeGxhYj0iVmVydGV4IERlZ3JlZSIsIHlsYWI9IkZyZXF1ZW5jeSIsIG1haW49IkRlZ3JlZSBkaXN0cmlidXRpb24iKQpwbG90KGRbaW5kXSwgZGQudGJuZXRbaW5kXSwgbG9nPSJ4eSIsIGNvbD0iYmx1ZSIsIHR5cGU9J3AnLCB4bGFiPWMoIkxvZy1EZWdyZWUiKSwgeWxhYj1jKCJMb2ctSW50ZW5zaXR5IiksbWFpbj0iTG9nLUxvZyBEZWdyZWUgRGlzdHJpYnV0aW9uIikKYGBgCipJbnRlcnByZXRhdGlvbjoqIEZyb20gdGhlIHBsb3Qgb24gdGhlIHJpZ2h0LCB0aGUgZGVncmVlcyBvZiB0aGUgbm9kZSBubyBjbGVhciBkaXN0cmlidXRpb24uIExldCdzIGludmVzdGlnYXRlIHRoZSBtYW5uZXIgaW4gd2hpY2ggbm9kZXMgb2YgZGlmZmVyZW50IGRlZ3JlZXMgYXJlIGxpbmtlZCB3aXRoIGVhY2ggb3RoZXIgaW4gdGhlIGNvYXV0aG9yc2hpcCBuZXR3b3JrLiBGb3IgdGhpcyBwdXJwb3NlLCB3ZSBicmluZyBpbiB0aGUgbm90aW9uIG9mIHRoZSBhdmVyYWdlIGRlZ3JlZSBvZiB0aGUgbmVpZ2hib3JzIG9mIGEgZ2l2ZW4gbm9kZS4gV2UgdGhlbiBwbG90IHRoZSBhdmVyYWdlIG5laWdoYm9yIGRlZ3JlZSBhZ2FpbnN0IG5vZGUgZGVncmVlLgoKYGBge3IsZmlnLmhlaWdodD03fQpwbG90KGQudGJuZXQsIGEubm4uZGVnLnRibmV0LCBsb2c9Inh5IiwgY29sPSJnb2xkZW5yb2QiLCB0eXBlPSdwJywgeGxhYj1jKCJMb2cgVmVydGV4IERlZ3JlZSIpLCB5bGFiPWMoIkxvZyBBdmVyYWdlIE5laWdoYm9yIERlZ3JlZSIpKQpgYGAKKkludGVycHJldGF0aW9uOiogVGhlIHBsb3QgYWJvdmUgc3VnZ2VzdHMgdGhhdCB3aGlsZSB0aGVyZSBpcyBhIHRlbmRlbmN5IG9mIG5vZGVzIG9mIGhpZ2hlciBkZWdyZWVzIHRvIGxpbmsgd2l0aCBzaW1pbGFyIG5vZGVzLCBub2RlcyBvZiBsb3dlciBkZWdyZWUgdGVuZCB0byBsaW5rIHdpdGggbm9kZXMgb2YgYm90aCBsb3dlciBhbmQgaGlnaGVyIGRlZ3JlZXMuIEluIG90aGVyIHdvcmRzLCB3aGlsZSBwcm9taW5lbnQgYXV0aG9ycyB3aXRoIGltcG9ydGFudCBjb2xsYWJvcmF0aW9ucyB0ZW5kIHRvIGNvbGxhYm9yYXRlIHdpdGggc2ltaWxhciBhdXRob3JzLCB5b3VuZyBvciBsZXNzIHByb2xpZmljIGF1dGhvcnMgdGVuZCB0byBjb2xsYWJvcmF0ZSB3aXRoIGJvdGggcHJvbGlmaWMgYW5kIGF1dGhvcnMgd2l0aCB2ZXJ5IGZldyBjb2xsYWJvcmF0aW9ucy4KCgpMZXQncyBjb21wdXRlIHRoZSBvdGhlciAzIG5vZGUgY2VudHJhbGl0eSBtZWFzdXJlczoKYGBge3J9CkEgPC0gZ2V0LmFkamFjZW5jeSh0Ym5ldDIsIHNwYXJzZSA9IEZBTFNFKQpnIDwtIG5ldHdvcms6OmFzLm5ldHdvcmsubWF0cml4KEEpCmBgYAoKCipDbG9zZW5lc3MgY2VudHJhbGl0eToqIGNhcHR1cmVzIHRoZSBub3Rpb24gdGhhdCBhIG5vZGUgaXMgY2VudHJhbCBpZiBpdCBjbG9zZSB0byBtYW55IG90aGVyIG5vZGVzCkNvbnNpZGVyaW5nIGEgbmV0d29yayAkRz0oVixFKSQgd2hlcmUgJFYkIGlzIHRoZSBzZXQgb2Ygbm9kZXMgYW5kICRFJCwgdGhlIHNldCBvZiBlZGdlcywgdGhlIGNsb3NlbmVzcyBjZW50cmFsaXR5ICRjX3tDbH0odikkIG9mIGEgbm9kZSAkdiQgaXMgZGVmaW5lZCBhczogCiQkY197Q2x9KHYpPVxmcmFjezF9e1xzdW1fe3VcaW4gVn1kaXN0KHYsdSl9JCQKJGRpc3Qodix1KSQgaXMgZGVmaW5lZCBhcyB0aGUgZ2VvZGVzaWMgZGlzdGFuY2UgYmV0d2VlbiB0aGUgbm9kZXMgJHUsdiBcaW4gViQuCmBgYHtyfQpjbC50Ym5ldCA8LSBjbG9zZW5lc3ModGJuZXQyKQpWKHRibmV0MikkY2xvc2VuZXNzIDwtIGNsLnRibmV0CnN1bW1hcnkoY2wudGJuZXQpCmBgYApgYGB7cixmaWcuaGVpZ2h0PTE1LGZpZy53aWR0aD0xNX0KcGxvdCh0Ym5ldDIsIGxheW91dD1sLnRibmV0MiwgbWFpbj0iQ2xvc2VuZXNzIGNlbnRyYWxpdHkiLCB2ZXJ0ZXgubGFiZWw9IiIsIHZlcnRleC5zaXplPTAuMjUqIHNxcnQoY2wudGJuZXQqMUU2KSwgdmVydGV4LmNvbG9yPSdsaWdodGJsdWUnKQpgYGAKCgoqQmV0d2VlbmVzcyBjZW50cmFsaXR5Oiogc3VtbWFyaXplcyB0aGUgZXh0ZW50IHRvIHdoaWNoIGEgbm9kZSBpcyBsb2NhdGVkIGJldHdlZW4gb3RoZXIgcGFpcnMgb2Ygbm9kZXMuIEl0IHJlbGF0ZXMgdG8gdGhlIHRvIHRoZSBwZXJzcGVjdGl2ZSB0aGF0IGltcG9ydGFuY2UgcmVsYXRlcyB0byB3aGVyZSBhIG5vZGUgaXMgbG9jYXRlZCB3aXRoIHJlc3BlY3QgdG8gdGhlIHBhdGhzIGluIHRoZSBuZXR3b3JrIGdyYXBoLiBBY2NvcmRpbmcgdG8gRnJlZW1hbiwgaXQgaXMgZGVmaW5lZCBhczoKJCRjX3tCfSh2KT1cZnJhY3tcc2lnbWEgKHMsdHx2KX17XHN1bV97cyBcbmVxIHQgXG5lcSB2IFxpbiBWfVxzaWdtYSAocyx0KX0kJAp3aGVyZSAkXHNpZ21hKHMsdHx2KSQgaXMgdGhlIHRvdGFsIG51bWJlciBvZiBzaG9ydGVzdCBwYXRocyBiZXR3ZWVuICRzJCBhbmQgJHQkIHRoYXQgcGFzcyB0aHJvdWdoICR2JCwgYW5kICRcc2lnbWEgKHMsdCkkIGlzIHRoZSB0b3RhbCBudW1iZXIgb2Ygc2hvcnRlc3QgcGF0aHMgYmV0d2VlbiAkcyQgYW5kICR0JCByZWdhcmRsZXNzIG9mIHdoZXRoZXIgb3Igbm90IHRoZXkgcGFzcyB0aHJvdWdoICR2JC4KYGBge3J9CmJ3LnRibmV0IDwtIGJldHdlZW5uZXNzKHRibmV0MikKVih0Ym5ldDIpJGJldHdlZW5uZXNzIDwtIGJ3LnRibmV0CnN1bW1hcnkoYncudGJuZXQpCmBgYApgYGB7cixmaWcuaGVpZ2h0PTE1LGZpZy53aWR0aD0xNX0KcGxvdCh0Ym5ldDIsIGxheW91dD1sLnRibmV0MiwgbWFpbj0iQmV0d2Vlbm5lc3MgY2VudHJhbGl0eSIsIHZlcnRleC5sYWJlbD0iIiwgdmVydGV4LnNpemU9NCAqIHNxcnQoYncudGJuZXQvMTAwMCksIHZlcnRleC5jb2xvcj0nbGlnaHRncmVlbicpCmBgYAoKCgoqRWlnZW52ZWN0b3IgY2VudHJhbGl0eToqIHNlZWtzIHRvIGNhcHR1cmUgdGhlIGlkZWEgdGhhdCB0aGUgbW9yZSBjZW50cmFsIHRoZSBuZWlnaGJvcnMgb2YgYSBub2RlIGFyZSwgdGhlIG1vcmUgY2VudHJhbCB0aGF0IG5vZGUgaXRzZWwgaXMuIEFjY29yZGluZyB0byBCb25hY2ljaCBhbmQgS2F0eiBbUGFnZSA0OCwgYm9vayBLb2xhY3p5ayBhbmQgQ3NhcmRpLCAybmQgZWRpdGlvbiwgMjAwOV0sIHRoZSBFaWdlbnZlY3RvciBjZW50cmFsaXR5IG1lYXN1cmUgaXMgZGVmaW5lZCBhczoKJCRjX3tFX2l9KHYpPVxhbHBoYSBcc3VtX3tce3Usdlx9XGluIEV9Y197RV9pfSh1KSQkCgpXaGVyZSB0aGUgdmVjdG9yICRcbWF0aGJme2N9X3tFX2l9PShjX3tFX2l9KDEpLFxkb3RzICxjX3tFX2l9KE5fdikpXlQkIGlzIHRoZSBzb2x1dGlvbiB0byB0aGUgZWlnZW52YWx1ZSBwcm9ibGVtICRcbWF0aGJme0FjfV97RV9pfT1cYWxwaGFeey0xfVxtYXRoYmZ7Y31fe0VfaX0kLCB3aGVyZSAkXG1hdGhiZntBfSQgaXMgdGhlIGFkamFjZW5jeSBtYXRyaXggZm9yIHRoZSBuZXR3b3JrICRHJC4gQWNjb3JkaW5nIHRvIEJvbmFjaWNoLCBhbiBvcHRpbWFsIGNob2ljZSBvZiAkXGFscGhhXnstMX0kIGlzIHRoZSBsYXJnZXN0IGVpZ2VudmFsdWUgb2YgJFxtYXRoYmZ7QX0kLgpgYGB7cn0KZXYudGJuZXQgPC0gZXZjZW50KHRibmV0MikkdmVjdG9yClYodGJuZXQyKSRlaWdlbnYgPC0gZXYudGJuZXQKc3VtbWFyeShldi50Ym5ldCkKYGBgCmBgYHtyLGZpZy5oZWlnaHQ9MTUsZmlnLndpZHRoPTE1fQpwbG90KHRibmV0MiwgbGF5b3V0PWwudGJuZXQyLCBtYWluPSJFaWdlbnZlY3RvcnMgY2VudHJhbGl0eSIsIHZlcnRleC5sYWJlbD0iIiwgdmVydGV4LnNpemU9MiAqIHNxcnQoZXYudGJuZXQqMTApLCB2ZXJ0ZXguY29sb3I9J3BpbmsnKQpgYGAKCgoKKlZpc3VhbGl6YXRpb24gaGlnaGxpdGluZyBIdWJzIGFuZCBBdXRob3JpdGllcyoKYGBge3IsZmlnLmhlaWdodD0xNSxmaWcud2lkdGg9MTV9CiMgcGFyKG1mcm93PWMoMSwyKSkKIyBwbG90KHRibmV0MiwgbGF5b3V0PWwudGJuZXQyLCBtYWluPSJIdWJzIiwgdmVydGV4LmxhYmVsPSIiLCB2ZXJ0ZXguc2l6ZT0xMCAqIHNxcnQoaHViLnNjb3JlKHRibmV0MikkdmVjdG9yKSwgdmVydGV4LmNvbG9yPSdncmVlbicpCiMgcGxvdCh0Ym5ldDIsIGxheW91dD1sLnRibmV0MiwgbWFpbj0iQXV0aG9yaXRpZXMiLCB2ZXJ0ZXgubGFiZWw9IiIsIHZlcnRleC5zaXplPTEwICoKIyBzcXJ0KGF1dGhvcml0eS5zY29yZSh0Ym5ldDIpJHZlY3RvciksIHZlcnRleC5jb2xvcj0ncmVkJykKYGBgCgpgYGB7cn0KVih0Ym5ldDIpJGh1YlNjb3JlIDwtIGh1Yi5zY29yZSh0Ym5ldDIpJHZlY3RvcgpWKHRibmV0MikkYXV0aFNjb3JlIDwtIGF1dGhvcml0eS5zY29yZSh0Ym5ldDIpJHZlY3RvcgpzdW1tYXJ5KHRibmV0MikKYGBgCk5vdGUgdGhhdCB0aGUgSHViIHNjb3JlcyBhbmQgQXV0aG9yaXR5IHNjb3JlcyBhcmUgZXhhY3RseSBlcXVhbCB0byB0aGUgZWlnZW52ZWN0b3JzIG9mIHRoZSBub2RlcyBpbiB0aGUgbmV0d29yay4gQW4gZXhwbG9yYXRpb24gb2YgdGhlIGF1dGhfZGF0YSBmcmFtZSBiZWxvdyBoZWxwcyB0aGUgcmVhZGVyIG5vdGljZSB0aGUgcmVtYXJrLgoKYGBge3J9CmF1dGhfZGF0YSA8LSBkYXRhLmZyYW1lKGlkPWFzLm51bWVyaWMoVih0Ym5ldDIpJGlkKSxuYW1lPVYodGJuZXQyKSRuYW1lLG51bVB1Yj1hcy5udW1lcmljKFYodGJuZXQyKSRudW1QdWIpLHRpbWVzQ2l0ZWQ9YXMubnVtZXJpYyhWKHRibmV0MikkdGltZXNDaXRlZCksZGVncmVlPVYodGJuZXQyKSRkZWdyZWUsY2xvc2VuZXNzPVYodGJuZXQyKSRjbG9zZW5lc3MsYmV0d2Vlbm5lc3M9Vih0Ym5ldDIpJGJldHdlZW5uZXNzLGVpZ2VuVj1WKHRibmV0MikkZWlnZW52LGh1YlNjb3JlPVYodGJuZXQyKSRodWJTY29yZSxhdXRoU2NvcmU9Vih0Ym5ldDIpJGF1dGhTY29yZSkKYXV0aF9kYXRhJGFmZmlsaWF0aW9uPC1WKHRibmV0MikkcGxhY2UKYXV0aF9kYXRhJGNpdHk8LVYodGJuZXQyKSRhZmZpbAphdXRoX2RhdGEkY291bnRyeTwtVih0Ym5ldDIpJGNvdW50cnkKYXV0aF9kYXRhJGNpdHlbd2hpY2goYXV0aF9kYXRhJGNpdHk9PSdMT05ET04gV0MxRSA3SFQnKV08LSdMT05ET04nCmF1dGhfZGF0YSRjaXR5W3doaWNoKGF1dGhfZGF0YSRjaXR5PT0nTE9ORE9OIFdDMScpXTwtJ0xPTkRPTicKYXV0aF9kYXRhJGNpdHlbd2hpY2goYXV0aF9kYXRhJGNpdHk9PSdMT05ET04gTlczIDJRRycpXTwtJ0xPTkRPTicKYXV0aF9kYXRhJGNvdW50cnlbd2hpY2goYXV0aF9kYXRhJGNpdHk9PSdDT1RPTk9VJyAmIGF1dGhfZGF0YSRjb3VudHJ5PT0nRlJBTkNFJyldPC0nQkVOSU4nCmF1dGhfZGF0YSRjb3VudHJ5W3doaWNoKFYodGJuZXQyKSRjb3VudHJ5PT0nTUEgVVNBJyldPC0nVVNBJwphdXRoX2RhdGEkY2l0eVt3aGljaChhdXRoX2RhdGEkY2l0eT09J0JPQk8gRElPVUxBU1NPIDAxJyldPC0nQk9CTyBESU9VTEFTU08nCgojIE9idGFpbmluZyBjb29yZGluYXRlcwojIGxpYnJhcnkoZ2dtYXApCiMgY29yZDwtdW5saXN0KGdlb2NvZGUocGFzdGUoYXV0aF9kYXRhJGNpdHlbMV0sYXV0aF9kYXRhJGNvdW50cnlbMV0sc2VwPScsICcpKSkKIyBsb25nbGF0PC1kYXRhLmZyYW1lKGlkPWF1dGhfZGF0YSRpZCwgbmFtZT1hdXRoX2RhdGEkbmFtZSwgbG9uZz1jb29yZFsxOjE3OTJdLGxhdD1jb29yZFsxNzkzOjM1ODRdKQojIHdyaXRlLmNzdihsb25nbGF0LGZpbGUgPSAnfi9Eb2N1bWVudHMvc25hL2dyYXBocy9hdXRoX2Nvb3JkaW5hdGVzLmNzdicpCgojIEdldHRpbmcgbm9kZSBpbmZvcm1hdGlvbgojIGxvbmdsYXQ8LXJlYWQuY3N2KCcuL2dyYXBocy9hdXRoX2Nvb3JkaW5hdGVzLmNzdicsaGVhZGVyID0gVCkKIyBsb25nbGF0PC1zdWJzZXQobG9uZ2xhdCwgc2VsZWN0ID0gLWMoWCkpCiMgYXV0aF9kYXRhJGxvbmc8LWxvbmdsYXQkbG9uZwojIGF1dGhfZGF0YSRsYXQ8LWxvbmdsYXQkbGF0CgojIEdldHRpbmcgZWRnZSBsaXN0IGFuZCByZWxhdGVkIGluZm9ybWF0aW9uCiMgZUxpc3Q8LWdldC5lZGdlbGlzdCh0Ym5ldDIsbmFtZXMgPSBUKQojIGNvbG5hbWVzKGVMaXN0KTwtYygnc291cmNlJywndGFyZ2V0JykKIyBlTGlzdDwtYXMuZGF0YS5mcmFtZShlTGlzdCkKIyBuZXcxPC1lTGlzdAojIG5ldzI8LWVMaXN0CiMgbmV3MVtdPC1hdXRoX2RhdGEkbG9uZ1ttYXRjaCh1bmxpc3QoZUxpc3QpLGF1dGhfZGF0YSRuYW1lKV0KIyBuZXcyW108LWF1dGhfZGF0YSRsYXRbbWF0Y2godW5saXN0KGVMaXN0KSxhdXRoX2RhdGEkbmFtZSldCiMgZWRnZXM8LWRhdGEuZnJhbWUoaWQ9MTpsZW5ndGgoRSh0Ym5ldDIpKSxzb3VyY2U9ZUxpc3Qkc291cmNlLHRhcmdldD1lTGlzdCR0YXJnZXQsd2VpZ2h0PUUodGJuZXQyKSR3ZWlnaHQsdGltZXNDaXRlZD1FKHRibmV0MikkdGltZXNDaXRlZCxsb25nX3NvdXJjZT1uZXcxJHNvdXJjZSxsYXRfc291cmNlPW5ldzIkc291cmNlLGxvbmdfdGFyZ2V0PW5ldzEkdGFyZ2V0LGxhdF90YXJnZXQ9bmV3MiR0YXJnZXQpCgpgYGAKCgoqQ2hhcmFjdGVyaXppbmcgRWRnZXM6KgpFZGdlIGJldHdlZW5uZXNzIGNlbnRyYWxpdHkgZXh0ZW5kcyBmcm9tIHRoZSBub3Rpb24gb2YgdmVydGV4IGNlbnRyYWxpdHkgYnkgYXNzaWduaW5nIHRvIGVhY2ggZWRnZSBhIHZhbHVlIHJlZmxlY3RpbmcgdGhlIG51bWJlciBvZiBzaG9ydGVzdCBwYXRocyB0cmF2ZXJzaW5nIHRoYXQgZWRnZS4gV2UgY29tcHV0ZSBlZGdlIGJldHdlZW5uZXNzIHRvIGFzc2VzcyB3aGljaCBjby1hdXRob3JzaGlwIGNvbGxhYm9yYXRpb25zIGFyZSBpbXBvcnRhbnQgZm9yIHRoZSBmbG93IG9mIGluZm9ybWF0aW9uLiBXZSB0aGVuIHByZXNlbnQgdGhlIDEwIG1vc3QgaW1wb3J0YW50IGNvbGxhYm9yYXRpb25zIGluIG91ciB0dWJlcmN1bG9zaXMgY28tYXV0aG9yc2hpcCBuZXR3b3JrLgpgYGB7cn0KZWIgPC0gZWRnZS5iZXR3ZWVubmVzcyh0Ym5ldDIpCkUoc2ltcGxpZnkodGJuZXQpKVtvcmRlcihlYiwgZGVjcmVhc2luZz1UKVsxOjEwXV0KYGBgCgoKKkNoYXJhY3Rlcml6aW5nIE5ldHdvcmsgY29oZXNpb246KgoKSW4gdGhpcyBzZWN0aW9uLCB3ZSBhcmUgZ29pbmcgdG8gYXNzZXNzIHRoZSBleHRlbnQgdG8gd2hpY2ggc3Vic2V0cyBvZiBhdXRob3JzIGFyZSBjb2hlc2l2ZSB3aXRoIHRoZSByZXNwZWN0IHRvIHRoZWlyIHJlbGF0aW9uIGluIHRoZSBjby1hdXRob3JzaGlwIG5ldHdvcmsuIFNwZWNpZmljYWxseSwgKip3ZSBhaW0gYXQgZGV0ZXJtaW5pbmcgaWYgY29sbGFib3JhdG9ycyAoY28tYXV0aG9ycykgb2YgYSBnaXZlbiBhdXRob3IgdGVuZCB0byBjb2xsYWJvcmF0ZSBhcyB3ZWxsKiouICoqV2hhdCBzdWJzZXQgb2YgY29sbGFib3JhdGluZyBhdXRob3JzIHRlbmQgdG8gYmUgbW9yZSBwcm9kdWN0aXZlIGluIG91ciBuZXR3b3JrPyoqCldoaWxlIHRoZXJlIGFyZSBtYW55IHRlY2huaXF1ZXMgdG8gZGV0ZXJtaW5lIG5ldHdvcmsgY29oZXNpb24sIHdlIGNob29zZSB0byBpbnZlc3RpZ2F0ZSBsb2NhbCB0cmlhZHMgYW5kIGdsb2JhbCBnaWFudCBjb21wb25lbnRzLCBjbGlxdWVzIGRldGVjdGlvbiBhcyB3ZWxsIGFzIGNsdXN0ZXJpbmcgb3IgY29tbXVuaXRpZXMgZGV0ZWN0aW9uIGluIG91ciB0dWJlcmN1bG9zaXMgY28tYXV0aG9yc2hpcCBuZXR3b3JrLgoKKkNsaXF1ZXM6KiBBY2NvcmRpbmcgdG8gS29sYWN6eWsgYW5kIENzYXJkaSAoMjAwOSksIGNsaXF1ZXMgYXJlIGRlZmluZWQgYXMgY29tcGxldGUgc3ViZ3JhcGhzIHN1Y2ggdGhhdCBhbGwgbm9kZXMgd2l0aGluIHRoZSBzdWJzZXQgYXJlIGNvbm5lY3RlZCBieSBlZGdlcy4gV2UgY29tcHV0ZSB0aGUgbnVtYmVyIG9mIGNsaXF1ZXMgaW4gb3VyIHR1YmVyY3Vsb3NpcyBjby1hdXRob3JzaGlwIG5ldHdvcmssIHRoZW4gY29tcHV0ZSB0aGUgbnVtYmVyIGFuZCBzaXplIG9mIHRoZSBtYXhpbWFsIGNsaXF1ZXMuCgpgYGB7cn0KY2xpcXVlLm51bWJlcih0Ym5ldDIpCmBgYApPdXIgdHViZXJjdWxvc2lzIGNvLWF1dGhvcnNoaXAgbmV0d29yayBjb250YWlucyAyOCBjbGlxdWVzLgoKYGBge3J9CnRhYmxlKHNhcHBseShtYXhpbWFsLmNsaXF1ZXModGJuZXQyKSwgbGVuZ3RoKSkKYGBgCjwhLS0gVGhlIHRhYmxlIGFib3ZlIGRpc3BsYXlzIHRoZSBzaXplIGFuZCBudW1iZXIgb2YgbWF4aW1hbCBjbGlxdWVzLiBGcm9tIHRoZSB0YWJsZSwgd2UgY2FuIHNlZSB0aGF0IG91ciBuZXR3b3JrIGNvbnRhaW5zIDEwIGNsaXF1ZXMgb2Ygc2l6ZSA4IGFuZCA3IGNsaXF1ZXMgb2Ygc2l6ZSA1LiBJdCBhbHNvIGNvbnRhaW5zIDEgY2xpcXVlcyBvZiBzaXplIDI4IGFuZCAxNDIgY2xpcXVlcyBvZiBzaXplIDcuIExhcmdlciBjbGlxdWVzIHNpemVzIHJhbmdlIGZyb20gIDEwMiBhdXRob3JzIHRvIDM2NSBhdXRob3JzIGFuZCBhcmUgYWxsIGZvdW5kIG9uY2UgYWNyb3NzIHRoZSBuZXR3b3JrLiAtLT4KCipEZW5zaXR5IGFuZCByZWxhdGVkIG5vdGlvbnMgb2YgcmVsYXRpdmUgZnJlcXVlbmN5OioKRGVmaW5lZCBhcyB0aGUgZnJlcXVlbmN5IG9mIHJlYWxpemVkIGVkZ2VzIHJlbGF0aXZlIHRvIHBvdGVudGlhbCBlZGdlcywgdGhlIGRlbnNpdHkgb2YgYSBzdWJncmFwaCAkSCQgaW4gJEckIHByb3ZpZGVzIGEgbWVhc3VyZSBvZiBob3cgY2xvc2UgJEgkIGlzIHRvIGJlIGEgY2xpcXVlIGluICRHJC4gRGVuc2l0eSB2YWx1ZXMgdmFyaWUgYmV0d2VlbiAwIGFuZCAxOgokJGRlbihIKT1cZnJhY3t8RV9IfH17fFZfSHwoVl9ILTEpLzJ9JCQKSGVyZSB3ZSBjb21wdXRlIHRoZSBnZW5lcmFsIGRlbnNpdHkgb2Ygb3VyIHR1YmVyY3Vsb3NpcyBjby1hdXRob3JzaGlwIG5ldHdvcmsuCgpgYGB7cn0KZ3JhcGguZGVuc2l0eSh0Ym5ldDIpCmBgYAoKV2UgYXNzZXNzIHRoZSByZWxhdGl2ZSBmcmVxdWVuY3kgb2YgJEckIGJ5IGNvbXB1dGluZyBpdHMgdHJhbnNpdGl2aXR5IGRlZmluZWQgYXM6CiQkY2xfVCA9IFxmcmFjezNcdGF1X1xEZWx0YSAoRyl9e1x0YXVfMyAoRyl9JCQKd2hlcmUgJFx0YXVfXERlbHRhIChHKSQgaXMgdGhlIG51bWJlciBvZiB0cmlhbmdsZXMgaW4gJEckLCBhbmQgJFx0YXVfMyAoRykkIGlzIHRoZSBudW1iZXIgb2YgY29ubmVjdGVkIHRyaXBsZXMgKHNvbWV0aW1lcyByZWZlcnJlZCB0byBhcyAyLXN0YXIpLiBUaGlzIG1lYXN1cmUgaXMgYWxzbyByZWZlcnJlZCB0byBhcyB0aGUgZnJhY3Rpb24gb2YgdHJhbnNpdGl2ZSB0cmlwbGVzLiBJdCByZXByZXNlbnRzIGEgbWVhc3VyZSBvZiBnbG9iYWwgY2x1c3RlcmluZyBvZiAkRyQgc3VtbWFyaXppbmcgdGhlIHJlbGF0aXZlIGZyZXF1ZW5jeSB3aXRoIHdoaWNoIGNvbm5lY3RlZCB0cmlwbGVzIGNsb3NlIHRvIGZvcm0gdHJpYW5nbGVzLgpgYGB7cn0KdHJhbnNpdGl2aXR5KHRibmV0MikKYGBgCiBBbm90aGVyIGFuYWxvZ3VlIG9mIHRoaXMgbWVhc3VyZSBpcyB0aGUgbG9jYWwgdHJhbnNpdGl2aXR5IGRlZmluZWQgYXM6CiAkJGNsKHYpPVx0YXVfXERlbHRhICh2KS9cdGF1XzMgKHYpJCQKIHdoZXJlICRcdGF1X1xEZWx0YSAodikkIGRlbm90ZXMgdGhlIG51bWJlciBvZiB0cmlhbmdsZXMgaW4gJEckIGludG8gd2hpY2ggJHYgXGluIFYkIGZhbGxzIGFuZCAkXHRhdV8zICh2KSQgaXMgdGhlIG51bWJlciBvZiBjb25uZWN0ZWQgdHJpcGxlcyBpbiAkRyQgZm9yIHdoaWNoIHRoZSB0d28gZWRnZXMgYXJlIGJvdGggaW5jaWRlbnQgdG8gJHYkLiBIZXJlIHdlIGNvbXB1dGUgdGhlIGxvY2FsIHRyYW5zaXRpdml0eSBmb3IgYWxsIHRoZSBub2RlcyBpbiBvdXIgdHViZXJjdWxvc2lzIGNvLWF1dGhvcnNoaXAgbmV0d29yay4KIApgYGB7cn0KdHI8LXRyYW5zaXRpdml0eSh0Ym5ldDIsJ2xvY2FsJyx2aWRzID0gMTpsZW5ndGgoVih0Ym5ldDIpKSkKVih0Ym5ldDIpJHRyYW5zaXRpdml0eTwtdHIKYXV0aF9kYXRhJHRyYW5zaXRpdml0eTwtdHIKYGBgCgoKKkNvbm5lY3Rpdml0eSwgQ3V0cywgYW5kIEZsb3dzOioKSW4gdGhpcyBzZWN0aW9uLCB3ZSBtZWFzdXJlIGhvdyBjbG9zZSBvdXIgdHViZXJjdWxvc2lzIGNvLWF1dGhvcnNoaXAgaXMgY2xvc2UgdG8gc2VwYXJhdGUgaW50byBkaXN0aW5jdHMgc3ViZ3JhcGhzLiBXZSBhcmUgYWxzbyBpbnRlcmVzdGVkIGluIGFzc2Vzc2luZyBob3cgd2VsbCBpbmZvcm1hdGlvbiBmbG93cyBpbiB0aGUgbmV0d29yay4KV2UgZmlyc3Qgc3RhcnQgd2l0aCB0aGUgY29uY2VwdCBvZiBjb25uZWN0ZWRuZXNzLiBTaW5jZSBvdXIgbmV0d29yayBpcyBhbiB1bmRpcmVjdGVkIGdyYXBoLCB3ZSBkbyBub3QgY29uc2lkZXIgdGhlIGlkZWEgb2Ygd2VhayBhbmQgc3Ryb25nIGNvbm5lY3Rpdml0eS4gQSBncmFwaCAkRyQgaXMgc2FpZCB0byBiZSBjb25uZWN0ZWQgaWYgZXZlcnkgbm9kZSBpbiAkRyQgaXMgcmVhY2hhYmxlIGZyb20gZXZlcnkgb3RoZXIgbm9kZS4KYGBge3J9CmlzLmNvbm5lY3RlZCh0Ym5ldDIpCmBgYApGcm9tIHRoZSBvdXRwdXQgYWJvdmUsIHdlIGNsZWFybHkgY29uY2x1ZGUgdGhhdCBvdXIgY28tYXV0aG9yc2hpcCBuZXR3b3JrIGlzIG5vdCBjb25uZWN0ZWQuCgpPZnRlbiB0aW1lLCBvbmUgb2YgdGhlIGNvbm5lY3RlZCBjb21wb25lbnRzIGNhbiBkb21pbmF0ZSB0aGUgb3RoZXJzLCBoZW5jZSB0aGUgaWRlYSBvZiAqZ2lhbnQgY29tcG9uZW50Ki4gTGV0J3MgdGhlbiBjZW5zdXMgb3VyIGNvLWF1dGhvcnNoaXA6CmBgYHtyfQpjb21wczwtZGVjb21wb3NlLmdyYXBoKHRibmV0MikKdGFibGUoc2FwcGx5KGNvbXBzLHZjb3VudCkpCmBgYAoKRnJvbSB0aGUgb3V0cHV0IG9mIHRoZSBjZW5zdXMgb2YgYWxsIGNvbm5lY3RlZCBjb21wb25lbnRzIG9mIHRoZSBuZXR3b3JrIGFib3ZlLCB3ZSBjYW4gc2VlIHRoYXQgdGhlcmUgYXJlIDIgbWFpbiBjb21wb25lbnRzIGNvbnRhaW5pbmcgcmVzcGVjdGl2ZWx5IDE2IGFuZCAxNTcgbm9kZXMuIFRoZXJlIGlzIGEgZ2lhbnQgY29tcG9uZW50IGNvbnRhaW5pbmcgJDE1Ny8xNzNcYXBwcm94IDkwLjhcJSQgb2YgYWxsIHRoZSB2ZXJ0aWNlcyBpbiB0aGUgbmV0d29yay4KCldlIGZ1cnRoZXIgZGV2b3RlIGNsb3NlciBhdHRlbnRpb24gdG8gdGhpcyBnaWFudCBjb21wb25lbnQuCmBgYHtyfQp0Ym5ldDIuZ2MgPC0gZGVjb21wb3NlLmdyYXBoKHRibmV0MilbWzFdXQpzdW1tYXJ5KHRibmV0Mi5nYykKYGBgCiAKIExldCdzIHBsb3QgdGhpcyBnaWFudCBjb21wb25lbnQ6CiAKYGBge3IsZmlnLmhlaWdodD0xNSxmaWcud2lkdGg9MTV9CnBsb3QodGJuZXQyLmdjLCBsYXlvdXQ9bGF5b3V0LmthbWFkYS5rYXdhaSh0Ym5ldDIuZ2MpLCB2ZXJ0ZXgubGFiZWw9TkEsIHZlcnRleC5zaXplPTIsIGVkZ2Uud2lkdGg9MC4wOCwgdmVydGV4LmNvbG9yPSdsaWdodGJsdWUnKQpgYGAKCk9uZSBpbXBvcnRhbnQgY2hhcmFjdGVyaXN0aWMgb2JzZXJ2ZWQgaW4gZ2lhbnQgY29tcG9uZW50IGlzIHRoZSBzby1jYWxsZWQgKnNtYWxsLXdvcmxkKiBwcm9wZXJ0eSB3aGljaCByZWZlcnMgdG8gdGhlIHNpdHVhdGlvbiB3aGVyZWluICoqdGhlIHNob3J0ZXN0LXBhdGggZGlzdGFuY2UgYmV0d2VlbiBwYWlycyBvZiBub2RlcyBpcyBnZW5lcmFsbHkgc21hbGwqKiBhbmQgKip0aGUgY2x1c3RlcmluZyBpcyByZWxhdGl2ZWx5IGhpZ2gqKi4gRm9yIG91ciB0dWJlcmN1bG9zaXMgY28tYXV0aG9yc2hpcCBuZXR3b3JrLCBsZXQncyBjb21wdXRlIHRoZSBhdmVyYWdlIHBhdGggbGVuZ3RoCmBgYHtyfQphdmVyYWdlLnBhdGgubGVuZ3RoKHRibmV0Mi5nYykKYGBgCmFuZCB0aGUgbG9uZ2VzdCBvZiBwYXRocwpgYGB7cn0KZGlhbWV0ZXIodGJuZXQyLmdjKQpgYGAKCkxldCdzIGFzc2VzcyB0aGUgdHJhbnNpdGl2aXR5IG9mIG91ciBnaWFudCBjb21wb25lbnQ6CmBgYHtyfQp0cmFuc2l0aXZpdHkodGJuZXQyLmdjKQpgYGAKCipJbnRlcnByZXRhdGlvbjoqICoqV2UgY2FuIHNlZSB0aGF0IHRoZSBhdmVyYWdlIHBhdGggbGVuZ3RoIG9mIHRoZSBnaWFudCBjb21wb25lbnQgb2Ygb3VyIGNvLWF1dGhvcnNoaXAgbmV0d29yayBpcyBzbWFsbCBhbmQgdGhlIGxvbmdlc3Qgb2YgcGF0aHMgaXMgbm90IG11Y2ggYmlnZ2VyLiBIZW5jZSBvdXIgZ2lhbnQgY29tcG9uZW50IGhhcyBhbGwgdGhlIGNoYXJhY3RlcmlzdGljcyBvZiBhIHNtYWxsLXdvcmxkLioqIEluIGFkZGl0aW9uLCB0aGUgY2x1c3RlcmluZyBpbiB0aGlzIG5ldHdvcmsgaXMgaGlnaCBpbmRpY2F0aW5nIHRoYXQgKio2MSUgb2YgdGhlIGNvbm5lY3RlZCB0cmlwbGVzIGFyZSBjbG9zZSB0byBmb3JtIHRyaWFuZ2xlcyoqLgoKCldlIGludmVzdGlnYXRlIHRoZSBjb25jZXB0cyBvZiB2ZXJ0ZXggYW5kIGVkZ2UgY3V0cyBkZXJpdmVkIGZyb20gdGhlIGNvbmNlcHQgb2YgdmVydGV4KGVkZ2UpIGNvbm5lY3Rpdml0eS4gVGhlIHZlcnRleCAoZWRnZSkgY29ubmVjdGl2aXR5IG9mIGEgZ3JhcGggJEckIGlzIHRoZSBsYXJnZXN0IGludGVnZXIgc3VjaCB0aGF0ICRHJCBpcyBrLXZlcnRleC0gKGVkZ2UtKSBjb25uZWN0ZWQuCgpgYGB7cn0KdmVydGV4LmNvbm5lY3Rpdml0eSh0Ym5ldDIuZ2MpCmBgYAoKYGBge3J9CmVkZ2UuY29ubmVjdGl2aXR5KHRibmV0Mi5nYykKYGBgCkluIHRoZSBjYXNlIG9mIHRoZSBnaWFudCBjb21wb25lbnQgb2Ygb3VyIGNvLWF1dGhvcnNoaXAgbmV0d29yaywgdGhlIHZlcnRleCBjb25uZWN0aXZpdHkgaXMgZXF1YWwgdG8gMSB3aGlsZSB0aGUgZWRnZSBjb25uZWN0aXZpdHkgaXMgZXF1YWwgdG8gMiwgKip0aHVzIHJlcXVpcmVzIHRoZSByZW1vdmFsIG9mIG9ubHkgYSBzaW5nbGUgd2VsbC1jaG9zZW4gbm9kZSAoYXV0aG9yKSBvciAyIGNvbGxhYm9yYXRpb24gdGllcyBpbiBvcmRlciB0byBicmVhayB0aGlzIHN1YmdyYXBoIGludG8gYWRkaXRpb25hbCBjb21wb25lbnRzKiouCgpBIHNldCBvZiBub2RlcyAoZWRnZXMpIHRoYXQgZGlzY29ubmVjdHMgdGhlIGdyYXBoIGlzIGNhbGxlZCBhICp2ZXJ0ZXggY3V0IChlZGdlIGN1dCkqLiBBIHNpbmdsZSBub2RlIChhdXRob3IpIHRoYXQgZGlzY29ubmVjdHMgb2Ygc3VjaCB2ZXJ0aWNlcyBpcyBjYWxsZWQgYSAqY3V0IHZlcnRleCogYW5kIGNhbiBwcm92aWRlIGEgc2Vuc2Ugb2Ygd2hlcmUgYSBuZXR3b3JrIGlzIHZ1bG5lcmFibGUuIExldCdzIGlkZW50aWZ5IHN1Y2ggd2VhayBwb2ludHMgaW4gb3VyIGNvLWF1dGhvcnNoaXAgbmV0d29yazogCmBgYHtyfQp0Ym5ldDIuY3V0LnZlcnRpY2VzIDwtIGFydGljdWxhdGlvbi5wb2ludHModGJuZXQyLmdjKQp0Ym5ldDIuY3V0LnZlcnRpY2VzCmBgYApUaGUgYWJvdmUgbGlzdGVkIGF1dGhvciBjb25zdGl0dXRlcyB0aGUgb25seSB3ZWFrICphcnRpY3VsYXRpb24gcG9pbnQqIG9mIG91ciBjby1hdXRob3JzaGlwIG5ldHdvcmsgYnV0IGFsc28gdGhlIG1vc3QgaW1wb3J0YW50IG5vZGVzIG9mIG91ciBuZXR3b3JrLgoKYGBge3J9Cmxlbmd0aCh0Ym5ldDIuY3V0LnZlcnRpY2VzKQpgYGAKSW4gb3VyIHR1YmVyY3Vsb3NpcyBjby1hdXRob3JzaGlwIG5ldHdvcmssIGxlc3MgdGhhbiAqKjElKiogb2YgdGhlIG5vZGVzIGFyZSBjdXQgdmVydGljZXMgbWVhbmluZyB0aGF0IHRoZSB2dWxuZXJhYmlsaXR5IG9mIHRoZSBuZXR3b3JrIGlzIGRlcGVuZGFudCBvbiBhIHZlcnkgc21hbGwgc2V0IG9mIGF1dGhvcnMgaW4gdGhlIGNvLWF1dGhvcnNoaXAgbmV0d29yay4KCgoqKkdyYXBoIFBhcnRpdGlvbmluZzoqKgpSZWd1bGFybHkgZnJhbWVkIGFzIGNvbW11bml0eSBkZXRlY3Rpb24gcHJvYmxlbSwgZ3JhcGggcGFydGl0aW9uaW5nIGlzIGFuIHVuc3VwZXJ2aXplZCBtZXRob2QgdXNlZCBpbiB0aGUgYW5hbHlzaXMgb2YgbmV0d29yayBkYXRhIHRvIGZpbmQgc3Vic2V0cyBvZiBub2RlcyB0aGF0IGRlbW9uc3RyYXRlIGEgJ2NvaGVzaXZlbmVzcycgd2l0aCByZXNwZWN0IHRvIHRoZWkgdW5kZXJseWluZyByZWxhdGlvbmFsIHBhdHRlcm5zLiBDb2hlc2l2ZSBzdWJzZXRzIG9mIG5vZGVzIGdlbmVyYWxseSBhcmUgd2VsbCBjb25uZWN0ZWQgYW1vbmcgdGhlbXNlbHZlcyBhbmQgYXJlIHdlbGwgc2VwYXJhdGVkIGZyb20gdGhlIG90aGVyIG5vZGVzIGluIHRoZSBncmFwaC4gSGVyZSwgd2UgcGVyZm9ybSB0d28gd2VsbCBlc3RhYmxpc2hlZCBtZXRob2RzIG9mIGdyYXBoIHBhcnRpdGlvbmluZzogKkhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nKiBhbmQgKlNwZWN0cmFsIGNsdXN0ZXJpbmcqLgoKKkhpZXJhcmNoaWNhbCBDbHVzdGVyaW5nOioKSGllcmFyY2hpY2FsIGNsdXN0ZXJpbmcgbWV0aG9kcyBhcmUgb2YgdHdvIGtpbmRzOgotIGFnZ2xvbWVyYXRpdmU6ICJiYXNlZCBvbiB0aGUgc3VjY2Vzc2l2ZSBjb2Fyc2VuaW5nIG9mIHBhcnRpdGlvbnMgdGhyb3VnaCB0aGUgcHJvY2VzcyBvZiBtZXJnaW5nIiwgaXQgdXNlcyBtb2R1bGFyaXR5IGFzIG1ldHJpY3MuCi0gZGl2aXNpdmU6ICJiYXNlZCBvbiB0aGUgc3VjY2Vzc2l2ZSByZWZpbmVtZW50IG9mIHBhcnRpdGlvbnMgdGhyb3VnaCB0aGUgcHJvY2VzcyBvZiBzcGxpdHRpbmciCgpIZXJlLCB3ZSBhcHBseSB0aGUgYWdnbG9tZXJhdGl2ZSBtZXRob2Qgb24gb3VyIHR1YmVyY3Vsb3NpcyBjby1hdXRob3JzaGlwIG5ldHdvcms6CmBgYHtyfQpjb20udGJuZXQyIDwtIGZhc3RncmVlZHkuY29tbXVuaXR5KHRibmV0MikKVih0Ym5ldDIpJGNvbW11bml0eSA8LSBjb20udGJuZXQyJG1lbWJlcnNoaXAKbGVuZ3RoKGNvbS50Ym5ldDIpCmBgYApUaGUgYWdnbG9tZXJhdGl2ZSBoaWVyYXJjaGljYWwgY2x1c3RlcmluZyBpZGVudGlmaWVzIDYgY29tbXVuaXRpZXMgaW4gb3VyIGNvLWF1dGhvcnNoaXAgbmV0d29yay4KYGBge3J9CnNpemVzKGNvbS50Ym5ldDIpCmBgYApUaGUgbGFyZ2VzdCBjb21tdW5pdHkgY29udGFpbnMgNTggYXV0aG9ycy4gTWVkaXVtIHNpemUgY29tbXVuaXRpZXMgY29udGFpbiBiZXR3ZWVuIDE0IGFuZCAyOSBhdXRob3JzLgoKTGV0J3Mgbm93IHZpc3VhbGl6ZSB0aGUgY29tbXVuaXRpZXM6CmBgYHtyLGZpZy5oZWlnaHQ9MTUsZmlnLndpZHRoPTE1fQpwbG90KGNvbS50Ym5ldDIsIHRibmV0Mix2ZXJ0ZXgubGFiZWw9JycsCiAgICAgbGF5b3V0PWwudGJuZXQyLAogICAgIG1hcmsuZ3JvdXBzID0gTlVMTCwKICAgICAjIHZlcnRleC5zaXplID0gMywKICAgICAjIGVkZ2UuY29sb3IgPSBtZW1iLnRibmV0MiwKICAgICBlZGdlLndpZHRoID0gMC4wOCwKICAgICB2ZXJ0ZXguc2l6ZT0xKzQgKiBzcXJ0KGJ3LnRibmV0LzEwMDApCiAgICAgKQpgYGAKCgpgYGB7cn0KZ2MuY29tIDwtIGZhc3RncmVlZHkuY29tbXVuaXR5KHRibmV0Mi5nYykKbGVuZ3RoKGdjLmNvbSkKYGBgCiBUaGVyZSBhcmUgOSBjb21tdW5pdGllcyBpbiB0aGUgZ2lhbnQgY29tcG9uZW50IG9mIG91ciBuZXR3b3JrLgogCiAKYGBge3J9CnNpemVzKGdjLmNvbSkKYGBgClRoZSA5IGNvbW11bml0aWVzIGNvbnRhaW4gYmV0d2VlbiA1IGFuZCA0OSBhdXRob3JzLgoKTGV0J3Mgbm93IHZpc3VhbGl6ZSB0aGUgOSBjb21tdW5pdGllcyBpbiB0aGUgZ2lhbnQgY29tcG9uZW50OgpgYGB7cn0KbC5nYzwtbGF5b3V0LmthbWFkYS5rYXdhaSh0Ym5ldDIuZ2MpCmJ3LmdjPC1iZXR3ZWVubmVzcyh0Ym5ldDIuZ2MpClYodGJuZXQyLmdjKSRjb21tdW5pdHkgPC0gbWVtYmVyc2hpcChnYy5jb20pCmBgYAoKYGBge3IsZmlnLmhlaWdodD0xNSxmaWcud2lkdGg9MTV9CnBsb3QoZ2MuY29tLCB0Ym5ldDIuZ2MsdmVydGV4LmxhYmVsPScnLAogICAgICMgbGF5b3V0PWwudGJuZXQyLAogICAgIGxheW91dCA9IGwuZ2MsCiAgICAgbWFyay5ncm91cHMgPSBOVUxMLAogICAgICMgdmVydGV4LnNpemUgPSAzLAogICAgICMgZWRnZS5jb2xvciA9IG1lbWIudGJuZXQyLAogICAgIGVkZ2Uud2lkdGggPSAwLjA2LAogICAgIHZlcnRleC5zaXplPTErNCAqIHNxcnQoYncuZ2MvMTAwMCkKICAgICApCmBgYAoKTGV0J3MgcGxvdCB0aGUgOSBjb21tdW5pdGllcyBzZXBhcmF0ZWx5OgpgYGB7cixmaWcud2lkdGg9MjAsZmlnLmhlaWdodD00MH0KcGFyKGNleC5tYWluPTMpCnBhcihtZnJvdz1jKDUsMikpCiMgUGxvdCBPcmlnaW5hbCBnaWFudCBjb21wb25lbnQgZ3JhcGgKcGxvdChnYy5jb20sIHRibmV0Mi5nYyx2ZXJ0ZXgubGFiZWw9JycsCiAgICAgbWFpbiA9ICdNYWluIGNvbXBvbmVudCcsCiAgICAgIyBsYXlvdXQ9bC50Ym5ldDIsCiAgICAgbGF5b3V0ID0gbC5nYywKICAgICBtYXJrLmdyb3VwcyA9IE5VTEwsCiAgICAgIyB2ZXJ0ZXguc2l6ZSA9IDMsCiAgICAgIyBlZGdlLmNvbG9yID0gbWVtYi50Ym5ldDIsCiAgICAgZWRnZS53aWR0aCA9IDAuMDUsCiAgICAgdmVydGV4LnNpemU9MSs0ICogc3FydChidy5nYy8xMDAwKQogICAgICkKCmZvcihpIGluIDE6OSl7CiAgZyA8LSB3aGljaChWKHRibmV0Mi5nYykkY29tbXVuaXR5PT1pKQogIEcuZ3JvdXAgPC0gc3ViZ3JhcGgodGJuZXQyLmdjLCBnKQogIHBsb3QoRy5ncm91cCx2ZXJ0ZXgubGFiZWw9JycsCiAgICAgbWFpbiA9IHBhc3RlKCdDb21tdW5pdHkvUGFydGl0aW9uICcsaSksCiAgICAgIyBsYXlvdXQ9bC50Ym5ldDIsCiAgICAgbGF5b3V0ID0gbC5nY1tnLF0sCiAgICAgbWFyay5ncm91cHMgPSBOVUxMLAogICAgIHZlcnRleC5jb2xvciA9IGdjLmNvbSRtZW1iZXJzaGlwW2ddLAogICAgICMgdmVydGV4LnNpemUgPSAzLAogICAgICMgZWRnZS5jb2xvciA9IG1lbWIudGJuZXQyLAogICAgIGVkZ2Uud2lkdGggPSAwLjA2LAogICAgIHZlcnRleC5zaXplPTErNCAqIHNxcnQoYncuZ2MvMTAwMCkKICAgICApCn0KYGBgCgpXZSBmaW5hbGx5IHNhdmUgYWxsIG91ciBnZW5lcmF0ZWQgUiBvYmplY3RzIGZvciBsYXRlciB1c2UuCmBgYHtyfQpzYXZlKHRibmV0LCBmaWxlID0gJy4vUmRhdGEvVEJuZXQucmRhJykKc2F2ZSh0Ym5ldDIsIGZpbGUgPSAnLi9SZGF0YS9UQm5ldDIucmRhJykKc2F2ZShhdXRoX2RhdGEsIGZpbGUgPSAnLi9SZGF0YS9UQmF1dGhfZGF0YS5yZGEnKQpzYXZlKGVkZ2VzLCBmaWxlID0gJy4vUmRhdGEvVEJlZGdlcy5yZGEnKQpzYXZlKHRibmV0Mi5nYywgZmlsZSA9ICcuL1JkYXRhL1RCbmV0Mi5nYy5yZGEnKQpgYGAKYGBge3J9CiMgc291cmNlKCdwbG90bHlfbWFwLlInKQpgYGAKJFxcJAoKPCEtLSBXZSBmaW5hbGx5IHBsb3QgdGhlIGNvbGxhYm9yYXRpb24gbWFwIHVzaW5nIFBsb3RseSBhbmQgYXZhaWxhYmxlIFtIRVJFXShodHRwczovL3Bsb3QubHkvfnJvc2VyaWNhem9uZGVrb24vNjEuZW1iZWQpLiAtLT4KCjwhLS0gPGlmcmFtZSBzcmM9Imh0dHBzOi8vcGxvdC5seS9+cm9zZXJpY2F6b25kZWtvbi82MS5lbWJlZCIgaGVpZ2h0PSI4MDAiIHdpZHRoPSIxMDAlIiAgICAgc2Nyb2xsaW5nPSJubyIgc2VhbWxlc3M9InNlYW1sZXNzIiBmcmFtZUJvcmRlcj0iMCI+PC9pZnJhbWU+IC0tPgo8IS0tIDxpbWcgc3JjPSJodHRwczovL3Bsb3QubHkvfnJvc2VyaWNhem9uZGVrb24vNjEucG5nIj4gLS0+CgokXFwkCiRcXCQKCgoqKk5FWFQgVFVUT1JJQUw6KiogW01hdGhlbWF0aWNhbCBNb2RlbGluZyBmb3IgTmV0d29yayBHcmFwaHNdKGh0dHA6Ly8jKQ==