Task 1: Sensitivity Analysis
Sensitivity analysis is the study of how the uncertainty in the output of a mathematical model or system (numerical or otherwise) can be apportioned to different sources of uncertainty in its inputs. We will use the lpSolveAPI R-package as we did in the previous lab.
In order to conduct a sensitivity analysis, we will need to download again the lpSolveAPI package unless you have it already installed in your R environment
# Require will load the package only if not installed
# Dependencies = TRUE makes sure that dependencies are install
if(!require("lpSolveAPI",quietly = TRUE))
install.packages("lpSolveAPI",dependencies = TRUE, repos = "https://cloud.r-project.org")
We will revisit and solve again the marketing case discussed in class (also part of previous lab).
# We start with `0` constraint and `2` decision variables. The object name `lpmark` is discretionary.
lpmark = make.lp(0, 2)
# Define type of optimization as maximum and dump the screen output into a `dummy` variable
dummy = lp.control(lpmark, sense="max")
# Set the objective function coefficients
set.objfn(lpmark, c(275.691, 48.341))
Add all constraints to the model.
add.constraint(lpmark, c(1, 1), "<=", 350000)
add.constraint(lpmark, c(1, 0), ">=", 15000)
add.constraint(lpmark, c(0, 1), ">=", 75000)
add.constraint(lpmark, c(2, -1), "=", 0)
add.constraint(lpmark, c(1, 0), ">=", 0)
add.constraint(lpmark, c(0, 1), ">=", 0)
Now, view the problem setting in tabular/matrix form. This is a good checkpoint to confirm that our contraints have been properly set.
lpmark
Model name:
C1 C2
Maximize 275.691 48.341
R1 1 1 <= 350000
R2 1 0 >= 15000
R3 0 1 >= 75000
R4 2 -1 = 0
R5 1 0 >= 0
R6 0 1 >= 0
Kind Std Std
Type Real Real
Upper Inf Inf
Lower 0 0
# solve
solve(lpmark)
[1] 0
Next we get the optimum results.
# display the objective function optimum value
get.objective(lpmark)
[1] 43443517
# display the decision variables optimum values
get.variables(lpmark)
[1] 116666.7 233333.3
For sensitivity we will introduce the code command to obtain the sensitivities due to changes in resources as expressed in the right hand side (rhs) constraints.
# display sensitivity to rhs constraints.
# There will be a total of m+n values where m is the number of contraints and n is the number of decision variables
get.sensitivity.rhs(lpmark)
$duals
[1] 124.12433 0.00000 0.00000 75.78333 0.00000 0.00000 0.00000 0.00000
$dualsfrom
[1] 1.125e+05 -1.000e+30 -1.000e+30 -3.050e+05 -1.000e+30 -1.000e+30 -1.000e+30 -1.000e+30
$dualstill
[1] 1.00e+30 1.00e+30 1.00e+30 4.75e+05 1.00e+30 1.00e+30 1.00e+30 1.00e+30
For this exercise we are only interested in the first six values (corresponding to the six constraints) of the output labeled duals.
##### 1A) Identify and explain what the binding/non-binding constraints, the surplus/slack, and the marginal values are and what they represent in the context of our marketing use case
The first constraint xradio+xtv<=350000 is a binding constraint. If the maximum budget increased by 1 dollar, then the optimal solution changes, and the sales value will increase by 124.12. The second constraint (xradio >= 15000) is non-binding on account of the zero value. calculation is below.
surplus_c_2= get.variables(lpmark)[1]-15000
surplus_c_2
[1] 101666.7
The third constraint of xtv >= 75000 is non-binding. Surplus calculated as follows:
surplus_c_3 = get.variables(lpmark)[2]-75000
surplus_c_3
[1] 158333.3
the fourth constraint 2Xradio - Xtv = 0 is binding. If the right hand side of the equation was increased by 1 dollar, then the optimal solution will change, sales value will increase by marginal value $75.78
To acquire a better understanding of the sensitivity results, and to confirm integrity of the calculations, independent tests can be conducted. To demonstrate, we will repeat the linear programming (LP) optimization problem by slightly tweaking one binding constraint and verifying the impact.
##### 1B) Define a new model object lpmark1 and add the constraints exactly as entered at beginning of this task. All being equal, change the budget constraint by a marginal $1 value and solve the LP problem. Note the new optimum value for sales. Calculate the differential change in optimum sales from the earlier computed optimum sales, and compare your calculation to the value obtained from the prior sensitivity calculation
# Define a new model object called lpmark1
lpmark1 = make.lp(0, 2)
# Repeat rest of commands with the one constraint change for budget. Solve and display the objective function optimum value
dummy = lp.control(lpmark1, sense="max")
set.objfn(lpmark1, c(275.691, 48.341))
add.constraint(lpmark1, c(1, 1), "<=", 350001)
add.constraint(lpmark1, c(1, 0), ">=", 15000)
add.constraint(lpmark1, c(0, 1), ">=", 75000)
add.constraint(lpmark1, c(2, -1), "=", 0)
add.constraint(lpmark1, c(1, 0), ">=", 0)
add.constraint(lpmark1, c(0, 1), ">=", 0)
lpmark1
Model name:
C1 C2
Maximize 275.691 48.341
R1 1 1 <= 350001
R2 1 0 >= 15000
R3 0 1 >= 75000
R4 2 -1 = 0
R5 1 0 >= 0
R6 0 1 >= 0
Kind Std Std
Type Real Real
Upper Inf Inf
Lower 0 0
solve(lpmark1)
[1] 0
get.objective(lpmark1)
[1] 43443641
get.variables(lpmark1)
[1] 116667 233334
Here we can subtract our optimum values to get the marginal change.
get.objective(lpmark1)-get.objective(lpmark)
[1] 124.1243
Here we can see that the marginal change in sales value is the amount we calculated for the previous problem.
##### 1C) Following the logic of calculations in 1B) and without running another code, explain the steps to validate the integrity of the other remaining marginal value identified in 1A). Use a different line to explain each step
Revert the right hand side of the first constraint back to 350000, then add 1 to the right hand side of the second constraint, keep the other constraints equal, then calculate the difference between the new optimum value and the original optimum value.
Revert the right hand side of the second constraint back to 15000, then add 1 to the right hand side of the third constraint, keep the other constraints equal, then calculate the difference between the new optimum value and the original optimum value.
Revert the right hand side of the third constraint back to 750000, then add 1 to the right hand side of the fourth constraint, keep the other constraints equal, then calculate the difference between the new optimum value and the original optimum value.
Task 2: Random Sampling & Monte Carlo Simulation
Monte Carlo Simulations utilize repeated random sampling from a given universe or population distribution to derive certain results. This type of simulation is known as a probabilistic simulation, as opposed to a deterministic simulation.
An example of a Monte Carlo simulation is the one applied to approximate the value of pi. The simulation is based on generating random points within a unit square and see how many points fall within the circle enclosed by the unit square (marked in red). The value of pi is estimated by the number of points inside the circle over the total number of points generated inside the square. The higher the number of sampled points the closer the result is to the actual result. After selecting 30,000 random points, the estimate for pi is much closer to the actual value within the four decimal points of precision. The interested and curious reader is encouraged to explore the mathematical foundation behind the logic.

For this task we will be running a Monte Carlo simulation to calculate the probability that the daily return from S&P will be > 5%. We will assume that the historical S&P daily return follows a normal distribution with an average daily return of 0.03 (%) and a standard deviation of 0.97 (%). Consider this as our population.
To begin we will generate 100 random samples from the normal distribution. For the generated samples we will calculate the mean, standard deviation, and probability of occurrence where the simulation result is greater than 5%.
To generate random samples from a normal distribution we will use the rnorm() function in R. In the example below we set the number of runs (or samples) to 100.
# number of simulations/samples
runs = 100
# random number generator per defined normal distribution with given mean and standard deviation
sims = rnorm(runs,mean=0.03,sd=0.97)
# Mean calculated from the random distribution of samples
average = mean(sims)
average
[1] 0.1272613
# STD calculated from the random distribution of samples
std = sd(sims)
std
[1] 1.078883
# probability of occurrence on any given day based on samples will be equal to count (or sum) where sample result is greater than 5% divided by total number of samples.
prob = sum(sims >=0.05)/runs
prob
[1] 0.5
##### 2A) Repeat the above calculations for the two cases where the number of simulations/samples is equal to 1000 and 10000. For each case record the mean, standard deviation, and probability
# Repeat calculations here
runs1 = 1000
sims1 = rnorm(runs1,mean=0.03,sd=0.97)
average1 = mean(sims1)
average1
[1] 0.02324018
std1 = sd(sims1)
std1
[1] 0.9686136
prob1 = sum(sims1 >=0.05)/runs1
prob1
[1] 0.501
runs2 = 10000
sims2 = rnorm(runs2,mean=0.03,sd=0.97)
average2 = mean(sims2)
average2
[1] 0.0262328
std2 = sd(sims2)
std2
[1] 0.9665475
prob2 = sum(sims2 >=0.05)/runs2
prob2
[1] 0.4898
##### 2B) List in seperate lines the values for mean, standard deviation, and probability for all three cases: 100, 1000, and 10000 simulations. Describe how the values change/behave as the number of simulations is increased. What is your best bet on the probability of occurrence greater than 5% and why? How is this behavior similar to the use case demonstration to calculate pi?
runs 100 mean = 0.1272613 sd = 1.078883 prob = 0.5
runs 1000 mean = 0.02324018 sd = 0.9686136 prob = 0.501
runs 10000 mean = 0.0262328 sd = 0.9665475 prob = 0.4898
The larger the sample, the closer we get to the average and standard deviation values we were given at the beginning of the problem. That being said, the best probability here is probably the 0.4898 that was given by the model of 10000 runs. ### Task 3: Training Set & Model Building
The first half of this lab focuses on building a model using the training data. The data we will be using was obtained from Kaggle site[1] and is in reference to world university rankings [2] . The data looks at university world scoring based on different ranking criteria such as quality of education, quality of faculty, and rank for patents. Spend some time to visit the referenced website to get more acquinated with the data. The data obtained is divided inteo two sets: a training set and a testing set. We begin by reading the training data set ‘universityrank_training.csv’ file, and checking the header lines to make sure the data is read correctly.
traindata = read.csv(file="universityrank_training.csv", header=TRUE)
head(traindata)
Next, we extract the two columns of interest and call them properly so we can easily refer to them later in the code.
patent_train = traindata$patents
score_train = traindata$score
The first model we will build is a simple linear model. We will use the patents ranking variable to predict the university score. To better understand the data, the lower the patents ranking number the better it is. A value of 1 is a top rank for patents and represent the highest category in terms of number of patents owned by the particular academic institution. On the other hand the higher the calculated total score the better, as reflected by the world rank number. A value of 100 is a perfect score.
linear_train = lm(score_train ~ patent_train)
summary(linear_train)
Call:
lm(formula = score_train ~ patent_train)
Residuals:
Min 1Q Median 3Q Max
-9.876 -4.010 -1.118 1.512 45.471
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 54.6397362 0.3857798 141.63 <2e-16 ***
patent_train -0.0157558 0.0008281 -19.03 <2e-16 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 7.535 on 1198 degrees of freedom
Multiple R-squared: 0.2321, Adjusted R-squared: 0.2314
F-statistic: 362 on 1 and 1198 DF, p-value: < 2.2e-16
plot(patent_train,score_train)
abline(linear_train, col="blue", lwd=2)

