OVERVIEW

The data science team of Salma Elshahawy, John K. Hancock, and Farhana Zahir have prepared the following technical report to address the issue of understanding ABC’s manufacturing process and its predictive factors. This report is the predictive value of the PH.

The report consists of the following:

PART 1: THE DATASETS

PART 2: DATA PREPARATION

PART 3: EXPERIMENTATION

PART 4: EVALUATE MODELS

PART 5: USE THE BEST MODEL TO FORECAST PH

PART 6: CONCLUSIONS

PART 1: THE DATASETS

In this section, we did the following:

    Import the Datasets
    Evaluate the Dataset
    Devise a Data Preparation Strategy

Evaluate the Dataset

After importing the Beverage Training dataset, we see that there are 2,571 observations consisiting of 32 predictor variables and one dependent variable, PH. We also see that “Brand Code” is a factor variable that will need to be handled as well as several observations with a number of NAs.

For the Beverage Testing dataset, we see 267 observations, the 32 predictors, and the dependent variable PH which is all NAs. This is the data that we will have to predict. Same as the training set, We also see that “Brand Code” is a character variable that will need to be handled as well as several observations with a number of NAs.

Beverage Training Data

## [1] 2571   33
## [1] "character"

Beverage Testing Data

Devise a Data Preparation Strategy

After analyzing the data, we devised the following processes in order to prepare the data for analysis

A. Isolate predictors from the dependent variable

B. Correct the Predictor Names

C. Create a data frame of numeric values only

D. Identify and Impute Missing Data

E. Identify and Address Outliers

F. Check for and remove correlated predictors

G. Identify Near Zero Variance Predictors

H. Impute missing values and Create dummy variables for Brand.Code

I. Impute missing data for Dependent Variable PH

PART 2: DATA PREPARATION

A. Isolate predictors from the dependent variables

For the training set, remove the predictor variable, PH and store it into the variable, y_train.

B. Correct the Predictor Names

Correct the space in the predictor names using the make.names function. The space in the names may be problematic. This was applied to both datasets.

##  [1] "Brand Code"        "Carb Volume"       "Fill Ounces"      
##  [4] "PC Volume"         "Carb Pressure"     "Carb Temp"        
##  [7] "PSC"               "PSC Fill"          "PSC CO2"          
## [10] "Mnf Flow"          "Carb Pressure1"    "Fill Pressure"    
## [13] "Hyd Pressure1"     "Hyd Pressure2"     "Hyd Pressure3"    
## [16] "Hyd Pressure4"     "Filler Level"      "Filler Speed"     
## [19] "Temperature"       "Usage cont"        "Carb Flow"        
## [22] "Density"           "MFR"               "Balling"          
## [25] "Pressure Vacuum"   "Oxygen Filler"     "Bowl Setpoint"    
## [28] "Pressure Setpoint" "Air Pressurer"     "Alch Rel"         
## [31] "Carb Rel"          "Balling Lvl"
##  [1] "Brand.Code"        "Carb.Volume"       "Fill.Ounces"      
##  [4] "PC.Volume"         "Carb.Pressure"     "Carb.Temp"        
##  [7] "PSC"               "PSC.Fill"          "PSC.CO2"          
## [10] "Mnf.Flow"          "Carb.Pressure1"    "Fill.Pressure"    
## [13] "Hyd.Pressure1"     "Hyd.Pressure2"     "Hyd.Pressure3"    
## [16] "Hyd.Pressure4"     "Filler.Level"      "Filler.Speed"     
## [19] "Temperature"       "Usage.cont"        "Carb.Flow"        
## [22] "Density"           "MFR"               "Balling"          
## [25] "Pressure.Vacuum"   "Oxygen.Filler"     "Bowl.Setpoint"    
## [28] "Pressure.Setpoint" "Air.Pressurer"     "Alch.Rel"         
## [31] "Carb.Rel"          "Balling.Lvl"
##  [1] "Brand.Code"        "Carb.Volume"       "Fill.Ounces"      
##  [4] "PC.Volume"         "Carb.Pressure"     "Carb.Temp"        
##  [7] "PSC"               "PSC.Fill"          "PSC.CO2"          
## [10] "Mnf.Flow"          "Carb.Pressure1"    "Fill.Pressure"    
## [13] "Hyd.Pressure1"     "Hyd.Pressure2"     "Hyd.Pressure3"    
## [16] "Hyd.Pressure4"     "Filler.Level"      "Filler.Speed"     
## [19] "Temperature"       "Usage.cont"        "Carb.Flow"        
## [22] "Density"           "MFR"               "Balling"          
## [25] "Pressure.Vacuum"   "Oxygen.Filler"     "Bowl.Setpoint"    
## [28] "Pressure.Setpoint" "Air.Pressurer"     "Alch.Rel"         
## [31] "Carb.Rel"          "Balling.Lvl"

C. Create a data frame of numeric values only

We saw earlier that Brand.Code is a categorical value. Because of that we subset the dataframe to remove it. We will handle this variable later.

D. Identify and Impute Missing Data

The predictor MFR has the most missing values at 212. I used knn imputation to handle missing values. After the knn imputation, there are still missing values for Brand.Code which will be handled in a later section.

Training Data

Predictors NAs
MFR 212
Filler.Speed 57
PC.Volume 39
PSC.CO2 39
Fill.Ounces 38
PSC 33

## [1] Predictors NAs       
## <0 rows> (or 0-length row.names)

F. Check for and remove correlated predictors

We identified five variables that are highly correlated with other variables at above .9. Highly correlated variables lead to Multicollinearity which reduces the precision of the estimate coefficients and weakens the statistical power of regression models.

## [1] "Balling"       "Hyd.Pressure3" "Balling.Lvl"   "Alch.Rel"     
## [5] "Bowl.Setpoint"

##  [1] "Carb.Volume"       "Fill.Ounces"       "PC.Volume"        
##  [4] "Carb.Pressure"     "Carb.Temp"         "PSC"              
##  [7] "PSC.Fill"          "PSC.CO2"           "Mnf.Flow"         
## [10] "Carb.Pressure1"    "Fill.Pressure"     "Hyd.Pressure1"    
## [13] "Hyd.Pressure2"     "Hyd.Pressure4"     "Filler.Level"     
## [16] "Filler.Speed"      "Temperature"       "Usage.cont"       
## [19] "Carb.Flow"         "Density"           "MFR"              
## [22] "Pressure.Vacuum"   "Oxygen.Filler"     "Pressure.Setpoint"
## [25] "Air.Pressurer"     "Carb.Rel"

G. Identify Near Zero Variance Predictors

Remove the zero variance predictor. There are no near zero variance predictors

## character(0)

H. Impute missing values and Create dummy variables for Brand.Code

Earlier, we saw that there are 120 missing values for Brand.Code, a factor variable. The imputation strategy here is to impute with the most frequent value, “B”. After imputation, Brand.Code was converted to dummy variables. The converted Brand.Code predictor is joined to the num_predictors_02.

## [1] 120
## [1] "A" "B" "C" "D"
## 
##    A    B    C    D 
##  293 1239  304  615
## factor(0)
## Levels: A B C D
## Dummy Variable Object
## 
## Formula: ~Brand.Code
## 1 variables, 1 factors
## Variables and levels will be separated by '.'
## A less than full rank encoding is used
Brand.Code.A Brand.Code.B Brand.Code.C Brand.Code.D
0 1 0 0
1 0 0 0
0 1 0 0
1 0 0 0
1 0 0 0
1 0 0 0

PART 3: EXPERIMENTATION

Split the Time Series

Before we begin with the experimentation, We split the training data into train and test sets

Modeling

We examined 12 models. We looked at Linear Models, Non Linear Regression Models, and Tree Based Models. For all of the models, MNF.Flow was the most important predictor with the exception of the bag tree model. Other consistently important predictors include predictor, Brand C and D. Residuals for each model appear random with no discernable patterns. In Part 4, we evaluated the metrics from each model.

Linear Models

Basic linear model

## 
## Call:
## lm(formula = .outcome ~ ., data = dat)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -0.50289 -0.07797  0.01076  0.08598  0.40461 
## 
## Coefficients: (1 not defined because of singularities)
##                    Estimate Std. Error t value Pr(>|t|)    
## (Intercept)        8.627224   0.014264 604.827  < 2e-16 ***
## Brand.Code.A      -0.084278   0.013618  -6.189 7.52e-10 ***
## Brand.Code.B      -0.097312   0.021305  -4.568 5.28e-06 ***
## Brand.Code.C      -0.214816   0.023129  -9.288  < 2e-16 ***
## Brand.Code.D             NA         NA      NA       NA    
## Carb.Volume       -0.027336   0.050197  -0.545 0.586112    
## Fill.Ounces       -0.045655   0.018421  -2.478 0.013288 *  
## PC.Volume         -0.040475   0.021897  -1.848 0.064714 .  
## Carb.Pressure      0.015385   0.075131   0.205 0.837768    
## Carb.Temp          0.037876   0.068645   0.552 0.581176    
## PSC               -0.013387   0.018070  -0.741 0.458875    
## PSC.Fill          -0.018989   0.018074  -1.051 0.293569    
## PSC.CO2           -0.014464   0.018446  -0.784 0.433067    
## Mnf.Flow          -0.377757   0.032001 -11.805  < 2e-16 ***
## Carb.Pressure1     0.158639   0.021034   7.542 7.35e-14 ***
## Fill.Pressure      0.078149   0.027974   2.794 0.005268 ** 
## Hyd.Pressure1      0.017788   0.028596   0.622 0.533995    
## Hyd.Pressure2      0.087998   0.036617   2.403 0.016354 *  
## Hyd.Pressure4      0.016160   0.027204   0.594 0.552564    
## Filler.Level       0.163086   0.025843   6.311 3.50e-10 ***
## Filler.Speed       0.006547   0.047481   0.138 0.890343    
## Temperature       -0.118405   0.022533  -5.255 1.66e-07 ***
## Usage.cont        -0.117676   0.021170  -5.559 3.13e-08 ***
## Carb.Flow          0.075019   0.024442   3.069 0.002178 ** 
## Density           -0.189315   0.048954  -3.867 0.000114 ***
## MFR               -0.019341   0.046950  -0.412 0.680434    
## Pressure.Vacuum   -0.024946   0.023264  -1.072 0.283727    
## Oxygen.Filler     -0.087742   0.023552  -3.725 0.000201 ***
## Pressure.Setpoint -0.092490   0.026740  -3.459 0.000555 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.1331 on 1773 degrees of freedom
## Multiple R-squared:  0.3996, Adjusted R-squared:  0.3905 
## F-statistic: 43.71 on 27 and 1773 DF,  p-value: < 2.2e-16
##   intercept      RMSE  Rsquared       MAE      RMSESD RsquaredSD       MAESD
## 1      TRUE 0.1341192 0.3840371 0.1050857 0.007437498 0.04013758 0.005812337
## lm variable importance
## 
##   only 20 most important variables shown (out of 27)
## 
##                   Overall
## Mnf.Flow          100.000
## Brand.Code.C       78.428
## Carb.Pressure1     63.463
## Filler.Level       52.908
## Brand.Code.A       51.864
## Usage.cont         46.464
## Temperature        43.859
## Brand.Code.B       37.968
## Density            31.965
## Oxygen.Filler      30.751
## Pressure.Setpoint  28.466
## Carb.Flow          25.126
## Fill.Pressure      22.763
## Fill.Ounces        20.062
## Hyd.Pressure2      19.417
## PC.Volume          14.661
## Pressure.Vacuum     8.009
## PSC.Fill            7.823
## PSC.CO2             5.539
## PSC                 5.168

Partial Least Squares or PLS

## Data:    X dimension: 1801 28 
##  Y dimension: 1801 1
## Fit method: oscorespls
## Number of components considered: 11
## TRAINING: % variance explained
##           1 comps  2 comps  3 comps  4 comps  5 comps  6 comps  7 comps
## X           12.48    32.90    46.73    54.34    59.16    65.70    69.89
## .outcome    29.79    32.31    35.64    38.01    39.02    39.35    39.63
##           8 comps  9 comps  10 comps  11 comps
## X           72.38     74.4     76.32     79.12
## .outcome    39.82     39.9     39.95     39.96

##    ncomp
## 11    11
##   ncomp      RMSE  Rsquared       MAE      RMSESD RsquaredSD       MAESD
## 1     8 0.1341466 0.3836032 0.1053703 0.007069507 0.03865624 0.005592642
## pls variable importance
## 
##   only 20 most important variables shown (out of 28)
## 
##                   Overall
## Mnf.Flow           100.00
## Brand.Code.C        88.42
## Brand.Code.D        74.32
## Filler.Level        71.42
## Usage.cont          63.67
## Pressure.Setpoint   59.66
## Brand.Code.B        46.25
## Fill.Pressure       45.66
## Hyd.Pressure2       41.42
## Pressure.Vacuum     39.57
## Carb.Flow           33.69
## Temperature         32.84
## Oxygen.Filler       31.18
## Brand.Code.A        30.20
## Carb.Pressure1      27.29
## Hyd.Pressure4       21.54
## PSC                 21.21
## Fill.Ounces         20.34
## Hyd.Pressure1       15.99
## Density             13.39

Ridge Regression

##             Length Class      Mode     
## call          4    -none-     call     
## actions      29    -none-     list     
## allset       28    -none-     numeric  
## beta.pure   812    -none-     numeric  
## vn           28    -none-     character
## mu            1    -none-     numeric  
## normx        28    -none-     numeric  
## meanx        28    -none-     numeric  
## lambda        1    -none-     numeric  
## L1norm       29    -none-     numeric  
## penalty      29    -none-     numeric  
## df           29    -none-     numeric  
## Cp           29    -none-     numeric  
## sigma2        1    -none-     numeric  
## xNames       28    -none-     character
## problemType   1    -none-     character
## tuneValue     1    data.frame list     
## obsLevels     1    -none-     logical  
## param         0    -none-     list

##       lambda
## 3 0.01428571
##       lambda      RMSE  Rsquared       MAE      RMSESD RsquaredSD       MAESD
## 3 0.01428571 0.1345357 0.3766957 0.1055482 0.007871785 0.05880301 0.005189457
## loess r-squared variable importance
## 
##   only 20 most important variables shown (out of 28)
## 
##                   Overall
## Mnf.Flow          100.000
## Filler.Level       72.742
## Usage.cont         66.059
## Pressure.Setpoint  56.947
## Fill.Pressure      46.135
## Brand.Code.C       35.539
## Oxygen.Filler      32.742
## Hyd.Pressure1      31.840
## Pressure.Vacuum    29.987
## Hyd.Pressure2      28.639
## Carb.Flow          26.675
## Temperature        20.496
## Density            18.039
## Carb.Pressure1     15.249
## Brand.Code.D       12.567
## Hyd.Pressure4       8.053
## MFR                 4.863
## PSC.Fill            4.778
## Fill.Ounces         4.747
## PSC                 4.534

Non Linear Regression

KNN

