The workhorse of statistical analysis is the linear model, particularly regression. Invented by Francis Galton to study relationship between parents and children described as regressing to the mean, it has become one of the most widely used modeling techniques. In this tutorial, we will focus on simple linear regression.
SIMPLE LINEAR REGRESSION
The simple linear regression determines the relationship between two variables. That is we are using one variable to tell us what we can expect from the other variable. The outcome variable, what we are trying to predict, is called the response variable, and the input variable, what we are using to predict, is called the predictor variable. The general idea of the simple linear regression is to use the predictor to come up with some average value of the response. The relationship is defined as: y = a + bx + E, where a is the intercept; and b is the slope; E is the error term; x is the predictor variable; and y is the outcome/response variable. The equation describes is essentially a straight line that goes through the data where a is the y-intercept and b is the slope. First, install the required packages for our analysis.
# install usingR and ggplot2 packages; packages already installed; loading them using library()
library(UsingR)
Loading required package: MASS Loading required package: HistData Loading required package: Hmisc Loading required package: lattice Loading required package: survival Loading required package: Formula Loading required package: ggplot2
Above is the list of items contained in UsingR package collection. To illustrate the simple linear regression, we use the fathers’ and sons’ data. We will also require ggplot2 for plotting.
# Require ggplot2 and UsingR
require(UsingR)
require(ggplot2)
EXPLORATORY ANALYSIS
Let’s check the first 10 and the last 10 observations for the fathers’ and sons’ data.
# The first 10 observation of our dataset using the print(head(data, n = 10)) function
print(head(father.son, n = 10))
# The last 10 observations of our dataset using print(tail(data, n = 10)) function
print(tail(father.son, n = 10))
The father.son data is Pearson’s data set on heights of fathers and their sons. The data set contains 1078 measurements of fathers’ heights and sons’ heights. Its class is a data.frame with 1078 observations on the following 2 variables: fheight for fathers’ heights in inches and sheight for sons’ heights in inches. This data set was used by Pearson to investigate regression. The above information can be found by just typing ?father.son in the R console. Alternatively, using the following commands can get you accustomed with the data set.
# Checking the class of the data set using class(dataset) function
class(father.son)
[1] "data.frame"
A data.frame is a collection of variables which share many of the properties of matrices and of lists, used as the fundamental data structure by most R’s modeling software.
# Checking the structure of the data set using str(dataset) function
str(father.son)
'data.frame': 1078 obs. of 2 variables:
$ fheight: num 65 63.3 65 65.8 61.1 ...
$ sheight: num 59.8 63.2 63.3 62.8 64.3 ...
The str(father.son) command tells us that our data set contains 1078 observations and 2 variables, which are numeric variables. Additionally, we can do a brief sommary of our data set using the summary() function.
#Summary statistics of father.son data set
summary(father.son)
fheight sheight
Min. :59.01 Min. :58.51
1st Qu.:65.79 1st Qu.:66.93
Median :67.77 Median :68.62
Mean :67.69 Mean :68.68
3rd Qu.:69.60 3rd Qu.:70.47
Max. :75.43 Max. :78.36
We can visualize the distribution of fathers’ heights by dividing the x axis into bins and counting the number of observations in each bin. Histogram, geom_histogram in the ggplot2 package, displays the count with bars, while geom_freqpoly displays the counts with lines. Frequency polygons are more suitable when you want to compare the distribution across the levels of a categorical variable.
# Histogram of father's height distribution
ggplot(data = father.son, mapping = aes(x = fheight)) +
geom_histogram(bins = 30, fill = "seagreen") +
ggtitle("Histogram of Father's Height") +
theme(plot.title = element_text(hjust = 0.5))

# Histogram of father's height using geom_freqpoly displaying the counts with lines
ggplot(data = father.son, mapping = aes(x = fheight)) +
geom_freqpoly(bins = 30) +
ggtitle("Histogram of Height Counts With Lines") +
theme(plot.title = element_text(hjust = 0.5))

We can also plot the histogram of the son’s height. We can apply the same technique as the one used in the previous graphs.
# Histogram of the son's height using the ggplot2 package
ggplot(data = father.son, mapping = aes(x = sheight)) +
geom_histogram(bins = 30, fill = "seagreen") +
ggtitle(expression(atop("Histogram of Son's Height", atop(italic("Heights by Count", ""))))) +
theme(plot.title = element_text(hjust = 0.5)) +
theme(plot.subtitle = element_text(hjust = 0.5))

We have added new elements, which are title and subtitle, by passing these arguments to ggtitle() function. Let’s apply the same technique to the line graph.
# Histogram of son's height using geom_freqpoly() function displaying counts by line.
ggplot(data = father.son, mapping = aes(x = sheight)) +
geom_freqpoly(bins = 30) +
ggtitle(expression(atop("Histogram of Son's Height", atop(italic("Height With Line Count", ""))))) +
theme(plot.title = element_text(hjust = 0.5)) +
theme(plot.subtitle = element_text(hjust = 0.5))

We can infer that both father’s height and son’s height seem to be normally distributed, according to the plotted histograms. We can extend our analysis by looking at how father’s height influences son’s height using simple linear regression.
SIMPLE LINEAR REGRESSION: USING FATHERS’HEIGHTS to DETERMINE SONS’ HEIGHTS
# Using fathers' heights to predict sons' heights with a geom_smooth () function
ggplot(data = father.son, mapping = aes(x = fheight, y = sheight)) +
geom_point() +
geom_smooth(method = "lm") +
labs(x = "Fathers", y = "Sons") +
ggtitle(expression(atop("Scatterplot of Fathers vs. Sons", atop(italic("With Fitted Regression Line", ""))))) +
theme(plot.title = element_text(hjust = 0.5)) +
theme(plot.subtitle = element_text(hjust = 0.5))

