library(fpp3)
-- Attaching packages ----------------------------------------- fpp3 0.3 --
v tibble      3.0.6     v feasts      0.1.6
v tsibbledata 0.2.0     v fable       0.2.1
-- Conflicts -------------------------------------------- fpp3_conflicts --
x lubridate::date()     masks base::date()
x dplyr::filter()       masks stats::filter()
x lubridate::interval() masks tsibble::interval()
x dplyr::lag()          masks stats::lag()

https://robjhyndman.com/seminars/uwa2017/

https://robjhyndman.com/seminars/isi2019workshop/

https://github.com/robjhyndman/ETC3550Slides/tree/fable

https://github.com/rstudio-conf-2020/time-series-forecasting/blob/master/materials/labs.R

http://www.gradaanwr.net/

1 Judgmental forecasts

https://otexts.com/fpp3/judgmental.html

In many cases, judgmental forecasting is the only option, such as when there is a complete lack of historical data, or when a new product is being launched, or when a new competitor enters the market, or during completely new and unique market conditions.

Research in this area3 has shown that the accuracy of judgmental forecasting improves when the forecaster has (i) important domain knowledge, and (ii) more timely, up-to-date information. A judgmental approach can be quick to adjust to such changes, information or events.

There are three general settings in which judgmental forecasting is used: (i) there are no available data, so that statistical methods are not applicable and judgmental forecasting is the only feasible approach; (ii) data are available, statistical forecasts are generated, and these are then adjusted using judgment; and (iii) data are available and statistical and judgmental forecasts are generated independently and then combined.

2 Time series regression models

2.1 The linear model

2.1.1 Simple linear regression

\(y_t = \beta_0 + \beta_1 x_t + \varepsilon_t.\)

The coefficients \(β_0\) and \(β_1\) denote the intercept and the slope of the line respectively.

We can think of each observation \(y_t\) as consisting of the systematic or explained part of the model, \(β_0+β_1x_t\), and the random “error,” \(ε_t\). The “error” term does not imply a mistake, but a deviation from the underlying straight line model. It captures anything that may affect \(y_t\) other than \(x_t\).

us_change %>%
  pivot_longer(c(Consumption, Income), names_to = "Series") %>%
  autoplot(value) +
  labs(y = "% change")

us_change %>%
  ggplot(aes(x = Income, y = Consumption)) +
  labs(y = "Consumption (quarterly % change)", x = "Income (quarterly % change)") +
  geom_point() +
  geom_smooth(method = "lm", se = FALSE)

\(\hat{y}_t=0.54 + 0.27x_t.\)

The equation is estimated using the TSLM() function:

us_change %>%
  model(TSLM(Consumption ~ Income)) %>%
  report()
Series: Consumption 
Model: TSLM 

Residuals:
     Min       1Q   Median       3Q      Max 
-2.58236 -0.27777  0.01862  0.32330  1.42229 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  0.54454    0.05403  10.079  < 2e-16 ***
Income       0.27183    0.04673   5.817  2.4e-08 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.5905 on 196 degrees of freedom
Multiple R-squared: 0.1472, Adjusted R-squared: 0.1429
F-statistic: 33.84 on 1 and 196 DF, p-value: 2.4022e-08

The fitted line has a positive slope, reflecting the positive relationship between income and consumption. The slope coefficient shows that a one unit increase in \(x\) (a 1 percentage point increase in personal disposable income) results on average in 0.27 units increase in \(y\) (an average increase of 0.27 percentage points in personal consumption expenditure).

2.1.2 Multiple linear regression

When there are two or more predictor variables, the model is called a multiple regression model. The general form of a multiple regression model is

\[\begin{equation} y_t = \beta_{0} + \beta_{1} x_{1,t} + \beta_{2} x_{2,t} + \cdots + \beta_{k} x_{k,t} + \varepsilon_t \end{equation}\]

the coefficients \(β_1,…,β_k\) measure the effect of each predictor after taking into account the effects of all the other predictors in the model. Thus, the coefficients measure the marginal effects of the predictor variables.

2.1.3 US consumption expenditure

The scatterplots show positive relationships with income and industrial production, and negative relationships with savings and unemployment. The strength of these relationships are shown by the correlation coefficients across the first row. The remaining scatterplots and correlation coefficients show the relationships between the predictors.

us_change %>%
  GGally::ggpairs(columns = 2:6)

2.1.4 Assumptions

First, we assume that the model is a reasonable approximation to reality; that is, the relationship between the forecast variable and the predictor variables satisfies this linear equation.

Second, we make the following assumptions about the errors: - they have mean zero; otherwise the forecasts will be systematically biased. - they are not autocorrelated; otherwise the forecasts will be inefficient, as there is more information in the data that can be exploited. - they are unrelated to the predictor variables; otherwise there would be more information that should be included in the systematic part of the model.

It is also useful to have the errors being normally distributed with a constant variance \(σ^2\) in order to easily produce prediction intervals.

Another important assumption in the linear regression model is that each predictor \(x\) is not a random variable.

2.1.5 Least squares estimation

The least squares principle provides a way of choosing the coefficients effectively by minimising the sum of the squared errors.

\[\begin{equation} \sum_{t=1}^T \varepsilon_t^2 = \sum_{t=1}^T (y_t - \beta_{0} - \beta_{1} x_{1,t} - \beta_{2} x_{2,t} - \cdots - \beta_{k} x_{k,t})^2. \end{equation}\]

This is called least squares estimation because it gives the least value for the sum of squared errors.

The TSLM() function fits a linear regression model to time series data. It is similar to the lm() function which is widely used for linear models, but TSLM() provides additional facilities for handling time series.

2.1.6 US consumption expenditure

The following output provides information about the fitted model. The first column of Coefficients gives an estimate of each \(β\) coefficient and the second column gives its standard error (i.e., the standard deviation which would be obtained from repeatedly estimating the \(β\) coefficients on similar data sets). The standard error gives a measure of the uncertainty in the estimated \(β\) coefficient.

fit.consMR <- us_change %>%
  model(tslm = TSLM(Consumption ~ Income + Production + Unemployment + Savings))
report(fit.consMR)
Series: Consumption 
Model: TSLM 

Residuals:
     Min       1Q   Median       3Q      Max 
-0.90555 -0.15821 -0.03608  0.13618  1.15471 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)    
(Intercept)   0.253105   0.034470   7.343 5.71e-12 ***
Income        0.740583   0.040115  18.461  < 2e-16 ***
Production    0.047173   0.023142   2.038   0.0429 *  
Unemployment -0.174685   0.095511  -1.829   0.0689 .  
Savings      -0.052890   0.002924 -18.088  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.3102 on 193 degrees of freedom
Multiple R-squared: 0.7683, Adjusted R-squared: 0.7635
F-statistic:   160 on 4 and 193 DF, p-value: < 2.22e-16

The “t value” is the ratio of an estimated \(β\) coefficient to its standard error and the last column gives the p-value: the probability of the estimated \(β\) coefficient being as large as it is if there was no real relationship between consumption and the corresponding predictor. This is useful when studying the effect of each predictor, but is not particularly useful for forecasting.

2.1.7 Fitted values

The following plots show the actual values compared to the fitted values for the percentage change in the US consumption expenditure series.

augment(fit.consMR) %>%
  ggplot(aes(x = Quarter)) +
  geom_line(aes(y = Consumption, colour = "Data")) +
  geom_line(aes(y = .fitted, colour = "Fitted")) +
  labs(y = NULL, title = "Percent change in US consumption expenditure") +
  scale_color_manual(values=c(Data="black",Fitted="red")) +
  guides(colour = guide_legend(title = NULL))

augment(fit.consMR) %>%
  ggplot(aes(x = Consumption, y = .fitted)) +
  geom_point() +
  labs(y = "Fitted (predicted values)",
       x = "Data (actual values)",
       title = "Percent change in US consumption expenditure") +
  geom_abline(intercept = 0, slope = 1)

2.1.8 Goodness-of-fit

A common way to summarise how well a linear regression model fits the data is via the coefficient of determination, or \(R^2\).

If the predictions are close to the actual values, we would expec \(R^2\) to be close to 1. On the other hand, if the predictions are unrelated to the actual values, then \(R^2 = 0\) (again, assuming there is an intercept). In all cases, \(R^2\) lies between 0 and 1.

The value of \(R^2\) will never decrease when adding an extra predictor to the model and this can lead to over-fitting. There are no set rules for what is a good \(R^2\) value, and typical values of \(R^2\) depend on the type of data used. Validating a model’s forecasting performance on the test data is much better than measuring the R^2$ value on the training data.

2.1.9 Standard error of the regression

Another measure of how well the model has fitted the data is the standard deviation of the residuals, which is often known as the “residual standard error.”

2.2 Evaluating the regression model

The differences between the observed y values and the corresponding fitted \(\hat{y}\) values are the training-set errors or “residuals”

Properties residuals:

  • the average of the residuals is zero
  • the correlation between the residuals and the observations for the predictor variable is also zero.

2.2.1 ACF plot of residuals

It is always a good idea to check whether the residuals are normally distributed. As we explained earlier, this is not essential for forecasting, but it does make the calculation of prediction intervals much easier.

fit.consMR %>% gg_tsresiduals()

augment(fit.consMR) %>% features(.innov, ljung_box, lag = 10, dof = 5)

The time plot shows some changing variation over time, but is otherwise relatively unremarkable. This heteroscedasticity will potentially make the prediction interval coverage inaccurate.

The histogram shows that the residuals seem to be slightly skewed, which may also affect the coverage probability of the prediction intervals.

The autocorrelation plot shows a significant spike at lag 7, and a significant Ljung-Box test at the 5% level. However, the autocorrelation is not particularly large, and at lag 7 it is unlikely to have any noticeable impact on the forecasts or the prediction intervals.

2.2.2 Residual plots against predictors

We would expect the residuals to be randomly scattered without showing any systematic patterns. A simple and quick way to check this is to examine scatterplots of the residuals against each of the predictor variables.

It is also necessary to plot the residuals against any predictors that are not in the model. If any of these show a pattern, then the corresponding predictor may need to be added to the model

#residuals from the multiple regression model for forecasting US consumption 
df <- left_join(us_change, residuals(fit.consMR), by = "Quarter")
p1 <- ggplot(df, aes(x = Income, y = .resid)) +
  geom_point() + labs(y = "Residuals")
p2 <- ggplot(df, aes(x = Production, y = .resid)) +
  geom_point() + labs(y = "Residuals")
p3 <- ggplot(df, aes(x = Savings, y = .resid)) +
  geom_point() + labs(y = "Residuals")
p4 <- ggplot(df, aes(x = Unemployment, y = .resid)) +
  geom_point() + labs(y = "Residuals")

library(cowplot)

Attaching package: 㤼㸱cowplot㤼㸲

The following object is masked from 㤼㸱package:lubridate㤼㸲:

    stamp
plot_grid(p1, p2, p3, p4, cols=2)
Argument 'cols' is deprecated. Use 'ncol' instead.

2.2.3 Residual plots against fitted values

A plot of the residuals against the fitted values should also show no pattern. If a pattern is observed, there may be “heteroscedasticity” in the errors which means that the variance of the residuals may not be constant. If this problem occurs, a transformation of the forecast variable such as a logarithm or square root may be required.

augment(fit.consMR) %>%
  ggplot(aes(x = .fitted, y = .resid)) +
  geom_point() + labs(x = "Fitted", y = "Residuals")

2.2.4 Outliers and influential observations

Observations that take extreme values compared to the majority of the data are called outliers. Observations that have a large influence on the estimated coefficients of a regression model are called influential observations. Usually, influential observations are also outliers that are extreme in the \(x\) direction.

A scatter plot of \(y\) against each \(x\) is always a useful starting point in regression analysis, and often helps to identify unusual observations.

Incorrect data entry should be corrected or removed from the sample immediately.

If an observation has been identified as a likely outlier, it is important to study it and analyse the possible reasons behind it. The decision to remove or retain an observation can be a challenging one (especially when outliers are influential observations). It is wise to report results both with and without the removal of such observations.

2.2.5 Spurious regression

More often than not, time series data are “non-stationary”; that is, the values of the time series do not fluctuate around a constant mean or with a constant variance.

Regressing non-stationary time series can lead to spurious regressions. The output of regressing Australian air passengers on rice production in Guinea is shown in Figure 7.13. High \(R^2\) and high residual autocorrelation can be signs of spurious regression. Notice these features in the output below.

fit <- aus_airpassengers %>%
  filter(Year <= 2011) %>%
  left_join(guinea_rice, by = "Year") %>%
  model(TSLM(Passengers ~ Production))
report(fit)
Series: Passengers 
Model: TSLM 

Residuals:
    Min      1Q  Median      3Q     Max 
-5.9448 -1.8917 -0.3272  1.8620 10.4210 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)   -7.493      1.203  -6.229 2.25e-07 ***
Production    40.288      1.337  30.135  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 3.239 on 40 degrees of freedom
Multiple R-squared: 0.9578, Adjusted R-squared: 0.9568
F-statistic: 908.1 on 1 and 40 DF, p-value: < 2.22e-16
fit %>% gg_tsresiduals()

2.3 Some useful predictors

2.3.1 Trend

A trend variable can be specified in the TSLM() function using the trend() specia

2.3.2 Dummy variables

Categorical variables like “yes” or “no”. his situation can still be handled within the framework of multiple regression models by creating a “dummy variable” which takes value 1 corresponding to “yes” and 0 corresponding to “no.” A dummy variable is also known as an “indicator variable.”

A dummy variable can also be used to account for an outlier in the data. Rather than omit the outlier, a dummy variable removes its effect. In this case, the dummy variable takes value 1 for that observation and 0 everywhere

TSLM() will automatically handle this case if you specify a factor variable as a predictor. There is usually no need to manually create the corresponding dummy variables.

2.3.3 Seasonal dummy variables

The general rule is to use one fewer dummy variables than categories. So for quarterly data, use three dummy variables; for monthly data, use 11 dummy variables; and for daily data, use six dummy variables, and so on.

The interpretation of each of the coefficients associated with the dummy variables is that it is a measure of the effect of that category relative to the omitted category.

The TSLM() function will automatically handle this situation if you specify the special season().

recent_production <- aus_production %>%
  filter(year(Quarter) >= 1992)
recent_production %>%
  autoplot(Beer) +
  labs(y = "Megalitres")

fit_beer <- recent_production %>%
  model(TSLM(Beer ~ trend() + season()))
report(fit_beer)
Series: Beer 
Model: TSLM 

Residuals:
     Min       1Q   Median       3Q      Max 
-42.9029  -7.5995  -0.4594   7.9908  21.7895 

Coefficients:
               Estimate Std. Error t value Pr(>|t|)    
(Intercept)   441.80044    3.73353 118.333  < 2e-16 ***
trend()        -0.34027    0.06657  -5.111 2.73e-06 ***
season()year2 -34.65973    3.96832  -8.734 9.10e-13 ***
season()year3 -17.82164    4.02249  -4.430 3.45e-05 ***
season()year4  72.79641    4.02305  18.095  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 12.23 on 69 degrees of freedom
Multiple R-squared: 0.9243, Adjusted R-squared: 0.9199
F-statistic: 210.7 on 4 and 69 DF, p-value: < 2.22e-16

There is an average downward trend of -0.34 megalitres per quarter. On average, the second quarter has production of 34.7 megalitres lower than the first quarter, the third quarter has production of 17.8 megalitres lower than the first quarter, and the fourth quarter has production of 72.8 megalitres higher than the first quarter.

augment(fit_beer) %>%
  ggplot(aes(x = Quarter)) +
  geom_line(aes(y = Beer, colour = "Data")) +
  geom_line(aes(y = .fitted, colour = "Fitted")) +
  scale_color_manual(values = c(Data = "black", Fitted = "red")) +
  labs(y = "Megalitres", title = "Quarterly Beer Production") +
  guides(colour = guide_legend(title = "Series"))

augment(fit_beer) %>%
  ggplot(aes(x = Beer, y = .fitted, colour = factor(quarter(Quarter)))) +
  geom_point() +
  labs(y = "Fitted", x = "Actual values",
       title = "Quarterly beer production") +
  geom_abline(intercept = 0, slope = 1) +
  guides(colour = guide_legend(title = "Quarter"))

2.3.4 Intervention variables

It is often necessary to model interventions that may have affected the variable to be forecast. For example, competitor activity, advertising expenditure, industrial action, and so on, can all have an effect.

When the effect lasts only for one period, we use a “spike” variable. This is a dummy variable that takes value one in the period of the intervention and zero elsewhere. A spike variable is equivalent to a dummy variable for handling an outlier.

Other interventions have an immediate and permanent effect. A step variable takes value zero before the intervention and one from the time of intervention onward.

2.3.5 Trading days

The number of trading days in a month can vary considerably and can have a substantial effect on sales data. To allow for this, the number of trading days in each month can be included as a predictor. Another form of permanent effect is a change of slope.

2.3.6 Distributed lags

It is often useful to include advertising expenditure as a predictor. However, since the effect of advertising can last beyond the actual campaign, we need to include lagged values of advertising expenditure.

2.3.7 Easter

Easter differs from most holidays because it is not held on the same date each year, and its effect can last for several days. In this case, a dummy variable can be used with value one where the holiday falls in the particular time period and zero otherwise.

2.3.8 Fourier series

An alternative to using seasonal dummy variables, especially for long seasonal periods, is to use Fourier terms.

If \(m\)is the seasonal period, then the first few Fourier terms are given by

\[\begin{equation} x_{1,t} = \sin\left(\textstyle\frac{2\pi t}{m}\right), x_{2,t} = \cos\left(\textstyle\frac{2\pi t}{m}\right), x_{3,t} = \sin\left(\textstyle\frac{4\pi t}{m}\right), \end{equation}\] \[\begin{equation} x_{4,t} = \cos\left(\textstyle\frac{4\pi t}{m}\right), x_{5,t} = \sin\left(\textstyle\frac{6\pi t}{m}\right), x_{6,t} = \cos\left(\textstyle\frac{6\pi t}{m}\right), \end{equation}\]

With Fourier terms, we often need fewer predictors than with dummy variables, especially when \(m\) is large. This makes them useful for weekly data, for example, where \(m ≈ 52\). For short seasonal periods (e.g., quarterly data), there is little advantage in using Fourier terms over seasonal dummy variables.

fourier_beer <- recent_production %>%
  model(TSLM(Beer ~ trend() + fourier(K = 2)))
report(fourier_beer)
Series: Beer 
Model: TSLM 

Residuals:
     Min       1Q   Median       3Q      Max 
-42.9029  -7.5995  -0.4594   7.9908  21.7895 

Coefficients:
                    Estimate Std. Error t value Pr(>|t|)    
(Intercept)        446.87920    2.87321 155.533  < 2e-16 ***
trend()             -0.34027    0.06657  -5.111 2.73e-06 ***
fourier(K = 2)C1_4   8.91082    2.01125   4.430 3.45e-05 ***
fourier(K = 2)S1_4 -53.72807    2.01125 -26.714  < 2e-16 ***
fourier(K = 2)C2_4 -13.98958    1.42256  -9.834 9.26e-15 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 12.23 on 69 degrees of freedom
Multiple R-squared: 0.9243, Adjusted R-squared: 0.9199
F-statistic: 210.7 on 4 and 69 DF, p-value: < 2.22e-16

The K argument to fourier() specifies how many pairs of sin and cos terms to include. The maximum allowed is \(K = m/2\) where \(m\) is the seasonal period. Because we have used the maximum here, the results are identical to those obtained when using seasonal dummy variables.

A regression model containing Fourier terms is often called a harmonic regression because the successive Fourier terms represent harmonics of the first two Fourier terms.

2.4 Selecting predictors

We will use a measure of predictive accuracy. They can be shown using the glance() function, here applied to the model for US consumption:

glance(fit.consMR) %>% select(adj_r_squared, CV, AIC, AICc, BIC)

We compare these values against the corresponding values from other models. For the CV, AIC, AICc and BIC measures, we want to find the model with the lowest value; for Adjusted \(R^2\), we seek the model with the highest value.

2.4.1 Adjusted \(R^2\)

\[\begin{equation} \bar{R}^2 = 1-(1-R^2)\frac{T-1}{T-k-1}, \end{equation}\]

This is an improvement on \(R^2\), as it will no longer increase with each added predictor. Using this measure, the best model will be the one with the largest value of \(\bar{R}^2\). Maximising \(\bar{R}^2\)is equivalent to minimising the standard error \(\hat{\sigma}_e\).

Maximising\(\bar{R}^2\) works quite well as a method of selecting predictors, although it does tend to err on the side of selecting too many predictors.

2.4.2 Cross-validation

For regression models, it is also possible to use classical leave-one-out cross-validation to selection predictors:

  1. Remove observation \(t\) from the data set, and fit the model using the remaining data. Then compute the error \((e∗t=y_t−\hat{y}_t)\) for the omitted observation. (This is not the same as the residual because the \(t\)th observation was not used in estimating the value of \(\hat{y}_t\).)
  2. Repeat step 1 for \(t =1, …,T\).
  3. Compute the MSE from \(e^∗_1,…,e^∗_T\). We shall call this the CV.

Under this criterion, the best model is the one with the smallest value of CV.

2.4.3 Akaike’s Information Criterion

The model with the minimum value of the AIC is often the best model for forecasting. For large values of \(T\), minimising the AIC is equivalent to minimising the CV value.

2.4.4 Corrected Akaike’s Information Criterion

For small values of \(T\), the AIC tends to select too many predictors, and so a bias-corrected version of the AIC has been developed. \(AICc\) should be minimised.

2.4.5 Schwarz’s Bayesian Information Criterion

Schwarz’s Bayesian Information Criterion (usually abbreviated to BIC, SBIC or SC). Minimising the BIC is intended to give the best model.

2.4.6 Which measure should we use?

We recommend that one of the AICc, AIC, or CV statistics be used, each of which has forecasting as their objective. If the value of T is large enough, they will all lead to the same model.

2.4.7 Best subset regression

Where possible, all potential regression models should be fitted (as was done in the example above) and the best model should be selected based on one of the measures discussed. This is known as “best subsets” regression or “all possible subsets” regression.

2.4.8 Stepwise regression

An approach that works quite well is backwards stepwise regression:

  • Start with the model containing all potential predictors.
  • Remove one predictor at a time. Keep the model if it improves the measure of predictive accuracy.
  • Iterate until no further improvement.

If the number of potential predictors is too large, then the backwards stepwise regression will not work and forward stepwise regression can be used instead. This procedure starts with a model that includes only the intercept. Predictors are added one at a time, and the one that most improves the measure of predictive accuracy is retained in the model. The procedure is repeated until no further improvement can be achieved.

2.5 Forecasting with regression

2.5.1 Ex-ante versus ex-post forecasts

Ex-ante forecasts are those that are made using only the information that is available in advance. Might require forecasts of the predictors.

Ex-post forecasts are those that are made using later information on the predictors.

The model from which ex-post forecasts are produced should not be estimated using data from the forecast period. That is, ex-post forecasts can assume knowledge of the predictor variables (the x variables), but should not assume knowledge of the data that are to be forecast (the y variable).

A comparative evaluation of ex-ante forecasts and ex-post forecasts can help to separate out the sources of forecast uncertainty.

2.5.1.1 Australian quarterly beer production

Calendar predictors or deterministic functions make no difference between ex-ante and ex-post forecasts.

recent_production <- aus_production %>% 
  filter(year(Quarter) >= 1992)
fit_beer <- recent_production %>%
  model(TSLM(Beer ~ trend() + season()))
fc_beer <- forecast(fit_beer)
fc_beer %>%
  autoplot(recent_production) +
  labs(title = "Forecasts of beer production using regression",
       y = "megalitres")

2.5.2 Scenario based forecasting

In this setting, the forecaster assumes possible scenarios for the predictor variables that are of interest. For example, a US policy maker may be interested in comparing the predicted change in consumption when there is a constant growth of 1% and 0.5% respectively for income and savings with no change in the employment rate, versus a respective decline of 1% and 0.5%, for each of the four quarters following the end of the sample.

fit_consBest <- us_change %>%
  model(lm = TSLM(Consumption ~ Income + Savings + Unemployment))

