1 Loading Libraries

library(devtools)
library(here)
load_all(pkg = here())
library(dplyr)
library(tidyr)
library(Hmisc)
library(MASS)
library(ggplot2)
library(ade4)

2 Reading ISRIC 2011 data

2.1 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)
test_tif <- file.path(extdata_dir,
            "Global_distribution_of_soil_phosphorus_retention_potential",
            "Global_distribution_of_soil_phosphorus_retention_potential.tif")
test_raster <- raster::raster(test_tif)

This raster has a sigle layer and its value corresponds to the soil map unit identifier (MUID) 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.

2.2 Plotting FAO Soil Map Unit Raster

op <- par()
par(mar = c(0, 0, 0, 0), oma = c(0, 0, 0, 0))
plot(test_raster, axes = FALSE, box = FALSE,
     frame.plot = FALSE,
     legend.width = 2,
     legend.shrink = 0.95,
     axis.args = list(cex.axis = 2),
     legend.args = list(text = "", cex = 1))
par(op)

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.

The 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 diferent percentages, depending on the composition of the map unit and each soil unit compoment phosphorus retention potential class a class was assigned to each map unit. In total 260 different MU phosphorus retention potential clases were assigned to the 4923 map units. These were further grouped into 16 main phosphorus retention potential classes.

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.

2.3 Extracting Raster Attribute Table from Microsoft Access database (mdb file).

For this I need Hmisc and the ODBC drivers. Mac installation is from brew.

The MS Access database has the tables for mapping the soil unit identifiers to soil properties, i.e. phosphorus retention as soil unit class percentage, or map unit class.

mdb_file <- file.path(extdata_dir,
                  "Global_distribution_of_soil_phosphorus_retention_potential",
                  "ISRIC_Phosphorus_Retention_Potential.mdb")
RATs <- read_ISRIC_RAT(mdb_file)
FAO74    <- RATs$FAO74
mapunit  <- RATs$mapunit
soilunit <- RATs$soilunit

3 Rebuilding the arcgis render appeareance

3.1 Applying Raster Attributes for obtainning P Retention Potential MainClass

We will use a manually curated raster attribute table stored in th soilclass dataframe.

See the manual for details.

?soilclass
Using development documentation for soilclass

First we need to merge soilclass to the Soil Map Unit id number.

data(soilclass)
mu_soilclass <- mapunit %>% 
  dplyr::left_join(soilclass, by=c("mainclass" ="main")) %>%
  dplyr::select(MUID,mainclass,ascending)
Column `mainclass`/`main` joining factors with different levels, coercing to character vector
ISRIC2011 <- read_P_ISRIC(tif = test_tif, soilclass = mu_soilclass,
                     is = "MUID",
                     becomes = "ascending",
                     filename = file.path(extdata_dir,"tif","mu_soilclass.tif"))
# Save ISRIC!!!!!

3.2 Plotting Soil Phosphorus Retention Potential Raster

op <- par()
par(mar = c(0, 0, 0, 0), oma = c(0, 0, 0, 0))
plot(ISRIC2011, axes = FALSE, box = FALSE,
     frame.plot = FALSE,
     legend.width = 2,
     legend.shrink = 0.95,
     axis.args = list(breaks=1:14, at = 0:15, cex.axis = 2),
     legend.args = list(text = "", cex = 1))
par(op)

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 Batjes 2011).

To remediate this I manually assigned integers inascending 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, 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 Moderate, High, and Very High soil unit P retention percentage the higher the integer. This assignation is naive and consequently both, intuitive and ad hoc.

3.3 Adding Raster Attribute Table to ISRIC2011

levels<- S3 method is internal to raster package and could not be exported to the soilP package, which prevented me from making a working package function out of the following snippet:

# Raster Attribute Table
rat <- levels(ISRIC2011)[[1]] %>% 
       dplyr::inner_join(soilclass, by = c("ID" = "ascending"))
levels(ISRIC2011) <- rat # This assignment method is not exported from raster
save(ISRIC2011, file = file.path(data_dir, "ISRIC2011.RData"))

3.4 Using rasterVis to appropiately label categories and show 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, att = "main",
          col.regions = soilclass$color_hex[1:16],
          maxpixels = ncell(ISRIC2011),  scales = list(draw = FALSE),
          xlab = NULL, ylab = NULL,
          main = "Generalized Phosphorus Retention Potential Map")

4 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

4.1 Map Unit Soil Composition Matrix

Each soil unit percentage can be used as a map unit descriptor variable for multivariate analysis of the phosphorus retention potential per map unit. This results in sparse matrix with essentially orthogonal columns.

mu_comp <- get_mu_composition(mapunit,FAO74)

4.2 Map unit - Phosphorus Retention Join table

mu_ret <- mapunit %>%
  dplyr::left_join(soilunit, by = c("SOIL1" = "key")) %>%
  dplyr::select(MUID,mainclass,Lo,Mo,Hi,VH,MISC,pH,SAND,SILT,CLAY,CECLAY)
Column `SOIL1`/`key` joining factors with different levels, coercing to character vector
is_soil <- function(x){
  !(x %in% c("GL1","RK1","RK2","WR1"))
}
ret_comp <- subset(cbind(mu_ret,mu_comp),
                    is_soil(mu_ret$mainclass))
soilclass <- soilclass[order(soilclass$ascending),]
ascending_order <- as.character(soilclass$main)[5:16]
ret_comp$mainclass <- factor(ret_comp$mainclass,
                             levels = ascending_order)

5 Discriminant analysis using ade4

5.1 Soil Retention Potential Space is essentially a tetrahedron

with Moderate retention at its center.

Only three degrees of freedom, Lo-VH, Mo, Mo-Hi,

# create dudi object for discriminant analysis
pca <- dudi.pca(ret_comp[,-(1:2)],scannf = FALSE, nf= 4)
dis <- discrimin(pca,factor(ret_comp$mainclass),  scannf = FALSE, nf = 3)
palette <- soilclass$color_hex[5:16]
color <- palette[ret_comp$mainclass]
par(mfrow = c(2, 2))
s.class(dis$li, xax = 2, yax = 1, fac = ret_comp$mainclass, col = palette)
s.class(dis$li, xax = 3, yax = 1, fac = ret_comp$mainclass, col = palette)
s.class(dis$li, xax = 3, yax = 2, fac = ret_comp$mainclass, col = palette)
s.corcircle(dis$va)
par(mfrow = c(1, 1))

6 Discriminant analysis using MASS

fit <- lda(mainclass ~ ., data=ret_comp[,-1])
variables are collinear
plda <-  predict(object = fit, # predictions
                 newdata = ret_comp[,-(1:2)])

7 Linearization of Phosphorus retention potential

From categorical variable to continous “linear” scale. Linear combination of map unit soil content.

ret_scale <- ret_comp[,c("MUID","Lo","Mo","Hi","VH","MISC")]
ret_scale$ret <- ret_comp$mainclass
# Weighted scale, ad hoc weights!!!!!!
ret_scale$weighted <- with(ret_scale,
                           {as.numeric((Lo + 2 * Mo + 3 * Hi + 4 * VH) / 400)})