##### 3A) Complete the steps in the code chunk below to build a non-linear quadratic model. Follow the steps used in lab08 for costs versus servers
# First define a new variable which is the squared value of patent_train (defined above)
patent_train2 = patent_train^2
# Next derive the quadratic regression model. You may want to call it quad_train.
quad_train = lm (score_train ~ patent_train + patent_train2)
# Publish the summary statistics
summary(quad_train)
Call:
lm(formula = score_train ~ patent_train + patent_train2)
Residuals:
Min 1Q Median 3Q Max
-13.555 -2.345 -0.582 1.302 40.843
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 5.966e+01 4.971e-01 120.02 <2e-16 ***
patent_train -6.249e-02 3.319e-03 -18.83 <2e-16 ***
patent_train2 5.972e-05 4.127e-06 14.47 <2e-16 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 6.955 on 1197 degrees of freedom
Multiple R-squared: 0.3464, Adjusted R-squared: 0.3453
F-statistic: 317.2 on 2 and 1197 DF, p-value: < 2.2e-16
score_fitted = coef(quad_train)[1] + coef(quad_train)[2] * patent_train + coef(quad_train)[3] * patent_train2
plot(patent_train,score_train, pch = 16)
par(new=TRUE, xaxt="n", yaxt="n", ann=FALSE)
plot(patent_train, score_fitted, col = "red", pch = 16)

##### 3B) Looking at the values for R-Squared and Adjusted R-Squared for both the linear and the quadratic select the best predictive model. Explain your logic
The quadratic model is best on account of its R Squared and Adjusted R Squared values, indicating it to be the best predictive model.
Task 4: Testing Set & Model Evaluation
The second half of predictive modeling is about testing the model using a different data set called the testing data. Again we must first read the testing data set, and make sure the dataset is read propertly.
testdata = read.csv("universityrank_testing.csv", header=TRUE)
head(testdata)
We extract again the two columns of interest, in reference this time to the testing data set, and call them accordingly.
patent_test = testdata$patents
score_test = testdata$score
We are ready now to check if the derived models are actually good predictive models. First we calculate the predicted test data score using the linear model as derived from the training set. Later we will consider the quadratic model.
# Calculate the predicted test data score
score_predict1 = coef(linear_train)[1] + coef(linear_train)[2]*patent_test
For a visual qualitative evaluation we can plot the actual testing data, and overlay the predicted values
# Plot the actual values for patent and score as observed in the testing data set
plot(patent_test, score_test, main='Test Data -- Score vs Patent')
# Overlay the predicted values as calculated from the linear model and derived using the training model
par(new=TRUE, xaxt="n", yaxt="n", ann=FALSE)
# The red color is used to distinguish the predicted values which, because of the linear model, will fit exactly a line
plot(patent_test, score_predict1, col="red")

A better way to qualify the goodness of a predictive model is to scatter plot the actual values against the predicted values. In a perfect predictive model the points will line up along the diagonal line. This is rarely the case, if ever!
#Plot predicted values from the linear model versus actual values form the test data
plot(score_test, score_predict1, xlab='Actual', ylab='Predict', main='Linear Model -- Predict vs Actual Test')

From the plot we can easily see that most of the predicted values versus actual are far from the diagoonal line. In many cases this is fine. Finally, to quantify the goodness of a model, we need to calculate the Root Mean Square Error (RMSE).
#Calculate RMSE for Linear Model
error1 = sum((score_predict1 - score_test)^2)/length(score_test)
rmse1 = sqrt(error1)
rmse1
[1] 5.95868
It is hard to judge the goodness of the number unless we compare to other possibilities. Of course a perfect scenario will have zero RMSE. We now need to repeat the above calculations for the non-linear quadratic model.
##### 4A) Fill in the code chunk below to calculate the predicted values for the non-linear quadratic model
# Calculate score_predict2 using the quadratic model as derived earlier from the training data and as applied to the actual patent values obtained from the testing data
patent_test2 = patent_test^2
score_predict2 = coef(quad_train)[1] + coef(quad_train)[2] * patent_test + coef(quad_train)[3] * patent_test2
For a visual representation, similar to the linear model, we need to do the following.
##### 4B) Fill-in the code chunk below to plot the actual Score vs Patent for the test data, and overlay the predicted values as calculated in 2A. Label axes and title properly (4pts)
# Plot the actual values for patent and score as observed in the testing data set
plot(patent_test, score_test, main='Test Data -- Score vs Patent')
# Overlay the predicted values as calculated from the quadratic model and derived using the training model.
par(new=TRUE, xaxt="n", yaxt="n", ann=FALSE)
# The green color is used to distinguish the predicted values which, because of the quadratic model, will in this case will fit exactly a parabola
plot(patent_test, score_predict2, col="green")

#### 4C) Plot the Predict vs Actual for the quadratic model. Label axes and title properly (2pts)
#Plot the predicted values form the quadratic model versus the actual values from the test data
plot(score_test, score_predict2, xlab='Actual', ylab='Predict', main='Quad Model -- Predict vs Actual Test')

