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==