Introduction

The goal of this project is to predict the number of cases of wine sold based on chemical characteristics of close to 13,000 wines. This report will cover the following processes: * Cleaning, standardizing, and exploring the data * Development of each of the following models * Poisson Regression * Zero-Inflated Poisson Regression * Negative Binomial Regression * Zero-Inflated Negative Binomial Regression * Multiple Linear Regression * Comparison of each model * Code to deploy the model on the test data set All code and data for this paper can be found here. # Cleaning and Exploring the Data The exploration and cleaning step involves creating flag variables for missing values and outliers, deleting the outliers, and use regression trees to impute the remaining values.

The STARS field has the most missing data while several chemical measurements are missing 5-10% of values. In case the absence of data is an important factor in sales, a binary flag variable is produced to identify places where data was initially missing from each variable.

package <U+393C><U+3E31>ggplot2<U+393C><U+3E32> was built under R version 3.4.2

Outliers, defined as \(Q1 - (1.5*IQR)\) and \(Q3 + (1.5*IQR)\) are removed from the data set and flag variables are appended in a similar fashion to the missing data flags. Finally, all missing and deleted variables are reimputed using regression trees.

Finally, we can examine the distributions of our variables. Flags with homogenous values are removed from the training set to get a jump start on stepwise dimension reduction used in the model development stage.

  fixedacidity       volatileacidity      citricacid        residualsugar         chlorides          freesulfurdioxide   totalsulfurdioxide 
 Min.   :-2.405343   Min.   :-2.36238   Min.   :-2.406982   Min.   :-2.432242   Min.   :-2.3748801   Min.   :-2.518626   Min.   :-2.744691  
 1st Qu.:-0.325198   1st Qu.:-0.29839   1st Qu.:-0.361933   1st Qu.:-0.241350   1st Qu.:-0.1642604   1st Qu.:-0.283968   1st Qu.:-0.455171  
 Median :-0.038940   Median :-0.09039   Median : 0.003254   Median :-0.104133   Median :-0.0517827   Median :-0.030304   Median : 0.017829  
 Mean   : 0.001865   Mean   :-0.00449   Mean   : 0.001676   Mean   :-0.001509   Mean   : 0.0002116   Mean   : 0.003729   Mean   : 0.003165  
 3rd Qu.: 0.399990   3rd Qu.: 0.43761   3rd Qu.: 0.353834   3rd Qu.: 0.357829   3rd Qu.: 0.2640202   3rd Qu.: 0.295835   3rd Qu.: 0.415349  
 Max.   : 2.499219   Max.   : 2.51761   Max.   : 2.398883   Max.   : 2.475539   Max.   : 2.4010968   Max.   : 2.554652   Max.   : 2.704869  
    density                ph              sulphates            alcohol           labelappeal         acidindex            stars         
 Min.   :-2.510270   Min.   :-2.620407   Min.   :-2.338310   Min.   :-2.993770   Min.   :-2.23427   Min.   :-2.62836   Min.   :-1.15425  
 1st Qu.:-0.260398   1st Qu.:-0.407492   1st Qu.:-0.289601   1st Qu.:-0.455298   1st Qu.:-1.11205   1st Qu.:-0.60274   1st Qu.:-1.15425  
 Median : 0.020346   Median :-0.009859   Median :-0.057414   Median :-0.037196   Median : 0.01017   Median : 0.41008   Median :-0.04626  
 Mean   : 0.002849   Mean   :-0.001639   Mean   : 0.003905   Mean   : 0.002461   Mean   : 0.00000   Mean   : 0.01698   Mean   :-0.02669  
 3rd Qu.: 0.288033   3rd Qu.: 0.422351   3rd Qu.: 0.379644   3rd Qu.: 0.530227   3rd Qu.: 1.13240   3rd Qu.: 0.41008   3rd Qu.: 1.06172  
 Max.   : 2.500037   Max.   : 2.635267   Max.   : 2.401037   Max.   : 3.098564   Max.   : 2.25462   Max.   : 2.43571   Max.   : 2.16970  
 residualsugar_na.flag chlorides_na.flag freesulfurdioxide_na.flag totalsulfurdioxide_na.flag   ph_na.flag      sulphates_na.flag
 Min.   :0.00000       Min.   :0.00000   Min.   :0.00000           Min.   :0.0000             Min.   :0.00000   Min.   :0.00000  
 1st Qu.:0.00000       1st Qu.:0.00000   1st Qu.:0.00000           1st Qu.:0.0000             1st Qu.:0.00000   1st Qu.:0.00000  
 Median :0.00000       Median :0.00000   Median :0.00000           Median :0.0000             Median :0.00000   Median :0.00000  
 Mean   :0.04814       Mean   :0.04986   Mean   :0.05057           Mean   :0.0533             Mean   :0.03087   Mean   :0.09457  
 3rd Qu.:0.00000       3rd Qu.:0.00000   3rd Qu.:0.00000           3rd Qu.:0.0000             3rd Qu.:0.00000   3rd Qu.:0.00000  
 Max.   :1.00000       Max.   :1.00000   Max.   :1.00000           Max.   :1.0000             Max.   :1.00000   Max.   :1.00000  
 alcohol_na.flag   stars_na.flag    fixedacidity_out.flag volatileacidity_out.flag citricacid_out.flag residualsugar_out.flag
 Min.   :0.00000   Min.   :0.0000   Min.   :0.00000       Min.   :0.00000          Min.   :0.00000     Min.   :0.0000        
 1st Qu.:0.00000   1st Qu.:0.0000   1st Qu.:0.00000       1st Qu.:0.00000          1st Qu.:0.00000     1st Qu.:0.0000        
 Median :0.00000   Median :0.0000   Median :0.00000       Median :0.00000          Median :0.00000     Median :0.0000        
 Mean   :0.05104   Mean   :0.2625   Mean   :0.04158       Mean   :0.05487          Mean   :0.05557     Mean   :0.1254        
 3rd Qu.:0.00000   3rd Qu.:1.0000   3rd Qu.:0.00000       3rd Qu.:0.00000          3rd Qu.:0.00000     3rd Qu.:0.0000        
 Max.   :1.00000   Max.   :1.0000   Max.   :1.00000       Max.   :1.00000          Max.   :1.00000     Max.   :1.0000        
 chlorides_out.flag freesulfurdioxide_out.flag totalsulfurdioxide_out.flag density_out.flag  ph_out.flag      sulphates_out.flag
 Min.   :0.00000    Min.   :0.0000             Min.   :0.00000             Min.   :0.0000   Min.   :0.00000   Min.   :0.00000   
 1st Qu.:0.00000    1st Qu.:0.0000             1st Qu.:0.00000             1st Qu.:0.0000   1st Qu.:0.00000   1st Qu.:0.00000   
 Median :0.00000    Median :0.0000             Median :0.00000             Median :0.0000   Median :0.00000   Median :0.00000   
 Mean   :0.08761    Mean   :0.1733             Mean   :0.02876             Mean   :0.1699   Mean   :0.03181   Mean   :0.05619   
 3rd Qu.:0.00000    3rd Qu.:0.0000             3rd Qu.:0.00000             3rd Qu.:0.0000   3rd Qu.:0.00000   3rd Qu.:0.00000   
 Max.   :1.00000    Max.   :1.0000             Max.   :1.00000             Max.   :1.0000   Max.   :1.00000   Max.   :1.00000   
 alcohol_out.flag  acidindex_out.flag     index           target     
 Min.   :0.00000   Min.   :0.00000    Min.   :    1   Min.   :0.000  
 1st Qu.:0.00000   1st Qu.:0.00000    1st Qu.: 4038   1st Qu.:2.000  
 Median :0.00000   Median :0.00000    Median : 8110   Median :3.000  
 Mean   :0.01844   Mean   :0.04103    Mean   : 8070   Mean   :3.029  
 3rd Qu.:0.00000   3rd Qu.:0.00000    3rd Qu.:12106   3rd Qu.:4.000  
 Max.   :1.00000   Max.   :1.00000    Max.   :16129   Max.   :8.000  

Model Development

Five models are developed for this paper. First is a Poisson regression model followed by a negative binomial regression model. Then zero-inflated versions of each model are developed as well as a multivariate linear regression model. Root mean squared error (RMSE), and mean absolute error (MAE) are calculated for each model for purposes of out of sample model evaluation.

Poisson and Negative Binomial Regression

Poisson regression models use the log link function to approximate regression processes for a count variable distributed such that the variance is equal to the mean 1. The backward stepwise feature selection algoritm returned a poisson model with twelve predictors2. Noteably, missing values for stars and outlier acidindex values significantly harm sales while labelappeal and present higher values for stars led to higher sales. Negative binomial models use a different probability density function than Poisson regression to account for over-dispersion of the target variable (as we have in this case)3. The coefficients for this model are identical to that of a Poisson model.

Zero-Inflated Regression

Poisson and negative binomial models can be skewed by an overabundance of zero values. Zero-inflated models assumethe distribution has two types of values - rightful zero measurements and a separate set of values that follows a more typical distribution. These models first sort values into their proper category, then predict their outcomes using separate sets of coefficients for each4. In this case, over 2,500 wines sold zero cases so zero-inflated models may improve predictive power. The same variables are used in the zero inflated models as the initial models5. In this case, some of the coefficients are flipped. Missing ratings and outlier acidity values are strong positive influences on sales. Label appeal is similarly positive as in previous models, but actual star ratings are negative. Again, the coefficients for poisson and negative binomial models are identical.

