Creating the environment

This template is based in this paper

https://revistas.ucm.es/index.php/REVE/article/view/75566/4564456557467

For a detail explanation of how to use it, please watch this video

https://www.youtube.com/watch?v=jtKSifvNvTM

Data getting

wos_scopus_tos <- 
  tosr::tosr_load("data/software_model_check.bib", 
                  "data/software_model_check.txt")

tree_of_science <- 
  tosr::tosR("data/software_model_check.bib", 
                  "data/software_model_check.txt")

wos <- 
  bibliometrix::convert2df("data/software_model_check.txt")  # create dataframe from wos file

scopus <- 
  bibliometrix::convert2df("data/software_model_check.bib", # Create dataframe from scopus file
                           dbsource = "scopus", 
                           format = "bibtex")

Table 1. Search Criteria

table_1 <- 
  tibble(wos = length(wos$SR), # Create a dataframe with the values.
         scopus = length(scopus$SR), 
         total = length(wos_scopus_tos$df$SR))
table_1

Figure 1. Languages

main_languages <- 
  wos_scopus_tos$df |> 
  select(LA) |> 
  separate_rows(LA, sep = "; ") |> 
  count(LA, sort = TRUE) |> 
  slice(1:5)

other_languages <- 
  wos_scopus_tos$df |> 
  separate_rows(LA, sep = "; ") |> 
  select(LA) |> 
  count(LA, sort = TRUE) |> 
  slice(6:n) |> 
  summarise(n = sum(n)) |> 
  mutate(LA = "OTHERS") |> 
  select(LA, n)
Warning in 6:n :
  numerical expression has 5 elements: only the first used
languages <- 
  main_languages |> 
  bind_rows(other_languages) |> 
  mutate(percentage = n / sum(n),
         percentage = round(percentage, 
                            digits = 2) ) |> 
  rename(language = LA) |>
  select(language, percentage, count = n)

languages
df <- languages |> 
  rename(value = percentage, group = language) |>
  mutate(value = value * 100) |> 
  select(value, group)

df2 <- df %>% 
  mutate(csum = rev(cumsum(rev(value))), 
         pos = value/2 + lead(csum, 1),
         pos = if_else(is.na(pos), value/2, pos))

ggplot(df, aes(x = 2 , y = value, fill = fct_inorder(group))) +
  geom_col(width = 1, color = 1) +
  coord_polar(theta = "y") +
  geom_label_repel(data = df2,
                   aes(y = pos, label = paste0(value, "%")),
                   size = 4.5, nudge_x = 1, show.legend = FALSE) +
  theme(panel.background = element_blank(),
        axis.line = element_blank(), 
        axis.text = element_blank(),
        axis.ticks = element_blank(),
        axis.title = element_blank(),
        plot.title = element_text(hjust = 0.5, size = 18)) +
  labs(title = "Languages") +
  guides(fill = guide_legend(title = "")) +
  theme_void() +
  xlim(0.5, 2.5)

Figure 2. Scientific Production

wos_anual_production <- 
  wos |> 
  select(PY) |> 
  count(PY, sort = TRUE) |> 
  na.omit() |> 
  filter(PY >= 2000,
         PY < year(today())) |> 
  mutate(ref_type = "wos")

scopus_anual_production  <- 
  scopus |> 
  select(PY) |> 
  count(PY, sort = TRUE) |> 
  na.omit() |> 
  filter(PY >= 2000,
         PY < year(today())) |>
  mutate(ref_type = "scopus")

total_anual_production <- 
  wos_scopus_tos$df |> 
  select(PY) |> 
  count(PY, sort = TRUE) |> 
  na.omit() |> 
  filter(PY >= 2000,
         PY < year(today())) |>
  mutate(ref_type = "total")

wos_scopus_total_annual_production <- 
  wos_anual_production |> 
  bind_rows(scopus_anual_production,
            total_anual_production) 

figure_2_data <- 
  wos_scopus_total_annual_production |> 
  mutate(PY = replace_na(PY, replace = 0)) |> 
  pivot_wider(names_from = ref_type, 
              values_from = n) |> 
  arrange(desc(PY))

figure_2_data 
wos_scopus_total_annual_production |> 
  ggplot(aes(x = PY, y = n, color = ref_type)) +
  geom_line() +
  labs(title = "Annual Scientific Production", 
       x = "years",
       y = "papers") +
  theme(plot.title = element_text(hjust = 0.5)) 

Table 2. Country production

data_biblio_wos <- biblioAnalysis(wos)

wos_country <- 
  data_biblio_wos$Countries |> 
  data.frame() |> 
  mutate(database = "wos") |> 
  select(country = Tab, papers = Freq, database ) |> 
  arrange(desc(papers)) 

data_biblio_scopus <- biblioAnalysis(scopus)

scopus_country <- 
  data_biblio_scopus$Countries |> 
  data.frame() |> 
  mutate(database = "scopus") |> 
  select(country = Tab, papers = Freq, database ) |> 
  arrange(desc(papers)) 

data_biblio_total <- biblioAnalysis(wos_scopus_tos$df)

total_country <- 
  data_biblio_total$Countries |> 
  data.frame() |> 
  mutate(database = "total") |> 
  select(country = Tab, papers = Freq, database ) |> 
  arrange(desc(papers)) 

wos_scopus_total_country <- 
  wos_country |> 
  bind_rows(scopus_country, 
            total_country) |> 
  mutate(country = as.character(country)) |> 
  pivot_wider(names_from = database, 
              values_from = papers) |> 
  arrange(desc(total)) |> 
  slice(1:10) |> 
  mutate(percentage = total / (table_1 |> pull(total)),
         percentage = round(percentage, digits = 2))

wos_scopus_total_country

Table 3. Author production

wos_authors <- 
  data_biblio_wos$Authors |> 
  data.frame() |> 
  rename(authors_wos = AU, papers_wos = Freq) |> 
  arrange(desc(papers_wos)) |> 
  slice(1:10) |> 
  mutate(database_wos = "wos")


scopus_authors <- 
  data_biblio_scopus$Authors |> 
  data.frame() |> 
  rename(authors_scopus = AU, papers_scopus = Freq) |> 
  arrange(desc(papers_scopus)) |> 
  slice(1:10) |> 
  mutate(database_scopus = "scopus")

total_authors <- 
  data_biblio_total$Authors |> 
  data.frame() |> 
  rename(authors_total = AU, 
         papers_total = Freq) |> 
  arrange(desc(papers_total)) |> 
  slice(1:10) |> 
  mutate(database_total = "total")

wos_scopus_authors <- 
  wos_authors |> 
  bind_cols(scopus_authors,
            total_authors)

wos_scopus_authors

Table 4. Journal production

wos_journal <- 
  wos |> 
  select(journal = SO) |> 
  na.omit() |> 
  count(journal, sort = TRUE) |> 
  slice(1:20) |> 
  rename(publications = n) |> 
  mutate(database = "wos")

scopus_journal <- 
  scopus |> 
  select(journal = SO) |> 
  na.omit() |> 
  count(journal, sort = TRUE) |> 
  slice(1:20) |> 
  rename(publications = n) |> 
  mutate(database = "scopus")

total_journal <- 
  wos_scopus_tos$df |> 
  select(journal = SO) |> 
  na.omit() |> 
  count(journal, sort = TRUE) |> 
  slice(1:20) |> 
  rename(publications = n) |> 
  mutate(database = "total")

wos_scopus_total_journal <- 
  wos_journal |> 
  bind_rows(scopus_journal, 
            total_journal) |> 
  pivot_wider(names_from = database, 
              values_from = publications) |> 
  arrange(desc(total)) |> 
  slice(1:10) |> 
  mutate(percentage = total / table_1 |> pull(total),
         percentage = round(percentage, digits = 2))


wos_scopus_total_journal

Figure 3. Co-citation network

Author co-citation network

wos_scopus_author_metatag <- 
  metaTagExtraction(wos_scopus_tos$df, Field = "CR_AU")

wos_scopus_author_co_citation_matrix <- 
  biblioNetwork(M = wos_scopus_author_metatag, 
                analysis = "co-citation", 
                network = "authors")

aca_tbl_graph <- 
  graph_from_adjacency_matrix(wos_scopus_author_co_citation_matrix , 
                              mode = "undirected", 
                              weighted = TRUE, 
                              diag = FALSE) |> 
  as_tbl_graph(aca_igraph, directed = FALSE ) |> 
  activate(nodes) |> 
  mutate(degree = centrality_degree()) |> 
  arrange(desc(degree)) |> 
  slice(1:30)

weight_tbl <- 
  aca_tbl_graph |> 
  activate(edges) |> 
  select(weight) |> 
  as.data.frame()

threshold <- 
  quantile(weight_tbl |> 
             select(weight) |> 
             pull(), 
           probs = 0.80)

aca_tbl_graph_filtered <- 
  aca_tbl_graph |> 
  activate(edges) |> 
  filter(weight >= threshold) |> 
  activate(nodes) |> 
  mutate(components = group_components(type = "weak")) |> 
  filter(components == 1) |> 
  mutate(degree = centrality_degree(),
         community = as.factor(group_louvain()) )

aca_tbl_graph_filtered |> 
  ggraph(layout = "kk") + 
  geom_edge_link(alpha = .25, 
                 aes(width = weight)) +
  geom_node_point(aes(colour = community, 
                      size = degree)) +
  geom_node_text(aes(label = name), repel = TRUE) +
  theme_graph()

Author Collaboration network

wos_scopus_author_collab_matrix <- 
  biblioNetwork(M = wos_scopus_tos$df, 
                analysis = "collaboration", 
                network = "authors")

plot_author_collab <- 
  networkPlot(NetMatrix = wos_scopus_author_collab_matrix, 
              weighted=T, n = 30, 
              Title = "Author Collaboration Network", 
              type = "fruchterman", 
              size=T,
              edgesize = 5,
              labelsize=0.7)


author_collab_tbl_graph <- 
  graph_from_adjacency_matrix(wos_scopus_author_collab_matrix , 
                              mode = "undirected", 
                              weighted = TRUE, 
                              diag = FALSE) |> 
  as_tbl_graph(aca_igraph, directed = FALSE ) |> 
  activate(nodes) |> 
  mutate(degree = centrality_degree()) |> 
  arrange(desc(degree)) |> 
  slice(1:30)

author_collab_tbl_graph_filtered <- 
  author_collab_tbl_graph |> 
  activate(edges) |> 
  filter(weight > 1) |> 
  activate(nodes) |> 
  mutate(components = group_components(type = "weak")) |>
  filter(components == 1) |>
  mutate(degree = centrality_degree(),
         community = as.factor(group_louvain()) )

author_collab_tbl_graph_filtered |> 
  ggraph(layout = "kk") + 
  geom_edge_link(alpha = .25, 
                 aes(width = weight)) +
  geom_node_point(aes(colour = community, 
                      size = degree)) +
  geom_node_text(aes(label = name), repel = TRUE) +
  theme_graph()

Country Collaboration Network

plot_country_collab <- 
  networkPlot(wos_scopus_country_collab_matrix, 
              weighted=T, n = 30, 
              Title = "Country Collaboration Network", 
              type = "fruchterman", 
              size=T,
              edgesize = 5,
              labelsize=0.7)
Warning in min(E(bsk.network)$weight) :
  no non-missing arguments to min; returning Inf
Warning in min(E(bsk.network)$weight) :
  no non-missing arguments to min; returning Inf
Warning in max(E(bsk.network)$weight + min(E(bsk.network)$weight)) :
  no non-missing arguments to max; returning -Inf