#### 4D) By looking at the scatterplots for the linear and quadratic models are you able to tell which model is better? Explain your logic (1pt)
Quadratic is a better fit because the actual points line up a tad bit closer compared to the linear one.
A better way is to quantify the goodness of the model by calculating again the RMSE.
#### 4E) Calculate the root mean square error (RMSE) for the quadratic model (2pts)
#Calculate RMSE for Quadratic Model
error2 = sum((score_predict2 - score_test)^2)/length(score_test)
rmse2 = sqrt(error2)
rmse2
[1] 5.685396
#### 4F) Based on the root mean square error (RMSE) which model is better? How your conclusion reconcile with the results from Task 1? (1pt)
RMSE for the quadratic model is less than the RMSE for the linear model. This means there is less error in the predictions of the quadratic model, making it the better predictor.
source [1]: http://www.kaggle.com
source [2]: http://www.cwur.org
LS0tDQp0aXRsZTogIlNlbnNpdGl2aXR5IEFuYWx5c2lzICYgU2ltdWxhdGlvbnMgKGxhYjA5KSINCmF1dGhvcjogIlVyaWVsIFJleWVzIFZhenF1ZXoiDQpkYXRlOiAiNC8yOS8yMDIwIg0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOiBkZWZhdWx0DQogIGh0bWxfZG9jdW1lbnQ6IGRlZmF1bHQNCiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0DQpzdWJ0aXRsZTogQlNBRCAzNDMsIEJ1c2luZXNzIEFuYWx5dGljcywgU3ByaW5nIDIwMjANCi0tLQ0KDQoNCiMjIyBBYm91dA0KDQpJbiB0aGlzIGxhYiwgd2Ugd2lsbCBsZWFybiBob3cgdG8gcnVuIGEgc2Vuc2l0aXZpdHkgYW5hbHlzaXMgb24gdGhlIG1hcmtldGluZyB1c2UgY2FzZSwgYW5kIGhvdyB0byBnZW5lcmF0ZSByYW5kb20gc2FtcGxlcyBmcm9tIGEgc2ltcGxlIG5vcm1hbCBkaXN0cmlidXRpb24gc2ltaWxhciB0byBhIE1vbnRlIENhcmxvIHNpbXVsYXRpb24uDQoNCiMjIyBTZXR1cA0KDQpSZW1lbWJlciB0byBhbHdheXMgc2V0IHlvdXIgd29ya2luZyBkaXJlY3RvcnkgdG8gdGhlIHNvdXJjZSBmaWxlIGxvY2F0aW9uLiBHbyB0byAnU2Vzc2lvbicsIHNjcm9sbCBkb3duIHRvICdTZXQgV29ya2luZyBEaXJlY3RvcnknLCBhbmQgY2xpY2sgJ1RvIFNvdXJjZSBGaWxlIExvY2F0aW9uJy4gUmVhZCBjYXJlZnVsbHkgdGhlIGJlbG93IGFuZCBmb2xsb3cgdGhlIGluc3RydWN0aW9ucyB0byBjb21wbGV0ZSB0aGUgdGFza3MgYW5kIGFuc3dlciBhbnkgcXVlc3Rpb25zLiAgU3VibWl0IHlvdXIgd29yayBpbiBTYWthaSBhcyBkZXRhaWxlZCBpbiBwcmV2aW91cyBub3Rlcy4gDQoNCiMjIyBOb3RlDQoNCkZvciB5b3VyIGFzc2lnbm1lbnQgeW91IG1heSBiZSB1c2luZyBkaWZmZXJlbnQgZGF0YSBzZXRzIHRoYW4gd2hhdCBpcyBpbmNsdWRlZCBoZXJlLiBBbHdheXMgcmVhZCBjYXJlZnVsbHkgdGhlIGluc3RydWN0aW9ucyBwcm92aWRlZCwgYmVmb3JlIGV4ZWN1dGluZyBhbnkgaW5jbHVkZWQgY29kZSBjaHVua3MgYW5kL29yIGFkZGluZyB5b3VyIG93biBjb2RlLiAgRm9yIGNsYXJpdHksIHRhc2tzL3F1ZXN0aW9ucyB0byBiZSBjb21wbGV0ZWQvYW5zd2VyZWQgYXJlIGhpZ2hsaWdodGVkIGluIHJlZCBjb2xvciBhbmQgbnVtYmVyZWQgYWNjb3JkaW5nIHRvIHRoZWlyIHBhcnRpY3VsYXIgcGxhY2VtZW50IGluIHRoZSB0YXNrIHNlY3Rpb24uICBUaGUgcmVkIGNvbG9yIGlzIG9ubHkgYXBwYXJlbnQgd2hlbiBpbiBQcmV2aWV3IG1vZGUuIFF1aXRlIG9mdGVuIHlvdSB3aWxsIG5lZWQgdG8gYWRkIHlvdXIgb3duIGNvZGUgY2h1bmsuDQoNCkV4ZWN1dGUgYWxsIGNvZGUgY2h1bmtzIChhbHJlYWR5IGluY2x1ZGVkIGFuZCBvd24gYWRkZWQpLCBwcmV2aWV3LCBjaGVjayBpbnRlZ3JpdHksIGFuZCBzdWJtaXQgZmluYWwgd29yayAoJGh0bWwkIGZpbGUpIGluIFNha2FpLg0KDQotLS0tLS0tLS0tLS0tLQ0KDQojIyMgVGFzayAxOiBTZW5zaXRpdml0eSBBbmFseXNpcw0KDQpTZW5zaXRpdml0eSBhbmFseXNpcyBpcyB0aGUgc3R1ZHkgb2YgaG93IHRoZSB1bmNlcnRhaW50eSBpbiB0aGUgb3V0cHV0IG9mIGEgbWF0aGVtYXRpY2FsIG1vZGVsIG9yIHN5c3RlbSAobnVtZXJpY2FsIG9yIG90aGVyd2lzZSkgY2FuIGJlIGFwcG9ydGlvbmVkIHRvIGRpZmZlcmVudCBzb3VyY2VzIG9mIHVuY2VydGFpbnR5IGluIGl0cyBpbnB1dHMuIFdlIHdpbGwgdXNlIHRoZSBgbHBTb2x2ZUFQSWAgUi1wYWNrYWdlIGFzIHdlIGRpZCBpbiB0aGUgcHJldmlvdXMgbGFiLiANCg0KSW4gb3JkZXIgdG8gY29uZHVjdCBhIHNlbnNpdGl2aXR5IGFuYWx5c2lzLCB3ZSB3aWxsIG5lZWQgdG8gZG93bmxvYWQgYWdhaW4gdGhlIGBscFNvbHZlQVBJYCBwYWNrYWdlIHVubGVzcyB5b3UgaGF2ZSBpdCBhbHJlYWR5IGluc3RhbGxlZCBpbiB5b3VyIFIgZW52aXJvbm1lbnQNCg0KYGBge3J9DQojIFJlcXVpcmUgd2lsbCBsb2FkIHRoZSBwYWNrYWdlIG9ubHkgaWYgbm90IGluc3RhbGxlZCANCiMgRGVwZW5kZW5jaWVzID0gVFJVRSBtYWtlcyBzdXJlIHRoYXQgZGVwZW5kZW5jaWVzIGFyZSBpbnN0YWxsDQppZighcmVxdWlyZSgibHBTb2x2ZUFQSSIscXVpZXRseSA9IFRSVUUpKQ0KICBpbnN0YWxsLnBhY2thZ2VzKCJscFNvbHZlQVBJIixkZXBlbmRlbmNpZXMgPSBUUlVFLCByZXBvcyA9ICJodHRwczovL2Nsb3VkLnItcHJvamVjdC5vcmciKQ0KYGBgDQoNCldlIHdpbGwgcmV2aXNpdCBhbmQgc29sdmUgYWdhaW4gdGhlIG1hcmtldGluZyBjYXNlIGRpc2N1c3NlZCBpbiBjbGFzcyAoYWxzbyBwYXJ0IG9mIHByZXZpb3VzIGxhYikuIA0KYGBge3J9DQojIFdlIHN0YXJ0IHdpdGggYDBgIGNvbnN0cmFpbnQgYW5kIGAyYCBkZWNpc2lvbiB2YXJpYWJsZXMuIFRoZSBvYmplY3QgbmFtZSBgbHBtYXJrYCBpcyBkaXNjcmV0aW9uYXJ5Lg0KbHBtYXJrID0gbWFrZS5scCgwLCAyKQ0KDQojIERlZmluZSB0eXBlIG9mIG9wdGltaXphdGlvbiBhcyBtYXhpbXVtIGFuZCBkdW1wIHRoZSBzY3JlZW4gb3V0cHV0IGludG8gYSBgZHVtbXlgIHZhcmlhYmxlDQpkdW1teSA9IGxwLmNvbnRyb2wobHBtYXJrLCBzZW5zZT0ibWF4IikgDQojIFNldCB0aGUgb2JqZWN0aXZlIGZ1bmN0aW9uIGNvZWZmaWNpZW50cyANCnNldC5vYmpmbihscG1hcmssIGMoMjc1LjY5MSwgNDguMzQxKSkNCmBgYA0KDQpBZGQgYWxsIGNvbnN0cmFpbnRzIHRvIHRoZSBtb2RlbC4NCg0KYGBge3J9DQphZGQuY29uc3RyYWludChscG1hcmssIGMoMSwgMSksICI8PSIsIDM1MDAwMCkNCmFkZC5jb25zdHJhaW50KGxwbWFyaywgYygxLCAwKSwgIj49IiwgMTUwMDApDQphZGQuY29uc3RyYWludChscG1hcmssIGMoMCwgMSksICI+PSIsIDc1MDAwKQ0KYWRkLmNvbnN0cmFpbnQobHBtYXJrLCBjKDIsIC0xKSwgIj0iLCAwKQ0KYWRkLmNvbnN0cmFpbnQobHBtYXJrLCBjKDEsIDApLCAiPj0iLCAwKQ0KYWRkLmNvbnN0cmFpbnQobHBtYXJrLCBjKDAsIDEpLCAiPj0iLCAwKQ0KYGBgDQoNCk5vdywgdmlldyB0aGUgcHJvYmxlbSBzZXR0aW5nIGluIHRhYnVsYXIvbWF0cml4IGZvcm0uIFRoaXMgaXMgYSBnb29kIGNoZWNrcG9pbnQgdG8gY29uZmlybSB0aGF0IG91ciBjb250cmFpbnRzIGhhdmUgYmVlbiBwcm9wZXJseSBzZXQuDQoNCmBgYHtyfQ0KbHBtYXJrDQojIHNvbHZlDQpzb2x2ZShscG1hcmspIA0KYGBgDQoNCk5leHQgd2UgZ2V0IHRoZSBvcHRpbXVtIHJlc3VsdHMuDQpgYGB7cn0NCiMgZGlzcGxheSB0aGUgb2JqZWN0aXZlIGZ1bmN0aW9uIG9wdGltdW0gdmFsdWUNCmdldC5vYmplY3RpdmUobHBtYXJrKQ0KDQojIGRpc3BsYXkgdGhlIGRlY2lzaW9uIHZhcmlhYmxlcyBvcHRpbXVtIHZhbHVlcw0KZ2V0LnZhcmlhYmxlcyhscG1hcmspDQpgYGANCg0KRm9yIHNlbnNpdGl2aXR5IHdlIHdpbGwgaW50cm9kdWNlIHRoZSBjb2RlIGNvbW1hbmQgdG8gb2J0YWluIHRoZSBzZW5zaXRpdml0aWVzIGR1ZSB0byBjaGFuZ2VzIGluIHJlc291cmNlcyBhcyBleHByZXNzZWQgaW4gdGhlIHJpZ2h0IGhhbmQgc2lkZSAocmhzKSBjb25zdHJhaW50cy4gDQoNCmBgYHtyfQ0KIyBkaXNwbGF5IHNlbnNpdGl2aXR5IHRvIHJocyBjb25zdHJhaW50cy4gDQojIFRoZXJlIHdpbGwgYmUgYSB0b3RhbCBvZiBtK24gdmFsdWVzIHdoZXJlIG0gaXMgdGhlIG51bWJlciBvZiBjb250cmFpbnRzIGFuZCBuIGlzIHRoZSBudW1iZXIgb2YgZGVjaXNpb24gdmFyaWFibGVzDQpnZXQuc2Vuc2l0aXZpdHkucmhzKGxwbWFyaykgDQpgYGANCg0KRm9yIHRoaXMgZXhlcmNpc2Ugd2UgYXJlIG9ubHkgaW50ZXJlc3RlZCBpbiB0aGUgZmlyc3Qgc2l4IHZhbHVlcyAoY29ycmVzcG9uZGluZyB0byB0aGUgc2l4IGNvbnN0cmFpbnRzKSBvZiB0aGUgb3V0cHV0IGxhYmVsZWQgYGR1YWxzYC4gDQoNCjxzcGFuIHN0eWxlPSJjb2xvcjpyZWQiPg0KIyMjIyMgMUEpICBJZGVudGlmeSBhbmQgZXhwbGFpbiB3aGF0IHRoZSBiaW5kaW5nL25vbi1iaW5kaW5nIGNvbnN0cmFpbnRzLCB0aGUgc3VycGx1cy9zbGFjaywgYW5kIHRoZSBtYXJnaW5hbCB2YWx1ZXMgYXJlIGFuZCB3aGF0IHRoZXkgcmVwcmVzZW50IGluIHRoZSBjb250ZXh0IG9mIG91ciBtYXJrZXRpbmcgdXNlIGNhc2UgDQo8L3NwYW4+DQoNClRoZSBmaXJzdCBjb25zdHJhaW50IHhyYWRpbyt4dHY8PTM1MDAwMCBpcyBhIGJpbmRpbmcgY29uc3RyYWludC4gSWYgdGhlIG1heGltdW0gYnVkZ2V0IGluY3JlYXNlZCBieSAxIGRvbGxhciwgdGhlbiB0aGUgb3B0aW1hbCBzb2x1dGlvbiBjaGFuZ2VzLCBhbmQgdGhlIHNhbGVzIHZhbHVlIHdpbGwgaW5jcmVhc2UgYnkgMTI0LjEyLg0KVGhlIHNlY29uZCBjb25zdHJhaW50ICh4cmFkaW8gPj0gMTUwMDApIGlzIG5vbi1iaW5kaW5nIG9uIGFjY291bnQgb2YgdGhlIHplcm8gdmFsdWUuIGNhbGN1bGF0aW9uIGlzIGJlbG93Lg0KYGBge3J9DQpzdXJwbHVzX2NfMj0gZ2V0LnZhcmlhYmxlcyhscG1hcmspWzFdLTE1MDAwDQpzdXJwbHVzX2NfMg0KYGBgDQpUaGUgdGhpcmQgY29uc3RyYWludCBvZiB4dHYgPj0gNzUwMDAgaXMgbm9uLWJpbmRpbmcuIFN1cnBsdXMgY2FsY3VsYXRlZCBhcyBmb2xsb3dzOg0KYGBge3J9DQpzdXJwbHVzX2NfMyA9IGdldC52YXJpYWJsZXMobHBtYXJrKVsyXS03NTAwMA0Kc3VycGx1c19jXzMNCmBgYA0KdGhlIGZvdXJ0aCBjb25zdHJhaW50IDJYcmFkaW8gLSBYdHYgPSAwIGlzIGJpbmRpbmcuIElmIHRoZSByaWdodCBoYW5kIHNpZGUgb2YgdGhlIGVxdWF0aW9uIHdhcyBpbmNyZWFzZWQgYnkgMSBkb2xsYXIsIHRoZW4gdGhlIG9wdGltYWwgc29sdXRpb24gd2lsbCBjaGFuZ2UsIHNhbGVzIHZhbHVlIHdpbGwgaW5jcmVhc2UgYnkgbWFyZ2luYWwgdmFsdWUgJDc1Ljc4DQoNClRvIGFjcXVpcmUgYSBiZXR0ZXIgdW5kZXJzdGFuZGluZyBvZiB0aGUgc2Vuc2l0aXZpdHkgcmVzdWx0cywgYW5kIHRvIGNvbmZpcm0gaW50ZWdyaXR5IG9mIHRoZSBjYWxjdWxhdGlvbnMsIGluZGVwZW5kZW50IHRlc3RzIGNhbiBiZSBjb25kdWN0ZWQuICBUbyBkZW1vbnN0cmF0ZSwgd2Ugd2lsbCByZXBlYXQgdGhlIGxpbmVhciBwcm9ncmFtbWluZyAoTFApIG9wdGltaXphdGlvbiBwcm9ibGVtIGJ5IHNsaWdodGx5IHR3ZWFraW5nIG9uZSBiaW5kaW5nIGNvbnN0cmFpbnQgYW5kIHZlcmlmeWluZyB0aGUgaW1wYWN0Lg0KDQo8c3BhbiBzdHlsZT0iY29sb3I6cmVkIj4NCiMjIyMjIDFCKSAgRGVmaW5lIGEgbmV3IG1vZGVsIG9iamVjdCBgbHBtYXJrMWAgYW5kIGFkZCB0aGUgY29uc3RyYWludHMgZXhhY3RseSBhcyBlbnRlcmVkIGF0IGJlZ2lubmluZyBvZiB0aGlzIHRhc2suIEFsbCBiZWluZyBlcXVhbCwgY2hhbmdlIHRoZSBidWRnZXQgY29uc3RyYWludCBieSBhIG1hcmdpbmFsICQxIHZhbHVlIGFuZCBzb2x2ZSB0aGUgTFAgcHJvYmxlbS4gIE5vdGUgdGhlIG5ldyBvcHRpbXVtIHZhbHVlIGZvciBzYWxlcy4gQ2FsY3VsYXRlIHRoZSBkaWZmZXJlbnRpYWwgY2hhbmdlIGluIG9wdGltdW0gc2FsZXMgZnJvbSB0aGUgZWFybGllciBjb21wdXRlZCBvcHRpbXVtIHNhbGVzLCAgYW5kIGNvbXBhcmUgeW91ciBjYWxjdWxhdGlvbiB0byB0aGUgdmFsdWUgb2J0YWluZWQgZnJvbSB0aGUgcHJpb3Igc2Vuc2l0aXZpdHkgY2FsY3VsYXRpb24gDQo8L3NwYW4+DQoNCmBgYHtyfQ0KIyBEZWZpbmUgYSBuZXcgbW9kZWwgb2JqZWN0IGNhbGxlZCBscG1hcmsxDQpscG1hcmsxID0gbWFrZS5scCgwLCAyKQ0KIyBSZXBlYXQgcmVzdCBvZiBjb21tYW5kcyB3aXRoIHRoZSBvbmUgY29uc3RyYWludCBjaGFuZ2UgZm9yIGJ1ZGdldC4gU29sdmUgYW5kIGRpc3BsYXkgdGhlIG9iamVjdGl2ZSBmdW5jdGlvbiBvcHRpbXVtIHZhbHVlDQoNCmR1bW15ID0gbHAuY29udHJvbChscG1hcmsxLCBzZW5zZT0ibWF4IikgDQoNCnNldC5vYmpmbihscG1hcmsxLCBjKDI3NS42OTEsIDQ4LjM0MSkpDQoNCmFkZC5jb25zdHJhaW50KGxwbWFyazEsIGMoMSwgMSksICI8PSIsIDM1MDAwMSkNCmFkZC5jb25zdHJhaW50KGxwbWFyazEsIGMoMSwgMCksICI+PSIsIDE1MDAwKQ0KYWRkLmNvbnN0cmFpbnQobHBtYXJrMSwgYygwLCAxKSwgIj49IiwgNzUwMDApDQphZGQuY29uc3RyYWludChscG1hcmsxLCBjKDIsIC0xKSwgIj0iLCAwKQ0KYWRkLmNvbnN0cmFpbnQobHBtYXJrMSwgYygxLCAwKSwgIj49IiwgMCkNCmFkZC5jb25zdHJhaW50KGxwbWFyazEsIGMoMCwgMSksICI+PSIsIDApDQoNCmxwbWFyazENCg0Kc29sdmUobHBtYXJrMSkgDQoNCmdldC5vYmplY3RpdmUobHBtYXJrMSkNCmdldC52YXJpYWJsZXMobHBtYXJrMSkgDQpgYGANCkhlcmUgd2UgY2FuIHN1YnRyYWN0IG91ciBvcHRpbXVtIHZhbHVlcyB0byBnZXQgdGhlIG1hcmdpbmFsIGNoYW5nZS4NCmBgYHtyfQ0KZ2V0Lm9iamVjdGl2ZShscG1hcmsxKS1nZXQub2JqZWN0aXZlKGxwbWFyaykNCmBgYA0KSGVyZSB3ZSBjYW4gc2VlIHRoYXQgdGhlIG1hcmdpbmFsIGNoYW5nZSBpbiBzYWxlcyB2YWx1ZSBpcyB0aGUgYW1vdW50IHdlIGNhbGN1bGF0ZWQgZm9yIHRoZSBwcmV2aW91cyBwcm9ibGVtLiANCg0KPHNwYW4gc3R5bGU9ImNvbG9yOnJlZCI+DQojIyMjIyAxQykgIEZvbGxvd2luZyB0aGUgbG9naWMgb2YgY2FsY3VsYXRpb25zIGluIDFCKSBhbmQgd2l0aG91dCBydW5uaW5nIGFub3RoZXIgY29kZSwgZXhwbGFpbiB0aGUgc3RlcHMgdG8gdmFsaWRhdGUgdGhlIGludGVncml0eSBvZiB0aGUgb3RoZXIgcmVtYWluaW5nIG1hcmdpbmFsIHZhbHVlIGlkZW50aWZpZWQgaW4gMUEpLiBVc2UgYSBkaWZmZXJlbnQgbGluZSB0byBleHBsYWluIGVhY2ggc3RlcCANCjwvc3Bhbj4NCg0KUmV2ZXJ0IHRoZSByaWdodCBoYW5kIHNpZGUgb2YgdGhlIGZpcnN0IGNvbnN0cmFpbnQgYmFjayB0byAzNTAwMDAsIHRoZW4gYWRkIDEgdG8gdGhlIHJpZ2h0IGhhbmQgc2lkZSBvZiB0aGUgc2Vjb25kIGNvbnN0cmFpbnQsIGtlZXAgdGhlIG90aGVyIGNvbnN0cmFpbnRzIGVxdWFsLCB0aGVuIGNhbGN1bGF0ZSB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSBuZXcgb3B0aW11bSB2YWx1ZSBhbmQgdGhlIG9yaWdpbmFsIG9wdGltdW0gdmFsdWUuDQoNClJldmVydCB0aGUgcmlnaHQgaGFuZCBzaWRlIG9mIHRoZSBzZWNvbmQgY29uc3RyYWludCBiYWNrIHRvIDE1MDAwLCB0aGVuIGFkZCAxIHRvIHRoZSByaWdodCBoYW5kIHNpZGUgb2YgdGhlIHRoaXJkIGNvbnN0cmFpbnQsIGtlZXAgdGhlIG90aGVyIGNvbnN0cmFpbnRzIGVxdWFsLCB0aGVuIGNhbGN1bGF0ZSB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSBuZXcgb3B0aW11bSB2YWx1ZSBhbmQgdGhlIG9yaWdpbmFsIG9wdGltdW0gdmFsdWUuDQoNClJldmVydCB0aGUgcmlnaHQgaGFuZCBzaWRlIG9mIHRoZSB0aGlyZCBjb25zdHJhaW50IGJhY2sgdG8gNzUwMDAwLCB0aGVuIGFkZCAxIHRvIHRoZSByaWdodCBoYW5kIHNpZGUgb2YgdGhlIGZvdXJ0aCBjb25zdHJhaW50LCBrZWVwIHRoZSBvdGhlciBjb25zdHJhaW50cyBlcXVhbCwgdGhlbiBjYWxjdWxhdGUgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgbmV3IG9wdGltdW0gdmFsdWUgYW5kIHRoZSBvcmlnaW5hbCBvcHRpbXVtIHZhbHVlLg0KLS0tLS0tLS0tLQ0KDQojIyMgVGFzayAyOiBSYW5kb20gU2FtcGxpbmcgJiBNb250ZSBDYXJsbyBTaW11bGF0aW9uDQoNCk1vbnRlIENhcmxvIFNpbXVsYXRpb25zIHV0aWxpemUgcmVwZWF0ZWQgcmFuZG9tIHNhbXBsaW5nIGZyb20gYSBnaXZlbiB1bml2ZXJzZSBvciBwb3B1bGF0aW9uIGRpc3RyaWJ1dGlvbiB0byBkZXJpdmUgY2VydGFpbiByZXN1bHRzLiBUaGlzIHR5cGUgb2Ygc2ltdWxhdGlvbiBpcyBrbm93biBhcyBhIHByb2JhYmlsaXN0aWMgc2ltdWxhdGlvbiwgYXMgb3Bwb3NlZCB0byBhIGRldGVybWluaXN0aWMgc2ltdWxhdGlvbi4NCg0KQW4gZXhhbXBsZSBvZiBhIE1vbnRlIENhcmxvIHNpbXVsYXRpb24gaXMgdGhlIG9uZSBhcHBsaWVkIHRvIGFwcHJveGltYXRlIHRoZSB2YWx1ZSBvZiBgcGlgLiBUaGUgc2ltdWxhdGlvbiBpcyBiYXNlZCBvbiBnZW5lcmF0aW5nIHJhbmRvbSBwb2ludHMgd2l0aGluIGEgdW5pdCBzcXVhcmUgYW5kIHNlZSBob3cgbWFueSBwb2ludHMgZmFsbCB3aXRoaW4gdGhlIGNpcmNsZSBlbmNsb3NlZCBieSB0aGUgdW5pdCBzcXVhcmUgKG1hcmtlZCBpbiByZWQpLiBUaGUgdmFsdWUgb2YgYHBpYCBpcyBlc3RpbWF0ZWQgYnkgdGhlIG51bWJlciBvZiBwb2ludHMgaW5zaWRlIHRoZSBjaXJjbGUgb3ZlciB0aGUgdG90YWwgbnVtYmVyIG9mIHBvaW50cyBnZW5lcmF0ZWQgaW5zaWRlIHRoZSBzcXVhcmUuICBUaGUgaGlnaGVyIHRoZSBudW1iZXIgb2Ygc2FtcGxlZCBwb2ludHMgdGhlIGNsb3NlciB0aGUgcmVzdWx0IGlzIHRvIHRoZSBhY3R1YWwgcmVzdWx0LiAgQWZ0ZXIgc2VsZWN0aW5nIDMwLDAwMCByYW5kb20gcG9pbnRzLCB0aGUgZXN0aW1hdGUgZm9yIGBwaWAgaXMgbXVjaCBjbG9zZXIgdG8gdGhlIGFjdHVhbCB2YWx1ZSB3aXRoaW4gdGhlIGZvdXIgZGVjaW1hbCBwb2ludHMgb2YgcHJlY2lzaW9uLiBUaGUgaW50ZXJlc3RlZCBhbmQgY3VyaW91cyByZWFkZXIgaXMgZW5jb3VyYWdlZCB0byBleHBsb3JlIHRoZSBtYXRoZW1hdGljYWwgZm91bmRhdGlvbiBiZWhpbmQgdGhlIGxvZ2ljLg0KDQohW10obW9udGVjYXJsby5naWYpDQoNCg0KRm9yIHRoaXMgdGFzayB3ZSB3aWxsIGJlIHJ1bm5pbmcgYSBNb250ZSBDYXJsbyBzaW11bGF0aW9uIHRvIGNhbGN1bGF0ZSB0aGUgcHJvYmFiaWxpdHkgdGhhdCB0aGUgZGFpbHkgcmV0dXJuIGZyb20gUyZQIHdpbGwgYmUgPiA1JS4gDQpXZSB3aWxsIGFzc3VtZSB0aGF0IHRoZSBoaXN0b3JpY2FsIFMmUCBkYWlseSByZXR1cm4gZm9sbG93cyBhIG5vcm1hbCBkaXN0cmlidXRpb24gd2l0aCBhbiBhdmVyYWdlIGRhaWx5IHJldHVybiBvZiAwLjAzICglKSBhbmQgYSBzdGFuZGFyZCBkZXZpYXRpb24gb2YgMC45NyAoJSkuIENvbnNpZGVyIHRoaXMgYXMgb3VyIHBvcHVsYXRpb24uDQoNClRvIGJlZ2luIHdlIHdpbGwgZ2VuZXJhdGUgMTAwIHJhbmRvbSBzYW1wbGVzIGZyb20gdGhlIG5vcm1hbCBkaXN0cmlidXRpb24uIEZvciB0aGUgZ2VuZXJhdGVkIHNhbXBsZXMgd2Ugd2lsbCBjYWxjdWxhdGUgdGhlIG1lYW4sIHN0YW5kYXJkIGRldmlhdGlvbiwgYW5kIHByb2JhYmlsaXR5IG9mIG9jY3VycmVuY2Ugd2hlcmUgdGhlIHNpbXVsYXRpb24gcmVzdWx0IGlzIGdyZWF0ZXIgdGhhbiA1JS4gDQoNClRvIGdlbmVyYXRlIHJhbmRvbSBzYW1wbGVzIGZyb20gYSBub3JtYWwgZGlzdHJpYnV0aW9uIHdlIHdpbGwgdXNlIHRoZSBgcm5vcm0oKWAgZnVuY3Rpb24gaW4gUi4gSW4gdGhlIGV4YW1wbGUgYmVsb3cgd2Ugc2V0IHRoZSBudW1iZXIgb2YgcnVucyAob3Igc2FtcGxlcykgdG8gMTAwLg0KDQpgYGB7cn0NCiMgbnVtYmVyIG9mIHNpbXVsYXRpb25zL3NhbXBsZXMNCnJ1bnMgPSAxMDANCiMgcmFuZG9tIG51bWJlciBnZW5lcmF0b3IgcGVyIGRlZmluZWQgbm9ybWFsIGRpc3RyaWJ1dGlvbiB3aXRoIGdpdmVuIG1lYW4gYW5kIHN0YW5kYXJkIGRldmlhdGlvbg0Kc2ltcyA9ICBybm9ybShydW5zLG1lYW49MC4wMyxzZD0wLjk3KQ0KYGBgDQoNCmBgYHtyfQ0KIyBNZWFuIGNhbGN1bGF0ZWQgZnJvbSB0aGUgcmFuZG9tIGRpc3RyaWJ1dGlvbiBvZiBzYW1wbGVzDQphdmVyYWdlID0gbWVhbihzaW1zKQ0KYXZlcmFnZQ0KYGBgDQoNCmBgYHtyfQ0KIyBTVEQgY2FsY3VsYXRlZCBmcm9tIHRoZSByYW5kb20gZGlzdHJpYnV0aW9uIG9mIHNhbXBsZXMNCnN0ZCA9IHNkKHNpbXMpIA0Kc3RkDQpgYGANCg0KYGBge3J9DQojIHByb2JhYmlsaXR5IG9mIG9jY3VycmVuY2Ugb24gYW55IGdpdmVuIGRheSBiYXNlZCBvbiBzYW1wbGVzIHdpbGwgYmUgZXF1YWwgdG8gY291bnQgKG9yIHN1bSkgd2hlcmUgc2FtcGxlIHJlc3VsdCBpcyBncmVhdGVyIHRoYW4gNSUgZGl2aWRlZCBieSB0b3RhbCBudW1iZXIgb2Ygc2FtcGxlcy4gDQpwcm9iID0gc3VtKHNpbXMgPj0wLjA1KS9ydW5zDQpwcm9iDQpgYGANCg0KPHNwYW4gc3R5bGU9ImNvbG9yOnJlZCI+DQojIyMjIyAyQSkgIFJlcGVhdCB0aGUgYWJvdmUgY2FsY3VsYXRpb25zIGZvciB0aGUgdHdvIGNhc2VzIHdoZXJlIHRoZSBudW1iZXIgb2Ygc2ltdWxhdGlvbnMvc2FtcGxlcyBpcyBlcXVhbCB0byAxMDAwIGFuZCAxMDAwMC4gIEZvciBlYWNoIGNhc2UgcmVjb3JkIHRoZSBtZWFuLCBzdGFuZGFyZCBkZXZpYXRpb24sIGFuZCBwcm9iYWJpbGl0eSANCjwvc3Bhbj4NCg0KYGBgYHtyfQ0KIyBSZXBlYXQgY2FsY3VsYXRpb25zIGhlcmUNCnJ1bnMxID0gMTAwMA0KDQpzaW1zMSA9ICBybm9ybShydW5zMSxtZWFuPTAuMDMsc2Q9MC45NykNCg0KYXZlcmFnZTEgPSBtZWFuKHNpbXMxKQ0KYXZlcmFnZTENCg0Kc3RkMSA9IHNkKHNpbXMxKSANCnN0ZDENCg0KcHJvYjEgPSBzdW0oc2ltczEgPj0wLjA1KS9ydW5zMQ0KcHJvYjENCg0KDQpydW5zMiA9IDEwMDAwDQoNCnNpbXMyID0gIHJub3JtKHJ1bnMyLG1lYW49MC4wMyxzZD0wLjk3KQ0KDQphdmVyYWdlMiA9IG1lYW4oc2ltczIpDQphdmVyYWdlMg0KDQpzdGQyID0gc2Qoc2ltczIpIA0Kc3RkMg0KDQpwcm9iMiA9IHN1bShzaW1zMiA+PTAuMDUpL3J1bnMyDQpwcm9iMg0KYGBgDQoNCjxzcGFuIHN0eWxlPSJjb2xvcjpyZWQiPg0KIyMjIyMgMkIpICBMaXN0IGluIHNlcGVyYXRlIGxpbmVzIHRoZSB2YWx1ZXMgZm9yIG1lYW4sIHN0YW5kYXJkIGRldmlhdGlvbiwgYW5kIHByb2JhYmlsaXR5IGZvciBhbGwgdGhyZWUgY2FzZXM6IDEwMCwgMTAwMCwgYW5kIDEwMDAwIHNpbXVsYXRpb25zLiBEZXNjcmliZSBob3cgdGhlIHZhbHVlcyBjaGFuZ2UvYmVoYXZlIGFzIHRoZSBudW1iZXIgb2Ygc2ltdWxhdGlvbnMgaXMgaW5jcmVhc2VkLiBXaGF0IGlzIHlvdXIgYmVzdCBiZXQgb24gdGhlIHByb2JhYmlsaXR5IG9mIG9jY3VycmVuY2UgZ3JlYXRlciB0aGFuIDUlIGFuZCB3aHk/IEhvdyBpcyB0aGlzIGJlaGF2aW9yIHNpbWlsYXIgdG8gdGhlIHVzZSBjYXNlIGRlbW9uc3RyYXRpb24gdG8gY2FsY3VsYXRlIGBwaWA/ICAgDQo8L3NwYW4+DQpydW5zIDEwMCANCm1lYW4gPSAwLjEyNzI2MTMNCnNkID0gMS4wNzg4ODMNCnByb2IgPSAwLjUNCg0KcnVucyAxMDAwDQptZWFuID0gMC4wMjMyNDAxOA0Kc2QgPSAwLjk2ODYxMzYNCnByb2IgPSAwLjUwMQ0KDQpydW5zIDEwMDAwDQptZWFuID0gMC4wMjYyMzI4DQpzZCA9IDAuOTY2NTQ3NQ0KcHJvYiA9IDAuNDg5OA0KDQpUaGUgbGFyZ2VyIHRoZSBzYW1wbGUsIHRoZSBjbG9zZXIgd2UgZ2V0IHRvIHRoZSBhdmVyYWdlIGFuZCBzdGFuZGFyZCBkZXZpYXRpb24gdmFsdWVzIHdlIHdlcmUgZ2l2ZW4gYXQgdGhlIGJlZ2lubmluZyBvZiB0aGUgcHJvYmxlbS4gVGhhdCBiZWluZyBzYWlkLCB0aGUgYmVzdCBwcm9iYWJpbGl0eSBoZXJlIGlzIHByb2JhYmx5IHRoZSAwLjQ4OTggdGhhdCB3YXMgZ2l2ZW4gYnkgdGhlIG1vZGVsIG9mIDEwMDAwIHJ1bnMuDQojIyMgVGFzayAzOiBUcmFpbmluZyBTZXQgJiBNb2RlbCBCdWlsZGluZw0KDQpUaGUgZmlyc3QgaGFsZiBvZiB0aGlzIGxhYiBmb2N1c2VzIG9uIGJ1aWxkaW5nIGEgbW9kZWwgdXNpbmcgdGhlIHRyYWluaW5nIGRhdGEuIFRoZSBkYXRhIHdlIHdpbGwgYmUgdXNpbmcgd2FzIG9idGFpbmVkIGZyb20gS2FnZ2xlIHNpdGVbMV0gYW5kIGlzIGluIHJlZmVyZW5jZSB0byB3b3JsZCB1bml2ZXJzaXR5IHJhbmtpbmdzIFsyXSAuIFRoZSBkYXRhIGxvb2tzIGF0IHVuaXZlcnNpdHkgd29ybGQgc2NvcmluZyBiYXNlZCBvbiBkaWZmZXJlbnQgcmFua2luZyBjcml0ZXJpYSBzdWNoIGFzIHF1YWxpdHkgb2YgZWR1Y2F0aW9uLCBxdWFsaXR5IG9mIGZhY3VsdHksIGFuZCByYW5rIGZvciBwYXRlbnRzLiBTcGVuZCBzb21lIHRpbWUgdG8gdmlzaXQgdGhlIHJlZmVyZW5jZWQgd2Vic2l0ZSB0byBnZXQgbW9yZSBhY3F1aW5hdGVkIHdpdGggdGhlIGRhdGEuICBUaGUgZGF0YSBvYnRhaW5lZCBpcyBkaXZpZGVkIGludGVvIHR3byBzZXRzOiBhIHRyYWluaW5nIHNldCBhbmQgYSB0ZXN0aW5nIHNldC4gV2UgYmVnaW4gYnkgcmVhZGluZyB0aGUgdHJhaW5pbmcgZGF0YSBzZXQgJ3VuaXZlcnNpdHlyYW5rX3RyYWluaW5nLmNzdicgZmlsZSwgYW5kIGNoZWNraW5nIHRoZSBoZWFkZXIgbGluZXMgdG8gbWFrZSBzdXJlIHRoZSBkYXRhIGlzIHJlYWQgY29ycmVjdGx5LiANCg0KYGBge3J9DQp0cmFpbmRhdGEgPSByZWFkLmNzdihmaWxlPSJ1bml2ZXJzaXR5cmFua190cmFpbmluZy5jc3YiLCBoZWFkZXI9VFJVRSkNCmhlYWQodHJhaW5kYXRhKQ0KYGBgDQoNCk5leHQsIHdlIGV4dHJhY3QgdGhlIHR3byBjb2x1bW5zIG9mIGludGVyZXN0IGFuZCBjYWxsIHRoZW0gcHJvcGVybHkgc28gd2UgY2FuIGVhc2lseSByZWZlciB0byB0aGVtIGxhdGVyIGluIHRoZSBjb2RlLiANCg0KYGBge3J9DQpwYXRlbnRfdHJhaW4gPSB0cmFpbmRhdGEkcGF0ZW50cw0Kc2NvcmVfdHJhaW4gPSB0cmFpbmRhdGEkc2NvcmUNCmBgYA0KDQpUaGUgZmlyc3QgbW9kZWwgd2Ugd2lsbCBidWlsZCBpcyBhIHNpbXBsZSBsaW5lYXIgbW9kZWwuIFdlIHdpbGwgdXNlIHRoZSBwYXRlbnRzIHJhbmtpbmcgdmFyaWFibGUgdG8gcHJlZGljdCB0aGUgdW5pdmVyc2l0eSBzY29yZS4gIFRvIGJldHRlciB1bmRlcnN0YW5kIHRoZSBkYXRhLCB0aGUgbG93ZXIgdGhlIHBhdGVudHMgcmFua2luZyBudW1iZXIgdGhlIGJldHRlciBpdCBpcy4gIEEgdmFsdWUgb2YgMSBpcyBhIHRvcCByYW5rIGZvciBwYXRlbnRzIGFuZCByZXByZXNlbnQgdGhlIGhpZ2hlc3QgY2F0ZWdvcnkgaW4gdGVybXMgb2YgbnVtYmVyIG9mIHBhdGVudHMgb3duZWQgYnkgdGhlIHBhcnRpY3VsYXIgYWNhZGVtaWMgaW5zdGl0dXRpb24uIE9uIHRoZSBvdGhlciBoYW5kIHRoZSBoaWdoZXIgdGhlIGNhbGN1bGF0ZWQgdG90YWwgc2NvcmUgdGhlIGJldHRlciwgYXMgcmVmbGVjdGVkICBieSB0aGUgd29ybGQgcmFuayBudW1iZXIuIEEgIHZhbHVlIG9mIDEwMCBpcyBhIHBlcmZlY3Qgc2NvcmUuICANCg0KYGBge3J9DQpsaW5lYXJfdHJhaW4gPSBsbShzY29yZV90cmFpbiB+IHBhdGVudF90cmFpbikNCnN1bW1hcnkobGluZWFyX3RyYWluKQ0KDQpwbG90KHBhdGVudF90cmFpbixzY29yZV90cmFpbikNCmFibGluZShsaW5lYXJfdHJhaW4sIGNvbD0iYmx1ZSIsIGx3ZD0yKQ0KYGBgDQoNCjxzcGFuIHN0eWxlPSJjb2xvcjpyZWQiPg0KIyMjIyMgM0EpIENvbXBsZXRlIHRoZSBzdGVwcyBpbiB0aGUgY29kZSBjaHVuayBiZWxvdyB0byBidWlsZCBhIG5vbi1saW5lYXIgcXVhZHJhdGljIG1vZGVsLiBGb2xsb3cgdGhlIHN0ZXBzIHVzZWQgaW4gbGFiMDggZm9yIGNvc3RzIHZlcnN1cyBzZXJ2ZXJzIA0KPC9zcGFuPg0KDQpgYGB7cn0NCiMgRmlyc3QgZGVmaW5lIGEgbmV3IHZhcmlhYmxlIHdoaWNoIGlzIHRoZSBzcXVhcmVkIHZhbHVlIG9mIHBhdGVudF90cmFpbiAoZGVmaW5lZCBhYm92ZSkNCnBhdGVudF90cmFpbjIgPSBwYXRlbnRfdHJhaW5eMg0KIyBOZXh0IGRlcml2ZSB0aGUgcXVhZHJhdGljIHJlZ3Jlc3Npb24gbW9kZWwuICBZb3UgbWF5IHdhbnQgdG8gY2FsbCBpdCBxdWFkX3RyYWluLg0KcXVhZF90cmFpbiA9IGxtIChzY29yZV90cmFpbiB+IHBhdGVudF90cmFpbiArIHBhdGVudF90cmFpbjIpDQojIFB1Ymxpc2ggdGhlIHN1bW1hcnkgc3RhdGlzdGljcw0Kc3VtbWFyeShxdWFkX3RyYWluKQ0KYGBgDQpgYGB7cn0NCnNjb3JlX2ZpdHRlZCA9IGNvZWYocXVhZF90cmFpbilbMV0gKyBjb2VmKHF1YWRfdHJhaW4pWzJdICogcGF0ZW50X3RyYWluICsgY29lZihxdWFkX3RyYWluKVszXSAqIHBhdGVudF90cmFpbjINCg0KcGxvdChwYXRlbnRfdHJhaW4sc2NvcmVfdHJhaW4sIHBjaCA9IDE2KQ0KDQpwYXIobmV3PVRSVUUsIHhheHQ9Im4iLCB5YXh0PSJuIiwgYW5uPUZBTFNFKSANCg0KcGxvdChwYXRlbnRfdHJhaW4sIHNjb3JlX2ZpdHRlZCwgY29sID0gInJlZCIsIHBjaCA9IDE2KQ0KYGBgDQoNCjxzcGFuIHN0eWxlPSJjb2xvcjpyZWQiPg0KIyMjIyMgM0IpIExvb2tpbmcgYXQgdGhlIHZhbHVlcyBmb3IgUi1TcXVhcmVkIGFuZCBBZGp1c3RlZCBSLVNxdWFyZWQgZm9yIGJvdGggdGhlIGxpbmVhciBhbmQgdGhlIHF1YWRyYXRpYyBzZWxlY3QgdGhlIGJlc3QgcHJlZGljdGl2ZSBtb2RlbC4gRXhwbGFpbiB5b3VyIGxvZ2ljIA0KPC9zcGFuPg0KDQpUaGUgcXVhZHJhdGljIG1vZGVsIGlzIGJlc3Qgb24gYWNjb3VudCBvZiBpdHMgUiBTcXVhcmVkIGFuZCBBZGp1c3RlZCBSIFNxdWFyZWQgdmFsdWVzLCBpbmRpY2F0aW5nIGl0IHRvIGJlIHRoZSBiZXN0IHByZWRpY3RpdmUgbW9kZWwuIA0KLS0tLS0tLS0tLQ0KDQojIyMgVGFzayA0OiBUZXN0aW5nIFNldCAmIE1vZGVsIEV2YWx1YXRpb24NCg0KVGhlIHNlY29uZCBoYWxmIG9mIHByZWRpY3RpdmUgbW9kZWxpbmcgaXMgYWJvdXQgdGVzdGluZyB0aGUgbW9kZWwgdXNpbmcgYSBkaWZmZXJlbnQgZGF0YSBzZXQgY2FsbGVkIHRoZSB0ZXN0aW5nIGRhdGEuIEFnYWluIHdlIG11c3QgZmlyc3QgcmVhZCB0aGUgdGVzdGluZyBkYXRhIHNldCwgYW5kIG1ha2Ugc3VyZSB0aGUgZGF0YXNldCBpcyByZWFkIHByb3BlcnRseS4NCg0KYGBge3J9DQp0ZXN0ZGF0YSA9IHJlYWQuY3N2KCJ1bml2ZXJzaXR5cmFua190ZXN0aW5nLmNzdiIsIGhlYWRlcj1UUlVFKQ0KaGVhZCh0ZXN0ZGF0YSkNCmBgYA0KDQpXZSBleHRyYWN0IGFnYWluIHRoZSB0d28gY29sdW1ucyBvZiBpbnRlcmVzdCwgaW4gcmVmZXJlbmNlIHRoaXMgdGltZSB0byB0aGUgdGVzdGluZyBkYXRhIHNldCwgYW5kIGNhbGwgdGhlbSBhY2NvcmRpbmdseS4NCg0KYGBge3J9DQpwYXRlbnRfdGVzdCA9IHRlc3RkYXRhJHBhdGVudHMNCnNjb3JlX3Rlc3QgPSB0ZXN0ZGF0YSRzY29yZQ0KYGBgDQoNCldlIGFyZSByZWFkeSBub3cgdG8gY2hlY2sgaWYgdGhlIGRlcml2ZWQgbW9kZWxzIGFyZSBhY3R1YWxseSBnb29kIHByZWRpY3RpdmUgbW9kZWxzLiBGaXJzdCB3ZSBjYWxjdWxhdGUgdGhlIHByZWRpY3RlZCB0ZXN0IGRhdGEgc2NvcmUgdXNpbmcgdGhlIGxpbmVhciBtb2RlbCBhcyBkZXJpdmVkIGZyb20gdGhlIHRyYWluaW5nIHNldC4gTGF0ZXIgd2Ugd2lsbCBjb25zaWRlciB0aGUgcXVhZHJhdGljIG1vZGVsLiANCg0KYGBge3J9DQojIENhbGN1bGF0ZSB0aGUgcHJlZGljdGVkIHRlc3QgZGF0YSBzY29yZSANCnNjb3JlX3ByZWRpY3QxID0gY29lZihsaW5lYXJfdHJhaW4pWzFdICsgY29lZihsaW5lYXJfdHJhaW4pWzJdKnBhdGVudF90ZXN0DQpgYGANCg0KRm9yIGEgdmlzdWFsIHF1YWxpdGF0aXZlIGV2YWx1YXRpb24gd2UgY2FuIHBsb3QgdGhlIGFjdHVhbCB0ZXN0aW5nIGRhdGEsIGFuZCBvdmVybGF5IHRoZSBwcmVkaWN0ZWQgdmFsdWVzDQoNCmBgYHtyfQ0KIyBQbG90IHRoZSBhY3R1YWwgdmFsdWVzIGZvciBwYXRlbnQgYW5kIHNjb3JlIGFzIG9ic2VydmVkIGluIHRoZSB0ZXN0aW5nIGRhdGEgc2V0DQpwbG90KHBhdGVudF90ZXN0LCBzY29yZV90ZXN0LCBtYWluPSdUZXN0IERhdGEgLS0gU2NvcmUgdnMgUGF0ZW50JykNCiMgT3ZlcmxheSB0aGUgcHJlZGljdGVkIHZhbHVlcyBhcyBjYWxjdWxhdGVkIGZyb20gdGhlIGxpbmVhciBtb2RlbCBhbmQgZGVyaXZlZCB1c2luZyB0aGUgdHJhaW5pbmcgbW9kZWwNCnBhcihuZXc9VFJVRSwgeGF4dD0ibiIsIHlheHQ9Im4iLCBhbm49RkFMU0UpDQojIFRoZSByZWQgY29sb3IgaXMgdXNlZCB0byBkaXN0aW5ndWlzaCB0aGUgcHJlZGljdGVkIHZhbHVlcyB3aGljaCwgYmVjYXVzZSBvZiB0aGUgbGluZWFyIG1vZGVsLCB3aWxsIGZpdCBleGFjdGx5IGEgbGluZQ0KcGxvdChwYXRlbnRfdGVzdCwgc2NvcmVfcHJlZGljdDEsIGNvbD0icmVkIikNCmBgYA0KDQpBIGJldHRlciB3YXkgdG8gcXVhbGlmeSB0aGUgZ29vZG5lc3Mgb2YgYSBwcmVkaWN0aXZlIG1vZGVsIGlzIHRvIHNjYXR0ZXIgcGxvdCB0aGUgYWN0dWFsIHZhbHVlcyBhZ2FpbnN0IHRoZSBwcmVkaWN0ZWQgdmFsdWVzLiBJbiBhIHBlcmZlY3QgcHJlZGljdGl2ZSBtb2RlbCB0aGUgcG9pbnRzIHdpbGwgbGluZSB1cCBhbG9uZyB0aGUgZGlhZ29uYWwgbGluZS4gIFRoaXMgaXMgcmFyZWx5IHRoZSBjYXNlLCBpZiBldmVyIQ0KDQpgYGB7cn0NCiNQbG90IHByZWRpY3RlZCB2YWx1ZXMgZnJvbSB0aGUgbGluZWFyIG1vZGVsIHZlcnN1cyBhY3R1YWwgdmFsdWVzIGZvcm0gdGhlIHRlc3QgZGF0YQ0KcGxvdChzY29yZV90ZXN0LCBzY29yZV9wcmVkaWN0MSwgeGxhYj0nQWN0dWFsJywgeWxhYj0nUHJlZGljdCcsIG1haW49J0xpbmVhciBNb2RlbCAtLSBQcmVkaWN0IHZzIEFjdHVhbCBUZXN0JykNCmBgYA0KDQpGcm9tIHRoZSBwbG90IHdlIGNhbiBlYXNpbHkgc2VlIHRoYXQgbW9zdCBvZiB0aGUgcHJlZGljdGVkIHZhbHVlcyB2ZXJzdXMgYWN0dWFsIGFyZSBmYXIgZnJvbSB0aGUgZGlhZ29vbmFsIGxpbmUuIEluIG1hbnkgY2FzZXMgdGhpcyBpcyBmaW5lLiBGaW5hbGx5LCB0byBxdWFudGlmeSB0aGUgZ29vZG5lc3Mgb2YgYSAgbW9kZWwsIHdlIG5lZWQgdG8gY2FsY3VsYXRlIHRoZSBSb290IE1lYW4gU3F1YXJlIEVycm9yIChSTVNFKS4NCg0KYGBge3J9DQojQ2FsY3VsYXRlIFJNU0UgZm9yIExpbmVhciBNb2RlbA0KZXJyb3IxID0gc3VtKChzY29yZV9wcmVkaWN0MSAtIHNjb3JlX3Rlc3QpXjIpL2xlbmd0aChzY29yZV90ZXN0KQ0Kcm1zZTEgPSBzcXJ0KGVycm9yMSkNCnJtc2UxDQpgYGANCg0KDQpJdCBpcyBoYXJkIHRvIGp1ZGdlIHRoZSBnb29kbmVzcyBvZiB0aGUgbnVtYmVyIHVubGVzcyB3ZSBjb21wYXJlIHRvIG90aGVyIHBvc3NpYmlsaXRpZXMuIE9mIGNvdXJzZSBhIHBlcmZlY3Qgc2NlbmFyaW8gd2lsbCBoYXZlIHplcm8gUk1TRS4gV2Ugbm93IG5lZWQgdG8gcmVwZWF0IHRoZSBhYm92ZSBjYWxjdWxhdGlvbnMgZm9yIHRoZSBub24tbGluZWFyIHF1YWRyYXRpYyBtb2RlbC4NCg0KPHNwYW4gc3R5bGU9ImNvbG9yOnJlZCI+DQojIyMjIyA0QSkgRmlsbCBpbiB0aGUgY29kZSBjaHVuayBiZWxvdyB0byBjYWxjdWxhdGUgdGhlIHByZWRpY3RlZCB2YWx1ZXMgZm9yIHRoZSBub24tbGluZWFyIHF1YWRyYXRpYyBtb2RlbCANCjwvc3Bhbj4NCg0KYGBge3J9DQojIENhbGN1bGF0ZSBzY29yZV9wcmVkaWN0MiB1c2luZyB0aGUgcXVhZHJhdGljIG1vZGVsIGFzIGRlcml2ZWQgZWFybGllciBmcm9tIHRoZSB0cmFpbmluZyBkYXRhIGFuZCBhcyBhcHBsaWVkIHRvIHRoZSBhY3R1YWwgcGF0ZW50IHZhbHVlcyBvYnRhaW5lZCBmcm9tIHRoZSB0ZXN0aW5nIGRhdGENCnBhdGVudF90ZXN0MiA9IHBhdGVudF90ZXN0XjINCnNjb3JlX3ByZWRpY3QyID0gY29lZihxdWFkX3RyYWluKVsxXSArIGNvZWYocXVhZF90cmFpbilbMl0gKiBwYXRlbnRfdGVzdCArIGNvZWYocXVhZF90cmFpbilbM10gKiBwYXRlbnRfdGVzdDINCg0KYGBgDQoNCkZvciBhIHZpc3VhbCByZXByZXNlbnRhdGlvbiwgc2ltaWxhciB0byB0aGUgbGluZWFyIG1vZGVsLCB3ZSBuZWVkIHRvIGRvIHRoZSBmb2xsb3dpbmcuDQoNCjxzcGFuIHN0eWxlPSJjb2xvcjpyZWQiPg0KIyMjIyMgNEIpIEZpbGwtaW4gdGhlIGNvZGUgY2h1bmsgYmVsb3cgdG8gcGxvdCB0aGUgYWN0dWFsIFNjb3JlIHZzIFBhdGVudCBmb3IgdGhlIHRlc3QgZGF0YSwgYW5kIG92ZXJsYXkgdGhlIHByZWRpY3RlZCB2YWx1ZXMgYXMgY2FsY3VsYXRlZCBpbiAyQS4gTGFiZWwgYXhlcyBhbmQgdGl0bGUgcHJvcGVybHkgKDRwdHMpDQo8L3NwYW4+DQoNCmBgYHtyfQ0KIyBQbG90IHRoZSBhY3R1YWwgdmFsdWVzIGZvciBwYXRlbnQgYW5kIHNjb3JlIGFzIG9ic2VydmVkIGluIHRoZSB0ZXN0aW5nIGRhdGEgc2V0DQpwbG90KHBhdGVudF90ZXN0LCBzY29yZV90ZXN0LCBtYWluPSdUZXN0IERhdGEgLS0gU2NvcmUgdnMgUGF0ZW50JykNCiMgT3ZlcmxheSB0aGUgcHJlZGljdGVkIHZhbHVlcyBhcyBjYWxjdWxhdGVkIGZyb20gdGhlIHF1YWRyYXRpYyBtb2RlbCBhbmQgZGVyaXZlZCB1c2luZyB0aGUgdHJhaW5pbmcgbW9kZWwuIA0KcGFyKG5ldz1UUlVFLCB4YXh0PSJuIiwgeWF4dD0ibiIsIGFubj1GQUxTRSkNCiMgVGhlIGdyZWVuIGNvbG9yIGlzIHVzZWQgdG8gZGlzdGluZ3Vpc2ggdGhlIHByZWRpY3RlZCB2YWx1ZXMgd2hpY2gsIGJlY2F1c2Ugb2YgdGhlIHF1YWRyYXRpYyBtb2RlbCwgd2lsbCBpbiB0aGlzIGNhc2Ugd2lsbCBmaXQgZXhhY3RseSBhIHBhcmFib2xhDQpwbG90KHBhdGVudF90ZXN0LCBzY29yZV9wcmVkaWN0MiwgY29sPSJncmVlbiIpDQpgYGANCg0KPHNwYW4gc3R5bGU9ImNvbG9yOnJlZCI+DQojIyMjIDRDKSBQbG90IHRoZSBQcmVkaWN0IHZzIEFjdHVhbCBmb3IgdGhlIHF1YWRyYXRpYyBtb2RlbC4gIExhYmVsIGF4ZXMgYW5kIHRpdGxlIHByb3Blcmx5ICgycHRzKQ0KPC9zcGFuPg0KDQpgYGB7cn0NCiNQbG90IHRoZSBwcmVkaWN0ZWQgdmFsdWVzIGZvcm0gdGhlIHF1YWRyYXRpYyBtb2RlbCB2ZXJzdXMgdGhlIGFjdHVhbCB2YWx1ZXMgZnJvbSB0aGUgdGVzdCBkYXRhDQpwbG90KHNjb3JlX3Rlc3QsIHNjb3JlX3ByZWRpY3QyLCB4bGFiPSdBY3R1YWwnLCB5bGFiPSdQcmVkaWN0JywgbWFpbj0nUXVhZCBNb2RlbCAtLSBQcmVkaWN0IHZzIEFjdHVhbCBUZXN0JykNCmBgYA0KDQo8c3BhbiBzdHlsZT0iY29sb3I6cmVkIj4NCiMjIyMgNEQpIEJ5IGxvb2tpbmcgYXQgdGhlIHNjYXR0ZXJwbG90cyBmb3IgdGhlIGxpbmVhciBhbmQgcXVhZHJhdGljIG1vZGVscyBhcmUgeW91IGFibGUgdG8gdGVsbCB3aGljaCBtb2RlbCBpcyBiZXR0ZXI/IEV4cGxhaW4geW91ciBsb2dpYyAoMXB0KQ0KPC9zcGFuPg0KDQpRdWFkcmF0aWMgaXMgYSBiZXR0ZXIgZml0IGJlY2F1c2UgdGhlIGFjdHVhbCBwb2ludHMgbGluZSB1cCBhIHRhZCBiaXQgY2xvc2VyIGNvbXBhcmVkIHRvIHRoZSBsaW5lYXIgb25lLg0KDQpBIGJldHRlciB3YXkgaXMgdG8gcXVhbnRpZnkgdGhlIGdvb2RuZXNzIG9mIHRoZSBtb2RlbCBieSBjYWxjdWxhdGluZyBhZ2FpbiB0aGUgUk1TRS4NCg0KPHNwYW4gc3R5bGU9ImNvbG9yOnJlZCI+DQojIyMjIDRFKSBDYWxjdWxhdGUgdGhlIHJvb3QgbWVhbiBzcXVhcmUgZXJyb3IgKFJNU0UpIGZvciB0aGUgcXVhZHJhdGljIG1vZGVsICgycHRzKQ0KPC9zcGFuPg0KDQpgYGB7cn0NCiNDYWxjdWxhdGUgUk1TRSAgZm9yIFF1YWRyYXRpYyBNb2RlbA0KZXJyb3IyID0gc3VtKChzY29yZV9wcmVkaWN0MiAtIHNjb3JlX3Rlc3QpXjIpL2xlbmd0aChzY29yZV90ZXN0KQ0Kcm1zZTIgPSBzcXJ0KGVycm9yMikNCnJtc2UyDQpgYGANCg0KPHNwYW4gc3R5bGU9ImNvbG9yOnJlZCI+DQojIyMjIDRGKSBCYXNlZCBvbiB0aGUgcm9vdCBtZWFuIHNxdWFyZSBlcnJvciAoUk1TRSkgIHdoaWNoIG1vZGVsIGlzIGJldHRlcj8gSG93IHlvdXIgY29uY2x1c2lvbiByZWNvbmNpbGUgd2l0aCB0aGUgcmVzdWx0cyBmcm9tIFRhc2sgMT8gKDFwdCkNCjwvc3Bhbj4NCg0KUk1TRSBmb3IgdGhlIHF1YWRyYXRpYyBtb2RlbCBpcyBsZXNzIHRoYW4gdGhlIFJNU0UgZm9yIHRoZSBsaW5lYXIgbW9kZWwuIFRoaXMgbWVhbnMgdGhlcmUgaXMgbGVzcyBlcnJvciBpbiB0aGUgcHJlZGljdGlvbnMgb2YgdGhlIHF1YWRyYXRpYyBtb2RlbCwgbWFraW5nIGl0IHRoZSBiZXR0ZXIgcHJlZGljdG9yLg0KDQpzb3VyY2UgWzFdOiBbaHR0cDovL3d3dy5rYWdnbGUuY29tXShodHRwOi8vd3d3LmthZ2dsZS5jb20pDQoNCnNvdXJjZSBbMl06IFtodHRwOi8vd3d3LmN3dXIub3JnXShodHRwOi8vd3d3LmN3dXIub3JnKSANCg0KDQo=