Introduction
library(here)
This document attempts a very crude estimate of COVID-19 R0 using confirmed cases as a proxy for true cases, leveraging data across multiple countries to support a weighted median estimate.
[1] "Last updated 2020-03-23"
Data on Count of Confirmed COVID-19 Cases
Data on confirmed case rate comes from 2019 Novel Coronavirus COVID-19 (2019-nCoV) Data Repository by Johns Hopkins CSSE.
library(tidyverse)
library(R0) # consider moving all library commands to top -- this one was in a loop below
confirmed <- read_csv(url("https://raw.githubusercontent.com/CSSEGISandData/COVID-19/master/csse_covid_19_data/csse_covid_19_time_series/time_series_19-covid-Confirmed.csv"))
deaths <- read_csv(url("https://raw.githubusercontent.com/CSSEGISandData/COVID-19/master/csse_covid_19_data/csse_covid_19_time_series/time_series_19-covid-Deaths.csv"))
recovered <- read_csv(url("https://raw.githubusercontent.com/CSSEGISandData/COVID-19/master/csse_covid_19_data/csse_covid_19_time_series/time_series_19-covid-Recovered.csv"))
confirmed_long <- pivot_longer(confirmed, names_to = "date", cols = ends_with("20"), values_to = "confirmed")
deaths_long <- pivot_longer(deaths, names_to = "date", cols = ends_with("20"), values_to = "deaths")
recovered_long <- pivot_longer(recovered, names_to = "date", cols = ends_with("20"), values_to = "recovered")
library(lubridate)
all_long <- deaths_long %>%
full_join(confirmed_long) %>%
full_join(recovered_long) %>%
filter(confirmed>0) %>%
mutate(date_asdate = mdy(str_replace(date,"20$","2020"))) %>%
rename(country=`Country/Region`, state=`Province/State`) %>%
#filter(!state %in% c("From Diamond Princess","Diamond Princess")) %>%
mutate(state=ifelse(is.na(state),"none",state)) %>%
mutate(country_state=paste0(country, "___", state) %>% as.factor() ) %>%
arrange(country_state, date_asdate) %>%
filter(confirmed>0) %>% #subset to only places/time periods that have had confirmed
group_by(country_state) %>%
mutate(days_since_1_confirmed=cumsum(confirmed>0)) %>%
mutate(confirmed_fd=confirmed-lag(confirmed)) %>%
ungroup() %>%
filter(days_since_1_confirmed>0)
padded <- all_long %>%
tidyr::expand(country_state, days_since_1_confirmed) %>%
separate(col=country_state, into=c('country','state'), sep = "___", remove = T)
all_long <- padded %>%
left_join(all_long) %>%
arrange(country, state, days_since_1_confirmed) %>%
mutate(country_state=paste0(country, "___", state) %>% as.factor() )
Begin study periods on the date of the first reported confirmed COVID-19 case.
library(DT)
all_long %>% dplyr::select(-country_state,-date_asdate) %>%
head(1000) %>% #first 1000 rows
datatable(rownames = FALSE,
options = list(
pageLength = 10,
columnDefs = list(list(className = 'dt-center', targets = "_all")
)
)
)
Population Data
Pull population data comes from wikidata.
fromscratch=F
if(fromscratch){
#We need to grab populations
places <- all_long %>% dplyr::select(country,state) %>% distinct() %>% mutate(search=paste0(state,", ", country )) %>% mutate(search= ifelse(is.na(state) | state=="none", country, state ) )
library(WikidataR)
wiki_searches <- list()
for(q in places$search){
print(q)
try({
#wiki <- find_item("Ramsey County")
wiki <- find_item(q)
temp <- as.data.frame(wiki[[1]])
temp$search <- q
wiki_searches[[q]] <- temp
})
}
wiki_searches_df <- bind_rows(wiki_searches)
item_df <- list()
for(q in wiki_searches_df$id){
print(q)
try({
item_df[[q]] <- get_item(id = q)
})
}
wiki <- find_item("New York")
latest_pop_list <- list()
for(q in names(item_df)){
print(q)
try({
latest_pop_list[[q]] <- data.frame(wikidata_id=q, P1082= rev(item_df[[q]][[1]]$claims$P1082$mainsnak$datavalue$value$amount)[1])
})
}
latest_pop_df <- places %>% left_join(wiki_searches_df %>% dplyr::select(search,wikidata_id=id), by=c('search'='search')) %>% left_join( bind_rows(latest_pop_list) ) %>%
mutate(population= str_replace(P1082,"\\+","") %>% as.numeric() ) %>%
mutate(population_log= log(population)) %>%
mutate(state=ifelse(is.na(state),"none",state)) %>% distinct() %>% arrange(country, state)
saveRDS(latest_pop_df,paste0(here::here(),"/data_in/latest_pop_df.RDS"))
}
latest_pop_df <- readRDS(paste0(here::here(), "/data_in/latest_pop_df.RDS"))
all_long_covariates <- all_long %>%
left_join(latest_pop_df) %>%
arrange(country, state, days_since_1_confirmed)
library(DT)
latest_pop_df %>% dplyr::select(-search, -population_log) %>%
datatable(rownames = FALSE,
options = list(
pageLength = 10,
columnDefs = list(list(className = 'dt-center', targets = "_all")
)
)
)
Estimating R0
Estimating with a log linear model
Experimenting with alternate estimation using epitrix packages.
https://www.repidemicsconsortium.org/epitrix/
library(epitrix) #install.packages("epitrix")
mu <- 3.96 #15.3 # mean in days days
sigma <- 4.75 #9.3 # standard deviation in days
cv <- sigma/mu # coefficient of variation
cv
param <- gamma_mucv2shapescale(mu, cv) # convertion to Gamma parameters
param
si <- distcrete::distcrete("gamma", interval = 1,
shape = param$shape,
scale = param$scale, w = 0)
si
set.seed(1)
x <- si$r(500)
head(x, 10)
hist(x, col = "grey", border = "white",
xlab = "Days between primary and secondary onset",
main = "Simulated serial intervals")
si_fit <- fit_disc_gamma(x)
si_fit
library(outbreaks) #install.packages('outbreaks')
library(incidence) #install.packages('incidence')
locations <- all_long_covariates$country_state
r_log_linear_list <- list()
for(q in locations %>% unique() ){
print(q)
try({
temp <- all_long_covariates %>% filter(country_state %in% q) %>% filter(!is.na(confirmed) )
temp$confirmed_fd[1]<-temp$confirmed[1]
incidents <- lapply(temp$days_since_1_confirmed[temp$confirmed_fd>0], FUN=function(x) { rep(temp$days_since_1_confirmed[x],temp$confirmed_fd[x]) } ) #print(x) ;
incidents <- unlist( incidents )
i <- incidence(incidents)
i
peak <- which(cummean(i$counts)==max(cummean(i$counts)))
f <- fit(i[1:peak]) #fit the curve to the peak mean incidents
plot(i, fit = f, color = "#9fc2fc")
r_log_linear=r2R0(f$info$r, si$d(1:100))
r_log_linear_list[[q]] <- data.frame(country=temp$country[1],state=temp$state[1], r_log_linear=round(r_log_linear,2 ))
})
}
r_log_linear_df <- bind_rows(r_log_linear_list)
Estimating with the R0 package
A critical assumption is the serial interval of the disease. Du et al. March 20, 2020 report a serial interval of 3.96 days with standard deviation of 4.75 days. Nishiura et al March 4, 2020 find a median of 4.0. WHO’s literature review reports an incubation period of 5-6 days and serial interval from 4.4 to 7.5 days. Many simulations go with the early January 29, 2020 estimate from Li et al. of a mean serial interval of 7.5 days.
#https://bmcmedinformdecismak.biomedcentral.com/articles/10.1186/1472-6947-12-147
#The generation time is the time lag between infection in a primary case and a secondary case. The generation time distribution should be obtained from the time lag between all infectee/infector pairs [8]. As it cannot be observed directly, it is often substituted with the serial interval distribution that measures time between symptoms onset. In our software package, the ‘generation.time’ function is used to represent a discretized generation time distribution. Discretization is carried out on the grid [0,0.5), [0.5, 1.5), [1.5, 2.5), etc.… where the unit is a user chosen time interval (hour, day, week…). Several descriptions are supported: “empirical” requiring the full specification of the distribution, or parametric distributions taken among “gamma”, “lognormal” or “weibull”. In the latter case, the mean and standard deviation must be provided in the desired time units.
library(R0)
mGT<-generation.time("gamma",
#Du et al. "The reported serial intervals range from -11 days to 20 days, with a mean of 3.96 days (95% confidence interval: 3.53-4.39), a standard deviation of 4.75 days (95% confidence interval: 4.46-5.07), "
val=c(
3.96,#Serial interval mean
4.75 #Serial interval standard deviation
) #"val Vector of values used for the empirical distribution, or c(mean, sd) if parametric."
)
R0 estimates generated with the R package R0
“The R0 package: a toolbox to estimate reproduction numbers for epidemic outbreaks” Obadia et al. 2012
Summary
Simple log linear model
weights <- table(r_log_linear_df$country)
weights <- weights[r_log_linear_df$country]
weights <- 1/(weights/max(weights))
library(spatstat) #install.packages('spatstat')
print(results_log_linear <- r_log_linear_df %>% filter(!is.na(pop)) %>% pull(r_log_linear) %>% weighted.quantile(w=weights)) #weight by country, exclude obs with no population data.
0% 25% 50% 75% 100%
0.91 1.48 1.64 1.85 5.42
Quantiles of R0 across locations downweighting multiple observations from countries that are dissagregated arbitarily by the data source. A median of R0=2.56 .
weights <- table(r_R0_df$country)
weights <- weights[r_R0_df$country]
weights <- 1/(weights/max(weights))
library(spatstat) #install.packages('spatstat')
print(results_R0 <- r_R0_df %>% filter(!is.na(pop)) %>% pull(r_ml) %>% weighted.quantile(w=weights)) #weight by country, exclude obs with no population data.
0% 25% 50% 75% 100%
1.250000 2.125815 2.560000 3.457194 31.620000
Distribution of R0 across locations.
r_estimates_all <- r_log_linear_df %>% full_join(r_R0_df)
r_estimates_all %>%
filter(!is.na(pop)) %>%
ggplot() +
geom_density(aes(x=r_log_linear), col="lightblue") + geom_vline(xintercept = results_log_linear[3], stat = 'vline', linetype = "dashed", col="blue") +
geom_density(aes(x=r_ml), col="pink") + geom_vline(xintercept = results_R0[3], stat = 'vline', linetype = "dashed", col="red") +
ggtitle("Distribution R0 Estimates Across Countries") +
theme_bw() +
scale_x_continuous(breaks=seq(0, 30, by = 1)) +
annotate("text", x = 7, y = .75, label = "Median R0_log_linear = 1.64", color="blue") +
annotate("text", x = 7, y = .5, label = "Median R0_ML = 2.56", color="red") +
xlab("R0 (maximum likelihood estimate)")
Raw Estimates
library(DT)
r_estimates_all %>%
mutate(r_ml= round(r_ml,2)) %>%
datatable(rownames = FALSE,
options = list(
pageLength = 50,
columnDefs = list(list(className = 'dt-center', targets = "_all")
)
)
)
NA
LS0tCnRpdGxlOiAiQ09WSUQtMTkgUjAgRXN0aW1hdGVzIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogeWVzCmRhdGU6ICIzLzIzLzIwMjAiCmF1dGhvcjogIlJleCBXLiBEb3VnbGFzcyIKLS0tCgojIEludHJvZHVjdGlvbgoKYGBge3IsIGNhY2hlPUYsIGluY2x1ZGUgPSBUfQpsaWJyYXJ5KGhlcmUpCmBgYAoKClRoaXMgZG9jdW1lbnQgYXR0ZW1wdHMgYSB2ZXJ5IGNydWRlIGVzdGltYXRlIG9mIENPVklELTE5IFIwIHVzaW5nIGNvbmZpcm1lZCBjYXNlcyBhcyBhIHByb3h5IGZvciB0cnVlIGNhc2VzLCBsZXZlcmFnaW5nIGRhdGEgYWNyb3NzIG11bHRpcGxlIGNvdW50cmllcyB0byBzdXBwb3J0IGEgd2VpZ2h0ZWQgbWVkaWFuIGVzdGltYXRlLgoKYGBge3IsIGVjaG89RiwgY2FjaGU9RiwgaW5jbHVkZSA9IFR9CnByaW50KHBhc3RlKCJMYXN0IHVwZGF0ZWQgIiwgU3lzLkRhdGUoKSkpCmBgYAoKIyMgRGF0YSBvbiBDb3VudCBvZiBDb25maXJtZWQgQ09WSUQtMTkgQ2FzZXMKCkRhdGEgb24gY29uZmlybWVkIGNhc2UgcmF0ZSBjb21lcyBmcm9tIFsyMDE5IE5vdmVsIENvcm9uYXZpcnVzIENPVklELTE5ICgyMDE5LW5Db1YpIERhdGEgUmVwb3NpdG9yeSBieSBKb2hucyBIb3BraW5zIENTU0VdKGh0dHBzOi8vZ2l0aHViLmNvbS9DU1NFR0lTYW5kRGF0YS9DT1ZJRC0xOSkuCgpgYGB7ciwgZWNobz1ULCBtZXNzYWdlPUZBTFNFLCByZXN1bHRzID0gRkFMU0UsIHdhcm5pbmc9RkFMU0V9CgpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShSMCkgICMgY29uc2lkZXIgbW92aW5nIGFsbCBsaWJyYXJ5IGNvbW1hbmRzIHRvIHRvcCAtLSB0aGlzIG9uZSB3YXMgaW4gYSBsb29wIGJlbG93CmNvbmZpcm1lZCA8LSByZWFkX2Nzdih1cmwoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9DU1NFR0lTYW5kRGF0YS9DT1ZJRC0xOS9tYXN0ZXIvY3NzZV9jb3ZpZF8xOV9kYXRhL2Nzc2VfY292aWRfMTlfdGltZV9zZXJpZXMvdGltZV9zZXJpZXNfMTktY292aWQtQ29uZmlybWVkLmNzdiIpKQpkZWF0aHMgPC0gcmVhZF9jc3YodXJsKCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vQ1NTRUdJU2FuZERhdGEvQ09WSUQtMTkvbWFzdGVyL2Nzc2VfY292aWRfMTlfZGF0YS9jc3NlX2NvdmlkXzE5X3RpbWVfc2VyaWVzL3RpbWVfc2VyaWVzXzE5LWNvdmlkLURlYXRocy5jc3YiKSkKcmVjb3ZlcmVkIDwtIHJlYWRfY3N2KHVybCgiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL0NTU0VHSVNhbmREYXRhL0NPVklELTE5L21hc3Rlci9jc3NlX2NvdmlkXzE5X2RhdGEvY3NzZV9jb3ZpZF8xOV90aW1lX3Nlcmllcy90aW1lX3Nlcmllc18xOS1jb3ZpZC1SZWNvdmVyZWQuY3N2IikpCgpjb25maXJtZWRfbG9uZyA8LSBwaXZvdF9sb25nZXIoY29uZmlybWVkLCBuYW1lc190byA9ICJkYXRlIiwgY29scyA9IGVuZHNfd2l0aCgiMjAiKSwgdmFsdWVzX3RvID0gImNvbmZpcm1lZCIpCmRlYXRoc19sb25nIDwtIHBpdm90X2xvbmdlcihkZWF0aHMsIG5hbWVzX3RvID0gImRhdGUiLCBjb2xzID0gZW5kc193aXRoKCIyMCIpLCB2YWx1ZXNfdG8gPSAiZGVhdGhzIikKcmVjb3ZlcmVkX2xvbmcgPC0gcGl2b3RfbG9uZ2VyKHJlY292ZXJlZCwgbmFtZXNfdG8gPSAiZGF0ZSIsIGNvbHMgPSBlbmRzX3dpdGgoIjIwIiksIHZhbHVlc190byA9ICJyZWNvdmVyZWQiKQoKbGlicmFyeShsdWJyaWRhdGUpCgphbGxfbG9uZyA8LSBkZWF0aHNfbG9uZyAlPiUgCiAgZnVsbF9qb2luKGNvbmZpcm1lZF9sb25nKSAlPiUgCiAgZnVsbF9qb2luKHJlY292ZXJlZF9sb25nKSAlPiUKICBmaWx0ZXIoY29uZmlybWVkPjApICU+JSAKICAKICBtdXRhdGUoZGF0ZV9hc2RhdGUgPSBtZHkoc3RyX3JlcGxhY2UoZGF0ZSwiMjAkIiwiMjAyMCIpKSkgJT4lIAogIHJlbmFtZShjb3VudHJ5PWBDb3VudHJ5L1JlZ2lvbmAsIHN0YXRlPWBQcm92aW5jZS9TdGF0ZWApICU+JQogICNmaWx0ZXIoIXN0YXRlICVpbiUgYygiRnJvbSBEaWFtb25kIFByaW5jZXNzIiwiRGlhbW9uZCBQcmluY2VzcyIpKSAlPiUKICBtdXRhdGUoc3RhdGU9aWZlbHNlKGlzLm5hKHN0YXRlKSwibm9uZSIsc3RhdGUpKSAlPiUgICAKICBtdXRhdGUoY291bnRyeV9zdGF0ZT1wYXN0ZTAoY291bnRyeSwgIl9fXyIsIHN0YXRlKSAlPiUgYXMuZmFjdG9yKCkgICkgJT4lCiAgCiAgYXJyYW5nZShjb3VudHJ5X3N0YXRlLCBkYXRlX2FzZGF0ZSkgJT4lCiAgZmlsdGVyKGNvbmZpcm1lZD4wKSAlPiUgI3N1YnNldCB0byBvbmx5IHBsYWNlcy90aW1lIHBlcmlvZHMgdGhhdCBoYXZlIGhhZCBjb25maXJtZWQKICBncm91cF9ieShjb3VudHJ5X3N0YXRlKSAlPiUKICBtdXRhdGUoZGF5c19zaW5jZV8xX2NvbmZpcm1lZD1jdW1zdW0oY29uZmlybWVkPjApKSAlPiUKICBtdXRhdGUoY29uZmlybWVkX2ZkPWNvbmZpcm1lZC1sYWcoY29uZmlybWVkKSkgJT4lCgogIHVuZ3JvdXAoKSAlPiUKICBmaWx0ZXIoZGF5c19zaW5jZV8xX2NvbmZpcm1lZD4wKSAKICAgIApwYWRkZWQgPC0gYWxsX2xvbmcgJT4lCiAgICAgICAgICB0aWR5cjo6ZXhwYW5kKGNvdW50cnlfc3RhdGUsIGRheXNfc2luY2VfMV9jb25maXJtZWQpICU+JSAKICAgICAgICAgIHNlcGFyYXRlKGNvbD1jb3VudHJ5X3N0YXRlLCBpbnRvPWMoJ2NvdW50cnknLCdzdGF0ZScpLCBzZXAgPSAiX19fIiwgcmVtb3ZlID0gVCkgCgphbGxfbG9uZyA8LSBwYWRkZWQgJT4lIAogICAgICAgICAgICBsZWZ0X2pvaW4oYWxsX2xvbmcpICU+JSAKICAgICAgICAgICAgYXJyYW5nZShjb3VudHJ5LCBzdGF0ZSwgZGF5c19zaW5jZV8xX2NvbmZpcm1lZCkgJT4lCiAgICAgICAgICAgIG11dGF0ZShjb3VudHJ5X3N0YXRlPXBhc3RlMChjb3VudHJ5LCAiX19fIiwgc3RhdGUpICU+JSBhcy5mYWN0b3IoKSAgKQpgYGAKCgpCZWdpbiBzdHVkeSBwZXJpb2RzIG9uIHRoZSBkYXRlIG9mIHRoZSBmaXJzdCByZXBvcnRlZCBjb25maXJtZWQgQ09WSUQtMTkgY2FzZS4KCmBgYHtyfQpsaWJyYXJ5KERUKQphbGxfbG9uZyAlPiUgZHBseXI6OnNlbGVjdCgtY291bnRyeV9zdGF0ZSwtZGF0ZV9hc2RhdGUpICU+JQogIGhlYWQoMTAwMCkgJT4lICNmaXJzdCAxMDAwIHJvd3MKZGF0YXRhYmxlKHJvd25hbWVzID0gRkFMU0UsCiAgICAgICAgICBvcHRpb25zID0gbGlzdCgKICAgICAgICAgICAgcGFnZUxlbmd0aCA9IDEwLAogICAgICAgICAgICBjb2x1bW5EZWZzID0gbGlzdChsaXN0KGNsYXNzTmFtZSA9ICdkdC1jZW50ZXInLCB0YXJnZXRzID0gIl9hbGwiKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICkKKQpgYGAKCiMjIFBvcHVsYXRpb24gRGF0YQoKUHVsbCBwb3B1bGF0aW9uIGRhdGEgY29tZXMgZnJvbSB3aWtpZGF0YS4KCmBgYHtyLCBlY2hvPVQsIG1lc3NhZ2U9RkFMU0UsIHJlc3VsdHMgPSBGQUxTRSwgd2FybmluZz1GQUxTRX0KZnJvbXNjcmF0Y2g9RgppZihmcm9tc2NyYXRjaCl7CiAgI1dlIG5lZWQgdG8gZ3JhYiBwb3B1bGF0aW9ucwogIHBsYWNlcyA8LSBhbGxfbG9uZyAlPiUgZHBseXI6OnNlbGVjdChjb3VudHJ5LHN0YXRlKSAlPiUgZGlzdGluY3QoKSAlPiUgbXV0YXRlKHNlYXJjaD1wYXN0ZTAoc3RhdGUsIiwgIiwgY291bnRyeSApKSAgJT4lIG11dGF0ZShzZWFyY2g9IGlmZWxzZShpcy5uYShzdGF0ZSkgfCBzdGF0ZT09Im5vbmUiLCBjb3VudHJ5LCBzdGF0ZSApICkKICBsaWJyYXJ5KFdpa2lkYXRhUikKICB3aWtpX3NlYXJjaGVzIDwtIGxpc3QoKQogIGZvcihxIGluIHBsYWNlcyRzZWFyY2gpewogICAgcHJpbnQocSkKICAgIHRyeSh7CiAgICAgICN3aWtpIDwtIGZpbmRfaXRlbSgiUmFtc2V5IENvdW50eSIpCiAgICAgIHdpa2kgPC0gZmluZF9pdGVtKHEpCiAgICAgIHRlbXAgPC0gYXMuZGF0YS5mcmFtZSh3aWtpW1sxXV0pCiAgICAgIHRlbXAkc2VhcmNoIDwtIHEKICAgICAgd2lraV9zZWFyY2hlc1tbcV1dIDwtIHRlbXAKICAgIH0pCiAgfQogIHdpa2lfc2VhcmNoZXNfZGYgPC0gYmluZF9yb3dzKHdpa2lfc2VhcmNoZXMpCiAgCiAgaXRlbV9kZiA8LSBsaXN0KCkKICBmb3IocSBpbiB3aWtpX3NlYXJjaGVzX2RmJGlkKXsKICAgIHByaW50KHEpCiAgICB0cnkoewogICAgICBpdGVtX2RmW1txXV0gPC0gZ2V0X2l0ZW0oaWQgPSBxKQogICAgfSkKICB9CiAgd2lraSA8LSBmaW5kX2l0ZW0oIk5ldyBZb3JrIikKICAKICBsYXRlc3RfcG9wX2xpc3QgPC0gbGlzdCgpCiAgZm9yKHEgaW4gbmFtZXMoaXRlbV9kZikpewogICAgcHJpbnQocSkKICAgIHRyeSh7CiAgICAgIGxhdGVzdF9wb3BfbGlzdFtbcV1dIDwtIGRhdGEuZnJhbWUod2lraWRhdGFfaWQ9cSwgUDEwODI9IHJldihpdGVtX2RmW1txXV1bWzFdXSRjbGFpbXMkUDEwODIkbWFpbnNuYWskZGF0YXZhbHVlJHZhbHVlJGFtb3VudClbMV0pCiAgICB9KQogIH0KICBsYXRlc3RfcG9wX2RmIDwtIHBsYWNlcyAlPiUgbGVmdF9qb2luKHdpa2lfc2VhcmNoZXNfZGYgJT4lIGRwbHlyOjpzZWxlY3Qoc2VhcmNoLHdpa2lkYXRhX2lkPWlkKSwgYnk9Yygnc2VhcmNoJz0nc2VhcmNoJykpICU+JSBsZWZ0X2pvaW4oIGJpbmRfcm93cyhsYXRlc3RfcG9wX2xpc3QpICkgJT4lIAogICAgbXV0YXRlKHBvcHVsYXRpb249IHN0cl9yZXBsYWNlKFAxMDgyLCJcXCsiLCIiKSAlPiUgYXMubnVtZXJpYygpICAgICkgJT4lCiAgICBtdXRhdGUocG9wdWxhdGlvbl9sb2c9IGxvZyhwb3B1bGF0aW9uKSkgJT4lCiAgICBtdXRhdGUoc3RhdGU9aWZlbHNlKGlzLm5hKHN0YXRlKSwibm9uZSIsc3RhdGUpKSAlPiUgZGlzdGluY3QoKSAlPiUgYXJyYW5nZShjb3VudHJ5LCBzdGF0ZSkgCiAgCiAgc2F2ZVJEUyhsYXRlc3RfcG9wX2RmLHBhc3RlMChoZXJlOjpoZXJlKCksIi9kYXRhX2luL2xhdGVzdF9wb3BfZGYuUkRTIikpCgp9CgpsYXRlc3RfcG9wX2RmIDwtIHJlYWRSRFMocGFzdGUwKGhlcmU6OmhlcmUoKSwgIi9kYXRhX2luL2xhdGVzdF9wb3BfZGYuUkRTIikpCgphbGxfbG9uZ19jb3ZhcmlhdGVzIDwtIGFsbF9sb25nICU+JSAKICAgICAgICAgICAgICAgICAgICAgICBsZWZ0X2pvaW4obGF0ZXN0X3BvcF9kZikgJT4lIAogICAgICAgICAgICAgICAgICAgICAgIGFycmFuZ2UoY291bnRyeSwgc3RhdGUsIGRheXNfc2luY2VfMV9jb25maXJtZWQpIApgYGAKCmBgYHtyfQpsaWJyYXJ5KERUKQpsYXRlc3RfcG9wX2RmICU+JSBkcGx5cjo6c2VsZWN0KC1zZWFyY2gsIC1wb3B1bGF0aW9uX2xvZykgJT4lCmRhdGF0YWJsZShyb3duYW1lcyA9IEZBTFNFLAogICAgICAgICAgb3B0aW9ucyA9IGxpc3QoCiAgICAgICAgICAgIHBhZ2VMZW5ndGggPSAxMCwKICAgICAgICAgICAgY29sdW1uRGVmcyA9IGxpc3QobGlzdChjbGFzc05hbWUgPSAnZHQtY2VudGVyJywgdGFyZ2V0cyA9ICJfYWxsIikKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICApCikKYGBgCgoKIyBFc3RpbWF0aW5nIFIwCgojIyBFc3RpbWF0aW5nIHdpdGggYSBsb2cgbGluZWFyIG1vZGVsCgpFeHBlcmltZW50aW5nIHdpdGggYWx0ZXJuYXRlIGVzdGltYXRpb24gdXNpbmcgZXBpdHJpeCAgcGFja2FnZXMuCgpodHRwczovL3d3dy5yZXBpZGVtaWNzY29uc29ydGl1bS5vcmcvZXBpdHJpeC8KCmBgYHtyLCBlY2hvPVQsIG1lc3NhZ2U9RkFMU0UsIHJlc3VsdHMgPSBGQUxTRSwgd2FybmluZz1GQUxTRX0KCmxpYnJhcnkoZXBpdHJpeCkgI2luc3RhbGwucGFja2FnZXMoImVwaXRyaXgiKQoKbXUgPC0gMy45NiAjMTUuMyAjIG1lYW4gaW4gZGF5cyBkYXlzCnNpZ21hIDwtIDQuNzUgIzkuMyAjIHN0YW5kYXJkIGRldmlhdGlvbiBpbiBkYXlzCmN2IDwtIHNpZ21hL211ICMgY29lZmZpY2llbnQgb2YgdmFyaWF0aW9uCmN2CgpwYXJhbSA8LSBnYW1tYV9tdWN2MnNoYXBlc2NhbGUobXUsIGN2KSAjIGNvbnZlcnRpb24gdG8gR2FtbWEgcGFyYW1ldGVycwpwYXJhbQoKc2kgPC0gZGlzdGNyZXRlOjpkaXN0Y3JldGUoImdhbW1hIiwgaW50ZXJ2YWwgPSAxLAogICAgICAgICAgICAgICBzaGFwZSA9IHBhcmFtJHNoYXBlLAogICAgICAgICAgICAgICBzY2FsZSA9IHBhcmFtJHNjYWxlLCB3ID0gMCkKc2kKCnNldC5zZWVkKDEpCnggPC0gc2kkcig1MDApCmhlYWQoeCwgMTApCgpoaXN0KHgsIGNvbCA9ICJncmV5IiwgYm9yZGVyID0gIndoaXRlIiwKICAgICB4bGFiID0gIkRheXMgYmV0d2VlbiBwcmltYXJ5IGFuZCBzZWNvbmRhcnkgb25zZXQiLAogICAgIG1haW4gPSAiU2ltdWxhdGVkIHNlcmlhbCBpbnRlcnZhbHMiKQoKc2lfZml0IDwtIGZpdF9kaXNjX2dhbW1hKHgpCnNpX2ZpdAoKbGlicmFyeShvdXRicmVha3MpICNpbnN0YWxsLnBhY2thZ2VzKCdvdXRicmVha3MnKQpsaWJyYXJ5KGluY2lkZW5jZSkgI2luc3RhbGwucGFja2FnZXMoJ2luY2lkZW5jZScpCgpsb2NhdGlvbnMgPC0gYWxsX2xvbmdfY292YXJpYXRlcyRjb3VudHJ5X3N0YXRlCnJfbG9nX2xpbmVhcl9saXN0IDwtIGxpc3QoKQpmb3IocSBpbiBsb2NhdGlvbnMgJT4lIHVuaXF1ZSgpICl7CiAgcHJpbnQocSkKICB0cnkoewogICAgdGVtcCA8LSBhbGxfbG9uZ19jb3ZhcmlhdGVzICU+JSBmaWx0ZXIoY291bnRyeV9zdGF0ZSAlaW4lIHEpICU+JSBmaWx0ZXIoIWlzLm5hKGNvbmZpcm1lZCkgKQogICAgdGVtcCRjb25maXJtZWRfZmRbMV08LXRlbXAkY29uZmlybWVkWzFdCgogICAgaW5jaWRlbnRzIDwtIGxhcHBseSh0ZW1wJGRheXNfc2luY2VfMV9jb25maXJtZWRbdGVtcCRjb25maXJtZWRfZmQ+MF0sIEZVTj1mdW5jdGlvbih4KSB7IHJlcCh0ZW1wJGRheXNfc2luY2VfMV9jb25maXJtZWRbeF0sdGVtcCRjb25maXJtZWRfZmRbeF0pIH0gKSAjcHJpbnQoeCkgOwogICAgaW5jaWRlbnRzIDwtIHVubGlzdCggaW5jaWRlbnRzICkKICAgIGkgPC0gaW5jaWRlbmNlKGluY2lkZW50cykKICAgIGkKCiAgICBwZWFrIDwtIHdoaWNoKGN1bW1lYW4oaSRjb3VudHMpPT1tYXgoY3VtbWVhbihpJGNvdW50cykpKQogICAgCiAgICBmIDwtIGZpdChpWzE6cGVha10pICAjZml0IHRoZSBjdXJ2ZSB0byB0aGUgcGVhayBtZWFuIGluY2lkZW50cwogICAgcGxvdChpLCBmaXQgPSBmLCBjb2xvciA9ICIjOWZjMmZjIikKICAgIAogICAgcl9sb2dfbGluZWFyPXIyUjAoZiRpbmZvJHIsIHNpJGQoMToxMDApKQogICAgCiAgICByX2xvZ19saW5lYXJfbGlzdFtbcV1dIDwtIGRhdGEuZnJhbWUoY291bnRyeT10ZW1wJGNvdW50cnlbMV0sc3RhdGU9dGVtcCRzdGF0ZVsxXSwgcl9sb2dfbGluZWFyPXJvdW5kKHJfbG9nX2xpbmVhciwyICkpCiAgfSkKfQpyX2xvZ19saW5lYXJfZGYgPC0gYmluZF9yb3dzKHJfbG9nX2xpbmVhcl9saXN0KQoKYGBgCgojIyBFc3RpbWF0aW5nIHdpdGggdGhlIFIwIHBhY2thZ2UKCkEgY3JpdGljYWwgYXNzdW1wdGlvbiBpcyB0aGUgc2VyaWFsIGludGVydmFsIG9mIHRoZSBkaXNlYXNlLiBbRHUgZXQgYWwuIE1hcmNoIDIwLCAyMDIwXShodHRwczovL3d3dy5tZWRyeGl2Lm9yZy9jb250ZW50LzEwLjExMDEvMjAyMC4wMi4xOS4yMDAyNTQ1MnY0KSByZXBvcnQgYSBzZXJpYWwgaW50ZXJ2YWwgb2YgMy45NiBkYXlzIHdpdGggc3RhbmRhcmQgZGV2aWF0aW9uIG9mIDQuNzUgZGF5cy4gTmlzaGl1cmEgZXQgYWwgTWFyY2ggNCwgMjAyMCBmaW5kIGEgbWVkaWFuIG9mIDQuMC4gW1dITydzIGxpdGVyYXR1cmUgcmV2aWV3XShodHRwczovL3d3dy53aG8uaW50L2RvY3MvZGVmYXVsdC1zb3VyY2UvY29yb25hdmlydXNlL3NpdHVhdGlvbi1yZXBvcnRzLzIwMjAwMjE5LXNpdHJlcC0zMC1jb3ZpZC0xOS5wZGY/c2Z2cnNuPTMzNDZiMDRmXzIpIHJlcG9ydHMgYW4gaW5jdWJhdGlvbiBwZXJpb2Qgb2YgNS02IGRheXMgYW5kIHNlcmlhbCBpbnRlcnZhbCBmcm9tIDQuNCB0byA3LjUgZGF5cy4gTWFueSBzaW11bGF0aW9ucyBnbyB3aXRoIHRoZSBlYXJseSBKYW51YXJ5IDI5LCAyMDIwIGVzdGltYXRlIGZyb20gW0xpIGV0IGFsLl0oaHR0cHM6Ly93d3cubmVqbS5vcmcvZG9pLzEwLjEwNTYvTkVKTW9hMjAwMTMxNikgb2YgYSBtZWFuIHNlcmlhbCBpbnRlcnZhbCBvZiA3LjUgZGF5cy4KCmBgYHtyfQojaHR0cHM6Ly9ibWNtZWRpbmZvcm1kZWNpc21hay5iaW9tZWRjZW50cmFsLmNvbS9hcnRpY2xlcy8xMC4xMTg2LzE0NzItNjk0Ny0xMi0xNDcKI1RoZSBnZW5lcmF0aW9uIHRpbWUgaXMgdGhlIHRpbWUgbGFnIGJldHdlZW4gaW5mZWN0aW9uIGluIGEgcHJpbWFyeSBjYXNlIGFuZCBhIHNlY29uZGFyeSBjYXNlLiBUaGUgZ2VuZXJhdGlvbiB0aW1lIGRpc3RyaWJ1dGlvbiBzaG91bGQgYmUgb2J0YWluZWQgZnJvbSB0aGUgdGltZSBsYWcgYmV0d2VlbiBhbGwgaW5mZWN0ZWUvaW5mZWN0b3IgcGFpcnMgWzhdLiBBcyBpdCBjYW5ub3QgYmUgb2JzZXJ2ZWQgZGlyZWN0bHksIGl0IGlzIG9mdGVuIHN1YnN0aXR1dGVkIHdpdGggdGhlIHNlcmlhbCBpbnRlcnZhbCBkaXN0cmlidXRpb24gdGhhdCBtZWFzdXJlcyB0aW1lIGJldHdlZW4gc3ltcHRvbXMgb25zZXQuIEluIG91ciBzb2Z0d2FyZSBwYWNrYWdlLCB0aGUg4oCYZ2VuZXJhdGlvbi50aW1l4oCZIGZ1bmN0aW9uIGlzIHVzZWQgdG8gcmVwcmVzZW50IGEgZGlzY3JldGl6ZWQgZ2VuZXJhdGlvbiB0aW1lIGRpc3RyaWJ1dGlvbi4gRGlzY3JldGl6YXRpb24gaXMgY2FycmllZCBvdXQgb24gdGhlIGdyaWQgWzAsMC41KSwgWzAuNSwgMS41KSwgWzEuNSwgMi41KSwgZXRjLuKApiB3aGVyZSB0aGUgdW5pdCBpcyBhIHVzZXIgY2hvc2VuIHRpbWUgaW50ZXJ2YWwgKGhvdXIsIGRheSwgd2Vla+KApikuIFNldmVyYWwgZGVzY3JpcHRpb25zIGFyZSBzdXBwb3J0ZWQ6IOKAnGVtcGlyaWNhbOKAnSByZXF1aXJpbmcgdGhlIGZ1bGwgc3BlY2lmaWNhdGlvbiBvZiB0aGUgZGlzdHJpYnV0aW9uLCBvciBwYXJhbWV0cmljIGRpc3RyaWJ1dGlvbnMgdGFrZW4gYW1vbmcg4oCcZ2FtbWHigJ0sIOKAnGxvZ25vcm1hbOKAnSBvciDigJx3ZWlidWxs4oCdLiBJbiB0aGUgbGF0dGVyIGNhc2UsIHRoZSBtZWFuIGFuZCBzdGFuZGFyZCBkZXZpYXRpb24gbXVzdCBiZSBwcm92aWRlZCBpbiB0aGUgZGVzaXJlZCB0aW1lIHVuaXRzLgpsaWJyYXJ5KFIwKQptR1Q8LWdlbmVyYXRpb24udGltZSgiZ2FtbWEiLCAKICAgICAgICAgICAgICAgICAgICAgI0R1IGV0IGFsLiAiVGhlIHJlcG9ydGVkIHNlcmlhbCBpbnRlcnZhbHMgcmFuZ2UgZnJvbSAtMTEgZGF5cyB0byAyMCBkYXlzLCB3aXRoIGEgbWVhbiBvZiAzLjk2IGRheXMgKDk1JSBjb25maWRlbmNlIGludGVydmFsOiAzLjUzLTQuMzkpLCBhIHN0YW5kYXJkIGRldmlhdGlvbiBvZiA0Ljc1IGRheXMgKDk1JSBjb25maWRlbmNlIGludGVydmFsOiA0LjQ2LTUuMDcpLCAiCiAgICAgICAgICAgICAgICAgICAgIHZhbD1jKAogICAgICAgICAgICAgICAgICAgICAgIDMuOTYsI1NlcmlhbCBpbnRlcnZhbCBtZWFuCiAgICAgICAgICAgICAgICAgICAgICAgNC43NSAjU2VyaWFsIGludGVydmFsIHN0YW5kYXJkIGRldmlhdGlvbgogICAgICAgICAgICAgICAgICAgICAgICkgIyJ2YWwJVmVjdG9yIG9mIHZhbHVlcyB1c2VkIGZvciB0aGUgZW1waXJpY2FsIGRpc3RyaWJ1dGlvbiwgb3IgYyhtZWFuLCBzZCkgaWYgcGFyYW1ldHJpYy4iCiAgICAgICAgICAgICAgICAgICAgICkKCmBgYAoKUjAgZXN0aW1hdGVzIGdlbmVyYXRlZCB3aXRoIHRoZSBSIHBhY2thZ2UgW1IwXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvUjAvaW5kZXguaHRtbCkKCiJUaGUgUjAgcGFja2FnZTogYSB0b29sYm94IHRvIGVzdGltYXRlIHJlcHJvZHVjdGlvbiBudW1iZXJzIGZvciBlcGlkZW1pYyBvdXRicmVha3MiIFtPYmFkaWEgZXQgYWwuIDIwMTJdKGh0dHBzOi8vYm1jbWVkaW5mb3JtZGVjaXNtYWsuYmlvbWVkY2VudHJhbC5jb20vYXJ0aWNsZXMvMTAuMTE4Ni8xNDcyLTY5NDctMTItMTQ3KQoKYGBge3IsIGVjaG89VCwgbWVzc2FnZT1GQUxTRSwgcmVzdWx0cyA9IEZBTFNFLCB3YXJuaW5nPUZBTFNFfQoKI05vdGUgbWFueSBvZiB0aGVzZSB3aWxsIHRocm93IGVycm9ycyBmb3IgbGFjayBvZiBkYXRhCgpsb2NhdGlvbnMgPC0gYWxsX2xvbmdfY292YXJpYXRlcyRjb3VudHJ5X3N0YXRlCnJfUjBfbGlzdCA8LSBsaXN0KCkKZm9yKHEgaW4gbG9jYXRpb25zICU+JSB1bmlxdWUoKSApewogIHByaW50KHEpCiAgdHJ5KHsKICAgIHRlbXAgPC0gYWxsX2xvbmdfY292YXJpYXRlcyAlPiUgZmlsdGVyKGNvdW50cnlfc3RhdGUgJWluJSBxKSAlPiUgZmlsdGVyKCFpcy5uYShjb25maXJtZWQpKQogICAgdGVtcCRjb25maXJtZWRfZmRbMV08LXRlbXAkY29uZmlybWVkWzFdCiAgICAKCiAgICBpbmNpZGVudHMgPC0gYXMudmVjdG9yKHRlbXAkY29uZmlybWVkX2ZkICkKICAgIHBvcCA8LSB0ZW1wJHBvcHVsYXRpb25bMV0KCiAgICBlc3RSMDwtZXN0LlIwLk1MKAogICAgICAgICAgZXBpZD1pbmNpZGVudHMsIAogICAgICAgICAgR1Q9bUdULCAKICAgICAgICAgIGJlZ2luPTEsCiAgICAgICAgICAjZW5kPWxlbmd0aChpbmNpZGVudHMpLCAKICAgICAgICAgIG1ldGhvZHM9Ik1MIiwgI2MoIkVHIiwgIk1MIiwgIlREIiwgIkFSIiwgIlNCIiksCiAgICAgICAgICBwb3Auc2l6ZT1wb3AsICNQb3B1bGF0aW9uIHNpemUgaW4gd2hpY2ggdGhlIGluY2lkZW50IGNhc2VzIHdlcmUgb2JzZXJ2ZWQuIFNlZSBtb3JlIGRldGFpbHMgaW4gZXN0LlIwLkFSIGRvY3VtZW50YXRpb24KICAgICAgICAgIGltcHV0ZS52YWx1ZXM9RiAjaW5jaWRlbnRzWzFdPjEwICNpZiB0aGUgZGF0YSBzdGFydCB3aXRoIG1vcmUgdGhhbiAxMCBjb25maXJtZWQgY2FzZXMgaW1tZWRpYXRlbHkgdGhlbiBhc3N1bWUgbGVmdCBjZW5zb3IgYW5kIGltcHV0ZSAjaW1wdXRhdGlvbiBpcyByZWFsbHkgc2xvdwogICAgKQogICAgI2F0dHJpYnV0ZXMoZXN0UjApCiAgICBlc3RSMAogICAgcl9SMF9saXN0W1txXV0gPC0gZGF0YS5mcmFtZShjb3VudHJ5PXRlbXAkY291bnRyeVsxXSxzdGF0ZT10ZW1wJHN0YXRlWzFdLCByX21sPXJvdW5kKGVzdFIwJFIsMiksIHBvcD1wb3AsIFIyPSByb3VuZChlc3RSMCRSc3F1YXJlZCwyKSkKICB9KQp9CnJfUjBfZGYgPC0gYmluZF9yb3dzKHJfUjBfbGlzdCkKCmBgYAoKIyBTdW1tYXJ5CgpTaW1wbGUgbG9nIGxpbmVhciBtb2RlbAoKYGBge3IsIGVjaG89VCwgbWVzc2FnZT1GQUxTRSwgcmVzdWx0cyA9IFQsIHdhcm5pbmc9RkFMU0V9Cgp3ZWlnaHRzIDwtIHRhYmxlKHJfbG9nX2xpbmVhcl9kZiRjb3VudHJ5KQp3ZWlnaHRzIDwtIHdlaWdodHNbcl9sb2dfbGluZWFyX2RmJGNvdW50cnldCndlaWdodHMgPC0gMS8od2VpZ2h0cy9tYXgod2VpZ2h0cykpCmxpYnJhcnkoc3BhdHN0YXQpICNpbnN0YWxsLnBhY2thZ2VzKCdzcGF0c3RhdCcpCnByaW50KHJlc3VsdHNfbG9nX2xpbmVhciA8LSByX2xvZ19saW5lYXJfZGYgJT4lIGZpbHRlcighaXMubmEocG9wKSkgJT4lIHB1bGwocl9sb2dfbGluZWFyKSAlPiUgd2VpZ2h0ZWQucXVhbnRpbGUodz13ZWlnaHRzKSkgI3dlaWdodCBieSBjb3VudHJ5LCBleGNsdWRlIG9icyB3aXRoIG5vIHBvcHVsYXRpb24gZGF0YS4KCmBgYAoKUXVhbnRpbGVzIG9mIFIwIGFjcm9zcyBsb2NhdGlvbnMgZG93bndlaWdodGluZyBtdWx0aXBsZSBvYnNlcnZhdGlvbnMgZnJvbSBjb3VudHJpZXMgdGhhdCBhcmUgZGlzc2FncmVnYXRlZCBhcmJpdGFyaWx5IGJ5IHRoZSBkYXRhIHNvdXJjZS4gQSBtZWRpYW4gb2YgUjA9Mi41NiAuCgpgYGB7ciwgZWNobz1ULCBtZXNzYWdlPUZBTFNFLCByZXN1bHRzID0gVCwgd2FybmluZz1GQUxTRX0KCndlaWdodHMgPC0gdGFibGUocl9SMF9kZiRjb3VudHJ5KQp3ZWlnaHRzIDwtIHdlaWdodHNbcl9SMF9kZiRjb3VudHJ5XQp3ZWlnaHRzIDwtIDEvKHdlaWdodHMvbWF4KHdlaWdodHMpKQoKbGlicmFyeShzcGF0c3RhdCkgI2luc3RhbGwucGFja2FnZXMoJ3NwYXRzdGF0JykKcHJpbnQocmVzdWx0c19SMCA8LSByX1IwX2RmICU+JSBmaWx0ZXIoIWlzLm5hKHBvcCkpICU+JSBwdWxsKHJfbWwpICU+JSB3ZWlnaHRlZC5xdWFudGlsZSh3PXdlaWdodHMpKSAjd2VpZ2h0IGJ5IGNvdW50cnksIGV4Y2x1ZGUgb2JzIHdpdGggbm8gcG9wdWxhdGlvbiBkYXRhLgoKYGBgCgpEaXN0cmlidXRpb24gb2YgUjAgYWNyb3NzIGxvY2F0aW9ucy4KCmBgYHtyLCBlY2hvPVQsIG1lc3NhZ2U9RkFMU0UsIHJlc3VsdHMgPSBGQUxTRSwgd2FybmluZz1GQUxTRX0KCnJfZXN0aW1hdGVzX2FsbCA8LSByX2xvZ19saW5lYXJfZGYgJT4lIGZ1bGxfam9pbihyX1IwX2RmKQoKcl9lc3RpbWF0ZXNfYWxsICU+JSAKICAgICAgICBmaWx0ZXIoIWlzLm5hKHBvcCkpICU+JSAKICAgICAgICBnZ3Bsb3QoKSArIAogICAgICAgIGdlb21fZGVuc2l0eShhZXMoeD1yX2xvZ19saW5lYXIpLCBjb2w9ImxpZ2h0Ymx1ZSIpICsKICAgICAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSByZXN1bHRzX2xvZ19saW5lYXJbM10sIHN0YXQgPSAndmxpbmUnLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2w9ImJsdWUiKSArIAogICAgICAgIGdlb21fZGVuc2l0eShhZXMoeD1yX21sKSwgY29sPSJwaW5rIikgKwogICAgICAgIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IHJlc3VsdHNfUjBbM10sIHN0YXQgPSAndmxpbmUnLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2w9InJlZCIpICsgCiAgICAgICAgZ2d0aXRsZSgiRGlzdHJpYnV0aW9uIFIwIEVzdGltYXRlcyBBY3Jvc3MgQ291bnRyaWVzIikgKyAKICAgICAgICB0aGVtZV9idygpICsKICAgICAgICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzPXNlcSgwLCAzMCwgYnkgPSAxKSkgKyAKICAgICAgICBhbm5vdGF0ZSgidGV4dCIsIHggPSA3LCB5ID0gLjc1LCBsYWJlbCA9ICJNZWRpYW4gUjBfbG9nX2xpbmVhciA9IDEuNjQiLCBjb2xvcj0iYmx1ZSIpICsKICAgICAgICBhbm5vdGF0ZSgidGV4dCIsIHggPSA3LCB5ID0gLjUsIGxhYmVsID0gIk1lZGlhbiBSMF9NTCA9IDIuNTYiLCBjb2xvcj0icmVkIikgKwogICAgICAgIHhsYWIoIlIwIChtYXhpbXVtIGxpa2VsaWhvb2QgZXN0aW1hdGUpIikKCmBgYAoKUmF3IEVzdGltYXRlcwoKYGBge3J9CgpsaWJyYXJ5KERUKQpyX2VzdGltYXRlc19hbGwgJT4lCiAgbXV0YXRlKHJfbWw9IHJvdW5kKHJfbWwsMikpICU+JQpkYXRhdGFibGUocm93bmFtZXMgPSBGQUxTRSwKICAgICAgICAgIG9wdGlvbnMgPSBsaXN0KAogICAgICAgICAgICBwYWdlTGVuZ3RoID0gNTAsCiAgICAgICAgICAgIGNvbHVtbkRlZnMgPSBsaXN0KGxpc3QoY2xhc3NOYW1lID0gJ2R0LWNlbnRlcicsIHRhcmdldHMgPSAiX2FsbCIpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgKQopCgpgYGAKCg==