Setup and data loading

source(here::here("src/common_basis.R"), local = knitr::knit_global())
here() starts at /Users/jiemakel/tyo/teaching-analysis
Registered S3 methods overwritten by 'dbplyr':
  method         from
  print.tbl_lazy     
  print.tbl_sql      
── Attaching packages ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse 1.3.2 ──✔ ggplot2 3.3.6      ✔ purrr   0.3.4 
✔ tibble  3.1.8      ✔ dplyr   1.0.10
✔ tidyr   1.2.0      ✔ stringr 1.4.1 
✔ readr   2.1.2      ✔ forcats 0.5.2 ── Conflicts ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
* Automatic snapshot has updated '~/tyo/teaching-analysis/renv.lock'.

Person work year data

group_costs <- tribble(
  ~subgroup,~cost,
  "4. porras / TR Staff Level 4",6500,
  "3. porras / TR Staff Level 3",5000,
  "2. porras / TR Staff Level 2",3500,
  "1. porras / TR Staff Level 1",2500,
  "Opetuksen ja tutkimuksen tukihenkilöstö / TR Support Staff",3000
)


pwy_in <- read_csv(here("data/input/LTS_henkilo_tehtavajaottelu_henkilostoryhma-htv.csv"),locale=locale(decimal_mark=','),col_types=cols(...35='c',...36='c',`Henkilöstöryhmä / Staff Group`='c',`Henkilöstöalaryhmä / Staff Subgroup`='c',`Vuosi / Year`='i',.default="d"))
New names:
pwy <- pwy_in %>%
  rename(year=`Vuosi / Year`,group=`Henkilöstöryhmä / Staff Group`,subgroup=`Henkilöstöalaryhmä / Staff Subgroup`,unit=...35,subunit=...36) %>%
  select(year,2:15,group,subgroup,unit,subunit) %>%
  pivot_longer(2:15,values_to="pwy") %>%
  filter(!is.na(pwy)) %>%
  select(-name) %>%
  distinct() %>%
  left_join(group_costs) %>%
#  replace_na(list(cost=2500)) %>%
  mutate(cost=pwy*cost)
Joining, by = "subgroup"
pwy

OKM gains data

gains <- read_csv(here("data/input/Rahoitus vuodelle 2022 suoritteen laskennallinen tuotto.csv"), locale=locale(decimal_mark=',')) %>%
  rename(gain=`Suoritteen laskennallinen tuotto vuoden 2022 rahoituksessa / Value of an output in 2022 funding`,okm_weight=`Kriteerin painoarvo OKM-rahoitusmallissa / Share of the criteria in the MinEdu core funding modell`,type=Suorite,subtype=Nimi) %>%
  distinct(type,subtype,okm_weight,gain) %>%
  mutate(type=case_when(
    type == "2 Tutkintopiste, alempi korkeakoulututkinto / Degree point, Bachelor's degree" ~ "Bachelor's",
    type == "1 Tutkintopiste, ylempi korkeakoulututkinto / Degree point, Master's degree" ~ "Master's",
    T ~ type
  ))
Rows: 163 Columns: 9── Column specification ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (2): Suorite, Nimi
dbl (7): Kriteerin painoarvo OKM-rahoitusmallissa / Share of the criteria in the MinEdu core funding modell, HY:n rahoitus kriteereillä vuoden 2022 rahoituksesta / UH fun...
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
gains

Publication data

publ <- read_csv(here("data/input/OKM-seurantakohteet jufo-tasoluokat Osasto ja Alayksikkö.csv")) %>%
  rename(unit=`Tiedekunnan koodi ja nimi`,subunit=`Laitoksen koodi ja nimi`,subsubunit=...1,year=Julkaisuvuosi...4,jufo=`Jufoluokitus (Virta)`,jufo2=`Vahvistettu julkaisukanavaluokitus`,n=`Julkaisujen lukumäärä`) %>%
  select(-c(Julkaisuvuosi...10,`%`,`Viety okm`)) %>%
  group_by(across(-c(`Vahvistettu julkaisufoorumi id`,n))) %>%
  summarize(n=sum(n),.groups="drop")
New names:Rows: 49785 Columns: 11── Column specification ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (4): ...1, Laitoksen koodi ja nimi, Tiedekunnan koodi ja nimi, Viety okm
dbl (7): Julkaisuvuosi...4, Vahvistettu julkaisukanavaluokitus, Jufoluokitus (Virta), Vahvistettu julkaisufoorumi id, Julkaisujen lukumäärä, %, Julkaisuvuosi...10
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
publ_values <- tribble(
  ~jufo, ~value,
  0, 0.1,
  1, 1,
  2, 3,
  3, 4
)

publ_gains <- publ_values %>%
  mutate(gain=(gains %>% filter(type=="9 Julkaisupiste / Point, scientific publications") %>% pull(gain))*value)

publ

Teaching data

teaching <- read_tsv(here("data/processed/all-courses.tsv.gz")) %>%
  filter(ects!=0) %>%
  mutate(type=case_when(
    unittype=="programme" & str_detect(name, "Bachelor") ~ "Bachelor's",
    unittype=="programme" & str_detect(name, "Master|Magister|^Degree") ~ "Master's"
  )) %>% left_join(
    gains %>% 
      filter(type %in% c("Bachelor's","Master's")) %>%
      select(type,gain)
  ) %>%
  mutate(gain_per_ects=if_else(type=="Bachelor's degree",gain/180,gain/120)) %>%
  select(-gain)
Rows: 35566 Columns: 7── Column specification ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: "\t"
chr (4): unitcode, name, unittype, parent_unitcode
dbl (3): year, ects, students
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.Joining, by = "type"
teaching

Degree attainment data

degrees <- read_tsv(here("data/processed/degrees-attained.tsv.gz")) %>%
  mutate(type=case_when(
    str_detect(name, "Bachelor") ~ "2 Tutkintopiste, alempi korkeakoulututkinto / Degree point, Bachelor's degree",
    str_detect(name, "Master|Magister|^Degree Programme") ~ "1 Tutkintopiste, ylempi korkeakoulututkinto / Degree point, Master's degree"
  )) %>%
  left_join(gains %>% select(type,gain)) %>%
  mutate(gain=degrees*gain)
Rows: 510 Columns: 5── Column specification ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: "\t"
chr (3): unitcode, name, parent_unitcode
dbl (2): year, degrees
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.Joining, by = "type"
degrees

OKM euros from publications per euro used for TR staff salary

Background

Costs are estimated using the following table:

group_costs

Note that this only covers core teaching and research staff, and does not take into account differences in salaries between faculties. Of these, “Opetuksen ja tutkimuksen tukihenkilöstö / TR Support Staff” is probably particularly problematic, as its proportion is significant in multiple faculties:

pwy %>%
  filter(year<2023,unit!="H92 Tohtoriohjelmat") %>%
  group_by(year,unit,subgroup) %>%
  summarize(pwy=sum(pwy),.groups="drop") %>%
  ggplot(aes(x=year,y=pwy,color=subgroup)) +
  geom_line() +
  geom_point() +
  scale_x_continuous(breaks=seq(1000,3000,by=4)) +
  facet_wrap(~unit,scales="free_y",ncol=4) +
  theme_hsci_discrete(base_family="Arial") +
  theme(legend.position="bottom") +
  ylab("Person work years") +
  xlab("Year") +
  guides(color=guide_legend(nrow=6))

Thus, in the following analyses, the units where the proportion of teaching support staff in person work years is more than 10% of the total have been grayed out as problematic to compare.

To better approximate the fact that research contributions affect publications with a delay, the gains for publications in a year are weighted by a mean of the staff costs for the year as well as the previous year.

