As described in the abstract we have 100 subjects undergoing various tests. The first thing to do is to read in the data Q1 Read in the data make sure that you are in the correct directory to read the data in. You can use the commented out setwd() command to bring you to the correct area if you need to.

#setwd("<YOUR DIRECTORY NAME HERE>")
data = read.csv("d10123132.csv", header = TRUE)

With that done we can begin to look at some of the features of the data Q2 Summarise the data and define types

Age=data$Age
Sex=data$Sex
Height=data$Height
ReactionTime=data$ReactionTime
AgeGroup= data$AGE_GROUP

Lets take a look at the basic data from the group

summary(data)
       X               Age        Sex        Height       ReactionTime  
 Min.   :  1.00   Min.   :20.36   F:48   Min.   :153.4   Min.   :143.8  
 1st Qu.: 25.75   1st Qu.:32.14   M:52   1st Qu.:162.6   1st Qu.:376.4  
 Median : 50.50   Median :46.08          Median :166.6   Median :508.5  
 Mean   : 50.50   Mean   :45.47          Mean   :168.4   Mean   :489.8  
 3rd Qu.: 75.25   3rd Qu.:58.86          3rd Qu.:173.7   3rd Qu.:593.8  
 Max.   :100.00   Max.   :69.77          Max.   :190.4   Max.   :800.6  
 AGE_GROUP 
 20-40:37  
 40-70:63  
           
           
           
           

Looking for the means and Standard Deviations where Available Q3 Calculate the mean and standard deviation of the Age, Height and Reaction Times of the test group

print(paste("Average Age: ",mean(Age), " years"))
[1] "Average Age:  45.47  years"
print(paste("Standard Deviation Age:", sd(Age)," years" ))
[1] "Standard Deviation Age: 14.6462281144258  years"
       
print(paste("Average Height: ",mean(Height), " cm"))
[1] "Average Height:  168.4159  cm"
print(paste("Standard Deviation Heigh:", sd(Height), " cm"))
[1] "Standard Deviation Heigh: 8.65580731023509  cm"
print(paste("Average Reaction Time: ",mean(ReactionTime), " ms"))
[1] "Average Reaction Time:  489.813  ms"
print(paste("Standard Deviation Reaction Time:", sd(ReactionTime), " ms"))
[1] "Standard Deviation Reaction Time: 138.382611533196  ms"

Lets see what some of the data looks like Q4A Plot a histogram of the i) reaction time data, ii) age data, and iii) height data

Since there is some plotting to be done here lets write a function to produce some nice plots from given parameters

plotHistogram <- function(data, unit, title, xlab, ylab) {
  
  #data for mean and median
  mean=round(mean(data), digits= 2)
  median=round(median(data), digits = 2)
  # get the legend
  
  Mean <- paste("Mean:", toString(mean), unit, sep=" ")
  Median <-  paste("Median: ",  toString(median), unit, sep=" ")
  key <- c(Mean, Median)
  color <- c("red", "blue")
  #data for scaling 
  ymax <- max(hist(data, plot = FALSE)$counts)
 
  
  hist(data, main=title, xlab = xlab, ylab=ylab, labels=TRUE, ylim=c(0, 1.1*ymax), col=rgb(0,0,1,alpha=.25))
  #add mean median and legend
  abline(v=mean, col= 'red')
  abline(v=median, col='blue')
  legend('topright',key, fill = color, cex= 0.75)
}

This function takes in the data to be graphed, A unit of measure for the x values of the data, and some labelling information in Title, X-axis label and Y-axis label. It then returns a labelled plot with the mean and median marked in it. Lets call it for Reaction Time data

plotHistogram(data=ReactionTime, 
              unit="ms", 
              title= "Frequence of reaction Times", 
              xlab = "Reaction Times (ms)",
              ylab = "Frequecy of occurance")

This histogram shows that the data for the reaction time is roughly symmetrical with a mean at 489.8 ms and median of 508.5 ms. This tightness of mean to median suggests that there are few outliers to the data here. We also see that there is some symmatery to the data suggesting that the data may come from a normal distribution. There a are 2 peaks to the data at about 300-400 ms and at about 500-600 ms which hopefully can be explained with further analysis

plotHistogram(data=Height, 
              unit="cm", 
              title= "Frequence of Heights in Sample group", 
              xlab = "Height(cm)",
              ylab = "Frequecy of occurance")

Here we see the data is slightly differently shaped. The mean and median are slightly more seperated and there is a tail to the data at the higher end ( > 190 cm) of the data. This suggests the presence of an outlier ‘pulling’ the data towards that end eg. A really tall person sampled.

plotHistogram(data = Age, 
              unit = "years",
              title = "Plot of the frequency of ages in the sample group",
              xlab = "Age in years",
              ylab = "Frequecy of ages"
                )

Finally this data is quite flat. The mean and median are almost identical suggeting that the data does not have many outliers or extreme values pulling at it. its also shows that the sample group is roughly equally likely to have ages anywhere from 20-70

b. Plot a histogram of the reaction time data for each age group (20-40yrs, 40-70yrs) First we need to group the sample into the age groups looked for then we can plot the data

younger<- subset(data, AGE_GROUP=='20-40')
older <- subset(data, AGE_GROUP=="40-70")
youngReaction <- younger$ReactionTime
olderReaction <- older$ReactionTime

Once that is done we can write some code to prettily plot the graphs side by side

sidebyside<- function(X,Y,title=NULL,xlabel=NULL, ylabel=NULL, legend=NULL){
#set parameters for the scale of the graph
if (max(X) > max(Y)){
  xmax <- max(X)
}else{
  xmax <- max(Y)
}
xtall <- max(hist(X, plot=FALSE)$counts)
ytall <- max(hist(Y, plot=FALSE)$counts)
if (xtall>ytall){
  ymax=xtall
}else{
  ymax=ytall
}
#set the legends
key <- legend
color <- c("blue", "red")
meanX<- mean(X)
meanY<- mean(Y)
#plot the 2 graphs side by side 
hist(X, col=rgb(0,0,1,alpha=.25), 
     main=title, xlab=xlabel, ylab=ylabel, xlim = c(0, 1.1*xmax), labels=TRUE,  ylim=c(0,1.1*ymax)) 
hist(Y, col=rgb(1,0,0, alpha=0.25), xlim = c(0, 1.1*xmax), ylim=c(0,1.1*ymax),labels=TRUE, add=TRUE) 
#add lines showing mean of each section
abline(v=meanX, col= 'blue')
text(meanX, ymax, paste("mean:", round(meanX, digits=2)) , col = "blue", cex=0.5) 
abline(v=meanY, col='red')
text(meanY, (ymax-0.5), paste("mean:", round(meanY, digits=2)) , col = "red", cex=0.5) 
#include a legend
legend('topleft',key, fill = color, cex= 0.5 )
}

Finally with all of the prep work done we can plot the data

sidebyside(youngReaction, olderReaction, title="Comparative reaction Times by Age", xlabel = "Reaction Times (ms)", ylabel="counts", legend=c("Age Group 20-40", "Age Group 40-70"))

Here we can see that for younger people the mean reaction is lower than for older people. The older population (in red) tends towars the 300-900 ms range while the younger population tends towards the 100-700 ms. We see a longer tail on the younger population that the corresponding tail on the older one which is slightly more symmaterical. When looking at this graph we see a reason for the 2 peaks meantioned in the previous reaction times graph. The 2 peaks of the combined graph coincide with the means of each of the age groups on this graph.

Given that the populations mean reaction time is 450ms. Conduct a hypothesis test stating the Null H0 and alternative Hα, calculating the test statistics, stating your criteria for rejection, and your conclusion for the reaction time data of the: a.20-40 year old group b. 40-70 year old group

  1. First lets write a function to calculate some Confidence intervals for the data and we will also use the features of the data to calculate them
findCI <- function(x, alpha){
  
  #set up variables
  xbar <- mean(x)
  sigma <- sd(x)
  n <- length(x)
  se <- sigma/sqrt(n)
  
  
  #calculate t
  tn_1_alpha.2 <- qt(1 - alpha/2, df = n - 1)
  
  #return the CI
  
  CI <-  c(xbar-tn_1_alpha.2*se,xbar+tn_1_alpha.2*se)
  return (CI)
  
} 

This takes a rejection criteria ( in decimal ) and a data set and calculates a confidence interval for the test statistic to fall inside in order for the null hypothesis to be accepted.

findCI(youngReaction,0.05)
[1] 354.5534 437.6536

We would like our null hypothesis to fall within this region if we are to accept the null hypothesis

CItest <- function ( h0, x, alpha){
  CI <- findCI( x, alpha)
  if(h0 < CI[1]){
    ans<-" H0 rejected"
    reason <-paste(h0, "falls below the lower limit of our", (1-alpha)*100, "% confidence interval which is found to be: " ,CI[1], sep=" ")
  }
  else if (h0>CI[2]){
    
    ans<-"H0 rejected"
    reason <-paste(h0, "falls above the upper limit of our", (1-alpha)*100, "% confidence interval which is found to be: " ,CI[2], sep=" ")
  }
  else{
    ans <- "H0 Not rejected"
    reason <-paste(h0, "falls between the lower and upper limit of our", (1-alpha)*100, "% confidence interval which is found to be between : " ,CI[1],"and",CI[2], sep=" ")
  }
  
return(paste(ans, reason, sep=" : "))  
}

Lets call this function to test what we can already see (h0: \(mu\)= 450 does not fit in our confidence interval)

CItest(450, youngReaction, 0.05)
[1] "H0 rejected : 450 falls above the upper limit of our 95 % confidence interval which is found to be:  437.653611494543"

So for this test we reject the null hypothesis. Because h0 falls outside the upper boundary of the the Confidence Interval we can deduce that the real mean is lower than 450.

t.test(youngReaction, mu=450, conf.level = .95)

    One Sample t-test

data:  youngReaction
t = -2.6307, df = 36, p-value = 0.01246
alternative hypothesis: true mean is not equal to 450
95 percent confidence interval:
 354.5534 437.6536
sample estimates:
mean of x 
 396.1035 

This built in function gives some more information, most notably the p-value which here is significantly less than the \(t{a/2},{n-1}\) so once again we can reject the null hypothesis lets graph this on the histogram of ages ( means more functions.)

histCI <- function(data, alpha, title=NULL, xlab=NULL, ylab=NULL, h0) {
  ymax=max(hist(data, plot=FALSE)$counts)
  hist(data, col=rgb(0,0,1,alpha=.25), 
       main=paste(title, " with ", ((1-alpha)*100), " Confidence Interval"), xlab=xlab, ylab=ylab,  ylim=c(0,1.1*ymax)) 
  abline(v=h0, col= 'blue')
  text(h0, ymax, "h0: mean=450ms" , col = "blue", cex=0.5) 
  CI <- findCI(data,alpha)
  CImin <- CI[1]
  CImax <- CI[2]
  abline(v=CImin, col= 'red')
  text(CImin, ymax-5, paste("CI min:", round(CImin, digits=2), " ms") , col = "red", cex=0.5) 
  abline(v=CImax, col= 'red')
  text(CImax, ymax-2.5, paste("CI max:", round(CImax, digits=2), " ms") , col = "red", cex=0.5) 
}
histCI(youngReaction, 0.05, "Young reaction times", "Reaction Times (ms)", "counts", 450)

