By: Jeffrey Ellington
9:00am Mon-Wed Section
4/19/17

1. Introduction: Assessing College Admissions Data

High school guidance counselors use college admissions data to properly advise high school seniors on where to apply to college. Studying the generally available summary-statistic level admissions data is one source of valuable insight into college selection criteria and the admissions process. However, summary-statistic level data is a crude tool for providing any specific student with insight into his or her individual chance of being admitted to a particular university. Using individual level admissions data is a potentially useful method to bolster a guidance counselor’s ability to provide tailored guidance to each student. Unfortunately, gathering individual level data from the over 2,000 colleges in the USA is expensive, if not impossible. As a result, we will examine individual level admissions outcome data from one ‘elite’ US university. This information can be analyzed to help advise students on their prospects of being admitted to this particular elite university (at least if they had theoretically applied in the given year from which our data was collected). Additionally, we will explore if it’s responsible to extrapolate these findings to other years at this specific university and potentially to other similarly elite universities.

2. Data Description

At first glance, it is unclear how the data were collected. The 8,700 observations were obtained from last year’s applicants at a particular ‘elite’ university. It’s not specified whether this is a sample of the data or the complete set of observations. Quickly reviewing publicly available Class of 2020 admissions data from the eight Ivy League schools reveals the range in the number of applicants stretches from 20,675 (Dartmouth) to 44,966 (Cornell). The average number of applicants across the eight schools 33,923. Given this, it is almost certain our 8,700 observations are a sample from a broader set of observations at this particular school. We can’t assume that this sample is representative of the presumed broader applicant pool. Finally, the data violate the IID assumption: admitting one applicant has an effect on subsequent applicants given there is a relatively fixed class size and composition. For our treatment, we will assume the data were not generated via a random process. Instead, for our treatment, we will examine the 8,700 observations of nine variables assuming the data were not collected randomly. The nine variables are:

1. admit — Coded “1” for admit and “0” for reject.
2. anglo — Coded “1” for anglo and “0” otherwise.
3. asian — Coded “1” for asian and “0” otherwise.
4. black — Coded “1” for black and “0” otherwise.
5. gpa.wtd — high school GPA weighted for AP courses. 
6. sati.verb — SAT verbal score.
7. sati.math — SAT math score.
8. income — household income capped at $100,000. 
9. sex — Coded “1” for male and “0” for female.

Admit is the response variable. It is a binary variable (“1” for admit / “0” for reject). Admit results were collected for the 8,700 applicants, where each individual result is assumed to be the outcome of a considered admissions review process (i.e. not random). The tally for admissions results can be seen below in Fig. 2.1. 2,686 of the 8,700 students were admitted, reflecting a 30.87% acceptance rate for our specific sample. Note that this does not imply that 30.87% is the overall acceptance rate for this particular university.

The other eight variables will be examined in much greater detail once they’ve been properly cleaned. However, it’s worth stating up front that while these eight variables seem important in an analysis of admissions, they are far from complete. An expert on university admissions, especially at elite schools, could likely point to several other important missing variables. A few examples might include athletic or artistic talent, legacy relationships, service or entrepreneurial recognition, and/or the quality of recommendation letters. There are countless others. In addition, we also have no framework for existing individual admissions decisions/policies at this particular “elite” university, which may impact the analysis. Whatever our estimate of the mean, we can be assured it will be misspecified without greater information. With that caveat stated, there are still many avenues of analysis available. (Administrative Note: while all of the variables are lower-case in the data, I will refer to them via capitalization to make it clear we are referencing a variable (e.g. “admit” will be written “Admit”)).

ggp <- ggplot(data.frame(admissions),aes(x=admit), aes(y=count))
# counts
ggp + geom_bar(fill="lightgreen") + labs(x = "0=Rejected, 1=Accepted", y = "Count", title = "Fig 2.1 Admissions Results", subtitle = "Acceptance Rate = 30.87%")

3. Data Cleaning

Before examining the relationships between Admit and the other eight variables, we will first need to clean and prepare the data. Reviewing the summary statistics for the nine variables below, a number of issues immediately pop out.

summary(admissions)
 admit     anglo       asian       black         gpa.wtd         sati.verb        sati.math         income        sex      
 0:6014   0   :5061   0   :4454   0   :7515   Min.   :-3.200   Min.   :   0.0   Min.   :  0.0   Min.   :  120   0   :4633  
 1:2686   1   :2811   1   :3417   1   : 356   1st Qu.: 3.500   1st Qu.: 510.0   1st Qu.:550.0   1st Qu.:32881   1   :4043  
          NA's: 828   NA's: 829   NA's: 829   Median : 3.850   Median : 580.0   Median :630.0   Median :65000   NA's:  24  
                                              Mean   : 3.789   Mean   : 555.5   Mean   :592.3   Mean   :63245              
                                              3rd Qu.: 4.150   3rd Qu.: 650.0   3rd Qu.:690.0   3rd Qu.:99999              
                                              Max.   : 4.950   Max.   :5240.0   Max.   :800.0   Max.   :99999              
                                                                                                NA's   :1724               

After systematically reviewing the data, eight data cleaning/transformation issues are apparent.

  1. The response variable, Admit, is encoded as an integer/numeric variable (as opposed to factor). We will need to co-erce it to a factor variable to ensure our future analysis uses the proper statistical techniques. We will also do this for all other categorical variables (Anglo, Asian, Black, and Sex).
  2. It’s immediately apparent Anglo has extraneous values. We will transform “white” to “1”" for observation #200 (and recede “NA” to 0 for Asian/Black in this case) and transform from “10” to “1” for observation #10, given Black and Asian are both 0. It’s worth noting this evidence for observation #10 is not conclusive, but most likely correct. Additionally, Anglo was initially input as character data, so we transformed to factor type.
  3. Turning our attention to the treatment of the categorical variables associated with race (Angle, Asian, Black), it’s quickly clear that a subset of observations (1288) are coded “0” for all three. This could reflect missing data. However, given there are 828 actual NA values, it’s more likely that this implies another category of race. We will add “Other” as another factor variable for these 1288 observations (note: used a cute mathematical transformation to create the factor!).
  4. The 828 remaining missing values (NAs) for race proved to be perplexing. Thankfully, the NAs were systematically placed in the same rows among Anglo, Asian, and Black (as opposed to be randomly scattered). The missing values represented ~10% of the total data set, yet there was no obvious way to properly use the data. Given our response is Admit, it seemed logical to first check the overall sample population admittance rate with the sub-population rate of NAs across Anglo, Asian, and Black. The results were similar: 30.87% vs. 33.69&, respectively. The NA’s were being admitted at a slightly higher rate. While the difference is likely material, not to mention cutting 10% of the data set is really painful, I didn’t know how to code a good transformation of the data. Simply imputing the mode would have treated the NA’s as Asian. Intuitively this felt wrong. Ideally, I would have been able to regress all the other variables to predict race; however, I did not have the R background to complete this. Ultimately, I cut the 828 NA’s.
  5. In addition, I removed the two remaining NA’s from Sex. This was less than 0.01% of the data, so felt immaterial.
  6. Income had a max value encoded as $99,999. Given 25% was coded as $99,999, we will treat this as a capped value and not an artifact/mis-reported statistic implying some type of missing value. Furthermore, Income had 1724 missing values coded as NAs. 1724 observations was certainly too many to strike from the analysis. Again, it made sense to compare the overall sample population admittance rate with the sub-population rate of NAs in Income. The results were similar: 30.87% vs. 33.58&, respectively. Missing values for Income are associated with a slightly higher acceptance rate. Without more information, it seemed the best option was to impute the NAs as the median value of $65k. While not perfect, I saw no better way to include these values without adding noise. This is admittedly less than perfect, as there could be many reasons beyond randomly missing data that NAs exist. Perhaps the applicant wants to hide that they are unable to pay for college or that they are wealthy and under performing for some reason. There are a multitude of reasonable explanations.
  7. The minimum GPA was initially reported as -3.20. This was simply inverted to 3.20. Additionally, 19 GPAs were entered as 0. Given the small proportion, the values were transformed to the mean of 3.80.
  8. Possible SAT scores range from 200-800, which quickly highlighted two errors. One verbal score was entered as 5,240. Given this student was admitted, the score was transformed to the median score of admitted students (650). More perplexing were the 457 0’s for both SAT Math and Verbal. Not coincidentally, the 0 values were always paired together, implying some type of systematic effect. The most likely scenarios are the students either a) took the ACT and did not report SAT scores or b) were permitted to be considered for admission without any standardized test scores. No transformations were made to these values, given there is likely signal in the decision not to report. We will examine further later.

As a result of these transformations, the final data set to be used for analysis contains 7,870 observations of 10 variables.

4. Analysis of Univariate Statistics

Starting with merit based predictors, let’s examine GPA and SAT scores. GPA is reported on a weighted scale from 0.00 - 5.00, with an actual range of 1.25 to 4.95. The median is 3.85 and the mean is 3.80. The histogram in Fig 4.1 follows a normal distribution, with a slight left-skew. This is good, as it implies there’s no artifacts/systematic errors in the data when converting from non-weighted to weighted GPAs. Individual Math/Verbal SAT scores highlight two interesting features of the data. First, as noted during EDA, there are a number of SAT Scores at 0. We’re leaving these as is, even though it’s impossible to score 0 (range is 200-800). They are indicative of something other than missing data – likely an active decision to take the ACT or no standardized test at all. We suspect this decision will be informative in the admissions decisions. Second, the individual scores follow roughly normal distributions, though there is a fair bit of right-skew bunching on Math at 800. When combined into an SAT Total score, the distribution becomes almost perfectly normal. This flattening of the right tails in both Math (especially) and Verbal (somewhat) implies that there a significant number of students who do exceptionally well on a particular section, but far less students do exceptionally well on both sections. Most importantly, both GPA and SAT Total scores feel representative of what we would expect to see on a national level from a distribution shape. However, the mean/median seem to be right shifted. Looking at all SAT data, the mean score is 1020 for all test takers and 1080 for all college bound students. From a Level I perspective, the sample of students applying to this particular “elite” university are above average nationally. Moving to a Level II analysis, it seems fair to extrapolate that there is a self-selection bias for better students to apply to better universities. We would expect this trend to hold across other elite universities and across other years.

ggp.v3 <- ggplot(admissions.final, aes(gpa.wtd)) + geom_histogram(fill="lightgreen") + labs(x = "GPA (Weighted)", y = "Count", title = "Fig 4.1: GPA Histogram", 
                                                    subtitle = "Median GPA = 3.85")
  ggp.v4 <- ggplot(admissions.final, aes(sati.math)) + geom_histogram(fill="lightgreen") + labs(x = "SAT Math", y = "Count", title = "Fig 4.2: SAT Math", 
                                                    subtitle = "Median Math SAT = 630")
  ggp.v5 <- ggplot(admissions.final, aes(sati.verb)) + geom_histogram(fill="lightgreen") + labs(x = "SAT Verb", y = "Count", title = "Fig 4.3: SAT Verb", 
                                                    subtitle = "Median Verb SAT = 580")
  ggp.v6 <- ggplot(admissions.final, aes(sati.total)) + geom_histogram(fill="lightgreen") + labs(x = "SAT Total", y = "Count", title = "Fig 4.4: SAT Total", 
                                                    subtitle = "Median Total SAT = 1210")
  grid.arrange(ggp.v3, ggp.v4, ggp.v5, ggp.v6, nrow=2, ncol=2)

Looking at demographic data, we see a few interesting facets of the data. There are roughly 17% more female applications than male. This could certainly be a result of our specific sample. From a Level II perspective, however, this wouldn’t be surprising. On almost every level (applications, enrollment, graduation, etc.) there are more female undergraduates than male represented. Given this, it feels reasonable to treat this male/female applicant count gap as representative of the broader population. Race, however, paints a different picture. Black students made up 356 (4.5%) of total applicants. Whether this is a Level I sampling anomaly or Level II broader representation of the college applicant pool is difficult to say. Looking at 2015 applications to “elite”" schools, most schools (Penn, Columbia, Yale, Duke, among many others reporting) had black student applicants comprising 10-13% of the applicant pool, between 2-3x our 4.5%. In contrast, UC Berkeley received just 4.9% of its applicants from black students (lowest among “elite” schools). While there is no conclusive evidence, using the best data available seems to indicate the proportion of black students in our sample is systematically low. Looking at Income, we included two histograms: the original (with 1724 NAs omitted) and the final with the 1724 NAs imputed to the mean. This obviously skewed the distribution, but seemed the “fairest” way to do so without adding noise. The distribution is right skewed, with about 25% of population making more than $100k. Among families with incomes under $100k, the distribution is fairly uniform.

par(mfrow=c(2,2))
      barplot(admit.apps.comp.gen, col = "lightgreen",
            xlab = "Gender: Male, Female",
            ylab = "Count",
            main = "Fig 4.5: Applicant Count by Gender",
            ylim = c(0, 5000))
      barplot(admit.apps.comp, col = "lightgreen",
              xlab = "Applicants: Anglo, Asian, Black, Other",
              ylab = "Count",
              main = "Fig 4.6: Applicant Count by Race",
              ylim = c(0, 4000))
      hist(admissions.final$income, col = "lightgreen",
           xlab = "Family Income ($)",
           ylab = "Count",
           main = "Fig 4.7: App Count by Income (Imputed)",
           ylim = c(0, 3000))
      hist(admissions$income, col = "lightgreen",
           xlab = "Family Income ($)",
           ylab = "Count",
           main = "Fig 4.8: App Count by Income (Original",
           ylim = c(0, 3000))

5. Analysis of Bivariate Statistics

Recreating the histogram (Fig 5.1) of the response variable (Admit) from the final data set, we see the acceptance rate has changed minorly from 30.87% to 30.58%. From here we will turn our attention to uncovering what features of the data set are predictive for admissions.

ggp <- ggplot(data.frame(admissions.final),aes(x=admit), aes(y=count))
# counts
ggp + geom_bar(fill="lightgreen") + labs(x = "0=Rejected, 1=Accepted", y = "Count", title = "Fig 5.1 Admissions Results (Final Dataset)", subtitle = "Acceptance Rate = 30.58%")

Based on an intuitive first pass, at least for anyone who’s spent time in the college admissions process, an immediate question pops up: for an “elite” school, is 30.58% a reasonable acceptance rate? Turning back to the publicly available Class of 2020 Ivy League information, acceptance rates ranged from 3.4% (Harvard) to 12.5% (Cornell). Boston College, my Alma mater, has an acceptance rate of 29% and is ranked around #30 nationally. So 30% is either systematically low for a truly “elite” college (and implies we have a skewed sample) or is representative of a school ranked around #30. How we decided to interpret will have implications on whether we can perform a Level I or II analysis.

Let’s turn to demographics. First, looking at race, we can see in Fig 5.2 that there is disparity between Anglo/Asian admit rates (32%) and Black/Other admit rates (24%). This information challenges, at least for this particular sample, an often repeated quip that it’s easier for underrepresented minority students to get into elite universities than Anglo or Asian students. This will be interesting to explore further in the multi-variate analysis once interaction effects like income, SAT, and/or GPA are considered. Examining income, let’s start by splitting the data in to two buckets: lower/middle-class (income less than $99,999) and affluent (income greater than $99,999). The admission rates are pretty similar: 30.1% and 32.1%, respectively. While affluent applicants are slightly favored in this data set, it’s not nearly as startling as the racial disparities. Finally, looking at gender, we see similar results to income: a small disparity between the male admission rate (29.8%) and the female admission rate (31.3%). Whether this is strictly a Level I sample artifact or a Level II generalization about college admissions is difficult to say based on this data alone and somewhat irrelevant given the disparity is minimal.

barplot(admit.rates.race.hold*100, col = "lightgreen",
            xlab = "Admit Rate: Total(30.6%), Anglo(32.5%), Asian(32.0%), Black(24.4%), Other(24.4%)",
            ylab = "Percent",
            main = "Fig 5.2: Admit Rate by Race",
            ylim = c(0, 40))

Merit based predictors prove to be more illustrative. We can look at how GPA, SAT Math, SAT Verbal, and SAT Total impact admissions decisions. Given we are working in the wrong model perspective and not focused on causality, let’s focus on visualizing the data. We can look at admissions rates for each of the 4 predictors in quartiles in Figs 5.3-6. It’s abundantly clear that GPA, SAT M, SAT V, and SAT T all greatly effect admissions decision in the sample population. Applicants from the Top quartile in any given category are 7-24x more likely to be admitted from those from the bottom. There is clearly co-linearity between these predictors, bot explicitly (SAT M + V = SAT Total) and implicitly (good SAT takers probably on average have higher GPAs). We can see this empirically. The correlations between SAT Math & SAT Verbal (0.85) and SAT Total and GPA (0.33) are printed below.

    cor(admissions.final$sati.math, admissions.final$sati.verb)
[1] 0.8509055
    cor(admissions.final$sati.total, admissions.final$gpa.wtd)
[1] 0.3286649
 par(mfrow=c(2,2))
    plot(c(top.gpa.admit.rate,
           scnd.gpa.admit.rate,
           thrd.gpa.admit.rate,
           bot.gpa.admit.rate),
         xlab = "Quartiles (1-4)",
         ylab = "Admit Rate %",
         main = "Fig 5.3: Admit by GPA",
         pch = 16,
         cex = 1.25,
         col = "lightgreen")
    plot(c(top.satm.admit.rate,
           scnd.satm.admit.rate,
           thrd.satm.admit.rate,
           bot.satm.admit.rate),
         xlab = "Quartiles (1-4)",
         ylab = "Admit Rate %",
         main = "Fig 5.4: Admit by SAT Math",
         pch = 16,
         cex = 1.25,
         col = "lightgreen")
    plot(c( top.satv.admit.rate,
            scnd.satv.admit.rate,
            thrd.satv.admit.rate,
            bot.satv.admit.rate),
         xlab = "Quartiles (1-4)",
         ylab = "Admit Rate %",
         main = "Fig 5.5: Admit by SAT Verbal",
         pch = 16,
         cex = 1.25,
         col = "lightgreen")
    plot(c( top.satt.admit.rate,
            scnd.satt.admit.rate,
            thrd.satt.admit.rate,
            bot.satt.admit.rate),
         xlab = "Quartiles (1-4)",
         ylab = "Admit Rate %",
         main = "Fig 5.5: Admit by SAT Total",
         pch = 16,
         cex = 1.25,
         col = "lightgreen")

6. Analysis of Multivariate Statistics

Finally, we get to the Random Forest (RF) Analysis: our principle topic of inquiry. Before we start, it’s important to lay out a few statistical concepts. First, as noted in Section 2: Data Description, our classification using RF will lack many potentially explanatory variables. In short, we have the 11 predictors we’ve observed, but there is a lot more information required to specify the true mean function. Given that, we will still be OK moving ahead at Level I and potentially Level II analysis, but we will not be able to draw conclusions at Level III. Second, in Section 5. Analysis of Bivariate Statistics, we pointed out that it’s unwise to imply causality with the limited data we’re analyzing. Now, we will completely depart from the idea of regression coefficients when using RF. The transformations and interaction effects involved in creating the model are sufficiently complex that there is not even a coefficient type analogy to consider.

To evaluate our model, we will allow the algorithm to split the data into training data and test data for us. The RF algorithm does this automatically by training on a subset of observations and then testing the quality of the decision tree it creates on the Out-of-Bag (OOB) test data. This framework is a good way to prevent over fitting, especially given it is averaged over a large number of trees. Given we have almost two orders of magnitude more observations than base predictors, the model should be stable.

6.1: No Misclassification Costs

For our initial model, we grow a random forest of 750 trees. Based on the plot below in Fig. 6.1, we can see the model improvement tapers off substantially by 50, and is almost flat by 200. 750 trees should capture plenty signal without wasting compute resources. The misclassification error rate is 15.91%, which can seen below in the model print out, showing OOB error rate and the confusion table. 15.91% seems reasonable at first pass given the amount of missing data in our model. We’d expect the error rate to drop if we could inform the model of everyone’s other talents, service, recommendation letters, and myriad other criteria. What’s potentially more interesting is the comparison of false negatives to false positives. The model had sensitivity of 66.9% and specificity of 91.6%. Our model preferred to predict someone would NOT be accepted than would be. The implied cost ratio was a bit less that 1:2. It forecasted 26.2% of applicants would be accepted (compared to the actual 30.6%). As a result, 796 students who would have been admitted would have been discouraged from applying. It’s worth tuning the false positive to false negative cost ratio in our model to promote a more useful prediction; after-all, we don’t want to discourage every high school senior during what is already an incredibly exhausting and trying time.

plot(rf1, main = "Fig 6.1: RF1 Plot")

print(rf1)

Call:
 randomForest(formula = admit ~ anglo + asian + black + other +      gpa.wtd + sati.verb + sati.math + sati.total + income + sex,      data = admissions.final, importance = T, ntree = 750) 
               Type of random forest: classification
                     Number of trees: 750
No. of variables tried at each split: 3

        OOB estimate of  error rate: 15.91%
Confusion matrix:
     0    1 class.error
0 5007  456  0.08347062
1  796 1611  0.33070212

6.2: Setting Misclassification Costs

For our next model, let’s build in a target 4:1 cost ratio. This essentially says that we want the model to favor false positives at 4x the rate it values false negatives. This is going to invariably degrade precision: we will be wrong more often when predicting “Yes”. However, the motivation seems just. As counselors, we can either inflate our vanity metrics of saying our students got into their “Top” school more often OR we can sacrifice our accuracy by pushing students to apply to better schools that they really do have a fighting chance to get into. I’d certainly rather strive for excellence and come up short sometimes than settle for mediocrity and spending a life wondering “what if”. Looking at the results, we achieved a 3.8:1 cost ratio. As a cost, the OOB error rate rose to 20.27%. What’s valuable here from a guidance counselor perspective is that the model is still accurately predicting acceptances at 62%. I’d be shocked if most students did not apply to an “elite” school (assuming they liked the other factors of the school) with a 62% chance of being admitted. On the flip side, when we tell a student that the model is not optimistic about their chances, it’s right 93% of the time. Not too many people will hang their hats on a 1:14 chance. For fun, let’s presume a student with average risk-appetite will apply to a school if there’s 1/3 chance of being accepted. What does that cost ratio look like? The cost ratio is about 155:1. Interestingly, though, the false negative error rate only gets cut by about 50%. This is a poor trade off to make.

print(rf2)

Call:
 randomForest(formula = admit ~ anglo + asian + black + other +      gpa.wtd + sati.verb + sati.math + sati.total + income + sex,      data = admissions.final, importance = T, ntree = 750, sampsize = c(1000,          1700)) 
               Type of random forest: classification
                     Number of trees: 750
No. of variables tried at each split: 3

        OOB estimate of  error rate: 20.27%
Confusion matrix:
     0    1 class.error
0 4199 1264   0.2313747
1  331 2076   0.1375156
print(rf3)

Call:
 randomForest(formula = admit ~ anglo + asian + black + other +      gpa.wtd + sati.verb + sati.math + sati.total + income + sex,      data = admissions.final, importance = T, ntree = 750, sampsize = c(100,          2000)) 
               Type of random forest: classification
                     Number of trees: 750
No. of variables tried at each split: 3

        OOB estimate of  error rate: 56.15%
Confusion matrix:
     0    1 class.error
0 1072 4391  0.80377082
1   28 2379  0.01163274

6.3: Importance Plots and Prediction Accuracy