## k-Nearest Neighbors 
## 
## 1801 samples
##   28 predictor
## 
## No pre-processing
## Resampling: Cross-Validated (10 fold) 
## Summary of sample sizes: 1621, 1621, 1620, 1621, 1622, 1621, ... 
## Resampling results across tuning parameters:
## 
##   k   RMSE       Rsquared   MAE       
##    5  0.1276587  0.4439380  0.09763729
##    7  0.1272656  0.4438134  0.09823129
##    9  0.1272041  0.4432306  0.09897602
##   11  0.1280010  0.4368874  0.10013307
##   13  0.1287157  0.4307457  0.10098574
##   15  0.1293585  0.4255093  0.10171256
##   17  0.1294558  0.4256276  0.10188982
##   19  0.1300873  0.4196128  0.10257053
##   21  0.1304567  0.4176642  0.10312453
##   23  0.1308364  0.4149229  0.10359010
##   25  0.1312923  0.4108721  0.10382238
##   27  0.1318122  0.4065335  0.10419869
##   29  0.1324342  0.4007120  0.10474214
##   31  0.1325288  0.4002344  0.10482740
##   33  0.1329340  0.3969451  0.10527922
##   35  0.1334962  0.3915696  0.10577731
##   37  0.1337796  0.3887952  0.10604974
##   39  0.1341816  0.3848644  0.10626547
##   41  0.1344483  0.3827242  0.10642978
##   43  0.1345961  0.3813827  0.10653220
##   45  0.1348650  0.3790528  0.10681576
##   47  0.1353028  0.3750859  0.10723104
##   49  0.1355825  0.3725323  0.10751230
##   51  0.1358745  0.3699478  0.10768527
##   53  0.1360560  0.3683863  0.10784690
## 
## RMSE was used to select the optimal model using the smallest value.
## The final value used for the model was k = 9.
##      RMSE  Rsquared       MAE 
## 0.1337444 0.4297242 0.1008811
## loess r-squared variable importance
## 
##   only 20 most important variables shown (out of 28)
## 
##                   Overall
## Mnf.Flow          100.000
## Filler.Level       72.742
## Usage.cont         66.059
## Pressure.Setpoint  56.947
## Fill.Pressure      46.135
## Brand.Code.C       35.539
## Oxygen.Filler      32.742
## Hyd.Pressure1      31.840
## Pressure.Vacuum    29.987
## Hyd.Pressure2      28.639
## Carb.Flow          26.675
## Temperature        20.496
## Density            18.039
## Carb.Pressure1     15.249
## Brand.Code.D       12.567
## Hyd.Pressure4       8.053
## MFR                 4.863
## PSC.Fill            4.778
## Fill.Ounces         4.747
## PSC                 4.534

Neural Network

## Model Averaged Neural Network 
## 
## 1801 samples
##   28 predictor
## 
## No pre-processing
## Resampling: Cross-Validated (10 fold) 
## Summary of sample sizes: 1621, 1621, 1621, 1621, 1621, 1621, ... 
## Resampling results across tuning parameters:
## 
##   decay  size  RMSE      Rsquared    MAE     
##   0.00    1    7.545910         NaN  7.543986
##   0.00    2    7.545910         NaN  7.543986
##   0.00    3    7.545910         NaN  7.543986
##   0.00    4    7.545910         NaN  7.543986
##   0.00    5    7.545910         NaN  7.543986
##   0.00    6         NaN         NaN       NaN
##   0.00    7         NaN         NaN       NaN
##   0.00    8         NaN         NaN       NaN
##   0.00    9         NaN         NaN       NaN
##   0.00   10         NaN         NaN       NaN
##   0.01    1    7.545915  0.04495528  7.543991
##   0.01    2    7.545914  0.05111649  7.543990
##   0.01    3    7.545913  0.04902320  7.543989
##   0.01    4    7.545912  0.05115683  7.543989
##   0.01    5    7.545912  0.05224777  7.543988
##   0.01    6         NaN         NaN       NaN
##   0.01    7         NaN         NaN       NaN
##   0.01    8         NaN         NaN       NaN
##   0.01    9         NaN         NaN       NaN
##   0.01   10         NaN         NaN       NaN
##   1.00    1    7.546270  0.04373027  7.544346
##   1.00    2    7.546187  0.04283044  7.544263
##   1.00    3    7.546144  0.04257038  7.544221
##   1.00    4    7.546118  0.04282537  7.544194
##   1.00    5    7.546100  0.04167386  7.544176
##   1.00    6         NaN         NaN       NaN
##   1.00    7         NaN         NaN       NaN
##   1.00    8         NaN         NaN       NaN
##   1.00    9         NaN         NaN       NaN
##   1.00   10         NaN         NaN       NaN
## 
## Tuning parameter 'bag' was held constant at a value of FALSE
## RMSE was used to select the optimal model using the smallest value.
## The final values used for the model were size = 1, decay = 0 and bag = FALSE.
##             Length Class      Mode     
## model        5     -none-     list     
## repeats      1     -none-     numeric  
## bag          1     -none-     logical  
## seeds        5     -none-     numeric  
## names       28     -none-     character
## terms        3     terms      call     
## coefnames   28     -none-     character
## xlevels      0     -none-     list     
## xNames      28     -none-     character
## problemType  1     -none-     character
## tuneValue    3     data.frame list     
## obsLevels    1     -none-     logical  
## param        4     -none-     list
##   size decay   bag
## 1    1     0 FALSE
##     RMSE Rsquared      MAE 
## 7.551577       NA 7.549506
##  plotmo grid:    Brand.Code.A Brand.Code.B Brand.Code.C Brand.Code.D
##                             0            1            0            0
##  Carb.Volume  Fill.Ounces   PC.Volume Carb.Pressure    Carb.Temp         PSC
##  -0.04813177 -0.003197337 -0.01273667 -0.0007745981 -0.005304253 -0.02933575
##     PSC.Fill     PSC.CO2   Mnf.Flow Carb.Pressure1 Fill.Pressure Hyd.Pressure1
##  -0.03032371 -0.06544576 0.05637876     0.02422739   -0.07935299   -0.01488397
##  Hyd.Pressure2 Hyd.Pressure4 Filler.Level Filler.Speed Temperature Usage.cont
##     0.08985365  -0.004717008   0.08813245    0.0678782 -0.04144125 0.05617176
##   Carb.Flow    Density        MFR Pressure.Vacuum Oxygen.Filler
##  0.09978965 -0.1027022 0.06435543     -0.04451928    -0.0531649
##  Pressure.Setpoint
##         -0.1146138

## loess r-squared variable importance
## 
##   only 20 most important variables shown (out of 28)
## 
##                   Overall
## Mnf.Flow          100.000
## Filler.Level       72.742
## Usage.cont         66.059
## Pressure.Setpoint  56.947
## Fill.Pressure      46.135
## Brand.Code.C       35.539
## Oxygen.Filler      32.742
## Hyd.Pressure1      31.840
## Pressure.Vacuum    29.987
## Hyd.Pressure2      28.639
## Carb.Flow          26.675
## Temperature        20.496
## Density            18.039
## Carb.Pressure1     15.249
## Brand.Code.D       12.567
## Hyd.Pressure4       8.053
## MFR                 4.863
## PSC.Fill            4.778
## Fill.Ounces         4.747
## PSC                 4.534

Multivariate Adaptive Regression Splines (MARS)

## Multivariate Adaptive Regression Spline 
## 
## 1801 samples
##   28 predictor
## 
## No pre-processing
## Resampling: Cross-Validated (10 fold) 
## Summary of sample sizes: 1621, 1621, 1621, 1621, 1621, 1621, ... 
## Resampling results across tuning parameters:
## 
##   degree  nprune  RMSE       Rsquared   MAE       
##   1        2      0.1519885  0.2069925  0.12117213
##   1        3      0.1477618  0.2515970  0.11726343
##   1        4      0.1424258  0.3043244  0.11101682
##   1        5      0.1417822  0.3108149  0.11022990
##   1        6      0.1412256  0.3161642  0.10995544
##   1        7      0.1389010  0.3388722  0.10819473
##   1        8      0.1376190  0.3502356  0.10703124
##   1        9      0.1360027  0.3651705  0.10595323
##   1       10      0.1353731  0.3710853  0.10461416
##   1       11      0.1338847  0.3850378  0.10321531
##   1       12      0.1336934  0.3873287  0.10249648
##   1       13      0.1321820  0.4013358  0.10166137
##   1       14      0.1313438  0.4098211  0.10077596
##   1       15      0.1311334  0.4114799  0.10026809
##   1       16      0.1303441  0.4183990  0.09940438
##   1       17      0.1301287  0.4208661  0.09917623
##   1       18      0.1301095  0.4211559  0.09911392
##   1       19      0.1305966  0.4171911  0.09947776
##   1       20      0.1303873  0.4188426  0.09923815
##   1       21      0.1303715  0.4190653  0.09917720
##   1       22      0.1302760  0.4199451  0.09915404
##   1       23      0.1301411  0.4213366  0.09918895
##   1       24      0.1302279  0.4210868  0.09909730
##   1       25      0.1303454  0.4204012  0.09918533
##   1       26      0.1301412  0.4220622  0.09920596
##   1       27      0.1303435  0.4203209  0.09928267
##   1       28      0.1303182  0.4207295  0.09922077
##   1       29      0.1304451  0.4197610  0.09939381
##   1       30      0.1303191  0.4208290  0.09936981
##   1       31      0.1304550  0.4196858  0.09948421
##   1       32      0.1304538  0.4198923  0.09947104
##   1       33      0.1304889  0.4199242  0.09954173
##   1       34      0.1304756  0.4199250  0.09944804
##   1       35      0.1304902  0.4198425  0.09945640
##   1       36      0.1304902  0.4198425  0.09945640
##   1       37      0.1304902  0.4198425  0.09945640
##   1       38      0.1304902  0.4198425  0.09945640
##   2        2      0.1497988  0.2299110  0.11761859
##   2        3      0.1447098  0.2827332  0.11380271
##   2        4      0.1421922  0.3069325  0.11123244
##   2        5      0.1390474  0.3368872  0.11036701
##   2        6      0.1373709  0.3533328  0.10863264
##   2        7      0.1352897  0.3729062  0.10633755
##   2        8      0.1337972  0.3863367  0.10480632
##   2        9      0.1332891  0.3922441  0.10361520
##   2       10      0.1321610  0.4022180  0.10269268
##   2       11      0.1310175  0.4131337  0.10164173
##   2       12      0.1295949  0.4246106  0.10002913
##   2       13      0.1284230  0.4355194  0.09917013
##   2       14      0.1280578  0.4390897  0.09886663
##   2       15      0.1276429  0.4432846  0.09868998
##   2       16      0.1273042  0.4462102  0.09830851
##   2       17      0.1270549  0.4485341  0.09819020
##   2       18      0.1263373  0.4547638  0.09760116
##   2       19      0.1265575  0.4539746  0.09745340
##   2       20      0.1265358  0.4549548  0.09728968
##   2       21      0.1268974  0.4523892  0.09773736
##   2       22      0.1269260  0.4526335  0.09784708
##   2       23      0.1270210  0.4520443  0.09785512
##   2       24      0.1270968  0.4520115  0.09763839
##   2       25      0.1271764  0.4520938  0.09757818
##   2       26      0.1272201  0.4521101  0.09772548
##   2       27      0.1270748  0.4535961  0.09744366
##   2       28      0.1272515  0.4520723  0.09749072
##   2       29      0.1270928  0.4533569  0.09737816
##   2       30      0.1271823  0.4527102  0.09748969
##   2       31      0.1273158  0.4517213  0.09764303
##   2       32      0.1272847  0.4519497  0.09755007
##   2       33      0.1274342  0.4508035  0.09766991
##   2       34      0.1274968  0.4503143  0.09771274
##   2       35      0.1275724  0.4496776  0.09775650
##   2       36      0.1275724  0.4496776  0.09775650
##   2       37      0.1275724  0.4496776  0.09775650
##   2       38      0.1275724  0.4496776  0.09775650
## 
## RMSE was used to select the optimal model using the smallest value.
## The final values used for the model were nprune = 18 and degree = 2.
##      RMSE  Rsquared       MAE 
## 0.1351018 0.4170886 0.1032485
##  plotmo grid:    Brand.Code.A Brand.Code.B Brand.Code.C Brand.Code.D
##                             0            1            0            0
##  Carb.Volume  Fill.Ounces   PC.Volume Carb.Pressure    Carb.Temp         PSC
##  -0.04813177 -0.003197337 -0.01273667 -0.0007745981 -0.005304253 -0.02933575
##     PSC.Fill     PSC.CO2   Mnf.Flow Carb.Pressure1 Fill.Pressure Hyd.Pressure1
##  -0.03032371 -0.06544576 0.05637876     0.02422739   -0.07935299   -0.01488397
##  Hyd.Pressure2 Hyd.Pressure4 Filler.Level Filler.Speed Temperature Usage.cont
##     0.08985365  -0.004717008   0.08813245    0.0678782 -0.04144125 0.05617176
##   Carb.Flow    Density        MFR Pressure.Vacuum Oxygen.Filler
##  0.09978965 -0.1027022 0.06435543     -0.04451928    -0.0531649
##  Pressure.Setpoint
##         -0.1146138

## earth variable importance
## 
##                   Overall
## Mnf.Flow           100.00
## Brand.Code.C        71.17
## Filler.Level        58.17
## Carb.Flow           58.17
## Pressure.Vacuum     50.83
## Carb.Pressure1      50.83
## Brand.Code.A        40.31
## Temperature         34.89
## Hyd.Pressure1       33.37
## Density             27.25
## Brand.Code.D        23.81
## Usage.cont          20.59
## Pressure.Setpoint    0.00

Support Vector Machines (SVM)

## Support Vector Machines with Linear Kernel 
## 
## 1801 samples
##   28 predictor
## 
## No pre-processing
## Resampling: Cross-Validated (10 fold) 
## Summary of sample sizes: 1621, 1621, 1621, 1621, 1621, 1621, ... 
## Resampling results:
## 
##   RMSE       Rsquared   MAE      
##   0.1357066  0.3743779  0.1042566
## 
## Tuning parameter 'C' was held constant at a value of 1
##      RMSE  Rsquared       MAE 
## 0.1416567 0.3594363 0.1054432
##  plotmo grid:    Brand.Code.A Brand.Code.B Brand.Code.C Brand.Code.D
##                             0            1            0            0
##  Carb.Volume  Fill.Ounces   PC.Volume Carb.Pressure    Carb.Temp         PSC
##  -0.04813177 -0.003197337 -0.01273667 -0.0007745981 -0.005304253 -0.02933575
##     PSC.Fill     PSC.CO2   Mnf.Flow Carb.Pressure1 Fill.Pressure Hyd.Pressure1
##  -0.03032371 -0.06544576 0.05637876     0.02422739   -0.07935299   -0.01488397
##  Hyd.Pressure2 Hyd.Pressure4 Filler.Level Filler.Speed Temperature Usage.cont
##     0.08985365  -0.004717008   0.08813245    0.0678782 -0.04144125 0.05617176
##   Carb.Flow    Density        MFR Pressure.Vacuum Oxygen.Filler
##  0.09978965 -0.1027022 0.06435543     -0.04451928    -0.0531649
##  Pressure.Setpoint
##         -0.1146138

## loess r-squared variable importance
## 
##   only 20 most important variables shown (out of 28)
## 
##                   Overall
## Mnf.Flow          100.000
## Filler.Level       72.742
## Usage.cont         66.059
## Pressure.Setpoint  56.947
## Fill.Pressure      46.135
## Brand.Code.C       35.539
## Oxygen.Filler      32.742
## Hyd.Pressure1      31.840
## Pressure.Vacuum    29.987
## Hyd.Pressure2      28.639
## Carb.Flow          26.675
## Temperature        20.496
## Density            18.039
## Carb.Pressure1     15.249
## Brand.Code.D       12.567
## Hyd.Pressure4       8.053
## MFR                 4.863
## PSC.Fill            4.778
## Fill.Ounces         4.747
## PSC                 4.534

Tree Based Models

