This notebook is to replicate Sara’s district-wise poverty regressions in R and explore alternate approaches to defining spatial weights across districts (testing for spatial autocorrelation in the series of predictors), and to map GWR coefficients. Sara’s original code is taken from 3 Regression analysis district rural poverty - shock reg.do.

library(raster)
library(foreign)
library(data.table)
library(spdep)
library(splm)
library(spgwr)
library(tmap)
library(stringr)
# Presentation options
library(pander)
panderOptions("big.mark", ",")
panderOptions("round", 2)
panderOptions("missing", ".")
panderOptions("table.split.table", 100)
# Load R workspace on BUSTER
setwd("~/Projects/hc-shiny/geospat")
load("./tmp/poverty_r16.05.RData")
#setwd("~/Dropbox (IFPRI)/SDA/Data/analysis/_global_codes")
#load("./temp/2016.05/poverty_r16.05.RData")
# Helper
"%||%" <- function(a, b) if (!is.null(a)) a else b
# Helper - Collect important coefficients from `splm` results
# Could also check if there's an existing broom method for `splm` and `grw` models...
splm.combine <- function(x) data.table(
  model=names(x), 
  do.call(rbind, lapply(x, coef)), 
  do.call(rbind, lapply(x, `[[`, "arcoef")),
  phi=as.numeric(do.call(rbind, lapply(x, function(x) try(x$errcomp[["phi"]])))),
  psi=as.numeric(do.call(rbind, lapply(x, function(x) try(x$errcomp[["psi"]])))),
  rho=as.numeric(do.call(rbind, lapply(x, function(x) try(x$errcomp[["rho"]])))))
# Helper - AIC function for `spml`, show goodness of fit measure
# Credits to https://stat.ethz.ch/pipermail/r-sig-geo/2016-February/024077.html
spml.godf<-function(object, k=2, criterion=c("AIC", "BIC"),  ...) {
  s <- summary(object)
  l <- s$logLik[1,1]
  np <- length(coef(s))
  N <- nrow(s$model)
  if (criterion=="AIC") {
    aic <- -2*l+k*np
    names(aic) <-"AIC"
    return(aic)
  }
  if (criterion=="BIC") {
    bic <- -2*l+log(N)*np
    names(bic) <-"BIC"
    if (k!=2) {
      warning("parameter <k> not used for BIC")
    }
    return(bic)
  }
}

Data

# Load districts and attributes
g2 <- shapefile("./maps/svyMaps_2016.06.22_sara.shp")
dt2 <- read.dta("./tmp/SSApoverty_Dist_forGWR.12.dta")
#g2 <- shapefile("./out/r16.05/svyMaps_2016.06.22_sara.shp")
#dt2 <- read.dta("./temp/2016.05/SSApoverty_Dist_forGWR.12.dta")
# Keep STATA labels for re-use
dt2.lbl <- data.table(varCode=names(dt2), varLabel=attr(dt2, "var.labels"))
setkey(dt2.lbl, varCode)
dt2.lbl[is.na(varLabel), varLabel := varCode]
dt2 <- data.table(dt2)
# Verify that we have all explanatory variables and shocks already constructed, e.g.
rbind(
  dt2.lbl[varCode %like% "ndvi"],
  dt2.lbl[varCode %like% "shock"],
  dt2.lbl[varCode %like% "AEZ"],
  dt2.lbl[varCode %like% "land"])
# Make unique shape IDs explicit
g2$rn <- row.names(g2)
g2.dt <- data.table(g2@data)
# Recode Ethiopia woredas
dt2[svyCode=="eth2010", svyL2Cd := svyL1Cd * 10000 + svyL2Cd]
# Merge shapes and attributes
setkey(g2.dt, ISO3, svyCode, svyL1Cd, svyL2Cd)
setkey(dt2, ISO3, svyCode, svyL1Cd, svyL2Cd)
# Look for possible duplicates
g2.dt[duplicated(g2.dt), .(ISO3, svyCode, svyL1Cd, svyL2Cd)]
dt2[duplicated(dt2), .(ISO3, svyCode, svyL1Cd, svyL2Cd)]
# Drop duplicated vars from Sara's file before merging
dt2[, `:=`(
  rn=NULL, svyL1Nm=NULL, svyL2Nm=NULL, prttyNm=NULL, areakm=NULL, X=NULL, Y=NULL)]
# Any unmatched obs?
dt2[!g2.dt, .N, by=svyCode]
# Seems okay, so let's merge
g2.dt <- dt2[g2.dt]
# Re-attach Sara's attributes to shapes
g2 <- SpatialPolygonsDataFrame(g2, data.frame(g2.dt), match.ID="rn")
# Visually check a few vars
data(World)
p0 <- tm_shape(World) + tm_fill("grey90") + tm_shape(g2, is.master=T)
p1 <- tm_shape(World) + tm_borders("grey50", lwd=.2)
p0 + tm_fill("ndvi_ave", title="NDVI\nLong-term mean",
  style="pretty", n=9, palette=rev(pal.nrwc), auto=F) + p1

p0 + tm_fill("pcexp_ppp_m", title="Per Capita\nExpenditure\n(PPP USD/month)",
  style="jenks", n=9, auto=F) + p1

p0 + tm_fill("foodexp_ppp_m", title="Food Expenditure\n(PPP USD/month)",
  style="jenks", n=9, auto=F) + p1

p0 + tm_fill("pre_lt", title="Precipitation\nlong-term mean\n(mm)",
  style="jenks", n=9, palette=pal.pre, auto=F) + p1

p0 + tm_fill("spei_lt", title="SPEI\nlong-term mean",
  style="jenks", n=9, palette="RdYlGn", auto=F) + p1

p0 + tm_fill("nighlight_yearly", title="Night Light\nReflectance",
  style="kmeans", n=9, palette=pal.earth[1:230], auto=F) + p1

p0 + tm_fill("tt10_20k", title="Travel Time\nto nearest 20K market\n(hours)",
  style="fixed", breaks=c(.5,1,2,4,6,8,10,12,24), palette="-YlOrRd", auto=F) + p1

Model Specifications

Following Sara’s setup, 2 outcome variables Y, 9 alternate groups of biophysical and climatic shock variables and 4 cross-combinations of predictors are considered, for a total of 72 models. Sara chose a SAC form in STATA (equivalent to sacsarlm() in R).

# Define outcome vars
Y <- c("pcexp_ppp_m", "foodexp_ppp_m")
# Define groups of explanatory vars
X <- list(
  socio    = c("hh_female", "agehead", "marriedhead", "meaneduc", "depratio2", "children"),
  agri     = c("LGP_AVG", "ELEVATION", "cropland_pcap", "TLU_pcap"),
  access   = c("nighlight_yearly", "tt10_20k", "PD12_TOT"),
  weather1 = c("spei_lt", "L1_speidif", "L1_speidif2"),
  weather2 = c("spei_lt", "Lcum_speidif", "Lcum_speidif2"), 
  weather3 = c("spei_lt", "L1_speihishock", "L1_speiloshock"),
  weather4 = c("pre_lt", "L1_predif", "L1_predif2", "temp_lt", "L1_tempdif", "L1_tempdif2"),
  weather5 = c("pre_lt", "Lcum_predif", "Lcum_predif2", "temp_lt", "Lcum_tempdif", "Lcum_tempdif2"),
  weather6 = c("pre_lt", "temp_lt", "L1_prehishock", "L1_preloshock", "L1_temphishock"),
  weather7 = c("ndvi_lt", "L1_ndvidif", "L1_ndvidif2"), 
  weather8 = c("ndvi_lt", "Lcum_ndvidif", "Lcum_ndvidif2"), 
  weather9 = c("ndvi_lt", "L1_ndvihishock", "L1_ndviloshock"),
  other    = c("Malaria_yearly"),
  AEZ      = c("arid_warm", "humid_cool", "humid_warm"),
  country  = c("ISO3"),
  yr       = c("year"))
# Create a list of 72 model formulas
models <- list()
for (i in Y) for (j in 1:9) for (k in LETTERS[1:4]) {
  models[[paste0(i, " ~ clim-", k, j)]] <- as.formula(switch(k,
    A = paste0(i, "~", paste(c(X[[paste0("weather", j)]]), collapse="+")),
    B = paste0(i, "~", paste(c(X[[paste0("weather", j)]], X$socio), collapse="+")),
    C = paste0(i, "~", paste(c(X[[paste0("weather", j)]], X$socio, X$access, X$agri), collapse="+")),
    D = paste0(i, "~", paste(c(X[[paste0("weather", j)]], X$socio, X$access, X$agri, X$other), collapse="+"))
  ))
}
# Print first/last 2 formulas
pander(models[c(1:2, 71:72)])
  • pcexp_ppp_m ~ clim-A1: pcexp_ppp_m ~ spei_lt + L1_speidif + L1_speidif2
  • pcexp_ppp_m ~ clim-B1: pcexp_ppp_m ~ spei_lt + L1_speidif + L1_speidif2 + hh_female + agehead + marriedhead + meaneduc + depratio2 + children
  • foodexp_ppp_m ~ clim-C9: foodexp_ppp_m ~ ndvi_lt + L1_ndvihishock + L1_ndviloshock + hh_female + agehead + marriedhead + meaneduc + depratio2 + children + nighlight_yearly + tt10_20k + PD12_TOT + LGP_AVG + ELEVATION + cropland_pcap + TLU_pcap
  • foodexp_ppp_m ~ clim-D9: foodexp_ppp_m ~ ndvi_lt + L1_ndvihishock + L1_ndviloshock + hh_female + agehead + marriedhead + meaneduc + depratio2 + children + nighlight_yearly + tt10_20k + PD12_TOT + LGP_AVG + ELEVATION + cropland_pcap + TLU_pcap + Malaria_yearly