Analysis between faculties

publ %>% 
  filter(year>2017,year<=2022) %>%
  complete(year,subunit,unit,jufo,fill=list(n=0)) %>%
  left_join(publ_gains) %>%
  group_by(year,unit) %>%
  summarise(gain=sum(gain*n,na.rm=T)) %>%
  mutate(unit=fct_reorder(unit,gain)) %>%
  ggplot(aes(x=unit,y=gain)) +
  geom_boxplot() +
  theme_hsci(base_family="Arial") +
  theme(legend.position="bottom") +
  scale_y_continuous(labels=scales::comma_format()) +
  xlab("Faculty") +
  ylab("Euros gained per year") +
  guides(color="none") +
  coord_flip()
Joining, by = "jufo"`summarise()` has grouped output by 'year'. You can override using the `.groups` argument.

problematic_units <- 
  pwy %>% 
  group_by(unit,subgroup) %>% 
  summarize(pwy=sum(pwy)) %>% 
  group_by(unit) %>% 
  mutate(prop=pwy/sum(pwy)) %>%
  summarise(problematic=any(subgroup=="Opetuksen ja tutkimuksen tukihenkilöstö / TR Support Staff" & prop>=0.1), .groups="drop")
`summarise()` has grouped output by 'unit'. You can override using the `.groups` argument.
pwy %>%
  filter(year>2017,year<=2022,!(unit %in% c("H92 Tohtoriohjelmat","H01 Yliopistopalvelut","H00 Yliopiston johto ja yhteiset", "H99 Erilliset laitokset"))) %>%
  group_by(year,unit) %>%
  summarise(cost=sum(cost,na.rm=T),.groups="drop") %>%
  left_join(
    publ %>% 
      filter(year>2017,year<=2022) %>%
      complete(year,subunit,unit,jufo,fill=list(n=0)) %>%
      left_join(publ_gains) %>%
      group_by(year,unit) %>%
      summarise(gain=sum(gain*n,na.rm=T))
  ) %>%
  group_by(unit) %>%
  arrange(year) %>%
  mutate(publ_efficiency=gain/(lag(cost)+cost)*2) %>%
  ungroup() %>%
  left_join(problematic_units) %>%
  filter(!is.na(publ_efficiency)) %>%
  mutate(unit=fct_reorder(unit,if_else(problematic,publ_efficiency,100000000+publ_efficiency))) %>%
  ggplot(aes(x=unit,y=publ_efficiency,color=if_else(problematic,NA,unit=="H40 Humanistinen tiedekunta"))) +
  geom_boxplot() +
  scale_color_manual(values=c("black","red","gray")) +
  theme_hsci(base_family="Arial") +
  theme(legend.position="bottom") +
  xlab("Faculty") +
  ylab("Euros gained per euro spent") +
  guides(color="none") +
  coord_flip()
Joining, by = "jufo"`summarise()` has grouped output by 'year'. You can override using the `.groups` argument.Joining, by = c("year", "unit")Joining, by = "unit"

Analysis within the Faculty of Arts

publ %>% 
  filter(unit=="H40 Humanistinen tiedekunta",year>2017,year<=2022) %>%
  complete(year,subunit,unit,jufo,fill=list(n=0)) %>%
  left_join(publ_gains) %>%
  group_by(year,subunit) %>%
  summarise(gain=sum(gain*n,na.rm=T)) %>%
  mutate(subunit=fct_reorder(subunit,gain)) %>%
  ggplot(aes(x=subunit,y=gain,color=subunit=="H402 Digitaalisten ihmistieteiden osasto")) +
  geom_boxplot() +
  theme_hsci(base_family="Arial") +
  scale_color_manual(values=c("black","red")) +
  scale_y_continuous(labels=scales::comma_format()) +
  theme(legend.position="bottom") +
  theme(legend.position="bottom") +
  xlab("Unit") +
  ylab("Euros gained per year") +
  guides(color="none") +
  coord_flip()
Joining, by = "jufo"`summarise()` has grouped output by 'year'. You can override using the `.groups` argument.

pwy %>%
  filter(unit=="H40 Humanistinen tiedekunta",subunit!="H400 Tiedekunnan yhteiset",year>2017,year<=2022) %>%
  group_by(year,subunit) %>%
  summarise(cost=sum(cost,na.rm=T),.groups="drop") %>%
  left_join(
    publ %>% 
      filter(year>2017,year<=2022) %>%
      complete(year,subunit,unit,jufo,fill=list(n=0)) %>%
      left_join(publ_gains) %>%
      group_by(year,subunit) %>%
      summarise(gain=sum(gain*n,na.rm=T))
  ) %>%
  group_by(subunit) %>%
  arrange(year) %>%
  mutate(publ_efficiency=gain/(lag(cost)+cost)*2) %>%
  ungroup() %>%
  filter(!is.na(publ_efficiency)) %>%
  mutate(subunit=fct_reorder(subunit,publ_efficiency)) %>%
  ggplot(aes(x=subunit,y=publ_efficiency,color=subunit=="H402 Digitaalisten ihmistieteiden osasto")) +
  geom_boxplot() +
  theme_hsci(base_family="Arial") +
  scale_color_manual(values=c("black","red")) +
  theme(legend.position="bottom") +
  theme(legend.position="bottom") +
  xlab("Unit") +
  ylab("Euros gained per euro spent") +
  guides(color="none") +
  coord_flip()
Joining, by = "jufo"`summarise()` has grouped output by 'year'. You can override using the `.groups` argument.Joining, by = c("year", "subunit")

Teaching efficiency

Background

To evaluate teaching efficiency, the average number of ECTS credits gained per course is used. This is the most non-problematic estimate that I could think of for the amount of work put in, as person work year estimates are not tied to teaching programmes and can thus not be used.

Further, to compare the monetary efficiency between master’s and bachelor’s programmes in a single view, gains per programme are calculated by taking OKM euros gained by the University of Helsinki from bachelor’s and master’s degrees and then dividing those by the nominal sizes of the degrees, distributing the gains according to the ECTS outputs of each unit. This is intended to better represent overall teaching contribution toward the degrees so that also course credit going toward minors and degrees in other programmes would be appropriately counted.

Analysis between faculties

teaching %>%
  filter(unittype=="programme") %>%
  select(-name) %>%
  left_join(teaching %>% distinct(parent_unitcode=unitcode,name)) %>%
  group_by(year,name) %>%
  summarize(gain=sum(ects*gain_per_ects),.groups="drop") %>%
  filter(!is.na(gain)) %>%
  mutate(name=fct_reorder(name,gain)) %>%
  ggplot(aes(x=name,y=gain,color=name=="Faculty of Arts")) +
  scale_y_continuous(labels=scales::number_format()) +
  geom_boxplot() + 
  coord_flip() +
  xlab("Faculty") +
  ylab("Total euros gained per year") +
  theme_hsci(base_family="Arial") +
  scale_color_manual(values=c("black","red")) +
  guides(color="none")
Joining, by = "parent_unitcode"

teaching %>% 
  filter(unittype=="course") %>%
  mutate(unitcode=parent_unitcode) %>%
  select(-parent_unitcode,-name,-gain_per_ects) %>%
  inner_join(teaching %>% distinct(unitcode,parent_unitcode,gain_per_ects)) %>% 
  inner_join(teaching %>% distinct(name,parent_unitcode=unitcode)) %>%
  filter(!is.na(gain_per_ects)) %>%
  mutate(name=fct_reorder(name,ects*gain_per_ects)) %>%
  ggplot(aes(x=name,y=ects*gain_per_ects,color=name=="Faculty of Arts")) +
  scale_y_continuous(labels=scales::number_format()) +
  geom_boxplot() + 
  coord_flip(ylim=c(0,25000)) +
  xlab("Faculty") +
  ylab("Euros gained per course given") +
  theme_hsci(base_family="Arial") +
  scale_color_manual(values=c("black","red")) +
  guides(color="none")
