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

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 # mnet <- read_graph(‘~/Documents/sna/graphs_old/CAnet.graphml’, format = ‘graphml’) mnet <- read_graph(‘./graphs/HIVnet.graphml’, format = ‘graphml’)

\(\\\)

Get a summary of the network:

rr summary(mnet)

IGRAPH UN-- 516 5114 -- 
+ 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 516 authors and 5114 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:

rr 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:

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.

The newly computed undirected graph contains 516 nodes and 3966 edges. Note that the simplify function in R does not capture the number of multiple collaborations between authors. In a previous tutorial, we have computed a simple version of the mnet network. Let’s instead of using the simplify() function, load mnet2 from its previously computed .graphml format:

Visualization

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

rr # l.mnet <- layout.kamada.kawai(mnet) # l.mnet2 <- layout.kamada.kawai(mnet2) l.mnet2 <- layout.kamada.kawai(mnet2)

rr # 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

rr 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] 15.37209

Now, ploting degree distribution of the simplified graph.

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

1 x value <= 0 omitted from logarithmic plot

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

rr cl.mnet <- closeness(mnet2) V(mnet2)$closeness <- cl.mnet summary(cl.mnet)

     Min.   1st Qu.    Median      Mean   3rd Qu.      Max. 
3.763e-06 3.117e-05 3.134e-05 2.821e-05 3.152e-05 3.193e-05 

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.mnet <- betweenness(mnet2) V(mnet2)$betweenness <- bw.mnet summary(bw.mnet)

    Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
    0.00     0.00    20.36   426.20   122.60 49280.00 

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.mnet <- evcent(mnet2)\(vector V(mnet2)\)eigenv <- ev.mnet summary(ev.mnet)

    Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
0.000000 0.003297 0.020150 0.045180 0.039090 1.000000 

Visualization highliting Hubs and Authorities

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

