Introduction

This document is an exploratory analysis of all accepted full papers, and posters at the GIScience conference series. The analysis is based on the text analysis published in “Reproducible research and GIScience: an evaluation using AGILE conference papers” (https://doi.org/10.7717/peerj.5072).

library("here")
library("pdftools")
library("stringr")
library("tidyverse")
library("tidytext")
library("wordcloud")
library("RColorBrewer")
library("grid")
library("gridBase")
library("gridExtra")
library("kableExtra")
library("quanteda")

# for deterministic cloud rendering
set.seed(nchar("International Conference on Geographic Information Science"))

Load data

List of proceedings

LNCS proceedings are available at the publisher website: https://link.springer.com/conference/giscience.

Note: The 2018 proceedings include the short papers in the same document. For comparability, only the full papers are taken into account for the analysis below.

data_path <- here::here("proceedings")
proceedings <- c(
  "2002" = "geographic-information-science-2002.pdf",
  "2004" = "geographic-information-science-2004.pdf",
  "2006" = "geographic-information-science-2006.pdf",
  "2008" = "geographic-information-science-2008.pdf",
  "2010" = "geographic-information-science-2010.pdf",
  "2012" = "10.1007_978-3-642-33024-7.pdf",
  "2014" = "10.1007_978-3-319-11593-1.pdf",
  "2016" = "10.1007_978-3-319-45738-3.pdf",
  "2018" = "lipics-vol114-giscience2018-complete.pdf"
)
proceedings_files <- file.path(data_path, proceedings)
names(proceedings_files) <- names(proceedings)

Add the PDFs to a directory called /home/rstudio/proceedings next to the file giscience-historic-text-analysis.Rmd (this file). The proceedings of the papers are not openly available for the years 2012 to 2016. You can contact the original paper authors and ask for the test dataset to reproduce the full analysis. Alternatively, you can download the 2018 proceedings from the LIPIcs website (Open Access; direct PDF link) and conduct the analysis with that subset of the data. For the analysis below the following input files were used:

knitr::kable(tibble(year = names(proceedings), file = proceedings)) %>%
  kableExtra::kable_styling("striped", full_width = FALSE)
year file
2002 geographic-information-science-2002.pdf
2004 geographic-information-science-2004.pdf
2006 geographic-information-science-2006.pdf
2008 geographic-information-science-2008.pdf
2010 geographic-information-science-2010.pdf
2012 10.1007_978-3-642-33024-7.pdf
2014 10.1007_978-3-319-11593-1.pdf
2016 10.1007_978-3-319-45738-3.pdf
2018 lipics-vol114-giscience2018-complete.pdf
# Code not evaluated when document is rendered!
dir.create(data_path, showWarnings = FALSE)

library("googledrive")
drive_dir <- googledrive::drive_get("https://drive.google.com/drive/folders/17EUtM_zCx1gQMea1MHN_5XSVrssxv9GA")
drive_dir_contents <- googledrive::drive_ls(drive_dir)
for (i in rownames(drive_dir_contents)) {
  current <- drive_dir_contents[i,]
  if(endsWith(current$name, ".pdf"))
    googledrive::drive_download(as_id(current$id), file.path(data_path, current$name))
}

The text is extracted from PDFs and it is processed to create a tidy data structure without stop words. The stop words include specific words, such as university, which is included in many pages, abbreviations such as e.g., and terms particular to scientific articles, such as figure. Also all numeric literas are removed from the word list.

texts <- lapply(proceedings_files, pdftools::pdf_text)

if(params$with_sp) {
  texts[["2018-sp"]] <- texts[["2018"]][c(283:length(texts[["2018"]]))]
  proceedings_files <- c(proceedings_files, `2018-sp` = proceedings_files[[4]])
}

# don't include short papers in 2018 year
texts[["2018"]] <- texts[["2018"]][c(1:282)]

texts <- unlist(lapply(texts, stringr::str_c, collapse = TRUE))

tidy_texts <- tibble::tibble(year = names(texts),
                             path = proceedings_files,
                             text = texts)

# create a table of all words
all_words <- tidy_texts %>%
  dplyr::select(year, text) %>%
  tidytext::unnest_tokens(word, text)

# remove stop words and remove numbers
my_stop_words <- tibble::tibble(
  word = c(
    "et",
    "al",
    "fig",
    "e.g",
    "i.e",
    "http",
    "ing",
    "pp",
    "figure",
    "based",
    "conference",
    "university",
    "table"
  ),
  lexicon = "giscience"
)

all_stop_words <- stop_words %>%
  dplyr::bind_rows(my_stop_words)
suppressWarnings({
  no_numbers <- all_words %>%
    dplyr::filter(is.na(as.numeric(word)))
})

no_stop_words <- no_numbers %>%
  dplyr::anti_join(all_stop_words, by = "word")

total_words = nrow(no_numbers)
after_cleanup = nrow(no_stop_words)

About 50 % of the words are considered stop words. The following tables shows how many non-stop words each conference year has, sorted by number of non-stop words (descending).

nonstopwords_per_year <- no_stop_words %>%
  dplyr::group_by(year) %>%
  dplyr::summarise(words = n()) %>%
  dplyr::arrange(desc(words)) %>%
  dplyr::rename(`non-stop words` = words)

words_per_year <- no_numbers %>%
  dplyr::group_by(year) %>%
  dplyr::summarise(words = n()) %>%
  dplyr::arrange(desc(words)) %>%
  dplyr::rename(`all words` = words)

dplyr::inner_join(nonstopwords_per_year, words_per_year, by = "year") %>%
  dplyr::bind_rows(tibble(year = "Total",
                   `non-stop words` = sum(nonstopwords_per_year$`non-stop words`),
                   `all words` = sum(words_per_year$`all words`))) %>%
  knitr::kable() %>%
  kableExtra::kable_styling("striped", full_width = FALSE) %>%
  kableExtra::row_spec(nrow(nonstopwords_per_year) + 1, bold = TRUE)
year non-stop words all words
2006 80336 168616
2014 74995 149063
2012 73440 141769
2004 68060 134234
2016 66642 130669
2008 64056 131032
2018 62111 121388
2002 58636 117016
2010 55408 110342
Total 603684 1204129

Top wordstems and wordstem clouds

# chosen manually
minimum_occurence <- 99
max_words <- 100

The following table shows the number of occurence for the 100 most occuring wordstems across all proceedings.

wordstems <- no_stop_words %>%
  dplyr::mutate(wordstem = quanteda::char_wordstem(no_stop_words$word))

countYearsUsingWordstem <- function(the_word) {
  sapply(the_word, function(w) {
    wordstems %>%
      dplyr::filter(wordstem == w) %>%
      dplyr::group_by(year) %>%
      dplyr::count() %>%
      nrow
  })
}

top_wordstems <- wordstems %>%
  dplyr::group_by(wordstem) %>%
  dplyr::tally() %>%
  dplyr::arrange(desc(n)) %>%
  head(n = max_words) %>%
  dplyr::mutate(`years w/ wordstem` = countYearsUsingWordstem(wordstem)) %>%
  tibble::add_column(place = c(1:nrow(.)), .before = 0)

write.csv(top_wordstems, here::here("results/text_analysis_topwordstems.csv"), row.names = FALSE)

top_wordstems %>%
  knitr::kable() %>%
  kableExtra::kable_styling("striped", full_width = FALSE) %>%
  kableExtra::scroll_box(height = "300px")
place wordstem n years w/ wordstem
1 data 7087 9
2 spatial 5442 9
3 model 4249 9
4 relat 4063 9
5 time 3547 9
6 inform 3419 9
7 set 3334 9
8 map 3331 9
9 object 2962 9
10 network 2653 9
11 locat 2642 9
12 region 2560 9
13 geograph 2553 9
14 comput 2347 9
15 result 2301 9
16 space 2267 9
17 algorithm 2234 9
18 node 2191 9
19 approach 1957 9
20 system 1938 9
21 rout 1904 9
22 true 1901 9
23 type 1890 9
24 method 1886 9
25 distanc 1824 9
26 edg 1824 9
27 queri 1721 9
28 process 1683 9
29 similar 1678 9
30 user 1600 9
31 repres 1560 9
32 intersect 1521 9
33 section 1521 9
34 analysi 1499 9
35 line 1487 9
36 road 1458 9
37 ontolog 1442 9
38 direct 1431 9
39 structur 1429 9
40 featur 1331 9
41 graph 1331 9
42 function 1319 9
43 measur 1315 9
44 pattern 1307 9
45 label 1293 9
46 research 1278 9
47 tempor 1276 9
48 paper 1224 9
49 event 1202 9
50 studi 1201 9
51 ed 1198 9
52 refer 1175 9
53 concept 1151 9
54 level 1148 9
55 path 1142 9
56 cell 1097 9
57 semant 1097 9
58 generat 1091 9
59 applic 1078 9
60 topolog 1048 9
61 provid 1036 9
62 class 1024 9
63 scienc 1009 9
64 intern 1008 9
65 oper 1003 9
66 requir 1000 9
67 boundari 992 9
68 term 988 9
69 local 978 9
70 develop 977 9
71 includ 966 9
72 defin 953 9
73 sensor 945 9
74 valu 923 9
75 chang 922 9
76 step 922 9
77 appli 915 9
78 gis 912 9
79 select 906 9
80 instanc 901 9
81 complex 900 9
82 scale 898 9
83 segment 891 9
84 environ 885 9
85 dataset 881 9
86 databas 876 9
87 connect 872 9
88 φ 872 5
89 represent 862 9
90 differ 857 8
91 size 845 9
92 cluster 843 9
93 decis 843 9
94 attribut 841 9
95 search 839 9
96 perform 830 9
97 properti 828 9
98 support 824 9
99 experi 820 9
100 distribut 813 9

The following clouds and table are based on word stems extracted with a stemming algorithm from package quanteda. Words must occur at least 99 times to be included in the cloud. Each cloud has a maximum of 100 words.

cloud_wordstems <- wordstems %>%
  dplyr::group_by(year, wordstem) %>%
  dplyr::tally() %>%
  dplyr::arrange(desc(n))
# plot is created to file to fit more words to a specific pixel size
png(filename = here::here("results/text_analysis_wordstemclouds.png"),
    width = 1000,
    height = 1000)

par(mfrow = c(3,3))
for (the_year in names(proceedings)) {
  year_cloud_wordstems <- cloud_wordstems %>%
    dplyr::filter(year == the_year) %>%
    dplyr::filter(n >= minimum_occurence) %>%
    head(n = max_words)
  #cat(str(year_cloud_wordstems))
  
  wordcloud::wordcloud(words = year_cloud_wordstems$wordstem,
                       freq = year_cloud_wordstems$n,
                       min.freq = 1,
                       random.order = FALSE,
                       fixed.asp = FALSE,
                       rot.per = 0,
                       color = brewer.pal(8, "Dark2"))
}
dev.off()
## png 
##   2
file.copy(from = here::here("results/text_analysis_wordstemclouds.png"),
          to = here::here("docs/text_analysis_wordstemclouds.png"),
          overwrite = TRUE)
## [1] TRUE

World clouds of full papers per conference year (rowwise, starting top left, from 2002 to 2018).

Colophon

This document is licensed under a Creative Commons Attribution 4.0 International License. All contained code is licensed under the Apache License 2.0. This document is versioned in a public git repository, https://github.com/nuest/reproducible-research-at-giscience, and archived on Zenodo at https://doi.org/10.5281/zenodo.4032875.

Runtime environment description:

## ─ Session info ───────────────────────────────────────────────────────────────
##  setting  value                       
##  version  R version 3.6.3 (2020-02-29)
##  os       Debian GNU/Linux 10 (buster)
##  system   x86_64, linux-gnu           
##  ui       X11                         
##  language (EN)                        
##  collate  en_US.UTF-8                 
##  ctype    en_US.UTF-8                 
##  tz       Etc/UTC                     
##  date     2021-06-01                  
## 
## ─ Packages ───────────────────────────────────────────────────────────────────
##  package      * version date       lib source                            
##  askpass        1.1     2019-01-13 [1] CRAN (R 3.6.3)                    
##  assertthat     0.2.1   2019-03-21 [1] CRAN (R 3.6.3)                    
##  backports      1.1.6   2020-04-05 [1] CRAN (R 3.6.3)                    
##  base         * 3.6.3   2020-05-14 [2] local                             
##  broom          0.5.6   2020-04-20 [1] CRAN (R 3.6.3)                    
##  callr          3.4.3   2020-03-28 [1] CRAN (R 3.6.3)                    
##  cellranger     1.1.0   2016-07-27 [1] CRAN (R 3.6.3)                    
##  cli            2.0.2   2020-02-28 [1] CRAN (R 3.6.3)                    
##  colorspace     1.4-1   2019-03-18 [1] CRAN (R 3.6.3)                    
##  compiler       3.6.3   2020-05-14 [2] local                             
##  crayon         1.3.4   2017-09-16 [1] CRAN (R 3.6.3)                    
##  data.table     1.12.8  2019-12-09 [1] CRAN (R 3.6.3)                    
##  datasets     * 3.6.3   2020-05-14 [2] local                             
##  DBI            1.1.0   2019-12-15 [1] CRAN (R 3.6.3)                    
##  dbplyr         1.4.3   2020-04-19 [1] CRAN (R 3.6.3)                    
##  desc           1.2.0   2018-05-01 [1] CRAN (R 3.6.3)                    
##  devtools       2.3.0   2020-04-10 [1] CRAN (R 3.6.3)                    
##  digest         0.6.25  2020-02-23 [1] CRAN (R 3.6.3)                    
##  dplyr        * 0.8.5   2020-03-07 [1] CRAN (R 3.6.3)                    
##  ellipsis       0.3.0   2019-09-20 [1] CRAN (R 3.6.3)                    
##  evaluate       0.14    2019-05-28 [1] CRAN (R 3.6.3)                    
##  fansi          0.4.1   2020-01-08 [1] CRAN (R 3.6.3)                    
##  fastmatch      1.1-0   2017-01-28 [1] CRAN (R 3.6.3)                    
##  forcats      * 0.5.0   2020-03-01 [1] CRAN (R 3.6.3)                    
##  fs             1.4.1   2020-04-04 [1] CRAN (R 3.6.3)                    
##  generics       0.0.2   2018-11-29 [1] CRAN (R 3.6.3)                    
##  ggplot2      * 3.3.0   2020-03-05 [1] CRAN (R 3.6.3)                    
##  glue           1.4.0   2020-04-03 [1] CRAN (R 3.6.3)                    
##  graphics     * 3.6.3   2020-05-14 [2] local                             
##  grDevices    * 3.6.3   2020-05-14 [2] local                             
##  grid         * 3.6.3   2020-05-14 [2] local                             
##  gridBase     * 0.4-7   2014-02-24 [1] CRAN (R 3.6.3)                    
##  gridExtra    * 2.3     2017-09-09 [1] CRAN (R 3.6.3)                    
##  gtable         0.3.0   2019-03-25 [1] CRAN (R 3.6.3)                    
##  haven          2.2.0   2019-11-08 [1] CRAN (R 3.6.3)                    
##  here         * 0.1     2017-05-28 [1] CRAN (R 3.6.3)                    
##  highr          0.8     2019-03-20 [1] CRAN (R 3.6.3)                    
##  hms            0.5.3   2020-01-08 [1] CRAN (R 3.6.3)                    
##  htmltools      0.4.0   2019-10-04 [1] CRAN (R 3.6.3)                    
##  httr           1.4.1   2019-08-05 [1] CRAN (R 3.6.3)                    
##  janeaustenr    0.1.5   2017-06-10 [1] CRAN (R 3.6.3)                    
##  jsonlite       1.6.1   2020-02-02 [1] CRAN (R 3.6.3)                    
##  kableExtra   * 1.1.0   2019-03-16 [1] CRAN (R 3.6.3)                    
##  knitr          1.28    2020-02-06 [1] CRAN (R 3.6.3)                    
##  lattice        0.20-38 2018-11-04 [2] CRAN (R 3.6.3)                    
##  lifecycle      0.2.0   2020-03-06 [1] CRAN (R 3.6.3)                    
##  lubridate      1.7.8   2020-04-06 [1] CRAN (R 3.6.3)                    
##  magrittr       1.5     2014-11-22 [1] CRAN (R 3.6.3)                    
##  Matrix         1.2-18  2019-11-27 [2] CRAN (R 3.6.3)                    
##  memoise        1.1.0   2017-04-21 [1] CRAN (R 3.6.3)                    
##  methods      * 3.6.3   2020-05-14 [2] local                             
##  modelr         0.1.6   2020-02-22 [1] CRAN (R 3.6.3)                    
##  munsell        0.5.0   2018-06-12 [1] CRAN (R 3.6.3)                    
##  nlme           3.1-144 2020-02-06 [2] CRAN (R 3.6.3)                    
##  pdftools     * 2.3     2019-11-10 [1] CRAN (R 3.6.3)                    
##  pillar         1.4.3   2019-12-20 [1] CRAN (R 3.6.3)                    
##  pkgbuild       1.0.6   2019-10-09 [1] CRAN (R 3.6.3)                    
##  pkgconfig      2.0.3   2019-09-22 [1] CRAN (R 3.6.3)                    
##  pkgload        1.0.2   2018-10-29 [1] CRAN (R 3.6.3)                    
##  prettyunits    1.1.1   2020-01-24 [1] CRAN (R 3.6.3)                    
##  processx       3.4.2   2020-02-09 [1] CRAN (R 3.6.3)                    
##  ps             1.3.2   2020-02-13 [1] CRAN (R 3.6.3)                    
##  purrr        * 0.3.4   2020-04-17 [1] CRAN (R 3.6.3)                    
##  qpdf           1.1     2019-03-07 [1] CRAN (R 3.6.3)                    
##  quanteda     * 2.0.1   2020-03-18 [1] CRAN (R 3.6.3)                    
##  R6             2.4.1   2019-11-12 [1] CRAN (R 3.6.3)                    
##  RColorBrewer * 1.1-2   2014-12-07 [1] CRAN (R 3.6.3)                    
##  Rcpp           1.0.4.6 2020-04-09 [1] CRAN (R 3.6.3)                    
##  RcppParallel   5.0.0   2020-03-11 [1] CRAN (R 3.6.3)                    
##  readr        * 1.3.1   2018-12-21 [1] CRAN (R 3.6.3)                    
##  readxl         1.3.1   2019-03-13 [1] CRAN (R 3.6.3)                    
##  remotes        2.1.1   2020-02-15 [1] CRAN (R 3.6.3)                    
##  reprex         0.3.0   2019-05-16 [1] CRAN (R 3.6.3)                    
##  rlang          0.4.5   2020-03-01 [1] CRAN (R 3.6.3)                    
##  rmarkdown      2.5     2021-06-01 [1] Github (rstudio/rmarkdown@4ff2093)
##  rprojroot      1.3-2   2018-01-03 [1] CRAN (R 3.6.3)                    
##  rstudioapi     0.11    2020-02-07 [1] CRAN (R 3.6.3)                    
##  rvest          0.3.5   2019-11-08 [1] CRAN (R 3.6.3)                    
##  scales         1.1.0   2019-11-18 [1] CRAN (R 3.6.3)                    
##  sessioninfo    1.1.1   2018-11-05 [1] CRAN (R 3.6.3)                    
##  SnowballC      0.7.0   2020-04-01 [1] CRAN (R 3.6.3)                    
##  stats        * 3.6.3   2020-05-14 [2] local                             
##  stopwords      2.0     2020-04-14 [1] CRAN (R 3.6.3)                    
##  stringi        1.4.6   2020-02-17 [1] CRAN (R 3.6.3)                    
##  stringr      * 1.4.0   2019-02-10 [1] CRAN (R 3.6.3)                    
##  testthat       2.3.2   2020-03-02 [1] CRAN (R 3.6.3)                    
##  tibble       * 3.0.1   2020-04-20 [1] CRAN (R 3.6.3)                    
##  tidyr        * 1.0.2   2020-01-24 [1] CRAN (R 3.6.3)                    
##  tidyselect     1.0.0   2020-01-27 [1] CRAN (R 3.6.3)                    
##  tidytext     * 0.2.4   2020-04-17 [1] CRAN (R 3.6.3)                    
##  tidyverse    * 1.3.0   2019-11-21 [1] CRAN (R 3.6.3)                    
##  tokenizers     0.2.1   2018-03-29 [1] CRAN (R 3.6.3)                    
##  tools          3.6.3   2020-05-14 [2] local                             
##  usethis        1.6.0   2020-04-09 [1] CRAN (R 3.6.3)                    
##  utils        * 3.6.3   2020-05-14 [2] local                             
##  vctrs          0.2.4   2020-03-10 [1] CRAN (R 3.6.3)                    
##  viridisLite    0.3.0   2018-02-01 [1] CRAN (R 3.6.3)                    
##  webshot        0.5.2   2019-11-22 [1] CRAN (R 3.6.3)                    
##  withr          2.2.0   2020-04-20 [1] CRAN (R 3.6.3)                    
##  wordcloud    * 2.6     2018-08-24 [1] CRAN (R 3.6.3)                    
##  xfun           0.15    2021-06-01 [1] Github (yihui/xfun@06e86a6)       
##  xml2           1.3.2   2020-04-23 [1] CRAN (R 3.6.3)                    
##  yaml           2.2.1   2020-02-01 [1] CRAN (R 3.6.3)                    
## 
## [1] /usr/local/lib/R/site-library
## [2] /usr/local/lib/R/library
LS0tCnRpdGxlOiAiVGV4dCBhbmFseXNpcyBvZiBhY2NlcHRlZCBmdWxsIHBhcGVycyBhdCBoaXN0b3JpYyBHSVNjaWVuY2UgY29uZmVyZW5jZXMiCmF1dGhvcjogRGFuaWVsIE7DvHN0LCBPcGVuaW5nIFJlcHJvZHVjaWJsZSBSZXNlYXJjaCAobzJyKSwgSW5zdGl0dXRlIGZvciBHZW9pbmZvcm1hdGljcywKICBVbml2ZXJzaXR5IG9mIE3DvG5zdGVyCmRhdGU6ICJgciBmb3JtYXQoU3lzLnRpbWUoKSwgJyVkICVCLCAlWScpYCIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IHllcwogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgY29kZV9mb2xkaW5nOiBoaWRlCiAgICBzZWxmX2NvbnRhaW5lZDogZmFsc2UKICAgIGxpYl9kaXI6IGxpYnMKcGFyYW1zOgogIHdpdGhfc3A6IG5vCi0tLQoKYGBge2NzcywgZWNobz1GQUxTRX0KcHJlIHsKICBmb250LXNpemU6IDEycHg7CiAgb3ZlcmZsb3cteDogYXV0bzsKfQpwcmUgY29kZSB7CiAgd29yZC13cmFwOiBub3JtYWw7CiAgd2hpdGUtc3BhY2U6IHByZTsKfQpgYGAKCiMjIEludHJvZHVjdGlvbgoKVGhpcyBkb2N1bWVudCBpcyBhbiBleHBsb3JhdG9yeSBhbmFseXNpcyBvZiBhbGwgYWNjZXB0ZWQgZnVsbCBwYXBlcnMsIGFuZCBwb3N0ZXJzIGF0IHRoZSBbR0lTY2llbmNlIGNvbmZlcmVuY2Ugc2VyaWVzXShodHRwczovL3d3dy5naXNjaWVuY2Uub3JnLykuClRoZSBhbmFseXNpcyBpcyBiYXNlZCBvbiB0aGUgdGV4dCBhbmFseXNpcyBwdWJsaXNoZWQgaW4gXyJSZXByb2R1Y2libGUgcmVzZWFyY2ggYW5kIEdJU2NpZW5jZTogYW4gZXZhbHVhdGlvbiB1c2luZyBBR0lMRSBjb25mZXJlbmNlIHBhcGVycyJfIChbaHR0cHM6Ly9kb2kub3JnLzEwLjc3MTcvcGVlcmouNTA3Ml0oaHR0cHM6Ly9kb2kub3JnLzEwLjc3MTcvcGVlcmouNTA3MikpLgoKYGBge3IgbG9hZF9saWJyYXJpZXMsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoImhlcmUiKQpsaWJyYXJ5KCJwZGZ0b29scyIpCmxpYnJhcnkoInN0cmluZ3IiKQpsaWJyYXJ5KCJ0aWR5dmVyc2UiKQpsaWJyYXJ5KCJ0aWR5dGV4dCIpCmxpYnJhcnkoIndvcmRjbG91ZCIpCmxpYnJhcnkoIlJDb2xvckJyZXdlciIpCmxpYnJhcnkoImdyaWQiKQpsaWJyYXJ5KCJncmlkQmFzZSIpCmxpYnJhcnkoImdyaWRFeHRyYSIpCmxpYnJhcnkoImthYmxlRXh0cmEiKQpsaWJyYXJ5KCJxdWFudGVkYSIpCgojIGZvciBkZXRlcm1pbmlzdGljIGNsb3VkIHJlbmRlcmluZwpzZXQuc2VlZChuY2hhcigiSW50ZXJuYXRpb25hbCBDb25mZXJlbmNlIG9uIEdlb2dyYXBoaWMgSW5mb3JtYXRpb24gU2NpZW5jZSIpKQpgYGAKCiMjIExvYWQgZGF0YQoKKipMaXN0IG9mIHByb2NlZWRpbmdzKioKCi0gUHJvY2VlZGluZ3MgMTB0aCBJbnRlcm5hdGlvbmFsIENvbmZlcmVuY2Ugb24gR2VvZ3JhcGhpYyBJbmZvcm1hdGlvbiBTY2llbmNlIChHSVNjaWVuY2UgMjAxOCkuIDIwMTguIFdpbnRlciwgUy4sIEdyaWZmaW4sIEEuLCBTZXN0ZXIsIE0uIChFZHMuKSwgTElQSUNTIFZvbC4gMTE0LiBJU0JOIDk3OC0zLTk1OTc3LTA4My01LiBodHRwOi8vd3d3LmRhZ3N0dWhsLmRlL2RhZ3B1Yi85NzgtMy05NTk3Ny0wODMtNQotIEdlb2dyYXBoaWMgSW5mb3JtYXRpb24gU2NpZW5jZS4gMjAxNi4gSi4gQS4gTWlsbGVyLCBELiBP4oCZU3VsbGl2YW4sICYgTi4gV2llZ2FuZCAoRWRzLiksIExlY3R1cmUgTm90ZXMgaW4gQ29tcHV0ZXIgU2NpZW5jZS4gU3ByaW5nZXIgSW50ZXJuYXRpb25hbCBQdWJsaXNoaW5nLiBodHRwczovL2RvaS5vcmcvMTAuMTAwNy85NzgtMy0zMTktNDU3MzgtMwotIEdlb2dyYXBoaWMgSW5mb3JtYXRpb24gU2NpZW5jZS4gMjAxNC4gTS4gRHVja2hhbSwgRS4gUGViZXNtYSwgSy4gU3Rld2FydCwgJiBBLiBVLiBGcmFuayAoRWRzLiksIExlY3R1cmUgTm90ZXMgaW4gQ29tcHV0ZXIgU2NpZW5jZS4gU3ByaW5nZXIgSW50ZXJuYXRpb25hbCBQdWJsaXNoaW5nLiBodHRwczovL2RvaS5vcmcvMTAuMTAwNy85NzgtMy0zMTktMTE1OTMtMQotIEdlb2dyYXBoaWMgSW5mb3JtYXRpb24gU2NpZW5jZS4gMjAxMi4gTi4gWGlhbywgTS4tUC4gS3dhbiwgTS4gRi4gR29vZGNoaWxkLCAmIFMuIFNoZWtoYXIgKEVkcy4pLCBMZWN0dXJlIE5vdGVzIGluIENvbXB1dGVyIFNjaWVuY2UuIFNwcmluZ2VyIEJlcmxpbiBIZWlkZWxiZXJnLiBodHRwczovL2RvaS5vcmcvMTAuMTAwNy85NzgtMy02NDItMzMwMjQtNwotIEdlb2dyYXBoaWMgSW5mb3JtYXRpb24gU2NpZW5jZS4gMjAxMC4gRmFicmlrYW50LCBTLkkuLCBSZWljaGVuYmFjaGVyLCBULiwgS3JldmVsZCwgTS4gdmFuLCBTY2hsaWVkZXIsIEMuIChFZHMuKSwgTGVjdHVyZSBOb3RlcyBpbiBDb21wdXRlciBTY2llbmNlLiBTcHJpbmdlciBCZXJsaW4gSGVpZGVsYmVyZy4gaHR0cHM6Ly9kb2kub3JnLzEwLjEwMDcvOTc4LTMtNjQyLTE1MzAwLTYKLSBHZW9ncmFwaGljIEluZm9ybWF0aW9uIFNjaWVuY2UuIDIwMDguIEluIENvdmEsIFQuSi4sIE1pbGxlciwgSC5KLiwgQmVhcmQsIEsuLCBGcmFuaywgQS5VLiwgR29vZGNoaWxkLCBNLkYuIChFZHMuKSwgTGVjdHVyZSBOb3RlcyBpbiBDb21wdXRlciBTY2llbmNlLiBTcHJpbmdlciBCZXJsaW4gSGVpZGVsYmVyZy4gaHR0cHM6Ly9kb2kub3JnLzEwLjEwMDcvOTc4LTMtNTQwLTg3NDczLTcKLSBHZW9ncmFwaGljIEluZm9ybWF0aW9uIFNjaWVuY2UuIDIwMDQuIEVnZW5ob2ZlciwgTS5KLiwgRnJla3NhLCBDLiwgTWlsbGVyLCBILkouIChFZHMuKSwgTGVjdHVyZSBOb3RlcyBpbiBDb21wdXRlciBTY2llbmNlLiBTcHJpbmdlciBCZXJsaW4gSGVpZGVsYmVyZy4gaHR0cHM6Ly9kb2kub3JnLzEwLjEwMDcvYjEwMTM5NwotIEdlb2dyYXBoaWMgSW5mb3JtYXRpb24gU2NpZW5jZS4gMjAwMi4gRWdlbmhvZmVyLCBNLkouLCBNYXJrLCBELk0uIChFZHMuKSwgTGVjdHVyZSBOb3RlcyBpbiBDb21wdXRlciBTY2llbmNlLiBTcHJpbmdlciBCZXJsaW4gSGVpZGVsYmVyZy4gaHR0cHM6Ly9kb2kub3JnLzEwLjEwMDcvMy01NDAtNDU3OTktMgotIEdlb2dyYXBoaWMgSW5mb3JtYXRpb24gU2NpZW5jZS4gMjAwNi4gUmF1YmFsLCBNLiwgTWlsbGVyLCBILkouLCBGcmFuaywgQS5VLiwgR29vZGNoaWxkLCBNLkYuIChFZHMuKSwgTGVjdHVyZSBOb3RlcyBpbiBDb21wdXRlciBTY2llbmNlLiBTcHJpbmdlciBCZXJsaW4gSGVpZGVsYmVyZy4gaHR0cHM6Ly9kb2kub3JnLzEwLjEwMDcvMTE4NjM5MzkKCkxOQ1MgcHJvY2VlZGluZ3MgYXJlIGF2YWlsYWJsZSBhdCB0aGUgcHVibGlzaGVyIHdlYnNpdGU6IFtodHRwczovL2xpbmsuc3ByaW5nZXIuY29tL2NvbmZlcmVuY2UvZ2lzY2llbmNlXShodHRwczovL2xpbmsuc3ByaW5nZXIuY29tL2NvbmZlcmVuY2UvZ2lzY2llbmNlKS4KCioqTm90ZToqKiBUaGUgMjAxOCBwcm9jZWVkaW5ncyBpbmNsdWRlIHRoZSBzaG9ydCBwYXBlcnMgaW4gdGhlIHNhbWUgZG9jdW1lbnQuCkZvciBjb21wYXJhYmlsaXR5LCBvbmx5IHRoZSBmdWxsIHBhcGVycyBhcmUgdGFrZW4gaW50byBhY2NvdW50IGZvciB0aGUgYW5hbHlzaXMgYmVsb3cuCgpgYGB7ciBpbnB1dF9maWxlc30KZGF0YV9wYXRoIDwtIGhlcmU6OmhlcmUoInByb2NlZWRpbmdzIikKcHJvY2VlZGluZ3MgPC0gYygKICAiMjAwMiIgPSAiZ2VvZ3JhcGhpYy1pbmZvcm1hdGlvbi1zY2llbmNlLTIwMDIucGRmIiwKICAiMjAwNCIgPSAiZ2VvZ3JhcGhpYy1pbmZvcm1hdGlvbi1zY2llbmNlLTIwMDQucGRmIiwKICAiMjAwNiIgPSAiZ2VvZ3JhcGhpYy1pbmZvcm1hdGlvbi1zY2llbmNlLTIwMDYucGRmIiwKICAiMjAwOCIgPSAiZ2VvZ3JhcGhpYy1pbmZvcm1hdGlvbi1zY2llbmNlLTIwMDgucGRmIiwKICAiMjAxMCIgPSAiZ2VvZ3JhcGhpYy1pbmZvcm1hdGlvbi1zY2llbmNlLTIwMTAucGRmIiwKICAiMjAxMiIgPSAiMTAuMTAwN185NzgtMy02NDItMzMwMjQtNy5wZGYiLAogICIyMDE0IiA9ICIxMC4xMDA3Xzk3OC0zLTMxOS0xMTU5My0xLnBkZiIsCiAgIjIwMTYiID0gIjEwLjEwMDdfOTc4LTMtMzE5LTQ1NzM4LTMucGRmIiwKICAiMjAxOCIgPSAibGlwaWNzLXZvbDExNC1naXNjaWVuY2UyMDE4LWNvbXBsZXRlLnBkZiIKKQpwcm9jZWVkaW5nc19maWxlcyA8LSBmaWxlLnBhdGgoZGF0YV9wYXRoLCBwcm9jZWVkaW5ncykKbmFtZXMocHJvY2VlZGluZ3NfZmlsZXMpIDwtIG5hbWVzKHByb2NlZWRpbmdzKQpgYGAKCkFkZCB0aGUgUERGcyB0byBhIGRpcmVjdG9yeSBjYWxsZWQgYCBgciBkYXRhX3BhdGhgIGAgbmV4dCB0byB0aGUgZmlsZSBgZ2lzY2llbmNlLWhpc3RvcmljLXRleHQtYW5hbHlzaXMuUm1kYCAodGhpcyBmaWxlKS4KVGhlIHByb2NlZWRpbmdzIG9mIHRoZSBwYXBlcnMgYXJlIG5vdCBvcGVubHkgYXZhaWxhYmxlIGZvciB0aGUgeWVhcnMgMjAxMiB0byAyMDE2LgpZb3UgY2FuIGNvbnRhY3QgdGhlIG9yaWdpbmFsIHBhcGVyIGF1dGhvcnMgYW5kIGFzayBmb3IgdGhlIHRlc3QgZGF0YXNldCB0byByZXByb2R1Y2UgdGhlIGZ1bGwgYW5hbHlzaXMuCkFsdGVybmF0aXZlbHksIHlvdSBjYW4gZG93bmxvYWQgdGhlIDIwMTggcHJvY2VlZGluZ3MgZnJvbSB0aGUgTElQSWNzIHdlYnNpdGUgKE9wZW4gQWNjZXNzOyBbZGlyZWN0IFBERiBsaW5rXShodHRwczovL2Ryb3BzLmRhZ3N0dWhsLmRlL29wdXMvdm9sbHRleHRlL2xpcGljcy1jb21wbGV0ZS9saXBpY3Mtdm9sMTE0LWdpc2NpZW5jZTIwMTgtY29tcGxldGUucGRmKSkgYW5kIGNvbmR1Y3QgdGhlIGFuYWx5c2lzIHdpdGggdGhhdCBzdWJzZXQgb2YgdGhlIGRhdGEuCkZvciB0aGUgYW5hbHlzaXMgYmVsb3cgdGhlIGZvbGxvd2luZyBpbnB1dCBmaWxlcyB3ZXJlIHVzZWQ6CgpgYGB7ciBsaXN0X2ZpbGVzfQprbml0cjo6a2FibGUodGliYmxlKHllYXIgPSBuYW1lcyhwcm9jZWVkaW5ncyksIGZpbGUgPSBwcm9jZWVkaW5ncykpICU+JQogIGthYmxlRXh0cmE6OmthYmxlX3N0eWxpbmcoInN0cmlwZWQiLCBmdWxsX3dpZHRoID0gRkFMU0UpCmBgYAoKYGBge3IgZGF0YV9kb3dubG9hZF9kcml2ZSwgZXZhbD1GQUxTRX0KIyBDb2RlIG5vdCBldmFsdWF0ZWQgd2hlbiBkb2N1bWVudCBpcyByZW5kZXJlZCEKZGlyLmNyZWF0ZShkYXRhX3BhdGgsIHNob3dXYXJuaW5ncyA9IEZBTFNFKQoKbGlicmFyeSgiZ29vZ2xlZHJpdmUiKQpkcml2ZV9kaXIgPC0gZ29vZ2xlZHJpdmU6OmRyaXZlX2dldCgiaHR0cHM6Ly9kcml2ZS5nb29nbGUuY29tL2RyaXZlL2ZvbGRlcnMvMTdFVXRNX3pDeDFnUU1lYTFNSE5fNVhTVnJzc3h2OUdBIikKZHJpdmVfZGlyX2NvbnRlbnRzIDwtIGdvb2dsZWRyaXZlOjpkcml2ZV9scyhkcml2ZV9kaXIpCmZvciAoaSBpbiByb3duYW1lcyhkcml2ZV9kaXJfY29udGVudHMpKSB7CiAgY3VycmVudCA8LSBkcml2ZV9kaXJfY29udGVudHNbaSxdCiAgaWYoZW5kc1dpdGgoY3VycmVudCRuYW1lLCAiLnBkZiIpKQogICAgZ29vZ2xlZHJpdmU6OmRyaXZlX2Rvd25sb2FkKGFzX2lkKGN1cnJlbnQkaWQpLCBmaWxlLnBhdGgoZGF0YV9wYXRoLCBjdXJyZW50JG5hbWUpKQp9CmBgYAoKVGhlIHRleHQgaXMgZXh0cmFjdGVkIGZyb20gUERGcyBhbmQgaXQgaXMgcHJvY2Vzc2VkIHRvIGNyZWF0ZSBhIFt0aWR5XShodHRwczovL3d3dy5qc3RhdHNvZnQub3JnL2FydGljbGUvdmlldy92MDU5aTEwKSBkYXRhIHN0cnVjdHVyZSB3aXRob3V0IFtzdG9wIHdvcmRzXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9TdG9wX3dvcmRzKS4KVGhlIHN0b3Agd29yZHMgaW5jbHVkZSBzcGVjaWZpYyB3b3Jkcywgc3VjaCBhcyBgdW5pdmVyc2l0eWAsIHdoaWNoIGlzIGluY2x1ZGVkIGluIG1hbnkgcGFnZXMsIGFiYnJldmlhdGlvbnMgc3VjaCBhcyBgZS5nLmAsIGFuZCB0ZXJtcyBwYXJ0aWN1bGFyIHRvIHNjaWVudGlmaWMgYXJ0aWNsZXMsIHN1Y2ggYXMgYGZpZ3VyZWAuCkFsc28gYWxsIG51bWVyaWMgbGl0ZXJhcyBhcmUgcmVtb3ZlZCBmcm9tIHRoZSB3b3JkIGxpc3QuCgpgYGB7ciBsb2FkX2ZpbGVzLCBjYWNoZT1UUlVFfQp0ZXh0cyA8LSBsYXBwbHkocHJvY2VlZGluZ3NfZmlsZXMsIHBkZnRvb2xzOjpwZGZfdGV4dCkKCmlmKHBhcmFtcyR3aXRoX3NwKSB7CiAgdGV4dHNbWyIyMDE4LXNwIl1dIDwtIHRleHRzW1siMjAxOCJdXVtjKDI4MzpsZW5ndGgodGV4dHNbWyIyMDE4Il1dKSldCiAgcHJvY2VlZGluZ3NfZmlsZXMgPC0gYyhwcm9jZWVkaW5nc19maWxlcywgYDIwMTgtc3BgID0gcHJvY2VlZGluZ3NfZmlsZXNbWzRdXSkKfQoKIyBkb24ndCBpbmNsdWRlIHNob3J0IHBhcGVycyBpbiAyMDE4IHllYXIKdGV4dHNbWyIyMDE4Il1dIDwtIHRleHRzW1siMjAxOCJdXVtjKDE6MjgyKV0KCnRleHRzIDwtIHVubGlzdChsYXBwbHkodGV4dHMsIHN0cmluZ3I6OnN0cl9jLCBjb2xsYXBzZSA9IFRSVUUpKQoKdGlkeV90ZXh0cyA8LSB0aWJibGU6OnRpYmJsZSh5ZWFyID0gbmFtZXModGV4dHMpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhdGggPSBwcm9jZWVkaW5nc19maWxlcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0ZXh0ID0gdGV4dHMpCgojIGNyZWF0ZSBhIHRhYmxlIG9mIGFsbCB3b3JkcwphbGxfd29yZHMgPC0gdGlkeV90ZXh0cyAlPiUKICBkcGx5cjo6c2VsZWN0KHllYXIsIHRleHQpICU+JQogIHRpZHl0ZXh0Ojp1bm5lc3RfdG9rZW5zKHdvcmQsIHRleHQpCgojIHJlbW92ZSBzdG9wIHdvcmRzIGFuZCByZW1vdmUgbnVtYmVycwpteV9zdG9wX3dvcmRzIDwtIHRpYmJsZTo6dGliYmxlKAogIHdvcmQgPSBjKAogICAgImV0IiwKICAgICJhbCIsCiAgICAiZmlnIiwKICAgICJlLmciLAogICAgImkuZSIsCiAgICAiaHR0cCIsCiAgICAiaW5nIiwKICAgICJwcCIsCiAgICAiZmlndXJlIiwKICAgICJiYXNlZCIsCiAgICAiY29uZmVyZW5jZSIsCiAgICAidW5pdmVyc2l0eSIsCiAgICAidGFibGUiCiAgKSwKICBsZXhpY29uID0gImdpc2NpZW5jZSIKKQoKYWxsX3N0b3Bfd29yZHMgPC0gc3RvcF93b3JkcyAlPiUKICBkcGx5cjo6YmluZF9yb3dzKG15X3N0b3Bfd29yZHMpCnN1cHByZXNzV2FybmluZ3MoewogIG5vX251bWJlcnMgPC0gYWxsX3dvcmRzICU+JQogICAgZHBseXI6OmZpbHRlcihpcy5uYShhcy5udW1lcmljKHdvcmQpKSkKfSkKCm5vX3N0b3Bfd29yZHMgPC0gbm9fbnVtYmVycyAlPiUKICBkcGx5cjo6YW50aV9qb2luKGFsbF9zdG9wX3dvcmRzLCBieSA9ICJ3b3JkIikKCnRvdGFsX3dvcmRzID0gbnJvdyhub19udW1iZXJzKQphZnRlcl9jbGVhbnVwID0gbnJvdyhub19zdG9wX3dvcmRzKQpgYGAKCkFib3V0IGByIHJvdW5kKGFmdGVyX2NsZWFudXAvdG90YWxfd29yZHMgKiAxMDApYCZuYnNwOyUgb2YgdGhlIHdvcmRzIGFyZSBjb25zaWRlcmVkIHN0b3Agd29yZHMuClRoZSBmb2xsb3dpbmcgdGFibGVzIHNob3dzIGhvdyBtYW55IG5vbi1zdG9wIHdvcmRzIGVhY2ggY29uZmVyZW5jZSB5ZWFyIGhhcywgc29ydGVkIGJ5IG51bWJlciBvZiBub24tc3RvcCB3b3JkcyAoZGVzY2VuZGluZykuCgpgYGB7ciBzdG9wX3dvcmRzLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpub25zdG9wd29yZHNfcGVyX3llYXIgPC0gbm9fc3RvcF93b3JkcyAlPiUKICBkcGx5cjo6Z3JvdXBfYnkoeWVhcikgJT4lCiAgZHBseXI6OnN1bW1hcmlzZSh3b3JkcyA9IG4oKSkgJT4lCiAgZHBseXI6OmFycmFuZ2UoZGVzYyh3b3JkcykpICU+JQogIGRwbHlyOjpyZW5hbWUoYG5vbi1zdG9wIHdvcmRzYCA9IHdvcmRzKQoKd29yZHNfcGVyX3llYXIgPC0gbm9fbnVtYmVycyAlPiUKICBkcGx5cjo6Z3JvdXBfYnkoeWVhcikgJT4lCiAgZHBseXI6OnN1bW1hcmlzZSh3b3JkcyA9IG4oKSkgJT4lCiAgZHBseXI6OmFycmFuZ2UoZGVzYyh3b3JkcykpICU+JQogIGRwbHlyOjpyZW5hbWUoYGFsbCB3b3Jkc2AgPSB3b3JkcykKCmRwbHlyOjppbm5lcl9qb2luKG5vbnN0b3B3b3Jkc19wZXJfeWVhciwgd29yZHNfcGVyX3llYXIsIGJ5ID0gInllYXIiKSAlPiUKICBkcGx5cjo6YmluZF9yb3dzKHRpYmJsZSh5ZWFyID0gIlRvdGFsIiwKICAgICAgICAgICAgICAgICAgIGBub24tc3RvcCB3b3Jkc2AgPSBzdW0obm9uc3RvcHdvcmRzX3Blcl95ZWFyJGBub24tc3RvcCB3b3Jkc2ApLAogICAgICAgICAgICAgICAgICAgYGFsbCB3b3Jkc2AgPSBzdW0od29yZHNfcGVyX3llYXIkYGFsbCB3b3Jkc2ApKSkgJT4lCiAga25pdHI6OmthYmxlKCkgJT4lCiAga2FibGVFeHRyYTo6a2FibGVfc3R5bGluZygic3RyaXBlZCIsIGZ1bGxfd2lkdGggPSBGQUxTRSkgJT4lCiAga2FibGVFeHRyYTo6cm93X3NwZWMobnJvdyhub25zdG9wd29yZHNfcGVyX3llYXIpICsgMSwgYm9sZCA9IFRSVUUpCmBgYAoKIyMgVG9wIHdvcmRzdGVtcyBhbmQgd29yZHN0ZW0gY2xvdWRzCgpgYGB7ciBwYXJhbXN9CiMgY2hvc2VuIG1hbnVhbGx5Cm1pbmltdW1fb2NjdXJlbmNlIDwtIDk5Cm1heF93b3JkcyA8LSAxMDAKYGBgCgpUaGUgZm9sbG93aW5nIHRhYmxlIHNob3dzIHRoZSBudW1iZXIgb2Ygb2NjdXJlbmNlIGZvciB0aGUgYHIgbWF4X3dvcmRzYCBtb3N0IG9jY3VyaW5nIHdvcmRzdGVtcyBhY3Jvc3MgYWxsIHByb2NlZWRpbmdzLgoKYGBge3IgdG9wX3dvcmRzdGVtc30Kd29yZHN0ZW1zIDwtIG5vX3N0b3Bfd29yZHMgJT4lCiAgZHBseXI6Om11dGF0ZSh3b3Jkc3RlbSA9IHF1YW50ZWRhOjpjaGFyX3dvcmRzdGVtKG5vX3N0b3Bfd29yZHMkd29yZCkpCgpjb3VudFllYXJzVXNpbmdXb3Jkc3RlbSA8LSBmdW5jdGlvbih0aGVfd29yZCkgewogIHNhcHBseSh0aGVfd29yZCwgZnVuY3Rpb24odykgewogICAgd29yZHN0ZW1zICU+JQogICAgICBkcGx5cjo6ZmlsdGVyKHdvcmRzdGVtID09IHcpICU+JQogICAgICBkcGx5cjo6Z3JvdXBfYnkoeWVhcikgJT4lCiAgICAgIGRwbHlyOjpjb3VudCgpICU+JQogICAgICBucm93CiAgfSkKfQoKdG9wX3dvcmRzdGVtcyA8LSB3b3Jkc3RlbXMgJT4lCiAgZHBseXI6Omdyb3VwX2J5KHdvcmRzdGVtKSAlPiUKICBkcGx5cjo6dGFsbHkoKSAlPiUKICBkcGx5cjo6YXJyYW5nZShkZXNjKG4pKSAlPiUKICBoZWFkKG4gPSBtYXhfd29yZHMpICU+JQogIGRwbHlyOjptdXRhdGUoYHllYXJzIHcvIHdvcmRzdGVtYCA9IGNvdW50WWVhcnNVc2luZ1dvcmRzdGVtKHdvcmRzdGVtKSkgJT4lCiAgdGliYmxlOjphZGRfY29sdW1uKHBsYWNlID0gYygxOm5yb3coLikpLCAuYmVmb3JlID0gMCkKCndyaXRlLmNzdih0b3Bfd29yZHN0ZW1zLCBoZXJlOjpoZXJlKCJyZXN1bHRzL3RleHRfYW5hbHlzaXNfdG9wd29yZHN0ZW1zLmNzdiIpLCByb3cubmFtZXMgPSBGQUxTRSkKCnRvcF93b3Jkc3RlbXMgJT4lCiAga25pdHI6OmthYmxlKCkgJT4lCiAga2FibGVFeHRyYTo6a2FibGVfc3R5bGluZygic3RyaXBlZCIsIGZ1bGxfd2lkdGggPSBGQUxTRSkgJT4lCiAga2FibGVFeHRyYTo6c2Nyb2xsX2JveChoZWlnaHQgPSAiMzAwcHgiKQpgYGAKClRoZSBmb2xsb3dpbmcgY2xvdWRzIGFuZCB0YWJsZSBhcmUgYmFzZWQgb24gd29yZCBzdGVtcyBleHRyYWN0ZWQgd2l0aCBhIHN0ZW1taW5nIGFsZ29yaXRobSBmcm9tIHBhY2thZ2UgW2BxdWFudGVkYWBdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3BhY2thZ2U9cXVhbnRlZGEpLgpXb3JkcyBtdXN0IG9jY3VyIGF0IGxlYXN0IGByIG1pbmltdW1fb2NjdXJlbmNlYCB0aW1lcyB0byBiZSBpbmNsdWRlZCBpbiB0aGUgY2xvdWQuCkVhY2ggY2xvdWQgaGFzIGEgbWF4aW11bSBvZiBgciBtYXhfd29yZHNgIHdvcmRzLgoKYGBge3IgY2xvdWRfd29yZHN0ZW1zLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpjbG91ZF93b3Jkc3RlbXMgPC0gd29yZHN0ZW1zICU+JQogIGRwbHlyOjpncm91cF9ieSh5ZWFyLCB3b3Jkc3RlbSkgJT4lCiAgZHBseXI6OnRhbGx5KCkgJT4lCiAgZHBseXI6OmFycmFuZ2UoZGVzYyhuKSkKYGBgCgpgYGB7ciB3b3JkY2xvdWRzX2NyZWF0ZV9wbG90LCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIHBsb3QgaXMgY3JlYXRlZCB0byBmaWxlIHRvIGZpdCBtb3JlIHdvcmRzIHRvIGEgc3BlY2lmaWMgcGl4ZWwgc2l6ZQpwbmcoZmlsZW5hbWUgPSBoZXJlOjpoZXJlKCJyZXN1bHRzL3RleHRfYW5hbHlzaXNfd29yZHN0ZW1jbG91ZHMucG5nIiksCiAgICB3aWR0aCA9IDEwMDAsCiAgICBoZWlnaHQgPSAxMDAwKQoKcGFyKG1mcm93ID0gYygzLDMpKQpmb3IgKHRoZV95ZWFyIGluIG5hbWVzKHByb2NlZWRpbmdzKSkgewogIHllYXJfY2xvdWRfd29yZHN0ZW1zIDwtIGNsb3VkX3dvcmRzdGVtcyAlPiUKICAgIGRwbHlyOjpmaWx0ZXIoeWVhciA9PSB0aGVfeWVhcikgJT4lCiAgICBkcGx5cjo6ZmlsdGVyKG4gPj0gbWluaW11bV9vY2N1cmVuY2UpICU+JQogICAgaGVhZChuID0gbWF4X3dvcmRzKQogICNjYXQoc3RyKHllYXJfY2xvdWRfd29yZHN0ZW1zKSkKICAKICB3b3JkY2xvdWQ6OndvcmRjbG91ZCh3b3JkcyA9IHllYXJfY2xvdWRfd29yZHN0ZW1zJHdvcmRzdGVtLAogICAgICAgICAgICAgICAgICAgICAgIGZyZXEgPSB5ZWFyX2Nsb3VkX3dvcmRzdGVtcyRuLAogICAgICAgICAgICAgICAgICAgICAgIG1pbi5mcmVxID0gMSwKICAgICAgICAgICAgICAgICAgICAgICByYW5kb20ub3JkZXIgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICBmaXhlZC5hc3AgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICByb3QucGVyID0gMCwKICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IGJyZXdlci5wYWwoOCwgIkRhcmsyIikpCn0KZGV2Lm9mZigpCgpmaWxlLmNvcHkoZnJvbSA9IGhlcmU6OmhlcmUoInJlc3VsdHMvdGV4dF9hbmFseXNpc193b3Jkc3RlbWNsb3Vkcy5wbmciKSwKICAgICAgICAgIHRvID0gaGVyZTo6aGVyZSgiZG9jcy90ZXh0X2FuYWx5c2lzX3dvcmRzdGVtY2xvdWRzLnBuZyIpLAogICAgICAgICAgb3ZlcndyaXRlID0gVFJVRSkKYGBgCgo8IS0tIHBhdGggZml4ZWQgdG8gb3V0cHV0IGluIGRvY3MvIGRpcmVjdG9yeSAtIHNlZSBNYWtlZmlsZSAtLT4KIVtdKHRleHRfYW5hbHlzaXNfd29yZHN0ZW1jbG91ZHMucG5nKQoKX2ByIHBhc3RlMCgiV29ybGQgY2xvdWRzIG9mIGZ1bGwgcGFwZXJzIHBlciBjb25mZXJlbmNlIHllYXIgKHJvd3dpc2UsIHN0YXJ0aW5nIHRvcCBsZWZ0LCBmcm9tICIsIGhlYWQobmFtZXMocHJvY2VlZGluZ3NfZmlsZXMpLCBuID0gMSksICIgdG8gIiwgdGFpbChuYW1lcyhwcm9jZWVkaW5nc19maWxlcyksIG4gPSAxKSwgIikuIilgXwoKIyMgUmVwcm9kdWNpYmxlIHJlc2VhcmNoLXJlbGF0ZWQga2V5d29yZHN0ZW1zIGluIEdJU2NpZW5jZSBwYXBlcnMKClRoZSBmb2xsb3dpbmcgdGFibGVzIGxpc3RzIGhvdyBvZnRlbiB3b3Jkc3RlbXMgb2YgdGVybXMgcmVsYXRlZCB0byByZXByb2R1Y2libGUgcmVzZWFyY2ggYXBwZWFyIGluIGVhY2ggZG9jdW1lbnQuClRoZSBkZXRlY3Rpb24gbWF0Y2hlcyBmdWxsIHdvcmRzIHVzaW5nIHJlZ2V4IG9wdGlvbiBgXGJgLgoKLSByZXByb2R1YyAoYHJlcHJvZHVjLipgLCByZXByb2R1Y2liaWxpdHksIHJlcHJvZHVjaWJsZSwgcmVwcm9kdWNlLCByZXByb2R1Y3Rpb24pCi0gcmVwbGljIChgcmVwbGljYXQuKmAsIGkuZS4gcmVwbGljYXRpb24sIHJlcGxpY2F0ZSkKLSByZXBlYXRhYiAoYHJlcGVhdGFiLipgLCBpLmUuIHJlcGVhdGFiaWxpdHksIHJlcGVhdGFibGUpCi0gc29mdHdhcmUKLSAocHNldWRvKSBjb2RlL3NjcmlwdChzKSBbY29sdW1uIG5hbWUgX2NvZGVfXQotIGFsZ29yaXRobSAoYGFsZ29yaXRobS4qYCwgaS5lLiBhbGdvcml0aG1zLCBhbGdvcml0aG1pYykKLSBwcm9jZXNzIChgcHJvY2Vzcy4qYCwgaS5lLiBwcm9jZXNzaW5nLCBwcm9jZXNzZXMsIHByZXByb2Nlc3NpbmcpCi0gZGF0YSAoYGRhdGEuKmAsIGkuZS4gZGF0YXNldChzKSwgZGF0YWJhc2UocykpCi0gcmVzdWx0KHMpIChgcmVzdWx0cz9gKQotIHJlcG9zaXRvcnkoaWVzKSAoYHJlcG9zaXRvcih5fGllcylgKQotIGNvbGxhYm9yYXRpb24gcGxhdGZvcm1zIChgZ2l0KGh1YnxsYWIpYCkKCmBgYHtyIGtleXdvcmRzX3Blcl95ZWFyLCB3YXJuaW5nPUZBTFNFfQp0aWR5X3RleHRzX2xvd2VyIDwtIHN0cmluZ3I6OnN0cl90b19sb3dlcih0aWR5X3RleHRzJHRleHQpCndvcmRfY291bnRzIDwtIHRpYmJsZTo6dGliYmxlKAogIHllYXIgPSB0aWR5X3RleHRzJHllYXIsCiAgYHdvcmRzYCA9IHN0cl9jb3VudCh0aWR5X3RleHRzX2xvd2VyLCAiXFxiLipcXGIiKSwKICBgcmVwcm9kdWMuLmAgPSBzdHJfY291bnQodGlkeV90ZXh0c19sb3dlciwgIlxcYnJlcHJvZHVjLipcXGIiKSwKICBgcmVwbGljLi5gID0gc3RyX2NvdW50KHRpZHlfdGV4dHNfbG93ZXIsICJcXGJyZXBsaWNhdC4qXFxiIiksCiAgYHJlcGVhdGFiLi5gID0gc3RyX2NvdW50KHRpZHlfdGV4dHNfbG93ZXIsICJcXGJyZXBlYXRhYi4qXFxiIiksCiAgYGNvZGVgID0gc3RyX2NvdW50KHRpZHlfdGV4dHNfbG93ZXIsCiAgICAiKFxcYmNvZGVcXGJ8XFxic2NyaXB0LipcXGJ8XFxicHNldWRvXCBjb2RlXFxiKSIpLAogIHNvZnR3YXJlID0gc3RyX2NvdW50KHRpZHlfdGV4dHNfbG93ZXIsICJcXGJzb2Z0d2FyZVxcYiIpLAogIGBhbGdvcml0aG0ocylgID0gc3RyX2NvdW50KHRpZHlfdGV4dHNfbG93ZXIsICJcXGJhbGdvcml0aG0uKlxcYiIpLAogIGAocHJlKXByb2Nlc3MuLmAgPSBzdHJfY291bnQodGlkeV90ZXh0c19sb3dlciwgCiAgICAgICAgICAgICAgICAiKFxcYnByb2Nlc3MuKlxcYnxcXGJwcmVwcm9jZXNzLipcXGJ8XFxicHJlLXByb2Nlc3MuKlxcYikiKSwKICBgZGF0YS4qYCA9IHN0cl9jb3VudCh0aWR5X3RleHRzX2xvd2VyLCAiXFxiZGF0YS4qXFxiIiksCiAgYHJlc3VsdChzKWAgPSBzdHJfY291bnQodGlkeV90ZXh0c19sb3dlciwgIlxcYnJlc3VsdHM/XFxiIiksCiAgYHJlcG9zaXRvcnkvaWVzYCA9IHN0cl9jb3VudCh0aWR5X3RleHRzX2xvd2VyLCAiXFxicmVwb3NpdG9yKHl8aWVzKVxcYiIpLAogICNgcmVwb3NgID0gc3RyX2NvdW50KHRpZHlfdGV4dHNfbG93ZXIsICJcXGJ6ZW5vZG98Zmlnc2hhcmV8b3NmfGRyeWFkXFxiIiksCiAgYGdpdGh1Yi9sYWJgID0gc3RyX2NvdW50KHRpZHlfdGV4dHNfbG93ZXIsICJcXGJnaXQoaHVifGxhYilcXGIiKQopCgp3b3JkX2NvdW50c19zdW1zIDwtIHJiaW5kKHdvcmRfY291bnRzLAogICAgICAgICAgICAgICAgICAgICAgICAgIHdvcmRfY291bnRzICU+JSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRwbHlyOjpzdW1tYXJpc2VfaWYoaXMubnVtZXJpYywgZnVucyhzdW0pKSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRpYmJsZTo6YWRkX2NvbHVtbih5ZWFyID0gIlRvdGFsIiwgLmJlZm9yZSA9IDApKQoKd3JpdGUuY3N2KHdvcmRfY291bnRzX3N1bXMsIGhlcmU6OmhlcmUoInJlc3VsdHMvdGV4dF9hbmFseXNpc19rZXl3b3Jkc3RlbXMuY3N2IiksIHJvdy5uYW1lcyA9IEZBTFNFKQoKd29yZF9jb3VudHNfc3VtcyAlPiUKICBrbml0cjo6a2FibGUoKSAlPiUKICBrYWJsZUV4dHJhOjprYWJsZV9zdHlsaW5nKCJzdHJpcGVkIiwgZm9udF9zaXplID0gMTAsIGJvb3RzdHJhcF9vcHRpb25zID0gImNvbmRlbnNlZCIpICAlPiUKICBrYWJsZUV4dHJhOjpyb3dfc3BlYygwLCBmb250X3NpemUgPSAieC1zbWFsbCIsIGJvbGQgPSBUKSAgJT4lCiAga2FibGVFeHRyYTo6cm93X3NwZWMobnJvdyh3b3JkX2NvdW50c19zdW1zKSwgYm9sZCA9IFQpCmBgYAoKKipOb3RlKio6IFRoZSBoaWdoIG51bWJlciBmb3IgImNvZGUiIGluIDIwMTIgaXMgbGFyZ2VseSBkdWUgdG8gYSBzaW5nbGUgcGFwZXIgYWJvdXQgImxhbmQgdXNlIGNvZGVzIi4KCiMjIENvbG9waG9uCgpUaGlzIGRvY3VtZW50IGlzIGxpY2Vuc2VkIHVuZGVyIGEgW0NyZWF0aXZlIENvbW1vbnMgQXR0cmlidXRpb24gNC4wIEludGVybmF0aW9uYWwgTGljZW5zZV0oaHR0cHM6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL2xpY2Vuc2VzL2J5LzQuMC8pLgpBbGwgY29udGFpbmVkIGNvZGUgaXMgbGljZW5zZWQgdW5kZXIgdGhlIFtBcGFjaGUgTGljZW5zZSAyLjBdKGh0dHBzOi8vY2hvb3NlYWxpY2Vuc2UuY29tL2xpY2Vuc2VzL2FwYWNoZS0yLjAvKS4KVGhpcyBkb2N1bWVudCBpcyB2ZXJzaW9uZWQgaW4gYSBwdWJsaWMgW2dpdF0oaHR0cHM6Ly9naXQtc2NtLmNvbS8pIHJlcG9zaXRvcnksIFtodHRwczovL2dpdGh1Yi5jb20vbnVlc3QvcmVwcm9kdWNpYmxlLXJlc2VhcmNoLWF0LWdpc2NpZW5jZV0oaHR0cHM6Ly9naXRodWIuY29tL251ZXN0L3JlcHJvZHVjaWJsZS1yZXNlYXJjaC1hdC1naXNjaWVuY2UpLCBhbmQgYXJjaGl2ZWQgb24gWmVub2RvIGF0IFtodHRwczovL2RvaS5vcmcvMTAuNTI4MS96ZW5vZG8uNDAzMjg3NV0oaHR0cHM6Ly9kb2kub3JnLzEwLjUyODEvemVub2RvLjQwMzI4NzUpLgoKKipSdW50aW1lIGVudmlyb25tZW50IGRlc2NyaXB0aW9uOioqCgpgYGB7ciBzZXNzaW9uX2luZm8sIGVjaG89RkFMU0V9CmRldnRvb2xzOjpzZXNzaW9uX2luZm8oaW5jbHVkZV9iYXNlID0gVFJVRSkKYGBgCgpgYGB7ciB1cGxvYWRfdG9fZHJpdmUsIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CiMgdXBsb2FkIHRoZSBIVE1MIGFuZCBSbWQgZmlsZSB0byB0aGUgYXV0aG9yaW5nIHRlYW0ncyBzaGFyZWQgZm9sZGVyCmxpYnJhcnkoImdvb2dsZWRyaXZlIikKZ29vZ2xlZHJpdmU6OmRyaXZlX2F1dGgodXNlX29vYiA9IFRSVUUpCmdvb2dsZWRyaXZlOjpkcml2ZV9wdXQoImdpc2NpZW5jZS1oaXN0b3JpYy10ZXh0LWFuYWx5c2lzLlJtZCIsIHBhdGggPSAiaHR0cHM6Ly9kcml2ZS5nb29nbGUuY29tL2RyaXZlL2ZvbGRlcnMvMTdFVXRNX3pDeDFnUU1lYTFNSE5fNVhTVnJzc3h2OUdBLyIpCmdvb2dsZWRyaXZlOjpkcml2ZV9wdXQoImdpc2NpZW5jZS1oaXN0b3JpYy10ZXh0LWFuYWx5c2lzLmh0bWwiLCBwYXRoID0gImh0dHBzOi8vZHJpdmUuZ29vZ2xlLmNvbS9kcml2ZS9mb2xkZXJzLzE3RVV0TV96Q3gxZ1FNZWExTUhOXzVYU1Zyc3N4djlHQS8iKQpgYGAK