1 Introduction

For this project, we will be working on creating a multiple logistic regression model. The data set for this project looks at the various factors affecting an individual patient’s risk of developing coronary heart disease (CHD) in a 10-year period of time. The response variable in this data set is binary with 1 representing yes, the patient is at risk of developing CHD within a 10-year period of time, and 0 representing no, the patient is not at risk of developing CHD within a 10-year period of time.

Our overall goal in this project is to create a model which has the greatest predictive performance. We will create several candidate models and compare them with each other to determine which model should be selected as the final model for the prediction of an individual’s odds of being at risk for developing CHD over a 10-year period of time. We will use the cross-validation process to determine which of the candidate models provides the best predictive power. We will also use ROC analysis to determine which of the candidate models has the best global goodness. This will allow us to see which of the candidate models provides the best utility and quality for predictive modeling.

1.1 Data Description

I found this data set on kaggle.com on the following web page: https://www.kaggle.com/datasets/dileep070/heart-disease-prediction-using-logistic-regression/data

This data set takes a look at various medical and personal factors which may have an impact on an individual’s risk of developing CHD within the next 10 years. The binary response variable tells whether an individual patient is at risk of developing CHD within the next 10 years, with a response value of 1 meaning yes they are at risk, and 0 meaning no they are not at risk.

1.2 Variables

There are 16 variables in this data set.

  • male: The gender of the patient. A categorical variable with 1 for “male”, 0 for “female”, and 2 for “other”.

  • age: The age of the patient in years. This is a quantitative, continuous variable given as an integer value.

  • education: The education level of the patient. A categorical variable with 1 for “less than high school”, 2 for “high school diploma”, 3 for “college graduate”, and 4 for “post-college graduate”.

  • currentSmoker: Whether or not the patient is currently a smoker. A binary variable with 1 for “yes” and 0 for “no”.

  • cigsPerDay: The average number of cigarettes the patient smokes in a day. This is a quantitative, continuous variable.

  • BPMeds: Whether or not the patient takes blood pressure medication. A binary variable with 1 for “yes” and 0 for “no”.

  • prevalentStroke: Whether or not the patient has a history of strokes. A binary variable with 1 for “yes” and 0 for “no”.

  • prevalentHyp: Whether or not the patient has a history of hypertension. A binary variable with 1 for “yes” and 0 for “no”.

  • diabetes: Whether or not the patient has a history of diabetes. A binary variable with 1 for “yes” and 0 for “no”.

  • totChol: The patient’s total cholesterol level, given in mg/dL (milligrams per deciliter). A quantitative, continuous variable.

  • sysBP: The patient’s systolic blood pressure, given in mmHG (millimeters of mercury). A quantitative, continuous variable.

  • diaBP: The patient’s diastolic blood pressure, given in mmHG (millimeters of mercury). A quantitative, continuous variable.

  • BMI: The patient’s body mass index (BMI). A quantitative, continuous variable.

  • heartRate: The patient’s heart rate, given in beats per minute. A numeric, quantitative variable.

  • glucose: The patient’s glucose level, given in mg/dL (milligrams per deciliter). A numeric, quantitative variable.

  • TenYearCHD (response variable): The binary response variable of this data set which represents whether or not the patient has a 10-year risk of developing Coronary Heart Disease (CHD). This response variable is binary with 1 meaning yes, the patient does have a risk of developing CHD in the next 10 years, and 0 meaning no, the patient does not have a risk of developing CHD in the next 10 years.

1.3 Research Questions

The key analytical question which I would like to investigate in this project is:

  • Can we create a model which has the highest predictive power in regards to predicting a patient’s odds of being at risk for developing coronary heart disease within a 10-year period of time based on the predictor variables in this data set?

This question will serve as the basis for creating the candidate multiple logistic regression models for this data set. We will ultimately choose a final model of these potential candidate models based upon our findings in regards to their predictive power. The findings to this question could provide utility for both patients and doctors to provide them with information relating to the odds and risks for developing CHD and which factors significantly impact these odds.

Some further questions to consider as a starting point for this project include:

  • Is there a major difference in the predictive powers of the candidate models we create?

  • Do the continuous independent variables in this data set follow a normal distribution? And if not, is there a potential explanation for why this may be the case?

  • How good is the predictive quality of our final model in regards of predicting whether an individual will be at risk for developing CHD in a 10-year period of time?

We will use these questions as a starting point for this project in order to create a multiple logistic regression model which has the greatest predictive power for predicting the odds of a patient being at risk for developing CHD in a 10-year period based on the many factors which play a role in this prediction.

2 Exploratory Data Analysis

First, we will read in the data set from Github and we will call it “heartdisease”.

heartdisease <- read.csv("https://raw.githubusercontent.com/JosieGallop/STA321/refs/heads/main/dataset/framingham.csv", header=TRUE)

str(heartdisease)
'data.frame':   4238 obs. of  16 variables:
 $ male           : int  1 0 1 0 0 0 0 0 1 1 ...
 $ age            : int  39 46 48 61 46 43 63 45 52 43 ...
 $ education      : int  4 2 1 3 3 2 1 2 1 1 ...
 $ currentSmoker  : int  0 0 1 1 1 0 0 1 0 1 ...
 $ cigsPerDay     : int  0 0 20 30 23 0 0 20 0 30 ...
 $ BPMeds         : int  0 0 0 0 0 0 0 0 0 0 ...
 $ prevalentStroke: int  0 0 0 0 0 0 0 0 0 0 ...
 $ prevalentHyp   : int  0 0 0 1 0 1 0 0 1 1 ...
 $ diabetes       : int  0 0 0 0 0 0 0 0 0 0 ...
 $ totChol        : int  195 250 245 225 285 228 205 313 260 225 ...
 $ sysBP          : num  106 121 128 150 130 ...
 $ diaBP          : num  70 81 80 95 84 110 71 71 89 107 ...
 $ BMI            : num  27 28.7 25.3 28.6 23.1 ...
 $ heartRate      : int  80 95 75 65 85 77 60 79 76 93 ...
 $ glucose        : int  77 76 70 103 85 99 85 78 79 88 ...
 $ TenYearCHD     : int  0 0 0 1 0 0 1 0 0 0 ...

We will make a copy of the data set called “heartdisease0” which will be the data set we will analyze and transform through the exploratory data analysis.

heartdisease0 <- heartdisease

2.1 Fixing the Missing Observations

When looking through the data set, I noticed that some observations appeared to be missing from the data set, as they had values of “NA” for certain variables. Before we can begin with the logistic regression, this is something that we should look into.

First, let’s see how many values are missing for each of the variables.

colSums(is.na(heartdisease0))
           male             age       education   currentSmoker      cigsPerDay 
              0               0             105               0              29 
         BPMeds prevalentStroke    prevalentHyp        diabetes         totChol 
             53               0               0               0              50 
          sysBP           diaBP             BMI       heartRate         glucose 
              0               0              19               1             388 
     TenYearCHD 
              0 

We can see that the variables education, cigsPerDay, BPMeds, totChol, BMI, heartRate, and glucose all have missing observations.

It is important to note that single imputation methods that could be used to fix the missing observations of the variables could lead to potential skew in their distributions. So, instead we will use multiple imputation methods to fill in these missing observations with randomly approximated values. Out of the variables with missing values, none of the variables have the majority of their data entries missing, so it seems suitable to fill in these missing observations with approximated values in order to prevent these entries from coming up as NA within the data set. However, it is important to be mindful of the fact that filling in these missing entries could still lead to a potential skew or inaccuracy in the distribution of these variables after filling in the missing observations. In order to properly handle this, we will first fix the missing values and then check the distributions of the variables to ensure that they are normally distributed, or are practically important enough variables to include within the final multiple logistic regression model.

2.2 Multiple Imputation of Missing Observations

We will use multiple imputation to fill in the missing values of the observations. Using this multiple imputation method will use a randomly iterated method to fill in the missing observations within the data set. This will help ensure that the missing observations are filled in the best way possible by handling missing values in a way that reduces the chance of false prediction. We will use the Multiple Imputation using Chained Equations (MICE) function to use multiple imputation to fill in the missing observations.

We will store the results of the multiple imputation in an object called “mice” for now.

set.seed(1)
mice <- mice(heartdisease0, method = "cart")

 iter imp variable
  1   1  education  cigsPerDay  BPMeds  totChol  BMI  heartRate  glucose
  1   2  education  cigsPerDay  BPMeds  totChol  BMI  heartRate  glucose
  1   3  education  cigsPerDay  BPMeds  totChol  BMI  heartRate  glucose
  1   4  education  cigsPerDay  BPMeds  totChol  BMI  heartRate  glucose
  1   5  education  cigsPerDay  BPMeds  totChol  BMI  heartRate  glucose
  2   1  education  cigsPerDay  BPMeds  totChol  BMI  heartRate  glucose
  2   2  education  cigsPerDay  BPMeds  totChol  BMI  heartRate  glucose
  2   3  education  cigsPerDay  BPMeds  totChol  BMI  heartRate  glucose
  2   4  education  cigsPerDay  BPMeds  totChol  BMI  heartRate  glucose
  2   5  education  cigsPerDay  BPMeds  totChol  BMI  heartRate  glucose
  3   1  education  cigsPerDay  BPMeds  totChol  BMI  heartRate  glucose
  3   2  education  cigsPerDay  BPMeds  totChol  BMI  heartRate  glucose
  3   3  education  cigsPerDay  BPMeds  totChol  BMI  heartRate  glucose
  3   4  education  cigsPerDay  BPMeds  totChol  BMI  heartRate  glucose
  3   5  education  cigsPerDay  BPMeds  totChol  BMI  heartRate  glucose
  4   1  education  cigsPerDay  BPMeds  totChol  BMI  heartRate  glucose
  4   2  education  cigsPerDay  BPMeds  totChol  BMI  heartRate  glucose
  4   3  education  cigsPerDay  BPMeds  totChol  BMI  heartRate  glucose
  4   4  education  cigsPerDay  BPMeds  totChol  BMI  heartRate  glucose
  4   5  education  cigsPerDay  BPMeds  totChol  BMI  heartRate  glucose
  5   1  education  cigsPerDay  BPMeds  totChol  BMI  heartRate  glucose
  5   2  education  cigsPerDay  BPMeds  totChol  BMI  heartRate  glucose
  5   3  education  cigsPerDay  BPMeds  totChol  BMI  heartRate  glucose
  5   4  education  cigsPerDay  BPMeds  totChol  BMI  heartRate  glucose
  5   5  education  cigsPerDay  BPMeds  totChol  BMI  heartRate  glucose

We will create a new data frame called “heartdisease1” to represent the corrected data set that will have no missing values after multiple imputation. We will convert the results of the multiple imputation from a large mids object back to a regular data frame. This will be used for the future analysis and we will call this object “heartdisease1”.

heartdisease1 <-mice::complete(mice,2) 

Now that we have used the multiple imputation method to fill in all of the missing observations, let’s double check that there are no more missing observations. All of the variables should now have a value of zero missing observations.

colSums(is.na(heartdisease1))
           male             age       education   currentSmoker      cigsPerDay 
              0               0               0               0               0 
         BPMeds prevalentStroke    prevalentHyp        diabetes         totChol 
              0               0               0               0               0 
          sysBP           diaBP             BMI       heartRate         glucose 
              0               0               0               0               0 
     TenYearCHD 
              0 

As we can see, all of the variables now have zero missing observations, so we successfully fixed all of the missing values by filling them in with multiple imputation by the use of the mice function. The revised data set “heartdisease1” has no missing observations, so we will use this for further analysis and for creating the multiple logistic regression model in the future steps of this project.

2.3 Fixing Variable Types

One other thing I noticed while looking through the data set is that the variable “cigsPerDay” is given as a character variable even though it should be a numeric variable. This variable is a measure of the number of cigarettes a patient smokes in a day, so it should be reported as a numeric variable since it is a continuous range of possible values and not collected as categories. So, let’s convert cigsPerDay from a character variable to a numeric variable. We will use the as.numeric() function to convert cigsPerDay to a numeric variable.

heartdisease1$cigsPerDay <- as.numeric(heartdisease1$cigsPerDay)

Now, the cigsPerDay variable is correctly given as a numeric variable.

The variable of age should also be given as a numeric value, but is is not so we will convert this variable to a numeric as well.

heartdisease1$age <- as.numeric(heartdisease1$age)

The variable of age is now correctly given as a numeric value.

Additionally, the variable BPMeds is given as a character, however this is a binary variable so it should be given as an integer variable with values 0 and 1 like the other binary variables in the data set. We will convert this variable from a character to an integer. We will use the as.integer() function to do this conversion.

heartdisease1$BPMeds <- as.integer(heartdisease1$BPMeds)

Now, the BPMeds variable is correctly given as a binary, integer variable.

Another issue is that the variable for education is supposed to be categorical with each number representing a different category of education level. However, this variable is given as an integer variable. So, we will fix this by making education a character variable.

heartdisease1$education <- as.character(heartdisease1$education)

The education variable is now correctly given as a categorical variable.

Now, all of the variables in the data set are given in the correct types that they should be given as.

2.4 Checking the Variable Distributions

Before we begin with creating and comparing the multiple logistic regression models, we should first check the distributions of the variables in the data set to ensure that they meet the criteria of being approximately normally distributed, for the quantitative variables. Or, if the variables do not appear to follow a normal distribution, we will see if they are practically important enough to be included within the model building process despite their lack of a normal distribution. We will take a look at some of the variables, particuarelly some of the quanitiative variables which had missing values that were filled in through the multiple imputation process.

2.4.1 totChol Distribution

totChol was one of the variables that had some missing observations that were filled in during the previous steps. So, we should definetly check the distribution of this variable.

ylimit = max(density(heartdisease1$totChol)$y)
hist(heartdisease1$totChol, probability = TRUE, main = "totChol Distribution", xlab="totChol", 
       col = "aliceblue", border="cornflowerblue")
  lines(density(heartdisease1$totChol, adjust=2), col="darkorchid")

Overall, totChol appears to be unimodal and approximately normally distributed. Therefore we can say that this variable is safe to include within the multiple logistic regression model building process and that the process of filling in the missing observations did not lead to any severe concerns for the distribution of this variable.

2.4.2 BMI Distribution

BMI is another variable that had missing observations that were filled in during the previous steps. We will check its distribution to make sure it appears to be approximately normally distributed.

ylimit = max(density(heartdisease1$BMI)$y)
hist(heartdisease1$BMI, probability = TRUE, main = "BMI Distribution", xlab="BMI", 
       col = "aliceblue", border="cornflowerblue")
  lines(density(heartdisease1$BMI, adjust=2), col="darkorchid")

The distribution for BMI is unimodal and appears to be approximately normally distributed. It appears that the majority of individuals have a BMI somewhere in the range of 20-30, with the vast majority of the data falling within this range. This allows us to conclude that it is safe to include the variable of BMI within the multiple logistic regression model building process and that the process of filling in the missing values did not lead to any major concerns with the distribution of this variable.

2.4.3 Glucose Distribution

The variable glucose was another variable that had missing observations which were filled in during the previous steps. We will check its distribution to make sure that everything looks alright.

ylimit = max(density(heartdisease1$glucose)$y)
hist(heartdisease1$glucose, probability = TRUE, main = "glucose", xlab="glucose", 
       col = "aliceblue", border="cornflowerblue")
  lines(density(heartdisease1$glucose, adjust=2), col="darkorchid")

The variable glucose is unimodal and appears to be approximately normally distributed with the exception of a few outliers to the right. The most common glucose value appears to be around 75, with the histogram peaking around this value. Despite the few outliers, we will keep this variable in the final model since a patient’s glucose level is a medically important variable which would be helpful to know in order to assess a patient’s odds of being at risk for developing CHD.

2.4.4 cigsPerDay Distribution

cigsPerDay was another variable that had missing observations which were filled in during the previous steps. So, we should check its distribution to make sure things look alright.

ylimit = max(density(heartdisease1$cigsPerDay)$y)
hist(heartdisease1$cigsPerDay, probability = TRUE, main = "cigsPerDay", xlab="cigsPerDay", 
       col = "aliceblue", border="cornflowerblue")
  lines(density(heartdisease1$cigsPerDay, adjust=2), col="darkorchid")

The variable cigsPerDay appears to be very notably skewed to the right. This can be attributed to that this variable represents the number of cigarettes a patient smokes per day. Many of the patients included in the data collection were not smokers, so they would have reported a value of 0 cigarettes smoked per day. This can explain the skew to the right of this variable, because non-smokers would report a value of 0 while smokers would report a value greater than 0 that represents the number of cigarettes they smoke in a day. This is a pratically important variable regardless of its skew, because smoking is a major risk factor for conditions like CHD. Additionally, this is an important piece of information, because doctors would want to know how many cigarettes their patients smoke in a day, because this has a significant impact on the patient’s risk factors. So, we will keep this variable in the final model despite its skew because it is an important variable in regards to a patient’s potential risk factors for developing CHD. But, it is important to keep in mind that this variable is skewed due to it being representative of both non-smokers and smokers.

2.4.5 heartRate Distribution

The variable heartRate was another one which had some missing observations that needed to be filled in during previous steps. We will check its distribution to ensure that everything looks alright.

ylimit = max(density(heartdisease1$heartRate)$y)
hist(heartdisease1$heartRate, probability = TRUE, main = "heartRate", xlab="heartRate", 
       col = "aliceblue", border="cornflowerblue")
  lines(density(heartdisease1$heartRate, adjust=2), col="darkorchid")

The distribution for heart rate is unimodal and approximately normally distributed without any notable skew or outliers that are apparent in its density plot. This allows us to conclude that this variable is safe to include within the multiple logistic regression model building process and that the process of filling in the missing values did not lead to any issues in its distribution.

2.4.6 Overall Findings

We have checked the distributions of the variables which had missing observations that were filled in during the previous steps of this project along with other important quantitative variables in this data set. We ensured that the variables either follows a distribution that appears to be approximately normal, or are practically important enough to be included in the multiple logistic regression model building process otherwise.

The only variable that did not appear to follow an approximately normal distribution was cigsPerDay. However, the skewness seen in this variable’s distribution can be attributed to the fact that this data was collected amongst both smokers and non-smokers. So, non-smokers reported values of 0 cigarettes smoked per day, while smokers reported values that were greater than zero. This appeared to the distribution for this variable appearing to be skewed to the right. However, this is a variable that is practically important because the number of cigarettes a patient smokes per day can have a significant impact on their odds of being at risk for developing CHD. Due to the practical importance of this variable, it must still be included within the multiple logistic regression model building process.

Altogether, we have ensured that the variables in this data set appear to be good enough to use within the model building process. So, now we will begin with the process of building the candidate multiple logistic regression models.

3 Variable Transformation- Standardizing the Quantitative Variables

The main goal for this project is to find a model with the best predictive performance and power. In order to do so, we will standardize the quantitative variables in the data set to ensure that our data will be its best for predictive performance.

We will standardize all of the numeric, quantitative variables in order to give our data the best predictive power for the future model building we will conduct in the following steps.

# Age
heartdisease1$sd.age = (heartdisease1$age-mean(heartdisease1$age))/sd(heartdisease1$age)

# cigsPerDay
heartdisease1$sd.cigsPerDay = (heartdisease1$cigsPerDay-mean(heartdisease1$cigsPerDay))/sd(heartdisease1$cigsPerDay)

# totChol
heartdisease1$sd.totChol = (heartdisease1$totChol-mean(heartdisease1$totChol))/sd(heartdisease1$totChol)

# sysBP
heartdisease1$sd.sysBP = (heartdisease1$sysBP-mean(heartdisease1$sysBP))/sd(heartdisease1$sysBP)

# diaBP
heartdisease1$sd.diaBP = (heartdisease1$diaBP-mean(heartdisease1$diaBP))/sd(heartdisease1$diaBP)

# BMI
heartdisease1$sd.BMI = (heartdisease1$BMI-mean(heartdisease1$BMI))/sd(heartdisease1$BMI)

# heartRate
heartdisease1$sd.heartRate = (heartdisease1$heartRate-mean(heartdisease1$heartRate))/sd(heartdisease1$heartRate)

# glucose
heartdisease1$sd.glucose = (heartdisease1$glucose-mean(heartdisease1$glucose))/sd(heartdisease1$glucose)

We will create a new data object to store the new standardized variables along with all of the binary and categorical predictor variables from the previous data set. We will call the new data set with all of the standardized quantitative variables “sd.heartdisease”.

# New data set with original, non-standardized variables dropped.
sd.heartdisease = select(heartdisease1, -2,-5,-10:-15)

Now, all of the quantitative, numerical variables in the original data set have been standardized in order to ensure that our future model will have the best predictive power because the goal of this project is to create a model with the best predictive power and performance.

4 Data Split

We will now split the data up into training and testing data. We will randomly split the data so that 80% of the data will be used for training and 20% of the data will be used for testing. By splitting the data up into the training and the testing data, this will allow us to build the candidate models and analyze their predictive performances. We will use the split data to conduct the cross validation process to analysis the predictive power and performances of each of the three candidate models we will investigate. This will allow us to eventually determine which of these three models is the ideal choice for our final model based upon its overall predictive power.

# Split the data into 80% training and 20% testing.
n <- dim(sd.heartdisease)[1]
train.n <- round(0.8*n)
train.id <- sample(1:n, train.n, replace = FALSE)

train <- sd.heartdisease[train.id, ]
test <- sd.heartdisease[-train.id, ]

Now, our data has been split up into a training and a testing data set, with 80% of the data for training and the remaining 20% for testing. The observations were split up randomly amongst these two data sets. We will specifically use the training data for the cross validation process. This process will allow us to identify which multiple logistic regression model is the best model for the purpose of prediction. This will allow us to select a model which predicts the odds of an individual being at risk for developing CHD with the highest quality of prediction.

5 Best Model Identification

Now that our variables have been checked and standardized to ensure their predictive performance being the best that it possibly can be, we will begin with the model building process. We will look at three potential, candidate models for the multiple logistic regression and determine which one has the best predictive performance.

5.1 Descriptions of the Three Candidate Models

We will take a look into the specifications of the three candidate multiple logistic regression models.

5.1.1 Full Model

The first of the three models will be the full model, with all of the variables included. This full model will use all of the variables in order to create a multiple logistic regression to predict the odds of a patient being at risk for developing CHD in a 10-year period.

The regression equation for this full model is represented as:

TenYearCHD = male + sd.age + education + currentSmoker + sd.cigsPerDay + BPMeds + prevalentStroke + prevalentHyp + diabetes + sd.totChol + sd.sysBP + sd.diaBP + sd.BMI + sd.heartRate + sd.glucose

We will find the specific multiple logisitic regression equation for this full model.

full.model = glm(TenYearCHD ~ male + sd.age + education + currentSmoker + sd.cigsPerDay + BPMeds + prevalentStroke + prevalentHyp + diabetes + sd.totChol + sd.sysBP + sd.diaBP + sd.BMI + sd.heartRate + sd.glucose, 
          family = binomial(link = "logit"),  
          data = sd.heartdisease)  
kable(summary(full.model)$coef, 
      caption = "Full Model Summary of the Inferential Statistics")
Full Model Summary of the Inferential Statistics
Estimate Std. Error z value Pr(>|z|)
(Intercept) -2.2024269 0.1240704 -17.7514313 0.0000000
male 0.4852112 0.1013449 4.7877196 0.0000017
sd.age 0.5168830 0.0539408 9.5824163 0.0000000
education2 -0.2356171 0.1154925 -2.0401079 0.0413396
education3 -0.1026466 0.1376433 -0.7457430 0.4558227
education4 0.0115144 0.1519726 0.0757666 0.9396048
currentSmoker 0.0219380 0.1443470 0.1519811 0.8792019
sd.cigsPerDay 0.2507207 0.0679008 3.6924543 0.0002221
BPMeds 0.3270416 0.2170889 1.5064870 0.1319422
prevalentStroke 0.9388714 0.4468276 2.1011937 0.0356240
prevalentHyp 0.2311662 0.1287922 1.7948770 0.0726733
diabetes 0.0912436 0.3005700 0.3035687 0.7614565
sd.totChol 0.0803445 0.0457538 1.7560185 0.0790852
sd.sysBP 0.3074739 0.0783167 3.9260304 0.0000864
sd.diaBP -0.0327142 0.0713895 -0.4582495 0.6467732
sd.BMI 0.0071253 0.0482202 0.1477649 0.8825283
sd.heartRate -0.0137062 0.0467007 -0.2934898 0.7691478
sd.glucose 0.1729621 0.0500142 3.4582600 0.0005437

The equation for the multiple logistic regression equation of the full model is given as follows:

log p/(1-p) = -2.2024 + 0.4852 * male + 0.5169 * sd.age - 0.2356 * education2 - 0.1026 * education3 + 0.0115 * education4 + 0.0219 * currentSmoker+ 0.2507 * sd.cigsPerDay + 0.3270 * BPMeds + 0.9389 * prevalentStroke + 0.2312 * prevalentHyp + 0.0912 * diabetes + 0.0803 * sd.totChol + 0.3075 * sd.sysBP - 0.0327 * sd.diaBP + 0.0071 * sd.BMI - 0.0137 * sd.heartRate + 0.1730 * sd.glucose

5.1.2 Reduced Model

The second of the three models will be the reduced model. This model will consist only of the most practically important variables within the context of this situation, using various factors to best predict the odds of an individual being at risk for developing CHD. The United States National Library of Medicine of the National Institutes of Health (NIH), the most notable risk factors for an individual being at risk of developing CHD are cigarette smoking, blood pressure, and cholesterol levels. In this data set, there are two variables related to smoking, currentSmoker, whether a patient is currently a smoker or not, and cigsPerDay which is was scaled to sd.cigsPerDay, how many cigarettes a patient smokes in a day. There are also two variables related to blood pressure, sysBP and diaBP, for systolic blood pressure and diastolic blood pressure. Both of these blood pressure variables were scaled and are now given as sd.sysBP and sd.diaBP. There is also a variable included in the data set for a patient’s total cholesterol levels, totChol, which was scaled and is now given as sd.totChol. These five variables in the data set represent the key risk factors that the medical report from the NIH stated as being the most significant in predicting a patient’s risk for CHD. So, our reduced model will consist only of these particular variables.

The general regression equation for this reduced model is represented as:

TenYearCHD = currentSmoker + sd.cigsPerDay + sd.sysBP + sd.diaBP + sd.totChol

We will find the specific multiple logistic regression equation for this reduced model.

reduced.model = glm(TenYearCHD ~ currentSmoker + sd.cigsPerDay + sd.sysBP + sd.diaBP + sd.totChol, 
          family = binomial(link = "logit"),
          data = sd.heartdisease) 
kable(summary(reduced.model)$coef, 
      caption = "Reduced Model Summary of the Inferential Statistics")
Reduced Model Summary of the Inferential Statistics
Estimate Std. Error z value Pr(>|z|)
(Intercept) -1.7675361 0.0822320 -21.494511 0.0000000
currentSmoker -0.1443498 0.1392986 -1.036262 0.3000800
sd.cigsPerDay 0.2683287 0.0637373 4.209920 0.0000255
sd.sysBP 0.6381074 0.0641837 9.941893 0.0000000
sd.diaBP -0.1409295 0.0650918 -2.165088 0.0303809
sd.totChol 0.1113871 0.0431866 2.579205 0.0099028

The equation for the multiple logistic regression equation of the reduced model is given as follows:

log p/(1-p) = -1.7675 - 0.1443 * currentSmoker + 0.2683 * sd.cigsPerDay + 0.6381 * sd.sysBP - 0.1409 * sd.diaBP + 0.1114 * sd.totChol

5.1.3 Forward Selection Model

