Background
Since late December 2019, an outbreak of a novel coronavirus disease (COVID-19; previously known as 2019-nCoV) was reported in Wuhan, China, which has subsequently affected 210 countries worldwide. In general, COVID-19 is an acute resolved disease but it can also be deadly, with a 2% case fatality rate.
The COVID-19 pandemic in Mexico is part of the ongoing worldwide pandemic of coronavirus disease 2019 (COVID-19) caused by severe acute respiratory syndrome coronavirus 2 (SARS-CoV-2). The virus was confirmed to have reached Mexico in February 2020. However, the National Council of Science and Technology (CONACYT) reported two cases of COVID-19 in mid-January 2020 in the states of Nayarit and Tabasco, one case per state. As of September 26, there had been 726,431 confirmed cases of COVID-19 in Mexico and 76,243 reported deaths, although the Secretariat of Health, through the “Programa Centinela” (Spanish for “Sentinel Program”) estimated in mid July 2020 that there were more than 2,875,734 cases in Mexico, because they were considering the total number of cases confirmed as a statistical sample. (Note: This paragraph is extracted from wiki: https://en.wikipedia.org/wiki/COVID-19_pandemic_in_Mexico).
Task and Objectives
In this take-home exercise, you are task to analyse the spatio-temporal patterns of COVID-19 case at Central Mexico (i.e. Mexico City (9), Mexico State (15) and Morelos State (17) by using localized spatial statistics methods.
Data Preparation
For the purpose of this study, a GIS data called municipalities_COVID is given. It is in ESRI shapefile format. The shapefile consists of reported COVID-19 cases and other related data at the municipality level. The data was extracted from Secretary of Health (Spanish: Secretaría de Salud), Government of Mexico homepage’s (http://datosabiertos.salud.gob.mx/gobmx/salud/datos_abiertos/).
The original data are stored in csv file format. The cases are recorded daily from 1st January 2020 until 6th August 2020. They had been aggregated up to epidemiological week (e-week) and municipality levels. There are a total of 32 weeks of records in the municipalities_COVID GIS data.
In this study, you are required to focus on Mexico City, Mexico State and Morelos State. Their CVE_ENT codes are 9, 15 and 17 respectively. In terms of epidemiological period, you are required to focus on e-week 13 to e-week 32.
Loading libraries
The code chunk below will check if the R packages in the packaging list have been installed. if not, install the library. After the installation, it will also load the R packages in R.
packages <- c('rgdal', 'spdep', 'tmap', 'tidyverse', 'prettydoc', 'sf', 'magick')
for (p in packages){
if(!require(p, character.only = T)){
install.packages(p)
}
library(p, character.only = T)
}
Read data
Understanding data
Calculate the COVID-19 rate (i.e. cases per 10000 population) from e-week 13 until e-week 32
The code chunk below loop through from e-week 13 to e-week 32. For each loop, it takes the number in columns started with “cumul”(cumulative) follow by i and divide by population / 10000. Then, store the result in a new column called “c19_rate_{i}”, with i representing week number.
Note: The “paste0” function concatenate vectors after converting to character.
for (n in c(13:32)) {
mexi_city[[paste0("c19_rate_", n)]] <- mexi_city[[paste0("cumul",n)]] / (mexi_city$Pop2020 / 10000)
mexi_state[[paste0("c19_rate_", n)]] <- mexi_state[[paste0("cumul",n)]] / (mexi_state$Pop2020 / 10000)
mor_state[[paste0("c19_rate_", n)]] <- mor_state[[paste0("cumul",n)]] / (mor_state$Pop2020 / 10000)
}
Spatio-temporal distribution of COVID-19 rates at municipality level
Use appropriate thematic mapping technique to show and describe the spatio-temporal patterns reveals.
Convert to sf object and merge into one
After coverting the three objects, we merge them into one Simple Feature DataFrame by using “rbind” function for better visualization in the following analysis.
mexi_city_sf <- st_as_sf(mexi_city)
mexi_state_sf <- st_as_sf(mexi_state)
mor_state_sf <- st_as_sf(mor_state)
central_mexi_sf <- rbind(mexi_city_sf, mexi_state_sf, mor_state_sf)
Filtered Columns
The following code chunk stores the columns name we want into selected_columns object. And create a subset from central_mexi_sf object by selecting the columns we need.
selected_columns = c("NOMGEO")
for (n in c(13:32)){
selected_columns <- c(selected_columns, paste0("c19_rate_", n))
}
filtered_central_mexi_sf <- subset(central_mexi_sf, select = selected_columns)
Adding Covid-19 Rate to Each Borough
For preparing the facet map, we need to add COVID-19 rate(from week 13 to 32) to each borough.
Firstly, we need to create a central_mexi_weeks Simple Feature DataFrome to store the result. And extract the unique boroughs from the central_mexi_sf.
In the for loop, we loop through the boroughs to get their geometry value. Then in each borough, we get the borough’s COVID-19 rate from week 13 to 32 and assign it to c19_rate object. And use rbind function to bind the borough name, borough geometry, COVID-19 rate, and week number to central_mexi_weeks object.
central_mexi_weeks <- data.frame()
uinique_boroughs <- unique(filtered_central_mexi_sf$NOMGEO)
for (borough in uinique_boroughs) {
geometry <- subset(filtered_central_mexi_sf, NOMGEO == borough)$geometry
for (week in c(13:32)) {
c19_rate <- subset(filtered_central_mexi_sf, NOMGEO == borough)[[paste0("c19_rate_",week)]]
central_mexi_weeks <- rbind(central_mexi_weeks, data.frame(borough, geometry, c19_rate, week))
}
}
central_mexi_weeks_sf <- st_as_sf(central_mexi_weeks)
Saving the result as RData and load
As the calculation above is time consuming, we will save the object as .rdata for saving time.
save(central_mexi_weeks_sf, file="../data/rdata/central_mexi_weeks_sf.RData")
load("../data/rdata/central_mexi_weeks_sf.RData")
Plotting the choropleth map
tm_shape(central_mexi_weeks_sf) +
tm_fill("c19_rate",
style="jenks") +
tm_borders() +
tm_facets(by = "week", nrow=1, ncol=1, as.layers = TRUE)
tm_layout(title.position = c("right","bottom"))
As the choropleth map showing above, we can see that COVID-19 rate increase from week 17. And the virus origniated from borough - Milpa Alta which located in Mexico City. And spread out to the neighbour boroughs. Follow by Morelos State and Mexio State. And the COVID-19 rate raised from 0 to 170 in 5 months.
Local Moran’s I analysis
Perform local Moran’s I analysis and display the results by using appropriate thematic mapping techniques. Describe the spatio-temporal patterns reveal by the maps.
The following code check will
- loop from week 13 to week 32
- create a subset for specific week
- create (queen) continuity based neighbours of the subset using ploy2nd fucntion
- create the row-standardized weights matrix with nb2listw function
- calculate local Moran’s I value
moran_central_mexi <- data.frame()
for (week in c(13:32)) {
subset <- central_mexi_weeks_sf %>% filter(week == week)
wm_q <- poly2nb(subset, queen=T)
rswm_q <- nb2listw(wm_q, zero.policy = TRUE)
fips <- order(subset$borough)
localMI <- localmoran(subset$c19_rate, rswm_q)
quadrant <- vector(mode="numeric",length=nrow(localMI))
DV <- subset$c19_rate - mean(subset$c19_rate)
C_mI <- localMI[,1] - mean(localMI[,1])
signif <- 0.05
quadrant[DV >0 & C_mI>0] <- 4
quadrant[DV <0 & C_mI<0] <- 1
quadrant[DV <0 & C_mI>0] <- 2
quadrant[DV >0 & C_mI<0] <- 3
quadrant[localMI[,5]>signif] <- 0
subset$quadrant <- quadrant
moran_central_mexi <- rbind(moran_central_mexi, cbind(subset, localMI))
}
moran_central_mexi_sf <- st_as_sf(moran_central_mexi)
Saving as .RData object and load it
As the calculation above is extremely time consuming, we will save the object as .rdata for saving time.
save(moran_central_mexi_sf, file="../data/rdata/moran_central_mexi_sf.RData")
load("../data/rdata/moran_central_mexi_sf.RData")
Mapping local Moran’s I values and p-values
tm_shape(moran_central_mexi_sf) +
tm_fill(col = "Ii",
style = "pretty",
title = "local moran statistics") +
tm_facets("week", nrow=1, ncol=1) +
tm_borders(alpha = 0.5)
tm_shape(moran_central_mexi_sf) +
tm_fill(col = "Pr.z...0.",
breaks=c(-Inf, 0.001, 0.01, 0.05, 0.1, Inf),
palette="-Blues",
title = "local Moran's I p-values") +
tm_facets("week", nrow=1, ncol=1) +
tm_borders(alpha = 0.5)
From the local Moran statistics graph we can see that Mexico City has a trend of increase in local Moran’s value. And other boroughs in the study area shows a slightly decrease of the value. Based on the local Moran’s I p-values graph, we also can understand the most of the p-values in the west part of the study area is less than 0.05 which is small enough for the results to be considered statistically significant. However, the p-values kept increasing from week 13 to 32 in west part of Mexico State. Apart from that, the p-values started to decrease from week 20 to week 32 in Mexico City.
Local Getis-Ord Gi* analysis
Deriving distance-based weight matrix and calculating Gi statisitcs
central_mexi_coor <- rbind(coordinates(mexi_city), coordinates(mexi_state), coordinates(mor_state))
The code chunk below will
- create a Simple Feature DataFrame object for storing the result
- loop from week 13 to week 32
- create a subset and assin the specific week to it
- create adaptive proximity matrix of sebst using adaptive distance function (knn2nb)
- calculate Gi statistics based on adaptive distance matrix
- bind to the Simple Feature DataFrame object to combine the result
central_mexi_gi <- data.frame()
fips <- order(central_mexi_weeks_sf$borough)
for (i in c(13:32)) {
subset <- central_mexi_weeks_sf %>% filter(week == i)
knb <- knn2nb(
knearneigh(
central_mexi_coor,
k=8,
longlat = FALSE
),
row.names=row.names(subset$m))
knb_lw <- nb2listw(knb, style = 'B')
gi.adaptive <- localG(subset$c19_rate, knb_lw)
gi <- cbind(subset, as.matrix(gi.adaptive))
names(gi)[4] <- "gstat_adaptive"
central_mexi_gi <- rbind(central_mexi_gi, gi)
}
head(central_mexi_gi)
Mapping Gi values based on adaptive distance weights
tm_shape(central_mexi_gi) +
tm_fill(col = "gstat_adaptive",
style = "pretty",
palette = "-RdYlBu",
title = "Local Gi Based on \nAdaptive Distance Weights") +
tm_facets("week", ncol=1, nrow=1) +
tm_borders(alpha = 0.5)
From the local Gi graph we can see that the hot spot areas which represents high COVID-19 rate is located at Mexico City. Furthermore, the neighbor boroughs surround Mexico City also have an increase of local Gi value through the weeks we study. The cold spot areas, on the other hand, mainly comprise of counties located on the south-western part of central Mexico.
LS0tCnRpdGxlOiAiRGV0ZWN0aW5nIFNwYXRpb3RlbXBvcmFsIFBhdHRlcm5zIG9mIENPVklELTE5IGluIENlbnRyYWwgTWV4aWNvIgphdXRob3I6ICJMaW4gQ2hpaC1Ic3VhbiIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICBjc3M6IHN0eWxlLmNzcwogICAgdGhlbWU6IHVuaXRlZAogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6CiAgICAgIGNvbGxhcHNlZDogZmFsc2UKICAgICAgc21vb3RoX3Njcm9sbDogdHJ1ZQogICAgdG9jX2RlcHRoOiAzCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICAgIGNvZGVfZm9sZGluZzogaGlkZQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQpiaWJsaW9ncmFwaHk6IHJlZmVyZW5jZXMuYmliCmxpbmstY2l0YXRpb25zOiB0cnVlCi0tLQoKIyBCYWNrZ3JvdW5kCgpTaW5jZSBsYXRlIERlY2VtYmVyIDIwMTksIGFuIG91dGJyZWFrIG9mIGEgbm92ZWwgY29yb25hdmlydXMgZGlzZWFzZSAoQ09WSUQtMTk7IHByZXZpb3VzbHkga25vd24gYXMgMjAxOS1uQ29WKSB3YXMgcmVwb3J0ZWQgaW4gV3VoYW4sIENoaW5hLCB3aGljaCBoYXMgc3Vic2VxdWVudGx5IGFmZmVjdGVkIDIxMCBjb3VudHJpZXMgd29ybGR3aWRlLiBJbiBnZW5lcmFsLCBDT1ZJRC0xOSBpcyBhbiBhY3V0ZSByZXNvbHZlZCBkaXNlYXNlIGJ1dCBpdCBjYW4gYWxzbyBiZSBkZWFkbHksIHdpdGggYSAyJSBjYXNlIGZhdGFsaXR5IHJhdGUuCgpUaGUgQ09WSUQtMTkgcGFuZGVtaWMgaW4gTWV4aWNvIGlzIHBhcnQgb2YgdGhlIG9uZ29pbmcgd29ybGR3aWRlIHBhbmRlbWljIG9mIGNvcm9uYXZpcnVzIGRpc2Vhc2UgMjAxOSAoQ09WSUQtMTkpIGNhdXNlZCBieSBzZXZlcmUgYWN1dGUgcmVzcGlyYXRvcnkgc3luZHJvbWUgY29yb25hdmlydXMgMiAoU0FSUy1Db1YtMikuIFRoZSB2aXJ1cyB3YXMgY29uZmlybWVkIHRvIGhhdmUgcmVhY2hlZCBNZXhpY28gaW4gRmVicnVhcnkgMjAyMC4gSG93ZXZlciwgdGhlIE5hdGlvbmFsIENvdW5jaWwgb2YgU2NpZW5jZSBhbmQgVGVjaG5vbG9neSAoQ09OQUNZVCkgcmVwb3J0ZWQgdHdvIGNhc2VzIG9mIENPVklELTE5IGluIG1pZC1KYW51YXJ5IDIwMjAgaW4gdGhlIHN0YXRlcyBvZiBOYXlhcml0IGFuZCBUYWJhc2NvLCBvbmUgY2FzZSBwZXIgc3RhdGUuIEFzIG9mIFNlcHRlbWJlciAyNiwgdGhlcmUgaGFkIGJlZW4gNzI2LDQzMSBjb25maXJtZWQgY2FzZXMgb2YgQ09WSUQtMTkgaW4gTWV4aWNvIGFuZCA3NiwyNDMgcmVwb3J0ZWQgZGVhdGhzLCBhbHRob3VnaCB0aGUgU2VjcmV0YXJpYXQgb2YgSGVhbHRoLCB0aHJvdWdoIHRoZSAiUHJvZ3JhbWEgQ2VudGluZWxhIiAoU3BhbmlzaCBmb3IgIlNlbnRpbmVsIFByb2dyYW0iKSBlc3RpbWF0ZWQgaW4gbWlkIEp1bHkgMjAyMCB0aGF0IHRoZXJlIHdlcmUgbW9yZSB0aGFuIDIsODc1LDczNCBjYXNlcyBpbiBNZXhpY28sIGJlY2F1c2UgdGhleSB3ZXJlIGNvbnNpZGVyaW5nIHRoZSB0b3RhbCBudW1iZXIgb2YgY2FzZXMgY29uZmlybWVkIGFzIGEgc3RhdGlzdGljYWwgc2FtcGxlLiAoTm90ZTogVGhpcyBwYXJhZ3JhcGggaXMgZXh0cmFjdGVkIGZyb20gd2lraTogaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvQ09WSUQtMTlfcGFuZGVtaWNfaW5fTWV4aWNvKS4KCiMgVGFzayBhbmQgT2JqZWN0aXZlcwoKSW4gdGhpcyB0YWtlLWhvbWUgZXhlcmNpc2UsIHlvdSBhcmUgdGFzayB0byBhbmFseXNlIHRoZSBzcGF0aW8tdGVtcG9yYWwgcGF0dGVybnMgb2YgQ09WSUQtMTkgY2FzZSBhdCBDZW50cmFsIE1leGljbyAoaS5lLiBNZXhpY28gQ2l0eSAoOSksIE1leGljbyBTdGF0ZSAoMTUpIGFuZCBNb3JlbG9zIFN0YXRlICgxNykgYnkgdXNpbmcgbG9jYWxpemVkIHNwYXRpYWwgc3RhdGlzdGljcyBtZXRob2RzLgoKIyBEYXRhIFByZXBhcmF0aW9uCgpGb3IgdGhlIHB1cnBvc2Ugb2YgdGhpcyBzdHVkeSwgYSBHSVMgZGF0YSBjYWxsZWQgbXVuaWNpcGFsaXRpZXNfQ09WSUQgaXMgZ2l2ZW4uIEl0IGlzIGluIEVTUkkgc2hhcGVmaWxlIGZvcm1hdC4gVGhlIHNoYXBlZmlsZSBjb25zaXN0cyBvZiByZXBvcnRlZCBDT1ZJRC0xOSBjYXNlcyBhbmQgb3RoZXIgcmVsYXRlZCBkYXRhIGF0IHRoZSBtdW5pY2lwYWxpdHkgbGV2ZWwuIFRoZSBkYXRhIHdhcyBleHRyYWN0ZWQgZnJvbSBTZWNyZXRhcnkgb2YgSGVhbHRoIChTcGFuaXNoOiBTZWNyZXRhcsOtYSBkZSBTYWx1ZCksIEdvdmVybm1lbnQgb2YgTWV4aWNvIGhvbWVwYWdl4oCZcyAoaHR0cDovL2RhdG9zYWJpZXJ0b3Muc2FsdWQuZ29iLm14L2dvYm14L3NhbHVkL2RhdG9zX2FiaWVydG9zLykuCgpUaGUgb3JpZ2luYWwgZGF0YSBhcmUgc3RvcmVkIGluIGNzdiBmaWxlIGZvcm1hdC4gVGhlIGNhc2VzIGFyZSByZWNvcmRlZCBkYWlseSBmcm9tIDFzdCBKYW51YXJ5IDIwMjAgdW50aWwgNnRoIEF1Z3VzdCAyMDIwLiBUaGV5IGhhZCBiZWVuIGFnZ3JlZ2F0ZWQgdXAgdG8gZXBpZGVtaW9sb2dpY2FsIHdlZWsgKGUtd2VlaykgYW5kIG11bmljaXBhbGl0eSBsZXZlbHMuIFRoZXJlIGFyZSBhIHRvdGFsIG9mIDMyIHdlZWtzIG9mIHJlY29yZHMgaW4gdGhlIG11bmljaXBhbGl0aWVzX0NPVklEIEdJUyBkYXRhLgoKSW4gdGhpcyBzdHVkeSwgeW91IGFyZSByZXF1aXJlZCB0byBmb2N1cyBvbiBNZXhpY28gQ2l0eSwgTWV4aWNvIFN0YXRlIGFuZCBNb3JlbG9zIFN0YXRlLiBUaGVpciBDVkVfRU5UIGNvZGVzIGFyZSA5LCAxNSBhbmQgMTcgcmVzcGVjdGl2ZWx5LiBJbiB0ZXJtcyBvZiBlcGlkZW1pb2xvZ2ljYWwgcGVyaW9kLCB5b3UgYXJlIHJlcXVpcmVkIHRvIGZvY3VzIG9uIGUtd2VlayAxMyB0byBlLXdlZWsgMzIuCgojIyBMb2FkaW5nIGxpYnJhcmllcwoKVGhlIGNvZGUgY2h1bmsgYmVsb3cgd2lsbCBjaGVjayBpZiB0aGUgUiBwYWNrYWdlcyBpbiB0aGUgcGFja2FnaW5nIGxpc3QgaGF2ZSBiZWVuIGluc3RhbGxlZC4gaWYgbm90LCBpbnN0YWxsIHRoZSBsaWJyYXJ5LiBBZnRlciB0aGUgaW5zdGFsbGF0aW9uLCBpdCB3aWxsIGFsc28gbG9hZCB0aGUgUiBwYWNrYWdlcyBpbiBSLgoKYGBge3IsIGVycm9yPUZBTFNFLCAgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KcGFja2FnZXMgPC0gYygncmdkYWwnLCAnc3BkZXAnLCAgJ3RtYXAnLCAndGlkeXZlcnNlJywgJ3ByZXR0eWRvYycsICdzZicsICdtYWdpY2snKQpmb3IgKHAgaW4gcGFja2FnZXMpewogIGlmKCFyZXF1aXJlKHAsIGNoYXJhY3Rlci5vbmx5ID0gVCkpewogICAgaW5zdGFsbC5wYWNrYWdlcyhwKQogIH0KICBsaWJyYXJ5KHAsIGNoYXJhY3Rlci5vbmx5ID0gVCkKfQpgYGAKCiMjIFJlYWQgZGF0YQoKYGBge3IgZXJyb3I9RkFMU0UsICB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBlY2hvPUZBTFNFLCByZXN1bHRzID0gImhpZGUifQptdW5pX0NPVklEID0gcmVhZE9HUihkc24gPSAiLi4vZGF0YS9nZW9zcGF0aWFsIiwgbGF5ZXI9Im11bmljaXBhbGl0aWVzX0NPVklEIiwgdmVyYm9zZSA9IEZBTFNFKQpgYGAKCiMgVW5kZXJzdGFuZGluZyBkYXRhCiMjIEV4dHJhY3QgbXVuaWNpcGFsaXRpZXMgbG9jYXRlZCB3aXRoIHRoZSBzdHVkeSBhcmVhCkZpbHRlciBvdXQgTWV4aWNvIENpdHksIE1leGljbyBTdGF0ZSBhbmQgTW9yZWxvcyBTdGF0ZS4gQXMgbWVudGlvbmVkIGFib3ZlLCB0aGVpciBDVkVfRU5UIGNvZGVzIGFyZSA5LCAxNSwgYW5kIDE3IHJlc3BlY3RpdmVseS4gQW5kIGNyZWF0ZWQgYW5vdGhlciBjb21iaW5lZCBvYmplY3Qgd2hpY2ggY29udGFpbmluZyB0aGUgdGhyZWUgYXJlYXMuCmBgYHtyIGVycm9yPUZBTFNFLCAgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KbWV4aV9jaXR5IDwtIG11bmlfQ09WSURbKG11bmlfQ09WSUQkQ1ZFX0VOVCA9PSAnMDknKSxdCm1leGlfc3RhdGUgPC0gbXVuaV9DT1ZJRFsobXVuaV9DT1ZJRCRDVkVfRU5UID09ICcxNScpLF0KbW9yX3N0YXRlIDwtIG11bmlfQ09WSURbKG11bmlfQ09WSUQkQ1ZFX0VOVCA9PSAnMTcnKSxdCgptZXhpX2NvbWJpbmVkIDwtIG11bmlfQ09WSURbKG11bmlfQ09WSUQkQ1ZFX0VOVCA9PSAnMDknIHwgbXVuaV9DT1ZJRCRDVkVfRU5UID09ICcxNScgfCBtdW5pX0NPVklEJENWRV9FTlQgPT0gJzE3JyksXQpgYGAKUGxvdCB0byBjaGVjayB0aGUgbWFwLgpgYGB7ciBlcnJvcj1GQUxTRSwgIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CnBsb3QobWV4aV9jaXR5LCBtYWluID0gIk1leGljbyBDaXR5IikKcGxvdChtZXhpX3N0YXRlLCBtYWluID0gIk1leGljbyBTdGF0ZSIpCnBsb3QobW9yX3N0YXRlLCBtYWluID0gIk1vcmVsb3MgU3RhdGUiKQpwbG90KG1leGlfY29tYmluZWQsIG1haW4gPSAiQ29tYmluZWQgU3R1ZHkgQXJlYSIpCnF0bShtZXhpX2NvbWJpbmVkLCAiQ1ZFX0VOVCIsICB0aXRsZSA9ICJNZXhpY28gQ2l0eSwgTWV4aWNvIFN0YXRlIGFuZCBNb3JlbG9zIFN0YXRlIikgKwogIHRtX2xlZ2VuZChzaG93ID0gVFJVRSkKYGBgCgoKIyMgQ2FsY3VsYXRlIHRoZSBDT1ZJRC0xOSByYXRlIChpLmUuIGNhc2VzIHBlciAxMDAwMCBwb3B1bGF0aW9uKSBmcm9tIGUtd2VlayAxMyB1bnRpbCBlLXdlZWsgMzIKClRoZSBjb2RlIGNodW5rIGJlbG93IGxvb3AgdGhyb3VnaCBmcm9tIGUtd2VlayAxMyB0byBlLXdlZWsgMzIuIEZvciBlYWNoIGxvb3AsIGl0IHRha2VzIHRoZSBudW1iZXIgaW4gY29sdW1ucyBzdGFydGVkIHdpdGggImN1bXVsIihjdW11bGF0aXZlKSBmb2xsb3cgYnkgaSBhbmQgZGl2aWRlIGJ5IHBvcHVsYXRpb24gLyAxMDAwMC4gVGhlbiwgc3RvcmUgdGhlIHJlc3VsdCBpbiBhIG5ldyBjb2x1bW4gY2FsbGVkICJjMTlfcmF0ZV97aX0iLCB3aXRoIGkgcmVwcmVzZW50aW5nIHdlZWsgbnVtYmVyLgoKTm90ZTogVGhlICJwYXN0ZTAiIGZ1bmN0aW9uIGNvbmNhdGVuYXRlIHZlY3RvcnMgYWZ0ZXIgY29udmVydGluZyB0byBjaGFyYWN0ZXIuCgpgYGB7ciwgZXJyb3I9RkFMU0UsICB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBldmFsPUZBTFNFfQpmb3IgKG4gaW4gYygxMzozMikpIHsKICBtZXhpX2NpdHlbW3Bhc3RlMCgiYzE5X3JhdGVfIiwgbildXSA8LSBtZXhpX2NpdHlbW3Bhc3RlMCgiY3VtdWwiLG4pXV0gLyAobWV4aV9jaXR5JFBvcDIwMjAgLyAxMDAwMCkKICBtZXhpX3N0YXRlW1twYXN0ZTAoImMxOV9yYXRlXyIsIG4pXV0gPC0gbWV4aV9zdGF0ZVtbcGFzdGUwKCJjdW11bCIsbildXSAvIChtZXhpX3N0YXRlJFBvcDIwMjAgLyAxMDAwMCkKICBtb3Jfc3RhdGVbW3Bhc3RlMCgiYzE5X3JhdGVfIiwgbildXSA8LSBtb3Jfc3RhdGVbW3Bhc3RlMCgiY3VtdWwiLG4pXV0gLyAobW9yX3N0YXRlJFBvcDIwMjAgLyAxMDAwMCkgCn0KYGBgCgojIFNwYXRpby10ZW1wb3JhbCBkaXN0cmlidXRpb24gb2YgQ09WSUQtMTkgcmF0ZXMgYXQgbXVuaWNpcGFsaXR5IGxldmVsClVzZSBhcHByb3ByaWF0ZSB0aGVtYXRpYyBtYXBwaW5nIHRlY2huaXF1ZSB0byBzaG93IGFuZCBkZXNjcmliZSB0aGUgc3BhdGlvLXRlbXBvcmFsIHBhdHRlcm5zIHJldmVhbHMuCgojIyBDb252ZXJ0IHRvIHNmIG9iamVjdCBhbmQgbWVyZ2UgaW50byBvbmUKQWZ0ZXIgY292ZXJ0aW5nIHRoZSB0aHJlZSBvYmplY3RzLCB3ZSBtZXJnZSB0aGVtIGludG8gb25lIFNpbXBsZSBGZWF0dXJlIERhdGFGcmFtZSBieSB1c2luZyAicmJpbmQiIGZ1bmN0aW9uIGZvciBiZXR0ZXIgdmlzdWFsaXphdGlvbiBpbiB0aGUgZm9sbG93aW5nIGFuYWx5c2lzLgoKYGBge3IsIGVycm9yPUZBTFNFLCAgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZXZhbD1GQUxTRX0KbWV4aV9jaXR5X3NmIDwtIHN0X2FzX3NmKG1leGlfY2l0eSkKbWV4aV9zdGF0ZV9zZiA8LSBzdF9hc19zZihtZXhpX3N0YXRlKQptb3Jfc3RhdGVfc2YgPC0gc3RfYXNfc2YobW9yX3N0YXRlKQpjZW50cmFsX21leGlfc2YgPC0gcmJpbmQobWV4aV9jaXR5X3NmLCBtZXhpX3N0YXRlX3NmLCBtb3Jfc3RhdGVfc2YpCmBgYAoKIyMgRmlsdGVyZWQgQ29sdW1ucwpUaGUgZm9sbG93aW5nIGNvZGUgY2h1bmsgc3RvcmVzIHRoZSBjb2x1bW5zIG5hbWUgd2Ugd2FudCBpbnRvIHNlbGVjdGVkX2NvbHVtbnMgb2JqZWN0LiBBbmQgY3JlYXRlIGEgc3Vic2V0IGZyb20gY2VudHJhbF9tZXhpX3NmIG9iamVjdCBieSBzZWxlY3RpbmcgdGhlIGNvbHVtbnMgd2UgbmVlZC4KCmBgYHtyLCBlcnJvcj1GQUxTRSwgIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGV2YWw9RkFMU0V9CnNlbGVjdGVkX2NvbHVtbnMgPSBjKCJOT01HRU8iKQpmb3IgKG4gaW4gYygxMzozMikpewogIHNlbGVjdGVkX2NvbHVtbnMgPC0gYyhzZWxlY3RlZF9jb2x1bW5zLCBwYXN0ZTAoImMxOV9yYXRlXyIsIG4pKQp9CmZpbHRlcmVkX2NlbnRyYWxfbWV4aV9zZiA8LSBzdWJzZXQoY2VudHJhbF9tZXhpX3NmLCBzZWxlY3QgPSBzZWxlY3RlZF9jb2x1bW5zKQpgYGAKCiMjIEFkZGluZyBDb3ZpZC0xOSBSYXRlIHRvIEVhY2ggQm9yb3VnaAoKRm9yIHByZXBhcmluZyB0aGUgZmFjZXQgbWFwLCB3ZSBuZWVkIHRvIGFkZCBDT1ZJRC0xOSByYXRlKGZyb20gd2VlayAxMyB0byAzMikgdG8gZWFjaCBib3JvdWdoLgoKRmlyc3RseSwgd2UgbmVlZCB0byBjcmVhdGUgYSBjZW50cmFsX21leGlfd2Vla3MgU2ltcGxlIEZlYXR1cmUgRGF0YUZyb21lIHRvIHN0b3JlIHRoZSByZXN1bHQuIEFuZCBleHRyYWN0IHRoZSB1bmlxdWUgYm9yb3VnaHMgZnJvbSB0aGUgY2VudHJhbF9tZXhpX3NmLiAKCkluIHRoZSBmb3IgbG9vcCwgd2UgbG9vcCB0aHJvdWdoIHRoZSBib3JvdWdocyB0byBnZXQgdGhlaXIgZ2VvbWV0cnkgdmFsdWUuIFRoZW4gaW4gZWFjaCBib3JvdWdoLCB3ZSBnZXQgdGhlIGJvcm91Z2gncyBDT1ZJRC0xOSByYXRlIGZyb20gd2VlayAxMyB0byAzMiBhbmQgYXNzaWduIGl0IHRvIGMxOV9yYXRlIG9iamVjdC4gQW5kIHVzZSByYmluZCBmdW5jdGlvbiB0byBiaW5kIHRoZSBib3JvdWdoIG5hbWUsIGJvcm91Z2ggZ2VvbWV0cnksIENPVklELTE5IHJhdGUsIGFuZCB3ZWVrIG51bWJlciB0byBjZW50cmFsX21leGlfd2Vla3Mgb2JqZWN0LgoKYGBge3IsIGVycm9yPUZBTFNFLCAgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZXZhbD1GQUxTRX0KY2VudHJhbF9tZXhpX3dlZWtzIDwtIGRhdGEuZnJhbWUoKQp1aW5pcXVlX2Jvcm91Z2hzIDwtIHVuaXF1ZShmaWx0ZXJlZF9jZW50cmFsX21leGlfc2YkTk9NR0VPKQoKZm9yIChib3JvdWdoIGluIHVpbmlxdWVfYm9yb3VnaHMpIHsKICBnZW9tZXRyeSA8LSBzdWJzZXQoZmlsdGVyZWRfY2VudHJhbF9tZXhpX3NmLCBOT01HRU8gPT0gYm9yb3VnaCkkZ2VvbWV0cnkKICBmb3IgKHdlZWsgaW4gYygxMzozMikpIHsKICAgICAgYzE5X3JhdGUgPC0gc3Vic2V0KGZpbHRlcmVkX2NlbnRyYWxfbWV4aV9zZiwgTk9NR0VPID09IGJvcm91Z2gpW1twYXN0ZTAoImMxOV9yYXRlXyIsd2VlayldXQogICAgICBjZW50cmFsX21leGlfd2Vla3MgPC0gcmJpbmQoY2VudHJhbF9tZXhpX3dlZWtzLCBkYXRhLmZyYW1lKGJvcm91Z2gsIGdlb21ldHJ5LCBjMTlfcmF0ZSwgd2VlaykpCiAgfQp9CgpjZW50cmFsX21leGlfd2Vla3Nfc2YgPC0gc3RfYXNfc2YoY2VudHJhbF9tZXhpX3dlZWtzKQpgYGAKCiMjIFNhdmluZyB0aGUgcmVzdWx0IGFzIFJEYXRhIGFuZCBsb2FkCgpBcyB0aGUgY2FsY3VsYXRpb24gYWJvdmUgaXMgdGltZSBjb25zdW1pbmcsIHdlIHdpbGwgc2F2ZSB0aGUgb2JqZWN0IGFzIC5yZGF0YSBmb3Igc2F2aW5nIHRpbWUuCmBgYHtyLCBlcnJvcj1GQUxTRSwgIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGV2YWw9RkFMU0V9CnNhdmUoY2VudHJhbF9tZXhpX3dlZWtzX3NmLCBmaWxlPSIuLi9kYXRhL3JkYXRhL2NlbnRyYWxfbWV4aV93ZWVrc19zZi5SRGF0YSIpCmxvYWQoIi4uL2RhdGEvcmRhdGEvY2VudHJhbF9tZXhpX3dlZWtzX3NmLlJEYXRhIikKYGBgCgojIyBQbG90dGluZyB0aGUgY2hvcm9wbGV0aCBtYXAKYGBge3IsIGVycm9yPUZBTFNFLCAgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZXZhbD1GQUxTRX0KdG1fc2hhcGUoY2VudHJhbF9tZXhpX3dlZWtzX3NmKSArCiAgICB0bV9maWxsKCJjMTlfcmF0ZSIsIAogICAgICAgICAgICBzdHlsZT0iamVua3MiKSArCiAgICB0bV9ib3JkZXJzKCkgKwogICAgdG1fZmFjZXRzKGJ5ID0gIndlZWsiLCBucm93PTEsIG5jb2w9MSwgYXMubGF5ZXJzID0gVFJVRSkKICAgIHRtX2xheW91dCh0aXRsZS5wb3NpdGlvbiA9IGMoInJpZ2h0IiwiYm90dG9tIikpCmBgYAohW0Nob3JvcGxldGggbWFwIEJhc2VkIG9uIENPVklELTE5IFJhdGVdKC4uL2RhdGEvaW1nL2NvdmlkLTE5X3JhdGUuZ2lmKQoKQXMgdGhlIGNob3JvcGxldGggbWFwIHNob3dpbmcgYWJvdmUsIHdlIGNhbiBzZWUgdGhhdCBDT1ZJRC0xOSByYXRlIGluY3JlYXNlIGZyb20gd2VlayAxNy4gQW5kIHRoZSB2aXJ1cyBvcmlnbmlhdGVkIGZyb20gYm9yb3VnaCAtIE1pbHBhIEFsdGEgd2hpY2ggbG9jYXRlZCBpbiBNZXhpY28gQ2l0eS4gQW5kIHNwcmVhZCBvdXQgdG8gdGhlIG5laWdoYm91ciBib3JvdWdocy4gRm9sbG93IGJ5IE1vcmVsb3MgU3RhdGUgYW5kIE1leGlvIFN0YXRlLiBBbmQgdGhlIENPVklELTE5IHJhdGUgcmFpc2VkIGZyb20gMCB0byAxNzAgaW4gNSBtb250aHMuCgojIExvY2FsIE1vcmFu4oCZcyBJIGFuYWx5c2lzClBlcmZvcm0gbG9jYWwgTW9yYW7igJlzIEkgYW5hbHlzaXMgYW5kIGRpc3BsYXkgdGhlIHJlc3VsdHMgYnkgdXNpbmcgYXBwcm9wcmlhdGUgdGhlbWF0aWMgbWFwcGluZyB0ZWNobmlxdWVzLiBEZXNjcmliZSB0aGUgc3BhdGlvLXRlbXBvcmFsIHBhdHRlcm5zIHJldmVhbCBieSB0aGUgbWFwcy4KClRoZSBmb2xsb3dpbmcgY29kZSBjaGVjayB3aWxsCgoqIGxvb3AgZnJvbSB3ZWVrIDEzIHRvIHdlZWsgMzIKKiBjcmVhdGUgYSBzdWJzZXQgZm9yIHNwZWNpZmljIHdlZWsKKiBjcmVhdGUgKHF1ZWVuKSBjb250aW51aXR5IGJhc2VkIG5laWdoYm91cnMgb2YgdGhlIHN1YnNldCB1c2luZyBwbG95Mm5kIGZ1Y250aW9uCiogY3JlYXRlIHRoZSByb3ctc3RhbmRhcmRpemVkIHdlaWdodHMgbWF0cml4IHdpdGggbmIybGlzdHcgZnVuY3Rpb24KKiBjYWxjdWxhdGUgbG9jYWwgTW9yYW4ncyBJIHZhbHVlCmBgYHtyLCBlcnJvcj1GQUxTRSwgIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGV2YWw9RkFMU0V9Cm1vcmFuX2NlbnRyYWxfbWV4aSA8LSBkYXRhLmZyYW1lKCkKCmZvciAod2VlayBpbiBjKDEzOjMyKSkgewogIAogIHN1YnNldCA8LSBjZW50cmFsX21leGlfd2Vla3Nfc2YgJT4lIGZpbHRlcih3ZWVrID09IHdlZWspCiAgCiAgd21fcSA8LSBwb2x5Mm5iKHN1YnNldCwgcXVlZW49VCkKICByc3dtX3EgPC0gbmIybGlzdHcod21fcSwgemVyby5wb2xpY3kgPSBUUlVFKQogIAogIGZpcHMgPC0gb3JkZXIoc3Vic2V0JGJvcm91Z2gpCiAgbG9jYWxNSSA8LSBsb2NhbG1vcmFuKHN1YnNldCRjMTlfcmF0ZSwgcnN3bV9xKQogIAogIHF1YWRyYW50IDwtIHZlY3Rvcihtb2RlPSJudW1lcmljIixsZW5ndGg9bnJvdyhsb2NhbE1JKSkKICAKICBEViA8LSBzdWJzZXQkYzE5X3JhdGUgLSBtZWFuKHN1YnNldCRjMTlfcmF0ZSkgICAgIAogIENfbUkgPC0gbG9jYWxNSVssMV0gLSBtZWFuKGxvY2FsTUlbLDFdKSAgICAKICBzaWduaWYgPC0gMC4wNSAgICAgICAKICBxdWFkcmFudFtEViA+MCAmIENfbUk+MF0gPC0gNCAgICAgIAogIHF1YWRyYW50W0RWIDwwICYgQ19tSTwwXSA8LSAxICAgICAgCiAgcXVhZHJhbnRbRFYgPDAgJiBDX21JPjBdIDwtIDIKICBxdWFkcmFudFtEViA+MCAmIENfbUk8MF0gPC0gMwogIHF1YWRyYW50W2xvY2FsTUlbLDVdPnNpZ25pZl0gPC0gMAogIAogIHN1YnNldCRxdWFkcmFudCA8LSBxdWFkcmFudAogIAogIG1vcmFuX2NlbnRyYWxfbWV4aSA8LSByYmluZChtb3Jhbl9jZW50cmFsX21leGksIGNiaW5kKHN1YnNldCwgbG9jYWxNSSkpCn0KCm1vcmFuX2NlbnRyYWxfbWV4aV9zZiA8LSBzdF9hc19zZihtb3Jhbl9jZW50cmFsX21leGkpCmBgYAoKIyMgU2F2aW5nIGFzIC5SRGF0YSBvYmplY3QgYW5kIGxvYWQgaXQKQXMgdGhlIGNhbGN1bGF0aW9uIGFib3ZlIGlzIGV4dHJlbWVseSB0aW1lIGNvbnN1bWluZywgd2Ugd2lsbCBzYXZlIHRoZSBvYmplY3QgYXMgLnJkYXRhIGZvciBzYXZpbmcgdGltZS4KYGBge3IsIGVycm9yPUZBTFNFLCAgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZXZhbD1GQUxTRX0Kc2F2ZShtb3Jhbl9jZW50cmFsX21leGlfc2YsIGZpbGU9Ii4uL2RhdGEvcmRhdGEvbW9yYW5fY2VudHJhbF9tZXhpX3NmLlJEYXRhIikKbG9hZCgiLi4vZGF0YS9yZGF0YS9tb3Jhbl9jZW50cmFsX21leGlfc2YuUkRhdGEiKQpgYGAKCiMjIE1hcHBpbmcgbG9jYWwgTW9yYW4ncyBJIHZhbHVlcyBhbmQgcC12YWx1ZXMKCmBgYHtyLCBlcnJvcj1GQUxTRSwgIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGV2YWw9RkFMU0V9CnRtX3NoYXBlKG1vcmFuX2NlbnRyYWxfbWV4aV9zZikgKwogIHRtX2ZpbGwoY29sID0gIklpIiwgCiAgICAgICAgICBzdHlsZSA9ICJwcmV0dHkiLAogICAgICAgICAgdGl0bGUgPSAibG9jYWwgbW9yYW4gc3RhdGlzdGljcyIpICsKICB0bV9mYWNldHMoIndlZWsiLCBucm93PTEsIG5jb2w9MSkgKwogIHRtX2JvcmRlcnMoYWxwaGEgPSAwLjUpCgp0bV9zaGFwZShtb3Jhbl9jZW50cmFsX21leGlfc2YpICsKICB0bV9maWxsKGNvbCA9ICJQci56Li4uMC4iLCAKICAgICAgICAgIGJyZWFrcz1jKC1JbmYsIDAuMDAxLCAwLjAxLCAwLjA1LCAwLjEsIEluZiksCiAgICAgICAgICBwYWxldHRlPSItQmx1ZXMiLCAKICAgICAgICAgIHRpdGxlID0gImxvY2FsIE1vcmFuJ3MgSSBwLXZhbHVlcyIpICsKICB0bV9mYWNldHMoIndlZWsiLCBucm93PTEsIG5jb2w9MSkgKwogIHRtX2JvcmRlcnMoYWxwaGEgPSAwLjUpCmBgYAoKIVtMb2NhbCBNb3JhbidzIEkgU3RhdGlzdGljc10oLi4vZGF0YS9pbWcvbG1zLmdpZikgCgohW0xvY2FsIE1vcmFuJ3MgSSBwLXZhbHVlc10oLi4vZGF0YS9pbWcvbG1zX3AuZ2lmKQoKCkZyb20gdGhlIGxvY2FsIE1vcmFuIHN0YXRpc3RpY3MgZ3JhcGggd2UgY2FuIHNlZSB0aGF0IE1leGljbyBDaXR5IGhhcyBhIHRyZW5kIG9mIGluY3JlYXNlIGluIGxvY2FsIE1vcmFuJ3MgdmFsdWUuIEFuZCBvdGhlciBib3JvdWdocyBpbiB0aGUgc3R1ZHkgYXJlYSBzaG93cyBhIHNsaWdodGx5IGRlY3JlYXNlIG9mIHRoZSB2YWx1ZS4gQmFzZWQgb24gdGhlIGxvY2FsIE1vcmFuJ3MgSSBwLXZhbHVlcyBncmFwaCwgd2UgYWxzbyBjYW4gdW5kZXJzdGFuZCB0aGUgbW9zdCBvZiB0aGUgcC12YWx1ZXMgaW4gdGhlIHdlc3QgcGFydCBvZiB0aGUgc3R1ZHkgYXJlYSBpcyBsZXNzIHRoYW4gMC4wNSB3aGljaCBpcyBzbWFsbCBlbm91Z2ggZm9yIHRoZSByZXN1bHRzIHRvIGJlIGNvbnNpZGVyZWQgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudC4gSG93ZXZlciwgdGhlIHAtdmFsdWVzIGtlcHQgaW5jcmVhc2luZyBmcm9tIHdlZWsgMTMgdG8gMzIgaW4gd2VzdCBwYXJ0IG9mIE1leGljbyBTdGF0ZS4gQXBhcnQgZnJvbSB0aGF0LCB0aGUgcC12YWx1ZXMgc3RhcnRlZCB0byBkZWNyZWFzZSBmcm9tIHdlZWsgMjAgdG8gd2VlayAzMiBpbiBNZXhpY28gQ2l0eS4KCiMgTG9jYWwgR2V0aXMtT3JkIEdpKiBhbmFseXNpcwoKIyMgRGVyaXZpbmcgZGlzdGFuY2UtYmFzZWQgd2VpZ2h0IG1hdHJpeCBhbmQgY2FsY3VsYXRpbmcgR2kgc3RhdGlzaXRjcwpgYGB7ciwgZXJyb3I9RkFMU0UsICB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBldmFsPUZBTFNFfQpjZW50cmFsX21leGlfY29vciA8LSByYmluZChjb29yZGluYXRlcyhtZXhpX2NpdHkpLCBjb29yZGluYXRlcyhtZXhpX3N0YXRlKSwgY29vcmRpbmF0ZXMobW9yX3N0YXRlKSkKYGBgCgpUaGUgY29kZSBjaHVuayBiZWxvdyB3aWxsCgoqIGNyZWF0ZSBhICBTaW1wbGUgRmVhdHVyZSBEYXRhRnJhbWUgb2JqZWN0IGZvciBzdG9yaW5nIHRoZSByZXN1bHQKKiBsb29wIGZyb20gd2VlayAxMyB0byB3ZWVrIDMyCiogY3JlYXRlIGEgc3Vic2V0IGFuZCBhc3NpbiB0aGUgc3BlY2lmaWMgd2VlayB0byBpdAoqIGNyZWF0ZSBhZGFwdGl2ZSBwcm94aW1pdHkgbWF0cml4IG9mIHNlYnN0IHVzaW5nIGFkYXB0aXZlIGRpc3RhbmNlIGZ1bmN0aW9uIChrbm4ybmIpCiogY2FsY3VsYXRlIEdpIHN0YXRpc3RpY3MgYmFzZWQgb24gYWRhcHRpdmUgZGlzdGFuY2UgbWF0cml4CiogYmluZCB0byB0aGUgU2ltcGxlIEZlYXR1cmUgRGF0YUZyYW1lIG9iamVjdCB0byBjb21iaW5lIHRoZSByZXN1bHQKCmBgYHtyLCBlcnJvcj1GQUxTRSwgIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGV2YWw9RkFMU0V9CmNlbnRyYWxfbWV4aV9naSA8LSBkYXRhLmZyYW1lKCkKZmlwcyA8LSBvcmRlcihjZW50cmFsX21leGlfd2Vla3Nfc2YkYm9yb3VnaCkKCmZvciAoaSBpbiBjKDEzOjMyKSkgewogIAogIHN1YnNldCA8LSBjZW50cmFsX21leGlfd2Vla3Nfc2YgJT4lIGZpbHRlcih3ZWVrID09IGkpCiAgCiAga25iIDwtIGtubjJuYigKICAgIGtuZWFybmVpZ2goCiAgICAgIGNlbnRyYWxfbWV4aV9jb29yLCAKICAgICAgaz04LCAKICAgICAgbG9uZ2xhdCA9IEZBTFNFCiAgICApLCAKICAgIHJvdy5uYW1lcz1yb3cubmFtZXMoc3Vic2V0JG0pKQogIAogIGtuYl9sdyA8LSBuYjJsaXN0dyhrbmIsIHN0eWxlID0gJ0InKQogIAogIGdpLmFkYXB0aXZlIDwtIGxvY2FsRyhzdWJzZXQkYzE5X3JhdGUsIGtuYl9sdykKICAKICBnaSA8LSBjYmluZChzdWJzZXQsIGFzLm1hdHJpeChnaS5hZGFwdGl2ZSkpCiAgbmFtZXMoZ2kpWzRdIDwtICJnc3RhdF9hZGFwdGl2ZSIKICAKICBjZW50cmFsX21leGlfZ2kgPC0gcmJpbmQoY2VudHJhbF9tZXhpX2dpLCBnaSkKfQoKaGVhZChjZW50cmFsX21leGlfZ2kpCmBgYAoKIyMgTWFwcGluZyBHaSB2YWx1ZXMgYmFzZWQgb24gYWRhcHRpdmUgZGlzdGFuY2Ugd2VpZ2h0cwoKYGBge3IsIGVycm9yPUZBTFNFLCAgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZXZhbD1GQUxTRX0KdG1fc2hhcGUoY2VudHJhbF9tZXhpX2dpKSArCiAgdG1fZmlsbChjb2wgPSAiZ3N0YXRfYWRhcHRpdmUiLAogICAgICAgICAgc3R5bGUgPSAicHJldHR5IiwKICAgICAgICAgIHBhbGV0dGUgPSAiLVJkWWxCdSIsCiAgICAgICAgICB0aXRsZSA9ICJMb2NhbCBHaSBCYXNlZCBvbiBcbkFkYXB0aXZlIERpc3RhbmNlIFdlaWdodHMiKSArCiAgdG1fZmFjZXRzKCJ3ZWVrIiwgbmNvbD0xLCBucm93PTEpICsKICB0bV9ib3JkZXJzKGFscGhhID0gMC41KQpgYGAKIVtMb2NhbCBHaSBCYXNlZCBvbiBBZGFwdGl2ZSBEaXN0YW5jZSBXZWlnaHRzXSguLi9kYXRhL2ltZy9tZ2kuZ2lmKQoKRnJvbSB0aGUgbG9jYWwgR2kgZ3JhcGggd2UgY2FuIHNlZSB0aGF0IHRoZSBob3Qgc3BvdCBhcmVhcyB3aGljaCByZXByZXNlbnRzIGhpZ2ggQ09WSUQtMTkgcmF0ZSBpcyBsb2NhdGVkIGF0IE1leGljbyBDaXR5LiBGdXJ0aGVybW9yZSwgdGhlIG5laWdoYm9yIGJvcm91Z2hzIHN1cnJvdW5kIE1leGljbyBDaXR5IGFsc28gaGF2ZSBhbiBpbmNyZWFzZSBvZiBsb2NhbCBHaSB2YWx1ZSB0aHJvdWdoIHRoZSB3ZWVrcyB3ZSBzdHVkeS4gVGhlIGNvbGQgc3BvdCBhcmVhcywgb24gdGhlIG90aGVyIGhhbmQsIG1haW5seSBjb21wcmlzZSBvZiBjb3VudGllcyBsb2NhdGVkIG9uIHRoZSBzb3V0aC13ZXN0ZXJuIHBhcnQgb2YgY2VudHJhbCBNZXhpY28uCgojIFJlZmVyZW5jZXMKKiA8aHR0cHM6Ly9ycHVicy5jb20vd3lubG8vSVM0MTVfVGFrZS1ob21lX0V4MDI+Cgo=