library(tidyverse)
library(datapasta)
library(igraph)
library(ggplot2)
library(ggraph)
library(cowplot)
library(graphlayouts)
library(visNetwork)
library(gganimate)
Data
In this tutorial, we will use anonymized results from the student survey, allowing us to analyse the friendship network within the course. To track the dynamics, the survey was conducted in three waves during the year. In one part of the survey, we asked students to identify their colleagues they consider their friends. We will pay special attention to the answers to this question. We will use students GPA as metadata.
source("~/noobsQA/lasi/datapasta_lasi_1.r") #datapasta directory
gpa <- gpa %>% filter(GPA1kurs <= 11) # because some NAs were set as 99
To begin with, let’s build a network based on the first wave of the survey To do this, we convert the existing data frame into a graph using the graph_from_data_frame function from the igraph package.
graph_c1w1 <- graph_from_data_frame(c1w1g[,c(3:4)])
w1 <- c1w1g %>% group_by(source_st) %>% select(source_st, Source_Program) %>% unique() %>% summarise(program = Source_Program) %>% mutate(num = as.numeric(str_extract(source_st, "(\\d)+"))) %>% arrange(num)
w1r <- c1w1g %>% group_by(target_st) %>% select(target_st, Target_Program) %>% unique() %>% summarise(program = Target_Program) %>% mutate(num = as.numeric(str_extract(target_st, "(\\d)+"))) %>% arrange(num)
w <- full_join(w1, w1r)
w <- left_join(w, gpa, by = c("source_st" = "un_id"))
V(graph_c1w1)$program = w$program
V(graph_c1w1)$gpa = w$GPA1kurs
ggraph(graph_c1w1) +
geom_edge_link0(alpha = 0.3, show.legend = FALSE)+
geom_node_point(aes(color = program ), shape = "circle", show.legend = TRUE)+
theme_graph()

Good! However, we can make our network more informative by adding such elements as the size of nodes. For example, let’s try to add one of the centrality metrics to the plot – degree.
nodesize <- igraph::degree(graph_c1w1, mode= "in")
ggraph(graph_c1w1, layout = "fr") +
geom_edge_link0(alpha = 0.3, show.legend = FALSE)+
geom_node_point(aes(color = program, size = nodesize), shape = "circle", stroke = 1, show.legend = TRUE)+
theme_graph()

At this point, we can see that some nodes are significantly bigger than others. Obiously we want to know more information about the, so, we add labels to our nodes.
ggraph(graph_c1w1, layout = "fr") +
geom_edge_link0(alpha = 0.3, show.legend = FALSE)+
geom_node_point(aes(color = program, size = nodesize), shape = "circle", stroke = 1, show.legend = TRUE)+
geom_node_text(aes(label= name), check_overlap = T, repel = F, size = 5)+
theme_graph()

Looks really messy, doesnt’t it? Luckily, it is easy to fix. We can labels to only those nodes who have the heighest degree. This allows us to identify the most important people within network.
ggraph(graph_c1w1, layout = "fr") +
geom_edge_link0(alpha = 0.3, show.legend = FALSE)+
geom_node_point(aes(color = program, size = nodesize), shape = "circle", stroke = 1, show.legend = TRUE)+
geom_node_text(aes(label= name, filter = ( nodesize >= 10) ), repel = T, size = 5)+
theme_graph()

As a next stage, we create a fixed layout which will help us to have a more stable representation of a network.
layout <- create_layout(graph = graph_c1w1, layout = 'fr')
ggraph(layout) +
geom_edge_link0(alpha = 0.3, show.legend = FALSE)+
geom_node_point(aes(color = program, size = nodesize), shape = "circle", stroke = 1, show.legend = TRUE)+
geom_node_text(aes(label= name, filter = ( nodesize >= 10) ), repel = T, size = 5)+
theme_graph()

Now, let’s look at different centrality metrics. There are three basic ones: betweenness, degree, and Kleinberg’s authority centrality scores.
ar = arrow(angle = 30, length = unit(2, "mm"), ends = "last", type = "open")
nodesize_btw <- betweenness(graph_c1w1)
btw <- ggraph(layout) +
geom_edge_link0(alpha = 0.5, arrow = ar, show.legend = FALSE, color = "gray66")+
geom_node_point(aes(color = nodesize_btw ), shape = "circle", stroke = 1, show.legend = TRUE)+
scale_colour_gradient(low="blue", high="red") +
labs(title = "betwennes")+
theme_graph()
nodesize_dgr <- degree(graph_c1w1, mode = "in")
dgr <- ggraph(layout) +
geom_edge_link0(alpha = 0.5, arrow = ar, show.legend = FALSE, color = "gray66")+
geom_node_point(aes(color = nodesize_dgr ), shape = "circle", stroke = 1, show.legend = TRUE)+
scale_colour_gradient(low="blue", high="red") +
labs(title = "degree")+
theme_graph()
nodesize_cnt <- centr_degree(graph_c1w1)
cnt <- ggraph(layout) +
geom_edge_link0(alpha = 0.5, arrow = ar, show.legend = FALSE, color = "gray66")+
geom_node_point(aes(color = nodesize_cnt$res ), shape = "circle", stroke = 1, show.legend = TRUE)+
scale_colour_gradient(low="blue", high="red") +
labs(title = "centrality")+
theme_graph()
nodesize_aut <- authority_score(graph_c1w1)
nodesize_aut <- as.numeric(nodesize_aut$vector)
egn <- ggraph(layout) +
geom_edge_link0(alpha = 0.5, arrow = ar, show.legend = FALSE, color = "gray66")+
geom_node_point(aes(color = nodesize_aut ), shape = "circle", stroke = 1, show.legend = TRUE)+
scale_colour_gradient(low="blue", high="red") +
labs(title = "authority_score")+
theme_graph()
plot_grid(btw, dgr, cnt, egn)

Your online position affects academic success, let’s look at the metric authority_score
?authority_score
If very simple, then this is a metric of access to nodes with high centrality
gpa_g <- ggraph(layout) +
geom_edge_link0(alpha = 0.5, arrow = ar, show.legend = FALSE, color = "gray66")+
geom_node_point(aes(color = V(graph_c1w1)$gpa ), shape = "circle", stroke = 1, show.legend = TRUE)+
scale_colour_gradient(low="green", high="red") +
labs(title = "GPA")+
theme_graph()
authority_score <- authority_score(graph_c1w1)
authority_score <- as.numeric(authority_score$vector)
egn <- ggraph(layout) +
geom_edge_link0(alpha = 0.5, arrow = ar, show.legend = FALSE, color = "gray66")+
geom_node_point(aes(color = authority_score ), shape = "circle", stroke = 1, show.legend = TRUE)+
scale_colour_gradient(low="green", high="red") +
labs(title = "authority_score")+
theme_graph()
plot_grid(gpa_g, egn)

Sometimes you want to apply custom colors to display a change in the attribute value. some examples colors and palets: + https://www.datanovia.com/en/blog/top-r-color-palettes-to-know-for-great-data-visualization/ + https://www.nceas.ucsb.edu/~frazier/RSpatialGuides/colorPaletteCheatsheet.pdf
gpa_g <- ggraph(layout) +
geom_edge_link0(alpha = 0.5, arrow = ar, show.legend = FALSE, color = "gray66")+
geom_node_point(aes(color = V(graph_c1w1)$gpa ), shape = "circle", stroke = 1, show.legend = TRUE)+
scale_colour_gradient(low="#3A1C71", high="#FFAF7B") +
labs(title = "GPA")+
theme_graph()
nodesize_aut <- authority_score(graph_c1w1)
nodesize_aut <- as.numeric(nodesize_aut$vector)
egn <- ggraph(layout) +
geom_edge_link0(alpha = 0.5, arrow = ar, show.legend = FALSE, color = "gray66")+
geom_node_point(aes(color = nodesize_aut ), shape = "circle", stroke = 1, show.legend = TRUE)+
scale_colour_gradient(low="#3A1C71", high="#FFAF7B") +
labs(title = "authority_score")+
theme_graph()
plot_grid(gpa_g, egn)
It is not always possible to show two metrics on the same chart. Since a person, in principle, does not perceive much information, and unfortunately there are not enough shapes.
ggraph(layout) +
geom_edge_link0(alpha = 0.5, arrow = ar, show.legend = FALSE, color = "gray66")+
geom_node_point(aes(color = V(graph_c1w1)$gpa, shape = program),stroke = 1, show.legend = TRUE)+
scale_colour_gradient2(low="#009fff", mid = "#d3cce3", high="#ec2f4b", midpoint = 7) +
labs(title = "GPA")+
theme_graph()

ggraph(layout) +
geom_edge_link0(alpha = 0.5, arrow = ar, show.legend = FALSE, color = "gray66")+
geom_node_point(aes(size = (gpa-5)*2, color = program), stroke = 1, show.legend = TRUE)+
labs(title = "GPA")+
theme_graph()

ggraph(layout) +
geom_edge_link0(alpha = 0.5, arrow = ar, show.legend = FALSE, color = "gray66")+
geom_node_point(aes(size = (gpa-5)*2, shape = as.factor(gpa), color = program), stroke = 1, show.legend = TRUE)+
labs(title = "GPA")+
theme_graph()

In most cases, two pictures of the network look more informative. The main thing: do not forget to fix the layout.
g_gpa <- ggraph(layout) +
geom_edge_link0(alpha = 0.5, arrow = ar, show.legend = FALSE, color = "gray66")+
geom_node_point(aes(color = V(graph_c1w1)$gpa),stroke = 1, show.legend = TRUE)+
scale_colour_gradient2(low="#009fff", mid = "#d3cce3", high="#ec2f4b", midpoint = 7) +
labs(title = "GPA")+
theme_graph()
g_prog <- ggraph(layout) +
geom_edge_link0(alpha = 0.5, arrow = ar, show.legend = FALSE, color = "gray66")+
geom_node_point(aes(color = program ), shape = "circle", stroke = 1, show.legend = TRUE)+
scale_fill_brewer(palette = "Set3")+
labs(title = "program")+
theme_graph()
plot_grid(g_gpa, g_prog)

It is completely useless to try to display all the names, but sometimes it is important to highlight some nodes on the network. So using the filter, we will leave only students with low grades and high grades in the adjacent charts.
V(graph_c1w1)$gpa_low <- ifelse(V(graph_c1w1)$gpa == 6, 1,0.3)
V(graph_c1w1)$gpa_high <- ifelse(V(graph_c1w1)$gpa >= 9, 1, 0.3)
layout <- create_layout(graph = graph_c1w1, layout = 'fr')
ggraph(layout) +
geom_edge_link0(alpha = 0.5, arrow = ar, show.legend = FALSE, color = "gray66")+
geom_node_point(aes(color = program, alpha = gpa_low), shape = "circle", stroke = 1, show.legend = TRUE)+
scale_fill_brewer(palette = "Set3")+
labs(title = "Low gpa students")+
theme_graph()

ggraph(layout) +
geom_edge_link0(alpha = 0.5, arrow = ar, show.legend = FALSE, color = "gray66")+
geom_node_point(aes(color = program, alpha = gpa_high), shape = "circle", stroke = 1, show.legend = TRUE)+
scale_fill_brewer(palette = "Set3")+
labs(title = "high gpa students")+
theme_graph()

interactive creation of graph
There is a package to configure network visualization through a more familiar graphical interface. Install the snahelper package, load it, and then highlight any igraph object (for example graph_c1w1) with the mouse, click on the Addins tab (top panel of rstudio), and select SNAhelper from the list.
# library(snahelper)
#
# SNAhelperGadget(graph_c1w1)
centrality, (ego network?)
Usually, most layouts show the entire structure of the network, or try to highlight the community. One example of such a mapping.
ggraph(graph_c1w1, layout = "stress")+
geom_edge_link0(edge_colour = "grey66", arrow = ar, alpha = 0.5)+
geom_node_point(aes(color = program, size = nodesize),shape=19)+
geom_node_text(aes(filter = nodesize >= 10, label = name), family="serif", size = 5)+
scale_edge_width(range = c(0.2,3))+
scale_fill_brewer(palette = "Set3")+
scale_size(range = c(1,6))+
theme_graph()+
theme(legend.position = "none")

This layout helps to focus on a specific node, look at its immediate surroundings.
w1_connected <- graph_c1w1 %>% delete.vertices(c("student 15", "student 100", "student 93", "student 143")) #connected graph is a graph without isolated nodes
nodesize_c <- degree(w1_connected, mode = "in")
ggraph(w1_connected, layout = "focus", v = 120)+ #v id of a node, not always same as a name
geom_edge_link0(edge_colour = "grey66")+
geom_node_point(aes(fill = program, size=nodesize_c), shape = 21)+
scale_edge_width_continuous(range = c(0.2,1.2))+
scale_size_continuous(range = c(1,5))+
geom_node_text(aes(filter = (nodesize_c >= 10),size = nodesize_c+2, label = name), size = 5,
family = "serif")+
scale_fill_brewer(palette = "Set3")+
coord_fixed()+
theme_graph()+
theme(legend.position = "none")

Add circles to better distinguish the order of the neighborhood.
ggraph(w1_connected, layout = "focus", v = 142)+
geom_edge_link0(edge_colour = "grey66")+
draw_circle(col = "#00BFFF", use = "focus", max.circle = 3)+
geom_node_point(aes(fill = program, size=gpa), shape = 21)+
scale_edge_width_continuous(range = c(0.2,1.2))+
scale_size_continuous(range = c(1,5))+
geom_node_text(aes(filter = (nodesize_c >= 1),size = nodesize_c+2, label = name), size = 5,
family = "serif", check_overlap = T)+
scale_fill_brewer(palette = "Set3")+
coord_fixed()+
theme_graph()+
theme(legend.position = "bottom")

So a student with a low gpa is on the edge of the network, and his neighbors do not have a big degree.
ggraph(w1_connected, layout = "focus", v = 102)+
geom_edge_link0(edge_colour = "grey66")+
draw_circle(col = "#00BFFF", use = "focus", max.circle = 3)+
geom_node_point(aes(fill = program, size=gpa), shape = 21)+
scale_edge_width_continuous(range = c(0.2,1.2))+
scale_size_continuous(range = c(1,5))+
geom_node_text(aes(filter = (nodesize_c >= 1),size = nodesize_c+2, label = name), size = 5,
family = "serif", check_overlap = T)+
scale_fill_brewer(palette = "Set3")+
coord_fixed()+
theme_graph()+
theme(legend.position = "bottom")

In turn, a student with a large GPA is more included in the network, most of his friends are from the same educational program, with the same level of GPA.
dynamic
Now just run the following chunks. the data for the second and third waves of the same survey are not very well prepared there.
w2 <- graph_from_data_frame(c1w2g[,c(3:4)])
w2 = simplify(w2, remove.multiple = F, remove.loops = T)
w3 <- graph_from_data_frame(c1w3g[,c(3:4)])
w3 = simplify(w3, remove.multiple = F, remove.loops = T)
w1s <- degree(graph_c1w1)
w1s <- data.frame(keyName=names(w1s), row.names=NULL)
w3s <- degree(w3)
w3s <- data.frame(keyName=names(w3s), row.names=NULL)
w2s <- degree(w2)
w2s <- data.frame(keyName=names(w2s), row.names=NULL)
w2s <- left_join(w2s, w, by = c("keyName"="source_st"))
V(w2)$program <- w2s$program
V(w2)$gpa <- w2s$GPA1kurs
w3s <- left_join(w3s, w, by = c("keyName"="source_st"))
V(w3)$program <- w3s$program
V(w3)$gpa <- w3s$GPA1kurs
One of the solutions to the problem of isolated nodes without information in some waves is to remove them from all waves. However, here we simply add these nodes to the graphs.
comparison1 <- setdiff(names(V(graph_c1w1)), names(V(w2)) )
comparison2 <- setdiff(names(V(w2)), names(V(w3)) )
comparison3 <- setdiff(names(V(w3)), names(V(graph_c1w1)) )
comparison <- c(comparison1,comparison2,comparison3)
comparison <- comparison %>% unique() %>% na.omit()
comparison <- data.frame(source_st = comparison[-22])
comparison <- left_join(comparison, w, by = c("source_st") )
comparison = comparison[,c(-3,-4)]
w1n <- data.frame(source_st = names(V(graph_c1w1)) , present = T)
w2n <- data.frame(source_st = names(V(w2)) , present = T)
w3n <- data.frame(source_st = names(V(w3)) , present = T)
right_join(w1n, comparison, by = "source_st")
w2n <- right_join(w2n, comparison, by = "source_st")
w3n <- right_join(w3n, comparison, by = "source_st")
w2n$present[is.na(w2n$present)] <- F
w3n$present[is.na(w3n$present)] <- F
w2n = w2n %>% filter(present == F) %>% select(-present)
w3n = w3n %>% filter(present == F )%>% select(-present)
w1_full <- graph_c1w1
w2_full <- w2 %>% add.vertices(nrow(w2n), name = w2n$source_st, program = w2n$program, gpa = w2n$GPA1kurs) %>% delete.vertices("NA")
w3_full <- w3 %>% add.vertices(nrow(w3n), name = w3n$source_st, program = w3n$program, gpa = w3n$GPA1kurs) %>% delete.vertices("NA")
write all three networks in one list
tnet <- list(w1_full, w2_full, w3_full)
xy <- layout_as_dynamic(tnet, alpha = 0.2)
remove the student’s words from the names of the nodes. it will be more beautiful and it takes up less space
V(w1_full)$name <- str_extract(V(w1_full)$name, "(\\d)+")
V(w2_full)$name <- str_extract(V(w2_full)$name, "(\\d)+")
V(w3_full)$name <- str_extract(V(w3_full)$name, "(\\d)+")
Due to the conflict of package versions, the function layout_as_dynamic is not recognized correctly. Therefore, we will substitute the coordinates in layout with a different algorithm in order to correctly display the positions on the charts.
l1 <- create_layout(tnet[[1]], layout = 'grid')
l1$x <- xy[[1]][,1]
l1$y <- xy[[1]][,2]
l2 <- create_layout(tnet[[2]], layout = 'grid')
l2$x <- xy[[2]][,1]
l2$y <- xy[[2]][,2]
l3 <- create_layout(tnet[[3]], layout = 'grid')
l3$x <- xy[[3]][,1]
l3$y <- xy[[3]][,2]
g1degree <- degree(tnet[[1]])
g2degree <- degree(tnet[[2]])
g3degree <- degree(tnet[[3]])
g1 <- ggraph(l1)+
geom_edge_link0(edge_width = 0.2,edge_colour = "grey25")+
geom_node_point(shape=21, aes(size = g1degree, fill = program))+
theme_graph()+
theme(legend.position = "none")+
geom_node_text(aes(label = name, filter = ( g1degree >= 10) ), check_overlap = T, repel = F, size = 5)+
labs(title = paste0("Wave ", 1))
g2 <- ggraph(l2)+
geom_edge_link0(edge_width = 0.2,edge_colour = "grey25")+
geom_node_point(shape=21,aes(size = g2degree, fill = program))+
theme_graph()+
theme(legend.position = "none")+
geom_node_text(aes(label= name, filter = ( g2degree >= 10) ), check_overlap = T, repel = F, size = 5)+
labs(title = paste0("Wave ", 2))
g3 <- ggraph(l3)+
geom_edge_link0(edge_width = 0.2,edge_colour = "grey25") +
geom_node_point(shape=21, aes(size = g3degree, fill = program))+
theme_graph()+
theme(legend.position = "none")+
geom_node_text(aes(label= name, filter = ( g3degree >= 10) ), check_overlap = T, repel = F, size = 5)+
labs(title = paste0("Wave ", 3))
plot_grid(g1, g2, g3, ncol = 3)

since the second wave practically does not reflect changes, it can be skipped
plot_grid(g1, g3, ncol = 2)

temporal
We write all three waves in one graph
edges.w1 <- as_tibble(as_edgelist(w1_full, names = TRUE))
edges.w2 <- as_tibble(as_edgelist(w2_full, names = TRUE))
edges.w3 <- as_tibble(as_edgelist(w3_full, names = TRUE))
edges.w1$wave = 1
edges.w2$wave = 2
edges.w3$wave = 3
edges.w123 <- rbind(edges.w1, edges.w2, edges.w3)
w123 <- graph_from_data_frame(edges.w123)
nodesize123 <- degree(w123)
w123s <- data.frame(keyName=as.numeric(names(nodesize123)))
w123s <- left_join(w123s, w, by = c("keyName"="num"))
V(w123)$program <- w123s$program
V(w123)$gpa <- w123s$GPA1kurs
V(w123)$gpa.omit <- w123s$GPA1kurs
V(w123)$gpa.omit[is.na(V(w123)$gpa.omit)] <- 5
Next, the layout will be calculated for the cumulative graph of all three waves, and the links of each wave will be displayed in their color.
nodesize123 <- degree(w123 ,mode ="in")
ggraph(w123, layout = "fr")+
geom_edge_link0(edge_width = 0.2, aes(color = as.factor(wave))) +
geom_node_point(aes(size = nodesize123, color = program), shape = "circle" ) +
theme_graph()+
theme(legend.position = "bottom")+
geom_node_text(aes(label= name, filter = nodesize123 >= 10 ), check_overlap = T, repel = F, size = 5)+
labs(title = paste0("Waves ", ""))

it is also possible to fix the nodes, and display the waves with a regular facet grid (facet_edges)
nodesize123 <- degree(w123 ,mode ="in")
V(w123)$nsize <- nodesize123
ggraph(w123, layout = "fr")+
geom_edge_link0(edge_width = 0.2) +
geom_node_point(shape=21, aes(fill = program, size = nsize, alpha = gpa))+
theme_graph()+
theme(legend.position = "bottom")+
geom_node_text(aes(label= name, filter = gpa.omit == 6), check_overlap = T, repel = F, size = 5)+
labs(title = paste0("Waves ", ""))+
facet_edges(~ wave)

# g.waves <- create_layout(graph = w123, layout = 'fr')
#
# # remove(gpa)
# # V(w123)$gpa
#
# firts <- ggraph(g.waves)+
# geom_edge_link0(edge_width = 0.2, aes( filter = wave == 1 )) +
# geom_node_point(shape= "circle", aes(size = nodesize123, color = program))+
# theme_graph()+
# theme(legend.position = "bottom")+
# geom_node_text(aes(label= name, alpha = gpa ), check_overlap = T, repel = F, size = 5)+
# labs(title = paste0("Wave ", 1))
#
# second <- ggraph(g.waves)+
# geom_edge_link0(edge_width = 0.2, aes( filter = wave == 2 )) +
# geom_node_point(shape= "circle", aes(size = nodesize123, color = program))+
# theme_graph()+
# theme(legend.position = "bottom")+
# geom_node_text(aes(label= name, alpha = gpa ), check_overlap = T, repel = F, size = 5)+
# labs(title = paste0("Wave ", 2))
#
# third <- ggraph(g.waves)+
# geom_edge_link0(edge_width = 0.2, aes( filter = wave == 3 )) +
# geom_node_point(shape= "circle", aes(size = nodesize123, color = program))+
# theme_graph()+
# theme(legend.position = "bottom")+
# geom_node_text(aes(label= name), check_overlap = T, repel = F, size = 5)+
# labs(title = paste0("Wave ", 3))
#
#
# plot_grid(firts, second, third, ncol = 3)
animation??
Sometimes if something moves, it’s better to show that something is moving.
Animation can hardly be very understandable, but it can be very spectacular.
library(gganimate)
library(gifski)
graph <- ggraph(w123, layout = "fr")+
geom_edge_link0(edge_width = 0.2, aes(color = as.factor(wave))) +
scale_fill_brewer(palette = "Set3")+
geom_node_point(shape=21, aes(size = nodesize123, fill = program))+
theme_graph()+
theme(legend.position = "bottom")+
geom_node_text(aes(label= name, filter = nodesize123 >= 10 ), check_overlap = T, repel = F, size = 5)+
labs(title = paste0("Waves ", ""))
p <- graph +
labs(subtitle = "Friendship in wave {trunc(frame_time)}") +
transition_time(wave) +
ease_aes("exponential-in-out")
# ?transition_time
# ?shadow_wake
#animate(p, fps = 25, duration = 8*3, width = 1000, height = 800)
Add the attenuation and extend the last frame a bit so that everything doesn’t break off very quickly.
p <- graph +
labs(subtitle = "Friendship in wave {trunc(frame_time)}") +
transition_time(wave) +
ease_aes("exponential-in-out")+
enter_fade() +
exit_fade()
animate(p,end_pause = 20)

