Topic model

For STM the number of observations in content covariate (7897), prevalence covariate (5768), and documents (7897) are not all equal. So removing missings from prevalence covariates and corresponding documents with missing covariates.

library(readtext)
library(quanteda)
library(dplyr)
library(ggplot2)
library(stm)
library(tidytext)
library(haven)
library(data.table)

UNGD data are available on the Harvard Dataverse at https://doi.org/10.7910/DVN/0TJX8Y



DATA_DIR <- "~/Dropbox/Research/UNGDC projects/UN Data/" 

ungd_files <- readtext(paste0(DATA_DIR, "TXT/*"), 
                                 docvarsfrom = "filenames", 
                                 dvsep="_", 
                                 docvarnames = c("Country", "Session", "Year"))

covariates <- read_dta("../MasterDS_EU_Feb2018.dta")

covariates$Year <- as.integer(covariates$year)

full_files <- left_join(ungd_files, covariates, by = c("Country"="iso", "Year"))

#Keeping only complete cases for STM model
nn <- as.data.table(full_files)

complete_eu <- na.omit(nn, cols = "eu_total")


corpus <- corpus(complete_eu, text_field = "text") 
#Tokenization and basic pre-processing
tok.sm <- tokens(corpus, what = "word",
              remove_numbers = TRUE, 
              remove_punct = TRUE,
              remove_symbols = TRUE,
              remove_twitter = TRUE,
              remove_url = TRUE,
              verbose = TRUE)
#DFM creation from tokens, removing stopwords.
dfm <- dfm(tok.sm, 
           tolower = TRUE,
           remove=stopwords("english"),
           verbose = TRUE)
dfm.m <- dfm_select(dfm, c("[\\d-]", "[[:punct:]]", "^.{1}$"), 
                           selection = "remove", 
                    valuetype="regex", verbose = TRUE)
head(featnames(dfm.m),50)
tail(featnames(dfm.m),50)
stm.dfm <- convert(dfm.m, to = "stm",  docvars = docvars(corpus))
search <- searchK(stm.dfm$documents, stm.dfm$vocab, 
                  K = c(3:50), 
                  prevalence = ~ factor(Country) + s(Year) + factor(eu_total),
                  data = stm.dfm$meta)

search.results <- as.data.frame(search$results)
readr::write_csv(search.results, "search.results.csv")

Figure 11 in supplementary materials

ggplot(search_results, aes(x=semcoh, y=exclus)) +
    geom_point(size=5, shape =1, color = "green") +
  geom_text(aes(label=K), size=2) +
   geom_smooth(method="lm", se = FALSE, color = "red", size = .3) +
  geom_vline(xintercept = mean(search_results$semcoh), size = .2, linetype="dashed") +
    geom_hline(yintercept = mean(search_results$exclus), size = .2, linetype="dashed") +
  theme_bw() +
 # ggtitle("Selecting optimal number of topics") + 
  xlab("Semantic coherence") + ylab("Exclusivity")

ggsave("optimal_topics.pdf")
topics14 <- stm(stm.dfm$documents, stm.dfm$vocab,  
             prevalence = ~ factor(Country) + s(Year) + factor(eu_total), 
             data = stm.dfm$meta, 
             K = 14, init.type = "Spectral")
words <- labelTopics(topics14, n = 15)
prob <- as.data.frame(words[1])
frex <- as.data.frame(words[2])
labelTopics(topics14,n = 5)

Figure 12 in supplementary materials

pdf("topic14_prob_words.pdf", width = 10, height = 7)

plot(topics14,type="summary", labeltype = "prob", 
     xlim = c(0, 1.5), 
     n = 25, 
     text.cex = .4, 
     main = "Top 25 highest prob words")

dev.off()
pdf("topic14_frex_words.pdf", width = 10, height = 7)

plot(topics14,type="summary", labeltype = "frex", 
     xlim = c(0, 1.5), 
     n = 25, 
     text.cex = .35, 
     main = "Top 25 FREX words")

dev.off()

Topic 1: Disarmament; Topic 2: African peace and security; Topic 3: The United Nations, Topic 4: Colonialism and independence; Topic 5: International security; Topic 6: War and peace; Topic 7: Middle East peace; Topic 8: Small Island Developing States (SIDS); Topic 9: Economic development and the United Nations; Topic 10: Africa region; Topic 11: Latin America region; Topic 12: International development and the Global South; Topic 13: Europe region; Topic 14: Sustainable development and climate change

topiclabels <- c("Disarmament", "African peace and security", "Pan-Asian cooperation", "Colonialism and independence", "International security", "Conflict and terrorism", "Middle East peace", "Small Island Developing States (SIDS)", "Economic development and the United Nations", "Africa region", "Latin America region", "International development and the Global South", "European region", "Sustainable development and climate change")
doc.names <- tidy(topics14, matrix = "gamma", document_names = names(stm.dfm$documents))
colnames(doc.names)[1] <- "country" 
doc.names
documents <- tidy(topics14, matrix = "gamma", document_names = stm.dfm$meta$eu_total)
topics <- cbind(documents, doc.names)
topics[5:6] <- NULL
topics
gamma <- stringr::str_replace(topics$country, ".txt", "") %>% 
  stringr::str_split(., "_", simplify = TRUE) %>% 
  cbind(., topics)

gamma$`1` <- as.character(gamma$`1`)
gamma$`2` <- as.numeric(as.character(gamma$`2`))
gamma$`3` <- as.numeric(as.character(gamma$`3`))

colnames(gamma)[1] <- "Country"
colnames(gamma)[2] <- "Session"
colnames(gamma)[3] <- "Year"
colnames(gamma)[4] <- "membership"