# Verify that all vars included in these models exist in the dataset
vars.missing <- setdiff(unlist(X,Y), dt2.lbl$varCode)
vars.missing
[1] "Lcum_speidif"  "Lcum_speidif2" "Lcum_predif"   "Lcum_predif2"  "Lcum_tempdif" 
[6] "Lcum_tempdif2" "ndvi_lt"       "Lcum_ndvidif"  "Lcum_ndvidif2"

Nine vars are missing so we need to reconstruct them or else drop the corresponding models from the analysis (done for now).

# Drop models with missing vars
models.drop <- sapply(models, str_locate, vars.missing)
models.drop <- colSums(models.drop, na.rm=T)
models <- models[models.drop==0]

This leaves 56 models to estimate.

Imputations

In R spatial regression commands require all predictor variables to be non-missing. Here we use simple region median value (or country in case regional median is missing). Other interpolation methods for missing values could be used (e.g. triangulation or kriging using district centroids). Since we are interpolating a biophysical phenomenon (SPEI) these techniques might indeed be more appropriate.


X <- unique(unlist(sapply(models, '[[', "X")))
X

g2.dt <- data.table(g2@data)

# Impute missing X values with regional median
g2.dt[, spei_lt_imp := median(spei_lt, na.rm=T), by=.(svyCode, svyL1Cd)]
g2.dt[is.na(spei_lt), spei_lt := spei_lt_imp]
g2.dt[is.na(spei_lt), .N, by=svyCode]

# Impute still missing X values with national median
g2.dt[, spei_lt_imp := median(spei_lt, na.rm=T), by=svyCode]
g2.dt[is.na(spei_lt), spei_lt := spei_lt_imp]

# Verify
g2.dt[is.na(spei_lt), .N, by=svyCode]

# Tabulate


# Re-attach imputed attributes to shapes
g2 <- SpatialPolygonsDataFrame(g2, data.frame(g2.dt), match.ID="rn")

Spatial Effects

This section is to test for the presence of spatial autocorrelation in one or more of the predictors, and to choose a regression approach such that model residuals are not spatially autocorrelated (thus ensuring a better fit).

Moran’s I Statistic


moran.plot(g2.nb@data[, Y], w, zero.policy=T,
  xlim=c(0, 200), ylim=c(0, 200),
  xlab=dt2.lbl[Y, varLabel], 
  ylab=paste("Spatially Lagged", Y))

#plot(
#  variogram(as.formula(paste(Y, "~1")), 
#    locations=coordinates(g2.nb), data=g2.nb, cloud=F), 
#  type="b", pch=16, main=paste("Variogram of", dt2.lbl[Y, varLabel]))

moran.mc(g2.nb@data[, Y], w, zero.policy=T, nsim=999)

moran.plot(g2.nb@data[, X[1]], w, zero.policy=T,
  xlab=dt2.lbl[X[1], varLabel], 
  ylab=paste("Spatially Lagged", X[1]))

moran.mc(g2.nb@data[, X[1]], w, zero.policy=T, nsim=999)

Spatial Weights

Need to choose between QUEEN or ROOK contiguity or else we can experiment with k-nearest points/shapes as neighbors using knn2nb() instead of poly2nb(). Can also assign neighbors based on a given distance threshold using dnearneigh().

Note that contiguity requires valid topology (which is surely not the case in this shapefile). Argument snap may be used to correct for slivers. Else one can use edit(nn) to make manual corrections to the matrix. Another approach to address non-contiguous splatial features is to use the feature centroid, or any weighted centroid (e.g. population weighted centroid of the admin unit).

There’s also the issue of choosing a method for the spatial weights (row-standardized, binary). Typically Row standardization is used to create proportional weights in cases where features have an unequal number of neighbors. Use Binary when you want comparable spatial parameters across different data sets with different connectivity structures.


# Generate spatial neighbour list for SSA
nb2 <- poly2nb(g2.nb, row.names=paste(g2.nb$ISO3, g2.nb$rn, sep="."))
summary(nb2)

# Verify the 5 discontiguous districts
bad <- c("477", "1052", "1343", "1746", "1947")
g2.nb.dt[rn %in% bad, .(rn, ISO3, svyL1Nm, svyL2Nm, pcexp_ppp_m)]

for(i in c("AGO", "ETH", "SEN")) {
  bb <- bbox(g2.nb[g2.nb$ISO3==i & g2.nb$rn %in% bad,])
  bb <- bb + c(-.5, -.5, .5, .5)
  print(
    tm_shape(g2, bbox=bb) + tm_borders() +
      tm_shape(g2.nb) + tm_fill("pcexp_ppp_m") + 
      tm_shape(g2.nb[g2.nb$ISO3==i & g2.nb$rn %in% bad,]) +
      tm_borders(col="red") + tm_text("svyL2Nm", col="red") +
      tm_layout(legend.outside=T)
  )}

They’re not islands, but surrounding districts have no data. Need to check a little more what’s going on, fix in QGIS if needed. Also refer to Bivand:

I did look at this 15 years ago with Boris Portnov, in the context of ESDA:

@incollection{bivand+portnov:04,
author = {R. S. Bivand and B. A. Portnov}, editor = {L. Anselin and R. J. G. M. Florax and S. J. Rey}, title = {Exploring spatial data analysis techniques using {}: the case of observations with no neighbours}, booktitle = {Advances in Spatial Econometrics: Methodology, Tools, Applications},
year = {2004},
publisher = {Springer},
address = {Berlin},
pages = {121–142} }

There are oddities in the Moran scatterplot, and also in mapping the graph-based neighbour representation into matrix form, say with the spatial lag of a no-neighbour observation’s value being zero (for zero.policy=TRUE). That paper was the basis for the zero.policy= framework. There are other consequences that you’ve found with respect to the number of subgraphs, which may or may not break formal assumptions of analysis methods. In addition, we don’t know how far the broken assumptions actually matter. This would probably be a good candidate for proper study including simulation.

Plot Contiguities


# Plot contiguities in a few countries
for (i in c("GHA", "ETH", "AGO", "SEN")) {
  
  tmp <- g2.nb[g2.nb$ISO3==i,]
  coords.tmp <- coordinates(tmp)
  nb2.tmp <- poly2nb(tmp)
  nb2.tmp
  
  plot(g2[g2$ISO3==i,], col="red", lwd=0.1)
  plot(tmp, col="grey90", lwd=0.1, add=T)
  plot(nb2.tmp, coords.tmp, col="blue", add=T)
  title(main=paste("Contiguity -", i), font.main=1)
}

# Save distance matrix for SSA (W=row standardized) to STATA for re-use
w2 <- nb2mat(nb2, style="W", zero.policy=T)
w2 <- as.data.frame(w2)
attr(w2, "var.labels") <- paste(g2$svyCode, g2$svyL1Cd, g2$svyL2Cd, sep=".")
write.dta(w2, "../out/r16.05/poverty_continguity.dta", version=12)

# Check population weights
summary(g2.nb$pop)

# Spatial weights for SSA (check doco for how to include pop weights)
# Note that If zero.policy is set to TRUE, weights vectors of zero length are inserted
# for regions without neighbour in the neighbours list. 
w <- nb2listw(nb2, zero.policy=T)

Spatial Regressions (OLS, LAG, SAC)

Batch run all models using OLS, LAG, and SAC regressions and save results to draw comparisons.


# Drop admin units with missing outcome values (not included in model)
g2.nb <- g2[!is.na(g2[[x]]),]

# Compare models
fm <- as.formula(paste(Y, "~", paste(X, collapse="+")))
fm

# Model 1: simple OLS
m <- lm(fm, data=g2.nb.dt, weights=1/pop)
summary(m)

# Examine spatial autocorrelation among the residuals
lm.morantest(m, listw=w, zero.policy=T)

# Model 2: LAG model
mlagsar <- lagsarlm(fm, w, zero.policy=T, data=g2.nb.dt)
summary(mlagsar)

# Model 3: SAC model
msacsar <- sacsarlm(fm, w, zero.policy=T, data=g2.nb.dt)
summary(msacsar)

# Also show impact effects of spatial models
# To understand the direct (local), indirect(spill-over), and total effect of a unit 
# change in each of the predictor variables
W <- as(w, "CsparseMatrix")
trMatc <- trW(W, type="mult")

impacts(mlagsar, tr=trMatc, R=2000)
impacts(msacsar, tr=trMatc, R=2000)

summary(impacts(mlagsar, tr=trMatc, R=2000), zstats=T, short=T)
summary(impacts(msacsar, tr=trMatc, R=2000), zstats=T, short=T)

The output from impacts() in the LAG model says that a 1 point increase in long-term SPEI leads to an increase in expenditure of PPP $11/month. A 1 sd increase in drought leads to a reduction in expenditure of PPP $20/month.


# Label 20 districts at random
rnd <- sample(1:nrow(g2.nb.dt), 20)

# Plot OLS
plot(m$model$pcexp_ppp_m, m$fitted.values,
  main=m$call, xlab=fm, cex=.5, pch=16,
  xlim=c(0, 300), ylim=c(0, 300))

text(m$model$pcexp_ppp_m[rnd], m$fitted.values[rnd], 
  labels=g2.nb.dt[rnd, svyL2Nm],
  cex=.6, pos=4)

# And residuals x fitted values
plot(m, which=3)

