Loading Libraries
library(raster)
library(soilP)
library(dplyr)
library(tidyr)
library(Hmisc)
library(MASS)
library(ggplot2)
library(ade4)
Reading ISRIC 2011 data
Starting with the base raster file
First we load the raster.
data_dir <- system.file("data", package = "soilP", mustWork = TRUE)
extdata_dir <- system.file("extdata", package = "soilP", mustWork = TRUE)
FAO74_tif <- file.path(extdata_dir,"ISRIC2011", "FAO74.tif")
# Initializing ISRIC2011 list with the base map
ISRIC2011 <- list()
ISRIC2011$FAO74 <- raster::raster(FAO74_tif)
This raster has a single layer and its value corresponds to the soil map unit identifier (ID) for the Soil Map of the World, FAO74. The same identifier can be found in more recent versions of the FAO soil map and in the harmonized soil database.
Plotting FAO Soil Map Unit Raster
nb_plot(ISRIC2011$FAO74, main = "Soil Map of the World, FAO74")

The MUIDs have the lowest values in Africa and then ascend through Asia, the Americas, Oceania and attain the highest values in Europe. Contrary to what the file name seems to imply these ids do not directly represent the phosphorus retention potential. Batjes used the ids and a series of SQL queries to assign phosphorus retention potential classes to each map unit, which then are easily projected over each pixel in the map.
These main classes can be extracted from the map unit raster, and the tables in the mdb files. These tables function as raster attribute tables.
Adding Raster Attribute Table, including Map Unit P Retention Potential
levels<-
S3 method is internal to raster
and it could not be exported to soilP
, which prevented me from making a working package function out of the following snippet:
### Get Raster Attribute Tables
ISRIC_AT <- list()
ISRIC_AT <- read_ISRIC_AT(mdb_file)
### Adding ID Raster Attribute Table to FAO74
ISRIC2011$FAO74 <- raster::raster(FAO74_tif)
ISRIC2011$FAO74 <- raster::ratify(ISRIC2011$FAO74)
levels(ISRIC2011$FAO74) <- ISRIC_AT$mapunit
the number of rows in the raster attributes (factors) data.frame is unexpectedlonger object length is not a multiple of shorter object lengththe values in the "ID" column in the raster attributes (factors) data.frame have changed
Soil Map Phosphorus Retention Potential as Percentages of Map Units
A phosphorus retention potential class was assigned to each of the FAO74 soil units (Low, Moderate, High and very High). Each map unit is composed of up to 8 soil units in different area percentages. Depending on the composition of the map unit and each component soil unit phosphorus retention potential class a map unit class was assigned. In total 260 different phosphorus retention potential classes were assigned to the 4923 map units. These were further grouped into 16 main phosphorus retention potential classes.
pct_vars <- c("Lo", "Mo", "Hi", "VH", "MISC")
ISRIC2011 <- c(ISRIC2011, layers_from(ISRIC2011$FAO74,cols = pct_vars))
for (varname in pct_vars) {
out_tif <- file.path( extdata_dir, "ISRIC2011", paste0(varname,".tif"))
writeRaster(ISRIC2011[[varname]],
file = out_tif,
datatype = 'INT1U',
overwrite = TRUE)
nb_plot(ISRIC2011[[varname]], main = varname)
}





Rebuilding the arcgis render appeareance
We will use a manually curated raster attribute table stored in th soilclass
dataframe.
See the manual for details.
Adding Raster Attribute Table to ISRIC2011$FAO74
First we need to merge soilclass
to the Soil Map Unit id number, then we add this as the RAT to the FAO74 raster.
### Adding ID Raster Attribute Table to FAO74
ISRIC_AT$mu_soilclass <- ISRIC_AT$mapunit %>%
dplyr::left_join(soilclass, by = c("mainclass" = "main"))
Column `mainclass`/`main` joining factors with different levels, coercing to character vector
levels(ISRIC2011$FAO74) <- ISRIC_AT$mu_soilclass
Reclassifying the FAO74 soil map into ascending Phosphorus Retention Classes
I manually assigned integers in ascending order to the ascending
column in the soilclass
dataframe as follows: For the Low map unit main class, higher percentage of Low soil unit P retention corresponded to lower integers. For the Moderate class, a multivariate analysis reveled that Mo2 has a higher content of Low souil units,snd simirlaly Mo3 has a greater content of Hi soil units tan Mo1, which suggests a Mo2, Mo1, Mo3, ascending order. For the High, and very High map unit main classes, higher integers were assigned to higher percentage of corresponding soil unit P retention. This means the higher the Low soil unit P retention percentage the lower the integer, and complentarily the higher the High, and Very High soil unit P retention percentage the higher the integer, except in the Moderate class that behaves unexpectedly.
ISRIC2011$main <- layers_from(ISRIC2011$FAO74, cols = "ascending")[[1]]
raster::writeRaster(
ISRIC2011$main,
file.path(extdata_dir,"ISRIC2011","main.tif"),
datatype = 'INT1U',
overwrite = TRUE)
Plotting Soil Phosphorus Retention Potential Raster
nb_plot(ISRIC2011$main,
axis.args = list(breaks=0:15, at = 0:15, cex.axis = 2),
main = "Phosphorus Retention Potential, ISRIC2011")

Now the numbers do represent the phosphorus retention potential!!!
However, the raster
plot legend above assumes a continuous scale from 0 to 15, while the data is explicitly a categorical variable (although derived from continuous percentages, see above).
# Raster Attribute Table
ISRIC2011$main <- raster::ratify(ISRIC2011$main)
# Assign RAT to main class raster
levels(ISRIC2011$main) <- rat(ISRIC2011$main) %>%
dplyr::inner_join(soilclass, by = c("ID" = "ascending"))
Adding arcgis render color and saving the map as full resolution geotif
main_color <- layers_from(ISRIC2011$FAO74, cols = c("r","g","b"))
plotRGB(stack(main_color))

writeRaster(stack(main_color),
filename = file.path(extdata_dir, "ISRIC2011","main_color.tif"),
datatype = "INT1U",
options = "TFW=YES",
format = "GTiff",
overwrite = TRUE)
Using rasterVis to appropiately label categories and add legend
Remember that we assigned to the to the phosphorus retention potential main classes an ad hoc order so we can interpret a direction of ascending retention potential (see above) in the plot legend.
In order to show the right labels in this ascending scale we use the levelplot
function from RasterVis
that uses the raster attribute table to transform the numerical value of the raster to discrete categories.
rasterVis::levelplot(
ISRIC2011$main, att = "main",
col.regions = soilclass$color_hex[1:16],
maxpixels = ncell(ISRIC2011$main),
scales = list(draw = FALSE),
xlab = NULL, ylab = NULL,
main = "Generalized Phosphorus Retention Potential Map")