gamma$membership[gamma$Country=="USA"] <- 4
gamma$membership[gamma$Country=="RUS"] <- 5
sums <- gamma %>%  group_by(membership,topic) %>% summarise(Topic_Proportion = mean(gamma)) %>% arrange(topic, Topic_Proportion) %>% filter(membership !=0)

topic_distributions <- as.data.frame(sums)
readr::write_csv(topic_distributions, "topic_distributions.csv")

knitr::kable(sums)
topic_names <- as_labeller(c(
                    `1` = "Disarmament",
                    `2` = "African peace and security",
                    `3` = "Pan-Asian cooperation",
                    `4` = "Colonialism and independence", 
                    `5` = "International security",
                    `6` = "Conflict and terrorism",
                    `7` = "Middle East peace",
                    `8` = "Small Island Developing States (SIDS)",
                    `9` = "Economic development and the United Nations",
                    `10` = "Africa region",
                    `11` = "Latin America region",
                    `12` = "International development and the Global South",
                    `13` = "European region",
                    `14` = "Sustainable development and climate change"
                    ))

Figure 13 in supplementary materials

ggplot(sums, aes(x = reorder(membership, Topic_Proportion), 
                 y = Topic_Proportion, fill = factor(topic))) +
    geom_col(show.legend = FALSE) +
    facet_wrap(~ topic, scales = "free", ncol=2, labeller = topic_names) +
    coord_flip() + labs(x="", y="") +
   scale_x_discrete(labels=c( "1" = "Applicant", "2" = "Candidate",
                              "3" = "Member", "4" = "USA", "5" = "RUS"), 
                    limits = c(1,2,3,4,5))

ggsave("topic_usage.pdf", width = 8, height = 14)
EU6 <- c("BEL", "FRA", "DEU", "ITA", "LUX", "NLD")
EU9 <- c("BEL", "FRA", "DEU", "ITA", "LUX", "NLD", "DNK", "IRL", "GBR")
EU12 <- c("BEL", "FRA", "DEU", "ITA", "LUX", "NLD", "DNK", "IRL", "GBR", "GRC","ESP", "PRT")
EU15 <- c("BEL", "FRA", "DEU", "ITA", "LUX", "NLD", "DNK", "IRL", "GBR", "GRC","ESP", "PRT","AUT", "FIN", "SWE")
wave1 <- c("DNK", "IRL", "GBR")
wave2 <- "GRC" 
wave3 <- c("ESP", "PRT") 
wave4 <- c("AUT", "FIN", "SWE") 
wave5 <- c("CZE", "HUN", "POL", "EST", "LVA", "LTU", "CYP", "MLT", "SVK", "SVN")
wave6 <- c("BGR", "ROU") 
wave7 <- "HRV" 

For a group of EU related topics

topics_eu <- c("2","9", "10", "11", "14")
gamma$topics_eu <- gamma$topic %in% topics_eu

topic_sums <- gamma %>% group_by(topics_eu, Country, Year) %>% summarise(sum_eu_topics = sum(gamma)) %>% filter(topics_eu==TRUE, Year>1990) %>% arrange(Country, Year, sum_eu_topics) 

topic_sums$eu10 <- topic_sums$Country %in% wave5
topic_sums$eu6 <- topic_sums$Country %in% EU6
topic_sums$eu9 <- topic_sums$Country %in% EU9
topic_sums$eu12 <- topic_sums$Country %in% EU12
topic_sums$eu15 <- topic_sums$Country %in% EU15
topic_sums$usa <- topic_sums$Country=="USA"
topic_sums$rus <- topic_sums$Country=="RUS"

eu10_mean_topic <- topic_sums %>% group_by(eu10, Year) %>% summarise(eu10_tp = mean(sum_eu_topics)) %>% filter(eu10==TRUE) 

eu6_mean_topic <- topic_sums %>% group_by(eu6, Year) %>% summarise(eu6_tp = mean(sum_eu_topics)) %>% filter(eu6==TRUE) 

eu9_mean_topic <- topic_sums %>% group_by(eu9, Year) %>% summarise(eu9_tp = mean(sum_eu_topics)) %>% filter(eu9==TRUE) 

eu12_mean_topic <- topic_sums %>% group_by(eu12, Year) %>% summarise(eu12_tp = mean(sum_eu_topics)) %>% filter(eu12==TRUE) 

eu15_mean_topic <- topic_sums %>% group_by(eu15, Year) %>% summarise(eu15_tp = mean(sum_eu_topics)) %>% filter(eu15==TRUE)

usa_mean_topic <- topic_sums %>% group_by(usa, Year) %>% summarise(usa_tp = mean(sum_eu_topics)) %>% filter(usa==TRUE)

rus_mean_topic <- topic_sums %>% group_by(rus, Year) %>% summarise(rus_tp = mean(sum_eu_topics)) %>% filter(rus==TRUE)

mean_topics <- bind_cols(eu10_mean_topic, eu6_mean_topic, eu9_mean_topic, eu12_mean_topic, eu15_mean_topic, usa_mean_topic, rus_mean_topic)

Figure 6 (and figure 19 in supplementary materials)

ggplot(mean_topics, aes(x=Year)) +
  geom_area(aes(y=eu10_tp), fill = "red", alpha=0.3) +
  ylab("EU Topics Proportion") +
  scale_x_continuous(breaks=c(1990,2000, 2010)) +
#  ggtitle("Total EU topic proportions average per year per country group: EU10") +
  theme_bw()

ggsave("eu10_topic_proportions.pdf")
ggplot(mean_topics, aes(x=Year)) +
  geom_area(aes(y=eu10_tp), fill = "red", alpha=0.3) +
  geom_area(aes(y=eu6_tp), fill = "blue", alpha=0.3) +
  geom_line(aes(y=usa_tp), colour = "black") +
  geom_line(aes(y=rus_tp), colour = "black", linetype = "dashed") +
  ylab("EU Topics Proportion") +
  scale_x_continuous(breaks=c(1990,2000, 2010)) +
