About This Project
In this assignment at the University of Pennsylvania Stuart Weitzman
School of Design, CPLN 675 Land Use and Environmental Modeling, our goal
was to develop a predictive flood inundation model using Calgary,
Alberta, Canada as the base geography on which to train a model that
could be applied to other cities, using the same factors.
For this model we selected the factors of: - Soil Drainage Efficacy
(i.e., soil type and its capacity to allow water absorption) - Water
Flow Accumulation (i.e., how much–and where–water collects
topographically?) - Surface Permeability (i.e., whether impervious
surfaces, such as buildings and concrete, sit on a given segment of land
area) - Distance to Water Bodies (i.e., how far is a given point in land
from where water collects?)
Setup & Visualizing Our Dependent Variable and Factors
Below, we walk through the steps of setting up and visualizing our
data, to help us to understand the apparent relationship between our
dependent variable, historic flooding (hist_flood), and our four factors
in the Calgay geography.
Importing Preliminary Spatial Data
First, we load our two cities’ boundaries and some other preliminary
datasets, and put them in web Mercator projections (CRS 3395).
calgary_boundary <- st_read("https://raw.githubusercontent.com/ana-oso/CalgaryvBoise/0ab4aadf9e7d83409aefd5ac8dab3e6515240b1f/geojsons/calgary_geojsons/calgary_geojsons/CALGARY_BOUNDARY.geojson")
calgary_boundary <- calgary_boundary %>%
st_transform(crs = 3395)
boise_boundary <- st_read("https://raw.githubusercontent.com/ana-oso/CalgaryvBoise/0ab4aadf9e7d83409aefd5ac8dab3e6515240b1f/geojsons/boise_geojsons/boise_geojsons/BOISE_BOUNDARY.geojson")
boise_boundary <- boise_boundary %>%
st_transform(crs = 3395)
#HYDROLOGY
#calgary_hydrology <- st_read("C:/Users/Lindsey/Desktop/CPLN675_Land_Use_Modeling_Desktop/Midterm/Data_Calgary/CalgaryHydrology.shp")
#calgary_hydrology <- calgary_hydrology %>%
# st_transform(crs = 3395)
#boise_hydrology1 <- st_read("C:/Users/Lindsey/Desktop/CPLN675_Land_Use_Modeling_Desktop/Midterm/Data_Boise/BoiseHydrology1.shp")
#boise_hydrology2 <- st_read("C:/Users/Lindsey/Desktop/CPLN675_Land_Use_Modeling_Desktop/Midterm/Data_Boise/BoiseHydrology2.shp")
# boise_hydrology1 <- boise_hydrology1 %>%
# st_transform(crs = 3395)
#boise_hydrology2 <- boise_hydrology2 %>%
# st_transform(crs = 3395)
calgary_master_fish <- st_read("https://raw.githubusercontent.com/ana-oso/CalgaryvBoise/0ab4aadf9e7d83409aefd5ac8dab3e6515240b1f/geojsons/calgary_geojsons/calgary_geojsons/CALGARY_MASTERFISH.geojson")
boise_master_fish <- st_read("https://raw.githubusercontent.com/ana-oso/CalgaryvBoise/0ab4aadf9e7d83409aefd5ac8dab3e6515240b1f/geojsons/boise_geojsons/boise_geojsons/BOISE_MASTERFISH.geojson")
Creating Fishnets
Now, we use R to create fishnets for the two cities with the same
cell size and projections, to ensure consistency among forthcoming
maps.
calgary_fishnet <- st_make_grid(calgary_boundary,
cellsize = 500,
square = FALSE) %>%
.[calgary_boundary] %>%
st_sf() %>%
mutate(uniqueID = rownames(.))
ggplot()+
geom_sf(data = calgary_fishnet,
fill = "pink")+
geom_sf(data = calgary_boundary,
color = "black", fill = "transparent") +
mapTheme

boise_fishnet <- st_make_grid(boise_boundary,
cellsize = 500,
square = FALSE) %>%
.[boise_boundary] %>%
st_sf() %>%
mutate(uniqueID = rownames(.))
ggplot()+
geom_sf(data = boise_fishnet,
fill = "lightblue")+
geom_sf(data = boise_boundary,
color = "black", fill = "transparent") +
mapTheme

#st_write(calgary_fishnet, "C:/Users/Lindsey/Desktop/CPLN675_Land_Use_Modeling_Desktop/Midterm/Derived Files/calgary_fishnet.shp", geometry?=?TRUE)
#st_write(boise_fishnet, "C:/Users/Lindsey/Desktop/CPLN675_Land_Use_Modeling_Desktop/Midterm/Derived Files/boise_fishnet.shp", geometry?=?TRUE)
Dependent Variable: Flooding Maps
Now, we visualize our two geographies with their outlays of
historical flooding. Considering historical flooding is the dependent
variable which all of our factors in our model are trying to predict, it
is good to first understand where flooding occurs as a baseline of our
model’s success.
grid.arrange(ncol = 2,
ggplot() +
labs(title="Calgary, Historical Flooding and Hydrology",
subtitle="Calgary; Yellow = Historical flooding observed")+
geom_sf(data = calgary_boundary,
color = "black")+
geom_sf(data=calgary_master_fish %>%
filter(hist_flood == 6),
fill="yellow",alpha = 0.6,color='transparent') +
# geom_sf(data=calgary_hydrology,
# fill = 'light blue')+
mapTheme,
ggplot() +
geom_sf(data = boise_boundary,
color = "black")+
geom_sf(data=boise_master_fish %>%
filter(hist_flood == 6),
fill="yellow",alpha = 0.6,color='transparent') +
#geom_sf(data=boise_hydrology1,
# fill = 'light blue')+
# geom_sf(data=boise_hydrology2,
# fill = 'light blue')+
mapTheme+
labs(title="Boise, Historical Flooding and Hydrology",
subtitle="Boise; Yellow = Historical flooding observed"))

Importing Other Files for Factors and Map Elements
Here, we import our spatial data to map our factors and train our
model. Most of this data was cleaned, derived, and assembled using
ArcGIS Pro.
Mapping
Calgary
Here, we create maps of our four factors for our base geography,
Calgary.

#IMPERVIOUS v. PERVIOUS SURFACES -- Good
ggplot() +
geom_sf(data = calgary_boundary,
color = "black")+
geom_sf(data = calgary_surf_fish,
aes(fill = Pervious),
color = "transparent")+
scale_fill_viridis('Surface Permeability (6 = least)',
direction = -1, # Direction of the color palette - either 1 or -1
option = 'F', # Viridis has multiple palettes to choose from
alpha = 0.6)+
labs(title="Surface Permeability") +
mapTheme

#DISTANCE TO WATER BODY -- Good
ggplot() +
geom_sf(data = calgary_boundary,
color = "black")+
geom_sf(data = calgary_disth2o_fish,
aes(fill = gridcode),
color = "transparent")+
scale_fill_viridis('Distance to Water (6 = closest)',
direction = -1, # Direction of the color palette - either 1 or -1
option = 'F',
alpha = 0.6)+
labs(title="Distance to Water Body") +
mapTheme

#FLOW ACCUMULATION -- good
ggplot() +
geom_sf(data = calgary_boundary,
color = "black")+
geom_sf(data = calgary_flow_fish,
aes(fill = FlowAcc),
color = "transparent")+
scale_fill_viridis('Flow Accumulation (6 = greatest)',
direction = -1,
option = 'F',
alpha = 0.6)+
labs(title="Water Flow Accumulation") +
mapTheme

Boise
Likewise, here we visualize the same four factors within Boise’s
boundaries.
###Boise
#SOIL DRAINAGE -- Good
ggplot() +
geom_sf(data = boise_boundary,
fill = "transparent", color = "black")+
geom_sf(data = boise_drain_fish,
aes(fill = DrainRank),
color = "transparent")+
scale_fill_viridis('Soil Drainage Efficacy (6 = best)',
direction = -1, # Direction of the color palette - either 1 or -1
option = 'F', # Viridis has multiple palettes to choose from
alpha = 0.6)+
labs(title="Soil Type & Drainage") +
mapTheme

#FLOW ACCUMULATION -- good
ggplot() +
geom_sf(data = boise_boundary,
fill = "transparent", color = "black")+
geom_sf(data = boise_flow_fish,
aes(fill = gridcode),
color = "transparent")+
scale_fill_viridis('Flow Accumulation (6 = greatest)',
direction = -1,
option = 'F',
alpha = 0.6)+
labs(title="Water Flow Accumulation") +
mapTheme

#IMPERVIOUS v. PERVIOUS SURFACES -- good
ggplot() +
geom_sf(data = boise_boundary,
color = "black")+
geom_sf(data = boise_surf_fish,
aes(fill = gridcode),
color = "transparent")+
scale_fill_viridis('Surface Permeability (6 = least)',
direction = -1,
option = 'F',
alpha = 0.6)+
labs(title="Surface Permeability") +
mapTheme

#DISTANCE TO WATER BODY -- good
ggplot() +
geom_sf(data = boise_boundary,
color = "black")+
geom_sf(data = boise_disth2o_fish,
aes(fill = gridcode),
color = "transparent")+
scale_fill_viridis('Distance to Water (6 = closest)',
direction = -1, # Direction of the color palette - either 1 or -1
option = 'F',
alpha = 0.6)+
labs(title="Distance to Water Body") +
mapTheme

Data Wrangling
Here, we clean the tabular data underlying the spatial data to
prepare it for running through the model.
Plotting
In these plots, we see how our factors in Calgary average out in
areas within the city that have flooding versus those that have not
flooded. We notice that areas that have soil with better drainage
efficacy (DrainRank) have been more likely to not flood. Likewise, areas
with lots of flow accumulation are more likely to flood than areas with
minimal accumulation.
Model Building
Here, we walk through the steps of building and training our model,
and then applying that model to our other geography, Boise.
Binomial Model
Here, we employ a glm linear model, which finds that soil drainage
efficacy (“DrainRank”) and Flow Accumulation (“FlowAcc”) are extremely
statistically significant in their relationship to Calgary’s historical
flooding. Distance to water body (“disth2o_gr”) was also found to be
statistically significant. Surface permeability (“Pervious”), however,
did not have a very strong relationship with observed flooding in
Calgary.
calgaryModel <- glm(hist_flood ~ DrainRank + FlowAcc + Pervious + disth2o_gr,
family="binomial"(link="logit"), data = calgaryTrain1 %>%
as.data.frame() %>% dplyr::select(-geometry,-OBJECTID,hist_flood,DrainRank,FlowAcc,Pervious,disth2o_gr))
summary(calgaryModel)
##
## Call:
## glm(formula = hist_flood ~ DrainRank + FlowAcc + Pervious + disth2o_gr,
## family = binomial(link = "logit"), data = calgaryTrain1 %>%
## as.data.frame() %>% dplyr::select(-geometry, -OBJECTID,
## hist_flood, DrainRank, FlowAcc, Pervious, disth2o_gr))
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) 0.829715 0.604726 1.372 0.170047
## DrainRank -1.573263 0.171256 -9.187 < 2e-16 ***
## FlowAcc 0.495187 0.058706 8.435 < 2e-16 ***
## Pervious 0.007057 0.045789 0.154 0.877513
## disth2o_gr 0.121958 0.034912 3.493 0.000477 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 2026.9 on 7153 degrees of freedom
## Residual deviance: 1718.4 on 7149 degrees of freedom
## AIC: 1728.4
##
## Number of Fisher Scoring iterations: 8
Model Validation
The items below interrogate how successful our model is at predicting
Calgary’s flooding, using several different visualizations and
statistical evaluations. In both the histogram and filled line graph,
our predictions are that flooded areas have about a .5 chance of
flooding, while there is more nuance for areas that do not have
historical flooding.
classProbs <- predict(calgaryModel, calgaryTest1, type="response")
hist(classProbs)

testProbs <- data.frame(obs = as.numeric(calgaryTest1$hist_flood),
pred = classProbs)
ggplot(testProbs, aes(x = pred, fill=as.factor(obs))) +
geom_density() +
facet_grid(obs ~ .) +
xlab("Probability") +
geom_vline(xintercept = .5) +
scale_fill_manual(values = c("#669999", "#00CCCC"),
labels = c("Not Flooded","Flooded"),
name = "") +
plotTheme