Multiple Regression

The final model attempted is multiple linear regression. A new stepwise selection algorithm is used to select model variables. The same variables with relatively large coefficients in the poisson and negative binomial models stand out in the linear regression model, although additional variables with smaller coefficients are included as well.

Model Evaluation

To assess the accuracy of the various models, each model is repeatedly trained on random samples of the data and tested out of sample. Iterative resampling is used to measure the error rates of each model and attempt to account for uncertainty in error measurements by producing distributions of possible error measures rather than single point estimates. The distributions of MAE and RMSE for each model are presented below. Similar to the above coefficients, the error rates for the poisson and negative binomial models are identical with the two zero-inflated models strongly outperforming the non-adjusted models. Multiple regression also made a strong showing, but did not improve predictive accuracy compared to the two zero-inflated models. Of the two winning models, zero-inflated negative binomial is preferred due to the slight overdispersion of the target variable.

Implementation

The following function applies the zero-inflated negative binomial regression model to out of sample data and generates a CSV file in the proper submission format.

apply_model <- function(csv){
  train.raw <- read_csv(csv)
  colnames(train_raw) <- tolower(colnames(train_raw))
  
  train.flagged <- train_raw%>%
    mutate_all(funs(na.flag = ifelse(is.na(.),1,0)))
  int_df <- train.flagged%>%
    dplyr::select(-index, -target, -index_na.flag, -target_na.flag)%>%
    dplyr::select_if(is.numeric)
  
  cleaned_cols <- list()
  for(c in colnames(train_raw%>%
                    dplyr::select(-index, -target)%>%
                    dplyr::select_if(is.numeric))){
    column <- train.flagged%>%select_(col = c)
    iqr <- quantile(column$col, na.rm = T)[4] - quantile(column$col, na.rm = T)[2]
    low <- quantile(column$col, na.rm = T)[2] - iqr
    high <- quantile(column$col, na.rm = T)[4] + iqr
    
    vals <- c()
    for(i in seq(1:nrow(int_df))){
      ifelse(between(column$col[i], low - (1.5*iqr), high + (1.5*iqr)),
             vals[i] <- column$col[i], 
             ifelse(is.na(column$col[i]), vals[i] <- NA, vals[i] <- NA))
    }
    
    ifelse(length(vals) == nrow(int_df),
           cleaned_cols[[c]] <- vals, 
           cleaned_cols[[c]] <- c(vals,NA))
  }
  
  df2 <- bind_cols(
    bind_cols(cleaned_cols)%>%
      scale(center = TRUE)%>%
      data.frame(),
    train.flagged%>%
      dplyr::select(ends_with('na.flag'))%>%
      dplyr::select(-index_na.flag, -target_na.flag)
  )
  
  df3 <- df2%>%
    mutate(
      fixedacidity_out.flag = ifelse(is.na(fixedacidity) & fixedacidity_na.flag ==0,1,0),
      volatileacidity_out.flag = ifelse(is.na(volatileacidity) & volatileacidity_na.flag ==0,1,0),
      citricacid_out.flag = ifelse(is.na(citricacid) & citricacid_na.flag ==0,1,0),
      residualsugar_out.flag = ifelse(is.na(residualsugar) & residualsugar_na.flag ==0,1,0),
      chlorides_out.flag = ifelse(is.na(chlorides) & chlorides_na.flag ==0,1,0),
      freesulfurdioxide_out.flag = ifelse(is.na(freesulfurdioxide) & freesulfurdioxide_na.flag ==0,1,0),
      totalsulfurdioxide_out.flag = ifelse(is.na(totalsulfurdioxide) & totalsulfurdioxide_na.flag ==0,1,0),
      density_out.flag = ifelse(is.na(density) & density_na.flag ==0,1,0),
      ph_out.flag = ifelse(is.na(ph) & ph_na.flag ==0,1,0),
      sulphates_out.flag = ifelse(is.na(sulphates) & sulphates_na.flag ==0,1,0),
      alcohol_out.flag = ifelse(is.na(alcohol) & alcohol_na.flag ==0,1,0),
      labelappeal_out.flag = ifelse(is.na(labelappeal) & labelappeal_na.flag ==0,1,0),
      acidindex_out.flag = ifelse(is.na(acidindex) & acidindex_na.flag ==0,1,0),
      stars_out.flag = ifelse(is.na(stars) & stars_na.flag ==0,1,0)
  )
  
  library(mice)
  temp_df <- mice(df3, method = 'cart', maxit = 1)
  train <- complete(temp_df)%>%
    bind_cols(train_raw%>%dplyr::select(index))%>%
    dplyr::select(-stars_out.flag, -labelappeal_out.flag, -density_na.flag,
                  -labelappeal_na.flag, -acidindex_na.flag, -fixedacidity_na.flag,
                  -volatileacidity_na.flag, -citricacid_na.flag)
  return(data.frame(
    index = train$index,
    target_p = predict(zinng.mod, newdata = train)
    )
  )
}

test <- apply_model('Wine_Random_Test.csv')
write.csv(test, 'yazman.csv', row.names = FALSE)

Citations


  1. Hoffmann, John P. Generalized Linear Models: an Applied Approach. Pearson/Allyn & Bacon, 2004.

  2. https://stats.idre.ucla.edu/r/dae/poisson-regression/

  3. https://stats.idre.ucla.edu/r/dae/negative-binomial-regression/

  4. https://statisticalhorizons.com/zero-inflated-models

  5. https://stats.idre.ucla.edu/r/dae/zip/