Looking at the importance plots below in Fig 6.2 and Fig 6.3, reminds us of the previous observations in the bivariate analysis. In descending order, the most important predictors are: GPA, SAT Total, SAT Verbal, and SAT Math. On an unstandardized basis, what this chart is saying is that when a given variable is shuffled, we lose the corresponding amount of predictive power (OOB classification error, formally). For example, shuffling GPA.wtd, the most important predictor causes a subsequent drop in over 25% of predictive power. From a Level I perspective, it’s interesting that GPA and SAT Total were most predictive and also followed the most normal distributions. The broader range of possible results (0.00 - 5.00 or 400 - 1600) and variance among results allowed the random forest algorithm to find incremental signal in a way that is very hard to do with a limited set of indicator variables.

 par(mfrow=c(1,2))
    varImpPlot(rf2,type=1,scale=F,class=1,
               main="Importance Plot for Admission
               (Unstandarized)",col="lightgreen",cex=1,pch=19)
    varImpPlot(rf2,type=1,scale=T,class=1,
               main="Importance Plot for Admission
               (Standardized)", col="lightgreen",cex=1,pch=19)
  par(mfrow=c(1,2))

    varImpPlot(rf2,type=1,scale=F,class=0,
               main="Importance Plot for Rejection
               (Unstandardized)", col="lightgreen",cex=1,pch=19)
    varImpPlot(rf2,type=1,scale=T,class=0,
               main="Importance Plot for Rejection
               (Standardized)", col="lightgreen",cex=1,pch=19)

6.4: Partial Dependence Plots

The partial dependence plots below in Fig 6.4 and Fig 6.5 show the relationships between the GPA/SAT Total and the centered log odds of admission. Both GPA and SAT Score are numeric with normal distributions. The chances of admission increase linearly with GPA from 3.3 until 4.5, at which point the data becomes thin and unstable. For SAT Scores the relationship is slightly different, growing near exponentially from around 1100 until 1300, before settling into a liner relationship and then plateauing at around 1400. High school students with GPAs around 4.5 and SAT Scores above 1400 have the highest chance of admission. The slight declines at the extreme right tails are probably best explained by sparse data. An astute college admissions specialist might have domain knowledge that could explain differently: perhaps students with 5.0 GPAs and 1600 SATs are lacking in other characteristics. There’s not enough evidence here to say.

par(mfrow=c(2,1))
    part1<-partialPlot(rf2,pred.data=admissions.final,x.var=gpa.wtd,
                       rug=T,which.class=1, main="Partial Dependence Plot for GPA on Admission", xlab="GPA",
                       ylab="Centered Log Odds")
    scatter.smooth(part1$x,part1$y,span=1/3,col="lightgreen",pch=19, main="Partial Dependence Plot for GPA on Admission", xlab="GPA",
                   ylab="Centered Log Odds")
    par(mfrow=c(2,1))

    part2<-partialPlot(rf2,pred.data=admissions.final,x.var=sati.total,
                       rug=T,which.class=1, main="Partial Dependence Plot for SAT Total on Admission", xlab="SAT Score",
                       ylab="Centered Log Odds")
    scatter.smooth(part1$x,part1$y,span=1/3,col="lightgreen",pch=19, main="Partial Dependence Plot for SAT Total on Admission", xlab="SAT Score",
                   ylab="Centered Log Odds")

7. Summary and Conclusions

Our final model used 750 trees and a cost ratio of 3.8:1. This lead to a 20.27% OOB Classification error rate, which we accepted due to it’s willingness to predict more students would be admitted, even if at lower precision. This was a policy decision driven by a desire to encourage high quality students to apply to an “elite” university. We can now summarize our findings and conclusions. There are three core issues I’d like to address:

  1. From a data collection perspective, this data set is less than ideal for a few reasons. First, we are unsure whether it is a complete sample of one year’s admission results from an elite university or just a sample of results. The limited number of observations compared to a normal elite university application count implies the data is a sample. From there, we don’t know if was generated randomly or as a biased selection. Furthermore, college admissions data is tricky in that is not fully independent. There are a relatively fixed number of seats and similar students compete over the same seat. This destroys the independence assumption – all previous decisions impact the subsequent ones in a given year (as clearly seen comparing early-decision admittance rates and regular-decision admittance rates). For the remainder of this discussion, we will assume the data were collected as a biased sample from a larger set of admissions observations. Luckily, gender, sex, and income were very minimally, if at all, predictive in our sample set.
  2. From a level I perspective, we can see the data are complex with many missing features. Addressing only these 7870 observations, we cannot approach the true mean specification. That said, we can directionally measure direction and importance of select predictors. For example, GPA and STAT Total appeared to be both correlated with each other and correlated with higher rates of admission, which we observed in the bivariate analysis as well as partial dependence and importance plots. At the far right of both GPA and SAT Scores, the distributions become thin and unstable. Most of the apparent reduction in admission rates there can be attributed to instability. The Random Forest algorithm proved to be a powerful classification tool, performing with an OOB classification error rate of 15.91% when unrestricted in cost ratio. Even after adding a 3.8:1 cost ratio, the algorithm slid 4.5% percentage points on OOB classification error (20.27%); a small trade-off for a much more useful predictive tool.
  3. From a Level II perspective, which despite the data collection issues, I believe we can justify, we can extrapolate some findings that could prove useful moving forward in real world applications for our high school students. I believe we can justify Level II analysis for two principle reasons. First, our model found the most predictive variables to be Weighted GPA and Total STAT Score, which are continually reported to be critical factors in college admissions. Rather than these findings being an artifact of our limited sample, they seem to be indicative of a much broader trend. Second, our data covered a broad set of values. Both GPA and STAT Total formed nearly perfectly normal distributions. Income showed a relative uniformity, aside from the spike at $99,999 (and subsequent spike at $65,000 from median imputation). Anglo, Asian, Other, and Sex were well represented. The one sparsity was around Black students. It would be wise to exercise caution in extrapolating the model for Black students without further data. If these distributions were sparser or highly skewed in illogical ways, I’d be much more cautious to extend to Level II analysis. This raises the question: how should we actually use the model? The answer is: it depends. As my recommendation, the model should be used sparingly overall. The model is best fit for the following situation: an 1) Anglo or Asian student who has 2) taken the SAT and is 3) applying to a University with a ~30% acceptance rate. If the student is Black, the school is substantially easier or harder to get into, the student is missing SAT data, or the time period has shifted significantly from when these data were collected, the model could be wildly less predictive. My recommendation would be to identify students at my high school that fit this profile, use the algorithm to predict their results as a pilot, and then evaluate the true predictive results vs. the OOB data. All the while, I’d be pushing to gather more robust data. As it stands today, the predictive power is for a limited scope of student applying to a fairly specific type of school.

Appendix: R Code

setwd("/Users/jeffreyellington/Dropbox/Wharton/Spring2017/STAT974/data/")
###### first glance at data
summary(admissions)
str(admissions)

## Plot Admissions histogram
library(ggplot2)
ggp <- ggplot(data.frame(admissions),aes(x=admit), aes(y=count))
# counts
ggp + geom_bar(fill="lightgreen") + labs(x = "0=Rejected, 1=Accepted", y = "Count", title = "Fig 2.1 Admissions Results", 
              subtitle = "Acceptance Rate = 30.87%")
## Calc acceptance rate
sum(admissions$admit)/length(admissions$admit)*100


##### EDA
## Find Weird Values for Categorical Variables, if any
str(admissions)
unique(admissions$anglo)
    ## Anglo, Misc "10"/"White" --> recode as "1" (and remove NA's for obs #200)
    admissions[[200,2]]=1
    admissions[[200,3]]=0
    admissions[[200,4]]=0
    admissions[[10,2]]=1
unique(admissions$asian)
unique(admissions$black)
unique(admissions$sex)

## Factor/Numeric variable transformations
admissions$admit<-as.factor(admissions$admit)
admissions$anglo<-as.factor(admissions$anglo)
admissions$asian<-as.factor(admissions$asian)
admissions$black<-as.factor(admissions$black)
admissions$sex<-as.factor(admissions$sex)
admissions$income<-as.numeric(admissions$income)
admissions$sati.math<-as.numeric(admissions$sati.math)
str(admissions)
summary(admissions)

## GPA Values
  ## Check if GPA is normally distributed (verifying no systematic deviances due to mis-haps in weighting)
  hist(admissions$gpa.wtd) ## looks normal without any systematic deviance // bi-modalities
  ## Invert negative value from -3.20 to 3.20
  admissions[[2000,5]]=3.20
  ## Make 0.00 GPA = Mean/Median GPA
  summary(admissions$gpa.wtd)
  ## Median = 3.85, Mean = 3.79, settled on 3.80, assuming mean was slightly pulled down by the 19 GPA=0.00 values
  admissions[[382,5]]=3.80
  admissions[[560,5]]=3.80
  admissions[[903,5]]=3.80
  admissions[[1900,5]]=3.80
  admissions[[3081,5]]=3.80
  admissions[[3146,5]]=3.80
  admissions[[3541,5]]=3.80
  admissions[[3816,5]]=3.80
  admissions[[3925,5]]=3.80
  admissions[[4034,5]]=3.80
  admissions[[4486,5]]=3.80
  admissions[[4576,5]]=3.80
  admissions[[5140,5]]=3.80
  admissions[[5462,5]]=3.80
  admissions[[6604,5]]=3.80
  admissions[[7131,5]]=3.80
  admissions[[7759,5]]=3.80
  admissions[[7938,5]]=3.80
  admissions[[8061,5]]=3.80
      #Don't laugh... couldn't get function to work :-(
  
## Income Max Encoded at 99,999
  sum(admissions$income >= 99999, na.rm = TRUE)
  2164/8700 *100
  ## 2164 at max income (24.87%)
  ## Seems OK to leave as ... 25% making more than $100k is reasonable for this pool
  
## SAT Max Score Error
  ## Max SAT Verbal = 5240; regressing to the median of admitted students (given they were admitted)
  admitted <- subset(admissions, admissions$admit == 1)
  summary(admitted$sati.verb) ## median = 650
  admissions[[20,6]]=650 


## Find NA's and remove if sensible
  summary(admissions)
  ## Sex NAs (24 total, 22 have tons of other missing values, striking from data set)
  which(is.na(admissions$sex))
  admissions.v1 <- admissions[-c(353, 379, 443, 762, 799, 1321, 1780, 4451, 4486, 4681, 5197, 5229, 5558, 5792, 6027, 6284, 6323, 6426, 6465, 6762, 6921, 7505, 7934, 8274),]
  
  ## NA for Income (1724, too big to strike)
  sum(is.na(admissions$income))
    ## check if systematic deviance from normal acceptance rate (579 Admitted out of 1724 Total)
    579/1724 *100 ## 33.58% vs. 30.87% ... seems omitted income info is slightly predictive, but not much. Regessing to median
    admissions.test <- admissions.v1 
    samp<-na.roughfix(admissions.test$income, median) ## impute NAs to median value
    admissions.test$income <- samp ## replace NAs with median value
    sum(admissions.test$income==65000) - sum(admissions.v1$income==65000, na.rm = TRUE) #1711, correct value
    admissions.v2 <- admissions.test ## create new dataframe with imputed values
    summary(admissions.v2)
    
  ## NA for Race (828)
  sum(is.na(admissions$asian))
  sum(is.na(admissions$black))
  sum(is.na(admissions$anglo))
    ## check if systematic deviance from normal acceptance rate
    summary(admissions) #828
    summary(admitted) #279
    279/828 * 100 ## 33.69% vs. 30.87% ... seems omitted race is slightly predictive, but not much
    ## can't regress to mean, the others are likely too predictive...
    ## striking from dataset... 10% hurts, but it's the best solution I see
    admissions.v3 <- subset(admissions.v2, (!is.na(admissions.v2[,2])) & (!is.na(admissions.v2[,3])) & (!is.na(admissions.v2[,4])))
    summary(admissions.v3)
    

## Is there another race category?
  ## Race Counts
  sum(is.na(admissions.v3)) ## No NA's in dataset, 7870 observations
  ## White / Asian / Black
  sum(admissions.v3$anglo==1) + sum(admissions.v3$asian==1) + sum(admissions.v3$black==1) ## 6582
  7870 - 6582 #1288
  ## Another level? or Multiple?
  sum(admissions.v3$anglo==0 & admissions.v3$asian==0 & admissions.v3$black==0) #1288, checks out
    ## Create 4th level for race [no idea how to do this with functions, so why not use some cute math :-)...]
    admissions.v3$other <- as.numeric(admissions.v3$anglo) + as.numeric(admissions.v3$asian) + as.numeric(admissions.v3$black)
    admissions.v3$other <- admissions.v3$other - 4
    admissions.v3$other <- admissions.v3$other * -1
    admissions.v3$other <- as.factor(admissions.v3$other)
  

## More SAT Issues, 0 values
  ## SAT Math = 0
  sum(admissions.v3$sati.math == 0)
  ## 408
  
  ## SAT Verb = 0
  sum(admissions.v3$sati.verb == 0)
  ## 408
  
  ## Not a coincidence --> Likely took the ACT
    ## check if systematic deviance from normal acceptance rate
    admitted.v3 <- subset(admissions.v3, admissions.v3$admit == 1)
    summary(admitted.v3)
    2407/7870 * 100 ## subset base admissions rate = 30.58% (negliblby different from original 30.87%)
    sum(admitted.v3$sati.math == 0)/sum(admissions.v3$sati.math == 0) * 100 ## 10.05% --> VERY DIFFERENT, need to consider
    sum(admitted.v3$sati.verb == 0)/sum(admissions.v3$sati.verb == 0) * 100 ## sanity check, verbal == math
    sub.test <- subset(admissions.v3, admissions.v3$admit == 1 & admissions.v3$sati.verb == 0)
    summary(sub.test)
    sub.test2 <- subset(admissions.v3, admissions.v3$sati.verb == 0) 
    sort(sub.test2$gpa.wtd)

## Final Dataset
    admissions.final <- admissions.v3

##### Univariate Analysis
  ## GPA
  ## Check if GPA is normally distributed (verifying no systematic deviances due to mis-haps in weighting)
  ggp.v3 <- ggplot(admissions.final, aes(gpa.wtd)) 
  ggp.v3 + geom_histogram(fill="lightgreen") + labs(x = "GPA (Weighted)", y = "Count", title = "Fig 4.1: GPA Histogram", 
                          subtitle = "Median GPA = 3.85")## looks normal without any systematic deviance // bi-modalities
  ## SAT Scores
  ## Math
  summary(admissions.final$sati.math)
  ggp.v4 <- ggplot(admissions.final, aes(sati.math)) 
  ggp.v4 + geom_histogram(fill="lightgreen") + labs(x = "SAT Math", y = "Count", title = "Fig 4.2: SAT Math", 
                          subtitle = "Median Math SAT = 630")
  ## Verb
  summary(admissions.final$sati.verb)
  ggp.v5 <- ggplot(admissions.final, aes(sati.verb)) 
  ggp.v5 + geom_histogram(fill="lightgreen") + labs(x = "SAT Verb", y = "Count", title = "Fig 4.3: SAT Verb", 
                                                    subtitle = "Median Verb SAT = 580")
  ## Combined
  ## Create new combined/total SAT variable
  admissions.final$sati.total <- admissions.final$sati.math + admissions.final$sati.verb
  summary(admissions.final$sati.total)
  ggp.v6 <- ggplot(admissions.final, aes(sati.total)) 
  ggp.v6 + geom_histogram(fill="lightgreen") + labs(x = "SAT Total", y = "Count", title = "Fig 4.4: SAT Total", 
                                                    subtitle = "Median Total SAT = 1210")
  ## combine 
  library(gridExtra)
  ggp.v3 <- ggplot(admissions.final, aes(gpa.wtd)) + geom_histogram(fill="lightgreen") + labs(x = "GPA (Weighted)", y = "Count", title = "Fig 4.1: GPA Histogram", 
                                                    subtitle = "Median GPA = 3.85")
  ggp.v4 <- ggplot(admissions.final, aes(sati.math)) + geom_histogram(fill="lightgreen") + labs(x = "SAT Math", y = "Count", title = "Fig 4.2: SAT Math", 
                                                    subtitle = "Median Math SAT = 630")
  ggp.v5 <- ggplot(admissions.final, aes(sati.verb)) + geom_histogram(fill="lightgreen") + labs(x = "SAT Verb", y = "Count", title = "Fig 4.3: SAT Verb", 
                                                    subtitle = "Median Verb SAT = 580")
  ggp.v6 <- ggplot(admissions.final, aes(sati.total)) + geom_histogram(fill="lightgreen") + labs(x = "SAT Total", y = "Count", title = "Fig 4.4: SAT Total", 
                                                    subtitle = "Median Total SAT = 1210")
  grid.arrange(ggp.v3, ggp.v4, ggp.v5, ggp.v6, nrow=2, ncol=2)
  
  ## Demographics: 
  ## Applicants by Race
  anglo.apps <- sum(admissions.final$anglo==1)
  asian.apps <- sum(admissions.final$asian==1)
  black.apps <- sum(admissions.final$black==1)
  other.apps <- sum(admissions.final$other==1)
  admit.apps.comp <- c(anglo.apps, asian.apps, black.apps, other.apps)
  admit.apps.comp
    ## Plot
    barplot(admit.apps.comp, col = "lightgreen",
          xlab = "Applicants: Anglo, Asian, Black, Other",
          ylab = "Count",
          main = "Fig 4.4: Applicant Count by Race",
          ylim = c(0, 4000))
    
  ## Income Histogram
    hist(admissions.final$income, col = "lightgreen",
         xlab = "Family Income ($)",
         ylab = "Count",
         main = "Fig 4.5: Applicant Count by Income",
         ylim = c(0, 3000))
  ## Original Income Histogram
    hist(admissions$income, col = "lightgreen",
         xlab = "Family Income ($)",
         ylab = "Count",
         main = "Fig 4.5: Applicant Count by Income",
         ylim = c(0, 3000))
    
  ## Gender Histogram
    ## Applicants by Sex
    male.apps <- sum(admissions.final$sex==1)
    female.apps <- sum(admissions.final$sex==0)
    admit.apps.comp.gen <- c(male.apps, female.apps)
    admit.apps.comp.gen
    barplot(admit.apps.comp.gen, col = "lightgreen",
         xlab = "Gender: Male, Female",
         ylab = "Count",
         main = "Fig 4.6: Applicant Count by Gender",
         ylim = c(0, 5000))
  
  ## Combine Plots
    par(mfrow=c(2,2))
      barplot(admit.apps.comp.gen, col = "lightgreen",
            xlab = "Gender: Male, Female",
            ylab = "Count",
            main = "Fig 4.5: Applicant Count by Gender",
            ylim = c(0, 5000))
      barplot(admit.apps.comp, col = "lightgreen",
              xlab = "Applicants: Anglo, Asian, Black, Other",
              ylab = "Count",
              main = "Fig 4.6: Applicant Count by Race",
              ylim = c(0, 4000))
      hist(admissions.final$income, col = "lightgreen",
           xlab = "Family Income ($)",
           ylab = "Count",
           main = "Fig 4.7: Transformed Applicant Count by Income",
           ylim = c(0, 3000))
      hist(admissions$income, col = "lightgreen",
           xlab = "Family Income ($)",
           ylab = "Count",
           main = "Fig 4.8: Original Applicant Count by Income",
           ylim = c(0, 3000))
    
  
###### Bivariate Analysis
  ## Setup / Basic Hist
  summary(admissions.final)
  total.admit.rate <- sum(admissions.final$admit==1)/length(admissions.final$admit)
  ggp <- ggplot(data.frame(admissions.final),aes(x=admit), aes(y=count))
  # histogram
  ggp + geom_bar(fill="lightgreen") + labs(x = "0=Rejected, 1=Accepted", y = "Count", 
                                           title = "Fig 5.1 Admissions Results (Final Dataset)", subtitle = "Acceptance Rate = 30.58%")
  
  ## Demographics (Race/Sex/$)
    ## Admissions Rates by Race
    anglo.admit.rate <- sum(admissions.final$admit==1 & admissions.final$anglo==1)/sum(admissions.final$anglo == 1)
    asian.admit.rate <- sum(admissions.final$admit==1 & admissions.final$asian==1)/sum(admissions.final$asian == 1)
    black.admit.rate <- sum(admissions.final$admit==1 & admissions.final$black==1)/sum(admissions.final$black == 1)
    other.admit.rate <- sum(admissions.final$admit==1 & admissions.final$other==1)/sum(admissions.final$other == 1)
    admit.rates.race.comp <- c(total.admit.rate, anglo.admit.rate, asian.admit.rate, black.admit.rate, other.admit.rate)
    admit.rates.race.comp
      ## Plot
      barplot(admit.rates.race.hold*100, col = "lightgreen",
              xlab = "Admit Rate: Total, Anglo, Asian, Black, Other",
              ylab = "Percent",
              main = "Fig 5.2: Admit Rate by Race",
              ylim = c(0, 40))
    ## Admissions by Income
    income.under.100 <- subset(admissions.final, admissions.final$income <=99998)
    income.over.100 <- subset(admissions.final, admissions.final$income == 99999)
    summary(income.under.100)
    summary(income.over.100)
    income.under.100.admit.rate <- 1769/(1769+4113)
    income.over.100.admit.rate <- 638/(638+1350)
    income.under.100.admit.rate
    income.over.100.admit.rate
    ## Admissions by Gender
    male.apps <- sum(admissions.final$sex==1)
    female.apps <- sum(admissions.final$sex==0)
    male.admits <- sum(admissions.final$sex==1 & admissions.final$admit==1)
    female.admits <- sum(admissions.final$sex==0 & admissions.final$admit==1)
    male.admit.rate <- male.admits/male.apps
    female.admit.rate <- female.admits/female.apps
    male.admit.rate
    female.admit.rate
  
    
  ## Merit (SAT/GPA)
    ## GPA Quartiles
    summary(admissions.final$gpa.wtd)
    top.gpa.q <- subset(admissions.final, admissions.final$gpa.wtd > 4.151)
    scnd.gpa.q <- subset(admissions.final, admissions.final$gpa.wtd <= 4.151 & admissions.final$gpa.wtd > 3.850)
    thrd.gpa.q <- subset(admissions.final, admissions.final$gpa.wtd <= 3.850 & admissions.final$gpa.wtd > 3.500)
    bot.gpa.q <- subset(admissions.final, admissions.final$gpa.wtd <= 3.500)
    top.gpa.admit.rate <- sum(top.gpa.q$admit == 1)/length(top.gpa.q$gpa.wtd)*100
    scnd.gpa.admit.rate <- sum(scnd.gpa.q$admit == 1)/length(scnd.gpa.q$gpa.wtd)*100
    thrd.gpa.admit.rate <- sum(thrd.gpa.q$admit == 1)/length(thrd.gpa.q$gpa.wtd)*100
    bot.gpa.admit.rate <- sum(bot.gpa.q$admit == 1)/length(bot.gpa.q$gpa.wtd)*100
    top.gpa.admit.rate
    scnd.gpa.admit.rate
    thrd.gpa.admit.rate
    bot.gpa.admit.rate
    plot(c(top.gpa.admit.rate,
           scnd.gpa.admit.rate,
           thrd.gpa.admit.rate,
           bot.gpa.admit.rate),
         xlab = "Quartiles (1-4)",
         ylab = "Admit Rate %",
         main = "Fig 5.3: Admit by GPA",
         pch = 16,
         cex = 1.75,
         col = "lightgreen")
    
    ## SAT Math
    summary(admissions.final$sati.math)
    top.satm.q <- subset(admissions.final, admissions.final$sati.math >= 690)
    scnd.satm.q <- subset(admissions.final, admissions.final$sati.math < 690 & admissions.final$sati.math >= 630)
    thrd.satm.q <- subset(admissions.final, admissions.final$sati.math < 630 & admissions.final$sati.math >= 550)
    bot.satm.q <- subset(admissions.final, admissions.final$sati.math < 550)
    top.satm.admit.rate <- sum(top.satm.q$admit == 1)/length(top.satm.q$sati.math)*100
    scnd.satm.admit.rate <- sum(scnd.satm.q$admit == 1)/length(scnd.satm.q$sati.math)*100
    thrd.satm.admit.rate <- sum(thrd.satm.q$admit == 1)/length(thrd.satm.q$sati.math)*100
    bot.satm.admit.rate <- sum(bot.satm.q$admit == 1)/length(bot.satm.q$sati.math)*100
    top.satm.admit.rate
    scnd.satm.admit.rate
    thrd.satm.admit.rate
    bot.satm.admit.rate
    plot(c(top.satm.admit.rate,
           scnd.satm.admit.rate,
           thrd.satm.admit.rate,
           bot.satm.admit.rate),
         xlab = "Quartiles (1-4)",
         ylab = "Admit Rate %",
         main = "Fig 5.4: Admit by SAT Math",
         pch = 16,
         cex = 1.75,
         col = "lightgreen")
    
    ## SAT Verb
    summary(admissions.final$sati.verb)
    top.satv.q <- subset(admissions.final, admissions.final$sati.verb >= 650)
    scnd.satv.q <- subset(admissions.final, admissions.final$sati.verb < 650 & admissions.final$sati.verb > 580)
    thrd.satv.q <- subset(admissions.final, admissions.final$sati.verb <= 580 & admissions.final$sati.verb > 500)
    bot.satv.q <- subset(admissions.final, admissions.final$sati.verb <= 500)
    top.satv.admit.rate <- sum(top.satv.q$admit == 1)/length(top.satv.q$sati.verb)*100
    scnd.satv.admit.rate <- sum(scnd.satv.q$admit == 1)/length(scnd.satv.q$sati.verb)*100
    thrd.satv.admit.rate <- sum(thrd.satv.q$admit == 1)/length(thrd.satv.q$sati.verb)*100
    bot.satv.admit.rate <- sum(bot.satv.q$admit == 1)/length(bot.satv.q$sati.verb)*100
    top.satv.admit.rate
    scnd.satv.admit.rate
    thrd.satv.admit.rate
    bot.satv.admit.rate
    plot(c( top.satv.admit.rate,
            scnd.satv.admit.rate,
            thrd.satv.admit.rate,
            bot.satv.admit.rate),
         xlab = "Quartiles (1-4)",
         ylab = "Admit Rate %",
         main = "Fig 5.5: Admit by SAT Verbal",
         pch = 16,
         cex = 1.75,
         col = "lightgreen")
    
    ## SAT Total
    summary(admissions.final$sati.total)
    top.satt.q <- subset(admissions.final, admissions.final$sati.total > 1320)
    scnd.satt.q <- subset(admissions.final, admissions.final$sati.total <= 1320 & admissions.final$sati.total >= 1210)
    thrd.satt.q <- subset(admissions.final, admissions.final$sati.total < 1210 & admissions.final$sati.total >= 1070)
    bot.satt.q <- subset(admissions.final, admissions.final$sati.total < 1070)
    top.satt.admit.rate <- sum(top.satt.q$admit == 1)/length(top.satt.q$sati.total)*100
    scnd.satt.admit.rate <- sum(scnd.satt.q$admit == 1)/length(scnd.satt.q$sati.total)*100
    thrd.satt.admit.rate <- sum(thrd.satt.q$admit == 1)/length(thrd.satt.q$sati.total)*100
    bot.satt.admit.rate <- sum(bot.satt.q$admit == 1)/length(bot.satt.q$sati.total)*100
    top.satt.admit.rate
    scnd.satt.admit.rate
    thrd.satt.admit.rate
    bot.satt.admit.rate
    plot(c( top.satt.admit.rate,
            scnd.satt.admit.rate,
            thrd.satt.admit.rate,
            bot.satt.admit.rate),
         xlab = "Quartiles (1-4)",
         ylab = "Admit Rate %",
         main = "Fig 5.5: Admit by SAT Total",
         pch = 16,
         cex = 1.75,
         col = "lightgreen")
    
    ## Par -- Graph all 4
    par(mfrow=c(2,2))
    plot(c(top.gpa.admit.rate,
           scnd.gpa.admit.rate,
           thrd.gpa.admit.rate,
           bot.gpa.admit.rate),
         xlab = "Quartiles (1-4)",
         ylab = "Admit Rate %",
         main = "Fig 5.3: Admit by GPA",
         pch = 16,
         cex = 1.75,
         col = "lightgreen")
    plot(c(top.satm.admit.rate,
           scnd.satm.admit.rate,
           thrd.satm.admit.rate,
           bot.satm.admit.rate),
         xlab = "Quartiles (1-4)",
         ylab = "Admit Rate %",
         main = "Fig 5.4: Admit by SAT Math",
         pch = 16,
         cex = 1.75,
         col = "lightgreen")
    plot(c( top.satv.admit.rate,
            scnd.satv.admit.rate,
            thrd.satv.admit.rate,
            bot.satv.admit.rate),
         xlab = "Quartiles (1-4)",
         ylab = "Admit Rate %",
         main = "Fig 5.5: Admit by SAT Verbal",
         pch = 16,
         cex = 1.75,
         col = "lightgreen")
    plot(c( top.satt.admit.rate,
            scnd.satt.admit.rate,
            thrd.satt.admit.rate,
            bot.satt.admit.rate),
         xlab = "Quartiles (1-4)",
         ylab = "Admit Rate %",
         main = "Fig 5.5: Admit by SAT Total",
         pch = 16,
         cex = 1.75,
         col = "lightgreen")
    
    ## Correlation 
    ##SAT M and SAT V
    cor(admissions.final$sati.math, admissions.final$sati.verb)
    ## SAT T and GPA
    cor(admissions.final$sati.total, admissions.final$gpa.wtd)
    
##### Multivariate Analysis (RF)
  ## No Misclassification costs
    # random forests
    rf1<-randomForest(admit~anglo+asian+black+other+gpa.wtd+sati.verb+sati.math+sati.total+income+sex,
      data=admissions.final,importance=T,ntree=750)
    rf1.conf <- rf1$confusion
    rf1.conf
    plot(rf1)
    print(rf1)
  ## Misclassification costs
    ## 4:1
    rf2<-randomForest(admit~anglo+asian+black+other+gpa.wtd+sati.verb+sati.math+sati.total+income+sex,
                      data=admissions.final,importance=T,ntree=750, sampsize=c(1000,1700))
    print(rf2)
    
    rf3<-randomForest(admit~anglo+asian+black+other+gpa.wtd+sati.verb+sati.math+sati.total+income+sex,
                      data=admissions.final,importance=T,ntree=750, sampsize=c(100,2000))
    print(rf3)
    
  ## Prediction Accuracy // Importance Plots
    # Variable Importance Plots
    par(mfrow=c(2,2))
    varImpPlot(rf2,type=1,scale=F,class=1,
               main="Forecasting Importance Plot for Admission
               (Unstandardized)",col="lightgreen",cex=1,pch=19)
    varImpPlot(rf2,type=1,scale=T,class=1,
               main="Forecasting Importance Plot for Admission
               (Standardized)", col="lightgreen",cex=1,pch=19)
    varImpPlot(rf2,type=1,scale=F,class=0,
               main="Forecasting Importance Plot for Rejection
               (Unstandardized)", col="lightgreen",cex=1,pch=19)
    varImpPlot(rf2,type=1,scale=T,class=0,
               main="Forecasting Importance Plot for Rejection
               (Standardized)", col="lightgreen",cex=1,pch=19)
    
    # Partial Dependence Plots
    par(mfrow=c(2,1))
    part1<-partialPlot(rf2,pred.data=admissions.final,x.var=gpa.wtd,
                       rug=T,which.class=1, main="Partial Dependence Plot for GPA on Admission", xlab="GPA",
                       ylab="Centered Log Odds")
    scatter.smooth(part1$x,part1$y,span=1/3,col="blue",pch=19, main="Partial Dependence Plot for GPA on Admission", xlab="GPA",
                   ylab="Centered Log Odds")
    par(mfrow=c(2,1))
    part2<-partialPlot(rf2,pred.data=admissions.final,x.var=sati.total,
                       rug=T,which.class=1, main="Partial Dependence Plot for SAT Total on Admission", xlab="SAT Score",
                       ylab="Centered Log Odds")
    scatter.smooth(part1$x,part1$y,span=1/3,col="blue",pch=19, main="Partial Dependence Plot for SAT Total on Admission", xlab="SAT Score",
                   ylab="Centered Log Odds")
LS0tCnRpdGxlOiAiU1RBVDk3NCBQcm9qZWN0ICMyOiBBcHBseWluZyBSRiB0byBDb2xsZWdlIEFkbWlzc2lvbnMgRGF0YSIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQojIyMjIyBCeTogSmVmZnJleSBFbGxpbmd0b24KIyMjIyMgOTowMGFtIE1vbi1XZWQgU2VjdGlvbgojIyMjIyA0LzE5LzE3CgojIyMgMS4gIEludHJvZHVjdGlvbjogQXNzZXNzaW5nIENvbGxlZ2UgQWRtaXNzaW9ucyBEYXRhCkhpZ2ggc2Nob29sIGd1aWRhbmNlIGNvdW5zZWxvcnMgdXNlIGNvbGxlZ2UgYWRtaXNzaW9ucyBkYXRhIHRvIHByb3Blcmx5IGFkdmlzZSBoaWdoIHNjaG9vbCBzZW5pb3JzIG9uIHdoZXJlIHRvIGFwcGx5IHRvIGNvbGxlZ2UuIFN0dWR5aW5nIHRoZSBnZW5lcmFsbHkgYXZhaWxhYmxlIHN1bW1hcnktc3RhdGlzdGljIGxldmVsIGFkbWlzc2lvbnMgZGF0YSBpcyBvbmUgc291cmNlIG9mIHZhbHVhYmxlIGluc2lnaHQgaW50byBjb2xsZWdlIHNlbGVjdGlvbiBjcml0ZXJpYSBhbmQgdGhlIGFkbWlzc2lvbnMgcHJvY2Vzcy4gSG93ZXZlciwgc3VtbWFyeS1zdGF0aXN0aWMgbGV2ZWwgZGF0YSBpcyBhIGNydWRlIHRvb2wgZm9yIHByb3ZpZGluZyBhbnkgc3BlY2lmaWMgc3R1ZGVudCB3aXRoIGluc2lnaHQgaW50byBoaXMgb3IgaGVyIGluZGl2aWR1YWwgY2hhbmNlIG9mIGJlaW5nIGFkbWl0dGVkIHRvIGEgcGFydGljdWxhciB1bml2ZXJzaXR5LiBVc2luZyBpbmRpdmlkdWFsIGxldmVsIGFkbWlzc2lvbnMgZGF0YSBpcyBhIHBvdGVudGlhbGx5IHVzZWZ1bCBtZXRob2QgdG8gYm9sc3RlciBhIGd1aWRhbmNlIGNvdW5zZWxvcidzIGFiaWxpdHkgdG8gcHJvdmlkZSB0YWlsb3JlZCBndWlkYW5jZSB0byBlYWNoIHN0dWRlbnQuIFVuZm9ydHVuYXRlbHksIGdhdGhlcmluZyBpbmRpdmlkdWFsIGxldmVsIGRhdGEgZnJvbSB0aGUgb3ZlciAyLDAwMCBjb2xsZWdlcyBpbiB0aGUgVVNBIGlzIGV4cGVuc2l2ZSwgaWYgbm90IGltcG9zc2libGUuIEFzIGEgcmVzdWx0LCB3ZSB3aWxsIGV4YW1pbmUgaW5kaXZpZHVhbCBsZXZlbCBhZG1pc3Npb25zIG91dGNvbWUgZGF0YSBmcm9tIG9uZSAnZWxpdGUnIFVTIHVuaXZlcnNpdHkuIFRoaXMgaW5mb3JtYXRpb24gY2FuIGJlIGFuYWx5emVkIHRvIGhlbHAgYWR2aXNlIHN0dWRlbnRzIG9uIHRoZWlyIHByb3NwZWN0cyBvZiBiZWluZyBhZG1pdHRlZCB0byB0aGlzIHBhcnRpY3VsYXIgZWxpdGUgdW5pdmVyc2l0eSAoYXQgbGVhc3QgaWYgdGhleSBoYWQgdGhlb3JldGljYWxseSBhcHBsaWVkIGluIHRoZSBnaXZlbiB5ZWFyIGZyb20gd2hpY2ggb3VyIGRhdGEgd2FzIGNvbGxlY3RlZCkuIEFkZGl0aW9uYWxseSwgd2Ugd2lsbCBleHBsb3JlIGlmIGl0J3MgcmVzcG9uc2libGUgdG8gZXh0cmFwb2xhdGUgdGhlc2UgZmluZGluZ3MgdG8gb3RoZXIgeWVhcnMgYXQgdGhpcyBzcGVjaWZpYyB1bml2ZXJzaXR5IGFuZCBwb3RlbnRpYWxseSB0byBvdGhlciBzaW1pbGFybHkgZWxpdGUgdW5pdmVyc2l0aWVzLiAKCiMjIyAyLiAgRGF0YSBEZXNjcmlwdGlvbgpBdCBmaXJzdCBnbGFuY2UsIGl0IGlzIHVuY2xlYXIgaG93IHRoZSBkYXRhIHdlcmUgY29sbGVjdGVkLiBUaGUgOCw3MDAgb2JzZXJ2YXRpb25zIHdlcmUgb2J0YWluZWQgZnJvbSBsYXN0IHllYXIncyBhcHBsaWNhbnRzIGF0IGEgcGFydGljdWxhciAnZWxpdGUnIHVuaXZlcnNpdHkuIEl0J3Mgbm90IHNwZWNpZmllZCB3aGV0aGVyIHRoaXMgaXMgYSBzYW1wbGUgb2YgdGhlIGRhdGEgb3IgdGhlIGNvbXBsZXRlIHNldCBvZiBvYnNlcnZhdGlvbnMuIFF1aWNrbHkgcmV2aWV3aW5nIHB1YmxpY2x5IGF2YWlsYWJsZSBDbGFzcyBvZiAyMDIwIGFkbWlzc2lvbnMgZGF0YSBmcm9tIHRoZSBlaWdodCBJdnkgTGVhZ3VlIHNjaG9vbHMgcmV2ZWFscyB0aGUgcmFuZ2UgaW4gdGhlIG51bWJlciBvZiBhcHBsaWNhbnRzIHN0cmV0Y2hlcyBmcm9tIDIwLDY3NSAoRGFydG1vdXRoKSB0byA0NCw5NjYgKENvcm5lbGwpLiBUaGUgYXZlcmFnZSBudW1iZXIgb2YgYXBwbGljYW50cyBhY3Jvc3MgdGhlIGVpZ2h0IHNjaG9vbHMgMzMsOTIzLiBHaXZlbiB0aGlzLCBpdCBpcyBhbG1vc3QgY2VydGFpbiBvdXIgOCw3MDAgb2JzZXJ2YXRpb25zIGFyZSBhIHNhbXBsZSBmcm9tIGEgYnJvYWRlciBzZXQgb2Ygb2JzZXJ2YXRpb25zIGF0IHRoaXMgcGFydGljdWxhciBzY2hvb2wuIFdlIGNhbid0IGFzc3VtZSB0aGF0IHRoaXMgc2FtcGxlIGlzIHJlcHJlc2VudGF0aXZlIG9mIHRoZSBwcmVzdW1lZCBicm9hZGVyIGFwcGxpY2FudCBwb29sLiBGaW5hbGx5LCB0aGUgZGF0YSB2aW9sYXRlIHRoZSBJSUQgYXNzdW1wdGlvbjogYWRtaXR0aW5nIG9uZSBhcHBsaWNhbnQgaGFzIGFuIGVmZmVjdCBvbiBzdWJzZXF1ZW50IGFwcGxpY2FudHMgZ2l2ZW4gdGhlcmUgaXMgYSByZWxhdGl2ZWx5IGZpeGVkIGNsYXNzIHNpemUgYW5kIGNvbXBvc2l0aW9uLiBGb3Igb3VyIHRyZWF0bWVudCwgd2Ugd2lsbCBhc3N1bWUgdGhlIGRhdGEgd2VyZSAqKm5vdCoqIGdlbmVyYXRlZCB2aWEgYSByYW5kb20gcHJvY2Vzcy4gSW5zdGVhZCwgZm9yIG91ciB0cmVhdG1lbnQsIHdlIHdpbGwgZXhhbWluZSB0aGUgOCw3MDAgb2JzZXJ2YXRpb25zIG9mIG5pbmUgdmFyaWFibGVzIGFzc3VtaW5nIHRoZSBkYXRhIHdlcmUgbm90IGNvbGxlY3RlZCByYW5kb21seS4gVGhlIG5pbmUgdmFyaWFibGVzIGFyZToKCiAgICAxLiBhZG1pdCDigJQgQ29kZWQg4oCcMeKAnSBmb3IgYWRtaXQgYW5kIOKAnDDigJ0gZm9yIHJlamVjdC4KICAgIDIuIGFuZ2xvIOKAlCBDb2RlZCDigJwx4oCdIGZvciBhbmdsbyBhbmQg4oCcMOKAnSBvdGhlcndpc2UuCiAgICAzLiBhc2lhbiDigJQgQ29kZWQg4oCcMeKAnSBmb3IgYXNpYW4gYW5kIOKAnDDigJ0gb3RoZXJ3aXNlLgogICAgNC4gYmxhY2sg4oCUIENvZGVkIOKAnDHigJ0gZm9yIGJsYWNrIGFuZCDigJww4oCdIG90aGVyd2lzZS4KICAgIDUuIGdwYS53dGQg4oCUIGhpZ2ggc2Nob29sIEdQQSB3ZWlnaHRlZCBmb3IgQVAgY291cnNlcy4gCiAgICA2LiBzYXRpLnZlcmIg4oCUIFNBVCB2ZXJiYWwgc2NvcmUuCiAgICA3LiBzYXRpLm1hdGgg4oCUIFNBVCBtYXRoIHNjb3JlLgogICAgOC4gaW5jb21lIOKAlCBob3VzZWhvbGQgaW5jb21lIGNhcHBlZCBhdCAkMTAwLDAwMC4gCiAgICA5LiBzZXgg4oCUIENvZGVkIOKAnDHigJ0gZm9yIG1hbGUgYW5kIOKAnDDigJ0gZm9yIGZlbWFsZS4KQWRtaXQgaXMgdGhlIHJlc3BvbnNlIHZhcmlhYmxlLiBJdCBpcyBhIGJpbmFyeSB2YXJpYWJsZSAoIjEiIGZvciBhZG1pdCAvICIwIiBmb3IgcmVqZWN0KS4gQWRtaXQgcmVzdWx0cyB3ZXJlIGNvbGxlY3RlZCBmb3IgdGhlIDgsNzAwIGFwcGxpY2FudHMsIHdoZXJlIGVhY2ggaW5kaXZpZHVhbCByZXN1bHQgaXMgYXNzdW1lZCB0byBiZSB0aGUgb3V0Y29tZSBvZiBhIGNvbnNpZGVyZWQgYWRtaXNzaW9ucyByZXZpZXcgcHJvY2VzcyAoaS5lLiBub3QgcmFuZG9tKS4gVGhlIHRhbGx5IGZvciBhZG1pc3Npb25zIHJlc3VsdHMgY2FuIGJlIHNlZW4gYmVsb3cgaW4gRmlnLiAyLjEuIDIsNjg2IG9mIHRoZSA4LDcwMCBzdHVkZW50cyB3ZXJlIGFkbWl0dGVkLCByZWZsZWN0aW5nIGEgMzAuODclIGFjY2VwdGFuY2UgcmF0ZSBmb3Igb3VyIHNwZWNpZmljIHNhbXBsZS4gTm90ZSB0aGF0IHRoaXMgZG9lcyAqKm5vdCoqIGltcGx5IHRoYXQgMzAuODclIGlzIHRoZSBvdmVyYWxsIGFjY2VwdGFuY2UgcmF0ZSBmb3IgdGhpcyBwYXJ0aWN1bGFyIHVuaXZlcnNpdHkuCgpUaGUgb3RoZXIgZWlnaHQgdmFyaWFibGVzIHdpbGwgYmUgZXhhbWluZWQgaW4gbXVjaCBncmVhdGVyIGRldGFpbCBvbmNlIHRoZXkndmUgYmVlbiBwcm9wZXJseSBjbGVhbmVkLiBIb3dldmVyLCBpdCdzIHdvcnRoIHN0YXRpbmcgdXAgZnJvbnQgdGhhdCB3aGlsZSB0aGVzZSBlaWdodCB2YXJpYWJsZXMgc2VlbSBpbXBvcnRhbnQgaW4gYW4gYW5hbHlzaXMgb2YgYWRtaXNzaW9ucywgdGhleSBhcmUgZmFyIGZyb20gY29tcGxldGUuIEFuIGV4cGVydCBvbiB1bml2ZXJzaXR5IGFkbWlzc2lvbnMsIGVzcGVjaWFsbHkgYXQgZWxpdGUgc2Nob29scywgY291bGQgbGlrZWx5IHBvaW50IHRvIHNldmVyYWwgb3RoZXIgaW1wb3J0YW50IG1pc3NpbmcgdmFyaWFibGVzLiBBIGZldyBleGFtcGxlcyBtaWdodCBpbmNsdWRlIGF0aGxldGljIG9yIGFydGlzdGljIHRhbGVudCwgbGVnYWN5IHJlbGF0aW9uc2hpcHMsIHNlcnZpY2Ugb3IgZW50cmVwcmVuZXVyaWFsIHJlY29nbml0aW9uLCBhbmQvb3IgdGhlIHF1YWxpdHkgb2YgcmVjb21tZW5kYXRpb24gbGV0dGVycy4gVGhlcmUgYXJlIGNvdW50bGVzcyBvdGhlcnMuIEluIGFkZGl0aW9uLCB3ZSBhbHNvIGhhdmUgbm8gZnJhbWV3b3JrIGZvciBleGlzdGluZyBpbmRpdmlkdWFsIGFkbWlzc2lvbnMgZGVjaXNpb25zL3BvbGljaWVzIGF0IHRoaXMgcGFydGljdWxhciAiZWxpdGUiIHVuaXZlcnNpdHksIHdoaWNoIG1heSBpbXBhY3QgdGhlIGFuYWx5c2lzLiBXaGF0ZXZlciBvdXIgZXN0aW1hdGUgb2YgdGhlIG1lYW4sIHdlIGNhbiBiZSBhc3N1cmVkIGl0IHdpbGwgYmUgbWlzc3BlY2lmaWVkIHdpdGhvdXQgZ3JlYXRlciBpbmZvcm1hdGlvbi4gV2l0aCB0aGF0IGNhdmVhdCBzdGF0ZWQsIHRoZXJlIGFyZSBzdGlsbCBtYW55IGF2ZW51ZXMgb2YgYW5hbHlzaXMgYXZhaWxhYmxlLiAoQWRtaW5pc3RyYXRpdmUgTm90ZTogd2hpbGUgYWxsIG9mIHRoZSB2YXJpYWJsZXMgYXJlIGxvd2VyLWNhc2UgaW4gdGhlIGRhdGEsIEkgd2lsbCByZWZlciB0byB0aGVtIHZpYSBjYXBpdGFsaXphdGlvbiB0byBtYWtlIGl0IGNsZWFyIHdlIGFyZSByZWZlcmVuY2luZyBhIHZhcmlhYmxlIChlLmcuICJhZG1pdCIgd2lsbCBiZSB3cml0dGVuICJBZG1pdCIpKS4KCmBgYHtyfQpnZ3AgPC0gZ2dwbG90KGRhdGEuZnJhbWUoYWRtaXNzaW9ucyksYWVzKHg9YWRtaXQpLCBhZXMoeT1jb3VudCkpCiMgY291bnRzCmdncCArIGdlb21fYmFyKGZpbGw9ImxpZ2h0Z3JlZW4iKSArIGxhYnMoeCA9ICIwPVJlamVjdGVkLCAxPUFjY2VwdGVkIiwgeSA9ICJDb3VudCIsIHRpdGxlID0gIkZpZyAyLjEgQWRtaXNzaW9ucyBSZXN1bHRzIiwgc3VidGl0bGUgPSAiQWNjZXB0YW5jZSBSYXRlID0gMzAuODclIikKYGBgCgojIyMgMy4gIERhdGEgQ2xlYW5pbmcKQmVmb3JlIGV4YW1pbmluZyB0aGUgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIEFkbWl0IGFuZCB0aGUgb3RoZXIgZWlnaHQgdmFyaWFibGVzLCB3ZSB3aWxsIGZpcnN0IG5lZWQgdG8gY2xlYW4gYW5kIHByZXBhcmUgdGhlIGRhdGEuIFJldmlld2luZyB0aGUgc3VtbWFyeSBzdGF0aXN0aWNzIGZvciB0aGUgbmluZSB2YXJpYWJsZXMgYmVsb3csIGEgbnVtYmVyIG9mIGlzc3VlcyBpbW1lZGlhdGVseSBwb3Agb3V0LgpgYGB7cn0Kc3VtbWFyeShhZG1pc3Npb25zKQpgYGAKQWZ0ZXIgc3lzdGVtYXRpY2FsbHkgcmV2aWV3aW5nIHRoZSBkYXRhLCBlaWdodCBkYXRhIGNsZWFuaW5nL3RyYW5zZm9ybWF0aW9uIGlzc3VlcyBhcmUgYXBwYXJlbnQuIAoKMS4gVGhlIHJlc3BvbnNlIHZhcmlhYmxlLCBBZG1pdCwgaXMgZW5jb2RlZCBhcyBhbiBpbnRlZ2VyL251bWVyaWMgdmFyaWFibGUgKGFzIG9wcG9zZWQgdG8gZmFjdG9yKS4gV2Ugd2lsbCBuZWVkIHRvIGNvLWVyY2UgaXQgdG8gYSBmYWN0b3IgdmFyaWFibGUgdG8gZW5zdXJlIG91ciBmdXR1cmUgYW5hbHlzaXMgdXNlcyB0aGUgcHJvcGVyIHN0YXRpc3RpY2FsIHRlY2huaXF1ZXMuIFdlIHdpbGwgYWxzbyBkbyB0aGlzIGZvciBhbGwgb3RoZXIgY2F0ZWdvcmljYWwgdmFyaWFibGVzIChBbmdsbywgQXNpYW4sIEJsYWNrLCBhbmQgU2V4KS4gCjIuIEl0J3MgaW1tZWRpYXRlbHkgYXBwYXJlbnQgQW5nbG8gaGFzIGV4dHJhbmVvdXMgdmFsdWVzLiBXZSB3aWxsIHRyYW5zZm9ybSAid2hpdGUiIHRvICIxIiIgZm9yIG9ic2VydmF0aW9uICMyMDAgKGFuZCByZWNlZGUgIk5BIiB0byAwIGZvciBBc2lhbi9CbGFjayBpbiB0aGlzIGNhc2UpIGFuZCB0cmFuc2Zvcm0gZnJvbSAiMTAiIHRvICIxIiBmb3Igb2JzZXJ2YXRpb24gIzEwLCBnaXZlbiBCbGFjayBhbmQgQXNpYW4gYXJlIGJvdGggMC4gSXQncyB3b3J0aCBub3RpbmcgdGhpcyBldmlkZW5jZSBmb3Igb2JzZXJ2YXRpb24gIzEwIGlzIG5vdCBjb25jbHVzaXZlLCBidXQgbW9zdCBsaWtlbHkgY29ycmVjdC4gQWRkaXRpb25hbGx5LCBBbmdsbyB3YXMgaW5pdGlhbGx5IGlucHV0IGFzIGNoYXJhY3RlciBkYXRhLCBzbyB3ZSB0cmFuc2Zvcm1lZCB0byBmYWN0b3IgdHlwZS4gCjMuIFR1cm5pbmcgb3VyIGF0dGVudGlvbiB0byB0aGUgdHJlYXRtZW50IG9mIHRoZSBjYXRlZ29yaWNhbCB2YXJpYWJsZXMgYXNzb2NpYXRlZCB3aXRoIHJhY2UgKEFuZ2xlLCBBc2lhbiwgQmxhY2spLCBpdCdzIHF1aWNrbHkgY2xlYXIgdGhhdCBhIHN1YnNldCBvZiBvYnNlcnZhdGlvbnMgKDEyODgpIGFyZSBjb2RlZCAiMCIgZm9yIGFsbCB0aHJlZS4gVGhpcyBjb3VsZCByZWZsZWN0IG1pc3NpbmcgZGF0YS4gSG93ZXZlciwgZ2l2ZW4gdGhlcmUgYXJlIDgyOCBhY3R1YWwgTkEgdmFsdWVzLCBpdCdzIG1vcmUgbGlrZWx5IHRoYXQgdGhpcyBpbXBsaWVzIGFub3RoZXIgY2F0ZWdvcnkgb2YgcmFjZS4gV2Ugd2lsbCBhZGQgIk90aGVyIiBhcyBhbm90aGVyIGZhY3RvciB2YXJpYWJsZSBmb3IgdGhlc2UgMTI4OCBvYnNlcnZhdGlvbnMgKG5vdGU6IHVzZWQgYSBjdXRlIG1hdGhlbWF0aWNhbCB0cmFuc2Zvcm1hdGlvbiB0byBjcmVhdGUgdGhlIGZhY3RvciEpLiAKNC4gVGhlIDgyOCByZW1haW5pbmcgbWlzc2luZyB2YWx1ZXMgKE5BcykgZm9yIHJhY2UgcHJvdmVkIHRvIGJlIHBlcnBsZXhpbmcuIFRoYW5rZnVsbHksIHRoZSBOQXMgd2VyZSBzeXN0ZW1hdGljYWxseSBwbGFjZWQgaW4gdGhlIHNhbWUgcm93cyBhbW9uZyBBbmdsbywgQXNpYW4sIGFuZCBCbGFjayAoYXMgb3Bwb3NlZCB0byBiZSByYW5kb21seSBzY2F0dGVyZWQpLiBUaGUgbWlzc2luZyB2YWx1ZXMgcmVwcmVzZW50ZWQgfjEwJSBvZiB0aGUgdG90YWwgZGF0YSBzZXQsIHlldCB0aGVyZSB3YXMgbm8gb2J2aW91cyB3YXkgdG8gcHJvcGVybHkgdXNlIHRoZSBkYXRhLiBHaXZlbiBvdXIgcmVzcG9uc2UgaXMgQWRtaXQsIGl0IHNlZW1lZCBsb2dpY2FsIHRvIGZpcnN0IGNoZWNrIHRoZSBvdmVyYWxsIHNhbXBsZSBwb3B1bGF0aW9uIGFkbWl0dGFuY2UgcmF0ZSB3aXRoIHRoZSBzdWItcG9wdWxhdGlvbiByYXRlIG9mIE5BcyBhY3Jvc3MgQW5nbG8sIEFzaWFuLCBhbmQgQmxhY2suIFRoZSByZXN1bHRzIHdlcmUgc2ltaWxhcjogMzAuODclIHZzLiAzMy42OSYsIHJlc3BlY3RpdmVseS4gVGhlIE5BJ3Mgd2VyZSBiZWluZyBhZG1pdHRlZCBhdCBhIHNsaWdodGx5IGhpZ2hlciByYXRlLiBXaGlsZSB0aGUgZGlmZmVyZW5jZSBpcyBsaWtlbHkgbWF0ZXJpYWwsIG5vdCB0byBtZW50aW9uIGN1dHRpbmcgMTAlIG9mIHRoZSBkYXRhIHNldCBpcyByZWFsbHkgcGFpbmZ1bCwgSSBkaWRuJ3Qga25vdyBob3cgdG8gY29kZSBhIGdvb2QgdHJhbnNmb3JtYXRpb24gb2YgdGhlIGRhdGEuIFNpbXBseSBpbXB1dGluZyB0aGUgbW9kZSB3b3VsZCBoYXZlIHRyZWF0ZWQgdGhlIE5BJ3MgYXMgQXNpYW4uIEludHVpdGl2ZWx5IHRoaXMgZmVsdCB3cm9uZy4gSWRlYWxseSwgSSB3b3VsZCBoYXZlIGJlZW4gYWJsZSB0byByZWdyZXNzIGFsbCB0aGUgb3RoZXIgdmFyaWFibGVzIHRvIHByZWRpY3QgcmFjZTsgaG93ZXZlciwgSSBkaWQgbm90IGhhdmUgdGhlIFIgYmFja2dyb3VuZCB0byBjb21wbGV0ZSB0aGlzLiBVbHRpbWF0ZWx5LCBJIGN1dCB0aGUgODI4IE5BJ3MuCjUuIEluIGFkZGl0aW9uLCBJIHJlbW92ZWQgdGhlIHR3byByZW1haW5pbmcgTkEncyBmcm9tIFNleC4gVGhpcyB3YXMgbGVzcyB0aGFuIDAuMDElIG9mIHRoZSBkYXRhLCBzbyBmZWx0IGltbWF0ZXJpYWwuCjYuIEluY29tZSBoYWQgYSBtYXggdmFsdWUgZW5jb2RlZCBhcyAkOTksOTk5LiBHaXZlbiAyNSUgd2FzIGNvZGVkIGFzICQ5OSw5OTksIHdlIHdpbGwgdHJlYXQgdGhpcyBhcyBhIGNhcHBlZCB2YWx1ZSBhbmQgbm90IGFuIGFydGlmYWN0L21pcy1yZXBvcnRlZCBzdGF0aXN0aWMgaW1wbHlpbmcgc29tZSB0eXBlIG9mIG1pc3NpbmcgdmFsdWUuIEZ1cnRoZXJtb3JlLCBJbmNvbWUgaGFkIDE3MjQgbWlzc2luZyB2YWx1ZXMgY29kZWQgYXMgTkFzLiAxNzI0IG9ic2VydmF0aW9ucyB3YXMgY2VydGFpbmx5IHRvbyBtYW55IHRvIHN0cmlrZSBmcm9tIHRoZSBhbmFseXNpcy4gQWdhaW4sIGl0IG1hZGUgc2Vuc2UgdG8gY29tcGFyZSB0aGUgb3ZlcmFsbCBzYW1wbGUgcG9wdWxhdGlvbiBhZG1pdHRhbmNlIHJhdGUgd2l0aCB0aGUgc3ViLXBvcHVsYXRpb24gcmF0ZSBvZiBOQXMgaW4gSW5jb21lLiBUaGUgcmVzdWx0cyB3ZXJlIHNpbWlsYXI6IDMwLjg3JSB2cy4gMzMuNTgmLCByZXNwZWN0aXZlbHkuIE1pc3NpbmcgdmFsdWVzIGZvciBJbmNvbWUgYXJlIGFzc29jaWF0ZWQgd2l0aCBhIHNsaWdodGx5IGhpZ2hlciBhY2NlcHRhbmNlIHJhdGUuIFdpdGhvdXQgbW9yZSBpbmZvcm1hdGlvbiwgaXQgc2VlbWVkIHRoZSBiZXN0IG9wdGlvbiB3YXMgdG8gaW1wdXRlIHRoZSBOQXMgYXMgdGhlIG1lZGlhbiB2YWx1ZSBvZiAkNjVrLiBXaGlsZSBub3QgcGVyZmVjdCwgSSBzYXcgbm8gYmV0dGVyIHdheSB0byBpbmNsdWRlIHRoZXNlIHZhbHVlcyB3aXRob3V0IGFkZGluZyBub2lzZS4gVGhpcyBpcyBhZG1pdHRlZGx5IGxlc3MgdGhhbiBwZXJmZWN0LCBhcyB0aGVyZSBjb3VsZCBiZSBtYW55IHJlYXNvbnMgYmV5b25kIHJhbmRvbWx5IG1pc3NpbmcgZGF0YSB0aGF0IE5BcyBleGlzdC4gUGVyaGFwcyB0aGUgYXBwbGljYW50IHdhbnRzIHRvIGhpZGUgdGhhdCB0aGV5IGFyZSB1bmFibGUgdG8gcGF5IGZvciBjb2xsZWdlICoqb3IqKiB0aGF0IHRoZXkgYXJlIHdlYWx0aHkgYW5kIHVuZGVyIHBlcmZvcm1pbmcgZm9yIHNvbWUgcmVhc29uLiBUaGVyZSBhcmUgYSBtdWx0aXR1ZGUgb2YgcmVhc29uYWJsZSBleHBsYW5hdGlvbnMuIAo3LiBUaGUgbWluaW11bSBHUEEgd2FzIGluaXRpYWxseSByZXBvcnRlZCBhcyAtMy4yMC4gVGhpcyB3YXMgc2ltcGx5IGludmVydGVkIHRvIDMuMjAuIEFkZGl0aW9uYWxseSwgMTkgR1BBcyB3ZXJlIGVudGVyZWQgYXMgMC4gR2l2ZW4gdGhlIHNtYWxsIHByb3BvcnRpb24sIHRoZSB2YWx1ZXMgd2VyZSB0cmFuc2Zvcm1lZCB0byB0aGUgbWVhbiBvZiAzLjgwLgo4LiBQb3NzaWJsZSBTQVQgc2NvcmVzIHJhbmdlIGZyb20gMjAwLTgwMCwgd2hpY2ggcXVpY2tseSBoaWdobGlnaHRlZCB0d28gZXJyb3JzLiBPbmUgdmVyYmFsIHNjb3JlIHdhcyBlbnRlcmVkIGFzIDUsMjQwLiBHaXZlbiB0aGlzIHN0dWRlbnQgd2FzIGFkbWl0dGVkLCB0aGUgc2NvcmUgd2FzIHRyYW5zZm9ybWVkIHRvIHRoZSBtZWRpYW4gc2NvcmUgb2YgYWRtaXR0ZWQgc3R1ZGVudHMgKDY1MCkuIE1vcmUgcGVycGxleGluZyB3ZXJlIHRoZSA0NTcgMCdzIGZvciBib3RoIFNBVCBNYXRoIGFuZCBWZXJiYWwuIE5vdCBjb2luY2lkZW50YWxseSwgdGhlIDAgdmFsdWVzIHdlcmUgYWx3YXlzIHBhaXJlZCB0b2dldGhlciwgaW1wbHlpbmcgc29tZSB0eXBlIG9mIHN5c3RlbWF0aWMgZWZmZWN0LiBUaGUgbW9zdCBsaWtlbHkgc2NlbmFyaW9zIGFyZSB0aGUgc3R1ZGVudHMgZWl0aGVyIGEpIHRvb2sgdGhlIEFDVCBhbmQgZGlkIG5vdCByZXBvcnQgU0FUIHNjb3JlcyBvciBiKSB3ZXJlIHBlcm1pdHRlZCB0byBiZSBjb25zaWRlcmVkIGZvciBhZG1pc3Npb24gd2l0aG91dCBhbnkgc3RhbmRhcmRpemVkIHRlc3Qgc2NvcmVzLiBObyB0cmFuc2Zvcm1hdGlvbnMgd2VyZSBtYWRlIHRvIHRoZXNlIHZhbHVlcywgZ2l2ZW4gdGhlcmUgaXMgbGlrZWx5IHNpZ25hbCBpbiB0aGUgZGVjaXNpb24gbm90IHRvIHJlcG9ydC4gV2Ugd2lsbCBleGFtaW5lIGZ1cnRoZXIgbGF0ZXIuCgpBcyBhIHJlc3VsdCBvZiB0aGVzZSB0cmFuc2Zvcm1hdGlvbnMsIHRoZSBmaW5hbCBkYXRhIHNldCB0byBiZSB1c2VkIGZvciBhbmFseXNpcyBjb250YWlucyA3LDg3MCBvYnNlcnZhdGlvbnMgb2YgMTAgdmFyaWFibGVzLgoKIyMjIDQuICBBbmFseXNpcyBvZiBVbml2YXJpYXRlIFN0YXRpc3RpY3MKU3RhcnRpbmcgd2l0aCBtZXJpdCBiYXNlZCBwcmVkaWN0b3JzLCBsZXQncyBleGFtaW5lIEdQQSBhbmQgU0FUIHNjb3Jlcy4gR1BBIGlzIHJlcG9ydGVkIG9uIGEgd2VpZ2h0ZWQgc2NhbGUgZnJvbSAwLjAwIC0gNS4wMCwgd2l0aCBhbiBhY3R1YWwgcmFuZ2Ugb2YgMS4yNSB0byA0Ljk1LiBUaGUgbWVkaWFuIGlzIDMuODUgYW5kIHRoZSBtZWFuIGlzIDMuODAuIFRoZSBoaXN0b2dyYW0gaW4gRmlnIDQuMSBmb2xsb3dzIGEgbm9ybWFsIGRpc3RyaWJ1dGlvbiwgd2l0aCBhIHNsaWdodCBsZWZ0LXNrZXcuIFRoaXMgaXMgZ29vZCwgYXMgaXQgaW1wbGllcyB0aGVyZSdzIG5vIGFydGlmYWN0cy9zeXN0ZW1hdGljIGVycm9ycyBpbiB0aGUgZGF0YSB3aGVuIGNvbnZlcnRpbmcgZnJvbSBub24td2VpZ2h0ZWQgdG8gd2VpZ2h0ZWQgR1BBcy4gSW5kaXZpZHVhbCBNYXRoL1ZlcmJhbCBTQVQgc2NvcmVzIGhpZ2hsaWdodCB0d28gaW50ZXJlc3RpbmcgZmVhdHVyZXMgb2YgdGhlIGRhdGEuIEZpcnN0LCBhcyBub3RlZCBkdXJpbmcgRURBLCB0aGVyZSBhcmUgYSBudW1iZXIgb2YgU0FUIFNjb3JlcyBhdCAwLiBXZSdyZSBsZWF2aW5nIHRoZXNlIGFzIGlzLCBldmVuIHRob3VnaCBpdCdzIGltcG9zc2libGUgdG8gc2NvcmUgMCAocmFuZ2UgaXMgMjAwLTgwMCkuIFRoZXkgYXJlIGluZGljYXRpdmUgb2Ygc29tZXRoaW5nIG90aGVyIHRoYW4gbWlzc2luZyBkYXRhIC0tIGxpa2VseSBhbiBhY3RpdmUgZGVjaXNpb24gdG8gdGFrZSB0aGUgQUNUIG9yIG5vIHN0YW5kYXJkaXplZCB0ZXN0IGF0IGFsbC4gV2Ugc3VzcGVjdCB0aGlzIGRlY2lzaW9uIHdpbGwgYmUgaW5mb3JtYXRpdmUgaW4gdGhlIGFkbWlzc2lvbnMgZGVjaXNpb25zLiBTZWNvbmQsIHRoZSBpbmRpdmlkdWFsIHNjb3JlcyBmb2xsb3cgcm91Z2hseSBub3JtYWwgZGlzdHJpYnV0aW9ucywgdGhvdWdoIHRoZXJlIGlzIGEgZmFpciBiaXQgb2YgcmlnaHQtc2tldyBidW5jaGluZyBvbiBNYXRoIGF0IDgwMC4gV2hlbiBjb21iaW5lZCBpbnRvIGFuIFNBVCBUb3RhbCBzY29yZSwgdGhlIGRpc3RyaWJ1dGlvbiBiZWNvbWVzIGFsbW9zdCBwZXJmZWN0bHkgbm9ybWFsLiBUaGlzIGZsYXR0ZW5pbmcgb2YgdGhlIHJpZ2h0IHRhaWxzIGluIGJvdGggTWF0aCAoZXNwZWNpYWxseSkgYW5kIFZlcmJhbCAoc29tZXdoYXQpIGltcGxpZXMgdGhhdCB0aGVyZSBhIHNpZ25pZmljYW50IG51bWJlciBvZiBzdHVkZW50cyB3aG8gZG8gZXhjZXB0aW9uYWxseSB3ZWxsIG9uIGEgcGFydGljdWxhciBzZWN0aW9uLCBidXQgZmFyIGxlc3Mgc3R1ZGVudHMgZG8gZXhjZXB0aW9uYWxseSB3ZWxsIG9uICoqYm90aCoqIHNlY3Rpb25zLiBNb3N0IGltcG9ydGFudGx5LCBib3RoIEdQQSBhbmQgU0FUIFRvdGFsIHNjb3JlcyBmZWVsIHJlcHJlc2VudGF0aXZlIG9mIHdoYXQgd2Ugd291bGQgZXhwZWN0IHRvIHNlZSBvbiBhIG5hdGlvbmFsIGxldmVsIGZyb20gYSBkaXN0cmlidXRpb24gc2hhcGUuIEhvd2V2ZXIsIHRoZSBtZWFuL21lZGlhbiBzZWVtIHRvIGJlIHJpZ2h0IHNoaWZ0ZWQuIExvb2tpbmcgYXQgYWxsIFNBVCBkYXRhLCB0aGUgbWVhbiBzY29yZSBpcyAxMDIwIGZvciBhbGwgdGVzdCB0YWtlcnMgYW5kIDEwODAgZm9yIGFsbCBjb2xsZWdlIGJvdW5kIHN0dWRlbnRzLiBGcm9tIGEgTGV2ZWwgSSBwZXJzcGVjdGl2ZSwgdGhlIHNhbXBsZSBvZiBzdHVkZW50cyBhcHBseWluZyB0byB0aGlzIHBhcnRpY3VsYXIgImVsaXRlIiB1bml2ZXJzaXR5IGFyZSBhYm92ZSBhdmVyYWdlIG5hdGlvbmFsbHkuIE1vdmluZyB0byBhIExldmVsIElJIGFuYWx5c2lzLCBpdCBzZWVtcyBmYWlyIHRvIGV4dHJhcG9sYXRlIHRoYXQgdGhlcmUgaXMgYSBzZWxmLXNlbGVjdGlvbiBiaWFzIGZvciBiZXR0ZXIgc3R1ZGVudHMgdG8gYXBwbHkgdG8gYmV0dGVyIHVuaXZlcnNpdGllcy4gV2Ugd291bGQgZXhwZWN0IHRoaXMgdHJlbmQgdG8gaG9sZCBhY3Jvc3Mgb3RoZXIgZWxpdGUgdW5pdmVyc2l0aWVzIGFuZCBhY3Jvc3Mgb3RoZXIgeWVhcnMuIApgYGB7cn0KZ2dwLnYzIDwtIGdncGxvdChhZG1pc3Npb25zLmZpbmFsLCBhZXMoZ3BhLnd0ZCkpICsgZ2VvbV9oaXN0b2dyYW0oZmlsbD0ibGlnaHRncmVlbiIpICsgbGFicyh4ID0gIkdQQSAoV2VpZ2h0ZWQpIiwgeSA9ICJDb3VudCIsIHRpdGxlID0gIkZpZyA0LjE6IEdQQSBIaXN0b2dyYW0iLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1YnRpdGxlID0gIk1lZGlhbiBHUEEgPSAzLjg1IikKICBnZ3AudjQgPC0gZ2dwbG90KGFkbWlzc2lvbnMuZmluYWwsIGFlcyhzYXRpLm1hdGgpKSArIGdlb21faGlzdG9ncmFtKGZpbGw9ImxpZ2h0Z3JlZW4iKSArIGxhYnMoeCA9ICJTQVQgTWF0aCIsIHkgPSAiQ291bnQiLCB0aXRsZSA9ICJGaWcgNC4yOiBTQVQgTWF0aCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VidGl0bGUgPSAiTWVkaWFuIE1hdGggU0FUID0gNjMwIikKICBnZ3AudjUgPC0gZ2dwbG90KGFkbWlzc2lvbnMuZmluYWwsIGFlcyhzYXRpLnZlcmIpKSArIGdlb21faGlzdG9ncmFtKGZpbGw9ImxpZ2h0Z3JlZW4iKSArIGxhYnMoeCA9ICJTQVQgVmVyYiIsIHkgPSAiQ291bnQiLCB0aXRsZSA9ICJGaWcgNC4zOiBTQVQgVmVyYiIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VidGl0bGUgPSAiTWVkaWFuIFZlcmIgU0FUID0gNTgwIikKICBnZ3AudjYgPC0gZ2dwbG90KGFkbWlzc2lvbnMuZmluYWwsIGFlcyhzYXRpLnRvdGFsKSkgKyBnZW9tX2hpc3RvZ3JhbShmaWxsPSJsaWdodGdyZWVuIikgKyBsYWJzKHggPSAiU0FUIFRvdGFsIiwgeSA9ICJDb3VudCIsIHRpdGxlID0gIkZpZyA0LjQ6IFNBVCBUb3RhbCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VidGl0bGUgPSAiTWVkaWFuIFRvdGFsIFNBVCA9IDEyMTAiKQogIGdyaWQuYXJyYW5nZShnZ3AudjMsIGdncC52NCwgZ2dwLnY1LCBnZ3AudjYsIG5yb3c9MiwgbmNvbD0yKQpgYGAKCkxvb2tpbmcgYXQgZGVtb2dyYXBoaWMgZGF0YSwgd2Ugc2VlIGEgZmV3IGludGVyZXN0aW5nIGZhY2V0cyBvZiB0aGUgZGF0YS4gVGhlcmUgYXJlIHJvdWdobHkgMTclIG1vcmUgZmVtYWxlIGFwcGxpY2F0aW9ucyB0aGFuIG1hbGUuIFRoaXMgY291bGQgY2VydGFpbmx5IGJlIGEgcmVzdWx0IG9mIG91ciBzcGVjaWZpYyBzYW1wbGUuIEZyb20gYSBMZXZlbCBJSSBwZXJzcGVjdGl2ZSwgaG93ZXZlciwgdGhpcyB3b3VsZG4ndCBiZSBzdXJwcmlzaW5nLiBPbiBhbG1vc3QgZXZlcnkgbGV2ZWwgKGFwcGxpY2F0aW9ucywgZW5yb2xsbWVudCwgZ3JhZHVhdGlvbiwgZXRjLikgdGhlcmUgYXJlIG1vcmUgZmVtYWxlIHVuZGVyZ3JhZHVhdGVzIHRoYW4gbWFsZSByZXByZXNlbnRlZC4gR2l2ZW4gdGhpcywgaXQgZmVlbHMgcmVhc29uYWJsZSB0byB0cmVhdCB0aGlzIG1hbGUvZmVtYWxlIGFwcGxpY2FudCBjb3VudCBnYXAgYXMgcmVwcmVzZW50YXRpdmUgb2YgdGhlIGJyb2FkZXIgcG9wdWxhdGlvbi4gUmFjZSwgaG93ZXZlciwgcGFpbnRzIGEgZGlmZmVyZW50IHBpY3R1cmUuIEJsYWNrIHN0dWRlbnRzIG1hZGUgdXAgMzU2ICg0LjUlKSBvZiB0b3RhbCBhcHBsaWNhbnRzLiBXaGV0aGVyIHRoaXMgaXMgYSBMZXZlbCBJIHNhbXBsaW5nIGFub21hbHkgb3IgTGV2ZWwgSUkgYnJvYWRlciByZXByZXNlbnRhdGlvbiBvZiB0aGUgY29sbGVnZSBhcHBsaWNhbnQgcG9vbCBpcyBkaWZmaWN1bHQgdG8gc2F5LiBMb29raW5nIGF0IDIwMTUgYXBwbGljYXRpb25zIHRvICJlbGl0ZSIiIHNjaG9vbHMsIG1vc3Qgc2Nob29scyAoUGVubiwgQ29sdW1iaWEsIFlhbGUsIER1a2UsIGFtb25nIG1hbnkgb3RoZXJzIHJlcG9ydGluZykgaGFkIGJsYWNrIHN0dWRlbnQgYXBwbGljYW50cyBjb21wcmlzaW5nIDEwLTEzJSBvZiB0aGUgYXBwbGljYW50IHBvb2wsIGJldHdlZW4gMi0zeCBvdXIgNC41JS4gSW4gY29udHJhc3QsIFVDIEJlcmtlbGV5IHJlY2VpdmVkIGp1c3QgNC45JSBvZiBpdHMgYXBwbGljYW50cyBmcm9tIGJsYWNrIHN0dWRlbnRzIChsb3dlc3QgYW1vbmcgImVsaXRlIiBzY2hvb2xzKS4gV2hpbGUgdGhlcmUgaXMgbm8gY29uY2x1c2l2ZSBldmlkZW5jZSwgdXNpbmcgdGhlIGJlc3QgZGF0YSBhdmFpbGFibGUgc2VlbXMgdG8gaW5kaWNhdGUgdGhlIHByb3BvcnRpb24gb2YgYmxhY2sgc3R1ZGVudHMgaW4gb3VyIHNhbXBsZSBpcyBzeXN0ZW1hdGljYWxseSBsb3cuIExvb2tpbmcgYXQgSW5jb21lLCB3ZSBpbmNsdWRlZCB0d28gaGlzdG9ncmFtczogdGhlIG9yaWdpbmFsICh3aXRoIDE3MjQgTkFzIG9taXR0ZWQpIGFuZCB0aGUgZmluYWwgd2l0aCB0aGUgMTcyNCBOQXMgaW1wdXRlZCB0byB0aGUgbWVhbi4gVGhpcyBvYnZpb3VzbHkgc2tld2VkIHRoZSBkaXN0cmlidXRpb24sIGJ1dCBzZWVtZWQgdGhlICJmYWlyZXN0IiB3YXkgdG8gZG8gc28gd2l0aG91dCBhZGRpbmcgbm9pc2UuIFRoZSBkaXN0cmlidXRpb24gaXMgcmlnaHQgc2tld2VkLCB3aXRoIGFib3V0IDI1JSBvZiBwb3B1bGF0aW9uIG1ha2luZyBtb3JlIHRoYW4gJDEwMGsuIEFtb25nIGZhbWlsaWVzIHdpdGggaW5jb21lcyB1bmRlciAkMTAwaywgdGhlIGRpc3RyaWJ1dGlvbiBpcyBmYWlybHkgdW5pZm9ybS4KYGBge3J9CnBhcihtZnJvdz1jKDIsMikpCiAgICAgIGJhcnBsb3QoYWRtaXQuYXBwcy5jb21wLmdlbiwgY29sID0gImxpZ2h0Z3JlZW4iLAogICAgICAgICAgICB4bGFiID0gIkdlbmRlcjogTWFsZSwgRmVtYWxlIiwKICAgICAgICAgICAgeWxhYiA9ICJDb3VudCIsCiAgICAgICAgICAgIG1haW4gPSAiRmlnIDQuNTogQXBwbGljYW50IENvdW50IGJ5IEdlbmRlciIsCiAgICAgICAgICAgIHlsaW0gPSBjKDAsIDUwMDApKQogICAgICBiYXJwbG90KGFkbWl0LmFwcHMuY29tcCwgY29sID0gImxpZ2h0Z3JlZW4iLAogICAgICAgICAgICAgIHhsYWIgPSAiQXBwbGljYW50czogQW5nbG8sIEFzaWFuLCBCbGFjaywgT3RoZXIiLAogICAgICAgICAgICAgIHlsYWIgPSAiQ291bnQiLAogICAgICAgICAgICAgIG1haW4gPSAiRmlnIDQuNjogQXBwbGljYW50IENvdW50IGJ5IFJhY2UiLAogICAgICAgICAgICAgIHlsaW0gPSBjKDAsIDQwMDApKQogICAgICBoaXN0KGFkbWlzc2lvbnMuZmluYWwkaW5jb21lLCBjb2wgPSAibGlnaHRncmVlbiIsCiAgICAgICAgICAgeGxhYiA9ICJGYW1pbHkgSW5jb21lICgkKSIsCiAgICAgICAgICAgeWxhYiA9ICJDb3VudCIsCiAgICAgICAgICAgbWFpbiA9ICJGaWcgNC43OiBBcHAgQ291bnQgYnkgSW5jb21lIChJbXB1dGVkKSIsCiAgICAgICAgICAgeWxpbSA9IGMoMCwgMzAwMCkpCiAgICAgIGhpc3QoYWRtaXNzaW9ucyRpbmNvbWUsIGNvbCA9ICJsaWdodGdyZWVuIiwKICAgICAgICAgICB4bGFiID0gIkZhbWlseSBJbmNvbWUgKCQpIiwKICAgICAgICAgICB5bGFiID0gIkNvdW50IiwKICAgICAgICAgICBtYWluID0gIkZpZyA0Ljg6IEFwcCBDb3VudCBieSBJbmNvbWUgKE9yaWdpbmFsIiwKICAgICAgICAgICB5bGltID0gYygwLCAzMDAwKSkKYGBgCgojIyMgNS4gQW5hbHlzaXMgb2YgQml2YXJpYXRlIFN0YXRpc3RpY3MKUmVjcmVhdGluZyB0aGUgaGlzdG9ncmFtIChGaWcgNS4xKSBvZiB0aGUgcmVzcG9uc2UgdmFyaWFibGUgKEFkbWl0KSBmcm9tIHRoZSBmaW5hbCBkYXRhIHNldCwgd2Ugc2VlIHRoZSBhY2NlcHRhbmNlIHJhdGUgaGFzIGNoYW5nZWQgbWlub3JseSBmcm9tIDMwLjg3JSB0byAzMC41OCUuIEZyb20gaGVyZSB3ZSB3aWxsIHR1cm4gb3VyIGF0dGVudGlvbiB0byB1bmNvdmVyaW5nIHdoYXQgZmVhdHVyZXMgb2YgdGhlIGRhdGEgc2V0IGFyZSBwcmVkaWN0aXZlIGZvciBhZG1pc3Npb25zLgpgYGB7cn0KZ2dwIDwtIGdncGxvdChkYXRhLmZyYW1lKGFkbWlzc2lvbnMuZmluYWwpLGFlcyh4PWFkbWl0KSwgYWVzKHk9Y291bnQpKQojIGNvdW50cwpnZ3AgKyBnZW9tX2JhcihmaWxsPSJsaWdodGdyZWVuIikgKyBsYWJzKHggPSAiMD1SZWplY3RlZCwgMT1BY2NlcHRlZCIsIHkgPSAiQ291bnQiLCB0aXRsZSA9ICJGaWcgNS4xIEFkbWlzc2lvbnMgUmVzdWx0cyAoRmluYWwgRGF0YXNldCkiLCBzdWJ0aXRsZSA9ICJBY2NlcHRhbmNlIFJhdGUgPSAzMC41OCUiKQpgYGAKCkJhc2VkIG9uIGFuIGludHVpdGl2ZSBmaXJzdCBwYXNzLCBhdCBsZWFzdCBmb3IgYW55b25lIHdobydzIHNwZW50IHRpbWUgaW4gdGhlIGNvbGxlZ2UgYWRtaXNzaW9ucyBwcm9jZXNzLCBhbiBpbW1lZGlhdGUgcXVlc3Rpb24gcG9wcyB1cDogZm9yIGFuICJlbGl0ZSIgc2Nob29sLCBpcyAzMC41OCUgYSByZWFzb25hYmxlIGFjY2VwdGFuY2UgcmF0ZT8gVHVybmluZyBiYWNrIHRvIHRoZSBwdWJsaWNseSBhdmFpbGFibGUgQ2xhc3Mgb2YgMjAyMCBJdnkgTGVhZ3VlIGluZm9ybWF0aW9uLCBhY2NlcHRhbmNlIHJhdGVzIHJhbmdlZCBmcm9tIDMuNCUgKEhhcnZhcmQpIHRvIDEyLjUlIChDb3JuZWxsKS4gQm9zdG9uIENvbGxlZ2UsIG15IEFsbWEgbWF0ZXIsIGhhcyBhbiBhY2NlcHRhbmNlIHJhdGUgb2YgMjklIGFuZCBpcyByYW5rZWQgYXJvdW5kICMzMCBuYXRpb25hbGx5LiBTbyAzMCUgaXMgZWl0aGVyIHN5c3RlbWF0aWNhbGx5IGxvdyBmb3IgYSB0cnVseSAiZWxpdGUiIGNvbGxlZ2UgKGFuZCBpbXBsaWVzIHdlIGhhdmUgYSBza2V3ZWQgc2FtcGxlKSBvciBpcyByZXByZXNlbnRhdGl2ZSBvZiBhIHNjaG9vbCByYW5rZWQgYXJvdW5kICMzMC4gSG93IHdlIGRlY2lkZWQgdG8gaW50ZXJwcmV0IHdpbGwgaGF2ZSBpbXBsaWNhdGlvbnMgb24gd2hldGhlciB3ZSBjYW4gcGVyZm9ybSBhIExldmVsIEkgb3IgSUkgYW5hbHlzaXMuCgpMZXQncyB0dXJuIHRvIGRlbW9ncmFwaGljcy4gRmlyc3QsIGxvb2tpbmcgYXQgcmFjZSwgd2UgY2FuIHNlZSBpbiBGaWcgNS4yIHRoYXQgdGhlcmUgaXMgZGlzcGFyaXR5IGJldHdlZW4gQW5nbG8vQXNpYW4gYWRtaXQgcmF0ZXMgKDMyJSkgYW5kIEJsYWNrL090aGVyIGFkbWl0IHJhdGVzICgyNCUpLiBUaGlzIGluZm9ybWF0aW9uIGNoYWxsZW5nZXMsIGF0IGxlYXN0IGZvciB0aGlzIHBhcnRpY3VsYXIgc2FtcGxlLCBhbiBvZnRlbiByZXBlYXRlZCBxdWlwIHRoYXQgaXQncyBlYXNpZXIgZm9yIHVuZGVycmVwcmVzZW50ZWQgbWlub3JpdHkgc3R1ZGVudHMgdG8gZ2V0IGludG8gZWxpdGUgdW5pdmVyc2l0aWVzIHRoYW4gQW5nbG8gb3IgQXNpYW4gc3R1ZGVudHMuIFRoaXMgd2lsbCBiZSBpbnRlcmVzdGluZyB0byBleHBsb3JlIGZ1cnRoZXIgaW4gdGhlIG11bHRpLXZhcmlhdGUgYW5hbHlzaXMgb25jZSBpbnRlcmFjdGlvbiBlZmZlY3RzIGxpa2UgaW5jb21lLCBTQVQsIGFuZC9vciBHUEEgYXJlIGNvbnNpZGVyZWQuIEV4YW1pbmluZyBpbmNvbWUsIGxldCdzIHN0YXJ0IGJ5IHNwbGl0dGluZyB0aGUgZGF0YSBpbiB0byB0d28gYnVja2V0czogbG93ZXIvbWlkZGxlLWNsYXNzIChpbmNvbWUgbGVzcyB0aGFuICQ5OSw5OTkpIGFuZCBhZmZsdWVudCAoaW5jb21lIGdyZWF0ZXIgdGhhbiAkOTksOTk5KS4gVGhlIGFkbWlzc2lvbiByYXRlcyBhcmUgcHJldHR5IHNpbWlsYXI6IDMwLjElIGFuZCAzMi4xJSwgcmVzcGVjdGl2ZWx5LiBXaGlsZSBhZmZsdWVudCBhcHBsaWNhbnRzIGFyZSBzbGlnaHRseSBmYXZvcmVkIGluIHRoaXMgZGF0YSBzZXQsIGl0J3Mgbm90IG5lYXJseSBhcyBzdGFydGxpbmcgYXMgdGhlIHJhY2lhbCBkaXNwYXJpdGllcy4gRmluYWxseSwgbG9va2luZyBhdCBnZW5kZXIsIHdlIHNlZSBzaW1pbGFyIHJlc3VsdHMgdG8gaW5jb21lOiBhIHNtYWxsIGRpc3Bhcml0eSBiZXR3ZWVuIHRoZSBtYWxlIGFkbWlzc2lvbiByYXRlICgyOS44JSkgYW5kIHRoZSBmZW1hbGUgYWRtaXNzaW9uIHJhdGUgKDMxLjMlKS4gV2hldGhlciB0aGlzIGlzIHN0cmljdGx5IGEgTGV2ZWwgSSBzYW1wbGUgYXJ0aWZhY3Qgb3IgYSBMZXZlbCBJSSBnZW5lcmFsaXphdGlvbiBhYm91dCBjb2xsZWdlIGFkbWlzc2lvbnMgaXMgZGlmZmljdWx0IHRvIHNheSBiYXNlZCBvbiB0aGlzIGRhdGEgYWxvbmUgYW5kIHNvbWV3aGF0IGlycmVsZXZhbnQgZ2l2ZW4gdGhlIGRpc3Bhcml0eSBpcyBtaW5pbWFsLgoKYGBge3J9CmJhcnBsb3QoYWRtaXQucmF0ZXMucmFjZS5ob2xkKjEwMCwgY29sID0gImxpZ2h0Z3JlZW4iLAogICAgICAgICAgICB4bGFiID0gIkFkbWl0IFJhdGU6IFRvdGFsKDMwLjYlKSwgQW5nbG8oMzIuNSUpLCBBc2lhbigzMi4wJSksIEJsYWNrKDI0LjQlKSwgT3RoZXIoMjQuNCUpIiwKICAgICAgICAgICAgeWxhYiA9ICJQZXJjZW50IiwKICAgICAgICAgICAgbWFpbiA9ICJGaWcgNS4yOiBBZG1pdCBSYXRlIGJ5IFJhY2UiLAogICAgICAgICAgICB5bGltID0gYygwLCA0MCkpCmBgYAoKTWVyaXQgYmFzZWQgcHJlZGljdG9ycyBwcm92ZSB0byBiZSBtb3JlIGlsbHVzdHJhdGl2ZS4gV2UgY2FuIGxvb2sgYXQgaG93IEdQQSwgU0FUIE1hdGgsIFNBVCBWZXJiYWwsIGFuZCBTQVQgVG90YWwgaW1wYWN0IGFkbWlzc2lvbnMgZGVjaXNpb25zLiBHaXZlbiB3ZSBhcmUgd29ya2luZyBpbiB0aGUgd3JvbmcgbW9kZWwgcGVyc3BlY3RpdmUgYW5kIG5vdCBmb2N1c2VkIG9uIGNhdXNhbGl0eSwgbGV0J3MgZm9jdXMgb24gdmlzdWFsaXppbmcgdGhlIGRhdGEuIFdlIGNhbiBsb29rIGF0IGFkbWlzc2lvbnMgcmF0ZXMgZm9yIGVhY2ggb2YgdGhlIDQgcHJlZGljdG9ycyBpbiBxdWFydGlsZXMgaW4gRmlncyA1LjMtNi4gSXQncyBhYnVuZGFudGx5IGNsZWFyIHRoYXQgR1BBLCBTQVQgTSwgU0FUIFYsIGFuZCBTQVQgVCBhbGwgZ3JlYXRseSBlZmZlY3QgYWRtaXNzaW9ucyBkZWNpc2lvbiBpbiB0aGUgc2FtcGxlIHBvcHVsYXRpb24uIEFwcGxpY2FudHMgZnJvbSB0aGUgVG9wIHF1YXJ0aWxlIGluIGFueSBnaXZlbiBjYXRlZ29yeSBhcmUgNy0yNHggbW9yZSBsaWtlbHkgdG8gYmUgYWRtaXR0ZWQgZnJvbSB0aG9zZSBmcm9tIHRoZSBib3R0b20uIFRoZXJlIGlzIGNsZWFybHkgY28tbGluZWFyaXR5IGJldHdlZW4gdGhlc2UgcHJlZGljdG9ycywgYm90IGV4cGxpY2l0bHkgKFNBVCBNICsgViA9IFNBVCBUb3RhbCkgYW5kIGltcGxpY2l0bHkgKGdvb2QgU0FUIHRha2VycyBwcm9iYWJseSBvbiBhdmVyYWdlIGhhdmUgaGlnaGVyIEdQQXMpLiBXZSBjYW4gc2VlIHRoaXMgZW1waXJpY2FsbHkuIFRoZSBjb3JyZWxhdGlvbnMgYmV0d2VlbiBTQVQgTWF0aCAmIFNBVCBWZXJiYWwgKDAuODUpICoqYW5kKiogU0FUIFRvdGFsIGFuZCBHUEEgKDAuMzMpIGFyZSBwcmludGVkIGJlbG93LiAKYGBge3J9CiAgICBjb3IoYWRtaXNzaW9ucy5maW5hbCRzYXRpLm1hdGgsIGFkbWlzc2lvbnMuZmluYWwkc2F0aS52ZXJiKQogICAgY29yKGFkbWlzc2lvbnMuZmluYWwkc2F0aS50b3RhbCwgYWRtaXNzaW9ucy5maW5hbCRncGEud3RkKQpgYGAKCmBgYHtyfQogcGFyKG1mcm93PWMoMiwyKSkKICAgIHBsb3QoYyh0b3AuZ3BhLmFkbWl0LnJhdGUsCiAgICAgICAgICAgc2NuZC5ncGEuYWRtaXQucmF0ZSwKICAgICAgICAgICB0aHJkLmdwYS5hZG1pdC5yYXRlLAogICAgICAgICAgIGJvdC5ncGEuYWRtaXQucmF0ZSksCiAgICAgICAgIHhsYWIgPSAiUXVhcnRpbGVzICgxLTQpIiwKICAgICAgICAgeWxhYiA9ICJBZG1pdCBSYXRlICUiLAogICAgICAgICBtYWluID0gIkZpZyA1LjM6IEFkbWl0IGJ5IEdQQSIsCiAgICAgICAgIHBjaCA9IDE2LAogICAgICAgICBjZXggPSAxLjI1LAogICAgICAgICBjb2wgPSAibGlnaHRncmVlbiIpCiAgICBwbG90KGModG9wLnNhdG0uYWRtaXQucmF0ZSwKICAgICAgICAgICBzY25kLnNhdG0uYWRtaXQucmF0ZSwKICAgICAgICAgICB0aHJkLnNhdG0uYWRtaXQucmF0ZSwKICAgICAgICAgICBib3Quc2F0bS5hZG1pdC5yYXRlKSwKICAgICAgICAgeGxhYiA9ICJRdWFydGlsZXMgKDEtNCkiLAogICAgICAgICB5bGFiID0gIkFkbWl0IFJhdGUgJSIsCiAgICAgICAgIG1haW4gPSAiRmlnIDUuNDogQWRtaXQgYnkgU0FUIE1hdGgiLAogICAgICAgICBwY2ggPSAxNiwKICAgICAgICAgY2V4ID0gMS4yNSwKICAgICAgICAgY29sID0gImxpZ2h0Z3JlZW4iKQogICAgcGxvdChjKCB0b3Auc2F0di5hZG1pdC5yYXRlLAogICAgICAgICAgICBzY25kLnNhdHYuYWRtaXQucmF0ZSwKICAgICAgICAgICAgdGhyZC5zYXR2LmFkbWl0LnJhdGUsCiAgICAgICAgICAgIGJvdC5zYXR2LmFkbWl0LnJhdGUpLAogICAgICAgICB4bGFiID0gIlF1YXJ0aWxlcyAoMS00KSIsCiAgICAgICAgIHlsYWIgPSAiQWRtaXQgUmF0ZSAlIiwKICAgICAgICAgbWFpbiA9ICJGaWcgNS41OiBBZG1pdCBieSBTQVQgVmVyYmFsIiwKICAgICAgICAgcGNoID0gMTYsCiAgICAgICAgIGNleCA9IDEuMjUsCiAgICAgICAgIGNvbCA9ICJsaWdodGdyZWVuIikKICAgIHBsb3QoYyggdG9wLnNhdHQuYWRtaXQucmF0ZSwKICAgICAgICAgICAgc2NuZC5zYXR0LmFkbWl0LnJhdGUsCiAgICAgICAgICAgIHRocmQuc2F0dC5hZG1pdC5yYXRlLAogICAgICAgICAgICBib3Quc2F0dC5hZG1pdC5yYXRlKSwKICAgICAgICAgeGxhYiA9ICJRdWFydGlsZXMgKDEtNCkiLAogICAgICAgICB5bGFiID0gIkFkbWl0IFJhdGUgJSIsCiAgICAgICAgIG1haW4gPSAiRmlnIDUuNTogQWRtaXQgYnkgU0FUIFRvdGFsIiwKICAgICAgICAgcGNoID0gMTYsCiAgICAgICAgIGNleCA9IDEuMjUsCiAgICAgICAgIGNvbCA9ICJsaWdodGdyZWVuIikKYGBgCgojIyMgNi4gIEFuYWx5c2lzIG9mIE11bHRpdmFyaWF0ZSBTdGF0aXN0aWNzCkZpbmFsbHksIHdlIGdldCB0byB0aGUgUmFuZG9tIEZvcmVzdCAoUkYpIEFuYWx5c2lzOiBvdXIgcHJpbmNpcGxlIHRvcGljIG9mIGlucXVpcnkuIEJlZm9yZSB3ZSBzdGFydCwgaXQncyBpbXBvcnRhbnQgdG8gbGF5IG91dCBhIGZldyBzdGF0aXN0aWNhbCBjb25jZXB0cy4gRmlyc3QsIGFzIG5vdGVkIGluICpTZWN0aW9uIDI6IERhdGEgRGVzY3JpcHRpb24qLCBvdXIgY2xhc3NpZmljYXRpb24gdXNpbmcgUkYgd2lsbCBsYWNrIG1hbnkgcG90ZW50aWFsbHkgZXhwbGFuYXRvcnkgdmFyaWFibGVzLiBJbiBzaG9ydCwgd2UgaGF2ZSB0aGUgMTEgcHJlZGljdG9ycyB3ZSd2ZSBvYnNlcnZlZCwgYnV0IHRoZXJlIGlzIGEgbG90IG1vcmUgaW5mb3JtYXRpb24gcmVxdWlyZWQgdG8gc3BlY2lmeSB0aGUgdHJ1ZSBtZWFuIGZ1bmN0aW9uLiBHaXZlbiB0aGF0LCB3ZSB3aWxsIHN0aWxsIGJlIE9LIG1vdmluZyBhaGVhZCBhdCBMZXZlbCBJIGFuZCBwb3RlbnRpYWxseSBMZXZlbCBJSSBhbmFseXNpcywgYnV0IHdlIHdpbGwgbm90IGJlIGFibGUgdG8gZHJhdyBjb25jbHVzaW9ucyBhdCBMZXZlbCBJSUkuIFNlY29uZCwgaW4gKlNlY3Rpb24gNS4gIEFuYWx5c2lzIG9mIEJpdmFyaWF0ZSBTdGF0aXN0aWNzKiwgd2UgcG9pbnRlZCBvdXQgdGhhdCBpdCdzIHVud2lzZSB0byBpbXBseSBjYXVzYWxpdHkgd2l0aCB0aGUgbGltaXRlZCBkYXRhIHdlJ3JlIGFuYWx5emluZy4gTm93LCB3ZSB3aWxsIGNvbXBsZXRlbHkgZGVwYXJ0IGZyb20gdGhlIGlkZWEgb2YgcmVncmVzc2lvbiBjb2VmZmljaWVudHMgd2hlbiB1c2luZyBSRi4gVGhlIHRyYW5zZm9ybWF0aW9ucyBhbmQgaW50ZXJhY3Rpb24gZWZmZWN0cyBpbnZvbHZlZCBpbiBjcmVhdGluZyB0aGUgbW9kZWwgYXJlIHN1ZmZpY2llbnRseSBjb21wbGV4IHRoYXQgdGhlcmUgaXMgbm90IGV2ZW4gYSBjb2VmZmljaWVudCB0eXBlIGFuYWxvZ3kgdG8gY29uc2lkZXIuCgpUbyBldmFsdWF0ZSBvdXIgbW9kZWwsIHdlIHdpbGwgYWxsb3cgdGhlIGFsZ29yaXRobSB0byBzcGxpdCB0aGUgZGF0YSBpbnRvIHRyYWluaW5nIGRhdGEgYW5kIHRlc3QgZGF0YSBmb3IgdXMuIFRoZSBSRiBhbGdvcml0aG0gZG9lcyB0aGlzIGF1dG9tYXRpY2FsbHkgYnkgdHJhaW5pbmcgb24gYSBzdWJzZXQgb2Ygb2JzZXJ2YXRpb25zIGFuZCB0aGVuIHRlc3RpbmcgdGhlIHF1YWxpdHkgb2YgdGhlIGRlY2lzaW9uIHRyZWUgaXQgY3JlYXRlcyBvbiB0aGUgT3V0LW9mLUJhZyAoT09CKSB0ZXN0IGRhdGEuIFRoaXMgZnJhbWV3b3JrIGlzIGEgZ29vZCB3YXkgdG8gcHJldmVudCBvdmVyIGZpdHRpbmcsIGVzcGVjaWFsbHkgZ2l2ZW4gaXQgaXMgYXZlcmFnZWQgb3ZlciBhIGxhcmdlIG51bWJlciBvZiB0cmVlcy4gR2l2ZW4gd2UgaGF2ZSBhbG1vc3QgdHdvIG9yZGVycyBvZiBtYWduaXR1ZGUgbW9yZSBvYnNlcnZhdGlvbnMgdGhhbiBiYXNlIHByZWRpY3RvcnMsIHRoZSBtb2RlbCBzaG91bGQgYmUgc3RhYmxlLiAKCiMjIyMgNi4xOiBObyBNaXNjbGFzc2lmaWNhdGlvbiBDb3N0cwpGb3Igb3VyIGluaXRpYWwgbW9kZWwsIHdlIGdyb3cgYSByYW5kb20gZm9yZXN0IG9mIDc1MCB0cmVlcy4gQmFzZWQgb24gdGhlIHBsb3QgYmVsb3cgaW4gRmlnLiA2LjEsIHdlIGNhbiBzZWUgdGhlIG1vZGVsIGltcHJvdmVtZW50IHRhcGVycyBvZmYgc3Vic3RhbnRpYWxseSBieSA1MCwgYW5kIGlzIGFsbW9zdCBmbGF0IGJ5IDIwMC4gNzUwIHRyZWVzIHNob3VsZCBjYXB0dXJlIHBsZW50eSBzaWduYWwgd2l0aG91dCB3YXN0aW5nIGNvbXB1dGUgcmVzb3VyY2VzLiBUaGUgbWlzY2xhc3NpZmljYXRpb24gZXJyb3IgcmF0ZSBpcyAxNS45MSUsIHdoaWNoIGNhbiBzZWVuIGJlbG93IGluIHRoZSBtb2RlbCBwcmludCBvdXQsIHNob3dpbmcgT09CIGVycm9yIHJhdGUgYW5kIHRoZSBjb25mdXNpb24gdGFibGUuIDE1LjkxJSBzZWVtcyByZWFzb25hYmxlIGF0IGZpcnN0IHBhc3MgZ2l2ZW4gdGhlIGFtb3VudCBvZiBtaXNzaW5nIGRhdGEgaW4gb3VyIG1vZGVsLiBXZSdkIGV4cGVjdCB0aGUgZXJyb3IgcmF0ZSB0byBkcm9wIGlmIHdlIGNvdWxkIGluZm9ybSB0aGUgbW9kZWwgb2YgZXZlcnlvbmUncyBvdGhlciB0YWxlbnRzLCBzZXJ2aWNlLCByZWNvbW1lbmRhdGlvbiBsZXR0ZXJzLCBhbmQgbXlyaWFkIG90aGVyIGNyaXRlcmlhLiBXaGF0J3MgcG90ZW50aWFsbHkgbW9yZSBpbnRlcmVzdGluZyBpcyB0aGUgY29tcGFyaXNvbiBvZiBmYWxzZSBuZWdhdGl2ZXMgdG8gZmFsc2UgcG9zaXRpdmVzLiBUaGUgbW9kZWwgaGFkIHNlbnNpdGl2aXR5IG9mIDY2LjklIGFuZCBzcGVjaWZpY2l0eSBvZiA5MS42JS4gT3VyIG1vZGVsIHByZWZlcnJlZCB0byBwcmVkaWN0IHNvbWVvbmUgd291bGQgKipOT1QqKiBiZSBhY2NlcHRlZCB0aGFuIHdvdWxkIGJlLiBUaGUgaW1wbGllZCBjb3N0IHJhdGlvIHdhcyBhIGJpdCBsZXNzIHRoYXQgMToyLiBJdCBmb3JlY2FzdGVkIDI2LjIlIG9mIGFwcGxpY2FudHMgd291bGQgYmUgYWNjZXB0ZWQgKGNvbXBhcmVkIHRvIHRoZSBhY3R1YWwgMzAuNiUpLiBBcyBhIHJlc3VsdCwgNzk2IHN0dWRlbnRzIHdobyB3b3VsZCBoYXZlIGJlZW4gYWRtaXR0ZWQgd291bGQgaGF2ZSBiZWVuIGRpc2NvdXJhZ2VkIGZyb20gYXBwbHlpbmcuIEl0J3Mgd29ydGggdHVuaW5nIHRoZSBmYWxzZSBwb3NpdGl2ZSB0byBmYWxzZSBuZWdhdGl2ZSBjb3N0IHJhdGlvIGluIG91ciBtb2RlbCB0byBwcm9tb3RlIGEgbW9yZSB1c2VmdWwgcHJlZGljdGlvbjsgYWZ0ZXItYWxsLCB3ZSBkb24ndCB3YW50IHRvIGRpc2NvdXJhZ2UgZXZlcnkgaGlnaCBzY2hvb2wgc2VuaW9yIGR1cmluZyB3aGF0IGlzIGFscmVhZHkgYW4gaW5jcmVkaWJseSBleGhhdXN0aW5nIGFuZCB0cnlpbmcgdGltZS4KYGBge3J9CnBsb3QocmYxLCBtYWluID0gIkZpZyA2LjE6IFJGMSBQbG90IikKYGBgCmBgYHtyfQpwcmludChyZjEpCmBgYAoKIyMjIyA2LjI6IFNldHRpbmcgTWlzY2xhc3NpZmljYXRpb24gQ29zdHMKRm9yIG91ciBuZXh0IG1vZGVsLCBsZXQncyBidWlsZCBpbiBhIHRhcmdldCA0OjEgY29zdCByYXRpby4gVGhpcyBlc3NlbnRpYWxseSBzYXlzIHRoYXQgd2Ugd2FudCB0aGUgbW9kZWwgdG8gZmF2b3IgZmFsc2UgcG9zaXRpdmVzIGF0IDR4IHRoZSByYXRlIGl0IHZhbHVlcyBmYWxzZSBuZWdhdGl2ZXMuIFRoaXMgaXMgZ29pbmcgdG8gaW52YXJpYWJseSBkZWdyYWRlIHByZWNpc2lvbjogd2Ugd2lsbCBiZSB3cm9uZyBtb3JlIG9mdGVuIHdoZW4gcHJlZGljdGluZyAiWWVzIi4gSG93ZXZlciwgdGhlIG1vdGl2YXRpb24gc2VlbXMganVzdC4gQXMgY291bnNlbG9ycywgd2UgY2FuIGVpdGhlciBpbmZsYXRlIG91ciB2YW5pdHkgbWV0cmljcyBvZiBzYXlpbmcgb3VyIHN0dWRlbnRzIGdvdCBpbnRvIHRoZWlyICJUb3AiIHNjaG9vbCBtb3JlIG9mdGVuICoqT1IqKiB3ZSBjYW4gc2FjcmlmaWNlIG91ciBhY2N1cmFjeSBieSBwdXNoaW5nIHN0dWRlbnRzIHRvIGFwcGx5IHRvIGJldHRlciBzY2hvb2xzIHRoYXQgdGhleSByZWFsbHkgZG8gaGF2ZSBhIGZpZ2h0aW5nIGNoYW5jZSB0byBnZXQgaW50by4gSSdkIGNlcnRhaW5seSByYXRoZXIgc3RyaXZlIGZvciBleGNlbGxlbmNlIGFuZCBjb21lIHVwIHNob3J0IHNvbWV0aW1lcyB0aGFuIHNldHRsZSBmb3IgbWVkaW9jcml0eSBhbmQgc3BlbmRpbmcgYSBsaWZlIHdvbmRlcmluZyAid2hhdCBpZiIuIExvb2tpbmcgYXQgdGhlIHJlc3VsdHMsIHdlIGFjaGlldmVkIGEgMy44OjEgY29zdCByYXRpby4gQXMgYSBjb3N0LCB0aGUgT09CIGVycm9yIHJhdGUgcm9zZSB0byAyMC4yNyUuIFdoYXQncyB2YWx1YWJsZSBoZXJlIGZyb20gYSBndWlkYW5jZSBjb3Vuc2Vsb3IgcGVyc3BlY3RpdmUgaXMgdGhhdCB0aGUgbW9kZWwgaXMgc3RpbGwgYWNjdXJhdGVseSBwcmVkaWN0aW5nIGFjY2VwdGFuY2VzIGF0IDYyJS4gSSdkIGJlIHNob2NrZWQgaWYgbW9zdCBzdHVkZW50cyBkaWQgKipub3QqKiBhcHBseSB0byBhbiAiZWxpdGUiIHNjaG9vbCAoYXNzdW1pbmcgdGhleSBsaWtlZCB0aGUgb3RoZXIgZmFjdG9ycyBvZiB0aGUgc2Nob29sKSB3aXRoIGEgNjIlIGNoYW5jZSBvZiBiZWluZyBhZG1pdHRlZC4gT24gdGhlIGZsaXAgc2lkZSwgd2hlbiB3ZSB0ZWxsIGEgc3R1ZGVudCB0aGF0IHRoZSBtb2RlbCBpcyBub3Qgb3B0aW1pc3RpYyBhYm91dCB0aGVpciBjaGFuY2VzLCBpdCdzIHJpZ2h0IDkzJSBvZiB0aGUgdGltZS4gTm90IHRvbyBtYW55IHBlb3BsZSB3aWxsIGhhbmcgdGhlaXIgaGF0cyBvbiBhIDE6MTQgY2hhbmNlLiBGb3IgZnVuLCBsZXQncyBwcmVzdW1lIGEgc3R1ZGVudCB3aXRoIGF2ZXJhZ2Ugcmlzay1hcHBldGl0ZSB3aWxsIGFwcGx5IHRvIGEgc2Nob29sIGlmIHRoZXJlJ3MgMS8zIGNoYW5jZSBvZiBiZWluZyBhY2NlcHRlZC4gV2hhdCBkb2VzIHRoYXQgY29zdCByYXRpbyBsb29rIGxpa2U/IFRoZSBjb3N0IHJhdGlvIGlzIGFib3V0IDE1NToxLiBJbnRlcmVzdGluZ2x5LCB0aG91Z2gsIHRoZSBmYWxzZSBuZWdhdGl2ZSBlcnJvciByYXRlIG9ubHkgZ2V0cyBjdXQgYnkgYWJvdXQgNTAlLiBUaGlzIGlzIGEgcG9vciB0cmFkZSBvZmYgdG8gbWFrZS4gCmBgYHtyfQpwcmludChyZjIpCgpwcmludChyZjMpCmBgYAoKIyMjIyA2LjM6IEltcG9ydGFuY2UgUGxvdHMgYW5kIFByZWRpY3Rpb24gQWNjdXJhY3kKTG9va2luZyBhdCB0aGUgaW1wb3J0YW5jZSBwbG90cyBiZWxvdyBpbiBGaWcgNi4yIGFuZCBGaWcgNi4zLCByZW1pbmRzIHVzIG9mIHRoZSBwcmV2aW91cyBvYnNlcnZhdGlvbnMgaW4gdGhlIGJpdmFyaWF0ZSBhbmFseXNpcy4gSW4gZGVzY2VuZGluZyBvcmRlciwgdGhlIG1vc3QgaW1wb3J0YW50IHByZWRpY3RvcnMgYXJlOiBHUEEsIFNBVCBUb3RhbCwgU0FUIFZlcmJhbCwgYW5kIFNBVCBNYXRoLiBPbiBhbiB1bnN0YW5kYXJkaXplZCBiYXNpcywgd2hhdCB0aGlzIGNoYXJ0IGlzIHNheWluZyBpcyB0aGF0IHdoZW4gYSBnaXZlbiB2YXJpYWJsZSBpcyBzaHVmZmxlZCwgd2UgbG9zZSB0aGUgY29ycmVzcG9uZGluZyBhbW91bnQgb2YgcHJlZGljdGl2ZSBwb3dlciAoT09CIGNsYXNzaWZpY2F0aW9uIGVycm9yLCBmb3JtYWxseSkuIEZvciBleGFtcGxlLCBzaHVmZmxpbmcgR1BBLnd0ZCwgdGhlIG1vc3QgaW1wb3J0YW50IHByZWRpY3RvciBjYXVzZXMgYSBzdWJzZXF1ZW50IGRyb3AgaW4gb3ZlciAyNSUgb2YgcHJlZGljdGl2ZSBwb3dlci4gRnJvbSBhIExldmVsIEkgcGVyc3BlY3RpdmUsIGl0J3MgaW50ZXJlc3RpbmcgdGhhdCBHUEEgYW5kIFNBVCBUb3RhbCB3ZXJlIG1vc3QgcHJlZGljdGl2ZSBhbmQgYWxzbyBmb2xsb3dlZCB0aGUgbW9zdCBub3JtYWwgZGlzdHJpYnV0aW9ucy4gVGhlIGJyb2FkZXIgcmFuZ2Ugb2YgcG9zc2libGUgcmVzdWx0cyAoMC4wMCAtIDUuMDAgb3IgNDAwIC0gMTYwMCkgYW5kIHZhcmlhbmNlIGFtb25nIHJlc3VsdHMgYWxsb3dlZCB0aGUgcmFuZG9tIGZvcmVzdCBhbGdvcml0aG0gdG8gZmluZCBpbmNyZW1lbnRhbCBzaWduYWwgaW4gYSB3YXkgdGhhdCBpcyB2ZXJ5IGhhcmQgdG8gZG8gd2l0aCBhIGxpbWl0ZWQgc2V0IG9mIGluZGljYXRvciB2YXJpYWJsZXMuIApgYGB7cn0KIHBhcihtZnJvdz1jKDEsMikpCiAgICB2YXJJbXBQbG90KHJmMix0eXBlPTEsc2NhbGU9RixjbGFzcz0xLAogICAgICAgICAgICAgICBtYWluPSJJbXBvcnRhbmNlIFBsb3QgZm9yIEFkbWlzc2lvbgogICAgICAgICAgICAgICAoVW5zdGFuZGFyaXplZCkiLGNvbD0ibGlnaHRncmVlbiIsY2V4PTEscGNoPTE5KQogICAgdmFySW1wUGxvdChyZjIsdHlwZT0xLHNjYWxlPVQsY2xhc3M9MSwKICAgICAgICAgICAgICAgbWFpbj0iSW1wb3J0YW5jZSBQbG90IGZvciBBZG1pc3Npb24KICAgICAgICAgICAgICAgKFN0YW5kYXJkaXplZCkiLCBjb2w9ImxpZ2h0Z3JlZW4iLGNleD0xLHBjaD0xOSkKICBwYXIobWZyb3c9YygxLDIpKQogICAgdmFySW1wUGxvdChyZjIsdHlwZT0xLHNjYWxlPUYsY2xhc3M9MCwKICAgICAgICAgICAgICAgbWFpbj0iSW1wb3J0YW5jZSBQbG90IGZvciBSZWplY3Rpb24KICAgICAgICAgICAgICAgKFVuc3RhbmRhcmRpemVkKSIsIGNvbD0ibGlnaHRncmVlbiIsY2V4PTEscGNoPTE5KQogICAgdmFySW1wUGxvdChyZjIsdHlwZT0xLHNjYWxlPVQsY2xhc3M9MCwKICAgICAgICAgICAgICAgbWFpbj0iSW1wb3J0YW5jZSBQbG90IGZvciBSZWplY3Rpb24KICAgICAgICAgICAgICAgKFN0YW5kYXJkaXplZCkiLCBjb2w9ImxpZ2h0Z3JlZW4iLGNleD0xLHBjaD0xOSkKYGBgCgojIyMjIDYuNDogUGFydGlhbCBEZXBlbmRlbmNlIFBsb3RzClRoZSBwYXJ0aWFsIGRlcGVuZGVuY2UgcGxvdHMgYmVsb3cgaW4gRmlnIDYuNCBhbmQgRmlnIDYuNSBzaG93IHRoZSByZWxhdGlvbnNoaXBzIGJldHdlZW4gdGhlIEdQQS9TQVQgVG90YWwgYW5kIHRoZSBjZW50ZXJlZCBsb2cgb2RkcyBvZiBhZG1pc3Npb24uIEJvdGggR1BBIGFuZCBTQVQgU2NvcmUgYXJlIG51bWVyaWMgd2l0aCBub3JtYWwgZGlzdHJpYnV0aW9ucy4gVGhlIGNoYW5jZXMgb2YgYWRtaXNzaW9uIGluY3JlYXNlIGxpbmVhcmx5IHdpdGggR1BBIGZyb20gMy4zIHVudGlsIDQuNSwgYXQgd2hpY2ggcG9pbnQgdGhlIGRhdGEgYmVjb21lcyB0aGluIGFuZCB1bnN0YWJsZS4gRm9yIFNBVCBTY29yZXMgdGhlIHJlbGF0aW9uc2hpcCBpcyBzbGlnaHRseSBkaWZmZXJlbnQsIGdyb3dpbmcgbmVhciBleHBvbmVudGlhbGx5IGZyb20gYXJvdW5kIDExMDAgdW50aWwgMTMwMCwgYmVmb3JlIHNldHRsaW5nIGludG8gYSBsaW5lciByZWxhdGlvbnNoaXAgYW5kIHRoZW4gcGxhdGVhdWluZyBhdCBhcm91bmQgMTQwMC4gSGlnaCBzY2hvb2wgc3R1ZGVudHMgd2l0aCBHUEFzIGFyb3VuZCA0LjUgYW5kIFNBVCBTY29yZXMgYWJvdmUgMTQwMCBoYXZlIHRoZSBoaWdoZXN0IGNoYW5jZSBvZiBhZG1pc3Npb24uICBUaGUgc2xpZ2h0IGRlY2xpbmVzIGF0IHRoZSBleHRyZW1lIHJpZ2h0IHRhaWxzIGFyZSBwcm9iYWJseSBiZXN0IGV4cGxhaW5lZCBieSBzcGFyc2UgZGF0YS4gQW4gYXN0dXRlIGNvbGxlZ2UgYWRtaXNzaW9ucyBzcGVjaWFsaXN0IG1pZ2h0IGhhdmUgZG9tYWluIGtub3dsZWRnZSB0aGF0IGNvdWxkIGV4cGxhaW4gZGlmZmVyZW50bHk6IHBlcmhhcHMgc3R1ZGVudHMgd2l0aCA1LjAgR1BBcyBhbmQgMTYwMCBTQVRzIGFyZSBsYWNraW5nIGluIG90aGVyIGNoYXJhY3RlcmlzdGljcy4gVGhlcmUncyBub3QgZW5vdWdoIGV2aWRlbmNlIGhlcmUgdG8gc2F5LgpgYGB7cn0KcGFyKG1mcm93PWMoMiwxKSkKICAgIHBhcnQxPC1wYXJ0aWFsUGxvdChyZjIscHJlZC5kYXRhPWFkbWlzc2lvbnMuZmluYWwseC52YXI9Z3BhLnd0ZCwKICAgICAgICAgICAgICAgICAgICAgICBydWc9VCx3aGljaC5jbGFzcz0xLCBtYWluPSJGaWcgNi40OiBQYXJ0aWFsIERlcGVuZGVuY2UgUGxvdCBmb3IgR1BBIG9uIEFkbWlzc2lvbiIsIHhsYWI9IkdQQSIsCiAgICAgICAgICAgICAgICAgICAgICAgeWxhYj0iQ2VudGVyZWQgTG9nIE9kZHMiKQogICAgc2NhdHRlci5zbW9vdGgocGFydDEkeCxwYXJ0MSR5LHNwYW49MS8zLGNvbD0ibGlnaHRncmVlbiIscGNoPTE5LCBtYWluPSJGaWcgNi40OiBQYXJ0aWFsIERlcGVuZGVuY2UgUGxvdCBmb3IgR1BBIG9uIEFkbWlzc2lvbiIsIHhsYWI9IkdQQSIsCiAgICAgICAgICAgICAgICAgICB5bGFiPSJDZW50ZXJlZCBMb2cgT2RkcyIpCiAgICBwYXIobWZyb3c9YygyLDEpKQogICAgcGFydDI8LXBhcnRpYWxQbG90KHJmMixwcmVkLmRhdGE9YWRtaXNzaW9ucy5maW5hbCx4LnZhcj1zYXRpLnRvdGFsLAogICAgICAgICAgICAgICAgICAgICAgIHJ1Zz1ULHdoaWNoLmNsYXNzPTEsIG1haW49IkZpZyA2LjU6IFBhcnRpYWwgRGVwZW5kZW5jZSBQbG90IGZvciBTQVQgVG90YWwgb24gQWRtaXNzaW9uIiwgeGxhYj0iU0FUIFNjb3JlIiwKICAgICAgICAgICAgICAgICAgICAgICB5bGFiPSJDZW50ZXJlZCBMb2cgT2RkcyIpCiAgICBzY2F0dGVyLnNtb290aChwYXJ0MSR4LHBhcnQxJHksc3Bhbj0xLzMsY29sPSJsaWdodGdyZWVuIixwY2g9MTksIG1haW49IkZpZyA2LjU6IFBhcnRpYWwgRGVwZW5kZW5jZSBQbG90IGZvciBTQVQgVG90YWwgb24gQWRtaXNzaW9uIiwgeGxhYj0iU0FUIFNjb3JlIiwKICAgICAgICAgICAgICAgICAgIHlsYWI9IkNlbnRlcmVkIExvZyBPZGRzIikKYGBgCgojIyMgNy4gIFN1bW1hcnkgYW5kIENvbmNsdXNpb25zCk91ciBmaW5hbCBtb2RlbCB1c2VkIDc1MCB0cmVlcyBhbmQgYSBjb3N0IHJhdGlvIG9mIDMuODoxLiBUaGlzIGxlYWQgdG8gYSAyMC4yNyUgT09CIENsYXNzaWZpY2F0aW9uIGVycm9yIHJhdGUsIHdoaWNoIHdlIGFjY2VwdGVkIGR1ZSB0byBpdCdzIHdpbGxpbmduZXNzIHRvIHByZWRpY3QgbW9yZSBzdHVkZW50cyB3b3VsZCBiZSBhZG1pdHRlZCwgZXZlbiBpZiBhdCBsb3dlciBwcmVjaXNpb24uIFRoaXMgd2FzIGEgcG9saWN5IGRlY2lzaW9uIGRyaXZlbiBieSBhIGRlc2lyZSB0byBlbmNvdXJhZ2UgaGlnaCBxdWFsaXR5IHN0dWRlbnRzIHRvIGFwcGx5IHRvIGFuICJlbGl0ZSIgdW5pdmVyc2l0eS4gV2UgY2FuIG5vdyBzdW1tYXJpemUgb3VyIGZpbmRpbmdzIGFuZCBjb25jbHVzaW9ucy4gVGhlcmUgYXJlIHRocmVlIGNvcmUgaXNzdWVzIEknZCBsaWtlIHRvIGFkZHJlc3M6CgoxLiBGcm9tIGEgZGF0YSBjb2xsZWN0aW9uIHBlcnNwZWN0aXZlLCB0aGlzIGRhdGEgc2V0IGlzIGxlc3MgdGhhbiBpZGVhbCBmb3IgYSBmZXcgcmVhc29ucy4gRmlyc3QsIHdlIGFyZSB1bnN1cmUgd2hldGhlciBpdCBpcyBhIGNvbXBsZXRlIHNhbXBsZSBvZiBvbmUgeWVhcidzIGFkbWlzc2lvbiByZXN1bHRzIGZyb20gYW4gZWxpdGUgdW5pdmVyc2l0eSBvciBqdXN0IGEgc2FtcGxlIG9mIHJlc3VsdHMuIFRoZSBsaW1pdGVkIG51bWJlciBvZiBvYnNlcnZhdGlvbnMgY29tcGFyZWQgdG8gYSBub3JtYWwgZWxpdGUgIHVuaXZlcnNpdHkgYXBwbGljYXRpb24gY291bnQgaW1wbGllcyB0aGUgZGF0YSBpcyBhIHNhbXBsZS4gRnJvbSB0aGVyZSwgd2UgZG9uJ3Qga25vdyBpZiB3YXMgZ2VuZXJhdGVkIHJhbmRvbWx5IG9yIGFzIGEgYmlhc2VkIHNlbGVjdGlvbi4gRnVydGhlcm1vcmUsIGNvbGxlZ2UgYWRtaXNzaW9ucyBkYXRhIGlzIHRyaWNreSBpbiB0aGF0IGlzIG5vdCBmdWxseSBpbmRlcGVuZGVudC4gVGhlcmUgYXJlIGEgcmVsYXRpdmVseSBmaXhlZCBudW1iZXIgb2Ygc2VhdHMgYW5kIHNpbWlsYXIgc3R1ZGVudHMgY29tcGV0ZSBvdmVyIHRoZSBzYW1lIHNlYXQuIFRoaXMgZGVzdHJveXMgdGhlIGluZGVwZW5kZW5jZSBhc3N1bXB0aW9uIC0tIGFsbCBwcmV2aW91cyBkZWNpc2lvbnMgaW1wYWN0IHRoZSBzdWJzZXF1ZW50IG9uZXMgaW4gYSBnaXZlbiB5ZWFyIChhcyBjbGVhcmx5IHNlZW4gY29tcGFyaW5nIGVhcmx5LWRlY2lzaW9uIGFkbWl0dGFuY2UgcmF0ZXMgYW5kIHJlZ3VsYXItZGVjaXNpb24gYWRtaXR0YW5jZSByYXRlcykuIEZvciB0aGUgcmVtYWluZGVyIG9mIHRoaXMgZGlzY3Vzc2lvbiwgd2Ugd2lsbCBhc3N1bWUgdGhlIGRhdGEgd2VyZSBjb2xsZWN0ZWQgYXMgYSBiaWFzZWQgc2FtcGxlIGZyb20gYSBsYXJnZXIgc2V0IG9mIGFkbWlzc2lvbnMgb2JzZXJ2YXRpb25zLiBMdWNraWx5LCBnZW5kZXIsIHNleCwgYW5kIGluY29tZSB3ZXJlIHZlcnkgbWluaW1hbGx5LCBpZiBhdCBhbGwsIHByZWRpY3RpdmUgaW4gb3VyIHNhbXBsZSBzZXQuCjIuIEZyb20gYSBsZXZlbCBJIHBlcnNwZWN0aXZlLCB3ZSBjYW4gc2VlIHRoZSBkYXRhIGFyZSBjb21wbGV4IHdpdGggbWFueSBtaXNzaW5nIGZlYXR1cmVzLiBBZGRyZXNzaW5nIG9ubHkgdGhlc2UgNzg3MCBvYnNlcnZhdGlvbnMsIHdlIGNhbm5vdCBhcHByb2FjaCB0aGUgdHJ1ZSBtZWFuIHNwZWNpZmljYXRpb24uIFRoYXQgc2FpZCwgd2UgY2FuIGRpcmVjdGlvbmFsbHkgbWVhc3VyZSBkaXJlY3Rpb24gYW5kIGltcG9ydGFuY2Ugb2Ygc2VsZWN0IHByZWRpY3RvcnMuIEZvciBleGFtcGxlLCBHUEEgYW5kIFNUQVQgVG90YWwgYXBwZWFyZWQgdG8gYmUgYm90aCBjb3JyZWxhdGVkIHdpdGggZWFjaCBvdGhlciBhbmQgY29ycmVsYXRlZCB3aXRoIGhpZ2hlciByYXRlcyBvZiBhZG1pc3Npb24sIHdoaWNoIHdlIG9ic2VydmVkIGluIHRoZSBiaXZhcmlhdGUgYW5hbHlzaXMgYXMgd2VsbCBhcyBwYXJ0aWFsIGRlcGVuZGVuY2UgYW5kIGltcG9ydGFuY2UgcGxvdHMuIEF0IHRoZSBmYXIgcmlnaHQgb2YgYm90aCBHUEEgYW5kIFNBVCBTY29yZXMsIHRoZSBkaXN0cmlidXRpb25zIGJlY29tZSB0aGluIGFuZCB1bnN0YWJsZS4gTW9zdCBvZiB0aGUgYXBwYXJlbnQgcmVkdWN0aW9uIGluIGFkbWlzc2lvbiByYXRlcyB0aGVyZSBjYW4gYmUgYXR0cmlidXRlZCB0byBpbnN0YWJpbGl0eS4gVGhlIFJhbmRvbSBGb3Jlc3QgYWxnb3JpdGhtIHByb3ZlZCB0byBiZSBhIHBvd2VyZnVsIGNsYXNzaWZpY2F0aW9uIHRvb2wsIHBlcmZvcm1pbmcgd2l0aCBhbiBPT0IgY2xhc3NpZmljYXRpb24gZXJyb3IgcmF0ZSBvZiAxNS45MSUgd2hlbiB1bnJlc3RyaWN0ZWQgaW4gY29zdCByYXRpby4gRXZlbiBhZnRlciBhZGRpbmcgYSAzLjg6MSBjb3N0IHJhdGlvLCB0aGUgYWxnb3JpdGhtIHNsaWQgNC41JSBwZXJjZW50YWdlIHBvaW50cyBvbiBPT0IgY2xhc3NpZmljYXRpb24gZXJyb3IgKDIwLjI3JSk7IGEgc21hbGwgdHJhZGUtb2ZmIGZvciBhIG11Y2ggbW9yZSB1c2VmdWwgcHJlZGljdGl2ZSB0b29sLgozLiBGcm9tIGEgTGV2ZWwgSUkgcGVyc3BlY3RpdmUsIHdoaWNoIGRlc3BpdGUgdGhlIGRhdGEgY29sbGVjdGlvbiBpc3N1ZXMsIEkgYmVsaWV2ZSB3ZSBjYW4ganVzdGlmeSwgd2UgY2FuIGV4dHJhcG9sYXRlIHNvbWUgZmluZGluZ3MgdGhhdCBjb3VsZCBwcm92ZSB1c2VmdWwgbW92aW5nIGZvcndhcmQgaW4gcmVhbCB3b3JsZCBhcHBsaWNhdGlvbnMgZm9yIG91ciBoaWdoIHNjaG9vbCBzdHVkZW50cy4gSSBiZWxpZXZlIHdlIGNhbiBqdXN0aWZ5IExldmVsIElJIGFuYWx5c2lzIGZvciB0d28gcHJpbmNpcGxlIHJlYXNvbnMuIEZpcnN0LCBvdXIgbW9kZWwgZm91bmQgdGhlIG1vc3QgcHJlZGljdGl2ZSB2YXJpYWJsZXMgdG8gYmUgV2VpZ2h0ZWQgR1BBIGFuZCBUb3RhbCBTVEFUIFNjb3JlLCB3aGljaCBhcmUgY29udGludWFsbHkgcmVwb3J0ZWQgdG8gYmUgY3JpdGljYWwgZmFjdG9ycyBpbiBjb2xsZWdlIGFkbWlzc2lvbnMuIFJhdGhlciB0aGFuIHRoZXNlIGZpbmRpbmdzIGJlaW5nIGFuIGFydGlmYWN0IG9mIG91ciBsaW1pdGVkIHNhbXBsZSwgdGhleSBzZWVtIHRvIGJlIGluZGljYXRpdmUgb2YgYSBtdWNoIGJyb2FkZXIgdHJlbmQuIFNlY29uZCwgb3VyIGRhdGEgY292ZXJlZCBhIGJyb2FkIHNldCBvZiB2YWx1ZXMuIEJvdGggR1BBIGFuZCBTVEFUIFRvdGFsIGZvcm1lZCBuZWFybHkgcGVyZmVjdGx5IG5vcm1hbCBkaXN0cmlidXRpb25zLiBJbmNvbWUgc2hvd2VkIGEgcmVsYXRpdmUgdW5pZm9ybWl0eSwgYXNpZGUgZnJvbSB0aGUgc3Bpa2UgYXQgJDk5LDk5OSAoYW5kIHN1YnNlcXVlbnQgc3Bpa2UgYXQgJDY1LDAwMCBmcm9tIG1lZGlhbiBpbXB1dGF0aW9uKS4gQW5nbG8sIEFzaWFuLCBPdGhlciwgYW5kIFNleCB3ZXJlIHdlbGwgcmVwcmVzZW50ZWQuIFRoZSBvbmUgc3BhcnNpdHkgd2FzIGFyb3VuZCBCbGFjayBzdHVkZW50cy4gSXQgd291bGQgYmUgd2lzZSB0byBleGVyY2lzZSBjYXV0aW9uIGluIGV4dHJhcG9sYXRpbmcgdGhlIG1vZGVsIGZvciBCbGFjayBzdHVkZW50cyB3aXRob3V0IGZ1cnRoZXIgZGF0YS4gSWYgdGhlc2UgZGlzdHJpYnV0aW9ucyB3ZXJlIHNwYXJzZXIgb3IgaGlnaGx5IHNrZXdlZCBpbiBpbGxvZ2ljYWwgd2F5cywgSSdkIGJlIG11Y2ggbW9yZSBjYXV0aW91cyB0byBleHRlbmQgdG8gTGV2ZWwgSUkgYW5hbHlzaXMuIFRoaXMgcmFpc2VzIHRoZSBxdWVzdGlvbjogaG93IHNob3VsZCB3ZSBhY3R1YWxseSB1c2UgdGhlIG1vZGVsPyBUaGUgYW5zd2VyIGlzOiBpdCBkZXBlbmRzLiBBcyBteSByZWNvbW1lbmRhdGlvbiwgdGhlIG1vZGVsIHNob3VsZCBiZSB1c2VkIHNwYXJpbmdseSBvdmVyYWxsLiBUaGUgbW9kZWwgaXMgYmVzdCBmaXQgZm9yIHRoZSBmb2xsb3dpbmcgc2l0dWF0aW9uOiBhbiAxKSBBbmdsbyBvciBBc2lhbiBzdHVkZW50IHdobyBoYXMgMikgdGFrZW4gdGhlIFNBVCBhbmQgaXMgMykgYXBwbHlpbmcgdG8gYSBVbml2ZXJzaXR5IHdpdGggYSB+MzAlIGFjY2VwdGFuY2UgcmF0ZS4gSWYgdGhlIHN0dWRlbnQgaXMgQmxhY2ssIHRoZSBzY2hvb2wgaXMgc3Vic3RhbnRpYWxseSBlYXNpZXIgb3IgaGFyZGVyIHRvIGdldCBpbnRvLCB0aGUgc3R1ZGVudCBpcyBtaXNzaW5nIFNBVCBkYXRhLCBvciB0aGUgdGltZSBwZXJpb2QgaGFzIHNoaWZ0ZWQgc2lnbmlmaWNhbnRseSBmcm9tIHdoZW4gdGhlc2UgZGF0YSB3ZXJlIGNvbGxlY3RlZCwgdGhlIG1vZGVsIGNvdWxkIGJlIHdpbGRseSBsZXNzIHByZWRpY3RpdmUuIE15IHJlY29tbWVuZGF0aW9uIHdvdWxkIGJlIHRvIGlkZW50aWZ5IHN0dWRlbnRzIGF0IG15IGhpZ2ggc2Nob29sIHRoYXQgZml0IHRoaXMgcHJvZmlsZSwgdXNlIHRoZSBhbGdvcml0aG0gdG8gcHJlZGljdCB0aGVpciByZXN1bHRzIGFzIGEgcGlsb3QsIGFuZCB0aGVuIGV2YWx1YXRlIHRoZSB0cnVlIHByZWRpY3RpdmUgcmVzdWx0cyB2cy4gdGhlIE9PQiBkYXRhLiBBbGwgdGhlIHdoaWxlLCBJJ2QgYmUgcHVzaGluZyB0byBnYXRoZXIgbW9yZSByb2J1c3QgZGF0YS4gQXMgaXQgc3RhbmRzIHRvZGF5LCB0aGUgcHJlZGljdGl2ZSBwb3dlciBpcyBmb3IgYSBsaW1pdGVkIHNjb3BlIG9mIHN0dWRlbnQgYXBwbHlpbmcgdG8gYSBmYWlybHkgc3BlY2lmaWMgdHlwZSBvZiBzY2hvb2wuIAoKCiMjIyBBcHBlbmRpeDogUiBDb2RlCmBgYHtyfQpzZXR3ZCgiL1VzZXJzL2plZmZyZXllbGxpbmd0b24vRHJvcGJveC9XaGFydG9uL1NwcmluZzIwMTcvU1RBVDk3NC9kYXRhLyIpCiMjIyMjIyBmaXJzdCBnbGFuY2UgYXQgZGF0YQpzdW1tYXJ5KGFkbWlzc2lvbnMpCnN0cihhZG1pc3Npb25zKQoKIyMgUGxvdCBBZG1pc3Npb25zIGhpc3RvZ3JhbQpsaWJyYXJ5KGdncGxvdDIpCmdncCA8LSBnZ3Bsb3QoZGF0YS5mcmFtZShhZG1pc3Npb25zKSxhZXMoeD1hZG1pdCksIGFlcyh5PWNvdW50KSkKIyBjb3VudHMKZ2dwICsgZ2VvbV9iYXIoZmlsbD0ibGlnaHRncmVlbiIpICsgbGFicyh4ID0gIjA9UmVqZWN0ZWQsIDE9QWNjZXB0ZWQiLCB5ID0gIkNvdW50IiwgdGl0bGUgPSAiRmlnIDIuMSBBZG1pc3Npb25zIFJlc3VsdHMiLCAKICAgICAgICAgICAgICBzdWJ0aXRsZSA9ICJBY2NlcHRhbmNlIFJhdGUgPSAzMC44NyUiKQojIyBDYWxjIGFjY2VwdGFuY2UgcmF0ZQpzdW0oYWRtaXNzaW9ucyRhZG1pdCkvbGVuZ3RoKGFkbWlzc2lvbnMkYWRtaXQpKjEwMAoKCiMjIyMjIEVEQQojIyBGaW5kIFdlaXJkIFZhbHVlcyBmb3IgQ2F0ZWdvcmljYWwgVmFyaWFibGVzLCBpZiBhbnkKc3RyKGFkbWlzc2lvbnMpCnVuaXF1ZShhZG1pc3Npb25zJGFuZ2xvKQogICAgIyMgQW5nbG8sIE1pc2MgIjEwIi8iV2hpdGUiIC0tPiByZWNvZGUgYXMgIjEiIChhbmQgcmVtb3ZlIE5BJ3MgZm9yIG9icyAjMjAwKQogICAgYWRtaXNzaW9uc1tbMjAwLDJdXT0xCiAgICBhZG1pc3Npb25zW1syMDAsM11dPTAKICAgIGFkbWlzc2lvbnNbWzIwMCw0XV09MAogICAgYWRtaXNzaW9uc1tbMTAsMl1dPTEKdW5pcXVlKGFkbWlzc2lvbnMkYXNpYW4pCnVuaXF1ZShhZG1pc3Npb25zJGJsYWNrKQp1bmlxdWUoYWRtaXNzaW9ucyRzZXgpCgojIyBGYWN0b3IvTnVtZXJpYyB2YXJpYWJsZSB0cmFuc2Zvcm1hdGlvbnMKYWRtaXNzaW9ucyRhZG1pdDwtYXMuZmFjdG9yKGFkbWlzc2lvbnMkYWRtaXQpCmFkbWlzc2lvbnMkYW5nbG88LWFzLmZhY3RvcihhZG1pc3Npb25zJGFuZ2xvKQphZG1pc3Npb25zJGFzaWFuPC1hcy5mYWN0b3IoYWRtaXNzaW9ucyRhc2lhbikKYWRtaXNzaW9ucyRibGFjazwtYXMuZmFjdG9yKGFkbWlzc2lvbnMkYmxhY2spCmFkbWlzc2lvbnMkc2V4PC1hcy5mYWN0b3IoYWRtaXNzaW9ucyRzZXgpCmFkbWlzc2lvbnMkaW5jb21lPC1hcy5udW1lcmljKGFkbWlzc2lvbnMkaW5jb21lKQphZG1pc3Npb25zJHNhdGkubWF0aDwtYXMubnVtZXJpYyhhZG1pc3Npb25zJHNhdGkubWF0aCkKc3RyKGFkbWlzc2lvbnMpCnN1bW1hcnkoYWRtaXNzaW9ucykKCiMjIEdQQSBWYWx1ZXMKICAjIyBDaGVjayBpZiBHUEEgaXMgbm9ybWFsbHkgZGlzdHJpYnV0ZWQgKHZlcmlmeWluZyBubyBzeXN0ZW1hdGljIGRldmlhbmNlcyBkdWUgdG8gbWlzLWhhcHMgaW4gd2VpZ2h0aW5nKQogIGhpc3QoYWRtaXNzaW9ucyRncGEud3RkKSAjIyBsb29rcyBub3JtYWwgd2l0aG91dCBhbnkgc3lzdGVtYXRpYyBkZXZpYW5jZSAvLyBiaS1tb2RhbGl0aWVzCiAgIyMgSW52ZXJ0IG5lZ2F0aXZlIHZhbHVlIGZyb20gLTMuMjAgdG8gMy4yMAogIGFkbWlzc2lvbnNbWzIwMDAsNV1dPTMuMjAKICAjIyBNYWtlIDAuMDAgR1BBID0gTWVhbi9NZWRpYW4gR1BBCiAgc3VtbWFyeShhZG1pc3Npb25zJGdwYS53dGQpCiAgIyMgTWVkaWFuID0gMy44NSwgTWVhbiA9IDMuNzksIHNldHRsZWQgb24gMy44MCwgYXNzdW1pbmcgbWVhbiB3YXMgc2xpZ2h0bHkgcHVsbGVkIGRvd24gYnkgdGhlIDE5IEdQQT0wLjAwIHZhbHVlcwogIGFkbWlzc2lvbnNbWzM4Miw1XV09My44MAogIGFkbWlzc2lvbnNbWzU2MCw1XV09My44MAogIGFkbWlzc2lvbnNbWzkwMyw1XV09My44MAogIGFkbWlzc2lvbnNbWzE5MDAsNV1dPTMuODAKICBhZG1pc3Npb25zW1szMDgxLDVdXT0zLjgwCiAgYWRtaXNzaW9uc1tbMzE0Niw1XV09My44MAogIGFkbWlzc2lvbnNbWzM1NDEsNV1dPTMuODAKICBhZG1pc3Npb25zW1szODE2LDVdXT0zLjgwCiAgYWRtaXNzaW9uc1tbMzkyNSw1XV09My44MAogIGFkbWlzc2lvbnNbWzQwMzQsNV1dPTMuODAKICBhZG1pc3Npb25zW1s0NDg2LDVdXT0zLjgwCiAgYWRtaXNzaW9uc1tbNDU3Niw1XV09My44MAogIGFkbWlzc2lvbnNbWzUxNDAsNV1dPTMuODAKICBhZG1pc3Npb25zW1s1NDYyLDVdXT0zLjgwCiAgYWRtaXNzaW9uc1tbNjYwNCw1XV09My44MAogIGFkbWlzc2lvbnNbWzcxMzEsNV1dPTMuODAKICBhZG1pc3Npb25zW1s3NzU5LDVdXT0zLjgwCiAgYWRtaXNzaW9uc1tbNzkzOCw1XV09My44MAogIGFkbWlzc2lvbnNbWzgwNjEsNV1dPTMuODAKICAgICAgI0Rvbid0IGxhdWdoLi4uIGNvdWxkbid0IGdldCBmdW5jdGlvbiB0byB3b3JrIDotKAogIAojIyBJbmNvbWUgTWF4IEVuY29kZWQgYXQgOTksOTk5CiAgc3VtKGFkbWlzc2lvbnMkaW5jb21lID49IDk5OTk5LCBuYS5ybSA9IFRSVUUpCiAgMjE2NC84NzAwICoxMDAKICAjIyAyMTY0IGF0IG1heCBpbmNvbWUgKDI0Ljg3JSkKICAjIyBTZWVtcyBPSyB0byBsZWF2ZSBhcyAuLi4gMjUlIG1ha2luZyBtb3JlIHRoYW4gJDEwMGsgaXMgcmVhc29uYWJsZSBmb3IgdGhpcyBwb29sCiAgCiMjIFNBVCBNYXggU2NvcmUgRXJyb3IKICAjIyBNYXggU0FUIFZlcmJhbCA9IDUyNDA7IHJlZ3Jlc3NpbmcgdG8gdGhlIG1lZGlhbiBvZiBhZG1pdHRlZCBzdHVkZW50cyAoZ2l2ZW4gdGhleSB3ZXJlIGFkbWl0dGVkKQogIGFkbWl0dGVkIDwtIHN1YnNldChhZG1pc3Npb25zLCBhZG1pc3Npb25zJGFkbWl0ID09IDEpCiAgc3VtbWFyeShhZG1pdHRlZCRzYXRpLnZlcmIpICMjIG1lZGlhbiA9IDY1MAogIGFkbWlzc2lvbnNbWzIwLDZdXT02NTAgCgoKIyMgRmluZCBOQSdzIGFuZCByZW1vdmUgaWYgc2Vuc2libGUKICBzdW1tYXJ5KGFkbWlzc2lvbnMpCiAgIyMgU2V4IE5BcyAoMjQgdG90YWwsIDIyIGhhdmUgdG9ucyBvZiBvdGhlciBtaXNzaW5nIHZhbHVlcywgc3RyaWtpbmcgZnJvbSBkYXRhIHNldCkKICB3aGljaChpcy5uYShhZG1pc3Npb25zJHNleCkpCiAgYWRtaXNzaW9ucy52MSA8LSBhZG1pc3Npb25zWy1jKDM1MywgMzc5LCA0NDMsIDc2MiwgNzk5LCAxMzIxLCAxNzgwLCA0NDUxLCA0NDg2LCA0NjgxLCA1MTk3LCA1MjI5LCA1NTU4LCA1NzkyLCA2MDI3LCA2Mjg0LCA2MzIzLCA2NDI2LCA2NDY1LCA2NzYyLCA2OTIxLCA3NTA1LCA3OTM0LCA4Mjc0KSxdCiAgCiAgIyMgTkEgZm9yIEluY29tZSAoMTcyNCwgdG9vIGJpZyB0byBzdHJpa2UpCiAgc3VtKGlzLm5hKGFkbWlzc2lvbnMkaW5jb21lKSkKICAgICMjIGNoZWNrIGlmIHN5c3RlbWF0aWMgZGV2aWFuY2UgZnJvbSBub3JtYWwgYWNjZXB0YW5jZSByYXRlICg1NzkgQWRtaXR0ZWQgb3V0IG9mIDE3MjQgVG90YWwpCiAgICA1NzkvMTcyNCAqMTAwICMjIDMzLjU4JSB2cy4gMzAuODclIC4uLiBzZWVtcyBvbWl0dGVkIGluY29tZSBpbmZvIGlzIHNsaWdodGx5IHByZWRpY3RpdmUsIGJ1dCBub3QgbXVjaC4gUmVnZXNzaW5nIHRvIG1lZGlhbgogICAgYWRtaXNzaW9ucy50ZXN0IDwtIGFkbWlzc2lvbnMudjEgCiAgICBzYW1wPC1uYS5yb3VnaGZpeChhZG1pc3Npb25zLnRlc3QkaW5jb21lLCBtZWRpYW4pICMjIGltcHV0ZSBOQXMgdG8gbWVkaWFuIHZhbHVlCiAgICBhZG1pc3Npb25zLnRlc3QkaW5jb21lIDwtIHNhbXAgIyMgcmVwbGFjZSBOQXMgd2l0aCBtZWRpYW4gdmFsdWUKICAgIHN1bShhZG1pc3Npb25zLnRlc3QkaW5jb21lPT02NTAwMCkgLSBzdW0oYWRtaXNzaW9ucy52MSRpbmNvbWU9PTY1MDAwLCBuYS5ybSA9IFRSVUUpICMxNzExLCBjb3JyZWN0IHZhbHVlCiAgICBhZG1pc3Npb25zLnYyIDwtIGFkbWlzc2lvbnMudGVzdCAjIyBjcmVhdGUgbmV3IGRhdGFmcmFtZSB3aXRoIGltcHV0ZWQgdmFsdWVzCiAgICBzdW1tYXJ5KGFkbWlzc2lvbnMudjIpCiAgICAKICAjIyBOQSBmb3IgUmFjZSAoODI4KQogIHN1bShpcy5uYShhZG1pc3Npb25zJGFzaWFuKSkKICBzdW0oaXMubmEoYWRtaXNzaW9ucyRibGFjaykpCiAgc3VtKGlzLm5hKGFkbWlzc2lvbnMkYW5nbG8pKQogICAgIyMgY2hlY2sgaWYgc3lzdGVtYXRpYyBkZXZpYW5jZSBmcm9tIG5vcm1hbCBhY2NlcHRhbmNlIHJhdGUKICAgIHN1bW1hcnkoYWRtaXNzaW9ucykgIzgyOAogICAgc3VtbWFyeShhZG1pdHRlZCkgIzI3OQogICAgMjc5LzgyOCAqIDEwMCAjIyAzMy42OSUgdnMuIDMwLjg3JSAuLi4gc2VlbXMgb21pdHRlZCByYWNlIGlzIHNsaWdodGx5IHByZWRpY3RpdmUsIGJ1dCBub3QgbXVjaAogICAgIyMgY2FuJ3QgcmVncmVzcyB0byBtZWFuLCB0aGUgb3RoZXJzIGFyZSBsaWtlbHkgdG9vIHByZWRpY3RpdmUuLi4KICAgICMjIHN0cmlraW5nIGZyb20gZGF0YXNldC4uLiAxMCUgaHVydHMsIGJ1dCBpdCdzIHRoZSBiZXN0IHNvbHV0aW9uIEkgc2VlCiAgICBhZG1pc3Npb25zLnYzIDwtIHN1YnNldChhZG1pc3Npb25zLnYyLCAoIWlzLm5hKGFkbWlzc2lvbnMudjJbLDJdKSkgJiAoIWlzLm5hKGFkbWlzc2lvbnMudjJbLDNdKSkgJiAoIWlzLm5hKGFkbWlzc2lvbnMudjJbLDRdKSkpCiAgICBzdW1tYXJ5KGFkbWlzc2lvbnMudjMpCiAgICAKCiMjIElzIHRoZXJlIGFub3RoZXIgcmFjZSBjYXRlZ29yeT8KICAjIyBSYWNlIENvdW50cwogIHN1bShpcy5uYShhZG1pc3Npb25zLnYzKSkgIyMgTm8gTkEncyBpbiBkYXRhc2V0LCA3ODcwIG9ic2VydmF0aW9ucwogICMjIFdoaXRlIC8gQXNpYW4gLyBCbGFjawogIHN1bShhZG1pc3Npb25zLnYzJGFuZ2xvPT0xKSArIHN1bShhZG1pc3Npb25zLnYzJGFzaWFuPT0xKSArIHN1bShhZG1pc3Npb25zLnYzJGJsYWNrPT0xKSAjIyA2NTgyCiAgNzg3MCAtIDY1ODIgIzEyODgKICAjIyBBbm90aGVyIGxldmVsPyBvciBNdWx0aXBsZT8KICBzdW0oYWRtaXNzaW9ucy52MyRhbmdsbz09MCAmIGFkbWlzc2lvbnMudjMkYXNpYW49PTAgJiBhZG1pc3Npb25zLnYzJGJsYWNrPT0wKSAjMTI4OCwgY2hlY2tzIG91dAogICAgIyMgQ3JlYXRlIDR0aCBsZXZlbCBmb3IgcmFjZSBbbm8gaWRlYSBob3cgdG8gZG8gdGhpcyB3aXRoIGZ1bmN0aW9ucywgc28gd2h5IG5vdCB1c2Ugc29tZSBjdXRlIG1hdGggOi0pLi4uXQogICAgYWRtaXNzaW9ucy52MyRvdGhlciA8LSBhcy5udW1lcmljKGFkbWlzc2lvbnMudjMkYW5nbG8pICsgYXMubnVtZXJpYyhhZG1pc3Npb25zLnYzJGFzaWFuKSArIGFzLm51bWVyaWMoYWRtaXNzaW9ucy52MyRibGFjaykKICAgIGFkbWlzc2lvbnMudjMkb3RoZXIgPC0gYWRtaXNzaW9ucy52MyRvdGhlciAtIDQKICAgIGFkbWlzc2lvbnMudjMkb3RoZXIgPC0gYWRtaXNzaW9ucy52MyRvdGhlciAqIC0xCiAgICBhZG1pc3Npb25zLnYzJG90aGVyIDwtIGFzLmZhY3RvcihhZG1pc3Npb25zLnYzJG90aGVyKQogIAoKIyMgTW9yZSBTQVQgSXNzdWVzLCAwIHZhbHVlcwogICMjIFNBVCBNYXRoID0gMAogIHN1bShhZG1pc3Npb25zLnYzJHNhdGkubWF0aCA9PSAwKQogICMjIDQwOAogIAogICMjIFNBVCBWZXJiID0gMAogIHN1bShhZG1pc3Npb25zLnYzJHNhdGkudmVyYiA9PSAwKQogICMjIDQwOAogIAogICMjIE5vdCBhIGNvaW5jaWRlbmNlIC0tPiBMaWtlbHkgdG9vayB0aGUgQUNUCiAgICAjIyBjaGVjayBpZiBzeXN0ZW1hdGljIGRldmlhbmNlIGZyb20gbm9ybWFsIGFjY2VwdGFuY2UgcmF0ZQogICAgYWRtaXR0ZWQudjMgPC0gc3Vic2V0KGFkbWlzc2lvbnMudjMsIGFkbWlzc2lvbnMudjMkYWRtaXQgPT0gMSkKICAgIHN1bW1hcnkoYWRtaXR0ZWQudjMpCiAgICAyNDA3Lzc4NzAgKiAxMDAgIyMgc3Vic2V0IGJhc2UgYWRtaXNzaW9ucyByYXRlID0gMzAuNTglIChuZWdsaWJsYnkgZGlmZmVyZW50IGZyb20gb3JpZ2luYWwgMzAuODclKQogICAgc3VtKGFkbWl0dGVkLnYzJHNhdGkubWF0aCA9PSAwKS9zdW0oYWRtaXNzaW9ucy52MyRzYXRpLm1hdGggPT0gMCkgKiAxMDAgIyMgMTAuMDUlIC0tPiBWRVJZIERJRkZFUkVOVCwgbmVlZCB0byBjb25zaWRlcgogICAgc3VtKGFkbWl0dGVkLnYzJHNhdGkudmVyYiA9PSAwKS9zdW0oYWRtaXNzaW9ucy52MyRzYXRpLnZlcmIgPT0gMCkgKiAxMDAgIyMgc2FuaXR5IGNoZWNrLCB2ZXJiYWwgPT0gbWF0aAogICAgc3ViLnRlc3QgPC0gc3Vic2V0KGFkbWlzc2lvbnMudjMsIGFkbWlzc2lvbnMudjMkYWRtaXQgPT0gMSAmIGFkbWlzc2lvbnMudjMkc2F0aS52ZXJiID09IDApCiAgICBzdW1tYXJ5KHN1Yi50ZXN0KQogICAgc3ViLnRlc3QyIDwtIHN1YnNldChhZG1pc3Npb25zLnYzLCBhZG1pc3Npb25zLnYzJHNhdGkudmVyYiA9PSAwKSAKICAgIHNvcnQoc3ViLnRlc3QyJGdwYS53dGQpCgojIyBGaW5hbCBEYXRhc2V0CiAgICBhZG1pc3Npb25zLmZpbmFsIDwtIGFkbWlzc2lvbnMudjMKCiMjIyMjIFVuaXZhcmlhdGUgQW5hbHlzaXMKICAjIyBHUEEKICAjIyBDaGVjayBpZiBHUEEgaXMgbm9ybWFsbHkgZGlzdHJpYnV0ZWQgKHZlcmlmeWluZyBubyBzeXN0ZW1hdGljIGRldmlhbmNlcyBkdWUgdG8gbWlzLWhhcHMgaW4gd2VpZ2h0aW5nKQogIGdncC52MyA8LSBnZ3Bsb3QoYWRtaXNzaW9ucy5maW5hbCwgYWVzKGdwYS53dGQpKSAKICBnZ3AudjMgKyBnZW9tX2hpc3RvZ3JhbShmaWxsPSJsaWdodGdyZWVuIikgKyBsYWJzKHggPSAiR1BBIChXZWlnaHRlZCkiLCB5ID0gIkNvdW50IiwgdGl0bGUgPSAiRmlnIDQuMTogR1BBIEhpc3RvZ3JhbSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgIHN1YnRpdGxlID0gIk1lZGlhbiBHUEEgPSAzLjg1IikjIyBsb29rcyBub3JtYWwgd2l0aG91dCBhbnkgc3lzdGVtYXRpYyBkZXZpYW5jZSAvLyBiaS1tb2RhbGl0aWVzCiAgIyMgU0FUIFNjb3JlcwogICMjIE1hdGgKICBzdW1tYXJ5KGFkbWlzc2lvbnMuZmluYWwkc2F0aS5tYXRoKQogIGdncC52NCA8LSBnZ3Bsb3QoYWRtaXNzaW9ucy5maW5hbCwgYWVzKHNhdGkubWF0aCkpIAogIGdncC52NCArIGdlb21faGlzdG9ncmFtKGZpbGw9ImxpZ2h0Z3JlZW4iKSArIGxhYnMoeCA9ICJTQVQgTWF0aCIsIHkgPSAiQ291bnQiLCB0aXRsZSA9ICJGaWcgNC4yOiBTQVQgTWF0aCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgIHN1YnRpdGxlID0gIk1lZGlhbiBNYXRoIFNBVCA9IDYzMCIpCiAgIyMgVmVyYgogIHN1bW1hcnkoYWRtaXNzaW9ucy5maW5hbCRzYXRpLnZlcmIpCiAgZ2dwLnY1IDwtIGdncGxvdChhZG1pc3Npb25zLmZpbmFsLCBhZXMoc2F0aS52ZXJiKSkgCiAgZ2dwLnY1ICsgZ2VvbV9oaXN0b2dyYW0oZmlsbD0ibGlnaHRncmVlbiIpICsgbGFicyh4ID0gIlNBVCBWZXJiIiwgeSA9ICJDb3VudCIsIHRpdGxlID0gIkZpZyA0LjM6IFNBVCBWZXJiIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdWJ0aXRsZSA9ICJNZWRpYW4gVmVyYiBTQVQgPSA1ODAiKQogICMjIENvbWJpbmVkCiAgIyMgQ3JlYXRlIG5ldyBjb21iaW5lZC90b3RhbCBTQVQgdmFyaWFibGUKICBhZG1pc3Npb25zLmZpbmFsJHNhdGkudG90YWwgPC0gYWRtaXNzaW9ucy5maW5hbCRzYXRpLm1hdGggKyBhZG1pc3Npb25zLmZpbmFsJHNhdGkudmVyYgogIHN1bW1hcnkoYWRtaXNzaW9ucy5maW5hbCRzYXRpLnRvdGFsKQogIGdncC52NiA8LSBnZ3Bsb3QoYWRtaXNzaW9ucy5maW5hbCwgYWVzKHNhdGkudG90YWwpKSAKICBnZ3AudjYgKyBnZW9tX2hpc3RvZ3JhbShmaWxsPSJsaWdodGdyZWVuIikgKyBsYWJzKHggPSAiU0FUIFRvdGFsIiwgeSA9ICJDb3VudCIsIHRpdGxlID0gIkZpZyA0LjQ6IFNBVCBUb3RhbCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VidGl0bGUgPSAiTWVkaWFuIFRvdGFsIFNBVCA9IDEyMTAiKQogICMjIGNvbWJpbmUgCiAgbGlicmFyeShncmlkRXh0cmEpCiAgZ2dwLnYzIDwtIGdncGxvdChhZG1pc3Npb25zLmZpbmFsLCBhZXMoZ3BhLnd0ZCkpICsgZ2VvbV9oaXN0b2dyYW0oZmlsbD0ibGlnaHRncmVlbiIpICsgbGFicyh4ID0gIkdQQSAoV2VpZ2h0ZWQpIiwgeSA9ICJDb3VudCIsIHRpdGxlID0gIkZpZyA0LjE6IEdQQSBIaXN0b2dyYW0iLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1YnRpdGxlID0gIk1lZGlhbiBHUEEgPSAzLjg1IikKICBnZ3AudjQgPC0gZ2dwbG90KGFkbWlzc2lvbnMuZmluYWwsIGFlcyhzYXRpLm1hdGgpKSArIGdlb21faGlzdG9ncmFtKGZpbGw9ImxpZ2h0Z3JlZW4iKSArIGxhYnMoeCA9ICJTQVQgTWF0aCIsIHkgPSAiQ291bnQiLCB0aXRsZSA9ICJGaWcgNC4yOiBTQVQgTWF0aCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VidGl0bGUgPSAiTWVkaWFuIE1hdGggU0FUID0gNjMwIikKICBnZ3AudjUgPC0gZ2dwbG90KGFkbWlzc2lvbnMuZmluYWwsIGFlcyhzYXRpLnZlcmIpKSArIGdlb21faGlzdG9ncmFtKGZpbGw9ImxpZ2h0Z3JlZW4iKSArIGxhYnMoeCA9ICJTQVQgVmVyYiIsIHkgPSAiQ291bnQiLCB0aXRsZSA9ICJGaWcgNC4zOiBTQVQgVmVyYiIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VidGl0bGUgPSAiTWVkaWFuIFZlcmIgU0FUID0gNTgwIikKICBnZ3AudjYgPC0gZ2dwbG90KGFkbWlzc2lvbnMuZmluYWwsIGFlcyhzYXRpLnRvdGFsKSkgKyBnZW9tX2hpc3RvZ3JhbShmaWxsPSJsaWdodGdyZWVuIikgKyBsYWJzKHggPSAiU0FUIFRvdGFsIiwgeSA9ICJDb3VudCIsIHRpdGxlID0gIkZpZyA0LjQ6IFNBVCBUb3RhbCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VidGl0bGUgPSAiTWVkaWFuIFRvdGFsIFNBVCA9IDEyMTAiKQogIGdyaWQuYXJyYW5nZShnZ3AudjMsIGdncC52NCwgZ2dwLnY1LCBnZ3AudjYsIG5yb3c9MiwgbmNvbD0yKQogIAogICMjIERlbW9ncmFwaGljczogCiAgIyMgQXBwbGljYW50cyBieSBSYWNlCiAgYW5nbG8uYXBwcyA8LSBzdW0oYWRtaXNzaW9ucy5maW5hbCRhbmdsbz09MSkKICBhc2lhbi5hcHBzIDwtIHN1bShhZG1pc3Npb25zLmZpbmFsJGFzaWFuPT0xKQogIGJsYWNrLmFwcHMgPC0gc3VtKGFkbWlzc2lvbnMuZmluYWwkYmxhY2s9PTEpCiAgb3RoZXIuYXBwcyA8LSBzdW0oYWRtaXNzaW9ucy5maW5hbCRvdGhlcj09MSkKICBhZG1pdC5hcHBzLmNvbXAgPC0gYyhhbmdsby5hcHBzLCBhc2lhbi5hcHBzLCBibGFjay5hcHBzLCBvdGhlci5hcHBzKQogIGFkbWl0LmFwcHMuY29tcAogICAgIyMgUGxvdAogICAgYmFycGxvdChhZG1pdC5hcHBzLmNvbXAsIGNvbCA9ICJsaWdodGdyZWVuIiwKICAgICAgICAgIHhsYWIgPSAiQXBwbGljYW50czogQW5nbG8sIEFzaWFuLCBCbGFjaywgT3RoZXIiLAogICAgICAgICAgeWxhYiA9ICJDb3VudCIsCiAgICAgICAgICBtYWluID0gIkZpZyA0LjQ6IEFwcGxpY2FudCBDb3VudCBieSBSYWNlIiwKICAgICAgICAgIHlsaW0gPSBjKDAsIDQwMDApKQogICAgCiAgIyMgSW5jb21lIEhpc3RvZ3JhbQogICAgaGlzdChhZG1pc3Npb25zLmZpbmFsJGluY29tZSwgY29sID0gImxpZ2h0Z3JlZW4iLAogICAgICAgICB4bGFiID0gIkZhbWlseSBJbmNvbWUgKCQpIiwKICAgICAgICAgeWxhYiA9ICJDb3VudCIsCiAgICAgICAgIG1haW4gPSAiRmlnIDQuNTogQXBwbGljYW50IENvdW50IGJ5IEluY29tZSIsCiAgICAgICAgIHlsaW0gPSBjKDAsIDMwMDApKQogICMjIE9yaWdpbmFsIEluY29tZSBIaXN0b2dyYW0KICAgIGhpc3QoYWRtaXNzaW9ucyRpbmNvbWUsIGNvbCA9ICJsaWdodGdyZWVuIiwKICAgICAgICAgeGxhYiA9ICJGYW1pbHkgSW5jb21lICgkKSIsCiAgICAgICAgIHlsYWIgPSAiQ291bnQiLAogICAgICAgICBtYWluID0gIkZpZyA0LjU6IEFwcGxpY2FudCBDb3VudCBieSBJbmNvbWUiLAogICAgICAgICB5bGltID0gYygwLCAzMDAwKSkKICAgIAogICMjIEdlbmRlciBIaXN0b2dyYW0KICAgICMjIEFwcGxpY2FudHMgYnkgU2V4CiAgICBtYWxlLmFwcHMgPC0gc3VtKGFkbWlzc2lvbnMuZmluYWwkc2V4PT0xKQogICAgZmVtYWxlLmFwcHMgPC0gc3VtKGFkbWlzc2lvbnMuZmluYWwkc2V4PT0wKQogICAgYWRtaXQuYXBwcy5jb21wLmdlbiA8LSBjKG1hbGUuYXBwcywgZmVtYWxlLmFwcHMpCiAgICBhZG1pdC5hcHBzLmNvbXAuZ2VuCiAgICBiYXJwbG90KGFkbWl0LmFwcHMuY29tcC5nZW4sIGNvbCA9ICJsaWdodGdyZWVuIiwKICAgICAgICAgeGxhYiA9ICJHZW5kZXI6IE1hbGUsIEZlbWFsZSIsCiAgICAgICAgIHlsYWIgPSAiQ291bnQiLAogICAgICAgICBtYWluID0gIkZpZyA0LjY6IEFwcGxpY2FudCBDb3VudCBieSBHZW5kZXIiLAogICAgICAgICB5bGltID0gYygwLCA1MDAwKSkKICAKICAjIyBDb21iaW5lIFBsb3RzCiAgICBwYXIobWZyb3c9YygyLDIpKQogICAgICBiYXJwbG90KGFkbWl0LmFwcHMuY29tcC5nZW4sIGNvbCA9ICJsaWdodGdyZWVuIiwKICAgICAgICAgICAgeGxhYiA9ICJHZW5kZXI6IE1hbGUsIEZlbWFsZSIsCiAgICAgICAgICAgIHlsYWIgPSAiQ291bnQiLAogICAgICAgICAgICBtYWluID0gIkZpZyA0LjU6IEFwcGxpY2FudCBDb3VudCBieSBHZW5kZXIiLAogICAgICAgICAgICB5bGltID0gYygwLCA1MDAwKSkKICAgICAgYmFycGxvdChhZG1pdC5hcHBzLmNvbXAsIGNvbCA9ICJsaWdodGdyZWVuIiwKICAgICAgICAgICAgICB4bGFiID0gIkFwcGxpY2FudHM6IEFuZ2xvLCBBc2lhbiwgQmxhY2ssIE90aGVyIiwKICAgICAgICAgICAgICB5bGFiID0gIkNvdW50IiwKICAgICAgICAgICAgICBtYWluID0gIkZpZyA0LjY6IEFwcGxpY2FudCBDb3VudCBieSBSYWNlIiwKICAgICAgICAgICAgICB5bGltID0gYygwLCA0MDAwKSkKICAgICAgaGlzdChhZG1pc3Npb25zLmZpbmFsJGluY29tZSwgY29sID0gImxpZ2h0Z3JlZW4iLAogICAgICAgICAgIHhsYWIgPSAiRmFtaWx5IEluY29tZSAoJCkiLAogICAgICAgICAgIHlsYWIgPSAiQ291bnQiLAogICAgICAgICAgIG1haW4gPSAiRmlnIDQuNzogVHJhbnNmb3JtZWQgQXBwbGljYW50IENvdW50IGJ5IEluY29tZSIsCiAgICAgICAgICAgeWxpbSA9IGMoMCwgMzAwMCkpCiAgICAgIGhpc3QoYWRtaXNzaW9ucyRpbmNvbWUsIGNvbCA9ICJsaWdodGdyZWVuIiwKICAgICAgICAgICB4bGFiID0gIkZhbWlseSBJbmNvbWUgKCQpIiwKICAgICAgICAgICB5bGFiID0gIkNvdW50IiwKICAgICAgICAgICBtYWluID0gIkZpZyA0Ljg6IE9yaWdpbmFsIEFwcGxpY2FudCBDb3VudCBieSBJbmNvbWUiLAogICAgICAgICAgIHlsaW0gPSBjKDAsIDMwMDApKQogICAgCiAgCiMjIyMjIyBCaXZhcmlhdGUgQW5hbHlzaXMKICAjIyBTZXR1cCAvIEJhc2ljIEhpc3QKICBzdW1tYXJ5KGFkbWlzc2lvbnMuZmluYWwpCiAgdG90YWwuYWRtaXQucmF0ZSA8LSBzdW0oYWRtaXNzaW9ucy5maW5hbCRhZG1pdD09MSkvbGVuZ3RoKGFkbWlzc2lvbnMuZmluYWwkYWRtaXQpCiAgZ2dwIDwtIGdncGxvdChkYXRhLmZyYW1lKGFkbWlzc2lvbnMuZmluYWwpLGFlcyh4PWFkbWl0KSwgYWVzKHk9Y291bnQpKQogICMgaGlzdG9ncmFtCiAgZ2dwICsgZ2VvbV9iYXIoZmlsbD0ibGlnaHRncmVlbiIpICsgbGFicyh4ID0gIjA9UmVqZWN0ZWQsIDE9QWNjZXB0ZWQiLCB5ID0gIkNvdW50IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aXRsZSA9ICJGaWcgNS4xIEFkbWlzc2lvbnMgUmVzdWx0cyAoRmluYWwgRGF0YXNldCkiLCBzdWJ0aXRsZSA9ICJBY2NlcHRhbmNlIFJhdGUgPSAzMC41OCUiKQogIAogICMjIERlbW9ncmFwaGljcyAoUmFjZS9TZXgvJCkKICAgICMjIEFkbWlzc2lvbnMgUmF0ZXMgYnkgUmFjZQogICAgYW5nbG8uYWRtaXQucmF0ZSA8LSBzdW0oYWRtaXNzaW9ucy5maW5hbCRhZG1pdD09MSAmIGFkbWlzc2lvbnMuZmluYWwkYW5nbG89PTEpL3N1bShhZG1pc3Npb25zLmZpbmFsJGFuZ2xvID09IDEpCiAgICBhc2lhbi5hZG1pdC5yYXRlIDwtIHN1bShhZG1pc3Npb25zLmZpbmFsJGFkbWl0PT0xICYgYWRtaXNzaW9ucy5maW5hbCRhc2lhbj09MSkvc3VtKGFkbWlzc2lvbnMuZmluYWwkYXNpYW4gPT0gMSkKICAgIGJsYWNrLmFkbWl0LnJhdGUgPC0gc3VtKGFkbWlzc2lvbnMuZmluYWwkYWRtaXQ9PTEgJiBhZG1pc3Npb25zLmZpbmFsJGJsYWNrPT0xKS9zdW0oYWRtaXNzaW9ucy5maW5hbCRibGFjayA9PSAxKQogICAgb3RoZXIuYWRtaXQucmF0ZSA8LSBzdW0oYWRtaXNzaW9ucy5maW5hbCRhZG1pdD09MSAmIGFkbWlzc2lvbnMuZmluYWwkb3RoZXI9PTEpL3N1bShhZG1pc3Npb25zLmZpbmFsJG90aGVyID09IDEpCiAgICBhZG1pdC5yYXRlcy5yYWNlLmNvbXAgPC0gYyh0b3RhbC5hZG1pdC5yYXRlLCBhbmdsby5hZG1pdC5yYXRlLCBhc2lhbi5hZG1pdC5yYXRlLCBibGFjay5hZG1pdC5yYXRlLCBvdGhlci5hZG1pdC5yYXRlKQogICAgYWRtaXQucmF0ZXMucmFjZS5jb21wCiAgICAgICMjIFBsb3QKICAgICAgYmFycGxvdChhZG1pdC5yYXRlcy5yYWNlLmhvbGQqMTAwLCBjb2wgPSAibGlnaHRncmVlbiIsCiAgICAgICAgICAgICAgeGxhYiA9ICJBZG1pdCBSYXRlOiBUb3RhbCwgQW5nbG8sIEFzaWFuLCBCbGFjaywgT3RoZXIiLAogICAgICAgICAgICAgIHlsYWIgPSAiUGVyY2VudCIsCiAgICAgICAgICAgICAgbWFpbiA9ICJGaWcgNS4yOiBBZG1pdCBSYXRlIGJ5IFJhY2UiLAogICAgICAgICAgICAgIHlsaW0gPSBjKDAsIDQwKSkKICAgICMjIEFkbWlzc2lvbnMgYnkgSW5jb21lCiAgICBpbmNvbWUudW5kZXIuMTAwIDwtIHN1YnNldChhZG1pc3Npb25zLmZpbmFsLCBhZG1pc3Npb25zLmZpbmFsJGluY29tZSA8PTk5OTk4KQogICAgaW5jb21lLm92ZXIuMTAwIDwtIHN1YnNldChhZG1pc3Npb25zLmZpbmFsLCBhZG1pc3Npb25zLmZpbmFsJGluY29tZSA9PSA5OTk5OSkKICAgIHN1bW1hcnkoaW5jb21lLnVuZGVyLjEwMCkKICAgIHN1bW1hcnkoaW5jb21lLm92ZXIuMTAwKQogICAgaW5jb21lLnVuZGVyLjEwMC5hZG1pdC5yYXRlIDwtIDE3NjkvKDE3NjkrNDExMykKICAgIGluY29tZS5vdmVyLjEwMC5hZG1pdC5yYXRlIDwtIDYzOC8oNjM4KzEzNTApCiAgICBpbmNvbWUudW5kZXIuMTAwLmFkbWl0LnJhdGUKICAgIGluY29tZS5vdmVyLjEwMC5hZG1pdC5yYXRlCiAgICAjIyBBZG1pc3Npb25zIGJ5IEdlbmRlcgogICAgbWFsZS5hcHBzIDwtIHN1bShhZG1pc3Npb25zLmZpbmFsJHNleD09MSkKICAgIGZlbWFsZS5hcHBzIDwtIHN1bShhZG1pc3Npb25zLmZpbmFsJHNleD09MCkKICAgIG1hbGUuYWRtaXRzIDwtIHN1bShhZG1pc3Npb25zLmZpbmFsJHNleD09MSAmIGFkbWlzc2lvbnMuZmluYWwkYWRtaXQ9PTEpCiAgICBmZW1hbGUuYWRtaXRzIDwtIHN1bShhZG1pc3Npb25zLmZpbmFsJHNleD09MCAmIGFkbWlzc2lvbnMuZmluYWwkYWRtaXQ9PTEpCiAgICBtYWxlLmFkbWl0LnJhdGUgPC0gbWFsZS5hZG1pdHMvbWFsZS5hcHBzCiAgICBmZW1hbGUuYWRtaXQucmF0ZSA8LSBmZW1hbGUuYWRtaXRzL2ZlbWFsZS5hcHBzCiAgICBtYWxlLmFkbWl0LnJhdGUKICAgIGZlbWFsZS5hZG1pdC5yYXRlCiAgCiAgICAKICAjIyBNZXJpdCAoU0FUL0dQQSkKICAgICMjIEdQQSBRdWFydGlsZXMKICAgIHN1bW1hcnkoYWRtaXNzaW9ucy5maW5hbCRncGEud3RkKQogICAgdG9wLmdwYS5xIDwtIHN1YnNldChhZG1pc3Npb25zLmZpbmFsLCBhZG1pc3Npb25zLmZpbmFsJGdwYS53dGQgPiA0LjE1MSkKICAgIHNjbmQuZ3BhLnEgPC0gc3Vic2V0KGFkbWlzc2lvbnMuZmluYWwsIGFkbWlzc2lvbnMuZmluYWwkZ3BhLnd0ZCA8PSA0LjE1MSAmIGFkbWlzc2lvbnMuZmluYWwkZ3BhLnd0ZCA+IDMuODUwKQogICAgdGhyZC5ncGEucSA8LSBzdWJzZXQoYWRtaXNzaW9ucy5maW5hbCwgYWRtaXNzaW9ucy5maW5hbCRncGEud3RkIDw9IDMuODUwICYgYWRtaXNzaW9ucy5maW5hbCRncGEud3RkID4gMy41MDApCiAgICBib3QuZ3BhLnEgPC0gc3Vic2V0KGFkbWlzc2lvbnMuZmluYWwsIGFkbWlzc2lvbnMuZmluYWwkZ3BhLnd0ZCA8PSAzLjUwMCkKICAgIHRvcC5ncGEuYWRtaXQucmF0ZSA8LSBzdW0odG9wLmdwYS5xJGFkbWl0ID09IDEpL2xlbmd0aCh0b3AuZ3BhLnEkZ3BhLnd0ZCkqMTAwCiAgICBzY25kLmdwYS5hZG1pdC5yYXRlIDwtIHN1bShzY25kLmdwYS5xJGFkbWl0ID09IDEpL2xlbmd0aChzY25kLmdwYS5xJGdwYS53dGQpKjEwMAogICAgdGhyZC5ncGEuYWRtaXQucmF0ZSA8LSBzdW0odGhyZC5ncGEucSRhZG1pdCA9PSAxKS9sZW5ndGgodGhyZC5ncGEucSRncGEud3RkKSoxMDAKICAgIGJvdC5ncGEuYWRtaXQucmF0ZSA8LSBzdW0oYm90LmdwYS5xJGFkbWl0ID09IDEpL2xlbmd0aChib3QuZ3BhLnEkZ3BhLnd0ZCkqMTAwCiAgICB0b3AuZ3BhLmFkbWl0LnJhdGUKICAgIHNjbmQuZ3BhLmFkbWl0LnJhdGUKICAgIHRocmQuZ3BhLmFkbWl0LnJhdGUKICAgIGJvdC5ncGEuYWRtaXQucmF0ZQogICAgcGxvdChjKHRvcC5ncGEuYWRtaXQucmF0ZSwKICAgICAgICAgICBzY25kLmdwYS5hZG1pdC5yYXRlLAogICAgICAgICAgIHRocmQuZ3BhLmFkbWl0LnJhdGUsCiAgICAgICAgICAgYm90LmdwYS5hZG1pdC5yYXRlKSwKICAgICAgICAgeGxhYiA9ICJRdWFydGlsZXMgKDEtNCkiLAogICAgICAgICB5bGFiID0gIkFkbWl0IFJhdGUgJSIsCiAgICAgICAgIG1haW4gPSAiRmlnIDUuMzogQWRtaXQgYnkgR1BBIiwKICAgICAgICAgcGNoID0gMTYsCiAgICAgICAgIGNleCA9IDEuNzUsCiAgICAgICAgIGNvbCA9ICJsaWdodGdyZWVuIikKICAgIAogICAgIyMgU0FUIE1hdGgKICAgIHN1bW1hcnkoYWRtaXNzaW9ucy5maW5hbCRzYXRpLm1hdGgpCiAgICB0b3Auc2F0bS5xIDwtIHN1YnNldChhZG1pc3Npb25zLmZpbmFsLCBhZG1pc3Npb25zLmZpbmFsJHNhdGkubWF0aCA+PSA2OTApCiAgICBzY25kLnNhdG0ucSA8LSBzdWJzZXQoYWRtaXNzaW9ucy5maW5hbCwgYWRtaXNzaW9ucy5maW5hbCRzYXRpLm1hdGggPCA2OTAgJiBhZG1pc3Npb25zLmZpbmFsJHNhdGkubWF0aCA+PSA2MzApCiAgICB0aHJkLnNhdG0ucSA8LSBzdWJzZXQoYWRtaXNzaW9ucy5maW5hbCwgYWRtaXNzaW9ucy5maW5hbCRzYXRpLm1hdGggPCA2MzAgJiBhZG1pc3Npb25zLmZpbmFsJHNhdGkubWF0aCA+PSA1NTApCiAgICBib3Quc2F0bS5xIDwtIHN1YnNldChhZG1pc3Npb25zLmZpbmFsLCBhZG1pc3Npb25zLmZpbmFsJHNhdGkubWF0aCA8IDU1MCkKICAgIHRvcC5zYXRtLmFkbWl0LnJhdGUgPC0gc3VtKHRvcC5zYXRtLnEkYWRtaXQgPT0gMSkvbGVuZ3RoKHRvcC5zYXRtLnEkc2F0aS5tYXRoKSoxMDAKICAgIHNjbmQuc2F0bS5hZG1pdC5yYXRlIDwtIHN1bShzY25kLnNhdG0ucSRhZG1pdCA9PSAxKS9sZW5ndGgoc2NuZC5zYXRtLnEkc2F0aS5tYXRoKSoxMDAKICAgIHRocmQuc2F0bS5hZG1pdC5yYXRlIDwtIHN1bSh0aHJkLnNhdG0ucSRhZG1pdCA9PSAxKS9sZW5ndGgodGhyZC5zYXRtLnEkc2F0aS5tYXRoKSoxMDAKICAgIGJvdC5zYXRtLmFkbWl0LnJhdGUgPC0gc3VtKGJvdC5zYXRtLnEkYWRtaXQgPT0gMSkvbGVuZ3RoKGJvdC5zYXRtLnEkc2F0aS5tYXRoKSoxMDAKICAgIHRvcC5zYXRtLmFkbWl0LnJhdGUKICAgIHNjbmQuc2F0bS5hZG1pdC5yYXRlCiAgICB0aHJkLnNhdG0uYWRtaXQucmF0ZQogICAgYm90LnNhdG0uYWRtaXQucmF0ZQogICAgcGxvdChjKHRvcC5zYXRtLmFkbWl0LnJhdGUsCiAgICAgICAgICAgc2NuZC5zYXRtLmFkbWl0LnJhdGUsCiAgICAgICAgICAgdGhyZC5zYXRtLmFkbWl0LnJhdGUsCiAgICAgICAgICAgYm90LnNhdG0uYWRtaXQucmF0ZSksCiAgICAgICAgIHhsYWIgPSAiUXVhcnRpbGVzICgxLTQpIiwKICAgICAgICAgeWxhYiA9ICJBZG1pdCBSYXRlICUiLAogICAgICAgICBtYWluID0gIkZpZyA1LjQ6IEFkbWl0IGJ5IFNBVCBNYXRoIiwKICAgICAgICAgcGNoID0gMTYsCiAgICAgICAgIGNleCA9IDEuNzUsCiAgICAgICAgIGNvbCA9ICJsaWdodGdyZWVuIikKICAgIAogICAgIyMgU0FUIFZlcmIKICAgIHN1bW1hcnkoYWRtaXNzaW9ucy5maW5hbCRzYXRpLnZlcmIpCiAgICB0b3Auc2F0di5xIDwtIHN1YnNldChhZG1pc3Npb25zLmZpbmFsLCBhZG1pc3Npb25zLmZpbmFsJHNhdGkudmVyYiA+PSA2NTApCiAgICBzY25kLnNhdHYucSA8LSBzdWJzZXQoYWRtaXNzaW9ucy5maW5hbCwgYWRtaXNzaW9ucy5maW5hbCRzYXRpLnZlcmIgPCA2NTAgJiBhZG1pc3Npb25zLmZpbmFsJHNhdGkudmVyYiA+IDU4MCkKICAgIHRocmQuc2F0di5xIDwtIHN1YnNldChhZG1pc3Npb25zLmZpbmFsLCBhZG1pc3Npb25zLmZpbmFsJHNhdGkudmVyYiA8PSA1ODAgJiBhZG1pc3Npb25zLmZpbmFsJHNhdGkudmVyYiA+IDUwMCkKICAgIGJvdC5zYXR2LnEgPC0gc3Vic2V0KGFkbWlzc2lvbnMuZmluYWwsIGFkbWlzc2lvbnMuZmluYWwkc2F0aS52ZXJiIDw9IDUwMCkKICAgIHRvcC5zYXR2LmFkbWl0LnJhdGUgPC0gc3VtKHRvcC5zYXR2LnEkYWRtaXQgPT0gMSkvbGVuZ3RoKHRvcC5zYXR2LnEkc2F0aS52ZXJiKSoxMDAKICAgIHNjbmQuc2F0di5hZG1pdC5yYXRlIDwtIHN1bShzY25kLnNhdHYucSRhZG1pdCA9PSAxKS9sZW5ndGgoc2NuZC5zYXR2LnEkc2F0aS52ZXJiKSoxMDAKICAgIHRocmQuc2F0di5hZG1pdC5yYXRlIDwtIHN1bSh0aHJkLnNhdHYucSRhZG1pdCA9PSAxKS9sZW5ndGgodGhyZC5zYXR2LnEkc2F0aS52ZXJiKSoxMDAKICAgIGJvdC5zYXR2LmFkbWl0LnJhdGUgPC0gc3VtKGJvdC5zYXR2LnEkYWRtaXQgPT0gMSkvbGVuZ3RoKGJvdC5zYXR2LnEkc2F0aS52ZXJiKSoxMDAKICAgIHRvcC5zYXR2LmFkbWl0LnJhdGUKICAgIHNjbmQuc2F0di5hZG1pdC5yYXRlCiAgICB0aHJkLnNhdHYuYWRtaXQucmF0ZQogICAgYm90LnNhdHYuYWRtaXQucmF0ZQogICAgcGxvdChjKCB0b3Auc2F0di5hZG1pdC5yYXRlLAogICAgICAgICAgICBzY25kLnNhdHYuYWRtaXQucmF0ZSwKICAgICAgICAgICAgdGhyZC5zYXR2LmFkbWl0LnJhdGUsCiAgICAgICAgICAgIGJvdC5zYXR2LmFkbWl0LnJhdGUpLAogICAgICAgICB4bGFiID0gIlF1YXJ0aWxlcyAoMS00KSIsCiAgICAgICAgIHlsYWIgPSAiQWRtaXQgUmF0ZSAlIiwKICAgICAgICAgbWFpbiA9ICJGaWcgNS41OiBBZG1pdCBieSBTQVQgVmVyYmFsIiwKICAgICAgICAgcGNoID0gMTYsCiAgICAgICAgIGNleCA9IDEuNzUsCiAgICAgICAgIGNvbCA9ICJsaWdodGdyZWVuIikKICAgIAogICAgIyMgU0FUIFRvdGFsCiAgICBzdW1tYXJ5KGFkbWlzc2lvbnMuZmluYWwkc2F0aS50b3RhbCkKICAgIHRvcC5zYXR0LnEgPC0gc3Vic2V0KGFkbWlzc2lvbnMuZmluYWwsIGFkbWlzc2lvbnMuZmluYWwkc2F0aS50b3RhbCA+IDEzMjApCiAgICBzY25kLnNhdHQucSA8LSBzdWJzZXQoYWRtaXNzaW9ucy5maW5hbCwgYWRtaXNzaW9ucy5maW5hbCRzYXRpLnRvdGFsIDw9IDEzMjAgJiBhZG1pc3Npb25zLmZpbmFsJHNhdGkudG90YWwgPj0gMTIxMCkKICAgIHRocmQuc2F0dC5xIDwtIHN1YnNldChhZG1pc3Npb25zLmZpbmFsLCBhZG1pc3Npb25zLmZpbmFsJHNhdGkudG90YWwgPCAxMjEwICYgYWRtaXNzaW9ucy5maW5hbCRzYXRpLnRvdGFsID49IDEwNzApCiAgICBib3Quc2F0dC5xIDwtIHN1YnNldChhZG1pc3Npb25zLmZpbmFsLCBhZG1pc3Npb25zLmZpbmFsJHNhdGkudG90YWwgPCAxMDcwKQogICAgdG9wLnNhdHQuYWRtaXQucmF0ZSA8LSBzdW0odG9wLnNhdHQucSRhZG1pdCA9PSAxKS9sZW5ndGgodG9wLnNhdHQucSRzYXRpLnRvdGFsKSoxMDAKICAgIHNjbmQuc2F0dC5hZG1pdC5yYXRlIDwtIHN1bShzY25kLnNhdHQucSRhZG1pdCA9PSAxKS9sZW5ndGgoc2NuZC5zYXR0LnEkc2F0aS50b3RhbCkqMTAwCiAgICB0aHJkLnNhdHQuYWRtaXQucmF0ZSA8LSBzdW0odGhyZC5zYXR0LnEkYWRtaXQgPT0gMSkvbGVuZ3RoKHRocmQuc2F0dC5xJHNhdGkudG90YWwpKjEwMAogICAgYm90LnNhdHQuYWRtaXQucmF0ZSA8LSBzdW0oYm90LnNhdHQucSRhZG1pdCA9PSAxKS9sZW5ndGgoYm90LnNhdHQucSRzYXRpLnRvdGFsKSoxMDAKICAgIHRvcC5zYXR0LmFkbWl0LnJhdGUKICAgIHNjbmQuc2F0dC5hZG1pdC5yYXRlCiAgICB0aHJkLnNhdHQuYWRtaXQucmF0ZQogICAgYm90LnNhdHQuYWRtaXQucmF0ZQogICAgcGxvdChjKCB0b3Auc2F0dC5hZG1pdC5yYXRlLAogICAgICAgICAgICBzY25kLnNhdHQuYWRtaXQucmF0ZSwKICAgICAgICAgICAgdGhyZC5zYXR0LmFkbWl0LnJhdGUsCiAgICAgICAgICAgIGJvdC5zYXR0LmFkbWl0LnJhdGUpLAogICAgICAgICB4bGFiID0gIlF1YXJ0aWxlcyAoMS00KSIsCiAgICAgICAgIHlsYWIgPSAiQWRtaXQgUmF0ZSAlIiwKICAgICAgICAgbWFpbiA9ICJGaWcgNS41OiBBZG1pdCBieSBTQVQgVG90YWwiLAogICAgICAgICBwY2ggPSAxNiwKICAgICAgICAgY2V4ID0gMS43NSwKICAgICAgICAgY29sID0gImxpZ2h0Z3JlZW4iKQogICAgCiAgICAjIyBQYXIgLS0gR3JhcGggYWxsIDQKICAgIHBhcihtZnJvdz1jKDIsMikpCiAgICBwbG90KGModG9wLmdwYS5hZG1pdC5yYXRlLAogICAgICAgICAgIHNjbmQuZ3BhLmFkbWl0LnJhdGUsCiAgICAgICAgICAgdGhyZC5ncGEuYWRtaXQucmF0ZSwKICAgICAgICAgICBib3QuZ3BhLmFkbWl0LnJhdGUpLAogICAgICAgICB4bGFiID0gIlF1YXJ0aWxlcyAoMS00KSIsCiAgICAgICAgIHlsYWIgPSAiQWRtaXQgUmF0ZSAlIiwKICAgICAgICAgbWFpbiA9ICJGaWcgNS4zOiBBZG1pdCBieSBHUEEiLAogICAgICAgICBwY2ggPSAxNiwKICAgICAgICAgY2V4ID0gMS43NSwKICAgICAgICAgY29sID0gImxpZ2h0Z3JlZW4iKQogICAgcGxvdChjKHRvcC5zYXRtLmFkbWl0LnJhdGUsCiAgICAgICAgICAgc2NuZC5zYXRtLmFkbWl0LnJhdGUsCiAgICAgICAgICAgdGhyZC5zYXRtLmFkbWl0LnJhdGUsCiAgICAgICAgICAgYm90LnNhdG0uYWRtaXQucmF0ZSksCiAgICAgICAgIHhsYWIgPSAiUXVhcnRpbGVzICgxLTQpIiwKICAgICAgICAgeWxhYiA9ICJBZG1pdCBSYXRlICUiLAogICAgICAgICBtYWluID0gIkZpZyA1LjQ6IEFkbWl0IGJ5IFNBVCBNYXRoIiwKICAgICAgICAgcGNoID0gMTYsCiAgICAgICAgIGNleCA9IDEuNzUsCiAgICAgICAgIGNvbCA9ICJsaWdodGdyZWVuIikKICAgIHBsb3QoYyggdG9wLnNhdHYuYWRtaXQucmF0ZSwKICAgICAgICAgICAgc2NuZC5zYXR2LmFkbWl0LnJhdGUsCiAgICAgICAgICAgIHRocmQuc2F0di5hZG1pdC5yYXRlLAogICAgICAgICAgICBib3Quc2F0di5hZG1pdC5yYXRlKSwKICAgICAgICAgeGxhYiA9ICJRdWFydGlsZXMgKDEtNCkiLAogICAgICAgICB5bGFiID0gIkFkbWl0IFJhdGUgJSIsCiAgICAgICAgIG1haW4gPSAiRmlnIDUuNTogQWRtaXQgYnkgU0FUIFZlcmJhbCIsCiAgICAgICAgIHBjaCA9IDE2LAogICAgICAgICBjZXggPSAxLjc1LAogICAgICAgICBjb2wgPSAibGlnaHRncmVlbiIpCiAgICBwbG90KGMoIHRvcC5zYXR0LmFkbWl0LnJhdGUsCiAgICAgICAgICAgIHNjbmQuc2F0dC5hZG1pdC5yYXRlLAogICAgICAgICAgICB0aHJkLnNhdHQuYWRtaXQucmF0ZSwKICAgICAgICAgICAgYm90LnNhdHQuYWRtaXQucmF0ZSksCiAgICAgICAgIHhsYWIgPSAiUXVhcnRpbGVzICgxLTQpIiwKICAgICAgICAgeWxhYiA9ICJBZG1pdCBSYXRlICUiLAogICAgICAgICBtYWluID0gIkZpZyA1LjU6IEFkbWl0IGJ5IFNBVCBUb3RhbCIsCiAgICAgICAgIHBjaCA9IDE2LAogICAgICAgICBjZXggPSAxLjc1LAogICAgICAgICBjb2wgPSAibGlnaHRncmVlbiIpCiAgICAKICAgICMjIENvcnJlbGF0aW9uIAogICAgIyNTQVQgTSBhbmQgU0FUIFYKICAgIGNvcihhZG1pc3Npb25zLmZpbmFsJHNhdGkubWF0aCwgYWRtaXNzaW9ucy5maW5hbCRzYXRpLnZlcmIpCiAgICAjIyBTQVQgVCBhbmQgR1BBCiAgICBjb3IoYWRtaXNzaW9ucy5maW5hbCRzYXRpLnRvdGFsLCBhZG1pc3Npb25zLmZpbmFsJGdwYS53dGQpCiAgICAKIyMjIyMgTXVsdGl2YXJpYXRlIEFuYWx5c2lzIChSRikKICAjIyBObyBNaXNjbGFzc2lmaWNhdGlvbiBjb3N0cwogICAgIyByYW5kb20gZm9yZXN0cwogICAgcmYxPC1yYW5kb21Gb3Jlc3QoYWRtaXR+YW5nbG8rYXNpYW4rYmxhY2srb3RoZXIrZ3BhLnd0ZCtzYXRpLnZlcmIrc2F0aS5tYXRoK3NhdGkudG90YWwraW5jb21lK3NleCwKICAgICAgZGF0YT1hZG1pc3Npb25zLmZpbmFsLGltcG9ydGFuY2U9VCxudHJlZT03NTApCiAgICByZjEuY29uZiA8LSByZjEkY29uZnVzaW9uCiAgICByZjEuY29uZgogICAgcGxvdChyZjEpCiAgICBwcmludChyZjEpCiAgIyMgTWlzY2xhc3NpZmljYXRpb24gY29zdHMKICAgICMjIDQ6MQogICAgcmYyPC1yYW5kb21Gb3Jlc3QoYWRtaXR+YW5nbG8rYXNpYW4rYmxhY2srb3RoZXIrZ3BhLnd0ZCtzYXRpLnZlcmIrc2F0aS5tYXRoK3NhdGkudG90YWwraW5jb21lK3NleCwKICAgICAgICAgICAgICAgICAgICAgIGRhdGE9YWRtaXNzaW9ucy5maW5hbCxpbXBvcnRhbmNlPVQsbnRyZWU9NzUwLCBzYW1wc2l6ZT1jKDEwMDAsMTcwMCkpCiAgICBwcmludChyZjIpCiAgICAKICAgIHJmMzwtcmFuZG9tRm9yZXN0KGFkbWl0fmFuZ2xvK2FzaWFuK2JsYWNrK290aGVyK2dwYS53dGQrc2F0aS52ZXJiK3NhdGkubWF0aCtzYXRpLnRvdGFsK2luY29tZStzZXgsCiAgICAgICAgICAgICAgICAgICAgICBkYXRhPWFkbWlzc2lvbnMuZmluYWwsaW1wb3J0YW5jZT1ULG50cmVlPTc1MCwgc2FtcHNpemU9YygxMDAsMjAwMCkpCiAgICBwcmludChyZjMpCiAgICAKICAjIyBQcmVkaWN0aW9uIEFjY3VyYWN5IC8vIEltcG9ydGFuY2UgUGxvdHMKICAgICMgVmFyaWFibGUgSW1wb3J0YW5jZSBQbG90cwogICAgcGFyKG1mcm93PWMoMiwyKSkKICAgIHZhckltcFBsb3QocmYyLHR5cGU9MSxzY2FsZT1GLGNsYXNzPTEsCiAgICAgICAgICAgICAgIG1haW49IkZvcmVjYXN0aW5nIEltcG9ydGFuY2UgUGxvdCBmb3IgQWRtaXNzaW9uCiAgICAgICAgICAgICAgIChVbnN0YW5kYXJkaXplZCkiLGNvbD0ibGlnaHRncmVlbiIsY2V4PTEscGNoPTE5KQogICAgdmFySW1wUGxvdChyZjIsdHlwZT0xLHNjYWxlPVQsY2xhc3M9MSwKICAgICAgICAgICAgICAgbWFpbj0iRm9yZWNhc3RpbmcgSW1wb3J0YW5jZSBQbG90IGZvciBBZG1pc3Npb24KICAgICAgICAgICAgICAgKFN0YW5kYXJkaXplZCkiLCBjb2w9ImxpZ2h0Z3JlZW4iLGNleD0xLHBjaD0xOSkKICAgIHZhckltcFBsb3QocmYyLHR5cGU9MSxzY2FsZT1GLGNsYXNzPTAsCiAgICAgICAgICAgICAgIG1haW49IkZvcmVjYXN0aW5nIEltcG9ydGFuY2UgUGxvdCBmb3IgUmVqZWN0aW9uCiAgICAgICAgICAgICAgIChVbnN0YW5kYXJkaXplZCkiLCBjb2w9ImxpZ2h0Z3JlZW4iLGNleD0xLHBjaD0xOSkKICAgIHZhckltcFBsb3QocmYyLHR5cGU9MSxzY2FsZT1ULGNsYXNzPTAsCiAgICAgICAgICAgICAgIG1haW49IkZvcmVjYXN0aW5nIEltcG9ydGFuY2UgUGxvdCBmb3IgUmVqZWN0aW9uCiAgICAgICAgICAgICAgIChTdGFuZGFyZGl6ZWQpIiwgY29sPSJsaWdodGdyZWVuIixjZXg9MSxwY2g9MTkpCiAgICAKICAgICMgUGFydGlhbCBEZXBlbmRlbmNlIFBsb3RzCiAgICBwYXIobWZyb3c9YygyLDEpKQogICAgcGFydDE8LXBhcnRpYWxQbG90KHJmMixwcmVkLmRhdGE9YWRtaXNzaW9ucy5maW5hbCx4LnZhcj1ncGEud3RkLAogICAgICAgICAgICAgICAgICAgICAgIHJ1Zz1ULHdoaWNoLmNsYXNzPTEsIG1haW49IlBhcnRpYWwgRGVwZW5kZW5jZSBQbG90IGZvciBHUEEgb24gQWRtaXNzaW9uIiwgeGxhYj0iR1BBIiwKICAgICAgICAgICAgICAgICAgICAgICB5bGFiPSJDZW50ZXJlZCBMb2cgT2RkcyIpCiAgICBzY2F0dGVyLnNtb290aChwYXJ0MSR4LHBhcnQxJHksc3Bhbj0xLzMsY29sPSJibHVlIixwY2g9MTksIG1haW49IlBhcnRpYWwgRGVwZW5kZW5jZSBQbG90IGZvciBHUEEgb24gQWRtaXNzaW9uIiwgeGxhYj0iR1BBIiwKICAgICAgICAgICAgICAgICAgIHlsYWI9IkNlbnRlcmVkIExvZyBPZGRzIikKICAgIHBhcihtZnJvdz1jKDIsMSkpCiAgICBwYXJ0MjwtcGFydGlhbFBsb3QocmYyLHByZWQuZGF0YT1hZG1pc3Npb25zLmZpbmFsLHgudmFyPXNhdGkudG90YWwsCiAgICAgICAgICAgICAgICAgICAgICAgcnVnPVQsd2hpY2guY2xhc3M9MSwgbWFpbj0iUGFydGlhbCBEZXBlbmRlbmNlIFBsb3QgZm9yIFNBVCBUb3RhbCBvbiBBZG1pc3Npb24iLCB4bGFiPSJTQVQgU2NvcmUiLAogICAgICAgICAgICAgICAgICAgICAgIHlsYWI9IkNlbnRlcmVkIExvZyBPZGRzIikKICAgIHNjYXR0ZXIuc21vb3RoKHBhcnQxJHgscGFydDEkeSxzcGFuPTEvMyxjb2w9ImJsdWUiLHBjaD0xOSwgbWFpbj0iUGFydGlhbCBEZXBlbmRlbmNlIFBsb3QgZm9yIFNBVCBUb3RhbCBvbiBBZG1pc3Npb24iLCB4bGFiPSJTQVQgU2NvcmUiLAogICAgICAgICAgICAgICAgICAgeWxhYj0iQ2VudGVyZWQgTG9nIE9kZHMiKQpgYGAK