This plot is built using fathers’ heights to predict sons’ heights using simple linear regression. The fathers’ heights are the predictors and the sons’ heights are the responses. The blue line running through the points is the regression line and the grey band around it represents uncertainty in the fit.
To generate this nice plot, we pass the argument, (method = “lm”), to the geom_smooth() function. To make the results of the regression available to us, we need to calculate a regression using the function lm.
CALCULATE LINEAR REGRESSION USING THE lm()FUNCTION
# Calculate Linear regression using lm() function
(height.lm <- lm(sheight ~ fheight, data = father.son))
Call:
lm(formula = sheight ~ fheight, data = father.son)
Coefficients:
(Intercept) fheight
33.8866 0.5141
The above formula specifies to regress sheight, the response or outcome variable, on fheight, the predictor variable, using the father.son data, and adds the intercept term automatically. The results show coefficients for (intercept) and fheight which is the slope for fheight, the predictor variable. How can we then interpret these results? The interpretation of this is that, for every extra inch of father’s height, we expect an extra half inch increase in son’s height. The intercept doesn’t make sense in this case because it represents the height of a son whose father height is zero height, which is nonsensical. To get a full report of the regression model, just pass the height.lm model to the summary function.
# Complete regression results using summary() function
(summary(height.lm))
Call:
lm(formula = sheight ~ fheight, data = father.son)
Residuals:
Min 1Q Median 3Q Max
-8.8772 -1.5144 -0.0079 1.6285 8.9685
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 33.88660 1.83235 18.49 <2e-16 ***
fheight 0.51409 0.02705 19.01 <2e-16 ***
---
Signif. codes: 0 *** 0.001 ** 0.01 * 0.05 . 0.1 1
Residual standard error: 2.437 on 1076 degrees of freedom
Multiple R-squared: 0.2513, Adjusted R-squared: 0.2506
F-statistic: 361.2 on 1 and 1076 DF, p-value: < 2.2e-16
This prints out a lot of information about the model, including the standard error, the t-values and p-values for the coefficients, the degrees of freedom, residual summary statistics and the results of the F-test.
ANOVA AS AN ALTERNATIVE REGRESSION
An alternative to running an ANOVA test is to fit a regression with just one categorical variable and no intercept term. To run ANOVA, we will use the tips data in the reshape2 package on which we will fit a regression. To learn more about the reshape2 package, just type ?reshape2 in the R console.
# Pass tips and package reshape2 to the data() function
data(tips, package = "reshape2")
Let’s check the content of the dataset tips.
str(tips)
'data.frame': 244 obs. of 7 variables:
$ total_bill: num 17 10.3 21 23.7 24.6 ...
$ tip : num 1.01 1.66 3.5 3.31 3.61 4.71 2 3.12 1.96 3.23 ...
$ sex : Factor w/ 2 levels "Female","Male": 1 2 2 2 1 2 2 2 2 2 ...
$ smoker : Factor w/ 2 levels "No","Yes": 1 1 1 1 1 1 1 1 1 1 ...
$ day : Factor w/ 4 levels "Fri","Sat","Sun",..: 3 3 3 3 3 3 3 3 3 3 ...
$ time : Factor w/ 2 levels "Dinner","Lunch": 1 1 1 1 1 1 1 1 1 1 ...
$ size : int 2 3 3 2 4 4 2 4 2 2 ...
# Checking the first 10 observations
(print(head(tips, n = 10)))
# Checking the last 10 observations
(print(tail(tips, n = 10)))
We are going to fit our model with tip regresses against day minus the intercept. Tip is our outcome variable and day is our predictor variable. We will use aov() function to fit a model with a categorical variable without intercept.
# Pass arguments to aov() function for an ANOVA test
(tips.anova <- aov(tip ~ day - 1, data = tips))
Call:
aov(formula = tip ~ day - 1, data = tips)
Terms:
day Residuals
Sum of Squares 2203.0066 455.6866
Deg. of Freedom 4 240
Residual standard error: 1.377931
Estimated effects are balanced
To see the full regression output, use summary() function.
# Pass the model tips.anova to summary()function
summary(tips.anova)
Df Sum Sq Mean Sq F value Pr(>F)
day 4 2203.0 550.8 290.1 <2e-16 ***
Residuals 240 455.7 1.9
---
Signif. codes: 0 *** 0.001 ** 0.01 * 0.05 . 0.1 1
Writting - 1 in the formula indicates that the intercept should not be included in the model; the categorical variable day is automatically setup to have a coefficient for each level. You can apply the same technique to the linear model using the lm() function.
# Linear model using the lm() function
(tips.lm <- lm(tip ~ day - 1, data = tips))
Call:
lm(formula = tip ~ day - 1, data = tips)
Coefficients:
dayFri daySat daySun dayThur
2.735 2.993 3.255 2.771
# The complete regression output
summary(tips.lm)
Call:
lm(formula = tip ~ day - 1, data = tips)
Residuals:
Min 1Q Median 3Q Max
-2.2451 -0.9931 -0.2347 0.5382 7.0069
Coefficients:
Estimate Std. Error t value Pr(>|t|)
dayFri 2.7347 0.3161 8.651 7.46e-16 ***
daySat 2.9931 0.1477 20.261 < 2e-16 ***
daySun 3.2551 0.1581 20.594 < 2e-16 ***
dayThur 2.7715 0.1750 15.837 < 2e-16 ***
---
Signif. codes: 0 *** 0.001 ** 0.01 * 0.05 . 0.1 1
Residual standard error: 1.378 on 240 degrees of freedom
Multiple R-squared: 0.8286, Adjusted R-squared: 0.8257
F-statistic: 290.1 on 4 and 240 DF, p-value: < 2.2e-16
From the output, we notice that the F-value of an ANOVA test is 290.1 and the F-statistic of the linear model is also 290.1. They are basically the same, as are degrees of freedom. This shows that the ANOVA and regression were derived along the same lines and can accomplish the same analysis.
VISUALIZING THE COEFFICIENTS
Visualizing the coefficients and the standard errors should show the same results as computing them using the ANOVA formula. The POINT ESTIMATES for the MEAN are identical and the CONFIDENCE INTERVALS are similar, the difference due to slightly different calculations as shown in the following plots.
- Calculating The Means And Confidence Interval Manually
# Install the plyr package : install it andd then load it
library(plyr)
require(plyr)
Having downloaded the required package, you can create an object called tips.by.day using the ddply() function.
# Create an object called tips.by.day using ddply() function from the plyr package
(tips.by.day <- ddply(tips, "day", summarize,
tip.mean = mean(tip),
tip.sd = sd(tip),
Length = NROW(tip),
tfrac = qt(p = .90, df = Length - 1),
Lower = tip.mean - tfrac * tip.sd / sqrt(Length),
Upper = tip.mean + tfrac * tip.sd / sqrt(Length)))
Now that we have the output of tips.by.day, we can extract them from summary for tips.lm. It is important to point out that nrow and ncol return the number of rows and columns present in a data set. NCOL() and NROW() do the same treating a vector as 1-column matrix. The tidyverse package would be helpful here as well.
# Extract them from the summary for tips.lm
(tips.Info <- summary(tips.lm))
Call:
lm(formula = tip ~ day - 1, data = tips)
Residuals:
Min 1Q Median 3Q Max
-2.2451 -0.9931 -0.2347 0.5382 7.0069
Coefficients:
Estimate Std. Error t value Pr(>|t|)
dayFri 2.7347 0.3161 8.651 7.46e-16 ***
daySat 2.9931 0.1477 20.261 < 2e-16 ***
daySun 3.2551 0.1581 20.594 < 2e-16 ***
dayThur 2.7715 0.1750 15.837 < 2e-16 ***
---
Signif. codes: 0 *** 0.001 ** 0.01 * 0.05 . 0.1 1
Residual standard error: 1.378 on 240 degrees of freedom
Multiple R-squared: 0.8286, Adjusted R-squared: 0.8257
F-statistic: 290.1 on 4 and 240 DF, p-value: < 2.2e-16
# Extract point estimates for the mean and standard errors of tips per day
(tips.Coef <- as.data.frame(tips.Info$coefficients[, 1:2]))
tips.Coef <- within(tips.Coef, {
Lower <- Estimate - qt(p = 0.90, df = tips.Info$df[2]) * `Std. Error`
Upper <- Estimate + qt(p = 0.90, df = tips.Info$df[2]) * `Std. Error`
day <- row.names(tips.Coef)
})
PLOT TIP.MEAN AGAINST DAY
library(ggplot2)
# PLOTTING tip.mean against day with horizontal error bars
ggplot(data = tips.by.day, mapping = aes(x = tip.mean, y = day)) +
geom_point() +
geom_errorbarh(aes(xmin = Lower, xmax = Upper), height = .4) +
ggtitle("Tips by Day Calculated Manually") +
theme(plot.title = element_text(hjust = 0.5))