LS0tDQp0aXRsZTogIlByZWRpY3QgNDEzIEZpbmFsOiBXaW5lIFNhbGVzIFByZWRpY3Rpb25zIg0KYXV0aG9yOiAnSm9zaCBZYXptYW4nDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQojIEludHJvZHVjdGlvbg0KVGhlIGdvYWwgb2YgdGhpcyBwcm9qZWN0IGlzIHRvIHByZWRpY3QgdGhlIG51bWJlciBvZiBjYXNlcyBvZiB3aW5lIHNvbGQgYmFzZWQgb24gY2hlbWljYWwgY2hhcmFjdGVyaXN0aWNzIG9mIGNsb3NlIHRvIDEzLDAwMCB3aW5lcy4gVGhpcyByZXBvcnQgd2lsbCBjb3ZlciB0aGUgZm9sbG93aW5nIHByb2Nlc3NlczoNCiogQ2xlYW5pbmcsIHN0YW5kYXJkaXppbmcsIGFuZCBleHBsb3JpbmcgdGhlIGRhdGENCiogRGV2ZWxvcG1lbnQgb2YgZWFjaCBvZiB0aGUgZm9sbG93aW5nIG1vZGVscw0KICAqIFBvaXNzb24gUmVncmVzc2lvbg0KICAqIFplcm8tSW5mbGF0ZWQgUG9pc3NvbiBSZWdyZXNzaW9uDQogICogTmVnYXRpdmUgQmlub21pYWwgUmVncmVzc2lvbg0KICAqIFplcm8tSW5mbGF0ZWQgTmVnYXRpdmUgQmlub21pYWwgUmVncmVzc2lvbg0KICAqIE11bHRpcGxlIExpbmVhciBSZWdyZXNzaW9uIA0KKiBDb21wYXJpc29uIG9mIGVhY2ggbW9kZWwNCiogQ29kZSB0byBkZXBsb3kgdGhlIG1vZGVsIG9uIHRoZSB0ZXN0IGRhdGEgc2V0DQpBbGwgY29kZSBhbmQgZGF0YSBmb3IgdGhpcyBwYXBlciBjYW4gYmUgZm91bmQgW2hlcmVdKGh0dHBzOi8vZ2l0aHViLmNvbS9qb3NoeWF6bWFuL25vcnRod2VzdGVybi90cmVlL21hc3Rlci9wcmVkaWN0LTQxMS93aW5lKS4NCiMgQ2xlYW5pbmcgYW5kIEV4cGxvcmluZyB0aGUgRGF0YQ0KVGhlIGV4cGxvcmF0aW9uIGFuZCBjbGVhbmluZyBzdGVwIGludm9sdmVzIGNyZWF0aW5nIGZsYWcgdmFyaWFibGVzIGZvciBtaXNzaW5nIHZhbHVlcyBhbmQgb3V0bGllcnMsIGRlbGV0aW5nIHRoZSBvdXRsaWVycywgYW5kIHVzZSByZWdyZXNzaW9uIHRyZWVzIHRvIGltcHV0ZSB0aGUgcmVtYWluaW5nIHZhbHVlcy4gDQoNCmBgYHtyLCBlY2hvID0gRkFMU0V9DQpsaWJyYXJ5KHJlYWRyKQ0KdHJhaW5fcmF3IDwtIHJlYWRfY3N2KCdXaW5lX1RyYWluaW5nLmNzdicpDQpgYGANCg0KVGhlIGBTVEFSU2AgZmllbGQgaGFzIHRoZSBtb3N0IG1pc3NpbmcgZGF0YSB3aGlsZSBzZXZlcmFsIGNoZW1pY2FsIG1lYXN1cmVtZW50cyBhcmUgbWlzc2luZyA1LTEwJSBvZiB2YWx1ZXMuIEluIGNhc2UgdGhlIGFic2VuY2Ugb2YgZGF0YSBpcyBhbiBpbXBvcnRhbnQgZmFjdG9yIGluIHNhbGVzLCBhIGJpbmFyeSBmbGFnIHZhcmlhYmxlIGlzIHByb2R1Y2VkIHRvIGlkZW50aWZ5IHBsYWNlcyB3aGVyZSBkYXRhIHdhcyBpbml0aWFsbHkgbWlzc2luZyBmcm9tIGVhY2ggdmFyaWFibGUuDQoNCmBgYHtyLCBlY2hvID0gRkFMU0V9DQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeSh5YXp0aGVtZSkNCm51bGxzIDwtIGRhdGEuZnJhbWUoY29sID0gYXMuY2hhcmFjdGVyKGNvbG5hbWVzKHRyYWluX3JhdykpLCANCiAgICAgICAgICAgICAgICAgICAgcGN0X251bGwgPSBjb2xTdW1zKGlzLm5hKHRyYWluX3JhdykpKjEwMC8oY29sU3Vtcyhpcy5uYSh0cmFpbl9yYXcpKStjb2xTdW1zKCFpcy5uYSh0cmFpbl9yYXcpKSkpJT4lDQogIGZpbHRlcihjb2wgIT0gJ0lOREVYJykNCmdncGxvdChudWxscywgYWVzKHggPSBjb2wsIHkgPSBwY3RfbnVsbCkpKw0KICBnZW9tX2JhcihmaWxsID0geWF6X2NvbHNbMV0sIHN0YXQgPSAnaWRlbnRpdHknKSsNCiAgY29vcmRfZmxpcCgpKw0KICBsYWJzKHRpdGxlID0gJ0Rpc3RyaWJ1dGlvbiBvZiBNaXNzaW5nIERhdGEnLA0KICAgICAgIHggPSBlbGVtZW50X2JsYW5rKCksIHkgPSAnUGVyY2VudCBvZiBJbmZvcm1hdGlvbiBNaXNzaW5nJykrDQogIHRoZW1lX3lheigpKw0KICB5bGltKDAsMTAwKQ0KYGBgDQoNCk91dGxpZXJzLCBkZWZpbmVkIGFzICRRMSAtICgxLjUqSVFSKSQgYW5kICRRMyArICgxLjUqSVFSKSQgYXJlIHJlbW92ZWQgZnJvbSB0aGUgZGF0YSBzZXQgYW5kIGZsYWcgdmFyaWFibGVzIGFyZSBhcHBlbmRlZCBpbiBhIHNpbWlsYXIgZmFzaGlvbiB0byB0aGUgbWlzc2luZyBkYXRhIGZsYWdzLiBGaW5hbGx5LCBhbGwgbWlzc2luZyBhbmQgZGVsZXRlZCB2YXJpYWJsZXMgYXJlIHJlaW1wdXRlZCB1c2luZyByZWdyZXNzaW9uIHRyZWVzLg0KDQpgYGB7ciwgZWNobyA9IEZBTFNFfQ0KY29sbmFtZXModHJhaW5fcmF3KSA8LSB0b2xvd2VyKGNvbG5hbWVzKHRyYWluX3JhdykpDQoNCnRyYWluLmZsYWdnZWQgPC0gdHJhaW5fcmF3JT4lDQogIG11dGF0ZV9hbGwoZnVucyhuYS5mbGFnID0gaWZlbHNlKGlzLm5hKC4pLDEsMCkpKQ0KaW50X2RmIDwtIHRyYWluLmZsYWdnZWQlPiUNCiAgZHBseXI6OnNlbGVjdCgtaW5kZXgsIC10YXJnZXQsIC1pbmRleF9uYS5mbGFnLCAtdGFyZ2V0X25hLmZsYWcpJT4lDQogIGRwbHlyOjpzZWxlY3RfaWYoaXMubnVtZXJpYykNCiMgbWQucGF0dGVybihpbnRfZGYpDQpjbGVhbmVkX2NvbHMgPC0gbGlzdCgpDQpmb3IoYyBpbiBjb2xuYW1lcyh0cmFpbl9yYXclPiUNCiAgICAgICAgICAgICAgICAgIGRwbHlyOjpzZWxlY3QoLWluZGV4LCAtdGFyZ2V0KSU+JQ0KICAgICAgICAgICAgICAgICAgZHBseXI6OnNlbGVjdF9pZihpcy5udW1lcmljKSkpew0KICBjb2x1bW4gPC0gdHJhaW4uZmxhZ2dlZCU+JXNlbGVjdF8oY29sID0gYykNCiAgaXFyIDwtIHF1YW50aWxlKGNvbHVtbiRjb2wsIG5hLnJtID0gVClbNF0gLSBxdWFudGlsZShjb2x1bW4kY29sLCBuYS5ybSA9IFQpWzJdDQogIGxvdyA8LSBxdWFudGlsZShjb2x1bW4kY29sLCBuYS5ybSA9IFQpWzJdIC0gaXFyDQogIGhpZ2ggPC0gcXVhbnRpbGUoY29sdW1uJGNvbCwgbmEucm0gPSBUKVs0XSArIGlxcg0KICANCiAgdmFscyA8LSBjKCkNCiAgZm9yKGkgaW4gc2VxKDE6bnJvdyhpbnRfZGYpKSl7DQogICAgaWZlbHNlKGJldHdlZW4oY29sdW1uJGNvbFtpXSwgbG93IC0gKDEuNSppcXIpLCBoaWdoICsgKDEuNSppcXIpKSwNCiAgICAgICAgICAgdmFsc1tpXSA8LSBjb2x1bW4kY29sW2ldLCANCiAgICAgICAgICAgaWZlbHNlKGlzLm5hKGNvbHVtbiRjb2xbaV0pLCB2YWxzW2ldIDwtIE5BLCB2YWxzW2ldIDwtIE5BKSkNCiAgfQ0KICANCiAgaWZlbHNlKGxlbmd0aCh2YWxzKSA9PSBucm93KGludF9kZiksDQogICAgICAgICBjbGVhbmVkX2NvbHNbW2NdXSA8LSB2YWxzLCANCiAgICAgICAgIGNsZWFuZWRfY29sc1tbY11dIDwtIGModmFscyxOQSkpDQp9DQoNCmRmMiA8LSBiaW5kX2NvbHMoDQogIGJpbmRfY29scyhjbGVhbmVkX2NvbHMpJT4lDQogICAgc2NhbGUoY2VudGVyID0gVFJVRSklPiUNCiAgICBkYXRhLmZyYW1lKCksDQogIHRyYWluLmZsYWdnZWQlPiUNCiAgICBkcGx5cjo6c2VsZWN0KGVuZHNfd2l0aCgnbmEuZmxhZycpKSU+JQ0KICAgIGRwbHlyOjpzZWxlY3QoLWluZGV4X25hLmZsYWcsIC10YXJnZXRfbmEuZmxhZykNCikNCg0KZGYzIDwtIGRmMiU+JQ0KICBtdXRhdGUoDQogICAgZml4ZWRhY2lkaXR5X291dC5mbGFnID0gaWZlbHNlKGlzLm5hKGZpeGVkYWNpZGl0eSkgJiBmaXhlZGFjaWRpdHlfbmEuZmxhZyA9PTAsMSwwKSwNCiAgICB2b2xhdGlsZWFjaWRpdHlfb3V0LmZsYWcgPSBpZmVsc2UoaXMubmEodm9sYXRpbGVhY2lkaXR5KSAmIHZvbGF0aWxlYWNpZGl0eV9uYS5mbGFnID09MCwxLDApLA0KICAgIGNpdHJpY2FjaWRfb3V0LmZsYWcgPSBpZmVsc2UoaXMubmEoY2l0cmljYWNpZCkgJiBjaXRyaWNhY2lkX25hLmZsYWcgPT0wLDEsMCksDQogICAgcmVzaWR1YWxzdWdhcl9vdXQuZmxhZyA9IGlmZWxzZShpcy5uYShyZXNpZHVhbHN1Z2FyKSAmIHJlc2lkdWFsc3VnYXJfbmEuZmxhZyA9PTAsMSwwKSwNCiAgICBjaGxvcmlkZXNfb3V0LmZsYWcgPSBpZmVsc2UoaXMubmEoY2hsb3JpZGVzKSAmIGNobG9yaWRlc19uYS5mbGFnID09MCwxLDApLA0KICAgIGZyZWVzdWxmdXJkaW94aWRlX291dC5mbGFnID0gaWZlbHNlKGlzLm5hKGZyZWVzdWxmdXJkaW94aWRlKSAmIGZyZWVzdWxmdXJkaW94aWRlX25hLmZsYWcgPT0wLDEsMCksDQogICAgdG90YWxzdWxmdXJkaW94aWRlX291dC5mbGFnID0gaWZlbHNlKGlzLm5hKHRvdGFsc3VsZnVyZGlveGlkZSkgJiB0b3RhbHN1bGZ1cmRpb3hpZGVfbmEuZmxhZyA9PTAsMSwwKSwNCiAgICBkZW5zaXR5X291dC5mbGFnID0gaWZlbHNlKGlzLm5hKGRlbnNpdHkpICYgZGVuc2l0eV9uYS5mbGFnID09MCwxLDApLA0KICAgIHBoX291dC5mbGFnID0gaWZlbHNlKGlzLm5hKHBoKSAmIHBoX25hLmZsYWcgPT0wLDEsMCksDQogICAgc3VscGhhdGVzX291dC5mbGFnID0gaWZlbHNlKGlzLm5hKHN1bHBoYXRlcykgJiBzdWxwaGF0ZXNfbmEuZmxhZyA9PTAsMSwwKSwNCiAgICBhbGNvaG9sX291dC5mbGFnID0gaWZlbHNlKGlzLm5hKGFsY29ob2wpICYgYWxjb2hvbF9uYS5mbGFnID09MCwxLDApLA0KICAgIGxhYmVsYXBwZWFsX291dC5mbGFnID0gaWZlbHNlKGlzLm5hKGxhYmVsYXBwZWFsKSAmIGxhYmVsYXBwZWFsX25hLmZsYWcgPT0wLDEsMCksDQogICAgYWNpZGluZGV4X291dC5mbGFnID0gaWZlbHNlKGlzLm5hKGFjaWRpbmRleCkgJiBhY2lkaW5kZXhfbmEuZmxhZyA9PTAsMSwwKSwNCiAgICBzdGFyc19vdXQuZmxhZyA9IGlmZWxzZShpcy5uYShzdGFycykgJiBzdGFyc19uYS5mbGFnID09MCwxLDApDQopDQoNCmxpYnJhcnkobWljZSkNCnRlbXBfZGYgPC0gbWljZShkZjMsIG1ldGhvZCA9ICdjYXJ0JywgbWF4aXQgPSAxKQ0KdHJhaW4gPC0gY29tcGxldGUodGVtcF9kZiklPiUNCiAgYmluZF9jb2xzKHRyYWluX3JhdyU+JWRwbHlyOjpzZWxlY3QoaW5kZXgsIHRhcmdldCkpJT4lDQogIGRwbHlyOjpzZWxlY3QoLXN0YXJzX291dC5mbGFnLCAtbGFiZWxhcHBlYWxfb3V0LmZsYWcsIC1kZW5zaXR5X25hLmZsYWcsDQogICAgICAgICAgICAgICAgLWxhYmVsYXBwZWFsX25hLmZsYWcsIC1hY2lkaW5kZXhfbmEuZmxhZywgLWZpeGVkYWNpZGl0eV9uYS5mbGFnLA0KICAgICAgICAgICAgICAgIC12b2xhdGlsZWFjaWRpdHlfbmEuZmxhZywgLWNpdHJpY2FjaWRfbmEuZmxhZykNCmBgYA0KDQpGaW5hbGx5LCB3ZSBjYW4gZXhhbWluZSAgdGhlIGRpc3RyaWJ1dGlvbnMgb2Ygb3VyIHZhcmlhYmxlcy4gRmxhZ3Mgd2l0aCBob21vZ2Vub3VzIHZhbHVlcyBhcmUgcmVtb3ZlZCBmcm9tIHRoZSB0cmFpbmluZyBzZXQgdG8gZ2V0IGEganVtcCBzdGFydCBvbiBzdGVwd2lzZSBkaW1lbnNpb24gcmVkdWN0aW9uIHVzZWQgaW4gdGhlIG1vZGVsIGRldmVsb3BtZW50IHN0YWdlLiANCg0KYGBge3IsIGVjaG8gPSBGQUxTRX0NCnN1bW1hcnkodHJhaW4pDQpgYGANCg0KIyBNb2RlbCBEZXZlbG9wbWVudA0KRml2ZSBtb2RlbHMgYXJlIGRldmVsb3BlZCBmb3IgdGhpcyBwYXBlci4gRmlyc3QgaXMgYSBQb2lzc29uIHJlZ3Jlc3Npb24gbW9kZWwgZm9sbG93ZWQgYnkgYSBuZWdhdGl2ZSBiaW5vbWlhbCByZWdyZXNzaW9uIG1vZGVsLiBUaGVuIHplcm8taW5mbGF0ZWQgdmVyc2lvbnMgb2YgZWFjaCBtb2RlbCBhcmUgZGV2ZWxvcGVkIGFzIHdlbGwgYXMgYSBtdWx0aXZhcmlhdGUgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwuIFJvb3QgbWVhbiBzcXVhcmVkIGVycm9yIChSTVNFKSwgYW5kIG1lYW4gYWJzb2x1dGUgZXJyb3IgKE1BRSkgYXJlIGNhbGN1bGF0ZWQgZm9yIGVhY2ggbW9kZWwgZm9yIHB1cnBvc2VzIG9mIG91dCBvZiBzYW1wbGUgbW9kZWwgZXZhbHVhdGlvbi4gDQoNCiMjIFBvaXNzb24gYW5kIE5lZ2F0aXZlIEJpbm9taWFsIFJlZ3Jlc3Npb24NClBvaXNzb24gcmVncmVzc2lvbiBtb2RlbHMgdXNlIHRoZSBsb2cgbGluayBmdW5jdGlvbiB0byBhcHByb3hpbWF0ZSByZWdyZXNzaW9uIHByb2Nlc3NlcyBmb3IgYSBjb3VudCB2YXJpYWJsZSBkaXN0cmlidXRlZCBzdWNoIHRoYXQgdGhlIHZhcmlhbmNlIGlzIGVxdWFsIHRvIHRoZSBtZWFuIFteMV0uIFRoZSBiYWNrd2FyZCBzdGVwd2lzZSBmZWF0dXJlIHNlbGVjdGlvbiBhbGdvcml0bSByZXR1cm5lZCBhIHBvaXNzb24gbW9kZWwgd2l0aCB0d2VsdmUgcHJlZGljdG9yc1teMl0uIE5vdGVhYmx5LCBtaXNzaW5nIHZhbHVlcyBmb3IgYHN0YXJzYCBhbmQgb3V0bGllciBgYWNpZGluZGV4YCB2YWx1ZXMgc2lnbmlmaWNhbnRseSBoYXJtIHNhbGVzIHdoaWxlIGBsYWJlbGFwcGVhbGAgYW5kIHByZXNlbnQgaGlnaGVyIHZhbHVlcyBmb3IgYHN0YXJzYCBsZWQgdG8gaGlnaGVyIHNhbGVzLiBOZWdhdGl2ZSBiaW5vbWlhbCBtb2RlbHMgdXNlIGEgZGlmZmVyZW50IHByb2JhYmlsaXR5IGRlbnNpdHkgZnVuY3Rpb24gdGhhbiBQb2lzc29uIHJlZ3Jlc3Npb24gdG8gYWNjb3VudCBmb3Igb3Zlci1kaXNwZXJzaW9uIG9mIHRoZSB0YXJnZXQgdmFyaWFibGUgKGFzIHdlIGhhdmUgaW4gdGhpcyBjYXNlKVteM10uIFRoZSBjb2VmZmljaWVudHMgZm9yIHRoaXMgbW9kZWwgYXJlIGlkZW50aWNhbCB0byB0aGF0IG9mIGEgUG9pc3NvbiBtb2RlbC4NCg0KYGBge3IsIGVjaG8gPSBGQUxTRX0NCmxpYnJhcnkoTUFTUykNCg0KYmFzZV9wb2lzcyA8LSBnbG0odGFyZ2V0IH4gLiwgZmFtaWx5PSJwb2lzc29uIiwgZGF0YT10cmFpbikNCnBvaXNzLmJhY2sgPC0gc3RlcEFJQyhiYXNlX3BvaXNzLCBkaXJlY3Rpb24gPSAnYmFja3dhcmQnKQ0KIyBzdW1tYXJ5KHBvaXNzLmJhY2spJGNhbGwNCnBvaXNzLm1vZCA8LSBnbG0oZm9ybXVsYSA9IHRhcmdldCB+IHZvbGF0aWxlYWNpZGl0eSArIHRvdGFsc3VsZnVyZGlveGlkZSArIA0KICAgIGRlbnNpdHkgKyBwaCArIHN1bHBoYXRlcyArIGFsY29ob2wgKyBsYWJlbGFwcGVhbCArIGFjaWRpbmRleCArIA0KICAgIHN0YXJzICsgc3RhcnNfbmEuZmxhZyArIHZvbGF0aWxlYWNpZGl0eV9vdXQuZmxhZyArIGFjaWRpbmRleF9vdXQuZmxhZywgDQogICAgZmFtaWx5ID0gInBvaXNzb24iLCBkYXRhID0gdHJhaW4pDQoNCnBvaXNzLmNvZWZmcyA8LSBkYXRhLmZyYW1lKHZhciA9IG5hbWVzKHBvaXNzLm1vZCRjb2VmZmljaWVudHMpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgY29lZmZpY2llbnQgPSBwb2lzcy5tb2QkY29lZmZpY2llbnRzKSU+JQ0KICBtdXRhdGUobWV0aG9kID0gJ1BvaXNzb24nKQ0KDQpuZWdiaW4ubW9kIDwtIGdsbS5uYihmb3JtdWxhID0gdGFyZ2V0IH4gdm9sYXRpbGVhY2lkaXR5ICsgdG90YWxzdWxmdXJkaW94aWRlICsgDQogICAgZGVuc2l0eSArIHBoICsgc3VscGhhdGVzICsgYWxjb2hvbCArIGxhYmVsYXBwZWFsICsgYWNpZGluZGV4ICsgDQogICAgc3RhcnMgKyBzdGFyc19uYS5mbGFnICsgdm9sYXRpbGVhY2lkaXR5X291dC5mbGFnICsgYWNpZGluZGV4X291dC5mbGFnLCANCiAgICBkYXRhID0gdHJhaW4pDQoNCm5lZ2Jpbi5jb2VmZnMgPC0gZGF0YS5mcmFtZSh2YXIgPSBuYW1lcyhuZWdiaW4ubW9kJGNvZWZmaWNpZW50cyksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgY29lZmZpY2llbnQgPSBuZWdiaW4ubW9kJGNvZWZmaWNpZW50cyklPiUNCiAgbXV0YXRlKG1ldGhvZCA9ICdOZWdhdGl2ZSBCaW5vbWlhbCcpDQoNCg0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeSh5YXp0aGVtZSkNCg0KZ2dwbG90KGJpbmRfcm93cyhuZWdiaW4uY29lZmZzLCBwb2lzcy5jb2VmZnMpLA0KICAgICAgIGFlcyh4ID0gcmVvcmRlcih2YXIsIGNvZWZmaWNpZW50KSwgeSA9IGNvZWZmaWNpZW50LCBmaWxsID0gbWV0aG9kKSkrDQogIGdlb21fY29sKHBvc2l0aW9uID0gJ2RvZGdlJykrDQogIGNvb3JkX2ZsaXAoKSsNCiAgbGFicyh5ID0gJ0NvZWZmaWNpZW50JywNCiAgICAgICB4ID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgIHRpdGxlID0gJ1JlZ3Jlc3Npb24gQ29lZmZpY2llbnRzJywNCiAgICAgICBzdWJ0aXRsZSA9ICdGZWF0dXJlcyBzZWxlY3RlZCB2aWEgYmFja3dhcmRzIHZhcmlhYmxlIHNlbGVjdGlvbicpKw0KICBzY2FsZV9maWxsX21hbnVhbChuYW1lID0gJ01ldGhvZCcsIHZhbHVlcyA9IHlhel9jb2xzWzQ6NV0pKw0KICB0aGVtZV95YXooKQ0KYGBgDQoNCiMjIFplcm8tSW5mbGF0ZWQgUmVncmVzc2lvbg0KUG9pc3NvbiBhbmQgbmVnYXRpdmUgYmlub21pYWwgbW9kZWxzIGNhbiBiZSBza2V3ZWQgYnkgYW4gb3ZlcmFidW5kYW5jZSBvZiB6ZXJvIHZhbHVlcy4gWmVyby1pbmZsYXRlZCBtb2RlbHMgYXNzdW1ldGhlIGRpc3RyaWJ1dGlvbiBoYXMgdHdvIHR5cGVzIG9mIHZhbHVlcyAtIHJpZ2h0ZnVsIHplcm8gbWVhc3VyZW1lbnRzIGFuZCBhIHNlcGFyYXRlIHNldCBvZiB2YWx1ZXMgdGhhdCBmb2xsb3dzIGEgbW9yZSB0eXBpY2FsIGRpc3RyaWJ1dGlvbi4gVGhlc2UgbW9kZWxzIGZpcnN0IHNvcnQgdmFsdWVzIGludG8gdGhlaXIgcHJvcGVyIGNhdGVnb3J5LCB0aGVuIHByZWRpY3QgdGhlaXIgb3V0Y29tZXMgdXNpbmcgc2VwYXJhdGUgc2V0cyBvZiBjb2VmZmljaWVudHMgZm9yIGVhY2hbXjRdLiBJbiB0aGlzIGNhc2UsIG92ZXIgMiw1MDAgd2luZXMgc29sZCB6ZXJvIGNhc2VzIHNvIHplcm8taW5mbGF0ZWQgbW9kZWxzIG1heSBpbXByb3ZlIHByZWRpY3RpdmUgcG93ZXIuIFRoZSBzYW1lIHZhcmlhYmxlcyBhcmUgdXNlZCBpbiB0aGUgemVybyBpbmZsYXRlZCBtb2RlbHMgYXMgdGhlIGluaXRpYWwgbW9kZWxzW141XS4gSW4gdGhpcyBjYXNlLCBzb21lIG9mIHRoZSBjb2VmZmljaWVudHMgYXJlIGZsaXBwZWQuIE1pc3NpbmcgcmF0aW5ncyBhbmQgb3V0bGllciBhY2lkaXR5IHZhbHVlcyBhcmUgc3Ryb25nIHBvc2l0aXZlIGluZmx1ZW5jZXMgb24gc2FsZXMuIExhYmVsIGFwcGVhbCBpcyBzaW1pbGFybHkgcG9zaXRpdmUgYXMgaW4gcHJldmlvdXMgbW9kZWxzLCBidXQgYWN0dWFsIHN0YXIgcmF0aW5ncyBhcmUgbmVnYXRpdmUuIEFnYWluLCB0aGUgY29lZmZpY2llbnRzIGZvciBwb2lzc29uIGFuZCBuZWdhdGl2ZSBiaW5vbWlhbCBtb2RlbHMgYXJlIGlkZW50aWNhbC4gDQoNCmBgYHtyLCBlY2hvID0gRkFMU0V9DQp6aW5wLm1vZCA8LSBwc2NsOjp6ZXJvaW5mbChmb3JtdWxhID0gdGFyZ2V0IH4gdm9sYXRpbGVhY2lkaXR5ICsgdG90YWxzdWxmdXJkaW94aWRlICsgDQogICAgZGVuc2l0eSArIHBoICsgc3VscGhhdGVzICsgYWxjb2hvbCArIGxhYmVsYXBwZWFsICsgYWNpZGluZGV4ICsgDQogICAgc3RhcnMgKyBzdGFyc19uYS5mbGFnICsgdm9sYXRpbGVhY2lkaXR5X291dC5mbGFnICsgYWNpZGluZGV4X291dC5mbGFnLCANCiAgICBkYXRhID0gdHJhaW4pDQoNCnppbnAuY29lZmZzIDwtIGRhdGEuZnJhbWUodmFyID0gbmFtZXMoemlucC5tb2QkY29lZmZpY2llbnRzJHplcm8pLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvZWZmaWNpZW50ID0gemlucC5tb2QkY29lZmZpY2llbnRzJHplcm8pJT4lDQogIG11dGF0ZShtZXRob2QgPSAnWmVyby1JbmZsYXRlZCBQb2lzc29uJykNCg0KemlubmcubW9kIDwtIHBzY2w6Onplcm9pbmZsKGZvcm11bGEgPSB0YXJnZXQgfiB2b2xhdGlsZWFjaWRpdHkgKyB0b3RhbHN1bGZ1cmRpb3hpZGUgKyANCiAgICBkZW5zaXR5ICsgcGggKyBzdWxwaGF0ZXMgKyBhbGNvaG9sICsgbGFiZWxhcHBlYWwgKyBhY2lkaW5kZXggKyANCiAgICBzdGFycyArIHN0YXJzX25hLmZsYWcgKyB2b2xhdGlsZWFjaWRpdHlfb3V0LmZsYWcgKyBhY2lkaW5kZXhfb3V0LmZsYWcsIA0KICAgIGRhdGEgPSB0cmFpbiwgZGlzdCA9ICJuZWdiaW4iLCBFTSA9IFRSVUUpDQoNCnppbm5nLmNvZWZmcyA8LSBkYXRhLmZyYW1lKHZhciA9IG5hbWVzKHppbm5nLm1vZCRjb2VmZmljaWVudHMkemVybyksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgY29lZmZpY2llbnQgPSB6aW5uZy5tb2QkY29lZmZpY2llbnRzJHplcm8pJT4lDQogIG11dGF0ZShtZXRob2QgPSAnWmVyby1JbmZsYXRlZCBOZWdhdGl2ZSBCaW5vbWlhbCcpDQoNCmdncGxvdChiaW5kX3Jvd3MoemlucC5jb2VmZnMsIHppbm5nLmNvZWZmcyksDQogICAgICAgYWVzKHggPSByZW9yZGVyKHZhciwgY29lZmZpY2llbnQpLCB5ID0gY29lZmZpY2llbnQsIGZpbGwgPSBtZXRob2QpKSsNCiAgZ2VvbV9jb2wocG9zaXRpb24gPSAnZG9kZ2UnKSsNCiAgY29vcmRfZmxpcCgpKw0KICBsYWJzKHkgPSAnQ29lZmZpY2llbnQnLA0KICAgICAgIHggPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgdGl0bGUgPSAnUmVncmVzc2lvbiBDb2VmZmljaWVudHMnLA0KICAgICAgIHN1YnRpdGxlID0gJ0ZlYXR1cmVzIHNlbGVjdGVkIHZpYSBiYWNrd2FyZHMgdmFyaWFibGUgc2VsZWN0aW9uJykrDQogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWUgPSAnTWV0aG9kJywgdmFsdWVzID0geWF6X2NvbHNbNDo1XSkrDQogIHRoZW1lX3lheigpDQpgYGANCg0KIyMgTXVsdGlwbGUgUmVncmVzc2lvbg0KVGhlIGZpbmFsIG1vZGVsIGF0dGVtcHRlZCBpcyBtdWx0aXBsZSBsaW5lYXIgcmVncmVzc2lvbi4gQSBuZXcgc3RlcHdpc2Ugc2VsZWN0aW9uIGFsZ29yaXRobSBpcyB1c2VkIHRvIHNlbGVjdCBtb2RlbCB2YXJpYWJsZXMuIFRoZSBzYW1lIHZhcmlhYmxlcyB3aXRoIHJlbGF0aXZlbHkgbGFyZ2UgY29lZmZpY2llbnRzIGluIHRoZSBwb2lzc29uIGFuZCBuZWdhdGl2ZSBiaW5vbWlhbCBtb2RlbHMgc3RhbmQgb3V0IGluIHRoZSBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbCwgYWx0aG91Z2ggYWRkaXRpb25hbCB2YXJpYWJsZXMgd2l0aCBzbWFsbGVyIGNvZWZmaWNpZW50cyBhcmUgaW5jbHVkZWQgYXMgd2VsbC4gDQoNCmBgYHtyLCBlY2hvID0gRkFMU0V9DQptbHIuYmFzZSA8LSBsbShmb3JtdWxhID0gdGFyZ2V0IH4gLiwgZGF0YSA9IHRyYWluKQ0KbWxyLnN0ZXAgPC0gc3RlcEFJQyhtbHIuYmFzZSwgZGlyZWN0aW9uID0gJ2JhY2t3YXJkJykNCm1sci5tb2QgPC0gbG0oZm9ybXVsYSA9IHRhcmdldCB+IHZvbGF0aWxlYWNpZGl0eSArIGNpdHJpY2FjaWQgKyBjaGxvcmlkZXMgKyANCiAgICB0b3RhbHN1bGZ1cmRpb3hpZGUgKyBkZW5zaXR5ICsgcGggKyBzdWxwaGF0ZXMgKyBhbGNvaG9sICsgDQogICAgbGFiZWxhcHBlYWwgKyBhY2lkaW5kZXggKyBzdGFycyArIHJlc2lkdWFsc3VnYXJfbmEuZmxhZyArIA0KICAgIHN0YXJzX25hLmZsYWcgKyB2b2xhdGlsZWFjaWRpdHlfb3V0LmZsYWcgKyBhY2lkaW5kZXhfb3V0LmZsYWcsIA0KICAgIGRhdGEgPSB0cmFpbikNCg0KbWxyLmNvZWZmcyA8LSBkYXRhLmZyYW1lKHZhciA9IG5hbWVzKG1sci5tb2QkY29lZmZpY2llbnRzKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICBjb2VmZmljaWVudCA9IG1sci5tb2QkY29lZmZpY2llbnRzKQ0KDQpnZ3Bsb3QobWxyLmNvZWZmcywgYWVzKHggPSByZW9yZGVyKHZhciwgY29lZmZpY2llbnQpLCB5ID0gY29lZmZpY2llbnQpKSsNCiAgZ2VvbV9jb2woZmlsbCA9IHlhel9jb2xzWzVdKSsNCiAgY29vcmRfZmxpcCgpKw0KICBsYWJzKHkgPSAnQ29lZmZpY2llbnQnLA0KICAgICAgIHggPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgdGl0bGUgPSAnTXVsdGlwbGUgTGluZWFyIFJlZ3Jlc3Npb24gUmVncmVzc2lvbiBDb2VmZmljaWVudHMnLA0KICAgICAgIHN1YnRpdGxlID0gJ0ZlYXR1cmVzIHNlbGVjdGVkIHZpYSBiYWNrd2FyZHMgdmFyaWFibGUgc2VsZWN0aW9uJykrDQogIHRoZW1lX3lheigpDQpgYGANCg0KIyBNb2RlbCBFdmFsdWF0aW9uDQpUbyBhc3Nlc3MgdGhlIGFjY3VyYWN5IG9mIHRoZSB2YXJpb3VzIG1vZGVscywgZWFjaCBtb2RlbCBpcyByZXBlYXRlZGx5IHRyYWluZWQgb24gcmFuZG9tIHNhbXBsZXMgb2YgdGhlIGRhdGEgYW5kIHRlc3RlZCBvdXQgb2Ygc2FtcGxlLiBJdGVyYXRpdmUgcmVzYW1wbGluZyBpcyB1c2VkIHRvIG1lYXN1cmUgdGhlIGVycm9yIHJhdGVzIG9mIGVhY2ggbW9kZWwgYW5kIGF0dGVtcHQgdG8gYWNjb3VudCBmb3IgdW5jZXJ0YWludHkgaW4gZXJyb3IgbWVhc3VyZW1lbnRzIGJ5IHByb2R1Y2luZyBkaXN0cmlidXRpb25zIG9mIHBvc3NpYmxlIGVycm9yIG1lYXN1cmVzIHJhdGhlciB0aGFuIHNpbmdsZSBwb2ludCBlc3RpbWF0ZXMuIFRoZSBkaXN0cmlidXRpb25zIG9mIE1BRSBhbmQgUk1TRSBmb3IgZWFjaCBtb2RlbCBhcmUgcHJlc2VudGVkIGJlbG93LiBTaW1pbGFyIHRvIHRoZSBhYm92ZSBjb2VmZmljaWVudHMsIHRoZSBlcnJvciByYXRlcyBmb3IgdGhlIHBvaXNzb24gYW5kIG5lZ2F0aXZlIGJpbm9taWFsIG1vZGVscyBhcmUgaWRlbnRpY2FsIHdpdGggdGhlIHR3byB6ZXJvLWluZmxhdGVkIG1vZGVscyBzdHJvbmdseSBvdXRwZXJmb3JtaW5nIHRoZSBub24tYWRqdXN0ZWQgbW9kZWxzLiBNdWx0aXBsZSByZWdyZXNzaW9uIGFsc28gbWFkZSBhIHN0cm9uZyBzaG93aW5nLCBidXQgZGlkIG5vdCBpbXByb3ZlIHByZWRpY3RpdmUgYWNjdXJhY3kgY29tcGFyZWQgdG8gdGhlIHR3byB6ZXJvLWluZmxhdGVkIG1vZGVscy4gT2YgdGhlIHR3byB3aW5uaW5nIG1vZGVscywgemVyby1pbmZsYXRlZCBuZWdhdGl2ZSBiaW5vbWlhbCBpcyBwcmVmZXJyZWQgZHVlIHRvIHRoZSBzbGlnaHQgb3ZlcmRpc3BlcnNpb24gb2YgdGhlIHRhcmdldCB2YXJpYWJsZS4NCg0KYGBge3IsIGZpZy5oZWlnaHQgPSA0LCBmaWcud2lkdGggPSA4LCBlY2hvID0gRkFMU0V9DQp0ZXN0X21vZHMgPC0gZnVuY3Rpb24oZGYsIGl0ZXJhdGlvbnMpew0KICBtZXRob2QgPC0gYyhyZXAoJ1BvaXNzb24nLCBpdGVyYXRpb25zKSwgcmVwKCdOZWdhdGl2ZSBCaW5vbWlhbCcsaXRlcmF0aW9ucyksDQogICAgICAgICAgICAgIHJlcCgnWmVyby1JbmZsYXRlZCBQb2lzc29uJywgaXRlcmF0aW9ucyksDQogICAgICAgICAgICAgIHJlcCgnWmVyby1JbmZsYXRlZCBOZWdhdGl2ZSBCaW5vbWlhbCcsIGl0ZXJhdGlvbnMpLA0KICAgICAgICAgICAgICByZXAoJ011bHRpcGxlIExpbmVhciBSZWdyZXNzaW9uJyxpdGVyYXRpb25zKSkNCiAgcG9pc3Mucm1zZSA8LSBjKCkNCiAgcG9pc3MubWFlIDwtIGMoKQ0KICBuZWdiaW4ucm1zZSA8LSBjKCkNCiAgbmVnYmluLm1hZSA8LSBjKCkNCiAgemlucC5ybXNlIDwtIGMoKQ0KICB6aW5wLm1hZSA8LSBjKCkNCiAgemlubmcucm1zZSA8LSBjKCkNCiAgemlubmcubWFlIDwtIGMoKQ0KICBtbHIucm1zZSA8LSBjKCkNCiAgbWxyLm1hZSA8LSBjKCkNCiAgZm9yKGkgaW4gc2VxKDEsaXRlcmF0aW9ucykpew0KICAgIHRlbXBkZiA8LSBzYW1wbGVfZnJhYyhkZiwgLjgpDQogICAgdGVzdCA8LSBzZXRkaWZmKGRmLCB0ZW1wZGYpDQogICAgcG9pc3MubW9kIDwtIGdsbShmb3JtdWxhID0gdGFyZ2V0IH4gdm9sYXRpbGVhY2lkaXR5ICsgdG90YWxzdWxmdXJkaW94aWRlICsNCiAgICAgICAgZGVuc2l0eSArIHBoICsgc3VscGhhdGVzICsgYWxjb2hvbCArIGxhYmVsYXBwZWFsICsgYWNpZGluZGV4ICsNCiAgICAgICAgc3RhcnMgKyBzdGFyc19uYS5mbGFnICsgdm9sYXRpbGVhY2lkaXR5X291dC5mbGFnICsgYWNpZGluZGV4X291dC5mbGFnLA0KICAgICAgICBmYW1pbHkgPSAicG9pc3NvbiIsIGRhdGEgPSB0ZW1wZGYpDQoNCiAgICBwb2lzcy5ybXNlW2ldIDwtIHNxcnQobWVhbigocHJlZGljdC5nbG0ocG9pc3MubW9kLCB0ZXN0KSAtIHRlc3QkdGFyZ2V0KV4yKSkNCiAgICBwb2lzcy5tYWVbaV0gPC0gbWVhbihhYnMocHJlZGljdC5nbG0ocG9pc3MubW9kLCB0ZXN0KSAtIHRlc3QkdGFyZ2V0KSkNCg0KDQogICAgbmVnYmluLm1vZCA8LSBnbG0ubmIoZm9ybXVsYSA9IHRhcmdldCB+IHZvbGF0aWxlYWNpZGl0eSArIHRvdGFsc3VsZnVyZGlveGlkZSArDQogICAgICAgIGRlbnNpdHkgKyBwaCArIHN1bHBoYXRlcyArIGFsY29ob2wgKyBsYWJlbGFwcGVhbCArIGFjaWRpbmRleCArDQogICAgICAgIHN0YXJzICsgc3RhcnNfbmEuZmxhZyArIHZvbGF0aWxlYWNpZGl0eV9vdXQuZmxhZyArIGFjaWRpbmRleF9vdXQuZmxhZywNCiAgICAgICAgZGF0YSA9IHRlbXBkZikNCg0KICAgIG5lZ2Jpbi5ybXNlW2ldIDwtIHNxcnQobWVhbigocHJlZGljdC5nbG0obmVnYmluLm1vZCwgdGVzdCkgLSB0ZXN0JHRhcmdldCleMikpDQogICAgbmVnYmluLm1hZVtpXSA8LSBtZWFuKGFicyhwcmVkaWN0LmdsbShuZWdiaW4ubW9kLCB0ZXN0KSAtIHRlc3QkdGFyZ2V0KSkNCg0KICAgIHppbnAubW9kIDwtIHBzY2w6Onplcm9pbmZsKGZvcm11bGEgPSB0YXJnZXQgfiB2b2xhdGlsZWFjaWRpdHkgKyB0b3RhbHN1bGZ1cmRpb3hpZGUgKw0KICAgICAgICBkZW5zaXR5ICsgcGggKyBzdWxwaGF0ZXMgKyBhbGNvaG9sICsgbGFiZWxhcHBlYWwgKyBhY2lkaW5kZXggKw0KICAgICAgICBzdGFycyArIHN0YXJzX25hLmZsYWcgKyB2b2xhdGlsZWFjaWRpdHlfb3V0LmZsYWcgKyBhY2lkaW5kZXhfb3V0LmZsYWcsDQogICAgICAgIGRhdGEgPSB0ZW1wZGYpDQoNCiAgICB6aW5wLnJtc2VbaV0gPC0gc3FydChtZWFuKChwcmVkaWN0KHppbnAubW9kLCB0ZXN0KSAtIHRlc3QkdGFyZ2V0KV4yKSkNCiAgICB6aW5wLm1hZVtpXSA8LSBtZWFuKGFicyhwcmVkaWN0KHppbnAubW9kLCB0ZXN0KSAtIHRlc3QkdGFyZ2V0KSkNCg0KICAgIHppbm5nLm1vZCA8LSBwc2NsOjp6ZXJvaW5mbChmb3JtdWxhID0gdGFyZ2V0IH4gdm9sYXRpbGVhY2lkaXR5ICsgdG90YWxzdWxmdXJkaW94aWRlICsNCiAgICAgICAgZGVuc2l0eSArIHBoICsgc3VscGhhdGVzICsgYWxjb2hvbCArIGxhYmVsYXBwZWFsICsgYWNpZGluZGV4ICsNCiAgICAgICAgc3RhcnMgKyBzdGFyc19uYS5mbGFnICsgdm9sYXRpbGVhY2lkaXR5X291dC5mbGFnICsgYWNpZGluZGV4X291dC5mbGFnLA0KICAgICAgICBkYXRhID0gdGVtcGRmLCBkaXN0ID0gIm5lZ2JpbiIsIEVNID0gVFJVRSkNCg0KICAgIHppbm5nLnJtc2VbaV0gPC0gc3FydChtZWFuKChwcmVkaWN0KHppbm5nLm1vZCwgdGVzdCkgLSB0ZXN0JHRhcmdldCleMikpDQogICAgemlubmcubWFlW2ldIDwtIG1lYW4oYWJzKHByZWRpY3QoemlubmcubW9kLCB0ZXN0KSAtIHRlc3QkdGFyZ2V0KSkNCg0KICAgIG1sci5tb2QgPC0gbG0oZm9ybXVsYSA9IHRhcmdldCB+IHZvbGF0aWxlYWNpZGl0eSArIGNpdHJpY2FjaWQgKyBjaGxvcmlkZXMgKw0KICAgICAgICB0b3RhbHN1bGZ1cmRpb3hpZGUgKyBkZW5zaXR5ICsgcGggKyBzdWxwaGF0ZXMgKyBhbGNvaG9sICsNCiAgICAgICAgbGFiZWxhcHBlYWwgKyBhY2lkaW5kZXggKyBzdGFycyArIHJlc2lkdWFsc3VnYXJfbmEuZmxhZyArDQogICAgICAgIHN0YXJzX25hLmZsYWcgKyB2b2xhdGlsZWFjaWRpdHlfb3V0LmZsYWcgKyBhY2lkaW5kZXhfb3V0LmZsYWcsDQogICAgICAgIGRhdGEgPSB0ZW1wZGYpDQogICAgbWxyLnJtc2VbaV0gPC0gc3FydChtZWFuKChwcmVkaWN0KG1sci5tb2QsIHRlc3QpIC0gdGVzdCR0YXJnZXQpXjIpKQ0KICAgIG1sci5tYWVbaV0gPC0gbWVhbihhYnMocHJlZGljdChtbHIubW9kLCB0ZXN0KSAtIHRlc3QkdGFyZ2V0KSkNCiAgfQ0KICByZXR1cm4oZGF0YS5mcmFtZSgNCiAgICBtZXRob2QsDQogICAgcm1zZSA9IGMocG9pc3Mucm1zZSwgbmVnYmluLnJtc2UsIHppbnAucm1zZSwgemlubmcucm1zZSwgbWxyLnJtc2UpLA0KICAgIG1hZSA9IGMocG9pc3MubWFlLCBuZWdiaW4ubWFlLCB6aW5wLm1hZSwgemlubmcubWFlLCBtbHIubWFlKQ0KICApKQ0KfQ0KbW9kZWxfZGlhZ25vc3RpY3MgPC0gdGVzdF9tb2RzKHRyYWluLCAyNTApDQoNCmxpYnJhcnkoZ3JpZEV4dHJhKQ0KbGlicmFyeShnZ3JpZGdlcykNCmxpYnJhcnkocmVzaGFwZTIpDQoNCmdncGxvdChtb2RlbF9kaWFnbm9zdGljcyU+JW1lbHQoaWQudmFycyA9ICdtZXRob2QnKSwgYWVzKHggPSB2YWx1ZSwgeSA9IG1ldGhvZCwgZmlsbCA9IHZhcmlhYmxlKSkrDQogIGdlb21fZGVuc2l0eV9yaWRnZXMoYWxwaGEgPSAuNzUpKw0KICBmYWNldF93cmFwKH50b3VwcGVyKHZhcmlhYmxlKSwgc2NhbGVzID0gJ2ZyZWUnKSsNCiAgdGhlbWVfeWF6KCkrDQogIGxhYnModGl0bGUgPSAnTW9kZWwgRGlhZ25vc3RpY3MgYnkgTWV0aG9kJywNCiAgICAgICB5ID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgIHggPSBlbGVtZW50X2JsYW5rKCkpKw0KICBzY2FsZV9maWxsX21hbnVhbChuYW1lID0gJ01ldHJpYycsIHZhbHVlcyA9IHlhel9jb2xzWzQ6NV0sIGxhYmVscyA9IGMoJ1JNU0UnLCAnTUFFJykpDQpgYGANCg0KIyBJbXBsZW1lbnRhdGlvbg0KVGhlIGZvbGxvd2luZyBmdW5jdGlvbiBhcHBsaWVzIHRoZSB6ZXJvLWluZmxhdGVkIG5lZ2F0aXZlIGJpbm9taWFsIHJlZ3Jlc3Npb24gbW9kZWwgdG8gb3V0IG9mIHNhbXBsZSBkYXRhIGFuZCBnZW5lcmF0ZXMgYSBDU1YgZmlsZSBpbiB0aGUgcHJvcGVyIHN1Ym1pc3Npb24gZm9ybWF0LiANCmBgYHtyLCBlY2hvID0gVFJVRX0NCmFwcGx5X21vZGVsIDwtIGZ1bmN0aW9uKGNzdil7DQogIHRyYWluLnJhdyA8LSByZWFkX2Nzdihjc3YpDQogIGNvbG5hbWVzKHRyYWluX3JhdykgPC0gdG9sb3dlcihjb2xuYW1lcyh0cmFpbl9yYXcpKQ0KICANCiAgdHJhaW4uZmxhZ2dlZCA8LSB0cmFpbl9yYXclPiUNCiAgICBtdXRhdGVfYWxsKGZ1bnMobmEuZmxhZyA9IGlmZWxzZShpcy5uYSguKSwxLDApKSkNCiAgaW50X2RmIDwtIHRyYWluLmZsYWdnZWQlPiUNCiAgICBkcGx5cjo6c2VsZWN0KC1pbmRleCwgLXRhcmdldCwgLWluZGV4X25hLmZsYWcsIC10YXJnZXRfbmEuZmxhZyklPiUNCiAgICBkcGx5cjo6c2VsZWN0X2lmKGlzLm51bWVyaWMpDQogIA0KICBjbGVhbmVkX2NvbHMgPC0gbGlzdCgpDQogIGZvcihjIGluIGNvbG5hbWVzKHRyYWluX3JhdyU+JQ0KICAgICAgICAgICAgICAgICAgICBkcGx5cjo6c2VsZWN0KC1pbmRleCwgLXRhcmdldCklPiUNCiAgICAgICAgICAgICAgICAgICAgZHBseXI6OnNlbGVjdF9pZihpcy5udW1lcmljKSkpew0KICAgIGNvbHVtbiA8LSB0cmFpbi5mbGFnZ2VkJT4lc2VsZWN0Xyhjb2wgPSBjKQ0KICAgIGlxciA8LSBxdWFudGlsZShjb2x1bW4kY29sLCBuYS5ybSA9IFQpWzRdIC0gcXVhbnRpbGUoY29sdW1uJGNvbCwgbmEucm0gPSBUKVsyXQ0KICAgIGxvdyA8LSBxdWFudGlsZShjb2x1bW4kY29sLCBuYS5ybSA9IFQpWzJdIC0gaXFyDQogICAgaGlnaCA8LSBxdWFudGlsZShjb2x1bW4kY29sLCBuYS5ybSA9IFQpWzRdICsgaXFyDQogICAgDQogICAgdmFscyA8LSBjKCkNCiAgICBmb3IoaSBpbiBzZXEoMTpucm93KGludF9kZikpKXsNCiAgICAgIGlmZWxzZShiZXR3ZWVuKGNvbHVtbiRjb2xbaV0sIGxvdyAtICgxLjUqaXFyKSwgaGlnaCArICgxLjUqaXFyKSksDQogICAgICAgICAgICAgdmFsc1tpXSA8LSBjb2x1bW4kY29sW2ldLCANCiAgICAgICAgICAgICBpZmVsc2UoaXMubmEoY29sdW1uJGNvbFtpXSksIHZhbHNbaV0gPC0gTkEsIHZhbHNbaV0gPC0gTkEpKQ0KICAgIH0NCiAgICANCiAgICBpZmVsc2UobGVuZ3RoKHZhbHMpID09IG5yb3coaW50X2RmKSwNCiAgICAgICAgICAgY2xlYW5lZF9jb2xzW1tjXV0gPC0gdmFscywgDQogICAgICAgICAgIGNsZWFuZWRfY29sc1tbY11dIDwtIGModmFscyxOQSkpDQogIH0NCiAgDQogIGRmMiA8LSBiaW5kX2NvbHMoDQogICAgYmluZF9jb2xzKGNsZWFuZWRfY29scyklPiUNCiAgICAgIHNjYWxlKGNlbnRlciA9IFRSVUUpJT4lDQogICAgICBkYXRhLmZyYW1lKCksDQogICAgdHJhaW4uZmxhZ2dlZCU+JQ0KICAgICAgZHBseXI6OnNlbGVjdChlbmRzX3dpdGgoJ25hLmZsYWcnKSklPiUNCiAgICAgIGRwbHlyOjpzZWxlY3QoLWluZGV4X25hLmZsYWcsIC10YXJnZXRfbmEuZmxhZykNCiAgKQ0KICANCiAgZGYzIDwtIGRmMiU+JQ0KICAgIG11dGF0ZSgNCiAgICAgIGZpeGVkYWNpZGl0eV9vdXQuZmxhZyA9IGlmZWxzZShpcy5uYShmaXhlZGFjaWRpdHkpICYgZml4ZWRhY2lkaXR5X25hLmZsYWcgPT0wLDEsMCksDQogICAgICB2b2xhdGlsZWFjaWRpdHlfb3V0LmZsYWcgPSBpZmVsc2UoaXMubmEodm9sYXRpbGVhY2lkaXR5KSAmIHZvbGF0aWxlYWNpZGl0eV9uYS5mbGFnID09MCwxLDApLA0KICAgICAgY2l0cmljYWNpZF9vdXQuZmxhZyA9IGlmZWxzZShpcy5uYShjaXRyaWNhY2lkKSAmIGNpdHJpY2FjaWRfbmEuZmxhZyA9PTAsMSwwKSwNCiAgICAgIHJlc2lkdWFsc3VnYXJfb3V0LmZsYWcgPSBpZmVsc2UoaXMubmEocmVzaWR1YWxzdWdhcikgJiByZXNpZHVhbHN1Z2FyX25hLmZsYWcgPT0wLDEsMCksDQogICAgICBjaGxvcmlkZXNfb3V0LmZsYWcgPSBpZmVsc2UoaXMubmEoY2hsb3JpZGVzKSAmIGNobG9yaWRlc19uYS5mbGFnID09MCwxLDApLA0KICAgICAgZnJlZXN1bGZ1cmRpb3hpZGVfb3V0LmZsYWcgPSBpZmVsc2UoaXMubmEoZnJlZXN1bGZ1cmRpb3hpZGUpICYgZnJlZXN1bGZ1cmRpb3hpZGVfbmEuZmxhZyA9PTAsMSwwKSwNCiAgICAgIHRvdGFsc3VsZnVyZGlveGlkZV9vdXQuZmxhZyA9IGlmZWxzZShpcy5uYSh0b3RhbHN1bGZ1cmRpb3hpZGUpICYgdG90YWxzdWxmdXJkaW94aWRlX25hLmZsYWcgPT0wLDEsMCksDQogICAgICBkZW5zaXR5X291dC5mbGFnID0gaWZlbHNlKGlzLm5hKGRlbnNpdHkpICYgZGVuc2l0eV9uYS5mbGFnID09MCwxLDApLA0KICAgICAgcGhfb3V0LmZsYWcgPSBpZmVsc2UoaXMubmEocGgpICYgcGhfbmEuZmxhZyA9PTAsMSwwKSwNCiAgICAgIHN1bHBoYXRlc19vdXQuZmxhZyA9IGlmZWxzZShpcy5uYShzdWxwaGF0ZXMpICYgc3VscGhhdGVzX25hLmZsYWcgPT0wLDEsMCksDQogICAgICBhbGNvaG9sX291dC5mbGFnID0gaWZlbHNlKGlzLm5hKGFsY29ob2wpICYgYWxjb2hvbF9uYS5mbGFnID09MCwxLDApLA0KICAgICAgbGFiZWxhcHBlYWxfb3V0LmZsYWcgPSBpZmVsc2UoaXMubmEobGFiZWxhcHBlYWwpICYgbGFiZWxhcHBlYWxfbmEuZmxhZyA9PTAsMSwwKSwNCiAgICAgIGFjaWRpbmRleF9vdXQuZmxhZyA9IGlmZWxzZShpcy5uYShhY2lkaW5kZXgpICYgYWNpZGluZGV4X25hLmZsYWcgPT0wLDEsMCksDQogICAgICBzdGFyc19vdXQuZmxhZyA9IGlmZWxzZShpcy5uYShzdGFycykgJiBzdGFyc19uYS5mbGFnID09MCwxLDApDQogICkNCiAgDQogIGxpYnJhcnkobWljZSkNCiAgdGVtcF9kZiA8LSBtaWNlKGRmMywgbWV0aG9kID0gJ2NhcnQnLCBtYXhpdCA9IDEpDQogIHRyYWluIDwtIGNvbXBsZXRlKHRlbXBfZGYpJT4lDQogICAgYmluZF9jb2xzKHRyYWluX3JhdyU+JWRwbHlyOjpzZWxlY3QoaW5kZXgpKSU+JQ0KICAgIGRwbHlyOjpzZWxlY3QoLXN0YXJzX291dC5mbGFnLCAtbGFiZWxhcHBlYWxfb3V0LmZsYWcsIC1kZW5zaXR5X25hLmZsYWcsDQogICAgICAgICAgICAgICAgICAtbGFiZWxhcHBlYWxfbmEuZmxhZywgLWFjaWRpbmRleF9uYS5mbGFnLCAtZml4ZWRhY2lkaXR5X25hLmZsYWcsDQogICAgICAgICAgICAgICAgICAtdm9sYXRpbGVhY2lkaXR5X25hLmZsYWcsIC1jaXRyaWNhY2lkX25hLmZsYWcpDQogIHJldHVybihkYXRhLmZyYW1lKA0KICAgIGluZGV4ID0gdHJhaW4kaW5kZXgsDQogICAgdGFyZ2V0X3AgPSBwcmVkaWN0KHppbm5nLm1vZCwgbmV3ZGF0YSA9IHRyYWluKQ0KICAgICkNCiAgKQ0KfQ0KDQp0ZXN0IDwtIGFwcGx5X21vZGVsKCdXaW5lX1JhbmRvbV9UZXN0LmNzdicpDQp3cml0ZS5jc3YodGVzdCwgJ3lhem1hbi5jc3YnLCByb3cubmFtZXMgPSBGQUxTRSkNCmBgYA0KDQojIENpdGF0aW9ucw0KW14xXTogSG9mZm1hbm4sIEpvaG4gUC4gR2VuZXJhbGl6ZWQgTGluZWFyIE1vZGVsczogYW4gQXBwbGllZCBBcHByb2FjaC4gUGVhcnNvbi9BbGx5biAmIEJhY29uLCAyMDA0Lg0KW14yXTogaHR0cHM6Ly9zdGF0cy5pZHJlLnVjbGEuZWR1L3IvZGFlL3BvaXNzb24tcmVncmVzc2lvbi8NClteM106IGh0dHBzOi8vc3RhdHMuaWRyZS51Y2xhLmVkdS9yL2RhZS9uZWdhdGl2ZS1iaW5vbWlhbC1yZWdyZXNzaW9uLw0KW140XTogaHR0cHM6Ly9zdGF0aXN0aWNhbGhvcml6b25zLmNvbS96ZXJvLWluZmxhdGVkLW1vZGVscw0KW141XTogaHR0cHM6Ly9zdGF0cy5pZHJlLnVjbGEuZWR1L3IvZGFlL3ppcC8=