Missingness

Why is it missing?

  • MCAR (Missing Completely At Random): “There’s no relationship between whether a data point is missing and any values in the data set, missing or observed.”

  • MAR (Missing At Random): “means the propensity for a data point to be missing is not related to the missing data, but it is related to some of the observed data”. In other words, the response is missing because of another question you asked. You asked me if I like spiders, I said no. Next you ask me if I have a pet spider. I dont respond.

  • MNAR (Missing Not At Random): “means the propensity for a data point to be missing is related to the missing data”. You have no idea why the response/question is missing, thus you cannot infer it.

Best MCAR. Worst is MNAR.

How big is my data set

  • Normal to Huge data set: Losing 5-10% of your data is not a problem. Move on in life. If larger, then you have to start thinking about missing analysis and replacement.
  • Very small data set (5-10 people per IV): Tea leaf reading or voodoo.

Common methods to Impute Missing data

  • Impute: “assign (a value) to something by inference from the value of the products or processes to which it contributes”.

Classical Methods (assumining MCAR)

  • Listwise deletion: Removes whole subject if one data point is missing. Easy to implement, but loses alot of data.

  • Pairwise deletion: Removes just that case of that subjects data (keeps most data). Can be to implement (can mess up the error term or make DF differ from model to model), but less data is lost.

  • Mean imputation: Fill in the missing data points with the mean of that variable. Lowers variance overall and keeps subject. No data loss, but you are affecting the error term and keeping the DF. This can create problems in very small samples.

  • Conditional mean imputation: Replaces missing values with the predicted scores from a linear regression equation. [Very complicated when MAR is an issue]

  • Most common in psychology tend to be Mean or Pair- or listwise (SPSS defaults depending on analysis. R defaults to listwise in most cases)

  • For a review of the theory see: Peugh, J. L., & Enders, C. K. (2004). Missing data in educational research: A review of reporting practices and suggestions for improvement. Review of Educational Research, 74, 525-556.

Classical Methods

  • We will do the listwise and mean replacement types
  • Simulate a simple regression - remove cases completely at random and test

Typical Sample Size

  • Simulate a data set
library(car)
set.seed(42)
n <- 50
# IVs
X <- runif(n, -10, 10)
Z <- rnorm(n, -10, 10)
# Our equation to  create Y
Y <- .8*X - .4*Z + 2 + rnorm(n, sd=10)
#Built our data frame
DataSet1<-data.frame(DV=Y,IV1=X,IV2=Z)
  • Remove IVs and DVs at random (ampute) at about 40% total across the IVs and DV
library(mice)
set.seed(666)
Amputed.Data<-ampute(data =DataSet1, prop = 0.4, mech = "MCAR")
DataSet1.M<-Amputed.Data$amp
  • Lets visualize the missingness
library(VIM)
aggr(DataSet1.M, col=c('navyblue','red'), numbers=TRUE, 
     sortVars=TRUE, labels=names(data), cex.axis=.7, 
     gap=3, 
     ylab=c("Histogram of missing data","Pattern"))
## 
##  Variables sorted by number of missings: 
##  Variable Count
##       IV1  0.20
##       IV2  0.16
##        DV  0.14

  • Lets visualize pairwise scatterplots based on where the missing data seems to be
marginplot(DataSet1.M[c(1,2)])
marginplot(DataSet1.M[c(2,3)])
marginplot(DataSet1.M[c(1,3)])

  • Run the regression of the original and missing data side by side
  • R defaults to listwise deletion (most conservative approach)
  • Pairwise-deletion regression is not build into R by default (but there is is pairwise deletion for other methods)
  • Original, Listwise, mean imputed (using the mice package)
    • Note: The function complete is in multiple packages (tidyr and mice) and they conflict, so I will use mice::complete to tell R which complete I want)
Orginal<-lm(DV~IV1+IV2, data= DataSet1)
Listwise<-lm(DV~IV1+IV2, data= DataSet1.M)

# Mean impute using MICE 
DataSet1.MM<- mice::complete(mice(DataSet1.M, meth='mean',printFlag = FALSE))
MeanImpute<-lm(DV~IV1+IV2, data= DataSet1.MM)
Dependent variable:
DV
Orginal Listwise Mean Impute
(1) (2) (3)
Constant 2.612 (1.909) 3.164 (2.665) 3.402 (1.846)
IV1 0.133 (0.209) 0.017 (0.276) -0.056 (0.216)
IV2 -0.428** (0.131) -0.361 (0.174) -0.428** (0.131)
Observations 50 25 50
R2 0.192 0.165 0.186
Adjusted R2 0.158 0.089 0.151
F Statistic 5.590** (df = 2; 47) 2.169 (df = 2; 22) 5.373** (df = 2; 47)
Note: p<0.05; p<0.01; p<0.001
  • If we run 1000 simulations for each methods, we can get an estimate of there Error
    • Betas for each parameter in the model (Estimated b - Orginal b/Orginal b)
      • We will take the median across simulations
    • Decisions errors using significance [Type I and Type II]
      • Type I = Orginal is not sig, but estimated is sig
      • Type II = Orginal is sig, but estimated is not sig
      • Look to the “orginal” simulation table to see which is possible for that model term

Huge Data Set

  • N = 500
set.seed(42)
n <- 500
# IVs
X <- runif(n, -10, 10)
Z <- rnorm(n, -10, 10)
# Our equation to  create Y
Y <- .8*X - .4*Z + 2 + rnorm(n, sd=10)
#Built our data frame
DataSet2<-data.frame(DV=Y,IV1=X,IV2=Z)
#remove cases at random 
set.seed(666)
Amputed.Data.2<-ampute(data =DataSet2, prop = 0.4, mech = "MCAR")
DataSet2.M<-Amputed.Data.2$amp
Orginal.2<-lm(DV~IV1+IV2, data= DataSet2)
Listwise.2<-lm(DV~IV1+IV2, data= DataSet2.M)
# Mean impute using MICE 
DataSet2.MM<- mice::complete(mice(DataSet2.M, meth='mean',printFlag = FALSE))
MeanImpute.2<-lm(DV~IV1+IV2, data= DataSet2.MM)
Dependent variable:
DV
Orginal Listwise Mean Impute
(1) (2) (3)
Constant 2.249** (0.680) 2.439** (0.932) 3.254*** (0.694)
IV1 0.817*** (0.079) 0.858*** (0.107) 0.710*** (0.084)
IV2 -0.378*** (0.047) -0.352*** (0.066) -0.274*** (0.050)
Observations 500 303 500
R2 0.258 0.231 0.170
Adjusted R2 0.255 0.226 0.167
F Statistic 86.263*** (df = 2; 497) 44.974*** (df = 2; 300) 50.876*** (df = 2; 497)
Note: p<0.05; p<0.01; p<0.001
  • We repeat the Error simulations for each methods using 500 subjects

Tiny Sample Size

  • N = 15
set.seed(42)
n <- 15
# IVs
X <- runif(n, -10, 10)
Z <- rnorm(n, -10, 10)
# Our equation to  create Y
Y <- .8*X - .4*Z + 2 + rnorm(n, sd=10)
#Built our data frame
DataSet3<-data.frame(DV=Y,IV1=X,IV2=Z)

#remove cases at random 
set.seed(666)
Amputed.Data.3<-ampute(data =DataSet3, prop = 0.4, mech = "MCAR")
DataSet3.M<-Amputed.Data.3$amp
Orginal.3<-lm(DV~IV1+IV2, data= DataSet3)
Listwise.3<-lm(DV~IV1+IV2, data= DataSet3.M)

# Mean impute using MICE 
DataSet3.MM<- mice::complete(mice(DataSet3.M, meth='mean',printFlag = FALSE))
MeanImpute.3<-lm(DV~IV1+IV2, data= DataSet3.MM)
Dependent variable:
DV
Orginal Listwise Mean Impute
(1) (2) (3)
Constant 3.822 (3.024) 3.259 (5.843) 1.427 (3.031)
IV1 1.310* (0.526) 0.802 (1.110) 1.124 (0.575)
IV2 -0.225 (0.272) 0.105 (0.854) -0.603* (0.263)
Observations 15 8 15
R2 0.405 0.127 0.373
Adjusted R2 0.306 -0.222 0.268
F Statistic 4.081* (df = 2; 12) 0.364 (df = 2; 5) 3.565 (df = 2; 12)
Note: p<0.05; p<0.01; p<0.001
  • We repeat the Error simulations for each methods using 15 subjects

Typical Samples with MNAR

  • N = 50
#remove cases at random from our orginal data set
set.seed(666)
Amputed.Data.4<-ampute(data =DataSet1, prop = 0.4, mech = "MNAR")
DataSet4.M<-Amputed.Data.4$amp
Orginal.4<-lm(DV~IV1+IV2, data= DataSet1)
Listwise.4<-lm(DV~IV1+IV2, data= DataSet4.M)
# Mean impute using MICE 
DataSet4.MM<- mice::complete(mice(DataSet4.M, meth='mean',printFlag = FALSE))
MeanImpute.4<-lm(DV~IV1+IV2, data= DataSet4.MM)
Dependent variable:
DV
Orginal Listwise Mean Impute
(1) (2) (3)
Constant 2.612 (1.909) 1.368 (2.277) 1.948 (1.708)
IV1 0.133 (0.209) 0.133 (0.266) 0.096 (0.197)
IV2 -0.428** (0.131) -0.421* (0.155) -0.388** (0.118)
Observations 50 30 50
R2 0.192 0.222 0.189
Adjusted R2 0.158 0.164 0.154
F Statistic 5.590** (df = 2; 47) 3.851* (df = 2; 27) 5.468** (df = 2; 47)
Note: p<0.05; p<0.01; p<0.001
  • We repeat the Error simulations for each methods for MNAR (50 subjects)

Typical Samples with MAR

  • N = 50
#remove cases from our orginal data set
set.seed(42)
Amputed.Data.5<-ampute(data =DataSet1, prop = 0.4, mech = "MAR")
DataSet5.M<-Amputed.Data.5$amp
Orginal.5<-lm(DV~IV1+IV2, data= DataSet1)
Listwise.5<-lm(DV~IV1+IV2, data= DataSet5.M)
# Mean impute using MICE 
DataSet5.MM<- mice::complete(mice(DataSet5.M, meth='mean',printFlag = FALSE))
MeanImpute.5<-lm(DV~IV1+IV2, data= DataSet5.MM)
Dependent variable:
DV
Orginal Listwise Mean Impute
(1) (2) (3)
Constant 2.612 (1.909) 0.863 (2.265) 2.996 (1.915)
IV1 0.133 (0.209) 0.134 (0.228) 0.011 (0.216)
IV2 -0.428** (0.131) -0.439** (0.141) -0.422** (0.133)
Observations 50 34 50
R2 0.192 0.244 0.176
Adjusted R2 0.158 0.195 0.141
F Statistic 5.590** (df = 2; 47) 5.000* (df = 2; 31) 5.023* (df = 2; 47)
Note: p<0.05; p<0.01; p<0.001
  • We repeat the Error simulations for each methods for MAR (50 subjects)

Notes

  • Mean imputing works OK in medium to large sample sizes
  • But it creates large bias in small samples
  • If you don’t have MCAR you have to turn to more complex methods

Modern approaches

  • There are many modern approaches often based on maximum likelihood estimation (ML) or Multiple Imputation (MI) or even Bayesian approaches
  • “ML estimation is to identify the population parameter values most likely to have produced a particular sample of data. This usually requires an iterative process whereby the model fitting program”tries out” different values for the parameters of interest (e.g., regression coefficients) en route to identifying the values most likely to have produced the sample data.” (Peugh & Enders, 2004)
  • Multiple Imputation: “Rather than treating a single set of imputed values as”true” estimates of the missing values, MI creates a number of imputed data sets (frequently between 5 and 10), each of which contains a different plausible estimate of the missing values.” (Peugh & Enders, 2004)
  • Lets test out MI as its the newest and considered an improvement over ML