IGRAPH UNW- 516 3966 -- 
+ 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(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(‘./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 HIV/AIDS co-authorship network.

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

+ 10/3966 edges (vertex names):
 [1] ZANNOU DJIMON MARCEL--LEROY VALERIANE        ZANNOU DJIMON MARCEL--NDOUR MARGUERITE      
 [3] ALARY MICHEL        --AZONKOUANOU ANGELE     ZANNOU DJIMON MARCEL--NDOYE IBRA            
 [5] ANAGOUNOU SEVERIN   --ADE GABRIEL            ZANNOU DJIMON MARCEL--WACHINOU ABLO PRUDENCE
 [7] ZANNOU DJIMON MARCEL--DALMEIDA MARCELLINE    AZONDEKON ALAIN     --ADE GABRIEL           
 [9] AZONKOUANOU ANGELE  --AZONDEKON ALAIN        ZANNOU DJIMON MARCEL--COFFIE PATRICK 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 HIV/AIDS 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 HIV/AIDS co-authorship network, then compute the number and size of the maximal cliques.

rr clique.number(mnet2)

[1] 29

Our HIV/AIDS co-authorship network contains 29 maximal cliques.

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


 1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 21 29 
 1  4  3  3 14 23 24 25  7 24 19  9  8  3  3  1  2  1  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 HIV/AIDS co-authorship network.

rr graph.density(mnet2)

[1] 0.02984872

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(mnet2)

[1] 0.4821666

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 HIV/AIDS co-authorship network.

rr 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 HIV/AIDS 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(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:

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


  1   2   3   5   8  10  11 457 
  1   4   2   3   1   1   1   1 

From the output of the census of all connected components of the network above, we can see that there clearly is a giant component containing \(457/516\approx 88.6\%\) 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.

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

IGRAPH UNW- 457 3798 -- 
+ 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(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 HIV/AIDS co-authorship network, let’s compute the average path length

rr average.path.length(mnet2.gc)

[1] 2.753004

and the longest of paths

rr diameter(mnet2.gc)

[1] 7

Let’s assess the transitivity of our giant component:

rr transitivity(mnet2.gc)

[1] 0.4769843

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 47.7% 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(mnet2.gc)

[1] 1

rr edge.connectivity(mnet2.gc)

[1] 3

In the case of the giant component of our co-authorship network, the vertex connectivity is equal to 1 while edge connectivity is equal to 3, thus requires the removal of only a single well-chosen node (author) or 3 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 mnet2.cut.vertices <- articulation.points(mnet2.gc) mnet2.cut.vertices

+ 8/457 vertices, named:
[1] ATADOKPEDE FELIX     NDOUR MARGUERITE     DALMEIDA MARCELLINE  AZONDEKON ALAIN     
[5] GANDAHO PROSPER      AFFOLABI D           ADE GABRIEL          ZANNOU DJIMON MARCEL

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

rr length(mnet2.cut.vertices)

In our HIV/AIDS co-authorship network, less than 0.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 HIV/AIDS co-authorship network:

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

[1] 24

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

rr sizes(com.mnet2)

Community sizes
  1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18  19  20  21  22  23  24 
 71  55  12  47  39  10  11  10  76   8 108   5   5   5   5   5   3   3   2   2   2   2  29   1 

Large communities contain between 55 and 108 authors. Medium size communities contain between 10 and 47 authors.

Let’s now visualize the communities:

rr 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) )

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

[1] 12

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

rr sizes(gc.com)

Community sizes
  1   2   3   4   5   6   7   8   9  10  11  12 
 70  68  12  47  10  39  57   8 107   5   5  29 

The 7 communities contain between 5 and 107 authors.

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

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

rr 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 12 communities separately:

rr par(cex.main=3) par(mfrow=c(6,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:12){ 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) ) }

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

rr save(mnet, file = ‘./Rdata/HIVmnet.rda’) save(mnet2, file = ‘./Rdata/HIVmnet2.rda’) save(auth_data, file = ‘./Rdata/HIVauth_data.rda’) save(edges, file = ‘./Rdata/HIVedges.rda’) save(mnet2.gc, file = ‘./Rdata/HIVmnet2.gc.rda’)

rr # source(‘plotly_map.R’)

\(\\\)

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

NEXT TUTORIAL: Mathematical Modeling for Network Graphs

LS0tCnRpdGxlOiAiU29jaWFsIE5ldHdvcmsgQW5hbHlzaXMgb2YgdGhlIEhJVi9BSURTIENvLWF1dGhvcnNoaXAgbmV0d29yayBmcm9tIEJlbmluIgphdXRob3I6ICJSb3NlcmljIEF6b25kZWtvbiIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQKICB3b3JkX2RvY3VtZW50OiBkZWZhdWx0Ci0tLQokXFwkCiRcXCQKClRoaXMgcHJvamVjdCBpcyBwYXJ0IG9mIG15IFBoRCBkaXNzZXJ0YXRpb24gcHJvamVjdC4gVGhlIG5ldHdvcmsgaGFzIGFscmVhZHkgYmVlbiBhc3NlbWJsZWQuCgpMZXQncyBmaXJzdCBsb2FkIHRoZSByZXF1aXJlZCBwYWNrYWdlcyBhbmQgdGhlIG5ldHdvcmsgZmlsZToKJFxcJAoKTG9hZGluZyBsaWJyYXJpZXMuLi4uCmBgYHtyLHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoaWdyYXBoKQpgYGAKJFxcJAoKKipEZXNjcmlwdGl2ZSBOZXR3b3JrIEFuYWx5c2lzKioKYGBge3J9CiMgbW5ldCA8LSByZWFkX2dyYXBoKCd+L0RvY3VtZW50cy9zbmEvZ3JhcGhzX29sZC9DQW5ldC5ncmFwaG1sJywgZm9ybWF0ID0gJ2dyYXBobWwnKQptbmV0IDwtIHJlYWRfZ3JhcGgoJy4vZ3JhcGhzL0hJVm5ldC5ncmFwaG1sJywgZm9ybWF0ID0gJ2dyYXBobWwnKQpgYGAKJFxcJAoKR2V0IGEgc3VtbWFyeSBvZiB0aGUgbmV0d29yazoKYGBge3J9CnN1bW1hcnkobW5ldCkKYGBgCgoqSW50ZXJwcmV0YXRpb246KiBPdXIgY28tYXV0aG9yc2hpcCBuZXR3b3JrIGlzIGFuIHVuZGlyZWN0ZWQgbXVsdGlncmFwaCAocGFyYWxsZWwgZWRnZXMpIHdpdGggKio1MTYgYXV0aG9ycyoqIGFuZCAqKjUxMTQgc2NpZW50aWZpYyBjb2xsYWJvcmF0aW9ucyoqLiBFYWNoIG5vZGUgKGF1dGhvcikgaW4gdGhlIG5ldHdvcmsgaGFzIDIgYXR0cmlidXRlczogbmFtZSBhbmQgaWQuIEVhY2ggZWRnZSBoYXMgOCBhdHRyaWJ1dGVzOiBrZXksIHN1YmplY3QsIGFic3RyYWN0LCB5ZWFyLCB3b3NpZCAoV2ViIG9mIHNjaWVuY2UgSWRlbnRpZmljYXRpb24gbnVtYmVyKSwgam91cm5hbCwgdGl0bGUgYW5kIGRvaS4KCgoqQ29tcHV0aW5nIE5vZGUgQ2VudHJhbGl0eSBNZWFzdXJlczoqCldlIGNvbXB1dGUgY2VudHJhbGl0eSBtZWFzdXJlcyBzdWNoIGFzIGRlZ3JlZSAobnVtYmVyIG9mIHRpZXMgdG8gYSBnaXZlbiBhdXRob3IpLCBiZXR3ZWVubmVzcyAobnVtYmVyIG9mIHNob3J0ZXN0IHBhdGhzIGJldHdlZW4gYWx0ZXJzIHRoYXQgZ28gdGhyb3VnaCBhIHBhcnRpY3VsYXIgYXV0aG9yKSwgY2xvc2VuZXNzIChudW1iZXIgb2Ygc3RlcHMgcmVxdWlyZWQgZm9yIGEgcGFydGljdWxhciBhdXRob3IgdG8gYWNjZXNzIGV2ZXJ5IG90aGVyIGF1dGhvciBpbiB0aGUgbmV0d29yaykgYW5kIGVpZ2VudmVjdG9ycyAoZGVncmVlIHRvIHdoaWNoIGFuIGF1dGhvciBpcyBjb25uZWN0ZWQgdG8gb3RoZXIgd2VsbCBjb25uZWN0ZWQgYXV0aG9ycyBpbiB0aGUgbmV0d29yayksIGJyb2tlcmFnZSAoZGVncmVlIHRvIHdoaWNoIGFuIGFjdG9yIG9jY3VwaWVzIGEgYnJva2VyYWdlIHBvc2l0aW9uIGFjcm9zcyBhbGwgcGFpcnMgb2YgYWx0ZXJzKSAKCipEZWdyZWUgY2VudHJhbGl0eSoKTGV0J3MgZmlyc3QgY29tcHV0ZSB0aGUgZGVncmVlIGFuZCBzdHJlbmd0aCBvZiB0aGUgbm9kZXMgaW4gdGhlIG5ldHdvcms6CmBgYHtyfQpkLm1uZXQgPC0gZGVncmVlKG1uZXQpCnMubW5ldCA8LSBncmFwaC5zdHJlbmd0aChzaW1wbGlmeShtbmV0KSkgIyBmb3Igd2VpZ2h0ZWQgZ3JhcGgKYGBgCiAKTm93LCBsZXQncyBwbG90IGEgaGlzdG9ncmFtIG9mIHRoZSBkZWdyZWUgYW5kIHN0cmVuZ3RoIGRpc3RyaWJ1dGlvbnM6CmBgYHtyLGZpZy53aWR0aD04fQpwYXIobWZyb3c9YygxLDIpKQpoaXN0KGQubW5ldCwgY29sPSJsaWdodGJsdWUiLCB4bGFiPSJWZXJ0ZXggRGVncmVlIiwgeWxhYj0iRnJlcXVlbmN5IiwgbWFpbj0iRGVncmVlIGRpc3RyaWJ1dGlvbiIpCmhpc3Qocy5tbmV0LCBjb2w9InBpbmsiLCB4bGFiPSJWZXJ0ZXggU3RyZW5ndGgiLCB5bGFiPSJGcmVxdWVuY3kiLCBtYWluPSJTdHJlbmd0aCBkaXN0cmlidXRpb24iKQpgYGAKKkludGVycHJldGF0aW9uOiogV2hpbGUgdGhlcmUgaXMgYSBzdWJzdGFudGlhbCBudW1iZXIgb2Ygbm9kZXMgb2YgcXVpdGUgbG93IGRlZ3JlZSwgdGhlcmUgYXJlIGFsc28gYSBub24tdHJpdmlhbCBudW1iZXIgb2Ygbm9kZXMgd2l0aCBoaWdoZXIgb3JkZXIgb2YgZGVncmVlIG1hZ25pdHVkZXMuIEdpdmVuIHRoZSBuYXR1cmUgb2YgdGhpcyBkaXN0cmlidXRpb24sIGEgbG9nLWxvZyBzY2FsZSBpcyBtb3JlIGVmZmVjdGl2ZSBhdCBzdW1tYXJpemluZyB0aGUgZGVncmVlIGluZm9ybWF0aW9uLgoKRnJvbSBoZXJlLCB0aGUgcHJvY2Vzc2luZyByZXF1aXJlcyBvdXIgbmV0d29yayB0byBiZSBhIHNpbXBsZSBncmFwaC4gV2Ugd291bGQgdGhlcmVmb3JlIGNoYW5nZSBvdXIgbXVsdGlncmFwaCB0byBhIGdyYXBoIG9iamVjdC4KYGBge3J9CkUobW5ldCkkd2VpZ2h0IDwtIDEKbW5ldDIgPC0gc2ltcGxpZnkobW5ldCwgZWRnZS5hdHRyLmNvbWIgPSBsaXN0KHdlaWdodD0ic3VtIix0aW1lc0NpdGVkPSJzdW0iLG51bVB1Yj0ic3VtIixrZXk9ImNvbmNhdCIsc3ViamVjdD0iY29uY2F0Iix5ZWFyPSJjb25jYXQiLHdvc2lkPSJjb25jYXQiLGpvdXJuYWw9ImNvbmNhdCIsdGl0bGU9ImNvbmNhdCIsZG9pPSJjb25jYXQiKSkKc3VtbWFyeShtbmV0MikKYGBgCgogVGhlIG5ld2x5IGNvbXB1dGVkIHVuZGlyZWN0ZWQgZ3JhcGggY29udGFpbnMgKjUxNiBub2RlcyogYW5kICozOTY2IGVkZ2VzKi4gTm90ZSB0aGF0IHRoZSBzaW1wbGlmeSBmdW5jdGlvbiBpbiBSIGRvZXMgbm90IGNhcHR1cmUgdGhlIG51bWJlciBvZiBtdWx0aXBsZSBjb2xsYWJvcmF0aW9ucyBiZXR3ZWVuIGF1dGhvcnMuIEluIGEgcHJldmlvdXMgdHV0b3JpYWwsIHdlIGhhdmUgY29tcHV0ZWQgYSBzaW1wbGUgdmVyc2lvbiBvZiB0aGUgbW5ldCBuZXR3b3JrLiBMZXQncyBpbnN0ZWFkIG9mIHVzaW5nIHRoZSAqc2ltcGxpZnkoKSogZnVuY3Rpb24sIGxvYWQgbW5ldDIgZnJvbSBpdHMgcHJldmlvdXNseSBjb21wdXRlZCAuZ3JhcGhtbCBmb3JtYXQ6Cgo8IS0tIGBgYHtyfSAtLT4KPCEtLSAjIG1uZXQzIDwtIHJlYWRfZ3JhcGgoJy4vZ3JhcGhzX29sZC9DQW5ldF9ncmFwaC5ncmFwaG1sJywgZm9ybWF0ID0gJ2dyYXBobWwnKSAtLT4KPCEtLSBtbmV0MyA8LSByZWFkX2dyYXBoKCcuL2dyYXBocy9DQW5ldF93ZWlnaHQuZ3JhcGhtbCcsIGZvcm1hdCA9ICdncmFwaG1sJykgLS0+CjwhLS0gVihtbmV0MykkbmFtZSA8LSBWKG1uZXQyKSRuYW1lIC0tPgo8IS0tIHN1bW1hcnkobW5ldDMpIC0tPgo8IS0tIGBgYCAtLT4KPCEtLSAqSW50ZXJwcmV0YXRpb246KiBOb3RlIGhlcmUgdGhhdCBtbmV0MyBoYXMgdGhlIGV4YWN0IG51bWJlciBvZiBub2RlcyBhbmQgZWRnZXMgYXMgbW5ldDIuIFRoZSBvbmx5IGRpZmZlcmVuY2UgaXMgdGhlcmUgaXMgYSB3ZWlnaHQgYXR0cmlidXRlIG9uIGV2ZXJ5IGVkZ2UuIFdlIGhhdmUgcmVtb3ZlZCB0aGUgbmFtZSBhdHRyaWJ1dGUgb24gZWFjaCBub2RlIGZvciBjb252ZW5pZW5jZSBpc3N1ZS4gLS0+CgoqVmlzdWFsaXphdGlvbioKCk5vdywgbGV0J3Mgbm93IHZpc3VhbGl6ZSBvdXIgY28tYXV0aG9yc2hpcCBncmFwaCBuZXR3b3JrIG1uZXQyIHVzaW5nIHRoZSBLYW1hZGEgYW5kIEthd2FpIGxheW91dDoKYGBge3J9CiMgbC5tbmV0IDwtIGxheW91dC5rYW1hZGEua2F3YWkobW5ldCkKIyBsLm1uZXQyIDwtIGxheW91dC5rYW1hZGEua2F3YWkobW5ldDIpCmwubW5ldDIgPC0gbGF5b3V0LmthbWFkYS5rYXdhaShtbmV0MikKYGBgCgoKYGBge3IsZmlnLmhlaWdodD0xNSxmaWcud2lkdGg9MTV9CiMgcGxvdChtbmV0LCBsYXlvdXQ9bC5tbmV0LCB2ZXJ0ZXgubGFiZWw9TkEsIHZlcnRleC5zaXplPTIpCiMgcGxvdChtbmV0MiwgbGF5b3V0PWwubW5ldDIsIHZlcnRleC5sYWJlbD1OQSwgdmVydGV4LnNpemU9MiwgdmVydGV4LmNvbG9yPSdyZWQnKQpwbG90KG1uZXQyLCBsYXlvdXQ9bC5tbmV0MiwgdmVydGV4LmxhYmVsPU5BLCB2ZXJ0ZXguc2l6ZT0yLCB2ZXJ0ZXguY29sb3I9J2JsdWUnKSAjIFdlIHVzZSBsLm1uZXQyIGFzIGxheW91dApgYGAKCmBgYHtyfQpkLm1uZXQgPC0gZGVncmVlKG1uZXQyKSAjIERlZ3JlZSBvZiBzaW1wbGUgZ3JhcGggbW5ldDIKVihtbmV0MikkZGVncmVlIDwtIGQubW5ldApkZC5tbmV0IDwtIGRlZ3JlZS5kaXN0cmlidXRpb24obW5ldDIpICMgQ29tcHV0ZSBkZWdyZWUgZGlzdHJpYnV0aW9uCmQgPC0gMTptYXgoZC5tbmV0KS0xCmluZCA8LSAoZGQubW5ldCAhPSAwKQphLm5uLmRlZy5tbmV0IDwtIGdyYXBoLmtubihtbmV0MiwgVihtbmV0MikpJGtubgptZWFuKGQubW5ldCkKYGBgCgpOb3csIHBsb3RpbmcgZGVncmVlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgc2ltcGxpZmllZCBncmFwaC4KYGBge3IsZmlnLndpZHRoPTh9CnBhcihtZnJvdz1jKDEsMikpCmhpc3QoZC5tbmV0LCBjb2w9ImxpZ2h0Ymx1ZSIsIHhsYWI9IlZlcnRleCBEZWdyZWUiLCB5bGFiPSJGcmVxdWVuY3kiLCBtYWluPSJEZWdyZWUgZGlzdHJpYnV0aW9uIikKcGxvdChkW2luZF0sIGRkLm1uZXRbaW5kXSwgbG9nPSJ4eSIsIGNvbD0iYmx1ZSIsIHR5cGU9J3AnLCB4bGFiPWMoIkxvZy1EZWdyZWUiKSwgeWxhYj1jKCJMb2ctSW50ZW5zaXR5IiksbWFpbj0iTG9nLUxvZyBEZWdyZWUgRGlzdHJpYnV0aW9uIikKYGBgCipJbnRlcnByZXRhdGlvbjoqIEZyb20gdGhlIHBsb3Qgb24gdGhlIHJpZ2h0LCB0aGUgZGVncmVlcyBvZiB0aGUgbm9kZSBzZWVtIHRvIGZvbGxvdyBhIGhlYXZ5IHJpZ2h0LXRhaWwgZGlzdHJpYnV0aW9uLCBjaGFyYWN0ZXJpc3RpYyBvZiBhIHBvd2VyIGxhdyBkaXN0cmlidXRpb24gd2l0aCBhbiBhdmVyYWdlIGRpc3RyaWJ1dGlvbiBvZiAkMTUuMzckLiBMZXQncyBpbnZlc3RpZ2F0ZSB0aGUgbWFubmVyIGluIHdoaWNoIG5vZGVzIG9mIGRpZmZlcmVudCBkZWdyZWVzIGFyZSBsaW5rZWQgd2l0aCBlYWNoIG90aGVyIGluIHRoZSBjb2F1dGhvcnNoaXAgbmV0d29yay4gRm9yIHRoaXMgcHVycG9zZSwgd2UgYnJpbmcgaW4gdGhlIG5vdGlvbiBvZiB0aGUgYXZlcmFnZSBkZWdyZWUgb2YgdGhlIG5laWdoYm9ycyBvZiBhIGdpdmVuIG5vZGUuIFdlIHRoZW4gcGxvdCB0aGUgYXZlcmFnZSBuZWlnaGJvciBkZWdyZWUgYWdhaW5zdCBub2RlIGRlZ3JlZS4KCmBgYHtyLGZpZy5oZWlnaHQ9N30KcGxvdChkLm1uZXQsIGEubm4uZGVnLm1uZXQsIGxvZz0ieHkiLCBjb2w9ImdvbGRlbnJvZCIsIHR5cGU9J3AnLCB4bGFiPWMoIkxvZyBWZXJ0ZXggRGVncmVlIiksIHlsYWI9YygiTG9nIEF2ZXJhZ2UgTmVpZ2hib3IgRGVncmVlIikpCmBgYAoqSW50ZXJwcmV0YXRpb246KiBUaGUgcGxvdCBhYm92ZSBzdWdnZXN0cyB0aGF0IHdoaWxlIHRoZXJlIGlzIGEgdGVuZGVuY3kgb2Ygbm9kZXMgb2YgaGlnaGVyIGRlZ3JlZXMgdG8gbGluayB3aXRoIHNpbWlsYXIgbm9kZXMsIG5vZGVzIG9mIGxvd2VyIGRlZ3JlZSB0ZW5kIHRvIGxpbmsgd2l0aCBub2RlcyBvZiBib3RoIGxvd2VyIGFuZCBoaWdoZXIgZGVncmVlcy4gSW4gb3RoZXIgd29yZHMsIHdoaWxlIHByb21pbmVudCBhdXRob3JzIHdpdGggaW1wb3J0YW50IGNvbGxhYm9yYXRpb25zIHRlbmQgdG8gY29sbGFib3JhdGUgd2l0aCBzaW1pbGFyIGF1dGhvcnMsIHlvdW5nIG9yIGxlc3MgcHJvbGlmaWMgYXV0aG9ycyB0ZW5kIHRvIGNvbGxhYm9yYXRlIHdpdGggYm90aCBwcm9saWZpYyBhbmQgYXV0aG9ycyB3aXRoIHZlcnkgZmV3IGNvbGxhYm9yYXRpb25zLgoKCkxldCdzIGNvbXB1dGUgdGhlIG90aGVyIDMgbm9kZSBjZW50cmFsaXR5IG1lYXN1cmVzOgpgYGB7cn0KQSA8LSBnZXQuYWRqYWNlbmN5KG1uZXQyLCBzcGFyc2UgPSBGQUxTRSkKZyA8LSBuZXR3b3JrOjphcy5uZXR3b3JrLm1hdHJpeChBKQpgYGAKCgoqQ2xvc2VuZXNzIGNlbnRyYWxpdHk6KiBjYXB0dXJlcyB0aGUgbm90aW9uIHRoYXQgYSBub2RlIGlzIGNlbnRyYWwgaWYgaXQgY2xvc2UgdG8gbWFueSBvdGhlciBub2RlcwpDb25zaWRlcmluZyBhIG5ldHdvcmsgJEc9KFYsRSkkIHdoZXJlICRWJCBpcyB0aGUgc2V0IG9mIG5vZGVzIGFuZCAkRSQsIHRoZSBzZXQgb2YgZWRnZXMsIHRoZSBjbG9zZW5lc3MgY2VudHJhbGl0eSAkY197Q2x9KHYpJCBvZiBhIG5vZGUgJHYkIGlzIGRlZmluZWQgYXM6IAokJGNfe0NsfSh2KT1cZnJhY3sxfXtcc3VtX3t1XGluIFZ9ZGlzdCh2LHUpfSQkCiRkaXN0KHYsdSkkIGlzIGRlZmluZWQgYXMgdGhlIGdlb2Rlc2ljIGRpc3RhbmNlIGJldHdlZW4gdGhlIG5vZGVzICR1LHYgXGluIFYkLgpgYGB7cn0KY2wubW5ldCA8LSBjbG9zZW5lc3MobW5ldDIpClYobW5ldDIpJGNsb3NlbmVzcyA8LSBjbC5tbmV0CnN1bW1hcnkoY2wubW5ldCkKYGBgCmBgYHtyLGZpZy5oZWlnaHQ9MTUsZmlnLndpZHRoPTE1fQpwbG90KG1uZXQyLCBsYXlvdXQ9bC5tbmV0MiwgbWFpbj0iQ2xvc2VuZXNzIGNlbnRyYWxpdHkiLCB2ZXJ0ZXgubGFiZWw9IiIsIHZlcnRleC5zaXplPTAuNzUgKiBzcXJ0KGNsLm1uZXQqMUU2KSwgdmVydGV4LmNvbG9yPSdsaWdodGJsdWUnKQpgYGAKCgoqQmV0d2VlbmVzcyBjZW50cmFsaXR5Oiogc3VtbWFyaXplcyB0aGUgZXh0ZW50IHRvIHdoaWNoIGEgbm9kZSBpcyBsb2NhdGVkIGJldHdlZW4gb3RoZXIgcGFpcnMgb2Ygbm9kZXMuIEl0IHJlbGF0ZXMgdG8gdGhlIHRvIHRoZSBwZXJzcGVjdGl2ZSB0aGF0IGltcG9ydGFuY2UgcmVsYXRlcyB0byB3aGVyZSBhIG5vZGUgaXMgbG9jYXRlZCB3aXRoIHJlc3BlY3QgdG8gdGhlIHBhdGhzIGluIHRoZSBuZXR3b3JrIGdyYXBoLiBBY2NvcmRpbmcgdG8gRnJlZW1hbiwgaXQgaXMgZGVmaW5lZCBhczoKJCRjX3tCfSh2KT1cZnJhY3tcc2lnbWEgKHMsdHx2KX17XHN1bV97cyBcbmVxIHQgXG5lcSB2IFxpbiBWfVxzaWdtYSAocyx0KX0kJAp3aGVyZSAkXHNpZ21hKHMsdHx2KSQgaXMgdGhlIHRvdGFsIG51bWJlciBvZiBzaG9ydGVzdCBwYXRocyBiZXR3ZWVuICRzJCBhbmQgJHQkIHRoYXQgcGFzcyB0aHJvdWdoICR2JCwgYW5kICRcc2lnbWEgKHMsdCkkIGlzIHRoZSB0b3RhbCBudW1iZXIgb2Ygc2hvcnRlc3QgcGF0aHMgYmV0d2VlbiAkcyQgYW5kICR0JCByZWdhcmRsZXNzIG9mIHdoZXRoZXIgb3Igbm90IHRoZXkgcGFzcyB0aHJvdWdoICR2JC4KYGBge3J9CmJ3Lm1uZXQgPC0gYmV0d2Vlbm5lc3MobW5ldDIpClYobW5ldDIpJGJldHdlZW5uZXNzIDwtIGJ3Lm1uZXQKc3VtbWFyeShidy5tbmV0KQpgYGAKYGBge3IsZmlnLmhlaWdodD0xNSxmaWcud2lkdGg9MTV9CnBsb3QobW5ldDIsIGxheW91dD1sLm1uZXQyLCBtYWluPSJCZXR3ZWVubmVzcyBjZW50cmFsaXR5IiwgdmVydGV4LmxhYmVsPSIiLCB2ZXJ0ZXguc2l6ZT0xICogc3FydChidy5tbmV0LzEwMDApLCB2ZXJ0ZXguY29sb3I9J2xpZ2h0Z3JlZW4nKQpgYGAKCgoKKkVpZ2VudmVjdG9yIGNlbnRyYWxpdHk6KiBzZWVrcyB0byBjYXB0dXJlIHRoZSBpZGVhIHRoYXQgdGhlIG1vcmUgY2VudHJhbCB0aGUgbmVpZ2hib3JzIG9mIGEgbm9kZSBhcmUsIHRoZSBtb3JlIGNlbnRyYWwgdGhhdCBub2RlIGl0c2VsIGlzLiBBY2NvcmRpbmcgdG8gQm9uYWNpY2ggYW5kIEthdHogW1BhZ2UgNDgsIGJvb2sgS29sYWN6eWsgYW5kIENzYXJkaSwgMm5kIGVkaXRpb24sIDIwMDldLCB0aGUgRWlnZW52ZWN0b3IgY2VudHJhbGl0eSBtZWFzdXJlIGlzIGRlZmluZWQgYXM6CiQkY197RV9pfSh2KT1cYWxwaGEgXHN1bV97XHt1LHZcfVxpbiBFfWNfe0VfaX0odSkkJAoKV2hlcmUgdGhlIHZlY3RvciAkXG1hdGhiZntjfV97RV9pfT0oY197RV9pfSgxKSxcZG90cyAsY197RV9pfShOX3YpKV5UJCBpcyB0aGUgc29sdXRpb24gdG8gdGhlIGVpZ2VudmFsdWUgcHJvYmxlbSAkXG1hdGhiZntBY31fe0VfaX09XGFscGhhXnstMX1cbWF0aGJme2N9X3tFX2l9JCwgd2hlcmUgJFxtYXRoYmZ7QX0kIGlzIHRoZSBhZGphY2VuY3kgbWF0cml4IGZvciB0aGUgbmV0d29yayAkRyQuIEFjY29yZGluZyB0byBCb25hY2ljaCwgYW4gb3B0aW1hbCBjaG9pY2Ugb2YgJFxhbHBoYV57LTF9JCBpcyB0aGUgbGFyZ2VzdCBlaWdlbnZhbHVlIG9mICRcbWF0aGJme0F9JC4KYGBge3J9CmV2Lm1uZXQgPC0gZXZjZW50KG1uZXQyKSR2ZWN0b3IKVihtbmV0MikkZWlnZW52IDwtIGV2Lm1uZXQKc3VtbWFyeShldi5tbmV0KQpgYGAKYGBge3IsZmlnLmhlaWdodD0xNSxmaWcud2lkdGg9MTV9CnBsb3QobW5ldDIsIGxheW91dD1sLm1uZXQyLCBtYWluPSJFaWdlbnZlY3RvcnMgY2VudHJhbGl0eSIsIHZlcnRleC5sYWJlbD0iIiwgdmVydGV4LnNpemU9MiAqIHNxcnQoZXYubW5ldCoxMCksIHZlcnRleC5jb2xvcj0ncGluaycpCmBgYAoKCgoqVmlzdWFsaXphdGlvbiBoaWdobGl0aW5nIEh1YnMgYW5kIEF1dGhvcml0aWVzKgpgYGB7cixmaWcuaGVpZ2h0PTE1LGZpZy53aWR0aD0xNX0KIyBwYXIobWZyb3c9YygxLDIpKQojIHBsb3QobW5ldDIsIGxheW91dD1sLm1uZXQyLCBtYWluPSJIdWJzIiwgdmVydGV4LmxhYmVsPSIiLCB2ZXJ0ZXguc2l6ZT0xMCAqIHNxcnQoaHViLnNjb3JlKG1uZXQyKSR2ZWN0b3IpLCB2ZXJ0ZXguY29sb3I9J2dyZWVuJykKIyBwbG90KG1uZXQyLCBsYXlvdXQ9bC5tbmV0MiwgbWFpbj0iQXV0aG9yaXRpZXMiLCB2ZXJ0ZXgubGFiZWw9IiIsIHZlcnRleC5zaXplPTEwICoKIyBzcXJ0KGF1dGhvcml0eS5zY29yZShtbmV0MikkdmVjdG9yKSwgdmVydGV4LmNvbG9yPSdyZWQnKQpgYGAKCmBgYHtyfQpWKG1uZXQyKSRodWJTY29yZSA8LSBodWIuc2NvcmUobW5ldDIpJHZlY3RvcgpWKG1uZXQyKSRhdXRoU2NvcmUgPC0gYXV0aG9yaXR5LnNjb3JlKG1uZXQyKSR2ZWN0b3IKc3VtbWFyeShtbmV0MikKYGBgCk5vdGUgdGhhdCB0aGUgSHViIHNjb3JlcyBhbmQgQXV0aG9yaXR5IHNjb3JlcyBhcmUgZXhhY3RseSBlcXVhbCB0byB0aGUgZWlnZW52ZWN0b3JzIG9mIHRoZSBub2RlcyBpbiB0aGUgbmV0d29yay4gQW4gZXhwbG9yYXRpb24gb2YgdGhlIGF1dGhfZGF0YSBmcmFtZSBiZWxvdyBoZWxwcyB0aGUgcmVhZGVyIG5vdGljZSB0aGUgcmVtYXJrLgoKYGBge3J9CmF1dGhfZGF0YSA8LSBkYXRhLmZyYW1lKGlkPWFzLm51bWVyaWMoVihtbmV0MikkaWQpLG5hbWU9VihtbmV0MikkbmFtZSxudW1QdWI9YXMubnVtZXJpYyhWKG1uZXQyKSRudW1QdWIpLHRpbWVzQ2l0ZWQ9YXMubnVtZXJpYyhWKG1uZXQyKSR0aW1lc0NpdGVkKSxkZWdyZWU9VihtbmV0MikkZGVncmVlLGNsb3NlbmVzcz1WKG1uZXQyKSRjbG9zZW5lc3MsYmV0d2Vlbm5lc3M9VihtbmV0MikkYmV0d2Vlbm5lc3MsZWlnZW5WPVYobW5ldDIpJGVpZ2VudixodWJTY29yZT1WKG1uZXQyKSRodWJTY29yZSxhdXRoU2NvcmU9VihtbmV0MikkYXV0aFNjb3JlKQphdXRoX2RhdGEkYWZmaWxpYXRpb248LVYobW5ldDIpJHBsYWNlCmF1dGhfZGF0YSRjaXR5PC1WKG1uZXQyKSRhZmZpbAphdXRoX2RhdGEkY291bnRyeTwtVihtbmV0MikkY291bnRyeQphdXRoX2RhdGEkY2l0eVt3aGljaChhdXRoX2RhdGEkY2l0eT09J0xPTkRPTiBXQzFFIDdIVCcpXTwtJ0xPTkRPTicKYXV0aF9kYXRhJGNpdHlbd2hpY2goYXV0aF9kYXRhJGNpdHk9PSdMT05ET04gV0MxJyldPC0nTE9ORE9OJwphdXRoX2RhdGEkY2l0eVt3aGljaChhdXRoX2RhdGEkY2l0eT09J0xPTkRPTiBOVzMgMlFHJyldPC0nTE9ORE9OJwphdXRoX2RhdGEkY291bnRyeVt3aGljaChhdXRoX2RhdGEkY2l0eT09J0NPVE9OT1UnICYgYXV0aF9kYXRhJGNvdW50cnk9PSdGUkFOQ0UnKV08LSdCRU5JTicKYXV0aF9kYXRhJGNvdW50cnlbd2hpY2goVihtbmV0MikkY291bnRyeT09J01BIFVTQScpXTwtJ1VTQScKYXV0aF9kYXRhJGNpdHlbd2hpY2goYXV0aF9kYXRhJGNpdHk9PSdCT0JPIERJT1VMQVNTTyAwMScpXTwtJ0JPQk8gRElPVUxBU1NPJwoKIyBPYnRhaW5pbmcgY29vcmRpbmF0ZXMKIyBsaWJyYXJ5KGdnbWFwKQojIGNvcmQ8LXVubGlzdChnZW9jb2RlKHBhc3RlKGF1dGhfZGF0YSRjaXR5WzFdLGF1dGhfZGF0YSRjb3VudHJ5WzFdLHNlcD0nLCAnKSkpCiMgbG9uZ2xhdDwtZGF0YS5mcmFtZShpZD1hdXRoX2RhdGEkaWQsIG5hbWU9YXV0aF9kYXRhJG5hbWUsIGxvbmc9Y29vcmRbMToxNzkyXSxsYXQ9Y29vcmRbMTc5MzozNTg0XSkKIyB3cml0ZS5jc3YobG9uZ2xhdCxmaWxlID0gJ34vRG9jdW1lbnRzL3NuYS9ncmFwaHMvYXV0aF9jb29yZGluYXRlcy5jc3YnKQoKIyBHZXR0aW5nIG5vZGUgaW5mb3JtYXRpb24KIyBsb25nbGF0PC1yZWFkLmNzdignLi9ncmFwaHMvYXV0aF9jb29yZGluYXRlcy5jc3YnLGhlYWRlciA9IFQpCiMgbG9uZ2xhdDwtc3Vic2V0KGxvbmdsYXQsIHNlbGVjdCA9IC1jKFgpKQojIGF1dGhfZGF0YSRsb25nPC1sb25nbGF0JGxvbmcKIyBhdXRoX2RhdGEkbGF0PC1sb25nbGF0JGxhdAojIAojICMgR2V0dGluZyBlZGdlIGxpc3QgYW5kIHJlbGF0ZWQgaW5mb3JtYXRpb24KIyBlTGlzdDwtZ2V0LmVkZ2VsaXN0KG1uZXQyLG5hbWVzID0gVCkKIyBjb2xuYW1lcyhlTGlzdCk8LWMoJ3NvdXJjZScsJ3RhcmdldCcpCiMgZUxpc3Q8LWFzLmRhdGEuZnJhbWUoZUxpc3QpCiMgbmV3MTwtZUxpc3QKIyBuZXcyPC1lTGlzdAojIG5ldzFbXTwtYXV0aF9kYXRhJGxvbmdbbWF0Y2godW5saXN0KGVMaXN0KSxhdXRoX2RhdGEkbmFtZSldCiMgbmV3MltdPC1hdXRoX2RhdGEkbGF0W21hdGNoKHVubGlzdChlTGlzdCksYXV0aF9kYXRhJG5hbWUpXQojIGVkZ2VzPC1kYXRhLmZyYW1lKGlkPTE6bGVuZ3RoKEUobW5ldDIpKSxzb3VyY2U9ZUxpc3Qkc291cmNlLHRhcmdldD1lTGlzdCR0YXJnZXQsd2VpZ2h0PUUobW5ldDIpJHdlaWdodCx0aW1lc0NpdGVkPUUobW5ldDIpJHRpbWVzQ2l0ZWQsbG9uZ19zb3VyY2U9bmV3MSRzb3VyY2UsbGF0X3NvdXJjZT1uZXcyJHNvdXJjZSxsb25nX3RhcmdldD1uZXcxJHRhcmdldCxsYXRfdGFyZ2V0PW5ldzIkdGFyZ2V0KQpgYGAKCgoqQ2hhcmFjdGVyaXppbmcgRWRnZXM6KgpFZGdlIGJldHdlZW5uZXNzIGNlbnRyYWxpdHkgZXh0ZW5kcyBmcm9tIHRoZSBub3Rpb24gb2YgdmVydGV4IGNlbnRyYWxpdHkgYnkgYXNzaWduaW5nIHRvIGVhY2ggZWRnZSBhIHZhbHVlIHJlZmxlY3RpbmcgdGhlIG51bWJlciBvZiBzaG9ydGVzdCBwYXRocyB0cmF2ZXJzaW5nIHRoYXQgZWRnZS4gV2UgY29tcHV0ZSBlZGdlIGJldHdlZW5uZXNzIHRvIGFzc2VzcyB3aGljaCBjby1hdXRob3JzaGlwIGNvbGxhYm9yYXRpb25zIGFyZSBpbXBvcnRhbnQgZm9yIHRoZSBmbG93IG9mIGluZm9ybWF0aW9uLiBXZSB0aGVuIHByZXNlbnQgdGhlIDEwIG1vc3QgaW1wb3J0YW50IGNvbGxhYm9yYXRpb25zIGluIG91ciBISVYvQUlEUyBjby1hdXRob3JzaGlwIG5ldHdvcmsuCmBgYHtyfQplYiA8LSBlZGdlLmJldHdlZW5uZXNzKG1uZXQyKQpFKHNpbXBsaWZ5KG1uZXQpKVtvcmRlcihlYiwgZGVjcmVhc2luZz1UKVsxOjEwXV0KYGBgCgoKKkNoYXJhY3Rlcml6aW5nIE5ldHdvcmsgY29oZXNpb246KgoKSW4gdGhpcyBzZWN0aW9uLCB3ZSBhcmUgZ29pbmcgdG8gYXNzZXNzIHRoZSBleHRlbnQgdG8gd2hpY2ggc3Vic2V0cyBvZiBhdXRob3JzIGFyZSBjb2hlc2l2ZSB3aXRoIHRoZSByZXNwZWN0IHRvIHRoZWlyIHJlbGF0aW9uIGluIHRoZSBjby1hdXRob3JzaGlwIG5ldHdvcmsuIFNwZWNpZmljYWxseSwgKip3ZSBhaW0gYXQgZGV0ZXJtaW5pbmcgaWYgY29sbGFib3JhdG9ycyAoY28tYXV0aG9ycykgb2YgYSBnaXZlbiBhdXRob3IgdGVuZCB0byBjb2xsYWJvcmF0ZSBhcyB3ZWxsKiouICoqV2hhdCBzdWJzZXQgb2YgY29sbGFib3JhdGluZyBhdXRob3JzIHRlbmQgdG8gYmUgbW9yZSBwcm9kdWN0aXZlIGluIG91ciBuZXR3b3JrPyoqCldoaWxlIHRoZXJlIGFyZSBtYW55IHRlY2huaXF1ZXMgdG8gZGV0ZXJtaW5lIG5ldHdvcmsgY29oZXNpb24sIHdlIGNob29zZSB0byBpbnZlc3RpZ2F0ZSBsb2NhbCB0cmlhZHMgYW5kIGdsb2JhbCBnaWFudCBjb21wb25lbnRzLCBjbGlxdWVzIGRldGVjdGlvbiBhcyB3ZWxsIGFzIGNsdXN0ZXJpbmcgb3IgY29tbXVuaXRpZXMgZGV0ZWN0aW9uIGluIG91ciBISVYvQUlEUyBjby1hdXRob3JzaGlwIG5ldHdvcmsuCgoqQ2xpcXVlczoqIEFjY29yZGluZyB0byBLb2xhY3p5ayBhbmQgQ3NhcmRpICgyMDA5KSwgY2xpcXVlcyBhcmUgZGVmaW5lZCBhcyBjb21wbGV0ZSBzdWJncmFwaHMgc3VjaCB0aGF0IGFsbCBub2RlcyB3aXRoaW4gdGhlIHN1YnNldCBhcmUgY29ubmVjdGVkIGJ5IGVkZ2VzLiBXZSBjb21wdXRlIHRoZSBudW1iZXIgb2YgY2xpcXVlcyBpbiBvdXIgSElWL0FJRFMgY28tYXV0aG9yc2hpcCBuZXR3b3JrLCB0aGVuIGNvbXB1dGUgdGhlIG51bWJlciBhbmQgc2l6ZSBvZiB0aGUgbWF4aW1hbCBjbGlxdWVzLgoKYGBge3J9CmNsaXF1ZS5udW1iZXIobW5ldDIpCmBgYApPdXIgSElWL0FJRFMgY28tYXV0aG9yc2hpcCBuZXR3b3JrIGNvbnRhaW5zIDI5IG1heGltYWwgY2xpcXVlcy4KCmBgYHtyfQp0YWJsZShzYXBwbHkobWF4aW1hbC5jbGlxdWVzKG1uZXQyKSwgbGVuZ3RoKSkKYGBgCjwhLS0gVGhlIHRhYmxlIGFib3ZlIGRpc3BsYXlzIHRoZSBzaXplIGFuZCBudW1iZXIgb2YgbWF4aW1hbCBjbGlxdWVzLiBGcm9tIHRoZSB0YWJsZSwgd2UgY2FuIHNlZSB0aGF0IG91ciBuZXR3b3JrIGNvbnRhaW5zIDkgY2xpcXVlcyBvZiBzaXplIDIgYW5kIDE0IGNsaXF1ZXMgb2Ygc2l6ZSAzLiBJdCBhbHNvIGNvbnRhaW5zIDE1NSBjbGlxdWVzIG9mIHNpemUgOCBhbmQgMTQyIGNsaXF1ZXMgb2Ygc2l6ZSA3LiBMYXJnZXIgY2xpcXVlcyBzaXplcyByYW5nZSBmcm9tICAxMDIgYXV0aG9ycyB0byAzNjUgYXV0aG9ycyBhbmQgYXJlIGFsbCBmb3VuZCBvbmNlIGFjcm9zcyB0aGUgbmV0d29yay4gLS0+CgoqRGVuc2l0eSBhbmQgcmVsYXRlZCBub3Rpb25zIG9mIHJlbGF0aXZlIGZyZXF1ZW5jeToqCkRlZmluZWQgYXMgdGhlIGZyZXF1ZW5jeSBvZiByZWFsaXplZCBlZGdlcyByZWxhdGl2ZSB0byBwb3RlbnRpYWwgZWRnZXMsIHRoZSBkZW5zaXR5IG9mIGEgc3ViZ3JhcGggJEgkIGluICRHJCBwcm92aWRlcyBhIG1lYXN1cmUgb2YgaG93IGNsb3NlICRIJCBpcyB0byBiZSBhIGNsaXF1ZSBpbiAkRyQuIERlbnNpdHkgdmFsdWVzIHZhcmllIGJldHdlZW4gMCBhbmQgMToKJCRkZW4oSCk9XGZyYWN7fEVfSHx9e3xWX0h8KFZfSC0xKS8yfSQkCkhlcmUgd2UgY29tcHV0ZSB0aGUgZ2VuZXJhbCBkZW5zaXR5IG9mIG91ciBISVYvQUlEUyBjby1hdXRob3JzaGlwIG5ldHdvcmsuCgpgYGB7cn0KZ3JhcGguZGVuc2l0eShtbmV0MikKYGBgCgpXZSBhc3Nlc3MgdGhlIHJlbGF0aXZlIGZyZXF1ZW5jeSBvZiAkRyQgYnkgY29tcHV0aW5nIGl0cyB0cmFuc2l0aXZpdHkgZGVmaW5lZCBhczoKJCRjbF9UID0gXGZyYWN7M1x0YXVfXERlbHRhIChHKX17XHRhdV8zIChHKX0kJAp3aGVyZSAkXHRhdV9cRGVsdGEgKEcpJCBpcyB0aGUgbnVtYmVyIG9mIHRyaWFuZ2xlcyBpbiAkRyQsIGFuZCAkXHRhdV8zIChHKSQgaXMgdGhlIG51bWJlciBvZiBjb25uZWN0ZWQgdHJpcGxlcyAoc29tZXRpbWVzIHJlZmVycmVkIHRvIGFzIDItc3RhcikuIFRoaXMgbWVhc3VyZSBpcyBhbHNvIHJlZmVycmVkIHRvIGFzIHRoZSBmcmFjdGlvbiBvZiB0cmFuc2l0aXZlIHRyaXBsZXMuIEl0IHJlcHJlc2VudHMgYSBtZWFzdXJlIG9mIGdsb2JhbCBjbHVzdGVyaW5nIG9mICRHJCBzdW1tYXJpemluZyB0aGUgcmVsYXRpdmUgZnJlcXVlbmN5IHdpdGggd2hpY2ggY29ubmVjdGVkIHRyaXBsZXMgY2xvc2UgdG8gZm9ybSB0cmlhbmdsZXMuCmBgYHtyfQp0cmFuc2l0aXZpdHkobW5ldDIpCmBgYAogQW5vdGhlciBhbmFsb2d1ZSBvZiB0aGlzIG1lYXN1cmUgaXMgdGhlIGxvY2FsIHRyYW5zaXRpdml0eSBkZWZpbmVkIGFzOgogJCRjbCh2KT1cdGF1X1xEZWx0YSAodikvXHRhdV8zICh2KSQkCiB3aGVyZSAkXHRhdV9cRGVsdGEgKHYpJCBkZW5vdGVzIHRoZSBudW1iZXIgb2YgdHJpYW5nbGVzIGluICRHJCBpbnRvIHdoaWNoICR2IFxpbiBWJCBmYWxscyBhbmQgJFx0YXVfMyAodikkIGlzIHRoZSBudW1iZXIgb2YgY29ubmVjdGVkIHRyaXBsZXMgaW4gJEckIGZvciB3aGljaCB0aGUgdHdvIGVkZ2VzIGFyZSBib3RoIGluY2lkZW50IHRvICR2JC4gSGVyZSB3ZSBjb21wdXRlIHRoZSBsb2NhbCB0cmFuc2l0aXZpdHkgZm9yIGFsbCB0aGUgbm9kZXMgaW4gb3VyIEhJVi9BSURTIGNvLWF1dGhvcnNoaXAgbmV0d29yay4KIApgYGB7cn0KdHI8LXRyYW5zaXRpdml0eShtbmV0MiwnbG9jYWwnLHZpZHMgPSAxOmxlbmd0aChWKG1uZXQyKSkpClYobW5ldDIpJHRyYW5zaXRpdml0eTwtdHIKYXV0aF9kYXRhJHRyYW5zaXRpdml0eTwtdHIKYGBgCgoKKkNvbm5lY3Rpdml0eSwgQ3V0cywgYW5kIEZsb3dzOioKSW4gdGhpcyBzZWN0aW9uLCB3ZSBtZWFzdXJlIGhvdyBjbG9zZSBvdXIgSElWL0FJRFMgY28tYXV0aG9yc2hpcCBpcyBjbG9zZSB0byBzZXBhcmF0ZSBpbnRvIGRpc3RpbmN0cyBzdWJncmFwaHMuIFdlIGFyZSBhbHNvIGludGVyZXN0ZWQgaW4gYXNzZXNzaW5nIGhvdyB3ZWxsIGluZm9ybWF0aW9uIGZsb3dzIGluIHRoZSBuZXR3b3JrLgpXZSBmaXJzdCBzdGFydCB3aXRoIHRoZSBjb25jZXB0IG9mIGNvbm5lY3RlZG5lc3MuIFNpbmNlIG91ciBuZXR3b3JrIGlzIGFuIHVuZGlyZWN0ZWQgZ3JhcGgsIHdlIGRvIG5vdCBjb25zaWRlciB0aGUgaWRlYSBvZiB3ZWFrIGFuZCBzdHJvbmcgY29ubmVjdGl2aXR5LiBBIGdyYXBoICRHJCBpcyBzYWlkIHRvIGJlIGNvbm5lY3RlZCBpZiBldmVyeSBub2RlIGluICRHJCBpcyByZWFjaGFibGUgZnJvbSBldmVyeSBvdGhlciBub2RlLgpgYGB7cn0KaXMuY29ubmVjdGVkKG1uZXQyKQpgYGAKRnJvbSB0aGUgb3V0cHV0IGFib3ZlLCB3ZSBjbGVhcmx5IGNvbmNsdWRlIHRoYXQgb3VyIGNvLWF1dGhvcnNoaXAgbmV0d29yayBpcyBub3QgY29ubmVjdGVkLgoKT2Z0ZW4gdGltZSwgb25lIG9mIHRoZSBjb25uZWN0ZWQgY29tcG9uZW50cyBjYW4gZG9taW5hdGUgdGhlIG90aGVycywgaGVuY2UgdGhlIGlkZWEgb2YgKmdpYW50IGNvbXBvbmVudCouIExldCdzIHRoZW4gY2Vuc3VzIG91ciBjby1hdXRob3JzaGlwOgpgYGB7cn0KY29tcHM8LWRlY29tcG9zZS5ncmFwaChtbmV0MikKdGFibGUoc2FwcGx5KGNvbXBzLHZjb3VudCkpCmBgYAoKRnJvbSB0aGUgb3V0cHV0IG9mIHRoZSBjZW5zdXMgb2YgYWxsIGNvbm5lY3RlZCBjb21wb25lbnRzIG9mIHRoZSBuZXR3b3JrIGFib3ZlLCB3ZSBjYW4gc2VlIHRoYXQgdGhlcmUgY2xlYXJseSBpcyBhIGdpYW50IGNvbXBvbmVudCBjb250YWluaW5nICQ0NTcvNTE2XGFwcHJveCA4OC42XCUkIG9mIGFsbCB0aGUgdmVydGljZXMgaW4gdGhlIG5ldHdvcmsgd2l0aCBub25lIG9mIHRoZSBvdGhlciBjb21wb25lbnRzIGFsb25lIGNhcnJ5aW5nIGV2ZW4gJDFcJSQgb2YgdGhlIHZlcnRpY2VzLgoKV2UgZnVydGhlciBkZXZvdGUgY2xvc2VyIGF0dGVudGlvbiB0byB0aGlzIGdpYW50IGNvbXBvbmVudC4KYGBge3J9Cm1uZXQyLmdjIDwtIGRlY29tcG9zZS5ncmFwaChtbmV0MilbWzFdXQpzdW1tYXJ5KG1uZXQyLmdjKQpgYGAKIAogTGV0J3MgcGxvdCB0aGlzIGdpYW50IGNvbXBvbmVudDoKIApgYGB7cixmaWcuaGVpZ2h0PTE1LGZpZy53aWR0aD0xNX0KcGxvdChtbmV0Mi5nYywgbGF5b3V0PWxheW91dC5rYW1hZGEua2F3YWkobW5ldDIuZ2MpLCB2ZXJ0ZXgubGFiZWw9TkEsIHZlcnRleC5zaXplPTIsIGVkZ2Uud2lkdGg9MC4wOCwgdmVydGV4LmNvbG9yPSdsaWdodGJsdWUnKQpgYGAKCk9uZSBpbXBvcnRhbnQgY2hhcmFjdGVyaXN0aWMgb2JzZXJ2ZWQgaW4gZ2lhbnQgY29tcG9uZW50IGlzIHRoZSBzby1jYWxsZWQgKnNtYWxsLXdvcmxkKiBwcm9wZXJ0eSB3aGljaCByZWZlcnMgdG8gdGhlIHNpdHVhdGlvbiB3aGVyZWluICoqdGhlIHNob3J0ZXN0LXBhdGggZGlzdGFuY2UgYmV0d2VlbiBwYWlycyBvZiBub2RlcyBpcyBnZW5lcmFsbHkgc21hbGwqKiBhbmQgKip0aGUgY2x1c3RlcmluZyBpcyByZWxhdGl2ZWx5IGhpZ2gqKi4gRm9yIG91ciBISVYvQUlEUyBjby1hdXRob3JzaGlwIG5ldHdvcmssIGxldCdzIGNvbXB1dGUgdGhlIGF2ZXJhZ2UgcGF0aCBsZW5ndGgKYGBge3J9CmF2ZXJhZ2UucGF0aC5sZW5ndGgobW5ldDIuZ2MpCmBgYAphbmQgdGhlIGxvbmdlc3Qgb2YgcGF0aHMKYGBge3J9CmRpYW1ldGVyKG1uZXQyLmdjKQpgYGAKCkxldCdzIGFzc2VzcyB0aGUgdHJhbnNpdGl2aXR5IG9mIG91ciBnaWFudCBjb21wb25lbnQ6CmBgYHtyfQp0cmFuc2l0aXZpdHkobW5ldDIuZ2MpCmBgYAoKKkludGVycHJldGF0aW9uOiogKipXZSBjYW4gc2VlIHRoYXQgdGhlIGF2ZXJhZ2UgcGF0aCBsZW5ndGggb2YgdGhlIGdpYW50IGNvbXBvbmVudCBvZiBvdXIgY28tYXV0aG9yc2hpcCBuZXR3b3JrIGlzIHNtYWxsIGFuZCB0aGUgbG9uZ2VzdCBvZiBwYXRocyBpcyBub3QgbXVjaCBiaWdnZXIuIEhlbmNlIG91ciBnaWFudCBjb21wb25lbnQgaGFzIGFsbCB0aGUgY2hhcmFjdGVyaXN0aWNzIG9mIGEgc21hbGwtd29ybGQuKiogSW4gYWRkaXRpb24sIHRoZSBjbHVzdGVyaW5nIGluIHRoaXMgbmV0d29yayBpcyB2ZXJ5IGxhcmdlIGluZGljYXRpbmcgdGhhdCAqKjQ3LjclIG9mIHRoZSBjb25uZWN0ZWQgdHJpcGxlcyBhcmUgY2xvc2UgdG8gZm9ybSB0cmlhbmdsZXMqKi4KCgpXZSBpbnZlc3RpZ2F0ZSB0aGUgY29uY2VwdHMgb2YgdmVydGV4IGFuZCBlZGdlIGN1dHMgZGVyaXZlZCBmcm9tIHRoZSBjb25jZXB0IG9mIHZlcnRleChlZGdlKSBjb25uZWN0aXZpdHkuIFRoZSB2ZXJ0ZXggKGVkZ2UpIGNvbm5lY3Rpdml0eSBvZiBhIGdyYXBoICRHJCBpcyB0aGUgbGFyZ2VzdCBpbnRlZ2VyIHN1Y2ggdGhhdCAkRyQgaXMgay12ZXJ0ZXgtIChlZGdlLSkgY29ubmVjdGVkLgoKYGBge3J9CnZlcnRleC5jb25uZWN0aXZpdHkobW5ldDIuZ2MpCmBgYAoKYGBge3J9CmVkZ2UuY29ubmVjdGl2aXR5KG1uZXQyLmdjKQpgYGAKSW4gdGhlIGNhc2Ugb2YgdGhlIGdpYW50IGNvbXBvbmVudCBvZiBvdXIgY28tYXV0aG9yc2hpcCBuZXR3b3JrLCB0aGUgdmVydGV4IGNvbm5lY3Rpdml0eSBpcyBlcXVhbCB0byAxIHdoaWxlIGVkZ2UgY29ubmVjdGl2aXR5IGlzIGVxdWFsIHRvIDMsICoqdGh1cyByZXF1aXJlcyB0aGUgcmVtb3ZhbCBvZiBvbmx5IGEgc2luZ2xlIHdlbGwtY2hvc2VuIG5vZGUgKGF1dGhvcikgb3IgMyBjb2xsYWJvcmF0aW9uIHRpZXMgaW4gb3JkZXIgdG8gYnJlYWsgdGhpcyBzdWJncmFwaCBpbnRvIGFkZGl0aW9uYWwgY29tcG9uZW50cyoqLgoKQSBzZXQgb2Ygbm9kZXMgKGVkZ2VzKSB0aGF0IGRpc2Nvbm5lY3RzIHRoZSBncmFwaCBpcyBjYWxsZWQgYSAqdmVydGV4IGN1dCAoZWRnZSBjdXQpKi4gQSBzaW5nbGUgbm9kZSAoYXV0aG9yKSB0aGF0IGRpc2Nvbm5lY3RzIG9mIHN1Y2ggdmVydGljZXMgaXMgY2FsbGVkIGEgKmN1dCB2ZXJ0ZXgqIGFuZCBjYW4gcHJvdmlkZSBhIHNlbnNlIG9mIHdoZXJlIGEgbmV0d29yayBpcyB2dWxuZXJhYmxlLiBMZXQncyBpZGVudGlmeSBzdWNoIHdlYWsgcG9pbnRzIGluIG91ciBjby1hdXRob3JzaGlwIG5ldHdvcms6IApgYGB7cn0KbW5ldDIuY3V0LnZlcnRpY2VzIDwtIGFydGljdWxhdGlvbi5wb2ludHMobW5ldDIuZ2MpCm1uZXQyLmN1dC52ZXJ0aWNlcwpgYGAKVGhlIGFib3ZlIGxpc3RlZCBhdXRob3JzIGNvbnN0aXR1dGUgdGhlIHdlYWsgKmFydGljdWxhdGlvbiBwb2ludHMqIG9mIG91ciBjby1hdXRob3JzaGlwIG5ldHdvcmsgYnV0IGFsc28gdGhlIG1vc3QgaW1wb3J0YW50IG5vZGVzIG9mIG91ciBuZXR3b3JrLgoKYGBge3J9Cmxlbmd0aChtbmV0Mi5jdXQudmVydGljZXMpCmBgYApJbiBvdXIgSElWL0FJRFMgY28tYXV0aG9yc2hpcCBuZXR3b3JrLCBsZXNzIHRoYW4gKiowLjElKiogb2YgdGhlIG5vZGVzIGFyZSBjdXQgdmVydGljZXMgbWVhbmluZyB0aGF0IHRoZSB2dWxuZXJhYmlsaXR5IG9mIHRoZSBuZXR3b3JrIGlzIGRlcGVuZGFudCBvbiBhIHZlcnkgc21hbGwgc2V0IG9mIGF1dGhvcnMgaW4gdGhlIGNvLWF1dGhvcnNoaXAgbmV0d29yay4KCgoqKkdyYXBoIFBhcnRpdGlvbmluZzoqKgpSZWd1bGFybHkgZnJhbWVkIGFzIGNvbW11bml0eSBkZXRlY3Rpb24gcHJvYmxlbSwgZ3JhcGggcGFydGl0aW9uaW5nIGlzIGFuIHVuc3VwZXJ2aXplZCBtZXRob2QgdXNlZCBpbiB0aGUgYW5hbHlzaXMgb2YgbmV0d29yayBkYXRhIHRvIGZpbmQgc3Vic2V0cyBvZiBub2RlcyB0aGF0IGRlbW9uc3RyYXRlIGEgJ2NvaGVzaXZlbmVzcycgd2l0aCByZXNwZWN0IHRvIHRoZWkgdW5kZXJseWluZyByZWxhdGlvbmFsIHBhdHRlcm5zLiBDb2hlc2l2ZSBzdWJzZXRzIG9mIG5vZGVzIGdlbmVyYWxseSBhcmUgd2VsbCBjb25uZWN0ZWQgYW1vbmcgdGhlbXNlbHZlcyBhbmQgYXJlIHdlbGwgc2VwYXJhdGVkIGZyb20gdGhlIG90aGVyIG5vZGVzIGluIHRoZSBncmFwaC4gSGVyZSwgd2UgcGVyZm9ybSB0d28gd2VsbCBlc3RhYmxpc2hlZCBtZXRob2RzIG9mIGdyYXBoIHBhcnRpdGlvbmluZzogKkhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nKiBhbmQgKlNwZWN0cmFsIGNsdXN0ZXJpbmcqLgoKKkhpZXJhcmNoaWNhbCBDbHVzdGVyaW5nOioKSGllcmFyY2hpY2FsIGNsdXN0ZXJpbmcgbWV0aG9kcyBhcmUgb2YgdHdvIGtpbmRzOgotIGFnZ2xvbWVyYXRpdmU6ICJiYXNlZCBvbiB0aGUgc3VjY2Vzc2l2ZSBjb2Fyc2VuaW5nIG9mIHBhcnRpdGlvbnMgdGhyb3VnaCB0aGUgcHJvY2VzcyBvZiBtZXJnaW5nIiwgaXQgdXNlcyBtb2R1bGFyaXR5IGFzIG1ldHJpY3MuCi0gZGl2aXNpdmU6ICJiYXNlZCBvbiB0aGUgc3VjY2Vzc2l2ZSByZWZpbmVtZW50IG9mIHBhcnRpdGlvbnMgdGhyb3VnaCB0aGUgcHJvY2VzcyBvZiBzcGxpdHRpbmciCgpIZXJlLCB3ZSBhcHBseSB0aGUgYWdnbG9tZXJhdGl2ZSBtZXRob2Qgb24gb3VyIEhJVi9BSURTIGNvLWF1dGhvcnNoaXAgbmV0d29yazoKYGBge3J9CmNvbS5tbmV0MiA8LSBmYXN0Z3JlZWR5LmNvbW11bml0eShtbmV0MikKVihtbmV0MikkY29tbXVuaXR5IDwtIGNvbS5tbmV0MiRtZW1iZXJzaGlwCmxlbmd0aChjb20ubW5ldDIpCmBgYApUaGUgYWdnbG9tZXJhdGl2ZSBoaWVyYXJjaGljYWwgY2x1c3RlcmluZyBpZGVudGlmaWVzIDI0IGNvbW11bml0aWVzIGluIG91ciBjby1hdXRob3JzaGlwIG5ldHdvcmsuCmBgYHtyfQpzaXplcyhjb20ubW5ldDIpCmBgYApMYXJnZSBjb21tdW5pdGllcyBjb250YWluIGJldHdlZW4gNTUgYW5kIDEwOCBhdXRob3JzLiBNZWRpdW0gc2l6ZSBjb21tdW5pdGllcyBjb250YWluIGJldHdlZW4gMTAgYW5kIDQ3IGF1dGhvcnMuCgpMZXQncyBub3cgdmlzdWFsaXplIHRoZSBjb21tdW5pdGllczoKYGBge3IsZmlnLmhlaWdodD0xNSxmaWcud2lkdGg9MTV9CnBsb3QoY29tLm1uZXQyLCBtbmV0Mix2ZXJ0ZXgubGFiZWw9JycsCiAgICAgbGF5b3V0PWwubW5ldDIsCiAgICAgbWFyay5ncm91cHMgPSBOVUxMLAogICAgICMgdmVydGV4LnNpemUgPSAzLAogICAgICMgZWRnZS5jb2xvciA9IG1lbWIubW5ldDIsCiAgICAgZWRnZS53aWR0aCA9IDAuMDgsCiAgICAgdmVydGV4LnNpemU9MSsxICogc3FydChidy5tbmV0LzEwMDApCiAgICAgKQpgYGAKCgpgYGB7cn0KZ2MuY29tIDwtIGZhc3RncmVlZHkuY29tbXVuaXR5KG1uZXQyLmdjKQpsZW5ndGgoZ2MuY29tKQpgYGAKIFRoZXJlJ3Mgb25seSAxMiBjb21tdW5pdGllcyBpbiB0aGUgZ2lhbnQgY29tcG9uZW50IG9mIG91ciBuZXR3b3JrLgogCiAKYGBge3J9CnNpemVzKGdjLmNvbSkKYGBgClRoZSA3IGNvbW11bml0aWVzIGNvbnRhaW4gYmV0d2VlbiA1IGFuZCAxMDcgYXV0aG9ycy4KCkxldCdzIG5vdyB2aXN1YWxpemUgdGhlIDEyIGNvbW11bml0aWVzIGluIHRoZSBnaWFudCBjb21wb25lbnQ6CmBgYHtyfQpsLmdjPC1sYXlvdXQua2FtYWRhLmthd2FpKG1uZXQyLmdjKQpidy5nYzwtYmV0d2Vlbm5lc3MobW5ldDIuZ2MpClYobW5ldDIuZ2MpJGNvbW11bml0eSA8LSBtZW1iZXJzaGlwKGdjLmNvbSkKYGBgCgpgYGB7cixmaWcuaGVpZ2h0PTE1LGZpZy53aWR0aD0xNX0KcGxvdChnYy5jb20sIG1uZXQyLmdjLHZlcnRleC5sYWJlbD0nJywKICAgICAjIGxheW91dD1sLm1uZXQyLAogICAgIGxheW91dCA9IGwuZ2MsCiAgICAgbWFyay5ncm91cHMgPSBOVUxMLAogICAgICMgdmVydGV4LnNpemUgPSAzLAogICAgICMgZWRnZS5jb2xvciA9IG1lbWIubW5ldDIsCiAgICAgZWRnZS53aWR0aCA9IDAuMDYsCiAgICAgdmVydGV4LnNpemU9MSsxICogc3FydChidy5nYy8xMDAwKQogICAgICkKYGBgCgpMZXQncyBwbG90IHRoZSAxMiBjb21tdW5pdGllcyBzZXBhcmF0ZWx5OgpgYGB7cixmaWcud2lkdGg9MjAsZmlnLmhlaWdodD00MCx3YXJuaW5nPUZBTFNFfQpwYXIoY2V4Lm1haW49MykKcGFyKG1mcm93PWMoNiwyKSkKIyBQbG90IE9yaWdpbmFsIGdpYW50IGNvbXBvbmVudCBncmFwaAojIHBsb3QoZ2MuY29tLCBtbmV0Mi5nYyx2ZXJ0ZXgubGFiZWw9JycsCiMgICAgICBtYWluID0gJ01haW4gY29tcG9uZW50JywKIyAgICAgICMgbGF5b3V0PWwubW5ldDIsCiMgICAgICBsYXlvdXQgPSBsLmdjLAojICAgICAgbWFyay5ncm91cHMgPSBOVUxMLAojICAgICAgIyB2ZXJ0ZXguc2l6ZSA9IDMsCiMgICAgICAjIGVkZ2UuY29sb3IgPSBtZW1iLm1uZXQyLAojICAgICAgZWRnZS53aWR0aCA9IDAuMDUsCiMgICAgICB2ZXJ0ZXguc2l6ZT0xKzEgKiBzcXJ0KGJ3LmdjLzEwMDApCiMgICAgICApCgpmb3IoaSBpbiAxOjEyKXsKICBnIDwtIHdoaWNoKFYobW5ldDIuZ2MpJGNvbW11bml0eT09aSkKICBHLmdyb3VwIDwtIHN1YmdyYXBoKG1uZXQyLmdjLCBnKQogIHBsb3QoRy5ncm91cCx2ZXJ0ZXgubGFiZWw9JycsCiAgICAgbWFpbiA9IHBhc3RlKCdDb21tdW5pdHkvUGFydGl0aW9uICcsaSksCiAgICAgIyBsYXlvdXQ9bC5tbmV0MiwKICAgICBsYXlvdXQgPSBsLmdjW2csXSwKICAgICBtYXJrLmdyb3VwcyA9IE5VTEwsCiAgICAgdmVydGV4LmNvbG9yID0gZ2MuY29tJG1lbWJlcnNoaXBbZ10sCiAgICAgIyB2ZXJ0ZXguc2l6ZSA9IDMsCiAgICAgIyBlZGdlLmNvbG9yID0gbWVtYi5tbmV0MiwKICAgICBlZGdlLndpZHRoID0gMC4wNiwKICAgICB2ZXJ0ZXguc2l6ZT0xKzEgKiBzcXJ0KGJ3LmdjLzEwMDApCiAgICAgKQp9CmBgYAoKV2UgZmluYWxseSBzYXZlIGFsbCBvdXIgZ2VuZXJhdGVkIFIgb2JqZWN0cyBmb3IgbGF0ZXIgdXNlLgpgYGB7cn0Kc2F2ZShtbmV0LCBmaWxlID0gJy4vUmRhdGEvSElWbW5ldC5yZGEnKQpzYXZlKG1uZXQyLCBmaWxlID0gJy4vUmRhdGEvSElWbW5ldDIucmRhJykKc2F2ZShhdXRoX2RhdGEsIGZpbGUgPSAnLi9SZGF0YS9ISVZhdXRoX2RhdGEucmRhJykKc2F2ZShlZGdlcywgZmlsZSA9ICcuL1JkYXRhL0hJVmVkZ2VzLnJkYScpCnNhdmUobW5ldDIuZ2MsIGZpbGUgPSAnLi9SZGF0YS9ISVZtbmV0Mi5nYy5yZGEnKQpgYGAKYGBge3J9CiMgc291cmNlKCdwbG90bHlfbWFwLlInKQpgYGAKJFxcJAoKPCEtLSBXZSBmaW5hbGx5IHBsb3QgdGhlIGNvbGxhYm9yYXRpb24gbWFwIHVzaW5nIFBsb3RseSBhbmQgYXZhaWxhYmxlIFtIRVJFXShodHRwczovL3Bsb3QubHkvfnJvc2VyaWNhem9uZGVrb24vNjEuZW1iZWQpLiAtLT4KCjwhLS0gPGlmcmFtZSBzcmM9Imh0dHBzOi8vcGxvdC5seS9+cm9zZXJpY2F6b25kZWtvbi82MS5lbWJlZCIgaGVpZ2h0PSI4MDAiIHdpZHRoPSIxMDAlIiAgICAgc2Nyb2xsaW5nPSJubyIgc2VhbWxlc3M9InNlYW1sZXNzIiBmcmFtZUJvcmRlcj0iMCI+PC9pZnJhbWU+IC0tPgo8IS0tIDxpbWcgc3JjPSJodHRwczovL3Bsb3QubHkvfnJvc2VyaWNhem9uZGVrb24vNjEucG5nIj4gLS0+CgokXFwkCiRcXCQKCgoqKk5FWFQgVFVUT1JJQUw6KiogW01hdGhlbWF0aWNhbCBNb2RlbGluZyBmb3IgTmV0d29yayBHcmFwaHNdKGh0dHA6Ly8jKQ==