Introduction
Description
The PGA 2004 dataset contains performance statistics and winnings
data for 196 participants in the PGA (Proffesional Golfers Association)
during the 2004 season. The dataset provides information on several key
aspects of player performance and earnings such as:
Name : The name of each golfer. Age : The age of the player. Average
Drive : The average driving distance of the player in yards Driving
Accuracy: The percentage of drives that land on the fairway, Greens in
Regulation : The percentage of greens reached in regulation, meaning the
number of strokes used by the player to land the ball on the green is
two or more less than par. Average Number of Putts : The average number
of putts per round Save Percentage : The percentage of times a player
saves par or better from around the green Money Rank : The rank of the
player based on total earnings for the season. Number of Events : The
total number of events the player participated in during the season.
Total Winnings : The total amount of money earned by the player over the
course of the season. Average Winnings : The average amount of money
earned per event by the player.
Research Question
We want to investigate what variables affect the players winnings of
this given season. We will look at mainly the average drive and how it
correlates to winnings.
Data Preperation
url <- "https://users.stat.ufl.edu/~winner/data/pga2004.dat"
pga_data <- read.table(url, header = FALSE, fill = TRUE)
pga_data$V1 <- NULL
colnames(pga_data) <- c("Player_name", "Player_Age", "Average_Drive", "Driving_Accuracy", "Greens_on_reg",
"Average_number_putts", "Save_Percent", "Money_Rank",
"Number_events", "Total_Winnings", "Average_Winnings")
head(pga_data)
Player_name Player_Age Average_Drive Driving_Accuracy Greens_on_reg
1 Baddeley 23 288.0 53.1 58.2
2 Scott 24 295.4 57.7 65.6
3 Cejka 34 285.8 64.2 63.8
4 Stolz 34 297.9 59.0 63.0
5 Atwal 31 289.4 60.5 62.5
6 Oberholser 29 284.6 68.8 67.0
Average_number_putts Save_Percent Money_Rank Number_events Total_Winnings
1 1.767 50.9 123 27 632878
2 1.757 59.3 7 16 3724984
3 1.795 50.7 54 24 1313484
4 1.787 47.7 101 20 808373
5 1.766 43.5 146 30 486053
6 1.780 50.9 52 23 1355433
Average_Winnings
1 23440
2 232812
3 54729
4 40419
5 16202
6 58932
We take a broad look at the data and see there was two categorical
variables one of which having the first name of a player and then a
second one with the last name. We void the first name and just look at
the last names.
pander(head(pga_data))
Table continues below
Baddeley |
23 |
288 |
53.1 |
58.2 |
Scott |
24 |
295.4 |
57.7 |
65.6 |
Cejka |
34 |
285.8 |
64.2 |
63.8 |
Stolz |
34 |
297.9 |
59 |
63 |
Atwal |
31 |
289.4 |
60.5 |
62.5 |
Oberholser |
29 |
284.6 |
68.8 |
67 |
Table continues below
1.767 |
50.9 |
123 |
27 |
1.757 |
59.3 |
7 |
16 |
1.795 |
50.7 |
54 |
24 |
1.787 |
47.7 |
101 |
20 |
1.766 |
43.5 |
146 |
30 |
1.78 |
50.9 |
52 |
23 |
632878 |
23440 |
3724984 |
232812 |
1313484 |
54729 |
808373 |
40419 |
486053 |
16202 |
1355433 |
58932 |
cor(pga_data[, c("Average_Drive" , "Driving_Accuracy" , "Greens_on_reg" , "Average_number_putts" , "Save_Percent" , "Money_Rank" , "Number_events" , "Total_Winnings" , "Average_Winnings")], use = "pairwise.complete.obs")
Average_Drive Driving_Accuracy Greens_on_reg
Average_Drive 1.000000000 -0.99252269 0.002134259
Driving_Accuracy -0.992522688 1.00000000 0.046962093
Greens_on_reg 0.002134259 0.04696209 1.000000000
Average_number_putts -0.988803391 0.99389638 0.022156599
Save_Percent 0.867471569 -0.87937510 -0.056544353
Money_Rank 0.186561899 -0.20345865 -0.498016597
Number_events -0.699109599 0.68370009 0.062783323
Total_Winnings 0.226265605 -0.21129425 0.405497292
Average_Winnings -0.773878692 0.79872244 0.013823260
Average_number_putts Save_Percent Money_Rank
Average_Drive -0.9888034 0.86747157 0.18656190
Driving_Accuracy 0.9938964 -0.87937510 -0.20345865
Greens_on_reg 0.0221566 -0.05654435 -0.49801660
Average_number_putts 1.0000000 -0.88762172 -0.19710155
Save_Percent -0.8876217 1.00000000 0.06385765
Money_Rank -0.1971016 0.06385765 1.00000000
Number_events 0.6924957 -0.63927742 -0.11418017
Total_Winnings -0.2044137 0.27127779 -0.66596035
Average_Winnings 0.7948355 -0.68320935 -0.27537388
Number_events Total_Winnings Average_Winnings
Average_Drive -0.69910960 0.226265605 -0.773878692
Driving_Accuracy 0.68370009 -0.211294247 0.798722439
Greens_on_reg 0.06278332 0.405497292 0.013823260
Average_number_putts 0.69249567 -0.204413695 0.794835458
Save_Percent -0.63927742 0.271277789 -0.683209349
Money_Rank -0.11418017 -0.665960350 -0.275373883
Number_events 1.00000000 -0.180433459 0.190800029
Total_Winnings -0.18043346 1.000000000 0.002463485
Average_Winnings 0.19080003 0.002463485 1.000000000
We see that a lot of our variables have high correlation which can
cause multicollinearity. We must deal with this issue before doing our
analysis.
colSums(is.na(pga_data))
Player_name Player_Age Average_Drive
0 0 10
Driving_Accuracy Greens_on_reg Average_number_putts
10 10 10
Save_Percent Money_Rank Number_events
10 10 10
Total_Winnings Average_Winnings
10 10
pga_data_clean <- na.omit(pga_data)
For the sake of missing observations we will be removing it for our
data analysis because we have a plentiful amount of observations.
ggplot(pga_data_clean, aes(x = Average_Drive, y = Average_Winnings)) +
geom_point() +
geom_smooth(method = "lm", col = "green") +
labs(title = "Scatter Plot of Average Drive vs. Average Winnings", x = "Average Drive", y = "Average Winnings")
`geom_smooth()` using formula = 'y ~ x'

str(pga_data_clean)
'data.frame': 196 obs. of 11 variables:
$ Player_name : chr "Baddeley" "Scott" "Cejka" "Stolz" ...
$ Player_Age : chr "23" "24" "34" "34" ...
$ Average_Drive : num 288 295 286 298 289 ...
$ Driving_Accuracy : num 53.1 57.7 64.2 59 60.5 68.8 74.2 64.4 64.3 62.6 ...
$ Greens_on_reg : num 58.2 65.6 63.8 63 62.5 67 68.9 64.2 63.4 65.3 ...
$ Average_number_putts: num 1.77 1.76 1.79 1.79 1.77 ...
$ Save_Percent : num 50.9 59.3 50.7 47.7 43.5 50.9 40.4 53.8 42.2 47.7 ...
$ Money_Rank : num 123 7 54 101 146 52 80 75 141 83 ...
$ Number_events : int 27 16 24 20 30 23 23 27 20 15 ...
$ Total_Winnings : int 632878 3724984 1313484 808373 486053 1355433 962167 1036958 500818 943589 ...
$ Average_Winnings : int 23440 232812 54729 40419 16202 58932 41833 38406 25041 62906 ...
- attr(*, "na.action")= 'omit' Named int [1:10] 15 32 54 58 68 110 142 155 198 200
..- attr(*, "names")= chr [1:10] "15" "32" "54" "58" ...
remove_low_drives <- function(pga_data_clean) {
filtered_data <- pga_data_clean %>%
filter(Average_Drive >= 150)
return(filtered_data)
}
# Example usage
pga_data_filtered <- remove_low_drives(pga_data_clean)
ggplot(pga_data_filtered, aes(x = Average_Drive, y = Average_Winnings)) +
geom_point() +
geom_smooth(method = "lm", col = "green") +
labs(
title = "Scatter Plot of Average Drive vs. Average Winnings (Filtered)",
x = "Average Drive",
y = "Average Winnings"
) +
theme_minimal()
`geom_smooth()` using formula = 'y ~ x'

We look at an initial graph of average winnigs vs average drive. It
looks like we have a good cluster of data but we have a couple of
outliers in our data that is scewing our line.
pga_data_filtered$Age_Above_30 <- pga_data_filtered$Player_Age > 30
head(pga_data_filtered$Age_Above_30)
[1] FALSE FALSE TRUE TRUE TRUE FALSE
pga_data_filtered <- pga_data_filtered %>% select(-Player_name, -Driving_Accuracy, -Total_Winnings, -Average_number_putts, -Money_Rank, -Player_Age)
kable(head(pga_data_filtered))
288.0 |
58.2 |
50.9 |
27 |
23440 |
FALSE |
295.4 |
65.6 |
59.3 |
16 |
232812 |
FALSE |
285.8 |
63.8 |
50.7 |
24 |
54729 |
TRUE |
297.9 |
63.0 |
47.7 |
20 |
40419 |
TRUE |
289.4 |
62.5 |
43.5 |
30 |
16202 |
TRUE |
284.6 |
67.0 |
50.9 |
23 |
58932 |
FALSE |
We remove player name and money rank because we are “ranking” them or
sorting them by +/- 30 age. We remove driving accuracy because we have a
variable with average drive because that is what we are making
assumptions on and also it could cause multicollinearity because driving
accuracy and average drive are highly correlated. The same can be said
for total winnings versus average winnings and also save percent and
number of putts. They are both correlated and can cause
multicollinearity.
Model Bulding
Linear Model
colnames(pga_data_filtered)
[1] "Average_Drive" "Greens_on_reg" "Save_Percent" "Number_events"
[5] "Average_Winnings" "Age_Above_30"
full.model = lm(Average_Winnings ~ Average_Drive + Greens_on_reg + Save_Percent + Number_events + Age_Above_30, data = pga_data_filtered)
kable(summary(full.model)$coef, caption ="Statistics of Regression Coefficients")
Statistics of Regression Coefficients
(Intercept) |
-802913.292 |
147739.1904 |
-5.434667 |
0.0000002 |
Average_Drive |
1198.229 |
435.7615 |
2.749735 |
0.0065731 |
Greens_on_reg |
7278.954 |
1205.2877 |
6.039183 |
0.0000000 |
Save_Percent |
2486.926 |
627.6347 |
3.962378 |
0.0001069 |
Number_events |
-3508.708 |
723.2608 |
-4.851234 |
0.0000026 |
Age_Above_30TRUE |
3537.995 |
8630.5405 |
0.409939 |
0.6823382 |
We transform the age category into a true or false of payers with
above or below age 30. All of our values are significant besides
Age.
par(mfrow=c(2,2))
plot(full.model)

It looks like we have faults in our assumptions. Based on the QQ plot
there seems to be a non-normal distribution with several outliers. There
also seems to be non-constant variance.
vif(full.model)
Average_Drive Greens_on_reg Save_Percent Number_events Age_Above_30
1.136250 1.044126 1.047080 1.018950 1.072829
barplot(vif(full.model), main = "VIF Values", horiz = FALSE, col = "steelblue")

We see in the vif values above that all of them are below 5 which
shows little to no multicollineairty so we can continue.
Box Cox
Transformation
We perform this transformation because our data has non constant
variance
par(pty = "s", mfrow = c(2, 2), oma=c(.1,.1,.1,.1), mar=c(4, 0, 2, 0))
##
boxcox(Average_Winnings ~ log(Average_Drive) + Greens_on_reg + Save_Percent + Number_events + Age_Above_30, data = pga_data_filtered, lambda = seq(-1, 1, length = 10),
xlab=expression(paste(lambda, ": log Average Drive")))
##
boxcox(Average_Winnings ~ Average_Drive + Greens_on_reg + Save_Percent + Number_events + Age_Above_30, data = pga_data_filtered, lambda = seq(-1, 1, length = 10),
xlab=expression(paste(lambda, ": Average Drive")))
##
boxcox(Average_Winnings ~ Average_Drive + Greens_on_reg + Save_Percent + Number_events + log(1+Age_Above_30), data = pga_data_filtered, lambda = seq(-1, 1, length = 10),
xlab=expression(paste(lambda, ": log-age")))
##
boxcox(Average_Winnings ~ log(Average_Drive) + Greens_on_reg + Save_Percent + Number_events + log(1+Age_Above_30), data = pga_data_filtered, lambda = seq(-1, 1, length = 10),
xlab=expression(paste(lambda, ": log-age, log Average Drive")))

