EDA and Feature
Engineering
First, we are going to download the data. The variable “Date” will be
dropped because it is the observation ID, Since it is a small data set,
we can look at the data and conclude there are no missing values. We are
also going to remove to commas from the variables “Total” and
“QueensboroBridge” so R Studio classifies them as numeric.
cycle <- read.csv("https://raw.githubusercontent.com/AvaDeSt/STA-321/refs/heads/main/Assignment%205%20data(Sheet1).csv", header = TRUE)[,-1]
cycle$Total <- as.numeric(gsub(",", "", cycle$Total))
cycle$QueensboroBridge <- as.numeric(gsub(",", "", cycle$QueensboroBridge))
data(cycle)
kable(head(cycle), caption = "First few records in the data set")
First few records in the data set
Saturday |
84.9 |
72.0 |
0.23 |
3216 |
11867 |
Sunday |
87.1 |
73.0 |
0.00 |
3579 |
13995 |
Monday |
87.1 |
71.1 |
0.45 |
4230 |
16067 |
Tuesday |
82.9 |
70.0 |
0.00 |
3861 |
13925 |
Wednesday |
84.9 |
71.1 |
0.00 |
5862 |
23110 |
Thursday |
75.0 |
71.1 |
0.00 |
5251 |
21861 |
Now that we have the data, we are going to create a new data set
called “cycle.new” that contains the variable AvgTemp. This variable
will take the average temperature in degrees farenheit of the variables
HighTemp and LowTemp.
cycle.new<- cycle %>%
mutate(AvgTemp = (HighTemp + LowTemp) / 2)
Next We are going to discretize the variable Precipitation. This will
mark any observation with 0 precipitation as “0”, and any observations
with precipitation levels greater than 0 as “1”.
cycle.new$NewPrecip = ifelse(cycle.new$Precipitation > 0, 1, 0)
data(cycle.new)
kable(head(cycle.new), caption = "First few records in the data set")
First few records in the data set
Saturday |
84.9 |
72.0 |
0.23 |
3216 |
11867 |
78.45 |
1 |
Sunday |
87.1 |
73.0 |
0.00 |
3579 |
13995 |
80.05 |
0 |
Monday |
87.1 |
71.1 |
0.45 |
4230 |
16067 |
79.10 |
1 |
Tuesday |
82.9 |
70.0 |
0.00 |
3861 |
13925 |
76.45 |
0 |
Wednesday |
84.9 |
71.1 |
0.00 |
5862 |
23110 |
78.00 |
0 |
Thursday |
75.0 |
71.1 |
0.00 |
5251 |
21861 |
73.05 |
0 |
Model Building
For this study, a poisson regression and a quasi poisson model will
be used. The quasi poisson model is similar to a poisson model except
that it corrects for over dispersion. This occurs when the mean and
variance of the data is not equal.The poisson regression model has four
basic assumptions that are as follows:
The response variable is a count per unit of time or space. ( In
our case it is the count of cyclists per day).
The observations are independent of one another.
The mean of the poisson random variable is equal to the variance.
(for a quasi poisson model, this does not have to be the case)
The log of the mean rate, log(λ), is a linear function of
x
Poisson Model
First, we will build a standard poisson model using our new variables
“AvgTemp” and “NewPrecip” as well as the original one “Day”. We will
also be extracting the pearson and deviance dispersion.
Note that ϕ = variance / mean. So a dispersion equal to one means
that the variance is equal to the mean. Anything not equal to one
indicates dispersion.
pois.model = glm(QueensboroBridge ~ factor(Day) + AvgTemp + NewPrecip,
family = poisson(link="log"), data =cycle.new)
summary(pois.model)
##
## Call:
## glm(formula = QueensboroBridge ~ factor(Day) + AvgTemp + NewPrecip,
## family = poisson(link = "log"), data = cycle.new)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) 7.9739275 0.0431838 184.651 <2e-16 ***
## factor(Day)Monday 0.1586579 0.0102399 15.494 <2e-16 ***
## factor(Day)Saturday -0.0989334 0.0107978 -9.162 <2e-16 ***
## factor(Day)Sunday -0.1283273 0.0108855 -11.789 <2e-16 ***
## factor(Day)Thursday 0.1686654 0.0106693 15.809 <2e-16 ***
## factor(Day)Tuesday 0.0934492 0.0109975 8.497 <2e-16 ***
## factor(Day)Wednesday 0.1894845 0.0107878 17.565 <2e-16 ***
## AvgTemp 0.0060353 0.0005483 11.007 <2e-16 ***
## NewPrecip -0.3189133 0.0072562 -43.950 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for poisson family taken to be 1)
##
## Null deviance: 7891 on 30 degrees of freedom
## Residual deviance: 2466 on 22 degrees of freedom
## AIC: 2801.1
##
## Number of Fisher Scoring iterations: 4
## predicted y: yhat
yhat = pois.model$fitted.values
pearson.resid = (cycle.new$QueensboroBridge - yhat)/sqrt(yhat)
Pearson.disp = sum(pearson.resid^2)/pois.model$df.residual
##
Deviance.disp = (pois.model$deviance)/pois.model$df.residual
##
disp = cbind(Pearson.disp = Pearson.disp, Deviance.disp = Deviance.disp)
kable(disp, caption="Dispersion parameter", align = 'c')
Dispersion parameter
109.1148 |
112.0891 |
We can see from the summary table that all of the p values indicate
that each variable is highly significant. However, both the pearson and
deviance dispersion are way more than one, both exceeding one hundred.
This indicates that this poisson model seriously violates that
assumption of equal mean and variance. This suggests that the p values
from this model are not reliable in the first place. We can see that the
coefficient for the AvgTemp is about 0.006. This means that for every
one degree increase in the temperature high, the log of the expected
count of cyclists increases by 0.006. Since exp(0.006) = 1.006, for each
one-unit increase in the predictor variable, the expected count of the
outcome variable increases by about 10.06%, holding other variables
constant.
Poisson Regression on
Rates
This model and analysis is similar to the regular poisson model
except it will take into account the total number of cycles who cross
other major bridges in New York.
model.rates <- glm(QueensboroBridge ~ Day + AvgTemp + factor(NewPrecip), offset = log(Total),
family = poisson(link = "log"), data = cycle.new)
kable(summary(model.rates)$coef, caption = "Poisson regression on the rate of cyclists.")
Poisson regression on the rate of cyclists.
(Intercept) |
-1.2569023 |
0.0432727 |
-29.046054 |
0.0000000 |
DayMonday |
-0.0678714 |
0.0102390 |
-6.628717 |
0.0000000 |
DaySaturday |
-0.0357733 |
0.0107891 |
-3.315688 |
0.0009142 |
DaySunday |
-0.0817864 |
0.0108370 |
-7.546972 |
0.0000000 |
DayThursday |
-0.0350102 |
0.0105830 |
-3.308148 |
0.0009392 |
DayTuesday |
-0.0477855 |
0.0109113 |
-4.379469 |
0.0000119 |
DayWednesday |
-0.0433977 |
0.0106518 |
-4.074197 |
0.0000462 |
AvgTemp |
-0.0016134 |
0.0005475 |
-2.946729 |
0.0032115 |
factor(NewPrecip)1 |
0.0476215 |
0.0071899 |
6.623369 |
0.0000000 |
yhat = model.rates$fitted.values
pearson.resid = (cycle.new$QueensboroBridge - yhat)/sqrt(yhat)
Pearson.disp = sum(pearson.resid^2)/model.rates$df.residual
##
Deviance.disp = (model.rates$deviance)/model.rates$df.residual
##
disp = cbind(Pearson.disp = Pearson.disp, Deviance.disp = Deviance.disp)
kable(disp, caption="Dispersion parameter", align = 'c')
Dispersion parameter
15.23799 |
15.02153 |
We can see that similar to the first model, that all of the p values
indicate that each variable is highly significant. However, both the
pearson and deviance dispersion are also way more than one, but they are
also significantly less than the dispersion from the previous poisson
regression model. This indicates that our poisson model on rates still
violates that assumption of equal mean and variance. This suggests that
the p values from this model are not reliable in the first place.
The table also shows that the log of bikers crossing the bridge is
not the same across all days of the week. The log rates for the day
Friday are higher than the rest of the days of the week. The intercept
represents the log base cyclist rate for the baseline day Friday. The
rest of the coefficients are the difference of log rates between the
baseline day Friday and the rest of the days of the week. We can see
from the table that -0.067 is the coefficient for Monday so:
log(RMonday / RFriday) = -0.067 ⇒ RMonday / RFriday = e^−0.067 ≈
0.935
This means that the rate of bikers on Monday is about 6.5% lower on
Monday than on Friday.
Quasi Poisson
Model
Since the dispersion from the previous model was very high, we are
going to instead use a quasi poisson model. This model does not require
an equal mean and variance.
quasi.model = glm(QueensboroBridge ~ Day + AvgTemp + factor(NewPrecip),
family = quasipoisson, data = cycle.new)
SE.quasi.pois = summary(quasi.model)$coef
kable(SE.quasi.pois, caption = "Summary statistics of quasi-poisson regression model")
Summary statistics of quasi-poisson regression model
(Intercept) |
7.9739275 |
0.4510894 |
17.6770432 |
0.0000000 |
DayMonday |
0.1586579 |
0.1069643 |
1.4832787 |
0.1521859 |
DaySaturday |
-0.0989334 |
0.1127917 |
-0.8771338 |
0.3898920 |
DaySunday |
-0.1283273 |
0.1137081 |
-1.1285685 |
0.2712370 |
DayThursday |
0.1686654 |
0.1114490 |
1.5133867 |
0.1444160 |
DayTuesday |
0.0934492 |
0.1148775 |
0.8134680 |
0.4246668 |
DayWednesday |
0.1894845 |
0.1126871 |
1.6815099 |
0.1068069 |
AvgTemp |
0.0060353 |
0.0057277 |
1.0537146 |
0.3034495 |
factor(NewPrecip)1 |
-0.3189133 |
0.0757971 |
-4.2074578 |
0.0003635 |
We can see from the table above that the variables “Day” and
“AvgTemp” are not significant but the variable “NewPrcip” is. This could
be a concern. But first, we are going to look at the dispersion
parameter.
Next, we will calculate the dispersion index of our quasi poisson
model.
ydif=cycle.new$QueensboroBridge-exp(quasi.model$linear.predictors) # diff between y and yhat
prsd = ydif/sqrt(exp(quasi.model$linear.predictors)) # Pearson residuals
phi = sum(prsd^2)/15 # Dispersion index: 24-9 = 15
pander(cbind(Dispersion = phi))
The dispersion index for this model is very high. But given what we
know of the dispersion from the origional poisson model this is to be
expected.
Quasi Poisson model
on Rates
Similar to the last section, we are going to build another quasi
poisson regression model but this time, based on the rates of the total
number of cyclists crossing several major bridges in New York. We will
also calculate the dispersion for this model.
quasimodel.rates <- glm(QueensboroBridge ~ Day + AvgTemp + factor(NewPrecip), offset = log(Total),
family = quasipoisson, data = cycle.new)
pander(summary(quasimodel.rates)$coef, caption = "Quasi-Poisson regression on the Number of Cyclists")
Quasi-Poisson regression on the Number of Cyclists
(Intercept) |
-1.257 |
0.1689 |
-7.441 |
1.923e-07 |
DayMonday |
-0.06787 |
0.03997 |
-1.698 |
0.1036 |
DaySaturday |
-0.03577 |
0.04212 |
-0.8494 |
0.4048 |
DaySunday |
-0.08179 |
0.0423 |
-1.933 |
0.06617 |
DayThursday |
-0.03501 |
0.04131 |
-0.8475 |
0.4059 |
DayTuesday |
-0.04779 |
0.04259 |
-1.122 |
0.274 |
DayWednesday |
-0.0434 |
0.04158 |
-1.044 |
0.308 |
AvgTemp |
-0.001613 |
0.002137 |
-0.7549 |
0.4583 |
factor(NewPrecip)1 |
0.04762 |
0.02807 |
1.697 |
0.1039 |
ydif=cycle.new$QueensboroBridge-exp(quasimodel.rates$linear.predictors) # diff between y and yhat
prsd = ydif/sqrt(exp(quasimodel.rates$linear.predictors)) # Pearson residuals
phi = sum(prsd^2)/15 # Dispersion index: 24-9 = 15
pander(cbind(Dispersion = phi))
From the table above, we can see that none of the p values are
significant. The dispersion index is still very high but not nearly as
high as the one from the regular quasi poisson model.
Final Model
The final model chosen for this analysis will be the quasi poisson
model on rates. Despite the quasi models having less significant p
values, they do not require equal mean and variance. We have seen from
calculating the dispersion of the poisson models that the means and
variances for both the poisson and poisson rates models were not at all
equal with very high dispersion. The rate quasi poisson model on rates
will be used because despite still having a high dispersion index, it
was much closer to one than on the regular The final model can be
written as:
rate = exp(-1.257 -0.0818 * DaySunday - 0.0679 * DayMonday - 0.0478 *
DayTuesday - 0.0434 * DayWednesday - 0.035 * DayThursday - 0.0358 *
DaySaturday) x (0.0016 * AvgTemp) x (0.0476 * NewPrecip)
Some Visual
Comparisons
Now, we will create a visual representation of how the explanatory
variables affect the actual number of cyclists on the Queensboro Bridge.
Each line on the graph will represent a day of the week. We will
visualize what effect day and average temperature have on them.
Sunday = c(exp(-1.257+7), exp(-1.257-0.0818-0.0016),
exp(-1.257-0.0818+0.0476))
Monday = c(exp(-1.257+0.001815), exp(-1.257+0.0679-0.0016),
exp(-1.257+0.0679+0.0476))
Tuesday = c(exp(-1.257+0.02529), exp(-1.257-0.0478-0.0016),
exp(-1.257-0.0478+0.0476))
Wednesday= c(exp(-1.257+0.01835), exp(-1.257-0.0434-0.0016),
exp(-1.257-0.0434+0.0476))
Thursday = c(exp(-1.257+0.0237), exp(-1.257-0.035-0.0016),
exp(-1.257-0.035+0.0476))
Friday = c(exp(-1.257), exp(-1.257-0.0016),
exp(-1.257+0.0476))
Saturday = c(exp(-1.257+0.03454), exp(-1.257-0.0358-0.0016),
exp(-1.257-0.0358+0.0476))
minmax = range(c(Monday,Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday))
plot(1:3, Friday, type="l", lty =1, col="red", xlab="",
ylab="Bike Rate", xlim=c(0,6), ylim=c(0.2, 0.35), axes=FALSE )
axis(2)
axis(1, labels=c("AvgTemp", "NewPrecip"), at = 1:2)
points(1:3,Friday, pch=19, col="red")
##
lines(1:3, Saturday, lty =2, col="green")
points(1:3, Saturday, pch=20, col="green")
##
lines(1:3, Sunday, lty =3, col="purple")
points(1:3, Sunday, pch=21, col="purple")
###
lines(1:3, Monday, lty =4, col="black")
points(1:3, Monday, pch=22, col="black")
##
lines(1:3, Tuesday, lty =5, col="blue")
points(1:3, Tuesday, pch=23, col="blue")
##
lines(1:3, Wednesday, lty =6, col="orange")
points(1:3, Wednesday, pch=24, col="orange")
##
lines(1:3, Thursday, lty =7, col="turquoise")
points(1:3, Thursday, pch=25, col="turquoise")
##
legend("topright", c("Friday","Saturday", "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday" ),
pch=19:22, lty=1:7, bty="n",
col=c("red", "green", "purple", "black", "blue", "orange", "turquoise"))

