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

library(tidygraph)
library(igraph)
library(ggraph)

This session

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

  1. xxx

Introduction

  • The main concern in designing a network visualization is the purpose it has to serve.
  • What are the structural properties that we want to highlight? What are the key concerns we want to address?

  • Network maps are far from the only visualization available for graphs - other network representation formats, and even simple charts of key characteristics, may be more appropriate in some cases.

  • In network maps, as in other visualization formats, we have several key elements that control the outcome. The major ones are color, size, shape, and position.

Visualization Basics

# We load the highschool network nd make up som characteristics
set.seed(1337)
g <- as_tbl_graph(highschool, directed = TRUE) %E>%
  mutate(weight = sample(1:5, n(), replace = TRUE),
         year = year %>% as.factor()) %N>%
  mutate(class = sample(LETTERS[1:3], n(), replace = TRUE),
         gender = rbinom(n = n(), size = 1, prob = 0.5) %>% as.logical(),
         label = randomNames::randomNames(gender = gender, name.order = "first.last"))
set.seed(1337)
g <- g %N>%
  mutate(community = group_edge_betweenness(weights = weight, directed = TRUE) %>% as.factor()) %N>%
  filter(!node_is_isolated()) 
g <- g %N>%
  mutate(popular = case_when(
    centrality_degree(mode = 'in') < 5 ~ 'unpopular',
    centrality_degree(mode = 'in') >= 15 ~ 'popular',
    TRUE  ~ 'medium') %>% factor()
    )
g %N>%
  as_tibble() %>%
  head()

Node Visualization

  • Nodes in a network are the entities that are connected. Sometimes these are also referred to as vertices.
  • While the nodes in a graph are the abstract concepts of entities, and the layout is their physical placement, the node geoms are the visual manifestation of the entities.

Node positions

  • Conceptually one can simply think of it in terms of a scatter plot — the layout provides the x and y coordinates, and these can be used to draw nodes in different ways in the plotting window.
  • Actually, due to the design of ggraph the standard scatterplot-like geoms from ggplot2 can be used directly for plotting nodes:
set.seed(1337)
g %>%
  ggraph(layout = "nicely") + 
    geom_point(aes(x = x, y = y))

  • The reason this works is that layouts (about which we talk in a moment) return a data.frame of node positions and metadata and this is used as the default plot data:
set.seed(1337)
g_layout <- g %>% create_layout(layout = "nicely") %>% select(x,y) 
g_layout %>% head()
  • While usage of the default ggplot2 is theoreticlly fine, ggraph practically comes with its own set of node geoms (geom_node_*()).
  • They by default already inherit the layout x and y coordinates, and come with extra features for network visualization.
  • ggraph also comes with an own plotting theme (theme_graph()), which optimizes for graph visualization, and we might want to use.
g %>% ggraph(layout = g_layout) + 
  geom_node_point() +
  theme_graph()

  • Usually (but not always) when visualizing a network, we are interested in the connectivity structure as expressed by the interplay between nodes and edges.
  • So, lets also plot the edges (the geometries from the geom_edge_* family, about which we talk in a moment)
g %>% ggraph(layout = g_layout) + 
  geom_node_point() + 
  geom_edge_link(alpha = 0.25) +
  theme_graph()

Size

  • Size is the first obvious choice to highlight important (eg. central) nodes on a contineous scale.
g %>% ggraph(layout = g_layout) + 
  geom_edge_link(alpha = 0.25) +
  geom_node_point(aes(size = centrality_degree())) +
  theme_graph()

Color

  • Color can also be used to visualize importance in a second continuous dimension, or to highlight categorical features
g %>% ggraph(layout = g_layout) + 
  geom_edge_link(alpha = 0.25) +
  geom_node_point(aes(color = community)) +
  theme_graph()

Alpha

g %>% ggraph(layout = g_layout) + 
  geom_edge_link(alpha = 0.25) +
  geom_node_point(aes(alpha = centrality_degree())) +
  theme_graph()

Shapes

  • In case we want to express even more categorical characteristics, we can also (just like in the visualiation of tabular data) use node shapes.
shapes() 
 [1] "circle"     "crectangle" "csquare"    "none"       "pie"        "raster"     "rectangle" 
 [8] "sphere"     "square"     "vrectangle"
g %>% ggraph(layout = g_layout) + 
  geom_edge_link(alpha = 0.25) +
  geom_node_point(aes(shape = gender)) +
  theme_graph() 

Labels

  • With the geom_node_text geometry, we can also ad labels to the node. They are subject to common aestetics.
g %>% ggraph(layout = g_layout) + 
  geom_edge_link(alpha = 0.25) +
  geom_node_text(aes(label = label)) +
  theme_graph() 

In large graphs, plotting labels can appear messy, so it might make sense to only focus on important nodes to label

g %>% ggraph(layout = g_layout) + 
  geom_edge_link(alpha = 0.25) +
  geom_node_point() +
  geom_node_text(aes(label = label), repel = TRUE) +
  theme_graph() 

  • Still looks like too much. If we want to highlight only certain important nodes with label, we can also only plot them.
  • Note that (very practical) all ggraph geoms have a filter aestetic we can use for that
g %>% ggraph(layout = g_layout) + 
  geom_edge_link(alpha = 0.25) +
  geom_node_point() +
  geom_node_text(aes(label = label, 
                     filter = centrality_degree() >= centrality_degree()  %>% quantile(0.9)), 
                 repel = TRUE) +
  theme_graph() 

Combined node visualization tools

g %>% ggraph(layout = g_layout) + 
  geom_edge_link(alpha = 0.25) +
  geom_node_point(aes(size = centrality_eigen(), 
                      color = community,
                      shape = gender)) +
  geom_node_text(aes(label = label, 
                     filter = centrality_eigen() >= centrality_eigen() %>% quantile(0.90)), 
                 repel = TRUE) +
  theme_graph() +
  theme(legend.position = 'none')

Edge Visualization

  • So, now that we captured nodes, lets see how we can highlight aspects of edges, which are visualized with the geometries of the geom_edge_* family.

Weight

  • Obviously, the edge weight (=thickness)
g %>% ggraph(layout = g_layout) + 
  geom_edge_link(aes(width = weight), alpha = 0.25) +
  geom_node_point() +
  theme_graph() 

  • Unfortunately, I wind the default to thick. We can also scale it.
g %>% ggraph(layout = g_layout) + 
  geom_edge_link(aes(width = weight), alpha = 0.25) +
  scale_edge_width(range = c(0.1, 2)) + 
  geom_node_point() +
  theme_graph() 

Color

  • Color can also be used to highlight edge significance (continuous)
  • However, color is more often used to highlight different edge categories.
  • Notice, since we want to represent the colors of potentially multiple edges between a node pair, I now use the geom_edge_fan geometry.
g %>% ggraph(layout = g_layout) + 
  geom_edge_fan(aes(size = weight,
                     color = year), alpha = 0.25) +
  geom_node_point() +
  theme_graph() 

Density

  • Density plots can also be used to highlight densely connected regions.
g %>% ggraph(layout = g_layout) + 
  geom_edge_link(aes(col = year), alpha = 0.1) +
  geom_edge_density(aes(fill = year)) +
  geom_node_point() +
  theme_graph() 

Directionality

  • The easiest way to express directionality is by defining the arrow(), which comes with own aestetics.
g %>% ggraph(layout = g_layout) + 
  geom_edge_fan(aes(color = year), 
                arrow = arrow(),
                alpha = 0.5) +
  geom_node_point() +
  theme_graph() 

  • The default open arrow and other settings are a bit ugly, so we use some adittional aestetics
g %>% ggraph(layout = g_layout) + 
  geom_edge_fan(aes(color = year), 
                arrow = arrow(type = "closed", length = unit(2, "mm")),
                start_cap = circle(1, "mm"),
                end_cap = circle(1, "mm"),
                alpha = 0.5) +
  geom_node_point() +
  theme_graph() 

  • Another nice trick is to work with alphas or colors, which change between start and end node.
g %>%
  ggraph(layout = g_layout) + 
  geom_edge_fan(aes(color = year,
                    alpha = stat(index)) # Notice that
                ) +
  geom_node_point() +
  theme_graph() + 
  scale_edge_alpha("Edge direction", guide = "edge_direction")

  • It can also be really practical to change edge characteristics by the characteristics of their adjacent nodes.
  • Remember, with .N(), we can access them to do so.
set.seed(1337)
g %>%
  ggraph(layout = 'nicely') + 
  geom_edge_fan(aes(color = .N()$community[to]), # Notice that
                alpha = 0.5,
                show.legend = FALSE) +
  geom_node_point(aes(color = community),
                  show.legend = FALSE) +
  theme_graph() 

Layouts

  • The graph layout refers to the node position on the reference system.

Ordinary graph style

  • Graphs can be represented in simple geometries such as squares, circles, lines, or randomly.
  • Or, specialized algorithms can be used to position nodes according to the properties of their connectivity.
  • They are usually designed to highlight different aspects of the network.
  • Lets inspect some standard layouts
set.seed(1337)
library(ggpubr)
layout_list <- c("randomly", "linear", "circle", 
                 "grid", "fr", "kk", 
                 "graphopt", "stress", 'mds', 
                 'dh', 'drl', 'lgl')

g_list <- list(NULL)
for(i in 1:length(layout_list)){
  g_list[[i]] <-g %>% 
    ggraph(layout = layout_list[i]) + 
  geom_edge_fan(aes(color = year,
                    width = weight,
                    alpha = weight), 
                arrow = arrow(type = "closed", length = unit(2, "mm")),
                start_cap = circle(1, "mm"),
                end_cap = circle(1, "mm"),
                show.legend = FALSE) +
    scale_edge_width(range = c(0.1, 0.5)) + 
    geom_node_point(aes(size = centrality_degree(mode = 'in'), 
                        color = community,
                        shape = gender),
                    show.legend = FALSE) +
    theme_graph() +
    labs(title = paste("Layout:", layout_list[i], sep = " "))
}

