Aurora Tsai Carnegie Mellon University
December 22, 2017 aurorat-at-andrew.cmu.edu



This tutorial introduces several techniques for conducting multiple imputation analysis (MIA).

Parts are adapted from the following sites:




Contents:


1. Introduction 2. Visualizing Missing Data
3. MIA with MICE (Multivariate Imputation by Chained Equations)
4. MIA with the missForest package
5. MIA with the Hmisc package
6. References & other resources


Introduction

 

We might have to work with incomplete data sets for any number of reasons. Participants may have skipped questions in a survey or assessment. Perhaps we designed our computer-mediated assessment to randomly select 50 questions from a pool of 60 questions to give to each test taker. For whatever the reason, we often have to work with incomplete data sets.

Multiple Imputation Analysis (MIA) (Little and Rubin, 2002) is a method used to fill in missing observations. It takes into account the uncertainty related to the unknown real values by imputing M plausible values for each unobserved response in the data. This renders M different versions of the data set, where the non-missing data is identical, but the missing data entries differ. Discarding all partially observed data units (e.g., through listwise deletion) is generally not recommended because it can lead to substantial bias and poor predictions (Ambler, Omar, & Royston, 2007).

It’s important to know how much missing data we have and how it is spread across our data. Data is considered Missing Completely at Random (MCAR) if “the propensity to observe a missing value in an item is unrelated to a) the value of the item itself and to other items; b) to the latent trait values; and c) to any other measured variables in the analysis” (Sulis & Porcu, 2017, p. 331). In other words, data is MCAR if there are no variables influencing what observations are missing (e.g., participants’ don’t answer a question because of the nature of the question, because it’s too difficult, or because participants in class B weren’t given the question. Data is Missing at Random (MAR) is their distribution depends only on observed data.



Preparing Data

For this tutorial, make sure you install and load the following packages:

library(missForest)
library(Hmisc)
library(mice)
library(VIM)
library(rms)


Sample Data iris

Let’s work with the iris sample data set in R.

data <- iris
head(iris)
ABCDEFGHIJ0123456789
 
 
Sepal.Length
<dbl>
Sepal.Width
<dbl>
Petal.Length
<dbl>
Petal.Width
<dbl>
Species
<fctr>
15.13.51.40.2setosa
24.93.01.40.2setosa
34.73.21.30.2setosa
44.63.11.50.2setosa
55.03.61.40.2setosa
65.43.91.70.4setosa


Now let’s randomly add missing values using the prodNA function from missForest.

#Produce NAs in 10% of the data
iris.mis <- prodNA(iris, noNA = 0.1)
head(iris.mis)
ABCDEFGHIJ0123456789
 
 
Sepal.Length
<dbl>
Sepal.Width
<dbl>
Petal.Length
<dbl>
Petal.Width
<dbl>
Species
<fctr>
15.13.51.40.2setosa
24.93.01.40.2setosa
34.73.21.3NAsetosa
44.63.11.50.2setosa
55.0NA1.40.2setosa
65.43.91.70.4setosa


Visualizing “Missing Data”

We can create a table of missing values with this function from the mice package:

md.pattern(iris.mis)
   Sepal.Length Species Sepal.Width Petal.Length Petal.Width   
90            1       1           1            1           1  0
 5            0       1           1            1           1  1
11            1       1           0            1           1  1
 7            1       1           1            0           1  1
16            1       1           1            1           0  1
 6            1       0           1            1           1  1
 1            0       1           0            1           1  2
 2            0       1           1            0           1  2
 3            1       1           0            0           1  2
 1            0       1           1            1           0  2
 2            1       1           1            0           0  2
 2            0       0           1            1           1  2
 1            1       0           0            1           1  2
 2            1       0           1            0           1  2
 1            1       0           1            1           0  2
             11      12          16           16          20 75


Alternatively, we can also find the number of NAs for each variable using sapply:

sapply(iris.mis, function(x) sum(is.na(x)))
Sepal.Length  Sepal.Width Petal.Length  Petal.Width      Species 
          11           16           16           20           12 


Use the aggr function from the VIM package to visualize missing data.

miss_plot <- aggr(iris.mis, col=c('navyblue','yellow'),
                    numbers=TRUE, sortVars=TRUE,
                    labels=names(iris.mis), cex.axis=.7,
                    gap=3, ylab=c("Missing data","Pattern"))
not enough horizontal space to display frequencies

 Variables sorted by number of missings: 
     Variable      Count
  Petal.Width 0.13333333
  Sepal.Width 0.10666667
 Petal.Length 0.10666667
      Species 0.08000000
 Sepal.Length 0.07333333


Using marginplot to visualize missing data for the Sepal.Width and Sepal.Length variables:

marginplot(iris.mis[c(1,2)])



MICE

Multivariate Imputation by Chained Equations (MICE)

In order to impute missing values with MICE, we use the mice package Depending on how big your data set is, this can take a while (30 sec to a few hours), so be prepared to wait.

imputed_Data <- mice(iris.mis, m=5, maxit = 50, method = 'pmm', seed = 500)

m: the number of imputations made per missing observation (5 is normal–generates 5 data sets with imputed/original values)
maxit: the number of iterations?
method: We use ’probable means ?? seed: Values to randomly generate from??

We can get a summary of the data here:

summary(imputed_Data)
Multiply imputed data set
Call:
mice(data = iris.mis, m = 5, method = "pmm", maxit = 50, seed = 500)
Number of multiple imputations:  5
Missing cells per column:
Sepal.Length  Sepal.Width Petal.Length  Petal.Width      Species 
          11           16           16           20           12 
Imputation methods:
Sepal.Length  Sepal.Width Petal.Length  Petal.Width      Species 
       "pmm"        "pmm"        "pmm"        "pmm"        "pmm" 
VisitSequence:
Sepal.Length  Sepal.Width Petal.Length  Petal.Width      Species 
           1            2            3            4            5 
PredictorMatrix:
             Sepal.Length Sepal.Width Petal.Length Petal.Width
Sepal.Length            0           1            1           1
Sepal.Width             1           0            1           1
Petal.Length            1           1            0           1
Petal.Width             1           1            1           0
Species                 1           1            1           1
             Species
Sepal.Length       1
Sepal.Width        1
Petal.Length       1
Petal.Width        1
Species            0
Random generator seed value:  500 


To check all 5 sets of imputed values for a given variable (such as Sepal.Width), run the following:

imputed_Data$imp$Sepal.Width
ABCDEFGHIJ0123456789
 
 
1
<dbl>
2
<dbl>
3
<dbl>
4
<dbl>
5
<dbl>
53.43.63.13.83.5
113.53.74.13.73.7
283.43.73.04.13.8
433.23.13.13.13.1
522.92.93.43.43.0
562.92.73.02.52.6
573.02.92.82.72.7
663.03.43.02.83.1
863.42.82.73.22.9
1003.02.62.63.02.9


Ways to visualize missing & observed data:

Plot sepal width against all other categories

xyplot(imputed_Data,Sepal.Width ~ Sepal.Length + Petal.Width,pch=18,cex=1)

densityplot(imputed_Data)

stripplot(imputed_Data, pch = 20, cex = 1.2)


Add the data back to original data using one of the iternations

Let’s use the third iteration:

completeData <- complete(imputed_Data, 3)
head(completeData)
ABCDEFGHIJ0123456789
 
 
Sepal.Length
<dbl>
Sepal.Width
<dbl>
Petal.Length
<dbl>
Petal.Width
<dbl>
Species
<fctr>
15.13.51.40.2setosa
24.93.01.40.2setosa
34.73.21.30.2setosa
44.63.11.50.2setosa
55.03.11.40.2setosa
65.43.91.70.4setosa


Build a predictive model

We can use all 5 imputed data sets to build predictive model. This only works for data where you expect some type of linear relationship.

fit <- with(data = imputed_Data, exp = lm(Sepal.Width ~ Sepal.Length + Petal.Width)) 


Then pool results of the predictive model to see how good a fit the imputed data sets are.

combine <- pool(fit)
summary(combine)
                    est         se         t       df
(Intercept)   1.8090186 0.32544437  5.558611 129.1517
Sepal.Length  0.3098584 0.06673043  4.643434 130.4314
Petal.Width  -0.4639563 0.07382371 -6.284652 109.1799
                 Pr(>|t|)      lo 95      hi 95 nmis        fmi
(Intercept)  1.493627e-07  1.1651261  2.4529111   NA 0.06000905
Sepal.Length 8.250837e-06  0.1778443  0.4418724   11 0.05735028
Petal.Width  6.902011e-09 -0.6102697 -0.3176428   20 0.09771899
                 lambda
(Intercept)  0.04556452
Sepal.Length 0.04300592
Petal.Width  0.08134066



missForest

Impute missing values, using all parameters as default values

iris.imp <- missForest(iris.mis)
  missForest iteration 1 in progress...done!
  missForest iteration 2 in progress...done!
  missForest iteration 3 in progress...done!
  missForest iteration 4 in progress...done!
  missForest iteration 5 in progress...done!
  missForest iteration 6 in progress...done!


Check imputed values by data set

iris.imp$ximp
ABCDEFGHIJ0123456789
Sepal.Length
<dbl>
Sepal.Width
<dbl>
Petal.Length
<dbl>
Petal.Width
<dbl>
Species
<fctr>
5.1000003.5000001.4000000.2000000setosa
4.9000003.0000001.4000000.2000000setosa
4.7000003.2000001.3000000.2085833setosa
4.6000003.1000001.5000000.2000000setosa
5.0000003.2730201.4000000.2000000setosa
5.4000003.9000001.7000000.4000000setosa
4.6000003.4000001.4000000.1978136setosa
5.0000003.4000001.4658900.2000000setosa
4.4000002.9000001.4000000.2000000setosa
4.9000003.1000001.5000000.1000000setosa


Check for imputation error:

iris.imp$OOBerror
     NRMSE        PFC 
0.13637876 0.04347826 


NRMSE is normalized mean squared error. It is used to represent error derived from imputing continuous values. PFC (proportion of falsely classified) is used to represent error derived from imputing categorical values.

Compare actual data with imputed data set to see error:

iris.err <- mixError(iris.imp$ximp, iris.mis, iris)
iris.err
   NRMSE      PFC 
0.162452 0.000000 



Hmisc

Hmisc has several functions and methods available to impute values.

Impute values based on the mean of observations:

iris.mis$imputed_age <- with(iris.mis, impute(Sepal.Length, mean))
head(iris.mis$imputed_age)
[1] 5.1 4.9 4.7 4.6 5.0 5.4


Impute values using randomly generated numbers:

iris.mis$imputed_age2 <- with(iris.mis, impute(Sepal.Length, 'random'))
head(iris.mis$imputed_age2)
[1] 5.1 4.9 4.7 4.6 5.0 5.4

(You can also use min, max, median to impute missing value)


argImpute

Using the argImpute function, Hmisc performs multiple imputation using bootstraping and predictive mean matching. Different bootstrap resamples are used for each of the multiple imputations. Then a flexible additive model is used to predict missing values from nonmissing values. It acheives this by checking the fit of bootstrapped samples based to a predictive model (default) based on the original data.

Predictive mean matching works well for continuous and categorical (binary & multi-level) without the need for computing residuals and maximum likelihood fit.

  • Assumes linearity in the variables being predicted.
  • Fisher’s optimum scoring method is used for predicting categorical variables.
impute_arg <- aregImpute(~ Sepal.Length + Sepal.Width + Petal.Length + Petal.Width + Species, data = iris.mis, n.impute = 5)
Iteration 1 
Iteration 2 
Iteration 3 
Iteration 4 
Iteration 5 
Iteration 6 
Iteration 7 
Iteration 8 

formula: The ~ Sepal.Length + … argument indicates what formula to use. In this case, we want all variables’ missing data to be imputed, so we add each one.
data: the data frame with missing data.
n.impute: number of multiple imputations. 5 is frequently used, but 10 or more doesn’t hurt

Note: argImpute() automatically identifies the variable type and treats them accordingly.

See a summary of imputed values:

impute_arg

Multiple Imputation using Bootstrap and PMM

aregImpute(formula = ~Sepal.Length + Sepal.Width + Petal.Length + 
    Petal.Width + Species, data = iris.mis, n.impute = 5)

n: 150  p: 5    Imputations: 5      nk: 3 

Number of NAs:
Sepal.Length  Sepal.Width Petal.Length  Petal.Width      Species 
          11           16           16           20           12 

             type d.f.
Sepal.Length    s    2
Sepal.Width     s    2
Petal.Length    s    2
Petal.Width     s    2
Species         c    2

Transformation of Target Variables Forced to be Linear

R-squares for Predicting Non-Missing Values for Each Variable
Using Last Imputations of Predictors
Sepal.Length  Sepal.Width Petal.Length  Petal.Width      Species 
       0.862        0.796        0.982        0.956        0.986 

The R square values indicate the liklihood that the predicted missing values in the ‘irt’ dataframe match what we would actually observe if participants had answered all questions. Higher values are better.

Check imputed values for variable Sepal.Length:

impute_arg$imputed$Sepal.Length
    [,1] [,2] [,3] [,4] [,5]
19   5.8  5.6  5.4  5.4  5.6
24   5.0  5.4  4.6  5.0  5.1
27   4.8  5.3  4.8  5.0  4.8
33   5.6  5.5  5.8  5.6  5.6
69   5.1  5.5  5.6  6.0  5.5
86   6.5  6.3  5.9  5.7  5.8
96   5.4  5.6  6.3  6.4  5.6
106  5.6  6.4  5.8  5.6  6.9
112  6.1  5.9  6.4  6.0  5.8
120  5.7  6.0  5.9  5.5  6.3
135  6.9  6.7  6.8  6.3  6.4



To use one of the iterations in our original data set, we can use the transcend function:

completeData2 <- impute.transcan(impute_arg, imputation=1, data=iris.mis, list.out=TRUE,pr=FALSE, check=FALSE) 
head(completeData2)
$Sepal.Length
  [1] 5.1  4.9  4.7  4.6  5.0  5.4  4.6  5.0  4.4  4.9  5.4  4.8 
 [13] 4.8  4.3  5.8  5.7  5.4  5.1  5.8* 5.1  5.4  5.1  4.6  5.0*
 [25] 4.8  5.0  4.8* 5.2  5.2  4.7  4.8  5.4  5.6* 5.5  4.9  5.0 
 [37] 5.5  4.9  4.4  5.1  5.0  4.5  4.4  5.0  5.1  4.8  5.1  4.6 
 [49] 5.3  5.0  7.0  6.4  6.9  5.5  6.5  5.7  6.3  4.9  6.6  5.2 
 [61] 5.0  5.9  6.0  6.1  5.6  6.7  5.6  5.8  5.1* 5.6  5.9  6.1 
 [73] 6.3  6.1  6.4  6.6  6.8  6.7  6.0  5.7  5.5  5.5  5.8  6.0 
 [85] 5.4  6.5* 6.7  6.3  5.6  5.5  5.5  6.1  5.8  5.0  5.6  5.4*
 [97] 5.7  6.2  5.1  5.7  6.3  5.8  7.1  6.3  6.5  5.6* 4.9  7.3 
[109] 6.7  7.2  6.5  6.1* 6.8  5.7  5.8  6.4  6.5  7.7  7.7  5.7*
[121] 6.9  5.6  7.7  6.3  6.7  7.2  6.2  6.1  6.4  7.2  7.4  7.9 
[133] 6.4  6.3  6.9* 7.7  6.3  6.4  6.0  6.9  6.7  6.9  5.8  6.8 
[145] 6.7  6.7  6.3  6.5  6.2  5.9 

$Sepal.Width
  [1] 3.5  3.0  3.2  3.1  3.6* 3.9  3.4  3.4  2.9  3.1  3.5* 3.4 
 [13] 3.0  3.0  4.0  4.4  3.9  3.5  3.8  3.8  3.4  3.7  3.6  3.3 
 [25] 3.4  3.0  3.4  3.2* 3.4  3.2  3.1  3.4  4.1  4.2  3.1  3.2 
 [37] 3.5  3.6  3.0  3.4  3.5  2.3  2.9* 3.5  3.8  3.0  3.8  3.2 
 [49] 3.7  3.3  3.2  2.9* 3.1  2.3  2.8  3.0* 2.8* 2.4  2.9  2.7 
 [61] 2.0  3.0  2.2  2.9  2.9  2.5* 3.0  2.7  2.2  2.5  3.2  2.8 
 [73] 2.5  2.8  2.9  3.0  2.8  3.0  2.9  2.6  2.4  2.4  2.7  2.7 
 [85] 3.0  3.3* 3.1  2.3  3.0  2.5  2.6  3.0  2.6  2.3  2.7  3.0 
 [97] 2.9  2.9  2.5  2.6* 3.3  2.7  3.0  2.9  3.2* 3.0  2.5  2.9 
[109] 2.5  3.6  3.2* 2.7  3.0  2.7* 2.8  3.2  3.0  3.8  3.2* 2.2 
[121] 3.2  2.8  2.8  2.7  3.3  3.2  2.8  3.0  2.8  3.0  2.8  3.8 
[133] 2.8  2.8  2.6  3.2* 3.4  3.1  3.0  3.1  3.1  3.1  2.7  3.2 
[145] 3.3  3.6* 2.5  3.0  3.4  3.0 

$Petal.Length
  [1] 1.4  1.4  1.3  1.5  1.4  1.7  1.4  1.6* 1.4  1.5  1.5  1.6 
 [13] 1.4  1.1  1.2  1.5  1.3  1.4  1.7  1.5  1.7  1.5  1.0  1.7 
 [25] 1.6* 1.6  1.6  1.5* 1.4  1.6  1.6  1.5  1.4* 1.4  1.5  1.3*
 [37] 1.3  1.4  1.3  1.5  1.3  1.3  1.3  1.6  1.9  1.4  1.6  1.4 
 [49] 1.5  1.4  4.7  4.5  4.9  4.0  4.9* 4.5  4.7  3.3  4.6  3.9 
 [61] 3.7* 4.2  4.0  4.7  3.6  4.4  4.5  4.1  4.5  3.9  4.8  4.5*
 [73] 4.9  4.7  4.3  4.4  4.8  5.0  4.5  4.1* 3.8  3.7  3.9  5.1 
 [85] 4.5  4.5  4.7  4.4  4.1  4.0  4.4  4.6  4.0  3.3  4.2  4.2 
 [97] 4.2  4.3  3.0  4.1  6.0  5.1  5.9  5.6  5.8  5.0* 4.5  6.3 
[109] 5.8  6.1  5.8* 5.3  5.5  5.0  5.1  5.3  5.5  6.7  6.9  5.0 
[121] 5.1* 4.9  6.7  4.9  5.7  6.0  4.8  4.9  5.6  5.8  6.1  6.1*
[133] 5.3* 5.1  5.6  6.9* 5.6  5.5  4.8  5.4  5.6  5.1  5.1  5.9 
[145] 5.7  5.2  5.5* 5.2  5.4  5.1 

$Petal.Width
  [1] 0.2  0.2  0.2* 0.2  0.2  0.4  0.4* 0.2  0.2  0.1  0.2  0.2 
 [13] 0.1  0.1  0.2* 0.4  0.4  0.3  0.3  0.3  0.2  0.4  0.2  0.5 
 [25] 0.4* 0.3* 0.4  0.2  0.2  0.2  0.2* 0.4  0.1  0.4* 0.2  0.2 
 [37] 0.2  0.1  0.2  0.2  0.4* 0.3  0.2  0.6  0.4  0.3  0.5* 0.2 
 [49] 0.5* 0.2  1.4  1.5  1.5  1.3  1.5  1.3  1.6  1.0  1.3  1.4 
 [61] 1.0  1.5* 1.0  1.4  1.3  1.4  1.5  1.0  1.5  1.1  1.8  1.5*
 [73] 1.5  1.3* 1.3* 1.4  1.4  1.7  1.5  1.0  1.1  1.0  1.2  1.6 
 [85] 1.5  1.6  1.5  1.3  1.3  1.3  1.2  1.4  1.2  1.0  1.3  1.5*
 [97] 1.3  1.3* 1.0* 1.3  2.5  1.8* 2.1  1.8  2.2  2.1  1.7  1.8 
[109] 1.8  2.5  2.0  1.9  2.1  2.0  2.4  2.3  1.8  2.2  2.3  1.5 
[121] 2.3  2.0  2.0  1.8  2.1  1.8  1.8  2.0* 2.4* 1.6  1.9  2.0 
[133] 2.2  1.5  1.4  2.3  2.4  1.8  1.8  2.1  2.4  2.3  1.9  2.3 
[145] 2.5  2.3  1.9  2.0  2.3  1.8 

$Species
  [1] setosa      setosa      setosa      setosa      setosa     
  [6] setosa      setosa      setosa      setosa      setosa     
 [11] setosa      setosa      setosa      setosa      setosa     
 [16] setosa      setosa      setosa      setosa      setosa     
 [21] setosa      setosa      setosa      setosa      setosa     
 [26] setosa*     setosa      setosa      setosa      setosa     
 [31] setosa      setosa      setosa      setosa      setosa     
 [36] setosa      setosa      setosa      setosa      setosa     
 [41] setosa      setosa      setosa      setosa      setosa     
 [46] setosa      setosa      setosa      setosa      setosa     
 [51] versicolor  versicolor  versicolor* versicolor  virginica* 
 [56] versicolor  versicolor  versicolor  versicolor  versicolor 
 [61] versicolor  versicolor  versicolor  versicolor  versicolor 
 [66] versicolor  versicolor  versicolor  virginica*  versicolor 
 [71] versicolor  versicolor  versicolor  versicolor  versicolor 
 [76] versicolor* versicolor  versicolor  versicolor  versicolor*
 [81] versicolor  versicolor  versicolor  versicolor  versicolor 
 [86] versicolor  versicolor  versicolor  versicolor  versicolor 
 [91] versicolor  versicolor  versicolor  versicolor  versicolor 
 [96] versicolor  versicolor  versicolor  versicolor  versicolor 
[101] virginica   virginica   virginica   virginica   virginica  
[106] virginica   virginica   virginica*  virginica   virginica  
[111] virginica   virginica*  virginica   virginica   virginica  
[116] virginica   virginica   virginica   virginica*  virginica  
[121] virginica   virginica   virginica   virginica   virginica  
[126] virginica*  virginica   virginica   virginica   virginica  
[131] virginica   virginica   virginica   virginica   virginica  
[136] virginica   virginica   virginica   virginica*  virginica  
[141] virginica   virginica   virginica*  virginica   virginica  
[146] virginica   virginica   virginica   virginica   virginica  

It conveniently puts an asterisk next to each imputed value.

Now we can create a fit model using ols (linear model) from the rms package (Regression Modeling Strategies)

fmi <- fit.mult.impute(Sepal.Length ~ Sepal.Width + Petal.Length + Petal.Width + Species, ols, impute_arg, data=iris.mis)

Variance Inflation Factors Due to Imputation:

         Intercept        Sepal.Width       Petal.Length 
              1.24               1.17               1.81 
       Petal.Width Species=versicolor  Species=virginica 
              1.54               1.63               1.95 

Rate of Missing Information:

         Intercept        Sepal.Width       Petal.Length 
              0.19               0.15               0.45 
       Petal.Width Species=versicolor  Species=virginica 
              0.35               0.39               0.49 

d.f. for t-distribution for Tests of Single Coefficients:

         Intercept        Sepal.Width       Petal.Length 
            109.87             183.76              20.07 
       Petal.Width Species=versicolor  Species=virginica 
             32.92              26.52              16.88 

The following fit components were averaged over the 5 model fits:

  fitted.values stats linear.predictors 

 

 

Conclusion

This tutorial synthesizes some of the research articles and online tutorials available on Multiple Imputation Analysis. However, you can learn more from some of the references below.  

 

 


 

Troubleshooting

MICE: if you still have NAs in your dataset, it’s possible that you need to increase your maxit number https://stackoverflow.com/questions/20947908/imputation-mice-in-r-still-na-left-in-dataset

 

Missing Data in IRT data sets

Boone, W. J. (2016). Rasch Analysis for Instrument Development: Why, When, and How? CBE Life Sciences Education, 15(4), rm4. http://doi.org/10.1187/cbe.16-04-0148

Finch, H. (2008). Estimation of item response theory parameters in the presence of missing data. Journal of Educational Measurement, 45(3), 225–245.

 

References

Ambler G., Omar R. Z., Royston P. (2007). A comparison of imputation techniques for handling missing predictor values in a risk model with a binary outcome. Statistical Methods in Medical Research, 16, 277–298.

Little, R. J. A., & Rubin, D. B. (2002). Statistical analysis with missing data. Hoboken, NJ: John Wiley and Sons, Inc.

Sulis, I., & Porcu, M. (2017). Handling Missing Data in Item Response Theory. Assessing the Accuracy of a Multiple Imputation Procedure Based on Latent Class Analysis. Journal of Classification, 34(2), 327–359. https://doi.org/10.1007/s00357-017-9220-3

LS0tCnRpdGxlOiAiTWV0aG9kcyBmb3IgTXVsdGlwbGUgSW1wdXRpbmcgQW5hbHlzaXMgd2l0aCBSIgpvdXRwdXQ6IAogIGh0bWxfbm90ZWJvb2sKICAgIAotLS0KPHRhYmxlIHdpZHRoPTkwJT4KPHRyPgo8dGQ+PGk+QXVyb3JhIFRzYWk8L2k+PC90ZD4KPHRkIGFsaWduPSJyaWdodCI+IDxpPkNhcm5lZ2llIE1lbGxvbiBVbml2ZXJzaXR5PC9pPiA8L3RkPgo8L3RyPgo8dHI+Cjx0ZD48aT5EZWNlbWJlciAyMiwgMjAxNzwvaT48L3RkPgo8dGQgYWxpZ249InJpZ2h0Ij48aT5hdXJvcmF0LWF0LWFuZHJldy5jbXUuZWR1PC9pPjwvdGQ+CjwvdHI+CjwvdGFibGU+CjxiciAvPgoKCgpcCgoKIyMjI1RoaXMgdHV0b3JpYWwgaW50cm9kdWNlcyBzZXZlcmFsIHRlY2huaXF1ZXMgZm9yIGNvbmR1Y3RpbmcgbXVsdGlwbGUgaW1wdXRhdGlvbiBhbmFseXNpcyAoTUlBKS4gCgpQYXJ0cyBhcmUgYWRhcHRlZCBmcm9tIHRoZSBmb2xsb3dpbmcgc2l0ZXM6ICAKCiogVmlkeWFzYWdhciBCaGFyZ2F2YSdzIE1pc3NpbmcgVmFsdWUgVHJlYXRtZW50IGd1aWRlcwogICAgKyBodHRwOi8vcnN0dWRpby1wdWJzLXN0YXRpYy5zMy5hbWF6b25hd3MuY29tLzIzMzM3MF81NjYxMjU4MDU2NmQ0YzQ2YTEwZmYyZjcwZmE4NThhNC5odG1sICAgCiAgICArIGh0dHBzOi8vd3d3LmFuYWx5dGljc3ZpZGh5YS5jb20vYmxvZy8yMDE2LzAzL3R1dG9yaWFsLXBvd2VyZnVsLXBhY2thZ2VzLWltcHV0aW5nLW1pc3NpbmctdmFsdWVzLyAgIAoqIEZpcm91emVoIE5vZ2hyZWhjaGkncyBNaXNzaW5nIERhdGEgYW5hbHlzaXMgd2l0aCBtaWNlICAKICAgICsgaHR0cDovL3dlYi5tYXRocy51bnN3LmVkdS5hdS9+ZHdhcnRvbi9taXNzaW5nRGF0YUxhYi5odG1sICAKClwKCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKXAoKCgoKIyMjQ29udGVudHM6ICAKXAoKMS4gW0ludHJvZHVjdGlvbl0oI2ludHJvKQoyLiBbVmlzdWFsaXppbmcgTWlzc2luZyBEYXRhXSgjdmlzdWFscykgICAgCjMuIFtNSUEgd2l0aCBNSUNFIChNdWx0aXZhcmlhdGUgSW1wdXRhdGlvbiBieSBDaGFpbmVkIEVxdWF0aW9ucyldKCNtaWNlKSAgIAo0LiBbTUlBIHdpdGggdGhlIG1pc3NGb3Jlc3QgcGFja2FnZV0oI21mKSAgICAKNS4gW01JQSB3aXRoIHRoZSBIbWlzYyBwYWNrYWdlXSgjaG1pc2MpICAgCjYuIFtSZWZlcmVuY2VzICYgb3RoZXIgcmVzb3VyY2VzXSgjcmVmKQoKXAoKCmBgYHtyIGdsb2JhbF9vcHRpb25zLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZmlnLndpZHRoPTYsIGZpZy5hc3A9MC4zLCBmaWcucGF0aD0nRmlncy8nLAogICAgICAgICAgICAgICAgICAgICAgZWNobz1ULCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFKQpgYGAKCgoKIyMjSW50cm9kdWN0aW9uIHsjaW50cm99CgpcICAKIAogCiAKV2UgbWlnaHQgaGF2ZSB0byB3b3JrIHdpdGggaW5jb21wbGV0ZSBkYXRhIHNldHMgZm9yIGFueSBudW1iZXIgb2YgcmVhc29ucy4gIFBhcnRpY2lwYW50cyBtYXkgaGF2ZSBza2lwcGVkIHF1ZXN0aW9ucyBpbiBhIHN1cnZleSBvciBhc3Nlc3NtZW50LiAgUGVyaGFwcyB3ZSBkZXNpZ25lZCBvdXIgY29tcHV0ZXItbWVkaWF0ZWQgYXNzZXNzbWVudCB0byByYW5kb21seSBzZWxlY3QgNTAgcXVlc3Rpb25zIGZyb20gYSBwb29sIG9mIDYwIHF1ZXN0aW9ucyB0byBnaXZlIHRvIGVhY2ggdGVzdCB0YWtlci4gIEZvciB3aGF0ZXZlciB0aGUgcmVhc29uLCB3ZSBvZnRlbiBoYXZlIHRvIHdvcmsgd2l0aCBpbmNvbXBsZXRlIGRhdGEgc2V0cy4gIAoKKipNdWx0aXBsZSBJbXB1dGF0aW9uIEFuYWx5c2lzIChNSUEpKiogKExpdHRsZSBhbmQgUnViaW4sIDIwMDIpIGlzIGEgbWV0aG9kIHVzZWQgdG8gZmlsbCBpbiBtaXNzaW5nIG9ic2VydmF0aW9ucy4gIEl0IHRha2VzIGludG8gYWNjb3VudCB0aGUgdW5jZXJ0YWludHkgcmVsYXRlZCB0byB0aGUgdW5rbm93biByZWFsIHZhbHVlcyBieSBpbXB1dGluZyAqTSogcGxhdXNpYmxlIHZhbHVlcyBmb3IgZWFjaCB1bm9ic2VydmVkIHJlc3BvbnNlIGluIHRoZSBkYXRhLiBUaGlzIHJlbmRlcnMgKk0qIGRpZmZlcmVudCB2ZXJzaW9ucyBvZiB0aGUgZGF0YSBzZXQsIHdoZXJlIHRoZSBub24tbWlzc2luZyBkYXRhIGlzIGlkZW50aWNhbCwgYnV0IHRoZSBtaXNzaW5nIGRhdGEgZW50cmllcyBkaWZmZXIuCkRpc2NhcmRpbmcgYWxsIHBhcnRpYWxseSBvYnNlcnZlZCBkYXRhIHVuaXRzIChlLmcuLCB0aHJvdWdoIGxpc3R3aXNlIGRlbGV0aW9uKSBpcyBnZW5lcmFsbHkgbm90IHJlY29tbWVuZGVkIGJlY2F1c2UgaXQgY2FuIGxlYWQgdG8gc3Vic3RhbnRpYWwgYmlhcyBhbmQgcG9vciBwcmVkaWN0aW9ucyAoQW1ibGVyLCBPbWFyLCAmIFJveXN0b24sIDIwMDcpLiAgCgoKSXQncyBpbXBvcnRhbnQgdG8ga25vdyBob3cgbXVjaCBtaXNzaW5nIGRhdGEgd2UgaGF2ZSBhbmQgaG93IGl0IGlzIHNwcmVhZCBhY3Jvc3Mgb3VyIGRhdGEuIERhdGEgaXMgY29uc2lkZXJlZCAqTWlzc2luZyBDb21wbGV0ZWx5IGF0IFJhbmRvbSogKE1DQVIpIGlmICJ0aGUgcHJvcGVuc2l0eSB0byBvYnNlcnZlIGEgbWlzc2luZyB2YWx1ZSBpbiBhbiBpdGVtIGlzIHVucmVsYXRlZCB0byBhKSB0aGUgdmFsdWUgb2YgdGhlIGl0ZW0gaXRzZWxmIGFuZCB0byBvdGhlciBpdGVtczsgYikgdG8gdGhlIGxhdGVudCB0cmFpdCB2YWx1ZXM7IGFuZCBjKSB0byBhbnkgb3RoZXIgbWVhc3VyZWQgdmFyaWFibGVzIGluIHRoZSBhbmFseXNpcyIgKFN1bGlzICYgUG9yY3UsIDIwMTcsIHAuIDMzMSkuICBJbiBvdGhlciB3b3JkcywgZGF0YSBpcyBNQ0FSIGlmIHRoZXJlIGFyZSBubyB2YXJpYWJsZXMgaW5mbHVlbmNpbmcgd2hhdCBvYnNlcnZhdGlvbnMgYXJlIG1pc3NpbmcgKGUuZy4sIHBhcnRpY2lwYW50cycgZG9uJ3QgYW5zd2VyIGEgcXVlc3Rpb24gYmVjYXVzZSBvZiB0aGUgbmF0dXJlIG9mIHRoZSBxdWVzdGlvbiwgYmVjYXVzZSBpdCdzIHRvbyBkaWZmaWN1bHQsIG9yIGJlY2F1c2UgcGFydGljaXBhbnRzIGluIGNsYXNzIEIgd2VyZW4ndCBnaXZlbiB0aGUgcXVlc3Rpb24uIERhdGEgaXMgKk1pc3NpbmcgYXQgUmFuZG9tKiAoTUFSKSBpcyB0aGVpciBkaXN0cmlidXRpb24gZGVwZW5kcyBvbmx5IG9uIG9ic2VydmVkIGRhdGEuICAgCgoKXApcCgoKIyMjI1ByZXBhcmluZyBEYXRhCgoKRm9yIHRoaXMgdHV0b3JpYWwsIG1ha2Ugc3VyZSB5b3UgaW5zdGFsbCBhbmQgbG9hZCB0aGUgZm9sbG93aW5nIHBhY2thZ2VzOgoKYGBge3IgbGlicmFyaWVzfQpsaWJyYXJ5KG1pc3NGb3Jlc3QpCmxpYnJhcnkoSG1pc2MpCmxpYnJhcnkobWljZSkKbGlicmFyeShWSU0pCmxpYnJhcnkocm1zKQpgYGAKXAoKCgoKIyMjI1NhbXBsZSBEYXRhICoqaXJpcyoqCgpMZXQncyB3b3JrIHdpdGggdGhlIGBpcmlzYCBzYW1wbGUgZGF0YSBzZXQgaW4gUi4KCmBgYHtyIGdldERhdGF9CgpkYXRhIDwtIGlyaXMKaGVhZChpcmlzKQpgYGAKXAoKTm93IGxldCdzIHJhbmRvbWx5IGFkZCBtaXNzaW5nIHZhbHVlcyB1c2luZyB0aGUgYHByb2ROQWAgZnVuY3Rpb24gZnJvbSBgbWlzc0ZvcmVzdGAuCgpgYGB7ciBwcm9kTkFzfQoKI1Byb2R1Y2UgTkFzIGluIDEwJSBvZiB0aGUgZGF0YQppcmlzLm1pcyA8LSBwcm9kTkEoaXJpcywgbm9OQSA9IDAuMSkKaGVhZChpcmlzLm1pcykKYGBgClwKCgoKIyMjVmlzdWFsaXppbmcgIk1pc3NpbmcgRGF0YSIgeyN2aXN1YWxzfQoKCldlIGNhbiBjcmVhdGUgYSB0YWJsZSBvZiBtaXNzaW5nIHZhbHVlcyB3aXRoIHRoaXMgZnVuY3Rpb24gZnJvbSB0aGUgYG1pY2VgIHBhY2thZ2U6CgpgYGB7ciBtZHBhdHRlcm59CgptZC5wYXR0ZXJuKGlyaXMubWlzKQpgYGAKXAoKQWx0ZXJuYXRpdmVseSwgd2UgY2FuIGFsc28gZmluZCB0aGUgbnVtYmVyIG9mIE5BcyBmb3IgZWFjaCB2YXJpYWJsZSB1c2luZyBzYXBwbHk6CgpgYGB7cn0KCnNhcHBseShpcmlzLm1pcywgZnVuY3Rpb24oeCkgc3VtKGlzLm5hKHgpKSkKYGBgClwKCgpVc2UgdGhlIGBhZ2dyYCBmdW5jdGlvbiBmcm9tIHRoZSBgVklNYCBwYWNrYWdlIHRvIHZpc3VhbGl6ZSBtaXNzaW5nIGRhdGEuCgpgYGB7ciBpcmlzLm1pcy1wbG90fQoKbWlzc19wbG90IDwtIGFnZ3IoaXJpcy5taXMsIGNvbD1jKCduYXZ5Ymx1ZScsJ3llbGxvdycpLAogICAgICAgICAgICAgICAgICAgIG51bWJlcnM9VFJVRSwgc29ydFZhcnM9VFJVRSwKICAgICAgICAgICAgICAgICAgICBsYWJlbHM9bmFtZXMoaXJpcy5taXMpLCBjZXguYXhpcz0uNywKICAgICAgICAgICAgICAgICAgICBnYXA9MywgeWxhYj1jKCJNaXNzaW5nIGRhdGEiLCJQYXR0ZXJuIikpCmBgYApcCgpVc2luZyBgbWFyZ2lucGxvdGAgdG8gdmlzdWFsaXplIG1pc3NpbmcgZGF0YSBmb3IgdGhlIGBTZXBhbC5XaWR0aGAgYW5kIGBTZXBhbC5MZW5ndGhgIHZhcmlhYmxlczoKCmBgYHtyfQoKbWFyZ2lucGxvdChpcmlzLm1pc1tjKDEsMildKQpgYGAKXAoKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMjTUlDRSB7I21pY2V9CipNdWx0aXZhcmlhdGUgSW1wdXRhdGlvbiBieSBDaGFpbmVkIEVxdWF0aW9ucyAoTUlDRSkqCgpJbiBvcmRlciB0byBpbXB1dGUgbWlzc2luZyB2YWx1ZXMgd2l0aCBNSUNFLCB3ZSB1c2UgdGhlIGBtaWNlYCBwYWNrYWdlICBEZXBlbmRpbmcgb24gaG93IGJpZyB5b3VyIGRhdGEgc2V0IGlzLCB0aGlzIGNhbiB0YWtlIGEgd2hpbGUgKDMwIHNlYyB0byBhIGZldyBob3VycyksIHNvIGJlIHByZXBhcmVkIHRvIHdhaXQuCgpgYGB7ciBtaWNlLCByZXN1bHRzPSJoaWRlIn0KCmltcHV0ZWRfRGF0YSA8LSBtaWNlKGlyaXMubWlzLCBtPTUsIG1heGl0ID0gNTAsIG1ldGhvZCA9ICdwbW0nLCBzZWVkID0gNTAwKQpgYGAKCioqbSoqOiB0aGUgbnVtYmVyIG9mIGltcHV0YXRpb25zIG1hZGUgcGVyIG1pc3Npbmcgb2JzZXJ2YXRpb24gKDUgaXMgbm9ybWFsLS1nZW5lcmF0ZXMgNSBkYXRhIHNldHMgd2l0aCBpbXB1dGVkL29yaWdpbmFsIHZhbHVlcykgIAoqKm1heGl0Kio6IHRoZSBudW1iZXIgb2YgaXRlcmF0aW9ucz8gIAoqKm1ldGhvZCoqOiBXZSB1c2UgJ3Byb2JhYmxlIG1lYW5zID8/Cioqc2VlZCoqOiBWYWx1ZXMgdG8gcmFuZG9tbHkgZ2VuZXJhdGUgZnJvbT8/ClwKIAogCldlIGNhbiBnZXQgYSBzdW1tYXJ5IG9mIHRoZSBkYXRhIGhlcmU6CgpgYGB7cn0KCnN1bW1hcnkoaW1wdXRlZF9EYXRhKQpgYGAKXAoKClRvIGNoZWNrIGFsbCA1IHNldHMgb2YgaW1wdXRlZCB2YWx1ZXMgZm9yIGEgZ2l2ZW4gdmFyaWFibGUgKHN1Y2ggYXMgU2VwYWwuV2lkdGgpLCBydW4gdGhlIGZvbGxvd2luZzoKCmBgYHtyfQppbXB1dGVkX0RhdGEkaW1wJFNlcGFsLldpZHRoCmBgYApcCgoKIyMjI1dheXMgdG8gdmlzdWFsaXplIG1pc3NpbmcgJiBvYnNlcnZlZCBkYXRhOiAKUGxvdCBzZXBhbCB3aWR0aCBhZ2FpbnN0IGFsbCBvdGhlciBjYXRlZ29yaWVzCgpgYGB7cn0KeHlwbG90KGltcHV0ZWRfRGF0YSxTZXBhbC5XaWR0aCB+IFNlcGFsLkxlbmd0aCArIFBldGFsLldpZHRoLHBjaD0xOCxjZXg9MSkKZGVuc2l0eXBsb3QoaW1wdXRlZF9EYXRhKQpzdHJpcHBsb3QoaW1wdXRlZF9EYXRhLCBwY2ggPSAyMCwgY2V4ID0gMS4yKQpgYGAKXAoKCiMjIyNBZGQgdGhlIGRhdGEgYmFjayB0byBvcmlnaW5hbCBkYXRhIHVzaW5nIG9uZSBvZiB0aGUgaXRlcm5hdGlvbnMKCkxldCdzIHVzZSB0aGUgdGhpcmQgaXRlcmF0aW9uOgoKYGBge3J9CmNvbXBsZXRlRGF0YSA8LSBjb21wbGV0ZShpbXB1dGVkX0RhdGEsIDMpCmhlYWQoY29tcGxldGVEYXRhKQpgYGAKXAoKCiMjIyNCdWlsZCBhIHByZWRpY3RpdmUgbW9kZWwKCldlIGNhbiB1c2UgYWxsIDUgaW1wdXRlZCBkYXRhIHNldHMgdG8gYnVpbGQgcHJlZGljdGl2ZSBtb2RlbC4gIFRoaXMgb25seSB3b3JrcyBmb3IgZGF0YSB3aGVyZSB5b3UgZXhwZWN0IHNvbWUgdHlwZSBvZiBsaW5lYXIgcmVsYXRpb25zaGlwLgoKYGBge3J9CmZpdCA8LSB3aXRoKGRhdGEgPSBpbXB1dGVkX0RhdGEsIGV4cCA9IGxtKFNlcGFsLldpZHRoIH4gU2VwYWwuTGVuZ3RoICsgUGV0YWwuV2lkdGgpKSAKYGBgClwKCgpUaGVuIHBvb2wgcmVzdWx0cyBvZiB0aGUgcHJlZGljdGl2ZSBtb2RlbCB0byBzZWUgaG93IGdvb2QgYSBmaXQgdGhlIGltcHV0ZWQgZGF0YSBzZXRzIGFyZS4KCmBgYHtyfQpjb21iaW5lIDwtIHBvb2woZml0KQpzdW1tYXJ5KGNvbWJpbmUpCmBgYApcCgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMjI21pc3NGb3Jlc3QgeyNtZn0KCkltcHV0ZSBtaXNzaW5nIHZhbHVlcywgdXNpbmcgYWxsIHBhcmFtZXRlcnMgYXMgZGVmYXVsdCB2YWx1ZXMKCmBgYHtyfQppcmlzLmltcCA8LSBtaXNzRm9yZXN0KGlyaXMubWlzKQpgYGAKXAoKCkNoZWNrIGltcHV0ZWQgdmFsdWVzIGJ5IGRhdGEgc2V0CgpgYGB7cn0KaXJpcy5pbXAkeGltcApgYGAKXAoKCkNoZWNrIGZvciBpbXB1dGF0aW9uIGVycm9yOgoKCmBgYHtyfQoKaXJpcy5pbXAkT09CZXJyb3IKYGBgClwKCgpOUk1TRSBpcyBub3JtYWxpemVkIG1lYW4gc3F1YXJlZCBlcnJvci4gSXQgaXMgdXNlZCB0byByZXByZXNlbnQgZXJyb3IgZGVyaXZlZCBmcm9tIGltcHV0aW5nIGNvbnRpbnVvdXMgdmFsdWVzLiBQRkMgKHByb3BvcnRpb24gb2YgZmFsc2VseSBjbGFzc2lmaWVkKSBpcyB1c2VkIHRvIHJlcHJlc2VudCBlcnJvciBkZXJpdmVkIGZyb20gaW1wdXRpbmcgY2F0ZWdvcmljYWwgdmFsdWVzLgoKQ29tcGFyZSBhY3R1YWwgZGF0YSB3aXRoIGltcHV0ZWQgZGF0YSBzZXQgdG8gc2VlIGVycm9yOiAKCmBgYHtyfQoKaXJpcy5lcnIgPC0gbWl4RXJyb3IoaXJpcy5pbXAkeGltcCwgaXJpcy5taXMsIGlyaXMpCmlyaXMuZXJyCmBgYApcCgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMjI0htaXNjIHsjaG1pc2N9CgpgSG1pc2NgIGhhcyBzZXZlcmFsIGZ1bmN0aW9ucyBhbmQgbWV0aG9kcyBhdmFpbGFibGUgdG8gaW1wdXRlIHZhbHVlcy4KCkltcHV0ZSB2YWx1ZXMgYmFzZWQgb24gdGhlIG1lYW4gb2Ygb2JzZXJ2YXRpb25zOgoKYGBge3J9CgppcmlzLm1pcyRpbXB1dGVkX2FnZSA8LSB3aXRoKGlyaXMubWlzLCBpbXB1dGUoU2VwYWwuTGVuZ3RoLCBtZWFuKSkKaGVhZChpcmlzLm1pcyRpbXB1dGVkX2FnZSkKYGBgClwKCgpJbXB1dGUgdmFsdWVzIHVzaW5nIHJhbmRvbWx5IGdlbmVyYXRlZCBudW1iZXJzOgpgYGB7cn0KaXJpcy5taXMkaW1wdXRlZF9hZ2UyIDwtIHdpdGgoaXJpcy5taXMsIGltcHV0ZShTZXBhbC5MZW5ndGgsICdyYW5kb20nKSkKaGVhZChpcmlzLm1pcyRpbXB1dGVkX2FnZTIpCmBgYAoKKihZb3UgY2FuIGFsc28gdXNlIG1pbiwgbWF4LCBtZWRpYW4gdG8gaW1wdXRlIG1pc3NpbmcgdmFsdWUpKiAgClwKXAogIAogIAojIyMjYXJnSW1wdXRlCgpVc2luZyB0aGUgYGFyZ0ltcHV0ZWAgZnVuY3Rpb24sIGBIbWlzY2AgcGVyZm9ybXMgbXVsdGlwbGUgaW1wdXRhdGlvbiB1c2luZyBib290c3RyYXBpbmcgYW5kIHByZWRpY3RpdmUgbWVhbiBtYXRjaGluZy4gIERpZmZlcmVudCBib290c3RyYXAgcmVzYW1wbGVzIGFyZSB1c2VkIGZvciBlYWNoIG9mIHRoZSBtdWx0aXBsZSBpbXB1dGF0aW9ucy4gIFRoZW4gYSBmbGV4aWJsZSBhZGRpdGl2ZSBtb2RlbCBpcyB1c2VkIHRvIHByZWRpY3QgbWlzc2luZyB2YWx1ZXMgZnJvbSBub25taXNzaW5nIHZhbHVlcy4gIEl0IGFjaGVpdmVzIHRoaXMgYnkgY2hlY2tpbmcgdGhlIGZpdCBvZiBib290c3RyYXBwZWQgc2FtcGxlcyBiYXNlZCB0byBhIHByZWRpY3RpdmUgbW9kZWwgKGRlZmF1bHQpIGJhc2VkIG9uIHRoZSBvcmlnaW5hbCBkYXRhLgoKUHJlZGljdGl2ZSBtZWFuIG1hdGNoaW5nIHdvcmtzIHdlbGwgZm9yIGNvbnRpbnVvdXMgYW5kIGNhdGVnb3JpY2FsIChiaW5hcnkgJiBtdWx0aS1sZXZlbCkgd2l0aG91dCB0aGUgbmVlZCBmb3IgY29tcHV0aW5nIHJlc2lkdWFscyBhbmQgbWF4aW11bSBsaWtlbGlob29kIGZpdC4KCiogQXNzdW1lcyBsaW5lYXJpdHkgaW4gdGhlIHZhcmlhYmxlcyBiZWluZyBwcmVkaWN0ZWQuCiogRmlzaGVy4oCZcyBvcHRpbXVtIHNjb3JpbmcgbWV0aG9kIGlzIHVzZWQgZm9yIHByZWRpY3RpbmcgY2F0ZWdvcmljYWwgdmFyaWFibGVzLgoKYGBge3J9CmltcHV0ZV9hcmcgPC0gYXJlZ0ltcHV0ZSh+IFNlcGFsLkxlbmd0aCArIFNlcGFsLldpZHRoICsgUGV0YWwuTGVuZ3RoICsgUGV0YWwuV2lkdGggKyBTcGVjaWVzLCBkYXRhID0gaXJpcy5taXMsIG4uaW1wdXRlID0gNSkKYGBgCgoqKmZvcm11bGEqKjogVGhlIH4gU2VwYWwuTGVuZ3RoICsgLi4uIGFyZ3VtZW50IGluZGljYXRlcyB3aGF0IGZvcm11bGEgdG8gdXNlLiAgSW4gdGhpcyBjYXNlLCB3ZSB3YW50IGFsbCB2YXJpYWJsZXMnIG1pc3NpbmcgZGF0YSB0byBiZSBpbXB1dGVkLCBzbyB3ZSBhZGQgZWFjaCBvbmUuICAKKipkYXRhKio6IHRoZSBkYXRhIGZyYW1lIHdpdGggbWlzc2luZyBkYXRhLiAgCioqbi5pbXB1dGUqKjogbnVtYmVyIG9mIG11bHRpcGxlIGltcHV0YXRpb25zLiAgNSBpcyBmcmVxdWVudGx5IHVzZWQsIGJ1dCAxMCBvciBtb3JlIGRvZXNuJ3QgaHVydCAgIAoKKk5vdGU6IGFyZ0ltcHV0ZSgpIGF1dG9tYXRpY2FsbHkgaWRlbnRpZmllcyB0aGUgdmFyaWFibGUgdHlwZSBhbmQgdHJlYXRzIHRoZW0gYWNjb3JkaW5nbHkuKgpcClwKCgoKU2VlIGEgc3VtbWFyeSBvZiBpbXB1dGVkIHZhbHVlczoKYGBge3J9CmltcHV0ZV9hcmcKYGBgCgoqVGhlIFIgc3F1YXJlIHZhbHVlcyBpbmRpY2F0ZSB0aGUgbGlrbGlob29kIHRoYXQgdGhlIHByZWRpY3RlZCBtaXNzaW5nIHZhbHVlcyBpbiB0aGUgJ2lydCcgZGF0YWZyYW1lIG1hdGNoIHdoYXQgd2Ugd291bGQgYWN0dWFsbHkgb2JzZXJ2ZSBpZiBwYXJ0aWNpcGFudHMgaGFkIGFuc3dlcmVkIGFsbCBxdWVzdGlvbnMuICBIaWdoZXIgdmFsdWVzIGFyZSBiZXR0ZXIuKgpcCgpcCgoKQ2hlY2sgaW1wdXRlZCB2YWx1ZXMgZm9yIHZhcmlhYmxlIGBTZXBhbC5MZW5ndGhgOgoKYGBge3J9CmltcHV0ZV9hcmckaW1wdXRlZCRTZXBhbC5MZW5ndGgKYGBgClwKClwKCgpUbyB1c2Ugb25lIG9mIHRoZSBpdGVyYXRpb25zIGluIG91ciBvcmlnaW5hbCBkYXRhIHNldCwgd2UgY2FuIHVzZSB0aGUgYHRyYW5zY2VuZGAgZnVuY3Rpb246CmBgYHtyfQpjb21wbGV0ZURhdGEyIDwtIGltcHV0ZS50cmFuc2NhbihpbXB1dGVfYXJnLCBpbXB1dGF0aW9uPTEsIGRhdGE9aXJpcy5taXMsIGxpc3Qub3V0PVRSVUUscHI9RkFMU0UsIGNoZWNrPUZBTFNFKSAKaGVhZChjb21wbGV0ZURhdGEyKQpgYGAKCipJdCBjb252ZW5pZW50bHkgcHV0cyBhbiBhc3RlcmlzayBuZXh0IHRvIGVhY2ggaW1wdXRlZCB2YWx1ZS4qClwKClwKCgpOb3cgd2UgY2FuIGNyZWF0ZSBhIGZpdCBtb2RlbCB1c2luZyAqb2xzKiAobGluZWFyIG1vZGVsKSBmcm9tIHRoZSAqcm1zKiBwYWNrYWdlIChSZWdyZXNzaW9uIE1vZGVsaW5nIFN0cmF0ZWdpZXMpCgpgYGB7cn0KCmZtaSA8LSBmaXQubXVsdC5pbXB1dGUoU2VwYWwuTGVuZ3RoIH4gU2VwYWwuV2lkdGggKyBQZXRhbC5MZW5ndGggKyBQZXRhbC5XaWR0aCArIFNwZWNpZXMsIG9scywgaW1wdXRlX2FyZywgZGF0YT1pcmlzLm1pcykKYGBgCgpcICAKCgpcICAKCgojIyNDb25jbHVzaW9uCgpUaGlzIHR1dG9yaWFsIHN5bnRoZXNpemVzIHNvbWUgb2YgdGhlIHJlc2VhcmNoIGFydGljbGVzIGFuZCBvbmxpbmUgdHV0b3JpYWxzIGF2YWlsYWJsZSBvbiAqKk11bHRpcGxlIEltcHV0YXRpb24gQW5hbHlzaXMqKi4gIEhvd2V2ZXIsIHlvdSBjYW4gbGVhcm4gbW9yZSBmcm9tIHNvbWUgb2YgdGhlIHJlZmVyZW5jZXMgYmVsb3cuClwgIAoKXCAgCgpcICAKCgoKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKClwgIAoKCgojIyMjVHJvdWJsZXNob290aW5nCgpNSUNFOiBpZiB5b3Ugc3RpbGwgaGF2ZSBOQXMgaW4geW91ciBkYXRhc2V0LCBpdCdzIHBvc3NpYmxlIHRoYXQKeW91IG5lZWQgdG8gaW5jcmVhc2UgeW91ciBtYXhpdCBudW1iZXIKaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvMjA5NDc5MDgvaW1wdXRhdGlvbi1taWNlLWluLXItc3RpbGwtbmEtbGVmdC1pbi1kYXRhc2V0CgpcICAKCgojIyMjTUlDRTogaGVscGZ1bCB0dXRvcmlhbHMKCiJNaXNzaW5nIGRhdGEiIFZpc3VhbGl6YXRpb25zICAKaHR0cHM6Ly9kYXRhc2NpZW5jZXBsdXMuY29tL2ltcHV0aW5nLW1pc3NpbmctZGF0YS13aXRoLXItbWljZS1wYWNrYWdlLwoKCmJlZ2lubmVyIGd1aWRlICAKaHR0cHM6Ly9kYXRhc2NpZW5jZXBsdXMuY29tL2hhbmRsaW5nLW1pc3NpbmctZGF0YS13aXRoLW1pY2UtcGFja2FnZS1hLXNpbXBsZS1hcHByb2FjaC8KCkV4cGxhbmF0aW9ucyBvZiBNSUNFIGFuZCB0eXBlcyBvZiBtaXNzaW5nIGRhdGEgIApodHRwczovL3d3dy5rZG51Z2dldHMuY29tLzIwMTcvMDkvbWlzc2luZy1kYXRhLWltcHV0YXRpb24tdXNpbmctci5odG1sCgpcICAKCiMjIyNNaXNzaW5nIERhdGEgaW4gSVJUIGRhdGEgc2V0cwoKQm9vbmUsIFcuIEouICgyMDE2KS4gUmFzY2ggQW5hbHlzaXMgZm9yIEluc3RydW1lbnQgRGV2ZWxvcG1lbnQ6IFdoeSwgV2hlbiwgYW5kIEhvdz8gQ0JFIExpZmUgU2NpZW5jZXMgRWR1Y2F0aW9uLCAxNSg0KSwgcm00LiBodHRwOi8vZG9pLm9yZy8xMC4xMTg3L2NiZS4xNi0wNC0wMTQ4ICAKCkZpbmNoLCBILiAoMjAwOCkuIEVzdGltYXRpb24gb2YgaXRlbSByZXNwb25zZSB0aGVvcnkgcGFyYW1ldGVycyBpbiB0aGUgcHJlc2VuY2Ugb2YgbWlzc2luZyBkYXRhLiAqSm91cm5hbCBvZiBFZHVjYXRpb25hbCBNZWFzdXJlbWVudCosIDQ1KDMpLCAyMjXigJMyNDUuICAKClwgIAoKCgojIyMjUmVmZXJlbmNlcyB7I3JlZn0KCkFtYmxlciBHLiwgT21hciBSLiBaLiwgUm95c3RvbiBQLiAoMjAwNykuIEEgY29tcGFyaXNvbiBvZiBpbXB1dGF0aW9uIHRlY2huaXF1ZXMgZm9yIGhhbmRsaW5nIG1pc3NpbmcgcHJlZGljdG9yIHZhbHVlcyBpbiBhIHJpc2sgbW9kZWwgd2l0aCBhIGJpbmFyeSBvdXRjb21lLiAqU3RhdGlzdGljYWwgTWV0aG9kcyBpbiBNZWRpY2FsIFJlc2VhcmNoKiwgMTYsIDI3N+KAkzI5OC4gIAoKTGl0dGxlLCBSLiBKLiBBLiwgJiBSdWJpbiwgRC4gQi4gKDIwMDIpLiAqU3RhdGlzdGljYWwgYW5hbHlzaXMgd2l0aCBtaXNzaW5nIGRhdGEuKiBIb2Jva2VuLCBOSjogSm9obiBXaWxleSBhbmQgU29ucywgSW5jLiAgCgpTdWxpcywgSS4sICYgUG9yY3UsIE0uICgyMDE3KS4gSGFuZGxpbmcgTWlzc2luZyBEYXRhIGluIEl0ZW0gUmVzcG9uc2UgVGhlb3J5LiAqQXNzZXNzaW5nIHRoZSBBY2N1cmFjeSBvZiBhIE11bHRpcGxlIEltcHV0YXRpb24gUHJvY2VkdXJlIEJhc2VkIG9uIExhdGVudCBDbGFzcyBBbmFseXNpcy4gSm91cm5hbCBvZiBDbGFzc2lmaWNhdGlvbiosIDM0KDIpLCAzMjfigJMzNTkuIGh0dHBzOi8vZG9pLm9yZy8xMC4xMDA3L3MwMDM1Ny0wMTctOTIyMC0zICAKCgo=