### Load standardpackages
library(tidyverse) # Collection of all the good stuff like dplyr, ggplot2 ect.
library(magrittr) # For extra-piping operators (eg. %<>%)

library(tidygraph)
library(ggraph)

Welcome to your second part of the introduction to network analysis. In this session you will learn:

  1. What directed networks are, and when that matters.
  2. How different measures have to be calculated in directed networks.
  3. What multidimensional networks are, and how they matter.
  4. How to compare network measures between graphs, and with random graphs

Introduction

Hello so far :)

Directed networks

  • Up to now, we did not pay attention to the direction of edges, and assumed them to be symetric (A->B == B->A). this makes sense in a lot of setting, for instance when we look at co-occurence networks.
  • However, in many cases, such as friendship networks, that might not be the case (the person you name a close friend not necessarily thinks the same about you).
  • In such cases, we would like to take this directionality into account, and analyse directed networks.

Lets look a brief example of highschool students data, which had to name their close friends.

highschool %>%
  head()

Again, here it sometimes happens that friendship is not reciprocal, so we will create a directed friendship graph.

g <- highschool %>% 
  as_tbl_graph(directed = TRUE)
g
# A tbl_graph: 70 nodes and 506 edges
#
# A directed multigraph with 1 component
#
# Node Data: 70 x 1 (active)
  name 
  <chr>
1 1    
2 2    
3 3    
4 4    
5 5    
6 6    
# … with 64 more rows
#
# Edge Data: 506 x 3
   from    to  year
  <int> <int> <dbl>
1     1    13  1957
2     1    14  1957
3     1    20  1957
# … with 503 more rows
set.seed(1337)
# The names where anonymized, which is a bit boring. So I will just give them some random names to associate with.
library(randomNames)

g <- g %N>%
  mutate(gender = rbinom(n = n(), size = 1, prob = 0.5),
         label= randomNames(gender = gender, name.order = "first.last"))
g %N>% as_tibble()
  • Lets plot this network briefly to get a sense.
  • Notice that we have edges for two years, so we can do a facet plot for every year.
set.seed(1337)
g %E>% 
  ggraph(layout = "nicely") + 
    geom_edge_link(arrow = arrow()) + 
    geom_node_point() +
    theme_graph() +
    facet_edges(~year)

We indeed see that the friendship structure alters slightly between years. To make it less complicated for now, we will only look at the 1958 network.

g <- g %E>% 
  filter(year == 1958) %N>%
  filter(!node_is_isolated()) 
set.seed(1337)
g %E>% 
  ggraph(layout = "nicely") + 
    geom_edge_link(arrow = arrow()) + 
    geom_node_point() +
    theme_graph() 

Centrality measures

Our network is now directed, meaning a node-pair now has two different roles:

  • Ego: The node the edge loriginates from.
  • Alter: The node the edge leads to.

Consequently, most network metrics have to take this directionality into account. For example, degree centrality is now differentiated between the in-degree centrality (now many edges lead to the node) and the out-degree centrality (now many edges lead to the node)

g <- g %N>%
  mutate(cent_dgr_in = centrality_degree(mode = "in"),
         cent_dgr_out = centrality_degree(mode = "out")) 

Community Structures

Now it is getting a bit more complicated. Most community detection algorithms implemented in igraph only work with undirected networks. So, now we could do 2 things:

  1. Convert the network in an undirected one.
  2. Use the “edge betweenness” algorithm, the only one implemented that can handle directed networks.
g <- g %N>%
  mutate(community = group_edge_betweenness(directed = TRUE) %>% as.factor())
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
g %E>% 
  ggraph(layout = "nicely") + 
    geom_edge_link(arrow = arrow()) + 
    geom_node_point(aes(col = community, size = cent_dgr_in)) +
    theme_graph() 

Case: Lawyers, Friends & Foes

Introduction to the case

  • Emmanuel Lazega, The Collegial Phenomenon: The Social Mechanisms of Cooperation Among Peers in a Corporate Law Partnership, Oxford University Press (2001).

Data

  • This data set comes from a network study of corporate law partnership that was carried out in a Northeastern US corporate law firm, referred to as SG&R, 1988-1991 in New England.
  • It includes (among others) measurements of networks among the 71 attorneys (partners and associates) of this firm, i.e. their strong-coworker network, advice network, friendship network, and indirect control networks.
  • Various members’ attributes are also part of the dataset, including seniority, formal status, office in which they work, gender, lawschool attended, individual performance measurements (hours worked, fees brought in), attitudes concerning various management policy options, etc.
  • This dataset was used to identify social processes such as bounded solidarity, lateral control, quality control, knowledge sharing, balancing powers, regulation, etc. among peers.

Setting

  • What do corporate lawyers do? Litigation and corporate work.
  • Division of work and interdependencies.
  • Three offices, no departments, built-in pressures to grow, intake and assignment rules.
  • Partners and associates: hierarchy, up or out rule, billing targets.
  • Partnership agreement (sharing benefits equally, 90% exclusion rule, governance structure, elusive committee system) and incompleteness of the contracts.
  • Informal, unwritten rules (ex: no moonlighting, no investment in buildings, no nepotism, no borrowing to pay partners, etc.).
  • Huge incentives to behave opportunistically ; thus the dataset is appropriate for the study of social processes that make cooperation among rival partners possible.
  • Sociometric name generators used to elicit coworkers, advice, and ‘friendship’ ties at SG&R:“Here is the list of all the members of your Firm.”

The networks where created according to the follwoing questionaire:

  • Strong coworkers network: “Because most firms like yours are also organized very informally, it is difficult to get a clear idea of how the members really work together. Think back over the past year, consider all the lawyers in your Firm. Would you go through this list and check the names of those with whom you have worked with. By”worked with" I mean that you have spent time together on at least one case, that you have been assigned to the same case, that they read or used your work product or that you have read or used their work product; this includes professional work done within the Firm like Bar association work, administration, etc."
  • Basic advice network: “Think back over the past year, consider all the lawyers in your Firm. To whom did you go for basic professional advice? For instance, you want to make sure that you are handling a case right, making a proper decision, and you want to consult someone whose professional opinions are in general of great value to you. By advice I do not mean simply technical advice.”
  • ‘Friendship’ network: “Would you go through this list, and check the names of those you socialize with outside work. You know their family, they know yours, for instance. I do not mean all the people you are simply on a friendly level with, or people you happen to meet at Firm functions.”

Data preperation

Load the data

Lets load the data! The three networks refer to cowork, friendship, and advice. The first 36 respondents are the partners in the firm.

mat_friendship
   1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
1  0 1 0 1 0 0 0 1 0  0  0  0  0  0  0  0  1  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
2  0 0 0 0 0 0 0 0 0  0  0  0  0  0  0  1  1  0  0  0  0  1  0  0  0  1  0  0  0  0  0  0  0  0  0  0  0  0  0  0
3  0 0 0 0 0 0 0 0 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
4  0 1 1 0 0 0 0 0 1  0  1  1  0  1  0  1  1  0  1  0  1  1  1  0  0  1  1  0  1  0  0  0  0  0  0  0  0  0  0  0
5  0 0 0 0 0 0 1 0 0  0  0  0  0  0  0  0  0  1  0  0  0  0  0  0  1  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
6  0 0 0 0 0 0 0 0 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
7  0 0 1 0 1 0 0 0 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
8  0 0 0 0 0 0 0 0 0  0  1  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
9  0 0 0 1 0 0 0 0 0  0  1  0  0  0  0  0  0  0  0  0  1  0  1  1  0  0  1  0  0  0  0  0  0  0  0  0  0  0  0  0
10 0 1 0 0 0 0 0 1 1  0  1  1  1  0  0  0  1  0  0  0  1  0  0  1  0  1  1  0  1  0  0  0  0  1  0  0  0  1  0  0
11 0 0 0 0 0 0 0 1 1  0  0  0  0  0  0  0  1  0  0  0  1  0  0  0  0  0  1  0  0  0  0  0  0  0  0  0  0  0  0  0
12 1 1 0 1 1 0 0 1 1  1  1  0  1  0  1  1  1  0  1  0  1  1  0  1  1  1  1  0  1  0  0  0  0  1  0  0  0  1  0  0
13 0 0 0 1 0 0 0 0 1  0  1  1  0  0  0  0  0  0  0  1  1  0  1  1  0  1  1  0  0  0  0  0  0  0  0  0  0  1  0  0
14 0 0 1 0 0 1 0 0 0  0  0  0  0  0  0  1  1  0  0  1  0  0  0  0  1  0  0  1  0  0  0  0  0  0  0  0  0  0  0  0
   41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
1   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
2   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
3   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
4   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
5   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
6   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
7   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
8   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
9   0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
10  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
11  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
12  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
13  1  0  1  0  0  0  0  0  0  0  0  1  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
14  0  0  0  0  0  0  0  0  0  1  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 [ reached getOption("max.print") -- omitted 57 rows ]

We also load a set of nodes

nodes %>% head()

Cleaning up

The variables in nodes are unnamed, but from the paper I know how they are coded, so we can give them names.

colnames(nodes) <- c("name", "seniority", "gender", "office", "tenure", "age", "practice", "school")

We can also recode the numeric codes in the data into something more intuitive. I agaion know from the data description of the paper the coding.

  • seniority status (1=partner; 2=associate)
  • gender (1=man; 2=woman)
  • office (1=Boston; 2=Hartford; 3=Providence)
  • years with the firm
  • age
  • practice (1=litigation; 2=corporate)
  • law school (1: harvard, yale; 2: ucon; 3: other)
