General data read-in

load('Rdata/raws.Rdata')

Honorees

  • Number of honorees up until 2019: 412.

  • Three types of honorees:

keynotes %>% 
  pull(conference) %>% 
  table()
## .
## ISCB Fellow        ISMB      RECOMB 
##          75         167         170

There are 412 entries and 290 unique names in the ISCB cohort.

Names sorted by the number of honors:

keynotes %>% 
  count(fore_name, last_name) %>% 
  arrange(desc(n)) %>% 
  DT::datatable()

Number of keynote speakers/fellows across years:

keynotes %>%
  select(year, conference) %>% 
  count(year, conference) %>% 
  ggplot(aes(x = year(year), y = n, color = conference)) +
  geom_point() +
  geom_line(alpha = 0.5) +
  coord_cartesian(ylim = c(0, 13)) +
  scale_x_continuous(breaks = seq(1995, 2019, 5)) +
  scale_y_continuous(breaks = seq(0, 14, 2)) +
  scale_color_viridis_d() +
  labs(x = 'Year', y = 'Number of keynote speakers/fellows')

Authors

Total number of last authors: 176110.

10 journals with the most computational biology articles:

corr_authors %>% 
  mutate(publication_date = ymd(publication_date, truncated = 2)) %>% 
  select(publication_date, journal) %>% 
  filter(journal %in% large_jours$journal) %>% 
  ggplot(aes(x = publication_date, fill = forcats::fct_infreq(journal))) +
  geom_histogram(size = 0, bins = 27) +
  scale_fill_brewer(palette = 'Set3') +
  theme(legend.position = c(0.2, 0.7)) +
  labs(x = NULL, y = NULL) +
scale_x_date(
    labels = scales::date_format("%Y"),
    breaks = as.Date(c('2000-01-01', '2010-01-01', '2019-01-01')),
    limits = c(as.Date('1993-01-01'), as.Date('2019-12-31'))) + 
  # geom_text(data = jours, aes(x = x, y = y, label = label), color = 'grey10') +
  NULL
## Warning: Removed 20 rows containing missing values (geom_bar).

nucleic_acids <- corr_authors %>% filter(grepl('Nucleic Acids Res', journal))
table(nucleic_acids$journal)
## 
##       Nucleic Acids Res Nucleic Acids Res Suppl 
##                    3419                       5

Gender analysis

gender_df <- read_tsv('data/gender/genderize.tsv')
## 
## ── Column specification ──────────────────────────────────────────
## cols(
##   fore_name_simple = col_character(),
##   n_authors = col_double(),
##   genderize_sample_size = col_double(),
##   query_date = col_date(format = ""),
##   probability_male = col_double()
## )
pubmed_gender_pmids <- corr_authors %>%
  left_join(gender_df, by = 'fore_name_simple')

iscb_gender_df <- keynotes %>% 
  left_join(gender_df, by = 'fore_name_simple')
pubmed_gender_pmids %>% 
  mutate(genderized = ifelse(is.na(probability_male), 'NOT genderized', 'Genderized')) %>% 
  ggplot(aes(year(year), fill = genderized)) +
  geom_bar() +
  scale_fill_viridis_d() +
  theme(legend.position = c(0.2, 0.8)) +
  ylab('Number of full names')

gender_check <- pubmed_gender_pmids %>%
  mutate(got_gender = case_when(
    is.na(probability_male) ~ 'Gender not predicted',
    TRUE ~ 'Gender predicted'))

pred_all <- pubmed_gender_pmids %>% 
  count(is.na(probability_male))

dash_df <- pubmed_gender_pmids %>% 
  filter(is.na(probability_male) & !is.na(fore_name_simple)) %>% 
  mutate(dashed_name = grepl('-', fore_name_simple)) %>% 
  count(dashed_name)

pred_before_2002 <- gender_check %>% 
  filter(year(year) < 2002) %>%
  count(is.na(probability_male), is.na(fore_name_simple))

1014 last authors with empty fore name field (i.e., missing metadata). 12512 authors with no fore_name_simple.

In total, 153655 authors had gender prediction and 22515 didn’t. 11498 authors with fore name that is NA once simplified (i.e., initials only). Among 10003 authors with fore name but no predictions, ~ 42% has a dash.

Before 2002, 35 authors had gender predictions, 2566 didn’t have gender predictions because of these authors only have initials for fore names.

Mean probability of selecting Asian among these names:

pubmed_gender_pmids %>% 
  filter(is.na(probability_male) & !is.na(fore_name_simple)) %>% 
  rename('surname' = last_name_simple) %>% 
  predict_race(surname.only = T, impute.missing = F) %>% 
  pull(pred.asi) %>% 
  mean(na.rm = T)
## [1] "Proceeding with surname-only predictions..."
## Warning in merge_surnames(voter.file, impute.missing =
## impute.missing): 1305 surnames were not matched.
## [1] 0.8174599

Honorees that didn’t receive a gender prediction: Chung-I Wu.

In summary, the NA predictions mostly include initials only, hyphenated names and perhaps names with accent marks.

US race analysis

pubmed_aff_pmids <- corr_authors %>% 
  tidyr::separate_rows(countries, sep = ',') %>% 
  filter(countries == 'US') # looking at only US affiliation

keynotes_us <- keynotes %>% 
  tidyr::separate_rows(afflcountries, sep = '\\|') %>% 
  filter(afflcountries == 'United States') 

pubmed_race_pmids <- pubmed_aff_pmids %>%
  rename('surname' = last_name_simple) %>% 
  predict_race(surname.only = T, impute.missing = F)