# Plot LAG
plot(mlagsar$y, mlagsar$fitted.values,
  main=mlagsar$call, xlab=fm, cex=.5, pch=16,
  xlim=c(0, 300), ylim=c(0, 300))

text(mlagsar$y[rnd], mlagsar$fitted.values[rnd], 
  labels=g2.nb.dt[rnd, svyL2Nm],
  cex=.6, pos=4)

# Plot SAC
plot(msacsar$y, msacsar$fitted.values,
  main=msacsar$call, xlab=fm, cex=.5, pch=16,
  xlim=c(0, 300), ylim=c(0, 300))

text(msacsar$y[rnd], msacsar$fitted.values[rnd], 
  labels=g2.nb.dt[rnd, svyL2Nm],
  cex=.6, pos=4)

Geographically Weighted Regression

See Bivand at https://cran.r-project.org/web/packages/spgwr/vignettes/GWR.pdf and Anselin http://www.csiss.org/gispopsci/workshops/2011/PSU/readings/W15_Anselin2007.pdf. Also Brunsdon http://rpubs.com/chrisbrunsdon/101305.

Note that sampling weights are not implemented. Choosing a method to estimate optimal bandwidth is unclear (check doco), also not clear how to choose a kernel function (default Gaussian).


# Load package
library(spgwr)

# Try GWR/LM on same model as above (pass shapes, will return shapes with coeff)
bwG <- gwr.sel(fm, data=g2.nb, gweight=gwr.Gauss, verbose=FALSE)
gwrG <- gwr(fm, data=g2.nb, bandwidth=bwG, gweight=gwr.Gauss, hatmatrix=TRUE)
gwrG

# Map coefficients
data(World)

for (i in X) {print(
    tm_shape(gwrG$SDF, is.master=T) + 
      tm_fill(i, palette="RdYlGn", style="jenks", n=9,
        title=stringr::str_wrap(dt2.lbl[Y, varLabel], 30)) +
      tm_shape(World) + tm_borders(lwd=0.1) +
      tm_layout(
        title=dt2.lbl[i, varLabel],
        title.snap.to.legend=T, legend.outside=T)
  )}
# Could also try a GWR/GLM on same model as above
bwG <- ggwr.sel(fm, data=g2.nb, gweight=gwr.Gauss, verbose=FALSE)
gwrG <- ggwr(fm, data=g2.nb, bandwidth=bwG, gweight=gwr.Gauss, hatmatrix=TRUE,
  family="poisson")