future_scenarios <- scenarios(
  Increase = new_data(us_change, 4) %>% 
    mutate(Income=1, Savings=0.5, Unemployment=0),
  Decrease = new_data(us_change, 4) %>% 
    mutate(Income=-1, Savings=-0.5, Unemployment=0),
  names_to = "Scenario")

fc <- forecast(fit_consBest, new_data = future_scenarios)
us_change %>%
  autoplot(Consumption) +
  autolayer(fc) +
  labs(y = "% change in US consumption")

2.5.3 Building a predictive regression model

The great advantage of regression models is that they can be used to capture important relationships between the forecast variable of interest and the predictor variables. A major challenge however, is that in order to generate ex-ante forecasts, the model requires future values of each predictor. If scenario based forecasting is of interest then these models are extremely useful. However, if ex-ante forecasting is the main focus, obtaining forecasts of the predictors can be challenging (in many cases generating forecasts for the predictor variables can be more challenging than forecasting directly the forecast variable without using predictors).

An alternative formulation is to use as predictors their lagged values. Including lagged values of the predictors does not only make the model operational for easily generating forecasts, it also makes it intuitively appealing.

2.5.4 Prediction intervals

The estimated simple regression line in the US consumption example is

\(\hat{y}_t=0.54+0.27x_t\)

#personal income increases by its historical mean
#extreme case, increase  5% in income
fit_cons <- us_change %>%
  model(TSLM(Consumption ~ Income))
new_cons <- scenarios(
  "Average increase" = new_data(us_change, 4) %>% mutate(Income = mean(us_change$Income)),
  "Extreme increase" = new_data(us_change, 4) %>% mutate(Income = 12),
  names_to = "Scenario"
)
fcast <- forecast(fit_cons, new_cons)

us_change %>%
  autoplot(Consumption) +
  autolayer(fcast) +
  labs(y = "% change in US consumption")

2.6 Nonlinear regression

The simplest way of modelling a nonlinear relationship is to transform the forecast variable y and/or the predictor variable x before estimating a regression model. While this provides a non-linear functional form, the model is still linear in the parameters.

\(\log y=\beta_0+\beta_1 \log x +\varepsilon\)

In this model, the slope β1 can be interpreted as an elasticity: β1 is the average percentage change in y resulting from a 1% increase in x. Other useful forms can also be specified. The log-linear form is specified by only transforming the forecast variable and the linear-log form is obtained by transforming the predictor.

2.6.1 Forecasting with a nonlinear trend

A better approach is to use the piecewise specification and fit a piecewise linear trend which bends at some point in time

2.6.1.1 Example: Boston marathon winning times

boston_men <- boston_marathon %>%
  filter(Year >= 1924) %>%
  filter(Event == "Men's open division") %>%
  mutate(Minutes = as.numeric(Time)/60)

The plot of winning times reveals three different periods. There is a lot of volatility in the winning times up to about 1950, with the winning times barely declining. After 1950 there is a clear decrease in times, followed by a flattening out after the 1980s, with the suggestion of an upturn towards the end of the sample. To account for these changes, we specify the years 1950 and 1980 as knots. We should warn here that subjective identification of knots can lead to over-fitting, which can be detrimental to the forecast performance of a model, and should be performed with caution.

fit_trends <- boston_men %>%
  model(
    linear = TSLM(Minutes ~ trend()),
    exponential = TSLM(log(Minutes) ~ trend()),
    piecewise = TSLM(Minutes ~ trend(knots = c(1950, 1980)))
  )
fc_trends <- fit_trends %>% forecast(h = 10)

boston_men %>%
  autoplot(Minutes) +
  geom_line(aes(y = .fitted, colour = .model), data = fitted(fit_trends)) +
  autolayer(fc_trends, alpha = 0.5, level = 95) +
  labs(y = "Winning times in minutes",
       title = "Boston Marathon")

2.7 Correlation, causation and forecasting

A variable x may be useful for forecasting a variable y, but that does not mean x is causing y. It is possible that x is causing y, but it may be that y is causing x, or that the relationship between them is more complicated than simple causality.

Confounder when a variable not included in the model influences both the response variable and at least one predictor variable. Confounding makes it difficult to determine what variables are causing changes in other variables, but it does not necessarily make forecasting more difficult.

Often a better model is possible if a causal mechanism can be determined.

2.7.1 Forecasting with correlated predictors

When two or more predictors are highly correlated it is always challenging to accurately separate their individual effects.

2.7.2 Multicollinearity and forecasting

A closely related issue is multicollinearity, which occurs when similar information is provided by two or more of the predictor variables in a multiple regression.

When multicollinearity is present, the uncertainty associated with individual regression coefficients will be large. This is because they are difficult to estimate. Consequently, statistical tests (e.g., t-tests) on regression coefficients are unreliable.

Forecasts will be unreliable if the values of the future predictors are outside the range of the historical values of the predictors.

3 Exponential smoothing

Forecasts produced using exponential smoothing methods are weighted averages of past observations, with the weights decaying exponentially as the observations get older.

3.1 Simple exponential smoothing

This method is suitable for forecasting data with no clear trend or seasonal pattern.

algeria_economy <- global_economy %>%
  filter(Country == "Algeria")
algeria_economy %>%
  autoplot(Exports) +
  labs(y="Exports (% of GDP)")

Forecasts are calculated using weighted averages, where the weights decrease exponentially as observations come from further in the past — the smallest weights are associated with the oldest observations:

\[\begin{equation} \hat{y}_{T+1|T} = \alpha y_T + \alpha(1-\alpha) y_{T-1} + \alpha(1-\alpha)^2 y_{T-2}+ \cdots, \tag{8.1} \end{equation}\]

where $0≤α≤1$ is the smoothing parameter.

3.1.1 Weighted average form

The forecast at time $T+1$ is equal to a weighted average between the most recent observation $y_T$ and the previous forecast \(\hat{y}_{T|T−1}\):

\(\hat{y}_{T+1|T} = \alpha y_T + (1-\alpha) \hat{y}_{T|T-1},\)

3.1.2 Component form

An alternative representation is the component form. For simple exponential smoothing, the only component included is the level, \(ℓ_t\). Component form representations of exponential smoothing methods comprise a forecast equation and a smoothing equation for each of the components included in the method. The component form of simple exponential smoothing is given by:

\[\begin{align*} \text{Forecast equation} && \hat{y}_{t+h|t} & = \ell_{t}\\ \text{Smoothing equation} && \ell_{t} & = \alpha y_{t} + (1 - \alpha)\ell_{t-1}, \end{align*}\]

3.1.3 Flat forecasts

Simple exponential smoothing has a “flat” forecast function:

\(\hat{y}_{T+h|T} = \hat{y}_{T+1|T}=\ell_T, \qquad h=2,3,\dots.\)

3.1.4 Optimisation

The application of every exponential smoothing method requires the smoothing parameters and the initial values to be chosen. In particular, for simple exponential smoothing, we need to select the values of \(α\) and \(ℓ_0\). All forecasts can be computed from the data once we know those values. For the methods that follow there is usually more than one smoothing parameter and more than one initial component to be chosen.

A more reliable and objective way to obtain values for the unknown parameters is to estimate them from the observed data. The unknown parameters and the initial values for any exponential smoothing method can be estimated by minimising the SSE.

3.1.5 Example: Algerian exports

# Estimate parameters
fit <- algeria_economy %>%
  model(ETS(Exports ~ error("A") + trend("N") + season("N")))
fc <- fit %>%
  forecast(h = 5)
report(fit)
Series: Exports 
Model: ETS(A,N,N) 
  Smoothing parameters:
    alpha = 0.8399875 

  Initial states:
      l
 39.539

  sigma^2:  35.6301

     AIC     AICc      BIC 
446.7154 447.1599 452.8968 

This gives parameter estimates \(/hat{^}α=0.84\) and \(/hat^ℓ_0=39.5\)

autoplot(fc) +
  geom_line(aes(y = Exports, col="Data"), data = algeria_economy) +
  geom_line(aes(y = .fitted, col="Fitted"), data = augment(fit)) +
  labs(y="Exports (% of GDP)") +
  scale_color_manual(values=c(Data="black",Fitted="red")) +
  guides(colour = guide_legend("Series"))

The prediction intervals shown here are calculated using the methods described in Section 8.7. The prediction intervals show that there is considerable uncertainty in the future exports over the five-year forecast period. So interpreting the point forecasts without accounting for the large uncertainty can be very misleading.

3.2 Methods with trend

3.2.1 Holt’s linear trend method

This method involves a forecast equation and two smoothing equations (one for the level and one for the trend):

\[\begin{align*} \text{Forecast equation}&& \hat{y}_{t+h|t} &= \ell_{t} + hb_{t} \\ \text{Level equation} && \ell_{t} &= \alpha y_{t} + (1 - \alpha)(\ell_{t-1} + b_{t-1})\\ \text{Trend equation} && b_{t} &= \beta^*(\ell_{t} - \ell_{t-1}) + (1 -\beta^*)b_{t-1}, \end{align*}\]

3.2.2 Example: Australian population

aus_economy <- global_economy %>%
  filter(Code == "AUS") %>%
  mutate(Pop = Population / 1e6)
autoplot(aus_economy, Pop) + labs(y = "Millions")

fit <- aus_economy %>%
  model(AAN = ETS(Pop ~ error("A") + trend("A") + season("N")))
fc <- fit %>% forecast(h = 10)
report(fit)
Series: Pop 
Model: ETS(A,A,N) 
  Smoothing parameters:
    alpha = 0.9999 
    beta  = 0.3266366 

  Initial states:
        l         b
 10.05414 0.2224818

  sigma^2:  0.0041

      AIC      AICc       BIC 
-76.98569 -75.83184 -66.68347 

The estimated smoothing coefficient for the level is \(\hat{^}α=1.00\). The very high value shows that the level changes rapidly in order to capture the highly trended series. The estimated smoothing coefficient for the slope is \(\hat{^}β^∗=0.33\). This is relatively large suggesting that the trend also changes often (even if the changes are slight).

3.2.3 Damped trend methods

“dampens” the trend to a flat line some time in the future. Methods that include a damped trend have proven to be very successful, and are arguably the most popular individual methods when forecasts are required automatically for many series.

this method also includes a damping parameter \(0<ϕ<1\):

\[\begin{align*} \hat{y}_{t+h|t} &= \ell_{t} + (\phi+\phi^2 + \dots + \phi^{h})b_{t} \\ \ell_{t} &= \alpha y_{t} + (1 - \alpha)(\ell_{t-1} + \phi b_{t-1})\\ b_{t} &= \beta^*(\ell_{t} - \ell_{t-1}) + (1 -\beta^*)\phi b_{t-1}. \end{align*}\]

In practice, \(ϕ\) is rarely less than 0.8 as the damping has a very strong effect for smaller values. Values of \(ϕ\) close to 1 will mean that a damped model is not able to be distinguished from a non-damped model. For these reasons, we usually restrict \(ϕ\) to a minimum of 0.8 and a maximum of 0.98.

3.2.4 Example: Australian Population (continued)

aus_economy %>%
  model(
    `Holt's method` = ETS(Pop ~ error("A") + trend("A") + season("N")),
    `Damped Holt's method` = ETS(Pop ~ error("A") + trend("Ad", phi = 0.9) + season("N"))
  ) %>%
  forecast(h = 15) %>%
  autoplot(aus_economy, level = NULL) +
  labs(title = "Forecasts from Holt's method",
       y = "Population of Australia (millions)") +
  guides(colour = guide_legend(title = "Forecast"))

We have set the damping parameter to a relatively low number (\(ϕ=0.90\)) to exaggerate the effect of damping for comparison. Usually, we would estimate \(ϕ\) along with the other parameters. We have also used a rather large forecast horizon (\(h=15\)) to highlight the difference between a damped trend and a linear trend.

3.2.5 Example: Internet usage

we compare the forecasting performance of the three exponential smoothing methods that we have considered so far in forecasting the number of users connected to the internet via a server. The data is observed over 100 minutes:

www_usage <- as_tsibble(WWWusage)
www_usage %>% autoplot(value) +
  labs(x="Minute", y="Number of users")

We will use time series cross-validation to compare the one-step forecast accuracy of the three methods.

www_usage %>%
  stretch_tsibble(.init = 10) %>%
  model(
    SES = ETS(value ~ error("A") + trend("N") + season("N")),
    Holt = ETS(value ~ error("A") + trend("A") + season("N")),
    Damped = ETS(value ~ error("A") + trend("Ad") + season("N"))
  ) %>%
  forecast(h = 1) %>%
  accuracy(www_usage)
The future dataset is incomplete, incomplete out-of-sample data will be treated as missing. 
1 observation is missing at 101

Damped Holt’s method is best whether you compare MAE or RMSE values. So we will proceed with using the damped Holt’s method and apply it to the whole data set to get forecasts for future years.

fit <- www_usage %>%
  model(Damped = ETS(value ~ error("A") + trend("Ad") + season("N")))
tidy(fit)

The smoothing parameter for the slope is estimated to be almost one, indicating that the trend changes to mostly reflect the slope between the last two minutes of internet usage. The value of \(α\) is very close to one, showing that the level reacts strongly to each new observation.

fit %>%
  forecast(h = 10) %>%
  autoplot(www_usage) +
  labs(x="Minute", y="Number of users")

The resulting forecasts look sensible with decreasing trend, and relatively wide prediction intervals reflecting the variation in the historical data.

3.3 Methods with seasonality

The Holt-Winters seasonal method comprises the forecast equation and three smoothing equations — one for the level \(ℓ_t\), one for the trend \(b_t\), and one for the seasonal component \(s_t\), with corresponding smoothing parameters \(α\), \(β^∗\) and \(γ\). We use \(m\) to denote the frequency of the seasonality, i.e., the number of seasons in a year. For example, for quarterly data \(m=4\), and for monthly data \(m=12\).

The additive method is preferred when the seasonal variations are roughly constant through the series, while the multiplicative method is preferred when the seasonal variations are changing proportional to the level of the series.

3.3.1 Holt-Winters’ additive method

\[\begin{align*} \hat{y}_{t+h|t} &= \ell_{t} + hb_{t} + s_{t+h-m(k+1)} \\ \ell_{t} &= \alpha(y_{t} - s_{t-m}) + (1 - \alpha)(\ell_{t-1} + b_{t-1})\\ b_{t} &= \beta^*(\ell_{t} - \ell_{t-1}) + (1 - \beta^*)b_{t-1}\\ s_{t} &= \gamma (y_{t}-\ell_{t-1}-b_{t-1}) + (1-\gamma)s_{t-m}, \end{align*}\]

3.3.2 Holt-Winters’ multiplicative method

\[\begin{align*} \hat{y}_{t+h|t} &= (\ell_{t} + hb_{t})s_{t+h-m(k+1)} \\ \ell_{t} &= \alpha \frac{y_{t}}{s_{t-m}} + (1 - \alpha)(\ell_{t-1} + b_{t-1})\\ b_{t} &= \beta^*(\ell_{t}-\ell_{t-1}) + (1 - \beta^*)b_{t-1} \\ s_{t} &= \gamma \frac{y_{t}}{(\ell_{t-1} + b_{t-1})} + (1 - \gamma)s_{t-m} \end{align*}\]

3.3.3 Example: Domestic overnight trips in Australia

We apply Holt-Winters’ method with both additive and multiplicative seasonality to forecast quarterly visitor nights in Australia spent by domestic tourists.

aus_holidays <- tourism %>%
  filter(Purpose == "Holiday") %>%
  summarise(Trips = sum(Trips)/1e3)
fit <- aus_holidays %>%
  model(
    additive = ETS(Trips ~ error("A") + trend("A") + season("A")),
    multiplicative = ETS(Trips ~ error("M") + trend("A") + season("M"))
  )
fc <- fit %>% forecast(h = "3 years")

fc %>%
  autoplot(aus_holidays, level = NULL) +
  labs(y="Overnight trips (millions)")

tidy(fit)

ecause both methods have exactly the same number of parameters to estimate, we can compare the training RMSE from both models. In this case, the method with multiplicative seasonality fits the data slightly better.

3.3.4 Holt-Winters’ damped method

Damping is possible with both additive and multiplicative Holt-Winters’ methods. A method that often provides accurate and robust forecasts for seasonal data is the Holt-Winters method with a damped trend and multiplicative seasonality:

\[\begin{align*} \hat{y}_{t+h|t} &= \left[\ell_{t} + (\phi+\phi^2 + \dots + \phi^{h})b_{t}\right]s_{t+h-m(k+1)}. \\ \ell_{t} &= \alpha(y_{t} / s_{t-m}) + (1 - \alpha)(\ell_{t-1} + \phi b_{t-1})\\ b_{t} &= \beta^*(\ell_{t} - \ell_{t-1}) + (1 - \beta^*)\phi b_{t-1} \\ s_{t} &= \gamma \frac{y_{t}}{(\ell_{t-1} + \phi b_{t-1})} + (1 - \gamma)s_{t-m}. \end{align*}\]
ETS(y ~ error("M") + trend("Ad") + season("M"))

3.3.5 Example: Holt-Winters method with daily data

The Holt-Winters method can also be used for daily type of data, where the seasonal period is \(m=7\), and the appropriate unit of time for \(h\) is in days. Here we forecast pedestrian traffic at a busy Melbourne train station in July 2016.

sth_cross_ped <- pedestrian %>%
  filter(Date >= "2016-07-01", Sensor == "Southern Cross Station") %>%
  index_by(Date) %>%
  summarise(Count = sum(Count))
sth_cross_ped %>%
  filter(Date <= "2016-07-31") %>%
  model(hw = ETS(Count ~ error("M") + trend("Ad") + season("M"))) %>%
  forecast(h = "2 weeks") %>%
  autoplot(sth_cross_ped %>% filter(Date <= "2016-08-14"))

Clearly the model has identified the weekly seasonal pattern and the increasing trend at the end of the data, and the forecasts are a close match to the test data.

3.4 A taxonomy of exponential smoothing methods

By considering variations in the combinations of the trend and seasonal components, nine exponential smoothing methods are possible. Each method is labelled by a pair of letters (T,S) defining the type of ‘Trend’ and ‘Seasonal’ components. For example, (A,M) is the method with an additive trend and multiplicative seasonality; (\(A_d\), N) is the method with damped trend and no seasonality; and so on.

Trend Component Seasonal Component
N (None) A(Additive) M (Multiplicative)
N (None) (N,N) (N,A) (N,M)
A (Additive) (A,N) (A,A) (A,M)
A_d (Additive damped) (\(A_d\),N) (\(A_d\),A) (\(A_d\),M)
Short hand Method
(N,N) Simple exponential smoothing
(A,N) Holt’s linear method
(\(A_d\),N) Additive damped trend method
(A,A) Additive Holt-Winters’ method
(A,M) Multiplicative Holt-Winters’ method
(\(A_d\),M) Holt-Winters’ damped method

3.5 Innovations state space models for exponential smoothing

The exponential smoothing methods presented in Table 8.6 are algorithms which generate point forecasts. The statistical models in this section generate the same point forecasts, but can also generate prediction (or forecast) intervals. A statistical model is a stochastic (or random) data generating process that can produce an entire forecast distribution.

Each model consists of a measurement equation that describes the observed data, and some state equations that describe how the unobserved components or states (level, trend, seasonal) change over time. Hence, these are referred to as state space models.

For each method there exist two models: one with additive errors and one with multiplicative errors. To distinguish between a model with additive errors and one with multiplicative errors (and also to distinguish the models from the methods), we add a third letter to the classification. We label each state space model as ETS(⋅,⋅,) for (Error, Trend, Seasonal).

3.5.1 ETS(A,N,N): simple exponential smoothing with additive errors

3.5.2 ETS(M,N,N): simple exponential smoothing with multiplicative errors

3.5.3 ETS(A,A,N): Holt’s linear method with additive errors

3.5.4 ETS(M,A,N): Holt’s linear method with multiplicative errors

3.6 Estimation and model selection

3.6.1 Estimating ETS models

An alternative to estimating the parameters by minimising the sum of squared errors is to maximise the “likelihood.” The likelihood is the probability of the data arising from the specified model. Thus, a large likelihood is associated with a good model. For an additive error model, maximising the likelihood (assuming normally distributed errors) gives the same results as minimising the sum of squared errors. However, different results will be obtained for multiplicative error models.

3.6.2 Model selection

A great advantage of the ETS statistical framework is that information criteria can be used for model selection. The AIC, \(AIC_c\) and BIC can be used here to determine which of the ETS models is most appropriate for a given time series.

Models with multiplicative errors are useful when the data are strictly positive, but are not numerically stable when the data contain zeros or negative values.

3.6.3 Example: Domestic holiday tourist visitor nights in Australia

We now employ the ETS statistical framework to forecast Australian holiday tourism over the period 2016–2019. We let the ETS() function select the model by minimising the AICc.

aus_holidays <- tourism %>%
  filter(Purpose == "Holiday") %>%
  summarise(Trips = sum(Trips)/1e3)
fit <- aus_holidays %>%
  model(ETS(Trips))
report(fit)
Series: Trips 
Model: ETS(M,N,A) 
  Smoothing parameters:
    alpha = 0.3484054 
    gamma = 0.0001000018 

  Initial states:
        l         s1         s2         s3       s4
 9.727072 -0.5376106 -0.6884343 -0.2933663 1.519411

  sigma^2:  0.0022

     AIC     AICc      BIC 
226.2289 227.7845 242.9031 

The model selected is ETS(M,N,A).

The small values of \(γ\) indicate that the seasonal components change very little over time. The narrow prediction intervals indicate that the series is relatively easy to forecast due to the strong trend and seasonality.

components(fit) %>%
  autoplot() +
  labs(title = "ETS(M,N,A) components")

Because this model has multiplicative errors, the innovation residuals are not equivalent to the regular residuals (i.e., the one-step training errors). The innovation residuals are given by \(\hat{ε}_t}\), while the regular residuals are defined as \(y_t−\hat{y}_{t|t−1}\).

augment(fit) %>% 
  autoplot(.resid)

augment(fit) %>% 
  autoplot(.innov)

3.7 Forecasting with ETS models

ETS point forecasts are equal to the medians of the forecast distributions. For models with only additive components, the forecast distributions are normal, so the medians and means are equal. For ETS models with multiplicative errors, or with multiplicative seasonality, the point forecasts will not be equal to the means of the forecast distributions.

fit %>%
  forecast(h = 8) %>%
  autoplot(aus_holidays) +
  labs(y="Domestic holiday visitors in Australia (thousands)")

3.7.1 Prediction intervals

The prediction intervals will differ between models with additive and multiplicative methods.

For most ETS models, a prediction interval can be written as

\(\hat{y}_{T+h|T} \pm c \sigma_h\)

