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

This session

In this applied session, you will:

Network data structures

  • Below an example ofa minimal edge list created with the tibble() function.
  • In this case, let us assume this network to be unweighted, meaning a connection can be eiter tresent or absent.
edge_list <- tibble(from = c(1, 2, 2, 1, 4, 3, 5), 
                    to = c(2, 3, 4, 5, 1, 2, 5))
edge_list

Adjacency Matrix

  • A second popular form of network representation is the adjacency-matrix (also called socio-matrix).
  • It is represented as a \(n*n\) matrix, where \(n\) stands for the number of elements of which their relationships should be represented.
  • The value in the cell that intercepts row \(n\) and column \(m\) indicates if an edge is present (=1) or absent (=0).
  • Tip: Given an edgelist, an adjacency matrix can easily be produced by crosstabulating:
adj_matrix <- edge_list %>%
  table() %>% 
  as.matrix()
adj_matrix
    to
from 1 2 3 4 5
   1 0 1 0 0 1
   2 0 0 1 1 0
   3 0 1 0 0 0
   4 1 0 0 0 0
   5 0 0 0 0 1

Note:

  • Existing as well as not existing connections are stored. Since most networks in reality are sparse (= more potential connections are inactive than active)
  • This is inefficient for storrage and computation.
  • Here, a sparse dgCMatrix object from the Matrixcan be helpful.
  • This sparse datasructure only stores a reference to non-empty cells and their values.
library(Matrix)
sparse_matrix <- edge_list %>%
  table() %>% 
  Matrix(sparse = TRUE)
sparse_matrix
5 x 5 sparse Matrix of class "dgCMatrix"
    to
from 1 2 3 4 5
   1 . 1 . . 1
   2 . . 1 1 .
   3 . 1 . . .
   4 1 . . . .
   5 . . . . 1
sparse_matrix %>% glimpse()
Formal class 'dgCMatrix' [package "Matrix"] with 6 slots
  ..@ i       : int [1:7] 3 0 2 1 1 0 4
  ..@ p       : int [1:6] 0 1 3 4 5 7
  ..@ Dim     : int [1:2] 5 5
  ..@ Dimnames:List of 2
  .. ..$ from: chr [1:5] "1" "2" "3" "4" ...
  .. ..$ to  : chr [1:5] "1" "2" "3" "4" ...
  ..@ x       : num [1:7] 1 1 1 1 1 1 1
  ..@ factors : list()

Nodelists

  • Edgelists as well as adjacency matrices only stores connectivity pattern between nodes, but due to their structure cannot store informations on the nodes in which we might be interested.
  • Therefore, we in many cases also provide a a node list with these informations (such as the names of the nodes or any kind of groupings).
node_list <- tibble(id = 1:5, 
                    name = c("Jesper", "Pernille", "Jacob", "Dorte", "Donald"),
                    gender = c("M", "F", "M", "F", "M"),
                    group = c("A", "B", "B", "A", "C"))
node_list

Graph objects

Graph objects in igraph

  • One of the most popular network/graph analytics framework in R and Python alike is igraph. * It provides a powerful toolbox for analysis as well as plotting alike. Lets take a peak.
  • To create an igraph object from an edge-list data frame we can use the graph_from_data_frame() function.
  • There are three arguments in the graph_from_data_frame() function: d, vertices, and directed.
    • d refers to the edge list,
    • vertices to the node list, *directed can be either TRUE or FALSE depending on whether the data is directed or undirected.
  • By default, graph.data.frame() treats the first two columns of the edge list and any remaining columns as edge attributes.
library(igraph)
g <- graph_from_data_frame(d = edge_list, vertices = node_list, directed = FALSE)
# g <- graph_from_adjacency_matrix(adj_matrix, mode = "undirected") # Same for the adjacency matrix
g
IGRAPH 0e6ab66 UN-- 5 7 -- 
+ attr: name (v/c), gender (v/c), group (v/c)
+ edges from 0e6ab66 (vertex names):
[1] Jesper  --Pernille Pernille--Jacob    Pernille--Dorte    Jesper  --Donald   Jesper  --Dorte   
[6] Pernille--Jacob    Donald  --Donald  

Lets inspect the resulting object. An igraph graph object summary reveals some interesting informations.

  • First, it tells us the graph-type: undirected UN, or directed DN
  • Afterwards, the number of nodes (4), and edges (5)
  • Followed by the node attributes (node level variables), which in this case are only their name, gender, and group (attr: name (v/c), gender (v/c), group (v/c))
  • Lastly, a list of all existing edges. Note: n--m indicates an undirected, n->m an directed edge.

Lets take a look at the structure of the object:

g[[1:2]]%>% glimpse() # Note the double brackets (g is a list object)
List of 2
 $ Jesper  : 'igraph.vs' Named int [1:3] 2 4 5
  ..- attr(*, "names")= chr [1:3] "Pernille" "Dorte" "Donald"
  ..- attr(*, "env")=<weakref> 
  ..- attr(*, "graph")= chr "0e6ab66c-8b3d-4871-8dac-6fa47bff7829"
 $ Pernille: 'igraph.vs' Named int [1:4] 1 3 3 4
  ..- attr(*, "names")= chr [1:4] "Jesper" "Jacob" "Jacob" "Dorte"
  ..- attr(*, "env")=<weakref> 
  ..- attr(*, "graph")= chr "0e6ab66c-8b3d-4871-8dac-6fa47bff7829"
  • We see, the object has a list-format, consisting of sepperate lists for every node, containing some attributes which are irrelevant now, and an edgelist for every node, capturing its ego-network (eg., ..$ Jesper: 'igraph.vs' Named int [1:3] 2 4 5)
  • We can also plot it to take a look. igraph object can be directly used with the plot() function.
  • The results can be adjusted with a set of parameters we will discover later.
  • It’s not super pretty, therefore we will later also explore more powerfull plotting tools for gaphs. However, its quick&dirty, so lets take it like that for now.
plot(g)

Note: We will not venture further into the igraph plotting functionality, since we will go all in with ggraph. However, there is a very neath tutorial here, that will tell you everything you need to know, in case you are interested.

We can inspect and manipulate the nodes via V(g) (V for vertices, its graph-theory slang), and edges with E(g)

V(g)
+ 5/5 vertices, named, from 0e6ab66:
[1] Jesper   Pernille Jacob    Dorte    Donald  
E(g)
+ 7/7 edges from 0e6ab66 (vertex names):
[1] Jesper  --Pernille Pernille--Jacob    Pernille--Dorte    Jesper  --Donald   Jesper  --Dorte   
[6] Pernille--Jacob    Donald  --Donald  

We can also use most of the base-R slicing&dicing.

V(g)[1:3]
+ 3/5 vertices, named, from 0e6ab66:
[1] Jesper   Pernille Jacob   
E(g)[2:4]
+ 3/7 edges from 0e6ab66 (vertex names):
[1] Pernille--Jacob  Pernille--Dorte  Jesper  --Donald

Remember, it’s a list-object. So, if we just want to have the values, we have to use the double bracket [[x]].

V(g)[[1:3]]
+ 3/5 vertices, named, from 0e6ab66:

We can also use the $ notation.

V(g)$name
[1] "Jesper"   "Pernille" "Jacob"    "Dorte"    "Donald"  

There is obviously a lot more to say about igraph and its rich functionality. You will learn much of the abse functionality of igraph in your DC assignments. Furthermore Katya Ognyanova, has a brilliant tutorial that can be studied.

Graph objects in tidygraph

  • While the igraph functionality still represents the core of R’s network analysis toolbox, recent developments have made network analytics much more accessible and intuitive.
  • Thomas Lin Pedersen (also known as the developer of beloved packages like ggforce, gganimate, and the R implementation of lime) has recently released the tidygraph package.
  • It leverage the power of igraph in a manner consistent with the tidyverse workflow.
  • It represents a lightweight wrapper around the core igraph object and functionality which makes it accessible for much of the traditional dplyr workflows.
  • Even better, he tops it up with ggraph, a consistent ggplot2-look-and-feel network visualization package.
  • For that reason, we will mostly work with the tidygraph framework, while we still in some few cases need to draw from the base igraph functionality.

All tidygraphfunctions are excellently documented here

Creating atbl_graph

library(tidygraph)
  • We here created the tbl_graph directly from the igraph object.
g  %<>% as_tbl_graph()
g
# A tbl_graph: 5 nodes and 7 edges
#
# An undirected multigraph with 1 component
#
# Node Data: 5 x 3 (active)
  name     gender group
  <chr>    <chr>  <chr>
1 Jesper   M      A    
2 Pernille F      B    
3 Jacob    M      B    
4 Dorte    F      A    
5 Donald   M      C    
#
# Edge Data: 7 x 2
   from    to
  <int> <int>
1     1     2
2     2     3
3     2     4
# … with 4 more rows
  • We see a more intuitive representation of the datastructure, consisting of a node as well as an edge dataframe.
  • We could for sure also create it based on our initial node- and edgelist.
g 
# A tbl_graph: 5 nodes and 7 edges
#
# An undirected multigraph with 1 component
#
# Node Data: 5 x 4 (active)
     id name     gender group
  <int> <chr>    <chr>  <chr>
1     1 Jesper   M      A    
2     2 Pernille F      B    
3     3 Jacob    M      B    
4     4 Dorte    F      A    
5     5 Donald   M      C    
#
# Edge Data: 7 x 2
   from    to
  <int> <int>
1     1     2
2     2     3
3     2     4
# … with 4 more rows
  • Note: The tbl_graph class is a thin wrapper around an igraph object that provides methods for manipulating the graph using the tidy API.
  • As it is just a subclass of igraph, every igraph method and its syntax will work as expected and can be used if necessary. However, it might convert it back into an original igraph object.
V(g)
+ 5/5 vertices, named, from cfdd05e:
[1] Jesper   Pernille Jacob    Dorte    Donald  
  • In adittionan, the as_tbl_graph() function also can transform different types of network data from objects such as data.frame, matrix, dendrogram, igraph, etc.

Acessing and manipulating nodes and edges

  • But how can a graph object be manipulated with dplyr syntax?
  • We know that a graph object contains an edge as well as node dataframe, so commands like g %>% filter(name == "Pernille") would be confusing, since it is unclear if we refer to nodes or edges.
  • tidygraph’s solution here are selective activation pipes:
    • %N>% activates nodes (short for longer alternative:%>% activate_nodes())
    • %E>% activates edges (short for longer alternative:%>% activate_edges())
  • Consequently, functions are executed on the currently active dataframe of either nodes or edges.
  • With this simple syntax trick, graphs become subject to most commonly known data manipulation workflows for tabular data.
g %N>%
  filter(gender == "F")
# A tbl_graph: 2 nodes and 1 edges
#
# An unrooted tree
#
# Node Data: 2 x 4 (active)
     id name     gender group
  <int> <chr>    <chr>  <chr>
1     2 Pernille F      B    
2     4 Dorte    F      A    
#
# Edge Data: 1 x 2
   from    to
  <int> <int>
1     1     2
  • Note that filtering nodes will simultaneously result in a filtering of edges. We for sure can also do manipulatings on both nodes and edges in one pipeline.
g %N>%
  filter(group %in% c("A", "B")) %E>%
  filter(to == 2)
# A tbl_graph: 4 nodes and 1 edges
#
# An unrooted forest with 3 trees
#
# Edge Data: 1 x 2 (active)
   from    to
  <int> <int>
1     1     2
#
# Node Data: 4 x 4
     id name     gender group
  <int> <chr>    <chr>  <chr>
1     1 Jesper   M      A    
2     2 Pernille F      B    
3     3 Jacob    M      B    
# … with 1 more row
  • Note that the filtering of edges did not reduce the nodeset. While nodes can be isolated in a nework, edges without an adjacent node cannot exist.
  • We can also pull the virtual node or edge dataframe out of the tbl_graph and use it for tabular analysis.