ggarrange(plotlist = g_list, nrow = 4, ncol = 3)

Arcs and circles

# An arc diagram
g %>% ggraph(layout = 'linear') + 
  geom_edge_arc() +
  geom_node_point(aes(size = centrality_degree(), 
                      color = community),
                  show.legend = FALSE) +
    theme_graph() 

# An arc diagram
g %>% ggraph(layout = 'linear', circular = TRUE) + 
  geom_edge_arc(aes(color = .N()$community[to])) +
  geom_node_point(aes(size = centrality_degree(), 
                      color = community),
                  show.legend = FALSE) +
    theme_graph() +
  coord_fixed()

Hive plots

  • A hive plot, while still technically a node-edge diagram, is a bit different from the rest as it uses information pertaining to the nodes, rather than the connection information in the graph.
  • This means that hive plots, to a certain extent are more interpretable as well as less vulnerable to small changes in the graph structure.
  • They are less common though, so use will often require some additional explanation.
g %>%
  ggraph(layout = 'hive', axis = popular, sort.by = centrality_degree(mode = 'in')) + 
    geom_edge_hive(aes(colour = year, alpha = ..index..), show.legend = FALSE) + 
    geom_axis_hive(aes(colour = popular), size = 3, label = FALSE) + 
    coord_fixed() + 
  theme_graph() +
  theme(legend.position = 'bottom')

Social Fabric

g %>% ggraph(layout = 'fabric', sort.by = community) + 
  geom_node_range(aes(colour = community), alpha = 0.3) + 
  geom_edge_span(aes(col = .N()$community[to]), end_shape = 'circle', alpha = 0.5) + 
  coord_fixed() + 
  theme_graph() +
  theme(legend.position = 'none')

Visualizing Hirarchical networks

  • If the network is by definition hierarchical, edges can only exist between nodes of higher to lower dept (eg. tree structures).

  • This offers us possibility for quite some adittional ways of representing it which are geared towards hirarchical (=nested) structures

  • Here an example of the dependency structures of the flare package

edges <- flare$edges
vertices <- flare$vertices %>% arrange(name) %>% mutate(name=factor(name, name))
connections <- flare$imports
vertices %>% head()
edges %>% head()
connections %>% head()
g_hir <- tbl_graph(vertices, edges)
g_hir

Tree structures

g_hir %>% ggraph('tree') + 
  geom_edge_diagonal() +
  theme_graph()

g_hir %>% ggraph( 'dendrogram') + 
    geom_edge_elbow() +
  theme_graph()

g_hir %>% ggraph('dendrogram', circular = TRUE) + 
    geom_edge_elbow() + 
    coord_fixed() +
  theme_graph()

# The connection object must refer to the ids of the leaves:
from = match(connections$from, vertices$name)
to = match(connections$to, vertices$name)
g_hir %>% ggraph(layout = 'dendrogram', circular = TRUE) + 
  geom_conn_bundle(data = get_con(from = from, to = to), alpha = 0.1) + 
  geom_edge_diagonal0() +
  #geom_node_text(aes(filter = leaf, angle = node_angle(x, y), label = shortName),
  # hjust = 'outward', size = 2) +
  coord_fixed() +
  theme_graph()

Non-edge-based

# An icicle plot
g_hir %>% ggraph('partition') + 
  geom_node_tile(aes(fill = depth), size = 0.25) +
  theme_graph()

# A sunburst plot
g_hir %>% ggraph('partition', circular = TRUE) + 
  geom_node_arc_bar(aes(fill = depth), size = 0.25) + 
  coord_fixed() +
  theme_graph()

g_hir %>% ggraph('circlepack') + # , weight = size
  geom_node_circle(aes(fill = depth), size = 0.25, n = 50) + 
  coord_fixed() +
  theme_graph()

g_hir %>% ggraph('treemap') + 
  geom_node_tile(aes(fill = depth), size = 0.25) +
  theme_graph()

Geospatial networks

Defining a map

library(maps)
map_us <- map_data("usa")
map_us %>%
  head()

Getting some network data

library(anyflights)
us_airports <- get_airports() %>%
  filter(lat >= 24 & lat <= 49 & lon >= -124 & lon <= -66)  %>%
  rename(name_full = name,
         name = faa)
us_airports %>% head()
flights <- get_flights(station = us_airports %>% pull(name), year = 2015, month = 5)
flights %>% head()
edges <- flights %>% count(origin, dest, sort = TRUE) %>%
  rename(from = origin, to = dest, weight = n) %>%
  semi_join(us_airports, by = c('from' = 'name')) %>%
  semi_join(us_airports, by = c('to' = 'name')) %>% 
  filter(percent_rank(weight) >= 0.25)
g_geo <- tbl_graph(nodes = us_airports, edges = edges, directed = TRUE) %N>%
  filter(!node_is_isolated())

Constructing a graph

coords <- g_geo %N>% 
  as_tibble() %>% 
  select(lat, lon) %>%
  rename(x = lon, y = lat)
g_geo %>%
  ggraph(layout = coords) +
  geom_polygon(data = map_us, aes(x=long, y = lat, group = group), fill = "#CECECE", color = "#515151") + 
  geom_edge_arc(aes(width = weight,   
                    alpha = weight,
                    filter = percent_rank(weight) >= 0.75,
                    circular = FALSE),
                strength = 0.33,
                color = 'chocolate2') +
  geom_node_point(aes(size = centrality_degree(),
                      col = centrality_degree())) + 
  scale_edge_width_continuous(range = c(0.1, 1)) + 
  coord_fixed(1.3) + 
  theme_graph() + 
  theme(legend.position = 'none')

Interactive networks

  • There are numerous ways to to interactive network visualizations in R
  • For the sake of time, I just show you what I find the easiest and most consistent implementation (plotly unfortunately does by now not support ggraph)
library(ggiraph)
g_plot_int <- g %>% 
  ggraph(layout = layout_list[i]) + 
  geom_edge_fan(aes(color = year,
                    width = weight,
                    alpha = weight), 
                arrow = arrow(type = "closed", length = unit(2, "mm")),
                start_cap = circle(1, "mm"),
                end_cap = circle(1, "mm"),
                show.legend = FALSE) +
    scale_edge_width(range = c(0.1, 0.5)) + 
    geom_node_point(aes(size = centrality_degree(mode = 'in'), 
                        color = community,
                        shape = gender),
                    show.legend = FALSE) +
  geom_point_interactive(aes(x, y, # Notice this extra layer
                             tooltip = label, data_id = name, 
                             size = centrality_degree(mode = 'in')), alpha = 0.01) +  
    theme_graph() +
  theme(legend.position = 'none')
girafe(ggobj = g_plot_int, width_svg = 10, height_svg = 10) %>% 
    girafe_options(opts_zoom(max = 10), opts_tooltip(opacity = 0.7) )

Your turn

Please do Exercise 1 in the corresponding section on Github. This time you are about to do your own bibliographic analysis!

Endnotes

More info

Packages & Ecosystem

Other souces

Session info

sessionInfo()
R version 4.0.2 (2020-06-22)
Platform: x86_64-apple-darwin17.0 (64-bit)
Running under: macOS Catalina 10.15.7

Matrix products: default
BLAS:   /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib
LAPACK: /Library/Frameworks/R.framework/Versions/4.0/Resources/lib/libRlapack.dylib

locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
 [1] gdtools_0.2.2    ggiraph_0.7.8    anyflights_0.3.0 maps_3.3.0       ggpubr_0.4.0    
 [6] ggraph_2.0.3     igraph_1.2.5     tidygraph_1.2.0  magrittr_1.5     forcats_0.5.0   
[11] stringr_1.4.0    dplyr_1.0.2      purrr_0.3.4      readr_1.3.1      tidyr_1.1.2     
[16] tibble_3.0.3     ggplot2_3.3.2    tidyverse_1.3.0  knitr_1.30      

loaded via a namespace (and not attached):
 [1] fs_1.5.0            bit64_4.0.5         lubridate_1.7.9     progress_1.2.2     
 [5] httr_1.4.2          tools_4.0.2         backports_1.1.10    utf8_1.1.4         
 [9] R6_2.4.1            DBI_1.1.0           colorspace_1.4-1    withr_2.3.0        