The third of three candidate models will be the model that was found through the automatic variable selection process, specifically with using forward selection. We will call this the fs.model for this project to represent it being the model found through the use of forward selection of the variables.

We will find the specific multiple logistic regression equation for this forward selection model.

fs.model = stepAIC(reduced.model, scope = list(lower=formula(reduced.model), upper=formula(full.model)),
                      direction = "forward",  
                      trace = 0   
                      )
kable(summary(fs.model)$coef, 
      caption="Final Model Summary of the Inferential Statistics")
Final Model Summary of the Inferential Statistics
Estimate Std. Error z value Pr(>|z|)
(Intercept) -2.2897777 0.1108872 -20.6496069 0.0000000
currentSmoker 0.0083694 0.1431896 0.0584499 0.9533902
sd.cigsPerDay 0.2497211 0.0677217 3.6874613 0.0002265
sd.sysBP 0.3080000 0.0777695 3.9604228 0.0000748
sd.diaBP -0.0324529 0.0698694 -0.4644798 0.6423040
sd.totChol 0.0755135 0.0454483 1.6615237 0.0966083
sd.age 0.5363993 0.0527922 10.1605849 0.0000000
male 0.5164091 0.0993494 5.1979103 0.0000002
sd.glucose 0.1825977 0.0373944 4.8830291 0.0000010
prevalentStroke 0.9413243 0.4425788 2.1269078 0.0334277
prevalentHyp 0.2310484 0.1281164 1.8034261 0.0713213
BPMeds 0.3233004 0.2160729 1.4962560 0.1345870

The equation for the multiple logistic regression equation of the forward selection model is given as follows:

log p/(1-p) = -2.2898 + 0.0084 * currentSmoker + 0.2497 * sd.cigsPerDay + 0.2497 * sd.sysBP - 0.0325 * sd.diaBP + 0.0755 * sd.totChol + 0.5364 * sd.age + 0.5164 * male + 0.1826 * sd.glucose + 0.9413 * prevalentStroke + 0.2310 * prevalentHyp + 0.3233 * BPMeds

The variables that were kept in this model after forward selection were currentSmoker, sd.cigsPerDay, sd.sysBP, sd.diaBP, sd.totChol, sd,age, male, sd.glucose, prevalentStroke, prevalentHyp, and BPMeds.

5.2 Cross Validation

Now that we’ve looked into the specifications of the three potential candidate models, we will use cross-validation to determine which of these three models we should use. We want to see which model has the best predictive performance. We will use a 5-fold cross validation to conduct the process of finding the predictive errors for each of the three candidate models. We will use this 5-fold cross validation process to calculate the average predictive errors for each of the three candidate models, the full model, the reduced model, and the forward selection model. We are looking for which model has the lowest value of average predictive errors. This model with the lowest value for the average predictive errors will be the ideal choice, as having the lowest predictive error value indicates a higher predictive power and more accurate predictions than a model with higher average predictive error would.

For our cross validation process, we will use a cut off probability of 0.5 to define between the binary response values of 1, yes, and 0, no. These values represent whether an individual will be stated as being at risk for developing CHD in a 10-year period based upon the multiple logistic regression model that was used for prediction.

We will label the full model as our first model (01), the reduced model as our second model (02), and the forward selection model as our third model (03) for this cross-validation process.

## 5 Fold Cross Validation
k = 5
fold.size = floor(dim(train)[1]/k)
PE1 = rep(0,5)
PE2 = rep(0,5)
PE3 = rep(0,5)

# Training and Testing Fold for the Models
for(i in 1:k){
  
 valid.id = (fold.size*(i-1)+1):(fold.size*i)
  valid = train[valid.id, ]
  train.data = train[-valid.id,]
  
# Full Model: all variables
full.model = glm(TenYearCHD ~ male + sd.age + education + currentSmoker +  sd.cigsPerDay + BPMeds + prevalentStroke + prevalentHyp + diabetes + sd.totChol + sd.sysBP + sd.diaBP + sd.BMI + sd.heartRate + sd.glucose, 
          family = binomial(link = "logit"),  
          data = train.data)

## Reduced Model: only the most practically important variables.
reduced.model = glm(TenYearCHD ~ currentSmoker + sd.cigsPerDay + sd.sysBP + sd.diaBP + sd.totChol, 
          family = binomial(link = "logit"),
          data = train.data) 

## Automatic Forward Selection (fs) Model: model found using forward selection
fs.model = stepAIC(full.model, 
                      scope = list(lower=formula(reduced.model),   
                      upper=formula(full.model)),
                      direction = "forward",  
                      trace = 0   
                      )

# Finding the predicted probabilities for each of the candidate models
   pred.full = predict(full.model, newdata = valid, type = "response")
   pred.reduced = predict(reduced.model, newdata = valid, type = "response")
   pred.fs = predict(fs.model, newdata = valid, type = "response")
   
   pre.outcome01 = ifelse(as.vector(pred.full) > 0.5, "1", "0")
   pre.outcome02 = ifelse(as.vector(pred.reduced) > 0.5, "1", "0")
   pre.outcome03 = ifelse(as.vector(pred.fs) > 0.5, "1", "0")
   
   PE1[i] = sum(pre.outcome01 == valid$TenYearCHD)/length(pred.full)
   PE2[i] = sum(pre.outcome02 == valid$TenYearCHD)/length(pred.reduced)
   PE3[i] = sum(pre.outcome03 == valid$TenYearCHD)/length(pred.fs)
}

# Finding the average predictive errors of each candidate model  
avg.pe = cbind(PE1 = mean(PE1), PE2 = mean(PE2), PE3 = mean(PE3))
kable(avg.pe, caption = "Average Predictive Errors of the Three Candidate Models") 
Average Predictive Errors of the Three Candidate Models
PE1 PE2 PE3
0.8545723 0.8495575 0.8545723

The average predictive errors of the three candidate models are given as follows:

The full model, represented by PE1, had an average predictive error of 0.8546. The reduced model, represented by PE2, had an average predictive error of 0.8495.

The forward selection model, represented by PE3, had an average predictive error of 0.8546.

We can see that both the full model and the forward selection model had equal values for their average predictive errors, with both having a value of 0.8546. However, the reduced model a lower average predictive error value than the other two models, with a value of 0.8496. This indicates that the reduced model is the one which we should choose as our final model.

The cross-validation process of calculating the average predictive errors for the three candidate models showed that the reduced model is the ideal one to use for prediction due to it having the lowest value of its average predictive error out of the three candidate models. This makes the reduced model have the highest predictive power and performance out of the three candidate models, as shown by how it has the least amount of its average predictive errors in comparison to the full model and the forward selection model. So, we will move forward with the use of the reduced model as our final multiple logistic regression model for prediction of the odds of an individual being at risk for developing CHD.

5.3 Final Model

In the previous cross-validation step, we used a cut off probability of 0.5 to identify between the response variable giving a positive or negative result. In this case, a “positive” result, or an output of 1, represents that an individual is at risk for developing CHD in a 10-year period. And, a “negative” result, or an output of 0, represents that an individual is not at risk for developing CHD in a 10-year period. We used the cross-validation process with this cut off probability of 0.5 to find the values of the average predictive errors of the three candidate models. We found that the reduced model was the one with the best predictive performance, so we will use that as our final model.

However, in order to report the details of the final model, we must find its actual accuracy based upon the test data. We previously split the data up into a training and a testing data set. We will use the test data set to find the actual accuracy of the final model, the reduced model, in order to provide the fullest accuracy in the report of this model.

pred.reduced = predict(reduced.model, newdata = test, type = "response")
pred.reduced.outcome = ifelse(as.vector(pred.reduced) > 0.5, "1", "0")

Accuracy = sum(pred.reduced.outcome == test$TenYearCHD)/length(pred.reduced)
kable(Accuracy, caption="Actual Accuracy of the Final Model (Reduced Model)")
Actual Accuracy of the Final Model (Reduced Model)
x
0.8443396

The actual accuracy of the final model, the reduced model, is given as 0.8443. This value represents the accuracy rate of this final model.

It appears that the final model has good accuracy as seen by its accuracy rate of 0.8443. This indicates that the final model we selected has good predictive performance for predicting the odds of an individual being at risk for developing CHD within a 10-year period.

6 ROC Analysis

We will use ROC analysis to measure the global performance of the multiple logistic regression model. This plot will illustrate the relationship of the true positive rate (TPR), also known as the sensitivity, against the false positive rate (FPR), also given as 1 - specificity. We will plot the ROC curves for each of the three candidate models to compare them against one another. We will also calculate the area under the curve (AUC) values for each of the three candidate models. The value of area under the curve represents the global goodness of the model, and a higher AUC value is ideal. A higher value of the area under the curve indicates better and more quality global goodness of that model.

We will plot the ROC curves for the full model, the reduced model, and the forward selection model. This will allow us to compare the area under the curves for each of the three models. Additionally, we will calculate the AUC values for each of the three models to compare them with each other and determine which model indicates the best global goodness.

TPR.FPR=function(pred){
  prob.seq = seq(0, 1, length=50)  
  pn = length(prob.seq)
  true.lab = as.vector(train$TenYearCHD)
  TPR = NULL
  FPR = NULL
  for (i in 1:pn){
   pred.lab = as.vector(ifelse(pred > prob.seq[i],"1", "0"))
   TPR[i] = length(which(true.lab=="1" & pred.lab=="1"))/length(which(true.lab=="1"))
   FPR[i] = length(which(true.lab=="0" & pred.lab=="1"))/length(which(true.lab=="0"))
  }
 cbind(FPR = FPR, TPR = TPR)
}

We will use the training data set to help find the predictive probabilities for each of the three candidate models.

# The Full Model
full.model = glm(TenYearCHD ~male + sd.age + education + currentSmoker +  sd.cigsPerDay + BPMeds + prevalentStroke + prevalentHyp + diabetes + sd.totChol + sd.sysBP + sd.diaBP + sd.BMI + sd.heartRate + sd.glucose, 
          family = binomial(link = "logit"),  
          data = train)

# The Reduced Model (final model)
reduced.model = glm(TenYearCHD ~ currentSmoker + sd.cigsPerDay + sd.sysBP + sd.diaBP + sd.totChol, 
          family = binomial(link = "logit"),
          data = train) 

# The Forward Selection (fs) Model
fs.model = stepAIC(full.model, 
                      scope = list(lower=formula(reduced.model),   
                      upper=formula(full.model)),
                      direction = "forward",  
                      trace = 0   
                      )

# Finding the Predicted Probabilities for Each Model
pred.full = predict.glm(full.model, newdata = train, type = "response") 
pred.reduced = predict.glm(reduced.model, newdata = train, type = "response")
pred.fs = predict.glm(fs.model, newdata = train, type = "response")

Now, we will plot the ROC curves for each of the three candidate models to compare them.

# Plotting the ROC curves
 plot(TPR.FPR(pred.full)[,1], TPR.FPR(pred.full)[,2], 
      type="l", col = 2, lty = 1, xlim = c(0,1), ylim = c(0,1),
      xlab = "FPR: 1 - Specificity",
      ylab ="TPR: Sensitivity",
      main = "ROC Curves for the Three Candidate Models",
      cex.main = 0.8,
      col.main = "navy")
 lines(TPR.FPR(pred.reduced)[,1], TPR.FPR(pred.reduced)[,2],  col=3, lty=2)
 lines(TPR.FPR(pred.fs)[,1], TPR.FPR(pred.fs)[,2],  col=4, lty=3)    
 
  category = train$TenYearCHD == "1"
  ROCobj01 <- roc(category, as.vector(pred.full))
  ROCobj02 <- roc(category, as.vector(pred.reduced))
  ROCobj03 <- roc(category, as.vector(pred.fs))
  AUC01 = round(auc(ROCobj01),4)
  AUC02 = round(auc(ROCobj02),4)
  AUC03 = round(auc(ROCobj03),4)
  
  legend("bottomright", c(paste("Full model: AUC = ", AUC01), 
                         paste("Reduced model: AUC =", AUC02),
                         paste("Forward Selection model: AUC =", AUC03)),
        col = 2:4, lty = 1:3, cex = 0.8, bty = "n")

We can see that both the full model and the forward selection model fall on the exact same curve, which is also evidenced by how these two models have the same values for their area under the curve Both the full model and the forward selection model have an area under the curve of 0.7356, and their ROC curves match up identically to each other. This matches up with the previous finding of the full model and the forward selection model having the exact same values for their average predictive errors.

Something interesting found in this ROC analysis, is that the reduced model actually has the smallest value of area under the curve with 0.6763. We found that the reduced model was the one with the lowest average predictive errors previously, which made it the ideal model choice for our final model. However, this model turned out to have the lowest area under the curve out of the three, which is not ideal. A model with a higher area under the curve has a better global goodness than a model with a lower area under the curve.

Solely based upon area under the curve values found in the ROC analysis, it would appear that the forward selection model provides the best global goodness. Both the full model and the forward selection model had the same value for their area under the curve, but the forward selection model would be the better choice due to it having less variables.

The ROC analysis revealed that the forward selection model may in fact be a good alternative to use as the final model due to it having a higher area under the curve than the reduced model, despite the reduced model having a lower average value of predictive errors as was found earlier. A potential reason for the forward selection model having a higher AUC than the reduced model despite the reduced model having a lower and more ideal average value of predictive errors could be due to the reduced model having less variables. It could be a possibility that the reduced model having fewer variables could have a higher likelihood of false positivies or false negatives due to some of the variables which may be significant not having been included in the reduced model.

7 Conclusion

In this project, three candidate models were created to compare their predictive powers and ultimately choose a final model that provides the best significance and quality of prediction for predicting the odds of an individual being at risk for developing CHD in a 10-year period. These models all consisted of multiple logistic regression to predict the binary response variable of an individual’s odds of being at risk for developing CHD. Creating a model for the purpose of this prediction can provide use for doctors and patients in order to predict their odds of being at risk for developing CHD based upon various personal and medical factors.

The three candidate models that were created included a full model with all of the variables in the data set, a reduced model with only the variables deemed as the most practically important, and a model created through forward variable selection. These three models were compared by their predictive power and quality for their potential use as a final multiple logistic regression model for this project.

The average predictive errors of all three candidate models were compared to assess their predictive powers through the process of cross-validation. It was found that the full model and the forward selection model had identical values for the average predictive errors. However, the reduced model turned out to have the lowest value of its average predictive errors out of the three candidate models. This indicated that the reduced model was the ideal choice due to it having the lowest value of average predictive errors, and therefore the highest quality of predictive power. This indicated that the reduced model would be a good choice for the final model due to its high quality predictive power shown by how it had the smallest value of average predictive errors out of all three candidate models.

Furthermore, ROC analysis was conducted to continue the analysis of the three candidate models. It was found that the full model and the forward selection model have identical ROC curves and area under the curve values. This matched up with how it was previously found that the full model and the forward selection model have the exact same average value of their predictive errors. Between these two models, we would choose the forward selection model over the full model due to it having less variables. However, we also found that the reduced model in fact had the lowest area under the curve value out of the three models. This is interesting as it contradicts with the reduced model previously being shown to have the lowest average value of predictive errors. It turns out that despite the reduced model having the lowest average of predictive errors and therefore the highest predictive power, it also has the lowest area under the curve meaning that it has lower global goodness out of the candidate models.

Overall, it was found that through cross-validation it turned out that the reduced model had the lowest average value of predictive errors, and therefore the highest predictive power. This made the reduced model the ideal choice for the selection of our final model. This reduced model focused on only the most practically important variables for predicting the odds of an individual being at risk for developing CHD. These most practically important variables included currentSmoker, sd.cigsPerDay, sd.sysBP, sd.diaBP, and sd.totChol. However, it was later found through ROC analysis that the reduced model had the lowest area under the curve value, indicating a lower global goodness. It was in fact the forward selection model that the ROC analysis suggested as being the ideal choice based upon its global goodness. The forward selection model included the variables of currentSmoker, sd.cigsPerDay, sd.sysBP, sd.diaBP, sd.totChol, sd,age, male, sd.glucose, prevalentStroke, prevalentHyp, and BPMeds.

Even though the reduced model provided the best predictive power due to its lower value of average predictive errors, the forward selection model was shown through ROC analysis to have the highest global goodness. Both the reduced model and the full model are high quality models for the prediction and estimation of the odds of an individual being at risk for developing CHD. This discrepancy between the reduced model having the highest predictive power yet the lowest global goodness could in fact be due to it having the fewest variables. By having fewer variables, this could potentially lead to higher rates of false positives and false negatives than the other models with more variables accounted for. So overall, the reduced model would be the ideal choice as the final model for a model with the highest predictive power, which is what we were looking for in this project. However, it is important to keep in mind that this reduced model has a lower global goodness than the forward selection model, likely due to the reduced model having fewer variables and potentially a higher rate for false positives and false negatives. This is important to keep in mind when creating a model for the prediction of an individual’s odds of being at risk for developing CHD, as high predictive power is ideal for ensuring that patient’s health is accounted for properly, but it is also important to be wary of the potential for false postives and false negatives in regards of these predictions.

7.1 Recommendations

Some recommendations I would make for future projects include:

  • Expand the data collection to ensure the accuracy of the findings found in this project. This could also involve reaching out to various hospitals and doctors for a better understanding of the most serious risk factors for CHD in order to ensure that the variables kept within the final model indeed are representative of a patient’s odds of being at risk for developing CHD.

  • Consider other variables which could be statistically significant in predicting the odds of a patient’s risk for developing CHD. All of the predictor variables that were included within this data set made sense in terms of this project and were all factors which could play a role in a patient’s odds of being at risk for developing CHD. However, future projects could consider looking into additional variables that may be statistically significant in predicting the odds of a patient being at risk for developing CHD. For instance, one potential variable that could be looked into is a patient’s income to see if the income of a patient has an impact on their odds of being at risk for developing CHD. A reason why I believe that this could be a significant variable for this model is because patients with higher incomes tend to have more options when it comes to medical care than patients with lower incomes would. So, perhaps there could be a relationship between a patient’s income and their odds of being at risk for developing CHD. This is just one example of a variable that is not in this data set which may provide use for future projects.

  • Consider other possible models that could provide quality predictive power in predicting the odds of a patient being at risk for developing CHD within a 10-year period of time. In this project, we looked at three candidate multiple logistic regression models and chose which one provided the best utility. These three models included a full model with all of the predictor variables left in the model, a reduced model with only the variables that the medical report described as most significant risk factors for CHD, and a final model that was created through automatic forward variable selection. We found that the reduced model was the one which provided the lowest average value for predictive errors out of the three models, and so we chose this to be our final model due to it providing the strongest predictive power. However, this is not to say that there is not any other possible model that could potentially be better than the one we created. If further projects found a model that provided better and more power in prediction than our final model, this could help doctors and patients better understand their odds of being at risk for developing CHD based on the various factors included in the model.

  • Further investigate the topic of false postives and false negatives amongst the predicted values. The topic of false postives and false negatives was addressed as potentially being a reason for the descrepancy between the reduced model having the greatest predictive power, yet also having the lowest global goodness. Having fewer varaibles could possibly lead to some factors being unaccounted for, and potentially resulting in some false positive or negative predictions. This is a potential topic which could be further looked into in further experiments to fully understand the occurences of false positive or negative predictions.

Overall, this project showed that the reduced model resulted in the lowest average predictive errors out of the three models, meaning this was the model with the highest predictive power. However, ROC analysis showed that the reduced model had a lower area under the curve model, and it was the forward selection model which had the highest global goodness as indicated by the ROC analysis. This is something important, because although the reduced model is an ideal choice for the final model due to its high quality of predictive power, this lower global goodness is an important thing to keep in mind when using this final model for the prediction of an individual’s odds of being at risk for developing CHD in a 10-year period.

8 References

This data set was found on kaggle.com under the collection of logistic regression data sets. Included below is the citation of the web page of where I found the data set I used for my project along with the medical report I found which described which factors are the most medically significant when it comes to determining a patient’s risk for developing CHD.

Dileep. (2019, June 7). Logistic regression to predict heart disease. Kaggle. https://www.kaggle.com/datasets/dileep070/heart-disease-prediction-using-logistic-regression?resource=download&select=framingham.csv

Hajar, R. (2017). Risk factors for coronary artery disease: Historical perspectives. Heart views : the official journal of the Gulf Heart Association. https://pmc.ncbi.nlm.nih.gov/articles/PMC5686931/