nodes %<>%
  mutate(name = name %>% as.numeric(),
         seniority = recode(seniority, "1" = "Partner", "2" = "Associate"),
         gender = recode(gender, "1" = "Man", "2" = "Woman"),
         office = recode(office, "1" = "Boston", "2" = "Hartford", "3" = "Providence"),
         practice = recode(practice, "1" = "Litigation", "2" = "Corporate"),
         school = recode(school, "1" = "Harvard, Yale", "2" = "Ucon", "3" = "Others"))   
nodes %>% head()

Generate the graph

  • Since we have now a multidimensional network (=different types of edges), we first load them into isolated networks.
  • We could also directly load them into one network with labeled edges, but that’s a bit more complicated, so we keep it for the sake of clarity seperated for now.
g_friendship <- mat_friendship %>% as_tbl_graph(directed = TRUE) %E>%
  mutate(type = "friendship") %N>%
  mutate(name = name %>% as.numeric()) %>%
  left_join(nodes, by = "name")

g_advice <- mat_advice %>% as_tbl_graph(directed = TRUE) %E>%
  mutate(type = "advice") %N>%
  mutate(name = name %>% as.numeric()) %>%
  left_join(nodes, by = "name")

g_work <- mat_work %>% as_tbl_graph(directed = TRUE) %E>%
  mutate(type = "work") %N>%
  mutate(name = name %>% as.numeric()) %>%
  left_join(nodes, by = "name")

# Notice: The node names are taken from the matrices dimnames as string, therefore need to be converted as numeric

First inspection

# We could also join all the networks together.
g_all <- g_friendship %>%
  graph_join(g_advice, by = "name") %>%
  graph_join(g_work, by = "name")
g_all %E>%
  as_tibble() %>%
  head()
# Then we could plot them pointly via an edge facett...
g_all %>% 
  ggraph(layout = 'fr') + 
  geom_edge_fan(aes(col = type), 
                arrow = arrow(angle = 30, length = unit(0.25, 'cm'),type = "closed"), 
                alpha = 0.25) + 
  geom_node_point(col = 'purple') +
  geom_node_text(aes(label = name)) + 
  theme_graph() +
  theme(legend.position = "none") +
  facet_edges(~type) 

This is convenient, yet somewhat of a compromise, since the layout is optimized on the full network of all edges. So it kind of fits to all, but not fully to one…

Network effects & structures

for the following, we will only look at the friendship network, while i leave the analysis of the other’s up to you.

g <- g_friendship

Lets take a look

set.seed(1337)
# Then we could plot them pointly via an edge facett...
g %N>% 
  filter(!node_is_isolated()) %>%
  ggraph(layout = 'stress') + 
  geom_edge_fan(arrow = arrow(angle = 30, length = unit(0.25, 'cm'),type = 'closed'), alpha = 0.25) + 
  geom_node_point(aes(col = office, size = centrality_eigen(directed = TRUE))) +
  geom_node_text(aes(label = name, size = centrality_eigen(directed = TRUE))) + 
  theme_graph() +
  theme(legend.position = "bottom") +
  facet_edges(~type) 

Node level (local)

  • We could look at all the node level characteristics (degree, betweenness etc.) again, but for the sake of time I skip that for now, since its all already in the last notebook.

Network level (global)

library(igraph)
  • Ok, lets do the whole exercise with getting the main-determinants of the network structure again. We can look at the classical structural determinants.
# Get density of a graph
edge_density(g)
[1] 0.1156942
# Get the diameter of the graph g
diameter(g, directed = TRUE)
[1] 7
# Get the average path length of the graph g
mean_distance(g, directed = TRUE)
[1] 2.505126
# Transistivity
transitivity(g, type ="global")
[1] 0.4486229

Network level (global direced)

  • Since we here have a directed network, a couple of interesting additional metrics are available, explicitly taking into account th direction of edges.
  • While there are many more, we here will just take a look at some of the most important ones, which are also known to be popular mechanisms in social networks.

  • Reciprocity measures the extend to which edges are reciprocal, meaning a edge between i & j implies also an edge between j & i
# reciprocity
reciprocity(g)
[1] 0.6121739
  • We have another important concept that often explains edge-formation in directed (social) networks: Assortativity, also called homopholy.
  • This is a measure of how preferentially attached vertices are to other vertices with identical attributes. In other words: How much “birds of the same feather flock together ”.
  • Lets first look at people of the same tenure flock together.
assortativity(g, V(g)$tenure, directed = TRUE)
[1] 0.5499482
  • What about people from elite universities?
assortativity(g, V(g)$school == "Harvard, Yale", directed = TRUE)
[1] 0.1808466
  • Lastly, what about the popularity (or “Matthew”) effect?
assortativity(g, degree(g, mode = "in"), directed = TRUE)
[1] 0.1771873
  • One more thing we didn’t talk about yet: Small worlds.
  • Small worlds are an interesting network structure, combining short path lenght betwen the nodes with a high clustering coefficient.
  • That means, that we have small interconnected clusters, which are in turn connected by gatekeepers (the edges we call bridges or structural holes).

This leads to an interesting setup, which has proven to be conductive for efficient communication and fast diffusion of information in social networks.

We calculate it for now in an easy way:

transitivity(g, type ="global") / mean_distance(g, directed = TRUE)
[1] 0.179082

However, you by now probably wonder how to interprete this numbers. Are they high, low, or whatever? What is the reference? In fact, it’s very hard to say. The best way to say something about that is to compare it with what a random network would look like.

So, lets create a random network. Here, we use the play_erdos_renyi() function, which creates a network with a given number of nodes and edge-density, but where the edges are constructed completely random.

g_r <- play_erdos_renyi(n = g %>% gorder(),  
                        m  = g %>% gsize(), 
                        directed = TRUE, 
                        loops = FALSE)

Looks kind of different. However, one randomly created network doesn’t present a good baseline. So, lets better create a bunch, and compare our network to the average values of the randomly generated ones.

# Generate n random graphs
n = 1000
g_l <- vector('list', n)
  
for(i in 1:n){
  g_l[[i]] <- play_erdos_renyi(n = g %>% gorder(),  
                        m  = g %>% gsize(), 
                        directed = TRUE, 
                        loops = FALSE)
}

Now we can see how meaningful our observed network statistics are, by comparing them with the mean of the statistics in the random network.

# Calculate average path length of 1000 random graphs
dist_r <- g_l %>% lapply(mean_distance, directed = TRUE) %>% unlist() #%>% mean()
cc_r <- g_l %>% lapply(transitivity, type = "global") %>% unlist() #%>% mean()
rp_r <- g_l %>% lapply(reciprocity) %>% unlist() #%>% mean()

Lets see:

stats_friend <- tibble(density = g %>% edge_density(),
                       diameter = g %>% diameter(directed = TRUE),
                       reciprocity = g %>% reciprocity(),
                       reciprocity_score = mean(reciprocity(g) > rp_r),
                       distance = g %>% mean_distance(directed = TRUE),
                       distance_score = mean(mean_distance(g, directed = TRUE) > dist_r),
                       clustering = g %>% transitivity(type = "global"),
                       clustering_score = mean(transitivity(g, type = "global")  > cc_r),
                       small_world = mean(transitivity(g, type = "global")  > cc_r) / mean(mean_distance(g, directed = TRUE) > dist_r) )
stats_friend

Your turn

Please do Exercise 1 in the corresponding section on Github.

Endnotes

Suggestions for further study

Literature

Classics

  • Wasserman, S., & Faust, K. (1994). Social network analysis: Methods and applications (Vol. 8). Cambridge university press.
  • Carrington, P. J., Scott, J., & Wasserman, S. (Eds.). (2005). Models and methods in social network analysis (Vol. 28). Cambridge university pres

Own work on directed networks

  • Hain, D., Buchmann, T., Kudic, M., & Müller, M. (2018). Endogenous dynamics of innovation networks in the German automotive industry: analysing structural network evolution using a stochastic actor-oriented approach. International Journal of Computational Economics and Econometrics, 8(3-4), 325-344.
  • Hain, Daniel S., and Roman Jurowetzki. “Incremental by Design? On the Role of Incumbents in Technology Niches.” Foundations of Economic Change. Springer, Cham, 2017. 299-332.

Session Info