Values of lamda are around 0 so we shoudl use a log transformation ##
Square Root Model
sqrt.winnings.log.drive = lm((Average_Winnings)^0.5 ~ Greens_on_reg + Save_Percent + Number_events + Age_Above_30 + log(Average_Drive), data = pga_data_filtered)
kable(summary(sqrt.winnings.log.drive)$coef, caption = "log-transformed model")
log-transformed model
(Intercept) |
-3187.392815 |
1176.262371 |
-2.7097635 |
0.0073840 |
Greens_on_reg |
16.184221 |
1.999182 |
8.0954209 |
0.0000000 |
Save_Percent |
5.154888 |
1.041392 |
4.9499977 |
0.0000017 |
Number_events |
-6.056634 |
1.199193 |
-5.0505915 |
0.0000011 |
Age_Above_30TRUE |
2.354375 |
14.312534 |
0.1644974 |
0.8695242 |
log(Average_Drive) |
394.504246 |
208.497492 |
1.8921295 |
0.0600792 |
par(mfrow = c(2,2))
plot(sqrt.winnings.log.drive)

This model worsened our QQ-plot normality and our non constant
variance remained the same, so we can try another model to see if it
helps.
Comparison of
models
par(pty = "s", mfrow = c(1, 3))
qqnorm(full.model$residuals, main = "Full-Model")
qqline(full.model$residuals)
qqnorm(log.winnings$residuals, main = "Log Winnings")
qqline(log.winnings$residuals)
qqnorm(sqrt.winnings.log.drive$residuals, main = "sqrt winnings log drive")
qqline(sqrt.winnings.log.drive$residuals)

We can see that Log Winnings is the best model for our data.
Goodness of Fit
Comparison
select=function(m){
e = m$resid
n0 = length(e)
SSE=(m$df)*(summary(m)$sigma)^2
R.sq=summary(m)$r.squared
R.adj=summary(m)$adj.r
MSE=(summary(m)$sigma)^2
Cp=(SSE/MSE)-(n0-2*(n0-m$df))
AIC=n0*log(SSE)-n0*log(n0)+2*(n0-m$df)
SBC=n0*log(SSE)-n0*log(n0)+(log(n0))*(n0-m$df)
X=model.matrix(m)
H=X%*%solve(t(X)%*%X)%*%t(X)
d=e/(1-diag(H))
PRESS=t(d)%*%d
tbl = as.data.frame(cbind(SSE=SSE, R.sq=R.sq, R.adj = R.adj, Cp = Cp, AIC = AIC, SBC = SBC, PRD = PRESS))
names(tbl)=c("SSE", "R.sq", "R.adj", "Cp", "AIC", "SBC", "PRESS")
tbl
}
output.sum = rbind(select(full.model), select(sqrt.winnings.log.drive), select(log.winnings))
row.names(output.sum) = c("full.model", "sqrt.winnings.log.drive", "log.winnings")
kable(output.sum, caption = "Goodness-of-fit Measures of Candidate Models")
Goodness-of-fit Measures of Candidate Models
full.model |
3.852006e+11 |
0.3349285 |
0.3164543 |
6 |
4001.9387 |
4021.2932 |
4.229954e+11 |
sqrt.winnings.log.drive |
1.058809e+06 |
0.4162839 |
0.4000695 |
6 |
1620.3250 |
1639.6795 |
1.143272e+06 |
log.winnings |
1.002463e+02 |
0.4547915 |
0.4396468 |
6 |
-102.9696 |
-83.6151 |
1.075339e+02 |
Even though the R, R squared, and Cp look better in the other two
models our value are still relatively good for log.winnings.
Log.winnings has better residuals, QQ plot, but less goodness of fit
measures but they are not significantly bad so we will stick with the
log model.
Final Model
kable(summary(log.winnings)$coef, caption = "Inferential Statistics of Final Model")
Inferential Statistics of Final Model
(Intercept) |
-5.4512847 |
2.3833429 |
-2.2872432 |
0.0233453 |
Average_Drive |
0.0057567 |
0.0070297 |
0.8189126 |
0.4139191 |
Greens_on_reg |
0.1920075 |
0.0194438 |
9.8749896 |
0.0000000 |
Save_Percent |
0.0563106 |
0.0101251 |
5.5615033 |
0.0000001 |
Number_events |
-0.0450036 |
0.0116677 |
-3.8571026 |
0.0001596 |
Age_Above_30TRUE |
0.0333772 |
0.1392287 |
0.2397290 |
0.8108131 |
While we do have a large sample of 196 and all of our pvalues are
close to 0 we do have one variable “Age” which is not significant.
Summary of Model
The final model can be represented as log(price)=3.9245 −
0.0246×Average_Drive + 0.1866×Greens_on_reg + 0.0387×Save_percent −
0.0181×Number_events + 0.1826×Age_Above_30TRUE
From the above data we can also conclude that as average winnings go
up our average drive goes down by -2.5% while other variables such as
greens on regulation and save percent go up.
log.winnings = lm(log(Average_Winnings) ~ Average_Drive + Greens_on_reg + Save_Percent + Number_events + Age_Above_30, data = pga_data_filtered)
B = 1000 #
num.p = length(coef(log.winnings)) # number of parameters in the model (includes intercept)
smpl.n = nrow(pga_data_filtered) # sample size
# Matrix to store bootstrap coefficients
coef.mtrx = matrix(0, nrow = B, ncol = num.p)
# Bootstrap loop
for (i in 1:B) {
bootc.id = sample(1:smpl.n, smpl.n, replace = TRUE) # Resample with replacement
boot_data = pga_data_filtered[bootc.id, ] # Create the bootstrap sample
log.winnings.btc = lm(log(Average_Winnings) ~ Average_Drive + Greens_on_reg + Save_Percent + Number_events + Age_Above_30, data = boot_data)
coef.mtrx[i, ] = coef(log.winnings.btc) # Store the coefficients
}
Bootstrapping Final
Model
boot.hist = function(cmtrx, bt.coef.mtrx, var.id, var.nm){
x1.1 <- seq(min(bt.coef.mtrx[,var.id]), max(bt.coef.mtrx[,var.id]), length=300 )
y1.1 <- dnorm(x1.1, mean(bt.coef.mtrx[,var.id]), sd(bt.coef.mtrx[,var.id]))
highestbar = max(hist(bt.coef.mtrx[,var.id], plot = FALSE)$density)
ylimit <- max(c(y1.1,highestbar))
hist(bt.coef.mtrx[,var.id], probability = TRUE, main = var.nm, xlab="",
col = "azure1",ylim=c(0,ylimit), border="lightseagreen")
lines(x = x1.1, y = y1.1, col = "red3")
lines(density(bt.coef.mtrx[,var.id], adjust=2), col="blue")
}
par(mfrow=c(2,3))
boot.hist(bt.coef.mtrx=coef.mtrx, var.id=1, var.nm ="Intercept" )
boot.hist(bt.coef.mtrx=coef.mtrx, var.id=2, var.nm ="Average Drive" )
boot.hist(bt.coef.mtrx=coef.mtrx, var.id=3, var.nm ="Greens On Regulation" )
boot.hist(bt.coef.mtrx=coef.mtrx, var.id=4, var.nm ="Save Percentage" )
boot.hist(bt.coef.mtrx=coef.mtrx, var.id=5, var.nm ="Number of Events" )
boot.hist(bt.coef.mtrx=coef.mtrx, var.id=6, var.nm ="Age Above 30" )

We see from our histograms that they look to have a equal
distribution. The blue curve represents the p-values reported. While the
blue curve represents the bootstrap intervals. They are very
similar.
95% CI
num.p = dim(coef.mtrx)[2]
btc.ci = NULL
btc.wd = NULL
for (i in 1:num.p) {
lci.025 = round(quantile(coef.mtrx[, i], 0.025, type = 2), 8)
uci.975 = round(quantile(coef.mtrx[, i], 0.975, type = 2), 8)
btc.wd[i] = uci.975 - lci.025
btc.ci[i] = paste("[", round(lci.025, 4), ", ", round(uci.975, 4), "]")
}
mean.coefs = apply(coef.mtrx, 2, mean)
results = as.data.frame(cbind(Mean_Coef = formatC(mean.coefs, 4, format = "f"), btc.ci.95 = btc.ci))
kable(results, caption = "Regression Coefficient Matrix with Bootstrap Confidence Intervals")
Regression Coefficient Matrix with Bootstrap Confidence
Intervals
-5.4436 |
[ -10.4839 , -0.2628 ] |
0.0059 |
[ -0.0065 , 0.0181 ] |
0.1917 |
[ 0.1442 , 0.236 ] |
0.0559 |
[ 0.038 , 0.0752 ] |
-0.0452 |
[ -0.0696 , -0.0216 ] |
0.0298 |
[ -0.2617 , 0.3228 ] |
We can see that our values are consistent with the p-values that we
got. Our 95% CI spans over 0 in the Age variable which matches up with
our p-value not being significant
hist(sort(log.winnings$residuals),n=40,
xlab="Residuals",
col = "lightblue",
border="navy",
main = "Histogram of Residuals")

We appear to have a normal distribution but we do appear to have some
outliers on either side of the curve.
Residual Bootstrap
Regression
log.winnings <- lm(log(Average_Winnings) ~ Average_Drive + Greens_on_reg + Save_Percent + Number_events + Age_Above_30, data = pga_data_filtered)
model.resid = log.winnings$residuals
B=1000
num.p = dim(model.matrix(log.winnings))[2]
samp.n = dim(model.matrix(log.winnings))[1]
btr.mtrx = matrix(rep(0,6*B), ncol=num.p)
for (i in 1:B){
bt.lg.winnings = log.winnings$fitted.values +
sample(log.winnings$residuals, samp.n, replace = TRUE)
pga_data_filtered$bt.lg.winnings = bt.lg.winnings # send the boot response to the data
btr.model = lm(bt.lg.winnings ~ Average_Drive + Greens_on_reg + Save_Percent + Number_events + Age_Above_30, data = pga_data_filtered)
btr.mtrx[i,]=btr.model$coefficients
}
boot.hist = function(bt.coef.mtrx, var.id, var.nm){
x1.1 <- seq(min(bt.coef.mtrx[,var.id]), max(bt.coef.mtrx[,var.id]), length=300 )
y1.1 <- dnorm(x1.1, mean(bt.coef.mtrx[,var.id]), sd(bt.coef.mtrx[,var.id]))
highestbar = max(hist(bt.coef.mtrx[,var.id], plot = FALSE)$density)
ylimit <- max(c(y1.1,highestbar))
hist(bt.coef.mtrx[,var.id], probability = TRUE, main = var.nm, xlab="",
col = "azure1",ylim=c(0,ylimit), border="lightseagreen")
lines(x = x1.1, y = y1.1, col = "red3")
lines(density(bt.coef.mtrx[,var.id], adjust=2), col="blue")
}
par(mfrow=c(2,3))
boot.hist(bt.coef.mtrx=btr.mtrx, var.id=1, var.nm ="Intercept" )
boot.hist(bt.coef.mtrx=btr.mtrx, var.id=2, var.nm ="Average Drive" )
boot.hist(bt.coef.mtrx=btr.mtrx, var.id=3, var.nm ="Greens on Regulation" )
boot.hist(bt.coef.mtrx=btr.mtrx, var.id=4, var.nm ="Save Percentage" )
boot.hist(bt.coef.mtrx=btr.mtrx, var.id=5, var.nm ="Number of Events" )
boot.hist(bt.coef.mtrx=btr.mtrx, var.id=6, var.nm ="Age" )