#  ggtitle("Total EU topic proportions average per year per country group: EU10 (red) vs EU6") +
  theme_bw()

ggsave("eu10_eu6_topic_proportions_rus_usa.pdf")

Figure 18 in supplementary materials

ggplot(mean_topics, aes(x=Year)) +
  geom_area(aes(y=eu10_tp), fill = "red", alpha=0.3) +
  geom_area(aes(y=eu6_tp), fill = "blue", alpha=0.3) +
  ylab("EU Topics Proportion") +
    scale_x_continuous(breaks=c(1990,2000, 2010)) +
#  ggtitle("Total EU topic proportions average per year per country group: EU10 (red) vs EU6") +
  theme_bw()

ggsave("eu10_eu6_topic_proportions.pdf")
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKCiMjIFRvcGljIG1vZGVsCgpGb3IgU1RNIHRoZSBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIGluIGNvbnRlbnQgY292YXJpYXRlICg3ODk3KSwgcHJldmFsZW5jZSBjb3ZhcmlhdGUgKDU3NjgpLCBhbmQgZG9jdW1lbnRzICg3ODk3KSBhcmUgbm90IGFsbCBlcXVhbC4gU28gcmVtb3ZpbmcgbWlzc2luZ3MgZnJvbSBwcmV2YWxlbmNlIGNvdmFyaWF0ZXMgYW5kIGNvcnJlc3BvbmRpbmcgZG9jdW1lbnRzIHdpdGggbWlzc2luZyBjb3ZhcmlhdGVzLgoKCgpgYGB7ciwgbWVzc2FnZT1GQUxTRX0KbGlicmFyeShyZWFkdGV4dCkKbGlicmFyeShxdWFudGVkYSkKbGlicmFyeShkcGx5cikKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHN0bSkKbGlicmFyeSh0aWR5dGV4dCkKbGlicmFyeShoYXZlbikKbGlicmFyeShkYXRhLnRhYmxlKQpgYGAKCgpVTkdEIGRhdGEgYXJlIGF2YWlsYWJsZSBvbiB0aGUgSGFydmFyZCBEYXRhdmVyc2UgYXQgaHR0cHM6Ly9kb2kub3JnLzEwLjc5MTAvRFZOLzBUSlg4WQoKCmBgYHtyfQoKCkRBVEFfRElSIDwtICJ+L0Ryb3Bib3gvUmVzZWFyY2gvVU5HREMgcHJvamVjdHMvVU4gRGF0YS8iIAoKdW5nZF9maWxlcyA8LSByZWFkdGV4dChwYXN0ZTAoREFUQV9ESVIsICJUWFQvKiIpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZG9jdmFyc2Zyb20gPSAiZmlsZW5hbWVzIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGR2c2VwPSJfIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRvY3Zhcm5hbWVzID0gYygiQ291bnRyeSIsICJTZXNzaW9uIiwgIlllYXIiKSkKCmBgYAoKCmBgYHtyfQoKY292YXJpYXRlcyA8LSByZWFkX2R0YSgiLi4vTWFzdGVyRFNfRVVfRmViMjAxOC5kdGEiKQoKY292YXJpYXRlcyRZZWFyIDwtIGFzLmludGVnZXIoY292YXJpYXRlcyR5ZWFyKQoKZnVsbF9maWxlcyA8LSBsZWZ0X2pvaW4odW5nZF9maWxlcywgY292YXJpYXRlcywgYnkgPSBjKCJDb3VudHJ5Ij0iaXNvIiwgIlllYXIiKSkKCiNLZWVwaW5nIG9ubHkgY29tcGxldGUgY2FzZXMgZm9yIFNUTSBtb2RlbApubiA8LSBhcy5kYXRhLnRhYmxlKGZ1bGxfZmlsZXMpCgpjb21wbGV0ZV9ldSA8LSBuYS5vbWl0KG5uLCBjb2xzID0gImV1X3RvdGFsIikKCgpjb3JwdXMgPC0gY29ycHVzKGNvbXBsZXRlX2V1LCB0ZXh0X2ZpZWxkID0gInRleHQiKSAKCmBgYAoKCmBgYHtyfQojVG9rZW5pemF0aW9uIGFuZCBiYXNpYyBwcmUtcHJvY2Vzc2luZwp0b2suc20gPC0gdG9rZW5zKGNvcnB1cywgd2hhdCA9ICJ3b3JkIiwKICAgICAgICAgICAgICByZW1vdmVfbnVtYmVycyA9IFRSVUUsIAogICAgICAgICAgICAgIHJlbW92ZV9wdW5jdCA9IFRSVUUsCiAgICAgICAgICAgICAgcmVtb3ZlX3N5bWJvbHMgPSBUUlVFLAogICAgICAgICAgICAgIHJlbW92ZV90d2l0dGVyID0gVFJVRSwKICAgICAgICAgICAgICByZW1vdmVfdXJsID0gVFJVRSwKICAgICAgICAgICAgICB2ZXJib3NlID0gVFJVRSkKYGBgCgpgYGB7cn0KI0RGTSBjcmVhdGlvbiBmcm9tIHRva2VucywgcmVtb3Zpbmcgc3RvcHdvcmRzLgpkZm0gPC0gZGZtKHRvay5zbSwgCiAgICAgICAgICAgdG9sb3dlciA9IFRSVUUsCiAgICAgICAgICAgcmVtb3ZlPXN0b3B3b3JkcygiZW5nbGlzaCIpLAogICAgICAgICAgIHZlcmJvc2UgPSBUUlVFKQoKYGBgCgoKCmBgYHtyfQpkZm0ubSA8LSBkZm1fc2VsZWN0KGRmbSwgYygiW1xcZC1dIiwgIltbOnB1bmN0Ol1dIiwgIl4uezF9JCIpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0aW9uID0gInJlbW92ZSIsIAogICAgICAgICAgICAgICAgICAgIHZhbHVldHlwZT0icmVnZXgiLCB2ZXJib3NlID0gVFJVRSkKCmBgYAoKYGBge3J9CmhlYWQoZmVhdG5hbWVzKGRmbS5tKSw1MCkKdGFpbChmZWF0bmFtZXMoZGZtLm0pLDUwKQpgYGAKCgoKYGBge3J9CnN0bS5kZm0gPC0gY29udmVydChkZm0ubSwgdG8gPSAic3RtIiwgIGRvY3ZhcnMgPSBkb2N2YXJzKGNvcnB1cykpCmBgYAoKCmBgYHtyIGV2YWw9RkFMU0V9CnNlYXJjaCA8LSBzZWFyY2hLKHN0bS5kZm0kZG9jdW1lbnRzLCBzdG0uZGZtJHZvY2FiLCAKICAgICAgICAgICAgICAgICAgSyA9IGMoMzo1MCksIAogICAgICAgICAgICAgICAgICBwcmV2YWxlbmNlID0gfiBmYWN0b3IoQ291bnRyeSkgKyBzKFllYXIpICsgZmFjdG9yKGV1X3RvdGFsKSwKICAgICAgICAgICAgICAgICAgZGF0YSA9IHN0bS5kZm0kbWV0YSkKCnNlYXJjaC5yZXN1bHRzIDwtIGFzLmRhdGEuZnJhbWUoc2VhcmNoJHJlc3VsdHMpCnJlYWRyOjp3cml0ZV9jc3Yoc2VhcmNoLnJlc3VsdHMsICJzZWFyY2gucmVzdWx0cy5jc3YiKQpgYGAKCgoKYGBge3IgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0Kc2VhcmNoX3Jlc3VsdHMgPC0gcmVhZF9jc3YoInNlYXJjaC5yZXN1bHRzLmNzdiIpCmBgYAoKCiMjIyMjIyMjIyMjIyMjIyMjCiMjIyMjIEZpZ3VyZSAxMSBpbiBzdXBwbGVtZW50YXJ5IG1hdGVyaWFscwojIyMjIyMjIyMjIyMjIyMjIwoKCmBgYHtyIGV2YWw9RkFMU0V9CmdncGxvdChzZWFyY2hfcmVzdWx0cywgYWVzKHg9c2VtY29oLCB5PWV4Y2x1cykpICsKICAgIGdlb21fcG9pbnQoc2l6ZT01LCBzaGFwZSA9MSwgY29sb3IgPSAiZ3JlZW4iKSArCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbD1LKSwgc2l6ZT0yKSArCiAgIGdlb21fc21vb3RoKG1ldGhvZD0ibG0iLCBzZSA9IEZBTFNFLCBjb2xvciA9ICJyZWQiLCBzaXplID0gLjMpICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBtZWFuKHNlYXJjaF9yZXN1bHRzJHNlbWNvaCksIHNpemUgPSAuMiwgbGluZXR5cGU9ImRhc2hlZCIpICsKICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IG1lYW4oc2VhcmNoX3Jlc3VsdHMkZXhjbHVzKSwgc2l6ZSA9IC4yLCBsaW5ldHlwZT0iZGFzaGVkIikgKwogIHRoZW1lX2J3KCkgKwogIyBnZ3RpdGxlKCJTZWxlY3Rpbmcgb3B0aW1hbCBudW1iZXIgb2YgdG9waWNzIikgKyAKICB4bGFiKCJTZW1hbnRpYyBjb2hlcmVuY2UiKSArIHlsYWIoIkV4Y2x1c2l2aXR5IikKCmdnc2F2ZSgib3B0aW1hbF90b3BpY3MucGRmIikKCmBgYAoKCgoKYGBge3J9CnRvcGljczE0IDwtIHN0bShzdG0uZGZtJGRvY3VtZW50cywgc3RtLmRmbSR2b2NhYiwgIAogICAgICAgICAgICAgcHJldmFsZW5jZSA9IH4gZmFjdG9yKENvdW50cnkpICsgcyhZZWFyKSArIGZhY3RvcihldV90b3RhbCksIAogICAgICAgICAgICAgZGF0YSA9IHN0bS5kZm0kbWV0YSwgCiAgICAgICAgICAgICBLID0gMTQsIGluaXQudHlwZSA9ICJTcGVjdHJhbCIpCmBgYAoKCmBgYHtyfQp3b3JkcyA8LSBsYWJlbFRvcGljcyh0b3BpY3MxNCwgbiA9IDE1KQpwcm9iIDwtIGFzLmRhdGEuZnJhbWUod29yZHNbMV0pCmZyZXggPC0gYXMuZGF0YS5mcmFtZSh3b3Jkc1syXSkKYGBgCgpgYGB7cn0KbGFiZWxUb3BpY3ModG9waWNzMTQsbiA9IDUpCgpgYGAKCgojIyMjIyMjIyMjIyMjIyMjIwojIyMjIyBGaWd1cmUgMTIgaW4gc3VwcGxlbWVudGFyeSBtYXRlcmlhbHMKIyMjIyMjIyMjIyMjIyMjIyMKCgpgYGB7cn0KcGRmKCJ0b3BpYzE0X3Byb2Jfd29yZHMucGRmIiwgd2lkdGggPSAxMCwgaGVpZ2h0ID0gNykKCnBsb3QodG9waWNzMTQsdHlwZT0ic3VtbWFyeSIsIGxhYmVsdHlwZSA9ICJwcm9iIiwgCiAgICAgeGxpbSA9IGMoMCwgMS41KSwgCiAgICAgbiA9IDI1LCAKICAgICB0ZXh0LmNleCA9IC40LCAKICAgICBtYWluID0gIlRvcCAyNSBoaWdoZXN0IHByb2Igd29yZHMiKQoKZGV2Lm9mZigpCmBgYAoKCmBgYHtyfQpwZGYoInRvcGljMTRfZnJleF93b3Jkcy5wZGYiLCB3aWR0aCA9IDEwLCBoZWlnaHQgPSA3KQoKcGxvdCh0b3BpY3MxNCx0eXBlPSJzdW1tYXJ5IiwgbGFiZWx0eXBlID0gImZyZXgiLCAKICAgICB4bGltID0gYygwLCAxLjUpLCAKICAgICBuID0gMjUsIAogICAgIHRleHQuY2V4ID0gLjM1LCAKICAgICBtYWluID0gIlRvcCAyNSBGUkVYIHdvcmRzIikKCmRldi5vZmYoKQpgYGAKCgoKClRvcGljIDE6IERpc2FybWFtZW50OyBUb3BpYyAyOiBBZnJpY2FuIHBlYWNlIGFuZCBzZWN1cml0eTsgVG9waWMgMzogVGhlIFVuaXRlZCBOYXRpb25zLCBUb3BpYyA0OiBDb2xvbmlhbGlzbSBhbmQgaW5kZXBlbmRlbmNlOyBUb3BpYyA1OiBJbnRlcm5hdGlvbmFsIHNlY3VyaXR5OyBUb3BpYyA2OiBXYXIgYW5kIHBlYWNlOyBUb3BpYyA3OiBNaWRkbGUgRWFzdCBwZWFjZTsgVG9waWMgODogU21hbGwgSXNsYW5kIERldmVsb3BpbmcgU3RhdGVzIChTSURTKTsgVG9waWMgOTogRWNvbm9taWMgZGV2ZWxvcG1lbnQgYW5kIHRoZSBVbml0ZWQgTmF0aW9uczsgVG9waWMgMTA6IEFmcmljYSByZWdpb247IFRvcGljIDExOiBMYXRpbiBBbWVyaWNhIHJlZ2lvbjsgVG9waWMgMTI6IEludGVybmF0aW9uYWwgZGV2ZWxvcG1lbnQgYW5kIHRoZSBHbG9iYWwgU291dGg7IFRvcGljIDEzOiBFdXJvcGUgcmVnaW9uOyBUb3BpYyAxNDogU3VzdGFpbmFibGUgZGV2ZWxvcG1lbnQgYW5kIGNsaW1hdGUgY2hhbmdlCgpgYGB7cn0KdG9waWNsYWJlbHMgPC0gYygiRGlzYXJtYW1lbnQiLCAiQWZyaWNhbiBwZWFjZSBhbmQgc2VjdXJpdHkiLCAiUGFuLUFzaWFuIGNvb3BlcmF0aW9uIiwgIkNvbG9uaWFsaXNtIGFuZCBpbmRlcGVuZGVuY2UiLCAiSW50ZXJuYXRpb25hbCBzZWN1cml0eSIsICJDb25mbGljdCBhbmQgdGVycm9yaXNtIiwgIk1pZGRsZSBFYXN0IHBlYWNlIiwgIlNtYWxsIElzbGFuZCBEZXZlbG9waW5nIFN0YXRlcyAoU0lEUykiLCAiRWNvbm9taWMgZGV2ZWxvcG1lbnQgYW5kIHRoZSBVbml0ZWQgTmF0aW9ucyIsICJBZnJpY2EgcmVnaW9uIiwgIkxhdGluIEFtZXJpY2EgcmVnaW9uIiwgIkludGVybmF0aW9uYWwgZGV2ZWxvcG1lbnQgYW5kIHRoZSBHbG9iYWwgU291dGgiLCAiRXVyb3BlYW4gcmVnaW9uIiwgIlN1c3RhaW5hYmxlIGRldmVsb3BtZW50IGFuZCBjbGltYXRlIGNoYW5nZSIpCmBgYAoKCgoKYGBge3J9CmRvYy5uYW1lcyA8LSB0aWR5KHRvcGljczE0LCBtYXRyaXggPSAiZ2FtbWEiLCBkb2N1bWVudF9uYW1lcyA9IG5hbWVzKHN0bS5kZm0kZG9jdW1lbnRzKSkKY29sbmFtZXMoZG9jLm5hbWVzKVsxXSA8LSAiY291bnRyeSIgCmRvYy5uYW1lcwpgYGAKCgpgYGB7cn0KZG9jdW1lbnRzIDwtIHRpZHkodG9waWNzMTQsIG1hdHJpeCA9ICJnYW1tYSIsIGRvY3VtZW50X25hbWVzID0gc3RtLmRmbSRtZXRhJGV1X3RvdGFsKQp0b3BpY3MgPC0gY2JpbmQoZG9jdW1lbnRzLCBkb2MubmFtZXMpCnRvcGljc1s1OjZdIDwtIE5VTEwKdG9waWNzCmBgYAoKCgpgYGB7cn0KZ2FtbWEgPC0gc3RyaW5ncjo6c3RyX3JlcGxhY2UodG9waWNzJGNvdW50cnksICIudHh0IiwgIiIpICU+JSAKICBzdHJpbmdyOjpzdHJfc3BsaXQoLiwgIl8iLCBzaW1wbGlmeSA9IFRSVUUpICU+JSAKICBjYmluZCguLCB0b3BpY3MpCgpnYW1tYSRgMWAgPC0gYXMuY2hhcmFjdGVyKGdhbW1hJGAxYCkKZ2FtbWEkYDJgIDwtIGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKGdhbW1hJGAyYCkpCmdhbW1hJGAzYCA8LSBhcy5udW1lcmljKGFzLmNoYXJhY3RlcihnYW1tYSRgM2ApKQoKY29sbmFtZXMoZ2FtbWEpWzFdIDwtICJDb3VudHJ5Igpjb2xuYW1lcyhnYW1tYSlbMl0gPC0gIlNlc3Npb24iCmNvbG5hbWVzKGdhbW1hKVszXSA8LSAiWWVhciIKY29sbmFtZXMoZ2FtbWEpWzRdIDwtICJtZW1iZXJzaGlwIgoKZ2FtbWEkbWVtYmVyc2hpcFtnYW1tYSRDb3VudHJ5PT0iVVNBIl0gPC0gNApnYW1tYSRtZW1iZXJzaGlwW2dhbW1hJENvdW50cnk9PSJSVVMiXSA8LSA1CgpgYGAKCmBgYHtyfQpzdW1zIDwtIGdhbW1hICU+JSAgZ3JvdXBfYnkobWVtYmVyc2hpcCx0b3BpYykgJT4lIHN1bW1hcmlzZShUb3BpY19Qcm9wb3J0aW9uID0gbWVhbihnYW1tYSkpICU+JSBhcnJhbmdlKHRvcGljLCBUb3BpY19Qcm9wb3J0aW9uKSAlPiUgZmlsdGVyKG1lbWJlcnNoaXAgIT0wKQoKdG9waWNfZGlzdHJpYnV0aW9ucyA8LSBhcy5kYXRhLmZyYW1lKHN1bXMpCnJlYWRyOjp3cml0ZV9jc3YodG9waWNfZGlzdHJpYnV0aW9ucywgInRvcGljX2Rpc3RyaWJ1dGlvbnMuY3N2IikKCmtuaXRyOjprYWJsZShzdW1zKQpgYGAKCmBgYHtyfQp0b3BpY19uYW1lcyA8LSBhc19sYWJlbGxlcihjKAogICAgICAgICAgICAgICAgICAgIGAxYCA9ICJEaXNhcm1hbWVudCIsCiAgICAgICAgICAgICAgICAgICAgYDJgID0gIkFmcmljYW4gcGVhY2UgYW5kIHNlY3VyaXR5IiwKICAgICAgICAgICAgICAgICAgICBgM2AgPSAiUGFuLUFzaWFuIGNvb3BlcmF0aW9uIiwKICAgICAgICAgICAgICAgICAgICBgNGAgPSAiQ29sb25pYWxpc20gYW5kIGluZGVwZW5kZW5jZSIsIAogICAgICAgICAgICAgICAgICAgIGA1YCA9ICJJbnRlcm5hdGlvbmFsIHNlY3VyaXR5IiwKICAgICAgICAgICAgICAgICAgICBgNmAgPSAiQ29uZmxpY3QgYW5kIHRlcnJvcmlzbSIsCiAgICAgICAgICAgICAgICAgICAgYDdgID0gIk1pZGRsZSBFYXN0IHBlYWNlIiwKICAgICAgICAgICAgICAgICAgICBgOGAgPSAiU21hbGwgSXNsYW5kIERldmVsb3BpbmcgU3RhdGVzIChTSURTKSIsCiAgICAgICAgICAgICAgICAgICAgYDlgID0gIkVjb25vbWljIGRldmVsb3BtZW50IGFuZCB0aGUgVW5pdGVkIE5hdGlvbnMiLAogICAgICAgICAgICAgICAgICAgIGAxMGAgPSAiQWZyaWNhIHJlZ2lvbiIsCiAgICAgICAgICAgICAgICAgICAgYDExYCA9ICJMYXRpbiBBbWVyaWNhIHJlZ2lvbiIsCiAgICAgICAgICAgICAgICAgICAgYDEyYCA9ICJJbnRlcm5hdGlvbmFsIGRldmVsb3BtZW50IGFuZCB0aGUgR2xvYmFsIFNvdXRoIiwKICAgICAgICAgICAgICAgICAgICBgMTNgID0gIkV1cm9wZWFuIHJlZ2lvbiIsCiAgICAgICAgICAgICAgICAgICAgYDE0YCA9ICJTdXN0YWluYWJsZSBkZXZlbG9wbWVudCBhbmQgY2xpbWF0ZSBjaGFuZ2UiCiAgICAgICAgICAgICAgICAgICAgKSkKYGBgCgoKIyMjIyMjIyMjIyMjIyMjIyMKIyMjIyMgRmlndXJlIDEzIGluIHN1cHBsZW1lbnRhcnkgbWF0ZXJpYWxzCiMjIyMjIyMjIyMjIyMjIyMjCgoKCmBgYHtyfQpnZ3Bsb3Qoc3VtcywgYWVzKHggPSByZW9yZGVyKG1lbWJlcnNoaXAsIFRvcGljX1Byb3BvcnRpb24pLCAKICAgICAgICAgICAgICAgICB5ID0gVG9waWNfUHJvcG9ydGlvbiwgZmlsbCA9IGZhY3Rvcih0b3BpYykpKSArCiAgICBnZW9tX2NvbChzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgICBmYWNldF93cmFwKH4gdG9waWMsIHNjYWxlcyA9ICJmcmVlIiwgbmNvbD0yLCBsYWJlbGxlciA9IHRvcGljX25hbWVzKSArCiAgICBjb29yZF9mbGlwKCkgKyBsYWJzKHg9IiIsIHk9IiIpICsKICAgc2NhbGVfeF9kaXNjcmV0ZShsYWJlbHM9YyggIjEiID0gIkFwcGxpY2FudCIsICIyIiA9ICJDYW5kaWRhdGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiMyIgPSAiTWVtYmVyIiwgIjQiID0gIlVTQSIsICI1IiA9ICJSVVMiKSwgCiAgICAgICAgICAgICAgICAgICAgbGltaXRzID0gYygxLDIsMyw0LDUpKQoKZ2dzYXZlKCJ0b3BpY191c2FnZS5wZGYiLCB3aWR0aCA9IDgsIGhlaWdodCA9IDE0KQpgYGAKCgoKCgpgYGB7cn0KRVU2IDwtIGMoIkJFTCIsICJGUkEiLCAiREVVIiwgIklUQSIsICJMVVgiLCAiTkxEIikKRVU5IDwtIGMoIkJFTCIsICJGUkEiLCAiREVVIiwgIklUQSIsICJMVVgiLCAiTkxEIiwgIkROSyIsICJJUkwiLCAiR0JSIikKRVUxMiA8LSBjKCJCRUwiLCAiRlJBIiwgIkRFVSIsICJJVEEiLCAiTFVYIiwgIk5MRCIsICJETksiLCAiSVJMIiwgIkdCUiIsICJHUkMiLCJFU1AiLCAiUFJUIikKRVUxNSA8LSBjKCJCRUwiLCAiRlJBIiwgIkRFVSIsICJJVEEiLCAiTFVYIiwgIk5MRCIsICJETksiLCAiSVJMIiwgIkdCUiIsICJHUkMiLCJFU1AiLCAiUFJUIiwiQVVUIiwgIkZJTiIsICJTV0UiKQp3YXZlMSA8LSBjKCJETksiLCAiSVJMIiwgIkdCUiIpCndhdmUyIDwtICJHUkMiIAp3YXZlMyA8LSBjKCJFU1AiLCAiUFJUIikgCndhdmU0IDwtIGMoIkFVVCIsICJGSU4iLCAiU1dFIikgCndhdmU1IDwtIGMoIkNaRSIsICJIVU4iLCAiUE9MIiwgIkVTVCIsICJMVkEiLCAiTFRVIiwgIkNZUCIsICJNTFQiLCAiU1ZLIiwgIlNWTiIpCndhdmU2IDwtIGMoIkJHUiIsICJST1UiKSAKd2F2ZTcgPC0gIkhSViIgCmBgYAoKCgpGb3IgYSBncm91cCBvZiBFVSByZWxhdGVkIHRvcGljcwoKYGBge3J9CnRvcGljc19ldSA8LSBjKCIyIiwiOSIsICIxMCIsICIxMSIsICIxNCIpCmdhbW1hJHRvcGljc19ldSA8LSBnYW1tYSR0b3BpYyAlaW4lIHRvcGljc19ldQoKdG9waWNfc3VtcyA8LSBnYW1tYSAlPiUgZ3JvdXBfYnkodG9waWNzX2V1LCBDb3VudHJ5LCBZZWFyKSAlPiUgc3VtbWFyaXNlKHN1bV9ldV90b3BpY3MgPSBzdW0oZ2FtbWEpKSAlPiUgZmlsdGVyKHRvcGljc19ldT09VFJVRSwgWWVhcj4xOTkwKSAlPiUgYXJyYW5nZShDb3VudHJ5LCBZZWFyLCBzdW1fZXVfdG9waWNzKSAKCnRvcGljX3N1bXMkZXUxMCA8LSB0b3BpY19zdW1zJENvdW50cnkgJWluJSB3YXZlNQp0b3BpY19zdW1zJGV1NiA8LSB0b3BpY19zdW1zJENvdW50cnkgJWluJSBFVTYKdG9waWNfc3VtcyRldTkgPC0gdG9waWNfc3VtcyRDb3VudHJ5ICVpbiUgRVU5CnRvcGljX3N1bXMkZXUxMiA8LSB0b3BpY19zdW1zJENvdW50cnkgJWluJSBFVTEyCnRvcGljX3N1bXMkZXUxNSA8LSB0b3BpY19zdW1zJENvdW50cnkgJWluJSBFVTE1CnRvcGljX3N1bXMkdXNhIDwtIHRvcGljX3N1bXMkQ291bnRyeT09IlVTQSIKdG9waWNfc3VtcyRydXMgPC0gdG9waWNfc3VtcyRDb3VudHJ5PT0iUlVTIgoKZXUxMF9tZWFuX3RvcGljIDwtIHRvcGljX3N1bXMgJT4lIGdyb3VwX2J5KGV1MTAsIFllYXIpICU+JSBzdW1tYXJpc2UoZXUxMF90cCA9IG1lYW4oc3VtX2V1X3RvcGljcykpICU+JSBmaWx0ZXIoZXUxMD09VFJVRSkgCgpldTZfbWVhbl90b3BpYyA8LSB0b3BpY19zdW1zICU+JSBncm91cF9ieShldTYsIFllYXIpICU+JSBzdW1tYXJpc2UoZXU2X3RwID0gbWVhbihzdW1fZXVfdG9waWNzKSkgJT4lIGZpbHRlcihldTY9PVRSVUUpIAoKZXU5X21lYW5fdG9waWMgPC0gdG9waWNfc3VtcyAlPiUgZ3JvdXBfYnkoZXU5LCBZZWFyKSAlPiUgc3VtbWFyaXNlKGV1OV90cCA9IG1lYW4oc3VtX2V1X3RvcGljcykpICU+JSBmaWx0ZXIoZXU5PT1UUlVFKSAKCmV1MTJfbWVhbl90b3BpYyA8LSB0b3BpY19zdW1zICU+JSBncm91cF9ieShldTEyLCBZZWFyKSAlPiUgc3VtbWFyaXNlKGV1MTJfdHAgPSBtZWFuKHN1bV9ldV90b3BpY3MpKSAlPiUgZmlsdGVyKGV1MTI9PVRSVUUpIAoKZXUxNV9tZWFuX3RvcGljIDwtIHRvcGljX3N1bXMgJT4lIGdyb3VwX2J5KGV1MTUsIFllYXIpICU+JSBzdW1tYXJpc2UoZXUxNV90cCA9IG1lYW4oc3VtX2V1X3RvcGljcykpICU+JSBmaWx0ZXIoZXUxNT09VFJVRSkKCnVzYV9tZWFuX3RvcGljIDwtIHRvcGljX3N1bXMgJT4lIGdyb3VwX2J5KHVzYSwgWWVhcikgJT4lIHN1bW1hcmlzZSh1c2FfdHAgPSBtZWFuKHN1bV9ldV90b3BpY3MpKSAlPiUgZmlsdGVyKHVzYT09VFJVRSkKCnJ1c19tZWFuX3RvcGljIDwtIHRvcGljX3N1bXMgJT4lIGdyb3VwX2J5KHJ1cywgWWVhcikgJT4lIHN1bW1hcmlzZShydXNfdHAgPSBtZWFuKHN1bV9ldV90b3BpY3MpKSAlPiUgZmlsdGVyKHJ1cz09VFJVRSkKCm1lYW5fdG9waWNzIDwtIGJpbmRfY29scyhldTEwX21lYW5fdG9waWMsIGV1Nl9tZWFuX3RvcGljLCBldTlfbWVhbl90b3BpYywgZXUxMl9tZWFuX3RvcGljLCBldTE1X21lYW5fdG9waWMsIHVzYV9tZWFuX3RvcGljLCBydXNfbWVhbl90b3BpYykKCmBgYAoKCgoKCgojIyMjIyMjIyMjIyMjIyMjIwojIyMjIyBGaWd1cmUgNiAoYW5kIGZpZ3VyZSAxOSBpbiBzdXBwbGVtZW50YXJ5IG1hdGVyaWFscykKIyMjIyMjIyMjIyMjIyMjIyMKCgpgYGB7cn0KZ2dwbG90KG1lYW5fdG9waWNzLCBhZXMoeD1ZZWFyKSkgKwogIGdlb21fYXJlYShhZXMoeT1ldTEwX3RwKSwgZmlsbCA9ICJyZWQiLCBhbHBoYT0wLjMpICsKICB5bGFiKCJFVSBUb3BpY3MgUHJvcG9ydGlvbiIpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzPWMoMTk5MCwyMDAwLCAyMDEwKSkgKwojICBnZ3RpdGxlKCJUb3RhbCBFVSB0b3BpYyBwcm9wb3J0aW9ucyBhdmVyYWdlIHBlciB5ZWFyIHBlciBjb3VudHJ5IGdyb3VwOiBFVTEwIikgKwogIHRoZW1lX2J3KCkKCmdnc2F2ZSgiZXUxMF90b3BpY19wcm9wb3J0aW9ucy5wZGYiKQpgYGAKCgoKYGBge3J9CmdncGxvdChtZWFuX3RvcGljcywgYWVzKHg9WWVhcikpICsKICBnZW9tX2FyZWEoYWVzKHk9ZXUxMF90cCksIGZpbGwgPSAicmVkIiwgYWxwaGE9MC4zKSArCiAgZ2VvbV9hcmVhKGFlcyh5PWV1Nl90cCksIGZpbGwgPSAiYmx1ZSIsIGFscGhhPTAuMykgKwogIGdlb21fbGluZShhZXMoeT11c2FfdHApLCBjb2xvdXIgPSAiYmxhY2siKSArCiAgZ2VvbV9saW5lKGFlcyh5PXJ1c190cCksIGNvbG91ciA9ICJibGFjayIsIGxpbmV0eXBlID0gImRhc2hlZCIpICsKICB5bGFiKCJFVSBUb3BpY3MgUHJvcG9ydGlvbiIpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzPWMoMTk5MCwyMDAwLCAyMDEwKSkgKwojICBnZ3RpdGxlKCJUb3RhbCBFVSB0b3BpYyBwcm9wb3J0aW9ucyBhdmVyYWdlIHBlciB5ZWFyIHBlciBjb3VudHJ5IGdyb3VwOiBFVTEwIChyZWQpIHZzIEVVNiIpICsKICB0aGVtZV9idygpCgpnZ3NhdmUoImV1MTBfZXU2X3RvcGljX3Byb3BvcnRpb25zX3J1c191c2EucGRmIikKYGBgCgoKCgoKCiMjIyMjIyMjIyMjIyMjIyMjCiMjIyMjIEZpZ3VyZSAxOCBpbiBzdXBwbGVtZW50YXJ5IG1hdGVyaWFscwojIyMjIyMjIyMjIyMjIyMjIwoKCgoKYGBge3J9CmdncGxvdChtZWFuX3RvcGljcywgYWVzKHg9WWVhcikpICsKICBnZW9tX2FyZWEoYWVzKHk9ZXUxMF90cCksIGZpbGwgPSAicmVkIiwgYWxwaGE9MC4zKSArCiAgZ2VvbV9hcmVhKGFlcyh5PWV1Nl90cCksIGZpbGwgPSAiYmx1ZSIsIGFscGhhPTAuMykgKwogIHlsYWIoIkVVIFRvcGljcyBQcm9wb3J0aW9uIikgKwogICAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcz1jKDE5OTAsMjAwMCwgMjAxMCkpICsKIyAgZ2d0aXRsZSgiVG90YWwgRVUgdG9waWMgcHJvcG9ydGlvbnMgYXZlcmFnZSBwZXIgeWVhciBwZXIgY291bnRyeSBncm91cDogRVUxMCAocmVkKSB2cyBFVTYiKSArCiAgdGhlbWVfYncoKQoKZ2dzYXZlKCJldTEwX2V1Nl90b3BpY19wcm9wb3J0aW9ucy5wZGYiKQpgYGAKCgoK