Confusion Metrics
The confusion metrics predict a high degree of accuracy with our
Calgary model, while still generating false positives and false
negatives.
testProbs$predClass = ifelse(testProbs$pred > .05 ,1,0)
#predicting that if its greater than 50% its a 1, less is 0
caret::confusionMatrix(reference = as.factor(testProbs$obs),
data = as.factor(testProbs$predClass),
positive = "1")
## Confusion Matrix and Statistics
##
## Reference
## Prediction 0 1
## 0 2454 43
## 1 537 32
##
## Accuracy : 0.8108
## 95% CI : (0.7965, 0.8246)
## No Information Rate : 0.9755
## P-Value [Acc > NIR] : 1
##
## Kappa : 0.0587
##
## Mcnemar's Test P-Value : <2e-16
##
## Sensitivity : 0.42667
## Specificity : 0.82046
## Pos Pred Value : 0.05624
## Neg Pred Value : 0.98278
## Prevalence : 0.02446
## Detection Rate : 0.01044
## Detection Prevalence : 0.18558
## Balanced Accuracy : 0.62356
##
## 'Positive' Class : 1
##
## How it works:
# **Predicted = 0, Observed = 0 -> True Negative**
# **Predicted = 1, Observed = 1 -> True Positive**
# **Predicted = 1, Observed = 0 -> False Positive**
# **Predicted = 0, Observed = 1 -> False Negative**
# **1. Sensitivity - the proportion of actual positives (1's) that were predicted to be positive. Also known as "true positive rate".**
# **2. Specificity - The proportion of actual negatives (0's) that were predicted to be negatives. Also known as "true negative rate".**
ROC Curve
Given that the ROC Curve is not bowed out to the top and left edges,
the model is not tremendously over-fit.
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.

## Setting levels: control = 0, case = 1
## Setting direction: controls < cases
## Area under the curve: 0.7751
Cross Validation
Cross validation tests the strength of our model on sample data. We
test our model on 100 test sets, and then we estimate the strength of
our model.
ctrl<- trainControl(method = "cv",
number = 100,
p = 0.7,
savePredictions = TRUE)
cvFit <- train(as.factor(hist_flood) ~ ., data =
calgary_vars %>%
as.data.frame() %>%
dplyr::select(hist_flood,DrainRank,FlowAcc,Pervious,disth2o_gr),
method="glm", family="binomial",
trControl = ctrl)
cvFit
## Generalized Linear Model
##
## 10220 samples
## 4 predictor
## 2 classes: '0', '1'
##
## No pre-processing
## Resampling: Cross-Validated (100 fold)
## Summary of sample sizes: 10118, 10117, 10118, 10118, 10118, 10117, ...
## Resampling results:
##
## Accuracy Kappa
## 0.9702579 0
The histogram of our cross validation shows that all tests of our
model are highly accurate.
ggplot(as.data.frame(cvFit$resample), aes(Accuracy)) +
geom_histogram() +
scale_x_continuous(limits = c(0, 1)) +
labs(x="Accuracy",
y="Count")+
plotTheme

Attaching Predictions to Calgary Model
In the final step of building our Calgary model, we attach our
model’s predictions to our original Calgary fishnet…
Visualizing Calgary Predictions
…and we visualize our model’s prediction of flooding against the
historical data of observed flooding.
ggplot() +
geom_sf(data=calgary1, aes(fill=factor(ntile(allPredictions,5))),
colour=NA) +
scale_fill_manual(values=c("#DEF5E5FF","#49C1ADFF","#357BA2FF","#3E356BFF","#0B0405FF"),
labels=as.character(quantile(calgary1$allPredictions,
c(0, .25, .5, .75, 1),
na.rm=T)),
name="Predicted Quintile\nProbabilities(%)\n(Darkest=\nHighest)") +
geom_sf(data=calgary1 %>%
filter(hist_flood == 1),
fill="yellow",alpha = .4, colour="NA") +
mapTheme +
labs(title="Observed and Predicted Flooding Areas",
subtitle="Calgary; Yellow = Historical flooding; Gradient = Predicted flooding (Darkest = Most)")

We see that our Calgary model has some unusual vertical striping. We
are not sure why this happened, but suspect it bay be the result of
incongruity units among our datasets visualized. We elaborate below as
to the potential implications of this, and how we would go about
adjusting our model to try to rectify this incorrect visualization.
Calgary Confusion Metrics:
By running confusion metrics, we see that our model predicts true
negatives and true positives well. However, it has a lot of false
positives because soil drainage efficacy appears to be over-weighted,
affecting the shape of the model and the confusion metrics
visualization.
calgary1 %>%
mutate(confResult=case_when(allPredictions < 4 & hist_flood==0 ~ "True_Negative",
allPredictions >= 1 & hist_flood==1 ~ "True_Positive",
allPredictions < 4 & hist_flood==1 ~ "False_Negative",
allPredictions >= 4 & hist_flood==0 ~ "False_Positive")) %>%
ggplot()+
geom_sf(aes(fill = confResult), color = "transparent")+
scale_fill_manual(values = c("Red","Light Blue","Orange","Pink"),
name="Outcomes")+
labs(title="Calgary Confusion Metrics") +
mapTheme

Data Wrangling - Boise
Here, we pull in our Boise data, which is our original fishnet, but
fully populated with Boise’s data on the four factors on which we
trained our Calgary model.
boise_master_fish <- st_read("https://raw.githubusercontent.com/ana-oso/CalgaryvBoise/0ab4aadf9e7d83409aefd5ac8dab3e6515240b1f/geojsons/boise_geojsons/boise_geojsons/BOISE_MASTERFISH.geojson")
## Reading layer `BOISE_MASTERFISH' from data source
## `https://raw.githubusercontent.com/ana-oso/CalgaryvBoise/0ab4aadf9e7d83409aefd5ac8dab3e6515240b1f/geojsons/boise_geojsons/boise_geojsons/BOISE_MASTERFISH.geojson'
## using driver `GeoJSON'
## Simple feature collection with 2255 features and 15 fields
## Geometry type: POLYGON
## Dimension: XY
## Bounding box: xmin: -12954270 ymin: 5360116 xmax: -12923270 ymax: 5388839
## Projected CRS: WGS 84 / World Mercator
boise_vars <-
boise_master_fish %>%
dplyr::select(hist_flood,DrainRank,FlowAcc,Pervious,disth2o_gr,OBJECTID)
boise_vars <- boise_vars %>% mutate(hist_flood=recode(hist_flood, '1'='0', '6'='1'))
boise_vars <- boise_vars %>% mutate(hist_flood = as.numeric(hist_flood))
boise_allPredictions <-
predict(cvFit, boise_vars, type="prob")[,2]
boise1 <-
cbind(boise_vars,boise_allPredictions) %>%
mutate(boise_allPredictions = round(boise_allPredictions * 100))
Testing the Calgary Flood Model on Boise
Here, we apply our Calgary flood model to our other geography, Boise.
We attach the prediction values, trained on the Calgary model, to Boise,
based on Boise’s spatial distribution of the four factors that we input
to our Calgary model.
ggplot() +
geom_sf(data=boise1, aes(fill=factor(ntile(boise_allPredictions,5))),
colour=NA) +
scale_fill_manual(values = c("#DEF5E5FF","#49C1ADFF","#357BA2FF","#3E356BFF","#0B0405FF"),
labels=as.character(quantile(boise1$boise_allPredictions,
c(0, .25, .5, .75, 1),
na.rm=T)),
name="Predicted Quintile\nProbabilities(%)\n(Darkest=\nHighest)") +
geom_sf(data=boise1 %>%
filter(hist_flood == 1),
fill="yellow",alpha = 0.5, colour="NA") +
coord_sf(xlim = c(-12955270, -12923270), ylim = c(5352033, 5394976), expand = FALSE) +
mapTheme +
labs(title="Observed and Predicted Flooding Areas",
subtitle="Boise; Yellow = Historical flooding; Gradient = Predicted flooding (Darkest = Most)")