This shows that the hypothesised mean falls ( just) outside the confidence intervals of the distribution b. 40 -70 age group We just repeat this for the older age group

CItest(450, olderReaction, 0.05)
[1] " H0 rejected : 450 falls below the lower limit of our 95 % confidence interval which is found to be:  515.867635752091"
histCI(olderReaction, 0.05, "Older reaction times", "Reaction Times (ms)", "counts", 450)

Here we see that that the Confidence interval is higher for the sample group, so that our mean of 450 is too low for the sample. Lets check the Ttest function to be sure

t.test(olderReaction, mu=450)

    One Sample t-test

data:  olderReaction
t = 6.5422, df = 62, p-value = 1.324e-08
alternative hypothesis: true mean is not equal to 450
95 percent confidence interval:
 515.8676 573.8298
sample estimates:
mean of x 
 544.8487 

Again if we look at the p-value it is far below the 1.96 needed to accept the null hypothesis

SO once again we reject the Null hypothesis that \({mu}= 450ms\)

Linear regression analysis a.Plot a scatterplot of the reaction time data (y-axis) as a function of age(x-axis). Conduct a linear regression analysis of reaction time as a functionage and interpret the results.

First lets plot the data

scatter.smooth(x=Age, y=ReactionTime, main="Reaction Time ~ Age")  # scatterplot

Lets see if we can clean this up first

par(mfrow=c(1,2))
boxplot(Age, main="Age", sub=paste("Outlier rows: ", boxplot.stats(Age)$out))  # box plot for 'Age'
boxplot(ReactionTime, main="Reaction Time", sub=paste("Outlier rows: ", boxplot.stats(ReactionTime)$out))  # box plot for 'Reaction'

From these two box plots we see that there are no outliers affecting the graph so there is not a lot of clean up available for this particular data

On its own this gives some information but its quite sparse.We see a line suggesting that the higher the age the higher the reaction time but we have no numbers for this or indeed how close these points come to the line. Lets try to fix that

First the regression line. Ideally we want it to be of the form ( in this simple case) \[ response= {\beta}{_0}+{\beta}{_1}Indicator{_1}\] or \[y=c+mx\] where m is the slope of the line and c the intercept.

linearReg <- lm(ReactionTime ~ Age, data)
Regsummary<-summary(linearReg)
Regsummary

Call:
lm(formula = ReactionTime ~ Age, data = data)

Residuals:
    Min      1Q  Median      3Q     Max 
-244.86  -66.97  -13.19   69.96  306.68 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept) 226.1311    35.9788   6.285 9.09e-09 ***
Age           5.7990     0.7535   7.696 1.12e-11 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 109.8 on 98 degrees of freedom
Multiple R-squared:  0.3767,    Adjusted R-squared:  0.3703 
F-statistic: 59.23 on 1 and 98 DF,  p-value: 1.123e-11

Taking the data we need we now we can see that the line we want is \[Reaction Time = 226.1311 + 5.7990 * age\] The next thing we want to calculate is how much of this results is explained by the model. For this we use the \(R ^{2}\) value of .3703 which suggests that 37% of the reaction time is related to age.

Finally lets conside the error of the slope and work out a confidence interval for the slope of the regression line. The standard error for the slope of this line is give as 0.7535 and we kknow that the slope is 5.770 so a 95% confidence interval for this is

getCI <- function(xbar, se, n, alpha){
  t=qt(1-(alpha/2), df=n-2)
  me=t*se
  CI=c(xbar-me, xbar+se)
  return(CI)
}
CIslope<-getCI(Regsummary$coefficients[2], Regsummary$coefficients[4], 100, 0.05)
CIslope
[1] 4.303709 6.552541

so we have 2 slopes for our line and can do the same for the intercept

CIintercept<-getCI(Regsummary$coefficients[1], Regsummary$coefficients[2], 100, 0.05)
CIintercept
[1] 214.6232 231.9302

Lets use all this information to show a better plot of the data we looked at earlier( another function )

plotregression<- function(X, Y, title, xlab, ylab, c,clow, chigh, m_bar, mlow, mhigh, CI)
{
plot(X, Y, main=title, xlab=xlab, ylab=ylab)
abline(c, m_bar , col="black")
abline(clow, mlow, col= 'red')
abline(chigh, mhigh, col= 'red')
line<- paste(ylab, " = ", round(c, digits=2), " + ",round(m_bar, digits= 2), " * ", xlab )
confidence<- paste(CI, "% condfidence interval for the Regression Line")
key <-c(line, confidence )
color<- c("black", "red")
legend("topleft", key, fill = color, cex=0.5)
}
plotregression(Age, ReactionTime,
               "Plot of Reaction Time -V- Age /w regression lines",
               "Age(years)", "Reaction Time(ms)",
               Regsummary$coefficients[1], CIintercept[1], CIintercept[2],
               Regsummary$coefficients[2], CIslope[1], CIslope[2], 95
               )

b.Plot a scatterplot of the reaction time data (y-axis) as a function of height(x-axis). Conduct a linear regression analysis of reaction time as a function of height and interpret the results. First lets plot the data

scatter.smooth(x=Height, y=ReactionTime, main="Reaction Time ~ Height")  # scatterplot

Here we see a far flatter graph ( again no info provided) and no indication there is any trends bar a reduction in reaction times at the very tallest end of the graph

Lets look for outliers

par(mfrow=c(1,2))
boxplot(Height, main="Height", sub=paste("Outlier rows: ", boxplot.stats(Age)$out))  # box plot for 'Age'
boxplot(ReactionTime, main="Reaction Time", sub=paste("Outlier rows: ", boxplot.stats(ReactionTime)$out))  # box plot for 'Reaction'

Again no huge outliers which is good ( data is ‘clean’ ) so lets move on with the regression analysis

linearReg <- lm(ReactionTime ~ Height, data)
Regsummary<-summary(linearReg)
Regsummary

Call:
lm(formula = ReactionTime ~ Height, data = data)

Residuals:
    Min      1Q  Median      3Q     Max 
-320.20 -115.69   25.34  100.50  310.46 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)  
(Intercept)  687.439    271.605   2.531    0.013 *
Height        -1.173      1.611  -0.729    0.468  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 138.7 on 98 degrees of freedom
Multiple R-squared:  0.005387,  Adjusted R-squared:  -0.004762 
F-statistic: 0.5308 on 1 and 98 DF,  p-value: 0.468

Here we get the Linear regression formula to be \[Reaction Time(ms) = 687.439 - 1.173*Height( in cm) \] and the \(R^{2}\) value for this is 0.005358 which translates to 0.5% of the reaction time explained by the height, so it would be fair to say that Height has miniscule influence on the reaction time

Lets do Confidence intervals and the prettied up regression line( for what its worth)

CIslope<-getCI(Regsummary$coefficients[2], Regsummary$coefficients[4], 100, 0.05)
CIintercept<-getCI(Regsummary$coefficients[1], Regsummary$coefficients[3], 100, 0.05)
plotregression(Age, ReactionTime,
               "Plot of Reaction Time -V- Height /w regression lines",
               "Height(cm)", "Reaction Time(ms)",
               Regsummary$coefficients[1], CIintercept[1], CIintercept[2],
               Regsummary$coefficients[2], CIslope[1], CIslope[2], 95)

To see why the CI lines dont show on the graph lets look at the CI data for the intercept

CIintercept
[1] 148.4473 959.0442

So we see that this does not actually fit onto the graph at the scale we are at showing how little value this regression brings to the data

Logistic regression analysis: a.Conduct a Logistic regression analysis of age group as a function of reaction time and interpret the results. Lets run the regression first

logistic<- glm(formula=AgeGroup~ReactionTime, family=binomial("logit"), data  )
summary(logistic)

Call:
glm(formula = AgeGroup ~ ReactionTime, family = binomial("logit"), 
    data = data)

Deviance Residuals: 
    Min       1Q   Median       3Q      Max  
-2.2024  -0.8247   0.4806   0.7638   1.6846  

Coefficients:
              Estimate Std. Error z value Pr(>|z|)    
(Intercept)  -4.156989   1.014849  -4.096  4.2e-05 ***
ReactionTime  0.009952   0.002145   4.639  3.5e-06 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 131.79  on 99  degrees of freedom
Residual deviance: 101.41  on 98  degrees of freedom
AIC: 105.41

Number of Fisher Scoring iterations: 4

From this we see that the logistic regression takes the form of \[Pr(Age Group 40-70|Reaction time)= \frac{e^{\eta_{i}}}{1+e^{\eta_{i}} }\] where \[\eta_{i}=-4.156989+0.009953*Reaction Time\]

and the odds on being in the the40-70 age group given a reaction time are \[e^{\eta_{i}}:1\] With eta as desribed.

Now we can investogate the significance of this model by investigating if a 95% Confidence interval have values either side of zero

CIintercept<-getCI(4.156989,1.014849, 100, 0.05)
CIslope<- getCI( 0.009952,0.002145, 100, 0.05)
CIintercept
[1] 2.143054 5.171838
CIslope
[1] 0.005695317 0.012097000

As we see ofr each of the values in the CI are positive so we can say that this model is significant.

Finally lets plot the regression

 # assign 'survival' to these 20 individuals non-randomly... most mortality occurs at smaller body size
dat=as.data.frame(cbind(ReactionTime,AgeGroup)) # saves dataframe with two columns: body size & survival
dat$AgeGroup=dat$AgeGroup-1 # just 
plot(dat$ReactionTime,dat$AgeGroup,xlab="Reaction Time",ylab="Probability age between 40-70", main='Logistic regression for Age Group V Reaction Time') # plot with body size on x-axis and survival (0 or 1) on y-axis
curve(predict(logistic,data.frame(ReactionTime=x),type="resp"),add=TRUE, col="red") # draws a curve based on prediction from logistic regression model
key<-c('Value of Reaction Time and Age group', 'Regression line of probability')
col<-c('black', 'red')
legend(600, 0.4, key, fill=col,cex=0.5 )

As we see here the graph is of a typical logistic regression shape so we can summarise that our models fits the data quite well

b.Conduct a Logistic regression analysis of age group as a function of height and interpret the results.

logistic<- glm(formula=AgeGroup~Height, family=binomial("logit"), data  )
summary(logistic)

Call:
glm(formula = AgeGroup ~ Height, family = binomial("logit"), 
    data = data)

Deviance Residuals: 
    Min       1Q   Median       3Q      Max  
-1.5539  -1.3860   0.8904   0.9689   1.1341  

Coefficients:
            Estimate Std. Error z value Pr(>|z|)
