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

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….

library(igraph)

\(\\\)

Descriptive Network Analysis

# mnet <- read_graph('~/Documents/sna/graphs_old/CAnet.graphml', format = 'graphml')
mnet <- read_graph('~/Documents/sna/graphs/CAnet.graphml', format = 'graphml')

\(\\\)

Get a summary of the network:

summary(mnet)
IGRAPH UN-- 1792 116388 -- 
+ 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 1792 authors and 116,388 scientific collaborations. Each node (author) in the network has 2 attributes: name 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:

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

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

par(mfrow=c(1,2))
hist(d.mnet, col="lightblue", xlab="Vertex Degree", ylab="Frequency", main="Degree distribution")
hist(s.mnet, col="pink", xlab="Vertex Strength", ylab="Frequency", main="Strength distribution")

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.

E(mnet)$weight <- 1
mnet2 <- simplify(mnet, edge.attr.comb = list(weight="sum",timesCited="sum",numPub="sum",key="concat",subject="concat",year="concat",wosid="concat",journal="concat",title="concat",doi="concat"))
summary(mnet2)
IGRAPH UNW- 1792 95707 -- 
+ attr: affil (v/c), numPub (v/n), place (v/c), country (v/c),
| name (v/c), timesCited (v/n), id (v/c), 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)

Visualization

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

# l.mnet <- layout.kamada.kawai(mnet)
# l.mnet2 <- layout.kamada.kawai(mnet2)
l.mnet2 <- layout.kamada.kawai(mnet2)
# plot(mnet, layout=l.mnet, vertex.label=NA, vertex.size=2)
# plot(mnet2, layout=l.mnet2, vertex.label=NA, vertex.size=2, vertex.color='red')
plot(mnet2, layout=l.mnet2, vertex.label=NA, vertex.size=2, vertex.color='blue') # We use l.mnet2 as layout

d.mnet <- degree(mnet2) # Degree of simple graph mnet2
V(mnet2)$degree <- d.mnet
dd.mnet <- degree.distribution(mnet2) # Compute degree distribution
d <- 1:max(d.mnet)-1
ind <- (dd.mnet != 0)
a.nn.deg.mnet <- graph.knn(mnet2, V(mnet2))$knn
mean(d.mnet)
[1] 106.8158

Now, ploting degree distribution of the simplified graph.

par(mfrow=c(1,2))
hist(d.mnet, col="lightblue", xlab="Vertex Degree", ylab="Frequency", main="Degree distribution")
plot(d[ind], dd.mnet[ind], log="xy", col="blue", type='p', xlab=c("Log-Degree"), ylab=c("Log-Intensity"),main="Log-Log Degree Distribution")

Interpretation: From the plot on the right, the degrees of the node seem to follow a heavy right-tail distribution, characteristic of a power law distribution with an average distribution of \(106.46\). 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.

plot(d.mnet, a.nn.deg.mnet, log="xy", col="goldenrod", type='p', xlab=c("Log Vertex Degree"), ylab=c("Log Average Neighbor 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:

A <- get.adjacency(mnet2, 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\).

cl.mnet <- closeness(mnet2)
V(mnet2)$closeness <- cl.mnet
summary(cl.mnet)
     Min.   1st Qu.    Median      Mean   3rd Qu.      Max. 
3.118e-07 5.091e-06 5.112e-06 4.820e-06 5.115e-06 5.152e-06 
plot(mnet2, layout=l.mnet2, main="Closeness centrality", vertex.label="", vertex.size=2 * sqrt(cl.mnet*1E6), vertex.color='lightblue')

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\).

bw.mnet <- betweenness(mnet2)
V(mnet2)$betweenness <- bw.mnet
summary(bw.mnet)
     Min.   1st Qu.    Median      Mean   3rd Qu.      Max. 
     0.00      0.00     21.37   1985.00    326.40 245600.00 
plot(mnet2, layout=l.mnet2, main="Betweenness centrality", vertex.label="", vertex.size=1 * sqrt(bw.mnet/1000), vertex.color='lightgreen')

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}\).

ev.mnet <- evcent(mnet2)$vector
V(mnet2)$eigenv <- ev.mnet
summary(ev.mnet)
     Min.   1st Qu.    Median      Mean   3rd Qu.      Max. 
0.0000000 0.0001524 0.0050580 0.0898800 0.1719000 1.0000000 
plot(mnet2, layout=l.mnet2, main="Eigenvectors centrality", vertex.label="", vertex.size=2 * sqrt(ev.mnet*10), vertex.color='pink')

Visualization highliting Hubs and Authorities

# par(mfrow=c(1,2))
# plot(mnet2, layout=l.mnet2, main="Hubs", vertex.label="", vertex.size=10 * sqrt(hub.score(mnet2)$vector), vertex.color='green')
# plot(mnet2, layout=l.mnet2, main="Authorities", vertex.label="", vertex.size=10 *
# sqrt(authority.score(mnet2)$vector), vertex.color='red')
V(mnet2)$hubScore <- hub.score(mnet2)$vector
V(mnet2)$authScore <- authority.score(mnet2)$vector
summary(mnet2)
IGRAPH UNW- 1792 95707 -- 
+ 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.