[13] prettyunits_1.1.1   tidyselect_1.1.0    gridExtra_2.3       bit_4.0.4          
[17] curl_4.3            compiler_4.0.2      cli_2.0.2           rvest_0.3.6        
[21] pacman_0.5.1        randomNames_1.4-0.0 xml2_1.3.2          labeling_0.3       
[25] scales_1.1.1        systemfonts_0.3.2   digest_0.6.25       foreign_0.8-80     
[29] rmarkdown_2.4       rio_0.5.16          pkgconfig_2.0.3     htmltools_0.5.0    
[33] dbplyr_1.4.4        htmlwidgets_1.5.1   rlang_0.4.7         readxl_1.3.1       
[37] rstudioapi_0.11     farver_2.0.3        generics_0.0.2      jsonlite_1.7.1     
[41] vroom_1.3.2         zip_2.1.1           car_3.0-10          Rcpp_1.0.5         
[45] munsell_0.5.0       fansi_0.4.1         abind_1.4-5         viridis_0.5.1      
[49] lifecycle_0.2.0     stringi_1.5.3       yaml_2.2.1          carData_3.0-4      
[53] MASS_7.3-53         grid_4.0.2          blob_1.2.1          parallel_4.0.2     
[57] ggrepel_0.8.2       crayon_1.3.4        cowplot_1.1.0       graphlayouts_0.7.0 
[61] haven_2.3.1         hms_0.5.3           pillar_1.4.6        uuid_0.1-4         
[65] ggsignif_0.6.0      reprex_0.3.0        glue_1.4.2          evaluate_0.14      
[69] data.table_1.13.0   modelr_0.1.8        vctrs_0.3.4         tweenr_1.0.1       
[73] testthat_2.3.2      cellranger_1.1.0    gtable_0.3.0        polyclip_1.10-0    
[77] assertthat_0.2.1    xfun_0.18           ggforce_0.3.2       openxlsx_4.2.2     
[81] broom_0.7.1         rstatix_0.6.0       rsconnect_0.8.16    viridisLite_0.3.0  
[85] toOrdinal_1.1-0.0   ellipsis_0.3.1     
LS0tCnRpdGxlOiAnSW50ZXJtZWRpYXRlIE5ldHdvcmsgQW5hbHlzaXM6IE5ldHdvcmsgVml6dWFsaXphdGlvbjogQXBwbGljYXRpb24gKFIpJwphdXRob3I6ICJEYW5pZWwgUy4gSGFpbiAoZHNoQGJ1c2luZXNzLmFhdS5kaykiCmRhdGU6ICJVcGRhdGVkIGByIGZvcm1hdChTeXMudGltZSgpLCAnJUIgJWQsICVZJylgIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIGNvZGVfZm9sZGluZzogc2hvdwogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgICB0b2M6IHRydWUKICAgIHRvY19kZXB0aDogMgogICAgdG9jX2Zsb2F0OgogICAgICBjb2xsYXBzZWQ6IGZhbHNlCiAgICB0aGVtZTogZmxhdGx5Ci0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CiMjIyBHZW5lcmljIHByZWFtYmxlCnJtKGxpc3Q9bHMoKSkKU3lzLnNldGVudihMQU5HID0gImVuIikgIyBGb3IgZW5nbGlzaCBsYW5ndWFnZQpvcHRpb25zKHNjaXBlbiA9IDUpICMgVG8gZGVhY3RpdmF0ZSBhbm5veWluZyBzY2llbnRpZmljIG51bWJlciBub3RhdGlvbgoKIyMjIEtuaXRyIG9wdGlvbnMKbGlicmFyeShrbml0cikgIyBGb3IgZGlzcGxheSBvZiB0aGUgbWFya2Rvd24Ka25pdHI6Om9wdHNfY2h1bmskc2V0KHdhcm5pbmc9RkFMU0UsCiAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2U9RkFMU0UsCiAgICAgICAgICAgICAgICAgICAgIGNvbW1lbnQ9RkFMU0UsIAogICAgICAgICAgICAgICAgICAgICBmaWcuYWxpZ249ImNlbnRlciIKICAgICAgICAgICAgICAgICAgICAgKQpgYGAKCmBgYHtyfQojIyMgTG9hZCBzdGFuZGFyZHBhY2thZ2VzCmxpYnJhcnkodGlkeXZlcnNlKSAjIENvbGxlY3Rpb24gb2YgYWxsIHRoZSBnb29kIHN0dWZmIGxpa2UgZHBseXIsIGdncGxvdDIgZWN0LgpsaWJyYXJ5KG1hZ3JpdHRyKSAjIEZvciBleHRyYS1waXBpbmcgb3BlcmF0b3JzIChlZy4gJTw+JSkKCmxpYnJhcnkodGlkeWdyYXBoKQpsaWJyYXJ5KGlncmFwaCkKbGlicmFyeShnZ3JhcGgpCmBgYAoKIyMjIFRoaXMgc2Vzc2lvbgoKV2VsY29tZSB0byB5b3VyIHNlY29uZCBwYXJ0IG9mIHRoZSBpbnRyb2R1Y3Rpb24gdG8gbmV0d29yayBhbmFseXNpcy4gSW4gdGhpcyBzZXNzaW9uIHlvdSB3aWxsIGxlYXJuOgoKMS4geHh4CgoKCiMgSW50cm9kdWN0aW9uCgoqIFRoZSBtYWluIGNvbmNlcm4gaW4gZGVzaWduaW5nIGEgbmV0d29yayB2aXN1YWxpemF0aW9uIGlzIHRoZSBwdXJwb3NlIGl0IGhhcyB0byBzZXJ2ZS4gCiogV2hhdCBhcmUgdGhlIHN0cnVjdHVyYWwgcHJvcGVydGllcyB0aGF0IHdlIHdhbnQgdG8gaGlnaGxpZ2h0PyBXaGF0IGFyZSB0aGUga2V5IGNvbmNlcm5zIHdlIHdhbnQgdG8gYWRkcmVzcz8KCiFbXShodHRwczovL3Nkcy1hYXUuZ2l0aHViLmlvL1NEUy1tYXN0ZXIvMDBfbWVkaWEvbmV0d29ya3Nfdml6X2dvYWwucG5nKXt3aWR0aD01MDBweH0KCiogTmV0d29yayBtYXBzIGFyZSBmYXIgZnJvbSB0aGUgb25seSB2aXN1YWxpemF0aW9uIGF2YWlsYWJsZSBmb3IgZ3JhcGhzIC0gb3RoZXIgbmV0d29yayByZXByZXNlbnRhdGlvbiBmb3JtYXRzLCBhbmQgZXZlbiBzaW1wbGUgY2hhcnRzIG9mIGtleSBjaGFyYWN0ZXJpc3RpY3MsIG1heSBiZSBtb3JlIGFwcHJvcHJpYXRlIGluIHNvbWUgY2FzZXMuCgohW10oaHR0cHM6Ly9zZHMtYWF1LmdpdGh1Yi5pby9TRFMtbWFzdGVyLzAwX21lZGlhL25ldHdvcmtzX3Zpel90eXBlLnBuZyl7d2lkdGg9NTAwcHh9CgoqIEluIG5ldHdvcmsgbWFwcywgYXMgaW4gb3RoZXIgdmlzdWFsaXphdGlvbiBmb3JtYXRzLCB3ZSBoYXZlIHNldmVyYWwga2V5IGVsZW1lbnRzIHRoYXQgY29udHJvbCB0aGUgb3V0Y29tZS4gVGhlIG1ham9yIG9uZXMgYXJlIGNvbG9yLCBzaXplLCBzaGFwZSwgYW5kIHBvc2l0aW9uLgoKIVtdKGh0dHBzOi8vc2RzLWFhdS5naXRodWIuaW8vU0RTLW1hc3Rlci8wMF9tZWRpYS9uZXR3b3Jrc192aXpfY29udHJvbHMucG5nKXt3aWR0aD01MDBweH0KCiMgVmlzdWFsaXphdGlvbiBCYXNpY3MKCmBgYHtyfQojIFdlIGxvYWQgdGhlIGhpZ2hzY2hvb2wgbmV0d29yayBuZCBtYWtlIHVwIHNvbSBjaGFyYWN0ZXJpc3RpY3MKc2V0LnNlZWQoMTMzNykKZyA8LSBhc190YmxfZ3JhcGgoaGlnaHNjaG9vbCwgZGlyZWN0ZWQgPSBUUlVFKSAlRT4lCiAgbXV0YXRlKHdlaWdodCA9IHNhbXBsZSgxOjUsIG4oKSwgcmVwbGFjZSA9IFRSVUUpLAogICAgICAgICB5ZWFyID0geWVhciAlPiUgYXMuZmFjdG9yKCkpICVOPiUKICBtdXRhdGUoY2xhc3MgPSBzYW1wbGUoTEVUVEVSU1sxOjNdLCBuKCksIHJlcGxhY2UgPSBUUlVFKSwKICAgICAgICAgZ2VuZGVyID0gcmJpbm9tKG4gPSBuKCksIHNpemUgPSAxLCBwcm9iID0gMC41KSAlPiUgYXMubG9naWNhbCgpLAogICAgICAgICBsYWJlbCA9IHJhbmRvbU5hbWVzOjpyYW5kb21OYW1lcyhnZW5kZXIgPSBnZW5kZXIsIG5hbWUub3JkZXIgPSAiZmlyc3QubGFzdCIpKQpgYGAKCmBgYHtyLHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGVycm9yPUZBTFNFLCBjb21tZW50PUZBTFNFfQpzZXQuc2VlZCgxMzM3KQpnIDwtIGcgJU4+JQogIG11dGF0ZShjb21tdW5pdHkgPSBncm91cF9lZGdlX2JldHdlZW5uZXNzKHdlaWdodHMgPSB3ZWlnaHQsIGRpcmVjdGVkID0gVFJVRSkgJT4lIGFzLmZhY3RvcigpKSAlTj4lCiAgZmlsdGVyKCFub2RlX2lzX2lzb2xhdGVkKCkpIApgYGAKCmBgYHtyfQpnIDwtIGcgJU4+JQogIG11dGF0ZShwb3B1bGFyID0gY2FzZV93aGVuKAogICAgY2VudHJhbGl0eV9kZWdyZWUobW9kZSA9ICdpbicpIDwgNSB+ICd1bnBvcHVsYXInLAogICAgY2VudHJhbGl0eV9kZWdyZWUobW9kZSA9ICdpbicpID49IDE1IH4gJ3BvcHVsYXInLAogICAgVFJVRSAgfiAnbWVkaXVtJykgJT4lIGZhY3RvcigpCiAgICApCmBgYAoKYGBge3J9CmcgJU4+JQogIGFzX3RpYmJsZSgpICU+JQogIGhlYWQoKQpgYGAKCiMjIE5vZGUgVmlzdWFsaXphdGlvbgoKKiBOb2RlcyBpbiBhIG5ldHdvcmsgYXJlIHRoZSBlbnRpdGllcyB0aGF0IGFyZSBjb25uZWN0ZWQuIFNvbWV0aW1lcyB0aGVzZSBhcmUgYWxzbyByZWZlcnJlZCB0byBhcyB2ZXJ0aWNlcy4gCiogV2hpbGUgdGhlIG5vZGVzIGluIGEgZ3JhcGggYXJlIHRoZSBhYnN0cmFjdCBjb25jZXB0cyBvZiBlbnRpdGllcywgYW5kIHRoZSBsYXlvdXQgaXMgdGhlaXIgcGh5c2ljYWwgcGxhY2VtZW50LCB0aGUgbm9kZSBnZW9tcyBhcmUgdGhlIHZpc3VhbCBtYW5pZmVzdGF0aW9uIG9mIHRoZSBlbnRpdGllcy4gCgojIyMgTm9kZSBwb3NpdGlvbnMKCiogQ29uY2VwdHVhbGx5IG9uZSBjYW4gc2ltcGx5IHRoaW5rIG9mIGl0IGluIHRlcm1zIG9mIGEgc2NhdHRlciBwbG90IOKAlCB0aGUgbGF5b3V0IHByb3ZpZGVzIHRoZSB4IGFuZCB5IGNvb3JkaW5hdGVzLCBhbmQgdGhlc2UgY2FuIGJlIHVzZWQgdG8gZHJhdyBub2RlcyBpbiBkaWZmZXJlbnQgd2F5cyBpbiB0aGUgcGxvdHRpbmcgd2luZG93LiAKKiBBY3R1YWxseSwgZHVlIHRvIHRoZSBkZXNpZ24gb2YgZ2dyYXBoIHRoZSBzdGFuZGFyZCBzY2F0dGVycGxvdC1saWtlIGdlb21zIGZyb20gZ2dwbG90MiBjYW4gYmUgdXNlZCBkaXJlY3RseSBmb3IgcGxvdHRpbmcgbm9kZXM6CgpgYGB7cn0Kc2V0LnNlZWQoMTMzNykKZyAlPiUKICBnZ3JhcGgobGF5b3V0ID0gIm5pY2VseSIpICsgCiAgICBnZW9tX3BvaW50KGFlcyh4ID0geCwgeSA9IHkpKQpgYGAKCiogVGhlIHJlYXNvbiB0aGlzIHdvcmtzIGlzIHRoYXQgbGF5b3V0cyAoYWJvdXQgd2hpY2ggd2UgdGFsayBpbiBhIG1vbWVudCkgcmV0dXJuIGEgYGRhdGEuZnJhbWVgIG9mIG5vZGUgcG9zaXRpb25zIGFuZCBtZXRhZGF0YSBhbmQgdGhpcyBpcyB1c2VkIGFzIHRoZSBkZWZhdWx0IHBsb3QgZGF0YToKCmBgYHtyfQpzZXQuc2VlZCgxMzM3KQpnX2xheW91dCA8LSBnICU+JSBjcmVhdGVfbGF5b3V0KGxheW91dCA9ICJuaWNlbHkiKSAlPiUgc2VsZWN0KHgseSkgCmBgYAoKYGBge3J9CmdfbGF5b3V0ICU+JSBoZWFkKCkKYGBgCiogV2hpbGUgdXNhZ2Ugb2YgdGhlIGRlZmF1bHQgYGdncGxvdDJgIGlzIHRoZW9yZXRpY2xseSBmaW5lLCBgZ2dyYXBoYCBwcmFjdGljYWxseSBjb21lcyB3aXRoIGl0cyBvd24gc2V0IG9mIG5vZGUgZ2VvbXMgKGBnZW9tX25vZGVfKigpYCkuIAoqIFRoZXkgYnkgZGVmYXVsdCBhbHJlYWR5IGluaGVyaXQgdGhlIGxheW91dCB4IGFuZCB5IGNvb3JkaW5hdGVzLCBhbmQgY29tZSB3aXRoIGV4dHJhIGZlYXR1cmVzIGZvciBuZXR3b3JrIHZpc3VhbGl6YXRpb24uCiogYGdncmFwaGAgYWxzbyBjb21lcyB3aXRoIGFuIG93biBwbG90dGluZyB0aGVtZSAoYHRoZW1lX2dyYXBoKClgKSwgd2hpY2ggb3B0aW1pemVzIGZvciBncmFwaCB2aXN1YWxpemF0aW9uLCBhbmQgd2UgbWlnaHQgd2FudCB0byB1c2UuCgpgYGB7cn0KZyAlPiUgZ2dyYXBoKGxheW91dCA9IGdfbGF5b3V0KSArIAogIGdlb21fbm9kZV9wb2ludCgpICsKICB0aGVtZV9ncmFwaCgpCmBgYAoKKiBVc3VhbGx5IChidXQgbm90IGFsd2F5cykgd2hlbiB2aXN1YWxpemluZyBhIG5ldHdvcmssIHdlIGFyZSBpbnRlcmVzdGVkIGluIHRoZSBjb25uZWN0aXZpdHkgc3RydWN0dXJlIGFzIGV4cHJlc3NlZCBieSB0aGUgaW50ZXJwbGF5IGJldHdlZW4gbm9kZXMgYW5kIGVkZ2VzLiAKKiBTbywgbGV0cyBhbHNvIHBsb3QgdGhlIGVkZ2VzICh0aGUgZ2VvbWV0cmllcyBmcm9tIHRoZSBgZ2VvbV9lZGdlXypgIGZhbWlseSwgYWJvdXQgd2hpY2ggd2UgdGFsayBpbiBhIG1vbWVudCkKCmBgYHtyfQpnICU+JSBnZ3JhcGgobGF5b3V0ID0gZ19sYXlvdXQpICsgCiAgZ2VvbV9ub2RlX3BvaW50KCkgKyAKICBnZW9tX2VkZ2VfbGluayhhbHBoYSA9IDAuMjUpICsKICB0aGVtZV9ncmFwaCgpCmBgYAoKIyMjIFNpemUKCiogU2l6ZSBpcyB0aGUgZmlyc3Qgb2J2aW91cyBjaG9pY2UgdG8gaGlnaGxpZ2h0IGltcG9ydGFudCAoZWcuIGNlbnRyYWwpIG5vZGVzIG9uIGEgY29udGluZW91cyBzY2FsZS4KCmBgYHtyfQpnICU+JSBnZ3JhcGgobGF5b3V0ID0gZ19sYXlvdXQpICsgCiAgZ2VvbV9lZGdlX2xpbmsoYWxwaGEgPSAwLjI1KSArCiAgZ2VvbV9ub2RlX3BvaW50KGFlcyhzaXplID0gY2VudHJhbGl0eV9kZWdyZWUoKSkpICsKICB0aGVtZV9ncmFwaCgpCmBgYAoKIyMjIENvbG9yCgoqIENvbG9yIGNhbiBhbHNvIGJlIHVzZWQgdG8gdmlzdWFsaXplIGltcG9ydGFuY2UgaW4gYSBzZWNvbmQgY29udGludW91cyBkaW1lbnNpb24sIG9yIHRvIGhpZ2hsaWdodCBjYXRlZ29yaWNhbCBmZWF0dXJlcwoKYGBge3J9CmcgJT4lIGdncmFwaChsYXlvdXQgPSBnX2xheW91dCkgKyAKICBnZW9tX2VkZ2VfbGluayhhbHBoYSA9IDAuMjUpICsKICBnZW9tX25vZGVfcG9pbnQoYWVzKGNvbG9yID0gY29tbXVuaXR5KSkgKwogIHRoZW1lX2dyYXBoKCkKYGBgCgojIyMgQWxwaGEKCmBgYHtyfQpnICU+JSBnZ3JhcGgobGF5b3V0ID0gZ19sYXlvdXQpICsgCiAgZ2VvbV9lZGdlX2xpbmsoYWxwaGEgPSAwLjI1KSArCiAgZ2VvbV9ub2RlX3BvaW50KGFlcyhhbHBoYSA9IGNlbnRyYWxpdHlfZGVncmVlKCkpKSArCiAgdGhlbWVfZ3JhcGgoKQpgYGAKCiMjIyBTaGFwZXMKCiogSW4gY2FzZSB3ZSB3YW50IHRvIGV4cHJlc3MgZXZlbiBtb3JlIGNhdGVnb3JpY2FsIGNoYXJhY3RlcmlzdGljcywgd2UgY2FuIGFsc28gKGp1c3QgbGlrZSBpbiB0aGUgdmlzdWFsaWF0aW9uIG9mIHRhYnVsYXIgZGF0YSkgdXNlIG5vZGUgc2hhcGVzLgoKYGBge3J9CnNoYXBlcygpIApgYGAKCmBgYHtyfQpnICU+JSBnZ3JhcGgobGF5b3V0ID0gZ19sYXlvdXQpICsgCiAgZ2VvbV9lZGdlX2xpbmsoYWxwaGEgPSAwLjI1KSArCiAgZ2VvbV9ub2RlX3BvaW50KGFlcyhzaGFwZSA9IGdlbmRlcikpICsKICB0aGVtZV9ncmFwaCgpIApgYGAKCiMjIyBMYWJlbHMKCiogV2l0aCB0aGUgYGdlb21fbm9kZV90ZXh0YCBnZW9tZXRyeSwgd2UgY2FuIGFsc28gYWQgbGFiZWxzIHRvIHRoZSBub2RlLiBUaGV5IGFyZSBzdWJqZWN0IHRvIGNvbW1vbiBhZXN0ZXRpY3MuCgpgYGB7cn0KZyAlPiUgZ2dyYXBoKGxheW91dCA9IGdfbGF5b3V0KSArIAogIGdlb21fZWRnZV9saW5rKGFscGhhID0gMC4yNSkgKwogIGdlb21fbm9kZV90ZXh0KGFlcyhsYWJlbCA9IGxhYmVsKSkgKwogIHRoZW1lX2dyYXBoKCkgCmBgYAoKSW4gbGFyZ2UgZ3JhcGhzLCBwbG90dGluZyBsYWJlbHMgY2FuIGFwcGVhciBtZXNzeSwgc28gaXQgbWlnaHQgbWFrZSBzZW5zZSB0byBvbmx5IGZvY3VzIG9uIGltcG9ydGFudCBub2RlcyB0byBsYWJlbAoKYGBge3J9CmcgJT4lIGdncmFwaChsYXlvdXQgPSBnX2xheW91dCkgKyAKICBnZW9tX2VkZ2VfbGluayhhbHBoYSA9IDAuMjUpICsKICBnZW9tX25vZGVfcG9pbnQoKSArCiAgZ2VvbV9ub2RlX3RleHQoYWVzKGxhYmVsID0gbGFiZWwpLCByZXBlbCA9IFRSVUUpICsKICB0aGVtZV9ncmFwaCgpIApgYGAKCiogU3RpbGwgbG9va3MgbGlrZSB0b28gbXVjaC4gSWYgd2Ugd2FudCB0byBoaWdobGlnaHQgb25seSBjZXJ0YWluIGltcG9ydGFudCBub2RlcyB3aXRoIGxhYmVsLCB3ZSBjYW4gYWxzbyBvbmx5IHBsb3QgdGhlbS4KKiBOb3RlIHRoYXQgKHZlcnkgcHJhY3RpY2FsKSBhbGwgYGdncmFwaGAgZ2VvbXMgaGF2ZSBhIGBmaWx0ZXJgIGFlc3RldGljIHdlIGNhbiB1c2UgZm9yIHRoYXQKCgpgYGB7cn0KZyAlPiUgZ2dyYXBoKGxheW91dCA9IGdfbGF5b3V0KSArIAogIGdlb21fZWRnZV9saW5rKGFscGhhID0gMC4yNSkgKwogIGdlb21fbm9kZV9wb2ludCgpICsKICBnZW9tX25vZGVfdGV4dChhZXMobGFiZWwgPSBsYWJlbCwgCiAgICAgICAgICAgICAgICAgICAgIGZpbHRlciA9IGNlbnRyYWxpdHlfZGVncmVlKCkgPj0gY2VudHJhbGl0eV9kZWdyZWUoKSAgJT4lIHF1YW50aWxlKDAuOSkpLCAKICAgICAgICAgICAgICAgICByZXBlbCA9IFRSVUUpICsKICB0aGVtZV9ncmFwaCgpIApgYGAKCiMjIyBDb21iaW5lZCBub2RlIHZpc3VhbGl6YXRpb24gdG9vbHMKCmBgYHtyfQpnICU+JSBnZ3JhcGgobGF5b3V0ID0gZ19sYXlvdXQpICsgCiAgZ2VvbV9lZGdlX2xpbmsoYWxwaGEgPSAwLjI1KSArCiAgZ2VvbV9ub2RlX3BvaW50KGFlcyhzaXplID0gY2VudHJhbGl0eV9laWdlbigpLCAKICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gY29tbXVuaXR5LAogICAgICAgICAgICAgICAgICAgICAgc2hhcGUgPSBnZW5kZXIpKSArCiAgZ2VvbV9ub2RlX3RleHQoYWVzKGxhYmVsID0gbGFiZWwsIAogICAgICAgICAgICAgICAgICAgICBmaWx0ZXIgPSBjZW50cmFsaXR5X2VpZ2VuKCkgPj0gY2VudHJhbGl0eV9laWdlbigpICU+JSBxdWFudGlsZSgwLjkwKSksIAogICAgICAgICAgICAgICAgIHJlcGVsID0gVFJVRSkgKwogIHRoZW1lX2dyYXBoKCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICdub25lJykKYGBgCgoKIyMgRWRnZSBWaXN1YWxpemF0aW9uCgoqIFNvLCBub3cgdGhhdCB3ZSBjYXB0dXJlZCBub2RlcywgbGV0cyBzZWUgaG93IHdlIGNhbiBoaWdobGlnaHQgYXNwZWN0cyBvZiBlZGdlcywgd2hpY2ggYXJlIHZpc3VhbGl6ZWQgd2l0aCB0aGUgZ2VvbWV0cmllcyBvZiB0aGUgYGdlb21fZWRnZV8qYCBmYW1pbHkuCgojIyMgV2VpZ2h0CgoqIE9idmlvdXNseSwgdGhlIGVkZ2Ugd2VpZ2h0ICg9dGhpY2tuZXNzKQoKYGBge3J9CmcgJT4lIGdncmFwaChsYXlvdXQgPSBnX2xheW91dCkgKyAKICBnZW9tX2VkZ2VfbGluayhhZXMod2lkdGggPSB3ZWlnaHQpLCBhbHBoYSA9IDAuMjUpICsKICBnZW9tX25vZGVfcG9pbnQoKSArCiAgdGhlbWVfZ3JhcGgoKSAKYGBgCgoqIFVuZm9ydHVuYXRlbHksIEkgd2luZCB0aGUgZGVmYXVsdCB0byB0aGljay4gV2UgY2FuIGFsc28gc2NhbGUgaXQuCgpgYGB7cn0KZyAlPiUgZ2dyYXBoKGxheW91dCA9IGdfbGF5b3V0KSArIAogIGdlb21fZWRnZV9saW5rKGFlcyh3aWR0aCA9IHdlaWdodCksIGFscGhhID0gMC4yNSkgKwogIHNjYWxlX2VkZ2Vfd2lkdGgocmFuZ2UgPSBjKDAuMSwgMikpICsgCiAgZ2VvbV9ub2RlX3BvaW50KCkgKwogIHRoZW1lX2dyYXBoKCkgCmBgYAoKIyMjIENvbG9yCgoqIENvbG9yIGNhbiBhbHNvIGJlIHVzZWQgdG8gaGlnaGxpZ2h0IGVkZ2Ugc2lnbmlmaWNhbmNlIChjb250aW51b3VzKQoqIEhvd2V2ZXIsIGNvbG9yIGlzIG1vcmUgb2Z0ZW4gdXNlZCB0byBoaWdobGlnaHQgZGlmZmVyZW50IGVkZ2UgY2F0ZWdvcmllcy4KKiBOb3RpY2UsIHNpbmNlIHdlIHdhbnQgdG8gcmVwcmVzZW50IHRoZSBjb2xvcnMgb2YgcG90ZW50aWFsbHkgbXVsdGlwbGUgZWRnZXMgYmV0d2VlbiBhIG5vZGUgcGFpciwgSSBub3cgdXNlIHRoZSBgZ2VvbV9lZGdlX2ZhbmAgZ2VvbWV0cnkuCgpgYGB7cn0KZyAlPiUgZ2dyYXBoKGxheW91dCA9IGdfbGF5b3V0KSArIAogIGdlb21fZWRnZV9mYW4oYWVzKHNpemUgPSB3ZWlnaHQsCiAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0geWVhciksIGFscGhhID0gMC4yNSkgKwogIGdlb21fbm9kZV9wb2ludCgpICsKICB0aGVtZV9ncmFwaCgpIApgYGAKCiMjIyBEZW5zaXR5CgoqIERlbnNpdHkgcGxvdHMgY2FuIGFsc28gYmUgdXNlZCB0byBoaWdobGlnaHQgZGVuc2VseSBjb25uZWN0ZWQgcmVnaW9ucy4KCmBgYHtyfQpnICU+JSBnZ3JhcGgobGF5b3V0ID0gZ19sYXlvdXQpICsgCiAgZ2VvbV9lZGdlX2xpbmsoYWVzKGNvbCA9IHllYXIpLCBhbHBoYSA9IDAuMSkgKwogIGdlb21fZWRnZV9kZW5zaXR5KGFlcyhmaWxsID0geWVhcikpICsKICBnZW9tX25vZGVfcG9pbnQoKSArCiAgdGhlbWVfZ3JhcGgoKSAKYGBgCgojIyMgRGlyZWN0aW9uYWxpdHkKCiogVGhlIGVhc2llc3Qgd2F5IHRvIGV4cHJlc3MgZGlyZWN0aW9uYWxpdHkgaXMgYnkgZGVmaW5pbmcgdGhlIGBhcnJvdygpYCwgd2hpY2ggY29tZXMgd2l0aCBvd24gYWVzdGV0aWNzLgoKYGBge3J9CmcgJT4lIGdncmFwaChsYXlvdXQgPSBnX2xheW91dCkgKyAKICBnZW9tX2VkZ2VfZmFuKGFlcyhjb2xvciA9IHllYXIpLCAKICAgICAgICAgICAgICAgIGFycm93ID0gYXJyb3coKSwKICAgICAgICAgICAgICAgIGFscGhhID0gMC41KSArCiAgZ2VvbV9ub2RlX3BvaW50KCkgKwogIHRoZW1lX2dyYXBoKCkgCmBgYAoKKiBUaGUgZGVmYXVsdCBvcGVuIGFycm93IGFuZCBvdGhlciBzZXR0aW5ncyBhcmUgYSBiaXQgdWdseSwgc28gd2UgdXNlIHNvbWUgYWRpdHRpb25hbCBhZXN0ZXRpY3MKCmBgYHtyfQpnICU+JSBnZ3JhcGgobGF5b3V0ID0gZ19sYXlvdXQpICsgCiAgZ2VvbV9lZGdlX2ZhbihhZXMoY29sb3IgPSB5ZWFyKSwgCiAgICAgICAgICAgICAgICBhcnJvdyA9IGFycm93KHR5cGUgPSAiY2xvc2VkIiwgbGVuZ3RoID0gdW5pdCgyLCAibW0iKSksCiAgICAgICAgICAgICAgICBzdGFydF9jYXAgPSBjaXJjbGUoMSwgIm1tIiksCiAgICAgICAgICAgICAgICBlbmRfY2FwID0gY2lyY2xlKDEsICJtbSIpLAogICAgICAgICAgICAgICAgYWxwaGEgPSAwLjUpICsKICBnZW9tX25vZGVfcG9pbnQoKSArCiAgdGhlbWVfZ3JhcGgoKSAKYGBgCgoqIEFub3RoZXIgbmljZSB0cmljayBpcyB0byB3b3JrIHdpdGggYWxwaGFzIG9yIGNvbG9ycywgd2hpY2ggY2hhbmdlIGJldHdlZW4gc3RhcnQgYW5kIGVuZCBub2RlLgoKYGBge3J9CmcgJT4lCiAgZ2dyYXBoKGxheW91dCA9IGdfbGF5b3V0KSArIAogIGdlb21fZWRnZV9mYW4oYWVzKGNvbG9yID0geWVhciwKICAgICAgICAgICAgICAgICAgICBhbHBoYSA9IHN0YXQoaW5kZXgpKSAjIE5vdGljZSB0aGF0CiAgICAgICAgICAgICAgICApICsKICBnZW9tX25vZGVfcG9pbnQoKSArCiAgdGhlbWVfZ3JhcGgoKSArIAogIHNjYWxlX2VkZ2VfYWxwaGEoIkVkZ2UgZGlyZWN0aW9uIiwgZ3VpZGUgPSAiZWRnZV9kaXJlY3Rpb24iKQpgYGAKCiogSXQgY2FuIGFsc28gYmUgcmVhbGx5IHByYWN0aWNhbCB0byBjaGFuZ2UgZWRnZSBjaGFyYWN0ZXJpc3RpY3MgYnkgdGhlIGNoYXJhY3RlcmlzdGljcyBvZiB0aGVpciBhZGphY2VudCBub2Rlcy4gCiogUmVtZW1iZXIsIHdpdGggYC5OKClgLCB3ZSBjYW4gYWNjZXNzIHRoZW0gdG8gZG8gc28uCgpgYGB7cn0Kc2V0LnNlZWQoMTMzNykKZyAlPiUKICBnZ3JhcGgobGF5b3V0ID0gJ25pY2VseScpICsgCiAgZ2VvbV9lZGdlX2ZhbihhZXMoY29sb3IgPSAuTigpJGNvbW11bml0eVt0b10pLCAjIE5vdGljZSB0aGF0CiAgICAgICAgICAgICAgICBhbHBoYSA9IDAuNSwKICAgICAgICAgICAgICAgIHNob3cubGVnZW5kID0gRkFMU0UpICsKICBnZW9tX25vZGVfcG9pbnQoYWVzKGNvbG9yID0gY29tbXVuaXR5KSwKICAgICAgICAgICAgICAgICAgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogIHRoZW1lX2dyYXBoKCkgCmBgYAoKCiMjIExheW91dHMKCiogVGhlIGdyYXBoIGxheW91dCByZWZlcnMgdG8gdGhlIG5vZGUgcG9zaXRpb24gb24gdGhlIHJlZmVyZW5jZSBzeXN0ZW0uCgojIyMgT3JkaW5hcnkgZ3JhcGggc3R5bGUKCiogR3JhcGhzIGNhbiBiZSByZXByZXNlbnRlZCBpbiBzaW1wbGUgZ2VvbWV0cmllcyBzdWNoIGFzIHNxdWFyZXMsIGNpcmNsZXMsIGxpbmVzLCBvciByYW5kb21seS4KKiBPciwgc3BlY2lhbGl6ZWQgYWxnb3JpdGhtcyBjYW4gYmUgdXNlZCB0byBwb3NpdGlvbiBub2RlcyBhY2NvcmRpbmcgdG8gdGhlIHByb3BlcnRpZXMgb2YgdGhlaXIgY29ubmVjdGl2aXR5LgoqIFRoZXkgYXJlIHVzdWFsbHkgZGVzaWduZWQgdG8gaGlnaGxpZ2h0IGRpZmZlcmVudCBhc3BlY3RzIG9mIHRoZSBuZXR3b3JrLgoqIExldHMgaW5zcGVjdCBzb21lIHN0YW5kYXJkIGxheW91dHMKCmBgYHtyLCBmaWcuaGVpZ2h0PTMwLCBmaWcud2lkdGg9IDIwfQpzZXQuc2VlZCgxMzM3KQpsaWJyYXJ5KGdncHVicikKbGF5b3V0X2xpc3QgPC0gYygicmFuZG9tbHkiLCAibGluZWFyIiwgImNpcmNsZSIsIAogICAgICAgICAgICAgICAgICJncmlkIiwgImZyIiwgImtrIiwgCiAgICAgICAgICAgICAgICAgImdyYXBob3B0IiwgInN0cmVzcyIsICdtZHMnLCAKICAgICAgICAgICAgICAgICAnZGgnLCAnZHJsJywgJ2xnbCcpCgpnX2xpc3QgPC0gbGlzdChOVUxMKQpmb3IoaSBpbiAxOmxlbmd0aChsYXlvdXRfbGlzdCkpewogIGdfbGlzdFtbaV1dIDwtZyAlPiUgCiAgICBnZ3JhcGgobGF5b3V0ID0gbGF5b3V0X2xpc3RbaV0pICsgCiAgZ2VvbV9lZGdlX2ZhbihhZXMoY29sb3IgPSB5ZWFyLAogICAgICAgICAgICAgICAgICAgIHdpZHRoID0gd2VpZ2h0LAogICAgICAgICAgICAgICAgICAgIGFscGhhID0gd2VpZ2h0KSwgCiAgICAgICAgICAgICAgICBhcnJvdyA9IGFycm93KHR5cGUgPSAiY2xvc2VkIiwgbGVuZ3RoID0gdW5pdCgyLCAibW0iKSksCiAgICAgICAgICAgICAgICBzdGFydF9jYXAgPSBjaXJjbGUoMSwgIm1tIiksCiAgICAgICAgICAgICAgICBlbmRfY2FwID0gY2lyY2xlKDEsICJtbSIpLAogICAgICAgICAgICAgICAgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogICAgc2NhbGVfZWRnZV93aWR0aChyYW5nZSA9IGMoMC4xLCAwLjUpKSArIAogICAgZ2VvbV9ub2RlX3BvaW50KGFlcyhzaXplID0gY2VudHJhbGl0eV9kZWdyZWUobW9kZSA9ICdpbicpLCAKICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSBjb21tdW5pdHksCiAgICAgICAgICAgICAgICAgICAgICAgIHNoYXBlID0gZ2VuZGVyKSwKICAgICAgICAgICAgICAgICAgICBzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgICB0aGVtZV9ncmFwaCgpICsKICAgIGxhYnModGl0bGUgPSBwYXN0ZSgiTGF5b3V0OiIsIGxheW91dF9saXN0W2ldLCBzZXAgPSAiICIpKQp9CgpnZ2FycmFuZ2UocGxvdGxpc3QgPSBnX2xpc3QsIG5yb3cgPSA0LCBuY29sID0gMykKYGBgCgoKIyMjIEFyY3MgYW5kIGNpcmNsZXMKCmBgYHtyfQojIEFuIGFyYyBkaWFncmFtCmcgJT4lIGdncmFwaChsYXlvdXQgPSAnbGluZWFyJykgKyAKICBnZW9tX2VkZ2VfYXJjKCkgKwogIGdlb21fbm9kZV9wb2ludChhZXMoc2l6ZSA9IGNlbnRyYWxpdHlfZGVncmVlKCksIAogICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSBjb21tdW5pdHkpLAogICAgICAgICAgICAgICAgICBzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgICB0aGVtZV9ncmFwaCgpIApgYGAKCmBgYHtyfQojIEFuIGFyYyBkaWFncmFtCmcgJT4lIGdncmFwaChsYXlvdXQgPSAnbGluZWFyJywgY2lyY3VsYXIgPSBUUlVFKSArIAogIGdlb21fZWRnZV9hcmMoYWVzKGNvbG9yID0gLk4oKSRjb21tdW5pdHlbdG9dKSkgKwogIGdlb21fbm9kZV9wb2ludChhZXMoc2l6ZSA9IGNlbnRyYWxpdHlfZGVncmVlKCksIAogICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSBjb21tdW5pdHkpLAogICAgICAgICAgICAgICAgICBzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgICB0aGVtZV9ncmFwaCgpICsKICBjb29yZF9maXhlZCgpCmBgYAoKIyMjIEhpdmUgcGxvdHMKCiogQSBoaXZlIHBsb3QsIHdoaWxlIHN0aWxsIHRlY2huaWNhbGx5IGEgbm9kZS1lZGdlIGRpYWdyYW0sIGlzIGEgYml0IGRpZmZlcmVudCBmcm9tIHRoZSByZXN0IGFzIGl0IHVzZXMgaW5mb3JtYXRpb24gcGVydGFpbmluZyB0byB0aGUgbm9kZXMsIHJhdGhlciB0aGFuIHRoZSBjb25uZWN0aW9uIGluZm9ybWF0aW9uIGluIHRoZSBncmFwaC4gCiogVGhpcyBtZWFucyB0aGF0IGhpdmUgcGxvdHMsIHRvIGEgY2VydGFpbiBleHRlbnQgYXJlIG1vcmUgaW50ZXJwcmV0YWJsZSBhcyB3ZWxsIGFzIGxlc3MgdnVsbmVyYWJsZSB0byBzbWFsbCBjaGFuZ2VzIGluIHRoZSBncmFwaCBzdHJ1Y3R1cmUuIAoqIFRoZXkgYXJlIGxlc3MgY29tbW9uIHRob3VnaCwgc28gdXNlIHdpbGwgb2Z0ZW4gcmVxdWlyZSBzb21lIGFkZGl0aW9uYWwgZXhwbGFuYXRpb24uCgpgYGB7cn0KZyAlPiUKICBnZ3JhcGgobGF5b3V0ID0gJ2hpdmUnLCBheGlzID0gcG9wdWxhciwgc29ydC5ieSA9IGNlbnRyYWxpdHlfZGVncmVlKG1vZGUgPSAnaW4nKSkgKyAKICAgIGdlb21fZWRnZV9oaXZlKGFlcyhjb2xvdXIgPSB5ZWFyLCBhbHBoYSA9IC4uaW5kZXguLiksIHNob3cubGVnZW5kID0gRkFMU0UpICsgCiAgICBnZW9tX2F4aXNfaGl2ZShhZXMoY29sb3VyID0gcG9wdWxhciksIHNpemUgPSAzLCBsYWJlbCA9IEZBTFNFKSArIAogICAgY29vcmRfZml4ZWQoKSArIAogIHRoZW1lX2dyYXBoKCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICdib3R0b20nKQpgYGAKCiMjIyBTb2NpYWwgRmFicmljCgpgYGB7cn0KZyAlPiUgZ2dyYXBoKGxheW91dCA9ICdmYWJyaWMnLCBzb3J0LmJ5ID0gY29tbXVuaXR5KSArIAogIGdlb21fbm9kZV9yYW5nZShhZXMoY29sb3VyID0gY29tbXVuaXR5KSwgYWxwaGEgPSAwLjMpICsgCiAgZ2VvbV9lZGdlX3NwYW4oYWVzKGNvbCA9IC5OKCkkY29tbXVuaXR5W3RvXSksIGVuZF9zaGFwZSA9ICdjaXJjbGUnLCBhbHBoYSA9IDAuNSkgKyAKICBjb29yZF9maXhlZCgpICsgCiAgdGhlbWVfZ3JhcGgoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gJ25vbmUnKQpgYGAKCgojIyBWaXN1YWxpemluZyBIaXJhcmNoaWNhbCBuZXR3b3JrcwoKKiBJZiB0aGUgbmV0d29yayBpcyBieSBkZWZpbml0aW9uIGhpZXJhcmNoaWNhbCwgZWRnZXMgY2FuIG9ubHkgZXhpc3QgYmV0d2VlbiBub2RlcyBvZiBoaWdoZXIgdG8gbG93ZXIgZGVwdCAoZWcuIHRyZWUgc3RydWN0dXJlcykuCiogVGhpcyBvZmZlcnMgdXMgcG9zc2liaWxpdHkgZm9yIHF1aXRlIHNvbWUgYWRpdHRpb25hbCB3YXlzIG9mIHJlcHJlc2VudGluZyBpdCB3aGljaCBhcmUgZ2VhcmVkIHRvd2FyZHMgaGlyYXJjaGljYWwgKD1uZXN0ZWQpIHN0cnVjdHVyZXMKCiogSGVyZSBhbiBleGFtcGxlIG9mIHRoZSBkZXBlbmRlbmN5IHN0cnVjdHVyZXMgb2YgdGhlIGBmbGFyZWAgcGFja2FnZQoKYGBge3J9CmVkZ2VzIDwtIGZsYXJlJGVkZ2VzCnZlcnRpY2VzIDwtIGZsYXJlJHZlcnRpY2VzICU+JSBhcnJhbmdlKG5hbWUpICU+JSBtdXRhdGUobmFtZT1mYWN0b3IobmFtZSwgbmFtZSkpCmNvbm5lY3Rpb25zIDwtIGZsYXJlJGltcG9ydHMKYGBgCgpgYGB7cn0KdmVydGljZXMgJT4lIGhlYWQoKQpgYGAKCmBgYHtyfQplZGdlcyAlPiUgaGVhZCgpCmBgYAoKYGBge3J9CmNvbm5lY3Rpb25zICU+JSBoZWFkKCkKYGBgCgpgYGB7cn0KZ19oaXIgPC0gdGJsX2dyYXBoKHZlcnRpY2VzLCBlZGdlcykKYGBgCgpgYGB7cn0KZ19oaXIKYGBgCgojIyMgVHJlZSBzdHJ1Y3R1cmVzCgpgYGB7cn0KZ19oaXIgJT4lIGdncmFwaCgndHJlZScpICsgCiAgZ2VvbV9lZGdlX2RpYWdvbmFsKCkgKwogIHRoZW1lX2dyYXBoKCkKYGBgCgoKYGBge3J9CmdfaGlyICU+JSBnZ3JhcGgoICdkZW5kcm9ncmFtJykgKyAKICAgIGdlb21fZWRnZV9lbGJvdygpICsKICB0aGVtZV9ncmFwaCgpCmBgYAoKYGBge3J9CmdfaGlyICU+JSBnZ3JhcGgoJ2RlbmRyb2dyYW0nLCBjaXJjdWxhciA9IFRSVUUpICsgCiAgICBnZW9tX2VkZ2VfZWxib3coKSArIAogICAgY29vcmRfZml4ZWQoKSArCiAgdGhlbWVfZ3JhcGgoKQpgYGAKCmBgYHtyfQojIFRoZSBjb25uZWN0aW9uIG9iamVjdCBtdXN0IHJlZmVyIHRvIHRoZSBpZHMgb2YgdGhlIGxlYXZlczoKZnJvbSA9IG1hdGNoKGNvbm5lY3Rpb25zJGZyb20sIHZlcnRpY2VzJG5hbWUpCnRvID0gbWF0Y2goY29ubmVjdGlvbnMkdG8sIHZlcnRpY2VzJG5hbWUpCmBgYAoKCmBgYHtyfQpnX2hpciAlPiUgZ2dyYXBoKGxheW91dCA9ICdkZW5kcm9ncmFtJywgY2lyY3VsYXIgPSBUUlVFKSArIAogIGdlb21fY29ubl9idW5kbGUoZGF0YSA9IGdldF9jb24oZnJvbSA9IGZyb20sIHRvID0gdG8pLCBhbHBoYSA9IDAuMSkgKyAKICBnZW9tX2VkZ2VfZGlhZ29uYWwwKCkgKwogICNnZW9tX25vZGVfdGV4dChhZXMoZmlsdGVyID0gbGVhZiwgYW5nbGUgPSBub2RlX2FuZ2xlKHgsIHkpLCBsYWJlbCA9IHNob3J0TmFtZSksCiAgIyBoanVzdCA9ICdvdXR3YXJkJywgc2l6ZSA9IDIpICsKICBjb29yZF9maXhlZCgpICsKICB0aGVtZV9ncmFwaCgpCmBgYAoKIyMjIE5vbi1lZGdlLWJhc2VkIAoKYGBge3J9CiMgQW4gaWNpY2xlIHBsb3QKZ19oaXIgJT4lIGdncmFwaCgncGFydGl0aW9uJykgKyAKICBnZW9tX25vZGVfdGlsZShhZXMoZmlsbCA9IGRlcHRoKSwgc2l6ZSA9IDAuMjUpICsKICB0aGVtZV9ncmFwaCgpCmBgYAoKYGBge3J9CiMgQSBzdW5idXJzdCBwbG90CmdfaGlyICU+JSBnZ3JhcGgoJ3BhcnRpdGlvbicsIGNpcmN1bGFyID0gVFJVRSkgKyAKICBnZW9tX25vZGVfYXJjX2JhcihhZXMoZmlsbCA9IGRlcHRoKSwgc2l6ZSA9IDAuMjUpICsgCiAgY29vcmRfZml4ZWQoKSArCiAgdGhlbWVfZ3JhcGgoKQpgYGAKCmBgYHtyfQpnX2hpciAlPiUgZ2dyYXBoKCdjaXJjbGVwYWNrJykgKyAjICwgd2VpZ2h0ID0gc2l6ZQogIGdlb21fbm9kZV9jaXJjbGUoYWVzKGZpbGwgPSBkZXB0aCksIHNpemUgPSAwLjI1LCBuID0gNTApICsgCiAgY29vcmRfZml4ZWQoKSArCiAgdGhlbWVfZ3JhcGgoKQpgYGAKCgoKCmBgYHtyfQpnX2hpciAlPiUgZ2dyYXBoKCd0cmVlbWFwJykgKyAKICBnZW9tX25vZGVfdGlsZShhZXMoZmlsbCA9IGRlcHRoKSwgc2l6ZSA9IDAuMjUpICsKICB0aGVtZV9ncmFwaCgpCmBgYAoKIyMgR2Vvc3BhdGlhbCBuZXR3b3JrcwoKIyMjIERlZmluaW5nIGEgbWFwCgpgYGB7cn0KbGlicmFyeShtYXBzKQpgYGAKCmBgYHtyfQptYXBfdXMgPC0gbWFwX2RhdGEoInVzYSIpCmBgYAoKYGBge3J9Cm1hcF91cyAlPiUKICBoZWFkKCkKYGBgCgojIyMgR2V0dGluZyBzb21lIG5ldHdvcmsgZGF0YQoKYGBge3J9CmxpYnJhcnkoYW55ZmxpZ2h0cykKYGBgCgpgYGB7cn0KdXNfYWlycG9ydHMgPC0gZ2V0X2FpcnBvcnRzKCkgJT4lCiAgZmlsdGVyKGxhdCA+PSAyNCAmIGxhdCA8PSA0OSAmIGxvbiA+PSAtMTI0ICYgbG9uIDw9IC02NikgICU+JQogIHJlbmFtZShuYW1lX2Z1bGwgPSBuYW1lLAogICAgICAgICBuYW1lID0gZmFhKQpgYGAKCmBgYHtyfQp1c19haXJwb3J0cyAlPiUgaGVhZCgpCmBgYAoKYGBge3J9CmZsaWdodHMgPC0gZ2V0X2ZsaWdodHMoc3RhdGlvbiA9IHVzX2FpcnBvcnRzICU+JSBwdWxsKG5hbWUpLCB5ZWFyID0gMjAxNSwgbW9udGggPSA1KQpgYGAKCmBgYHtyfQpmbGlnaHRzICU+JSBoZWFkKCkKYGBgCgoKYGBge3J9CmVkZ2VzIDwtIGZsaWdodHMgJT4lIGNvdW50KG9yaWdpbiwgZGVzdCwgc29ydCA9IFRSVUUpICU+JQogIHJlbmFtZShmcm9tID0gb3JpZ2luLCB0byA9IGRlc3QsIHdlaWdodCA9IG4pICU+JQogIHNlbWlfam9pbih1c19haXJwb3J0cywgYnkgPSBjKCdmcm9tJyA9ICduYW1lJykpICU+JQogIHNlbWlfam9pbih1c19haXJwb3J0cywgYnkgPSBjKCd0bycgPSAnbmFtZScpKSAlPiUgCiAgZmlsdGVyKHBlcmNlbnRfcmFuayh3ZWlnaHQpID49IDAuMjUpCmBgYAoKYGBge3J9CmdfZ2VvIDwtIHRibF9ncmFwaChub2RlcyA9IHVzX2FpcnBvcnRzLCBlZGdlcyA9IGVkZ2VzLCBkaXJlY3RlZCA9IFRSVUUpICVOPiUKICBmaWx0ZXIoIW5vZGVfaXNfaXNvbGF0ZWQoKSkKYGBgCgoKIyMjIENvbnN0cnVjdGluZyBhIGdyYXBoCgpgYGB7cn0KY29vcmRzIDwtIGdfZ2VvICVOPiUgCiAgYXNfdGliYmxlKCkgJT4lIAogIHNlbGVjdChsYXQsIGxvbikgJT4lCiAgcmVuYW1lKHggPSBsb24sIHkgPSBsYXQpCmBgYAoKYGBge3J9CmdfZ2VvICU+JQogIGdncmFwaChsYXlvdXQgPSBjb29yZHMpICsKICBnZW9tX3BvbHlnb24oZGF0YSA9IG1hcF91cywgYWVzKHg9bG9uZywgeSA9IGxhdCwgZ3JvdXAgPSBncm91cCksIGZpbGwgPSAiI0NFQ0VDRSIsIGNvbG9yID0gIiM1MTUxNTEiKSArIAogIGdlb21fZWRnZV9hcmMoYWVzKHdpZHRoID0gd2VpZ2h0LCAgIAogICAgICAgICAgICAgICAgICAgIGFscGhhID0gd2VpZ2h0LAogICAgICAgICAgICAgICAgICAgIGZpbHRlciA9IHBlcmNlbnRfcmFuayh3ZWlnaHQpID49IDAuNzUsCiAgICAgICAgICAgICAgICAgICAgY2lyY3VsYXIgPSBGQUxTRSksCiAgICAgICAgICAgICAgICBzdHJlbmd0aCA9IDAuMzMsCiAgICAgICAgICAgICAgICBjb2xvciA9ICdjaG9jb2xhdGUyJykgKwogIGdlb21fbm9kZV9wb2ludChhZXMoc2l6ZSA9IGNlbnRyYWxpdHlfZGVncmVlKCksCiAgICAgICAgICAgICAgICAgICAgICBjb2wgPSBjZW50cmFsaXR5X2RlZ3JlZSgpKSkgKyAKICBzY2FsZV9lZGdlX3dpZHRoX2NvbnRpbnVvdXMocmFuZ2UgPSBjKDAuMSwgMSkpICsgCiAgY29vcmRfZml4ZWQoMS4zKSArIAogIHRoZW1lX2dyYXBoKCkgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAnbm9uZScpCmBgYAoKIyMgSW50ZXJhY3RpdmUgbmV0d29ya3MKCiogVGhlcmUgYXJlIG51bWVyb3VzIHdheXMgdG8gdG8gaW50ZXJhY3RpdmUgbmV0d29yayB2aXN1YWxpemF0aW9ucyBpbiBSCiogRm9yIHRoZSBzYWtlIG9mIHRpbWUsIEkganVzdCBzaG93IHlvdSB3aGF0IEkgZmluZCB0aGUgZWFzaWVzdCBhbmQgbW9zdCBjb25zaXN0ZW50IGltcGxlbWVudGF0aW9uIChwbG90bHkgdW5mb3J0dW5hdGVseSBkb2VzIGJ5IG5vdyBub3Qgc3VwcG9ydCBnZ3JhcGgpCgpgYGB7cn0KbGlicmFyeShnZ2lyYXBoKQpgYGAKCgpgYGB7cn0KZ19wbG90X2ludCA8LSBnICU+JSAKICBnZ3JhcGgobGF5b3V0ID0gbGF5b3V0X2xpc3RbaV0pICsgCiAgZ2VvbV9lZGdlX2ZhbihhZXMoY29sb3IgPSB5ZWFyLAogICAgICAgICAgICAgICAgICAgIHdpZHRoID0gd2VpZ2h0LAogICAgICAgICAgICAgICAgICAgIGFscGhhID0gd2VpZ2h0KSwgCiAgICAgICAgICAgICAgICBhcnJvdyA9IGFycm93KHR5cGUgPSAiY2xvc2VkIiwgbGVuZ3RoID0gdW5pdCgyLCAibW0iKSksCiAgICAgICAgICAgICAgICBzdGFydF9jYXAgPSBjaXJjbGUoMSwgIm1tIiksCiAgICAgICAgICAgICAgICBlbmRfY2FwID0gY2lyY2xlKDEsICJtbSIpLAogICAgICAgICAgICAgICAgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogICAgc2NhbGVfZWRnZV93aWR0aChyYW5nZSA9IGMoMC4xLCAwLjUpKSArIAogICAgZ2VvbV9ub2RlX3BvaW50KGFlcyhzaXplID0gY2VudHJhbGl0eV9kZWdyZWUobW9kZSA9ICdpbicpLCAKICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSBjb21tdW5pdHksCiAgICAgICAgICAgICAgICAgICAgICAgIHNoYXBlID0gZ2VuZGVyKSwKICAgICAgICAgICAgICAgICAgICBzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgZ2VvbV9wb2ludF9pbnRlcmFjdGl2ZShhZXMoeCwgeSwgIyBOb3RpY2UgdGhpcyBleHRyYSBsYXllcgogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRvb2x0aXAgPSBsYWJlbCwgZGF0YV9pZCA9IG5hbWUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSBjZW50cmFsaXR5X2RlZ3JlZShtb2RlID0gJ2luJykpLCBhbHBoYSA9IDAuMDEpICsgIAogICAgdGhlbWVfZ3JhcGgoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gJ25vbmUnKQpgYGAKCmBgYHtyLCBmaWcuaGVpZ2h0PTEwLCBmaWcud2lkdGg9MTB9CmdpcmFmZShnZ29iaiA9IGdfcGxvdF9pbnQsIHdpZHRoX3N2ZyA9IDEwLCBoZWlnaHRfc3ZnID0gMTApICU+JSAKICAgIGdpcmFmZV9vcHRpb25zKG9wdHNfem9vbShtYXggPSAxMCksIG9wdHNfdG9vbHRpcChvcGFjaXR5ID0gMC43KSApCmBgYAoKCiMgWW91ciB0dXJuClBsZWFzZSBkbyAqKkV4ZXJjaXNlIDEqKiBpbiB0aGUgY29ycmVzcG9uZGluZyBzZWN0aW9uIG9uIGBHaXRodWJgLiBUaGlzIHRpbWUgeW91IGFyZSBhYm91dCB0byBkbyB5b3VyIG93biBiaWJsaW9ncmFwaGljIGFuYWx5c2lzIQoKIyBFbmRub3RlcwoKIyMjIE1vcmUgaW5mbwoKIyMjIyBQYWNrYWdlcyAmIEVjb3N5c3RlbQoKKiBgdGlkeWdyYXBoYCBbaGVyZV0oaHR0cHM6Ly90aWR5Z3JhcGguZGF0YS1pbWFnaW5pc3QuY29tLykKKiBgZ2dyYXBoYCBbaGVyZV0oaHR0cHM6Ly9nZ3JhcGguZGF0YS1pbWFnaW5pc3QuY29tLykKKiBgZ2dpcmFwaGAgW2hlcmVdKGh0dHBzOi8vZGF2aWRnb2hlbC5naXRodWIuaW8vZ2dpcmFwaC8pCgojIyMjIE90aGVyIHNvdWNlcwoKKiBbSW50cm86IE5ldHdvcmsgVmlzdWFsaXphdGlvbnMgaW4gUiB1c2luZyBnZ3JhcGggYW5kIGdyYXBobGF5b3V0c10oaHR0cDovL21yLnNjaG9jaGFzdGljcy5uZXQvbmV0Vml6Ui5odG1sKTogR29vZCBpbnRyb3MgdG8gZ2dyYXBoIGZ1bmN0aW9uYWxpdHkgJiBmaW5ldHVuaW5nLgoqIFtLYXRoZXJpbmUgT2dueWFub3ZhJ3MgQmxvZyldKGh0dHBzOi8va2F0ZXRvLm5ldC8pOiBIZXIgYmxvZyBpcyBmdWxsIG9mIHNvbWUgb2YgdGhlIG1vc3QgY29tcGxldGUgaW50cm9kdWN0aW9ucyB0byBuZXR3b3JrIHZpc3VhbGl6YXRpb24uIERvZXMgdXNlIGlncmFwaCwgYnV0IHRoZXJlIGFyZSBmb3Igc3VyZSAxIG9yIHRyaWNrcyB5b3UgY2FuIGxlYXJuIGZyb20gaGVyLCBwYXJ0aWN1bGFybHkgd2hlbiBpdCdzIGFib3V0IGludGVyYWN0aXZlIG5ldHdvcmsgdmlvc3VhbGl6YXRpb24uCiogW0dvb2Qgc2xpZGVkZWNrIG9uIHN0YXRpYyAvIGludGVyYWN0aXZlIG5ldHdvcmsgdml6IGluIFJdKGh0dHA6Ly9jdXJsZXlsYWIucHN5Y2guY29sdW1iaWEuZWR1L25ldHZpei9uZXR2aXoxLmh0bWwjLyk6IFdvcnRoIHZpc2l0aW5nCgoKIyMjIFNlc3Npb24gaW5mbwpgYGB7cn0Kc2Vzc2lvbkluZm8oKQpgYGAKCg==