Joining, by = "unitcode"Joining, by = "parent_unitcode"

Analysis within the Faculty of Arts

teaching %>%
  filter(parent_unitcode=="H40") %>%
  mutate(name=fct_reorder(name,ects*gain_per_ects)) %>%
  ggplot(aes(x=name,y=ects*gain_per_ects,color=name=="Master's Programme in Linguistic Diversity and Digital Humanities")) +
  scale_y_continuous(labels=scales::number_format()) +
  geom_boxplot() + 
  coord_flip() +
  xlab("Programme") +
  ylab("Total euros gained per year") +
  theme_hsci(base_family="Arial") +
  scale_color_manual(values=c("black","red")) +
  guides(color="none")

teaching %>% 
  filter(unittype=="course") %>%
  mutate(unitcode=parent_unitcode) %>%
  select(-parent_unitcode,-name,-gain_per_ects) %>%
  inner_join(teaching %>% distinct(unitcode,parent_unitcode,name,gain_per_ects)) %>% 
  filter(parent_unitcode=="H40") %>%
  mutate(name=fct_reorder(name,ects*gain_per_ects)) %>%
  ggplot(aes(x=name,y=ects*gain_per_ects,color=name=="Master's Programme in Linguistic Diversity and Digital Humanities")) +
  scale_y_continuous(labels=scales::number_format()) +
  geom_boxplot() + 
  coord_flip(ylim=c(0,25000)) +
  xlab("Faculty") +
  ylab("Euros gained per course given") +
  theme_hsci(base_family="Arial") +
  scale_color_manual(values=c("black","red")) +
  guides(color="none")
Joining, by = "unitcode"

Analysis within the Master’s Programme in Linguistic Diversity and Digital Humanities

teaching %>% 
  filter(str_detect(unitcode,"^LDA-")) %>%
  mutate(unitcode=str_sub(unitcode,end=5)) %>%
  mutate(unitcode=fct_recode(unitcode,"Common"="LDA-3","MA-Thesis"="LDA-8","Digital humanities"="LDA-H","Cognitive science"="LDA-C","Language technology"="LDA-T","Phonetics"="LDA-P","Linguistics"="LDA-D","Linguistics"="LDA-G","Linguistics"="LDA-L","Common"="LDA-E","Common"="LDA-M")) %>%
  mutate(gain=ects*(gains %>% filter(type=="Master's") %>% pull(gain))/120) %>%
  mutate(unitcode=fct_reorder(unitcode,gain)) %>%
  ggplot(aes(x=unitcode,y=gain)) +
  scale_y_continuous(labels=scales::number_format()) +
  geom_boxplot() + 
  coord_flip() +
  xlab("Track") +
  ylab("Euros gained per course given") +
  theme_hsci_discrete(base_family="Arial")

