By: Jeffrey Ellington
9:00am Mon-Wed Section
3/1/17
1. Introduction: Assessing Homelessness in LA
Public policy initiatives in Los Angeles use data on homelessness to properly direct funding to social programs. The information provides valuable insight into the counts and location of homeless people across LA. However, gathering data across over 2,000 census tracks in LA is burdensome and expensive. As a result, the city collected information from 509 of its over 2,000 census tracks to form the basis for a statistical analysis of homelessness in LA. This information can be analyzed to help allocate resources efficiently to the locations where homelessness is most prevalent.
2. Data Description
The data were generated from a random sample of 509 of the over 2,000 census tracts in Los Angeles. For our treatment, we will examine six variables:
1. StreetTotal – The number of homeless individuals in a census tract
2. MedianIncome – Median household income in a census tract
3. PropVacant – The proportion of vacant residential dwellings in a census tract
4. PropMinority – The proportion of minority residents in a census tract
5. PctCommercial – The percentage of a census tract zoned for commercial enterprises
6. PctIndustrial – The percentage of a census tract zoned for industrial enterprises
StreetTotal is the response variable. It is expressed as a positive integer given it is count data. StreetTotal counts were collected in the 509 census tracts, where each individual count was performed over two consecutive days/evenings. The histogram for homelessness counts in LA can be seen below in Fig. 2.1. Note that the distribution is heavily skewed, with over 450 (92%) census tracts having counts of less than 100 homeless people observed during the sampling period. However, on the right extreme, 13 tracts had counts over 200. The highest count is 931, which is over 58x the median value of 16.
The other five variables will be examined in much greater detail once they’ve been properly cleaned. However, it’s worth stating up front that while these 5 variables seem important in an analysis of homelessness, they are far from complete. An expert on homelessness in Los Angeles could likely point to several other important variables, like crime or drug usage/access, not to mention how the data were actually collected and by whom. We also have no framework for existing policy in LA that may effect 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.
hist(Homeless$StreetTotal, xlab = "Homeless Count", ylab = "Frequency", main = "Fig 2.1: Histogram of LA Homelessness Counts")

3. Data Cleaning
Before examining the relationships between StreetTotal and the other five variables, we will first need to clean and prepare the data. Reviewing the summary statistics for the 6 variables below, a few issues immediately pop out.
summary(Homeless)
StreetTotal MedianIncome PropVacant PropMinority PctCommercial PctIndustrial
Min. : 0.0 Min. : 0 Min. :0.00000 Min. : 0.05231 Min. : 0.000 Min. : 0.000
1st Qu.: 6.0 1st Qu.: 27315 1st Qu.:0.02504 1st Qu.: 0.36683 1st Qu.: 7.978 1st Qu.: 0.000
Median : 16.0 Median : 36800 Median :0.03701 Median : 0.56099 Median :14.614 Median : 0.000
Mean : 37.6 Mean : 41744 Mean :0.04807 Mean : 1.11793 Mean :17.438 Mean : 6.777
3rd Qu.: 36.0 3rd Qu.: 49853 3rd Qu.:0.05909 3rd Qu.: 0.69608 3rd Qu.:23.068 3rd Qu.: 7.503
Max. :931.0 Max. :200001 Max. :0.43003 Max. :100.00000 Max. :99.050 Max. :75.718
NA's :4
First, PropVacant has 4 missing values. Looking at these four values more directly reveals several other quirks in the data. The MedianIncome for these 4 census tracts is zero, which is not believable. In addition, three of the four PropMinority values are 100.00, despite the acceptable range of values being 0.00 to 1.00. The information on these four census tracts is unreliable. Given the reasons above and that the StreetTotal counts are ordinary (range of 5-12), we will remove these four census tracts from the analysis. Luckily, there are no other cases of PropMinority or MedianIncome displaying these issues (MeidanIncome = 0 and PropMinority > 1.00).
Homeless[c(10,445,501,502),]
Let’s review the updated summary statistics for the new data set with the 4 faulty values removed to see if any other issues are immediately obvious. PropMinority’s max value of 1.00 and PropVacant/PctCommercial/PctIndustrial’s lower bounds of 0.00 are worth investigating. A quick glance at PropVacant/PctCommercial/PctIndustrial’s lower bounds of 0.00 did not reveal anything systematically divergent or suggest anything wrong in the data reporting. However, the census tract with PropMinority = 1.00 was a cause for skepticism.
summary(Homeless1)
StreetTotal MedianIncome PropVacant PropMinority PctCommercial PctIndustrial
Min. : 0.00 Min. : 3779 Min. :0.00000 Min. :0.07659 Min. : 0.000 Min. : 0.000
1st Qu.: 6.00 1st Qu.: 27654 1st Qu.:0.02504 1st Qu.:0.36683 1st Qu.: 7.978 1st Qu.: 0.000
Median : 16.00 Median : 37034 Median :0.03702 Median :0.56059 Median :14.614 Median : 0.000
Mean : 37.84 Mean : 42075 Mean :0.04807 Mean :0.53262 Mean :17.471 Mean : 6.657
3rd Qu.: 36.00 3rd Qu.: 49929 3rd Qu.:0.05909 3rd Qu.:0.69256 3rd Qu.:23.160 3rd Qu.: 7.263
Max. :931.00 Max. :200001 Max. :0.43003 Max. :1.00000 Max. :99.050 Max. :75.718
The census tract with PropMinority = 1.00 proves interesting on multiple levels. It is also one of three census tracts with the highest median income (MedianIncome = $201,000). While it’s difficult to believe the census tract is literally 100% minority, it’s at least very directionally possible. There are 17 other census tracts with the proportion of minority residents greater than 90%, including a tract with 98.1%. What is less believable is the median income of $201,000, given a proportion of minority residents of 100%. Looking at the data, 304 of the 505 census tracts (60%) have a proportion of minority residents over 50%. Of this subset, the median MedianIncome is $30,830 and the mean MedianIncome is $33,560. Aside from the $201,000 value under examination, there is one other value greater than $100,000 ($105,652). For these reasons, it’s likely to believe that this value is either an extreme outlier that will skew the rest of the analysis -or- is misreported. In either case, it’s worth striking from the analysis.
One final adjustment we will make is to convert PropVacant and PropMinority to percentages (like PctCommercial and PctIndustrial). This will help center units in the same range (as opposed to 100x disparity), which will help for statistical reasons. Moving forward PropVacant and PropMinority will be seen as PctVacant and PctMinority.
At this point, after removing NA’s and looking for unreasonable values, the data seem clean and ready for analysis.
4. Analysis of Univariate Statistics
Quickly looking again with our response variable, StreetTotal, we see a skewed distribution. The median count of observed homeless people is 16. The 90th percentile is a count of 83. Yet, the top 5 are: 480, 595, 655, 666, 931. From a Level I perspective, we can state that based on the observed counts, homelessness is not evenly distributed across the 504 census tracts. Instead, homelessness clumps in specific areas as Fig 4.1 helps visualize below. Note: Fig 4.1 only shows the top 40 counts (8% of all census tracts).
hist(HomelessOver100$StreetTotal, xlab = "Homeless Count", ylab = "Frequency", main = "Fig 4.1: Histogram of LA Homelessness Counts Over 100")

Based on an intuitive first pass, at least for anyone who’s spent time in LA, we shouldn’t be too surprised by these findings. It’s fairly rare to see homeless people in affluent suburban neighborhoods and fairly common to see homeless people in either denser parts of the city where people cluster or in industrial parts of the city where homeless people are less likely to be bothered. Before we can generalize from this basic heuristic, we should look at the other variables to see how they are distributed. From there, we can build an understanding of how LA is constructed as a city in terms of racial, income, and property type distributions.
Looking at the other parameters, four of the five variables also form heavily skewed distributions as can be seen below in Fig 4.2a-d. Commonsense says that median income will follow a skewed or logarithmic distribution. It’s also not surprising that 92% of census tracts have residential vacancy less than 10% given LA is a desirable place to live. The distributions of commercial and industrial real estate percentages also reflect our general experiences: Most areas in a major metropolis are not industrial, but a few areas are very industrial. Likewise, commercial and residential tend to blend together more, given people like to shop near where they live for convenience. Again, nothing here is meant to imply causality; instead, we are simply stating that the distributions look reasonable to an intelligent person who has spent time in LA. That said, at this point, the analysis is trending toward Level II, given we are extrapolating from the specific observations within these 504 LA census tracts to inform a broader picture of LA as a city. Given the shape of the distributions, this feels reasonable.
par(mfrow=c(2,2))
hist(HomelessF$MedianIncome, xlab = "Median Income ($)", ylab = "Frequency", main = "Fig. 4.2a: LA Median Incomes")
hist(HomelessF$PctVacant, xlab = "Vacancy (%)", ylab = "Frequency", main = "Fig. 4.2b: LA % Vacant (Residential)")
hist(HomelessF$PctCommercial, xlab = "Commercial (%)", ylab = "Frequency", main = "Fig. 4.2c: LA % Commercial Real Esate")
hist(HomelessF$PctIndustrial, xlab = "Industrial (%)", ylab = "Frequency", main = "Fig. 4.2d: LA % Industrial Real Esate")