Multiple Imputation by Chained Equations

  • See Azur et al, 2011 for details
  • MICE package in R: Lots of old and modern algorithms to choose from (also for design beyond linear regression)
  • Predictive mean matching is a good version to use as it “is a general purpose semi-parametric imputation method. … imputations are restricted to the observed values and that it can preserve non-linear relations…” (Buuren & Groothuis-Oudshoorn, 2011)
  • We will work from our MAR missing Dataset5.M
  • We can compare this to our classical methods and some other methods which the package allows (Bootstapping & Bayesian)

Typical sample size

#Linear regression using bootstrap
DataDataSet5.Boot<- mice::complete(mice(DataSet5.M, m = 10,meth='norm.boot',
                                  printFlag = FALSE, seed = 666))
Boot<-lm(DV~IV1+IV2, data= DataDataSet5.Boot)

#Bayesian linear regression
DataDataSet5.BLR<- mice::complete(mice(DataSet5.M, m = 10,meth='norm',
                                 printFlag = FALSE, seed = 666))
BLR<-lm(DV~IV1+IV2, data= DataDataSet5.BLR)

#Predictive mean matching 
DataDataSet5.pmm<- mice::complete(mice(DataSet5.M, m = 10,meth='pmm',
                                 printFlag = FALSE, seed = 666))
PMM<-lm(DV~IV1+IV2, data= DataDataSet5.pmm)
Dependent variable:
DV
Orginal Boot BLR PMM
(1) (2) (3) (4)
Constant 2.612 (1.909) 0.531 (1.889) -0.099 (1.817) 1.355 (1.779)
IV1 0.133 (0.209) -0.011 (0.210) -0.007 (0.191) 0.002 (0.206)
IV2 -0.428** (0.131) -0.551*** (0.117) -0.546*** (0.104) -0.478*** (0.108)
Observations 50 50 50 50
R2 0.192 0.325 0.377 0.295
Adjusted R2 0.158 0.296 0.351 0.265
F Statistic (df = 2; 47) 5.590** 11.289*** 14.247*** 9.835***
Note: p<0.05; p<0.01; p<0.001
  • Predictive mean matching is the default of this program and you see it does a good job

Small sample size

  • Use the same exact small data (N = 15)
#Linear regression using bootstrap
DataDataSet3.Boot<- mice::complete(mice(DataSet3.M, m = 15,meth='norm.boot',
                                  printFlag = FALSE, seed = 666))
Boot.3<-lm(DV~IV1+IV2, data= DataDataSet3.Boot)

#Bayesian linear regression
DataDataSet3.BLR<- mice::complete(mice(DataSet3.M, m = 15,meth='norm',
                                 printFlag = FALSE, seed = 666))
BLR.3<-lm(DV~IV1+IV2, data= DataDataSet3.BLR)

#Predictive mean matching 
DataDataSet3.pmm<- mice::complete(mice(DataSet3.M, m = 15,meth='pmm',
                                 printFlag = FALSE, seed = 666))
PMM.3<-lm(DV~IV1+IV2, data= DataDataSet3.pmm)
Dependent variable:
DV
Orginal Boot BLR PMM
(1) (2) (3) (4)
Constant 3.822 (3.024) 0.978 (3.532) -0.047 (4.002) 4.401 (4.016)
IV1 1.310* (0.526) 0.741 (0.683) 1.192 (0.756) 1.038 (0.670)
IV2 -0.225 (0.272) -0.800 (0.397) -0.489 (0.317) -0.441 (0.339)
Observations 15 15 15 15
R2 0.405 0.265 0.219 0.213
Adjusted R2 0.306 0.142 0.088 0.082
F Statistic (df = 2; 12) 4.081* 2.161 1.679 1.621
Note: p<0.05; p<0.01; p<0.001
  • We need to find practicer of the dark arts, as none of the methods did a good job

Notes

  • You have to manually set the number of iterations (number of passes)
  • You will have to research each algorithm for your particular data missingness

Best Practices

  • Each situation is different and which method you use depends on many factors
  • Also you must think carefully about the assumptions you are making
  • For example, if you mean (or PMM) replace you are assuming the data are normal
  • There are other advanced methods for specific situation: “hot decking” for survey research; nearest neighbor, splines, or autoregressive methods for time-series or longitudinal data
  • Best practice: compare serval logical replacement methods and also compare it to listwise removal. If the story you are trying to tell is very different based on each method and very different as it relates to the listwise removal it suggests you have not factored in some assumption correctly and best to rethink what you are doing.