# Plotting tip estimates against day with horizontal error bars displayed
ggplot(data = tips.Coef, mapping = aes(x = Estimate, y = day)) +
geom_point() +
geom_errorbarh(aes(xmin = Lower, xmax = Upper), height = .4) +
ggtitle("Tips by Day Calculated From Regression Model") +
theme(plot.title = element_text(hjust = 0.5))

Regression coefficients and confidence intervals are taken from a regression model and calculated manually. The point estimates for the mean are identical and the confidence intervals are very similar, the difference due to slightly different calculations. The y-axis labels are also different because when dealing with factors lm tacks on the name of the variable to the level value.
A new function and a new feature were used here. We have introduced within(), which is similar to with() in that it lets us refer to columns in a data.frame by name, but different in that we can create new columns within that data.frame, hence the name. Second, one of the columns was named Std.Error with a space. To refer to a variable with spaces in its name, even as a column in a data.frame, we must enclose the name in back tips.
LS0tDQp0aXRsZTogIlNpbXBsZSBMaW5lYXIgUmVncmVzc2lvbiBpbiBSIg0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOiBkZWZhdWx0DQogIEJ5IFBpZXJyZSBLb2xvd2U6IGRlZmF1bHQNCi0tLQ0KVGhlIHdvcmtob3JzZSBvZiBzdGF0aXN0aWNhbCBhbmFseXNpcyBpcyB0aGUgbGluZWFyIG1vZGVsLCBwYXJ0aWN1bGFybHkgcmVncmVzc2lvbi4gSW52ZW50ZWQgYnkgRnJhbmNpcyBHYWx0b24gdG8gc3R1ZHkgcmVsYXRpb25zaGlwIGJldHdlZW4gcGFyZW50cyBhbmQgY2hpbGRyZW4gZGVzY3JpYmVkIGFzIHJlZ3Jlc3NpbmcgdG8gdGhlIG1lYW4sIGl0IGhhcyBiZWNvbWUgb25lIG9mIHRoZSBtb3N0IHdpZGVseSB1c2VkIG1vZGVsaW5nIHRlY2huaXF1ZXMuIEluIHRoaXMgdHV0b3JpYWwsIHdlIHdpbGwgZm9jdXMgb24gc2ltcGxlIGxpbmVhciByZWdyZXNzaW9uLg0KDQpTSU1QTEUgTElORUFSIFJFR1JFU1NJT04NCg0KVGhlIHNpbXBsZSBsaW5lYXIgcmVncmVzc2lvbiBkZXRlcm1pbmVzIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0d28gdmFyaWFibGVzLiBUaGF0IGlzIHdlIGFyZSB1c2luZyBvbmUgdmFyaWFibGUgdG8gdGVsbCB1cyB3aGF0IHdlIGNhbiBleHBlY3QgZnJvbSB0aGUgb3RoZXIgdmFyaWFibGUuIFRoZSBvdXRjb21lIHZhcmlhYmxlLCB3aGF0IHdlIGFyZSB0cnlpbmcgdG8gcHJlZGljdCwgaXMgY2FsbGVkIHRoZSByZXNwb25zZSB2YXJpYWJsZSwgYW5kIHRoZSBpbnB1dCB2YXJpYWJsZSwgd2hhdCB3ZSBhcmUgdXNpbmcgdG8gcHJlZGljdCwgaXMgY2FsbGVkIHRoZSBwcmVkaWN0b3IgdmFyaWFibGUuIFRoZSBnZW5lcmFsIGlkZWEgb2YgdGhlIHNpbXBsZSBsaW5lYXIgcmVncmVzc2lvbiBpcyB0byB1c2UgdGhlIHByZWRpY3RvciB0byBjb21lIHVwIHdpdGggc29tZSBhdmVyYWdlIHZhbHVlIG9mIHRoZSByZXNwb25zZS4gVGhlIHJlbGF0aW9uc2hpcCBpcyBkZWZpbmVkIGFzOiANCnkgPSBhICsgYnggKyBFLCB3aGVyZSBhIGlzIHRoZSBpbnRlcmNlcHQ7IGFuZCBiIGlzIHRoZSBzbG9wZTsgRSBpcyB0aGUgZXJyb3IgdGVybTsgeCBpcyB0aGUgcHJlZGljdG9yIHZhcmlhYmxlOyBhbmQgeSBpcyB0aGUgb3V0Y29tZS9yZXNwb25zZSB2YXJpYWJsZS4gVGhlIGVxdWF0aW9uIGRlc2NyaWJlcyBpcyBlc3NlbnRpYWxseSBhIHN0cmFpZ2h0IGxpbmUgdGhhdCBnb2VzIHRocm91Z2ggdGhlIGRhdGEgd2hlcmUgYSBpcyB0aGUgeS1pbnRlcmNlcHQgYW5kIGIgaXMgdGhlIHNsb3BlLiBGaXJzdCwgaW5zdGFsbCB0aGUgcmVxdWlyZWQgcGFja2FnZXMgZm9yIG91ciBhbmFseXNpcy4NCg0KYGBge3J9DQojIGluc3RhbGwgdXNpbmdSIGFuZCBnZ3Bsb3QyIHBhY2thZ2VzOyBwYWNrYWdlcyBhbHJlYWR5IGluc3RhbGxlZDsgbG9hZGluZyB0aGVtIHVzaW5nIGxpYnJhcnkoKQ0KbGlicmFyeShVc2luZ1IpDQpgYGANCg0KPkxvYWRpbmcgcmVxdWlyZWQgcGFja2FnZTogTUFTUw0KPkxvYWRpbmcgcmVxdWlyZWQgcGFja2FnZTogSGlzdERhdGENCj5Mb2FkaW5nIHJlcXVpcmVkIHBhY2thZ2U6IEhtaXNjDQo+TG9hZGluZyByZXF1aXJlZCBwYWNrYWdlOiBsYXR0aWNlDQo+TG9hZGluZyByZXF1aXJlZCBwYWNrYWdlOiBzdXJ2aXZhbA0KPkxvYWRpbmcgcmVxdWlyZWQgcGFja2FnZTogRm9ybXVsYQ0KPkxvYWRpbmcgcmVxdWlyZWQgcGFja2FnZTogZ2dwbG90Mg0KDQpBYm92ZSBpcyB0aGUgbGlzdCBvZiBpdGVtcyBjb250YWluZWQgaW4gVXNpbmdSIHBhY2thZ2UgY29sbGVjdGlvbi4gVG8gaWxsdXN0cmF0ZSB0aGUgc2ltcGxlIGxpbmVhciByZWdyZXNzaW9uLCB3ZSB1c2UgdGhlIGZhdGhlcnMnIGFuZCBzb25zJyBkYXRhLiBXZSB3aWxsIGFsc28gcmVxdWlyZSBnZ3Bsb3QyIGZvciBwbG90dGluZy4NCg0KYGBge3J9DQojIFJlcXVpcmUgZ2dwbG90MiBhbmQgVXNpbmdSDQpyZXF1aXJlKFVzaW5nUikNCnJlcXVpcmUoZ2dwbG90MikNCmBgYA0KDQpFWFBMT1JBVE9SWSBBTkFMWVNJUw0KDQpMZXQncyBjaGVjayB0aGUgZmlyc3QgMTAgYW5kIHRoZSBsYXN0IDEwIG9ic2VydmF0aW9ucyBmb3IgdGhlIGZhdGhlcnMnIGFuZCBzb25zJyBkYXRhLg0KDQpgYGB7cn0NCiMgVGhlIGZpcnN0IDEwIG9ic2VydmF0aW9uIG9mIG91ciBkYXRhc2V0IHVzaW5nIHRoZSBwcmludChoZWFkKGRhdGEsIG4gPSAxMCkpIGZ1bmN0aW9uDQpwcmludChoZWFkKGZhdGhlci5zb24sIG4gPSAxMCkpDQpgYGANCg0KYGBge3J9DQojIFRoZSBsYXN0IDEwIG9ic2VydmF0aW9ucyBvZiBvdXIgZGF0YXNldCB1c2luZyBwcmludCh0YWlsKGRhdGEsIG4gPSAxMCkpIGZ1bmN0aW9uDQpwcmludCh0YWlsKGZhdGhlci5zb24sIG4gPSAxMCkpDQpgYGANCg0KVGhlIGZhdGhlci5zb24gZGF0YSBpcyBQZWFyc29uJ3MgZGF0YSBzZXQgb24gaGVpZ2h0cyBvZiBmYXRoZXJzIGFuZCB0aGVpciBzb25zLiBUaGUgZGF0YSBzZXQgY29udGFpbnMgMTA3OCBtZWFzdXJlbWVudHMgb2YgZmF0aGVycycgaGVpZ2h0cyBhbmQgc29ucycgaGVpZ2h0cy4gSXRzIGNsYXNzIGlzIGEgZGF0YS5mcmFtZSB3aXRoIDEwNzggb2JzZXJ2YXRpb25zIG9uIHRoZSBmb2xsb3dpbmcgMiB2YXJpYWJsZXM6IGZoZWlnaHQgZm9yIGZhdGhlcnMnIGhlaWdodHMgaW4gaW5jaGVzIGFuZCBzaGVpZ2h0IGZvciBzb25zJyBoZWlnaHRzIGluIGluY2hlcy4gVGhpcyBkYXRhIHNldCB3YXMgdXNlZCBieSBQZWFyc29uIHRvIGludmVzdGlnYXRlIHJlZ3Jlc3Npb24uIFRoZSBhYm92ZSBpbmZvcm1hdGlvbiBjYW4gYmUgZm91bmQgYnkganVzdCB0eXBpbmcgP2ZhdGhlci5zb24gaW4gdGhlIFIgY29uc29sZS4gQWx0ZXJuYXRpdmVseSwgdXNpbmcgdGhlIGZvbGxvd2luZyBjb21tYW5kcyBjYW4gZ2V0IHlvdSBhY2N1c3RvbWVkIHdpdGggdGhlIGRhdGEgc2V0Lg0KDQpgYGB7cn0NCiMgQ2hlY2tpbmcgdGhlIGNsYXNzIG9mIHRoZSBkYXRhIHNldCB1c2luZyBjbGFzcyhkYXRhc2V0KSBmdW5jdGlvbg0KY2xhc3MoZmF0aGVyLnNvbikNCmBgYA0KDQpBIGRhdGEuZnJhbWUgaXMgYSBjb2xsZWN0aW9uIG9mIHZhcmlhYmxlcyB3aGljaCBzaGFyZSBtYW55IG9mIHRoZSBwcm9wZXJ0aWVzIG9mIG1hdHJpY2VzIGFuZCBvZiBsaXN0cywgdXNlZCBhcyB0aGUgZnVuZGFtZW50YWwgZGF0YSBzdHJ1Y3R1cmUgYnkgbW9zdCBSJ3MgbW9kZWxpbmcgc29mdHdhcmUuDQoNCmBgYHtyfQ0KIyBDaGVja2luZyB0aGUgc3RydWN0dXJlIG9mIHRoZSBkYXRhIHNldCB1c2luZyBzdHIoZGF0YXNldCkgZnVuY3Rpb24NCnN0cihmYXRoZXIuc29uKQ0KYGBgDQoNClRoZSBzdHIoZmF0aGVyLnNvbikgY29tbWFuZCB0ZWxscyB1cyB0aGF0IG91ciBkYXRhIHNldCBjb250YWlucyAxMDc4IG9ic2VydmF0aW9ucyBhbmQgMiB2YXJpYWJsZXMsIHdoaWNoIGFyZSBudW1lcmljIHZhcmlhYmxlcy4gQWRkaXRpb25hbGx5LCB3ZSBjYW4gZG8gYSBicmllZiBzb21tYXJ5IG9mIG91ciBkYXRhIHNldCB1c2luZyB0aGUgc3VtbWFyeSgpIGZ1bmN0aW9uLg0KDQpgYGB7cn0NCiNTdW1tYXJ5IHN0YXRpc3RpY3Mgb2YgZmF0aGVyLnNvbiBkYXRhIHNldA0Kc3VtbWFyeShmYXRoZXIuc29uKQ0KYGBgDQoNCldlIGNhbiB2aXN1YWxpemUgdGhlIGRpc3RyaWJ1dGlvbiBvZiBmYXRoZXJzJyBoZWlnaHRzIGJ5IGRpdmlkaW5nIHRoZSB4IGF4aXMgaW50byBiaW5zIGFuZCBjb3VudGluZyB0aGUgbnVtYmVyIG9mIG9ic2VydmF0aW9ucyBpbiBlYWNoIGJpbi4gSGlzdG9ncmFtLCBnZW9tX2hpc3RvZ3JhbSBpbiB0aGUgZ2dwbG90MiBwYWNrYWdlLCBkaXNwbGF5cyB0aGUgY291bnQgd2l0aCBiYXJzLCB3aGlsZSBnZW9tX2ZyZXFwb2x5IGRpc3BsYXlzIHRoZSBjb3VudHMgd2l0aCBsaW5lcy4gRnJlcXVlbmN5IHBvbHlnb25zIGFyZSBtb3JlIHN1aXRhYmxlIHdoZW4geW91IHdhbnQgdG8gY29tcGFyZSB0aGUgZGlzdHJpYnV0aW9uIGFjcm9zcyB0aGUgbGV2ZWxzIG9mIGEgY2F0ZWdvcmljYWwgdmFyaWFibGUuDQoNCmBgYHtyfQ0KIyBIaXN0b2dyYW0gb2YgZmF0aGVyJ3MgaGVpZ2h0IGRpc3RyaWJ1dGlvbg0KZ2dwbG90KGRhdGEgPSBmYXRoZXIuc29uLCBtYXBwaW5nID0gYWVzKHggPSBmaGVpZ2h0KSkgKw0KICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMzAsIGZpbGwgPSAic2VhZ3JlZW4iKSArDQogIGdndGl0bGUoIkhpc3RvZ3JhbSBvZiBGYXRoZXIncyBIZWlnaHQiKSArDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKSANCmBgYA0KDQpgYGB7cn0NCiMgSGlzdG9ncmFtIG9mIGZhdGhlcidzIGhlaWdodCB1c2luZyBnZW9tX2ZyZXFwb2x5IGRpc3BsYXlpbmcgdGhlIGNvdW50cyB3aXRoIGxpbmVzDQpnZ3Bsb3QoZGF0YSA9IGZhdGhlci5zb24sIG1hcHBpbmcgPSBhZXMoeCA9IGZoZWlnaHQpKSArDQogIGdlb21fZnJlcXBvbHkoYmlucyA9IDMwKSArDQogIGdndGl0bGUoIkhpc3RvZ3JhbSBvZiBIZWlnaHQgQ291bnRzIFdpdGggTGluZXMiKSArDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQ0KYGBgDQoNCldlIGNhbiBhbHNvIHBsb3QgdGhlIGhpc3RvZ3JhbSBvZiB0aGUgc29uJ3MgaGVpZ2h0LiBXZSBjYW4gYXBwbHkgdGhlIHNhbWUgdGVjaG5pcXVlIGFzIHRoZSBvbmUgdXNlZCBpbiB0aGUgcHJldmlvdXMgZ3JhcGhzLg0KDQpgYGB7cn0NCiMgSGlzdG9ncmFtIG9mIHRoZSBzb24ncyBoZWlnaHQgdXNpbmcgdGhlIGdncGxvdDIgcGFja2FnZQ0KZ2dwbG90KGRhdGEgPSBmYXRoZXIuc29uLCBtYXBwaW5nID0gYWVzKHggPSBzaGVpZ2h0KSkgKw0KICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMzAsIGZpbGwgPSAic2VhZ3JlZW4iKSArDQogIGdndGl0bGUoZXhwcmVzc2lvbihhdG9wKCJIaXN0b2dyYW0gb2YgU29uJ3MgSGVpZ2h0IiwgYXRvcChpdGFsaWMoIkhlaWdodHMgYnkgQ291bnQiLCAiIikpKSkpICsNCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpICsNCiAgdGhlbWUocGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpDQpgYGANCiBXZSBoYXZlIGFkZGVkIG5ldyBlbGVtZW50cywgd2hpY2ggYXJlIHRpdGxlIGFuZCBzdWJ0aXRsZSwgYnkgcGFzc2luZyB0aGVzZSBhcmd1bWVudHMgdG8gZ2d0aXRsZSgpIGZ1bmN0aW9uLiBMZXQncyBhcHBseSB0aGUgc2FtZSB0ZWNobmlxdWUgdG8gdGhlIGxpbmUgZ3JhcGguDQogDQpgYGB7cn0NCiMgSGlzdG9ncmFtIG9mIHNvbidzIGhlaWdodCB1c2luZyBnZW9tX2ZyZXFwb2x5KCkgZnVuY3Rpb24gZGlzcGxheWluZyBjb3VudHMgYnkgbGluZS4NCmdncGxvdChkYXRhID0gZmF0aGVyLnNvbiwgbWFwcGluZyA9IGFlcyh4ID0gc2hlaWdodCkpICsNCiAgZ2VvbV9mcmVxcG9seShiaW5zID0gMzApICsNCiAgZ2d0aXRsZShleHByZXNzaW9uKGF0b3AoIkhpc3RvZ3JhbSBvZiBTb24ncyBIZWlnaHQiLCBhdG9wKGl0YWxpYygiSGVpZ2h0IFdpdGggTGluZSBDb3VudCIsICIiKSkpKSkgKw0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkgKw0KICB0aGVtZShwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkNCmBgYA0KDQpXZSBjYW4gaW5mZXIgdGhhdCBib3RoIGZhdGhlcidzIGhlaWdodCBhbmQgc29uJ3MgaGVpZ2h0IHNlZW0gdG8gYmUgbm9ybWFsbHkgZGlzdHJpYnV0ZWQsIGFjY29yZGluZyB0byB0aGUgcGxvdHRlZCBoaXN0b2dyYW1zLiBXZSBjYW4gZXh0ZW5kIG91ciBhbmFseXNpcyBieSBsb29raW5nIGF0IGhvdyBmYXRoZXIncyBoZWlnaHQgaW5mbHVlbmNlcyBzb24ncyBoZWlnaHQgdXNpbmcgc2ltcGxlIGxpbmVhciByZWdyZXNzaW9uLg0KDQpTSU1QTEUgTElORUFSIFJFR1JFU1NJT046IFVTSU5HIEZBVEhFUlMnSEVJR0hUUyB0byBERVRFUk1JTkUgU09OUycgSEVJR0hUUw0KDQpgYGB7cn0NCiMgVXNpbmcgZmF0aGVycycgaGVpZ2h0cyB0byBwcmVkaWN0IHNvbnMnIGhlaWdodHMgd2l0aCBhIGdlb21fc21vb3RoICgpIGZ1bmN0aW9uDQpnZ3Bsb3QoZGF0YSA9IGZhdGhlci5zb24sIG1hcHBpbmcgPSBhZXMoeCA9IGZoZWlnaHQsIHkgPSBzaGVpZ2h0KSkgKyANCiAgZ2VvbV9wb2ludCgpICsNCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIikgKyANCiAgbGFicyh4ID0gIkZhdGhlcnMiLCB5ID0gIlNvbnMiKSArDQogIGdndGl0bGUoZXhwcmVzc2lvbihhdG9wKCJTY2F0dGVycGxvdCBvZiBGYXRoZXJzIHZzLiBTb25zIiwgYXRvcChpdGFsaWMoIldpdGggRml0dGVkIFJlZ3Jlc3Npb24gTGluZSIsICIiKSkpKSkgKw0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkgKw0KICB0aGVtZShwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkNCmBgYA0KDQpUaGlzIHBsb3QgaXMgYnVpbHQgdXNpbmcgZmF0aGVycycgaGVpZ2h0cyB0byBwcmVkaWN0IHNvbnMnIGhlaWdodHMgdXNpbmcgc2ltcGxlIGxpbmVhciByZWdyZXNzaW9uLiBUaGUgZmF0aGVycycgaGVpZ2h0cyBhcmUgdGhlIHByZWRpY3RvcnMgYW5kIHRoZSBzb25zJyBoZWlnaHRzIGFyZSB0aGUgcmVzcG9uc2VzLiBUaGUgYmx1ZSBsaW5lIHJ1bm5pbmcgdGhyb3VnaCB0aGUgcG9pbnRzIGlzIHRoZSByZWdyZXNzaW9uIGxpbmUgYW5kIHRoZSBncmV5IGJhbmQgYXJvdW5kIGl0IHJlcHJlc2VudHMgdW5jZXJ0YWludHkgaW4gdGhlIGZpdC4NCg0KVG8gZ2VuZXJhdGUgdGhpcyBuaWNlIHBsb3QsIHdlIHBhc3MgdGhlIGFyZ3VtZW50LCAobWV0aG9kID0gImxtIiksIHRvIHRoZSBnZW9tX3Ntb290aCgpIGZ1bmN0aW9uLiBUbyBtYWtlIHRoZSByZXN1bHRzIG9mIHRoZSByZWdyZXNzaW9uIGF2YWlsYWJsZSB0byB1cywgd2UgbmVlZCB0byBjYWxjdWxhdGUgYSByZWdyZXNzaW9uIHVzaW5nIHRoZSBmdW5jdGlvbiBsbS4NCg0KQ0FMQ1VMQVRFIExJTkVBUiBSRUdSRVNTSU9OIFVTSU5HIFRIRSBsbSgpRlVOQ1RJT04NCg0KYGBge3J9DQojIENhbGN1bGF0ZSBMaW5lYXIgcmVncmVzc2lvbiB1c2luZyBsbSgpIGZ1bmN0aW9uDQooaGVpZ2h0LmxtIDwtIGxtKHNoZWlnaHQgfiBmaGVpZ2h0LCBkYXRhID0gZmF0aGVyLnNvbikpDQpgYGANCg0KVGhlIGFib3ZlIGZvcm11bGEgc3BlY2lmaWVzIHRvIHJlZ3Jlc3Mgc2hlaWdodCwgdGhlIHJlc3BvbnNlIG9yIG91dGNvbWUgdmFyaWFibGUsIG9uIGZoZWlnaHQsIHRoZSBwcmVkaWN0b3IgdmFyaWFibGUsIHVzaW5nIHRoZSBmYXRoZXIuc29uIGRhdGEsIGFuZCBhZGRzIHRoZSBpbnRlcmNlcHQgdGVybSBhdXRvbWF0aWNhbGx5LiBUaGUgcmVzdWx0cyBzaG93IGNvZWZmaWNpZW50cyBmb3IgKGludGVyY2VwdCkgYW5kIGZoZWlnaHQgd2hpY2ggaXMgdGhlIHNsb3BlIGZvciBmaGVpZ2h0LCB0aGUgcHJlZGljdG9yIHZhcmlhYmxlLiBIb3cgY2FuIHdlIHRoZW4gaW50ZXJwcmV0IHRoZXNlIHJlc3VsdHM/IFRoZSBpbnRlcnByZXRhdGlvbiBvZiB0aGlzIGlzIHRoYXQsIGZvciBldmVyeSBleHRyYSBpbmNoIG9mIGZhdGhlcidzIGhlaWdodCwgd2UgZXhwZWN0IGFuIGV4dHJhIGhhbGYgaW5jaCBpbmNyZWFzZSBpbiBzb24ncyBoZWlnaHQuIFRoZSBpbnRlcmNlcHQgZG9lc24ndCBtYWtlIHNlbnNlIGluIHRoaXMgY2FzZSBiZWNhdXNlIGl0IHJlcHJlc2VudHMgdGhlIGhlaWdodCBvZiBhIHNvbiB3aG9zZSBmYXRoZXIgaGVpZ2h0IGlzIHplcm8gaGVpZ2h0LCB3aGljaCBpcyBub25zZW5zaWNhbC4gIFRvIGdldCBhIGZ1bGwgcmVwb3J0IG9mIHRoZSByZWdyZXNzaW9uIG1vZGVsLCBqdXN0IHBhc3MgdGhlIGhlaWdodC5sbSBtb2RlbCB0byB0aGUgc3VtbWFyeSBmdW5jdGlvbi4NCg0KYGBge3J9DQojIENvbXBsZXRlIHJlZ3Jlc3Npb24gcmVzdWx0cyB1c2luZyBzdW1tYXJ5KCkgZnVuY3Rpb24NCihzdW1tYXJ5KGhlaWdodC5sbSkpDQpgYGANCg0KVGhpcyBwcmludHMgb3V0IGEgbG90IG9mIGluZm9ybWF0aW9uIGFib3V0IHRoZSBtb2RlbCwgaW5jbHVkaW5nIHRoZSBzdGFuZGFyZCBlcnJvciwgdGhlIHQtdmFsdWVzIGFuZCBwLXZhbHVlcyBmb3IgdGhlIGNvZWZmaWNpZW50cywgdGhlIGRlZ3JlZXMgb2YgZnJlZWRvbSwgcmVzaWR1YWwgc3VtbWFyeSBzdGF0aXN0aWNzIGFuZCB0aGUgcmVzdWx0cyBvZiB0aGUgRi10ZXN0Lg0KDQpBTk9WQSBBUyBBTiBBTFRFUk5BVElWRSBSRUdSRVNTSU9ODQoNCkFuIGFsdGVybmF0aXZlIHRvIHJ1bm5pbmcgYW4gQU5PVkEgdGVzdCBpcyB0byBmaXQgYSByZWdyZXNzaW9uIHdpdGgganVzdCBvbmUgY2F0ZWdvcmljYWwgdmFyaWFibGUgYW5kIG5vIGludGVyY2VwdCB0ZXJtLiBUbyBydW4gQU5PVkEsIHdlIHdpbGwgdXNlIHRoZSB0aXBzIGRhdGEgaW4gdGhlIHJlc2hhcGUyIHBhY2thZ2Ugb24gd2hpY2ggd2Ugd2lsbCBmaXQgYSByZWdyZXNzaW9uLiBUbyBsZWFybiBtb3JlIGFib3V0IHRoZSByZXNoYXBlMiBwYWNrYWdlLCBqdXN0IHR5cGUgP3Jlc2hhcGUyIGluIHRoZSBSIGNvbnNvbGUuDQoNCmBgYHtyfQ0KIyBQYXNzIHRpcHMgYW5kIHBhY2thZ2UgcmVzaGFwZTIgdG8gdGhlIGRhdGEoKSBmdW5jdGlvbg0KZGF0YSh0aXBzLCBwYWNrYWdlID0gInJlc2hhcGUyIikNCmBgYA0KDQpMZXQncyBjaGVjayB0aGUgY29udGVudCBvZiB0aGUgZGF0YXNldCB0aXBzLg0KDQpgYGB7cn0NCnN0cih0aXBzKQ0KYGBgDQoNCmBgYHtyfQ0KIyBDaGVja2luZyB0aGUgZmlyc3QgMTAgb2JzZXJ2YXRpb25zDQoocHJpbnQoaGVhZCh0aXBzLCBuID0gMTApKSkNCg0KYGBgDQoNCmBgYHtyfQ0KIyBDaGVja2luZyB0aGUgbGFzdCAxMCBvYnNlcnZhdGlvbnMNCihwcmludCh0YWlsKHRpcHMsIG4gPSAxMCkpKQ0KYGBgDQoNCldlIGFyZSBnb2luZyB0byBmaXQgb3VyIG1vZGVsIHdpdGggdGlwIHJlZ3Jlc3NlcyBhZ2FpbnN0IGRheSBtaW51cyB0aGUgaW50ZXJjZXB0LiBUaXAgaXMgb3VyIG91dGNvbWUgdmFyaWFibGUgYW5kIGRheSBpcyBvdXIgcHJlZGljdG9yIHZhcmlhYmxlLiBXZSB3aWxsIHVzZSBhb3YoKSBmdW5jdGlvbiB0byBmaXQgYSBtb2RlbCB3aXRoIGEgY2F0ZWdvcmljYWwgdmFyaWFibGUgd2l0aG91dCBpbnRlcmNlcHQuDQoNCmBgYHtyfQ0KIyBQYXNzIGFyZ3VtZW50cyB0byBhb3YoKSBmdW5jdGlvbiBmb3IgYW4gQU5PVkEgdGVzdA0KKHRpcHMuYW5vdmEgPC0gYW92KHRpcCB+IGRheSAtIDEsIGRhdGEgPSB0aXBzKSkNCmBgYA0KDQpUbyBzZWUgdGhlIGZ1bGwgcmVncmVzc2lvbiBvdXRwdXQsIHVzZSBzdW1tYXJ5KCkgZnVuY3Rpb24uDQoNCmBgYHtyfQ0KIyBQYXNzIHRoZSBtb2RlbCB0aXBzLmFub3ZhIHRvIHN1bW1hcnkoKWZ1bmN0aW9uDQpzdW1tYXJ5KHRpcHMuYW5vdmEpDQpgYGANCg0KV3JpdHRpbmcgLSAxIGluIHRoZSBmb3JtdWxhIGluZGljYXRlcyB0aGF0IHRoZSBpbnRlcmNlcHQgc2hvdWxkIG5vdCBiZSBpbmNsdWRlZCBpbiB0aGUgbW9kZWw7IHRoZSBjYXRlZ29yaWNhbCB2YXJpYWJsZSBkYXkgaXMgYXV0b21hdGljYWxseSBzZXR1cCB0byBoYXZlIGEgY29lZmZpY2llbnQgZm9yIGVhY2ggbGV2ZWwuIFlvdSBjYW4gYXBwbHkgdGhlIHNhbWUgdGVjaG5pcXVlIHRvIHRoZSBsaW5lYXIgbW9kZWwgdXNpbmcgdGhlIGxtKCkgZnVuY3Rpb24uDQoNCmBgYHtyfQ0KIyBMaW5lYXIgbW9kZWwgdXNpbmcgdGhlIGxtKCkgZnVuY3Rpb24NCih0aXBzLmxtIDwtIGxtKHRpcCB+IGRheSAtIDEsIGRhdGEgPSB0aXBzKSkNCmBgYA0KDQpgYGB7cn0NCiMgVGhlIGNvbXBsZXRlIHJlZ3Jlc3Npb24gb3V0cHV0DQpzdW1tYXJ5KHRpcHMubG0pDQpgYGANCg0KRnJvbSB0aGUgb3V0cHV0LCB3ZSBub3RpY2UgdGhhdCB0aGUgRi12YWx1ZSBvZiBhbiBBTk9WQSB0ZXN0IGlzIDI5MC4xIGFuZCB0aGUgRi1zdGF0aXN0aWMgb2YgdGhlIGxpbmVhciBtb2RlbCBpcyBhbHNvIDI5MC4xLiBUaGV5IGFyZSBiYXNpY2FsbHkgdGhlIHNhbWUsIGFzIGFyZSBkZWdyZWVzIG9mIGZyZWVkb20uIFRoaXMgc2hvd3MgdGhhdCB0aGUgQU5PVkEgYW5kIHJlZ3Jlc3Npb24gd2VyZSBkZXJpdmVkIGFsb25nIHRoZSBzYW1lIGxpbmVzIGFuZCBjYW4gYWNjb21wbGlzaCB0aGUgc2FtZSBhbmFseXNpcy4NCg0KVklTVUFMSVpJTkcgVEhFIENPRUZGSUNJRU5UUw0KDQpWaXN1YWxpemluZyB0aGUgY29lZmZpY2llbnRzIGFuZCB0aGUgc3RhbmRhcmQgZXJyb3JzIHNob3VsZCBzaG93IHRoZSBzYW1lIHJlc3VsdHMgYXMgY29tcHV0aW5nIHRoZW0gdXNpbmcgdGhlIEFOT1ZBIGZvcm11bGEuIFRoZSBQT0lOVCBFU1RJTUFURVMgZm9yIHRoZSBNRUFOIGFyZSBpZGVudGljYWwgYW5kIHRoZSBDT05GSURFTkNFIElOVEVSVkFMUyBhcmUgc2ltaWxhciwgdGhlIGRpZmZlcmVuY2UgZHVlIHRvIHNsaWdodGx5IGRpZmZlcmVudCBjYWxjdWxhdGlvbnMgYXMgc2hvd24gaW4gdGhlIGZvbGxvd2luZyBwbG90cy4NCg0KMS4gQ2FsY3VsYXRpbmcgVGhlIE1lYW5zIEFuZCBDb25maWRlbmNlIEludGVydmFsIE1hbnVhbGx5DQoNCmBgYHtyfQ0KIyBJbnN0YWxsIHRoZSBwbHlyIHBhY2thZ2UgOiBpbnN0YWxsIGl0IGFuZGQgdGhlbiBsb2FkIGl0DQpsaWJyYXJ5KHBseXIpDQpyZXF1aXJlKHBseXIpDQpgYGANCkhhdmluZyBkb3dubG9hZGVkIHRoZSByZXF1aXJlZCBwYWNrYWdlLCB5b3UgY2FuIGNyZWF0ZSBhbiBvYmplY3QgY2FsbGVkIHRpcHMuYnkuZGF5IHVzaW5nIHRoZSBkZHBseSgpIGZ1bmN0aW9uLg0KDQpgYGB7cn0NCiMgQ3JlYXRlIGFuIG9iamVjdCBjYWxsZWQgdGlwcy5ieS5kYXkgdXNpbmcgZGRwbHkoKSBmdW5jdGlvbiBmcm9tIHRoZSBwbHlyIHBhY2thZ2UNCih0aXBzLmJ5LmRheSA8LSBkZHBseSh0aXBzLCAiZGF5Iiwgc3VtbWFyaXplLA0KICAgICAgICAgICAgICAgICAgICAgdGlwLm1lYW4gPSBtZWFuKHRpcCksDQogICAgICAgICAgICAgICAgICAgICB0aXAuc2QgPSBzZCh0aXApLA0KICAgICAgICAgICAgICAgICAgICAgTGVuZ3RoID0gTlJPVyh0aXApLA0KICAgICAgICAgICAgICAgICAgICAgdGZyYWMgPSBxdChwID0gLjkwLCBkZiA9IExlbmd0aCAtIDEpLA0KICAgICAgICAgICAgICAgICAgICAgTG93ZXIgPSB0aXAubWVhbiAtIHRmcmFjICogdGlwLnNkIC8gc3FydChMZW5ndGgpLA0KICAgICAgICAgICAgICAgICAgICAgVXBwZXIgPSB0aXAubWVhbiArIHRmcmFjICogdGlwLnNkIC8gc3FydChMZW5ndGgpKSkNCmBgYA0KDQpOb3cgdGhhdCB3ZSBoYXZlIHRoZSBvdXRwdXQgb2YgdGlwcy5ieS5kYXksIHdlIGNhbiBleHRyYWN0IHRoZW0gZnJvbSBzdW1tYXJ5IGZvciB0aXBzLmxtLiBJdCBpcyBpbXBvcnRhbnQgdG8gcG9pbnQgb3V0IHRoYXQgbnJvdyBhbmQgbmNvbCByZXR1cm4gdGhlIG51bWJlciBvZiByb3dzIGFuZCBjb2x1bW5zIHByZXNlbnQgaW4gYSBkYXRhIHNldC4gTkNPTCgpIGFuZCBOUk9XKCkgZG8gdGhlIHNhbWUgdHJlYXRpbmcgYSB2ZWN0b3IgYXMgMS1jb2x1bW4gbWF0cml4LiBUaGUgdGlkeXZlcnNlIHBhY2thZ2Ugd291bGQgYmUgaGVscGZ1bCBoZXJlIGFzIHdlbGwuDQoNCmBgYHtyfQ0KIyBFeHRyYWN0IHRoZW0gZnJvbSB0aGUgc3VtbWFyeSBmb3IgdGlwcy5sbQ0KKHRpcHMuSW5mbyA8LSBzdW1tYXJ5KHRpcHMubG0pKQ0KYGBgDQoNCmBgYHtyfQ0KIyBFeHRyYWN0IHBvaW50IGVzdGltYXRlcyBmb3IgdGhlIG1lYW4gYW5kIHN0YW5kYXJkIGVycm9ycyBvZiB0aXBzIHBlciBkYXkNCih0aXBzLkNvZWYgPC0gYXMuZGF0YS5mcmFtZSh0aXBzLkluZm8kY29lZmZpY2llbnRzWywgMToyXSkpDQpgYGANCg0KYGBge3J9DQp0aXBzLkNvZWYgPC0gd2l0aGluKHRpcHMuQ29lZiwgew0KICBMb3dlciA8LSBFc3RpbWF0ZSAtIHF0KHAgPSAwLjkwLCBkZiA9IHRpcHMuSW5mbyRkZlsyXSkgKiBgU3RkLiBFcnJvcmANCiAgVXBwZXIgPC0gRXN0aW1hdGUgKyBxdChwID0gMC45MCwgZGYgPSB0aXBzLkluZm8kZGZbMl0pICogYFN0ZC4gRXJyb3JgDQogIGRheSA8LSByb3cubmFtZXModGlwcy5Db2VmKQ0KICB9KQ0KYGBgDQoNClBMT1QgVElQLk1FQU4gQUdBSU5TVCBEQVkNCg0KYGBge3J9DQpsaWJyYXJ5KGdncGxvdDIpDQojIFBMT1RUSU5HIHRpcC5tZWFuIGFnYWluc3QgZGF5IHdpdGggaG9yaXpvbnRhbCBlcnJvciBiYXJzDQpnZ3Bsb3QoZGF0YSA9IHRpcHMuYnkuZGF5LCBtYXBwaW5nID0gYWVzKHggPSB0aXAubWVhbiwgeSA9IGRheSkpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgZ2VvbV9lcnJvcmJhcmgoYWVzKHhtaW4gPSBMb3dlciwgeG1heCA9IFVwcGVyKSwgaGVpZ2h0ID0gLjQpICsNCiAgZ2d0aXRsZSgiVGlwcyBieSBEYXkgQ2FsY3VsYXRlZCBNYW51YWxseSIpICsNCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpDQpgYGANCg0KYGBge3J9DQojIFBsb3R0aW5nIHRpcCBlc3RpbWF0ZXMgYWdhaW5zdCBkYXkgd2l0aCBob3Jpem9udGFsIGVycm9yIGJhcnMgZGlzcGxheWVkDQpnZ3Bsb3QoZGF0YSA9IHRpcHMuQ29lZiwgbWFwcGluZyA9IGFlcyh4ID0gRXN0aW1hdGUsIHkgPSBkYXkpKSArDQogIGdlb21fcG9pbnQoKSArDQogIGdlb21fZXJyb3JiYXJoKGFlcyh4bWluID0gTG93ZXIsIHhtYXggPSBVcHBlciksIGhlaWdodCA9IC40KSArDQogIGdndGl0bGUoIlRpcHMgYnkgRGF5IENhbGN1bGF0ZWQgRnJvbSBSZWdyZXNzaW9uIE1vZGVsIikgKw0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkNCmBgYA0KDQpSZWdyZXNzaW9uIGNvZWZmaWNpZW50cyBhbmQgY29uZmlkZW5jZSBpbnRlcnZhbHMgYXJlIHRha2VuIGZyb20gYSByZWdyZXNzaW9uIG1vZGVsIGFuZCBjYWxjdWxhdGVkIG1hbnVhbGx5LiBUaGUgcG9pbnQgZXN0aW1hdGVzIGZvciB0aGUgbWVhbiBhcmUgaWRlbnRpY2FsIGFuZCB0aGUgY29uZmlkZW5jZSBpbnRlcnZhbHMgYXJlIHZlcnkgc2ltaWxhciwgdGhlIGRpZmZlcmVuY2UgZHVlIHRvIHNsaWdodGx5IGRpZmZlcmVudCBjYWxjdWxhdGlvbnMuIFRoZSB5LWF4aXMgbGFiZWxzIGFyZSBhbHNvIGRpZmZlcmVudCBiZWNhdXNlIHdoZW4gZGVhbGluZyB3aXRoIGZhY3RvcnMgbG0gdGFja3Mgb24gdGhlIG5hbWUgb2YgdGhlIHZhcmlhYmxlIHRvIHRoZSBsZXZlbCB2YWx1ZS4NCg0KQSBuZXcgZnVuY3Rpb24gYW5kIGEgbmV3IGZlYXR1cmUgd2VyZSB1c2VkIGhlcmUuIFdlIGhhdmUgaW50cm9kdWNlZCB3aXRoaW4oKSwgd2hpY2ggaXMgc2ltaWxhciB0byB3aXRoKCkgaW4gdGhhdCBpdCBsZXRzIHVzIHJlZmVyIHRvIGNvbHVtbnMgaW4gYSBkYXRhLmZyYW1lIGJ5IG5hbWUsIGJ1dCBkaWZmZXJlbnQgaW4gdGhhdCB3ZSBjYW4gY3JlYXRlIG5ldyBjb2x1bW5zIHdpdGhpbiB0aGF0IGRhdGEuZnJhbWUsIGhlbmNlIHRoZSBuYW1lLg0KU2Vjb25kLCBvbmUgb2YgdGhlIGNvbHVtbnMgd2FzIG5hbWVkIFN0ZC5FcnJvciB3aXRoIGEgc3BhY2UuIFRvIHJlZmVyIHRvIGEgdmFyaWFibGUgd2l0aCBzcGFjZXMgaW4gaXRzIG5hbWUsIGV2ZW4gYXMgYSBjb2x1bW4gaW4gYSBkYXRhLmZyYW1lLCB3ZSBtdXN0IGVuY2xvc2UgdGhlIG5hbWUgaW4gYmFjayB0aXBzLg0KDQoNCg0K