sessionInfo()
LS0tCnRpdGxlOiAnSW50ZXJtZWRpYXRlIChEaXJlY3RlZCkgTmV0d29yayBBbmFseXNpczogQXBwbGljYXRpb24gKFIpJwphdXRob3I6ICJEYW5pZWwgUy4gSGFpbiAoZHNoQGJ1c2luZXNzLmFhdS5kaykiCmRhdGU6ICJVcGRhdGVkIGByIGZvcm1hdChTeXMudGltZSgpLCAnJUIgJWQsICVZJylgIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIGNvZGVfZm9sZGluZzogc2hvdwogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgICB0b2M6IHRydWUKICAgIHRvY19kZXB0aDogMgogICAgdG9jX2Zsb2F0OgogICAgICBjb2xsYXBzZWQ6IGZhbHNlCiAgICB0aGVtZTogZmxhdGx5Ci0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CiMjIyBHZW5lcmljIHByZWFtYmxlCnJtKGxpc3Q9bHMoKSkKU3lzLnNldGVudihMQU5HID0gImVuIikgIyBGb3IgZW5nbGlzaCBsYW5ndWFnZQpvcHRpb25zKHNjaXBlbiA9IDUpICMgVG8gZGVhY3RpdmF0ZSBhbm5veWluZyBzY2llbnRpZmljIG51bWJlciBub3RhdGlvbgoKIyMjIEtuaXRyIG9wdGlvbnMKbGlicmFyeShrbml0cikgIyBGb3IgZGlzcGxheSBvZiB0aGUgbWFya2Rvd24Ka25pdHI6Om9wdHNfY2h1bmskc2V0KHdhcm5pbmc9RkFMU0UsCiAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2U9RkFMU0UsCiAgICAgICAgICAgICAgICAgICAgIGNvbW1lbnQ9RkFMU0UsIAogICAgICAgICAgICAgICAgICAgICBmaWcuYWxpZ249ImNlbnRlciIKICAgICAgICAgICAgICAgICAgICAgKQpgYGAKCmBgYHtyfQojIyMgTG9hZCBzdGFuZGFyZHBhY2thZ2VzCmxpYnJhcnkodGlkeXZlcnNlKSAjIENvbGxlY3Rpb24gb2YgYWxsIHRoZSBnb29kIHN0dWZmIGxpa2UgZHBseXIsIGdncGxvdDIgZWN0LgpsaWJyYXJ5KG1hZ3JpdHRyKSAjIEZvciBleHRyYS1waXBpbmcgb3BlcmF0b3JzIChlZy4gJTw+JSkKCmxpYnJhcnkodGlkeWdyYXBoKQpsaWJyYXJ5KGdncmFwaCkKYGBgCgoKV2VsY29tZSB0byB5b3VyIHNlY29uZCBwYXJ0IG9mIHRoZSBpbnRyb2R1Y3Rpb24gdG8gbmV0d29yayBhbmFseXNpcy4gSW4gdGhpcyBzZXNzaW9uIHlvdSB3aWxsIGxlYXJuOgoKMS4gV2hhdCBkaXJlY3RlZCBuZXR3b3JrcyBhcmUsIGFuZCB3aGVuIHRoYXQgbWF0dGVycy4KMi4gSG93IGRpZmZlcmVudCBtZWFzdXJlcyBoYXZlIHRvIGJlIGNhbGN1bGF0ZWQgaW4gZGlyZWN0ZWQgbmV0d29ya3MuCjMuIFdoYXQgbXVsdGlkaW1lbnNpb25hbCBuZXR3b3JrcyBhcmUsIGFuZCBob3cgdGhleSBtYXR0ZXIuCjQuIEhvdyB0byBjb21wYXJlIG5ldHdvcmsgbWVhc3VyZXMgYmV0d2VlbiBncmFwaHMsIGFuZCB3aXRoIHJhbmRvbSBncmFwaHMKCiMgSW50cm9kdWN0aW9uCgpIZWxsbyBzbyBmYXIgOikgCgojIERpcmVjdGVkIG5ldHdvcmtzCgoqIFVwIHRvIG5vdywgd2UgZGlkIG5vdCBwYXkgYXR0ZW50aW9uIHRvIHRoZSBkaXJlY3Rpb24gb2YgZWRnZXMsIGFuZCBhc3N1bWVkIHRoZW0gdG8gYmUgc3ltZXRyaWMgKGBBLT5CID09IEItPkFgKS4gdGhpcyBtYWtlcyBzZW5zZSBpbiBhIGxvdCBvZiBzZXR0aW5nLCBmb3IgaW5zdGFuY2Ugd2hlbiB3ZSBsb29rIGF0IGNvLW9jY3VyZW5jZSBuZXR3b3Jrcy4gCiogSG93ZXZlciwgaW4gbWFueSBjYXNlcywgc3VjaCBhcyBmcmllbmRzaGlwIG5ldHdvcmtzLCB0aGF0IG1pZ2h0IG5vdCBiZSB0aGUgY2FzZSAodGhlIHBlcnNvbiB5b3UgbmFtZSBhIGNsb3NlIGZyaWVuZCBub3QgbmVjZXNzYXJpbHkgdGhpbmtzIHRoZSBzYW1lIGFib3V0IHlvdSkuIAoqIEluIHN1Y2ggY2FzZXMsIHdlIHdvdWxkIGxpa2UgdG8gdGFrZSB0aGlzIGRpcmVjdGlvbmFsaXR5IGludG8gYWNjb3VudCwgYW5kIGFuYWx5c2UgKipkaXJlY3RlZCBuZXR3b3JrcyoqLiAKCkxldHMgbG9vayBhIGJyaWVmIGV4YW1wbGUgb2YgaGlnaHNjaG9vbCBzdHVkZW50cyBkYXRhLCB3aGljaCBoYWQgdG8gbmFtZSB0aGVpciBjbG9zZSBmcmllbmRzLgoKYGBge3J9CmhpZ2hzY2hvb2wgJT4lCiAgaGVhZCgpCmBgYAoKQWdhaW4sIGhlcmUgaXQgc29tZXRpbWVzIGhhcHBlbnMgdGhhdCBmcmllbmRzaGlwIGlzIG5vdCByZWNpcHJvY2FsLCBzbyB3ZSB3aWxsIGNyZWF0ZSBhIGRpcmVjdGVkIGZyaWVuZHNoaXAgZ3JhcGguCgpgYGB7cn0KZyA8LSBoaWdoc2Nob29sICU+JSAKICBhc190YmxfZ3JhcGgoZGlyZWN0ZWQgPSBUUlVFKQpgYGAKCmBgYHtyfQpnCmBgYAoKCmBgYHtyfQpzZXQuc2VlZCgxMzM3KQojIFRoZSBuYW1lcyB3aGVyZSBhbm9ueW1pemVkLCB3aGljaCBpcyBhIGJpdCBib3JpbmcuIFNvIEkgd2lsbCBqdXN0IGdpdmUgdGhlbSBzb21lIHJhbmRvbSBuYW1lcyB0byBhc3NvY2lhdGUgd2l0aC4KbGlicmFyeShyYW5kb21OYW1lcykKCmcgPC0gZyAlTj4lCiAgbXV0YXRlKGdlbmRlciA9IHJiaW5vbShuID0gbigpLCBzaXplID0gMSwgcHJvYiA9IDAuNSksCiAgICAgICAgIGxhYmVsPSByYW5kb21OYW1lcyhnZW5kZXIgPSBnZW5kZXIsIG5hbWUub3JkZXIgPSAiZmlyc3QubGFzdCIpKQpgYGAKCmBgYHtyfQpnICVOPiUgYXNfdGliYmxlKCkKYGBgCgoqIExldHMgcGxvdCB0aGlzIG5ldHdvcmsgYnJpZWZseSB0byBnZXQgYSBzZW5zZS4gCiogTm90aWNlIHRoYXQgd2UgaGF2ZSBlZGdlcyBmb3IgdHdvIHllYXJzLCBzbyB3ZSBjYW4gZG8gYSBmYWNldCBwbG90IGZvciBldmVyeSB5ZWFyLgoKYGBge3J9CnNldC5zZWVkKDEzMzcpCmcgJUU+JSAKICBnZ3JhcGgobGF5b3V0ID0gIm5pY2VseSIpICsgCiAgICBnZW9tX2VkZ2VfbGluayhhcnJvdyA9IGFycm93KCkpICsgCiAgICBnZW9tX25vZGVfcG9pbnQoKSArCiAgICB0aGVtZV9ncmFwaCgpICsKICAgIGZhY2V0X2VkZ2VzKH55ZWFyKQpgYGAKV2UgaW5kZWVkIHNlZSB0aGF0IHRoZSBmcmllbmRzaGlwIHN0cnVjdHVyZSBhbHRlcnMgc2xpZ2h0bHkgYmV0d2VlbiB5ZWFycy4gVG8gbWFrZSBpdCBsZXNzIGNvbXBsaWNhdGVkIGZvciBub3csIHdlIHdpbGwgb25seSBsb29rIGF0IHRoZSAxOTU4IG5ldHdvcmsuCgpgYGB7cn0KZyA8LSBnICVFPiUgCiAgZmlsdGVyKHllYXIgPT0gMTk1OCkgJU4+JQogIGZpbHRlcighbm9kZV9pc19pc29sYXRlZCgpKSAKYGBgCgpgYGB7cn0Kc2V0LnNlZWQoMTMzNykKZyAlRT4lIAogIGdncmFwaChsYXlvdXQgPSAibmljZWx5IikgKyAKICAgIGdlb21fZWRnZV9saW5rKGFycm93ID0gYXJyb3coKSkgKyAKICAgIGdlb21fbm9kZV9wb2ludCgpICsKICAgIHRoZW1lX2dyYXBoKCkgCmBgYAoKIyMgQ2VudHJhbGl0eSBtZWFzdXJlcwoKT3VyIG5ldHdvcmsgaXMgbm93IGRpcmVjdGVkLCBtZWFuaW5nIGEgbm9kZS1wYWlyIG5vdyBoYXMgdHdvIGRpZmZlcmVudCByb2xlczoKCiogKipFZ286KiogVGhlIG5vZGUgdGhlIGVkZ2UgbG9yaWdpbmF0ZXMgZnJvbS4KKiAqKkFsdGVyOioqIFRoZSBub2RlIHRoZSBlZGdlIGxlYWRzIHRvLgoKQ29uc2VxdWVudGx5LCBtb3N0IG5ldHdvcmsgbWV0cmljcyBoYXZlIHRvIHRha2UgdGhpcyBkaXJlY3Rpb25hbGl0eSBpbnRvIGFjY291bnQuIEZvciBleGFtcGxlLCBkZWdyZWUgY2VudHJhbGl0eSBpcyBub3cgZGlmZmVyZW50aWF0ZWQgYmV0d2VlbiB0aGUgKippbi1kZWdyZWUqKiBjZW50cmFsaXR5IChub3cgbWFueSBlZGdlcyBsZWFkIHRvIHRoZSBub2RlKSBhbmQgdGhlICoqb3V0LWRlZ3JlZSoqIGNlbnRyYWxpdHkgKG5vdyBtYW55IGVkZ2VzIGxlYWQgdG8gdGhlIG5vZGUpCgpgYGB7cn0KZyA8LSBnICVOPiUKICBtdXRhdGUoY2VudF9kZ3JfaW4gPSBjZW50cmFsaXR5X2RlZ3JlZShtb2RlID0gImluIiksCiAgICAgICAgIGNlbnRfZGdyX291dCA9IGNlbnRyYWxpdHlfZGVncmVlKG1vZGUgPSAib3V0IikpIApgYGAKCiMjIENvbW11bml0eSBTdHJ1Y3R1cmVzCgpOb3cgaXQgaXMgZ2V0dGluZyBhIGJpdCBtb3JlIGNvbXBsaWNhdGVkLiBNb3N0IGNvbW11bml0eSBkZXRlY3Rpb24gYWxnb3JpdGhtcyBpbXBsZW1lbnRlZCBpbiBgaWdyYXBoYCBvbmx5IHdvcmsgd2l0aCB1bmRpcmVjdGVkIG5ldHdvcmtzLiBTbywgbm93IHdlIGNvdWxkIGRvIDIgdGhpbmdzOgoKMS4gQ29udmVydCB0aGUgbmV0d29yayBpbiBhbiB1bmRpcmVjdGVkIG9uZS4KMi4gVXNlIHRoZSAiZWRnZSBiZXR3ZWVubmVzcyIgYWxnb3JpdGhtLCB0aGUgb25seSBvbmUgaW1wbGVtZW50ZWQgdGhhdCBjYW4gaGFuZGxlIGRpcmVjdGVkIG5ldHdvcmtzLgoKYGBge3IsIHdhcm5pbmc9RkFMU0UsY29tbWVudD1GQUxTRSxlcnJvcj1GQUxTRSxtZXNzYWdlPUZBTFNFfQpnIDwtIGcgJU4+JQogIG11dGF0ZShjb21tdW5pdHkgPSBncm91cF9lZGdlX2JldHdlZW5uZXNzKGRpcmVjdGVkID0gVFJVRSkgJT4lIGFzLmZhY3RvcigpKQpgYGAKCmBgYHtyfQpnICVFPiUgCiAgZ2dyYXBoKGxheW91dCA9ICJuaWNlbHkiKSArIAogICAgZ2VvbV9lZGdlX2xpbmsoYXJyb3cgPSBhcnJvdygpKSArIAogICAgZ2VvbV9ub2RlX3BvaW50KGFlcyhjb2wgPSBjb21tdW5pdHksIHNpemUgPSBjZW50X2Rncl9pbikpICsKICAgIHRoZW1lX2dyYXBoKCkgCmBgYAoKCiMgQ2FzZTogTGF3eWVycywgRnJpZW5kcyAmIEZvZXMKCiMjIEludHJvZHVjdGlvbiB0byB0aGUgY2FzZQoKKiBFbW1hbnVlbCBMYXplZ2EsIFRoZSBDb2xsZWdpYWwgUGhlbm9tZW5vbjogVGhlIFNvY2lhbCBNZWNoYW5pc21zIG9mIENvb3BlcmF0aW9uIEFtb25nIFBlZXJzIGluIGEgQ29ycG9yYXRlIExhdyBQYXJ0bmVyc2hpcCwgT3hmb3JkIFVuaXZlcnNpdHkgUHJlc3MgKDIwMDEpLgoKIyMjIERhdGEgCiogVGhpcyBkYXRhIHNldCBjb21lcyBmcm9tIGEgbmV0d29yayBzdHVkeSBvZiBjb3Jwb3JhdGUgbGF3IHBhcnRuZXJzaGlwIHRoYXQgd2FzIGNhcnJpZWQgb3V0IGluIGEgTm9ydGhlYXN0ZXJuIFVTIGNvcnBvcmF0ZSBsYXcgZmlybSwgcmVmZXJyZWQgdG8gYXMgU0cmUiwgMTk4OC0xOTkxIGluIE5ldyBFbmdsYW5kLiAKKiBJdCBpbmNsdWRlcyAoYW1vbmcgb3RoZXJzKSBtZWFzdXJlbWVudHMgb2YgbmV0d29ya3MgYW1vbmcgdGhlIDcxIGF0dG9ybmV5cyAocGFydG5lcnMgYW5kIGFzc29jaWF0ZXMpIG9mIHRoaXMgZmlybSwgaS5lLiB0aGVpciBzdHJvbmctY293b3JrZXIgbmV0d29yaywgYWR2aWNlIG5ldHdvcmssIGZyaWVuZHNoaXAgbmV0d29yaywgYW5kIGluZGlyZWN0IGNvbnRyb2wgbmV0d29ya3MuIAoqIFZhcmlvdXMgbWVtYmVycycgYXR0cmlidXRlcyBhcmUgYWxzbyBwYXJ0IG9mIHRoZSBkYXRhc2V0LCBpbmNsdWRpbmcgc2VuaW9yaXR5LCBmb3JtYWwgc3RhdHVzLCBvZmZpY2UgaW4gd2hpY2ggdGhleSB3b3JrLCBnZW5kZXIsIGxhd3NjaG9vbCBhdHRlbmRlZCwgaW5kaXZpZHVhbCBwZXJmb3JtYW5jZSBtZWFzdXJlbWVudHMgKGhvdXJzIHdvcmtlZCwgZmVlcyBicm91Z2h0IGluKSwgYXR0aXR1ZGVzIGNvbmNlcm5pbmcgdmFyaW91cyBtYW5hZ2VtZW50IHBvbGljeSBvcHRpb25zLCBldGMuIAoqIFRoaXMgZGF0YXNldCB3YXMgdXNlZCB0byBpZGVudGlmeSBzb2NpYWwgcHJvY2Vzc2VzIHN1Y2ggYXMgYm91bmRlZCBzb2xpZGFyaXR5LCBsYXRlcmFsIGNvbnRyb2wsIHF1YWxpdHkgY29udHJvbCwga25vd2xlZGdlIHNoYXJpbmcsIGJhbGFuY2luZyBwb3dlcnMsIHJlZ3VsYXRpb24sIGV0Yy4gYW1vbmcgcGVlcnMuCgojIyMgU2V0dGluZwoqIFdoYXQgZG8gY29ycG9yYXRlIGxhd3llcnMgZG8/IExpdGlnYXRpb24gYW5kIGNvcnBvcmF0ZSB3b3JrLgoqIERpdmlzaW9uIG9mIHdvcmsgYW5kIGludGVyZGVwZW5kZW5jaWVzLgoqIFRocmVlIG9mZmljZXMsIG5vIGRlcGFydG1lbnRzLCBidWlsdC1pbiBwcmVzc3VyZXMgdG8gZ3JvdywgaW50YWtlIGFuZCBhc3NpZ25tZW50IHJ1bGVzLgoqIFBhcnRuZXJzIGFuZCBhc3NvY2lhdGVzOiBoaWVyYXJjaHksIHVwIG9yIG91dCBydWxlLCBiaWxsaW5nIHRhcmdldHMuCiogUGFydG5lcnNoaXAgYWdyZWVtZW50IChzaGFyaW5nIGJlbmVmaXRzIGVxdWFsbHksIDkwJSBleGNsdXNpb24gcnVsZSwgZ292ZXJuYW5jZSBzdHJ1Y3R1cmUsIGVsdXNpdmUgY29tbWl0dGVlIHN5c3RlbSkgYW5kIGluY29tcGxldGVuZXNzIG9mIHRoZSBjb250cmFjdHMuCiogSW5mb3JtYWwsIHVud3JpdHRlbiBydWxlcyAoZXg6IG5vIG1vb25saWdodGluZywgbm8gaW52ZXN0bWVudCBpbiBidWlsZGluZ3MsIG5vIG5lcG90aXNtLCBubyBib3Jyb3dpbmcgdG8gcGF5IHBhcnRuZXJzLCBldGMuKS4KKiBIdWdlIGluY2VudGl2ZXMgdG8gYmVoYXZlIG9wcG9ydHVuaXN0aWNhbGx5IDsgdGh1cyB0aGUgZGF0YXNldCBpcyBhcHByb3ByaWF0ZSBmb3IgdGhlIHN0dWR5IG9mIHNvY2lhbCBwcm9jZXNzZXMgdGhhdCBtYWtlIGNvb3BlcmF0aW9uIGFtb25nIHJpdmFsIHBhcnRuZXJzIHBvc3NpYmxlLiAKKiBTb2Npb21ldHJpYyBuYW1lIGdlbmVyYXRvcnMgdXNlZCB0byBlbGljaXQgY293b3JrZXJzLCBhZHZpY2UsIGFuZCAnZnJpZW5kc2hpcCcgdGllcyBhdCBTRyZSOiJIZXJlIGlzIHRoZSBsaXN0IG9mIGFsbCB0aGUgbWVtYmVycyBvZiB5b3VyIEZpcm0uIgoKVGhlIG5ldHdvcmtzIHdoZXJlIGNyZWF0ZWQgYWNjb3JkaW5nIHRvIHRoZSBmb2xsd29pbmcgcXVlc3Rpb25haXJlOgoKKiBTdHJvbmcgY293b3JrZXJzIG5ldHdvcms6ICJCZWNhdXNlIG1vc3QgZmlybXMgbGlrZSB5b3VycyBhcmUgYWxzbyBvcmdhbml6ZWQgdmVyeSBpbmZvcm1hbGx5LCBpdCBpcyBkaWZmaWN1bHQgdG8gZ2V0IGEgY2xlYXIgaWRlYSBvZiBob3cgdGhlIG1lbWJlcnMgcmVhbGx5IHdvcmsgdG9nZXRoZXIuIFRoaW5rIGJhY2sgb3ZlciB0aGUgcGFzdCB5ZWFyLCBjb25zaWRlciBhbGwgdGhlIGxhd3llcnMgaW4geW91ciBGaXJtLiBXb3VsZCB5b3UgZ28gdGhyb3VnaCB0aGlzIGxpc3QgYW5kIGNoZWNrIHRoZSBuYW1lcyBvZiB0aG9zZSB3aXRoIHdob20geW91IGhhdmUgd29ya2VkIHdpdGguIEJ5ICJ3b3JrZWQgd2l0aCIgSSBtZWFuIHRoYXQgeW91IGhhdmUgc3BlbnQgdGltZSB0b2dldGhlciBvbiBhdCBsZWFzdCBvbmUgY2FzZSwgdGhhdCB5b3UgaGF2ZSBiZWVuIGFzc2lnbmVkIHRvIHRoZSBzYW1lIGNhc2UsIHRoYXQgdGhleSByZWFkIG9yIHVzZWQgeW91ciB3b3JrIHByb2R1Y3Qgb3IgdGhhdCB5b3UgaGF2ZSByZWFkIG9yIHVzZWQgdGhlaXIgd29yayBwcm9kdWN0OyB0aGlzIGluY2x1ZGVzIHByb2Zlc3Npb25hbCB3b3JrIGRvbmUgd2l0aGluIHRoZSBGaXJtIGxpa2UgQmFyIGFzc29jaWF0aW9uIHdvcmssIGFkbWluaXN0cmF0aW9uLCBldGMuIgoqIEJhc2ljIGFkdmljZSBuZXR3b3JrOiAiVGhpbmsgYmFjayBvdmVyIHRoZSBwYXN0IHllYXIsIGNvbnNpZGVyIGFsbCB0aGUgbGF3eWVycyBpbiB5b3VyIEZpcm0uIFRvIHdob20gZGlkIHlvdSBnbyBmb3IgYmFzaWMgcHJvZmVzc2lvbmFsIGFkdmljZT8gRm9yIGluc3RhbmNlLCB5b3Ugd2FudCB0byBtYWtlIHN1cmUgdGhhdCB5b3UgYXJlIGhhbmRsaW5nIGEgY2FzZSByaWdodCwgbWFraW5nIGEgcHJvcGVyIGRlY2lzaW9uLCBhbmQgeW91IHdhbnQgdG8gY29uc3VsdCBzb21lb25lIHdob3NlIHByb2Zlc3Npb25hbCBvcGluaW9ucyBhcmUgaW4gZ2VuZXJhbCBvZiBncmVhdCB2YWx1ZSB0byB5b3UuIEJ5IGFkdmljZSBJIGRvIG5vdCBtZWFuIHNpbXBseSB0ZWNobmljYWwgYWR2aWNlLiIKKiAnRnJpZW5kc2hpcCcgbmV0d29yazoKIldvdWxkIHlvdSBnbyB0aHJvdWdoIHRoaXMgbGlzdCwgYW5kIGNoZWNrIHRoZSBuYW1lcyBvZiB0aG9zZSB5b3Ugc29jaWFsaXplIHdpdGggb3V0c2lkZSB3b3JrLiBZb3Uga25vdyB0aGVpciBmYW1pbHksIHRoZXkga25vdyB5b3VycywgZm9yIGluc3RhbmNlLiBJIGRvIG5vdCBtZWFuIGFsbCB0aGUgcGVvcGxlIHlvdSBhcmUgc2ltcGx5IG9uIGEgZnJpZW5kbHkgbGV2ZWwgd2l0aCwgb3IgcGVvcGxlIHlvdSBoYXBwZW4gdG8gbWVldCBhdCBGaXJtIGZ1bmN0aW9ucy4iCgojIyBEYXRhIHByZXBlcmF0aW9uCgojIyMgIExvYWQgdGhlIGRhdGEKCkxldHMgbG9hZCB0aGUgZGF0YSEgVGhlIHRocmVlIG5ldHdvcmtzIHJlZmVyIHRvIGNvd29yaywgZnJpZW5kc2hpcCwgYW5kIGFkdmljZS4gVGhlIGZpcnN0IDM2IHJlc3BvbmRlbnRzIGFyZSB0aGUgcGFydG5lcnMgaW4gdGhlIGZpcm0uIAoKYGBge3J9CiMgTm90ZSB0aGUgLmRhdCBmb3JtYXQgaXMgYSBiaXQgdW5jb25mb3J0YWJsZSB0byBsb2FkIHdpdGggcmVhZHIsIHNpbmNlIHdlIGhhdmUgdG8gc3BlY2lmeSB0aGUgZGVsaW1pdGVycyBvbiBvdXIgb3duLiBUaGVyZWZvcmUgSSB1c2UgdGhlIGNvbnZlbmllbnQgZnJlYWQgZnVuY3Rpb24gYnkgdGhlIGRhdGEudGFibGUgcGFja2FnZSwgd2hpY2ggaXMgYWJsZSB0byBkZXRlY3QgdGhlbSB3aXRob3V0IGhlbHAKbGlicmFyeShkYXRhLnRhYmxlKQptYXRfZnJpZW5kc2hpcCA8LSBmcmVhZCgnaHR0cHM6Ly9zZHMtYWF1LmdpdGh1Yi5pby9TRFMtbWFzdGVyLzAwX2RhdGEvbmV0d29ya19sYXd5ZXJzL0VMZnJpZW5kLmRhdCcpICU+JSBhcy5tYXRyaXgoKQptYXRfYWR2aWNlIDwtIGZyZWFkKCdodHRwczovL3Nkcy1hYXUuZ2l0aHViLmlvL1NEUy1tYXN0ZXIvMDBfZGF0YS9uZXR3b3JrX2xhd3llcnMvRUxhZHYuZGF0JykgJT4lIGFzLm1hdHJpeChkaW1uYW1lcyA9IGxpc3QoYygxOm5yb3coLikpLCBjKDE6bmNvbCguKSkpKQptYXRfd29yayA8LSBmcmVhZCgnaHR0cHM6Ly9zZHMtYWF1LmdpdGh1Yi5pby9TRFMtbWFzdGVyLzAwX2RhdGEvbmV0d29ya19sYXd5ZXJzL0VMd29yay5kYXQnKSAlPiUgYXMubWF0cml4KGRpbW5hbWVzID0gbGlzdChjKDE6bnJvdyguKSksIGMoMTpuY29sKC4pKSkpCgpkaW1uYW1lcyhtYXRfZnJpZW5kc2hpcCkgPSBsaXN0KGMoMTpucm93KG1hdF9mcmllbmRzaGlwKSksIGMoMTpuY29sKG1hdF9mcmllbmRzaGlwKSkpCmRpbW5hbWVzKG1hdF9hZHZpY2UpID0gbGlzdChjKDE6bnJvdyhtYXRfYWR2aWNlKSksIGMoMTpuY29sKG1hdF9hZHZpY2UpKSkKZGltbmFtZXMobWF0X3dvcmspID0gbGlzdChjKDE6bnJvdyhtYXRfd29yaykpLCBjKDE6bmNvbChtYXRfd29yaykpKQojIE5vdGUgdGhhdCB3ZSBoYXZlIHRvIG92ZXJ3cml0ZSB0aGUgY29sdW1uIGFuZCByb3duYW1lcyBvZiB0aGUgbWF0cmljZXMgd2l0aCAxOjcxIChjb3JyZXNwb25kaW5nIHRvIHRoZSBuYW1lIGNvZGVzIGluIHRoZSBub2Rlc2xpc3QpCmBgYAoKV2UgYWxzbyBsb2FkIGEgc2V0IG9mIG5vZGVzCgpgYGB7cn0Kbm9kZXMgPC0gZnJlYWQoJ2h0dHBzOi8vc2RzLWFhdS5naXRodWIuaW8vU0RTLW1hc3Rlci8wMF9kYXRhL25ldHdvcmtfbGF3eWVycy9FTGF0dHIuZGF0JykgJT4lIGFzX3RpYmJsZSgpCmBgYAoKYGBge3J9Cm5vZGVzICU+JSBoZWFkKCkKYGBgCgojIyMgQ2xlYW5pbmcgdXAKClRoZSB2YXJpYWJsZXMgaW4gYG5vZGVzYCBhcmUgdW5uYW1lZCwgYnV0IGZyb20gdGhlIHBhcGVyIEkga25vdyBob3cgdGhleSBhcmUgY29kZWQsIHNvIHdlIGNhbiBnaXZlIHRoZW0gbmFtZXMuCgpgYGB7cn0KY29sbmFtZXMobm9kZXMpIDwtIGMoIm5hbWUiLCAic2VuaW9yaXR5IiwgImdlbmRlciIsICJvZmZpY2UiLCAidGVudXJlIiwgImFnZSIsICJwcmFjdGljZSIsICJzY2hvb2wiKQpgYGAKCldlIGNhbiBhbHNvIHJlY29kZSB0aGUgbnVtZXJpYyBjb2RlcyBpbiB0aGUgZGF0YSBpbnRvIHNvbWV0aGluZyBtb3JlIGludHVpdGl2ZS4gSSBhZ2Fpb24ga25vdyBmcm9tIHRoZSBkYXRhIGRlc2NyaXB0aW9uIG9mIHRoZSBwYXBlciB0aGUgY29kaW5nLgoKKiBzZW5pb3JpdHkgc3RhdHVzICgxPXBhcnRuZXI7IDI9YXNzb2NpYXRlKQoqIGdlbmRlciAoMT1tYW47IDI9d29tYW4pCiogb2ZmaWNlICgxPUJvc3RvbjsgMj1IYXJ0Zm9yZDsgMz1Qcm92aWRlbmNlKQoqIHllYXJzIHdpdGggdGhlIGZpcm0KKiBhZ2UKKiBwcmFjdGljZSAoMT1saXRpZ2F0aW9uOyAyPWNvcnBvcmF0ZSkKKiBsYXcgc2Nob29sICgxOiBoYXJ2YXJkLCB5YWxlOyAyOiB1Y29uOyAzOiBvdGhlcikKCmBgYHtyfQpub2RlcyAlPD4lCiAgbXV0YXRlKG5hbWUgPSBuYW1lICU+JSBhcy5udW1lcmljKCksCiAgICAgICAgIHNlbmlvcml0eSA9IHJlY29kZShzZW5pb3JpdHksICIxIiA9ICJQYXJ0bmVyIiwgIjIiID0gIkFzc29jaWF0ZSIpLAogICAgICAgICBnZW5kZXIgPSByZWNvZGUoZ2VuZGVyLCAiMSIgPSAiTWFuIiwgIjIiID0gIldvbWFuIiksCiAgICAgICAgIG9mZmljZSA9IHJlY29kZShvZmZpY2UsICIxIiA9ICJCb3N0b24iLCAiMiIgPSAiSGFydGZvcmQiLCAiMyIgPSAiUHJvdmlkZW5jZSIpLAogICAgICAgICBwcmFjdGljZSA9IHJlY29kZShwcmFjdGljZSwgIjEiID0gIkxpdGlnYXRpb24iLCAiMiIgPSAiQ29ycG9yYXRlIiksCiAgICAgICAgIHNjaG9vbCA9IHJlY29kZShzY2hvb2wsICIxIiA9ICJIYXJ2YXJkLCBZYWxlIiwgIjIiID0gIlVjb24iLCAiMyIgPSAiT3RoZXJzIikpICAgCmBgYAoKYGBge3J9Cm5vZGVzICU+JSBoZWFkKCkKYGBgCgojIyMgR2VuZXJhdGUgdGhlIGdyYXBoCgoqIFNpbmNlIHdlIGhhdmUgbm93IGEgKiptdWx0aWRpbWVuc2lvbmFsKiogbmV0d29yayAoPWRpZmZlcmVudCB0eXBlcyBvZiBlZGdlcyksIHdlIGZpcnN0IGxvYWQgdGhlbSBpbnRvIGlzb2xhdGVkIG5ldHdvcmtzLgoqIFdlIGNvdWxkIGFsc28gZGlyZWN0bHkgbG9hZCB0aGVtIGludG8gb25lIG5ldHdvcmsgd2l0aCBsYWJlbGVkIGVkZ2VzLCBidXQgdGhhdCdzIGEgYml0IG1vcmUgY29tcGxpY2F0ZWQsIHNvIHdlIGtlZXAgaXQgZm9yIHRoZSBzYWtlIG9mIGNsYXJpdHkgc2VwZXJhdGVkIGZvciBub3cuCgpgYGB7cn0KZ19mcmllbmRzaGlwIDwtIG1hdF9mcmllbmRzaGlwICU+JSBhc190YmxfZ3JhcGgoZGlyZWN0ZWQgPSBUUlVFKSAlRT4lCiAgbXV0YXRlKHR5cGUgPSAiZnJpZW5kc2hpcCIpICVOPiUKICBtdXRhdGUobmFtZSA9IG5hbWUgJT4lIGFzLm51bWVyaWMoKSkgJT4lCiAgbGVmdF9qb2luKG5vZGVzLCBieSA9ICJuYW1lIikKCmdfYWR2aWNlIDwtIG1hdF9hZHZpY2UgJT4lIGFzX3RibF9ncmFwaChkaXJlY3RlZCA9IFRSVUUpICVFPiUKICBtdXRhdGUodHlwZSA9ICJhZHZpY2UiKSAlTj4lCiAgbXV0YXRlKG5hbWUgPSBuYW1lICU+JSBhcy5udW1lcmljKCkpICU+JQogIGxlZnRfam9pbihub2RlcywgYnkgPSAibmFtZSIpCgpnX3dvcmsgPC0gbWF0X3dvcmsgJT4lIGFzX3RibF9ncmFwaChkaXJlY3RlZCA9IFRSVUUpICVFPiUKICBtdXRhdGUodHlwZSA9ICJ3b3JrIikgJU4+JQogIG11dGF0ZShuYW1lID0gbmFtZSAlPiUgYXMubnVtZXJpYygpKSAlPiUKICBsZWZ0X2pvaW4obm9kZXMsIGJ5ID0gIm5hbWUiKQoKIyBOb3RpY2U6IFRoZSBub2RlIG5hbWVzIGFyZSB0YWtlbiBmcm9tIHRoZSBtYXRyaWNlcyBkaW1uYW1lcyBhcyBzdHJpbmcsIHRoZXJlZm9yZSBuZWVkIHRvIGJlIGNvbnZlcnRlZCBhcyBudW1lcmljCmBgYAoKIyMjIEZpcnN0IGluc3BlY3Rpb24KCmBgYHtyfQojIFdlIGNvdWxkIGFsc28gam9pbiBhbGwgdGhlIG5ldHdvcmtzIHRvZ2V0aGVyLgpnX2FsbCA8LSBnX2ZyaWVuZHNoaXAgJT4lCiAgZ3JhcGhfam9pbihnX2FkdmljZSwgYnkgPSAibmFtZSIpICU+JQogIGdyYXBoX2pvaW4oZ193b3JrLCBieSA9ICJuYW1lIikKYGBgCgpgYGB7cn0KZ19hbGwgJUU+JQogIGFzX3RpYmJsZSgpICU+JQogIGhlYWQoKQpgYGAKCmBgYHtyLCBmaWcuaGVpZ2h0PSA3LjUsIGZpZy53aWR0aD0xMi41fQojIFRoZW4gd2UgY291bGQgcGxvdCB0aGVtIHBvaW50bHkgdmlhIGFuIGVkZ2UgZmFjZXR0Li4uCmdfYWxsICU+JSAKICBnZ3JhcGgobGF5b3V0ID0gJ2ZyJykgKyAKICBnZW9tX2VkZ2VfZmFuKGFlcyhjb2wgPSB0eXBlKSwgCiAgICAgICAgICAgICAgICBhcnJvdyA9IGFycm93KGFuZ2xlID0gMzAsIGxlbmd0aCA9IHVuaXQoMC4yNSwgJ2NtJyksdHlwZSA9ICJjbG9zZWQiKSwgCiAgICAgICAgICAgICAgICBhbHBoYSA9IDAuMjUpICsgCiAgZ2VvbV9ub2RlX3BvaW50KGNvbCA9ICdwdXJwbGUnKSArCiAgZ2VvbV9ub2RlX3RleHQoYWVzKGxhYmVsID0gbmFtZSkpICsgCiAgdGhlbWVfZ3JhcGgoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgZmFjZXRfZWRnZXMofnR5cGUpIApgYGAKClRoaXMgaXMgY29udmVuaWVudCwgeWV0IHNvbWV3aGF0IG9mIGEgY29tcHJvbWlzZSwgc2luY2UgdGhlIGxheW91dCBpcyBvcHRpbWl6ZWQgb24gdGhlIGZ1bGwgbmV0d29yayBvZiBhbGwgZWRnZXMuIFNvIGl0IGtpbmQgb2YgZml0cyB0byBhbGwsIGJ1dCBub3QgZnVsbHkgdG8gb25lLi4uCgojIyBOZXR3b3JrIGVmZmVjdHMgJiBzdHJ1Y3R1cmVzCgpmb3IgdGhlIGZvbGxvd2luZywgd2Ugd2lsbCBvbmx5IGxvb2sgYXQgdGhlIGZyaWVuZHNoaXAgbmV0d29yaywgd2hpbGUgaSBsZWF2ZSB0aGUgYW5hbHlzaXMgb2YgdGhlIG90aGVyJ3MgdXAgdG8geW91LgoKYGBge3J9CmcgPC0gZ19mcmllbmRzaGlwCmBgYAoKTGV0cyB0YWtlIGEgbG9vawoKYGBge3IsIGZpZy5oZWlnaHQ9IDcuNSwgZmlnLndpZHRoPTEyLjV9CnNldC5zZWVkKDEzMzcpCiMgVGhlbiB3ZSBjb3VsZCBwbG90IHRoZW0gcG9pbnRseSB2aWEgYW4gZWRnZSBmYWNldHQuLi4KZyAlTj4lIAogIGZpbHRlcighbm9kZV9pc19pc29sYXRlZCgpKSAlPiUKICBnZ3JhcGgobGF5b3V0ID0gJ3N0cmVzcycpICsgCiAgZ2VvbV9lZGdlX2ZhbihhcnJvdyA9IGFycm93KGFuZ2xlID0gMzAsIGxlbmd0aCA9IHVuaXQoMC4yNSwgJ2NtJyksdHlwZSA9ICdjbG9zZWQnKSwgYWxwaGEgPSAwLjI1KSArIAogIGdlb21fbm9kZV9wb2ludChhZXMoY29sID0gb2ZmaWNlLCBzaXplID0gY2VudHJhbGl0eV9laWdlbihkaXJlY3RlZCA9IFRSVUUpKSkgKwogIGdlb21fbm9kZV90ZXh0KGFlcyhsYWJlbCA9IG5hbWUsIHNpemUgPSBjZW50cmFsaXR5X2VpZ2VuKGRpcmVjdGVkID0gVFJVRSkpKSArIAogIHRoZW1lX2dyYXBoKCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSArCiAgZmFjZXRfZWRnZXMofnR5cGUpIApgYGAKCiMjIyBOb2RlIGxldmVsIChsb2NhbCkKCiogV2UgY291bGQgbG9vayBhdCBhbGwgdGhlIG5vZGUgbGV2ZWwgY2hhcmFjdGVyaXN0aWNzIChkZWdyZWUsIGJldHdlZW5uZXNzIGV0Yy4pIGFnYWluLCBidXQgZm9yIHRoZSBzYWtlIG9mIHRpbWUgSSBza2lwIHRoYXQgZm9yIG5vdywgc2luY2UgaXRzIGFsbCBhbHJlYWR5IGluIHRoZSBsYXN0IG5vdGVib29rLgoKIyMjIE5ldHdvcmsgbGV2ZWwgKGdsb2JhbCkKCmBgYHtyfQpsaWJyYXJ5KGlncmFwaCkKYGBgCgoKKiBPaywgbGV0cyBkbyB0aGUgd2hvbGUgZXhlcmNpc2Ugd2l0aCBnZXR0aW5nIHRoZSBtYWluLWRldGVybWluYW50cyBvZiB0aGUgbmV0d29yayBzdHJ1Y3R1cmUgYWdhaW4uIFdlIGNhbiBsb29rIGF0IHRoZSBjbGFzc2ljYWwgc3RydWN0dXJhbCBkZXRlcm1pbmFudHMuCgpgYGB7cn0KIyBHZXQgZGVuc2l0eSBvZiBhIGdyYXBoCmVkZ2VfZGVuc2l0eShnKQpgYGAKCmBgYHtyfQojIEdldCB0aGUgZGlhbWV0ZXIgb2YgdGhlIGdyYXBoIGcKZGlhbWV0ZXIoZywgZGlyZWN0ZWQgPSBUUlVFKQpgYGAKCmBgYHtyfQojIEdldCB0aGUgYXZlcmFnZSBwYXRoIGxlbmd0aCBvZiB0aGUgZ3JhcGggZwptZWFuX2Rpc3RhbmNlKGcsIGRpcmVjdGVkID0gVFJVRSkKYGBgCgpgYGB7cn0KIyBUcmFuc2lzdGl2aXR5CnRyYW5zaXRpdml0eShnLCB0eXBlID0iZ2xvYmFsIikKYGBgCgojIyMgTmV0d29yayBsZXZlbCAoZ2xvYmFsIGRpcmVjZWQpCgoqIFNpbmNlIHdlIGhlcmUgaGF2ZSBhIGRpcmVjdGVkIG5ldHdvcmssIGEgY291cGxlIG9mIGludGVyZXN0aW5nIGFkZGl0aW9uYWwgbWV0cmljcyBhcmUgYXZhaWxhYmxlLCBleHBsaWNpdGx5IHRha2luZyBpbnRvIGFjY291bnQgdGggZGlyZWN0aW9uIG9mIGVkZ2VzLgoqIFdoaWxlIHRoZXJlIGFyZSBtYW55IG1vcmUsIHdlIGhlcmUgd2lsbCBqdXN0IHRha2UgYSBsb29rIGF0IHNvbWUgb2YgdGhlIG1vc3QgaW1wb3J0YW50IG9uZXMsIHdoaWNoIGFyZSBhbHNvIGtub3duIHRvIGJlIHBvcHVsYXIgbWVjaGFuaXNtcyBpbiBzb2NpYWwgbmV0d29ya3MuCgohW10oaHR0cHM6Ly9zZHMtYWF1LmdpdGh1Yi5pby9TRFMtbWFzdGVyLzAwX21lZGlhL25ldHdvcmtzX2RpcmVjdGVkX21ldHJpY3MucG5nKXt3aWR0aD01MDBweH0KCiogUmVjaXByb2NpdHkgbWVhc3VyZXMgdGhlIGV4dGVuZCB0byB3aGljaCBlZGdlcyBhcmUgcmVjaXByb2NhbCwgbWVhbmluZyBhIGVkZ2UgYmV0d2VlbiBpICYgaiBpbXBsaWVzIGFsc28gYW4gZWRnZSBiZXR3ZWVuIGogJiBpCgpgYGB7cn0KIyByZWNpcHJvY2l0eQpyZWNpcHJvY2l0eShnKQpgYGAKCiogV2UgaGF2ZSBhbm90aGVyIGltcG9ydGFudCBjb25jZXB0IHRoYXQgb2Z0ZW4gZXhwbGFpbnMgZWRnZS1mb3JtYXRpb24gaW4gZGlyZWN0ZWQgKHNvY2lhbCkgbmV0d29ya3M6ICoqQXNzb3J0YXRpdml0eSoqLCBhbHNvIGNhbGxlZCAqKmhvbW9waG9seSoqLiAKKiBUaGlzIGlzIGEgbWVhc3VyZSBvZiBob3cgcHJlZmVyZW50aWFsbHkgYXR0YWNoZWQgdmVydGljZXMgYXJlIHRvIG90aGVyIHZlcnRpY2VzIHdpdGggaWRlbnRpY2FsIGF0dHJpYnV0ZXMuIEluIG90aGVyIHdvcmRzOiBIb3cgbXVjaCAiKmJpcmRzIG9mIHRoZSBzYW1lIGZlYXRoZXIgZmxvY2sgdG9nZXRoZXIgKiIuCiogTGV0cyBmaXJzdCBsb29rIGF0IHBlb3BsZSBvZiB0aGUgc2FtZSB0ZW51cmUgZmxvY2sgdG9nZXRoZXIuCgpgYGB7cn0KYXNzb3J0YXRpdml0eShnLCBWKGcpJHRlbnVyZSwgZGlyZWN0ZWQgPSBUUlVFKQpgYGAKCiogV2hhdCBhYm91dCBwZW9wbGUgZnJvbSBlbGl0ZSB1bml2ZXJzaXRpZXM/CgpgYGB7cn0KYXNzb3J0YXRpdml0eShnLCBWKGcpJHNjaG9vbCA9PSAiSGFydmFyZCwgWWFsZSIsIGRpcmVjdGVkID0gVFJVRSkKYGBgCgoqIExhc3RseSwgd2hhdCBhYm91dCB0aGUgcG9wdWxhcml0eSAob3IgIk1hdHRoZXciKSBlZmZlY3Q/CgpgYGB7cn0KYXNzb3J0YXRpdml0eShnLCBkZWdyZWUoZywgbW9kZSA9ICJpbiIpLCBkaXJlY3RlZCA9IFRSVUUpCmBgYAoKKiBPbmUgbW9yZSB0aGluZyB3ZSBkaWRuJ3QgdGFsayBhYm91dCB5ZXQ6IFNtYWxsIHdvcmxkcy4KKiBTbWFsbCB3b3JsZHMgYXJlIGFuIGludGVyZXN0aW5nIG5ldHdvcmsgc3RydWN0dXJlLCBjb21iaW5pbmcgc2hvcnQgcGF0aCBsZW5naHQgYmV0d2VuIHRoZSBub2RlcyB3aXRoIGEgaGlnaCBjbHVzdGVyaW5nIGNvZWZmaWNpZW50LiAKKiBUaGF0IG1lYW5zLCB0aGF0IHdlIGhhdmUgc21hbGwgaW50ZXJjb25uZWN0ZWQgY2x1c3RlcnMsIHdoaWNoIGFyZSBpbiB0dXJuIGNvbm5lY3RlZCBieSAqKmdhdGVrZWVwZXJzKiogKHRoZSBlZGdlcyB3ZSBjYWxsICoqYnJpZGdlcyoqIG9yICoqc3RydWN0dXJhbCBob2xlcyoqKS4gCgohW10oaHR0cHM6Ly9zZHMtYWF1LmdpdGh1Yi5pby9TRFMtbWFzdGVyLzAwX21lZGlhL25ldHdvcmtzX3NtYWxsd29ybGQyLmpwZyl7d2lkdGg9NTAwcHh9CgpUaGlzIGxlYWRzIHRvIGFuIGludGVyZXN0aW5nIHNldHVwLCB3aGljaCBoYXMgcHJvdmVuIHRvIGJlIGNvbmR1Y3RpdmUgZm9yIGVmZmljaWVudCBjb21tdW5pY2F0aW9uIGFuZCBmYXN0IGRpZmZ1c2lvbiBvZiBpbmZvcm1hdGlvbiBpbiBzb2NpYWwgbmV0d29ya3MuCgohW10oaHR0cHM6Ly9zZHMtYWF1LmdpdGh1Yi5pby9TRFMtbWFzdGVyLzAwX21lZGlhL25ldHdvcmtzX3NtYWxsd29ybGQxLmpwZyl7d2lkdGg9NTAwcHh9CgpXZSBjYWxjdWxhdGUgaXQgZm9yIG5vdyBpbiBhbiBlYXN5IHdheToKCmBgYHtyfQp0cmFuc2l0aXZpdHkoZywgdHlwZSA9Imdsb2JhbCIpIC8gbWVhbl9kaXN0YW5jZShnLCBkaXJlY3RlZCA9IFRSVUUpCmBgYAoKSG93ZXZlciwgeW91IGJ5IG5vdyBwcm9iYWJseSB3b25kZXIgaG93IHRvIGludGVycHJldGUgdGhpcyBudW1iZXJzLiBBcmUgdGhleSBoaWdoLCBsb3csIG9yIHdoYXRldmVyPyBXaGF0IGlzIHRoZSByZWZlcmVuY2U/IEluIGZhY3QsIGl0J3MgdmVyeSBoYXJkIHRvIHNheS4gVGhlIGJlc3Qgd2F5IHRvIHNheSBzb21ldGhpbmcgYWJvdXQgdGhhdCBpcyB0byBjb21wYXJlIGl0IHdpdGggd2hhdCBhIHJhbmRvbSBuZXR3b3JrIHdvdWxkIGxvb2sgbGlrZS4KClNvLCBsZXRzIGNyZWF0ZSBhIHJhbmRvbSBuZXR3b3JrLiBIZXJlLCB3ZSB1c2UgdGhlIGBwbGF5X2VyZG9zX3JlbnlpKClgIGZ1bmN0aW9uLCB3aGljaCBjcmVhdGVzIGEgbmV0d29yayB3aXRoIGEgZ2l2ZW4gbnVtYmVyIG9mIG5vZGVzIGFuZCBlZGdlLWRlbnNpdHksIGJ1dCB3aGVyZSB0aGUgZWRnZXMgYXJlIGNvbnN0cnVjdGVkIGNvbXBsZXRlbHkgcmFuZG9tLgoKYGBge3J9CmdfciA8LSBwbGF5X2VyZG9zX3JlbnlpKG4gPSBnICU+JSBnb3JkZXIoKSwgIAogICAgICAgICAgICAgICAgICAgICAgICBtICA9IGcgJT4lIGdzaXplKCksIAogICAgICAgICAgICAgICAgICAgICAgICBkaXJlY3RlZCA9IFRSVUUsIAogICAgICAgICAgICAgICAgICAgICAgICBsb29wcyA9IEZBTFNFKQpgYGAKCkxvb2tzIGtpbmQgb2YgZGlmZmVyZW50LiBIb3dldmVyLCBvbmUgcmFuZG9tbHkgY3JlYXRlZCBuZXR3b3JrIGRvZXNuJ3QgcHJlc2VudCBhIGdvb2QgYmFzZWxpbmUuIFNvLCBsZXRzIGJldHRlciBjcmVhdGUgYSBidW5jaCwgYW5kIGNvbXBhcmUgb3VyIG5ldHdvcmsgdG8gdGhlIGF2ZXJhZ2UgdmFsdWVzIG9mIHRoZSByYW5kb21seSBnZW5lcmF0ZWQgb25lcy4KCmBgYHtyfQojIEdlbmVyYXRlIG4gcmFuZG9tIGdyYXBocwpuID0gMTAwMApnX2wgPC0gdmVjdG9yKCdsaXN0JywgbikKICAKZm9yKGkgaW4gMTpuKXsKICBnX2xbW2ldXSA8LSBwbGF5X2VyZG9zX3JlbnlpKG4gPSBnICU+JSBnb3JkZXIoKSwgIAogICAgICAgICAgICAgICAgICAgICAgICBtICA9IGcgJT4lIGdzaXplKCksIAogICAgICAgICAgICAgICAgICAgICAgICBkaXJlY3RlZCA9IFRSVUUsIAogICAgICAgICAgICAgICAgICAgICAgICBsb29wcyA9IEZBTFNFKQp9CmBgYAoKCk5vdyB3ZSBjYW4gc2VlIGhvdyBtZWFuaW5nZnVsIG91ciBvYnNlcnZlZCBuZXR3b3JrIHN0YXRpc3RpY3MgYXJlLCBieSBjb21wYXJpbmcgdGhlbSB3aXRoIHRoZSBtZWFuIG9mIHRoZSBzdGF0aXN0aWNzIGluIHRoZSByYW5kb20gbmV0d29yay4KCmBgYHtyfQojIENhbGN1bGF0ZSBhdmVyYWdlIHBhdGggbGVuZ3RoIG9mIDEwMDAgcmFuZG9tIGdyYXBocwpkaXN0X3IgPC0gZ19sICU+JSBsYXBwbHkobWVhbl9kaXN0YW5jZSwgZGlyZWN0ZWQgPSBUUlVFKSAlPiUgdW5saXN0KCkgIyU+JSBtZWFuKCkKY2NfciA8LSBnX2wgJT4lIGxhcHBseSh0cmFuc2l0aXZpdHksIHR5cGUgPSAiZ2xvYmFsIikgJT4lIHVubGlzdCgpICMlPiUgbWVhbigpCnJwX3IgPC0gZ19sICU+JSBsYXBwbHkocmVjaXByb2NpdHkpICU+JSB1bmxpc3QoKSAjJT4lIG1lYW4oKQpgYGAKCkxldHMgc2VlOgoKYGBge3J9CnN0YXRzX2ZyaWVuZCA8LSB0aWJibGUoZGVuc2l0eSA9IGcgJT4lIGVkZ2VfZGVuc2l0eSgpLAogICAgICAgICAgICAgICAgICAgICAgIGRpYW1ldGVyID0gZyAlPiUgZGlhbWV0ZXIoZGlyZWN0ZWQgPSBUUlVFKSwKICAgICAgICAgICAgICAgICAgICAgICByZWNpcHJvY2l0eSA9IGcgJT4lIHJlY2lwcm9jaXR5KCksCiAgICAgICAgICAgICAgICAgICAgICAgcmVjaXByb2NpdHlfc2NvcmUgPSBtZWFuKHJlY2lwcm9jaXR5KGcpID4gcnBfciksCiAgICAgICAgICAgICAgICAgICAgICAgZGlzdGFuY2UgPSBnICU+JSBtZWFuX2Rpc3RhbmNlKGRpcmVjdGVkID0gVFJVRSksCiAgICAgICAgICAgICAgICAgICAgICAgZGlzdGFuY2Vfc2NvcmUgPSBtZWFuKG1lYW5fZGlzdGFuY2UoZywgZGlyZWN0ZWQgPSBUUlVFKSA+IGRpc3RfciksCiAgICAgICAgICAgICAgICAgICAgICAgY2x1c3RlcmluZyA9IGcgJT4lIHRyYW5zaXRpdml0eSh0eXBlID0gImdsb2JhbCIpLAogICAgICAgICAgICAgICAgICAgICAgIGNsdXN0ZXJpbmdfc2NvcmUgPSBtZWFuKHRyYW5zaXRpdml0eShnLCB0eXBlID0gImdsb2JhbCIpICA+IGNjX3IpLAogICAgICAgICAgICAgICAgICAgICAgIHNtYWxsX3dvcmxkID0gbWVhbih0cmFuc2l0aXZpdHkoZywgdHlwZSA9ICJnbG9iYWwiKSAgPiBjY19yKSAvIG1lYW4obWVhbl9kaXN0YW5jZShnLCBkaXJlY3RlZCA9IFRSVUUpID4gZGlzdF9yKSApCgpgYGAKCmBgYHtyfQpzdGF0c19mcmllbmQKYGBgCgojIyBZb3VyIHR1cm4KUGxlYXNlIGRvICoqRXhlcmNpc2UgMSoqIGluIHRoZSBjb3JyZXNwb25kaW5nIHNlY3Rpb24gb24gYEdpdGh1YmAuCgojIEVuZG5vdGVzCgojIyMgU3VnZ2VzdGlvbnMgZm9yIGZ1cnRoZXIgc3R1ZHkKCiMjIyMgTGl0ZXJhdHVyZQoKQ2xhc3NpY3MKCiogV2Fzc2VybWFuLCBTLiwgJiBGYXVzdCwgSy4gKDE5OTQpLiBTb2NpYWwgbmV0d29yayBhbmFseXNpczogTWV0aG9kcyBhbmQgYXBwbGljYXRpb25zIChWb2wuIDgpLiBDYW1icmlkZ2UgdW5pdmVyc2l0eSBwcmVzcy4KKiBDYXJyaW5ndG9uLCBQLiBKLiwgU2NvdHQsIEouLCAmIFdhc3Nlcm1hbiwgUy4gKEVkcy4pLiAoMjAwNSkuIE1vZGVscyBhbmQgbWV0aG9kcyBpbiBzb2NpYWwgbmV0d29yayBhbmFseXNpcyAoVm9sLiAyOCkuIENhbWJyaWRnZSB1bml2ZXJzaXR5IHByZXMKCk93biB3b3JrIG9uIGRpcmVjdGVkIG5ldHdvcmtzCgoqIEhhaW4sIEQuLCBCdWNobWFubiwgVC4sIEt1ZGljLCBNLiwgJiBNw7xsbGVyLCBNLiAoMjAxOCkuIEVuZG9nZW5vdXMgZHluYW1pY3Mgb2YgaW5ub3ZhdGlvbiBuZXR3b3JrcyBpbiB0aGUgR2VybWFuIGF1dG9tb3RpdmUgaW5kdXN0cnk6IGFuYWx5c2luZyBzdHJ1Y3R1cmFsIG5ldHdvcmsgZXZvbHV0aW9uIHVzaW5nIGEgc3RvY2hhc3RpYyBhY3Rvci1vcmllbnRlZCBhcHByb2FjaC4gSW50ZXJuYXRpb25hbCBKb3VybmFsIG9mIENvbXB1dGF0aW9uYWwgRWNvbm9taWNzIGFuZCBFY29ub21ldHJpY3MsIDgoMy00KSwgMzI1LTM0NC4KKiBIYWluLCBEYW5pZWwgUy4sIGFuZCBSb21hbiBKdXJvd2V0emtpLiAiSW5jcmVtZW50YWwgYnkgRGVzaWduPyBPbiB0aGUgUm9sZSBvZiBJbmN1bWJlbnRzIGluIFRlY2hub2xvZ3kgTmljaGVzLiIgRm91bmRhdGlvbnMgb2YgRWNvbm9taWMgQ2hhbmdlLiBTcHJpbmdlciwgQ2hhbSwgMjAxNy4gMjk5LTMzMi4KCiAgCiMjIyBTZXNzaW9uIEluZm8KCmBgYHtyfQpzZXNzaW9uSW5mbygpCmBgYAoKCgoKCg==