# Linear Discrimant scale, post hoc weights/coefficients
ret_scale$LD2 <-  plda$x[,2]
plot_P_scales(ret_scale, palette = palette,
              scale_x = "LD2",scale_y = "weighted")

scale256 <- function(x){
  as.integer(round(245 * (x - min(x,na.rm = TRUE))/(max(x,na.rm = TRUE) -
                                                      min(x,na.rm = TRUE))) +5)}
P_retention <- mu_soilclass %>% 
  dplyr::left_join(ret_scale, by = "MUID")
P_retention$weighted <- scale256(P_retention$weighted)
P_retention$weighted[is.na(P_retention$weighted)] <- 0
P_retention$LD2 <- scale256(P_retention$LD2) 
P_retention$LD2[!is_soil(P_retention$mainclass)] <- 0
P_weighted <- read_P_ISRIC(tif = test_tif, soilclass = P_retention,
                     is = "MUID",
                     becomes = "weighted",
                     filename = file.path(extdata_dir,"tif","P_weighted.tif"))
P_LD2 <- read_P_ISRIC(tif = test_tif, soilclass = P_retention,
                     is = "MUID",
                     becomes = "LD2",
                     filename = file.path(extdata_dir,"tif","P_LD2.tif"))

7.1 Weighted P retention Potential

op <- par()
par(mar = c(0, 0, 0, 0), oma = c(0, 0, 0, 0))
plot(P_weighted, axes = FALSE, box = FALSE,
     frame.plot = FALSE,
     legend.width = 2,
     legend.shrink = 0.95,
     axis.args = list(cex.axis = 2),
     legend.args = list(text = "", cex = 1))
par(op)

7.2 Linear Discriminant P retention Potential

op <- par()
par(mar = c(0, 0, 0, 0), oma = c(0, 0, 0, 0))
plot(P_LD2, axes = FALSE, box = FALSE,
     frame.plot = FALSE,
     legend.width = 2,
     legend.shrink = 0.95,
     axis.args = list(cex.axis = 2),
     legend.args = list(text = "", cex = 1))
par(op)

8 Soil Map Phosphorus Retention Potential as Percentages of Map Units

P_retention[!is_soil(P_retention$mainclass),c("Lo","Mo","Hi","VH")] <- 0
P_retention[!is_soil(P_retention$mainclass),"MISC"] <- 100
ISRIC2011_Lo <- read_P_ISRIC(tif = test_tif, soilclass = P_retention,
                     is = "MUID",
                     becomes = "Lo",
                     filename = file.path(extdata_dir,"tif","ISRIC2011_Lo.tif"))
ISRIC2011_Mo <- read_P_ISRIC(tif = test_tif, soilclass = P_retention,
                     is = "MUID",
                     becomes = "Mo",
                     filename = file.path(extdata_dir,"tif","ISRIC2011_Mo.tif"))
ISRIC2011_Hi <- read_P_ISRIC(tif = test_tif, soilclass = P_retention,
                     is = "MUID",
                     becomes = "Hi",
                     filename = file.path(extdata_dir,"tif","ISRIC2011_Hi.tif"))
ISRIC2011_VH <- read_P_ISRIC(tif = test_tif, soilclass = P_retention,
                     is = "MUID",
                     becomes = "Hi",
                     filename = file.path(extdata_dir,"tif","ISRIC2011_VH.tif"))
ISRIC2011_MISC <- read_P_ISRIC(tif = test_tif, soilclass = P_retention,
                     is = "MUID",
                     becomes = "MISC",
                     filename = file.path(extdata_dir,"tif","ISRIC2011_MISC.tif"))

8.1 Lo P retention Potential

op <- par()
par(mar = c(0, 0, 0, 0), oma = c(0, 0, 0, 0))
plot(ISRIC2011_Lo, axes = FALSE, box = FALSE,
     frame.plot = FALSE,
     legend.width = 2,
     legend.shrink = 0.95,
     axis.args = list(cex.axis = 2),
     legend.args = list(text = "", cex = 1))
par(op)

8.2 Mo P retention Potential

op <- par()
par(mar = c(0, 0, 0, 0), oma = c(0, 0, 0, 0))
plot(ISRIC2011_Mo, axes = FALSE, box = FALSE,
     frame.plot = FALSE,
     legend.width = 2,
     legend.shrink = 0.95,
     axis.args = list(cex.axis = 2),
     legend.args = list(text = "", cex = 1))
par(op)

8.3 Hi P retention Potential

op <- par()
par(mar = c(0, 0, 0, 0), oma = c(0, 0, 0, 0))
plot(ISRIC2011_Hi, axes = FALSE, box = FALSE,
     frame.plot = FALSE,
     legend.width = 2,
     legend.shrink = 0.95,
     axis.args = list(cex.axis = 2),
     legend.args = list(text = "", cex = 1))
par(op)

8.4 VH P retention Potential

op <- par()
par(mar = c(0, 0, 0, 0), oma = c(0, 0, 0, 0))
plot(ISRIC2011_VH, axes = FALSE, box = FALSE,
     frame.plot = FALSE,
     legend.width = 2,
     legend.shrink = 0.95,
     axis.args = list(cex.axis = 2),
     legend.args = list(text = "", cex = 1))
par(op)

8.5 MISC P retention Potential

op <- par()
par(mar = c(0, 0, 0, 0), oma = c(0, 0, 0, 0))
plot(ISRIC2011_MISC, axes = FALSE, box = FALSE,
     frame.plot = FALSE,
     legend.width = 2,
     legend.shrink = 0.95,
     axis.args = list(cex.axis = 2),
     legend.args = list(text = "", cex = 1))
par(op)