LS0tCnRpdGxlOiAiRGF0YSBjcmVhdGlvbiBhbmQgYW5hbHlzaXMgZXhhbXBsZXMiCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCIKb3V0cHV0OiAKICBodG1sX25vdGVib29rOgogICAgdG9jOiB5ZXMKICAgIGNvZGVfZm9sZGluZzogaGlkZQogIG1kX2RvY3VtZW50OgogICAgdmFyaWFudDogZ2ZtIAogICAgdG9jOiB5ZXMKLS0tCgojIFNldHVwIGFuZCBkYXRhIGxvYWRpbmcKCmBgYHtyIHNldHVwfQpzb3VyY2UoaGVyZTo6aGVyZSgic3JjL2NvbW1vbl9iYXNpcy5SIiksIGxvY2FsID0ga25pdHI6OmtuaXRfZ2xvYmFsKCkpCmBgYAoKIyMgUGVyc29uIHdvcmsgeWVhciBkYXRhCgpgYGB7cn0KZ3JvdXBfY29zdHMgPC0gdHJpYmJsZSgKICB+c3ViZ3JvdXAsfmNvc3QsCiAgIjQuIHBvcnJhcyAvIFRSIFN0YWZmIExldmVsIDQiLDY1MDAsCiAgIjMuIHBvcnJhcyAvIFRSIFN0YWZmIExldmVsIDMiLDUwMDAsCiAgIjIuIHBvcnJhcyAvIFRSIFN0YWZmIExldmVsIDIiLDM1MDAsCiAgIjEuIHBvcnJhcyAvIFRSIFN0YWZmIExldmVsIDEiLDI1MDAsCiAgIk9wZXR1a3NlbiBqYSB0dXRraW11a3NlbiB0dWtpaGVua2lsw7ZzdMO2IC8gVFIgU3VwcG9ydCBTdGFmZiIsMzAwMAopCgpwd3lfaW4gPC0gcmVhZF9jc3YoaGVyZSgiZGF0YS9pbnB1dC9MVFNfaGVua2lsb190ZWh0YXZhamFvdHRlbHVfaGVua2lsb3N0b3J5aG1hLWh0di5jc3YiKSxsb2NhbGU9bG9jYWxlKGRlY2ltYWxfbWFyaz0nLCcpLGNvbF90eXBlcz1jb2xzKC4uLjM1PSdjJywuLi4zNj0nYycsYEhlbmtpbMO2c3TDtnJ5aG3DpCAvIFN0YWZmIEdyb3VwYD0nYycsYEhlbmtpbMO2c3TDtmFsYXJ5aG3DpCAvIFN0YWZmIFN1Ymdyb3VwYD0nYycsYFZ1b3NpIC8gWWVhcmA9J2knLC5kZWZhdWx0PSJkIikpCnB3eSA8LSBwd3lfaW4gJT4lCiAgcmVuYW1lKHllYXI9YFZ1b3NpIC8gWWVhcmAsZ3JvdXA9YEhlbmtpbMO2c3TDtnJ5aG3DpCAvIFN0YWZmIEdyb3VwYCxzdWJncm91cD1gSGVua2lsw7ZzdMO2YWxhcnlobcOkIC8gU3RhZmYgU3ViZ3JvdXBgLHVuaXQ9Li4uMzUsc3VidW5pdD0uLi4zNikgJT4lCiAgc2VsZWN0KHllYXIsMjoxNSxncm91cCxzdWJncm91cCx1bml0LHN1YnVuaXQpICU+JQogIHBpdm90X2xvbmdlcigyOjE1LHZhbHVlc190bz0icHd5IikgJT4lCiAgZmlsdGVyKCFpcy5uYShwd3kpKSAlPiUKICBzZWxlY3QoLW5hbWUpICU+JQogIGRpc3RpbmN0KCkgJT4lCiAgbGVmdF9qb2luKGdyb3VwX2Nvc3RzKSAlPiUKIyAgcmVwbGFjZV9uYShsaXN0KGNvc3Q9MjUwMCkpICU+JQogIG11dGF0ZShjb3N0PXB3eSpjb3N0KQpwd3kKYGBgCgojIyBPS00gZ2FpbnMgZGF0YQoKYGBge3J9CmdhaW5zIDwtIHJlYWRfY3N2KGhlcmUoImRhdGEvaW5wdXQvUmFob2l0dXMgdnVvZGVsbGUgMjAyMiBzdW9yaXR0ZWVuIGxhc2tlbm5hbGxpbmVuIHR1b3R0by5jc3YiKSwgbG9jYWxlPWxvY2FsZShkZWNpbWFsX21hcms9JywnKSkgJT4lCiAgcmVuYW1lKGdhaW49YFN1b3JpdHRlZW4gbGFza2VubmFsbGluZW4gdHVvdHRvIHZ1b2RlbiAyMDIyIHJhaG9pdHVrc2Vzc2EgLyBWYWx1ZSBvZiBhbiBvdXRwdXQgaW4gMjAyMiBmdW5kaW5nYCxva21fd2VpZ2h0PWBLcml0ZWVyaW4gcGFpbm9hcnZvIE9LTS1yYWhvaXR1c21hbGxpc3NhIC8gU2hhcmUgb2YgdGhlIGNyaXRlcmlhIGluIHRoZSBNaW5FZHUgY29yZSBmdW5kaW5nIG1vZGVsbGAsdHlwZT1TdW9yaXRlLHN1YnR5cGU9TmltaSkgJT4lCiAgZGlzdGluY3QodHlwZSxzdWJ0eXBlLG9rbV93ZWlnaHQsZ2FpbikgJT4lCiAgbXV0YXRlKHR5cGU9Y2FzZV93aGVuKAogICAgdHlwZSA9PSAiMiBUdXRraW50b3Bpc3RlLCBhbGVtcGkga29ya2Vha291bHV0dXRraW50byAvIERlZ3JlZSBwb2ludCwgQmFjaGVsb3IncyBkZWdyZWUiIH4gIkJhY2hlbG9yJ3MiLAogICAgdHlwZSA9PSAiMSBUdXRraW50b3Bpc3RlLCB5bGVtcGkga29ya2Vha291bHV0dXRraW50byAvIERlZ3JlZSBwb2ludCwgTWFzdGVyJ3MgZGVncmVlIiB+ICJNYXN0ZXIncyIsCiAgICBUIH4gdHlwZQogICkpCmdhaW5zCmBgYAoKCiMjIFB1YmxpY2F0aW9uIGRhdGEKCmBgYHtyfQpwdWJsIDwtIHJlYWRfY3N2KGhlcmUoImRhdGEvaW5wdXQvT0tNLXNldXJhbnRha29odGVldCBqdWZvLXRhc29sdW9rYXQgT3Nhc3RvIGphIEFsYXlrc2lra8O2LmNzdiIpKSAlPiUKICByZW5hbWUodW5pdD1gVGllZGVrdW5uYW4ga29vZGkgamEgbmltaWAsc3VidW5pdD1gTGFpdG9rc2VuIGtvb2RpIGphIG5pbWlgLHN1YnN1YnVuaXQ9Li4uMSx5ZWFyPUp1bGthaXN1dnVvc2kuLi40LGp1Zm89YEp1Zm9sdW9raXR1cyAoVmlydGEpYCxqdWZvMj1gVmFodmlzdGV0dHUganVsa2Fpc3VrYW5hdmFsdW9raXR1c2Asbj1gSnVsa2Fpc3VqZW4gbHVrdW3DpMOkcsOkYCkgJT4lCiAgc2VsZWN0KC1jKEp1bGthaXN1dnVvc2kuLi4xMCxgJWAsYFZpZXR5IG9rbWApKSAlPiUKICBncm91cF9ieShhY3Jvc3MoLWMoYFZhaHZpc3RldHR1IGp1bGthaXN1Zm9vcnVtaSBpZGAsbikpKSAlPiUKICBzdW1tYXJpemUobj1zdW0obiksLmdyb3Vwcz0iZHJvcCIpCgpwdWJsX3ZhbHVlcyA8LSB0cmliYmxlKAogIH5qdWZvLCB+dmFsdWUsCiAgMCwgMC4xLAogIDEsIDEsCiAgMiwgMywKICAzLCA0CikKCnB1YmxfZ2FpbnMgPC0gcHVibF92YWx1ZXMgJT4lCiAgbXV0YXRlKGdhaW49KGdhaW5zICU+JSBmaWx0ZXIodHlwZT09IjkgSnVsa2Fpc3VwaXN0ZSAvIFBvaW50LCBzY2llbnRpZmljIHB1YmxpY2F0aW9ucyIpICU+JSBwdWxsKGdhaW4pKSp2YWx1ZSkKCnB1YmwKYGBgCgojIyBUZWFjaGluZyBkYXRhCgpgYGB7cn0KdGVhY2hpbmcgPC0gcmVhZF90c3YoaGVyZSgiZGF0YS9wcm9jZXNzZWQvYWxsLWNvdXJzZXMudHN2Lmd6IikpICU+JQogIGZpbHRlcihlY3RzIT0wKSAlPiUKICBtdXRhdGUodHlwZT1jYXNlX3doZW4oCiAgICB1bml0dHlwZT09InByb2dyYW1tZSIgJiBzdHJfZGV0ZWN0KG5hbWUsICJCYWNoZWxvciIpIH4gIkJhY2hlbG9yJ3MiLAogICAgdW5pdHR5cGU9PSJwcm9ncmFtbWUiICYgc3RyX2RldGVjdChuYW1lLCAiTWFzdGVyfE1hZ2lzdGVyfF5EZWdyZWUiKSB+ICJNYXN0ZXIncyIKICApKSAlPiUgbGVmdF9qb2luKAogICAgZ2FpbnMgJT4lIAogICAgICBmaWx0ZXIodHlwZSAlaW4lIGMoIkJhY2hlbG9yJ3MiLCJNYXN0ZXIncyIpKSAlPiUKICAgICAgc2VsZWN0KHR5cGUsZ2FpbikKICApICU+JQogIG11dGF0ZShnYWluX3Blcl9lY3RzPWlmX2Vsc2UodHlwZT09IkJhY2hlbG9yJ3MgZGVncmVlIixnYWluLzE4MCxnYWluLzEyMCkpICU+JQogIHNlbGVjdCgtZ2FpbikKdGVhY2hpbmcKYGBgCgojIyBEZWdyZWUgYXR0YWlubWVudCBkYXRhCgpgYGB7cn0KZGVncmVlcyA8LSByZWFkX3RzdihoZXJlKCJkYXRhL3Byb2Nlc3NlZC9kZWdyZWVzLWF0dGFpbmVkLnRzdi5neiIpKSAlPiUKICBtdXRhdGUodHlwZT1jYXNlX3doZW4oCiAgICBzdHJfZGV0ZWN0KG5hbWUsICJCYWNoZWxvciIpIH4gIjIgVHV0a2ludG9waXN0ZSwgYWxlbXBpIGtvcmtlYWtvdWx1dHV0a2ludG8gLyBEZWdyZWUgcG9pbnQsIEJhY2hlbG9yJ3MgZGVncmVlIiwKICAgIHN0cl9kZXRlY3QobmFtZSwgIk1hc3RlcnxNYWdpc3RlcnxeRGVncmVlIFByb2dyYW1tZSIpIH4gIjEgVHV0a2ludG9waXN0ZSwgeWxlbXBpIGtvcmtlYWtvdWx1dHV0a2ludG8gLyBEZWdyZWUgcG9pbnQsIE1hc3RlcidzIGRlZ3JlZSIKICApKSAlPiUKICBsZWZ0X2pvaW4oZ2FpbnMgJT4lIHNlbGVjdCh0eXBlLGdhaW4pKSAlPiUKICBtdXRhdGUoZ2Fpbj1kZWdyZWVzKmdhaW4pCmRlZ3JlZXMKYGBgCgojIE9LTSBldXJvcyBmcm9tIHB1YmxpY2F0aW9ucyBwZXIgZXVybyB1c2VkIGZvciBUUiBzdGFmZiBzYWxhcnkKCiMjIEJhY2tncm91bmQKCkNvc3RzIGFyZSBlc3RpbWF0ZWQgdXNpbmcgdGhlIGZvbGxvd2luZyB0YWJsZToKCmBgYHtyfQpncm91cF9jb3N0cwpgYGAKIApOb3RlIHRoYXQgdGhpcyBvbmx5IGNvdmVycyBjb3JlIHRlYWNoaW5nIGFuZCByZXNlYXJjaCBzdGFmZiwgYW5kIGRvZXMgbm90IHRha2UgaW50byBhY2NvdW50IGRpZmZlcmVuY2VzIGluIHNhbGFyaWVzIGJldHdlZW4gZmFjdWx0aWVzLiBPZiB0aGVzZSwgIk9wZXR1a3NlbiBqYSB0dXRraW11a3NlbiB0dWtpaGVua2lsw7ZzdMO2IC8gVFIgU3VwcG9ydCBTdGFmZiIgaXMgcHJvYmFibHkgcGFydGljdWxhcmx5IHByb2JsZW1hdGljLCBhcyBpdHMgcHJvcG9ydGlvbiBpcyBzaWduaWZpY2FudCBpbiBtdWx0aXBsZSBmYWN1bHRpZXM6CgpgYGB7cixmaWcud2lkdGg9OCxmaWcuaGVpZ2h0PTExfQpwd3kgJT4lCiAgZmlsdGVyKHllYXI8MjAyMyx1bml0IT0iSDkyIFRvaHRvcmlvaGplbG1hdCIpICU+JQogIGdyb3VwX2J5KHllYXIsdW5pdCxzdWJncm91cCkgJT4lCiAgc3VtbWFyaXplKHB3eT1zdW0ocHd5KSwuZ3JvdXBzPSJkcm9wIikgJT4lCiAgZ2dwbG90KGFlcyh4PXllYXIseT1wd3ksY29sb3I9c3ViZ3JvdXApKSArCiAgZ2VvbV9saW5lKCkgKwogIGdlb21fcG9pbnQoKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcz1zZXEoMTAwMCwzMDAwLGJ5PTQpKSArCiAgZmFjZXRfd3JhcCh+dW5pdCxzY2FsZXM9ImZyZWVfeSIsbmNvbD00KSArCiAgdGhlbWVfaHNjaV9kaXNjcmV0ZShiYXNlX2ZhbWlseT0iQXJpYWwiKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJib3R0b20iKSArCiAgeWxhYigiUGVyc29uIHdvcmsgeWVhcnMiKSArCiAgeGxhYigiWWVhciIpICsKICBndWlkZXMoY29sb3I9Z3VpZGVfbGVnZW5kKG5yb3c9NikpCmBgYAoKVGh1cywgaW4gdGhlIGZvbGxvd2luZyBhbmFseXNlcywgdGhlICoqdW5pdHMgd2hlcmUgdGhlIHByb3BvcnRpb24gb2YgdGVhY2hpbmcgc3VwcG9ydCBzdGFmZiBpbiBwZXJzb24gd29yayB5ZWFycyBpcyBtb3JlIHRoYW4gMTAlIG9mIHRoZSB0b3RhbCBoYXZlIGJlZW4gZ3JheWVkIG91dCBhcyBwcm9ibGVtYXRpYyB0byBjb21wYXJlKiouCgoqKlRvIGJldHRlciBhcHByb3hpbWF0ZSB0aGUgZmFjdCB0aGF0IHJlc2VhcmNoIGNvbnRyaWJ1dGlvbnMgYWZmZWN0IHB1YmxpY2F0aW9ucyB3aXRoIGEgZGVsYXksIHRoZSBnYWlucyBmb3IgcHVibGljYXRpb25zIGluIGEgeWVhciBhcmUgd2VpZ2h0ZWQgYnkgYSBtZWFuIG9mIHRoZSBzdGFmZiBjb3N0cyBmb3IgdGhlIHllYXIgYXMgd2VsbCBhcyB0aGUgcHJldmlvdXMgeWVhcioqLgoKIyMgQW5hbHlzaXMgYmV0d2VlbiBmYWN1bHRpZXMKCmBgYHtyfQpwdWJsICU+JSAKICBmaWx0ZXIoeWVhcj4yMDE3LHllYXI8PTIwMjIpICU+JQogIGNvbXBsZXRlKHllYXIsc3VidW5pdCx1bml0LGp1Zm8sZmlsbD1saXN0KG49MCkpICU+JQogIGxlZnRfam9pbihwdWJsX2dhaW5zKSAlPiUKICBncm91cF9ieSh5ZWFyLHVuaXQpICU+JQogIHN1bW1hcmlzZShnYWluPXN1bShnYWluKm4sbmEucm09VCkpICU+JQogIG11dGF0ZSh1bml0PWZjdF9yZW9yZGVyKHVuaXQsZ2FpbikpICU+JQogIGdncGxvdChhZXMoeD11bml0LHk9Z2FpbikpICsKICBnZW9tX2JveHBsb3QoKSArCiAgdGhlbWVfaHNjaShiYXNlX2ZhbWlseT0iQXJpYWwiKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJib3R0b20iKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscz1zY2FsZXM6OmNvbW1hX2Zvcm1hdCgpKSArCiAgeGxhYigiRmFjdWx0eSIpICsKICB5bGFiKCJFdXJvcyBnYWluZWQgcGVyIHllYXIiKSArCiAgZ3VpZGVzKGNvbG9yPSJub25lIikgKwogIGNvb3JkX2ZsaXAoKQpgYGAKCgpgYGB7cn0KcHJvYmxlbWF0aWNfdW5pdHMgPC0gCiAgcHd5ICU+JSAKICBncm91cF9ieSh1bml0LHN1Ymdyb3VwKSAlPiUgCiAgc3VtbWFyaXplKHB3eT1zdW0ocHd5KSkgJT4lIAogIGdyb3VwX2J5KHVuaXQpICU+JSAKICBtdXRhdGUocHJvcD1wd3kvc3VtKHB3eSkpICU+JQogIHN1bW1hcmlzZShwcm9ibGVtYXRpYz1hbnkoc3ViZ3JvdXA9PSJPcGV0dWtzZW4gamEgdHV0a2ltdWtzZW4gdHVraWhlbmtpbMO2c3TDtiAvIFRSIFN1cHBvcnQgU3RhZmYiICYgcHJvcD49MC4xKSwgLmdyb3Vwcz0iZHJvcCIpCgpwd3kgJT4lCiAgZmlsdGVyKHllYXI+MjAxNyx5ZWFyPD0yMDIyLCEodW5pdCAlaW4lIGMoIkg5MiBUb2h0b3Jpb2hqZWxtYXQiLCJIMDEgWWxpb3Bpc3RvcGFsdmVsdXQiLCJIMDAgWWxpb3Bpc3RvbiBqb2h0byBqYSB5aHRlaXNldCIsICJIOTkgRXJpbGxpc2V0IGxhaXRva3NldCIpKSkgJT4lCiAgZ3JvdXBfYnkoeWVhcix1bml0KSAlPiUKICBzdW1tYXJpc2UoY29zdD1zdW0oY29zdCxuYS5ybT1UKSwuZ3JvdXBzPSJkcm9wIikgJT4lCiAgbGVmdF9qb2luKAogICAgcHVibCAlPiUgCiAgICAgIGZpbHRlcih5ZWFyPjIwMTcseWVhcjw9MjAyMikgJT4lCiAgICAgIGNvbXBsZXRlKHllYXIsc3VidW5pdCx1bml0LGp1Zm8sZmlsbD1saXN0KG49MCkpICU+JQogICAgICBsZWZ0X2pvaW4ocHVibF9nYWlucykgJT4lCiAgICAgIGdyb3VwX2J5KHllYXIsdW5pdCkgJT4lCiAgICAgIHN1bW1hcmlzZShnYWluPXN1bShnYWluKm4sbmEucm09VCkpCiAgKSAlPiUKICBncm91cF9ieSh1bml0KSAlPiUKICBhcnJhbmdlKHllYXIpICU+JQogIG11dGF0ZShwdWJsX2VmZmljaWVuY3k9Z2Fpbi8obGFnKGNvc3QpK2Nvc3QpKjIpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBsZWZ0X2pvaW4ocHJvYmxlbWF0aWNfdW5pdHMpICU+JQogIGZpbHRlcighaXMubmEocHVibF9lZmZpY2llbmN5KSkgJT4lCiAgbXV0YXRlKHVuaXQ9ZmN0X3Jlb3JkZXIodW5pdCxpZl9lbHNlKHByb2JsZW1hdGljLHB1YmxfZWZmaWNpZW5jeSwxMDAwMDAwMDArcHVibF9lZmZpY2llbmN5KSkpICU+JQogIGdncGxvdChhZXMoeD11bml0LHk9cHVibF9lZmZpY2llbmN5LGNvbG9yPWlmX2Vsc2UocHJvYmxlbWF0aWMsTkEsdW5pdD09Ikg0MCBIdW1hbmlzdGluZW4gdGllZGVrdW50YSIpKSkgKwogIGdlb21fYm94cGxvdCgpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoImJsYWNrIiwicmVkIiwiZ3JheSIpKSArCiAgdGhlbWVfaHNjaShiYXNlX2ZhbWlseT0iQXJpYWwiKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJib3R0b20iKSArCiAgeGxhYigiRmFjdWx0eSIpICsKICB5bGFiKCJFdXJvcyBnYWluZWQgcGVyIGV1cm8gc3BlbnQiKSArCiAgZ3VpZGVzKGNvbG9yPSJub25lIikgKwogIGNvb3JkX2ZsaXAoKQpgYGAKCiMjIEFuYWx5c2lzIHdpdGhpbiB0aGUgRmFjdWx0eSBvZiBBcnRzCgpgYGB7cn0KcHVibCAlPiUgCiAgZmlsdGVyKHVuaXQ9PSJINDAgSHVtYW5pc3RpbmVuIHRpZWRla3VudGEiLHllYXI+MjAxNyx5ZWFyPD0yMDIyKSAlPiUKICBjb21wbGV0ZSh5ZWFyLHN1YnVuaXQsdW5pdCxqdWZvLGZpbGw9bGlzdChuPTApKSAlPiUKICBsZWZ0X2pvaW4ocHVibF9nYWlucykgJT4lCiAgZ3JvdXBfYnkoeWVhcixzdWJ1bml0KSAlPiUKICBzdW1tYXJpc2UoZ2Fpbj1zdW0oZ2FpbipuLG5hLnJtPVQpKSAlPiUKICBtdXRhdGUoc3VidW5pdD1mY3RfcmVvcmRlcihzdWJ1bml0LGdhaW4pKSAlPiUKICBnZ3Bsb3QoYWVzKHg9c3VidW5pdCx5PWdhaW4sY29sb3I9c3VidW5pdD09Ikg0MDIgRGlnaXRhYWxpc3RlbiBpaG1pc3RpZXRlaWRlbiBvc2FzdG8iKSkgKwogIGdlb21fYm94cGxvdCgpICsKICB0aGVtZV9oc2NpKGJhc2VfZmFtaWx5PSJBcmlhbCIpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoImJsYWNrIiwicmVkIikpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzPXNjYWxlczo6Y29tbWFfZm9ybWF0KCkpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249ImJvdHRvbSIpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249ImJvdHRvbSIpICsKICB4bGFiKCJVbml0IikgKwogIHlsYWIoIkV1cm9zIGdhaW5lZCBwZXIgeWVhciIpICsKICBndWlkZXMoY29sb3I9Im5vbmUiKSArCiAgY29vcmRfZmxpcCgpCmBgYAoKCmBgYHtyfQpwd3kgJT4lCiAgZmlsdGVyKHVuaXQ9PSJINDAgSHVtYW5pc3RpbmVuIHRpZWRla3VudGEiLHN1YnVuaXQhPSJINDAwIFRpZWRla3VubmFuIHlodGVpc2V0Iix5ZWFyPjIwMTcseWVhcjw9MjAyMikgJT4lCiAgZ3JvdXBfYnkoeWVhcixzdWJ1bml0KSAlPiUKICBzdW1tYXJpc2UoY29zdD1zdW0oY29zdCxuYS5ybT1UKSwuZ3JvdXBzPSJkcm9wIikgJT4lCiAgbGVmdF9qb2luKAogICAgcHVibCAlPiUgCiAgICAgIGZpbHRlcih5ZWFyPjIwMTcseWVhcjw9MjAyMikgJT4lCiAgICAgIGNvbXBsZXRlKHllYXIsc3VidW5pdCx1bml0LGp1Zm8sZmlsbD1saXN0KG49MCkpICU+JQogICAgICBsZWZ0X2pvaW4ocHVibF9nYWlucykgJT4lCiAgICAgIGdyb3VwX2J5KHllYXIsc3VidW5pdCkgJT4lCiAgICAgIHN1bW1hcmlzZShnYWluPXN1bShnYWluKm4sbmEucm09VCkpCiAgKSAlPiUKICBncm91cF9ieShzdWJ1bml0KSAlPiUKICBhcnJhbmdlKHllYXIpICU+JQogIG11dGF0ZShwdWJsX2VmZmljaWVuY3k9Z2Fpbi8obGFnKGNvc3QpK2Nvc3QpKjIpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBmaWx0ZXIoIWlzLm5hKHB1YmxfZWZmaWNpZW5jeSkpICU+JQogIG11dGF0ZShzdWJ1bml0PWZjdF9yZW9yZGVyKHN1YnVuaXQscHVibF9lZmZpY2llbmN5KSkgJT4lCiAgZ2dwbG90KGFlcyh4PXN1YnVuaXQseT1wdWJsX2VmZmljaWVuY3ksY29sb3I9c3VidW5pdD09Ikg0MDIgRGlnaXRhYWxpc3RlbiBpaG1pc3RpZXRlaWRlbiBvc2FzdG8iKSkgKwogIGdlb21fYm94cGxvdCgpICsKICB0aGVtZV9oc2NpKGJhc2VfZmFtaWx5PSJBcmlhbCIpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoImJsYWNrIiwicmVkIikpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249ImJvdHRvbSIpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249ImJvdHRvbSIpICsKICB4bGFiKCJVbml0IikgKwogIHlsYWIoIkV1cm9zIGdhaW5lZCBwZXIgZXVybyBzcGVudCIpICsKICBndWlkZXMoY29sb3I9Im5vbmUiKSArCiAgY29vcmRfZmxpcCgpCmBgYAoKIyBUZWFjaGluZyBlZmZpY2llbmN5CgojIyBCYWNrZ3JvdW5kCgoqKlRvIGV2YWx1YXRlIHRlYWNoaW5nIGVmZmljaWVuY3ksIHRoZSBhdmVyYWdlIG51bWJlciBvZiBFQ1RTIGNyZWRpdHMgZ2FpbmVkIHBlciBjb3Vyc2UgaXMgdXNlZCoqLiBUaGlzIGlzIHRoZSBtb3N0IG5vbi1wcm9ibGVtYXRpYyBlc3RpbWF0ZSB0aGF0IEkgY291bGQgdGhpbmsgb2YgZm9yIHRoZSBhbW91bnQgb2Ygd29yayBwdXQgaW4sIGFzIHBlcnNvbiB3b3JrIHllYXIgZXN0aW1hdGVzIGFyZSBub3QgdGllZCB0byB0ZWFjaGluZyBwcm9ncmFtbWVzIGFuZCBjYW4gdGh1cyBub3QgYmUgdXNlZC4KCkZ1cnRoZXIsIHRvIGNvbXBhcmUgdGhlIG1vbmV0YXJ5IGVmZmljaWVuY3kgYmV0d2VlbiBtYXN0ZXIncyBhbmQgYmFjaGVsb3IncyBwcm9ncmFtbWVzIGluIGEgc2luZ2xlIHZpZXcsIGdhaW5zIHBlciBwcm9ncmFtbWUgYXJlIGNhbGN1bGF0ZWQgYnkgdGFraW5nIE9LTSBldXJvcyBnYWluZWQgYnkgdGhlIFVuaXZlcnNpdHkgb2YgSGVsc2lua2kgZnJvbSBiYWNoZWxvcidzIGFuZCBtYXN0ZXIncyBkZWdyZWVzIGFuZCB0aGVuIGRpdmlkaW5nIHRob3NlIGJ5IHRoZSBub21pbmFsIHNpemVzIG9mIHRoZSBkZWdyZWVzLCBkaXN0cmlidXRpbmcgdGhlIGdhaW5zIGFjY29yZGluZyB0byB0aGUgRUNUUyBvdXRwdXRzIG9mIGVhY2ggdW5pdC4gVGhpcyBpcyBpbnRlbmRlZCB0byBiZXR0ZXIgcmVwcmVzZW50IG92ZXJhbGwgdGVhY2hpbmcgY29udHJpYnV0aW9uIHRvd2FyZCB0aGUgZGVncmVlcyBzbyB0aGF0IGFsc28gY291cnNlIGNyZWRpdCBnb2luZyB0b3dhcmQgbWlub3JzIGFuZCBkZWdyZWVzIGluIG90aGVyIHByb2dyYW1tZXMgd291bGQgYmUgYXBwcm9wcmlhdGVseSBjb3VudGVkLgoKIyMgQW5hbHlzaXMgYmV0d2VlbiBmYWN1bHRpZXMKCmBgYHtyfQp0ZWFjaGluZyAlPiUKICBmaWx0ZXIodW5pdHR5cGU9PSJwcm9ncmFtbWUiKSAlPiUKICBzZWxlY3QoLW5hbWUpICU+JQogIGxlZnRfam9pbih0ZWFjaGluZyAlPiUgZGlzdGluY3QocGFyZW50X3VuaXRjb2RlPXVuaXRjb2RlLG5hbWUpKSAlPiUKICBncm91cF9ieSh5ZWFyLG5hbWUpICU+JQogIHN1bW1hcml6ZShnYWluPXN1bShlY3RzKmdhaW5fcGVyX2VjdHMpLC5ncm91cHM9ImRyb3AiKSAlPiUKICBmaWx0ZXIoIWlzLm5hKGdhaW4pKSAlPiUKICBtdXRhdGUobmFtZT1mY3RfcmVvcmRlcihuYW1lLGdhaW4pKSAlPiUKICBnZ3Bsb3QoYWVzKHg9bmFtZSx5PWdhaW4sY29sb3I9bmFtZT09IkZhY3VsdHkgb2YgQXJ0cyIpKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscz1zY2FsZXM6Om51bWJlcl9mb3JtYXQoKSkgKwogIGdlb21fYm94cGxvdCgpICsgCiAgY29vcmRfZmxpcCgpICsKICB4bGFiKCJGYWN1bHR5IikgKwogIHlsYWIoIlRvdGFsIGV1cm9zIGdhaW5lZCBwZXIgeWVhciIpICsKICB0aGVtZV9oc2NpKGJhc2VfZmFtaWx5PSJBcmlhbCIpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoImJsYWNrIiwicmVkIikpICsKICBndWlkZXMoY29sb3I9Im5vbmUiKQpgYGAKCmBgYHtyfQp0ZWFjaGluZyAlPiUgCiAgZmlsdGVyKHVuaXR0eXBlPT0iY291cnNlIikgJT4lCiAgbXV0YXRlKHVuaXRjb2RlPXBhcmVudF91bml0Y29kZSkgJT4lCiAgc2VsZWN0KC1wYXJlbnRfdW5pdGNvZGUsLW5hbWUsLWdhaW5fcGVyX2VjdHMpICU+JQogIGlubmVyX2pvaW4odGVhY2hpbmcgJT4lIGRpc3RpbmN0KHVuaXRjb2RlLHBhcmVudF91bml0Y29kZSxnYWluX3Blcl9lY3RzKSkgJT4lIAogIGlubmVyX2pvaW4odGVhY2hpbmcgJT4lIGRpc3RpbmN0KG5hbWUscGFyZW50X3VuaXRjb2RlPXVuaXRjb2RlKSkgJT4lCiAgZmlsdGVyKCFpcy5uYShnYWluX3Blcl9lY3RzKSkgJT4lCiAgbXV0YXRlKG5hbWU9ZmN0X3Jlb3JkZXIobmFtZSxlY3RzKmdhaW5fcGVyX2VjdHMpKSAlPiUKICBnZ3Bsb3QoYWVzKHg9bmFtZSx5PWVjdHMqZ2Fpbl9wZXJfZWN0cyxjb2xvcj1uYW1lPT0iRmFjdWx0eSBvZiBBcnRzIikpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzPXNjYWxlczo6bnVtYmVyX2Zvcm1hdCgpKSArCiAgZ2VvbV9ib3hwbG90KCkgKyAKICBjb29yZF9mbGlwKHlsaW09YygwLDI1MDAwKSkgKwogIHhsYWIoIkZhY3VsdHkiKSArCiAgeWxhYigiRXVyb3MgZ2FpbmVkIHBlciBjb3Vyc2UgZ2l2ZW4iKSArCiAgdGhlbWVfaHNjaShiYXNlX2ZhbWlseT0iQXJpYWwiKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCJibGFjayIsInJlZCIpKSArCiAgZ3VpZGVzKGNvbG9yPSJub25lIikKYGBgCgojIyBBbmFseXNpcyB3aXRoaW4gdGhlIEZhY3VsdHkgb2YgQXJ0cwoKYGBge3J9CnRlYWNoaW5nICU+JQogIGZpbHRlcihwYXJlbnRfdW5pdGNvZGU9PSJINDAiKSAlPiUKICBtdXRhdGUobmFtZT1mY3RfcmVvcmRlcihuYW1lLGVjdHMqZ2Fpbl9wZXJfZWN0cykpICU+JQogIGdncGxvdChhZXMoeD1uYW1lLHk9ZWN0cypnYWluX3Blcl9lY3RzLGNvbG9yPW5hbWU9PSJNYXN0ZXIncyBQcm9ncmFtbWUgaW4gTGluZ3Vpc3RpYyBEaXZlcnNpdHkgYW5kIERpZ2l0YWwgSHVtYW5pdGllcyIpKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscz1zY2FsZXM6Om51bWJlcl9mb3JtYXQoKSkgKwogIGdlb21fYm94cGxvdCgpICsgCiAgY29vcmRfZmxpcCgpICsKICB4bGFiKCJQcm9ncmFtbWUiKSArCiAgeWxhYigiVG90YWwgZXVyb3MgZ2FpbmVkIHBlciB5ZWFyIikgKwogIHRoZW1lX2hzY2koYmFzZV9mYW1pbHk9IkFyaWFsIikgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygiYmxhY2siLCJyZWQiKSkgKwogIGd1aWRlcyhjb2xvcj0ibm9uZSIpCmBgYAoKYGBge3J9CnRlYWNoaW5nICU+JSAKICBmaWx0ZXIodW5pdHR5cGU9PSJjb3Vyc2UiKSAlPiUKICBtdXRhdGUodW5pdGNvZGU9cGFyZW50X3VuaXRjb2RlKSAlPiUKICBzZWxlY3QoLXBhcmVudF91bml0Y29kZSwtbmFtZSwtZ2Fpbl9wZXJfZWN0cykgJT4lCiAgaW5uZXJfam9pbih0ZWFjaGluZyAlPiUgZGlzdGluY3QodW5pdGNvZGUscGFyZW50X3VuaXRjb2RlLG5hbWUsZ2Fpbl9wZXJfZWN0cykpICU+JSAKICBmaWx0ZXIocGFyZW50X3VuaXRjb2RlPT0iSDQwIikgJT4lCiAgbXV0YXRlKG5hbWU9ZmN0X3Jlb3JkZXIobmFtZSxlY3RzKmdhaW5fcGVyX2VjdHMpKSAlPiUKICBnZ3Bsb3QoYWVzKHg9bmFtZSx5PWVjdHMqZ2Fpbl9wZXJfZWN0cyxjb2xvcj1uYW1lPT0iTWFzdGVyJ3MgUHJvZ3JhbW1lIGluIExpbmd1aXN0aWMgRGl2ZXJzaXR5IGFuZCBEaWdpdGFsIEh1bWFuaXRpZXMiKSkgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHM9c2NhbGVzOjpudW1iZXJfZm9ybWF0KCkpICsKICBnZW9tX2JveHBsb3QoKSArIAogIGNvb3JkX2ZsaXAoeWxpbT1jKDAsMjUwMDApKSArCiAgeGxhYigiRmFjdWx0eSIpICsKICB5bGFiKCJFdXJvcyBnYWluZWQgcGVyIGNvdXJzZSBnaXZlbiIpICsKICB0aGVtZV9oc2NpKGJhc2VfZmFtaWx5PSJBcmlhbCIpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoImJsYWNrIiwicmVkIikpICsKICBndWlkZXMoY29sb3I9Im5vbmUiKQpgYGAKCiMjIEFuYWx5c2lzIHdpdGhpbiB0aGUgTWFzdGVyJ3MgUHJvZ3JhbW1lIGluIExpbmd1aXN0aWMgRGl2ZXJzaXR5IGFuZCBEaWdpdGFsIEh1bWFuaXRpZXMKCmBgYHtyfQp0ZWFjaGluZyAlPiUgCiAgZmlsdGVyKHN0cl9kZXRlY3QodW5pdGNvZGUsIl5MREEtIikpICU+JQogIG11dGF0ZSh1bml0Y29kZT1zdHJfc3ViKHVuaXRjb2RlLGVuZD01KSkgJT4lCiAgbXV0YXRlKHVuaXRjb2RlPWZjdF9yZWNvZGUodW5pdGNvZGUsIkNvbW1vbiI9IkxEQS0zIiwiTUEtVGhlc2lzIj0iTERBLTgiLCJEaWdpdGFsIGh1bWFuaXRpZXMiPSJMREEtSCIsIkNvZ25pdGl2ZSBzY2llbmNlIj0iTERBLUMiLCJMYW5ndWFnZSB0ZWNobm9sb2d5Ij0iTERBLVQiLCJQaG9uZXRpY3MiPSJMREEtUCIsIkxpbmd1aXN0aWNzIj0iTERBLUQiLCJMaW5ndWlzdGljcyI9IkxEQS1HIiwiTGluZ3Vpc3RpY3MiPSJMREEtTCIsIkNvbW1vbiI9IkxEQS1FIiwiQ29tbW9uIj0iTERBLU0iKSkgJT4lCiAgZ3JvdXBfYnkoeWVhcix1bml0Y29kZSkgJT4lCiAgc3VtbWFyaXNlKGdhaW49c3VtKGVjdHMqKGdhaW5zICU+JSBmaWx0ZXIodHlwZT09Ik1hc3RlcidzIikgJT4lIHB1bGwoZ2FpbikpLzEyMCksLmdyb3Vwcz0iZHJvcCIpICU+JQogIG11dGF0ZSh1bml0Y29kZT1mY3RfcmVvcmRlcih1bml0Y29kZSxnYWluKSkgJT4lCiAgZ2dwbG90KGFlcyh4PXVuaXRjb2RlLHk9Z2FpbikpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzPXNjYWxlczo6bnVtYmVyX2Zvcm1hdCgpKSArCiAgZ2VvbV9ib3hwbG90KCkgKyAKICBjb29yZF9mbGlwKCkgKwogIHhsYWIoIlRyYWNrIikgKwogIHlsYWIoIlRvdGFsIEV1cm9zIGdhaW5lZCBwZXIgeWVhciIpICsKICB0aGVtZV9oc2NpX2Rpc2NyZXRlKGJhc2VfZmFtaWx5PSJBcmlhbCIpCmBgYAoKYGBge3J9CnRlYWNoaW5nICU+JSAKICBmaWx0ZXIoc3RyX2RldGVjdCh1bml0Y29kZSwiXkxEQS0iKSkgJT4lCiAgbXV0YXRlKHVuaXRjb2RlPXN0cl9zdWIodW5pdGNvZGUsZW5kPTUpKSAlPiUKICBtdXRhdGUodW5pdGNvZGU9ZmN0X3JlY29kZSh1bml0Y29kZSwiQ29tbW9uIj0iTERBLTMiLCJNQS1UaGVzaXMiPSJMREEtOCIsIkRpZ2l0YWwgaHVtYW5pdGllcyI9IkxEQS1IIiwiQ29nbml0aXZlIHNjaWVuY2UiPSJMREEtQyIsIkxhbmd1YWdlIHRlY2hub2xvZ3kiPSJMREEtVCIsIlBob25ldGljcyI9IkxEQS1QIiwiTGluZ3Vpc3RpY3MiPSJMREEtRCIsIkxpbmd1aXN0aWNzIj0iTERBLUciLCJMaW5ndWlzdGljcyI9IkxEQS1MIiwiQ29tbW9uIj0iTERBLUUiLCJDb21tb24iPSJMREEtTSIpKSAlPiUKICBtdXRhdGUoZ2Fpbj1lY3RzKihnYWlucyAlPiUgZmlsdGVyKHR5cGU9PSJNYXN0ZXIncyIpICU+JSBwdWxsKGdhaW4pKS8xMjApICU+JQogIG11dGF0ZSh1bml0Y29kZT1mY3RfcmVvcmRlcih1bml0Y29kZSxnYWluKSkgJT4lCiAgZ2dwbG90KGFlcyh4PXVuaXRjb2RlLHk9Z2FpbikpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzPXNjYWxlczo6bnVtYmVyX2Zvcm1hdCgpKSArCiAgZ2VvbV9ib3hwbG90KCkgKyAKICBjb29yZF9mbGlwKCkgKwogIHhsYWIoIlRyYWNrIikgKwogIHlsYWIoIkV1cm9zIGdhaW5lZCBwZXIgY291cnNlIGdpdmVuIikgKwogIHRoZW1lX2hzY2lfZGlzY3JldGUoYmFzZV9mYW1pbHk9IkFyaWFsIikKYGBgCg==