Looking at distribution of the percentage of residents that are minorities within a given census tract, we see a very different picture. The distribution is fairly normal, with a slightly fat tail built up on the left side (Fig 4.3). The mean and median are 53% and 56%, respectively, and reflect a diverse population. Looking at Wikipedia’s demographic information on LA for the 2010 census, Whites made up 29%, Latinos 49%, Blacks 10%, and Asians 11% (https://en.Wikipedia.org/wiki/Los_Angeles#Demographics). What our specific census data on the 504 tracts reflects is that most individual census tracts are comprised of a diverse mix of people. Again, on a Level I analysis, we see heterogeneity among and within the census tracts. Pushing to Level II, the Wikipedia information corroborates these findings for LA as a whole.
hist(HomelessF$PctMinority, xlab = "Minorities (%)", ylab = "Frequency", main = "Fig 4.3: LA Percentage Minorities")

5. Analysis of Bivariate Statistics
Now that we have a firm understanding of the data, it’s time to explore how our response, StreetTotal, interacts with the other variables.
First, we will set a baseline using simple linear regression. For this, we will remain strictly in Level I analysis and not split our data in anyway. The goal here is merely to understand the direction and magnitude of effect each variable has on the response. In the code output below, we see the intercept and linear regression coefficient for each variable against StreetTotal (independently as single variable regressions).
co.Com
(Intercept) PctCommercial
21.6521639 0.9289005
co.Ind
(Intercept) PctIndustrial
30.113596 1.169307
co.MI
(Intercept) MedianIncome
69.8774712945 -0.0007654172
co.Min
(Intercept) PctMinority
10.3413383 0.5185553
co.Vac
(Intercept) PctVacant
10.981298 5.590858
These coefficients state the following about the relationship with each variable and StreetTotal, for only these 504 census tracts. Again, to reiterate, this in not implying causality or statistical significance, merely a description of the data:
PctCommercial: For every 1% point increase in percent of commercial land in a given census tract, the count of homeless observations increases by 0.93
PctIndustrial: For every 1% point increase in percent of industrial land in a given census tract, the count of homeless observations increases by 1.17
MedianIncome: For every $1 increase in the median income in a given census tract, the count of homeless observations decreases by 0.00076. Translated, for every increase of $1,000, the count of homeless observations decreases by 0.76
PctMinority: For every 1% point increase in percent of the residents that are minorities in a given census tract, the count of homeless observations increases by 0.52
PctVacant: For every 1% point increase in percent of residential property that is vacant in a given census tract, the count of homeless observations increases by 5.59
Reviewing these descriptions, it’s easy to see how some of these may generalize. On the whole, we’d intuitively expect to find higher counts of homeless people in places with industrial or vacant property, which the data reflect. Additionally, we’d expect to see homelessness counts drop in more affluent areas, which again the data reflect. The percent of minority residents has the weakest effect (among the four directly comparable percent values), which feels right given that we observed LA is a diverse community, both within and among census tracts. The slightly positive effect likely stems from the relationship we observed earlier between percent minority and median income. We look at this relationship again, plotted below as Fig 5.1. The relationship between Median Income and % Minority is weak, but noticeable. Again, all of the analysis directly above should be seen as a Level I description of the data (with the occasional sanity check), nothing more.
plot(MedianIncome~PctMinority, main = "Fig. 5.1: Median Income by % Minority")

Next, we look at an application of smoothing splines to the data to move beyond basic linear methods. The idea here is that we are looking at a highly skewed response distribution (as well as highly skewed prediction variables). Given this, we don’t anticipate a linear model will best fit the data. Instead of forcing a linear equation onto the data, we can instead using smoothing splines to identify knots (think of them as inflection points) in the data. The technical math is beyond the scope of this paper (and my abilities!), but the key concept is that for any given range of X, we will apply a given function, F(x) that isn’t constrained to being linear. When there is sufficient movement in the data away from F(x), we will introduce a knot, and then describe a new F(x) over a new range of x. As the number of knots increases, we become prone to over fitting the data. To combat this, we can segment the data into training, evaluation, and test samples. For the sake of speed, we will simply use the default method here of leave-one-out cross validation, which roughly approximates the results you’d typically see in a training, evaluation, and test split.
The three variables we will examine are: PctIndustrial (Fig 5.2a-d), PctVacant (Fig 5.3a-d), and MedianIncome (Fig 5.4a-d). We will examine four underlying concepts in univariate analysis. Each thought will be tied to a specific plot to help elucidate the point:
We are still in Level 1 analysis. Any attempt to interpret coefficients, degrees of freedom, or model fit is futile. There are too many statistical processes occurring under the hood to infer causality or statistical significant. The curves simply reflect that data as presented among the 504 random census tract samples. (Note: General, could be applied to all plots.)
When SPAR is small (0.5, in this case), the fits are extremely bumpy and heavily influenced by outliers. For example, in graph 5.2a: HL Count by % Indust.(spar=0.5), the sharp bump at PctIndustrial = 20% does not imply some magic increase in homelessness occurs then. It simply means that the large outliers in the count are pulling the function up at that moment. To be able to extend this to Level II analysis, we’d need a DEEP subject matter expert, who then could maybe infer that there are specific zoning laws/benefit programs tied to zoning laws in LA that have an artificial cliff at 20%. But for us to draw this type of conclusion from the data as presented would be irresponsible.
The distribution of the underlying variables being analyzed matters. To expand on this, let’s compare two plots: 5.2b: HL Count by % Indust.(spar=1.0) & 5.4b: HL Count by Med Income (spr=1.0). Even when SPAR = 1.0 (theoretically the max penalty for SPAR; essentially the algorithm is trying to add as few knots as possible), in 5.2b we see a large spike at Postindustrial = 65% before a sharp decline at PctIndustrial = 75%. Because there are so few observations at the tail end of this distribution (4 of 504 above 60%, i.e. <1%), the model will likely overfit. In contrast, in figure 5.4b, we see a very “linear” looking relationship all the way out to median income of $201,000. However, while homelessness does seem to decline with increasing median, the broader point is that the data in that tail is irrelevant given it has so little support. We could likely find a district in the Bay Area (my home city) that has a sky high median income and also a high level of homelessness: Downtown Palo Alto. The point is even with advanced models, do not place too much weight in any explanation that relies on weakly supported data. When dealing with income, this will almost always be the case given it’s often skewed underlying distribution.
Lastly, we discuss the Bias-Variance trade off by looking at high values of SPAR (SPAR = 1.5) as an approximation of a linear model. The simple conceptual point about the algorithm is: with enough penalty placed on each additional knot, the model will eventually approximate a linear model. In practice, we want to keep SPAR <= 1.0. However, to elucidate the underlying mechanisms of the tool, compare figures: 5.2c: HL Count by % Indust.(spar=1.5) & 5.2d: HL Count by % Indust.(linear). The lines are nearly indistinguishable. What is so important to generalize is: this highlights the larger and incredibly important overall Bias-Variance trade off. When SPAR is low, it better approximates the data as presented. When SPAR is sufficiently high, it approximates a linear model. In this case, we know the low value of SPAR is overfitting to the data in an attempt to capture all of the variance. When SPAR is high, it sacrifices trying to explain all the variance and instead biases its results toward the overall trend. To see this very clearly, look at the spike vs. non-spike at PctIndustrial = 65% in 5.2a vs. 5.2d. At SPAR = 1.5, the algorithm ignores the large outliers to preserve the overall trend.
par(mfrow=c(2,2))
out<-gam(StreetTotal~s(PctIndustrial, spar=.5),data=HomelessF)
plot(out, residuals=T, ylab = "Count", main = "5.2a: HL Count by % Indust.(spar=0.5)")
out<-gam(StreetTotal~s(PctIndustrial, spar=1.0),data=HomelessF)
plot(out, residuals=T, ylab = "Count", main = "5.2b: HL Count by % Indust.(spar=1.0)")
out<-gam(StreetTotal~s(PctIndustrial, spar=1.5),data=HomelessF)
plot(out, residuals=T, ylab = "Count", main = "5.2c: HL Count by % Indust.(spar=1.5)")
plot(StreetTotal~PctIndustrial, ylab = "Count", main = "5.2d: HL Count by % Indust.(linear)")
abline(a=PctIndustrial, b=StreetTotal)
par(mfrow=c(2,2))

out<-gam(StreetTotal~s(PctVacant, spar=.5),data=HomelessF)
plot(out, residuals=T, ylab = "Count", main = "5.3a: HL Count by % Vacant (spar=0.5)")
out<-gam(StreetTotal~s(PctVacant, spar=1.0),data=HomelessF)
plot(out, residuals=T, ylab = "Count", main = "5.3b: HL Count by % Vacant (spar=1.0)")
out<-gam(StreetTotal~s(PctVacant, spar=1.5),data=HomelessF)
plot(out, residuals=T, ylab = "Count", main = "5.3c: HL Count by % Vacant (spar=1.5)")
plot(StreetTotal~PctVacant, ylab = "Count", main = "5.3d: HL Count by % Vacant (linear)")
abline(a=PctVacant, b=StreetTotal)
par(mfrow=c(2,2))

out<-gam(StreetTotal~s(MedianIncome, spar=.5),data=HomelessF)
plot(out, residuals=T, ylab = "Count", main = "5.4a: HL Count by Med Income (spr=0.5)")
out<-gam(StreetTotal~s(MedianIncome, spar=1.0),data=HomelessF)
plot(out, residuals=T, ylab = "Count", main = "5.4b: HL Count by Med Income (spr=1.0)")
out<-gam(StreetTotal~s(MedianIncome, spar=1.5),data=HomelessF)
plot(out, residuals=T, ylab = "Count", main = "5.4c: HL Count by Med Income (spr=1.5)")
plot(StreetTotal~MedianIncome, ylab = "Count", main = "5.4d: HL Count by Med Income (linear)")
abline(a=MedianIncome, b=StreetTotal)

6. Analysis of Multivariate Statistics
Finally, we get to the General Additive Model (GAM): 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 estimate using GAM will lack many potentially explanatory variables. In short, we have the 5 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 put significant weight on any explanation that is weakly supported by few observations. We saw that it took a SPAR = 1.5 to “ignore” the leverage points in 5.2c: HL Count by % Indust.(spar=1.5). Well, with GAM we are adding more variables to the models and hence creating hyper dimensional space. As a result, any observation in n-dimensional space will be less supported than it was in 2D space. Both response outliers and sparse patches of data will have to be examined cautiously. Finally, we will completely depart from the idea of regression coefficients when using GAM. The transformations involved in creating the model are sufficiently complex that any interpretation of the coefficients would depart from reality.
To evaluate our model, we will split the data into three buckets: training data (60%), evaluation data (20%), and test data (20%). This allows us to fit a model based on training data, tweak parameters via evaluation data (some snooping), and then test the model’s fit on the test data. This framework is a good way to prevent overfitting. One crucial point to emphasize: with our relatively small sample set (n=504) and existence of both massive outliers in the response and highly skewed underlying distributions for four of the five predictors, we will have to think critically about how to extrapolate results and extra beware of overfitting.
Based on my initial split, the data look reasonably stable. There are large outliers for StreetTotal among all three splits. In addition, the medians, mins, and maxs across the predictive variables are tolerably consistent. PctIndustrial’s 4 outliers above 60% may cause issues. Less noticeable, MedianIncome and PctCommercial may exhibit slight bias given outlier values. Overall, however, the split seems reasonable and in the spirit of a random split, we will move forward.
6.1: Test Data
After building the GAM model, we look at the fitted Homeless Counts on the training data for varying levels of SPAR (0.5, 0.75, 1.0, 1.5). Figure 6.1a-d displays these histograms below. From our thorough analysis before, we know that SPAR = 0.5 is highly sensitive and prone to overfit. In contrast, SPAR = 1.5 approaches a linear model, which is reflected in the (nearly) normal distribution produced in 6.1d. Looking at the underlying fits for SPAR = 1.5 on an individual element basis, the trend to approximate a linear model continues. SPAR = 0.75 and 1.0 are very hard to distinguish. On a more substantive level, how SPAR = 0.75 and 1.0 differ is interesting. SPAR = 0.75 (Fig. 6.1b) leaves more data in the tails (both at zero and the far right). From a policy level, 0 homeless vs. <5 homeless people is likely not the core concern. However, areas with massive homeless counts are likely very interesting to the policy makers. Given these factors, let’s error on the side of better fit and proceed with the evaluating the SPAR = 0.75 model on our evaluation data.
par(mfrow=c(2,2))
hist(fitted.values(out.5),breaks=20, main = "6.1a: Fitted HL Counts (SP=0.5)")
hist(fitted.values(out.75),breaks=20, main = "6.1b: Fitted HL Counts (SP=0.75)")
hist(fitted.values(out1),breaks=20, main = "6.1c: Fitted HL Counts (SP=1.0)")
hist(fitted.values(out1.5),breaks=20, main = "6.1d: Fitted HL Counts (SP=1.5)")

6.2: Evaluation Data
Interestingly, SPAR = 0.75 does a fairly poor job predicting on the evaluation data. To compare the MSE, which is the fit estimate for this type of model, we can see:
- SPAR 1.0 MSE = 9,235
- SPAR 0.75 MSE = 7,080
- SPAR 0.5 MSE = 2892
The bias-variance trade off favors the 0.5 model, but potentially too much at the expense of overfitting. Given this, let’s re-train our model to have a SPAR = 0.6, to see if we can get more “value” from at a SPAR value between the two.
After the re-fitting SPAR = 0.6 model on the training data, we see the histogram in Fig 6.2 below. On a descriptive basis, it’s hard to distinguish from either SPAR = 0.5 and SPAR = 0.75.
hist(fitted.values(out0.6),breaks=20, main = "6.2: Fitted HL Counts (SP=0.6) (Training Data)")

Moving back to the evaluation data, we see the fit as shown in Fig 6.2.1a-b. The MSE is 4409. While we moved SPAR = 0.6 40% of the distance between SPAR = 0.5 and 0.75, we only lost 36% of the explanatory power. Most importantly, the SPAR = 0.6 model still seems open to outliers, having predicted a max over 400 compared to just 250 in the SPAR = 0.75 evaluation. While using rough heuristics at best, it seems the trade is worth it.
hist(preds.75, breaks=20, main = "6.2.1a: Evaluated HL Counts (SP=0.75) (Evaluation Data)")

hist(preds0.6, breaks=20, main = "6.2.1b: Evaluated HL Counts (SP=0.6) (Evaluation Data)")

6.3: Test Data
We now evaluate the SPAR = 0.6 model on test date. The model fits well, with an MSE of 1546 and the histogram as seen in Fig 6.3. What’s especially interesting is that the model predicts some values to be negative. The range is: [-29.73, 586.63]. From an interpretive standpoint, we’d just expect those areas to be “very low” homelessness zones, not to imply there can actually be negative homeless people. On a more substantive, nuanced level, it’s not irrational to assume just because an observation for homeless counts over two consecutive days was zero, that there not ever homeless people in the area. In that sense, with much more granular data and a much deeper subject matter expertise, we might be able to quantify/predict how “insulated” an area is from homelessness (i.e. it’s distance below zero). Finally, it’s important to note that the model showed two outliers above 420. Looking back at our entire n=504 sample, we had six observations above 420. Given the testing data comprises 20% of the data, we’d expect at random to see 1.2 observations above 420. Our observation of 2 seems totally plausible (and would happen 20% of the time).
hist(tpreds0.6, main = "6.3: Test HL Counts (SP=0.6) (Test Data)")

Section 7: Summary and Conclusions
As can be seen in Fig 7.1 below and in the MSE score of 1546, the GAM model with a SPAR = 0.6 does a good job of fitting both the training and test data.
par(mfrow=c(3,2))
plot(out0.6,residual=T,cex=1,pch=14,col="light blue", main = "Fig 7.1: SPAR = 0.6 Fit")

We can now summarize our findings and conclusions. There are three core findings I’d like to discuss:
From a Level I perspective, we can see that the data are complex with many features. Addressing only these 504 census tracts, we cannot approach the true mean specification function, however we can observe directional and magnitude relationships empirically for these observations. For example, low Median Income shows a rise in observed homelessness when Median Income is less than $50k. After that, with fewer data points the relationship a) becomes less stable and b) flattens out. Likewise, the percentage of commercial property is fairly bumpy (but flat overall) up until 50%. Beyond 50%, homeless count observations grow rapidly, before tapering back off near 80%. Again, towards the ends of this distribution there are both a) less observations and b) significant outliers.
From a Level II perspective, which I believe we can now just barely justify, we can likely extrapolate our GAM from the n=504 census tracts to the other n=1500+ census tracts in LA and get sensible results. I believe this for a two reasons. First, our GAM model for SPAR = 0.6 performed quite well on the testing hold-out. Assuming the n=504 census tracts were genuinely selected at random, it’s likely we’d see this trend continue in the other 75% of the sample hold out. Second, our data covered a seemingly broad sample of the potential values. From Minority rates of 98% and Vacancy rates up to 43%, to homeless counts of 931 and median incomes of $200k, we seem to have a stable slice of the overall population. If any of these distributions were severely contained, I’d be much more hesitant to extend to Level II. Of course, with that said, I’d forewarn of three potential error (among countless others) that might be seen extrapolating the data to the other 75% of census tracts. First, the underlying distributions of PctIndustrial, PctCommercial, PctVacant, and Median Income are all sensitive in the tail. In PctIndustrial and PctCommercial we see significant spikes where as Median Income and PctVacant do not. It’s not hard to imagine counter-factual in either direction. Second, PctMinority is a broad statistic. We have very little understanding of its underlying composition. It may be the case that when certain minority groups comprise the vast majority of the population in that census tract, the response variable changes. We simply have no way of knowing at this point given the summary data. Third, and finally, this information is temporally isolated to one moment in time. If it were to be extended, the actual factors describing where homeless people congregate may have changed in any given area. This is the issue with only having five predictive variables – we become more sensitive to any number of the many unobserved changes.
Finally, writing this paper was fun and made me think about a lot of the core underlying assumptions I carry into problems. When I decided to get my MBA at Wharton, the goal was to come statistically fluent. It’s a joy to have climbed up the ladder and gotten in-over-my-head, and now climb back down the ladder and unpack elements of what’s happening under the covers. Thank you!
Appendix: R Code
###### first glance at data
summary(Homeless)
hist(Homeless$StreetTotal, xlab = "Homeless Count", ylab = "Frequency", main = "Fig 2.1: Histogram of LA Homelessness Counts")
##### EDA
## Find NA's and remove if sensible
which(is.na(Homeless$PropVacant))
Homeless[c(10,445,501,502),]
Homeless1 <- Homeless[-c(10,445,501,502),]
Homeless1
## PropMinority == 1.00 ??? (removed)
sum(Homeless1$PropMinority > 0.00)
sum(Homeless1$MedianIncome == 0)
which.max(Homeless1$PropMinority)
Homeless1[496,]
sum(Homeless1$PropMinority > 0.90)
sum(Homeless1$PropMinority > 0.50)
hold1 <- which(Homeless1$PropMinority > 0.50)
hold2 <- Homeless1$MedianIncome[hold1]
sort(hold2)
summary(hold2)
## Final dataset with removed NA's/outliers (and Prop converted to %)
Homeless2 <- Homeless1[-496,]
Homeless2["PctVacant"] <- Homeless2$PropVacant*100
Homeless2["PctMinority"] <- Homeless2$PropMinority*100
summary(Homeless2)
HomelessF <- Homeless2[,-c(3,4)]
summary(HomelessF)
###### Univariate Analysis
## Setup / Basic Hist
summary(Homeless2)
sum(Homeless2$StreetTotal > 83)
sort(Homeless2$StreetTotal)
hist(Homeless2$StreetTotal)
## Top 100 counts & Histogram
HomelessOver100 <- subset(Homeless2, Homeless2$StreetTotal >= 100)
hist(HomelessOver100$StreetTotal, xlab = "Homeless Count", ylab = "Frequency", main = "Fig 4.1: Histogram of LA Homelessness Counts Over 100")
sum(Homeless2$StreetTotal >= 100)
## Reviewing other variable histograms
## skewed
par(mfrow=c(2,2))
hist(Homeless2$MedianIncome, xlab = "Median Income", ylab = "Frequency", main = "Histogram of LA Median Incomes")
hist(Homeless2$PropVacant, xlab = "Proportion Vacant", ylab = "Frequency", main = "Histogram of LA Proportion Vacant (Residential)")
hist(Homeless2$PctCommercial, xlab = "Percent Commercial", ylab = "Frequency", main = "Histogram of LA % Commercial Real Esate")
hist(Homeless2$PctIndustrial, xlab = "Percent Industrial", ylab = "Frequency", main = "Histogram of LA % Industrial Real Esate")
sum(Homeless2$PropVacant < .1)/504
## normal-ish
hist(Homeless2$PropMinority, xlab = "Minorities (Proportion)", ylab = "Frequency", main = "Fig 4.3: LA Proportion Minorities")
summary(Homeless2$PropMinority)
##### Bivariate Analysis (Regression --> SPAR)
## Normal GLM regression (extracting coefficients)
attach(HomelessF)
mod.MI <- glm(StreetTotal~MedianIncome)
summary(mod.MI)
co.MI <- coef(mod.MI)
mod.Vac <- glm(StreetTotal~PctVacant)
summary(mod.Vac)
co.Vac <- coef(mod.Vac)
mod.Com <- glm(StreetTotal~PctCommercial)
summary(mod.Com)
co.Com <- coef(mod.Com)
mod.Ind <- glm(StreetTotal~PctIndustrial)
summary(mod.Ind)
co.Ind <- coef(mod.Ind)
mod.Min <- glm(StreetTotal~PctMinority)
summary(mod.Min)
co.Min <- coef(mod.Min)
sum.stats <- c(co.Com, co.Ind, co.MI, co.Min, co.Vac)
sum.stats
mod.MIbyMin<- glm(MedianIncome~PctMinority)
summary(mod.MIbyMin)
co.MIbyMin <- coef(mod.MIbyMin)
plot(MedianIncome~PctMinority, main = "Median Income by % Minority")
## SPAR for single variable models (smoothing splines)
## Smoothing Splines
library(gam)
## % Ind
par(mfrow=c(2,2))
out<-gam(StreetTotal~s(PctIndustrial, spar=.5),data=HomelessF)
plot(out, residuals=T, ylab = "Count", main = "Fig 5.2a: Homeless Count by % Industrial (spar=0.5)")
out<-gam(StreetTotal~s(PctIndustrial, spar=1.0),data=HomelessF)
plot(out, residuals=T, ylab = "Count", main = "Fig 5.2b: Homeless Count by % Industrial (spar=1.0)")
out<-gam(StreetTotal~s(PctIndustrial, spar=1.5),data=HomelessF)
plot(out, residuals=T, ylab = "Count", main = "Fig 5.2c: Homeless Count by % Industrial (spar=1.5)")
plot(StreetTotal~PctIndustrial, ylab = "Count", main = "Fig 5.2d: Homeless Count by % Industrial (linear)")
abline(a=PctIndustrial, b=StreetTotal)
## % Vacant
par(mfrow=c(2,2))
out<-gam(StreetTotal~s(PctVacant, spar=.5),data=HomelessF)
plot(out, residuals=T, ylab = "Count", main = "5.3a: Homeless Count by % Vacant (spar=0.5)")
out<-gam(StreetTotal~s(PctVacant, spar=1.0),data=HomelessF)
plot(out, residuals=T, ylab = "Count", main = "5.3b: Homeless Count by % Vacant (spar=1.0)")
out<-gam(StreetTotal~s(PctVacant, spar=1.5),data=HomelessF)
plot(out, residuals=T, ylab = "Count", main = "5.3c: Homeless Count by % Vacant (spar=1.5)")
plot(StreetTotal~PctVacant, ylab = "Count", main = "5.3d: Homeless Count by % Vacant (linear)")
abline(a=PctVacant, b=StreetTotal)
## Med Inc
par(mfrow=c(2,2))
out<-gam(StreetTotal~s(MedianIncome, spar=.5),data=HomelessF)
plot(out, residuals=T, ylab = "Count", main = "Fig 5.4a: Homeless Count by Median Inccome (spar=0.5)")
out<-gam(StreetTotal~s(MedianIncome, spar=1.0),data=HomelessF)
plot(out, residuals=T, ylab = "Count", main = "Fig 5.4b: Homeless Count by Median Inccome (spar=1.0)")
out<-gam(StreetTotal~s(MedianIncome, spar=1.5),data=HomelessF)
plot(out, residuals=T, ylab = "Count", main = "Fig 5.4c: Homeless Count by Median Inccome (spar=1.5)")
plot(StreetTotal~MedianIncome, ylab = "Count", main = "Fig 5.4d: Homeless Count by Median Income (linear)")
abline(a=MedianIncome, b=StreetTotal)
##### Multivariate Analysis (GAM)
## Creating Training/Eval/Test Split
## Set seed
set.seed(17)
## Split data
spec = c(train = .6, evaluation = .2, test = .2)
g = sample(cut(
seq(nrow(HomelessF)),
nrow(HomelessF)*cumsum(c(0,spec)),
labels = names(spec)
))
HomelessSplit = split(HomelessF, g)
sapply(HomelessSplit, nrow)/nrow(HomelessF)
## Look at summary statistics
summary(HomelessSplit$train)
hist(HomelessSplit$train$StreetTotal)
summary(HomelessSplit$evaluation)
hist(HomelessSplit$evaluation$StreetTotal)
summary(HomelessSplit$test)
hist(HomelessSplit$test$StreetTotal)
## GAM Model
## Gam library
library(gam)
## spar = 0.5
out.5<-gam(StreetTotal~s(MedianIncome,spar=.5)+s(PctCommercial,spar=.5)+
s(PctIndustrial,spar=.5)+s(PctMinority,spar=.5)+s(PctVacant,spar=.5),
family=gaussian) # Specify spar
summary(out.5)
par(mfrow=c(1,1))
plot(out.5,ask=T) # centered around the mean of the response
hist(fitted.values(out.5),breaks=20)
## spar = 0.75
out.75<-gam(StreetTotal~s(MedianIncome,spar=.75)+s(PctCommercial,spar=.75)+
s(PctIndustrial,spar=.75)+s(PctMinority,spar=.75)+s(PctVacant,spar=.75),
family=gaussian) # Specify spar
summary(out.75)
par(mfrow=c(1,1))
plot(out.75,ask=T) # centered around the mean of the response
hist(fitted.values(out.75),breaks=20)
## spar = 1.0
out1<-gam(StreetTotal~s(MedianIncome,spar=1)+s(PctCommercial,spar=1)+
s(PctIndustrial,spar=1)+s(PctMinority,spar=1)+s(PctVacant,spar=1),
family=gaussian) # Specify spar
summary(out1)
par(mfrow=c(1,1))
plot(out1,ask=T) # centered around the mean of the response
hist(fitted.values(out1),breaks=20)
## spar = 1.5
out1.5<-gam(StreetTotal~s(MedianIncome,spar=1.5)+s(PctCommercial,spar=1.5)+
s(PctIndustrial,spar=1.5)+s(PctMinority,spar=1.5)+s(PctVacant,spar=1.5),
family=gaussian) # Specify spar
summary(out1.5)
par(mfrow=c(1,1))
plot(out1.5,ask=T) # centered around the mean of the response
hist(fitted.values(out1.5),breaks=20)
par(mfrow=c(2,2))
hist(fitted.values(out.5),breaks=20)
hist(fitted.values(out.75),breaks=20)
hist(fitted.values(out1),breaks=20)
hist(fitted.values(out1.5),breaks=20)
## spar = 0.6
out0.6<-gam(StreetTotal~s(MedianIncome,spar=0.6)+s(PctCommercial,spar=0.6)+
s(PctIndustrial,spar=0.6)+s(PctMinority,spar=0.6)+s(PctVacant,spar=0.6),
family=gaussian) # Specify spar
summary(out0.6)
par(mfrow=c(1,1))
plot(out0.6,ask=T) # centered around the mean of the response
hist(fitted.values(out0.6),breaks=20)
## Evaluation Data (SPAR = 1.0)
preds.1<-predict(out1, newdata=HomelessSplit$evaluation) # get fitted values
hist(preds.1) # what does the distribution look like?
resids.1<-HomelessSplit$evaluation$StreetTotal-preds.1 # get residuals
var(resids.1) # MSE for the residuals as a measure of fit
## Evaluation Data (SPAR = 0.75)
preds.75<-predict(out.75, newdata=HomelessSplit$evaluation) # get fitted values
hist(preds.75) # what does the distribution look like?
resids.75<-HomelessSplit$evaluation$StreetTotal-preds.75 # get residuals
var(resids.75) # MSE for the residuals as a measure of fit
## Evaluation Data (SPAR = 0.5)
preds.5<-predict(out.5, newdata=HomelessSplit$evaluation) # get fitted values
hist(preds.5) # what does the distribution look like?
resids.5<-HomelessSplit$evaluation$StreetTotal-preds.5 # get residuals
var(resids.5) # MSE for the residuals as a measure of fit
## Evaluation Data (SPAR = 0.6)
preds0.6<-predict(out0.6, newdata=HomelessSplit$evaluation) # get fitted values
hist(preds0.6) # what does the distribution look like?
resids0.6<-HomelessSplit$evaluation$StreetTotal-preds0.6 # get residuals
var(resids0.6) # MSE for the residuals as a measure of fit
## Test data
## Test Data (SPAR = 0.6)
tpreds0.6<-predict(out0.6, newdata=HomelessSplit$test) # get fitted values
hist(tpreds0.6) # what does the distribution look like?
tresids0.6<-HomelessSplit$test$StreetTotal-tpreds0.6 # get residuals
var(tresids0.6) # MSE for the residuals as a measure of fit
range(tpreds0.6)
sort(tpreds0.6)
##### Summary and Conclusion
par(mfrow=c(3,2))
plot(out0.6,residual=T,cex=1,pch=14,col="light blue", main = "Fig 7.1: SPAR = 0.6 Fit")
LS0tCnRpdGxlOiAnU1RBVDk3NCBQcm9qZWN0ICMxOiBBcHBseWluZyBHQU0gdG8gSG9tZWxlc3NuZXNzIGluIExBJwpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdAogIHBkZl9kb2N1bWVudDogZGVmYXVsdAogIHdvcmRfZG9jdW1lbnQ6IGRlZmF1bHQKLS0tCiMjIyMjIEJ5OiBKZWZmcmV5IEVsbGluZ3RvbgojIyMjIyA5OjAwYW0gTW9uLVdlZCBTZWN0aW9uCiMjIyMjIDMvMS8xNwoKIyMjIDEuICBJbnRyb2R1Y3Rpb246IEFzc2Vzc2luZyBIb21lbGVzc25lc3MgaW4gTEEKUHVibGljIHBvbGljeSBpbml0aWF0aXZlcyBpbiBMb3MgQW5nZWxlcyB1c2UgZGF0YSBvbiBob21lbGVzc25lc3MgdG8gcHJvcGVybHkgZGlyZWN0IGZ1bmRpbmcgdG8gc29jaWFsIHByb2dyYW1zLiBUaGUgaW5mb3JtYXRpb24gcHJvdmlkZXMgdmFsdWFibGUgaW5zaWdodCBpbnRvIHRoZSBjb3VudHMgYW5kIGxvY2F0aW9uIG9mIGhvbWVsZXNzIHBlb3BsZSBhY3Jvc3MgTEEuIEhvd2V2ZXIsIGdhdGhlcmluZyBkYXRhIGFjcm9zcyBvdmVyIDIsMDAwIGNlbnN1cyB0cmFja3MgaW4gTEEgaXMgYnVyZGVuc29tZSBhbmQgZXhwZW5zaXZlLiBBcyBhIHJlc3VsdCwgdGhlIGNpdHkgY29sbGVjdGVkIGluZm9ybWF0aW9uIGZyb20gNTA5IG9mIGl0cyBvdmVyIDIsMDAwIGNlbnN1cyB0cmFja3MgdG8gZm9ybSB0aGUgYmFzaXMgZm9yIGEgc3RhdGlzdGljYWwgYW5hbHlzaXMgb2YgaG9tZWxlc3NuZXNzIGluIExBLiBUaGlzIGluZm9ybWF0aW9uIGNhbiBiZSBhbmFseXplZCB0byBoZWxwIGFsbG9jYXRlIHJlc291cmNlcyBlZmZpY2llbnRseSB0byB0aGUgbG9jYXRpb25zIHdoZXJlIGhvbWVsZXNzbmVzcyBpcyBtb3N0IHByZXZhbGVudC4gCgojIyMgMi4gIERhdGEgRGVzY3JpcHRpb24KVGhlIGRhdGEgd2VyZSBnZW5lcmF0ZWQgZnJvbSBhIHJhbmRvbSBzYW1wbGUgb2YgNTA5IG9mIHRoZSBvdmVyIDIsMDAwIGNlbnN1cyB0cmFjdHMgaW4gTG9zIEFuZ2VsZXMuIEZvciBvdXIgdHJlYXRtZW50LCB3ZSB3aWxsIGV4YW1pbmUgc2l4IHZhcmlhYmxlczogCgogICAgICAxLiBTdHJlZXRUb3RhbCDigJMgVGhlIG51bWJlciBvZiBob21lbGVzcyBpbmRpdmlkdWFscyBpbiBhIGNlbnN1cyB0cmFjdCAKICAgICAgMi4gTWVkaWFuSW5jb21lIOKAkyBNZWRpYW4gaG91c2Vob2xkIGluY29tZSBpbiBhIGNlbnN1cyB0cmFjdAogICAgICAzLiBQcm9wVmFjYW50IOKAkyBUaGUgcHJvcG9ydGlvbiBvZiB2YWNhbnQgcmVzaWRlbnRpYWwgZHdlbGxpbmdzIGluIGEgY2Vuc3VzIHRyYWN0CiAgICAgIDQuIFByb3BNaW5vcml0eSDigJMgVGhlIHByb3BvcnRpb24gb2YgbWlub3JpdHkgcmVzaWRlbnRzIGluIGEgY2Vuc3VzIHRyYWN0CiAgICAgIDUuIFBjdENvbW1lcmNpYWwg4oCTIFRoZSBwZXJjZW50YWdlIG9mIGEgY2Vuc3VzIHRyYWN0IHpvbmVkIGZvciBjb21tZXJjaWFsIGVudGVycHJpc2VzCiAgICAgIDYuIFBjdEluZHVzdHJpYWwg4oCTIFRoZSBwZXJjZW50YWdlIG9mIGEgY2Vuc3VzIHRyYWN0IHpvbmVkIGZvciBpbmR1c3RyaWFsIGVudGVycHJpc2VzClN0cmVldFRvdGFsIGlzIHRoZSByZXNwb25zZSB2YXJpYWJsZS4gSXQgaXMgZXhwcmVzc2VkIGFzIGEgcG9zaXRpdmUgaW50ZWdlciBnaXZlbiBpdCBpcyBjb3VudCBkYXRhLiBTdHJlZXRUb3RhbCBjb3VudHMgd2VyZSBjb2xsZWN0ZWQgaW4gdGhlIDUwOSBjZW5zdXMgdHJhY3RzLCB3aGVyZSBlYWNoIGluZGl2aWR1YWwgY291bnQgd2FzIHBlcmZvcm1lZCBvdmVyIHR3byBjb25zZWN1dGl2ZSBkYXlzL2V2ZW5pbmdzLiBUaGUgaGlzdG9ncmFtIGZvciBob21lbGVzc25lc3MgY291bnRzIGluIExBIGNhbiBiZSBzZWVuIGJlbG93IGluIEZpZy4gMi4xLiBOb3RlIHRoYXQgdGhlIGRpc3RyaWJ1dGlvbiBpcyBoZWF2aWx5IHNrZXdlZCwgd2l0aCBvdmVyIDQ1MCAoOTIlKSBjZW5zdXMgdHJhY3RzIGhhdmluZyBjb3VudHMgb2YgbGVzcyB0aGFuIDEwMCBob21lbGVzcyBwZW9wbGUgb2JzZXJ2ZWQgZHVyaW5nIHRoZSBzYW1wbGluZyBwZXJpb2QuIEhvd2V2ZXIsIG9uIHRoZSByaWdodCBleHRyZW1lLCAxMyB0cmFjdHMgaGFkIGNvdW50cyBvdmVyIDIwMC4gVGhlIGhpZ2hlc3QgY291bnQgaXMgOTMxLCB3aGljaCBpcyBvdmVyIDU4eCB0aGUgbWVkaWFuIHZhbHVlIG9mIDE2LiAKClRoZSBvdGhlciBmaXZlIHZhcmlhYmxlcyB3aWxsIGJlIGV4YW1pbmVkIGluIG11Y2ggZ3JlYXRlciBkZXRhaWwgb25jZSB0aGV5J3ZlIGJlZW4gcHJvcGVybHkgY2xlYW5lZC4gSG93ZXZlciwgaXQncyB3b3J0aCBzdGF0aW5nIHVwIGZyb250IHRoYXQgd2hpbGUgdGhlc2UgNSB2YXJpYWJsZXMgc2VlbSBpbXBvcnRhbnQgaW4gYW4gYW5hbHlzaXMgb2YgaG9tZWxlc3NuZXNzLCB0aGV5IGFyZSBmYXIgZnJvbSBjb21wbGV0ZS4gQW4gZXhwZXJ0IG9uIGhvbWVsZXNzbmVzcyBpbiBMb3MgQW5nZWxlcyBjb3VsZCBsaWtlbHkgcG9pbnQgdG8gc2V2ZXJhbCBvdGhlciBpbXBvcnRhbnQgdmFyaWFibGVzLCBsaWtlIGNyaW1lIG9yIGRydWcgdXNhZ2UvYWNjZXNzLCBub3QgdG8gbWVudGlvbiBob3cgdGhlIGRhdGEgd2VyZSBhY3R1YWxseSBjb2xsZWN0ZWQgYW5kIGJ5IHdob20uIFdlIGFsc28gaGF2ZSBubyBmcmFtZXdvcmsgZm9yIGV4aXN0aW5nIHBvbGljeSBpbiBMQSB0aGF0IG1heSBlZmZlY3QgdGhlIGFuYWx5c2lzLiBXaGF0ZXZlciBvdXIgZXN0aW1hdGUgb2YgdGhlIG1lYW4sIHdlIGNhbiBiZSBhc3N1cmVkIGl0IHdpbGwgYmUgbWlzc3BlY2lmaWVkIHdpdGhvdXQgZ3JlYXRlciBpbmZvcm1hdGlvbi4gV2l0aCB0aGF0IGNhdmVhdCBzdGF0ZWQsIHRoZXJlIGFyZSBzdGlsbCBtYW55IGF2ZW51ZXMgb2YgYW5hbHlzaXMgYXZhaWxhYmxlLiAKCmBgYHtyfQpoaXN0KEhvbWVsZXNzJFN0cmVldFRvdGFsLCB4bGFiID0gIkhvbWVsZXNzIENvdW50IiwgeWxhYiA9ICJGcmVxdWVuY3kiLCBtYWluID0gIkZpZyAyLjE6IEhpc3RvZ3JhbSBvZiBMQSBIb21lbGVzc25lc3MgQ291bnRzIikKYGBgCgojIyMgMy4gIERhdGEgQ2xlYW5pbmcKQmVmb3JlIGV4YW1pbmluZyB0aGUgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIFN0cmVldFRvdGFsIGFuZCB0aGUgb3RoZXIgZml2ZSB2YXJpYWJsZXMsIHdlIHdpbGwgZmlyc3QgbmVlZCB0byBjbGVhbiBhbmQgcHJlcGFyZSB0aGUgZGF0YS4gUmV2aWV3aW5nIHRoZSBzdW1tYXJ5IHN0YXRpc3RpY3MgZm9yIHRoZSA2IHZhcmlhYmxlcyBiZWxvdywgYSBmZXcgaXNzdWVzIGltbWVkaWF0ZWx5IHBvcCBvdXQuCmBgYHtyfQpzdW1tYXJ5KEhvbWVsZXNzKQpgYGAKRmlyc3QsIFByb3BWYWNhbnQgaGFzIDQgbWlzc2luZyB2YWx1ZXMuIExvb2tpbmcgYXQgdGhlc2UgZm91ciB2YWx1ZXMgbW9yZSBkaXJlY3RseSByZXZlYWxzIHNldmVyYWwgb3RoZXIgcXVpcmtzIGluIHRoZSBkYXRhLiBUaGUgTWVkaWFuSW5jb21lIGZvciB0aGVzZSA0IGNlbnN1cyB0cmFjdHMgaXMgemVybywgd2hpY2ggaXMgbm90IGJlbGlldmFibGUuIEluIGFkZGl0aW9uLCB0aHJlZSBvZiB0aGUgZm91ciBQcm9wTWlub3JpdHkgdmFsdWVzIGFyZSAxMDAuMDAsIGRlc3BpdGUgdGhlIGFjY2VwdGFibGUgcmFuZ2Ugb2YgdmFsdWVzIGJlaW5nIDAuMDAgdG8gMS4wMC4gVGhlIGluZm9ybWF0aW9uIG9uIHRoZXNlIGZvdXIgY2Vuc3VzIHRyYWN0cyBpcyB1bnJlbGlhYmxlLiBHaXZlbiB0aGUgcmVhc29ucyBhYm92ZSBfKiphbmQqKl8gdGhhdCB0aGUgU3RyZWV0VG90YWwgY291bnRzIGFyZSBvcmRpbmFyeSAocmFuZ2Ugb2YgNS0xMiksIHdlIHdpbGwgcmVtb3ZlIHRoZXNlIGZvdXIgY2Vuc3VzIHRyYWN0cyBmcm9tIHRoZSBhbmFseXNpcy4gTHVja2lseSwgdGhlcmUgYXJlIG5vIG90aGVyIGNhc2VzIG9mIFByb3BNaW5vcml0eSBvciBNZWRpYW5JbmNvbWUgZGlzcGxheWluZyB0aGVzZSBpc3N1ZXMgKE1laWRhbkluY29tZSA9IDAgYW5kIFByb3BNaW5vcml0eSA+IDEuMDApLiAKYGBge3J9CkhvbWVsZXNzW2MoMTAsNDQ1LDUwMSw1MDIpLF0KYGBgCkxldCdzIHJldmlldyB0aGUgdXBkYXRlZCBzdW1tYXJ5IHN0YXRpc3RpY3MgZm9yIHRoZSBuZXcgZGF0YSBzZXQgd2l0aCB0aGUgNCBmYXVsdHkgdmFsdWVzIHJlbW92ZWQgdG8gc2VlIGlmIGFueSBvdGhlciBpc3N1ZXMgYXJlIGltbWVkaWF0ZWx5IG9idmlvdXMuIFByb3BNaW5vcml0eSdzIG1heCB2YWx1ZSBvZiAxLjAwIGFuZCBQcm9wVmFjYW50L1BjdENvbW1lcmNpYWwvUGN0SW5kdXN0cmlhbCdzIGxvd2VyIGJvdW5kcyBvZiAwLjAwIGFyZSB3b3J0aCBpbnZlc3RpZ2F0aW5nLiBBIHF1aWNrIGdsYW5jZSBhdCBQcm9wVmFjYW50L1BjdENvbW1lcmNpYWwvUGN0SW5kdXN0cmlhbCdzIGxvd2VyIGJvdW5kcyBvZiAwLjAwIGRpZCBub3QgcmV2ZWFsIGFueXRoaW5nIHN5c3RlbWF0aWNhbGx5IGRpdmVyZ2VudCBvciBzdWdnZXN0IGFueXRoaW5nIHdyb25nIGluIHRoZSBkYXRhIHJlcG9ydGluZy4gSG93ZXZlciwgdGhlIGNlbnN1cyB0cmFjdCB3aXRoIFByb3BNaW5vcml0eSA9IDEuMDAgd2FzIGEgY2F1c2UgZm9yIHNrZXB0aWNpc20uCmBgYHtyfQpzdW1tYXJ5KEhvbWVsZXNzMSkKYGBgClRoZSBjZW5zdXMgdHJhY3Qgd2l0aCBQcm9wTWlub3JpdHkgPSAxLjAwIHByb3ZlcyBpbnRlcmVzdGluZyBvbiBtdWx0aXBsZSBsZXZlbHMuIEl0IGlzIGFsc28gb25lIG9mIHRocmVlIGNlbnN1cyB0cmFjdHMgd2l0aCB0aGUgaGlnaGVzdCBtZWRpYW4gaW5jb21lIChNZWRpYW5JbmNvbWUgPSAkMjAxLDAwMCkuIFdoaWxlIGl0J3MgZGlmZmljdWx0IHRvIGJlbGlldmUgdGhlIGNlbnN1cyB0cmFjdCBpcyBsaXRlcmFsbHkgMTAwJSBtaW5vcml0eSwgaXQncyBhdCBsZWFzdCB2ZXJ5IGRpcmVjdGlvbmFsbHkgcG9zc2libGUuIFRoZXJlIGFyZSAxNyBvdGhlciBjZW5zdXMgdHJhY3RzIHdpdGggdGhlIHByb3BvcnRpb24gb2YgbWlub3JpdHkgcmVzaWRlbnRzIGdyZWF0ZXIgdGhhbiA5MCUsIGluY2x1ZGluZyBhIHRyYWN0IHdpdGggOTguMSUuIFdoYXQgaXMgbGVzcyBiZWxpZXZhYmxlIGlzIHRoZSBtZWRpYW4gaW5jb21lIG9mICQyMDEsMDAwLCBnaXZlbiBhIHByb3BvcnRpb24gb2YgbWlub3JpdHkgcmVzaWRlbnRzIG9mIDEwMCUuIExvb2tpbmcgYXQgdGhlIGRhdGEsIDMwNCBvZiB0aGUgNTA1IGNlbnN1cyB0cmFjdHMgKDYwJSkgaGF2ZSBhIHByb3BvcnRpb24gb2YgbWlub3JpdHkgcmVzaWRlbnRzIG92ZXIgNTAlLiBPZiB0aGlzIHN1YnNldCwgdGhlIG1lZGlhbiBNZWRpYW5JbmNvbWUgaXMgJDMwLDgzMCBhbmQgdGhlIG1lYW4gTWVkaWFuSW5jb21lIGlzICQzMyw1NjAuIEFzaWRlIGZyb20gdGhlICQyMDEsMDAwIHZhbHVlIHVuZGVyIGV4YW1pbmF0aW9uLCB0aGVyZSBpcyBvbmUgb3RoZXIgdmFsdWUgZ3JlYXRlciB0aGFuICQxMDAsMDAwICgkMTA1LDY1MikuIEZvciB0aGVzZSByZWFzb25zLCBpdCdzIGxpa2VseSB0byBiZWxpZXZlIHRoYXQgdGhpcyB2YWx1ZSBpcyBlaXRoZXIgYW4gZXh0cmVtZSBvdXRsaWVyIHRoYXQgd2lsbCBza2V3IHRoZSByZXN0IG9mIHRoZSBhbmFseXNpcyAtb3ItIGlzIG1pc3JlcG9ydGVkLiBJbiBlaXRoZXIgY2FzZSwgaXQncyB3b3J0aCBzdHJpa2luZyBmcm9tIHRoZSBhbmFseXNpcy4gCgpPbmUgZmluYWwgYWRqdXN0bWVudCB3ZSB3aWxsIG1ha2UgaXMgdG8gY29udmVydCBQcm9wVmFjYW50IGFuZCBQcm9wTWlub3JpdHkgdG8gcGVyY2VudGFnZXMgKGxpa2UgUGN0Q29tbWVyY2lhbCBhbmQgUGN0SW5kdXN0cmlhbCkuIFRoaXMgd2lsbCBoZWxwIGNlbnRlciB1bml0cyBpbiB0aGUgc2FtZSByYW5nZSAoYXMgb3Bwb3NlZCB0byAxMDB4IGRpc3Bhcml0eSksIHdoaWNoIHdpbGwgaGVscCBmb3Igc3RhdGlzdGljYWwgcmVhc29ucy4gTW92aW5nIGZvcndhcmQgUHJvcFZhY2FudCBhbmQgUHJvcE1pbm9yaXR5IHdpbGwgYmUgc2VlbiBhcyBQY3RWYWNhbnQgYW5kIFBjdE1pbm9yaXR5LgoKQXQgdGhpcyBwb2ludCwgYWZ0ZXIgcmVtb3ZpbmcgTkEncyBhbmQgbG9va2luZyBmb3IgdW5yZWFzb25hYmxlIHZhbHVlcywgdGhlIGRhdGEgc2VlbSBjbGVhbiBhbmQgcmVhZHkgZm9yIGFuYWx5c2lzLgoKIyMjIDQuICBBbmFseXNpcyBvZiBVbml2YXJpYXRlIFN0YXRpc3RpY3MKUXVpY2tseSBsb29raW5nIGFnYWluIHdpdGggb3VyIHJlc3BvbnNlIHZhcmlhYmxlLCBTdHJlZXRUb3RhbCwgd2Ugc2VlIGEgc2tld2VkIGRpc3RyaWJ1dGlvbi4gVGhlIG1lZGlhbiBjb3VudCBvZiBvYnNlcnZlZCBob21lbGVzcyBwZW9wbGUgaXMgMTYuIFRoZSA5MHRoIHBlcmNlbnRpbGUgaXMgYSBjb3VudCBvZiA4My4gWWV0LCB0aGUgdG9wIDUgYXJlOiA0ODAsIDU5NSwgNjU1LCA2NjYsIDkzMS4gRnJvbSBhIExldmVsIEkgcGVyc3BlY3RpdmUsIHdlIGNhbiBzdGF0ZSB0aGF0IGJhc2VkIG9uIHRoZSBvYnNlcnZlZCBjb3VudHMsIGhvbWVsZXNzbmVzcyBpcyBub3QgZXZlbmx5IGRpc3RyaWJ1dGVkIGFjcm9zcyB0aGUgNTA0IGNlbnN1cyB0cmFjdHMuIEluc3RlYWQsIGhvbWVsZXNzbmVzcyBjbHVtcHMgaW4gc3BlY2lmaWMgYXJlYXMgYXMgRmlnIDQuMSBoZWxwcyB2aXN1YWxpemUgYmVsb3cuIE5vdGU6IEZpZyA0LjEgb25seSBzaG93cyB0aGUgdG9wIDQwIGNvdW50cyAoOCUgb2YgYWxsIGNlbnN1cyB0cmFjdHMpLgpgYGB7cn0KaGlzdChIb21lbGVzc092ZXIxMDAkU3RyZWV0VG90YWwsIHhsYWIgPSAiSG9tZWxlc3MgQ291bnQiLCB5bGFiID0gIkZyZXF1ZW5jeSIsIG1haW4gPSAiRmlnIDQuMTogSGlzdG9ncmFtIG9mIExBIEhvbWVsZXNzbmVzcyBDb3VudHMgT3ZlciAxMDAiKQpgYGAKQmFzZWQgb24gYW4gaW50dWl0aXZlIGZpcnN0IHBhc3MsIGF0IGxlYXN0IGZvciBhbnlvbmUgd2hvJ3Mgc3BlbnQgdGltZSBpbiBMQSwgd2Ugc2hvdWxkbid0IGJlIHRvbyBzdXJwcmlzZWQgYnkgdGhlc2UgZmluZGluZ3MuIEl0J3MgZmFpcmx5IHJhcmUgdG8gc2VlIGhvbWVsZXNzIHBlb3BsZSBpbiBhZmZsdWVudCBzdWJ1cmJhbiBuZWlnaGJvcmhvb2RzIGFuZCBmYWlybHkgY29tbW9uIHRvIHNlZSBob21lbGVzcyBwZW9wbGUgaW4gZWl0aGVyIGRlbnNlciBwYXJ0cyBvZiB0aGUgY2l0eSB3aGVyZSBwZW9wbGUgY2x1c3RlciBfKipvcioqXyBpbiBpbmR1c3RyaWFsIHBhcnRzIG9mIHRoZSBjaXR5IHdoZXJlIGhvbWVsZXNzIHBlb3BsZSBhcmUgbGVzcyBsaWtlbHkgdG8gYmUgYm90aGVyZWQuIEJlZm9yZSB3ZSBjYW4gZ2VuZXJhbGl6ZSBmcm9tIHRoaXMgYmFzaWMgaGV1cmlzdGljLCB3ZSBzaG91bGQgbG9vayBhdCB0aGUgb3RoZXIgdmFyaWFibGVzIHRvIHNlZSBob3cgdGhleSBhcmUgZGlzdHJpYnV0ZWQuIEZyb20gdGhlcmUsIHdlIGNhbiBidWlsZCBhbiB1bmRlcnN0YW5kaW5nIG9mIGhvdyBMQSBpcyBjb25zdHJ1Y3RlZCBhcyBhIGNpdHkgaW4gdGVybXMgb2YgcmFjaWFsLCBpbmNvbWUsIGFuZCBwcm9wZXJ0eSB0eXBlIGRpc3RyaWJ1dGlvbnMuCgpMb29raW5nIGF0IHRoZSBvdGhlciBwYXJhbWV0ZXJzLCBmb3VyIG9mIHRoZSBmaXZlIHZhcmlhYmxlcyBhbHNvIGZvcm0gaGVhdmlseSBza2V3ZWQgZGlzdHJpYnV0aW9ucyBhcyBjYW4gYmUgc2VlbiBiZWxvdyBpbiBGaWcgNC4yYS1kLiBDb21tb25zZW5zZSBzYXlzIHRoYXQgbWVkaWFuIGluY29tZSB3aWxsIGZvbGxvdyBhIHNrZXdlZCBvciBsb2dhcml0aG1pYyBkaXN0cmlidXRpb24uIEl0J3MgYWxzbyBub3Qgc3VycHJpc2luZyB0aGF0IDkyJSBvZiBjZW5zdXMgdHJhY3RzIGhhdmUgcmVzaWRlbnRpYWwgdmFjYW5jeSBsZXNzIHRoYW4gMTAlIGdpdmVuIExBIGlzIGEgZGVzaXJhYmxlIHBsYWNlIHRvIGxpdmUuIFRoZSBkaXN0cmlidXRpb25zIG9mIGNvbW1lcmNpYWwgYW5kIGluZHVzdHJpYWwgcmVhbCBlc3RhdGUgcGVyY2VudGFnZXMgYWxzbyByZWZsZWN0IG91ciBnZW5lcmFsIGV4cGVyaWVuY2VzOiBNb3N0IGFyZWFzIGluIGEgbWFqb3IgbWV0cm9wb2xpcyBhcmUgbm90IGluZHVzdHJpYWwsIGJ1dCBhIGZldyBhcmVhcyBhcmUgdmVyeSBpbmR1c3RyaWFsLiBMaWtld2lzZSwgY29tbWVyY2lhbCBhbmQgcmVzaWRlbnRpYWwgdGVuZCB0byBibGVuZCB0b2dldGhlciBtb3JlLCBnaXZlbiBwZW9wbGUgbGlrZSB0byBzaG9wIG5lYXIgd2hlcmUgdGhleSBsaXZlIGZvciBjb252ZW5pZW5jZS4gQWdhaW4sIG5vdGhpbmcgaGVyZSBpcyBtZWFudCB0byBpbXBseSBjYXVzYWxpdHk7IGluc3RlYWQsIHdlIGFyZSBzaW1wbHkgc3RhdGluZyB0aGF0IHRoZSBkaXN0cmlidXRpb25zIGxvb2sgcmVhc29uYWJsZSB0byBhbiBpbnRlbGxpZ2VudCBwZXJzb24gd2hvIGhhcyBzcGVudCB0aW1lIGluIExBLiBUaGF0IHNhaWQsIGF0IHRoaXMgcG9pbnQsIHRoZSBhbmFseXNpcyBpcyB0cmVuZGluZyB0b3dhcmQgTGV2ZWwgSUksIGdpdmVuIHdlIGFyZSBleHRyYXBvbGF0aW5nIGZyb20gdGhlIHNwZWNpZmljIG9ic2VydmF0aW9ucyB3aXRoaW4gdGhlc2UgNTA0IExBIGNlbnN1cyB0cmFjdHMgdG8gaW5mb3JtIGEgYnJvYWRlciBwaWN0dXJlIG9mIExBIGFzIGEgY2l0eS4gR2l2ZW4gdGhlIHNoYXBlIG9mIHRoZSBkaXN0cmlidXRpb25zLCB0aGlzIGZlZWxzIHJlYXNvbmFibGUuCmBgYHtyfQpwYXIobWZyb3c9YygyLDIpKQpoaXN0KEhvbWVsZXNzRiRNZWRpYW5JbmNvbWUsIHhsYWIgPSAiTWVkaWFuIEluY29tZSAoJCkiLCB5bGFiID0gIkZyZXF1ZW5jeSIsIG1haW4gPSAiRmlnLiA0LjJhOiBMQSBNZWRpYW4gSW5jb21lcyIpCmhpc3QoSG9tZWxlc3NGJFBjdFZhY2FudCwgeGxhYiA9ICJWYWNhbmN5ICglKSIsIHlsYWIgPSAiRnJlcXVlbmN5IiwgbWFpbiA9ICJGaWcuIDQuMmI6IExBICUgVmFjYW50IChSZXNpZGVudGlhbCkiKQpoaXN0KEhvbWVsZXNzRiRQY3RDb21tZXJjaWFsLCB4bGFiID0gIkNvbW1lcmNpYWwgKCUpIiwgeWxhYiA9ICJGcmVxdWVuY3kiLCBtYWluID0gIkZpZy4gNC4yYzogTEEgJSBDb21tZXJjaWFsIFJlYWwgRXNhdGUiKQpoaXN0KEhvbWVsZXNzRiRQY3RJbmR1c3RyaWFsLCB4bGFiID0gIkluZHVzdHJpYWwgKCUpIiwgeWxhYiA9ICJGcmVxdWVuY3kiLCBtYWluID0gIkZpZy4gNC4yZDogTEEgJSBJbmR1c3RyaWFsIFJlYWwgRXNhdGUiKQpgYGAKCkxvb2tpbmcgYXQgZGlzdHJpYnV0aW9uIG9mIHRoZSBwZXJjZW50YWdlIG9mIHJlc2lkZW50cyB0aGF0IGFyZSBtaW5vcml0aWVzIHdpdGhpbiBhIGdpdmVuIGNlbnN1cyB0cmFjdCwgd2Ugc2VlIGEgdmVyeSBkaWZmZXJlbnQgcGljdHVyZS4gVGhlIGRpc3RyaWJ1dGlvbiBpcyBmYWlybHkgbm9ybWFsLCB3aXRoIGEgc2xpZ2h0bHkgZmF0IHRhaWwgYnVpbHQgdXAgb24gdGhlIGxlZnQgc2lkZSAoRmlnIDQuMykuIFRoZSBtZWFuIGFuZCBtZWRpYW4gYXJlIDUzJSBhbmQgNTYlLCByZXNwZWN0aXZlbHksIGFuZCByZWZsZWN0IGEgZGl2ZXJzZSBwb3B1bGF0aW9uLiBMb29raW5nIGF0IFdpa2lwZWRpYSdzIGRlbW9ncmFwaGljIGluZm9ybWF0aW9uIG9uIExBIGZvciB0aGUgMjAxMCBjZW5zdXMsIFdoaXRlcyBtYWRlIHVwIDI5JSwgTGF0aW5vcyA0OSUsIEJsYWNrcyAxMCUsIGFuZCBBc2lhbnMgMTElIChodHRwczovL2VuLldpa2lwZWRpYS5vcmcvd2lraS9Mb3NfQW5nZWxlcyNEZW1vZ3JhcGhpY3MpLiBXaGF0IG91ciBzcGVjaWZpYyBjZW5zdXMgZGF0YSBvbiB0aGUgNTA0IHRyYWN0cyByZWZsZWN0cyBpcyB0aGF0IG1vc3QgaW5kaXZpZHVhbCBjZW5zdXMgdHJhY3RzIGFyZSBjb21wcmlzZWQgb2YgYSBkaXZlcnNlIG1peCBvZiBwZW9wbGUuIEFnYWluLCBvbiBhIExldmVsIEkgYW5hbHlzaXMsIHdlIHNlZSBoZXRlcm9nZW5laXR5IGFtb25nIF8qKmFuZCoqXyB3aXRoaW4gdGhlIGNlbnN1cyB0cmFjdHMuIFB1c2hpbmcgdG8gTGV2ZWwgSUksIHRoZSBXaWtpcGVkaWEgaW5mb3JtYXRpb24gY29ycm9ib3JhdGVzIHRoZXNlIGZpbmRpbmdzIGZvciBMQSBhcyBhIHdob2xlLgpgYGB7cn0KaGlzdChIb21lbGVzc0YkUGN0TWlub3JpdHksIHhsYWIgPSAiTWlub3JpdGllcyAoJSkiLCB5bGFiID0gIkZyZXF1ZW5jeSIsIG1haW4gPSAiRmlnIDQuMzogTEEgUGVyY2VudGFnZSBNaW5vcml0aWVzIikgCmBgYAoKIyMjIDUuICBBbmFseXNpcyBvZiBCaXZhcmlhdGUgU3RhdGlzdGljcwpOb3cgdGhhdCB3ZSBoYXZlIGEgZmlybSB1bmRlcnN0YW5kaW5nIG9mIHRoZSBkYXRhLCBpdCdzIHRpbWUgdG8gZXhwbG9yZSBob3cgb3VyIHJlc3BvbnNlLCBTdHJlZXRUb3RhbCwgaW50ZXJhY3RzIHdpdGggdGhlIG90aGVyIHZhcmlhYmxlcy4KCkZpcnN0LCB3ZSB3aWxsIHNldCBhIGJhc2VsaW5lIHVzaW5nIHNpbXBsZSBsaW5lYXIgcmVncmVzc2lvbi4gRm9yIHRoaXMsIHdlIHdpbGwgcmVtYWluIHN0cmljdGx5IGluIExldmVsIEkgYW5hbHlzaXMgYW5kIG5vdCBzcGxpdCBvdXIgZGF0YSBpbiBhbnl3YXkuIFRoZSBnb2FsIGhlcmUgaXMgbWVyZWx5IHRvIHVuZGVyc3RhbmQgdGhlIGRpcmVjdGlvbiBhbmQgbWFnbml0dWRlIG9mIGVmZmVjdCBlYWNoIHZhcmlhYmxlIGhhcyBvbiB0aGUgcmVzcG9uc2UuIEluIHRoZSBjb2RlIG91dHB1dCBiZWxvdywgd2Ugc2VlIHRoZSBpbnRlcmNlcHQgYW5kIGxpbmVhciByZWdyZXNzaW9uIGNvZWZmaWNpZW50IGZvciBlYWNoIHZhcmlhYmxlIGFnYWluc3QgU3RyZWV0VG90YWwgKGluZGVwZW5kZW50bHkgYXMgc2luZ2xlIHZhcmlhYmxlIHJlZ3Jlc3Npb25zKS4KYGBge3J9CmNvLkNvbQpjby5JbmQKY28uTUkKY28uTWluCmNvLlZhYwpgYGAKVGhlc2UgY29lZmZpY2llbnRzIHN0YXRlIHRoZSBmb2xsb3dpbmcgYWJvdXQgdGhlIHJlbGF0aW9uc2hpcCB3aXRoIGVhY2ggdmFyaWFibGUgYW5kIFN0cmVldFRvdGFsLCBmb3IgXyoqb25seSoqXyB0aGVzZSA1MDQgY2Vuc3VzIHRyYWN0cy4gQWdhaW4sIHRvIHJlaXRlcmF0ZSwgdGhpcyBpbiBub3QgaW1wbHlpbmcgY2F1c2FsaXR5IG9yIHN0YXRpc3RpY2FsIHNpZ25pZmljYW5jZSwgbWVyZWx5IGEgZGVzY3JpcHRpb24gb2YgdGhlIGRhdGE6CgoqIFBjdENvbW1lcmNpYWw6IEZvciBldmVyeSAxJSBwb2ludCBpbmNyZWFzZSBpbiBwZXJjZW50IG9mIGNvbW1lcmNpYWwgbGFuZCBpbiBhIGdpdmVuIGNlbnN1cyB0cmFjdCwgdGhlIGNvdW50IG9mIGhvbWVsZXNzIG9ic2VydmF0aW9ucyBpbmNyZWFzZXMgYnkgMC45MwogIAoqIFBjdEluZHVzdHJpYWw6IEZvciBldmVyeSAxJSBwb2ludCBpbmNyZWFzZSBpbiBwZXJjZW50IG9mIGluZHVzdHJpYWwgbGFuZCBpbiBhIGdpdmVuIGNlbnN1cyB0cmFjdCwgdGhlIGNvdW50IG9mIGhvbWVsZXNzIG9ic2VydmF0aW9ucyBpbmNyZWFzZXMgYnkgMS4xNwogIAoqIE1lZGlhbkluY29tZTogRm9yIGV2ZXJ5ICQxIGluY3JlYXNlIGluIHRoZSBtZWRpYW4gaW5jb21lIGluIGEgZ2l2ZW4gY2Vuc3VzIHRyYWN0LCB0aGUgY291bnQgb2YgaG9tZWxlc3Mgb2JzZXJ2YXRpb25zIGRlY3JlYXNlcyBieSAwLjAwMDc2LiBUcmFuc2xhdGVkLCBmb3IgZXZlcnkgaW5jcmVhc2Ugb2YgJDEsMDAwLCB0aGUgY291bnQgb2YgaG9tZWxlc3Mgb2JzZXJ2YXRpb25zIGRlY3JlYXNlcyBieSAwLjc2CiAgCiogUGN0TWlub3JpdHk6IEZvciBldmVyeSAxJSBwb2ludCBpbmNyZWFzZSBpbiBwZXJjZW50IG9mIHRoZSByZXNpZGVudHMgdGhhdCBhcmUgbWlub3JpdGllcyBpbiBhIGdpdmVuIGNlbnN1cyB0cmFjdCwgdGhlIGNvdW50IG9mIGhvbWVsZXNzIG9ic2VydmF0aW9ucyBpbmNyZWFzZXMgYnkgMC41MgogIAoqIFBjdFZhY2FudDogRm9yIGV2ZXJ5IDElIHBvaW50IGluY3JlYXNlIGluIHBlcmNlbnQgb2YgcmVzaWRlbnRpYWwgcHJvcGVydHkgdGhhdCBpcyB2YWNhbnQgaW4gYSBnaXZlbiBjZW5zdXMgdHJhY3QsIHRoZSBjb3VudCBvZiBob21lbGVzcyBvYnNlcnZhdGlvbnMgaW5jcmVhc2VzIGJ5IDUuNTkKClJldmlld2luZyB0aGVzZSBkZXNjcmlwdGlvbnMsIGl0J3MgZWFzeSB0byBzZWUgaG93IHNvbWUgb2YgdGhlc2UgbWF5IGdlbmVyYWxpemUuIE9uIHRoZSB3aG9sZSwgd2UnZCBpbnR1aXRpdmVseSBleHBlY3QgdG8gZmluZCBoaWdoZXIgY291bnRzIG9mIGhvbWVsZXNzIHBlb3BsZSBpbiBwbGFjZXMgd2l0aCBpbmR1c3RyaWFsIG9yIHZhY2FudCBwcm9wZXJ0eSwgd2hpY2ggdGhlIGRhdGEgcmVmbGVjdC4gQWRkaXRpb25hbGx5LCB3ZSdkIGV4cGVjdCB0byBzZWUgaG9tZWxlc3NuZXNzIGNvdW50cyBkcm9wIGluIG1vcmUgYWZmbHVlbnQgYXJlYXMsIHdoaWNoIGFnYWluIHRoZSBkYXRhIHJlZmxlY3QuIFRoZSBwZXJjZW50IG9mIG1pbm9yaXR5IHJlc2lkZW50cyBoYXMgdGhlIHdlYWtlc3QgZWZmZWN0IChhbW9uZyB0aGUgZm91ciBkaXJlY3RseSBjb21wYXJhYmxlIHBlcmNlbnQgdmFsdWVzKSwgd2hpY2ggZmVlbHMgcmlnaHQgZ2l2ZW4gdGhhdCB3ZSBvYnNlcnZlZCBMQSBpcyBhIGRpdmVyc2UgY29tbXVuaXR5LCBib3RoIHdpdGhpbiBhbmQgYW1vbmcgY2Vuc3VzIHRyYWN0cy4gVGhlIHNsaWdodGx5IHBvc2l0aXZlIGVmZmVjdCBsaWtlbHkgc3RlbXMgZnJvbSB0aGUgcmVsYXRpb25zaGlwIHdlIG9ic2VydmVkIGVhcmxpZXIgYmV0d2VlbiBwZXJjZW50IG1pbm9yaXR5IGFuZCBtZWRpYW4gaW5jb21lLiBXZSBsb29rIGF0IHRoaXMgcmVsYXRpb25zaGlwIGFnYWluLCBwbG90dGVkIGJlbG93IGFzIEZpZyA1LjEuIFRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBNZWRpYW4gSW5jb21lIGFuZCAlIE1pbm9yaXR5IGlzIHdlYWssIGJ1dCBub3RpY2VhYmxlLiBBZ2FpbiwgYWxsIG9mIHRoZSBhbmFseXNpcyBkaXJlY3RseSBhYm92ZSBzaG91bGQgYmUgc2VlbiBhcyBhIExldmVsIEkgZGVzY3JpcHRpb24gb2YgdGhlIGRhdGEgKHdpdGggdGhlIG9jY2FzaW9uYWwgc2FuaXR5IGNoZWNrKSwgbm90aGluZyBtb3JlLgoKYGBge3J9CnBsb3QoTWVkaWFuSW5jb21lflBjdE1pbm9yaXR5LCBtYWluID0gIkZpZy4gNS4xOiBNZWRpYW4gSW5jb21lIGJ5ICUgTWlub3JpdHkiKQpgYGAKCk5leHQsIHdlIGxvb2sgYXQgYW4gYXBwbGljYXRpb24gb2Ygc21vb3RoaW5nIHNwbGluZXMgdG8gdGhlIGRhdGEgdG8gbW92ZSBiZXlvbmQgYmFzaWMgbGluZWFyIG1ldGhvZHMuIFRoZSBpZGVhIGhlcmUgaXMgdGhhdCB3ZSBhcmUgbG9va2luZyBhdCBhIGhpZ2hseSBza2V3ZWQgcmVzcG9uc2UgZGlzdHJpYnV0aW9uIChhcyB3ZWxsIGFzIGhpZ2hseSBza2V3ZWQgcHJlZGljdGlvbiB2YXJpYWJsZXMpLiBHaXZlbiB0aGlzLCB3ZSBkb24ndCBhbnRpY2lwYXRlIGEgbGluZWFyIG1vZGVsIHdpbGwgYmVzdCBmaXQgdGhlIGRhdGEuIEluc3RlYWQgb2YgZm9yY2luZyBhIGxpbmVhciBlcXVhdGlvbiBvbnRvIHRoZSBkYXRhLCB3ZSBjYW4gaW5zdGVhZCB1c2luZyBzbW9vdGhpbmcgc3BsaW5lcyB0byBpZGVudGlmeSBrbm90cyAodGhpbmsgb2YgdGhlbSBhcyBpbmZsZWN0aW9uIHBvaW50cykgaW4gdGhlIGRhdGEuIFRoZSB0ZWNobmljYWwgbWF0aCBpcyBiZXlvbmQgdGhlIHNjb3BlIG9mIHRoaXMgcGFwZXIgKGFuZCBteSBhYmlsaXRpZXMhKSwgYnV0IHRoZSBrZXkgY29uY2VwdCBpcyB0aGF0IGZvciBhbnkgZ2l2ZW4gcmFuZ2Ugb2YgWCwgd2Ugd2lsbCBhcHBseSBhIGdpdmVuIGZ1bmN0aW9uLCBGKHgpIHRoYXQgaXNuJ3QgY29uc3RyYWluZWQgdG8gYmVpbmcgbGluZWFyLiBXaGVuIHRoZXJlIGlzIHN1ZmZpY2llbnQgbW92ZW1lbnQgaW4gdGhlIGRhdGEgYXdheSBmcm9tIEYoeCksIHdlIHdpbGwgaW50cm9kdWNlIGEga25vdCwgYW5kIHRoZW4gZGVzY3JpYmUgYSBuZXcgRih4KSBvdmVyIGEgbmV3IHJhbmdlIG9mIHguIEFzIHRoZSBudW1iZXIgb2Yga25vdHMgaW5jcmVhc2VzLCB3ZSBiZWNvbWUgcHJvbmUgdG8gb3ZlciBmaXR0aW5nIHRoZSBkYXRhLiBUbyBjb21iYXQgdGhpcywgd2UgY2FuIHNlZ21lbnQgdGhlIGRhdGEgaW50byB0cmFpbmluZywgZXZhbHVhdGlvbiwgYW5kIHRlc3Qgc2FtcGxlcy4gRm9yIHRoZSBzYWtlIG9mIHNwZWVkLCB3ZSB3aWxsIHNpbXBseSB1c2UgdGhlIGRlZmF1bHQgbWV0aG9kIGhlcmUgb2YgbGVhdmUtb25lLW91dCBjcm9zcyB2YWxpZGF0aW9uLCB3aGljaCByb3VnaGx5IGFwcHJveGltYXRlcyB0aGUgcmVzdWx0cyB5b3UnZCB0eXBpY2FsbHkgc2VlIGluIGEgdHJhaW5pbmcsIGV2YWx1YXRpb24sIGFuZCB0ZXN0IHNwbGl0LgoKVGhlIHRocmVlIHZhcmlhYmxlcyB3ZSB3aWxsIGV4YW1pbmUgYXJlOiBQY3RJbmR1c3RyaWFsIChGaWcgNS4yYS1kKSwgUGN0VmFjYW50IChGaWcgNS4zYS1kKSwgYW5kIE1lZGlhbkluY29tZSAoRmlnIDUuNGEtZCkuIFdlIHdpbGwgZXhhbWluZSBmb3VyIHVuZGVybHlpbmcgY29uY2VwdHMgaW4gdW5pdmFyaWF0ZSBhbmFseXNpcy4gRWFjaCB0aG91Z2h0IHdpbGwgYmUgdGllZCB0byBhIHNwZWNpZmljIHBsb3QgdG8gaGVscCBlbHVjaWRhdGUgdGhlIHBvaW50OgoKMS4gV2UgYXJlIHN0aWxsIGluIExldmVsIDEgYW5hbHlzaXMuIEFueSBhdHRlbXB0IHRvIGludGVycHJldCBjb2VmZmljaWVudHMsIGRlZ3JlZXMgb2YgZnJlZWRvbSwgb3IgbW9kZWwgZml0IGlzIGZ1dGlsZS4gVGhlcmUgYXJlIHRvbyBtYW55IHN0YXRpc3RpY2FsIHByb2Nlc3NlcyBvY2N1cnJpbmcgdW5kZXIgdGhlIGhvb2QgdG8gaW5mZXIgY2F1c2FsaXR5IG9yIHN0YXRpc3RpY2FsIHNpZ25pZmljYW50LiBUaGUgY3VydmVzIHNpbXBseSByZWZsZWN0IHRoYXQgZGF0YSBhcyBwcmVzZW50ZWQgYW1vbmcgdGhlIDUwNCByYW5kb20gY2Vuc3VzIHRyYWN0IHNhbXBsZXMuIChOb3RlOiBHZW5lcmFsLCBjb3VsZCBiZSBhcHBsaWVkIHRvIGFsbCBwbG90cy4pCgoyLiBXaGVuIFNQQVIgaXMgc21hbGwgKDAuNSwgaW4gdGhpcyBjYXNlKSwgdGhlIGZpdHMgYXJlIGV4dHJlbWVseSBidW1weSBhbmQgaGVhdmlseSBpbmZsdWVuY2VkIGJ5IG91dGxpZXJzLiBGb3IgZXhhbXBsZSwgaW4gZ3JhcGggNS4yYTogSEwgQ291bnQgYnkgJSBJbmR1c3QuKHNwYXI9MC41KSwgdGhlIHNoYXJwIGJ1bXAgYXQgUGN0SW5kdXN0cmlhbCA9IDIwJSBkb2VzIG5vdCBpbXBseSBzb21lIG1hZ2ljIGluY3JlYXNlIGluIGhvbWVsZXNzbmVzcyBvY2N1cnMgdGhlbi4gSXQgc2ltcGx5IG1lYW5zIHRoYXQgdGhlIGxhcmdlIG91dGxpZXJzIGluIHRoZSBjb3VudCBhcmUgcHVsbGluZyB0aGUgZnVuY3Rpb24gdXAgYXQgdGhhdCBtb21lbnQuIFRvIGJlIGFibGUgdG8gZXh0ZW5kIHRoaXMgdG8gTGV2ZWwgSUkgYW5hbHlzaXMsIHdlJ2QgbmVlZCBhIERFRVAgc3ViamVjdCBtYXR0ZXIgZXhwZXJ0LCB3aG8gdGhlbiBjb3VsZCBtYXliZSBpbmZlciB0aGF0IHRoZXJlIGFyZSBzcGVjaWZpYyB6b25pbmcgbGF3cy9iZW5lZml0IHByb2dyYW1zIHRpZWQgdG8gem9uaW5nIGxhd3MgaW4gTEEgdGhhdCBoYXZlIGFuIGFydGlmaWNpYWwgY2xpZmYgYXQgMjAlLiBCdXQgZm9yIHVzIHRvIGRyYXcgdGhpcyB0eXBlIG9mIGNvbmNsdXNpb24gZnJvbSB0aGUgZGF0YSBhcyBwcmVzZW50ZWQgd291bGQgYmUgaXJyZXNwb25zaWJsZS4KCjMuIFRoZSBkaXN0cmlidXRpb24gb2YgdGhlIHVuZGVybHlpbmcgdmFyaWFibGVzIGJlaW5nIGFuYWx5emVkIG1hdHRlcnMuIFRvIGV4cGFuZCBvbiB0aGlzLCBsZXQncyBjb21wYXJlIHR3byBwbG90czogNS4yYjogSEwgQ291bnQgYnkgJSBJbmR1c3QuKHNwYXI9MS4wKSAqKiYqKiA1LjRiOiBITCBDb3VudCBieSBNZWQgSW5jb21lIChzcHI9MS4wKS4gRXZlbiB3aGVuIFNQQVIgPSAxLjAgKHRoZW9yZXRpY2FsbHkgdGhlIG1heCBwZW5hbHR5IGZvciBTUEFSOyBlc3NlbnRpYWxseSB0aGUgYWxnb3JpdGhtIGlzIHRyeWluZyB0byBhZGQgYXMgZmV3IGtub3RzIGFzIHBvc3NpYmxlKSwgaW4gNS4yYiB3ZSBzZWUgYSBsYXJnZSBzcGlrZSBhdCBQb3N0aW5kdXN0cmlhbCA9IDY1JSBiZWZvcmUgYSBzaGFycCBkZWNsaW5lIGF0IFBjdEluZHVzdHJpYWwgPSA3NSUuIEJlY2F1c2UgdGhlcmUgYXJlIHNvIGZldyBvYnNlcnZhdGlvbnMgYXQgdGhlIHRhaWwgZW5kIG9mIHRoaXMgZGlzdHJpYnV0aW9uICg0IG9mIDUwNCBhYm92ZSA2MCUsIGkuZS4gPDElKSwgdGhlIG1vZGVsIHdpbGwgbGlrZWx5IG92ZXJmaXQuIEluIGNvbnRyYXN0LCBpbiBmaWd1cmUgNS40Yiwgd2Ugc2VlIGEgdmVyeSAibGluZWFyIiBsb29raW5nIHJlbGF0aW9uc2hpcCBhbGwgdGhlIHdheSBvdXQgdG8gbWVkaWFuIGluY29tZSBvZiAkMjAxLDAwMC4gSG93ZXZlciwgd2hpbGUgaG9tZWxlc3NuZXNzIGRvZXMgc2VlbSB0byBkZWNsaW5lIHdpdGggaW5jcmVhc2luZyBtZWRpYW4sIHRoZSBicm9hZGVyIHBvaW50IGlzIHRoYXQgdGhlIGRhdGEgaW4gdGhhdCB0YWlsIGlzIGlycmVsZXZhbnQgZ2l2ZW4gaXQgaGFzIHNvIGxpdHRsZSBzdXBwb3J0LiBXZSBjb3VsZCBsaWtlbHkgZmluZCBhIGRpc3RyaWN0IGluIHRoZSBCYXkgQXJlYSAobXkgaG9tZSBjaXR5KSB0aGF0IGhhcyBhIHNreSBoaWdoIG1lZGlhbiBpbmNvbWUgYW5kIGFsc28gYSBoaWdoIGxldmVsIG9mIGhvbWVsZXNzbmVzczogRG93bnRvd24gUGFsbyBBbHRvLiBUaGUgcG9pbnQgaXMgZXZlbiB3aXRoIGFkdmFuY2VkIG1vZGVscywgZG8gbm90IHBsYWNlIHRvbyBtdWNoIHdlaWdodCBpbiBhbnkgZXhwbGFuYXRpb24gdGhhdCByZWxpZXMgb24gd2Vha2x5IHN1cHBvcnRlZCBkYXRhLiBXaGVuIGRlYWxpbmcgd2l0aCBpbmNvbWUsIHRoaXMgd2lsbCBhbG1vc3QgYWx3YXlzIGJlIHRoZSBjYXNlIGdpdmVuIGl0J3Mgb2Z0ZW4gc2tld2VkIHVuZGVybHlpbmcgZGlzdHJpYnV0aW9uLgoKNC4gTGFzdGx5LCB3ZSBkaXNjdXNzIHRoZSBCaWFzLVZhcmlhbmNlIHRyYWRlIG9mZiBieSBsb29raW5nIGF0IGhpZ2ggdmFsdWVzIG9mIFNQQVIgKFNQQVIgPSAxLjUpIGFzIGFuIGFwcHJveGltYXRpb24gb2YgYSBsaW5lYXIgbW9kZWwuIFRoZSBzaW1wbGUgY29uY2VwdHVhbCBwb2ludCBhYm91dCB0aGUgYWxnb3JpdGhtIGlzOiB3aXRoIGVub3VnaCBwZW5hbHR5IHBsYWNlZCBvbiBlYWNoIGFkZGl0aW9uYWwga25vdCwgdGhlIG1vZGVsIHdpbGwgZXZlbnR1YWxseSBhcHByb3hpbWF0ZSBhIGxpbmVhciBtb2RlbC4gSW4gcHJhY3RpY2UsIHdlIHdhbnQgdG8ga2VlcCBTUEFSIDw9IDEuMC4gSG93ZXZlciwgdG8gZWx1Y2lkYXRlIHRoZSB1bmRlcmx5aW5nIG1lY2hhbmlzbXMgb2YgdGhlIHRvb2wsIGNvbXBhcmUgZmlndXJlczogNS4yYzogSEwgQ291bnQgYnkgJSBJbmR1c3QuKHNwYXI9MS41KSAqKiYqKiA1LjJkOiBITCBDb3VudCBieSAlIEluZHVzdC4obGluZWFyKS4gVGhlIGxpbmVzIGFyZSBuZWFybHkgaW5kaXN0aW5ndWlzaGFibGUuIFdoYXQgaXMgc28gaW1wb3J0YW50IHRvIGdlbmVyYWxpemUgaXM6IHRoaXMgaGlnaGxpZ2h0cyB0aGUgbGFyZ2VyIGFuZCBpbmNyZWRpYmx5IGltcG9ydGFudCBvdmVyYWxsIEJpYXMtVmFyaWFuY2UgdHJhZGUgb2ZmLiBXaGVuIFNQQVIgaXMgbG93LCBpdCBiZXR0ZXIgYXBwcm94aW1hdGVzIHRoZSBkYXRhIGFzIHByZXNlbnRlZC4gV2hlbiBTUEFSIGlzIHN1ZmZpY2llbnRseSBoaWdoLCBpdCBhcHByb3hpbWF0ZXMgYSBsaW5lYXIgbW9kZWwuIEluIHRoaXMgY2FzZSwgd2Uga25vdyB0aGUgbG93IHZhbHVlIG9mIFNQQVIgaXMgb3ZlcmZpdHRpbmcgdG8gdGhlIGRhdGEgaW4gYW4gYXR0ZW1wdCB0byBjYXB0dXJlIGFsbCBvZiB0aGUgdmFyaWFuY2UuIFdoZW4gU1BBUiBpcyBoaWdoLCBpdCBzYWNyaWZpY2VzIHRyeWluZyB0byBleHBsYWluIGFsbCB0aGUgdmFyaWFuY2UgYW5kIGluc3RlYWQgYmlhc2VzIGl0cyByZXN1bHRzIHRvd2FyZCB0aGUgb3ZlcmFsbCB0cmVuZC4gVG8gc2VlIHRoaXMgdmVyeSBjbGVhcmx5LCBsb29rIGF0IHRoZSBzcGlrZSB2cy4gbm9uLXNwaWtlIGF0IFBjdEluZHVzdHJpYWwgPSA2NSUgaW4gNS4yYSB2cy4gNS4yZC4gQXQgU1BBUiA9IDEuNSwgdGhlIGFsZ29yaXRobSBpZ25vcmVzIHRoZSBsYXJnZSBvdXRsaWVycyB0byBwcmVzZXJ2ZSB0aGUgb3ZlcmFsbCB0cmVuZC4gCgpgYGB7cn0KcGFyKG1mcm93PWMoMiwyKSkKb3V0PC1nYW0oU3RyZWV0VG90YWx+cyhQY3RJbmR1c3RyaWFsLCBzcGFyPS41KSxkYXRhPUhvbWVsZXNzRikKcGxvdChvdXQsIHJlc2lkdWFscz1ULCB5bGFiID0gIkNvdW50IiwgbWFpbiA9ICI1LjJhOiBITCBDb3VudCBieSAlIEluZHVzdC4oc3Bhcj0wLjUpIikKb3V0PC1nYW0oU3RyZWV0VG90YWx+cyhQY3RJbmR1c3RyaWFsLCBzcGFyPTEuMCksZGF0YT1Ib21lbGVzc0YpCnBsb3Qob3V0LCByZXNpZHVhbHM9VCwgeWxhYiA9ICJDb3VudCIsIG1haW4gPSAiNS4yYjogSEwgQ291bnQgYnkgJSBJbmR1c3QuKHNwYXI9MS4wKSIpCm91dDwtZ2FtKFN0cmVldFRvdGFsfnMoUGN0SW5kdXN0cmlhbCwgc3Bhcj0xLjUpLGRhdGE9SG9tZWxlc3NGKQpwbG90KG91dCwgcmVzaWR1YWxzPVQsIHlsYWIgPSAiQ291bnQiLCBtYWluID0gIjUuMmM6IEhMIENvdW50IGJ5ICUgSW5kdXN0LihzcGFyPTEuNSkiKQpwbG90KFN0cmVldFRvdGFsflBjdEluZHVzdHJpYWwsIHlsYWIgPSAiQ291bnQiLCBtYWluID0gIjUuMmQ6IEhMIENvdW50IGJ5ICUgSW5kdXN0LihsaW5lYXIpIikKYWJsaW5lKGE9UGN0SW5kdXN0cmlhbCwgYj1TdHJlZXRUb3RhbCkKCnBhcihtZnJvdz1jKDIsMikpCm91dDwtZ2FtKFN0cmVldFRvdGFsfnMoUGN0VmFjYW50LCBzcGFyPS41KSxkYXRhPUhvbWVsZXNzRikKcGxvdChvdXQsIHJlc2lkdWFscz1ULCB5bGFiID0gIkNvdW50IiwgbWFpbiA9ICI1LjNhOiBITCBDb3VudCBieSAlIFZhY2FudCAoc3Bhcj0wLjUpIikKb3V0PC1nYW0oU3RyZWV0VG90YWx+cyhQY3RWYWNhbnQsIHNwYXI9MS4wKSxkYXRhPUhvbWVsZXNzRikKcGxvdChvdXQsIHJlc2lkdWFscz1ULCB5bGFiID0gIkNvdW50IiwgbWFpbiA9ICI1LjNiOiBITCBDb3VudCBieSAlIFZhY2FudCAoc3Bhcj0xLjApIikKb3V0PC1nYW0oU3RyZWV0VG90YWx+cyhQY3RWYWNhbnQsIHNwYXI9MS41KSxkYXRhPUhvbWVsZXNzRikKcGxvdChvdXQsIHJlc2lkdWFscz1ULCB5bGFiID0gIkNvdW50IiwgbWFpbiA9ICI1LjNjOiBITCBDb3VudCBieSAlIFZhY2FudCAoc3Bhcj0xLjUpIikKcGxvdChTdHJlZXRUb3RhbH5QY3RWYWNhbnQsIHlsYWIgPSAiQ291bnQiLCBtYWluID0gIjUuM2Q6IEhMIENvdW50IGJ5ICUgVmFjYW50IChsaW5lYXIpIikKYWJsaW5lKGE9UGN0VmFjYW50LCBiPVN0cmVldFRvdGFsKQoKcGFyKG1mcm93PWMoMiwyKSkKb3V0PC1nYW0oU3RyZWV0VG90YWx+cyhNZWRpYW5JbmNvbWUsIHNwYXI9LjUpLGRhdGE9SG9tZWxlc3NGKQpwbG90KG91dCwgcmVzaWR1YWxzPVQsIHlsYWIgPSAiQ291bnQiLCBtYWluID0gIjUuNGE6IEhMIENvdW50IGJ5IE1lZCBJbmNvbWUgKHNwcj0wLjUpIikKb3V0PC1nYW0oU3RyZWV0VG90YWx+cyhNZWRpYW5JbmNvbWUsIHNwYXI9MS4wKSxkYXRhPUhvbWVsZXNzRikKcGxvdChvdXQsIHJlc2lkdWFscz1ULCB5bGFiID0gIkNvdW50IiwgbWFpbiA9ICI1LjRiOiBITCBDb3VudCBieSBNZWQgSW5jb21lIChzcHI9MS4wKSIpCm91dDwtZ2FtKFN0cmVldFRvdGFsfnMoTWVkaWFuSW5jb21lLCBzcGFyPTEuNSksZGF0YT1Ib21lbGVzc0YpCnBsb3Qob3V0LCByZXNpZHVhbHM9VCwgeWxhYiA9ICJDb3VudCIsIG1haW4gPSAiNS40YzogSEwgQ291bnQgYnkgTWVkIEluY29tZSAoc3ByPTEuNSkiKQpwbG90KFN0cmVldFRvdGFsfk1lZGlhbkluY29tZSwgeWxhYiA9ICJDb3VudCIsIG1haW4gPSAiNS40ZDogSEwgQ291bnQgYnkgTWVkIEluY29tZSAobGluZWFyKSIpCmFibGluZShhPU1lZGlhbkluY29tZSwgYj1TdHJlZXRUb3RhbCkKYGBgCgojIyMgNi4gIEFuYWx5c2lzIG9mIE11bHRpdmFyaWF0ZSBTdGF0aXN0aWNzCkZpbmFsbHksIHdlIGdldCB0byB0aGUgR2VuZXJhbCBBZGRpdGl2ZSBNb2RlbCAoR0FNKTogb3VyIHByaW5jaXBsZSB0b3BpYyBvZiBpbnF1aXJ5LiBCZWZvcmUgd2Ugc3RhcnQsIGl0J3MgaW1wb3J0YW50IHRvIGxheSBvdXQgYSBmZXcgc3RhdGlzdGljYWwgY29uY2VwdHMuIEZpcnN0LCBhcyBub3RlZCBpbiAqU2VjdGlvbiAyOiBEYXRhIERlc2NyaXB0aW9uKiwgb3VyIGVzdGltYXRlIHVzaW5nIEdBTSB3aWxsIGxhY2sgbWFueSBwb3RlbnRpYWxseSBleHBsYW5hdG9yeSB2YXJpYWJsZXMuIEluIHNob3J0LCB3ZSBoYXZlIHRoZSA1IHByZWRpY3RvcnMgd2UndmUgb2JzZXJ2ZWQsIGJ1dCB0aGVyZSBpcyBhIGxvdCBtb3JlIGluZm9ybWF0aW9uIHJlcXVpcmVkIHRvIHNwZWNpZnkgdGhlIHRydWUgbWVhbiBmdW5jdGlvbi4gR2l2ZW4gdGhhdCwgd2Ugd2lsbCBzdGlsbCBiZSBPSyBtb3ZpbmcgYWhlYWQgYXQgTGV2ZWwgSSBhbmQgcG90ZW50aWFsbHkgTGV2ZWwgSUkgYW5hbHlzaXMsIGJ1dCB3ZSB3aWxsIG5vdCBiZSBhYmxlIHRvIGRyYXcgY29uY2x1c2lvbnMgYXQgTGV2ZWwgSUlJLiBTZWNvbmQsIGluICpTZWN0aW9uIDUuICBBbmFseXNpcyBvZiBCaXZhcmlhdGUgU3RhdGlzdGljcyosIHdlIHBvaW50ZWQgb3V0IHRoYXQgaXQncyB1bndpc2UgdG8gcHV0IHNpZ25pZmljYW50IHdlaWdodCBvbiBhbnkgZXhwbGFuYXRpb24gdGhhdCBpcyB3ZWFrbHkgc3VwcG9ydGVkIGJ5IGZldyBvYnNlcnZhdGlvbnMuIFdlIHNhdyB0aGF0IGl0IHRvb2sgYSBTUEFSID0gMS41IHRvICJpZ25vcmUiIHRoZSBsZXZlcmFnZSBwb2ludHMgaW4gKio1LjJjOiBITCBDb3VudCBieSAlIEluZHVzdC4oc3Bhcj0xLjUpKiouIFdlbGwsIHdpdGggR0FNIHdlIGFyZSBhZGRpbmcgbW9yZSB2YXJpYWJsZXMgdG8gdGhlIG1vZGVscyBhbmQgaGVuY2UgY3JlYXRpbmcgaHlwZXIgZGltZW5zaW9uYWwgc3BhY2UuIEFzIGEgcmVzdWx0LCBhbnkgb2JzZXJ2YXRpb24gaW4gbi1kaW1lbnNpb25hbCBzcGFjZSB3aWxsIGJlIGxlc3Mgc3VwcG9ydGVkIHRoYW4gaXQgd2FzIGluIDJEIHNwYWNlLiBCb3RoIHJlc3BvbnNlIG91dGxpZXJzIGFuZCBzcGFyc2UgcGF0Y2hlcyBvZiBkYXRhIHdpbGwgaGF2ZSB0byBiZSBleGFtaW5lZCBjYXV0aW91c2x5LiBGaW5hbGx5LCB3ZSB3aWxsIGNvbXBsZXRlbHkgZGVwYXJ0IGZyb20gdGhlIGlkZWEgb2YgcmVncmVzc2lvbiBjb2VmZmljaWVudHMgd2hlbiB1c2luZyBHQU0uIFRoZSB0cmFuc2Zvcm1hdGlvbnMgaW52b2x2ZWQgaW4gY3JlYXRpbmcgdGhlIG1vZGVsIGFyZSBzdWZmaWNpZW50bHkgY29tcGxleCB0aGF0IGFueSBpbnRlcnByZXRhdGlvbiBvZiB0aGUgY29lZmZpY2llbnRzIHdvdWxkIGRlcGFydCBmcm9tIHJlYWxpdHkuCgpUbyBldmFsdWF0ZSBvdXIgbW9kZWwsIHdlIHdpbGwgc3BsaXQgdGhlIGRhdGEgaW50byB0aHJlZSBidWNrZXRzOiB0cmFpbmluZyBkYXRhICg2MCUpLCBldmFsdWF0aW9uIGRhdGEgKDIwJSksIGFuZCB0ZXN0IGRhdGEgKDIwJSkuIFRoaXMgYWxsb3dzIHVzIHRvIGZpdCBhIG1vZGVsIGJhc2VkIG9uIHRyYWluaW5nIGRhdGEsIHR3ZWFrIHBhcmFtZXRlcnMgdmlhIGV2YWx1YXRpb24gZGF0YSAoc29tZSBzbm9vcGluZyksIGFuZCB0aGVuIHRlc3QgdGhlIG1vZGVsJ3MgZml0IG9uIHRoZSB0ZXN0IGRhdGEuIFRoaXMgZnJhbWV3b3JrIGlzIGEgZ29vZCB3YXkgdG8gcHJldmVudCBvdmVyZml0dGluZy4gT25lIGNydWNpYWwgcG9pbnQgdG8gZW1waGFzaXplOiB3aXRoIG91ciByZWxhdGl2ZWx5IHNtYWxsIHNhbXBsZSBzZXQgKG49NTA0KSBhbmQgZXhpc3RlbmNlIG9mIGJvdGggbWFzc2l2ZSBvdXRsaWVycyBpbiB0aGUgcmVzcG9uc2UgYW5kIGhpZ2hseSBza2V3ZWQgdW5kZXJseWluZyBkaXN0cmlidXRpb25zIGZvciBmb3VyIG9mIHRoZSBmaXZlIHByZWRpY3RvcnMsIHdlIHdpbGwgaGF2ZSB0byB0aGluayBjcml0aWNhbGx5IGFib3V0IGhvdyB0byBleHRyYXBvbGF0ZSByZXN1bHRzIGFuZCBleHRyYSBiZXdhcmUgb2Ygb3ZlcmZpdHRpbmcuIAoKQmFzZWQgb24gbXkgaW5pdGlhbCBzcGxpdCwgdGhlIGRhdGEgbG9vayByZWFzb25hYmx5IHN0YWJsZS4gVGhlcmUgYXJlIGxhcmdlIG91dGxpZXJzIGZvciBTdHJlZXRUb3RhbCBhbW9uZyBhbGwgdGhyZWUgc3BsaXRzLiBJbiBhZGRpdGlvbiwgdGhlIG1lZGlhbnMsIG1pbnMsIGFuZCBtYXhzIGFjcm9zcyB0aGUgcHJlZGljdGl2ZSB2YXJpYWJsZXMgYXJlIHRvbGVyYWJseSBjb25zaXN0ZW50LiBQY3RJbmR1c3RyaWFsJ3MgNCBvdXRsaWVycyBhYm92ZSA2MCUgbWF5IGNhdXNlIGlzc3Vlcy4gTGVzcyBub3RpY2VhYmxlLCBNZWRpYW5JbmNvbWUgYW5kIFBjdENvbW1lcmNpYWwgbWF5IGV4aGliaXQgc2xpZ2h0IGJpYXMgZ2l2ZW4gb3V0bGllciB2YWx1ZXMuIE92ZXJhbGwsIGhvd2V2ZXIsIHRoZSBzcGxpdCBzZWVtcyByZWFzb25hYmxlIGFuZCBpbiB0aGUgc3Bpcml0IG9mIGEgcmFuZG9tIHNwbGl0LCB3ZSB3aWxsIG1vdmUgZm9yd2FyZC4KCiMjIyMgNi4xOiBUZXN0IERhdGEKQWZ0ZXIgYnVpbGRpbmcgdGhlIEdBTSBtb2RlbCwgd2UgbG9vayBhdCB0aGUgZml0dGVkIEhvbWVsZXNzIENvdW50cyBvbiB0aGUgdHJhaW5pbmcgZGF0YSBmb3IgdmFyeWluZyBsZXZlbHMgb2YgU1BBUiAoMC41LCAwLjc1LCAxLjAsIDEuNSkuIEZpZ3VyZSA2LjFhLWQgZGlzcGxheXMgdGhlc2UgaGlzdG9ncmFtcyBiZWxvdy4gRnJvbSBvdXIgdGhvcm91Z2ggYW5hbHlzaXMgYmVmb3JlLCB3ZSBrbm93IHRoYXQgU1BBUiA9IDAuNSBpcyBoaWdobHkgc2Vuc2l0aXZlIGFuZCBwcm9uZSB0byBvdmVyZml0LiBJbiBjb250cmFzdCwgU1BBUiA9IDEuNSBhcHByb2FjaGVzIGEgbGluZWFyIG1vZGVsLCB3aGljaCBpcyByZWZsZWN0ZWQgaW4gdGhlIChuZWFybHkpIG5vcm1hbCBkaXN0cmlidXRpb24gcHJvZHVjZWQgaW4gNi4xZC4gTG9va2luZyBhdCB0aGUgdW5kZXJseWluZyBmaXRzIGZvciBTUEFSID0gMS41IG9uIGFuIGluZGl2aWR1YWwgZWxlbWVudCBiYXNpcywgdGhlIHRyZW5kIHRvIGFwcHJveGltYXRlIGEgbGluZWFyIG1vZGVsIGNvbnRpbnVlcy4gU1BBUiA9IDAuNzUgYW5kIDEuMCBhcmUgdmVyeSBoYXJkIHRvIGRpc3Rpbmd1aXNoLiBPbiBhIG1vcmUgc3Vic3RhbnRpdmUgbGV2ZWwsIGhvdyBTUEFSID0gMC43NSBhbmQgMS4wIGRpZmZlciBpcyBpbnRlcmVzdGluZy4gU1BBUiA9IDAuNzUgKEZpZy4gNi4xYikgbGVhdmVzIG1vcmUgZGF0YSBpbiB0aGUgdGFpbHMgKGJvdGggYXQgemVybyBhbmQgdGhlIGZhciByaWdodCkuIEZyb20gYSBwb2xpY3kgbGV2ZWwsIDAgaG9tZWxlc3MgdnMuIDw1IGhvbWVsZXNzIHBlb3BsZSBpcyBsaWtlbHkgbm90IHRoZSBjb3JlIGNvbmNlcm4uIEhvd2V2ZXIsIGFyZWFzIHdpdGggbWFzc2l2ZSBob21lbGVzcyBjb3VudHMgYXJlIGxpa2VseSAqKnZlcnkqKiBpbnRlcmVzdGluZyB0byB0aGUgcG9saWN5IG1ha2Vycy4gR2l2ZW4gdGhlc2UgZmFjdG9ycywgbGV0J3MgZXJyb3Igb24gdGhlIHNpZGUgb2YgYmV0dGVyIGZpdCBhbmQgcHJvY2VlZCB3aXRoIHRoZSBldmFsdWF0aW5nIHRoZSBTUEFSID0gMC43NSBtb2RlbCBvbiBvdXIgZXZhbHVhdGlvbiBkYXRhLiAKCmBgYHtyfQpwYXIobWZyb3c9YygyLDIpKQpoaXN0KGZpdHRlZC52YWx1ZXMob3V0LjUpLGJyZWFrcz0yMCwgbWFpbiA9ICI2LjFhOiBGaXR0ZWQgSEwgQ291bnRzIChTUD0wLjUpIikgCmhpc3QoZml0dGVkLnZhbHVlcyhvdXQuNzUpLGJyZWFrcz0yMCwgbWFpbiA9ICI2LjFiOiBGaXR0ZWQgSEwgQ291bnRzIChTUD0wLjc1KSIpIApoaXN0KGZpdHRlZC52YWx1ZXMob3V0MSksYnJlYWtzPTIwLCBtYWluID0gIjYuMWM6IEZpdHRlZCBITCBDb3VudHMgKFNQPTEuMCkiKSAKaGlzdChmaXR0ZWQudmFsdWVzKG91dDEuNSksYnJlYWtzPTIwLCBtYWluID0gIjYuMWQ6IEZpdHRlZCBITCBDb3VudHMgKFNQPTEuNSkiKSAKYGBgCgojIyMjIDYuMjogRXZhbHVhdGlvbiBEYXRhCkludGVyZXN0aW5nbHksIFNQQVIgPSAwLjc1IGRvZXMgYSBmYWlybHkgcG9vciBqb2IgcHJlZGljdGluZyBvbiB0aGUgZXZhbHVhdGlvbiBkYXRhLiBUbyBjb21wYXJlIHRoZSBNU0UsIHdoaWNoIGlzIHRoZSBmaXQgZXN0aW1hdGUgZm9yIHRoaXMgdHlwZSBvZiBtb2RlbCwgd2UgY2FuIHNlZToKCiogU1BBUiAxLjAgTVNFID0gOSwyMzUKKiBTUEFSIDAuNzUgTVNFID0gNywwODAKKiBTUEFSIDAuNSBNU0UgPSAyODkyCgpUaGUgYmlhcy12YXJpYW5jZSB0cmFkZSBvZmYgZmF2b3JzIHRoZSAwLjUgbW9kZWwsIGJ1dCBwb3RlbnRpYWxseSB0b28gbXVjaCBhdCB0aGUgZXhwZW5zZSBvZiBvdmVyZml0dGluZy4gR2l2ZW4gdGhpcywgbGV0J3MgcmUtdHJhaW4gb3VyIG1vZGVsIHRvIGhhdmUgYSBTUEFSID0gMC42LCB0byBzZWUgaWYgd2UgY2FuIGdldCBtb3JlICJ2YWx1ZSIgZnJvbSBhdCBhIFNQQVIgdmFsdWUgYmV0d2VlbiB0aGUgdHdvLgoKQWZ0ZXIgdGhlIHJlLWZpdHRpbmcgU1BBUiA9IDAuNiBtb2RlbCBvbiB0aGUgdHJhaW5pbmcgZGF0YSwgd2Ugc2VlIHRoZSBoaXN0b2dyYW0gaW4gRmlnIDYuMiBiZWxvdy4gT24gYSBkZXNjcmlwdGl2ZSBiYXNpcywgaXQncyBoYXJkIHRvIGRpc3Rpbmd1aXNoIGZyb20gZWl0aGVyIFNQQVIgPSAwLjUgYW5kIFNQQVIgPSAwLjc1LgpgYGB7cn0KaGlzdChmaXR0ZWQudmFsdWVzKG91dDAuNiksYnJlYWtzPTIwLCBtYWluID0gIjYuMjogRml0dGVkIEhMIENvdW50cyAoU1A9MC42KSAoVHJhaW5pbmcgRGF0YSkiKSAKYGBgCgpNb3ZpbmcgYmFjayB0byB0aGUgZXZhbHVhdGlvbiBkYXRhLCB3ZSBzZWUgdGhlIGZpdCBhcyBzaG93biBpbiBGaWcgNi4yLjFhLWIuIFRoZSBNU0UgaXMgNDQwOS4gV2hpbGUgd2UgbW92ZWQgU1BBUiA9IDAuNiA0MCUgb2YgdGhlIGRpc3RhbmNlIGJldHdlZW4gU1BBUiA9IDAuNSBhbmQgMC43NSwgd2Ugb25seSBsb3N0IDM2JSBvZiB0aGUgZXhwbGFuYXRvcnkgcG93ZXIuIE1vc3QgaW1wb3J0YW50bHksIHRoZSBTUEFSID0gMC42IG1vZGVsIHN0aWxsIHNlZW1zICpvcGVuKiB0byBvdXRsaWVycywgaGF2aW5nIHByZWRpY3RlZCBhIG1heCBvdmVyIDQwMCBjb21wYXJlZCB0byBqdXN0IDI1MCBpbiB0aGUgU1BBUiA9IDAuNzUgZXZhbHVhdGlvbi4gV2hpbGUgdXNpbmcgcm91Z2ggaGV1cmlzdGljcyAqYXQgYmVzdCosIGl0IHNlZW1zIHRoZSB0cmFkZSBpcyB3b3J0aCBpdC4KCmBgYHtyfQpoaXN0KHByZWRzLjc1LCBicmVha3M9MjAsIG1haW4gPSAiNi4yLjFhOiBFdmFsdWF0ZWQgSEwgQ291bnRzIChTUD0wLjc1KSAoRXZhbHVhdGlvbiBEYXRhKSIpCmhpc3QocHJlZHMwLjYsIGJyZWFrcz0yMCwgbWFpbiA9ICI2LjIuMWI6IEV2YWx1YXRlZCBITCBDb3VudHMgKFNQPTAuNikgKEV2YWx1YXRpb24gRGF0YSkiKQpgYGAKCiMjIyMgNi4zOiBUZXN0IERhdGEKV2Ugbm93IGV2YWx1YXRlIHRoZSBTUEFSID0gMC42IG1vZGVsIG9uIHRlc3QgZGF0ZS4gVGhlIG1vZGVsIGZpdHMgd2VsbCwgd2l0aCBhbiBNU0Ugb2YgMTU0NiBhbmQgdGhlIGhpc3RvZ3JhbSBhcyBzZWVuIGluIEZpZyA2LjMuIFdoYXQncyBlc3BlY2lhbGx5IGludGVyZXN0aW5nIGlzIHRoYXQgdGhlIG1vZGVsIHByZWRpY3RzIHNvbWUgdmFsdWVzIHRvIGJlIG5lZ2F0aXZlLiBUaGUgcmFuZ2UgaXM6IFstMjkuNzMsIDU4Ni42M10uIEZyb20gYW4gaW50ZXJwcmV0aXZlIHN0YW5kcG9pbnQsIHdlJ2QganVzdCBleHBlY3QgdGhvc2UgYXJlYXMgdG8gYmUgInZlcnkgbG93IiBob21lbGVzc25lc3Mgem9uZXMsIG5vdCB0byBpbXBseSB0aGVyZSBjYW4gYWN0dWFsbHkgYmUgbmVnYXRpdmUgaG9tZWxlc3MgcGVvcGxlLiBPbiBhIG1vcmUgc3Vic3RhbnRpdmUsIG51YW5jZWQgbGV2ZWwsIGl0J3Mgbm90IGlycmF0aW9uYWwgdG8gYXNzdW1lIGp1c3QgYmVjYXVzZSBhbiBvYnNlcnZhdGlvbiBmb3IgaG9tZWxlc3MgY291bnRzIG92ZXIgdHdvIGNvbnNlY3V0aXZlIGRheXMgd2FzIHplcm8sIHRoYXQgdGhlcmUgbm90ICpldmVyKiBob21lbGVzcyBwZW9wbGUgaW4gdGhlIGFyZWEuIEluIHRoYXQgc2Vuc2UsIHdpdGggbXVjaCBtb3JlIGdyYW51bGFyIGRhdGEgYW5kIGEgbXVjaCBkZWVwZXIgc3ViamVjdCBtYXR0ZXIgZXhwZXJ0aXNlLCB3ZSBtaWdodCBiZSBhYmxlIHRvIHF1YW50aWZ5L3ByZWRpY3QgaG93ICJpbnN1bGF0ZWQiIGFuIGFyZWEgaXMgZnJvbSBob21lbGVzc25lc3MgKGkuZS4gaXQncyBkaXN0YW5jZSBiZWxvdyB6ZXJvKS4gRmluYWxseSwgaXQncyBpbXBvcnRhbnQgdG8gbm90ZSB0aGF0IHRoZSBtb2RlbCBzaG93ZWQgdHdvIG91dGxpZXJzIGFib3ZlIDQyMC4gTG9va2luZyBiYWNrIGF0IG91ciBlbnRpcmUgbj01MDQgc2FtcGxlLCB3ZSBoYWQgc2l4IG9ic2VydmF0aW9ucyBhYm92ZSA0MjAuIEdpdmVuIHRoZSB0ZXN0aW5nIGRhdGEgY29tcHJpc2VzIDIwJSBvZiB0aGUgZGF0YSwgd2UnZCBleHBlY3QgYXQgcmFuZG9tIHRvIHNlZSAxLjIgb2JzZXJ2YXRpb25zIGFib3ZlIDQyMC4gT3VyIG9ic2VydmF0aW9uIG9mIDIgc2VlbXMgdG90YWxseSBwbGF1c2libGUgKGFuZCB3b3VsZCBoYXBwZW4gMjAlIG9mIHRoZSB0aW1lKS4KYGBge3J9Cmhpc3QodHByZWRzMC42LCBtYWluID0gIjYuMzogVGVzdCBITCBDb3VudHMgKFNQPTAuNikgKFRlc3QgRGF0YSkiKQpgYGAKCiMjIyBTZWN0aW9uIDc6IFN1bW1hcnkgYW5kIENvbmNsdXNpb25zCkFzIGNhbiBiZSBzZWVuIGluIEZpZyA3LjEgYmVsb3cgYW5kIGluIHRoZSBNU0Ugc2NvcmUgb2YgMTU0NiwgdGhlIEdBTSBtb2RlbCB3aXRoIGEgU1BBUiA9IDAuNiBkb2VzIGEgZ29vZCBqb2Igb2YgZml0dGluZyBib3RoIHRoZSB0cmFpbmluZyBhbmQgdGVzdCBkYXRhLiAKYGBge3J9CnBhcihtZnJvdz1jKDMsMikpCnBsb3Qob3V0MC42LHJlc2lkdWFsPVQsY2V4PTEscGNoPTE0LGNvbD0ibGlnaHQgYmx1ZSIsIG1haW4gPSAiRmlnIDcuMTogU1BBUiA9IDAuNiBGaXQiKQpgYGAKCldlIGNhbiBub3cgc3VtbWFyaXplIG91ciBmaW5kaW5ncyBhbmQgY29uY2x1c2lvbnMuIFRoZXJlIGFyZSB0aHJlZSBjb3JlIGZpbmRpbmdzIEknZCBsaWtlIHRvIGRpc2N1c3M6CgoxLiBGcm9tIGEgTGV2ZWwgSSBwZXJzcGVjdGl2ZSwgd2UgY2FuIHNlZSB0aGF0IHRoZSBkYXRhIGFyZSBjb21wbGV4IHdpdGggbWFueSBmZWF0dXJlcy4gQWRkcmVzc2luZyBvbmx5IHRoZXNlIDUwNCBjZW5zdXMgdHJhY3RzLCB3ZSBjYW5ub3QgYXBwcm9hY2ggdGhlIHRydWUgbWVhbiBzcGVjaWZpY2F0aW9uIGZ1bmN0aW9uLCBob3dldmVyIHdlIGNhbiBvYnNlcnZlIGRpcmVjdGlvbmFsIGFuZCBtYWduaXR1ZGUgcmVsYXRpb25zaGlwcyBlbXBpcmljYWxseSBmb3IgdGhlc2Ugb2JzZXJ2YXRpb25zLiBGb3IgZXhhbXBsZSwgbG93IE1lZGlhbiBJbmNvbWUgc2hvd3MgYSByaXNlIGluIG9ic2VydmVkIGhvbWVsZXNzbmVzcyB3aGVuIE1lZGlhbiBJbmNvbWUgaXMgbGVzcyB0aGFuICQ1MGsuIEFmdGVyIHRoYXQsIHdpdGggZmV3ZXIgZGF0YSBwb2ludHMgdGhlIHJlbGF0aW9uc2hpcCBhKSBiZWNvbWVzIGxlc3Mgc3RhYmxlIGFuZCBiKSBmbGF0dGVucyBvdXQuIExpa2V3aXNlLCB0aGUgcGVyY2VudGFnZSBvZiBjb21tZXJjaWFsIHByb3BlcnR5IGlzIGZhaXJseSBidW1weSAoYnV0IGZsYXQgb3ZlcmFsbCkgdXAgdW50aWwgNTAlLiBCZXlvbmQgNTAlLCBob21lbGVzcyBjb3VudCBvYnNlcnZhdGlvbnMgZ3JvdyByYXBpZGx5LCBiZWZvcmUgdGFwZXJpbmcgYmFjayBvZmYgbmVhciA4MCUuIEFnYWluLCB0b3dhcmRzIHRoZSBlbmRzIG9mIHRoaXMgZGlzdHJpYnV0aW9uIHRoZXJlIGFyZSBib3RoIGEpIGxlc3Mgb2JzZXJ2YXRpb25zIGFuZCBiKSBzaWduaWZpY2FudCBvdXRsaWVycy4gCgoyLiBGcm9tIGEgTGV2ZWwgSUkgcGVyc3BlY3RpdmUsIHdoaWNoIEkgYmVsaWV2ZSB3ZSBjYW4gbm93IGp1c3QgYmFyZWx5IGp1c3RpZnksIHdlIGNhbiBsaWtlbHkgZXh0cmFwb2xhdGUgb3VyIEdBTSBmcm9tIHRoZSBuPTUwNCBjZW5zdXMgdHJhY3RzIHRvIHRoZSBvdGhlciBuPTE1MDArIGNlbnN1cyB0cmFjdHMgaW4gTEEgYW5kIGdldCBzZW5zaWJsZSByZXN1bHRzLiBJIGJlbGlldmUgdGhpcyBmb3IgYSB0d28gcmVhc29ucy4gRmlyc3QsIG91ciBHQU0gbW9kZWwgZm9yIFNQQVIgPSAwLjYgcGVyZm9ybWVkIHF1aXRlIHdlbGwgb24gdGhlIHRlc3RpbmcgaG9sZC1vdXQuIEFzc3VtaW5nIHRoZSBuPTUwNCBjZW5zdXMgdHJhY3RzIHdlcmUgZ2VudWluZWx5IHNlbGVjdGVkIGF0IHJhbmRvbSwgaXQncyBsaWtlbHkgd2UnZCBzZWUgdGhpcyB0cmVuZCBjb250aW51ZSBpbiB0aGUgb3RoZXIgNzUlIG9mIHRoZSBzYW1wbGUgaG9sZCBvdXQuIFNlY29uZCwgb3VyIGRhdGEgY292ZXJlZCBhIHNlZW1pbmdseSBicm9hZCBzYW1wbGUgb2YgdGhlIHBvdGVudGlhbCB2YWx1ZXMuIEZyb20gTWlub3JpdHkgcmF0ZXMgb2YgOTglIGFuZCBWYWNhbmN5IHJhdGVzIHVwIHRvIDQzJSwgdG8gaG9tZWxlc3MgY291bnRzIG9mIDkzMSBhbmQgbWVkaWFuIGluY29tZXMgb2YgJDIwMGssIHdlIHNlZW0gdG8gaGF2ZSBhIHN0YWJsZSBzbGljZSBvZiB0aGUgb3ZlcmFsbCBwb3B1bGF0aW9uLiBJZiBhbnkgb2YgdGhlc2UgZGlzdHJpYnV0aW9ucyB3ZXJlIHNldmVyZWx5IGNvbnRhaW5lZCwgSSdkIGJlIG11Y2ggbW9yZSBoZXNpdGFudCB0byBleHRlbmQgdG8gTGV2ZWwgSUkuIE9mIGNvdXJzZSwgd2l0aCB0aGF0IHNhaWQsIEknZCBmb3Jld2FybiBvZiB0aHJlZSBwb3RlbnRpYWwgZXJyb3IgKGFtb25nIGNvdW50bGVzcyBvdGhlcnMpIHRoYXQgbWlnaHQgYmUgc2VlbiBleHRyYXBvbGF0aW5nIHRoZSBkYXRhIHRvIHRoZSBvdGhlciA3NSUgb2YgY2Vuc3VzIHRyYWN0cy4gRmlyc3QsIHRoZSB1bmRlcmx5aW5nIGRpc3RyaWJ1dGlvbnMgb2YgUGN0SW5kdXN0cmlhbCwgUGN0Q29tbWVyY2lhbCwgUGN0VmFjYW50LCBhbmQgTWVkaWFuIEluY29tZSBhcmUgYWxsIHNlbnNpdGl2ZSBpbiB0aGUgdGFpbC4gSW4gUGN0SW5kdXN0cmlhbCBhbmQgUGN0Q29tbWVyY2lhbCB3ZSBzZWUgc2lnbmlmaWNhbnQgc3Bpa2VzIHdoZXJlIGFzIE1lZGlhbiBJbmNvbWUgYW5kIFBjdFZhY2FudCBkbyBub3QuIEl0J3Mgbm90IGhhcmQgdG8gaW1hZ2luZSBjb3VudGVyLWZhY3R1YWwgaW4gZWl0aGVyIGRpcmVjdGlvbi4gU2Vjb25kLCBQY3RNaW5vcml0eSBpcyBhIGJyb2FkIHN0YXRpc3RpYy4gV2UgaGF2ZSB2ZXJ5IGxpdHRsZSB1bmRlcnN0YW5kaW5nIG9mIGl0cyB1bmRlcmx5aW5nIGNvbXBvc2l0aW9uLiBJdCBtYXkgYmUgdGhlIGNhc2UgdGhhdCB3aGVuIGNlcnRhaW4gbWlub3JpdHkgZ3JvdXBzIGNvbXByaXNlIHRoZSB2YXN0IG1ham9yaXR5IG9mIHRoZSBwb3B1bGF0aW9uIGluIHRoYXQgY2Vuc3VzIHRyYWN0LCB0aGUgcmVzcG9uc2UgdmFyaWFibGUgY2hhbmdlcy4gV2Ugc2ltcGx5IGhhdmUgbm8gd2F5IG9mIGtub3dpbmcgYXQgdGhpcyBwb2ludCBnaXZlbiB0aGUgc3VtbWFyeSBkYXRhLiBUaGlyZCwgYW5kIGZpbmFsbHksIHRoaXMgaW5mb3JtYXRpb24gaXMgdGVtcG9yYWxseSBpc29sYXRlZCB0byBvbmUgbW9tZW50IGluIHRpbWUuIElmIGl0IHdlcmUgdG8gYmUgZXh0ZW5kZWQsIHRoZSBhY3R1YWwgZmFjdG9ycyBkZXNjcmliaW5nIHdoZXJlIGhvbWVsZXNzIHBlb3BsZSBjb25ncmVnYXRlIG1heSBoYXZlIGNoYW5nZWQgaW4gYW55IGdpdmVuIGFyZWEuIFRoaXMgaXMgdGhlIGlzc3VlIHdpdGggb25seSBoYXZpbmcgZml2ZSBwcmVkaWN0aXZlIHZhcmlhYmxlcyAtLSB3ZSBiZWNvbWUgbW9yZSBzZW5zaXRpdmUgdG8gYW55IG51bWJlciBvZiB0aGUgbWFueSB1bm9ic2VydmVkIGNoYW5nZXMuCgozLiBGaW5hbGx5LCB3cml0aW5nIHRoaXMgcGFwZXIgd2FzIGZ1biBhbmQgbWFkZSBtZSB0aGluayBhYm91dCBhIGxvdCBvZiB0aGUgY29yZSB1bmRlcmx5aW5nIGFzc3VtcHRpb25zIEkgY2FycnkgaW50byBwcm9ibGVtcy4gV2hlbiBJIGRlY2lkZWQgdG8gZ2V0IG15IE1CQSBhdCBXaGFydG9uLCB0aGUgZ29hbCB3YXMgdG8gY29tZSBzdGF0aXN0aWNhbGx5IGZsdWVudC4gSXQncyBhIGpveSB0byBoYXZlIGNsaW1iZWQgdXAgdGhlIGxhZGRlciBhbmQgZ290dGVuIGluLW92ZXItbXktaGVhZCwgYW5kIG5vdyBjbGltYiBiYWNrIGRvd24gdGhlIGxhZGRlciBhbmQgdW5wYWNrIGVsZW1lbnRzIG9mIHdoYXQncyBoYXBwZW5pbmcgdW5kZXIgdGhlIGNvdmVycy4gVGhhbmsgeW91IQoKCiMjIyBBcHBlbmRpeDogUiBDb2RlCgpgYGB7ciBldmFsPUZBTFNFfQojIyMjIyMgZmlyc3QgZ2xhbmNlIGF0IGRhdGEKc3VtbWFyeShIb21lbGVzcykKaGlzdChIb21lbGVzcyRTdHJlZXRUb3RhbCwgeGxhYiA9ICJIb21lbGVzcyBDb3VudCIsIHlsYWIgPSAiRnJlcXVlbmN5IiwgbWFpbiA9ICJGaWcgMi4xOiBIaXN0b2dyYW0gb2YgTEEgSG9tZWxlc3NuZXNzIENvdW50cyIpCgojIyMjIyBFREEKIyMgRmluZCBOQSdzIGFuZCByZW1vdmUgaWYgc2Vuc2libGUKd2hpY2goaXMubmEoSG9tZWxlc3MkUHJvcFZhY2FudCkpCkhvbWVsZXNzW2MoMTAsNDQ1LDUwMSw1MDIpLF0KSG9tZWxlc3MxIDwtIEhvbWVsZXNzWy1jKDEwLDQ0NSw1MDEsNTAyKSxdCkhvbWVsZXNzMQojIyBQcm9wTWlub3JpdHkgPT0gMS4wMCA/Pz8gKHJlbW92ZWQpCnN1bShIb21lbGVzczEkUHJvcE1pbm9yaXR5ID4gMC4wMCkKc3VtKEhvbWVsZXNzMSRNZWRpYW5JbmNvbWUgPT0gMCkKd2hpY2gubWF4KEhvbWVsZXNzMSRQcm9wTWlub3JpdHkpCkhvbWVsZXNzMVs0OTYsXQpzdW0oSG9tZWxlc3MxJFByb3BNaW5vcml0eSA+IDAuOTApCnN1bShIb21lbGVzczEkUHJvcE1pbm9yaXR5ID4gMC41MCkKaG9sZDEgPC0gd2hpY2goSG9tZWxlc3MxJFByb3BNaW5vcml0eSA+IDAuNTApCmhvbGQyIDwtIEhvbWVsZXNzMSRNZWRpYW5JbmNvbWVbaG9sZDFdCnNvcnQoaG9sZDIpCnN1bW1hcnkoaG9sZDIpCiMjIEZpbmFsIGRhdGFzZXQgd2l0aCByZW1vdmVkIE5BJ3Mvb3V0bGllcnMgKGFuZCBQcm9wIGNvbnZlcnRlZCB0byAlKQpIb21lbGVzczIgPC0gSG9tZWxlc3MxWy00OTYsXQpIb21lbGVzczJbIlBjdFZhY2FudCJdIDwtIEhvbWVsZXNzMiRQcm9wVmFjYW50KjEwMApIb21lbGVzczJbIlBjdE1pbm9yaXR5Il0gPC0gSG9tZWxlc3MyJFByb3BNaW5vcml0eSoxMDAKc3VtbWFyeShIb21lbGVzczIpCkhvbWVsZXNzRiA8LSBIb21lbGVzczJbLC1jKDMsNCldCnN1bW1hcnkoSG9tZWxlc3NGKQoKIyMjIyMjIFVuaXZhcmlhdGUgQW5hbHlzaXMKIyMgU2V0dXAgLyBCYXNpYyBIaXN0CnN1bW1hcnkoSG9tZWxlc3MyKQpzdW0oSG9tZWxlc3MyJFN0cmVldFRvdGFsID4gODMpCnNvcnQoSG9tZWxlc3MyJFN0cmVldFRvdGFsKQpoaXN0KEhvbWVsZXNzMiRTdHJlZXRUb3RhbCkKIyMgVG9wIDEwMCBjb3VudHMgJiBIaXN0b2dyYW0KSG9tZWxlc3NPdmVyMTAwIDwtIHN1YnNldChIb21lbGVzczIsIEhvbWVsZXNzMiRTdHJlZXRUb3RhbCA+PSAxMDApCmhpc3QoSG9tZWxlc3NPdmVyMTAwJFN0cmVldFRvdGFsLCB4bGFiID0gIkhvbWVsZXNzIENvdW50IiwgeWxhYiA9ICJGcmVxdWVuY3kiLCBtYWluID0gIkZpZyA0LjE6IEhpc3RvZ3JhbSBvZiBMQSBIb21lbGVzc25lc3MgQ291bnRzIE92ZXIgMTAwIikKc3VtKEhvbWVsZXNzMiRTdHJlZXRUb3RhbCA+PSAxMDApCiMjIFJldmlld2luZyBvdGhlciB2YXJpYWJsZSBoaXN0b2dyYW1zCiMjIHNrZXdlZCAKcGFyKG1mcm93PWMoMiwyKSkKaGlzdChIb21lbGVzczIkTWVkaWFuSW5jb21lLCB4bGFiID0gIk1lZGlhbiBJbmNvbWUiLCB5bGFiID0gIkZyZXF1ZW5jeSIsIG1haW4gPSAiSGlzdG9ncmFtIG9mIExBIE1lZGlhbiBJbmNvbWVzIikKaGlzdChIb21lbGVzczIkUHJvcFZhY2FudCwgeGxhYiA9ICJQcm9wb3J0aW9uIFZhY2FudCIsIHlsYWIgPSAiRnJlcXVlbmN5IiwgbWFpbiA9ICJIaXN0b2dyYW0gb2YgTEEgUHJvcG9ydGlvbiBWYWNhbnQgKFJlc2lkZW50aWFsKSIpCmhpc3QoSG9tZWxlc3MyJFBjdENvbW1lcmNpYWwsIHhsYWIgPSAiUGVyY2VudCBDb21tZXJjaWFsIiwgeWxhYiA9ICJGcmVxdWVuY3kiLCBtYWluID0gIkhpc3RvZ3JhbSBvZiBMQSAlIENvbW1lcmNpYWwgUmVhbCBFc2F0ZSIpCmhpc3QoSG9tZWxlc3MyJFBjdEluZHVzdHJpYWwsIHhsYWIgPSAiUGVyY2VudCBJbmR1c3RyaWFsIiwgeWxhYiA9ICJGcmVxdWVuY3kiLCBtYWluID0gIkhpc3RvZ3JhbSBvZiBMQSAlIEluZHVzdHJpYWwgUmVhbCBFc2F0ZSIpCnN1bShIb21lbGVzczIkUHJvcFZhY2FudCA8IC4xKS81MDQKIyMgbm9ybWFsLWlzaApoaXN0KEhvbWVsZXNzMiRQcm9wTWlub3JpdHksIHhsYWIgPSAiTWlub3JpdGllcyAoUHJvcG9ydGlvbikiLCB5bGFiID0gIkZyZXF1ZW5jeSIsIG1haW4gPSAiRmlnIDQuMzogTEEgUHJvcG9ydGlvbiBNaW5vcml0aWVzIikgCnN1bW1hcnkoSG9tZWxlc3MyJFByb3BNaW5vcml0eSkKCgojIyMjIyBCaXZhcmlhdGUgQW5hbHlzaXMgKFJlZ3Jlc3Npb24gLS0+IFNQQVIpCiMjIE5vcm1hbCBHTE0gcmVncmVzc2lvbiAoZXh0cmFjdGluZyBjb2VmZmljaWVudHMpCmF0dGFjaChIb21lbGVzc0YpCm1vZC5NSSA8LSBnbG0oU3RyZWV0VG90YWx+TWVkaWFuSW5jb21lKQpzdW1tYXJ5KG1vZC5NSSkKY28uTUkgPC0gY29lZihtb2QuTUkpCgptb2QuVmFjIDwtIGdsbShTdHJlZXRUb3RhbH5QY3RWYWNhbnQpCnN1bW1hcnkobW9kLlZhYykKY28uVmFjIDwtIGNvZWYobW9kLlZhYykKCm1vZC5Db20gPC0gZ2xtKFN0cmVldFRvdGFsflBjdENvbW1lcmNpYWwpCnN1bW1hcnkobW9kLkNvbSkKY28uQ29tIDwtIGNvZWYobW9kLkNvbSkKCm1vZC5JbmQgPC0gZ2xtKFN0cmVldFRvdGFsflBjdEluZHVzdHJpYWwpCnN1bW1hcnkobW9kLkluZCkKY28uSW5kIDwtIGNvZWYobW9kLkluZCkKCm1vZC5NaW4gPC0gZ2xtKFN0cmVldFRvdGFsflBjdE1pbm9yaXR5KQpzdW1tYXJ5KG1vZC5NaW4pCmNvLk1pbiA8LSBjb2VmKG1vZC5NaW4pCgpzdW0uc3RhdHMgPC0gYyhjby5Db20sIGNvLkluZCwgY28uTUksIGNvLk1pbiwgY28uVmFjKQpzdW0uc3RhdHMKCm1vZC5NSWJ5TWluPC0gZ2xtKE1lZGlhbkluY29tZX5QY3RNaW5vcml0eSkKc3VtbWFyeShtb2QuTUlieU1pbikKY28uTUlieU1pbiA8LSBjb2VmKG1vZC5NSWJ5TWluKQpwbG90KE1lZGlhbkluY29tZX5QY3RNaW5vcml0eSwgbWFpbiA9ICJNZWRpYW4gSW5jb21lIGJ5ICUgTWlub3JpdHkiKQoKIyMgU1BBUiBmb3Igc2luZ2xlIHZhcmlhYmxlIG1vZGVscyAoc21vb3RoaW5nIHNwbGluZXMpCiMjIFNtb290aGluZyBTcGxpbmVzCmxpYnJhcnkoZ2FtKQojIyAlIEluZApwYXIobWZyb3c9YygyLDIpKQpvdXQ8LWdhbShTdHJlZXRUb3RhbH5zKFBjdEluZHVzdHJpYWwsIHNwYXI9LjUpLGRhdGE9SG9tZWxlc3NGKQpwbG90KG91dCwgcmVzaWR1YWxzPVQsIHlsYWIgPSAiQ291bnQiLCBtYWluID0gIkZpZyA1LjJhOiBIb21lbGVzcyBDb3VudCBieSAlIEluZHVzdHJpYWwgKHNwYXI9MC41KSIpCm91dDwtZ2FtKFN0cmVldFRvdGFsfnMoUGN0SW5kdXN0cmlhbCwgc3Bhcj0xLjApLGRhdGE9SG9tZWxlc3NGKQpwbG90KG91dCwgcmVzaWR1YWxzPVQsIHlsYWIgPSAiQ291bnQiLCBtYWluID0gIkZpZyA1LjJiOiBIb21lbGVzcyBDb3VudCBieSAlIEluZHVzdHJpYWwgKHNwYXI9MS4wKSIpCm91dDwtZ2FtKFN0cmVldFRvdGFsfnMoUGN0SW5kdXN0cmlhbCwgc3Bhcj0xLjUpLGRhdGE9SG9tZWxlc3NGKQpwbG90KG91dCwgcmVzaWR1YWxzPVQsIHlsYWIgPSAiQ291bnQiLCBtYWluID0gIkZpZyA1LjJjOiBIb21lbGVzcyBDb3VudCBieSAlIEluZHVzdHJpYWwgKHNwYXI9MS41KSIpCnBsb3QoU3RyZWV0VG90YWx+UGN0SW5kdXN0cmlhbCwgeWxhYiA9ICJDb3VudCIsIG1haW4gPSAiRmlnIDUuMmQ6IEhvbWVsZXNzIENvdW50IGJ5ICUgSW5kdXN0cmlhbCAobGluZWFyKSIpCmFibGluZShhPVBjdEluZHVzdHJpYWwsIGI9U3RyZWV0VG90YWwpCgojIyAlIFZhY2FudApwYXIobWZyb3c9YygyLDIpKQpvdXQ8LWdhbShTdHJlZXRUb3RhbH5zKFBjdFZhY2FudCwgc3Bhcj0uNSksZGF0YT1Ib21lbGVzc0YpCnBsb3Qob3V0LCByZXNpZHVhbHM9VCwgeWxhYiA9ICJDb3VudCIsIG1haW4gPSAiNS4zYTogSG9tZWxlc3MgQ291bnQgYnkgJSBWYWNhbnQgKHNwYXI9MC41KSIpCm91dDwtZ2FtKFN0cmVldFRvdGFsfnMoUGN0VmFjYW50LCBzcGFyPTEuMCksZGF0YT1Ib21lbGVzc0YpCnBsb3Qob3V0LCByZXNpZHVhbHM9VCwgeWxhYiA9ICJDb3VudCIsIG1haW4gPSAiNS4zYjogSG9tZWxlc3MgQ291bnQgYnkgJSBWYWNhbnQgKHNwYXI9MS4wKSIpCm91dDwtZ2FtKFN0cmVldFRvdGFsfnMoUGN0VmFjYW50LCBzcGFyPTEuNSksZGF0YT1Ib21lbGVzc0YpCnBsb3Qob3V0LCByZXNpZHVhbHM9VCwgeWxhYiA9ICJDb3VudCIsIG1haW4gPSAiNS4zYzogSG9tZWxlc3MgQ291bnQgYnkgJSBWYWNhbnQgKHNwYXI9MS41KSIpCnBsb3QoU3RyZWV0VG90YWx+UGN0VmFjYW50LCB5bGFiID0gIkNvdW50IiwgbWFpbiA9ICI1LjNkOiBIb21lbGVzcyBDb3VudCBieSAlIFZhY2FudCAobGluZWFyKSIpCmFibGluZShhPVBjdFZhY2FudCwgYj1TdHJlZXRUb3RhbCkKCiMjIE1lZCBJbmMKcGFyKG1mcm93PWMoMiwyKSkKb3V0PC1nYW0oU3RyZWV0VG90YWx+cyhNZWRpYW5JbmNvbWUsIHNwYXI9LjUpLGRhdGE9SG9tZWxlc3NGKQpwbG90KG91dCwgcmVzaWR1YWxzPVQsIHlsYWIgPSAiQ291bnQiLCBtYWluID0gIkZpZyA1LjRhOiBIb21lbGVzcyBDb3VudCBieSBNZWRpYW4gSW5jY29tZSAoc3Bhcj0wLjUpIikKb3V0PC1nYW0oU3RyZWV0VG90YWx+cyhNZWRpYW5JbmNvbWUsIHNwYXI9MS4wKSxkYXRhPUhvbWVsZXNzRikKcGxvdChvdXQsIHJlc2lkdWFscz1ULCB5bGFiID0gIkNvdW50IiwgbWFpbiA9ICJGaWcgNS40YjogSG9tZWxlc3MgQ291bnQgYnkgTWVkaWFuIEluY2NvbWUgKHNwYXI9MS4wKSIpCm91dDwtZ2FtKFN0cmVldFRvdGFsfnMoTWVkaWFuSW5jb21lLCBzcGFyPTEuNSksZGF0YT1Ib21lbGVzc0YpCnBsb3Qob3V0LCByZXNpZHVhbHM9VCwgeWxhYiA9ICJDb3VudCIsIG1haW4gPSAiRmlnIDUuNGM6IEhvbWVsZXNzIENvdW50IGJ5IE1lZGlhbiBJbmNjb21lIChzcGFyPTEuNSkiKQpwbG90KFN0cmVldFRvdGFsfk1lZGlhbkluY29tZSwgeWxhYiA9ICJDb3VudCIsIG1haW4gPSAiRmlnIDUuNGQ6IEhvbWVsZXNzIENvdW50IGJ5IE1lZGlhbiBJbmNvbWUgKGxpbmVhcikiKQphYmxpbmUoYT1NZWRpYW5JbmNvbWUsIGI9U3RyZWV0VG90YWwpCgoKIyMjIyMgTXVsdGl2YXJpYXRlIEFuYWx5c2lzIChHQU0pCiMjIENyZWF0aW5nIFRyYWluaW5nL0V2YWwvVGVzdCBTcGxpdCAKIyMgU2V0IHNlZWQKc2V0LnNlZWQoMTcpCgojIyBTcGxpdCBkYXRhCnNwZWMgPSBjKHRyYWluID0gLjYsIGV2YWx1YXRpb24gPSAuMiwgdGVzdCA9IC4yKQoKZyA9IHNhbXBsZShjdXQoCiAgc2VxKG5yb3coSG9tZWxlc3NGKSksIAogIG5yb3coSG9tZWxlc3NGKSpjdW1zdW0oYygwLHNwZWMpKSwKICBsYWJlbHMgPSBuYW1lcyhzcGVjKQopKQoKSG9tZWxlc3NTcGxpdCA9IHNwbGl0KEhvbWVsZXNzRiwgZykKc2FwcGx5KEhvbWVsZXNzU3BsaXQsIG5yb3cpL25yb3coSG9tZWxlc3NGKQojIyBMb29rIGF0IHN1bW1hcnkgc3RhdGlzdGljcwpzdW1tYXJ5KEhvbWVsZXNzU3BsaXQkdHJhaW4pCmhpc3QoSG9tZWxlc3NTcGxpdCR0cmFpbiRTdHJlZXRUb3RhbCkKc3VtbWFyeShIb21lbGVzc1NwbGl0JGV2YWx1YXRpb24pCmhpc3QoSG9tZWxlc3NTcGxpdCRldmFsdWF0aW9uJFN0cmVldFRvdGFsKQpzdW1tYXJ5KEhvbWVsZXNzU3BsaXQkdGVzdCkKaGlzdChIb21lbGVzc1NwbGl0JHRlc3QkU3RyZWV0VG90YWwpCgojIyBHQU0gTW9kZWwKIyMgR2FtIGxpYnJhcnkKbGlicmFyeShnYW0pCiMjIHNwYXIgPSAwLjUKb3V0LjU8LWdhbShTdHJlZXRUb3RhbH5zKE1lZGlhbkluY29tZSxzcGFyPS41KStzKFBjdENvbW1lcmNpYWwsc3Bhcj0uNSkrCiAgICAgICAgICAgIHMoUGN0SW5kdXN0cmlhbCxzcGFyPS41KStzKFBjdE1pbm9yaXR5LHNwYXI9LjUpK3MoUGN0VmFjYW50LHNwYXI9LjUpLAogICAgICAgICAgICBmYW1pbHk9Z2F1c3NpYW4pICMgU3BlY2lmeSBzcGFyCnN1bW1hcnkob3V0LjUpCnBhcihtZnJvdz1jKDEsMSkpCnBsb3Qob3V0LjUsYXNrPVQpICMgY2VudGVyZWQgYXJvdW5kIHRoZSBtZWFuIG9mIHRoZSByZXNwb25zZQpoaXN0KGZpdHRlZC52YWx1ZXMob3V0LjUpLGJyZWFrcz0yMCkgCgojIyBzcGFyID0gMC43NQpvdXQuNzU8LWdhbShTdHJlZXRUb3RhbH5zKE1lZGlhbkluY29tZSxzcGFyPS43NSkrcyhQY3RDb21tZXJjaWFsLHNwYXI9Ljc1KSsKICAgICAgICAgICAgcyhQY3RJbmR1c3RyaWFsLHNwYXI9Ljc1KStzKFBjdE1pbm9yaXR5LHNwYXI9Ljc1KStzKFBjdFZhY2FudCxzcGFyPS43NSksCiAgICAgICAgICBmYW1pbHk9Z2F1c3NpYW4pICMgU3BlY2lmeSBzcGFyCnN1bW1hcnkob3V0Ljc1KQpwYXIobWZyb3c9YygxLDEpKQpwbG90KG91dC43NSxhc2s9VCkgIyBjZW50ZXJlZCBhcm91bmQgdGhlIG1lYW4gb2YgdGhlIHJlc3BvbnNlCmhpc3QoZml0dGVkLnZhbHVlcyhvdXQuNzUpLGJyZWFrcz0yMCkgCgojIyBzcGFyID0gMS4wCm91dDE8LWdhbShTdHJlZXRUb3RhbH5zKE1lZGlhbkluY29tZSxzcGFyPTEpK3MoUGN0Q29tbWVyY2lhbCxzcGFyPTEpKwogICAgICAgICAgICBzKFBjdEluZHVzdHJpYWwsc3Bhcj0xKStzKFBjdE1pbm9yaXR5LHNwYXI9MSkrcyhQY3RWYWNhbnQsc3Bhcj0xKSwKICAgICAgICAgIGZhbWlseT1nYXVzc2lhbikgIyBTcGVjaWZ5IHNwYXIKc3VtbWFyeShvdXQxKQpwYXIobWZyb3c9YygxLDEpKQpwbG90KG91dDEsYXNrPVQpICMgY2VudGVyZWQgYXJvdW5kIHRoZSBtZWFuIG9mIHRoZSByZXNwb25zZQpoaXN0KGZpdHRlZC52YWx1ZXMob3V0MSksYnJlYWtzPTIwKSAKCiMjIHNwYXIgPSAxLjUKb3V0MS41PC1nYW0oU3RyZWV0VG90YWx+cyhNZWRpYW5JbmNvbWUsc3Bhcj0xLjUpK3MoUGN0Q29tbWVyY2lhbCxzcGFyPTEuNSkrCiAgICAgICAgICAgIHMoUGN0SW5kdXN0cmlhbCxzcGFyPTEuNSkrcyhQY3RNaW5vcml0eSxzcGFyPTEuNSkrcyhQY3RWYWNhbnQsc3Bhcj0xLjUpLAogICAgICAgICAgZmFtaWx5PWdhdXNzaWFuKSAjIFNwZWNpZnkgc3BhcgpzdW1tYXJ5KG91dDEuNSkKcGFyKG1mcm93PWMoMSwxKSkKcGxvdChvdXQxLjUsYXNrPVQpICMgY2VudGVyZWQgYXJvdW5kIHRoZSBtZWFuIG9mIHRoZSByZXNwb25zZQpoaXN0KGZpdHRlZC52YWx1ZXMob3V0MS41KSxicmVha3M9MjApIAoKcGFyKG1mcm93PWMoMiwyKSkKaGlzdChmaXR0ZWQudmFsdWVzKG91dC41KSxicmVha3M9MjApIApoaXN0KGZpdHRlZC52YWx1ZXMob3V0Ljc1KSxicmVha3M9MjApIApoaXN0KGZpdHRlZC52YWx1ZXMob3V0MSksYnJlYWtzPTIwKSAKaGlzdChmaXR0ZWQudmFsdWVzKG91dDEuNSksYnJlYWtzPTIwKSAKCiMjIHNwYXIgPSAwLjYKb3V0MC42PC1nYW0oU3RyZWV0VG90YWx+cyhNZWRpYW5JbmNvbWUsc3Bhcj0wLjYpK3MoUGN0Q29tbWVyY2lhbCxzcGFyPTAuNikrCiAgICAgICAgICAgICAgcyhQY3RJbmR1c3RyaWFsLHNwYXI9MC42KStzKFBjdE1pbm9yaXR5LHNwYXI9MC42KStzKFBjdFZhY2FudCxzcGFyPTAuNiksCiAgICAgICAgICAgIGZhbWlseT1nYXVzc2lhbikgIyBTcGVjaWZ5IHNwYXIKc3VtbWFyeShvdXQwLjYpCnBhcihtZnJvdz1jKDEsMSkpCnBsb3Qob3V0MC42LGFzaz1UKSAjIGNlbnRlcmVkIGFyb3VuZCB0aGUgbWVhbiBvZiB0aGUgcmVzcG9uc2UKaGlzdChmaXR0ZWQudmFsdWVzKG91dDAuNiksYnJlYWtzPTIwKSAKCiMjIEV2YWx1YXRpb24gRGF0YSAoU1BBUiA9IDEuMCkKcHJlZHMuMTwtcHJlZGljdChvdXQxLCBuZXdkYXRhPUhvbWVsZXNzU3BsaXQkZXZhbHVhdGlvbikgIyBnZXQgZml0dGVkIHZhbHVlcwpoaXN0KHByZWRzLjEpICMgd2hhdCBkb2VzIHRoZSBkaXN0cmlidXRpb24gbG9vayBsaWtlPwpyZXNpZHMuMTwtSG9tZWxlc3NTcGxpdCRldmFsdWF0aW9uJFN0cmVldFRvdGFsLXByZWRzLjEgIyBnZXQgcmVzaWR1YWxzCnZhcihyZXNpZHMuMSkgIyBNU0UgZm9yIHRoZSByZXNpZHVhbHMgYXMgYSBtZWFzdXJlIG9mIGZpdAoKIyMgRXZhbHVhdGlvbiBEYXRhIChTUEFSID0gMC43NSkKcHJlZHMuNzU8LXByZWRpY3Qob3V0Ljc1LCBuZXdkYXRhPUhvbWVsZXNzU3BsaXQkZXZhbHVhdGlvbikgIyBnZXQgZml0dGVkIHZhbHVlcwpoaXN0KHByZWRzLjc1KSAjIHdoYXQgZG9lcyB0aGUgZGlzdHJpYnV0aW9uIGxvb2sgbGlrZT8KcmVzaWRzLjc1PC1Ib21lbGVzc1NwbGl0JGV2YWx1YXRpb24kU3RyZWV0VG90YWwtcHJlZHMuNzUgIyBnZXQgcmVzaWR1YWxzCnZhcihyZXNpZHMuNzUpICMgTVNFIGZvciB0aGUgcmVzaWR1YWxzIGFzIGEgbWVhc3VyZSBvZiBmaXQKCiMjIEV2YWx1YXRpb24gRGF0YSAoU1BBUiA9IDAuNSkKcHJlZHMuNTwtcHJlZGljdChvdXQuNSwgbmV3ZGF0YT1Ib21lbGVzc1NwbGl0JGV2YWx1YXRpb24pICMgZ2V0IGZpdHRlZCB2YWx1ZXMKaGlzdChwcmVkcy41KSAjIHdoYXQgZG9lcyB0aGUgZGlzdHJpYnV0aW9uIGxvb2sgbGlrZT8KcmVzaWRzLjU8LUhvbWVsZXNzU3BsaXQkZXZhbHVhdGlvbiRTdHJlZXRUb3RhbC1wcmVkcy41ICMgZ2V0IHJlc2lkdWFscwp2YXIocmVzaWRzLjUpICMgTVNFIGZvciB0aGUgcmVzaWR1YWxzIGFzIGEgbWVhc3VyZSBvZiBmaXQKCiMjIEV2YWx1YXRpb24gRGF0YSAoU1BBUiA9IDAuNikKcHJlZHMwLjY8LXByZWRpY3Qob3V0MC42LCBuZXdkYXRhPUhvbWVsZXNzU3BsaXQkZXZhbHVhdGlvbikgIyBnZXQgZml0dGVkIHZhbHVlcwpoaXN0KHByZWRzMC42KSAjIHdoYXQgZG9lcyB0aGUgZGlzdHJpYnV0aW9uIGxvb2sgbGlrZT8KcmVzaWRzMC42PC1Ib21lbGVzc1NwbGl0JGV2YWx1YXRpb24kU3RyZWV0VG90YWwtcHJlZHMwLjYgIyBnZXQgcmVzaWR1YWxzCnZhcihyZXNpZHMwLjYpICMgTVNFIGZvciB0aGUgcmVzaWR1YWxzIGFzIGEgbWVhc3VyZSBvZiBmaXQKCiMjIFRlc3QgZGF0YQojIyBUZXN0IERhdGEgKFNQQVIgPSAwLjYpCnRwcmVkczAuNjwtcHJlZGljdChvdXQwLjYsIG5ld2RhdGE9SG9tZWxlc3NTcGxpdCR0ZXN0KSAjIGdldCBmaXR0ZWQgdmFsdWVzCmhpc3QodHByZWRzMC42KSAjIHdoYXQgZG9lcyB0aGUgZGlzdHJpYnV0aW9uIGxvb2sgbGlrZT8KdHJlc2lkczAuNjwtSG9tZWxlc3NTcGxpdCR0ZXN0JFN0cmVldFRvdGFsLXRwcmVkczAuNiAjIGdldCByZXNpZHVhbHMKdmFyKHRyZXNpZHMwLjYpICMgTVNFIGZvciB0aGUgcmVzaWR1YWxzIGFzIGEgbWVhc3VyZSBvZiBmaXQKCnJhbmdlKHRwcmVkczAuNikKc29ydCh0cHJlZHMwLjYpCgojIyMjIyBTdW1tYXJ5IGFuZCBDb25jbHVzaW9uCnBhcihtZnJvdz1jKDMsMikpCnBsb3Qob3V0MC42LHJlc2lkdWFsPVQsY2V4PTEscGNoPTE0LGNvbD0ibGlnaHQgYmx1ZSIsIG1haW4gPSAiRmlnIDcuMTogU1BBUiA9IDAuNiBGaXQiKQpgYGAKCgoK