From the graph we can see a few things:
LS0tDQp0aXRsZTogJ0Rpc3BlcnNlZCBQb2lzc29uIFJlZ3Jlc3Npb24gb24gTmV3IFlvcmsgQ3ljbGlzdHMnDQphdXRob3I6ICdBdmEgRGVTdGVmYW5vJw0KZGF0ZTogIjEwLTI5LTI0Ig0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50OiANCiAgICB0b2M6IHllcw0KICAgIHRvY19mbG9hdDogeWVzDQogICAgdG9jX2RlcHRoOiA0DQogICAgZmlnX3dpZHRoOiA2DQogICAgZmlnX2hlaWdodDogNA0KICAgIGZpZ19jYXB0aW9uOiB5ZXMNCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcw0KICAgIHRvY19jb2xsYXBzZWQ6IHllcw0KICAgIGNvZGVfZm9sZGluZzogaGlkZQ0KICAgIGNvZGVfZG93bmxvYWQ6IHllcw0KICAgIHNtb290aF9zY3JvbGw6IHllcw0KICAgIHRoZW1lOiBsdW1lbg0KICB3b3JkX2RvY3VtZW50OiANCiAgICB0b2M6IHllcw0KICAgIHRvY19kZXB0aDogNA0KICAgIGZpZ19jYXB0aW9uOiB5ZXMNCiAgICBrZWVwX21kOiB5ZXMNCiAgcGRmX2RvY3VtZW50OiANCiAgICB0b2M6IHllcw0KICAgIHRvY19kZXB0aDogNA0KICAgIGZpZ19jYXB0aW9uOiB5ZXMNCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcw0KICAgIGZpZ193aWR0aDogNQ0KICAgIGZpZ19oZWlnaHQ6IDQNCi0tLQ0KDQpgYGB7PWh0bWx9DQo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPg0KaDEudGl0bGUgew0KICBmb250LXNpemU6IDI0cHg7DQogIGZvbnQtd2VpZ2h0OiBib2xkOw0KICBjb2xvcjogRGFya1JlZDsNCiAgdGV4dC1hbGlnbjogY2VudGVyOw0KfQ0KaDQuYXV0aG9yIHsgLyogSGVhZGVyIDQgLSBhbmQgdGhlIGF1dGhvciBhbmQgZGF0YSBoZWFkZXJzIHVzZSB0aGlzIHRvbyAgKi8NCiAgICBmb250LXNpemU6IDE4cHg7DQogICAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOw0KICBjb2xvcjogRGFya1JlZDsNCiAgdGV4dC1hbGlnbjogY2VudGVyOw0KfQ0KaDQuZGF0ZSB7IC8qIEhlYWRlciA0IC0gYW5kIHRoZSBhdXRob3IgYW5kIGRhdGEgaGVhZGVycyB1c2UgdGhpcyB0b28gICovDQogIGZvbnQtc2l6ZTogMThweDsNCiAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOw0KICBjb2xvcjogRGFya0JsdWU7DQogIHRleHQtYWxpZ246IGNlbnRlcjsNCn0NCmgxIHsgLyogSGVhZGVyIDMgLSBhbmQgdGhlIGF1dGhvciBhbmQgZGF0YSBoZWFkZXJzIHVzZSB0aGlzIHRvbyAgKi8NCiAgICBmb250LXNpemU6IDIycHg7DQogICAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogICAgY29sb3I6IGRhcmtyZWQ7DQogICAgdGV4dC1hbGlnbjogY2VudGVyOw0KfQ0KaDIgeyAvKiBIZWFkZXIgMyAtIGFuZCB0aGUgYXV0aG9yIGFuZCBkYXRhIGhlYWRlcnMgdXNlIHRoaXMgdG9vICAqLw0KICAgIGZvbnQtc2l6ZTogMjBweDsNCiAgICBmb250LXdlaWdodDogYm9sZDsNCiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgICBjb2xvcjogbmF2eTsNCiAgICB0ZXh0LWFsaWduOiBsZWZ0Ow0KfQ0KDQpoMyB7IC8qIEhlYWRlciAzIC0gYW5kIHRoZSBhdXRob3IgYW5kIGRhdGEgaGVhZGVycyB1c2UgdGhpcyB0b28gICovDQogICAgZm9udC1zaXplOiAxOHB4Ow0KICAgIGZvbnQtd2VpZ2h0OiBib2xkOw0KICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOw0KICAgIGNvbG9yOiBuYXZ5Ow0KICAgIHRleHQtYWxpZ246IGxlZnQ7DQp9DQoNCmg0IHsgLyogSGVhZGVyIDQgLSBhbmQgdGhlIGF1dGhvciBhbmQgZGF0YSBoZWFkZXJzIHVzZSB0aGlzIHRvbyAgKi8NCiAgICBmb250LXNpemU6IDE2cHg7DQogICAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogICAgY29sb3I6IGRhcmtyZWQ7DQogICAgdGV4dC1hbGlnbjogbGVmdDsNCn0NCjwvc3R5bGU+DQpgYGANCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0KIyBjb2RlIGNodW5rIHNwZWNpZmllcyB3aGV0aGVyIHRoZSBSIGNvZGUsIHdhcm5pbmdzLCBhbmQgb3V0cHV0IA0KIyB3aWxsIGJlIGluY2x1ZGVkIGluIHRoZSBvdXRwdXQgZmlsZXMuDQppZiAoIXJlcXVpcmUoIklTd1IiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygiSVN3UiIpDQogICBsaWJyYXJ5KElTd1IpDQp9DQppZiAoIXJlcXVpcmUoIk1BU1MiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygiTUFTUyIpDQogICBsaWJyYXJ5KE1BU1MpDQp9DQppZiAoIXJlcXVpcmUoImtuaXRyIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoImtuaXRyIikNCiAgIGxpYnJhcnkoa25pdHIpDQp9DQppZiAoIXJlcXVpcmUoImRwbHlyIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoImRwbHlyIikNCiAgIGxpYnJhcnkoZHBseXIpDQp9DQppZiAoIXJlcXVpcmUoInRpZHl2ZXJzZSIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dmVyc2UiKQ0KICAgbGlicmFyeSh0aWR5dmVyc2UpDQp9DQppZiAoIXJlcXVpcmUoInBhbmRlciIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJwYW5kZXIiKQ0KICAgbGlicmFyeShwYW5kZXIpDQp9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsICAgICAgIyBpbmNsdWRlIGNvZGUgY2h1bmsgaW4gdGhlIG91dHB1dCBmaWxlDQogICAgICAgICAgICAgICAgICAgICAgd2FybmluZyA9IEZBTFNFLCAgIyBzb21ldGltZXMsIHlvdSBjb2RlIG1heSBwcm9kdWNlIHdhcm5pbmcgbWVzc2FnZXMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB5b3UgY2FuIGNob29zZSB0byBpbmNsdWRlIHRoZSB3YXJuaW5nIG1lc3NhZ2VzIGluDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB0aGUgb3V0cHV0IGZpbGUuIA0KICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdHMgPSBUUlVFLCAgICMgeW91IGNhbiBhbHNvIGRlY2lkZSB3aGV0aGVyIHRvIGluY2x1ZGUgdGhlIG91dHB1dA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgaW4gdGhlIG91dHB1dCBmaWxlLg0KICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2UgPSBGQUxTRSwNCiAgICAgICAgICAgICAgICAgICAgICBmaWcuYWxpZ249J2NlbnRlcicsIA0KICAgICAgICAgICAgICAgICAgICAgIGZpZy5wb3MgPSAnaCcNCiAgICAgICAgICAgICAgICAgICAgIA0KICAgICAgICAgICAgICAgICAgICAgICkgIA0KYGBgDQoNCiMgSW50cm9kdWN0aW9uIA0KDQpUaGUgZGF0YSBzZXQgZm9yIHRoaXMgc3R1ZHkgd2FzIGNvbGxlY3RlZCBmcm9tIHRoZSBUcmFmZmljIEluZm9ybWF0aW9uIE1hbmFnZW1lbnQgU3lzdGVtLiBJdCBrZWVwcyB0cmFjayBvZiB0aGUgbnVtYmVyIG9mIGN5Y2xpc3RzIGVudGVyaW5nIGFuZCBsZWF2aW5nIHRoZSBRdWVlbnNib3JvIEJyaWRnZSBmcm9tIHRoZSBkYXRlcyBKdWx5IDFzdCB0byBKdWx5IDMxc3QuIFRoaXMgZGF0YSBzZXQgaW5jbHVkZXMgYSB0b3RhbCBvZiAzMSBvYnNlcnZhdGlvbnMgYW5kIHNldmVuIHZhcmlhYmxlcy4gVGhlIHJlc3BvbnNlIHZhcmlhYmxlIGlzIHRoZSB0b3RhbCBudW1iZXIgb2YgY3ljbGlzdCB0aGF0IHBhc3MgdGhyb3VnaCB0aGUgIFF1ZWVuc2Jvcm8gYnJpZGdlIG9uIGVhY2ggZ2l2ZW4gZGF5LiBUaGUgZXhwbGFuYXRvcnkgdmFyaWFibGVzIGZvciB0aGlzIGRhdGEgc2V0IGludm9sdmUgdGhlIHNwZWNpZmljIGNvbmRpdGlvbnMgb2YgZWFjaCBkYXksIHN1Y2ggYXMgdGhlIHdlYXRoZXIuDQoNCiMjIFZhcmlhYmxlIERlc2NyaXB0aW9uDQoNCkhlcmUgYXJlIHdoYXQgdGhlIHNldmVuIHZhcmlhYmxlcyBpbiB0aGUgZGF0YSBzZXQgcmVwcmVzZW50Og0KDQoqIERhdGUgKHgxKSAtIG5vdGUgdGhpcyByZXByZXNlbnRzIHRoZSBvYnNlcnZhdGlvbiBJRA0KDQoqIERheSAoeDIpIC0gVGhlIGRheSBvZiB0aGUgd2Vlaw0KDQoqIEhpZ2hUZW1wICh4MykgLSB0aGUgdGVtcGVyYXR1cmUgaGlnaCBmb3IgdGhlIGRheSBpbiBkZWdyZWVzIEZhaHJlbmhlaXQNCg0KKiBMb3dUZW1wICh4NCkgLSB0aGUgdGVtcGVyYXR1cmUgaGlnaCBmb3IgdGhlIGRheSBpbiBkZWdyZWVzIEZhaHJlbmhlaXQNCg0KKiBQcmVjaXBpdGF0aW9uICh4NSkgLSB0aGUgdG90YWwgcHJlY2lwaXRhdGlvbiBmb3IgdGhlIGRheSBpbiBpbmNoZXMNCg0KKiBRdWVlbnNib3JvIEJyaWRnZSAoWSkgLSBUaGUgbnVtYmVyIG9mIGN5Y2xpc3RzIG9uIHRoZSBRdWVlbnNib3JvIGJyaWRnZS4NCg0KKiBUb3RhbCAoeDYpIC0gdGhlIHRvdGFsIG51bWJlciBvZiBjeWNsaXN0cyB3aG8gZW50ZXIgYW5kIGxlYXZlIHRoZSBicmlkZ2VzIGluIE5ZQyBlYWNoIGRheQ0KDQojIyBQcmFjdGljYWwgUXVlc3Rpb24gDQoNCkRvIHRoZSBjb25kaXRpb25zIHN1cnJvdW5kaW5nIHRoZSBkYXkgdGhlIGN5Y2xpc3RzIGFyZSByZWNvcmRlZCBhZmZlY3QgdGhlIG51bWJlciBvZiB0aGVtIGVudGVyIGFuZCBsZWF2ZSB0aGUgUXVlZW5zYm9yb0JyaWRnZT8NCg0KIyBFREEgYW5kIEZlYXR1cmUgRW5naW5lZXJpbmcgDQoNCkZpcnN0LCB3ZSBhcmUgZ29pbmcgdG8gZG93bmxvYWQgdGhlIGRhdGEuIFRoZSB2YXJpYWJsZSAiRGF0ZSIgd2lsbCBiZSBkcm9wcGVkIGJlY2F1c2UgaXQgaXMgdGhlIG9ic2VydmF0aW9uIElELCBTaW5jZSBpdCBpcyBhIHNtYWxsIGRhdGEgc2V0LCB3ZSBjYW4gbG9vayBhdCB0aGUgZGF0YSBhbmQgY29uY2x1ZGUgdGhlcmUgYXJlIG5vIG1pc3NpbmcgdmFsdWVzLiBXZSBhcmUgYWxzbyBnb2luZyB0byByZW1vdmUgdG8gY29tbWFzIGZyb20gdGhlIHZhcmlhYmxlcyAiVG90YWwiIGFuZCAiUXVlZW5zYm9yb0JyaWRnZSIgc28gUiBTdHVkaW8gY2xhc3NpZmllcyB0aGVtIGFzIG51bWVyaWMuIA0KDQpgYGB7cn0NCmN5Y2xlIDwtIHJlYWQuY3N2KCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vQXZhRGVTdC9TVEEtMzIxL3JlZnMvaGVhZHMvbWFpbi9Bc3NpZ25tZW50JTIwNSUyMGRhdGEoU2hlZXQxKS5jc3YiLCBoZWFkZXIgPSBUUlVFKVssLTFdDQogICAgICAgICAgDQpjeWNsZSRUb3RhbCA8LSBhcy5udW1lcmljKGdzdWIoIiwiLCAiIiwgY3ljbGUkVG90YWwpKQ0KY3ljbGUkUXVlZW5zYm9yb0JyaWRnZSA8LSBhcy5udW1lcmljKGdzdWIoIiwiLCAiIiwgY3ljbGUkUXVlZW5zYm9yb0JyaWRnZSkpDQoNCg0KZGF0YShjeWNsZSkNCmthYmxlKGhlYWQoY3ljbGUpLCBjYXB0aW9uID0gIkZpcnN0IGZldyByZWNvcmRzIGluIHRoZSBkYXRhIHNldCIpDQoNCmBgYA0KDQoNCg0KTm93IHRoYXQgd2UgaGF2ZSB0aGUgZGF0YSwgd2UgYXJlIGdvaW5nIHRvIGNyZWF0ZSBhIG5ldyBkYXRhIHNldCBjYWxsZWQgImN5Y2xlLm5ldyIgdGhhdCBjb250YWlucyB0aGUgdmFyaWFibGUgQXZnVGVtcC4gVGhpcyB2YXJpYWJsZSB3aWxsIHRha2UgdGhlIGF2ZXJhZ2UgdGVtcGVyYXR1cmUgaW4gZGVncmVlcyBmYXJlbmhlaXQgb2YgdGhlIHZhcmlhYmxlcyBIaWdoVGVtcCBhbmQgTG93VGVtcC4NCg0KYGBge3J9DQpjeWNsZS5uZXc8LSBjeWNsZSAlPiUNCiAgbXV0YXRlKEF2Z1RlbXAgPSAoSGlnaFRlbXAgKyBMb3dUZW1wKSAvIDIpDQoNCmBgYA0KDQpOZXh0IFdlIGFyZSBnb2luZyB0byBkaXNjcmV0aXplIHRoZSB2YXJpYWJsZSBQcmVjaXBpdGF0aW9uLiBUaGlzIHdpbGwgbWFyayBhbnkgb2JzZXJ2YXRpb24gd2l0aCAwIHByZWNpcGl0YXRpb24gYXMgIjAiLCBhbmQgYW55IG9ic2VydmF0aW9ucyB3aXRoIHByZWNpcGl0YXRpb24gbGV2ZWxzIGdyZWF0ZXIgdGhhbiAwIGFzICIxIi4gDQoNCmBgYHtyfQ0KY3ljbGUubmV3JE5ld1ByZWNpcCA9IGlmZWxzZShjeWNsZS5uZXckUHJlY2lwaXRhdGlvbiA+IDAsIDEsIDApDQoNCmRhdGEoY3ljbGUubmV3KQ0Ka2FibGUoaGVhZChjeWNsZS5uZXcpLCBjYXB0aW9uID0gIkZpcnN0IGZldyByZWNvcmRzIGluIHRoZSBkYXRhIHNldCIpDQoNCmBgYA0KDQojIE1vZGVsIEJ1aWxkaW5nDQoNCkZvciB0aGlzIHN0dWR5LCBhIHBvaXNzb24gcmVncmVzc2lvbiBhbmQgYSBxdWFzaSBwb2lzc29uIG1vZGVsIHdpbGwgYmUgdXNlZC4gVGhlIHF1YXNpIHBvaXNzb24gbW9kZWwgaXMgc2ltaWxhciB0byBhIHBvaXNzb24gbW9kZWwgZXhjZXB0IHRoYXQgaXQgY29ycmVjdHMgZm9yIG92ZXIgZGlzcGVyc2lvbi4gVGhpcyBvY2N1cnMgd2hlbiB0aGUgbWVhbiBhbmQgdmFyaWFuY2Ugb2YgdGhlIGRhdGEgaXMgbm90IGVxdWFsLlRoZSBwb2lzc29uIHJlZ3Jlc3Npb24gbW9kZWwgaGFzIGZvdXIgYmFzaWMgYXNzdW1wdGlvbnMgdGhhdCBhcmUgYXMgZm9sbG93czoNCg0KLSBUaGUgcmVzcG9uc2UgdmFyaWFibGUgaXMgYSBjb3VudCBwZXIgdW5pdCBvZiB0aW1lIG9yIHNwYWNlLiAoIEluIG91ciBjYXNlIGl0IGlzIHRoZSBjb3VudCBvZiBjeWNsaXN0cyBwZXIgZGF5KS4NCg0KLSBUaGUgb2JzZXJ2YXRpb25zIGFyZSBpbmRlcGVuZGVudCBvZiBvbmUgYW5vdGhlci4NCg0KLSBUaGUgbWVhbiBvZiB0aGUgcG9pc3NvbiByYW5kb20gdmFyaWFibGUgaXMgZXF1YWwgdG8gdGhlIHZhcmlhbmNlLiAoZm9yIGEgcXVhc2kgcG9pc3NvbiBtb2RlbCwgdGhpcyBkb2VzIG5vdCBoYXZlIHRvIGJlIHRoZSBjYXNlKQ0KDQotIFRoZSBsb2cgb2YgdGhlIG1lYW4gcmF0ZSwgbG9nKM67KSwgaXMgYSBsaW5lYXIgZnVuY3Rpb24gb2YgeA0KDQojIyBQb2lzc29uIE1vZGVsDQoNCkZpcnN0LCB3ZSB3aWxsIGJ1aWxkIGEgc3RhbmRhcmQgcG9pc3NvbiBtb2RlbCB1c2luZyBvdXIgbmV3IHZhcmlhYmxlcyAiQXZnVGVtcCIgYW5kICJOZXdQcmVjaXAiIGFzIHdlbGwgYXMgdGhlIG9yaWdpbmFsIG9uZSAiRGF5Ii4gIFdlIHdpbGwgYWxzbyBiZSBleHRyYWN0aW5nIHRoZSBwZWFyc29uIGFuZCBkZXZpYW5jZSBkaXNwZXJzaW9uLg0KDQpOb3RlIHRoYXQgz5UgPSB2YXJpYW5jZSAvIG1lYW4uIFNvIGEgZGlzcGVyc2lvbiBlcXVhbCB0byBvbmUgbWVhbnMgdGhhdCB0aGUgdmFyaWFuY2UgaXMgZXF1YWwgdG8gdGhlIG1lYW4uIEFueXRoaW5nIG5vdCBlcXVhbCB0byBvbmUgaW5kaWNhdGVzIGRpc3BlcnNpb24uIA0KDQpgYGB7cn0NCnBvaXMubW9kZWwgPSBnbG0oUXVlZW5zYm9yb0JyaWRnZSB+IGZhY3RvcihEYXkpICsgQXZnVGVtcCArIE5ld1ByZWNpcCwgDQogICAgICAgICAgICAgICAgIGZhbWlseSA9IHBvaXNzb24obGluaz0ibG9nIiksIGRhdGEgPWN5Y2xlLm5ldykgDQpzdW1tYXJ5KHBvaXMubW9kZWwpDQoNCiMjIHByZWRpY3RlZCB5OiB5aGF0DQp5aGF0ID0gcG9pcy5tb2RlbCRmaXR0ZWQudmFsdWVzDQpwZWFyc29uLnJlc2lkID0gKGN5Y2xlLm5ldyRRdWVlbnNib3JvQnJpZGdlIC0geWhhdCkvc3FydCh5aGF0KQ0KUGVhcnNvbi5kaXNwID0gc3VtKHBlYXJzb24ucmVzaWReMikvcG9pcy5tb2RlbCRkZi5yZXNpZHVhbA0KIyMNCkRldmlhbmNlLmRpc3AgPSAocG9pcy5tb2RlbCRkZXZpYW5jZSkvcG9pcy5tb2RlbCRkZi5yZXNpZHVhbA0KIyMNCmRpc3AgPSBjYmluZChQZWFyc29uLmRpc3AgPSBQZWFyc29uLmRpc3AsIERldmlhbmNlLmRpc3AgPSBEZXZpYW5jZS5kaXNwKQ0Ka2FibGUoZGlzcCwgY2FwdGlvbj0iRGlzcGVyc2lvbiBwYXJhbWV0ZXIiLCBhbGlnbiA9ICdjJykNCmBgYA0KV2UgY2FuIHNlZSBmcm9tIHRoZSBzdW1tYXJ5IHRhYmxlIHRoYXQgYWxsIG9mIHRoZSBwIHZhbHVlcyBpbmRpY2F0ZSB0aGF0IGVhY2ggdmFyaWFibGUgaXMgaGlnaGx5IHNpZ25pZmljYW50LiBIb3dldmVyLCBib3RoIHRoZSBwZWFyc29uIGFuZCBkZXZpYW5jZSBkaXNwZXJzaW9uIGFyZSB3YXkgbW9yZSB0aGFuIG9uZSwgYm90aCBleGNlZWRpbmcgb25lIGh1bmRyZWQuIFRoaXMgaW5kaWNhdGVzIHRoYXQgdGhpcyBwb2lzc29uIG1vZGVsIHNlcmlvdXNseSB2aW9sYXRlcyB0aGF0IGFzc3VtcHRpb24gb2YgZXF1YWwgbWVhbiBhbmQgdmFyaWFuY2UuIFRoaXMgc3VnZ2VzdHMgdGhhdCB0aGUgcCB2YWx1ZXMgZnJvbSB0aGlzIG1vZGVsIGFyZSBub3QgcmVsaWFibGUgaW4gdGhlIGZpcnN0IHBsYWNlLiBXZSBjYW4gc2VlIHRoYXQgdGhlIGNvZWZmaWNpZW50IGZvciB0aGUgQXZnVGVtcCBpcyBhYm91dCAwLjAwNi4gVGhpcyBtZWFucyB0aGF0IGZvciBldmVyeSBvbmUgZGVncmVlIGluY3JlYXNlIGluIHRoZSB0ZW1wZXJhdHVyZSBoaWdoLCB0aGUgbG9nIG9mIHRoZSBleHBlY3RlZCBjb3VudCBvZiBjeWNsaXN0cyBpbmNyZWFzZXMgYnkgMC4wMDYuIFNpbmNlIGV4cCgwLjAwNikgPSAxLjAwNiwgZm9yIGVhY2ggb25lLXVuaXQgaW5jcmVhc2UgaW4gdGhlIHByZWRpY3RvciB2YXJpYWJsZSwgdGhlIGV4cGVjdGVkIGNvdW50IG9mIHRoZSBvdXRjb21lIHZhcmlhYmxlIGluY3JlYXNlcyBieSBhYm91dCAxMC4wNiUsIGhvbGRpbmcgb3RoZXIgdmFyaWFibGVzIGNvbnN0YW50Lg0KDQojIyBQb2lzc29uIFJlZ3Jlc3Npb24gb24gUmF0ZXMNCg0KVGhpcyBtb2RlbCBhbmQgYW5hbHlzaXMgaXMgc2ltaWxhciB0byB0aGUgcmVndWxhciBwb2lzc29uIG1vZGVsIGV4Y2VwdCBpdCB3aWxsIHRha2UgaW50byBhY2NvdW50IHRoZSB0b3RhbCBudW1iZXIgb2YgY3ljbGVzIHdobyBjcm9zcyBvdGhlciBtYWpvciBicmlkZ2VzIGluIE5ldyBZb3JrLg0KDQpgYGB7cn0NCm1vZGVsLnJhdGVzIDwtIGdsbShRdWVlbnNib3JvQnJpZGdlIH4gRGF5ICsgQXZnVGVtcCArIGZhY3RvcihOZXdQcmVjaXApLCBvZmZzZXQgPSBsb2coVG90YWwpLCANCiAgICAgICAgICAgICAgICAgICBmYW1pbHkgPSBwb2lzc29uKGxpbmsgPSAibG9nIiksIGRhdGEgPSBjeWNsZS5uZXcpDQprYWJsZShzdW1tYXJ5KG1vZGVsLnJhdGVzKSRjb2VmLCBjYXB0aW9uID0gIlBvaXNzb24gcmVncmVzc2lvbiBvbiB0aGUgcmF0ZSBvZiBjeWNsaXN0cy4iKQ0KDQoNCg0KeWhhdCA9IG1vZGVsLnJhdGVzJGZpdHRlZC52YWx1ZXMNCnBlYXJzb24ucmVzaWQgPSAoY3ljbGUubmV3JFF1ZWVuc2Jvcm9CcmlkZ2UgLSB5aGF0KS9zcXJ0KHloYXQpDQpQZWFyc29uLmRpc3AgPSBzdW0ocGVhcnNvbi5yZXNpZF4yKS9tb2RlbC5yYXRlcyRkZi5yZXNpZHVhbA0KIyMNCkRldmlhbmNlLmRpc3AgPSAobW9kZWwucmF0ZXMkZGV2aWFuY2UpL21vZGVsLnJhdGVzJGRmLnJlc2lkdWFsDQojIw0KZGlzcCA9IGNiaW5kKFBlYXJzb24uZGlzcCA9IFBlYXJzb24uZGlzcCwgRGV2aWFuY2UuZGlzcCA9IERldmlhbmNlLmRpc3ApDQprYWJsZShkaXNwLCBjYXB0aW9uPSJEaXNwZXJzaW9uIHBhcmFtZXRlciIsIGFsaWduID0gJ2MnKQ0KYGBgDQpXZSBjYW4gc2VlIHRoYXQgc2ltaWxhciB0byB0aGUgZmlyc3QgbW9kZWwsIHRoYXQgYWxsIG9mIHRoZSBwIHZhbHVlcyBpbmRpY2F0ZSB0aGF0IGVhY2ggdmFyaWFibGUgaXMgaGlnaGx5IHNpZ25pZmljYW50LiBIb3dldmVyLCBib3RoIHRoZSBwZWFyc29uIGFuZCBkZXZpYW5jZSBkaXNwZXJzaW9uIGFyZSBhbHNvIHdheSBtb3JlIHRoYW4gb25lLCBidXQgdGhleSBhcmUgYWxzbyBzaWduaWZpY2FudGx5IGxlc3MgdGhhbiB0aGUgZGlzcGVyc2lvbiBmcm9tIHRoZSBwcmV2aW91cyBwb2lzc29uIHJlZ3Jlc3Npb24gbW9kZWwuIFRoaXMgaW5kaWNhdGVzIHRoYXQgb3VyIHBvaXNzb24gIG1vZGVsIG9uIHJhdGVzIHN0aWxsIHZpb2xhdGVzIHRoYXQgYXNzdW1wdGlvbiBvZiBlcXVhbCBtZWFuIGFuZCB2YXJpYW5jZS4gVGhpcyBzdWdnZXN0cyB0aGF0IHRoZSBwIHZhbHVlcyBmcm9tIHRoaXMgbW9kZWwgYXJlIG5vdCByZWxpYWJsZSBpbiB0aGUgZmlyc3QgcGxhY2UuIA0KDQpUaGUgdGFibGUgYWxzbyBzaG93cyB0aGF0IHRoZSBsb2cgb2YgYmlrZXJzIGNyb3NzaW5nIHRoZSBicmlkZ2UgaXMgbm90IHRoZSBzYW1lIGFjcm9zcyBhbGwgZGF5cyBvZiB0aGUgd2Vlay4gVGhlIGxvZyByYXRlcyBmb3IgdGhlIGRheSBGcmlkYXkgYXJlIGhpZ2hlciB0aGFuIHRoZSByZXN0IG9mIHRoZSBkYXlzIG9mIHRoZSB3ZWVrLiBUaGUgaW50ZXJjZXB0IHJlcHJlc2VudHMgdGhlIGxvZyBiYXNlIGN5Y2xpc3QgcmF0ZSBmb3IgdGhlIGJhc2VsaW5lIGRheSBGcmlkYXkuIFRoZSByZXN0IG9mIHRoZSBjb2VmZmljaWVudHMgYXJlIHRoZSBkaWZmZXJlbmNlIG9mIGxvZyByYXRlcyBiZXR3ZWVuIHRoZSBiYXNlbGluZSBkYXkgRnJpZGF5IGFuZCB0aGUgcmVzdCBvZiB0aGUgZGF5cyBvZiB0aGUgd2Vlay4gV2UgY2FuIHNlZSBmcm9tIHRoZSB0YWJsZSB0aGF0IC0wLjA2NyBpcyB0aGUgY29lZmZpY2llbnQgZm9yIE1vbmRheSBzbzoNCg0KbG9nKFJNb25kYXkgLyBSRnJpZGF5KSA9IC0wLjA2NyDih5IgICBSTW9uZGF5IC8gUkZyaWRheSA9IGVe4oiSMC4wNjcg4omIIDAuOTM1DQoNClRoaXMgbWVhbnMgdGhhdCB0aGUgcmF0ZSBvZiBiaWtlcnMgb24gTW9uZGF5IGlzIGFib3V0IDYuNSUgbG93ZXIgb24gTW9uZGF5IHRoYW4gb24gRnJpZGF5Lg0KDQojIyBRdWFzaSBQb2lzc29uIE1vZGVsDQoNClNpbmNlIHRoZSBkaXNwZXJzaW9uIGZyb20gdGhlIHByZXZpb3VzIG1vZGVsIHdhcyB2ZXJ5IGhpZ2gsIHdlIGFyZSBnb2luZyB0byBpbnN0ZWFkIHVzZSBhIHF1YXNpIHBvaXNzb24gbW9kZWwuIFRoaXMgbW9kZWwgZG9lcyBub3QgcmVxdWlyZSBhbiBlcXVhbCBtZWFuIGFuZCB2YXJpYW5jZS4gDQoNCmBgYHtyfQ0KcXVhc2kubW9kZWwgPSBnbG0oUXVlZW5zYm9yb0JyaWRnZSB+IERheSArIEF2Z1RlbXAgKyBmYWN0b3IoTmV3UHJlY2lwKSwgDQogICAgICAgICAgICAgICAgIGZhbWlseSA9IHF1YXNpcG9pc3NvbiwgZGF0YSA9IGN5Y2xlLm5ldykgIA0KDQpTRS5xdWFzaS5wb2lzID0gc3VtbWFyeShxdWFzaS5tb2RlbCkkY29lZg0Ka2FibGUoU0UucXVhc2kucG9pcywgY2FwdGlvbiA9ICJTdW1tYXJ5IHN0YXRpc3RpY3Mgb2YgcXVhc2ktcG9pc3NvbiByZWdyZXNzaW9uIG1vZGVsIikNCg0KYGBgDQpXZSBjYW4gc2VlIGZyb20gdGhlIHRhYmxlIGFib3ZlIHRoYXQgdGhlIHZhcmlhYmxlcyAiRGF5IiBhbmQgIkF2Z1RlbXAiIGFyZSBub3Qgc2lnbmlmaWNhbnQgYnV0IHRoZSB2YXJpYWJsZSAiTmV3UHJjaXAiIGlzLiBUaGlzIGNvdWxkIGJlIGEgY29uY2Vybi4gQnV0IGZpcnN0LCB3ZSBhcmUgZ29pbmcgdG8gbG9vayBhdCB0aGUgZGlzcGVyc2lvbiBwYXJhbWV0ZXIuDQoNCk5leHQsIHdlIHdpbGwgY2FsY3VsYXRlIHRoZSBkaXNwZXJzaW9uIGluZGV4IG9mIG91ciBxdWFzaSBwb2lzc29uIG1vZGVsLiANCg0KYGBge3J9DQp5ZGlmPWN5Y2xlLm5ldyRRdWVlbnNib3JvQnJpZGdlLWV4cChxdWFzaS5tb2RlbCRsaW5lYXIucHJlZGljdG9ycykgICMgZGlmZiBiZXR3ZWVuIHkgYW5kIHloYXQNCnByc2QgPSB5ZGlmL3NxcnQoZXhwKHF1YXNpLm1vZGVsJGxpbmVhci5wcmVkaWN0b3JzKSkgICAjIFBlYXJzb24gcmVzaWR1YWxzDQpwaGkgPSBzdW0ocHJzZF4yKS8xNSAgICAgICAgICAjIERpc3BlcnNpb24gaW5kZXg6IDI0LTkgPSAxNSAgDQpwYW5kZXIoY2JpbmQoRGlzcGVyc2lvbiA9IHBoaSkpDQoNCmBgYA0KVGhlIGRpc3BlcnNpb24gaW5kZXggZm9yIHRoaXMgbW9kZWwgaXMgdmVyeSBoaWdoLiBCdXQgZ2l2ZW4gd2hhdCB3ZSBrbm93IG9mIHRoZSBkaXNwZXJzaW9uIGZyb20gdGhlIG9yaWdpb25hbCBwb2lzc29uIG1vZGVsIHRoaXMgaXMgdG8gYmUgZXhwZWN0ZWQuIA0KDQojIyBRdWFzaSBQb2lzc29uIG1vZGVsIG9uIFJhdGVzDQoNClNpbWlsYXIgdG8gdGhlIGxhc3Qgc2VjdGlvbiwgd2UgYXJlIGdvaW5nIHRvIGJ1aWxkIGFub3RoZXIgcXVhc2kgcG9pc3NvbiByZWdyZXNzaW9uIG1vZGVsIGJ1dCB0aGlzIHRpbWUsIGJhc2VkIG9uIHRoZSByYXRlcyBvZiB0aGUgdG90YWwgbnVtYmVyIG9mIGN5Y2xpc3RzIGNyb3NzaW5nIHNldmVyYWwgbWFqb3IgYnJpZGdlcyBpbiBOZXcgWW9yay4gV2Ugd2lsbCBhbHNvIGNhbGN1bGF0ZSB0aGUgZGlzcGVyc2lvbiBmb3IgdGhpcyBtb2RlbC4NCg0KYGBge3J9DQpxdWFzaW1vZGVsLnJhdGVzIDwtIGdsbShRdWVlbnNib3JvQnJpZGdlIH4gRGF5ICsgQXZnVGVtcCArIGZhY3RvcihOZXdQcmVjaXApLCBvZmZzZXQgPSBsb2coVG90YWwpLCANCiAgICAgICAgICAgICAgICAgICBmYW1pbHkgPSBxdWFzaXBvaXNzb24sIGRhdGEgPSBjeWNsZS5uZXcpDQpwYW5kZXIoc3VtbWFyeShxdWFzaW1vZGVsLnJhdGVzKSRjb2VmLCBjYXB0aW9uID0gIlF1YXNpLVBvaXNzb24gcmVncmVzc2lvbiBvbiB0aGUgTnVtYmVyIG9mIEN5Y2xpc3RzIikNCg0KeWRpZj1jeWNsZS5uZXckUXVlZW5zYm9yb0JyaWRnZS1leHAocXVhc2ltb2RlbC5yYXRlcyRsaW5lYXIucHJlZGljdG9ycykgICMgZGlmZiBiZXR3ZWVuIHkgYW5kIHloYXQNCnByc2QgPSB5ZGlmL3NxcnQoZXhwKHF1YXNpbW9kZWwucmF0ZXMkbGluZWFyLnByZWRpY3RvcnMpKSAgICMgUGVhcnNvbiByZXNpZHVhbHMNCnBoaSA9IHN1bShwcnNkXjIpLzE1ICAgICAgICAgICMgRGlzcGVyc2lvbiBpbmRleDogMjQtOSA9IDE1ICANCnBhbmRlcihjYmluZChEaXNwZXJzaW9uID0gcGhpKSkNCmBgYA0KRnJvbSB0aGUgdGFibGUgYWJvdmUsIHdlIGNhbiBzZWUgdGhhdCBub25lIG9mIHRoZSBwIHZhbHVlcyBhcmUgc2lnbmlmaWNhbnQuIFRoZSBkaXNwZXJzaW9uIGluZGV4IGlzIHN0aWxsIHZlcnkgaGlnaCBidXQgbm90IG5lYXJseSBhcyBoaWdoIGFzIHRoZSBvbmUgZnJvbSB0aGUgcmVndWxhciBxdWFzaSBwb2lzc29uIG1vZGVsLiANCg0KIyMgRmluYWwgTW9kZWwNCg0KVGhlIGZpbmFsIG1vZGVsIGNob3NlbiBmb3IgdGhpcyBhbmFseXNpcyB3aWxsIGJlIHRoZSBxdWFzaSBwb2lzc29uIG1vZGVsIG9uIHJhdGVzLiBEZXNwaXRlIHRoZSBxdWFzaSBtb2RlbHMgaGF2aW5nIGxlc3Mgc2lnbmlmaWNhbnQgcCB2YWx1ZXMsIHRoZXkgZG8gbm90IHJlcXVpcmUgZXF1YWwgbWVhbiBhbmQgdmFyaWFuY2UuIFdlIGhhdmUgc2VlbiBmcm9tIGNhbGN1bGF0aW5nIHRoZSBkaXNwZXJzaW9uIG9mIHRoZSBwb2lzc29uIG1vZGVscyB0aGF0IHRoZSBtZWFucyBhbmQgdmFyaWFuY2VzIGZvciBib3RoIHRoZSBwb2lzc29uIGFuZCBwb2lzc29uIHJhdGVzIG1vZGVscyB3ZXJlIG5vdCBhdCBhbGwgZXF1YWwgd2l0aCB2ZXJ5IGhpZ2ggZGlzcGVyc2lvbi4gVGhlIHJhdGUgcXVhc2kgcG9pc3NvbiBtb2RlbCBvbiByYXRlcyB3aWxsIGJlIHVzZWQgYmVjYXVzZSBkZXNwaXRlIHN0aWxsIGhhdmluZyBhIGhpZ2ggZGlzcGVyc2lvbiBpbmRleCwgaXQgd2FzIG11Y2ggY2xvc2VyIHRvIG9uZSB0aGFuIG9uIHRoZSByZWd1bGFyICBUaGUgZmluYWwgbW9kZWwgY2FuIGJlIHdyaXR0ZW4gYXM6DQoNCnJhdGUgPSBleHAoLTEuMjU3IC0wLjA4MTggKiBEYXlTdW5kYXkgLSAwLjA2NzkgKiBEYXlNb25kYXkgLSAwLjA0NzggKiBEYXlUdWVzZGF5IC0gMC4wNDM0ICogRGF5V2VkbmVzZGF5IC0gMC4wMzUgKiBEYXlUaHVyc2RheSAtIDAuMDM1OCAqIERheVNhdHVyZGF5KSB4ICgwLjAwMTYgKiBBdmdUZW1wKSB4ICgwLjA0NzYgKiBOZXdQcmVjaXApDQoNCiMjIFNvbWUgVmlzdWFsIENvbXBhcmlzb25zIA0KDQpOb3csIHdlIHdpbGwgY3JlYXRlIGEgdmlzdWFsIHJlcHJlc2VudGF0aW9uIG9mIGhvdyB0aGUgZXhwbGFuYXRvcnkgdmFyaWFibGVzIGFmZmVjdCB0aGUgYWN0dWFsIG51bWJlciBvZiBjeWNsaXN0cyBvbiB0aGUgUXVlZW5zYm9ybyBCcmlkZ2UuIEVhY2ggbGluZSBvbiB0aGUgZ3JhcGggd2lsbCByZXByZXNlbnQgYSBkYXkgb2YgdGhlIHdlZWsuIFdlIHdpbGwgdmlzdWFsaXplIHdoYXQgZWZmZWN0IGRheSBhbmQgYXZlcmFnZSB0ZW1wZXJhdHVyZSBoYXZlIG9uIHRoZW0uICANCg0KYGBge3J9DQpTdW5kYXkgPSBjKGV4cCgtMS4yNTcrNyksIGV4cCgtMS4yNTctMC4wODE4LTAuMDAxNiksICAgDQogICAgICAgICAgICAgICBleHAoLTEuMjU3LTAuMDgxOCswLjA0NzYpKQ0KDQpNb25kYXkgPSBjKGV4cCgtMS4yNTcrMC4wMDE4MTUpLCBleHAoLTEuMjU3KzAuMDY3OS0wLjAwMTYpLCAgIA0KICAgICAgICAgICAgICAgZXhwKC0xLjI1NyswLjA2NzkrMC4wNDc2KSkNCg0KDQpUdWVzZGF5ID0gYyhleHAoLTEuMjU3KzAuMDI1MjkpLCBleHAoLTEuMjU3LTAuMDQ3OC0wLjAwMTYpLCAgIA0KICAgICAgICAgICAgICAgZXhwKC0xLjI1Ny0wLjA0NzgrMC4wNDc2KSkNCg0KDQpXZWRuZXNkYXk9IGMoZXhwKC0xLjI1NyswLjAxODM1KSwgZXhwKC0xLjI1Ny0wLjA0MzQtMC4wMDE2KSwgICANCiAgICAgICAgICAgICAgIGV4cCgtMS4yNTctMC4wNDM0KzAuMDQ3NikpDQoNCg0KVGh1cnNkYXkgPSBjKGV4cCgtMS4yNTcrMC4wMjM3KSwgZXhwKC0xLjI1Ny0wLjAzNS0wLjAwMTYpLCAgIA0KICAgICAgICAgICAgICAgZXhwKC0xLjI1Ny0wLjAzNSswLjA0NzYpKQ0KDQoNCkZyaWRheSA9IGMoZXhwKC0xLjI1NyksIGV4cCgtMS4yNTctMC4wMDE2KSwgICANCiAgICAgICAgICAgICAgIGV4cCgtMS4yNTcrMC4wNDc2KSkNCg0KDQpTYXR1cmRheSA9IGMoZXhwKC0xLjI1NyswLjAzNDU0KSwgZXhwKC0xLjI1Ny0wLjAzNTgtMC4wMDE2KSwgICANCiAgICAgICAgICAgICAgIGV4cCgtMS4yNTctMC4wMzU4KzAuMDQ3NikpDQoNCm1pbm1heCA9IHJhbmdlKGMoTW9uZGF5LFR1ZXNkYXksIFdlZG5lc2RheSwgVGh1cnNkYXksIEZyaWRheSwgU2F0dXJkYXksIFN1bmRheSkpDQoNCmBgYA0KDQpgYGB7ciBmaWcuYWxpZ249J2NlbnRlcicsIGZpZy53aWR0aD02LCBmaWcuaGVpZ2h0PTZ9DQpwbG90KDE6MywgRnJpZGF5LCB0eXBlPSJsIiwgbHR5ID0xLCBjb2w9InJlZCIsIHhsYWI9IiIsIA0KICAgICAgICAgICAgICAgeWxhYj0iQmlrZSBSYXRlIiwgeGxpbT1jKDAsNiksIHlsaW09YygwLjIsIDAuMzUpLCBheGVzPUZBTFNFICkNCmF4aXMoMikNCmF4aXMoMSwgbGFiZWxzPWMoIkF2Z1RlbXAiLCAiTmV3UHJlY2lwIiksIGF0ID0gMToyKQ0KcG9pbnRzKDE6MyxGcmlkYXksIHBjaD0xOSwgY29sPSJyZWQiKQ0KIyMNCmxpbmVzKDE6MywgU2F0dXJkYXksIGx0eSA9MiwgY29sPSJncmVlbiIpDQpwb2ludHMoMTozLCBTYXR1cmRheSwgcGNoPTIwLCBjb2w9ImdyZWVuIikNCiMjDQpsaW5lcygxOjMsIFN1bmRheSwgbHR5ID0zLCBjb2w9InB1cnBsZSIpDQpwb2ludHMoMTozLCBTdW5kYXksIHBjaD0yMSwgY29sPSJwdXJwbGUiKQ0KIyMjDQpsaW5lcygxOjMsIE1vbmRheSwgbHR5ID00LCBjb2w9ImJsYWNrIikNCnBvaW50cygxOjMsIE1vbmRheSwgcGNoPTIyLCBjb2w9ImJsYWNrIikNCiMjDQpsaW5lcygxOjMsIFR1ZXNkYXksIGx0eSA9NSwgY29sPSJibHVlIikNCnBvaW50cygxOjMsIFR1ZXNkYXksIHBjaD0yMywgY29sPSJibHVlIikNCiMjDQpsaW5lcygxOjMsIFdlZG5lc2RheSwgbHR5ID02LCBjb2w9Im9yYW5nZSIpDQpwb2ludHMoMTozLCBXZWRuZXNkYXksIHBjaD0yNCwgY29sPSJvcmFuZ2UiKQ0KIyMNCmxpbmVzKDE6MywgVGh1cnNkYXksIGx0eSA9NywgY29sPSJ0dXJxdW9pc2UiKQ0KcG9pbnRzKDE6MywgVGh1cnNkYXksIHBjaD0yNSwgY29sPSJ0dXJxdW9pc2UiKQ0KIyMNCg0KDQpsZWdlbmQoInRvcHJpZ2h0IiwgYygiRnJpZGF5IiwiU2F0dXJkYXkiLCAiU3VuZGF5IiwgIk1vbmRheSIsICJUdWVzZGF5IiwgIldlZG5lc2RheSIsICJUaHVyc2RheSIgKSwNCiAgICAgICAgICAgICAgICAgIHBjaD0xOToyMiwgbHR5PTE6NywgIGJ0eT0ibiIsIA0KICAgICAgICBjb2w9YygicmVkIiwgImdyZWVuIiwgInB1cnBsZSIsICJibGFjayIsICJibHVlIiwgIm9yYW5nZSIsICJ0dXJxdW9pc2UiKSkNCmBgYA0KDQoNCkZyb20gdGhlIGdyYXBoIHdlIGNhbiBzZWUgYSBmZXcgdGhpbmdzOg0KDQotIFRoZSBtb3N0IGJpa2VycyBjcm9zcyB0aGUgUXVlZW5zYm9ybyBCcmlkZ2Ugb24gTW9uZGF5IG9uIGF2ZXJhZ2UuDQoNCi0gVGhlcmUgaXMgYSBwb3NpdGl2ZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBhdmVyYWdlIHRlbXBlcmF0dXJlIGFuZCB0aGUgbnVtYmVyIG9mIGN5Y2xpc3RzLg0KDQoNCg0KIyBTdW1tYXJ5IGFuZCBDb25jbHVzaW9uDQoNClRvIHN1bW1hcml6ZSwgd2UgbG9va2VkIGF0IGEgZGF0YSBzZXQgdGhhdCBsb29rcyBhdCBob3cgbWFueSBiaWtlcnMgY3Jvc3Mgb3ZlciB0aGUgUXVlZW5zYm9ybyBCcmlkZ2UgaW4gTllDIGV2ZXJ5IGRheSBmb3IgdGhlIG1vbnRoIG9mIEp1bHksIGxlYXZpbmcgdXMgd2l0aCAzMSBvYnNlcnZhdGlvbnMuIFRoZSA0IGV4cGxhbmF0b3J5IHZhcmlhYmxlcyBsb29rIGF0IHRoZSBkYXkgb2YgdGhlIHdlZWsgYW5kIHRoZSB3ZWF0aGVyIGNvbmRpdGlvbnMgb24gZWFjaCBkYXkuIFdlIGNyZWF0ZWQgdHdvIG5ldyB2YXJpYWJsZXMgaW4gdGhlIGRhdGEgc2V0IGFuZCB1c2VkIHRoZW0gaW4gYWxsIG91ciBtb2RlbHMgYWxvbmcgd2l0aCB0aGUgdmFyaWFibGUgJ0RheSIuIE9uZSB3YXMgY2FsbGVkICJBdmdUZW1wIiBhbmQgaXQgdG9vayB0aGUgYXZlcmFnZSB0ZW1wZXJhdHVyZSBvZiB0aGUgaGlnaCBhbmQgbG93IHRlbXBlcmF0dXJlIHZhcmlhYmxlcy4gVGhlIG90aGVyIHZhcmlhYmxlICJOZXdQcmVjaXAiIGNhdGVnb3JpemVkIHRoZSB2YXJpYWJsZSAiUHJlY2lwaXRhdGlvbiIgYXM6IGlmIFByZWNpcGl0YXRpb24gPSAwLCB0aGVuIE5ld1ByZWNpcCA9IDA7IGlmIFByZWNpcGl0YXRpb24gPiAwLCB0aGVuIE5ld1ByZWNpcCA9IDEgT3VyIGdvYWwgd2FzIHRvIHNlZSB3aGF0IHRoZSByZWxhdGlvbnNoaXAgd2FzIGJldHdlZW4gdGhlc2UgdmFyaWFibGVzIGFuZCB0aGUgbnVtYmVyIG9mIGN5Y2xpc3RzLiBXZSBhbHNvIHdhbnRlZCB0byBzZWUgaWYgdGFraW5nIGludG8gYWNjb3VudCB0aGUgdG90YWwgbnVtYmVyIG9mIGN5Y2xpc3RzIHRoYXQgY3Jvc3Mgc2V2ZXJhbCBtYWpvciBicmlkZ2VzIGluIE5ZQy4gVG8gZmlndXJlIHRoaXMgb3V0LCB3ZSBidWlsdCBhIFBvaXNzb24gZnJlcXVlbmN5IHJlZ3Jlc3Npb24gbW9kZWwsIGEgUG9pc3NvbiBNb2RlbCBvbiByYXRlcywgYW5kIGEgcXVhc2kgcG9pc3NvbiBtb2RlbCwgYW5kIGEgcXVhc2kgcG9pc3NvbiByZWdyZXNzaW9uIG1vZGVsIG9uIHJhdGVzLiANCg0KV2UgZm91bmQgZnJvbSBjYWxjdWxhdGluZyB0aGUgZGlzcGVyc2lvbiBvZiB0aGUgcG9pc3NvbiBtb2RlbCB0aGF0IHRoZSBtZWFuIGFuZCB2YXJpYW5jZSB3ZXJlIG5vdCBlcXVhbC4gQmVjYXVzZSBvZiB0aGlzLCB3ZSBkZWNpZGVkIHRoaXMgd2FzIG5vdCB0aGUgYmVzdCBtb2RlbC4gVGhlIGZpbmFsIG1vZGVsIGNob3NlbiB3YXMgdGhlIHF1YXNpIHBvaXNzb24gbW9kZWwgb24gcmF0ZXMuRGVzcGl0ZSB0aGUgcXVhc2kgbW9kZWxzIGhhdmluZyBsZXNzIHNpZ25pZmljYW50IHAgdmFsdWVzLCB0aGV5IGRvIG5vdCByZXF1aXJlIGVxdWFsIG1lYW4gYW5kIHZhcmlhbmNlLiBBIHJlY29tbWVuZGF0aW9uIGZvciBmdXJ0aGVyIHJlc2VhcmNoIGlzIHRvIHRyeSB0byBmaW5kIGhvdyB0cnVseSBzaWduaWZpY2FudCB0aGUgZXhwbGFuYXRvcnkgdmFyaWFibGVzLiBXaXRoIGVhY2ggbW9kZWwgd2UgdXNlZCwgdGhlIHAgdmFsdWVzIGNoYW5nZWQsIHNvIHRoZXkgd2VyZSBub3QgdmVyeSBtZWFuaW5nZnVsLiANCg0KDQoNCg0K