LS0tCnRpdGxlOiAiUmVhZGluZyBJU1JJQyBQaG9zcGhvcnVzIFJldGVudGlvbiBEYXRhIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIGhpZ2hsaWdodDogdGFuZ28KICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQogICAgdGhlbWU6IHNwYWNlbGFiCiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQotLS0KCiMgTG9hZGluZyBMaWJyYXJpZXMKCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCByZXN1bHRzPSJoaWRlIn0KbGlicmFyeShkZXZ0b29scykKbGlicmFyeShoZXJlKQpsb2FkX2FsbChwa2cgPSBoZXJlKCkpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkoSG1pc2MpCmxpYnJhcnkoTUFTUykKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGFkZTQpCmBgYAojIFJlYWRpbmcgSVNSSUMgMjAxMSBkYXRhCiMjIFN0YXJ0aW5nIHdpdGggdGhlIGJhc2UgcmFzdGVyIGZpbGUKRmlyc3Qgd2UgbG9hZCB0aGUgcmFzdGVyLiAKYGBge3J9CmRhdGFfZGlyIDwtIHN5c3RlbS5maWxlKCJkYXRhIiwgcGFja2FnZSA9ICJzb2lsUCIsIG11c3RXb3JrID0gVFJVRSkKZXh0ZGF0YV9kaXIgPC0gc3lzdGVtLmZpbGUoImV4dGRhdGEiLCBwYWNrYWdlID0gInNvaWxQIiwgbXVzdFdvcmsgPSBUUlVFKQoKdGVzdF90aWYgPC0gZmlsZS5wYXRoKGV4dGRhdGFfZGlyLAogICAgICAgICAgICAiR2xvYmFsX2Rpc3RyaWJ1dGlvbl9vZl9zb2lsX3Bob3NwaG9ydXNfcmV0ZW50aW9uX3BvdGVudGlhbCIsCiAgICAgICAgICAgICJHbG9iYWxfZGlzdHJpYnV0aW9uX29mX3NvaWxfcGhvc3Bob3J1c19yZXRlbnRpb25fcG90ZW50aWFsLnRpZiIpCnRlc3RfcmFzdGVyIDwtIHJhc3Rlcjo6cmFzdGVyKHRlc3RfdGlmKQoKYGBgClRoaXMgcmFzdGVyIGhhcyBhIHNpZ2xlIGxheWVyIGFuZCBpdHMgIHZhbHVlIGNvcnJlc3BvbmRzIHRvIHRoZSBzb2lsIG1hcCB1bml0IAppZGVudGlmaWVyIChNVUlEKSBmb3IgdGhlIFNvaWwgTWFwIG9mIHRoZSBXb3JsZCwgRkFPNzQuIFRoZSBzYW1lIGlkZW50aWZpZXIgY2FuCmJlIGZvdW5kIGluIG1vcmUgcmVjZW50IHZlcnNpb25zIG9mIHRoZSBGQU8gc29pbCBtYXAgYW5kIGluIHRoZSBoYXJtb25pemVkIHNvaWwgCmRhdGFiYXNlLgoKIyMgUGxvdHRpbmcgRkFPIFNvaWwgTWFwIFVuaXQgUmFzdGVyCmBgYHtyLCBmaWcuaGVpZ2h0PTQsIGZpZy53aWR0aD0xMCwgd2FybmluZz1GQUxTRX0Kb3AgPC0gcGFyKCkKcGFyKG1hciA9IGMoMCwgMCwgMCwgMCksIG9tYSA9IGMoMCwgMCwgMCwgMCkpCnBsb3QodGVzdF9yYXN0ZXIsIGF4ZXMgPSBGQUxTRSwgYm94ID0gRkFMU0UsCiAgICAgZnJhbWUucGxvdCA9IEZBTFNFLAogICAgIGxlZ2VuZC53aWR0aCA9IDIsCiAgICAgbGVnZW5kLnNocmluayA9IDAuOTUsCiAgICAgYXhpcy5hcmdzID0gbGlzdChjZXguYXhpcyA9IDIpLAogICAgIGxlZ2VuZC5hcmdzID0gbGlzdCh0ZXh0ID0gIiIsIGNleCA9IDEpKQpwYXIob3ApCmBgYAoKVGhlIE1VSURzIGhhdmUgdGhlIGxvd2VzdCB2YWx1ZXMgaW4gQWZyaWNhIGFuZCB0aGVuIGFzY2VuZCB0aHJvdWdoIEFzaWEsCnRoZSBBbWVyaWNhcywgT2NlYW5pYSBhbmQgYXR0YWluIHRoZSBoaWdoZXN0IHZhbHVlcyBpbiBFdXJvcGUuCkNvbnRyYXJ5IHRvIHdoYXQgdGhlIGZpbGUgbmFtZSBzZWVtcyB0byBpbXBseSB0aGVzZSBpZHMgZG8gbm90IGRpcmVjdGx5CnJlcHJlc2VudCAgdGhlIHBob3NwaG9ydXMgcmV0ZW50aW9uIHBvdGVudGlhbC4gQmF0amVzIHVzZWQgdGhlIGlkcyBhbmQgCmEgc2VyaWVzIG9mIFNRTCBxdWVyaWVzIHRvIGFzc2lnbiBwaG9zcGhvcnVzIHJldGVudGlvbiBwb3RlbnRpYWwgY2xhc3Nlcwp0byBlYWNoIG1hcCB1bml0LCB3aGljaCB0aGVuIGFyZSBlYXNpbHkgcHJvamVjdGVkIG92ZXIgZWFjaCBwaXhlbCBpbiB0aGUKbWFwLgoKVGhlIHBob3NwaG9ydXMgcmV0ZW50aW9uIHBvdGVudGlhbCBjbGFzcyB3YXMgYXNzaWduZWQgdG8gZWFjaCBvZiB0aGUgRkFPNzQKc29pbCB1bml0cyAoTG93LCBNb2RlcmF0ZSwgSGlnaCBhbmQgdmVyeSBIaWdoKS4gCkVhY2ggbWFwIHVuaXQgaXMgY29tcG9zZWQgb2YgdXAgdG8gOCBzb2lsIHVuaXRzIGluIGRpZmVyZW50IHBlcmNlbnRhZ2VzLCAKZGVwZW5kaW5nIG9uIHRoZSBjb21wb3NpdGlvbiBvZiB0aGUgbWFwIHVuaXQgYW5kIGVhY2ggc29pbCB1bml0IGNvbXBvbWVudCAKcGhvc3Bob3J1cyByZXRlbnRpb24gcG90ZW50aWFsIGNsYXNzIGEgY2xhc3Mgd2FzIGFzc2lnbmVkIHRvIGVhY2ggbWFwIHVuaXQuCkluIHRvdGFsIDI2MCBkaWZmZXJlbnQgTVUgcGhvc3Bob3J1cyByZXRlbnRpb24gcG90ZW50aWFsIGNsYXNlcyB3ZXJlIGFzc2lnbmVkIHRvCnRoZSA0OTIzIG1hcCB1bml0cy4gVGhlc2Ugd2VyZSBmdXJ0aGVyIGdyb3VwZWQgaW50byAxNiBtYWluIHBob3NwaG9ydXMgcmV0ZW50aW9uCnBvdGVudGlhbCBjbGFzc2VzLgoKVGhlc2UgbWFpbiBjbGFzc2VzIGNhbiBiZSBleHRyYWN0ZWQgZnJvbSB0aGUgbWFwIHVuaXQgcmFzdGVyLCBhbmQgdGhlIHRhYmxlcyBpbgp0aGUgbWRiIGZpbGVzLiBUaGVzZSB0YWJsZXMgZnVuY3Rpb24gYXMgcmFzdGVyIGF0dHJpYnV0ZSB0YWJsZXMuCgojIyBFeHRyYWN0aW5nIFJhc3RlciBBdHRyaWJ1dGUgVGFibGUgZnJvbSBNaWNyb3NvZnQgQWNjZXNzIGRhdGFiYXNlIChgbWRiYCBmaWxlKS4KCkZvciB0aGlzIEkgbmVlZCBIbWlzYyBhbmQgdGhlIE9EQkMgZHJpdmVycy4gTWFjIGluc3RhbGxhdGlvbiBpcyBmcm9tIGJyZXcuCgpUaGUgTVMgQWNjZXNzIGRhdGFiYXNlIGhhcyB0aGUgdGFibGVzIGZvciBtYXBwaW5nIHRoZSBzb2lsIHVuaXQgaWRlbnRpZmllcnMgdG8Kc29pbCBwcm9wZXJ0aWVzLCBpLmUuIHBob3NwaG9ydXMgcmV0ZW50aW9uIGFzIHNvaWwgdW5pdCBjbGFzcyBwZXJjZW50YWdlLCAKb3IgbWFwIHVuaXQgY2xhc3MuCgpgYGB7cn0KCm1kYl9maWxlIDwtIGZpbGUucGF0aChleHRkYXRhX2RpciwKICAgICAgICAgICAgICAgICAgIkdsb2JhbF9kaXN0cmlidXRpb25fb2Zfc29pbF9waG9zcGhvcnVzX3JldGVudGlvbl9wb3RlbnRpYWwiLAogICAgICAgICAgICAgICAgICAiSVNSSUNfUGhvc3Bob3J1c19SZXRlbnRpb25fUG90ZW50aWFsLm1kYiIpClJBVHMgPC0gcmVhZF9JU1JJQ19SQVQobWRiX2ZpbGUpCgpGQU83NCAgICA8LSBSQVRzJEZBTzc0Cm1hcHVuaXQgIDwtIFJBVHMkbWFwdW5pdApzb2lsdW5pdCA8LSBSQVRzJHNvaWx1bml0CmBgYAoKIyBSZWJ1aWxkaW5nIHRoZSBhcmNnaXMgcmVuZGVyIGFwcGVhcmVhbmNlIAoKIyMgQXBwbHlpbmcgUmFzdGVyIEF0dHJpYnV0ZXMgZm9yIG9idGFpbm5pbmcgUCBSZXRlbnRpb24gUG90ZW50aWFsIE1haW5DbGFzcwoKV2Ugd2lsbCB1c2UgYSBtYW51YWxseSBjdXJhdGVkIHJhc3RlciBhdHRyaWJ1dGUgdGFibGUgc3RvcmVkIGluIHRoIGBzb2lsY2xhc3NgCmRhdGFmcmFtZS4KClNlZSB0aGUgbWFudWFsIGZvciBkZXRhaWxzLgpgYGB7cn0KP3NvaWxjbGFzcwpgYGAKCkZpcnN0IHdlIG5lZWQgdG8gIG1lcmdlIGBzb2lsY2xhc3NgIHRvIHRoZSBTb2lsIE1hcCBVbml0IGlkIG51bWJlci4KCmBgYHtyfQpkYXRhKHNvaWxjbGFzcykKbXVfc29pbGNsYXNzIDwtIG1hcHVuaXQgJT4lIAogIGRwbHlyOjpsZWZ0X2pvaW4oc29pbGNsYXNzLCBieT1jKCJtYWluY2xhc3MiID0ibWFpbiIpKSAlPiUKICBkcGx5cjo6c2VsZWN0KE1VSUQsbWFpbmNsYXNzLGFzY2VuZGluZykKCklTUklDMjAxMSA8LSByZWFkX1BfSVNSSUModGlmID0gdGVzdF90aWYsIHNvaWxjbGFzcyA9IG11X3NvaWxjbGFzcywKICAgICAgICAgICAgICAgICAgICAgaXMgPSAiTVVJRCIsCiAgICAgICAgICAgICAgICAgICAgIGJlY29tZXMgPSAiYXNjZW5kaW5nIiwKICAgICAgICAgICAgICAgICAgICAgZmlsZW5hbWUgPSBmaWxlLnBhdGgoZXh0ZGF0YV9kaXIsInRpZiIsIm11X3NvaWxjbGFzcy50aWYiKSkKCiMgU2F2ZSBJU1JJQyEhISEhCmBgYAoKIyMgUGxvdHRpbmcgU29pbCBQaG9zcGhvcnVzIFJldGVudGlvbiBQb3RlbnRpYWwgUmFzdGVyCgpgYGB7ciwgZmlnLmhlaWdodD00LCBmaWcud2lkdGg9MTAsIHdhcm5pbmc9RkFMU0V9Cm9wIDwtIHBhcigpCnBhcihtYXIgPSBjKDAsIDAsIDAsIDApLCBvbWEgPSBjKDAsIDAsIDAsIDApKQpwbG90KElTUklDMjAxMSwgYXhlcyA9IEZBTFNFLCBib3ggPSBGQUxTRSwKICAgICBmcmFtZS5wbG90ID0gRkFMU0UsCiAgICAgbGVnZW5kLndpZHRoID0gMiwKICAgICBsZWdlbmQuc2hyaW5rID0gMC45NSwKICAgICBheGlzLmFyZ3MgPSBsaXN0KGJyZWFrcz0xOjE0LCBhdCA9IDA6MTUsIGNleC5heGlzID0gMiksCiAgICAgbGVnZW5kLmFyZ3MgPSBsaXN0KHRleHQgPSAiIiwgY2V4ID0gMSkpCnBhcihvcCkKYGBgCgpOb3cgdGhlIG51bWJlcnMgZG8gcmVwcmVzZW50IHRoZSBwaG9zcGhvcnVzIHJldGVudGlvbiBwb3RlbnRpYWwhISEKCkhvd2V2ZXIsIHRoZSBgcmFzdGVyYCBwbG90IGxlZ2VuZCBhYm92ZSBhc3N1bWVzIGEgY29udGludW91cyBzY2FsZSBmcm9tIDAgdG8gMTUsCndoaWxlIHRoZSBkYXRhIGlzIGV4cGxpY2l0bHkgYSBjYXRlZ29yaWNhbCB2YXJpYWJsZSAoYWx0aG91Z2ggZGVyaXZlZCBmcm9tCmNvbnRpbnVvdXMgcGVyY2VudGFnZXMsIHNlZSBCYXRqZXMgMjAxMSkuIAoKVG8gcmVtZWRpYXRlIHRoaXMgSSBtYW51YWxseSBhc3NpZ25lZCBpbnRlZ2VycyBpbmFzY2VuZGluZyBvcmRlciB0byB0aGUKYGFzY2VuZGluZ2AgY29sdW1uIGluIHRoZSBgc29pbGNsYXNzYCBkYXRhZnJhbWUgYXMgZm9sbG93czogRm9yIHRoZSBMb3cgbWFwIHVuaXQKbWFpbiBjbGFzcywgaGlnaGVyIHBlcmNlbnRhZ2Ugb2YgTG93IHNvaWwgdW5pdCBQIHJldGVudGlvbiBjb3JyZXNwb25kZWQgdG8gbG93ZXIKaW50ZWdlcnMuIEZvciB0aGUgTW9kZXJhdGUsIEhpZ2gsIGFuZCB2ZXJ5IEhpZ2ggbWFwIHVuaXQgbWFpbiBjbGFzc2VzLCBoaWdoZXIKaW50ZWdlcnMgd2VyZSBhc3NpZ25lZCB0byBoaWdoZXIgcGVyY2VudGFnZSBvZiBjb3JyZXNwb25kaW5nIHNvaWwgdW5pdCBQCnJldGVudGlvbi4gVGhpcyBtZWFucyB0aGUgaGlnaGVyIHRoZSBMb3cgc29pbCB1bml0IFAgcmV0ZW50aW9uIHBlcmNlbnRhZ2UgdGhlCmxvd2VyIHRoZSBpbnRlZ2VyLCBhbmQgY29tcGxlbnRhcmlseSB0aGUgaGlnaGVyIHRoZSBNb2RlcmF0ZSwgSGlnaCwgYW5kIFZlcnkgCkhpZ2ggc29pbCB1bml0IFAgcmV0ZW50aW9uIHBlcmNlbnRhZ2UgdGhlIGhpZ2hlciB0aGUgaW50ZWdlci4KVGhpcyBhc3NpZ25hdGlvbiBpcyBuYWl2ZSBhbmQgY29uc2VxdWVudGx5IGJvdGgsIGludHVpdGl2ZSBhbmQgKmFkIGhvYyouCgoKIyMgQWRkaW5nIFJhc3RlciBBdHRyaWJ1dGUgVGFibGUgdG8gIGBJU1JJQzIwMTFgCmBsZXZlbHM8LWAgUzMgbWV0aG9kIGlzIGludGVybmFsIHRvIGByYXN0ZXJgIHBhY2thZ2UgYW5kIGNvdWxkIG5vdCBiZSBleHBvcnRlZCAKdG8gdGhlIGBzb2lsUGAgcGFja2FnZSwgd2hpY2ggcHJldmVudGVkIG1lIGZyb20gbWFraW5nIGEgd29ya2luZyBwYWNrYWdlIGZ1bmN0aW9uCm91dCBvZiB0aGUgZm9sbG93aW5nIHNuaXBwZXQ6CgpgYGB7cn0KIyBSYXN0ZXIgQXR0cmlidXRlIFRhYmxlCnJhdCA8LSBsZXZlbHMoSVNSSUMyMDExKVtbMV1dICU+JSAKICAgICAgIGRwbHlyOjppbm5lcl9qb2luKHNvaWxjbGFzcywgYnkgPSBjKCJJRCIgPSAiYXNjZW5kaW5nIikpCgpsZXZlbHMoSVNSSUMyMDExKSA8LSByYXQgIyBUaGlzIGFzc2lnbm1lbnQgbWV0aG9kIGlzIG5vdCBleHBvcnRlZCBmcm9tIHJhc3RlcgoKc2F2ZShJU1JJQzIwMTEsIGZpbGUgPSBmaWxlLnBhdGgoZGF0YV9kaXIsICJJU1JJQzIwMTEuUkRhdGEiKSkKCmBgYAoKIyMgVXNpbmcgcmFzdGVyVmlzIHRvIGFwcHJvcGlhdGVseSBsYWJlbCBjYXRlZ29yaWVzIGFuZCBzaG93IGxlZ2VuZCAKUmVtZW1iZXIgdGhhdCB3ZSBhc3NpZ25lZCB0byB0aGUgdG8gdGhlIHBob3NwaG9ydXMgcmV0ZW50aW9uIHBvdGVudGlhbCBtYWluCmNsYXNzZXMgYW4gKmFkIGhvYyogb3JkZXIgc28gd2UgY2FuIGludGVycHJldCBhIGRpcmVjdGlvbiBvZiBhc2NlbmRpbmcgcmV0ZW50aW9uIApwb3RlbnRpYWwgKHNlZSBhYm92ZSkgaW4gdGhlIHBsb3QgbGVnZW5kLgoKSW4gb3JkZXIgdG8gc2hvdyB0aGUgcmlnaHQgbGFiZWxzIGluIHRoaXMgYXNjZW5kaW5nIHNjYWxlIHdlIHVzZSB0aGUgYGxldmVscGxvdGAKZnVuY3Rpb24gZnJvbSBgUmFzdGVyVmlzYCB0aGF0IHVzZXMgdGhlIHJhc3RlciBhdHRyaWJ1dGUgdGFibGUgdG8gdHJhbnNmb3JtIHRoZSAKbnVtZXJpY2FsIHZhbHVlIG9mIHRoZSByYXN0ZXIgdG8gZGlzY3JldGUgY2F0ZWdvcmllcy4KCgpgYGB7cn0KcmFzdGVyVmlzOjpsZXZlbHBsb3QoSVNSSUMyMDExLCBhdHQgPSAibWFpbiIsCiAgICAgICAgICBjb2wucmVnaW9ucyA9IHNvaWxjbGFzcyRjb2xvcl9oZXhbMToxNl0sCiAgICAgICAgICBtYXhwaXhlbHMgPSBuY2VsbChJU1JJQzIwMTEpLCAgc2NhbGVzID0gbGlzdChkcmF3ID0gRkFMU0UpLAogICAgICAgICAgeGxhYiA9IE5VTEwsIHlsYWIgPSBOVUxMLAogICAgICAgICAgbWFpbiA9ICJHZW5lcmFsaXplZCBQaG9zcGhvcnVzIFJldGVudGlvbiBQb3RlbnRpYWwgTWFwIikKCmBgYAoKIyBNdWx0aXZhcmlhdGUgYW5hbHlzaXMgb2YgU29pbCByZXRlbnRpb24gUG90ZW50aWFsCgpXZSBzaG91bGQgZXN0YWJsaXNoIGEgZGF0YSBiYXNlZCBvcmRlciBvZiBtYXAgdW5pdCBzb2lsIHBob3NwaG9ydXMgCnJldGVudGlvbiBjbGFzc2VzIGluc3RlYWQgb2YgcG9zdHVsYXRpbmcgYW4gYWQgaG9jIG9yZGVyLiBUaGlzIGNhbiBiZSBkb25lCnRocm91Z2ggc2VsZWN0aW5nIHRoZSBmaXJzdCBkaXNjcmltaW5hbnQgZGltZW5zaW9uIGZyb20gYSBEaXNjcmltaW5hbnQKQW5hbHlzaXMgb2YgdGhlIHBlcmNlbnRhZ2Ugb2Ygc29pbCB1bml0IHBob3NwaG9ydXMgcmV0ZW50aW9uIGNsYXNzZXMgcGVyCm1hcHBpbmcgdW5pdC4gRnVydGhlcm1vcmUsIHdlIGNhbiB1c2UgdGhhdCBkaXNjcmltaW5hbnQgZGltZW50aW9uIGFzIGEKY29udGludW91cyB2YXJpYWJsZSBpbnN0ZWFkIG9mIHRoZSBkaXNjcmV0ZSBjbGFzc2VzIGZvciBkb3duc3RyZWFtCmFuYWx5c2VzCiAgICAgIAojIyBNYXAgVW5pdCBTb2lsIENvbXBvc2l0aW9uIE1hdHJpeAoKRWFjaCBzb2lsIHVuaXQgcGVyY2VudGFnZSBjYW4gYmUgdXNlZCBhcyBhIG1hcCB1bml0IGRlc2NyaXB0b3IgdmFyaWFibGUgZm9yIAptdWx0aXZhcmlhdGUgYW5hbHlzaXMgb2YgdGhlIHBob3NwaG9ydXMgcmV0ZW50aW9uIHBvdGVudGlhbCBwZXIgbWFwIHVuaXQuClRoaXMgcmVzdWx0cyBpbiBzcGFyc2UgbWF0cml4IHdpdGggZXNzZW50aWFsbHkgb3J0aG9nb25hbCBjb2x1bW5zLgoKYGBge3J9Cm11X2NvbXAgPC0gZ2V0X211X2NvbXBvc2l0aW9uKG1hcHVuaXQsRkFPNzQpCmBgYAoKIyMgTWFwIHVuaXQgLSBQaG9zcGhvcnVzIFJldGVudGlvbiBKb2luIHRhYmxlCgpgYGB7cn0KCm11X3JldCA8LSBtYXB1bml0ICU+JQogIGRwbHlyOjpsZWZ0X2pvaW4oc29pbHVuaXQsIGJ5ID0gYygiU09JTDEiID0gImtleSIpKSAlPiUKICBkcGx5cjo6c2VsZWN0KE1VSUQsbWFpbmNsYXNzLExvLE1vLEhpLFZILE1JU0MscEgsU0FORCxTSUxULENMQVksQ0VDTEFZKQoKaXNfc29pbCA8LSBmdW5jdGlvbih4KXsKICAhKHggJWluJSBjKCJHTDEiLCJSSzEiLCJSSzIiLCJXUjEiKSkKfQoKcmV0X2NvbXAgPC0gc3Vic2V0KGNiaW5kKG11X3JldCxtdV9jb21wKSwKICAgICAgICAgICAgICAgICAgICBpc19zb2lsKG11X3JldCRtYWluY2xhc3MpKQoKc29pbGNsYXNzIDwtIHNvaWxjbGFzc1tvcmRlcihzb2lsY2xhc3MkYXNjZW5kaW5nKSxdCmFzY2VuZGluZ19vcmRlciA8LSBhcy5jaGFyYWN0ZXIoc29pbGNsYXNzJG1haW4pWzU6MTZdCnJldF9jb21wJG1haW5jbGFzcyA8LSBmYWN0b3IocmV0X2NvbXAkbWFpbmNsYXNzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGFzY2VuZGluZ19vcmRlcikKCmBgYAoKIyBEaXNjcmltaW5hbnQgYW5hbHlzaXMgdXNpbmcgYWRlNAoKIyMgU29pbCBSZXRlbnRpb24gUG90ZW50aWFsIFNwYWNlIGlzIGVzc2VudGlhbGx5IGEgdGV0cmFoZWRyb24gCiAgd2l0aCBNb2RlcmF0ZSByZXRlbnRpb24gYXQgaXRzIGNlbnRlci4KCk9ubHkgdGhyZWUgZGVncmVlcyBvZiBmcmVlZG9tLCBMby1WSCwgTW8sIE1vLUhpLAoKCmBgYHtyfQojIGNyZWF0ZSBkdWRpIG9iamVjdCBmb3IgZGlzY3JpbWluYW50IGFuYWx5c2lzCgpwY2EgPC0gZHVkaS5wY2EocmV0X2NvbXBbLC0oMToyKV0sc2Nhbm5mID0gRkFMU0UsIG5mPSA0KQoKZGlzIDwtIGRpc2NyaW1pbihwY2EsZmFjdG9yKHJldF9jb21wJG1haW5jbGFzcyksICBzY2FubmYgPSBGQUxTRSwgbmYgPSAzKQoKcGFsZXR0ZSA8LSBzb2lsY2xhc3MkY29sb3JfaGV4WzU6MTZdCmNvbG9yIDwtIHBhbGV0dGVbcmV0X2NvbXAkbWFpbmNsYXNzXQoKcGFyKG1mcm93ID0gYygyLCAyKSkKcy5jbGFzcyhkaXMkbGksIHhheCA9IDIsIHlheCA9IDEsIGZhYyA9IHJldF9jb21wJG1haW5jbGFzcywgY29sID0gcGFsZXR0ZSkKcy5jbGFzcyhkaXMkbGksIHhheCA9IDMsIHlheCA9IDEsIGZhYyA9IHJldF9jb21wJG1haW5jbGFzcywgY29sID0gcGFsZXR0ZSkKcy5jbGFzcyhkaXMkbGksIHhheCA9IDMsIHlheCA9IDIsIGZhYyA9IHJldF9jb21wJG1haW5jbGFzcywgY29sID0gcGFsZXR0ZSkKcy5jb3JjaXJjbGUoZGlzJHZhKQpwYXIobWZyb3cgPSBjKDEsIDEpKQpgYGAKCgojIERpc2NyaW1pbmFudCBhbmFseXNpcyB1c2luZyBNQVNTCmBgYHtyfQoKZml0IDwtIGxkYShtYWluY2xhc3MgfiAuLCBkYXRhPXJldF9jb21wWywtMV0pCgpwbGRhIDwtICBwcmVkaWN0KG9iamVjdCA9IGZpdCwgIyBwcmVkaWN0aW9ucwogICAgICAgICAgICAgICAgIG5ld2RhdGEgPSByZXRfY29tcFssLSgxOjIpXSkKCmBgYAojIExpbmVhcml6YXRpb24gb2YgUGhvc3Bob3J1cyByZXRlbnRpb24gcG90ZW50aWFsCgpGcm9tIGNhdGVnb3JpY2FsIHZhcmlhYmxlIHRvIGNvbnRpbm91cyAibGluZWFyIiBzY2FsZS4KTGluZWFyIGNvbWJpbmF0aW9uIG9mIG1hcCB1bml0IHNvaWwgY29udGVudC4KCmBgYHtyfQpyZXRfc2NhbGUgPC0gcmV0X2NvbXBbLGMoIk1VSUQiLCJMbyIsIk1vIiwiSGkiLCJWSCIsIk1JU0MiKV0KcmV0X3NjYWxlJHJldCA8LSByZXRfY29tcCRtYWluY2xhc3MKCiMgV2VpZ2h0ZWQgc2NhbGUsIGFkIGhvYyB3ZWlnaHRzISEhISEhCgpyZXRfc2NhbGUkd2VpZ2h0ZWQgPC0gd2l0aChyZXRfc2NhbGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHthcy5udW1lcmljKChMbyArIDIgKiBNbyArIDMgKiBIaSArIDQgKiBWSCkgLyA0MDApfSkKCiMgTGluZWFyIERpc2NyaW1hbnQgc2NhbGUsIHBvc3QgaG9jIHdlaWdodHMvY29lZmZpY2llbnRzCgpyZXRfc2NhbGUkTEQyIDwtICBwbGRhJHhbLDJdCmBgYAoKYGBge3IsIGZpZy5hc3AgPSAxfQpwbG90X1Bfc2NhbGVzKHJldF9zY2FsZSwgcGFsZXR0ZSA9IHBhbGV0dGUsCiAgICAgICAgICAgICAgc2NhbGVfeCA9ICJMRDIiLHNjYWxlX3kgPSAid2VpZ2h0ZWQiKQoKYGBgCgpgYGB7cn0Kc2NhbGUyNTYgPC0gZnVuY3Rpb24oeCl7CiAgYXMuaW50ZWdlcihyb3VuZCgyNDUgKiAoeCAtIG1pbih4LG5hLnJtID0gVFJVRSkpLyhtYXgoeCxuYS5ybSA9IFRSVUUpIC0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWluKHgsbmEucm0gPSBUUlVFKSkpICs1KX0KClBfcmV0ZW50aW9uIDwtIG11X3NvaWxjbGFzcyAlPiUgCiAgZHBseXI6OmxlZnRfam9pbihyZXRfc2NhbGUsIGJ5ID0gIk1VSUQiKQoKUF9yZXRlbnRpb24kd2VpZ2h0ZWQgPC0gc2NhbGUyNTYoUF9yZXRlbnRpb24kd2VpZ2h0ZWQpClBfcmV0ZW50aW9uJHdlaWdodGVkW2lzLm5hKFBfcmV0ZW50aW9uJHdlaWdodGVkKV0gPC0gMAoKUF9yZXRlbnRpb24kTEQyIDwtIHNjYWxlMjU2KFBfcmV0ZW50aW9uJExEMikgClBfcmV0ZW50aW9uJExEMlshaXNfc29pbChQX3JldGVudGlvbiRtYWluY2xhc3MpXSA8LSAwCgpQX3dlaWdodGVkIDwtIHJlYWRfUF9JU1JJQyh0aWYgPSB0ZXN0X3RpZiwgc29pbGNsYXNzID0gUF9yZXRlbnRpb24sCiAgICAgICAgICAgICAgICAgICAgIGlzID0gIk1VSUQiLAogICAgICAgICAgICAgICAgICAgICBiZWNvbWVzID0gIndlaWdodGVkIiwKICAgICAgICAgICAgICAgICAgICAgZmlsZW5hbWUgPSBmaWxlLnBhdGgoZXh0ZGF0YV9kaXIsInRpZiIsIlBfd2VpZ2h0ZWQudGlmIikpCgpQX0xEMiA8LSByZWFkX1BfSVNSSUModGlmID0gdGVzdF90aWYsIHNvaWxjbGFzcyA9IFBfcmV0ZW50aW9uLAogICAgICAgICAgICAgICAgICAgICBpcyA9ICJNVUlEIiwKICAgICAgICAgICAgICAgICAgICAgYmVjb21lcyA9ICJMRDIiLAogICAgICAgICAgICAgICAgICAgICBmaWxlbmFtZSA9IGZpbGUucGF0aChleHRkYXRhX2RpciwidGlmIiwiUF9MRDIudGlmIikpCmBgYAoKIyMgV2VpZ2h0ZWQgUCByZXRlbnRpb24gUG90ZW50aWFsCmBgYHtyLCBmaWcuaGVpZ2h0PTQsIGZpZy53aWR0aD0xMCwgd2FybmluZz1GQUxTRX0Kb3AgPC0gcGFyKCkKcGFyKG1hciA9IGMoMCwgMCwgMCwgMCksIG9tYSA9IGMoMCwgMCwgMCwgMCkpCnBsb3QoUF93ZWlnaHRlZCwgYXhlcyA9IEZBTFNFLCBib3ggPSBGQUxTRSwKICAgICBmcmFtZS5wbG90ID0gRkFMU0UsCiAgICAgbGVnZW5kLndpZHRoID0gMiwKICAgICBsZWdlbmQuc2hyaW5rID0gMC45NSwKICAgICBheGlzLmFyZ3MgPSBsaXN0KGNleC5heGlzID0gMiksCiAgICAgbGVnZW5kLmFyZ3MgPSBsaXN0KHRleHQgPSAiIiwgY2V4ID0gMSkpCnBhcihvcCkKYGBgCgojIyBMaW5lYXIgRGlzY3JpbWluYW50IFAgcmV0ZW50aW9uIFBvdGVudGlhbApgYGB7ciwgZmlnLmhlaWdodD00LCBmaWcud2lkdGg9MTAsIHdhcm5pbmc9RkFMU0V9Cm9wIDwtIHBhcigpCnBhcihtYXIgPSBjKDAsIDAsIDAsIDApLCBvbWEgPSBjKDAsIDAsIDAsIDApKQpwbG90KFBfTEQyLCBheGVzID0gRkFMU0UsIGJveCA9IEZBTFNFLAogICAgIGZyYW1lLnBsb3QgPSBGQUxTRSwKICAgICBsZWdlbmQud2lkdGggPSAyLAogICAgIGxlZ2VuZC5zaHJpbmsgPSAwLjk1LAogICAgIGF4aXMuYXJncyA9IGxpc3QoY2V4LmF4aXMgPSAyKSwKICAgICBsZWdlbmQuYXJncyA9IGxpc3QodGV4dCA9ICIiLCBjZXggPSAxKSkKcGFyKG9wKQpgYGAKCiMgU29pbCBNYXAgUGhvc3Bob3J1cyBSZXRlbnRpb24gUG90ZW50aWFsIGFzIFBlcmNlbnRhZ2VzIG9mIE1hcCBVbml0cwoKYGBge3IsIGZpZy5oZWlnaHQ9NCwgZmlnLndpZHRoPTEwLCB3YXJuaW5nPUZBTFNFfQoKUF9yZXRlbnRpb25bIWlzX3NvaWwoUF9yZXRlbnRpb24kbWFpbmNsYXNzKSxjKCJMbyIsIk1vIiwiSGkiLCJWSCIpXSA8LSAwClBfcmV0ZW50aW9uWyFpc19zb2lsKFBfcmV0ZW50aW9uJG1haW5jbGFzcyksIk1JU0MiXSA8LSAxMDAKCklTUklDMjAxMV9MbyA8LSByZWFkX1BfSVNSSUModGlmID0gdGVzdF90aWYsIHNvaWxjbGFzcyA9IFBfcmV0ZW50aW9uLAogICAgICAgICAgICAgICAgICAgICBpcyA9ICJNVUlEIiwKICAgICAgICAgICAgICAgICAgICAgYmVjb21lcyA9ICJMbyIsCiAgICAgICAgICAgICAgICAgICAgIGZpbGVuYW1lID0gZmlsZS5wYXRoKGV4dGRhdGFfZGlyLCJ0aWYiLCJJU1JJQzIwMTFfTG8udGlmIikpCgpJU1JJQzIwMTFfTW8gPC0gcmVhZF9QX0lTUklDKHRpZiA9IHRlc3RfdGlmLCBzb2lsY2xhc3MgPSBQX3JldGVudGlvbiwKICAgICAgICAgICAgICAgICAgICAgaXMgPSAiTVVJRCIsCiAgICAgICAgICAgICAgICAgICAgIGJlY29tZXMgPSAiTW8iLAogICAgICAgICAgICAgICAgICAgICBmaWxlbmFtZSA9IGZpbGUucGF0aChleHRkYXRhX2RpciwidGlmIiwiSVNSSUMyMDExX01vLnRpZiIpKQoKSVNSSUMyMDExX0hpIDwtIHJlYWRfUF9JU1JJQyh0aWYgPSB0ZXN0X3RpZiwgc29pbGNsYXNzID0gUF9yZXRlbnRpb24sCiAgICAgICAgICAgICAgICAgICAgIGlzID0gIk1VSUQiLAogICAgICAgICAgICAgICAgICAgICBiZWNvbWVzID0gIkhpIiwKICAgICAgICAgICAgICAgICAgICAgZmlsZW5hbWUgPSBmaWxlLnBhdGgoZXh0ZGF0YV9kaXIsInRpZiIsIklTUklDMjAxMV9IaS50aWYiKSkKSVNSSUMyMDExX1ZIIDwtIHJlYWRfUF9JU1JJQyh0aWYgPSB0ZXN0X3RpZiwgc29pbGNsYXNzID0gUF9yZXRlbnRpb24sCiAgICAgICAgICAgICAgICAgICAgIGlzID0gIk1VSUQiLAogICAgICAgICAgICAgICAgICAgICBiZWNvbWVzID0gIlZIIiwKICAgICAgICAgICAgICAgICAgICAgZmlsZW5hbWUgPSBmaWxlLnBhdGgoZXh0ZGF0YV9kaXIsInRpZiIsIklTUklDMjAxMV9WSC50aWYiKSkKCklTUklDMjAxMV9NSVNDIDwtIHJlYWRfUF9JU1JJQyh0aWYgPSB0ZXN0X3RpZiwgc29pbGNsYXNzID0gUF9yZXRlbnRpb24sCiAgICAgICAgICAgICAgICAgICAgIGlzID0gIk1VSUQiLAogICAgICAgICAgICAgICAgICAgICBiZWNvbWVzID0gIk1JU0MiLAogICAgICAgICAgICAgICAgICAgICBmaWxlbmFtZSA9IGZpbGUucGF0aChleHRkYXRhX2RpciwidGlmIiwiSVNSSUMyMDExX01JU0MudGlmIikpCmBgYAoKIyMgTG8gUCByZXRlbnRpb24gUG90ZW50aWFsCmBgYHtyLCBmaWcuaGVpZ2h0PTQsIGZpZy53aWR0aD0xMCwgd2FybmluZz1GQUxTRX0Kb3AgPC0gcGFyKCkKcGFyKG1hciA9IGMoMCwgMCwgMCwgMCksIG9tYSA9IGMoMCwgMCwgMCwgMCkpCnBsb3QoSVNSSUMyMDExX0xvLCBheGVzID0gRkFMU0UsIGJveCA9IEZBTFNFLAogICAgIGZyYW1lLnBsb3QgPSBGQUxTRSwKICAgICBsZWdlbmQud2lkdGggPSAyLAogICAgIGxlZ2VuZC5zaHJpbmsgPSAwLjk1LAogICAgIGF4aXMuYXJncyA9IGxpc3QoY2V4LmF4aXMgPSAyKSwKICAgICBsZWdlbmQuYXJncyA9IGxpc3QodGV4dCA9ICIiLCBjZXggPSAxKSkKcGFyKG9wKQpgYGAKCiMjIE1vIFAgcmV0ZW50aW9uIFBvdGVudGlhbApgYGB7ciwgZmlnLmhlaWdodD00LCBmaWcud2lkdGg9MTAsIHdhcm5pbmc9RkFMU0V9Cm9wIDwtIHBhcigpCnBhcihtYXIgPSBjKDAsIDAsIDAsIDApLCBvbWEgPSBjKDAsIDAsIDAsIDApKQpwbG90KElTUklDMjAxMV9NbywgYXhlcyA9IEZBTFNFLCBib3ggPSBGQUxTRSwKICAgICBmcmFtZS5wbG90ID0gRkFMU0UsCiAgICAgbGVnZW5kLndpZHRoID0gMiwKICAgICBsZWdlbmQuc2hyaW5rID0gMC45NSwKICAgICBheGlzLmFyZ3MgPSBsaXN0KGNleC5heGlzID0gMiksCiAgICAgbGVnZW5kLmFyZ3MgPSBsaXN0KHRleHQgPSAiIiwgY2V4ID0gMSkpCnBhcihvcCkKYGBgCgojIyBIaSBQIHJldGVudGlvbiBQb3RlbnRpYWwKYGBge3IsIGZpZy5oZWlnaHQ9NCwgZmlnLndpZHRoPTEwLCB3YXJuaW5nPUZBTFNFfQpvcCA8LSBwYXIoKQpwYXIobWFyID0gYygwLCAwLCAwLCAwKSwgb21hID0gYygwLCAwLCAwLCAwKSkKcGxvdChJU1JJQzIwMTFfSGksIGF4ZXMgPSBGQUxTRSwgYm94ID0gRkFMU0UsCiAgICAgZnJhbWUucGxvdCA9IEZBTFNFLAogICAgIGxlZ2VuZC53aWR0aCA9IDIsCiAgICAgbGVnZW5kLnNocmluayA9IDAuOTUsCiAgICAgYXhpcy5hcmdzID0gbGlzdChjZXguYXhpcyA9IDIpLAogICAgIGxlZ2VuZC5hcmdzID0gbGlzdCh0ZXh0ID0gIiIsIGNleCA9IDEpKQpwYXIob3ApCmBgYAoKIyMgVkggUCByZXRlbnRpb24gUG90ZW50aWFsCmBgYHtyLCBmaWcuaGVpZ2h0PTQsIGZpZy53aWR0aD0xMCwgd2FybmluZz1GQUxTRX0Kb3AgPC0gcGFyKCkKcGFyKG1hciA9IGMoMCwgMCwgMCwgMCksIG9tYSA9IGMoMCwgMCwgMCwgMCkpCnBsb3QoSVNSSUMyMDExX1ZILCBheGVzID0gRkFMU0UsIGJveCA9IEZBTFNFLAogICAgIGZyYW1lLnBsb3QgPSBGQUxTRSwKICAgICBsZWdlbmQud2lkdGggPSAyLAogICAgIGxlZ2VuZC5zaHJpbmsgPSAwLjk1LAogICAgIGF4aXMuYXJncyA9IGxpc3QoY2V4LmF4aXMgPSAyKSwKICAgICBsZWdlbmQuYXJncyA9IGxpc3QodGV4dCA9ICIiLCBjZXggPSAxKSkKcGFyKG9wKQpgYGAKCiMjIE1JU0MgUCByZXRlbnRpb24gUG90ZW50aWFsCmBgYHtyLCBmaWcuaGVpZ2h0PTQsIGZpZy53aWR0aD0xMCwgd2FybmluZz1GQUxTRX0Kb3AgPC0gcGFyKCkKcGFyKG1hciA9IGMoMCwgMCwgMCwgMCksIG9tYSA9IGMoMCwgMCwgMCwgMCkpCnBsb3QoSVNSSUMyMDExX01JU0MsIGF4ZXMgPSBGQUxTRSwgYm94ID0gRkFMU0UsCiAgICAgZnJhbWUucGxvdCA9IEZBTFNFLAogICAgIGxlZ2VuZC53aWR0aCA9IDIsCiAgICAgbGVnZW5kLnNocmluayA9IDAuOTUsCiAgICAgYXhpcy5hcmdzID0gbGlzdChjZXguYXhpcyA9IDIpLAogICAgIGxlZ2VuZC5hcmdzID0gbGlzdCh0ZXh0ID0gIiIsIGNleCA9IDEpKQpwYXIob3ApCmBgYAo=