## [1] "Proceeding with surname-only predictions..."
iscb_us_race <- keynotes_us %>% 
  rename('surname' = last_name_simple) %>%
  predict_race(surname.only = T, impute.missing = F)
## [1] "Proceeding with surname-only predictions..."
  • Number of honorees affiliated with the US: 239.

  • Number of last authors affiliated with the US: 27410.

  • Number of authors with no race prediction: 5166 (of which 6 did not have a surname).

  • Number of honorees with no race prediction: 45 (of which 0 did not have a surname).

pubmed_race_pmids %>% 
  mutate(race_pred = ifelse(is.na(pred.whi), 'Race NOT predicted', 'Race predicted')) %>% 
  ggplot(aes(year(year), fill = fct_rev(race_pred))) +
  geom_bar() +
  scale_fill_viridis_d() +
  theme(legend.position = c(0.2, 0.8)) +
  ylab('Number of full names')

Name origin analysis

region_levels <- paste(c('Celtic/English', 'European', 'East Asian', 'Hispanic', 'South Asian', 'Arabic', 'Hebrew', 'African', 'Nordic', 'Greek'), 'names')

pubmed_nat_pmids <- corr_authors %>%
  left_join(nationalize_df, by = c('fore_name', 'last_name'))

pubmed_nat_df <- pubmed_nat_pmids %>% 
  group_by(pmid, journal, publication_date, year) %>%
  summarise_at(vars(African:SouthAsian), mean, na.rm = T) %>%
  ungroup()

iscb_nat_df <- keynotes %>%
  left_join(nationalize_df, by = c('fore_name', 'last_name'))
set.seed(0)

top_names <- map_dfc(
  nationalize_df %>% 
    select(African:SouthAsian) %>% 
    colnames(), 
  function(x) {
    nationalize_df %>%
      filter((!!sym(x)) > 0.9) %>%
      sample_n(6) %>%
      select(full_name) %>%
      rename(!!x := full_name)
  })

top_names %>% 
  pivot_longer(everything(), names_to = 'region', values_to = 'names') %>% 
  recode_region() %>% 
  group_by(region) %>% 
  summarise(names = paste(names, collapse = ', '), .groups = 'drop') %>% 
  arrange(factor(region, levels = region_levels)) %>%
  DT::datatable() %>% 
  # write_tsv('data/names/example_name_origin.tsv') %>% 
  {.}
## Warning: Problem with `mutate()` input `region`.
## ℹ Unknown levels in `f`: OtherCategories
## ℹ Input `region` is `fct_recode(...)`.
## Warning: Unknown levels in `f`: OtherCategories
 pubmed_nat_pmids %>% count(!is.na(African), is.na(fore_name_simple.x))
## # A tibble: 2 x 3
##   `!is.na(African)` `is.na(fore_name_simple.x)`      n
##   <lgl>             <lgl>                        <int>
## 1 FALSE             TRUE                         12512
## 2 TRUE              FALSE                       163598

0 ISCB speakers did not have nationality predictions. 12512 pubmed full names not nationalized. 12512 of these don’t have fore_name_simple. (See earlier conclusion in Gender analysis.)

pubmed_nat_df %>% 
  mutate(nationalized = ifelse(is.na(African), 'NOT nationalized', 'Nationalized')) %>% 
  ggplot(aes(year(year), fill = nationalized)) +
  geom_bar() +
  scale_fill_viridis_d() +
  theme(legend.position = c(0.2, 0.8)) +
  ylab('Number of full names')

Affiliation analysis

corr_authors %>% 
  mutate(affi_country_found = ifelse(is.na(countries), 'Country NOT found', 'Country found')) %>% 
  ggplot(aes(year(year), fill = affi_country_found)) +
  geom_bar(position = 'stack') +
  scale_fill_viridis_d() +
  theme(legend.position = c(0.2, 0.8)) +
  ylab('Number of full names')

corr_authors %>% 
  ggplot(aes(x = adjusted_citations)) +
  geom_density()

corr_authors %>% 
  mutate(affi_country_found = ifelse(
    is.na(countries), 'Country NOT found', 'Country found'),
         has_pmcid = ifelse(is.na(pmcid), 'No PMCID', 'Has PMCID')) %>% 
  count(affi_country_found, has_pmcid)
## # A tibble: 4 x 3
##   affi_country_found has_pmcid     n
##   <chr>              <chr>     <int>
## 1 Country found      Has PMCID 44165
## 2 Country found      No PMCID  39972
## 3 Country NOT found  Has PMCID 37424
## 4 Country NOT found  No PMCID  54549

Due to the lack of metadata, our method couldn’t extract countries for most articles before 2014. Even though many of these articles have PMCIDs, the affiliations were not mapped to authors in PMC metadata and thus couldn’t be parsed. However, unlike the other analyses where we considered “year” as a covariate, we calculated the enrichment by gathering data from all the years together. Therefore, we leave all the data in as is.

US name origin analysis

pubmed_nat_pmids <- corr_authors %>%
  separate_rows(countries, sep = ',') %>% 
  filter(countries == 'US') %>% 
  left_join(nationalize_df, by = c('fore_name', 'last_name'))

pubmed_nat_df <- pubmed_nat_pmids %>% 
  group_by(pmid, journal, publication_date, year) %>%
  summarise_at(vars(African:SouthAsian), mean, na.rm = T) %>%
  ungroup()

iscb_nat_df <- keynotes %>%
  separate_rows(afflcountries, sep = '\\|') %>% 
  filter(afflcountries == 'United States') %>% 
  left_join(nationalize_df, by = c('fore_name', 'last_name'))