LS0tDQp0aXRsZTogIkZvcmVjYXN0aW5nOiBQcmluY2lwbGVzIGFuZCBQcmFjdGljZSAzIg0KYXV0aG9yOiAiSnVkZ21lbnRhbCBmb3JlY2FzdHMsIFJlZ3Jlc3Npb24gYW5kIEV4cG9uZW50aWFsIFNtb290aGluZyINCm91dHB1dDoNCiAgaHRtbF9ub3RlYm9vazoNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZmxvYXQ6IHRydWUNCiAgICB0b2NfY29sbGFwc2VkOiBmYWxzZQ0KICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQ0KICAgIA0KdG9jX2RlcHRoOiAzDQotLS0NCg0KYGBge3J9DQpsaWJyYXJ5KGZwcDMpDQpgYGANCg0KPGh0dHBzOi8vcm9iamh5bmRtYW4uY29tL3NlbWluYXJzL3V3YTIwMTcvPg0KDQo8aHR0cHM6Ly9yb2JqaHluZG1hbi5jb20vc2VtaW5hcnMvaXNpMjAxOXdvcmtzaG9wLz4NCg0KPGh0dHBzOi8vZ2l0aHViLmNvbS9yb2JqaHluZG1hbi9FVEMzNTUwU2xpZGVzL3RyZWUvZmFibGU+DQoNCjxodHRwczovL2dpdGh1Yi5jb20vcnN0dWRpby1jb25mLTIwMjAvdGltZS1zZXJpZXMtZm9yZWNhc3RpbmcvYmxvYi9tYXN0ZXIvbWF0ZXJpYWxzL2xhYnMuUj4NCg0KPGh0dHA6Ly93d3cuZ3JhZGFhbndyLm5ldC8+DQoNCiMgSnVkZ21lbnRhbCBmb3JlY2FzdHMNCg0KPGh0dHBzOi8vb3RleHRzLmNvbS9mcHAzL2p1ZGdtZW50YWwuaHRtbD4NCg0KSW4gbWFueSBjYXNlcywganVkZ21lbnRhbCBmb3JlY2FzdGluZyBpcyB0aGUgb25seSBvcHRpb24sIHN1Y2ggYXMgd2hlbiB0aGVyZSBpcyBhIGNvbXBsZXRlIGxhY2sgb2YgaGlzdG9yaWNhbCBkYXRhLCBvciB3aGVuIGEgbmV3IHByb2R1Y3QgaXMgYmVpbmcgbGF1bmNoZWQsIG9yIHdoZW4gYSBuZXcgY29tcGV0aXRvciBlbnRlcnMgdGhlIG1hcmtldCwgb3IgZHVyaW5nIGNvbXBsZXRlbHkgbmV3IGFuZCB1bmlxdWUgbWFya2V0IGNvbmRpdGlvbnMuDQoNClJlc2VhcmNoIGluIHRoaXMgYXJlYTMgaGFzIHNob3duIHRoYXQgdGhlIGFjY3VyYWN5IG9mIGp1ZGdtZW50YWwgZm9yZWNhc3RpbmcgaW1wcm92ZXMgd2hlbiB0aGUgZm9yZWNhc3RlciBoYXMgKGkpIGltcG9ydGFudCBkb21haW4ga25vd2xlZGdlLCBhbmQgKGlpKSBtb3JlIHRpbWVseSwgdXAtdG8tZGF0ZSBpbmZvcm1hdGlvbi4gQSBqdWRnbWVudGFsIGFwcHJvYWNoIGNhbiBiZSBxdWljayB0byBhZGp1c3QgdG8gc3VjaCBjaGFuZ2VzLCBpbmZvcm1hdGlvbiBvciBldmVudHMuDQoNClRoZXJlIGFyZSB0aHJlZSBnZW5lcmFsIHNldHRpbmdzIGluIHdoaWNoIGp1ZGdtZW50YWwgZm9yZWNhc3RpbmcgaXMgdXNlZDogKGkpIHRoZXJlIGFyZSBubyBhdmFpbGFibGUgZGF0YSwgc28gdGhhdCBzdGF0aXN0aWNhbCBtZXRob2RzIGFyZSBub3QgYXBwbGljYWJsZSBhbmQganVkZ21lbnRhbCBmb3JlY2FzdGluZyBpcyB0aGUgb25seSBmZWFzaWJsZSBhcHByb2FjaDsgKGlpKSBkYXRhIGFyZSBhdmFpbGFibGUsIHN0YXRpc3RpY2FsIGZvcmVjYXN0cyBhcmUgZ2VuZXJhdGVkLCBhbmQgdGhlc2UgYXJlIHRoZW4gYWRqdXN0ZWQgdXNpbmcganVkZ21lbnQ7IGFuZCAoaWlpKSBkYXRhIGFyZSBhdmFpbGFibGUgYW5kIHN0YXRpc3RpY2FsIGFuZCBqdWRnbWVudGFsIGZvcmVjYXN0cyBhcmUgZ2VuZXJhdGVkIGluZGVwZW5kZW50bHkgYW5kIHRoZW4gY29tYmluZWQuDQoNCiMgVGltZSBzZXJpZXMgcmVncmVzc2lvbiBtb2RlbHMNCg0KIyMgVGhlIGxpbmVhciBtb2RlbA0KDQojIyMgU2ltcGxlIGxpbmVhciByZWdyZXNzaW9uDQoNCiR5X3QgPSBcYmV0YV8wICsgXGJldGFfMSB4X3QgKyBcdmFyZXBzaWxvbl90LiQNCg0KVGhlIGNvZWZmaWNpZW50cyAkzrJfMCQgYW5kICTOsl8xJCBkZW5vdGUgdGhlIGludGVyY2VwdCBhbmQgdGhlIHNsb3BlIG9mIHRoZSBsaW5lIHJlc3BlY3RpdmVseS4NCg0KV2UgY2FuIHRoaW5rIG9mIGVhY2ggb2JzZXJ2YXRpb24gJHlfdCQgYXMgY29uc2lzdGluZyBvZiB0aGUgc3lzdGVtYXRpYyBvciBleHBsYWluZWQgcGFydCBvZiB0aGUgbW9kZWwsICTOsl8wK86yXzF4X3QkLCBhbmQgdGhlIHJhbmRvbSAiZXJyb3IsIiAkzrVfdCQuIFRoZSAiZXJyb3IiIHRlcm0gZG9lcyBub3QgaW1wbHkgYSBtaXN0YWtlLCBidXQgYSBkZXZpYXRpb24gZnJvbSB0aGUgdW5kZXJseWluZyBzdHJhaWdodCBsaW5lIG1vZGVsLiBJdCBjYXB0dXJlcyBhbnl0aGluZyB0aGF0IG1heSBhZmZlY3QgJHlfdCQgb3RoZXIgdGhhbiAkeF90JC4NCg0KYGBge3J9DQp1c19jaGFuZ2UgJT4lDQogIHBpdm90X2xvbmdlcihjKENvbnN1bXB0aW9uLCBJbmNvbWUpLCBuYW1lc190byA9ICJTZXJpZXMiKSAlPiUNCiAgYXV0b3Bsb3QodmFsdWUpICsNCiAgbGFicyh5ID0gIiUgY2hhbmdlIikNCmBgYA0KDQpgYGB7cn0NCnVzX2NoYW5nZSAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gSW5jb21lLCB5ID0gQ29uc3VtcHRpb24pKSArDQogIGxhYnMoeSA9ICJDb25zdW1wdGlvbiAocXVhcnRlcmx5ICUgY2hhbmdlKSIsIHggPSAiSW5jb21lIChxdWFydGVybHkgJSBjaGFuZ2UpIikgKw0KICBnZW9tX3BvaW50KCkgKw0KICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzZSA9IEZBTFNFKQ0KYGBgDQoNCiRcaGF0e3l9X3Q9MC41NCArIDAuMjd4X3QuJA0KDQpUaGUgZXF1YXRpb24gaXMgZXN0aW1hdGVkIHVzaW5nIHRoZSBUU0xNKCkgZnVuY3Rpb246DQoNCmBgYHtyfQ0KdXNfY2hhbmdlICU+JQ0KICBtb2RlbChUU0xNKENvbnN1bXB0aW9uIH4gSW5jb21lKSkgJT4lDQogIHJlcG9ydCgpDQpgYGANCg0KVGhlIGZpdHRlZCBsaW5lIGhhcyBhIHBvc2l0aXZlIHNsb3BlLCByZWZsZWN0aW5nIHRoZSBwb3NpdGl2ZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBpbmNvbWUgYW5kIGNvbnN1bXB0aW9uLiBUaGUgc2xvcGUgY29lZmZpY2llbnQgc2hvd3MgdGhhdCBhIG9uZSB1bml0IGluY3JlYXNlIGluICR4JCAoYSAxIHBlcmNlbnRhZ2UgcG9pbnQgaW5jcmVhc2UgaW4gcGVyc29uYWwgZGlzcG9zYWJsZSBpbmNvbWUpIHJlc3VsdHMgb24gYXZlcmFnZSBpbiAwLjI3IHVuaXRzIGluY3JlYXNlIGluICR5JCAoYW4gYXZlcmFnZSBpbmNyZWFzZSBvZiAwLjI3IHBlcmNlbnRhZ2UgcG9pbnRzIGluIHBlcnNvbmFsIGNvbnN1bXB0aW9uIGV4cGVuZGl0dXJlKS4NCg0KIyMjIE11bHRpcGxlIGxpbmVhciByZWdyZXNzaW9uDQoNCldoZW4gdGhlcmUgYXJlIHR3byBvciBtb3JlIHByZWRpY3RvciB2YXJpYWJsZXMsIHRoZSBtb2RlbCBpcyBjYWxsZWQgYSBtdWx0aXBsZSByZWdyZXNzaW9uIG1vZGVsLiBUaGUgZ2VuZXJhbCBmb3JtIG9mIGEgbXVsdGlwbGUgcmVncmVzc2lvbiBtb2RlbCBpcw0KDQpgYGB7PXRleH0NClxiZWdpbntlcXVhdGlvbn0NCiAgeV90ID0gXGJldGFfezB9ICsgXGJldGFfezF9IHhfezEsdH0gKyBcYmV0YV97Mn0geF97Mix0fSArIFxjZG90cyArIFxiZXRhX3trfSB4X3trLHR9ICsgXHZhcmVwc2lsb25fdA0KXGVuZHtlcXVhdGlvbn0NCmBgYA0KdGhlIGNvZWZmaWNpZW50cyAkzrJfMSzigKYszrJfayQgbWVhc3VyZSB0aGUgZWZmZWN0IG9mIGVhY2ggcHJlZGljdG9yIGFmdGVyIHRha2luZyBpbnRvIGFjY291bnQgdGhlIGVmZmVjdHMgb2YgYWxsIHRoZSBvdGhlciBwcmVkaWN0b3JzIGluIHRoZSBtb2RlbC4gVGh1cywgdGhlIGNvZWZmaWNpZW50cyBtZWFzdXJlIHRoZSBtYXJnaW5hbCBlZmZlY3RzIG9mIHRoZSBwcmVkaWN0b3IgdmFyaWFibGVzLg0KDQojIyMgVVMgY29uc3VtcHRpb24gZXhwZW5kaXR1cmUNCg0KVGhlIHNjYXR0ZXJwbG90cyBzaG93IHBvc2l0aXZlIHJlbGF0aW9uc2hpcHMgd2l0aCBpbmNvbWUgYW5kIGluZHVzdHJpYWwgcHJvZHVjdGlvbiwgYW5kIG5lZ2F0aXZlIHJlbGF0aW9uc2hpcHMgd2l0aCBzYXZpbmdzIGFuZCB1bmVtcGxveW1lbnQuIFRoZSBzdHJlbmd0aCBvZiB0aGVzZSByZWxhdGlvbnNoaXBzIGFyZSBzaG93biBieSB0aGUgY29ycmVsYXRpb24gY29lZmZpY2llbnRzIGFjcm9zcyB0aGUgZmlyc3Qgcm93LiBUaGUgcmVtYWluaW5nIHNjYXR0ZXJwbG90cyBhbmQgY29ycmVsYXRpb24gY29lZmZpY2llbnRzIHNob3cgdGhlIHJlbGF0aW9uc2hpcHMgYmV0d2VlbiB0aGUgcHJlZGljdG9ycy4NCg0KYGBge3J9DQp1c19jaGFuZ2UgJT4lDQogIEdHYWxseTo6Z2dwYWlycyhjb2x1bW5zID0gMjo2KQ0KYGBgDQoNCiMjIyBBc3N1bXB0aW9ucw0KDQpGaXJzdCwgd2UgYXNzdW1lIHRoYXQgdGhlIG1vZGVsIGlzIGEgcmVhc29uYWJsZSBhcHByb3hpbWF0aW9uIHRvIHJlYWxpdHk7IHRoYXQgaXMsIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgZm9yZWNhc3QgdmFyaWFibGUgYW5kIHRoZSBwcmVkaWN0b3IgdmFyaWFibGVzIHNhdGlzZmllcyB0aGlzIGxpbmVhciBlcXVhdGlvbi4NCg0KU2Vjb25kLCB3ZSBtYWtlIHRoZSBmb2xsb3dpbmcgYXNzdW1wdGlvbnMgYWJvdXQgdGhlIGVycm9yczogLSB0aGV5IGhhdmUgbWVhbiB6ZXJvOyBvdGhlcndpc2UgdGhlIGZvcmVjYXN0cyB3aWxsIGJlIHN5c3RlbWF0aWNhbGx5IGJpYXNlZC4gLSB0aGV5IGFyZSBub3QgYXV0b2NvcnJlbGF0ZWQ7IG90aGVyd2lzZSB0aGUgZm9yZWNhc3RzIHdpbGwgYmUgaW5lZmZpY2llbnQsIGFzIHRoZXJlIGlzIG1vcmUgaW5mb3JtYXRpb24gaW4gdGhlIGRhdGEgdGhhdCBjYW4gYmUgZXhwbG9pdGVkLiAtIHRoZXkgYXJlIHVucmVsYXRlZCB0byB0aGUgcHJlZGljdG9yIHZhcmlhYmxlczsgb3RoZXJ3aXNlIHRoZXJlIHdvdWxkIGJlIG1vcmUgaW5mb3JtYXRpb24gdGhhdCBzaG91bGQgYmUgaW5jbHVkZWQgaW4gdGhlIHN5c3RlbWF0aWMgcGFydCBvZiB0aGUgbW9kZWwuDQoNCkl0IGlzIGFsc28gdXNlZnVsIHRvIGhhdmUgdGhlIGVycm9ycyBiZWluZyBub3JtYWxseSBkaXN0cmlidXRlZCB3aXRoIGEgY29uc3RhbnQgdmFyaWFuY2UgJM+DXjIkIGluIG9yZGVyIHRvIGVhc2lseSBwcm9kdWNlIHByZWRpY3Rpb24gaW50ZXJ2YWxzLg0KDQpBbm90aGVyIGltcG9ydGFudCBhc3N1bXB0aW9uIGluIHRoZSBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbCBpcyB0aGF0IGVhY2ggcHJlZGljdG9yICR4JCBpcyBub3QgYSByYW5kb20gdmFyaWFibGUuDQoNCiMjIyBMZWFzdCBzcXVhcmVzIGVzdGltYXRpb24NCg0KVGhlIGxlYXN0IHNxdWFyZXMgcHJpbmNpcGxlIHByb3ZpZGVzIGEgd2F5IG9mIGNob29zaW5nIHRoZSBjb2VmZmljaWVudHMgZWZmZWN0aXZlbHkgYnkgbWluaW1pc2luZyB0aGUgc3VtIG9mIHRoZSBzcXVhcmVkIGVycm9ycy4NCg0KYGBgez10ZXh9DQpcYmVnaW57ZXF1YXRpb259DQpcc3VtX3t0PTF9XlQgXHZhcmVwc2lsb25fdF4yID0gXHN1bV97dD0xfV5UICh5X3QgLQ0KICBcYmV0YV97MH0gLSBcYmV0YV97MX0geF97MSx0fSAtIFxiZXRhX3syfSB4X3syLHR9IC0gXGNkb3RzIC0gXGJldGFfe2t9IHhfe2ssdH0pXjIuDQpcZW5ke2VxdWF0aW9ufQ0KYGBgDQpUaGlzIGlzIGNhbGxlZCBsZWFzdCBzcXVhcmVzIGVzdGltYXRpb24gYmVjYXVzZSBpdCBnaXZlcyB0aGUgbGVhc3QgdmFsdWUgZm9yIHRoZSBzdW0gb2Ygc3F1YXJlZCBlcnJvcnMuDQoNClRoZSBUU0xNKCkgZnVuY3Rpb24gZml0cyBhIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsIHRvIHRpbWUgc2VyaWVzIGRhdGEuIEl0IGlzIHNpbWlsYXIgdG8gdGhlIGxtKCkgZnVuY3Rpb24gd2hpY2ggaXMgd2lkZWx5IHVzZWQgZm9yIGxpbmVhciBtb2RlbHMsIGJ1dCBUU0xNKCkgcHJvdmlkZXMgYWRkaXRpb25hbCBmYWNpbGl0aWVzIGZvciBoYW5kbGluZyB0aW1lIHNlcmllcy4NCg0KIyMjIFVTIGNvbnN1bXB0aW9uIGV4cGVuZGl0dXJlDQoNClRoZSBmb2xsb3dpbmcgb3V0cHV0IHByb3ZpZGVzIGluZm9ybWF0aW9uIGFib3V0IHRoZSBmaXR0ZWQgbW9kZWwuIFRoZSBmaXJzdCBjb2x1bW4gb2YgQ29lZmZpY2llbnRzIGdpdmVzIGFuIGVzdGltYXRlIG9mIGVhY2ggJM6yJCBjb2VmZmljaWVudCBhbmQgdGhlIHNlY29uZCBjb2x1bW4gZ2l2ZXMgaXRzIHN0YW5kYXJkIGVycm9yIChpLmUuLCB0aGUgc3RhbmRhcmQgZGV2aWF0aW9uIHdoaWNoIHdvdWxkIGJlIG9idGFpbmVkIGZyb20gcmVwZWF0ZWRseSBlc3RpbWF0aW5nIHRoZSAkzrIkIGNvZWZmaWNpZW50cyBvbiBzaW1pbGFyIGRhdGEgc2V0cykuIFRoZSBzdGFuZGFyZCBlcnJvciBnaXZlcyBhIG1lYXN1cmUgb2YgdGhlIHVuY2VydGFpbnR5IGluIHRoZSBlc3RpbWF0ZWQgJM6yJCBjb2VmZmljaWVudC4NCg0KYGBge3J9DQpmaXQuY29uc01SIDwtIHVzX2NoYW5nZSAlPiUNCiAgbW9kZWwodHNsbSA9IFRTTE0oQ29uc3VtcHRpb24gfiBJbmNvbWUgKyBQcm9kdWN0aW9uICsgVW5lbXBsb3ltZW50ICsgU2F2aW5ncykpDQpyZXBvcnQoZml0LmNvbnNNUikNCmBgYA0KDQpUaGUgInQgdmFsdWUiIGlzIHRoZSByYXRpbyBvZiBhbiBlc3RpbWF0ZWQgJM6yJCBjb2VmZmljaWVudCB0byBpdHMgc3RhbmRhcmQgZXJyb3IgYW5kIHRoZSBsYXN0IGNvbHVtbiBnaXZlcyB0aGUgcC12YWx1ZTogdGhlIHByb2JhYmlsaXR5IG9mIHRoZSBlc3RpbWF0ZWQgJM6yJCBjb2VmZmljaWVudCBiZWluZyBhcyBsYXJnZSBhcyBpdCBpcyBpZiB0aGVyZSB3YXMgbm8gcmVhbCByZWxhdGlvbnNoaXAgYmV0d2VlbiBjb25zdW1wdGlvbiBhbmQgdGhlIGNvcnJlc3BvbmRpbmcgcHJlZGljdG9yLiBUaGlzIGlzIHVzZWZ1bCB3aGVuIHN0dWR5aW5nIHRoZSBlZmZlY3Qgb2YgZWFjaCBwcmVkaWN0b3IsIGJ1dCBpcyBub3QgcGFydGljdWxhcmx5IHVzZWZ1bCBmb3IgZm9yZWNhc3RpbmcuDQoNCiMjIyBGaXR0ZWQgdmFsdWVzDQoNClRoZSBmb2xsb3dpbmcgcGxvdHMgc2hvdyB0aGUgYWN0dWFsIHZhbHVlcyBjb21wYXJlZCB0byB0aGUgZml0dGVkIHZhbHVlcyBmb3IgdGhlIHBlcmNlbnRhZ2UgY2hhbmdlIGluIHRoZSBVUyBjb25zdW1wdGlvbiBleHBlbmRpdHVyZSBzZXJpZXMuDQoNCmBgYHtyfQ0KYXVnbWVudChmaXQuY29uc01SKSAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gUXVhcnRlcikpICsNCiAgZ2VvbV9saW5lKGFlcyh5ID0gQ29uc3VtcHRpb24sIGNvbG91ciA9ICJEYXRhIikpICsNCiAgZ2VvbV9saW5lKGFlcyh5ID0gLmZpdHRlZCwgY29sb3VyID0gIkZpdHRlZCIpKSArDQogIGxhYnMoeSA9IE5VTEwsIHRpdGxlID0gIlBlcmNlbnQgY2hhbmdlIGluIFVTIGNvbnN1bXB0aW9uIGV4cGVuZGl0dXJlIikgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoRGF0YT0iYmxhY2siLEZpdHRlZD0icmVkIikpICsNCiAgZ3VpZGVzKGNvbG91ciA9IGd1aWRlX2xlZ2VuZCh0aXRsZSA9IE5VTEwpKQ0KYGBgDQoNCmBgYHtyfQ0KYXVnbWVudChmaXQuY29uc01SKSAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gQ29uc3VtcHRpb24sIHkgPSAuZml0dGVkKSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBsYWJzKHkgPSAiRml0dGVkIChwcmVkaWN0ZWQgdmFsdWVzKSIsDQogICAgICAgeCA9ICJEYXRhIChhY3R1YWwgdmFsdWVzKSIsDQogICAgICAgdGl0bGUgPSAiUGVyY2VudCBjaGFuZ2UgaW4gVVMgY29uc3VtcHRpb24gZXhwZW5kaXR1cmUiKSArDQogIGdlb21fYWJsaW5lKGludGVyY2VwdCA9IDAsIHNsb3BlID0gMSkNCmBgYA0KDQojIyMgR29vZG5lc3Mtb2YtZml0DQoNCkEgY29tbW9uIHdheSB0byBzdW1tYXJpc2UgaG93IHdlbGwgYSBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbCBmaXRzIHRoZSBkYXRhIGlzIHZpYSB0aGUgY29lZmZpY2llbnQgb2YgZGV0ZXJtaW5hdGlvbiwgb3IgJFJeMiQuDQoNCklmIHRoZSBwcmVkaWN0aW9ucyBhcmUgY2xvc2UgdG8gdGhlIGFjdHVhbCB2YWx1ZXMsIHdlIHdvdWxkIGV4cGVjICRSXjIkIHRvIGJlIGNsb3NlIHRvIDEuIE9uIHRoZSBvdGhlciBoYW5kLCBpZiB0aGUgcHJlZGljdGlvbnMgYXJlIHVucmVsYXRlZCB0byB0aGUgYWN0dWFsIHZhbHVlcywgdGhlbiAkUl4yID0gMCQgKGFnYWluLCBhc3N1bWluZyB0aGVyZSBpcyBhbiBpbnRlcmNlcHQpLiBJbiBhbGwgY2FzZXMsICRSXjIkIGxpZXMgYmV0d2VlbiAwIGFuZCAxLg0KDQpUaGUgdmFsdWUgb2YgJFJeMiQgd2lsbCBuZXZlciBkZWNyZWFzZSB3aGVuIGFkZGluZyBhbiBleHRyYSBwcmVkaWN0b3IgdG8gdGhlIG1vZGVsIGFuZCB0aGlzIGNhbiBsZWFkIHRvIG92ZXItZml0dGluZy4gVGhlcmUgYXJlIG5vIHNldCBydWxlcyBmb3Igd2hhdCBpcyBhIGdvb2QgJFJeMiQgdmFsdWUsIGFuZCB0eXBpY2FsIHZhbHVlcyBvZiAkUl4yJCBkZXBlbmQgb24gdGhlIHR5cGUgb2YgZGF0YSB1c2VkLiBWYWxpZGF0aW5nIGEgbW9kZWwncyBmb3JlY2FzdGluZyBwZXJmb3JtYW5jZSBvbiB0aGUgdGVzdCBkYXRhIGlzIG11Y2ggYmV0dGVyIHRoYW4gbWVhc3VyaW5nIHRoZSBSXF4yXCQgdmFsdWUgb24gdGhlIHRyYWluaW5nIGRhdGEuDQoNCiMjIyBTdGFuZGFyZCBlcnJvciBvZiB0aGUgcmVncmVzc2lvbg0KDQpBbm90aGVyIG1lYXN1cmUgb2YgaG93IHdlbGwgdGhlIG1vZGVsIGhhcyBmaXR0ZWQgdGhlIGRhdGEgaXMgdGhlIHN0YW5kYXJkIGRldmlhdGlvbiBvZiB0aGUgcmVzaWR1YWxzLCB3aGljaCBpcyBvZnRlbiBrbm93biBhcyB0aGUgInJlc2lkdWFsIHN0YW5kYXJkIGVycm9yLiINCg0KIyMgRXZhbHVhdGluZyB0aGUgcmVncmVzc2lvbiBtb2RlbA0KDQpUaGUgZGlmZmVyZW5jZXMgYmV0d2VlbiB0aGUgb2JzZXJ2ZWQgeSB2YWx1ZXMgYW5kIHRoZSBjb3JyZXNwb25kaW5nIGZpdHRlZCAkXGhhdHt5fSQgdmFsdWVzIGFyZSB0aGUgdHJhaW5pbmctc2V0IGVycm9ycyBvciAicmVzaWR1YWxzIg0KDQpQcm9wZXJ0aWVzIHJlc2lkdWFsczoNCg0KLSAgIHRoZSBhdmVyYWdlIG9mIHRoZSByZXNpZHVhbHMgaXMgemVybw0KLSAgIHRoZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRoZSByZXNpZHVhbHMgYW5kIHRoZSBvYnNlcnZhdGlvbnMgZm9yIHRoZSBwcmVkaWN0b3IgdmFyaWFibGUgaXMgYWxzbyB6ZXJvLg0KDQojIyMgQUNGIHBsb3Qgb2YgcmVzaWR1YWxzDQoNCkl0IGlzIGFsd2F5cyBhIGdvb2QgaWRlYSB0byBjaGVjayB3aGV0aGVyIHRoZSByZXNpZHVhbHMgYXJlIG5vcm1hbGx5IGRpc3RyaWJ1dGVkLiBBcyB3ZSBleHBsYWluZWQgZWFybGllciwgdGhpcyBpcyBub3QgZXNzZW50aWFsIGZvciBmb3JlY2FzdGluZywgYnV0IGl0IGRvZXMgbWFrZSB0aGUgY2FsY3VsYXRpb24gb2YgcHJlZGljdGlvbiBpbnRlcnZhbHMgbXVjaCBlYXNpZXIuDQoNCmBgYHtyfQ0KZml0LmNvbnNNUiAlPiUgZ2dfdHNyZXNpZHVhbHMoKQ0KYGBgDQoNCmBgYHtyfQ0KYXVnbWVudChmaXQuY29uc01SKSAlPiUgZmVhdHVyZXMoLmlubm92LCBsanVuZ19ib3gsIGxhZyA9IDEwLCBkb2YgPSA1KQ0KYGBgDQoNClRoZSB0aW1lIHBsb3Qgc2hvd3Mgc29tZSBjaGFuZ2luZyB2YXJpYXRpb24gb3ZlciB0aW1lLCBidXQgaXMgb3RoZXJ3aXNlIHJlbGF0aXZlbHkgdW5yZW1hcmthYmxlLiBUaGlzIGhldGVyb3NjZWRhc3RpY2l0eSB3aWxsIHBvdGVudGlhbGx5IG1ha2UgdGhlIHByZWRpY3Rpb24gaW50ZXJ2YWwgY292ZXJhZ2UgaW5hY2N1cmF0ZS4NCg0KVGhlIGhpc3RvZ3JhbSBzaG93cyB0aGF0IHRoZSByZXNpZHVhbHMgc2VlbSB0byBiZSBzbGlnaHRseSBza2V3ZWQsIHdoaWNoIG1heSBhbHNvIGFmZmVjdCB0aGUgY292ZXJhZ2UgcHJvYmFiaWxpdHkgb2YgdGhlIHByZWRpY3Rpb24gaW50ZXJ2YWxzLg0KDQpUaGUgYXV0b2NvcnJlbGF0aW9uIHBsb3Qgc2hvd3MgYSBzaWduaWZpY2FudCBzcGlrZSBhdCBsYWcgNywgYW5kIGEgc2lnbmlmaWNhbnQgTGp1bmctQm94IHRlc3QgYXQgdGhlIDUlIGxldmVsLiBIb3dldmVyLCB0aGUgYXV0b2NvcnJlbGF0aW9uIGlzIG5vdCBwYXJ0aWN1bGFybHkgbGFyZ2UsIGFuZCBhdCBsYWcgNyBpdCBpcyB1bmxpa2VseSB0byBoYXZlIGFueSBub3RpY2VhYmxlIGltcGFjdCBvbiB0aGUgZm9yZWNhc3RzIG9yIHRoZSBwcmVkaWN0aW9uIGludGVydmFscy4NCg0KIyMjIFJlc2lkdWFsIHBsb3RzIGFnYWluc3QgcHJlZGljdG9ycw0KDQpXZSB3b3VsZCBleHBlY3QgdGhlIHJlc2lkdWFscyB0byBiZSByYW5kb21seSBzY2F0dGVyZWQgd2l0aG91dCBzaG93aW5nIGFueSBzeXN0ZW1hdGljIHBhdHRlcm5zLiBBIHNpbXBsZSBhbmQgcXVpY2sgd2F5IHRvIGNoZWNrIHRoaXMgaXMgdG8gZXhhbWluZSBzY2F0dGVycGxvdHMgb2YgdGhlIHJlc2lkdWFscyBhZ2FpbnN0IGVhY2ggb2YgdGhlIHByZWRpY3RvciB2YXJpYWJsZXMuDQoNCkl0IGlzIGFsc28gbmVjZXNzYXJ5IHRvIHBsb3QgdGhlIHJlc2lkdWFscyBhZ2FpbnN0IGFueSBwcmVkaWN0b3JzIHRoYXQgYXJlIG5vdCBpbiB0aGUgbW9kZWwuIElmIGFueSBvZiB0aGVzZSBzaG93IGEgcGF0dGVybiwgdGhlbiB0aGUgY29ycmVzcG9uZGluZyBwcmVkaWN0b3IgbWF5IG5lZWQgdG8gYmUgYWRkZWQgdG8gdGhlIG1vZGVsDQoNCmBgYHtyfQ0KI3Jlc2lkdWFscyBmcm9tIHRoZSBtdWx0aXBsZSByZWdyZXNzaW9uIG1vZGVsIGZvciBmb3JlY2FzdGluZyBVUyBjb25zdW1wdGlvbiANCmRmIDwtIGxlZnRfam9pbih1c19jaGFuZ2UsIHJlc2lkdWFscyhmaXQuY29uc01SKSwgYnkgPSAiUXVhcnRlciIpDQpwMSA8LSBnZ3Bsb3QoZGYsIGFlcyh4ID0gSW5jb21lLCB5ID0gLnJlc2lkKSkgKw0KICBnZW9tX3BvaW50KCkgKyBsYWJzKHkgPSAiUmVzaWR1YWxzIikNCnAyIDwtIGdncGxvdChkZiwgYWVzKHggPSBQcm9kdWN0aW9uLCB5ID0gLnJlc2lkKSkgKw0KICBnZW9tX3BvaW50KCkgKyBsYWJzKHkgPSAiUmVzaWR1YWxzIikNCnAzIDwtIGdncGxvdChkZiwgYWVzKHggPSBTYXZpbmdzLCB5ID0gLnJlc2lkKSkgKw0KICBnZW9tX3BvaW50KCkgKyBsYWJzKHkgPSAiUmVzaWR1YWxzIikNCnA0IDwtIGdncGxvdChkZiwgYWVzKHggPSBVbmVtcGxveW1lbnQsIHkgPSAucmVzaWQpKSArDQogIGdlb21fcG9pbnQoKSArIGxhYnMoeSA9ICJSZXNpZHVhbHMiKQ0KDQpsaWJyYXJ5KGNvd3Bsb3QpDQpwbG90X2dyaWQocDEsIHAyLCBwMywgcDQsIGNvbHM9MikNCmBgYA0KDQojIyMgUmVzaWR1YWwgcGxvdHMgYWdhaW5zdCBmaXR0ZWQgdmFsdWVzDQoNCkEgcGxvdCBvZiB0aGUgcmVzaWR1YWxzIGFnYWluc3QgdGhlIGZpdHRlZCB2YWx1ZXMgc2hvdWxkIGFsc28gc2hvdyBubyBwYXR0ZXJuLiBJZiBhIHBhdHRlcm4gaXMgb2JzZXJ2ZWQsIHRoZXJlIG1heSBiZSAiaGV0ZXJvc2NlZGFzdGljaXR5IiBpbiB0aGUgZXJyb3JzIHdoaWNoIG1lYW5zIHRoYXQgdGhlIHZhcmlhbmNlIG9mIHRoZSByZXNpZHVhbHMgbWF5IG5vdCBiZSBjb25zdGFudC4gSWYgdGhpcyBwcm9ibGVtIG9jY3VycywgYSB0cmFuc2Zvcm1hdGlvbiBvZiB0aGUgZm9yZWNhc3QgdmFyaWFibGUgc3VjaCBhcyBhIGxvZ2FyaXRobSBvciBzcXVhcmUgcm9vdCBtYXkgYmUgcmVxdWlyZWQuDQoNCmBgYHtyfQ0KYXVnbWVudChmaXQuY29uc01SKSAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gLmZpdHRlZCwgeSA9IC5yZXNpZCkpICsNCiAgZ2VvbV9wb2ludCgpICsgbGFicyh4ID0gIkZpdHRlZCIsIHkgPSAiUmVzaWR1YWxzIikNCmBgYA0KDQojIyMgT3V0bGllcnMgYW5kIGluZmx1ZW50aWFsIG9ic2VydmF0aW9ucw0KDQpPYnNlcnZhdGlvbnMgdGhhdCB0YWtlIGV4dHJlbWUgdmFsdWVzIGNvbXBhcmVkIHRvIHRoZSBtYWpvcml0eSBvZiB0aGUgZGF0YSBhcmUgY2FsbGVkIG91dGxpZXJzLiBPYnNlcnZhdGlvbnMgdGhhdCBoYXZlIGEgbGFyZ2UgaW5mbHVlbmNlIG9uIHRoZSBlc3RpbWF0ZWQgY29lZmZpY2llbnRzIG9mIGEgcmVncmVzc2lvbiBtb2RlbCBhcmUgY2FsbGVkIGluZmx1ZW50aWFsIG9ic2VydmF0aW9ucy4gVXN1YWxseSwgaW5mbHVlbnRpYWwgb2JzZXJ2YXRpb25zIGFyZSBhbHNvIG91dGxpZXJzIHRoYXQgYXJlIGV4dHJlbWUgaW4gdGhlICR4JCBkaXJlY3Rpb24uDQoNCkEgc2NhdHRlciBwbG90IG9mICR5JCBhZ2FpbnN0IGVhY2ggJHgkIGlzIGFsd2F5cyBhIHVzZWZ1bCBzdGFydGluZyBwb2ludCBpbiByZWdyZXNzaW9uIGFuYWx5c2lzLCBhbmQgb2Z0ZW4gaGVscHMgdG8gaWRlbnRpZnkgdW51c3VhbCBvYnNlcnZhdGlvbnMuDQoNCkluY29ycmVjdCBkYXRhIGVudHJ5IHNob3VsZCBiZSBjb3JyZWN0ZWQgb3IgcmVtb3ZlZCBmcm9tIHRoZSBzYW1wbGUgaW1tZWRpYXRlbHkuDQoNCklmIGFuIG9ic2VydmF0aW9uIGhhcyBiZWVuIGlkZW50aWZpZWQgYXMgYSBsaWtlbHkgb3V0bGllciwgaXQgaXMgaW1wb3J0YW50IHRvIHN0dWR5IGl0IGFuZCBhbmFseXNlIHRoZSBwb3NzaWJsZSByZWFzb25zIGJlaGluZCBpdC4gVGhlIGRlY2lzaW9uIHRvIHJlbW92ZSBvciByZXRhaW4gYW4gb2JzZXJ2YXRpb24gY2FuIGJlIGEgY2hhbGxlbmdpbmcgb25lIChlc3BlY2lhbGx5IHdoZW4gb3V0bGllcnMgYXJlIGluZmx1ZW50aWFsIG9ic2VydmF0aW9ucykuIEl0IGlzIHdpc2UgdG8gcmVwb3J0IHJlc3VsdHMgYm90aCB3aXRoIGFuZCB3aXRob3V0IHRoZSByZW1vdmFsIG9mIHN1Y2ggb2JzZXJ2YXRpb25zLg0KDQojIyMgU3B1cmlvdXMgcmVncmVzc2lvbg0KDQpNb3JlIG9mdGVuIHRoYW4gbm90LCB0aW1lIHNlcmllcyBkYXRhIGFyZSAibm9uLXN0YXRpb25hcnkiOyB0aGF0IGlzLCB0aGUgdmFsdWVzIG9mIHRoZSB0aW1lIHNlcmllcyBkbyBub3QgZmx1Y3R1YXRlIGFyb3VuZCBhIGNvbnN0YW50IG1lYW4gb3Igd2l0aCBhIGNvbnN0YW50IHZhcmlhbmNlLg0KDQpSZWdyZXNzaW5nIG5vbi1zdGF0aW9uYXJ5IHRpbWUgc2VyaWVzIGNhbiBsZWFkIHRvIHNwdXJpb3VzIHJlZ3Jlc3Npb25zLiBUaGUgb3V0cHV0IG9mIHJlZ3Jlc3NpbmcgQXVzdHJhbGlhbiBhaXIgcGFzc2VuZ2VycyBvbiByaWNlIHByb2R1Y3Rpb24gaW4gR3VpbmVhIGlzIHNob3duIGluIEZpZ3VyZSA3LjEzLiBIaWdoICRSXjIkIGFuZCBoaWdoIHJlc2lkdWFsIGF1dG9jb3JyZWxhdGlvbiBjYW4gYmUgc2lnbnMgb2Ygc3B1cmlvdXMgcmVncmVzc2lvbi4gTm90aWNlIHRoZXNlIGZlYXR1cmVzIGluIHRoZSBvdXRwdXQgYmVsb3cuDQoNCmBgYHtyfQ0KZml0IDwtIGF1c19haXJwYXNzZW5nZXJzICU+JQ0KICBmaWx0ZXIoWWVhciA8PSAyMDExKSAlPiUNCiAgbGVmdF9qb2luKGd1aW5lYV9yaWNlLCBieSA9ICJZZWFyIikgJT4lDQogIG1vZGVsKFRTTE0oUGFzc2VuZ2VycyB+IFByb2R1Y3Rpb24pKQ0KcmVwb3J0KGZpdCkNCmBgYA0KDQpgYGB7cn0NCmZpdCAlPiUgZ2dfdHNyZXNpZHVhbHMoKQ0KYGBgDQoNCiMjIFNvbWUgdXNlZnVsIHByZWRpY3RvcnMNCg0KIyMjIFRyZW5kDQoNCkEgdHJlbmQgdmFyaWFibGUgY2FuIGJlIHNwZWNpZmllZCBpbiB0aGUgVFNMTSgpIGZ1bmN0aW9uIHVzaW5nIHRoZSB0cmVuZCgpIHNwZWNpYQ0KDQojIyMgRHVtbXkgdmFyaWFibGVzDQoNCkNhdGVnb3JpY2FsIHZhcmlhYmxlcyBsaWtlICJ5ZXMiIG9yICJubyIuIGhpcyBzaXR1YXRpb24gY2FuIHN0aWxsIGJlIGhhbmRsZWQgd2l0aGluIHRoZSBmcmFtZXdvcmsgb2YgbXVsdGlwbGUgcmVncmVzc2lvbiBtb2RlbHMgYnkgY3JlYXRpbmcgYSAiZHVtbXkgdmFyaWFibGUiIHdoaWNoIHRha2VzIHZhbHVlIDEgY29ycmVzcG9uZGluZyB0byAieWVzIiBhbmQgMCBjb3JyZXNwb25kaW5nIHRvICJuby4iIEEgZHVtbXkgdmFyaWFibGUgaXMgYWxzbyBrbm93biBhcyBhbiAiaW5kaWNhdG9yIHZhcmlhYmxlLiINCg0KQSBkdW1teSB2YXJpYWJsZSBjYW4gYWxzbyBiZSB1c2VkIHRvIGFjY291bnQgZm9yIGFuIG91dGxpZXIgaW4gdGhlIGRhdGEuIFJhdGhlciB0aGFuIG9taXQgdGhlIG91dGxpZXIsIGEgZHVtbXkgdmFyaWFibGUgcmVtb3ZlcyBpdHMgZWZmZWN0LiBJbiB0aGlzIGNhc2UsIHRoZSBkdW1teSB2YXJpYWJsZSB0YWtlcyB2YWx1ZSAxIGZvciB0aGF0IG9ic2VydmF0aW9uIGFuZCAwIGV2ZXJ5d2hlcmUNCg0KVFNMTSgpIHdpbGwgYXV0b21hdGljYWxseSBoYW5kbGUgdGhpcyBjYXNlIGlmIHlvdSBzcGVjaWZ5IGEgZmFjdG9yIHZhcmlhYmxlIGFzIGEgcHJlZGljdG9yLiBUaGVyZSBpcyB1c3VhbGx5IG5vIG5lZWQgdG8gbWFudWFsbHkgY3JlYXRlIHRoZSBjb3JyZXNwb25kaW5nIGR1bW15IHZhcmlhYmxlcy4NCg0KIyMjIFNlYXNvbmFsIGR1bW15IHZhcmlhYmxlcw0KDQpUaGUgZ2VuZXJhbCBydWxlIGlzIHRvIHVzZSBvbmUgZmV3ZXIgZHVtbXkgdmFyaWFibGVzIHRoYW4gY2F0ZWdvcmllcy4gU28gZm9yIHF1YXJ0ZXJseSBkYXRhLCB1c2UgdGhyZWUgZHVtbXkgdmFyaWFibGVzOyBmb3IgbW9udGhseSBkYXRhLCB1c2UgMTEgZHVtbXkgdmFyaWFibGVzOyBhbmQgZm9yIGRhaWx5IGRhdGEsIHVzZSBzaXggZHVtbXkgdmFyaWFibGVzLCBhbmQgc28gb24uDQoNClRoZSBpbnRlcnByZXRhdGlvbiBvZiBlYWNoIG9mIHRoZSBjb2VmZmljaWVudHMgYXNzb2NpYXRlZCB3aXRoIHRoZSBkdW1teSB2YXJpYWJsZXMgaXMgdGhhdCBpdCBpcyBhIG1lYXN1cmUgb2YgdGhlIGVmZmVjdCBvZiB0aGF0IGNhdGVnb3J5IHJlbGF0aXZlIHRvIHRoZSBvbWl0dGVkIGNhdGVnb3J5Lg0KDQpUaGUgVFNMTSgpIGZ1bmN0aW9uIHdpbGwgYXV0b21hdGljYWxseSBoYW5kbGUgdGhpcyBzaXR1YXRpb24gaWYgeW91IHNwZWNpZnkgdGhlIHNwZWNpYWwgc2Vhc29uKCkuDQoNCmBgYHtyfQ0KcmVjZW50X3Byb2R1Y3Rpb24gPC0gYXVzX3Byb2R1Y3Rpb24gJT4lDQogIGZpbHRlcih5ZWFyKFF1YXJ0ZXIpID49IDE5OTIpDQpyZWNlbnRfcHJvZHVjdGlvbiAlPiUNCiAgYXV0b3Bsb3QoQmVlcikgKw0KICBsYWJzKHkgPSAiTWVnYWxpdHJlcyIpDQpgYGANCg0KYGBge3J9DQpmaXRfYmVlciA8LSByZWNlbnRfcHJvZHVjdGlvbiAlPiUNCiAgbW9kZWwoVFNMTShCZWVyIH4gdHJlbmQoKSArIHNlYXNvbigpKSkNCnJlcG9ydChmaXRfYmVlcikNCmBgYA0KDQpUaGVyZSBpcyBhbiBhdmVyYWdlIGRvd253YXJkIHRyZW5kIG9mIC0wLjM0IG1lZ2FsaXRyZXMgcGVyIHF1YXJ0ZXIuIE9uIGF2ZXJhZ2UsIHRoZSBzZWNvbmQgcXVhcnRlciBoYXMgcHJvZHVjdGlvbiBvZiAzNC43IG1lZ2FsaXRyZXMgbG93ZXIgdGhhbiB0aGUgZmlyc3QgcXVhcnRlciwgdGhlIHRoaXJkIHF1YXJ0ZXIgaGFzIHByb2R1Y3Rpb24gb2YgMTcuOCBtZWdhbGl0cmVzIGxvd2VyIHRoYW4gdGhlIGZpcnN0IHF1YXJ0ZXIsIGFuZCB0aGUgZm91cnRoIHF1YXJ0ZXIgaGFzIHByb2R1Y3Rpb24gb2YgNzIuOCBtZWdhbGl0cmVzIGhpZ2hlciB0aGFuIHRoZSBmaXJzdCBxdWFydGVyLg0KDQpgYGB7cn0NCmF1Z21lbnQoZml0X2JlZXIpICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBRdWFydGVyKSkgKw0KICBnZW9tX2xpbmUoYWVzKHkgPSBCZWVyLCBjb2xvdXIgPSAiRGF0YSIpKSArDQogIGdlb21fbGluZShhZXMoeSA9IC5maXR0ZWQsIGNvbG91ciA9ICJGaXR0ZWQiKSkgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYyhEYXRhID0gImJsYWNrIiwgRml0dGVkID0gInJlZCIpKSArDQogIGxhYnMoeSA9ICJNZWdhbGl0cmVzIiwgdGl0bGUgPSAiUXVhcnRlcmx5IEJlZXIgUHJvZHVjdGlvbiIpICsNCiAgZ3VpZGVzKGNvbG91ciA9IGd1aWRlX2xlZ2VuZCh0aXRsZSA9ICJTZXJpZXMiKSkNCmBgYA0KDQpgYGB7cn0NCmF1Z21lbnQoZml0X2JlZXIpICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBCZWVyLCB5ID0gLmZpdHRlZCwgY29sb3VyID0gZmFjdG9yKHF1YXJ0ZXIoUXVhcnRlcikpKSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBsYWJzKHkgPSAiRml0dGVkIiwgeCA9ICJBY3R1YWwgdmFsdWVzIiwNCiAgICAgICB0aXRsZSA9ICJRdWFydGVybHkgYmVlciBwcm9kdWN0aW9uIikgKw0KICBnZW9tX2FibGluZShpbnRlcmNlcHQgPSAwLCBzbG9wZSA9IDEpICsNCiAgZ3VpZGVzKGNvbG91ciA9IGd1aWRlX2xlZ2VuZCh0aXRsZSA9ICJRdWFydGVyIikpDQpgYGANCg0KIyMjIEludGVydmVudGlvbiB2YXJpYWJsZXMNCg0KSXQgaXMgb2Z0ZW4gbmVjZXNzYXJ5IHRvIG1vZGVsIGludGVydmVudGlvbnMgdGhhdCBtYXkgaGF2ZSBhZmZlY3RlZCB0aGUgdmFyaWFibGUgdG8gYmUgZm9yZWNhc3QuIEZvciBleGFtcGxlLCBjb21wZXRpdG9yIGFjdGl2aXR5LCBhZHZlcnRpc2luZyBleHBlbmRpdHVyZSwgaW5kdXN0cmlhbCBhY3Rpb24sIGFuZCBzbyBvbiwgY2FuIGFsbCBoYXZlIGFuIGVmZmVjdC4NCg0KV2hlbiB0aGUgZWZmZWN0IGxhc3RzIG9ubHkgZm9yIG9uZSBwZXJpb2QsIHdlIHVzZSBhICJzcGlrZSIgdmFyaWFibGUuIFRoaXMgaXMgYSBkdW1teSB2YXJpYWJsZSB0aGF0IHRha2VzIHZhbHVlIG9uZSBpbiB0aGUgcGVyaW9kIG9mIHRoZSBpbnRlcnZlbnRpb24gYW5kIHplcm8gZWxzZXdoZXJlLiBBIHNwaWtlIHZhcmlhYmxlIGlzIGVxdWl2YWxlbnQgdG8gYSBkdW1teSB2YXJpYWJsZSBmb3IgaGFuZGxpbmcgYW4gb3V0bGllci4NCg0KT3RoZXIgaW50ZXJ2ZW50aW9ucyBoYXZlIGFuIGltbWVkaWF0ZSBhbmQgcGVybWFuZW50IGVmZmVjdC4gQSBzdGVwIHZhcmlhYmxlIHRha2VzIHZhbHVlIHplcm8gYmVmb3JlIHRoZSBpbnRlcnZlbnRpb24gYW5kIG9uZSBmcm9tIHRoZSB0aW1lIG9mIGludGVydmVudGlvbiBvbndhcmQuDQoNCiMjIyBUcmFkaW5nIGRheXMNCg0KVGhlIG51bWJlciBvZiB0cmFkaW5nIGRheXMgaW4gYSBtb250aCBjYW4gdmFyeSBjb25zaWRlcmFibHkgYW5kIGNhbiBoYXZlIGEgc3Vic3RhbnRpYWwgZWZmZWN0IG9uIHNhbGVzIGRhdGEuIFRvIGFsbG93IGZvciB0aGlzLCB0aGUgbnVtYmVyIG9mIHRyYWRpbmcgZGF5cyBpbiBlYWNoIG1vbnRoIGNhbiBiZSBpbmNsdWRlZCBhcyBhIHByZWRpY3Rvci4gQW5vdGhlciBmb3JtIG9mIHBlcm1hbmVudCBlZmZlY3QgaXMgYSBjaGFuZ2Ugb2Ygc2xvcGUuDQoNCiMjIyBEaXN0cmlidXRlZCBsYWdzDQoNCkl0IGlzIG9mdGVuIHVzZWZ1bCB0byBpbmNsdWRlIGFkdmVydGlzaW5nIGV4cGVuZGl0dXJlIGFzIGEgcHJlZGljdG9yLiBIb3dldmVyLCBzaW5jZSB0aGUgZWZmZWN0IG9mIGFkdmVydGlzaW5nIGNhbiBsYXN0IGJleW9uZCB0aGUgYWN0dWFsIGNhbXBhaWduLCB3ZSBuZWVkIHRvIGluY2x1ZGUgbGFnZ2VkIHZhbHVlcyBvZiBhZHZlcnRpc2luZyBleHBlbmRpdHVyZS4NCg0KIyMjIEVhc3Rlcg0KDQpFYXN0ZXIgZGlmZmVycyBmcm9tIG1vc3QgaG9saWRheXMgYmVjYXVzZSBpdCBpcyBub3QgaGVsZCBvbiB0aGUgc2FtZSBkYXRlIGVhY2ggeWVhciwgYW5kIGl0cyBlZmZlY3QgY2FuIGxhc3QgZm9yIHNldmVyYWwgZGF5cy4gSW4gdGhpcyBjYXNlLCBhIGR1bW15IHZhcmlhYmxlIGNhbiBiZSB1c2VkIHdpdGggdmFsdWUgb25lIHdoZXJlIHRoZSBob2xpZGF5IGZhbGxzIGluIHRoZSBwYXJ0aWN1bGFyIHRpbWUgcGVyaW9kIGFuZCB6ZXJvIG90aGVyd2lzZS4NCg0KIyMjIEZvdXJpZXIgc2VyaWVzDQoNCkFuIGFsdGVybmF0aXZlIHRvIHVzaW5nIHNlYXNvbmFsIGR1bW15IHZhcmlhYmxlcywgZXNwZWNpYWxseSBmb3IgbG9uZyBzZWFzb25hbCBwZXJpb2RzLCBpcyB0byB1c2UgRm91cmllciB0ZXJtcy4NCg0KSWYgJG0kaXMgdGhlIHNlYXNvbmFsIHBlcmlvZCwgdGhlbiB0aGUgZmlyc3QgZmV3IEZvdXJpZXIgdGVybXMgYXJlIGdpdmVuIGJ5DQoNCmBgYHs9dGV4fQ0KXGJlZ2lue2VxdWF0aW9ufQ0KeF97MSx0fSA9IFxzaW5cbGVmdChcdGV4dHN0eWxlXGZyYWN7MlxwaSB0fXttfVxyaWdodCksDQogIHhfezIsdH0gPSBcY29zXGxlZnQoXHRleHRzdHlsZVxmcmFjezJccGkgdH17bX1ccmlnaHQpLA0KICB4X3szLHR9ID0gXHNpblxsZWZ0KFx0ZXh0c3R5bGVcZnJhY3s0XHBpIHR9e219XHJpZ2h0KSwNClxlbmR7ZXF1YXRpb259DQpgYGANCmBgYHs9dGV4fQ0KXGJlZ2lue2VxdWF0aW9ufQ0KeF97NCx0fSA9IFxjb3NcbGVmdChcdGV4dHN0eWxlXGZyYWN7NFxwaSB0fXttfVxyaWdodCksDQogIHhfezUsdH0gPSBcc2luXGxlZnQoXHRleHRzdHlsZVxmcmFjezZccGkgdH17bX1ccmlnaHQpLA0KICB4X3s2LHR9ID0gXGNvc1xsZWZ0KFx0ZXh0c3R5bGVcZnJhY3s2XHBpIHR9e219XHJpZ2h0KSwNClxlbmR7ZXF1YXRpb259DQpgYGANCldpdGggRm91cmllciB0ZXJtcywgd2Ugb2Z0ZW4gbmVlZCBmZXdlciBwcmVkaWN0b3JzIHRoYW4gd2l0aCBkdW1teSB2YXJpYWJsZXMsIGVzcGVjaWFsbHkgd2hlbiAkbSQgaXMgbGFyZ2UuIFRoaXMgbWFrZXMgdGhlbSB1c2VmdWwgZm9yIHdlZWtseSBkYXRhLCBmb3IgZXhhbXBsZSwgd2hlcmUgJG0g4omIIDUyJC4gRm9yIHNob3J0IHNlYXNvbmFsIHBlcmlvZHMgKGUuZy4sIHF1YXJ0ZXJseSBkYXRhKSwgdGhlcmUgaXMgbGl0dGxlIGFkdmFudGFnZSBpbiB1c2luZyBGb3VyaWVyIHRlcm1zIG92ZXIgc2Vhc29uYWwgZHVtbXkgdmFyaWFibGVzLg0KDQpgYGB7cn0NCmZvdXJpZXJfYmVlciA8LSByZWNlbnRfcHJvZHVjdGlvbiAlPiUNCiAgbW9kZWwoVFNMTShCZWVyIH4gdHJlbmQoKSArIGZvdXJpZXIoSyA9IDIpKSkNCnJlcG9ydChmb3VyaWVyX2JlZXIpDQpgYGANCg0KVGhlIEsgYXJndW1lbnQgdG8gZm91cmllcigpIHNwZWNpZmllcyBob3cgbWFueSBwYWlycyBvZiBzaW4gYW5kIGNvcyB0ZXJtcyB0byBpbmNsdWRlLiBUaGUgbWF4aW11bSBhbGxvd2VkIGlzICRLID0gbS8yJCB3aGVyZSAkbSQgaXMgdGhlIHNlYXNvbmFsIHBlcmlvZC4gQmVjYXVzZSB3ZSBoYXZlIHVzZWQgdGhlIG1heGltdW0gaGVyZSwgdGhlIHJlc3VsdHMgYXJlIGlkZW50aWNhbCB0byB0aG9zZSBvYnRhaW5lZCB3aGVuIHVzaW5nIHNlYXNvbmFsIGR1bW15IHZhcmlhYmxlcy4NCg0KQSByZWdyZXNzaW9uIG1vZGVsIGNvbnRhaW5pbmcgRm91cmllciB0ZXJtcyBpcyBvZnRlbiBjYWxsZWQgYSBoYXJtb25pYyByZWdyZXNzaW9uIGJlY2F1c2UgdGhlIHN1Y2Nlc3NpdmUgRm91cmllciB0ZXJtcyByZXByZXNlbnQgaGFybW9uaWNzIG9mIHRoZSBmaXJzdCB0d28gRm91cmllciB0ZXJtcy4NCg0KIyMgU2VsZWN0aW5nIHByZWRpY3RvcnMNCg0KV2Ugd2lsbCB1c2UgYSBtZWFzdXJlIG9mIHByZWRpY3RpdmUgYWNjdXJhY3kuIFRoZXkgY2FuIGJlIHNob3duIHVzaW5nIHRoZSBnbGFuY2UoKSBmdW5jdGlvbiwgaGVyZSBhcHBsaWVkIHRvIHRoZSBtb2RlbCBmb3IgVVMgY29uc3VtcHRpb246DQoNCmBgYHtyfQ0KZ2xhbmNlKGZpdC5jb25zTVIpICU+JSBzZWxlY3QoYWRqX3Jfc3F1YXJlZCwgQ1YsIEFJQywgQUlDYywgQklDKQ0KYGBgDQoNCldlIGNvbXBhcmUgdGhlc2UgdmFsdWVzIGFnYWluc3QgdGhlIGNvcnJlc3BvbmRpbmcgdmFsdWVzIGZyb20gb3RoZXIgbW9kZWxzLiBGb3IgdGhlIENWLCBBSUMsIEFJQ2MgYW5kIEJJQyBtZWFzdXJlcywgd2Ugd2FudCB0byBmaW5kIHRoZSBtb2RlbCB3aXRoIHRoZSBsb3dlc3QgdmFsdWU7IGZvciBBZGp1c3RlZCAkUl4yJCwgd2Ugc2VlayB0aGUgbW9kZWwgd2l0aCB0aGUgaGlnaGVzdCB2YWx1ZS4NCg0KIyMjIEFkanVzdGVkICRSXjIkDQoNCmBgYHs9dGV4fQ0KXGJlZ2lue2VxdWF0aW9ufQ0KXGJhcntSfV4yID0gMS0oMS1SXjIpXGZyYWN7VC0xfXtULWstMX0sDQpcZW5ke2VxdWF0aW9ufQ0KYGBgDQpUaGlzIGlzIGFuIGltcHJvdmVtZW50IG9uICRSXjIkLCBhcyBpdCB3aWxsIG5vIGxvbmdlciBpbmNyZWFzZSB3aXRoIGVhY2ggYWRkZWQgcHJlZGljdG9yLiBVc2luZyB0aGlzIG1lYXN1cmUsIHRoZSBiZXN0IG1vZGVsIHdpbGwgYmUgdGhlIG9uZSB3aXRoIHRoZSBsYXJnZXN0IHZhbHVlIG9mICRcYmFye1J9XjIkLiBNYXhpbWlzaW5nICRcYmFye1J9XjIkaXMgZXF1aXZhbGVudCB0byBtaW5pbWlzaW5nIHRoZSBzdGFuZGFyZCBlcnJvciAkXGhhdHtcc2lnbWF9X2UkLg0KDQpNYXhpbWlzaW5nJFxiYXJ7Un1eMiQgd29ya3MgcXVpdGUgd2VsbCBhcyBhIG1ldGhvZCBvZiBzZWxlY3RpbmcgcHJlZGljdG9ycywgYWx0aG91Z2ggaXQgZG9lcyB0ZW5kIHRvIGVyciBvbiB0aGUgc2lkZSBvZiBzZWxlY3RpbmcgdG9vIG1hbnkgcHJlZGljdG9ycy4NCg0KIyMjIENyb3NzLXZhbGlkYXRpb24NCg0KRm9yIHJlZ3Jlc3Npb24gbW9kZWxzLCBpdCBpcyBhbHNvIHBvc3NpYmxlIHRvIHVzZSBjbGFzc2ljYWwgbGVhdmUtb25lLW91dCBjcm9zcy12YWxpZGF0aW9uIHRvIHNlbGVjdGlvbiBwcmVkaWN0b3JzOg0KDQoxLiAgUmVtb3ZlIG9ic2VydmF0aW9uICR0JCBmcm9tIHRoZSBkYXRhIHNldCwgYW5kIGZpdCB0aGUgbW9kZWwgdXNpbmcgdGhlIHJlbWFpbmluZyBkYXRhLiBUaGVuIGNvbXB1dGUgdGhlIGVycm9yICQoZeKIl3Q9eV904oiSXGhhdHt5fV90KSQgZm9yIHRoZSBvbWl0dGVkIG9ic2VydmF0aW9uLiAoVGhpcyBpcyBub3QgdGhlIHNhbWUgYXMgdGhlIHJlc2lkdWFsIGJlY2F1c2UgdGhlICR0JHRoIG9ic2VydmF0aW9uIHdhcyBub3QgdXNlZCBpbiBlc3RpbWF0aW5nIHRoZSB2YWx1ZSBvZiAkXGhhdHt5fV90JC4pDQoyLiAgUmVwZWF0IHN0ZXAgMSBmb3IgJHQgPTEsIOKApixUJC4NCjMuICBDb21wdXRlIHRoZSBNU0UgZnJvbSAkZV7iiJdfMSzigKYsZV7iiJdfVCQuIFdlIHNoYWxsIGNhbGwgdGhpcyB0aGUgQ1YuDQoNClVuZGVyIHRoaXMgY3JpdGVyaW9uLCB0aGUgYmVzdCBtb2RlbCBpcyB0aGUgb25lIHdpdGggdGhlIHNtYWxsZXN0IHZhbHVlIG9mIENWLg0KDQojIyMgQWthaWtlJ3MgSW5mb3JtYXRpb24gQ3JpdGVyaW9uDQoNClRoZSBtb2RlbCB3aXRoIHRoZSBtaW5pbXVtIHZhbHVlIG9mIHRoZSBBSUMgaXMgb2Z0ZW4gdGhlIGJlc3QgbW9kZWwgZm9yIGZvcmVjYXN0aW5nLiBGb3IgbGFyZ2UgdmFsdWVzIG9mICRUJCwgbWluaW1pc2luZyB0aGUgQUlDIGlzIGVxdWl2YWxlbnQgdG8gbWluaW1pc2luZyB0aGUgQ1YgdmFsdWUuDQoNCiMjIyBDb3JyZWN0ZWQgQWthaWtlJ3MgSW5mb3JtYXRpb24gQ3JpdGVyaW9uDQoNCkZvciBzbWFsbCB2YWx1ZXMgb2YgJFQkLCB0aGUgQUlDIHRlbmRzIHRvIHNlbGVjdCB0b28gbWFueSBwcmVkaWN0b3JzLCBhbmQgc28gYSBiaWFzLWNvcnJlY3RlZCB2ZXJzaW9uIG9mIHRoZSBBSUMgaGFzIGJlZW4gZGV2ZWxvcGVkLiAkQUlDYyQgc2hvdWxkIGJlIG1pbmltaXNlZC4NCg0KIyMjIFNjaHdhcnoncyBCYXllc2lhbiBJbmZvcm1hdGlvbiBDcml0ZXJpb24NCg0KU2Nod2FyeidzIEJheWVzaWFuIEluZm9ybWF0aW9uIENyaXRlcmlvbiAodXN1YWxseSBhYmJyZXZpYXRlZCB0byBCSUMsIFNCSUMgb3IgU0MpLiBNaW5pbWlzaW5nIHRoZSBCSUMgaXMgaW50ZW5kZWQgdG8gZ2l2ZSB0aGUgYmVzdCBtb2RlbC4NCg0KIyMjIFdoaWNoIG1lYXN1cmUgc2hvdWxkIHdlIHVzZT8NCg0KV2UgcmVjb21tZW5kIHRoYXQgb25lIG9mIHRoZSBBSUNjLCBBSUMsIG9yIENWIHN0YXRpc3RpY3MgYmUgdXNlZCwgZWFjaCBvZiB3aGljaCBoYXMgZm9yZWNhc3RpbmcgYXMgdGhlaXIgb2JqZWN0aXZlLiBJZiB0aGUgdmFsdWUgb2YgVCBpcyBsYXJnZSBlbm91Z2gsIHRoZXkgd2lsbCBhbGwgbGVhZCB0byB0aGUgc2FtZSBtb2RlbC4NCg0KIyMjIEJlc3Qgc3Vic2V0IHJlZ3Jlc3Npb24NCg0KV2hlcmUgcG9zc2libGUsIGFsbCBwb3RlbnRpYWwgcmVncmVzc2lvbiBtb2RlbHMgc2hvdWxkIGJlIGZpdHRlZCAoYXMgd2FzIGRvbmUgaW4gdGhlIGV4YW1wbGUgYWJvdmUpIGFuZCB0aGUgYmVzdCBtb2RlbCBzaG91bGQgYmUgc2VsZWN0ZWQgYmFzZWQgb24gb25lIG9mIHRoZSBtZWFzdXJlcyBkaXNjdXNzZWQuIFRoaXMgaXMga25vd24gYXMgImJlc3Qgc3Vic2V0cyIgcmVncmVzc2lvbiBvciAiYWxsIHBvc3NpYmxlIHN1YnNldHMiIHJlZ3Jlc3Npb24uDQoNCiMjIyBTdGVwd2lzZSByZWdyZXNzaW9uDQoNCkFuIGFwcHJvYWNoIHRoYXQgd29ya3MgcXVpdGUgd2VsbCBpcyBiYWNrd2FyZHMgc3RlcHdpc2UgcmVncmVzc2lvbjoNCg0KLSAgIFN0YXJ0IHdpdGggdGhlIG1vZGVsIGNvbnRhaW5pbmcgYWxsIHBvdGVudGlhbCBwcmVkaWN0b3JzLg0KLSAgIFJlbW92ZSBvbmUgcHJlZGljdG9yIGF0IGEgdGltZS4gS2VlcCB0aGUgbW9kZWwgaWYgaXQgaW1wcm92ZXMgdGhlIG1lYXN1cmUgb2YgcHJlZGljdGl2ZSBhY2N1cmFjeS4NCi0gICBJdGVyYXRlIHVudGlsIG5vIGZ1cnRoZXIgaW1wcm92ZW1lbnQuDQoNCklmIHRoZSBudW1iZXIgb2YgcG90ZW50aWFsIHByZWRpY3RvcnMgaXMgdG9vIGxhcmdlLCB0aGVuIHRoZSBiYWNrd2FyZHMgc3RlcHdpc2UgcmVncmVzc2lvbiB3aWxsIG5vdCB3b3JrIGFuZCBmb3J3YXJkIHN0ZXB3aXNlIHJlZ3Jlc3Npb24gY2FuIGJlIHVzZWQgaW5zdGVhZC4gVGhpcyBwcm9jZWR1cmUgc3RhcnRzIHdpdGggYSBtb2RlbCB0aGF0IGluY2x1ZGVzIG9ubHkgdGhlIGludGVyY2VwdC4gUHJlZGljdG9ycyBhcmUgYWRkZWQgb25lIGF0IGEgdGltZSwgYW5kIHRoZSBvbmUgdGhhdCBtb3N0IGltcHJvdmVzIHRoZSBtZWFzdXJlIG9mIHByZWRpY3RpdmUgYWNjdXJhY3kgaXMgcmV0YWluZWQgaW4gdGhlIG1vZGVsLiBUaGUgcHJvY2VkdXJlIGlzIHJlcGVhdGVkIHVudGlsIG5vIGZ1cnRoZXIgaW1wcm92ZW1lbnQgY2FuIGJlIGFjaGlldmVkLg0KDQojIyBGb3JlY2FzdGluZyB3aXRoIHJlZ3Jlc3Npb24NCg0KIyMjIEV4LWFudGUgdmVyc3VzIGV4LXBvc3QgZm9yZWNhc3RzDQoNCioqRXgtYW50ZSBmb3JlY2FzdHMqKiBhcmUgdGhvc2UgdGhhdCBhcmUgbWFkZSB1c2luZyBvbmx5IHRoZSBpbmZvcm1hdGlvbiB0aGF0IGlzIGF2YWlsYWJsZSBpbiBhZHZhbmNlLiBNaWdodCByZXF1aXJlIGZvcmVjYXN0cyBvZiB0aGUgcHJlZGljdG9ycy4NCg0KKipFeC1wb3N0IGZvcmVjYXN0cyoqIGFyZSB0aG9zZSB0aGF0IGFyZSBtYWRlIHVzaW5nIGxhdGVyIGluZm9ybWF0aW9uIG9uIHRoZSBwcmVkaWN0b3JzLg0KDQpUaGUgbW9kZWwgZnJvbSB3aGljaCBleC1wb3N0IGZvcmVjYXN0cyBhcmUgcHJvZHVjZWQgc2hvdWxkIG5vdCBiZSBlc3RpbWF0ZWQgdXNpbmcgZGF0YSBmcm9tIHRoZSBmb3JlY2FzdCBwZXJpb2QuIFRoYXQgaXMsIGV4LXBvc3QgZm9yZWNhc3RzIGNhbiBhc3N1bWUga25vd2xlZGdlIG9mIHRoZSBwcmVkaWN0b3IgdmFyaWFibGVzICh0aGUgeCB2YXJpYWJsZXMpLCBidXQgc2hvdWxkIG5vdCBhc3N1bWUga25vd2xlZGdlIG9mIHRoZSBkYXRhIHRoYXQgYXJlIHRvIGJlIGZvcmVjYXN0ICh0aGUgeSB2YXJpYWJsZSkuDQoNCkEgY29tcGFyYXRpdmUgZXZhbHVhdGlvbiBvZiBleC1hbnRlIGZvcmVjYXN0cyBhbmQgZXgtcG9zdCBmb3JlY2FzdHMgY2FuIGhlbHAgdG8gc2VwYXJhdGUgb3V0IHRoZSBzb3VyY2VzIG9mIGZvcmVjYXN0IHVuY2VydGFpbnR5Lg0KDQojIyMjIEF1c3RyYWxpYW4gcXVhcnRlcmx5IGJlZXIgcHJvZHVjdGlvbg0KDQpDYWxlbmRhciBwcmVkaWN0b3JzIG9yIGRldGVybWluaXN0aWMgZnVuY3Rpb25zIG1ha2Ugbm8gZGlmZmVyZW5jZSBiZXR3ZWVuIGV4LWFudGUgYW5kIGV4LXBvc3QgZm9yZWNhc3RzLg0KDQpgYGB7cn0NCnJlY2VudF9wcm9kdWN0aW9uIDwtIGF1c19wcm9kdWN0aW9uICU+JSANCiAgZmlsdGVyKHllYXIoUXVhcnRlcikgPj0gMTk5MikNCmZpdF9iZWVyIDwtIHJlY2VudF9wcm9kdWN0aW9uICU+JQ0KICBtb2RlbChUU0xNKEJlZXIgfiB0cmVuZCgpICsgc2Vhc29uKCkpKQ0KZmNfYmVlciA8LSBmb3JlY2FzdChmaXRfYmVlcikNCmZjX2JlZXIgJT4lDQogIGF1dG9wbG90KHJlY2VudF9wcm9kdWN0aW9uKSArDQogIGxhYnModGl0bGUgPSAiRm9yZWNhc3RzIG9mIGJlZXIgcHJvZHVjdGlvbiB1c2luZyByZWdyZXNzaW9uIiwNCiAgICAgICB5ID0gIm1lZ2FsaXRyZXMiKQ0KYGBgDQoNCiMjIyBTY2VuYXJpbyBiYXNlZCBmb3JlY2FzdGluZw0KDQpJbiB0aGlzIHNldHRpbmcsIHRoZSBmb3JlY2FzdGVyIGFzc3VtZXMgcG9zc2libGUgc2NlbmFyaW9zIGZvciB0aGUgcHJlZGljdG9yIHZhcmlhYmxlcyB0aGF0IGFyZSBvZiBpbnRlcmVzdC4gRm9yIGV4YW1wbGUsIGEgVVMgcG9saWN5IG1ha2VyIG1heSBiZSBpbnRlcmVzdGVkIGluIGNvbXBhcmluZyB0aGUgcHJlZGljdGVkIGNoYW5nZSBpbiBjb25zdW1wdGlvbiB3aGVuIHRoZXJlIGlzIGEgY29uc3RhbnQgZ3Jvd3RoIG9mIDElIGFuZCAwLjUlIHJlc3BlY3RpdmVseSBmb3IgaW5jb21lIGFuZCBzYXZpbmdzIHdpdGggbm8gY2hhbmdlIGluIHRoZSBlbXBsb3ltZW50IHJhdGUsIHZlcnN1cyBhIHJlc3BlY3RpdmUgZGVjbGluZSBvZiAxJSBhbmQgMC41JSwgZm9yIGVhY2ggb2YgdGhlIGZvdXIgcXVhcnRlcnMgZm9sbG93aW5nIHRoZSBlbmQgb2YgdGhlIHNhbXBsZS4NCg0KYGBge3J9DQpmaXRfY29uc0Jlc3QgPC0gdXNfY2hhbmdlICU+JQ0KICBtb2RlbChsbSA9IFRTTE0oQ29uc3VtcHRpb24gfiBJbmNvbWUgKyBTYXZpbmdzICsgVW5lbXBsb3ltZW50KSkNCg0KZnV0dXJlX3NjZW5hcmlvcyA8LSBzY2VuYXJpb3MoDQogIEluY3JlYXNlID0gbmV3X2RhdGEodXNfY2hhbmdlLCA0KSAlPiUgDQogICAgbXV0YXRlKEluY29tZT0xLCBTYXZpbmdzPTAuNSwgVW5lbXBsb3ltZW50PTApLA0KICBEZWNyZWFzZSA9IG5ld19kYXRhKHVzX2NoYW5nZSwgNCkgJT4lIA0KICAgIG11dGF0ZShJbmNvbWU9LTEsIFNhdmluZ3M9LTAuNSwgVW5lbXBsb3ltZW50PTApLA0KICBuYW1lc190byA9ICJTY2VuYXJpbyIpDQoNCmZjIDwtIGZvcmVjYXN0KGZpdF9jb25zQmVzdCwgbmV3X2RhdGEgPSBmdXR1cmVfc2NlbmFyaW9zKQ0KdXNfY2hhbmdlICU+JQ0KICBhdXRvcGxvdChDb25zdW1wdGlvbikgKw0KICBhdXRvbGF5ZXIoZmMpICsNCiAgbGFicyh5ID0gIiUgY2hhbmdlIGluIFVTIGNvbnN1bXB0aW9uIikNCmBgYA0KDQojIyMgQnVpbGRpbmcgYSBwcmVkaWN0aXZlIHJlZ3Jlc3Npb24gbW9kZWwNCg0KVGhlIGdyZWF0IGFkdmFudGFnZSBvZiByZWdyZXNzaW9uIG1vZGVscyBpcyB0aGF0IHRoZXkgY2FuIGJlIHVzZWQgdG8gY2FwdHVyZSBpbXBvcnRhbnQgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIHRoZSBmb3JlY2FzdCB2YXJpYWJsZSBvZiBpbnRlcmVzdCBhbmQgdGhlIHByZWRpY3RvciB2YXJpYWJsZXMuIEEgbWFqb3IgY2hhbGxlbmdlIGhvd2V2ZXIsIGlzIHRoYXQgaW4gb3JkZXIgdG8gZ2VuZXJhdGUgZXgtYW50ZSBmb3JlY2FzdHMsIHRoZSBtb2RlbCByZXF1aXJlcyBmdXR1cmUgdmFsdWVzIG9mIGVhY2ggcHJlZGljdG9yLiBJZiBzY2VuYXJpbyBiYXNlZCBmb3JlY2FzdGluZyBpcyBvZiBpbnRlcmVzdCB0aGVuIHRoZXNlIG1vZGVscyBhcmUgZXh0cmVtZWx5IHVzZWZ1bC4gSG93ZXZlciwgaWYgZXgtYW50ZSBmb3JlY2FzdGluZyBpcyB0aGUgbWFpbiBmb2N1cywgb2J0YWluaW5nIGZvcmVjYXN0cyBvZiB0aGUgcHJlZGljdG9ycyBjYW4gYmUgY2hhbGxlbmdpbmcgKGluIG1hbnkgY2FzZXMgZ2VuZXJhdGluZyBmb3JlY2FzdHMgZm9yIHRoZSBwcmVkaWN0b3IgdmFyaWFibGVzIGNhbiBiZSBtb3JlIGNoYWxsZW5naW5nIHRoYW4gZm9yZWNhc3RpbmcgZGlyZWN0bHkgdGhlIGZvcmVjYXN0IHZhcmlhYmxlIHdpdGhvdXQgdXNpbmcgcHJlZGljdG9ycykuDQoNCkFuIGFsdGVybmF0aXZlIGZvcm11bGF0aW9uIGlzIHRvIHVzZSBhcyBwcmVkaWN0b3JzIHRoZWlyIGxhZ2dlZCB2YWx1ZXMuIEluY2x1ZGluZyBsYWdnZWQgdmFsdWVzIG9mIHRoZSBwcmVkaWN0b3JzIGRvZXMgbm90IG9ubHkgbWFrZSB0aGUgbW9kZWwgb3BlcmF0aW9uYWwgZm9yIGVhc2lseSBnZW5lcmF0aW5nIGZvcmVjYXN0cywgaXQgYWxzbyBtYWtlcyBpdCBpbnR1aXRpdmVseSBhcHBlYWxpbmcuDQoNCiMjIyBQcmVkaWN0aW9uIGludGVydmFscw0KDQpUaGUgZXN0aW1hdGVkIHNpbXBsZSByZWdyZXNzaW9uIGxpbmUgaW4gdGhlIFVTIGNvbnN1bXB0aW9uIGV4YW1wbGUgaXMNCg0KJFxoYXR7eX1fdD0wLjU0KzAuMjd4X3QkDQoNCmBgYHtyfQ0KI3BlcnNvbmFsIGluY29tZSBpbmNyZWFzZXMgYnkgaXRzIGhpc3RvcmljYWwgbWVhbg0KI2V4dHJlbWUgY2FzZSwgaW5jcmVhc2UgIDUlIGluIGluY29tZQ0KZml0X2NvbnMgPC0gdXNfY2hhbmdlICU+JQ0KICBtb2RlbChUU0xNKENvbnN1bXB0aW9uIH4gSW5jb21lKSkNCm5ld19jb25zIDwtIHNjZW5hcmlvcygNCiAgIkF2ZXJhZ2UgaW5jcmVhc2UiID0gbmV3X2RhdGEodXNfY2hhbmdlLCA0KSAlPiUgbXV0YXRlKEluY29tZSA9IG1lYW4odXNfY2hhbmdlJEluY29tZSkpLA0KICAiRXh0cmVtZSBpbmNyZWFzZSIgPSBuZXdfZGF0YSh1c19jaGFuZ2UsIDQpICU+JSBtdXRhdGUoSW5jb21lID0gMTIpLA0KICBuYW1lc190byA9ICJTY2VuYXJpbyINCikNCmZjYXN0IDwtIGZvcmVjYXN0KGZpdF9jb25zLCBuZXdfY29ucykNCg0KdXNfY2hhbmdlICU+JQ0KICBhdXRvcGxvdChDb25zdW1wdGlvbikgKw0KICBhdXRvbGF5ZXIoZmNhc3QpICsNCiAgbGFicyh5ID0gIiUgY2hhbmdlIGluIFVTIGNvbnN1bXB0aW9uIikNCmBgYA0KDQojIyBOb25saW5lYXIgcmVncmVzc2lvbg0KDQpUaGUgc2ltcGxlc3Qgd2F5IG9mIG1vZGVsbGluZyBhIG5vbmxpbmVhciByZWxhdGlvbnNoaXAgaXMgdG8gdHJhbnNmb3JtIHRoZSBmb3JlY2FzdCB2YXJpYWJsZSB5IGFuZC9vciB0aGUgcHJlZGljdG9yIHZhcmlhYmxlIHggYmVmb3JlIGVzdGltYXRpbmcgYSByZWdyZXNzaW9uIG1vZGVsLiBXaGlsZSB0aGlzIHByb3ZpZGVzIGEgbm9uLWxpbmVhciBmdW5jdGlvbmFsIGZvcm0sIHRoZSBtb2RlbCBpcyBzdGlsbCBsaW5lYXIgaW4gdGhlIHBhcmFtZXRlcnMuDQoNCiRcbG9nIHk9XGJldGFfMCtcYmV0YV8xIFxsb2cgeCArXHZhcmVwc2lsb24kDQoNCkluIHRoaXMgbW9kZWwsIHRoZSBzbG9wZSDOsjEgY2FuIGJlIGludGVycHJldGVkIGFzIGFuIGVsYXN0aWNpdHk6IM6yMSBpcyB0aGUgYXZlcmFnZSBwZXJjZW50YWdlIGNoYW5nZSBpbiB5IHJlc3VsdGluZyBmcm9tIGEgMSUgaW5jcmVhc2UgaW4geC4gT3RoZXIgdXNlZnVsIGZvcm1zIGNhbiBhbHNvIGJlIHNwZWNpZmllZC4gVGhlICoqbG9nLWxpbmVhcioqIGZvcm0gaXMgc3BlY2lmaWVkIGJ5IG9ubHkgdHJhbnNmb3JtaW5nIHRoZSBmb3JlY2FzdCB2YXJpYWJsZSBhbmQgdGhlICoqbGluZWFyLWxvZyoqIGZvcm0gaXMgb2J0YWluZWQgYnkgdHJhbnNmb3JtaW5nIHRoZSBwcmVkaWN0b3IuDQoNCiMjIyBGb3JlY2FzdGluZyB3aXRoIGEgbm9ubGluZWFyIHRyZW5kDQoNCkEgYmV0dGVyIGFwcHJvYWNoIGlzIHRvIHVzZSB0aGUgcGllY2V3aXNlIHNwZWNpZmljYXRpb24gYW5kIGZpdCBhIHBpZWNld2lzZSBsaW5lYXIgdHJlbmQgd2hpY2ggYmVuZHMgYXQgc29tZSBwb2ludCBpbiB0aW1lDQoNCiMjIyMgRXhhbXBsZTogQm9zdG9uIG1hcmF0aG9uIHdpbm5pbmcgdGltZXMNCg0KYGBge3J9DQpib3N0b25fbWVuIDwtIGJvc3Rvbl9tYXJhdGhvbiAlPiUNCiAgZmlsdGVyKFllYXIgPj0gMTkyNCkgJT4lDQogIGZpbHRlcihFdmVudCA9PSAiTWVuJ3Mgb3BlbiBkaXZpc2lvbiIpICU+JQ0KICBtdXRhdGUoTWludXRlcyA9IGFzLm51bWVyaWMoVGltZSkvNjApDQpgYGANCg0KVGhlIHBsb3Qgb2Ygd2lubmluZyB0aW1lcyByZXZlYWxzIHRocmVlIGRpZmZlcmVudCBwZXJpb2RzLiBUaGVyZSBpcyBhIGxvdCBvZiB2b2xhdGlsaXR5IGluIHRoZSB3aW5uaW5nIHRpbWVzIHVwIHRvIGFib3V0IDE5NTAsIHdpdGggdGhlIHdpbm5pbmcgdGltZXMgYmFyZWx5IGRlY2xpbmluZy4gQWZ0ZXIgMTk1MCB0aGVyZSBpcyBhIGNsZWFyIGRlY3JlYXNlIGluIHRpbWVzLCBmb2xsb3dlZCBieSBhIGZsYXR0ZW5pbmcgb3V0IGFmdGVyIHRoZSAxOTgwcywgd2l0aCB0aGUgc3VnZ2VzdGlvbiBvZiBhbiB1cHR1cm4gdG93YXJkcyB0aGUgZW5kIG9mIHRoZSBzYW1wbGUuIFRvIGFjY291bnQgZm9yIHRoZXNlIGNoYW5nZXMsIHdlIHNwZWNpZnkgdGhlIHllYXJzIDE5NTAgYW5kIDE5ODAgYXMga25vdHMuIFdlIHNob3VsZCB3YXJuIGhlcmUgdGhhdCBzdWJqZWN0aXZlIGlkZW50aWZpY2F0aW9uIG9mIGtub3RzIGNhbiBsZWFkIHRvIG92ZXItZml0dGluZywgd2hpY2ggY2FuIGJlIGRldHJpbWVudGFsIHRvIHRoZSBmb3JlY2FzdCBwZXJmb3JtYW5jZSBvZiBhIG1vZGVsLCBhbmQgc2hvdWxkIGJlIHBlcmZvcm1lZCB3aXRoIGNhdXRpb24uDQoNCmBgYHtyfQ0KZml0X3RyZW5kcyA8LSBib3N0b25fbWVuICU+JQ0KICBtb2RlbCgNCiAgICBsaW5lYXIgPSBUU0xNKE1pbnV0ZXMgfiB0cmVuZCgpKSwNCiAgICBleHBvbmVudGlhbCA9IFRTTE0obG9nKE1pbnV0ZXMpIH4gdHJlbmQoKSksDQogICAgcGllY2V3aXNlID0gVFNMTShNaW51dGVzIH4gdHJlbmQoa25vdHMgPSBjKDE5NTAsIDE5ODApKSkNCiAgKQ0KZmNfdHJlbmRzIDwtIGZpdF90cmVuZHMgJT4lIGZvcmVjYXN0KGggPSAxMCkNCg0KYm9zdG9uX21lbiAlPiUNCiAgYXV0b3Bsb3QoTWludXRlcykgKw0KICBnZW9tX2xpbmUoYWVzKHkgPSAuZml0dGVkLCBjb2xvdXIgPSAubW9kZWwpLCBkYXRhID0gZml0dGVkKGZpdF90cmVuZHMpKSArDQogIGF1dG9sYXllcihmY190cmVuZHMsIGFscGhhID0gMC41LCBsZXZlbCA9IDk1KSArDQogIGxhYnMoeSA9ICJXaW5uaW5nIHRpbWVzIGluIG1pbnV0ZXMiLA0KICAgICAgIHRpdGxlID0gIkJvc3RvbiBNYXJhdGhvbiIpDQpgYGANCg0KIyMgQ29ycmVsYXRpb24sIGNhdXNhdGlvbiBhbmQgZm9yZWNhc3RpbmcNCg0KQSB2YXJpYWJsZSB4IG1heSBiZSB1c2VmdWwgZm9yIGZvcmVjYXN0aW5nIGEgdmFyaWFibGUgeSwgYnV0IHRoYXQgZG9lcyBub3QgbWVhbiB4IGlzIGNhdXNpbmcgeS4gSXQgaXMgcG9zc2libGUgdGhhdCB4ICppcyogY2F1c2luZyB5LCBidXQgaXQgbWF5IGJlIHRoYXQgeSBpcyBjYXVzaW5nIHgsIG9yIHRoYXQgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZW0gaXMgbW9yZSBjb21wbGljYXRlZCB0aGFuIHNpbXBsZSBjYXVzYWxpdHkuDQoNCioqQ29uZm91bmRlcioqIHdoZW4gYSB2YXJpYWJsZSBub3QgaW5jbHVkZWQgaW4gdGhlIG1vZGVsIGluZmx1ZW5jZXMgYm90aCB0aGUgcmVzcG9uc2UgdmFyaWFibGUgYW5kIGF0IGxlYXN0IG9uZSBwcmVkaWN0b3IgdmFyaWFibGUuIENvbmZvdW5kaW5nIG1ha2VzIGl0IGRpZmZpY3VsdCB0byBkZXRlcm1pbmUgd2hhdCB2YXJpYWJsZXMgYXJlICpjYXVzaW5nKiBjaGFuZ2VzIGluIG90aGVyIHZhcmlhYmxlcywgYnV0IGl0IGRvZXMgbm90IG5lY2Vzc2FyaWx5IG1ha2UgZm9yZWNhc3RpbmcgbW9yZSBkaWZmaWN1bHQuDQoNCk9mdGVuIGEgYmV0dGVyIG1vZGVsIGlzIHBvc3NpYmxlIGlmIGEgY2F1c2FsIG1lY2hhbmlzbSBjYW4gYmUgZGV0ZXJtaW5lZC4NCg0KIyMjIEZvcmVjYXN0aW5nIHdpdGggY29ycmVsYXRlZCBwcmVkaWN0b3JzDQoNCldoZW4gdHdvIG9yIG1vcmUgcHJlZGljdG9ycyBhcmUgaGlnaGx5IGNvcnJlbGF0ZWQgaXQgaXMgYWx3YXlzIGNoYWxsZW5naW5nIHRvIGFjY3VyYXRlbHkgc2VwYXJhdGUgdGhlaXIgaW5kaXZpZHVhbCBlZmZlY3RzLg0KDQojIyMgTXVsdGljb2xsaW5lYXJpdHkgYW5kIGZvcmVjYXN0aW5nDQoNCkEgY2xvc2VseSByZWxhdGVkIGlzc3VlIGlzICoqbXVsdGljb2xsaW5lYXJpdHkqKiwgd2hpY2ggb2NjdXJzIHdoZW4gc2ltaWxhciBpbmZvcm1hdGlvbiBpcyBwcm92aWRlZCBieSB0d28gb3IgbW9yZSBvZiB0aGUgcHJlZGljdG9yIHZhcmlhYmxlcyBpbiBhIG11bHRpcGxlIHJlZ3Jlc3Npb24uDQoNCldoZW4gbXVsdGljb2xsaW5lYXJpdHkgaXMgcHJlc2VudCwgdGhlIHVuY2VydGFpbnR5IGFzc29jaWF0ZWQgd2l0aCBpbmRpdmlkdWFsIHJlZ3Jlc3Npb24gY29lZmZpY2llbnRzIHdpbGwgYmUgbGFyZ2UuIFRoaXMgaXMgYmVjYXVzZSB0aGV5IGFyZSBkaWZmaWN1bHQgdG8gZXN0aW1hdGUuIENvbnNlcXVlbnRseSwgc3RhdGlzdGljYWwgdGVzdHMgKGUuZy4sIHQtdGVzdHMpIG9uIHJlZ3Jlc3Npb24gY29lZmZpY2llbnRzIGFyZSB1bnJlbGlhYmxlLg0KDQpGb3JlY2FzdHMgd2lsbCBiZSB1bnJlbGlhYmxlIGlmIHRoZSB2YWx1ZXMgb2YgdGhlIGZ1dHVyZSBwcmVkaWN0b3JzIGFyZSBvdXRzaWRlIHRoZSByYW5nZSBvZiB0aGUgaGlzdG9yaWNhbCB2YWx1ZXMgb2YgdGhlIHByZWRpY3RvcnMuDQoNCiMgRXhwb25lbnRpYWwgc21vb3RoaW5nDQoNCkZvcmVjYXN0cyBwcm9kdWNlZCB1c2luZyBleHBvbmVudGlhbCBzbW9vdGhpbmcgbWV0aG9kcyBhcmUgd2VpZ2h0ZWQgYXZlcmFnZXMgb2YgcGFzdCBvYnNlcnZhdGlvbnMsIHdpdGggdGhlIHdlaWdodHMgZGVjYXlpbmcgZXhwb25lbnRpYWxseSBhcyB0aGUgb2JzZXJ2YXRpb25zIGdldCBvbGRlci4NCg0KIyMgU2ltcGxlIGV4cG9uZW50aWFsIHNtb290aGluZw0KDQpUaGlzIG1ldGhvZCBpcyBzdWl0YWJsZSBmb3IgZm9yZWNhc3RpbmcgZGF0YSB3aXRoIG5vIGNsZWFyIHRyZW5kIG9yIHNlYXNvbmFsIHBhdHRlcm4uDQoNCmBgYHtyfQ0KYWxnZXJpYV9lY29ub215IDwtIGdsb2JhbF9lY29ub215ICU+JQ0KICBmaWx0ZXIoQ291bnRyeSA9PSAiQWxnZXJpYSIpDQphbGdlcmlhX2Vjb25vbXkgJT4lDQogIGF1dG9wbG90KEV4cG9ydHMpICsNCiAgbGFicyh5PSJFeHBvcnRzICglIG9mIEdEUCkiKQ0KYGBgDQoNCkZvcmVjYXN0cyBhcmUgY2FsY3VsYXRlZCB1c2luZyB3ZWlnaHRlZCBhdmVyYWdlcywgd2hlcmUgdGhlIHdlaWdodHMgZGVjcmVhc2UgZXhwb25lbnRpYWxseSBhcyBvYnNlcnZhdGlvbnMgY29tZSBmcm9tIGZ1cnRoZXIgaW4gdGhlIHBhc3QgLS0tIHRoZSBzbWFsbGVzdCB3ZWlnaHRzIGFyZSBhc3NvY2lhdGVkIHdpdGggdGhlIG9sZGVzdCBvYnNlcnZhdGlvbnM6DQoNCmBgYHs9dGV4fQ0KXGJlZ2lue2VxdWF0aW9ufQ0KICBcaGF0e3l9X3tUKzF8VH0gPSBcYWxwaGEgeV9UICsgXGFscGhhKDEtXGFscGhhKSB5X3tULTF9ICsgXGFscGhhKDEtXGFscGhhKV4yIHlfe1QtMn0rIFxjZG90cywgICBcdGFnezguMX0NClxlbmR7ZXF1YXRpb259DQpgYGANCndoZXJlIFwkMOKJpM6x4omkMVwkIGlzIHRoZSBzbW9vdGhpbmcgcGFyYW1ldGVyLg0KDQojIyMgV2VpZ2h0ZWQgYXZlcmFnZSBmb3JtDQoNClRoZSBmb3JlY2FzdCBhdCB0aW1lIFwkVCsxXCQgaXMgZXF1YWwgdG8gYSB3ZWlnaHRlZCBhdmVyYWdlIGJldHdlZW4gdGhlIG1vc3QgcmVjZW50IG9ic2VydmF0aW9uIFwkeV9UXCQgYW5kIHRoZSBwcmV2aW91cyBmb3JlY2FzdCAkXGhhdHt5fV97VHxU4oiSMX0kOg0KDQokXGhhdHt5fV97VCsxfFR9ID0gXGFscGhhIHlfVCArICgxLVxhbHBoYSkgXGhhdHt5fV97VHxULTF9LCQNCg0KIyMjIENvbXBvbmVudCBmb3JtDQoNCkFuIGFsdGVybmF0aXZlIHJlcHJlc2VudGF0aW9uIGlzIHRoZSBjb21wb25lbnQgZm9ybS4gRm9yIHNpbXBsZSBleHBvbmVudGlhbCBzbW9vdGhpbmcsIHRoZSBvbmx5IGNvbXBvbmVudCBpbmNsdWRlZCBpcyB0aGUgbGV2ZWwsICTihJNfdCQuIENvbXBvbmVudCBmb3JtIHJlcHJlc2VudGF0aW9ucyBvZiBleHBvbmVudGlhbCBzbW9vdGhpbmcgbWV0aG9kcyBjb21wcmlzZSBhIGZvcmVjYXN0IGVxdWF0aW9uIGFuZCBhIHNtb290aGluZyBlcXVhdGlvbiBmb3IgZWFjaCBvZiB0aGUgY29tcG9uZW50cyBpbmNsdWRlZCBpbiB0aGUgbWV0aG9kLiBUaGUgY29tcG9uZW50IGZvcm0gb2Ygc2ltcGxlIGV4cG9uZW50aWFsIHNtb290aGluZyBpcyBnaXZlbiBieToNCg0KYGBgez10ZXh9DQpcYmVnaW57YWxpZ24qfQ0KICBcdGV4dHtGb3JlY2FzdCBlcXVhdGlvbn0gICYmIFxoYXR7eX1fe3QraHx0fSAmID0gXGVsbF97dH1cXA0KICBcdGV4dHtTbW9vdGhpbmcgZXF1YXRpb259ICYmIFxlbGxfe3R9ICAgICAgICAmID0gXGFscGhhIHlfe3R9ICsgKDEgLSBcYWxwaGEpXGVsbF97dC0xfSwNClxlbmR7YWxpZ24qfQ0KYGBgDQojIyMgRmxhdCBmb3JlY2FzdHMNCg0KU2ltcGxlIGV4cG9uZW50aWFsIHNtb290aGluZyBoYXMgYSAiZmxhdCIgZm9yZWNhc3QgZnVuY3Rpb246DQoNCiRcaGF0e3l9X3tUK2h8VH0gPSBcaGF0e3l9X3tUKzF8VH09XGVsbF9ULCBccXF1YWQgaD0yLDMsXGRvdHMuJA0KDQojIyMgT3B0aW1pc2F0aW9uDQoNClRoZSBhcHBsaWNhdGlvbiBvZiBldmVyeSBleHBvbmVudGlhbCBzbW9vdGhpbmcgbWV0aG9kIHJlcXVpcmVzIHRoZSBzbW9vdGhpbmcgcGFyYW1ldGVycyBhbmQgdGhlIGluaXRpYWwgdmFsdWVzIHRvIGJlIGNob3Nlbi4gSW4gcGFydGljdWxhciwgZm9yIHNpbXBsZSBleHBvbmVudGlhbCBzbW9vdGhpbmcsIHdlIG5lZWQgdG8gc2VsZWN0IHRoZSB2YWx1ZXMgb2YgJM6xJCBhbmQgJOKEk18wJC4gQWxsIGZvcmVjYXN0cyBjYW4gYmUgY29tcHV0ZWQgZnJvbSB0aGUgZGF0YSBvbmNlIHdlIGtub3cgdGhvc2UgdmFsdWVzLiBGb3IgdGhlIG1ldGhvZHMgdGhhdCBmb2xsb3cgdGhlcmUgaXMgdXN1YWxseSBtb3JlIHRoYW4gb25lIHNtb290aGluZyBwYXJhbWV0ZXIgYW5kIG1vcmUgdGhhbiBvbmUgaW5pdGlhbCBjb21wb25lbnQgdG8gYmUgY2hvc2VuLg0KDQpBIG1vcmUgcmVsaWFibGUgYW5kIG9iamVjdGl2ZSB3YXkgdG8gb2J0YWluIHZhbHVlcyBmb3IgdGhlIHVua25vd24gcGFyYW1ldGVycyBpcyB0byBlc3RpbWF0ZSB0aGVtIGZyb20gdGhlIG9ic2VydmVkIGRhdGEuIFRoZSB1bmtub3duIHBhcmFtZXRlcnMgYW5kIHRoZSBpbml0aWFsIHZhbHVlcyBmb3IgYW55IGV4cG9uZW50aWFsIHNtb290aGluZyBtZXRob2QgY2FuIGJlIGVzdGltYXRlZCBieSBtaW5pbWlzaW5nIHRoZSBTU0UuDQoNCiMjIyBFeGFtcGxlOiBBbGdlcmlhbiBleHBvcnRzDQoNCmBgYHtyfQ0KIyBFc3RpbWF0ZSBwYXJhbWV0ZXJzDQpmaXQgPC0gYWxnZXJpYV9lY29ub215ICU+JQ0KICBtb2RlbChFVFMoRXhwb3J0cyB+IGVycm9yKCJBIikgKyB0cmVuZCgiTiIpICsgc2Vhc29uKCJOIikpKQ0KZmMgPC0gZml0ICU+JQ0KICBmb3JlY2FzdChoID0gNSkNCmBgYA0KDQpgYGB7cn0NCnJlcG9ydChmaXQpDQpgYGANCg0KVGhpcyBnaXZlcyBwYXJhbWV0ZXIgZXN0aW1hdGVzICQvaGF0e159zrE9MC44NCQgYW5kICQvaGF0XuKEk18wPTM5LjUkDQoNCmBgYHtyfQ0KYXV0b3Bsb3QoZmMpICsNCiAgZ2VvbV9saW5lKGFlcyh5ID0gRXhwb3J0cywgY29sPSJEYXRhIiksIGRhdGEgPSBhbGdlcmlhX2Vjb25vbXkpICsNCiAgZ2VvbV9saW5lKGFlcyh5ID0gLmZpdHRlZCwgY29sPSJGaXR0ZWQiKSwgZGF0YSA9IGF1Z21lbnQoZml0KSkgKw0KICBsYWJzKHk9IkV4cG9ydHMgKCUgb2YgR0RQKSIpICsNCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKERhdGE9ImJsYWNrIixGaXR0ZWQ9InJlZCIpKSArDQogIGd1aWRlcyhjb2xvdXIgPSBndWlkZV9sZWdlbmQoIlNlcmllcyIpKQ0KYGBgDQoNClRoZSBwcmVkaWN0aW9uIGludGVydmFscyBzaG93biBoZXJlIGFyZSBjYWxjdWxhdGVkIHVzaW5nIHRoZSBtZXRob2RzIGRlc2NyaWJlZCBpbiBTZWN0aW9uIDguNy4gVGhlIHByZWRpY3Rpb24gaW50ZXJ2YWxzIHNob3cgdGhhdCB0aGVyZSBpcyBjb25zaWRlcmFibGUgdW5jZXJ0YWludHkgaW4gdGhlIGZ1dHVyZSBleHBvcnRzIG92ZXIgdGhlIGZpdmUteWVhciBmb3JlY2FzdCBwZXJpb2QuIFNvIGludGVycHJldGluZyB0aGUgcG9pbnQgZm9yZWNhc3RzIHdpdGhvdXQgYWNjb3VudGluZyBmb3IgdGhlIGxhcmdlIHVuY2VydGFpbnR5IGNhbiBiZSB2ZXJ5IG1pc2xlYWRpbmcuDQoNCiMjIE1ldGhvZHMgd2l0aCB0cmVuZA0KDQojIyMgSG9sdCdzIGxpbmVhciB0cmVuZCBtZXRob2QNCg0KVGhpcyBtZXRob2QgaW52b2x2ZXMgYSBmb3JlY2FzdCBlcXVhdGlvbiBhbmQgdHdvIHNtb290aGluZyBlcXVhdGlvbnMgKG9uZSBmb3IgdGhlIGxldmVsIGFuZCBvbmUgZm9yIHRoZSB0cmVuZCk6DQoNCmBgYHs9dGV4fQ0KXGJlZ2lue2FsaWduKn0NCiAgXHRleHR7Rm9yZWNhc3QgZXF1YXRpb259JiYgXGhhdHt5fV97dCtofHR9ICY9IFxlbGxfe3R9ICsgaGJfe3R9IFxcDQogIFx0ZXh0e0xldmVsIGVxdWF0aW9ufSAgICYmIFxlbGxfe3R9ICY9IFxhbHBoYSB5X3t0fSArICgxIC0gXGFscGhhKShcZWxsX3t0LTF9ICsgYl97dC0xfSlcXA0KICBcdGV4dHtUcmVuZCBlcXVhdGlvbn0gICAmJiBiX3t0fSAgICAmPSBcYmV0YV4qKFxlbGxfe3R9IC0gXGVsbF97dC0xfSkgKyAoMSAtXGJldGFeKiliX3t0LTF9LA0KXGVuZHthbGlnbip9DQpgYGANCiMjIyBFeGFtcGxlOiBBdXN0cmFsaWFuIHBvcHVsYXRpb24NCg0KYGBge3J9DQphdXNfZWNvbm9teSA8LSBnbG9iYWxfZWNvbm9teSAlPiUNCiAgZmlsdGVyKENvZGUgPT0gIkFVUyIpICU+JQ0KICBtdXRhdGUoUG9wID0gUG9wdWxhdGlvbiAvIDFlNikNCmF1dG9wbG90KGF1c19lY29ub215LCBQb3ApICsgbGFicyh5ID0gIk1pbGxpb25zIikNCmBgYA0KDQpgYGB7cn0NCmZpdCA8LSBhdXNfZWNvbm9teSAlPiUNCiAgbW9kZWwoQUFOID0gRVRTKFBvcCB+IGVycm9yKCJBIikgKyB0cmVuZCgiQSIpICsgc2Vhc29uKCJOIikpKQ0KZmMgPC0gZml0ICU+JSBmb3JlY2FzdChoID0gMTApDQpyZXBvcnQoZml0KQ0KYGBgDQoNClRoZSBlc3RpbWF0ZWQgc21vb3RoaW5nIGNvZWZmaWNpZW50IGZvciB0aGUgbGV2ZWwgaXMgJFxoYXR7Xn3OsT0xLjAwJC4gVGhlIHZlcnkgaGlnaCB2YWx1ZSBzaG93cyB0aGF0IHRoZSBsZXZlbCBjaGFuZ2VzIHJhcGlkbHkgaW4gb3JkZXIgdG8gY2FwdHVyZSB0aGUgaGlnaGx5IHRyZW5kZWQgc2VyaWVzLiBUaGUgZXN0aW1hdGVkIHNtb290aGluZyBjb2VmZmljaWVudCBmb3IgdGhlIHNsb3BlIGlzICRcaGF0e159zrJe4oiXPTAuMzMkLiBUaGlzIGlzIHJlbGF0aXZlbHkgbGFyZ2Ugc3VnZ2VzdGluZyB0aGF0IHRoZSB0cmVuZCBhbHNvIGNoYW5nZXMgb2Z0ZW4gKGV2ZW4gaWYgdGhlIGNoYW5nZXMgYXJlIHNsaWdodCkuDQoNCiMjIyBEYW1wZWQgdHJlbmQgbWV0aG9kcw0KDQoiZGFtcGVucyIgdGhlIHRyZW5kIHRvIGEgZmxhdCBsaW5lIHNvbWUgdGltZSBpbiB0aGUgZnV0dXJlLiBNZXRob2RzIHRoYXQgaW5jbHVkZSBhIGRhbXBlZCB0cmVuZCBoYXZlIHByb3ZlbiB0byBiZSB2ZXJ5IHN1Y2Nlc3NmdWwsIGFuZCBhcmUgYXJndWFibHkgdGhlIG1vc3QgcG9wdWxhciBpbmRpdmlkdWFsIG1ldGhvZHMgd2hlbiBmb3JlY2FzdHMgYXJlIHJlcXVpcmVkIGF1dG9tYXRpY2FsbHkgZm9yIG1hbnkgc2VyaWVzLg0KDQp0aGlzIG1ldGhvZCBhbHNvIGluY2x1ZGVzIGEgZGFtcGluZyBwYXJhbWV0ZXIgJDA8z5U8MSQ6DQoNCmBgYHs9dGV4fQ0KXGJlZ2lue2FsaWduKn0NCiAgXGhhdHt5fV97dCtofHR9ICY9IFxlbGxfe3R9ICsgKFxwaGkrXHBoaV4yICsgXGRvdHMgKyBccGhpXntofSliX3t0fSBcXA0KICBcZWxsX3t0fSAmPSBcYWxwaGEgeV97dH0gKyAoMSAtIFxhbHBoYSkoXGVsbF97dC0xfSArIFxwaGkgYl97dC0xfSlcXA0KICBiX3t0fSAmPSBcYmV0YV4qKFxlbGxfe3R9IC0gXGVsbF97dC0xfSkgKyAoMSAtXGJldGFeKilccGhpIGJfe3QtMX0uDQpcZW5ke2FsaWduKn0NCmBgYA0KSW4gcHJhY3RpY2UsICTPlSQgaXMgcmFyZWx5IGxlc3MgdGhhbiAwLjggYXMgdGhlIGRhbXBpbmcgaGFzIGEgdmVyeSBzdHJvbmcgZWZmZWN0IGZvciBzbWFsbGVyIHZhbHVlcy4gVmFsdWVzIG9mICTPlSQgY2xvc2UgdG8gMSB3aWxsIG1lYW4gdGhhdCBhIGRhbXBlZCBtb2RlbCBpcyBub3QgYWJsZSB0byBiZSBkaXN0aW5ndWlzaGVkIGZyb20gYSBub24tZGFtcGVkIG1vZGVsLiBGb3IgdGhlc2UgcmVhc29ucywgd2UgdXN1YWxseSByZXN0cmljdCAkz5UkIHRvIGEgbWluaW11bSBvZiAwLjggYW5kIGEgbWF4aW11bSBvZiAwLjk4Lg0KDQojIyMgRXhhbXBsZTogQXVzdHJhbGlhbiBQb3B1bGF0aW9uIChjb250aW51ZWQpDQoNCmBgYHtyfQ0KYXVzX2Vjb25vbXkgJT4lDQogIG1vZGVsKA0KICAgIGBIb2x0J3MgbWV0aG9kYCA9IEVUUyhQb3AgfiBlcnJvcigiQSIpICsgdHJlbmQoIkEiKSArIHNlYXNvbigiTiIpKSwNCiAgICBgRGFtcGVkIEhvbHQncyBtZXRob2RgID0gRVRTKFBvcCB+IGVycm9yKCJBIikgKyB0cmVuZCgiQWQiLCBwaGkgPSAwLjkpICsgc2Vhc29uKCJOIikpDQogICkgJT4lDQogIGZvcmVjYXN0KGggPSAxNSkgJT4lDQogIGF1dG9wbG90KGF1c19lY29ub215LCBsZXZlbCA9IE5VTEwpICsNCiAgbGFicyh0aXRsZSA9ICJGb3JlY2FzdHMgZnJvbSBIb2x0J3MgbWV0aG9kIiwNCiAgICAgICB5ID0gIlBvcHVsYXRpb24gb2YgQXVzdHJhbGlhIChtaWxsaW9ucykiKSArDQogIGd1aWRlcyhjb2xvdXIgPSBndWlkZV9sZWdlbmQodGl0bGUgPSAiRm9yZWNhc3QiKSkNCmBgYA0KDQpXZSBoYXZlIHNldCB0aGUgZGFtcGluZyBwYXJhbWV0ZXIgdG8gYSByZWxhdGl2ZWx5IGxvdyBudW1iZXIgKCTPlT0wLjkwJCkgdG8gZXhhZ2dlcmF0ZSB0aGUgZWZmZWN0IG9mIGRhbXBpbmcgZm9yIGNvbXBhcmlzb24uIFVzdWFsbHksIHdlIHdvdWxkIGVzdGltYXRlICTPlSQgYWxvbmcgd2l0aCB0aGUgb3RoZXIgcGFyYW1ldGVycy4gV2UgaGF2ZSBhbHNvIHVzZWQgYSByYXRoZXIgbGFyZ2UgZm9yZWNhc3QgaG9yaXpvbiAoJGg9MTUkKSB0byBoaWdobGlnaHQgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiBhIGRhbXBlZCB0cmVuZCBhbmQgYSBsaW5lYXIgdHJlbmQuDQoNCiMjIyBFeGFtcGxlOiBJbnRlcm5ldCB1c2FnZQ0KDQp3ZSBjb21wYXJlIHRoZSBmb3JlY2FzdGluZyBwZXJmb3JtYW5jZSBvZiB0aGUgdGhyZWUgZXhwb25lbnRpYWwgc21vb3RoaW5nIG1ldGhvZHMgdGhhdCB3ZSBoYXZlIGNvbnNpZGVyZWQgc28gZmFyIGluIGZvcmVjYXN0aW5nIHRoZSBudW1iZXIgb2YgdXNlcnMgY29ubmVjdGVkIHRvIHRoZSBpbnRlcm5ldCB2aWEgYSBzZXJ2ZXIuIFRoZSBkYXRhIGlzIG9ic2VydmVkIG92ZXIgMTAwIG1pbnV0ZXM6DQoNCmBgYHtyfQ0Kd3d3X3VzYWdlIDwtIGFzX3RzaWJibGUoV1dXdXNhZ2UpDQp3d3dfdXNhZ2UgJT4lIGF1dG9wbG90KHZhbHVlKSArDQogIGxhYnMoeD0iTWludXRlIiwgeT0iTnVtYmVyIG9mIHVzZXJzIikNCmBgYA0KDQpXZSB3aWxsIHVzZSB0aW1lIHNlcmllcyBjcm9zcy12YWxpZGF0aW9uIHRvIGNvbXBhcmUgdGhlIG9uZS1zdGVwIGZvcmVjYXN0IGFjY3VyYWN5IG9mIHRoZSB0aHJlZSBtZXRob2RzLg0KDQpgYGB7cn0NCnd3d191c2FnZSAlPiUNCiAgc3RyZXRjaF90c2liYmxlKC5pbml0ID0gMTApICU+JQ0KICBtb2RlbCgNCiAgICBTRVMgPSBFVFModmFsdWUgfiBlcnJvcigiQSIpICsgdHJlbmQoIk4iKSArIHNlYXNvbigiTiIpKSwNCiAgICBIb2x0ID0gRVRTKHZhbHVlIH4gZXJyb3IoIkEiKSArIHRyZW5kKCJBIikgKyBzZWFzb24oIk4iKSksDQogICAgRGFtcGVkID0gRVRTKHZhbHVlIH4gZXJyb3IoIkEiKSArIHRyZW5kKCJBZCIpICsgc2Vhc29uKCJOIikpDQogICkgJT4lDQogIGZvcmVjYXN0KGggPSAxKSAlPiUNCiAgYWNjdXJhY3kod3d3X3VzYWdlKQ0KYGBgDQoNCkRhbXBlZCBIb2x0J3MgbWV0aG9kIGlzIGJlc3Qgd2hldGhlciB5b3UgY29tcGFyZSBNQUUgb3IgUk1TRSB2YWx1ZXMuIFNvIHdlIHdpbGwgcHJvY2VlZCB3aXRoIHVzaW5nIHRoZSBkYW1wZWQgSG9sdCdzIG1ldGhvZCBhbmQgYXBwbHkgaXQgdG8gdGhlIHdob2xlIGRhdGEgc2V0IHRvIGdldCBmb3JlY2FzdHMgZm9yIGZ1dHVyZSB5ZWFycy4NCg0KYGBge3J9DQpmaXQgPC0gd3d3X3VzYWdlICU+JQ0KICBtb2RlbChEYW1wZWQgPSBFVFModmFsdWUgfiBlcnJvcigiQSIpICsgdHJlbmQoIkFkIikgKyBzZWFzb24oIk4iKSkpDQpgYGANCg0KYGBge3J9DQp0aWR5KGZpdCkNCmBgYA0KDQpUaGUgc21vb3RoaW5nIHBhcmFtZXRlciBmb3IgdGhlIHNsb3BlIGlzIGVzdGltYXRlZCB0byBiZSBhbG1vc3Qgb25lLCBpbmRpY2F0aW5nIHRoYXQgdGhlIHRyZW5kIGNoYW5nZXMgdG8gbW9zdGx5IHJlZmxlY3QgdGhlIHNsb3BlIGJldHdlZW4gdGhlIGxhc3QgdHdvIG1pbnV0ZXMgb2YgaW50ZXJuZXQgdXNhZ2UuIFRoZSB2YWx1ZSBvZiAkzrEkIGlzIHZlcnkgY2xvc2UgdG8gb25lLCBzaG93aW5nIHRoYXQgdGhlIGxldmVsIHJlYWN0cyBzdHJvbmdseSB0byBlYWNoIG5ldyBvYnNlcnZhdGlvbi4NCg0KYGBge3J9DQpmaXQgJT4lDQogIGZvcmVjYXN0KGggPSAxMCkgJT4lDQogIGF1dG9wbG90KHd3d191c2FnZSkgKw0KICBsYWJzKHg9Ik1pbnV0ZSIsIHk9Ik51bWJlciBvZiB1c2VycyIpDQpgYGANCg0KVGhlIHJlc3VsdGluZyBmb3JlY2FzdHMgbG9vayBzZW5zaWJsZSB3aXRoIGRlY3JlYXNpbmcgdHJlbmQsIGFuZCByZWxhdGl2ZWx5IHdpZGUgcHJlZGljdGlvbiBpbnRlcnZhbHMgcmVmbGVjdGluZyB0aGUgdmFyaWF0aW9uIGluIHRoZSBoaXN0b3JpY2FsIGRhdGEuDQoNCiMjIE1ldGhvZHMgd2l0aCBzZWFzb25hbGl0eQ0KDQpUaGUgSG9sdC1XaW50ZXJzIHNlYXNvbmFsIG1ldGhvZCBjb21wcmlzZXMgdGhlIGZvcmVjYXN0IGVxdWF0aW9uIGFuZCB0aHJlZSBzbW9vdGhpbmcgZXF1YXRpb25zIC0tLSBvbmUgZm9yIHRoZSBsZXZlbCAk4oSTX3QkLCBvbmUgZm9yIHRoZSB0cmVuZCAkYl90JCwgYW5kIG9uZSBmb3IgdGhlIHNlYXNvbmFsIGNvbXBvbmVudCAkc190JCwgd2l0aCBjb3JyZXNwb25kaW5nIHNtb290aGluZyBwYXJhbWV0ZXJzICTOsSQsICTOsl7iiJckIGFuZCAkzrMkLiBXZSB1c2UgJG0kIHRvIGRlbm90ZSB0aGUgZnJlcXVlbmN5IG9mIHRoZSBzZWFzb25hbGl0eSwgaS5lLiwgdGhlIG51bWJlciBvZiBzZWFzb25zIGluIGEgeWVhci4gRm9yIGV4YW1wbGUsIGZvciBxdWFydGVybHkgZGF0YSAkbT00JCwgYW5kIGZvciBtb250aGx5IGRhdGEgJG09MTIkLg0KDQpUaGUgYWRkaXRpdmUgbWV0aG9kIGlzIHByZWZlcnJlZCB3aGVuIHRoZSBzZWFzb25hbCB2YXJpYXRpb25zIGFyZSByb3VnaGx5IGNvbnN0YW50IHRocm91Z2ggdGhlIHNlcmllcywgd2hpbGUgdGhlIG11bHRpcGxpY2F0aXZlIG1ldGhvZCBpcyBwcmVmZXJyZWQgd2hlbiB0aGUgc2Vhc29uYWwgdmFyaWF0aW9ucyBhcmUgY2hhbmdpbmcgcHJvcG9ydGlvbmFsIHRvIHRoZSBsZXZlbCBvZiB0aGUgc2VyaWVzLg0KDQojIyMgSG9sdC1XaW50ZXJzJyBhZGRpdGl2ZSBtZXRob2QNCg0KYGBgez10ZXh9DQpcYmVnaW57YWxpZ24qfQ0KICBcaGF0e3l9X3t0K2h8dH0gJj0gXGVsbF97dH0gKyBoYl97dH0gKyBzX3t0K2gtbShrKzEpfSBcXA0KICBcZWxsX3t0fSAmPSBcYWxwaGEoeV97dH0gLSBzX3t0LW19KSArICgxIC0gXGFscGhhKShcZWxsX3t0LTF9ICsgYl97dC0xfSlcXA0KICBiX3t0fSAmPSBcYmV0YV4qKFxlbGxfe3R9IC0gXGVsbF97dC0xfSkgKyAoMSAtIFxiZXRhXiopYl97dC0xfVxcDQogIHNfe3R9ICY9IFxnYW1tYSAoeV97dH0tXGVsbF97dC0xfS1iX3t0LTF9KSArICgxLVxnYW1tYSlzX3t0LW19LA0KXGVuZHthbGlnbip9DQpgYGANCiMjIyBIb2x0LVdpbnRlcnMnIG11bHRpcGxpY2F0aXZlIG1ldGhvZA0KDQpgYGB7PXRleH0NClxiZWdpbnthbGlnbip9DQogIFxoYXR7eX1fe3QraHx0fSAmPSAoXGVsbF97dH0gKyBoYl97dH0pc197dCtoLW0oaysxKX0gXFwNCiAgXGVsbF97dH0gJj0gXGFscGhhIFxmcmFje3lfe3R9fXtzX3t0LW19fSArICgxIC0gXGFscGhhKShcZWxsX3t0LTF9ICsgYl97dC0xfSlcXA0KICBiX3t0fSAmPSBcYmV0YV4qKFxlbGxfe3R9LVxlbGxfe3QtMX0pICsgKDEgLSBcYmV0YV4qKWJfe3QtMX0gICAgICAgICAgICAgICAgXFwNCiAgc197dH0gJj0gXGdhbW1hIFxmcmFje3lfe3R9fXsoXGVsbF97dC0xfSArIGJfe3QtMX0pfSArICgxIC0gXGdhbW1hKXNfe3QtbX0NClxlbmR7YWxpZ24qfQ0KYGBgDQojIyMgRXhhbXBsZTogRG9tZXN0aWMgb3Zlcm5pZ2h0IHRyaXBzIGluIEF1c3RyYWxpYQ0KDQpXZSBhcHBseSBIb2x0LVdpbnRlcnMnIG1ldGhvZCB3aXRoIGJvdGggYWRkaXRpdmUgYW5kIG11bHRpcGxpY2F0aXZlIHNlYXNvbmFsaXR5IHRvIGZvcmVjYXN0IHF1YXJ0ZXJseSB2aXNpdG9yIG5pZ2h0cyBpbiBBdXN0cmFsaWEgc3BlbnQgYnkgZG9tZXN0aWMgdG91cmlzdHMuDQoNCmBgYHtyfQ0KYXVzX2hvbGlkYXlzIDwtIHRvdXJpc20gJT4lDQogIGZpbHRlcihQdXJwb3NlID09ICJIb2xpZGF5IikgJT4lDQogIHN1bW1hcmlzZShUcmlwcyA9IHN1bShUcmlwcykvMWUzKQ0KZml0IDwtIGF1c19ob2xpZGF5cyAlPiUNCiAgbW9kZWwoDQogICAgYWRkaXRpdmUgPSBFVFMoVHJpcHMgfiBlcnJvcigiQSIpICsgdHJlbmQoIkEiKSArIHNlYXNvbigiQSIpKSwNCiAgICBtdWx0aXBsaWNhdGl2ZSA9IEVUUyhUcmlwcyB+IGVycm9yKCJNIikgKyB0cmVuZCgiQSIpICsgc2Vhc29uKCJNIikpDQogICkNCmZjIDwtIGZpdCAlPiUgZm9yZWNhc3QoaCA9ICIzIHllYXJzIikNCg0KZmMgJT4lDQogIGF1dG9wbG90KGF1c19ob2xpZGF5cywgbGV2ZWwgPSBOVUxMKSArDQogIGxhYnMoeT0iT3Zlcm5pZ2h0IHRyaXBzIChtaWxsaW9ucykiKQ0KYGBgDQoNCmBgYHtyfQ0KdGlkeShmaXQpDQpgYGANCg0KZWNhdXNlIGJvdGggbWV0aG9kcyBoYXZlIGV4YWN0bHkgdGhlIHNhbWUgbnVtYmVyIG9mIHBhcmFtZXRlcnMgdG8gZXN0aW1hdGUsIHdlIGNhbiBjb21wYXJlIHRoZSB0cmFpbmluZyBSTVNFIGZyb20gYm90aCBtb2RlbHMuIEluIHRoaXMgY2FzZSwgdGhlIG1ldGhvZCB3aXRoIG11bHRpcGxpY2F0aXZlIHNlYXNvbmFsaXR5IGZpdHMgdGhlIGRhdGEgc2xpZ2h0bHkgYmV0dGVyLg0KDQojIyMgSG9sdC1XaW50ZXJzJyBkYW1wZWQgbWV0aG9kDQoNCkRhbXBpbmcgaXMgcG9zc2libGUgd2l0aCBib3RoIGFkZGl0aXZlIGFuZCBtdWx0aXBsaWNhdGl2ZSBIb2x0LVdpbnRlcnMnIG1ldGhvZHMuIEEgbWV0aG9kIHRoYXQgb2Z0ZW4gcHJvdmlkZXMgYWNjdXJhdGUgYW5kIHJvYnVzdCBmb3JlY2FzdHMgZm9yIHNlYXNvbmFsIGRhdGEgaXMgdGhlIEhvbHQtV2ludGVycyBtZXRob2Qgd2l0aCBhIGRhbXBlZCB0cmVuZCBhbmQgbXVsdGlwbGljYXRpdmUgc2Vhc29uYWxpdHk6DQoNCmBgYHs9dGV4fQ0KXGJlZ2lue2FsaWduKn0NCiAgXGhhdHt5fV97dCtofHR9ICY9IFxsZWZ0W1xlbGxfe3R9ICsgKFxwaGkrXHBoaV4yICsgXGRvdHMgKyBccGhpXntofSliX3t0fVxyaWdodF1zX3t0K2gtbShrKzEpfS4gXFwNCiAgXGVsbF97dH0gJj0gXGFscGhhKHlfe3R9IC8gc197dC1tfSkgKyAoMSAtIFxhbHBoYSkoXGVsbF97dC0xfSArIFxwaGkgYl97dC0xfSlcXA0KICBiX3t0fSAmPSBcYmV0YV4qKFxlbGxfe3R9IC0gXGVsbF97dC0xfSkgKyAoMSAtIFxiZXRhXiopXHBoaSBiX3t0LTF9ICAgICAgICAgICAgIFxcDQogIHNfe3R9ICY9IFxnYW1tYSBcZnJhY3t5X3t0fX17KFxlbGxfe3QtMX0gKyBccGhpIGJfe3QtMX0pfSArICgxIC0gXGdhbW1hKXNfe3QtbX0uDQpcZW5ke2FsaWduKn0NCmBgYA0KICAgIEVUUyh5IH4gZXJyb3IoIk0iKSArIHRyZW5kKCJBZCIpICsgc2Vhc29uKCJNIikpDQoNCiMjIyBFeGFtcGxlOiBIb2x0LVdpbnRlcnMgbWV0aG9kIHdpdGggZGFpbHkgZGF0YQ0KDQpUaGUgSG9sdC1XaW50ZXJzIG1ldGhvZCBjYW4gYWxzbyBiZSB1c2VkIGZvciBkYWlseSB0eXBlIG9mIGRhdGEsIHdoZXJlIHRoZSBzZWFzb25hbCBwZXJpb2QgaXMgJG09NyQsIGFuZCB0aGUgYXBwcm9wcmlhdGUgdW5pdCBvZiB0aW1lIGZvciAkaCQgaXMgaW4gZGF5cy4gSGVyZSB3ZSBmb3JlY2FzdCBwZWRlc3RyaWFuIHRyYWZmaWMgYXQgYSBidXN5IE1lbGJvdXJuZSB0cmFpbiBzdGF0aW9uIGluIEp1bHkgMjAxNi4NCg0KYGBge3J9DQpzdGhfY3Jvc3NfcGVkIDwtIHBlZGVzdHJpYW4gJT4lDQogIGZpbHRlcihEYXRlID49ICIyMDE2LTA3LTAxIiwgU2Vuc29yID09ICJTb3V0aGVybiBDcm9zcyBTdGF0aW9uIikgJT4lDQogIGluZGV4X2J5KERhdGUpICU+JQ0KICBzdW1tYXJpc2UoQ291bnQgPSBzdW0oQ291bnQpKQ0Kc3RoX2Nyb3NzX3BlZCAlPiUNCiAgZmlsdGVyKERhdGUgPD0gIjIwMTYtMDctMzEiKSAlPiUNCiAgbW9kZWwoaHcgPSBFVFMoQ291bnQgfiBlcnJvcigiTSIpICsgdHJlbmQoIkFkIikgKyBzZWFzb24oIk0iKSkpICU+JQ0KICBmb3JlY2FzdChoID0gIjIgd2Vla3MiKSAlPiUNCiAgYXV0b3Bsb3Qoc3RoX2Nyb3NzX3BlZCAlPiUgZmlsdGVyKERhdGUgPD0gIjIwMTYtMDgtMTQiKSkNCmBgYA0KDQpDbGVhcmx5IHRoZSBtb2RlbCBoYXMgaWRlbnRpZmllZCB0aGUgd2Vla2x5IHNlYXNvbmFsIHBhdHRlcm4gYW5kIHRoZSBpbmNyZWFzaW5nIHRyZW5kIGF0IHRoZSBlbmQgb2YgdGhlIGRhdGEsIGFuZCB0aGUgZm9yZWNhc3RzIGFyZSBhIGNsb3NlIG1hdGNoIHRvIHRoZSB0ZXN0IGRhdGEuDQoNCiMjIEEgdGF4b25vbXkgb2YgZXhwb25lbnRpYWwgc21vb3RoaW5nIG1ldGhvZHMNCg0KQnkgY29uc2lkZXJpbmcgdmFyaWF0aW9ucyBpbiB0aGUgY29tYmluYXRpb25zIG9mIHRoZSB0cmVuZCBhbmQgc2Vhc29uYWwgY29tcG9uZW50cywgbmluZSBleHBvbmVudGlhbCBzbW9vdGhpbmcgbWV0aG9kcyBhcmUgcG9zc2libGUuIEVhY2ggbWV0aG9kIGlzIGxhYmVsbGVkIGJ5IGEgcGFpciBvZiBsZXR0ZXJzIChULFMpIGRlZmluaW5nIHRoZSB0eXBlIG9mICdUcmVuZCcgYW5kICdTZWFzb25hbCcgY29tcG9uZW50cy4gRm9yIGV4YW1wbGUsIChBLE0pIGlzIHRoZSBtZXRob2Qgd2l0aCBhbiBhZGRpdGl2ZSB0cmVuZCBhbmQgbXVsdGlwbGljYXRpdmUgc2Vhc29uYWxpdHk7ICgkQV9kJCwgTikgaXMgdGhlIG1ldGhvZCB3aXRoIGRhbXBlZCB0cmVuZCBhbmQgbm8gc2Vhc29uYWxpdHk7IGFuZCBzbyBvbi4NCg0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8IFRyZW5kIENvbXBvbmVudCAgICAgICAgICAgfCBTZWFzb25hbCBDb21wb25lbnQgfCAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgIHwNCis9PT09PT09PT09PT09PT09PT09PT09PT09PT0rPT09PT09PT09PT09PT09PT09PT0rPT09PT09PT09PT09PT09PT0rPT09PT09PT09PT09PT09PT09PT09PT09Kw0KfCAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgKipOIChOb25lKSoqICAgICAgIHwgKipBKEFkZGl0aXZlKSoqIHwgKipNIChNdWx0aXBsaWNhdGl2ZSkqKiB8DQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgKipOIChOb25lKSoqICAgICAgICAgICAgICB8IChOLE4pICAgICAgICAgICAgICB8IChOLEEpICAgICAgICAgICB8IChOLE0pICAgICAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8ICoqQSAoQWRkaXRpdmUpKiogICAgICAgICAgfCAoQSxOKSAgICAgICAgICAgICAgfCAoQSxBKSAgICAgICAgICAgfCAoQSxNKSAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCAqKkFfZCAoQWRkaXRpdmUgZGFtcGVkKSoqIHwgKCRBX2QkLE4pICAgICAgICAgIHwgKCRBX2QkLEEpICAgICAgIHwgKCRBX2QkLE0pICAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCg0KfCAqKlNob3J0IGhhbmQqKiB8ICoqTWV0aG9kKiogICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnw6LS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18DQp8IChOLE4pICAgICAgICAgIHwgU2ltcGxlIGV4cG9uZW50aWFsIHNtb290aGluZyAgICAgICAgfA0KfCAoQSxOKSAgICAgICAgICB8IEhvbHQncyBsaW5lYXIgbWV0aG9kICAgICAgICAgICAgICAgIHwNCnwgKCRBX2QkLE4pICAgICAgfCBBZGRpdGl2ZSBkYW1wZWQgdHJlbmQgbWV0aG9kICAgICAgICB8DQp8IChBLEEpICAgICAgICAgIHwgQWRkaXRpdmUgSG9sdC1XaW50ZXJzJyBtZXRob2QgICAgICAgfA0KfCAoQSxNKSAgICAgICAgICB8IE11bHRpcGxpY2F0aXZlIEhvbHQtV2ludGVycycgbWV0aG9kIHwNCnwgKCRBX2QkLE0pICAgICAgfCBIb2x0LVdpbnRlcnMnIGRhbXBlZCBtZXRob2QgICAgICAgICB8DQoNCiMjIElubm92YXRpb25zIHN0YXRlIHNwYWNlIG1vZGVscyBmb3IgZXhwb25lbnRpYWwgc21vb3RoaW5nDQoNClRoZSBleHBvbmVudGlhbCBzbW9vdGhpbmcgbWV0aG9kcyBwcmVzZW50ZWQgaW4gVGFibGUgOC42IGFyZSBhbGdvcml0aG1zIHdoaWNoIGdlbmVyYXRlIHBvaW50IGZvcmVjYXN0cy4gVGhlIHN0YXRpc3RpY2FsIG1vZGVscyBpbiB0aGlzIHNlY3Rpb24gZ2VuZXJhdGUgdGhlIHNhbWUgcG9pbnQgZm9yZWNhc3RzLCBidXQgY2FuIGFsc28gZ2VuZXJhdGUgcHJlZGljdGlvbiAob3IgZm9yZWNhc3QpIGludGVydmFscy4gQSBzdGF0aXN0aWNhbCBtb2RlbCBpcyBhIHN0b2NoYXN0aWMgKG9yIHJhbmRvbSkgZGF0YSBnZW5lcmF0aW5nIHByb2Nlc3MgdGhhdCBjYW4gcHJvZHVjZSBhbiBlbnRpcmUgZm9yZWNhc3QgZGlzdHJpYnV0aW9uLg0KDQpFYWNoIG1vZGVsIGNvbnNpc3RzIG9mIGEgbWVhc3VyZW1lbnQgZXF1YXRpb24gdGhhdCBkZXNjcmliZXMgdGhlIG9ic2VydmVkIGRhdGEsIGFuZCBzb21lIHN0YXRlIGVxdWF0aW9ucyB0aGF0IGRlc2NyaWJlIGhvdyB0aGUgdW5vYnNlcnZlZCBjb21wb25lbnRzIG9yIHN0YXRlcyAobGV2ZWwsIHRyZW5kLCBzZWFzb25hbCkgY2hhbmdlIG92ZXIgdGltZS4gSGVuY2UsIHRoZXNlIGFyZSByZWZlcnJlZCB0byBhcyBzdGF0ZSBzcGFjZSBtb2RlbHMuDQoNCkZvciBlYWNoIG1ldGhvZCB0aGVyZSBleGlzdCB0d28gbW9kZWxzOiBvbmUgd2l0aCBhZGRpdGl2ZSBlcnJvcnMgYW5kIG9uZSB3aXRoIG11bHRpcGxpY2F0aXZlIGVycm9ycy4gVG8gZGlzdGluZ3Vpc2ggYmV0d2VlbiBhIG1vZGVsIHdpdGggYWRkaXRpdmUgZXJyb3JzIGFuZCBvbmUgd2l0aCBtdWx0aXBsaWNhdGl2ZSBlcnJvcnMgKGFuZCBhbHNvIHRvIGRpc3Rpbmd1aXNoIHRoZSBtb2RlbHMgZnJvbSB0aGUgbWV0aG9kcyksIHdlIGFkZCBhIHRoaXJkIGxldHRlciB0byB0aGUgY2xhc3NpZmljYXRpb24uIFdlIGxhYmVsIGVhY2ggc3RhdGUgc3BhY2UgbW9kZWwgYXMgRVRTKOKLhSzii4UsKSBmb3IgKEVycm9yLCBUcmVuZCwgU2Vhc29uYWwpLg0KDQojIyMgRVRTKEEsTixOKTogc2ltcGxlIGV4cG9uZW50aWFsIHNtb290aGluZyB3aXRoIGFkZGl0aXZlIGVycm9ycw0KDQojIyMgRVRTKE0sTixOKTogc2ltcGxlIGV4cG9uZW50aWFsIHNtb290aGluZyB3aXRoIG11bHRpcGxpY2F0aXZlIGVycm9ycw0KDQojIyMgRVRTKEEsQSxOKTogSG9sdOKAmXMgbGluZWFyIG1ldGhvZCB3aXRoIGFkZGl0aXZlIGVycm9ycw0KDQojIyMgRVRTKE0sQSxOKTogSG9sdOKAmXMgbGluZWFyIG1ldGhvZCB3aXRoIG11bHRpcGxpY2F0aXZlIGVycm9ycw0KDQojIyBFc3RpbWF0aW9uIGFuZCBtb2RlbCBzZWxlY3Rpb24NCg0KIyMjIEVzdGltYXRpbmcgRVRTIG1vZGVscw0KDQpBbiBhbHRlcm5hdGl2ZSB0byBlc3RpbWF0aW5nIHRoZSBwYXJhbWV0ZXJzIGJ5IG1pbmltaXNpbmcgdGhlIHN1bSBvZiBzcXVhcmVkIGVycm9ycyBpcyB0byBtYXhpbWlzZSB0aGUg4oCcbGlrZWxpaG9vZC7igJ0gVGhlIGxpa2VsaWhvb2QgaXMgdGhlIHByb2JhYmlsaXR5IG9mIHRoZSBkYXRhIGFyaXNpbmcgZnJvbSB0aGUgc3BlY2lmaWVkIG1vZGVsLiBUaHVzLCBhIGxhcmdlIGxpa2VsaWhvb2QgaXMgYXNzb2NpYXRlZCB3aXRoIGEgZ29vZCBtb2RlbC4gRm9yIGFuIGFkZGl0aXZlIGVycm9yIG1vZGVsLCBtYXhpbWlzaW5nIHRoZSBsaWtlbGlob29kIChhc3N1bWluZyBub3JtYWxseSBkaXN0cmlidXRlZCBlcnJvcnMpIGdpdmVzIHRoZSBzYW1lIHJlc3VsdHMgYXMgbWluaW1pc2luZyB0aGUgc3VtIG9mIHNxdWFyZWQgZXJyb3JzLiBIb3dldmVyLCBkaWZmZXJlbnQgcmVzdWx0cyB3aWxsIGJlIG9idGFpbmVkIGZvciBtdWx0aXBsaWNhdGl2ZSBlcnJvciBtb2RlbHMuIA0KDQojIyMgTW9kZWwgc2VsZWN0aW9uDQoNCkEgZ3JlYXQgYWR2YW50YWdlIG9mIHRoZSBFVFMgc3RhdGlzdGljYWwgZnJhbWV3b3JrIGlzIHRoYXQgaW5mb3JtYXRpb24gY3JpdGVyaWEgY2FuIGJlIHVzZWQgZm9yIG1vZGVsIHNlbGVjdGlvbi4gVGhlIEFJQywgJEFJQ19jJCBhbmQgQklDIGNhbiBiZSB1c2VkIGhlcmUgdG8gZGV0ZXJtaW5lIHdoaWNoIG9mIHRoZSBFVFMgbW9kZWxzIGlzIG1vc3QgYXBwcm9wcmlhdGUgZm9yIGEgZ2l2ZW4gdGltZSBzZXJpZXMuDQoNCk1vZGVscyB3aXRoIG11bHRpcGxpY2F0aXZlIGVycm9ycyBhcmUgdXNlZnVsIHdoZW4gdGhlIGRhdGEgYXJlIHN0cmljdGx5IHBvc2l0aXZlLCBidXQgYXJlIG5vdCBudW1lcmljYWxseSBzdGFibGUgd2hlbiB0aGUgZGF0YSBjb250YWluIHplcm9zIG9yIG5lZ2F0aXZlIHZhbHVlcy4gDQoNCiMjIyBFeGFtcGxlOiBEb21lc3RpYyBob2xpZGF5IHRvdXJpc3QgdmlzaXRvciBuaWdodHMgaW4gQXVzdHJhbGlhDQoNCldlIG5vdyBlbXBsb3kgdGhlIEVUUyBzdGF0aXN0aWNhbCBmcmFtZXdvcmsgdG8gZm9yZWNhc3QgQXVzdHJhbGlhbiBob2xpZGF5IHRvdXJpc20gb3ZlciB0aGUgcGVyaW9kIDIwMTbigJMyMDE5LiBXZSBsZXQgdGhlIEVUUygpIGZ1bmN0aW9uIHNlbGVjdCB0aGUgbW9kZWwgYnkgbWluaW1pc2luZyB0aGUgQUlDYy4NCg0KYGBge3J9DQphdXNfaG9saWRheXMgPC0gdG91cmlzbSAlPiUNCiAgZmlsdGVyKFB1cnBvc2UgPT0gIkhvbGlkYXkiKSAlPiUNCiAgc3VtbWFyaXNlKFRyaXBzID0gc3VtKFRyaXBzKS8xZTMpDQpmaXQgPC0gYXVzX2hvbGlkYXlzICU+JQ0KICBtb2RlbChFVFMoVHJpcHMpKQ0KcmVwb3J0KGZpdCkNCmBgYA0KVGhlIG1vZGVsIHNlbGVjdGVkIGlzIEVUUyhNLE4sQSkuDQoNClRoZSBzbWFsbCB2YWx1ZXMgb2YgJM6zJCBpbmRpY2F0ZSB0aGF0IHRoZSBzZWFzb25hbCBjb21wb25lbnRzIGNoYW5nZSB2ZXJ5IGxpdHRsZSBvdmVyIHRpbWUuIFRoZSBuYXJyb3cgcHJlZGljdGlvbiBpbnRlcnZhbHMgaW5kaWNhdGUgdGhhdCB0aGUgc2VyaWVzIGlzIHJlbGF0aXZlbHkgZWFzeSB0byBmb3JlY2FzdCBkdWUgdG8gdGhlIHN0cm9uZyB0cmVuZCBhbmQgc2Vhc29uYWxpdHkuDQpgYGB7cn0NCmNvbXBvbmVudHMoZml0KSAlPiUNCiAgYXV0b3Bsb3QoKSArDQogIGxhYnModGl0bGUgPSAiRVRTKE0sTixBKSBjb21wb25lbnRzIikNCmBgYA0KICBCZWNhdXNlIHRoaXMgbW9kZWwgaGFzIG11bHRpcGxpY2F0aXZlIGVycm9ycywgdGhlIGlubm92YXRpb24gcmVzaWR1YWxzIGFyZSBub3QgZXF1aXZhbGVudCB0byB0aGUgcmVndWxhciByZXNpZHVhbHMgKGkuZS4sIHRoZSBvbmUtc3RlcCB0cmFpbmluZyBlcnJvcnMpLiBUaGUgaW5ub3ZhdGlvbiByZXNpZHVhbHMgYXJlIGdpdmVuIGJ5ICRcaGF0e861fV90fSQsIHdoaWxlIHRoZSByZWd1bGFyIHJlc2lkdWFscyBhcmUgZGVmaW5lZCBhcyAkeV904oiSXGhhdHt5fV97dHx04oiSMX0kLg0KYGBge3J9DQphdWdtZW50KGZpdCkgJT4lIA0KICBhdXRvcGxvdCgucmVzaWQpDQphdWdtZW50KGZpdCkgJT4lIA0KICBhdXRvcGxvdCguaW5ub3YpDQpgYGANCiMjIEZvcmVjYXN0aW5nIHdpdGggRVRTIG1vZGVscw0KDQpFVFMgcG9pbnQgZm9yZWNhc3RzIGFyZSBlcXVhbCB0byB0aGUgbWVkaWFucyBvZiB0aGUgZm9yZWNhc3QgZGlzdHJpYnV0aW9ucy4gRm9yIG1vZGVscyB3aXRoIG9ubHkgYWRkaXRpdmUgY29tcG9uZW50cywgdGhlIGZvcmVjYXN0IGRpc3RyaWJ1dGlvbnMgYXJlIG5vcm1hbCwgc28gdGhlIG1lZGlhbnMgYW5kIG1lYW5zIGFyZSBlcXVhbC4gRm9yIEVUUyBtb2RlbHMgd2l0aCBtdWx0aXBsaWNhdGl2ZSBlcnJvcnMsIG9yIHdpdGggbXVsdGlwbGljYXRpdmUgc2Vhc29uYWxpdHksIHRoZSBwb2ludCBmb3JlY2FzdHMgd2lsbCBub3QgYmUgZXF1YWwgdG8gdGhlIG1lYW5zIG9mIHRoZSBmb3JlY2FzdCBkaXN0cmlidXRpb25zLg0KDQpgYGB7cn0NCmZpdCAlPiUNCiAgZm9yZWNhc3QoaCA9IDgpICU+JQ0KICBhdXRvcGxvdChhdXNfaG9saWRheXMpICsNCiAgbGFicyh5PSJEb21lc3RpYyBob2xpZGF5IHZpc2l0b3JzIGluIEF1c3RyYWxpYSAodGhvdXNhbmRzKSIpDQpgYGANCiMjIyBQcmVkaWN0aW9uIGludGVydmFscw0KDQogVGhlIHByZWRpY3Rpb24gaW50ZXJ2YWxzIHdpbGwgZGlmZmVyIGJldHdlZW4gbW9kZWxzIHdpdGggYWRkaXRpdmUgYW5kIG11bHRpcGxpY2F0aXZlIG1ldGhvZHMuDQoNCkZvciBtb3N0IEVUUyBtb2RlbHMsIGEgcHJlZGljdGlvbiBpbnRlcnZhbCBjYW4gYmUgd3JpdHRlbiBhcw0KDQokXGhhdHt5fV97VCtofFR9IFxwbSBjIFxzaWdtYV9oJA==