LS0tDQp0aXRsZTogIlByZWRpY3RpbmcgYSBQYXRpZW50J3MgT2RkcyBvZiBCZWluZyBhdCBSaXNrIGZvciBEZXZlbG9waW5nIENIRC0gQmluYXJ5IFByZWRpY3RpdmUgTW9kZWxpbmciDQphdXRob3I6ICJKb3NpZSBHYWxsb3AiDQpkYXRlOiAiMjAyNC0xMC0yMSINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDogDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZGVwdGg6IDQNCiAgICB0b2NfZmxvYXQ6IHllcw0KICAgIGZpZ193aWR0aDogNg0KICAgIGZpZ19jYXB0aW9uOiB5ZXMNCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcw0KICAgIHRvY19jb2xsYXBzZWQ6IHllcw0KICAgIGNvZGVfZm9sZGluZzogaGlkZQ0KICAgIGNvZGVfZG93bmxvYWQ6IHllcw0KICAgIHNtb290aF9zY3JvbGw6IHllcw0KICAgIHRoZW1lOiBsdW1lbg0KICB3b3JkX2RvY3VtZW50OiANCiAgICB0b2M6IHllcw0KICAgIHRvY19kZXB0aDogNA0KICAgIGZpZ19jYXB0aW9uOiB5ZXMNCiAgICBrZWVwX21kOiB5ZXMNCiAgcGRmX2RvY3VtZW50OiANCiAgICB0b2M6IHllcw0KICAgIHRvY19kZXB0aDogNA0KICAgIGZpZ19jYXB0aW9uOiB5ZXMNCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcw0KZWRpdG9yX29wdGlvbnM6IA0KICBjaHVua19vdXRwdXRfdHlwZTogY29uc29sZQ0KLS0tDQoNCmBgYHs9aHRtbH0NCg0KPHN0eWxlIHR5cGU9InRleHQvY3NzIj4NCg0KLyogQ2FzY2FkaW5nIFN0eWxlIFNoZWV0cyAoQ1NTKSBpcyBhIHN0eWxlc2hlZXQgbGFuZ3VhZ2UgdXNlZCB0byBkZXNjcmliZSB0aGUgcHJlc2VudGF0aW9uIG9mIGEgZG9jdW1lbnQgd3JpdHRlbiBpbiBIVE1MIG9yIFhNTC4gaXQgaXMgYSBzaW1wbGUgbWVjaGFuaXNtIGZvciBhZGRpbmcgc3R5bGUgKGUuZy4sIGZvbnRzLCBjb2xvcnMsIHNwYWNpbmcpIHRvIFdlYiBkb2N1bWVudHMuICovDQoNCmgxLnRpdGxlIHsgIC8qIFRpdGxlIC0gZm9udCBzcGVjaWZpY2F0aW9ucyBvZiB0aGUgcmVwb3J0IHRpdGxlICovDQogIGZvbnQtc2l6ZTogMjRweDsNCiAgY29sb3I6IERhcmtSZWQ7DQogIHRleHQtYWxpZ246IGNlbnRlcjsNCiAgZm9udC1mYW1pbHk6ICJHaWxsIFNhbnMiLCBzYW5zLXNlcmlmOw0KfQ0KaDQuYXV0aG9yIHsgLyogSGVhZGVyIDQgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciBhdXRob3JzICAqLw0KICBmb250LXNpemU6IDIwcHg7DQogIGZvbnQtZmFtaWx5OiBzeXN0ZW0tdWk7DQogIGNvbG9yOiBEYXJrUmVkOw0KICB0ZXh0LWFsaWduOiBjZW50ZXI7DQp9DQpoNC5kYXRlIHsgLyogSGVhZGVyIDQgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciB0aGUgZGF0ZSAgKi8NCiAgZm9udC1zaXplOiAxOHB4Ow0KICBmb250LWZhbWlseTogc3lzdGVtLXVpOw0KICBjb2xvcjogRGFya0JsdWU7DQogIHRleHQtYWxpZ246IGNlbnRlcjsNCn0NCmgxIHsgLyogSGVhZGVyIDEgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciBsZXZlbCAxIHNlY3Rpb24gdGl0bGUgICovDQogICAgZm9udC1zaXplOiAyMnB4Ow0KICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOw0KICAgIGNvbG9yOiBuYXZ5Ow0KICAgIHRleHQtYWxpZ246IGNlbnRlcjsNCn0NCmgyIHsgLyogSGVhZGVyIDIgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciBsZXZlbCAyIHNlY3Rpb24gdGl0bGUgKi8NCiAgICBmb250LXNpemU6IDIwcHg7DQogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogICAgY29sb3I6IG5hdnk7DQogICAgdGV4dC1hbGlnbjogbGVmdDsNCn0NCg0KaDMgeyAvKiBIZWFkZXIgMyAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgb2YgbGV2ZWwgMyBzZWN0aW9uIHRpdGxlICAqLw0KICAgIGZvbnQtc2l6ZTogMThweDsNCiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgICBjb2xvcjogbmF2eTsNCiAgICB0ZXh0LWFsaWduOiBsZWZ0Ow0KfQ0KDQpoNCB7IC8qIEhlYWRlciA0IC0gZm9udCBzcGVjaWZpY2F0aW9ucyBvZiBsZXZlbCA0IHNlY3Rpb24gdGl0bGUgICovDQogICAgZm9udC1zaXplOiAxOHB4Ow0KICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOw0KICAgIGNvbG9yOiBkYXJrcmVkOw0KICAgIHRleHQtYWxpZ246IGxlZnQ7DQp9DQoNCmJvZHkgeyBiYWNrZ3JvdW5kLWNvbG9yOndoaXRlOyB9DQoNCi5oaWdobGlnaHRtZSB7IGJhY2tncm91bmQtY29sb3I6eWVsbG93OyB9DQoNCnAgeyBiYWNrZ3JvdW5kLWNvbG9yOndoaXRlOyB9DQoNCjwvc3R5bGU+DQpgYGANCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0KIyBEZXRlY3QsIGluc3RhbGwsIGFuZCBsb2FkIHBhY2thZ2VzIGlmIG5lZWRlZC4NCmlmICghcmVxdWlyZSgia25pdHIiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygia25pdHIiKQ0KICAgbGlicmFyeShrbml0cikNCn0NCmlmICghcmVxdWlyZSgibGVhZmxldCIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJsZWFmbGV0IikNCiAgIGxpYnJhcnkobGVhZmxldCkNCn0NCmlmICghcmVxdWlyZSgiRW52U3RhdHMiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygiRW52U3RhdHMiKQ0KICAgbGlicmFyeShFbnZTdGF0cykNCn0NCmlmICghcmVxdWlyZSgiTUFTUyIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJNQVNTIikNCiAgIGxpYnJhcnkoTUFTUykNCn0NCmlmICghcmVxdWlyZSgicGh5dG9vbHMiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygicGh5dG9vbHMiKQ0KICAgbGlicmFyeShwaHl0b29scykNCn0NCmlmKCFyZXF1aXJlKCJkcGx5ciIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJkcGx5ciIpDQogICBsaWJyYXJ5KGRwbHlyKQ0KfQ0KaWYoIXJlcXVpcmUoInRpZHl2ZXJzZSIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dmVyc2UiKQ0KICAgbGlicmFyeSh0aWR5dmVyc2UpDQp9DQppZighcmVxdWlyZSgiR0dhbGx5IikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoIkdHYWxseSIpDQogICBsaWJyYXJ5KEdHYWxseSkNCn0NCmlmKCFyZXF1aXJlKCJ1c2RtIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoInVzZG0iKQ0KICAgbGlicmFyeSh1c2RtKQ0KfQ0KaWYoIXJlcXVpcmUoImNhciIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJjYXIiKQ0KICAgbGlicmFyeShjYXIpDQp9DQppZiAoIXJlcXVpcmUoImJvb3QiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygiYm9vdCIpDQogICBsaWJyYXJ5KGJvb3QpDQp9DQppZighcmVxdWlyZSgicGFuZGVyIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoInBhbmRlciIpDQogICBsaWJyYXJ5KHBhbmRlcikNCn0NCmlmKCFyZXF1aXJlKCJtaWNlIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoIm1pY2UiKQ0KICAgbGlicmFyeShtaWNlKQ0KfQ0KaWYoIXJlcXVpcmUoIm1sYmVuY2giKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygibWxiZW5jaCIpDQogICBsaWJyYXJ5KG1sYmVuY2gpDQp9DQppZighcmVxdWlyZSgicHN5Y2giKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygicHN5Y2giKQ0KICAgbGlicmFyeShwc3ljaCkNCn0NCmlmKCFyZXF1aXJlKCJicm9vbS5taXhlZCIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJicm9vbS5taXhlZCIpDQogICBsaWJyYXJ5KGJyb29tLm1peGVkKQ0KfQ0KaWYoIXJlcXVpcmUoIkdHYWxseSIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJHR2FsbHkiKQ0KICAgbGlicmFyeShHR2FsbHkpDQp9DQppZighcmVxdWlyZSgiY2FyZXQiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygiY2FyZXQiKQ0KICAgbGlicmFyeShjYXJldCkNCn0NCmlmICghcmVxdWlyZSgicFJPQyIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJwUk9DIikNCiAgIGxpYnJhcnkocFJPQykNCn0NCiMgU3BlY2lmaWNhdGlvbnMgb2Ygb3V0cHV0cyBvZiBjb2RlIGluIGNvZGUgY2h1bmtzDQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsICAjIGluY2x1ZGUgY29kZSBjaHVuayBpbiB0aGUgb3V0cHV0ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGUNCiAgICAgICAgICAgICAgICAgICB3YXJuaW5nID0gRkFMU0UsICAjIFNvbWV0aW1lcywgeW91ciBjb2RlIG1heSBwcm9kdWNlIGEgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHdhcm5pbmcNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIG1lc3NhZ2VzLCB5b3UgY2FuIGNob29zZSB0byBpbmNsdWRlICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRoZQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgd2FybmluZyBtZXNzYWdlcyBpbiB0aGUgb3V0cHV0IGZpbGUuIA0KICAgICAgICAgICAgICAgICAgIG1lc3NhZ2UgPSBGQUxTRSwgIA0KICAgICAgICAgICAgICAgICAgIHJlc3VsdHMgPSBUUlVFLCAgICMgeW91IGNhbiBhbHNvIGRlY2lkZSB3aGV0aGVyIHRvICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW5jbHVkZSANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHRoZSBvdXRwdXQgaW4gdGhlIG91dHB1dCBmaWxlLg0KICAgICAgICAgICAgICAgICAgIGNvbW1lbnQgPSBOQSAgICAgICMgU3VwcHJlc3MgaGFzaC10YWdzIGluIHRoZSBvdXRwdXQgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVzdWx0cy4NCiAgICAgICAgICAgICAgICAgICAgICApICAgDQpgYGANCg0KDQojIEludHJvZHVjdGlvbg0KDQpGb3IgdGhpcyBwcm9qZWN0LCB3ZSB3aWxsIGJlIHdvcmtpbmcgb24gY3JlYXRpbmcgYSBtdWx0aXBsZSBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsLiBUaGUgZGF0YSBzZXQgZm9yIHRoaXMgcHJvamVjdCBsb29rcyBhdCB0aGUgdmFyaW91cyBmYWN0b3JzIGFmZmVjdGluZyBhbiBpbmRpdmlkdWFsIHBhdGllbnQncyByaXNrIG9mIGRldmVsb3BpbmcgY29yb25hcnkgaGVhcnQgZGlzZWFzZSAoQ0hEKSBpbiBhIDEwLXllYXIgcGVyaW9kIG9mIHRpbWUuIFRoZSByZXNwb25zZSB2YXJpYWJsZSBpbiB0aGlzIGRhdGEgc2V0IGlzIGJpbmFyeSB3aXRoIDEgcmVwcmVzZW50aW5nIHllcywgdGhlIHBhdGllbnQgaXMgYXQgcmlzayBvZiBkZXZlbG9waW5nIENIRCB3aXRoaW4gYSAxMC15ZWFyIHBlcmlvZCBvZiB0aW1lLCBhbmQgMCByZXByZXNlbnRpbmcgbm8sIHRoZSBwYXRpZW50IGlzIG5vdCBhdCByaXNrIG9mIGRldmVsb3BpbmcgQ0hEIHdpdGhpbiBhIDEwLXllYXIgcGVyaW9kIG9mIHRpbWUuDQoNCk91ciBvdmVyYWxsIGdvYWwgaW4gdGhpcyBwcm9qZWN0IGlzIHRvIGNyZWF0ZSBhIG1vZGVsIHdoaWNoIGhhcyB0aGUgZ3JlYXRlc3QgcHJlZGljdGl2ZSBwZXJmb3JtYW5jZS4gV2Ugd2lsbCBjcmVhdGUgc2V2ZXJhbCBjYW5kaWRhdGUgbW9kZWxzIGFuZCBjb21wYXJlIHRoZW0gd2l0aCBlYWNoIG90aGVyIHRvIGRldGVybWluZSB3aGljaCBtb2RlbCBzaG91bGQgYmUgc2VsZWN0ZWQgYXMgdGhlIGZpbmFsIG1vZGVsIGZvciB0aGUgcHJlZGljdGlvbiBvZiBhbiBpbmRpdmlkdWFsJ3Mgb2RkcyBvZiBiZWluZyBhdCByaXNrIGZvciBkZXZlbG9waW5nIENIRCBvdmVyIGEgMTAteWVhciBwZXJpb2Qgb2YgdGltZS4gV2Ugd2lsbCB1c2UgdGhlIGNyb3NzLXZhbGlkYXRpb24gcHJvY2VzcyB0byBkZXRlcm1pbmUgd2hpY2ggb2YgdGhlIGNhbmRpZGF0ZSBtb2RlbHMgcHJvdmlkZXMgdGhlIGJlc3QgcHJlZGljdGl2ZSBwb3dlci4gV2Ugd2lsbCBhbHNvIHVzZSBST0MgYW5hbHlzaXMgdG8gZGV0ZXJtaW5lIHdoaWNoIG9mIHRoZSBjYW5kaWRhdGUgbW9kZWxzIGhhcyB0aGUgYmVzdCBnbG9iYWwgZ29vZG5lc3MuIFRoaXMgd2lsbCBhbGxvdyB1cyB0byBzZWUgd2hpY2ggb2YgdGhlIGNhbmRpZGF0ZSBtb2RlbHMgcHJvdmlkZXMgdGhlIGJlc3QgdXRpbGl0eSBhbmQgcXVhbGl0eSBmb3IgcHJlZGljdGl2ZSBtb2RlbGluZy4gDQoNCg0KDQojIyBEYXRhIERlc2NyaXB0aW9uDQoNCkkgZm91bmQgdGhpcyBkYXRhIHNldCBvbiBrYWdnbGUuY29tIG9uIHRoZSBmb2xsb3dpbmcgd2ViIHBhZ2U6DQpodHRwczovL3d3dy5rYWdnbGUuY29tL2RhdGFzZXRzL2RpbGVlcDA3MC9oZWFydC1kaXNlYXNlLXByZWRpY3Rpb24tdXNpbmctbG9naXN0aWMtcmVncmVzc2lvbi9kYXRhDQoNClRoaXMgZGF0YSBzZXQgdGFrZXMgYSBsb29rIGF0IHZhcmlvdXMgbWVkaWNhbCBhbmQgcGVyc29uYWwgZmFjdG9ycyB3aGljaCBtYXkgaGF2ZSBhbiBpbXBhY3Qgb24gYW4gaW5kaXZpZHVhbCdzIHJpc2sgb2YgZGV2ZWxvcGluZyBDSEQgd2l0aGluIHRoZSBuZXh0IDEwIHllYXJzLiBUaGUgYmluYXJ5IHJlc3BvbnNlIHZhcmlhYmxlIHRlbGxzIHdoZXRoZXIgYW4gaW5kaXZpZHVhbCBwYXRpZW50IGlzIGF0IHJpc2sgb2YgZGV2ZWxvcGluZyBDSEQgd2l0aGluIHRoZSBuZXh0IDEwIHllYXJzLCB3aXRoIGEgcmVzcG9uc2UgdmFsdWUgb2YgMSBtZWFuaW5nIHllcyB0aGV5IGFyZSBhdCByaXNrLCBhbmQgMCBtZWFuaW5nIG5vIHRoZXkgYXJlIG5vdCBhdCByaXNrLg0KDQoNCg0KIyMgVmFyaWFibGVzDQoNClRoZXJlIGFyZSAxNiB2YXJpYWJsZXMgaW4gdGhpcyBkYXRhIHNldC4NCg0KKiBtYWxlOiBUaGUgZ2VuZGVyIG9mIHRoZSBwYXRpZW50LiBBIGNhdGVnb3JpY2FsIHZhcmlhYmxlIHdpdGggMSBmb3IgIm1hbGUiLCAgMCBmb3IgImZlbWFsZSIsIGFuZCAyIGZvciAib3RoZXIiLiANCg0KKiBhZ2U6IFRoZSBhZ2Ugb2YgdGhlIHBhdGllbnQgaW4geWVhcnMuIFRoaXMgaXMgYSBxdWFudGl0YXRpdmUsIGNvbnRpbnVvdXMgdmFyaWFibGUgZ2l2ZW4gYXMgYW4gaW50ZWdlciB2YWx1ZS4NCg0KKiBlZHVjYXRpb246IFRoZSBlZHVjYXRpb24gbGV2ZWwgb2YgdGhlIHBhdGllbnQuIEEgY2F0ZWdvcmljYWwgdmFyaWFibGUgd2l0aCAxIGZvciAibGVzcyB0aGFuIGhpZ2ggc2Nob29sIiwgMiBmb3IgImhpZ2ggc2Nob29sIGRpcGxvbWEiLCAzIGZvciAiY29sbGVnZSBncmFkdWF0ZSIsIGFuZCA0IGZvciAicG9zdC1jb2xsZWdlIGdyYWR1YXRlIi4NCg0KKiBjdXJyZW50U21va2VyOiBXaGV0aGVyIG9yIG5vdCB0aGUgcGF0aWVudCBpcyBjdXJyZW50bHkgYSBzbW9rZXIuIEEgYmluYXJ5IHZhcmlhYmxlIHdpdGggMSBmb3IgInllcyIgYW5kIDAgZm9yICJubyIuDQoNCiogY2lnc1BlckRheTogVGhlIGF2ZXJhZ2UgbnVtYmVyIG9mIGNpZ2FyZXR0ZXMgdGhlIHBhdGllbnQgc21va2VzIGluIGEgZGF5LiBUaGlzIGlzIGEgcXVhbnRpdGF0aXZlLCBjb250aW51b3VzIHZhcmlhYmxlLiANCg0KKiBCUE1lZHM6IFdoZXRoZXIgb3Igbm90IHRoZSBwYXRpZW50IHRha2VzIGJsb29kIHByZXNzdXJlIG1lZGljYXRpb24uIEEgYmluYXJ5IHZhcmlhYmxlIHdpdGggMSBmb3IgInllcyIgYW5kIDAgZm9yICJubyIuDQoNCiogcHJldmFsZW50U3Ryb2tlOiBXaGV0aGVyIG9yIG5vdCB0aGUgcGF0aWVudCBoYXMgYSBoaXN0b3J5IG9mIHN0cm9rZXMuIEEgYmluYXJ5IHZhcmlhYmxlIHdpdGggMSBmb3IgInllcyIgYW5kIDAgZm9yICJubyIuDQoNCiogcHJldmFsZW50SHlwOiBXaGV0aGVyIG9yIG5vdCB0aGUgcGF0aWVudCBoYXMgYSBoaXN0b3J5IG9mIGh5cGVydGVuc2lvbi4gQSBiaW5hcnkgdmFyaWFibGUgd2l0aCAxIGZvciAieWVzIiBhbmQgMCBmb3IgIm5vIi4NCg0KKiBkaWFiZXRlczogV2hldGhlciBvciBub3QgdGhlIHBhdGllbnQgaGFzIGEgaGlzdG9yeSBvZiBkaWFiZXRlcy4gQSBiaW5hcnkgdmFyaWFibGUgd2l0aCAxIGZvciAieWVzIiBhbmQgMCBmb3IgIm5vIi4NCg0KKiB0b3RDaG9sOiBUaGUgcGF0aWVudCdzIHRvdGFsIGNob2xlc3Rlcm9sIGxldmVsLCBnaXZlbiBpbiBtZy9kTCAobWlsbGlncmFtcyBwZXIgZGVjaWxpdGVyKS4gQSBxdWFudGl0YXRpdmUsIGNvbnRpbnVvdXMgdmFyaWFibGUuIA0KDQoqIHN5c0JQOiBUaGUgcGF0aWVudCdzIHN5c3RvbGljIGJsb29kIHByZXNzdXJlLCBnaXZlbiBpbiBtbUhHIChtaWxsaW1ldGVycyBvZiBtZXJjdXJ5KS4gQSBxdWFudGl0YXRpdmUsIGNvbnRpbnVvdXMgdmFyaWFibGUuIA0KDQoqIGRpYUJQOiBUaGUgcGF0aWVudCdzIGRpYXN0b2xpYyBibG9vZCBwcmVzc3VyZSwgZ2l2ZW4gaW4gbW1IRyAobWlsbGltZXRlcnMgb2YgbWVyY3VyeSkuIEEgcXVhbnRpdGF0aXZlLCBjb250aW51b3VzIHZhcmlhYmxlLiANCg0KKiBCTUk6IFRoZSBwYXRpZW50J3MgYm9keSBtYXNzIGluZGV4IChCTUkpLiBBIHF1YW50aXRhdGl2ZSwgY29udGludW91cyB2YXJpYWJsZS4gDQoNCiogaGVhcnRSYXRlOiBUaGUgcGF0aWVudCdzIGhlYXJ0IHJhdGUsIGdpdmVuIGluIGJlYXRzIHBlciBtaW51dGUuIEEgbnVtZXJpYywgcXVhbnRpdGF0aXZlIHZhcmlhYmxlLiANCg0KKiBnbHVjb3NlOiBUaGUgcGF0aWVudCdzIGdsdWNvc2UgbGV2ZWwsIGdpdmVuIGluIG1nL2RMIChtaWxsaWdyYW1zIHBlciBkZWNpbGl0ZXIpLiBBIG51bWVyaWMsIHF1YW50aXRhdGl2ZSB2YXJpYWJsZS4gDQoNCiogVGVuWWVhckNIRCAocmVzcG9uc2UgdmFyaWFibGUpOiBUaGUgYmluYXJ5IHJlc3BvbnNlIHZhcmlhYmxlIG9mIHRoaXMgZGF0YSBzZXQgd2hpY2ggcmVwcmVzZW50cyB3aGV0aGVyIG9yIG5vdCB0aGUgcGF0aWVudCBoYXMgYSAxMC15ZWFyIHJpc2sgb2YgZGV2ZWxvcGluZyBDb3JvbmFyeSBIZWFydCBEaXNlYXNlIChDSEQpLiBUaGlzIHJlc3BvbnNlIHZhcmlhYmxlIGlzIGJpbmFyeSB3aXRoIDEgbWVhbmluZyB5ZXMsIHRoZSBwYXRpZW50IGRvZXMgaGF2ZSBhIHJpc2sgb2YgZGV2ZWxvcGluZyBDSEQgaW4gdGhlIG5leHQgMTAgeWVhcnMsIGFuZCAwIG1lYW5pbmcgbm8sIHRoZSBwYXRpZW50IGRvZXMgbm90IGhhdmUgYSByaXNrIG9mIGRldmVsb3BpbmcgQ0hEIGluIHRoZSBuZXh0IDEwIHllYXJzLg0KDQoNCg0KIyMgUmVzZWFyY2ggUXVlc3Rpb25zDQoNCg0KVGhlIGtleSBhbmFseXRpY2FsIHF1ZXN0aW9uIHdoaWNoIEkgd291bGQgbGlrZSB0byBpbnZlc3RpZ2F0ZSBpbiB0aGlzIHByb2plY3QgaXM6DQoNCiogQ2FuIHdlIGNyZWF0ZSBhIG1vZGVsIHdoaWNoIGhhcyB0aGUgaGlnaGVzdCBwcmVkaWN0aXZlIHBvd2VyIGluIHJlZ2FyZHMgdG8gcHJlZGljdGluZyBhIHBhdGllbnQncyBvZGRzIG9mIGJlaW5nIGF0IHJpc2sgZm9yIGRldmVsb3BpbmcgY29yb25hcnkgaGVhcnQgZGlzZWFzZSB3aXRoaW4gYSAxMC15ZWFyIHBlcmlvZCBvZiB0aW1lIGJhc2VkIG9uIHRoZSBwcmVkaWN0b3IgdmFyaWFibGVzIGluIHRoaXMgZGF0YSBzZXQ/DQoNClRoaXMgcXVlc3Rpb24gd2lsbCBzZXJ2ZSBhcyB0aGUgYmFzaXMgZm9yIGNyZWF0aW5nIHRoZSBjYW5kaWRhdGUgbXVsdGlwbGUgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbHMgZm9yIHRoaXMgZGF0YSBzZXQuIFdlIHdpbGwgdWx0aW1hdGVseSBjaG9vc2UgYSBmaW5hbCBtb2RlbCBvZiB0aGVzZSBwb3RlbnRpYWwgY2FuZGlkYXRlIG1vZGVscyBiYXNlZCB1cG9uIG91ciBmaW5kaW5ncyBpbiByZWdhcmRzIHRvIHRoZWlyIHByZWRpY3RpdmUgcG93ZXIuIFRoZSBmaW5kaW5ncyB0byB0aGlzIHF1ZXN0aW9uIGNvdWxkIHByb3ZpZGUgdXRpbGl0eSBmb3IgYm90aCBwYXRpZW50cyBhbmQgZG9jdG9ycyB0byBwcm92aWRlIHRoZW0gd2l0aCBpbmZvcm1hdGlvbiByZWxhdGluZyB0byB0aGUgb2RkcyBhbmQgcmlza3MgZm9yIGRldmVsb3BpbmcgQ0hEIGFuZCB3aGljaCBmYWN0b3JzIHNpZ25pZmljYW50bHkgaW1wYWN0IHRoZXNlIG9kZHMuIA0KDQpTb21lIGZ1cnRoZXIgcXVlc3Rpb25zIHRvIGNvbnNpZGVyIGFzIGEgc3RhcnRpbmcgcG9pbnQgZm9yIHRoaXMgcHJvamVjdCBpbmNsdWRlOg0KDQoqIElzIHRoZXJlIGEgbWFqb3IgZGlmZmVyZW5jZSBpbiB0aGUgcHJlZGljdGl2ZSBwb3dlcnMgb2YgdGhlIGNhbmRpZGF0ZSBtb2RlbHMgd2UgY3JlYXRlPw0KDQoqIERvIHRoZSBjb250aW51b3VzIGluZGVwZW5kZW50IHZhcmlhYmxlcyBpbiB0aGlzIGRhdGEgc2V0IGZvbGxvdyBhIG5vcm1hbCBkaXN0cmlidXRpb24/IEFuZCBpZiBub3QsIGlzIHRoZXJlIGEgcG90ZW50aWFsIGV4cGxhbmF0aW9uIGZvciB3aHkgdGhpcyBtYXkgYmUgdGhlIGNhc2U/DQoNCiogSG93IGdvb2QgaXMgdGhlIHByZWRpY3RpdmUgcXVhbGl0eSBvZiBvdXIgZmluYWwgbW9kZWwgaW4gcmVnYXJkcyBvZiBwcmVkaWN0aW5nIHdoZXRoZXIgYW4gaW5kaXZpZHVhbCB3aWxsIGJlIGF0IHJpc2sgZm9yIGRldmVsb3BpbmcgQ0hEIGluIGEgMTAteWVhciBwZXJpb2Qgb2YgdGltZT8NCg0KDQpXZSB3aWxsIHVzZSB0aGVzZSBxdWVzdGlvbnMgYXMgYSBzdGFydGluZyBwb2ludCBmb3IgdGhpcyBwcm9qZWN0IGluIG9yZGVyIHRvIGNyZWF0ZSBhIG11bHRpcGxlIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWwgd2hpY2ggaGFzIHRoZSBncmVhdGVzdCBwcmVkaWN0aXZlIHBvd2VyIGZvciBwcmVkaWN0aW5nIHRoZSBvZGRzIG9mIGEgcGF0aWVudCBiZWluZyBhdCByaXNrIGZvciBkZXZlbG9waW5nIENIRCBpbiBhIDEwLXllYXIgcGVyaW9kIGJhc2VkIG9uIHRoZSBtYW55IGZhY3RvcnMgd2hpY2ggcGxheSBhIHJvbGUgaW4gdGhpcyBwcmVkaWN0aW9uLiANCg0KDQojIEV4cGxvcmF0b3J5IERhdGEgQW5hbHlzaXMNCg0KRmlyc3QsIHdlIHdpbGwgcmVhZCBpbiB0aGUgZGF0YSBzZXQgZnJvbSBHaXRodWIgYW5kIHdlIHdpbGwgY2FsbCBpdCAiaGVhcnRkaXNlYXNlIi4gDQoNCmBgYHtyfQ0KaGVhcnRkaXNlYXNlIDwtIHJlYWQuY3N2KCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vSm9zaWVHYWxsb3AvU1RBMzIxL3JlZnMvaGVhZHMvbWFpbi9kYXRhc2V0L2ZyYW1pbmdoYW0uY3N2IiwgaGVhZGVyPVRSVUUpDQoNCnN0cihoZWFydGRpc2Vhc2UpDQpgYGANCg0KDQpXZSB3aWxsIG1ha2UgYSBjb3B5IG9mIHRoZSBkYXRhIHNldCBjYWxsZWQgImhlYXJ0ZGlzZWFzZTAiIHdoaWNoIHdpbGwgYmUgdGhlIGRhdGEgc2V0IHdlIHdpbGwgYW5hbHl6ZSBhbmQgdHJhbnNmb3JtIHRocm91Z2ggdGhlIGV4cGxvcmF0b3J5IGRhdGEgYW5hbHlzaXMuIA0KDQpgYGB7cn0NCmhlYXJ0ZGlzZWFzZTAgPC0gaGVhcnRkaXNlYXNlDQpgYGANCg0KDQoNCiMjIEZpeGluZyB0aGUgTWlzc2luZyBPYnNlcnZhdGlvbnMNCg0KDQpXaGVuIGxvb2tpbmcgdGhyb3VnaCB0aGUgZGF0YSBzZXQsIEkgbm90aWNlZCB0aGF0IHNvbWUgb2JzZXJ2YXRpb25zIGFwcGVhcmVkIHRvIGJlIG1pc3NpbmcgZnJvbSB0aGUgZGF0YSBzZXQsIGFzIHRoZXkgaGFkIHZhbHVlcyBvZiAiTkEiIGZvciBjZXJ0YWluIHZhcmlhYmxlcy4gQmVmb3JlIHdlIGNhbiBiZWdpbiB3aXRoIHRoZSBsb2dpc3RpYyByZWdyZXNzaW9uLCB0aGlzIGlzIHNvbWV0aGluZyB0aGF0IHdlIHNob3VsZCBsb29rIGludG8uDQoNCkZpcnN0LCBsZXQncyBzZWUgaG93IG1hbnkgdmFsdWVzIGFyZSBtaXNzaW5nIGZvciBlYWNoIG9mIHRoZSB2YXJpYWJsZXMuDQoNCmBgYHtyfQ0KY29sU3Vtcyhpcy5uYShoZWFydGRpc2Vhc2UwKSkNCmBgYA0KDQpXZSBjYW4gc2VlIHRoYXQgdGhlIHZhcmlhYmxlcyBlZHVjYXRpb24sIGNpZ3NQZXJEYXksIEJQTWVkcywgdG90Q2hvbCwgQk1JLCBoZWFydFJhdGUsIGFuZCBnbHVjb3NlIGFsbCBoYXZlIG1pc3Npbmcgb2JzZXJ2YXRpb25zLg0KDQpJdCBpcyBpbXBvcnRhbnQgdG8gbm90ZSB0aGF0IHNpbmdsZSBpbXB1dGF0aW9uIG1ldGhvZHMgdGhhdCBjb3VsZCBiZSB1c2VkIHRvIGZpeCB0aGUgbWlzc2luZyBvYnNlcnZhdGlvbnMgb2YgdGhlIHZhcmlhYmxlcyBjb3VsZCBsZWFkIHRvIHBvdGVudGlhbCBza2V3IGluIHRoZWlyIGRpc3RyaWJ1dGlvbnMuIFNvLCBpbnN0ZWFkIHdlIHdpbGwgdXNlIG11bHRpcGxlIGltcHV0YXRpb24gbWV0aG9kcyB0byBmaWxsIGluIHRoZXNlIG1pc3Npbmcgb2JzZXJ2YXRpb25zIHdpdGggcmFuZG9tbHkgYXBwcm94aW1hdGVkIHZhbHVlcy4gT3V0IG9mIHRoZSB2YXJpYWJsZXMgd2l0aCBtaXNzaW5nIHZhbHVlcywgbm9uZSBvZiB0aGUgdmFyaWFibGVzIGhhdmUgdGhlIG1ham9yaXR5IG9mIHRoZWlyIGRhdGEgZW50cmllcyBtaXNzaW5nLCBzbyBpdCBzZWVtcyBzdWl0YWJsZSB0byBmaWxsIGluIHRoZXNlIG1pc3Npbmcgb2JzZXJ2YXRpb25zIHdpdGggYXBwcm94aW1hdGVkIHZhbHVlcyBpbiBvcmRlciB0byBwcmV2ZW50IHRoZXNlIGVudHJpZXMgZnJvbSBjb21pbmcgdXAgYXMgTkEgd2l0aGluIHRoZSBkYXRhIHNldC4gSG93ZXZlciwgaXQgaXMgaW1wb3J0YW50IHRvIGJlIG1pbmRmdWwgb2YgdGhlIGZhY3QgdGhhdCBmaWxsaW5nIGluIHRoZXNlIG1pc3NpbmcgZW50cmllcyBjb3VsZCBzdGlsbCBsZWFkIHRvIGEgcG90ZW50aWFsIHNrZXcgb3IgaW5hY2N1cmFjeSBpbiB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoZXNlIHZhcmlhYmxlcyBhZnRlciBmaWxsaW5nIGluIHRoZSBtaXNzaW5nIG9ic2VydmF0aW9ucy4gSW4gb3JkZXIgdG8gcHJvcGVybHkgaGFuZGxlIHRoaXMsIHdlIHdpbGwgZmlyc3QgZml4IHRoZSBtaXNzaW5nIHZhbHVlcyBhbmQgdGhlbiBjaGVjayB0aGUgZGlzdHJpYnV0aW9ucyBvZiB0aGUgdmFyaWFibGVzIHRvIGVuc3VyZSB0aGF0IHRoZXkgYXJlIG5vcm1hbGx5IGRpc3RyaWJ1dGVkLCBvciBhcmUgcHJhY3RpY2FsbHkgaW1wb3J0YW50IGVub3VnaCB2YXJpYWJsZXMgdG8gaW5jbHVkZSB3aXRoaW4gdGhlIGZpbmFsIG11bHRpcGxlIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWwuDQoNCg0KIyMgTXVsdGlwbGUgSW1wdXRhdGlvbiBvZiBNaXNzaW5nIE9ic2VydmF0aW9ucw0KDQpXZSB3aWxsIHVzZSBtdWx0aXBsZSBpbXB1dGF0aW9uIHRvIGZpbGwgaW4gdGhlIG1pc3NpbmcgdmFsdWVzIG9mIHRoZSBvYnNlcnZhdGlvbnMuIFVzaW5nIHRoaXMgbXVsdGlwbGUgaW1wdXRhdGlvbiBtZXRob2Qgd2lsbCB1c2UgYSByYW5kb21seSBpdGVyYXRlZCBtZXRob2QgdG8gZmlsbCBpbiB0aGUgbWlzc2luZyBvYnNlcnZhdGlvbnMgd2l0aGluIHRoZSBkYXRhIHNldC4gVGhpcyB3aWxsIGhlbHAgZW5zdXJlIHRoYXQgdGhlIG1pc3Npbmcgb2JzZXJ2YXRpb25zIGFyZSBmaWxsZWQgaW4gdGhlIGJlc3Qgd2F5IHBvc3NpYmxlIGJ5IGhhbmRsaW5nIG1pc3NpbmcgdmFsdWVzIGluIGEgd2F5IHRoYXQgcmVkdWNlcyB0aGUgY2hhbmNlIG9mIGZhbHNlIHByZWRpY3Rpb24uIFdlIHdpbGwgdXNlIHRoZSBNdWx0aXBsZSBJbXB1dGF0aW9uIHVzaW5nIENoYWluZWQgRXF1YXRpb25zIChNSUNFKSBmdW5jdGlvbiB0byB1c2UgbXVsdGlwbGUgaW1wdXRhdGlvbiB0byBmaWxsIGluIHRoZSBtaXNzaW5nIG9ic2VydmF0aW9ucy4gDQoNCldlIHdpbGwgc3RvcmUgdGhlIHJlc3VsdHMgb2YgdGhlIG11bHRpcGxlIGltcHV0YXRpb24gaW4gYW4gb2JqZWN0IGNhbGxlZCAibWljZSIgZm9yIG5vdy4NCg0KYGBge3J9DQpzZXQuc2VlZCgxKQ0KbWljZSA8LSBtaWNlKGhlYXJ0ZGlzZWFzZTAsIG1ldGhvZCA9ICJjYXJ0IikNCmBgYA0KDQpXZSB3aWxsIGNyZWF0ZSBhIG5ldyBkYXRhIGZyYW1lIGNhbGxlZCAiaGVhcnRkaXNlYXNlMSIgdG8gcmVwcmVzZW50IHRoZSBjb3JyZWN0ZWQgZGF0YSBzZXQgdGhhdCB3aWxsIGhhdmUgbm8gbWlzc2luZyB2YWx1ZXMgYWZ0ZXIgbXVsdGlwbGUgaW1wdXRhdGlvbi4gV2Ugd2lsbCBjb252ZXJ0IHRoZSByZXN1bHRzIG9mIHRoZSBtdWx0aXBsZSBpbXB1dGF0aW9uIGZyb20gYSBsYXJnZSBtaWRzIG9iamVjdCBiYWNrIHRvIGEgcmVndWxhciBkYXRhIGZyYW1lLiBUaGlzIHdpbGwgYmUgdXNlZCBmb3IgdGhlIGZ1dHVyZSBhbmFseXNpcyBhbmQgd2Ugd2lsbCBjYWxsIHRoaXMgb2JqZWN0ICJoZWFydGRpc2Vhc2UxIi4NCg0KYGBge3J9DQpoZWFydGRpc2Vhc2UxIDwtbWljZTo6Y29tcGxldGUobWljZSwyKSANCmBgYA0KDQpOb3cgdGhhdCB3ZSBoYXZlIHVzZWQgdGhlIG11bHRpcGxlIGltcHV0YXRpb24gbWV0aG9kIHRvIGZpbGwgaW4gYWxsIG9mIHRoZSBtaXNzaW5nIG9ic2VydmF0aW9ucywgbGV0J3MgZG91YmxlIGNoZWNrIHRoYXQgdGhlcmUgYXJlIG5vIG1vcmUgbWlzc2luZyBvYnNlcnZhdGlvbnMuIEFsbCBvZiB0aGUgdmFyaWFibGVzIHNob3VsZCBub3cgaGF2ZSBhIHZhbHVlIG9mIHplcm8gbWlzc2luZyBvYnNlcnZhdGlvbnMuIA0KDQpgYGB7cn0NCmNvbFN1bXMoaXMubmEoaGVhcnRkaXNlYXNlMSkpDQpgYGANCg0KQXMgd2UgY2FuIHNlZSwgYWxsIG9mIHRoZSB2YXJpYWJsZXMgbm93IGhhdmUgemVybyBtaXNzaW5nIG9ic2VydmF0aW9ucywgc28gd2Ugc3VjY2Vzc2Z1bGx5IGZpeGVkIGFsbCBvZiB0aGUgbWlzc2luZyB2YWx1ZXMgYnkgZmlsbGluZyB0aGVtIGluIHdpdGggbXVsdGlwbGUgaW1wdXRhdGlvbiBieSB0aGUgdXNlIG9mIHRoZSBtaWNlIGZ1bmN0aW9uLiBUaGUgcmV2aXNlZCBkYXRhIHNldCAiaGVhcnRkaXNlYXNlMSIgaGFzIG5vIG1pc3Npbmcgb2JzZXJ2YXRpb25zLCBzbyB3ZSB3aWxsIHVzZSB0aGlzIGZvciBmdXJ0aGVyIGFuYWx5c2lzIGFuZCBmb3IgY3JlYXRpbmcgdGhlIG11bHRpcGxlIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWwgaW4gdGhlIGZ1dHVyZSBzdGVwcyBvZiB0aGlzIHByb2plY3QuDQoNCg0KDQojIyBGaXhpbmcgVmFyaWFibGUgVHlwZXMNCg0KT25lIG90aGVyIHRoaW5nIEkgbm90aWNlZCB3aGlsZSBsb29raW5nIHRocm91Z2ggdGhlIGRhdGEgc2V0IGlzIHRoYXQgdGhlIHZhcmlhYmxlICJjaWdzUGVyRGF5IiBpcyBnaXZlbiBhcyBhIGNoYXJhY3RlciB2YXJpYWJsZSBldmVuIHRob3VnaCBpdCBzaG91bGQgYmUgYSBudW1lcmljIHZhcmlhYmxlLiBUaGlzIHZhcmlhYmxlIGlzIGEgbWVhc3VyZSBvZiB0aGUgbnVtYmVyIG9mIGNpZ2FyZXR0ZXMgYSBwYXRpZW50IHNtb2tlcyBpbiBhIGRheSwgc28gaXQgc2hvdWxkIGJlIHJlcG9ydGVkIGFzIGEgbnVtZXJpYyB2YXJpYWJsZSBzaW5jZSBpdCBpcyBhIGNvbnRpbnVvdXMgcmFuZ2Ugb2YgcG9zc2libGUgdmFsdWVzIGFuZCBub3QgY29sbGVjdGVkIGFzIGNhdGVnb3JpZXMuIFNvLCBsZXQncyBjb252ZXJ0IGNpZ3NQZXJEYXkgZnJvbSBhIGNoYXJhY3RlciB2YXJpYWJsZSB0byBhIG51bWVyaWMgdmFyaWFibGUuIFdlIHdpbGwgdXNlIHRoZSBhcy5udW1lcmljKCkgZnVuY3Rpb24gdG8gY29udmVydCBjaWdzUGVyRGF5IHRvIGEgbnVtZXJpYyB2YXJpYWJsZS4NCg0KYGBge3J9DQpoZWFydGRpc2Vhc2UxJGNpZ3NQZXJEYXkgPC0gYXMubnVtZXJpYyhoZWFydGRpc2Vhc2UxJGNpZ3NQZXJEYXkpDQpgYGANCg0KTm93LCB0aGUgY2lnc1BlckRheSB2YXJpYWJsZSBpcyBjb3JyZWN0bHkgZ2l2ZW4gYXMgYSBudW1lcmljIHZhcmlhYmxlLg0KDQpUaGUgdmFyaWFibGUgb2YgYWdlIHNob3VsZCBhbHNvIGJlIGdpdmVuIGFzIGEgbnVtZXJpYyB2YWx1ZSwgYnV0IGlzIGlzIG5vdCBzbyB3ZSB3aWxsIGNvbnZlcnQgdGhpcyB2YXJpYWJsZSB0byBhIG51bWVyaWMgYXMgd2VsbC4NCg0KYGBge3J9DQpoZWFydGRpc2Vhc2UxJGFnZSA8LSBhcy5udW1lcmljKGhlYXJ0ZGlzZWFzZTEkYWdlKQ0KYGBgDQoNClRoZSB2YXJpYWJsZSBvZiBhZ2UgaXMgbm93IGNvcnJlY3RseSBnaXZlbiBhcyBhIG51bWVyaWMgdmFsdWUuIA0KDQpBZGRpdGlvbmFsbHksIHRoZSB2YXJpYWJsZSBCUE1lZHMgaXMgZ2l2ZW4gYXMgYSBjaGFyYWN0ZXIsIGhvd2V2ZXIgdGhpcyBpcyBhIGJpbmFyeSB2YXJpYWJsZSBzbyBpdCBzaG91bGQgYmUgZ2l2ZW4gYXMgYW4gaW50ZWdlciB2YXJpYWJsZSB3aXRoIHZhbHVlcyAwIGFuZCAxIGxpa2UgdGhlIG90aGVyIGJpbmFyeSB2YXJpYWJsZXMgaW4gdGhlIGRhdGEgc2V0LiBXZSB3aWxsIGNvbnZlcnQgdGhpcyB2YXJpYWJsZSBmcm9tIGEgY2hhcmFjdGVyIHRvIGFuIGludGVnZXIuIFdlIHdpbGwgdXNlIHRoZSBhcy5pbnRlZ2VyKCkgZnVuY3Rpb24gdG8gZG8gdGhpcyBjb252ZXJzaW9uLg0KDQpgYGB7cn0NCmhlYXJ0ZGlzZWFzZTEkQlBNZWRzIDwtIGFzLmludGVnZXIoaGVhcnRkaXNlYXNlMSRCUE1lZHMpDQpgYGANCg0KTm93LCB0aGUgQlBNZWRzIHZhcmlhYmxlIGlzIGNvcnJlY3RseSBnaXZlbiBhcyBhIGJpbmFyeSwgaW50ZWdlciB2YXJpYWJsZS4NCg0KDQpBbm90aGVyIGlzc3VlIGlzIHRoYXQgdGhlIHZhcmlhYmxlIGZvciBlZHVjYXRpb24gaXMgc3VwcG9zZWQgdG8gYmUgY2F0ZWdvcmljYWwgd2l0aCBlYWNoIG51bWJlciByZXByZXNlbnRpbmcgYSBkaWZmZXJlbnQgY2F0ZWdvcnkgb2YgZWR1Y2F0aW9uIGxldmVsLiBIb3dldmVyLCB0aGlzIHZhcmlhYmxlIGlzIGdpdmVuIGFzIGFuIGludGVnZXIgdmFyaWFibGUuIFNvLCB3ZSB3aWxsIGZpeCB0aGlzIGJ5IG1ha2luZyBlZHVjYXRpb24gYSBjaGFyYWN0ZXIgdmFyaWFibGUuDQoNCmBgYHtyfQ0KaGVhcnRkaXNlYXNlMSRlZHVjYXRpb24gPC0gYXMuY2hhcmFjdGVyKGhlYXJ0ZGlzZWFzZTEkZWR1Y2F0aW9uKQ0KYGBgDQoNClRoZSBlZHVjYXRpb24gdmFyaWFibGUgaXMgbm93IGNvcnJlY3RseSBnaXZlbiBhcyBhIGNhdGVnb3JpY2FsIHZhcmlhYmxlLg0KDQpOb3csIGFsbCBvZiB0aGUgdmFyaWFibGVzIGluIHRoZSBkYXRhIHNldCBhcmUgZ2l2ZW4gaW4gdGhlIGNvcnJlY3QgdHlwZXMgdGhhdCB0aGV5IHNob3VsZCBiZSBnaXZlbiBhcy4gDQoNCg0KDQojIyBDaGVja2luZyB0aGUgVmFyaWFibGUgRGlzdHJpYnV0aW9ucw0KDQoNCkJlZm9yZSB3ZSBiZWdpbiB3aXRoIGNyZWF0aW5nIGFuZCBjb21wYXJpbmcgdGhlIG11bHRpcGxlIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWxzLCB3ZSBzaG91bGQgZmlyc3QgY2hlY2sgdGhlIGRpc3RyaWJ1dGlvbnMgb2YgdGhlIHZhcmlhYmxlcyBpbiB0aGUgZGF0YSBzZXQgdG8gZW5zdXJlIHRoYXQgdGhleSBtZWV0IHRoZSBjcml0ZXJpYSBvZiBiZWluZyBhcHByb3hpbWF0ZWx5IG5vcm1hbGx5IGRpc3RyaWJ1dGVkLCBmb3IgdGhlIHF1YW50aXRhdGl2ZSB2YXJpYWJsZXMuIE9yLCBpZiB0aGUgdmFyaWFibGVzIGRvIG5vdCBhcHBlYXIgdG8gZm9sbG93IGEgbm9ybWFsIGRpc3RyaWJ1dGlvbiwgd2Ugd2lsbCBzZWUgaWYgdGhleSBhcmUgcHJhY3RpY2FsbHkgaW1wb3J0YW50IGVub3VnaCB0byBiZSBpbmNsdWRlZCB3aXRoaW4gdGhlIG1vZGVsIGJ1aWxkaW5nIHByb2Nlc3MgZGVzcGl0ZSB0aGVpciBsYWNrIG9mIGEgbm9ybWFsIGRpc3RyaWJ1dGlvbi4gV2Ugd2lsbCB0YWtlIGEgbG9vayBhdCBzb21lIG9mIHRoZSB2YXJpYWJsZXMsIHBhcnRpY3VhcmVsbHkgc29tZSBvZiB0aGUgcXVhbml0aWF0aXZlIHZhcmlhYmxlcyB3aGljaCBoYWQgbWlzc2luZyB2YWx1ZXMgdGhhdCB3ZXJlIGZpbGxlZCBpbiB0aHJvdWdoIHRoZSBtdWx0aXBsZSBpbXB1dGF0aW9uIHByb2Nlc3MuIA0KDQoNCiMjIyB0b3RDaG9sIERpc3RyaWJ1dGlvbg0KDQp0b3RDaG9sIHdhcyBvbmUgb2YgdGhlIHZhcmlhYmxlcyB0aGF0IGhhZCBzb21lIG1pc3Npbmcgb2JzZXJ2YXRpb25zIHRoYXQgd2VyZSBmaWxsZWQgaW4gZHVyaW5nIHRoZSBwcmV2aW91cyBzdGVwcy4gU28sIHdlIHNob3VsZCBkZWZpbmV0bHkgY2hlY2sgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGlzIHZhcmlhYmxlLg0KDQpgYGB7cn0NCnlsaW1pdCA9IG1heChkZW5zaXR5KGhlYXJ0ZGlzZWFzZTEkdG90Q2hvbCkkeSkNCmhpc3QoaGVhcnRkaXNlYXNlMSR0b3RDaG9sLCBwcm9iYWJpbGl0eSA9IFRSVUUsIG1haW4gPSAidG90Q2hvbCBEaXN0cmlidXRpb24iLCB4bGFiPSJ0b3RDaG9sIiwgDQogICAgICAgY29sID0gImFsaWNlYmx1ZSIsIGJvcmRlcj0iY29ybmZsb3dlcmJsdWUiKQ0KICBsaW5lcyhkZW5zaXR5KGhlYXJ0ZGlzZWFzZTEkdG90Q2hvbCwgYWRqdXN0PTIpLCBjb2w9ImRhcmtvcmNoaWQiKQ0KYGBgDQoNCk92ZXJhbGwsIHRvdENob2wgYXBwZWFycyB0byBiZSB1bmltb2RhbCBhbmQgYXBwcm94aW1hdGVseSBub3JtYWxseSBkaXN0cmlidXRlZC4gVGhlcmVmb3JlIHdlIGNhbiBzYXkgdGhhdCB0aGlzIHZhcmlhYmxlIGlzIHNhZmUgdG8gaW5jbHVkZSB3aXRoaW4gdGhlIG11bHRpcGxlIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWwgYnVpbGRpbmcgcHJvY2VzcyBhbmQgdGhhdCB0aGUgcHJvY2VzcyBvZiBmaWxsaW5nIGluIHRoZSBtaXNzaW5nIG9ic2VydmF0aW9ucyBkaWQgbm90IGxlYWQgdG8gYW55IHNldmVyZSBjb25jZXJucyBmb3IgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGlzIHZhcmlhYmxlLg0KDQoNCiMjIyBCTUkgRGlzdHJpYnV0aW9uDQoNCkJNSSBpcyBhbm90aGVyIHZhcmlhYmxlIHRoYXQgaGFkIG1pc3Npbmcgb2JzZXJ2YXRpb25zIHRoYXQgd2VyZSBmaWxsZWQgaW4gZHVyaW5nIHRoZSBwcmV2aW91cyBzdGVwcy4gV2Ugd2lsbCBjaGVjayBpdHMgZGlzdHJpYnV0aW9uIHRvIG1ha2Ugc3VyZSBpdCBhcHBlYXJzIHRvIGJlIGFwcHJveGltYXRlbHkgbm9ybWFsbHkgZGlzdHJpYnV0ZWQuIA0KDQpgYGB7cn0NCnlsaW1pdCA9IG1heChkZW5zaXR5KGhlYXJ0ZGlzZWFzZTEkQk1JKSR5KQ0KaGlzdChoZWFydGRpc2Vhc2UxJEJNSSwgcHJvYmFiaWxpdHkgPSBUUlVFLCBtYWluID0gIkJNSSBEaXN0cmlidXRpb24iLCB4bGFiPSJCTUkiLCANCiAgICAgICBjb2wgPSAiYWxpY2VibHVlIiwgYm9yZGVyPSJjb3JuZmxvd2VyYmx1ZSIpDQogIGxpbmVzKGRlbnNpdHkoaGVhcnRkaXNlYXNlMSRCTUksIGFkanVzdD0yKSwgY29sPSJkYXJrb3JjaGlkIikNCmBgYA0KDQpUaGUgZGlzdHJpYnV0aW9uIGZvciBCTUkgaXMgdW5pbW9kYWwgYW5kIGFwcGVhcnMgdG8gYmUgYXBwcm94aW1hdGVseSBub3JtYWxseSBkaXN0cmlidXRlZC4gSXQgYXBwZWFycyB0aGF0IHRoZSBtYWpvcml0eSBvZiBpbmRpdmlkdWFscyBoYXZlIGEgQk1JIHNvbWV3aGVyZSBpbiB0aGUgcmFuZ2Ugb2YgMjAtMzAsIHdpdGggdGhlIHZhc3QgbWFqb3JpdHkgb2YgdGhlIGRhdGEgZmFsbGluZyB3aXRoaW4gdGhpcyByYW5nZS4gVGhpcyBhbGxvd3MgdXMgdG8gY29uY2x1ZGUgdGhhdCBpdCBpcyBzYWZlIHRvIGluY2x1ZGUgdGhlIHZhcmlhYmxlIG9mIEJNSSB3aXRoaW4gdGhlIG11bHRpcGxlIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWwgYnVpbGRpbmcgcHJvY2VzcyBhbmQgdGhhdCB0aGUgcHJvY2VzcyBvZiBmaWxsaW5nIGluIHRoZSBtaXNzaW5nIHZhbHVlcyBkaWQgbm90IGxlYWQgdG8gYW55IG1ham9yIGNvbmNlcm5zIHdpdGggdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGlzIHZhcmlhYmxlLiANCg0KDQoNCiMjIyBHbHVjb3NlIERpc3RyaWJ1dGlvbg0KDQpUaGUgdmFyaWFibGUgZ2x1Y29zZSB3YXMgYW5vdGhlciB2YXJpYWJsZSB0aGF0IGhhZCBtaXNzaW5nIG9ic2VydmF0aW9ucyB3aGljaCB3ZXJlIGZpbGxlZCBpbiBkdXJpbmcgdGhlIHByZXZpb3VzIHN0ZXBzLiBXZSB3aWxsIGNoZWNrIGl0cyBkaXN0cmlidXRpb24gdG8gbWFrZSBzdXJlIHRoYXQgZXZlcnl0aGluZyBsb29rcyBhbHJpZ2h0Lg0KDQpgYGB7cn0NCnlsaW1pdCA9IG1heChkZW5zaXR5KGhlYXJ0ZGlzZWFzZTEkZ2x1Y29zZSkkeSkNCmhpc3QoaGVhcnRkaXNlYXNlMSRnbHVjb3NlLCBwcm9iYWJpbGl0eSA9IFRSVUUsIG1haW4gPSAiZ2x1Y29zZSIsIHhsYWI9ImdsdWNvc2UiLCANCiAgICAgICBjb2wgPSAiYWxpY2VibHVlIiwgYm9yZGVyPSJjb3JuZmxvd2VyYmx1ZSIpDQogIGxpbmVzKGRlbnNpdHkoaGVhcnRkaXNlYXNlMSRnbHVjb3NlLCBhZGp1c3Q9MiksIGNvbD0iZGFya29yY2hpZCIpDQpgYGANCg0KVGhlIHZhcmlhYmxlIGdsdWNvc2UgaXMgdW5pbW9kYWwgYW5kIGFwcGVhcnMgdG8gYmUgYXBwcm94aW1hdGVseSBub3JtYWxseSBkaXN0cmlidXRlZCB3aXRoIHRoZSBleGNlcHRpb24gb2YgYSBmZXcgb3V0bGllcnMgdG8gdGhlIHJpZ2h0LiBUaGUgbW9zdCBjb21tb24gZ2x1Y29zZSB2YWx1ZSBhcHBlYXJzIHRvIGJlIGFyb3VuZCA3NSwgd2l0aCB0aGUgaGlzdG9ncmFtIHBlYWtpbmcgYXJvdW5kIHRoaXMgdmFsdWUuIERlc3BpdGUgdGhlIGZldyBvdXRsaWVycywgd2Ugd2lsbCBrZWVwIHRoaXMgdmFyaWFibGUgaW4gdGhlIGZpbmFsIG1vZGVsIHNpbmNlIGEgcGF0aWVudCdzIGdsdWNvc2UgbGV2ZWwgaXMgYSBtZWRpY2FsbHkgaW1wb3J0YW50IHZhcmlhYmxlIHdoaWNoIHdvdWxkIGJlIGhlbHBmdWwgdG8ga25vdyBpbiBvcmRlciB0byBhc3Nlc3MgYSBwYXRpZW50J3Mgb2RkcyBvZiBiZWluZyBhdCByaXNrIGZvciBkZXZlbG9waW5nIENIRC4gIA0KDQoNCg0KIyMjIGNpZ3NQZXJEYXkgRGlzdHJpYnV0aW9uDQoNCmNpZ3NQZXJEYXkgd2FzIGFub3RoZXIgdmFyaWFibGUgdGhhdCBoYWQgbWlzc2luZyBvYnNlcnZhdGlvbnMgd2hpY2ggd2VyZSBmaWxsZWQgaW4gZHVyaW5nIHRoZSBwcmV2aW91cyBzdGVwcy4gU28sIHdlIHNob3VsZCBjaGVjayBpdHMgZGlzdHJpYnV0aW9uIHRvIG1ha2Ugc3VyZSB0aGluZ3MgbG9vayBhbHJpZ2h0Lg0KDQpgYGB7cn0NCnlsaW1pdCA9IG1heChkZW5zaXR5KGhlYXJ0ZGlzZWFzZTEkY2lnc1BlckRheSkkeSkNCmhpc3QoaGVhcnRkaXNlYXNlMSRjaWdzUGVyRGF5LCBwcm9iYWJpbGl0eSA9IFRSVUUsIG1haW4gPSAiY2lnc1BlckRheSIsIHhsYWI9ImNpZ3NQZXJEYXkiLCANCiAgICAgICBjb2wgPSAiYWxpY2VibHVlIiwgYm9yZGVyPSJjb3JuZmxvd2VyYmx1ZSIpDQogIGxpbmVzKGRlbnNpdHkoaGVhcnRkaXNlYXNlMSRjaWdzUGVyRGF5LCBhZGp1c3Q9MiksIGNvbD0iZGFya29yY2hpZCIpDQpgYGANCg0KVGhlIHZhcmlhYmxlIGNpZ3NQZXJEYXkgYXBwZWFycyB0byBiZSB2ZXJ5IG5vdGFibHkgc2tld2VkIHRvIHRoZSByaWdodC4gVGhpcyBjYW4gYmUgYXR0cmlidXRlZCB0byB0aGF0IHRoaXMgdmFyaWFibGUgcmVwcmVzZW50cyB0aGUgbnVtYmVyIG9mIGNpZ2FyZXR0ZXMgYSBwYXRpZW50IHNtb2tlcyBwZXIgZGF5LiBNYW55IG9mIHRoZSBwYXRpZW50cyBpbmNsdWRlZCBpbiB0aGUgZGF0YSBjb2xsZWN0aW9uIHdlcmUgbm90IHNtb2tlcnMsIHNvIHRoZXkgd291bGQgaGF2ZSByZXBvcnRlZCBhIHZhbHVlIG9mIDAgY2lnYXJldHRlcyBzbW9rZWQgcGVyIGRheS4gVGhpcyBjYW4gZXhwbGFpbiB0aGUgc2tldyB0byB0aGUgcmlnaHQgb2YgdGhpcyB2YXJpYWJsZSwgYmVjYXVzZSBub24tc21va2VycyB3b3VsZCByZXBvcnQgYSB2YWx1ZSBvZiAwIHdoaWxlIHNtb2tlcnMgd291bGQgcmVwb3J0IGEgdmFsdWUgZ3JlYXRlciB0aGFuIDAgdGhhdCByZXByZXNlbnRzIHRoZSBudW1iZXIgb2YgY2lnYXJldHRlcyB0aGV5IHNtb2tlIGluIGEgZGF5LiANClRoaXMgaXMgYSBwcmF0aWNhbGx5IGltcG9ydGFudCB2YXJpYWJsZSByZWdhcmRsZXNzIG9mIGl0cyBza2V3LCBiZWNhdXNlIHNtb2tpbmcgaXMgYSBtYWpvciByaXNrIGZhY3RvciBmb3IgY29uZGl0aW9ucyBsaWtlIENIRC4gQWRkaXRpb25hbGx5LCB0aGlzIGlzIGFuIGltcG9ydGFudCBwaWVjZSBvZiBpbmZvcm1hdGlvbiwgYmVjYXVzZSBkb2N0b3JzIHdvdWxkIHdhbnQgdG8ga25vdyBob3cgbWFueSBjaWdhcmV0dGVzIHRoZWlyIHBhdGllbnRzIHNtb2tlIGluIGEgZGF5LCBiZWNhdXNlIHRoaXMgaGFzIGEgc2lnbmlmaWNhbnQgaW1wYWN0IG9uIHRoZSBwYXRpZW50J3MgcmlzayBmYWN0b3JzLiBTbywgd2Ugd2lsbCBrZWVwIHRoaXMgdmFyaWFibGUgaW4gdGhlIGZpbmFsIG1vZGVsIGRlc3BpdGUgaXRzIHNrZXcgYmVjYXVzZSBpdCBpcyBhbiBpbXBvcnRhbnQgdmFyaWFibGUgaW4gcmVnYXJkcyB0byBhIHBhdGllbnQncyBwb3RlbnRpYWwgcmlzayBmYWN0b3JzIGZvciBkZXZlbG9waW5nIENIRC4gQnV0LCBpdCBpcyBpbXBvcnRhbnQgdG8ga2VlcCBpbiBtaW5kIHRoYXQgdGhpcyB2YXJpYWJsZSBpcyBza2V3ZWQgZHVlIHRvIGl0IGJlaW5nIHJlcHJlc2VudGF0aXZlIG9mIGJvdGggbm9uLXNtb2tlcnMgYW5kIHNtb2tlcnMuIA0KDQoNCiMjIyBoZWFydFJhdGUgRGlzdHJpYnV0aW9uDQoNClRoZSB2YXJpYWJsZSBoZWFydFJhdGUgd2FzIGFub3RoZXIgb25lIHdoaWNoIGhhZCBzb21lIG1pc3Npbmcgb2JzZXJ2YXRpb25zIHRoYXQgbmVlZGVkIHRvIGJlIGZpbGxlZCBpbiBkdXJpbmcgcHJldmlvdXMgc3RlcHMuIFdlIHdpbGwgY2hlY2sgaXRzIGRpc3RyaWJ1dGlvbiB0byBlbnN1cmUgdGhhdCBldmVyeXRoaW5nIGxvb2tzIGFscmlnaHQuIA0KDQpgYGB7cn0NCnlsaW1pdCA9IG1heChkZW5zaXR5KGhlYXJ0ZGlzZWFzZTEkaGVhcnRSYXRlKSR5KQ0KaGlzdChoZWFydGRpc2Vhc2UxJGhlYXJ0UmF0ZSwgcHJvYmFiaWxpdHkgPSBUUlVFLCBtYWluID0gImhlYXJ0UmF0ZSIsIHhsYWI9ImhlYXJ0UmF0ZSIsIA0KICAgICAgIGNvbCA9ICJhbGljZWJsdWUiLCBib3JkZXI9ImNvcm5mbG93ZXJibHVlIikNCiAgbGluZXMoZGVuc2l0eShoZWFydGRpc2Vhc2UxJGhlYXJ0UmF0ZSwgYWRqdXN0PTIpLCBjb2w9ImRhcmtvcmNoaWQiKQ0KYGBgDQoNClRoZSBkaXN0cmlidXRpb24gZm9yIGhlYXJ0IHJhdGUgaXMgdW5pbW9kYWwgYW5kIGFwcHJveGltYXRlbHkgbm9ybWFsbHkgZGlzdHJpYnV0ZWQgd2l0aG91dCBhbnkgbm90YWJsZSBza2V3IG9yIG91dGxpZXJzIHRoYXQgYXJlIGFwcGFyZW50IGluIGl0cyBkZW5zaXR5IHBsb3QuIFRoaXMgYWxsb3dzIHVzIHRvIGNvbmNsdWRlIHRoYXQgdGhpcyB2YXJpYWJsZSBpcyBzYWZlIHRvIGluY2x1ZGUgd2l0aGluIHRoZSBtdWx0aXBsZSBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsIGJ1aWxkaW5nIHByb2Nlc3MgYW5kIHRoYXQgdGhlIHByb2Nlc3Mgb2YgZmlsbGluZyBpbiB0aGUgbWlzc2luZyB2YWx1ZXMgZGlkIG5vdCBsZWFkIHRvIGFueSBpc3N1ZXMgaW4gaXRzIGRpc3RyaWJ1dGlvbi4NCg0KDQojIyMgT3ZlcmFsbCBGaW5kaW5ncw0KDQpXZSBoYXZlIGNoZWNrZWQgdGhlIGRpc3RyaWJ1dGlvbnMgb2YgdGhlIHZhcmlhYmxlcyB3aGljaCBoYWQgbWlzc2luZyBvYnNlcnZhdGlvbnMgdGhhdCB3ZXJlIGZpbGxlZCBpbiBkdXJpbmcgdGhlIHByZXZpb3VzIHN0ZXBzIG9mIHRoaXMgcHJvamVjdCBhbG9uZyB3aXRoIG90aGVyIGltcG9ydGFudCBxdWFudGl0YXRpdmUgdmFyaWFibGVzIGluIHRoaXMgZGF0YSBzZXQuIFdlIGVuc3VyZWQgdGhhdCB0aGUgdmFyaWFibGVzIGVpdGhlciBmb2xsb3dzIGEgZGlzdHJpYnV0aW9uIHRoYXQgYXBwZWFycyB0byBiZSBhcHByb3hpbWF0ZWx5IG5vcm1hbCwgb3IgYXJlIHByYWN0aWNhbGx5IGltcG9ydGFudCBlbm91Z2ggdG8gYmUgaW5jbHVkZWQgaW4gdGhlIG11bHRpcGxlIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWwgYnVpbGRpbmcgcHJvY2VzcyBvdGhlcndpc2UuIA0KDQpUaGUgb25seSB2YXJpYWJsZSB0aGF0IGRpZCBub3QgYXBwZWFyIHRvIGZvbGxvdyBhbiBhcHByb3hpbWF0ZWx5IG5vcm1hbCBkaXN0cmlidXRpb24gd2FzIGNpZ3NQZXJEYXkuIEhvd2V2ZXIsIHRoZSBza2V3bmVzcyBzZWVuIGluIHRoaXMgdmFyaWFibGUncyBkaXN0cmlidXRpb24gY2FuIGJlIGF0dHJpYnV0ZWQgdG8gdGhlIGZhY3QgdGhhdCB0aGlzIGRhdGEgd2FzIGNvbGxlY3RlZCBhbW9uZ3N0IGJvdGggc21va2VycyBhbmQgbm9uLXNtb2tlcnMuIFNvLCBub24tc21va2VycyByZXBvcnRlZCB2YWx1ZXMgb2YgMCBjaWdhcmV0dGVzIHNtb2tlZCBwZXIgZGF5LCB3aGlsZSBzbW9rZXJzIHJlcG9ydGVkIHZhbHVlcyB0aGF0IHdlcmUgZ3JlYXRlciB0aGFuIHplcm8uIFRoaXMgYXBwZWFyZWQgdG8gdGhlIGRpc3RyaWJ1dGlvbiBmb3IgdGhpcyB2YXJpYWJsZSBhcHBlYXJpbmcgdG8gYmUgc2tld2VkIHRvIHRoZSByaWdodC4gSG93ZXZlciwgdGhpcyBpcyBhIHZhcmlhYmxlIHRoYXQgaXMgcHJhY3RpY2FsbHkgaW1wb3J0YW50IGJlY2F1c2UgdGhlIG51bWJlciBvZiBjaWdhcmV0dGVzIGEgcGF0aWVudCBzbW9rZXMgcGVyIGRheSBjYW4gaGF2ZSBhIHNpZ25pZmljYW50IGltcGFjdCBvbiB0aGVpciBvZGRzIG9mIGJlaW5nIGF0IHJpc2sgZm9yIGRldmVsb3BpbmcgQ0hELiBEdWUgdG8gdGhlIHByYWN0aWNhbCBpbXBvcnRhbmNlIG9mIHRoaXMgdmFyaWFibGUsIGl0IG11c3Qgc3RpbGwgYmUgaW5jbHVkZWQgd2l0aGluIHRoZSBtdWx0aXBsZSBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsIGJ1aWxkaW5nIHByb2Nlc3MuDQoNCkFsdG9nZXRoZXIsIHdlIGhhdmUgZW5zdXJlZCB0aGF0IHRoZSB2YXJpYWJsZXMgaW4gdGhpcyBkYXRhIHNldCBhcHBlYXIgdG8gYmUgZ29vZCBlbm91Z2ggdG8gdXNlIHdpdGhpbiB0aGUgbW9kZWwgYnVpbGRpbmcgcHJvY2Vzcy4gU28sIG5vdyB3ZSB3aWxsIGJlZ2luIHdpdGggdGhlIHByb2Nlc3Mgb2YgYnVpbGRpbmcgdGhlIGNhbmRpZGF0ZSBtdWx0aXBsZSBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVscy4gDQoNCg0KDQoNCiMgVmFyaWFibGUgVHJhbnNmb3JtYXRpb24tIFN0YW5kYXJkaXppbmcgdGhlIFF1YW50aXRhdGl2ZSBWYXJpYWJsZXMNCg0KDQpUaGUgbWFpbiBnb2FsIGZvciB0aGlzIHByb2plY3QgaXMgdG8gZmluZCBhIG1vZGVsIHdpdGggdGhlIGJlc3QgcHJlZGljdGl2ZSBwZXJmb3JtYW5jZSBhbmQgcG93ZXIuIEluIG9yZGVyIHRvIGRvIHNvLCB3ZSB3aWxsIHN0YW5kYXJkaXplIHRoZSBxdWFudGl0YXRpdmUgdmFyaWFibGVzIGluIHRoZSBkYXRhIHNldCB0byBlbnN1cmUgdGhhdCBvdXIgZGF0YSB3aWxsIGJlIGl0cyBiZXN0IGZvciBwcmVkaWN0aXZlIHBlcmZvcm1hbmNlLiANCg0KV2Ugd2lsbCBzdGFuZGFyZGl6ZSBhbGwgb2YgdGhlIG51bWVyaWMsIHF1YW50aXRhdGl2ZSB2YXJpYWJsZXMgaW4gb3JkZXIgdG8gZ2l2ZSBvdXIgZGF0YSB0aGUgYmVzdCBwcmVkaWN0aXZlIHBvd2VyIGZvciB0aGUgZnV0dXJlIG1vZGVsIGJ1aWxkaW5nIHdlIHdpbGwgY29uZHVjdCBpbiB0aGUgZm9sbG93aW5nIHN0ZXBzLiANCg0KYGBge3J9DQojIEFnZQ0KaGVhcnRkaXNlYXNlMSRzZC5hZ2UgPSAoaGVhcnRkaXNlYXNlMSRhZ2UtbWVhbihoZWFydGRpc2Vhc2UxJGFnZSkpL3NkKGhlYXJ0ZGlzZWFzZTEkYWdlKQ0KDQojIGNpZ3NQZXJEYXkNCmhlYXJ0ZGlzZWFzZTEkc2QuY2lnc1BlckRheSA9IChoZWFydGRpc2Vhc2UxJGNpZ3NQZXJEYXktbWVhbihoZWFydGRpc2Vhc2UxJGNpZ3NQZXJEYXkpKS9zZChoZWFydGRpc2Vhc2UxJGNpZ3NQZXJEYXkpDQoNCiMgdG90Q2hvbA0KaGVhcnRkaXNlYXNlMSRzZC50b3RDaG9sID0gKGhlYXJ0ZGlzZWFzZTEkdG90Q2hvbC1tZWFuKGhlYXJ0ZGlzZWFzZTEkdG90Q2hvbCkpL3NkKGhlYXJ0ZGlzZWFzZTEkdG90Q2hvbCkNCg0KIyBzeXNCUA0KaGVhcnRkaXNlYXNlMSRzZC5zeXNCUCA9IChoZWFydGRpc2Vhc2UxJHN5c0JQLW1lYW4oaGVhcnRkaXNlYXNlMSRzeXNCUCkpL3NkKGhlYXJ0ZGlzZWFzZTEkc3lzQlApDQoNCiMgZGlhQlANCmhlYXJ0ZGlzZWFzZTEkc2QuZGlhQlAgPSAoaGVhcnRkaXNlYXNlMSRkaWFCUC1tZWFuKGhlYXJ0ZGlzZWFzZTEkZGlhQlApKS9zZChoZWFydGRpc2Vhc2UxJGRpYUJQKQ0KDQojIEJNSQ0KaGVhcnRkaXNlYXNlMSRzZC5CTUkgPSAoaGVhcnRkaXNlYXNlMSRCTUktbWVhbihoZWFydGRpc2Vhc2UxJEJNSSkpL3NkKGhlYXJ0ZGlzZWFzZTEkQk1JKQ0KDQojIGhlYXJ0UmF0ZQ0KaGVhcnRkaXNlYXNlMSRzZC5oZWFydFJhdGUgPSAoaGVhcnRkaXNlYXNlMSRoZWFydFJhdGUtbWVhbihoZWFydGRpc2Vhc2UxJGhlYXJ0UmF0ZSkpL3NkKGhlYXJ0ZGlzZWFzZTEkaGVhcnRSYXRlKQ0KDQojIGdsdWNvc2UNCmhlYXJ0ZGlzZWFzZTEkc2QuZ2x1Y29zZSA9IChoZWFydGRpc2Vhc2UxJGdsdWNvc2UtbWVhbihoZWFydGRpc2Vhc2UxJGdsdWNvc2UpKS9zZChoZWFydGRpc2Vhc2UxJGdsdWNvc2UpDQpgYGANCg0KV2Ugd2lsbCBjcmVhdGUgYSBuZXcgZGF0YSBvYmplY3QgdG8gc3RvcmUgdGhlIG5ldyBzdGFuZGFyZGl6ZWQgdmFyaWFibGVzIGFsb25nIHdpdGggYWxsIG9mIHRoZSBiaW5hcnkgYW5kIGNhdGVnb3JpY2FsIHByZWRpY3RvciB2YXJpYWJsZXMgZnJvbSB0aGUgcHJldmlvdXMgZGF0YSBzZXQuIFdlIHdpbGwgY2FsbCB0aGUgbmV3IGRhdGEgc2V0IHdpdGggYWxsIG9mIHRoZSBzdGFuZGFyZGl6ZWQgcXVhbnRpdGF0aXZlIHZhcmlhYmxlcyAic2QuaGVhcnRkaXNlYXNlIi4gDQoNCmBgYHtyfQ0KIyBOZXcgZGF0YSBzZXQgd2l0aCBvcmlnaW5hbCwgbm9uLXN0YW5kYXJkaXplZCB2YXJpYWJsZXMgZHJvcHBlZC4NCnNkLmhlYXJ0ZGlzZWFzZSA9IHNlbGVjdChoZWFydGRpc2Vhc2UxLCAtMiwtNSwtMTA6LTE1KQ0KYGBgDQoNCk5vdywgYWxsIG9mIHRoZSBxdWFudGl0YXRpdmUsIG51bWVyaWNhbCB2YXJpYWJsZXMgaW4gdGhlIG9yaWdpbmFsIGRhdGEgc2V0IGhhdmUgYmVlbiBzdGFuZGFyZGl6ZWQgaW4gb3JkZXIgdG8gZW5zdXJlIHRoYXQgb3VyIGZ1dHVyZSBtb2RlbCB3aWxsIGhhdmUgdGhlIGJlc3QgcHJlZGljdGl2ZSBwb3dlciBiZWNhdXNlIHRoZSBnb2FsIG9mIHRoaXMgcHJvamVjdCBpcyB0byBjcmVhdGUgYSBtb2RlbCB3aXRoIHRoZSBiZXN0IHByZWRpY3RpdmUgcG93ZXIgYW5kIHBlcmZvcm1hbmNlLiANCg0KDQoNCg0KIyBEYXRhIFNwbGl0DQoNCldlIHdpbGwgbm93IHNwbGl0IHRoZSBkYXRhIHVwIGludG8gdHJhaW5pbmcgYW5kIHRlc3RpbmcgZGF0YS4gV2Ugd2lsbCByYW5kb21seSBzcGxpdCB0aGUgZGF0YSBzbyB0aGF0IDgwJSBvZiB0aGUgZGF0YSB3aWxsIGJlIHVzZWQgZm9yIHRyYWluaW5nIGFuZCAyMCUgb2YgdGhlIGRhdGEgd2lsbCBiZSB1c2VkIGZvciB0ZXN0aW5nLiBCeSBzcGxpdHRpbmcgdGhlIGRhdGEgdXAgaW50byB0aGUgdHJhaW5pbmcgYW5kIHRoZSB0ZXN0aW5nIGRhdGEsIHRoaXMgd2lsbCBhbGxvdyB1cyB0byBidWlsZCB0aGUgY2FuZGlkYXRlIG1vZGVscyBhbmQgYW5hbHl6ZSB0aGVpciBwcmVkaWN0aXZlIHBlcmZvcm1hbmNlcy4gV2Ugd2lsbCB1c2UgdGhlIHNwbGl0IGRhdGEgdG8gY29uZHVjdCB0aGUgY3Jvc3MgdmFsaWRhdGlvbiBwcm9jZXNzIHRvIGFuYWx5c2lzIHRoZSBwcmVkaWN0aXZlIHBvd2VyIGFuZCBwZXJmb3JtYW5jZXMgb2YgZWFjaCBvZiB0aGUgdGhyZWUgY2FuZGlkYXRlIG1vZGVscyB3ZSB3aWxsIGludmVzdGlnYXRlLiBUaGlzIHdpbGwgYWxsb3cgdXMgdG8gZXZlbnR1YWxseSBkZXRlcm1pbmUgd2hpY2ggb2YgdGhlc2UgdGhyZWUgbW9kZWxzIGlzIHRoZSBpZGVhbCBjaG9pY2UgZm9yIG91ciBmaW5hbCBtb2RlbCBiYXNlZCB1cG9uIGl0cyBvdmVyYWxsIHByZWRpY3RpdmUgcG93ZXIuIA0KDQpgYGB7cn0NCiMgU3BsaXQgdGhlIGRhdGEgaW50byA4MCUgdHJhaW5pbmcgYW5kIDIwJSB0ZXN0aW5nLg0KbiA8LSBkaW0oc2QuaGVhcnRkaXNlYXNlKVsxXQ0KdHJhaW4ubiA8LSByb3VuZCgwLjgqbikNCnRyYWluLmlkIDwtIHNhbXBsZSgxOm4sIHRyYWluLm4sIHJlcGxhY2UgPSBGQUxTRSkNCg0KdHJhaW4gPC0gc2QuaGVhcnRkaXNlYXNlW3RyYWluLmlkLCBdDQp0ZXN0IDwtIHNkLmhlYXJ0ZGlzZWFzZVstdHJhaW4uaWQsIF0NCmBgYA0KDQpOb3csIG91ciBkYXRhIGhhcyBiZWVuIHNwbGl0IHVwIGludG8gYSB0cmFpbmluZyBhbmQgYSB0ZXN0aW5nIGRhdGEgc2V0LCB3aXRoIDgwJSBvZiB0aGUgZGF0YSBmb3IgdHJhaW5pbmcgYW5kIHRoZSByZW1haW5pbmcgMjAlIGZvciB0ZXN0aW5nLiBUaGUgb2JzZXJ2YXRpb25zIHdlcmUgc3BsaXQgdXAgcmFuZG9tbHkgYW1vbmdzdCB0aGVzZSB0d28gZGF0YSBzZXRzLiBXZSB3aWxsIHNwZWNpZmljYWxseSB1c2UgdGhlIHRyYWluaW5nIGRhdGEgZm9yIHRoZSBjcm9zcyB2YWxpZGF0aW9uIHByb2Nlc3MuIFRoaXMgcHJvY2VzcyB3aWxsIGFsbG93IHVzIHRvIGlkZW50aWZ5IHdoaWNoIG11bHRpcGxlIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWwgaXMgdGhlIGJlc3QgbW9kZWwgZm9yIHRoZSBwdXJwb3NlIG9mIHByZWRpY3Rpb24uIFRoaXMgd2lsbCBhbGxvdyB1cyB0byBzZWxlY3QgYSBtb2RlbCB3aGljaCBwcmVkaWN0cyB0aGUgb2RkcyBvZiBhbiBpbmRpdmlkdWFsIGJlaW5nIGF0IHJpc2sgZm9yIGRldmVsb3BpbmcgQ0hEIHdpdGggdGhlIGhpZ2hlc3QgcXVhbGl0eSBvZiBwcmVkaWN0aW9uLiANCg0KDQoNCg0KIyBCZXN0IE1vZGVsIElkZW50aWZpY2F0aW9uDQoNCk5vdyB0aGF0IG91ciB2YXJpYWJsZXMgaGF2ZSBiZWVuIGNoZWNrZWQgYW5kIHN0YW5kYXJkaXplZCB0byBlbnN1cmUgdGhlaXIgcHJlZGljdGl2ZSBwZXJmb3JtYW5jZSBiZWluZyB0aGUgYmVzdCB0aGF0IGl0IHBvc3NpYmx5IGNhbiBiZSwgd2Ugd2lsbCBiZWdpbiB3aXRoIHRoZSBtb2RlbCBidWlsZGluZyBwcm9jZXNzLiBXZSB3aWxsIGxvb2sgYXQgdGhyZWUgcG90ZW50aWFsLCBjYW5kaWRhdGUgbW9kZWxzIGZvciB0aGUgbXVsdGlwbGUgbG9naXN0aWMgcmVncmVzc2lvbiBhbmQgZGV0ZXJtaW5lIHdoaWNoIG9uZSBoYXMgdGhlIGJlc3QgcHJlZGljdGl2ZSBwZXJmb3JtYW5jZS4gDQoNCiMjIERlc2NyaXB0aW9ucyBvZiB0aGUgVGhyZWUgQ2FuZGlkYXRlIE1vZGVscyANCg0KV2Ugd2lsbCB0YWtlIGEgbG9vayBpbnRvIHRoZSBzcGVjaWZpY2F0aW9ucyBvZiB0aGUgdGhyZWUgY2FuZGlkYXRlIG11bHRpcGxlIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWxzLiANCg0KDQojIyMgRnVsbCBNb2RlbA0KDQpUaGUgZmlyc3Qgb2YgdGhlIHRocmVlIG1vZGVscyB3aWxsIGJlIHRoZSBmdWxsIG1vZGVsLCB3aXRoIGFsbCBvZiB0aGUgdmFyaWFibGVzIGluY2x1ZGVkLiBUaGlzIGZ1bGwgbW9kZWwgd2lsbCB1c2UgYWxsIG9mIHRoZSB2YXJpYWJsZXMgaW4gb3JkZXIgdG8gY3JlYXRlIGEgbXVsdGlwbGUgbG9naXN0aWMgcmVncmVzc2lvbiB0byBwcmVkaWN0IHRoZSBvZGRzIG9mIGEgcGF0aWVudCBiZWluZyBhdCByaXNrIGZvciBkZXZlbG9waW5nIENIRCBpbiBhIDEwLXllYXIgcGVyaW9kLiANCg0KVGhlICByZWdyZXNzaW9uIGVxdWF0aW9uIGZvciB0aGlzIGZ1bGwgbW9kZWwgaXMgcmVwcmVzZW50ZWQgYXM6DQoNClRlblllYXJDSEQgPSBtYWxlICsgc2QuYWdlICsgZWR1Y2F0aW9uICsgY3VycmVudFNtb2tlciArIHNkLmNpZ3NQZXJEYXkgKyBCUE1lZHMgKyBwcmV2YWxlbnRTdHJva2UgKyBwcmV2YWxlbnRIeXAgKyBkaWFiZXRlcyArIHNkLnRvdENob2wgKyBzZC5zeXNCUCArIHNkLmRpYUJQICsgc2QuQk1JICsgc2QuaGVhcnRSYXRlICsgc2QuZ2x1Y29zZQ0KDQpXZSB3aWxsIGZpbmQgdGhlIHNwZWNpZmljIG11bHRpcGxlIGxvZ2lzaXRpYyByZWdyZXNzaW9uIGVxdWF0aW9uIGZvciB0aGlzIGZ1bGwgbW9kZWwuDQoNCmBgYHtyfQ0KZnVsbC5tb2RlbCA9IGdsbShUZW5ZZWFyQ0hEIH4gbWFsZSArIHNkLmFnZSArIGVkdWNhdGlvbiArIGN1cnJlbnRTbW9rZXIgKyBzZC5jaWdzUGVyRGF5ICsgQlBNZWRzICsgcHJldmFsZW50U3Ryb2tlICsgcHJldmFsZW50SHlwICsgZGlhYmV0ZXMgKyBzZC50b3RDaG9sICsgc2Quc3lzQlAgKyBzZC5kaWFCUCArIHNkLkJNSSArIHNkLmhlYXJ0UmF0ZSArIHNkLmdsdWNvc2UsIA0KICAgICAgICAgIGZhbWlseSA9IGJpbm9taWFsKGxpbmsgPSAibG9naXQiKSwgIA0KICAgICAgICAgIGRhdGEgPSBzZC5oZWFydGRpc2Vhc2UpICANCmthYmxlKHN1bW1hcnkoZnVsbC5tb2RlbCkkY29lZiwgDQogICAgICBjYXB0aW9uID0gIkZ1bGwgTW9kZWwgU3VtbWFyeSBvZiB0aGUgSW5mZXJlbnRpYWwgU3RhdGlzdGljcyIpDQpgYGANCg0KVGhlIGVxdWF0aW9uIGZvciB0aGUgbXVsdGlwbGUgbG9naXN0aWMgcmVncmVzc2lvbiBlcXVhdGlvbiBvZiB0aGUgZnVsbCBtb2RlbCBpcyBnaXZlbiBhcyBmb2xsb3dzOg0KDQpsb2cgcC8oMS1wKSA9IC0yLjIwMjQgKyAwLjQ4NTIgKiBtYWxlICsgMC41MTY5ICogc2QuYWdlIC0gMC4yMzU2ICogZWR1Y2F0aW9uMiAtIDAuMTAyNiAqIGVkdWNhdGlvbjMgKyAwLjAxMTUgKiBlZHVjYXRpb240ICsgMC4wMjE5ICogY3VycmVudFNtb2tlcisgMC4yNTA3ICogc2QuY2lnc1BlckRheSArIDAuMzI3MCAqIEJQTWVkcyArIDAuOTM4OSAqIHByZXZhbGVudFN0cm9rZSArIDAuMjMxMiAqIHByZXZhbGVudEh5cCArIDAuMDkxMiAqIGRpYWJldGVzICsgMC4wODAzICogc2QudG90Q2hvbCArIDAuMzA3NSAqIHNkLnN5c0JQIC0gMC4wMzI3ICogc2QuZGlhQlAgKyAwLjAwNzEgKiBzZC5CTUkgLSAwLjAxMzcgKiBzZC5oZWFydFJhdGUgKyAwLjE3MzAgKiBzZC5nbHVjb3NlDQoNCg0KIyMjIFJlZHVjZWQgTW9kZWwNCg0KVGhlIHNlY29uZCBvZiB0aGUgdGhyZWUgbW9kZWxzIHdpbGwgYmUgdGhlIHJlZHVjZWQgbW9kZWwuIFRoaXMgbW9kZWwgd2lsbCBjb25zaXN0IG9ubHkgb2YgdGhlIG1vc3QgcHJhY3RpY2FsbHkgaW1wb3J0YW50IHZhcmlhYmxlcyB3aXRoaW4gdGhlIGNvbnRleHQgb2YgdGhpcyBzaXR1YXRpb24sIHVzaW5nIHZhcmlvdXMgZmFjdG9ycyB0byBiZXN0IHByZWRpY3QgdGhlIG9kZHMgb2YgYW4gaW5kaXZpZHVhbCBiZWluZyBhdCByaXNrIGZvciBkZXZlbG9waW5nIENIRC4gVGhlIFVuaXRlZCBTdGF0ZXMgTmF0aW9uYWwgTGlicmFyeSBvZiBNZWRpY2luZSBvZiB0aGUgTmF0aW9uYWwgSW5zdGl0dXRlcyBvZiBIZWFsdGggKE5JSCksIHRoZSBtb3N0IG5vdGFibGUgcmlzayBmYWN0b3JzIGZvciBhbiBpbmRpdmlkdWFsIGJlaW5nIGF0IHJpc2sgb2YgZGV2ZWxvcGluZyBDSEQgYXJlIGNpZ2FyZXR0ZSBzbW9raW5nLCBibG9vZCBwcmVzc3VyZSwgYW5kIGNob2xlc3Rlcm9sIGxldmVscy4gSW4gdGhpcyBkYXRhIHNldCwgdGhlcmUgYXJlIHR3byB2YXJpYWJsZXMgcmVsYXRlZCB0byBzbW9raW5nLCBjdXJyZW50U21va2VyLCB3aGV0aGVyIGEgcGF0aWVudCBpcyBjdXJyZW50bHkgYSBzbW9rZXIgb3Igbm90LCBhbmQgY2lnc1BlckRheSB3aGljaCBpcyB3YXMgc2NhbGVkIHRvIHNkLmNpZ3NQZXJEYXksIGhvdyBtYW55IGNpZ2FyZXR0ZXMgYSBwYXRpZW50IHNtb2tlcyBpbiBhIGRheS4gVGhlcmUgYXJlIGFsc28gdHdvIHZhcmlhYmxlcyByZWxhdGVkIHRvIGJsb29kIHByZXNzdXJlLCBzeXNCUCBhbmQgZGlhQlAsIGZvciBzeXN0b2xpYyBibG9vZCBwcmVzc3VyZSBhbmQgZGlhc3RvbGljIGJsb29kIHByZXNzdXJlLiBCb3RoIG9mIHRoZXNlIGJsb29kIHByZXNzdXJlIHZhcmlhYmxlcyB3ZXJlIHNjYWxlZCBhbmQgYXJlIG5vdyBnaXZlbiBhcyBzZC5zeXNCUCBhbmQgc2QuZGlhQlAuIFRoZXJlIGlzIGFsc28gYSB2YXJpYWJsZSBpbmNsdWRlZCBpbiB0aGUgZGF0YSBzZXQgZm9yIGEgcGF0aWVudCdzIHRvdGFsIGNob2xlc3Rlcm9sIGxldmVscywgdG90Q2hvbCwgd2hpY2ggd2FzIHNjYWxlZCBhbmQgaXMgbm93IGdpdmVuIGFzIHNkLnRvdENob2wuIFRoZXNlIGZpdmUgdmFyaWFibGVzIGluIHRoZSBkYXRhIHNldCByZXByZXNlbnQgdGhlIGtleSByaXNrIGZhY3RvcnMgdGhhdCB0aGUgbWVkaWNhbCByZXBvcnQgZnJvbSB0aGUgTklIIHN0YXRlZCBhcyBiZWluZyB0aGUgbW9zdCBzaWduaWZpY2FudCBpbiBwcmVkaWN0aW5nIGEgcGF0aWVudCdzIHJpc2sgZm9yIENIRC4gU28sIG91ciByZWR1Y2VkIG1vZGVsIHdpbGwgY29uc2lzdCBvbmx5IG9mIHRoZXNlIHBhcnRpY3VsYXIgdmFyaWFibGVzLiANCg0KVGhlIGdlbmVyYWwgcmVncmVzc2lvbiBlcXVhdGlvbiBmb3IgdGhpcyByZWR1Y2VkIG1vZGVsIGlzIHJlcHJlc2VudGVkIGFzOg0KDQpUZW5ZZWFyQ0hEID0gY3VycmVudFNtb2tlciArIHNkLmNpZ3NQZXJEYXkgKyBzZC5zeXNCUCArIHNkLmRpYUJQICsgc2QudG90Q2hvbA0KDQpXZSB3aWxsIGZpbmQgdGhlIHNwZWNpZmljIG11bHRpcGxlIGxvZ2lzdGljIHJlZ3Jlc3Npb24gZXF1YXRpb24gZm9yIHRoaXMgcmVkdWNlZCBtb2RlbC4NCg0KYGBge3J9DQpyZWR1Y2VkLm1vZGVsID0gZ2xtKFRlblllYXJDSEQgfiBjdXJyZW50U21va2VyICsgc2QuY2lnc1BlckRheSArIHNkLnN5c0JQICsgc2QuZGlhQlAgKyBzZC50b3RDaG9sLCANCiAgICAgICAgICBmYW1pbHkgPSBiaW5vbWlhbChsaW5rID0gImxvZ2l0IiksDQogICAgICAgICAgZGF0YSA9IHNkLmhlYXJ0ZGlzZWFzZSkgDQprYWJsZShzdW1tYXJ5KHJlZHVjZWQubW9kZWwpJGNvZWYsIA0KICAgICAgY2FwdGlvbiA9ICJSZWR1Y2VkIE1vZGVsIFN1bW1hcnkgb2YgdGhlIEluZmVyZW50aWFsIFN0YXRpc3RpY3MiKQ0KYGBgDQoNClRoZSBlcXVhdGlvbiBmb3IgdGhlIG11bHRpcGxlIGxvZ2lzdGljIHJlZ3Jlc3Npb24gZXF1YXRpb24gb2YgdGhlIHJlZHVjZWQgbW9kZWwgaXMgZ2l2ZW4gYXMgZm9sbG93czoNCg0KbG9nIHAvKDEtcCkgPSAtMS43Njc1IC0gMC4xNDQzICogY3VycmVudFNtb2tlciArIDAuMjY4MyAqIHNkLmNpZ3NQZXJEYXkgKyAwLjYzODEgKiBzZC5zeXNCUCAtIDAuMTQwOSAqIHNkLmRpYUJQICsgMC4xMTE0ICogc2QudG90Q2hvbA0KDQoNCiMjIyBGb3J3YXJkIFNlbGVjdGlvbiBNb2RlbA0KDQpUaGUgdGhpcmQgb2YgdGhyZWUgY2FuZGlkYXRlIG1vZGVscyB3aWxsIGJlIHRoZSBtb2RlbCB0aGF0IHdhcyBmb3VuZCB0aHJvdWdoIHRoZSBhdXRvbWF0aWMgdmFyaWFibGUgc2VsZWN0aW9uIHByb2Nlc3MsIHNwZWNpZmljYWxseSB3aXRoIHVzaW5nIGZvcndhcmQgc2VsZWN0aW9uLiBXZSB3aWxsIGNhbGwgdGhpcyB0aGUgZnMubW9kZWwgZm9yIHRoaXMgcHJvamVjdCB0byByZXByZXNlbnQgaXQgYmVpbmcgdGhlIG1vZGVsIGZvdW5kIHRocm91Z2ggdGhlIHVzZSBvZiBmb3J3YXJkIHNlbGVjdGlvbiBvZiB0aGUgdmFyaWFibGVzLiANCg0KV2Ugd2lsbCBmaW5kIHRoZSBzcGVjaWZpYyBtdWx0aXBsZSBsb2dpc3RpYyByZWdyZXNzaW9uIGVxdWF0aW9uIGZvciB0aGlzIGZvcndhcmQgc2VsZWN0aW9uIG1vZGVsLg0KDQpgYGB7cn0NCmZzLm1vZGVsID0gc3RlcEFJQyhyZWR1Y2VkLm1vZGVsLCBzY29wZSA9IGxpc3QobG93ZXI9Zm9ybXVsYShyZWR1Y2VkLm1vZGVsKSwgdXBwZXI9Zm9ybXVsYShmdWxsLm1vZGVsKSksDQogICAgICAgICAgICAgICAgICAgICAgZGlyZWN0aW9uID0gImZvcndhcmQiLCAgDQogICAgICAgICAgICAgICAgICAgICAgdHJhY2UgPSAwICAgDQogICAgICAgICAgICAgICAgICAgICAgKQ0Ka2FibGUoc3VtbWFyeShmcy5tb2RlbCkkY29lZiwgDQogICAgICBjYXB0aW9uPSJGaW5hbCBNb2RlbCBTdW1tYXJ5IG9mIHRoZSBJbmZlcmVudGlhbCBTdGF0aXN0aWNzIikNCmBgYA0KDQpUaGUgZXF1YXRpb24gZm9yIHRoZSBtdWx0aXBsZSBsb2dpc3RpYyByZWdyZXNzaW9uIGVxdWF0aW9uIG9mIHRoZSBmb3J3YXJkIHNlbGVjdGlvbiBtb2RlbCBpcyBnaXZlbiBhcyBmb2xsb3dzOg0KDQpsb2cgcC8oMS1wKSA9IC0yLjI4OTggKyAwLjAwODQgKiBjdXJyZW50U21va2VyICsgMC4yNDk3ICogc2QuY2lnc1BlckRheSArIDAuMjQ5NyAqIHNkLnN5c0JQIC0gMC4wMzI1ICogc2QuZGlhQlAgKyAwLjA3NTUgKiBzZC50b3RDaG9sICsgMC41MzY0ICogc2QuYWdlICsgMC41MTY0ICogbWFsZSArIDAuMTgyNiAqIHNkLmdsdWNvc2UgKyAwLjk0MTMgKiBwcmV2YWxlbnRTdHJva2UgKyAwLjIzMTAgKiBwcmV2YWxlbnRIeXAgKyAwLjMyMzMgKiBCUE1lZHMgDQoNClRoZSB2YXJpYWJsZXMgdGhhdCB3ZXJlIGtlcHQgaW4gdGhpcyBtb2RlbCBhZnRlciBmb3J3YXJkIHNlbGVjdGlvbiB3ZXJlIGN1cnJlbnRTbW9rZXIsIHNkLmNpZ3NQZXJEYXksIHNkLnN5c0JQLCBzZC5kaWFCUCwgc2QudG90Q2hvbCwgc2QsYWdlLCBtYWxlLCBzZC5nbHVjb3NlLCBwcmV2YWxlbnRTdHJva2UsIHByZXZhbGVudEh5cCwgYW5kIEJQTWVkcy4gDQoNCg0KIyMgQ3Jvc3MgVmFsaWRhdGlvbg0KDQpOb3cgdGhhdCB3ZSd2ZSBsb29rZWQgaW50byB0aGUgc3BlY2lmaWNhdGlvbnMgb2YgdGhlIHRocmVlIHBvdGVudGlhbCBjYW5kaWRhdGUgbW9kZWxzLCB3ZSB3aWxsIHVzZSBjcm9zcy12YWxpZGF0aW9uIHRvIGRldGVybWluZSB3aGljaCBvZiB0aGVzZSB0aHJlZSBtb2RlbHMgd2Ugc2hvdWxkIHVzZS4gV2Ugd2FudCB0byBzZWUgd2hpY2ggbW9kZWwgaGFzIHRoZSBiZXN0IHByZWRpY3RpdmUgcGVyZm9ybWFuY2UuIFdlIHdpbGwgdXNlIGEgNS1mb2xkIGNyb3NzIHZhbGlkYXRpb24gdG8gY29uZHVjdCB0aGUgcHJvY2VzcyBvZiBmaW5kaW5nIHRoZSBwcmVkaWN0aXZlIGVycm9ycyBmb3IgZWFjaCBvZiB0aGUgdGhyZWUgY2FuZGlkYXRlIG1vZGVscy4gV2Ugd2lsbCB1c2UgdGhpcyA1LWZvbGQgY3Jvc3MgdmFsaWRhdGlvbiBwcm9jZXNzIHRvIGNhbGN1bGF0ZSB0aGUgYXZlcmFnZSBwcmVkaWN0aXZlIGVycm9ycyBmb3IgZWFjaCBvZiB0aGUgdGhyZWUgY2FuZGlkYXRlIG1vZGVscywgdGhlIGZ1bGwgbW9kZWwsIHRoZSByZWR1Y2VkIG1vZGVsLCBhbmQgdGhlIGZvcndhcmQgc2VsZWN0aW9uIG1vZGVsLiBXZSBhcmUgbG9va2luZyBmb3Igd2hpY2ggbW9kZWwgaGFzIHRoZSBsb3dlc3QgdmFsdWUgb2YgYXZlcmFnZSBwcmVkaWN0aXZlIGVycm9ycy4gVGhpcyBtb2RlbCB3aXRoIHRoZSBsb3dlc3QgdmFsdWUgZm9yIHRoZSBhdmVyYWdlIHByZWRpY3RpdmUgZXJyb3JzIHdpbGwgYmUgdGhlIGlkZWFsIGNob2ljZSwgYXMgaGF2aW5nIHRoZSBsb3dlc3QgcHJlZGljdGl2ZSBlcnJvciB2YWx1ZSBpbmRpY2F0ZXMgYSBoaWdoZXIgcHJlZGljdGl2ZSBwb3dlciBhbmQgbW9yZSBhY2N1cmF0ZSBwcmVkaWN0aW9ucyB0aGFuIGEgbW9kZWwgd2l0aCBoaWdoZXIgYXZlcmFnZSBwcmVkaWN0aXZlIGVycm9yIHdvdWxkLiANCg0KRm9yIG91ciBjcm9zcyB2YWxpZGF0aW9uIHByb2Nlc3MsIHdlIHdpbGwgdXNlIGEgY3V0IG9mZiBwcm9iYWJpbGl0eSBvZiAwLjUgdG8gZGVmaW5lIGJldHdlZW4gdGhlIGJpbmFyeSByZXNwb25zZSB2YWx1ZXMgb2YgMSwgeWVzLCBhbmQgMCwgbm8uIFRoZXNlIHZhbHVlcyByZXByZXNlbnQgd2hldGhlciBhbiBpbmRpdmlkdWFsIHdpbGwgYmUgc3RhdGVkIGFzIGJlaW5nIGF0IHJpc2sgZm9yIGRldmVsb3BpbmcgQ0hEIGluIGEgMTAteWVhciBwZXJpb2QgYmFzZWQgdXBvbiB0aGUgbXVsdGlwbGUgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbCB0aGF0IHdhcyB1c2VkIGZvciBwcmVkaWN0aW9uLg0KDQpXZSB3aWxsIGxhYmVsIHRoZSBmdWxsIG1vZGVsIGFzIG91ciBmaXJzdCBtb2RlbCAoMDEpLCB0aGUgcmVkdWNlZCBtb2RlbCBhcyBvdXIgc2Vjb25kIG1vZGVsICgwMiksIGFuZCB0aGUgZm9yd2FyZCBzZWxlY3Rpb24gbW9kZWwgYXMgb3VyIHRoaXJkIG1vZGVsICgwMykgZm9yIHRoaXMgY3Jvc3MtdmFsaWRhdGlvbiBwcm9jZXNzLiANCg0KYGBge3J9DQojIyA1IEZvbGQgQ3Jvc3MgVmFsaWRhdGlvbg0KayA9IDUNCmZvbGQuc2l6ZSA9IGZsb29yKGRpbSh0cmFpbilbMV0vaykNClBFMSA9IHJlcCgwLDUpDQpQRTIgPSByZXAoMCw1KQ0KUEUzID0gcmVwKDAsNSkNCg0KIyBUcmFpbmluZyBhbmQgVGVzdGluZyBGb2xkIGZvciB0aGUgTW9kZWxzDQpmb3IoaSBpbiAxOmspew0KICANCiB2YWxpZC5pZCA9IChmb2xkLnNpemUqKGktMSkrMSk6KGZvbGQuc2l6ZSppKQ0KICB2YWxpZCA9IHRyYWluW3ZhbGlkLmlkLCBdDQogIHRyYWluLmRhdGEgPSB0cmFpblstdmFsaWQuaWQsXQ0KICANCiMgRnVsbCBNb2RlbDogYWxsIHZhcmlhYmxlcw0KZnVsbC5tb2RlbCA9IGdsbShUZW5ZZWFyQ0hEIH4gbWFsZSArIHNkLmFnZSArIGVkdWNhdGlvbiArIGN1cnJlbnRTbW9rZXIgKyAgc2QuY2lnc1BlckRheSArIEJQTWVkcyArIHByZXZhbGVudFN0cm9rZSArIHByZXZhbGVudEh5cCArIGRpYWJldGVzICsgc2QudG90Q2hvbCArIHNkLnN5c0JQICsgc2QuZGlhQlAgKyBzZC5CTUkgKyBzZC5oZWFydFJhdGUgKyBzZC5nbHVjb3NlLCANCiAgICAgICAgICBmYW1pbHkgPSBiaW5vbWlhbChsaW5rID0gImxvZ2l0IiksICANCiAgICAgICAgICBkYXRhID0gdHJhaW4uZGF0YSkNCg0KIyMgUmVkdWNlZCBNb2RlbDogb25seSB0aGUgbW9zdCBwcmFjdGljYWxseSBpbXBvcnRhbnQgdmFyaWFibGVzLg0KcmVkdWNlZC5tb2RlbCA9IGdsbShUZW5ZZWFyQ0hEIH4gY3VycmVudFNtb2tlciArIHNkLmNpZ3NQZXJEYXkgKyBzZC5zeXNCUCArIHNkLmRpYUJQICsgc2QudG90Q2hvbCwgDQogICAgICAgICAgZmFtaWx5ID0gYmlub21pYWwobGluayA9ICJsb2dpdCIpLA0KICAgICAgICAgIGRhdGEgPSB0cmFpbi5kYXRhKSANCg0KIyMgQXV0b21hdGljIEZvcndhcmQgU2VsZWN0aW9uIChmcykgTW9kZWw6IG1vZGVsIGZvdW5kIHVzaW5nIGZvcndhcmQgc2VsZWN0aW9uDQpmcy5tb2RlbCA9IHN0ZXBBSUMoZnVsbC5tb2RlbCwgDQogICAgICAgICAgICAgICAgICAgICAgc2NvcGUgPSBsaXN0KGxvd2VyPWZvcm11bGEocmVkdWNlZC5tb2RlbCksICAgDQogICAgICAgICAgICAgICAgICAgICAgdXBwZXI9Zm9ybXVsYShmdWxsLm1vZGVsKSksDQogICAgICAgICAgICAgICAgICAgICAgZGlyZWN0aW9uID0gImZvcndhcmQiLCAgDQogICAgICAgICAgICAgICAgICAgICAgdHJhY2UgPSAwICAgDQogICAgICAgICAgICAgICAgICAgICAgKQ0KDQojIEZpbmRpbmcgdGhlIHByZWRpY3RlZCBwcm9iYWJpbGl0aWVzIGZvciBlYWNoIG9mIHRoZSBjYW5kaWRhdGUgbW9kZWxzDQogICBwcmVkLmZ1bGwgPSBwcmVkaWN0KGZ1bGwubW9kZWwsIG5ld2RhdGEgPSB2YWxpZCwgdHlwZSA9ICJyZXNwb25zZSIpDQogICBwcmVkLnJlZHVjZWQgPSBwcmVkaWN0KHJlZHVjZWQubW9kZWwsIG5ld2RhdGEgPSB2YWxpZCwgdHlwZSA9ICJyZXNwb25zZSIpDQogICBwcmVkLmZzID0gcHJlZGljdChmcy5tb2RlbCwgbmV3ZGF0YSA9IHZhbGlkLCB0eXBlID0gInJlc3BvbnNlIikNCiAgIA0KICAgcHJlLm91dGNvbWUwMSA9IGlmZWxzZShhcy52ZWN0b3IocHJlZC5mdWxsKSA+IDAuNSwgIjEiLCAiMCIpDQogICBwcmUub3V0Y29tZTAyID0gaWZlbHNlKGFzLnZlY3RvcihwcmVkLnJlZHVjZWQpID4gMC41LCAiMSIsICIwIikNCiAgIHByZS5vdXRjb21lMDMgPSBpZmVsc2UoYXMudmVjdG9yKHByZWQuZnMpID4gMC41LCAiMSIsICIwIikNCiAgIA0KICAgUEUxW2ldID0gc3VtKHByZS5vdXRjb21lMDEgPT0gdmFsaWQkVGVuWWVhckNIRCkvbGVuZ3RoKHByZWQuZnVsbCkNCiAgIFBFMltpXSA9IHN1bShwcmUub3V0Y29tZTAyID09IHZhbGlkJFRlblllYXJDSEQpL2xlbmd0aChwcmVkLnJlZHVjZWQpDQogICBQRTNbaV0gPSBzdW0ocHJlLm91dGNvbWUwMyA9PSB2YWxpZCRUZW5ZZWFyQ0hEKS9sZW5ndGgocHJlZC5mcykNCn0NCg0KIyBGaW5kaW5nIHRoZSBhdmVyYWdlIHByZWRpY3RpdmUgZXJyb3JzIG9mIGVhY2ggY2FuZGlkYXRlIG1vZGVsICANCmF2Zy5wZSA9IGNiaW5kKFBFMSA9IG1lYW4oUEUxKSwgUEUyID0gbWVhbihQRTIpLCBQRTMgPSBtZWFuKFBFMykpDQprYWJsZShhdmcucGUsIGNhcHRpb24gPSAiQXZlcmFnZSBQcmVkaWN0aXZlIEVycm9ycyBvZiB0aGUgVGhyZWUgQ2FuZGlkYXRlIE1vZGVscyIpIA0KYGBgDQoNClRoZSBhdmVyYWdlIHByZWRpY3RpdmUgZXJyb3JzIG9mIHRoZSB0aHJlZSBjYW5kaWRhdGUgbW9kZWxzIGFyZSBnaXZlbiBhcyBmb2xsb3dzOg0KDQpUaGUgZnVsbCBtb2RlbCwgcmVwcmVzZW50ZWQgYnkgUEUxLCBoYWQgYW4gYXZlcmFnZSBwcmVkaWN0aXZlIGVycm9yIG9mIDAuODU0Ni4gDQpUaGUgcmVkdWNlZCBtb2RlbCwgcmVwcmVzZW50ZWQgYnkgUEUyLCBoYWQgYW4gYXZlcmFnZSBwcmVkaWN0aXZlIGVycm9yIG9mIDAuODQ5NS4gDQoNClRoZSBmb3J3YXJkIHNlbGVjdGlvbiBtb2RlbCwgcmVwcmVzZW50ZWQgYnkgUEUzLCBoYWQgYW4gYXZlcmFnZSBwcmVkaWN0aXZlIGVycm9yIG9mIDAuODU0Ni4NCg0KV2UgY2FuIHNlZSB0aGF0IGJvdGggdGhlIGZ1bGwgbW9kZWwgYW5kIHRoZSBmb3J3YXJkIHNlbGVjdGlvbiBtb2RlbCBoYWQgZXF1YWwgdmFsdWVzIGZvciB0aGVpciBhdmVyYWdlIHByZWRpY3RpdmUgZXJyb3JzLCB3aXRoIGJvdGggaGF2aW5nIGEgdmFsdWUgb2YgMC44NTQ2LiBIb3dldmVyLCB0aGUgcmVkdWNlZCBtb2RlbCBhIGxvd2VyIGF2ZXJhZ2UgcHJlZGljdGl2ZSBlcnJvciB2YWx1ZSB0aGFuIHRoZSBvdGhlciB0d28gbW9kZWxzLCB3aXRoIGEgdmFsdWUgb2YgMC44NDk2LiBUaGlzIGluZGljYXRlcyB0aGF0IHRoZSByZWR1Y2VkIG1vZGVsIGlzIHRoZSBvbmUgd2hpY2ggd2Ugc2hvdWxkIGNob29zZSBhcyBvdXIgZmluYWwgbW9kZWwuDQoNClRoZSBjcm9zcy12YWxpZGF0aW9uIHByb2Nlc3Mgb2YgY2FsY3VsYXRpbmcgdGhlIGF2ZXJhZ2UgcHJlZGljdGl2ZSBlcnJvcnMgZm9yIHRoZSB0aHJlZSBjYW5kaWRhdGUgbW9kZWxzIHNob3dlZCB0aGF0IHRoZSByZWR1Y2VkIG1vZGVsIGlzIHRoZSBpZGVhbCBvbmUgdG8gdXNlIGZvciBwcmVkaWN0aW9uIGR1ZSB0byBpdCBoYXZpbmcgdGhlIGxvd2VzdCB2YWx1ZSBvZiBpdHMgYXZlcmFnZSBwcmVkaWN0aXZlIGVycm9yIG91dCBvZiB0aGUgdGhyZWUgY2FuZGlkYXRlIG1vZGVscy4gVGhpcyBtYWtlcyB0aGUgcmVkdWNlZCBtb2RlbCBoYXZlIHRoZSBoaWdoZXN0IHByZWRpY3RpdmUgcG93ZXIgYW5kIHBlcmZvcm1hbmNlIG91dCBvZiB0aGUgdGhyZWUgY2FuZGlkYXRlIG1vZGVscywgYXMgc2hvd24gYnkgaG93IGl0IGhhcyB0aGUgbGVhc3QgYW1vdW50IG9mIGl0cyBhdmVyYWdlIHByZWRpY3RpdmUgZXJyb3JzIGluIGNvbXBhcmlzb24gdG8gdGhlIGZ1bGwgbW9kZWwgYW5kIHRoZSBmb3J3YXJkIHNlbGVjdGlvbiBtb2RlbC4gU28sIHdlIHdpbGwgbW92ZSBmb3J3YXJkIHdpdGggdGhlIHVzZSBvZiB0aGUgcmVkdWNlZCBtb2RlbCBhcyBvdXIgZmluYWwgbXVsdGlwbGUgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbCBmb3IgcHJlZGljdGlvbiBvZiB0aGUgb2RkcyBvZiBhbiBpbmRpdmlkdWFsIGJlaW5nIGF0IHJpc2sgZm9yIGRldmVsb3BpbmcgQ0hELiANCg0KDQojIyBGaW5hbCBNb2RlbA0KDQpJbiB0aGUgcHJldmlvdXMgY3Jvc3MtdmFsaWRhdGlvbiBzdGVwLCB3ZSB1c2VkIGEgY3V0IG9mZiBwcm9iYWJpbGl0eSBvZiAwLjUgdG8gaWRlbnRpZnkgYmV0d2VlbiB0aGUgcmVzcG9uc2UgdmFyaWFibGUgZ2l2aW5nIGEgcG9zaXRpdmUgb3IgbmVnYXRpdmUgcmVzdWx0LiBJbiB0aGlzIGNhc2UsIGEgInBvc2l0aXZlIiByZXN1bHQsIG9yIGFuIG91dHB1dCBvZiAxLCByZXByZXNlbnRzIHRoYXQgYW4gaW5kaXZpZHVhbCBpcyBhdCByaXNrIGZvciBkZXZlbG9waW5nIENIRCBpbiBhIDEwLXllYXIgcGVyaW9kLiBBbmQsIGEgIm5lZ2F0aXZlIiByZXN1bHQsIG9yIGFuIG91dHB1dCBvZiAwLCByZXByZXNlbnRzIHRoYXQgYW4gaW5kaXZpZHVhbCBpcyBub3QgYXQgcmlzayBmb3IgZGV2ZWxvcGluZyBDSEQgaW4gYSAxMC15ZWFyIHBlcmlvZC4gV2UgdXNlZCB0aGUgY3Jvc3MtdmFsaWRhdGlvbiBwcm9jZXNzIHdpdGggdGhpcyBjdXQgb2ZmIHByb2JhYmlsaXR5IG9mIDAuNSB0byBmaW5kIHRoZSB2YWx1ZXMgb2YgdGhlIGF2ZXJhZ2UgcHJlZGljdGl2ZSBlcnJvcnMgb2YgdGhlIHRocmVlIGNhbmRpZGF0ZSBtb2RlbHMuIFdlIGZvdW5kIHRoYXQgdGhlIHJlZHVjZWQgbW9kZWwgd2FzIHRoZSBvbmUgd2l0aCB0aGUgYmVzdCBwcmVkaWN0aXZlIHBlcmZvcm1hbmNlLCBzbyB3ZSB3aWxsIHVzZSB0aGF0IGFzIG91ciBmaW5hbCBtb2RlbC4gDQoNCkhvd2V2ZXIsIGluIG9yZGVyIHRvIHJlcG9ydCB0aGUgZGV0YWlscyBvZiB0aGUgZmluYWwgbW9kZWwsIHdlIG11c3QgZmluZCBpdHMgYWN0dWFsIGFjY3VyYWN5IGJhc2VkIHVwb24gdGhlIHRlc3QgZGF0YS4gV2UgcHJldmlvdXNseSBzcGxpdCB0aGUgZGF0YSB1cCBpbnRvIGEgdHJhaW5pbmcgYW5kIGEgdGVzdGluZyBkYXRhIHNldC4gV2Ugd2lsbCB1c2UgdGhlIHRlc3QgZGF0YSBzZXQgdG8gZmluZCB0aGUgYWN0dWFsIGFjY3VyYWN5IG9mIHRoZSBmaW5hbCBtb2RlbCwgdGhlIHJlZHVjZWQgbW9kZWwsIGluIG9yZGVyIHRvIHByb3ZpZGUgdGhlIGZ1bGxlc3QgYWNjdXJhY3kgaW4gdGhlIHJlcG9ydCBvZiB0aGlzIG1vZGVsLiANCg0KYGBge3J9DQpwcmVkLnJlZHVjZWQgPSBwcmVkaWN0KHJlZHVjZWQubW9kZWwsIG5ld2RhdGEgPSB0ZXN0LCB0eXBlID0gInJlc3BvbnNlIikNCnByZWQucmVkdWNlZC5vdXRjb21lID0gaWZlbHNlKGFzLnZlY3RvcihwcmVkLnJlZHVjZWQpID4gMC41LCAiMSIsICIwIikNCg0KQWNjdXJhY3kgPSBzdW0ocHJlZC5yZWR1Y2VkLm91dGNvbWUgPT0gdGVzdCRUZW5ZZWFyQ0hEKS9sZW5ndGgocHJlZC5yZWR1Y2VkKQ0Ka2FibGUoQWNjdXJhY3ksIGNhcHRpb249IkFjdHVhbCBBY2N1cmFjeSBvZiB0aGUgRmluYWwgTW9kZWwgKFJlZHVjZWQgTW9kZWwpIikNCmBgYA0KDQpUaGUgYWN0dWFsIGFjY3VyYWN5IG9mIHRoZSBmaW5hbCBtb2RlbCwgdGhlIHJlZHVjZWQgbW9kZWwsIGlzIGdpdmVuIGFzIDAuODQ0My4gVGhpcyB2YWx1ZSByZXByZXNlbnRzIHRoZSBhY2N1cmFjeSByYXRlIG9mIHRoaXMgZmluYWwgbW9kZWwuIA0KDQpJdCBhcHBlYXJzIHRoYXQgdGhlIGZpbmFsIG1vZGVsIGhhcyBnb29kIGFjY3VyYWN5IGFzIHNlZW4gYnkgaXRzIGFjY3VyYWN5IHJhdGUgb2YgMC44NDQzLiBUaGlzIGluZGljYXRlcyB0aGF0IHRoZSBmaW5hbCBtb2RlbCB3ZSBzZWxlY3RlZCBoYXMgZ29vZCBwcmVkaWN0aXZlIHBlcmZvcm1hbmNlIGZvciBwcmVkaWN0aW5nIHRoZSBvZGRzIG9mIGFuIGluZGl2aWR1YWwgYmVpbmcgYXQgcmlzayBmb3IgZGV2ZWxvcGluZyBDSEQgd2l0aGluIGEgMTAteWVhciBwZXJpb2QuIA0KDQoNCg0KIyBST0MgQW5hbHlzaXMNCg0KV2Ugd2lsbCB1c2UgUk9DIGFuYWx5c2lzIHRvIG1lYXN1cmUgdGhlIGdsb2JhbCBwZXJmb3JtYW5jZSBvZiB0aGUgbXVsdGlwbGUgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbC4gVGhpcyBwbG90IHdpbGwgaWxsdXN0cmF0ZSB0aGUgcmVsYXRpb25zaGlwIG9mIHRoZSB0cnVlIHBvc2l0aXZlIHJhdGUgKFRQUiksIGFsc28ga25vd24gYXMgdGhlIHNlbnNpdGl2aXR5LCBhZ2FpbnN0IHRoZSBmYWxzZSBwb3NpdGl2ZSByYXRlIChGUFIpLCBhbHNvIGdpdmVuIGFzIDEgLSBzcGVjaWZpY2l0eS4gV2Ugd2lsbCBwbG90IHRoZSBST0MgY3VydmVzIGZvciBlYWNoIG9mIHRoZSB0aHJlZSBjYW5kaWRhdGUgbW9kZWxzIHRvIGNvbXBhcmUgdGhlbSBhZ2FpbnN0IG9uZSBhbm90aGVyLiBXZSB3aWxsIGFsc28gY2FsY3VsYXRlIHRoZSBhcmVhIHVuZGVyIHRoZSBjdXJ2ZSAoQVVDKSB2YWx1ZXMgZm9yIGVhY2ggb2YgdGhlIHRocmVlIGNhbmRpZGF0ZSBtb2RlbHMuIFRoZSB2YWx1ZSBvZiBhcmVhIHVuZGVyIHRoZSBjdXJ2ZSByZXByZXNlbnRzIHRoZSBnbG9iYWwgZ29vZG5lc3Mgb2YgdGhlIG1vZGVsLCBhbmQgYSBoaWdoZXIgQVVDIHZhbHVlIGlzIGlkZWFsLiBBIGhpZ2hlciB2YWx1ZSBvZiB0aGUgYXJlYSB1bmRlciB0aGUgY3VydmUgaW5kaWNhdGVzIGJldHRlciBhbmQgbW9yZSBxdWFsaXR5IGdsb2JhbCBnb29kbmVzcyBvZiB0aGF0IG1vZGVsLiANCg0KV2Ugd2lsbCBwbG90IHRoZSBST0MgY3VydmVzIGZvciB0aGUgZnVsbCBtb2RlbCwgdGhlIHJlZHVjZWQgbW9kZWwsIGFuZCB0aGUgZm9yd2FyZCBzZWxlY3Rpb24gbW9kZWwuIFRoaXMgd2lsbCBhbGxvdyB1cyB0byBjb21wYXJlIHRoZSBhcmVhIHVuZGVyIHRoZSBjdXJ2ZXMgZm9yIGVhY2ggb2YgdGhlIHRocmVlIG1vZGVscy4gQWRkaXRpb25hbGx5LCB3ZSB3aWxsIGNhbGN1bGF0ZSB0aGUgQVVDIHZhbHVlcyBmb3IgZWFjaCBvZiB0aGUgdGhyZWUgbW9kZWxzIHRvIGNvbXBhcmUgdGhlbSB3aXRoIGVhY2ggb3RoZXIgYW5kIGRldGVybWluZSB3aGljaCBtb2RlbCBpbmRpY2F0ZXMgdGhlIGJlc3QgZ2xvYmFsIGdvb2RuZXNzLiANCg0KDQpgYGB7cn0NClRQUi5GUFI9ZnVuY3Rpb24ocHJlZCl7DQogIHByb2Iuc2VxID0gc2VxKDAsIDEsIGxlbmd0aD01MCkgIA0KICBwbiA9IGxlbmd0aChwcm9iLnNlcSkNCiAgdHJ1ZS5sYWIgPSBhcy52ZWN0b3IodHJhaW4kVGVuWWVhckNIRCkNCiAgVFBSID0gTlVMTA0KICBGUFIgPSBOVUxMDQogIGZvciAoaSBpbiAxOnBuKXsNCiAgIHByZWQubGFiID0gYXMudmVjdG9yKGlmZWxzZShwcmVkID4gcHJvYi5zZXFbaV0sIjEiLCAiMCIpKQ0KICAgVFBSW2ldID0gbGVuZ3RoKHdoaWNoKHRydWUubGFiPT0iMSIgJiBwcmVkLmxhYj09IjEiKSkvbGVuZ3RoKHdoaWNoKHRydWUubGFiPT0iMSIpKQ0KICAgRlBSW2ldID0gbGVuZ3RoKHdoaWNoKHRydWUubGFiPT0iMCIgJiBwcmVkLmxhYj09IjEiKSkvbGVuZ3RoKHdoaWNoKHRydWUubGFiPT0iMCIpKQ0KICB9DQogY2JpbmQoRlBSID0gRlBSLCBUUFIgPSBUUFIpDQp9DQpgYGANCg0KV2Ugd2lsbCB1c2UgdGhlIHRyYWluaW5nIGRhdGEgc2V0IHRvIGhlbHAgZmluZCB0aGUgcHJlZGljdGl2ZSBwcm9iYWJpbGl0aWVzIGZvciBlYWNoIG9mIHRoZSB0aHJlZSBjYW5kaWRhdGUgbW9kZWxzLiANCg0KYGBge3J9DQojIFRoZSBGdWxsIE1vZGVsDQpmdWxsLm1vZGVsID0gZ2xtKFRlblllYXJDSEQgfm1hbGUgKyBzZC5hZ2UgKyBlZHVjYXRpb24gKyBjdXJyZW50U21va2VyICsgIHNkLmNpZ3NQZXJEYXkgKyBCUE1lZHMgKyBwcmV2YWxlbnRTdHJva2UgKyBwcmV2YWxlbnRIeXAgKyBkaWFiZXRlcyArIHNkLnRvdENob2wgKyBzZC5zeXNCUCArIHNkLmRpYUJQICsgc2QuQk1JICsgc2QuaGVhcnRSYXRlICsgc2QuZ2x1Y29zZSwgDQogICAgICAgICAgZmFtaWx5ID0gYmlub21pYWwobGluayA9ICJsb2dpdCIpLCAgDQogICAgICAgICAgZGF0YSA9IHRyYWluKQ0KDQojIFRoZSBSZWR1Y2VkIE1vZGVsIChmaW5hbCBtb2RlbCkNCnJlZHVjZWQubW9kZWwgPSBnbG0oVGVuWWVhckNIRCB+IGN1cnJlbnRTbW9rZXIgKyBzZC5jaWdzUGVyRGF5ICsgc2Quc3lzQlAgKyBzZC5kaWFCUCArIHNkLnRvdENob2wsIA0KICAgICAgICAgIGZhbWlseSA9IGJpbm9taWFsKGxpbmsgPSAibG9naXQiKSwNCiAgICAgICAgICBkYXRhID0gdHJhaW4pIA0KDQojIFRoZSBGb3J3YXJkIFNlbGVjdGlvbiAoZnMpIE1vZGVsDQpmcy5tb2RlbCA9IHN0ZXBBSUMoZnVsbC5tb2RlbCwgDQogICAgICAgICAgICAgICAgICAgICAgc2NvcGUgPSBsaXN0KGxvd2VyPWZvcm11bGEocmVkdWNlZC5tb2RlbCksICAgDQogICAgICAgICAgICAgICAgICAgICAgdXBwZXI9Zm9ybXVsYShmdWxsLm1vZGVsKSksDQogICAgICAgICAgICAgICAgICAgICAgZGlyZWN0aW9uID0gImZvcndhcmQiLCAgDQogICAgICAgICAgICAgICAgICAgICAgdHJhY2UgPSAwICAgDQogICAgICAgICAgICAgICAgICAgICAgKQ0KDQojIEZpbmRpbmcgdGhlIFByZWRpY3RlZCBQcm9iYWJpbGl0aWVzIGZvciBFYWNoIE1vZGVsDQpwcmVkLmZ1bGwgPSBwcmVkaWN0LmdsbShmdWxsLm1vZGVsLCBuZXdkYXRhID0gdHJhaW4sIHR5cGUgPSAicmVzcG9uc2UiKSANCnByZWQucmVkdWNlZCA9IHByZWRpY3QuZ2xtKHJlZHVjZWQubW9kZWwsIG5ld2RhdGEgPSB0cmFpbiwgdHlwZSA9ICJyZXNwb25zZSIpDQpwcmVkLmZzID0gcHJlZGljdC5nbG0oZnMubW9kZWwsIG5ld2RhdGEgPSB0cmFpbiwgdHlwZSA9ICJyZXNwb25zZSIpDQpgYGANCg0KTm93LCB3ZSB3aWxsIHBsb3QgdGhlIFJPQyBjdXJ2ZXMgZm9yIGVhY2ggb2YgdGhlIHRocmVlIGNhbmRpZGF0ZSBtb2RlbHMgdG8gY29tcGFyZSB0aGVtLiANCg0KYGBge3J9DQojIFBsb3R0aW5nIHRoZSBST0MgY3VydmVzDQogcGxvdChUUFIuRlBSKHByZWQuZnVsbClbLDFdLCBUUFIuRlBSKHByZWQuZnVsbClbLDJdLCANCiAgICAgIHR5cGU9ImwiLCBjb2wgPSAyLCBsdHkgPSAxLCB4bGltID0gYygwLDEpLCB5bGltID0gYygwLDEpLA0KICAgICAgeGxhYiA9ICJGUFI6IDEgLSBTcGVjaWZpY2l0eSIsDQogICAgICB5bGFiID0iVFBSOiBTZW5zaXRpdml0eSIsDQogICAgICBtYWluID0gIlJPQyBDdXJ2ZXMgZm9yIHRoZSBUaHJlZSBDYW5kaWRhdGUgTW9kZWxzIiwNCiAgICAgIGNleC5tYWluID0gMC44LA0KICAgICAgY29sLm1haW4gPSAibmF2eSIpDQogbGluZXMoVFBSLkZQUihwcmVkLnJlZHVjZWQpWywxXSwgVFBSLkZQUihwcmVkLnJlZHVjZWQpWywyXSwgIGNvbD0zLCBsdHk9MikNCiBsaW5lcyhUUFIuRlBSKHByZWQuZnMpWywxXSwgVFBSLkZQUihwcmVkLmZzKVssMl0sICBjb2w9NCwgbHR5PTMpICAgIA0KIA0KICBjYXRlZ29yeSA9IHRyYWluJFRlblllYXJDSEQgPT0gIjEiDQogIFJPQ29iajAxIDwtIHJvYyhjYXRlZ29yeSwgYXMudmVjdG9yKHByZWQuZnVsbCkpDQogIFJPQ29iajAyIDwtIHJvYyhjYXRlZ29yeSwgYXMudmVjdG9yKHByZWQucmVkdWNlZCkpDQogIFJPQ29iajAzIDwtIHJvYyhjYXRlZ29yeSwgYXMudmVjdG9yKHByZWQuZnMpKQ0KICBBVUMwMSA9IHJvdW5kKGF1YyhST0NvYmowMSksNCkNCiAgQVVDMDIgPSByb3VuZChhdWMoUk9Db2JqMDIpLDQpDQogIEFVQzAzID0gcm91bmQoYXVjKFJPQ29iajAzKSw0KQ0KICANCiAgbGVnZW5kKCJib3R0b21yaWdodCIsIGMocGFzdGUoIkZ1bGwgbW9kZWw6IEFVQyA9ICIsIEFVQzAxKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUoIlJlZHVjZWQgbW9kZWw6IEFVQyA9IiwgQVVDMDIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlKCJGb3J3YXJkIFNlbGVjdGlvbiBtb2RlbDogQVVDID0iLCBBVUMwMykpLA0KICAgICAgICBjb2wgPSAyOjQsIGx0eSA9IDE6MywgY2V4ID0gMC44LCBidHkgPSAibiIpDQpgYGANCg0KDQpXZSBjYW4gc2VlIHRoYXQgYm90aCB0aGUgZnVsbCBtb2RlbCBhbmQgdGhlIGZvcndhcmQgc2VsZWN0aW9uIG1vZGVsIGZhbGwgb24gdGhlIGV4YWN0IHNhbWUgY3VydmUsIHdoaWNoIGlzIGFsc28gZXZpZGVuY2VkIGJ5IGhvdyB0aGVzZSB0d28gbW9kZWxzIGhhdmUgdGhlIHNhbWUgdmFsdWVzIGZvciB0aGVpciBhcmVhIHVuZGVyIHRoZSBjdXJ2ZSBCb3RoIHRoZSBmdWxsIG1vZGVsIGFuZCB0aGUgZm9yd2FyZCBzZWxlY3Rpb24gbW9kZWwgaGF2ZSBhbiBhcmVhIHVuZGVyIHRoZSBjdXJ2ZSBvZiAwLjczNTYsIGFuZCB0aGVpciBST0MgY3VydmVzIG1hdGNoIHVwIGlkZW50aWNhbGx5IHRvIGVhY2ggb3RoZXIuIFRoaXMgbWF0Y2hlcyB1cCB3aXRoIHRoZSBwcmV2aW91cyBmaW5kaW5nIG9mIHRoZSBmdWxsIG1vZGVsIGFuZCB0aGUgZm9yd2FyZCBzZWxlY3Rpb24gbW9kZWwgaGF2aW5nIHRoZSBleGFjdCBzYW1lIHZhbHVlcyBmb3IgdGhlaXIgYXZlcmFnZSBwcmVkaWN0aXZlIGVycm9ycy4gDQoNClNvbWV0aGluZyBpbnRlcmVzdGluZyBmb3VuZCBpbiB0aGlzIFJPQyBhbmFseXNpcywgaXMgdGhhdCB0aGUgcmVkdWNlZCBtb2RlbCBhY3R1YWxseSBoYXMgdGhlIHNtYWxsZXN0IHZhbHVlIG9mIGFyZWEgdW5kZXIgdGhlIGN1cnZlIHdpdGggMC42NzYzLiBXZSBmb3VuZCB0aGF0IHRoZSByZWR1Y2VkIG1vZGVsIHdhcyB0aGUgb25lIHdpdGggdGhlIGxvd2VzdCBhdmVyYWdlIHByZWRpY3RpdmUgZXJyb3JzIHByZXZpb3VzbHksIHdoaWNoIG1hZGUgaXQgdGhlIGlkZWFsIG1vZGVsIGNob2ljZSBmb3Igb3VyIGZpbmFsIG1vZGVsLiBIb3dldmVyLCB0aGlzIG1vZGVsIHR1cm5lZCBvdXQgdG8gaGF2ZSB0aGUgbG93ZXN0IGFyZWEgdW5kZXIgdGhlIGN1cnZlIG91dCBvZiB0aGUgdGhyZWUsIHdoaWNoIGlzIG5vdCBpZGVhbC4gQSBtb2RlbCB3aXRoIGEgaGlnaGVyIGFyZWEgdW5kZXIgdGhlIGN1cnZlIGhhcyBhIGJldHRlciBnbG9iYWwgZ29vZG5lc3MgdGhhbiBhIG1vZGVsIHdpdGggYSBsb3dlciBhcmVhIHVuZGVyIHRoZSBjdXJ2ZS4gDQoNClNvbGVseSBiYXNlZCB1cG9uIGFyZWEgdW5kZXIgdGhlIGN1cnZlIHZhbHVlcyBmb3VuZCBpbiB0aGUgUk9DIGFuYWx5c2lzLCBpdCB3b3VsZCBhcHBlYXIgdGhhdCB0aGUgZm9yd2FyZCBzZWxlY3Rpb24gbW9kZWwgcHJvdmlkZXMgdGhlIGJlc3QgZ2xvYmFsIGdvb2RuZXNzLiBCb3RoIHRoZSBmdWxsIG1vZGVsIGFuZCB0aGUgZm9yd2FyZCBzZWxlY3Rpb24gbW9kZWwgaGFkIHRoZSBzYW1lIHZhbHVlIGZvciB0aGVpciBhcmVhIHVuZGVyIHRoZSBjdXJ2ZSwgYnV0IHRoZSBmb3J3YXJkIHNlbGVjdGlvbiBtb2RlbCB3b3VsZCBiZSB0aGUgYmV0dGVyIGNob2ljZSBkdWUgdG8gaXQgaGF2aW5nIGxlc3MgdmFyaWFibGVzLg0KDQpUaGUgUk9DIGFuYWx5c2lzIHJldmVhbGVkIHRoYXQgdGhlIGZvcndhcmQgc2VsZWN0aW9uIG1vZGVsIG1heSBpbiBmYWN0IGJlIGEgZ29vZCBhbHRlcm5hdGl2ZSB0byB1c2UgYXMgdGhlIGZpbmFsIG1vZGVsIGR1ZSB0byBpdCBoYXZpbmcgYSBoaWdoZXIgYXJlYSB1bmRlciB0aGUgY3VydmUgdGhhbiB0aGUgcmVkdWNlZCBtb2RlbCwgZGVzcGl0ZSB0aGUgcmVkdWNlZCBtb2RlbCBoYXZpbmcgYSBsb3dlciBhdmVyYWdlIHZhbHVlIG9mIHByZWRpY3RpdmUgZXJyb3JzIGFzIHdhcyBmb3VuZCBlYXJsaWVyLiBBIHBvdGVudGlhbCByZWFzb24gZm9yIHRoZSBmb3J3YXJkIHNlbGVjdGlvbiBtb2RlbCBoYXZpbmcgYSBoaWdoZXIgQVVDIHRoYW4gdGhlIHJlZHVjZWQgbW9kZWwgZGVzcGl0ZSB0aGUgcmVkdWNlZCBtb2RlbCBoYXZpbmcgYSBsb3dlciBhbmQgbW9yZSBpZGVhbCBhdmVyYWdlIHZhbHVlIG9mIHByZWRpY3RpdmUgZXJyb3JzIGNvdWxkIGJlIGR1ZSB0byB0aGUgcmVkdWNlZCBtb2RlbCBoYXZpbmcgbGVzcyB2YXJpYWJsZXMuIEl0IGNvdWxkIGJlIGEgcG9zc2liaWxpdHkgdGhhdCB0aGUgcmVkdWNlZCBtb2RlbCBoYXZpbmcgZmV3ZXIgdmFyaWFibGVzIGNvdWxkIGhhdmUgYSBoaWdoZXIgbGlrZWxpaG9vZCBvZiBmYWxzZSBwb3NpdGl2aWVzIG9yIGZhbHNlIG5lZ2F0aXZlcyBkdWUgdG8gc29tZSBvZiB0aGUgdmFyaWFibGVzIHdoaWNoIG1heSBiZSBzaWduaWZpY2FudCBub3QgaGF2aW5nIGJlZW4gaW5jbHVkZWQgaW4gdGhlIHJlZHVjZWQgbW9kZWwuIA0KDQoNCg0KIyBDb25jbHVzaW9uDQoNCkluIHRoaXMgcHJvamVjdCwgdGhyZWUgY2FuZGlkYXRlIG1vZGVscyB3ZXJlIGNyZWF0ZWQgdG8gY29tcGFyZSB0aGVpciBwcmVkaWN0aXZlIHBvd2VycyBhbmQgdWx0aW1hdGVseSBjaG9vc2UgYSBmaW5hbCBtb2RlbCB0aGF0IHByb3ZpZGVzIHRoZSBiZXN0IHNpZ25pZmljYW5jZSBhbmQgcXVhbGl0eSBvZiBwcmVkaWN0aW9uIGZvciBwcmVkaWN0aW5nIHRoZSBvZGRzIG9mIGFuIGluZGl2aWR1YWwgYmVpbmcgYXQgcmlzayBmb3IgZGV2ZWxvcGluZyBDSEQgaW4gYSAxMC15ZWFyIHBlcmlvZC4gVGhlc2UgbW9kZWxzIGFsbCBjb25zaXN0ZWQgb2YgbXVsdGlwbGUgbG9naXN0aWMgcmVncmVzc2lvbiB0byBwcmVkaWN0IHRoZSBiaW5hcnkgcmVzcG9uc2UgdmFyaWFibGUgb2YgYW4gaW5kaXZpZHVhbCdzIG9kZHMgb2YgYmVpbmcgYXQgcmlzayBmb3IgZGV2ZWxvcGluZyBDSEQuIENyZWF0aW5nIGEgbW9kZWwgZm9yIHRoZSBwdXJwb3NlIG9mIHRoaXMgcHJlZGljdGlvbiBjYW4gcHJvdmlkZSB1c2UgZm9yIGRvY3RvcnMgYW5kIHBhdGllbnRzIGluIG9yZGVyIHRvIHByZWRpY3QgdGhlaXIgb2RkcyBvZiBiZWluZyBhdCByaXNrIGZvciBkZXZlbG9waW5nIENIRCBiYXNlZCB1cG9uIHZhcmlvdXMgcGVyc29uYWwgYW5kIG1lZGljYWwgZmFjdG9ycy4gDQoNClRoZSB0aHJlZSBjYW5kaWRhdGUgbW9kZWxzIHRoYXQgd2VyZSBjcmVhdGVkIGluY2x1ZGVkIGEgZnVsbCBtb2RlbCB3aXRoIGFsbCBvZiB0aGUgdmFyaWFibGVzIGluIHRoZSBkYXRhIHNldCwgYSByZWR1Y2VkIG1vZGVsIHdpdGggb25seSB0aGUgdmFyaWFibGVzIGRlZW1lZCBhcyB0aGUgbW9zdCBwcmFjdGljYWxseSBpbXBvcnRhbnQsIGFuZCBhIG1vZGVsIGNyZWF0ZWQgdGhyb3VnaCBmb3J3YXJkIHZhcmlhYmxlIHNlbGVjdGlvbi4gVGhlc2UgdGhyZWUgbW9kZWxzIHdlcmUgY29tcGFyZWQgYnkgdGhlaXIgcHJlZGljdGl2ZSBwb3dlciBhbmQgcXVhbGl0eSBmb3IgdGhlaXIgcG90ZW50aWFsIHVzZSBhcyBhIGZpbmFsIG11bHRpcGxlIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWwgZm9yIHRoaXMgcHJvamVjdC4gDQoNClRoZSBhdmVyYWdlIHByZWRpY3RpdmUgZXJyb3JzIG9mIGFsbCB0aHJlZSBjYW5kaWRhdGUgbW9kZWxzIHdlcmUgY29tcGFyZWQgdG8gYXNzZXNzIHRoZWlyIHByZWRpY3RpdmUgcG93ZXJzIHRocm91Z2ggdGhlIHByb2Nlc3Mgb2YgY3Jvc3MtdmFsaWRhdGlvbi4gSXQgd2FzIGZvdW5kIHRoYXQgdGhlIGZ1bGwgbW9kZWwgYW5kIHRoZSBmb3J3YXJkIHNlbGVjdGlvbiBtb2RlbCBoYWQgaWRlbnRpY2FsIHZhbHVlcyBmb3IgdGhlIGF2ZXJhZ2UgcHJlZGljdGl2ZSBlcnJvcnMuIEhvd2V2ZXIsIHRoZSByZWR1Y2VkIG1vZGVsIHR1cm5lZCBvdXQgdG8gaGF2ZSB0aGUgbG93ZXN0IHZhbHVlIG9mIGl0cyBhdmVyYWdlIHByZWRpY3RpdmUgZXJyb3JzIG91dCBvZiB0aGUgdGhyZWUgY2FuZGlkYXRlIG1vZGVscy4gVGhpcyBpbmRpY2F0ZWQgdGhhdCB0aGUgcmVkdWNlZCBtb2RlbCB3YXMgdGhlIGlkZWFsIGNob2ljZSBkdWUgdG8gaXQgaGF2aW5nIHRoZSBsb3dlc3QgdmFsdWUgb2YgYXZlcmFnZSBwcmVkaWN0aXZlIGVycm9ycywgYW5kIHRoZXJlZm9yZSB0aGUgaGlnaGVzdCBxdWFsaXR5IG9mIHByZWRpY3RpdmUgcG93ZXIuIFRoaXMgaW5kaWNhdGVkIHRoYXQgdGhlIHJlZHVjZWQgbW9kZWwgd291bGQgYmUgYSBnb29kIGNob2ljZSBmb3IgdGhlIGZpbmFsIG1vZGVsIGR1ZSB0byBpdHMgaGlnaCBxdWFsaXR5IHByZWRpY3RpdmUgcG93ZXIgc2hvd24gYnkgaG93IGl0IGhhZCB0aGUgc21hbGxlc3QgdmFsdWUgb2YgYXZlcmFnZSBwcmVkaWN0aXZlIGVycm9ycyBvdXQgb2YgYWxsIHRocmVlIGNhbmRpZGF0ZSBtb2RlbHMuIA0KDQpGdXJ0aGVybW9yZSwgUk9DIGFuYWx5c2lzIHdhcyBjb25kdWN0ZWQgdG8gY29udGludWUgdGhlIGFuYWx5c2lzIG9mIHRoZSB0aHJlZSBjYW5kaWRhdGUgbW9kZWxzLiBJdCB3YXMgZm91bmQgdGhhdCB0aGUgZnVsbCBtb2RlbCBhbmQgdGhlIGZvcndhcmQgc2VsZWN0aW9uIG1vZGVsIGhhdmUgaWRlbnRpY2FsIFJPQyBjdXJ2ZXMgYW5kIGFyZWEgdW5kZXIgdGhlIGN1cnZlIHZhbHVlcy4gVGhpcyBtYXRjaGVkIHVwIHdpdGggaG93IGl0IHdhcyBwcmV2aW91c2x5IGZvdW5kIHRoYXQgdGhlIGZ1bGwgbW9kZWwgYW5kIHRoZSBmb3J3YXJkIHNlbGVjdGlvbiBtb2RlbCBoYXZlIHRoZSBleGFjdCBzYW1lIGF2ZXJhZ2UgdmFsdWUgb2YgdGhlaXIgcHJlZGljdGl2ZSBlcnJvcnMuIEJldHdlZW4gdGhlc2UgdHdvIG1vZGVscywgd2Ugd291bGQgY2hvb3NlIHRoZSBmb3J3YXJkIHNlbGVjdGlvbiBtb2RlbCBvdmVyIHRoZSBmdWxsIG1vZGVsIGR1ZSB0byBpdCBoYXZpbmcgbGVzcyB2YXJpYWJsZXMuIEhvd2V2ZXIsIHdlIGFsc28gZm91bmQgdGhhdCB0aGUgcmVkdWNlZCBtb2RlbCBpbiBmYWN0IGhhZCB0aGUgbG93ZXN0IGFyZWEgdW5kZXIgdGhlIGN1cnZlIHZhbHVlIG91dCBvZiB0aGUgdGhyZWUgbW9kZWxzLiBUaGlzIGlzIGludGVyZXN0aW5nIGFzIGl0IGNvbnRyYWRpY3RzIHdpdGggdGhlIHJlZHVjZWQgbW9kZWwgcHJldmlvdXNseSBiZWluZyBzaG93biB0byBoYXZlIHRoZSBsb3dlc3QgYXZlcmFnZSB2YWx1ZSBvZiBwcmVkaWN0aXZlIGVycm9ycy4gSXQgdHVybnMgb3V0IHRoYXQgZGVzcGl0ZSB0aGUgcmVkdWNlZCBtb2RlbCBoYXZpbmcgdGhlIGxvd2VzdCBhdmVyYWdlIG9mIHByZWRpY3RpdmUgZXJyb3JzIGFuZCB0aGVyZWZvcmUgdGhlIGhpZ2hlc3QgcHJlZGljdGl2ZSBwb3dlciwgaXQgYWxzbyBoYXMgdGhlIGxvd2VzdCBhcmVhIHVuZGVyIHRoZSBjdXJ2ZSBtZWFuaW5nIHRoYXQgaXQgaGFzIGxvd2VyIGdsb2JhbCBnb29kbmVzcyBvdXQgb2YgdGhlIGNhbmRpZGF0ZSBtb2RlbHMuDQoNCk92ZXJhbGwsIGl0IHdhcyBmb3VuZCB0aGF0IHRocm91Z2ggY3Jvc3MtdmFsaWRhdGlvbiBpdCB0dXJuZWQgb3V0IHRoYXQgdGhlIHJlZHVjZWQgbW9kZWwgaGFkIHRoZSBsb3dlc3QgYXZlcmFnZSB2YWx1ZSBvZiBwcmVkaWN0aXZlIGVycm9ycywgYW5kIHRoZXJlZm9yZSB0aGUgaGlnaGVzdCBwcmVkaWN0aXZlIHBvd2VyLiBUaGlzIG1hZGUgdGhlIHJlZHVjZWQgbW9kZWwgdGhlIGlkZWFsIGNob2ljZSBmb3IgdGhlIHNlbGVjdGlvbiBvZiBvdXIgZmluYWwgbW9kZWwuIFRoaXMgcmVkdWNlZCBtb2RlbCBmb2N1c2VkIG9uIG9ubHkgdGhlIG1vc3QgcHJhY3RpY2FsbHkgaW1wb3J0YW50IHZhcmlhYmxlcyBmb3IgcHJlZGljdGluZyB0aGUgb2RkcyBvZiBhbiBpbmRpdmlkdWFsIGJlaW5nIGF0IHJpc2sgZm9yIGRldmVsb3BpbmcgQ0hELiBUaGVzZSBtb3N0IHByYWN0aWNhbGx5IGltcG9ydGFudCB2YXJpYWJsZXMgaW5jbHVkZWQgY3VycmVudFNtb2tlciwgc2QuY2lnc1BlckRheSwgc2Quc3lzQlAsIHNkLmRpYUJQLCBhbmQgc2QudG90Q2hvbC4gSG93ZXZlciwgaXQgd2FzIGxhdGVyIGZvdW5kIHRocm91Z2ggUk9DIGFuYWx5c2lzIHRoYXQgdGhlIHJlZHVjZWQgbW9kZWwgaGFkIHRoZSBsb3dlc3QgYXJlYSB1bmRlciB0aGUgY3VydmUgdmFsdWUsIGluZGljYXRpbmcgYSBsb3dlciBnbG9iYWwgZ29vZG5lc3MuIEl0IHdhcyBpbiBmYWN0IHRoZSBmb3J3YXJkIHNlbGVjdGlvbiBtb2RlbCB0aGF0IHRoZSBST0MgYW5hbHlzaXMgc3VnZ2VzdGVkIGFzIGJlaW5nIHRoZSBpZGVhbCBjaG9pY2UgYmFzZWQgdXBvbiBpdHMgZ2xvYmFsIGdvb2RuZXNzLiBUaGUgZm9yd2FyZCBzZWxlY3Rpb24gbW9kZWwgaW5jbHVkZWQgdGhlIHZhcmlhYmxlcyBvZiBjdXJyZW50U21va2VyLCBzZC5jaWdzUGVyRGF5LCBzZC5zeXNCUCwgc2QuZGlhQlAsIHNkLnRvdENob2wsIHNkLGFnZSwgbWFsZSwgc2QuZ2x1Y29zZSwgcHJldmFsZW50U3Ryb2tlLCBwcmV2YWxlbnRIeXAsIGFuZCBCUE1lZHMuIA0KDQpFdmVuIHRob3VnaCB0aGUgcmVkdWNlZCBtb2RlbCBwcm92aWRlZCB0aGUgYmVzdCBwcmVkaWN0aXZlIHBvd2VyIGR1ZSB0byBpdHMgbG93ZXIgdmFsdWUgb2YgYXZlcmFnZSBwcmVkaWN0aXZlIGVycm9ycywgdGhlIGZvcndhcmQgc2VsZWN0aW9uIG1vZGVsIHdhcyBzaG93biB0aHJvdWdoIFJPQyBhbmFseXNpcyB0byBoYXZlIHRoZSBoaWdoZXN0IGdsb2JhbCBnb29kbmVzcy4gQm90aCB0aGUgcmVkdWNlZCBtb2RlbCBhbmQgdGhlIGZ1bGwgbW9kZWwgYXJlIGhpZ2ggcXVhbGl0eSBtb2RlbHMgZm9yIHRoZSBwcmVkaWN0aW9uIGFuZCBlc3RpbWF0aW9uIG9mIHRoZSBvZGRzIG9mIGFuIGluZGl2aWR1YWwgYmVpbmcgYXQgcmlzayBmb3IgZGV2ZWxvcGluZyBDSEQuIFRoaXMgZGlzY3JlcGFuY3kgYmV0d2VlbiB0aGUgcmVkdWNlZCBtb2RlbCBoYXZpbmcgdGhlIGhpZ2hlc3QgcHJlZGljdGl2ZSBwb3dlciB5ZXQgdGhlIGxvd2VzdCBnbG9iYWwgZ29vZG5lc3MgY291bGQgaW4gZmFjdCBiZSBkdWUgdG8gaXQgaGF2aW5nIHRoZSBmZXdlc3QgdmFyaWFibGVzLiBCeSBoYXZpbmcgZmV3ZXIgdmFyaWFibGVzLCB0aGlzIGNvdWxkIHBvdGVudGlhbGx5IGxlYWQgdG8gaGlnaGVyIHJhdGVzIG9mIGZhbHNlIHBvc2l0aXZlcyBhbmQgZmFsc2UgbmVnYXRpdmVzIHRoYW4gdGhlIG90aGVyIG1vZGVscyB3aXRoIG1vcmUgdmFyaWFibGVzIGFjY291bnRlZCBmb3IuIFNvIG92ZXJhbGwsIHRoZSByZWR1Y2VkIG1vZGVsIHdvdWxkIGJlIHRoZSBpZGVhbCBjaG9pY2UgYXMgdGhlIGZpbmFsIG1vZGVsIGZvciBhIG1vZGVsIHdpdGggdGhlIGhpZ2hlc3QgcHJlZGljdGl2ZSBwb3dlciwgd2hpY2ggaXMgd2hhdCB3ZSB3ZXJlIGxvb2tpbmcgZm9yIGluIHRoaXMgcHJvamVjdC4gSG93ZXZlciwgaXQgaXMgaW1wb3J0YW50IHRvIGtlZXAgaW4gbWluZCB0aGF0IHRoaXMgcmVkdWNlZCBtb2RlbCBoYXMgYSBsb3dlciBnbG9iYWwgZ29vZG5lc3MgdGhhbiB0aGUgZm9yd2FyZCBzZWxlY3Rpb24gbW9kZWwsIGxpa2VseSBkdWUgdG8gdGhlIHJlZHVjZWQgbW9kZWwgaGF2aW5nIGZld2VyIHZhcmlhYmxlcyBhbmQgcG90ZW50aWFsbHkgYSBoaWdoZXIgcmF0ZSBmb3IgZmFsc2UgcG9zaXRpdmVzIGFuZCBmYWxzZSBuZWdhdGl2ZXMuIFRoaXMgaXMgaW1wb3J0YW50IHRvIGtlZXAgaW4gbWluZCB3aGVuIGNyZWF0aW5nIGEgbW9kZWwgZm9yIHRoZSBwcmVkaWN0aW9uIG9mIGFuIGluZGl2aWR1YWwncyBvZGRzIG9mIGJlaW5nIGF0IHJpc2sgZm9yIGRldmVsb3BpbmcgQ0hELCBhcyBoaWdoIHByZWRpY3RpdmUgcG93ZXIgaXMgaWRlYWwgZm9yIGVuc3VyaW5nIHRoYXQgcGF0aWVudCdzIGhlYWx0aCBpcyBhY2NvdW50ZWQgZm9yIHByb3Blcmx5LCBidXQgaXQgaXMgYWxzbyBpbXBvcnRhbnQgdG8gYmUgd2FyeSBvZiB0aGUgcG90ZW50aWFsIGZvciBmYWxzZSBwb3N0aXZlcyBhbmQgZmFsc2UgbmVnYXRpdmVzIGluIHJlZ2FyZHMgb2YgdGhlc2UgcHJlZGljdGlvbnMuIA0KDQoNCg0KIyMgUmVjb21tZW5kYXRpb25zDQoNClNvbWUgcmVjb21tZW5kYXRpb25zIEkgd291bGQgbWFrZSBmb3IgZnV0dXJlIHByb2plY3RzIGluY2x1ZGU6DQoNCiogRXhwYW5kIHRoZSBkYXRhIGNvbGxlY3Rpb24gdG8gZW5zdXJlIHRoZSBhY2N1cmFjeSBvZiB0aGUgZmluZGluZ3MgZm91bmQgaW4gdGhpcyBwcm9qZWN0LiBUaGlzIGNvdWxkIGFsc28gaW52b2x2ZSByZWFjaGluZyBvdXQgdG8gdmFyaW91cyBob3NwaXRhbHMgYW5kIGRvY3RvcnMgZm9yIGEgYmV0dGVyIHVuZGVyc3RhbmRpbmcgb2YgdGhlIG1vc3Qgc2VyaW91cyByaXNrIGZhY3RvcnMgZm9yIENIRCBpbiBvcmRlciB0byBlbnN1cmUgdGhhdCB0aGUgdmFyaWFibGVzIGtlcHQgd2l0aGluIHRoZSBmaW5hbCBtb2RlbCBpbmRlZWQgYXJlIHJlcHJlc2VudGF0aXZlIG9mIGEgcGF0aWVudCdzIG9kZHMgb2YgYmVpbmcgYXQgcmlzayBmb3IgZGV2ZWxvcGluZyBDSEQuDQoNCiogQ29uc2lkZXIgb3RoZXIgdmFyaWFibGVzIHdoaWNoIGNvdWxkIGJlIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgaW4gcHJlZGljdGluZyB0aGUgb2RkcyBvZiBhIHBhdGllbnQncyByaXNrIGZvciBkZXZlbG9waW5nIENIRC4gQWxsIG9mIHRoZSBwcmVkaWN0b3IgdmFyaWFibGVzIHRoYXQgd2VyZSBpbmNsdWRlZCB3aXRoaW4gdGhpcyBkYXRhIHNldCBtYWRlIHNlbnNlIGluIHRlcm1zIG9mIHRoaXMgcHJvamVjdCBhbmQgd2VyZSBhbGwgZmFjdG9ycyB3aGljaCBjb3VsZCBwbGF5IGEgcm9sZSBpbiBhIHBhdGllbnQncyBvZGRzIG9mIGJlaW5nIGF0IHJpc2sgZm9yIGRldmVsb3BpbmcgQ0hELiBIb3dldmVyLCBmdXR1cmUgcHJvamVjdHMgY291bGQgY29uc2lkZXIgbG9va2luZyBpbnRvIGFkZGl0aW9uYWwgdmFyaWFibGVzIHRoYXQgbWF5IGJlIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgaW4gcHJlZGljdGluZyB0aGUgb2RkcyBvZiBhIHBhdGllbnQgYmVpbmcgYXQgcmlzayBmb3IgZGV2ZWxvcGluZyBDSEQuIEZvciBpbnN0YW5jZSwgb25lIHBvdGVudGlhbCB2YXJpYWJsZSB0aGF0IGNvdWxkIGJlIGxvb2tlZCBpbnRvIGlzIGEgcGF0aWVudCdzIGluY29tZSB0byBzZWUgaWYgdGhlIGluY29tZSBvZiBhIHBhdGllbnQgaGFzIGFuIGltcGFjdCBvbiB0aGVpciBvZGRzIG9mIGJlaW5nIGF0IHJpc2sgZm9yIGRldmVsb3BpbmcgQ0hELiBBIHJlYXNvbiB3aHkgSSBiZWxpZXZlIHRoYXQgdGhpcyBjb3VsZCBiZSBhIHNpZ25pZmljYW50IHZhcmlhYmxlIGZvciB0aGlzIG1vZGVsIGlzIGJlY2F1c2UgcGF0aWVudHMgd2l0aCBoaWdoZXIgaW5jb21lcyB0ZW5kIHRvIGhhdmUgbW9yZSBvcHRpb25zIHdoZW4gaXQgY29tZXMgdG8gbWVkaWNhbCBjYXJlIHRoYW4gcGF0aWVudHMgd2l0aCBsb3dlciBpbmNvbWVzIHdvdWxkLiBTbywgcGVyaGFwcyB0aGVyZSBjb3VsZCBiZSBhIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGEgcGF0aWVudCdzIGluY29tZSBhbmQgdGhlaXIgb2RkcyBvZiBiZWluZyBhdCByaXNrIGZvciBkZXZlbG9waW5nIENIRC4gVGhpcyBpcyBqdXN0IG9uZSBleGFtcGxlIG9mIGEgdmFyaWFibGUgdGhhdCBpcyBub3QgaW4gdGhpcyBkYXRhIHNldCB3aGljaCBtYXkgcHJvdmlkZSB1c2UgZm9yIGZ1dHVyZSBwcm9qZWN0cy4NCg0KKiBDb25zaWRlciBvdGhlciBwb3NzaWJsZSBtb2RlbHMgdGhhdCBjb3VsZCBwcm92aWRlIHF1YWxpdHkgcHJlZGljdGl2ZSBwb3dlciBpbiBwcmVkaWN0aW5nIHRoZSBvZGRzIG9mIGEgcGF0aWVudCBiZWluZyBhdCByaXNrIGZvciBkZXZlbG9waW5nIENIRCB3aXRoaW4gYSAxMC15ZWFyIHBlcmlvZCBvZiB0aW1lLiBJbiB0aGlzIHByb2plY3QsIHdlIGxvb2tlZCBhdCB0aHJlZSBjYW5kaWRhdGUgbXVsdGlwbGUgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbHMgYW5kIGNob3NlIHdoaWNoIG9uZSBwcm92aWRlZCB0aGUgYmVzdCB1dGlsaXR5LiBUaGVzZSB0aHJlZSBtb2RlbHMgaW5jbHVkZWQgYSBmdWxsIG1vZGVsIHdpdGggYWxsIG9mIHRoZSBwcmVkaWN0b3IgdmFyaWFibGVzIGxlZnQgaW4gdGhlIG1vZGVsLCBhIHJlZHVjZWQgbW9kZWwgd2l0aCBvbmx5IHRoZSB2YXJpYWJsZXMgdGhhdCB0aGUgbWVkaWNhbCByZXBvcnQgZGVzY3JpYmVkIGFzIG1vc3Qgc2lnbmlmaWNhbnQgcmlzayBmYWN0b3JzIGZvciBDSEQsIGFuZCBhIGZpbmFsIG1vZGVsIHRoYXQgd2FzIGNyZWF0ZWQgdGhyb3VnaCBhdXRvbWF0aWMgZm9yd2FyZCB2YXJpYWJsZSBzZWxlY3Rpb24uIFdlIGZvdW5kIHRoYXQgdGhlIHJlZHVjZWQgbW9kZWwgd2FzIHRoZSBvbmUgd2hpY2ggcHJvdmlkZWQgdGhlIGxvd2VzdCBhdmVyYWdlIHZhbHVlIGZvciBwcmVkaWN0aXZlIGVycm9ycyBvdXQgb2YgdGhlIHRocmVlIG1vZGVscywgYW5kIHNvIHdlIGNob3NlIHRoaXMgdG8gYmUgb3VyIGZpbmFsIG1vZGVsIGR1ZSB0byBpdCBwcm92aWRpbmcgdGhlIHN0cm9uZ2VzdCBwcmVkaWN0aXZlIHBvd2VyLiBIb3dldmVyLCB0aGlzIGlzIG5vdCB0byBzYXkgdGhhdCB0aGVyZSBpcyBub3QgYW55IG90aGVyIHBvc3NpYmxlIG1vZGVsIHRoYXQgY291bGQgcG90ZW50aWFsbHkgYmUgYmV0dGVyIHRoYW4gdGhlIG9uZSB3ZSBjcmVhdGVkLiBJZiBmdXJ0aGVyIHByb2plY3RzIGZvdW5kIGEgbW9kZWwgdGhhdCBwcm92aWRlZCBiZXR0ZXIgYW5kIG1vcmUgcG93ZXIgaW4gcHJlZGljdGlvbiB0aGFuIG91ciBmaW5hbCBtb2RlbCwgdGhpcyBjb3VsZCBoZWxwIGRvY3RvcnMgYW5kIHBhdGllbnRzIGJldHRlciB1bmRlcnN0YW5kIHRoZWlyIG9kZHMgb2YgYmVpbmcgYXQgcmlzayBmb3IgZGV2ZWxvcGluZyBDSEQgYmFzZWQgb24gdGhlIHZhcmlvdXMgZmFjdG9ycyBpbmNsdWRlZCBpbiB0aGUgbW9kZWwuIA0KDQoqIEZ1cnRoZXIgaW52ZXN0aWdhdGUgdGhlIHRvcGljIG9mIGZhbHNlIHBvc3RpdmVzIGFuZCBmYWxzZSBuZWdhdGl2ZXMgYW1vbmdzdCB0aGUgcHJlZGljdGVkIHZhbHVlcy4gVGhlIHRvcGljIG9mIGZhbHNlIHBvc3RpdmVzIGFuZCBmYWxzZSBuZWdhdGl2ZXMgd2FzIGFkZHJlc3NlZCBhcyBwb3RlbnRpYWxseSBiZWluZyBhIHJlYXNvbiBmb3IgdGhlIGRlc2NyZXBhbmN5IGJldHdlZW4gdGhlIHJlZHVjZWQgbW9kZWwgaGF2aW5nIHRoZSBncmVhdGVzdCBwcmVkaWN0aXZlIHBvd2VyLCB5ZXQgYWxzbyBoYXZpbmcgdGhlIGxvd2VzdCBnbG9iYWwgZ29vZG5lc3MuIEhhdmluZyBmZXdlciB2YXJhaWJsZXMgY291bGQgcG9zc2libHkgbGVhZCB0byBzb21lIGZhY3RvcnMgYmVpbmcgdW5hY2NvdW50ZWQgZm9yLCBhbmQgcG90ZW50aWFsbHkgcmVzdWx0aW5nIGluIHNvbWUgZmFsc2UgcG9zaXRpdmUgb3IgbmVnYXRpdmUgcHJlZGljdGlvbnMuIFRoaXMgaXMgYSBwb3RlbnRpYWwgdG9waWMgd2hpY2ggY291bGQgYmUgZnVydGhlciBsb29rZWQgaW50byBpbiBmdXJ0aGVyIGV4cGVyaW1lbnRzIHRvIGZ1bGx5IHVuZGVyc3RhbmQgdGhlIG9jY3VyZW5jZXMgb2YgZmFsc2UgcG9zaXRpdmUgb3IgbmVnYXRpdmUgcHJlZGljdGlvbnMuIA0KDQoNCk92ZXJhbGwsIHRoaXMgcHJvamVjdCBzaG93ZWQgdGhhdCB0aGUgcmVkdWNlZCBtb2RlbCByZXN1bHRlZCBpbiB0aGUgbG93ZXN0IGF2ZXJhZ2UgcHJlZGljdGl2ZSBlcnJvcnMgb3V0IG9mIHRoZSB0aHJlZSBtb2RlbHMsIG1lYW5pbmcgdGhpcyB3YXMgdGhlIG1vZGVsIHdpdGggdGhlIGhpZ2hlc3QgcHJlZGljdGl2ZSBwb3dlci4gSG93ZXZlciwgUk9DIGFuYWx5c2lzIHNob3dlZCB0aGF0IHRoZSByZWR1Y2VkIG1vZGVsIGhhZCBhIGxvd2VyIGFyZWEgdW5kZXIgdGhlIGN1cnZlIG1vZGVsLCBhbmQgaXQgd2FzIHRoZSBmb3J3YXJkIHNlbGVjdGlvbiBtb2RlbCB3aGljaCBoYWQgdGhlIGhpZ2hlc3QgZ2xvYmFsIGdvb2RuZXNzIGFzIGluZGljYXRlZCBieSB0aGUgUk9DIGFuYWx5c2lzLiBUaGlzIGlzIHNvbWV0aGluZyBpbXBvcnRhbnQsIGJlY2F1c2UgYWx0aG91Z2ggdGhlIHJlZHVjZWQgbW9kZWwgaXMgYW4gaWRlYWwgY2hvaWNlIGZvciB0aGUgZmluYWwgbW9kZWwgZHVlIHRvIGl0cyBoaWdoIHF1YWxpdHkgb2YgcHJlZGljdGl2ZSBwb3dlciwgdGhpcyBsb3dlciBnbG9iYWwgZ29vZG5lc3MgaXMgYW4gaW1wb3J0YW50IHRoaW5nIHRvIGtlZXAgaW4gbWluZCB3aGVuIHVzaW5nIHRoaXMgZmluYWwgbW9kZWwgZm9yIHRoZSBwcmVkaWN0aW9uIG9mIGFuIGluZGl2aWR1YWwncyBvZGRzIG9mIGJlaW5nIGF0IHJpc2sgZm9yIGRldmVsb3BpbmcgQ0hEIGluIGEgMTAteWVhciBwZXJpb2QuIA0KDQoNCg0KDQojIFJlZmVyZW5jZXMNCg0KVGhpcyBkYXRhIHNldCB3YXMgZm91bmQgb24ga2FnZ2xlLmNvbSB1bmRlciB0aGUgY29sbGVjdGlvbiBvZiBsb2dpc3RpYyByZWdyZXNzaW9uIGRhdGEgc2V0cy4gSW5jbHVkZWQgYmVsb3cgaXMgdGhlIGNpdGF0aW9uIG9mIHRoZSB3ZWIgcGFnZSBvZiB3aGVyZSBJIGZvdW5kIHRoZSBkYXRhIHNldCBJIHVzZWQgZm9yIG15IHByb2plY3QgYWxvbmcgd2l0aCB0aGUgbWVkaWNhbCByZXBvcnQgSSBmb3VuZCB3aGljaCBkZXNjcmliZWQgd2hpY2ggZmFjdG9ycyBhcmUgdGhlIG1vc3QgbWVkaWNhbGx5IHNpZ25pZmljYW50IHdoZW4gaXQgY29tZXMgdG8gZGV0ZXJtaW5pbmcgYSBwYXRpZW50J3MgcmlzayBmb3IgZGV2ZWxvcGluZyBDSEQuDQoNCg0KRGlsZWVwLiAoMjAxOSwgSnVuZSA3KS4gTG9naXN0aWMgcmVncmVzc2lvbiB0byBwcmVkaWN0IGhlYXJ0IGRpc2Vhc2UuIEthZ2dsZS4gaHR0cHM6Ly93d3cua2FnZ2xlLmNvbS9kYXRhc2V0cy9kaWxlZXAwNzAvaGVhcnQtZGlzZWFzZS1wcmVkaWN0aW9uLXVzaW5nLWxvZ2lzdGljLXJlZ3Jlc3Npb24/cmVzb3VyY2U9ZG93bmxvYWQmc2VsZWN0PWZyYW1pbmdoYW0uY3N2IA0KDQpIYWphciwgUi4gKDIwMTcpLiBSaXNrIGZhY3RvcnMgZm9yIGNvcm9uYXJ5IGFydGVyeSBkaXNlYXNlOiBIaXN0b3JpY2FsIHBlcnNwZWN0aXZlcy4gSGVhcnQgdmlld3PigK86IHRoZSBvZmZpY2lhbCBqb3VybmFsIG9mIHRoZSBHdWxmIEhlYXJ0IEFzc29jaWF0aW9uLiBodHRwczovL3BtYy5uY2JpLm5sbS5uaWguZ292L2FydGljbGVzL1BNQzU2ODY5MzEvIA0KDQo=