Interactive plots
visNetwork is a library for R for interactive network visualization however, the syntax is not the easiest, and based on lists. HOWEVER, it could plot igraph objects directly
library(visNetwork)
V(w1_full)$size <- g1degree *2
library(RColorBrewer)
marker = list(color = brewer.pal(nlevels(as.factor(V(w1_full)$program)), "Set3"))
ep <- data.frame(program = V(w1_full)$program)
ep_c <- data.frame(program = unique(ep), color = marker$color)
ep <- left_join(ep, ep_c)
V(w1_full)$color <- as.character(ep$color)
# get data and plot :
# data <- toVisNetworkData(w1_full)
# visNetwork(nodes = data$nodes, edges = data$edges) %>%
# visNodes(size = V(w1_full)$nsize, color = V(w1_full)$program) %>%
# visOptions(highlightNearest = list(enabled = T, hover = T),
# nodesIdSelection = T)
# may take a lot of time, but will allow changing parameter values through package syntax
visIgraph(w1_full)%>%
visNodes() %>%
visOptions(highlightNearest = list(enabled = T, hover = T),
nodesIdSelection = T)
# faster, why?
LS0tCnRpdGxlOiAiTmV0d29ya3MgPC0tPiBFZHVjYXRpb24gPC0tPiBWaXp1YWxpemF0aW9uIgphdXRob3I6ICJWc2V2b2xvZCBTdXNjaGV2c2tpeSIKZGF0ZTogImByIFN5cy5EYXRlKClgIgpsaW5rY29sb3I6IHZpb2xldApvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgIHRoZW1lOiBjb3NtbwogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlCiAgICB0b2M6IHRydWUKICAgIHRvY19kZXB0aDogNAogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICBkZl9wcmludDogcGFnZWQKICAgIGZpZ19jYXB0aW9uOiB0cnVlCiAgcGRmX2RvY3VtZW50OgogICAgdG9jOiB0cnVlCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGKQpgYGAKCmBgYHtyIGxpYnJhcmllc30KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoZGF0YXBhc3RhKQoKbGlicmFyeShpZ3JhcGgpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShnZ3JhcGgpCgpsaWJyYXJ5KGNvd3Bsb3QpCmxpYnJhcnkoZ3JhcGhsYXlvdXRzKQoKbGlicmFyeSh2aXNOZXR3b3JrKQpsaWJyYXJ5KGdnYW5pbWF0ZSkKYGBgCgoKCiMjIERhdGEKCkluIHRoaXMgdHV0b3JpYWwsIHdlIHdpbGwgdXNlIGFub255bWl6ZWQgcmVzdWx0cyBmcm9tIHRoZSBzdHVkZW50IHN1cnZleSwgYWxsb3dpbmcgdXMgdG8gYW5hbHlzZSB0aGUgZnJpZW5kc2hpcCBuZXR3b3JrIHdpdGhpbiB0aGUgY291cnNlLiBUbyB0cmFjayB0aGUgZHluYW1pY3MsIHRoZSBzdXJ2ZXkgd2FzIGNvbmR1Y3RlZCBpbiB0aHJlZSB3YXZlcyBkdXJpbmcgdGhlIHllYXIuIEluIG9uZSBwYXJ0IG9mIHRoZSBzdXJ2ZXksIHdlIGFza2VkIHN0dWRlbnRzIHRvIGlkZW50aWZ5IHRoZWlyIGNvbGxlYWd1ZXMgdGhleSBjb25zaWRlciB0aGVpciBmcmllbmRzLiBXZSB3aWxsIHBheSBzcGVjaWFsIGF0dGVudGlvbiB0byB0aGUgYW5zd2VycyB0byB0aGlzIHF1ZXN0aW9uLiBXZSB3aWxsIHVzZSBzdHVkZW50cyBHUEEgYXMgbWV0YWRhdGEuIAoKYGBge3IgZGF0YX0Kc291cmNlKCJ+L25vb2JzUUEvbGFzaS9kYXRhcGFzdGFfbGFzaV8xLnIiKSAjZGF0YXBhc3RhIGRpcmVjdG9yeQoKZ3BhIDwtIGdwYSAlPiUgZmlsdGVyKEdQQTFrdXJzIDw9IDExKSAjIGJlY2F1c2Ugc29tZSBOQXMgd2VyZSBzZXQgYXMgOTkKYGBgCgoKVG8gYmVnaW4gd2l0aCwgbGV0J3MgYnVpbGQgYSBuZXR3b3JrIGJhc2VkIG9uIHRoZSBmaXJzdCB3YXZlIG9mIHRoZSBzdXJ2ZXkgVG8gZG8gdGhpcywgd2UgY29udmVydCB0aGUgZXhpc3RpbmcgZGF0YSBmcmFtZSBpbnRvIGEgZ3JhcGggdXNpbmcgdGhlICpncmFwaF9mcm9tX2RhdGFfZnJhbWUqIGZ1bmN0aW9uIGZyb20gdGhlICppZ3JhcGgqIHBhY2thZ2UuIAoKYGBge3IgY3JlYXRlIGdyYXBofQpncmFwaF9jMXcxIDwtIGdyYXBoX2Zyb21fZGF0YV9mcmFtZShjMXcxZ1ssYygzOjQpXSkKCncxIDwtIGMxdzFnICU+JSBncm91cF9ieShzb3VyY2Vfc3QpICU+JSBzZWxlY3Qoc291cmNlX3N0LCBTb3VyY2VfUHJvZ3JhbSkgJT4lIHVuaXF1ZSgpICU+JSAgc3VtbWFyaXNlKHByb2dyYW0gPSBTb3VyY2VfUHJvZ3JhbSkgJT4lIG11dGF0ZShudW0gPSBhcy5udW1lcmljKHN0cl9leHRyYWN0KHNvdXJjZV9zdCwgIihcXGQpKyIpKSkgJT4lIGFycmFuZ2UobnVtKQoKdzFyIDwtIGMxdzFnICU+JSBncm91cF9ieSh0YXJnZXRfc3QpICU+JSBzZWxlY3QodGFyZ2V0X3N0LCBUYXJnZXRfUHJvZ3JhbSkgJT4lIHVuaXF1ZSgpICU+JSAgc3VtbWFyaXNlKHByb2dyYW0gPSBUYXJnZXRfUHJvZ3JhbSkgJT4lIG11dGF0ZShudW0gPSBhcy5udW1lcmljKHN0cl9leHRyYWN0KHRhcmdldF9zdCwgIihcXGQpKyIpKSkgJT4lIGFycmFuZ2UobnVtKQoKdyA8LSBmdWxsX2pvaW4odzEsIHcxcikKCncgPC0gbGVmdF9qb2luKHcsIGdwYSwgYnkgPSBjKCJzb3VyY2Vfc3QiID0gInVuX2lkIikpCgpWKGdyYXBoX2MxdzEpJHByb2dyYW0gPSB3JHByb2dyYW0KVihncmFwaF9jMXcxKSRncGEgPSB3JEdQQTFrdXJzCgpgYGAKCgoKYGBge3IgYmFzaWMgcGxvdH0KZ2dyYXBoKGdyYXBoX2MxdzEpICsKICBnZW9tX2VkZ2VfbGluazAoYWxwaGEgPSAwLjMsIHNob3cubGVnZW5kID0gRkFMU0UpKwogIGdlb21fbm9kZV9wb2ludChhZXMoY29sb3IgPSBwcm9ncmFtICksIHNoYXBlID0gImNpcmNsZSIsIHNob3cubGVnZW5kID0gVFJVRSkrCiAgdGhlbWVfZ3JhcGgoKQpgYGAKCkdvb2QhIEhvd2V2ZXIsIHdlIGNhbiBtYWtlIG91ciBuZXR3b3JrIG1vcmUgaW5mb3JtYXRpdmUgYnkgYWRkaW5nIHN1Y2ggZWxlbWVudHMgYXMgdGhlIHNpemUgb2Ygbm9kZXMuIEZvciBleGFtcGxlLCBsZXQncyB0cnkgdG8gYWRkIG9uZSBvZiB0aGUgY2VudHJhbGl0eSBtZXRyaWNzIHRvIHRoZSBwbG90IC0tICpkZWdyZWUqLiAKCgpgYGB7ciBkZWdyZWV9Cm5vZGVzaXplIDwtIGlncmFwaDo6ZGVncmVlKGdyYXBoX2MxdzEsIG1vZGU9ICJpbiIpCgpnZ3JhcGgoZ3JhcGhfYzF3MSwgbGF5b3V0ID0gImZyIikgKwogIGdlb21fZWRnZV9saW5rMChhbHBoYSA9IDAuMywgICBzaG93LmxlZ2VuZCA9IEZBTFNFKSsKICBnZW9tX25vZGVfcG9pbnQoYWVzKGNvbG9yID0gcHJvZ3JhbSwgc2l6ZSA9IG5vZGVzaXplKSwgc2hhcGUgPSAiY2lyY2xlIiwgc3Ryb2tlID0gMSwgc2hvdy5sZWdlbmQgPSBUUlVFKSsKICB0aGVtZV9ncmFwaCgpCmBgYAoKQXQgdGhpcyBwb2ludCwgd2UgY2FuIHNlZSB0aGF0IHNvbWUgbm9kZXMgYXJlIHNpZ25pZmljYW50bHkgYmlnZ2VyIHRoYW4gb3RoZXJzLiBPYmlvdXNseSB3ZSB3YW50IHRvIGtub3cgbW9yZSBpbmZvcm1hdGlvbiBhYm91dCB0aGUsIHNvLCB3ZSBhZGQgbGFiZWxzIHRvIG91ciBub2Rlcy4KCmBgYHtyIG5hbWVzfQpnZ3JhcGgoZ3JhcGhfYzF3MSwgbGF5b3V0ID0gImZyIikgKwogIGdlb21fZWRnZV9saW5rMChhbHBoYSA9IDAuMywgICBzaG93LmxlZ2VuZCA9IEZBTFNFKSsKICBnZW9tX25vZGVfcG9pbnQoYWVzKGNvbG9yID0gcHJvZ3JhbSwgc2l6ZSA9IG5vZGVzaXplKSwgc2hhcGUgPSAiY2lyY2xlIiwgc3Ryb2tlID0gMSwgc2hvdy5sZWdlbmQgPSBUUlVFKSsKICBnZW9tX25vZGVfdGV4dChhZXMobGFiZWw9IG5hbWUpLCBjaGVja19vdmVybGFwID0gVCwgcmVwZWwgPSBGLCBzaXplID0gNSkrCiAgdGhlbWVfZ3JhcGgoKQpgYGAKCkxvb2tzIHJlYWxseSBtZXNzeSwgZG9lc250J3QgaXQ/IEx1Y2tpbHksIGl0IGlzIGVhc3kgdG8gZml4LiBXZSBjYW4gbGFiZWxzIHRvIG9ubHkgdGhvc2Ugbm9kZXMgd2hvIGhhdmUgdGhlIGhlaWdoZXN0IGRlZ3JlZS4gVGhpcyBhbGxvd3MgdXMgdG8gaWRlbnRpZnkgdGhlIG1vc3QgaW1wb3J0YW50IHBlb3BsZSB3aXRoaW4gbmV0d29yay4KCmBgYHtyIG5hbWVzIGFuZCBmaWx0ZXJ9CmdncmFwaChncmFwaF9jMXcxLCBsYXlvdXQgPSAiZnIiKSArCiAgZ2VvbV9lZGdlX2xpbmswKGFscGhhID0gMC4zLCAgIHNob3cubGVnZW5kID0gRkFMU0UpKwogIGdlb21fbm9kZV9wb2ludChhZXMoY29sb3IgPSBwcm9ncmFtLCBzaXplID0gbm9kZXNpemUpLCBzaGFwZSA9ICJjaXJjbGUiLCBzdHJva2UgPSAxLCBzaG93LmxlZ2VuZCA9IFRSVUUpKwogIGdlb21fbm9kZV90ZXh0KGFlcyhsYWJlbD0gbmFtZSwgZmlsdGVyID0gKCBub2Rlc2l6ZSA+PSAxMCkgKSwgcmVwZWwgID0gVCwgc2l6ZSA9IDUpKwogIHRoZW1lX2dyYXBoKCkKYGBgCgpBcyBhIG5leHQgc3RhZ2UsIHdlIGNyZWF0ZSBhIGZpeGVkIGxheW91dCB3aGljaCB3aWxsIGhlbHAgdXMgdG8gaGF2ZSBhIG1vcmUgc3RhYmxlIHJlcHJlc2VudGF0aW9uIG9mIGEgbmV0d29yay4KCmBgYHtyIGxheW91dH0KbGF5b3V0IDwtIGNyZWF0ZV9sYXlvdXQoZ3JhcGggPSBncmFwaF9jMXcxLCBsYXlvdXQgPSAnZnInKQoKCmdncmFwaChsYXlvdXQpICsKICBnZW9tX2VkZ2VfbGluazAoYWxwaGEgPSAwLjMsICAgc2hvdy5sZWdlbmQgPSBGQUxTRSkrCiAgZ2VvbV9ub2RlX3BvaW50KGFlcyhjb2xvciA9IHByb2dyYW0sIHNpemUgPSBub2Rlc2l6ZSksIHNoYXBlID0gImNpcmNsZSIsIHN0cm9rZSA9IDEsIHNob3cubGVnZW5kID0gVFJVRSkrCiAgZ2VvbV9ub2RlX3RleHQoYWVzKGxhYmVsPSBuYW1lLCBmaWx0ZXIgPSAoIG5vZGVzaXplID49IDEwKSApLCByZXBlbCAgPSBULCBzaXplID0gNSkrCiAgdGhlbWVfZ3JhcGgoKQpgYGAKCk5vdywgbGV0J3MgbG9vayBhdCBkaWZmZXJlbnQgY2VudHJhbGl0eSBtZXRyaWNzLiBUaGVyZSBhcmUgdGhyZWUgYmFzaWMgb25lczogKmJldHdlZW5uZXNzKiwgKmRlZ3JlZSosIGFuZCAqS2xlaW5iZXJnJ3MgYXV0aG9yaXR5IGNlbnRyYWxpdHkgc2NvcmVzKi4KCmBgYHtyIGRpZmZlcmVudCBtZXRyaWNzfQphciA9IGFycm93KGFuZ2xlID0gMzAsIGxlbmd0aCA9IHVuaXQoMiwgIm1tIiksIGVuZHMgPSAibGFzdCIsIHR5cGUgPSAib3BlbiIpCgoKbm9kZXNpemVfYnR3IDwtIGJldHdlZW5uZXNzKGdyYXBoX2MxdzEpCmJ0dyA8LSAgZ2dyYXBoKGxheW91dCkgKwogIGdlb21fZWRnZV9saW5rMChhbHBoYSA9IDAuNSwgIGFycm93ID0gYXIsIHNob3cubGVnZW5kID0gRkFMU0UsIGNvbG9yID0gImdyYXk2NiIpKwogIGdlb21fbm9kZV9wb2ludChhZXMoY29sb3IgPSBub2Rlc2l6ZV9idHcgKSwgIHNoYXBlID0gImNpcmNsZSIsIHN0cm9rZSA9IDEsIHNob3cubGVnZW5kID0gVFJVRSkrCiAgc2NhbGVfY29sb3VyX2dyYWRpZW50KGxvdz0iYmx1ZSIsIGhpZ2g9InJlZCIpICsKICBsYWJzKHRpdGxlID0gImJldHdlbm5lcyIpKwogIHRoZW1lX2dyYXBoKCkKCm5vZGVzaXplX2RnciA8LSBkZWdyZWUoZ3JhcGhfYzF3MSwgbW9kZSA9ICJpbiIpCmRnciA8LSAgZ2dyYXBoKGxheW91dCkgKwogIGdlb21fZWRnZV9saW5rMChhbHBoYSA9IDAuNSwgIGFycm93ID0gYXIsIHNob3cubGVnZW5kID0gRkFMU0UsIGNvbG9yID0gImdyYXk2NiIpKwogIGdlb21fbm9kZV9wb2ludChhZXMoY29sb3IgPSBub2Rlc2l6ZV9kZ3IgKSwgICBzaGFwZSA9ICJjaXJjbGUiLCBzdHJva2UgPSAxLCBzaG93LmxlZ2VuZCA9IFRSVUUpKwogIHNjYWxlX2NvbG91cl9ncmFkaWVudChsb3c9ImJsdWUiLCBoaWdoPSJyZWQiKSArCiAgbGFicyh0aXRsZSA9ICJkZWdyZWUiKSsKICB0aGVtZV9ncmFwaCgpCgoKbm9kZXNpemVfY250IDwtIGNlbnRyX2RlZ3JlZShncmFwaF9jMXcxKQpjbnQgPC0gZ2dyYXBoKGxheW91dCkgKwogIGdlb21fZWRnZV9saW5rMChhbHBoYSA9IDAuNSwgIGFycm93ID0gYXIsIHNob3cubGVnZW5kID0gRkFMU0UsIGNvbG9yID0gImdyYXk2NiIpKwogIGdlb21fbm9kZV9wb2ludChhZXMoY29sb3IgPSBub2Rlc2l6ZV9jbnQkcmVzICksICAgc2hhcGUgPSAiY2lyY2xlIiwgc3Ryb2tlID0gMSwgc2hvdy5sZWdlbmQgPSBUUlVFKSsKICBzY2FsZV9jb2xvdXJfZ3JhZGllbnQobG93PSJibHVlIiwgaGlnaD0icmVkIikgKwogIGxhYnModGl0bGUgPSAiY2VudHJhbGl0eSIpKwogIHRoZW1lX2dyYXBoKCkKCm5vZGVzaXplX2F1dCA8LSBhdXRob3JpdHlfc2NvcmUoZ3JhcGhfYzF3MSkKbm9kZXNpemVfYXV0IDwtIGFzLm51bWVyaWMobm9kZXNpemVfYXV0JHZlY3RvcikKZWduIDwtIGdncmFwaChsYXlvdXQpICsKICBnZW9tX2VkZ2VfbGluazAoYWxwaGEgPSAwLjUsICBhcnJvdyA9IGFyLCBzaG93LmxlZ2VuZCA9IEZBTFNFLCBjb2xvciA9ICJncmF5NjYiKSsKICBnZW9tX25vZGVfcG9pbnQoYWVzKGNvbG9yID0gbm9kZXNpemVfYXV0ICksICBzaGFwZSA9ICJjaXJjbGUiLCBzdHJva2UgPSAxLCBzaG93LmxlZ2VuZCA9IFRSVUUpKwogIHNjYWxlX2NvbG91cl9ncmFkaWVudChsb3c9ImJsdWUiLCBoaWdoPSJyZWQiKSArCiAgbGFicyh0aXRsZSA9ICJhdXRob3JpdHlfc2NvcmUiKSsKICB0aGVtZV9ncmFwaCgpCgpwbG90X2dyaWQoYnR3LCBkZ3IsIGNudCwgZWduKQoKYGBgCgpZb3VyIG9ubGluZSBwb3NpdGlvbiBhZmZlY3RzIGFjYWRlbWljIHN1Y2Nlc3MsIGxldCdzIGxvb2sgYXQgdGhlIG1ldHJpYyBhdXRob3JpdHlfc2NvcmUKCmBgYHtyIGF1dGhvcml0eV9zY29yZX0KP2F1dGhvcml0eV9zY29yZQpgYGAKCklmIHZlcnkgc2ltcGxlLCB0aGVuIHRoaXMgaXMgYSBtZXRyaWMgb2YgYWNjZXNzIHRvIG5vZGVzIHdpdGggaGlnaCBjZW50cmFsaXR5CgpgYGB7ciBncGEgYW5kIGh1Yn0KCmdwYV9nIDwtIGdncmFwaChsYXlvdXQpICsKICBnZW9tX2VkZ2VfbGluazAoYWxwaGEgPSAwLjUsICBhcnJvdyA9IGFyLCBzaG93LmxlZ2VuZCA9IEZBTFNFLCBjb2xvciA9ICJncmF5NjYiKSsKICBnZW9tX25vZGVfcG9pbnQoYWVzKGNvbG9yID0gVihncmFwaF9jMXcxKSRncGEgKSwgICBzaGFwZSA9ICJjaXJjbGUiLCBzdHJva2UgPSAxLCBzaG93LmxlZ2VuZCA9IFRSVUUpKwogIHNjYWxlX2NvbG91cl9ncmFkaWVudChsb3c9ImdyZWVuIiwgaGlnaD0icmVkIikgKwogIGxhYnModGl0bGUgPSAiR1BBIikrCiAgdGhlbWVfZ3JhcGgoKQoKYXV0aG9yaXR5X3Njb3JlIDwtIGF1dGhvcml0eV9zY29yZShncmFwaF9jMXcxKQphdXRob3JpdHlfc2NvcmUgPC0gYXMubnVtZXJpYyhhdXRob3JpdHlfc2NvcmUkdmVjdG9yKQplZ24gPC0gZ2dyYXBoKGxheW91dCkgKwogIGdlb21fZWRnZV9saW5rMChhbHBoYSA9IDAuNSwgIGFycm93ID0gYXIsIHNob3cubGVnZW5kID0gRkFMU0UsIGNvbG9yID0gImdyYXk2NiIpKwogIGdlb21fbm9kZV9wb2ludChhZXMoY29sb3IgPSBhdXRob3JpdHlfc2NvcmUgKSwgIHNoYXBlID0gImNpcmNsZSIsIHN0cm9rZSA9IDEsIHNob3cubGVnZW5kID0gVFJVRSkrCiAgc2NhbGVfY29sb3VyX2dyYWRpZW50KGxvdz0iZ3JlZW4iLCBoaWdoPSJyZWQiKSArCiAgbGFicyh0aXRsZSA9ICJhdXRob3JpdHlfc2NvcmUiKSsKICB0aGVtZV9ncmFwaCgpCgpwbG90X2dyaWQoZ3BhX2csIGVnbikKYGBgCgpTb21ldGltZXMgeW91IHdhbnQgdG8gYXBwbHkgY3VzdG9tIGNvbG9ycyB0byBkaXNwbGF5IGEgY2hhbmdlIGluIHRoZSBhdHRyaWJ1dGUgdmFsdWUuCnNvbWUgZXhhbXBsZXMgY29sb3JzIGFuZCBwYWxldHM6CisgaHR0cHM6Ly93d3cuZGF0YW5vdmlhLmNvbS9lbi9ibG9nL3RvcC1yLWNvbG9yLXBhbGV0dGVzLXRvLWtub3ctZm9yLWdyZWF0LWRhdGEtdmlzdWFsaXphdGlvbi8KKyBodHRwczovL3d3dy5uY2Vhcy51Y3NiLmVkdS9+ZnJhemllci9SU3BhdGlhbEd1aWRlcy9jb2xvclBhbGV0dGVDaGVhdHNoZWV0LnBkZgoKYGBge3IgbmljZSBjb2xvdXJ9CmdwYV9nIDwtIGdncmFwaChsYXlvdXQpICsKICBnZW9tX2VkZ2VfbGluazAoYWxwaGEgPSAwLjUsICBhcnJvdyA9IGFyLCBzaG93LmxlZ2VuZCA9IEZBTFNFLCBjb2xvciA9ICJncmF5NjYiKSsKICBnZW9tX25vZGVfcG9pbnQoYWVzKGNvbG9yID0gVihncmFwaF9jMXcxKSRncGEgKSwgICBzaGFwZSA9ICJjaXJjbGUiLCBzdHJva2UgPSAxLCBzaG93LmxlZ2VuZCA9IFRSVUUpKwogIHNjYWxlX2NvbG91cl9ncmFkaWVudChsb3c9IiMzQTFDNzEiLCBoaWdoPSIjRkZBRjdCIikgKwogIGxhYnModGl0bGUgPSAiR1BBIikrCiAgdGhlbWVfZ3JhcGgoKQoKCm5vZGVzaXplX2F1dCA8LSBhdXRob3JpdHlfc2NvcmUoZ3JhcGhfYzF3MSkKbm9kZXNpemVfYXV0IDwtIGFzLm51bWVyaWMobm9kZXNpemVfYXV0JHZlY3RvcikKZWduIDwtIGdncmFwaChsYXlvdXQpICsKICBnZW9tX2VkZ2VfbGluazAoYWxwaGEgPSAwLjUsICBhcnJvdyA9IGFyLCBzaG93LmxlZ2VuZCA9IEZBTFNFLCBjb2xvciA9ICJncmF5NjYiKSsKICBnZW9tX25vZGVfcG9pbnQoYWVzKGNvbG9yID0gbm9kZXNpemVfYXV0ICksICBzaGFwZSA9ICJjaXJjbGUiLCBzdHJva2UgPSAxLCBzaG93LmxlZ2VuZCA9IFRSVUUpKwogIHNjYWxlX2NvbG91cl9ncmFkaWVudChsb3c9IiMzQTFDNzEiLCAgaGlnaD0iI0ZGQUY3QiIpICsKICBsYWJzKHRpdGxlID0gImF1dGhvcml0eV9zY29yZSIpKwogIHRoZW1lX2dyYXBoKCkKCnBsb3RfZ3JpZChncGFfZywgZWduKQpgYGAKSXQgaXMgbm90IGFsd2F5cyBwb3NzaWJsZSB0byBzaG93IHR3byBtZXRyaWNzIG9uIHRoZSBzYW1lIGNoYXJ0LiBTaW5jZSBhIHBlcnNvbiwgaW4gcHJpbmNpcGxlLCBkb2VzIG5vdCBwZXJjZWl2ZSBtdWNoIGluZm9ybWF0aW9uLCBhbmQgdW5mb3J0dW5hdGVseSB0aGVyZSBhcmUgbm90IGVub3VnaCBzaGFwZXMuCgpgYGB7ciBncGEgYW5kIHByb2dyYW0gKEJBRCl9CgpnZ3JhcGgobGF5b3V0KSArCiAgZ2VvbV9lZGdlX2xpbmswKGFscGhhID0gMC41LCAgYXJyb3cgPSBhciwgc2hvdy5sZWdlbmQgPSBGQUxTRSwgY29sb3IgPSAiZ3JheTY2IikrCiAgZ2VvbV9ub2RlX3BvaW50KGFlcyhjb2xvciA9IFYoZ3JhcGhfYzF3MSkkZ3BhLCBzaGFwZSA9IHByb2dyYW0pLHN0cm9rZSA9IDEsIHNob3cubGVnZW5kID0gVFJVRSkrCiAgc2NhbGVfY29sb3VyX2dyYWRpZW50Mihsb3c9IiMwMDlmZmYiLCBtaWQgPSAiI2QzY2NlMyIsIGhpZ2g9IiNlYzJmNGIiLCBtaWRwb2ludCA9IDcpICsKICBsYWJzKHRpdGxlID0gIkdQQSIpKwogIHRoZW1lX2dyYXBoKCkKCmdncmFwaChsYXlvdXQpICsKICBnZW9tX2VkZ2VfbGluazAoYWxwaGEgPSAwLjUsICBhcnJvdyA9IGFyLCBzaG93LmxlZ2VuZCA9IEZBTFNFLCBjb2xvciA9ICJncmF5NjYiKSsKICBnZW9tX25vZGVfcG9pbnQoYWVzKHNpemUgPSAoZ3BhLTUpKjIsIGNvbG9yID0gcHJvZ3JhbSksIHN0cm9rZSA9IDEsIHNob3cubGVnZW5kID0gVFJVRSkrCiAgbGFicyh0aXRsZSA9ICJHUEEiKSsKICB0aGVtZV9ncmFwaCgpCgpnZ3JhcGgobGF5b3V0KSArCiAgZ2VvbV9lZGdlX2xpbmswKGFscGhhID0gMC41LCAgYXJyb3cgPSBhciwgc2hvdy5sZWdlbmQgPSBGQUxTRSwgY29sb3IgPSAiZ3JheTY2IikrCiAgZ2VvbV9ub2RlX3BvaW50KGFlcyhzaXplID0gKGdwYS01KSoyLCBzaGFwZSA9IGFzLmZhY3RvcihncGEpLCBjb2xvciA9IHByb2dyYW0pLCBzdHJva2UgPSAxLCBzaG93LmxlZ2VuZCA9IFRSVUUpKwogIGxhYnModGl0bGUgPSAiR1BBIikrCiAgdGhlbWVfZ3JhcGgoKQpgYGAKCkluIG1vc3QgY2FzZXMsIHR3byBwaWN0dXJlcyBvZiB0aGUgbmV0d29yayBsb29rIG1vcmUgaW5mb3JtYXRpdmUuIFRoZSBtYWluIHRoaW5nOiBkbyBub3QgZm9yZ2V0IHRvIGZpeCB0aGUgbGF5b3V0LgoKYGBge3IgMiBwbG90cyBmb3IgZ3BhIGFuZCB9CgpnX2dwYSA8LSAgZ2dyYXBoKGxheW91dCkgKwogIGdlb21fZWRnZV9saW5rMChhbHBoYSA9IDAuNSwgIGFycm93ID0gYXIsIHNob3cubGVnZW5kID0gRkFMU0UsIGNvbG9yID0gImdyYXk2NiIpKwogIGdlb21fbm9kZV9wb2ludChhZXMoY29sb3IgPSBWKGdyYXBoX2MxdzEpJGdwYSksc3Ryb2tlID0gMSwgc2hvdy5sZWdlbmQgPSBUUlVFKSsKICBzY2FsZV9jb2xvdXJfZ3JhZGllbnQyKGxvdz0iIzAwOWZmZiIsIG1pZCA9ICIjZDNjY2UzIiwgaGlnaD0iI2VjMmY0YiIsIG1pZHBvaW50ID0gNykgKwogIGxhYnModGl0bGUgPSAiR1BBIikrCiAgdGhlbWVfZ3JhcGgoKQoKCgpnX3Byb2cgPC0gZ2dyYXBoKGxheW91dCkgKwogIGdlb21fZWRnZV9saW5rMChhbHBoYSA9IDAuNSwgIGFycm93ID0gYXIsIHNob3cubGVnZW5kID0gRkFMU0UsIGNvbG9yID0gImdyYXk2NiIpKwogIGdlb21fbm9kZV9wb2ludChhZXMoY29sb3IgPSBwcm9ncmFtICksICBzaGFwZSA9ICJjaXJjbGUiLCBzdHJva2UgPSAxLCBzaG93LmxlZ2VuZCA9IFRSVUUpKwogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiU2V0MyIpKwogIGxhYnModGl0bGUgPSAicHJvZ3JhbSIpKwogIHRoZW1lX2dyYXBoKCkKCnBsb3RfZ3JpZChnX2dwYSwgZ19wcm9nKQoKYGBgCgpJdCBpcyBjb21wbGV0ZWx5IHVzZWxlc3MgdG8gdHJ5IHRvIGRpc3BsYXkgYWxsIHRoZSBuYW1lcywgYnV0IHNvbWV0aW1lcyBpdCBpcyBpbXBvcnRhbnQgdG8gaGlnaGxpZ2h0IHNvbWUgbm9kZXMgb24gdGhlIG5ldHdvcmsuIFNvIHVzaW5nIHRoZSBmaWx0ZXIsIHdlIHdpbGwgbGVhdmUgb25seSBzdHVkZW50cyB3aXRoIGxvdyBncmFkZXMgYW5kIGhpZ2ggZ3JhZGVzIGluIHRoZSBhZGphY2VudCBjaGFydHMuCgpgYGB7ciBmaWx0ZXIgYW5kIGFscGhhfQoKVihncmFwaF9jMXcxKSRncGFfbG93IDwtIGlmZWxzZShWKGdyYXBoX2MxdzEpJGdwYSA9PSA2LCAxLDAuMykgClYoZ3JhcGhfYzF3MSkkZ3BhX2hpZ2ggPC0gaWZlbHNlKFYoZ3JhcGhfYzF3MSkkZ3BhID49IDksIDEsIDAuMykgCmxheW91dCA8LSBjcmVhdGVfbGF5b3V0KGdyYXBoID0gZ3JhcGhfYzF3MSwgbGF5b3V0ID0gJ2ZyJykKCgpnZ3JhcGgobGF5b3V0KSArCiAgZ2VvbV9lZGdlX2xpbmswKGFscGhhID0gMC41LCAgYXJyb3cgPSBhciwgc2hvdy5sZWdlbmQgPSBGQUxTRSwgY29sb3IgPSAiZ3JheTY2IikrCiAgZ2VvbV9ub2RlX3BvaW50KGFlcyhjb2xvciA9IHByb2dyYW0sIGFscGhhID0gZ3BhX2xvdyksICBzaGFwZSA9ICJjaXJjbGUiLCBzdHJva2UgPSAxLCBzaG93LmxlZ2VuZCA9IFRSVUUpKwogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiU2V0MyIpKwogIGxhYnModGl0bGUgPSAiTG93IGdwYSBzdHVkZW50cyIpKwogIHRoZW1lX2dyYXBoKCkKCmdncmFwaChsYXlvdXQpICsKICBnZW9tX2VkZ2VfbGluazAoYWxwaGEgPSAwLjUsICBhcnJvdyA9IGFyLCBzaG93LmxlZ2VuZCA9IEZBTFNFLCBjb2xvciA9ICJncmF5NjYiKSsKICBnZW9tX25vZGVfcG9pbnQoYWVzKGNvbG9yID0gcHJvZ3JhbSwgYWxwaGEgPSBncGFfaGlnaCksICBzaGFwZSA9ICJjaXJjbGUiLCBzdHJva2UgPSAxLCBzaG93LmxlZ2VuZCA9IFRSVUUpKwogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiU2V0MyIpKwogIGxhYnModGl0bGUgPSAiaGlnaCBncGEgc3R1ZGVudHMiKSsKICB0aGVtZV9ncmFwaCgpCmBgYAoKIyMgaW50ZXJhY3RpdmUgY3JlYXRpb24gb2YgZ3JhcGgKClRoZXJlIGlzIGEgcGFja2FnZSB0byBjb25maWd1cmUgbmV0d29yayB2aXN1YWxpemF0aW9uIHRocm91Z2ggYSBtb3JlIGZhbWlsaWFyIGdyYXBoaWNhbCBpbnRlcmZhY2UuIEluc3RhbGwgdGhlIHNuYWhlbHBlciBwYWNrYWdlLCBsb2FkIGl0LCBhbmQgdGhlbiBoaWdobGlnaHQgYW55IGlncmFwaCBvYmplY3QgKGZvciBleGFtcGxlIGdyYXBoX2MxdzEpIHdpdGggdGhlIG1vdXNlLCBjbGljayBvbiB0aGUgQWRkaW5zIHRhYiAodG9wIHBhbmVsIG9mIHJzdHVkaW8pLCBhbmQgc2VsZWN0IF9fU05BaGVscGVyX18gZnJvbSB0aGUgbGlzdC4KCmBgYHtyIHNuYWhlbHBlciBwbG90IHVpfQojIGxpYnJhcnkoc25haGVscGVyKQojIAojIFNOQWhlbHBlckdhZGdldChncmFwaF9jMXcxKQpgYGAKCiMjIGNlbnRyYWxpdHksIChlZ28gbmV0d29yaz8pCgpVc3VhbGx5LCBtb3N0IGxheW91dHMgc2hvdyB0aGUgZW50aXJlIHN0cnVjdHVyZSBvZiB0aGUgbmV0d29yaywgb3IgdHJ5IHRvIGhpZ2hsaWdodCB0aGUgY29tbXVuaXR5LiBPbmUgZXhhbXBsZSBvZiBzdWNoIGEgbWFwcGluZy4KCmBgYHtyIHN0cmVzc30KZ2dyYXBoKGdyYXBoX2MxdzEsIGxheW91dCA9ICJzdHJlc3MiKSsKICBnZW9tX2VkZ2VfbGluazAoZWRnZV9jb2xvdXIgPSAiZ3JleTY2IiwgYXJyb3cgPSBhciwgYWxwaGEgPSAwLjUpKwogIGdlb21fbm9kZV9wb2ludChhZXMoY29sb3IgPSBwcm9ncmFtLCBzaXplID0gbm9kZXNpemUpLHNoYXBlPTE5KSsKICBnZW9tX25vZGVfdGV4dChhZXMoZmlsdGVyID0gbm9kZXNpemUgPj0gMTAsIGxhYmVsID0gbmFtZSksIGZhbWlseT0ic2VyaWYiLCBzaXplID0gNSkrCiAgc2NhbGVfZWRnZV93aWR0aChyYW5nZSA9IGMoMC4yLDMpKSsKICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIlNldDMiKSsKICBzY2FsZV9zaXplKHJhbmdlID0gYygxLDYpKSsKICB0aGVtZV9ncmFwaCgpKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKYGBgCgpUaGlzIGxheW91dCBoZWxwcyB0byBfX2ZvY3VzX18gb24gYSBzcGVjaWZpYyBub2RlLCBsb29rIGF0IGl0cyBpbW1lZGlhdGUgc3Vycm91bmRpbmdzLgoKYGBge3J9CncxX2Nvbm5lY3RlZCA8LSBncmFwaF9jMXcxICU+JSBkZWxldGUudmVydGljZXMoYygic3R1ZGVudCAxNSIsICJzdHVkZW50IDEwMCIsICJzdHVkZW50IDkzIiwgInN0dWRlbnQgMTQzIikpICNjb25uZWN0ZWQgZ3JhcGggaXMgYSBncmFwaCB3aXRob3V0IGlzb2xhdGVkIG5vZGVzCm5vZGVzaXplX2MgPC0gZGVncmVlKHcxX2Nvbm5lY3RlZCwgbW9kZSA9ICJpbiIpCgpnZ3JhcGgodzFfY29ubmVjdGVkLCBsYXlvdXQgPSAiZm9jdXMiLCB2ID0gMTIwKSsgI3YgaWQgb2YgYSBub2RlLCBub3QgYWx3YXlzIHNhbWUgYXMgYSBuYW1lCiAgZ2VvbV9lZGdlX2xpbmswKGVkZ2VfY29sb3VyID0gImdyZXk2NiIpKwogIGdlb21fbm9kZV9wb2ludChhZXMoZmlsbCA9IHByb2dyYW0sIHNpemU9bm9kZXNpemVfYyksIHNoYXBlID0gMjEpKwogIHNjYWxlX2VkZ2Vfd2lkdGhfY29udGludW91cyhyYW5nZSA9IGMoMC4yLDEuMikpKwogIHNjYWxlX3NpemVfY29udGludW91cyhyYW5nZSA9IGMoMSw1KSkrCiAgZ2VvbV9ub2RlX3RleHQoYWVzKGZpbHRlciA9IChub2Rlc2l6ZV9jID49IDEwKSxzaXplID0gbm9kZXNpemVfYysyLCBsYWJlbCA9IG5hbWUpLCBzaXplID0gNSwKICAgICAgICAgICAgICAgICBmYW1pbHkgPSAic2VyaWYiKSsKICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIlNldDMiKSsKICBjb29yZF9maXhlZCgpKwogIHRoZW1lX2dyYXBoKCkrCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpgYGAKCkFkZCBjaXJjbGVzIHRvIGJldHRlciBkaXN0aW5ndWlzaCB0aGUgb3JkZXIgb2YgdGhlIG5laWdoYm9yaG9vZC4KCmBgYHtyIGxvdyBncGEgY2VudGVyZWR9CmdncmFwaCh3MV9jb25uZWN0ZWQsIGxheW91dCA9ICJmb2N1cyIsIHYgPSAxNDIpKwogIGdlb21fZWRnZV9saW5rMChlZGdlX2NvbG91ciA9ICJncmV5NjYiKSsKICBkcmF3X2NpcmNsZShjb2wgPSAiIzAwQkZGRiIsIHVzZSA9ICJmb2N1cyIsIG1heC5jaXJjbGUgPSAzKSsKICBnZW9tX25vZGVfcG9pbnQoYWVzKGZpbGwgPSBwcm9ncmFtLCBzaXplPWdwYSksIHNoYXBlID0gMjEpKwogIHNjYWxlX2VkZ2Vfd2lkdGhfY29udGludW91cyhyYW5nZSA9IGMoMC4yLDEuMikpKwogIHNjYWxlX3NpemVfY29udGludW91cyhyYW5nZSA9IGMoMSw1KSkrCiAgZ2VvbV9ub2RlX3RleHQoYWVzKGZpbHRlciA9IChub2Rlc2l6ZV9jID49IDEpLHNpemUgPSBub2Rlc2l6ZV9jKzIsIGxhYmVsID0gbmFtZSksIHNpemUgPSA1LAogICAgICAgICAgICAgICAgIGZhbWlseSA9ICJzZXJpZiIsIGNoZWNrX292ZXJsYXAgPSBUKSsKICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIlNldDMiKSsKICBjb29yZF9maXhlZCgpKwogIHRoZW1lX2dyYXBoKCkrCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpCmBgYAoKU28gYSBzdHVkZW50IHdpdGggYSBsb3cgZ3BhIGlzIG9uIHRoZSBlZGdlIG9mIHRoZSBuZXR3b3JrLCBhbmQgaGlzIG5laWdoYm9ycyBkbyBub3QgaGF2ZSBhIGJpZyBkZWdyZWUuCgpgYGB7ciBoaWdoIGNlbnRlcmVkfQpnZ3JhcGgodzFfY29ubmVjdGVkLCBsYXlvdXQgPSAiZm9jdXMiLCB2ID0gMTAyKSsKICBnZW9tX2VkZ2VfbGluazAoZWRnZV9jb2xvdXIgPSAiZ3JleTY2IikrCiAgZHJhd19jaXJjbGUoY29sID0gIiMwMEJGRkYiLCB1c2UgPSAiZm9jdXMiLCBtYXguY2lyY2xlID0gMykrCiAgZ2VvbV9ub2RlX3BvaW50KGFlcyhmaWxsID0gcHJvZ3JhbSwgc2l6ZT1ncGEpLCBzaGFwZSA9IDIxKSsKICBzY2FsZV9lZGdlX3dpZHRoX2NvbnRpbnVvdXMocmFuZ2UgPSBjKDAuMiwxLjIpKSsKICBzY2FsZV9zaXplX2NvbnRpbnVvdXMocmFuZ2UgPSBjKDEsNSkpKwogIGdlb21fbm9kZV90ZXh0KGFlcyhmaWx0ZXIgPSAobm9kZXNpemVfYyA+PSAxKSxzaXplID0gbm9kZXNpemVfYysyLCBsYWJlbCA9IG5hbWUpLCBzaXplID0gNSwKICAgICAgICAgICAgICAgICBmYW1pbHkgPSAic2VyaWYiLCBjaGVja19vdmVybGFwID0gVCkrCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJTZXQzIikrCiAgY29vcmRfZml4ZWQoKSsKICB0aGVtZV9ncmFwaCgpKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQpgYGAKCkluIHR1cm4sIGEgc3R1ZGVudCB3aXRoIGEgbGFyZ2UgR1BBIGlzIG1vcmUgaW5jbHVkZWQgaW4gdGhlIG5ldHdvcmssIG1vc3Qgb2YgaGlzIGZyaWVuZHMgYXJlIGZyb20gdGhlIHNhbWUgZWR1Y2F0aW9uYWwgcHJvZ3JhbSwgd2l0aCB0aGUgc2FtZSBsZXZlbCBvZiBHUEEuCgoKIyMgZHluYW1pYyAKCk5vdyBqdXN0IHJ1biB0aGUgZm9sbG93aW5nIGNodW5rcy4gdGhlIGRhdGEgZm9yIHRoZSBzZWNvbmQgYW5kIHRoaXJkIHdhdmVzIG9mIHRoZSBzYW1lIHN1cnZleSBhcmUgbm90IHZlcnkgd2VsbCBwcmVwYXJlZCB0aGVyZS4KCmBgYHtyIHR3byBsYXN0IHdhdmVzIGlnbm9yZSBwbHp9CgoKdzIgPC0gZ3JhcGhfZnJvbV9kYXRhX2ZyYW1lKGMxdzJnWyxjKDM6NCldKQp3MiA9IHNpbXBsaWZ5KHcyLCByZW1vdmUubXVsdGlwbGUgPSBGLCByZW1vdmUubG9vcHMgPSBUKSAKdzMgPC0gZ3JhcGhfZnJvbV9kYXRhX2ZyYW1lKGMxdzNnWyxjKDM6NCldKQp3MyA9IHNpbXBsaWZ5KHczLCByZW1vdmUubXVsdGlwbGUgPSBGLCByZW1vdmUubG9vcHMgPSBUKQoKdzFzIDwtICBkZWdyZWUoZ3JhcGhfYzF3MSkKdzFzIDwtIGRhdGEuZnJhbWUoa2V5TmFtZT1uYW1lcyh3MXMpLCByb3cubmFtZXM9TlVMTCkKCgp3M3MgPC0gIGRlZ3JlZSh3MykKdzNzIDwtIGRhdGEuZnJhbWUoa2V5TmFtZT1uYW1lcyh3M3MpLCByb3cubmFtZXM9TlVMTCkKCgp3MnMgPC0gIGRlZ3JlZSh3MikKdzJzIDwtIGRhdGEuZnJhbWUoa2V5TmFtZT1uYW1lcyh3MnMpLCByb3cubmFtZXM9TlVMTCkKCgoKdzJzIDwtIGxlZnRfam9pbih3MnMsIHcsIGJ5ID0gYygia2V5TmFtZSI9InNvdXJjZV9zdCIpKQpWKHcyKSRwcm9ncmFtIDwtIHcycyRwcm9ncmFtClYodzIpJGdwYSA8LSB3MnMkR1BBMWt1cnMKdzNzIDwtIGxlZnRfam9pbih3M3MsIHcsIGJ5ID0gYygia2V5TmFtZSI9InNvdXJjZV9zdCIpKQpWKHczKSRwcm9ncmFtIDwtIHczcyRwcm9ncmFtClYodzMpJGdwYSA8LSB3M3MkR1BBMWt1cnMKYGBgCgpPbmUgb2YgdGhlIHNvbHV0aW9ucyB0byB0aGUgcHJvYmxlbSBvZiBpc29sYXRlZCBub2RlcyB3aXRob3V0IGluZm9ybWF0aW9uIGluIHNvbWUgd2F2ZXMgaXMgdG8gcmVtb3ZlIHRoZW0gZnJvbSBhbGwgd2F2ZXMuIEhvd2V2ZXIsIGhlcmUgd2Ugc2ltcGx5IGFkZCB0aGVzZSBub2RlcyB0byB0aGUgZ3JhcGhzLgoKYGBge3IgbWlzc2luZ3MgaWdub3JlIHgyfQpjb21wYXJpc29uMSA8LSAgc2V0ZGlmZihuYW1lcyhWKGdyYXBoX2MxdzEpKSwgbmFtZXMoVih3MikpICkKY29tcGFyaXNvbjIgPC0gIHNldGRpZmYobmFtZXMoVih3MikpLCBuYW1lcyhWKHczKSkgKQpjb21wYXJpc29uMyA8LSAgc2V0ZGlmZihuYW1lcyhWKHczKSksIG5hbWVzKFYoZ3JhcGhfYzF3MSkpICkKY29tcGFyaXNvbiA8LSBjKGNvbXBhcmlzb24xLGNvbXBhcmlzb24yLGNvbXBhcmlzb24zKQoKY29tcGFyaXNvbiA8LSBjb21wYXJpc29uICU+JSB1bmlxdWUoKSAlPiUgbmEub21pdCgpCmNvbXBhcmlzb24gPC0gZGF0YS5mcmFtZShzb3VyY2Vfc3QgPSBjb21wYXJpc29uWy0yMl0pCgpjb21wYXJpc29uIDwtIGxlZnRfam9pbihjb21wYXJpc29uLCB3LCBieSA9IGMoInNvdXJjZV9zdCIpICkKY29tcGFyaXNvbiA9IGNvbXBhcmlzb25bLGMoLTMsLTQpXQoKdzFuIDwtIGRhdGEuZnJhbWUoc291cmNlX3N0ID0gbmFtZXMoVihncmFwaF9jMXcxKSkgLCBwcmVzZW50ID0gVCkKdzJuIDwtIGRhdGEuZnJhbWUoc291cmNlX3N0ID0gbmFtZXMoVih3MikpICwgcHJlc2VudCA9IFQpCnczbiA8LSBkYXRhLmZyYW1lKHNvdXJjZV9zdCA9IG5hbWVzKFYodzMpKSAsIHByZXNlbnQgPSBUKQoKcmlnaHRfam9pbih3MW4sIGNvbXBhcmlzb24sIGJ5ID0gInNvdXJjZV9zdCIpCncybiA8LSAgcmlnaHRfam9pbih3Mm4sIGNvbXBhcmlzb24sIGJ5ID0gInNvdXJjZV9zdCIpCnczbiA8LSAgcmlnaHRfam9pbih3M24sIGNvbXBhcmlzb24sIGJ5ID0gInNvdXJjZV9zdCIpCgp3Mm4kcHJlc2VudFtpcy5uYSh3Mm4kcHJlc2VudCldIDwtIEYKdzNuJHByZXNlbnRbaXMubmEodzNuJHByZXNlbnQpXSA8LSBGCgp3Mm4gPSB3Mm4gJT4lIGZpbHRlcihwcmVzZW50ID09IEYpICU+JSBzZWxlY3QoLXByZXNlbnQpCnczbiA9IHczbiAlPiUgZmlsdGVyKHByZXNlbnQgPT0gRiApJT4lIHNlbGVjdCgtcHJlc2VudCkKYGBgCgpgYGB7ciBhZGQgbWlzc2luZ3MgdG8gbmV0c30KdzFfZnVsbCA8LSAgZ3JhcGhfYzF3MQoKCncyX2Z1bGwgPC0gdzIgJT4lIGFkZC52ZXJ0aWNlcyhucm93KHcybiksIG5hbWUgPSB3Mm4kc291cmNlX3N0LCBwcm9ncmFtID0gdzJuJHByb2dyYW0sIGdwYSA9IHcybiRHUEExa3VycykgJT4lIGRlbGV0ZS52ZXJ0aWNlcygiTkEiKQoKdzNfZnVsbCA8LSB3MyAlPiUgYWRkLnZlcnRpY2VzKG5yb3codzNuKSwgbmFtZSA9IHczbiRzb3VyY2Vfc3QsIHByb2dyYW0gPSB3M24kcHJvZ3JhbSwgZ3BhID0gdzNuJEdQQTFrdXJzKSAlPiUgZGVsZXRlLnZlcnRpY2VzKCJOQSIpCmBgYAoKd3JpdGUgYWxsIHRocmVlIG5ldHdvcmtzIGluIG9uZSBsaXN0CgpgYGB7ciBuZXR3b3JrcyB0byBsaXN0fQp0bmV0IDwtIGxpc3QodzFfZnVsbCwgdzJfZnVsbCwgIHczX2Z1bGwpCnh5IDwtIGxheW91dF9hc19keW5hbWljKHRuZXQsIGFscGhhID0gMC4yKQpgYGAKCnJlbW92ZSB0aGUgc3R1ZGVudOKAmXMgd29yZHMgZnJvbSB0aGUgbmFtZXMgb2YgdGhlIG5vZGVzLiBpdCB3aWxsIGJlIG1vcmUgYmVhdXRpZnVsIGFuZCBpdCB0YWtlcyB1cCBsZXNzIHNwYWNlCgpgYGB7ciByZW1vdmUgd29yZCBzdHVkZW50fQpWKHcxX2Z1bGwpJG5hbWUgPC0gc3RyX2V4dHJhY3QoVih3MV9mdWxsKSRuYW1lLCAiKFxcZCkrIikKVih3Ml9mdWxsKSRuYW1lIDwtIHN0cl9leHRyYWN0KFYodzJfZnVsbCkkbmFtZSwgIihcXGQpKyIpClYodzNfZnVsbCkkbmFtZSA8LSBzdHJfZXh0cmFjdChWKHczX2Z1bGwpJG5hbWUsICIoXFxkKSsiKQpgYGAKCkR1ZSB0byB0aGUgY29uZmxpY3Qgb2YgcGFja2FnZSB2ZXJzaW9ucywgdGhlIGZ1bmN0aW9uIGxheW91dF9hc19keW5hbWljIGlzIG5vdCByZWNvZ25pemVkIGNvcnJlY3RseS4gVGhlcmVmb3JlLCB3ZSB3aWxsIHN1YnN0aXR1dGUgdGhlIGNvb3JkaW5hdGVzIGluIGxheW91dCB3aXRoIGEgZGlmZmVyZW50IGFsZ29yaXRobSBpbiBvcmRlciB0byBjb3JyZWN0bHkgZGlzcGxheSB0aGUgcG9zaXRpb25zIG9uIHRoZSBjaGFydHMuCgpgYGB7ciBtYXNraW5nIGxheW91dCB3aXRoIG5ldyBjb29yZGluYXRlc30KbDEgPC0gIGNyZWF0ZV9sYXlvdXQodG5ldFtbMV1dLCBsYXlvdXQgPSAnZ3JpZCcpCmwxJHggPC0geHlbWzFdXVssMV0KbDEkeSA8LSB4eVtbMV1dWywyXQoKbDIgPC0gIGNyZWF0ZV9sYXlvdXQodG5ldFtbMl1dLCBsYXlvdXQgPSAnZ3JpZCcpCmwyJHggPC0geHlbWzJdXVssMV0KbDIkeSA8LSB4eVtbMl1dWywyXQoKbDMgPC0gIGNyZWF0ZV9sYXlvdXQodG5ldFtbM11dLCBsYXlvdXQgPSAnZ3JpZCcpCmwzJHggPC0geHlbWzNdXVssMV0KbDMkeSA8LSB4eVtbM11dWywyXQoKCmcxZGVncmVlIDwtIGRlZ3JlZSh0bmV0W1sxXV0pCmcyZGVncmVlIDwtIGRlZ3JlZSh0bmV0W1syXV0pCmczZGVncmVlIDwtIGRlZ3JlZSh0bmV0W1szXV0pCgpnMSA8LSAgZ2dyYXBoKGwxKSsKICBnZW9tX2VkZ2VfbGluazAoZWRnZV93aWR0aCA9IDAuMixlZGdlX2NvbG91ciA9ICJncmV5MjUiKSsKICBnZW9tX25vZGVfcG9pbnQoc2hhcGU9MjEsIGFlcyhzaXplID0gZzFkZWdyZWUsIGZpbGwgPSBwcm9ncmFtKSkrCiAgdGhlbWVfZ3JhcGgoKSsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpKwogICBnZW9tX25vZGVfdGV4dChhZXMobGFiZWwgPSBuYW1lLCBmaWx0ZXIgPSAoIGcxZGVncmVlID49IDEwKSApLCBjaGVja19vdmVybGFwID0gVCwgcmVwZWwgID0gRiwgc2l6ZSA9IDUpKwogIGxhYnModGl0bGUgPSBwYXN0ZTAoIldhdmUgIiwgMSkpCgoKZzIgPC0gZ2dyYXBoKGwyKSsKICBnZW9tX2VkZ2VfbGluazAoZWRnZV93aWR0aCA9IDAuMixlZGdlX2NvbG91ciA9ICJncmV5MjUiKSsKICBnZW9tX25vZGVfcG9pbnQoc2hhcGU9MjEsYWVzKHNpemUgPSBnMmRlZ3JlZSwgZmlsbCA9IHByb2dyYW0pKSsKICB0aGVtZV9ncmFwaCgpKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikrCiAgICAgZ2VvbV9ub2RlX3RleHQoYWVzKGxhYmVsPSBuYW1lLCBmaWx0ZXIgPSAoIGcyZGVncmVlID49IDEwKSApLCBjaGVja19vdmVybGFwID0gVCwgcmVwZWwgID0gRiwgc2l6ZSA9IDUpKwogIGxhYnModGl0bGUgPSBwYXN0ZTAoIldhdmUgIiwgMikpCgoKCmczIDwtIGdncmFwaChsMykrCiAgZ2VvbV9lZGdlX2xpbmswKGVkZ2Vfd2lkdGggPSAwLjIsZWRnZV9jb2xvdXIgPSAiZ3JleTI1IikgKwogIGdlb21fbm9kZV9wb2ludChzaGFwZT0yMSwgYWVzKHNpemUgPSBnM2RlZ3JlZSwgZmlsbCA9IHByb2dyYW0pKSsKICB0aGVtZV9ncmFwaCgpKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikrCiAgICAgICBnZW9tX25vZGVfdGV4dChhZXMobGFiZWw9IG5hbWUsIGZpbHRlciA9ICggZzNkZWdyZWUgPj0gMTApICksIGNoZWNrX292ZXJsYXAgPSBULCByZXBlbCAgPSBGLCBzaXplID0gNSkrCiAgbGFicyh0aXRsZSA9IHBhc3RlMCgiV2F2ZSAiLCAzKSkKCgpwbG90X2dyaWQoZzEsIGcyLCBnMywgbmNvbCA9IDMpCmBgYAoKc2luY2UgdGhlIHNlY29uZCB3YXZlIHByYWN0aWNhbGx5IGRvZXMgbm90IHJlZmxlY3QgY2hhbmdlcywgaXQgY2FuIGJlIHNraXBwZWQKCmBgYHtyIHNjaW5jZSBzZWNvbmQgaXMgbm90IHJlYWxseSBkaWZmZXJlbnR9CnBsb3RfZ3JpZChnMSwgZzMsIG5jb2wgPSAyKQpgYGAKCiMjIHRlbXBvcmFsCgpXZSB3cml0ZSBhbGwgdGhyZWUgd2F2ZXMgaW4gb25lIGdyYXBoCgpgYGB7ciBtZXJnZSB3YXZlc30KZWRnZXMudzEgPC0gYXNfdGliYmxlKGFzX2VkZ2VsaXN0KHcxX2Z1bGwsIG5hbWVzID0gVFJVRSkpCmVkZ2VzLncyIDwtIGFzX3RpYmJsZShhc19lZGdlbGlzdCh3Ml9mdWxsLCBuYW1lcyA9IFRSVUUpKQplZGdlcy53MyA8LSBhc190aWJibGUoYXNfZWRnZWxpc3QodzNfZnVsbCwgbmFtZXMgPSBUUlVFKSkKCmVkZ2VzLncxJHdhdmUgPSAxCmVkZ2VzLncyJHdhdmUgPSAyCmVkZ2VzLnczJHdhdmUgPSAzCgplZGdlcy53MTIzIDwtIHJiaW5kKGVkZ2VzLncxLCBlZGdlcy53MiwgZWRnZXMudzMpCncxMjMgPC0gZ3JhcGhfZnJvbV9kYXRhX2ZyYW1lKGVkZ2VzLncxMjMpCgpub2Rlc2l6ZTEyMyA8LSAgZGVncmVlKHcxMjMpCgp3MTIzcyA8LSBkYXRhLmZyYW1lKGtleU5hbWU9YXMubnVtZXJpYyhuYW1lcyhub2Rlc2l6ZTEyMykpKQp3MTIzcyA8LSBsZWZ0X2pvaW4odzEyM3MsIHcsIGJ5ID0gYygia2V5TmFtZSI9Im51bSIpKQpWKHcxMjMpJHByb2dyYW0gPC0gdzEyM3MkcHJvZ3JhbQpWKHcxMjMpJGdwYSA8LSB3MTIzcyRHUEExa3VycwpWKHcxMjMpJGdwYS5vbWl0IDwtIHcxMjNzJEdQQTFrdXJzClYodzEyMykkZ3BhLm9taXRbaXMubmEoVih3MTIzKSRncGEub21pdCldIDwtIDUKYGBgCgpOZXh0LCB0aGUgbGF5b3V0IHdpbGwgYmUgY2FsY3VsYXRlZCBmb3IgdGhlIGN1bXVsYXRpdmUgZ3JhcGggb2YgYWxsIHRocmVlIHdhdmVzLCBhbmQgdGhlIGxpbmtzIG9mIGVhY2ggd2F2ZSB3aWxsIGJlIGRpc3BsYXllZCBpbiB0aGVpciBjb2xvci4KCmBgYHtyIHBsb3QgMTIzfQpub2Rlc2l6ZTEyMyA8LSBkZWdyZWUodzEyMyAsbW9kZSA9ImluIikKCmdncmFwaCh3MTIzLCBsYXlvdXQgPSAiZnIiKSsKICBnZW9tX2VkZ2VfbGluazAoZWRnZV93aWR0aCA9IDAuMiwgYWVzKGNvbG9yID0gYXMuZmFjdG9yKHdhdmUpKSkgKwogIGdlb21fbm9kZV9wb2ludChhZXMoc2l6ZSA9IG5vZGVzaXplMTIzLCBjb2xvciA9IHByb2dyYW0pLCBzaGFwZSA9ICJjaXJjbGUiICkgKwogIHRoZW1lX2dyYXBoKCkrCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpKwogICAgICAgZ2VvbV9ub2RlX3RleHQoYWVzKGxhYmVsPSBuYW1lLCBmaWx0ZXIgPSAgbm9kZXNpemUxMjMgPj0gMTAgKSwgY2hlY2tfb3ZlcmxhcCA9IFQsIHJlcGVsICA9IEYsIHNpemUgPSA1KSsKICBsYWJzKHRpdGxlID0gcGFzdGUwKCJXYXZlcyAiLCAiIikpCgpgYGAKCml0IGlzIGFsc28gcG9zc2libGUgdG8gZml4IHRoZSBub2RlcywgYW5kIGRpc3BsYXkgdGhlIHdhdmVzIHdpdGggYSByZWd1bGFyIGZhY2V0IGdyaWQgKF9mYWNldF9lZGdlc18pCgpgYGB7ciBmYWNldCBncmlkfQoKbm9kZXNpemUxMjMgPC0gZGVncmVlKHcxMjMgLG1vZGUgPSJpbiIpClYodzEyMykkbnNpemUgPC0gbm9kZXNpemUxMjMKCgpnZ3JhcGgodzEyMywgbGF5b3V0ID0gImZyIikrCiAgZ2VvbV9lZGdlX2xpbmswKGVkZ2Vfd2lkdGggPSAwLjIpICsKICBnZW9tX25vZGVfcG9pbnQoc2hhcGU9MjEsIGFlcyhmaWxsID0gcHJvZ3JhbSwgc2l6ZSA9IG5zaXplLCBhbHBoYSA9IGdwYSkpKwogIHRoZW1lX2dyYXBoKCkrCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpKwogICAgICAgZ2VvbV9ub2RlX3RleHQoYWVzKGxhYmVsPSBuYW1lLCBmaWx0ZXIgID0gZ3BhLm9taXQgPT0gNiksIGNoZWNrX292ZXJsYXAgPSBULCByZXBlbCAgPSBGLCBzaXplID0gNSkrCiAgbGFicyh0aXRsZSA9IHBhc3RlMCgiV2F2ZXMgIiwgIiIpKSsKICBmYWNldF9lZGdlcyh+IHdhdmUpCgpgYGAKCgoKYGBge3Igd2F2ZXN9CiMgZy53YXZlcyA8LSBjcmVhdGVfbGF5b3V0KGdyYXBoID0gdzEyMywgbGF5b3V0ID0gJ2ZyJykKIyAKIyAjIHJlbW92ZShncGEpCiMgIyBWKHcxMjMpJGdwYQojIAojIGZpcnRzIDwtIGdncmFwaChnLndhdmVzKSsKIyAgIGdlb21fZWRnZV9saW5rMChlZGdlX3dpZHRoID0gMC4yLCBhZXMoIGZpbHRlciA9IHdhdmUgPT0gMSApKSArCiMgICBnZW9tX25vZGVfcG9pbnQoc2hhcGU9ICJjaXJjbGUiLCBhZXMoc2l6ZSA9IG5vZGVzaXplMTIzLCBjb2xvciA9IHByb2dyYW0pKSsKIyAgIHRoZW1lX2dyYXBoKCkrCiMgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikrCiMgICAgICAgIGdlb21fbm9kZV90ZXh0KGFlcyhsYWJlbD0gbmFtZSwgYWxwaGEgPSBncGEgKSwgY2hlY2tfb3ZlcmxhcCA9IFQsIHJlcGVsICA9IEYsIHNpemUgPSA1KSsKIyAgIGxhYnModGl0bGUgPSBwYXN0ZTAoIldhdmUgIiwgMSkpCiMgCiMgc2Vjb25kIDwtIGdncmFwaChnLndhdmVzKSsKIyAgIGdlb21fZWRnZV9saW5rMChlZGdlX3dpZHRoID0gMC4yLCBhZXMoIGZpbHRlciA9IHdhdmUgPT0gMiApKSArCiMgICBnZW9tX25vZGVfcG9pbnQoc2hhcGU9ICJjaXJjbGUiLCBhZXMoc2l6ZSA9IG5vZGVzaXplMTIzLCBjb2xvciA9IHByb2dyYW0pKSsKIyAgIHRoZW1lX2dyYXBoKCkrCiMgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikrCiMgICAgICAgIGdlb21fbm9kZV90ZXh0KGFlcyhsYWJlbD0gbmFtZSwgYWxwaGEgPSBncGEgKSwgY2hlY2tfb3ZlcmxhcCA9IFQsIHJlcGVsICA9IEYsIHNpemUgPSA1KSsKIyAgIGxhYnModGl0bGUgPSBwYXN0ZTAoIldhdmUgIiwgMikpCiMgCiMgdGhpcmQgPC0gZ2dyYXBoKGcud2F2ZXMpKwojICAgZ2VvbV9lZGdlX2xpbmswKGVkZ2Vfd2lkdGggPSAwLjIsIGFlcyggZmlsdGVyID0gd2F2ZSA9PSAzICkpICsKIyAgIGdlb21fbm9kZV9wb2ludChzaGFwZT0gImNpcmNsZSIsIGFlcyhzaXplID0gbm9kZXNpemUxMjMsIGNvbG9yID0gcHJvZ3JhbSkpKwojICAgdGhlbWVfZ3JhcGgoKSsKIyAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSsKIyAgICAgICAgZ2VvbV9ub2RlX3RleHQoYWVzKGxhYmVsPSBuYW1lKSwgY2hlY2tfb3ZlcmxhcCA9IFQsIHJlcGVsICA9IEYsIHNpemUgPSA1KSsKIyAgIGxhYnModGl0bGUgPSBwYXN0ZTAoIldhdmUgIiwgMykpCiMgCiMgCiMgcGxvdF9ncmlkKGZpcnRzLCBzZWNvbmQsIHRoaXJkLCBuY29sID0gMykKYGBgCgojIyBhbmltYXRpb24/PwoKU29tZXRpbWVzIGlmIHNvbWV0aGluZyBtb3ZlcywgaXTigJlzIGJldHRlciB0byBzaG93IHRoYXQgc29tZXRoaW5nIGlzIG1vdmluZy4KCkFuaW1hdGlvbiBjYW4gaGFyZGx5IGJlIHZlcnkgdW5kZXJzdGFuZGFibGUsIGJ1dCBpdCBjYW4gYmUgdmVyeSBzcGVjdGFjdWxhci4KCmBgYHtyIGFuaW1hdGlvbn0KbGlicmFyeShnZ2FuaW1hdGUpCmxpYnJhcnkoZ2lmc2tpKQoKZ3JhcGggPC0gZ2dyYXBoKHcxMjMsIGxheW91dCA9ICJmciIpKwogIGdlb21fZWRnZV9saW5rMChlZGdlX3dpZHRoID0gMC4yLCBhZXMoY29sb3IgPSBhcy5mYWN0b3Iod2F2ZSkpKSArCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJTZXQzIikrCiAgZ2VvbV9ub2RlX3BvaW50KHNoYXBlPTIxLCBhZXMoc2l6ZSA9IG5vZGVzaXplMTIzLCBmaWxsID0gcHJvZ3JhbSkpKwogIHRoZW1lX2dyYXBoKCkrCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpKwogICAgICAgZ2VvbV9ub2RlX3RleHQoYWVzKGxhYmVsPSBuYW1lLCBmaWx0ZXIgPSAgbm9kZXNpemUxMjMgPj0gMTAgKSwgY2hlY2tfb3ZlcmxhcCA9IFQsIHJlcGVsICA9IEYsIHNpemUgPSA1KSsKICBsYWJzKHRpdGxlID0gcGFzdGUwKCJXYXZlcyAiLCAiIikpCgpwIDwtIGdyYXBoICsKICBsYWJzKHN1YnRpdGxlID0gIkZyaWVuZHNoaXAgaW4gd2F2ZSB7dHJ1bmMoZnJhbWVfdGltZSl9IikgKwogIHRyYW5zaXRpb25fdGltZSh3YXZlKSArCiAgZWFzZV9hZXMoImV4cG9uZW50aWFsLWluLW91dCIpCgojID90cmFuc2l0aW9uX3RpbWUKIyA/c2hhZG93X3dha2UKCiNhbmltYXRlKHAsIGZwcyA9IDI1LCBkdXJhdGlvbiA9IDgqMywgd2lkdGggPSAxMDAwLCBoZWlnaHQgPSA4MDApIApgYGAKCkFkZCB0aGUgYXR0ZW51YXRpb24gYW5kIGV4dGVuZCB0aGUgbGFzdCBmcmFtZSBhIGJpdCBzbyB0aGF0IGV2ZXJ5dGhpbmcgZG9lc24ndCBicmVhayBvZmYgdmVyeSBxdWlja2x5LgoKYGBge3IgYW5pbWF0aW9uIHdpdCBhIGZhZGV9CnAgPC0gZ3JhcGggKwogIGxhYnMoc3VidGl0bGUgPSAiRnJpZW5kc2hpcCBpbiB3YXZlIHt0cnVuYyhmcmFtZV90aW1lKX0iKSArCiAgdHJhbnNpdGlvbl90aW1lKHdhdmUpICsKICBlYXNlX2FlcygiZXhwb25lbnRpYWwtaW4tb3V0IikrCiAgZW50ZXJfZmFkZSgpICsKICBleGl0X2ZhZGUoKQoKYW5pbWF0ZShwLGVuZF9wYXVzZSA9IDIwKQpgYGAKCiMjIEludGVyYWN0aXZlIHBsb3RzCgp2aXNOZXR3b3JrIGlzIGEgbGlicmFyeSBmb3IgUiBmb3IgaW50ZXJhY3RpdmUgbmV0d29yayB2aXN1YWxpemF0aW9uCmhvd2V2ZXIsIHRoZSBzeW50YXggaXMgbm90IHRoZSBlYXNpZXN0LCBhbmQgYmFzZWQgb24gbGlzdHMuIApIT1dFVkVSLCBpdCBjb3VsZCBwbG90IGlncmFwaCBvYmplY3RzIGRpcmVjdGx5CgpgYGB7ciBpbnRlcmFjdGl2ZX0KbGlicmFyeSh2aXNOZXR3b3JrKQoKVih3MV9mdWxsKSRzaXplIDwtIGcxZGVncmVlICoyCgpsaWJyYXJ5KFJDb2xvckJyZXdlcikKbWFya2VyID0gbGlzdChjb2xvciA9IGJyZXdlci5wYWwobmxldmVscyhhcy5mYWN0b3IoVih3MV9mdWxsKSRwcm9ncmFtKSksICJTZXQzIikpCgplcCA8LSAgZGF0YS5mcmFtZShwcm9ncmFtID0gVih3MV9mdWxsKSRwcm9ncmFtKQplcF9jIDwtIGRhdGEuZnJhbWUocHJvZ3JhbSA9IHVuaXF1ZShlcCksIGNvbG9yID0gbWFya2VyJGNvbG9yKQplcCA8LSBsZWZ0X2pvaW4oZXAsIGVwX2MpCgpWKHcxX2Z1bGwpJGNvbG9yIDwtIGFzLmNoYXJhY3RlcihlcCRjb2xvcikKCgojIGdldCBkYXRhIGFuZCBwbG90IDoKIyBkYXRhIDwtIHRvVmlzTmV0d29ya0RhdGEodzFfZnVsbCkKCiMgdmlzTmV0d29yayhub2RlcyA9IGRhdGEkbm9kZXMsIGVkZ2VzID0gZGF0YSRlZGdlcykgJT4lIAojICAgdmlzTm9kZXMoc2l6ZSA9IFYodzFfZnVsbCkkbnNpemUsIGNvbG9yID0gVih3MV9mdWxsKSRwcm9ncmFtKSAlPiUKIyAgIHZpc09wdGlvbnMoaGlnaGxpZ2h0TmVhcmVzdCA9IGxpc3QoZW5hYmxlZCA9IFQsIGhvdmVyID0gVCksIAojICAgICAgICAgICAgICBub2Rlc0lkU2VsZWN0aW9uID0gVCkKCiMgbWF5IHRha2UgYSBsb3Qgb2YgdGltZSwgYnV0IHdpbGwgYWxsb3cgY2hhbmdpbmcgcGFyYW1ldGVyIHZhbHVlcyB0aHJvdWdoIHBhY2thZ2Ugc3ludGF4CmBgYAoKYGBge3IgZGlyZWN0bHl9Cgp2aXNJZ3JhcGgodzFfZnVsbCklPiUgCiAgdmlzTm9kZXMoKSAlPiUKICB2aXNPcHRpb25zKGhpZ2hsaWdodE5lYXJlc3QgPSBsaXN0KGVuYWJsZWQgPSBULCBob3ZlciA9IFQpLCAKICAgICAgICAgICAgIG5vZGVzSWRTZWxlY3Rpb24gPSBUKQoKIyBmYXN0ZXIsIHdoeT8KYGBgCgo=