Introduction
- Weighting by sample size.
- Interpretation of output.
- Weighting to counter heteroscedasticity.
The key idea in this topic is that not all datapoints in the dataset
need have the same information value. Some data rows may contain very
precise data, while data in the other rows may be less precise.
We therefore adjust our regression analysis to give more “weight” to
those datapoints which are more reliable or informative.
Weighting by Sample Size
The CPS5 dataset is famous in econometrics, referred to often in the
book “Introductory Econometrics” by Goldberger.
The dataset has individual-level data on 528 employees
ED
ucation level, whether or not they are in the
SO
uth of the United States, BL
=whether the
person was non-white and non-Hispanic 1, otherwise 0, HP
=
whether or not the person was Hispanic, whether or not the employee was
FE
male, years of work EX
perience,
MS
= marital status (1=married, 0 not), whether or not they
are members of a UN
ion, and WG
=hourly
wage.
Goldberger begins his text by calculating the average WG
value for individuals at the various levels of education. (6 years to 18
years of education). Note that most people had 12, 14 or 16 years of
education, corresponding to high-school graduates, college graduates and
university graduates respectively.
Goldberger then regresses the average WG
on
ED
.
Download CPS5grouped.csv
## CPS5grouped = read.csv("CPS5grouped.csv", header = TRUE)
CPS5grouped
EDgroup AvWage SEMean StDev N
1 6 4.4567 0.79805 1.38226 3
2 7 5.7700 0.83690 1.87136 5
3 8 5.9787 0.62779 2.43142 15
4 9 7.3317 1.29904 4.50000 12
5 10 7.3182 0.64444 2.65711 17
6 11 6.5844 0.64084 3.32992 27
7 12 7.8182 0.25206 3.72159 218
8 13 7.8351 0.66765 4.06114 37
9 14 11.0223 0.92157 6.89643 56
10 15 10.6738 1.39739 5.03837 13
11 16 10.8361 0.63981 5.35304 70
12 17 13.6150 1.42417 6.97699 24
13 18 13.5310 1.09328 6.08715 31
ggplot(data = CPS5grouped, mapping = aes(x = EDgroup, y = AvWage)) + geom_point(mapping = aes(size = N)) +
geom_smooth(method = "lm") + labs(x = "Education (hours)", y = "Average wage ($)")
The data appears to be quite close to the line, which often happens
when we have data which have been averaged. The regression suggests we
have a high \(R^2 >0.9\).
grouped.lm <- lm(AvWage ~ EDgroup, data = CPS5grouped)
summary(grouped.lm)
Call:
lm(formula = AvWage ~ EDgroup, data = CPS5grouped)
Residuals:
Min 1Q Median 3Q Max
-1.5637 -0.7350 0.1266 0.7158 1.3198
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) -0.01445 0.87462 -0.017 0.987
EDgroup 0.72410 0.06958 10.406 4.96e-07 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 0.9387 on 11 degrees of freedom
Multiple R-squared: 0.9078, Adjusted R-squared: 0.8994
F-statistic: 108.3 on 1 and 11 DF, p-value: 4.958e-07
However note that the left-hand points represent only a few
individuals compared to some of the right-hand points, represented by
the size of the circles scaling with the number of individuals \(N\) contributing to the average. We would
like the regression line to be closer to those points that represent far
more people.
The line is close to the first category (which has only three people
in it) and far from the 12, 14, 16-year categories which have the most
people. This doesn’t really make sense.
If we use the ungrouped data the relationship between Wage and
Education is much weaker in terms of goodness of fit of the model.
Download CPS5.csv
## CPS5 = read.csv("CPS5.csv", header = TRUE)
head(CPS5)
ED SO BL HP FE MS EX UN WG
1 10 0 0 0 0 1 27 0 9.0
2 12 0 0 0 0 1 20 0 5.5
3 12 0 0 0 1 0 4 0 3.8
4 12 0 0 0 1 1 29 0 10.5
5 12 0 0 0 0 1 40 1 15.0
6 16 0 0 0 1 1 27 0 9.0
ggplot(data = CPS5, mapping = aes(x = ED, y = WG)) + geom_point() + geom_smooth(method = "lm") +
labs(x = "Education (years)", y = "Wage ($)")
`geom_smooth()` using formula = 'y ~ x'
cps5.lm = lm(WG ~ ED, data = CPS5)
summary(cps5.lm)
Call:
lm(formula = WG ~ ED, data = CPS5)
Residuals:
Min 1Q Median 3Q Max
-8.068 -3.163 -0.700 2.289 34.709
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) -1.60468 1.10318 -1.455 0.146
ED 0.81395 0.08281 9.829 <2e-16 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 4.733 on 526 degrees of freedom
Multiple R-squared: 0.1552, Adjusted R-squared: 0.1536
F-statistic: 96.6 on 1 and 526 DF, p-value: < 2.2e-16
We see there is an outlier, which could be a typo (decimal point in
the wrong place?) However the main focus here is that there is a lot
more variation among the individual data than there is among the
averaged data, so the \(R^2\) is much
lower. Also the regression coefficients have different values.
Why Grouped Regression Results Differ
Formulas not examinable but conclusion is important.
To explain the difference between these two regressions, we consider
the regression of \(Y\) vs \(X\) (\(n\)
data) and \(\bar Y\) versus \(\bar X\) (where the data have been divided
into \(m\) groups - a much smaller
number than \(n\).) In Least Squares we
choose \(\beta_0\) and \(\beta_1\) to minimise
\[SS_{error} = \sum^n_{i=1}(y_i - \beta_0-
\beta_1 x_i)^2 ~~~~ (1) \] However the regression for the
averages treats all \(m\) groups as
being of equal worth: \[ \sum^m_{j=1}(\bar
y_j - \beta_0- \beta_1 \bar x_j)^2 ~~~~~~ (2) \]
That is, the computations will try just as hard to fit a value that
arises from a small group of data as a value from a large group.
But in fact we don’t have all the \(y_{ij}\) equal. Instead what we do is to
add and subtract \(\bar y_j\) inside
the parentheses in equation (3), and then expand the square: \[\begin{aligned}SS_{error}
&= \sum^m_{j=1}\sum^{n_j}_{i=1}(y_{ij} -\bar y_j + \bar y_j -
\beta_0- \beta_1 x_{ij})^2\\&= \sum^m_{j=1}\sum^{n_j}_{i=1}(y_{ij}
-\bar y_j)^2 + \sum^m_{j=1}\sum^{n_j}_{i=1}(\bar y_j - \beta_0- \beta_1
\bar x_j)^2 ~~~~~(*)\\&= \sum^m_{j=1}\sum^{n_j}_{i=1}(y_{ij} -\bar
y_j)^2 + \sum^m_{j=1} n_j~(\bar y_j - \beta_0- \beta_1 \bar
x_j)^2\end{aligned}\]
(*) Equality happens because the cross-product term \(\sum^m_{j=1} \sum^{n_j}_{i=1} ~2~(y_{ij} -\bar
y_j) (\bar y_j - \beta_0- \beta_1 \bar x_j) =0\).
The key fact is that the error SS splits into two parts:
\[SS_{error}
= \sum^m_{j=1}\sum^{n_j}_{i=1}(y_{ij} -\bar y_j)^2 + \sum^m_{j=1}
n_j~(\bar y_j - \beta_0- \beta_1 \bar x_j)^2\] - The first term
is the “pure error” SS for \(y_{ij}\)
and relates to the variation in the \(y_{ij}\)s around the group means \(\bar y_j\), and
- the second term is the weighted SS that one gets from pretending all
the \(y_{ij}\)s occur at \(\bar y_j\) .
Note that estimation of \(\beta_0\)
and \(\beta_1\) depends only on the
second term. Therefore the regression coefficients you get from
weighted regression should be exactly the same as those from
handling the whole data.
However the estimate of goodness of fit (\(R^2\)) would be entirely wrong as the
variance is underestimated (missing the first term).
Weighted regression in R
To do a weighted regression we have to specify weights
in the lm()
command. Below we compare the weighted least
squares (WLS) estimates from the averaged data to the
ungrouped ordinary least squares (OLS) estimates from
the raw data.
cps.wls = lm(AvWage ~ EDgroup, weights = N, data = CPS5grouped)
summary(cps.wls)
Call:
lm(formula = AvWage ~ EDgroup, data = CPS5grouped, weights = N)
Weighted Residuals:
Min 1Q Median 3Q Max
-6.944 -3.971 2.699 4.151 9.217
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) -1.60468 1.27442 -1.259 0.234
EDgroup 0.81395 0.09567 8.508 3.62e-06 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 5.467 on 11 degrees of freedom
Multiple R-squared: 0.8681, Adjusted R-squared: 0.8561
F-statistic: 72.39 on 1 and 11 DF, p-value: 3.621e-06
Call:
lm(formula = WG ~ ED, data = CPS5)
Residuals:
Min 1Q Median 3Q Max
-8.068 -3.163 -0.700 2.289 34.709
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) -1.60468 1.10318 -1.455 0.146
ED 0.81395 0.08281 9.829 <2e-16 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 4.733 on 526 degrees of freedom
Multiple R-squared: 0.1552, Adjusted R-squared: 0.1536
F-statistic: 96.6 on 1 and 526 DF, p-value: < 2.2e-16
We see that the coefficient estimates are the same, but the standard
errors and the \(R^2\) are
different.
The following plot shows the averaged data with the unweighted
(dashed) and weighted (solid red) lines. The weighted line is closer to
where more data are.
ggplot(data = CPS5grouped, mapping = aes(x = AvWage, y = EDgroup)) + geom_point(mapping = aes(size = N)) +
geom_smooth(method = "lm", mapping = aes(weight = N), col = "red", se = FALSE) +
geom_smooth(method = "lm", linetype = "dashed", se = FALSE) + labs(x = "Education (years)",
y = "Average wage ($)")
Residuals for Weighted Regression
Clearly a plot of ordinary residuals could be misleading in the
context of weighted least squares. The low weight points on the left are
badly fitted and this could make them look like influential
outliers.
augment(cps.wls, CPS5grouped) |>
ggplot(mapping = aes(x = EDgroup, y = .resid)) + geom_point() + geom_hline(yintercept = 0,
linetype = "dotted") + labs(y = "Simple residuals", x = "Education group (years)")
Instead we use “weighted” or Pearson” Residuals \(r_j = \sqrt{w_j}\varepsilon_j\).
augment(cps.wls, CPS5grouped) |>
mutate(pearson = .resid * sqrt(N)) |>
ggplot(mapping = aes(x = EDgroup, y = pearson)) + geom_point() + geom_hline(yintercept = 0,
linetype = "dotted") + labs(y = "Pearson (weighted) residuals", x = "Education group (years)")
This has given a proper sense of importance to each residual. We see
that the low-weight points on the left are not particularly
important.
Unfortunately the \(Y\) axis scale
is now meaningless so all we can look at is the shape and the
relative importance or lack of fit for each point.
Weighting For Non-Constant Variance
Suppose we have data for which the variance is non-constant. So we
want to give more weight to those points which have small variance (or
are known more precisely) and give less weight to those points that have
bigger variance (or are known less precisely).
This still fits with the idea that Weight represents the amount of
information carried by each datapoint.
Supervisors and Workers
The following data is on the number of supervisors \(Y\) (e.g. team leaders) and the number of
workers \(X\) at a number of industrial
establishments. The data are from the book by Chatterjee and Price
“Regression Analysis by Example”
Download supervisors.csv
## supervisors = read.csv("supervisors.csv")
supervisors
X Y
1 294 30
2 247 32
3 267 37
4 358 44
5 423 47
6 311 49
7 450 56
8 534 62
9 438 68
10 697 78
11 688 80
12 630 84
13 709 88
14 627 97
15 615 100
16 999 109
17 1022 114
18 1015 117
19 700 106
20 850 128
21 980 130
22 1025 160
23 1021 97
24 1200 180
25 1250 112
26 1500 210
27 1650 135
ggplot(supervisors, mapping = aes(x = X, y = Y)) + geom_point() + geom_smooth(method = "lm") +
labs(x = "Number of workers (X)", y = "Number of supervisors (Y)")
Note that the line is overshooting the cluster of points on the left.
The residual plots show curvature and heteroscedasticity. The fourth
graph shows a simple linear model is highly influenced by the points at
the right, which have very large residuals.
sup.lm <- lm(Y ~ X, data = supervisors)
par(mfrow = c(1, 2))
plot(sup.lm, which = c(1, 5))
Considering the heteroscedasticity, look at the amount of vertical
spread of residuals when \(x\)= 400,
800 and 1600. As \(X\) doubles, the
spread roughly doubles. This suggests that \(\mbox{sd}(\varepsilon) \propto x\), or in
other words \(\sigma = k x\) for some
constant \(k\).
Now let us suppose we want to fit a straight-line model \[y_i = \beta_0 + \beta_1 x_i + \varepsilon_i
~ \] We would ordinarily solve this by minimising the \[SS_{error} = \sum( y_i - \beta_0 - \beta_1
x_i)^2\] where the components of the sum (the residuals \(y_i - \beta_0 - \beta_1 x_i\)) have
constant standard deviation. Except now the standard deviation is not
constant.
But what we could do is divide through by \(x_i\). The model becomes \[\begin{aligned}\frac{y_i}{x_i} &=
\frac{\beta_0}{x_i} + \beta_1\frac{x_i}{x_i} +
\frac{\varepsilon_i}{x_i}\\Y_i &=\beta_0 X_i + \beta_1 +
\eta_i\end{aligned}\] where \(Y_i =
\frac{y_i}{x_i}\), \(X_i =
\frac{1}{x_i}\) and \(\eta_i =
\frac{\varepsilon_i}{x_i}\).
The error term \(\eta_i\) now has
constant standard deviation as
\[\mbox{sd}\left(
\frac{\varepsilon_i}{x_i}\right) = \frac{ kx_i}{x_i} = k.\]
We now have a linear model with constant variance so can solve by
least squares in the usual way.
We could do the transformation ourselves and then just use
lm()
normally, but another way to look at it is we’re
minimising
\[\sum \left(\frac{y_i}{x_i} -
\frac{\beta_0}{x_i} - \beta_1 \right)^2 ~~= ~~ \sum
\frac{1}{x_i^2}\left(y_i - \beta_0 -\beta_1 x_i \right)^2\]
which is a weighted regression with \(w_i =
1/ x_i^2\). So the task with this transformation is the same as
weighted least squares with these weights.
sup.wls = lm(Y ~ X, data = supervisors, weights = 1/X^2)
summary(sup.wls)
Call:
lm(formula = Y ~ X, data = supervisors, weights = 1/X^2)
Weighted Residuals:
Min 1Q Median 3Q Max
-0.041477 -0.013852 -0.004998 0.024671 0.035427
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 3.803296 4.569745 0.832 0.413
X 0.120990 0.008999 13.445 6.04e-13 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 0.02266 on 25 degrees of freedom
Multiple R-squared: 0.8785, Adjusted R-squared: 0.8737
F-statistic: 180.8 on 1 and 25 DF, p-value: 6.044e-13
ggplot(data = supervisors, mapping = aes(x = X, y = Y)) + geom_point() + geom_smooth(method = "lm",
mapping = aes(weight = 1/X^2), col = "red", se = FALSE) + geom_smooth(method = "lm",
linetype = "dotted", se = FALSE) + labs(x = "Number of workers (X)", y = "Number of supervisors (Y)")
The plot shows that weighted line (red, solid) is now going closer to
the data at the left, where the random error is smallest, whereas the
OLS line (dotted) overshot the data at left.
The plot of weighted residuals below shows constant variance (with
perhaps a little curvature).
augment(sup.wls) |>
mutate(pearson = .resid/X) |>
ggplot(mapping = aes(x = X, y = pearson)) + geom_point() + geom_hline(yintercept = 0,
linetype = "dotted")
labs(y = "Pearson (weighted) residuals", x = "Number of workers (X)")
$y
[1] "Pearson (weighted) residuals"
$x
[1] "Number of workers (X)"
attr(,"class")
[1] "labels"
Basic rule for Weighting
Assume weights \[w_i =
\frac{n_i}{\mbox{variance}_i} ~~~~~ (6) \]
where, if \(n\) doesn’t vary between
data rows then just replace by \(n=1\),
and if the precision of data (1/variance) does not differ between data
rows then just drop that term.
Revisiting the CPS5grouped data
For these data, there was a column of standard deviations
(StDev
) as well as sample sizes N
. So we could
use weights \(W_i = N_i/
\mbox{StDev}_i^2\)
CPS5weighted <- CPS5grouped |> mutate(Wt = N/ StDev^2)
cps.wls2 <- lm(AvWage ~ EDgroup, weights=Wt, data=CPS5weighted)
summary(cps.wls2)
Call:
lm(formula = AvWage ~ EDgroup, data = CPS5weighted, weights = Wt)
Weighted Residuals:
Min 1Q Median 3Q Max
-1.4367 0.1153 0.7399 1.0895 1.7058
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 0.26605 0.93427 0.285 0.781
EDgroup 0.65602 0.07828 8.381 4.19e-06 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 1.21 on 11 degrees of freedom
Multiple R-squared: 0.8646, Adjusted R-squared: 0.8523
F-statistic: 70.24 on 1 and 11 DF, p-value: 4.186e-06
ggplot(data = CPS5weighted, mapping = aes(x = EDgroup, y = AvWage)) + geom_point(mapping = aes(size = N)) +
geom_smooth(method = "lm", mapping = aes(weight = Wt), col = "red", se = FALSE) +
geom_smooth(method = "lm", mapping = aes(weight = N), linetype = "dotted", se = FALSE) +
labs(x = "Education group (years)", y = "Average Wage ($)")
The standard deviations are much higher for the highly-educated
groups, so that counteracts some of the effect of higher sample
sizes.
LS0tDQp0aXRsZTogIkxlY3R1cmUgMzA6IFdlaWdodGVkIHJlZ3Jlc3Npb24iDQpzdWJ0aXRsZTogMTYxLjI1MSBSZWdyZXNzaW9uIE1vZGVsbGluZw0KYXV0aG9yOiAiUHJlc2VudGVkIGJ5IEpvbmF0aGFuIE1hcnNoYWxsIDxKLkMubWFyc2hhbGxAbWFzc2V5LmFjLm56PiIgIA0KZGF0ZTogIldlZWsgMTEgb2YgU2VtZXN0ZXIgMiwgYHIgbHVicmlkYXRlOjp5ZWFyKGx1YnJpZGF0ZTo6bm93KCkpYCINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQogICAgdGhlbWU6IHlldGkNCiAgICBoaWdobGlnaHQ6IHRhbmdvDQogIGh0bWxfbm90ZWJvb2s6DQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KICAgIHRoZW1lOiB5ZXRpDQogICAgaGlnaGxpZ2h0OiB0YW5nbw0KICBpb3NsaWRlc19wcmVzZW50YXRpb246DQogICAgd2lkZXNjcmVlbjogdHJ1ZQ0KICAgIHNtYWxsZXI6IHRydWUNCiAgd29yZF9kb2N1bWVudDogZGVmYXVsdA0KICBzbGlkeV9wcmVzZW50YXRpb246IA0KICAgIHRoZW1lOiB5ZXRpDQogICAgaGlnaGxpZ2h0OiB0YW5nbw0KICBwZGZfZG9jdW1lbnQ6IGRlZmF1bHQNCi0tLQ0KDQoNCg0KDQo8IS0tLSBEYXRhIGlzIG9uDQpodHRwczovL3ItcmVzb3VyY2VzLm1hc3NleS5hYy5uei9kYXRhLzE2MTI1MS8NCi0tLT4NCg0KYGBge3Igc2V0dXAsIHB1cmw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9DQpsaWJyYXJ5KGtuaXRyKQ0Kb3B0c19jaHVuayRzZXQoZGV2PWMoInBuZyIsICJwZGYiKSkNCm9wdHNfY2h1bmskc2V0KGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTcsIGZpZy5wYXRoPSJGaWd1cmVzLyIsIGZpZy5hbHQ9InVubGFiZWxsZWQiKQ0Kb3B0c19jaHVuayRzZXQoY29tbWVudD0iIiwgZmlnLmFsaWduPSJjZW50ZXIiLCB0aWR5PVRSVUUpDQpvcHRpb25zKGtuaXRyLmthYmxlLk5BID0gJycpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkoYnJvb20pDQpgYGANCg0KDQo8IS0tLSBEbyBub3QgZWRpdCBhbnl0aGluZyBhYm92ZSB0aGlzIGxpbmUuIC0tLT4NCgojIyBJbnRyb2R1Y3Rpb24KCi0gV2VpZ2h0aW5nIGJ5IHNhbXBsZSBzaXplLgotIEludGVycHJldGF0aW9uIG9mIG91dHB1dC4KLSBXZWlnaHRpbmcgdG8gY291bnRlciBoZXRlcm9zY2VkYXN0aWNpdHkuCgpUaGUga2V5IGlkZWEgaW4gdGhpcyB0b3BpYyBpcyB0aGF0IG5vdCBhbGwgZGF0YXBvaW50cyBpbiB0aGUgZGF0YXNldCBuZWVkIGhhdmUgdGhlIHNhbWUgaW5mb3JtYXRpb24gdmFsdWUuICBTb21lIGRhdGEgcm93cyBtYXkgY29udGFpbiB2ZXJ5IHByZWNpc2UgZGF0YSwgd2hpbGUgZGF0YSBpbiB0aGUgb3RoZXIgcm93cyBtYXkgYmUgbGVzcyBwcmVjaXNlLgoKV2UgdGhlcmVmb3JlIGFkanVzdCBvdXIgcmVncmVzc2lvbiBhbmFseXNpcyB0byBnaXZlIG1vcmUgIndlaWdodCIgdG8gdGhvc2UgZGF0YXBvaW50cyB3aGljaCBhcmUgbW9yZSByZWxpYWJsZSBvciBpbmZvcm1hdGl2ZS4gCgojIyBXZWlnaHRpbmcgYnkgU2FtcGxlIFNpemUgCgpUaGUgQ1BTNSBkYXRhc2V0IGlzIGZhbW91cyBpbiBlY29ub21ldHJpY3MsIHJlZmVycmVkIHRvIG9mdGVuIGluIHRoZSBib29rICJJbnRyb2R1Y3RvcnkgRWNvbm9tZXRyaWNzIiBieSBHb2xkYmVyZ2VyLiAKClRoZSBkYXRhc2V0IGhhcyBpbmRpdmlkdWFsLWxldmVsIGRhdGEgb24gNTI4IGVtcGxveWVlcyBgRURgdWNhdGlvbiBsZXZlbCwgd2hldGhlciBvciBub3QgdGhleSBhcmUgaW4gdGhlIGBTT2B1dGggb2YgdGhlIFVuaXRlZCBTdGF0ZXMsIGBCTGA9d2hldGhlciB0aGUgcGVyc29uIHdhcyBub24td2hpdGUgYW5kIG5vbi1IaXNwYW5pYyAxLCBvdGhlcndpc2UgMCwgYEhQYD0gd2hldGhlciBvciBub3QgdGhlIHBlcnNvbiB3YXMgSGlzcGFuaWMsIHdoZXRoZXIgb3Igbm90IHRoZSBlbXBsb3llZSB3YXMgYEZFYG1hbGUsICB5ZWFycyBvZiB3b3JrIGBFWGBwZXJpZW5jZSwgYE1TYD0gbWFyaXRhbCBzdGF0dXMgKDE9bWFycmllZCwgMCBub3QpLCAgd2hldGhlciBvciBub3QgdGhleSBhcmUgbWVtYmVycyBvZiBhIGBVTmBpb24sIGFuZCBgV0dgPWhvdXJseSB3YWdlLgoKR29sZGJlcmdlciBiZWdpbnMgaGlzIHRleHQgYnkgY2FsY3VsYXRpbmcgdGhlIGF2ZXJhZ2UgYFdHYCB2YWx1ZSBmb3IgaW5kaXZpZHVhbHMgYXQgdGhlIHZhcmlvdXMgbGV2ZWxzIG9mIGVkdWNhdGlvbi4gICg2IHllYXJzIHRvIDE4IHllYXJzIG9mIGVkdWNhdGlvbikuICBOb3RlIHRoYXQgbW9zdCBwZW9wbGUgaGFkIDEyLCAxNCBvciAxNiB5ZWFycyBvZiBlZHVjYXRpb24sIGNvcnJlc3BvbmRpbmcgdG8gaGlnaC1zY2hvb2wgZ3JhZHVhdGVzLCBjb2xsZWdlIGdyYWR1YXRlcyBhbmQgdW5pdmVyc2l0eSBncmFkdWF0ZXMgcmVzcGVjdGl2ZWx5LgoKR29sZGJlcmdlciB0aGVuIHJlZ3Jlc3NlcyB0aGUgYXZlcmFnZSBgV0dgIG9uIGBFRGAuIAoKIyMKCmByIHhmdW46OmVtYmVkX2ZpbGUoIi4uLy4uL2RhdGEvQ1BTNWdyb3VwZWQuY3N2IilgCgpgYGB7ciByZWFkIENQUzVncm91cGVkLCBldmFsPS0xLCBlY2hvPS0yfQpDUFM1Z3JvdXBlZCA9cmVhZC5jc3YoIkNQUzVncm91cGVkLmNzdiIsaGVhZGVyPVRSVUUpCkNQUzVncm91cGVkID1yZWFkLmNzdigiLi4vLi4vZGF0YS9DUFM1Z3JvdXBlZC5jc3YiKQpDUFM1Z3JvdXBlZApgYGAKCiMjCgpgYGB7ciwgbWVzc2FnZT1GQUxTRX0KZ2dwbG90KGRhdGE9Q1BTNWdyb3VwZWQsCiAgICAgICBtYXBwaW5nPWFlcyh4PUVEZ3JvdXAsIHk9QXZXYWdlKSkgKwogIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyhzaXplID0gTikpICsKICBnZW9tX3Ntb290aChtZXRob2Q9J2xtJykgKwogIGxhYnMoeD0iRWR1Y2F0aW9uIChob3VycykiLCB5ID0gIkF2ZXJhZ2Ugd2FnZSAoJCkiKQpgYGAKCiMjCgpUaGUgZGF0YSBhcHBlYXJzIHRvIGJlIHF1aXRlIGNsb3NlIHRvIHRoZSBsaW5lLCB3aGljaCBvZnRlbiBoYXBwZW5zIHdoZW4gd2UgaGF2ZSBkYXRhIHdoaWNoIGhhdmUgYmVlbiBhdmVyYWdlZC4gVGhlIHJlZ3Jlc3Npb24gc3VnZ2VzdHMgd2UgaGF2ZSBhIGhpZ2ggJFJeMiA+MC45JC4gICAKCmBgYHtyIGdyb3VwZWQgc3VtbWFyeX0KZ3JvdXBlZC5sbSA8LSBsbShBdldhZ2UgfiBFRGdyb3VwLCBkYXRhPUNQUzVncm91cGVkKQpzdW1tYXJ5KGdyb3VwZWQubG0pCmBgYAoKIyMKCkhvd2V2ZXIgbm90ZSB0aGF0IHRoZSBsZWZ0LWhhbmQgcG9pbnRzIHJlcHJlc2VudCBvbmx5IGEgZmV3IGluZGl2aWR1YWxzIGNvbXBhcmVkIHRvIHNvbWUgb2YgdGhlIHJpZ2h0LWhhbmQgcG9pbnRzLCByZXByZXNlbnRlZCBieSB0aGUgc2l6ZSBvZiB0aGUgY2lyY2xlcyBzY2FsaW5nIHdpdGggdGhlIG51bWJlciBvZiBpbmRpdmlkdWFscyAkTiQgY29udHJpYnV0aW5nIHRvIHRoZSBhdmVyYWdlLiBXZSB3b3VsZCBsaWtlIHRoZSByZWdyZXNzaW9uIGxpbmUgdG8gYmUgY2xvc2VyIHRvIHRob3NlIHBvaW50cyB0aGF0IHJlcHJlc2VudCBmYXIgbW9yZSBwZW9wbGUuCgpUaGUgbGluZSBpcyBjbG9zZSB0byB0aGUgZmlyc3QgY2F0ZWdvcnkgKHdoaWNoIGhhcyBvbmx5IHRocmVlIHBlb3BsZSBpbiBpdCkgYW5kIGZhciBmcm9tIHRoZSAxMiwgMTQsIDE2LXllYXIgY2F0ZWdvcmllcyB3aGljaCBoYXZlIHRoZSBtb3N0IHBlb3BsZS4gVGhpcyBkb2Vzbid0IHJlYWxseSBtYWtlIHNlbnNlLiAKCklmIHdlIHVzZSB0aGUgdW5ncm91cGVkIGRhdGEgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIFdhZ2UgYW5kIEVkdWNhdGlvbiBpcyBtdWNoIHdlYWtlciBpbiB0ZXJtcyBvZiBnb29kbmVzcyBvZiBmaXQgb2YgdGhlIG1vZGVsLgoKIyMKCmByIHhmdW46OmVtYmVkX2ZpbGUoIi4uLy4uL2RhdGEvQ1BTNS5jc3YiKWAKCmBgYHtyIHJlYWQgQ1BTNSwgZXZhbD0tMSwgZWNobz0tMn0KQ1BTNSA9IHJlYWQuY3N2KCJDUFM1LmNzdiIsIGhlYWRlcj1UUlVFKQpDUFM1ID0gcmVhZC5jc3YoIi4uLy4uL2RhdGEvQ1BTNS5jc3YiLCBoZWFkZXI9VFJVRSkKaGVhZChDUFM1KTsKYGBgCgojIwoKYGBge3J9CmdncGxvdChkYXRhPUNQUzUsCiAgICAgICBtYXBwaW5nPWFlcyh4PUVELCB5PVdHKSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kPSdsbScpICsKICBsYWJzKHg9IkVkdWNhdGlvbiAoeWVhcnMpIiwgeT0iV2FnZSAoJCkiKQpgYGAKCiMjCgpgYGB7cn0KY3BzNS5sbSAgPSBsbShXRyB+IEVELCBkYXRhPUNQUzUpCnN1bW1hcnkoY3BzNS5sbSkKYGBgCgpXZSBzZWUgdGhlcmUgaXMgYW4gb3V0bGllciwgd2hpY2ggY291bGQgYmUgYSB0eXBvIChkZWNpbWFsIHBvaW50IGluIHRoZSB3cm9uZyBwbGFjZT8pIEhvd2V2ZXIgdGhlIG1haW4gZm9jdXMgaGVyZSBpcyB0aGF0IHRoZXJlIGlzIGEgbG90IG1vcmUgdmFyaWF0aW9uIGFtb25nIHRoZSBpbmRpdmlkdWFsIGRhdGEgdGhhbiB0aGVyZSBpcyBhbW9uZyB0aGUgYXZlcmFnZWQgZGF0YSwgc28gdGhlICRSXjIkIGlzIG11Y2ggbG93ZXIuIEFsc28gdGhlIHJlZ3Jlc3Npb24gY29lZmZpY2llbnRzIGhhdmUgZGlmZmVyZW50IHZhbHVlcy4gCgojIyBXaHkgR3JvdXBlZCBSZWdyZXNzaW9uIFJlc3VsdHMgRGlmZmVyCgoqRm9ybXVsYXMgbm90IGV4YW1pbmFibGUgYnV0IGNvbmNsdXNpb24gaXMgaW1wb3J0YW50LioKClRvIGV4cGxhaW4gdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGVzZSB0d28gcmVncmVzc2lvbnMsIHdlIGNvbnNpZGVyIHRoZSByZWdyZXNzaW9uIG9mICRZJCB2cyAkWCQgICgkbiQgZGF0YSkgIGFuZCAkXGJhciBZJCB2ZXJzdXMgJFxiYXIgWCQgICAod2hlcmUgdGhlIGRhdGEgaGF2ZSBiZWVuIGRpdmlkZWQgaW50byAgJG0kIGdyb3VwcyAgLSBhIG11Y2ggc21hbGxlciBudW1iZXIgdGhhbiAkbiQuKQpJbiBMZWFzdCBTcXVhcmVzIHdlIGNob29zZSAkXGJldGFfMCQgYW5kICRcYmV0YV8xJCAgdG8gbWluaW1pc2UgICAgICAgICAgCiQkU1Nfe2Vycm9yfSA9ICBcc3VtXm5fe2k9MX0oeV9pIC0gXGJldGFfMC0gXGJldGFfMSB4X2kpXjIgfn5+fiAgICgxKSAgJCQKSG93ZXZlciB0aGUgcmVncmVzc2lvbiBmb3IgdGhlIGF2ZXJhZ2VzIHRyZWF0cyBhbGwgJG0kIGdyb3VwcyBhcyBiZWluZyBvZiBlcXVhbCB3b3J0aDoKICQkIFxzdW1ebV97aj0xfShcYmFyIHlfaiAtIFxiZXRhXzAtIFxiZXRhXzEgXGJhciB4X2opXjIgfn5+fn5+ICAgKDIpICAkJCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIApUaGF0IGlzLCAgdGhlIGNvbXB1dGF0aW9ucyB3aWxsIHRyeSBqdXN0IGFzIGhhcmQgdG8gZml0IGEgdmFsdWUgdGhhdCBhcmlzZXMgZnJvbSBhIHNtYWxsIGdyb3VwIG9mIGRhdGEgYXMgYSAgdmFsdWUgZnJvbSBhIGxhcmdlIGdyb3VwLiAKCiMjIEhvdyBhcmUgdGhlc2UgdHdvIHN1bXMgb2Ygc3F1YXJlcyByZWxhdGVkPyAgCgpJZiB3ZSB0YWtlIHRoZSBzdW0gaW4gKDEpIGFuZCByZS1vcmRlciBpdCBpbnRvIEVkdWNhdGlvbiBsZXZlbCBncm91cHMgd2UgZ2V0CiQkU1Nfe2Vycm9yfSA9ICBcc3VtXm1fe2o9MX1cc3VtXntuX2p9X3tpPTF9KHlfe2lqfSAtIFxiZXRhXzAtIFxiZXRhXzEgeF97aWp9KV4yIH5+fn4gICAoMykgICQkCiAKTm93IHN1cHBvc2UgZm9yIGEgbW9tZW50IHdlIHByZXRlbmQgdGhhdCBhbGwgdGhlICR5X3tpan0kIHZhbHVlcyBhcmUgdGhlIHNhbWUgYXMgdGhlIGdyb3VwIG1lYW4gJFxiYXIgeV9qJCAgIChhbmQgbm90ZSB0aGF0IGFscmVhZHkgJHhfe2lqfSA9IFxiYXIgeF9qJCApLiAgVGhlbiB0aGUgc3VtIG9uIHRoZSByaWdodC1oYW5kIHNpZGUgcmVkdWNlcyB0byAKJCRTU197ZXJyb3J9ID0gIFxzdW1ebV97aj0xfSAgbl9qKFxiYXIgeV97IGp9IC0gXGJldGFfMCAtIFxiZXRhXzEgXGJhciB4X2opXjIgfn5+fiAgICg0KSAgJCQKCgpTbyB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoaXMgc3VtIGFuZCB0aGUgc3VtICgyKSBpcyB0aGF0IHRoZSBzcXVhcmVkIHJlc2lkdWFsICBpcyBtdWx0aXBsaWVkIGJ5ICAkbl9qJCwgIHRoZSBudW1iZXIgb2YgcGVvcGxlIGluIHRoYXQgZ3JvdXAuICBXZSBjYWxsIHRoaXMgbXVsdGlwbGllciB0aGUgKip3ZWlnaHQqKiAgJHdfaiQuCgpFc3NlbnRpYWxseSB3ZSBpbnRlcnByZXQgdGhlIHdlaWdodCBhcyB0aGUgKiphbW91bnQgb2YgaW5mb3JtYXRpb24gcmVwcmVzZW50ZWQgYnkgdGhlIGRhdGFwb2ludCoqLiAgU28gaGVyZSBpbiB0aGlzIHNpbXBsZSBleGFtcGxlICJhbW91bnQgb2YgaW5mb3JtYXRpb24iIGlzIGVhc2lseSBtZWFzdXJlZCBhcyB0aGUgc2FtcGxlIHNpemUgJHdfaiA9IG5faiQgYXQgZWFjaCBlZHVjYXRpb24gbGV2ZWwuICAgICAgIAoKIyMKCkJ1dCBpbiBmYWN0IHdlIGRvbid0IGhhdmUgYWxsIHRoZSAkeV97aWp9JCBlcXVhbC4gSW5zdGVhZCB3aGF0IHdlIGRvIGlzIHRvIGFkZCBhbmQgc3VidHJhY3QgJFxiYXIgeV9qJCBpbnNpZGUgdGhlIHBhcmVudGhlc2VzIGluIGVxdWF0aW9uICgzKSwgYW5kIHRoZW4gZXhwYW5kIHRoZSBzcXVhcmU6CiQkXGJlZ2lue2FsaWduZWR9U1Nfe2Vycm9yfSAmPSAgXHN1bV5tX3tqPTF9XHN1bV57bl9qfV97aT0xfSh5X3tpan0gLVxiYXIgeV9qICsgXGJhciB5X2ogLSBcYmV0YV8wLSBcYmV0YV8xIHhfe2lqfSleMlxcJj0gIFxzdW1ebV97aj0xfVxzdW1ee25fan1fe2k9MX0oeV97aWp9IC1cYmFyIHlfaileMiAgKyBcc3VtXm1fe2o9MX1cc3VtXntuX2p9X3tpPTF9KFxiYXIgeV9qIC0gXGJldGFfMC0gXGJldGFfMSBcYmFyIHhfaileMiB+fn5+figqKVxcJj0gIFxzdW1ebV97aj0xfVxzdW1ee25fan1fe2k9MX0oeV97aWp9IC1cYmFyIHlfaileMiAgKyBcc3VtXm1fe2o9MX0gbl9qfihcYmFyIHlfaiAtIFxiZXRhXzAtIFxiZXRhXzEgXGJhciB4X2opXjJcZW5ke2FsaWduZWR9JCQKCigqKSBFcXVhbGl0eSBoYXBwZW5zIGJlY2F1c2UgdGhlIGNyb3NzLXByb2R1Y3QgdGVybSAkXHN1bV5tX3tqPTF9IFxzdW1ee25fan1fe2k9MX0gfjJ+KHlfe2lqfSAtXGJhciB5X2opIChcYmFyIHlfaiAtIFxiZXRhXzAtIFxiZXRhXzEgXGJhciB4X2opID0wJC4gCgojIwoKVGhlIGtleSBmYWN0IGlzIHRoYXQgdGhlIGVycm9yIFNTIHNwbGl0cyBpbnRvIHR3byBwYXJ0czogCgokJFNTX3tlcnJvcn0gPSAgXHN1bV5tX3tqPTF9XHN1bV57bl9qfV97aT0xfSh5X3tpan0gLVxiYXIgeV9qKV4yICArIFxzdW1ebV97aj0xfSBuX2p+KFxiYXIgeV9qIC0gXGJldGFfMC0gXGJldGFfMSBcYmFyIHhfaileMiQkCi0JVGhlIGZpcnN0IHRlcm0gaXMgdGhlICJwdXJlIGVycm9yIiBTUyBmb3IgJHlfe2lqfSQgYW5kIHJlbGF0ZXMgdG8gdGhlIHZhcmlhdGlvbiBpbiB0aGUgJHlfe2lqfSRzIGFyb3VuZCB0aGUgZ3JvdXAgbWVhbnMgJFxiYXIgeV9qJCwgYW5kIAoKLQl0aGUgc2Vjb25kIHRlcm0gaXMgdGhlIHdlaWdodGVkIFNTIHRoYXQgb25lIGdldHMgZnJvbSBwcmV0ZW5kaW5nIGFsbCB0aGUgJHlfe2lqfSRzICAgb2NjdXIgYXQgJFxiYXIgeV9qJCAuCgpOb3RlIHRoYXQgZXN0aW1hdGlvbiBvZiAkXGJldGFfMCQgYW5kICRcYmV0YV8xJCAqZGVwZW5kcyBvbmx5IG9uIHRoZSBzZWNvbmQgdGVybSouICAgVGhlcmVmb3JlICB0aGUgcmVncmVzc2lvbiBjb2VmZmljaWVudHMgeW91IGdldCBmcm9tIHdlaWdodGVkIHJlZ3Jlc3Npb24gc2hvdWxkIGJlICpleGFjdGx5IHRoZSBzYW1lKiBhcyB0aG9zZSBmcm9tIGhhbmRsaW5nIHRoZSB3aG9sZSBkYXRhLgoKSG93ZXZlciB0aGUgZXN0aW1hdGUgb2YgZ29vZG5lc3Mgb2YgZml0ICgkUl4yJCkgd291bGQgYmUgZW50aXJlbHkgd3JvbmcgYXMgdGhlIHZhcmlhbmNlIGlzIHVuZGVyZXN0aW1hdGVkIChtaXNzaW5nIHRoZSBmaXJzdCB0ZXJtKS4KCiMjIFdlaWdodGVkIHJlZ3Jlc3Npb24gaW4gUgoKVG8gZG8gYSB3ZWlnaHRlZCByZWdyZXNzaW9uIHdlIGhhdmUgdG8gc3BlY2lmeSBgd2VpZ2h0c2AgaW4gdGhlIGBsbSgpYCBjb21tYW5kLiAgIEJlbG93IHdlIGNvbXBhcmUgdGhlIHdlaWdodGVkIGxlYXN0IHNxdWFyZXMgKCoqV0xTKiopIGVzdGltYXRlcyBmcm9tIHRoZSBhdmVyYWdlZCBkYXRhIHRvIHRoZSB1bmdyb3VwZWQgb3JkaW5hcnkgbGVhc3Qgc3F1YXJlcyAoKipPTFMqKikgZXN0aW1hdGVzIGZyb20gdGhlIHJhdyBkYXRhLgoKYGBge3Igd2VpZ2h0ZWR9CmNwcy53bHMgPSBsbShBdldhZ2UgfiBFRGdyb3VwLCB3ZWlnaHRzPU4sIGRhdGE9Q1BTNWdyb3VwZWQpCnN1bW1hcnkoY3BzLndscykKYGBgCgojIwoKYGBge3J9CnN1bW1hcnkoY3BzNS5sbSkKYGBgCgpXZSBzZWUgdGhhdCB0aGUgY29lZmZpY2llbnQgZXN0aW1hdGVzIGFyZSB0aGUgc2FtZSwgYnV0IHRoZSBzdGFuZGFyZCBlcnJvcnMgYW5kIHRoZSAkUl4yJCBhcmUgZGlmZmVyZW50LiAKClRoZSBmb2xsb3dpbmcgcGxvdCBzaG93cyB0aGUgYXZlcmFnZWQgZGF0YSB3aXRoIHRoZSB1bndlaWdodGVkIChkYXNoZWQpIGFuZCB3ZWlnaHRlZCAoc29saWQgcmVkKSBsaW5lcy4gVGhlIHdlaWdodGVkIGxpbmUgaXMgY2xvc2VyIHRvIHdoZXJlIG1vcmUgZGF0YSBhcmUuIAoKIyMKCmBgYHtyIFdMUyB2cyBPTFMsIG1lc3NhZ2U9RkFMU0V9CmdncGxvdChkYXRhPUNQUzVncm91cGVkLAogICAgICAgbWFwcGluZz1hZXMoeD1BdldhZ2UsIHk9RURncm91cCkpICsKICBnZW9tX3BvaW50KG1hcHBpbmc9YWVzKHNpemU9TikpICsKICBnZW9tX3Ntb290aChtZXRob2Q9J2xtJywgbWFwcGluZz1hZXMod2VpZ2h0PU4pLCBjb2w9J3JlZCcsIHNlPUZBTFNFKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kPSdsbScsIGxpbmV0eXBlID0gJ2Rhc2hlZCcgLCBzZT1GQUxTRSkgKwogIGxhYnMoeD0iRWR1Y2F0aW9uICh5ZWFycykiLCB5PSJBdmVyYWdlIHdhZ2UgKCQpIikKYGBgCgojIyBSZXNpZHVhbHMgZm9yIFdlaWdodGVkIFJlZ3Jlc3Npb24KCkNsZWFybHkgYSBwbG90IG9mIG9yZGluYXJ5IHJlc2lkdWFscyBjb3VsZCBiZSBtaXNsZWFkaW5nIGluIHRoZSBjb250ZXh0IG9mIHdlaWdodGVkIGxlYXN0IHNxdWFyZXMuICBUaGUgbG93IHdlaWdodCBwb2ludHMgb24gdGhlIGxlZnQgYXJlIGJhZGx5IGZpdHRlZCBhbmQgdGhpcyBjb3VsZCBtYWtlIHRoZW0gbG9vayBsaWtlIGluZmx1ZW50aWFsIG91dGxpZXJzLiAKIApgYGB7ciByZXNpZHVhbHMgZnJvbSBXTFMsIGZpZy5oZWlnaHQ9NH0KYXVnbWVudChjcHMud2xzLCBDUFM1Z3JvdXBlZCkgfD4KICBnZ3Bsb3QobWFwcGluZyA9IGFlcyh4PUVEZ3JvdXAsIHk9LnJlc2lkKSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0PTAsIGxpbmV0eXBlPSJkb3R0ZWQiKSArCiAgbGFicyh5PSJTaW1wbGUgcmVzaWR1YWxzIiwgeD0iRWR1Y2F0aW9uIGdyb3VwICh5ZWFycykiKQpgYGAKCiMjCgpJbnN0ZWFkIHdlIHVzZSAid2VpZ2h0ZWQiIG9yICBQZWFyc29uIiBSZXNpZHVhbHMgJHJfaiA9IFxzcXJ0e3dfan1cdmFyZXBzaWxvbl9qJC4KIApgYGB7ciB3ZWlnaHRlZCByZXNpZHVhbHMsIGZpZy5oZWlnaHQ9NC41fQphdWdtZW50KGNwcy53bHMsIENQUzVncm91cGVkKSB8PgogIG11dGF0ZShwZWFyc29uID0gLnJlc2lkKnNxcnQoTikpIHw+CiAgZ2dwbG90KG1hcHBpbmcgPSBhZXMoeD1FRGdyb3VwLCB5PXBlYXJzb24pKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9MCwgbGluZXR5cGU9ImRvdHRlZCIpICsKICBsYWJzKHk9IlBlYXJzb24gKHdlaWdodGVkKSByZXNpZHVhbHMiLCB4PSJFZHVjYXRpb24gZ3JvdXAgKHllYXJzKSIpCmBgYAoKIyMKClRoaXMgaGFzIGdpdmVuIGEgcHJvcGVyIHNlbnNlIG9mIGltcG9ydGFuY2UgdG8gZWFjaCByZXNpZHVhbC4gV2Ugc2VlIHRoYXQgdGhlIGxvdy13ZWlnaHQgcG9pbnRzIG9uIHRoZSBsZWZ0IGFyZSBub3QgcGFydGljdWxhcmx5IGltcG9ydGFudC4gCgpVbmZvcnR1bmF0ZWx5IHRoZSAkWSQgYXhpcyBzY2FsZSBpcyBub3cgbWVhbmluZ2xlc3Mgc28gYWxsIHdlIGNhbiBsb29rIGF0IGlzIHRoZSAqc2hhcGUqIGFuZCB0aGUgKnJlbGF0aXZlKiBpbXBvcnRhbmNlIG9yIGxhY2sgb2YgZml0IGZvciBlYWNoIHBvaW50LiAKCiMjIFdlaWdodGluZyBGb3IgTm9uLUNvbnN0YW50IFZhcmlhbmNlCgpTdXBwb3NlIHdlIGhhdmUgZGF0YSBmb3Igd2hpY2ggdGhlIHZhcmlhbmNlIGlzIG5vbi1jb25zdGFudC4gClNvIHdlIHdhbnQgdG8gZ2l2ZSBtb3JlIHdlaWdodCB0byB0aG9zZSBwb2ludHMgd2hpY2ggaGF2ZSBzbWFsbCB2YXJpYW5jZSAob3IgYXJlIGtub3duIG1vcmUgcHJlY2lzZWx5KSAgYW5kIGdpdmUgbGVzcyB3ZWlnaHQgdG8gdGhvc2UgcG9pbnRzIHRoYXQgaGF2ZSBiaWdnZXIgdmFyaWFuY2UgIChvciBhcmUga25vd24gbGVzcyBwcmVjaXNlbHkpLgoKVGhpcyBzdGlsbCBmaXRzIHdpdGggdGhlIGlkZWEgdGhhdCBXZWlnaHQgcmVwcmVzZW50cyB0aGUgIGFtb3VudCBvZiBpbmZvcm1hdGlvbiBjYXJyaWVkIGJ5IGVhY2ggZGF0YXBvaW50LiAKCiMjIyBTdXBlcnZpc29ycyBhbmQgV29ya2VycwoKVGhlIGZvbGxvd2luZyBkYXRhIGlzIG9uIHRoZSBudW1iZXIgb2Ygc3VwZXJ2aXNvcnMgJFkkIChlLmcuIHRlYW0gbGVhZGVycykgYW5kIHRoZSBudW1iZXIgb2Ygd29ya2VycyAkWCQgYXQgYSBudW1iZXIgb2YgaW5kdXN0cmlhbCBlc3RhYmxpc2htZW50cy4gICBUaGUgZGF0YSBhcmUgZnJvbSB0aGUgYm9vayBieSBDaGF0dGVyamVlIGFuZCBQcmljZSAiUmVncmVzc2lvbiBBbmFseXNpcyBieSBFeGFtcGxlIgoKYHIgeGZ1bjo6ZW1iZWRfZmlsZSgiLi4vLi4vZGF0YS9zdXBlcnZpc29ycy5jc3YiKWAKCmBgYHtyIHJlYWQgU3VwZXJ2aXNvcnMsIGV2YWw9LTEsIGVjaG89LTJ9CnN1cGVydmlzb3JzID0gcmVhZC5jc3YoInN1cGVydmlzb3JzLmNzdiIpCnN1cGVydmlzb3JzID0gcmVhZC5jc3YoIi4uLy4uL2RhdGEvc3VwZXJ2aXNvcnMuY3N2IikKc3VwZXJ2aXNvcnMKYGBgCgojIwoKYGBge3IsIG1lc3NhZ2U9RkFMU0V9CmdncGxvdChzdXBlcnZpc29ycywKICAgICAgIG1hcHBpbmc9YWVzKHg9WCwgeT1ZKSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kPSdsbScpICsKICBsYWJzKHggPSAiTnVtYmVyIG9mIHdvcmtlcnMgKFgpIiwgeSA9ICJOdW1iZXIgb2Ygc3VwZXJ2aXNvcnMgKFkpIikKYGBgCgojIwoKTm90ZSB0aGF0IHRoZSBsaW5lIGlzIG92ZXJzaG9vdGluZyB0aGUgY2x1c3RlciBvZiBwb2ludHMgb24gdGhlIGxlZnQuICBUaGUgcmVzaWR1YWwgcGxvdHMgc2hvdyBjdXJ2YXR1cmUgYW5kIGhldGVyb3NjZWRhc3RpY2l0eS4gIFRoZSBmb3VydGggZ3JhcGggc2hvd3MgYSBzaW1wbGUgbGluZWFyIG1vZGVsIGlzIGhpZ2hseSBpbmZsdWVuY2VkIGJ5IHRoZSBwb2ludHMgYXQgdGhlIHJpZ2h0LCB3aGljaCBoYXZlIHZlcnkgbGFyZ2UgcmVzaWR1YWxzLgoKYGBge3IgU3VwZXJ2aXNvciByZXNpZHVhbHMsIGZpZy5oZWlnaHQ9NH0Kc3VwLmxtIDwtIGxtKFkgfiBYLCBkYXRhPXN1cGVydmlzb3JzKQpwYXIobWZyb3c9YygxLDIpKQpwbG90KHN1cC5sbSwgd2hpY2g9YygxLDUpKQpgYGAKCiMjCgpDb25zaWRlcmluZyB0aGUgaGV0ZXJvc2NlZGFzdGljaXR5LCBsb29rIGF0IHRoZSBhbW91bnQgb2YgdmVydGljYWwgc3ByZWFkIG9mIHJlc2lkdWFscyB3aGVuICR4JD0gNDAwLCA4MDAgYW5kIDE2MDAuIEFzICRYJCBkb3VibGVzLCB0aGUgc3ByZWFkIHJvdWdobHkgZG91Ymxlcy4gIFRoaXMgc3VnZ2VzdHMgdGhhdCAkXG1ib3h7c2R9KFx2YXJlcHNpbG9uKSBccHJvcHRvIHgkLCAgb3IgaW4gb3RoZXIgd29yZHMgJFxzaWdtYSA9IGsgeCQgZm9yIHNvbWUgY29uc3RhbnQgJGskLgoKTm93IGxldCB1cyBzdXBwb3NlIHdlIHdhbnQgdG8gZml0IGEgc3RyYWlnaHQtbGluZSBtb2RlbCAkJHlfaSA9IFxiZXRhXzAgKyBcYmV0YV8xIHhfaSAgKyBcdmFyZXBzaWxvbl9pIH4gICAkJApXZSB3b3VsZCBvcmRpbmFyaWx5IHNvbHZlIHRoaXMgYnkgbWluaW1pc2luZyB0aGUgCiQkU1Nfe2Vycm9yfSA9IFxzdW0oIHlfaSAtIFxiZXRhXzAgLSBcYmV0YV8xIHhfaSleMiQkIHdoZXJlIHRoZSBjb21wb25lbnRzIG9mIHRoZSBzdW0gKHRoZSByZXNpZHVhbHMgJHlfaSAtIFxiZXRhXzAgLSBcYmV0YV8xIHhfaSQpIGhhdmUgY29uc3RhbnQgc3RhbmRhcmQgZGV2aWF0aW9uLiAgRXhjZXB0IG5vdyB0aGUgc3RhbmRhcmQgZGV2aWF0aW9uIGlzIG5vdCBjb25zdGFudC4gCgpCdXQgd2hhdCB3ZSBjb3VsZCBkbyBpcyBkaXZpZGUgdGhyb3VnaCBieSAkeF9pJC4gIFRoZSBtb2RlbCBiZWNvbWVzCiQkXGJlZ2lue2FsaWduZWR9XGZyYWN7eV9pfXt4X2l9ICY9IFxmcmFje1xiZXRhXzB9e3hfaX0gKyBcYmV0YV8xXGZyYWN7eF9pfXt4X2l9ICArIFxmcmFje1x2YXJlcHNpbG9uX2l9e3hfaX1cXFlfaSAmPVxiZXRhXzAgWF9pICsgXGJldGFfMSArIFxldGFfaVxlbmR7YWxpZ25lZH0kJAp3aGVyZSAkWV9pID0gXGZyYWN7eV9pfXt4X2l9JCwgJFhfaSA9IFxmcmFjezF9e3hfaX0kIGFuZCAkXGV0YV9pID0gXGZyYWN7XHZhcmVwc2lsb25faX17eF9pfSQuCgojIwoKVGhlIGVycm9yIHRlcm0gJFxldGFfaSQgbm93IGhhcyBjb25zdGFudCBzdGFuZGFyZCBkZXZpYXRpb24gYXMKCiQkXG1ib3h7c2R9XGxlZnQoIFxmcmFje1x2YXJlcHNpbG9uX2l9e3hfaX1ccmlnaHQpID0gXGZyYWN7IGt4X2l9e3hfaX0gPSBrLiQkCgpXZSBub3cgaGF2ZSBhIGxpbmVhciBtb2RlbCB3aXRoIGNvbnN0YW50IHZhcmlhbmNlIHNvIGNhbiBzb2x2ZSBieSBsZWFzdCBzcXVhcmVzIGluIHRoZSB1c3VhbCB3YXkuCgpXZSBjb3VsZCBkbyB0aGUgdHJhbnNmb3JtYXRpb24gb3Vyc2VsdmVzIGFuZCB0aGVuIGp1c3QgdXNlIGBsbSgpYCBub3JtYWxseSwgYnV0IGFub3RoZXIgd2F5IHRvIGxvb2sgYXQgaXQgaXMgd2UncmUgbWluaW1pc2luZwoKJCRcc3VtIFxsZWZ0KFxmcmFje3lfaX17eF9pfSAtIFxmcmFje1xiZXRhXzB9e3hfaX0gLSBcYmV0YV8xIFxyaWdodCleMiAgIH5+PSB+fiBcc3VtIFxmcmFjezF9e3hfaV4yfVxsZWZ0KHlfaSAtIFxiZXRhXzAgLVxiZXRhXzEgeF9pIFxyaWdodCleMiQkCgp3aGljaCBpcyBhIHdlaWdodGVkIHJlZ3Jlc3Npb24gd2l0aCAkd19pID0gMS8geF9pXjIkLiAgU28gdGhlIHRhc2sgd2l0aCB0aGlzIHRyYW5zZm9ybWF0aW9uIGlzIHRoZSBzYW1lIGFzIHdlaWdodGVkIGxlYXN0IHNxdWFyZXMgd2l0aCB0aGVzZSB3ZWlnaHRzLgoKIyMKCmBgYHtyIHdscyBmb3Igc3VwZXJ2aXNvcnN9CnN1cC53bHMgPSBsbShZIH4gWCAgLCBkYXRhPXN1cGVydmlzb3JzLCB3ZWlnaHRzID0gMS9YXjIgKQpzdW1tYXJ5KHN1cC53bHMpCmBgYAoKIyMKCmBgYHtyLCBtZXNzYWdlPUZBTFNFfQpnZ3Bsb3QoZGF0YT1zdXBlcnZpc29ycywKICAgICAgIG1hcHBpbmc9YWVzKHg9WCwgeT1ZKSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kPSdsbScsIG1hcHBpbmc9YWVzKHdlaWdodD0xL1heMiksIGNvbD0ncmVkJywgc2U9RkFMU0UpICsKICBnZW9tX3Ntb290aChtZXRob2Q9J2xtJywgbGluZXR5cGU9J2RvdHRlZCcsIHNlPUZBTFNFKSArCiAgbGFicyh4PSJOdW1iZXIgb2Ygd29ya2VycyAoWCkiLCB5ID0gIk51bWJlciBvZiBzdXBlcnZpc29ycyAoWSkiKQpgYGAKCiMjCgpUaGUgcGxvdCBzaG93cyB0aGF0IHdlaWdodGVkIGxpbmUgKHJlZCwgc29saWQpIGlzIG5vdyBnb2luZyBjbG9zZXIgdG8gdGhlIGRhdGEgYXQgdGhlIGxlZnQsIHdoZXJlIHRoZSByYW5kb20gZXJyb3IgaXMgc21hbGxlc3QsIHdoZXJlYXMgdGhlIE9MUyBsaW5lIChkb3R0ZWQpIG92ZXJzaG90IHRoZSBkYXRhIGF0IGxlZnQuCgpUaGUgcGxvdCBvZiB3ZWlnaHRlZCByZXNpZHVhbHMgYmVsb3cgc2hvd3MgY29uc3RhbnQgdmFyaWFuY2UgKHdpdGggcGVyaGFwcyBhIGxpdHRsZSBjdXJ2YXR1cmUpLiAKCmBgYHtyIFd0IFJlcyBmb3IgU3VwLndscywgZmlnLmhlaWdodD00fQphdWdtZW50KHN1cC53bHMpIHw+CiAgbXV0YXRlKHBlYXJzb24gPSAucmVzaWQvWCkgfD4KICBnZ3Bsb3QobWFwcGluZyA9IGFlcyh4PVgsIHk9cGVhcnNvbikpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdD0wLCBsaW5ldHlwZT0iZG90dGVkIikKICBsYWJzKHk9IlBlYXJzb24gKHdlaWdodGVkKSByZXNpZHVhbHMiLCB4PSJOdW1iZXIgb2Ygd29ya2VycyAoWCkiKQpgYGAKCiMjIEJhc2ljIHJ1bGUgZm9yIFdlaWdodGluZwoKQXNzdW1lIHdlaWdodHMgICQkd19pID0gXGZyYWN7bl9pfXtcbWJveHt2YXJpYW5jZX1faX0gfn5+fn4gKDYpICQkCgp3aGVyZSwgaWYgJG4kIGRvZXNuJ3QgdmFyeSBiZXR3ZWVuIGRhdGEgcm93cyB0aGVuIGp1c3QgcmVwbGFjZSBieSAkbj0xJCwgIGFuZCBpZiB0aGUgcHJlY2lzaW9uIG9mIGRhdGEgKDEvdmFyaWFuY2UpIGRvZXMgbm90IGRpZmZlciBiZXR3ZWVuIGRhdGEgcm93cyB0aGVuIGp1c3QgZHJvcCB0aGF0IHRlcm0uCgojIyMgQXBwbGljYXRpb246IE1ldGEgQW5hbHlzaXMKCllvdSBtYXkgaGF2ZSBoZWFyZCBvZiB0aGUgIGZpZWxkIG9mICoqbWV0YS1hbmFseXNpcyoqLCB3aGljaCAgaXMgY29uY2VybmVkIHdpdGggY29tYmluaW5nIHRoZSByZXN1bHRzIG9mIG1hbnkgc21hbGwgc3R1ZGllcyBpbiBvcmRlciB0byBnZXQgYW4gb3ZlcmFsbCByZXN1bHQuICAKCk1ldGEtYW5hbHlzaXMgdXNlcyB3ZWlnaHRzIG9mIHRoZSBmb3JtICg2KSB0byByZXByZXNlbnQgdGhlIGRpZmZlcmluZyBhbW91bnRzIGFuZCBxdWFsaXR5IG9mIGRhdGEgZnJvbSB0aGUgZGlmZmVyZW50IHN0dWRpZXMuCgojIyBSZXZpc2l0aW5nIHRoZSBDUFM1Z3JvdXBlZCBkYXRhCgpGb3IgdGhlc2UgZGF0YSwgdGhlcmUgd2FzIGEgY29sdW1uIG9mICBzdGFuZGFyZCBkZXZpYXRpb25zIChgU3REZXZgKSBhcyB3ZWxsIGFzIHNhbXBsZSBzaXplcyBgTmAuICBTbyB3ZSBjb3VsZCB1c2Ugd2VpZ2h0cyAkV19pID0gTl9pLyBcbWJveHtTdERldn1faV4yJAoKYGBge3IgQ1BTNSByZS13ZWlnaHRlZCwgdGlkeT1GQUxTRX0KQ1BTNXdlaWdodGVkIDwtIENQUzVncm91cGVkIHw+IG11dGF0ZShXdCA9IE4vIFN0RGV2XjIpCmNwcy53bHMyIDwtIGxtKEF2V2FnZSB+IEVEZ3JvdXAsIHdlaWdodHM9V3QsIGRhdGE9Q1BTNXdlaWdodGVkKQpzdW1tYXJ5KGNwcy53bHMyKQpgYGAKCiMjCgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgZmlnLmhlaWdodD00fQpnZ3Bsb3QoZGF0YT1DUFM1d2VpZ2h0ZWQsCiAgICAgICBtYXBwaW5nPWFlcyh4PUVEZ3JvdXAsIHk9QXZXYWdlKSkgKwogIGdlb21fcG9pbnQobWFwcGluZz1hZXMoc2l6ZT1OKSkgKwogIGdlb21fc21vb3RoKG1ldGhvZD0nbG0nLCBtYXBwaW5nPWFlcyh3ZWlnaHQ9V3QpLCBjb2w9J3JlZCcsIHNlPUZBTFNFKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kPSdsbScsIG1hcHBpbmc9YWVzKHdlaWdodD1OKSwgbGluZXR5cGU9J2RvdHRlZCcsIHNlPUZBTFNFKSArCiAgbGFicyh4PSJFZHVjYXRpb24gZ3JvdXAgKHllYXJzKSIsIHk9IkF2ZXJhZ2UgV2FnZSAoJCkiKQpgYGAKClRoZSBzdGFuZGFyZCBkZXZpYXRpb25zIGFyZSBtdWNoIGhpZ2hlciBmb3IgdGhlIGhpZ2hseS1lZHVjYXRlZCBncm91cHMsIHNvIHRoYXQgY291bnRlcmFjdHMgc29tZSBvZiB0aGUgZWZmZWN0IG9mIGhpZ2hlciBzYW1wbGUgc2l6ZXMuCg==