We again see that our histograms have an equal distribution. Again we
see that the p-values and residual boostrap have very similar
values.
um.p = dim(btr.mtrx)[2]
btr.ci = NULL
btr.wd = NULL
for (i in 1:num.p) {
lci.025 = round(quantile(btr.mtrx[, i], 0.025, type = 2), 8)
uci.975 = round(quantile(btr.mtrx[, i], 0.975, type = 2), 8)
btr.wd[i] = uci.975 - lci.025
btr.ci[i] = paste("[", round(lci.025, 4), ", ", round(uci.975, 4), "]")
}
kable(as.data.frame(cbind(formatC(btr.mtrx[1, ], 4, format = "f"), btr.ci.95 = btr.ci)),
caption = "Regression Coefficient Matrix with 95% Residual Bootstrap CI")
Regression Coefficient Matrix with 95% Residual Bootstrap
CI
-3.9016 |
[ -10.2057 , -1.1977 ] |
-0.0006 |
[ -0.0066 , 0.0192 ] |
0.2044 |
[ 0.155 , 0.2292 ] |
0.0500 |
[ 0.0369 , 0.0758 ] |
-0.0583 |
[ -0.0669 , -0.0231 ] |
0.1272 |
[ -0.2253 , 0.291 ] |
Once again we get the same output as expected. The values above show
the same results as our p-values, all being significant except for age
which includes 0.
Combining
results
p_values <- summary(log.winnings)$coefficients[-3, 4]
combined_matrix <- cbind(
Coefficients = formatC(coef(log.winnings)[-3], 4, format = "f"),
`95% CI (Bootstrap t)` = btc.ci,
`95% CI (Bootstrap r)` = btr.ci,
`p-values` = formatC(p_values, 4, format = "f")
)
Warning in cbind(Coefficients = formatC(coef(log.winnings)[-3], 4, format =
"f"), : number of rows of result is not a multiple of vector length (arg 1)
library(knitr)
kable(as.data.frame(combined_matrix),
caption = "Final Combined Inferential Statistics: Coefficients, p-values, and Bootstrap CIs")
Final Combined Inferential Statistics: Coefficients, p-values,
and Bootstrap CIs
-5.4513 |
[ -10.4839 , -0.2628 ] |
[ -10.2057 , -1.1977 ] |
0.0233 |
0.0058 |
[ -0.0065 , 0.0181 ] |
[ -0.0066 , 0.0192 ] |
0.4139 |
0.0563 |
[ 0.1442 , 0.236 ] |
[ 0.155 , 0.2292 ] |
0.0000 |
-0.0450 |
[ 0.038 , 0.0752 ] |
[ 0.0369 , 0.0758 ] |
0.0002 |
0.0334 |
[ -0.0696 , -0.0216 ] |
[ -0.0669 , -0.0231 ] |
0.8108 |
-5.4513 |
[ -0.2617 , 0.3228 ] |
[ -0.2253 , 0.291 ] |
0.0233 |
All of the models yield the same result, showing there is a violation
in the age variable but all other variables are significant.
kable(round(cbind(btc.wd, btr.wd),4), caption="width of the two bootstrap confidence intervals")
width of the two bootstrap confidence intervals
10.2211 |
9.0080 |
0.0245 |
0.0258 |
0.0918 |
0.0741 |
0.0372 |
0.0389 |
0.0480 |
0.0438 |
0.5845 |
0.5163 |
We see from the above analysis that the two values are very similar
to each other.
Summary
We see from our findings above that all three of our models come to
the same conclusion. We will be using our initial final model where we
used a log transformation because they seem to be a little more
significant.
Again we see that the final model can be represented as
log(price)=3.9245 − 0.0246×Average_Drive + 0.1866×Greens_on_reg +
0.0387×Save_percent − 0.0181×Number_events + 0.1826×Age_Above_30TRUE
From the above data we can also conclude that as average winnings go
up our average drive goes down by -2.5% while other variables such as
greens on regulation and save percent go up.
Conclusion
The main conclusion we can draw is that while driving distance in
golf is perceived to be a big indication of whether a person is winning
or not we do not see this in our analysis. While the Average Winnings
goes up the average drive goes down by -2.5% while Save Percentage goes
up by 3.9% and Greens on Regulation goes up by 1.9% which shows that the
“short” game in golf (putting and chipping) is more important than
driving in regards to average money won.
We see one flaw in our analysis and it was that age was not
significant which could mess up our analysis. in the future we could
push our age up or down for the true or false statement to see if that
changes the significance. We could also look at some values of age to
see if there are any outliers, for example golfers are not younger than
20 but they can be 45+ which could mess with our significance.
We can use this model for seasons to come to see if things may have
changed. For example since golf has evolved recently to players having
longer drives we could run this model again in 2024 to see if the
“short” game is less likely to win more money than the “long” game
would.
LS0tDQp0aXRsZTogIkZhY3RvcnMgSW5mbHVlbmNlIEdvbGYgRWFybmluZ3MiDQphdXRob3I6ICdUeWxlciBCYXR0YWdsaW5pJw0KZGF0ZTogIjIwMjQtMDktMTkiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6IA0KICAgIHRvYzogeWVzDQogICAgdG9jX2RlcHRoOiA0DQogICAgdG9jX2Zsb2F0OiB5ZXMNCiAgICBmaWdfd2lkdGg6IDQNCiAgICBmaWdfY2FwdGlvbjogeWVzDQogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMNCiAgICB0b2NfY29sbGFwc2VkOiB5ZXMNCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUNCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMNCiAgICBzbW9vdGhfc2Nyb2xsOiB5ZXMNCiAgICB0aGVtZTogbHVtZW4NCiAgd29yZF9kb2N1bWVudDogDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZGVwdGg6IDQNCiAgICBmaWdfY2FwdGlvbjogeWVzDQogICAga2VlcF9tZDogeWVzDQogIHBkZl9kb2N1bWVudDogDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZGVwdGg6IDQNCiAgICBmaWdfY2FwdGlvbjogeWVzDQogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMNCiAgICBmaWdfd2lkdGg6IDMNCiAgICBmaWdfaGVpZ2h0OiAzDQplZGl0b3Jfb3B0aW9uczogDQogIGNodW5rX291dHB1dF90eXBlOiBpbmxpbmUNCmFsd2F5c19hbGxvd19odG1sOiB0cnVlDQotLS0NCg0KYGBgez1odG1sfQ0KDQo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPg0KDQovKiBDYXNjYWRpbmcgU3R5bGUgU2hlZXRzIChDU1MpIGlzIGEgc3R5bGVzaGVldCBsYW5ndWFnZSB1c2VkIHRvIGRlc2NyaWJlIHRoZSBwcmVzZW50YXRpb24gb2YgYSBkb2N1bWVudCB3cml0dGVuIGluIEhUTUwgb3IgWE1MLiBpdCBpcyBhIHNpbXBsZSBtZWNoYW5pc20gZm9yIGFkZGluZyBzdHlsZSAoZS5nLiwgZm9udHMsIGNvbG9ycywgc3BhY2luZykgdG8gV2ViIGRvY3VtZW50cy4gKi8NCg0KaDEudGl0bGUgeyAgLyogVGl0bGUgLSBmb250IHNwZWNpZmljYXRpb25zIG9mIHRoZSByZXBvcnQgdGl0bGUgKi8NCiAgZm9udC1zaXplOiAyNHB4Ow0KICBmb250LXdlaWdodDogYm9sZDsNCiAgY29sb3I6IERhcmtSZWQ7DQogIHRleHQtYWxpZ246IGNlbnRlcjsNCiAgZm9udC1mYW1pbHk6ICJHaWxsIFNhbnMiLCBzYW5zLXNlcmlmOw0KfQ0KaDQuYXV0aG9yIHsgLyogSGVhZGVyIDQgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciBhdXRob3JzICAqLw0KICBmb250LXNpemU6IDIwcHg7DQogIGZvbnQtd2VpZ2h0OiBib2xkOw0KICBmb250LWZhbWlseTogc3lzdGVtLXVpOw0KICBjb2xvcjogRGFya1JlZDsNCiAgdGV4dC1hbGlnbjogY2VudGVyOw0KfQ0KaDQuZGF0ZSB7IC8qIEhlYWRlciA0IC0gZm9udCBzcGVjaWZpY2F0aW9ucyBmb3IgdGhlIGRhdGUgICovDQogIGZvbnQtc2l6ZTogMThweDsNCiAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogIGZvbnQtZmFtaWx5OiBzeXN0ZW0tdWk7DQogIGNvbG9yOiBEYXJrQmx1ZTsNCiAgdGV4dC1hbGlnbjogY2VudGVyOw0KfQ0KaDEgeyAvKiBIZWFkZXIgMSAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgZm9yIGxldmVsIDEgc2VjdGlvbiB0aXRsZSAgKi8NCiAgICBmb250LXNpemU6IDIycHg7DQogICAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogICAgZm9udC1mYW1pbHk6IHN5c3RlbS11aTsNCiAgICBjb2xvcjogbmF2eTsNCiAgICB0ZXh0LWFsaWduOiBsZWZ0Ow0KfQ0KaDIgeyAvKiBIZWFkZXIgMiAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgZm9yIGxldmVsIDIgc2VjdGlvbiB0aXRsZSAqLw0KICAgIGZvbnQtc2l6ZTogMjBweDsNCiAgICBmb250LXdlaWdodDogYm9sZDsNCiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgICBjb2xvcjogbmF2eTsNCiAgICB0ZXh0LWFsaWduOiBsZWZ0Ow0KfQ0KDQpoMyB7IC8qIEhlYWRlciAzIC0gZm9udCBzcGVjaWZpY2F0aW9ucyBvZiBsZXZlbCAzIHNlY3Rpb24gdGl0bGUgICovDQogICAgZm9udC1zaXplOiAxOHB4Ow0KICAgIGZvbnQtd2VpZ2h0OiBib2xkOw0KICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOw0KICAgIGNvbG9yOiBuYXZ5Ow0KICAgIHRleHQtYWxpZ246IGxlZnQ7DQp9DQoNCmg0IHsgLyogSGVhZGVyIDQgLSBmb250IHNwZWNpZmljYXRpb25zIG9mIGxldmVsIDQgc2VjdGlvbiB0aXRsZSAgKi8NCiAgICBmb250LXNpemU6IDE4cHg7DQogICAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogICAgY29sb3I6IGRhcmtyZWQ7DQogICAgdGV4dC1hbGlnbjogbGVmdDsNCn0NCg0KYm9keSB7IGJhY2tncm91bmQtY29sb3I6d2hpdGU7IH0NCg0KLmhpZ2hsaWdodG1lIHsgYmFja2dyb3VuZC1jb2xvcjp5ZWxsb3c7IH0NCg0KcCB7IGJhY2tncm91bmQtY29sb3I6d2hpdGU7IH0NCg0KPC9zdHlsZT4NCmBgYA0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCiMgRGV0ZWN0LCBpbnN0YWxsIGFuZCBsb2FkIHBhY2thZ2VzIGlmIG5lZWRlZC4NCmlmICghcmVxdWlyZSgia25pdHIiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygia25pdHIiKQ0KICAgbGlicmFyeShrbml0cikNCn0NCmlmICghcmVxdWlyZSgiTUFTUyIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJNQVNTIikNCiAgIGxpYnJhcnkoTUFTUykNCn0NCmlmICghcmVxdWlyZSgibmxlcXNsdiIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJubGVxc2x2IikNCiAgIGxpYnJhcnkobmxlcXNsdikNCn0NCiMNCmlmICghcmVxdWlyZSgicGFuZGVyIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoInBhbmRlciIpDQogICBsaWJyYXJ5KHBhbmRlcikNCn0NCg0KaWYgKCFyZXF1aXJlKCJwc3ljaCIpKSB7ICAgDQogIGluc3RhbGwucGFja2FnZXMoInBzeWNoIikNCiAgIGxpYnJhcnkocHN5Y2gpDQp9DQppZiAoIXJlcXVpcmUoIk1BU1MiKSkgeyAgIA0KICBpbnN0YWxsLnBhY2thZ2VzKCJNQVNTIikNCiAgIGxpYnJhcnkoTUFTUykNCn0NCmlmICghcmVxdWlyZSgiZ2dwbG90MiIpKSB7ICAgDQogIGluc3RhbGwucGFja2FnZXMoImdncGxvdDIiKQ0KICAgbGlicmFyeShnZ3Bsb3QyKQ0KfQ0KaWYgKCFyZXF1aXJlKCJHR2FsbHkiKSkgeyAgIA0KICBpbnN0YWxsLnBhY2thZ2VzKCJHR2FsbHkiKQ0KICAgbGlicmFyeShHR2FsbHkpDQp9DQppZiAoIXJlcXVpcmUoImNhciIpKSB7ICAgDQogIGluc3RhbGwucGFja2FnZXMoImNhciIpDQogICBsaWJyYXJ5KGNhcikNCn0NCmlmICghcmVxdWlyZSgiZHBseXIiKSkgeyAgIA0KICBpbnN0YWxsLnBhY2thZ2VzKCJkcGx5ciIpDQogICBsaWJyYXJ5KGRwbHlyKQ0KfQ0KaWYgKCFyZXF1aXJlKCJjYXJldCIpKSB7ICAgDQogIGluc3RhbGwucGFja2FnZXMoImNhcmV0IikNCiAgIGxpYnJhcnkoY2FyZXQpDQp9DQoNCiMgc3BlY2lmaWNhdGlvbnMgb2Ygb3V0cHV0cyBvZiBjb2RlIGluIGNvZGUgY2h1bmtzDQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsICAgICAgIyBpbmNsdWRlIGNvZGUgY2h1bmsgaW4gdGhlIG91dHB1dCBmaWxlDQogICAgICAgICAgICAgICAgICAgICAgd2FybmluZ3MgPSBGQUxTRSwgICMgc29tZXRpbWVzLCB5b3UgY29kZSBtYXkgcHJvZHVjZSB3YXJuaW5nIG1lc3NhZ2VzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHlvdSBjYW4gY2hvb3NlIHRvIGluY2x1ZGUgdGhlIHdhcm5pbmcgbWVzc2FnZXMgaW4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB0aGUgb3V0cHV0IGZpbGUuIA0KICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2VzID0gRkFMU0UsICAjDQogICAgICAgICAgICAgICAgICAgICAgcmVzdWx0cyA9IFRSVUUsDQogICAgICAgICAgICAgICAgICAgICAgDQogICAgICAgICAgICAgICAgICAgICAgY29tbWVudCA9IE5BICAgICAgICMgeW91IGNhbiBhbHNvIGRlY2lkZSB3aGV0aGVyIHRvIGluY2x1ZGUgdGhlIG91dHB1dA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGluIHRoZSBvdXRwdXQgZmlsZS4NCiAgICAgICAgICAgICAgICAgICAgICApICAgDQpgYGANCg0KDQojIyMgSW50cm9kdWN0aW9uDQoNCiMjIERlc2NyaXB0aW9uDQpUaGUgUEdBIDIwMDQgZGF0YXNldCBjb250YWlucyBwZXJmb3JtYW5jZSBzdGF0aXN0aWNzIGFuZCB3aW5uaW5ncyBkYXRhIGZvciAxOTYgcGFydGljaXBhbnRzIGluIHRoZSBQR0EgKFByb2ZmZXNpb25hbCBHb2xmZXJzIEFzc29jaWF0aW9uKSBkdXJpbmcgdGhlIDIwMDQgc2Vhc29uLiBUaGUgZGF0YXNldCBwcm92aWRlcyBpbmZvcm1hdGlvbiBvbiBzZXZlcmFsIGtleSBhc3BlY3RzIG9mIHBsYXllciBwZXJmb3JtYW5jZSBhbmQgZWFybmluZ3Mgc3VjaCBhczoNCg0KTmFtZSA6IFRoZSBuYW1lIG9mIGVhY2ggZ29sZmVyLg0KQWdlIDogVGhlIGFnZSBvZiB0aGUgcGxheWVyLg0KQXZlcmFnZSBEcml2ZSA6IFRoZSBhdmVyYWdlIGRyaXZpbmcgZGlzdGFuY2Ugb2YgdGhlIHBsYXllciBpbiB5YXJkcw0KRHJpdmluZyBBY2N1cmFjeTogVGhlIHBlcmNlbnRhZ2Ugb2YgZHJpdmVzIHRoYXQgbGFuZCBvbiB0aGUgZmFpcndheSwgDQpHcmVlbnMgaW4gUmVndWxhdGlvbiA6IFRoZSBwZXJjZW50YWdlIG9mIGdyZWVucyByZWFjaGVkIGluIHJlZ3VsYXRpb24sIG1lYW5pbmcgdGhlIG51bWJlciBvZiBzdHJva2VzIHVzZWQgYnkgdGhlIHBsYXllciB0byBsYW5kIHRoZSBiYWxsIG9uIHRoZSBncmVlbiBpcyB0d28gb3IgbW9yZSBsZXNzIHRoYW4gcGFyLg0KQXZlcmFnZSBOdW1iZXIgb2YgUHV0dHMgOiBUaGUgYXZlcmFnZSBudW1iZXIgb2YgcHV0dHMgcGVyIHJvdW5kDQpTYXZlIFBlcmNlbnRhZ2UgOiBUaGUgcGVyY2VudGFnZSBvZiB0aW1lcyBhIHBsYXllciBzYXZlcyBwYXIgb3IgYmV0dGVyIGZyb20gYXJvdW5kIHRoZSBncmVlbiANCk1vbmV5IFJhbmsgOiBUaGUgcmFuayBvZiB0aGUgcGxheWVyIGJhc2VkIG9uIHRvdGFsIGVhcm5pbmdzIGZvciB0aGUgc2Vhc29uLg0KTnVtYmVyIG9mIEV2ZW50cyA6IFRoZSB0b3RhbCBudW1iZXIgb2YgZXZlbnRzIHRoZSBwbGF5ZXIgcGFydGljaXBhdGVkIGluIGR1cmluZyB0aGUgc2Vhc29uLg0KVG90YWwgV2lubmluZ3MgOiBUaGUgdG90YWwgYW1vdW50IG9mIG1vbmV5IGVhcm5lZCBieSB0aGUgcGxheWVyIG92ZXIgdGhlIGNvdXJzZSBvZiB0aGUgc2Vhc29uLg0KQXZlcmFnZSBXaW5uaW5ncyA6IFRoZSBhdmVyYWdlIGFtb3VudCBvZiBtb25leSBlYXJuZWQgcGVyIGV2ZW50IGJ5IHRoZSBwbGF5ZXIuDQoNCiMjIFJlc2VhcmNoIFF1ZXN0aW9uDQpXZSB3YW50IHRvIGludmVzdGlnYXRlIHdoYXQgdmFyaWFibGVzIGFmZmVjdCB0aGUgcGxheWVycyB3aW5uaW5ncyBvZiB0aGlzIGdpdmVuIHNlYXNvbi4gV2Ugd2lsbCBsb29rIGF0IG1haW5seSB0aGUgYXZlcmFnZSBkcml2ZSBhbmQgaG93IGl0IGNvcnJlbGF0ZXMgdG8gd2lubmluZ3MuDQoNCiMjIERhdGEgUHJlcGVyYXRpb24NCg0KYGBge3J9DQoNCnVybCA8LSAiaHR0cHM6Ly91c2Vycy5zdGF0LnVmbC5lZHUvfndpbm5lci9kYXRhL3BnYTIwMDQuZGF0Ig0KcGdhX2RhdGEgPC0gcmVhZC50YWJsZSh1cmwsIGhlYWRlciA9IEZBTFNFLCBmaWxsID0gVFJVRSkNCnBnYV9kYXRhJFYxIDwtIE5VTEwNCmNvbG5hbWVzKHBnYV9kYXRhKSA8LSBjKCJQbGF5ZXJfbmFtZSIsICJQbGF5ZXJfQWdlIiwgIkF2ZXJhZ2VfRHJpdmUiLCAiRHJpdmluZ19BY2N1cmFjeSIsICJHcmVlbnNfb25fcmVnIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAiQXZlcmFnZV9udW1iZXJfcHV0dHMiLCAiU2F2ZV9QZXJjZW50IiwgIk1vbmV5X1JhbmsiLA0KICAgICAgICAgICAgICAgICAgICAgICAgIk51bWJlcl9ldmVudHMiLCAiVG90YWxfV2lubmluZ3MiLCAiQXZlcmFnZV9XaW5uaW5ncyIpDQpoZWFkKHBnYV9kYXRhKQ0KYGBgDQoNCldlIHRha2UgYSBicm9hZCBsb29rIGF0IHRoZSBkYXRhIGFuZCBzZWUgdGhlcmUgd2FzIHR3byBjYXRlZ29yaWNhbCB2YXJpYWJsZXMgb25lIG9mIHdoaWNoIGhhdmluZyB0aGUgZmlyc3QgbmFtZSBvZiBhIHBsYXllciBhbmQgdGhlbiBhIHNlY29uZCBvbmUgd2l0aCB0aGUgbGFzdCBuYW1lLiBXZSB2b2lkIHRoZSBmaXJzdCBuYW1lIGFuZCBqdXN0IGxvb2sgYXQgdGhlIGxhc3QgbmFtZXMuDQoNCmBgYHtyfQ0KcGFuZGVyKGhlYWQocGdhX2RhdGEpKQ0KDQpgYGANCmBgYHtyfQ0KY29yKHBnYV9kYXRhWywgYygiQXZlcmFnZV9Ecml2ZSIgLCAiRHJpdmluZ19BY2N1cmFjeSIgLCAiR3JlZW5zX29uX3JlZyIgLCAiQXZlcmFnZV9udW1iZXJfcHV0dHMiICwgIlNhdmVfUGVyY2VudCIgLCAiTW9uZXlfUmFuayIgLCAiTnVtYmVyX2V2ZW50cyIgLCAiVG90YWxfV2lubmluZ3MiICwgIkF2ZXJhZ2VfV2lubmluZ3MiKV0sIHVzZSA9ICJwYWlyd2lzZS5jb21wbGV0ZS5vYnMiKQ0KDQoNCmBgYA0KV2Ugc2VlIHRoYXQgYSBsb3Qgb2Ygb3VyIHZhcmlhYmxlcyBoYXZlIGhpZ2ggY29ycmVsYXRpb24gd2hpY2ggY2FuIGNhdXNlIG11bHRpY29sbGluZWFyaXR5LiBXZSBtdXN0IGRlYWwgd2l0aCB0aGlzIGlzc3VlIGJlZm9yZSBkb2luZyBvdXIgYW5hbHlzaXMuIA0KDQpgYGB7cn0NCmNvbFN1bXMoaXMubmEocGdhX2RhdGEpKQ0KcGdhX2RhdGFfY2xlYW4gPC0gbmEub21pdChwZ2FfZGF0YSkNCmBgYA0KDQpGb3IgdGhlIHNha2Ugb2YgbWlzc2luZyBvYnNlcnZhdGlvbnMgd2Ugd2lsbCBiZSByZW1vdmluZyBpdCBmb3Igb3VyIGRhdGEgYW5hbHlzaXMgYmVjYXVzZSB3ZSBoYXZlIGEgcGxlbnRpZnVsIGFtb3VudCBvZiBvYnNlcnZhdGlvbnMuDQoNCmBgYHtyfQ0KZ2dwbG90KHBnYV9kYXRhX2NsZWFuLCBhZXMoeCA9IEF2ZXJhZ2VfRHJpdmUsIHkgPSBBdmVyYWdlX1dpbm5pbmdzKSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBjb2wgPSAiZ3JlZW4iKSArDQogIGxhYnModGl0bGUgPSAiU2NhdHRlciBQbG90IG9mIEF2ZXJhZ2UgRHJpdmUgdnMuIEF2ZXJhZ2UgV2lubmluZ3MiLCB4ID0gIkF2ZXJhZ2UgRHJpdmUiLCB5ID0gIkF2ZXJhZ2UgV2lubmluZ3MiKQ0Kc3RyKHBnYV9kYXRhX2NsZWFuKQ0KcmVtb3ZlX2xvd19kcml2ZXMgPC0gZnVuY3Rpb24ocGdhX2RhdGFfY2xlYW4pIHsNCiAgZmlsdGVyZWRfZGF0YSA8LSBwZ2FfZGF0YV9jbGVhbiAlPiUNCiAgICBmaWx0ZXIoQXZlcmFnZV9Ecml2ZSA+PSAxNTApDQogIA0KICByZXR1cm4oZmlsdGVyZWRfZGF0YSkNCn0NCg0KIyBFeGFtcGxlIHVzYWdlDQpwZ2FfZGF0YV9maWx0ZXJlZCA8LSByZW1vdmVfbG93X2RyaXZlcyhwZ2FfZGF0YV9jbGVhbikNCmdncGxvdChwZ2FfZGF0YV9maWx0ZXJlZCwgYWVzKHggPSBBdmVyYWdlX0RyaXZlLCB5ID0gQXZlcmFnZV9XaW5uaW5ncykpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgY29sID0gImdyZWVuIikgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIlNjYXR0ZXIgUGxvdCBvZiBBdmVyYWdlIERyaXZlIHZzLiBBdmVyYWdlIFdpbm5pbmdzIChGaWx0ZXJlZCkiLA0KICAgIHggPSAiQXZlcmFnZSBEcml2ZSIsDQogICAgeSA9ICJBdmVyYWdlIFdpbm5pbmdzIg0KICApICsNCiAgdGhlbWVfbWluaW1hbCgpIA0KYGBgDQoNCg0KV2UgbG9vayBhdCBhbiBpbml0aWFsIGdyYXBoIG9mIGF2ZXJhZ2Ugd2lubmlncyB2cyBhdmVyYWdlIGRyaXZlLiBJdCBsb29rcyBsaWtlIHdlIGhhdmUgYSBnb29kIGNsdXN0ZXIgb2YgZGF0YSBidXQgd2UgaGF2ZSBhIGNvdXBsZSBvZiBvdXRsaWVycyBpbiBvdXIgZGF0YSB0aGF0IGlzIHNjZXdpbmcgb3VyIGxpbmUuDQoNCmBgYHtyfQ0KcGdhX2RhdGFfZmlsdGVyZWQkQWdlX0Fib3ZlXzMwIDwtIHBnYV9kYXRhX2ZpbHRlcmVkJFBsYXllcl9BZ2UgPiAzMA0KDQpoZWFkKHBnYV9kYXRhX2ZpbHRlcmVkJEFnZV9BYm92ZV8zMCkNCnBnYV9kYXRhX2ZpbHRlcmVkIDwtIHBnYV9kYXRhX2ZpbHRlcmVkICU+JSBzZWxlY3QoLVBsYXllcl9uYW1lLCAtRHJpdmluZ19BY2N1cmFjeSwgLVRvdGFsX1dpbm5pbmdzLCAtQXZlcmFnZV9udW1iZXJfcHV0dHMsIC1Nb25leV9SYW5rLCAtUGxheWVyX0FnZSkNCmthYmxlKGhlYWQocGdhX2RhdGFfZmlsdGVyZWQpKQ0KDQoNCmBgYA0KDQpXZSByZW1vdmUgcGxheWVyIG5hbWUgYW5kIG1vbmV5IHJhbmsgYmVjYXVzZSB3ZSBhcmUgInJhbmtpbmciIHRoZW0gb3Igc29ydGluZyB0aGVtIGJ5ICsvLSAzMCBhZ2UuIFdlIHJlbW92ZSBkcml2aW5nIGFjY3VyYWN5IGJlY2F1c2Ugd2UgaGF2ZSBhIHZhcmlhYmxlIHdpdGggYXZlcmFnZSBkcml2ZSBiZWNhdXNlIHRoYXQgaXMgd2hhdCB3ZSBhcmUgbWFraW5nIGFzc3VtcHRpb25zIG9uIGFuZCBhbHNvIGl0IGNvdWxkIGNhdXNlIG11bHRpY29sbGluZWFyaXR5IGJlY2F1c2UgZHJpdmluZyBhY2N1cmFjeSBhbmQgYXZlcmFnZSBkcml2ZSBhcmUgaGlnaGx5IGNvcnJlbGF0ZWQuIFRoZSBzYW1lIGNhbiBiZSBzYWlkIGZvciB0b3RhbCB3aW5uaW5ncyB2ZXJzdXMgYXZlcmFnZSB3aW5uaW5ncyBhbmQgYWxzbyBzYXZlIHBlcmNlbnQgYW5kIG51bWJlciBvZiBwdXR0cy4gVGhleSBhcmUgYm90aCBjb3JyZWxhdGVkIGFuZCBjYW4gY2F1c2UgbXVsdGljb2xsaW5lYXJpdHkuDQoNCiMjIyBNb2RlbCBCdWxkaW5nDQoNCiMjIExpbmVhciBNb2RlbA0KDQpgYGB7cn0NCmNvbG5hbWVzKHBnYV9kYXRhX2ZpbHRlcmVkKQ0KZnVsbC5tb2RlbCA9IGxtKEF2ZXJhZ2VfV2lubmluZ3MgfiAgQXZlcmFnZV9Ecml2ZSArIEdyZWVuc19vbl9yZWcgKyAgU2F2ZV9QZXJjZW50ICsgTnVtYmVyX2V2ZW50cyArIEFnZV9BYm92ZV8zMCwgZGF0YSA9IHBnYV9kYXRhX2ZpbHRlcmVkKQ0Ka2FibGUoc3VtbWFyeShmdWxsLm1vZGVsKSRjb2VmLCBjYXB0aW9uID0iU3RhdGlzdGljcyBvZiBSZWdyZXNzaW9uIENvZWZmaWNpZW50cyIpDQoNCg0KYGBgDQpXZSB0cmFuc2Zvcm0gdGhlIGFnZSBjYXRlZ29yeSBpbnRvIGEgdHJ1ZSBvciBmYWxzZSBvZiBwYXllcnMgd2l0aCBhYm92ZSBvciBiZWxvdyBhZ2UgMzAuIEFsbCBvZiBvdXIgdmFsdWVzIGFyZSBzaWduaWZpY2FudCBiZXNpZGVzIEFnZS4NCmBgYHtyfQ0KcGFyKG1mcm93PWMoMiwyKSkNCnBsb3QoZnVsbC5tb2RlbCkNCg0KDQpgYGANCg0KSXQgbG9va3MgbGlrZSB3ZSBoYXZlIGZhdWx0cyBpbiBvdXIgYXNzdW1wdGlvbnMuIEJhc2VkIG9uIHRoZSBRUSBwbG90IHRoZXJlIHNlZW1zIHRvIGJlIGEgbm9uLW5vcm1hbCBkaXN0cmlidXRpb24gd2l0aCBzZXZlcmFsIG91dGxpZXJzLiBUaGVyZSBhbHNvIHNlZW1zIHRvIGJlIG5vbi1jb25zdGFudCB2YXJpYW5jZS4NCg0KYGBge3J9DQp2aWYoZnVsbC5tb2RlbCkNCmJhcnBsb3QodmlmKGZ1bGwubW9kZWwpLCBtYWluID0gIlZJRiBWYWx1ZXMiLCBob3JpeiA9IEZBTFNFLCBjb2wgPSAic3RlZWxibHVlIikNCmBgYA0KDQpXZSBzZWUgaW4gdGhlIHZpZiB2YWx1ZXMgYWJvdmUgdGhhdCBhbGwgb2YgdGhlbSBhcmUgYmVsb3cgNSB3aGljaCBzaG93cyBsaXR0bGUgdG8gbm8gbXVsdGljb2xsaW5lYWlydHkgc28gd2UgY2FuIGNvbnRpbnVlLg0KDQojIyBCb3ggQ294IFRyYW5zZm9ybWF0aW9uDQoNCldlIHBlcmZvcm0gdGhpcyB0cmFuc2Zvcm1hdGlvbiBiZWNhdXNlIG91ciBkYXRhIGhhcyBub24gY29uc3RhbnQgdmFyaWFuY2UNCg0KYGBge3J9DQpwYXIocHR5ID0gInMiLCBtZnJvdyA9IGMoMiwgMiksIG9tYT1jKC4xLC4xLC4xLC4xKSwgbWFyPWMoNCwgMCwgMiwgMCkpDQojIw0KYm94Y294KEF2ZXJhZ2VfV2lubmluZ3MgfiBsb2coQXZlcmFnZV9Ecml2ZSkgKyBHcmVlbnNfb25fcmVnICsgIFNhdmVfUGVyY2VudCArIE51bWJlcl9ldmVudHMgKyBBZ2VfQWJvdmVfMzAsIGRhdGEgPSBwZ2FfZGF0YV9maWx0ZXJlZCwgbGFtYmRhID0gc2VxKC0xLCAxLCBsZW5ndGggPSAxMCksIA0KICAgICAgIHhsYWI9ZXhwcmVzc2lvbihwYXN0ZShsYW1iZGEsICI6IGxvZyBBdmVyYWdlIERyaXZlIikpKQ0KIyMNCmJveGNveChBdmVyYWdlX1dpbm5pbmdzIH4gQXZlcmFnZV9Ecml2ZSArIEdyZWVuc19vbl9yZWcgKyAgU2F2ZV9QZXJjZW50ICsgTnVtYmVyX2V2ZW50cyArIEFnZV9BYm92ZV8zMCwgZGF0YSA9IHBnYV9kYXRhX2ZpbHRlcmVkLCBsYW1iZGEgPSBzZXEoLTEsIDEsIGxlbmd0aCA9IDEwKSwgDQogICAgICAgeGxhYj1leHByZXNzaW9uKHBhc3RlKGxhbWJkYSwgIjogQXZlcmFnZSBEcml2ZSIpKSkNCiMjDQpib3hjb3goQXZlcmFnZV9XaW5uaW5ncyB+IEF2ZXJhZ2VfRHJpdmUgKyBHcmVlbnNfb25fcmVnICsgIFNhdmVfUGVyY2VudCArIE51bWJlcl9ldmVudHMgKyBsb2coMStBZ2VfQWJvdmVfMzApLCBkYXRhID0gcGdhX2RhdGFfZmlsdGVyZWQsIGxhbWJkYSA9IHNlcSgtMSwgMSwgbGVuZ3RoID0gMTApLCANCiAgICAgICB4bGFiPWV4cHJlc3Npb24ocGFzdGUobGFtYmRhLCAiOiBsb2ctYWdlIikpKQ0KIyMNCmJveGNveChBdmVyYWdlX1dpbm5pbmdzIH4gbG9nKEF2ZXJhZ2VfRHJpdmUpICsgR3JlZW5zX29uX3JlZyArICBTYXZlX1BlcmNlbnQgKyBOdW1iZXJfZXZlbnRzICsgbG9nKDErQWdlX0Fib3ZlXzMwKSwgZGF0YSA9IHBnYV9kYXRhX2ZpbHRlcmVkLCBsYW1iZGEgPSBzZXEoLTEsIDEsIGxlbmd0aCA9IDEwKSwgDQogICAgICB4bGFiPWV4cHJlc3Npb24ocGFzdGUobGFtYmRhLCAiOiBsb2ctYWdlLCBsb2cgQXZlcmFnZSBEcml2ZSIpKSkNCg0KYGBgDQoNClZhbHVlcyBvZiBsYW1kYSBhcmUgYXJvdW5kIDAgc28gd2Ugc2hvdWRsIHVzZSBhIGxvZyB0cmFuc2Zvcm1hdGlvbg0KIyMgU3F1YXJlIFJvb3QgTW9kZWwNCg0KYGBge3J9DQpzcXJ0Lndpbm5pbmdzLmxvZy5kcml2ZSA9IGxtKChBdmVyYWdlX1dpbm5pbmdzKV4wLjUgfiBHcmVlbnNfb25fcmVnICsgIFNhdmVfUGVyY2VudCArIE51bWJlcl9ldmVudHMgKyBBZ2VfQWJvdmVfMzAgKyBsb2coQXZlcmFnZV9Ecml2ZSksIGRhdGEgPSBwZ2FfZGF0YV9maWx0ZXJlZCkNCmthYmxlKHN1bW1hcnkoc3FydC53aW5uaW5ncy5sb2cuZHJpdmUpJGNvZWYsIGNhcHRpb24gPSAibG9nLXRyYW5zZm9ybWVkIG1vZGVsIikNCg0KDQpgYGANCg0KYGBge3J9DQpwYXIobWZyb3cgPSBjKDIsMikpDQpwbG90KHNxcnQud2lubmluZ3MubG9nLmRyaXZlKQ0KDQpgYGANCg0KVGhpcyBtb2RlbCB3b3JzZW5lZCBvdXIgUVEtcGxvdCBub3JtYWxpdHkgYW5kIG91ciBub24gY29uc3RhbnQgdmFyaWFuY2UgcmVtYWluZWQgdGhlIHNhbWUsIHNvIHdlIGNhbiB0cnkgYW5vdGhlciBtb2RlbCB0byBzZWUgaWYgaXQgaGVscHMuDQoNCiMjIExvZyBUcmFuc2Zvcm1hdGlvbg0KDQpgYGB7cn0NCmxvZy53aW5uaW5ncyA9IGxtKGxvZyhBdmVyYWdlX1dpbm5pbmdzKSB+IEF2ZXJhZ2VfRHJpdmUgKyBHcmVlbnNfb25fcmVnICsgIFNhdmVfUGVyY2VudCArIE51bWJlcl9ldmVudHMgKyBBZ2VfQWJvdmVfMzAsIGRhdGEgPSBwZ2FfZGF0YV9maWx0ZXJlZCkNCmthYmxlKHN1bW1hcnkobG9nLndpbm5pbmdzKSRjb2VmLCBjYXB0aW9uID0gImxvZy10cmFuc2Zvcm1lZCBtb2RlbCIpDQoNCg0KYGBgDQoNCmBgYHtyfQ0KcGFyKG1mcm93ID0gYygyLDIpKQ0KcGxvdChsb2cud2lubmluZ3MpDQoNCmBgYA0KDQpUaGlzIG1vZGVsIGxvb2tzIGRyYXN0aWNhbGx5IGJldHRlciB0aGVuIHRoZSBvdGhlciB0d28gbW9kZWxzLiBXaGlsZSBub3JtYWxpdHkgc3RpbGwgY2Fubm90IGJlIGFzc3VtZWQgdGhlIFFRIFBsb3QgbG9va3MgYmV0dGVyLiBUaGUgc2FtZSB0aGluZyBoYXMgaGFwcGVuZWQgd2l0aCBvdXIgUmVzaWR1YWwgcGxvdCB3aGlsZSBjb25zdGFudCB2YXJpYW5jZSBjYW4gc3RpbGwgbm90IGJlIGFzc3VtZWQgaXQgaXMgZHJhc3RpY2FsbHkgYmV0dGVyLg0KDQojIyBDb21wYXJpc29uIG9mIG1vZGVscw0KDQpgYGB7cn0NCg0KcGFyKHB0eSA9ICJzIiwgbWZyb3cgPSBjKDEsIDMpKQ0KDQpxcW5vcm0oZnVsbC5tb2RlbCRyZXNpZHVhbHMsIG1haW4gPSAiRnVsbC1Nb2RlbCIpDQpxcWxpbmUoZnVsbC5tb2RlbCRyZXNpZHVhbHMpDQoNCnFxbm9ybShsb2cud2lubmluZ3MkcmVzaWR1YWxzLCBtYWluID0gIkxvZyBXaW5uaW5ncyIpDQpxcWxpbmUobG9nLndpbm5pbmdzJHJlc2lkdWFscykNCg0KcXFub3JtKHNxcnQud2lubmluZ3MubG9nLmRyaXZlJHJlc2lkdWFscywgbWFpbiA9ICJzcXJ0IHdpbm5pbmdzIGxvZyBkcml2ZSIpDQpxcWxpbmUoc3FydC53aW5uaW5ncy5sb2cuZHJpdmUkcmVzaWR1YWxzKQ0KDQoNCmBgYA0KDQpXZSBjYW4gc2VlIHRoYXQgTG9nIFdpbm5pbmdzIGlzIHRoZSBiZXN0IG1vZGVsIGZvciBvdXIgZGF0YS4NCg0KIyMgR29vZG5lc3Mgb2YgRml0IENvbXBhcmlzb24NCg0KYGBge3J9DQpzZWxlY3Q9ZnVuY3Rpb24obSl7IA0KIGUgPSBtJHJlc2lkICAgICAgICAgICAgICAgICAgICAgICAgICAgDQogbjAgPSBsZW5ndGgoZSkgICAgICAgICAgICAgICAgICAgICAgICANCiBTU0U9KG0kZGYpKihzdW1tYXJ5KG0pJHNpZ21hKV4yICAgICAgIA0KIFIuc3E9c3VtbWFyeShtKSRyLnNxdWFyZWQgICAgICAgICAgICAgDQogUi5hZGo9c3VtbWFyeShtKSRhZGouciAgICAgICAgICAgICAgIA0KIE1TRT0oc3VtbWFyeShtKSRzaWdtYSleMiAgICAgICAgICAgICAgDQogQ3A9KFNTRS9NU0UpLShuMC0yKihuMC1tJGRmKSkgICAgICAgICANCiBBSUM9bjAqbG9nKFNTRSktbjAqbG9nKG4wKSsyKihuMC1tJGRmKSAgICAgICAgICANCiBTQkM9bjAqbG9nKFNTRSktbjAqbG9nKG4wKSsobG9nKG4wKSkqKG4wLW0kZGYpICANCiBYPW1vZGVsLm1hdHJpeChtKSAgICAgICAgICAgICAgICAgICAgIA0KIEg9WCUqJXNvbHZlKHQoWCklKiVYKSUqJXQoWCkgICAgICAgICAgDQogZD1lLygxLWRpYWcoSCkpICAgICAgICAgICAgICAgICAgICAgICANCiBQUkVTUz10KGQpJSolZCAgIA0KIHRibCA9IGFzLmRhdGEuZnJhbWUoY2JpbmQoU1NFPVNTRSwgUi5zcT1SLnNxLCBSLmFkaiA9IFIuYWRqLCBDcCA9IENwLCBBSUMgPSBBSUMsIFNCQyA9IFNCQywgUFJEID0gUFJFU1MpKQ0KIG5hbWVzKHRibCk9YygiU1NFIiwgIlIuc3EiLCAiUi5hZGoiLCAiQ3AiLCAiQUlDIiwgIlNCQyIsICJQUkVTUyIpDQogdGJsDQogfQ0KDQpgYGANCg0KYGBge3J9DQoNCm91dHB1dC5zdW0gPSByYmluZChzZWxlY3QoZnVsbC5tb2RlbCksIHNlbGVjdChzcXJ0Lndpbm5pbmdzLmxvZy5kcml2ZSksIHNlbGVjdChsb2cud2lubmluZ3MpKQ0Kcm93Lm5hbWVzKG91dHB1dC5zdW0pID0gYygiZnVsbC5tb2RlbCIsICJzcXJ0Lndpbm5pbmdzLmxvZy5kcml2ZSIsICJsb2cud2lubmluZ3MiKQ0Ka2FibGUob3V0cHV0LnN1bSwgY2FwdGlvbiA9ICJHb29kbmVzcy1vZi1maXQgTWVhc3VyZXMgb2YgQ2FuZGlkYXRlIE1vZGVscyIpDQoNCmBgYA0KDQpFdmVuIHRob3VnaCB0aGUgUiwgUiBzcXVhcmVkLCBhbmQgQ3AgbG9vayBiZXR0ZXIgaW4gdGhlIG90aGVyIHR3byBtb2RlbHMgb3VyIHZhbHVlIGFyZSBzdGlsbCByZWxhdGl2ZWx5IGdvb2QgZm9yIGxvZy53aW5uaW5ncy4gTG9nLndpbm5pbmdzIGhhcyBiZXR0ZXIgcmVzaWR1YWxzLCBRUSBwbG90LCBidXQgbGVzcyBnb29kbmVzcyBvZiBmaXQgbWVhc3VyZXMgYnV0IHRoZXkgYXJlIG5vdCBzaWduaWZpY2FudGx5IGJhZCBzbyB3ZSB3aWxsIHN0aWNrIHdpdGggdGhlIGxvZyBtb2RlbC4NCg0KIyMgRmluYWwgTW9kZWwNCg0KYGBge3J9DQprYWJsZShzdW1tYXJ5KGxvZy53aW5uaW5ncykkY29lZiwgY2FwdGlvbiA9ICJJbmZlcmVudGlhbCBTdGF0aXN0aWNzIG9mIEZpbmFsIE1vZGVsIikNCmBgYA0KDQpXaGlsZSB3ZSBkbyBoYXZlIGEgbGFyZ2Ugc2FtcGxlIG9mIDE5NiBhbmQgYWxsIG9mIG91ciBwdmFsdWVzIGFyZSBjbG9zZSB0byAwIHdlIGRvIGhhdmUgb25lIHZhcmlhYmxlICJBZ2UiIHdoaWNoIGlzIG5vdCBzaWduaWZpY2FudC4NCg0KIyMjIFN1bW1hcnkgb2YgTW9kZWwNClRoZSBmaW5hbCBtb2RlbCBjYW4gYmUgcmVwcmVzZW50ZWQgYXMgbG9nKHByaWNlKT0zLjkyNDUg4oiSIDAuMDI0NsOXQXZlcmFnZV9Ecml2ZSArIDAuMTg2NsOXR3JlZW5zX29uX3JlZyArIA0KMC4wMzg3w5dTYXZlX3BlcmNlbnQg4oiSIDAuMDE4McOXTnVtYmVyX2V2ZW50cyArIDAuMTgyNsOXQWdlX0Fib3ZlXzMwVFJVRQ0KDQpGcm9tIHRoZSBhYm92ZSBkYXRhIHdlIGNhbiBhbHNvIGNvbmNsdWRlIHRoYXQgYXMgYXZlcmFnZSB3aW5uaW5ncyBnbyB1cCBvdXIgYXZlcmFnZSBkcml2ZSBnb2VzIGRvd24gYnkgLTIuNSUgd2hpbGUgb3RoZXIgdmFyaWFibGVzIHN1Y2ggYXMgZ3JlZW5zIG9uIHJlZ3VsYXRpb24gYW5kIHNhdmUgcGVyY2VudCBnbyB1cC4NCg0KYGBge3J9DQpsb2cud2lubmluZ3MgPSBsbShsb2coQXZlcmFnZV9XaW5uaW5ncykgfiBBdmVyYWdlX0RyaXZlICsgR3JlZW5zX29uX3JlZyArIFNhdmVfUGVyY2VudCArIE51bWJlcl9ldmVudHMgKyBBZ2VfQWJvdmVfMzAsIGRhdGEgPSBwZ2FfZGF0YV9maWx0ZXJlZCkNCg0KQiA9IDEwMDAgICMNCg0KbnVtLnAgPSBsZW5ndGgoY29lZihsb2cud2lubmluZ3MpKSAgIyBudW1iZXIgb2YgcGFyYW1ldGVycyBpbiB0aGUgbW9kZWwgKGluY2x1ZGVzIGludGVyY2VwdCkNCnNtcGwubiA9IG5yb3cocGdhX2RhdGFfZmlsdGVyZWQpICAgICAgICMgc2FtcGxlIHNpemUNCg0KIyBNYXRyaXggdG8gc3RvcmUgYm9vdHN0cmFwIGNvZWZmaWNpZW50cw0KY29lZi5tdHJ4ID0gbWF0cml4KDAsIG5yb3cgPSBCLCBuY29sID0gbnVtLnApDQoNCiMgQm9vdHN0cmFwIGxvb3ANCmZvciAoaSBpbiAxOkIpIHsNCiAgYm9vdGMuaWQgPSBzYW1wbGUoMTpzbXBsLm4sIHNtcGwubiwgcmVwbGFjZSA9IFRSVUUpICAjIFJlc2FtcGxlIHdpdGggcmVwbGFjZW1lbnQNCiAgYm9vdF9kYXRhID0gcGdhX2RhdGFfZmlsdGVyZWRbYm9vdGMuaWQsIF0gICMgQ3JlYXRlIHRoZSBib290c3RyYXAgc2FtcGxlDQogIGxvZy53aW5uaW5ncy5idGMgPSBsbShsb2coQXZlcmFnZV9XaW5uaW5ncykgfiBBdmVyYWdlX0RyaXZlICsgR3JlZW5zX29uX3JlZyArIFNhdmVfUGVyY2VudCArIE51bWJlcl9ldmVudHMgKyBBZ2VfQWJvdmVfMzAsIGRhdGEgPSBib290X2RhdGEpDQogIGNvZWYubXRyeFtpLCBdID0gY29lZihsb2cud2lubmluZ3MuYnRjKSAgIyBTdG9yZSB0aGUgY29lZmZpY2llbnRzDQp9DQoNCmBgYA0KDQojIyMgQm9vdHN0cmFwcGluZyBGaW5hbCBNb2RlbA0KDQpgYGB7cn0NCg0KYm9vdC5oaXN0ID0gZnVuY3Rpb24oY210cngsIGJ0LmNvZWYubXRyeCwgdmFyLmlkLCB2YXIubm0pew0KDQogIHgxLjEgPC0gc2VxKG1pbihidC5jb2VmLm10cnhbLHZhci5pZF0pLCBtYXgoYnQuY29lZi5tdHJ4Wyx2YXIuaWRdKSwgbGVuZ3RoPTMwMCApDQogIHkxLjEgPC0gZG5vcm0oeDEuMSwgbWVhbihidC5jb2VmLm10cnhbLHZhci5pZF0pLCBzZChidC5jb2VmLm10cnhbLHZhci5pZF0pKQ0KDQogIGhpZ2hlc3RiYXIgPSBtYXgoaGlzdChidC5jb2VmLm10cnhbLHZhci5pZF0sIHBsb3QgPSBGQUxTRSkkZGVuc2l0eSkgDQogIHlsaW1pdCA8LSBtYXgoYyh5MS4xLGhpZ2hlc3RiYXIpKQ0KICBoaXN0KGJ0LmNvZWYubXRyeFssdmFyLmlkXSwgcHJvYmFiaWxpdHkgPSBUUlVFLCBtYWluID0gdmFyLm5tLCB4bGFiPSIiLCANCiAgICAgICBjb2wgPSAiYXp1cmUxIix5bGltPWMoMCx5bGltaXQpLCBib3JkZXI9ImxpZ2h0c2VhZ3JlZW4iKQ0KICBsaW5lcyh4ID0geDEuMSwgeSA9IHkxLjEsIGNvbCA9ICJyZWQzIikNCiAgbGluZXMoZGVuc2l0eShidC5jb2VmLm10cnhbLHZhci5pZF0sIGFkanVzdD0yKSwgY29sPSJibHVlIikgDQogIA0KfQ0KDQpgYGANCg0KYGBge3J9DQpwYXIobWZyb3c9YygyLDMpKSAgDQpib290Lmhpc3QoYnQuY29lZi5tdHJ4PWNvZWYubXRyeCwgdmFyLmlkPTEsIHZhci5ubSA9IkludGVyY2VwdCIgKQ0KYm9vdC5oaXN0KGJ0LmNvZWYubXRyeD1jb2VmLm10cngsIHZhci5pZD0yLCB2YXIubm0gPSJBdmVyYWdlIERyaXZlIiApDQpib290Lmhpc3QoYnQuY29lZi5tdHJ4PWNvZWYubXRyeCwgdmFyLmlkPTMsIHZhci5ubSA9IkdyZWVucyBPbiBSZWd1bGF0aW9uIiApDQpib290Lmhpc3QoYnQuY29lZi5tdHJ4PWNvZWYubXRyeCwgdmFyLmlkPTQsIHZhci5ubSA9IlNhdmUgUGVyY2VudGFnZSIgKQ0KYm9vdC5oaXN0KGJ0LmNvZWYubXRyeD1jb2VmLm10cngsIHZhci5pZD01LCB2YXIubm0gPSJOdW1iZXIgb2YgRXZlbnRzIiApDQpib290Lmhpc3QoYnQuY29lZi5tdHJ4PWNvZWYubXRyeCwgdmFyLmlkPTYsIHZhci5ubSA9IkFnZSBBYm92ZSAzMCIgKQ0KYGBgDQoNCldlIHNlZSBmcm9tIG91ciBoaXN0b2dyYW1zIHRoYXQgdGhleSBsb29rIHRvIGhhdmUgYSBlcXVhbCBkaXN0cmlidXRpb24uIFRoZSBibHVlIGN1cnZlIHJlcHJlc2VudHMgdGhlIHAtdmFsdWVzIHJlcG9ydGVkLiBXaGlsZSB0aGUgYmx1ZSBjdXJ2ZSByZXByZXNlbnRzIHRoZSBib290c3RyYXAgaW50ZXJ2YWxzLiBUaGV5IGFyZSB2ZXJ5IHNpbWlsYXIuDQoNCiMjIDk1JSBDSQ0KDQpgYGB7cn0NCg0KbnVtLnAgPSBkaW0oY29lZi5tdHJ4KVsyXSAgDQoNCmJ0Yy5jaSA9IE5VTEwNCmJ0Yy53ZCA9IE5VTEwNCg0KZm9yIChpIGluIDE6bnVtLnApIHsNCiAgbGNpLjAyNSA9IHJvdW5kKHF1YW50aWxlKGNvZWYubXRyeFssIGldLCAwLjAyNSwgdHlwZSA9IDIpLCA4KQ0KICB1Y2kuOTc1ID0gcm91bmQocXVhbnRpbGUoY29lZi5tdHJ4WywgaV0sIDAuOTc1LCB0eXBlID0gMiksIDgpDQogIGJ0Yy53ZFtpXSA9IHVjaS45NzUgLSBsY2kuMDI1DQogIGJ0Yy5jaVtpXSA9IHBhc3RlKCJbIiwgcm91bmQobGNpLjAyNSwgNCksICIsICIsIHJvdW5kKHVjaS45NzUsIDQpLCAiXSIpDQp9DQoNCm1lYW4uY29lZnMgPSBhcHBseShjb2VmLm10cngsIDIsIG1lYW4pDQoNCnJlc3VsdHMgPSBhcy5kYXRhLmZyYW1lKGNiaW5kKE1lYW5fQ29lZiA9IGZvcm1hdEMobWVhbi5jb2VmcywgNCwgZm9ybWF0ID0gImYiKSwgYnRjLmNpLjk1ID0gYnRjLmNpKSkNCg0Ka2FibGUocmVzdWx0cywgY2FwdGlvbiA9ICJSZWdyZXNzaW9uIENvZWZmaWNpZW50IE1hdHJpeCB3aXRoIEJvb3RzdHJhcCBDb25maWRlbmNlIEludGVydmFscyIpDQoNCmBgYA0KDQpXZSBjYW4gc2VlIHRoYXQgb3VyIHZhbHVlcyBhcmUgY29uc2lzdGVudCB3aXRoIHRoZSBwLXZhbHVlcyB0aGF0IHdlIGdvdC4gT3VyIDk1JSBDSSBzcGFucyBvdmVyIDAgaW4gdGhlIEFnZSB2YXJpYWJsZSB3aGljaCBtYXRjaGVzIHVwIHdpdGggb3VyIHAtdmFsdWUgbm90IGJlaW5nIHNpZ25pZmljYW50DQoNCmBgYHtyfQ0KaGlzdChzb3J0KGxvZy53aW5uaW5ncyRyZXNpZHVhbHMpLG49NDAsDQogICAgIHhsYWI9IlJlc2lkdWFscyIsDQogICAgIGNvbCA9ICJsaWdodGJsdWUiLA0KICAgICBib3JkZXI9Im5hdnkiLA0KICAgICBtYWluID0gIkhpc3RvZ3JhbSBvZiBSZXNpZHVhbHMiKQ0KDQoNCmBgYA0KDQpXZSBhcHBlYXIgdG8gaGF2ZSBhIG5vcm1hbCBkaXN0cmlidXRpb24gYnV0IHdlIGRvIGFwcGVhciB0byBoYXZlIHNvbWUgb3V0bGllcnMgb24gZWl0aGVyIHNpZGUgb2YgdGhlIGN1cnZlLg0KDQojIyBSZXNpZHVhbCBCb290c3RyYXAgUmVncmVzc2lvbg0KDQpgYGB7cn0NCmxvZy53aW5uaW5ncyA8LSBsbShsb2coQXZlcmFnZV9XaW5uaW5ncykgfiBBdmVyYWdlX0RyaXZlICsgR3JlZW5zX29uX3JlZyArIFNhdmVfUGVyY2VudCArIE51bWJlcl9ldmVudHMgKyBBZ2VfQWJvdmVfMzAsIGRhdGEgPSBwZ2FfZGF0YV9maWx0ZXJlZCkNCm1vZGVsLnJlc2lkID0gbG9nLndpbm5pbmdzJHJlc2lkdWFscw0KQj0xMDAwDQpudW0ucCA9IGRpbShtb2RlbC5tYXRyaXgobG9nLndpbm5pbmdzKSlbMl0gICANCnNhbXAubiA9IGRpbShtb2RlbC5tYXRyaXgobG9nLndpbm5pbmdzKSlbMV0gIA0KYnRyLm10cnggPSBtYXRyaXgocmVwKDAsNipCKSwgbmNvbD1udW0ucCkNCmZvciAoaSBpbiAxOkIpew0KDQogIGJ0LmxnLndpbm5pbmdzID0gbG9nLndpbm5pbmdzJGZpdHRlZC52YWx1ZXMgKyANCiAgICAgICAgc2FtcGxlKGxvZy53aW5uaW5ncyRyZXNpZHVhbHMsIHNhbXAubiwgcmVwbGFjZSA9IFRSVUUpICANCiAgDQogIHBnYV9kYXRhX2ZpbHRlcmVkJGJ0LmxnLndpbm5pbmdzID0gIGJ0LmxnLndpbm5pbmdzICAgIyAgc2VuZCB0aGUgYm9vdCByZXNwb25zZSB0byB0aGUgZGF0YQ0KICBidHIubW9kZWwgPSBsbShidC5sZy53aW5uaW5ncyB+IEF2ZXJhZ2VfRHJpdmUgKyBHcmVlbnNfb25fcmVnICsgU2F2ZV9QZXJjZW50ICsgTnVtYmVyX2V2ZW50cyArIEFnZV9BYm92ZV8zMCwgZGF0YSA9IHBnYV9kYXRhX2ZpbHRlcmVkKSAgDQogIGJ0ci5tdHJ4W2ksXT1idHIubW9kZWwkY29lZmZpY2llbnRzDQp9DQpgYGANCg0KYGBge3J9DQpib290Lmhpc3QgPSBmdW5jdGlvbihidC5jb2VmLm10cngsIHZhci5pZCwgdmFyLm5tKXsNCg0KICB4MS4xIDwtIHNlcShtaW4oYnQuY29lZi5tdHJ4Wyx2YXIuaWRdKSwgbWF4KGJ0LmNvZWYubXRyeFssdmFyLmlkXSksIGxlbmd0aD0zMDAgKQ0KICB5MS4xIDwtIGRub3JtKHgxLjEsIG1lYW4oYnQuY29lZi5tdHJ4Wyx2YXIuaWRdKSwgc2QoYnQuY29lZi5tdHJ4Wyx2YXIuaWRdKSkNCg0KICBoaWdoZXN0YmFyID0gbWF4KGhpc3QoYnQuY29lZi5tdHJ4Wyx2YXIuaWRdLCBwbG90ID0gRkFMU0UpJGRlbnNpdHkpIA0KICB5bGltaXQgPC0gbWF4KGMoeTEuMSxoaWdoZXN0YmFyKSkNCiAgaGlzdChidC5jb2VmLm10cnhbLHZhci5pZF0sIHByb2JhYmlsaXR5ID0gVFJVRSwgbWFpbiA9IHZhci5ubSwgeGxhYj0iIiwgDQogICAgICAgY29sID0gImF6dXJlMSIseWxpbT1jKDAseWxpbWl0KSwgYm9yZGVyPSJsaWdodHNlYWdyZWVuIikNCiAgbGluZXMoeCA9IHgxLjEsIHkgPSB5MS4xLCBjb2wgPSAicmVkMyIpICAgICAgICAgICAgIA0KICBsaW5lcyhkZW5zaXR5KGJ0LmNvZWYubXRyeFssdmFyLmlkXSwgYWRqdXN0PTIpLCBjb2w9ImJsdWUiKSAgICANCn0gDQoNCg0KDQpgYGANCg0KYGBge3J9DQpwYXIobWZyb3c9YygyLDMpKSAgDQpib290Lmhpc3QoYnQuY29lZi5tdHJ4PWJ0ci5tdHJ4LCB2YXIuaWQ9MSwgdmFyLm5tID0iSW50ZXJjZXB0IiApDQpib290Lmhpc3QoYnQuY29lZi5tdHJ4PWJ0ci5tdHJ4LCB2YXIuaWQ9MiwgdmFyLm5tID0iQXZlcmFnZSBEcml2ZSIgKQ0KYm9vdC5oaXN0KGJ0LmNvZWYubXRyeD1idHIubXRyeCwgdmFyLmlkPTMsIHZhci5ubSA9IkdyZWVucyBvbiBSZWd1bGF0aW9uIiApDQpib290Lmhpc3QoYnQuY29lZi5tdHJ4PWJ0ci5tdHJ4LCB2YXIuaWQ9NCwgdmFyLm5tID0iU2F2ZSBQZXJjZW50YWdlIiApDQpib290Lmhpc3QoYnQuY29lZi5tdHJ4PWJ0ci5tdHJ4LCB2YXIuaWQ9NSwgdmFyLm5tID0iTnVtYmVyIG9mIEV2ZW50cyIgKQ0KYm9vdC5oaXN0KGJ0LmNvZWYubXRyeD1idHIubXRyeCwgdmFyLmlkPTYsIHZhci5ubSA9IkFnZSIgKQ0KDQoNCmBgYA0KDQpXZSBhZ2FpbiBzZWUgdGhhdCBvdXIgaGlzdG9ncmFtcyBoYXZlIGFuIGVxdWFsIGRpc3RyaWJ1dGlvbi4gQWdhaW4gd2Ugc2VlIHRoYXQgdGhlIHAtdmFsdWVzIGFuZCByZXNpZHVhbCBib29zdHJhcCBoYXZlIHZlcnkgc2ltaWxhciB2YWx1ZXMuDQpgYGB7cn0NCnVtLnAgPSBkaW0oYnRyLm10cngpWzJdICANCmJ0ci5jaSA9IE5VTEwNCmJ0ci53ZCA9IE5VTEwNCg0KZm9yIChpIGluIDE6bnVtLnApIHsNCiAgbGNpLjAyNSA9IHJvdW5kKHF1YW50aWxlKGJ0ci5tdHJ4WywgaV0sIDAuMDI1LCB0eXBlID0gMiksIDgpDQogIHVjaS45NzUgPSByb3VuZChxdWFudGlsZShidHIubXRyeFssIGldLCAwLjk3NSwgdHlwZSA9IDIpLCA4KQ0KICBidHIud2RbaV0gPSB1Y2kuOTc1IC0gbGNpLjAyNQ0KICBidHIuY2lbaV0gPSBwYXN0ZSgiWyIsIHJvdW5kKGxjaS4wMjUsIDQpLCAiLCAiLCByb3VuZCh1Y2kuOTc1LCA0KSwgIl0iKQ0KfQ0KDQprYWJsZShhcy5kYXRhLmZyYW1lKGNiaW5kKGZvcm1hdEMoYnRyLm10cnhbMSwgXSwgNCwgZm9ybWF0ID0gImYiKSwgYnRyLmNpLjk1ID0gYnRyLmNpKSksIA0KICAgICAgY2FwdGlvbiA9ICJSZWdyZXNzaW9uIENvZWZmaWNpZW50IE1hdHJpeCB3aXRoIDk1JSBSZXNpZHVhbCBCb290c3RyYXAgQ0kiKQ0KDQpgYGANCg0KT25jZSBhZ2FpbiB3ZSBnZXQgdGhlIHNhbWUgb3V0cHV0IGFzIGV4cGVjdGVkLiBUaGUgdmFsdWVzIGFib3ZlIHNob3cgdGhlIHNhbWUgcmVzdWx0cyBhcyBvdXIgcC12YWx1ZXMsIGFsbCBiZWluZyBzaWduaWZpY2FudCBleGNlcHQgZm9yIGFnZSB3aGljaCBpbmNsdWRlcyAwLg0KDQojIyMgQ29tYmluaW5nIHJlc3VsdHMNCg0KYGBge3J9DQpwX3ZhbHVlcyA8LSBzdW1tYXJ5KGxvZy53aW5uaW5ncykkY29lZmZpY2llbnRzWy0zLCA0XSAgDQoNCmNvbWJpbmVkX21hdHJpeCA8LSBjYmluZCgNCiAgQ29lZmZpY2llbnRzID0gZm9ybWF0Qyhjb2VmKGxvZy53aW5uaW5ncylbLTNdLCA0LCBmb3JtYXQgPSAiZiIpLCAgDQogIGA5NSUgQ0kgKEJvb3RzdHJhcCB0KWAgPSBidGMuY2ksICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICANCiAgYDk1JSBDSSAoQm9vdHN0cmFwIHIpYCA9IGJ0ci5jaSwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICANCiAgYHAtdmFsdWVzYCA9IGZvcm1hdEMocF92YWx1ZXMsIDQsIGZvcm1hdCA9ICJmIikgICAgICAgICAgICAgICAgICANCikNCg0KDQpsaWJyYXJ5KGtuaXRyKQ0Ka2FibGUoYXMuZGF0YS5mcmFtZShjb21iaW5lZF9tYXRyaXgpLCANCiAgICAgIGNhcHRpb24gPSAiRmluYWwgQ29tYmluZWQgSW5mZXJlbnRpYWwgU3RhdGlzdGljczogQ29lZmZpY2llbnRzLCBwLXZhbHVlcywgYW5kIEJvb3RzdHJhcCBDSXMiKQ0KDQoNCg0KYGBgDQoNCkFsbCBvZiB0aGUgbW9kZWxzIHlpZWxkIHRoZSBzYW1lIHJlc3VsdCwgc2hvd2luZyB0aGVyZSBpcyBhIHZpb2xhdGlvbiBpbiB0aGUgYWdlIHZhcmlhYmxlIGJ1dCBhbGwgb3RoZXIgdmFyaWFibGVzIGFyZSBzaWduaWZpY2FudC4NCg0KYGBge3J9DQoNCmthYmxlKHJvdW5kKGNiaW5kKGJ0Yy53ZCwgYnRyLndkKSw0KSwgY2FwdGlvbj0id2lkdGggb2YgdGhlIHR3byBib290c3RyYXAgY29uZmlkZW5jZSBpbnRlcnZhbHMiKQ0KDQoNCmBgYA0KDQpXZSBzZWUgZnJvbSB0aGUgYWJvdmUgYW5hbHlzaXMgdGhhdCB0aGUgdHdvIHZhbHVlcyBhcmUgdmVyeSBzaW1pbGFyIHRvIGVhY2ggb3RoZXIuDQoNCg0KDQojIyMgU3VtbWFyeQ0KDQpXZSBzZWUgZnJvbSBvdXIgZmluZGluZ3MgYWJvdmUgdGhhdCBhbGwgdGhyZWUgb2Ygb3VyIG1vZGVscyBjb21lIHRvIHRoZSBzYW1lIGNvbmNsdXNpb24uIFdlIHdpbGwgYmUgdXNpbmcgb3VyIGluaXRpYWwgZmluYWwgbW9kZWwgd2hlcmUgd2UgdXNlZCBhIGxvZyB0cmFuc2Zvcm1hdGlvbiBiZWNhdXNlIHRoZXkgc2VlbSB0byBiZSBhIGxpdHRsZSBtb3JlIHNpZ25pZmljYW50LiANCg0KQWdhaW4gd2Ugc2VlIHRoYXQgdGhlIGZpbmFsIG1vZGVsIGNhbiBiZSByZXByZXNlbnRlZCBhcyBsb2cocHJpY2UpPTMuOTI0NSDiiJIgMC4wMjQ2w5dBdmVyYWdlX0RyaXZlICsgMC4xODY2w5dHcmVlbnNfb25fcmVnICsgDQowLjAzODfDl1NhdmVfcGVyY2VudCDiiJIgMC4wMTgxw5dOdW1iZXJfZXZlbnRzICsgMC4xODI2w5dBZ2VfQWJvdmVfMzBUUlVFDQoNCkZyb20gdGhlIGFib3ZlIGRhdGEgd2UgY2FuIGFsc28gY29uY2x1ZGUgdGhhdCBhcyBhdmVyYWdlIHdpbm5pbmdzIGdvIHVwIG91ciBhdmVyYWdlIGRyaXZlIGdvZXMgZG93biBieSAtMi41JSB3aGlsZSBvdGhlciB2YXJpYWJsZXMgc3VjaCBhcyBncmVlbnMgb24gcmVndWxhdGlvbiBhbmQgc2F2ZSBwZXJjZW50IGdvIHVwLg0KDQojIyBDb25jbHVzaW9uIA0KVGhlIG1haW4gY29uY2x1c2lvbiB3ZSBjYW4gZHJhdyBpcyB0aGF0IHdoaWxlIGRyaXZpbmcgZGlzdGFuY2UgaW4gZ29sZiBpcyBwZXJjZWl2ZWQgdG8gYmUgYSBiaWcgaW5kaWNhdGlvbiBvZiB3aGV0aGVyIGEgcGVyc29uIGlzIHdpbm5pbmcgb3Igbm90IHdlIGRvIG5vdCBzZWUgdGhpcyBpbiBvdXIgYW5hbHlzaXMuIFdoaWxlIHRoZSBBdmVyYWdlIFdpbm5pbmdzIGdvZXMgdXAgdGhlIGF2ZXJhZ2UgZHJpdmUgZ29lcyBkb3duIGJ5IC0yLjUlIHdoaWxlIFNhdmUgUGVyY2VudGFnZSBnb2VzIHVwIGJ5IDMuOSUgYW5kIEdyZWVucyBvbiBSZWd1bGF0aW9uIGdvZXMgdXAgYnkgMS45JSB3aGljaCBzaG93cyB0aGF0IHRoZSAic2hvcnQiIGdhbWUgaW4gZ29sZiAocHV0dGluZyBhbmQgY2hpcHBpbmcpIGlzIG1vcmUgaW1wb3J0YW50IHRoYW4gZHJpdmluZyBpbiByZWdhcmRzIHRvIGF2ZXJhZ2UgbW9uZXkgd29uLg0KDQpXZSBzZWUgb25lIGZsYXcgaW4gb3VyIGFuYWx5c2lzIGFuZCBpdCB3YXMgdGhhdCBhZ2Ugd2FzIG5vdCBzaWduaWZpY2FudCB3aGljaCBjb3VsZCBtZXNzIHVwIG91ciBhbmFseXNpcy4gaW4gdGhlIGZ1dHVyZSB3ZSBjb3VsZCBwdXNoIG91ciBhZ2UgdXAgb3IgZG93biBmb3IgdGhlIHRydWUgb3IgZmFsc2Ugc3RhdGVtZW50IHRvIHNlZSBpZiB0aGF0IGNoYW5nZXMgdGhlIHNpZ25pZmljYW5jZS4gV2UgY291bGQgYWxzbyBsb29rIGF0IHNvbWUgdmFsdWVzIG9mIGFnZSB0byBzZWUgaWYgdGhlcmUgYXJlIGFueSBvdXRsaWVycywgZm9yIGV4YW1wbGUgZ29sZmVycyBhcmUgbm90IHlvdW5nZXIgdGhhbiAyMCBidXQgdGhleSBjYW4gYmUgNDUrIHdoaWNoIGNvdWxkIG1lc3Mgd2l0aCBvdXIgc2lnbmlmaWNhbmNlLg0KDQpXZSBjYW4gdXNlIHRoaXMgbW9kZWwgZm9yIHNlYXNvbnMgdG8gY29tZSB0byBzZWUgaWYgdGhpbmdzIG1heSBoYXZlIGNoYW5nZWQuIEZvciBleGFtcGxlIHNpbmNlIGdvbGYgaGFzIGV2b2x2ZWQgcmVjZW50bHkgdG8gcGxheWVycyBoYXZpbmcgbG9uZ2VyIGRyaXZlcyB3ZSBjb3VsZCBydW4gdGhpcyBtb2RlbCBhZ2FpbiBpbiAyMDI0IHRvIHNlZSBpZiB0aGUgInNob3J0IiBnYW1lIGlzIGxlc3MgbGlrZWx5IHRvIHdpbiBtb3JlIG1vbmV5IHRoYW4gdGhlICJsb25nIiBnYW1lIHdvdWxkLg0KDQo=