auth_data <- data.frame(id=as.numeric(V(mnet2)$id),name=V(mnet2)$name,numPub=as.numeric(V(mnet2)$numPub),timesCited=as.numeric(V(mnet2)$timesCited),degree=V(mnet2)$degree,closeness=V(mnet2)$closeness,betweenness=V(mnet2)$betweenness,eigenV=V(mnet2)$eigenv,hubScore=V(mnet2)$hubScore,authScore=V(mnet2)$authScore)
auth_data$affiliation<-V(mnet2)$place
auth_data$city<-V(mnet2)$affil
auth_data$country<-V(mnet2)$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(mnet2)$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('~/Documents/sna/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(mnet2,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(mnet2)),source=eList$source,target=eList$target,weight=E(mnet2)$weight,timesCited=E(mnet2)$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 malaria co-authorship network.

eb <- edge.betweenness(mnet2)
E(simplify(mnet))[order(eb, decreasing=T)[1:10]]
+ 10/95707 edges (vertex names):
 [1] DABIRE K ROCH       --KENGNE ANDRE PASCAL  
 [2] BALDET THIERRY      --KENGNE ANDRE PASCAL  
 [3] AKOGBETO MARTIN     --MALIK ELFATIH M      
 [4] AVLESSI FELICIEN    --MOUDACHIROU MANSOUROU
 [5] AKOGBETO MARTIN     --AVLESSI FELICIEN     
 [6] MASSOUGBODJI ACHILLE--RAHIMY MOHAMED CHERIF
 [7] DIABATE ABDOULAYE   --KENGNE ANDRE PASCAL  
 [8] GARCIA ANDRE        --SANNI AMBALIOU       
 [9] KAREMA CORINE       --MALIK ELFATIH M      
[10] HAY SIMON I         --MALIK ELFATIH M      

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 malaria 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 malaria co-authorship network, then compute the number and size of the maximal cliques.

clique.number(mnet2)
[1] 365

Our malaria co-authorship network contains 365 cliques.

table(sapply(maximal.cliques(mnet2), length))

  2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18 
  9  14  21  58  80 135 155 107  98  61  39  23  15   7   9  11   3 
 20  21  22  23  24  25  26  27  34 102 106 115 116 120 126 130 131 
  3   1   3   3   1   1   1   1   1   1   1   1   1   1   2   1   2 
137 139 142 152 365 
  1   1   1   1   1 

The table above displays the size and number of maximal cliques. From the table, we can see that our network contains 9 cliques of size 2 and 14 cliques of size 3. It also contains 155 cliques of size 8 and 142 cliques of size 7. Larger cliques sizes range from 102 authors to 365 authors and are all found once across the network.

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 malaria co-authorship network.

graph.density(mnet2)
[1] 0.05964034

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.

transitivity(mnet2)
[1] 0.9645148

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 malaria co-authorship network.

tr<-transitivity(mnet2,'local',vids = 1:length(V(mnet2)))
V(mnet2)$transitivity<-tr
auth_data$transitivity<-tr

Connectivity, Cuts, and Flows: In this section, we measure how close our malaria 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.

is.connected(mnet2)
[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:

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

   2    3    5    6    8    9   10   26 1686 
   2    3    5    1    1    2    1    1    1 

From the output of the census of all connected components of the network above, we can that there clearly is a giant component containing \(1686/1792\approx 94\%\) of all the vertices in the network with none of the other components alone carrying even \(1\%\) of the vertices.

We further devote closer attention to this giant component.

mnet2.gc <- decompose.graph(mnet2)[[1]]
summary(mnet2.gc)
IGRAPH UNW- 1686 95364 -- 
+ 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:

plot(mnet2.gc, layout=layout.kamada.kawai(mnet2.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 malaria co-authorship network, let’s compute the average path length

average.path.length(mnet2.gc)
[1] 2.986212

and the longest of paths

diameter(mnet2.gc)
[1] 10

Let’s assess the transitivity of our giant component:

transitivity(mnet2.gc)
[1] 0.9645176

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 very large indicating that 96% 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 k0vertex- (edge-) connected.

vertex.connectivity(mnet2.gc)
[1] 1
edge.connectivity(mnet2.gc)
[1] 1

In the case of the giant component of our co-authorship network, the vertex and edge connectivity are both equal to 1, thus requires the removal of only a single well-chosen node (author) or collaboration 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:

mnet2.cut.vertices <- articulation.points(mnet2.gc)
mnet2.cut.vertices
+ 16/1686 vertices, named:
 [1] NOEL VALERIE                DJOGBENOU LUC              
 [3] ZOHOUN I                    SANNI AMBALIOU             
 [5] EDORH ALEODJRODO PATRICK    ALLABI AUREL               
 [7] HOUNKONNOU MAHOUTON NORBERT FAYOMI BENJAMIN            
 [9] KINDEGAZARD DOROTHEE A      DJOUAKA ROUSSEAU           
[11] RAHIMY MOHAMED CHERIF       BALDET THIERRY             
[13] DOSSOUGBETE L               GARCIA ANDRE               
[15] MASSOUGBODJI ACHILLE        AKOGBETO MARTIN            

The above listed authors constitute the weak articulation points of our co-authorship network but also the most important nodes of our network.

length(mnet2.cut.vertices)
[1] 16

In our malaria 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 malaria co-authorship network:

com.mnet2 <- fastgreedy.community(mnet2)
V(mnet2)$community <- com.mnet2$membership
length(com.mnet2)
[1] 23

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

sizes(com.mnet2)
Community sizes
  1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17 
363 570 439  58  26  10   9   8   6   9   5   5   5   5   5   3   3 
 18  19  20  21  22  23 
  3   2   2  23 203  30 

Large communities contain between 202 and 569 authors. Medium size communities contain between 10 and 62 authors.

Let’s now visualize the communities:

plot(com.mnet2, mnet2,vertex.label='',
     layout=l.mnet2,
     mark.groups = NULL,
     # vertex.size = 3,
     # edge.color = memb.mnet2,
     edge.width = 0.08,
     vertex.size=1+1 * sqrt(bw.mnet/1000)
     )

gc.com <- fastgreedy.community(mnet2.gc)
length(gc.com)
[1] 7

There’s only 7 communities in the giant component of our network.

sizes(gc.com)
Community sizes
  1   2   3   4   5   6   7 
363 571 438  58  23 203  30 

The 7 communities contain between 30 and 569 authors.

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

l.gc<-layout.kamada.kawai(mnet2.gc)
bw.gc<-betweenness(mnet2.gc)
V(mnet2.gc)$community <- membership(gc.com)
plot(gc.com, mnet2.gc,vertex.label='',
     # layout=l.mnet2,
     layout = l.gc,
     mark.groups = NULL,
     # vertex.size = 3,
     # edge.color = memb.mnet2,
     edge.width = 0.06,
     vertex.size=1+1 * sqrt(bw.gc/1000)
     )

Let’s plot the 7 communities separately:

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

save(mnet, file = '~/Documents/sna/R_scripts/mnet.rda')
save(mnet2, file = '~/Documents/sna/R_scripts/mnet2.rda')
save(auth_data, file = '~/Documents/sna/R_scripts/auth_data.rda')
save(edges, file = '~/Documents/sna/R_scripts/edges.rda')
save(mnet2.gc, file = '~/Documents/sna/R_scripts/mnet2.gc.rda')
# source('plotly_map.R')

\(\\\)

We finally plot the collaboration map using Plotly and available HERE.

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

NEXT TUTORIAL: Mathematical Modeling for Network Graphs

LS0tCnRpdGxlOiAiU29jaWFsIE5ldHdvcmsgQW5hbHlzaXMgb2YgdGhlIE1hbGFyaWEgQ28tYXV0aG9yc2hpcCBuZXR3b3JrIGZyb20gQmVuaW4iCmF1dGhvcjogIlJvc2VyaWMgQXpvbmRla29uIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdAogIHdvcmRfZG9jdW1lbnQ6IGRlZmF1bHQKLS0tCiRcXCQKJFxcJAoKVGhpcyBwcm9qZWN0IGlzIHBhcnQgb2YgbXkgUGhEIGRpc3NlcnRhdGlvbiBwcm9qZWN0LiBUaGUgbmV0d29yayBoYXMgYWxyZWFkeSBiZWVuIGFzc2VtYmxlZC4KCkxldCdzIGZpcnN0IGxvYWQgdGhlIHJlcXVpcmVkIHBhY2thZ2VzIGFuZCB0aGUgbmV0d29yayBmaWxlOgokXFwkCgpMb2FkaW5nIGxpYnJhcmllcy4uLi4KYGBge3Isd2FybmluZz1GQUxTRX0KbGlicmFyeShpZ3JhcGgpCmBgYAokXFwkCgoqKkRlc2NyaXB0aXZlIE5ldHdvcmsgQW5hbHlzaXMqKgpgYGB7cn0KIyBtbmV0IDwtIHJlYWRfZ3JhcGgoJ34vRG9jdW1lbnRzL3NuYS9ncmFwaHNfb2xkL0NBbmV0LmdyYXBobWwnLCBmb3JtYXQgPSAnZ3JhcGhtbCcpCm1uZXQgPC0gcmVhZF9ncmFwaCgnfi9Eb2N1bWVudHMvc25hL2dyYXBocy9DQW5ldC5ncmFwaG1sJywgZm9ybWF0ID0gJ2dyYXBobWwnKQpgYGAKJFxcJAoKR2V0IGEgc3VtbWFyeSBvZiB0aGUgbmV0d29yazoKYGBge3J9CnN1bW1hcnkobW5ldCkKYGBgCgoqSW50ZXJwcmV0YXRpb246KiBPdXIgY28tYXV0aG9yc2hpcCBuZXR3b3JrIGlzIGFuIHVuZGlyZWN0ZWQgbXVsdGlncmFwaCAocGFyYWxsZWwgZWRnZXMpIHdpdGggKioxNzkyIGF1dGhvcnMqKiBhbmQgKioxMTYsMzg4IHNjaWVudGlmaWMgY29sbGFib3JhdGlvbnMqKi4gRWFjaCBub2RlIChhdXRob3IpIGluIHRoZSBuZXR3b3JrIGhhcyAyIGF0dHJpYnV0ZXM6IG5hbWUgYW5kIGlkLiBFYWNoIGVkZ2UgaGFzIDggYXR0cmlidXRlczoga2V5LCBzdWJqZWN0LCBhYnN0cmFjdCwgeWVhciwgd29zaWQgKFdlYiBvZiBzY2llbmNlIElkZW50aWZpY2F0aW9uIG51bWJlciksIGpvdXJuYWwsIHRpdGxlIGFuZCBkb2kuCgoKKkNvbXB1dGluZyBOb2RlIENlbnRyYWxpdHkgTWVhc3VyZXM6KgpXZSBjb21wdXRlIGNlbnRyYWxpdHkgbWVhc3VyZXMgc3VjaCBhcyBkZWdyZWUgKG51bWJlciBvZiB0aWVzIHRvIGEgZ2l2ZW4gYXV0aG9yKSwgYmV0d2Vlbm5lc3MgKG51bWJlciBvZiBzaG9ydGVzdCBwYXRocyBiZXR3ZWVuIGFsdGVycyB0aGF0IGdvIHRocm91Z2ggYSBwYXJ0aWN1bGFyIGF1dGhvciksIGNsb3NlbmVzcyAobnVtYmVyIG9mIHN0ZXBzIHJlcXVpcmVkIGZvciBhIHBhcnRpY3VsYXIgYXV0aG9yIHRvIGFjY2VzcyBldmVyeSBvdGhlciBhdXRob3IgaW4gdGhlIG5ldHdvcmspIGFuZCBlaWdlbnZlY3RvcnMgKGRlZ3JlZSB0byB3aGljaCBhbiBhdXRob3IgaXMgY29ubmVjdGVkIHRvIG90aGVyIHdlbGwgY29ubmVjdGVkIGF1dGhvcnMgaW4gdGhlIG5ldHdvcmspLCBicm9rZXJhZ2UgKGRlZ3JlZSB0byB3aGljaCBhbiBhY3RvciBvY2N1cGllcyBhIGJyb2tlcmFnZSBwb3NpdGlvbiBhY3Jvc3MgYWxsIHBhaXJzIG9mIGFsdGVycykgCgoqRGVncmVlIGNlbnRyYWxpdHkqCkxldCdzIGZpcnN0IGNvbXB1dGUgdGhlIGRlZ3JlZSBhbmQgc3RyZW5ndGggb2YgdGhlIG5vZGVzIGluIHRoZSBuZXR3b3JrOgpgYGB7cn0KZC5tbmV0IDwtIGRlZ3JlZShtbmV0KQpzLm1uZXQgPC0gZ3JhcGguc3RyZW5ndGgoc2ltcGxpZnkobW5ldCkpICMgZm9yIHdlaWdodGVkIGdyYXBoCmBgYAogCk5vdywgbGV0J3MgcGxvdCBhIGhpc3RvZ3JhbSBvZiB0aGUgZGVncmVlIGFuZCBzdHJlbmd0aCBkaXN0cmlidXRpb25zOgpgYGB7cixmaWcud2lkdGg9OH0KcGFyKG1mcm93PWMoMSwyKSkKaGlzdChkLm1uZXQsIGNvbD0ibGlnaHRibHVlIiwgeGxhYj0iVmVydGV4IERlZ3JlZSIsIHlsYWI9IkZyZXF1ZW5jeSIsIG1haW49IkRlZ3JlZSBkaXN0cmlidXRpb24iKQpoaXN0KHMubW5ldCwgY29sPSJwaW5rIiwgeGxhYj0iVmVydGV4IFN0cmVuZ3RoIiwgeWxhYj0iRnJlcXVlbmN5IiwgbWFpbj0iU3RyZW5ndGggZGlzdHJpYnV0aW9uIikKYGBgCipJbnRlcnByZXRhdGlvbjoqIFdoaWxlIHRoZXJlIGlzIGEgc3Vic3RhbnRpYWwgbnVtYmVyIG9mIG5vZGVzIG9mIHF1aXRlIGxvdyBkZWdyZWUsIHRoZXJlIGFyZSBhbHNvIGEgbm9uLXRyaXZpYWwgbnVtYmVyIG9mIG5vZGVzIHdpdGggaGlnaGVyIG9yZGVyIG9mIGRlZ3JlZSBtYWduaXR1ZGVzLiBHaXZlbiB0aGUgbmF0dXJlIG9mIHRoaXMgZGlzdHJpYnV0aW9uLCBhIGxvZy1sb2cgc2NhbGUgaXMgbW9yZSBlZmZlY3RpdmUgYXQgc3VtbWFyaXppbmcgdGhlIGRlZ3JlZSBpbmZvcm1hdGlvbi4KCkZyb20gaGVyZSwgdGhlIHByb2Nlc3NpbmcgcmVxdWlyZXMgb3VyIG5ldHdvcmsgdG8gYmUgYSBzaW1wbGUgZ3JhcGguIFdlIHdvdWxkIHRoZXJlZm9yZSBjaGFuZ2Ugb3VyIG11bHRpZ3JhcGggdG8gYSBncmFwaCBvYmplY3QuCmBgYHtyfQpFKG1uZXQpJHdlaWdodCA8LSAxCm1uZXQyIDwtIHNpbXBsaWZ5KG1uZXQsIGVkZ2UuYXR0ci5jb21iID0gbGlzdCh3ZWlnaHQ9InN1bSIsdGltZXNDaXRlZD0ic3VtIixudW1QdWI9InN1bSIsa2V5PSJjb25jYXQiLHN1YmplY3Q9ImNvbmNhdCIseWVhcj0iY29uY2F0Iix3b3NpZD0iY29uY2F0Iixqb3VybmFsPSJjb25jYXQiLHRpdGxlPSJjb25jYXQiLGRvaT0iY29uY2F0IikpCnN1bW1hcnkobW5ldDIpCmBgYAoKPCEtLSAgVGhlIG5ld2x5IGNvbXB1dGVkIHVuZGlyZWN0ZWQgZ3JhcGggY29udGFpbnMgKjE3OTIgbm9kZXMqIGFuZCAqOTU3MDcgZWRnZXMqLiBOb3RlIHRoYXQgdGhlIHNpbXBsaWZ5IGZ1bmN0aW9uIGluIFIgZG9lcyBub3QgY2FwdHVyZSB0aGUgbnVtYmVyIG9mIG11bHRpcGxlIGNvbGxhYm9yYXRpb25zIGJldHdlZW4gYXV0aG9ycy4gSW4gYSBwcmV2aW91cyB0dXRvcmlhbCwgd2UgaGF2ZSBjb21wdXRlZCBhIHNpbXBsZSB2ZXJzaW9uIG9mIHRoZSBtbmV0IG5ldHdvcmsuIExldCdzIGluc3RlYWQgb2YgdXNpbmcgdGhlICpzaW1wbGlmeSgpKiBmdW5jdGlvbiwgbG9hZCBtbmV0MiBmcm9tIGl0cyBwcmV2aW91c2x5IGNvbXB1dGVkIC5ncmFwaG1sIGZvcm1hdDogLS0+Cgo8IS0tIGBgYHtyfSAtLT4KPCEtLSAjIG1uZXQzIDwtIHJlYWRfZ3JhcGgoJ34vRG9jdW1lbnRzL3NuYS9ncmFwaHNfb2xkL0NBbmV0X2dyYXBoLmdyYXBobWwnLCBmb3JtYXQgPSAnZ3JhcGhtbCcpIC0tPgo8IS0tIG1uZXQzIDwtIHJlYWRfZ3JhcGgoJ34vRG9jdW1lbnRzL3NuYS9ncmFwaHMvQ0FuZXRfd2VpZ2h0LmdyYXBobWwnLCBmb3JtYXQgPSAnZ3JhcGhtbCcpIC0tPgo8IS0tIFYobW5ldDMpJG5hbWUgPC0gVihtbmV0MikkbmFtZSAtLT4KPCEtLSBzdW1tYXJ5KG1uZXQzKSAtLT4KPCEtLSBgYGAgLS0+CjwhLS0gKkludGVycHJldGF0aW9uOiogTm90ZSBoZXJlIHRoYXQgbW5ldDMgaGFzIHRoZSBleGFjdCBudW1iZXIgb2Ygbm9kZXMgYW5kIGVkZ2VzIGFzIG1uZXQyLiBUaGUgb25seSBkaWZmZXJlbmNlIGlzIHRoZXJlIGlzIGEgd2VpZ2h0IGF0dHJpYnV0ZSBvbiBldmVyeSBlZGdlLiBXZSBoYXZlIHJlbW92ZWQgdGhlIG5hbWUgYXR0cmlidXRlIG9uIGVhY2ggbm9kZSBmb3IgY29udmVuaWVuY2UgaXNzdWUuIC0tPgoKKlZpc3VhbGl6YXRpb24qCgpOb3csIGxldCdzIG5vdyB2aXN1YWxpemUgb3VyIGNvLWF1dGhvcnNoaXAgZ3JhcGggbmV0d29yayBtbmV0MiB1c2luZyB0aGUgS2FtYWRhIGFuZCBLYXdhaSBsYXlvdXQ6CmBgYHtyfQojIGwubW5ldCA8LSBsYXlvdXQua2FtYWRhLmthd2FpKG1uZXQpCiMgbC5tbmV0MiA8LSBsYXlvdXQua2FtYWRhLmthd2FpKG1uZXQyKQpsLm1uZXQyIDwtIGxheW91dC5rYW1hZGEua2F3YWkobW5ldDIpCmBgYAoKCmBgYHtyLGZpZy5oZWlnaHQ9MTUsZmlnLndpZHRoPTE1fQojIHBsb3QobW5ldCwgbGF5b3V0PWwubW5ldCwgdmVydGV4LmxhYmVsPU5BLCB2ZXJ0ZXguc2l6ZT0yKQojIHBsb3QobW5ldDIsIGxheW91dD1sLm1uZXQyLCB2ZXJ0ZXgubGFiZWw9TkEsIHZlcnRleC5zaXplPTIsIHZlcnRleC5jb2xvcj0ncmVkJykKcGxvdChtbmV0MiwgbGF5b3V0PWwubW5ldDIsIHZlcnRleC5sYWJlbD1OQSwgdmVydGV4LnNpemU9MiwgdmVydGV4LmNvbG9yPSdibHVlJykgIyBXZSB1c2UgbC5tbmV0MiBhcyBsYXlvdXQKYGBgCgpgYGB7cn0KZC5tbmV0IDwtIGRlZ3JlZShtbmV0MikgIyBEZWdyZWUgb2Ygc2ltcGxlIGdyYXBoIG1uZXQyClYobW5ldDIpJGRlZ3JlZSA8LSBkLm1uZXQKZGQubW5ldCA8LSBkZWdyZWUuZGlzdHJpYnV0aW9uKG1uZXQyKSAjIENvbXB1dGUgZGVncmVlIGRpc3RyaWJ1dGlvbgpkIDwtIDE6bWF4KGQubW5ldCktMQppbmQgPC0gKGRkLm1uZXQgIT0gMCkKYS5ubi5kZWcubW5ldCA8LSBncmFwaC5rbm4obW5ldDIsIFYobW5ldDIpKSRrbm4KbWVhbihkLm1uZXQpCmBgYAoKTm93LCBwbG90aW5nIGRlZ3JlZSBkaXN0cmlidXRpb24gb2YgdGhlIHNpbXBsaWZpZWQgZ3JhcGguCmBgYHtyLGZpZy53aWR0aD04fQpwYXIobWZyb3c9YygxLDIpKQpoaXN0KGQubW5ldCwgY29sPSJsaWdodGJsdWUiLCB4bGFiPSJWZXJ0ZXggRGVncmVlIiwgeWxhYj0iRnJlcXVlbmN5IiwgbWFpbj0iRGVncmVlIGRpc3RyaWJ1dGlvbiIpCnBsb3QoZFtpbmRdLCBkZC5tbmV0W2luZF0sIGxvZz0ieHkiLCBjb2w9ImJsdWUiLCB0eXBlPSdwJywgeGxhYj1jKCJMb2ctRGVncmVlIiksIHlsYWI9YygiTG9nLUludGVuc2l0eSIpLG1haW49IkxvZy1Mb2cgRGVncmVlIERpc3RyaWJ1dGlvbiIpCmBgYAoqSW50ZXJwcmV0YXRpb246KiBGcm9tIHRoZSBwbG90IG9uIHRoZSByaWdodCwgdGhlIGRlZ3JlZXMgb2YgdGhlIG5vZGUgc2VlbSB0byBmb2xsb3cgYSBoZWF2eSByaWdodC10YWlsIGRpc3RyaWJ1dGlvbiwgY2hhcmFjdGVyaXN0aWMgb2YgYSBwb3dlciBsYXcgZGlzdHJpYnV0aW9uIHdpdGggYW4gYXZlcmFnZSBkaXN0cmlidXRpb24gb2YgJDEwNi40NiQuIExldCdzIGludmVzdGlnYXRlIHRoZSBtYW5uZXIgaW4gd2hpY2ggbm9kZXMgb2YgZGlmZmVyZW50IGRlZ3JlZXMgYXJlIGxpbmtlZCB3aXRoIGVhY2ggb3RoZXIgaW4gdGhlIGNvYXV0aG9yc2hpcCBuZXR3b3JrLiBGb3IgdGhpcyBwdXJwb3NlLCB3ZSBicmluZyBpbiB0aGUgbm90aW9uIG9mIHRoZSBhdmVyYWdlIGRlZ3JlZSBvZiB0aGUgbmVpZ2hib3JzIG9mIGEgZ2l2ZW4gbm9kZS4gV2UgdGhlbiBwbG90IHRoZSBhdmVyYWdlIG5laWdoYm9yIGRlZ3JlZSBhZ2FpbnN0IG5vZGUgZGVncmVlLgoKYGBge3IsZmlnLmhlaWdodD03fQpwbG90KGQubW5ldCwgYS5ubi5kZWcubW5ldCwgbG9nPSJ4eSIsIGNvbD0iZ29sZGVucm9kIiwgdHlwZT0ncCcsIHhsYWI9YygiTG9nIFZlcnRleCBEZWdyZWUiKSwgeWxhYj1jKCJMb2cgQXZlcmFnZSBOZWlnaGJvciBEZWdyZWUiKSkKYGBgCipJbnRlcnByZXRhdGlvbjoqIFRoZSBwbG90IGFib3ZlIHN1Z2dlc3RzIHRoYXQgd2hpbGUgdGhlcmUgaXMgYSB0ZW5kZW5jeSBvZiBub2RlcyBvZiBoaWdoZXIgZGVncmVlcyB0byBsaW5rIHdpdGggc2ltaWxhciBub2Rlcywgbm9kZXMgb2YgbG93ZXIgZGVncmVlIHRlbmQgdG8gbGluayB3aXRoIG5vZGVzIG9mIGJvdGggbG93ZXIgYW5kIGhpZ2hlciBkZWdyZWVzLiBJbiBvdGhlciB3b3Jkcywgd2hpbGUgcHJvbWluZW50IGF1dGhvcnMgd2l0aCBpbXBvcnRhbnQgY29sbGFib3JhdGlvbnMgdGVuZCB0byBjb2xsYWJvcmF0ZSB3aXRoIHNpbWlsYXIgYXV0aG9ycywgeW91bmcgb3IgbGVzcyBwcm9saWZpYyBhdXRob3JzIHRlbmQgdG8gY29sbGFib3JhdGUgd2l0aCBib3RoIHByb2xpZmljIGFuZCBhdXRob3JzIHdpdGggdmVyeSBmZXcgY29sbGFib3JhdGlvbnMuCgoKTGV0J3MgY29tcHV0ZSB0aGUgb3RoZXIgMyBub2RlIGNlbnRyYWxpdHkgbWVhc3VyZXM6CmBgYHtyfQpBIDwtIGdldC5hZGphY2VuY3kobW5ldDIsIHNwYXJzZSA9IEZBTFNFKQpnIDwtIG5ldHdvcms6OmFzLm5ldHdvcmsubWF0cml4KEEpCmBgYAoKCipDbG9zZW5lc3MgY2VudHJhbGl0eToqIGNhcHR1cmVzIHRoZSBub3Rpb24gdGhhdCBhIG5vZGUgaXMgY2VudHJhbCBpZiBpdCBjbG9zZSB0byBtYW55IG90aGVyIG5vZGVzCkNvbnNpZGVyaW5nIGEgbmV0d29yayAkRz0oVixFKSQgd2hlcmUgJFYkIGlzIHRoZSBzZXQgb2Ygbm9kZXMgYW5kICRFJCwgdGhlIHNldCBvZiBlZGdlcywgdGhlIGNsb3NlbmVzcyBjZW50cmFsaXR5ICRjX3tDbH0odikkIG9mIGEgbm9kZSAkdiQgaXMgZGVmaW5lZCBhczogCiQkY197Q2x9KHYpPVxmcmFjezF9e1xzdW1fe3VcaW4gVn1kaXN0KHYsdSl9JCQKJGRpc3Qodix1KSQgaXMgZGVmaW5lZCBhcyB0aGUgZ2VvZGVzaWMgZGlzdGFuY2UgYmV0d2VlbiB0aGUgbm9kZXMgJHUsdiBcaW4gViQuCmBgYHtyfQpjbC5tbmV0IDwtIGNsb3NlbmVzcyhtbmV0MikKVihtbmV0MikkY2xvc2VuZXNzIDwtIGNsLm1uZXQKc3VtbWFyeShjbC5tbmV0KQpgYGAKYGBge3IsZmlnLmhlaWdodD0xNSxmaWcud2lkdGg9MTV9CnBsb3QobW5ldDIsIGxheW91dD1sLm1uZXQyLCBtYWluPSJDbG9zZW5lc3MgY2VudHJhbGl0eSIsIHZlcnRleC5sYWJlbD0iIiwgdmVydGV4LnNpemU9MiAqIHNxcnQoY2wubW5ldCoxRTYpLCB2ZXJ0ZXguY29sb3I9J2xpZ2h0Ymx1ZScpCmBgYAoKCipCZXR3ZWVuZXNzIGNlbnRyYWxpdHk6KiBzdW1tYXJpemVzIHRoZSBleHRlbnQgdG8gd2hpY2ggYSBub2RlIGlzIGxvY2F0ZWQgYmV0d2VlbiBvdGhlciBwYWlycyBvZiBub2Rlcy4gSXQgcmVsYXRlcyB0byB0aGUgdG8gdGhlIHBlcnNwZWN0aXZlIHRoYXQgaW1wb3J0YW5jZSByZWxhdGVzIHRvIHdoZXJlIGEgbm9kZSBpcyBsb2NhdGVkIHdpdGggcmVzcGVjdCB0byB0aGUgcGF0aHMgaW4gdGhlIG5ldHdvcmsgZ3JhcGguIEFjY29yZGluZyB0byBGcmVlbWFuLCBpdCBpcyBkZWZpbmVkIGFzOgokJGNfe0J9KHYpPVxmcmFje1xzaWdtYSAocyx0fHYpfXtcc3VtX3tzIFxuZXEgdCBcbmVxIHYgXGluIFZ9XHNpZ21hIChzLHQpfSQkCndoZXJlICRcc2lnbWEocyx0fHYpJCBpcyB0aGUgdG90YWwgbnVtYmVyIG9mIHNob3J0ZXN0IHBhdGhzIGJldHdlZW4gJHMkIGFuZCAkdCQgdGhhdCBwYXNzIHRocm91Z2ggJHYkLCBhbmQgJFxzaWdtYSAocyx0KSQgaXMgdGhlIHRvdGFsIG51bWJlciBvZiBzaG9ydGVzdCBwYXRocyBiZXR3ZWVuICRzJCBhbmQgJHQkIHJlZ2FyZGxlc3Mgb2Ygd2hldGhlciBvciBub3QgdGhleSBwYXNzIHRocm91Z2ggJHYkLgpgYGB7cn0KYncubW5ldCA8LSBiZXR3ZWVubmVzcyhtbmV0MikKVihtbmV0MikkYmV0d2Vlbm5lc3MgPC0gYncubW5ldApzdW1tYXJ5KGJ3Lm1uZXQpCmBgYApgYGB7cixmaWcuaGVpZ2h0PTE1LGZpZy53aWR0aD0xNX0KcGxvdChtbmV0MiwgbGF5b3V0PWwubW5ldDIsIG1haW49IkJldHdlZW5uZXNzIGNlbnRyYWxpdHkiLCB2ZXJ0ZXgubGFiZWw9IiIsIHZlcnRleC5zaXplPTEgKiBzcXJ0KGJ3Lm1uZXQvMTAwMCksIHZlcnRleC5jb2xvcj0nbGlnaHRncmVlbicpCmBgYAoKCgoqRWlnZW52ZWN0b3IgY2VudHJhbGl0eToqIHNlZWtzIHRvIGNhcHR1cmUgdGhlIGlkZWEgdGhhdCB0aGUgbW9yZSBjZW50cmFsIHRoZSBuZWlnaGJvcnMgb2YgYSBub2RlIGFyZSwgdGhlIG1vcmUgY2VudHJhbCB0aGF0IG5vZGUgaXRzZWwgaXMuIEFjY29yZGluZyB0byBCb25hY2ljaCBhbmQgS2F0eiBbUGFnZSA0OCwgYm9vayBLb2xhY3p5ayBhbmQgQ3NhcmRpLCAybmQgZWRpdGlvbiwgMjAwOV0sIHRoZSBFaWdlbnZlY3RvciBjZW50cmFsaXR5IG1lYXN1cmUgaXMgZGVmaW5lZCBhczoKJCRjX3tFX2l9KHYpPVxhbHBoYSBcc3VtX3tce3Usdlx9XGluIEV9Y197RV9pfSh1KSQkCgpXaGVyZSB0aGUgdmVjdG9yICRcbWF0aGJme2N9X3tFX2l9PShjX3tFX2l9KDEpLFxkb3RzICxjX3tFX2l9KE5fdikpXlQkIGlzIHRoZSBzb2x1dGlvbiB0byB0aGUgZWlnZW52YWx1ZSBwcm9ibGVtICRcbWF0aGJme0FjfV97RV9pfT1cYWxwaGFeey0xfVxtYXRoYmZ7Y31fe0VfaX0kLCB3aGVyZSAkXG1hdGhiZntBfSQgaXMgdGhlIGFkamFjZW5jeSBtYXRyaXggZm9yIHRoZSBuZXR3b3JrICRHJC4gQWNjb3JkaW5nIHRvIEJvbmFjaWNoLCBhbiBvcHRpbWFsIGNob2ljZSBvZiAkXGFscGhhXnstMX0kIGlzIHRoZSBsYXJnZXN0IGVpZ2VudmFsdWUgb2YgJFxtYXRoYmZ7QX0kLgpgYGB7cn0KZXYubW5ldCA8LSBldmNlbnQobW5ldDIpJHZlY3RvcgpWKG1uZXQyKSRlaWdlbnYgPC0gZXYubW5ldApzdW1tYXJ5KGV2Lm1uZXQpCmBgYApgYGB7cixmaWcuaGVpZ2h0PTE1LGZpZy53aWR0aD0xNX0KcGxvdChtbmV0MiwgbGF5b3V0PWwubW5ldDIsIG1haW49IkVpZ2VudmVjdG9ycyBjZW50cmFsaXR5IiwgdmVydGV4LmxhYmVsPSIiLCB2ZXJ0ZXguc2l6ZT0yICogc3FydChldi5tbmV0KjEwKSwgdmVydGV4LmNvbG9yPSdwaW5rJykKYGBgCgoKCipWaXN1YWxpemF0aW9uIGhpZ2hsaXRpbmcgSHVicyBhbmQgQXV0aG9yaXRpZXMqCmBgYHtyLGZpZy5oZWlnaHQ9MTUsZmlnLndpZHRoPTE1fQojIHBhcihtZnJvdz1jKDEsMikpCiMgcGxvdChtbmV0MiwgbGF5b3V0PWwubW5ldDIsIG1haW49Ikh1YnMiLCB2ZXJ0ZXgubGFiZWw9IiIsIHZlcnRleC5zaXplPTEwICogc3FydChodWIuc2NvcmUobW5ldDIpJHZlY3RvciksIHZlcnRleC5jb2xvcj0nZ3JlZW4nKQojIHBsb3QobW5ldDIsIGxheW91dD1sLm1uZXQyLCBtYWluPSJBdXRob3JpdGllcyIsIHZlcnRleC5sYWJlbD0iIiwgdmVydGV4LnNpemU9MTAgKgojIHNxcnQoYXV0aG9yaXR5LnNjb3JlKG1uZXQyKSR2ZWN0b3IpLCB2ZXJ0ZXguY29sb3I9J3JlZCcpCmBgYAoKYGBge3J9ClYobW5ldDIpJGh1YlNjb3JlIDwtIGh1Yi5zY29yZShtbmV0MikkdmVjdG9yClYobW5ldDIpJGF1dGhTY29yZSA8LSBhdXRob3JpdHkuc2NvcmUobW5ldDIpJHZlY3RvcgpzdW1tYXJ5KG1uZXQyKQpgYGAKTm90ZSB0aGF0IHRoZSBIdWIgc2NvcmVzIGFuZCBBdXRob3JpdHkgc2NvcmVzIGFyZSBleGFjdGx5IGVxdWFsIHRvIHRoZSBlaWdlbnZlY3RvcnMgb2YgdGhlIG5vZGVzIGluIHRoZSBuZXR3b3JrLiBBbiBleHBsb3JhdGlvbiBvZiB0aGUgYXV0aF9kYXRhIGZyYW1lIGJlbG93IGhlbHBzIHRoZSByZWFkZXIgbm90aWNlIHRoZSByZW1hcmsuCgpgYGB7cn0KYXV0aF9kYXRhIDwtIGRhdGEuZnJhbWUoaWQ9YXMubnVtZXJpYyhWKG1uZXQyKSRpZCksbmFtZT1WKG1uZXQyKSRuYW1lLG51bVB1Yj1hcy5udW1lcmljKFYobW5ldDIpJG51bVB1YiksdGltZXNDaXRlZD1hcy5udW1lcmljKFYobW5ldDIpJHRpbWVzQ2l0ZWQpLGRlZ3JlZT1WKG1uZXQyKSRkZWdyZWUsY2xvc2VuZXNzPVYobW5ldDIpJGNsb3NlbmVzcyxiZXR3ZWVubmVzcz1WKG1uZXQyKSRiZXR3ZWVubmVzcyxlaWdlblY9VihtbmV0MikkZWlnZW52LGh1YlNjb3JlPVYobW5ldDIpJGh1YlNjb3JlLGF1dGhTY29yZT1WKG1uZXQyKSRhdXRoU2NvcmUpCmF1dGhfZGF0YSRhZmZpbGlhdGlvbjwtVihtbmV0MikkcGxhY2UKYXV0aF9kYXRhJGNpdHk8LVYobW5ldDIpJGFmZmlsCmF1dGhfZGF0YSRjb3VudHJ5PC1WKG1uZXQyKSRjb3VudHJ5CmF1dGhfZGF0YSRjaXR5W3doaWNoKGF1dGhfZGF0YSRjaXR5PT0nTE9ORE9OIFdDMUUgN0hUJyldPC0nTE9ORE9OJwphdXRoX2RhdGEkY2l0eVt3aGljaChhdXRoX2RhdGEkY2l0eT09J0xPTkRPTiBXQzEnKV08LSdMT05ET04nCmF1dGhfZGF0YSRjaXR5W3doaWNoKGF1dGhfZGF0YSRjaXR5PT0nTE9ORE9OIE5XMyAyUUcnKV08LSdMT05ET04nCmF1dGhfZGF0YSRjb3VudHJ5W3doaWNoKGF1dGhfZGF0YSRjaXR5PT0nQ09UT05PVScgJiBhdXRoX2RhdGEkY291bnRyeT09J0ZSQU5DRScpXTwtJ0JFTklOJwphdXRoX2RhdGEkY291bnRyeVt3aGljaChWKG1uZXQyKSRjb3VudHJ5PT0nTUEgVVNBJyldPC0nVVNBJwphdXRoX2RhdGEkY2l0eVt3aGljaChhdXRoX2RhdGEkY2l0eT09J0JPQk8gRElPVUxBU1NPIDAxJyldPC0nQk9CTyBESU9VTEFTU08nCgojIE9idGFpbmluZyBjb29yZGluYXRlcwojIGxpYnJhcnkoZ2dtYXApCiMgY29yZDwtdW5saXN0KGdlb2NvZGUocGFzdGUoYXV0aF9kYXRhJGNpdHlbMV0sYXV0aF9kYXRhJGNvdW50cnlbMV0sc2VwPScsICcpKSkKIyBsb25nbGF0PC1kYXRhLmZyYW1lKGlkPWF1dGhfZGF0YSRpZCwgbmFtZT1hdXRoX2RhdGEkbmFtZSwgbG9uZz1jb29yZFsxOjE3OTJdLGxhdD1jb29yZFsxNzkzOjM1ODRdKQojIHdyaXRlLmNzdihsb25nbGF0LGZpbGUgPSAnfi9Eb2N1bWVudHMvc25hL2dyYXBocy9hdXRoX2Nvb3JkaW5hdGVzLmNzdicpCgojIEdldHRpbmcgbm9kZSBpbmZvcm1hdGlvbgpsb25nbGF0PC1yZWFkLmNzdignfi9Eb2N1bWVudHMvc25hL2dyYXBocy9hdXRoX2Nvb3JkaW5hdGVzLmNzdicsaGVhZGVyID0gVCkKbG9uZ2xhdDwtc3Vic2V0KGxvbmdsYXQsIHNlbGVjdCA9IC1jKFgpKQphdXRoX2RhdGEkbG9uZzwtbG9uZ2xhdCRsb25nCmF1dGhfZGF0YSRsYXQ8LWxvbmdsYXQkbGF0CgojIEdldHRpbmcgZWRnZSBsaXN0IGFuZCByZWxhdGVkIGluZm9ybWF0aW9uCmVMaXN0PC1nZXQuZWRnZWxpc3QobW5ldDIsbmFtZXMgPSBUKQpjb2xuYW1lcyhlTGlzdCk8LWMoJ3NvdXJjZScsJ3RhcmdldCcpCmVMaXN0PC1hcy5kYXRhLmZyYW1lKGVMaXN0KQpuZXcxPC1lTGlzdApuZXcyPC1lTGlzdApuZXcxW108LWF1dGhfZGF0YSRsb25nW21hdGNoKHVubGlzdChlTGlzdCksYXV0aF9kYXRhJG5hbWUpXQpuZXcyW108LWF1dGhfZGF0YSRsYXRbbWF0Y2godW5saXN0KGVMaXN0KSxhdXRoX2RhdGEkbmFtZSldCmVkZ2VzPC1kYXRhLmZyYW1lKGlkPTE6bGVuZ3RoKEUobW5ldDIpKSxzb3VyY2U9ZUxpc3Qkc291cmNlLHRhcmdldD1lTGlzdCR0YXJnZXQsd2VpZ2h0PUUobW5ldDIpJHdlaWdodCx0aW1lc0NpdGVkPUUobW5ldDIpJHRpbWVzQ2l0ZWQsbG9uZ19zb3VyY2U9bmV3MSRzb3VyY2UsbGF0X3NvdXJjZT1uZXcyJHNvdXJjZSxsb25nX3RhcmdldD1uZXcxJHRhcmdldCxsYXRfdGFyZ2V0PW5ldzIkdGFyZ2V0KQpgYGAKCgoqQ2hhcmFjdGVyaXppbmcgRWRnZXM6KgpFZGdlIGJldHdlZW5uZXNzIGNlbnRyYWxpdHkgZXh0ZW5kcyBmcm9tIHRoZSBub3Rpb24gb2YgdmVydGV4IGNlbnRyYWxpdHkgYnkgYXNzaWduaW5nIHRvIGVhY2ggZWRnZSBhIHZhbHVlIHJlZmxlY3RpbmcgdGhlIG51bWJlciBvZiBzaG9ydGVzdCBwYXRocyB0cmF2ZXJzaW5nIHRoYXQgZWRnZS4gV2UgY29tcHV0ZSBlZGdlIGJldHdlZW5uZXNzIHRvIGFzc2VzcyB3aGljaCBjby1hdXRob3JzaGlwIGNvbGxhYm9yYXRpb25zIGFyZSBpbXBvcnRhbnQgZm9yIHRoZSBmbG93IG9mIGluZm9ybWF0aW9uLiBXZSB0aGVuIHByZXNlbnQgdGhlIDEwIG1vc3QgaW1wb3J0YW50IGNvbGxhYm9yYXRpb25zIGluIG91ciBtYWxhcmlhIGNvLWF1dGhvcnNoaXAgbmV0d29yay4KYGBge3J9CmViIDwtIGVkZ2UuYmV0d2Vlbm5lc3MobW5ldDIpCkUoc2ltcGxpZnkobW5ldCkpW29yZGVyKGViLCBkZWNyZWFzaW5nPVQpWzE6MTBdXQpgYGAKCgoqQ2hhcmFjdGVyaXppbmcgTmV0d29yayBjb2hlc2lvbjoqCgpJbiB0aGlzIHNlY3Rpb24sIHdlIGFyZSBnb2luZyB0byBhc3Nlc3MgdGhlIGV4dGVudCB0byB3aGljaCBzdWJzZXRzIG9mIGF1dGhvcnMgYXJlIGNvaGVzaXZlIHdpdGggdGhlIHJlc3BlY3QgdG8gdGhlaXIgcmVsYXRpb24gaW4gdGhlIGNvLWF1dGhvcnNoaXAgbmV0d29yay4gU3BlY2lmaWNhbGx5LCAqKndlIGFpbSBhdCBkZXRlcm1pbmluZyBpZiBjb2xsYWJvcmF0b3JzIChjby1hdXRob3JzKSBvZiBhIGdpdmVuIGF1dGhvciB0ZW5kIHRvIGNvbGxhYm9yYXRlIGFzIHdlbGwqKi4gKipXaGF0IHN1YnNldCBvZiBjb2xsYWJvcmF0aW5nIGF1dGhvcnMgdGVuZCB0byBiZSBtb3JlIHByb2R1Y3RpdmUgaW4gb3VyIG5ldHdvcms/KioKV2hpbGUgdGhlcmUgYXJlIG1hbnkgdGVjaG5pcXVlcyB0byBkZXRlcm1pbmUgbmV0d29yayBjb2hlc2lvbiwgd2UgY2hvb3NlIHRvIGludmVzdGlnYXRlIGxvY2FsIHRyaWFkcyBhbmQgZ2xvYmFsIGdpYW50IGNvbXBvbmVudHMsIGNsaXF1ZXMgZGV0ZWN0aW9uIGFzIHdlbGwgYXMgY2x1c3RlcmluZyBvciBjb21tdW5pdGllcyBkZXRlY3Rpb24gaW4gb3VyIG1hbGFyaWEgY28tYXV0aG9yc2hpcCBuZXR3b3JrLgoKKkNsaXF1ZXM6KiBBY2NvcmRpbmcgdG8gS29sYWN6eWsgYW5kIENzYXJkaSAoMjAwOSksIGNsaXF1ZXMgYXJlIGRlZmluZWQgYXMgY29tcGxldGUgc3ViZ3JhcGhzIHN1Y2ggdGhhdCBhbGwgbm9kZXMgd2l0aGluIHRoZSBzdWJzZXQgYXJlIGNvbm5lY3RlZCBieSBlZGdlcy4gV2UgY29tcHV0ZSB0aGUgbnVtYmVyIG9mIGNsaXF1ZXMgaW4gb3VyIG1hbGFyaWEgY28tYXV0aG9yc2hpcCBuZXR3b3JrLCB0aGVuIGNvbXB1dGUgdGhlIG51bWJlciBhbmQgc2l6ZSBvZiB0aGUgbWF4aW1hbCBjbGlxdWVzLgoKYGBge3J9CmNsaXF1ZS5udW1iZXIobW5ldDIpCmBgYApPdXIgbWFsYXJpYSBjby1hdXRob3JzaGlwIG5ldHdvcmsgY29udGFpbnMgMzY1IGNsaXF1ZXMuCgpgYGB7cn0KdGFibGUoc2FwcGx5KG1heGltYWwuY2xpcXVlcyhtbmV0MiksIGxlbmd0aCkpCmBgYApUaGUgdGFibGUgYWJvdmUgZGlzcGxheXMgdGhlIHNpemUgYW5kIG51bWJlciBvZiBtYXhpbWFsIGNsaXF1ZXMuIEZyb20gdGhlIHRhYmxlLCB3ZSBjYW4gc2VlIHRoYXQgb3VyIG5ldHdvcmsgY29udGFpbnMgOSBjbGlxdWVzIG9mIHNpemUgMiBhbmQgMTQgY2xpcXVlcyBvZiBzaXplIDMuIEl0IGFsc28gY29udGFpbnMgMTU1IGNsaXF1ZXMgb2Ygc2l6ZSA4IGFuZCAxNDIgY2xpcXVlcyBvZiBzaXplIDcuIExhcmdlciBjbGlxdWVzIHNpemVzIHJhbmdlIGZyb20gIDEwMiBhdXRob3JzIHRvIDM2NSBhdXRob3JzIGFuZCBhcmUgYWxsIGZvdW5kIG9uY2UgYWNyb3NzIHRoZSBuZXR3b3JrLgoKKkRlbnNpdHkgYW5kIHJlbGF0ZWQgbm90aW9ucyBvZiByZWxhdGl2ZSBmcmVxdWVuY3k6KgpEZWZpbmVkIGFzIHRoZSBmcmVxdWVuY3kgb2YgcmVhbGl6ZWQgZWRnZXMgcmVsYXRpdmUgdG8gcG90ZW50aWFsIGVkZ2VzLCB0aGUgZGVuc2l0eSBvZiBhIHN1YmdyYXBoICRIJCBpbiAkRyQgcHJvdmlkZXMgYSBtZWFzdXJlIG9mIGhvdyBjbG9zZSAkSCQgaXMgdG8gYmUgYSBjbGlxdWUgaW4gJEckLiBEZW5zaXR5IHZhbHVlcyB2YXJpZSBiZXR3ZWVuIDAgYW5kIDE6CiQkZGVuKEgpPVxmcmFje3xFX0h8fXt8Vl9IfChWX0gtMSkvMn0kJApIZXJlIHdlIGNvbXB1dGUgdGhlIGdlbmVyYWwgZGVuc2l0eSBvZiBvdXIgbWFsYXJpYSBjby1hdXRob3JzaGlwIG5ldHdvcmsuCgpgYGB7cn0KZ3JhcGguZGVuc2l0eShtbmV0MikKYGBgCgpXZSBhc3Nlc3MgdGhlIHJlbGF0aXZlIGZyZXF1ZW5jeSBvZiAkRyQgYnkgY29tcHV0aW5nIGl0cyB0cmFuc2l0aXZpdHkgZGVmaW5lZCBhczoKJCRjbF9UID0gXGZyYWN7M1x0YXVfXERlbHRhIChHKX17XHRhdV8zIChHKX0kJAp3aGVyZSAkXHRhdV9cRGVsdGEgKEcpJCBpcyB0aGUgbnVtYmVyIG9mIHRyaWFuZ2xlcyBpbiAkRyQsIGFuZCAkXHRhdV8zIChHKSQgaXMgdGhlIG51bWJlciBvZiBjb25uZWN0ZWQgdHJpcGxlcyAoc29tZXRpbWVzIHJlZmVycmVkIHRvIGFzIDItc3RhcikuIFRoaXMgbWVhc3VyZSBpcyBhbHNvIHJlZmVycmVkIHRvIGFzIHRoZSBmcmFjdGlvbiBvZiB0cmFuc2l0aXZlIHRyaXBsZXMuIEl0IHJlcHJlc2VudHMgYSBtZWFzdXJlIG9mIGdsb2JhbCBjbHVzdGVyaW5nIG9mICRHJCBzdW1tYXJpemluZyB0aGUgcmVsYXRpdmUgZnJlcXVlbmN5IHdpdGggd2hpY2ggY29ubmVjdGVkIHRyaXBsZXMgY2xvc2UgdG8gZm9ybSB0cmlhbmdsZXMuCmBgYHtyfQp0cmFuc2l0aXZpdHkobW5ldDIpCmBgYAogQW5vdGhlciBhbmFsb2d1ZSBvZiB0aGlzIG1lYXN1cmUgaXMgdGhlIGxvY2FsIHRyYW5zaXRpdml0eSBkZWZpbmVkIGFzOgogJCRjbCh2KT1cdGF1X1xEZWx0YSAodikvXHRhdV8zICh2KSQkCiB3aGVyZSAkXHRhdV9cRGVsdGEgKHYpJCBkZW5vdGVzIHRoZSBudW1iZXIgb2YgdHJpYW5nbGVzIGluICRHJCBpbnRvIHdoaWNoICR2IFxpbiBWJCBmYWxscyBhbmQgJFx0YXVfMyAodikkIGlzIHRoZSBudW1iZXIgb2YgY29ubmVjdGVkIHRyaXBsZXMgaW4gJEckIGZvciB3aGljaCB0aGUgdHdvIGVkZ2VzIGFyZSBib3RoIGluY2lkZW50IHRvICR2JC4gSGVyZSB3ZSBjb21wdXRlIHRoZSBsb2NhbCB0cmFuc2l0aXZpdHkgZm9yIGFsbCB0aGUgbm9kZXMgaW4gb3VyIG1hbGFyaWEgY28tYXV0aG9yc2hpcCBuZXR3b3JrLgogCmBgYHtyfQp0cjwtdHJhbnNpdGl2aXR5KG1uZXQyLCdsb2NhbCcsdmlkcyA9IDE6bGVuZ3RoKFYobW5ldDIpKSkKVihtbmV0MikkdHJhbnNpdGl2aXR5PC10cgphdXRoX2RhdGEkdHJhbnNpdGl2aXR5PC10cgpgYGAKCgoqQ29ubmVjdGl2aXR5LCBDdXRzLCBhbmQgRmxvd3M6KgpJbiB0aGlzIHNlY3Rpb24sIHdlIG1lYXN1cmUgaG93IGNsb3NlIG91ciBtYWxhcmlhIGNvLWF1dGhvcnNoaXAgaXMgY2xvc2UgdG8gc2VwYXJhdGUgaW50byBkaXN0aW5jdHMgc3ViZ3JhcGhzLiBXZSBhcmUgYWxzbyBpbnRlcmVzdGVkIGluIGFzc2Vzc2luZyBob3cgd2VsbCBpbmZvcm1hdGlvbiBmbG93cyBpbiB0aGUgbmV0d29yay4KV2UgZmlyc3Qgc3RhcnQgd2l0aCB0aGUgY29uY2VwdCBvZiBjb25uZWN0ZWRuZXNzLiBTaW5jZSBvdXIgbmV0d29yayBpcyBhbiB1bmRpcmVjdGVkIGdyYXBoLCB3ZSBkbyBub3QgY29uc2lkZXIgdGhlIGlkZWEgb2Ygd2VhayBhbmQgc3Ryb25nIGNvbm5lY3Rpdml0eS4gQSBncmFwaCAkRyQgaXMgc2FpZCB0byBiZSBjb25uZWN0ZWQgaWYgZXZlcnkgbm9kZSBpbiAkRyQgaXMgcmVhY2hhYmxlIGZyb20gZXZlcnkgb3RoZXIgbm9kZS4KYGBge3J9CmlzLmNvbm5lY3RlZChtbmV0MikKYGBgCkZyb20gdGhlIG91dHB1dCBhYm92ZSwgd2UgY2xlYXJseSBjb25jbHVkZSB0aGF0IG91ciBjby1hdXRob3JzaGlwIG5ldHdvcmsgaXMgbm90IGNvbm5lY3RlZC4KCk9mdGVuIHRpbWUsIG9uZSBvZiB0aGUgY29ubmVjdGVkIGNvbXBvbmVudHMgY2FuIGRvbWluYXRlIHRoZSBvdGhlcnMsIGhlbmNlIHRoZSBpZGVhIG9mICpnaWFudCBjb21wb25lbnQqLiBMZXQncyB0aGVuIGNlbnN1cyBvdXIgY28tYXV0aG9yc2hpcDoKYGBge3J9CmNvbXBzPC1kZWNvbXBvc2UuZ3JhcGgobW5ldDIpCnRhYmxlKHNhcHBseShjb21wcyx2Y291bnQpKQpgYGAKCkZyb20gdGhlIG91dHB1dCBvZiB0aGUgY2Vuc3VzIG9mIGFsbCBjb25uZWN0ZWQgY29tcG9uZW50cyBvZiB0aGUgbmV0d29yayBhYm92ZSwgd2UgY2FuIHRoYXQgdGhlcmUgY2xlYXJseSBpcyBhIGdpYW50IGNvbXBvbmVudCBjb250YWluaW5nICQxNjg2LzE3OTJcYXBwcm94IDk0XCUkIG9mIGFsbCB0aGUgdmVydGljZXMgaW4gdGhlIG5ldHdvcmsgd2l0aCBub25lIG9mIHRoZSBvdGhlciBjb21wb25lbnRzIGFsb25lIGNhcnJ5aW5nIGV2ZW4gJDFcJSQgb2YgdGhlIHZlcnRpY2VzLgoKV2UgZnVydGhlciBkZXZvdGUgY2xvc2VyIGF0dGVudGlvbiB0byB0aGlzIGdpYW50IGNvbXBvbmVudC4KYGBge3J9Cm1uZXQyLmdjIDwtIGRlY29tcG9zZS5ncmFwaChtbmV0MilbWzFdXQpzdW1tYXJ5KG1uZXQyLmdjKQpgYGAKIAogTGV0J3MgcGxvdCB0aGlzIGdpYW50IGNvbXBvbmVudDoKIApgYGB7cixmaWcuaGVpZ2h0PTE1LGZpZy53aWR0aD0xNX0KcGxvdChtbmV0Mi5nYywgbGF5b3V0PWxheW91dC5rYW1hZGEua2F3YWkobW5ldDIuZ2MpLCB2ZXJ0ZXgubGFiZWw9TkEsIHZlcnRleC5zaXplPTIsIGVkZ2Uud2lkdGg9MC4wOCwgdmVydGV4LmNvbG9yPSdsaWdodGJsdWUnKQpgYGAKCk9uZSBpbXBvcnRhbnQgY2hhcmFjdGVyaXN0aWMgb2JzZXJ2ZWQgaW4gZ2lhbnQgY29tcG9uZW50IGlzIHRoZSBzby1jYWxsZWQgKnNtYWxsLXdvcmxkKiBwcm9wZXJ0eSB3aGljaCByZWZlcnMgdG8gdGhlIHNpdHVhdGlvbiB3aGVyZWluICoqdGhlIHNob3J0ZXN0LXBhdGggZGlzdGFuY2UgYmV0d2VlbiBwYWlycyBvZiBub2RlcyBpcyBnZW5lcmFsbHkgc21hbGwqKiBhbmQgKip0aGUgY2x1c3RlcmluZyBpcyByZWxhdGl2ZWx5IGhpZ2gqKi4gRm9yIG91ciBtYWxhcmlhIGNvLWF1dGhvcnNoaXAgbmV0d29yaywgbGV0J3MgY29tcHV0ZSB0aGUgYXZlcmFnZSBwYXRoIGxlbmd0aApgYGB7cn0KYXZlcmFnZS5wYXRoLmxlbmd0aChtbmV0Mi5nYykKYGBgCmFuZCB0aGUgbG9uZ2VzdCBvZiBwYXRocwpgYGB7cn0KZGlhbWV0ZXIobW5ldDIuZ2MpCmBgYAoKTGV0J3MgYXNzZXNzIHRoZSB0cmFuc2l0aXZpdHkgb2Ygb3VyIGdpYW50IGNvbXBvbmVudDoKYGBge3J9CnRyYW5zaXRpdml0eShtbmV0Mi5nYykKYGBgCgoqSW50ZXJwcmV0YXRpb246KiAqKldlIGNhbiBzZWUgdGhhdCB0aGUgYXZlcmFnZSBwYXRoIGxlbmd0aCBvZiB0aGUgZ2lhbnQgY29tcG9uZW50IG9mIG91ciBjby1hdXRob3JzaGlwIG5ldHdvcmsgaXMgc21hbGwgYW5kIHRoZSBsb25nZXN0IG9mIHBhdGhzIGlzIG5vdCBtdWNoIGJpZ2dlci4gSGVuY2Ugb3VyIGdpYW50IGNvbXBvbmVudCBoYXMgYWxsIHRoZSBjaGFyYWN0ZXJpc3RpY3Mgb2YgYSBzbWFsbC13b3JsZC4qKiBJbiBhZGRpdGlvbiwgdGhlIGNsdXN0ZXJpbmcgaW4gdGhpcyBuZXR3b3JrIGlzIHZlcnkgbGFyZ2UgaW5kaWNhdGluZyB0aGF0ICoqOTYlIG9mIHRoZSBjb25uZWN0ZWQgdHJpcGxlcyBhcmUgY2xvc2UgdG8gZm9ybSB0cmlhbmdsZXMqKi4KCgpXZSBpbnZlc3RpZ2F0ZSB0aGUgY29uY2VwdHMgb2YgdmVydGV4IGFuZCBlZGdlIGN1dHMgZGVyaXZlZCBmcm9tIHRoZSBjb25jZXB0IG9mIHZlcnRleChlZGdlKSBjb25uZWN0aXZpdHkuIFRoZSB2ZXJ0ZXggKGVkZ2UpIGNvbm5lY3Rpdml0eSBvZiBhIGdyYXBoICRHJCBpcyB0aGUgbGFyZ2VzdCBpbnRlZ2VyIHN1Y2ggdGhhdCAkRyQgaXMgazB2ZXJ0ZXgtIChlZGdlLSkgY29ubmVjdGVkLgoKYGBge3J9CnZlcnRleC5jb25uZWN0aXZpdHkobW5ldDIuZ2MpCmBgYAoKYGBge3J9CmVkZ2UuY29ubmVjdGl2aXR5KG1uZXQyLmdjKQpgYGAKSW4gdGhlIGNhc2Ugb2YgdGhlIGdpYW50IGNvbXBvbmVudCBvZiBvdXIgY28tYXV0aG9yc2hpcCBuZXR3b3JrLCB0aGUgdmVydGV4IGFuZCBlZGdlIGNvbm5lY3Rpdml0eSBhcmUgYm90aCBlcXVhbCB0byAxLCAqKnRodXMgcmVxdWlyZXMgdGhlIHJlbW92YWwgb2Ygb25seSBhIHNpbmdsZSB3ZWxsLWNob3NlbiBub2RlIChhdXRob3IpIG9yIGNvbGxhYm9yYXRpb24gaW4gb3JkZXIgdG8gYnJlYWsgdGhpcyBzdWJncmFwaCBpbnRvIGFkZGl0aW9uYWwgY29tcG9uZW50cyoqLgoKQSBzZXQgb2Ygbm9kZXMgKGVkZ2VzKSB0aGF0IGRpc2Nvbm5lY3RzIHRoZSBncmFwaCBpcyBjYWxsZWQgYSAqdmVydGV4IGN1dCAoZWRnZSBjdXQpKi4gQSBzaW5nbGUgbm9kZSAoYXV0aG9yKSB0aGF0IGRpc2Nvbm5lY3RzIG9mIHN1Y2ggdmVydGljZXMgaXMgY2FsbGVkIGEgKmN1dCB2ZXJ0ZXgqIGFuZCBjYW4gcHJvdmlkZSBhIHNlbnNlIG9mIHdoZXJlIGEgbmV0d29yayBpcyB2dWxuZXJhYmxlLiBMZXQncyBpZGVudGlmeSBzdWNoIHdlYWsgcG9pbnRzIGluIG91ciBjby1hdXRob3JzaGlwIG5ldHdvcms6IApgYGB7cn0KbW5ldDIuY3V0LnZlcnRpY2VzIDwtIGFydGljdWxhdGlvbi5wb2ludHMobW5ldDIuZ2MpCm1uZXQyLmN1dC52ZXJ0aWNlcwpgYGAKVGhlIGFib3ZlIGxpc3RlZCBhdXRob3JzIGNvbnN0aXR1dGUgdGhlIHdlYWsgKmFydGljdWxhdGlvbiBwb2ludHMqIG9mIG91ciBjby1hdXRob3JzaGlwIG5ldHdvcmsgYnV0IGFsc28gdGhlIG1vc3QgaW1wb3J0YW50IG5vZGVzIG9mIG91ciBuZXR3b3JrLgoKYGBge3J9Cmxlbmd0aChtbmV0Mi5jdXQudmVydGljZXMpCmBgYApJbiBvdXIgbWFsYXJpYSBjby1hdXRob3JzaGlwIG5ldHdvcmssIGxlc3MgdGhhbiAqKjElKiogb2YgdGhlIG5vZGVzIGFyZSBjdXQgdmVydGljZXMgbWVhbmluZyB0aGF0IHRoZSB2dWxuZXJhYmlsaXR5IG9mIHRoZSBuZXR3b3JrIGlzIGRlcGVuZGFudCBvbiBhIHZlcnkgc21hbGwgc2V0IG9mIGF1dGhvcnMgaW4gdGhlIGNvLWF1dGhvcnNoaXAgbmV0d29yay4KCgoqKkdyYXBoIFBhcnRpdGlvbmluZzoqKgpSZWd1bGFybHkgZnJhbWVkIGFzIGNvbW11bml0eSBkZXRlY3Rpb24gcHJvYmxlbSwgZ3JhcGggcGFydGl0aW9uaW5nIGlzIGFuIHVuc3VwZXJ2aXplZCBtZXRob2QgdXNlZCBpbiB0aGUgYW5hbHlzaXMgb2YgbmV0d29yayBkYXRhIHRvIGZpbmQgc3Vic2V0cyBvZiBub2RlcyB0aGF0IGRlbW9uc3RyYXRlIGEgJ2NvaGVzaXZlbmVzcycgd2l0aCByZXNwZWN0IHRvIHRoZWkgdW5kZXJseWluZyByZWxhdGlvbmFsIHBhdHRlcm5zLiBDb2hlc2l2ZSBzdWJzZXRzIG9mIG5vZGVzIGdlbmVyYWxseSBhcmUgd2VsbCBjb25uZWN0ZWQgYW1vbmcgdGhlbXNlbHZlcyBhbmQgYXJlIHdlbGwgc2VwYXJhdGVkIGZyb20gdGhlIG90aGVyIG5vZGVzIGluIHRoZSBncmFwaC4gSGVyZSwgd2UgcGVyZm9ybSB0d28gd2VsbCBlc3RhYmxpc2hlZCBtZXRob2RzIG9mIGdyYXBoIHBhcnRpdGlvbmluZzogKkhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nKiBhbmQgKlNwZWN0cmFsIGNsdXN0ZXJpbmcqLgoKKkhpZXJhcmNoaWNhbCBDbHVzdGVyaW5nOioKSGllcmFyY2hpY2FsIGNsdXN0ZXJpbmcgbWV0aG9kcyBhcmUgb2YgdHdvIGtpbmRzOgotIGFnZ2xvbWVyYXRpdmU6ICJiYXNlZCBvbiB0aGUgc3VjY2Vzc2l2ZSBjb2Fyc2VuaW5nIG9mIHBhcnRpdGlvbnMgdGhyb3VnaCB0aGUgcHJvY2VzcyBvZiBtZXJnaW5nIiwgaXQgdXNlcyBtb2R1bGFyaXR5IGFzIG1ldHJpY3MuCi0gZGl2aXNpdmU6ICJiYXNlZCBvbiB0aGUgc3VjY2Vzc2l2ZSByZWZpbmVtZW50IG9mIHBhcnRpdGlvbnMgdGhyb3VnaCB0aGUgcHJvY2VzcyBvZiBzcGxpdHRpbmciCgpIZXJlLCB3ZSBhcHBseSB0aGUgYWdnbG9tZXJhdGl2ZSBtZXRob2Qgb24gb3VyIG1hbGFyaWEgY28tYXV0aG9yc2hpcCBuZXR3b3JrOgpgYGB7cn0KY29tLm1uZXQyIDwtIGZhc3RncmVlZHkuY29tbXVuaXR5KG1uZXQyKQpWKG1uZXQyKSRjb21tdW5pdHkgPC0gY29tLm1uZXQyJG1lbWJlcnNoaXAKbGVuZ3RoKGNvbS5tbmV0MikKYGBgClRoZSBhZ2dsb21lcmF0aXZlIGhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nIGlkZW50aWZpZXMgMjMgY29tbXVuaXRpZXMgaW4gb3VyIGNvLWF1dGhvcnNoaXAgbmV0d29yay4KYGBge3J9CnNpemVzKGNvbS5tbmV0MikKYGBgCkxhcmdlIGNvbW11bml0aWVzIGNvbnRhaW4gYmV0d2VlbiAyMDIgYW5kIDU2OSBhdXRob3JzLiBNZWRpdW0gc2l6ZSBjb21tdW5pdGllcyBjb250YWluIGJldHdlZW4gMTAgYW5kIDYyIGF1dGhvcnMuCgpMZXQncyBub3cgdmlzdWFsaXplIHRoZSBjb21tdW5pdGllczoKYGBge3IsZmlnLmhlaWdodD0xNSxmaWcud2lkdGg9MTV9CnBsb3QoY29tLm1uZXQyLCBtbmV0Mix2ZXJ0ZXgubGFiZWw9JycsCiAgICAgbGF5b3V0PWwubW5ldDIsCiAgICAgbWFyay5ncm91cHMgPSBOVUxMLAogICAgICMgdmVydGV4LnNpemUgPSAzLAogICAgICMgZWRnZS5jb2xvciA9IG1lbWIubW5ldDIsCiAgICAgZWRnZS53aWR0aCA9IDAuMDgsCiAgICAgdmVydGV4LnNpemU9MSsxICogc3FydChidy5tbmV0LzEwMDApCiAgICAgKQpgYGAKCgpgYGB7cn0KZ2MuY29tIDwtIGZhc3RncmVlZHkuY29tbXVuaXR5KG1uZXQyLmdjKQpsZW5ndGgoZ2MuY29tKQpgYGAKIFRoZXJlJ3Mgb25seSA3IGNvbW11bml0aWVzIGluIHRoZSBnaWFudCBjb21wb25lbnQgb2Ygb3VyIG5ldHdvcmsuCiAKIApgYGB7cn0Kc2l6ZXMoZ2MuY29tKQpgYGAKVGhlIDcgY29tbXVuaXRpZXMgY29udGFpbiBiZXR3ZWVuIDMwIGFuZCA1NjkgYXV0aG9ycy4KCkxldCdzIG5vdyB2aXN1YWxpemUgdGhlIDcgY29tbXVuaXRpZXMgaW4gdGhlIGdpYW50IGNvbXBvbmVudDoKYGBge3J9CmwuZ2M8LWxheW91dC5rYW1hZGEua2F3YWkobW5ldDIuZ2MpCmJ3LmdjPC1iZXR3ZWVubmVzcyhtbmV0Mi5nYykKVihtbmV0Mi5nYykkY29tbXVuaXR5IDwtIG1lbWJlcnNoaXAoZ2MuY29tKQpgYGAKCmBgYHtyLGZpZy5oZWlnaHQ9MTUsZmlnLndpZHRoPTE1fQpwbG90KGdjLmNvbSwgbW5ldDIuZ2MsdmVydGV4LmxhYmVsPScnLAogICAgICMgbGF5b3V0PWwubW5ldDIsCiAgICAgbGF5b3V0ID0gbC5nYywKICAgICBtYXJrLmdyb3VwcyA9IE5VTEwsCiAgICAgIyB2ZXJ0ZXguc2l6ZSA9IDMsCiAgICAgIyBlZGdlLmNvbG9yID0gbWVtYi5tbmV0MiwKICAgICBlZGdlLndpZHRoID0gMC4wNiwKICAgICB2ZXJ0ZXguc2l6ZT0xKzEgKiBzcXJ0KGJ3LmdjLzEwMDApCiAgICAgKQpgYGAKCkxldCdzIHBsb3QgdGhlIDcgY29tbXVuaXRpZXMgc2VwYXJhdGVseToKYGBge3IsZmlnLndpZHRoPTIwLGZpZy5oZWlnaHQ9NDB9CnBhcihjZXgubWFpbj0zKQpwYXIobWZyb3c9Yyg0LDIpKQojIFBsb3QgT3JpZ2luYWwgZ2lhbnQgY29tcG9uZW50IGdyYXBoCnBsb3QoZ2MuY29tLCBtbmV0Mi5nYyx2ZXJ0ZXgubGFiZWw9JycsCiAgICAgbWFpbiA9ICdNYWluIGNvbXBvbmVudCcsCiAgICAgIyBsYXlvdXQ9bC5tbmV0MiwKICAgICBsYXlvdXQgPSBsLmdjLAogICAgIG1hcmsuZ3JvdXBzID0gTlVMTCwKICAgICAjIHZlcnRleC5zaXplID0gMywKICAgICAjIGVkZ2UuY29sb3IgPSBtZW1iLm1uZXQyLAogICAgIGVkZ2Uud2lkdGggPSAwLjA1LAogICAgIHZlcnRleC5zaXplPTErMSAqIHNxcnQoYncuZ2MvMTAwMCkKICAgICApCgpmb3IoaSBpbiAxOjcpewogIGcgPC0gd2hpY2goVihtbmV0Mi5nYykkY29tbXVuaXR5PT1pKQogIEcuZ3JvdXAgPC0gc3ViZ3JhcGgobW5ldDIuZ2MsIGcpCiAgcGxvdChHLmdyb3VwLHZlcnRleC5sYWJlbD0nJywKICAgICBtYWluID0gcGFzdGUoJ0NvbW11bml0eS9QYXJ0aXRpb24gJyxpKSwKICAgICAjIGxheW91dD1sLm1uZXQyLAogICAgIGxheW91dCA9IGwuZ2NbZyxdLAogICAgIG1hcmsuZ3JvdXBzID0gTlVMTCwKICAgICB2ZXJ0ZXguY29sb3IgPSBnYy5jb20kbWVtYmVyc2hpcFtnXSwKICAgICAjIHZlcnRleC5zaXplID0gMywKICAgICAjIGVkZ2UuY29sb3IgPSBtZW1iLm1uZXQyLAogICAgIGVkZ2Uud2lkdGggPSAwLjA2LAogICAgIHZlcnRleC5zaXplPTErMSAqIHNxcnQoYncuZ2MvMTAwMCkKICAgICApCn0KYGBgCgpXZSBmaW5hbGx5IHNhdmUgYWxsIG91ciBnZW5lcmF0ZWQgUiBvYmplY3RzIGZvciBsYXRlciB1c2UuCmBgYHtyfQpzYXZlKG1uZXQsIGZpbGUgPSAnfi9Eb2N1bWVudHMvc25hL1Jfc2NyaXB0cy9tbmV0LnJkYScpCnNhdmUobW5ldDIsIGZpbGUgPSAnfi9Eb2N1bWVudHMvc25hL1Jfc2NyaXB0cy9tbmV0Mi5yZGEnKQpzYXZlKGF1dGhfZGF0YSwgZmlsZSA9ICd+L0RvY3VtZW50cy9zbmEvUl9zY3JpcHRzL2F1dGhfZGF0YS5yZGEnKQpzYXZlKGVkZ2VzLCBmaWxlID0gJ34vRG9jdW1lbnRzL3NuYS9SX3NjcmlwdHMvZWRnZXMucmRhJykKc2F2ZShtbmV0Mi5nYywgZmlsZSA9ICd+L0RvY3VtZW50cy9zbmEvUl9zY3JpcHRzL21uZXQyLmdjLnJkYScpCmBgYApgYGB7cn0KIyBzb3VyY2UoJ3Bsb3RseV9tYXAuUicpCmBgYAokXFwkCgpXZSBmaW5hbGx5IHBsb3QgdGhlIGNvbGxhYm9yYXRpb24gbWFwIHVzaW5nIFBsb3RseSBhbmQgYXZhaWxhYmxlIFtIRVJFXShodHRwczovL3Bsb3QubHkvfnJvc2VyaWNhem9uZGVrb24vNjEuZW1iZWQpLgoKPGlmcmFtZSBzcmM9Imh0dHBzOi8vcGxvdC5seS9+cm9zZXJpY2F6b25kZWtvbi82MS5lbWJlZCIgaGVpZ2h0PSI4MDAiIHdpZHRoPSIxMDAlIiAgICAgc2Nyb2xsaW5nPSJubyIgc2VhbWxlc3M9InNlYW1sZXNzIiBmcmFtZUJvcmRlcj0iMCI+PC9pZnJhbWU+CjwhLS0gPGltZyBzcmM9Imh0dHBzOi8vcGxvdC5seS9+cm9zZXJpY2F6b25kZWtvbi82MS5wbmciPiAtLT4KCiRcXCQKJFxcJAoKCioqTkVYVCBUVVRPUklBTDoqKiBbTWF0aGVtYXRpY2FsIE1vZGVsaW5nIGZvciBOZXR3b3JrIEdyYXBoc10oaHR0cDovL3JwdWJzLmNvbS9yb3NlcmljYXpvbmRla29uL21hdGhtb2RlbGluZyk=