Creating the environment

library(tidyverse)
library(tosr)
library(bibliometrix)
library(lubridate)
library(igraph)
library(tidytext)
library(wordcloud)
library(rebus)
library(ggrepel) # improve donut visualization

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("scopus.bib",  # Create data from searches   
                  "WoS.txt")
[1] 2

Converting your scopus collection into a bibliographic dataframe

Done!


Generating affiliation field tag AU_UN from C1:  Done!


Converting your wos collection into a bibliographic dataframe


Warning:
In your file, some mandatory metadata are missing. Bibliometrix functions may not work properly!

Please, take a look at the vignettes:
- 'Data Importing and Converting' (https://www.bibliometrix.org/vignettes/Data-Importing-and-Converting.html)
- 'A brief introduction to bibliometrix' (https://www.bibliometrix.org/vignettes/Introduction_to_bibliometrix.html)


Missing fields:  CR 
Done!


Generating affiliation field tag AU_UN from C1:  Done!


 180 duplicated documents have been removed
tree_of_science <- 
  tosr::tosR("scopus.bib",  # Create data from searches   
             "WoS.txt")
[1] 2

Converting your scopus collection into a bibliographic dataframe

Done!


Generating affiliation field tag AU_UN from C1:  Done!


Converting your wos collection into a bibliographic dataframe


Warning:
In your file, some mandatory metadata are missing. Bibliometrix functions may not work properly!

Please, take a look at the vignettes:
- 'Data Importing and Converting' (https://www.bibliometrix.org/vignettes/Data-Importing-and-Converting.html)
- 'A brief introduction to bibliometrix' (https://www.bibliometrix.org/vignettes/Introduction_to_bibliometrix.html)


Missing fields:  CR 
Done!


Generating affiliation field tag AU_UN from C1:  Done!


 180 duplicated documents have been removed
Computing TOS SAP
Computing TOS subfields
wos <- 
  bibliometrix::convert2df(c("WoS.txt"))  # create dataframe from wos file

Converting your wos collection into a bibliographic dataframe


Warning:
In your file, some mandatory metadata are missing. Bibliometrix functions may not work properly!

Please, take a look at the vignettes:
- 'Data Importing and Converting' (https://www.bibliometrix.org/vignettes/Data-Importing-and-Converting.html)
- 'A brief introduction to bibliometrix' (https://www.bibliometrix.org/vignettes/Introduction_to_bibliometrix.html)


Missing fields:  CR 
Done!


Generating affiliation field tag AU_UN from C1:  Done!
scopus <- 
  bibliometrix::convert2df("scopus.bib", # Create dataframe from scopus file
                           dbsource = "scopus", 
                           format = "bibtex")

Converting your scopus collection into a bibliographic dataframe

Done!


Generating affiliation field tag AU_UN from C1:  Done!

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 4 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 |> 
  filter(str_detect(DT, "ARTICLE")) |> 
  select(journal = SO) |> 
  na.omit() |> 
  count(journal, sort = TRUE) |> 
  slice(1:20) |> 
  rename(publications = n) |> 
  mutate(database = "wos")

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

total_journal <- 
  wos_scopus_tos$df |> 
  filter(str_detect(DT, "ARTICLE")) |> 
  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")

plot_net_author_co_citation <- 
  networkPlot(wos_scopus_author_co_citation_matrix, 
              weighted=T, 
              n = 30, 
              Title = "Author Co-citation Network", 
              type = "fruchterman", 
              size=T,
              edgesize = 5,
              labelsize=0.7)

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)

Country Collaboration Network

wos_scopus_country_collab_matrix <- 
  biblioNetwork(M = wos_scopus_tos$df, 
                analysis = "collaboration", 
                network = "countries")

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)

Keyword co-citation 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)

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 f829abe DN-- 1925 5829 -- 
+ 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() 

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 100bf54 DN-- 796 1900 -- 
+ 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 = "citation"),
         word == str_remove(word, pattern = "index"),  # Words removed
         word == str_remove(word, pattern = "water"), 
         word == str_remove(word, pattern = "gis"),
         word == str_remove(word, pattern = "quality"),
         word == str_remove(word, pattern = "groundwater")) |>
  count(word, sort = TRUE) |> 
  with(wordcloud(word, 
                 n, 
                 random.order = FALSE, 
                 max.words = 50, 
                 colors=pal))
Joining, by = "word"

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 = "citation"),
         word == str_remove(word, pattern = "index"), 
         word == str_remove(word, pattern = "water"), 
         word == str_remove(word, pattern = "gis"),
         word == str_remove(word, pattern = "quality"),
         word == str_remove(word, pattern = "groundwater")) |>
  count(word, sort = TRUE) |> 
  with(wordcloud(word, 
                 n, 
                 random.order = FALSE, 
                 max.words = 50, 
                 colors=pal))
Joining, by = "word"

Cluster 3

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 = "citation"),
         word == str_remove(word, pattern = "index"), 
         word == str_remove(word, pattern = "water"), 
         word == str_remove(word, pattern = "gis"),
         word == str_remove(word, pattern = "quality"),
         word == str_remove(word, pattern = "groundwater")) |>
  count(word, sort = TRUE) |> 
  with(wordcloud(word, 
                 n, 
                 random.order = FALSE, 
                 max.words = 50, 
                 colors=pal))
Joining, by = "word"

Cluster 4

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 = "citation"),
         word == str_remove(word, pattern = "index"), 
         word == str_remove(word, pattern = "water"), 
         word == str_remove(word, pattern = "gis"),
         word == str_remove(word, pattern = "quality"),
         word == str_remove(word, pattern = "groundwater")) |>
  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) :
  application could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  geochemistry could not be fit on page. It will not be plotted.
Warning in wordcloud(word, n, random.order = FALSE, max.words = 50, colors = pal) :
  visakhapatnam could not be fit on page. It will not be plotted.

Exporting files


write_csv(table_1, "table_1.csv") # Exporting table 1
write_csv(wos_scopus_total_country, "table_2_.csv")  # Exporting table 2
write_csv(wos_scopus_authors, "table_3.csv") # Exporting table 3
write_csv(wos_scopus_total_journal, "table_4.csv") # Exporting table 4


write_csv(languages, "figure_1.csv") # Exporting data figure 1 
write_csv(figure_2_data, "figure_2.csv") # Exporting data figure 2

write.graph(wos_scopus_citation_network, "citation_network_full.graphml", "graphml") # Exporting graph
write.graph(wos_scopus_citation_network_clusters, 
            "wos_scopus_citation_network_clusters.graphml", 
            "graphml")

write.csv(tree_of_science, "tree_of_science.csv") # Exporting Tree of Science

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
write.csv(cluster_4_df, "cluster_4.csv") # Exporting cluster 4