g %N>%
  filter(group == "B") %>%
  as_tibble()

Visualization (preview)

  • One last thing for now: While igraph also provides a powerful network visualization functionality, I will also mostly go with Thomas sister package, ggraph, which provides a network visualization interface compatible and consistent with ggplot2
  • The rest works like any ggplot function call, just that we use special geoms for our network
  • Fir instance, we use: geom_edge_density() to draw a shadow where the edge density is higher, geom_edge_link() to connect edges with a straight line, geom_node_point() to draw node points and geom_node_text() to draw the labels. More options can be found here.
library(ggraph)
g %>% ggraph(layout = "nicely") + 
  geom_edge_link() + 
  geom_node_point() + 
  geom_node_text(aes(label = name))

Not very impressive up to now, but wait for the real stuff to come in later sessions.

Network analysis and measures

# generate a sample network: play_smallworld() Create graphs based on the Watts-Strogatz small- world model.
set.seed(1337)
g <- play_barabasi_albert(n = 200, # Number of nodes
                          power = 0.75, # Power of preferential attachment effect
                          directed = FALSE # Undirected network
                          )              
set.seed(1337)
g %>%
    ggraph(layout = "fr") + 
    geom_edge_link() + 
    geom_node_point() + 
    theme_graph() # Adding `theme_graph()` introduces a stileguide better suited for rgaphs

Node level measures

Centralities can be easily created on node level wit the centrality_[...] function. All centralities available can be found here

g <- g %N>%
  mutate(centrality_dgr = centrality_degree(),
         centrality_eigen = centrality_eigen(),
         centrality_between = centrality_betweenness()) 
g %N>%
  as_tibble() %>% 
  head()

Degree centrality

set.seed(1337)
g %>%
    ggraph(layout = "fr") + 
    geom_edge_link() + 
    geom_node_point(aes(size = centrality_dgr, colour = centrality_dgr)) + 
    scale_color_continuous(guide = "legend") + 
    theme_graph()

Eigenvector centrality

set.seed(1337)
g %>%
    ggraph(layout = "fr") + 
    geom_edge_link() + 
    geom_node_point(aes(size = centrality_eigen, colour = centrality_eigen)) + 
    scale_color_continuous(guide = "legend") + 
    theme_graph()

Betweenness centrality

set.seed(1337)
g %>%
    ggraph(layout = "fr") + 
    geom_edge_link() + 
    geom_node_point(aes(size = centrality_between, colour = centrality_between)) + 
    scale_color_continuous(guide = "legend") + 
    theme_graph()

Clustering (Community detection)

  • All clustering algorithms from igraph are available in tidygraph using the group_* prefix.
  • All of these functions return an integer vector with nodes (or edges) sharing the same integer being grouped together.
  • There are-just like for clusterin of tabular data in UML-many different algorithms and approaches to

Lets illustrate

set.seed(1337)
# We create an example network
g <- play_islands(n_islands = 5, #  The number of densely connected islands
                  size_islands = 15, # The number of nodes in each island
                  p_within = 0.75, # The probability of edges within and between groups/blocks
                  m_between = 5 # The number of edges between groups/islands
                  ) 
set.seed(1337)
# As planned, we clearely see distinct communities
g %>% 
    ggraph(layout = 'kk') + 
    geom_edge_link() + 
    geom_node_point(size = 7) + 
    theme_graph()

set.seed(1337)
# We run a community detection simply with the group_* function of tidygraph. here, the Lovain algorithm is a well performing and fast choice.
g <- g %N>% 
    mutate(community = group_louvain() %>% as.factor()) 
set.seed(1337)
# Lets see how well it did...
g %>% 
    ggraph(layout = 'kk') + 
    geom_edge_link() + 
    geom_node_point(aes(colour = community), size = 7) + 
    theme_graph()

Neighborhood of a Node

  • Lets check the size of all nodes neighborhood at distance 2.
g %N>%
  mutate(neighborhood_size = local_size(order = 2)) %>%
  as_tibble() %>%
  arrange(desc(neighborhood_size)) %>%
  head()

’ We can also not only look at it, but produce a new sub-graph only of this ego-network. ’ Here, we need to use the base igraph function. Note that it produces an igraph object, so we have to make a tidygraph again…

g1 <- make_ego_graph(g, 2, nodes = 1)[[1]] %>% as_tbl_graph()
g50 <- make_ego_graph(g, 2, nodes = 50)[[1]] %>% as_tbl_graph()
set.seed(1337)
g1 %>% 
    ggraph(layout = 'kk') + 
    geom_edge_link() + 
    geom_node_point(aes(colour = community), size = 7) + 
    theme_graph()

set.seed(1337)
g50 %>% 
    ggraph(layout = 'kk') + 
    geom_edge_link() + 
    geom_node_point(aes(colour = community), size = 7) + 
    theme_graph()

(Global) Network structure

  • Finally, it is often also informative to look at the overal characteristics of the network. We will do this in more detail next time, but just so you know:

  • The density of a measure represents the share of all connected to all possible connections in the network

edge_density(g)
[1] 0.1545946

Transistivity, also c alled the Clustering Cefficient indicates how much the network tends to be locally clustered. That is measured by the share of closed triplets. Again,w e will dig into that next time.

transitivity(g)
[1] 0.5551739
  • The diameter is the longest of the shortest paths between two nodes of the network.
diameter(g, directed = F, weights = NA)
[1] 4
  • Finally, the mean distance, or average path lenght represents the mean of all shortest paths between all nodes. It is a measure of diffusion potential within a network.
mean_distance(g, directed = F)
[1] 2.321441

Case: Networks are coming…

  • So, lets get serious. Appropriate for the weather these days in Denmark, the theme is “winter is comming…”.
  • Therefore, we will have some fun analysing the Game of Thrones data provided by Andrew Beveridge.
  • It is a Character Interaction Networks for George R. R. Martin’s “A Song of Ice and Fire” saga (yes, we are talking about the books…).
  • These networks were created by connecting two characters whenever their names (or nicknames) appeared within 15 words of one another in one of the books in “A Song of Ice and Fire.”
  • The edge weight corresponds to the number of interactions.
  • This is a nice skill you will have after the second part of M2 on your own.

Build the graph

  • First, we load all nodes, representing all characters appearing in the books:
edges <- read_csv('https://sds-aau.github.io/SDS-master/00_data/GoT_network/asoiaf-all-edges.csv') 
edges %>% head()
colnames(edges) <- tolower(colnames(edges))
  • So, that’s what we have, a classical edgelist, with id1 in column 1 and id2 in column2.
  • Note, the edges are in this case weighted.

Ok, lets see how many characters we have overal.

n_distinct(c(edges$source, edges$target))
[1] 796
  • Because there are so many characters in the books, many of them minor,
  • I am subsetting the data to the 100 characters with the most interactions across all books.
  • The edges are undirected, therefore there are no redundant Source-Target combinations.
  • Because of this, I pivot Source and Target data before summing up the weights.
head(chars_main)
  • So far so good, if we only go by edge weights,
  • Lets reduce our edgelist to this main characters, just to warm up and keep the overview.
edges %<>%
  filter(source %in% chars_main$name & target %in% chars_main$name) %>%
  select(source, target, weight) %>%
  rename(from = source,
         to = target)
# Note: Since it is small data, this way with %in% is ok. However, with large datasets I would filter via semi_join() instead (more efficient)

Now we can convert our edgelist into a tbl_graph object structure.

g <- edges %>% as_tbl_graph(directed = FALSE)
g
# A tbl_graph: 100 nodes and 798 edges
#
# An undirected simple graph with 1 component
#
# Node Data: 100 x 1 (active)
  name                           
  <chr>                          
1 Aemon-Targaryen-(Maester-Aemon)
2 Aeron-Greyjoy                  
3 Aerys-II-Targaryen             
4 Alliser-Thorne                 
5 Arianne-Martell                
6 Arya-Stark                     
# … with 94 more rows
#
# Edge Data: 798 x 3
   from    to weight
  <int> <int>  <dbl>
1     1     4      7
2     1    13      4
3     1    28      3
# … with 795 more rows
  • We can use some of the tidygraph helpers to briefly clean the graph. Check ?node_is_* and ?edge_is_* for options.
# Filtering out multiple edges and isolated nodes (unconnected), in case there are some
g <- g %E>%
  filter(!edge_is_multiple()) %N>%
  filter(!node_is_isolated()) 
  • Note that the edges in this graph are weighted. We can briefly look at the weight distribution:
g %E>%
  as_tibble() %>%
  ggplot(aes(x = weight)) +
  geom_histogram()

We see a right skewed distribution with many weak and some very strong edges. Lets take a look what are the edges with the highest weight (meaning here: the characters with most intraction).

g %E>%
  as_tibble() %>%
  arrange(desc(weight)) %>%
  head()

tidygraph always uses numeric IDs for nodes, which are also labeling the edges. This is not very helpful to get insights. So, lets take the node names in instead.

# We access the nodes directly via .N(). The same can be done for edges with .E() and the graph with .G(). Check ?context_accessors for more infos
g %E>%
  mutate(name_from = .N()$name[from],
         name_to = .N()$name[to]) %>%
  as_tibble() %>%
  select(name_from, name_to, weight) %>%
  arrange(desc(weight)) %>%
  head()

Node Characteristics

g <- g %N>%
  mutate(centrality_dgr = centrality_degree(weights = weight),
         centrality_eigen = centrality_eigen(weights = weight),
         centrality_between = centrality_betweenness(weights = weight)) 
bind_cols(g %N>%
            select(name, centrality_dgr) %>%
            arrange(desc(centrality_dgr)) %>%
            as_tibble(),
          g %N>%
            select(name, centrality_eigen) %>%
            arrange(desc(centrality_eigen)) %>%
            as_tibble(),
          g %N>%
            select(name, centrality_between) %>%
            arrange(desc(centrality_between)) %>%
            as_tibble()) %>%
  mutate_if(is.numeric, round, 1) %>%
  head()

Communities & Groups

g <- g %N>% 
    mutate(community = group_louvain() %>% as.factor()) 
g %N>%
  select(name, community, centrality_dgr) %>%
  as_tibble() %>% 
  arrange(community, desc(centrality_dgr)) %>%
  group_by(community) %>%
  slice(1:5) %>% mutate(n = 1:5) %>%
  ungroup() %>%
  select(-centrality_dgr) %>%
  spread(community, name)
NA

Network Visualization I

Ok, lets give it a first minimal shot:

g %>% ggraph(layout = "fr") + 
    geom_edge_link() + 
    geom_node_point() +
  geom_node_text(aes(label = name)) 

Not very exciting. Maybe we can do a bit better, using more options in the ggraph functionality to visualize aspects of the network.

g %E>% 
  filter(weight >= quantile(weight, 0.5)) %N>%
  filter(!node_is_isolated()) %>%
  ggraph(layout = "fr") + 
    geom_edge_link(aes(width = weight), alpha = 0.2) + 
    geom_node_point(aes(color = community, size = centrality_eigen)) +
    geom_node_text(aes(label = name, size = centrality_eigen), repel = TRUE) +
    scale_color_brewer(palette = "Set1") +
    theme_graph() +
    labs(title = "A Song of Ice and Fire character network",
         subtitle = "Nodes are colored by community")

Your turn

Please do Exercise 1 in the corresponding section on Github.

Endnotes

Packages & Ecosystem

Suggestions for further study

Session Info