## 
## Call:
## summary.resamples(object = resamples)
## 
## Models: CondInfTree, BaggedTree, BoostedTree, Cubist 
## Number of resamples: 10 
## 
## MAE 
##                   Min.    1st Qu.     Median       Mean    3rd Qu.       Max.
## CondInfTree 0.08473608 0.08861893 0.09678825 0.09472839 0.09863304 0.10685968
## BaggedTree  0.07378771 0.07915025 0.08041089 0.08156085 0.08497580 0.08983852
## BoostedTree 0.07957218 0.08495870 0.08981795 0.08876060 0.09242079 0.09642614
## Cubist      0.07151854 0.07393146 0.07585573 0.07787389 0.08160881 0.08865890
##             NA's
## CondInfTree    0
## BaggedTree     0
## BoostedTree    0
## Cubist         0
## 
## RMSE 
##                   Min.   1st Qu.    Median      Mean   3rd Qu.      Max. NA's
## CondInfTree 0.11272576 0.1192896 0.1294941 0.1261672 0.1310877 0.1371878    0
## BaggedTree  0.09986836 0.1043449 0.1101592 0.1101612 0.1149333 0.1206988    0
## BoostedTree 0.10603498 0.1142042 0.1156811 0.1164589 0.1205235 0.1277245    0
## Cubist      0.09965847 0.1023029 0.1042195 0.1056102 0.1082524 0.1153712    0
## 
## Rsquared 
##                  Min.   1st Qu.    Median      Mean   3rd Qu.      Max. NA's
## CondInfTree 0.3979264 0.4302470 0.4657415 0.4667827 0.5132486 0.5242436    0
## BaggedTree  0.5416168 0.5580249 0.5838407 0.5875821 0.6033266 0.6660155    0
## BoostedTree 0.4940727 0.5174132 0.5323353 0.5393031 0.5516800 0.6073912    0
## Cubist      0.5622753 0.5996309 0.6180414 0.6179799 0.6280174 0.6935418    0

Single Tree Models - cTree

##                      Overall
## Mnf.Flow          100.000000
## Filler.Level       72.742140
## Usage.cont         66.059001
## Pressure.Setpoint  56.946619
## Fill.Pressure      46.135071
## Brand.Code.C       35.538704
## Oxygen.Filler      32.741803
## Hyd.Pressure1      31.839796
## Pressure.Vacuum    29.986506
## Hyd.Pressure2      28.639461
## Carb.Flow          26.675011
## Temperature        20.495620
## Density            18.039226
## Carb.Pressure1     15.248864
## Brand.Code.D       12.567132
## Hyd.Pressure4       8.053456
## MFR                 4.862906
## PSC.Fill            4.778115
## Fill.Ounces         4.747072
## PSC                 4.534167

##       RMSE   Rsquared        MAE 
## 0.13074344 0.46173157 0.09763883

Bagged Trees - baggedTreeModel

##                  Overall
## Carb.Volume    100.00000
## PC.Volume       99.74968
## Fill.Ounces     97.92218
## Carb.Pressure   92.19660
## Carb.Temp       88.07704
## PSC             68.47249
## PSC.Fill        61.53297
## PSC.CO2         54.13883
## Mnf.Flow        49.23656
## Carb.Pressure1  45.20649
## Fill.Pressure   41.05836
## Hyd.Pressure1   36.46444
## Hyd.Pressure4   32.16729
## Hyd.Pressure2   28.68248
## Filler.Level    28.45692
## Temperature     25.64155
## Filler.Speed    24.32391
## Usage.cont      21.10092
## Density         20.21854
## Carb.Flow       19.80553

## Bagged CART 
## 
## 1801 samples
##   28 predictor
## 
## No pre-processing
## Resampling: Cross-Validated (10 fold) 
## Summary of sample sizes: 1621, 1621, 1621, 1621, 1621, 1621, ... 
## Resampling results:
## 
##   RMSE       Rsquared   MAE       
##   0.1101612  0.5875821  0.08156085

Random Forest - rfModel

##                    Overall
## Mnf.Flow          61.55518
## Usage.cont        52.20738
## Brand.Code.C      50.95006
## Pressure.Vacuum   43.22820
## Filler.Level      39.82271
## Temperature       38.31964
## Carb.Pressure1    36.84317
## Oxygen.Filler     36.75693
## Density           34.53080
## Filler.Speed      28.01853
## Hyd.Pressure1     27.23747
## Carb.Flow         27.21569
## Pressure.Setpoint 25.79915
## Hyd.Pressure2     24.44479
## Carb.Volume       22.75455
## Brand.Code.D      22.48350
## Fill.Pressure     21.60299
## MFR               19.85477
## Hyd.Pressure4     18.94150
## Brand.Code.B      17.17734

##       RMSE   Rsquared        MAE 
## 0.11744003 0.58984937 0.08682643

Gradient Boost Model - gbmModel

##                      Overall
## Mnf.Flow          100.000000
## Pressure.Vacuum    33.129929
## Brand.Code.C       32.497261
## Temperature        31.832688
## Density            30.635680
## Usage.cont         26.303913
## Filler.Level       21.751844
## Carb.Pressure1     20.375560
## Oxygen.Filler      17.319583
## Brand.Code.D        9.621853
## PSC.Fill            9.240987
## MFR                 8.478939
## Filler.Speed        7.836084
## Carb.Flow           7.276953
## Pressure.Setpoint   7.209489
## Hyd.Pressure4       6.917653
## Hyd.Pressure1       6.706023
## Fill.Pressure       6.670622
## Fill.Ounces         6.218982
## Carb.Temp           5.983509

##       RMSE   Rsquared        MAE 
## 0.12161792 0.53764990 0.09197442

Cubist Model - cubistModel

##                     Overall
## Mnf.Flow          100.00000
## Density            72.02797
## Pressure.Vacuum    66.43357
## Filler.Level       62.93706
## Temperature        53.14685
## Usage.cont         51.04895
## Carb.Pressure1     47.55245
## Oxygen.Filler      44.75524
## Brand.Code.C       40.55944
## Hyd.Pressure2      35.66434
## Carb.Flow          34.96503
## Pressure.Setpoint  34.96503
## Carb.Pressure      28.67133
## Hyd.Pressure1      25.87413
## Carb.Volume        23.77622
## Carb.Temp          23.77622
## Fill.Pressure      23.07692
## Brand.Code.D       20.97902
## Filler.Speed       20.27972
## Hyd.Pressure4      16.08392

##       RMSE   Rsquared        MAE 
## 0.11424048 0.58481353 0.08299015

##  plotmo grid:    Brand.Code.A Brand.Code.B Brand.Code.C Brand.Code.D
##                             0            1            0            0
##  Carb.Volume  Fill.Ounces   PC.Volume Carb.Pressure    Carb.Temp         PSC
##  -0.04813177 -0.003197337 -0.01273667 -0.0007745981 -0.005304253 -0.02933575
##     PSC.Fill     PSC.CO2   Mnf.Flow Carb.Pressure1 Fill.Pressure Hyd.Pressure1
##  -0.03032371 -0.06544576 0.05637876     0.02422739   -0.07935299   -0.01488397
##  Hyd.Pressure2 Hyd.Pressure4 Filler.Level Filler.Speed Temperature Usage.cont
##     0.08985365  -0.004717008   0.08813245    0.0678782 -0.04144125 0.05617176
##   Carb.Flow    Density        MFR Pressure.Vacuum Oxygen.Filler
##  0.09978965 -0.1027022 0.06435543     -0.04451928    -0.0531649
##  Pressure.Setpoint
##         -0.1146138

PART 4: EVALUATE MODELS

From our experimentation with 12 different models, we saw that the Cubist model had the lowest RMSE (0.10976) value as well as the lowest MAE value (0.081). It also had the highest Rsquared value (0.601).

Model RMSE Rsquared MAE
baggedTree Model 0.110161222176994 0.587582121274168 0.0815608529120577
Cubist Model 0.114240479875035 0.584813533518392 0.082990152334238
Random Forest Model 0.117440026812849 0.589849374176466 0.086826432034632
Gradient Boost Model 0.121617924199404 0.537649903321855 0.091974418000837
KNN 0.127265605246562 0.443813404567053 0.0982312937654356
cTree Model 0.130743440773608 0.461731568560653 0.0976388347059531
Linear Model 0.134119241211064 0.384037147180449 0.105085743503772
Partial Least Square 0.134146647737492 0.383603238597427 0.105370300144996
Ridge Regression 0.134535682073648 0.376695740032475 0.105548235473887
Multivariate Adaptive Regression Spline 0.135101845051539 0.417088631109576 0.103248537986403
Support Vector Machines - Linear 0.141656699921108 0.359436260223437 0.105443174125596
Neural Network 7.55157663076987 NA 7.54950649350649

PART 5: USE THE BEST MODEL TO FORECAST PH

We will use the Cubist model against the Student evaluation data and make predictions of the PH variable.

First, as we did with the Student train data, we have to convert the Brand.Code categorical value in the Student evaluation data to Dummy variables.

## Dummy Variable Object
## 
## Formula: ~Brand.Code
## 1 variables, 0 factors
## Variables and levels will be separated by '.'
## A less than full rank encoding is used
x
8.76
8.72
8.76
8.85
8.76
8.76
8.73
8.91
8.72
8.90

PART 6: CONCLUSIONS

The data science team found that the Cubist model is the best for predicting the PH value. The most important predictors from this model are shown in the visualization below. The top five predictors are Mnf.Flow, Density, Temperature, Pressure.Vacuum, and Filler Level. Two discrete categorical factors, Brand Codes C and D, are also in the most important predictors.

We have exported the predicted PH values in the attached excel file.