(Intercept)  4.24830    4.05677   1.047    0.295
Height      -0.02204    0.02400  -0.918    0.358

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 131.79  on 99  degrees of freedom
Residual deviance: 130.95  on 98  degrees of freedom
AIC: 134.95

Number of Fisher Scoring iterations: 4

From this we see that the logistic regression takes the form of \[Pr(Age Group 40-70|Height)= \frac{e^{\eta_{i}}}{1+e^{\eta_{i}} }\]

where \[\eta_{i}=4.24830-0.02204*Height\]

and the odds on being in the the40-70 age group given a reaction time are

\[e^{\eta_{i}}:1\]

With eta as desribed.

Now we can investogate the significance of this model by investigating if a 95% Confidence interval have values either side of zero

CIintercept<-getCI( 4.24830,4.05677 , 100, 0.05)
CIslope<- getCI( -0.02204,0.024005, 100, 0.05)
CIintercept
[1] -3.802228  8.305070
CIslope
[1] -0.06967714  0.00196500

In this case we see that the CI clealy passes over 0 in both cases so we can say that the model is not significant. We can also plot this regression to see what it tells us.

 # assign 'survival' to these 20 individuals non-randomly... most mortality occurs at smaller body size
dat=as.data.frame(cbind(Height,AgeGroup)) # saves dataframe with two columns: body size & survival
dat$AgeGroup=dat$AgeGroup-1 # just 
plot(dat$Height,dat$AgeGroup,xlab="Height",ylab="Probability age between 40-70", main='Logistic regression for Age Group V Height') # plot with body size on x-axis and survival (0 or 1) on y-axis
curve(predict(logistic,data.frame(Height=x),type="resp"),add=TRUE, col="red") # draws a curve based on prediction from logistic regression model
key<-c('Value of Height and Age group', 'Regression line of probability')
col<-c('black', 'red')
legend(180, 0.4, key, fill=col,cex=0.5 )

Here we see the regression line is flatter and has no real way to predict the age group from the Height given( as we alluded to with the confidence intervals)