sessionInfo()
LS0tCnRpdGxlOiAnSW50cm9kdWN0aW9uIHRvIE5ldHdvcmsgQW5hbHlzaXM6IEFwcGxpY2F0aW9uIChSKScKYXV0aG9yOiAiRGFuaWVsIFMuIEhhaW4gKGRzaEBidXNpbmVzcy5hYXUuZGspIgpkYXRlOiAiVXBkYXRlZCBgciBmb3JtYXQoU3lzLnRpbWUoKSwgJyVCICVkLCAlWScpYCIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICBjb2RlX2ZvbGRpbmc6IHNob3cKICAgIGRmX3ByaW50OiBwYWdlZAogICAgdG9jOiB0cnVlCiAgICB0b2NfZGVwdGg6IDIKICAgIHRvY19mbG9hdDoKICAgICAgY29sbGFwc2VkOiBmYWxzZQogICAgdGhlbWU6IGZsYXRseQotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQojIyMgR2VuZXJpYyBwcmVhbWJsZQpybShsaXN0PWxzKCkpClN5cy5zZXRlbnYoTEFORyA9ICJlbiIpICMgRm9yIGVuZ2xpc2ggbGFuZ3VhZ2UKb3B0aW9ucyhzY2lwZW4gPSA1KSAjIFRvIGRlYWN0aXZhdGUgYW5ub3lpbmcgc2NpZW50aWZpYyBudW1iZXIgbm90YXRpb24KCiMjIyBLbml0ciBvcHRpb25zCmxpYnJhcnkoa25pdHIpICMgRm9yIGRpc3BsYXkgb2YgdGhlIG1hcmtkb3duCmtuaXRyOjpvcHRzX2NodW5rJHNldCh3YXJuaW5nPUZBTFNFLAogICAgICAgICAgICAgICAgICAgICBtZXNzYWdlPUZBTFNFLAogICAgICAgICAgICAgICAgICAgICBjb21tZW50PUZBTFNFLCAKICAgICAgICAgICAgICAgICAgICAgZmlnLmFsaWduPSJjZW50ZXIiCiAgICAgICAgICAgICAgICAgICAgICkKYGBgCgpgYGB7cn0KIyMjIExvYWQgc3RhbmRhcmRwYWNrYWdlcwpsaWJyYXJ5KHRpZHl2ZXJzZSkgIyBDb2xsZWN0aW9uIG9mIGFsbCB0aGUgZ29vZCBzdHVmZiBsaWtlIGRwbHlyLCBnZ3Bsb3QyIGVjdC4KbGlicmFyeShtYWdyaXR0cikgIyBGb3IgZXh0cmEtcGlwaW5nIG9wZXJhdG9ycyAoZWcuICU8PiUpCmBgYAoKIyMjIFRoaXMgc2Vzc2lvbgoKSW4gdGhpcyBhcHBsaWVkIHNlc3Npb24sIHlvdSB3aWxsOgoKCiMgTmV0d29yayBkYXRhIHN0cnVjdHVyZXMKCiogQmVsb3cgYW4gZXhhbXBsZSBvZmEgbWluaW1hbCBlZGdlIGxpc3QgY3JlYXRlZCB3aXRoIHRoZSBgdGliYmxlKClgIGZ1bmN0aW9uLiAKKiBJbiB0aGlzIGNhc2UsIGxldCB1cyBhc3N1bWUgdGhpcyBuZXR3b3JrIHRvIGJlIHVud2VpZ2h0ZWQsIG1lYW5pbmcgYSBjb25uZWN0aW9uIGNhbiBiZSBlaXRlciB0cmVzZW50IG9yIGFic2VudC4KCmBgYHtyIGVkZ2VsaXN0c30KZWRnZV9saXN0IDwtIHRpYmJsZShmcm9tID0gYygxLCAyLCAyLCAxLCA0LCAzLCA1KSwgCiAgICAgICAgICAgICAgICAgICAgdG8gPSBjKDIsIDMsIDQsIDUsIDEsIDIsIDUpKQpgYGAKCmBgYHtyfQplZGdlX2xpc3QKYGBgCgojIyBBZGphY2VuY3kgTWF0cml4CgoqIEEgc2Vjb25kIHBvcHVsYXIgZm9ybSBvZiBuZXR3b3JrIHJlcHJlc2VudGF0aW9uIGlzIHRoZSAqKmFkamFjZW5jeS1tYXRyaXgqKiAoYWxzbyBjYWxsZWQgKipzb2Npby1tYXRyaXgqKikuIAoqIEl0IGlzIHJlcHJlc2VudGVkIGFzIGEgJG4qbiQgbWF0cml4LCB3aGVyZSAkbiQgc3RhbmRzIGZvciB0aGUgbnVtYmVyIG9mIGVsZW1lbnRzIG9mIHdoaWNoIHRoZWlyIHJlbGF0aW9uc2hpcHMgc2hvdWxkIGJlIHJlcHJlc2VudGVkLiAKKiBUaGUgdmFsdWUgaW4gdGhlIGNlbGwgdGhhdCBpbnRlcmNlcHRzIHJvdyAkbiQgYW5kIGNvbHVtbiAkbSQgaW5kaWNhdGVzIGlmIGFuIGVkZ2UgaXMgcHJlc2VudCAoPTEpIG9yIGFic2VudCAoPTApLgoqIFRpcDogR2l2ZW4gYW4gZWRnZWxpc3QsIGFuIGFkamFjZW5jeSBtYXRyaXggY2FuIGVhc2lseSBiZSBwcm9kdWNlZCBieSBjcm9zc3RhYnVsYXRpbmc6CgpgYGB7ciBtYXRyaXh9CmFkal9tYXRyaXggPC0gZWRnZV9saXN0ICU+JQogIHRhYmxlKCkgJT4lIAogIGFzLm1hdHJpeCgpCmBgYAoKYGBge3J9CmFkal9tYXRyaXgKYGBgCgoKKk5vdGU6KiAKCiogRXhpc3RpbmcgYXMgd2VsbCBhcyBub3QgZXhpc3RpbmcgY29ubmVjdGlvbnMgYXJlIHN0b3JlZC4gU2luY2UgbW9zdCBuZXR3b3JrcyBpbiByZWFsaXR5IGFyZSAqKnNwYXJzZSoqICg9IG1vcmUgcG90ZW50aWFsIGNvbm5lY3Rpb25zIGFyZSBpbmFjdGl2ZSB0aGFuIGFjdGl2ZSkKKiBUaGlzIGlzIGluZWZmaWNpZW50IGZvciBzdG9ycmFnZSBhbmQgY29tcHV0YXRpb24uIAoqIEhlcmUsIGEgc3BhcnNlIGBkZ0NNYXRyaXhgIG9iamVjdCBmcm9tIHRoZSBgTWF0cml4YGNhbiBiZSBoZWxwZnVsLgoqIFRoaXMgc3BhcnNlIGRhdGFzcnVjdHVyZSBvbmx5IHN0b3JlcyBhIHJlZmVyZW5jZSB0byBub24tZW1wdHkgY2VsbHMgYW5kIHRoZWlyIHZhbHVlcy4gCgpgYGB7cn0KbGlicmFyeShNYXRyaXgpCnNwYXJzZV9tYXRyaXggPC0gZWRnZV9saXN0ICU+JQogIHRhYmxlKCkgJT4lIAogIE1hdHJpeChzcGFyc2UgPSBUUlVFKQpgYGAKCmBgYHtyfQpzcGFyc2VfbWF0cml4CmBgYAoKYGBge3J9CnNwYXJzZV9tYXRyaXggJT4lIGdsaW1wc2UoKQpgYGAKCiMjIE5vZGVsaXN0cwoqIEVkZ2VsaXN0cyBhcyB3ZWxsIGFzIGFkamFjZW5jeSBtYXRyaWNlcyBvbmx5IHN0b3JlcyBjb25uZWN0aXZpdHkgcGF0dGVybiBiZXR3ZWVuIG5vZGVzLCBidXQgZHVlIHRvIHRoZWlyIHN0cnVjdHVyZSBjYW5ub3Qgc3RvcmUgaW5mb3JtYXRpb25zIG9uIHRoZSBub2RlcyBpbiB3aGljaCB3ZSBtaWdodCBiZSBpbnRlcmVzdGVkLiAKKiBUaGVyZWZvcmUsIHdlIGluIG1hbnkgY2FzZXMgYWxzbyBwcm92aWRlIGEgYSAqKm5vZGUgbGlzdCoqIHdpdGggdGhlc2UgaW5mb3JtYXRpb25zIChzdWNoIGFzIHRoZSBuYW1lcyBvZiB0aGUgbm9kZXMgb3IgYW55IGtpbmQgb2YgZ3JvdXBpbmdzKS4KCmBgYHtyIG5vZGVsaXN0c30Kbm9kZV9saXN0IDwtIHRpYmJsZShpZCA9IDE6NSwgCiAgICAgICAgICAgICAgICAgICAgbmFtZSA9IGMoIkplc3BlciIsICJQZXJuaWxsZSIsICJKYWNvYiIsICJEb3J0ZSIsICJEb25hbGQiKSwKICAgICAgICAgICAgICAgICAgICBnZW5kZXIgPSBjKCJNIiwgIkYiLCAiTSIsICJGIiwgIk0iKSwKICAgICAgICAgICAgICAgICAgICBncm91cCA9IGMoIkEiLCAiQiIsICJCIiwgIkEiLCAiQyIpKQpgYGAKCmBgYHtyfQpub2RlX2xpc3QKYGBgCgoKIyMgR3JhcGggb2JqZWN0cwoKIyMjIEdyYXBoIG9iamVjdHMgaW4gW2BpZ3JhcGhgXShodHRwczovL2lncmFwaC5vcmcvci8pCgoqIE9uZSBvZiB0aGUgbW9zdCBwb3B1bGFyIG5ldHdvcmsvZ3JhcGggYW5hbHl0aWNzIGZyYW1ld29yayBpbiBgUmAgYW5kIGBQeXRob25gIGFsaWtlIGlzIFtgaWdyYXBoYF0oaHR0cDovL2lncmFwaC5vcmcpLiAqIEl0IHByb3ZpZGVzIGEgcG93ZXJmdWwgdG9vbGJveCBmb3IgYW5hbHlzaXMgYXMgd2VsbCBhcyBwbG90dGluZyBhbGlrZS4gTGV0cyB0YWtlIGEgcGVhay4KKiBUbyBjcmVhdGUgYW4gYGlncmFwaGAgb2JqZWN0IGZyb20gYW4gZWRnZS1saXN0IGRhdGEgZnJhbWUgd2UgY2FuIHVzZSB0aGUgYGdyYXBoX2Zyb21fZGF0YV9mcmFtZSgpYCBmdW5jdGlvbi4gCiogVGhlcmUgYXJlIHRocmVlIGFyZ3VtZW50cyBpbiB0aGUgYGdyYXBoX2Zyb21fZGF0YV9mcmFtZSgpYCBmdW5jdGlvbjogZCwgdmVydGljZXMsIGFuZCBkaXJlY3RlZC4gCiAgICogZCByZWZlcnMgdG8gdGhlIGVkZ2UgbGlzdCwgCiAgICogdmVydGljZXMgdG8gdGhlIG5vZGUgbGlzdCwgCiAgICpkaXJlY3RlZCBjYW4gYmUgZWl0aGVyIGBUUlVFYCBvciBgRkFMU0VgIGRlcGVuZGluZyBvbiB3aGV0aGVyIHRoZSBkYXRhIGlzIGRpcmVjdGVkIG9yIHVuZGlyZWN0ZWQuIAoqIEJ5IGRlZmF1bHQsIGBncmFwaC5kYXRhLmZyYW1lKClgIHRyZWF0cyB0aGUgZmlyc3QgdHdvIGNvbHVtbnMgb2YgdGhlIGVkZ2UgbGlzdCBhbmQgYW55IHJlbWFpbmluZyBjb2x1bW5zIGFzIGVkZ2UgYXR0cmlidXRlcy4KCmBgYHtyfQpsaWJyYXJ5KGlncmFwaCkKYGBgCgoKYGBge3J9CmcgPC0gZ3JhcGhfZnJvbV9kYXRhX2ZyYW1lKGQgPSBlZGdlX2xpc3QsIHZlcnRpY2VzID0gbm9kZV9saXN0LCBkaXJlY3RlZCA9IEZBTFNFKQojIGcgPC0gZ3JhcGhfZnJvbV9hZGphY2VuY3lfbWF0cml4KGFkal9tYXRyaXgsIG1vZGUgPSAidW5kaXJlY3RlZCIpICMgU2FtZSBmb3IgdGhlIGFkamFjZW5jeSBtYXRyaXgKYGBgCgpgYGB7cn0KZwpgYGAKCgpMZXRzIGluc3BlY3QgdGhlIHJlc3VsdGluZyBvYmplY3QuIEFuIGBpZ3JhcGhgIGdyYXBoIG9iamVjdCBzdW1tYXJ5IHJldmVhbHMgc29tZSBpbnRlcmVzdGluZyBpbmZvcm1hdGlvbnMuCgoqIEZpcnN0LCBpdCB0ZWxscyB1cyB0aGUgZ3JhcGgtdHlwZTogdW5kaXJlY3RlZCBgVU5gLCBvciAgZGlyZWN0ZWQgYEROYAoqIEFmdGVyd2FyZHMsIHRoZSBudW1iZXIgb2Ygbm9kZXMgKDQpLCBhbmQgZWRnZXMgKDUpCiogRm9sbG93ZWQgYnkgdGhlIG5vZGUgYXR0cmlidXRlcyAobm9kZSBsZXZlbCB2YXJpYWJsZXMpLCB3aGljaCBpbiB0aGlzIGNhc2UgYXJlIG9ubHkgdGhlaXIgbmFtZSwgZ2VuZGVyLCBhbmQgZ3JvdXAgKGBhdHRyOiBuYW1lICh2L2MpLCBnZW5kZXIgKHYvYyksIGdyb3VwICh2L2MpYCkKKiBMYXN0bHksIGEgbGlzdCBvZiBhbGwgZXhpc3RpbmcgZWRnZXMuIE5vdGU6IGBuLS1tYCBpbmRpY2F0ZXMgYW4gdW5kaXJlY3RlZCwgYG4tPm1gIGFuIGRpcmVjdGVkIGVkZ2UuCgpMZXRzIHRha2UgYSBsb29rIGF0IHRoZSBzdHJ1Y3R1cmUgb2YgdGhlIG9iamVjdDoKCmBgYHtyfQpnW1sxOjJdXSU+JSBnbGltcHNlKCkgIyBOb3RlIHRoZSBkb3VibGUgYnJhY2tldHMgKGcgaXMgYSBsaXN0IG9iamVjdCkKYGBgCgoqIFdlIHNlZSwgdGhlIG9iamVjdCBoYXMgYSBsaXN0LWZvcm1hdCwgY29uc2lzdGluZyBvZiBzZXBwZXJhdGUgbGlzdHMgZm9yIGV2ZXJ5IG5vZGUsIGNvbnRhaW5pbmcgc29tZSBhdHRyaWJ1dGVzIHdoaWNoIGFyZSBpcnJlbGV2YW50IG5vdywgYW5kIGFuIGVkZ2VsaXN0IGZvciBldmVyeSBub2RlLCBjYXB0dXJpbmcgaXRzIGVnby1uZXR3b3JrIChlZy4sIGAuLiQgSmVzcGVyOiAnaWdyYXBoLnZzJyBOYW1lZCBpbnQgWzE6M10gMiA0IDVgKQoqIFdlIGNhbiBhbHNvIHBsb3QgaXQgdG8gdGFrZSBhIGxvb2suIGBpZ3JhcGhgIG9iamVjdCBjYW4gYmUgZGlyZWN0bHkgdXNlZCB3aXRoIHRoZSBgcGxvdCgpYCBmdW5jdGlvbi4gCiogVGhlIHJlc3VsdHMgY2FuIGJlIGFkanVzdGVkIHdpdGggYSBzZXQgb2YgcGFyYW1ldGVycyB3ZSB3aWxsIGRpc2NvdmVyIGxhdGVyLiAKKiBJdCdzIG5vdCBzdXBlciBwcmV0dHksIHRoZXJlZm9yZSB3ZSB3aWxsIGxhdGVyIGFsc28gZXhwbG9yZSBtb3JlIHBvd2VyZnVsbCBwbG90dGluZyB0b29scyBmb3IgZ2FwaHMuIEhvd2V2ZXIsIGl0cyBxdWljayZkaXJ0eSwgc28gbGV0cyB0YWtlIGl0IGxpa2UgdGhhdCBmb3Igbm93LiAKCmBgYHtyfQpwbG90KGcpCmBgYAoKKk5vdGU6KiBXZSB3aWxsIG5vdCB2ZW50dXJlIGZ1cnRoZXIgaW50byB0aGUgYGlncmFwaGAgcGxvdHRpbmcgZnVuY3Rpb25hbGl0eSwgc2luY2Ugd2Ugd2lsbCBnbyBhbGwgaW4gd2l0aCBgZ2dyYXBoYC4gSG93ZXZlciwgdGhlcmUgaXMgYSB2ZXJ5IG5lYXRoIHR1dG9yaWFsIFtoZXJlXShodHRwczovL3Jhdy5naXRoYWNrLmNvbS9rYXRldG8vUi1OZXR3b3JrLVZpc3VhbGl6YXRpb24tV29ya3Nob3AvbWFzdGVyL1N1bmJlbHQlMjAyMDE5JTIwUiUyME5ldHdvcmslMjBWaXN1YWxpemF0aW9uJTIwV29ya3Nob3AuaHRtbCksIHRoYXQgd2lsbCB0ZWxsIHlvdSBldmVyeXRoaW5nIHlvdSBuZWVkIHRvIGtub3csIGluIGNhc2UgeW91IGFyZSBpbnRlcmVzdGVkLgoKV2UgY2FuIGluc3BlY3QgYW5kIG1hbmlwdWxhdGUgdGhlIG5vZGVzIHZpYSBgVihnKWAgKFYgZm9yIHZlcnRpY2VzLCBpdHMgZ3JhcGgtdGhlb3J5IHNsYW5nKSwgYW5kIGVkZ2VzIHdpdGggYEUoZylgCgpgYGB7cn0KVihnKQpgYGAKCmBgYHtyfQpFKGcpCmBgYAoKV2UgY2FuIGFsc28gdXNlIG1vc3Qgb2YgdGhlIGJhc2UtUiBzbGljaW5nJmRpY2luZy4KCmBgYHtyfQpWKGcpWzE6M10KYGBgCgpgYGB7cn0KRShnKVsyOjRdCmBgYAoKClJlbWVtYmVyLCBpdCdzIGEgbGlzdC1vYmplY3QuIFNvLCBpZiB3ZSBqdXN0IHdhbnQgdG8gaGF2ZSB0aGUgdmFsdWVzLCB3ZSBoYXZlIHRvIHVzZSB0aGUgZG91YmxlIGJyYWNrZXQgYFtbeF1dYC4gCgpgYGB7cn0KVihnKVtbMTozXV0KYGBgCgpXZSBjYW4gYWxzbyB1c2UgdGhlIGAkYCBub3RhdGlvbi4KCmBgYHtyfQpWKGcpJG5hbWUKYGBgCgpUaGVyZSBpcyBvYnZpb3VzbHkgYSBsb3QgbW9yZSB0byBzYXkgYWJvdXQgYGlncmFwaGAgYW5kIGl0cyByaWNoIGZ1bmN0aW9uYWxpdHkuIFlvdSB3aWxsIGxlYXJuIG11Y2ggb2YgdGhlIGFic2UgZnVuY3Rpb25hbGl0eSBvZiBgaWdyYXBoYCBpbiB5b3VyIERDIGFzc2lnbm1lbnRzLiBGdXJ0aGVybW9yZSBbS2F0eWEgT2dueWFub3ZhXShodHRwczovL2thdGV0by5uZXQvKSwgaGFzIGEgW2JyaWxsaWFudCB0dXRvcmlhbF0oaHR0cHM6Ly9rYXRldG8ubmV0L25ldHdvcmtzLXItaWdyYXBoKSB0aGF0IGNhbiBiZSBzdHVkaWVkLgoKIyMjIEdyYXBoIG9iamVjdHMgaW4gW2B0aWR5Z3JhcGhgXShodHRwczovL3RpZHlncmFwaC5kYXRhLWltYWdpbmlzdC5jb20vKQoKKiBXaGlsZSB0aGUgYGlncmFwaGAgZnVuY3Rpb25hbGl0eSBzdGlsbCByZXByZXNlbnRzIHRoZSBjb3JlIG9mIGBSYCdzIG5ldHdvcmsgYW5hbHlzaXMgdG9vbGJveCwgcmVjZW50IGRldmVsb3BtZW50cyBoYXZlIG1hZGUgbmV0d29yayBhbmFseXRpY3MgbXVjaCBtb3JlIGFjY2Vzc2libGUgYW5kIGludHVpdGl2ZS4KKiBbVGhvbWFzIExpbiBQZWRlcnNlbl0oaHR0cDovL3d3dy5kYXRhLWltYWdpbmlzdC5jb20pIChhbHNvIGtub3duIGFzIHRoZSBkZXZlbG9wZXIgb2YgYmVsb3ZlZCBwYWNrYWdlcyBsaWtlIGBnZ2ZvcmNlYCwgYGdnYW5pbWF0ZWAsIGFuZCB0aGUgYFJgIGltcGxlbWVudGF0aW9uIG9mIGBsaW1lYCkgIGhhcyByZWNlbnRseSByZWxlYXNlZCB0aGUgW2B0aWR5Z3JhcGhgXShodHRwczovL3RpZHlncmFwaC5kYXRhLWltYWdpbmlzdC5jb20vKSBwYWNrYWdlLgoqIEl0IGxldmVyYWdlIHRoZSBwb3dlciBvZiBgaWdyYXBoYCBpbiBhIG1hbm5lciBjb25zaXN0ZW50IHdpdGggdGhlIFtgdGlkeXZlcnNlYF0oaHR0cDovL3d3dy50aWR5dmVyc2Uub3JnKSB3b3JrZmxvdy4KKiBJdCByZXByZXNlbnRzIGEgbGlnaHR3ZWlnaHQgd3JhcHBlciBhcm91bmQgdGhlIGNvcmUgYGlncmFwaGAgb2JqZWN0IGFuZCBmdW5jdGlvbmFsaXR5IHdoaWNoIG1ha2VzIGl0IGFjY2Vzc2libGUgZm9yIG11Y2ggb2YgdGhlIHRyYWRpdGlvbmFsIGBkcGx5cmAgd29ya2Zsb3dzLgoqIEV2ZW4gYmV0dGVyLCBoZSB0b3BzIGl0IHVwIHdpdGggW2BnZ3JhcGhgXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvZ2dyYXBoL2luZGV4Lmh0bWwpLCBhIGNvbnNpc3RlbnQgYGdncGxvdDJgLWxvb2stYW5kLWZlZWwgbmV0d29yayB2aXN1YWxpemF0aW9uIHBhY2thZ2UuCiogRm9yIHRoYXQgcmVhc29uLCB3ZSB3aWxsIG1vc3RseSB3b3JrIHdpdGggdGhlIGB0aWR5Z3JhcGhgIGZyYW1ld29yaywgd2hpbGUgd2Ugc3RpbGwgaW4gc29tZSBmZXcgY2FzZXMgbmVlZCB0byBkcmF3IGZyb20gdGhlIGJhc2UgYGlncmFwaGAgZnVuY3Rpb25hbGl0eS4gCgohW10oaHR0cHM6Ly9zZHMtYWF1LmdpdGh1Yi5pby9TRFMtbWFzdGVyLzAwX21lZGlhL25ldHdvcmtzX2RhdGFfc3RydWN0dXJlLnBuZykKCkFsbCBgdGlkeWdyYXBoYGZ1bmN0aW9ucyBhcmUgZXhjZWxsZW50bHkgZG9jdW1lbnRlZCBbaGVyZV0oaHR0cHM6Ly90aWR5Z3JhcGguZGF0YS1pbWFnaW5pc3QuY29tL3JlZmVyZW5jZS9pbmRleC5odG1sKQoKCiMjIyBDcmVhdGluZyBhYHRibF9ncmFwaGAKCmBgYHtyfQpsaWJyYXJ5KHRpZHlncmFwaCkKYGBgCgoKKiBXZSBoZXJlIGNyZWF0ZWQgdGhlIGB0YmxfZ3JhcGhgIGRpcmVjdGx5IGZyb20gdGhlIGBpZ3JhcGhgIG9iamVjdC4KCmBgYHtyfQpnICAlPD4lIGFzX3RibF9ncmFwaCgpCmBgYAoKYGBge3J9CmcKYGBgCgoqIFdlIHNlZSBhIG1vcmUgaW50dWl0aXZlIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBkYXRhc3RydWN0dXJlLCBjb25zaXN0aW5nIG9mIGEgbm9kZSBhcyB3ZWxsIGFzIGFuIGVkZ2UgZGF0YWZyYW1lLiAKKiBXZSBjb3VsZCBmb3Igc3VyZSBhbHNvIGNyZWF0ZSBpdCBiYXNlZCBvbiBvdXIgaW5pdGlhbCBub2RlLSBhbmQgZWRnZWxpc3QuCgpgYGB7cn0KZyA8LSB0YmxfZ3JhcGgoZWRnZXMgPSBlZGdlX2xpc3QsIG5vZGVzID0gbm9kZV9saXN0LCBkaXJlY3RlZCA9IEZBTFNFKQpgYGAKCiogKipOb3RlOioqIFRoZSBgdGJsX2dyYXBoYCBjbGFzcyBpcyBhIHRoaW4gd3JhcHBlciBhcm91bmQgYW4gYGlncmFwaGAgb2JqZWN0IHRoYXQgcHJvdmlkZXMgbWV0aG9kcyBmb3IgbWFuaXB1bGF0aW5nIHRoZSBncmFwaCB1c2luZyB0aGUgdGlkeSBBUEkuIAoqIEFzIGl0IGlzIGp1c3QgYSBzdWJjbGFzcyBvZiBgaWdyYXBoYCwgZXZlcnkgYGlncmFwaGAgbWV0aG9kIGFuZCBpdHMgc3ludGF4IHdpbGwgd29yayBhcyBleHBlY3RlZCBhbmQgY2FuIGJlIHVzZWQgaWYgbmVjZXNzYXJ5LiBIb3dldmVyLCBpdCBtaWdodCBjb252ZXJ0IGl0IGJhY2sgaW50byBhbiBvcmlnaW5hbCBgaWdyYXBoYCBvYmplY3QuCgpgYGB7cn0KVihnKQpgYGAKCiogSW4gYWRpdHRpb25hbiwgdGhlIGBhc190YmxfZ3JhcGgoKWAgZnVuY3Rpb24gYWxzbyBjYW4gdHJhbnNmb3JtIGRpZmZlcmVudCB0eXBlcyBvZiBuZXR3b3JrIGRhdGEgZnJvbSBvYmplY3RzIHN1Y2ggYXMgYGRhdGEuZnJhbWVgLCBgbWF0cml4YCwgYGRlbmRyb2dyYW1gLCBgaWdyYXBoYCwgZXRjLgoKIyMjIEFjZXNzaW5nIGFuZCBtYW5pcHVsYXRpbmcgbm9kZXMgYW5kIGVkZ2VzCgoqIEJ1dCBob3cgY2FuIGEgZ3JhcGggb2JqZWN0IGJlIG1hbmlwdWxhdGVkIHdpdGggYGRwbHlyYCBzeW50YXg/IAoqIFdlIGtub3cgdGhhdCBhIGdyYXBoIG9iamVjdCBjb250YWlucyBhbiBlZGdlIGFzIHdlbGwgYXMgbm9kZSBkYXRhZnJhbWUsIHNvIGNvbW1hbmRzIGxpa2UgYGcgJT4lIGZpbHRlcihuYW1lID09ICJQZXJuaWxsZSIpYCB3b3VsZCBiZSBjb25mdXNpbmcsIHNpbmNlIGl0IGlzIHVuY2xlYXIgaWYgd2UgcmVmZXIgdG8gbm9kZXMgb3IgZWRnZXMuIAoqIGB0aWR5Z3JhcGhgJ3Mgc29sdXRpb24gaGVyZSBhcmUgc2VsZWN0aXZlICoqYWN0aXZhdGlvbiBwaXBlcyoqOgogICAqIGAlTj4lYCBhY3RpdmF0ZXMgbm9kZXMgKHNob3J0IGZvciBsb25nZXIgYWx0ZXJuYXRpdmU6YCU+JSBhY3RpdmF0ZV9ub2RlcygpYCkKICAgKiBgJUU+JWAgYWN0aXZhdGVzIGVkZ2VzIChzaG9ydCBmb3IgbG9uZ2VyIGFsdGVybmF0aXZlOmAlPiUgYWN0aXZhdGVfZWRnZXMoKWApCiogQ29uc2VxdWVudGx5LCBmdW5jdGlvbnMgYXJlIGV4ZWN1dGVkIG9uIHRoZSBjdXJyZW50bHkgYWN0aXZlIGRhdGFmcmFtZSBvZiBlaXRoZXIgbm9kZXMgb3IgZWRnZXMuIAoqIFdpdGggdGhpcyBzaW1wbGUgc3ludGF4IHRyaWNrLCBncmFwaHMgYmVjb21lIHN1YmplY3QgdG8gbW9zdCBjb21tb25seSBrbm93biBkYXRhIG1hbmlwdWxhdGlvbiB3b3JrZmxvd3MgZm9yIHRhYnVsYXIgZGF0YS4KCmBgYHtyfQpnICVOPiUKICBmaWx0ZXIoZ2VuZGVyID09ICJGIikKYGBgCgoqIE5vdGUgdGhhdCBmaWx0ZXJpbmcgbm9kZXMgd2lsbCBzaW11bHRhbmVvdXNseSByZXN1bHQgaW4gYSBmaWx0ZXJpbmcgb2YgZWRnZXMuIFdlIGZvciBzdXJlIGNhbiBhbHNvIGRvIG1hbmlwdWxhdGluZ3Mgb24gYm90aCBub2RlcyBhbmQgZWRnZXMgaW4gb25lIHBpcGVsaW5lLgoKYGBge3J9CmcgJU4+JQogIGZpbHRlcihncm91cCAlaW4lIGMoIkEiLCAiQiIpKSAlRT4lCiAgZmlsdGVyKHRvID09IDIpCmBgYAoKKiBOb3RlIHRoYXQgdGhlIGZpbHRlcmluZyBvZiBlZGdlcyBkaWQgbm90IHJlZHVjZSB0aGUgbm9kZXNldC4gV2hpbGUgbm9kZXMgY2FuIGJlIGlzb2xhdGVkIGluIGEgbmV3b3JrLCBlZGdlcyB3aXRob3V0IGFuIGFkamFjZW50IG5vZGUgY2Fubm90IGV4aXN0LgoqIFdlIGNhbiBhbHNvIHB1bGwgdGhlIHZpcnR1YWwgbm9kZSBvciBlZGdlIGRhdGFmcmFtZSBvdXQgb2YgdGhlIGB0YmxfZ3JhcGhgIGFuZCB1c2UgaXQgZm9yIHRhYnVsYXIgYW5hbHlzaXMuCgpgYGB7cn0KZyAlTj4lCiAgZmlsdGVyKGdyb3VwID09ICJCIikgJT4lCiAgYXNfdGliYmxlKCkKYGBgCgoKIyMjIFZpc3VhbGl6YXRpb24gKHByZXZpZXcpCgoqIE9uZSBsYXN0IHRoaW5nIGZvciBub3c6IFdoaWxlIGBpZ3JhcGhgIGFsc28gcHJvdmlkZXMgYSBwb3dlcmZ1bCBuZXR3b3JrIHZpc3VhbGl6YXRpb24gZnVuY3Rpb25hbGl0eSwgSSB3aWxsIGFsc28gbW9zdGx5IGdvIHdpdGggVGhvbWFzIHNpc3RlciBwYWNrYWdlLCBbYGdncmFwaGBdKGh0dHBzOi8vZ2l0aHViLmNvbS90aG9tYXNwODUvZ2dyYXBoKSwgd2hpY2ggcHJvdmlkZXMgYSBuZXR3b3JrIHZpc3VhbGl6YXRpb24gaW50ZXJmYWNlIGNvbXBhdGlibGUgYW5kIGNvbnNpc3RlbnQgd2l0aCBgZ2dwbG90MmAKKiBUaGUgcmVzdCB3b3JrcyBsaWtlIGFueSBgZ2dwbG90YCBmdW5jdGlvbiBjYWxsLCBqdXN0IHRoYXQgd2UgdXNlIHNwZWNpYWwgZ2VvbXMgZm9yIG91ciBuZXR3b3JrCiogRmlyIGluc3RhbmNlLCB3ZSB1c2U6IGBnZW9tX2VkZ2VfZGVuc2l0eSgpYCB0byBkcmF3IGEgc2hhZG93IHdoZXJlIHRoZSBlZGdlIGRlbnNpdHkgaXMgaGlnaGVyLCBgZ2VvbV9lZGdlX2xpbmsoKWAgdG8gY29ubmVjdCBlZGdlcyB3aXRoIGEgc3RyYWlnaHQgbGluZSwgYGdlb21fbm9kZV9wb2ludCgpYCB0byBkcmF3IG5vZGUgcG9pbnRzIGFuZCBgZ2VvbV9ub2RlX3RleHQoKWAgdG8gZHJhdyB0aGUgbGFiZWxzLiBNb3JlIG9wdGlvbnMgY2FuIGJlIGZvdW5kIGhlcmUuCgpgYGB7cn0KbGlicmFyeShnZ3JhcGgpCmBgYAoKYGBge3J9CmcgJT4lIGdncmFwaChsYXlvdXQgPSAibmljZWx5IikgKyAKICBnZW9tX2VkZ2VfbGluaygpICsgCiAgZ2VvbV9ub2RlX3BvaW50KCkgKyAKICBnZW9tX25vZGVfdGV4dChhZXMobGFiZWwgPSBuYW1lKSkKYGBgCgpOb3QgdmVyeSBpbXByZXNzaXZlIHVwIHRvIG5vdywgYnV0IHdhaXQgZm9yIHRoZSByZWFsIHN0dWZmIHRvIGNvbWUgaW4gbGF0ZXIgc2Vzc2lvbnMuCgojIE5ldHdvcmsgYW5hbHlzaXMgYW5kIG1lYXN1cmVzCgpgYGB7cn0KIyBnZW5lcmF0ZSBhIHNhbXBsZSBuZXR3b3JrOiBwbGF5X3NtYWxsd29ybGQoKSBDcmVhdGUgZ3JhcGhzIGJhc2VkIG9uIHRoZSBXYXR0cy1TdHJvZ2F0eiBzbWFsbC0gd29ybGQgbW9kZWwuCnNldC5zZWVkKDEzMzcpCmcgPC0gcGxheV9iYXJhYmFzaV9hbGJlcnQobiA9IDIwMCwgIyBOdW1iZXIgb2Ygbm9kZXMKICAgICAgICAgICAgICAgICAgICAgICAgICBwb3dlciA9IDAuNzUsICMgUG93ZXIgb2YgcHJlZmVyZW50aWFsIGF0dGFjaG1lbnQgZWZmZWN0CiAgICAgICAgICAgICAgICAgICAgICAgICAgZGlyZWN0ZWQgPSBGQUxTRSAjIFVuZGlyZWN0ZWQgbmV0d29yawogICAgICAgICAgICAgICAgICAgICAgICAgICkgICAgICAgICAgICAgIApgYGAKCmBgYHtyfQpzZXQuc2VlZCgxMzM3KQpnICU+JQogICAgZ2dyYXBoKGxheW91dCA9ICJmciIpICsgCiAgICBnZW9tX2VkZ2VfbGluaygpICsgCiAgICBnZW9tX25vZGVfcG9pbnQoKSArIAogICAgdGhlbWVfZ3JhcGgoKSAjIEFkZGluZyBgdGhlbWVfZ3JhcGgoKWAgaW50cm9kdWNlcyBhIHN0aWxlZ3VpZGUgYmV0dGVyIHN1aXRlZCBmb3IgcmdhcGhzCmBgYAoKCiMjIE5vZGUgbGV2ZWwgbWVhc3VyZXMKCkNlbnRyYWxpdGllcyBjYW4gYmUgZWFzaWx5IGNyZWF0ZWQgb24gbm9kZSBsZXZlbCB3aXQgdGhlIGBjZW50cmFsaXR5X1suLi5dYCBmdW5jdGlvbi4gQWxsIGNlbnRyYWxpdGllcyBhdmFpbGFibGUgY2FuIGJlIGZvdW5kIFtoZXJlXShodHRwczovL3RpZHlncmFwaC5kYXRhLWltYWdpbmlzdC5jb20vcmVmZXJlbmNlL2luZGV4Lmh0bWwpCgpgYGB7cn0KZyA8LSBnICVOPiUKICBtdXRhdGUoY2VudHJhbGl0eV9kZ3IgPSBjZW50cmFsaXR5X2RlZ3JlZSgpLAogICAgICAgICBjZW50cmFsaXR5X2VpZ2VuID0gY2VudHJhbGl0eV9laWdlbigpLAogICAgICAgICBjZW50cmFsaXR5X2JldHdlZW4gPSBjZW50cmFsaXR5X2JldHdlZW5uZXNzKCkpIApgYGAKCmBgYHtyfQpnICVOPiUKICBhc190aWJibGUoKSAlPiUgCiAgaGVhZCgpCmBgYAoKIyMjIyBEZWdyZWUgY2VudHJhbGl0eQoKYGBge3J9CnNldC5zZWVkKDEzMzcpCmcgJT4lCiAgICBnZ3JhcGgobGF5b3V0ID0gImZyIikgKyAKICAgIGdlb21fZWRnZV9saW5rKCkgKyAKICAgIGdlb21fbm9kZV9wb2ludChhZXMoc2l6ZSA9IGNlbnRyYWxpdHlfZGdyLCBjb2xvdXIgPSBjZW50cmFsaXR5X2RncikpICsgCiAgICBzY2FsZV9jb2xvcl9jb250aW51b3VzKGd1aWRlID0gImxlZ2VuZCIpICsgCiAgICB0aGVtZV9ncmFwaCgpCmBgYAoKIyMjIyBFaWdlbnZlY3RvciBjZW50cmFsaXR5CgpgYGB7cn0Kc2V0LnNlZWQoMTMzNykKZyAlPiUKICAgIGdncmFwaChsYXlvdXQgPSAiZnIiKSArIAogICAgZ2VvbV9lZGdlX2xpbmsoKSArIAogICAgZ2VvbV9ub2RlX3BvaW50KGFlcyhzaXplID0gY2VudHJhbGl0eV9laWdlbiwgY29sb3VyID0gY2VudHJhbGl0eV9laWdlbikpICsgCiAgICBzY2FsZV9jb2xvcl9jb250aW51b3VzKGd1aWRlID0gImxlZ2VuZCIpICsgCiAgICB0aGVtZV9ncmFwaCgpCmBgYAoKIyMjIyBCZXR3ZWVubmVzcyBjZW50cmFsaXR5CgpgYGB7cn0Kc2V0LnNlZWQoMTMzNykKZyAlPiUKICAgIGdncmFwaChsYXlvdXQgPSAiZnIiKSArIAogICAgZ2VvbV9lZGdlX2xpbmsoKSArIAogICAgZ2VvbV9ub2RlX3BvaW50KGFlcyhzaXplID0gY2VudHJhbGl0eV9iZXR3ZWVuLCBjb2xvdXIgPSBjZW50cmFsaXR5X2JldHdlZW4pKSArIAogICAgc2NhbGVfY29sb3JfY29udGludW91cyhndWlkZSA9ICJsZWdlbmQiKSArIAogICAgdGhlbWVfZ3JhcGgoKQpgYGAKCgojIyBDbHVzdGVyaW5nIChDb21tdW5pdHkgZGV0ZWN0aW9uKQoKKiBBbGwgY2x1c3RlcmluZyBhbGdvcml0aG1zIGZyb20gYGlncmFwaGAgYXJlIGF2YWlsYWJsZSBpbiB0aWR5Z3JhcGggdXNpbmcgdGhlIGBncm91cF8qYCBwcmVmaXguIAoqIEFsbCBvZiB0aGVzZSBmdW5jdGlvbnMgcmV0dXJuIGFuIGludGVnZXIgdmVjdG9yIHdpdGggbm9kZXMgKG9yIGVkZ2VzKSBzaGFyaW5nIHRoZSBzYW1lIGludGVnZXIgYmVpbmcgZ3JvdXBlZCB0b2dldGhlci4KKiBUaGVyZSBhcmUtanVzdCBsaWtlIGZvciBjbHVzdGVyaW4gb2YgdGFidWxhciBkYXRhIGluIFVNTC1tYW55IGRpZmZlcmVudCBhbGdvcml0aG1zIGFuZCBhcHByb2FjaGVzIHRvIAoKTGV0cyBpbGx1c3RyYXRlIAoKYGBge3J9CnNldC5zZWVkKDEzMzcpCiMgV2UgY3JlYXRlIGFuIGV4YW1wbGUgbmV0d29yawpnIDwtIHBsYXlfaXNsYW5kcyhuX2lzbGFuZHMgPSA1LCAjIAlUaGUgbnVtYmVyIG9mIGRlbnNlbHkgY29ubmVjdGVkIGlzbGFuZHMKICAgICAgICAgICAgICAgICAgc2l6ZV9pc2xhbmRzID0gMTUsICMgVGhlIG51bWJlciBvZiBub2RlcyBpbiBlYWNoIGlzbGFuZAogICAgICAgICAgICAgICAgICBwX3dpdGhpbiA9IDAuNzUsICMgVGhlIHByb2JhYmlsaXR5IG9mIGVkZ2VzIHdpdGhpbiBhbmQgYmV0d2VlbiBncm91cHMvYmxvY2tzCiAgICAgICAgICAgICAgICAgIG1fYmV0d2VlbiA9IDUgIyBUaGUgbnVtYmVyIG9mIGVkZ2VzIGJldHdlZW4gZ3JvdXBzL2lzbGFuZHMKICAgICAgICAgICAgICAgICAgKSAKYGBgCgpgYGB7cn0Kc2V0LnNlZWQoMTMzNykKIyBBcyBwbGFubmVkLCB3ZSBjbGVhcmVseSBzZWUgZGlzdGluY3QgY29tbXVuaXRpZXMKZyAlPiUgCiAgICBnZ3JhcGgobGF5b3V0ID0gJ2trJykgKyAKICAgIGdlb21fZWRnZV9saW5rKCkgKyAKICAgIGdlb21fbm9kZV9wb2ludChzaXplID0gNykgKyAKICAgIHRoZW1lX2dyYXBoKCkKYGBgCgpgYGB7cn0Kc2V0LnNlZWQoMTMzNykKIyBXZSBydW4gYSBjb21tdW5pdHkgZGV0ZWN0aW9uIHNpbXBseSB3aXRoIHRoZSBncm91cF8qIGZ1bmN0aW9uIG9mIHRpZHlncmFwaC4gaGVyZSwgdGhlIExvdmFpbiBhbGdvcml0aG0gaXMgYSB3ZWxsIHBlcmZvcm1pbmcgYW5kIGZhc3QgY2hvaWNlLgpnIDwtIGcgJU4+JSAKICAgIG11dGF0ZShjb21tdW5pdHkgPSBncm91cF9sb3V2YWluKCkgJT4lIGFzLmZhY3RvcigpKSAKYGBgCgpgYGB7cn0Kc2V0LnNlZWQoMTMzNykKIyBMZXRzIHNlZSBob3cgd2VsbCBpdCBkaWQuLi4KZyAlPiUgCiAgICBnZ3JhcGgobGF5b3V0ID0gJ2trJykgKyAKICAgIGdlb21fZWRnZV9saW5rKCkgKyAKICAgIGdlb21fbm9kZV9wb2ludChhZXMoY29sb3VyID0gY29tbXVuaXR5KSwgc2l6ZSA9IDcpICsgCiAgICB0aGVtZV9ncmFwaCgpCmBgYAoKIyMgTmVpZ2hib3Job29kIG9mIGEgTm9kZQoKKiBMZXRzIGNoZWNrIHRoZSBzaXplIG9mIGFsbCBub2RlcyBuZWlnaGJvcmhvb2QgYXQgZGlzdGFuY2UgMi4KCmBgYHtyfQpnICVOPiUKICBtdXRhdGUobmVpZ2hib3Job29kX3NpemUgPSBsb2NhbF9zaXplKG9yZGVyID0gMikpICU+JQogIGFzX3RpYmJsZSgpICU+JQogIGFycmFuZ2UoZGVzYyhuZWlnaGJvcmhvb2Rfc2l6ZSkpICU+JQogIGhlYWQoKQpgYGAKCicgV2UgY2FuIGFsc28gbm90IG9ubHkgbG9vayBhdCBpdCwgYnV0IHByb2R1Y2UgYSBuZXcgc3ViLWdyYXBoIG9ubHkgb2YgdGhpcyBlZ28tbmV0d29yay4gCicgSGVyZSwgd2UgbmVlZCB0byB1c2UgdGhlIGJhc2UgYGlncmFwaGAgZnVuY3Rpb24uIE5vdGUgdGhhdCBpdCBwcm9kdWNlcyBhbiBgaWdyYXBoYCBvYmplY3QsIHNvIHdlIGhhdmUgdG8gbWFrZSBhIGB0aWR5Z3JhcGhgIGFnYWluLi4uCgpgYGB7cn0KZzEgPC0gbWFrZV9lZ29fZ3JhcGgoZywgMSwgbm9kZXMgPSAxKVtbMV1dICU+JSBhc190YmxfZ3JhcGgoKQpnNTAgPC0gbWFrZV9lZ29fZ3JhcGgoZywgMSwgbm9kZXMgPSA1MClbWzFdXSAlPiUgYXNfdGJsX2dyYXBoKCkKYGBgCgpgYGB7cn0Kc2V0LnNlZWQoMTMzNykKZzEgJT4lIAogICAgZ2dyYXBoKGxheW91dCA9ICdraycpICsgCiAgICBnZW9tX2VkZ2VfbGluaygpICsgCiAgICBnZW9tX25vZGVfcG9pbnQoYWVzKGNvbG91ciA9IGNvbW11bml0eSksIHNpemUgPSA3KSArIAogICAgdGhlbWVfZ3JhcGgoKQpgYGAKCmBgYHtyfQpzZXQuc2VlZCgxMzM3KQpnNTAgJT4lIAogICAgZ2dyYXBoKGxheW91dCA9ICdraycpICsgCiAgICBnZW9tX2VkZ2VfbGluaygpICsgCiAgICBnZW9tX25vZGVfcG9pbnQoYWVzKGNvbG91ciA9IGNvbW11bml0eSksIHNpemUgPSA3KSArIAogICAgdGhlbWVfZ3JhcGgoKQpgYGAKCiMjIChHbG9iYWwpIE5ldHdvcmsgc3RydWN0dXJlCgoqIEZpbmFsbHksIGl0IGlzIG9mdGVuIGFsc28gaW5mb3JtYXRpdmUgdG8gbG9vayBhdCB0aGUgb3ZlcmFsIGNoYXJhY3RlcmlzdGljcyBvZiB0aGUgbmV0d29yay4gV2Ugd2lsbCBkbyB0aGlzIGluIG1vcmUgZGV0YWlsIG5leHQgdGltZSwgYnV0IGp1c3Qgc28geW91IGtub3c6CgoqIFRoZSAqKmRlbnNpdHkqKiBvZiBhIG1lYXN1cmUgcmVwcmVzZW50cyB0aGUgc2hhcmUgb2YgYWxsIGNvbm5lY3RlZCB0byBhbGwgcG9zc2libGUgY29ubmVjdGlvbnMgaW4gdGhlIG5ldHdvcmsKCmBgYHtyfQplZGdlX2RlbnNpdHkoZykKYGBgCgoqKipUcmFuc2lzdGl2aXR5KiosIGFsc28gYyBhbGxlZCB0aGUgKipDbHVzdGVyaW5nIENlZmZpY2llbnQqKiBpbmRpY2F0ZXMgaG93IG11Y2ggdGhlIG5ldHdvcmsgdGVuZHMgdG8gYmUgbG9jYWxseSBjbHVzdGVyZWQuIAoqIFRoYXQgaXMgbWVhc3VyZWQgYnkgdGhlIHNoYXJlIG9mICoqY2xvc2VkIHRyaXBsZXRzKiouIEFnYWluLHcgZSB3aWxsIGRpZyBpbnRvIHRoYXQgbmV4dCB0aW1lLgoKYGBge3J9CnRyYW5zaXRpdml0eShnKQpgYGAKCiogVGhlICoqZGlhbWV0ZXIqKiBpcyB0aGUgbG9uZ2VzdCBvZiB0aGUgc2hvcnRlc3QgcGF0aHMgYmV0d2VlbiB0d28gbm9kZXMgb2YgdGhlIG5ldHdvcmsuCgpgYGB7cn0KZGlhbWV0ZXIoZywgZGlyZWN0ZWQgPSBGLCB3ZWlnaHRzID0gTkEpCmBgYAoKKiBGaW5hbGx5LCB0aGUgKiptZWFuIGRpc3RhbmNlKiosIG9yICoqYXZlcmFnZSBwYXRoIGxlbmdodCoqIHJlcHJlc2VudHMgdGhlIG1lYW4gb2YgYWxsIHNob3J0ZXN0IHBhdGhzIGJldHdlZW4gYWxsIG5vZGVzLiBJdCBpcyBhIG1lYXN1cmUgb2YgZGlmZnVzaW9uIHBvdGVudGlhbCB3aXRoaW4gYSBuZXR3b3JrLgoKYGBge3J9Cm1lYW5fZGlzdGFuY2UoZywgZGlyZWN0ZWQgPSBGKQpgYGAKCgojIENhc2U6IE5ldHdvcmtzIGFyZSBjb21pbmcuLi4KCiFbXShodHRwczovL3Nkcy1hYXUuZ2l0aHViLmlvL1NEUy1tYXN0ZXIvMDBfbWVkaWEvcmFuZG9tX2dvdC5qcGcpCgoqIFNvLCBsZXRzIGdldCBzZXJpb3VzLiBBcHByb3ByaWF0ZSBmb3IgdGhlIHdlYXRoZXIgdGhlc2UgZGF5cyBpbiBEZW5tYXJrLCB0aGUgdGhlbWUgaXMgIndpbnRlciBpcyBjb21taW5nLi4uIi4KKiBUaGVyZWZvcmUsIHdlIHdpbGwgaGF2ZSBzb21lIGZ1biBhbmFseXNpbmcgdGhlIEdhbWUgb2YgVGhyb25lcyBkYXRhIHByb3ZpZGVkIGJ5IFtBbmRyZXcgQmV2ZXJpZGdlXShodHRwczovL2dpdGh1Yi5jb20vbWF0aGJldmVyaWRnZS9hc29pYWYpLiAKKiBJdCBpcyBhIENoYXJhY3RlciBJbnRlcmFjdGlvbiBOZXR3b3JrcyBmb3IgR2VvcmdlIFIuIFIuIE1hcnRpbidzICJBIFNvbmcgb2YgSWNlIGFuZCBGaXJlIiBzYWdhICh5ZXMsIHdlIGFyZSB0YWxraW5nIGFib3V0IHRoZSBib29rcy4uLikuIAoqIFRoZXNlIG5ldHdvcmtzIHdlcmUgY3JlYXRlZCBieSBjb25uZWN0aW5nIHR3byBjaGFyYWN0ZXJzIHdoZW5ldmVyIHRoZWlyIG5hbWVzIChvciBuaWNrbmFtZXMpIGFwcGVhcmVkIHdpdGhpbiAxNSB3b3JkcyBvZiBvbmUgYW5vdGhlciBpbiBvbmUgb2YgdGhlIGJvb2tzIGluICJBIFNvbmcgb2YgSWNlIGFuZCBGaXJlLiIgCiogVGhlIGVkZ2Ugd2VpZ2h0IGNvcnJlc3BvbmRzIHRvIHRoZSBudW1iZXIgb2YgaW50ZXJhY3Rpb25zLiAKKiBUaGlzIGlzIGEgbmljZSBza2lsbCB5b3Ugd2lsbCBoYXZlIGFmdGVyIHRoZSBzZWNvbmQgcGFydCBvZiBNMiBvbiB5b3VyIG93bi4KCiMjIEJ1aWxkIHRoZSBncmFwaAoKKiBGaXJzdCwgd2UgbG9hZCBhbGwgbm9kZXMsIHJlcHJlc2VudGluZyBhbGwgY2hhcmFjdGVycyBhcHBlYXJpbmcgaW4gdGhlIGJvb2tzOgoKYGBge3J9CmVkZ2VzIDwtIHJlYWRfY3N2KCdodHRwczovL3Nkcy1hYXUuZ2l0aHViLmlvL1NEUy1tYXN0ZXIvMDBfZGF0YS9Hb1RfbmV0d29yay9hc29pYWYtYWxsLWVkZ2VzLmNzdicpIApgYGAKCmBgYHtyfQplZGdlcyAlPiUgaGVhZCgpCmBgYAoKYGBge3J9CmNvbG5hbWVzKGVkZ2VzKSA8LSB0b2xvd2VyKGNvbG5hbWVzKGVkZ2VzKSkKYGBgCgoKKiBTbywgdGhhdCdzIHdoYXQgd2UgaGF2ZSwgYSBjbGFzc2ljYWwgZWRnZWxpc3QsIHdpdGggaWQxIGluIGNvbHVtbiAxIGFuZCBpZDIgaW4gY29sdW1uMi4gCiogTm90ZSwgdGhlIGVkZ2VzIGFyZSBpbiB0aGlzIGNhc2Ugd2VpZ2h0ZWQuIAoKT2ssIGxldHMgc2VlIGhvdyBtYW55IGNoYXJhY3RlcnMgd2UgaGF2ZSBvdmVyYWwuCgpgYGB7cn0Kbl9kaXN0aW5jdChjKGVkZ2VzJHNvdXJjZSwgZWRnZXMkdGFyZ2V0KSkKYGBgCgoqIEJlY2F1c2UgdGhlcmUgYXJlIHNvIG1hbnkgY2hhcmFjdGVycyBpbiB0aGUgYm9va3MsIG1hbnkgb2YgdGhlbSBtaW5vciwgCiogSSBhbSBzdWJzZXR0aW5nIHRoZSBkYXRhIHRvIHRoZSAxMDAgY2hhcmFjdGVycyB3aXRoIHRoZSBtb3N0IGludGVyYWN0aW9ucyBhY3Jvc3MgYWxsIGJvb2tzLiAKKiBUaGUgZWRnZXMgYXJlIHVuZGlyZWN0ZWQsIHRoZXJlZm9yZSB0aGVyZSBhcmUgbm8gcmVkdW5kYW50IFNvdXJjZS1UYXJnZXQgY29tYmluYXRpb25zLgoqIEJlY2F1c2Ugb2YgdGhpcywgSSBwaXZvdCBTb3VyY2UgYW5kIFRhcmdldCBkYXRhIGJlZm9yZSBzdW1taW5nIHVwIHRoZSB3ZWlnaHRzLgoKCmBgYHtyfQpjaGFyc19tYWluIDwtIGVkZ2VzICU+JQogIHNlbGVjdCgtdHlwZSkgJT4lCiAgcGl2b3RfbG9uZ2VyKHNvdXJjZTp0YXJnZXQpICU+JQogIGdyb3VwX2J5KHZhbHVlKSAlPiUKICBzdW1tYXJpc2Uoc3VtX3dlaWdodCA9IHN1bSh3ZWlnaHQpKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgYXJyYW5nZShkZXNjKHN1bV93ZWlnaHQpKSAlPiUKICBzbGljZSgxOjEwMCkgJT4lCiAgcmVuYW1lKG5hbWUgPSB2YWx1ZSkKYGBgCgpgYGB7cn0KaGVhZChjaGFyc19tYWluKQpgYGAKCiogU28gZmFyIHNvIGdvb2QsIGlmIHdlIG9ubHkgZ28gYnkgZWRnZSB3ZWlnaHRzLCAKKiBMZXRzIHJlZHVjZSBvdXIgZWRnZWxpc3QgdG8gdGhpcyBtYWluIGNoYXJhY3RlcnMsIGp1c3QgdG8gd2FybSB1cCBhbmQga2VlcCB0aGUgb3ZlcnZpZXcuCgpgYGB7cn0KZWRnZXMgJTw+JQogIGZpbHRlcihzb3VyY2UgJWluJSBjaGFyc19tYWluJG5hbWUgJiB0YXJnZXQgJWluJSBjaGFyc19tYWluJG5hbWUpICU+JQogIHNlbGVjdChzb3VyY2UsIHRhcmdldCwgd2VpZ2h0KSAlPiUKICByZW5hbWUoZnJvbSA9IHNvdXJjZSwKICAgICAgICAgdG8gPSB0YXJnZXQpCiMgTm90ZTogU2luY2UgaXQgaXMgc21hbGwgZGF0YSwgdGhpcyB3YXkgd2l0aCAlaW4lIGlzIG9rLiBIb3dldmVyLCB3aXRoIGxhcmdlIGRhdGFzZXRzIEkgd291bGQgZmlsdGVyIHZpYSBzZW1pX2pvaW4oKSBpbnN0ZWFkIChtb3JlIGVmZmljaWVudCkKYGBgCgpOb3cgd2UgY2FuIGNvbnZlcnQgb3VyIGVkZ2VsaXN0IGludG8gYSBgdGJsX2dyYXBoYCBvYmplY3Qgc3RydWN0dXJlLiAKCmBgYHtyfQpnIDwtIGVkZ2VzICU+JSBhc190YmxfZ3JhcGgoZGlyZWN0ZWQgPSBGQUxTRSkKYGBgCgpgYGB7cn0KZwpgYGAKCiogV2UgY2FuIHVzZSBzb21lIG9mIHRoZSBgdGlkeWdyYXBoYCBoZWxwZXJzIHRvIGJyaWVmbHkgY2xlYW4gdGhlIGdyYXBoLiBDaGVjayBgP25vZGVfaXNfKmAgYW5kIGA/ZWRnZV9pc18qYCBmb3Igb3B0aW9ucy4KCmBgYHtyfQojIEZpbHRlcmluZyBvdXQgbXVsdGlwbGUgZWRnZXMgYW5kIGlzb2xhdGVkIG5vZGVzICh1bmNvbm5lY3RlZCksIGluIGNhc2UgdGhlcmUgYXJlIHNvbWUKZyA8LSBnICVFPiUKICBmaWx0ZXIoIWVkZ2VfaXNfbXVsdGlwbGUoKSkgJU4+JQogIGZpbHRlcighbm9kZV9pc19pc29sYXRlZCgpKSAKYGBgCgoqIE5vdGUgdGhhdCB0aGUgZWRnZXMgaW4gdGhpcyBncmFwaCBhcmUgd2VpZ2h0ZWQuIFdlIGNhbiBicmllZmx5IGxvb2sgYXQgdGhlIHdlaWdodCBkaXN0cmlidXRpb246CgpgYGB7cn0KZyAlRT4lCiAgYXNfdGliYmxlKCkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gd2VpZ2h0KSkgKwogIGdlb21faGlzdG9ncmFtKCkKYGBgCgpXZSBzZWUgYSByaWdodCBza2V3ZWQgZGlzdHJpYnV0aW9uIHdpdGggbWFueSB3ZWFrIGFuZCBzb21lIHZlcnkgc3Ryb25nIGVkZ2VzLiBMZXRzIHRha2UgYSBsb29rIHdoYXQgYXJlIHRoZSBlZGdlcyB3aXRoIHRoZSBoaWdoZXN0IHdlaWdodCAobWVhbmluZyBoZXJlOiB0aGUgY2hhcmFjdGVycyB3aXRoIG1vc3QgaW50cmFjdGlvbikuCgpgYGB7cn0KZyAlRT4lCiAgYXNfdGliYmxlKCkgJT4lCiAgYXJyYW5nZShkZXNjKHdlaWdodCkpICU+JQogIGhlYWQoKQpgYGAKCmB0aWR5Z3JhcGhgIGFsd2F5cyB1c2VzIG51bWVyaWMgSURzIGZvciBub2Rlcywgd2hpY2ggYXJlIGFsc28gbGFiZWxpbmcgdGhlIGVkZ2VzLiBUaGlzIGlzIG5vdCB2ZXJ5IGhlbHBmdWwgdG8gZ2V0IGluc2lnaHRzLiBTbywgbGV0cyB0YWtlIHRoZSBub2RlIG5hbWVzIGluIGluc3RlYWQuCgpgYGB7cn0KIyBXZSBhY2Nlc3MgdGhlIG5vZGVzIGRpcmVjdGx5IHZpYSAuTigpLiBUaGUgc2FtZSBjYW4gYmUgZG9uZSBmb3IgZWRnZXMgd2l0aCAuRSgpIGFuZCB0aGUgZ3JhcGggd2l0aCAuRygpLiBDaGVjayA/Y29udGV4dF9hY2Nlc3NvcnMgZm9yIG1vcmUgaW5mb3MKZyAlRT4lCiAgbXV0YXRlKG5hbWVfZnJvbSA9IC5OKCkkbmFtZVtmcm9tXSwKICAgICAgICAgbmFtZV90byA9IC5OKCkkbmFtZVt0b10pICU+JQogIGFzX3RpYmJsZSgpICU+JQogIHNlbGVjdChuYW1lX2Zyb20sIG5hbWVfdG8sIHdlaWdodCkgJT4lCiAgYXJyYW5nZShkZXNjKHdlaWdodCkpICU+JQogIGhlYWQoKQpgYGAKCiMjIE5vZGUgQ2hhcmFjdGVyaXN0aWNzCgpgYGB7cn0KZyA8LSBnICVOPiUKICBtdXRhdGUoY2VudHJhbGl0eV9kZ3IgPSBjZW50cmFsaXR5X2RlZ3JlZSh3ZWlnaHRzID0gd2VpZ2h0KSwKICAgICAgICAgY2VudHJhbGl0eV9laWdlbiA9IGNlbnRyYWxpdHlfZWlnZW4od2VpZ2h0cyA9IHdlaWdodCksCiAgICAgICAgIGNlbnRyYWxpdHlfYmV0d2VlbiA9IGNlbnRyYWxpdHlfYmV0d2Vlbm5lc3Mod2VpZ2h0cyA9IHdlaWdodCkpIApgYGAKCmBgYHtyfQpiaW5kX2NvbHMoZyAlTj4lCiAgICAgICAgICAgIHNlbGVjdChuYW1lLCBjZW50cmFsaXR5X2RncikgJT4lCiAgICAgICAgICAgIGFycmFuZ2UoZGVzYyhjZW50cmFsaXR5X2RncikpICU+JQogICAgICAgICAgICBhc190aWJibGUoKSwKICAgICAgICAgIGcgJU4+JQogICAgICAgICAgICBzZWxlY3QobmFtZSwgY2VudHJhbGl0eV9laWdlbikgJT4lCiAgICAgICAgICAgIGFycmFuZ2UoZGVzYyhjZW50cmFsaXR5X2VpZ2VuKSkgJT4lCiAgICAgICAgICAgIGFzX3RpYmJsZSgpLAogICAgICAgICAgZyAlTj4lCiAgICAgICAgICAgIHNlbGVjdChuYW1lLCBjZW50cmFsaXR5X2JldHdlZW4pICU+JQogICAgICAgICAgICBhcnJhbmdlKGRlc2MoY2VudHJhbGl0eV9iZXR3ZWVuKSkgJT4lCiAgICAgICAgICAgIGFzX3RpYmJsZSgpKSAlPiUKICBtdXRhdGVfaWYoaXMubnVtZXJpYywgcm91bmQsIDEpICU+JQogIGhlYWQoKQpgYGAKCiMjIENvbW11bml0aWVzICYgR3JvdXBzCgpgYGB7cn0KZyA8LSBnICVOPiUgCiAgICBtdXRhdGUoY29tbXVuaXR5ID0gZ3JvdXBfbG91dmFpbigpICU+JSBhcy5mYWN0b3IoKSkgCmBgYAoKYGBge3J9CmcgJU4+JQogIHNlbGVjdChuYW1lLCBjb21tdW5pdHksIGNlbnRyYWxpdHlfZGdyKSAlPiUKICBhc190aWJibGUoKSAlPiUgCiAgYXJyYW5nZShjb21tdW5pdHksIGRlc2MoY2VudHJhbGl0eV9kZ3IpKSAlPiUKICBncm91cF9ieShjb21tdW5pdHkpICU+JQogIHNsaWNlKDE6NSkgJT4lIG11dGF0ZShuID0gMTo1KSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgc2VsZWN0KC1jZW50cmFsaXR5X2RncikgJT4lCiAgc3ByZWFkKGNvbW11bml0eSwgbmFtZSkKICAgIApgYGAKCiMjIE5ldHdvcmsgVmlzdWFsaXphdGlvbiBJCgpPaywgbGV0cyBnaXZlIGl0IGEgZmlyc3QgbWluaW1hbCBzaG90OgoKYGBge3J9CmcgJT4lIGdncmFwaChsYXlvdXQgPSAiZnIiKSArIAogICAgZ2VvbV9lZGdlX2xpbmsoKSArIAogICAgZ2VvbV9ub2RlX3BvaW50KCkgKwogIGdlb21fbm9kZV90ZXh0KGFlcyhsYWJlbCA9IG5hbWUpKSAKYGBgCgpOb3QgdmVyeSBleGNpdGluZy4gTWF5YmUgd2UgY2FuIGRvIGEgYml0IGJldHRlciwgdXNpbmcgbW9yZSBvcHRpb25zIGluIHRoZSBgZ2dyYXBoYCBmdW5jdGlvbmFsaXR5IHRvIHZpc3VhbGl6ZSBhc3BlY3RzIG9mIHRoZSBuZXR3b3JrLgoKYGBge3IsIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD0xMH0KZyAlRT4lIAogIGZpbHRlcih3ZWlnaHQgPj0gcXVhbnRpbGUod2VpZ2h0LCAwLjUpKSAlTj4lCiAgZmlsdGVyKCFub2RlX2lzX2lzb2xhdGVkKCkpICU+JQogIGdncmFwaChsYXlvdXQgPSAiZnIiKSArIAogICAgZ2VvbV9lZGdlX2xpbmsoYWVzKHdpZHRoID0gd2VpZ2h0KSwgYWxwaGEgPSAwLjIpICsgCiAgICBnZW9tX25vZGVfcG9pbnQoYWVzKGNvbG9yID0gY29tbXVuaXR5LCBzaXplID0gY2VudHJhbGl0eV9laWdlbikpICsKICAgIGdlb21fbm9kZV90ZXh0KGFlcyhsYWJlbCA9IG5hbWUsIHNpemUgPSBjZW50cmFsaXR5X2VpZ2VuKSwgcmVwZWwgPSBUUlVFKSArCiAgICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZSA9ICJTZXQxIikgKwogICAgdGhlbWVfZ3JhcGgoKSArCiAgICBsYWJzKHRpdGxlID0gIkEgU29uZyBvZiBJY2UgYW5kIEZpcmUgY2hhcmFjdGVyIG5ldHdvcmsiLAogICAgICAgICBzdWJ0aXRsZSA9ICJOb2RlcyBhcmUgY29sb3JlZCBieSBjb21tdW5pdHkiKQpgYGAgICAgICAgIAoKIyMgWW91ciB0dXJuClBsZWFzZSBkbyAqKkV4ZXJjaXNlIDEqKiBpbiB0aGUgY29ycmVzcG9uZGluZyBzZWN0aW9uIG9uIGBHaXRodWJgLgoKIyBFbmRub3RlcwoKIyMjIFBhY2thZ2VzICYgRWNvc3lzdGVtCgoqIHRpZHlncmFwaCBbaGVyZV0oaHR0cHM6Ly90aWR5Z3JhcGguZGF0YS1pbWFnaW5pc3QuY29tLykKKiBnZ3JhcGggW2hlcmVdKGh0dHBzOi8vd3d3LmRhdGEtaW1hZ2luaXN0LmNvbS8yMDE3L2Fubm91bmNpbmctZ2dyYXBoLykKCiMjIyBTdWdnZXN0aW9ucyBmb3IgZnVydGhlciBzdHVkeQoKKiBEYXRhQ2FtcCAoIUFsbCBjb3Vyc2VzIGhhdmUgc29tZXdoYXQgb3V0ZGF0ZWQgZWNvc3lzdGVtcykKICAgKiBbTmV0d29yayBBbmFseXNpcyBpbiBSXShodHRwczovL2xlYXJuLmRhdGFjYW1wLmNvbS9jb3Vyc2VzL25ldHdvcmstYW5hbHlzaXMtaW4tcik6IEdvb2QgZm9yIHNvbWUgb2YgdGhlIGJhc2ljcwogICAqIFtDYXNlIFN0dWRpZXM6IE5ldHdvcmsgQW5hbHlzaXMgaW4gUl0oaHR0cHM6Ly9sZWFybi5kYXRhY2FtcC5jb20vY291cnNlcy9jYXNlLXN0dWRpZXMtbmV0d29yay1hbmFseXNpcy1pbi1yKTogR29vZCB0byBnZXQgc29tZSBmaXJzdCBwcmFjdGljZS4KICAgKiBbUHJlZGljdGl2ZSBBbmFseXRpY3MgdXNpbmcgTmV0d29ya2VkIERhdGEgaW4gUl0oaHR0cHM6Ly9sZWFybi5kYXRhY2FtcC5jb20vY291cnNlcy9wcmVkaWN0aXZlLWFuYWx5dGljcy11c2luZy1uZXR3b3JrZWQtZGF0YS1pbi1yKTogCiAgICogRnVydGhlcjogQSBEYXRhY2FtcCBQeXRob24gcHJvamVjdCBmb3IgdGhlIHNhbWUgKEdvVCkgZGF0YSBzZXQgW2hlcmVdKGh0dHBzOi8vd3d3LmRhdGFjYW1wLmNvbS9wcm9qZWN0cy83Nj91dG1fY2FtcGFpZ249YnJvYWRjYXN0JnV0bV9tZWRpdW09YnJvYWRjYXN0XzgmdXRtX3NvdXJjZT1tYWluKQoKICAKIyMjIFNlc3Npb24gSW5mbwoKYGBge3J9CnNlc3Npb25JbmZvKCkKYGBgCgoKCgoK