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?)

Calgary and Boise

We selected Boise, Idaho as our comparable city.

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)")

Results

Unfortunately, it does not seem that our model works too well on Boise! The areas where our model predicted that Boise would flood have historically been free of flooding. While this may be a result of inaccurate/uncorrelated factors, it could also be the result of data incongruities that caused the visible, vertical striping on our Calgary model.

NEXT STEPS

If we were to revisit our model to tune it to be more accurate, we would train it to be more sensitive to surface permeability, and to de-prioritize soil drainage efficacy, which influenced the whole shape of the prediction in ways that are not accurate to the historical flood patterns. In order to do this, we might need to use a multivariate model that allows us to manually weight one factor more highly against another.

As a fist step in diagnosing why the model did not work very well on Boise, would run the confusion metrics on Boise to see where there is misalignment.

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