LS0tDQp0aXRsZTogIlByZWRpY3RpdmUgRmxvb2QgTW9kZWxsaW5nIGZvciBCb2lzZSwgSWRhaG8iDQphdXRob3I6ICJMaW5kc2V5IEhvdmVyLCBTYW1yaWRkaGkgS2hhcmUgJiBBbmFzdGFzaWEgT3NvcmlvIg0KZGF0ZTogIjQvNi8yMDIzIg0Kb3V0cHV0OiANCiAgaHRtbF9kb2N1bWVudDoNCiAgICB0aGVtZTogcmVhZGFibGUNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZmxvYXQ6IHRydWUNCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQogICAgY29kZV9mb2xkaW5nOiBoaWRlDQplZGl0b3Jfb3B0aW9uczogDQogIG1hcmtkb3duOiANCiAgICB3cmFwOiA3Mg0KLS0tDQoNCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9DQpybShsaXN0PWxzKCkpDQojc2V0d2QoIkM6L1VzZXJzL0xpbmRzZXkvRGVza3RvcC9DUExONjc1X0xhbmRfVXNlX01vZGVsaW5nX0Rlc2t0b3AvTWlkdGVybS9SU2NyaXB0cyIpDQoNCmluc3RhbGwucGFja2FnZXMoInJtYXJrZG93biIpIA0KaW5zdGFsbC5wYWNrYWdlcygia25pdHIiKQ0KaW5zdGFsbC5wYWNrYWdlcygiY2FyZXQiKSANCmluc3RhbGwucGFja2FnZXMoInBzY2wiKQ0KaW5zdGFsbC5wYWNrYWdlcygicGxvdFJPQyIpIGluc3RhbGwucGFja2FnZXMoInBST0MiKQ0KaW5zdGFsbC5wYWNrYWdlcygia2FibGVFeHRyYSIpIGluc3RhbGwucGFja2FnZXMoImdyaWRFeHRyYSIpDQppbnN0YWxsLnBhY2thZ2VzKCJyYXN0ZXIiKSANCmluc3RhbGwucGFja2FnZXMoImthYmxlRXh0cmEiKSBpbnN0YWxsLnBhY2thZ2VzKCJnZW9qc29uaW8pDQoNCmBgYA0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRSxtZXNzYWdlID0gRkFMU0UsY2FjaGU9VFJVRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkNCm9wdGlvbnMoc2NpcGVuPTk5OSkNCmxpYnJhcnkoa25pdHIpDQpgYGANCg0KYGBge3IgbGlicmFyaWVzLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQ0KbGlicmFyeShjYXJldCkNCmxpYnJhcnkocGxvdFJPQykNCmxpYnJhcnkocFJPQykNCmxpYnJhcnkoc2YpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkoa25pdHIpDQpsaWJyYXJ5KGthYmxlRXh0cmEpDQpsaWJyYXJ5KHRpZ3JpcykNCmxpYnJhcnkodmlyaWRpcykNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoZ3JpZEV4dHJhKQ0KbGlicmFyeShyYXN0ZXIpDQojbGlicmFyeShyZ2RhbCkNCmxpYnJhcnkoa2FibGVFeHRyYSkNCmxpYnJhcnkoZ2VvanNvbmlvKQ0KDQpgYGANCg0KYGBge3IgbWFwVGhlbWUsIGluY2x1ZGU9RkFMU0V9DQptYXBUaGVtZSA8LSB0aGVtZShwbG90LnRpdGxlID1lbGVtZW50X3RleHQoc2l6ZT0xMiksDQogICAgICAgICAgICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9OCksDQogICAgICAgICAgICAgICAgICBwbG90LmNhcHRpb24gPSBlbGVtZW50X3RleHQoc2l6ZSA9IDYpLA0KICAgICAgICAgICAgICAgICAgYXhpcy5saW5lPWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgICAgICAgICAgIGF4aXMudGV4dC54PWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgICAgICAgICAgIGF4aXMudGV4dC55PWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgICAgICAgICAgIGF4aXMudGlja3M9ZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgICAgICAgICAgYXhpcy50aXRsZS54PWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgICAgICAgICAgIGF4aXMudGl0bGUueT1lbGVtZW50X2JsYW5rKCksDQogICAgICAgICAgICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kPWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgICAgICAgICAgIHBhbmVsLmJvcmRlcj1lbGVtZW50X2JsYW5rKCksDQogICAgICAgICAgICAgICAgICBwYW5lbC5ncmlkLm1ham9yPWVsZW1lbnRfbGluZShjb2xvdXIgPSAndHJhbnNwYXJlbnQnKSwNCiAgICAgICAgICAgICAgICAgIHBhbmVsLmdyaWQubWlub3I9ZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgICAgICAgICAgbGVnZW5kLmRpcmVjdGlvbiA9ICJ2ZXJ0aWNhbCIsIA0KICAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IiwNCiAgICAgICAgICAgICAgICAgIHBsb3QubWFyZ2luID0gbWFyZ2luKDEsIDEsIDEsIDEsICdjbScpLA0KICAgICAgICAgICAgICAgICAgbGVnZW5kLmtleS5oZWlnaHQgPSB1bml0KDEsICJjbSIpLCBsZWdlbmQua2V5LndpZHRoID0gdW5pdCgwLjIsICJjbSIpKQ0KDQpwbG90VGhlbWUgPC0gdGhlbWUoDQogIHBsb3QudGl0bGUgPWVsZW1lbnRfdGV4dChzaXplPTEyKSwNCiAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTgpLA0KICBwbG90LmNhcHRpb24gPSBlbGVtZW50X3RleHQoc2l6ZSA9IDYpLA0KICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTAsIGFuZ2xlID0gNDUsIGhqdXN0ID0gMSksDQogIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksDQogIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLA0KICAjIFNldCB0aGUgZW50aXJlIGNoYXJ0IHJlZ2lvbiB0byBibGFuaw0KICBwYW5lbC5iYWNrZ3JvdW5kPWVsZW1lbnRfYmxhbmsoKSwNCiAgcGxvdC5iYWNrZ3JvdW5kPWVsZW1lbnRfYmxhbmsoKSwNCiAgI3BhbmVsLmJvcmRlcj1lbGVtZW50X3JlY3QoY29sb3VyPSIjRjBGMEYwIiksDQogICMgRm9ybWF0IHRoZSBncmlkDQogIHBhbmVsLmdyaWQubWFqb3I9ZWxlbWVudF9saW5lKGNvbG91cj0iI0QwRDBEMCIsc2l6ZT0uNzUpLA0KICBheGlzLnRpY2tzPWVsZW1lbnRfYmxhbmsoKSkNCmBgYA0KDQojIEFib3V0IFRoaXMgUHJvamVjdA0KDQpJbiB0aGlzIGFzc2lnbm1lbnQgYXQgdGhlIFVuaXZlcnNpdHkgb2YgUGVubnN5bHZhbmlhIFN0dWFydCBXZWl0em1hbg0KU2Nob29sIG9mIERlc2lnbiwgQ1BMTiA2NzUgTGFuZCBVc2UgYW5kIEVudmlyb25tZW50YWwgTW9kZWxpbmcsIG91ciBnb2FsDQp3YXMgdG8gZGV2ZWxvcCBhIHByZWRpY3RpdmUgZmxvb2QgaW51bmRhdGlvbiBtb2RlbCB1c2luZyBDYWxnYXJ5LA0KQWxiZXJ0YSwgQ2FuYWRhIGFzIHRoZSBiYXNlIGdlb2dyYXBoeSBvbiB3aGljaCB0byB0cmFpbiBhIG1vZGVsIHRoYXQNCmNvdWxkIGJlIGFwcGxpZWQgdG8gb3RoZXIgY2l0aWVzLCB1c2luZyB0aGUgc2FtZSBmYWN0b3JzLg0KDQpGb3IgdGhpcyBtb2RlbCB3ZSBzZWxlY3RlZCB0aGUgZmFjdG9ycyBvZjogLSBTb2lsIERyYWluYWdlIEVmZmljYWN5DQooaS5lLiwgc29pbCB0eXBlIGFuZCBpdHMgY2FwYWNpdHkgdG8gYWxsb3cgd2F0ZXIgYWJzb3JwdGlvbikgLSBXYXRlcg0KRmxvdyBBY2N1bXVsYXRpb24gKGkuZS4sIGhvdyBtdWNoLS1hbmQgd2hlcmUtLXdhdGVyIGNvbGxlY3RzDQp0b3BvZ3JhcGhpY2FsbHk/KSAtIFN1cmZhY2UgUGVybWVhYmlsaXR5IChpLmUuLCB3aGV0aGVyIGltcGVydmlvdXMNCnN1cmZhY2VzLCBzdWNoIGFzIGJ1aWxkaW5ncyBhbmQgY29uY3JldGUsIHNpdCBvbiBhIGdpdmVuIHNlZ21lbnQgb2YgbGFuZA0KYXJlYSkgLSBEaXN0YW5jZSB0byBXYXRlciBCb2RpZXMgKGkuZS4sIGhvdyBmYXIgaXMgYSBnaXZlbiBwb2ludCBpbiBsYW5kDQpmcm9tIHdoZXJlIHdhdGVyIGNvbGxlY3RzPykNCg0KIyBDYWxnYXJ5IGFuZCBCb2lzZQ0KDQpXZSBzZWxlY3RlZCBCb2lzZSwgSWRhaG8gYXMgb3VyIGNvbXBhcmFibGUgY2l0eS4NCg0KIyBTZXR1cCAmIFZpc3VhbGl6aW5nIE91ciBEZXBlbmRlbnQgVmFyaWFibGUgYW5kIEZhY3RvcnMNCg0KQmVsb3csIHdlIHdhbGsgdGhyb3VnaCB0aGUgc3RlcHMgb2Ygc2V0dGluZyB1cCBhbmQgdmlzdWFsaXppbmcgb3VyIGRhdGEsDQp0byBoZWxwIHVzIHRvIHVuZGVyc3RhbmQgdGhlIGFwcGFyZW50IHJlbGF0aW9uc2hpcCBiZXR3ZWVuIG91ciBkZXBlbmRlbnQNCnZhcmlhYmxlLCBoaXN0b3JpYyBmbG9vZGluZyAoaGlzdF9mbG9vZCksIGFuZCBvdXIgZm91ciBmYWN0b3JzIGluIHRoZQ0KQ2FsZ2F5IGdlb2dyYXBoeS4NCg0KIyMjIERhdGEgU291cmNlcw0KDQotICAgPGh0dHBzOi8vb3BlbmRhdGEuY2l0eW9mYm9pc2Uub3JnLz4NCi0gICA8aHR0cHM6Ly9vcGVuZ2lzZGF0YS1pZGFob2RlcS5vcGVuZGF0YS5hcmNnaXMuY29tLz4NCi0gICA8aHR0cHM6Ly9pbnNpZGVpZGFoby5vcmcvYnJvd3NlLW9yZy5odG1sIz4NCi0gICA8aHR0cHM6Ly9hcHBzLm5hdGlvbmFsbWFwLmdvdi9kb3dubG9hZGVyLz4NCi0gICA8aHR0cHM6Ly9kYXRhLmNhbGdhcnkuY2EvPg0KLSAgIDxodHRwczovL29wZW4uYWxiZXJ0YS5jYS9vcGVuZGF0YT4NCg0KIyMjIEltcG9ydGluZyBQcmVsaW1pbmFyeSBTcGF0aWFsIERhdGENCg0KRmlyc3QsIHdlIGxvYWQgb3VyIHR3byBjaXRpZXMnIGJvdW5kYXJpZXMgYW5kIHNvbWUgb3RoZXIgcHJlbGltaW5hcnkNCmRhdGFzZXRzLCBhbmQgcHV0IHRoZW0gaW4gd2ViIE1lcmNhdG9yIHByb2plY3Rpb25zIChDUlMgMzM5NSkuDQoNCmBgYHtyIGxvYWRfYm91bmRhcnlfZGF0YSwgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCByZXN1bHRzPSdoaWRlJ30NCmNhbGdhcnlfYm91bmRhcnkgPC0gc3RfcmVhZCgiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2FuYS1vc28vQ2FsZ2FyeXZCb2lzZS8wYWI0YWFkZjllN2Q4MzQwOWFlZmQ1YWM4ZGFiM2U2NTE1MjQwYjFmL2dlb2pzb25zL2NhbGdhcnlfZ2VvanNvbnMvY2FsZ2FyeV9nZW9qc29ucy9DQUxHQVJZX0JPVU5EQVJZLmdlb2pzb24iKQ0KDQoNCmNhbGdhcnlfYm91bmRhcnkgPC0gY2FsZ2FyeV9ib3VuZGFyeSAlPiUgDQogIHN0X3RyYW5zZm9ybShjcnMgPSAzMzk1KQ0KICANCmJvaXNlX2JvdW5kYXJ5IDwtIHN0X3JlYWQoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9hbmEtb3NvL0NhbGdhcnl2Qm9pc2UvMGFiNGFhZGY5ZTdkODM0MDlhZWZkNWFjOGRhYjNlNjUxNTI0MGIxZi9nZW9qc29ucy9ib2lzZV9nZW9qc29ucy9ib2lzZV9nZW9qc29ucy9CT0lTRV9CT1VOREFSWS5nZW9qc29uIikNCg0KYm9pc2VfYm91bmRhcnkgPC0gYm9pc2VfYm91bmRhcnkgJT4lIA0KICBzdF90cmFuc2Zvcm0oY3JzID0gMzM5NSkNCg0KI0hZRFJPTE9HWQ0KI2NhbGdhcnlfaHlkcm9sb2d5IDwtIHN0X3JlYWQoIkM6L1VzZXJzL0xpbmRzZXkvRGVza3RvcC9DUExONjc1X0xhbmRfVXNlX01vZGVsaW5nX0Rlc2t0b3AvTWlkdGVybS9EYXRhX0NhbGdhcnkvQ2FsZ2FyeUh5ZHJvbG9neS5zaHAiKQ0KICAjY2FsZ2FyeV9oeWRyb2xvZ3kgPC0gY2FsZ2FyeV9oeWRyb2xvZ3kgJT4lIA0KICAgIyBzdF90cmFuc2Zvcm0oY3JzID0gMzM5NSkNCg0KI2JvaXNlX2h5ZHJvbG9neTEgPC0gc3RfcmVhZCgiQzovVXNlcnMvTGluZHNleS9EZXNrdG9wL0NQTE42NzVfTGFuZF9Vc2VfTW9kZWxpbmdfRGVza3RvcC9NaWR0ZXJtL0RhdGFfQm9pc2UvQm9pc2VIeWRyb2xvZ3kxLnNocCIpDQojYm9pc2VfaHlkcm9sb2d5MiA8LSBzdF9yZWFkKCJDOi9Vc2Vycy9MaW5kc2V5L0Rlc2t0b3AvQ1BMTjY3NV9MYW5kX1VzZV9Nb2RlbGluZ19EZXNrdG9wL01pZHRlcm0vRGF0YV9Cb2lzZS9Cb2lzZUh5ZHJvbG9neTIuc2hwIikNCiAjIGJvaXNlX2h5ZHJvbG9neTEgPC0gYm9pc2VfaHlkcm9sb2d5MSAlPiUgDQogICMgICAgc3RfdHJhbnNmb3JtKGNycyA9IDMzOTUpDQogICNib2lzZV9oeWRyb2xvZ3kyIDwtIGJvaXNlX2h5ZHJvbG9neTIgJT4lIA0KICAgIyAgIHN0X3RyYW5zZm9ybShjcnMgPSAzMzk1KQ0KICANCmNhbGdhcnlfbWFzdGVyX2Zpc2ggPC0gc3RfcmVhZCgiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2FuYS1vc28vQ2FsZ2FyeXZCb2lzZS8wYWI0YWFkZjllN2Q4MzQwOWFlZmQ1YWM4ZGFiM2U2NTE1MjQwYjFmL2dlb2pzb25zL2NhbGdhcnlfZ2VvanNvbnMvY2FsZ2FyeV9nZW9qc29ucy9DQUxHQVJZX01BU1RFUkZJU0guZ2VvanNvbiIpDQoNCmJvaXNlX21hc3Rlcl9maXNoIDwtIHN0X3JlYWQoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9hbmEtb3NvL0NhbGdhcnl2Qm9pc2UvMGFiNGFhZGY5ZTdkODM0MDlhZWZkNWFjOGRhYjNlNjUxNTI0MGIxZi9nZW9qc29ucy9ib2lzZV9nZW9qc29ucy9ib2lzZV9nZW9qc29ucy9CT0lTRV9NQVNURVJGSVNILmdlb2pzb24iKQ0KDQoNCmBgYA0KDQojIyMgQ3JlYXRpbmcgRmlzaG5ldHMNCg0KTm93LCB3ZSB1c2UgUiB0byBjcmVhdGUgZmlzaG5ldHMgZm9yIHRoZSB0d28gY2l0aWVzIHdpdGggdGhlIHNhbWUgY2VsbA0Kc2l6ZSBhbmQgcHJvamVjdGlvbnMsIHRvIGVuc3VyZSBjb25zaXN0ZW5jeSBhbW9uZyBmb3J0aGNvbWluZyBtYXBzLg0KDQpgYGB7ciBmaXNobmV0cywgZWNobz1UUlVFfQ0KY2FsZ2FyeV9maXNobmV0IDwtIHN0X21ha2VfZ3JpZChjYWxnYXJ5X2JvdW5kYXJ5LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjZWxsc2l6ZSA9IDUwMCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3F1YXJlID0gRkFMU0UpICU+JSANCiAgLltjYWxnYXJ5X2JvdW5kYXJ5XSAlPiUgDQogIHN0X3NmKCkgJT4lIA0KICBtdXRhdGUodW5pcXVlSUQgPSByb3duYW1lcyguKSkNCg0KZ2dwbG90KCkrDQogIGdlb21fc2YoZGF0YSA9IGNhbGdhcnlfZmlzaG5ldCwNCiAgICAgICAgICBmaWxsID0gInBpbmsiKSsNCiAgZ2VvbV9zZihkYXRhID0gY2FsZ2FyeV9ib3VuZGFyeSwgDQogICAgICAgICAgY29sb3IgPSAiYmxhY2siLCBmaWxsID0gInRyYW5zcGFyZW50IikgKw0KICBtYXBUaGVtZQ0KDQoNCmJvaXNlX2Zpc2huZXQgPC0gc3RfbWFrZV9ncmlkKGJvaXNlX2JvdW5kYXJ5LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjZWxsc2l6ZSA9IDUwMCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3F1YXJlID0gRkFMU0UpICU+JSANCiAgLltib2lzZV9ib3VuZGFyeV0gJT4lIA0KICBzdF9zZigpICU+JSANCiAgbXV0YXRlKHVuaXF1ZUlEID0gcm93bmFtZXMoLikpDQoNCmdncGxvdCgpKw0KICBnZW9tX3NmKGRhdGEgPSBib2lzZV9maXNobmV0LA0KICAgICAgICAgIGZpbGwgPSAibGlnaHRibHVlIikrDQogIGdlb21fc2YoZGF0YSA9IGJvaXNlX2JvdW5kYXJ5LCANCiAgICAgICAgICBjb2xvciA9ICJibGFjayIsIGZpbGwgPSAidHJhbnNwYXJlbnQiKSArDQogIG1hcFRoZW1lDQoNCg0KI3N0X3dyaXRlKGNhbGdhcnlfZmlzaG5ldCwgIkM6L1VzZXJzL0xpbmRzZXkvRGVza3RvcC9DUExONjc1X0xhbmRfVXNlX01vZGVsaW5nX0Rlc2t0b3AvTWlkdGVybS9EZXJpdmVkIEZpbGVzL2NhbGdhcnlfZmlzaG5ldC5zaHAiLCBnZW9tZXRyeT89P1RSVUUpDQojc3Rfd3JpdGUoYm9pc2VfZmlzaG5ldCwgIkM6L1VzZXJzL0xpbmRzZXkvRGVza3RvcC9DUExONjc1X0xhbmRfVXNlX01vZGVsaW5nX0Rlc2t0b3AvTWlkdGVybS9EZXJpdmVkIEZpbGVzL2JvaXNlX2Zpc2huZXQuc2hwIiwgZ2VvbWV0cnk/PT9UUlVFKQ0KYGBgDQoNCiMjIyBEZXBlbmRlbnQgVmFyaWFibGU6IEZsb29kaW5nIE1hcHMNCg0KTm93LCB3ZSB2aXN1YWxpemUgb3VyIHR3byBnZW9ncmFwaGllcyB3aXRoIHRoZWlyIG91dGxheXMgb2YgaGlzdG9yaWNhbA0KZmxvb2RpbmcuIENvbnNpZGVyaW5nIGhpc3RvcmljYWwgZmxvb2RpbmcgaXMgdGhlIGRlcGVuZGVudCB2YXJpYWJsZQ0Kd2hpY2ggYWxsIG9mIG91ciBmYWN0b3JzIGluIG91ciBtb2RlbCBhcmUgdHJ5aW5nIHRvIHByZWRpY3QsIGl0IGlzIGdvb2QNCnRvIGZpcnN0IHVuZGVyc3RhbmQgd2hlcmUgZmxvb2Rpbmcgb2NjdXJzIGFzIGEgYmFzZWxpbmUgb2Ygb3VyIG1vZGVsJ3MNCnN1Y2Nlc3MuDQoNCmBgYHtyIGhpc3RfZmxvb2RpbmcsIGVjaG89VFJVRX0NCmdyaWQuYXJyYW5nZShuY29sID0gMiwNCiAgZ2dwbG90KCkgKw0KICAgIGxhYnModGl0bGU9IkNhbGdhcnksIEhpc3RvcmljYWwgRmxvb2RpbmcgYW5kIEh5ZHJvbG9neSIsDQogICAgICAgc3VidGl0bGU9IkNhbGdhcnk7IFllbGxvdyA9IEhpc3RvcmljYWwgZmxvb2Rpbmcgb2JzZXJ2ZWQiKSsNCiAgZ2VvbV9zZihkYXRhID0gY2FsZ2FyeV9ib3VuZGFyeSwNCiAgICAgICAgICAgY29sb3IgPSAiYmxhY2siKSsNCiAgICBnZW9tX3NmKGRhdGE9Y2FsZ2FyeV9tYXN0ZXJfZmlzaCAgJT4lIA0KICAgICAgICAgICAgICAgZmlsdGVyKGhpc3RfZmxvb2QgPT0gNiksDQogICAgICAgICAgIGZpbGw9InllbGxvdyIsYWxwaGEgPSAwLjYsY29sb3I9J3RyYW5zcGFyZW50JykgKw0KICAjIGdlb21fc2YoZGF0YT1jYWxnYXJ5X2h5ZHJvbG9neSwNCiAgICMgICAgICAgIGZpbGwgPSAnbGlnaHQgYmx1ZScpKw0KICAgIG1hcFRoZW1lLA0KICANCiAgZ2dwbG90KCkgKw0KICAgIGdlb21fc2YoZGF0YSA9IGJvaXNlX2JvdW5kYXJ5LA0KICAgICAgICAgICBjb2xvciA9ICJibGFjayIpKw0KICAgIGdlb21fc2YoZGF0YT1ib2lzZV9tYXN0ZXJfZmlzaCAgJT4lIA0KICAgICAgICAgICAgICAgZmlsdGVyKGhpc3RfZmxvb2QgPT0gNiksDQogICAgICAgICAgIGZpbGw9InllbGxvdyIsYWxwaGEgPSAwLjYsY29sb3I9J3RyYW5zcGFyZW50JykgKw0KICAgI2dlb21fc2YoZGF0YT1ib2lzZV9oeWRyb2xvZ3kxLA0KICAgICMgICAgICAgZmlsbCA9ICdsaWdodCBibHVlJykrDQogICAgIyBnZW9tX3NmKGRhdGE9Ym9pc2VfaHlkcm9sb2d5MiwNCiAgICAgIyAgICAgZmlsbCA9ICdsaWdodCBibHVlJykrDQogICAgbWFwVGhlbWUrDQogIGxhYnModGl0bGU9IkJvaXNlLCBIaXN0b3JpY2FsIEZsb29kaW5nIGFuZCBIeWRyb2xvZ3kiLA0KICAgICAgIHN1YnRpdGxlPSJCb2lzZTsgWWVsbG93ID0gSGlzdG9yaWNhbCBmbG9vZGluZyBvYnNlcnZlZCIpKQ0KYGBgDQoNCiMjIyBJbXBvcnRpbmcgT3RoZXIgRmlsZXMgZm9yIEZhY3RvcnMgYW5kIE1hcCBFbGVtZW50cw0KDQpIZXJlLCB3ZSBpbXBvcnQgb3VyIHNwYXRpYWwgZGF0YSB0byBtYXAgb3VyIGZhY3RvcnMgYW5kIHRyYWluIG91ciBtb2RlbC4NCk1vc3Qgb2YgdGhpcyBkYXRhIHdhcyBjbGVhbmVkLCBkZXJpdmVkLCBhbmQgYXNzZW1ibGVkIHVzaW5nIEFyY0dJUyBQcm8uDQoNCmBgYHtyIGZpc2hfc2hwcywgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgaW5jbHVkZT1GQUxTRSwgcmVzdWx0cz0naGlkZSd9DQoNCg0KI1NPSUwgRFJBSU5BR0UNCmNhbGdhcnlfZHJhaW5fZmlzaCA8LSBzdF9yZWFkKCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vYW5hLW9zby9DYWxnYXJ5dkJvaXNlLzBhYjRhYWRmOWU3ZDgzNDA5YWVmZDVhYzhkYWIzZTY1MTUyNDBiMWYvZ2VvanNvbnMvY2FsZ2FyeV9nZW9qc29ucy9jYWxnYXJ5X2dlb2pzb25zL0NBTEdBUllfRFJBSU5BR0UuZ2VvanNvbiIpDQogIGNhbGdhcnlfZHJhaW5fZmlzaCA8LSBjYWxnYXJ5X2RyYWluX2Zpc2ggJT4lIA0KICAgIHN0X3RyYW5zZm9ybShjcnMgPSAzMzk1KQ0KICANCmJvaXNlX2RyYWluX2Zpc2ggPC0gc3RfcmVhZCgiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2FuYS1vc28vQ2FsZ2FyeXZCb2lzZS8wYWI0YWFkZjllN2Q4MzQwOWFlZmQ1YWM4ZGFiM2U2NTE1MjQwYjFmL2dlb2pzb25zL2JvaXNlX2dlb2pzb25zL2JvaXNlX2dlb2pzb25zL0JPSVNFX0RSQUlOQUdFLmdlb2pzb24iKQ0KICBib2lzZV9kcmFpbl9maXNoIDwtIGJvaXNlX2RyYWluX2Zpc2ggJT4lIA0KICAgIHN0X3RyYW5zZm9ybShjcnMgPSAzMzk1KQ0KICAgIA0KI0ZMT1cgQUNDVU1VTEFUSU9OIC0tIFVTRVMgTUFTVEVSIEZJTEUhIQ0KY2FsZ2FyeV9mbG93X2Zpc2ggPC0gc3RfcmVhZCgiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2FuYS1vc28vQ2FsZ2FyeXZCb2lzZS8wYWI0YWFkZjllN2Q4MzQwOWFlZmQ1YWM4ZGFiM2U2NTE1MjQwYjFmL2dlb2pzb25zL2NhbGdhcnlfZ2VvanNvbnMvY2FsZ2FyeV9nZW9qc29ucy9DQUxHQVJZX01BU1RFUkZJU0guZ2VvanNvbiIpDQogIGNhbGdhcnlfZmxvd19maXNoIDwtIGNhbGdhcnlfZmxvd19maXNoICU+JSANCiAgICBzdF90cmFuc2Zvcm0oY3JzID0gMzM5NSkNCg0KYm9pc2VfZmxvd19maXNoIDwtIHN0X3JlYWQoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9hbmEtb3NvL0NhbGdhcnl2Qm9pc2UvMGFiNGFhZGY5ZTdkODM0MDlhZWZkNWFjOGRhYjNlNjUxNTI0MGIxZi9nZW9qc29ucy9ib2lzZV9nZW9qc29ucy9ib2lzZV9nZW9qc29ucy9CT0lTRV9GTE9XX0FDQ1UuZ2VvanNvbiIpDQoNCiNJTVBFUlZJT1VTIHYuIFBFUlZJT1VTIFNVUkZBQ0VTDQpjYWxnYXJ5X3N1cmZfZmlzaCA8LSBzdF9yZWFkKCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vYW5hLW9zby9DYWxnYXJ5dkJvaXNlLzBhYjRhYWRmOWU3ZDgzNDA5YWVmZDVhYzhkYWIzZTY1MTUyNDBiMWYvZ2VvanNvbnMvY2FsZ2FyeV9nZW9qc29ucy9jYWxnYXJ5X2dlb2pzb25zL0NBTEdBUllfU1VSRkFDRS5nZW9qc29uIikNCiAgY2FsZ2FyeV9zdXJmX2Zpc2ggPC0gY2FsZ2FyeV9zdXJmX2Zpc2ggJT4lIA0KICAgIHN0X3RyYW5zZm9ybShjcnMgPSAzMzk1KSANCg0KYm9pc2Vfc3VyZl9maXNoIDwtIHN0X3JlYWQoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9hbmEtb3NvL0NhbGdhcnl2Qm9pc2UvMGFiNGFhZGY5ZTdkODM0MDlhZWZkNWFjOGRhYjNlNjUxNTI0MGIxZi9nZW9qc29ucy9ib2lzZV9nZW9qc29ucy9ib2lzZV9nZW9qc29ucy9CT0lTRV9TVVJGQUNFLmdlb2pzb24iKQ0KDQojRElTVEFOQ0UgVE8gV0FURVIgQk9EWQ0KY2FsZ2FyeV9kaXN0aDJvX2Zpc2ggPC0gc3RfcmVhZCgiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2FuYS1vc28vQ2FsZ2FyeXZCb2lzZS8wYWI0YWFkZjllN2Q4MzQwOWFlZmQ1YWM4ZGFiM2U2NTE1MjQwYjFmL2dlb2pzb25zL2NhbGdhcnlfZ2VvanNvbnMvY2FsZ2FyeV9nZW9qc29ucy9DQUxHQVJZX0RJU1RfVE9fV0FURVIuZ2VvanNvbiIpDQogIGNhbGdhcnlfZGlzdGgyb19maXNoIDwtIGNhbGdhcnlfZGlzdGgyb19maXNoICU+JSANCiAgICBzdF90cmFuc2Zvcm0oY3JzID0gMzM5NSkNCiAgDQpib2lzZV9kaXN0aDJvX2Zpc2ggPC0gc3RfcmVhZCgiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2FuYS1vc28vQ2FsZ2FyeXZCb2lzZS8wYWI0YWFkZjllN2Q4MzQwOWFlZmQ1YWM4ZGFiM2U2NTE1MjQwYjFmL2dlb2pzb25zL2JvaXNlX2dlb2pzb25zL2JvaXNlX2dlb2pzb25zL0JPSVNFX0RJU1RfVE9fV0FURVIuZ2VvanNvbiIpDQpgYGANCg0KIyBNYXBwaW5nDQoNCiMjIyMgQ2FsZ2FyeQ0KDQpIZXJlLCB3ZSBjcmVhdGUgbWFwcyBvZiBvdXIgZm91ciBmYWN0b3JzIGZvciBvdXIgYmFzZSBnZW9ncmFwaHksDQpDYWxnYXJ5Lg0KDQpgYGB7ciBjYWxnYXJ5X2ZhY3Rvcl9tYXBzLCBlY2hvPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBpbWdfd2lkdGggPSAxMCB9DQojIyNDYWxnYXJ5DQogICNTT0lMIERSQUlOQUdFIC0tIEdvb2QNCiAgZ2dwbG90KCkgKw0KICAgIGdlb21fc2YoZGF0YSA9IGNhbGdhcnlfYm91bmRhcnksDQogICAgICAgICAgIGNvbG9yID0gImJsYWNrIikrDQogICAgZ2VvbV9zZihkYXRhID0gY2FsZ2FyeV9kcmFpbl9maXNoLA0KICAgICAgICAgICAgYWVzKGZpbGwgPSBEcmFpbmFnZVJhKSwNCiAgICAgICAgICAgIGNvbG9yID0gInRyYW5zcGFyZW50IikrDQogICAgc2NhbGVfZmlsbF92aXJpZGlzKCdTb2lsIERyYWluYWdlIEVmZmljYWN5ICg2ID0gYmVzdCknLA0KICAgICAgICAgICAgICAgICAgICAgICBkaXJlY3Rpb24gPSAtMSwgIyBEaXJlY3Rpb24gb2YgdGhlIGNvbG9yIHBhbGV0dGUgLSBlaXRoZXIgMSBvciAtMQ0KICAgICAgICAgICAgICAgICAgICAgICBvcHRpb24gPSAnRicsICAjIFZpcmlkaXMgaGFzIG11bHRpcGxlIHBhbGV0dGVzIHRvIGNob29zZSBmcm9tDQogICAgICAgICAgICAgICAgICAgICAgIGFscGhhID0gMC42KSsgIA0KICAgIGxhYnModGl0bGU9IlNvaWwgVHlwZSAmIERyYWluYWdlIikgKw0KICAgIG1hcFRoZW1lDQogDQoNCmBgYA0KDQpgYGB7cn0NCiNJTVBFUlZJT1VTIHYuIFBFUlZJT1VTIFNVUkZBQ0VTIC0tIEdvb2QNCmdncGxvdCgpICsNCiAgZ2VvbV9zZihkYXRhID0gY2FsZ2FyeV9ib3VuZGFyeSwNCiAgICAgICAgICAgY29sb3IgPSAiYmxhY2siKSsNCiAgZ2VvbV9zZihkYXRhID0gY2FsZ2FyeV9zdXJmX2Zpc2gsDQogICAgICAgICAgYWVzKGZpbGwgPSBQZXJ2aW91cyksDQogICAgICAgICAgY29sb3IgPSAidHJhbnNwYXJlbnQiKSsNCiAgc2NhbGVfZmlsbF92aXJpZGlzKCdTdXJmYWNlIFBlcm1lYWJpbGl0eSAoNiA9IGxlYXN0KScsDQogICAgICAgICAgICAgICAgICAgICBkaXJlY3Rpb24gPSAtMSwgIyBEaXJlY3Rpb24gb2YgdGhlIGNvbG9yIHBhbGV0dGUgLSBlaXRoZXIgMSBvciAtMQ0KICAgICAgICAgICAgICAgICAgICAgb3B0aW9uID0gJ0YnLCAgIyBWaXJpZGlzIGhhcyBtdWx0aXBsZSBwYWxldHRlcyB0byBjaG9vc2UgZnJvbQ0KICAgICAgICAgICAgICAgICAgICAgYWxwaGEgPSAwLjYpKyAgDQogIGxhYnModGl0bGU9IlN1cmZhY2UgUGVybWVhYmlsaXR5IikgKw0KICBtYXBUaGVtZQ0KDQpgYGANCg0KYGBge3J9DQoNCiNESVNUQU5DRSBUTyBXQVRFUiBCT0RZIC0tIEdvb2QNCmdncGxvdCgpICsNCiAgZ2VvbV9zZihkYXRhID0gY2FsZ2FyeV9ib3VuZGFyeSwNCiAgICAgICAgICBjb2xvciA9ICJibGFjayIpKw0KICBnZW9tX3NmKGRhdGEgPSBjYWxnYXJ5X2Rpc3RoMm9fZmlzaCwNCiAgICAgICAgICBhZXMoZmlsbCA9IGdyaWRjb2RlKSwNCiAgICAgICAgICBjb2xvciA9ICJ0cmFuc3BhcmVudCIpKw0KICBzY2FsZV9maWxsX3ZpcmlkaXMoJ0Rpc3RhbmNlIHRvIFdhdGVyICg2ID0gY2xvc2VzdCknLA0KICAgICAgICAgICAgICAgICAgICAgZGlyZWN0aW9uID0gLTEsICMgRGlyZWN0aW9uIG9mIHRoZSBjb2xvciBwYWxldHRlIC0gZWl0aGVyIDEgb3IgLTENCiAgICAgICAgICAgICAgICAgICAgIG9wdGlvbiA9ICdGJywNCiAgICAgICAgICAgICAgICAgICAgIGFscGhhID0gMC42KSsgIA0KICBsYWJzKHRpdGxlPSJEaXN0YW5jZSB0byBXYXRlciBCb2R5IikgKw0KICBtYXBUaGVtZQ0KDQpgYGANCg0KYGBge3J9DQoNCiAgI0ZMT1cgQUNDVU1VTEFUSU9OIC0tIGdvb2QNCiAgZ2dwbG90KCkgKw0KICAgIGdlb21fc2YoZGF0YSA9IGNhbGdhcnlfYm91bmRhcnksDQogICAgICAgICAgICBjb2xvciA9ICJibGFjayIpKw0KICAgIGdlb21fc2YoZGF0YSA9IGNhbGdhcnlfZmxvd19maXNoLA0KICAgICAgICAgICAgYWVzKGZpbGwgPSBGbG93QWNjKSwNCiAgICAgICAgICAgIGNvbG9yID0gInRyYW5zcGFyZW50IikrDQogICAgc2NhbGVfZmlsbF92aXJpZGlzKCdGbG93IEFjY3VtdWxhdGlvbiAoNiA9IGdyZWF0ZXN0KScsDQogICAgICAgICAgICAgICAgICAgICAgIGRpcmVjdGlvbiA9IC0xLCANCiAgICAgICAgICAgICAgICAgICAgICAgb3B0aW9uID0gJ0YnLCANCiAgICAgICAgICAgICAgICAgICAgICAgYWxwaGEgPSAwLjYpKyAgDQogICAgbGFicyh0aXRsZT0iV2F0ZXIgRmxvdyBBY2N1bXVsYXRpb24iKSArDQogICAgbWFwVGhlbWUNCiAgDQpgYGANCg0KDQoNCg0KIyMjIyBCb2lzZQ0KDQpMaWtld2lzZSwgaGVyZSB3ZSB2aXN1YWxpemUgdGhlIHNhbWUgZm91ciBmYWN0b3JzIHdpdGhpbiBCb2lzZSdzDQpib3VuZGFyaWVzLg0KDQpgYGB7ciBib2lzZV9mYWN0b3JfbWFwcywgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyMjQm9pc2UNCiAgI1NPSUwgRFJBSU5BR0UgLS0gR29vZA0KICBnZ3Bsb3QoKSArDQogICBnZW9tX3NmKGRhdGEgPSBib2lzZV9ib3VuZGFyeSwNCiAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gInRyYW5zcGFyZW50IiwgY29sb3IgPSAiYmxhY2siKSsNCiAgICAgICAgICAgICAgICAgZ2VvbV9zZihkYXRhID0gYm9pc2VfZHJhaW5fZmlzaCwNCiAgICAgICAgICAgICAgICAgICAgICAgICBhZXMoZmlsbCA9IERyYWluUmFuayksDQogICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSAidHJhbnNwYXJlbnQiKSsNCiAgICAgICAgICAgICAgICAgc2NhbGVfZmlsbF92aXJpZGlzKCdTb2lsIERyYWluYWdlIEVmZmljYWN5ICg2ID0gYmVzdCknLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGlyZWN0aW9uID0gLTEsICMgRGlyZWN0aW9uIG9mIHRoZSBjb2xvciBwYWxldHRlIC0gZWl0aGVyIDEgb3IgLTENCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9wdGlvbiA9ICdGJywgICMgVmlyaWRpcyBoYXMgbXVsdGlwbGUgcGFsZXR0ZXMgdG8gY2hvb3NlIGZyb20NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFscGhhID0gMC42KSsgIA0KICAgICAgICAgICAgICAgICBsYWJzKHRpdGxlPSJTb2lsIFR5cGUgJiBEcmFpbmFnZSIpICsNCiAgICAgICAgICAgICAgICAgbWFwVGhlbWUNCmBgYA0KDQoNCmBgYHtyfQ0KICAgICAgICAgICAgICAgDQojRkxPVyBBQ0NVTVVMQVRJT04gLS0gZ29vZA0KICBnZ3Bsb3QoKSArDQogICBnZW9tX3NmKGRhdGEgPSBib2lzZV9ib3VuZGFyeSwNCiAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gInRyYW5zcGFyZW50IiwgY29sb3IgPSAiYmxhY2siKSsNCiAgICAgICAgICAgICAgICAgZ2VvbV9zZihkYXRhID0gYm9pc2VfZmxvd19maXNoLA0KICAgICAgICAgICAgICAgICAgICAgICAgIGFlcyhmaWxsID0gZ3JpZGNvZGUpLA0KICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gInRyYW5zcGFyZW50IikrDQogICAgICAgICAgICAgICAgIHNjYWxlX2ZpbGxfdmlyaWRpcygnRmxvdyBBY2N1bXVsYXRpb24gKDYgPSBncmVhdGVzdCknLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGlyZWN0aW9uID0gLTEsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3B0aW9uID0gJ0YnLCAgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbHBoYSA9IDAuNikrICANCiAgICAgICAgICAgICAgICAgbGFicyh0aXRsZT0iV2F0ZXIgRmxvdyBBY2N1bXVsYXRpb24iKSArDQogICAgICAgICAgICAgICAgIG1hcFRoZW1lDQoNCmBgYA0KDQpgYGB7cn0NCiAgICAgICAgICAgICAgIA0KI0lNUEVSVklPVVMgdi4gUEVSVklPVVMgU1VSRkFDRVMgLS0gZ29vZA0KICBnZ3Bsb3QoKSArDQogICAgZ2VvbV9zZihkYXRhID0gYm9pc2VfYm91bmRhcnksDQogICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSAiYmxhY2siKSsNCiAgICAgICAgICAgICAgICAgZ2VvbV9zZihkYXRhID0gYm9pc2Vfc3VyZl9maXNoLA0KICAgICAgICAgICAgICAgICAgICAgICAgIGFlcyhmaWxsID0gZ3JpZGNvZGUpLA0KICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gInRyYW5zcGFyZW50IikrDQogICAgICAgICAgICAgICAgIHNjYWxlX2ZpbGxfdmlyaWRpcygnU3VyZmFjZSBQZXJtZWFiaWxpdHkgKDYgPSBsZWFzdCknLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGlyZWN0aW9uID0gLTEsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3B0aW9uID0gJ0YnLCAgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbHBoYSA9IDAuNikrICANCiAgICAgICAgICAgICAgICAgbGFicyh0aXRsZT0iU3VyZmFjZSBQZXJtZWFiaWxpdHkiKSArDQogICAgICAgICAgICAgICAgIG1hcFRoZW1lDQoNCmBgYA0KDQpgYGB7cn0NCiAgICAgICAgICAgICAgIA0KI0RJU1RBTkNFIFRPIFdBVEVSIEJPRFkgLS0gZ29vZA0KZ2dwbG90KCkgKw0KICBnZW9tX3NmKGRhdGEgPSBib2lzZV9ib3VuZGFyeSwNCiAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9ICJibGFjayIpKw0KICAgICAgICAgICAgICAgICBnZW9tX3NmKGRhdGEgPSBib2lzZV9kaXN0aDJvX2Zpc2gsDQogICAgICAgICAgICAgICAgICAgICAgICAgYWVzKGZpbGwgPSBncmlkY29kZSksDQogICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSAidHJhbnNwYXJlbnQiKSsNCiAgICAgICAgICAgICAgICAgc2NhbGVfZmlsbF92aXJpZGlzKCdEaXN0YW5jZSB0byBXYXRlciAoNiA9IGNsb3Nlc3QpJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpcmVjdGlvbiA9IC0xLCAjIERpcmVjdGlvbiBvZiB0aGUgY29sb3IgcGFsZXR0ZSAtIGVpdGhlciAxIG9yIC0xDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvcHRpb24gPSAnRicsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbHBoYSA9IDAuNikrICANCiAgICAgICAgICAgICAgICAgbGFicyh0aXRsZT0iRGlzdGFuY2UgdG8gV2F0ZXIgQm9keSIpICsNCiAgICAgICAgICAgICAgICAgbWFwVGhlbWUNCmBgYA0KDQojIyMgRGF0YSBXcmFuZ2xpbmcNCg0KSGVyZSwgd2UgY2xlYW4gdGhlIHRhYnVsYXIgZGF0YSB1bmRlcmx5aW5nIHRoZSBzcGF0aWFsIGRhdGEgdG8gcHJlcGFyZQ0KaXQgZm9yIHJ1bm5pbmcgdGhyb3VnaCB0aGUgbW9kZWwuDQoNCmBgYHtyIGNhbGdhcnlfZGF0YV9tYW5pcHVsYXRpb24sIGluY2x1ZGU9RkFMU0V9DQpjYWxnYXJ5X21hc3Rlcl9maXNoIDwtIHN0X3JlYWQoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9hbmEtb3NvL0NhbGdhcnl2Qm9pc2UvMGFiNGFhZGY5ZTdkODM0MDlhZWZkNWFjOGRhYjNlNjUxNTI0MGIxZi9nZW9qc29ucy9jYWxnYXJ5X2dlb2pzb25zL2NhbGdhcnlfZ2VvanNvbnMvQ0FMR0FSWV9NQVNURVJGSVNILmdlb2pzb24iKQ0KDQogIGNhbGdhcnlfdmFycyA8LSANCiAgICBjYWxnYXJ5X21hc3Rlcl9maXNoICU+JSANCiAgICBkcGx5cjo6c2VsZWN0KGhpc3RfZmxvb2QsRHJhaW5SYW5rLEZsb3dBY2MsUGVydmlvdXMsZGlzdGgyb19ncixPQkpFQ1RJRCkNCiAgDQogIGNhbGdhcnlfdmFycyA8LSBjYWxnYXJ5X3ZhcnMgJT4lIG11dGF0ZShoaXN0X2Zsb29kPXJlY29kZShoaXN0X2Zsb29kLCAnMSc9JzAnLCAnNic9JzEnKSkNCiAgY2FsZ2FyeV92YXJzIDwtIGNhbGdhcnlfdmFycyAlPiUgbXV0YXRlKGhpc3RfZmxvb2QgPSBhcy5udW1lcmljKGhpc3RfZmxvb2QpKQ0KICANCmBgYA0KDQojIyMgUGxvdHRpbmcNCg0KSW4gdGhlc2UgcGxvdHMsIHdlIHNlZSBob3cgb3VyIGZhY3RvcnMgaW4gQ2FsZ2FyeSBhdmVyYWdlIG91dCBpbiBhcmVhcw0Kd2l0aGluIHRoZSBjaXR5IHRoYXQgaGF2ZSBmbG9vZGluZyB2ZXJzdXMgdGhvc2UgdGhhdCBoYXZlIG5vdCBmbG9vZGVkLg0KV2Ugbm90aWNlIHRoYXQgYXJlYXMgdGhhdCBoYXZlIHNvaWwgd2l0aCBiZXR0ZXIgZHJhaW5hZ2UgZWZmaWNhY3kNCihEcmFpblJhbmspIGhhdmUgYmVlbiBtb3JlIGxpa2VseSB0byBub3QgZmxvb2QuIExpa2V3aXNlLCBhcmVhcyB3aXRoDQpsb3RzIG9mIGZsb3cgYWNjdW11bGF0aW9uIGFyZSBtb3JlIGxpa2VseSB0byBmbG9vZCB0aGFuIGFyZWFzIHdpdGgNCm1pbmltYWwgYWNjdW11bGF0aW9uLg0KDQpgYGB7ciBmbG9vZF9vcl9ub3RfcGxvdHMsIGluY2x1ZGU9RkFMU0V9DQogIGNhbGdhcnlfcGxvdCA8LQ0KICAgIGNhbGdhcnlfdmFycyAlPiUgDQogICAgYXMuZGF0YS5mcmFtZSgpICU+JSANCiAgICBkcGx5cjo6c2VsZWN0KGhpc3RfZmxvb2QsRHJhaW5SYW5rLEZsb3dBY2MsUGVydmlvdXMsZGlzdGgyb19ncikgJT4lIA0KICAgICAgZ2F0aGVyKHZhcmlhYmxlLCB2YWx1ZSwgLWhpc3RfZmxvb2QpDQogDQogICMgNiA9IHByZXZpb3VzbHkgZmxvb2RlZCA7IDEgPSBub3QgZmxvb2RlZA0KICAgICAgIGdncGxvdChjYWxnYXJ5X3Bsb3QgJT4lDQogICAgICAgICAgICAgICAgZ3JvdXBfYnkoaGlzdF9mbG9vZCwgdmFyaWFibGUpICU+JQ0KICAgICAgICAgICAgICAgIHN1bW1hcml6ZShtZWFuID0gbWVhbih2YWx1ZSkpKSArIA0KICAgICAgICAgZ2VvbV9iYXIoYWVzKGFzLmZhY3RvcihoaXN0X2Zsb29kKSwgDQogICAgICAgICAgICAgICAgICAgICAgbWVhbiwgDQogICAgICAgICAgICAgICAgICAgICAgZmlsbD1hcy5mYWN0b3IoaGlzdF9mbG9vZCkpLA0KICAgICAgICAgICAgICAgICAgc3RhdD0iaWRlbnRpdHkiKSArIA0KICAgICAgICAgZmFjZXRfd3JhcCh+dmFyaWFibGUsIG5jb2w9NSwgc2NhbGVzID0gImZyZWUiKSArDQogICAgICAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCIjNjY5OTk5IiwgIiMwMENDQ0MiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIk5vdCBGbG9vZGVkIiwiRmxvb2RlZCIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICIiKSArDQogICAgICAgICBsYWJzKHg9IkZsb29kZWQgb3IgTm90IiwgeT0iVmFsdWUiKQ0KYGBgDQoNCiMgTW9kZWwgQnVpbGRpbmcNCg0KSGVyZSwgd2Ugd2FsayB0aHJvdWdoIHRoZSBzdGVwcyBvZiBidWlsZGluZyBhbmQgdHJhaW5pbmcgb3VyIG1vZGVsLCBhbmQNCnRoZW4gYXBwbHlpbmcgdGhhdCBtb2RlbCB0byBvdXIgb3RoZXIgZ2VvZ3JhcGh5LCBCb2lzZS4NCg0KYGBge3IgbW9kZWxfdHJhaW4sIGluY2x1ZGU9RkFMU0V9DQoNCnRyYWluSW5kZXgxIDwtIGNyZWF0ZURhdGFQYXJ0aXRpb24oY2FsZ2FyeV92YXJzJGhpc3RfZmxvb2QsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHAgPSAuNzAsDQpsaXN0ID0gRkFMU0UsDQp0aW1lcyA9IDEpDQogICAgICAgDQogICAgICAgY2FsZ2FyeVRyYWluMSA8LSBjYWxnYXJ5X3ZhcnNbIHRyYWluSW5kZXgxLF0NCiAgICAgICBjYWxnYXJ5VGVzdDEgIDwtIGNhbGdhcnlfdmFyc1stdHJhaW5JbmRleDEsXQ0KYGBgDQoNCiMjIyBCaW5vbWlhbCBNb2RlbA0KDQpIZXJlLCB3ZSBlbXBsb3kgYSBnbG0gbGluZWFyIG1vZGVsLCB3aGljaCBmaW5kcyB0aGF0IHNvaWwgZHJhaW5hZ2UNCmVmZmljYWN5ICgiRHJhaW5SYW5rIikgYW5kIEZsb3cgQWNjdW11bGF0aW9uICgiRmxvd0FjYyIpIGFyZSBleHRyZW1lbHkNCnN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgaW4gdGhlaXIgcmVsYXRpb25zaGlwIHRvIENhbGdhcnkncyBoaXN0b3JpY2FsDQpmbG9vZGluZy4gRGlzdGFuY2UgdG8gd2F0ZXIgYm9keSAoImRpc3RoMm9fZ3IiKSB3YXMgYWxzbyBmb3VuZCB0byBiZQ0Kc3RhdGlzdGljYWxseSBzaWduaWZpY2FudC4gU3VyZmFjZSBwZXJtZWFiaWxpdHkgKCJQZXJ2aW91cyIpLCBob3dldmVyLA0KZGlkIG5vdCBoYXZlIGEgdmVyeSBzdHJvbmcgcmVsYXRpb25zaGlwIHdpdGggb2JzZXJ2ZWQgZmxvb2RpbmcgaW4NCkNhbGdhcnkuDQoNCmBgYHtyIGJpbm9taWFsLCBlY2hvPVRSVUV9DQoNCg0KY2FsZ2FyeU1vZGVsIDwtIGdsbShoaXN0X2Zsb29kIH4gRHJhaW5SYW5rICsgRmxvd0FjYyArIFBlcnZpb3VzICsgZGlzdGgyb19nciwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYW1pbHk9ImJpbm9taWFsIihsaW5rPSJsb2dpdCIpLCBkYXRhID0gY2FsZ2FyeVRyYWluMSAlPiUNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcy5kYXRhLmZyYW1lKCkgJT4lIGRwbHlyOjpzZWxlY3QoLWdlb21ldHJ5LC1PQkpFQ1RJRCxoaXN0X2Zsb29kLERyYWluUmFuayxGbG93QWNjLFBlcnZpb3VzLGRpc3RoMm9fZ3IpKSAgICAgICAgICAgICAgICAgICAgIA0KDQpzdW1tYXJ5KGNhbGdhcnlNb2RlbCkNCg0KYGBgDQoNCiMjIyBNb2RlbCBWYWxpZGF0aW9uDQoNClRoZSBpdGVtcyBiZWxvdyBpbnRlcnJvZ2F0ZSBob3cgc3VjY2Vzc2Z1bCBvdXIgbW9kZWwgaXMgYXQgcHJlZGljdGluZw0KQ2FsZ2FyeSdzIGZsb29kaW5nLCB1c2luZyBzZXZlcmFsIGRpZmZlcmVudCB2aXN1YWxpemF0aW9ucyBhbmQNCnN0YXRpc3RpY2FsIGV2YWx1YXRpb25zLiBJbiBib3RoIHRoZSBoaXN0b2dyYW0gYW5kIGZpbGxlZCBsaW5lIGdyYXBoLA0Kb3VyIHByZWRpY3Rpb25zIGFyZSB0aGF0IGZsb29kZWQgYXJlYXMgaGF2ZSBhYm91dCBhIC41IGNoYW5jZSBvZg0KZmxvb2RpbmcsIHdoaWxlIHRoZXJlIGlzIG1vcmUgbnVhbmNlIGZvciBhcmVhcyB0aGF0IGRvIG5vdCBoYXZlDQpoaXN0b3JpY2FsIGZsb29kaW5nLg0KDQpgYGB7ciB2YWxpZGF0aW9uLCBlY2hvPVRSVUV9DQoNCmNsYXNzUHJvYnMgPC0gcHJlZGljdChjYWxnYXJ5TW9kZWwsIGNhbGdhcnlUZXN0MSwgdHlwZT0icmVzcG9uc2UiKQ0KDQpoaXN0KGNsYXNzUHJvYnMpDQoNCnRlc3RQcm9icyA8LSBkYXRhLmZyYW1lKG9icyA9IGFzLm51bWVyaWMoY2FsZ2FyeVRlc3QxJGhpc3RfZmxvb2QpLA0KICAgICAgICAgICAgICAgICAgICAgICAgcHJlZCA9IGNsYXNzUHJvYnMpDQoNCmdncGxvdCh0ZXN0UHJvYnMsIGFlcyh4ID0gcHJlZCwgZmlsbD1hcy5mYWN0b3Iob2JzKSkpICsgDQogIGdlb21fZGVuc2l0eSgpICsNCiAgZmFjZXRfZ3JpZChvYnMgfiAuKSArIA0KICB4bGFiKCJQcm9iYWJpbGl0eSIpICsgDQogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IC41KSArDQogICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIiM2Njk5OTkiLCAiIzAwQ0NDQyIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygiTm90IEZsb29kZWQiLCJGbG9vZGVkIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICBuYW1lID0gIiIpICsNCiAgICBwbG90VGhlbWUNCiAgDQoNCmBgYA0KDQojIyMgQ29uZnVzaW9uIE1ldHJpY3MNCg0KVGhlIGNvbmZ1c2lvbiBtZXRyaWNzIHByZWRpY3QgYSBoaWdoIGRlZ3JlZSBvZiBhY2N1cmFjeSB3aXRoIG91ciBDYWxnYXJ5DQptb2RlbCwgd2hpbGUgc3RpbGwgZ2VuZXJhdGluZyBmYWxzZSBwb3NpdGl2ZXMgYW5kIGZhbHNlIG5lZ2F0aXZlcy4NCg0KYGBge3IgY29uZnVzaW9uX21hdHJpeCwgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KdGVzdFByb2JzJHByZWRDbGFzcyAgPSBpZmVsc2UodGVzdFByb2JzJHByZWQgPiAuMDUgLDEsMCkNCiNwcmVkaWN0aW5nIHRoYXQgaWYgaXRzIGdyZWF0ZXIgdGhhbiA1MCUgaXRzIGEgMSwgbGVzcyBpcyAwDQoNCmNhcmV0Ojpjb25mdXNpb25NYXRyaXgocmVmZXJlbmNlID0gYXMuZmFjdG9yKHRlc3RQcm9icyRvYnMpLCANCiAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IGFzLmZhY3Rvcih0ZXN0UHJvYnMkcHJlZENsYXNzKSwgDQogICAgICAgICAgICAgICAgICAgICAgIHBvc2l0aXZlID0gIjEiKQ0KDQoNCg0KIyMgSG93IGl0IHdvcmtzOg0KICAjICoqUHJlZGljdGVkID0gMCwgT2JzZXJ2ZWQgPSAwIC0+IFRydWUgTmVnYXRpdmUqKg0KICAjICoqUHJlZGljdGVkID0gMSwgT2JzZXJ2ZWQgPSAxIC0+IFRydWUgUG9zaXRpdmUqKg0KICAjICAqKlByZWRpY3RlZCA9IDEsIE9ic2VydmVkID0gMCAtPiBGYWxzZSBQb3NpdGl2ZSoqDQogICMgICoqUHJlZGljdGVkID0gMCwgT2JzZXJ2ZWQgPSAxIC0+IEZhbHNlIE5lZ2F0aXZlKioNCiAgICAjICoqMS4gU2Vuc2l0aXZpdHkgLSB0aGUgcHJvcG9ydGlvbiBvZiBhY3R1YWwgcG9zaXRpdmVzICgxJ3MpIHRoYXQgd2VyZSBwcmVkaWN0ZWQgdG8gYmUgcG9zaXRpdmUuIEFsc28ga25vd24gYXMgInRydWUgcG9zaXRpdmUgcmF0ZSIuKioNCiAgICAjICoqMi4gU3BlY2lmaWNpdHkgLSBUaGUgcHJvcG9ydGlvbiBvZiBhY3R1YWwgbmVnYXRpdmVzICgwJ3MpIHRoYXQgd2VyZSBwcmVkaWN0ZWQgdG8gYmUgbmVnYXRpdmVzLiBBbHNvIGtub3duIGFzICJ0cnVlIG5lZ2F0aXZlIHJhdGUiLioqDQpgYGANCg0KIyMjIFJPQyBDdXJ2ZQ0KDQpHaXZlbiB0aGF0IHRoZSBST0MgQ3VydmUgaXMgbm90IGJvd2VkIG91dCB0byB0aGUgdG9wIGFuZCBsZWZ0IGVkZ2VzLCB0aGUNCm1vZGVsIGlzIG5vdCB0cmVtZW5kb3VzbHkgb3Zlci1maXQuDQoNCmBgYHtyIHJvY19jdXJ2ZSwgZWNobz1GQUxTRX0NCg0KZ2dwbG90KHRlc3RQcm9icywgYWVzKGQgPSBvYnMsIG0gPSBwcmVkKSkgKyANCiAgZ2VvbV9yb2Mobi5jdXRzID0gNTAsIGxhYmVscyA9IEZBTFNFKSArIA0KICBzdHlsZV9yb2ModGhlbWUgPSB0aGVtZV9ncmV5KSArDQogIGdlb21fYWJsaW5lKHNsb3BlID0gMSwgaW50ZXJjZXB0ID0gMCwgc2l6ZSA9IDEuNSwgY29sb3IgPSAnZ3JleScpIA0KYGBgDQoNCmBgYHtyIGF1YywgZWNobz1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmF1Yyh0ZXN0UHJvYnMkb2JzLCB0ZXN0UHJvYnMkcHJlZCkNCmBgYA0KDQojIyMgQ3Jvc3MgVmFsaWRhdGlvbg0KDQpDcm9zcyB2YWxpZGF0aW9uIHRlc3RzIHRoZSBzdHJlbmd0aCBvZiBvdXIgbW9kZWwgb24gc2FtcGxlIGRhdGEuIFdlIHRlc3QNCm91ciBtb2RlbCBvbiAxMDAgdGVzdCBzZXRzLCBhbmQgdGhlbiB3ZSBlc3RpbWF0ZSB0aGUgc3RyZW5ndGggb2Ygb3VyDQptb2RlbC4NCg0KYGBge3Iga19mb2xkLCBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpjdHJsPC0gdHJhaW5Db250cm9sKG1ldGhvZCA9ICJjdiIsIA0KICAgICAgICAgICAgICAgICAgICAgbnVtYmVyID0gMTAwLCANCiAgICAgICAgICAgICAgICAgICAgIHAgPSAwLjcsIA0KICAgICAgICAgICAgICAgICAgICAgc2F2ZVByZWRpY3Rpb25zID0gVFJVRSkNCg0KY3ZGaXQgPC0gdHJhaW4oYXMuZmFjdG9yKGhpc3RfZmxvb2QpIH4gLiwgIGRhdGEgPQ0KICAgICAgICAgICAgICAgICBjYWxnYXJ5X3ZhcnMgJT4lIA0KICAgICAgICAgICAgICAgICBhcy5kYXRhLmZyYW1lKCkgJT4lIA0KICAgICAgICAgICAgICBkcGx5cjo6c2VsZWN0KGhpc3RfZmxvb2QsRHJhaW5SYW5rLEZsb3dBY2MsUGVydmlvdXMsZGlzdGgyb19nciksIA0KICAgICAgICAgICAgICAgbWV0aG9kPSJnbG0iLCBmYW1pbHk9ImJpbm9taWFsIiwNCiAgICAgICAgICAgICAgIHRyQ29udHJvbCA9IGN0cmwpDQoNCmN2Rml0DQoNCmBgYA0KDQpUaGUgaGlzdG9ncmFtIG9mIG91ciBjcm9zcyB2YWxpZGF0aW9uIHNob3dzIHRoYXQgYWxsIHRlc3RzIG9mIG91ciBtb2RlbA0KYXJlIGhpZ2hseSBhY2N1cmF0ZS4NCg0KYGBge3IgY3ZfaGlzdCwgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KZ2dwbG90KGFzLmRhdGEuZnJhbWUoY3ZGaXQkcmVzYW1wbGUpLCBhZXMoQWNjdXJhY3kpKSArIA0KICBnZW9tX2hpc3RvZ3JhbSgpICsNCiAgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cyA9IGMoMCwgMSkpICsNCiAgbGFicyh4PSJBY2N1cmFjeSIsDQogICAgICAgeT0iQ291bnQiKSsNCiAgcGxvdFRoZW1lDQpgYGANCg0KIyMjIEF0dGFjaGluZyBQcmVkaWN0aW9ucyB0byBDYWxnYXJ5IE1vZGVsDQoNCkluIHRoZSBmaW5hbCBzdGVwIG9mIGJ1aWxkaW5nIG91ciBDYWxnYXJ5IG1vZGVsLCB3ZSBhdHRhY2ggb3VyIG1vZGVsJ3MNCnByZWRpY3Rpb25zIHRvIG91ciBvcmlnaW5hbCBDYWxnYXJ5IGZpc2huZXQuLi4NCg0KYGBge3IgcHJlZGljdF93aG9sZSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgaW5jbHVkZT1GQUxTRX0NCg0KYWxsUHJlZGljdGlvbnMgPC0gDQogIHByZWRpY3QoY3ZGaXQsIGNhbGdhcnlfdmFycywgdHlwZT0icHJvYiIpWywyXQ0KDQpjYWxnYXJ5X3ZhcnMgPC0gDQogIGNiaW5kKGNhbGdhcnlfdmFycyxhbGxQcmVkaWN0aW9ucykgJT4lDQogIG11dGF0ZShhbGxQcmVkaWN0aW9ucyA9IHJvdW5kKGFsbFByZWRpY3Rpb25zICogMTAwKSkgDQoNCg0KY2FsZ2FyeTEgPC0gY2FsZ2FyeV92YXJzICU+JQ0KIG11dGF0ZShQcmVkQ2xhc3MgPSBpZmVsc2UoYWxsUHJlZGljdGlvbnMgPiAxMCwgMSwgMCkpDQoNCmNhbGdhcnkxIDwtIGNhbGdhcnkxICU+JQ0KIG11dGF0ZShDb3JyZWN0ID0gaWZlbHNlKFByZWRDbGFzcyA9PSBoaXN0X2Zsb29kLCAiMSIsICIwIiksDQogICAgICAgICBJbmNvcnJlY3QgPSBpZmVsc2UoUHJlZENsYXNzICE9IGhpc3RfZmxvb2QsICIxIiwgIjAiKSkNCg0KYGBgDQoNCiMjIyBWaXN1YWxpemluZyBDYWxnYXJ5IFByZWRpY3Rpb25zDQoNCi4uLmFuZCB3ZSB2aXN1YWxpemUgb3VyIG1vZGVsJ3MgcHJlZGljdGlvbiBvZiBmbG9vZGluZyBhZ2FpbnN0IHRoZQ0KaGlzdG9yaWNhbCBkYXRhIG9mIG9ic2VydmVkIGZsb29kaW5nLg0KDQpgYGB7ciBwcmVkaWN0ZWRfbWFwMSwgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KZ2dwbG90KCkgKyANCiAgZ2VvbV9zZihkYXRhPWNhbGdhcnkxLCBhZXMoZmlsbD1mYWN0b3IobnRpbGUoYWxsUHJlZGljdGlvbnMsNSkpKSwgDQogICAgICAgICAgY29sb3VyPU5BKSArDQogICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9YygiI0RFRjVFNUZGIiwiIzQ5QzFBREZGIiwiIzM1N0JBMkZGIiwiIzNFMzU2QkZGIiwiIzBCMDQwNUZGIiksDQogICAgICAgICAgICAgICAgICAgIGxhYmVscz1hcy5jaGFyYWN0ZXIocXVhbnRpbGUoY2FsZ2FyeTEkYWxsUHJlZGljdGlvbnMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYygwLCAuMjUsIC41LCAuNzUsIDEpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hLnJtPVQpKSwNCiAgICAgICAgICAgICAgICAgICAgbmFtZT0iUHJlZGljdGVkIFF1aW50aWxlXG5Qcm9iYWJpbGl0aWVzKCUpXG4oRGFya2VzdD1cbkhpZ2hlc3QpIikgKw0KICAgIGdlb21fc2YoZGF0YT1jYWxnYXJ5MSAgJT4lIA0KICAgICAgICAgICAgICAgZmlsdGVyKGhpc3RfZmxvb2QgPT0gMSksIA0KICAgICAgICAgICAgICAgZmlsbD0ieWVsbG93IixhbHBoYSA9IC40LCBjb2xvdXI9Ik5BIikgKw0KICBtYXBUaGVtZSArDQogIGxhYnModGl0bGU9Ik9ic2VydmVkIGFuZCBQcmVkaWN0ZWQgRmxvb2RpbmcgQXJlYXMiLA0KICAgICAgIHN1YnRpdGxlPSJDYWxnYXJ5OyBZZWxsb3cgPSBIaXN0b3JpY2FsIGZsb29kaW5nOyBHcmFkaWVudCA9IFByZWRpY3RlZCBmbG9vZGluZyAoRGFya2VzdCA9IE1vc3QpIikNCmBgYA0KDQpXZSBzZWUgdGhhdCBvdXIgQ2FsZ2FyeSBtb2RlbCBoYXMgc29tZSB1bnVzdWFsIHZlcnRpY2FsIHN0cmlwaW5nLiBXZSBhcmUNCm5vdCBzdXJlIHdoeSB0aGlzIGhhcHBlbmVkLCBidXQgc3VzcGVjdCBpdCBiYXkgYmUgdGhlIHJlc3VsdCBvZg0KaW5jb25ncnVpdHkgdW5pdHMgYW1vbmcgb3VyIGRhdGFzZXRzIHZpc3VhbGl6ZWQuIFdlIGVsYWJvcmF0ZSBiZWxvdyBhcw0KdG8gdGhlIHBvdGVudGlhbCBpbXBsaWNhdGlvbnMgb2YgdGhpcywgYW5kIGhvdyB3ZSB3b3VsZCBnbyBhYm91dA0KYWRqdXN0aW5nIG91ciBtb2RlbCB0byB0cnkgdG8gcmVjdGlmeSB0aGlzIGluY29ycmVjdCB2aXN1YWxpemF0aW9uLg0KDQojIyMgQ2FsZ2FyeSBDb25mdXNpb24gTWV0cmljczoNCg0KQnkgcnVubmluZyBjb25mdXNpb24gbWV0cmljcywgd2Ugc2VlIHRoYXQgb3VyIG1vZGVsIHByZWRpY3RzIHRydWUNCm5lZ2F0aXZlcyBhbmQgdHJ1ZSBwb3NpdGl2ZXMgd2VsbC4gSG93ZXZlciwgaXQgaGFzIGEgbG90IG9mIGZhbHNlDQpwb3NpdGl2ZXMgYmVjYXVzZSBzb2lsIGRyYWluYWdlIGVmZmljYWN5IGFwcGVhcnMgdG8gYmUgb3Zlci13ZWlnaHRlZCwNCmFmZmVjdGluZyB0aGUgc2hhcGUgb2YgdGhlIG1vZGVsIGFuZCB0aGUgY29uZnVzaW9uIG1ldHJpY3MNCnZpc3VhbGl6YXRpb24uDQoNCmBgYHtyIGVycm9yX21hcCwgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KY2FsZ2FyeTEgJT4lDQogIG11dGF0ZShjb25mUmVzdWx0PWNhc2Vfd2hlbihhbGxQcmVkaWN0aW9ucyA8IDQgJiBoaXN0X2Zsb29kPT0wIH4gIlRydWVfTmVnYXRpdmUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWxsUHJlZGljdGlvbnMgPj0gMSAmIGhpc3RfZmxvb2Q9PTEgfiAiVHJ1ZV9Qb3NpdGl2ZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbGxQcmVkaWN0aW9ucyA8IDQgJiBoaXN0X2Zsb29kPT0xIH4gIkZhbHNlX05lZ2F0aXZlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFsbFByZWRpY3Rpb25zID49IDQgJiBoaXN0X2Zsb29kPT0wIH4gIkZhbHNlX1Bvc2l0aXZlIikpICU+JQ0KICBnZ3Bsb3QoKSsNCiAgZ2VvbV9zZihhZXMoZmlsbCA9IGNvbmZSZXN1bHQpLCBjb2xvciA9ICJ0cmFuc3BhcmVudCIpKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJSZWQiLCJMaWdodCBCbHVlIiwiT3JhbmdlIiwiUGluayIpLA0KICAgICAgICAgICAgICAgICAgICBuYW1lPSJPdXRjb21lcyIpKw0KICBsYWJzKHRpdGxlPSJDYWxnYXJ5IENvbmZ1c2lvbiBNZXRyaWNzIikgKw0KICBtYXBUaGVtZQ0KDQpgYGANCg0KIyMjIERhdGEgV3JhbmdsaW5nIC0gQm9pc2UNCg0KSGVyZSwgd2UgcHVsbCBpbiBvdXIgQm9pc2UgZGF0YSwgd2hpY2ggaXMgb3VyIG9yaWdpbmFsIGZpc2huZXQsIGJ1dA0KZnVsbHkgcG9wdWxhdGVkIHdpdGggQm9pc2UncyBkYXRhIG9uIHRoZSBmb3VyIGZhY3RvcnMgb24gd2hpY2ggd2UNCnRyYWluZWQgb3VyIENhbGdhcnkgbW9kZWwuDQoNCmBgYHtyIGVjaG89VFJVRX0NCg0KYm9pc2VfbWFzdGVyX2Zpc2ggPC0gc3RfcmVhZCgiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2FuYS1vc28vQ2FsZ2FyeXZCb2lzZS8wYWI0YWFkZjllN2Q4MzQwOWFlZmQ1YWM4ZGFiM2U2NTE1MjQwYjFmL2dlb2pzb25zL2JvaXNlX2dlb2pzb25zL2JvaXNlX2dlb2pzb25zL0JPSVNFX01BU1RFUkZJU0guZ2VvanNvbiIpDQoNCmJvaXNlX3ZhcnMgPC0gDQogICAgYm9pc2VfbWFzdGVyX2Zpc2ggJT4lIA0KICAgIGRwbHlyOjpzZWxlY3QoaGlzdF9mbG9vZCxEcmFpblJhbmssRmxvd0FjYyxQZXJ2aW91cyxkaXN0aDJvX2dyLE9CSkVDVElEKQ0KICANCmJvaXNlX3ZhcnMgPC0gYm9pc2VfdmFycyAlPiUgbXV0YXRlKGhpc3RfZmxvb2Q9cmVjb2RlKGhpc3RfZmxvb2QsICcxJz0nMCcsICc2Jz0nMScpKQ0KYm9pc2VfdmFycyA8LSBib2lzZV92YXJzICU+JSBtdXRhdGUoaGlzdF9mbG9vZCA9IGFzLm51bWVyaWMoaGlzdF9mbG9vZCkpDQoNCmJvaXNlX2FsbFByZWRpY3Rpb25zIDwtIA0KICBwcmVkaWN0KGN2Rml0LCBib2lzZV92YXJzLCB0eXBlPSJwcm9iIilbLDJdIA0KDQpib2lzZTEgPC0gDQogIGNiaW5kKGJvaXNlX3ZhcnMsYm9pc2VfYWxsUHJlZGljdGlvbnMpICU+JSANCiAgbXV0YXRlKGJvaXNlX2FsbFByZWRpY3Rpb25zID0gcm91bmQoYm9pc2VfYWxsUHJlZGljdGlvbnMgKiAxMDApKQ0KDQoNCmBgYA0KDQojIFRlc3RpbmcgdGhlIENhbGdhcnkgRmxvb2QgTW9kZWwgb24gQm9pc2UNCg0KSGVyZSwgd2UgYXBwbHkgb3VyIENhbGdhcnkgZmxvb2QgbW9kZWwgdG8gb3VyIG90aGVyIGdlb2dyYXBoeSwgQm9pc2UuIFdlDQphdHRhY2ggdGhlIHByZWRpY3Rpb24gdmFsdWVzLCB0cmFpbmVkIG9uIHRoZSBDYWxnYXJ5IG1vZGVsLCB0byBCb2lzZSwNCmJhc2VkIG9uIEJvaXNlJ3Mgc3BhdGlhbCBkaXN0cmlidXRpb24gb2YgdGhlIGZvdXIgZmFjdG9ycyB0aGF0IHdlIGlucHV0DQp0byBvdXIgQ2FsZ2FyeSBtb2RlbC4NCg0KYGBge3IgZWNobz1UUlVFfQ0KZ2dwbG90KCkgKyANCiAgZ2VvbV9zZihkYXRhPWJvaXNlMSwgYWVzKGZpbGw9ZmFjdG9yKG50aWxlKGJvaXNlX2FsbFByZWRpY3Rpb25zLDUpKSksIA0KICAgICAgICAgIGNvbG91cj1OQSkgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCIjREVGNUU1RkYiLCIjNDlDMUFERkYiLCIjMzU3QkEyRkYiLCIjM0UzNTZCRkYiLCIjMEIwNDA1RkYiKSwNCiAgICAgICAgICAgICAgICAgICAgbGFiZWxzPWFzLmNoYXJhY3RlcihxdWFudGlsZShib2lzZTEkYm9pc2VfYWxsUHJlZGljdGlvbnMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjKDAsIC4yNSwgLjUsIC43NSwgMSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmEucm09VCkpLA0KICAgICAgICAgICAgICAgICAgICBuYW1lPSJQcmVkaWN0ZWQgUXVpbnRpbGVcblByb2JhYmlsaXRpZXMoJSlcbihEYXJrZXN0PVxuSGlnaGVzdCkiKSArDQogICAgZ2VvbV9zZihkYXRhPWJvaXNlMSAgJT4lIA0KICAgICAgICAgICAgICAgZmlsdGVyKGhpc3RfZmxvb2QgPT0gMSksIA0KICAgICAgICAgICAgICAgZmlsbD0ieWVsbG93IixhbHBoYSA9IDAuNSwgY29sb3VyPSJOQSIpICsNCiBjb29yZF9zZih4bGltID0gYygtMTI5NTUyNzAsIC0xMjkyMzI3MCksIHlsaW0gPSBjKDUzNTIwMzMsIDUzOTQ5NzYpLCBleHBhbmQgPSBGQUxTRSkgKw0KICBtYXBUaGVtZSArDQogIGxhYnModGl0bGU9Ik9ic2VydmVkIGFuZCBQcmVkaWN0ZWQgRmxvb2RpbmcgQXJlYXMiLA0KICAgICAgIHN1YnRpdGxlPSJCb2lzZTsgWWVsbG93ID0gSGlzdG9yaWNhbCBmbG9vZGluZzsgR3JhZGllbnQgPSBQcmVkaWN0ZWQgZmxvb2RpbmcgKERhcmtlc3QgPSBNb3N0KSIpDQoNCmBgYA0KDQojIFJlc3VsdHMNCg0KVW5mb3J0dW5hdGVseSwgaXQgZG9lcyBub3Qgc2VlbSB0aGF0IG91ciBtb2RlbCB3b3JrcyB0b28gd2VsbCBvbiBCb2lzZSENClRoZSBhcmVhcyB3aGVyZSBvdXIgbW9kZWwgcHJlZGljdGVkIHRoYXQgQm9pc2Ugd291bGQgZmxvb2QgaGF2ZQ0KaGlzdG9yaWNhbGx5IGJlZW4gZnJlZSBvZiBmbG9vZGluZy4gV2hpbGUgdGhpcyBtYXkgYmUgYSByZXN1bHQgb2YNCmluYWNjdXJhdGUvdW5jb3JyZWxhdGVkIGZhY3RvcnMsIGl0IGNvdWxkIGFsc28gYmUgdGhlIHJlc3VsdCBvZiBkYXRhDQppbmNvbmdydWl0aWVzIHRoYXQgY2F1c2VkIHRoZSB2aXNpYmxlLCB2ZXJ0aWNhbCBzdHJpcGluZyBvbiBvdXIgQ2FsZ2FyeQ0KbW9kZWwuDQoNCiMjIyBORVhUIFNURVBTDQoNCklmIHdlIHdlcmUgdG8gcmV2aXNpdCBvdXIgbW9kZWwgdG8gdHVuZSBpdCB0byBiZSBtb3JlIGFjY3VyYXRlLCB3ZSB3b3VsZA0KdHJhaW4gaXQgdG8gYmUgbW9yZSBzZW5zaXRpdmUgdG8gc3VyZmFjZSBwZXJtZWFiaWxpdHksIGFuZCB0bw0KZGUtcHJpb3JpdGl6ZSBzb2lsIGRyYWluYWdlIGVmZmljYWN5LCB3aGljaCBpbmZsdWVuY2VkIHRoZSB3aG9sZSBzaGFwZQ0Kb2YgdGhlIHByZWRpY3Rpb24gaW4gd2F5cyB0aGF0IGFyZSBub3QgYWNjdXJhdGUgdG8gdGhlIGhpc3RvcmljYWwgZmxvb2QNCnBhdHRlcm5zLiBJbiBvcmRlciB0byBkbyB0aGlzLCB3ZSBtaWdodCBuZWVkIHRvIHVzZSBhIG11bHRpdmFyaWF0ZSBtb2RlbA0KdGhhdCBhbGxvd3MgdXMgdG8gbWFudWFsbHkgd2VpZ2h0IG9uZSBmYWN0b3IgbW9yZSBoaWdobHkgYWdhaW5zdA0KYW5vdGhlci4NCg0KQXMgYSBmaXN0IHN0ZXAgaW4gZGlhZ25vc2luZyB3aHkgdGhlIG1vZGVsIGRpZCBub3Qgd29yayB2ZXJ5IHdlbGwgb24NCkJvaXNlLCB3b3VsZCBydW4gdGhlIGNvbmZ1c2lvbiBtZXRyaWNzIG9uIEJvaXNlIHRvIHNlZSB3aGVyZSB0aGVyZSBpcw0KbWlzYWxpZ25tZW50Lg0K