Error in if (V(bsk.network)$community[x[1]] == V(bsk.network)$community[x[2]]) { : 
  argument is of length zero

Keyword co-occurrence network

wos_scopus_keyword_co_occurrence_matrix <- 
  biblioNetwork(M = wos_scopus_tos$df, 
                analysis = "co-occurrences", 
                network = "keywords", 
                sep = ";")

plot_net_co_occurrence <- 
  networkPlot(wos_scopus_keyword_co_occurrence_matrix, 
              weighted=T, n = 30, 
              Title = "Keyword Co-occurrence Network", 
              type = "fruchterman", 
              size=T,
              edgesize = 5,
              labelsize=0.7)


keyword_co_occurrence_tbl_graph <- 
  graph_from_adjacency_matrix(wos_scopus_keyword_co_occurrence_matrix , 
                              mode = "undirected", 
                              weighted = TRUE, 
                              diag = FALSE) |> 
  as_tbl_graph(aca_igraph, directed = FALSE ) |> 
  activate(nodes) |> 
  mutate(degree = centrality_degree()) |> 
  arrange(desc(degree)) |> 
  slice(1:30)

keyword_co_occurrence_weight_tbl <- 
  keyword_co_occurrence_tbl_graph |> 
  activate(edges) |> 
  select(weight) |> 
  as.data.frame()

threshold <- 
  quantile(keyword_co_occurrence_weight_tbl |> 
             select(weight) |> 
             pull(), 
           probs = 0.80)

keyword_co_occurrence_tbl_graph_filtered <- 
  keyword_co_occurrence_tbl_graph |> 
  activate(edges) |> 
  filter(weight >= threshold) |> 
  activate(nodes) |> 
  mutate(components = group_components(type = "weak")) |> 
  filter(components == 1) |> 
  mutate(degree = centrality_degree(),
         community = as.factor(group_louvain()) )

keyword_co_occurrence_tbl_graph_filtered |> 
  ggraph(layout = "kk") + 
  geom_edge_link(alpha = .25, 
                 aes(width = weight)) +
  geom_node_point(aes(colour = community, 
                      size = degree)) +
  geom_node_text(aes(label = name), repel = TRUE) +
  theme_graph()

Figure 4. Tree of Science

Tree of Science

tree_of_science

Clustering analysis

Finding the clusters

nodes <-  # Create a dataframe with the fullname of articles 
  tibble(name = V(wos_scopus_tos$graph)$name) |> 
  left_join(wos_scopus_tos$nodes, 
            by = c("name" = "ID_TOS"))

wos_scopus_citation_network_1 <- # Add the article names to the citation network
  wos_scopus_tos$graph |> 
  igraph::set.vertex.attribute(name = "full_name", 
                               index = V(wos_scopus_tos$graph)$name, 
                               value = nodes$CITE)

nodes_1 <- # Create a dataframe with subfields (clusters)
  tibble(name = V(wos_scopus_citation_network_1)$name,
         cluster = V(wos_scopus_citation_network_1)$subfield,
         full_name = V(wos_scopus_citation_network_1)$full_name)

nodes_2 <- # Count the number of articles per cluster
  nodes_1 |> 
  count(cluster, sort = TRUE) |> 
  mutate(cluster_1 = row_number()) |> 
  select(cluster, cluster_1)

nodes_3 <- 
  nodes_1 |> 
  left_join(nodes_2) |> 
  rename(subfield = cluster_1) |> 
  select(name, full_name, subfield)
Joining, by = "cluster"
edge_list <- 
  get.edgelist(wos_scopus_citation_network_1) |> 
  data.frame() |> 
  rename(Source = X1, Target = X2)

wos_scopus_citation_network <- 
  graph.data.frame(d = edge_list, 
                   directed = TRUE, 
                   vertices = nodes_3)

wos_scopus_citation_network |> 
  summary()
IGRAPH 1cbc7cc DN-- 1817 5862 -- 
+ attr: name (v/c), full_name (v/c), subfield (v/n)

Choosing clusters

We proposed the tipping point option to choose the number of clusters. See this paper:

https://www.nature.com/articles/s41598-021-85041-8

clusters <- 
  tibble(cluster = V(wos_scopus_citation_network)$subfield) |> 
  count(cluster, sort = TRUE)

clusters |> 
  ggplot(aes(x = reorder(cluster, n), y = n)) +
  geom_point(size = 3) +
  labs(x = "Clusters", y = "Number of papers") +
  theme(axis.title.x = element_text(size = 16 , 
                                    family =  "Arial"),
        axis.title.y = element_text(size = 16, family = "Arial"),
        axis.text.x = element_text(size = 12, family = "Arial"), 
        axis.text.y = element_text(size = 12, family = "Arial"))

Removing not chosen clusters

wos_scopus_citation_network_clusters <- 
  wos_scopus_citation_network |> 
  delete.vertices(which(V(wos_scopus_citation_network)$subfield != 1 & # filter clusters 
                          V(wos_scopus_citation_network)$subfield != 2 &
                          V(wos_scopus_citation_network)$subfield != 3  &
                          V(wos_scopus_citation_network)$subfield != 4))

wos_scopus_citation_network_clusters |> 
  summary()
IGRAPH 60a01fe DN-- 788 2432 -- 
+ attr: name (v/c), full_name (v/c), subfield (v/n)

Cluster 1

nodes_full_data |> 
  filter(cluster == 1) |> 
  select(full_name) |> 
  mutate(full_name = str_extract(full_name, SPC %R%  # Regular expressions 
                                   one_or_more(WRD) %R% 
                                   SPC %R% 
                                   one_or_more(or(WRD, ANY_CHAR))),
         full_name = str_remove(full_name, OPEN_PAREN %R% 
                                  repeated(DGT, 4) %R% 
                                  CLOSE_PAREN %R%
                                  one_or_more(or(WRD,ANY_CHAR))),
         full_name = str_trim(full_name))  |> 
  unnest_tokens(output = word, input = full_name) |> # Tokenization
  anti_join(stop_words) |>  # Removing stop words
  filter(word != "doi",
         !str_detect(word, "[0-9]")) |>  # WoS data
  filter(word == str_remove(word, pattern = "model"),
         word == str_remove(word, pattern = "software"),  # Words removed
         word == str_remove(word, pattern = "checking"), 
         word == str_remove(word, pattern = "lecture"),
         word == str_remove(word, pattern = "notes"),
         word == str_remove(word, pattern = "management"),
         word == str_remove(word, pattern = "bibliometric"),
         word == str_remove(word, pattern = "review"),
         word == str_remove(word, pattern = "journal")) |>
  count(word, sort = TRUE) |> 
  with(wordcloud(word, 
                 n, 
                 random.order = FALSE, 
                 max.words = 50, 
                 colors=pal))
Joining, by = "word"
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  systems could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  bioinformatics could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  including could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  intelligence could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  programs could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  subseries could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  proceedings could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  programming could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  symmetry could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  generation could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  specification could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  temporal could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  oriented could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  properties could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  specifications could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  automated could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  engineering could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  extensible could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  reduction could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  environment could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  languages could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  symbolic could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  applications could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  symposium could not be fit on page. It will not be plotted.

Cluster 2

nodes_full_data |> 
  filter(cluster == 2) |> 
  select(full_name) |> 
  mutate(full_name = str_extract(full_name, SPC %R%  # Regular expressions 
                                   one_or_more(WRD) %R% 
                                   SPC %R% 
                                   one_or_more(or(WRD, ANY_CHAR))),
         full_name = str_remove(full_name, OPEN_PAREN %R% 
                                  repeated(DGT, 4) %R% 
                                  CLOSE_PAREN %R%
                                  one_or_more(or(WRD,ANY_CHAR))),
         full_name = str_trim(full_name))  |> 
  unnest_tokens(output = word, input = full_name) |> 
  anti_join(stop_words) |>
  filter(word != "doi",
         !str_detect(word, "[0-9]")) |>  # WoS data
  filter(word == str_remove(word, pattern = "model"),
         word == str_remove(word, pattern = "software"),  # Words removed
         word == str_remove(word, pattern = "checking"), 
         word == str_remove(word, pattern = "lecture"),
         word == str_remove(word, pattern = "notes"),
         word == str_remove(word, pattern = "proceedings")) |>
  count(word, sort = TRUE) |> 
  with(wordcloud(word, 
                 n, 
                 random.order = FALSE, 
                 max.words = 50, 
                 colors=pal))
Joining, by = "word"
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  intelligence could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  symposium could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  incremental could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  interpolants could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  interpolation could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  reachability could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  refinement could not be fit on page. It will not be plotted.

Cluster 3


cluster_3 <- 
  wos_scopus_citation_network |> 
  delete.vertices(which(V(wos_scopus_citation_network)$subfield != 3))

cluster_3_page_rank <- 
  cluster_3 |> 
  set.vertex.attribute(name = "page_rank", 
                       value = page_rank(cluster_3)$vector)

cluster_3_df <- 
  tibble(name = V(cluster_3_page_rank)$name,
         full_name = V(cluster_3_page_rank)$full_name,
         page_rank = V(cluster_3_page_rank)$page_rank,
         cluster = V(cluster_3_page_rank)$subfield,)

nodes_full_data |> 
  filter(cluster == 3) |> 
  select(full_name) |> 
  mutate(full_name = str_extract(full_name, SPC %R%  # Regular expressions 
                                   one_or_more(WRD) %R% 
                                   SPC %R% 
                                   one_or_more(or(WRD, ANY_CHAR))),
         full_name = str_remove(full_name, OPEN_PAREN %R% 
                                  repeated(DGT, 4) %R% 
                                  CLOSE_PAREN %R%
                                  one_or_more(or(WRD,ANY_CHAR))),
         full_name = str_trim(full_name))  |> 
  unnest_tokens(output = word, input = full_name) |> 
  anti_join(stop_words) |>
  filter(word != "doi",
         !str_detect(word, "[0-9]")) |>  # WoS data 
 filter(word == str_remove(word, pattern = "model"),
         word == str_remove(word, pattern = "software"),  # Words removed
         word == str_remove(word, pattern = "checking"), 
         word == str_remove(word, pattern = "lecture"),
         word == str_remove(word, pattern = "notes"),
         word == str_remove(word, pattern = "proceedings"),
         word == str_remove(word, pattern = "computer"),
         word == str_remove(word, pattern = "systems"),
         word == str_remove(word, pattern = "systemc"))|>
  count(word, sort = TRUE) |> 
  with(wordcloud(word, 
                 n, 
                 random.order = FALSE, 
                 max.words = 50, 
                 colors=pal))
Joining, by = "word"
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  verification could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  analysis could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  programs could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  conference could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  efficient could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  international could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  testing could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  verifying could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  computing could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  engineering could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  partial could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  predicate could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  smtbased could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  study could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  abstraction could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  abstractions could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  approach could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  automata could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  designs could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  properties could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  smt could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  symbolic could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  tool could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  design could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  embedded could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  memory could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  multithreaded could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  safety could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  control could not be fit on page. It will not be plotted.

Cluster 4


cluster_4 <- 
  wos_scopus_citation_network |> 
  delete.vertices(which(V(wos_scopus_citation_network)$subfield != 4))

cluster_4_page_rank <- 
  cluster_4 |> 
  set.vertex.attribute(name = "page_rank", 
                       value = page_rank(cluster_4)$vector)

cluster_4_df <- 
  tibble(name = V(cluster_4_page_rank)$name,
         full_name = V(cluster_4_page_rank)$full_name,
         page_rank = V(cluster_4_page_rank)$page_rank,
         cluster = V(cluster_4_page_rank)$subfield,)

nodes_full_data |> 
  filter(cluster == 4) |> 
  select(full_name) |> 
  mutate(full_name = str_extract(full_name, SPC %R%  # Regular expressions 
                                   one_or_more(WRD) %R% 
                                   SPC %R% 
                                   one_or_more(or(WRD, ANY_CHAR))),
         full_name = str_remove(full_name, OPEN_PAREN %R% 
                                  repeated(DGT, 4) %R% 
                                  CLOSE_PAREN %R%
                                  one_or_more(or(WRD,ANY_CHAR))),
         full_name = str_trim(full_name))  |> 
  unnest_tokens(output = word, input = full_name) |> 
  anti_join(stop_words) |> 
  filter(word != "doi",
         !str_detect(word, "[0-9]")) |>  # WoS data
 filter(word == str_remove(word, pattern = "model"),
         word == str_remove(word, pattern = "software"),  # Words removed
         word == str_remove(word, pattern = "checking"), 
         word == str_remove(word, pattern = "lecture"),
         word == str_remove(word, pattern = "notes"),
         word == str_remove(word, pattern = "proceedings"),
         word == str_remove(word, pattern = "computer"),
         word == str_remove(word, pattern = "systems"),
         word == str_remove(word, pattern = "systemc")) |>
  count(word, sort = TRUE) |> 
  with(wordcloud(word, 
                 n, 
                 random.order = FALSE, 
                 max.words = 50, 
                 colors=pal))
Joining, by = "word"
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  combining could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  embedded could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  including could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  intelligence could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  journal could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  subseries could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  symbolic could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  parallel could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  probabilistic could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  proving could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  theorem could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  reduction could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  technology could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  automatic could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  cambridge could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  compositional could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  design could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  partial could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  program could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  symposium could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  temporal could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  university could not be fit on page. It will not be plotted.

Exporting files

write.csv(cluster_1_df, "cluster_1.csv") # Exporting cluster 1
write.csv(cluster_2_df, "cluster_2.csv") # Exporting cluster 2
write.csv(cluster_3_df, "cluster_3.csv") # Exporting cluster 3
Error in is.data.frame(x) : object 'cluster_3_df' not found
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCmVkaXRvcl9vcHRpb25zOiAKICBjaHVua19vdXRwdXRfdHlwZTogY29uc29sZQotLS0KCiMgQ3JlYXRpbmcgdGhlIGVudmlyb25tZW50CgpgYGB7ciBlY2hvPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeSh0b3NyKQpsaWJyYXJ5KGJpYmxpb21ldHJpeCkKbGlicmFyeShsdWJyaWRhdGUpCmxpYnJhcnkoaWdyYXBoKQpsaWJyYXJ5KHRpZHl0ZXh0KQpsaWJyYXJ5KHdvcmRjbG91ZCkKbGlicmFyeShyZWJ1cykKbGlicmFyeShnZ3JlcGVsKSAjIGltcHJvdmUgZG9udXQgdmlzdWFsaXphdGlvbgpsaWJyYXJ5KGdncmFwaCkKbGlicmFyeSh2aXNOZXR3b3JrKSAKbGlicmFyeSh0aWR5Z3JhcGgpCmBgYAoKVGhpcyB0ZW1wbGF0ZSBpcyBiYXNlZCBpbiB0aGlzIHBhcGVyCgpodHRwczovL3JldmlzdGFzLnVjbS5lcy9pbmRleC5waHAvUkVWRS9hcnRpY2xlL3ZpZXcvNzU1NjYvNDU2NDQ1NjU1NzQ2NwoKRm9yIGEgZGV0YWlsIGV4cGxhbmF0aW9uIG9mIGhvdyB0byB1c2UgaXQsIHBsZWFzZSB3YXRjaCB0aGlzIHZpZGVvIAoKaHR0cHM6Ly93d3cueW91dHViZS5jb20vd2F0Y2g/dj1qdEtTaWZ2TnZUTQoKIyBEYXRhIGdldHRpbmcKCmBgYHtyfQp3b3Nfc2NvcHVzX3RvcyA8LSAKICB0b3NyOjp0b3NyX2xvYWQoImRhdGEvc29mdHdhcmVfbW9kZWxfY2hlY2suYmliIiwgCiAgICAgICAgICAgICAgICAgICJkYXRhL3NvZnR3YXJlX21vZGVsX2NoZWNrLnR4dCIpCgp0cmVlX29mX3NjaWVuY2UgPC0gCiAgdG9zcjo6dG9zUigiZGF0YS9zb2Z0d2FyZV9tb2RlbF9jaGVjay5iaWIiLCAKICAgICAgICAgICAgICAgICAgImRhdGEvc29mdHdhcmVfbW9kZWxfY2hlY2sudHh0IikKCndvcyA8LSAKICBiaWJsaW9tZXRyaXg6OmNvbnZlcnQyZGYoImRhdGEvc29mdHdhcmVfbW9kZWxfY2hlY2sudHh0IikgICMgY3JlYXRlIGRhdGFmcmFtZSBmcm9tIHdvcyBmaWxlCgpzY29wdXMgPC0gCiAgYmlibGlvbWV0cml4Ojpjb252ZXJ0MmRmKCJkYXRhL3NvZnR3YXJlX21vZGVsX2NoZWNrLmJpYiIsICMgQ3JlYXRlIGRhdGFmcmFtZSBmcm9tIHNjb3B1cyBmaWxlCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGRic291cmNlID0gInNjb3B1cyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICBmb3JtYXQgPSAiYmlidGV4IikKYGBgCgojIyBUYWJsZSAxLiBTZWFyY2ggQ3JpdGVyaWEKCmBgYHtyfQp0YWJsZV8xIDwtIAogIHRpYmJsZSh3b3MgPSBsZW5ndGgod29zJFNSKSwgIyBDcmVhdGUgYSBkYXRhZnJhbWUgd2l0aCB0aGUgdmFsdWVzLgogICAgICAgICBzY29wdXMgPSBsZW5ndGgoc2NvcHVzJFNSKSwgCiAgICAgICAgIHRvdGFsID0gbGVuZ3RoKHdvc19zY29wdXNfdG9zJGRmJFNSKSkKdGFibGVfMQpgYGAKCiMjIEZpZ3VyZSAxLiBMYW5ndWFnZXMKCmBgYHtyfQptYWluX2xhbmd1YWdlcyA8LSAKICB3b3Nfc2NvcHVzX3RvcyRkZiB8PiAKICBzZWxlY3QoTEEpIHw+IAogIHNlcGFyYXRlX3Jvd3MoTEEsIHNlcCA9ICI7ICIpIHw+IAogIGNvdW50KExBLCBzb3J0ID0gVFJVRSkgfD4gCiAgc2xpY2UoMTo1KQoKb3RoZXJfbGFuZ3VhZ2VzIDwtIAogIHdvc19zY29wdXNfdG9zJGRmIHw+IAogIHNlcGFyYXRlX3Jvd3MoTEEsIHNlcCA9ICI7ICIpIHw+IAogIHNlbGVjdChMQSkgfD4gCiAgY291bnQoTEEsIHNvcnQgPSBUUlVFKSB8PiAKICBzbGljZSg2Om4pIHw+IAogIHN1bW1hcmlzZShuID0gc3VtKG4pKSB8PiAKICBtdXRhdGUoTEEgPSAiT1RIRVJTIikgfD4gCiAgc2VsZWN0KExBLCBuKQoKbGFuZ3VhZ2VzIDwtIAogIG1haW5fbGFuZ3VhZ2VzIHw+IAogIGJpbmRfcm93cyhvdGhlcl9sYW5ndWFnZXMpIHw+IAogIG11dGF0ZShwZXJjZW50YWdlID0gbiAvIHN1bShuKSwKICAgICAgICAgcGVyY2VudGFnZSA9IHJvdW5kKHBlcmNlbnRhZ2UsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgZGlnaXRzID0gMikgKSB8PiAKICByZW5hbWUobGFuZ3VhZ2UgPSBMQSkgfD4KICBzZWxlY3QobGFuZ3VhZ2UsIHBlcmNlbnRhZ2UsIGNvdW50ID0gbikKCmxhbmd1YWdlcwpgYGAKCgpgYGB7cn0KZGYgPC0gbGFuZ3VhZ2VzIHw+IAogIHJlbmFtZSh2YWx1ZSA9IHBlcmNlbnRhZ2UsIGdyb3VwID0gbGFuZ3VhZ2UpIHw+CiAgbXV0YXRlKHZhbHVlID0gdmFsdWUgKiAxMDApIHw+IAogIHNlbGVjdCh2YWx1ZSwgZ3JvdXApCgpkZjIgPC0gZGYgJT4lIAogIG11dGF0ZShjc3VtID0gcmV2KGN1bXN1bShyZXYodmFsdWUpKSksIAogICAgICAgICBwb3MgPSB2YWx1ZS8yICsgbGVhZChjc3VtLCAxKSwKICAgICAgICAgcG9zID0gaWZfZWxzZShpcy5uYShwb3MpLCB2YWx1ZS8yLCBwb3MpKQoKZ2dwbG90KGRmLCBhZXMoeCA9IDIgLCB5ID0gdmFsdWUsIGZpbGwgPSBmY3RfaW5vcmRlcihncm91cCkpKSArCiAgZ2VvbV9jb2wod2lkdGggPSAxLCBjb2xvciA9IDEpICsKICBjb29yZF9wb2xhcih0aGV0YSA9ICJ5IikgKwogIGdlb21fbGFiZWxfcmVwZWwoZGF0YSA9IGRmMiwKICAgICAgICAgICAgICAgICAgIGFlcyh5ID0gcG9zLCBsYWJlbCA9IHBhc3RlMCh2YWx1ZSwgIiUiKSksCiAgICAgICAgICAgICAgICAgICBzaXplID0gNC41LCBudWRnZV94ID0gMSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogIHRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICBheGlzLnRleHQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIHNpemUgPSAxOCkpICsKICBsYWJzKHRpdGxlID0gIkxhbmd1YWdlcyIpICsKICBndWlkZXMoZmlsbCA9IGd1aWRlX2xlZ2VuZCh0aXRsZSA9ICIiKSkgKwogIHRoZW1lX3ZvaWQoKSArCiAgeGxpbSgwLjUsIDIuNSkKYGBgCgojIyBGaWd1cmUgMi4gU2NpZW50aWZpYyBQcm9kdWN0aW9uCgpgYGB7cn0Kd29zX2FudWFsX3Byb2R1Y3Rpb24gPC0gCiAgd29zIHw+IAogIHNlbGVjdChQWSkgfD4gCiAgY291bnQoUFksIHNvcnQgPSBUUlVFKSB8PiAKICBuYS5vbWl0KCkgfD4gCiAgZmlsdGVyKFBZID49IDIwMDAsCiAgICAgICAgIFBZIDwgeWVhcih0b2RheSgpKSkgfD4gCiAgbXV0YXRlKHJlZl90eXBlID0gIndvcyIpCgpzY29wdXNfYW51YWxfcHJvZHVjdGlvbiAgPC0gCiAgc2NvcHVzIHw+IAogIHNlbGVjdChQWSkgfD4gCiAgY291bnQoUFksIHNvcnQgPSBUUlVFKSB8PiAKICBuYS5vbWl0KCkgfD4gCiAgZmlsdGVyKFBZID49IDIwMDAsCiAgICAgICAgIFBZIDwgeWVhcih0b2RheSgpKSkgfD4KICBtdXRhdGUocmVmX3R5cGUgPSAic2NvcHVzIikKCnRvdGFsX2FudWFsX3Byb2R1Y3Rpb24gPC0gCiAgd29zX3Njb3B1c190b3MkZGYgfD4gCiAgc2VsZWN0KFBZKSB8PiAKICBjb3VudChQWSwgc29ydCA9IFRSVUUpIHw+IAogIG5hLm9taXQoKSB8PiAKICBmaWx0ZXIoUFkgPj0gMjAwMCwKICAgICAgICAgUFkgPCB5ZWFyKHRvZGF5KCkpKSB8PgogIG11dGF0ZShyZWZfdHlwZSA9ICJ0b3RhbCIpCgp3b3Nfc2NvcHVzX3RvdGFsX2FubnVhbF9wcm9kdWN0aW9uIDwtIAogIHdvc19hbnVhbF9wcm9kdWN0aW9uIHw+IAogIGJpbmRfcm93cyhzY29wdXNfYW51YWxfcHJvZHVjdGlvbiwKICAgICAgICAgICAgdG90YWxfYW51YWxfcHJvZHVjdGlvbikgCgpmaWd1cmVfMl9kYXRhIDwtIAogIHdvc19zY29wdXNfdG90YWxfYW5udWFsX3Byb2R1Y3Rpb24gfD4gCiAgbXV0YXRlKFBZID0gcmVwbGFjZV9uYShQWSwgcmVwbGFjZSA9IDApKSB8PiAKICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gcmVmX3R5cGUsIAogICAgICAgICAgICAgIHZhbHVlc19mcm9tID0gbikgfD4gCiAgYXJyYW5nZShkZXNjKFBZKSkKCmZpZ3VyZV8yX2RhdGEgCmBgYAoKYGBge3J9Cndvc19zY29wdXNfdG90YWxfYW5udWFsX3Byb2R1Y3Rpb24gfD4gCiAgZ2dwbG90KGFlcyh4ID0gUFksIHkgPSBuLCBjb2xvciA9IHJlZl90eXBlKSkgKwogIGdlb21fbGluZSgpICsKICBsYWJzKHRpdGxlID0gIkFubnVhbCBTY2llbnRpZmljIFByb2R1Y3Rpb24iLCAKICAgICAgIHggPSAieWVhcnMiLAogICAgICAgeSA9ICJwYXBlcnMiKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpIApgYGAKCiMjIFRhYmxlIDIuIENvdW50cnkgcHJvZHVjdGlvbgoKYGBge3J9CmRhdGFfYmlibGlvX3dvcyA8LSBiaWJsaW9BbmFseXNpcyh3b3MpCgp3b3NfY291bnRyeSA8LSAKICBkYXRhX2JpYmxpb193b3MkQ291bnRyaWVzIHw+IAogIGRhdGEuZnJhbWUoKSB8PiAKICBtdXRhdGUoZGF0YWJhc2UgPSAid29zIikgfD4gCiAgc2VsZWN0KGNvdW50cnkgPSBUYWIsIHBhcGVycyA9IEZyZXEsIGRhdGFiYXNlICkgfD4gCiAgYXJyYW5nZShkZXNjKHBhcGVycykpIAoKZGF0YV9iaWJsaW9fc2NvcHVzIDwtIGJpYmxpb0FuYWx5c2lzKHNjb3B1cykKCnNjb3B1c19jb3VudHJ5IDwtIAogIGRhdGFfYmlibGlvX3Njb3B1cyRDb3VudHJpZXMgfD4gCiAgZGF0YS5mcmFtZSgpIHw+IAogIG11dGF0ZShkYXRhYmFzZSA9ICJzY29wdXMiKSB8PiAKICBzZWxlY3QoY291bnRyeSA9IFRhYiwgcGFwZXJzID0gRnJlcSwgZGF0YWJhc2UgKSB8PiAKICBhcnJhbmdlKGRlc2MocGFwZXJzKSkgCgpkYXRhX2JpYmxpb190b3RhbCA8LSBiaWJsaW9BbmFseXNpcyh3b3Nfc2NvcHVzX3RvcyRkZikKCnRvdGFsX2NvdW50cnkgPC0gCiAgZGF0YV9iaWJsaW9fdG90YWwkQ291bnRyaWVzIHw+IAogIGRhdGEuZnJhbWUoKSB8PiAKICBtdXRhdGUoZGF0YWJhc2UgPSAidG90YWwiKSB8PiAKICBzZWxlY3QoY291bnRyeSA9IFRhYiwgcGFwZXJzID0gRnJlcSwgZGF0YWJhc2UgKSB8PiAKICBhcnJhbmdlKGRlc2MocGFwZXJzKSkgCgp3b3Nfc2NvcHVzX3RvdGFsX2NvdW50cnkgPC0gCiAgd29zX2NvdW50cnkgfD4gCiAgYmluZF9yb3dzKHNjb3B1c19jb3VudHJ5LCAKICAgICAgICAgICAgdG90YWxfY291bnRyeSkgfD4gCiAgbXV0YXRlKGNvdW50cnkgPSBhcy5jaGFyYWN0ZXIoY291bnRyeSkpIHw+IAogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBkYXRhYmFzZSwgCiAgICAgICAgICAgICAgdmFsdWVzX2Zyb20gPSBwYXBlcnMpIHw+IAogIGFycmFuZ2UoZGVzYyh0b3RhbCkpIHw+IAogIHNsaWNlKDE6MTApIHw+IAogIG11dGF0ZShwZXJjZW50YWdlID0gdG90YWwgLyAodGFibGVfMSB8PiBwdWxsKHRvdGFsKSksCiAgICAgICAgIHBlcmNlbnRhZ2UgPSByb3VuZChwZXJjZW50YWdlLCBkaWdpdHMgPSAyKSkKCndvc19zY29wdXNfdG90YWxfY291bnRyeQpgYGAKCiMjIFRhYmxlIDMuIEF1dGhvciBwcm9kdWN0aW9uCgpgYGB7cn0Kd29zX2F1dGhvcnMgPC0gCiAgZGF0YV9iaWJsaW9fd29zJEF1dGhvcnMgfD4gCiAgZGF0YS5mcmFtZSgpIHw+IAogIHJlbmFtZShhdXRob3JzX3dvcyA9IEFVLCBwYXBlcnNfd29zID0gRnJlcSkgfD4gCiAgYXJyYW5nZShkZXNjKHBhcGVyc193b3MpKSB8PiAKICBzbGljZSgxOjEwKSB8PiAKICBtdXRhdGUoZGF0YWJhc2Vfd29zID0gIndvcyIpCgoKc2NvcHVzX2F1dGhvcnMgPC0gCiAgZGF0YV9iaWJsaW9fc2NvcHVzJEF1dGhvcnMgfD4gCiAgZGF0YS5mcmFtZSgpIHw+IAogIHJlbmFtZShhdXRob3JzX3Njb3B1cyA9IEFVLCBwYXBlcnNfc2NvcHVzID0gRnJlcSkgfD4gCiAgYXJyYW5nZShkZXNjKHBhcGVyc19zY29wdXMpKSB8PiAKICBzbGljZSgxOjEwKSB8PiAKICBtdXRhdGUoZGF0YWJhc2Vfc2NvcHVzID0gInNjb3B1cyIpCgp0b3RhbF9hdXRob3JzIDwtIAogIGRhdGFfYmlibGlvX3RvdGFsJEF1dGhvcnMgfD4gCiAgZGF0YS5mcmFtZSgpIHw+IAogIHJlbmFtZShhdXRob3JzX3RvdGFsID0gQVUsIAogICAgICAgICBwYXBlcnNfdG90YWwgPSBGcmVxKSB8PiAKICBhcnJhbmdlKGRlc2MocGFwZXJzX3RvdGFsKSkgfD4gCiAgc2xpY2UoMToxMCkgfD4gCiAgbXV0YXRlKGRhdGFiYXNlX3RvdGFsID0gInRvdGFsIikKCndvc19zY29wdXNfYXV0aG9ycyA8LSAKICB3b3NfYXV0aG9ycyB8PiAKICBiaW5kX2NvbHMoc2NvcHVzX2F1dGhvcnMsCiAgICAgICAgICAgIHRvdGFsX2F1dGhvcnMpCgp3b3Nfc2NvcHVzX2F1dGhvcnMKYGBgCgojIyBUYWJsZSA0LiBKb3VybmFsIHByb2R1Y3Rpb24KCmBgYHtyfQp3b3Nfam91cm5hbCA8LSAKICB3b3MgfD4gCiAgc2VsZWN0KGpvdXJuYWwgPSBTTykgfD4gCiAgbmEub21pdCgpIHw+IAogIGNvdW50KGpvdXJuYWwsIHNvcnQgPSBUUlVFKSB8PiAKICBzbGljZSgxOjIwKSB8PiAKICByZW5hbWUocHVibGljYXRpb25zID0gbikgfD4gCiAgbXV0YXRlKGRhdGFiYXNlID0gIndvcyIpCgpzY29wdXNfam91cm5hbCA8LSAKICBzY29wdXMgfD4gCiAgc2VsZWN0KGpvdXJuYWwgPSBTTykgfD4gCiAgbmEub21pdCgpIHw+IAogIGNvdW50KGpvdXJuYWwsIHNvcnQgPSBUUlVFKSB8PiAKICBzbGljZSgxOjIwKSB8PiAKICByZW5hbWUocHVibGljYXRpb25zID0gbikgfD4gCiAgbXV0YXRlKGRhdGFiYXNlID0gInNjb3B1cyIpCgp0b3RhbF9qb3VybmFsIDwtIAogIHdvc19zY29wdXNfdG9zJGRmIHw+IAogIHNlbGVjdChqb3VybmFsID0gU08pIHw+IAogIG5hLm9taXQoKSB8PiAKICBjb3VudChqb3VybmFsLCBzb3J0ID0gVFJVRSkgfD4gCiAgc2xpY2UoMToyMCkgfD4gCiAgcmVuYW1lKHB1YmxpY2F0aW9ucyA9IG4pIHw+IAogIG11dGF0ZShkYXRhYmFzZSA9ICJ0b3RhbCIpCgp3b3Nfc2NvcHVzX3RvdGFsX2pvdXJuYWwgPC0gCiAgd29zX2pvdXJuYWwgfD4gCiAgYmluZF9yb3dzKHNjb3B1c19qb3VybmFsLCAKICAgICAgICAgICAgdG90YWxfam91cm5hbCkgfD4gCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IGRhdGFiYXNlLCAKICAgICAgICAgICAgICB2YWx1ZXNfZnJvbSA9IHB1YmxpY2F0aW9ucykgfD4gCiAgYXJyYW5nZShkZXNjKHRvdGFsKSkgfD4gCiAgc2xpY2UoMToxMCkgfD4gCiAgbXV0YXRlKHBlcmNlbnRhZ2UgPSB0b3RhbCAvIHRhYmxlXzEgfD4gcHVsbCh0b3RhbCksCiAgICAgICAgIHBlcmNlbnRhZ2UgPSByb3VuZChwZXJjZW50YWdlLCBkaWdpdHMgPSAyKSkKCgp3b3Nfc2NvcHVzX3RvdGFsX2pvdXJuYWwKYGBgCgojIyBGaWd1cmUgMy4gQ28tY2l0YXRpb24gbmV0d29yawoKIyMjIEF1dGhvciBjby1jaXRhdGlvbiBuZXR3b3JrCgpgYGB7cn0Kd29zX3Njb3B1c19hdXRob3JfbWV0YXRhZyA8LSAKICBtZXRhVGFnRXh0cmFjdGlvbih3b3Nfc2NvcHVzX3RvcyRkZiwgRmllbGQgPSAiQ1JfQVUiKQoKd29zX3Njb3B1c19hdXRob3JfY29fY2l0YXRpb25fbWF0cml4IDwtIAogIGJpYmxpb05ldHdvcmsoTSA9IHdvc19zY29wdXNfYXV0aG9yX21ldGF0YWcsIAogICAgICAgICAgICAgICAgYW5hbHlzaXMgPSAiY28tY2l0YXRpb24iLCAKICAgICAgICAgICAgICAgIG5ldHdvcmsgPSAiYXV0aG9ycyIpCgphY2FfdGJsX2dyYXBoIDwtIAogIGdyYXBoX2Zyb21fYWRqYWNlbmN5X21hdHJpeCh3b3Nfc2NvcHVzX2F1dGhvcl9jb19jaXRhdGlvbl9tYXRyaXggLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9kZSA9ICJ1bmRpcmVjdGVkIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHdlaWdodGVkID0gVFJVRSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpYWcgPSBGQUxTRSkgfD4gCiAgYXNfdGJsX2dyYXBoKGFjYV9pZ3JhcGgsIGRpcmVjdGVkID0gRkFMU0UgKSB8PiAKICBhY3RpdmF0ZShub2RlcykgfD4gCiAgbXV0YXRlKGRlZ3JlZSA9IGNlbnRyYWxpdHlfZGVncmVlKCkpIHw+IAogIGFycmFuZ2UoZGVzYyhkZWdyZWUpKSB8PiAKICBzbGljZSgxOjMwKQoKd2VpZ2h0X3RibCA8LSAKICBhY2FfdGJsX2dyYXBoIHw+IAogIGFjdGl2YXRlKGVkZ2VzKSB8PiAKICBzZWxlY3Qod2VpZ2h0KSB8PiAKICBhcy5kYXRhLmZyYW1lKCkKCnRocmVzaG9sZCA8LSAKICBxdWFudGlsZSh3ZWlnaHRfdGJsIHw+IAogICAgICAgICAgICAgc2VsZWN0KHdlaWdodCkgfD4gCiAgICAgICAgICAgICBwdWxsKCksIAogICAgICAgICAgIHByb2JzID0gMC44MCkKCmFjYV90YmxfZ3JhcGhfZmlsdGVyZWQgPC0gCiAgYWNhX3RibF9ncmFwaCB8PiAKICBhY3RpdmF0ZShlZGdlcykgfD4gCiAgZmlsdGVyKHdlaWdodCA+PSB0aHJlc2hvbGQpIHw+IAogIGFjdGl2YXRlKG5vZGVzKSB8PiAKICBtdXRhdGUoY29tcG9uZW50cyA9IGdyb3VwX2NvbXBvbmVudHModHlwZSA9ICJ3ZWFrIikpIHw+IAogIGZpbHRlcihjb21wb25lbnRzID09IDEpIHw+IAogIG11dGF0ZShkZWdyZWUgPSBjZW50cmFsaXR5X2RlZ3JlZSgpLAogICAgICAgICBjb21tdW5pdHkgPSBhcy5mYWN0b3IoZ3JvdXBfbG91dmFpbigpKSApCgphY2FfdGJsX2dyYXBoX2ZpbHRlcmVkIHw+IAogIGdncmFwaChsYXlvdXQgPSAia2siKSArIAogIGdlb21fZWRnZV9saW5rKGFscGhhID0gLjI1LCAKICAgICAgICAgICAgICAgICBhZXMod2lkdGggPSB3ZWlnaHQpKSArCiAgZ2VvbV9ub2RlX3BvaW50KGFlcyhjb2xvdXIgPSBjb21tdW5pdHksIAogICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IGRlZ3JlZSkpICsKICBnZW9tX25vZGVfdGV4dChhZXMobGFiZWwgPSBuYW1lKSwgcmVwZWwgPSBUUlVFKSArCiAgdGhlbWVfZ3JhcGgoKQpgYGAKCiMjIyBBdXRob3IgQ29sbGFib3JhdGlvbiBuZXR3b3JrCgpgYGB7cn0Kd29zX3Njb3B1c19hdXRob3JfY29sbGFiX21hdHJpeCA8LSAKICBiaWJsaW9OZXR3b3JrKE0gPSB3b3Nfc2NvcHVzX3RvcyRkZiwgCiAgICAgICAgICAgICAgICBhbmFseXNpcyA9ICJjb2xsYWJvcmF0aW9uIiwgCiAgICAgICAgICAgICAgICBuZXR3b3JrID0gImF1dGhvcnMiKQoKcGxvdF9hdXRob3JfY29sbGFiIDwtIAogIG5ldHdvcmtQbG90KE5ldE1hdHJpeCA9IHdvc19zY29wdXNfYXV0aG9yX2NvbGxhYl9tYXRyaXgsIAogICAgICAgICAgICAgIHdlaWdodGVkPVQsIG4gPSAzMCwgCiAgICAgICAgICAgICAgVGl0bGUgPSAiQXV0aG9yIENvbGxhYm9yYXRpb24gTmV0d29yayIsIAogICAgICAgICAgICAgIHR5cGUgPSAiZnJ1Y2h0ZXJtYW4iLCAKICAgICAgICAgICAgICBzaXplPVQsCiAgICAgICAgICAgICAgZWRnZXNpemUgPSA1LAogICAgICAgICAgICAgIGxhYmVsc2l6ZT0wLjcpCgphdXRob3JfY29sbGFiX3RibF9ncmFwaCA8LSAKICBncmFwaF9mcm9tX2FkamFjZW5jeV9tYXRyaXgod29zX3Njb3B1c19hdXRob3JfY29sbGFiX21hdHJpeCAsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtb2RlID0gInVuZGlyZWN0ZWQiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgd2VpZ2h0ZWQgPSBUUlVFLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGlhZyA9IEZBTFNFKSB8PiAKICBhc190YmxfZ3JhcGgoYWNhX2lncmFwaCwgZGlyZWN0ZWQgPSBGQUxTRSApIHw+IAogIGFjdGl2YXRlKG5vZGVzKSB8PiAKICBtdXRhdGUoZGVncmVlID0gY2VudHJhbGl0eV9kZWdyZWUoKSkgfD4gCiAgYXJyYW5nZShkZXNjKGRlZ3JlZSkpIHw+IAogIHNsaWNlKDE6MzApCgphdXRob3JfY29sbGFiX3RibF9ncmFwaF9maWx0ZXJlZCA8LSAKICBhdXRob3JfY29sbGFiX3RibF9ncmFwaCB8PiAKICBhY3RpdmF0ZShlZGdlcykgfD4gCiAgZmlsdGVyKHdlaWdodCA+IDEpIHw+IAogIGFjdGl2YXRlKG5vZGVzKSB8PiAKICBtdXRhdGUoY29tcG9uZW50cyA9IGdyb3VwX2NvbXBvbmVudHModHlwZSA9ICJ3ZWFrIikpIHw+CiAgZmlsdGVyKGNvbXBvbmVudHMgPT0gMSkgfD4KICBtdXRhdGUoZGVncmVlID0gY2VudHJhbGl0eV9kZWdyZWUoKSwKICAgICAgICAgY29tbXVuaXR5ID0gYXMuZmFjdG9yKGdyb3VwX2xvdXZhaW4oKSkgKQoKYXV0aG9yX2NvbGxhYl90YmxfZ3JhcGhfZmlsdGVyZWQgfD4gCiAgZ2dyYXBoKGxheW91dCA9ICJrayIpICsgCiAgZ2VvbV9lZGdlX2xpbmsoYWxwaGEgPSAuMjUsIAogICAgICAgICAgICAgICAgIGFlcyh3aWR0aCA9IHdlaWdodCkpICsKICBnZW9tX25vZGVfcG9pbnQoYWVzKGNvbG91ciA9IGNvbW11bml0eSwgCiAgICAgICAgICAgICAgICAgICAgICBzaXplID0gZGVncmVlKSkgKwogIGdlb21fbm9kZV90ZXh0KGFlcyhsYWJlbCA9IG5hbWUpLCByZXBlbCA9IFRSVUUpICsKICB0aGVtZV9ncmFwaCgpCmBgYAoKIyMjIENvdW50cnkgQ29sbGFib3JhdGlvbiBOZXR3b3JrCgpgYGB7cn0KIyB3b3Nfc2NvcHVzX2NvdW50cnlfY29sbGFiX21hdHJpeCA8LSAKIyAgIGJpYmxpb05ldHdvcmsoTSA9IHdvc19zY29wdXNfdG9zJGRmLCAKIyAgICAgICAgICAgICAgICAgYW5hbHlzaXMgPSAiY29sbGFib3JhdGlvbiIsIAojICAgICAgICAgICAgICAgICBuZXR3b3JrID0gImNvdW50cmllcyIpCiMgCiMgcGxvdF9jb3VudHJ5X2NvbGxhYiA8LSAKIyAgIG5ldHdvcmtQbG90KHdvc19zY29wdXNfY291bnRyeV9jb2xsYWJfbWF0cml4LCAKIyAgICAgICAgICAgICAgIHdlaWdodGVkPVQsIG4gPSAzMCwgCiMgICAgICAgICAgICAgICBUaXRsZSA9ICJDb3VudHJ5IENvbGxhYm9yYXRpb24gTmV0d29yayIsIAojICAgICAgICAgICAgICAgdHlwZSA9ICJmcnVjaHRlcm1hbiIsIAojICAgICAgICAgICAgICAgc2l6ZT1ULAojICAgICAgICAgICAgICAgZWRnZXNpemUgPSA1LAojICAgICAgICAgICAgICAgbGFiZWxzaXplPTAuNykKIyAKIyBjb3VudHJ5X2NvbGxhYl90YmxfZ3JhcGggPC0gCiMgICBncmFwaF9mcm9tX2FkamFjZW5jeV9tYXRyaXgod29zX3Njb3B1c19jb3VudHJ5X2NvbGxhYl9tYXRyaXggLCAKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtb2RlID0gInVuZGlyZWN0ZWQiLCAKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB3ZWlnaHRlZCA9IFRSVUUsIAojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpYWcgPSBGQUxTRSkgfD4gCiMgICBhc190YmxfZ3JhcGgoYWNhX2lncmFwaCwgZGlyZWN0ZWQgPSBGQUxTRSApIHw+IAojICAgYWN0aXZhdGUobm9kZXMpIHw+IAojICAgbXV0YXRlKGRlZ3JlZSA9IGNlbnRyYWxpdHlfZGVncmVlKCkpIHw+IAojICAgYXJyYW5nZShkZXNjKGRlZ3JlZSkpIHw+IAojICAgc2xpY2UoMTozMCkKIyAKIyBjb3VudHJ5X2NvbGxhYl90YmxfZ3JhcGhfZmlsdGVyZWQgPC0gCiMgICBjb3VudHJ5X2NvbGxhYl90YmxfZ3JhcGggfD4gCiMgICBhY3RpdmF0ZShub2RlcykgfD4gCiMgICBtdXRhdGUoY29tcG9uZW50cyA9IGdyb3VwX2NvbXBvbmVudHModHlwZSA9ICJ3ZWFrIikpIHw+CiMgICBmaWx0ZXIoY29tcG9uZW50cyA9PSAxKSB8PgojICAgbXV0YXRlKGRlZ3JlZSA9IGNlbnRyYWxpdHlfZGVncmVlKCksCiMgICAgICAgICAgY29tbXVuaXR5ID0gYXMuZmFjdG9yKGdyb3VwX2xvdXZhaW4oKSkgKQojIAojIGNvdW50cnlfY29sbGFiX3RibF9ncmFwaF9maWx0ZXJlZCB8PiAKIyAgIGdncmFwaChsYXlvdXQgPSAia2siKSArIAojICAgZ2VvbV9lZGdlX2xpbmsoYWxwaGEgPSAuMjUsIAojICAgICAgICAgICAgICAgICAgYWVzKHdpZHRoID0gd2VpZ2h0KSkgKwojICAgZ2VvbV9ub2RlX3BvaW50KGFlcyhjb2xvdXIgPSBjb21tdW5pdHksIAojICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gZGVncmVlKSkgKwojICAgZ2VvbV9ub2RlX3RleHQoYWVzKGxhYmVsID0gbmFtZSksIHJlcGVsID0gVFJVRSkgKwojICAgdGhlbWVfZ3JhcGgoKQpgYGAKCiMjIyBLZXl3b3JkIGNvLW9jY3VycmVuY2UgbmV0d29yawoKYGBge3J9Cndvc19zY29wdXNfa2V5d29yZF9jb19vY2N1cnJlbmNlX21hdHJpeCA8LSAKICBiaWJsaW9OZXR3b3JrKE0gPSB3b3Nfc2NvcHVzX3RvcyRkZiwgCiAgICAgICAgICAgICAgICBhbmFseXNpcyA9ICJjby1vY2N1cnJlbmNlcyIsIAogICAgICAgICAgICAgICAgbmV0d29yayA9ICJrZXl3b3JkcyIsIAogICAgICAgICAgICAgICAgc2VwID0gIjsiKQoKcGxvdF9uZXRfY29fb2NjdXJyZW5jZSA8LSAKICBuZXR3b3JrUGxvdCh3b3Nfc2NvcHVzX2tleXdvcmRfY29fb2NjdXJyZW5jZV9tYXRyaXgsIAogICAgICAgICAgICAgIHdlaWdodGVkPVQsIG4gPSAzMCwgCiAgICAgICAgICAgICAgVGl0bGUgPSAiS2V5d29yZCBDby1vY2N1cnJlbmNlIE5ldHdvcmsiLCAKICAgICAgICAgICAgICB0eXBlID0gImZydWNodGVybWFuIiwgCiAgICAgICAgICAgICAgc2l6ZT1ULAogICAgICAgICAgICAgIGVkZ2VzaXplID0gNSwKICAgICAgICAgICAgICBsYWJlbHNpemU9MC43KQoKa2V5d29yZF9jb19vY2N1cnJlbmNlX3RibF9ncmFwaCA8LSAKICBncmFwaF9mcm9tX2FkamFjZW5jeV9tYXRyaXgod29zX3Njb3B1c19rZXl3b3JkX2NvX29jY3VycmVuY2VfbWF0cml4ICwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1vZGUgPSAidW5kaXJlY3RlZCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB3ZWlnaHRlZCA9IFRSVUUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaWFnID0gRkFMU0UpIHw+IAogIGFzX3RibF9ncmFwaChhY2FfaWdyYXBoLCBkaXJlY3RlZCA9IEZBTFNFICkgfD4gCiAgYWN0aXZhdGUobm9kZXMpIHw+IAogIG11dGF0ZShkZWdyZWUgPSBjZW50cmFsaXR5X2RlZ3JlZSgpKSB8PiAKICBhcnJhbmdlKGRlc2MoZGVncmVlKSkgfD4gCiAgc2xpY2UoMTozMCkKCmtleXdvcmRfY29fb2NjdXJyZW5jZV93ZWlnaHRfdGJsIDwtIAogIGtleXdvcmRfY29fb2NjdXJyZW5jZV90YmxfZ3JhcGggfD4gCiAgYWN0aXZhdGUoZWRnZXMpIHw+IAogIHNlbGVjdCh3ZWlnaHQpIHw+IAogIGFzLmRhdGEuZnJhbWUoKQoKdGhyZXNob2xkIDwtIAogIHF1YW50aWxlKGtleXdvcmRfY29fb2NjdXJyZW5jZV93ZWlnaHRfdGJsIHw+IAogICAgICAgICAgICAgc2VsZWN0KHdlaWdodCkgfD4gCiAgICAgICAgICAgICBwdWxsKCksIAogICAgICAgICAgIHByb2JzID0gMC44MCkKCmtleXdvcmRfY29fb2NjdXJyZW5jZV90YmxfZ3JhcGhfZmlsdGVyZWQgPC0gCiAga2V5d29yZF9jb19vY2N1cnJlbmNlX3RibF9ncmFwaCB8PiAKICBhY3RpdmF0ZShlZGdlcykgfD4gCiAgZmlsdGVyKHdlaWdodCA+PSB0aHJlc2hvbGQpIHw+IAogIGFjdGl2YXRlKG5vZGVzKSB8PiAKICBtdXRhdGUoY29tcG9uZW50cyA9IGdyb3VwX2NvbXBvbmVudHModHlwZSA9ICJ3ZWFrIikpIHw+IAogIGZpbHRlcihjb21wb25lbnRzID09IDEpIHw+IAogIG11dGF0ZShkZWdyZWUgPSBjZW50cmFsaXR5X2RlZ3JlZSgpLAogICAgICAgICBjb21tdW5pdHkgPSBhcy5mYWN0b3IoZ3JvdXBfbG91dmFpbigpKSApCgprZXl3b3JkX2NvX29jY3VycmVuY2VfdGJsX2dyYXBoX2ZpbHRlcmVkIHw+IAogIGdncmFwaChsYXlvdXQgPSAia2siKSArIAogIGdlb21fZWRnZV9saW5rKGFscGhhID0gLjI1LCAKICAgICAgICAgICAgICAgICBhZXMod2lkdGggPSB3ZWlnaHQpKSArCiAgZ2VvbV9ub2RlX3BvaW50KGFlcyhjb2xvdXIgPSBjb21tdW5pdHksIAogICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IGRlZ3JlZSkpICsKICBnZW9tX25vZGVfdGV4dChhZXMobGFiZWwgPSBuYW1lKSwgcmVwZWwgPSBUUlVFKSArCiAgdGhlbWVfZ3JhcGgoKQpgYGAKCiMjIEZpZ3VyZSA0LiBUcmVlIG9mIFNjaWVuY2UKCiMjIyBUcmVlIG9mIFNjaWVuY2UKCmBgYHtyfQp0cmVlX29mX3NjaWVuY2UKYGBgCgojIyMgQ2x1c3RlcmluZyBhbmFseXNpcwoKRmluZGluZyB0aGUgY2x1c3RlcnMKCmBgYHtyfQpub2RlcyA8LSAgIyBDcmVhdGUgYSBkYXRhZnJhbWUgd2l0aCB0aGUgZnVsbG5hbWUgb2YgYXJ0aWNsZXMgCiAgdGliYmxlKG5hbWUgPSBWKHdvc19zY29wdXNfdG9zJGdyYXBoKSRuYW1lKSB8PiAKICBsZWZ0X2pvaW4od29zX3Njb3B1c190b3Mkbm9kZXMsIAogICAgICAgICAgICBieSA9IGMoIm5hbWUiID0gIklEX1RPUyIpKQoKd29zX3Njb3B1c19jaXRhdGlvbl9uZXR3b3JrXzEgPC0gIyBBZGQgdGhlIGFydGljbGUgbmFtZXMgdG8gdGhlIGNpdGF0aW9uIG5ldHdvcmsKICB3b3Nfc2NvcHVzX3RvcyRncmFwaCB8PiAKICBpZ3JhcGg6OnNldC52ZXJ0ZXguYXR0cmlidXRlKG5hbWUgPSAiZnVsbF9uYW1lIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbmRleCA9IFYod29zX3Njb3B1c190b3MkZ3JhcGgpJG5hbWUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsdWUgPSBub2RlcyRDSVRFKQoKbm9kZXNfMSA8LSAjIENyZWF0ZSBhIGRhdGFmcmFtZSB3aXRoIHN1YmZpZWxkcyAoY2x1c3RlcnMpCiAgdGliYmxlKG5hbWUgPSBWKHdvc19zY29wdXNfY2l0YXRpb25fbmV0d29ya18xKSRuYW1lLAogICAgICAgICBjbHVzdGVyID0gVih3b3Nfc2NvcHVzX2NpdGF0aW9uX25ldHdvcmtfMSkkc3ViZmllbGQsCiAgICAgICAgIGZ1bGxfbmFtZSA9IFYod29zX3Njb3B1c19jaXRhdGlvbl9uZXR3b3JrXzEpJGZ1bGxfbmFtZSkKCm5vZGVzXzIgPC0gIyBDb3VudCB0aGUgbnVtYmVyIG9mIGFydGljbGVzIHBlciBjbHVzdGVyCiAgbm9kZXNfMSB8PiAKICBjb3VudChjbHVzdGVyLCBzb3J0ID0gVFJVRSkgfD4gCiAgbXV0YXRlKGNsdXN0ZXJfMSA9IHJvd19udW1iZXIoKSkgfD4gCiAgc2VsZWN0KGNsdXN0ZXIsIGNsdXN0ZXJfMSkKCm5vZGVzXzMgPC0gCiAgbm9kZXNfMSB8PiAKICBsZWZ0X2pvaW4obm9kZXNfMikgfD4gCiAgcmVuYW1lKHN1YmZpZWxkID0gY2x1c3Rlcl8xKSB8PiAKICBzZWxlY3QobmFtZSwgZnVsbF9uYW1lLCBzdWJmaWVsZCkKCmVkZ2VfbGlzdCA8LSAKICBnZXQuZWRnZWxpc3Qod29zX3Njb3B1c19jaXRhdGlvbl9uZXR3b3JrXzEpIHw+IAogIGRhdGEuZnJhbWUoKSB8PiAKICByZW5hbWUoU291cmNlID0gWDEsIFRhcmdldCA9IFgyKQoKd29zX3Njb3B1c19jaXRhdGlvbl9uZXR3b3JrIDwtIAogIGdyYXBoLmRhdGEuZnJhbWUoZCA9IGVkZ2VfbGlzdCwgCiAgICAgICAgICAgICAgICAgICBkaXJlY3RlZCA9IFRSVUUsIAogICAgICAgICAgICAgICAgICAgdmVydGljZXMgPSBub2Rlc18zKQoKd29zX3Njb3B1c19jaXRhdGlvbl9uZXR3b3JrIHw+IAogIHN1bW1hcnkoKQpgYGAKCkNob29zaW5nIGNsdXN0ZXJzCgpXZSBwcm9wb3NlZCB0aGUgdGlwcGluZyBwb2ludCBvcHRpb24gdG8gY2hvb3NlIHRoZSBudW1iZXIgb2YgY2x1c3RlcnMuIFNlZSB0aGlzIHBhcGVyOgoKaHR0cHM6Ly93d3cubmF0dXJlLmNvbS9hcnRpY2xlcy9zNDE1OTgtMDIxLTg1MDQxLTgKCmBgYHtyfQpjbHVzdGVycyA8LSAKICB0aWJibGUoY2x1c3RlciA9IFYod29zX3Njb3B1c19jaXRhdGlvbl9uZXR3b3JrKSRzdWJmaWVsZCkgfD4gCiAgY291bnQoY2x1c3Rlciwgc29ydCA9IFRSVUUpCgpjbHVzdGVycyB8PiAKICBnZ3Bsb3QoYWVzKHggPSByZW9yZGVyKGNsdXN0ZXIsIG4pLCB5ID0gbikpICsKICBnZW9tX3BvaW50KHNpemUgPSAzKSArCiAgbGFicyh4ID0gIkNsdXN0ZXJzIiwgeSA9ICJOdW1iZXIgb2YgcGFwZXJzIikgKwogIHRoZW1lKGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYgLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFtaWx5ID0gICJBcmlhbCIpLAogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYsIGZhbWlseSA9ICJBcmlhbCIpLAogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgZmFtaWx5ID0gIkFyaWFsIiksIAogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgZmFtaWx5ID0gIkFyaWFsIikpCmBgYAoKUmVtb3Zpbmcgbm90IGNob3NlbiBjbHVzdGVycwoKYGBge3J9Cndvc19zY29wdXNfY2l0YXRpb25fbmV0d29ya19jbHVzdGVycyA8LSAKICB3b3Nfc2NvcHVzX2NpdGF0aW9uX25ldHdvcmsgfD4gCiAgZGVsZXRlLnZlcnRpY2VzKHdoaWNoKFYod29zX3Njb3B1c19jaXRhdGlvbl9uZXR3b3JrKSRzdWJmaWVsZCAhPSAxICYgIyBmaWx0ZXIgY2x1c3RlcnMgCiAgICAgICAgICAgICAgICAgICAgICAgICAgVih3b3Nfc2NvcHVzX2NpdGF0aW9uX25ldHdvcmspJHN1YmZpZWxkICE9IDIgJgogICAgICAgICAgICAgICAgICAgICAgICAgIFYod29zX3Njb3B1c19jaXRhdGlvbl9uZXR3b3JrKSRzdWJmaWVsZCAhPSAzICAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgVih3b3Nfc2NvcHVzX2NpdGF0aW9uX25ldHdvcmspJHN1YmZpZWxkICE9IDQpKQoKd29zX3Njb3B1c19jaXRhdGlvbl9uZXR3b3JrX2NsdXN0ZXJzIHw+IAogIHN1bW1hcnkoKQpgYGAKCiMjIyBDbHVzdGVyIDEKCmBgYHtyfQpwYWwgPC0gYnJld2VyLnBhbCg4LCJEYXJrMiIpCgpub2Rlc19mdWxsX2RhdGEgPC0gCiAgdGliYmxlKG5hbWUgPSBWKHdvc19zY29wdXNfY2l0YXRpb25fbmV0d29yaykkbmFtZSwKICAgICAgICAgY2x1c3RlciA9IFYod29zX3Njb3B1c19jaXRhdGlvbl9uZXR3b3JrKSRzdWJmaWVsZCwKICAgICAgICAgZnVsbF9uYW1lID0gVih3b3Nfc2NvcHVzX2NpdGF0aW9uX25ldHdvcmspJGZ1bGxfbmFtZSkKCmNsdXN0ZXJfMSA8LSAKICB3b3Nfc2NvcHVzX2NpdGF0aW9uX25ldHdvcmsgfD4gCiAgZGVsZXRlLnZlcnRpY2VzKHdoaWNoKFYod29zX3Njb3B1c19jaXRhdGlvbl9uZXR3b3JrKSRzdWJmaWVsZCAhPSAxKSkKCmNsdXN0ZXJfMV9wYWdlX3JhbmsgPC0gCiAgY2x1c3Rlcl8xIHw+IAogIHNldC52ZXJ0ZXguYXR0cmlidXRlKG5hbWUgPSAicGFnZV9yYW5rIiwgCiAgICAgICAgICAgICAgICAgICAgICAgdmFsdWUgPSBwYWdlX3JhbmsoY2x1c3Rlcl8xKSR2ZWN0b3IpCgpjbHVzdGVyXzFfZGYgPC0gCiAgdGliYmxlKG5hbWUgPSBWKGNsdXN0ZXJfMV9wYWdlX3JhbmspJG5hbWUsCiAgICAgICAgIGZ1bGxfbmFtZSA9IFYoY2x1c3Rlcl8xX3BhZ2VfcmFuaykkZnVsbF9uYW1lLAogICAgICAgICBwYWdlX3JhbmsgPSBWKGNsdXN0ZXJfMV9wYWdlX3JhbmspJHBhZ2VfcmFuaywKICAgICAgICAgY2x1c3RlciA9IFYoY2x1c3Rlcl8xX3BhZ2VfcmFuaykkc3ViZmllbGQsKQoKbm9kZXNfZnVsbF9kYXRhIHw+IAogIGZpbHRlcihjbHVzdGVyID09IDEpIHw+IAogIHNlbGVjdChmdWxsX25hbWUpIHw+IAogIG11dGF0ZShmdWxsX25hbWUgPSBzdHJfZXh0cmFjdChmdWxsX25hbWUsIFNQQyAlUiUgICMgUmVndWxhciBleHByZXNzaW9ucyAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvbmVfb3JfbW9yZShXUkQpICVSJSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBTUEMgJVIlIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9uZV9vcl9tb3JlKG9yKFdSRCwgQU5ZX0NIQVIpKSksCiAgICAgICAgIGZ1bGxfbmFtZSA9IHN0cl9yZW1vdmUoZnVsbF9uYW1lLCBPUEVOX1BBUkVOICVSJSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcGVhdGVkKERHVCwgNCkgJVIlIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQ0xPU0VfUEFSRU4gJVIlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvbmVfb3JfbW9yZShvcihXUkQsQU5ZX0NIQVIpKSksCiAgICAgICAgIGZ1bGxfbmFtZSA9IHN0cl90cmltKGZ1bGxfbmFtZSkpICB8PiAKICB1bm5lc3RfdG9rZW5zKG91dHB1dCA9IHdvcmQsIGlucHV0ID0gZnVsbF9uYW1lKSB8PiAjIFRva2VuaXphdGlvbgogIGFudGlfam9pbihzdG9wX3dvcmRzKSB8PiAgIyBSZW1vdmluZyBzdG9wIHdvcmRzCiAgZmlsdGVyKHdvcmQgIT0gImRvaSIsCiAgICAgICAgICFzdHJfZGV0ZWN0KHdvcmQsICJbMC05XSIpKSB8PiAgIyBXb1MgZGF0YQogIGZpbHRlcih3b3JkID09IHN0cl9yZW1vdmUod29yZCwgcGF0dGVybiA9ICJtb2RlbCIpLAogICAgICAgICB3b3JkID09IHN0cl9yZW1vdmUod29yZCwgcGF0dGVybiA9ICJzb2Z0d2FyZSIpLCAgIyBXb3JkcyByZW1vdmVkCiAgICAgICAgIHdvcmQgPT0gc3RyX3JlbW92ZSh3b3JkLCBwYXR0ZXJuID0gImNoZWNraW5nIiksIAogICAgICAgICB3b3JkID09IHN0cl9yZW1vdmUod29yZCwgcGF0dGVybiA9ICJsZWN0dXJlIiksCiAgICAgICAgIHdvcmQgPT0gc3RyX3JlbW92ZSh3b3JkLCBwYXR0ZXJuID0gIm5vdGVzIiksCiAgICAgICAgIHdvcmQgPT0gc3RyX3JlbW92ZSh3b3JkLCBwYXR0ZXJuID0gIm1hbmFnZW1lbnQiKSwKICAgICAgICAgd29yZCA9PSBzdHJfcmVtb3ZlKHdvcmQsIHBhdHRlcm4gPSAiYmlibGlvbWV0cmljIiksCiAgICAgICAgIHdvcmQgPT0gc3RyX3JlbW92ZSh3b3JkLCBwYXR0ZXJuID0gInJldmlldyIpLAogICAgICAgICB3b3JkID09IHN0cl9yZW1vdmUod29yZCwgcGF0dGVybiA9ICJqb3VybmFsIikpIHw+CiAgY291bnQod29yZCwgc29ydCA9IFRSVUUpIHw+IAogIHdpdGgod29yZGNsb3VkKHdvcmQsIAogICAgICAgICAgICAgICAgIG4sIAogICAgICAgICAgICAgICAgIHJhbmRvbS5vcmRlciA9IEZBTFNFLCAKICAgICAgICAgICAgICAgICBtYXgud29yZHMgPSA1MCwgCiAgICAgICAgICAgICAgICAgY29sb3JzPXBhbCkpCmBgYAoKIyMjIENsdXN0ZXIgMgoKYGBge3J9CmNsdXN0ZXJfMiA8LSAKICB3b3Nfc2NvcHVzX2NpdGF0aW9uX25ldHdvcmsgfD4gCiAgZGVsZXRlLnZlcnRpY2VzKHdoaWNoKFYod29zX3Njb3B1c19jaXRhdGlvbl9uZXR3b3JrKSRzdWJmaWVsZCAhPSAyKSkKCmNsdXN0ZXJfMl9wYWdlX3JhbmsgPC0gCiAgY2x1c3Rlcl8yIHw+IAogIHNldC52ZXJ0ZXguYXR0cmlidXRlKG5hbWUgPSAicGFnZV9yYW5rIiwgCiAgICAgICAgICAgICAgICAgICAgICAgdmFsdWUgPSBwYWdlX3JhbmsoY2x1c3Rlcl8yKSR2ZWN0b3IpCgpjbHVzdGVyXzJfZGYgPC0gCiAgdGliYmxlKG5hbWUgPSBWKGNsdXN0ZXJfMl9wYWdlX3JhbmspJG5hbWUsCiAgICAgICAgIGZ1bGxfbmFtZSA9IFYoY2x1c3Rlcl8yX3BhZ2VfcmFuaykkZnVsbF9uYW1lLAogICAgICAgICBwYWdlX3JhbmsgPSBWKGNsdXN0ZXJfMl9wYWdlX3JhbmspJHBhZ2VfcmFuaywKICAgICAgICAgY2x1c3RlciA9IFYoY2x1c3Rlcl8yX3BhZ2VfcmFuaykkc3ViZmllbGQsKQoKbm9kZXNfZnVsbF9kYXRhIHw+IAogIGZpbHRlcihjbHVzdGVyID09IDIpIHw+IAogIHNlbGVjdChmdWxsX25hbWUpIHw+IAogIG11dGF0ZShmdWxsX25hbWUgPSBzdHJfZXh0cmFjdChmdWxsX25hbWUsIFNQQyAlUiUgICMgUmVndWxhciBleHByZXNzaW9ucyAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvbmVfb3JfbW9yZShXUkQpICVSJSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBTUEMgJVIlIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9uZV9vcl9tb3JlKG9yKFdSRCwgQU5ZX0NIQVIpKSksCiAgICAgICAgIGZ1bGxfbmFtZSA9IHN0cl9yZW1vdmUoZnVsbF9uYW1lLCBPUEVOX1BBUkVOICVSJSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcGVhdGVkKERHVCwgNCkgJVIlIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQ0xPU0VfUEFSRU4gJVIlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvbmVfb3JfbW9yZShvcihXUkQsQU5ZX0NIQVIpKSksCiAgICAgICAgIGZ1bGxfbmFtZSA9IHN0cl90cmltKGZ1bGxfbmFtZSkpICB8PiAKICB1bm5lc3RfdG9rZW5zKG91dHB1dCA9IHdvcmQsIGlucHV0ID0gZnVsbF9uYW1lKSB8PiAKICBhbnRpX2pvaW4oc3RvcF93b3JkcykgfD4KICBmaWx0ZXIod29yZCAhPSAiZG9pIiwKICAgICAgICAgIXN0cl9kZXRlY3Qod29yZCwgIlswLTldIikpIHw+ICAjIFdvUyBkYXRhCiAgZmlsdGVyKHdvcmQgPT0gc3RyX3JlbW92ZSh3b3JkLCBwYXR0ZXJuID0gIm1vZGVsIiksCiAgICAgICAgIHdvcmQgPT0gc3RyX3JlbW92ZSh3b3JkLCBwYXR0ZXJuID0gInNvZnR3YXJlIiksICAjIFdvcmRzIHJlbW92ZWQKICAgICAgICAgd29yZCA9PSBzdHJfcmVtb3ZlKHdvcmQsIHBhdHRlcm4gPSAiY2hlY2tpbmciKSwgCiAgICAgICAgIHdvcmQgPT0gc3RyX3JlbW92ZSh3b3JkLCBwYXR0ZXJuID0gImxlY3R1cmUiKSwKICAgICAgICAgd29yZCA9PSBzdHJfcmVtb3ZlKHdvcmQsIHBhdHRlcm4gPSAibm90ZXMiKSwKICAgICAgICAgd29yZCA9PSBzdHJfcmVtb3ZlKHdvcmQsIHBhdHRlcm4gPSAicHJvY2VlZGluZ3MiKSkgfD4KICBjb3VudCh3b3JkLCBzb3J0ID0gVFJVRSkgfD4gCiAgd2l0aCh3b3JkY2xvdWQod29yZCwgCiAgICAgICAgICAgICAgICAgbiwgCiAgICAgICAgICAgICAgICAgcmFuZG9tLm9yZGVyID0gRkFMU0UsIAogICAgICAgICAgICAgICAgIG1heC53b3JkcyA9IDUwLCAKICAgICAgICAgICAgICAgICBjb2xvcnM9cGFsKSkKYGBgCgojIyMgQ2x1c3RlciAzCgpgYGB7cn0KCmNsdXN0ZXJfMyA8LSAKICB3b3Nfc2NvcHVzX2NpdGF0aW9uX25ldHdvcmsgfD4gCiAgZGVsZXRlLnZlcnRpY2VzKHdoaWNoKFYod29zX3Njb3B1c19jaXRhdGlvbl9uZXR3b3JrKSRzdWJmaWVsZCAhPSAzKSkKCmNsdXN0ZXJfM19wYWdlX3JhbmsgPC0gCiAgY2x1c3Rlcl8zIHw+IAogIHNldC52ZXJ0ZXguYXR0cmlidXRlKG5hbWUgPSAicGFnZV9yYW5rIiwgCiAgICAgICAgICAgICAgICAgICAgICAgdmFsdWUgPSBwYWdlX3JhbmsoY2x1c3Rlcl8zKSR2ZWN0b3IpCgpjbHVzdGVyXzNfZGYgPC0gCiAgdGliYmxlKG5hbWUgPSBWKGNsdXN0ZXJfM19wYWdlX3JhbmspJG5hbWUsCiAgICAgICAgIGZ1bGxfbmFtZSA9IFYoY2x1c3Rlcl8zX3BhZ2VfcmFuaykkZnVsbF9uYW1lLAogICAgICAgICBwYWdlX3JhbmsgPSBWKGNsdXN0ZXJfM19wYWdlX3JhbmspJHBhZ2VfcmFuaywKICAgICAgICAgY2x1c3RlciA9IFYoY2x1c3Rlcl8zX3BhZ2VfcmFuaykkc3ViZmllbGQsKQoKbm9kZXNfZnVsbF9kYXRhIHw+IAogIGZpbHRlcihjbHVzdGVyID09IDMpIHw+IAogIHNlbGVjdChmdWxsX25hbWUpIHw+IAogIG11dGF0ZShmdWxsX25hbWUgPSBzdHJfZXh0cmFjdChmdWxsX25hbWUsIFNQQyAlUiUgICMgUmVndWxhciBleHByZXNzaW9ucyAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvbmVfb3JfbW9yZShXUkQpICVSJSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBTUEMgJVIlIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9uZV9vcl9tb3JlKG9yKFdSRCwgQU5ZX0NIQVIpKSksCiAgICAgICAgIGZ1bGxfbmFtZSA9IHN0cl9yZW1vdmUoZnVsbF9uYW1lLCBPUEVOX1BBUkVOICVSJSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcGVhdGVkKERHVCwgNCkgJVIlIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQ0xPU0VfUEFSRU4gJVIlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvbmVfb3JfbW9yZShvcihXUkQsQU5ZX0NIQVIpKSksCiAgICAgICAgIGZ1bGxfbmFtZSA9IHN0cl90cmltKGZ1bGxfbmFtZSkpICB8PiAKICB1bm5lc3RfdG9rZW5zKG91dHB1dCA9IHdvcmQsIGlucHV0ID0gZnVsbF9uYW1lKSB8PiAKICBhbnRpX2pvaW4oc3RvcF93b3JkcykgfD4KICBmaWx0ZXIod29yZCAhPSAiZG9pIiwKICAgICAgICAgIXN0cl9kZXRlY3Qod29yZCwgIlswLTldIikpIHw+ICAjIFdvUyBkYXRhIAogZmlsdGVyKHdvcmQgPT0gc3RyX3JlbW92ZSh3b3JkLCBwYXR0ZXJuID0gIm1vZGVsIiksCiAgICAgICAgIHdvcmQgPT0gc3RyX3JlbW92ZSh3b3JkLCBwYXR0ZXJuID0gInNvZnR3YXJlIiksICAjIFdvcmRzIHJlbW92ZWQKICAgICAgICAgd29yZCA9PSBzdHJfcmVtb3ZlKHdvcmQsIHBhdHRlcm4gPSAiY2hlY2tpbmciKSwgCiAgICAgICAgIHdvcmQgPT0gc3RyX3JlbW92ZSh3b3JkLCBwYXR0ZXJuID0gImxlY3R1cmUiKSwKICAgICAgICAgd29yZCA9PSBzdHJfcmVtb3ZlKHdvcmQsIHBhdHRlcm4gPSAibm90ZXMiKSwKICAgICAgICAgd29yZCA9PSBzdHJfcmVtb3ZlKHdvcmQsIHBhdHRlcm4gPSAicHJvY2VlZGluZ3MiKSwKICAgICAgICAgd29yZCA9PSBzdHJfcmVtb3ZlKHdvcmQsIHBhdHRlcm4gPSAiY29tcHV0ZXIiKSwKICAgICAgICAgd29yZCA9PSBzdHJfcmVtb3ZlKHdvcmQsIHBhdHRlcm4gPSAic3lzdGVtcyIpLAogICAgICAgICB3b3JkID09IHN0cl9yZW1vdmUod29yZCwgcGF0dGVybiA9ICJzeXN0ZW1jIikpfD4KICBjb3VudCh3b3JkLCBzb3J0ID0gVFJVRSkgfD4gCiAgd2l0aCh3b3JkY2xvdWQod29yZCwgCiAgICAgICAgICAgICAgICAgbiwgCiAgICAgICAgICAgICAgICAgcmFuZG9tLm9yZGVyID0gRkFMU0UsIAogICAgICAgICAgICAgICAgIG1heC53b3JkcyA9IDUwLCAKICAgICAgICAgICAgICAgICBjb2xvcnM9cGFsKSkKYGBgCiMjIyBDbHVzdGVyIDQKCmBgYHtyfQoKY2x1c3Rlcl80IDwtIAogIHdvc19zY29wdXNfY2l0YXRpb25fbmV0d29yayB8PiAKICBkZWxldGUudmVydGljZXMod2hpY2goVih3b3Nfc2NvcHVzX2NpdGF0aW9uX25ldHdvcmspJHN1YmZpZWxkICE9IDQpKQoKY2x1c3Rlcl80X3BhZ2VfcmFuayA8LSAKICBjbHVzdGVyXzQgfD4gCiAgc2V0LnZlcnRleC5hdHRyaWJ1dGUobmFtZSA9ICJwYWdlX3JhbmsiLCAKICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZSA9IHBhZ2VfcmFuayhjbHVzdGVyXzQpJHZlY3RvcikKCmNsdXN0ZXJfNF9kZiA8LSAKICB0aWJibGUobmFtZSA9IFYoY2x1c3Rlcl80X3BhZ2VfcmFuaykkbmFtZSwKICAgICAgICAgZnVsbF9uYW1lID0gVihjbHVzdGVyXzRfcGFnZV9yYW5rKSRmdWxsX25hbWUsCiAgICAgICAgIHBhZ2VfcmFuayA9IFYoY2x1c3Rlcl80X3BhZ2VfcmFuaykkcGFnZV9yYW5rLAogICAgICAgICBjbHVzdGVyID0gVihjbHVzdGVyXzRfcGFnZV9yYW5rKSRzdWJmaWVsZCwpCgpub2Rlc19mdWxsX2RhdGEgfD4gCiAgZmlsdGVyKGNsdXN0ZXIgPT0gNCkgfD4gCiAgc2VsZWN0KGZ1bGxfbmFtZSkgfD4gCiAgbXV0YXRlKGZ1bGxfbmFtZSA9IHN0cl9leHRyYWN0KGZ1bGxfbmFtZSwgU1BDICVSJSAgIyBSZWd1bGFyIGV4cHJlc3Npb25zIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9uZV9vcl9tb3JlKFdSRCkgJVIlIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFNQQyAlUiUgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb25lX29yX21vcmUob3IoV1JELCBBTllfQ0hBUikpKSwKICAgICAgICAgZnVsbF9uYW1lID0gc3RyX3JlbW92ZShmdWxsX25hbWUsIE9QRU5fUEFSRU4gJVIlIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVwZWF0ZWQoREdULCA0KSAlUiUgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBDTE9TRV9QQVJFTiAlUiUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9uZV9vcl9tb3JlKG9yKFdSRCxBTllfQ0hBUikpKSwKICAgICAgICAgZnVsbF9uYW1lID0gc3RyX3RyaW0oZnVsbF9uYW1lKSkgIHw+IAogIHVubmVzdF90b2tlbnMob3V0cHV0ID0gd29yZCwgaW5wdXQgPSBmdWxsX25hbWUpIHw+IAogIGFudGlfam9pbihzdG9wX3dvcmRzKSB8PiAKICBmaWx0ZXIod29yZCAhPSAiZG9pIiwKICAgICAgICAgIXN0cl9kZXRlY3Qod29yZCwgIlswLTldIikpIHw+ICAjIFdvUyBkYXRhCiBmaWx0ZXIod29yZCA9PSBzdHJfcmVtb3ZlKHdvcmQsIHBhdHRlcm4gPSAibW9kZWwiKSwKICAgICAgICAgd29yZCA9PSBzdHJfcmVtb3ZlKHdvcmQsIHBhdHRlcm4gPSAic29mdHdhcmUiKSwgICMgV29yZHMgcmVtb3ZlZAogICAgICAgICB3b3JkID09IHN0cl9yZW1vdmUod29yZCwgcGF0dGVybiA9ICJjaGVja2luZyIpLCAKICAgICAgICAgd29yZCA9PSBzdHJfcmVtb3ZlKHdvcmQsIHBhdHRlcm4gPSAibGVjdHVyZSIpLAogICAgICAgICB3b3JkID09IHN0cl9yZW1vdmUod29yZCwgcGF0dGVybiA9ICJub3RlcyIpLAogICAgICAgICB3b3JkID09IHN0cl9yZW1vdmUod29yZCwgcGF0dGVybiA9ICJwcm9jZWVkaW5ncyIpLAogICAgICAgICB3b3JkID09IHN0cl9yZW1vdmUod29yZCwgcGF0dGVybiA9ICJjb21wdXRlciIpLAogICAgICAgICB3b3JkID09IHN0cl9yZW1vdmUod29yZCwgcGF0dGVybiA9ICJzeXN0ZW1zIiksCiAgICAgICAgIHdvcmQgPT0gc3RyX3JlbW92ZSh3b3JkLCBwYXR0ZXJuID0gInN5c3RlbWMiKSkgfD4KICBjb3VudCh3b3JkLCBzb3J0ID0gVFJVRSkgfD4gCiAgd2l0aCh3b3JkY2xvdWQod29yZCwgCiAgICAgICAgICAgICAgICAgbiwgCiAgICAgICAgICAgICAgICAgcmFuZG9tLm9yZGVyID0gRkFMU0UsIAogICAgICAgICAgICAgICAgIG1heC53b3JkcyA9IDUwLCAKICAgICAgICAgICAgICAgICBjb2xvcnM9cGFsKSkKYGBgCgojIEV4cG9ydGluZyBmaWxlcwoKYGBge3J9Cgp3cml0ZV9jc3Yod29zX3Njb3B1c190b3MkZGYsICJ3b3Nfc2NvcHVzX3Rvcy5jc3YiKSAjIEV4cG9ydGluZyBhbGwgZGF0YSBtZXJnZWQKCndyaXRlX2Nzdih0YWJsZV8xLCAidGFibGVfMS5jc3YiKSAjIEV4cG9ydGluZyB0YWJsZSAxCndyaXRlX2Nzdih3b3Nfc2NvcHVzX3RvdGFsX2NvdW50cnksICJ0YWJsZV8yXy5jc3YiKSAgIyBFeHBvcnRpbmcgdGFibGUgMgp3cml0ZV9jc3Yod29zX3Njb3B1c19hdXRob3JzLCAidGFibGVfMy5jc3YiKSAjIEV4cG9ydGluZyB0YWJsZSAzCndyaXRlX2Nzdih3b3Nfc2NvcHVzX3RvdGFsX2pvdXJuYWwsICJ0YWJsZV80LmNzdiIpICMgRXhwb3J0aW5nIHRhYmxlIDQKCgp3cml0ZV9jc3YobGFuZ3VhZ2VzLCAiZmlndXJlXzEuY3N2IikgIyBFeHBvcnRpbmcgZGF0YSBmaWd1cmUgMSAKd3JpdGVfY3N2KGZpZ3VyZV8yX2RhdGEsICJmaWd1cmVfMi5jc3YiKSAjIEV4cG9ydGluZyBkYXRhIGZpZ3VyZSAyCgp3cml0ZS5ncmFwaCh3b3Nfc2NvcHVzX2NpdGF0aW9uX25ldHdvcmssICJjaXRhdGlvbl9uZXR3b3JrX2Z1bGwuZ3JhcGhtbCIsICJncmFwaG1sIikgIyBFeHBvcnRpbmcgZ3JhcGgKd3JpdGUuZ3JhcGgod29zX3Njb3B1c19jaXRhdGlvbl9uZXR3b3JrX2NsdXN0ZXJzLCAKICAgICAgICAgICAgIndvc19zY29wdXNfY2l0YXRpb25fbmV0d29ya19jbHVzdGVycy5ncmFwaG1sIiwgCiAgICAgICAgICAgICJncmFwaG1sIikKCmFjYV9ncmFwaG1sX25vZGVzIDwtIAogIGFjYV90YmxfZ3JhcGhfZmlsdGVyZWQgfD4gCiAgYWN0aXZhdGUobm9kZXMpIHw+IAogIGFzX3RpYmJsZSgpIHw+IAogIHJlbmFtZShhdXRob3IgPSBuYW1lKSB8PiAKICByb3duYW1lc190b19jb2x1bW4oIm5hbWUiKQoKYWNhX2dyYXBobWxfZWRnZXMgPC0gCiAgYWNhX3RibF9ncmFwaF9maWx0ZXJlZCB8PiAKICBhY3RpdmF0ZShlZGdlcykgfD4gCiAgYXNfdGliYmxlKCkgCgphY2FfZ3JhcGhtbCA8LSAKICBncmFwaF9mcm9tX2RhdGFfZnJhbWUoZCA9IGFjYV9ncmFwaG1sX2VkZ2VzLCAKICAgICAgICAgICAgICAgICAgICAgICAgZGlyZWN0ZWQgPSBGQUxTRSwgCiAgICAgICAgICAgICAgICAgICAgICAgIHZlcnRpY2VzID0gYWNhX2dyYXBobWxfbm9kZXMpCgp3cml0ZV9ncmFwaChhY2FfZ3JhcGhtbCwgImFjYV9ncmFwaC5ncmFwaG1sIiwgImdyYXBobWwiKSAjIEV4cG9ydCBhdXRob3IgY28tY2l0YXRpb24gZ3JhcGgKCmF1dGhvcl9jb2xsYWJfZ3JhcGhtbF9ub2RlcyA8LSAKICBhdXRob3JfY29sbGFiX3RibF9ncmFwaF9maWx0ZXJlZCB8PiAKICBhY3RpdmF0ZShub2RlcykgfD4gCiAgYXNfdGliYmxlKCkgfD4gCiAgcmVuYW1lKGF1dGhvciA9IG5hbWUpIHw+IAogIHJvd25hbWVzX3RvX2NvbHVtbigibmFtZSIpCgphdXRob3JfY29sbGFiX2dyYXBobWxfZWRnZXMgPC0gCiAgYXV0aG9yX2NvbGxhYl90YmxfZ3JhcGhfZmlsdGVyZWQgfD4gCiAgYWN0aXZhdGUoZWRnZXMpIHw+IAogIGFzX3RpYmJsZSgpIAoKYXV0aG9yX2NvbGxhYl9ncmFwaG1sIDwtIAogIGdyYXBoX2Zyb21fZGF0YV9mcmFtZShkID0gYXV0aG9yX2NvbGxhYl9ncmFwaG1sX2VkZ2VzLCAKICAgICAgICAgICAgICAgICAgICAgICAgZGlyZWN0ZWQgPSBGQUxTRSwgCiAgICAgICAgICAgICAgICAgICAgICAgIHZlcnRpY2VzID0gYXV0aG9yX2NvbGxhYl9ncmFwaG1sX25vZGVzKQoKd3JpdGVfZ3JhcGgoYXV0aG9yX2NvbGxhYl9ncmFwaG1sLCAiYXV0aG9yX2NvbGxhYl9ncmFwaG1sLmdyYXBobWwiLCAiZ3JhcGhtbCIpICMgRXhwb3J0IGF1dGhvciBjby1jaXRhdGlvbiBncmFwaAoKY291bnRyeV9jb2xsYWJfZ3JhcGhtbF9ub2RlcyA8LSAKICBjb3VudHJ5X2NvbGxhYl90YmxfZ3JhcGhfZmlsdGVyZWQgfD4gCiAgYWN0aXZhdGUobm9kZXMpIHw+IAogIGFzX3RpYmJsZSgpIHw+IAogIHJlbmFtZShhdXRob3IgPSBuYW1lKSB8PiAKICByb3duYW1lc190b19jb2x1bW4oIm5hbWUiKQoKY291bnRyeV9jb2xsYWJfZ3JhcGhtbF9lZGdlcyA8LSAKICBjb3VudHJ5X2NvbGxhYl90YmxfZ3JhcGhfZmlsdGVyZWQgfD4gCiAgYWN0aXZhdGUoZWRnZXMpIHw+IAogIGFzX3RpYmJsZSgpIAoKY291bnRyeV9jb2xsYWJfZ3JhcGhtbCA8LSAKICBncmFwaF9mcm9tX2RhdGFfZnJhbWUoZCA9IGNvdW50cnlfY29sbGFiX2dyYXBobWxfZWRnZXMsIAogICAgICAgICAgICAgICAgICAgICAgICBkaXJlY3RlZCA9IEZBTFNFLCAKICAgICAgICAgICAgICAgICAgICAgICAgdmVydGljZXMgPSBjb3VudHJ5X2NvbGxhYl9ncmFwaG1sX25vZGVzKQoKd3JpdGVfZ3JhcGgoY291bnRyeV9jb2xsYWJfZ3JhcGhtbCwgImNvdW50cnlfY29sbGFiX2dyYXBobWwuZ3JhcGhtbCIsICJncmFwaG1sIikgIyBFeHBvcnQgYXV0aG9yIGNvLWNpdGF0aW9uIGdyYXBoCgprZXl3b3JkX2NvX29jY3VycmVuY2VfZ3JhcGhtbF9ub2RlcyA8LSAKICBrZXl3b3JkX2NvX29jY3VycmVuY2VfdGJsX2dyYXBoX2ZpbHRlcmVkIHw+IAogIGFjdGl2YXRlKG5vZGVzKSB8PiAKICBhc190aWJibGUoKSB8PiAKICByZW5hbWUoYXV0aG9yID0gbmFtZSkgfD4gCiAgcm93bmFtZXNfdG9fY29sdW1uKCJuYW1lIikKCmtleXdvcmRfY29fb2NjdXJyZW5jZV9ncmFwaG1sX2VkZ2VzIDwtIAogIGtleXdvcmRfY29fb2NjdXJyZW5jZV90YmxfZ3JhcGhfZmlsdGVyZWQgfD4gCiAgYWN0aXZhdGUoZWRnZXMpIHw+IAogIGFzX3RpYmJsZSgpICAKCmtleXdvcmRfY29fb2NjdXJyZW5jZV9ncmFwaG1sIDwtIAogIGdyYXBoX2Zyb21fZGF0YV9mcmFtZShkID0ga2V5d29yZF9jb19vY2N1cnJlbmNlX2dyYXBobWxfZWRnZXMsIAogICAgICAgICAgICAgICAgICAgICAgICBkaXJlY3RlZCA9IEZBTFNFLCAKICAgICAgICAgICAgICAgICAgICAgICAgdmVydGljZXMgPSBrZXl3b3JkX2NvX29jY3VycmVuY2VfZ3JhcGhtbF9ub2RlcykKCndyaXRlX2dyYXBoKGtleXdvcmRfY29fb2NjdXJyZW5jZV9ncmFwaG1sLCAia2V5d29yZF9jb19vY2N1cnJlbmNlX2dyYXBobWwuZ3JhcGhtbCIsICJncmFwaG1sIikgIyBFeHBvcnQgYXV0aG9yIGNvLWNpdGF0aW9uIGdyYXBoCgp3cml0ZS5jc3YodHJlZV9vZl9zY2llbmNlLCAidHJlZV9vZl9zY2llbmNlLmNzdiIpICMgRXhwb3J0aW5nIFRyZWUgb2YgU2NpZW5jZQoKd3JpdGUuY3N2KGNsdXN0ZXJfMV9kZiwgImNsdXN0ZXJfMS5jc3YiKSAjIEV4cG9ydGluZyBjbHVzdGVyIDEKd3JpdGUuY3N2KGNsdXN0ZXJfMl9kZiwgImNsdXN0ZXJfMi5jc3YiKSAjIEV4cG9ydGluZyBjbHVzdGVyIDIKd3JpdGUuY3N2KGNsdXN0ZXJfM19kZiwgImNsdXN0ZXJfMy5jc3YiKSAjIEV4cG9ydGluZyBjbHVzdGVyIDMKd3JpdGUuY3N2KGNsdXN0ZXJfNF9kZiwgImNsdXN0ZXJfNC5jc3YiKSAjIEV4cG9ydGluZyBjbHVzdGVyIDQKCndyaXRlLmNzdihub2Rlc19mdWxsX2RhdGEsICJub2Rlc19mdWxsX2RhdGEuY3N2IikgIyBFeHBvcnRpbmcgYWxsIG5vZGVzCmBgYAo=