0 US-affiliated ISCB speakers did not have nationality predictions. pubmed_nat_pmids %>% filter(is.na(African)) %>% nrow() US-affiliated authors did not have nationality predictions. 1205 of these don’t have fore_name_simple. (See earlier conclusion in Gender analysis.)

pubmed_nat_df %>% 
  mutate(nationalized = ifelse(is.na(African), 'NOT nationalized', 'Nationalized')) %>% 
  ggplot(aes(year(year), fill = nationalized)) +
  geom_bar() +
  scale_fill_viridis_d() +
  theme(legend.position = c(0.2, 0.8)) +
  ylab('Number of US-affiliated full names')

LS0tCnRpdGxlOiAiU3VtbWFyeSBzdGF0aXN0aWNzIGFuZCBtaXNzaW5nIGRhdGEgYW5hbHlzaXMiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdGhlbWU6IGZsYXRseQogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgIGhpZ2hsaWdodDogdGFuZ28Ka25pdDogKGZ1bmN0aW9uKGlucHV0RmlsZSwgZW5jb2RpbmcpIHsKICBybWFya2Rvd246OnJlbmRlcihpbnB1dEZpbGUsIGVuY29kaW5nID0gZW5jb2RpbmcsIG91dHB1dF9kaXIgPSAiZG9jcyIpIH0pCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGx1YnJpZGF0ZSkKbGlicmFyeShybmF0dXJhbGVhcnRoKQpsaWJyYXJ5KHdydSkKc291cmNlKCd1dGlscy9yLXV0aWxzLlInKQp0aGVtZV9zZXQodGhlbWVfYncoKSArIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkpCmBgYAoKIyMgR2VuZXJhbCBkYXRhIHJlYWQtaW4KCmBgYHtyfQpsb2FkKCdSZGF0YS9yYXdzLlJkYXRhJykKYGBgCiMjIEhvbm9yZWVzCgotIE51bWJlciBvZiBob25vcmVlcyB1cCB1bnRpbCAyMDE5OiBgciBucm93KGtleW5vdGVzKWAuCgotIFRocmVlIHR5cGVzIG9mIGhvbm9yZWVzOgpgYGB7cn0Ka2V5bm90ZXMgJT4lIAogIHB1bGwoY29uZmVyZW5jZSkgJT4lIAogIHRhYmxlKCkKYGBgCgpUaGVyZSBhcmUgYHIgbnJvdyhrZXlub3RlcylgIGVudHJpZXMgYW5kIGByIGtleW5vdGVzICU+JSBkaXN0aW5jdChmb3JlX25hbWUsIGxhc3RfbmFtZSkgJT4lIG5yb3coKWAgdW5pcXVlIG5hbWVzIGluIHRoZSBJU0NCIGNvaG9ydC4KCk5hbWVzIHNvcnRlZCBieSB0aGUgbnVtYmVyIG9mIGhvbm9yczoKYGBge3J9CmtleW5vdGVzICU+JSAKICBjb3VudChmb3JlX25hbWUsIGxhc3RfbmFtZSkgJT4lIAogIGFycmFuZ2UoZGVzYyhuKSkgJT4lIAogIERUOjpkYXRhdGFibGUoKQpgYGAKCk51bWJlciBvZiBrZXlub3RlIHNwZWFrZXJzL2ZlbGxvd3MgYWNyb3NzIHllYXJzOgoKYGBge3J9CmtleW5vdGVzICU+JQogIHNlbGVjdCh5ZWFyLCBjb25mZXJlbmNlKSAlPiUgCiAgY291bnQoeWVhciwgY29uZmVyZW5jZSkgJT4lIAogIGdncGxvdChhZXMoeCA9IHllYXIoeWVhciksIHkgPSBuLCBjb2xvciA9IGNvbmZlcmVuY2UpKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX2xpbmUoYWxwaGEgPSAwLjUpICsKICBjb29yZF9jYXJ0ZXNpYW4oeWxpbSA9IGMoMCwgMTMpKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgxOTk1LCAyMDE5LCA1KSkgKwogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgMTQsIDIpKSArCiAgc2NhbGVfY29sb3JfdmlyaWRpc19kKCkgKwogIGxhYnMoeCA9ICdZZWFyJywgeSA9ICdOdW1iZXIgb2Yga2V5bm90ZSBzcGVha2Vycy9mZWxsb3dzJykKYGBgCgoKIyMgQXV0aG9ycwpUb3RhbCBudW1iZXIgb2YgbGFzdCBhdXRob3JzOiBgciBucm93KGNvcnJfYXV0aG9ycylgLgoKMTAgam91cm5hbHMgd2l0aCB0aGUgbW9zdCBjb21wdXRhdGlvbmFsIGJpb2xvZ3kgYXJ0aWNsZXM6CgpgYGB7cn0KY29ycl9hdXRob3JzICU+JSAKICBtdXRhdGUocHVibGljYXRpb25fZGF0ZSA9IHltZChwdWJsaWNhdGlvbl9kYXRlLCB0cnVuY2F0ZWQgPSAyKSkgJT4lIAogIHNlbGVjdChwdWJsaWNhdGlvbl9kYXRlLCBqb3VybmFsKSAlPiUgCiAgZmlsdGVyKGpvdXJuYWwgJWluJSBsYXJnZV9qb3VycyRqb3VybmFsKSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gcHVibGljYXRpb25fZGF0ZSwgZmlsbCA9IGZvcmNhdHM6OmZjdF9pbmZyZXEoam91cm5hbCkpKSArCiAgZ2VvbV9oaXN0b2dyYW0oc2l6ZSA9IDAsIGJpbnMgPSAyNykgKwogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAnU2V0MycpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSBjKDAuMiwgMC43KSkgKwogIGxhYnMoeCA9IE5VTEwsIHkgPSBOVUxMKSArCnNjYWxlX3hfZGF0ZSgKICAgIGxhYmVscyA9IHNjYWxlczo6ZGF0ZV9mb3JtYXQoIiVZIiksCiAgICBicmVha3MgPSBhcy5EYXRlKGMoJzIwMDAtMDEtMDEnLCAnMjAxMC0wMS0wMScsICcyMDE5LTAxLTAxJykpLAogICAgbGltaXRzID0gYyhhcy5EYXRlKCcxOTkzLTAxLTAxJyksIGFzLkRhdGUoJzIwMTktMTItMzEnKSkpICsgCiAgIyBnZW9tX3RleHQoZGF0YSA9IGpvdXJzLCBhZXMoeCA9IHgsIHkgPSB5LCBsYWJlbCA9IGxhYmVsKSwgY29sb3IgPSAnZ3JleTEwJykgKwogIE5VTEwKYGBgCgpgYGB7cn0KbnVjbGVpY19hY2lkcyA8LSBjb3JyX2F1dGhvcnMgJT4lIGZpbHRlcihncmVwbCgnTnVjbGVpYyBBY2lkcyBSZXMnLCBqb3VybmFsKSkKdGFibGUobnVjbGVpY19hY2lkcyRqb3VybmFsKQpgYGAKCmBgYHtyIGluY2x1ZGUgPSBGLCBldmFsID0gRn0KIyBIb3cgbXVjaCBkaWQgSVNDQiBob25vcmVlcyBwdWJsaXNoIGluIHRoZXNlIGpvdXJuYWxzIGFzIGxhc3QgYXV0aG9ycz8KCm5fcHVicyA8LSBrZXlub3RlcyAlPiUgCiAgZGlzdGluY3QoZm9yZV9uYW1lX3NpbXBsZSwgbGFzdF9uYW1lX3NpbXBsZSkgJT4lIAogICMgbGVmdF9qb2luKG5hdF90b19yZWcsIGJ5ID0gYygnYWZmbGNvdW50cmllcycgPSAnY291bnRyeV9uYW1lJykpICU+JSAKICBsZWZ0X2pvaW4oY29ycl9hdXRob3JzLCBieSA9IGMoJ2ZvcmVfbmFtZV9zaW1wbGUnLCAnbGFzdF9uYW1lX3NpbXBsZScpKSAlPiUgCiAgZ3JvdXBfYnkoZm9yZV9uYW1lX3NpbXBsZSwgbGFzdF9uYW1lX3NpbXBsZSkgJT4lIAogIHN1bW1hcmlzZShudW1fcHVicyA9IHN1bSghaXMubmEocG1pZCkpLCAuZ3JvdXBzID0gJ2Ryb3AnKQpuX3B1YnMkbnVtX3B1YnMgJT4lIHN1bSgpCiMgbl9wdWJzICU+JSAKIyAgIGdncGxvdCgpICsKIyAgIGdlb21fYmFyKGFlcyh4ID0gbnVtX3B1YnMpKQpgYGAKCiMjIEdlbmRlciBhbmFseXNpcwoKYGBge3J9CmdlbmRlcl9kZiA8LSByZWFkX3RzdignZGF0YS9nZW5kZXIvZ2VuZGVyaXplLnRzdicpCgpwdWJtZWRfZ2VuZGVyX3BtaWRzIDwtIGNvcnJfYXV0aG9ycyAlPiUKICBsZWZ0X2pvaW4oZ2VuZGVyX2RmLCBieSA9ICdmb3JlX25hbWVfc2ltcGxlJykKCmlzY2JfZ2VuZGVyX2RmIDwtIGtleW5vdGVzICU+JSAKICBsZWZ0X2pvaW4oZ2VuZGVyX2RmLCBieSA9ICdmb3JlX25hbWVfc2ltcGxlJykKYGBgCgpgYGB7cn0KcHVibWVkX2dlbmRlcl9wbWlkcyAlPiUgCiAgbXV0YXRlKGdlbmRlcml6ZWQgPSBpZmVsc2UoaXMubmEocHJvYmFiaWxpdHlfbWFsZSksICdOT1QgZ2VuZGVyaXplZCcsICdHZW5kZXJpemVkJykpICU+JSAKICBnZ3Bsb3QoYWVzKHllYXIoeWVhciksIGZpbGwgPSBnZW5kZXJpemVkKSkgKwogIGdlb21fYmFyKCkgKwogIHNjYWxlX2ZpbGxfdmlyaWRpc19kKCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC4yLCAwLjgpKSArCiAgeWxhYignTnVtYmVyIG9mIGZ1bGwgbmFtZXMnKQpgYGAKCiAgCmBgYHtyfQpnZW5kZXJfY2hlY2sgPC0gcHVibWVkX2dlbmRlcl9wbWlkcyAlPiUKICBtdXRhdGUoZ290X2dlbmRlciA9IGNhc2Vfd2hlbigKICAgIGlzLm5hKHByb2JhYmlsaXR5X21hbGUpIH4gJ0dlbmRlciBub3QgcHJlZGljdGVkJywKICAgIFRSVUUgfiAnR2VuZGVyIHByZWRpY3RlZCcpKQoKcHJlZF9hbGwgPC0gcHVibWVkX2dlbmRlcl9wbWlkcyAlPiUgCiAgY291bnQoaXMubmEocHJvYmFiaWxpdHlfbWFsZSkpCgpkYXNoX2RmIDwtIHB1Ym1lZF9nZW5kZXJfcG1pZHMgJT4lIAogIGZpbHRlcihpcy5uYShwcm9iYWJpbGl0eV9tYWxlKSAmICFpcy5uYShmb3JlX25hbWVfc2ltcGxlKSkgJT4lIAogIG11dGF0ZShkYXNoZWRfbmFtZSA9IGdyZXBsKCctJywgZm9yZV9uYW1lX3NpbXBsZSkpICU+JSAKICBjb3VudChkYXNoZWRfbmFtZSkKCnByZWRfYmVmb3JlXzIwMDIgPC0gZ2VuZGVyX2NoZWNrICU+JSAKICBmaWx0ZXIoeWVhcih5ZWFyKSA8IDIwMDIpICU+JQogIGNvdW50KGlzLm5hKHByb2JhYmlsaXR5X21hbGUpLCBpcy5uYShmb3JlX25hbWVfc2ltcGxlKSkKCmBgYAoKYHIgcHVibWVkX2dlbmRlcl9wbWlkcyAlPiUgZmlsdGVyKGlzLm5hKGZvcmVfbmFtZSkpICU+JSBucm93KClgIGxhc3QgYXV0aG9ycyB3aXRoIGVtcHR5IGZvcmUgbmFtZSBmaWVsZCAoaS5lLiwgbWlzc2luZyBtZXRhZGF0YSkuCmByIHB1Ym1lZF9nZW5kZXJfcG1pZHMgJT4lIGZpbHRlcihpcy5uYShmb3JlX25hbWVfc2ltcGxlKSkgJT4lIG5yb3coKWAgYXV0aG9ycyB3aXRoIG5vIGBmb3JlX25hbWVfc2ltcGxlYC4KCkluIHRvdGFsLCBgciBwcmVkX2FsbFsxLCAnbiddYCBhdXRob3JzIGhhZCBnZW5kZXIgcHJlZGljdGlvbiBhbmQgYHIgcHJlZF9hbGxbMiwgJ24nXWAgZGlkbid0LgpgciBwdWJtZWRfZ2VuZGVyX3BtaWRzICU+JSBmaWx0ZXIoIWlzLm5hKGZvcmVfbmFtZSkmaXMubmEoZm9yZV9uYW1lX3NpbXBsZSkpICU+JSBucm93KClgIGF1dGhvcnMgd2l0aCBmb3JlIG5hbWUgdGhhdCBpcyBOQSBvbmNlIHNpbXBsaWZpZWQgKGkuZS4sIGluaXRpYWxzIG9ubHkpLgpBbW9uZyBgciBzdW0oZGFzaF9kZiRuKWAgYXV0aG9ycyB3aXRoIGZvcmUgbmFtZSBidXQgbm8gcHJlZGljdGlvbnMsIH4gYHIgcm91bmQoZGFzaF9kZiRuWzFdL3N1bShkYXNoX2RmJG4pKjEwMClgJSBoYXMgYSBkYXNoLgoKQmVmb3JlIDIwMDIsIGByIHByZWRfYmVmb3JlXzIwMDJbMSwgJ24nXWAgYXV0aG9ycyBoYWQgZ2VuZGVyIHByZWRpY3Rpb25zLCBgciBwcmVkX2JlZm9yZV8yMDAyWzIsICduJ11gIGRpZG4ndCBoYXZlIGdlbmRlciBwcmVkaWN0aW9ucyBiZWNhdXNlIG9mIHRoZXNlIGF1dGhvcnMgb25seSBoYXZlIGluaXRpYWxzIGZvciBmb3JlIG5hbWVzLgoKCk1lYW4gcHJvYmFiaWxpdHkgb2Ygc2VsZWN0aW5nIEFzaWFuIGFtb25nIHRoZXNlIG5hbWVzOgpgYGB7cn0KcHVibWVkX2dlbmRlcl9wbWlkcyAlPiUgCiAgZmlsdGVyKGlzLm5hKHByb2JhYmlsaXR5X21hbGUpICYgIWlzLm5hKGZvcmVfbmFtZV9zaW1wbGUpKSAlPiUgCiAgcmVuYW1lKCdzdXJuYW1lJyA9IGxhc3RfbmFtZV9zaW1wbGUpICU+JSAKICBwcmVkaWN0X3JhY2Uoc3VybmFtZS5vbmx5ID0gVCwgaW1wdXRlLm1pc3NpbmcgPSBGKSAlPiUgCiAgcHVsbChwcmVkLmFzaSkgJT4lIAogIG1lYW4obmEucm0gPSBUKQpgYGAKCkhvbm9yZWVzIHRoYXQgZGlkbid0IHJlY2VpdmUgYSBnZW5kZXIgcHJlZGljdGlvbjogYHIgaXNjYl9nZW5kZXJfZGYgJT4lIGZpbHRlcihpcy5uYShwcm9iYWJpbGl0eV9tYWxlKSkgJT4lIHB1bGwoZnVsbF9uYW1lKWAuCgoqKkluIHN1bW1hcnksIHRoZSBOQSBwcmVkaWN0aW9ucyBtb3N0bHkgaW5jbHVkZSBpbml0aWFscyBvbmx5LCBoeXBoZW5hdGVkIG5hbWVzIGFuZCBwZXJoYXBzIG5hbWVzIHdpdGggYWNjZW50IG1hcmtzLioqCgoKIyMgVVMgcmFjZSBhbmFseXNpcwoKYGBge3Igd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KcHVibWVkX2FmZl9wbWlkcyA8LSBjb3JyX2F1dGhvcnMgJT4lIAogIHRpZHlyOjpzZXBhcmF0ZV9yb3dzKGNvdW50cmllcywgc2VwID0gJywnKSAlPiUgCiAgZmlsdGVyKGNvdW50cmllcyA9PSAnVVMnKSAjIGxvb2tpbmcgYXQgb25seSBVUyBhZmZpbGlhdGlvbgoKa2V5bm90ZXNfdXMgPC0ga2V5bm90ZXMgJT4lIAogIHRpZHlyOjpzZXBhcmF0ZV9yb3dzKGFmZmxjb3VudHJpZXMsIHNlcCA9ICdcXHwnKSAlPiUgCiAgZmlsdGVyKGFmZmxjb3VudHJpZXMgPT0gJ1VuaXRlZCBTdGF0ZXMnKSAKCnB1Ym1lZF9yYWNlX3BtaWRzIDwtIHB1Ym1lZF9hZmZfcG1pZHMgJT4lCiAgcmVuYW1lKCdzdXJuYW1lJyA9IGxhc3RfbmFtZV9zaW1wbGUpICU+JSAKICBwcmVkaWN0X3JhY2Uoc3VybmFtZS5vbmx5ID0gVCwgaW1wdXRlLm1pc3NpbmcgPSBGKQoKaXNjYl91c19yYWNlIDwtIGtleW5vdGVzX3VzICU+JSAKICByZW5hbWUoJ3N1cm5hbWUnID0gbGFzdF9uYW1lX3NpbXBsZSkgJT4lCiAgcHJlZGljdF9yYWNlKHN1cm5hbWUub25seSA9IFQsIGltcHV0ZS5taXNzaW5nID0gRikKICAKYGBgCgotIE51bWJlciBvZiBob25vcmVlcyBhZmZpbGlhdGVkIHdpdGggdGhlIFVTOiBgciBucm93KGtleW5vdGVzX3VzKWAuCgotIE51bWJlciBvZiBsYXN0IGF1dGhvcnMgYWZmaWxpYXRlZCB3aXRoIHRoZSBVUzogYHIgbnJvdyhwdWJtZWRfYWZmX3BtaWRzKWAuCgotIE51bWJlciBvZiBhdXRob3JzIHdpdGggbm8gcmFjZSBwcmVkaWN0aW9uOiBgciBzdW0oaXMubmEocHVibWVkX3JhY2VfcG1pZHMkcHJlZC53aGkpKWAgKG9mIHdoaWNoIGByIHN1bShpcy5uYShwdWJtZWRfcmFjZV9wbWlkcyRzdXJuYW1lKSlgIGRpZCBub3QgaGF2ZSBhIHN1cm5hbWUpLgoKLSBOdW1iZXIgb2YgaG9ub3JlZXMgd2l0aCBubyByYWNlIHByZWRpY3Rpb246IGByIHN1bShpcy5uYShpc2NiX3VzX3JhY2UkcHJlZC53aGkpKWAgKG9mIHdoaWNoIGByIHN1bShpcy5uYShpc2NiX3VzX3JhY2Ukc3VybmFtZSkpYCBkaWQgbm90IGhhdmUgYSBzdXJuYW1lKS4KCmBgYHtyfQpwdWJtZWRfcmFjZV9wbWlkcyAlPiUgCiAgbXV0YXRlKHJhY2VfcHJlZCA9IGlmZWxzZShpcy5uYShwcmVkLndoaSksICdSYWNlIE5PVCBwcmVkaWN0ZWQnLCAnUmFjZSBwcmVkaWN0ZWQnKSkgJT4lIAogIGdncGxvdChhZXMoeWVhcih5ZWFyKSwgZmlsbCA9IGZjdF9yZXYocmFjZV9wcmVkKSkpICsKICBnZW9tX2JhcigpICsKICBzY2FsZV9maWxsX3ZpcmlkaXNfZCgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSBjKDAuMiwgMC44KSkgKwogIHlsYWIoJ051bWJlciBvZiBmdWxsIG5hbWVzJykKYGBgCgoKIyMgTmFtZSBvcmlnaW4gYW5hbHlzaXMKCmBgYHtyfQpyZWdpb25fbGV2ZWxzIDwtIHBhc3RlKGMoJ0NlbHRpYy9FbmdsaXNoJywgJ0V1cm9wZWFuJywgJ0Vhc3QgQXNpYW4nLCAnSGlzcGFuaWMnLCAnU291dGggQXNpYW4nLCAnQXJhYmljJywgJ0hlYnJldycsICdBZnJpY2FuJywgJ05vcmRpYycsICdHcmVlaycpLCAnbmFtZXMnKQoKcHVibWVkX25hdF9wbWlkcyA8LSBjb3JyX2F1dGhvcnMgJT4lCiAgbGVmdF9qb2luKG5hdGlvbmFsaXplX2RmLCBieSA9IGMoJ2ZvcmVfbmFtZScsICdsYXN0X25hbWUnKSkKCnB1Ym1lZF9uYXRfZGYgPC0gcHVibWVkX25hdF9wbWlkcyAlPiUgCiAgZ3JvdXBfYnkocG1pZCwgam91cm5hbCwgcHVibGljYXRpb25fZGF0ZSwgeWVhcikgJT4lCiAgc3VtbWFyaXNlX2F0KHZhcnMoQWZyaWNhbjpTb3V0aEFzaWFuKSwgbWVhbiwgbmEucm0gPSBUKSAlPiUKICB1bmdyb3VwKCkKCmlzY2JfbmF0X2RmIDwtIGtleW5vdGVzICU+JQogIGxlZnRfam9pbihuYXRpb25hbGl6ZV9kZiwgYnkgPSBjKCdmb3JlX25hbWUnLCAnbGFzdF9uYW1lJykpCgpgYGAKCmBgYHtyfQpzZXQuc2VlZCgwKQoKdG9wX25hbWVzIDwtIG1hcF9kZmMoCiAgbmF0aW9uYWxpemVfZGYgJT4lIAogICAgc2VsZWN0KEFmcmljYW46U291dGhBc2lhbikgJT4lIAogICAgY29sbmFtZXMoKSwgCiAgZnVuY3Rpb24oeCkgewogICAgbmF0aW9uYWxpemVfZGYgJT4lCiAgICAgIGZpbHRlcigoISFzeW0oeCkpID4gMC45KSAlPiUKICAgICAgc2FtcGxlX24oNikgJT4lCiAgICAgIHNlbGVjdChmdWxsX25hbWUpICU+JQogICAgICByZW5hbWUoISF4IDo9IGZ1bGxfbmFtZSkKICB9KQoKdG9wX25hbWVzICU+JSAKICBwaXZvdF9sb25nZXIoZXZlcnl0aGluZygpLCBuYW1lc190byA9ICdyZWdpb24nLCB2YWx1ZXNfdG8gPSAnbmFtZXMnKSAlPiUgCiAgcmVjb2RlX3JlZ2lvbigpICU+JSAKICBncm91cF9ieShyZWdpb24pICU+JSAKICBzdW1tYXJpc2UobmFtZXMgPSBwYXN0ZShuYW1lcywgY29sbGFwc2UgPSAnLCAnKSwgLmdyb3VwcyA9ICdkcm9wJykgJT4lIAogIGFycmFuZ2UoZmFjdG9yKHJlZ2lvbiwgbGV2ZWxzID0gcmVnaW9uX2xldmVscykpICU+JQogIERUOjpkYXRhdGFibGUoKSAlPiUgCiAgIyB3cml0ZV90c3YoJ2RhdGEvbmFtZXMvZXhhbXBsZV9uYW1lX29yaWdpbi50c3YnKSAlPiUgCiAgey59CgogcHVibWVkX25hdF9wbWlkcyAlPiUgY291bnQoIWlzLm5hKEFmcmljYW4pLCBpcy5uYShmb3JlX25hbWVfc2ltcGxlLngpKQpgYGAKCmByIGlzY2JfbmF0X2RmICU+JSBmaWx0ZXIoaXMubmEoQWZyaWNhbikpICU+JSBucm93KClgIElTQ0Igc3BlYWtlcnMgZGlkIG5vdCBoYXZlIG5hdGlvbmFsaXR5IHByZWRpY3Rpb25zLgpgciBwdWJtZWRfbmF0X3BtaWRzICU+JSBmaWx0ZXIoaXMubmEoQWZyaWNhbikpICU+JSBucm93KClgIHB1Ym1lZCBmdWxsIG5hbWVzIG5vdCBuYXRpb25hbGl6ZWQuCmByIHB1Ym1lZF9uYXRfcG1pZHMgJT4lIGZpbHRlcihpcy5uYShBZnJpY2FuKSZpcy5uYShmb3JlX25hbWVfc2ltcGxlLngpKSAlPiUgbnJvdygpYCBvZiB0aGVzZSBkb24ndCBoYXZlIGZvcmVfbmFtZV9zaW1wbGUuCihTZWUgZWFybGllciBjb25jbHVzaW9uIGluIEdlbmRlciBhbmFseXNpcy4pCgpgYGB7cn0KcHVibWVkX25hdF9kZiAlPiUgCiAgbXV0YXRlKG5hdGlvbmFsaXplZCA9IGlmZWxzZShpcy5uYShBZnJpY2FuKSwgJ05PVCBuYXRpb25hbGl6ZWQnLCAnTmF0aW9uYWxpemVkJykpICU+JSAKICBnZ3Bsb3QoYWVzKHllYXIoeWVhciksIGZpbGwgPSBuYXRpb25hbGl6ZWQpKSArCiAgZ2VvbV9iYXIoKSArCiAgc2NhbGVfZmlsbF92aXJpZGlzX2QoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gYygwLjIsIDAuOCkpICsKICB5bGFiKCdOdW1iZXIgb2YgZnVsbCBuYW1lcycpCmBgYAoKIyMgQWZmaWxpYXRpb24gYW5hbHlzaXMKCmBgYHtyfQpjb3JyX2F1dGhvcnMgJT4lIAogIG11dGF0ZShhZmZpX2NvdW50cnlfZm91bmQgPSBpZmVsc2UoaXMubmEoY291bnRyaWVzKSwgJ0NvdW50cnkgTk9UIGZvdW5kJywgJ0NvdW50cnkgZm91bmQnKSkgJT4lIAogIGdncGxvdChhZXMoeWVhcih5ZWFyKSwgZmlsbCA9IGFmZmlfY291bnRyeV9mb3VuZCkpICsKICBnZW9tX2Jhcihwb3NpdGlvbiA9ICdzdGFjaycpICsKICBzY2FsZV9maWxsX3ZpcmlkaXNfZCgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSBjKDAuMiwgMC44KSkgKwogIHlsYWIoJ051bWJlciBvZiBmdWxsIG5hbWVzJykKCmNvcnJfYXV0aG9ycyAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gYWRqdXN0ZWRfY2l0YXRpb25zKSkgKwogIGdlb21fZGVuc2l0eSgpCgpjb3JyX2F1dGhvcnMgJT4lIAogIG11dGF0ZShhZmZpX2NvdW50cnlfZm91bmQgPSBpZmVsc2UoCiAgICBpcy5uYShjb3VudHJpZXMpLCAnQ291bnRyeSBOT1QgZm91bmQnLCAnQ291bnRyeSBmb3VuZCcpLAogICAgICAgICBoYXNfcG1jaWQgPSBpZmVsc2UoaXMubmEocG1jaWQpLCAnTm8gUE1DSUQnLCAnSGFzIFBNQ0lEJykpICU+JSAKICBjb3VudChhZmZpX2NvdW50cnlfZm91bmQsIGhhc19wbWNpZCkKYGBgCgpEdWUgdG8gdGhlIGxhY2sgb2YgbWV0YWRhdGEsIG91ciBtZXRob2QgY291bGRuJ3QgZXh0cmFjdCBjb3VudHJpZXMgZm9yIG1vc3QgYXJ0aWNsZXMgYmVmb3JlIDIwMTQuCkV2ZW4gdGhvdWdoIG1hbnkgb2YgdGhlc2UgYXJ0aWNsZXMgaGF2ZSBQTUNJRHMsIHRoZSBhZmZpbGlhdGlvbnMgd2VyZSBub3QgbWFwcGVkIHRvIGF1dGhvcnMgaW4gUE1DIG1ldGFkYXRhIGFuZCB0aHVzIGNvdWxkbid0IGJlIHBhcnNlZC4KSG93ZXZlciwgdW5saWtlIHRoZSBvdGhlciBhbmFseXNlcyB3aGVyZSB3ZSBjb25zaWRlcmVkICJ5ZWFyIiBhcyBhIGNvdmFyaWF0ZSwgd2UgY2FsY3VsYXRlZCB0aGUgZW5yaWNobWVudCBieSBnYXRoZXJpbmcgZGF0YSBmcm9tIGFsbCB0aGUgeWVhcnMgdG9nZXRoZXIuClRoZXJlZm9yZSwgd2UgbGVhdmUgYWxsIHRoZSBkYXRhIGluIGFzIGlzLgoKIyMgVVMgbmFtZSBvcmlnaW4gYW5hbHlzaXMKCmBgYHtyfQpwdWJtZWRfbmF0X3BtaWRzIDwtIGNvcnJfYXV0aG9ycyAlPiUKICBzZXBhcmF0ZV9yb3dzKGNvdW50cmllcywgc2VwID0gJywnKSAlPiUgCiAgZmlsdGVyKGNvdW50cmllcyA9PSAnVVMnKSAlPiUgCiAgbGVmdF9qb2luKG5hdGlvbmFsaXplX2RmLCBieSA9IGMoJ2ZvcmVfbmFtZScsICdsYXN0X25hbWUnKSkKCnB1Ym1lZF9uYXRfZGYgPC0gcHVibWVkX25hdF9wbWlkcyAlPiUgCiAgZ3JvdXBfYnkocG1pZCwgam91cm5hbCwgcHVibGljYXRpb25fZGF0ZSwgeWVhcikgJT4lCiAgc3VtbWFyaXNlX2F0KHZhcnMoQWZyaWNhbjpTb3V0aEFzaWFuKSwgbWVhbiwgbmEucm0gPSBUKSAlPiUKICB1bmdyb3VwKCkKCmlzY2JfbmF0X2RmIDwtIGtleW5vdGVzICU+JQogIHNlcGFyYXRlX3Jvd3MoYWZmbGNvdW50cmllcywgc2VwID0gJ1xcfCcpICU+JSAKICBmaWx0ZXIoYWZmbGNvdW50cmllcyA9PSAnVW5pdGVkIFN0YXRlcycpICU+JSAKICBsZWZ0X2pvaW4obmF0aW9uYWxpemVfZGYsIGJ5ID0gYygnZm9yZV9uYW1lJywgJ2xhc3RfbmFtZScpKQpgYGAKCmByIGlzY2JfbmF0X2RmICU+JSBmaWx0ZXIoaXMubmEoQWZyaWNhbikpICU+JSBucm93KClgIFVTLWFmZmlsaWF0ZWQgSVNDQiBzcGVha2VycyBkaWQgbm90IGhhdmUgbmF0aW9uYWxpdHkgcHJlZGljdGlvbnMuCmBwdWJtZWRfbmF0X3BtaWRzICU+JSBmaWx0ZXIoaXMubmEoQWZyaWNhbikpICU+JSBucm93KClgIFVTLWFmZmlsaWF0ZWQgYXV0aG9ycyBkaWQgbm90IGhhdmUgbmF0aW9uYWxpdHkgcHJlZGljdGlvbnMuCmByIHB1Ym1lZF9uYXRfcG1pZHMgJT4lIGZpbHRlcihpcy5uYShBZnJpY2FuKSZpcy5uYShmb3JlX25hbWVfc2ltcGxlLngpKSAlPiUgbnJvdygpYCBvZiB0aGVzZSBkb24ndCBoYXZlIGZvcmVfbmFtZV9zaW1wbGUuCihTZWUgZWFybGllciBjb25jbHVzaW9uIGluIEdlbmRlciBhbmFseXNpcy4pCgoKYGBge3J9CnB1Ym1lZF9uYXRfZGYgJT4lIAogIG11dGF0ZShuYXRpb25hbGl6ZWQgPSBpZmVsc2UoaXMubmEoQWZyaWNhbiksICdOT1QgbmF0aW9uYWxpemVkJywgJ05hdGlvbmFsaXplZCcpKSAlPiUgCiAgZ2dwbG90KGFlcyh5ZWFyKHllYXIpLCBmaWxsID0gbmF0aW9uYWxpemVkKSkgKwogIGdlb21fYmFyKCkgKwogIHNjYWxlX2ZpbGxfdmlyaWRpc19kKCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC4yLCAwLjgpKSArCiAgeWxhYignTnVtYmVyIG9mIFVTLWFmZmlsaWF0ZWQgZnVsbCBuYW1lcycpCmBgYAoKCgoK