Task 1: Sensitivity Analysis
In order to conduct the 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 the sensitivity part we will add two new code sections to obtain the sensitivity results. Frist, we will obtain the sensitivity values due to changes in the coefficients of the objective function.
# display sensitivity to coefficients of objective function.
get.sensitivity.obj(lpmark)
$`objfrom`
[1] -96.6820 -137.8455
$objtill
[1] 1e+30 1e+30
The first part of the output labeled objfrom displays the lower limit/boundary and the output labeled objtill displays the upper limit/boundary of the objective function coefficients sensitivity.
##### 1A) Explain in clear words what the sensitivity boundary values (lower/upper limits) represent in context of the marketing model. Refer to class notes.
The sensitivity boundary values represent how much we can how changes in the coefficients of the objective function will impact the optimum Z value, but not necessarily the optimum solution that are radio and tv. In the cases, we can tell that the lower limit is labeled ‘objfrom’ which the value -96.6820 and -137.8455, and the upper limit is labeled ‘objtill’ which the value is infinity.Also,if the coefficients change out that range, it woule affect the ridao, tv and sales. It says that the coefficients of two decision variable which are optimum solution radio and tv woule not chang by -96.6820 and -137.8455, but it is going to change the optimum value for sales.
Next we will obtain the sensitivity results due to changes in resources impacting the constraints.
# display sensitivity to right hand side 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.
##### 1B) Explain in clear words what each of the zero and non-zero sensitivity values represent. In your explanation identify the binding/non-binding constraints, the surplus/slack, and the marginal values.
The zero values represent that the constraint is non-binding, which means have surplus/slack and no marginal value, and it is not going to change the optimum value for sales. However, the non-zero means that the constraint is binding with no surplus or slack, and represent the marginal values change in the optimum value by adjusting a binding constraint. In this case, we can tell that there are four non-binding constraints and two binding constraints. For the binding constraint, if we increase in $ 1, the optimum value of sales will increase 124.12433 and 75.78333 as well in the first/fourth constraint.
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.
##### 1C) 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 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
# Define type of optimization as maximum and dump the screen output into a `dummy` variable
dummy = lp.control(lpmark1, sense="max")
# Set the objective function coefficients
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
solve(lpmark1)
[1] 0
# display the objective function optimum value
get.objective(lpmark1)
[1] 43443641
# display the decision variables optimum values
get.variables(lpmark1)
[1] 116667 233334
# display sensitivity to coefficients of objective function.
get.sensitivity.obj(lpmark1)
$`objfrom`
[1] -96.6820 -137.8455
$objtill
[1] 1e+30 1e+30
# display sensitivity to right hand side 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(lpmark1)
$`duals`
[1] 124.12433 0.00000 0.00000 75.78333 0.00000 0.00000 0.00000 0.00000
$dualsfrom
[1] 1.12500e+05 -1.00000e+30 -1.00000e+30 -3.05001e+05 -1.00000e+30 -1.00000e+30 -1.00000e+30 -1.00000e+30
$dualstill
[1] 1.00000e+30 1.00000e+30 1.00000e+30 4.75002e+05 1.00000e+30 1.00000e+30 1.00000e+30 1.00000e+30
Lpmark sales equal to 43,443,517 and Lpmark1 sales equal to 43,443,641. The $1 change in the first constraint, it would result in a change of 124 dollars in the optimum value of sales. It is clear that the budget constraint is binding, because when I changed the budget constraint, it changed the optimum value for sales.
##### 1D) Based on the previous exercise explain in clear words, and without running another solver again , how would you check the integrity of the other marginal value identified in 1B).
I would check the integrity of the other marginal value from 1B) by increasing the 4th constraint to $1. I would then compare the optimum values for sales and observe the changes that appeared. Finally, I would check to see if the optimum sales value changed by 75.7833.
Task 2: Monte Carlo Simulation
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.01210434
# STD calculated from the random distribution of samples
std = sd(sims)
std
[1] 0.9556717
# 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.44
##### 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.
# number of simulations/samples
runs = 1000
# 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.03734675
# STD calculated from the random distribution of samples
std = sd(sims)
std
[1] 0.983215
# 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.492
# number of simulations/samples
runs = 10000
# 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.04024144
# STD calculated from the random distribution of samples
std = sd(sims)
std
[1] 0.9639549
# 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.4994
##### 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 introductory image use case demonstration to calculate pi?
Sample: 100 Mean: 0.01210434 Standard Deviation:0.9556717 Probability: 0.44
Sample: 1000 Mean: 0.03734675 Standard Deviation: 0.983215 Probability: 0.492
Sample: 10000 Mean: 0.04024144 Standard Deviation: 0.9639549 Probability: 0.4994
As the number of simulations increased, the average got closer and closer to 0.03 standard deviation got closer to 0.97. In terms of the probability differences, it stayed around 0.50 which means 50% probability. I would guess that the probability is around 49% because that was about the number for the most accurate simulation. This is similar to the image that was presented in the introductory paragraph because they both make the point that with more samples, the more accurate your results will be.
The last exercise is optional for those interested in further enhancing their subject matter learning, and refining their problem skills in R. Your work will be assessed but you will not be graded for this exercise. You can follow the instructions presented in the video Excel equivalent example at [https://www.youtube.com/watch?v=wKdmEXCvo9s]
##### 2C-Optional!) Repeat the exercise for the S&P daily return where all is equal except we are now interested in the weekly cumulative return and the probability that the weekly cummulative return is greater than 5%. Set the number of simulations to 10000.
# number of simulations/samples
runs = 10000
# 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.02373781
# STD calculated from the random distribution of samples
std = sd(sims)
std
[1] 0.9663548
# 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.48
LS0tDQp0aXRsZTogIkJTQUQzNDMgRmFsbCAyMDE4IExhYiBXb3Jrc2hlZXQgMDgiDQphdXRob3I6ICJZaSBQYW4iDQpkYXRlOiAiMTEtMDYtMjAxOCINCm91dHB1dDoNCiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdA0KICBodG1sX2RvY3VtZW50OiBkZWZhdWx0DQogIHBkZl9kb2N1bWVudDogZGVmYXVsdA0Kc3VidGl0bGU6IFNlbnNpdGl2aXR5IEFuYWx5c2lzICYgU2ltdWxhdGlvbnMgKGJzYWQtbGFiMDgpDQotLS0NCg0KDQojIyMgQWJvdXQNCg0KSW4gdGhpcyBsYWIgd2Ugd2lsbCBmb2N1cyBvbiBzZW5zaXRpdml0eSBhbmFseXNpcyBhbmQgTW9udGUgQ2FybG8gc2ltdWxhdGlvbnMuIA0KDQpTZW5zaXRpdml0eSBhbmFseXNpcyBpcyB0aGUgc3R1ZHkgb2YgaG93IHRoZSB1bmNlcnRhaW50eSBpbiB0aGUgb3V0cHV0IG9mIGEgbWF0aGVtYXRpY2FsIG1vZGVsIG9yIHN5c3RlbSAobnVtZXJpY2FsIG9yIG90aGVyd2lzZSkgY2FuIGJlIGFwcG9ydGlvbmVkIHRvIGRpZmZlcmVudCBzb3VyY2VzIG9mIHVuY2VydGFpbnR5IGluIGl0cyBpbnB1dHMuIFdlIHdpbGwgdXNlIHRoZSBgbHBTb2x2ZUFQSWAgUi1wYWNrYWdlIGFzIHdlIGRpZCBpbiB0aGUgcHJldmlvdXMgbGFiLiANCg0KTW9udGUgQ2FybG8gU2ltdWxhdGlvbnMgdXRpbGl6ZSByZXBlYXRlZCByYW5kb20gc2FtcGxpbmcgZnJvbSBhIGdpdmVuIHVuaXZlcnNlIG9yIHBvcHVsYXRpb24gZGlzdHJpYnV0aW9uIHRvIGRlcml2ZSBjZXJ0YWluIHJlc3VsdHMuIFRoaXMgdHlwZSBvZiBzaW11bGF0aW9uIGlzIGtub3duIGFzIGEgcHJvYmFiaWxpc3RpYyBzaW11bGF0aW9uLCBhcyBvcHBvc2VkIHRvIGEgZGV0ZXJtaW5pc3RpYyBzaW11bGF0aW9uLg0KDQpBbiBleGFtcGxlIG9mIGEgTW9udGUgQ2FybG8gc2ltdWxhdGlvbiBpcyB0aGUgb25lIGFwcGxpZWQgdG8gYXBwcm94aW1hdGUgdGhlIHZhbHVlIG9mIGBwaWAuIFRoZSBzaW11bGF0aW9uIGlzIGJhc2VkIG9uIGdlbmVyYXRpbmcgcmFuZG9tIHBvaW50cyB3aXRoaW4gYSB1bml0IHNxdWFyZSBhbmQgc2VlIGhvdyBtYW55IHBvaW50cyBmYWxsIHdpdGhpbiB0aGUgY2lyY2xlIGVuY2xvc2VkIGJ5IHRoZSB1bml0IHNxdWFyZSAobWFya2VkIGluIHJlZCkuIFRoZSB2YWx1ZSBvZiBgcGlgIGlzIGVzdGltYXRlZCBieSB0aGUgbnVtYmVyIG9mIHBvaW50cyBpbnNpZGUgdGhlIGNpcmNsZSBvdmVyIHRoZSB0b3RhbCBudW1iZXIgb2YgcG9pbnRzIGdlbmVyYXRlZCBpbnNpZGUgdGhlIHNxdWFyZS4gIFRoZSBoaWdoZXIgdGhlIG51bWJlciBvZiBzYW1wbGVkIHBvaW50cyB0aGUgY2xvc2VyIHRoZSByZXN1bHQgaXMgdG8gdGhlIGFjdHVhbCByZXN1bHQuICBBZnRlciBzZWxlY3RpbmcgMzAsMDAwIHJhbmRvbSBwb2ludHMsIHRoZSBlc3RpbWF0ZSBmb3IgYHBpYCBpcyBtdWNoIGNsb3NlciB0byB0aGUgYWN0dWFsIHZhbHVlIHdpdGhpbiB0aGUgZm91ciBkZWNpbWFsIHBvaW50cyBvZiBwcmVjaXNpb24uIFRoZSBpbnRlcmVzdGVkIGFuZCBjdXJpb3VzIHJlYWRlciBpcyBlbmNvdXJhZ2VkIHRvIGV4cGxvcmUgdGhlIG1hdGhlbWF0aWNhbCBmb3VuZGF0aW9uIGJlaGluZCB0aGUgbG9naWMuDQoNCiFbXShpbWdzL21vbnRlY2FybG8uZ2lmKQ0KDQpJbiB0aGlzIGxhYiwgd2Ugd2lsbCBsZWFybiBob3cgdG8gZ2VuZXJhdGUgcmFuZG9tIHNhbXBsZXMgd2l0aCB2YXJpb3VzIHNpbXVsYXRpb25zIGFuZCBob3cgdG8gcnVuIGEgc2Vuc2l0aXZpdHkgYW5hbHlzaXMgb24gdGhlIG1hcmtldGluZyB1c2UgY2FzZSBjb3ZlcmVkIHNvIGZhci4NCg0KIyMjIFNldHVwDQoNClJlbWVtYmVyIHRvIGFsd2F5cyBzZXQgeW91ciB3b3JraW5nIGRpcmVjdG9yeSB0byB0aGUgc291cmNlIGZpbGUgbG9jYXRpb24uIEdvIHRvICdTZXNzaW9uJywgc2Nyb2xsIGRvd24gdG8gJ1NldCBXb3JraW5nIERpcmVjdG9yeScsIGFuZCBjbGljayAnVG8gU291cmNlIEZpbGUgTG9jYXRpb24nLiBSZWFkIGNhcmVmdWxseSB0aGUgYmVsb3cgYW5kIGZvbGxvdyB0aGUgaW5zdHJ1Y3Rpb25zIHRvIGNvbXBsZXRlIHRoZSB0YXNrcyBhbmQgYW5zd2VyIGFueSBxdWVzdGlvbnMuICBTdWJtaXQgeW91ciB3b3JrIHRvIFJQdWJzIGFzIGRldGFpbGVkIGluIHByZXZpb3VzIG5vdGVzLiANCg0KIyMjIE5vdGUNCg0KRm9yIHlvdXIgYXNzaWdubWVudCB5b3UgbWF5IGJlIHVzaW5nIGRpZmZlcmVudCBkYXRhIHNldHMgdGhhbiB3aGF0IGlzIGluY2x1ZGVkIGhlcmUuIEFsd2F5cyByZWFkIGNhcmVmdWxseSB0aGUgaW5zdHJ1Y3Rpb25zIG9uIFNha2FpLiAgRm9yIGNsYXJpdHksIHRhc2tzL3F1ZXN0aW9ucyB0byBiZSBjb21wbGV0ZWQvYW5zd2VyZWQgYXJlIGhpZ2hsaWdodGVkIGluIHJlZCBjb2xvciAodmlzaWJsZSBpbiBwcmV2aWV3KSBhbmQgbnVtYmVyZWQgYWNjb3JkaW5nIHRvIHRoZWlyIHBhcnRpY3VsYXIgcGxhY2VtZW50IGluIHRoZSB0YXNrIHNlY3Rpb24uICBRdWl0ZSBvZnRlbiB5b3Ugd2lsbCBuZWVkIHRvIGFkZCB5b3VyIG93biBjb2RlIGNodW5rLg0KDQpFeGVjdXRlIGFsbCBjb2RlIGNodW5rcywgcHJldmlldywgcHVibGlzaCwgYW5kIHN1Ym1pdCBsaW5rIG9uIFNha2FpLg0KDQotLS0tLS0tLS0tLS0tLQ0KDQojIyMgVGFzayAxOiBTZW5zaXRpdml0eSBBbmFseXNpcw0KDQpJbiBvcmRlciB0byBjb25kdWN0IHRoZSBzZW5zaXRpdml0eSBhbmFseXNpcywgd2Ugd2lsbCBuZWVkIHRvIGRvd25sb2FkIGFnYWluIHRoZSBgbHBTb2x2ZUFQSWAgcGFja2FnZSB1bmxlc3MgeW91IGhhdmUgaXQgYWxyZWFkeSBpbnN0YWxsZWQgaW4geW91ciBSIGVudmlyb25tZW50DQoNCmBgYHtyfQ0KIyBSZXF1aXJlIHdpbGwgbG9hZCB0aGUgcGFja2FnZSBvbmx5IGlmIG5vdCBpbnN0YWxsZWQgDQojIERlcGVuZGVuY2llcyA9IFRSVUUgbWFrZXMgc3VyZSB0aGF0IGRlcGVuZGVuY2llcyBhcmUgaW5zdGFsbA0KaWYoIXJlcXVpcmUoImxwU29sdmVBUEkiLHF1aWV0bHkgPSBUUlVFKSkNCiAgaW5zdGFsbC5wYWNrYWdlcygibHBTb2x2ZUFQSSIsZGVwZW5kZW5jaWVzID0gVFJVRSwgcmVwb3MgPSAiaHR0cHM6Ly9jbG91ZC5yLXByb2plY3Qub3JnIikNCmBgYA0KDQpXZSB3aWxsIHJldmlzaXQgYW5kIHNvbHZlIGFnYWluIHRoZSBtYXJrZXRpbmcgY2FzZSBkaXNjdXNzZWQgaW4gY2xhc3MgKGFsc28gcGFydCBvZiBwcmV2aW91cyBsYWIpLiANCmBgYHtyfQ0KIyBXZSBzdGFydCB3aXRoIGAwYCBjb25zdHJhaW50IGFuZCBgMmAgZGVjaXNpb24gdmFyaWFibGVzLiBUaGUgb2JqZWN0IG5hbWUgYGxwbWFya2AgaXMgZGlzY3JldGlvbmFyeS4NCmxwbWFyayA9IG1ha2UubHAoMCwgMikNCg0KIyBEZWZpbmUgdHlwZSBvZiBvcHRpbWl6YXRpb24gYXMgbWF4aW11bSBhbmQgZHVtcCB0aGUgc2NyZWVuIG91dHB1dCBpbnRvIGEgYGR1bW15YCB2YXJpYWJsZQ0KZHVtbXkgPSBscC5jb250cm9sKGxwbWFyaywgc2Vuc2U9Im1heCIpIA0KIyBTZXQgdGhlIG9iamVjdGl2ZSBmdW5jdGlvbiBjb2VmZmljaWVudHMgDQpzZXQub2JqZm4obHBtYXJrLCBjKDI3NS42OTEsIDQ4LjM0MSkpDQpgYGANCg0KQWRkIGFsbCBjb25zdHJhaW50cyB0byB0aGUgbW9kZWwuDQoNCmBgYHtyfQ0KYWRkLmNvbnN0cmFpbnQobHBtYXJrLCBjKDEsIDEpLCAiPD0iLCAzNTAwMDApDQphZGQuY29uc3RyYWludChscG1hcmssIGMoMSwgMCksICI+PSIsIDE1MDAwKQ0KYWRkLmNvbnN0cmFpbnQobHBtYXJrLCBjKDAsIDEpLCAiPj0iLCA3NTAwMCkNCmFkZC5jb25zdHJhaW50KGxwbWFyaywgYygyLCAtMSksICI9IiwgMCkNCmFkZC5jb25zdHJhaW50KGxwbWFyaywgYygxLCAwKSwgIj49IiwgMCkNCmFkZC5jb25zdHJhaW50KGxwbWFyaywgYygwLCAxKSwgIj49IiwgMCkNCmBgYA0KDQpOb3csIHZpZXcgdGhlIHByb2JsZW0gc2V0dGluZyBpbiB0YWJ1bGFyL21hdHJpeCBmb3JtLiBUaGlzIGlzIGEgZ29vZCBjaGVja3BvaW50IHRvIGNvbmZpcm0gdGhhdCBvdXIgY29udHJhaW50cyBoYXZlIGJlZW4gcHJvcGVybHkgc2V0Lg0KDQpgYGB7cn0NCmxwbWFyaw0KIyBzb2x2ZQ0Kc29sdmUobHBtYXJrKSANCmBgYA0KDQpOZXh0IHdlIGdldCB0aGUgb3B0aW11bSByZXN1bHRzLg0KYGBge3J9DQojIGRpc3BsYXkgdGhlIG9iamVjdGl2ZSBmdW5jdGlvbiBvcHRpbXVtIHZhbHVlDQpnZXQub2JqZWN0aXZlKGxwbWFyaykNCg0KIyBkaXNwbGF5IHRoZSBkZWNpc2lvbiB2YXJpYWJsZXMgb3B0aW11bSB2YWx1ZXMNCmdldC52YXJpYWJsZXMobHBtYXJrKQ0KYGBgDQoNCkZvciB0aGUgc2Vuc2l0aXZpdHkgcGFydCB3ZSB3aWxsIGFkZCB0d28gbmV3IGNvZGUgc2VjdGlvbnMgdG8gb2J0YWluIHRoZSBzZW5zaXRpdml0eSByZXN1bHRzLiBGcmlzdCwgd2Ugd2lsbCBvYnRhaW4gdGhlIHNlbnNpdGl2aXR5IHZhbHVlcyBkdWUgdG8gY2hhbmdlcyBpbiB0aGUgY29lZmZpY2llbnRzIG9mIHRoZSBvYmplY3RpdmUgZnVuY3Rpb24uDQoNCmBgYHtyfQ0KIyBkaXNwbGF5IHNlbnNpdGl2aXR5IHRvIGNvZWZmaWNpZW50cyBvZiBvYmplY3RpdmUgZnVuY3Rpb24uIA0KZ2V0LnNlbnNpdGl2aXR5Lm9iaihscG1hcmspDQpgYGANCg0KVGhlIGZpcnN0IHBhcnQgb2YgdGhlIG91dHB1dCBsYWJlbGVkIGBvYmpmcm9tYCBkaXNwbGF5cyB0aGUgbG93ZXIgbGltaXQvYm91bmRhcnkgYW5kIHRoZSBvdXRwdXQgbGFiZWxlZCBgb2JqdGlsbGAgZGlzcGxheXMgdGhlIHVwcGVyIGxpbWl0L2JvdW5kYXJ5IG9mIHRoZSBvYmplY3RpdmUgZnVuY3Rpb24gY29lZmZpY2llbnRzIHNlbnNpdGl2aXR5Lg0KDQo8c3BhbiBzdHlsZT0iY29sb3I6cmVkIj4NCiMjIyMjIDFBKSAgRXhwbGFpbiBpbiBjbGVhciB3b3JkcyB3aGF0IHRoZSBzZW5zaXRpdml0eSBib3VuZGFyeSB2YWx1ZXMgKGxvd2VyL3VwcGVyIGxpbWl0cykgcmVwcmVzZW50IGluIGNvbnRleHQgb2YgdGhlIG1hcmtldGluZyBtb2RlbC4gUmVmZXIgdG8gY2xhc3Mgbm90ZXMuDQo8L3NwYW4+DQoNClRoZSBzZW5zaXRpdml0eSBib3VuZGFyeSB2YWx1ZXMgcmVwcmVzZW50IGhvdyBtdWNoIHdlIGNhbiAgaG93IGNoYW5nZXMgaW4gdGhlIGNvZWZmaWNpZW50cyBvZiB0aGUgb2JqZWN0aXZlIGZ1bmN0aW9uIHdpbGwgaW1wYWN0IHRoZSBvcHRpbXVtIFogdmFsdWUsIGJ1dCBub3QgbmVjZXNzYXJpbHkgdGhlIG9wdGltdW0gc29sdXRpb24gdGhhdCBhcmUgcmFkaW8gYW5kIHR2LiBJbiB0aGUgY2FzZXMsIHdlIGNhbiB0ZWxsIHRoYXQgdGhlIGxvd2VyIGxpbWl0IGlzIGxhYmVsZWQgJ29iamZyb20nIHdoaWNoIHRoZSB2YWx1ZSAtOTYuNjgyMCBhbmQgLTEzNy44NDU1LCBhbmQgdGhlIHVwcGVyIGxpbWl0IGlzIGxhYmVsZWQgJ29ianRpbGwnIHdoaWNoIHRoZSB2YWx1ZSBpcyBpbmZpbml0eS5BbHNvLGlmIHRoZSBjb2VmZmljaWVudHMgY2hhbmdlIG91dCB0aGF0IHJhbmdlLCBpdCB3b3VsZSBhZmZlY3QgdGhlIHJpZGFvLCB0diBhbmQgc2FsZXMuIEl0IHNheXMgdGhhdCB0aGUgY29lZmZpY2llbnRzIG9mIHR3byBkZWNpc2lvbiB2YXJpYWJsZSB3aGljaCBhcmUgb3B0aW11bSBzb2x1dGlvbiByYWRpbyBhbmQgdHYgd291bGUgbm90IGNoYW5nIGJ5IC05Ni42ODIwIGFuZCAtMTM3Ljg0NTUsIGJ1dCBpdCBpcyBnb2luZyB0byBjaGFuZ2UgdGhlIG9wdGltdW0gdmFsdWUgZm9yIHNhbGVzLiAgDQoNCg0KTmV4dCB3ZSB3aWxsIG9idGFpbiB0aGUgc2Vuc2l0aXZpdHkgcmVzdWx0cyBkdWUgdG8gY2hhbmdlcyBpbiByZXNvdXJjZXMgaW1wYWN0aW5nIHRoZSBjb25zdHJhaW50cy4gDQoNCmBgYHtyfQ0KIyBkaXNwbGF5IHNlbnNpdGl2aXR5IHRvIHJpZ2h0IGhhbmQgc2lkZSBjb25zdHJhaW50cy4gDQojIFRoZXJlIHdpbGwgYmUgYSB0b3RhbCBvZiBtK24gdmFsdWVzIHdoZXJlIG0gaXMgdGhlIG51bWJlciBvZiBjb250cmFpbnRzIGFuZCBuIGlzIHRoZSBudW1iZXIgb2YgZGVjaXNpb24gdmFyaWFibGVzDQpnZXQuc2Vuc2l0aXZpdHkucmhzKGxwbWFyaykgDQpgYGANCg0KRm9yIHRoaXMgZXhlcmNpc2Ugd2UgYXJlIG9ubHkgaW50ZXJlc3RlZCBpbiB0aGUgZmlyc3Qgc2l4IHZhbHVlcyAoY29ycmVzcG9uZGluZyB0byB0aGUgc2l4IGNvbnN0cmFpbnRzKSBvZiB0aGUgb3V0cHV0IGxhYmVsZWQgYGR1YWxzYC4gDQoNCjxzcGFuIHN0eWxlPSJjb2xvcjpyZWQiPg0KIyMjIyMgMUIpICBFeHBsYWluIGluIGNsZWFyIHdvcmRzIHdoYXQgZWFjaCBvZiB0aGUgemVybyBhbmQgbm9uLXplcm8gc2Vuc2l0aXZpdHkgdmFsdWVzIHJlcHJlc2VudC4gSW4geW91ciBleHBsYW5hdGlvbiBpZGVudGlmeSB0aGUgYmluZGluZy9ub24tYmluZGluZyBjb25zdHJhaW50cywgdGhlIHN1cnBsdXMvc2xhY2ssIGFuZCB0aGUgbWFyZ2luYWwgdmFsdWVzLg0KPC9zcGFuPg0KDQpUaGUgemVybyB2YWx1ZXMgcmVwcmVzZW50IHRoYXQgdGhlIGNvbnN0cmFpbnQgaXMgbm9uLWJpbmRpbmcsIHdoaWNoIG1lYW5zIGhhdmUgc3VycGx1cy9zbGFjayBhbmQgbm8gbWFyZ2luYWwgdmFsdWUsIGFuZCBpdCBpcyBub3QgZ29pbmcgdG8gY2hhbmdlIHRoZSBvcHRpbXVtIHZhbHVlIGZvciBzYWxlcy4gSG93ZXZlciwgdGhlIG5vbi16ZXJvIG1lYW5zIHRoYXQgdGhlIGNvbnN0cmFpbnQgaXMgYmluZGluZyB3aXRoIG5vIHN1cnBsdXMgb3Igc2xhY2ssIGFuZCByZXByZXNlbnQgdGhlIG1hcmdpbmFsIHZhbHVlcyBjaGFuZ2UgaW4gdGhlIG9wdGltdW0gdmFsdWUgYnkgYWRqdXN0aW5nIGEgYmluZGluZyBjb25zdHJhaW50LiBJbiB0aGlzIGNhc2UsIHdlIGNhbiB0ZWxsIHRoYXQgdGhlcmUgYXJlIGZvdXIgbm9uLWJpbmRpbmcgY29uc3RyYWludHMgYW5kIHR3byBiaW5kaW5nIGNvbnN0cmFpbnRzLiBGb3IgdGhlIGJpbmRpbmcgY29uc3RyYWludCwgaWYgd2UgaW5jcmVhc2UgaW4gJCAxLCB0aGUgb3B0aW11bSB2YWx1ZSBvZiBzYWxlcyB3aWxsIGluY3JlYXNlIDEyNC4xMjQzMyBhbmQgNzUuNzgzMzMgYXMgd2VsbCBpbiB0aGUgZmlyc3QvZm91cnRoIGNvbnN0cmFpbnQuDQoNCg0KVG8gYWNxdWlyZSBhIGJldHRlciB1bmRlcnN0YW5kaW5nIG9mIHRoZSBzZW5zaXRpdml0eSByZXN1bHRzLCBhbmQgdG8gY29uZmlybSBpbnRlZ3JpdHkgb2YgdGhlIGNhbGN1bGF0aW9ucywgaW5kZXBlbmRlbnQgdGVzdHMgY2FuIGJlIGNvbmR1Y3RlZC4gIFRvIGRlbW9uc3RyYXRlLCB3ZSB3aWxsIHJlcGVhdCB0aGUgbGluZWFyIHByb2dyYW1taW5nIChMUCkgb3B0aW1pemF0aW9uIHByb2JsZW0gYnkgc2xpZ2h0bHkgdHdlYWtpbmcgb25lIGJpbmRpbmcgY29uc3RyYWludCBhbmQgdmVyaWZ5aW5nIHRoZSBpbXBhY3QuDQoNCjxzcGFuIHN0eWxlPSJjb2xvcjpyZWQiPg0KIyMjIyMgMUMpICBEZWZpbmUgYSBuZXcgbW9kZWwgb2JqZWN0IGBscG1hcmsxYCBhbmQgYWRkIHRoZSBjb25zdHJhaW50cyBleGFjdGx5IGFzIGVudGVyZWQgYXQgYmVnaW5uaW5nIG9mIHRoaXMgdGFzay4gQWxsIGJlaW5nIGVxdWFsLCBjaGFuZ2UgdGhlIGJ1ZGdldCBjb25zdHJhaW50IGJ5IGEgbWFyZ2luYWwgJDEgdmFsdWUgYW5kIHNvbHZlIHRoZSBMUCBwcm9ibGVtLiAgTm90ZSB0aGUgbmV3IG9wdGltdW0gdmFsdWUgZm9yIHNhbGVzLiBDYWxjdWxhdGUgdGhlIGRpZmZlcmVudGlhbCBjaGFuZ2UgaW4gb3B0aW11bSBzYWxlcyBmcm9tIHRoZSBlYXJsaWVyIGNvbXB1dGVkIG9wdGltdW0gc2FsZXMsICBhbmQgY29tcGFyZSB5b3VyIGNhbGN1bGF0aW9uIHRvIHRoZSB2YWx1ZSBvYnRhaW5lZCBmcm9tIHRoZSBzZW5zaXRpdml0eSBjYWxjdWxhdGlvbi4gIA0KPC9zcGFuPg0KDQpgYGB7cn0NCiMgRGVmaW5lIGEgbmV3IG1vZGVsIG9iamVjdCBjYWxsZWQgbHBtYXJrMQ0KbHBtYXJrMSA9IG1ha2UubHAoMCwgMikNCiMgUmVwZWF0IHJlc3Qgb2YgY29tbWFuZHMgd2l0aCB0aGUgb25lIGNvbnN0cmFpbnQgY2hhbmdlIGZvciBidWRnZXQuIFNvbHZlIGFuZCBkaXNwbGF5IHRoZSBvYmplY3RpdmUgZnVuY3Rpb24gb3B0aW11bSB2YWx1ZQ0KDQojIERlZmluZSB0eXBlIG9mIG9wdGltaXphdGlvbiBhcyBtYXhpbXVtIGFuZCBkdW1wIHRoZSBzY3JlZW4gb3V0cHV0IGludG8gYSBgZHVtbXlgIHZhcmlhYmxlDQpkdW1teSA9IGxwLmNvbnRyb2wobHBtYXJrMSwgc2Vuc2U9Im1heCIpIA0KIyBTZXQgdGhlIG9iamVjdGl2ZSBmdW5jdGlvbiBjb2VmZmljaWVudHMgDQpzZXQub2JqZm4obHBtYXJrMSwgYygyNzUuNjkxLCA0OC4zNDEpKQ0KDQpgYGANCg0KYGBge3J9DQphZGQuY29uc3RyYWludChscG1hcmsxLCBjKDEsIDEpLCAiPD0iLCAzNTAwMDEpDQphZGQuY29uc3RyYWludChscG1hcmsxLCBjKDEsIDApLCAiPj0iLCAxNTAwMCkNCmFkZC5jb25zdHJhaW50KGxwbWFyazEsIGMoMCwgMSksICI+PSIsIDc1MDAwKQ0KYWRkLmNvbnN0cmFpbnQobHBtYXJrMSwgYygyLCAtMSksICI9IiwgMCkNCmFkZC5jb25zdHJhaW50KGxwbWFyazEsIGMoMSwgMCksICI+PSIsIDApDQphZGQuY29uc3RyYWludChscG1hcmsxLCBjKDAsIDEpLCAiPj0iLCAwKQ0KYGBgDQoNCmBgYHtyfQ0KbHBtYXJrMQ0KIyBzb2x2ZQ0Kc29sdmUobHBtYXJrMSkgDQpgYGANCg0KYGBge3J9DQojIGRpc3BsYXkgdGhlIG9iamVjdGl2ZSBmdW5jdGlvbiBvcHRpbXVtIHZhbHVlDQpnZXQub2JqZWN0aXZlKGxwbWFyazEpDQoNCiMgZGlzcGxheSB0aGUgZGVjaXNpb24gdmFyaWFibGVzIG9wdGltdW0gdmFsdWVzDQpnZXQudmFyaWFibGVzKGxwbWFyazEpDQpgYGANCg0KYGBge3J9DQojIGRpc3BsYXkgc2Vuc2l0aXZpdHkgdG8gY29lZmZpY2llbnRzIG9mIG9iamVjdGl2ZSBmdW5jdGlvbi4gDQpnZXQuc2Vuc2l0aXZpdHkub2JqKGxwbWFyazEpDQpgYGANCg0KYGBge3J9DQojIGRpc3BsYXkgc2Vuc2l0aXZpdHkgdG8gcmlnaHQgaGFuZCBzaWRlIGNvbnN0cmFpbnRzLiANCiMgVGhlcmUgd2lsbCBiZSBhIHRvdGFsIG9mIG0rbiB2YWx1ZXMgd2hlcmUgbSBpcyB0aGUgbnVtYmVyIG9mIGNvbnRyYWludHMgYW5kIG4gaXMgdGhlIG51bWJlciBvZiBkZWNpc2lvbiB2YXJpYWJsZXMNCmdldC5zZW5zaXRpdml0eS5yaHMobHBtYXJrMSkgDQpgYGANCg0KTHBtYXJrIHNhbGVzIGVxdWFsIHRvIDQzLDQ0Myw1MTcgYW5kIExwbWFyazEgc2FsZXMgZXF1YWwgdG8gNDMsNDQzLDY0MS4gVGhlICQxIGNoYW5nZSBpbiB0aGUgZmlyc3QgY29uc3RyYWludCwgaXQgd291bGQgcmVzdWx0IGluIGEgY2hhbmdlIG9mIDEyNCBkb2xsYXJzIGluIHRoZSBvcHRpbXVtIHZhbHVlIG9mIHNhbGVzLiBJdCBpcyBjbGVhciB0aGF0IHRoZSBidWRnZXQgY29uc3RyYWludCBpcyBiaW5kaW5nLCBiZWNhdXNlIHdoZW4gSSBjaGFuZ2VkIHRoZSBidWRnZXQgY29uc3RyYWludCwgaXQgY2hhbmdlZCB0aGUgb3B0aW11bSB2YWx1ZSBmb3Igc2FsZXMuDQoNCjxzcGFuIHN0eWxlPSJjb2xvcjpyZWQiPg0KIyMjIyMgMUQpICBCYXNlZCBvbiB0aGUgcHJldmlvdXMgZXhlcmNpc2UgZXhwbGFpbiBpbiBjbGVhciB3b3JkcywgYW5kIHdpdGhvdXQgcnVubmluZyBhbm90aGVyIHNvbHZlciBhZ2FpbiAsIGhvdyB3b3VsZCB5b3UgY2hlY2sgdGhlIGludGVncml0eSBvZiB0aGUgb3RoZXIgbWFyZ2luYWwgdmFsdWUgaWRlbnRpZmllZCBpbiAxQikuDQo8L3NwYW4+DQoNCkkgd291bGQgY2hlY2sgdGhlIGludGVncml0eSBvZiB0aGUgb3RoZXIgbWFyZ2luYWwgdmFsdWUgZnJvbSAxQikgYnkgaW5jcmVhc2luZyB0aGUgNHRoIGNvbnN0cmFpbnQgdG8gJDEuIEkgd291bGQgdGhlbiBjb21wYXJlIHRoZSBvcHRpbXVtIHZhbHVlcyBmb3Igc2FsZXMgYW5kIG9ic2VydmUgdGhlIGNoYW5nZXMgdGhhdCBhcHBlYXJlZC4gRmluYWxseSwgSSB3b3VsZCBjaGVjayB0byBzZWUgaWYgdGhlIG9wdGltdW0gc2FsZXMgdmFsdWUgY2hhbmdlZCBieSA3NS43ODMzLg0KDQotLS0tLS0tLS0tDQoNCiMjIyBUYXNrIDI6IE1vbnRlIENhcmxvIFNpbXVsYXRpb24NCg0KRm9yIHRoaXMgdGFzayB3ZSB3aWxsIGJlIHJ1bm5pbmcgYSBNb250ZSBDYXJsbyBzaW11bGF0aW9uIHRvIGNhbGN1bGF0ZSB0aGUgcHJvYmFiaWxpdHkgdGhhdCB0aGUgZGFpbHkgcmV0dXJuIGZyb20gUyZQIHdpbGwgYmUgPiA1JS4gDQpXZSB3aWxsIGFzc3VtZSB0aGF0IHRoZSBoaXN0b3JpY2FsIFMmUCBkYWlseSByZXR1cm4gZm9sbG93cyBhIG5vcm1hbCBkaXN0cmlidXRpb24gd2l0aCBhbiBhdmVyYWdlIGRhaWx5IHJldHVybiBvZiAwLjAzICglKSBhbmQgYSBzdGFuZGFyZCBkZXZpYXRpb24gb2YgMC45NyAoJSkuIENvbnNpZGVyIHRoaXMgYXMgb3VyIHBvcHVsYXRpb24uDQoNClRvIGJlZ2luIHdlIHdpbGwgZ2VuZXJhdGUgMTAwIHJhbmRvbSBzYW1wbGVzIGZyb20gdGhlIG5vcm1hbCBkaXN0cmlidXRpb24uIEZvciB0aGUgZ2VuZXJhdGVkIHNhbXBsZXMgd2Ugd2lsbCBjYWxjdWxhdGUgdGhlIG1lYW4sIHN0YW5kYXJkIGRldmlhdGlvbiwgYW5kIHByb2JhYmlsaXR5IG9mIG9jY3VycmVuY2Ugd2hlcmUgdGhlIHNpbXVsYXRpb24gcmVzdWx0IGlzIGdyZWF0ZXIgdGhhbiA1JS4gDQoNClRvIGdlbmVyYXRlIHJhbmRvbSBzYW1wbGVzIGZyb20gYSBub3JtYWwgZGlzdHJpYnV0aW9uIHdlIHdpbGwgdXNlIHRoZSBgcm5vcm0oKWAgZnVuY3Rpb24gaW4gUi4gSW4gdGhlIGV4YW1wbGUgYmVsb3cgd2Ugc2V0IHRoZSBudW1iZXIgb2YgcnVucyAob3Igc2FtcGxlcykgdG8gMTAwLg0KDQpgYGB7cn0NCiMgbnVtYmVyIG9mIHNpbXVsYXRpb25zL3NhbXBsZXMNCnJ1bnMgPSAxMDANCiMgcmFuZG9tIG51bWJlciBnZW5lcmF0b3IgcGVyIGRlZmluZWQgbm9ybWFsIGRpc3RyaWJ1dGlvbiB3aXRoIGdpdmVuIG1lYW4gYW5kIHN0YW5kYXJkIGRldmlhdGlvbg0Kc2ltcyA9ICBybm9ybShydW5zLG1lYW49MC4wMyxzZD0wLjk3KQ0KYGBgDQoNCmBgYHtyfQ0KIyBNZWFuIGNhbGN1bGF0ZWQgZnJvbSB0aGUgcmFuZG9tIGRpc3RyaWJ1dGlvbiBvZiBzYW1wbGVzDQphdmVyYWdlID0gbWVhbihzaW1zKQ0KYXZlcmFnZQ0KYGBgDQoNCmBgYHtyfQ0KIyBTVEQgY2FsY3VsYXRlZCBmcm9tIHRoZSByYW5kb20gZGlzdHJpYnV0aW9uIG9mIHNhbXBsZXMNCnN0ZCA9IHNkKHNpbXMpIA0Kc3RkDQpgYGANCg0KYGBge3J9DQojIHByb2JhYmlsaXR5IG9mIG9jY3VycmVuY2Ugb24gYW55IGdpdmVuIGRheSBiYXNlZCBvbiBzYW1wbGVzIHdpbGwgYmUgZXF1YWwgdG8gY291bnQgKG9yIHN1bSkgd2hlcmUgc2FtcGxlIHJlc3VsdCBpcyBncmVhdGVyIHRoYW4gNSUgZGl2aWRlZCBieSB0b3RhbCBudW1iZXIgb2Ygc2FtcGxlcy4gDQpwcm9iID0gc3VtKHNpbXMgPj0wLjA1KS9ydW5zDQpwcm9iDQpgYGANCg0KPHNwYW4gc3R5bGU9ImNvbG9yOnJlZCI+DQojIyMjIyAyQSkgIFJlcGVhdCB0aGUgYWJvdmUgY2FsY3VsYXRpb25zIGZvciB0aGUgdHdvIGNhc2VzIHdoZXJlIHRoZSBudW1iZXIgb2Ygc2ltdWxhdGlvbnMvc2FtcGxlcyBpcyBlcXVhbCB0byAxMDAwIGFuZCAxMDAwMC4gIEZvciBlYWNoIGNhc2UgcmVjb3JkIHRoZSBtZWFuLCBzdGFuZGFyZCBkZXZpYXRpb24sIGFuZCBwcm9iYWJpbGl0eS4NCjwvc3Bhbj4NCg0KYGBge3J9DQojIG51bWJlciBvZiBzaW11bGF0aW9ucy9zYW1wbGVzDQpydW5zID0gMTAwMA0KIyByYW5kb20gbnVtYmVyIGdlbmVyYXRvciBwZXIgZGVmaW5lZCBub3JtYWwgZGlzdHJpYnV0aW9uIHdpdGggZ2l2ZW4gbWVhbiBhbmQgc3RhbmRhcmQgZGV2aWF0aW9uDQpzaW1zID0gIHJub3JtKHJ1bnMsbWVhbj0wLjAzLHNkPTAuOTcpDQoNCiMgTWVhbiBjYWxjdWxhdGVkIGZyb20gdGhlIHJhbmRvbSBkaXN0cmlidXRpb24gb2Ygc2FtcGxlcw0KYXZlcmFnZSA9IG1lYW4oc2ltcykNCmF2ZXJhZ2UNCg0KIyBTVEQgY2FsY3VsYXRlZCBmcm9tIHRoZSByYW5kb20gZGlzdHJpYnV0aW9uIG9mIHNhbXBsZXMNCnN0ZCA9IHNkKHNpbXMpIA0Kc3RkDQoNCg0KIyBwcm9iYWJpbGl0eSBvZiBvY2N1cnJlbmNlIG9uIGFueSBnaXZlbiBkYXkgYmFzZWQgb24gc2FtcGxlcyB3aWxsIGJlIGVxdWFsIHRvIGNvdW50IChvciBzdW0pIHdoZXJlIHNhbXBsZSByZXN1bHQgaXMgZ3JlYXRlciB0aGFuIDUlIGRpdmlkZWQgYnkgdG90YWwgbnVtYmVyIG9mIHNhbXBsZXMuIA0KcHJvYiA9IHN1bShzaW1zID49MC4wNSkvcnVucw0KcHJvYg0KDQpgYGANCg0KYGBge3J9DQojIG51bWJlciBvZiBzaW11bGF0aW9ucy9zYW1wbGVzDQpydW5zID0gMTAwMDANCiMgcmFuZG9tIG51bWJlciBnZW5lcmF0b3IgcGVyIGRlZmluZWQgbm9ybWFsIGRpc3RyaWJ1dGlvbiB3aXRoIGdpdmVuIG1lYW4gYW5kIHN0YW5kYXJkIGRldmlhdGlvbg0Kc2ltcyA9ICBybm9ybShydW5zLG1lYW49MC4wMyxzZD0wLjk3KQ0KDQojIE1lYW4gY2FsY3VsYXRlZCBmcm9tIHRoZSByYW5kb20gZGlzdHJpYnV0aW9uIG9mIHNhbXBsZXMNCmF2ZXJhZ2UgPSBtZWFuKHNpbXMpDQphdmVyYWdlDQoNCiMgU1REIGNhbGN1bGF0ZWQgZnJvbSB0aGUgcmFuZG9tIGRpc3RyaWJ1dGlvbiBvZiBzYW1wbGVzDQpzdGQgPSBzZChzaW1zKSANCnN0ZA0KDQoNCiMgcHJvYmFiaWxpdHkgb2Ygb2NjdXJyZW5jZSBvbiBhbnkgZ2l2ZW4gZGF5IGJhc2VkIG9uIHNhbXBsZXMgd2lsbCBiZSBlcXVhbCB0byBjb3VudCAob3Igc3VtKSB3aGVyZSBzYW1wbGUgcmVzdWx0IGlzIGdyZWF0ZXIgdGhhbiA1JSBkaXZpZGVkIGJ5IHRvdGFsIG51bWJlciBvZiBzYW1wbGVzLiANCnByb2IgPSBzdW0oc2ltcyA+PTAuMDUpL3J1bnMNCnByb2INCmBgYA0KDQo8c3BhbiBzdHlsZT0iY29sb3I6cmVkIj4NCiMjIyMjIDJCKSAgTGlzdCBpbiBzZXBlcmF0ZSBsaW5lcyB0aGUgdmFsdWVzIGZvciBtZWFuLCBzdGFuZGFyZCBkZXZpYXRpb24sIGFuZCBwcm9iYWJpbGl0eSBmb3IgYWxsIHRocmVlIGNhc2VzOiAxMDAsIDEwMDAsIGFuZCAxMDAwMCBzaW11bGF0aW9ucy4gRGVzY3JpYmUgaG93IHRoZSB2YWx1ZXMgY2hhbmdlL2JlaGF2ZSBhcyB0aGUgbnVtYmVyIG9mIHNpbXVsYXRpb25zIGlzIGluY3JlYXNlZC4gV2hhdCBpcyB5b3VyIGJlc3QgYmV0IG9uIHRoZSBwcm9iYWJpbGl0eSBvZiBvY2N1cnJlbmNlIGdyZWF0ZXIgdGhhbiA1JSBhbmQgd2h5PyBIb3cgaXMgdGhpcyBiZWhhdmlvciBzaW1pbGFyIHRvIHRoZSBpbnRyb2R1Y3RvcnkgaW1hZ2UgdXNlIGNhc2UgZGVtb25zdHJhdGlvbiB0byBjYWxjdWxhdGUgYHBpYD8gDQo8L3NwYW4+DQoNClNhbXBsZTogMTAwIE1lYW46ICAwLjAxMjEwNDM0IFN0YW5kYXJkIERldmlhdGlvbjowLjk1NTY3MTcgUHJvYmFiaWxpdHk6IDAuNDQNCg0KU2FtcGxlOiAxMDAwIE1lYW46IDAuMDM3MzQ2NzUgU3RhbmRhcmQgRGV2aWF0aW9uOiAwLjk4MzIxNSBQcm9iYWJpbGl0eTogMC40OTINCg0KU2FtcGxlOiAxMDAwMCBNZWFuOiAwLjA0MDI0MTQ0ICBTdGFuZGFyZCBEZXZpYXRpb246IDAuOTYzOTU0OSAgUHJvYmFiaWxpdHk6IDAuNDk5NA0KDQpBcyB0aGUgbnVtYmVyIG9mIHNpbXVsYXRpb25zIGluY3JlYXNlZCwgdGhlIGF2ZXJhZ2UgZ290IGNsb3NlciBhbmQgY2xvc2VyIHRvIDAuMDMgc3RhbmRhcmQgZGV2aWF0aW9uIGdvdCBjbG9zZXIgdG8gMC45Ny4gSW4gdGVybXMgb2YgdGhlIHByb2JhYmlsaXR5IGRpZmZlcmVuY2VzLCBpdCBzdGF5ZWQgYXJvdW5kIDAuNTAgd2hpY2ggbWVhbnMgNTAlIHByb2JhYmlsaXR5LiBJIHdvdWxkIGd1ZXNzIHRoYXQgdGhlIHByb2JhYmlsaXR5IGlzIGFyb3VuZCA0OSUgYmVjYXVzZSB0aGF0IHdhcyBhYm91dCB0aGUgbnVtYmVyIGZvciB0aGUgbW9zdCBhY2N1cmF0ZSBzaW11bGF0aW9uLiBUaGlzIGlzIHNpbWlsYXIgdG8gdGhlIGltYWdlIHRoYXQgd2FzIHByZXNlbnRlZCBpbiB0aGUgaW50cm9kdWN0b3J5IHBhcmFncmFwaCBiZWNhdXNlIHRoZXkgYm90aCBtYWtlIHRoZSBwb2ludCB0aGF0IHdpdGggbW9yZSBzYW1wbGVzLCB0aGUgbW9yZSBhY2N1cmF0ZSB5b3VyIHJlc3VsdHMgd2lsbCBiZS4NCg0KVGhlIGxhc3QgZXhlcmNpc2UgaXMgb3B0aW9uYWwgZm9yIHRob3NlIGludGVyZXN0ZWQgaW4gZnVydGhlciBlbmhhbmNpbmcgdGhlaXIgc3ViamVjdCBtYXR0ZXIgbGVhcm5pbmcsIGFuZCByZWZpbmluZyB0aGVpciBwcm9ibGVtIHNraWxscyBpbiBSLiBZb3VyIHdvcmsgd2lsbCBiZSBhc3Nlc3NlZCBidXQgeW91IHdpbGwgbm90IGJlIGdyYWRlZCBmb3IgdGhpcyBleGVyY2lzZS4gWW91IGNhbiBmb2xsb3cgdGhlIGluc3RydWN0aW9ucyBwcmVzZW50ZWQgaW4gdGhlIHZpZGVvIEV4Y2VsIGVxdWl2YWxlbnQgZXhhbXBsZSBhdCBbaHR0cHM6Ly93d3cueW91dHViZS5jb20vd2F0Y2g/dj13S2RtRVhDdm85c10gIA0KDQo8c3BhbiBzdHlsZT0iY29sb3I6cmVkIj4NCiMjIyMjIDJDLU9wdGlvbmFsISkgIFJlcGVhdCB0aGUgZXhlcmNpc2UgZm9yIHRoZSBTJlAgZGFpbHkgcmV0dXJuIHdoZXJlIGFsbCBpcyBlcXVhbCBleGNlcHQgd2UgYXJlIG5vdyBpbnRlcmVzdGVkIGluIHRoZSB3ZWVrbHkgY3VtdWxhdGl2ZSByZXR1cm4gYW5kIHRoZSBwcm9iYWJpbGl0eSB0aGF0IHRoZSB3ZWVrbHkgY3VtbXVsYXRpdmUgcmV0dXJuIGlzIGdyZWF0ZXIgdGhhbiA1JS4gU2V0IHRoZSBudW1iZXIgb2Ygc2ltdWxhdGlvbnMgdG8gMTAwMDAuDQo8L3NwYW4+DQoNCmBgYHtyfQ0KIyBudW1iZXIgb2Ygc2ltdWxhdGlvbnMvc2FtcGxlcw0KcnVucyA9IDEwMDAwDQojIHJhbmRvbSBudW1iZXIgZ2VuZXJhdG9yIHBlciBkZWZpbmVkIG5vcm1hbCBkaXN0cmlidXRpb24gd2l0aCBnaXZlbiBtZWFuIGFuZCBzdGFuZGFyZCBkZXZpYXRpb24NCnNpbXMgPSAgcm5vcm0ocnVucyxtZWFuPTAuMDMsc2Q9MC45NykNCg0KIyBNZWFuIGNhbGN1bGF0ZWQgZnJvbSB0aGUgcmFuZG9tIGRpc3RyaWJ1dGlvbiBvZiBzYW1wbGVzDQphdmVyYWdlID0gbWVhbihzaW1zKQ0KYXZlcmFnZQ0KDQojIFNURCBjYWxjdWxhdGVkIGZyb20gdGhlIHJhbmRvbSBkaXN0cmlidXRpb24gb2Ygc2FtcGxlcw0Kc3RkID0gc2Qoc2ltcykgDQpzdGQNCg0KDQojIHByb2JhYmlsaXR5IG9mIG9jY3VycmVuY2Ugb24gYW55IGdpdmVuIGRheSBiYXNlZCBvbiBzYW1wbGVzIHdpbGwgYmUgZXF1YWwgdG8gY291bnQgKG9yIHN1bSkgd2hlcmUgc2FtcGxlIHJlc3VsdCBpcyBncmVhdGVyIHRoYW4gNSUgZGl2aWRlZCBieSB0b3RhbCBudW1iZXIgb2Ygc2FtcGxlcy4gDQpwcm9iID0gc3VtKHNpbXMgPj0wLjA1KS9ydW5zDQpwcm9iDQpgYGANCg0KDQoNCg0K