LS0tDQp0aXRsZTogJ01pc3NpbmcgRGF0YScNCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMNCiAgICBmb250c2l6ZTogOHB0DQogICAgaGlnaGxpZ2h0OiB0ZXh0bWF0ZQ0KICAgIG51bWJlcl9zZWN0aW9uczogbm8NCiAgICB0aGVtZTogZmxhdGx5DQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZmxvYXQ6DQogICAgICBjb2xsYXBzZWQ6IG5vDQotLS0NCg0KDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGNhY2hlID0gVFJVRSkgI1Nob3cgYWxsIHNjcmlwdCBieSBkZWZhdWx0DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpICNTaG93IGFsbCBzY3JpcHQgYnkgZGVmYXVsdA0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KG1lc3NhZ2UgPSBGQUxTRSkgI2hpZGUgbWVzc2FnZXMgDQprbml0cjo6b3B0c19jaHVuayRzZXQod2FybmluZyA9ICBGQUxTRSkgI2hpZGUgcGFja2FnZSB3YXJuaW5ncyANCmtuaXRyOjpvcHRzX2NodW5rJHNldChmaWcud2lkdGg9NSkgI1NldCBkZWZhdWx0IGZpZ3VyZSBzaXplcw0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGZpZy5oZWlnaHQ9My41KSAjU2V0IGRlZmF1bHQgZmlndXJlIHNpemVzDQprbml0cjo6b3B0c19jaHVuayRzZXQoZmlnLmFsaWduPSdjZW50ZXInKSAjU2V0IGRlZmF1bHQgZmlndXJlDQprbml0cjo6b3B0c19jaHVuayRzZXQoZmlnLnNob3cgPSAiaG9sZCIpICNTZXQgZGVmYXVsdCBmaWd1cmUNCmtuaXRyOjpvcHRzX2NodW5rJHNldChyZXN1bHRzID0gImhvbGQiKSANCmBgYA0KDQojIE1pc3NpbmduZXNzDQoNCiMjIFdoeSBpcyBpdCBtaXNzaW5nPw0KLSBNQ0FSIChNaXNzaW5nIENvbXBsZXRlbHkgQXQgUmFuZG9tKTogICJUaGVyZSdzIG5vIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHdoZXRoZXIgYSBkYXRhIHBvaW50IGlzIG1pc3NpbmcgYW5kIGFueSB2YWx1ZXMgaW4gdGhlIGRhdGEgc2V0LCBtaXNzaW5nIG9yIG9ic2VydmVkLiIgDQoNCi0gTUFSIChNaXNzaW5nIEF0IFJhbmRvbSk6ICJtZWFucyB0aGUgcHJvcGVuc2l0eSBmb3IgYSBkYXRhIHBvaW50IHRvIGJlIG1pc3NpbmcgaXMgbm90IHJlbGF0ZWQgdG8gdGhlIG1pc3NpbmcgZGF0YSwgYnV0IGl0IGlzIHJlbGF0ZWQgdG8gc29tZSBvZiB0aGUgb2JzZXJ2ZWQgZGF0YSIuIEluIG90aGVyIHdvcmRzLCB0aGUgcmVzcG9uc2UgaXMgbWlzc2luZyBiZWNhdXNlIG9mIGFub3RoZXIgcXVlc3Rpb24geW91IGFza2VkLiBZb3UgYXNrZWQgbWUgaWYgSSBsaWtlIHNwaWRlcnMsIEkgc2FpZCBuby4gTmV4dCB5b3UgYXNrIG1lIGlmIEkgaGF2ZSBhIHBldCBzcGlkZXIuIEkgZG9udCByZXNwb25kLiANCg0KLSBNTkFSIChNaXNzaW5nIE5vdCBBdCBSYW5kb20pOiAibWVhbnMgdGhlIHByb3BlbnNpdHkgZm9yIGEgZGF0YSBwb2ludCB0byBiZSBtaXNzaW5nIGlzIHJlbGF0ZWQgdG8gdGhlIG1pc3NpbmcgZGF0YSIuIFlvdSBoYXZlIG5vIGlkZWEgd2h5IHRoZSByZXNwb25zZS9xdWVzdGlvbiBpcyBtaXNzaW5nLCB0aHVzIHlvdSBjYW5ub3QgaW5mZXIgaXQuIA0KDQpCZXN0IE1DQVIuIFdvcnN0IGlzIE1OQVIuIA0KIA0KLSBTZWU6IGh0dHA6Ly93d3cudGhlYW5hbHlzaXNmYWN0b3IuY29tL21hci1hbmQtbWNhci1taXNzaW5nLWRhdGEvDQotIFNlZTogaHR0cHM6Ly93d3cuci1ibG9nZ2Vycy5jb20vaW1wdXRpbmctbWlzc2luZy1kYXRhLXdpdGgtci1taWNlLXBhY2thZ2UvDQoNCiMjIEhvdyBiaWcgaXMgbXkgZGF0YSBzZXQNCi0gTm9ybWFsIHRvIEh1Z2UgZGF0YSBzZXQ6IExvc2luZyA1LTEwJSBvZiB5b3VyIGRhdGEgaXMgbm90IGEgcHJvYmxlbS4gTW92ZSBvbiBpbiBsaWZlLiBJZiBsYXJnZXIsIHRoZW4geW91IGhhdmUgdG8gc3RhcnQgdGhpbmtpbmcgYWJvdXQgbWlzc2luZyBhbmFseXNpcyBhbmQgcmVwbGFjZW1lbnQuDQotIFZlcnkgc21hbGwgZGF0YSBzZXQgKDUtMTAgcGVvcGxlIHBlciBJVik6IFRlYSBsZWFmIHJlYWRpbmcgb3Igdm9vZG9vLiAgDQoNCiMgQ29tbW9uIG1ldGhvZHMgdG8gSW1wdXRlIE1pc3NpbmcgZGF0YQ0KLSBJbXB1dGU6ICJhc3NpZ24gKGEgdmFsdWUpIHRvIHNvbWV0aGluZyBieSBpbmZlcmVuY2UgZnJvbSB0aGUgdmFsdWUgb2YgdGhlIHByb2R1Y3RzIG9yIHByb2Nlc3NlcyB0byB3aGljaCBpdCBjb250cmlidXRlcyIuDQoNCiMjIENsYXNzaWNhbCBNZXRob2RzIChhc3N1bWluaW5nIE1DQVIpDQotIExpc3R3aXNlIGRlbGV0aW9uOiBSZW1vdmVzIHdob2xlIHN1YmplY3QgaWYgb25lIGRhdGEgcG9pbnQgaXMgbWlzc2luZy4gRWFzeSB0byBpbXBsZW1lbnQsIGJ1dCBsb3NlcyBhbG90IG9mIGRhdGEuIA0KLSBQYWlyd2lzZSBkZWxldGlvbjogUmVtb3ZlcyBqdXN0IHRoYXQgY2FzZSBvZiB0aGF0IHN1YmplY3RzIGRhdGEgKGtlZXBzIG1vc3QgZGF0YSkuIENhbiBiZSB0byBpbXBsZW1lbnQgKGNhbiBtZXNzIHVwIHRoZSBlcnJvciB0ZXJtIG9yIG1ha2UgREYgZGlmZmVyIGZyb20gbW9kZWwgdG8gbW9kZWwpLCBidXQgbGVzcyBkYXRhIGlzIGxvc3QuIA0KLSBNZWFuIGltcHV0YXRpb246IEZpbGwgaW4gdGhlIG1pc3NpbmcgZGF0YSBwb2ludHMgd2l0aCB0aGUgbWVhbiBvZiB0aGF0IHZhcmlhYmxlLiBMb3dlcnMgdmFyaWFuY2Ugb3ZlcmFsbCBhbmQga2VlcHMgc3ViamVjdC4gTm8gZGF0YSBsb3NzLCBidXQgeW91IGFyZSBhZmZlY3RpbmcgdGhlIGVycm9yIHRlcm0gYW5kIGtlZXBpbmcgdGhlIERGLiBUaGlzIGNhbiBjcmVhdGUgcHJvYmxlbXMgaW4gdmVyeSBzbWFsbCBzYW1wbGVzLg0KLSBDb25kaXRpb25hbCBtZWFuIGltcHV0YXRpb246ICBSZXBsYWNlcyBtaXNzaW5nIHZhbHVlcyB3aXRoIHRoZSBwcmVkaWN0ZWQgc2NvcmVzIGZyb20gYSBsaW5lYXIgcmVncmVzc2lvbiBlcXVhdGlvbi4gW1ZlcnkgY29tcGxpY2F0ZWQgd2hlbiBNQVIgaXMgYW4gaXNzdWVdDQoNCi0gTW9zdCBjb21tb24gaW4gcHN5Y2hvbG9neSB0ZW5kIHRvIGJlIE1lYW4gb3IgUGFpci0gb3IgbGlzdHdpc2UgKFNQU1MgZGVmYXVsdHMgZGVwZW5kaW5nIG9uIGFuYWx5c2lzLiBSIGRlZmF1bHRzIHRvIGxpc3R3aXNlIGluIG1vc3QgY2FzZXMpDQoNCi0gRm9yIGEgcmV2aWV3IG9mIHRoZSB0aGVvcnkgc2VlOiBQZXVnaCwgSi4gTC4sICYgRW5kZXJzLCBDLiBLLiAoMjAwNCkuIE1pc3NpbmcgZGF0YSBpbiBlZHVjYXRpb25hbCByZXNlYXJjaDogQSByZXZpZXcgb2YgcmVwb3J0aW5nIHByYWN0aWNlcyBhbmQgc3VnZ2VzdGlvbnMgZm9yIGltcHJvdmVtZW50LiBSZXZpZXcgb2YgRWR1Y2F0aW9uYWwgUmVzZWFyY2gsIDc0LCA1MjUtNTU2Lg0KDQojIyMgQ2xhc3NpY2FsIE1ldGhvZHMNCi0gV2Ugd2lsbCBkbyB0aGUgbGlzdHdpc2UgYW5kIG1lYW4gcmVwbGFjZW1lbnQgdHlwZXMNCi0gU2ltdWxhdGUgYSBzaW1wbGUgcmVncmVzc2lvbiAtIHJlbW92ZSBjYXNlcyBjb21wbGV0ZWx5IGF0IHJhbmRvbSBhbmQgdGVzdA0KDQojIyMjIFR5cGljYWwgU2FtcGxlIFNpemUNCi0gU2ltdWxhdGUgYSBkYXRhIHNldA0KDQpgYGB7ciwgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlcz0gRkFMU0V9DQpsaWJyYXJ5KGNhcikNCnNldC5zZWVkKDQyKQ0KbiA8LSA1MA0KIyBJVnMNClggPC0gcnVuaWYobiwgLTEwLCAxMCkNClogPC0gcm5vcm0obiwgLTEwLCAxMCkNCiMgT3VyIGVxdWF0aW9uIHRvICBjcmVhdGUgWQ0KWSA8LSAuOCpYIC0gLjQqWiArIDIgKyBybm9ybShuLCBzZD0xMCkNCiNCdWlsdCBvdXIgZGF0YSBmcmFtZQ0KRGF0YVNldDE8LWRhdGEuZnJhbWUoRFY9WSxJVjE9WCxJVjI9WikNCmBgYA0KDQotIFJlbW92ZSBJVnMgYW5kIERWcyBhdCByYW5kb20gKGFtcHV0ZSkgYXQgYWJvdXQgNDAlIHRvdGFsIGFjcm9zcyB0aGUgSVZzIGFuZCBEVg0KDQpgYGB7ciwgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFLG1lc3NhZ2VzPSBGQUxTRX0NCmxpYnJhcnkobWljZSkNCnNldC5zZWVkKDY2NikNCkFtcHV0ZWQuRGF0YTwtYW1wdXRlKGRhdGEgPURhdGFTZXQxLCBwcm9wID0gMC40LCBtZWNoID0gIk1DQVIiKQ0KRGF0YVNldDEuTTwtQW1wdXRlZC5EYXRhJGFtcA0KYGBgDQogDQogLSBMZXRzIHZpc3VhbGl6ZSB0aGUgbWlzc2luZ25lc3MNCmBgYHtyLCBlY2hvPVRSVUUsIHdhcm5pbmc9RkFMU0V9DQpsaWJyYXJ5KFZJTSkNCmFnZ3IoRGF0YVNldDEuTSwgY29sPWMoJ25hdnlibHVlJywncmVkJyksIG51bWJlcnM9VFJVRSwgDQogICAgIHNvcnRWYXJzPVRSVUUsIGxhYmVscz1uYW1lcyhkYXRhKSwgY2V4LmF4aXM9LjcsIA0KICAgICBnYXA9MywgDQogICAgIHlsYWI9YygiSGlzdG9ncmFtIG9mIG1pc3NpbmcgZGF0YSIsIlBhdHRlcm4iKSkNCmBgYA0KIA0KICAtIExldHMgdmlzdWFsaXplIHBhaXJ3aXNlIHNjYXR0ZXJwbG90cyBiYXNlZCBvbiB3aGVyZSB0aGUgbWlzc2luZyBkYXRhIHNlZW1zIHRvIGJlDQpgYGB7cixlY2hvPVRSVUUsIG91dC53aWR0aD0nLjQ5XFxsaW5ld2lkdGgnLCBmaWcud2lkdGg9MywgZmlnLmhlaWdodD0zLGZpZy5zaG93PSdob2xkJyxmaWcuYWxpZ249J2NlbnRlcid9DQptYXJnaW5wbG90KERhdGFTZXQxLk1bYygxLDIpXSkNCm1hcmdpbnBsb3QoRGF0YVNldDEuTVtjKDIsMyldKQ0KbWFyZ2lucGxvdChEYXRhU2V0MS5NW2MoMSwzKV0pDQpgYGANCiANCiANCi0gUnVuIHRoZSByZWdyZXNzaW9uIG9mIHRoZSBvcmlnaW5hbCBhbmQgbWlzc2luZyBkYXRhIHNpZGUgYnkgc2lkZQ0KLSBSIGRlZmF1bHRzIHRvIGxpc3R3aXNlIGRlbGV0aW9uIChtb3N0IGNvbnNlcnZhdGl2ZSBhcHByb2FjaCkNCi0gUGFpcndpc2UtZGVsZXRpb24gcmVncmVzc2lvbiBpcyBub3QgYnVpbGQgaW50byBSIGJ5IGRlZmF1bHQgKGJ1dCB0aGVyZSBpcyBpcyBwYWlyd2lzZSBkZWxldGlvbiBmb3Igb3RoZXIgbWV0aG9kcykNCi0gT3JpZ2luYWwsIExpc3R3aXNlLCBtZWFuIGltcHV0ZWQgKHVzaW5nIHRoZSBtaWNlIHBhY2thZ2UpDQogICAgLSBOb3RlOiAqVGhlIGZ1bmN0aW9uIGBjb21wbGV0ZWAgaXMgaW4gbXVsdGlwbGUgcGFja2FnZXMgKHRpZHlyIGFuZCBtaWNlKSBhbmQgdGhleSBjb25mbGljdCwgc28gSSB3aWxsIHVzZSBgbWljZTo6Y29tcGxldGVgIHRvIHRlbGwgUiB3aGljaCBjb21wbGV0ZSBJIHdhbnQqKSANCiANCmBgYHtyfQ0KT3JnaW5hbDwtbG0oRFZ+SVYxK0lWMiwgZGF0YT0gRGF0YVNldDEpDQpMaXN0d2lzZTwtbG0oRFZ+SVYxK0lWMiwgZGF0YT0gRGF0YVNldDEuTSkNCg0KIyBNZWFuIGltcHV0ZSB1c2luZyBNSUNFIA0KRGF0YVNldDEuTU08LSBtaWNlOjpjb21wbGV0ZShtaWNlKERhdGFTZXQxLk0sIG1ldGg9J21lYW4nLHByaW50RmxhZyA9IEZBTFNFKSkNCk1lYW5JbXB1dGU8LWxtKERWfklWMStJVjIsIGRhdGE9IERhdGFTZXQxLk1NKQ0KYGBgDQoNCmBgYHtyLGVjaG89RkFMU0UsIHdhcm5pbmc9RkFMU0UsbWVzc2FnZT1GQUxTRSxyZXN1bHRzPSdhc2lzJ30NCmxpYnJhcnkoc3RhcmdhemVyKQ0Kc3RhcmdhemVyKE9yZ2luYWwsTGlzdHdpc2UsTWVhbkltcHV0ZSx0eXBlPSJodG1sIiwNCiAgICAgICAgICBjb2x1bW4ubGFiZWxzID0gYygiT3JnaW5hbCIsICJMaXN0d2lzZSIsIk1lYW4gSW1wdXRlIiksDQogICAgICAgICAgaW50ZXJjZXB0LmJvdHRvbSA9IEZBTFNFLCAgc2luZ2xlLnJvdz1UUlVFLCBub3Rlcy5hcHBlbmQgPSBGQUxTRSwNCiAgICAgICAgICBvbWl0LnN0YXQ9Yygic2VyIiksIHN0YXIuY3V0b2ZmcyA9IGMoMC4wNSwgMC4wMSwgMC4wMDEpLA0KICAgICAgICAgIGhlYWRlcj1GQUxTRSkNCmBgYA0KDQotIElmIHdlIHJ1biAxMDAwIHNpbXVsYXRpb25zIGZvciBlYWNoIG1ldGhvZHMsIHdlIGNhbiBnZXQgYW4gZXN0aW1hdGUgb2YgdGhlcmUgRXJyb3IgDQogICAgLSBCZXRhcyBmb3IgZWFjaCBwYXJhbWV0ZXIgaW4gdGhlIG1vZGVsIChFc3RpbWF0ZWQgYiAtIE9yZ2luYWwgYi9PcmdpbmFsIGIpDQogICAgICAgIC0gV2Ugd2lsbCB0YWtlIHRoZSBtZWRpYW4gYWNyb3NzIHNpbXVsYXRpb25zIA0KICAgIC0gRGVjaXNpb25zIGVycm9ycyB1c2luZyBzaWduaWZpY2FuY2UgW1R5cGUgSSBhbmQgVHlwZSBJSV0gDQogICAgICAgIC0gVHlwZSBJID0gT3JnaW5hbCBpcyBub3Qgc2lnLCBidXQgZXN0aW1hdGVkIGlzIHNpZyANCiAgICAgICAgLSBUeXBlIElJID0gT3JnaW5hbCBpcyBzaWcsIGJ1dCBlc3RpbWF0ZWQgaXMgbm90IHNpZyANCiAgICAgICAgLSBMb29rIHRvIHRoZSAib3JnaW5hbCIgc2ltdWxhdGlvbiB0YWJsZSB0byBzZWUgd2hpY2ggaXMgcG9zc2libGUgZm9yIHRoYXQgbW9kZWwgdGVybQ0KDQpgYGB7ciwgZWNobyA9IEZBTFNFLCBmaWcud2lkdGg9NyxmaWcuaGVpZ2h0PTN9DQpsaWJyYXJ5KHRpZHlyKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkoYnJvb20pDQpsaWJyYXJ5KGdncGxvdDIpDQoNCkJvb3ROb3JtYWw8LWZ1bmN0aW9uKFBlcmNlbnQpew0KICBBbXB1dGVkLkRhdGE8LWFtcHV0ZShkYXRhID1EYXRhU2V0MSwgcHJvcCA9IFBlcmNlbnQsIG1lY2ggPSAiTUNBUiIpDQogIERhdGFTZXQxLk08LUFtcHV0ZWQuRGF0YSRhbXANCiAgTGlzdHdpc2U8LWxtKERWfklWMStJVjIsIGRhdGE9IERhdGFTZXQxLk0pDQogICMgTWVhbiBpbXB1dGUgdXNpbmcgTUlDRSANCiAgRGF0YVNldDEuTU08LW1pY2U6OmNvbXBsZXRlKG1pY2UoRGF0YVNldDEuTSwgbWV0aD0nbWVhbicscHJpbnRGbGFnID0gRkFMU0UpKQ0KICBNZWFuSW1wdXRlPC1sbShEVn5JVjErSVYyLCBkYXRhPSBEYXRhU2V0MS5NTSkNCiAgTFc8LXRpZHkoTGlzdHdpc2UpOyBNUDwtdGlkeShNZWFuSW1wdXRlKSAgICAgICAgICAgICAgICAgIA0KICBSMTwtYXMudmVjdG9yKGFzLm1hdHJpeChMV1syXSkpOyBSMjwtYXMudmVjdG9yKGFzLm1hdHJpeChMV1s1XSkpDQogIFIzPC1hcy52ZWN0b3IoYXMubWF0cml4KE1QWzJdKSk7IFI0PC1hcy52ZWN0b3IoYXMubWF0cml4KE1QWzVdKSkNCiAgcmV0dXJuKGMoUjEsUjIsUjMsUjQpKQ0KfQ0KDQpTaW1zPTEwMDANClNpbTA8LXQocmVwbGljYXRlKFNpbXMsIEJvb3ROb3JtYWwoLjQpKSkNClNpbTE8LWFzLmRhdGEuZnJhbWUoU2ltMCkNCmNvbG5hbWVzKFNpbTEpPC1jKCJMV19CX0ludGVyY2VwdCIsIkxXX0JfSVYxIiwiTFdfQl9JVjIiLA0KICAgICAgICAgICAgICAgICAgIkxXX3BfSW50ZXJjZXB0IiwiTFdfcF9JVjEiLCJMV19wX0lWMiIsDQogICAgICAgICAgICAgICAgICAiTUlfQl9JbnRlcmNlcHQiLCJNSV9CX0lWMSIsIk1JX0JfSVYyIiwNCiAgICAgICAgICAgICAgICAgICJNSV9wX0ludGVyY2VwdCIsIk1JX3BfSVYxIiwiTUlfcF9JVjIiKQ0KU2ltMSRJRDwtc2VxKDE6U2ltcykNCg0KT3JnaW5hbEJldGFzPC10aWR5KE9yZ2luYWwpDQpPcmdpbmFsQmV0YXMkdGVybTwtIGMoIkludGVyY2VwdCIsICJJVjEiLCJJVjIiKQ0KDQpTaW0xLlBsb3Q8LQ0KICBTaW0xJT4lDQogIGdhdGhlcihWYXIsIFZhbHVlLCBMV19CX0ludGVyY2VwdDpNSV9wX0lWMikgJT4lDQogIHNlcGFyYXRlKFZhcixjKCJUeXBlIiwgIk1ldHJpYyIsInRlcm0iKSxzZXA9Il8iKSAlPiUNCiAgc3ByZWFkKE1ldHJpYyxWYWx1ZSkgJT4lDQogIGxlZnRfam9pbihPcmdpbmFsQmV0YXMpICU+JSANCiAgbXV0YXRlKEJFcnJvcj1hYnMoKGVzdGltYXRlLUIpL0IpKjEwMCkgJT4lIA0KICBtdXRhdGUoU2lnU2ltPShwPC4wNSkvMSxTaWc9KHAudmFsdWU8LjA1KS8xKSAlPiUNCiAgbXV0YXRlKFR5cGVJID0gaWZfZWxzZShTaWc9PTEgJiBTaWdTaW09PTEgfA0KICAgICAgICAgICAgICAgICAgICAgICAgIFNpZz09MCAmIFNpZ1NpbT09MCwwLGlmX2Vsc2UoU2lnPT0wICYgU2lnU2ltPT0xLDEwMCwwKSksDQogICAgICAgICBUeXBlSUkgPSBpZl9lbHNlKFNpZz09MSAmIFNpZ1NpbT09MSB8DQogICAgICAgICAgICAgICAgICAgICAgICAgU2lnPT0wICYgU2lnU2ltPT0wLDAsaWZfZWxzZShTaWc9PTEgJiBTaWdTaW09PTAsMTAwLDApKSkNCg0KU2ltMS5TdW1tYXJ5PC1TaW0xLlBsb3QgJT4lDQogIGdyb3VwX2J5KHRlcm0sIFR5cGUpJT4lIA0KICBzdW1tYXJpc2UoQmV0YS5FcnJvcnM9bWVkaWFuKEJFcnJvciksIFR5cGVJLlJhdGU9bWVhbihUeXBlSSksVHlwZUlJLlJhdGU9bWVhbihUeXBlSUkpKSAlPiUgDQogIGdhdGhlcihQYXJhbWV0ZXIsIEVycm9yLCBCZXRhLkVycm9yczpUeXBlSUkuUmF0ZSkNCg0KZ2dwbG90KFNpbTEuU3VtbWFyeSwgYWVzKHg9dGVybSwgeT1FcnJvcixmaWxsID0gVHlwZSwgY29sb3I9VHlwZSkpKw0KICBjb29yZF9jYXJ0ZXNpYW4oeT1jKDAsMTAwKSkrDQogIGdlb21fY29sKHBvc2l0aW9uID0gImRvZGdlIikrDQogIHlsYWIoIkVycm9yIikreGxhYigiTW9kZWwgVGVybSIpKw0KICBmYWNldF9ncmlkKH5QYXJhbWV0ZXIpKw0KICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0PTApKSt0aGVtZV9idygpDQpgYGANCg0KIyMjIyBIdWdlIERhdGEgU2V0DQotIE4gPSA1MDANCg0KYGBge3IsIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRX0NCnNldC5zZWVkKDQyKQ0KbiA8LSA1MDANCiMgSVZzDQpYIDwtIHJ1bmlmKG4sIC0xMCwgMTApDQpaIDwtIHJub3JtKG4sIC0xMCwgMTApDQojIE91ciBlcXVhdGlvbiB0byAgY3JlYXRlIFkNClkgPC0gLjgqWCAtIC40KlogKyAyICsgcm5vcm0obiwgc2Q9MTApDQojQnVpbHQgb3VyIGRhdGEgZnJhbWUNCkRhdGFTZXQyPC1kYXRhLmZyYW1lKERWPVksSVYxPVgsSVYyPVopDQojcmVtb3ZlIGNhc2VzIGF0IHJhbmRvbSANCnNldC5zZWVkKDY2NikNCkFtcHV0ZWQuRGF0YS4yPC1hbXB1dGUoZGF0YSA9RGF0YVNldDIsIHByb3AgPSAwLjQsIG1lY2ggPSAiTUNBUiIpDQpEYXRhU2V0Mi5NPC1BbXB1dGVkLkRhdGEuMiRhbXANCmBgYA0KDQpgYGB7cn0NCk9yZ2luYWwuMjwtbG0oRFZ+SVYxK0lWMiwgZGF0YT0gRGF0YVNldDIpDQpMaXN0d2lzZS4yPC1sbShEVn5JVjErSVYyLCBkYXRhPSBEYXRhU2V0Mi5NKQ0KIyBNZWFuIGltcHV0ZSB1c2luZyBNSUNFIA0KRGF0YVNldDIuTU08LSBtaWNlOjpjb21wbGV0ZShtaWNlKERhdGFTZXQyLk0sIG1ldGg9J21lYW4nLHByaW50RmxhZyA9IEZBTFNFKSkNCk1lYW5JbXB1dGUuMjwtbG0oRFZ+SVYxK0lWMiwgZGF0YT0gRGF0YVNldDIuTU0pDQpgYGANCg0KYGBge3IsZWNobz1GQUxTRSwgd2FybmluZz1GQUxTRSxtZXNzYWdlPUZBTFNFLHJlc3VsdHM9J2FzaXMnfQ0Kc3RhcmdhemVyKE9yZ2luYWwuMixMaXN0d2lzZS4yLE1lYW5JbXB1dGUuMix0eXBlPSJodG1sIiwNCiAgICAgICAgICBjb2x1bW4ubGFiZWxzID0gYygiT3JnaW5hbCIsICJMaXN0d2lzZSIsIk1lYW4gSW1wdXRlIiksDQogICAgICAgICAgaW50ZXJjZXB0LmJvdHRvbSA9IEZBTFNFLCAgc2luZ2xlLnJvdz1UUlVFLCBub3Rlcy5hcHBlbmQgPSBGQUxTRSwNCiAgICAgICAgICBvbWl0LnN0YXQ9Yygic2VyIiksIHN0YXIuY3V0b2ZmcyA9IGMoMC4wNSwgMC4wMSwgMC4wMDEpLA0KICAgICAgICAgIGhlYWRlcj1GQUxTRSkNCmBgYA0KDQotIFdlIHJlcGVhdCB0aGUgRXJyb3Igc2ltdWxhdGlvbnMgZm9yIGVhY2ggbWV0aG9kcyB1c2luZyA1MDAgc3ViamVjdHMNCg0KYGBge3IsIGVjaG8gPSBGQUxTRSwgZmlnLndpZHRoPTcsZmlnLmhlaWdodD0zfQ0KQm9vdE5vcm1hbDI8LWZ1bmN0aW9uKFBlcmNlbnQpew0KICBBbXB1dGVkLkRhdGEuMjwtYW1wdXRlKGRhdGEgPURhdGFTZXQyLCBwcm9wID0gUGVyY2VudCwgbWVjaCA9ICJNQ0FSIikNCiAgRGF0YVNldDIuTTwtQW1wdXRlZC5EYXRhLjIkYW1wDQogIExpc3R3aXNlLjI8LWxtKERWfklWMStJVjIsIGRhdGE9IERhdGFTZXQyLk0pDQogICMgTWVhbiBpbXB1dGUgdXNpbmcgTUlDRSANCiAgRGF0YVNldDIuTU08LSBtaWNlOjpjb21wbGV0ZShtaWNlKERhdGFTZXQyLk0sIG1ldGg9J21lYW4nLHByaW50RmxhZyA9IEZBTFNFKSkNCiAgTWVhbkltcHV0ZS4yPC1sbShEVn5JVjErSVYyLCBkYXRhPSBEYXRhU2V0Mi5NTSkNCiAgTFc8LXRpZHkoTGlzdHdpc2UuMik7IE1QPC10aWR5KE1lYW5JbXB1dGUuMikgICAgICAgICAgICAgICAgICANCiAgUjE8LWFzLnZlY3Rvcihhcy5tYXRyaXgoTFdbMl0pKTsgUjI8LWFzLnZlY3Rvcihhcy5tYXRyaXgoTFdbNV0pKQ0KICBSMzwtYXMudmVjdG9yKGFzLm1hdHJpeChNUFsyXSkpOyBSNDwtYXMudmVjdG9yKGFzLm1hdHJpeChNUFs1XSkpDQogIHJldHVybihjKFIxLFIyLFIzLFI0KSkNCn0NCg0KU2ltcz0xMDAwDQpTaW0wLjI8LXQocmVwbGljYXRlKFNpbXMsIEJvb3ROb3JtYWwyKC40KSkpDQpTaW0yPC1hcy5kYXRhLmZyYW1lKFNpbTAuMikNCmNvbG5hbWVzKFNpbTIpPC1jKCJMV19CX0ludGVyY2VwdCIsIkxXX0JfSVYxIiwiTFdfQl9JVjIiLA0KICAgICAgICAgICAgICAgICAgIkxXX3BfSW50ZXJjZXB0IiwiTFdfcF9JVjEiLCJMV19wX0lWMiIsDQogICAgICAgICAgICAgICAgICAiTUlfQl9JbnRlcmNlcHQiLCJNSV9CX0lWMSIsIk1JX0JfSVYyIiwNCiAgICAgICAgICAgICAgICAgICJNSV9wX0ludGVyY2VwdCIsIk1JX3BfSVYxIiwiTUlfcF9JVjIiKQ0KU2ltMiRJRDwtc2VxKDE6U2ltcykNCg0KDQpPcmdpbmFsQmV0YXMyPC10aWR5KE9yZ2luYWwuMikNCk9yZ2luYWxCZXRhczIkdGVybTwtIGMoIkludGVyY2VwdCIsICJJVjEiLCJJVjIiKQ0KDQpTaW0yLlBsb3Q8LQ0KICBTaW0yJT4lDQogIGdhdGhlcihWYXIsIFZhbHVlLCBMV19CX0ludGVyY2VwdDpNSV9wX0lWMikgJT4lDQogIHNlcGFyYXRlKFZhcixjKCJUeXBlIiwgIk1ldHJpYyIsInRlcm0iKSxzZXA9Il8iKSAlPiUNCiAgc3ByZWFkKE1ldHJpYyxWYWx1ZSkgJT4lDQogIGxlZnRfam9pbihPcmdpbmFsQmV0YXMyKSAlPiUgDQogIG11dGF0ZShCRXJyb3I9YWJzKChlc3RpbWF0ZS1CKS9CKSoxMDApICU+JSANCiAgbXV0YXRlKFNpZ1NpbT0ocDwuMDUpLzEsU2lnPShwLnZhbHVlPC4wNSkvMSkgJT4lDQogIG11dGF0ZShUeXBlSSA9IGlmX2Vsc2UoU2lnPT0xICYgU2lnU2ltPT0xIHwNCiAgICAgICAgICAgICAgICAgICAgICAgICBTaWc9PTAgJiBTaWdTaW09PTAsMCxpZl9lbHNlKFNpZz09MCAmIFNpZ1NpbT09MSwxMDAsMCkpLA0KICAgICAgICAgVHlwZUlJID0gaWZfZWxzZShTaWc9PTEgJiBTaWdTaW09PTEgfA0KICAgICAgICAgICAgICAgICAgICAgICAgIFNpZz09MCAmIFNpZ1NpbT09MCwwLGlmX2Vsc2UoU2lnPT0xICYgU2lnU2ltPT0wLDEwMCwwKSkpDQogIA0KDQpTaW0yLlN1bW1hcnk8LVNpbTIuUGxvdCAlPiUNCiAgZ3JvdXBfYnkodGVybSwgVHlwZSklPiUgDQogIHN1bW1hcmlzZShCZXRhLkVycm9ycz1tZWRpYW4oQkVycm9yKSwgVHlwZUkuUmF0ZT1tZWFuKFR5cGVJKSxUeXBlSUkuUmF0ZT1tZWFuKFR5cGVJSSkpICU+JSANCiAgZ2F0aGVyKFBhcmFtZXRlciwgRXJyb3IsIEJldGEuRXJyb3JzOlR5cGVJSS5SYXRlKQ0KDQpnZ3Bsb3QoU2ltMi5TdW1tYXJ5LCBhZXMoeD10ZXJtLCB5PUVycm9yLGZpbGwgPSBUeXBlLCBjb2xvcj1UeXBlKSkrDQogIGNvb3JkX2NhcnRlc2lhbih5PWMoMCwxMDApKSsNCiAgZ2VvbV9jb2wocG9zaXRpb24gPSAiZG9kZ2UiKSsNCiAgeWxhYigiRXJyb3IiKSt4bGFiKCJNb2RlbCBUZXJtIikrDQogIGZhY2V0X2dyaWQoflBhcmFtZXRlcikrDQogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQ9MCkpK3RoZW1lX2J3KCkNCmBgYA0KDQoNCiMjIyMgVGlueSBTYW1wbGUgU2l6ZQ0KLSBOID0gMTUNCg0KYGBge3IsIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRX0NCnNldC5zZWVkKDQyKQ0KbiA8LSAxNQ0KIyBJVnMNClggPC0gcnVuaWYobiwgLTEwLCAxMCkNClogPC0gcm5vcm0obiwgLTEwLCAxMCkNCiMgT3VyIGVxdWF0aW9uIHRvICBjcmVhdGUgWQ0KWSA8LSAuOCpYIC0gLjQqWiArIDIgKyBybm9ybShuLCBzZD0xMCkNCiNCdWlsdCBvdXIgZGF0YSBmcmFtZQ0KRGF0YVNldDM8LWRhdGEuZnJhbWUoRFY9WSxJVjE9WCxJVjI9WikNCg0KI3JlbW92ZSBjYXNlcyBhdCByYW5kb20gDQpzZXQuc2VlZCg2NjYpDQpBbXB1dGVkLkRhdGEuMzwtYW1wdXRlKGRhdGEgPURhdGFTZXQzLCBwcm9wID0gMC40LCBtZWNoID0gIk1DQVIiKQ0KRGF0YVNldDMuTTwtQW1wdXRlZC5EYXRhLjMkYW1wDQpgYGANCg0KDQpgYGB7cn0NCk9yZ2luYWwuMzwtbG0oRFZ+SVYxK0lWMiwgZGF0YT0gRGF0YVNldDMpDQpMaXN0d2lzZS4zPC1sbShEVn5JVjErSVYyLCBkYXRhPSBEYXRhU2V0My5NKQ0KDQojIE1lYW4gaW1wdXRlIHVzaW5nIE1JQ0UgDQpEYXRhU2V0My5NTTwtIG1pY2U6OmNvbXBsZXRlKG1pY2UoRGF0YVNldDMuTSwgbWV0aD0nbWVhbicscHJpbnRGbGFnID0gRkFMU0UpKQ0KTWVhbkltcHV0ZS4zPC1sbShEVn5JVjErSVYyLCBkYXRhPSBEYXRhU2V0My5NTSkNCmBgYA0KDQpgYGB7cixlY2hvPUZBTFNFLCB3YXJuaW5nPUZBTFNFLG1lc3NhZ2U9RkFMU0UscmVzdWx0cz0nYXNpcyd9DQpzdGFyZ2F6ZXIoT3JnaW5hbC4zLExpc3R3aXNlLjMsTWVhbkltcHV0ZS4zLHR5cGU9Imh0bWwiLA0KICAgICAgICAgIGNvbHVtbi5sYWJlbHMgPSBjKCJPcmdpbmFsIiwgIkxpc3R3aXNlIiwiTWVhbiBJbXB1dGUiKSwNCiAgICAgICAgICBpbnRlcmNlcHQuYm90dG9tID0gRkFMU0UsICBzaW5nbGUucm93PVRSVUUsIG5vdGVzLmFwcGVuZCA9IEZBTFNFLA0KICAgICAgICAgIG9taXQuc3RhdD1jKCJzZXIiKSwgc3Rhci5jdXRvZmZzID0gYygwLjA1LCAwLjAxLCAwLjAwMSksDQogICAgICAgICAgaGVhZGVyPUZBTFNFKQ0KYGBgDQoNCi0gV2UgcmVwZWF0IHRoZSBFcnJvciBzaW11bGF0aW9ucyBmb3IgZWFjaCBtZXRob2RzIHVzaW5nIDE1IHN1YmplY3RzDQoNCmBgYHtyLCBlY2hvID0gRkFMU0UsIGZpZy53aWR0aD03LGZpZy5oZWlnaHQ9M30NCkJvb3ROb3JtYWwzPC1mdW5jdGlvbihQZXJjZW50KXsNCiAgQW1wdXRlZC5EYXRhLjM8LWFtcHV0ZShkYXRhID1EYXRhU2V0MywgcHJvcCA9IFBlcmNlbnQsIG1lY2ggPSAiTUNBUiIpDQogIERhdGFTZXQzLk08LUFtcHV0ZWQuRGF0YS4zJGFtcA0KICBMaXN0d2lzZS4zPC1sbShEVn5JVjErSVYyLCBkYXRhPSBEYXRhU2V0My5NKQ0KICAjIE1lYW4gaW1wdXRlIHVzaW5nIE1JQ0UgDQogIERhdGFTZXQzLk1NPC0gbWljZTo6Y29tcGxldGUobWljZShEYXRhU2V0My5NLCBtZXRoPSdtZWFuJyxwcmludEZsYWcgPSBGQUxTRSkpDQogIE1lYW5JbXB1dGUuMzwtbG0oRFZ+SVYxK0lWMiwgZGF0YT0gRGF0YVNldDMuTU0pDQogIExXPC10aWR5KExpc3R3aXNlLjMpOyBNUDwtdGlkeShNZWFuSW1wdXRlLjMpICAgICAgICAgICAgICAgICAgDQogIFIxPC1hcy52ZWN0b3IoYXMubWF0cml4KExXWzJdKSk7IFIyPC1hcy52ZWN0b3IoYXMubWF0cml4KExXWzVdKSkNCiAgUjM8LWFzLnZlY3Rvcihhcy5tYXRyaXgoTVBbMl0pKTsgUjQ8LWFzLnZlY3Rvcihhcy5tYXRyaXgoTVBbNV0pKQ0KICByZXR1cm4oYyhSMSxSMixSMyxSNCkpDQp9DQoNClNpbXM9MTAwMA0KU2ltMC4zPC10KHJlcGxpY2F0ZShTaW1zLCBCb290Tm9ybWFsMyguNCkpKQ0KU2ltMzwtYXMuZGF0YS5mcmFtZShTaW0wLjMpDQpjb2xuYW1lcyhTaW0zKTwtYygiTFdfQl9JbnRlcmNlcHQiLCJMV19CX0lWMSIsIkxXX0JfSVYyIiwNCiAgICAgICAgICAgICAgICAgICJMV19wX0ludGVyY2VwdCIsIkxXX3BfSVYxIiwiTFdfcF9JVjIiLA0KICAgICAgICAgICAgICAgICAgIk1JX0JfSW50ZXJjZXB0IiwiTUlfQl9JVjEiLCJNSV9CX0lWMiIsDQogICAgICAgICAgICAgICAgICAiTUlfcF9JbnRlcmNlcHQiLCJNSV9wX0lWMSIsIk1JX3BfSVYyIikNClNpbTMkSUQ8LXNlcSgxOlNpbXMpDQoNCk9yZ2luYWxCZXRhczM8LXRpZHkoT3JnaW5hbC4zKQ0KT3JnaW5hbEJldGFzMyR0ZXJtPC0gYygiSW50ZXJjZXB0IiwgIklWMSIsIklWMiIpDQoNClNpbTMuUGxvdDwtDQogIFNpbTMlPiUNCiAgZ2F0aGVyKFZhciwgVmFsdWUsIExXX0JfSW50ZXJjZXB0Ok1JX3BfSVYyKSAlPiUNCiAgc2VwYXJhdGUoVmFyLGMoIlR5cGUiLCAiTWV0cmljIiwidGVybSIpLHNlcD0iXyIpICU+JQ0KICBzcHJlYWQoTWV0cmljLFZhbHVlKSAlPiUNCiAgbGVmdF9qb2luKE9yZ2luYWxCZXRhczMpICU+JSANCiAgbXV0YXRlKEJFcnJvcj1hYnMoKGVzdGltYXRlLUIpL0IpKjEwMCkgJT4lIA0KICBtdXRhdGUoU2lnU2ltPShwPC4wNSkvMSxTaWc9KHAudmFsdWU8LjA1KS8xKSAlPiUNCiAgbXV0YXRlKFR5cGVJID0gaWZfZWxzZShTaWc9PTEgJiBTaWdTaW09PTEgfA0KICAgICAgICAgICAgICAgICAgICAgICAgIFNpZz09MCAmIFNpZ1NpbT09MCwwLGlmX2Vsc2UoU2lnPT0wICYgU2lnU2ltPT0xLDEwMCwwKSksDQogICAgICAgICBUeXBlSUkgPSBpZl9lbHNlKFNpZz09MSAmIFNpZ1NpbT09MSB8DQogICAgICAgICAgICAgICAgICAgICAgICAgU2lnPT0wICYgU2lnU2ltPT0wLDAsaWZfZWxzZShTaWc9PTEgJiBTaWdTaW09PTAsMTAwLDApKSkNCiAgDQoNClNpbTMuU3VtbWFyeTwtU2ltMy5QbG90ICU+JQ0KICBncm91cF9ieSh0ZXJtLCBUeXBlKSU+JSANCiAgc3VtbWFyaXNlKEJldGEuRXJyb3JzPW1lZGlhbihCRXJyb3IpLCBUeXBlSS5SYXRlPW1lYW4oVHlwZUkpLFR5cGVJSS5SYXRlPW1lYW4oVHlwZUlJKSkgJT4lIA0KICBnYXRoZXIoUGFyYW1ldGVyLCBFcnJvciwgQmV0YS5FcnJvcnM6VHlwZUlJLlJhdGUpDQoNCmdncGxvdChTaW0zLlN1bW1hcnksIGFlcyh4PXRlcm0sIHk9RXJyb3IsZmlsbCA9IFR5cGUsIGNvbG9yPVR5cGUpKSsNCiAgY29vcmRfY2FydGVzaWFuKHk9YygwLDEwMCkpKw0KICBnZW9tX2NvbChwb3NpdGlvbiA9ICJkb2RnZSIpKw0KICB5bGFiKCJFcnJvciIpK3hsYWIoIk1vZGVsIFRlcm0iKSsNCiAgZmFjZXRfZ3JpZCh+UGFyYW1ldGVyKSsNCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdD0wKSkrdGhlbWVfYncoKQ0KYGBgDQoNCg0KDQojIyMjIFR5cGljYWwgU2FtcGxlcyB3aXRoIE1OQVINCi0gTiA9IDUwDQoNCmBgYHtyLCBlY2hvPVRSVUUsIHdhcm5pbmc9RkFMU0V9DQojcmVtb3ZlIGNhc2VzIGF0IHJhbmRvbSBmcm9tIG91ciBvcmdpbmFsIGRhdGEgc2V0DQpzZXQuc2VlZCg2NjYpDQpBbXB1dGVkLkRhdGEuNDwtYW1wdXRlKGRhdGEgPURhdGFTZXQxLCBwcm9wID0gMC40LCBtZWNoID0gIk1OQVIiKQ0KRGF0YVNldDQuTTwtQW1wdXRlZC5EYXRhLjQkYW1wDQpgYGANCg0KYGBge3J9DQpPcmdpbmFsLjQ8LWxtKERWfklWMStJVjIsIGRhdGE9IERhdGFTZXQxKQ0KTGlzdHdpc2UuNDwtbG0oRFZ+SVYxK0lWMiwgZGF0YT0gRGF0YVNldDQuTSkNCiMgTWVhbiBpbXB1dGUgdXNpbmcgTUlDRSANCkRhdGFTZXQ0Lk1NPC0gbWljZTo6Y29tcGxldGUobWljZShEYXRhU2V0NC5NLCBtZXRoPSdtZWFuJyxwcmludEZsYWcgPSBGQUxTRSkpDQpNZWFuSW1wdXRlLjQ8LWxtKERWfklWMStJVjIsIGRhdGE9IERhdGFTZXQ0Lk1NKQ0KYGBgDQoNCmBgYHtyLGVjaG89RkFMU0UsIHdhcm5pbmc9RkFMU0UsbWVzc2FnZT1GQUxTRSxyZXN1bHRzPSdhc2lzJ30NCnN0YXJnYXplcihPcmdpbmFsLjQsTGlzdHdpc2UuNCxNZWFuSW1wdXRlLjQsdHlwZT0iaHRtbCIsDQogICAgICAgICAgY29sdW1uLmxhYmVscyA9IGMoIk9yZ2luYWwiLCAiTGlzdHdpc2UiLCJNZWFuIEltcHV0ZSIpLA0KICAgICAgICAgIGludGVyY2VwdC5ib3R0b20gPSBGQUxTRSwgIHNpbmdsZS5yb3c9VFJVRSwgbm90ZXMuYXBwZW5kID0gRkFMU0UsDQogICAgICAgICAgb21pdC5zdGF0PWMoInNlciIpLCBzdGFyLmN1dG9mZnMgPSBjKDAuMDUsIDAuMDEsIDAuMDAxKSwNCiAgICAgICAgICBoZWFkZXI9RkFMU0UpDQpgYGANCg0KLSBXZSByZXBlYXQgdGhlIEVycm9yIHNpbXVsYXRpb25zIGZvciBlYWNoIG1ldGhvZHMgZm9yIE1OQVIgKDUwIHN1YmplY3RzKQ0KDQpgYGB7ciwgZWNobyA9IEZBTFNFLCBmaWcud2lkdGg9NyxmaWcuaGVpZ2h0PTN9DQpCb290Tm9ybWFsNDwtZnVuY3Rpb24oUGVyY2VudCl7DQogIEFtcHV0ZWQuRGF0YS40PC1hbXB1dGUoZGF0YSA9RGF0YVNldDEsIHByb3AgPSBQZXJjZW50LCBtZWNoID0gIk1OQVIiKQ0KICBEYXRhU2V0NC5NPC1BbXB1dGVkLkRhdGEuNCRhbXANCiAgTGlzdHdpc2UuNDwtbG0oRFZ+SVYxK0lWMiwgZGF0YT0gRGF0YVNldDQuTSkNCiAgIyBNZWFuIGltcHV0ZSB1c2luZyBNSUNFIA0KICBEYXRhU2V0NC5NTTwtIG1pY2U6OmNvbXBsZXRlKG1pY2UoRGF0YVNldDQuTSwgbWV0aD0nbWVhbicscHJpbnRGbGFnID0gRkFMU0UpKQ0KICBNZWFuSW1wdXRlLjQ8LWxtKERWfklWMStJVjIsIGRhdGE9IERhdGFTZXQ0Lk1NKQ0KICBMVzwtdGlkeShMaXN0d2lzZS40KTsgTVA8LXRpZHkoTWVhbkltcHV0ZS40KSAgICAgICAgICAgICAgICAgIA0KICBSMTwtYXMudmVjdG9yKGFzLm1hdHJpeChMV1syXSkpOyBSMjwtYXMudmVjdG9yKGFzLm1hdHJpeChMV1s1XSkpDQogIFIzPC1hcy52ZWN0b3IoYXMubWF0cml4KE1QWzJdKSk7IFI0PC1hcy52ZWN0b3IoYXMubWF0cml4KE1QWzVdKSkNCiAgcmV0dXJuKGMoUjEsUjIsUjMsUjQpKQ0KfQ0KDQpTaW1zPTEwMDANClNpbTAuNDwtdChyZXBsaWNhdGUoU2ltcywgQm9vdE5vcm1hbDQoLjQpKSkNClNpbTQ8LWFzLmRhdGEuZnJhbWUoU2ltMC40KQ0KY29sbmFtZXMoU2ltNCk8LWMoIkxXX0JfSW50ZXJjZXB0IiwiTFdfQl9JVjEiLCJMV19CX0lWMiIsDQogICAgICAgICAgICAgICAgICAiTFdfcF9JbnRlcmNlcHQiLCJMV19wX0lWMSIsIkxXX3BfSVYyIiwNCiAgICAgICAgICAgICAgICAgICJNSV9CX0ludGVyY2VwdCIsIk1JX0JfSVYxIiwiTUlfQl9JVjIiLA0KICAgICAgICAgICAgICAgICAgIk1JX3BfSW50ZXJjZXB0IiwiTUlfcF9JVjEiLCJNSV9wX0lWMiIpDQpTaW00JElEPC1zZXEoMTpTaW1zKQ0KDQpPcmdpbmFsQmV0YXM0PC10aWR5KE9yZ2luYWwuNCkNCk9yZ2luYWxCZXRhczQkdGVybTwtIGMoIkludGVyY2VwdCIsICJJVjEiLCJJVjIiKQ0KDQpTaW00LlBsb3Q8LQ0KICBTaW00JT4lDQogIGdhdGhlcihWYXIsIFZhbHVlLCBMV19CX0ludGVyY2VwdDpNSV9wX0lWMikgJT4lDQogIHNlcGFyYXRlKFZhcixjKCJUeXBlIiwgIk1ldHJpYyIsInRlcm0iKSxzZXA9Il8iKSAlPiUNCiAgc3ByZWFkKE1ldHJpYyxWYWx1ZSkgJT4lDQogIGxlZnRfam9pbihPcmdpbmFsQmV0YXM0KSAlPiUgDQogIG11dGF0ZShCRXJyb3I9YWJzKChlc3RpbWF0ZS1CKS9CKSoxMDApICU+JSANCiAgbXV0YXRlKFNpZ1NpbT0ocDwuMDUpLzEsU2lnPShwLnZhbHVlPC4wNSkvMSkgJT4lDQogIG11dGF0ZShUeXBlSSA9IGlmX2Vsc2UoU2lnPT0xICYgU2lnU2ltPT0xIHwNCiAgICAgICAgICAgICAgICAgICAgICAgICBTaWc9PTAgJiBTaWdTaW09PTAsMCxpZl9lbHNlKFNpZz09MCAmIFNpZ1NpbT09MSwxMDAsMCkpLA0KICAgICAgICAgVHlwZUlJID0gaWZfZWxzZShTaWc9PTEgJiBTaWdTaW09PTEgfA0KICAgICAgICAgICAgICAgICAgICAgICAgIFNpZz09MCAmIFNpZ1NpbT09MCwwLGlmX2Vsc2UoU2lnPT0xICYgU2lnU2ltPT0wLDEwMCwwKSkpIA0KDQpTaW00LlN1bW1hcnk8LVNpbTQuUGxvdCAlPiUNCiAgZ3JvdXBfYnkodGVybSwgVHlwZSklPiUgDQogIHN1bW1hcmlzZShCZXRhLkVycm9ycz1tZWRpYW4oQkVycm9yKSwgVHlwZUkuUmF0ZT1tZWFuKFR5cGVJKSxUeXBlSUkuUmF0ZT1tZWFuKFR5cGVJSSkpICU+JSANCiAgZ2F0aGVyKFBhcmFtZXRlciwgRXJyb3IsIEJldGEuRXJyb3JzOlR5cGVJSS5SYXRlKQ0KDQpnZ3Bsb3QoU2ltNC5TdW1tYXJ5LCBhZXMoeD10ZXJtLCB5PUVycm9yLGZpbGwgPSBUeXBlLCBjb2xvcj1UeXBlKSkrDQogIGNvb3JkX2NhcnRlc2lhbih5PWMoMCwxMDApKSsNCiAgZ2VvbV9jb2wocG9zaXRpb24gPSAiZG9kZ2UiKSsNCiAgeWxhYigiRXJyb3IiKSt4bGFiKCJNb2RlbCBUZXJtIikrDQogIGZhY2V0X2dyaWQoflBhcmFtZXRlcikrDQogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQ9MCkpK3RoZW1lX2J3KCkNCmBgYA0KDQojIyMjIFR5cGljYWwgU2FtcGxlcyB3aXRoIE1BUg0KLSBOID0gNTANCg0KYGBge3IsIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRX0NCiNyZW1vdmUgY2FzZXMgZnJvbSBvdXIgb3JnaW5hbCBkYXRhIHNldA0Kc2V0LnNlZWQoNDIpDQpBbXB1dGVkLkRhdGEuNTwtYW1wdXRlKGRhdGEgPURhdGFTZXQxLCBwcm9wID0gMC40LCBtZWNoID0gIk1BUiIpDQpEYXRhU2V0NS5NPC1BbXB1dGVkLkRhdGEuNSRhbXANCmBgYA0KDQoNCmBgYHtyfQ0KT3JnaW5hbC41PC1sbShEVn5JVjErSVYyLCBkYXRhPSBEYXRhU2V0MSkNCkxpc3R3aXNlLjU8LWxtKERWfklWMStJVjIsIGRhdGE9IERhdGFTZXQ1Lk0pDQojIE1lYW4gaW1wdXRlIHVzaW5nIE1JQ0UgDQpEYXRhU2V0NS5NTTwtIG1pY2U6OmNvbXBsZXRlKG1pY2UoRGF0YVNldDUuTSwgbWV0aD0nbWVhbicscHJpbnRGbGFnID0gRkFMU0UpKQ0KTWVhbkltcHV0ZS41PC1sbShEVn5JVjErSVYyLCBkYXRhPSBEYXRhU2V0NS5NTSkNCmBgYA0KDQoNCmBgYHtyLGVjaG89RkFMU0UsIHdhcm5pbmc9RkFMU0UsbWVzc2FnZT1GQUxTRSxyZXN1bHRzPSdhc2lzJ30NCnN0YXJnYXplcihPcmdpbmFsLjUsTGlzdHdpc2UuNSxNZWFuSW1wdXRlLjUsdHlwZT0iaHRtbCIsDQogICAgICAgICAgY29sdW1uLmxhYmVscyA9IGMoIk9yZ2luYWwiLCAiTGlzdHdpc2UiLCJNZWFuIEltcHV0ZSIpLA0KICAgICAgICAgIGludGVyY2VwdC5ib3R0b20gPSBGQUxTRSwgIHNpbmdsZS5yb3c9VFJVRSwgbm90ZXMuYXBwZW5kID0gRkFMU0UsDQogICAgICAgICAgb21pdC5zdGF0PWMoInNlciIpLCBzdGFyLmN1dG9mZnMgPSBjKDAuMDUsIDAuMDEsIDAuMDAxKSwNCiAgICAgICAgICBoZWFkZXI9RkFMU0UpDQpgYGANCg0KLSBXZSByZXBlYXQgdGhlIEVycm9yIHNpbXVsYXRpb25zIGZvciBlYWNoIG1ldGhvZHMgZm9yIE1BUiAoNTAgc3ViamVjdHMpDQoNCmBgYHtyLCBlY2hvID0gRkFMU0UsIGZpZy53aWR0aD03LGZpZy5oZWlnaHQ9M30NCkJvb3ROb3JtYWw1PC1mdW5jdGlvbihQZXJjZW50KXsNCiAgQW1wdXRlZC5EYXRhLjU8LWFtcHV0ZShkYXRhID1EYXRhU2V0MSwgcHJvcCA9IFBlcmNlbnQsIG1lY2ggPSAiTUFSIikNCiAgRGF0YVNldDUuTTwtQW1wdXRlZC5EYXRhLjUkYW1wDQogIExpc3R3aXNlLjU8LWxtKERWfklWMStJVjIsIGRhdGE9IERhdGFTZXQ1Lk0pDQogICMgTWVhbiBpbXB1dGUgdXNpbmcgTUlDRSANCiAgRGF0YVNldDUuTU08LSBtaWNlOjpjb21wbGV0ZShtaWNlKERhdGFTZXQ1Lk0sIG1ldGg9J21lYW4nLHByaW50RmxhZyA9IEZBTFNFKSkNCiAgTWVhbkltcHV0ZS41PC1sbShEVn5JVjErSVYyLCBkYXRhPSBEYXRhU2V0NS5NTSkNCiAgTFc8LXRpZHkoTGlzdHdpc2UuNSk7IE1QPC10aWR5KE1lYW5JbXB1dGUuNSkgICAgICAgICAgICAgICAgICANCiAgUjE8LWFzLnZlY3Rvcihhcy5tYXRyaXgoTFdbMl0pKTsgUjI8LWFzLnZlY3Rvcihhcy5tYXRyaXgoTFdbNV0pKQ0KICBSMzwtYXMudmVjdG9yKGFzLm1hdHJpeChNUFsyXSkpOyBSNDwtYXMudmVjdG9yKGFzLm1hdHJpeChNUFs1XSkpDQogIHJldHVybihjKFIxLFIyLFIzLFI0KSkNCn0NCg0KU2ltcz0xMDAwDQpTaW0wLjU8LXQocmVwbGljYXRlKFNpbXMsIEJvb3ROb3JtYWw1KC40KSkpDQpTaW01PC1hcy5kYXRhLmZyYW1lKFNpbTAuNSkNCmNvbG5hbWVzKFNpbTUpPC1jKCJMV19CX0ludGVyY2VwdCIsIkxXX0JfSVYxIiwiTFdfQl9JVjIiLA0KICAgICAgICAgICAgICAgICAgIkxXX3BfSW50ZXJjZXB0IiwiTFdfcF9JVjEiLCJMV19wX0lWMiIsDQogICAgICAgICAgICAgICAgICAiTUlfQl9JbnRlcmNlcHQiLCJNSV9CX0lWMSIsIk1JX0JfSVYyIiwNCiAgICAgICAgICAgICAgICAgICJNSV9wX0ludGVyY2VwdCIsIk1JX3BfSVYxIiwiTUlfcF9JVjIiKQ0KU2ltNSRJRDwtc2VxKDE6U2ltcykNCg0KT3JnaW5hbEJldGFzNTwtdGlkeShPcmdpbmFsLjUpDQpPcmdpbmFsQmV0YXM1JHRlcm08LSBjKCJJbnRlcmNlcHQiLCAiSVYxIiwiSVYyIikNCg0KU2ltNS5QbG90PC0NCiAgU2ltNSU+JQ0KICBnYXRoZXIoVmFyLCBWYWx1ZSwgTFdfQl9JbnRlcmNlcHQ6TUlfcF9JVjIpICU+JQ0KICBzZXBhcmF0ZShWYXIsYygiVHlwZSIsICJNZXRyaWMiLCJ0ZXJtIiksc2VwPSJfIikgJT4lDQogIHNwcmVhZChNZXRyaWMsVmFsdWUpICU+JQ0KICBsZWZ0X2pvaW4oT3JnaW5hbEJldGFzNSkgJT4lIA0KICBtdXRhdGUoQkVycm9yPWFicygoZXN0aW1hdGUtQikvQikqMTAwKSAlPiUgDQogIG11dGF0ZShTaWdTaW09KHA8LjA1KS8xLFNpZz0ocC52YWx1ZTwuMDUpLzEpICU+JQ0KICBtdXRhdGUoVHlwZUkgPSBpZl9lbHNlKFNpZz09MSAmIFNpZ1NpbT09MSB8DQogICAgICAgICAgICAgICAgICAgICAgICAgU2lnPT0wICYgU2lnU2ltPT0wLDAsaWZfZWxzZShTaWc9PTAgJiBTaWdTaW09PTEsMTAwLDApKSwNCiAgICAgICAgIFR5cGVJSSA9IGlmX2Vsc2UoU2lnPT0xICYgU2lnU2ltPT0xIHwNCiAgICAgICAgICAgICAgICAgICAgICAgICBTaWc9PTAgJiBTaWdTaW09PTAsMCxpZl9lbHNlKFNpZz09MSAmIFNpZ1NpbT09MCwxMDAsMCkpKQ0KDQpTaW01LlN1bW1hcnk8LVNpbTUuUGxvdCAlPiUNCiAgZ3JvdXBfYnkodGVybSwgVHlwZSklPiUgDQogIHN1bW1hcmlzZShCZXRhLkVycm9ycz1tZWRpYW4oQkVycm9yKSwgVHlwZUkuUmF0ZT1tZWFuKFR5cGVJKSxUeXBlSUkuUmF0ZT1tZWFuKFR5cGVJSSkpICU+JSANCiAgZ2F0aGVyKFBhcmFtZXRlciwgRXJyb3IsIEJldGEuRXJyb3JzOlR5cGVJSS5SYXRlKQ0KDQpnZ3Bsb3QoU2ltNS5TdW1tYXJ5LCBhZXMoeD10ZXJtLCB5PUVycm9yLGZpbGwgPSBUeXBlLCBjb2xvcj1UeXBlKSkrDQogIGNvb3JkX2NhcnRlc2lhbih5PWMoMCwxMDApKSsNCiAgZ2VvbV9jb2wocG9zaXRpb24gPSAiZG9kZ2UiKSsNCiAgeWxhYigiRXJyb3IiKSt4bGFiKCJNb2RlbCBUZXJtIikrDQogIGZhY2V0X2dyaWQoflBhcmFtZXRlcikrDQogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQ9MCkpK3RoZW1lX2J3KCkNCg0KYGBgDQoNCg0KDQojIyMgTm90ZXMNCi0gTWVhbiBpbXB1dGluZyB3b3JrcyBPSyBpbiBtZWRpdW0gdG8gbGFyZ2Ugc2FtcGxlIHNpemVzDQotIEJ1dCBpdCBjcmVhdGVzIGxhcmdlIGJpYXMgaW4gc21hbGwgc2FtcGxlcw0KLSBJZiB5b3UgZG9uJ3QgaGF2ZSBNQ0FSIHlvdSBoYXZlIHRvIHR1cm4gdG8gbW9yZSBjb21wbGV4IG1ldGhvZHMgDQoNCiMgTW9kZXJuIGFwcHJvYWNoZXMNCi0gVGhlcmUgYXJlIG1hbnkgbW9kZXJuIGFwcHJvYWNoZXMgb2Z0ZW4gYmFzZWQgb24gbWF4aW11bSBsaWtlbGlob29kIGVzdGltYXRpb24gKE1MKSBvciBNdWx0aXBsZSBJbXB1dGF0aW9uIChNSSkgb3IgZXZlbiBCYXllc2lhbiBhcHByb2FjaGVzDQotICJNTCBlc3RpbWF0aW9uIGlzIHRvIGlkZW50aWZ5IHRoZSBwb3B1bGF0aW9uIHBhcmFtZXRlciB2YWx1ZXMgbW9zdCBsaWtlbHkgdG8gaGF2ZSBwcm9kdWNlZCBhIHBhcnRpY3VsYXIgc2FtcGxlIG9mIGRhdGEuIFRoaXMgdXN1YWxseSByZXF1aXJlcyBhbiBpdGVyYXRpdmUgcHJvY2VzcyB3aGVyZWJ5IHRoZSBtb2RlbCBmaXR0aW5nIHByb2dyYW0gInRyaWVzIG91dCIgZGlmZmVyZW50IHZhbHVlcyBmb3IgdGhlIHBhcmFtZXRlcnMgb2YgaW50ZXJlc3QgKGUuZy4sIHJlZ3Jlc3Npb24gY29lZmZpY2llbnRzKSBlbiByb3V0ZSB0byBpZGVudGlmeWluZyB0aGUgdmFsdWVzIG1vc3QgbGlrZWx5IHRvIGhhdmUgcHJvZHVjZWQgdGhlIHNhbXBsZSBkYXRhLiIgKFBldWdoICYgRW5kZXJzLCAyMDA0KQ0KLSBNdWx0aXBsZSBJbXB1dGF0aW9uOiAiUmF0aGVyIHRoYW4gdHJlYXRpbmcgYSBzaW5nbGUgc2V0IG9mIGltcHV0ZWQgdmFsdWVzIGFzICJ0cnVlIiBlc3RpbWF0ZXMgb2YgdGhlIG1pc3NpbmcgdmFsdWVzLCBNSSBjcmVhdGVzIGEgbnVtYmVyIG9mIGltcHV0ZWQgZGF0YSBzZXRzIChmcmVxdWVudGx5IGJldHdlZW4gNSBhbmQgMTApLCBlYWNoIG9mIHdoaWNoIGNvbnRhaW5zIGEgZGlmZmVyZW50IHBsYXVzaWJsZSBlc3RpbWF0ZSBvZiB0aGUgbWlzc2luZyB2YWx1ZXMuIiAoUGV1Z2ggJiBFbmRlcnMsIDIwMDQpDQotIExldHMgdGVzdCBvdXQgTUkgYXMgaXRzIHRoZSBuZXdlc3QgYW5kIGNvbnNpZGVyZWQgYW4gaW1wcm92ZW1lbnQgb3ZlciBNTA0KDQojIyBNdWx0aXBsZSBJbXB1dGF0aW9uIGJ5IENoYWluZWQgRXF1YXRpb25zDQotICBTZWUgQXp1ciBldCBhbCwgMjAxMSBmb3IgZGV0YWlscw0KLSBNSUNFIHBhY2thZ2UgaW4gUjogTG90cyBvZiBvbGQgYW5kIG1vZGVybiBhbGdvcml0aG1zIHRvIGNob29zZSBmcm9tIChhbHNvIGZvciBkZXNpZ24gYmV5b25kIGxpbmVhciByZWdyZXNzaW9uKSANCi0gUHJlZGljdGl2ZSBtZWFuIG1hdGNoaW5nIGlzIGEgZ29vZCB2ZXJzaW9uIHRvIHVzZSBhcyBpdCAiaXMgYSBnZW5lcmFsIHB1cnBvc2Ugc2VtaS1wYXJhbWV0cmljIGltcHV0YXRpb24gbWV0aG9kLiAuLi4gaW1wdXRhdGlvbnMgYXJlIHJlc3RyaWN0ZWQgdG8gdGhlIG9ic2VydmVkIHZhbHVlcyBhbmQgdGhhdCBpdCBjYW4gcHJlc2VydmUgbm9uLWxpbmVhciByZWxhdGlvbnMuLi4iIChCdXVyZW4gJiBHcm9vdGh1aXMtT3Vkc2hvb3JuLCAyMDExKQ0KLSBXZSB3aWxsIHdvcmsgZnJvbSBvdXIgTUFSIG1pc3NpbmcgRGF0YXNldDUuTSANCi0gV2UgY2FuIGNvbXBhcmUgdGhpcyB0byBvdXIgY2xhc3NpY2FsIG1ldGhvZHMgYW5kIHNvbWUgb3RoZXIgbWV0aG9kcyB3aGljaCB0aGUgcGFja2FnZSBhbGxvd3MgKEJvb3RzdGFwcGluZyAmIEJheWVzaWFuKQ0KDQojIyMgVHlwaWNhbCBzYW1wbGUgc2l6ZQ0KDQpgYGB7ciwgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFLG1lc3NhZ2U9RkFMU0UscmVzdWx0cz0nYXNpcyd9DQojTGluZWFyIHJlZ3Jlc3Npb24gdXNpbmcgYm9vdHN0cmFwDQpEYXRhRGF0YVNldDUuQm9vdDwtIG1pY2U6OmNvbXBsZXRlKG1pY2UoRGF0YVNldDUuTSwgbSA9IDEwLG1ldGg9J25vcm0uYm9vdCcsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJpbnRGbGFnID0gRkFMU0UsIHNlZWQgPSA2NjYpKQ0KQm9vdDwtbG0oRFZ+SVYxK0lWMiwgZGF0YT0gRGF0YURhdGFTZXQ1LkJvb3QpDQoNCiNCYXllc2lhbiBsaW5lYXIgcmVncmVzc2lvbg0KRGF0YURhdGFTZXQ1LkJMUjwtIG1pY2U6OmNvbXBsZXRlKG1pY2UoRGF0YVNldDUuTSwgbSA9IDEwLG1ldGg9J25vcm0nLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJpbnRGbGFnID0gRkFMU0UsIHNlZWQgPSA2NjYpKQ0KQkxSPC1sbShEVn5JVjErSVYyLCBkYXRhPSBEYXRhRGF0YVNldDUuQkxSKQ0KDQojUHJlZGljdGl2ZSBtZWFuIG1hdGNoaW5nIA0KRGF0YURhdGFTZXQ1LnBtbTwtIG1pY2U6OmNvbXBsZXRlKG1pY2UoRGF0YVNldDUuTSwgbSA9IDEwLG1ldGg9J3BtbScsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcmludEZsYWcgPSBGQUxTRSwgc2VlZCA9IDY2NikpDQpQTU08LWxtKERWfklWMStJVjIsIGRhdGE9IERhdGFEYXRhU2V0NS5wbW0pDQpgYGANCg0KYGBge3IsZWNobz1GQUxTRSwgd2FybmluZz1GQUxTRSxtZXNzYWdlPUZBTFNFLHJlc3VsdHM9J2FzaXMnfQ0Kc3RhcmdhemVyKE9yZ2luYWwuNSwgQm9vdCwgQkxSLCBQTU0sdHlwZT0iaHRtbCIsDQogICAgICAgICAgY29sdW1uLmxhYmVscyA9IGMoIk9yZ2luYWwiLCJCb290IiwiQkxSIiwiUE1NIiksDQogICAgICAgICAgaW50ZXJjZXB0LmJvdHRvbSA9IEZBTFNFLCAgc2luZ2xlLnJvdz1UUlVFLCBub3Rlcy5hcHBlbmQgPSBGQUxTRSwNCiAgICAgICAgICBvbWl0LnN0YXQ9Yygic2VyIiksIHN0YXIuY3V0b2ZmcyA9IGMoMC4wNSwgMC4wMSwgMC4wMDEpLA0KICAgICAgICAgIGhlYWRlcj1GQUxTRSkNCmBgYA0KDQotIFByZWRpY3RpdmUgbWVhbiBtYXRjaGluZyBpcyB0aGUgZGVmYXVsdCBvZiB0aGlzIHByb2dyYW0gYW5kIHlvdSBzZWUgaXQgZG9lcyBhIGdvb2Qgam9iDQoNCg0KDQojIyMgU21hbGwgc2FtcGxlIHNpemUNCi0gVXNlIHRoZSBzYW1lIGV4YWN0IHNtYWxsIGRhdGEgKE4gPSAxNSkNCg0KYGBge3IsIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRSxtZXNzYWdlPUZBTFNFLHJlc3VsdHM9J2FzaXMnfQ0KI0xpbmVhciByZWdyZXNzaW9uIHVzaW5nIGJvb3RzdHJhcA0KRGF0YURhdGFTZXQzLkJvb3Q8LSBtaWNlOjpjb21wbGV0ZShtaWNlKERhdGFTZXQzLk0sIG0gPSAxNSxtZXRoPSdub3JtLmJvb3QnLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByaW50RmxhZyA9IEZBTFNFLCBzZWVkID0gNjY2KSkNCkJvb3QuMzwtbG0oRFZ+SVYxK0lWMiwgZGF0YT0gRGF0YURhdGFTZXQzLkJvb3QpDQoNCiNCYXllc2lhbiBsaW5lYXIgcmVncmVzc2lvbg0KRGF0YURhdGFTZXQzLkJMUjwtIG1pY2U6OmNvbXBsZXRlKG1pY2UoRGF0YVNldDMuTSwgbSA9IDE1LG1ldGg9J25vcm0nLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJpbnRGbGFnID0gRkFMU0UsIHNlZWQgPSA2NjYpKQ0KQkxSLjM8LWxtKERWfklWMStJVjIsIGRhdGE9IERhdGFEYXRhU2V0My5CTFIpDQoNCiNQcmVkaWN0aXZlIG1lYW4gbWF0Y2hpbmcgDQpEYXRhRGF0YVNldDMucG1tPC0gbWljZTo6Y29tcGxldGUobWljZShEYXRhU2V0My5NLCBtID0gMTUsbWV0aD0ncG1tJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByaW50RmxhZyA9IEZBTFNFLCBzZWVkID0gNjY2KSkNClBNTS4zPC1sbShEVn5JVjErSVYyLCBkYXRhPSBEYXRhRGF0YVNldDMucG1tKQ0KYGBgDQoNCmBgYHtyLGVjaG89RkFMU0UsIHdhcm5pbmc9RkFMU0UsbWVzc2FnZT1GQUxTRSxyZXN1bHRzPSdhc2lzJ30NCnN0YXJnYXplcihPcmdpbmFsLjMsIEJvb3QuMywgQkxSLjMsIFBNTS4zLHR5cGU9Imh0bWwiLA0KICAgICAgICAgIGNvbHVtbi5sYWJlbHMgPSBjKCJPcmdpbmFsIiwiQm9vdCIsIkJMUiIsIlBNTSIpLA0KICAgICAgICAgIGludGVyY2VwdC5ib3R0b20gPSBGQUxTRSwgIHNpbmdsZS5yb3c9VFJVRSwgbm90ZXMuYXBwZW5kID0gRkFMU0UsDQogICAgICAgICAgb21pdC5zdGF0PWMoInNlciIpLCBzdGFyLmN1dG9mZnMgPSBjKDAuMDUsIDAuMDEsIDAuMDAxKSwNCiAgICAgICAgICBoZWFkZXI9RkFMU0UpDQpgYGANCg0KLSBXZSBuZWVkIHRvIGZpbmQgcHJhY3RpY2VyIG9mIHRoZSBkYXJrIGFydHMsIGFzIG5vbmUgb2YgdGhlIG1ldGhvZHMgZGlkIGEgZ29vZCBqb2INCg0KIyMjIE5vdGVzDQotIFlvdSBoYXZlIHRvIG1hbnVhbGx5IHNldCB0aGUgbnVtYmVyIG9mIGl0ZXJhdGlvbnMgKG51bWJlciBvZiBwYXNzZXMpDQotIFlvdSB3aWxsIGhhdmUgdG8gcmVzZWFyY2ggZWFjaCBhbGdvcml0aG0gZm9yIHlvdXIgcGFydGljdWxhciBkYXRhIG1pc3NpbmduZXNzDQoNCiMgQmVzdCBQcmFjdGljZXMNCi0gRWFjaCBzaXR1YXRpb24gaXMgZGlmZmVyZW50IGFuZCB3aGljaCBtZXRob2QgeW91IHVzZSBkZXBlbmRzIG9uIG1hbnkgZmFjdG9ycw0KLSBBbHNvIHlvdSBtdXN0IHRoaW5rIGNhcmVmdWxseSBhYm91dCB0aGUgYXNzdW1wdGlvbnMgeW91IGFyZSBtYWtpbmcNCi0gRm9yIGV4YW1wbGUsIGlmIHlvdSBtZWFuIChvciBQTU0pIHJlcGxhY2UgeW91IGFyZSBhc3N1bWluZyB0aGUgZGF0YSBhcmUgbm9ybWFsDQotIFRoZXJlIGFyZSBvdGhlciBhZHZhbmNlZCBtZXRob2RzIGZvciBzcGVjaWZpYyBzaXR1YXRpb246ICJob3QgZGVja2luZyIgZm9yIHN1cnZleSByZXNlYXJjaDsgIG5lYXJlc3QgbmVpZ2hib3IsIHNwbGluZXMsIG9yIGF1dG9yZWdyZXNzaXZlIG1ldGhvZHMgZm9yIHRpbWUtc2VyaWVzIG9yIGxvbmdpdHVkaW5hbCBkYXRhDQotIEJlc3QgcHJhY3RpY2U6IGNvbXBhcmUgc2VydmFsIGxvZ2ljYWwgcmVwbGFjZW1lbnQgbWV0aG9kcyBhbmQgYWxzbyBjb21wYXJlIGl0IHRvIGxpc3R3aXNlIHJlbW92YWwuIElmIHRoZSBzdG9yeSB5b3UgYXJlIHRyeWluZyB0byB0ZWxsIGlzIHZlcnkgZGlmZmVyZW50IGJhc2VkIG9uIGVhY2ggbWV0aG9kIGFuZCB2ZXJ5IGRpZmZlcmVudCBhcyBpdCByZWxhdGVzIHRvIHRoZSBsaXN0d2lzZSByZW1vdmFsIGl0IHN1Z2dlc3RzIHlvdSBoYXZlIG5vdCBmYWN0b3JlZCBpbiBzb21lIGFzc3VtcHRpb24gY29ycmVjdGx5IGFuZCBiZXN0IHRvIHJldGhpbmsgd2hhdCB5b3UgYXJlIGRvaW5nLiANCg0KPHNjcmlwdD4NCiAgKGZ1bmN0aW9uKGkscyxvLGcscixhLG0pe2lbJ0dvb2dsZUFuYWx5dGljc09iamVjdCddPXI7aVtyXT1pW3JdfHxmdW5jdGlvbigpew0KICAoaVtyXS5xPWlbcl0ucXx8W10pLnB1c2goYXJndW1lbnRzKX0saVtyXS5sPTEqbmV3IERhdGUoKTthPXMuY3JlYXRlRWxlbWVudChvKSwNCiAgbT1zLmdldEVsZW1lbnRzQnlUYWdOYW1lKG8pWzBdO2EuYXN5bmM9MTthLnNyYz1nO20ucGFyZW50Tm9kZS5pbnNlcnRCZWZvcmUoYSxtKQ0KICB9KSh3aW5kb3csZG9jdW1lbnQsJ3NjcmlwdCcsJ2h0dHBzOi8vd3d3Lmdvb2dsZS1hbmFseXRpY3MuY29tL2FuYWx5dGljcy5qcycsJ2dhJyk7DQoNCiAgZ2EoJ2NyZWF0ZScsICdVQS05MDQxNTE2MC0xJywgJ2F1dG8nKTsNCiAgZ2EoJ3NlbmQnLCAncGFnZXZpZXcnKTsNCg0KPC9zY3JpcHQ+DQo=