Multivariate analysis of Soil retention Potential
We should establish a data based order of map unit soil phosphorus retention classes instead of postulating an ad hoc order. This can be done through selecting the first discriminant dimension from a Discriminant Analysis of the percentage of soil unit phosphorus retention classes per mapping unit. Furthermore, we can use that discriminant dimention as a continuous variable instead of the discrete classes for downstream analyses
Map Unit Soil Composition Matrix and entropy
Each soil unit percentage can be used as a map unit descriptor variable for multivariate analysis of the phosphorus retention potential per map unit.
ISRIC_AT <- within(ISRIC_AT, {
su_share <- soil_composition(mapunit,FAO74)
su_share <- as.data.frame(
cbind( ID = mapunit$ID,
su_share,
soilS = row_entropy(su_share/100)))
})
However this results in sparse matrix with essentially orthogonal columns.
Phosphorus Retention, Soil Unit Composition Join table
phychem_vars <- c("pH","SAND","SILT","CLAY","CECLAY")
su_types <- colnames(ISRIC_AT$su_share)
ret_vars <- c("ID",
"mainclass",
phychem_vars,
pct_vars,
su_types)
levels <- soilclass %>%
dplyr::arrange(ascending) %>%
dplyr::select(main)
ISRIC_AT <- within(ISRIC_AT, {
ret_share <- mapunit %>%
dplyr::left_join(soilunit, by = c("SOIL1" = "key")) %>%
dplyr::left_join(su_share, by = "ID") %>%
dplyr::select(!!!ret_vars)
ret_share$mainclass <- factor(ret_share$mainclass, levels = levels$main[1:16])
})
Column `SOIL1`/`key` joining factors with different levels, coercing to character vector
Soil and Phosphorus Retention Class Composition Entropy
ISRIC_AT <- within(ISRIC_AT, {
ret_share$pS <- row_entropy(ret_share[,pct_vars] / 100)
})
with(ISRIC_AT,{
boxplot(soilS ~ mainclass, data = ret_share, las = 2,
main = "Map Unit Soil Composition Entropy")
boxplot(pS ~ mainclass, data = ret_share, las = 2,
main = "Phosphorus Retention Class Composition Entropy")
})


Exploratory Discriminant Analysis using ade4
Soil Retention Potential Space is essentially a tetrahedron with Moderate retention at its center.Only three degrees of freedom, Lo-VH, Mo-Hi, Lo-Hi.
# create dudi object for discriminant analysis
soil_idx <- with(ISRIC_AT$ret_share,{
which(is_soil(mainclass))
})
soil_data <- ISRIC_AT$ret_share[soil_idx,-1]
soil_data$mainclass <- with(ISRIC_AT$ret_share,{
factor(mainclass[soil_idx], levels = levels$main[5:16])
})
pca <- dudi.pca(soil_data[,-1], scannf = FALSE, nf = 139)
dis <- discrimin(pca, fac = soil_data$mainclass, scannf = FALSE, nf = 10)
palette <- soilclass$color_hex[5:16]
color <- palette[soil_data$mainclass]
# Select most important variables
disva <- as.data.frame(dis$va)
disva$idx <- (1:nrow(dis$va)) / 1000
major_vars <- 1000 * disva %>%
dplyr::filter_all(any_vars(abs(.) > 0.5)) %>%
dplyr::select(idx)
# Plot
par(mfrow = c(2, 2))
s.class(dis$li, xax = 2, yax = 1, fac = soil_data$mainclass, col = palette)
s.class(dis$li, xax = 3, yax = 1, fac = soil_data$mainclass, col = palette)
s.class(dis$li, xax = 3, yax = 2, fac = soil_data$mainclass, col = palette)
s.corcircle(dis$va[major_vars$idx,])

Linearization of Phosphorus Retention Potential with MASS
LDA
The MASS
LDA wiill use all info by default.
fit <- MASS::lda(mainclass ~ ., data = soil_data)
variables are collinear
plda <- predict(object = fit, # predictions
newdata = ISRIC_AT$ret_share[,-c(1,2)])
From categorical variable to continous “linear” scale. Linear combination of map unit soil content.
ret_scale<- NA
ISRIC_AT <- within(ISRIC_AT,{
ret_scale <- ret_share[,c("ID",pct_vars)]
ret_scale$ret <- ret_share$mainclass
ret_scale[!is_soil(ret_scale$ret), pct_vars] <- NA
})
ISRIC_AT$ret_scale <- within(ISRIC_AT$ret_scale,{
# Weighted scale, ad hoc weights!!!!!!
weighted <- as.double((Lo + 2 * Mo + 3 * Hi + 4 * VH) / 400)
# Linear Discrimant scale, post hoc weights/coefficients
LD1 <- plda$x[,1]
LD2 <- -plda$x[,2] # sign manually adjusted
LD3 <- -plda$x[,3] # sign manually adjusted
LD1[!is_soil(ret)] <- NA
LD2[!is_soil(ret)] <- NA
LD3[!is_soil(ret)] <- NA
})
with(ISRIC_AT,{
plot_P_scales(ret_scale[soil_idx,], palette = palette,
scale_x = "LD2",scale_y = "weighted")
})

ld_vars <- c("weighted", "LD1", "LD2", "LD3")
ISRIC_AT <- within(ISRIC_AT,{
raster_scale <- mu_soilclass[,c("ID",pct_vars)] %>%
dplyr::left_join(ret_scale[,c("ID",ld_vars)],
by = c("ID" = "ID")) %>%
dplyr::mutate_if(colnames(.) %in% ld_vars, scale256)
})
Linear Discriminant Maps
levels(ISRIC2011$FAO74) <- ISRIC_AT$raster_scale
ISRIC2011 <- c(ISRIC2011, layers_from(ISRIC2011$FAO74, cols = ld_vars))
for(varname in ld_vars) {
out_tif <- file.path( extdata_dir, "ISRIC2011", paste0(varname,".tif"))
writeRaster(ISRIC2011[[varname]],
file = out_tif,
datatype = 'INT1U',
overwrite = TRUE)
nb_plot(ISRIC2011[[varname]], main = varname)
}




# finally save ISRIC2011 in .RData format
save(ISRIC_AT, file = file.path(data_dir, "ISRIC_AT.RData"))
save(ISRIC2011, file = file.path(data_dir, "ISRIC2011.RData"))
False Color Combination of Linear Discriminants
LD_composite <- stack(ISRIC2011[c("LD2","LD1","LD3")])
op <- par()
par(mar = c(0, 0, 0, 0), oma = c(0, 0, 0, 0))
plotRGB(LD_composite)
par(op)