write.csv(nodes_full_data, "nodes_full_data.csv") # Exporting all nodes
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCmVkaXRvcl9vcHRpb25zOiAKICBjaHVua19vdXRwdXRfdHlwZTogaW5saW5lCi0tLQoKIyBDcmVhdGluZyB0aGUgZW52aXJvbm1lbnQKCmBgYHtyfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeSh0b3NyKQpsaWJyYXJ5KGJpYmxpb21ldHJpeCkKbGlicmFyeShsdWJyaWRhdGUpCmxpYnJhcnkoaWdyYXBoKQpsaWJyYXJ5KHRpZHl0ZXh0KQpsaWJyYXJ5KHdvcmRjbG91ZCkKbGlicmFyeShyZWJ1cykKbGlicmFyeShnZ3JlcGVsKSAjIGltcHJvdmUgZG9udXQgdmlzdWFsaXphdGlvbgpgYGAKClRoaXMgdGVtcGxhdGUgaXMgYmFzZWQgaW4gdGhpcyBwYXBlcgoKaHR0cHM6Ly9yZXZpc3Rhcy51Y20uZXMvaW5kZXgucGhwL1JFVkUvYXJ0aWNsZS92aWV3Lzc1NTY2LzQ1NjQ0NTY1NTc0NjcKCkZvciBhIGRldGFpbCBleHBsYW5hdGlvbiBvZiBob3cgdG8gdXNlIGl0LCBwbGVhc2Ugd2F0Y2ggdGhpcyB2aWRlbyAKCmh0dHBzOi8vd3d3LnlvdXR1YmUuY29tL3dhdGNoP3Y9anRLU2lmdk52VE0KCgojIERhdGEgZ2V0dGluZwoKYGBge3J9Cndvc19zY29wdXNfdG9zIDwtIAogIHRvc3I6OnRvc3JfbG9hZCgic2NvcHVzLmJpYiIsICAjIENyZWF0ZSBkYXRhIGZyb20gc2VhcmNoZXMgICAKICAgICAgICAgICAgICAgICAgIldvUy50eHQiKQoKdHJlZV9vZl9zY2llbmNlIDwtIAogIHRvc3I6OnRvc1IoInNjb3B1cy5iaWIiLCAgIyBDcmVhdGUgZGF0YSBmcm9tIHNlYXJjaGVzICAgCiAgICAgICAgICAgICAiV29TLnR4dCIpCgp3b3MgPC0gCiAgYmlibGlvbWV0cml4Ojpjb252ZXJ0MmRmKGMoIldvUy50eHQiKSkgICMgY3JlYXRlIGRhdGFmcmFtZSBmcm9tIHdvcyBmaWxlCgpzY29wdXMgPC0gCiAgYmlibGlvbWV0cml4Ojpjb252ZXJ0MmRmKCJzY29wdXMuYmliIiwgIyBDcmVhdGUgZGF0YWZyYW1lIGZyb20gc2NvcHVzIGZpbGUKICAgICAgICAgICAgICAgICAgICAgICAgICAgZGJzb3VyY2UgPSAic2NvcHVzIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGZvcm1hdCA9ICJiaWJ0ZXgiKQpgYGAKCiMjIFRhYmxlIDEuIFNlYXJjaCBDcml0ZXJpYQoKYGBge3J9CnRhYmxlXzEgPC0gCiAgdGliYmxlKHdvcyA9IGxlbmd0aCh3b3MkU1IpLCAjIENyZWF0ZSBhIGRhdGFmcmFtZSB3aXRoIHRoZSB2YWx1ZXMuCiAgICAgICAgIHNjb3B1cyA9IGxlbmd0aChzY29wdXMkU1IpLCAKICAgICAgICAgdG90YWwgPSBsZW5ndGgod29zX3Njb3B1c190b3MkZGYkU1IpKQp0YWJsZV8xCmBgYAoKIyMgRmlndXJlIDEuIExhbmd1YWdlcwoKYGBge3J9Cm1haW5fbGFuZ3VhZ2VzIDwtIAogIHdvc19zY29wdXNfdG9zJGRmIHw+IAogIHNlbGVjdChMQSkgfD4gCiAgc2VwYXJhdGVfcm93cyhMQSwgc2VwID0gIjsgIikgfD4gCiAgY291bnQoTEEsIHNvcnQgPSBUUlVFKSB8PiAKICBzbGljZSgxOjUpCgpvdGhlcl9sYW5ndWFnZXMgPC0gCiAgd29zX3Njb3B1c190b3MkZGYgfD4gCiAgc2VwYXJhdGVfcm93cyhMQSwgc2VwID0gIjsgIikgfD4gCiAgc2VsZWN0KExBKSB8PiAKICBjb3VudChMQSwgc29ydCA9IFRSVUUpIHw+IAogIHNsaWNlKDY6bikgfD4gCiAgc3VtbWFyaXNlKG4gPSBzdW0obikpIHw+IAogIG11dGF0ZShMQSA9ICJPVEhFUlMiKSB8PiAKICBzZWxlY3QoTEEsIG4pCgpsYW5ndWFnZXMgPC0gCiAgbWFpbl9sYW5ndWFnZXMgfD4gCiAgYmluZF9yb3dzKG90aGVyX2xhbmd1YWdlcykgfD4gCiAgbXV0YXRlKHBlcmNlbnRhZ2UgPSBuIC8gc3VtKG4pLAogICAgICAgICBwZXJjZW50YWdlID0gcm91bmQocGVyY2VudGFnZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaWdpdHMgPSAyKSApIHw+IAogIHJlbmFtZShsYW5ndWFnZSA9IExBKSB8PgogIHNlbGVjdChsYW5ndWFnZSwgcGVyY2VudGFnZSwgY291bnQgPSBuKQoKbGFuZ3VhZ2VzCmBgYAoKCmBgYHtyfQpkZiA8LSBsYW5ndWFnZXMgfD4gCiAgcmVuYW1lKHZhbHVlID0gcGVyY2VudGFnZSwgZ3JvdXAgPSBsYW5ndWFnZSkgfD4KICBtdXRhdGUodmFsdWUgPSB2YWx1ZSAqIDEwMCkgfD4gCiAgc2VsZWN0KHZhbHVlLCBncm91cCkKCmRmMiA8LSBkZiAlPiUgCiAgbXV0YXRlKGNzdW0gPSByZXYoY3Vtc3VtKHJldih2YWx1ZSkpKSwgCiAgICAgICAgIHBvcyA9IHZhbHVlLzIgKyBsZWFkKGNzdW0sIDEpLAogICAgICAgICBwb3MgPSBpZl9lbHNlKGlzLm5hKHBvcyksIHZhbHVlLzIsIHBvcykpCgpnZ3Bsb3QoZGYsIGFlcyh4ID0gMiAsIHkgPSB2YWx1ZSwgZmlsbCA9IGZjdF9pbm9yZGVyKGdyb3VwKSkpICsKICBnZW9tX2NvbCh3aWR0aCA9IDEsIGNvbG9yID0gMSkgKwogIGNvb3JkX3BvbGFyKHRoZXRhID0gInkiKSArCiAgZ2VvbV9sYWJlbF9yZXBlbChkYXRhID0gZGYyLAogICAgICAgICAgICAgICAgICAgYWVzKHkgPSBwb3MsIGxhYmVsID0gcGFzdGUwKHZhbHVlLCAiJSIpKSwKICAgICAgICAgICAgICAgICAgIHNpemUgPSA0LjUsIG51ZGdlX3ggPSAxLCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgdGhlbWUocGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpY2tzID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgc2l6ZSA9IDE4KSkgKwogIGxhYnModGl0bGUgPSAiTGFuZ3VhZ2VzIikgKwogIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKHRpdGxlID0gIiIpKSArCiAgdGhlbWVfdm9pZCgpICsKICB4bGltKDAuNSwgMi41KQpgYGAKCiMjIEZpZ3VyZSAyLiBTY2llbnRpZmljIFByb2R1Y3Rpb24KCmBgYHtyfQp3b3NfYW51YWxfcHJvZHVjdGlvbiA8LSAKICB3b3MgfD4gCiAgc2VsZWN0KFBZKSB8PiAKICBjb3VudChQWSwgc29ydCA9IFRSVUUpIHw+IAogIG5hLm9taXQoKSB8PiAKICBmaWx0ZXIoUFkgPj0gMjAwMCwKICAgICAgICAgUFkgPCB5ZWFyKHRvZGF5KCkpKSB8PiAKICBtdXRhdGUocmVmX3R5cGUgPSAid29zIikKCnNjb3B1c19hbnVhbF9wcm9kdWN0aW9uICA8LSAKICBzY29wdXMgfD4gCiAgc2VsZWN0KFBZKSB8PiAKICBjb3VudChQWSwgc29ydCA9IFRSVUUpIHw+IAogIG5hLm9taXQoKSB8PiAKICBmaWx0ZXIoUFkgPj0gMjAwMCwKICAgICAgICAgUFkgPCB5ZWFyKHRvZGF5KCkpKSB8PgogIG11dGF0ZShyZWZfdHlwZSA9ICJzY29wdXMiKQoKdG90YWxfYW51YWxfcHJvZHVjdGlvbiA8LSAKICB3b3Nfc2NvcHVzX3RvcyRkZiB8PiAKICBzZWxlY3QoUFkpIHw+IAogIGNvdW50KFBZLCBzb3J0ID0gVFJVRSkgfD4gCiAgbmEub21pdCgpIHw+IAogIGZpbHRlcihQWSA+PSAyMDAwLAogICAgICAgICBQWSA8IHllYXIodG9kYXkoKSkpIHw+CiAgbXV0YXRlKHJlZl90eXBlID0gInRvdGFsIikKCndvc19zY29wdXNfdG90YWxfYW5udWFsX3Byb2R1Y3Rpb24gPC0gCiAgd29zX2FudWFsX3Byb2R1Y3Rpb24gfD4gCiAgYmluZF9yb3dzKHNjb3B1c19hbnVhbF9wcm9kdWN0aW9uLAogICAgICAgICAgICB0b3RhbF9hbnVhbF9wcm9kdWN0aW9uKSAKCmZpZ3VyZV8yX2RhdGEgPC0gCiAgd29zX3Njb3B1c190b3RhbF9hbm51YWxfcHJvZHVjdGlvbiB8PiAKICBtdXRhdGUoUFkgPSByZXBsYWNlX25hKFBZLCByZXBsYWNlID0gMCkpIHw+IAogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSByZWZfdHlwZSwgCiAgICAgICAgICAgICAgdmFsdWVzX2Zyb20gPSBuKSB8PiAKICBhcnJhbmdlKGRlc2MoUFkpKQoKZmlndXJlXzJfZGF0YSAKYGBgCgpgYGB7cn0Kd29zX3Njb3B1c190b3RhbF9hbm51YWxfcHJvZHVjdGlvbiB8PiAKICBnZ3Bsb3QoYWVzKHggPSBQWSwgeSA9IG4sIGNvbG9yID0gcmVmX3R5cGUpKSArCiAgZ2VvbV9saW5lKCkgKwogIGxhYnModGl0bGUgPSAiQW5udWFsIFNjaWVudGlmaWMgUHJvZHVjdGlvbiIsIAogICAgICAgeCA9ICJ5ZWFycyIsCiAgICAgICB5ID0gInBhcGVycyIpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkgCmBgYAoKIyMgVGFibGUgMi4gQ291bnRyeSBwcm9kdWN0aW9uCgpgYGB7cn0KZGF0YV9iaWJsaW9fd29zIDwtIGJpYmxpb0FuYWx5c2lzKHdvcykKCndvc19jb3VudHJ5IDwtIAogIGRhdGFfYmlibGlvX3dvcyRDb3VudHJpZXMgfD4gCiAgZGF0YS5mcmFtZSgpIHw+IAogIG11dGF0ZShkYXRhYmFzZSA9ICJ3b3MiKSB8PiAKICBzZWxlY3QoY291bnRyeSA9IFRhYiwgcGFwZXJzID0gRnJlcSwgZGF0YWJhc2UgKSB8PiAKICBhcnJhbmdlKGRlc2MocGFwZXJzKSkgCgpkYXRhX2JpYmxpb19zY29wdXMgPC0gYmlibGlvQW5hbHlzaXMoc2NvcHVzKQoKc2NvcHVzX2NvdW50cnkgPC0gCiAgZGF0YV9iaWJsaW9fc2NvcHVzJENvdW50cmllcyB8PiAKICBkYXRhLmZyYW1lKCkgfD4gCiAgbXV0YXRlKGRhdGFiYXNlID0gInNjb3B1cyIpIHw+IAogIHNlbGVjdChjb3VudHJ5ID0gVGFiLCBwYXBlcnMgPSBGcmVxLCBkYXRhYmFzZSApIHw+IAogIGFycmFuZ2UoZGVzYyhwYXBlcnMpKSAKCmRhdGFfYmlibGlvX3RvdGFsIDwtIGJpYmxpb0FuYWx5c2lzKHdvc19zY29wdXNfdG9zJGRmKQoKdG90YWxfY291bnRyeSA8LSAKICBkYXRhX2JpYmxpb190b3RhbCRDb3VudHJpZXMgfD4gCiAgZGF0YS5mcmFtZSgpIHw+IAogIG11dGF0ZShkYXRhYmFzZSA9ICJ0b3RhbCIpIHw+IAogIHNlbGVjdChjb3VudHJ5ID0gVGFiLCBwYXBlcnMgPSBGcmVxLCBkYXRhYmFzZSApIHw+IAogIGFycmFuZ2UoZGVzYyhwYXBlcnMpKSAKCndvc19zY29wdXNfdG90YWxfY291bnRyeSA8LSAKICB3b3NfY291bnRyeSB8PiAKICBiaW5kX3Jvd3Moc2NvcHVzX2NvdW50cnksIAogICAgICAgICAgICB0b3RhbF9jb3VudHJ5KSB8PiAKICBtdXRhdGUoY291bnRyeSA9IGFzLmNoYXJhY3Rlcihjb3VudHJ5KSkgfD4gCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IGRhdGFiYXNlLCAKICAgICAgICAgICAgICB2YWx1ZXNfZnJvbSA9IHBhcGVycykgfD4gCiAgYXJyYW5nZShkZXNjKHRvdGFsKSkgfD4gCiAgc2xpY2UoMToxMCkgfD4gCiAgbXV0YXRlKHBlcmNlbnRhZ2UgPSB0b3RhbCAvICh0YWJsZV8xIHw+IHB1bGwodG90YWwpKSwKICAgICAgICAgcGVyY2VudGFnZSA9IHJvdW5kKHBlcmNlbnRhZ2UsIGRpZ2l0cyA9IDIpKQoKd29zX3Njb3B1c190b3RhbF9jb3VudHJ5CmBgYAoKIyMgVGFibGUgMy4gQXV0aG9yIHByb2R1Y3Rpb24KCmBgYHtyfQp3b3NfYXV0aG9ycyA8LSAKICBkYXRhX2JpYmxpb193b3MkQXV0aG9ycyB8PiAKICBkYXRhLmZyYW1lKCkgfD4gCiAgcmVuYW1lKGF1dGhvcnNfd29zID0gQVUsIHBhcGVyc193b3MgPSBGcmVxKSB8PiAKICBhcnJhbmdlKGRlc2MocGFwZXJzX3dvcykpIHw+IAogIHNsaWNlKDE6MTApIHw+IAogIG11dGF0ZShkYXRhYmFzZV93b3MgPSAid29zIikKCgpzY29wdXNfYXV0aG9ycyA8LSAKICBkYXRhX2JpYmxpb19zY29wdXMkQXV0aG9ycyB8PiAKICBkYXRhLmZyYW1lKCkgfD4gCiAgcmVuYW1lKGF1dGhvcnNfc2NvcHVzID0gQVUsIHBhcGVyc19zY29wdXMgPSBGcmVxKSB8PiAKICBhcnJhbmdlKGRlc2MocGFwZXJzX3Njb3B1cykpIHw+IAogIHNsaWNlKDE6MTApIHw+IAogIG11dGF0ZShkYXRhYmFzZV9zY29wdXMgPSAic2NvcHVzIikKCnRvdGFsX2F1dGhvcnMgPC0gCiAgZGF0YV9iaWJsaW9fdG90YWwkQXV0aG9ycyB8PiAKICBkYXRhLmZyYW1lKCkgfD4gCiAgcmVuYW1lKGF1dGhvcnNfdG90YWwgPSBBVSwgCiAgICAgICAgIHBhcGVyc190b3RhbCA9IEZyZXEpIHw+IAogIGFycmFuZ2UoZGVzYyhwYXBlcnNfdG90YWwpKSB8PiAKICBzbGljZSgxOjEwKSB8PiAKICBtdXRhdGUoZGF0YWJhc2VfdG90YWwgPSAidG90YWwiKQoKd29zX3Njb3B1c19hdXRob3JzIDwtIAogIHdvc19hdXRob3JzIHw+IAogIGJpbmRfY29scyhzY29wdXNfYXV0aG9ycywKICAgICAgICAgICAgdG90YWxfYXV0aG9ycykKCndvc19zY29wdXNfYXV0aG9ycwpgYGAKCiMjIFRhYmxlIDQuIEpvdXJuYWwgcHJvZHVjdGlvbgoKYGBge3J9Cndvc19qb3VybmFsIDwtIAogIHdvcyB8PiAKICBmaWx0ZXIoc3RyX2RldGVjdChEVCwgIkFSVElDTEUiKSkgfD4gCiAgc2VsZWN0KGpvdXJuYWwgPSBTTykgfD4gCiAgbmEub21pdCgpIHw+IAogIGNvdW50KGpvdXJuYWwsIHNvcnQgPSBUUlVFKSB8PiAKICBzbGljZSgxOjIwKSB8PiAKICByZW5hbWUocHVibGljYXRpb25zID0gbikgfD4gCiAgbXV0YXRlKGRhdGFiYXNlID0gIndvcyIpCgpzY29wdXNfam91cm5hbCA8LSAKICBzY29wdXMgfD4gCiAgZmlsdGVyKHN0cl9kZXRlY3QoRFQsICJBUlRJQ0xFIikpIHw+IAogIHNlbGVjdChqb3VybmFsID0gU08pIHw+IAogIG5hLm9taXQoKSB8PiAKICBjb3VudChqb3VybmFsLCBzb3J0ID0gVFJVRSkgfD4gCiAgc2xpY2UoMToyMCkgfD4gCiAgcmVuYW1lKHB1YmxpY2F0aW9ucyA9IG4pIHw+IAogIG11dGF0ZShkYXRhYmFzZSA9ICJzY29wdXMiKQoKdG90YWxfam91cm5hbCA8LSAKICB3b3Nfc2NvcHVzX3RvcyRkZiB8PiAKICBmaWx0ZXIoc3RyX2RldGVjdChEVCwgIkFSVElDTEUiKSkgfD4gCiAgc2VsZWN0KGpvdXJuYWwgPSBTTykgfD4gCiAgbmEub21pdCgpIHw+IAogIGNvdW50KGpvdXJuYWwsIHNvcnQgPSBUUlVFKSB8PiAKICBzbGljZSgxOjIwKSB8PiAKICByZW5hbWUocHVibGljYXRpb25zID0gbikgfD4gCiAgbXV0YXRlKGRhdGFiYXNlID0gInRvdGFsIikKCndvc19zY29wdXNfdG90YWxfam91cm5hbCA8LSAKICB3b3Nfam91cm5hbCB8PiAKICBiaW5kX3Jvd3Moc2NvcHVzX2pvdXJuYWwsIAogICAgICAgICAgICB0b3RhbF9qb3VybmFsKSB8PiAKICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gZGF0YWJhc2UsIAogICAgICAgICAgICAgIHZhbHVlc19mcm9tID0gcHVibGljYXRpb25zKSB8PiAKICBhcnJhbmdlKGRlc2ModG90YWwpKSB8PiAKICBzbGljZSgxOjEwKSB8PiAKICBtdXRhdGUocGVyY2VudGFnZSA9IHRvdGFsIC8gdGFibGVfMSB8PiBwdWxsKHRvdGFsKSwKICAgICAgICAgcGVyY2VudGFnZSA9IHJvdW5kKHBlcmNlbnRhZ2UsIGRpZ2l0cyA9IDIpKQoKCndvc19zY29wdXNfdG90YWxfam91cm5hbApgYGAKCiMjIEZpZ3VyZSAzLiBDby1jaXRhdGlvbiBuZXR3b3JrCgojIyMgQXV0aG9yIGNvLWNpdGF0aW9uIG5ldHdvcmsKCmBgYHtyfQp3b3Nfc2NvcHVzX2F1dGhvcl9tZXRhdGFnIDwtIAogIG1ldGFUYWdFeHRyYWN0aW9uKHdvc19zY29wdXNfdG9zJGRmLCBGaWVsZCA9ICJDUl9BVSIpCgp3b3Nfc2NvcHVzX2F1dGhvcl9jb19jaXRhdGlvbl9tYXRyaXggPC0gCiAgYmlibGlvTmV0d29yayhNID0gd29zX3Njb3B1c19hdXRob3JfbWV0YXRhZywgCiAgICAgICAgICAgICAgICBhbmFseXNpcyA9ICJjby1jaXRhdGlvbiIsIAogICAgICAgICAgICAgICAgbmV0d29yayA9ICJhdXRob3JzIikKCnBsb3RfbmV0X2F1dGhvcl9jb19jaXRhdGlvbiA8LSAKICBuZXR3b3JrUGxvdCh3b3Nfc2NvcHVzX2F1dGhvcl9jb19jaXRhdGlvbl9tYXRyaXgsIAogICAgICAgICAgICAgIHdlaWdodGVkPVQsIAogICAgICAgICAgICAgIG4gPSAzMCwgCiAgICAgICAgICAgICAgVGl0bGUgPSAiQXV0aG9yIENvLWNpdGF0aW9uIE5ldHdvcmsiLCAKICAgICAgICAgICAgICB0eXBlID0gImZydWNodGVybWFuIiwgCiAgICAgICAgICAgICAgc2l6ZT1ULAogICAgICAgICAgICAgIGVkZ2VzaXplID0gNSwKICAgICAgICAgICAgICBsYWJlbHNpemU9MC43KQpgYGAKCiMjIyBBdXRob3IgQ29sbGFib3JhdGlvbiBuZXR3b3JrCgpgYGB7cn0Kd29zX3Njb3B1c19hdXRob3JfY29sbGFiX21hdHJpeCA8LSAKICBiaWJsaW9OZXR3b3JrKE0gPSB3b3Nfc2NvcHVzX3RvcyRkZiwgCiAgICAgICAgICAgICAgICBhbmFseXNpcyA9ICJjb2xsYWJvcmF0aW9uIiwgCiAgICAgICAgICAgICAgICBuZXR3b3JrID0gImF1dGhvcnMiKQoKcGxvdF9hdXRob3JfY29sbGFiIDwtIAogIG5ldHdvcmtQbG90KE5ldE1hdHJpeCA9IHdvc19zY29wdXNfYXV0aG9yX2NvbGxhYl9tYXRyaXgsIAogICAgICAgICAgICAgIHdlaWdodGVkPVQsIG4gPSAzMCwgCiAgICAgICAgICAgICAgVGl0bGUgPSAiQXV0aG9yIENvbGxhYm9yYXRpb24gTmV0d29yayIsIAogICAgICAgICAgICAgIHR5cGUgPSAiZnJ1Y2h0ZXJtYW4iLCAKICAgICAgICAgICAgICBzaXplPVQsCiAgICAgICAgICAgICAgZWRnZXNpemUgPSA1LAogICAgICAgICAgICAgIGxhYmVsc2l6ZT0wLjcpCmBgYAoKIyMjIENvdW50cnkgQ29sbGFib3JhdGlvbiBOZXR3b3JrCgpgYGB7cn0Kd29zX3Njb3B1c19jb3VudHJ5X2NvbGxhYl9tYXRyaXggPC0gCiAgYmlibGlvTmV0d29yayhNID0gd29zX3Njb3B1c190b3MkZGYsIAogICAgICAgICAgICAgICAgYW5hbHlzaXMgPSAiY29sbGFib3JhdGlvbiIsIAogICAgICAgICAgICAgICAgbmV0d29yayA9ICJjb3VudHJpZXMiKQoKcGxvdF9jb3VudHJ5X2NvbGxhYiA8LSAKICBuZXR3b3JrUGxvdCh3b3Nfc2NvcHVzX2NvdW50cnlfY29sbGFiX21hdHJpeCwgCiAgICAgICAgICAgICAgd2VpZ2h0ZWQ9VCwgbiA9IDMwLCAKICAgICAgICAgICAgICBUaXRsZSA9ICJDb3VudHJ5IENvbGxhYm9yYXRpb24gTmV0d29yayIsIAogICAgICAgICAgICAgIHR5cGUgPSAiZnJ1Y2h0ZXJtYW4iLCAKICAgICAgICAgICAgICBzaXplPVQsCiAgICAgICAgICAgICAgZWRnZXNpemUgPSA1LAogICAgICAgICAgICAgIGxhYmVsc2l6ZT0wLjcpCmBgYAoKIyMjIEtleXdvcmQgY28tY2l0YXRpb24gbmV0d29yawoKYGBge3J9Cndvc19zY29wdXNfa2V5d29yZF9jb19vY2N1cnJlbmNlX21hdHJpeCA8LSAKICBiaWJsaW9OZXR3b3JrKE0gPSB3b3Nfc2NvcHVzX3RvcyRkZiwgCiAgICAgICAgICAgICAgICBhbmFseXNpcyA9ICJjby1vY2N1cnJlbmNlcyIsIAogICAgICAgICAgICAgICAgbmV0d29yayA9ICJrZXl3b3JkcyIsIAogICAgICAgICAgICAgICAgc2VwID0gIjsiKQoKcGxvdF9uZXRfY29fb2NjdXJyZW5jZSA8LSAKICBuZXR3b3JrUGxvdCh3b3Nfc2NvcHVzX2tleXdvcmRfY29fb2NjdXJyZW5jZV9tYXRyaXgsIAogICAgICAgICAgICAgIHdlaWdodGVkPVQsIG4gPSAzMCwgCiAgICAgICAgICAgICAgVGl0bGUgPSAiS2V5d29yZCBDby1vY2N1cnJlbmNlIE5ldHdvcmsiLCAKICAgICAgICAgICAgICB0eXBlID0gImZydWNodGVybWFuIiwgCiAgICAgICAgICAgICAgc2l6ZT1ULAogICAgICAgICAgICAgIGVkZ2VzaXplID0gNSwKICAgICAgICAgICAgICBsYWJlbHNpemU9MC43KQpgYGAKCiMjIEZpZ3VyZSA0LiBUcmVlIG9mIFNjaWVuY2UKCiMjIyBUcmVlIG9mIFNjaWVuY2UKCmBgYHtyfQp0cmVlX29mX3NjaWVuY2UKYGBgCgojIyMgQ2x1c3RlcmluZyBhbmFseXNpcwoKRmluZGluZyB0aGUgY2x1c3RlcnMKCmBgYHtyfQpub2RlcyA8LSAgIyBDcmVhdGUgYSBkYXRhZnJhbWUgd2l0aCB0aGUgZnVsbG5hbWUgb2YgYXJ0aWNsZXMgCiAgdGliYmxlKG5hbWUgPSBWKHdvc19zY29wdXNfdG9zJGdyYXBoKSRuYW1lKSB8PiAKICBsZWZ0X2pvaW4od29zX3Njb3B1c190b3Mkbm9kZXMsIAogICAgICAgICAgICBieSA9IGMoIm5hbWUiID0gIklEX1RPUyIpKQoKd29zX3Njb3B1c19jaXRhdGlvbl9uZXR3b3JrXzEgPC0gIyBBZGQgdGhlIGFydGljbGUgbmFtZXMgdG8gdGhlIGNpdGF0aW9uIG5ldHdvcmsKICB3b3Nfc2NvcHVzX3RvcyRncmFwaCB8PiAKICBpZ3JhcGg6OnNldC52ZXJ0ZXguYXR0cmlidXRlKG5hbWUgPSAiZnVsbF9uYW1lIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbmRleCA9IFYod29zX3Njb3B1c190b3MkZ3JhcGgpJG5hbWUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsdWUgPSBub2RlcyRDSVRFKQoKbm9kZXNfMSA8LSAjIENyZWF0ZSBhIGRhdGFmcmFtZSB3aXRoIHN1YmZpZWxkcyAoY2x1c3RlcnMpCiAgdGliYmxlKG5hbWUgPSBWKHdvc19zY29wdXNfY2l0YXRpb25fbmV0d29ya18xKSRuYW1lLAogICAgICAgICBjbHVzdGVyID0gVih3b3Nfc2NvcHVzX2NpdGF0aW9uX25ldHdvcmtfMSkkc3ViZmllbGQsCiAgICAgICAgIGZ1bGxfbmFtZSA9IFYod29zX3Njb3B1c19jaXRhdGlvbl9uZXR3b3JrXzEpJGZ1bGxfbmFtZSkKCm5vZGVzXzIgPC0gIyBDb3VudCB0aGUgbnVtYmVyIG9mIGFydGljbGVzIHBlciBjbHVzdGVyCiAgbm9kZXNfMSB8PiAKICBjb3VudChjbHVzdGVyLCBzb3J0ID0gVFJVRSkgfD4gCiAgbXV0YXRlKGNsdXN0ZXJfMSA9IHJvd19udW1iZXIoKSkgfD4gCiAgc2VsZWN0KGNsdXN0ZXIsIGNsdXN0ZXJfMSkKCm5vZGVzXzMgPC0gCiAgbm9kZXNfMSB8PiAKICBsZWZ0X2pvaW4obm9kZXNfMikgfD4gCiAgcmVuYW1lKHN1YmZpZWxkID0gY2x1c3Rlcl8xKSB8PiAKICBzZWxlY3QobmFtZSwgZnVsbF9uYW1lLCBzdWJmaWVsZCkKCmVkZ2VfbGlzdCA8LSAKICBnZXQuZWRnZWxpc3Qod29zX3Njb3B1c19jaXRhdGlvbl9uZXR3b3JrXzEpIHw+IAogIGRhdGEuZnJhbWUoKSB8PiAKICByZW5hbWUoU291cmNlID0gWDEsIFRhcmdldCA9IFgyKQoKd29zX3Njb3B1c19jaXRhdGlvbl9uZXR3b3JrIDwtIAogIGdyYXBoLmRhdGEuZnJhbWUoZCA9IGVkZ2VfbGlzdCwgCiAgICAgICAgICAgICAgICAgICBkaXJlY3RlZCA9IFRSVUUsIAogICAgICAgICAgICAgICAgICAgdmVydGljZXMgPSBub2Rlc18zKQoKd29zX3Njb3B1c19jaXRhdGlvbl9uZXR3b3JrIHw+IAogIHN1bW1hcnkoKQpgYGAKCkNob29zaW5nIGNsdXN0ZXJzCgpXZSBwcm9wb3NlZCB0aGUgdGlwcGluZyBwb2ludCBvcHRpb24gdG8gY2hvb3NlIHRoZSBudW1iZXIgb2YgY2x1c3RlcnMuIFNlZSB0aGlzIHBhcGVyOgoKaHR0cHM6Ly93d3cubmF0dXJlLmNvbS9hcnRpY2xlcy9zNDE1OTgtMDIxLTg1MDQxLTgKCmBgYHtyfQpjbHVzdGVycyA8LSAKICB0aWJibGUoY2x1c3RlciA9IFYod29zX3Njb3B1c19jaXRhdGlvbl9uZXR3b3JrKSRzdWJmaWVsZCkgfD4gCiAgY291bnQoY2x1c3Rlciwgc29ydCA9IFRSVUUpCgpjbHVzdGVycyB8PiAKICBnZ3Bsb3QoYWVzKHggPSByZW9yZGVyKGNsdXN0ZXIsIG4pLCB5ID0gbikpICsKICBnZW9tX3BvaW50KCkgCmBgYAoKUmVtb3Zpbmcgbm90IGNob3NlbiBjbHVzdGVycwoKYGBge3J9Cndvc19zY29wdXNfY2l0YXRpb25fbmV0d29ya19jbHVzdGVycyA8LSAKICB3b3Nfc2NvcHVzX2NpdGF0aW9uX25ldHdvcmsgfD4gCiAgZGVsZXRlLnZlcnRpY2VzKHdoaWNoKFYod29zX3Njb3B1c19jaXRhdGlvbl9uZXR3b3JrKSRzdWJmaWVsZCAhPSAxICYgIyBmaWx0ZXIgY2x1c3RlcnMgCiAgICAgICAgICAgICAgICAgICAgICAgICAgVih3b3Nfc2NvcHVzX2NpdGF0aW9uX25ldHdvcmspJHN1YmZpZWxkICE9IDIgJgogICAgICAgICAgICAgICAgICAgICAgICAgIFYod29zX3Njb3B1c19jaXRhdGlvbl9uZXR3b3JrKSRzdWJmaWVsZCAhPSAzICAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgVih3b3Nfc2NvcHVzX2NpdGF0aW9uX25ldHdvcmspJHN1YmZpZWxkICE9IDQpKQoKd29zX3Njb3B1c19jaXRhdGlvbl9uZXR3b3JrX2NsdXN0ZXJzIHw+IAogIHN1bW1hcnkoKQpgYGAKCiMjIyBDbHVzdGVyIDEKCmBgYHtyfQpwYWwgPC0gYnJld2VyLnBhbCg4LCJEYXJrMiIpCgpub2Rlc19mdWxsX2RhdGEgPC0gCiAgdGliYmxlKG5hbWUgPSBWKHdvc19zY29wdXNfY2l0YXRpb25fbmV0d29yaykkbmFtZSwKICAgICAgICAgY2x1c3RlciA9IFYod29zX3Njb3B1c19jaXRhdGlvbl9uZXR3b3JrKSRzdWJmaWVsZCwKICAgICAgICAgZnVsbF9uYW1lID0gVih3b3Nfc2NvcHVzX2NpdGF0aW9uX25ldHdvcmspJGZ1bGxfbmFtZSkKCmNsdXN0ZXJfMSA8LSAKICB3b3Nfc2NvcHVzX2NpdGF0aW9uX25ldHdvcmsgfD4gCiAgZGVsZXRlLnZlcnRpY2VzKHdoaWNoKFYod29zX3Njb3B1c19jaXRhdGlvbl9uZXR3b3JrKSRzdWJmaWVsZCAhPSAxKSkKCmNsdXN0ZXJfMV9wYWdlX3JhbmsgPC0gCiAgY2x1c3Rlcl8xIHw+IAogIHNldC52ZXJ0ZXguYXR0cmlidXRlKG5hbWUgPSAicGFnZV9yYW5rIiwgCiAgICAgICAgICAgICAgICAgICAgICAgdmFsdWUgPSBwYWdlX3JhbmsoY2x1c3Rlcl8xKSR2ZWN0b3IpCgpjbHVzdGVyXzFfZGYgPC0gCiAgdGliYmxlKG5hbWUgPSBWKGNsdXN0ZXJfMV9wYWdlX3JhbmspJG5hbWUsCiAgICAgICAgIGZ1bGxfbmFtZSA9IFYoY2x1c3Rlcl8xX3BhZ2VfcmFuaykkZnVsbF9uYW1lLAogICAgICAgICBwYWdlX3JhbmsgPSBWKGNsdXN0ZXJfMV9wYWdlX3JhbmspJHBhZ2VfcmFuaywKICAgICAgICAgY2x1c3RlciA9IFYoY2x1c3Rlcl8xX3BhZ2VfcmFuaykkc3ViZmllbGQsKQoKbm9kZXNfZnVsbF9kYXRhIHw+IAogIGZpbHRlcihjbHVzdGVyID09IDEpIHw+IAogIHNlbGVjdChmdWxsX25hbWUpIHw+IAogIG11dGF0ZShmdWxsX25hbWUgPSBzdHJfZXh0cmFjdChmdWxsX25hbWUsIFNQQyAlUiUgICMgUmVndWxhciBleHByZXNzaW9ucyAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvbmVfb3JfbW9yZShXUkQpICVSJSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBTUEMgJVIlIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9uZV9vcl9tb3JlKG9yKFdSRCwgQU5ZX0NIQVIpKSksCiAgICAgICAgIGZ1bGxfbmFtZSA9IHN0cl9yZW1vdmUoZnVsbF9uYW1lLCBPUEVOX1BBUkVOICVSJSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcGVhdGVkKERHVCwgNCkgJVIlIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQ0xPU0VfUEFSRU4gJVIlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvbmVfb3JfbW9yZShvcihXUkQsQU5ZX0NIQVIpKSksCiAgICAgICAgIGZ1bGxfbmFtZSA9IHN0cl90cmltKGZ1bGxfbmFtZSkpICB8PiAKICB1bm5lc3RfdG9rZW5zKG91dHB1dCA9IHdvcmQsIGlucHV0ID0gZnVsbF9uYW1lKSB8PiAjIFRva2VuaXphdGlvbgogIGFudGlfam9pbihzdG9wX3dvcmRzKSB8PiAgIyBSZW1vdmluZyBzdG9wIHdvcmRzCiAgZmlsdGVyKHdvcmQgIT0gImRvaSIsCiAgICAgICAgICFzdHJfZGV0ZWN0KHdvcmQsICJbMC05XSIpKSB8PiAgIyBXb1MgZGF0YQogIGZpbHRlcih3b3JkID09IHN0cl9yZW1vdmUod29yZCwgcGF0dGVybiA9ICJjaXRhdGlvbiIpLAogICAgICAgICB3b3JkID09IHN0cl9yZW1vdmUod29yZCwgcGF0dGVybiA9ICJpbmRleCIpLCAgIyBXb3JkcyByZW1vdmVkCiAgICAgICAgIHdvcmQgPT0gc3RyX3JlbW92ZSh3b3JkLCBwYXR0ZXJuID0gIndhdGVyIiksIAogICAgICAgICB3b3JkID09IHN0cl9yZW1vdmUod29yZCwgcGF0dGVybiA9ICJnaXMiKSwKICAgICAgICAgd29yZCA9PSBzdHJfcmVtb3ZlKHdvcmQsIHBhdHRlcm4gPSAicXVhbGl0eSIpLAogICAgICAgICB3b3JkID09IHN0cl9yZW1vdmUod29yZCwgcGF0dGVybiA9ICJncm91bmR3YXRlciIpKSB8PgogIGNvdW50KHdvcmQsIHNvcnQgPSBUUlVFKSB8PiAKICB3aXRoKHdvcmRjbG91ZCh3b3JkLCAKICAgICAgICAgICAgICAgICBuLCAKICAgICAgICAgICAgICAgICByYW5kb20ub3JkZXIgPSBGQUxTRSwgCiAgICAgICAgICAgICAgICAgbWF4LndvcmRzID0gNTAsIAogICAgICAgICAgICAgICAgIGNvbG9ycz1wYWwpKQpgYGAKCiMjIyBDbHVzdGVyIDIKCmBgYHtyfQpjbHVzdGVyXzIgPC0gCiAgd29zX3Njb3B1c19jaXRhdGlvbl9uZXR3b3JrIHw+IAogIGRlbGV0ZS52ZXJ0aWNlcyh3aGljaChWKHdvc19zY29wdXNfY2l0YXRpb25fbmV0d29yaykkc3ViZmllbGQgIT0gMikpCgpjbHVzdGVyXzJfcGFnZV9yYW5rIDwtIAogIGNsdXN0ZXJfMiB8PiAKICBzZXQudmVydGV4LmF0dHJpYnV0ZShuYW1lID0gInBhZ2VfcmFuayIsIAogICAgICAgICAgICAgICAgICAgICAgIHZhbHVlID0gcGFnZV9yYW5rKGNsdXN0ZXJfMikkdmVjdG9yKQoKY2x1c3Rlcl8yX2RmIDwtIAogIHRpYmJsZShuYW1lID0gVihjbHVzdGVyXzJfcGFnZV9yYW5rKSRuYW1lLAogICAgICAgICBmdWxsX25hbWUgPSBWKGNsdXN0ZXJfMl9wYWdlX3JhbmspJGZ1bGxfbmFtZSwKICAgICAgICAgcGFnZV9yYW5rID0gVihjbHVzdGVyXzJfcGFnZV9yYW5rKSRwYWdlX3JhbmssCiAgICAgICAgIGNsdXN0ZXIgPSBWKGNsdXN0ZXJfMl9wYWdlX3JhbmspJHN1YmZpZWxkLCkKCm5vZGVzX2Z1bGxfZGF0YSB8PiAKICBmaWx0ZXIoY2x1c3RlciA9PSAyKSB8PiAKICBzZWxlY3QoZnVsbF9uYW1lKSB8PiAKICBtdXRhdGUoZnVsbF9uYW1lID0gc3RyX2V4dHJhY3QoZnVsbF9uYW1lLCBTUEMgJVIlICAjIFJlZ3VsYXIgZXhwcmVzc2lvbnMgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb25lX29yX21vcmUoV1JEKSAlUiUgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgU1BDICVSJSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvbmVfb3JfbW9yZShvcihXUkQsIEFOWV9DSEFSKSkpLAogICAgICAgICBmdWxsX25hbWUgPSBzdHJfcmVtb3ZlKGZ1bGxfbmFtZSwgT1BFTl9QQVJFTiAlUiUgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXBlYXRlZChER1QsIDQpICVSJSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIENMT1NFX1BBUkVOICVSJQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb25lX29yX21vcmUob3IoV1JELEFOWV9DSEFSKSkpLAogICAgICAgICBmdWxsX25hbWUgPSBzdHJfdHJpbShmdWxsX25hbWUpKSAgfD4gCiAgdW5uZXN0X3Rva2VucyhvdXRwdXQgPSB3b3JkLCBpbnB1dCA9IGZ1bGxfbmFtZSkgfD4gCiAgYW50aV9qb2luKHN0b3Bfd29yZHMpIHw+CiAgZmlsdGVyKHdvcmQgIT0gImRvaSIsCiAgICAgICAgICFzdHJfZGV0ZWN0KHdvcmQsICJbMC05XSIpKSB8PiAgIyBXb1MgZGF0YQogIGZpbHRlcih3b3JkID09IHN0cl9yZW1vdmUod29yZCwgcGF0dGVybiA9ICJjaXRhdGlvbiIpLAogICAgICAgICB3b3JkID09IHN0cl9yZW1vdmUod29yZCwgcGF0dGVybiA9ICJpbmRleCIpLCAKICAgICAgICAgd29yZCA9PSBzdHJfcmVtb3ZlKHdvcmQsIHBhdHRlcm4gPSAid2F0ZXIiKSwgCiAgICAgICAgIHdvcmQgPT0gc3RyX3JlbW92ZSh3b3JkLCBwYXR0ZXJuID0gImdpcyIpLAogICAgICAgICB3b3JkID09IHN0cl9yZW1vdmUod29yZCwgcGF0dGVybiA9ICJxdWFsaXR5IiksCiAgICAgICAgIHdvcmQgPT0gc3RyX3JlbW92ZSh3b3JkLCBwYXR0ZXJuID0gImdyb3VuZHdhdGVyIikpIHw+CiAgY291bnQod29yZCwgc29ydCA9IFRSVUUpIHw+IAogIHdpdGgod29yZGNsb3VkKHdvcmQsIAogICAgICAgICAgICAgICAgIG4sIAogICAgICAgICAgICAgICAgIHJhbmRvbS5vcmRlciA9IEZBTFNFLCAKICAgICAgICAgICAgICAgICBtYXgud29yZHMgPSA1MCwgCiAgICAgICAgICAgICAgICAgY29sb3JzPXBhbCkpCmBgYAoKIyMjIENsdXN0ZXIgMwoKYGBge3J9CgpjbHVzdGVyXzMgPC0gCiAgd29zX3Njb3B1c19jaXRhdGlvbl9uZXR3b3JrIHw+IAogIGRlbGV0ZS52ZXJ0aWNlcyh3aGljaChWKHdvc19zY29wdXNfY2l0YXRpb25fbmV0d29yaykkc3ViZmllbGQgIT0gMykpCgpjbHVzdGVyXzNfcGFnZV9yYW5rIDwtIAogIGNsdXN0ZXJfMyB8PiAKICBzZXQudmVydGV4LmF0dHJpYnV0ZShuYW1lID0gInBhZ2VfcmFuayIsIAogICAgICAgICAgICAgICAgICAgICAgIHZhbHVlID0gcGFnZV9yYW5rKGNsdXN0ZXJfMykkdmVjdG9yKQoKY2x1c3Rlcl8zX2RmIDwtIAogIHRpYmJsZShuYW1lID0gVihjbHVzdGVyXzNfcGFnZV9yYW5rKSRuYW1lLAogICAgICAgICBmdWxsX25hbWUgPSBWKGNsdXN0ZXJfM19wYWdlX3JhbmspJGZ1bGxfbmFtZSwKICAgICAgICAgcGFnZV9yYW5rID0gVihjbHVzdGVyXzNfcGFnZV9yYW5rKSRwYWdlX3JhbmssCiAgICAgICAgIGNsdXN0ZXIgPSBWKGNsdXN0ZXJfM19wYWdlX3JhbmspJHN1YmZpZWxkLCkKCm5vZGVzX2Z1bGxfZGF0YSB8PiAKICBmaWx0ZXIoY2x1c3RlciA9PSAzKSB8PiAKICBzZWxlY3QoZnVsbF9uYW1lKSB8PiAKICBtdXRhdGUoZnVsbF9uYW1lID0gc3RyX2V4dHJhY3QoZnVsbF9uYW1lLCBTUEMgJVIlICAjIFJlZ3VsYXIgZXhwcmVzc2lvbnMgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb25lX29yX21vcmUoV1JEKSAlUiUgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgU1BDICVSJSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvbmVfb3JfbW9yZShvcihXUkQsIEFOWV9DSEFSKSkpLAogICAgICAgICBmdWxsX25hbWUgPSBzdHJfcmVtb3ZlKGZ1bGxfbmFtZSwgT1BFTl9QQVJFTiAlUiUgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXBlYXRlZChER1QsIDQpICVSJSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIENMT1NFX1BBUkVOICVSJQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb25lX29yX21vcmUob3IoV1JELEFOWV9DSEFSKSkpLAogICAgICAgICBmdWxsX25hbWUgPSBzdHJfdHJpbShmdWxsX25hbWUpKSAgfD4gCiAgdW5uZXN0X3Rva2VucyhvdXRwdXQgPSB3b3JkLCBpbnB1dCA9IGZ1bGxfbmFtZSkgfD4gCiAgYW50aV9qb2luKHN0b3Bfd29yZHMpIHw+CiAgZmlsdGVyKHdvcmQgIT0gImRvaSIsCiAgICAgICAgICFzdHJfZGV0ZWN0KHdvcmQsICJbMC05XSIpKSB8PiAgIyBXb1MgZGF0YSAKICBmaWx0ZXIod29yZCA9PSBzdHJfcmVtb3ZlKHdvcmQsIHBhdHRlcm4gPSAiY2l0YXRpb24iKSwKICAgICAgICAgd29yZCA9PSBzdHJfcmVtb3ZlKHdvcmQsIHBhdHRlcm4gPSAiaW5kZXgiKSwgCiAgICAgICAgIHdvcmQgPT0gc3RyX3JlbW92ZSh3b3JkLCBwYXR0ZXJuID0gIndhdGVyIiksIAogICAgICAgICB3b3JkID09IHN0cl9yZW1vdmUod29yZCwgcGF0dGVybiA9ICJnaXMiKSwKICAgICAgICAgd29yZCA9PSBzdHJfcmVtb3ZlKHdvcmQsIHBhdHRlcm4gPSAicXVhbGl0eSIpLAogICAgICAgICB3b3JkID09IHN0cl9yZW1vdmUod29yZCwgcGF0dGVybiA9ICJncm91bmR3YXRlciIpKSB8PgogIGNvdW50KHdvcmQsIHNvcnQgPSBUUlVFKSB8PiAKICB3aXRoKHdvcmRjbG91ZCh3b3JkLCAKICAgICAgICAgICAgICAgICBuLCAKICAgICAgICAgICAgICAgICByYW5kb20ub3JkZXIgPSBGQUxTRSwgCiAgICAgICAgICAgICAgICAgbWF4LndvcmRzID0gNTAsIAogICAgICAgICAgICAgICAgIGNvbG9ycz1wYWwpKQpgYGAKIyMjIENsdXN0ZXIgNAoKYGBge3J9CgpjbHVzdGVyXzQgPC0gCiAgd29zX3Njb3B1c19jaXRhdGlvbl9uZXR3b3JrIHw+IAogIGRlbGV0ZS52ZXJ0aWNlcyh3aGljaChWKHdvc19zY29wdXNfY2l0YXRpb25fbmV0d29yaykkc3ViZmllbGQgIT0gNCkpCgpjbHVzdGVyXzRfcGFnZV9yYW5rIDwtIAogIGNsdXN0ZXJfNCB8PiAKICBzZXQudmVydGV4LmF0dHJpYnV0ZShuYW1lID0gInBhZ2VfcmFuayIsIAogICAgICAgICAgICAgICAgICAgICAgIHZhbHVlID0gcGFnZV9yYW5rKGNsdXN0ZXJfNCkkdmVjdG9yKQoKY2x1c3Rlcl80X2RmIDwtIAogIHRpYmJsZShuYW1lID0gVihjbHVzdGVyXzRfcGFnZV9yYW5rKSRuYW1lLAogICAgICAgICBmdWxsX25hbWUgPSBWKGNsdXN0ZXJfNF9wYWdlX3JhbmspJGZ1bGxfbmFtZSwKICAgICAgICAgcGFnZV9yYW5rID0gVihjbHVzdGVyXzRfcGFnZV9yYW5rKSRwYWdlX3JhbmssCiAgICAgICAgIGNsdXN0ZXIgPSBWKGNsdXN0ZXJfNF9wYWdlX3JhbmspJHN1YmZpZWxkLCkKCm5vZGVzX2Z1bGxfZGF0YSB8PiAKICBmaWx0ZXIoY2x1c3RlciA9PSA0KSB8PiAKICBzZWxlY3QoZnVsbF9uYW1lKSB8PiAKICBtdXRhdGUoZnVsbF9uYW1lID0gc3RyX2V4dHJhY3QoZnVsbF9uYW1lLCBTUEMgJVIlICAjIFJlZ3VsYXIgZXhwcmVzc2lvbnMgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb25lX29yX21vcmUoV1JEKSAlUiUgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgU1BDICVSJSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvbmVfb3JfbW9yZShvcihXUkQsIEFOWV9DSEFSKSkpLAogICAgICAgICBmdWxsX25hbWUgPSBzdHJfcmVtb3ZlKGZ1bGxfbmFtZSwgT1BFTl9QQVJFTiAlUiUgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXBlYXRlZChER1QsIDQpICVSJSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIENMT1NFX1BBUkVOICVSJQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb25lX29yX21vcmUob3IoV1JELEFOWV9DSEFSKSkpLAogICAgICAgICBmdWxsX25hbWUgPSBzdHJfdHJpbShmdWxsX25hbWUpKSAgfD4gCiAgdW5uZXN0X3Rva2VucyhvdXRwdXQgPSB3b3JkLCBpbnB1dCA9IGZ1bGxfbmFtZSkgfD4gCiAgYW50aV9qb2luKHN0b3Bfd29yZHMpIHw+IAogIGZpbHRlcih3b3JkICE9ICJkb2kiLAogICAgICAgICAhc3RyX2RldGVjdCh3b3JkLCAiWzAtOV0iKSkgfD4gICMgV29TIGRhdGEKICBmaWx0ZXIod29yZCA9PSBzdHJfcmVtb3ZlKHdvcmQsIHBhdHRlcm4gPSAiY2l0YXRpb24iKSwKICAgICAgICAgd29yZCA9PSBzdHJfcmVtb3ZlKHdvcmQsIHBhdHRlcm4gPSAiaW5kZXgiKSwgCiAgICAgICAgIHdvcmQgPT0gc3RyX3JlbW92ZSh3b3JkLCBwYXR0ZXJuID0gIndhdGVyIiksIAogICAgICAgICB3b3JkID09IHN0cl9yZW1vdmUod29yZCwgcGF0dGVybiA9ICJnaXMiKSwKICAgICAgICAgd29yZCA9PSBzdHJfcmVtb3ZlKHdvcmQsIHBhdHRlcm4gPSAicXVhbGl0eSIpLAogICAgICAgICB3b3JkID09IHN0cl9yZW1vdmUod29yZCwgcGF0dGVybiA9ICJncm91bmR3YXRlciIpKSB8PgogIGNvdW50KHdvcmQsIHNvcnQgPSBUUlVFKSB8PiAKICB3aXRoKHdvcmRjbG91ZCh3b3JkLCAKICAgICAgICAgICAgICAgICBuLCAKICAgICAgICAgICAgICAgICByYW5kb20ub3JkZXIgPSBGQUxTRSwgCiAgICAgICAgICAgICAgICAgbWF4LndvcmRzID0gNTAsIAogICAgICAgICAgICAgICAgIGNvbG9ycz1wYWwpKQpgYGAKCiMgRXhwb3J0aW5nIGZpbGVzCgpgYGB7cn0KCndyaXRlX2Nzdih0YWJsZV8xLCAidGFibGVfMS5jc3YiKSAjIEV4cG9ydGluZyB0YWJsZSAxCndyaXRlX2Nzdih3b3Nfc2NvcHVzX3RvdGFsX2NvdW50cnksICJ0YWJsZV8yXy5jc3YiKSAgIyBFeHBvcnRpbmcgdGFibGUgMgp3cml0ZV9jc3Yod29zX3Njb3B1c19hdXRob3JzLCAidGFibGVfMy5jc3YiKSAjIEV4cG9ydGluZyB0YWJsZSAzCndyaXRlX2Nzdih3b3Nfc2NvcHVzX3RvdGFsX2pvdXJuYWwsICJ0YWJsZV80LmNzdiIpICMgRXhwb3J0aW5nIHRhYmxlIDQKCgp3cml0ZV9jc3YobGFuZ3VhZ2VzLCAiZmlndXJlXzEuY3N2IikgIyBFeHBvcnRpbmcgZGF0YSBmaWd1cmUgMSAKd3JpdGVfY3N2KGZpZ3VyZV8yX2RhdGEsICJmaWd1cmVfMi5jc3YiKSAjIEV4cG9ydGluZyBkYXRhIGZpZ3VyZSAyCgp3cml0ZS5ncmFwaCh3b3Nfc2NvcHVzX2NpdGF0aW9uX25ldHdvcmssICJjaXRhdGlvbl9uZXR3b3JrX2Z1bGwuZ3JhcGhtbCIsICJncmFwaG1sIikgIyBFeHBvcnRpbmcgZ3JhcGgKd3JpdGUuZ3JhcGgod29zX3Njb3B1c19jaXRhdGlvbl9uZXR3b3JrX2NsdXN0ZXJzLCAKICAgICAgICAgICAgIndvc19zY29wdXNfY2l0YXRpb25fbmV0d29ya19jbHVzdGVycy5ncmFwaG1sIiwgCiAgICAgICAgICAgICJncmFwaG1sIikKCndyaXRlLmNzdih0cmVlX29mX3NjaWVuY2UsICJ0cmVlX29mX3NjaWVuY2UuY3N2IikgIyBFeHBvcnRpbmcgVHJlZSBvZiBTY2llbmNlCgp3cml0ZS5jc3YoY2x1c3Rlcl8xX2RmLCAiY2x1c3Rlcl8xLmNzdiIpICMgRXhwb3J0aW5nIGNsdXN0ZXIgMQp3cml0ZS5jc3YoY2x1c3Rlcl8yX2RmLCAiY2x1c3Rlcl8yLmNzdiIpICMgRXhwb3J0aW5nIGNsdXN0ZXIgMgp3cml0ZS5jc3YoY2x1c3Rlcl8zX2RmLCAiY2x1c3Rlcl8zLmNzdiIpICMgRXhwb3J0aW5nIGNsdXN0ZXIgMwp3cml0ZS5jc3YoY2x1c3Rlcl80X2RmLCAiY2x1c3Rlcl80LmNzdiIpICMgRXhwb3J0aW5nIGNsdXN0ZXIgNAoKd3JpdGUuY3N2KG5vZGVzX2Z1bGxfZGF0YSwgIm5vZGVzX2Z1bGxfZGF0YS5jc3YiKSAjIEV4cG9ydGluZyBhbGwgbm9kZXMKYGBgCgo=