1 Introduction

This RMarkdown document is part of the Generic Skills Component (GSK) of the Course of the Foundation Studies Programme at Srishti Manipal Institute of Art, Design, and Technology, Bangalore India. The material is based on A Layered Grammar of Graphics by Hadley Wickham. The course is meant for First Year students pursuing a Degree in Art and Design.

The intent of this GSK part is to build Skill in coding in R, and also appreciate R as a way to metaphorically visualize information of various kinds, using predominantly geometric figures and structures.

All RMarkdown files combine code, text, web-images, and figures developed using code. Everything is text; code chunks are enclosed in fences (```)

2 Goals

At the end of this Lab session, we should:

  • know the types and structures of network data and be able to work with them
  • understand the basics of modern network packages in R
  • be able to create network visualizations using tidygraph, ggraph( static visualizations ) and visNetwork (interactive visualizations)
  • see directions for how the network metaphor applies in a variety of domains (e.g. biology/ecology, ideas/influence, technology, transportation, to name a few)

3 Pedagogical Note

The method followed will be based on PRIMM:

  • PREDICT Inspect the code and guess at what the code might do, write predictions
  • RUN the code provided and check what happens
  • INFER what the parameters of the code do and write comments to explain. What bells and whistles can you see?
  • MODIFY the parameters code provided to understand the options available. Write comments to show what you have aimed for and achieved.
  • MAKE : take an idea/concept of your own, and graph it.

4 Set Up

The setup code chunk below brings into our coding session R packages that provide specific computational abilities and also datasets which we can use.

To reiterate: Packages and datasets are not the same thing !! Packages are (small) collections of programs. Datasets are just….information.

5 Graph Metaphors

Network graphs are characterized by two key terms: nodes and edges

  1. Nodes : Entities

    • Metaphors: Individual People? Things? Ideas? Places? to be connected in the network.
    • Synonyms: vertices. Nodes have IDs.
  2. Edges: Connections

    • Metaphors: Interactions? Relationships? Influence? Letters sent and received? Dependence? between the entities.
    • Synonyms: links, ties.

In R, we create network representations using node and edge information. One way in which these could be organized are:

  • Node list: a data frame with a single column listing the node IDs found in the edge list. You can also add attribute columns to the data frame such as the names of the nodes or grouping variables.
Node Table
ID Node Name Attribute? Qualities? Categories? Family? Country? Planet? Race?
1 Ned Nursery School Teacher
2 Jaguar Paw Main Character in Apocalypto
3 John Snow Epidemiologist
  • Edge list: data frame containing two columns: source node and destination node of an edge. Source and Destination have node IDs.
  • Weighted network graph: An edge list can also contain additional columns describing attributes of the edges such as a magnitude aspect for an edge. If the edges have a magnitude attribute the graph is considered weighted.
Edges Table
From To Relationship Weight
1 3 Financial Dealings 6
2 1 History Lessons 2
2 3 Vaccination 15
  • Layout: A geometric arrangement of nodes and edges.

    • Metaphors: Location? Spacing? Distance? Coordinates? Colour? Shape? Size? Provides visual insight due to the arrangement.
  • Layout Algorithms : Method to arranges nodes and edges with the aim of optimizing some metric .

    • Metaphors: Nodes are masses and edges are springs. The Layout algorithm minimizes the stretching and compressing of all springs.(BTW, are the Spring Constants K the same for all springs?…)
  1. Directed and undirected network graph: If the distinction between source and target is meaningful, the network is directed.

    If the distinction is not meaningful, the network is undirected.

    Directed edges represent an ordering of nodes, like a relationship extending from one node to another, where switching the direction would change the structure of the network.

    Undirected edges are simply links between nodes where order does not matter.

Examples:

  • The World Wide Web is an example of a directed network because hyperlinks connect one Web page to another, but not necessarily the other way around.

  • Co-authorship networks represent examples of un-directed networks, where nodes are authors and they are connected by an edge if they have written a publication together

  • When people send e-mail to each other, the distinction between the sender (source) and the recipient (target) is clearly meaningful, therefore the network is directed.

  1. Connected and Disconnected graphs: If there is some path from any node to any other node, the Networks is said to be Connected. Else, Disconnected.

6 Predict/Run/Infer -1

6.1 Using tidygraph and ggraph

tidygraph and ggraph are modern R packages for network data. Graph Data setup and manipulation is done in tidygraph and graph visualization with ggraph.

  • tidygraph Node + Edge Data -> “Network Object” in R.
  • ggraph Network Object -> Plots using a chosen layout/algo.

Both leverage the power of igraph, which is the Big Daddy of all network packages. We will be using the Grey’s Anatomy dataset in our first foray into networks.

6.2 Step1. Read the data

Save these two CSV files in your project’s data folder.

NOTE: these are actually semicolon separated values files, so our familiar read_csv() will not work. We will need to use read_delim() and use the argument delim = ";" .

Remove the eval = FALSE in the code chunk options below:

grey_nodes <- read_delim("data/grey_nodes.csv",delim = ";")
grey_edges <- read_delim("data/grey_edges.csv", delim = ";")

Let’s look at what we have read in:

grey_nodes
## # A tibble: 54 × 7
##    name               sex   race  birthyear position  season sign    
##    <chr>              <chr> <chr>     <dbl> <chr>      <dbl> <chr>   
##  1 Addison Montgomery F     White      1967 Attending      1 Libra   
##  2 Adele Webber       F     Black      1949 Non-Staff      2 Leo     
##  3 Teddy Altman       F     White      1969 Attending      6 Pisces  
##  4 Amelia Shepherd    F     White      1981 Attending      7 Libra   
##  5 Arizona Robbins    F     White      1976 Attending      5 Leo     
##  6 Rebecca Pope       F     White      1975 Non-Staff      3 Gemini  
##  7 Jackson Avery      M     Black      1981 Resident       6 Leo     
##  8 Miranda Bailey     F     Black      1969 Attending      1 Virgo   
##  9 Ben Warren         M     Black      1972 Other          6 Aquarius
## 10 Henry Burton       M     White      1972 Non-Staff      7 Cancer  
## # … with 44 more rows
grey_edges
## # A tibble: 57 × 4
##    from            to              weight type        
##    <chr>           <chr>            <dbl> <chr>       
##  1 Leah Murphy     Arizona Robbins      2 friends     
##  2 Leah Murphy     Alex Karev           4 benefits    
##  3 Lauren Boswell  Arizona Robbins      1 friends     
##  4 Arizona Robbins Callie Torres        1 friends     
##  5 Callie Torres   Erica Hahn           6 friends     
##  6 Callie Torres   Alex Karev          12 benefits    
##  7 Callie Torres   Mark Sloan           5 professional
##  8 Callie Torres   George O'Malley      2 professional
##  9 George O'Malley Izzie Stevens        3 professional
## 10 George O'Malley Meredith Grey        4 friends     
## # … with 47 more rows

6.2.1 Questions and Inferences #1:

Look at the console output thumbnail. What attributes (i.e. extra information columns) are seen for Nodes and Edges? Understand the data in both nodes and edges as shown in the second and third thumbnails. Write some comments and inferences here.

6.3 Step 2.Create a network object using tidygraph:

Key function:

  • tbl_graph(): (aka “tibble graph”).
  • Key arguments: nodes, edges and directed.
  • Note this is a very versatile command and can take many input forms, such as data structures that result from other packages. Type ?tbl_graph in the Console and see the Usage section.

NOTE: ggplot needs a dataframe/tibble to start its plotting, as you recall. The graph plotting package equivalent of ggplot is ggraph. It needs a “dataframe” that is suitable for network plotting. This is the network object created by tidygraph.

ga <- tbl_graph(nodes = grey_nodes, 
                edges = grey_edges, 
                directed = FALSE)
ga
## # A tbl_graph: 54 nodes and 57 edges
## #
## # An undirected simple graph with 4 components
## #
## # Node Data: 54 × 7 (active)
##   name               sex   race  birthyear position  season sign  
##   <chr>              <chr> <chr>     <dbl> <chr>      <dbl> <chr> 
## 1 Addison Montgomery F     White      1967 Attending      1 Libra 
## 2 Adele Webber       F     Black      1949 Non-Staff      2 Leo   
## 3 Teddy Altman       F     White      1969 Attending      6 Pisces
## 4 Amelia Shepherd    F     White      1981 Attending      7 Libra 
## 5 Arizona Robbins    F     White      1976 Attending      5 Leo   
## 6 Rebecca Pope       F     White      1975 Non-Staff      3 Gemini
## # … with 48 more rows
## #
## # Edge Data: 57 × 4
##    from    to weight type    
##   <int> <int>  <dbl> <chr>   
## 1     5    47      2 friends 
## 2    21    47      4 benefits
## 3     5    46      1 friends 
## # … with 54 more rows

6.3.1 Questions and Inferences #2:

What new information does the graph object contain? Have the attributes of the nodes and edges been properly included?

6.4 Step 3. Plot using ggraph

3a. Quick Plot: autograph() This is to check quickly is the data is imported properly and to decide upon going on to a more elaborate plotting.

autograph(ga)

6.4.1 Questions and Inferences #3:

Describe this graph, in simple words here. Try to use some of the new domain words we have just acquired: nodes/edges, connected/disconnected, directed/undirected.

3b. More elaborate plot

Key functions:

  • ggraph(layout = "......"): Create classic node-edge diagrams; i.e. Sets up the graph. Rather like ggplot for networks!

Two kinds of geom: one set for nodes, and another for edges

  • geom_node_point(aes(.....)): Draws node as “points”. Alternatives are circle / arc_bar / tile / voronoi. Remember the geoms that we have seen before in Grammar of Graphics!

  • geom_edge_link(aes(.....)): Draws edges as “links”. Alternatives are arc / bend / elbow / hive / loop / parallel / diagonal / point / span /tile.

  • geom_node_text(aes(label = ......), repel = TRUE): Adds text labels (non-overlapping). Alternatives are label /...

  • labs(title = "....", subtitle = "....", caption = "...."): Change main titles, axis labels and legend titles. We know this from our work with ggplot.

# Write Comments next to each line 
# About what that line does for the overall graph

ggraph(graph = ga,  layout = "kk") +
  #
  
  geom_edge_link(width = 2, 
                 color = "pink") +
  #
  
  geom_node_point(
    shape = 21,
    size = 6,
    fill = "blue",
    color = "green",
    stroke = 2
  ) +
  #
  
  labs(title = "Whoo Hoo! My first silly Grey's Anatomy graph in R!",
       subtitle = "Why did Kumar put me in this course...",
       caption = "Bro, they are doing **cool** things in the other classes...")

6.4.2 Questions and Inferences #4:

What parameters have been changed here, compared to the earlier graph? Where do you see these changes in the code above?

Let us Play with this graph and see if we can make some small changes. Colour? Fill? Width? Size? Stroke? Labels? Of course!

# Change the parameters in each of the commands here to new ones
# Use fixed values for colours or sizes...etc. 

ggraph(graph = ga,  layout = "kk") + 
  geom_edge_link(width = 1,
                 color = "springgreen2") + 
  geom_node_point(shape = 25, 
                  size = 4, 
                  fill = "burlywood", 
                  color = "firebrick", 
                  stroke = 1) +
  labs(title = "Whoo Hoo! My next silly Grey's Anatomy graph in R!",
       subtitle = "Why did Kumar put me in this course...",
       caption = "Bro, they are still doing cool things in the other classes...")

6.4.3 Questions and Inferences #5:

What did the shape parameter achieve? What are the possibilities with shape? How about including alpha?

3c. Aesthetic Mapping from Node and Edge attribute columns

Up to now, we have assigned specific numbers to geometric aesthetics such as shape and size. Now we are ready ( maybe ?) change the meaning and significance of the entire graph and each element within it, and use aesthetics / metaphoric mappings to achieve new meanings or insights. Let us try using aes() inside each geom to map a variable to a geometric aspect.

Don’t try to use more than 2 aesthetic mappings simultaneously!!

The node elements we can tweak are:

  • Types of Nodes: geom_node_****()
  • Node Parameters: inside geom_node_****(aes(...............))
    • aes(alpha = node-variable) : opacity; a value between 0 and 1

    • aes(shape = node-variable) : node shape

    • aes(colour = node-variable) : node colour

    • aes(fill = node-variable) : fill colour for node

    • aes(size = node-variable) : size of node

The edge elements we can tweak are:

  • Type of Edges” geom_edge_****()
  • Edge Parameters: inside geom_edge_****(aes(...............))
    • aes(colour = edge-variable) : colour of the edge

    • aes(width = edge-variable) : width of the edge

    • aes(label = some_variable) : labels for the edge

Type ?geom_node_point and ?geom-edge_link in your Console for more information.

# Look at the column names for Edges and Nodes again. 
# Which column is which kind ( Qual or Quant?)
names(grey_edges)
## [1] "from"   "to"     "weight" "type"
names(grey_nodes)
## [1] "name"      "sex"       "race"      "birthyear" "position"  "season"    "sign"
# OK, let's modify some aesthetics
# 
ggraph(graph = ga, layout = "fr") +
  geom_edge_link0(aes(width = weight)) + # add mapping here
  
  geom_node_point(aes(color = race), size = 6) + # add mapping here

  # geom_node_label(aes(label = name), # modify this mapping
  #                 repel = TRUE, max.overlaps = 20,
  #                 alpha = 0.6,
  #                 size = 3) +

  labs(title = "Whoo Hoo! Yet another Grey's Anatomy graph in R!")

6.4.4 Questions and Inferences #6:

Describe some of the changes here. What types of edges worked? Which variables were you able to use for nodes and edges and how? What did not work with either of the two?

6.5 Playing with Layout

# Arc diagram
ggraph(ga, layout = "linear") +
  geom_edge_arc(aes(width = weight), alpha = 0.8) +
  scale_edge_width(range = c(0.2, 2)) +
  geom_node_point(size = 2, colour = "red") +
  labs(edge_width = "Weight") +
  theme_graph()+
  theme(legend.position = "top")

6.5.1 Questions and Inferences #7:

How does this graph look “metaphorically” different? Do you see a difference in the relationships between people here? Why?

# Coord diagram, circular
ggraph(ga, layout = "linear", circular = TRUE) + # Seems weird? ;-D
  geom_edge_arc(aes(width = weight), alpha = 0.8) + 
  scale_edge_width(range = c(0.2, 2)) +
  geom_node_point(size = 4,colour = "red") + 
  geom_node_text(aes(label = name),repel = TRUE, 
                 size = 3,
                 max.overlaps = 20) +
  labs(edge_width = "Weight") +
  
  theme_graph()+
  theme(legend.position = "right", 
        aspect.ratio = 1)

6.5.2 Questions and Inferences #8:

How does this graph look “metaphorically” different? Do you see a difference in the relationships between people here? Why?

7 Hierarchical layouts

These provide for some alternative metaphorical views of networks. Note that not all layouts are possible for all datasets!!

# setting theme_graph from ggraph
set_graph_style()

# This dataset contains the graph that describes the class 
# hierarchy for the Flare visualization library.
# Type ?flare in your Console
head(flare$vertices)
##                                           name size             shortName
## 1 flare.analytics.cluster.AgglomerativeCluster 3938  AgglomerativeCluster
## 2   flare.analytics.cluster.CommunityStructure 3812    CommunityStructure
## 3  flare.analytics.cluster.HierarchicalCluster 6714   HierarchicalCluster
## 4            flare.analytics.cluster.MergeEdge  743             MergeEdge
## 5  flare.analytics.graph.BetweennessCentrality 3534 BetweennessCentrality
## 6           flare.analytics.graph.LinkDistance 5731          LinkDistance
head(flare$edges)
##                      from                                           to
## 1 flare.analytics.cluster flare.analytics.cluster.AgglomerativeCluster
## 2 flare.analytics.cluster   flare.analytics.cluster.CommunityStructure
## 3 flare.analytics.cluster  flare.analytics.cluster.HierarchicalCluster
## 4 flare.analytics.cluster            flare.analytics.cluster.MergeEdge
## 5   flare.analytics.graph  flare.analytics.graph.BetweennessCentrality
## 6   flare.analytics.graph           flare.analytics.graph.LinkDistance
# flare class hierarchy
graph = tbl_graph(edges = flare$edges, nodes = flare$vertices)

# dendrogram
ggraph(graph, layout = "dendrogram") + 
  geom_edge_diagonal() + 
  labs(title = "Dendrogram")

# circular dendrogram
ggraph(graph, layout = "dendrogram", circular = TRUE) + 
  geom_edge_diagonal() + 
  geom_node_point(aes(filter = leaf)) + 
  coord_fixed()+ 
  labs(title = "Circular Dendrogram")

# rectangular tree map
ggraph(graph, layout = "treemap", weight = size) + 
  geom_node_tile(aes(fill = depth), size = 0.25) + 
  labs(title = "Rectangular Tree Map")

# circular tree map
ggraph(graph, layout = "circlepack", weight = size) + 
  geom_node_circle(aes(fill = depth), size = 0.25, n = 50) + 
  coord_fixed() + 
  labs(title = "Circular Tree Map")

# icicle
ggraph(graph, layout = "partition") + 
  geom_node_tile(aes(y = -y, fill = depth))

# sunburst (circular icicle)
ggraph(graph, layout = "partition", circular = TRUE) +
  geom_node_arc_bar(aes(fill = depth)) +
  coord_fixed() + 
  labs(title = "Circular Icicle")

7.0.1 Questions and Inferences #9:

How do graphs look “metaphorically” different? Do they reveal different aspects of the group? How?

7.1 Faceting

Faceting allows to create sub-plots according to the values of a qualitative attribute on nodes or edges.

# setting theme_graph 
set_graph_style()


# facet edges by type
ggraph(ga,layout = "linear", circular = TRUE) + 
  geom_edge_link(aes(color = type)) + 
  geom_node_point() +
  facet_edges(~ type)

# facet nodes by sex
ggraph(ga,layout = "linear", circular = TRUE) + 
  geom_edge_link() + 
  geom_node_point() +
  facet_nodes(~race)

# facet both nodes and edges
ggraph(ga,layout = "linear", circular = TRUE) + 
  geom_edge_link(aes(color = type)) + 
  geom_node_point() +
  facet_graph(type ~ race) + 
  th_foreground(border = TRUE)

7.1.1 Questions and Inferences #10:

Does splitting up the main graph into sub-networks give you more insight? Describe some of these.

8 Network analysis with tidygraph

The data frame graph representation can be easily augmented with metrics or statistics computed on the graph. Remember how we computed counts with the penguin dataset in Grammar of Graphics.

Before computing a metric on nodes or edges use the activate() function to activate either node or edge data frames. Use dplyr verbs (filter, arrange, mutate) to achieve your computation in the proper way.

8.1 Network Centrality

Centrality is a an “ill-defined” metric of node and edge importance in a network. It is therefore calculated in many ways.

Type ?centrality in your Console.

Standards

Let’s add a few columns to the nodes and edges based on network centrality measures:

ga_centrality <- ga %>% 
  activate(nodes) %>% 
  
  # Most connections?
  mutate(degree = centrality_degree(mode = c("in"))) %>% 
  filter(degree > 0) %>% 
  
  activate(edges) %>% 
  # "Busiest" edge?
  mutate(betweenness = centrality_edge_betweenness())

ga_centrality %>% activate(nodes) %>% as_tibble()
## # A tibble: 54 × 8
##    name               sex   race  birthyear position  season sign     degree
##    <chr>              <chr> <chr>     <dbl> <chr>      <dbl> <chr>     <dbl>
##  1 Addison Montgomery F     White      1967 Attending      1 Libra         3
##  2 Adele Webber       F     Black      1949 Non-Staff      2 Leo           1
##  3 Teddy Altman       F     White      1969 Attending      6 Pisces        4
##  4 Amelia Shepherd    F     White      1981 Attending      7 Libra         2
##  5 Arizona Robbins    F     White      1976 Attending      5 Leo           3
##  6 Rebecca Pope       F     White      1975 Non-Staff      3 Gemini        1
##  7 Jackson Avery      M     Black      1981 Resident       6 Leo           3
##  8 Miranda Bailey     F     Black      1969 Attending      1 Virgo         4
##  9 Ben Warren         M     Black      1972 Other          6 Aquarius      1
## 10 Henry Burton       M     White      1972 Non-Staff      7 Cancer        1
## # … with 44 more rows
ga_centrality %>% activate(edges) %>% as_tibble()
## # A tibble: 57 × 5
##     from    to weight type         betweenness
##    <int> <int>  <dbl> <chr>              <dbl>
##  1     5    47      2 friends             20.3
##  2    21    47      4 benefits            44.7
##  3     5    46      1 friends             39  
##  4     5    41      1 friends             66.3
##  5    18    41      6 friends             39  
##  6    21    41     12 benefits            91.5
##  7    37    41      5 professional       164. 
##  8    31    41      2 professional        98.8
##  9    20    31      3 professional        47.2
## 10    17    31      4 friends            102. 
## # … with 47 more rows

Let us plot the graph with the (numerical) Centrality measuring colouring the Nodes:

# setting theme_graph 
set_graph_style()

ga_centrality %>% # Start with onr new Network Object
  
  # Now to continue with plotting
  ggraph(layout = "nicely") +
  geom_edge_link(aes(alpha = betweenness)) +
  
  geom_node_point(aes(size = degree, 
                      colour = degree)) + 
  
  # discrete colour legend
  scale_color_gradient(guide = "legend",
                       low = "blue", 
                       high = "red") +
  
  scale_edge_alpha(range = c(0.06, 1))

8.2 Analyse and visualize network communities

Who is close to whom? Which are the groups you can see?

# visualize communities of nodes
# Add a column, a Categorical Column to show 
# Community Membership
# 
ga_community <- ga %>% 
  activate(nodes) %>%
  mutate(community = as.factor(group_louvain())) 

ga_community %>% activate(nodes) %>% as_tibble()
## # A tibble: 54 × 8
##    name               sex   race  birthyear position  season sign     community
##    <chr>              <chr> <chr>     <dbl> <chr>      <dbl> <chr>    <fct>    
##  1 Addison Montgomery F     White      1967 Attending      1 Libra    4        
##  2 Adele Webber       F     Black      1949 Non-Staff      2 Leo      5        
##  3 Teddy Altman       F     White      1969 Attending      6 Pisces   2        
##  4 Amelia Shepherd    F     White      1981 Attending      7 Libra    2        
##  5 Arizona Robbins    F     White      1976 Attending      5 Leo      1        
##  6 Rebecca Pope       F     White      1975 Non-Staff      3 Gemini   1        
##  7 Jackson Avery      M     Black      1981 Resident       6 Leo      7        
##  8 Miranda Bailey     F     Black      1969 Attending      1 Virgo    6        
##  9 Ben Warren         M     Black      1972 Other          6 Aquarius 6        
## 10 Henry Burton       M     White      1972 Non-Staff      7 Cancer   2        
## # … with 44 more rows

Now let us plot the Communities, and colour the nodes based on which Community they belong with:

# setting theme_graph 
set_graph_style()


ga_community %>% 
  ggraph(layout = "graphopt") + 
  geom_edge_link() + 
  geom_node_point(aes(color = community), 
                  size = 5) +
    geom_node_text(aes(label = name),
                   repel = TRUE, 
                   size = 2,
                   nudge_x = 3, nudge_y = 3) +
  scale_colour_brewer(palette = "Accent")

We can also place names or labels in place of the shapes, for each node:

# setting theme_graph 
set_graph_style()


ga_community %>% 
  ggraph(layout = "graphopt") + 
  geom_edge_link0() + 

    geom_node_label(aes(label = name, fill = community), 
                    size = 3, repel = TRUE) +
  scale_fill_brewer(palette = "Accent")

8.3 Interactive Graphs with visNetwork

Exploring the VisNetwork package. Make graphs wiggle and shake using tidy commands! The package implements interactivity using the physical metaphor of weights and springs we discussed earlier.

The visNetwork() function uses a nodes list and edges list to create an interactive graph. The nodes list must include an "id" column, and the edge list must have "from" and "to" columns. The function also plots the labels for the nodes, using the names of the cities from the "label" column in the node list.

library(visNetwork)

# Prepare the data for plotting by visNetwork
grey_nodes
## # A tibble: 54 × 7
##    name               sex   race  birthyear position  season sign    
##    <chr>              <chr> <chr>     <dbl> <chr>      <dbl> <chr>   
##  1 Addison Montgomery F     White      1967 Attending      1 Libra   
##  2 Adele Webber       F     Black      1949 Non-Staff      2 Leo     
##  3 Teddy Altman       F     White      1969 Attending      6 Pisces  
##  4 Amelia Shepherd    F     White      1981 Attending      7 Libra   
##  5 Arizona Robbins    F     White      1976 Attending      5 Leo     
##  6 Rebecca Pope       F     White      1975 Non-Staff      3 Gemini  
##  7 Jackson Avery      M     Black      1981 Resident       6 Leo     
##  8 Miranda Bailey     F     Black      1969 Attending      1 Virgo   
##  9 Ben Warren         M     Black      1972 Other          6 Aquarius
## 10 Henry Burton       M     White      1972 Non-Staff      7 Cancer  
## # … with 44 more rows
grey_edges
## # A tibble: 57 × 4
##    from            to              weight type        
##    <chr>           <chr>            <dbl> <chr>       
##  1 Leah Murphy     Arizona Robbins      2 friends     
##  2 Leah Murphy     Alex Karev           4 benefits    
##  3 Lauren Boswell  Arizona Robbins      1 friends     
##  4 Arizona Robbins Callie Torres        1 friends     
##  5 Callie Torres   Erica Hahn           6 friends     
##  6 Callie Torres   Alex Karev          12 benefits    
##  7 Callie Torres   Mark Sloan           5 professional
##  8 Callie Torres   George O'Malley      2 professional
##  9 George O'Malley Izzie Stevens        3 professional
## 10 George O'Malley Meredith Grey        4 friends     
## # … with 47 more rows

Let us relabel greys anatomy nodes and edges for use with VisNetwork:

grey_nodes_vis <- grey_nodes %>% 
  rowid_to_column(var = "id") %>% 
  rename("label" = name) %>% 
  mutate(sex = case_when(sex == "F" ~ "Female",
                         sex == "M" ~ "Male")) %>% 
  replace_na(., list(sex = "Transgender?")) %>% 
  rename("group" = sex)
grey_nodes_vis
## # A tibble: 54 × 8
##       id label              group  race  birthyear position  season sign    
##    <int> <chr>              <chr>  <chr>     <dbl> <chr>      <dbl> <chr>   
##  1     1 Addison Montgomery Female White      1967 Attending      1 Libra   
##  2     2 Adele Webber       Female Black      1949 Non-Staff      2 Leo     
##  3     3 Teddy Altman       Female White      1969 Attending      6 Pisces  
##  4     4 Amelia Shepherd    Female White      1981 Attending      7 Libra   
##  5     5 Arizona Robbins    Female White      1976 Attending      5 Leo     
##  6     6 Rebecca Pope       Female White      1975 Non-Staff      3 Gemini  
##  7     7 Jackson Avery      Male   Black      1981 Resident       6 Leo     
##  8     8 Miranda Bailey     Female Black      1969 Attending      1 Virgo   
##  9     9 Ben Warren         Male   Black      1972 Other          6 Aquarius
## 10    10 Henry Burton       Male   White      1972 Non-Staff      7 Cancer  
## # … with 44 more rows

And now for the edges:

grey_edges_vis <- grey_edges %>% 
  select(from, to) %>% 
  left_join(., grey_nodes_vis, 
            by = c("from" = "label")) %>% 
  left_join(., grey_nodes_vis, 
            by = c("to" = "label")) %>%
  select("from"= id.x, "to" = id.y)
grey_edges_vis
## # A tibble: 57 × 2
##     from    to
##    <int> <int>
##  1    47     5
##  2    47    21
##  3    46     5
##  4     5    41
##  5    41    18
##  6    41    21
##  7    41    37
##  8    41    31
##  9    31    20
## 10    31    17
## # … with 47 more rows

We will be using fontawesome icons to plot the Nodes in this graph!!

grey_nodes_vis %>%

  visNetwork(nodes = ., edges = grey_edges_vis) %>% 
  visNodes(font = list(size = 40)) %>% 
  
  # Colour and icons for each of the gender-groups
  visGroups(groupname = "Female", shape = "icon", 
            icon = list(code = "f182", size = 75, color = "tomato"),
            shadow = list(enabled = TRUE)) %>% 
  
  visGroups(groupname = "Male", shape = "icon", 
            icon = list(code = "f183", size = 75, color = "slateblue"), 
            shadow = list(enabled = TRUE)) %>% 
  
  visGroups(groupname = "Transgender?", shape = "icon", 
            icon = list(code = "f22c", size = 75, color = "fuchsia"), 
            shadow = list(enabled = TRUE)) %>% 
  
  #visLegend() %>%
  #Add the fontawesome icons!!
  addFontAwesome() %>% 
  
  # Add Interaction Controls
  visInteraction(navigationButtons = TRUE,
                 hover = TRUE,
                 selectConnectedEdges = TRUE,
                 hoverConnectedEdges = TRUE,
                 zoomView = TRUE)

There is another family of icons available in visNetwork, called ionicons. Let’s see how they look:

grey_nodes_vis %>%

  visNetwork(nodes = ., edges = grey_edges_vis,) %>%
  visLayout(randomSeed = 12345) %>%
  visNodes(font = list(size = 50)) %>%
  visEdges(color = "green") %>%
  visGroups(
    groupname = "Female",
    shape = "icon",
    icon = list(
      face = 'Ionicons',
      code = "f25d",
      color = "fuchsia",
      size = 125
    )
  ) %>%
  
  visGroups(
    groupname = "Male",
    shape = "icon",
    icon = list(
      face = 'Ionicons',
      code = "f202",
      color = "green",
      size = 125
    )
  ) %>%
  
  visGroups(
    groupname = "Transgender?",
    shape = "icon",
    icon = list(
      face = 'Ionicons',
      code = "f233",
      color = "dodgerblue",
      size = 125
    )
  ) %>%
  visLegend() %>%
  addIonicons() %>%
  visInteraction(
    navigationButtons = TRUE,
    hover = TRUE,
    selectConnectedEdges = TRUE,
    hoverConnectedEdges = TRUE,
    zoomView = TRUE
  )

Some idea of interactivity and controls with visNetwork:

Download this StarWars datasets and store them in your /data folder. Again, note these are semicolon separated values files are before:

library(visNetwork)
# let's look again at the data
starwars_nodes <- read_delim("./Data/star-wars-network-nodes.csv",
                             delim = ";")

starwars_edges <- read_delim("./Data/star-wars-network-edges.csv",
                             delim = ";")

starwars_edges
starwars_nodes
# We need to rename starwars nodes dataframe and edge dataframe columns for visNetwork
starwars_nodes_vis <- 
  starwars_nodes %>% 
  rename("label" = name)

# Convert from and to columns to **node ids**
starwars_edges_vis <- 
  starwars_edges %>% 
  
  # Matching Source <- Source Node id ("id.x")
  left_join(., starwars_nodes_vis, by = c("source" = "label")) %>% 
  
  # Matching Target <- Target Node id ("id.y")
  left_join(., starwars_nodes_vis, by = c("target" = "label")) %>% 
  
# Select "id.x" and "id.y" ONLY
# Rename them as "from" and "to"
# keep "weight" column for aesthetics of edges
  select("from" = id.x, "to" = id.y, "value" = weight)

# Check everything once
starwars_nodes_vis
## # A tibble: 22 × 2
##    label          id
##    <chr>       <dbl>
##  1 R2-D2           0
##  2 CHEWBACCA       1
##  3 C-3PO           2
##  4 LUKE            3
##  5 DARTH VADER     4
##  6 CAMIE           5
##  7 BIGGS           6
##  8 LEIA            7
##  9 BERU            8
## 10 OWEN            9
## # … with 12 more rows
starwars_edges_vis
## # A tibble: 60 × 3
##     from    to value
##    <dbl> <dbl> <dbl>
##  1     2     0    17
##  2     3     0    13
##  3    10     0     6
##  4     7     0     5
##  5    13     0     5
##  6     1     0     3
##  7    16     0     1
##  8     1    10     7
##  9     2     1     5
## 10     1     3    16
## # … with 50 more rows

Ok, let’s make things move and shake!!

visNetwork(nodes = starwars_nodes_vis,
           edges = starwars_edges_vis) %>% 
  visNodes(font = list(size = 30), shape = "icon", 
           icon = list(code = "f1e3", size = 75)) %>% 
  addFontAwesome() %>% 
  visEdges(color = "red")
visNetwork(nodes = starwars_nodes_vis,
           edges = starwars_edges_vis) %>% 
  visNodes(font = list(size = 30)) %>% 
  visEdges(color = "red")

9 Your Assignments:

9.1 Make-1 : With a ready made dataset

Step 1. Fire up a new R Project and a new RMarkdown within. Write your name, file_name and date.

Step 2. Take any one of the Datasets datasets described below.

Step 3. RMarkdown contents:

  • Introduce / Inspect in R your data and describe
  • Introduce your Purpose
  • Create graph objects.
  • Write comments in the code
  • Write narrative in text with sections, bold ,italic etc.

Step 4. Knit before you submit. Submit your entire Project as a .zip file.

9.2 Datasets:

  1. Airline Data:
  • As before check that you use read_delim() rather than read_csv(), as needed.
  1. The Famous Zachary Karate Club dataset
  • Start with pulling this data into your Rmarkdown:
data("karate",package= "igraphdata")
karate
  • Try ?karate in the console
  • Note that this is not a set of nodes, nor edges, but already a graph-object!
  • So no need to create a graph object using tbl_graph.
  • You will need to just go ahead and plot using ggraph.

9.3 Make-2: Literary Network with TV Show / Book / Story / Play

This is in groups. Groups of 4. To be announced

You need to create a Network Graph for your favourite Book, play, TV serial or Show. (E.g. Brooklyn99, Friends, BBT, or LB or HIMYM…or Hamlet, Little Women , Pride and Prejudice, or LoTR)

Step 0. Fire up a new RProject, and a new Rmd file within.

Step 1. Go to: Literary Networks for instructions. Make your data using the instructions.

  • In the nodes excel, use id and names as your columns. Any other details in other columns to the right.
  • In your edges Excel file, use from and to are your first columns. Entries in these columns can be names or ids but be consistent and don’t mix.
  • ENSURE SPELLINGS ARE CONSISTENT FOR THE NAMES

Step 3. Decide on 3 answers that you to seek and plan to make graphs for.

Step 4. Create graph objects. Say 3 visualizations.

Step 5. Write comments/answers in the code and narrative text. Add pictures from the web using Markdown syntax.

Step 6. Write Reflection (OK, a short one!) inside your RMarkdown. Make sure it knits!!

Step 7. Group Submission: Submit the entire Project as a zip file. Each person submits on their Assignments. All get the same grade on this one.

Ask me for clarifications on what to do after you have read the Instructions in your group.

10 Read more

Thomas Lin Pedersen - 1 giraffe, 2 giraffe,GO!

Igraph: Network Analysis and Visualization. https://CRAN.R-project.org/package=igraph.

Pedersen, Thomas Lin. 2017a. Ggraph: An Implementation of Grammar of Graphics for Graphs and Networks. https://CRAN.R-project.org/package=ggraph.

———. 2017b. Tidygraph: A Tidy Api for Graph Manipulation. https://CRAN.R-project.org/package=tidygraph.

Tyner, Sam, François Briatte, and Heike Hofmann. 2017. “Network Visualization with ggplot2.” The R Journal 9 (1): 27–59. https://journal.r-project.org/archive/2017/RJ-2017-023/index.html.

Network Datasets https://icon.colorado.edu/#!/networks

Yunran Chen, Introduction to Network Analysis Using R

LS0tDQp0aXRsZTogTGFiIDA3IC0gVGhlIEdyYW1tYXIgb2YgTmV0d29ya3MNCmF1dGhvcjogQXJ2aW5kIFZlbmthdGFkcmkNCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICB0aGVtZTogZmxhdGx5DQogICAgdG9jOiBUUlVFDQogICAgdG9jX2Zsb2F0OiBUUlVFDQogICAgdG9jX2RlcHRoOiAyDQogICAgbnVtYmVyX3NlY3Rpb25zOiBUUlVFDQogICAgY29kZV9mb2xkaW5nOiBoaWRlDQogICAgY29kZV9kb3dubG9hZDogVFJVRQ0KZWRpdG9yX29wdGlvbnM6IA0KICBtYXJrZG93bjogDQogICAgd3JhcDogNzINCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgd2FybmluZyA9IEZBTFNFKQ0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KIyBGb3IgR2VuZXJhbCBEYXRhIE1hbmlwdWxhdGlvbg0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KHZpc05ldHdvcmspDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQojIE5ldHdvcmsgQW5hbHlzaXMgTGlicmFyeSAoSGFuZGxlIGRhdGEgYW5kIFZpeikNCmxpYnJhcnkoaWdyYXBoKQ0KDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQojIEZvciBOZXR3b3JrICJNYW5pcHVsYXRpb24iDQpsaWJyYXJ5KHRpZHlncmFwaCkNCg0KIyBGb3IgTmV0d29yayBWaXN1YWxpemF0aW9uDQpsaWJyYXJ5KGdncmFwaCkNCmxpYnJhcnkoZ3JhcGhsYXlvdXRzKQ0KDQojIEZvciAiTmV0d29yayIgRGF0YXNldHMNCmxpYnJhcnkoaWdyYXBoZGF0YSkNCg0KbGlicmFyeShkb3dubG9hZHRoaXMpICMgT25seSBmb3IgeW91ciBUZWFjaGVyISBOb3QgZm9yIHlvdSENCg0KYGBgDQoNCiMgSW50cm9kdWN0aW9uDQoNClRoaXMgUk1hcmtkb3duIGRvY3VtZW50IGlzIHBhcnQgb2YgdGhlIEdlbmVyaWMgU2tpbGxzIENvbXBvbmVudCAoR1NLKSBvZg0KdGhlIENvdXJzZSBvZiB0aGUgRm91bmRhdGlvbiBTdHVkaWVzIFByb2dyYW1tZSBhdCBTcmlzaHRpIE1hbmlwYWwNCkluc3RpdHV0ZSBvZiBBcnQsIERlc2lnbiwgYW5kIFRlY2hub2xvZ3ksIEJhbmdhbG9yZSBJbmRpYS4gVGhlIG1hdGVyaWFsDQppcyBiYXNlZCBvbiAqQSBMYXllcmVkIEdyYW1tYXIgb2YgR3JhcGhpY3MqIGJ5IEhhZGxleSBXaWNraGFtLiBUaGUNCmNvdXJzZSBpcyBtZWFudCBmb3IgRmlyc3QgWWVhciBzdHVkZW50cyBwdXJzdWluZyBhIERlZ3JlZSBpbiBBcnQgYW5kDQpEZXNpZ24uDQoNClRoZSBpbnRlbnQgb2YgdGhpcyBHU0sgcGFydCBpcyB0byBidWlsZCBTa2lsbCBpbiBjb2RpbmcgaW4gUiwgYW5kIGFsc28NCmFwcHJlY2lhdGUgUiBhcyBhIHdheSB0byBtZXRhcGhvcmljYWxseSB2aXN1YWxpemUgaW5mb3JtYXRpb24gb2YgdmFyaW91cw0Ka2luZHMsIHVzaW5nIHByZWRvbWluYW50bHkgZ2VvbWV0cmljIGZpZ3VyZXMgYW5kIHN0cnVjdHVyZXMuDQoNCkFsbCBSTWFya2Rvd24gZmlsZXMgY29tYmluZSBjb2RlLCB0ZXh0LCB3ZWItaW1hZ2VzLCBhbmQgZmlndXJlcw0KZGV2ZWxvcGVkIHVzaW5nIGNvZGUuIEV2ZXJ5dGhpbmcgaXMgdGV4dDsgY29kZSBjaHVua3MgYXJlIGVuY2xvc2VkIGluDQoqKmZlbmNlcyoqIChcYFxgXGApDQoNCiMgR29hbHMNCg0KQXQgdGhlIGVuZCBvZiB0aGlzIExhYiBzZXNzaW9uLCB3ZSBzaG91bGQ6DQoNCi0gICBrbm93IHRoZSB0eXBlcyBhbmQgc3RydWN0dXJlcyBvZiBgbmV0d29yayBkYXRhYCBhbmQgYmUgYWJsZSB0byB3b3JrDQogICAgd2l0aCB0aGVtDQotICAgdW5kZXJzdGFuZCB0aGUgYmFzaWNzIG9mIG1vZGVybiBuZXR3b3JrIHBhY2thZ2VzIGluIFINCi0gICBiZSBhYmxlIHRvIGNyZWF0ZSBuZXR3b3JrIHZpc3VhbGl6YXRpb25zIHVzaW5nIGB0aWR5Z3JhcGhgLA0KICAgIGBnZ3JhcGhgKCBzdGF0aWMgdmlzdWFsaXphdGlvbnMgKSBhbmQgYHZpc05ldHdvcmtgIChpbnRlcmFjdGl2ZQ0KICAgIHZpc3VhbGl6YXRpb25zKQ0KLSAgIHNlZSBkaXJlY3Rpb25zIGZvciBob3cgdGhlIG5ldHdvcmsgbWV0YXBob3IgYXBwbGllcyBpbiBhIHZhcmlldHkgb2YNCiAgICBkb21haW5zIChlLmcuIGJpb2xvZ3kvZWNvbG9neSwgaWRlYXMvaW5mbHVlbmNlLCB0ZWNobm9sb2d5LA0KICAgIHRyYW5zcG9ydGF0aW9uLCB0byBuYW1lIGEgZmV3KQ0KDQojIFBlZGFnb2dpY2FsIE5vdGUNCg0KVGhlIG1ldGhvZCBmb2xsb3dlZCB3aWxsIGJlIGJhc2VkIG9uDQpbUFJJTU1dKGh0dHBzOi8vYmxvZ3Mua2NsLmFjLnVrL2NzZXIvMjAxNy8wOS8wMS9wcmltbS1hLXN0cnVjdHVyZWQtYXBwcm9hY2gtdG8tdGVhY2hpbmctcHJvZ3JhbW1pbmcvKToNCg0KLSAgICoqUFJFRElDVCoqIEluc3BlY3QgdGhlIGNvZGUgYW5kIGd1ZXNzIGF0IHdoYXQgdGhlIGNvZGUgbWlnaHQgZG8sDQogICAgKip3cml0ZSBwcmVkaWN0aW9ucyoqDQotICAgKipSVU4qKiB0aGUgY29kZSBwcm92aWRlZCBhbmQgY2hlY2sgd2hhdCBoYXBwZW5zDQotICAgKipJTkZFUioqIHdoYXQgdGhlIGBwYXJhbWV0ZXJzYCBvZiB0aGUgY29kZSBkbyBhbmQgKip3cml0ZSBjb21tZW50cw0KICAgIHRvIGV4cGxhaW4qKi4gV2hhdCBiZWxscyBhbmQgd2hpc3RsZXMgY2FuIHlvdSBzZWU/DQotICAgKipNT0RJRlkqKiB0aGUgYHBhcmFtZXRlcnNgIGNvZGUgcHJvdmlkZWQgdG8gdW5kZXJzdGFuZCB0aGUNCiAgICBgb3B0aW9uc2AgYXZhaWxhYmxlLiAqKldyaXRlIGNvbW1lbnRzKiogdG8gc2hvdyB3aGF0IHlvdSBoYXZlIGFpbWVkDQogICAgZm9yIGFuZCBhY2hpZXZlZC4NCi0gICAqKk1BS0UqKiA6IHRha2UgYW4gaWRlYS9jb25jZXB0IG9mIHlvdXIgb3duLCBhbmQgZ3JhcGggaXQuDQoNCiMgU2V0IFVwDQoNClRoZSBgc2V0dXBgIGNvZGUgKipjaHVuayoqIGJlbG93IGJyaW5ncyBpbnRvIG91ciBjb2Rpbmcgc2Vzc2lvbiAqKlINCnBhY2thZ2VzKiogdGhhdCBwcm92aWRlIHNwZWNpZmljIGNvbXB1dGF0aW9uYWwgYWJpbGl0aWVzIGFuZCBhbHNvDQoqKmRhdGFzZXRzKiogd2hpY2ggd2UgY2FuIHVzZS4NCg0KVG8gcmVpdGVyYXRlOiBQYWNrYWdlcyBhbmQgZGF0YXNldHMgYXJlICoqbm90KiogdGhlIHNhbWUgdGhpbmcgISENClBhY2thZ2VzIGFyZSAoc21hbGwpIGNvbGxlY3Rpb25zIG9mIHByb2dyYW1zLiBEYXRhc2V0cyBhcmUNCmp1c3QuLi4uaW5mb3JtYXRpb24uDQoNCiMgR3JhcGggTWV0YXBob3JzDQoNCk5ldHdvcmsgZ3JhcGhzIGFyZSBjaGFyYWN0ZXJpemVkIGJ5IHR3byBrZXkgdGVybXM6ICoqbm9kZXMqKiBhbmQNCioqZWRnZXMqKg0KDQoxLiAgYE5vZGVzYCA6ICoqRW50aXRpZXMqKg0KDQogICAgLSAgIE1ldGFwaG9yczogSW5kaXZpZHVhbCBQZW9wbGU/IFRoaW5ncz8gSWRlYXM/IFBsYWNlcz8gdG8gYmUNCiAgICAgICAgY29ubmVjdGVkIGluIHRoZSBuZXR3b3JrLg0KICAgIC0gICBTeW5vbnltczogYHZlcnRpY2VzYC4gTm9kZXMgaGF2ZSBgSURzYC4NCg0KMi4gIGBFZGdlc2A6ICoqQ29ubmVjdGlvbnMqKg0KDQogICAgLSAgIE1ldGFwaG9yczogSW50ZXJhY3Rpb25zPyBSZWxhdGlvbnNoaXBzPyBJbmZsdWVuY2U/IExldHRlcnMgc2VudA0KICAgICAgICBhbmQgcmVjZWl2ZWQ/IERlcGVuZGVuY2U/IGJldHdlZW4gdGhlIGVudGl0aWVzLg0KICAgIC0gICBTeW5vbnltczogYGxpbmtzYCwgYHRpZXNgLg0KDQpJbiBSLCB3ZSBjcmVhdGUgbmV0d29yayByZXByZXNlbnRhdGlvbnMgdXNpbmcgbm9kZSBhbmQgZWRnZSBpbmZvcm1hdGlvbi4NCipPbmUgd2F5KiBpbiB3aGljaCB0aGVzZSBjb3VsZCBiZSBvcmdhbml6ZWQgYXJlOg0KDQotICAgYE5vZGUgbGlzdGA6IGEgZGF0YSBmcmFtZSB3aXRoIGEgc2luZ2xlIGNvbHVtbiBsaXN0aW5nIHRoZSBub2RlIElEcw0KICAgIGZvdW5kIGluIHRoZSBlZGdlIGxpc3QuIFlvdSBjYW4gYWxzbyBhZGQgKiphdHRyaWJ1dGUgY29sdW1ucyoqIHRvDQogICAgdGhlIGRhdGEgZnJhbWUgc3VjaCBhcyB0aGUgbmFtZXMgb2YgdGhlIG5vZGVzIG9yIGdyb3VwaW5nIHZhcmlhYmxlcy4NCg0KfCAgICAgfCAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8LS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18DQp8IElEICB8IE5vZGUgTmFtZSAgfCBBdHRyaWJ1dGU/IFF1YWxpdGllcz8gQ2F0ZWdvcmllcz8gRmFtaWx5PyBDb3VudHJ5PyBQbGFuZXQ/IFJhY2U/IHwNCnwgMSAgIHwgTmVkICAgICAgICB8IE51cnNlcnkgU2Nob29sIFRlYWNoZXIgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCAyICAgfCBKYWd1YXIgUGF3IHwgTWFpbiBDaGFyYWN0ZXIgaW4gQXBvY2FseXB0byAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IDMgICB8IEpvaG4gU25vdyAgfCBFcGlkZW1pb2xvZ2lzdCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCg0KOiBOb2RlIFRhYmxlDQoNCi0gICBgRWRnZSBsaXN0YDogZGF0YSBmcmFtZSBjb250YWluaW5nIHR3byBjb2x1bW5zOiAqKnNvdXJjZSBub2RlKiogYW5kDQogICAgKipkZXN0aW5hdGlvbiBub2RlKiogb2YgYW4gYGVkZ2VgLiBTb3VyY2UgYW5kIERlc3RpbmF0aW9uIGhhdmUNCiAgICBgbm9kZSBJRHNgLg0KLSAgIGBXZWlnaHRlZCBuZXR3b3JrIGdyYXBoYDogQW4gZWRnZSBsaXN0IGNhbiBhbHNvIGNvbnRhaW4gYWRkaXRpb25hbA0KICAgIGNvbHVtbnMgZGVzY3JpYmluZyBhdHRyaWJ1dGVzIG9mIHRoZSBlZGdlcyBzdWNoIGFzIGEgbWFnbml0dWRlDQogICAgYXNwZWN0IGZvciBhbiBlZGdlLiBJZiB0aGUgZWRnZXMgaGF2ZSBhIG1hZ25pdHVkZSBhdHRyaWJ1dGUgdGhlDQogICAgZ3JhcGggaXMgY29uc2lkZXJlZCB3ZWlnaHRlZC4NCg0KfCAgICAgIHwgICAgIHwgICAgICAgICAgICAgICAgICAgIHwgICAgICAgIHwNCnwtLS0tLS18LS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS18DQp8IEZyb20gfCBUbyAgfCBSZWxhdGlvbnNoaXAgICAgICAgfCBXZWlnaHQgfA0KfCAxICAgIHwgMyAgIHwgRmluYW5jaWFsIERlYWxpbmdzIHwgNiAgICAgIHwNCnwgMiAgICB8IDEgICB8IEhpc3RvcnkgTGVzc29ucyAgICB8IDIgICAgICB8DQp8IDIgICAgfCAzICAgfCBWYWNjaW5hdGlvbiAgICAgICAgfCAxNSAgICAgfA0KDQo6IEVkZ2VzIFRhYmxlDQoNCi0gICBgTGF5b3V0YDogQSAqKmdlb21ldHJpYyoqIGFycmFuZ2VtZW50IG9mIGBub2Rlc2AgYW5kIGBlZGdlc2AuDQoNCiAgICAtICAgTWV0YXBob3JzOiBMb2NhdGlvbj8gU3BhY2luZz8gRGlzdGFuY2U/IENvb3JkaW5hdGVzPyBDb2xvdXI/DQogICAgICAgIFNoYXBlPyBTaXplPyBQcm92aWRlcyB2aXN1YWwgaW5zaWdodCBkdWUgdG8gdGhlIGBhcnJhbmdlbWVudGAuDQoNCi0gICBgTGF5b3V0IEFsZ29yaXRobXNgIDogYE1ldGhvZGAgdG8gYXJyYW5nZXMgYG5vZGVzYCBhbmQgYGVkZ2VzYCB3aXRoDQogICAgdGhlIGFpbSBvZiBvcHRpbWl6aW5nIHNvbWUgYG1ldHJpY2AgLg0KDQogICAgLSAgIE1ldGFwaG9yczogTm9kZXMgYXJlIGBtYXNzZXNgIGFuZCBlZGdlcyBhcmUgYHNwcmluZ3NgLiBUaGUNCiAgICAgICAgTGF5b3V0IGFsZ29yaXRobSBtaW5pbWl6ZXMgdGhlIHN0cmV0Y2hpbmcgYW5kIGNvbXByZXNzaW5nIG9mIGFsbA0KICAgICAgICBzcHJpbmdzLihCVFcsIGFyZSB0aGUgU3ByaW5nIENvbnN0YW50cyBLIHRoZSBzYW1lIGZvciBhbGwNCiAgICAgICAgc3ByaW5ncz8uLi4pDQoNCjguICBgRGlyZWN0ZWQgYW5kIHVuZGlyZWN0ZWQgbmV0d29yayBncmFwaGA6IElmIHRoZSBkaXN0aW5jdGlvbiBiZXR3ZWVuDQogICAgc291cmNlIGFuZCB0YXJnZXQgaXMgbWVhbmluZ2Z1bCwgdGhlIG5ldHdvcmsgaXMgKipkaXJlY3RlZC4qKg0KDQogICAgSWYgdGhlIGRpc3RpbmN0aW9uIGlzIG5vdCBtZWFuaW5nZnVsLCB0aGUgbmV0d29yayBpcyAqKnVuZGlyZWN0ZWQqKi4NCg0KICAgICoqRGlyZWN0ZWQgZWRnZXMqKiByZXByZXNlbnQgYW4gb3JkZXJpbmcgb2Ygbm9kZXMsIGxpa2UgYQ0KICAgIHJlbGF0aW9uc2hpcCBleHRlbmRpbmcgZnJvbSBvbmUgbm9kZSB0byBhbm90aGVyLCB3aGVyZSBzd2l0Y2hpbmcgdGhlDQogICAgZGlyZWN0aW9uIHdvdWxkIGNoYW5nZSB0aGUgc3RydWN0dXJlIG9mIHRoZSBuZXR3b3JrLg0KDQogICAgKipVbmRpcmVjdGVkIGVkZ2VzKiogYXJlIHNpbXBseSBsaW5rcyBiZXR3ZWVuIG5vZGVzIHdoZXJlIG9yZGVyIGRvZXMNCiAgICBub3QgbWF0dGVyLg0KDQpFeGFtcGxlczoNCg0KLSAgIFRoZSBXb3JsZCBXaWRlIFdlYiBpcyBhbiBleGFtcGxlIG9mIGEgKipkaXJlY3RlZCBuZXR3b3JrKiogYmVjYXVzZQ0KICAgIGh5cGVybGlua3MgY29ubmVjdCBvbmUgV2ViIHBhZ2UgdG8gYW5vdGhlciwgYnV0IG5vdCBuZWNlc3NhcmlseSB0aGUNCiAgICBvdGhlciB3YXkgYXJvdW5kLg0KDQotICAgQ28tYXV0aG9yc2hpcCBuZXR3b3JrcyByZXByZXNlbnQgZXhhbXBsZXMgb2YgKip1bi1kaXJlY3RlZA0KICAgIG5ldHdvcmtzKiosIHdoZXJlIG5vZGVzIGFyZSBhdXRob3JzIGFuZCB0aGV5IGFyZSBjb25uZWN0ZWQgYnkgYW4NCiAgICBlZGdlIGlmIHRoZXkgaGF2ZSB3cml0dGVuIGEgcHVibGljYXRpb24gdG9nZXRoZXINCg0KLSAgIFdoZW4gcGVvcGxlIHNlbmQgZS1tYWlsIHRvIGVhY2ggb3RoZXIsIHRoZSBkaXN0aW5jdGlvbiBiZXR3ZWVuIHRoZQ0KICAgIHNlbmRlciAoc291cmNlKSBhbmQgdGhlIHJlY2lwaWVudCAodGFyZ2V0KSBpcyBjbGVhcmx5IG1lYW5pbmdmdWwsDQogICAgdGhlcmVmb3JlIHRoZSBuZXR3b3JrIGlzIGRpcmVjdGVkLg0KDQo5LiAgYENvbm5lY3RlZGAgYW5kIGBEaXNjb25uZWN0ZWRgIGdyYXBoczogSWYgdGhlcmUgaXMgKnNvbWUqIHBhdGggZnJvbQ0KICAgICphbnkgbm9kZSogdG8gKmFueSBvdGhlciBub2RlKiwgdGhlIE5ldHdvcmtzIGlzIHNhaWQgdG8gYmUNCiAgICBDb25uZWN0ZWQuIEVsc2UsIERpc2Nvbm5lY3RlZC4NCg0KIyBQcmVkaWN0L1J1bi9JbmZlciAtMQ0KDQojIyBVc2luZyBgdGlkeWdyYXBoYCBhbmQgYGdncmFwaGANCg0KYHRpZHlncmFwaGAgYW5kIGBnZ3JhcGhgIGFyZSBtb2Rlcm4gUiBwYWNrYWdlcyBmb3IgbmV0d29yayBkYXRhLiBHcmFwaA0KRGF0YSBzZXR1cCBhbmQgbWFuaXB1bGF0aW9uIGlzIGRvbmUgaW4gYHRpZHlncmFwaGAgYW5kIGdyYXBoDQp2aXN1YWxpemF0aW9uIHdpdGggYGdncmFwaGAuDQoNCi0gICBgdGlkeWdyYXBoYCBOb2RlICsgRWRnZSBEYXRhIC1cPiAiTmV0d29yayBPYmplY3QiIGluIFIuDQotICAgYGdncmFwaGAgTmV0d29yayBPYmplY3QgLVw+IFBsb3RzIHVzaW5nIGEgY2hvc2VuIGxheW91dC9hbGdvLg0KDQpCb3RoIGxldmVyYWdlIHRoZSBwb3dlciBvZiAqKmBpZ3JhcGhgKiosIHdoaWNoIGlzIHRoZSAqKkJpZyBEYWRkeSoqIG9mDQphbGwgbmV0d29yayBwYWNrYWdlcy4gV2Ugd2lsbCBiZSB1c2luZyB0aGUgKkdyZXkncyBBbmF0b215IGRhdGFzZXQqIGluDQpvdXIgZmlyc3QgZm9yYXkgaW50byBuZXR3b3Jrcy4NCg0KIyMgU3RlcDEuIFJlYWQgdGhlIGRhdGENCg0KYGBge3IgMS5SZWFkLWdyZXlzLWFuYXRvbXktZGF0YSwgbWVzc2FnZT1GQUxTRSwgaW5jbHVkZT1GQUxTRX0NCg0KZ3JleV9ub2RlcyA8LSByZWFkX2NzdigiZGF0YS9ncmV5X25vZGVzLmNzdiIpDQpncmV5X2VkZ2VzIDwtIHJlYWRfY3N2KCJkYXRhL2dyZXlfZWRnZXMuY3N2IikNCg0KDQojIGdyZXlfbm9kZXMgPC0gcmVhZF9kZWxpbSgiLi9EYXRhL2dyZXlzLW5vZGVzLmNzdiIsIGRlbGltID0gIjsiKQ0KIyBnZXJfZWRnZXMgPC0gcmVhZF9kZWxpbSgifi9Eb3dubG9hZHMvZ3JleS1lZGdlcy5jc3YiLCANCiMgICAgIGRlbGltID0gIjsiLCBlc2NhcGVfZG91YmxlID0gRkFMU0UsIHRyaW1fd3MgPSBUUlVFKQ0KZ3JleV9ub2Rlcw0KZ3JleV9lZGdlcw0KDQpgYGANCg0KYGBge3IsIGZvci10ZWFjaGluZy1vbmx5LCBlY2hvPUZBTFNFfQ0KZ3JleV9ub2RlcyAlPiUgDQogIGRvd25sb2FkX3RoaXMoDQogICAgb3V0cHV0X25hbWUgPSAiZ3JleXMtbm9kZXMiLA0KICAgIG91dHB1dF9leHRlbnNpb24gPSAiLmNzdiIsDQogICAgYnV0dG9uX2xhYmVsID0gIkRvd25sb2FkIEdyZXkgTm9kZXMgYXMgY3N2IiwNCiAgICBidXR0b25fdHlwZSA9ICJkZWZhdWx0IiwNCiAgICBoYXNfaWNvbiA9IFRSVUUsDQogICAgaWNvbiA9ICJmYSBmYS1zYXZlIiwNCiAgICBjbGFzcyA9ICJodnItc3dlZXAtdG8tbGVmdCINCiAgKQ0KDQpncmV5X2VkZ2VzICU+JSANCiAgZG93bmxvYWRfdGhpcygNCiAgICBvdXRwdXRfbmFtZSA9ICJncmV5cy1lZGdlcyIsDQogICAgb3V0cHV0X2V4dGVuc2lvbiA9ICIuY3N2IiwNCiAgICBidXR0b25fbGFiZWwgPSAiRG93bmxvYWQgR3JleSBFZGdlcyBkYXRhIGFzIGNzdiIsDQogICAgYnV0dG9uX3R5cGUgPSAiZGVmYXVsdCIsDQogICAgaGFzX2ljb24gPSBUUlVFLA0KICAgIGljb24gPSAiZmEgZmEtc2F2ZSIsDQogICAgY2xhc3MgPSAiaHZyLXN3ZWVwLXRvLWxlZnQiDQogICkNCg0KYGBgDQoNClNhdmUgdGhlc2UgdHdvIENTViBmaWxlcyBpbiB5b3VyIHByb2plY3QncyBgZGF0YWAgZm9sZGVyLg0KDQpOT1RFOiB0aGVzZSBhcmUgYWN0dWFsbHkgKipzZW1pY29sb24gc2VwYXJhdGVkIHZhbHVlcyoqIGZpbGVzLCBzbyBvdXINCmZhbWlsaWFyIGByZWFkX2NzdigpYCB3aWxsIG5vdCB3b3JrLiBXZSB3aWxsIG5lZWQgdG8gdXNlIGByZWFkX2RlbGltKClgDQphbmQgdXNlIHRoZSBhcmd1bWVudCBgZGVsaW0gPSAiOyJgIC4NCg0KUmVtb3ZlIHRoZSBgZXZhbCA9IEZBTFNFYCBpbiB0aGUgY29kZSBjaHVuayBvcHRpb25zIGJlbG93Og0KDQpgYGB7ciByZWFkLWRhdGEsIGV2YWw9RkFMU0V9DQpncmV5X25vZGVzIDwtIHJlYWRfZGVsaW0oImRhdGEvZ3JleV9ub2Rlcy5jc3YiLGRlbGltID0gIjsiKQ0KZ3JleV9lZGdlcyA8LSByZWFkX2RlbGltKCJkYXRhL2dyZXlfZWRnZXMuY3N2IiwgZGVsaW0gPSAiOyIpDQoNCmBgYA0KDQpMZXQncyBsb29rIGF0IHdoYXQgd2UgaGF2ZSByZWFkIGluOg0KDQpgYGB7cn0NCmdyZXlfbm9kZXMNCmdyZXlfZWRnZXMNCg0KYGBgDQoNCiMjIyBRdWVzdGlvbnMgYW5kIEluZmVyZW5jZXMgIzE6DQoNCkxvb2sgYXQgdGhlIGNvbnNvbGUgb3V0cHV0IHRodW1ibmFpbC4gV2hhdCBhdHRyaWJ1dGVzIChpLmUuIGV4dHJhDQppbmZvcm1hdGlvbiBjb2x1bW5zKSBhcmUgc2VlbiBmb3IgTm9kZXMgYW5kIEVkZ2VzPyBVbmRlcnN0YW5kIHRoZSBkYXRhDQppbiBib3RoIG5vZGVzIGFuZCBlZGdlcyBhcyBzaG93biBpbiB0aGUgc2Vjb25kIGFuZCB0aGlyZCB0aHVtYm5haWxzLg0KV3JpdGUgc29tZSBjb21tZW50cyBhbmQgaW5mZXJlbmNlcyBoZXJlLg0KDQojIyBTdGVwIDIuQ3JlYXRlIGEgbmV0d29yayBvYmplY3QgdXNpbmcgYHRpZHlncmFwaGA6DQoNCktleSBmdW5jdGlvbjoNCg0KLSAgIGB0YmxfZ3JhcGgoKWA6IChha2EgInRpYmJsZSBncmFwaCIpLg0KLSAgIEtleSBhcmd1bWVudHM6IGBub2Rlc2AsIGBlZGdlc2AgYW5kIGBkaXJlY3RlZGAuDQotICAgTm90ZSB0aGlzIGlzIGEgdmVyeSB2ZXJzYXRpbGUgY29tbWFuZCBhbmQgY2FuIHRha2UgbWFueSBpbnB1dCBmb3JtcywNCiAgICBzdWNoIGFzIGRhdGEgc3RydWN0dXJlcyB0aGF0IHJlc3VsdCBmcm9tIG90aGVyIHBhY2thZ2VzLiBUeXBlDQogICAgYD90YmxfZ3JhcGhgIGluIHRoZSBDb25zb2xlIGFuZCBzZWUgdGhlIGBVc2FnZWAgc2VjdGlvbi4NCg0KTk9URTogYGdncGxvdGAgbmVlZHMgYSBkYXRhZnJhbWUvdGliYmxlIHRvIHN0YXJ0IGl0cyBwbG90dGluZywgYXMgeW91DQpyZWNhbGwuIFRoZSBncmFwaCBwbG90dGluZyBwYWNrYWdlIGVxdWl2YWxlbnQgb2YgYGdncGxvdGAgaXMgYGdncmFwaGAuDQpJdCBuZWVkcyBhICoiZGF0YWZyYW1lIiogdGhhdCBpcyBzdWl0YWJsZSBmb3IgKipuZXR3b3JrIHBsb3R0aW5nKiouIFRoaXMNCmlzIHRoZSAqKm5ldHdvcmsgb2JqZWN0KiogY3JlYXRlZCBieSBgdGlkeWdyYXBoYC4NCg0KYGBge3IgMi5DcmVhdGUtZ3JleXMtYW5hdG9teS1ncmFwaC1vYmplY3R9DQpnYSA8LSB0YmxfZ3JhcGgobm9kZXMgPSBncmV5X25vZGVzLCANCiAgICAgICAgICAgICAgICBlZGdlcyA9IGdyZXlfZWRnZXMsIA0KICAgICAgICAgICAgICAgIGRpcmVjdGVkID0gRkFMU0UpDQpnYQ0KDQpgYGANCg0KIyMjIFF1ZXN0aW9ucyBhbmQgSW5mZXJlbmNlcyAjMjoNCg0KV2hhdCBuZXcgaW5mb3JtYXRpb24gZG9lcyB0aGUgZ3JhcGggb2JqZWN0IGNvbnRhaW4/IEhhdmUgdGhlIGF0dHJpYnV0ZXMNCm9mIHRoZSBub2RlcyBhbmQgZWRnZXMgYmVlbiBwcm9wZXJseSBpbmNsdWRlZD8NCg0KIyMgU3RlcCAzLiBQbG90IHVzaW5nIGBnZ3JhcGhgDQoNCjNhLiBRdWljayBQbG90OiBgYXV0b2dyYXBoKClgIFRoaXMgaXMgdG8gY2hlY2sgcXVpY2tseSBpcyB0aGUgZGF0YSBpcw0KaW1wb3J0ZWQgcHJvcGVybHkgYW5kIHRvIGRlY2lkZSB1cG9uIGdvaW5nIG9uIHRvIGEgbW9yZSBlbGFib3JhdGUNCnBsb3R0aW5nLg0KDQpgYGB7ciAzYS5BdXRvLWdyYXBoLWdyZXlzLWFuYXRvbXl9DQoNCmF1dG9ncmFwaChnYSkNCg0KYGBgDQoNCiMjIyBRdWVzdGlvbnMgYW5kIEluZmVyZW5jZXMgIzM6DQoNCkRlc2NyaWJlIHRoaXMgZ3JhcGgsIGluIHNpbXBsZSB3b3JkcyBoZXJlLiBUcnkgdG8gdXNlIHNvbWUgb2YgdGhlIG5ldw0KZG9tYWluIHdvcmRzIHdlIGhhdmUganVzdCBhY3F1aXJlZDogKm5vZGVzL2VkZ2VzLA0KY29ubmVjdGVkL2Rpc2Nvbm5lY3RlZCwgZGlyZWN0ZWQvdW5kaXJlY3RlZCouDQoNCjNiLiBNb3JlIGVsYWJvcmF0ZSBwbG90DQoNCktleSBmdW5jdGlvbnM6DQoNCi0gICBgZ2dyYXBoKGxheW91dCA9ICIuLi4uLi4iKWA6IENyZWF0ZSBjbGFzc2ljIG5vZGUtZWRnZSBkaWFncmFtczsgaS5lLg0KICAgIFNldHMgdXAgdGhlIGdyYXBoLiBSYXRoZXIgbGlrZSBgZ2dwbG90YCBmb3IgbmV0d29ya3MhDQoNClR3byBraW5kcyBvZiBgZ2VvbWA6IG9uZSBzZXQgZm9yIG5vZGVzLCBhbmQgYW5vdGhlciBmb3IgZWRnZXMNCg0KLSAgIGBnZW9tX25vZGVfcG9pbnQoYWVzKC4uLi4uKSlgOiBEcmF3cyBub2RlIGFzICJwb2ludHMiLiBBbHRlcm5hdGl2ZXMNCiAgICBhcmUgYGNpcmNsZSAvIGFyY19iYXIgLyB0aWxlIC8gdm9yb25vaWAuIFJlbWVtYmVyIHRoZSBgZ2VvbWBzIHRoYXQNCiAgICB3ZSBoYXZlIHNlZW4gYmVmb3JlIGluIEdyYW1tYXIgb2YgR3JhcGhpY3MhDQoNCi0gICBgZ2VvbV9lZGdlX2xpbmsoYWVzKC4uLi4uKSlgOiBEcmF3cyBlZGdlcyBhcyAibGlua3MiLiBBbHRlcm5hdGl2ZXMNCiAgICBhcmUNCiAgICBgYXJjIC8gYmVuZCAvIGVsYm93IC8gaGl2ZSAvIGxvb3AgLyBwYXJhbGxlbCAvIGRpYWdvbmFsIC8gcG9pbnQgLyBzcGFuIC90aWxlYC4NCg0KLSAgIGBnZW9tX25vZGVfdGV4dChhZXMobGFiZWwgPSAuLi4uLi4pLCByZXBlbCA9IFRSVUUpYDogQWRkcyB0ZXh0DQogICAgbGFiZWxzIChub24tb3ZlcmxhcHBpbmcpLiBBbHRlcm5hdGl2ZXMgYXJlIGBsYWJlbCAvLi4uYA0KDQotICAgYGxhYnModGl0bGUgPSAiLi4uLiIsIHN1YnRpdGxlID0gIi4uLi4iLCBjYXB0aW9uID0gIi4uLi4iKWA6IENoYW5nZQ0KICAgIG1haW4gdGl0bGVzLCBheGlzIGxhYmVscyBhbmQgbGVnZW5kIHRpdGxlcy4gV2Uga25vdyB0aGlzIGZyb20gb3VyDQogICAgd29yayB3aXRoIGBnZ3Bsb3RgLg0KDQpgYGB7ciAzYi5HZ3JhcGgtZ3JleXMtYW5hdG9teX0NCg0KIyBXcml0ZSBDb21tZW50cyBuZXh0IHRvIGVhY2ggbGluZSANCiMgQWJvdXQgd2hhdCB0aGF0IGxpbmUgZG9lcyBmb3IgdGhlIG92ZXJhbGwgZ3JhcGgNCg0KZ2dyYXBoKGdyYXBoID0gZ2EsICBsYXlvdXQgPSAia2siKSArDQogICMNCiAgDQogIGdlb21fZWRnZV9saW5rKHdpZHRoID0gMiwgDQogICAgICAgICAgICAgICAgIGNvbG9yID0gInBpbmsiKSArDQogICMNCiAgDQogIGdlb21fbm9kZV9wb2ludCgNCiAgICBzaGFwZSA9IDIxLA0KICAgIHNpemUgPSA2LA0KICAgIGZpbGwgPSAiYmx1ZSIsDQogICAgY29sb3IgPSAiZ3JlZW4iLA0KICAgIHN0cm9rZSA9IDINCiAgKSArDQogICMNCiAgDQogIGxhYnModGl0bGUgPSAiV2hvbyBIb28hIE15IGZpcnN0IHNpbGx5IEdyZXkncyBBbmF0b215IGdyYXBoIGluIFIhIiwNCiAgICAgICBzdWJ0aXRsZSA9ICJXaHkgZGlkIEt1bWFyIHB1dCBtZSBpbiB0aGlzIGNvdXJzZS4uLiIsDQogICAgICAgY2FwdGlvbiA9ICJCcm8sIHRoZXkgYXJlIGRvaW5nICoqY29vbCoqIHRoaW5ncyBpbiB0aGUgb3RoZXIgY2xhc3Nlcy4uLiIpDQoNCg0KDQpgYGANCg0KIyMjIFF1ZXN0aW9ucyBhbmQgSW5mZXJlbmNlcyAjNDoNCg0KV2hhdCBwYXJhbWV0ZXJzIGhhdmUgYmVlbiBjaGFuZ2VkIGhlcmUsIGNvbXBhcmVkIHRvIHRoZSBlYXJsaWVyIGdyYXBoPw0KV2hlcmUgZG8geW91IHNlZSB0aGVzZSBjaGFuZ2VzIGluIHRoZSBjb2RlIGFib3ZlPw0KDQpMZXQgdXMgKipQbGF5Kiogd2l0aCB0aGlzIGdyYXBoIGFuZCBzZWUgaWYgd2UgY2FuIG1ha2Ugc29tZSBzbWFsbA0KY2hhbmdlcy4gQ29sb3VyPyBGaWxsPyBXaWR0aD8gU2l6ZT8gU3Ryb2tlPyBMYWJlbHM/IE9mIGNvdXJzZSENCg0KYGBge3IgM2MuTW9kaWZ5LUdyZXlzLUFuYXRvbXktZ2dyYXBofQ0KIyBDaGFuZ2UgdGhlIHBhcmFtZXRlcnMgaW4gZWFjaCBvZiB0aGUgY29tbWFuZHMgaGVyZSB0byBuZXcgb25lcw0KIyBVc2UgZml4ZWQgdmFsdWVzIGZvciBjb2xvdXJzIG9yIHNpemVzLi4uZXRjLiANCg0KZ2dyYXBoKGdyYXBoID0gZ2EsICBsYXlvdXQgPSAia2siKSArIA0KICBnZW9tX2VkZ2VfbGluayh3aWR0aCA9IDEsDQogICAgICAgICAgICAgICAgIGNvbG9yID0gInNwcmluZ2dyZWVuMiIpICsgDQogIGdlb21fbm9kZV9wb2ludChzaGFwZSA9IDI1LCANCiAgICAgICAgICAgICAgICAgIHNpemUgPSA0LCANCiAgICAgICAgICAgICAgICAgIGZpbGwgPSAiYnVybHl3b29kIiwgDQogICAgICAgICAgICAgICAgICBjb2xvciA9ICJmaXJlYnJpY2siLCANCiAgICAgICAgICAgICAgICAgIHN0cm9rZSA9IDEpICsNCiAgbGFicyh0aXRsZSA9ICJXaG9vIEhvbyEgTXkgbmV4dCBzaWxseSBHcmV5J3MgQW5hdG9teSBncmFwaCBpbiBSISIsDQogICAgICAgc3VidGl0bGUgPSAiV2h5IGRpZCBLdW1hciBwdXQgbWUgaW4gdGhpcyBjb3Vyc2UuLi4iLA0KICAgICAgIGNhcHRpb24gPSAiQnJvLCB0aGV5IGFyZSBzdGlsbCBkb2luZyBjb29sIHRoaW5ncyBpbiB0aGUgb3RoZXIgY2xhc3Nlcy4uLiIpDQoNCg0KDQpgYGANCg0KIyMjIFF1ZXN0aW9ucyBhbmQgSW5mZXJlbmNlcyAjNTogDQoNCldoYXQgZGlkIHRoZSBgc2hhcGVgIHBhcmFtZXRlciBhY2hpZXZlPyBXaGF0IGFyZSB0aGUgcG9zc2liaWxpdGllcyB3aXRoDQpgc2hhcGVgPyBIb3cgYWJvdXQgaW5jbHVkaW5nIGBhbHBoYWA/DQoNCjNjLiBBZXN0aGV0aWMgTWFwcGluZyBmcm9tIE5vZGUgYW5kIEVkZ2UgYXR0cmlidXRlIGNvbHVtbnMNCg0KVXAgdG8gbm93LCB3ZSBoYXZlICoqYXNzaWduZWQqKiAqc3BlY2lmaWMgbnVtYmVycyogdG8gZ2VvbWV0cmljDQphZXN0aGV0aWNzIHN1Y2ggYXMgc2hhcGUgYW5kIHNpemUuIE5vdyB3ZSBhcmUgcmVhZHkgKCBtYXliZSA/KSBjaGFuZ2UNCnRoZSBtZWFuaW5nIGFuZCBzaWduaWZpY2FuY2Ugb2YgdGhlIGVudGlyZSBncmFwaCBhbmQgZWFjaCBlbGVtZW50IHdpdGhpbg0KaXQsIGFuZCB1c2UgKiphZXN0aGV0aWNzIC8gbWV0YXBob3JpYyBtYXBwaW5ncyoqIHRvIGFjaGlldmUgbmV3IG1lYW5pbmdzDQpvciBpbnNpZ2h0cy4gTGV0IHVzIHRyeSB1c2luZyBgYWVzKClgIGluc2lkZSBlYWNoIGBnZW9tYCB0byBtYXAgYQ0KYHZhcmlhYmxlYCB0byBhIGdlb21ldHJpYyBhc3BlY3QuDQoNCkRvbid0IHRyeSB0byB1c2UgbW9yZSB0aGFuIDIgYWVzdGhldGljIG1hcHBpbmdzIHNpbXVsdGFuZW91c2x5ISENCg0KVGhlIG5vZGUgZWxlbWVudHMgd2UgY2FuIHR3ZWFrIGFyZToNCg0KLSAgIFR5cGVzIG9mIE5vZGVzOiBgZ2VvbV9ub2RlXyoqKiooKWANCi0gICBOb2RlIFBhcmFtZXRlcnM6IGluc2lkZSBgZ2VvbV9ub2RlXyoqKiooYWVzKC4uLi4uLi4uLi4uLi4uLikpYA0KICAgIC0gICBgYWVzKGFscGhhICA9IG5vZGUtdmFyaWFibGUpYCA6IG9wYWNpdHk7IGEgdmFsdWUgYmV0d2VlbiAwIGFuZCAxDQoNCiAgICAtICAgYGFlcyhzaGFwZSAgPSBub2RlLXZhcmlhYmxlKWAgOiBub2RlIHNoYXBlDQoNCiAgICAtICAgYGFlcyhjb2xvdXIgPSBub2RlLXZhcmlhYmxlKWAgOiBub2RlIGNvbG91cg0KDQogICAgLSAgIGBhZXMoZmlsbCAgID0gbm9kZS12YXJpYWJsZSlgIDogZmlsbCBjb2xvdXIgZm9yIG5vZGUNCg0KICAgIC0gICBgYWVzKHNpemUgICA9IG5vZGUtdmFyaWFibGUpYCA6IHNpemUgb2Ygbm9kZQ0KDQpUaGUgZWRnZSBlbGVtZW50cyB3ZSBjYW4gdHdlYWsgYXJlOg0KDQotICAgVHlwZSBvZiBFZGdlcyIgYGdlb21fZWRnZV8qKioqKClgDQotICAgRWRnZSBQYXJhbWV0ZXJzOiBpbnNpZGUgYGdlb21fZWRnZV8qKioqKGFlcyguLi4uLi4uLi4uLi4uLi4pKWANCiAgICAtICAgYGFlcyhjb2xvdXIgPSBlZGdlLXZhcmlhYmxlKWAgOiBjb2xvdXIgb2YgdGhlIGVkZ2UNCg0KICAgIC0gICBgYWVzKHdpZHRoICA9IGVkZ2UtdmFyaWFibGUpYCA6IHdpZHRoIG9mIHRoZSBlZGdlDQoNCiAgICAtICAgYGFlcyhsYWJlbCAgPSBzb21lX3ZhcmlhYmxlKWAgOiBsYWJlbHMgZm9yIHRoZSBlZGdlDQoNClR5cGUgYD9nZW9tX25vZGVfcG9pbnRgIGFuZCBgP2dlb20tZWRnZV9saW5rYCBpbiB5b3VyIENvbnNvbGUgZm9yIG1vcmUNCmluZm9ybWF0aW9uLg0KDQpgYGB7ciAzZC5hZXN0aGV0aWNzLXVzaW5nLWdncmFwaC1Nb2RpZnl9DQoNCiMgTG9vayBhdCB0aGUgY29sdW1uIG5hbWVzIGZvciBFZGdlcyBhbmQgTm9kZXMgYWdhaW4uIA0KIyBXaGljaCBjb2x1bW4gaXMgd2hpY2gga2luZCAoIFF1YWwgb3IgUXVhbnQ/KQ0KbmFtZXMoZ3JleV9lZGdlcykNCm5hbWVzKGdyZXlfbm9kZXMpDQoNCiMgT0ssIGxldCdzIG1vZGlmeSBzb21lIGFlc3RoZXRpY3MNCiMgDQpnZ3JhcGgoZ3JhcGggPSBnYSwgbGF5b3V0ID0gImZyIikgKw0KICBnZW9tX2VkZ2VfbGluazAoYWVzKHdpZHRoID0gd2VpZ2h0KSkgKyAjIGFkZCBtYXBwaW5nIGhlcmUNCiAgDQogIGdlb21fbm9kZV9wb2ludChhZXMoY29sb3IgPSByYWNlKSwgc2l6ZSA9IDYpICsgIyBhZGQgbWFwcGluZyBoZXJlDQoNCiAgIyBnZW9tX25vZGVfbGFiZWwoYWVzKGxhYmVsID0gbmFtZSksICMgbW9kaWZ5IHRoaXMgbWFwcGluZw0KICAjICAgICAgICAgICAgICAgICByZXBlbCA9IFRSVUUsIG1heC5vdmVybGFwcyA9IDIwLA0KICAjICAgICAgICAgICAgICAgICBhbHBoYSA9IDAuNiwNCiAgIyAgICAgICAgICAgICAgICAgc2l6ZSA9IDMpICsNCg0KICBsYWJzKHRpdGxlID0gIldob28gSG9vISBZZXQgYW5vdGhlciBHcmV5J3MgQW5hdG9teSBncmFwaCBpbiBSISIpDQoNCmBgYA0KDQojIyMgUXVlc3Rpb25zIGFuZCBJbmZlcmVuY2VzICM2Og0KDQpEZXNjcmliZSBzb21lIG9mIHRoZSBjaGFuZ2VzIGhlcmUuIFdoYXQgdHlwZXMgb2YgZWRnZXMgd29ya2VkPyBXaGljaA0KdmFyaWFibGVzIHdlcmUgeW91IGFibGUgdG8gdXNlIGZvciBub2RlcyBhbmQgZWRnZXMgYW5kIGhvdz8gV2hhdCBkaWQgbm90DQp3b3JrIHdpdGggZWl0aGVyIG9mIHRoZSB0d28/DQoNCiMjIFBsYXlpbmcgd2l0aCBMYXlvdXQNCg0KYGBge3IgYXJjLWRpYWdyYW19DQojIEFyYyBkaWFncmFtDQpnZ3JhcGgoZ2EsIGxheW91dCA9ICJsaW5lYXIiKSArDQogIGdlb21fZWRnZV9hcmMoYWVzKHdpZHRoID0gd2VpZ2h0KSwgYWxwaGEgPSAwLjgpICsNCiAgc2NhbGVfZWRnZV93aWR0aChyYW5nZSA9IGMoMC4yLCAyKSkgKw0KICBnZW9tX25vZGVfcG9pbnQoc2l6ZSA9IDIsIGNvbG91ciA9ICJyZWQiKSArDQogIGxhYnMoZWRnZV93aWR0aCA9ICJXZWlnaHQiKSArDQogIHRoZW1lX2dyYXBoKCkrDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKQ0KDQoNCmBgYA0KDQoNCg0KIyMjIFF1ZXN0aW9ucyBhbmQgSW5mZXJlbmNlcyAjNzoNCg0KSG93IGRvZXMgdGhpcyBncmFwaCBsb29rICJtZXRhcGhvcmljYWxseSIgZGlmZmVyZW50PyBEbyB5b3Ugc2VlIGENCmRpZmZlcmVuY2UgaW4gdGhlIHJlbGF0aW9uc2hpcHMgYmV0d2VlbiBwZW9wbGUgaGVyZT8gV2h5Pw0KDQpgYGB7ciBDb29yZC1EaWFncmFtLWNpcmN1bGFyfQ0KIyBDb29yZCBkaWFncmFtLCBjaXJjdWxhcg0KZ2dyYXBoKGdhLCBsYXlvdXQgPSAibGluZWFyIiwgY2lyY3VsYXIgPSBUUlVFKSArICMgU2VlbXMgd2VpcmQ/IDstRA0KICBnZW9tX2VkZ2VfYXJjKGFlcyh3aWR0aCA9IHdlaWdodCksIGFscGhhID0gMC44KSArIA0KICBzY2FsZV9lZGdlX3dpZHRoKHJhbmdlID0gYygwLjIsIDIpKSArDQogIGdlb21fbm9kZV9wb2ludChzaXplID0gNCxjb2xvdXIgPSAicmVkIikgKyANCiAgZ2VvbV9ub2RlX3RleHQoYWVzKGxhYmVsID0gbmFtZSkscmVwZWwgPSBUUlVFLCANCiAgICAgICAgICAgICAgICAgc2l6ZSA9IDMsDQogICAgICAgICAgICAgICAgIG1heC5vdmVybGFwcyA9IDIwKSArDQogIGxhYnMoZWRnZV93aWR0aCA9ICJXZWlnaHQiKSArDQogIA0KICB0aGVtZV9ncmFwaCgpKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiLCANCiAgICAgICAgYXNwZWN0LnJhdGlvID0gMSkNCg0KYGBgDQoNCiMjIyBRdWVzdGlvbnMgYW5kIEluZmVyZW5jZXMgIzg6DQoNCkhvdyBkb2VzIHRoaXMgZ3JhcGggbG9vayAibWV0YXBob3JpY2FsbHkiIGRpZmZlcmVudD8gRG8geW91IHNlZSBhDQpkaWZmZXJlbmNlIGluIHRoZSByZWxhdGlvbnNoaXBzIGJldHdlZW4gcGVvcGxlIGhlcmU/IFdoeT8NCg0KIyBIaWVyYXJjaGljYWwgbGF5b3V0cw0KDQpUaGVzZSBwcm92aWRlIGZvciBzb21lIGFsdGVybmF0aXZlIG1ldGFwaG9yaWNhbCB2aWV3cyBvZiBuZXR3b3Jrcy4gTm90ZQ0KdGhhdCBub3QgYWxsIGxheW91dHMgYXJlIHBvc3NpYmxlIGZvciBhbGwgZGF0YXNldHMhIQ0KDQpgYGB7cn0NCiMgc2V0dGluZyB0aGVtZV9ncmFwaCBmcm9tIGdncmFwaA0Kc2V0X2dyYXBoX3N0eWxlKCkNCg0KIyBUaGlzIGRhdGFzZXQgY29udGFpbnMgdGhlIGdyYXBoIHRoYXQgZGVzY3JpYmVzIHRoZSBjbGFzcyANCiMgaGllcmFyY2h5IGZvciB0aGUgRmxhcmUgdmlzdWFsaXphdGlvbiBsaWJyYXJ5Lg0KIyBUeXBlID9mbGFyZSBpbiB5b3VyIENvbnNvbGUNCmhlYWQoZmxhcmUkdmVydGljZXMpDQpoZWFkKGZsYXJlJGVkZ2VzKQ0KDQojIGZsYXJlIGNsYXNzIGhpZXJhcmNoeQ0KZ3JhcGggPSB0YmxfZ3JhcGgoZWRnZXMgPSBmbGFyZSRlZGdlcywgbm9kZXMgPSBmbGFyZSR2ZXJ0aWNlcykNCg0KIyBkZW5kcm9ncmFtDQpnZ3JhcGgoZ3JhcGgsIGxheW91dCA9ICJkZW5kcm9ncmFtIikgKyANCiAgZ2VvbV9lZGdlX2RpYWdvbmFsKCkgKyANCiAgbGFicyh0aXRsZSA9ICJEZW5kcm9ncmFtIikNCg0KIyBjaXJjdWxhciBkZW5kcm9ncmFtDQpnZ3JhcGgoZ3JhcGgsIGxheW91dCA9ICJkZW5kcm9ncmFtIiwgY2lyY3VsYXIgPSBUUlVFKSArIA0KICBnZW9tX2VkZ2VfZGlhZ29uYWwoKSArIA0KICBnZW9tX25vZGVfcG9pbnQoYWVzKGZpbHRlciA9IGxlYWYpKSArIA0KICBjb29yZF9maXhlZCgpKyANCiAgbGFicyh0aXRsZSA9ICJDaXJjdWxhciBEZW5kcm9ncmFtIikNCg0KIyByZWN0YW5ndWxhciB0cmVlIG1hcA0KZ2dyYXBoKGdyYXBoLCBsYXlvdXQgPSAidHJlZW1hcCIsIHdlaWdodCA9IHNpemUpICsgDQogIGdlb21fbm9kZV90aWxlKGFlcyhmaWxsID0gZGVwdGgpLCBzaXplID0gMC4yNSkgKyANCiAgbGFicyh0aXRsZSA9ICJSZWN0YW5ndWxhciBUcmVlIE1hcCIpDQoNCiMgY2lyY3VsYXIgdHJlZSBtYXANCmdncmFwaChncmFwaCwgbGF5b3V0ID0gImNpcmNsZXBhY2siLCB3ZWlnaHQgPSBzaXplKSArIA0KICBnZW9tX25vZGVfY2lyY2xlKGFlcyhmaWxsID0gZGVwdGgpLCBzaXplID0gMC4yNSwgbiA9IDUwKSArIA0KICBjb29yZF9maXhlZCgpICsgDQogIGxhYnModGl0bGUgPSAiQ2lyY3VsYXIgVHJlZSBNYXAiKQ0KDQojIGljaWNsZQ0KZ2dyYXBoKGdyYXBoLCBsYXlvdXQgPSAicGFydGl0aW9uIikgKyANCiAgZ2VvbV9ub2RlX3RpbGUoYWVzKHkgPSAteSwgZmlsbCA9IGRlcHRoKSkNCg0KIyBzdW5idXJzdCAoY2lyY3VsYXIgaWNpY2xlKQ0KZ2dyYXBoKGdyYXBoLCBsYXlvdXQgPSAicGFydGl0aW9uIiwgY2lyY3VsYXIgPSBUUlVFKSArDQogIGdlb21fbm9kZV9hcmNfYmFyKGFlcyhmaWxsID0gZGVwdGgpKSArDQogIGNvb3JkX2ZpeGVkKCkgKyANCiAgbGFicyh0aXRsZSA9ICJDaXJjdWxhciBJY2ljbGUiKQ0KYGBgDQoNCiMjIyBRdWVzdGlvbnMgYW5kIEluZmVyZW5jZXMgIzk6DQoNCkhvdyBkbyBncmFwaHMgbG9vayAibWV0YXBob3JpY2FsbHkiIGRpZmZlcmVudD8gRG8gdGhleSByZXZlYWwgZGlmZmVyZW50DQphc3BlY3RzIG9mIHRoZSBncm91cD8gSG93Pw0KDQojIyBGYWNldGluZw0KDQpGYWNldGluZyBhbGxvd3MgdG8gY3JlYXRlIHN1Yi1wbG90cyBhY2NvcmRpbmcgdG8gdGhlIHZhbHVlcyBvZiBhDQpxdWFsaXRhdGl2ZSBhdHRyaWJ1dGUgb24gbm9kZXMgb3IgZWRnZXMuDQoNCmBgYHtyIGZhY2V0aW5nfQ0KIyBzZXR0aW5nIHRoZW1lX2dyYXBoIA0Kc2V0X2dyYXBoX3N0eWxlKCkNCg0KDQojIGZhY2V0IGVkZ2VzIGJ5IHR5cGUNCmdncmFwaChnYSxsYXlvdXQgPSAibGluZWFyIiwgY2lyY3VsYXIgPSBUUlVFKSArIA0KICBnZW9tX2VkZ2VfbGluayhhZXMoY29sb3IgPSB0eXBlKSkgKyANCiAgZ2VvbV9ub2RlX3BvaW50KCkgKw0KICBmYWNldF9lZGdlcyh+IHR5cGUpDQoNCiMgZmFjZXQgbm9kZXMgYnkgc2V4DQpnZ3JhcGgoZ2EsbGF5b3V0ID0gImxpbmVhciIsIGNpcmN1bGFyID0gVFJVRSkgKyANCiAgZ2VvbV9lZGdlX2xpbmsoKSArIA0KICBnZW9tX25vZGVfcG9pbnQoKSArDQogIGZhY2V0X25vZGVzKH5yYWNlKQ0KDQojIGZhY2V0IGJvdGggbm9kZXMgYW5kIGVkZ2VzDQpnZ3JhcGgoZ2EsbGF5b3V0ID0gImxpbmVhciIsIGNpcmN1bGFyID0gVFJVRSkgKyANCiAgZ2VvbV9lZGdlX2xpbmsoYWVzKGNvbG9yID0gdHlwZSkpICsgDQogIGdlb21fbm9kZV9wb2ludCgpICsNCiAgZmFjZXRfZ3JhcGgodHlwZSB+IHJhY2UpICsgDQogIHRoX2ZvcmVncm91bmQoYm9yZGVyID0gVFJVRSkNCmBgYA0KDQojIyMgUXVlc3Rpb25zIGFuZCBJbmZlcmVuY2VzICMxMDogDQoNCkRvZXMgc3BsaXR0aW5nIHVwIHRoZSBtYWluIGdyYXBoIGludG8gc3ViLW5ldHdvcmtzIGdpdmUgeW91IG1vcmUNCmluc2lnaHQ/IERlc2NyaWJlIHNvbWUgb2YgdGhlc2UuDQoNCiMgTmV0d29yayBhbmFseXNpcyB3aXRoIGB0aWR5Z3JhcGhgDQoNClRoZSBkYXRhIGZyYW1lIGdyYXBoIHJlcHJlc2VudGF0aW9uIGNhbiBiZSBlYXNpbHkgYXVnbWVudGVkIHdpdGgNCioqbWV0cmljcyoqIG9yICoqc3RhdGlzdGljcyoqIGNvbXB1dGVkIG9uIHRoZSBncmFwaC4gUmVtZW1iZXIgaG93IHdlDQpjb21wdXRlZCBgY291bnRzYCB3aXRoIHRoZSBwZW5ndWluIGRhdGFzZXQgaW4gR3JhbW1hciBvZiBHcmFwaGljcy4NCg0KQmVmb3JlIGNvbXB1dGluZyBhIG1ldHJpYyBvbiBub2RlcyBvciBlZGdlcyB1c2UgdGhlIGBhY3RpdmF0ZSgpYA0KZnVuY3Rpb24gdG8gYWN0aXZhdGUgZWl0aGVyIG5vZGUgb3IgZWRnZSBkYXRhIGZyYW1lcy4gVXNlIGBkcGx5cmANCioqdmVyYnMqKiAoYGZpbHRlciwgYXJyYW5nZSwgbXV0YXRlYCkgdG8gYWNoaWV2ZSB5b3VyIGNvbXB1dGF0aW9uIGluIHRoZQ0KcHJvcGVyIHdheS4NCg0KIyMgTmV0d29yayBDZW50cmFsaXR5DQoNCkNlbnRyYWxpdHkgaXMgYSBhbiAiaWxsLWRlZmluZWQiIG1ldHJpYyBvZiAqKm5vZGUgYW5kIGVkZ2UgaW1wb3J0YW5jZSoqDQppbiBhIG5ldHdvcmsuIEl0IGlzIHRoZXJlZm9yZSBjYWxjdWxhdGVkIGluIG1hbnkgd2F5cy4NCg0KVHlwZSBgP2NlbnRyYWxpdHlgIGluIHlvdXIgQ29uc29sZS4NCg0KIVtTdGFuZGFyZHNdKGh0dHBzOi8vaW1ncy54a2NkLmNvbS9jb21pY3Mvc3RhbmRhcmRzLnBuZykNCg0KTGV0J3MgYWRkIGEgZmV3IGNvbHVtbnMgdG8gdGhlIG5vZGVzIGFuZCBlZGdlcyBiYXNlZCBvbiBuZXR3b3JrDQpjZW50cmFsaXR5IG1lYXN1cmVzOg0KDQpgYGB7ciBuZXR3b3JrLWFuYWx5c2lzLTF9DQpnYV9jZW50cmFsaXR5IDwtIGdhICU+JSANCiAgYWN0aXZhdGUobm9kZXMpICU+JSANCiAgDQogICMgTW9zdCBjb25uZWN0aW9ucz8NCiAgbXV0YXRlKGRlZ3JlZSA9IGNlbnRyYWxpdHlfZGVncmVlKG1vZGUgPSBjKCJpbiIpKSkgJT4lIA0KICBmaWx0ZXIoZGVncmVlID4gMCkgJT4lIA0KICANCiAgYWN0aXZhdGUoZWRnZXMpICU+JSANCiAgIyAiQnVzaWVzdCIgZWRnZT8NCiAgbXV0YXRlKGJldHdlZW5uZXNzID0gY2VudHJhbGl0eV9lZGdlX2JldHdlZW5uZXNzKCkpDQoNCmdhX2NlbnRyYWxpdHkgJT4lIGFjdGl2YXRlKG5vZGVzKSAlPiUgYXNfdGliYmxlKCkNCmdhX2NlbnRyYWxpdHkgJT4lIGFjdGl2YXRlKGVkZ2VzKSAlPiUgYXNfdGliYmxlKCkNCg0KDQpgYGANCg0KTGV0IHVzIHBsb3QgdGhlIGdyYXBoIHdpdGggdGhlIChudW1lcmljYWwpIENlbnRyYWxpdHkgbWVhc3VyaW5nDQpjb2xvdXJpbmcgdGhlIE5vZGVzOg0KDQpgYGB7ciBQbG90LVVzaW5nLUNlbnRyYWxpdHl9DQojIHNldHRpbmcgdGhlbWVfZ3JhcGggDQpzZXRfZ3JhcGhfc3R5bGUoKQ0KDQpnYV9jZW50cmFsaXR5ICU+JSAjIFN0YXJ0IHdpdGggb25yIG5ldyBOZXR3b3JrIE9iamVjdA0KICANCiAgIyBOb3cgdG8gY29udGludWUgd2l0aCBwbG90dGluZw0KICBnZ3JhcGgobGF5b3V0ID0gIm5pY2VseSIpICsNCiAgZ2VvbV9lZGdlX2xpbmsoYWVzKGFscGhhID0gYmV0d2Vlbm5lc3MpKSArDQogIA0KICBnZW9tX25vZGVfcG9pbnQoYWVzKHNpemUgPSBkZWdyZWUsIA0KICAgICAgICAgICAgICAgICAgICAgIGNvbG91ciA9IGRlZ3JlZSkpICsgDQogIA0KICAjIGRpc2NyZXRlIGNvbG91ciBsZWdlbmQNCiAgc2NhbGVfY29sb3JfZ3JhZGllbnQoZ3VpZGUgPSAibGVnZW5kIiwNCiAgICAgICAgICAgICAgICAgICAgICAgbG93ID0gImJsdWUiLCANCiAgICAgICAgICAgICAgICAgICAgICAgaGlnaCA9ICJyZWQiKSArDQogIA0KICBzY2FsZV9lZGdlX2FscGhhKHJhbmdlID0gYygwLjA2LCAxKSkNCg0KYGBgDQoNCiMjIEFuYWx5c2UgYW5kIHZpc3VhbGl6ZSBuZXR3b3JrIGNvbW11bml0aWVzDQoNCldobyBpcyBjbG9zZSB0byB3aG9tPyBXaGljaCBhcmUgdGhlIGdyb3VwcyB5b3UgY2FuIHNlZT8NCg0KYGBge3IgQ2FsY3VsYXRlLUNvbW11bml0eX0NCg0KIyB2aXN1YWxpemUgY29tbXVuaXRpZXMgb2Ygbm9kZXMNCiMgQWRkIGEgY29sdW1uLCBhIENhdGVnb3JpY2FsIENvbHVtbiB0byBzaG93IA0KIyBDb21tdW5pdHkgTWVtYmVyc2hpcA0KIyANCmdhX2NvbW11bml0eSA8LSBnYSAlPiUgDQogIGFjdGl2YXRlKG5vZGVzKSAlPiUNCiAgbXV0YXRlKGNvbW11bml0eSA9IGFzLmZhY3Rvcihncm91cF9sb3V2YWluKCkpKSANCg0KZ2FfY29tbXVuaXR5ICU+JSBhY3RpdmF0ZShub2RlcykgJT4lIGFzX3RpYmJsZSgpDQoNCmBgYA0KDQpOb3cgbGV0IHVzIHBsb3QgdGhlIENvbW11bml0aWVzLCBhbmQgY29sb3VyIHRoZSBub2RlcyBiYXNlZCBvbiB3aGljaA0KQ29tbXVuaXR5IHRoZXkgYmVsb25nIHdpdGg6DQoNCmBgYHtyIFBsb3QtQ29tbXVuaXR5LTF9DQoNCiMgc2V0dGluZyB0aGVtZV9ncmFwaCANCnNldF9ncmFwaF9zdHlsZSgpDQoNCg0KZ2FfY29tbXVuaXR5ICU+JSANCiAgZ2dyYXBoKGxheW91dCA9ICJncmFwaG9wdCIpICsgDQogIGdlb21fZWRnZV9saW5rKCkgKyANCiAgZ2VvbV9ub2RlX3BvaW50KGFlcyhjb2xvciA9IGNvbW11bml0eSksIA0KICAgICAgICAgICAgICAgICAgc2l6ZSA9IDUpICsNCiAgICBnZW9tX25vZGVfdGV4dChhZXMobGFiZWwgPSBuYW1lKSwNCiAgICAgICAgICAgICAgICAgICByZXBlbCA9IFRSVUUsIA0KICAgICAgICAgICAgICAgICAgIHNpemUgPSAyLA0KICAgICAgICAgICAgICAgICAgIG51ZGdlX3ggPSAzLCBudWRnZV95ID0gMykgKw0KICBzY2FsZV9jb2xvdXJfYnJld2VyKHBhbGV0dGUgPSAiQWNjZW50IikNCg0KYGBgDQoNCldlIGNhbiBhbHNvIHBsYWNlICpuYW1lcyogb3IgKmxhYmVscyogaW4gcGxhY2Ugb2YgdGhlIHNoYXBlcywgZm9yIGVhY2ggbm9kZToNCg0KDQpgYGB7ciBQbG90LUNvbW11bml0eS0yfQ0KDQojIHNldHRpbmcgdGhlbWVfZ3JhcGggDQpzZXRfZ3JhcGhfc3R5bGUoKQ0KDQoNCmdhX2NvbW11bml0eSAlPiUgDQogIGdncmFwaChsYXlvdXQgPSAiZ3JhcGhvcHQiKSArIA0KICBnZW9tX2VkZ2VfbGluazAoKSArIA0KDQogICAgZ2VvbV9ub2RlX2xhYmVsKGFlcyhsYWJlbCA9IG5hbWUsIGZpbGwgPSBjb21tdW5pdHkpLCANCiAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IDMsIHJlcGVsID0gVFJVRSkgKw0KICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIkFjY2VudCIpDQoNCmBgYA0KDQoNCiMjIEludGVyYWN0aXZlIEdyYXBocyB3aXRoIGB2aXNOZXR3b3JrYA0KDQpFeHBsb3JpbmcgdGhlIGBWaXNOZXR3b3JrYCBwYWNrYWdlLiBNYWtlIGdyYXBocyB3aWdnbGUgYW5kIHNoYWtlIHVzaW5nDQpgdGlkeWAgY29tbWFuZHMhIFRoZSBwYWNrYWdlIGltcGxlbWVudHMgaW50ZXJhY3Rpdml0eSB1c2luZyB0aGUgcGh5c2ljYWwNCm1ldGFwaG9yIG9mIHdlaWdodHMgYW5kIHNwcmluZ3Mgd2UgZGlzY3Vzc2VkIGVhcmxpZXIuDQoNClRoZSBgdmlzTmV0d29yaygpYCBmdW5jdGlvbiB1c2VzIGEgbm9kZXMgbGlzdCBhbmQgZWRnZXMgbGlzdCB0byBjcmVhdGUNCmFuIGludGVyYWN0aXZlIGdyYXBoLiBUaGUgbm9kZXMgbGlzdCAqKm11c3QqKiBpbmNsdWRlIGFuIGAiaWQiYCBjb2x1bW4sDQphbmQgdGhlIGVkZ2UgbGlzdCAqKm11c3QqKiBoYXZlIGAiZnJvbSJgIGFuZCBgInRvImAgY29sdW1ucy4gVGhlDQpmdW5jdGlvbiBhbHNvIHBsb3RzIHRoZSBsYWJlbHMgZm9yIHRoZSBub2RlcywgdXNpbmcgdGhlIG5hbWVzIG9mIHRoZQ0KY2l0aWVzIGZyb20gdGhlIGAibGFiZWwiYCBjb2x1bW4gaW4gdGhlIG5vZGUgbGlzdC4NCg0KYGBge3IgZ3JleXMtYW5hdG9teS1kYXRhLWZvci12aXNuZXR3b3JrfQ0KbGlicmFyeSh2aXNOZXR3b3JrKQ0KDQojIFByZXBhcmUgdGhlIGRhdGEgZm9yIHBsb3R0aW5nIGJ5IHZpc05ldHdvcmsNCmdyZXlfbm9kZXMNCmdyZXlfZWRnZXMNCg0KYGBgDQoNCkxldCB1cyByZWxhYmVsIGdyZXlzIGFuYXRvbXkgbm9kZXMgYW5kIGVkZ2VzIGZvciB1c2Ugd2l0aCBWaXNOZXR3b3JrOg0KDQpgYGB7cn0NCmdyZXlfbm9kZXNfdmlzIDwtIGdyZXlfbm9kZXMgJT4lIA0KICByb3dpZF90b19jb2x1bW4odmFyID0gImlkIikgJT4lIA0KICByZW5hbWUoImxhYmVsIiA9IG5hbWUpICU+JSANCiAgbXV0YXRlKHNleCA9IGNhc2Vfd2hlbihzZXggPT0gIkYiIH4gIkZlbWFsZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgc2V4ID09ICJNIiB+ICJNYWxlIikpICU+JSANCiAgcmVwbGFjZV9uYSguLCBsaXN0KHNleCA9ICJUcmFuc2dlbmRlcj8iKSkgJT4lIA0KICByZW5hbWUoImdyb3VwIiA9IHNleCkNCmdyZXlfbm9kZXNfdmlzDQoNCmBgYA0KDQpBbmQgbm93IGZvciB0aGUgZWRnZXM6DQoNCmBgYHtyfQ0KZ3JleV9lZGdlc192aXMgPC0gZ3JleV9lZGdlcyAlPiUgDQogIHNlbGVjdChmcm9tLCB0bykgJT4lIA0KICBsZWZ0X2pvaW4oLiwgZ3JleV9ub2Rlc192aXMsIA0KICAgICAgICAgICAgYnkgPSBjKCJmcm9tIiA9ICJsYWJlbCIpKSAlPiUgDQogIGxlZnRfam9pbiguLCBncmV5X25vZGVzX3ZpcywgDQogICAgICAgICAgICBieSA9IGMoInRvIiA9ICJsYWJlbCIpKSAlPiUNCiAgc2VsZWN0KCJmcm9tIj0gaWQueCwgInRvIiA9IGlkLnkpDQpncmV5X2VkZ2VzX3Zpcw0KDQpgYGANCg0KV2Ugd2lsbCBiZSB1c2luZyBbZm9udGF3ZXNvbWUgaWNvbnNdKGh0dHBzOi8vZm9udGF3ZXNvbWUuY29tL3Y2LjAvaWNvbnMpDQp0byBwbG90IHRoZSBOb2RlcyBpbiB0aGlzIGdyYXBoISENCg0KYGBge3IgZ3JleXMtYW5hdG9teS12aXNOZXR3b3JrfQ0KZ3JleV9ub2Rlc192aXMgJT4lDQoNCiAgdmlzTmV0d29yayhub2RlcyA9IC4sIGVkZ2VzID0gZ3JleV9lZGdlc192aXMpICU+JSANCiAgdmlzTm9kZXMoZm9udCA9IGxpc3Qoc2l6ZSA9IDQwKSkgJT4lIA0KICANCiAgIyBDb2xvdXIgYW5kIGljb25zIGZvciBlYWNoIG9mIHRoZSBnZW5kZXItZ3JvdXBzDQogIHZpc0dyb3Vwcyhncm91cG5hbWUgPSAiRmVtYWxlIiwgc2hhcGUgPSAiaWNvbiIsIA0KICAgICAgICAgICAgaWNvbiA9IGxpc3QoY29kZSA9ICJmMTgyIiwgc2l6ZSA9IDc1LCBjb2xvciA9ICJ0b21hdG8iKSwNCiAgICAgICAgICAgIHNoYWRvdyA9IGxpc3QoZW5hYmxlZCA9IFRSVUUpKSAlPiUgDQogIA0KICB2aXNHcm91cHMoZ3JvdXBuYW1lID0gIk1hbGUiLCBzaGFwZSA9ICJpY29uIiwgDQogICAgICAgICAgICBpY29uID0gbGlzdChjb2RlID0gImYxODMiLCBzaXplID0gNzUsIGNvbG9yID0gInNsYXRlYmx1ZSIpLCANCiAgICAgICAgICAgIHNoYWRvdyA9IGxpc3QoZW5hYmxlZCA9IFRSVUUpKSAlPiUgDQogIA0KICB2aXNHcm91cHMoZ3JvdXBuYW1lID0gIlRyYW5zZ2VuZGVyPyIsIHNoYXBlID0gImljb24iLCANCiAgICAgICAgICAgIGljb24gPSBsaXN0KGNvZGUgPSAiZjIyYyIsIHNpemUgPSA3NSwgY29sb3IgPSAiZnVjaHNpYSIpLCANCiAgICAgICAgICAgIHNoYWRvdyA9IGxpc3QoZW5hYmxlZCA9IFRSVUUpKSAlPiUgDQogIA0KICAjdmlzTGVnZW5kKCkgJT4lDQogICNBZGQgdGhlIGZvbnRhd2Vzb21lIGljb25zISENCiAgYWRkRm9udEF3ZXNvbWUoKSAlPiUgDQogIA0KICAjIEFkZCBJbnRlcmFjdGlvbiBDb250cm9scw0KICB2aXNJbnRlcmFjdGlvbihuYXZpZ2F0aW9uQnV0dG9ucyA9IFRSVUUsDQogICAgICAgICAgICAgICAgIGhvdmVyID0gVFJVRSwNCiAgICAgICAgICAgICAgICAgc2VsZWN0Q29ubmVjdGVkRWRnZXMgPSBUUlVFLA0KICAgICAgICAgICAgICAgICBob3ZlckNvbm5lY3RlZEVkZ2VzID0gVFJVRSwNCiAgICAgICAgICAgICAgICAgem9vbVZpZXcgPSBUUlVFKQ0KYGBgDQoNClRoZXJlIGlzIGFub3RoZXIgZmFtaWx5IG9mIGljb25zIGF2YWlsYWJsZSBpbiB2aXNOZXR3b3JrLCBjYWxsZWQNCltgaW9uaWNvbnNgXShodHRwczovL3VucGtnLmNvbS9pb25pY29uc0A1LjUuMi9kaXN0L2NoZWF0c2hlZXQuaHRtbCkuDQpMZXQncyBzZWUgaG93IHRoZXkgbG9vazoNCg0KYGBge3IgdXNpbmctaW9uaWNvbnN9DQpncmV5X25vZGVzX3ZpcyAlPiUNCg0KICB2aXNOZXR3b3JrKG5vZGVzID0gLiwgZWRnZXMgPSBncmV5X2VkZ2VzX3ZpcywpICU+JQ0KICB2aXNMYXlvdXQocmFuZG9tU2VlZCA9IDEyMzQ1KSAlPiUNCiAgdmlzTm9kZXMoZm9udCA9IGxpc3Qoc2l6ZSA9IDUwKSkgJT4lDQogIHZpc0VkZ2VzKGNvbG9yID0gImdyZWVuIikgJT4lDQogIHZpc0dyb3VwcygNCiAgICBncm91cG5hbWUgPSAiRmVtYWxlIiwNCiAgICBzaGFwZSA9ICJpY29uIiwNCiAgICBpY29uID0gbGlzdCgNCiAgICAgIGZhY2UgPSAnSW9uaWNvbnMnLA0KICAgICAgY29kZSA9ICJmMjVkIiwNCiAgICAgIGNvbG9yID0gImZ1Y2hzaWEiLA0KICAgICAgc2l6ZSA9IDEyNQ0KICAgICkNCiAgKSAlPiUNCiAgDQogIHZpc0dyb3VwcygNCiAgICBncm91cG5hbWUgPSAiTWFsZSIsDQogICAgc2hhcGUgPSAiaWNvbiIsDQogICAgaWNvbiA9IGxpc3QoDQogICAgICBmYWNlID0gJ0lvbmljb25zJywNCiAgICAgIGNvZGUgPSAiZjIwMiIsDQogICAgICBjb2xvciA9ICJncmVlbiIsDQogICAgICBzaXplID0gMTI1DQogICAgKQ0KICApICU+JQ0KICANCiAgdmlzR3JvdXBzKA0KICAgIGdyb3VwbmFtZSA9ICJUcmFuc2dlbmRlcj8iLA0KICAgIHNoYXBlID0gImljb24iLA0KICAgIGljb24gPSBsaXN0KA0KICAgICAgZmFjZSA9ICdJb25pY29ucycsDQogICAgICBjb2RlID0gImYyMzMiLA0KICAgICAgY29sb3IgPSAiZG9kZ2VyYmx1ZSIsDQogICAgICBzaXplID0gMTI1DQogICAgKQ0KICApICU+JQ0KICB2aXNMZWdlbmQoKSAlPiUNCiAgYWRkSW9uaWNvbnMoKSAlPiUNCiAgdmlzSW50ZXJhY3Rpb24oDQogICAgbmF2aWdhdGlvbkJ1dHRvbnMgPSBUUlVFLA0KICAgIGhvdmVyID0gVFJVRSwNCiAgICBzZWxlY3RDb25uZWN0ZWRFZGdlcyA9IFRSVUUsDQogICAgaG92ZXJDb25uZWN0ZWRFZGdlcyA9IFRSVUUsDQogICAgem9vbVZpZXcgPSBUUlVFDQogICkNCmBgYA0KDQpTb21lIGlkZWEgb2YgaW50ZXJhY3Rpdml0eSBhbmQgY29udHJvbHMgd2l0aCBgdmlzTmV0d29ya2A6DQoNCmBgYHtyIHZpc05ldHdvcmstc3RhcndhcnMtMSwgaW5jbHVkZT1GQUxTRX0NCmxpYnJhcnkodmlzTmV0d29yaykNCiMgbGV0J3MgbG9vayBhZ2FpbiBhdCB0aGUgZGF0YQ0Kc3RhcndhcnNfbm9kZXMgPC0gcmVhZF9jc3YoIi4vRGF0YS9zdGFyLXdhcnMtbmV0d29yay1ub2Rlcy5jc3YiKQ0Kc3RhcndhcnNfZWRnZXMgPC0gcmVhZF9jc3YoIi4vRGF0YS9zdGFyLXdhcnMtbmV0d29yay1lZGdlcy5jc3YiKQ0KDQpgYGANCg0KRG93bmxvYWQgdGhpcyBgU3RhcldhcnNgIGRhdGFzZXRzIGFuZCBzdG9yZSB0aGVtIGluIHlvdXIgYC9kYXRhYCBmb2xkZXIuDQpBZ2Fpbiwgbm90ZSB0aGVzZSBhcmUgKnNlbWljb2xvbiBzZXBhcmF0ZWQgdmFsdWVzKiBmaWxlcyBhcmUgYmVmb3JlOg0KDQpgYGB7ciBlY2hvPUZBTFNFfQ0Kc3RhcndhcnNfbm9kZXMgJT4lIA0KICBkb3dubG9hZF90aGlzKA0KICAgIG91dHB1dF9uYW1lID0gInN0YXItd2Fycy1uZXR3b3JrLW5vZGVzIiwNCiAgICBvdXRwdXRfZXh0ZW5zaW9uID0gIi5jc3YiLA0KICAgIGJ1dHRvbl9sYWJlbCA9ICJEb3dubG9hZCBOb2RlcyBkYXRhIGFzIGNzdiIsDQogICAgYnV0dG9uX3R5cGUgPSAiZGVmYXVsdCIsDQogICAgaGFzX2ljb24gPSBUUlVFLA0KICAgIGljb24gPSAiZmEgZmEtc2F2ZSIsDQogICAgY2xhc3MgPSAiaHZyLXN3ZWVwLXRvLWxlZnQiDQogICkNCnN0YXJ3YXJzX2VkZ2VzICU+JSANCiAgZG93bmxvYWRfdGhpcygNCiAgICBvdXRwdXRfbmFtZSA9ICJzdGFyLXdhcnMtbmV0d29yay1lZGdlcyIsDQogICAgb3V0cHV0X2V4dGVuc2lvbiA9ICIuY3N2IiwNCiAgICBidXR0b25fbGFiZWwgPSAiRG93bmxvYWQgRWRnZXMgZGF0YSBhcyBjc3YiLA0KICAgIGJ1dHRvbl90eXBlID0gImRlZmF1bHQiLA0KICAgIGhhc19pY29uID0gVFJVRSwNCiAgICBpY29uID0gImZhIGZhLXNhdmUiLA0KICAgIGNsYXNzID0gImh2ci1zd2VlcC10by1sZWZ0Ig0KICApDQpgYGANCg0KYGBge3IgdmlzTmV0d29yay1zdGFyd2Fycy0yLCBldmFsPUZBTFNFfQ0KbGlicmFyeSh2aXNOZXR3b3JrKQ0KIyBsZXQncyBsb29rIGFnYWluIGF0IHRoZSBkYXRhDQpzdGFyd2Fyc19ub2RlcyA8LSByZWFkX2RlbGltKCIuL0RhdGEvc3Rhci13YXJzLW5ldHdvcmstbm9kZXMuY3N2IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVsaW0gPSAiOyIpDQoNCnN0YXJ3YXJzX2VkZ2VzIDwtIHJlYWRfZGVsaW0oIi4vRGF0YS9zdGFyLXdhcnMtbmV0d29yay1lZGdlcy5jc3YiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZWxpbSA9ICI7IikNCg0Kc3RhcndhcnNfZWRnZXMNCnN0YXJ3YXJzX25vZGVzDQoNCmBgYA0KDQpgYGB7ciB2aXNOZXR3b3JrLXN0YXJ3YXJzLTN9DQojIFdlIG5lZWQgdG8gcmVuYW1lIHN0YXJ3YXJzIG5vZGVzIGRhdGFmcmFtZSBhbmQgZWRnZSBkYXRhZnJhbWUgY29sdW1ucyBmb3IgdmlzTmV0d29yaw0Kc3RhcndhcnNfbm9kZXNfdmlzIDwtIA0KICBzdGFyd2Fyc19ub2RlcyAlPiUgDQogIHJlbmFtZSgibGFiZWwiID0gbmFtZSkNCg0KIyBDb252ZXJ0IGZyb20gYW5kIHRvIGNvbHVtbnMgdG8gKipub2RlIGlkcyoqDQpzdGFyd2Fyc19lZGdlc192aXMgPC0gDQogIHN0YXJ3YXJzX2VkZ2VzICU+JSANCiAgDQogICMgTWF0Y2hpbmcgU291cmNlIDwtIFNvdXJjZSBOb2RlIGlkICgiaWQueCIpDQogIGxlZnRfam9pbiguLCBzdGFyd2Fyc19ub2Rlc192aXMsIGJ5ID0gYygic291cmNlIiA9ICJsYWJlbCIpKSAlPiUgDQogIA0KICAjIE1hdGNoaW5nIFRhcmdldCA8LSBUYXJnZXQgTm9kZSBpZCAoImlkLnkiKQ0KICBsZWZ0X2pvaW4oLiwgc3RhcndhcnNfbm9kZXNfdmlzLCBieSA9IGMoInRhcmdldCIgPSAibGFiZWwiKSkgJT4lIA0KICANCiMgU2VsZWN0ICJpZC54IiBhbmQgImlkLnkiIE9OTFkNCiMgUmVuYW1lIHRoZW0gYXMgImZyb20iIGFuZCAidG8iDQojIGtlZXAgIndlaWdodCIgY29sdW1uIGZvciBhZXN0aGV0aWNzIG9mIGVkZ2VzDQogIHNlbGVjdCgiZnJvbSIgPSBpZC54LCAidG8iID0gaWQueSwgInZhbHVlIiA9IHdlaWdodCkNCg0KIyBDaGVjayBldmVyeXRoaW5nIG9uY2UNCnN0YXJ3YXJzX25vZGVzX3Zpcw0Kc3RhcndhcnNfZWRnZXNfdmlzDQoNCmBgYA0KDQpPaywgbGV0J3MgbWFrZSB0aGluZ3MgbW92ZSBhbmQgc2hha2UhIQ0KDQpgYGB7ciB2aXNOZXR3b3JrLXN0YXJ3YXJzLWZ1dGJvbH0NCnZpc05ldHdvcmsobm9kZXMgPSBzdGFyd2Fyc19ub2Rlc192aXMsDQogICAgICAgICAgIGVkZ2VzID0gc3RhcndhcnNfZWRnZXNfdmlzKSAlPiUgDQogIHZpc05vZGVzKGZvbnQgPSBsaXN0KHNpemUgPSAzMCksIHNoYXBlID0gImljb24iLCANCiAgICAgICAgICAgaWNvbiA9IGxpc3QoY29kZSA9ICJmMWUzIiwgc2l6ZSA9IDc1KSkgJT4lIA0KICBhZGRGb250QXdlc29tZSgpICU+JSANCiAgdmlzRWRnZXMoY29sb3IgPSAicmVkIikNCg0KYGBgDQoNCmBgYHtyIHZpc05ldHdvcmstc3RhcndhcnN9DQp2aXNOZXR3b3JrKG5vZGVzID0gc3RhcndhcnNfbm9kZXNfdmlzLA0KICAgICAgICAgICBlZGdlcyA9IHN0YXJ3YXJzX2VkZ2VzX3ZpcykgJT4lIA0KICB2aXNOb2Rlcyhmb250ID0gbGlzdChzaXplID0gMzApKSAlPiUgDQogIHZpc0VkZ2VzKGNvbG9yID0gInJlZCIpDQpgYGANCg0KIyBZb3VyIEFzc2lnbm1lbnRzOg0KDQojIyBNYWtlLTEgOiBXaXRoIGEgcmVhZHkgbWFkZSBkYXRhc2V0DQoNClN0ZXAgMS4gRmlyZSB1cCBhIG5ldyBSIFByb2plY3QgYW5kIGEgbmV3IFJNYXJrZG93biB3aXRoaW4uIFdyaXRlIHlvdXIgbmFtZSwgZmlsZV9uYW1lIGFuZCBkYXRlLg0KDQpTdGVwIDIuIFRha2UgKiphbnkgb25lKiogb2YgdGhlIERhdGFzZXRzIGRhdGFzZXRzIGRlc2NyaWJlZCBiZWxvdy4NCg0KU3RlcCAzLiBSTWFya2Rvd24gY29udGVudHM6DQoNCi0gICBJbnRyb2R1Y2UgLyBJbnNwZWN0IGluIFIgeW91ciBkYXRhIGFuZCBkZXNjcmliZQ0KLSAgIEludHJvZHVjZSB5b3VyIFB1cnBvc2UNCi0gICBDcmVhdGUgZ3JhcGggb2JqZWN0cy4NCi0gICBXcml0ZSBjb21tZW50cyBpbiB0aGUgY29kZQ0KLSAgIFdyaXRlIG5hcnJhdGl2ZSBpbiB0ZXh0IHdpdGggc2VjdGlvbnMsIGJvbGQgLGl0YWxpYyBldGMuDQoNClN0ZXAgNC4gS25pdCBiZWZvcmUgeW91IHN1Ym1pdC4gU3VibWl0IHlvdXIgKmVudGlyZSogUHJvamVjdCBhcyBhIC56aXAgZmlsZS4NCg0KIyMgRGF0YXNldHM6DQoNCmEpICBBaXJsaW5lIERhdGE6DQoNCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQ0KICAgIGFpcmxpbmVfbm9kZXMgPC0gcmVhZF9jc3YoIi4vRGF0YS9BSVJMSU5FUy1OT0RFUy5jc3YiKSAlPiUNCiAgICAgIG11dGF0ZShJZCA9IElkICsgMSkNCiAgICBhaXJsaW5lX2VkZ2VzIDwtIHJlYWRfY3N2KCIuL0RhdGEvQUlSTElORVMtRURHRVMuY3N2IikgJT4lDQogICAgICBtdXRhdGUoU291cmNlID0gU291cmNlICsgMSwgVGFyZ2V0ID0gVGFyZ2V0ICsgMSkNCiAgICBgYGANCg0KDQpgYGB7ciwgZWNobz1GQUxTRX0NCmFpcmxpbmVfbm9kZXMgJT4lIA0KICBkb3dubG9hZF90aGlzKA0KICAgIG91dHB1dF9uYW1lID0gIkFJUkxJTkVTLU5PREVTIiwNCiAgICBvdXRwdXRfZXh0ZW5zaW9uID0gIi5jc3YiLA0KICAgIGJ1dHRvbl9sYWJlbCA9ICJEb3dubG9hZCBkYXRhIGFzIGNzdiIsDQogICAgYnV0dG9uX3R5cGUgPSAiZGVmYXVsdCIsDQogICAgaGFzX2ljb24gPSBUUlVFLA0KICAgIGljb24gPSAiZmEgZmEtc2F2ZSIsDQogICAgY2xhc3MgPSAiaHZyLXN3ZWVwLXRvLWxlZnQiDQogICkNCg0KYWlybGluZV9lZGdlcyAlPiUgDQogIGRvd25sb2FkX3RoaXMoDQogICAgb3V0cHV0X25hbWUgPSAiQUlSTElORVMtRURHRVMiLA0KICAgIG91dHB1dF9leHRlbnNpb24gPSAiLmNzdiIsDQogICAgYnV0dG9uX2xhYmVsID0gIkRvd25sb2FkIGRhdGEgYXMgY3N2IiwNCiAgICBidXR0b25fdHlwZSA9ICJkZWZhdWx0IiwNCiAgICBoYXNfaWNvbiA9IFRSVUUsDQogICAgaWNvbiA9ICJmYSBmYS1zYXZlIiwNCiAgICBjbGFzcyA9ICJodnItc3dlZXAtdG8tbGVmdCINCiAgKQ0KYGBgDQoNCiAtIEFzIGJlZm9yZSBjaGVjayB0aGF0IHlvdSB1c2UgYHJlYWRfZGVsaW0oKWAgcmF0aGVyIHRoYW4NCiAgICBgcmVhZF9jc3YoKWAsIGFzIG5lZWRlZC4NCg0KYikgIFRoZSBGYW1vdXMgWmFjaGFyeSBLYXJhdGUgQ2x1YiBkYXRhc2V0DQogLSBTdGFydCB3aXRoIHB1bGxpbmcgdGhpcyBkYXRhIGludG8geW91ciBSbWFya2Rvd246DQoNCjwhLS0gLS0+DQoNCiAgICBkYXRhKCJrYXJhdGUiLHBhY2thZ2U9ICJpZ3JhcGhkYXRhIikNCiAgICBrYXJhdGUNCg0KIC0gVHJ5IGA/a2FyYXRlYCBpbiB0aGUgY29uc29sZQ0KIC0gTm90ZSB0aGF0IHRoaXMgaXMgKipub3QqKiBhIHNldCBvZiBub2Rlcywgbm9yIGVkZ2VzLCAqKmJ1dCoqIGFscmVhZHkgYSAqKmdyYXBoLW9iamVjdCoqIQ0KIC0gU28gbm8gbmVlZCB0byBjcmVhdGUgYSBncmFwaCBvYmplY3QgdXNpbmcgYHRibF9ncmFwaGAuDQogLSBZb3Ugd2lsbCBuZWVkIHRvIGp1c3QgZ28gYWhlYWQgYW5kIHBsb3QgdXNpbmcgYGdncmFwaGAuDQoNCiMjIE1ha2UtMjogTGl0ZXJhcnkgTmV0d29yayB3aXRoIFRWIFNob3cgLyBCb29rIC8gU3RvcnkgLyBQbGF5DQoNClRoaXMgaXMgaW4gZ3JvdXBzLiBHcm91cHMgb2YgNC4gVG8gYmUgYW5ub3VuY2VkDQoNCllvdSBuZWVkIHRvIGNyZWF0ZSBhIE5ldHdvcmsgR3JhcGggZm9yIHlvdXIgZmF2b3VyaXRlICoqQm9vaywgcGxheSwgVFYgc2VyaWFsIG9yIFNob3cqKi4gKEUuZy4gQnJvb2tseW45OSwgRnJpZW5kcywgQkJULCBvciBMQiBvciBISU1ZTS4uLm9yIEhhbWxldCwgTGl0dGxlIFdvbWVuICwgUHJpZGUgYW5kIFByZWp1ZGljZSwgb3IgTG9UUikNCg0KU3RlcCAwLiBGaXJlIHVwIGEgbmV3IFJQcm9qZWN0LCBhbmQgYSBuZXcgUm1kIGZpbGUgd2l0aGluLg0KDQpTdGVwIDEuIEdvIHRvOiBbTGl0ZXJhcnkNCk5ldHdvcmtzXShodHRwczovL3d3dy50ZWFjaGVuZ2luZWVyaW5nLm9yZy9hY3Rpdml0aWVzL3ZpZXcvdW5vX2dyYXBodGhlb3J5X2xlc3NvbjAxX2FjdGl2aXR5MikgZm9yIGluc3RydWN0aW9ucy4gKipNYWtlIHlvdXIgZGF0YSoqIHVzaW5nIHRoZSBpbnN0cnVjdGlvbnMuDQoNCiAgLSBJbiB0aGUgbm9kZXMgZXhjZWwsIHVzZSBpZCBhbmQgbmFtZXMgYXMgeW91ciBjb2x1bW5zLiBBbnkgb3RoZXINCiAgICBkZXRhaWxzIGluIG90aGVyIGNvbHVtbnMgdG8gdGhlIHJpZ2h0Lg0KICAtIEluIHlvdXIgZWRnZXMgRXhjZWwgZmlsZSwgdXNlIGZyb20gYW5kIHRvIGFyZSB5b3VyIGZpcnN0IGNvbHVtbnMuIEVudHJpZXMgaW4gdGhlc2UgY29sdW1ucyBjYW4gYmUgbmFtZXMgb3IgaWRzIGJ1dCBiZSBjb25zaXN0ZW50IGFuZA0KICAgIGRvbid0IG1peC4NCiAgLSBFTlNVUkUgU1BFTExJTkdTIEFSRSBDT05TSVNURU5UIEZPUiBUSEUgTkFNRVMNCg0KU3RlcCAzLiBEZWNpZGUgb24gMyBhbnN3ZXJzIHRoYXQgeW91IHRvIHNlZWsgYW5kIHBsYW4gdG8gbWFrZSBncmFwaHMNCmZvci4NCg0KU3RlcCA0LiBDcmVhdGUgZ3JhcGggb2JqZWN0cy4gU2F5IDMgdmlzdWFsaXphdGlvbnMuDQoNClN0ZXAgNS4gV3JpdGUgY29tbWVudHMvYW5zd2VycyBpbiB0aGUgY29kZSBhbmQgbmFycmF0aXZlIHRleHQuIEFkZCBwaWN0dXJlcyBmcm9tIHRoZSB3ZWIgdXNpbmcgTWFya2Rvd24gc3ludGF4Lg0KDQpTdGVwIDYuIFdyaXRlIFJlZmxlY3Rpb24gKE9LLCBhIHNob3J0IG9uZSEpICppbnNpZGUqIHlvdXIgUk1hcmtkb3duLiBNYWtlIHN1cmUgaXQga25pdHMhIQ0KDQpTdGVwIDcuIEdyb3VwIFN1Ym1pc3Npb246IFN1Ym1pdCB0aGUgZW50aXJlIFByb2plY3QgYXMgYSB6aXAgZmlsZS4gRWFjaCBwZXJzb24gc3VibWl0cyBvbiB0aGVpciBBc3NpZ25tZW50cy4gQWxsIGdldCB0aGUgc2FtZSBncmFkZSBvbiB0aGlzIG9uZS4NCg0KQXNrIG1lIGZvciBjbGFyaWZpY2F0aW9ucyBvbiB3aGF0IHRvIGRvICphZnRlciogeW91IGhhdmUgcmVhZCB0aGUgSW5zdHJ1Y3Rpb25zIGluIHlvdXIgZ3JvdXAuDQoNCiMgUmVhZCBtb3JlDQoNCltUaG9tYXMgTGluIFBlZGVyc2VuIC0gMSBnaXJhZmZlLCAyDQpnaXJhZmZlLEdPIV0oaHR0cHM6Ly93d3cuZGF0YS1pbWFnaW5pc3QuY29tLzIwMTkvMS1naXJhZmZlLTItZ2lyYWZmZS1nby8pDQoNCklncmFwaDogTmV0d29yayBBbmFseXNpcyBhbmQgVmlzdWFsaXphdGlvbi4NCjxodHRwczovL0NSQU4uUi1wcm9qZWN0Lm9yZy9wYWNrYWdlPWlncmFwaD4uDQoNClBlZGVyc2VuLCBUaG9tYXMgTGluLiAyMDE3YS4gR2dyYXBoOiBBbiBJbXBsZW1lbnRhdGlvbiBvZiBHcmFtbWFyIG9mDQpHcmFwaGljcyBmb3IgR3JhcGhzIGFuZCBOZXR3b3Jrcy4NCjxodHRwczovL0NSQU4uUi1wcm9qZWN0Lm9yZy9wYWNrYWdlPWdncmFwaD4uDQoNCi0tLS0tLS0tLS4gMjAxN2IuIFRpZHlncmFwaDogQSBUaWR5IEFwaSBmb3IgR3JhcGggTWFuaXB1bGF0aW9uLg0KPGh0dHBzOi8vQ1JBTi5SLXByb2plY3Qub3JnL3BhY2thZ2U9dGlkeWdyYXBoPi4NCg0KVHluZXIsIFNhbSwgRnJhbsOnb2lzIEJyaWF0dGUsIGFuZCBIZWlrZSBIb2ZtYW5uLiAyMDE3LiAiTmV0d29yaw0KVmlzdWFsaXphdGlvbiB3aXRoIGdncGxvdDIuIiBUaGUgUiBKb3VybmFsIDkgKDEpOiAyNy0tNTkuDQo8aHR0cHM6Ly9qb3VybmFsLnItcHJvamVjdC5vcmcvYXJjaGl2ZS8yMDE3L1JKLTIwMTctMDIzL2luZGV4Lmh0bWw+Lg0KDQpOZXR3b3JrIERhdGFzZXRzIDxodHRwczovL2ljb24uY29sb3JhZG8uZWR1LyMhL25ldHdvcmtzPg0KDQpZdW5yYW4gQ2hlbiwgW0ludHJvZHVjdGlvbiB0byBOZXR3b3JrIEFuYWx5c2lzIFVzaW5nDQpSXShodHRwczovL3l1bnJhbmNoZW4uZ2l0aHViLmlvL2ludHJvLW5ldC1yL2luZGV4Lmh0bWwpDQo=