save.image("./tmp/poverty_r16.05.RData")
LS0tCnRpdGxlOiAgU1NBIFBvdmVydHkgLSBTcGF0aWFsIFJlZ3Jlc3Npb24KYXV0aG9yOiBJRlBSSS9IYXJ2ZXN0Q2hvaWNlCmRhdGU6ICAgTGFzdCB1cGRhdGVkIG9uIGByIFN5cy5EYXRlKClgLiBEUkFGVCwgRE8gTk9UIFVTRSBPUiBDSVRFLgpvdXRwdXQ6IAogIGh0bWxfbm90ZWJvb2s6CiAgICBmaWdfY2FwdGlvbjogeWVzCiAgICBmaWdfaGVpZ2h0OiA1CiAgICB0b2M6IHllcwogICAgdG9jX2RlcHRoOiAyCiAgICB0b2NfZmxvYXQ6IHllcwotLS0KClRoaXMgbm90ZWJvb2sgaXMgdG8gcmVwbGljYXRlIFNhcmEncyBkaXN0cmljdC13aXNlIHBvdmVydHkgcmVncmVzc2lvbnMgaW4gUiBhbmQgZXhwbG9yZSBhbHRlcm5hdGUgYXBwcm9hY2hlcyB0byBkZWZpbmluZyBzcGF0aWFsIHdlaWdodHMgYWNyb3NzIGRpc3RyaWN0cyAodGVzdGluZyBmb3Igc3BhdGlhbCBhdXRvY29ycmVsYXRpb24gaW4gdGhlIHNlcmllcyBvZiBwcmVkaWN0b3JzKSwgYW5kIHRvIG1hcCBHV1IgY29lZmZpY2llbnRzLiBTYXJhJ3Mgb3JpZ2luYWwgY29kZSBpcyB0YWtlbiBmcm9tIFszIFJlZ3Jlc3Npb24gYW5hbHlzaXMgZGlzdHJpY3QgcnVyYWwgcG92ZXJ0eSAtIHNob2NrIHJlZy5kb10oaHR0cHM6Ly93d3cuZHJvcGJveC5jb20vd29yay9TREEvRGF0YS9hbmFseXNpcy9fZ2xvYmFsX2NvZGVzL2RvL1NTQSBEaXN0IFBvdiZDQy8zIFJlZyBkaXN0cmljdCBydXJhbCBwb3ZlcnR5LmRvKS4KCgpgYGB7ciwgbWVzc2FnZT1GQUxTRX0KCmxpYnJhcnkocmFzdGVyKQpsaWJyYXJ5KGZvcmVpZ24pCmxpYnJhcnkoZGF0YS50YWJsZSkKbGlicmFyeShzcGRlcCkKbGlicmFyeShzcGxtKQpsaWJyYXJ5KHNwZ3dyKQpsaWJyYXJ5KHRtYXApCmxpYnJhcnkoc3RyaW5ncikKCmBgYAoKYGBge3IsIGNvbGxhcHNlPVRSVUV9CgojIFByZXNlbnRhdGlvbiBvcHRpb25zCmxpYnJhcnkocGFuZGVyKQpwYW5kZXJPcHRpb25zKCJiaWcubWFyayIsICIsIikKcGFuZGVyT3B0aW9ucygicm91bmQiLCAyKQpwYW5kZXJPcHRpb25zKCJtaXNzaW5nIiwgIi4iKQpwYW5kZXJPcHRpb25zKCJ0YWJsZS5zcGxpdC50YWJsZSIsIDEwMCkKCmBgYAoKYGBge3J9CgojIExvYWQgUiB3b3Jrc3BhY2Ugb24gQlVTVEVSCnNldHdkKCJ+L1Byb2plY3RzL2hjLXNoaW55L2dlb3NwYXQiKQpsb2FkKCIuL3RtcC9wb3ZlcnR5X3IxNi4wNS5SRGF0YSIpCiNzZXR3ZCgifi9Ecm9wYm94IChJRlBSSSkvU0RBL0RhdGEvYW5hbHlzaXMvX2dsb2JhbF9jb2RlcyIpCiNsb2FkKCIuL3RlbXAvMjAxNi4wNS9wb3ZlcnR5X3IxNi4wNS5SRGF0YSIpCgpgYGAKCmBgYHtyLCBjb2xsYXBzZT1UUlVFfQoKIyBIZWxwZXIKIiV8fCUiIDwtIGZ1bmN0aW9uKGEsIGIpIGlmICghaXMubnVsbChhKSkgYSBlbHNlIGIKCiMgSGVscGVyIC0gQ29sbGVjdCBpbXBvcnRhbnQgY29lZmZpY2llbnRzIGZyb20gYHNwbG1gIHJlc3VsdHMKIyBDb3VsZCBhbHNvIGNoZWNrIGlmIHRoZXJlJ3MgYW4gZXhpc3RpbmcgYnJvb20gbWV0aG9kIGZvciBgc3BsbWAgYW5kIGBncndgIG1vZGVscy4uLgpzcGxtLmNvbWJpbmUgPC0gZnVuY3Rpb24oeCkgZGF0YS50YWJsZSgKICBtb2RlbD1uYW1lcyh4KSwgCiAgZG8uY2FsbChyYmluZCwgbGFwcGx5KHgsIGNvZWYpKSwgCiAgZG8uY2FsbChyYmluZCwgbGFwcGx5KHgsIGBbW2AsICJhcmNvZWYiKSksCiAgcGhpPWFzLm51bWVyaWMoZG8uY2FsbChyYmluZCwgbGFwcGx5KHgsIGZ1bmN0aW9uKHgpIHRyeSh4JGVycmNvbXBbWyJwaGkiXV0pKSkpLAogIHBzaT1hcy5udW1lcmljKGRvLmNhbGwocmJpbmQsIGxhcHBseSh4LCBmdW5jdGlvbih4KSB0cnkoeCRlcnJjb21wW1sicHNpIl1dKSkpKSwKICByaG89YXMubnVtZXJpYyhkby5jYWxsKHJiaW5kLCBsYXBwbHkoeCwgZnVuY3Rpb24oeCkgdHJ5KHgkZXJyY29tcFtbInJobyJdXSkpKSkpCgojIEhlbHBlciAtIEFJQyBmdW5jdGlvbiBmb3IgYHNwbWxgLCBzaG93IGdvb2RuZXNzIG9mIGZpdCBtZWFzdXJlCiMgQ3JlZGl0cyB0byBodHRwczovL3N0YXQuZXRoei5jaC9waXBlcm1haWwvci1zaWctZ2VvLzIwMTYtRmVicnVhcnkvMDI0MDc3Lmh0bWwKc3BtbC5nb2RmPC1mdW5jdGlvbihvYmplY3QsIGs9MiwgY3JpdGVyaW9uPWMoIkFJQyIsICJCSUMiKSwgIC4uLikgewogIHMgPC0gc3VtbWFyeShvYmplY3QpCiAgbCA8LSBzJGxvZ0xpa1sxLDFdCiAgbnAgPC0gbGVuZ3RoKGNvZWYocykpCiAgTiA8LSBucm93KHMkbW9kZWwpCiAgaWYgKGNyaXRlcmlvbj09IkFJQyIpIHsKICAgIGFpYyA8LSAtMipsK2sqbnAKICAgIG5hbWVzKGFpYykgPC0iQUlDIgogICAgcmV0dXJuKGFpYykKICB9CiAgaWYgKGNyaXRlcmlvbj09IkJJQyIpIHsKICAgIGJpYyA8LSAtMipsK2xvZyhOKSpucAogICAgbmFtZXMoYmljKSA8LSJCSUMiCiAgICBpZiAoayE9MikgewogICAgICB3YXJuaW5nKCJwYXJhbWV0ZXIgPGs+IG5vdCB1c2VkIGZvciBCSUMiKQogICAgfQogICAgcmV0dXJuKGJpYykKICB9Cn0KCmBgYAoKIyMgRGF0YQoKYGBge3IgZGF0YX0KCiMgTG9hZCBkaXN0cmljdHMgYW5kIGF0dHJpYnV0ZXMKZzIgPC0gc2hhcGVmaWxlKCIuL21hcHMvc3Z5TWFwc18yMDE2LjA2LjIyX3NhcmEuc2hwIikKZHQyIDwtIHJlYWQuZHRhKCIuL3RtcC9TU0Fwb3ZlcnR5X0Rpc3RfZm9yR1dSLjEyLmR0YSIpCiNnMiA8LSBzaGFwZWZpbGUoIi4vb3V0L3IxNi4wNS9zdnlNYXBzXzIwMTYuMDYuMjJfc2FyYS5zaHAiKQojZHQyIDwtIHJlYWQuZHRhKCIuL3RlbXAvMjAxNi4wNS9TU0Fwb3ZlcnR5X0Rpc3RfZm9yR1dSLjEyLmR0YSIpCgojIEtlZXAgU1RBVEEgbGFiZWxzIGZvciByZS11c2UKZHQyLmxibCA8LSBkYXRhLnRhYmxlKHZhckNvZGU9bmFtZXMoZHQyKSwgdmFyTGFiZWw9YXR0cihkdDIsICJ2YXIubGFiZWxzIikpCnNldGtleShkdDIubGJsLCB2YXJDb2RlKQpkdDIubGJsW2lzLm5hKHZhckxhYmVsKSwgdmFyTGFiZWwgOj0gdmFyQ29kZV0KZHQyIDwtIGRhdGEudGFibGUoZHQyKQoKYGBgCgpgYGB7cn0KCiMgVmVyaWZ5IHRoYXQgd2UgaGF2ZSBhbGwgZXhwbGFuYXRvcnkgdmFyaWFibGVzIGFuZCBzaG9ja3MgYWxyZWFkeSBjb25zdHJ1Y3RlZCwgZS5nLgpyYmluZCgKICBkdDIubGJsW3ZhckNvZGUgJWxpa2UlICJuZHZpIl0sCiAgZHQyLmxibFt2YXJDb2RlICVsaWtlJSAic2hvY2siXSwKICBkdDIubGJsW3ZhckNvZGUgJWxpa2UlICJBRVoiXSwKICBkdDIubGJsW3ZhckNvZGUgJWxpa2UlICJsYW5kIl0pCgpgYGAKCgoKYGBge3IgbWFwfQoKIyBNYWtlIHVuaXF1ZSBzaGFwZSBJRHMgZXhwbGljaXQKZzIkcm4gPC0gcm93Lm5hbWVzKGcyKQpnMi5kdCA8LSBkYXRhLnRhYmxlKGcyQGRhdGEpCgojIFJlY29kZSBFdGhpb3BpYSB3b3JlZGFzCmR0MltzdnlDb2RlPT0iZXRoMjAxMCIsIHN2eUwyQ2QgOj0gc3Z5TDFDZCAqIDEwMDAwICsgc3Z5TDJDZF0KCiMgTWVyZ2Ugc2hhcGVzIGFuZCBhdHRyaWJ1dGVzCnNldGtleShnMi5kdCwgSVNPMywgc3Z5Q29kZSwgc3Z5TDFDZCwgc3Z5TDJDZCkKc2V0a2V5KGR0MiwgSVNPMywgc3Z5Q29kZSwgc3Z5TDFDZCwgc3Z5TDJDZCkKCiMgTG9vayBmb3IgcG9zc2libGUgZHVwbGljYXRlcwpnMi5kdFtkdXBsaWNhdGVkKGcyLmR0KSwgLihJU08zLCBzdnlDb2RlLCBzdnlMMUNkLCBzdnlMMkNkKV0KZHQyW2R1cGxpY2F0ZWQoZHQyKSwgLihJU08zLCBzdnlDb2RlLCBzdnlMMUNkLCBzdnlMMkNkKV0KCiMgRHJvcCBkdXBsaWNhdGVkIHZhcnMgZnJvbSBTYXJhJ3MgZmlsZSBiZWZvcmUgbWVyZ2luZwpkdDJbLCBgOj1gKAogIHJuPU5VTEwsIHN2eUwxTm09TlVMTCwgc3Z5TDJObT1OVUxMLCBwcnR0eU5tPU5VTEwsIGFyZWFrbT1OVUxMLCBYPU5VTEwsIFk9TlVMTCldCgojIEFueSB1bm1hdGNoZWQgb2JzPwpkdDJbIWcyLmR0LCAuTiwgYnk9c3Z5Q29kZV0KCmBgYAoKYGBge3J9CgojIFNlZW1zIG9rYXksIHNvIGxldCdzIG1lcmdlCmcyLmR0IDwtIGR0MltnMi5kdF0KCiMgUmUtYXR0YWNoIFNhcmEncyBhdHRyaWJ1dGVzIHRvIHNoYXBlcwpnMiA8LSBTcGF0aWFsUG9seWdvbnNEYXRhRnJhbWUoZzIsIGRhdGEuZnJhbWUoZzIuZHQpLCBtYXRjaC5JRD0icm4iKQoKYGBgCgpgYGB7ciB2aWV3dmFyc30KCiMgVmlzdWFsbHkgY2hlY2sgYSBmZXcgdmFycwpkYXRhKFdvcmxkKQpwMCA8LSB0bV9zaGFwZShXb3JsZCkgKyB0bV9maWxsKCJncmV5OTAiKSArIHRtX3NoYXBlKGcyLCBpcy5tYXN0ZXI9VCkKcDEgPC0gdG1fc2hhcGUoV29ybGQpICsgdG1fYm9yZGVycygiZ3JleTUwIiwgbHdkPS4yKQoKcDAgKyB0bV9maWxsKCJuZHZpX2F2ZSIsIHRpdGxlPSJORFZJXG5Mb25nLXRlcm0gbWVhbiIsCiAgc3R5bGU9InByZXR0eSIsIG49OSwgcGFsZXR0ZT1yZXYocGFsLm5yd2MpLCBhdXRvPUYpICsgcDEKcDAgKyB0bV9maWxsKCJwY2V4cF9wcHBfbSIsIHRpdGxlPSJQZXIgQ2FwaXRhXG5FeHBlbmRpdHVyZVxuKFBQUCBVU0QvbW9udGgpIiwKICBzdHlsZT0iamVua3MiLCBuPTksIGF1dG89RikgKyBwMQpwMCArIHRtX2ZpbGwoImZvb2RleHBfcHBwX20iLCB0aXRsZT0iRm9vZCBFeHBlbmRpdHVyZVxuKFBQUCBVU0QvbW9udGgpIiwKICBzdHlsZT0iamVua3MiLCBuPTksIGF1dG89RikgKyBwMQpwMCArIHRtX2ZpbGwoInByZV9sdCIsIHRpdGxlPSJQcmVjaXBpdGF0aW9uXG5sb25nLXRlcm0gbWVhblxuKG1tKSIsCiAgc3R5bGU9ImplbmtzIiwgbj05LCBwYWxldHRlPXBhbC5wcmUsIGF1dG89RikgKyBwMQpwMCArIHRtX2ZpbGwoInNwZWlfbHQiLCB0aXRsZT0iU1BFSVxubG9uZy10ZXJtIG1lYW4iLAogIHN0eWxlPSJqZW5rcyIsIG49OSwgcGFsZXR0ZT0iUmRZbEduIiwgYXV0bz1GKSArIHAxCnAwICsgdG1fZmlsbCgibmlnaGxpZ2h0X3llYXJseSIsIHRpdGxlPSJOaWdodCBMaWdodFxuUmVmbGVjdGFuY2UiLAogIHN0eWxlPSJrbWVhbnMiLCBuPTksIHBhbGV0dGU9cGFsLmVhcnRoWzE6MjMwXSwgYXV0bz1GKSArIHAxCnAwICsgdG1fZmlsbCgidHQxMF8yMGsiLCB0aXRsZT0iVHJhdmVsIFRpbWVcbnRvIG5lYXJlc3QgMjBLIG1hcmtldFxuKGhvdXJzKSIsCiAgc3R5bGU9ImZpeGVkIiwgYnJlYWtzPWMoLjUsMSwyLDQsNiw4LDEwLDEyLDI0KSwgcGFsZXR0ZT0iLVlsT3JSZCIsIGF1dG89RikgKyBwMQoKYGBgCgoKIyMgTW9kZWwgU3BlY2lmaWNhdGlvbnMKCkZvbGxvd2luZyBTYXJhJ3Mgc2V0dXAsIDIgb3V0Y29tZSB2YXJpYWJsZXMgYFlgLCA5IGFsdGVybmF0ZSBncm91cHMgb2YgYmlvcGh5c2ljYWwgYW5kIGNsaW1hdGljIHNob2NrIHZhcmlhYmxlcyBhbmQgNCBjcm9zcy1jb21iaW5hdGlvbnMgb2YgcHJlZGljdG9ycyBhcmUgY29uc2lkZXJlZCwgZm9yIGEgdG90YWwgb2YgKio3MiBtb2RlbHMqKi4gU2FyYSBjaG9zZSBhIFNBQyBmb3JtIGluIFNUQVRBIChlcXVpdmFsZW50IHRvIGBzYWNzYXJsbSgpYCBpbiBSKS4KCgpgYGB7ciBtb2RlbHMsIHJlc3VsdHM9ImFzaXMifQoKIyBEZWZpbmUgb3V0Y29tZSB2YXJzClkgPC0gYygicGNleHBfcHBwX20iLCAiZm9vZGV4cF9wcHBfbSIpCgojIERlZmluZSBncm91cHMgb2YgZXhwbGFuYXRvcnkgdmFycwpYIDwtIGxpc3QoCiAgc29jaW8gICAgPSBjKCJoaF9mZW1hbGUiLCAiYWdlaGVhZCIsICJtYXJyaWVkaGVhZCIsICJtZWFuZWR1YyIsICJkZXByYXRpbzIiLCAiY2hpbGRyZW4iKSwKICBhZ3JpICAgICA9IGMoIkxHUF9BVkciLCAiRUxFVkFUSU9OIiwgImNyb3BsYW5kX3BjYXAiLCAiVExVX3BjYXAiKSwKICBhY2Nlc3MgICA9IGMoIm5pZ2hsaWdodF95ZWFybHkiLCAidHQxMF8yMGsiLCAiUEQxMl9UT1QiKSwKICB3ZWF0aGVyMSA9IGMoInNwZWlfbHQiLCAiTDFfc3BlaWRpZiIsICJMMV9zcGVpZGlmMiIpLAogIHdlYXRoZXIyID0gYygic3BlaV9sdCIsICJMY3VtX3NwZWlkaWYiLCAiTGN1bV9zcGVpZGlmMiIpLCAKICB3ZWF0aGVyMyA9IGMoInNwZWlfbHQiLCAiTDFfc3BlaWhpc2hvY2siLCAiTDFfc3BlaWxvc2hvY2siKSwKICB3ZWF0aGVyNCA9IGMoInByZV9sdCIsICJMMV9wcmVkaWYiLCAiTDFfcHJlZGlmMiIsICJ0ZW1wX2x0IiwgIkwxX3RlbXBkaWYiLCAiTDFfdGVtcGRpZjIiKSwKICB3ZWF0aGVyNSA9IGMoInByZV9sdCIsICJMY3VtX3ByZWRpZiIsICJMY3VtX3ByZWRpZjIiLCAidGVtcF9sdCIsICJMY3VtX3RlbXBkaWYiLCAiTGN1bV90ZW1wZGlmMiIpLAogIHdlYXRoZXI2ID0gYygicHJlX2x0IiwgInRlbXBfbHQiLCAiTDFfcHJlaGlzaG9jayIsICJMMV9wcmVsb3Nob2NrIiwgIkwxX3RlbXBoaXNob2NrIiksCiAgd2VhdGhlcjcgPSBjKCJuZHZpX2x0IiwgIkwxX25kdmlkaWYiLCAiTDFfbmR2aWRpZjIiKSwgCiAgd2VhdGhlcjggPSBjKCJuZHZpX2x0IiwgIkxjdW1fbmR2aWRpZiIsICJMY3VtX25kdmlkaWYyIiksIAogIHdlYXRoZXI5ID0gYygibmR2aV9sdCIsICJMMV9uZHZpaGlzaG9jayIsICJMMV9uZHZpbG9zaG9jayIpLAogIG90aGVyICAgID0gYygiTWFsYXJpYV95ZWFybHkiKSwKICBBRVogICAgICA9IGMoImFyaWRfd2FybSIsICJodW1pZF9jb29sIiwgImh1bWlkX3dhcm0iKSwKICBjb3VudHJ5ICA9IGMoIklTTzMiKSwKICB5ciAgICAgICA9IGMoInllYXIiKSkKCiMgQ3JlYXRlIGEgbGlzdCBvZiA3MiBtb2RlbCBmb3JtdWxhcwptb2RlbHMgPC0gbGlzdCgpCmZvciAoaSBpbiBZKSBmb3IgKGogaW4gMTo5KSBmb3IgKGsgaW4gTEVUVEVSU1sxOjRdKSB7CiAgbW9kZWxzW1twYXN0ZTAoaSwgIiB+IGNsaW0tIiwgaywgaildXSA8LSBhcy5mb3JtdWxhKHN3aXRjaChrLAogICAgQSA9IHBhc3RlMChpLCAifiIsIHBhc3RlKGMoWFtbcGFzdGUwKCJ3ZWF0aGVyIiwgaildXSksIGNvbGxhcHNlPSIrIikpLAogICAgQiA9IHBhc3RlMChpLCAifiIsIHBhc3RlKGMoWFtbcGFzdGUwKCJ3ZWF0aGVyIiwgaildXSwgWCRzb2NpbyksIGNvbGxhcHNlPSIrIikpLAogICAgQyA9IHBhc3RlMChpLCAifiIsIHBhc3RlKGMoWFtbcGFzdGUwKCJ3ZWF0aGVyIiwgaildXSwgWCRzb2NpbywgWCRhY2Nlc3MsIFgkYWdyaSksIGNvbGxhcHNlPSIrIikpLAogICAgRCA9IHBhc3RlMChpLCAifiIsIHBhc3RlKGMoWFtbcGFzdGUwKCJ3ZWF0aGVyIiwgaildXSwgWCRzb2NpbywgWCRhY2Nlc3MsIFgkYWdyaSwgWCRvdGhlciksIGNvbGxhcHNlPSIrIikpCiAgKSkKfQoKIyBQcmludCBmaXJzdC9sYXN0IDIgZm9ybXVsYXMKcGFuZGVyKG1vZGVsc1tjKDE6MiwgNzE6NzIpXSkKCmBgYAoKYGBge3J9CgojIFZlcmlmeSB0aGF0IGFsbCB2YXJzIGluY2x1ZGVkIGluIHRoZXNlIG1vZGVscyBleGlzdCBpbiB0aGUgZGF0YXNldAp2YXJzLm1pc3NpbmcgPC0gc2V0ZGlmZih1bmxpc3QoWCxZKSwgZHQyLmxibCR2YXJDb2RlKQp2YXJzLm1pc3NpbmcKCmBgYAoKKipOaW5lIHZhcnMqKiBhcmUgbWlzc2luZyBzbyB3ZSBuZWVkIHRvIHJlY29uc3RydWN0IHRoZW0gb3IgZWxzZSBkcm9wIHRoZSBjb3JyZXNwb25kaW5nIG1vZGVscyBmcm9tIHRoZSBhbmFseXNpcyAoZG9uZSBmb3Igbm93KS4KCmBgYHtyfQoKIyBEcm9wIG1vZGVscyB3aXRoIG1pc3NpbmcgdmFycwptb2RlbHMuZHJvcCA8LSBzYXBwbHkobW9kZWxzLCBzdHJfbG9jYXRlLCB2YXJzLm1pc3NpbmcpCm1vZGVscy5kcm9wIDwtIGNvbFN1bXMobW9kZWxzLmRyb3AsIG5hLnJtPVQpCm1vZGVscyA8LSBtb2RlbHNbbW9kZWxzLmRyb3A9PTBdCgpgYGAKClRoaXMgbGVhdmVzICoqYHIgbGVuZ3RoKG1vZGVscylgIG1vZGVscyoqIHRvIGVzdGltYXRlLgoKCiMjIyBJbXB1dGF0aW9ucwoKSW4gUiBzcGF0aWFsIHJlZ3Jlc3Npb24gY29tbWFuZHMgcmVxdWlyZSBhbGwgcHJlZGljdG9yIHZhcmlhYmxlcyB0byBiZSBub24tbWlzc2luZy4gSGVyZSB3ZSB1c2Ugc2ltcGxlIHJlZ2lvbiBtZWRpYW4gdmFsdWUgKG9yIGNvdW50cnkgaW4gY2FzZSByZWdpb25hbCBtZWRpYW4gaXMgbWlzc2luZykuIE90aGVyIGludGVycG9sYXRpb24gbWV0aG9kcyBmb3IgbWlzc2luZyB2YWx1ZXMgY291bGQgYmUgdXNlZCAoZS5nLiB0cmlhbmd1bGF0aW9uIG9yIGtyaWdpbmcgdXNpbmcgZGlzdHJpY3QgY2VudHJvaWRzKS4gU2luY2Ugd2UgYXJlIGludGVycG9sYXRpbmcgYSBiaW9waHlzaWNhbCBwaGVub21lbm9uIChTUEVJKSB0aGVzZSB0ZWNobmlxdWVzIG1pZ2h0IGluZGVlZCBiZSBtb3JlIGFwcHJvcHJpYXRlLiAKCmBgYHtyIGltcHV0ZX0KClggPC0gdW5pcXVlKHVubGlzdChzYXBwbHkobW9kZWxzLCAnW1snLCAiWCIpKSkKWAoKZzIuZHQgPC0gZGF0YS50YWJsZShnMkBkYXRhKQoKIyBJbXB1dGUgbWlzc2luZyBYIHZhbHVlcyB3aXRoIHJlZ2lvbmFsIG1lZGlhbgpnMi5kdFssIHNwZWlfbHRfaW1wIDo9IG1lZGlhbihzcGVpX2x0LCBuYS5ybT1UKSwgYnk9LihzdnlDb2RlLCBzdnlMMUNkKV0KZzIuZHRbaXMubmEoc3BlaV9sdCksIHNwZWlfbHQgOj0gc3BlaV9sdF9pbXBdCmcyLmR0W2lzLm5hKHNwZWlfbHQpLCAuTiwgYnk9c3Z5Q29kZV0KCiMgSW1wdXRlIHN0aWxsIG1pc3NpbmcgWCB2YWx1ZXMgd2l0aCBuYXRpb25hbCBtZWRpYW4KZzIuZHRbLCBzcGVpX2x0X2ltcCA6PSBtZWRpYW4oc3BlaV9sdCwgbmEucm09VCksIGJ5PXN2eUNvZGVdCmcyLmR0W2lzLm5hKHNwZWlfbHQpLCBzcGVpX2x0IDo9IHNwZWlfbHRfaW1wXQoKIyBWZXJpZnkKZzIuZHRbaXMubmEoc3BlaV9sdCksIC5OLCBieT1zdnlDb2RlXQoKIyBUYWJ1bGF0ZQoKCiMgUmUtYXR0YWNoIGltcHV0ZWQgYXR0cmlidXRlcyB0byBzaGFwZXMKZzIgPC0gU3BhdGlhbFBvbHlnb25zRGF0YUZyYW1lKGcyLCBkYXRhLmZyYW1lKGcyLmR0KSwgbWF0Y2guSUQ9InJuIikKCgpgYGAKCgojIyBTcGF0aWFsIEVmZmVjdHMKClRoaXMgc2VjdGlvbiBpcyB0byB0ZXN0IGZvciB0aGUgcHJlc2VuY2Ugb2Ygc3BhdGlhbCBhdXRvY29ycmVsYXRpb24gaW4gb25lIG9yIG1vcmUgb2YgdGhlIHByZWRpY3RvcnMsIGFuZCB0byBjaG9vc2UgYSByZWdyZXNzaW9uIGFwcHJvYWNoIHN1Y2ggdGhhdCBtb2RlbCByZXNpZHVhbHMgYXJlIG5vdCBzcGF0aWFsbHkgYXV0b2NvcnJlbGF0ZWQgKHRodXMgZW5zdXJpbmcgYSBiZXR0ZXIgZml0KS4KCgojIyMgTW9yYW4ncyAqSSogU3RhdGlzdGljCgoKYGBge3IgbW9yYW59Cgptb3Jhbi5wbG90KGcyLm5iQGRhdGFbLCBZXSwgdywgemVyby5wb2xpY3k9VCwKICB4bGltPWMoMCwgMjAwKSwgeWxpbT1jKDAsIDIwMCksCiAgeGxhYj1kdDIubGJsW1ksIHZhckxhYmVsXSwgCiAgeWxhYj1wYXN0ZSgiU3BhdGlhbGx5IExhZ2dlZCIsIFkpKQoKI3Bsb3QoCiMgIHZhcmlvZ3JhbShhcy5mb3JtdWxhKHBhc3RlKFksICJ+MSIpKSwgCiMgICAgbG9jYXRpb25zPWNvb3JkaW5hdGVzKGcyLm5iKSwgZGF0YT1nMi5uYiwgY2xvdWQ9RiksIAojICB0eXBlPSJiIiwgcGNoPTE2LCBtYWluPXBhc3RlKCJWYXJpb2dyYW0gb2YiLCBkdDIubGJsW1ksIHZhckxhYmVsXSkpCgptb3Jhbi5tYyhnMi5uYkBkYXRhWywgWV0sIHcsIHplcm8ucG9saWN5PVQsIG5zaW09OTk5KQoKbW9yYW4ucGxvdChnMi5uYkBkYXRhWywgWFsxXV0sIHcsIHplcm8ucG9saWN5PVQsCiAgeGxhYj1kdDIubGJsW1hbMV0sIHZhckxhYmVsXSwgCiAgeWxhYj1wYXN0ZSgiU3BhdGlhbGx5IExhZ2dlZCIsIFhbMV0pKQoKbW9yYW4ubWMoZzIubmJAZGF0YVssIFhbMV1dLCB3LCB6ZXJvLnBvbGljeT1ULCBuc2ltPTk5OSkKCgpgYGAKCgoKIyMgU3BhdGlhbCBXZWlnaHRzCgpOZWVkIHRvIGNob29zZSBiZXR3ZWVuIFFVRUVOIG9yIFJPT0sgY29udGlndWl0eSBvciBlbHNlIHdlIGNhbiBleHBlcmltZW50IHdpdGggay1uZWFyZXN0IHBvaW50cy9zaGFwZXMgYXMgbmVpZ2hib3JzIHVzaW5nIGBrbm4ybmIoKWAgaW5zdGVhZCBvZiBgcG9seTJuYigpYC4gQ2FuIGFsc28gYXNzaWduIG5laWdoYm9ycyBiYXNlZCBvbiBhIGdpdmVuIGRpc3RhbmNlIHRocmVzaG9sZCB1c2luZyBgZG5lYXJuZWlnaCgpYC4KCk5vdGUgdGhhdCBjb250aWd1aXR5IHJlcXVpcmVzIHZhbGlkIHRvcG9sb2d5ICh3aGljaCBpcyBzdXJlbHkgbm90IHRoZSBjYXNlIGluIHRoaXMgc2hhcGVmaWxlKS4gQXJndW1lbnQgYHNuYXBgIG1heSBiZSB1c2VkIHRvIGNvcnJlY3QgZm9yIHNsaXZlcnMuIEVsc2Ugb25lIGNhbiB1c2UgYGVkaXQobm4pYCB0byBtYWtlIG1hbnVhbCBjb3JyZWN0aW9ucyB0byB0aGUgbWF0cml4LiBBbm90aGVyIGFwcHJvYWNoIHRvIGFkZHJlc3Mgbm9uLWNvbnRpZ3VvdXMgc3BsYXRpYWwgZmVhdHVyZXMgaXMgdG8gdXNlIHRoZSBmZWF0dXJlIGNlbnRyb2lkLCBvciBhbnkgd2VpZ2h0ZWQgY2VudHJvaWQgKGUuZy4gcG9wdWxhdGlvbiB3ZWlnaHRlZCBjZW50cm9pZCBvZiB0aGUgYWRtaW4gdW5pdCkuCgpUaGVyZSdzIGFsc28gdGhlIGlzc3VlIG9mIGNob29zaW5nIGEgbWV0aG9kIGZvciB0aGUgc3BhdGlhbCB3ZWlnaHRzIChyb3ctc3RhbmRhcmRpemVkLCBiaW5hcnkpLiBUeXBpY2FsbHkgUm93IHN0YW5kYXJkaXphdGlvbiBpcyB1c2VkIHRvIGNyZWF0ZSBwcm9wb3J0aW9uYWwgd2VpZ2h0cyBpbiBjYXNlcyB3aGVyZSBmZWF0dXJlcyBoYXZlIGFuIHVuZXF1YWwgbnVtYmVyIG9mIG5laWdoYm9ycy4gVXNlIEJpbmFyeSB3aGVuIHlvdSB3YW50IGNvbXBhcmFibGUgc3BhdGlhbCBwYXJhbWV0ZXJzIGFjcm9zcyBkaWZmZXJlbnQgZGF0YSBzZXRzIHdpdGggZGlmZmVyZW50IGNvbm5lY3Rpdml0eSBzdHJ1Y3R1cmVzLgogCgpgYGB7ciB3ZWlnaHRzfQoKIyBHZW5lcmF0ZSBzcGF0aWFsIG5laWdoYm91ciBsaXN0IGZvciBTU0EKbmIyIDwtIHBvbHkybmIoZzIubmIsIHJvdy5uYW1lcz1wYXN0ZShnMi5uYiRJU08zLCBnMi5uYiRybiwgc2VwPSIuIikpCnN1bW1hcnkobmIyKQoKIyBWZXJpZnkgdGhlIDUgZGlzY29udGlndW91cyBkaXN0cmljdHMKYmFkIDwtIGMoIjQ3NyIsICIxMDUyIiwgIjEzNDMiLCAiMTc0NiIsICIxOTQ3IikKZzIubmIuZHRbcm4gJWluJSBiYWQsIC4ocm4sIElTTzMsIHN2eUwxTm0sIHN2eUwyTm0sIHBjZXhwX3BwcF9tKV0KCmZvcihpIGluIGMoIkFHTyIsICJFVEgiLCAiU0VOIikpIHsKICBiYiA8LSBiYm94KGcyLm5iW2cyLm5iJElTTzM9PWkgJiBnMi5uYiRybiAlaW4lIGJhZCxdKQogIGJiIDwtIGJiICsgYygtLjUsIC0uNSwgLjUsIC41KQogIHByaW50KAogICAgdG1fc2hhcGUoZzIsIGJib3g9YmIpICsgdG1fYm9yZGVycygpICsKICAgICAgdG1fc2hhcGUoZzIubmIpICsgdG1fZmlsbCgicGNleHBfcHBwX20iKSArIAogICAgICB0bV9zaGFwZShnMi5uYltnMi5uYiRJU08zPT1pICYgZzIubmIkcm4gJWluJSBiYWQsXSkgKwogICAgICB0bV9ib3JkZXJzKGNvbD0icmVkIikgKyB0bV90ZXh0KCJzdnlMMk5tIiwgY29sPSJyZWQiKSArCiAgICAgIHRtX2xheW91dChsZWdlbmQub3V0c2lkZT1UKQogICl9CgpgYGAKClRoZXkncmUgbm90IGlzbGFuZHMsIGJ1dCBzdXJyb3VuZGluZyBkaXN0cmljdHMgaGF2ZSBubyBkYXRhLiBOZWVkIHRvIGNoZWNrIGEKbGl0dGxlIG1vcmUgd2hhdCdzIGdvaW5nIG9uLCBmaXggaW4gUUdJUyBpZiBuZWVkZWQuIEFsc28gcmVmZXIgdG8gQml2YW5kOgoKPiBJIGRpZCBsb29rIGF0IHRoaXMgMTUgeWVhcnMgYWdvIHdpdGggQm9yaXMgUG9ydG5vdiwgaW4gdGhlIGNvbnRleHQgb2YgRVNEQToKPiAgICAgCj4gQGluY29sbGVjdGlvbntiaXZhbmQrcG9ydG5vdjowNCwgICAKPiBhdXRob3IgPSB7Ui4gUy4gQml2YW5kIGFuZCBCLiBBLiBQb3J0bm92fSwKPiBlZGl0b3IgPSB7TC4gQW5zZWxpbiBhbmQgUi4gSi4gRy4gTS4gRmxvcmF4IGFuZCBTLiBKLiBSZXl9LAo+IHRpdGxlID0ge0V4cGxvcmluZyBzcGF0aWFsIGRhdGEgYW5hbHlzaXMgdGVjaG5pcXVlcyB1c2luZyB7XFJSfTogdGhlIGNhc2Ugb2Ygb2JzZXJ2YXRpb25zIHdpdGggbm8gbmVpZ2hib3Vyc30sCj4gYm9va3RpdGxlID0ge0FkdmFuY2VzIGluIFNwYXRpYWwgRWNvbm9tZXRyaWNzOiBNZXRob2RvbG9neSwgVG9vbHMsIEFwcGxpY2F0aW9uc30sICAgCj4geWVhciA9IHsyMDA0fSwgICAKPiBwdWJsaXNoZXIgPSB7U3ByaW5nZXJ9LCAgIAo+IGFkZHJlc3MgPSB7QmVybGlufSwgICAKPiBwYWdlcyA9IHsxMjEtLTE0Mn0KPiB9Cj4gCj4gVGhlcmUgYXJlIG9kZGl0aWVzIGluIHRoZSBNb3JhbiBzY2F0dGVycGxvdCwgYW5kIGFsc28gaW4gbWFwcGluZyB0aGUKPiBncmFwaC1iYXNlZCBuZWlnaGJvdXIgcmVwcmVzZW50YXRpb24gaW50byBtYXRyaXggZm9ybSwgc2F5IHdpdGggdGhlIHNwYXRpYWwKPiBsYWcgb2YgYSBuby1uZWlnaGJvdXIgb2JzZXJ2YXRpb24ncyB2YWx1ZSBiZWluZyB6ZXJvIChmb3IgemVyby5wb2xpY3k9VFJVRSkuCj4gVGhhdCBwYXBlciB3YXMgdGhlIGJhc2lzIGZvciB0aGUgemVyby5wb2xpY3k9IGZyYW1ld29yay4gVGhlcmUgYXJlIG90aGVyCj4gY29uc2VxdWVuY2VzIHRoYXQgeW91J3ZlIGZvdW5kIHdpdGggcmVzcGVjdCB0byB0aGUgbnVtYmVyIG9mIHN1YmdyYXBocywgd2hpY2gKPiBtYXkgb3IgbWF5IG5vdCBicmVhayBmb3JtYWwgYXNzdW1wdGlvbnMgb2YgYW5hbHlzaXMgbWV0aG9kcy4gSW4gYWRkaXRpb24sIHdlCj4gZG9uJ3Qga25vdyBob3cgZmFyIHRoZSBicm9rZW4gYXNzdW1wdGlvbnMgYWN0dWFsbHkgbWF0dGVyLiBUaGlzIHdvdWxkIHByb2JhYmx5Cj4gYmUgYSBnb29kIGNhbmRpZGF0ZSBmb3IgcHJvcGVyIHN0dWR5IGluY2x1ZGluZyBzaW11bGF0aW9uLgoKCiMjIyBQbG90IENvbnRpZ3VpdGllcwoKCmBgYHtyIGNvbnRpZ3VpdHl9CgojIFBsb3QgY29udGlndWl0aWVzIGluIGEgZmV3IGNvdW50cmllcwpmb3IgKGkgaW4gYygiR0hBIiwgIkVUSCIsICJBR08iLCAiU0VOIikpIHsKICAKICB0bXAgPC0gZzIubmJbZzIubmIkSVNPMz09aSxdCiAgY29vcmRzLnRtcCA8LSBjb29yZGluYXRlcyh0bXApCiAgbmIyLnRtcCA8LSBwb2x5Mm5iKHRtcCkKICBuYjIudG1wCiAgCiAgcGxvdChnMltnMiRJU08zPT1pLF0sIGNvbD0icmVkIiwgbHdkPTAuMSkKICBwbG90KHRtcCwgY29sPSJncmV5OTAiLCBsd2Q9MC4xLCBhZGQ9VCkKICBwbG90KG5iMi50bXAsIGNvb3Jkcy50bXAsIGNvbD0iYmx1ZSIsIGFkZD1UKQogIHRpdGxlKG1haW49cGFzdGUoIkNvbnRpZ3VpdHkgLSIsIGkpLCBmb250Lm1haW49MSkKfQoKCmBgYAoKCmBgYHtyfQoKIyBTYXZlIGRpc3RhbmNlIG1hdHJpeCBmb3IgU1NBIChXPXJvdyBzdGFuZGFyZGl6ZWQpIHRvIFNUQVRBIGZvciByZS11c2UKdzIgPC0gbmIybWF0KG5iMiwgc3R5bGU9IlciLCB6ZXJvLnBvbGljeT1UKQp3MiA8LSBhcy5kYXRhLmZyYW1lKHcyKQphdHRyKHcyLCAidmFyLmxhYmVscyIpIDwtIHBhc3RlKGcyJHN2eUNvZGUsIGcyJHN2eUwxQ2QsIGcyJHN2eUwyQ2QsIHNlcD0iLiIpCndyaXRlLmR0YSh3MiwgIi4uL291dC9yMTYuMDUvcG92ZXJ0eV9jb250aW5ndWl0eS5kdGEiLCB2ZXJzaW9uPTEyKQoKYGBgCgoKCmBgYHtyIHNwYXR3ZWlnaHRzfQoKIyBDaGVjayBwb3B1bGF0aW9uIHdlaWdodHMKc3VtbWFyeShnMi5uYiRwb3ApCgojIFNwYXRpYWwgd2VpZ2h0cyBmb3IgU1NBIChjaGVjayBkb2NvIGZvciBob3cgdG8gaW5jbHVkZSBwb3Agd2VpZ2h0cykKIyBOb3RlIHRoYXQgSWYgemVyby5wb2xpY3kgaXMgc2V0IHRvIFRSVUUsIHdlaWdodHMgdmVjdG9ycyBvZiB6ZXJvIGxlbmd0aCBhcmUgaW5zZXJ0ZWQKIyBmb3IgcmVnaW9ucyB3aXRob3V0IG5laWdoYm91ciBpbiB0aGUgbmVpZ2hib3VycyBsaXN0LiAKdyA8LSBuYjJsaXN0dyhuYjIsIHplcm8ucG9saWN5PVQpCgpgYGAKCgoKIyMgU3BhdGlhbCBSZWdyZXNzaW9ucyAoT0xTLCBMQUcsIFNBQykKCkJhdGNoIHJ1biBhbGwgbW9kZWxzIHVzaW5nIE9MUywgTEFHLCBhbmQgU0FDIHJlZ3Jlc3Npb25zIGFuZCBzYXZlIHJlc3VsdHMgdG8gZHJhdyBjb21wYXJpc29ucy4KCmBgYHtyIHNwYXRyZWcsIGNhY2hlPVR9CgojIERyb3AgYWRtaW4gdW5pdHMgd2l0aCBtaXNzaW5nIG91dGNvbWUgdmFsdWVzIChub3QgaW5jbHVkZWQgaW4gbW9kZWwpCmcyLm5iIDwtIGcyWyFpcy5uYShnMltbeF1dKSxdCgojIENvbXBhcmUgbW9kZWxzCmZtIDwtIGFzLmZvcm11bGEocGFzdGUoWSwgIn4iLCBwYXN0ZShYLCBjb2xsYXBzZT0iKyIpKSkKZm0KCiMgTW9kZWwgMTogc2ltcGxlIE9MUwptIDwtIGxtKGZtLCBkYXRhPWcyLm5iLmR0LCB3ZWlnaHRzPTEvcG9wKQpzdW1tYXJ5KG0pCgojIEV4YW1pbmUgc3BhdGlhbCBhdXRvY29ycmVsYXRpb24gYW1vbmcgdGhlIHJlc2lkdWFscwpsbS5tb3JhbnRlc3QobSwgbGlzdHc9dywgemVyby5wb2xpY3k9VCkKCiMgTW9kZWwgMjogTEFHIG1vZGVsCm1sYWdzYXIgPC0gbGFnc2FybG0oZm0sIHcsIHplcm8ucG9saWN5PVQsIGRhdGE9ZzIubmIuZHQpCnN1bW1hcnkobWxhZ3NhcikKCiMgTW9kZWwgMzogU0FDIG1vZGVsCm1zYWNzYXIgPC0gc2Fjc2FybG0oZm0sIHcsIHplcm8ucG9saWN5PVQsIGRhdGE9ZzIubmIuZHQpCnN1bW1hcnkobXNhY3NhcikKCiMgQWxzbyBzaG93IGltcGFjdCBlZmZlY3RzIG9mIHNwYXRpYWwgbW9kZWxzCiMgVG8gdW5kZXJzdGFuZCB0aGUgZGlyZWN0IChsb2NhbCksIGluZGlyZWN0KHNwaWxsLW92ZXIpLCBhbmQgdG90YWwgZWZmZWN0IG9mIGEgdW5pdCAKIyBjaGFuZ2UgaW4gZWFjaCBvZiB0aGUgcHJlZGljdG9yIHZhcmlhYmxlcwpXIDwtIGFzKHcsICJDc3BhcnNlTWF0cml4IikKdHJNYXRjIDwtIHRyVyhXLCB0eXBlPSJtdWx0IikKCmltcGFjdHMobWxhZ3NhciwgdHI9dHJNYXRjLCBSPTIwMDApCmltcGFjdHMobXNhY3NhciwgdHI9dHJNYXRjLCBSPTIwMDApCgpzdW1tYXJ5KGltcGFjdHMobWxhZ3NhciwgdHI9dHJNYXRjLCBSPTIwMDApLCB6c3RhdHM9VCwgc2hvcnQ9VCkKc3VtbWFyeShpbXBhY3RzKG1zYWNzYXIsIHRyPXRyTWF0YywgUj0yMDAwKSwgenN0YXRzPVQsIHNob3J0PVQpCgoKYGBgCgpUaGUgb3V0cHV0IGZyb20gYGltcGFjdHMoKWAgaW4gdGhlIExBRyBtb2RlbCBzYXlzIHRoYXQgYSAxIHBvaW50IGluY3JlYXNlIGluCmxvbmctdGVybSBTUEVJIGxlYWRzIHRvIGFuIGluY3JlYXNlIGluIGV4cGVuZGl0dXJlIG9mIFBQUCAkMTEvbW9udGguIEEgMSBzZAppbmNyZWFzZSBpbiBkcm91Z2h0IGxlYWRzIHRvIGEgcmVkdWN0aW9uIGluIGV4cGVuZGl0dXJlIG9mIFBQUCAkMjAvbW9udGguCgoKYGBge3J9CgojIExhYmVsIDIwIGRpc3RyaWN0cyBhdCByYW5kb20Kcm5kIDwtIHNhbXBsZSgxOm5yb3coZzIubmIuZHQpLCAyMCkKCiMgUGxvdCBPTFMKcGxvdChtJG1vZGVsJHBjZXhwX3BwcF9tLCBtJGZpdHRlZC52YWx1ZXMsCiAgbWFpbj1tJGNhbGwsIHhsYWI9Zm0sIGNleD0uNSwgcGNoPTE2LAogIHhsaW09YygwLCAzMDApLCB5bGltPWMoMCwgMzAwKSkKCnRleHQobSRtb2RlbCRwY2V4cF9wcHBfbVtybmRdLCBtJGZpdHRlZC52YWx1ZXNbcm5kXSwgCiAgbGFiZWxzPWcyLm5iLmR0W3JuZCwgc3Z5TDJObV0sCiAgY2V4PS42LCBwb3M9NCkKCiMgQW5kIHJlc2lkdWFscyB4IGZpdHRlZCB2YWx1ZXMKcGxvdChtLCB3aGljaD0zKQoKYGBgCgoKYGBge3J9CgojIFBsb3QgTEFHCnBsb3QobWxhZ3NhciR5LCBtbGFnc2FyJGZpdHRlZC52YWx1ZXMsCiAgbWFpbj1tbGFnc2FyJGNhbGwsIHhsYWI9Zm0sIGNleD0uNSwgcGNoPTE2LAogIHhsaW09YygwLCAzMDApLCB5bGltPWMoMCwgMzAwKSkKCnRleHQobWxhZ3NhciR5W3JuZF0sIG1sYWdzYXIkZml0dGVkLnZhbHVlc1tybmRdLCAKICBsYWJlbHM9ZzIubmIuZHRbcm5kLCBzdnlMMk5tXSwKICBjZXg9LjYsIHBvcz00KQoKYGBgCgoKYGBge3J9CgojIFBsb3QgU0FDCnBsb3QobXNhY3NhciR5LCBtc2Fjc2FyJGZpdHRlZC52YWx1ZXMsCiAgbWFpbj1tc2Fjc2FyJGNhbGwsIHhsYWI9Zm0sIGNleD0uNSwgcGNoPTE2LAogIHhsaW09YygwLCAzMDApLCB5bGltPWMoMCwgMzAwKSkKCnRleHQobXNhY3NhciR5W3JuZF0sIG1zYWNzYXIkZml0dGVkLnZhbHVlc1tybmRdLCAKICBsYWJlbHM9ZzIubmIuZHRbcm5kLCBzdnlMMk5tXSwKICBjZXg9LjYsIHBvcz00KQoKYGBgCgoKIyMgR2VvZ3JhcGhpY2FsbHkgV2VpZ2h0ZWQgUmVncmVzc2lvbgoKU2VlIEJpdmFuZCBhdCBodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvc3Bnd3IvdmlnbmV0dGVzL0dXUi5wZGYKYW5kIEFuc2VsaW4KaHR0cDovL3d3dy5jc2lzcy5vcmcvZ2lzcG9wc2NpL3dvcmtzaG9wcy8yMDExL1BTVS9yZWFkaW5ncy9XMTVfQW5zZWxpbjIwMDcucGRmLgpBbHNvIEJydW5zZG9uIGh0dHA6Ly9ycHVicy5jb20vY2hyaXNicnVuc2Rvbi8xMDEzMDUuCgpOb3RlIHRoYXQgc2FtcGxpbmcgd2VpZ2h0cyBhcmUgbm90IGltcGxlbWVudGVkLiBDaG9vc2luZyBhIG1ldGhvZCB0byBlc3RpbWF0ZQpvcHRpbWFsIGJhbmR3aWR0aCBpcyB1bmNsZWFyIChjaGVjayBkb2NvKSwgYWxzbyBub3QgY2xlYXIgaG93IHRvIGNob29zZSBhCmtlcm5lbCBmdW5jdGlvbiAoZGVmYXVsdCBHYXVzc2lhbikuCgoKYGBge3IgZ3dyLCBjYWNoZT1UUlVFfQoKIyBMb2FkIHBhY2thZ2UKbGlicmFyeShzcGd3cikKCiMgVHJ5IEdXUi9MTSBvbiBzYW1lIG1vZGVsIGFzIGFib3ZlIChwYXNzIHNoYXBlcywgd2lsbCByZXR1cm4gc2hhcGVzIHdpdGggY29lZmYpCmJ3RyA8LSBnd3Iuc2VsKGZtLCBkYXRhPWcyLm5iLCBnd2VpZ2h0PWd3ci5HYXVzcywgdmVyYm9zZT1GQUxTRSkKZ3dyRyA8LSBnd3IoZm0sIGRhdGE9ZzIubmIsIGJhbmR3aWR0aD1id0csIGd3ZWlnaHQ9Z3dyLkdhdXNzLCBoYXRtYXRyaXg9VFJVRSkKZ3dyRwoKIyBNYXAgY29lZmZpY2llbnRzCmRhdGEoV29ybGQpCgpmb3IgKGkgaW4gWCkge3ByaW50KAogICAgdG1fc2hhcGUoZ3dyRyRTREYsIGlzLm1hc3Rlcj1UKSArIAogICAgICB0bV9maWxsKGksIHBhbGV0dGU9IlJkWWxHbiIsIHN0eWxlPSJqZW5rcyIsIG49OSwKICAgICAgICB0aXRsZT1zdHJpbmdyOjpzdHJfd3JhcChkdDIubGJsW1ksIHZhckxhYmVsXSwgMzApKSArCiAgICAgIHRtX3NoYXBlKFdvcmxkKSArIHRtX2JvcmRlcnMobHdkPTAuMSkgKwogICAgICB0bV9sYXlvdXQoCiAgICAgICAgdGl0bGU9ZHQyLmxibFtpLCB2YXJMYWJlbF0sCiAgICAgICAgdGl0bGUuc25hcC50by5sZWdlbmQ9VCwgbGVnZW5kLm91dHNpZGU9VCkKICApfQoKCmBgYAoKCmBgYHtyIGdnd3IsIGV2YWw9RkFMU0V9CgojIENvdWxkIGFsc28gdHJ5IGEgR1dSL0dMTSBvbiBzYW1lIG1vZGVsIGFzIGFib3ZlCmJ3RyA8LSBnZ3dyLnNlbChmbSwgZGF0YT1nMi5uYiwgZ3dlaWdodD1nd3IuR2F1c3MsIHZlcmJvc2U9RkFMU0UpCmd3ckcgPC0gZ2d3cihmbSwgZGF0YT1nMi5uYiwgYmFuZHdpZHRoPWJ3RywgZ3dlaWdodD1nd3IuR2F1c3MsIGhhdG1hdHJpeD1UUlVFLAogIGZhbWlseT0icG9pc3NvbiIpCgpgYGAKCmBgYHtyIHNhdmUsIGV2YWw9RkFMU0V9CgpzYXZlLmltYWdlKCIuL3RtcC9wb3ZlcnR5X3IxNi4wNS5SRGF0YSIpCgpgYGAKCgo=