LS0tDQp0aXRsZTogIlRha2UgSG9tZSBBc3NpZ25tZW50Ig0Kc3VidGl0bGU6IENpYXJhbiBEYXJjeSBEMTAxMjMxMzIgZDEwMTIzMTMyQG15ZGl0LmllDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQNCiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0DQogIHdvcmRfZG9jdW1lbnQ6IGRlZmF1bHQNCi0tLQ0KDQpBcyBkZXNjcmliZWQgaW4gdGhlIGFic3RyYWN0IHdlIGhhdmUgMTAwIHN1YmplY3RzIHVuZGVyZ29pbmcgdmFyaW91cyB0ZXN0cy4gVGhlIGZpcnN0IHRoaW5nIHRvIGRvIGlzIHRvIHJlYWQgaW4gdGhlIGRhdGEgDQoqKlExICBSZWFkIGluIHRoZSBkYXRhKiogbWFrZSBzdXJlIHRoYXQgeW91IGFyZSBpbiB0aGUgY29ycmVjdCBkaXJlY3RvcnkgdG8gcmVhZCB0aGUgZGF0YSBpbi4gWW91IGNhbiB1c2UgdGhlIGNvbW1lbnRlZCBvdXQgc2V0d2QoKSBjb21tYW5kIHRvIGJyaW5nIHlvdSB0byB0aGUgY29ycmVjdCBhcmVhIGlmIHlvdSBuZWVkIHRvLiANCmBgYHtyfQ0KI3NldHdkKCI8WU9VUiBESVJFQ1RPUlkgTkFNRSBIRVJFPiIpDQpkYXRhID0gcmVhZC5jc3YoImQxMDEyMzEzMi5jc3YiLCBoZWFkZXIgPSBUUlVFKQ0KDQpgYGANCldpdGggdGhhdCBkb25lIHdlIGNhbiBiZWdpbiB0byBsb29rIGF0IHNvbWUgb2YgdGhlIGZlYXR1cmVzIG9mIHRoZSBkYXRhDQoqKlEyIFN1bW1hcmlzZSB0aGUgZGF0YSBhbmQgZGVmaW5lIHR5cGVzKioNCmBgYHtyfQ0KQWdlPWRhdGEkQWdlDQpTZXg9ZGF0YSRTZXgNCkhlaWdodD1kYXRhJEhlaWdodA0KUmVhY3Rpb25UaW1lPWRhdGEkUmVhY3Rpb25UaW1lDQpBZ2VHcm91cD0gZGF0YSRBR0VfR1JPVVANCmBgYA0KTGV0cyB0YWtlIGEgbG9vayBhdCB0aGUgYmFzaWMgZGF0YSBmcm9tIHRoZSBncm91cA0KYGBge3J9DQpzdW1tYXJ5KGRhdGEpDQoNCmBgYA0KTG9va2luZyBmb3IgdGhlIG1lYW5zIGFuZCBTdGFuZGFyZCBEZXZpYXRpb25zIHdoZXJlIEF2YWlsYWJsZQ0KKipRMyBDYWxjdWxhdGUgdGhlIG1lYW4gYW5kIHN0YW5kYXJkIGRldmlhdGlvbiBvZiB0aGUgQWdlLCBIZWlnaHQgYW5kIFJlYWN0aW9uIFRpbWVzIG9mIHRoZSB0ZXN0IGdyb3VwKioNCmBgYHtyfQ0KcHJpbnQocGFzdGUoIkF2ZXJhZ2UgQWdlOiAiLG1lYW4oQWdlKSwgIiB5ZWFycyIpKQ0KcHJpbnQocGFzdGUoIlN0YW5kYXJkIERldmlhdGlvbiBBZ2U6Iiwgc2QoQWdlKSwiIHllYXJzIiApKQ0KICAgICAgIA0KYGBgDQpgYGB7cn0NCnByaW50KHBhc3RlKCJBdmVyYWdlIEhlaWdodDogIixtZWFuKEhlaWdodCksICIgY20iKSkNCnByaW50KHBhc3RlKCJTdGFuZGFyZCBEZXZpYXRpb24gSGVpZ2g6Iiwgc2QoSGVpZ2h0KSwgIiBjbSIpKQ0KYGBgDQpgYGB7cn0NCnByaW50KHBhc3RlKCJBdmVyYWdlIFJlYWN0aW9uIFRpbWU6ICIsbWVhbihSZWFjdGlvblRpbWUpLCAiIG1zIikpDQpwcmludChwYXN0ZSgiU3RhbmRhcmQgRGV2aWF0aW9uIFJlYWN0aW9uIFRpbWU6Iiwgc2QoUmVhY3Rpb25UaW1lKSwgIiBtcyIpKQ0KYGBgDQpMZXRzIHNlZSB3aGF0IHNvbWUgb2YgdGhlIGRhdGEgbG9va3MgbGlrZQ0KKipRNEEgUGxvdCBhIGhpc3RvZ3JhbSBvZiB0aGUgaSkgcmVhY3Rpb24gdGltZSBkYXRhLCBpaSkgYWdlIGRhdGEsIGFuZCBpaWkpIGhlaWdodCBkYXRhKioNCg0KU2luY2UgdGhlcmUgaXMgc29tZSBwbG90dGluZyB0byBiZSBkb25lIGhlcmUgbGV0cyB3cml0ZSBhIGZ1bmN0aW9uIHRvIHByb2R1Y2Ugc29tZSBuaWNlIHBsb3RzIGZyb20gZ2l2ZW4gcGFyYW1ldGVycw0KYGBge3J9DQpwbG90SGlzdG9ncmFtIDwtIGZ1bmN0aW9uKGRhdGEsIHVuaXQsIHRpdGxlLCB4bGFiLCB5bGFiKSB7DQogIA0KICAjZGF0YSBmb3IgbWVhbiBhbmQgbWVkaWFuDQogIG1lYW49cm91bmQobWVhbihkYXRhKSwgZGlnaXRzPSAyKQ0KICBtZWRpYW49cm91bmQobWVkaWFuKGRhdGEpLCBkaWdpdHMgPSAyKQ0KICAjIGdldCB0aGUgbGVnZW5kDQogIA0KICBNZWFuIDwtIHBhc3RlKCJNZWFuOiIsIHRvU3RyaW5nKG1lYW4pLCB1bml0LCBzZXA9IiAiKQ0KICBNZWRpYW4gPC0gIHBhc3RlKCJNZWRpYW46ICIsICB0b1N0cmluZyhtZWRpYW4pLCB1bml0LCBzZXA9IiAiKQ0KICBrZXkgPC0gYyhNZWFuLCBNZWRpYW4pDQogIGNvbG9yIDwtIGMoInJlZCIsICJibHVlIikNCiAgI2RhdGEgZm9yIHNjYWxpbmcgDQogIHltYXggPC0gbWF4KGhpc3QoZGF0YSwgcGxvdCA9IEZBTFNFKSRjb3VudHMpDQogDQogIA0KICBoaXN0KGRhdGEsIG1haW49dGl0bGUsIHhsYWIgPSB4bGFiLCB5bGFiPXlsYWIsIGxhYmVscz1UUlVFLCB5bGltPWMoMCwgMS4xKnltYXgpLCBjb2w9cmdiKDAsMCwxLGFscGhhPS4yNSkpDQoNCg0KICAjYWRkIG1lYW4gbWVkaWFuIGFuZCBsZWdlbmQNCiAgYWJsaW5lKHY9bWVhbiwgY29sPSAncmVkJykNCiAgYWJsaW5lKHY9bWVkaWFuLCBjb2w9J2JsdWUnKQ0KICBsZWdlbmQoJ3RvcHJpZ2h0JyxrZXksIGZpbGwgPSBjb2xvciwgY2V4PSAwLjc1KQ0KfQ0KDQpgYGANClRoaXMgZnVuY3Rpb24gdGFrZXMgaW4gdGhlIGRhdGEgdG8gYmUgZ3JhcGhlZCwgQSB1bml0IG9mIG1lYXN1cmUgZm9yIHRoZSB4IHZhbHVlcyBvZiB0aGUgZGF0YSwgYW5kIHNvbWUgbGFiZWxsaW5nIGluZm9ybWF0aW9uIGluIFRpdGxlLCBYLWF4aXMgbGFiZWwgYW5kIFktYXhpcyBsYWJlbC4gSXQgdGhlbiByZXR1cm5zIGEgbGFiZWxsZWQgcGxvdCB3aXRoIHRoZSBtZWFuIGFuZCBtZWRpYW4gbWFya2VkIGluIGl0Lg0KTGV0cyBjYWxsIGl0IGZvciBSZWFjdGlvbiBUaW1lIGRhdGENCmBgYHtyfQ0KcGxvdEhpc3RvZ3JhbShkYXRhPVJlYWN0aW9uVGltZSwgDQogICAgICAgICAgICAgIHVuaXQ9Im1zIiwgDQogICAgICAgICAgICAgIHRpdGxlPSAiRnJlcXVlbmNlIG9mIHJlYWN0aW9uIFRpbWVzIiwgDQogICAgICAgICAgICAgIHhsYWIgPSAiUmVhY3Rpb24gVGltZXMgKG1zKSIsDQogICAgICAgICAgICAgIHlsYWIgPSAiRnJlcXVlY3kgb2Ygb2NjdXJhbmNlIikNCg0KYGBgDQoNClRoaXMgaGlzdG9ncmFtIHNob3dzIHRoYXQgdGhlIGRhdGEgZm9yIHRoZSByZWFjdGlvbiB0aW1lIGlzIHJvdWdobHkgc3ltbWV0cmljYWwgd2l0aCBhIG1lYW4gYXQgNDg5LjggbXMgYW5kIG1lZGlhbiBvZiA1MDguNSBtcy4gVGhpcyB0aWdodG5lc3Mgb2YgbWVhbiB0byBtZWRpYW4gc3VnZ2VzdHMgdGhhdCB0aGVyZSBhcmUgZmV3IG91dGxpZXJzIHRvIHRoZSBkYXRhIGhlcmUuIFdlIGFsc28gc2VlIHRoYXQgdGhlcmUgaXMgc29tZSBzeW1tYXRlcnkgdG8gdGhlIGRhdGEgc3VnZ2VzdGluZyB0aGF0IHRoZSBkYXRhIG1heSBjb21lIGZyb20gYSBub3JtYWwgZGlzdHJpYnV0aW9uLiBUaGVyZSBhIGFyZSAyIHBlYWtzIHRvIHRoZSBkYXRhIGF0IGFib3V0IDMwMC00MDAgbXMgYW5kIGF0IGFib3V0IDUwMC02MDAgbXMgd2hpY2ggaG9wZWZ1bGx5IGNhbiBiZSBleHBsYWluZWQgd2l0aCBmdXJ0aGVyIGFuYWx5c2lzDQoNCmBgYHtyfQ0KcGxvdEhpc3RvZ3JhbShkYXRhPUhlaWdodCwgDQogICAgICAgICAgICAgIHVuaXQ9ImNtIiwgDQogICAgICAgICAgICAgIHRpdGxlPSAiRnJlcXVlbmNlIG9mIEhlaWdodHMgaW4gU2FtcGxlIGdyb3VwIiwgDQogICAgICAgICAgICAgIHhsYWIgPSAiSGVpZ2h0KGNtKSIsDQogICAgICAgICAgICAgIHlsYWIgPSAiRnJlcXVlY3kgb2Ygb2NjdXJhbmNlIikNCmBgYA0KSGVyZSB3ZSBzZWUgdGhlIGRhdGEgaXMgc2xpZ2h0bHkgZGlmZmVyZW50bHkgc2hhcGVkLiBUaGUgbWVhbiBhbmQgbWVkaWFuIGFyZSBzbGlnaHRseSBtb3JlIHNlcGVyYXRlZCBhbmQgdGhlcmUgaXMgYSB0YWlsIHRvIHRoZSBkYXRhIGF0IHRoZSBoaWdoZXIgZW5kICggPiAxOTAgY20pIG9mIHRoZSBkYXRhLiBUaGlzIHN1Z2dlc3RzIHRoZSBwcmVzZW5jZSBvZiBhbiBvdXRsaWVyICdwdWxsaW5nJyB0aGUgZGF0YSB0b3dhcmRzIHRoYXQgZW5kIGVnLiBBIHJlYWxseSB0YWxsIHBlcnNvbiBzYW1wbGVkLiAgDQpgYGB7cn0NCnBsb3RIaXN0b2dyYW0oZGF0YSA9IEFnZSwgDQogICAgICAgICAgICAgIHVuaXQgPSAieWVhcnMiLA0KICAgICAgICAgICAgICB0aXRsZSA9ICJQbG90IG9mIHRoZSBmcmVxdWVuY3kgb2YgYWdlcyBpbiB0aGUgc2FtcGxlIGdyb3VwIiwNCiAgICAgICAgICAgICAgeGxhYiA9ICJBZ2UgaW4geWVhcnMiLA0KICAgICAgICAgICAgICB5bGFiID0gIkZyZXF1ZWN5IG9mIGFnZXMiDQogICAgICAgICAgICAgICAgKQ0KYGBgDQoNCkZpbmFsbHkgdGhpcyBkYXRhIGlzIHF1aXRlIGZsYXQuIFRoZSBtZWFuIGFuZCBtZWRpYW4gYXJlIGFsbW9zdCBpZGVudGljYWwgc3VnZ2V0aW5nIHRoYXQgdGhlIGRhdGEgZG9lcyBub3QgaGF2ZSBtYW55IG91dGxpZXJzIG9yIGV4dHJlbWUgdmFsdWVzIHB1bGxpbmcgYXQgaXQuIGl0cyBhbHNvIHNob3dzIHRoYXQgdGhlIHNhbXBsZSBncm91cCBpcyByb3VnaGx5IGVxdWFsbHkgbGlrZWx5IHRvIGhhdmUgYWdlcyBhbnl3aGVyZSBmcm9tIDIwLTcwIA0KDQoNCioqYi4gUGxvdCBhIGhpc3RvZ3JhbSBvZiB0aGUgcmVhY3Rpb24gdGltZSBkYXRhIGZvciBlYWNoIGFnZSBncm91cCAoMjAtNDB5cnMsIDQwLTcweXJzKSoqDQpGaXJzdCB3ZSBuZWVkIHRvIGdyb3VwIHRoZSBzYW1wbGUgaW50byB0aGUgYWdlIGdyb3VwcyBsb29rZWQgZm9yIHRoZW4gd2UgY2FuIHBsb3QgdGhlIGRhdGENCmBgYHtyfQ0KeW91bmdlcjwtIHN1YnNldChkYXRhLCBBR0VfR1JPVVA9PScyMC00MCcpDQpvbGRlciA8LSBzdWJzZXQoZGF0YSwgQUdFX0dST1VQPT0iNDAtNzAiKQ0KDQp5b3VuZ1JlYWN0aW9uIDwtIHlvdW5nZXIkUmVhY3Rpb25UaW1lDQpvbGRlclJlYWN0aW9uIDwtIG9sZGVyJFJlYWN0aW9uVGltZQ0KDQpgYGANCk9uY2UgdGhhdCBpcyBkb25lIHdlIGNhbiB3cml0ZSBzb21lIGNvZGUgdG8gcHJldHRpbHkgcGxvdCB0aGUgZ3JhcGhzIHNpZGUgYnkgc2lkZSANCmBgYHtyfQ0Kc2lkZWJ5c2lkZTwtIGZ1bmN0aW9uKFgsWSx0aXRsZT1OVUxMLHhsYWJlbD1OVUxMLCB5bGFiZWw9TlVMTCwgbGVnZW5kPU5VTEwpew0KI3NldCBwYXJhbWV0ZXJzIGZvciB0aGUgc2NhbGUgb2YgdGhlIGdyYXBoDQppZiAobWF4KFgpID4gbWF4KFkpKXsNCiAgeG1heCA8LSBtYXgoWCkNCn1lbHNlew0KICB4bWF4IDwtIG1heChZKQ0KfQ0KDQp4dGFsbCA8LSBtYXgoaGlzdChYLCBwbG90PUZBTFNFKSRjb3VudHMpDQp5dGFsbCA8LSBtYXgoaGlzdChZLCBwbG90PUZBTFNFKSRjb3VudHMpDQoNCmlmICh4dGFsbD55dGFsbCl7DQogIHltYXg9eHRhbGwNCn1lbHNlew0KICB5bWF4PXl0YWxsDQp9DQoNCiNzZXQgdGhlIGxlZ2VuZHMNCmtleSA8LSBsZWdlbmQNCmNvbG9yIDwtIGMoImJsdWUiLCAicmVkIikNCg0KbWVhblg8LSBtZWFuKFgpDQptZWFuWTwtIG1lYW4oWSkNCg0KDQojcGxvdCB0aGUgMiBncmFwaHMgc2lkZSBieSBzaWRlIA0KaGlzdChYLCBjb2w9cmdiKDAsMCwxLGFscGhhPS4yNSksIA0KICAgICBtYWluPXRpdGxlLCB4bGFiPXhsYWJlbCwgeWxhYj15bGFiZWwsIHhsaW0gPSBjKDAsIDEuMSp4bWF4KSwgbGFiZWxzPVRSVUUsICB5bGltPWMoMCwxLjEqeW1heCkpIA0KaGlzdChZLCBjb2w9cmdiKDEsMCwwLCBhbHBoYT0wLjI1KSwgeGxpbSA9IGMoMCwgMS4xKnhtYXgpLCB5bGltPWMoMCwxLjEqeW1heCksbGFiZWxzPVRSVUUsIGFkZD1UUlVFKSANCg0KI2FkZCBsaW5lcyBzaG93aW5nIG1lYW4gb2YgZWFjaCBzZWN0aW9uDQphYmxpbmUodj1tZWFuWCwgY29sPSAnYmx1ZScpDQp0ZXh0KG1lYW5YLCB5bWF4LCBwYXN0ZSgibWVhbjoiLCByb3VuZChtZWFuWCwgZGlnaXRzPTIpKSAsIGNvbCA9ICJibHVlIiwgY2V4PTAuNSkgDQoNCmFibGluZSh2PW1lYW5ZLCBjb2w9J3JlZCcpDQp0ZXh0KG1lYW5ZLCAoeW1heC0wLjUpLCBwYXN0ZSgibWVhbjoiLCByb3VuZChtZWFuWSwgZGlnaXRzPTIpKSAsIGNvbCA9ICJyZWQiLCBjZXg9MC41KSANCg0KI2luY2x1ZGUgYSBsZWdlbmQNCmxlZ2VuZCgndG9wbGVmdCcsa2V5LCBmaWxsID0gY29sb3IsIGNleD0gMC41ICkNCg0KfQ0KYGBgDQoNCkZpbmFsbHkgd2l0aCBhbGwgb2YgdGhlIHByZXAgd29yayBkb25lIHdlIGNhbiBwbG90IHRoZSBkYXRhDQpgYGB7cn0NCnNpZGVieXNpZGUoeW91bmdSZWFjdGlvbiwgb2xkZXJSZWFjdGlvbiwgdGl0bGU9IkNvbXBhcmF0aXZlIHJlYWN0aW9uIFRpbWVzIGJ5IEFnZSIsIHhsYWJlbCA9ICJSZWFjdGlvbiBUaW1lcyAobXMpIiwgeWxhYmVsPSJjb3VudHMiLCBsZWdlbmQ9YygiQWdlIEdyb3VwIDIwLTQwIiwgIkFnZSBHcm91cCA0MC03MCIpKQ0KDQpgYGANCkhlcmUgd2UgY2FuIHNlZSB0aGF0IGZvciB5b3VuZ2VyIHBlb3BsZSB0aGUgbWVhbiByZWFjdGlvbiBpcyBsb3dlciB0aGFuIGZvciBvbGRlciBwZW9wbGUuIFRoZSBvbGRlciBwb3B1bGF0aW9uIChpbiByZWQpIHRlbmRzIHRvd2FycyB0aGUgMzAwLTkwMCBtcyByYW5nZSB3aGlsZSB0aGUgeW91bmdlciBwb3B1bGF0aW9uIHRlbmRzIHRvd2FyZHMgdGhlIDEwMC03MDAgbXMuIFdlIHNlZSBhIGxvbmdlciB0YWlsIG9uIHRoZSB5b3VuZ2VyIHBvcHVsYXRpb24gdGhhdCB0aGUgY29ycmVzcG9uZGluZyB0YWlsIG9uIHRoZSBvbGRlciBvbmUgd2hpY2ggaXMgc2xpZ2h0bHkgbW9yZSBzeW1tYXRlcmljYWwuIFdoZW4gbG9va2luZyBhdCB0aGlzIGdyYXBoIHdlIHNlZSBhIHJlYXNvbiBmb3IgdGhlIDIgcGVha3MgbWVhbnRpb25lZCBpbiB0aGUgcHJldmlvdXMgcmVhY3Rpb24gdGltZXMgZ3JhcGguIFRoZSAyIHBlYWtzIG9mIHRoZSBjb21iaW5lZCBncmFwaCBjb2luY2lkZSB3aXRoIHRoZSBtZWFucyBvZiBlYWNoIG9mIHRoZSBhZ2UgZ3JvdXBzIG9uIHRoaXMgZ3JhcGguIA0KDQoNCioqR2l2ZW4gdGhhdCB0aGUgDQpwb3B1bGF0aW9ucyBtZWFuIHJlYWN0aW9uIHRpbWUgaXMgNDUwbXMuIENvbmR1Y3QgYSBoeXBvdGhlc2lzIHRlc3Qgc3RhdGluZyB0aGUgTnVsbCBIMCBhbmQgYWx0ZXJuYXRpdmUgSM6xLCBjYWxjdWxhdGluZyB0aGUgdGVzdCBzdGF0aXN0aWNzLCBzdGF0aW5nIHlvdXIgY3JpdGVyaWEgZm9yIHJlamVjdGlvbiwgYW5kIHlvdXIgY29uY2x1c2lvbiBmb3IgdGhlIHJlYWN0aW9uIHRpbWUgZGF0YSBvZiB0aGU6DQphLjIwLTQwIHllYXIgb2xkIGdyb3VwDQpiLiA0MC03MCB5ZWFyIG9sZCBncm91cCoqDQoNCmEuIEZpcnN0IGxldHMgd3JpdGUgYSBmdW5jdGlvbiB0byBjYWxjdWxhdGUgc29tZSBDb25maWRlbmNlIGludGVydmFscyBmb3IgdGhlIGRhdGEgYW5kIHdlIHdpbGwgYWxzbyB1c2UgdGhlIGZlYXR1cmVzIG9mIHRoZSBkYXRhIHRvIGNhbGN1bGF0ZSB0aGVtIA0KDQoNCmBgYHtyfQ0KZmluZENJIDwtIGZ1bmN0aW9uKHgsIGFscGhhKXsNCiAgDQogICNzZXQgdXAgdmFyaWFibGVzDQogIHhiYXIgPC0gbWVhbih4KQ0KICBzaWdtYSA8LSBzZCh4KQ0KICBuIDwtIGxlbmd0aCh4KQ0KICBzZSA8LSBzaWdtYS9zcXJ0KG4pDQogIA0KICANCiAgI2NhbGN1bGF0ZSB0DQogIHRuXzFfYWxwaGEuMiA8LSBxdCgxIC0gYWxwaGEvMiwgZGYgPSBuIC0gMSkNCiAgDQogICNyZXR1cm4gdGhlIENJDQogIA0KICBDSSA8LSAgYyh4YmFyLXRuXzFfYWxwaGEuMipzZSx4YmFyK3RuXzFfYWxwaGEuMipzZSkNCiAgcmV0dXJuIChDSSkNCiAgDQp9IA0KDQpgYGANClRoaXMgdGFrZXMgYSByZWplY3Rpb24gY3JpdGVyaWEgKCBpbiBkZWNpbWFsICkgYW5kIGEgZGF0YSBzZXQgYW5kIGNhbGN1bGF0ZXMgYSBjb25maWRlbmNlIGludGVydmFsIGZvciB0aGUgdGVzdCBzdGF0aXN0aWMgdG8gZmFsbCBpbnNpZGUgaW4gb3JkZXIgZm9yIHRoZSBudWxsIGh5cG90aGVzaXMgdG8gYmUgYWNjZXB0ZWQuDQpgYGB7cn0NCmZpbmRDSSh5b3VuZ1JlYWN0aW9uLDAuMDUpDQpgYGANCg0KV2Ugd291bGQgbGlrZSBvdXIgbnVsbCBoeXBvdGhlc2lzIHRvIGZhbGwgd2l0aGluIHRoaXMgcmVnaW9uIGlmIHdlIGFyZSB0byBhY2NlcHQgdGhlIG51bGwgaHlwb3RoZXNpcw0KYGBge3J9DQpDSXRlc3QgPC0gZnVuY3Rpb24gKCBoMCwgeCwgYWxwaGEpew0KICBDSSA8LSBmaW5kQ0koIHgsIGFscGhhKQ0KICBpZihoMCA8IENJWzFdKXsNCiAgICBhbnM8LSIgSDAgcmVqZWN0ZWQiDQogICAgcmVhc29uIDwtcGFzdGUoaDAsICJmYWxscyBiZWxvdyB0aGUgbG93ZXIgbGltaXQgb2Ygb3VyIiwgKDEtYWxwaGEpKjEwMCwgIiUgY29uZmlkZW5jZSBpbnRlcnZhbCB3aGljaCBpcyBmb3VuZCB0byBiZTogIiAsQ0lbMV0sIHNlcD0iICIpDQogIH0NCiAgZWxzZSBpZiAoaDA+Q0lbMl0pew0KICAgIA0KICAgIGFuczwtIkgwIHJlamVjdGVkIg0KICAgIHJlYXNvbiA8LXBhc3RlKGgwLCAiZmFsbHMgYWJvdmUgdGhlIHVwcGVyIGxpbWl0IG9mIG91ciIsICgxLWFscGhhKSoxMDAsICIlIGNvbmZpZGVuY2UgaW50ZXJ2YWwgd2hpY2ggaXMgZm91bmQgdG8gYmU6ICIgLENJWzJdLCBzZXA9IiAiKQ0KICB9DQoNCiAgZWxzZXsNCiAgICBhbnMgPC0gIkgwIE5vdCByZWplY3RlZCINCiAgICByZWFzb24gPC1wYXN0ZShoMCwgImZhbGxzIGJldHdlZW4gdGhlIGxvd2VyIGFuZCB1cHBlciBsaW1pdCBvZiBvdXIiLCAoMS1hbHBoYSkqMTAwLCAiJSBjb25maWRlbmNlIGludGVydmFsIHdoaWNoIGlzIGZvdW5kIHRvIGJlIGJldHdlZW4gOiAiICxDSVsxXSwiYW5kIixDSVsyXSwgc2VwPSIgIikNCiAgfQ0KICANCnJldHVybihwYXN0ZShhbnMsIHJlYXNvbiwgc2VwPSIgOiAiKSkgIA0KfQ0KYGBgDQpMZXRzIGNhbGwgdGhpcyBmdW5jdGlvbiB0byB0ZXN0IHdoYXQgd2UgY2FuIGFscmVhZHkgc2VlIChoMDogJG11JD0gNDUwIGRvZXMgbm90IGZpdCBpbiBvdXIgY29uZmlkZW5jZSBpbnRlcnZhbCkNCmBgYHtyfQ0KDQoNCmBgYA0KDQpgYGB7cn0NCkNJdGVzdCg0NTAsIHlvdW5nUmVhY3Rpb24sIDAuMDUpDQpgYGANCg0KDQpTbyBmb3IgdGhpcyB0ZXN0IHdlIHJlamVjdCB0aGUgbnVsbCBoeXBvdGhlc2lzLiBCZWNhdXNlIGgwIGZhbGxzIG91dHNpZGUgdGhlIHVwcGVyIGJvdW5kYXJ5IG9mIHRoZSB0aGUgQ29uZmlkZW5jZSBJbnRlcnZhbCB3ZSBjYW4gZGVkdWNlIHRoYXQgdGhlIHJlYWwgbWVhbiBpcyBsb3dlciB0aGFuIDQ1MC4gDQpgYGB7cn0NCnQudGVzdCh5b3VuZ1JlYWN0aW9uLCBtdT00NTAsIGNvbmYubGV2ZWwgPSAuOTUpDQoNCmBgYA0KVGhpcyBidWlsdCBpbiBmdW5jdGlvbiBnaXZlcyBzb21lIG1vcmUgaW5mb3JtYXRpb24sIG1vc3Qgbm90YWJseSB0aGUgcC12YWx1ZSB3aGljaCBoZXJlIGlzIHNpZ25pZmljYW50bHkgbGVzcyB0aGFuIHRoZSAkdHthLzJ9LHtuLTF9JCBzbyBvbmNlIGFnYWluIHdlIGNhbiByZWplY3QgdGhlIG51bGwgaHlwb3RoZXNpcw0KbGV0cyBncmFwaCB0aGlzIG9uIHRoZSBoaXN0b2dyYW0gb2YgYWdlcyAoIG1lYW5zIG1vcmUgZnVuY3Rpb25zLikNCg0KYGBge3J9DQpoaXN0Q0kgPC0gZnVuY3Rpb24oZGF0YSwgYWxwaGEsIHRpdGxlPU5VTEwsIHhsYWI9TlVMTCwgeWxhYj1OVUxMLCBoMCkgew0KICB5bWF4PW1heChoaXN0KGRhdGEsIHBsb3Q9RkFMU0UpJGNvdW50cykNCiAgaGlzdChkYXRhLCBjb2w9cmdiKDAsMCwxLGFscGhhPS4yNSksIA0KICAgICAgIG1haW49cGFzdGUodGl0bGUsICIgd2l0aCAiLCAoKDEtYWxwaGEpKjEwMCksICIgQ29uZmlkZW5jZSBJbnRlcnZhbCIpLCB4bGFiPXhsYWIsIHlsYWI9eWxhYiwgIHlsaW09YygwLDEuMSp5bWF4KSkgDQogIGFibGluZSh2PWgwLCBjb2w9ICdibHVlJykNCiAgdGV4dChoMCwgeW1heCwgImgwOiBtZWFuPTQ1MG1zIiAsIGNvbCA9ICJibHVlIiwgY2V4PTAuNSkgDQogIENJIDwtIGZpbmRDSShkYXRhLGFscGhhKQ0KICBDSW1pbiA8LSBDSVsxXQ0KICBDSW1heCA8LSBDSVsyXQ0KICBhYmxpbmUodj1DSW1pbiwgY29sPSAncmVkJykNCiAgdGV4dChDSW1pbiwgeW1heC01LCBwYXN0ZSgiQ0kgbWluOiIsIHJvdW5kKENJbWluLCBkaWdpdHM9MiksICIgbXMiKSAsIGNvbCA9ICJyZWQiLCBjZXg9MC41KSANCiAgYWJsaW5lKHY9Q0ltYXgsIGNvbD0gJ3JlZCcpDQogIHRleHQoQ0ltYXgsIHltYXgtMi41LCBwYXN0ZSgiQ0kgbWF4OiIsIHJvdW5kKENJbWF4LCBkaWdpdHM9MiksICIgbXMiKSAsIGNvbCA9ICJyZWQiLCBjZXg9MC41KSANCn0NCmBgYA0KDQpgYGB7cn0NCmhpc3RDSSh5b3VuZ1JlYWN0aW9uLCAwLjA1LCAiWW91bmcgcmVhY3Rpb24gdGltZXMiLCAiUmVhY3Rpb24gVGltZXMgKG1zKSIsICJjb3VudHMiLCA0NTApDQpgYGANCg0KDQpUaGlzIHNob3dzIHRoYXQgdGhlIGh5cG90aGVzaXNlZCBtZWFuIGZhbGxzICgganVzdCkgIG91dHNpZGUgdGhlIGNvbmZpZGVuY2UgaW50ZXJ2YWxzIG9mIHRoZSBkaXN0cmlidXRpb24NCioqYi4gNDAgLTcwIGFnZSBncm91cCoqDQpXZSBqdXN0IHJlcGVhdCB0aGlzIGZvciB0aGUgb2xkZXIgYWdlIGdyb3VwDQoNCmBgYHtyfQ0KQ0l0ZXN0KDQ1MCwgb2xkZXJSZWFjdGlvbiwgMC4wNSkNCmBgYA0KDQpgYGB7cn0NCmhpc3RDSShvbGRlclJlYWN0aW9uLCAwLjA1LCAiT2xkZXIgcmVhY3Rpb24gdGltZXMiLCAiUmVhY3Rpb24gVGltZXMgKG1zKSIsICJjb3VudHMiLCA0NTApDQpgYGANCkhlcmUgd2Ugc2VlIHRoYXQgdGhhdCB0aGUgQ29uZmlkZW5jZSBpbnRlcnZhbCBpcyBoaWdoZXIgZm9yIHRoZSBzYW1wbGUgZ3JvdXAsIHNvIHRoYXQgb3VyIG1lYW4gb2YgNDUwIGlzIHRvbyBsb3cgZm9yIHRoZSBzYW1wbGUuIExldHMgY2hlY2sgdGhlIFR0ZXN0IGZ1bmN0aW9uIHRvIGJlIHN1cmUNCg0KYGBge3J9DQp0LnRlc3Qob2xkZXJSZWFjdGlvbiwgbXU9NDUwKQ0KYGBgDQoNCkFnYWluIGlmIHdlIGxvb2sgYXQgdGhlIHAtdmFsdWUgaXQgaXMgZmFyIGJlbG93IHRoZSAxLjk2IG5lZWRlZCB0byBhY2NlcHQgdGhlIG51bGwgaHlwb3RoZXNpcw0KDQpTTyBvbmNlIGFnYWluIHdlIHJlamVjdCB0aGUgTnVsbCBoeXBvdGhlc2lzIHRoYXQgJHttdX09IDQ1MG1zJA0KDQoNCioqTGluZWFyIHJlZ3Jlc3Npb24gYW5hbHlzaXMqKg0KKmEuUGxvdCBhIHNjYXR0ZXJwbG90IG9mIHRoZSByZWFjdGlvbiB0aW1lIGRhdGEgKHktYXhpcykgYXMgYSBmdW5jdGlvbiBvZiBhZ2UoeC1heGlzKS4gQ29uZHVjdCBhIGxpbmVhciByZWdyZXNzaW9uIGFuYWx5c2lzIG9mIHJlYWN0aW9uIHRpbWUgYXMgYSBmdW5jdGlvbmFnZSBhbmQgaW50ZXJwcmV0IHRoZSByZXN1bHRzLioNCg0KRmlyc3QgbGV0cyBwbG90IHRoZSBkYXRhDQpgYGB7cn0NCnNjYXR0ZXIuc21vb3RoKHg9QWdlLCB5PVJlYWN0aW9uVGltZSwgbWFpbj0iUmVhY3Rpb24gVGltZSB+IEFnZSIpICAjIHNjYXR0ZXJwbG90DQpgYGANCg0KDQoNCkxldHMgc2VlIGlmIHdlIGNhbiBjbGVhbiB0aGlzIHVwIGZpcnN0DQoNCmBgYHtyfQ0KcGFyKG1mcm93PWMoMSwyKSkNCmJveHBsb3QoQWdlLCBtYWluPSJBZ2UiLCBzdWI9cGFzdGUoIk91dGxpZXIgcm93czogIiwgYm94cGxvdC5zdGF0cyhBZ2UpJG91dCkpICAjIGJveCBwbG90IGZvciAnQWdlJw0KYm94cGxvdChSZWFjdGlvblRpbWUsIG1haW49IlJlYWN0aW9uIFRpbWUiLCBzdWI9cGFzdGUoIk91dGxpZXIgcm93czogIiwgYm94cGxvdC5zdGF0cyhSZWFjdGlvblRpbWUpJG91dCkpICAjIGJveCBwbG90IGZvciAnUmVhY3Rpb24nDQpgYGANCkZyb20gdGhlc2UgdHdvIGJveCBwbG90cyB3ZSBzZWUgdGhhdCB0aGVyZSBhcmUgbm8gb3V0bGllcnMgYWZmZWN0aW5nIHRoZSBncmFwaCBzbyB0aGVyZSBpcyBub3QgYSBsb3Qgb2YgY2xlYW4gdXAgYXZhaWxhYmxlIGZvciB0aGlzIHBhcnRpY3VsYXIgZGF0YQ0KDQoNCk9uIGl0cyBvd24gdGhpcyBnaXZlcyBzb21lIGluZm9ybWF0aW9uIGJ1dCBpdHMgcXVpdGUgc3BhcnNlLldlIHNlZSBhIGxpbmUgc3VnZ2VzdGluZyB0aGF0IHRoZSBoaWdoZXIgdGhlIGFnZSB0aGUgaGlnaGVyIHRoZSByZWFjdGlvbiB0aW1lIGJ1dCB3ZSBoYXZlIG5vIG51bWJlcnMgZm9yIHRoaXMgb3IgaW5kZWVkIGhvdyBjbG9zZSB0aGVzZSBwb2ludHMgY29tZSB0byB0aGUgbGluZS4gTGV0cyB0cnkgdG8gZml4IHRoYXQgDQoNCkZpcnN0IHRoZSByZWdyZXNzaW9uIGxpbmUuIElkZWFsbHkgd2Ugd2FudCBpdCB0byBiZSBvZiB0aGUgZm9ybSAoIGluIHRoaXMgc2ltcGxlIGNhc2UpIA0KJCQgcmVzcG9uc2U9IHtcYmV0YX17XzB9K3tcYmV0YX17XzF9SW5kaWNhdG9ye18xfSQkDQpvciANCiQkeT1jK214JCQNCndoZXJlIG0gaXMgdGhlIHNsb3BlIG9mIHRoZSBsaW5lIGFuZCBjIHRoZSBpbnRlcmNlcHQuIA0KYGBge3J9DQoNCmxpbmVhclJlZyA8LSBsbShSZWFjdGlvblRpbWUgfiBBZ2UsIGRhdGEpDQpSZWdzdW1tYXJ5PC1zdW1tYXJ5KGxpbmVhclJlZykNClJlZ3N1bW1hcnkNCg0KYGBgDQoNClRha2luZyB0aGUgZGF0YSB3ZSBuZWVkIHdlIG5vdyB3ZSBjYW4gc2VlIHRoYXQgdGhlIGxpbmUgd2Ugd2FudCBpcyANCiQkUmVhY3Rpb24gVGltZSA9IDIyNi4xMzExICsgNS43OTkwICogYWdlJCQNClRoZSBuZXh0IHRoaW5nIHdlIHdhbnQgdG8gY2FsY3VsYXRlIGlzIGhvdyBtdWNoIG9mIHRoaXMgcmVzdWx0cyBpcyBleHBsYWluZWQgYnkgdGhlIG1vZGVsLiBGb3IgdGhpcyB3ZSB1c2UgdGhlICRSIF57Mn0kIHZhbHVlIG9mIC4zNzAzIHdoaWNoIHN1Z2dlc3RzIHRoYXQgKiozNyUqKiBvZiB0aGUgcmVhY3Rpb24gdGltZSBpcyByZWxhdGVkIHRvIGFnZS4gDQoNCkZpbmFsbHkgbGV0cyBjb25zaWRlIHRoZSBlcnJvciBvZiB0aGUgc2xvcGUgYW5kIHdvcmsgb3V0IGEgY29uZmlkZW5jZSBpbnRlcnZhbCBmb3IgdGhlIHNsb3BlIG9mIHRoZSByZWdyZXNzaW9uIGxpbmUuDQpUaGUgc3RhbmRhcmQgZXJyb3IgZm9yIHRoZSBzbG9wZSBvZiB0aGlzIGxpbmUgaXMgZ2l2ZSBhcyAqKjAuNzUzNSoqIGFuZCB3ZSBra25vdyB0aGF0IHRoZSBzbG9wZSBpcyAqKjUuNzcwKiogc28gYSA5NSUgY29uZmlkZW5jZSBpbnRlcnZhbCBmb3IgdGhpcyBpcyANCg0KYGBge3J9DQpnZXRDSSA8LSBmdW5jdGlvbih4YmFyLCBzZSwgbiwgYWxwaGEpew0KICB0PXF0KDEtKGFscGhhLzIpLCBkZj1uLTIpDQogIG1lPXQqc2UNCiAgQ0k9Yyh4YmFyLW1lLCB4YmFyK3NlKQ0KICByZXR1cm4oQ0kpDQp9DQpgYGANCg0KDQpgYGB7cn0NCkNJc2xvcGU8LWdldENJKFJlZ3N1bW1hcnkkY29lZmZpY2llbnRzWzJdLCBSZWdzdW1tYXJ5JGNvZWZmaWNpZW50c1s0XSwgMTAwLCAwLjA1KQ0KQ0lzbG9wZQ0KYGBgDQoNCnNvIHdlIGhhdmUgMiBzbG9wZXMgZm9yIG91ciBsaW5lIGFuZCBjYW4gZG8gdGhlIHNhbWUgZm9yIHRoZSBpbnRlcmNlcHQNCmBgYHtyfQ0KQ0lpbnRlcmNlcHQ8LWdldENJKFJlZ3N1bW1hcnkkY29lZmZpY2llbnRzWzFdLCBSZWdzdW1tYXJ5JGNvZWZmaWNpZW50c1syXSwgMTAwLCAwLjA1KQ0KQ0lpbnRlcmNlcHQNCmBgYA0KDQpMZXRzIHVzZSBhbGwgdGhpcyBpbmZvcm1hdGlvbiB0byBzaG93IGEgYmV0dGVyIHBsb3Qgb2YgdGhlIGRhdGEgd2UgbG9va2VkIGF0IGVhcmxpZXIoIGFub3RoZXIgZnVuY3Rpb24gKQ0KDQpgYGB7cn0NCnBsb3RyZWdyZXNzaW9uPC0gZnVuY3Rpb24oWCwgWSwgdGl0bGUsIHhsYWIsIHlsYWIsIGMsY2xvdywgY2hpZ2gsIG1fYmFyLCBtbG93LCBtaGlnaCwgQ0kpDQp7DQpwbG90KFgsIFksIG1haW49dGl0bGUsIHhsYWI9eGxhYiwgeWxhYj15bGFiKQ0KDQphYmxpbmUoYywgbV9iYXIgLCBjb2w9ImJsYWNrIikNCmFibGluZShjbG93LCBtbG93LCBjb2w9ICdyZWQnKQ0KYWJsaW5lKGNoaWdoLCBtaGlnaCwgY29sPSAncmVkJykNCg0KbGluZTwtIHBhc3RlKHlsYWIsICIgPSAiLCByb3VuZChjLCBkaWdpdHM9MiksICIgKyAiLHJvdW5kKG1fYmFyLCBkaWdpdHM9IDIpLCAiICogIiwgeGxhYiApDQpjb25maWRlbmNlPC0gcGFzdGUoQ0ksICIlIGNvbmRmaWRlbmNlIGludGVydmFsIGZvciB0aGUgUmVncmVzc2lvbiBMaW5lIikNCmtleSA8LWMobGluZSwgY29uZmlkZW5jZSApDQpjb2xvcjwtIGMoImJsYWNrIiwgInJlZCIpDQpsZWdlbmQoInRvcGxlZnQiLCBrZXksIGZpbGwgPSBjb2xvciwgY2V4PTAuNSkNCn0NCmBgYA0KYGBge3J9DQpwbG90cmVncmVzc2lvbihBZ2UsIFJlYWN0aW9uVGltZSwNCiAgICAgICAgICAgICAgICJQbG90IG9mIFJlYWN0aW9uIFRpbWUgLVYtIEFnZSAvdyByZWdyZXNzaW9uIGxpbmVzIiwNCiAgICAgICAgICAgICAgICJBZ2UoeWVhcnMpIiwgIlJlYWN0aW9uIFRpbWUobXMpIiwNCiAgICAgICAgICAgICAgIFJlZ3N1bW1hcnkkY29lZmZpY2llbnRzWzFdLCBDSWludGVyY2VwdFsxXSwgQ0lpbnRlcmNlcHRbMl0sDQogICAgICAgICAgICAgICBSZWdzdW1tYXJ5JGNvZWZmaWNpZW50c1syXSwgQ0lzbG9wZVsxXSwgQ0lzbG9wZVsyXSwgOTUNCiAgICAgICAgICAgICAgICkNCmBgYA0KDQoNCipiLlBsb3QgYSBzY2F0dGVycGxvdCBvZiB0aGUgcmVhY3Rpb24gdGltZSBkYXRhICh5LWF4aXMpIGFzIGEgZnVuY3Rpb24gb2YgaGVpZ2h0KHgtYXhpcykuIENvbmR1Y3QgYSBsaW5lYXIgcmVncmVzc2lvbiBhbmFseXNpcyBvZiByZWFjdGlvbiB0aW1lIGFzIGEgZnVuY3Rpb24gb2YgaGVpZ2h0IGFuZCBpbnRlcnByZXQgdGhlIHJlc3VsdHMuKg0KRmlyc3QgbGV0cyBwbG90IHRoZSBkYXRhDQpgYGB7cn0NCnNjYXR0ZXIuc21vb3RoKHg9SGVpZ2h0LCB5PVJlYWN0aW9uVGltZSwgbWFpbj0iUmVhY3Rpb24gVGltZSB+IEhlaWdodCIpICAjIHNjYXR0ZXJwbG90DQpgYGANCkhlcmUgd2Ugc2VlIGEgZmFyIGZsYXR0ZXIgZ3JhcGggKCBhZ2FpbiBubyBpbmZvIHByb3ZpZGVkKSBhbmQgbm8gaW5kaWNhdGlvbiB0aGVyZSBpcyBhbnkgdHJlbmRzIGJhciBhIHJlZHVjdGlvbiBpbiByZWFjdGlvbiB0aW1lcyBhdCB0aGUgdmVyeSB0YWxsZXN0IGVuZCBvZiB0aGUgZ3JhcGgNCg0KTGV0cyBsb29rIGZvciBvdXRsaWVycw0KDQpgYGB7cn0NCnBhcihtZnJvdz1jKDEsMikpDQpib3hwbG90KEhlaWdodCwgbWFpbj0iSGVpZ2h0Iiwgc3ViPXBhc3RlKCJPdXRsaWVyIHJvd3M6ICIsIGJveHBsb3Quc3RhdHMoQWdlKSRvdXQpKSAgIyBib3ggcGxvdCBmb3IgJ0FnZScNCmJveHBsb3QoUmVhY3Rpb25UaW1lLCBtYWluPSJSZWFjdGlvbiBUaW1lIiwgc3ViPXBhc3RlKCJPdXRsaWVyIHJvd3M6ICIsIGJveHBsb3Quc3RhdHMoUmVhY3Rpb25UaW1lKSRvdXQpKSAgIyBib3ggcGxvdCBmb3IgJ1JlYWN0aW9uJw0KYGBgDQpBZ2FpbiBubyBodWdlIG91dGxpZXJzIHdoaWNoIGlzIGdvb2QgKCBkYXRhIGlzICdjbGVhbicgKSBzbyBsZXRzIG1vdmUgb24gd2l0aCB0aGUgcmVncmVzc2lvbiBhbmFseXNpcw0KYGBge3J9DQpsaW5lYXJSZWcgPC0gbG0oUmVhY3Rpb25UaW1lIH4gSGVpZ2h0LCBkYXRhKQ0KUmVnc3VtbWFyeTwtc3VtbWFyeShsaW5lYXJSZWcpDQpSZWdzdW1tYXJ5DQpgYGANCkhlcmUgd2UgZ2V0IHRoZSBMaW5lYXIgcmVncmVzc2lvbiBmb3JtdWxhIHRvIGJlDQokJFJlYWN0aW9uIFRpbWUobXMpID0gNjg3LjQzOSAtIDEuMTczKkhlaWdodCggaW4gY20pICQkDQphbmQgdGhlICRSXnsyfSQgdmFsdWUgZm9yIHRoaXMgaXMgMC4wMDUzNTggd2hpY2ggdHJhbnNsYXRlcyB0byAqKjAuNSUqKiBvZiB0aGUgcmVhY3Rpb24gdGltZSBleHBsYWluZWQgYnkgdGhlIGhlaWdodCwgc28gaXQgd291bGQgYmUgZmFpciB0byBzYXkgdGhhdCBIZWlnaHQgaGFzIG1pbmlzY3VsZSBpbmZsdWVuY2Ugb24gdGhlIHJlYWN0aW9uIHRpbWUNCg0KDQpMZXRzIGRvIENvbmZpZGVuY2UgaW50ZXJ2YWxzIGFuZCB0aGUgcHJldHRpZWQgdXAgcmVncmVzc2lvbiBsaW5lKCBmb3Igd2hhdCBpdHMgd29ydGgpDQoNCmBgYHtyfQ0KQ0lzbG9wZTwtZ2V0Q0koUmVnc3VtbWFyeSRjb2VmZmljaWVudHNbMl0sIFJlZ3N1bW1hcnkkY29lZmZpY2llbnRzWzRdLCAxMDAsIDAuMDUpDQoNCg0KQ0lpbnRlcmNlcHQ8LWdldENJKFJlZ3N1bW1hcnkkY29lZmZpY2llbnRzWzFdLCBSZWdzdW1tYXJ5JGNvZWZmaWNpZW50c1szXSwgMTAwLCAwLjA1KQ0KDQoNCnBsb3RyZWdyZXNzaW9uKEFnZSwgUmVhY3Rpb25UaW1lLA0KICAgICAgICAgICAgICAgIlBsb3Qgb2YgUmVhY3Rpb24gVGltZSAtVi0gSGVpZ2h0IC93IHJlZ3Jlc3Npb24gbGluZXMiLA0KICAgICAgICAgICAgICAgIkhlaWdodChjbSkiLCAiUmVhY3Rpb24gVGltZShtcykiLA0KICAgICAgICAgICAgICAgUmVnc3VtbWFyeSRjb2VmZmljaWVudHNbMV0sIENJaW50ZXJjZXB0WzFdLCBDSWludGVyY2VwdFsyXSwNCiAgICAgICAgICAgICAgIFJlZ3N1bW1hcnkkY29lZmZpY2llbnRzWzJdLCBDSXNsb3BlWzFdLCBDSXNsb3BlWzJdLCA5NSkNCmBgYA0KVG8gc2VlIHdoeSB0aGUgQ0kgbGluZXMgZG9udCBzaG93IG9uIHRoZSBncmFwaCBsZXRzIGxvb2sgYXQgdGhlIENJIGRhdGEgZm9yIHRoZSBpbnRlcmNlcHQNCmBgYHtyfQ0KDQpDSWludGVyY2VwdA0KYGBgDQpTbyB3ZSBzZWUgdGhhdCB0aGlzIGRvZXMgbm90IGFjdHVhbGx5IGZpdCBvbnRvIHRoZSBncmFwaCBhdCB0aGUgc2NhbGUgd2UgYXJlIGF0IHNob3dpbmcgaG93IGxpdHRsZSB2YWx1ZSB0aGlzIHJlZ3Jlc3Npb24gYnJpbmdzIHRvIHRoZSBkYXRhDQoNCioqTG9naXN0aWMgcmVncmVzc2lvbiBhbmFseXNpczoqKg0KKmEuQ29uZHVjdCBhIExvZ2lzdGljIHJlZ3Jlc3Npb24gYW5hbHlzaXMgb2YgYWdlIGdyb3VwIGFzIGEgZnVuY3Rpb24gb2YgcmVhY3Rpb24gdGltZSBhbmQgaW50ZXJwcmV0IHRoZSByZXN1bHRzLioNCkxldHMgcnVuIHRoZSByZWdyZXNzaW9uIGZpcnN0IA0KDQpgYGB7cn0NCmxvZ2lzdGljPC0gZ2xtKGZvcm11bGE9QWdlR3JvdXB+UmVhY3Rpb25UaW1lLCBmYW1pbHk9Ymlub21pYWwoImxvZ2l0IiksIGRhdGEgICkNCnN1bW1hcnkobG9naXN0aWMpDQpgYGANCkZyb20gdGhpcyB3ZSBzZWUgdGhhdCB0aGUgbG9naXN0aWMgcmVncmVzc2lvbiB0YWtlcyB0aGUgZm9ybSBvZg0KJCRQcihBZ2UgR3JvdXAgNDAtNzB8UmVhY3Rpb24gdGltZSk9IFxmcmFje2Vee1xldGFfe2l9fX17MStlXntcZXRhX3tpfX0gfSQkDQp3aGVyZQ0KJCRcZXRhX3tpfT0tNC4xNTY5ODkrMC4wMDk5NTMqUmVhY3Rpb24gVGltZSQkDQoNCmFuZCB0aGUgb2RkcyBvbiBiZWluZyBpbiB0aGUgdGhlNDAtNzAgYWdlIGdyb3VwIGdpdmVuIGEgcmVhY3Rpb24gdGltZSBhcmUNCiQkZV57XGV0YV97aX19OjEkJA0KV2l0aCBldGEgYXMgZGVzcmliZWQuDQoNCk5vdyB3ZSBjYW4gaW52ZXN0b2dhdGUgdGhlIHNpZ25pZmljYW5jZSBvZiB0aGlzIG1vZGVsIGJ5IGludmVzdGlnYXRpbmcgaWYgYSAgOTUlIENvbmZpZGVuY2UgaW50ZXJ2YWwgaGF2ZSB2YWx1ZXMgZWl0aGVyIHNpZGUgb2YgemVybw0KDQpgYGB7cn0NCkNJaW50ZXJjZXB0PC1nZXRDSSg0LjE1Njk4OSwxLjAxNDg0OSwgMTAwLCAwLjA1KQ0KQ0lzbG9wZTwtIGdldENJKCAwLjAwOTk1MiwwLjAwMjE0NSwgMTAwLCAwLjA1KQ0KDQpDSWludGVyY2VwdA0KQ0lzbG9wZQ0KYGBgDQpBcyB3ZSBzZWUgb2ZyIGVhY2ggb2YgdGhlIHZhbHVlcyBpbiB0aGUgQ0kgYXJlIHBvc2l0aXZlIHNvIHdlIGNhbiBzYXkgdGhhdCB0aGlzIG1vZGVsIGlzIHNpZ25pZmljYW50LiANCg0KRmluYWxseSBsZXRzIHBsb3QgdGhlIHJlZ3Jlc3Npb24NCmBgYHtyfQ0KICMgYXNzaWduICdzdXJ2aXZhbCcgdG8gdGhlc2UgMjAgaW5kaXZpZHVhbHMgbm9uLXJhbmRvbWx5Li4uIG1vc3QgbW9ydGFsaXR5IG9jY3VycyBhdCBzbWFsbGVyIGJvZHkgc2l6ZQ0KZGF0PWFzLmRhdGEuZnJhbWUoY2JpbmQoUmVhY3Rpb25UaW1lLEFnZUdyb3VwKSkgIyBzYXZlcyBkYXRhZnJhbWUgd2l0aCB0d28gY29sdW1uczogYm9keSBzaXplICYgc3Vydml2YWwNCmRhdCRBZ2VHcm91cD1kYXQkQWdlR3JvdXAtMSAjIGp1c3QgDQoNCnBsb3QoZGF0JFJlYWN0aW9uVGltZSxkYXQkQWdlR3JvdXAseGxhYj0iUmVhY3Rpb24gVGltZSIseWxhYj0iUHJvYmFiaWxpdHkgYWdlIGJldHdlZW4gNDAtNzAiLCBtYWluPSdMb2dpc3RpYyByZWdyZXNzaW9uIGZvciBBZ2UgR3JvdXAgViBSZWFjdGlvbiBUaW1lJykgIyBwbG90IHdpdGggYm9keSBzaXplIG9uIHgtYXhpcyBhbmQgc3Vydml2YWwgKDAgb3IgMSkgb24geS1heGlzDQoNCmN1cnZlKHByZWRpY3QobG9naXN0aWMsZGF0YS5mcmFtZShSZWFjdGlvblRpbWU9eCksdHlwZT0icmVzcCIpLGFkZD1UUlVFLCBjb2w9InJlZCIpICMgZHJhd3MgYSBjdXJ2ZSBiYXNlZCBvbiBwcmVkaWN0aW9uIGZyb20gbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbA0KDQprZXk8LWMoJ1ZhbHVlIG9mIFJlYWN0aW9uIFRpbWUgYW5kIEFnZSBncm91cCcsICdSZWdyZXNzaW9uIGxpbmUgb2YgcHJvYmFiaWxpdHknKQ0KY29sPC1jKCdibGFjaycsICdyZWQnKQ0KDQpsZWdlbmQoNjAwLCAwLjQsIGtleSwgZmlsbD1jb2wsY2V4PTAuNSApDQpgYGANCkFzIHdlIHNlZSBoZXJlIHRoZSBncmFwaCBpcyBvZiBhIHR5cGljYWwgbG9naXN0aWMgcmVncmVzc2lvbiBzaGFwZSBzbyB3ZSBjYW4gc3VtbWFyaXNlIHRoYXQgb3VyIG1vZGVscyBmaXRzIHRoZSBkYXRhIHF1aXRlIHdlbGwNCg0KKmIuQ29uZHVjdCBhIExvZ2lzdGljIHJlZ3Jlc3Npb24gYW5hbHlzaXMgb2YgYWdlIGdyb3VwIGFzIGEgZnVuY3Rpb24gb2YgaGVpZ2h0IGFuZCBpbnRlcnByZXQgdGhlIHJlc3VsdHMuKg0KDQpgYGB7cn0NCmxvZ2lzdGljPC0gZ2xtKGZvcm11bGE9QWdlR3JvdXB+SGVpZ2h0LCBmYW1pbHk9Ymlub21pYWwoImxvZ2l0IiksIGRhdGEgICkNCnN1bW1hcnkobG9naXN0aWMpDQpgYGANCkZyb20gdGhpcyB3ZSBzZWUgdGhhdCB0aGUgbG9naXN0aWMgcmVncmVzc2lvbiB0YWtlcyB0aGUgZm9ybSBvZg0KJCRQcihBZ2UgR3JvdXAgNDAtNzB8SGVpZ2h0KT0gXGZyYWN7ZV57XGV0YV97aX19fXsxK2Vee1xldGFfe2l9fSB9JCQNCg0Kd2hlcmUNCiQkXGV0YV97aX09NC4yNDgzMC0wLjAyMjA0KkhlaWdodCQkDQoNCmFuZCB0aGUgb2RkcyBvbiBiZWluZyBpbiB0aGUgdGhlNDAtNzAgYWdlIGdyb3VwIGdpdmVuIGEgcmVhY3Rpb24gdGltZSBhcmUNCg0KJCRlXntcZXRhX3tpfX06MSQkDQoNCldpdGggZXRhIGFzIGRlc3JpYmVkLg0KDQpOb3cgd2UgY2FuIGludmVzdG9nYXRlIHRoZSBzaWduaWZpY2FuY2Ugb2YgdGhpcyBtb2RlbCBieSBpbnZlc3RpZ2F0aW5nIGlmIGEgIDk1JSBDb25maWRlbmNlIGludGVydmFsIGhhdmUgdmFsdWVzIGVpdGhlciBzaWRlIG9mIHplcm8NCg0KYGBge3J9DQpDSWludGVyY2VwdDwtZ2V0Q0koIDQuMjQ4MzAsNC4wNTY3NyAsIDEwMCwgMC4wNSkNCkNJc2xvcGU8LSBnZXRDSSggLTAuMDIyMDQsMC4wMjQwMDUsIDEwMCwgMC4wNSkNCg0KQ0lpbnRlcmNlcHQNCkNJc2xvcGUNCmBgYA0KSW4gdGhpcyBjYXNlIHdlIHNlZSB0aGF0IHRoZSBDSSBjbGVhbHkgcGFzc2VzIG92ZXIgMCBpbiBib3RoIGNhc2VzIHNvIHdlIGNhbiBzYXkgdGhhdCB0aGUgbW9kZWwgaXMgbm90IHNpZ25pZmljYW50LiBXZSBjYW4gYWxzbyBwbG90IHRoaXMgcmVncmVzc2lvbiB0byBzZWUgd2hhdCBpdCB0ZWxscyB1cy4NCmBgYHtyfQ0KICMgYXNzaWduICdzdXJ2aXZhbCcgdG8gdGhlc2UgMjAgaW5kaXZpZHVhbHMgbm9uLXJhbmRvbWx5Li4uIG1vc3QgbW9ydGFsaXR5IG9jY3VycyBhdCBzbWFsbGVyIGJvZHkgc2l6ZQ0KZGF0PWFzLmRhdGEuZnJhbWUoY2JpbmQoSGVpZ2h0LEFnZUdyb3VwKSkgIyBzYXZlcyBkYXRhZnJhbWUgd2l0aCB0d28gY29sdW1uczogYm9keSBzaXplICYgc3Vydml2YWwNCmRhdCRBZ2VHcm91cD1kYXQkQWdlR3JvdXAtMSAjIGp1c3QgDQoNCnBsb3QoZGF0JEhlaWdodCxkYXQkQWdlR3JvdXAseGxhYj0iSGVpZ2h0Iix5bGFiPSJQcm9iYWJpbGl0eSBhZ2UgYmV0d2VlbiA0MC03MCIsIG1haW49J0xvZ2lzdGljIHJlZ3Jlc3Npb24gZm9yIEFnZSBHcm91cCBWIEhlaWdodCcpICMgcGxvdCB3aXRoIGJvZHkgc2l6ZSBvbiB4LWF4aXMgYW5kIHN1cnZpdmFsICgwIG9yIDEpIG9uIHktYXhpcw0KDQpjdXJ2ZShwcmVkaWN0KGxvZ2lzdGljLGRhdGEuZnJhbWUoSGVpZ2h0PXgpLHR5cGU9InJlc3AiKSxhZGQ9VFJVRSwgY29sPSJyZWQiKSAjIGRyYXdzIGEgY3VydmUgYmFzZWQgb24gcHJlZGljdGlvbiBmcm9tIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWwNCg0Ka2V5PC1jKCdWYWx1ZSBvZiBIZWlnaHQgYW5kIEFnZSBncm91cCcsICdSZWdyZXNzaW9uIGxpbmUgb2YgcHJvYmFiaWxpdHknKQ0KY29sPC1jKCdibGFjaycsICdyZWQnKQ0KDQpsZWdlbmQoMTgwLCAwLjQsIGtleSwgZmlsbD1jb2wsY2V4PTAuNSApDQpgYGANCg0KSGVyZSB3ZSBzZWUgdGhlIHJlZ3Jlc3Npb24gbGluZSBpcyBmbGF0dGVyIGFuZCBoYXMgbm8gcmVhbCB3YXkgdG8gcHJlZGljdCB0aGUgYWdlIGdyb3VwIGZyb20gdGhlIEhlaWdodCBnaXZlbiggYXMgd2UgYWxsdWRlZCB0byB3aXRoIHRoZSBjb25maWRlbmNlIGludGVydmFscyk=