LS0tDQp0aXRsZTogIlByb2plY3QgVHdvOiAgVW5kZXJzdGFuZGluZyBQcmVkaWN0aXZlIEZhY3RvcnMgZm9yIEFCQyBCZXZlcmFnZSINCmF1dGhvcjogIlNhbG1hIEVsc2hhaGF3eSwgSm9obiBLLiBIYW5jb2NrLCBhbmQgRmFyaGFuYSBaYWhpciINCmRhdGU6ICI1LzIzLzIwMjEiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6DQogICAgY29kZV9kb3dubG9hZDogeWVzDQogICAgY29kZV9mb2xkaW5nOiBzaG93DQogICAgaGlnaGxpZ2h0OiBweWdtZW50cw0KICAgIG51bWJlcl9zZWN0aW9uczogbm8NCiAgICB0aGVtZTogY2VydWxlYW4NCiAgICB0b2M6IHllcw0KICAgIHRvY19mbG9hdDogeWVzDQogIHBkZl9kb2N1bWVudDoNCiAgICB0b2M6IHllcw0KLS0tDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQ0KYGBgDQoNCg0KYGBge3IsIGluY2x1ZGU9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoZ2djb3JycGxvdCkNCmxpYnJhcnkoY2FyZXQpDQpsaWJyYXJ5KGVsYXN0aWNuZXQpDQpsaWJyYXJ5KGxhcnMpDQpsaWJyYXJ5KHBscykNCmxpYnJhcnkobmFuaWFyKQ0KbGlicmFyeShoZWF0bWFwbHkpDQpsaWJyYXJ5KFZJTSkNCmxpYnJhcnkoSUNTTlApDQpsaWJyYXJ5KHJzYW1wbGUpDQpsaWJyYXJ5KGdsbW5ldCkNCmxpYnJhcnkoIm1pY2UiKQ0KbGlicmFyeSgiZTEwNzEiKQ0KbGlicmFyeShSQU5OKQ0KbGlicmFyeShlYXJ0aCkNCmxpYnJhcnkoa2VybmxhYikNCmxpYnJhcnkobm5ldCkNCg0KbGlicmFyeShDdWJpc3QpDQpsaWJyYXJ5KGdibSkNCmxpYnJhcnkoaXByZWQpDQpsaWJyYXJ5KHBhcnR5KQ0KbGlicmFyeShwYXJ0eWtpdCkNCmxpYnJhcnkocmFuZG9tRm9yZXN0KQ0KbGlicmFyeShycGFydCkNCmxpYnJhcnkoUldla2EpDQoNCmxpYnJhcnkocmVzaGFwZTIpDQpsaWJyYXJ5KERNd1IyKQ0KbGlicmFyeSh2aXApDQpsaWJyYXJ5KGh0dHIpDQoNCmBgYA0KDQojIyBPVkVSVklFVw0KDQpUaGUgZGF0YSBzY2llbmNlIHRlYW0gb2YgU2FsbWEgRWxzaGFoYXd5LCBKb2huIEsuIEhhbmNvY2ssIGFuZCBGYXJoYW5hIFphaGlyIGhhdmUgcHJlcGFyZWQgdGhlIGZvbGxvd2luZyB0ZWNobmljYWwgcmVwb3J0IHRvIGFkZHJlc3MgdGhlIGlzc3VlIG9mIHVuZGVyc3RhbmRpbmcgQUJDJ3MgbWFudWZhY3R1cmluZyBwcm9jZXNzIGFuZCBpdHMgcHJlZGljdGl2ZSBmYWN0b3JzLiBUaGlzIHJlcG9ydCBpcyB0aGUgcHJlZGljdGl2ZSB2YWx1ZSBvZiB0aGUgUEguIA0KDQpUaGUgcmVwb3J0IGNvbnNpc3RzIG9mIHRoZSBmb2xsb3dpbmc6DQoNCjxiPjx1PlBBUlQgMTogVEhFIERBVEFTRVRTIDwvdT48L2I+DQoNCjxiPjx1PlBBUlQgMjogREFUQSBQUkVQQVJBVElPTiA8L3U+PC9iPg0KDQo8Yj48dT5QQVJUIDM6IEVYUEVSSU1FTlRBVElPTiA8L3U+PC9iPiANCg0KPGI+PHU+UEFSVCA0OiBFVkFMVUFURSBNT0RFTFMgPC91PjwvYj4gDQoNCjx1PjxiPlBBUlQgNTogVVNFIFRIRSBCRVNUIE1PREVMIFRPIEZPUkVDQVNUIFBIPC91PjwvYj4gDQoNCjx1PjxiPlBBUlQgNjogQ09OQ0xVU0lPTlMgPC91PjwvYj4gDQoNCg0KIyMgUEFSVCAxOiBUSEUgREFUQVNFVFMNCg0KSW4gdGhpcyBzZWN0aW9uLCB3ZSBkaWQgdGhlIGZvbGxvd2luZzoNCiAgIA0KICAgDQogICAgICAgIEltcG9ydCB0aGUgRGF0YXNldHMNCiAgICAgICAgRXZhbHVhdGUgdGhlIERhdGFzZXQNCiAgICAgICAgRGV2aXNlIGEgRGF0YSBQcmVwYXJhdGlvbiBTdHJhdGVneQ0KDQoNCg0KIyMjIEltcG9ydCB0aGUgRGF0YQ0KDQpUaGUgZXhjZWwgZmlsZXMsIFN0dWRlbnREYXRhLnhsc3ggYW5kIFN0dWRlbnRFdmFsdWF0aW9uLnhsc3gsIGFyZSBob3N0ZWQgb24gdGhlIHRlYW0ncyBHaXRodWIgcGFnZS4gIEhlcmUsIHRoZXkncmUgZG93bmxvYWRlZCBhbmQgcmVhZCBpbnRvIHRoZSBkYXRhZnJhbWVzLCBiZXZlcmFnZV90cmFpbmluZ19kYXRhIGFuZCBiZXZlcmFnZV90ZXN0X2RhdGEuIA0KDQpgYGB7cn0NCnRlbXBfdHJhaW5fZmlsZSA8LSB0ZW1wZmlsZShwYXR0ZXJuPSJTdHVkZW50RGF0YSIsIGZpbGVleHQgPSAiLnhsc3giKQ0KdGVtcF9ldmFsX2ZpbGUgPC0gdGVtcGZpbGUocGF0dGVybj0iU3R1ZGVudEV2YWx1YXRpb24iLCBmaWxlZXh0ID0gIi54bHN4IikNCg0Kc3R1ZGVudF90cmFpbiA8LSAgImh0dHBzOi8vZ2l0aHViLmNvbS9Kb2huS0hhbmNvY2svQ1VOWV9EQVRBNjI0X1Byb2plY3QyL2Jsb2IvbWFpbi9yYXcvU3R1ZGVudERhdGEueGxzeD9yYXc9dHJ1ZSINCnN0dWRlbnRfZXZhbCA8LSAgICJodHRwczovL2dpdGh1Yi5jb20vSm9obktIYW5jb2NrL0NVTllfREFUQTYyNF9Qcm9qZWN0Mi9ibG9iL21haW4vcmF3L1N0dWRlbnRFdmFsdWF0aW9uLnhsc3g/cmF3PXRydWUiDQoNCg0KDQpzdHVkZW50X2RhdGEgPC0gR0VUKHN0dWRlbnRfdHJhaW4sDQogICAgICAgICAgICAgICBhdXRoZW50aWNhdGUoU3lzLmdldGVudigiR0lUSFVCX1BBVCIpLCAiIiksDQogICAgICAgICAgICAgICB3cml0ZV9kaXNrKHBhdGggPSB0ZW1wX3RyYWluX2ZpbGUpKQ0KDQpzdHVkZW50X2V2YWwgPC0gIEdFVChzdHVkZW50X2V2YWwsDQogICAgICAgICAgICAgICBhdXRoZW50aWNhdGUoU3lzLmdldGVudigiR0lUSFVCX1BBVCIpLCAiIiksDQogICAgICAgICAgICAgICB3cml0ZV9kaXNrKHBhdGggPSB0ZW1wX2V2YWxfZmlsZSkpDQoNCg0KDQpgYGANCg0KYGBge3IgaW5jbHVkZT1GQUxTRX0NCmJldmVyYWdlX3RyYWluaW5nX2RhdGEgPC0gcmVhZHhsOjpyZWFkX2V4Y2VsKHRlbXBfdHJhaW5fZmlsZSxza2lwPTApDQpgYGANCg0KDQpgYGB7ciwgaW5jbHVkZT1GQUxTRX0NCmJldmVyYWdlX3Rlc3RfZGF0YSA8LSByZWFkeGw6OnJlYWRfZXhjZWwodGVtcF9ldmFsX2ZpbGUsIHNraXA9MCkNCg0KYGBgDQoNCg0KDQoNCg0KIyMjIEV2YWx1YXRlIHRoZSBEYXRhc2V0DQpBZnRlciBpbXBvcnRpbmcgdGhlIEJldmVyYWdlIFRyYWluaW5nIGRhdGFzZXQsIHdlIHNlZSB0aGF0IHRoZXJlIGFyZSAyLDU3MSBvYnNlcnZhdGlvbnMgY29uc2lzaXRpbmcgb2YgMzIgcHJlZGljdG9yIHZhcmlhYmxlcyBhbmQgb25lIGRlcGVuZGVudCB2YXJpYWJsZSwgUEguIFdlIGFsc28gc2VlIHRoYXQgIkJyYW5kIENvZGUiIGlzIGEgZmFjdG9yIHZhcmlhYmxlIHRoYXQgd2lsbCBuZWVkIHRvIGJlIGhhbmRsZWQgYXMgd2VsbCBhcyBzZXZlcmFsIG9ic2VydmF0aW9ucyB3aXRoIGEgbnVtYmVyIG9mIE5Bcy4NCg0KRm9yIHRoZSBCZXZlcmFnZSBUZXN0aW5nIGRhdGFzZXQsIHdlIHNlZSAyNjcgb2JzZXJ2YXRpb25zLCB0aGUgMzIgcHJlZGljdG9ycywgYW5kIHRoZSBkZXBlbmRlbnQgdmFyaWFibGUgUEggd2hpY2ggaXMgYWxsIE5Bcy4gIFRoaXMgaXMgdGhlIGRhdGEgdGhhdCB3ZSB3aWxsIGhhdmUgdG8gcHJlZGljdC4gU2FtZSBhcyB0aGUgdHJhaW5pbmcgc2V0LCBXZSBhbHNvIHNlZSB0aGF0ICJCcmFuZCBDb2RlIiBpcyBhIGNoYXJhY3RlciB2YXJpYWJsZSB0aGF0IHdpbGwgbmVlZCB0byBiZSBoYW5kbGVkIGFzIHdlbGwgYXMgc2V2ZXJhbCBvYnNlcnZhdGlvbnMgd2l0aCBhIG51bWJlciBvZiBOQXMuIA0KDQoNCjxiPkJldmVyYWdlIFRyYWluaW5nIERhdGE8L2I+DQoNCmBgYHtyfQ0KZGltKGJldmVyYWdlX3RyYWluaW5nX2RhdGEpDQpgYGANCg0KDQpgYGB7cn0NCnR5cGVvZihiZXZlcmFnZV90cmFpbmluZ19kYXRhJGBCcmFuZCBDb2RlYCkNCmBgYA0KDQoNCmBgYHtyIGluY2x1ZGU9RkFMU0V9DQpzdW1tYXJ5KGJldmVyYWdlX3RyYWluaW5nX2RhdGEpDQpgYGANCg0KDQo8Yj5CZXZlcmFnZSBUZXN0aW5nIERhdGE8L2I+DQoNCmBgYHtyIGluY2x1ZGU9RkFMU0V9DQpnbGltcHNlKGJldmVyYWdlX3Rlc3RfZGF0YSkNCmBgYA0KDQpgYGB7ciBpbmNsdWRlPUZBTFNFfQ0Kc3VtbWFyeShiZXZlcmFnZV90ZXN0X2RhdGEpDQpgYGANCg0KDQojIyMgRGV2aXNlIGEgRGF0YSBQcmVwYXJhdGlvbiBTdHJhdGVneQ0KDQpBZnRlciBhbmFseXppbmcgdGhlIGRhdGEsIHdlIGRldmlzZWQgdGhlIGZvbGxvd2luZyBwcm9jZXNzZXMgaW4gb3JkZXIgdG8gcHJlcGFyZSB0aGUgZGF0YSBmb3IgYW5hbHlzaXMNCg0KDQpBLiBJc29sYXRlIHByZWRpY3RvcnMgZnJvbSB0aGUgZGVwZW5kZW50IHZhcmlhYmxlIA0KDQpCLiBDb3JyZWN0IHRoZSBQcmVkaWN0b3IgTmFtZXMgDQoNCkMuIENyZWF0ZSBhIGRhdGEgZnJhbWUgb2YgbnVtZXJpYyB2YWx1ZXMgb25seSANCg0KRC4gSWRlbnRpZnkgYW5kIEltcHV0ZSBNaXNzaW5nIERhdGEgDQoNCkUuIElkZW50aWZ5IGFuZCBBZGRyZXNzIE91dGxpZXJzDQoNCkYuIENoZWNrIGZvciBhbmQgcmVtb3ZlIGNvcnJlbGF0ZWQgcHJlZGljdG9ycyANCg0KRy4gSWRlbnRpZnkgTmVhciBaZXJvIFZhcmlhbmNlIFByZWRpY3RvcnMgDQoNCkguIEltcHV0ZSBtaXNzaW5nIHZhbHVlcyBhbmQgQ3JlYXRlIGR1bW15IHZhcmlhYmxlcyBmb3IgQnJhbmQuQ29kZSANCg0KSS4gSW1wdXRlIG1pc3NpbmcgZGF0YSBmb3IgRGVwZW5kZW50IFZhcmlhYmxlIFBIDQoNCg0KIyMgUEFSVCAyOiBEQVRBIFBSRVBBUkFUSU9ODQoNCg0KIyMjIEEuIElzb2xhdGUgcHJlZGljdG9ycyBmcm9tIHRoZSBkZXBlbmRlbnQgdmFyaWFibGVzDQoNCkZvciB0aGUgdHJhaW5pbmcgc2V0LCByZW1vdmUgdGhlIHByZWRpY3RvciB2YXJpYWJsZSwgUEggYW5kIHN0b3JlIGl0IGludG8gdGhlIHZhcmlhYmxlLCB5X3RyYWluLg0KDQpgYGB7cn0NCnByZWRpY3RvcnMgPC0gc3Vic2V0KGJldmVyYWdlX3RyYWluaW5nX2RhdGEsIHNlbGVjdCA9IC1jKFBIKSkNCnByZWRpY3RvcnNfZXZhbHVhdGUgPC0gc3Vic2V0KGJldmVyYWdlX3Rlc3RfZGF0YSwgc2VsZWN0ID0gLWMoUEgpKSANCnlfdHJhaW4gPC0gYXMuZGF0YS5mcmFtZShiZXZlcmFnZV90cmFpbmluZ19kYXRhJFBIKQ0KY29sbmFtZXMoeV90cmFpbikgPC0gYygiUEgiKQ0KYGBgDQoNCg0KIyMjIEIuIENvcnJlY3QgdGhlIFByZWRpY3RvciBOYW1lcw0KDQpDb3JyZWN0IHRoZSBzcGFjZSBpbiB0aGUgcHJlZGljdG9yIG5hbWVzIHVzaW5nIHRoZSBtYWtlLm5hbWVzIGZ1bmN0aW9uLiAgVGhlIHNwYWNlIGluIHRoZSBuYW1lcyBtYXkgYmUgcHJvYmxlbWF0aWMuIFRoaXMgd2FzIGFwcGxpZWQgdG8gYm90aCBkYXRhc2V0cy4gDQoNCmBgYHtyfQ0KY29sbmFtZXMocHJlZGljdG9ycykNCmBgYA0KDQoNCmBgYHtyfQ0KY29sbmFtZXMocHJlZGljdG9ycyk8LSBtYWtlLm5hbWVzKGNvbG5hbWVzKHByZWRpY3RvcnMpKQ0KY29sbmFtZXMocHJlZGljdG9ycykNCmBgYA0KDQpgYGB7cn0NCmNvbG5hbWVzKHByZWRpY3RvcnNfZXZhbHVhdGUpPC0gbWFrZS5uYW1lcyhjb2xuYW1lcyhwcmVkaWN0b3JzX2V2YWx1YXRlKSkNCmNvbG5hbWVzKHByZWRpY3RvcnNfZXZhbHVhdGUpDQpgYGANCg0KDQojIyMgQy4gQ3JlYXRlIGEgZGF0YSBmcmFtZSBvZiBudW1lcmljIHZhbHVlcyBvbmx5DQoNCldlIHNhdyBlYXJsaWVyIHRoYXQgQnJhbmQuQ29kZSBpcyBhIGNhdGVnb3JpY2FsIHZhbHVlLiAgQmVjYXVzZSBvZiB0aGF0IHdlIHN1YnNldCB0aGUgZGF0YWZyYW1lIHRvIHJlbW92ZSBpdC4gV2Ugd2lsbCBoYW5kbGUgdGhpcyB2YXJpYWJsZSBsYXRlci4NCg0KYGBge3J9DQpudW1fcHJlZGljdG9ycyA8LSBzdWJzZXQgKHByZWRpY3RvcnMsIHNlbGVjdCA9IC1CcmFuZC5Db2RlKQ0KbnVtX3ByZWRpY3RvcnMgPC0gYXMuZGF0YS5mcmFtZShudW1fcHJlZGljdG9ycykNCg0KYGBgDQoNCg0KIyMjIEQuIElkZW50aWZ5IGFuZCBJbXB1dGUgTWlzc2luZyBEYXRhIA0KDQpUaGUgcHJlZGljdG9yIE1GUiBoYXMgdGhlIG1vc3QgbWlzc2luZyB2YWx1ZXMgYXQgMjEyLiAgSSB1c2VkIGtubiBpbXB1dGF0aW9uIHRvIGhhbmRsZSBtaXNzaW5nIHZhbHVlcy4gIEFmdGVyIHRoZSBrbm4gaW1wdXRhdGlvbiwgdGhlcmUgYXJlIHN0aWxsIG1pc3NpbmcgdmFsdWVzIGZvciBCcmFuZC5Db2RlIHdoaWNoIHdpbGwgYmUgaGFuZGxlZCBpbiBhIGxhdGVyIHNlY3Rpb24uDQoNCjxiPlRyYWluaW5nIERhdGE8L2I+DQpgYGB7ciBpbmNsdWRlPUZBTFNFfQ0KbWlzc2luZ0RhdGEgPC0gYXMuZGF0YS5mcmFtZShjb2xTdW1zKGlzLm5hKG51bV9wcmVkaWN0b3JzKSkpDQpjb2xuYW1lcyhtaXNzaW5nRGF0YSkgPC0gYygiTkFzIikgDQptaXNzaW5nRGF0YSA8LSBjYmluZChQcmVkaWN0b3JzID0gcm93bmFtZXMobWlzc2luZ0RhdGEpLCBtaXNzaW5nRGF0YSkNCnJvd25hbWVzKG1pc3NpbmdEYXRhKSA8LSAxOm5yb3cobWlzc2luZ0RhdGEpDQptaXNzaW5nRGF0YSA8LSBtaXNzaW5nRGF0YVttaXNzaW5nRGF0YSROQXMgIT0gMCxdICU+JSANCiAgICAgICAgICAgICAgICBhcnJhbmdlKGRlc2MoTkFzKSkNCmhlYWRfbWlzc2luZyA8LSBoZWFkKG1pc3NpbmdEYXRhKQ0KYGBgDQoNCg0KDQoNCmBgYHtyLCBlY2hvPUZBTFNFfQ0Ka25pdHI6OmthYmxlKGhlYWRfbWlzc2luZywibWFya2Rvd24iLCBhbGlnbiA9ICdjJykNCmBgYA0KDQpgYGB7ciAgZmlnLmhlaWdodD03LCBmaWcuYWxpZ249J2NlbnRlcid9DQptaXNzaW5nRGF0YSAgJT4lDQogIGdncGxvdCgpICsNCiAgICBnZW9tX2JhcihhZXMoeD1yZW9yZGVyKFByZWRpY3RvcnMsTkFzKSwgeT1OQXMsIGZpbGw9ZmFjdG9yKE5BcykpLCBzdGF0ID0gJ2lkZW50aXR5JywgKSArDQogICAgbGFicyh4PSdQcmVkaWN0b3InLCB5PSJOQXMiLCB0aXRsZT0nTnVtYmVyIG9mIG1pc3NpbmcgdmFsdWVzJykgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKSArIGNvb3JkX2ZsaXAoKSANCmBgYA0KDQoNCg0KYGBge3IsIGluY2x1ZGU9RkFMU0V9DQpwcmVwcm9jZXNzaW5nIDwtIHByZVByb2Nlc3MoYXMuZGF0YS5mcmFtZShudW1fcHJlZGljdG9ycyksIG1ldGhvZCA9ICJrbm5JbXB1dGUiKSANCnByZWRpY3RvcnNfaW1wdXRlZCA8LSBwcmVkaWN0KHByZXByb2Nlc3NpbmcsIG51bV9wcmVkaWN0b3JzKQ0KDQoNCmBgYA0KDQoNCg0KDQpgYGB7cn0NCm1pc3NpbmdEYXRhIDwtIGFzLmRhdGEuZnJhbWUoY29sU3Vtcyhpcy5uYShwcmVkaWN0b3JzX2ltcHV0ZWQpKSkNCmNvbG5hbWVzKG1pc3NpbmdEYXRhKSA8LSBjKCJOQXMiKSANCm1pc3NpbmdEYXRhIDwtIGNiaW5kKFByZWRpY3RvcnMgPSByb3duYW1lcyhtaXNzaW5nRGF0YSksIG1pc3NpbmdEYXRhKQ0Kcm93bmFtZXMobWlzc2luZ0RhdGEpIDwtIDE6bnJvdyhtaXNzaW5nRGF0YSkNCm1pc3NpbmdEYXRhIDwtIG1pc3NpbmdEYXRhW21pc3NpbmdEYXRhJE5BcyAhPSAwLF0gJT4lIA0KICAgICAgICAgICAgICAgIGFycmFuZ2UoZGVzYyhOQXMpKQ0KaGVhZChtaXNzaW5nRGF0YSkNCmBgYA0KIyMjIEUuIElkZW50aWZ5IFNrZXduZXNzIGFuZCBPdXRsaWVycw0KDQpOZXh0IHdlIGxvb2tlZCBhdCB0aGUgZGlzdHJpYnV0aW9ucyBvZiB0aGUgbnVtZXJpYyB2YXJpYWJsZXMuIFRoZXJlIGFyZSBvbmx5IGZvdXIgcHJlZGljdG9ycyB0aGF0IGFyZSBub3JtYWxseSBkaXN0cmlidXRlZC4gVGhlIGJveCBwbG90cyBzaG93IGEgaGlnaCBudW1iZXIgb2Ygb3V0bGllcnMgaW4gdGhlIGRhdGEuIFRvIGNvcnJlY3QgZm9yIHRoaXMsIHRoZSBwcmUgcHJvY2Vzc2luZyBzdGVwIG9mIGNlbnRlciBhbmQgc2NhbGUgd2FzIHVzZWQuIFdlIGNlbnRlcmVkIGFuZCBzY2FsZWQgdGhlc2UgZGlzdHJpYnV0aW9ucy4gDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmdzPUZBTFNFLCBmaWcuaGVpZ2h0PSAyMCwgZmlnLndpZHRoPSAxNSwgZmlnLmFsaWduPSdjZW50ZXInfQ0KDQpwYXIobWZyb3cgPSBjKDMsIDMpKQ0KZGF0YXN1YiA9IG1lbHQocHJlZGljdG9yc19pbXB1dGVkKSANCnN1cHByZXNzV2FybmluZ3MoZ2dwbG90KGRhdGFzdWIsIGFlcyh4PSB2YWx1ZSkpICsgDQogICAgICAgICAgICAgICAgICAgZ2VvbV9kZW5zaXR5KGZpbGw9J29yYW5nZScpICsgZmFjZXRfd3JhcCh+dmFyaWFibGUsIHNjYWxlcyA9ICdmcmVlJykgKQ0KYGBgDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmdzPUZBTFNFLCBmaWcuaGVpZ2h0PSAyMCwgZmlnLndpZHRoPSAxNSwgZmlnLmFsaWduPSdjZW50ZXInfQ0KZ2dwbG90KGRhdGEgPSBkYXRhc3ViICwgYWVzKHg9dmFyaWFibGUsIHk9dmFsdWUpKSArIA0KICBnZW9tX2JveHBsb3Qob3V0bGllci5jb2xvdXI9InJlZCIsIG91dGxpZXIuc2hhcGU9Mywgb3V0bGllci5zaXplPTgsYWVzKGZpbGw9dmFyaWFibGUpKSArDQogIGNvb3JkX2ZsaXAoKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikNCiAgDQoNCmBgYA0KDQoNCmBgYHtyfQ0KcHJlcHJvY2Vzc2luZyA8LSBwcmVQcm9jZXNzKGFzLmRhdGEuZnJhbWUocHJlZGljdG9yc19pbXB1dGVkKSwgbWV0aG9kID0gYygiY2VudGVyIiwgInNjYWxlIikpIA0KcHJlcHJvY2Vzc2luZyANCm51bV9wcmVkaWN0b3JzXzAxIDwtIHByZWRpY3QocHJlcHJvY2Vzc2luZywgcHJlZGljdG9yc19pbXB1dGVkKQ0KYGBgDQoNCg0KYGBge3J9DQpudW1fcHJlZGljdG9yc18wMiA8LSBzcGF0aWFsU2lnbihudW1fcHJlZGljdG9yc18wMSkNCm51bV9wcmVkaWN0b3JzXzAyIDwtIGFzLmRhdGEuZnJhbWUobnVtX3ByZWRpY3RvcnNfMDIpDQoNCmBgYA0KDQoNCg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5ncz1GQUxTRSwgZmlnLmhlaWdodD0yMCwgZmlnLndpZHRoPSAxNSwgZmlnLmFsaWduPSdjZW50ZXInfQ0KDQpwYXIobWZyb3cgPSBjKDMsIDMpKQ0KZGF0YXN1YiA9IG1lbHQobnVtX3ByZWRpY3RvcnNfMDIpIA0Kc3VwcHJlc3NXYXJuaW5ncyhnZ3Bsb3QoZGF0YXN1YiwgYWVzKHg9IHZhbHVlKSkgKyANCiAgICAgICAgICAgICAgICAgICBnZW9tX2RlbnNpdHkoZmlsbD0nYmx1ZScpICsgZmFjZXRfd3JhcCh+dmFyaWFibGUsIHNjYWxlcyA9ICdmcmVlJykgKQ0KYGBgDQoNCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZ3M9RkFMU0UsIGZpZy5oZWlnaHQ9MjAsIGZpZy53aWR0aD0gMTUsIGZpZy5hbGlnbj0nY2VudGVyJ30NCmdncGxvdChkYXRhID0gZGF0YXN1YiAsIGFlcyh4PXZhcmlhYmxlLCB5PXZhbHVlKSkgKyANCiAgZ2VvbV9ib3hwbG90KG91dGxpZXIuY29sb3VyPSJyZWQiLCBvdXRsaWVyLnNoYXBlPTMsIG91dGxpZXIuc2l6ZT04LGFlcyhmaWxsPXZhcmlhYmxlKSkgKw0KICBjb29yZF9mbGlwKCkgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQogIA0KDQpgYGANCg0KDQoNCiMjIyBGLiBDaGVjayBmb3IgYW5kIHJlbW92ZSBjb3JyZWxhdGVkIHByZWRpY3RvcnMNCg0KV2UgaWRlbnRpZmllZCBmaXZlIHZhcmlhYmxlcyB0aGF0IGFyZSBoaWdobHkgY29ycmVsYXRlZCB3aXRoIG90aGVyIHZhcmlhYmxlcyBhdCBhYm92ZSAuOS4gSGlnaGx5IGNvcnJlbGF0ZWQgdmFyaWFibGVzIGxlYWQgdG8gTXVsdGljb2xsaW5lYXJpdHkgd2hpY2ggcmVkdWNlcyB0aGUgcHJlY2lzaW9uIG9mIHRoZSBlc3RpbWF0ZSBjb2VmZmljaWVudHMgYW5kIHdlYWtlbnMgdGhlIHN0YXRpc3RpY2FsIHBvd2VyIG9mIHJlZ3Jlc3Npb24gbW9kZWxzLg0KDQpgYGB7cn0NCnRvb0hpZ2ggPC0gZmluZENvcnJlbGF0aW9uKGNvcihudW1fcHJlZGljdG9yc18wMiwgdXNlPSJuYS5vci5jb21wbGV0ZSIpLCBjdXRvZmYgPSAuOSwgbmFtZXMgPSBUUlVFKQ0KdG9vSGlnaA0KYGBgDQoNCg0KYGBge3IgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPSAxMCwgZmlnLmFsaWduPSdjZW50ZXInfQ0KDQpjb3JyIDwtIHJvdW5kKGNvcihudW1fcHJlZGljdG9yc18wMiwgdXNlPSJuYS5vci5jb21wbGV0ZSIpLCAxKQ0KDQpnZ2NvcnJwbG90KGNvcnIsDQogICAgICAgICAgIHR5cGU9Imxvd2VyIiwNCiAgICAgICAgICAgbGFiPVRSVUUsDQogICAgICAgICAgIGxhYl9zaXplPTMsDQogICAgICAgICAgIG1ldGhvZD0iY2lyY2xlIiwNCiAgICAgICAgICAgY29sb3JzPWMoInRvbWF0bzIiLCAid2hpdGUiLCAic3ByaW5nZ3JlZW4zIiksDQogICAgICAgICAgIHRpdGxlPSJDb3JyZWxhdGlvbiBvZiB2YXJpYWJsZXMgaW4gVHJhaW5pbmcgRGF0YSBTZXQiLA0KICAgICAgICAgICBnZ3RoZW1lPXRoZW1lX2J3KQ0KDQpgYGANCg0KYGBge3J9DQpudW1fcHJlZGljdG9yc18wMlsgLGModG9vSGlnaCldIDwtIGxpc3QoTlVMTCkNCmNvbG5hbWVzKG51bV9wcmVkaWN0b3JzXzAyKQ0KYGBgDQoNCiMjIyBHLiBJZGVudGlmeSBOZWFyIFplcm8gVmFyaWFuY2UgUHJlZGljdG9ycw0KDQpSZW1vdmUgdGhlIHplcm8gdmFyaWFuY2UgcHJlZGljdG9yLiBUaGVyZSBhcmUgbm8gbmVhciB6ZXJvIHZhcmlhbmNlIHByZWRpY3RvcnMNCg0KYGBge3J9DQpjYXJldDo6bmVhclplcm9WYXIobnVtX3ByZWRpY3RvcnNfMDIsIG5hbWVzID0gVFJVRSkNCmBgYA0KDQoNCiMjIyBILiBJbXB1dGUgbWlzc2luZyB2YWx1ZXMgYW5kIENyZWF0ZSBkdW1teSB2YXJpYWJsZXMgZm9yIEJyYW5kLkNvZGUNCg0KRWFybGllciwgd2Ugc2F3IHRoYXQgdGhlcmUgYXJlIDEyMCBtaXNzaW5nIHZhbHVlcyBmb3IgQnJhbmQuQ29kZSwgYSBmYWN0b3IgdmFyaWFibGUuIFRoZSBpbXB1dGF0aW9uIHN0cmF0ZWd5IGhlcmUgaXMgdG8gaW1wdXRlIHdpdGggdGhlIG1vc3QgZnJlcXVlbnQgdmFsdWUsICJCIi4gQWZ0ZXIgaW1wdXRhdGlvbiwgQnJhbmQuQ29kZSB3YXMgY29udmVydGVkIHRvIGR1bW15IHZhcmlhYmxlcy4gVGhlIGNvbnZlcnRlZCBCcmFuZC5Db2RlIHByZWRpY3RvciBpcyBqb2luZWQgdG8gdGhlIG51bV9wcmVkaWN0b3JzXzAyLiANCg0KYGBge3J9DQpCcmFuZENvZGVOQXMgPC0gcHJlZGljdG9ycyRCcmFuZC5Db2RlW2lzLm5hKHByZWRpY3RvcnMkQnJhbmQuQ29kZSA9PVRSVUUpXQ0KbGVuZ3RoKEJyYW5kQ29kZU5BcykNCmBgYA0KDQoNCmBgYHtyfQ0KcHJlZGljdG9ycyRCcmFuZC5Db2RlIDwtIGFzLmZhY3RvcihwcmVkaWN0b3JzJEJyYW5kLkNvZGUpDQpsZXZlbHMocHJlZGljdG9ycyRCcmFuZC5Db2RlICkNCmBgYA0KDQoNCmBgYHtyfQ0KdGFibGUocHJlZGljdG9ycyRCcmFuZC5Db2RlKQ0KDQpgYGANCg0KDQpgYGB7cn0NCg0KcHJlZGljdG9ycyRCcmFuZC5Db2RlW2lzLm5hKHByZWRpY3RvcnMkQnJhbmQuQ29kZSldID0gIkIiDQoNCmBgYA0KDQoNCmBgYHtyfQ0KcHJlZGljdG9ycyRCcmFuZC5Db2RlW2lzLm5hKHByZWRpY3RvcnMkQnJhbmQuQ29kZSldDQpgYGANCg0KDQpgYGB7cn0NCm1vZDwtIGR1bW15VmFycyh+QnJhbmQuQ29kZSwNCiAgICAgICAgICBkYXRhPXByZWRpY3RvcnMsDQogICAgICAgICAgbGV2ZWxzT25seSA9IEZBTFNFKQ0KbW9kDQpgYGANCg0KDQpgYGB7ciBpbmNsdWRlPUZBTFNFfQ0KZHVtbWllcyA8LSBhcy5kYXRhLmZyYW1lKHByZWRpY3QobW9kLCBwcmVkaWN0b3JzKSkNCmhlYWRfZHVtbSA8LSBoZWFkKGR1bW1pZXMsNikNCmBgYA0KDQoNCg0KYGBge3IsIGVjaG89RkFMU0V9DQprbml0cjo6a2FibGUoaGVhZF9kdW1tLCJtYXJrZG93biIsIGFsaWduID0gJ2MnKQ0KYGBgDQoNCg0KDQpgYGB7cn0NCmV2YWwuZGF0YSA8LSBjYmluZChkdW1taWVzLCBudW1fcHJlZGljdG9yc18wMikNCg0KYGBgDQoNCg0KDQojIyMgSS4gSW1wdXRlIG1pc3NpbmcgZGF0YSBmb3IgRGVwZW5kZW50IFZhcmlhYmxlIFBIDQoNClRoZSBmaW5hbCBzdGVwIGlzIHRvIGltcHV0ZSBtaXNzaW5nIHZhbHVlcyBmb3IgdGhlIGRlcGVuZGVudCB2YXJpYWJsZSwgUEgsIHdpdGggdGhlIG1lZGlhbiBmb3IgUEguIA0KDQoNCmBgYHtyfQ0KeV90cmFpbltpcy5uYSh5X3RyYWluJFBIKSxdIDwtIG1lZGlhbih5X3RyYWluJFBILG5hLnJtPVRSVUUpDQpgYGANCg0KDQoNCmBgYHtyfQ0KcHJvY2Vzc2VkLnRyYWluIDwtIGNiaW5kKHlfdHJhaW4sIGV2YWwuZGF0YSkNCg0KYGBgDQoNCg0KYGBge3J9DQptaXNzaW5nRGF0YSA8LSBhcy5kYXRhLmZyYW1lKGNvbFN1bXMoaXMubmEocHJvY2Vzc2VkLnRyYWluKSkpDQpjb2xuYW1lcyhtaXNzaW5nRGF0YSkgPC0gYygiTkFzIikgDQptaXNzaW5nRGF0YSA8LSBjYmluZChQcmVkaWN0b3JzID0gcm93bmFtZXMobWlzc2luZ0RhdGEpLCBtaXNzaW5nRGF0YSkNCnJvd25hbWVzKG1pc3NpbmdEYXRhKSA8LSAxOm5yb3cobWlzc2luZ0RhdGEpDQptaXNzaW5nRGF0YSA8LSBtaXNzaW5nRGF0YVttaXNzaW5nRGF0YSROQXMgIT0gMCxdICU+JSANCiAgICAgICAgICAgICAgICBhcnJhbmdlKGRlc2MoTkFzKSkNCmhlYWQobWlzc2luZ0RhdGEpDQpgYGANCg0KDQojIyBQQVJUIDM6IEVYUEVSSU1FTlRBVElPTiANCg0KDQo8Yj48dT5TcGxpdCB0aGUgVGltZSBTZXJpZXM8L2I+PC91Pg0KDQpCZWZvcmUgd2UgYmVnaW4gd2l0aCB0aGUgZXhwZXJpbWVudGF0aW9uLCBXZSBzcGxpdCB0aGUgdHJhaW5pbmcgZGF0YSBpbnRvIHRyYWluIGFuZCB0ZXN0IHNldHMgDQoNCiAgDQpgYGB7cn0NCmV2YWx1YXRpb24uc3BsaXQgPC0gaW5pdGlhbF9zcGxpdChwcm9jZXNzZWQudHJhaW4sIHByb3AgPSAwLjcsIHN0cmF0YSA9ICJQSCIpDQp0cmFpbiA8LSB0cmFpbmluZyhldmFsdWF0aW9uLnNwbGl0KQ0KdGVzdCA8LSB0ZXN0aW5nKGV2YWx1YXRpb24uc3BsaXQpDQpgYGANCg0KDQo8Yj48dT5Nb2RlbGluZzwvYj48L3U+DQoNCldlIGV4YW1pbmVkIDEyIG1vZGVscy4gIFdlIGxvb2tlZCBhdCBMaW5lYXIgTW9kZWxzLCBOb24gTGluZWFyIFJlZ3Jlc3Npb24gTW9kZWxzLCBhbmQgVHJlZSBCYXNlZCBNb2RlbHMuIEZvciBhbGwgb2YgdGhlIG1vZGVscywgTU5GLkZsb3cgd2FzIHRoZSBtb3N0IGltcG9ydGFudCBwcmVkaWN0b3Igd2l0aCB0aGUgZXhjZXB0aW9uIG9mIHRoZSBiYWcgdHJlZSBtb2RlbC4gT3RoZXIgY29uc2lzdGVudGx5IGltcG9ydGFudCBwcmVkaWN0b3JzIGluY2x1ZGUgcHJlZGljdG9yLCBCcmFuZCBDIGFuZCBELiBSZXNpZHVhbHMgZm9yIGVhY2ggbW9kZWwgYXBwZWFyIHJhbmRvbSB3aXRoIG5vIGRpc2Nlcm5hYmxlIHBhdHRlcm5zLiBJbiBQYXJ0IDQsIHdlIGV2YWx1YXRlZCB0aGUgbWV0cmljcyBmcm9tIGVhY2ggbW9kZWwuIA0KDQojIyMgTGluZWFyIE1vZGVscw0KDQpgYGB7cn0NCnNldC5zZWVkKDEwMCkNCnhfdHJhaW4gPC0gdHJhaW5bLCAyOjI5XQ0KeV90cmFpbiA8LSBhcy5kYXRhLmZyYW1lKHRyYWluJFBIKQ0KY29sbmFtZXMoeV90cmFpbikgPC0gYygiUEgiKQ0KDQp4X3Rlc3QgPC0gdGVzdFssIDI6MjldDQp5X3Rlc3QgPC0gYXMuZGF0YS5mcmFtZSh0ZXN0JFBIKQ0KY29sbmFtZXMoeV90ZXN0KSA8LSBjKCJQSCIpDQpjdHJsIDwtIHRyYWluQ29udHJvbChtZXRob2QgPSAiY3YiLCBudW1iZXIgPSAxMCkNCg0KYGBgDQoNCg0KPGI+PHU+QmFzaWMgbGluZWFyIG1vZGVsPC9iPjwvdT4NCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmxtRml0MSA8LSB0cmFpbih4X3RyYWluLCB5X3RyYWluJFBILA0KICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJsbSIsIA0KICAgICAgICAgICAgICAgIHRyQ29udHJvbCA9IGN0cmwpDQpgYGANCg0KDQpgYGB7cn0NCnN1bW1hcnkobG1GaXQxKQ0KYGBgDQoNCmBgYHtyfQ0KbG1GaXQxJHJlc3VsdHMNCmBgYA0KDQpgYGB7cn0NCnZhckltcChsbUZpdDEpDQpgYGANCg0KDQpgYGB7cn0NCg0KcGxvdChyZXNpZHVhbHMobG1GaXQxKSApDQpgYGANCg0KDQoNCjxiPjx1PlBhcnRpYWwgTGVhc3QgU3F1YXJlcyBvciBQTFM8L2I+PC91Pg0KDQpgYGB7cn0NCnNldC5zZWVkKDEwMCkNCnBsc0ZpdDEgPC0gdHJhaW4oeF90cmFpbiwgeV90cmFpbiRQSCwNCiAgbWV0aG9kID0gInBscyIsDQogIHR1bmVMZW5ndGggPSAyNSwNCiAgdHJDb250cm9sID0gY3RybCkNCg0KYGBgDQoNCg0KYGBge3J9DQpzdW1tYXJ5KHBsc0ZpdDEpDQpgYGANCg0KDQpgYGB7cn0NCnBsb3QocGxzRml0MSkNCmBgYA0KDQoNCmBgYHtyfQ0KcGxzRml0MSRiZXN0VHVuZQ0KYGBgDQoNCg0KDQpgYGB7cn0NCnRyYWluX3NldF9yZXN1bHRzIDwtIHBsc0ZpdDEkcmVzdWx0cyAlPiUgDQogIGZpbHRlcihuY29tcD09OCkNCg0KdHJhaW5fc2V0X3Jlc3VsdHMNCmBgYA0KDQoNCmBgYHtyfQ0KdmFySW1wKHBsc0ZpdDEpDQpgYGANCg0KDQoNCmBgYHtyfQ0KcGxvdChyZXNpZHVhbHMocGxzRml0MSkgKQ0KYGBgDQoNCg0KDQoNCg0KPGI+PHU+UmlkZ2UgUmVncmVzc2lvbjwvYj48L3U+DQoNCmBgYHtyfQ0KcmlkZ2VHcmlkIDwtIGRhdGEuZnJhbWUoLmxhbWJkYSA9IHNlcSgwLCAuMSwgbGVuZ3RoID0gMTUpKQ0KDQpyaWRnZVJlZ0ZpdCA8LSB0cmFpbih4X3RyYWluLCB5X3RyYWluJFBILA0KICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kID0gInJpZGdlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgIHR1bmVHcmlkID0gcmlkZ2VHcmlkLA0KICAgICAgICAgICAgICAgICAgICAgICAgdHJDb250cm9sID0gY3RybCkNCmBgYA0KDQoNCg0KYGBge3J9DQpzdW1tYXJ5KHJpZGdlUmVnRml0KQ0KYGBgDQoNCg0KDQpgYGB7cn0NCnBsb3QocmlkZ2VSZWdGaXQpDQpgYGANCg0KDQpgYGB7cn0NCnJpZGdlUmVnRml0JGJlc3RUdW5lDQpgYGANCmBgYHtyfQ0KdHJhaW5fc2V0X3Jlc3VsdHMgPC0gcmlkZ2VSZWdGaXQkcmVzdWx0cyAgDQoNCnRyYWluX3NldF9yZXN1bHRzW3Jvdy5uYW1lcyh0cmFpbl9zZXRfcmVzdWx0cykgPT0gMywgXQ0KYGBgDQoNCg0KDQpgYGB7cn0NCnZhckltcChyaWRnZVJlZ0ZpdCkNCmBgYA0KDQoNCg0KYGBge3J9DQpwbG90KHJlc2lkdWFscyhyaWRnZVJlZ0ZpdCkgKQ0KYGBgDQoNCg0KDQojIyMgTm9uIExpbmVhciBSZWdyZXNzaW9uDQoNCjxiPjx1PktOTjwvYj48L3U+DQoNCmBgYHtyfQ0Ka25uTW9kZWwgPC0gdHJhaW4oeCA9IHhfdHJhaW4sIHkgPSB5X3RyYWluJFBILA0KICAgICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJrbm4iLA0KICAgICAgICAgICAgICAgICAgIHR1bmVMZW5ndGggPSAyNSwgDQogICAgICAgICAgICAgICAgICAgdHJDb250cm9sID0gY3RybCkNCg0Ka25uTW9kZWwNCmBgYA0KDQpgYGB7cn0NCmtublByZWQgPC0gcHJlZGljdChrbm5Nb2RlbCwgbmV3ZGF0YSA9IHhfdGVzdCkNCg0Ka25uX3JlcyA8LSBwb3N0UmVzYW1wbGUocHJlZCA9IGtublByZWQsIG9icyA9IHlfdGVzdCRQSCkNCmtubl9yZXMNCmBgYA0KDQpgYGB7cn0NCnZhckltcChrbm5Nb2RlbCkNCmBgYA0KDQpgYGB7cn0NCnBsb3QocmVzaWR1YWxzKGtubk1vZGVsKSkNCmBgYA0KDQoNCjxiPjx1Pk5ldXJhbCBOZXR3b3JrPC9iPjwvdT4NCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCm5uZXRHcmlkIDwtIGV4cGFuZC5ncmlkKC5kZWNheSA9IGMoMCwgLjAxLCAxKSwNCiAgICAgICAgICAgICAgICAgICAgICAgIC5zaXplID0gYygxOjEwKSwNCiAgICAgICAgICAgICAgICAgICAgICAgIC5iYWcgPSBGQUxTRSkNCnNldC5zZWVkKDEwMCkNCm5uZXRUdW5lIDwtIHRyYWluKHggPSB4X3RyYWluLA0KICAgICAgICAgICAgICAgICAgeSA9IHlfdHJhaW4kUEgsDQogICAgICAgICAgICAgICAgICBtZXRob2QgPSAiYXZOTmV0IiwNCiAgICAgICAgICAgICAgICAgIHR1bmVHcmlkID0gbm5ldEdyaWQsDQogICAgICAgICAgICAgICAgICB0ckNvbnRyb2wgPSBjdHJsLA0KICAgICAgICAgICAgICAgICAgbGlub3V0ID0gRkFMU0UsICB0cmFjZSA9IEZBTFNFLA0KICAgICAgICAgICAgICAgICAgTWF4Tld0cyA9IDUqIChuY29sKHhfdHJhaW4pICsgMSkgKyA1ICsgMSwNCiAgICAgICAgICAgICAgICAgIG1heGl0ID0gMjUwKQ0KICAgICAgICAgICAgICAgICAgDQpgYGANCg0KDQogDQpgYGB7cn0NCm5uZXRUdW5lDQpgYGANCg0KYGBge3J9DQpzdW1tYXJ5KG5uZXRUdW5lKQ0KYGBgDQoNCg0KYGBge3J9DQpubmV0VHVuZSRiZXN0VHVuZQ0KYGBgDQoNCg0KYGBge3J9DQpubmV0UHJlZCA8LSBwcmVkaWN0KG5uZXRUdW5lLCBuZXdkYXRhPXhfdGVzdCkNCk5ORVQgPC0gcG9zdFJlc2FtcGxlKHByZWQgPSBubmV0UHJlZCwgb2JzID0geV90ZXN0JFBIKQ0KTk5FVA0KYGBgDQoNCmBgYHtyfQ0KcGxvdG1vKG5uZXRUdW5lKQ0KYGBgDQoNCmBgYHtyfQ0KdmFySW1wKG5uZXRUdW5lKQ0KYGBgDQoNCg0KDQo8Yj48dT5NdWx0aXZhcmlhdGUgQWRhcHRpdmUgUmVncmVzc2lvbiBTcGxpbmVzIChNQVJTKTwvdT48L2I+DQoNCg0KYGBge3J9DQpzZXQuc2VlZCgxMDApDQptYXJzR3JpZCA8LSBleHBhbmQuZ3JpZCguZGVncmVlID0gMToyLCAubnBydW5lID0gMjozOCkNCm1hcnNUdW5lZCA8LSB0cmFpbih4ID0geF90cmFpbiwgeSA9ICB5X3RyYWluJFBILA0KICAgICAgICAgICAgICAgICAgbWV0aG9kID0gImVhcnRoIiwgDQogICAgICAgICAgICAgICAgICB0dW5lR3JpZCA9IG1hcnNHcmlkLA0KICAgICAgICAgICAgICAgICAgdHJDb250cm9sID0gY3RybCkNCm1hcnNUdW5lZA0KYGBgDQoNCg0KDQpgYGB7cn0NCm1hcnNQcmVkIDwtIHByZWRpY3QobWFyc1R1bmVkLCBuZXdkYXRhPXhfdGVzdCkNCk1BUlMgPC0gcG9zdFJlc2FtcGxlKHByZWQgPSBtYXJzUHJlZCwgb2JzID0geV90ZXN0JFBIKQ0KTUFSUw0KYGBgDQoNCg0KYGBge3J9DQpwbG90bW8obWFyc1R1bmVkKQ0KYGBgDQoNCmBgYHtyfQ0KdmFySW1wKG1hcnNUdW5lZCkNCmBgYA0KDQoNCmBgYHtyfQ0KcGxvdChyZXNpZHVhbHMobWFyc1R1bmVkKSkNCmBgYA0KDQo8Yj48dT4gU3VwcG9ydCBWZWN0b3IgTWFjaGluZXMgKFNWTSk8L3U+PC9iPg0KDQpgYGB7cn0NCnNldC5zZWVkKDEwMCkNCnN2bUxUdW5lZCA8LSB0cmFpbih4ID0geF90cmFpbiwgeSA9ICB5X3RyYWluJFBILA0KICAgICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJzdm1MaW5lYXIiLA0KICAgICAgICAgICAgICAgICAgIHR1bmVMZW5ndGggPSAyNSwNCiAgICAgICAgICAgICAgICAgICB0ckNvbnRyb2wgPSB0cmFpbkNvbnRyb2wobWV0aG9kID0gImN2IikpDQpzdm1MVHVuZWQNCmBgYA0KDQoNCmBgYHtyfQ0Kc3ZtTFByZWQgPC0gcHJlZGljdChzdm1MVHVuZWQsIG5ld2RhdGE9eF90ZXN0KQ0Kc3ZtTDwtIHBvc3RSZXNhbXBsZShwcmVkID0gc3ZtTFByZWQsIG9icyA9IHlfdGVzdCRQSCkNCnN2bUwNCmBgYA0KDQoNCmBgYHtyfQ0KcGxvdG1vKHN2bUxUdW5lZCkNCmBgYA0KDQoNCg0KYGBge3J9DQp2YXJJbXAoc3ZtTFR1bmVkKQ0KYGBgDQoNCg0KIyMjIFRyZWUgQmFzZWQgTW9kZWxzDQoNCg0KDQpgYGB7ciAgaW5jbHVkZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0NCg0KICAgICAgICAgICAgICAgICAgICANCnNldC5zZWVkKDEwMCkNCmN0cmVlTW9kZWwgPC0gdHJhaW4oeCA9IHhfdHJhaW4sIHkgPSAgeV90cmFpbiRQSCwNCiAgICAgICAgICAgICAgICAgICAgbWV0aG9kID0gImN0cmVlIiwNCiAgICAgICAgICAgICAgICAgICAgdHVuZUxlbmd0aCA9IDEwLA0KICAgICAgICAgICAgICAgICAgICB0ckNvbnRyb2wgPSBjdHJsDQogICAgICAgICAgICAgICAgICAgICkNCg0KDQoNCnNldC5zZWVkKDEwMCkNCnJmTW9kZWwgPC0gcmFuZG9tRm9yZXN0KHggPSB4X3RyYWluLCB5ID0gIHlfdHJhaW4kUEgsDQogICAgICAgICAgICAgICAgICAgICAgIGltcG9ydGFuY2UgPSBULA0KICAgICAgICAgICAgICAgICAgICAgICBudHJlZT0xMDAwKQ0KDQoNCnNldC5zZWVkKDEwMCkNCmJhZ2dlZFRyZWVNb2RlbCA8LSB0cmFpbih4ID0geF90cmFpbiwgeSA9ICB5X3RyYWluJFBILA0KICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAidHJlZWJhZyIsDQogICAgICAgICAgICAgICAgICAgIHRyQ29udHJvbCA9IGN0cmwsDQogICAgICAgICAgICAgICAgICAgIG5iYWdnID0gNzUsICANCiAgICAgICAgICAgICAgICAgICAgY29udHJvbCA9IHJwYXJ0LmNvbnRyb2wobWluc3BsaXQgPSAyLCBjcCA9IDApDQogICAgICAgICAgICAgICkNCg0KDQpzZXQuc2VlZCgxMDApDQpnYm1HcmlkIDwtIGV4cGFuZC5ncmlkKGludGVyYWN0aW9uLmRlcHRoID0gc2VxKDEsIDcsIGJ5ID0gMiksDQogICAgICAgICAgICAgICAgICAgICAgICBuLnRyZWVzID0gYyhzZXEoMTAwLCAxMDAwLCBieSA9IDUwKSksDQogICAgICAgICAgICAgICAgICAgICAgICBzaHJpbmthZ2UgPSBjKDAuMDEsIC4xKSwNCiAgICAgICAgICAgICAgICAgICAgICAgIG4ubWlub2JzaW5ub2RlID0gYyg1LCAxMCwgMTUpKQ0KDQpzZXQuc2VlZCgxMDApDQpnYm1Nb2RlbCA8LSB0cmFpbih4ID0geF90cmFpbiwgeSA9ICB5X3RyYWluJFBILA0KICAgICAgICAgICAgICAgICAgbWV0aG9kID0gImdibSIsDQogICAgICAgICAgICAgICAgICB0dW5lR3JpZCA9IGdibUdyaWQsDQogICAgICAgICAgICAgICAgICB2ZXJib3NlID0gRkFMU0UsDQogICAgICAgICAgICAgICAgICB0ckNvbnRyb2wgPSBjdHJsDQogICAgICAgICAgICAgICAgICkNCg0KDQpjdWJpc3RHcmlkIDwtIGV4cGFuZC5ncmlkKGNvbW1pdHRlZXMgPSBjKDEsIDUsIDEwLCA1MCwgNzUsIDEwMCksDQogICAgICAgICAgICAgICAgICAgICAgICAgIG5laWdoYm9ycyA9IGMoMCwgMSwgMywgNSwgNywgOSkpDQoNCnNldC5zZWVkKDEwMCkNCmN1YmlzdE1vZGVsPC0gdHJhaW4oICB4PSB4X3RyYWluWywgY29sbmFtZXMoeF90cmFpbildLA0KICAgICAgICAgICAgICAgICAgICAgICB5ID0geV90cmFpbiRQSCwgDQogICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJjdWJpc3QiLCANCiAgICAgICAgICAgICAgICAgICAgICAgdHVuZUdyaWQgPSBjdWJpc3RHcmlkLA0KICAgICAgICAgICAgICAgICAgICAgICB0ckNvbnRyb2wgPSBjdHJsDQogICAgICAgICAgICAgICAgICAgICAgKQ0KICANCg0KDQoNCmBgYA0KDQoNCg0KYGBge3J9DQpyZXNhbXBsZXMgPC0gcmVzYW1wbGVzKCBsaXN0KENvbmRJbmZUcmVlID1jdHJlZU1vZGVsLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIEJhZ2dlZFRyZWUgPSBiYWdnZWRUcmVlTW9kZWwsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgQm9vc3RlZFRyZWUgPSBnYm1Nb2RlbCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBDdWJpc3Q9Y3ViaXN0TW9kZWwpICkNCnN1bW1hcnkocmVzYW1wbGVzKQ0KYGBgDQoNCg0KPGI+PHU+U2luZ2xlIFRyZWUgTW9kZWxzIC0gY1RyZWU8L2I+PC91Pg0KDQpgYGB7cn0NCmNvbnZlcnRfdG9wXzIwX3RvX2RmIDwtIGZ1bmN0aW9uKGRmKXsNCiAgICAgICAgICBkZjEgPC0gYXMuZGF0YS5mcmFtZShkZikNCiAgICAgICAgICBkZjFbJ1ByZWRpY3RvcnMnXSAgPC0gcm93bmFtZXMoZGYpDQogICAgICAgICAgY29sbmFtZXMoZGYxKSA8LSBjKCJPdmVyYWxsIiwgIlByZWRpY3RvcnMiKQ0KICAgICAgICAgIHJvd25hbWVzKGRmMSkgPC0gMTpucm93KGRmMSkNCiAgICAgICAgICANCiAgICAgICAgICByZXR1cm4gKGRmMSkNCn0NCmBgYA0KDQoNCg0KYGBge3J9DQpwbG90KGN0cmVlTW9kZWwsIG1haW4gPSAiU2luZ2xlIFRyZWUgTW9kZWwgKGNUcmVlKSIpDQpgYGANCg0KYGBge3J9DQpjdHJlZV8yMCA8LSB2YXJJbXAoY3RyZWVNb2RlbCkNCmN0cmVlXzIwIDwtIGN0cmVlXzIwJGltcG9ydGFuY2UgJT4lIA0KICBhcnJhbmdlKGRlc2MoT3ZlcmFsbCkpIA0KY3RyZWVfMjAgPC0gICBoZWFkKGN0cmVlXzIwLDIwKQ0KY3RyZWVfMjANCmBgYA0KDQoNCmBgYHtyfQ0KY3RyZWVfMjBfZGY8LSBjb252ZXJ0X3RvcF8yMF90b19kZihjdHJlZV8yMCkNCg0KY3RyZWVfMjBfZGYgJT4lIA0KICAgICAgICAgICAgYXJyYW5nZShPdmVyYWxsKSU+JSANCiAgICAgICAgICAgIG11dGF0ZShuYW1lID0gZmFjdG9yKFByZWRpY3RvcnMsIGxldmVscz1jKFByZWRpY3RvcnMpKSkgJT4lIA0KICAgICAgICAgICAgZ2dwbG90KGFlcyh4PW5hbWUsIHk9T3ZlcmFsbCkpICsNCiAgICAgICAgICAgIGdlb21fc2VnbWVudChhZXMoeGVuZCA9IFByZWRpY3RvcnMsIHllbmQgPSAwKSkgKw0KICAgICAgICAgICAgZ2VvbV9wb2ludChzaXplID0gNCwgY29sb3IgPSAiYmx1ZSIpICsgDQogICAgICAgICAgICB0aGVtZV9taW5pbWFsKCkgKyANCiAgICAgICAgICAgIGNvb3JkX2ZsaXAoKSArDQogICAgICAgICAgICBsYWJzKHRpdGxlPSJyUGFydCBQcmVkaWN0b3IgVmFyaWFibGUgSW1wb3J0YW5jZSIsDQogICAgICAgICAgICAgICB5PSJyUGFydCBJbXBvcnRhbmNlIiwgeD0iUHJlZGljdG9ycyIpICsNCiAgICAgICAgICAgIHNjYWxlX3lfY29udGludW91cygpDQpgYGANCg0KDQoNCmBgYHtyfQ0KY1RyZWVQcmVkIDwtIHByZWRpY3QoY3RyZWVNb2RlbCwgbmV3ZGF0YT14X3Rlc3QpDQpjVHJlZVByZWQgPC0gcG9zdFJlc2FtcGxlKHByZWQgPSBjVHJlZVByZWQsIG9icyA9IHlfdGVzdCRQSCkNCmNUcmVlUHJlZA0KYGBgDQoNCg0KPGI+PHU+QmFnZ2VkIFRyZWVzIC0gYmFnZ2VkVHJlZU1vZGVsIDwvYj48L3U+DQoNCg0KYGBge3J9DQpiYWdnZWRUcmVlTW9kZWxfMjAgPC0gdmFySW1wKGJhZ2dlZFRyZWVNb2RlbCkNCmJhZ2dlZFRyZWVNb2RlbF8yMCA8LSBiYWdnZWRUcmVlTW9kZWxfMjAkaW1wb3J0YW5jZSAlPiUgDQogIGFycmFuZ2UoZGVzYyhPdmVyYWxsKSkgDQpiYWdnZWRUcmVlTW9kZWxfMjAgPC0gICBoZWFkKGJhZ2dlZFRyZWVNb2RlbF8yMCwyMCkNCmJhZ2dlZFRyZWVNb2RlbF8yMA0KYGBgDQoNCg0KYGBge3J9DQpiYWdnZWRUcmVlTW9kZWxfMjBfZGY8LSBjb252ZXJ0X3RvcF8yMF90b19kZihiYWdnZWRUcmVlTW9kZWxfMjApDQoNCmJhZ2dlZFRyZWVNb2RlbF8yMF9kZiAlPiUgDQogICAgICAgICAgICBhcnJhbmdlKE92ZXJhbGwpJT4lIA0KICAgICAgICAgICAgbXV0YXRlKG5hbWUgPSBmYWN0b3IoUHJlZGljdG9ycywgbGV2ZWxzPWMoUHJlZGljdG9ycykpKSAlPiUgDQogICAgICAgICAgICBnZ3Bsb3QoYWVzKHg9bmFtZSwgeT1PdmVyYWxsKSkgKw0KICAgICAgICAgICAgZ2VvbV9zZWdtZW50KGFlcyh4ZW5kID0gUHJlZGljdG9ycywgeWVuZCA9IDApKSArDQogICAgICAgICAgICBnZW9tX3BvaW50KHNpemUgPSA0LCBjb2xvciA9ICJncmVlbiIpICsgDQogICAgICAgICAgICB0aGVtZV9taW5pbWFsKCkgKyANCiAgICAgICAgICAgIGNvb3JkX2ZsaXAoKSArDQogICAgICAgICAgICBsYWJzKHRpdGxlPSJiYWdnZWRUcmVlTW9kZWwgUHJlZGljdG9yIFZhcmlhYmxlIEltcG9ydGFuY2UiLA0KICAgICAgICAgICAgICAgeT0iYmFnZ2VkVHJlZU1vZGVsIEltcG9ydGFuY2UiLCB4PSJQcmVkaWN0b3JzIikgKw0KICAgICAgICAgICAgc2NhbGVfeV9jb250aW51b3VzKCkNCmBgYA0KDQoNCg0KYGBge3J9DQpiYWdnZWRUcmVlTW9kZWxQcmVkIDwtIHByZWRpY3QoYmFnZ2VkVHJlZU1vZGVsLCBuZXdkYXRhPXhfdGVzdCkNCmJhZ2dlZFRyZWVNb2RlbFByZWQgPC0gcG9zdFJlc2FtcGxlKHByZWQgPSBiYWdnZWRUcmVlTW9kZWxQcmVkLCBvYnMgPSB5X3Rlc3QkUEgpDQpiYWdnZWRUcmVlTW9kZWwNCmBgYA0KDQoNCg0KPGI+PHU+UmFuZG9tIEZvcmVzdCAtIHJmTW9kZWwgPC9iPjwvdT4NCg0KYGBge3J9DQpyZk1vZGVsXzIwIDwtIHZhckltcChyZk1vZGVsKQ0KcmZNb2RlbF8yMCA8LSByZk1vZGVsXzIwICU+JSANCiAgYXJyYW5nZShkZXNjKE92ZXJhbGwpKSANCnJmTW9kZWxfMjAgPC0gICBoZWFkKHJmTW9kZWxfMjAsMjApDQpyZk1vZGVsXzIwDQpgYGANCg0KDQpgYGB7cn0NCnJmTW9kZWxfMjBfZGY8LSBjb252ZXJ0X3RvcF8yMF90b19kZihyZk1vZGVsXzIwKQ0KDQpyZk1vZGVsXzIwX2RmICU+JSANCiAgICAgICAgICAgIGFycmFuZ2UoT3ZlcmFsbCklPiUgDQogICAgICAgICAgICBtdXRhdGUobmFtZSA9IGZhY3RvcihQcmVkaWN0b3JzLCBsZXZlbHM9YyhQcmVkaWN0b3JzKSkpICU+JSANCiAgICAgICAgICAgIGdncGxvdChhZXMoeD1uYW1lLCB5PU92ZXJhbGwpKSArDQogICAgICAgICAgICBnZW9tX3NlZ21lbnQoYWVzKHhlbmQgPSBQcmVkaWN0b3JzLCB5ZW5kID0gMCkpICsNCiAgICAgICAgICAgIGdlb21fcG9pbnQoc2l6ZSA9IDQsIGNvbG9yID0gInB1cnBsZSIpICsgDQogICAgICAgICAgICB0aGVtZV9taW5pbWFsKCkgKyANCiAgICAgICAgICAgIGNvb3JkX2ZsaXAoKSArDQogICAgICAgICAgICBsYWJzKHRpdGxlPSJyZk1vZGVsIFByZWRpY3RvciBWYXJpYWJsZSBJbXBvcnRhbmNlIiwNCiAgICAgICAgICAgICAgIHk9InJmTW9kZWwgSW1wb3J0YW5jZSIsIHg9IlByZWRpY3RvcnMiKSArDQogICAgICAgICAgICBzY2FsZV95X2NvbnRpbnVvdXMoKQ0KYGBgDQoNCg0KDQpgYGB7cn0NCnJmTW9kZWxQcmVkIDwtIHByZWRpY3QocmZNb2RlbCwgbmV3ZGF0YT14X3Rlc3QpDQpyZk1vZGVsUHJlZCA8LSBwb3N0UmVzYW1wbGUocHJlZCA9IHJmTW9kZWxQcmVkLCBvYnMgPSB5X3Rlc3QkUEgpDQpyZk1vZGVsUHJlZA0KYGBgDQoNCg0KDQoNCg0KIDxiPjx1PkdyYWRpZW50IEJvb3N0IE1vZGVsIC0gZ2JtTW9kZWwgIDwvYj48L3U+DQoNCmBgYHtyfQ0KZ2JtTW9kZWxfMjAgPC0gdmFySW1wKGdibU1vZGVsKQ0KZ2JtTW9kZWxfMjAgPC0gZ2JtTW9kZWxfMjAkaW1wb3J0YW5jZSAlPiUgDQogIGFycmFuZ2UoZGVzYyhPdmVyYWxsKSkgDQpnYm1Nb2RlbF8yMCA8LSAgIGhlYWQoZ2JtTW9kZWxfMjAsMjApDQpnYm1Nb2RlbF8yMA0KYGBgDQoNCg0KYGBge3J9DQpnYm1Nb2RlbF8yMF9kZjwtIGNvbnZlcnRfdG9wXzIwX3RvX2RmKGdibU1vZGVsXzIwKQ0KDQpnYm1Nb2RlbF8yMF9kZiAlPiUgDQogICAgICAgICAgICBhcnJhbmdlKE92ZXJhbGwpJT4lIA0KICAgICAgICAgICAgbXV0YXRlKG5hbWUgPSBmYWN0b3IoUHJlZGljdG9ycywgbGV2ZWxzPWMoUHJlZGljdG9ycykpKSAlPiUgDQogICAgICAgICAgICBnZ3Bsb3QoYWVzKHg9bmFtZSwgeT1PdmVyYWxsKSkgKw0KICAgICAgICAgICAgZ2VvbV9zZWdtZW50KGFlcyh4ZW5kID0gUHJlZGljdG9ycywgeWVuZCA9IDApKSArDQogICAgICAgICAgICBnZW9tX3BvaW50KHNpemUgPSA0LCBjb2xvciA9ICJnb2xkIikgKyANCiAgICAgICAgICAgIHRoZW1lX21pbmltYWwoKSArIA0KICAgICAgICAgICAgY29vcmRfZmxpcCgpICsNCiAgICAgICAgICAgIGxhYnModGl0bGU9ImdibU1vZGVsIFByZWRpY3RvciBWYXJpYWJsZSBJbXBvcnRhbmNlIiwNCiAgICAgICAgICAgICAgIHk9ImdibU1vZGVsIEltcG9ydGFuY2UiLCB4PSJQcmVkaWN0b3JzIikgKw0KICAgICAgICAgICAgc2NhbGVfeV9jb250aW51b3VzKCkNCmBgYA0KDQoNCg0KYGBge3J9DQpnYm1Nb2RlbFByZWQgPC0gcHJlZGljdChnYm1Nb2RlbCwgbmV3ZGF0YT14X3Rlc3QpDQpnYm1Nb2RlbFByZWQ8LSBwb3N0UmVzYW1wbGUocHJlZCA9IGdibU1vZGVsUHJlZCwgb2JzID0geV90ZXN0JFBIKQ0KZ2JtTW9kZWxQcmVkDQpgYGANCg0KDQogPGI+PHU+Q3ViaXN0IE1vZGVsIC0gY3ViaXN0TW9kZWwgIDwvYj48L3U+DQoNCmBgYHtyfQ0KY3ViaXN0TW9kZWxfMjAgPC0gdmFySW1wKGN1YmlzdE1vZGVsKQ0KY3ViaXN0TW9kZWxfMjAgPC0gY3ViaXN0TW9kZWxfMjAkaW1wb3J0YW5jZSAlPiUgDQogIGFycmFuZ2UoZGVzYyhPdmVyYWxsKSkgDQpjdWJpc3RNb2RlbF8yMCA8LSAgIGhlYWQoY3ViaXN0TW9kZWxfMjAsMjApDQpjdWJpc3RNb2RlbF8yMA0KYGBgDQoNCg0KYGBge3J9DQpjdWJpc3RNb2RlbF8yMF9kZjwtIGNvbnZlcnRfdG9wXzIwX3RvX2RmKGN1YmlzdE1vZGVsXzIwKQ0KDQpjdWJpc3RWaXN1YWxNb3N0SW1wb3J0YW50IDwtIGN1YmlzdE1vZGVsXzIwX2RmICU+JSANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXJyYW5nZShPdmVyYWxsKSU+JSANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbXV0YXRlKG5hbWUgPSBmYWN0b3IoUHJlZGljdG9ycywgbGV2ZWxzPWMoUHJlZGljdG9ycykpKSAlPiUgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdncGxvdChhZXMoeD1uYW1lLCB5PU92ZXJhbGwpKSArDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdlb21fc2VnbWVudChhZXMoeGVuZCA9IFByZWRpY3RvcnMsIHllbmQgPSAwKSkgKw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZW9tX3BvaW50KHNpemUgPSA0LCBjb2xvciA9ICJwaW5rIikgKyANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhlbWVfbWluaW1hbCgpICsgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvb3JkX2ZsaXAoKSArDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYnModGl0bGU9ImN1YmlzdE1vZGVsIFByZWRpY3RvciBWYXJpYWJsZSBJbXBvcnRhbmNlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeT0iY3ViaXN0TW9kZWwgSW1wb3J0YW5jZSIsIHg9IlByZWRpY3RvcnMiKSArDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNjYWxlX3lfY29udGludW91cygpDQoNCmN1YmlzdFZpc3VhbE1vc3RJbXBvcnRhbnQNCmBgYA0KDQoNCg0KYGBge3J9DQpjdWJpc3RNb2RlbFByZWQgPC0gcHJlZGljdChjdWJpc3RNb2RlbCwgbmV3ZGF0YT14X3Rlc3QpDQpjdWJpc3RNb2RlbFByZWQ8LSBwb3N0UmVzYW1wbGUocHJlZCA9IGN1YmlzdE1vZGVsUHJlZCwgb2JzID0geV90ZXN0JFBIKQ0KY3ViaXN0TW9kZWxQcmVkDQpgYGANCg0KYGBge3J9DQpwbG90KHJlc2lkdWFscyhjdWJpc3RNb2RlbCkpDQpgYGANCmBgYHtyfQ0KcGxvdG1vKGN1YmlzdE1vZGVsKQ0KYGBgDQoNCg0KDQoNCg0KIyMgUEFSVCA0OiBFVkFMVUFURSBNT0RFTFMNCg0KRnJvbSBvdXIgZXhwZXJpbWVudGF0aW9uIHdpdGggMTIgZGlmZmVyZW50IG1vZGVscywgd2Ugc2F3IHRoYXQgdGhlIEN1YmlzdCBtb2RlbCBoYWQgdGhlIGxvd2VzdCBSTVNFICgwLjEwOTc2KSB2YWx1ZSBhcyB3ZWxsIGFzIHRoZSBsb3dlc3QgTUFFIHZhbHVlICgwLjA4MSkuIEl0IGFsc28gaGFkIHRoZSBoaWdoZXN0IFJzcXVhcmVkIHZhbHVlICgwLjYwMSkuICANCg0KDQpgYGB7ciBpbmNsdWRlPUZBTFNFfQ0KcGxzX3Jlc3VsdHMgPC0gcGxzRml0MSRyZXN1bHRzICU+JSANCiAgZmlsdGVyKG5jb21wID09OCkgJT4lIA0KICBzZWxlY3QoUk1TRSwgUnNxdWFyZWQsIE1BRSkNCmBgYA0KDQpgYGB7ciBpbmNsdWRlPUZBTFNFfQ0KcmlkZ2VfcmVzdWx0cyA8LSB0cmFpbl9zZXRfcmVzdWx0cyAlPiUgDQogICAgICAgICAgICAgICAgICBmaWx0ZXIocm93Lm5hbWVzKHRyYWluX3NldF9yZXN1bHRzKSA9PSAzKSU+JSANCiAgICAgICAgICAgICAgICAgIHNlbGVjdChSTVNFLCBSc3F1YXJlZCwgTUFFKQ0KDQpgYGANCg0KDQpgYGB7ciBpbmNsdWRlPUZBTFNFfQ0Ka25uTW9kZWxfcmVzIDwtIGtubk1vZGVsJHJlc3VsdHMgJT4lIA0KICAgICAgICAgICAgICAgIGZpbHRlcihrPT03KSAlPiUgDQogICAgICAgICAgICAgICAgc2VsZWN0KFJNU0UsIFJzcXVhcmVkLCBNQUUpDQoNCg0KYGBgDQoNCg0KDQoNCg0KYGBge3IgaW5jbHVkZT1GQUxTRX0NCg0KbGluZWFyX01vZGVsX3JlcyA8LSBjKCdMaW5lYXIgTW9kZWwnLCBsbUZpdDEkcmVzdWx0cyRSTVNFLCBsbUZpdDEkcmVzdWx0cyRSc3F1YXJlZCwgbG1GaXQxJHJlc3VsdHMkTUFFKQ0KcGFydGlhbF9sZWFzdF9zcXVhcmVfcmVzIDwtIGMoJ1BhcnRpYWwgTGVhc3QgU3F1YXJlJywgcGxzX3Jlc3VsdHMkUk1TRSwgcGxzX3Jlc3VsdHMkUnNxdWFyZWQsIHBsc19yZXN1bHRzJE1BRSkNCnJpZGdlX3JlcyA8LSBjKCdSaWRnZSBSZWdyZXNzaW9uJywgcmlkZ2VfcmVzdWx0cyRSTVNFLCByaWRnZV9yZXN1bHRzJFJzcXVhcmVkLCByaWRnZV9yZXN1bHRzJE1BRSkNCg0Ka25uX3JlcyA8LSBjKCdLTk4nLCBrbm5Nb2RlbF9yZXMkUk1TRSwga25uTW9kZWxfcmVzJFJzcXVhcmVkLCBrbm5Nb2RlbF9yZXMkTUFFKQ0Kbm5fcmVzIDwtIGMoJ05ldXJhbCBOZXR3b3JrJywgTk5FVFsxXSwgTk5FVFsyXSwgTk5FVFszXSkNCm1hcnNfcmVzIDwtIGMoJ011bHRpdmFyaWF0ZSBBZGFwdGl2ZSBSZWdyZXNzaW9uIFNwbGluZScsIE1BUlNbMV0sIE1BUlNbMl0sIE1BUlNbM10pDQpzdm1MX3JlcyA8LSBjKCdTdXBwb3J0IFZlY3RvciBNYWNoaW5lcyAtIExpbmVhcicsIHN2bUxbMV0sIHN2bUxbMl0sIHN2bUxbM10pDQoNCmJUTV9yZXMgPC0gYygnYmFnZ2VkVHJlZSBNb2RlbCcsIGJhZ2dlZFRyZWVNb2RlbCRyZXN1bHRzJFJNU0UsIGJhZ2dlZFRyZWVNb2RlbCRyZXN1bHRzJFJzcXVhcmVkLCBiYWdnZWRUcmVlTW9kZWwkcmVzdWx0cyRNQUUpDQpjVHJlZU1vZGVsX3JlcyA8LSBjKCdjVHJlZSBNb2RlbCcsIGNUcmVlUHJlZFsxXSwgY1RyZWVQcmVkWzJdLCBjVHJlZVByZWRbM10pDQpyYW5kb21Gb3Jlc3RNb2RlbFByZWRfcmVzIDwtIGMoJ1JhbmRvbSBGb3Jlc3QgTW9kZWwnLCByZk1vZGVsUHJlZFsxXSwgcmZNb2RlbFByZWRbMl0sIHJmTW9kZWxQcmVkWzNdKQ0KZ3JhZGllbnRCb29zdE1vZGVsUHJlZF9yZXMgPC0gYygnR3JhZGllbnQgQm9vc3QgTW9kZWwnLCBnYm1Nb2RlbFByZWRbMV0sIGdibU1vZGVsUHJlZFsyXSwgZ2JtTW9kZWxQcmVkWzNdKQ0KY3ViaXN0TW9kZWxQcmVkX3JlcyA8LSBjKCdDdWJpc3QgTW9kZWwnLGN1YmlzdE1vZGVsUHJlZFsxXSwgY3ViaXN0TW9kZWxQcmVkWzJdLCBjdWJpc3RNb2RlbFByZWRbM10pDQoNCnJlc3VsdHM8LSBhcy5kYXRhLmZyYW1lKHJiaW5kKGxpbmVhcl9Nb2RlbF9yZXMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXJ0aWFsX2xlYXN0X3NxdWFyZV9yZXMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICByaWRnZV9yZXMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBrbm5fcmVzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm5fcmVzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWFyc19yZXMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdm1MX3JlcywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNUcmVlTW9kZWxfcmVzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYlRNX3JlcywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJhbmRvbUZvcmVzdE1vZGVsUHJlZF9yZXMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBncmFkaWVudEJvb3N0TW9kZWxQcmVkX3JlcywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGN1YmlzdE1vZGVsUHJlZF9yZXMpKQ0KY29sbmFtZXMocmVzdWx0cykgPC0gYygnTW9kZWwnLCAnUk1TRScsICdSc3F1YXJlZCcsICdNQUUnKQ0Kcm93Lm5hbWVzKHJlc3VsdHMpIDwtIGMoMTpucm93KHJlc3VsdHMpKQ0KcmVzdWx0c190YWJsZSA8LSByZXN1bHRzICU+JSANCiAgYXJyYW5nZShSTVNFKQ0KYGBgDQoNCg0KDQoNCmBgYHtyfQ0Ka25pdHI6OmthYmxlKHJlc3VsdHNfdGFibGUsIm1hcmtkb3duIikNCmBgYA0KDQoNCg0KDQoNCg0KIyMgUEFSVCA1OiBVU0UgVEhFIEJFU1QgTU9ERUwgVE8gRk9SRUNBU1QgUEgNCg0KV2Ugd2lsbCB1c2UgdGhlIEN1YmlzdCBtb2RlbCBhZ2FpbnN0IHRoZSBTdHVkZW50IGV2YWx1YXRpb24gZGF0YSBhbmQgbWFrZSBwcmVkaWN0aW9ucyBvZiB0aGUgUEggdmFyaWFibGUuIA0KDQpGaXJzdCwgYXMgd2UgZGlkIHdpdGggdGhlIFN0dWRlbnQgdHJhaW4gZGF0YSwgd2UgaGF2ZSB0byBjb252ZXJ0IHRoZSBCcmFuZC5Db2RlIGNhdGVnb3JpY2FsIHZhbHVlIGluIHRoZSBTdHVkZW50IGV2YWx1YXRpb24gZGF0YSB0byBEdW1teSB2YXJpYWJsZXMuIA0KDQoNCmBgYHtyfQ0KbW9kMjwtIGR1bW15VmFycyh+QnJhbmQuQ29kZSwNCiAgICAgICAgICBkYXRhPXByZWRpY3RvcnNfZXZhbHVhdGUsDQogICAgICAgICAgbGV2ZWxzT25seSA9IEZBTFNFKQ0KbW9kMg0KYGBgDQoNCg0KYGBge3J9DQpkdW1taWVzMiA8LSBhcy5kYXRhLmZyYW1lKHByZWRpY3QobW9kLCBwcmVkaWN0b3JzX2V2YWx1YXRlKSkNCnByZWRpY3RvcnNfZXZhbHVhdGUyIDwtIHN1YnNldChwcmVkaWN0b3JzX2V2YWx1YXRlLCBzZWxlY3QgPSAtYyhCcmFuZC5Db2RlKSkNCnByZWRpY3RvcnNfZXZhbHVhdGUyIDwtIGNiaW5kKGR1bW1pZXMyLHByZWRpY3RvcnNfZXZhbHVhdGUpDQoNCmBgYA0KDQoNCmBgYHtyIH0NCmN1YmlzdFByZWQgPC0gcm91bmQocHJlZGljdChjdWJpc3RNb2RlbCwgbmV3ZGF0YT1wcmVkaWN0b3JzX2V2YWx1YXRlMiksMikNCmhlYWRfcHJlZGljdGlvbnMgPC0gaGVhZChjdWJpc3RQcmVkLDEwKQ0KYGBgDQoNCg0KDQpgYGB7ciwgZWNobz1GQUxTRX0NCmtuaXRyOjprYWJsZShoZWFkX3ByZWRpY3Rpb25zLCJtYXJrZG93biIpDQpgYGANCg0KYGBge3J9DQpleHBvcnRlZF9wcmVkaWN0aW9ucyA8LSBjYmluZChjdWJpc3RQcmVkLHByZWRpY3RvcnNfZXZhbHVhdGUpDQpuYW1lcyhleHBvcnRlZF9wcmVkaWN0aW9ucylbMV0gPC0gIlByZWRpY3RlZCBQSCINCmBgYA0KDQoNCiMjIFBBUlQgNjogQ09OQ0xVU0lPTlMNCg0KVGhlIGRhdGEgc2NpZW5jZSB0ZWFtIGZvdW5kIHRoYXQgdGhlIEN1YmlzdCBtb2RlbCBpcyB0aGUgYmVzdCBmb3IgcHJlZGljdGluZyB0aGUgUEggdmFsdWUuIFRoZSBtb3N0IGltcG9ydGFudCBwcmVkaWN0b3JzIGZyb20gdGhpcyBtb2RlbCBhcmUgc2hvd24gaW4gdGhlIHZpc3VhbGl6YXRpb24gYmVsb3cuIFRoZSB0b3AgZml2ZSBwcmVkaWN0b3JzIGFyZSBNbmYuRmxvdywgRGVuc2l0eSwgVGVtcGVyYXR1cmUsIFByZXNzdXJlLlZhY3V1bSwgYW5kIEZpbGxlciBMZXZlbC4gVHdvIGRpc2NyZXRlIGNhdGVnb3JpY2FsIGZhY3RvcnMsIEJyYW5kIENvZGVzIEMgYW5kIEQsIGFyZSBhbHNvIGluIHRoZSBtb3N0IGltcG9ydGFudCBwcmVkaWN0b3JzLiANCg0KV2UgaGF2ZSBleHBvcnRlZCB0aGUgcHJlZGljdGVkIFBIIHZhbHVlcyBpbiB0aGUgYXR0YWNoZWQgZXhjZWwgZmlsZS4gDQoNCg0KYGBge3J9DQpjdWJpc3RWaXN1YWxNb3N0SW1wb3J0YW50DQpgYGANCg0K