out_tif <- file.path(extdata_dir,"ISRIC2011","LD_composite.tif")
writeRaster(LD_composite, filename = out_tif,
datatype = "INT1U",
options = "TFW=YES",
format = "GTiff",
overwrite = TRUE)
LS0tCnRpdGxlOiAiUmVhZGluZyBJU1JJQyBQaG9zcGhvcnVzIFJldGVudGlvbiBEYXRhIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIGhpZ2hsaWdodDogdGFuZ28KICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQogICAgdGhlbWU6IHNwYWNlbGFiCiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQotLS0KCiMgTG9hZGluZyBMaWJyYXJpZXMKCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCByZXN1bHRzPSJoaWRlIn0KbGlicmFyeShyYXN0ZXIpCmxpYnJhcnkoc29pbFApCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkoSG1pc2MpCmxpYnJhcnkoTUFTUykKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGFkZTQpCmBgYAojIFJlYWRpbmcgSVNSSUMgMjAxMSBkYXRhCiMjIFN0YXJ0aW5nIHdpdGggdGhlIGJhc2UgcmFzdGVyIGZpbGUKRmlyc3Qgd2UgbG9hZCB0aGUgcmFzdGVyLiAKYGBge3J9CmRhdGFfZGlyICAgIDwtIHN5c3RlbS5maWxlKCJkYXRhIiwgICAgcGFja2FnZSA9ICJzb2lsUCIsIG11c3RXb3JrID0gVFJVRSkKZXh0ZGF0YV9kaXIgPC0gc3lzdGVtLmZpbGUoImV4dGRhdGEiLCBwYWNrYWdlID0gInNvaWxQIiwgbXVzdFdvcmsgPSBUUlVFKQoKRkFPNzRfdGlmIDwtIGZpbGUucGF0aChleHRkYXRhX2RpciwiSVNSSUMyMDExIiwgIkZBTzc0LnRpZiIpCgojIEluaXRpYWxpemluZyBJU1JJQzIwMTEgbGlzdCB3aXRoIHRoZSBiYXNlIG1hcApJU1JJQzIwMTEgPC0gbGlzdCgpCklTUklDMjAxMSRGQU83NCA8LSByYXN0ZXI6OnJhc3RlcihGQU83NF90aWYpCgpgYGAKClRoaXMgcmFzdGVyIGhhcyBhIHNpbmdsZSBsYXllciBhbmQgaXRzICB2YWx1ZSBjb3JyZXNwb25kcyB0byB0aGUgc29pbCBtYXAgdW5pdCAKaWRlbnRpZmllciAoSUQpIGZvciB0aGUgU29pbCBNYXAgb2YgdGhlIFdvcmxkLCBGQU83NC4gVGhlIHNhbWUgaWRlbnRpZmllciBjYW4KYmUgZm91bmQgaW4gbW9yZSByZWNlbnQgdmVyc2lvbnMgb2YgdGhlIEZBTyBzb2lsIG1hcCBhbmQgaW4gdGhlIGhhcm1vbml6ZWQgc29pbCAKZGF0YWJhc2UuCgojIyBQbG90dGluZyBGQU8gU29pbCBNYXAgVW5pdCBSYXN0ZXIKYGBge3IsIGZpZy5oZWlnaHQ9NCwgZmlnLndpZHRoPTEwLCB3YXJuaW5nPUZBTFNFfQpuYl9wbG90KElTUklDMjAxMSRGQU83NCwgbWFpbiA9ICJTb2lsIE1hcCBvZiB0aGUgV29ybGQsIEZBTzc0IikKYGBgCgpUaGUgTVVJRHMgaGF2ZSB0aGUgbG93ZXN0IHZhbHVlcyBpbiBBZnJpY2EgYW5kIHRoZW4gYXNjZW5kIHRocm91Z2ggQXNpYSwKdGhlIEFtZXJpY2FzLCBPY2VhbmlhIGFuZCBhdHRhaW4gdGhlIGhpZ2hlc3QgdmFsdWVzIGluIEV1cm9wZS4KQ29udHJhcnkgdG8gd2hhdCB0aGUgZmlsZSBuYW1lIHNlZW1zIHRvIGltcGx5IHRoZXNlIGlkcyBkbyBub3QgZGlyZWN0bHkKcmVwcmVzZW50ICB0aGUgcGhvc3Bob3J1cyByZXRlbnRpb24gcG90ZW50aWFsLiBCYXRqZXMgdXNlZCB0aGUgaWRzIGFuZCAKYSBzZXJpZXMgb2YgU1FMIHF1ZXJpZXMgdG8gYXNzaWduIHBob3NwaG9ydXMgcmV0ZW50aW9uIHBvdGVudGlhbCBjbGFzc2VzCnRvIGVhY2ggbWFwIHVuaXQsIHdoaWNoIHRoZW4gYXJlIGVhc2lseSBwcm9qZWN0ZWQgb3ZlciBlYWNoIHBpeGVsIGluIHRoZQptYXAuCgpUaGVzZSBtYWluIGNsYXNzZXMgY2FuIGJlIGV4dHJhY3RlZCBmcm9tIHRoZSBtYXAgdW5pdCByYXN0ZXIsIGFuZCB0aGUgdGFibGVzIGluCnRoZSBtZGIgZmlsZXMuIFRoZXNlIHRhYmxlcyBmdW5jdGlvbiBhcyByYXN0ZXIgYXR0cmlidXRlIHRhYmxlcy4KCiMjIEV4dHJhY3RpbmcgUmFzdGVyIEF0dHJpYnV0ZSBUYWJsZSBmcm9tIE1pY3Jvc29mdCBBY2Nlc3MgZGF0YWJhc2UgKGBtZGJgIGZpbGUpLgoKRm9yIHRoaXMgSSBuZWVkIEhtaXNjIGFuZCB0aGUgT0RCQyBkcml2ZXJzLiBNYWMgaW5zdGFsbGF0aW9uIGlzIGZyb20gYnJldy4KClRoZSBNUyBBY2Nlc3MgZGF0YWJhc2UgaGFzIHRoZSB0YWJsZXMgZm9yIG1hcHBpbmcgdGhlIHNvaWwgdW5pdCBpZGVudGlmaWVycyB0bwpzb2lsIHByb3BlcnRpZXMsIGkuZS4gcGhvc3Bob3J1cyByZXRlbnRpb24gYXMgc29pbCB1bml0IGNvbXBvc2l0aW9uIHBlcmNlbnRhZ2UsIApvciBwaHlzaWNvY2hlbWljYWwgdmFyaWFibGVzLgoKYGBge3J9CgptZGJfZmlsZSA8LSBmaWxlLnBhdGgoCiAgZXh0ZGF0YV9kaXIsCiAgIklTUklDMjAxMSIsCiAgIklTUklDX1Bob3NwaG9ydXNfUmV0ZW50aW9uX1BvdGVudGlhbC5tZGIiKQpgYGAKCiMjIEFkZGluZyBSYXN0ZXIgQXR0cmlidXRlIFRhYmxlLCBpbmNsdWRpbmcgTWFwIFVuaXQgUCBSZXRlbnRpb24gUG90ZW50aWFsIAoKYGxldmVsczwtYCBTMyBtZXRob2QgaXMgaW50ZXJuYWwgdG8gYHJhc3RlcmAgYW5kIGl0IGNvdWxkIG5vdCBiZQpleHBvcnRlZCAgdG8gYHNvaWxQYCwgd2hpY2ggcHJldmVudGVkIG1lIGZyb20gbWFraW5nIGEgd29ya2luZyBwYWNrYWdlIGZ1bmN0aW9uCm91dCBvZiB0aGUgZm9sbG93aW5nIHNuaXBwZXQ6CgoKYGBge3J9CgojIyMgR2V0IFJhc3RlciBBdHRyaWJ1dGUgVGFibGVzCklTUklDX0FUIDwtIGxpc3QoKQoKSVNSSUNfQVQgPC0gcmVhZF9JU1JJQ19BVChtZGJfZmlsZSkKCiMjIyBBZGRpbmcgSUQgUmFzdGVyIEF0dHJpYnV0ZSBUYWJsZSB0byBGQU83NApJU1JJQzIwMTEkRkFPNzQgPC0gcmFzdGVyOjpyYXN0ZXIoRkFPNzRfdGlmKQpJU1JJQzIwMTEkRkFPNzQgPC0gcmFzdGVyOjpyYXRpZnkoSVNSSUMyMDExJEZBTzc0KQoKbGV2ZWxzKElTUklDMjAxMSRGQU83NCkgPC0gSVNSSUNfQVQkbWFwdW5pdAoKYGBgCiMgU29pbCBNYXAgUGhvc3Bob3J1cyBSZXRlbnRpb24gUG90ZW50aWFsIGFzIFBlcmNlbnRhZ2VzIG9mIE1hcCBVbml0cwoKQSBwaG9zcGhvcnVzIHJldGVudGlvbiBwb3RlbnRpYWwgY2xhc3Mgd2FzIGFzc2lnbmVkIHRvIGVhY2ggb2YgdGhlIEZBTzc0CnNvaWwgdW5pdHMgKExvdywgTW9kZXJhdGUsIEhpZ2ggYW5kIHZlcnkgSGlnaCkuIApFYWNoIG1hcCB1bml0IGlzIGNvbXBvc2VkIG9mIHVwIHRvIDggc29pbCB1bml0cyBpbiBkaWZmZXJlbnQgYXJlYSBwZXJjZW50YWdlcy4KRGVwZW5kaW5nIG9uIHRoZSBjb21wb3NpdGlvbiBvZiB0aGUgbWFwIHVuaXQgYW5kIGVhY2ggY29tcG9uZW50IHNvaWwgdW5pdCAKcGhvc3Bob3J1cyByZXRlbnRpb24gcG90ZW50aWFsIGNsYXNzIGEgIG1hcCB1bml0IGNsYXNzIHdhcyBhc3NpZ25lZC4KSW4gdG90YWwgMjYwIGRpZmZlcmVudCBwaG9zcGhvcnVzIHJldGVudGlvbiBwb3RlbnRpYWwgY2xhc3NlcyB3ZXJlIGFzc2lnbmVkIHRvCnRoZSA0OTIzIG1hcCB1bml0cy4gVGhlc2Ugd2VyZSBmdXJ0aGVyIGdyb3VwZWQgaW50byAxNiBtYWluIHBob3NwaG9ydXMgcmV0ZW50aW9uCnBvdGVudGlhbCBjbGFzc2VzLgoKYGBge3IsIGZpZy5oZWlnaHQ9NCwgZmlnLndpZHRoPTEwLCB3YXJuaW5nPUZBTFNFfQoKcGN0X3ZhcnMgPC0gYygiTG8iLCAiTW8iLCAiSGkiLCAiVkgiLCAiTUlTQyIpIAoKSVNSSUMyMDExIDwtIGMoSVNSSUMyMDExLCBsYXllcnNfZnJvbShJU1JJQzIwMTEkRkFPNzQsY29scyA9IHBjdF92YXJzKSkKCmZvciAodmFybmFtZSBpbiBwY3RfdmFycykgewogIG91dF90aWYgPC0gZmlsZS5wYXRoKCBleHRkYXRhX2RpciwgIklTUklDMjAxMSIsIHBhc3RlMCh2YXJuYW1lLCIudGlmIikpCiAgICAKICB3cml0ZVJhc3RlcihJU1JJQzIwMTFbW3Zhcm5hbWVdXSwKICAgICAgICAgICAgICBmaWxlID0gb3V0X3RpZiwKICAgICAgICAgICAgICBkYXRhdHlwZSA9ICdJTlQxVScsCiAgICAgICAgICAgICAgb3ZlcndyaXRlID0gVFJVRSkKICAgIAogIG5iX3Bsb3QoSVNSSUMyMDExW1t2YXJuYW1lXV0sIG1haW4gPSAgdmFybmFtZSkKfQoKCmBgYAoKIyBSZWJ1aWxkaW5nIHRoZSBhcmNnaXMgcmVuZGVyIGFwcGVhcmVhbmNlIAoKV2Ugd2lsbCB1c2UgYSBtYW51YWxseSBjdXJhdGVkIHJhc3RlciBhdHRyaWJ1dGUgdGFibGUgc3RvcmVkIGluIHRoIGBzb2lsY2xhc3NgCmRhdGFmcmFtZS4KClNlZSB0aGUgbWFudWFsIGZvciBkZXRhaWxzLgpgYGB7cn0KP3NvaWxjbGFzcwpgYGAKCiMjIEFkZGluZyBSYXN0ZXIgQXR0cmlidXRlIFRhYmxlIHRvICBgSVNSSUMyMDExJEZBTzc0YAoKRmlyc3Qgd2UgbmVlZCB0byAgbWVyZ2UgYHNvaWxjbGFzc2AgdG8gdGhlIFNvaWwgTWFwIFVuaXQgaWQgbnVtYmVyLCB0aGVuIHdlIGFkZCB0aGlzIGFzIHRoZSBSQVQgdG8gdGhlIEZBTzc0IHJhc3Rlci4KCgpgYGB7cn0KCiMjIyBBZGRpbmcgSUQgUmFzdGVyIEF0dHJpYnV0ZSBUYWJsZSB0byBGQU83NAoKSVNSSUNfQVQkbXVfc29pbGNsYXNzIDwtIElTUklDX0FUJG1hcHVuaXQgJT4lIAogICAgZHBseXI6OmxlZnRfam9pbihzb2lsY2xhc3MsIGJ5ID0gYygibWFpbmNsYXNzIiA9ICJtYWluIikpCgpsZXZlbHMoSVNSSUMyMDExJEZBTzc0KSA8LSBJU1JJQ19BVCRtdV9zb2lsY2xhc3MKYGBgCgojIyAgUmVjbGFzc2lmeWluZyB0aGUgRkFPNzQgc29pbCBtYXAgaW50byBhc2NlbmRpbmcgUGhvc3Bob3J1cyBSZXRlbnRpb24gQ2xhc3NlcwoKSSBtYW51YWxseSBhc3NpZ25lZCBpbnRlZ2VycyBpbiBhc2NlbmRpbmcgb3JkZXIgdG8gdGhlCmBhc2NlbmRpbmdgIGNvbHVtbiBpbiB0aGUgYHNvaWxjbGFzc2AgZGF0YWZyYW1lIGFzIGZvbGxvd3M6IEZvciB0aGUgTG93IG1hcCB1bml0Cm1haW4gY2xhc3MsIGhpZ2hlciBwZXJjZW50YWdlIG9mIExvdyBzb2lsIHVuaXQgUCByZXRlbnRpb24gY29ycmVzcG9uZGVkIHRvIGxvd2VyCmludGVnZXJzLiBGb3IgdGhlIE1vZGVyYXRlIGNsYXNzLCBhIG11bHRpdmFyaWF0ZSBhbmFseXNpcyByZXZlbGVkIHRoYXQgTW8yIGhhcyBhCmhpZ2hlciBjb250ZW50IG9mIExvdyBzb3VpbCB1bml0cyxzbmQgIHNpbWlybGFseSBNbzMgaGFzIGEgZ3JlYXRlciBjb250ZW50IG9mIEhpCnNvaWwgdW5pdHMgdGFuIE1vMSwgd2hpY2ggc3VnZ2VzdHMgYSBNbzIsIE1vMSwgTW8zLCBhc2NlbmRpbmcgb3JkZXIuCkZvciB0aGUgSGlnaCwgYW5kIHZlcnkgSGlnaCBtYXAgdW5pdCBtYWluIGNsYXNzZXMsIGhpZ2hlciBpbnRlZ2VycyB3ZXJlIGFzc2lnbmVkCnRvIGhpZ2hlciBwZXJjZW50YWdlIG9mIGNvcnJlc3BvbmRpbmcgc29pbCB1bml0IFAgcmV0ZW50aW9uLiAKVGhpcyBtZWFucyB0aGUgaGlnaGVyIHRoZSBMb3cgc29pbCB1bml0IFAgcmV0ZW50aW9uIHBlcmNlbnRhZ2UgdGhlIGxvd2VyIAp0aGUgaW50ZWdlciwgYW5kIGNvbXBsZW50YXJpbHkgdGhlIGhpZ2hlciB0aGUgSGlnaCwgYW5kIFZlcnkgCkhpZ2ggc29pbCB1bml0IFAgcmV0ZW50aW9uIHBlcmNlbnRhZ2UgdGhlIGhpZ2hlciB0aGUgaW50ZWdlciwgZXhjZXB0IGluIHRoZQpNb2RlcmF0ZSBjbGFzcyB0aGF0IGJlaGF2ZXMgdW5leHBlY3RlZGx5LgoKCmBgYHtyfQoKSVNSSUMyMDExJG1haW4gPC0gbGF5ZXJzX2Zyb20oSVNSSUMyMDExJEZBTzc0LCBjb2xzID0gImFzY2VuZGluZyIpW1sxXV0KCnJhc3Rlcjo6d3JpdGVSYXN0ZXIoCiAgSVNSSUMyMDExJG1haW4sCiAgZmlsZS5wYXRoKGV4dGRhdGFfZGlyLCJJU1JJQzIwMTEiLCJtYWluLnRpZiIpLAogIGRhdGF0eXBlID0gJ0lOVDFVJywKICBvdmVyd3JpdGUgPSBUUlVFKQoKYGBgCgojIyBQbG90dGluZyBTb2lsIFBob3NwaG9ydXMgUmV0ZW50aW9uIFBvdGVudGlhbCBSYXN0ZXIKCmBgYHtyLCBmaWcuaGVpZ2h0PTQsIGZpZy53aWR0aD0xMCwgd2FybmluZz1GQUxTRX0KCm5iX3Bsb3QoSVNSSUMyMDExJG1haW4sCiAgICAgICAgYXhpcy5hcmdzID0gbGlzdChicmVha3M9MDoxNSwgYXQgPSAwOjE1LCBjZXguYXhpcyA9IDIpLAogICAgICAgIG1haW4gPSAgIlBob3NwaG9ydXMgUmV0ZW50aW9uIFBvdGVudGlhbCwgSVNSSUMyMDExIikKCmBgYAoKTm93IHRoZSBudW1iZXJzIGRvIHJlcHJlc2VudCB0aGUgcGhvc3Bob3J1cyByZXRlbnRpb24gcG90ZW50aWFsISEhCgpIb3dldmVyLCB0aGUgYHJhc3RlcmAgcGxvdCBsZWdlbmQgYWJvdmUgYXNzdW1lcyBhIGNvbnRpbnVvdXMgc2NhbGUgZnJvbSAwIHRvIDE1LAp3aGlsZSB0aGUgZGF0YSBpcyBleHBsaWNpdGx5IGEgY2F0ZWdvcmljYWwgdmFyaWFibGUgKGFsdGhvdWdoIGRlcml2ZWQgZnJvbQpjb250aW51b3VzIHBlcmNlbnRhZ2VzLCBzZWUgYWJvdmUpLiAKCgpgYGB7cn0KIyBSYXN0ZXIgQXR0cmlidXRlIFRhYmxlCklTUklDMjAxMSRtYWluIDwtIHJhc3Rlcjo6cmF0aWZ5KElTUklDMjAxMSRtYWluKQojIEFzc2lnbiBSQVQgdG8gbWFpbiBjbGFzcyByYXN0ZXIKbGV2ZWxzKElTUklDMjAxMSRtYWluKSA8LSByYXQoSVNSSUMyMDExJG1haW4pICU+JSAKICAgIGRwbHlyOjppbm5lcl9qb2luKHNvaWxjbGFzcywgYnkgPSBjKCJJRCIgPSAiYXNjZW5kaW5nIikpCmBgYAoKIyMgQWRkaW5nIGFyY2dpcyByZW5kZXIgY29sb3IgYW5kIHNhdmluZyB0aGUgbWFwIGFzIGZ1bGwgcmVzb2x1dGlvbiBnZW90aWYKCmBgYHtyLCBmaWcuaGVpZ2h0PTQsIGZpZy53aWR0aD0xMCwgd2FybmluZz1GQUxTRX0KCm1haW5fY29sb3IgPC0gbGF5ZXJzX2Zyb20oSVNSSUMyMDExJEZBTzc0LCBjb2xzID0gYygiciIsImciLCJiIikpCgpwbG90UkdCKHN0YWNrKG1haW5fY29sb3IpKQoKd3JpdGVSYXN0ZXIoc3RhY2sobWFpbl9jb2xvciksCiAgZmlsZW5hbWUgID0gZmlsZS5wYXRoKGV4dGRhdGFfZGlyLCAiSVNSSUMyMDExIiwibWFpbl9jb2xvci50aWYiKSwKICBkYXRhdHlwZSAgPSAiSU5UMVUiLAogIG9wdGlvbnMgICA9ICJURlc9WUVTIiwKICBmb3JtYXQgICAgPSAiR1RpZmYiLAogIG92ZXJ3cml0ZSA9IFRSVUUpCmBgYAoKIyMgVXNpbmcgcmFzdGVyVmlzIHRvIGFwcHJvcGlhdGVseSBsYWJlbCBjYXRlZ29yaWVzIGFuZCBhZGQgbGVnZW5kIApSZW1lbWJlciB0aGF0IHdlIGFzc2lnbmVkIHRvIHRoZSB0byB0aGUgcGhvc3Bob3J1cyByZXRlbnRpb24gcG90ZW50aWFsIG1haW4KY2xhc3NlcyBhbiAqYWQgaG9jKiBvcmRlciBzbyB3ZSBjYW4gaW50ZXJwcmV0IGEgZGlyZWN0aW9uIG9mIGFzY2VuZGluZyByZXRlbnRpb24gCnBvdGVudGlhbCAoc2VlIGFib3ZlKSBpbiB0aGUgcGxvdCBsZWdlbmQuCgpJbiBvcmRlciB0byBzaG93IHRoZSByaWdodCBsYWJlbHMgaW4gdGhpcyBhc2NlbmRpbmcgc2NhbGUgd2UgdXNlIHRoZSBgbGV2ZWxwbG90YApmdW5jdGlvbiBmcm9tIGBSYXN0ZXJWaXNgIHRoYXQgdXNlcyB0aGUgcmFzdGVyIGF0dHJpYnV0ZSB0YWJsZSB0byB0cmFuc2Zvcm0gdGhlIApudW1lcmljYWwgdmFsdWUgb2YgdGhlIHJhc3RlciB0byBkaXNjcmV0ZSBjYXRlZ29yaWVzLgoKCmBgYHtyfQpyYXN0ZXJWaXM6OmxldmVscGxvdCgKICBJU1JJQzIwMTEkbWFpbiwgYXR0ID0gIm1haW4iLAogIGNvbC5yZWdpb25zID0gc29pbGNsYXNzJGNvbG9yX2hleFsxOjE2XSwKICBtYXhwaXhlbHMgPSBuY2VsbChJU1JJQzIwMTEkbWFpbiksCiAgc2NhbGVzID0gbGlzdChkcmF3ID0gRkFMU0UpLAogIHhsYWIgPSBOVUxMLCB5bGFiID0gTlVMTCwKICBtYWluID0gIkdlbmVyYWxpemVkIFBob3NwaG9ydXMgUmV0ZW50aW9uIFBvdGVudGlhbCBNYXAiKQoKYGBgCgoKCiMgTXVsdGl2YXJpYXRlIGFuYWx5c2lzIG9mIFNvaWwgcmV0ZW50aW9uIFBvdGVudGlhbAoKV2Ugc2hvdWxkIGVzdGFibGlzaCBhIGRhdGEgYmFzZWQgb3JkZXIgb2YgbWFwIHVuaXQgc29pbCBwaG9zcGhvcnVzIApyZXRlbnRpb24gY2xhc3NlcyBpbnN0ZWFkIG9mIHBvc3R1bGF0aW5nIGFuIGFkIGhvYyBvcmRlci4gVGhpcyBjYW4gYmUgZG9uZQp0aHJvdWdoIHNlbGVjdGluZyB0aGUgZmlyc3QgZGlzY3JpbWluYW50IGRpbWVuc2lvbiBmcm9tIGEgRGlzY3JpbWluYW50CkFuYWx5c2lzIG9mIHRoZSBwZXJjZW50YWdlIG9mIHNvaWwgdW5pdCBwaG9zcGhvcnVzIHJldGVudGlvbiBjbGFzc2VzIHBlcgptYXBwaW5nIHVuaXQuIEZ1cnRoZXJtb3JlLCB3ZSBjYW4gdXNlIHRoYXQgZGlzY3JpbWluYW50IGRpbWVudGlvbiBhcyBhCmNvbnRpbnVvdXMgdmFyaWFibGUgaW5zdGVhZCBvZiB0aGUgZGlzY3JldGUgY2xhc3NlcyBmb3IgZG93bnN0cmVhbQphbmFseXNlcwogICAgICAKIyMgTWFwIFVuaXQgU29pbCBDb21wb3NpdGlvbiBNYXRyaXggYW5kIGVudHJvcHkKCkVhY2ggc29pbCB1bml0IHBlcmNlbnRhZ2UgY2FuIGJlIHVzZWQgYXMgYSBtYXAgdW5pdCBkZXNjcmlwdG9yIHZhcmlhYmxlIGZvciAKbXVsdGl2YXJpYXRlIGFuYWx5c2lzIG9mIHRoZSBwaG9zcGhvcnVzIHJldGVudGlvbiBwb3RlbnRpYWwgcGVyIG1hcCB1bml0LgoKYGBge3J9CklTUklDX0FUIDwtIHdpdGhpbihJU1JJQ19BVCwgewogIHN1X3NoYXJlIDwtIHNvaWxfY29tcG9zaXRpb24obWFwdW5pdCxGQU83NCkKICBzdV9zaGFyZSA8LSBhcy5kYXRhLmZyYW1lKAogICAgY2JpbmQoIElEID0gbWFwdW5pdCRJRCwKICAgICAgICAgICBzdV9zaGFyZSwKICAgICAgICAgICBzb2lsUyA9IHJvd19lbnRyb3B5KHN1X3NoYXJlLzEwMCkpKQp9KQoKYGBgCkhvd2V2ZXIgdGhpcyByZXN1bHRzIGluIHNwYXJzZSBtYXRyaXggd2l0aCBlc3NlbnRpYWxseSBvcnRob2dvbmFsIGNvbHVtbnMuCgojIyBQaG9zcGhvcnVzIFJldGVudGlvbiwgU29pbCBVbml0IENvbXBvc2l0aW9uICBKb2luIHRhYmxlCgpgYGB7cn0KcGh5Y2hlbV92YXJzIDwtIGMoInBIIiwiU0FORCIsIlNJTFQiLCJDTEFZIiwiQ0VDTEFZIikKc3VfdHlwZXMgPC0gY29sbmFtZXMoSVNSSUNfQVQkc3Vfc2hhcmUpCnJldF92YXJzIDwtIGMoIklEIiwKICAgICAgICAgICAgICAibWFpbmNsYXNzIiwKICAgICAgICAgICAgICBwaHljaGVtX3ZhcnMsCiAgICAgICAgICAgICAgcGN0X3ZhcnMsCiAgICAgICAgICAgICAgc3VfdHlwZXMpCgpsZXZlbHMgPC0gc29pbGNsYXNzICU+JQogICAgICAgICAgICBkcGx5cjo6YXJyYW5nZShhc2NlbmRpbmcpICU+JQogICAgICAgICAgICBkcGx5cjo6c2VsZWN0KG1haW4pCgpJU1JJQ19BVCA8LSB3aXRoaW4oSVNSSUNfQVQsIHsKICByZXRfc2hhcmUgPC0gbWFwdW5pdCAlPiUKICAgIGRwbHlyOjpsZWZ0X2pvaW4oc29pbHVuaXQsIGJ5ID0gYygiU09JTDEiID0gImtleSIpKSAlPiUKICAgIGRwbHlyOjpsZWZ0X2pvaW4oc3Vfc2hhcmUsIGJ5ID0gIklEIikgJT4lCiAgICBkcGx5cjo6c2VsZWN0KCEhIXJldF92YXJzKSAKICByZXRfc2hhcmUkbWFpbmNsYXNzIDwtIGZhY3RvcihyZXRfc2hhcmUkbWFpbmNsYXNzLCBsZXZlbHMgPSBsZXZlbHMkbWFpblsxOjE2XSkKfSkKCgpgYGAKCiMjIFNvaWwgYW5kIFBob3NwaG9ydXMgUmV0ZW50aW9uIENsYXNzIENvbXBvc2l0aW9uIEVudHJvcHkKYGBge3J9CklTUklDX0FUIDwtIHdpdGhpbihJU1JJQ19BVCwgewogIHJldF9zaGFyZSRwUyA8LSByb3dfZW50cm9weShyZXRfc2hhcmVbLHBjdF92YXJzXSAvIDEwMCkKfSkKCndpdGgoSVNSSUNfQVQsewogIGJveHBsb3Qoc29pbFMgfiBtYWluY2xhc3MsIGRhdGEgPSByZXRfc2hhcmUsIGxhcyA9IDIsIAogICAgICAgICAgbWFpbiA9ICJNYXAgVW5pdCBTb2lsIENvbXBvc2l0aW9uIEVudHJvcHkiKQogIGJveHBsb3QocFMgfiBtYWluY2xhc3MsIGRhdGEgPSByZXRfc2hhcmUsIGxhcyA9IDIsCiAgICAgICAgICBtYWluID0gIlBob3NwaG9ydXMgUmV0ZW50aW9uIENsYXNzIENvbXBvc2l0aW9uIEVudHJvcHkiKQp9KQpgYGAKCiMjIEV4cGxvcmF0b3J5IERpc2NyaW1pbmFudCBBbmFseXNpcyB1c2luZyBgYWRlNGAKClNvaWwgUmV0ZW50aW9uIFBvdGVudGlhbCBTcGFjZSBpcyBlc3NlbnRpYWxseSBhIHRldHJhaGVkcm9uIHdpdGggTW9kZXJhdGUKcmV0ZW50aW9uIGF0IGl0cyBjZW50ZXIuT25seSB0aHJlZSBkZWdyZWVzIG9mIGZyZWVkb20sIExvLVZILCBNby1IaSwgTG8tSGkuCgoKYGBge3J9CiMgY3JlYXRlIGR1ZGkgb2JqZWN0IGZvciBkaXNjcmltaW5hbnQgYW5hbHlzaXMKCnNvaWxfaWR4IDwtIHdpdGgoSVNSSUNfQVQkcmV0X3NoYXJlLHsKICAgIHdoaWNoKGlzX3NvaWwobWFpbmNsYXNzKSkKfSkKCnNvaWxfZGF0YSA8LSBJU1JJQ19BVCRyZXRfc2hhcmVbc29pbF9pZHgsLTFdCgpzb2lsX2RhdGEkbWFpbmNsYXNzICAgPC0gd2l0aChJU1JJQ19BVCRyZXRfc2hhcmUsewogICAgZmFjdG9yKG1haW5jbGFzc1tzb2lsX2lkeF0sIGxldmVscyA9IGxldmVscyRtYWluWzU6MTZdKQp9KQoKCgpwY2EgPC0gZHVkaS5wY2Eoc29pbF9kYXRhWywtMV0sIHNjYW5uZiA9IEZBTFNFLCBuZiA9IDEzOSkKICAKZGlzIDwtIGRpc2NyaW1pbihwY2EsIGZhYyA9IHNvaWxfZGF0YSRtYWluY2xhc3MsIHNjYW5uZiA9IEZBTFNFLCBuZiA9IDEwKQpwYWxldHRlIDwtIHNvaWxjbGFzcyRjb2xvcl9oZXhbNToxNl0KY29sb3IgPC0gcGFsZXR0ZVtzb2lsX2RhdGEkbWFpbmNsYXNzXQoKIyBTZWxlY3QgbW9zdCBpbXBvcnRhbnQgdmFyaWFibGVzCmRpc3ZhIDwtIGFzLmRhdGEuZnJhbWUoZGlzJHZhKQpkaXN2YSRpZHggPC0gKDE6bnJvdyhkaXMkdmEpKSAvIDEwMDAKCm1ham9yX3ZhcnMgPC0gMTAwMCAqIGRpc3ZhICU+JSAKICBkcGx5cjo6ZmlsdGVyX2FsbChhbnlfdmFycyhhYnMoLikgPiAwLjUpKSAlPiUKICBkcGx5cjo6c2VsZWN0KGlkeCkKCiMgUGxvdApwYXIobWZyb3cgPSBjKDIsIDIpKQpzLmNsYXNzKGRpcyRsaSwgeGF4ID0gMiwgeWF4ID0gMSwgZmFjID0gc29pbF9kYXRhJG1haW5jbGFzcywgY29sID0gcGFsZXR0ZSkKcy5jbGFzcyhkaXMkbGksIHhheCA9IDMsIHlheCA9IDEsIGZhYyA9IHNvaWxfZGF0YSRtYWluY2xhc3MsIGNvbCA9IHBhbGV0dGUpCnMuY2xhc3MoZGlzJGxpLCB4YXggPSAzLCB5YXggPSAyLCBmYWMgPSBzb2lsX2RhdGEkbWFpbmNsYXNzLCBjb2wgPSBwYWxldHRlKQpzLmNvcmNpcmNsZShkaXMkdmFbbWFqb3JfdmFycyRpZHgsXSkKcGFyKG1mcm93ID0gYygxLCAxKSkKYGBgCgoKIyBMaW5lYXJpemF0aW9uIG9mIFBob3NwaG9ydXMgUmV0ZW50aW9uIFBvdGVudGlhbCB3aXRoIGBNQVNTYCBMREEKClRoZSBgTUFTU2AgTERBIHdpaWxsIHVzZSBhbGwgaW5mbyBieSBkZWZhdWx0LgoKYGBge3J9CgpmaXQgPC0gTUFTUzo6bGRhKG1haW5jbGFzcyB+IC4sIGRhdGEgPSBzb2lsX2RhdGEpCgpwbGRhIDwtIHByZWRpY3Qob2JqZWN0ID0gZml0LCAjIHByZWRpY3Rpb25zCiAgICAgICAgICAgICAgICAgbmV3ZGF0YSA9IElTUklDX0FUJHJldF9zaGFyZVssLWMoMSwyKV0pCgpgYGAKCkZyb20gY2F0ZWdvcmljYWwgdmFyaWFibGUgdG8gY29udGlub3VzICJsaW5lYXIiIHNjYWxlLgpMaW5lYXIgY29tYmluYXRpb24gb2YgbWFwIHVuaXQgc29pbCBjb250ZW50LgoKYGBge3J9CnJldF9zY2FsZTwtIE5BCklTUklDX0FUIDwtIHdpdGhpbihJU1JJQ19BVCx7CiAgcmV0X3NjYWxlIDwtIHJldF9zaGFyZVssYygiSUQiLHBjdF92YXJzKV0KICByZXRfc2NhbGUkcmV0IDwtIHJldF9zaGFyZSRtYWluY2xhc3MKICByZXRfc2NhbGVbIWlzX3NvaWwocmV0X3NjYWxlJHJldCksIHBjdF92YXJzXSA8LSBOQQp9KQoKCklTUklDX0FUJHJldF9zY2FsZSA8LSB3aXRoaW4oSVNSSUNfQVQkcmV0X3NjYWxlLHsKICAgICMgV2VpZ2h0ZWQgc2NhbGUsIGFkIGhvYyB3ZWlnaHRzISEhISEhICAgIAogICAgd2VpZ2h0ZWQgPC0gYXMuZG91YmxlKChMbyArIDIgKiBNbyArIDMgKiBIaSArIDQgKiBWSCkgLyA0MDApCiAgICAjIExpbmVhciBEaXNjcmltYW50IHNjYWxlLCBwb3N0IGhvYyB3ZWlnaHRzL2NvZWZmaWNpZW50cwogICAgTEQxIDwtICBwbGRhJHhbLDFdCiAgICBMRDIgPC0gIC1wbGRhJHhbLDJdICMgc2lnbiBtYW51YWxseSBhZGp1c3RlZAogICAgTEQzIDwtICAtcGxkYSR4WywzXSAjIHNpZ24gbWFudWFsbHkgYWRqdXN0ZWQKICAgIExEMVshaXNfc29pbChyZXQpXSA8LSBOQQogICAgTEQyWyFpc19zb2lsKHJldCldIDwtIE5BCiAgICBMRDNbIWlzX3NvaWwocmV0KV0gPC0gTkEKfSkKCmBgYAoKYGBge3IsIGZpZy5hc3AgPSAxfQp3aXRoKElTUklDX0FULHsKICBwbG90X1Bfc2NhbGVzKHJldF9zY2FsZVtzb2lsX2lkeCxdLCBwYWxldHRlID0gcGFsZXR0ZSwKICAgICAgICAgICAgICBzY2FsZV94ID0gIkxEMiIsc2NhbGVfeSA9ICJ3ZWlnaHRlZCIpCn0pCgpgYGAKCmBgYHtyfQpsZF92YXJzIDwtIGMoIndlaWdodGVkIiwgIkxEMSIsICJMRDIiLCAiTEQzIikKCklTUklDX0FUIDwtIHdpdGhpbihJU1JJQ19BVCx7CiAgcmFzdGVyX3NjYWxlIDwtIG11X3NvaWxjbGFzc1ssYygiSUQiLHBjdF92YXJzKV0gJT4lIAogICAgZHBseXI6OmxlZnRfam9pbihyZXRfc2NhbGVbLGMoIklEIixsZF92YXJzKV0sCiAgICAgICAgICAgICAgICAgICAgIGJ5ID0gYygiSUQiID0gIklEIikpICU+JQogICAgZHBseXI6Om11dGF0ZV9pZihjb2xuYW1lcyguKSAlaW4lIGxkX3ZhcnMsIHNjYWxlMjU2KQp9KQpgYGAKCiMjIExpbmVhciBEaXNjcmltaW5hbnQgTWFwcwpgYGB7ciwgZmlnLmhlaWdodD00LCBmaWcud2lkdGg9MTAsIHdhcm5pbmc9RkFMU0V9CgpsZXZlbHMoSVNSSUMyMDExJEZBTzc0KSA8LSBJU1JJQ19BVCRyYXN0ZXJfc2NhbGUKCklTUklDMjAxMSA8LSBjKElTUklDMjAxMSwgbGF5ZXJzX2Zyb20oSVNSSUMyMDExJEZBTzc0LCBjb2xzID0gbGRfdmFycykpCgpmb3IodmFybmFtZSBpbiBsZF92YXJzKSB7CgogIG91dF90aWYgPC0gZmlsZS5wYXRoKCBleHRkYXRhX2RpciwgIklTUklDMjAxMSIsIHBhc3RlMCh2YXJuYW1lLCIudGlmIikpCiAgICAKICB3cml0ZVJhc3RlcihJU1JJQzIwMTFbW3Zhcm5hbWVdXSwKICAgICAgICAgICAgICBmaWxlID0gb3V0X3RpZiwKICAgICAgICAgICAgICBkYXRhdHlwZSA9ICdJTlQxVScsCiAgICAgICAgICAgICAgb3ZlcndyaXRlID0gVFJVRSkKICAgIAogIG5iX3Bsb3QoSVNSSUMyMDExW1t2YXJuYW1lXV0sIG1haW4gPSAgdmFybmFtZSkKfQoKIyBmaW5hbGx5IHNhdmUgSVNSSUMyMDExIGluIC5SRGF0YSBmb3JtYXQKc2F2ZShJU1JJQ19BVCwgZmlsZSA9IGZpbGUucGF0aChkYXRhX2RpciwgIklTUklDX0FULlJEYXRhIikpCnNhdmUoSVNSSUMyMDExLCBmaWxlID0gZmlsZS5wYXRoKGRhdGFfZGlyLCAiSVNSSUMyMDExLlJEYXRhIikpCmBgYAoKIyMgRmFsc2UgQ29sb3IgQ29tYmluYXRpb24gb2YgTGluZWFyIERpc2NyaW1pbmFudHMKYGBge3IsIGZpZy5oZWlnaHQ9NCwgZmlnLndpZHRoPTEwLCB3YXJuaW5nPUZBTFNFfQoKTERfY29tcG9zaXRlIDwtIHN0YWNrKElTUklDMjAxMVtjKCJMRDIiLCJMRDEiLCJMRDMiKV0pCgpvcCA8LSBwYXIoKQpwYXIobWFyID0gYygwLCAwLCAwLCAwKSwgb21hID0gYygwLCAwLCAwLCAwKSkKCnBsb3RSR0IoTERfY29tcG9zaXRlKQoKcGFyKG9wKQoKb3V0X3RpZiA8LSBmaWxlLnBhdGgoZXh0ZGF0YV9kaXIsIklTUklDMjAxMSIsIkxEX2NvbXBvc2l0ZS50aWYiKQp3cml0ZVJhc3RlcihMRF9jb21wb3NpdGUsIGZpbGVuYW1lID0gb3V0X3RpZiwKICAgICAgICAgICAgZGF0YXR5cGUgID0gIklOVDFVIiwKICAgICAgICAgICAgb3B0aW9ucyAgID0gIlRGVz1ZRVMiLAogICAgICAgICAgICBmb3JtYXQgICAgPSAiR1RpZmYiLAogICAgICAgICAgICBvdmVyd3JpdGUgPSBUUlVFKQpgYGAK