library(tidyverse)
## ── Attaching packages ─────────────────────────────────────── tidyverse 1.3.2 ──
## ✔ ggplot2 3.3.6      ✔ purrr   0.3.4 
## ✔ tibble  3.1.8      ✔ dplyr   1.0.10
## ✔ tidyr   1.2.1      ✔ stringr 1.4.1 
## ✔ readr   2.1.2      ✔ forcats 0.5.1 
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()

Exercise 1: Visualising the model with a prediction band

visreg provides us an efficient routine to visualise our linear model. However, it only produces the line with a confidence band. We need to do a bit more work to visualising our model with a prediction band.

  1. Load the package datarium and the data marketing. Fit the linear model, and save the result in the object lm.youtube again. Then, sweep your fitted result by the package broom and get the tidy version lm.youtube.

    library(datarium)
    library(broom)
    data(`marketing`)
    lm.youtube <- lm(sales ~ youtube, data=marketing)
    lm.youtube.fit <- augment(lm.youtube)
  2. Get all prediction intervals for each observed youtube by calling predict() without supplying new.data, and save the result in lm.youtube.pred. We can then bind lm.youtube.pred with lm.youtube.fit by column with the R function cbind(), aka colunm bind.

    lm.youtube.pred <- predict(lm.youtube, interval='prediction')
    ## Warning in predict.lm(lm.youtube, interval = "prediction"): predictions on current data refer to _future_ responses
    lm.youtube.fit.pred <- lm.youtube.fit |> cbind(lm.youtube.pred) |> select(-fit) |> tibble()
    lm.youtube.fit.pred
    ## # A tibble: 200 × 10
    ##    sales youtube .fitted .resid    .hat .sigma   .cooksd .std.resid   lwr   upr
    ##    <dbl>   <dbl>   <dbl>  <dbl>   <dbl>  <dbl>     <dbl>      <dbl> <dbl> <dbl>
    ##  1 26.5    276.    21.6   4.96  0.00970   3.90 0.00794       1.27   13.8   29.3
    ##  2 12.5     53.4   11.0   1.50  0.0122    3.92 0.000920      0.387   3.22  18.7
    ##  3 11.2     20.6    9.42  1.74  0.0165    3.92 0.00169       0.449   1.65  17.2
    ##  4 22.2    182.    17.1   5.12  0.00501   3.90 0.00434       1.31    9.35  24.8
    ##  5 15.5    217.    18.8  -3.27  0.00578   3.91 0.00205      -0.839  11.0   26.5
    ##  6  8.64    10.4    8.94 -0.295 0.0180    3.92 0.0000534    -0.0762  1.15  16.7
    ##  7 14.2     69     11.7   2.44  0.0105    3.92 0.00208       0.627   3.97  19.5
    ##  8 15.8    144.    15.3   0.544 0.00549   3.92 0.0000538     0.140   7.56  23.0
    ##  9  5.76    10.3    8.93 -3.17  0.0181    3.91 0.00616      -0.818   1.15  16.7
    ## 10 12.7    240.    19.8  -7.12  0.00690   3.89 0.0116       -1.83   12.1   27.6
    ## # … with 190 more rows

    Here we further remove the duplicate column fit.

  3. First, visualise your linear model with the fitted line and the scatter plot. You can then add the prediction band by the R function geom_ribbon() with aes(ymin = lwr, ymax = upr) which defines a shaded region bounded by the lower limits and upper limits of the prediction intervals in your plot. You may need to adjust fill and alpha in geom_ribbon() to get a nicer plot.

    lm.youtube.fit.pred |> ggplot(aes(x = youtube ,y=sales)) +
        geom_point() +
        geom_ribbon(aes(ymin = lwr, ymax = upr), fill = "grey70", alpha=0.5) +
        geom_smooth(method='lm')
    ## `geom_smooth()` using formula 'y ~ x'

  4. The default prediction interval in R is a 95% prediction interval which means around 95% observations shall fall into the bands. Check the plot in Step 3 and count the points falling outside the prediction band. Add one or two comments on your findings.

Answer: It is not hard to see that there are 9 points falling outside the bands. This results in \(1-9/200=95.5\%\) observations within the bands. The result agrees well with the confidence level. We can also complete this job by filter() and summarise().

```r
lm.youtube.fit.pred |> filter(sales>upr|sales<lwr) |> summarise(n())
```

```
## # A tibble: 1 × 1
##   `n()`
##   <int>
## 1     9
```
  1. Actually, the prediction band we plotted is a pointwise 95% prediction interval. The term pointwise means that, each individual observation falls into the corresponding prediction interval with a probability 95%. Try to split youtube into two groups youtube<200 and youtube>200 and count the points falling in the prediction band for each group. Add one or two comments on your findings.

Answer: Graphical approaches are not attrative this time but we can still use filter() and summarise(). The results can be found in the following R code chunks. The critical issue is that the prediction bands seem too wide for the lower budgets and too narrow for the higher budgets. The bands are not consistent for different budgets (x) and therefore violate the pointwise condition.

```r
lm.youtube.fit.pred |> filter(youtube>200) |> ggplot(aes(x = youtube ,y=sales)) +
    geom_point() +
    geom_ribbon(aes(ymin = lwr, ymax = upr), fill = "grey70", alpha=0.5) +
    geom_smooth(method='lm')
```

```
## `geom_smooth()` using formula 'y ~ x'
```

<img src="solC03_files/figure-html/unnamed-chunk-6-1.png" width="672" />

```r
    lm.youtube.fit.pred |> filter(youtube<200) |> ggplot(aes(x = youtube ,y=sales)) +
    geom_point() +
    geom_ribbon(aes(ymin = lwr, ymax = upr), fill = "grey70", alpha=0.5) +
    geom_smooth(method='lm')
```

```
## `geom_smooth()` using formula 'y ~ x'
```

<img src="solC03_files/figure-html/unnamed-chunk-6-2.png" width="672" />


```r
lm.youtube.fit.pred |> filter(youtube>200) |> summarise(n())
```

```
## # A tibble: 1 × 1
##   `n()`
##   <int>
## 1    93
```

```r
lm.youtube.fit.pred |> filter(youtube>200) |> filter(sales<upr) |> filter(sales>lwr) |> summarise(n())
```

```
## # A tibble: 1 × 1
##   `n()`
##   <int>
## 1    84
```

```r
lm.youtube.fit.pred |> filter(youtube<200) |> summarise(n())
```

```
## # A tibble: 1 × 1
##   `n()`
##   <int>
## 1   107
```

```r
lm.youtube.fit.pred |> filter(youtube<200) |> filter(sales<upr) |> filter(sales>lwr) |> summarise(n())
```

```
## # A tibble: 1 × 1
##   `n()`
##   <int>
## 1   107
```

Exercise 2: Model diagnostics

As we can see from Exercise 1, the prediction interval tends to be too wide when youtube is large and too narrow when youtube is small. A similar issue also arises when we try to predict the sales given zero budget in Exercise 3 of Lab C2. The prediction at zero budget may significantly overestimate the actual sales.

These issues allude to a critical point: does our linear model provide a good fit to our data?

Of course, we can always get some clues from the scatter plot and smoothed curve for sales against youtube. We can even conclude that the fitted line fails to capture a significant portion of uncertainties in our data. So a linear model may not be an adequate model for the relationship between sales and youtube.

The next question is how to identify and quantify the inadequacy of our linear model?

The visualisation of model identifies some ill-posed patterns in our model but it fails to identify the root cause of the problem. \(R^2\) is a simple indicator but it won’t capture the issues on prediction discussed above.

What we need are some diagnostic tools to check the fitness of our linear model on the data set and identify the crux in our fitted model. However, before we carry out the examination on our fitted linear model, we need to know when a fitted linear model will behave well.

To ensure the overall good performance of a fitted linear model, we need our data satisfy a few conditions, i.e. four assumptions as follows

with Linearity being the most important.

  1. Firstly we’ll be looking at linearity and equal variance, both of which can be assessed using a plot of the residuals versus the fitted value.

    If linearity holds, we’d expect a plot of residuals vs fitted value to show no trend - the points should be scattered fairly constantly above and below the line - in particular we don’t want to see a curve.

    If equal variance holds, we’d expect the scatter of points around the trend to be constant as the fitted value changes. You want it to be relatively even, and in particular not increasing from left to right (i.e. not spreading out).

    We can demonstrate this idea by using the stochastic simulation. An example of a good plot (left) and bad plot (right) is shown below for two artificial data sets, i.e., linear and exponential.

    set.seed(2020)
    n <- 50
    ab <- c(0,2)
    demo <- tibble(x=(1:n)/n, e= rnorm(n, sd=0.1)) |> mutate (y=ab[1]+ab[2]*x+e) |> 
      mutate (y2=exp(ab[1]+ab[2]*x+e))
    demo |> select(x, y, y2) |> gather(key = "Group", value = "Y", -x) |>
      ggplot(aes(x=x,y=Y,col=Group)) + geom_point()

    g1 <- lm(y~x,data=demo) |> augment() |> 
      ggplot(aes(x=.fitted,y=.resid)) + geom_point() +geom_smooth()+geom_hline(yintercept=0)
    g2 <- lm(y2~x,data=demo) |> augment() |> 
      ggplot(aes(x=.fitted,y=.resid)) + geom_point() +geom_smooth()+geom_hline(yintercept=0)
    library(patchwork)
    g1+g2 
    ## `geom_smooth()` using method = 'loess' and formula 'y ~ x'
    ## `geom_smooth()` using method = 'loess' and formula 'y ~ x'

    Compare the scatter plots and the plots of residuals versus fitted values. Think about the reason why we include horizontal lines at \(y=0\) and smoothed curves with confidence bands in the residual plots.

    Answer: The right panel shows a clear curvature but the left panel looks quite random. The blue smoothed curves try to extract general patterns from residuals vs fits plots and the confidence bands can help to tell if the general trend differs a lot from the baseline (\(y=0\)).

    You can see more examples using the interactive found here:

    https://shiny.massey.ac.nz/jcmarsha/linearity

    You can of course modify some parameters in the above R code chunk and even change the exponential function to some other functions. A possible choice is to change ab <- c(0,2) to ab <- c(0,0.5).

    set.seed(2020)
    n <- 50
    ab <- c(0,0.5)
    demo <- tibble(x=(1:n)/n, e= rnorm(n, sd=0.1)) |> mutate (y=ab[1]+ab[2]*x+e) |> 
      mutate (y2=exp(ab[1]+ab[2]*x+e))
    demo |> select(x, y, y2) |> gather(key = "Group", value = "Y", -x) |>
      ggplot(aes(x=x,y=Y,col=Group)) + geom_point()

    g1 <- lm(y~x,data=demo) |> augment() |> 
      ggplot(aes(x=.fitted,y=.resid)) + geom_point() +geom_smooth()+geom_hline(yintercept=0)
    g2 <- lm(y2~x,data=demo) |> augment() |> 
      ggplot(aes(x=.fitted,y=.resid)) + geom_point() +geom_smooth()+geom_hline(yintercept=0)
    library(patchwork)
    g1+g2 
    ## `geom_smooth()` using method = 'loess' and formula 'y ~ x'
    ## `geom_smooth()` using method = 'loess' and formula 'y ~ x'

    Answer: This case is a little tricky as the residuals vs fits plot does not reveal any issue in the exponential model. Why? This is because the exponential function \(\exp(x)\) can be well approximated by a linear function \(1+x\) if \(x\) is small. This fact actually provides additional warranties on using linear models in scientific research. Though many natural phenomena may follow some nonlinear laws, those complicated nonlinear functions can be approximated by our linear models under certain conditions!

  2. Let’s see how well our model for sales does by producing the diagnostic plot for the linear model you fit above using the following.

    lm.youtube.fit |> ggplot(aes(x=.fitted,y=.resid)) +
      geom_point()+geom_smooth()+geom_hline(yintercept=0)
    ## `geom_smooth()` using method = 'loess' and formula 'y ~ x'

    Take a good look at the plot. Do you think linearity and equal variance hold? Add some notes about each assumption to your notebook.

    Answer: Linearity looks ok, though we still worry about the left end corresponding to small budgets. The equal variance is certainly violated!

  3. In addition to the residuals vs fits plot, we have another tool, i.e. the scale-location plot, to check equal variance. The plot can be generated by using the following R code chunk:

    lm.youtube.fit |> mutate(.root.abs.std.resid=sqrt(abs(.std.resid))) |>
      ggplot(aes(x=.fitted,y=.root.abs.std.resid)) + geom_point()+geom_smooth()+geom_hline(yintercept=0)
    ## `geom_smooth()` using method = 'loess' and formula 'y ~ x'

    Similar to the residual versus fits plot, the scale-Location plot shows whether residuals are spread equally along the ranges of input variables (predictor). The assumption of equal variance (homoscedasticity) could also be checked with this plot. If we see a horizontal line with randomly spread points, it means that the model is good.

    Take a good look at the plot. Do you think linearity and equal variance hold? Add some notes about each assumption to your notebook.

    Answer: Certainly, equal variance fails to hold. But the linearity can not be verified by the scale-location plot.

    In the scale-location plot we still use the fitted values as \(x\) coordinates. But another quantity - the square root of absolute value of standardised residuals- is used as \(y\) coordinates. Standardised residuals are obtained by dividing the residuals by the standard error of residuals as \(s_i=e_i/sd_{res}\). An advantange of standardised residuals is that both the variance and standard error are just 1 for this sequence.

    The standardised residuals can be readily obtained from the fitted model by the R function rstandard(). Otherwise, if you have carefully read the tibble from augment() and the R code chunk for the scale-location plot, you should have found the standardised residuals .std.resid right following .resid.

    The operation of dividing a sequence by its standard error is called standardisation in statistics. It is frequently used in statistics as the data on different scales can be handle with similar measures after the standardisaton.

    Optional Challenge: Modify the R code chunk in Step 1 and perform a simulation study on the scale-location plot.

    Answer: The following R code chunk can produce the simulation study. We can notice that the scale-location plot is not really a great tool for diagnostics.

    set.seed(2020)
    n <- 100
    ab <- c(0,2)
    demo <- tibble(x=(1:n)/n, e= rnorm(n, sd=0.25)) |> mutate (y=ab[1]+ab[2]*x+e) |> 
      mutate (y2=exp(ab[1]+ab[2]*x+e))
    demo |> select(x, y, y2) |> gather(key = "Group", value = "Y", -x) |>
      ggplot(aes(x=x,y=Y,col=Group)) + geom_point()

    g1 <- lm(y~x,data=demo) |> augment() |> mutate(.root.abs.std.resid=sqrt(abs(.std.resid))) |>
      ggplot(aes(x=.fitted,y=.root.abs.std.resid))  + geom_point() +geom_smooth()+geom_hline(yintercept=0)
    g2 <- lm(y2~x,data=demo) |> augment() |> mutate(.root.abs.std.resid=sqrt(abs(.std.resid))) |>
      ggplot(aes(x=.fitted,y=.root.abs.std.resid))  + geom_point() +geom_smooth()+geom_hline(yintercept=0)
    library(patchwork)
    g1+g2 
    ## `geom_smooth()` using method = 'loess' and formula 'y ~ x'
    ## `geom_smooth()` using method = 'loess' and formula 'y ~ x'

  4. The residuals vs fits plot and scale-location plot are usually sufficient for us to tell the issues on linearity and equal variance. Our next task is to verify the normality. Recall the simulation study in Lab 2 where we make a scatter plot of the residuals versus true random errors which can be approximated by the line \(y=x\). Even if we can’t observe the true random error in a real problem, we can still make a similar plot called quantile-quantile plot, aka Q-Q plot, via geom_qq() and geom_qq_line as follows.

    lm.youtube.fit |> ggplot(aes(sample=.std.resid)) + geom_qq(alpha=0.3) +  geom_qq_line(color='red')

    We are using the standardised residuals again. geom_qq() adds the black points while geom_qq_line() dispicts the red line. If most points follow the red line, we can feel free to assume the normality of the residuals. However, one must confirm the linearity and equal variance first before thinking about the normality.

    Of course, we need to know the appearance of a good Q-Q plot before we make any solid judgement. The normality assumption says the residuals follow normal distribution and the standardised residuals has zero mean and unit variance. We can then simulate a standard normal sample with zero mean and unit variance and check its Q-Q plot as follows.

    set.seed(2020)
    tibble(e=rnorm(100)) |> ggplot(aes(sample=e)) + geom_qq(alpha=0.3) +  geom_qq_line(color='red')

    Take a good look at the above two plots. Do you think the normality hold? Add some notes about the assumption to your notebook.

    Answer: From the Q-Q plot, one can argue that the normality holds. However, one need to first check the linearity and equal variance. If the above two conditions fail to hold, it is non-sense to check the normality.

  5. The last thing to check is slightly different from the above assumptions. We will look at the outliers in linear model. Just like we observe some distant points in a boxplot for one variable, we can have some outliers for \(x\) and \(y\). The trick thing is that, even both \(x\) and \(y\) look good in their own boxplots, their joint effort may push the point away from the main body in a scatter plot. A possible pitfall in fitting a linear model is that the linear model found by least square method is not very robust against outliers. Only a few outliers can distort the point estimates significantly. So it is essential to spot those bad guys hidden in our data.

    Of course, even a scatter plot can help us identify the outliers. If we want some more quantitative measurements, there is a specific measure called Cook’s distance, or just Cook’s \(D\), for each data point to measure its influence on the regression line. A large Cook’s distance suggests that this point may be an outlier which has a big influence on the whole regression line.

    This peice of informaiont is readily collected by augment() in the column .cooksd. The following R code chunk produces a plot of Cook’s distance against the index.

    lm.youtube.fit |> mutate(.index=1:n()) |> ggplot(aes(y=.cooksd,x=.index)) + geom_col() 

    Those spikes in the above plot suggests potential outliers. However, one need a cut-off point to distinguish the outliers from those tamed observations. Unfortunately, there is no a golden rule to split these two groups of points for arbitrary data sets. Some people uses 0.5 or 1 as a cut-off point but our Cook’s distances here are much smaller. We will see a refined diagnostic plot for outlier detection in the next step.

  6. We will skip independence in Lab 3 but revisit it in Lab 4. Before I conclude this exercise, I would like to give you a handy shortcut to produce all above plots. One can easily generate a set of residuals diagnostic plots via plot(), i.e the default plot function in R, as follows.

    plot(lm.youtube)

    The last one of above plots is called the residuals versus leverage plot. The leverage is another measure for identifying outliers and you shall notice that there are several points being flagged already. This plot will also contain a cut-off curve in the red dash line for Cook’s distances if there are any points with Cook’s \(D>0.5\).

    Besides of the convenience, another advantage of plot() is that the indices of potential outliers will be flagged in the plot. You can use the information in these diagnostic plots to locate the untamed outliers in your data.

    However, one shall notice that, we can’t customise those plots by following the same procedures in ggplot().

    In fact, plot() can produce six different diagnostic plots. You can make the individual plot by specifying which in plot() like plot(lm.youtube,which=1) or plot(lm.youtube,which=4). Have a try with which=1,2,3,4,5,6 and see which number gives you the desired plot.

    Answer: Just notice that the last one is Cook’s distance vs leverage plot, another tool for outlier detection.

    plot(lm.youtube,which=1) # residuals vs fits

    plot(lm.youtube,which=2) # Q-Q plot

    plot(lm.youtube,which=3) # scale-location

    plot(lm.youtube,which=4) # Cook's distance

    plot(lm.youtube,which=5) # Residuals vs leverage

    plot(lm.youtube,which=6) # Cook's distance vs leverage

Exercise 3: Transforming for a better model

We have to acknowledge that the residuals plots of lm.youtube suggest that the linear model is not a great one for describing the relationship between sales and youtube. A transformation is one way to deal with the non-linearity and unequal variance of the data. We will try the log transformation in this exercise.

  1. Instead of modelling sales in terms of youtube, we could instead take log transforms of sales and youtube to see if it is possible to get rid of the curvature in the relationship.

    lm.youtube.log <- lm(log(sales) ~ log(youtube), data=marketing)
    summary(lm.youtube)
    ## 
    ## Call:
    ## lm(formula = sales ~ youtube, data = marketing)
    ## 
    ## Residuals:
    ##      Min       1Q   Median       3Q      Max 
    ## -10.0632  -2.3454  -0.2295   2.4805   8.6548 
    ## 
    ## Coefficients:
    ##             Estimate Std. Error t value Pr(>|t|)    
    ## (Intercept) 8.439112   0.549412   15.36   <2e-16 ***
    ## youtube     0.047537   0.002691   17.67   <2e-16 ***
    ## ---
    ## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
    ## 
    ## Residual standard error: 3.91 on 198 degrees of freedom
    ## Multiple R-squared:  0.6119, Adjusted R-squared:  0.6099 
    ## F-statistic: 312.1 on 1 and 198 DF,  p-value: < 2.2e-16
    summary(lm.youtube.log)
    ## 
    ## Call:
    ## lm(formula = log(sales) ~ log(youtube), data = marketing)
    ## 
    ## Residuals:
    ##      Min       1Q   Median       3Q      Max 
    ## -0.43349 -0.15917  0.01696  0.16910  0.39399 
    ## 
    ## Coefficients:
    ##              Estimate Std. Error t value Pr(>|t|)    
    ## (Intercept)   1.02284    0.07372   13.87   <2e-16 ***
    ## log(youtube)  0.35504    0.01487   23.87   <2e-16 ***
    ## ---
    ## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
    ## 
    ## Residual standard error: 0.2109 on 198 degrees of freedom
    ## Multiple R-squared:  0.7421, Adjusted R-squared:  0.7408 
    ## F-statistic: 569.8 on 1 and 198 DF,  p-value: < 2.2e-16

    Compare the summary output of the two models you have. Which do you think is better? Why?

    Answer: Multiple R-squared has been improved a lot. Therefore, the log transformed model is better.

  2. We add the following code to produce the residual vs fitted plot for this new model.

    lm.youtube.log.fit <- augment(lm.youtube.log)
    lm.youtube.log.fit |> ggplot(aes(x=.fitted,y=.std.resid)) +
      geom_point()+geom_smooth(span=1.2)+geom_hline(yintercept=0)
    ## `geom_smooth()` using method = 'loess' and formula 'y ~ x'

    What the hell is this curve smoothed by loess - the weirdness of it going through the outlier in the bottom left?

    Answer: loess means LOcally WEighted Scatter-plot Smoother. It is a generalisation of our linear model to the nonlinear function. One potential drawback of loess is that it may depend heavily on one (or a small portion of) data point(s) as it smooths the scatter-plot locally. This is the root cause of the weirdness.

    The \(y\)-axis is now the standardised residuals. If one fit a linear model with a transformation on \(y\), the residuals will not be reported and the standardised residuals will be used thoroughly. Why do we not use the residuals?

    How is the assumption of linearity now? What about equal variance? Remember to add comments to your notebook.

    Answer: The linearity is getting better if we ignore the bottom left. Equal variance does not hold anyway, but there is a slight improvement. Produce other diagnostic plots and add some comments on them.

      plot(lm.youtube.log)

    Answer: Most issues are still there but getting better slightly. A potential outlier (131) can be identified from the residual vs leverage plot.

  3. Earlier we produced prediction intervals for sales with youtube budget equal to zero dollar. Re-do these using your transformed model. Remember that you’ll need to exponentiate the resulting intervals using the exp() function (Why?)

    How do these intervals compare with your previous ones? Add some comments to your notebook about this.

    Answer: Sorry that I made a mistake here when preparing this lab. Clearly, we can’t take the logarithm of zero. So it is not viable to produce the prediction by using predict(). One thing we can notice that the log transformed model is a power law model at the raw scale as \(sales=c\times(youtube)^k\). So the sales will be exactly zero given zero budget. Of course, we may not want to use a model like this since the sales should not be zero even we have not advertising budget. A quick remedy is to use a shifted log tranformation as \(x^*=\log(x+1)\) as follows. This will fix the problem caused by the logarithm of zero.

    lm.youtube.log1 <- lm(log(sales) ~ log(youtube+1), data=marketing)
    newdata <- data.frame(youtube=0)
    exp(predict(lm.youtube.log1,newdata,interval = 'prediction'))
    ##        fit      lwr      upr
    ## 1 2.603275 1.664424 4.071702

    Now we have a much narrow prediction interval after comparing it with Step 5 of Ex 3 in Lab 2!

  4. Now let’s try visualising the second linear model. You should notice that the \(x\)-axis is on the normal scale (even though we applied a log transformation in the model formula) but the \(y\)-axis is on the log scale.

    library(visreg)
    visreg(lm.youtube.log, gg=TRUE)

    Nonetheless, the model fit should be a bit better, and will be curved. Notice that we’ve used a linear model to fit a curved relationship. The key is that the linearity of the model is in terms of the coefficients (each term can contain only one \(\beta\) as a multiplier, and terms must be added together) not in terms of the way \(y\) and \(x\) are related. You can apply any transformation you like to \(x\) and \(y\) as needed to fit the data.

  5. You can also visualise the second linear model on the natural scale by applying a transformation in the visreg command. Try the following:

    visreg(lm.youtube.log, trans=exp, partial=TRUE, gg =TRUE)

    The trans=exp uses exponentiation to transform the outcome variable. The partial=TRUE means that residuals deviated from the line (and thus data values) are plotted as points as well. You may want to change the y-axis label by adding ylab="Body weight (kg)" to the above command.

    Add some comments to your notebook about the model fit and how well you think it does. Notice that the confidence bands at the right end are larger than those at the left end or in the middle. Why is this?

    Answer: Though the unequal variance is still there, the visualisation suggests a much better fit. The fitted curve traces the trend of sales well with points scatterring around the two sides of the curve evenly. The wider confidence bands is made by the inverse transformation, i.e. the exponential function.

  6. Optional Challenge: Try to find a better transform for this data set!

    Answer: No standard solution. But I am happy to review your work if you send it to me via email.

LS0tCnRpdGxlOiAiV29ya3Nob3AgQzM6IERpYWdub3N0aWNzIGFuZCBUcmFuc2Zvcm1hdGlvbnMiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OiAKICAgIHRvYzogeWVzCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCi0tLQoKYGBge3J9CmxpYnJhcnkodGlkeXZlcnNlKQpgYGAKCiMjIEV4ZXJjaXNlIDE6IFZpc3VhbGlzaW5nIHRoZSBtb2RlbCB3aXRoIGEgcHJlZGljdGlvbiBiYW5kCgpgdmlzcmVnYCBwcm92aWRlcyB1cyBhbiBlZmZpY2llbnQgcm91dGluZSB0byB2aXN1YWxpc2Ugb3VyIGxpbmVhciBtb2RlbC4gSG93ZXZlciwgaXQgb25seSBwcm9kdWNlcyB0aGUgbGluZSB3aXRoIGEgY29uZmlkZW5jZSBiYW5kLiBXZSBuZWVkIHRvIGRvIGEgYml0IG1vcmUgd29yayB0byB2aXN1YWxpc2luZyBvdXIgbW9kZWwgd2l0aCBhIHByZWRpY3Rpb24gYmFuZC4gCgoxLiBMb2FkIHRoZSBwYWNrYWdlIGBkYXRhcml1bWAgYW5kIHRoZSBkYXRhIGBtYXJrZXRpbmdgLiBGaXQgdGhlIGxpbmVhciBtb2RlbCwgYW5kIHNhdmUgdGhlIHJlc3VsdCBpbiB0aGUgb2JqZWN0IGBsbS55b3V0dWJlYCBhZ2Fpbi4gVGhlbiwgc3dlZXAgeW91ciBmaXR0ZWQgcmVzdWx0IGJ5IHRoZSBwYWNrYWdlIGBicm9vbWAgYW5kIGdldCB0aGUgdGlkeSB2ZXJzaW9uIGBsbS55b3V0dWJlYC4KCiAgICBgYGB7cn0KICAgIGxpYnJhcnkoZGF0YXJpdW0pCiAgICBsaWJyYXJ5KGJyb29tKQogICAgZGF0YShgbWFya2V0aW5nYCkKICAgIGxtLnlvdXR1YmUgPC0gbG0oc2FsZXMgfiB5b3V0dWJlLCBkYXRhPW1hcmtldGluZykKICAgIGxtLnlvdXR1YmUuZml0IDwtIGF1Z21lbnQobG0ueW91dHViZSkKICAgIGBgYAoKMi4gR2V0IGFsbCBwcmVkaWN0aW9uIGludGVydmFscyBmb3IgZWFjaCBvYnNlcnZlZCBgeW91dHViZWAgYnkgY2FsbGluZyBgcHJlZGljdCgpYCB3aXRob3V0IHN1cHBseWluZyBgbmV3LmRhdGFgLCBhbmQgc2F2ZSB0aGUgcmVzdWx0IGluIGBsbS55b3V0dWJlLnByZWRgLiBXZSBjYW4gdGhlbiBiaW5kIGBsbS55b3V0dWJlLnByZWRgIHdpdGggYGxtLnlvdXR1YmUuZml0YCBieSBjb2x1bW4gd2l0aCB0aGUgUiBmdW5jdGlvbiBgY2JpbmQoKWAsIGFrYSBjb2x1bm0gYmluZC4KCiAgICBgYGB7cn0KICAgIGxtLnlvdXR1YmUucHJlZCA8LSBwcmVkaWN0KGxtLnlvdXR1YmUsIGludGVydmFsPSdwcmVkaWN0aW9uJykKICAgIGxtLnlvdXR1YmUuZml0LnByZWQgPC0gbG0ueW91dHViZS5maXQgfD4gY2JpbmQobG0ueW91dHViZS5wcmVkKSB8PiBzZWxlY3QoLWZpdCkgfD4gdGliYmxlKCkKICAgIGxtLnlvdXR1YmUuZml0LnByZWQKICAgIGBgYAoKICAgIEhlcmUgd2UgZnVydGhlciByZW1vdmUgdGhlIGR1cGxpY2F0ZSBjb2x1bW4gYGZpdGAuCgozLiBGaXJzdCwgdmlzdWFsaXNlIHlvdXIgbGluZWFyIG1vZGVsIHdpdGggdGhlIGZpdHRlZCBsaW5lIGFuZCB0aGUgc2NhdHRlciBwbG90LiBZb3UgY2FuIHRoZW4gYWRkIHRoZSBwcmVkaWN0aW9uIGJhbmQgYnkgdGhlIFIgZnVuY3Rpb24gYGdlb21fcmliYm9uKClgIHdpdGggYGFlcyh5bWluID0gbHdyLCB5bWF4ID0gdXByKWAgd2hpY2ggZGVmaW5lcyBhIHNoYWRlZCByZWdpb24gYm91bmRlZCBieSB0aGUgbG93ZXIgbGltaXRzIGFuZCB1cHBlciBsaW1pdHMgb2YgdGhlIHByZWRpY3Rpb24gaW50ZXJ2YWxzIGluIHlvdXIgcGxvdC4gWW91IG1heSBuZWVkIHRvIGFkanVzdCBgZmlsbGAgYW5kIGBhbHBoYWAgaW4gYGdlb21fcmliYm9uKClgIHRvIGdldCBhIG5pY2VyIHBsb3QuCgogICAgYGBge3J9CiAgICBsbS55b3V0dWJlLmZpdC5wcmVkIHw+IGdncGxvdChhZXMoeCA9IHlvdXR1YmUgLHk9c2FsZXMpKSArCiAgICAgICAgZ2VvbV9wb2ludCgpICsKICAgICAgICBnZW9tX3JpYmJvbihhZXMoeW1pbiA9IGx3ciwgeW1heCA9IHVwciksIGZpbGwgPSAiZ3JleTcwIiwgYWxwaGE9MC41KSArCiAgICAgICAgZ2VvbV9zbW9vdGgobWV0aG9kPSdsbScpCiAgICBgYGAKICAgIAo0LiBUaGUgZGVmYXVsdCBwcmVkaWN0aW9uIGludGVydmFsIGluIFIgaXMgYSA5NSUgcHJlZGljdGlvbiBpbnRlcnZhbCB3aGljaCBtZWFucyBhcm91bmQgOTUlIG9ic2VydmF0aW9ucyBzaGFsbCBmYWxsIGludG8gdGhlIGJhbmRzLiAqQ2hlY2sgdGhlIHBsb3QgaW4gU3RlcCAzIGFuZCBjb3VudCB0aGUgcG9pbnRzIGZhbGxpbmcgb3V0c2lkZSB0aGUgcHJlZGljdGlvbiBiYW5kLiBBZGQgb25lIG9yIHR3byBjb21tZW50cyBvbiB5b3VyIGZpbmRpbmdzLioKCioqQW5zd2VyKio6IEl0IGlzIG5vdCBoYXJkIHRvIHNlZSB0aGF0IHRoZXJlIGFyZSA5IHBvaW50cyBmYWxsaW5nIG91dHNpZGUgdGhlIGJhbmRzLiBUaGlzIHJlc3VsdHMgaW4gJDEtOS8yMDA9OTUuNVwlJCBvYnNlcnZhdGlvbnMgd2l0aGluIHRoZSBiYW5kcy4gVGhlIHJlc3VsdCBhZ3JlZXMgd2VsbCB3aXRoIHRoZSBjb25maWRlbmNlIGxldmVsLiBXZSBjYW4gYWxzbyBjb21wbGV0ZSB0aGlzIGpvYiBieSBgZmlsdGVyKClgIGFuZCBgc3VtbWFyaXNlKClgLgoKICAgIGBgYHtyfQogICAgbG0ueW91dHViZS5maXQucHJlZCB8PiBmaWx0ZXIoc2FsZXM+dXByfHNhbGVzPGx3cikgfD4gc3VtbWFyaXNlKG4oKSkKICAgIGBgYAoKNS4gQWN0dWFsbHksIHRoZSBwcmVkaWN0aW9uIGJhbmQgd2UgcGxvdHRlZCBpcyBhICoqcG9pbnR3aXNlKiogOTUlIHByZWRpY3Rpb24gaW50ZXJ2YWwuIFRoZSB0ZXJtICoqcG9pbnR3aXNlKiogbWVhbnMgdGhhdCwgZWFjaCBpbmRpdmlkdWFsIG9ic2VydmF0aW9uIGZhbGxzIGludG8gdGhlIGNvcnJlc3BvbmRpbmcgcHJlZGljdGlvbiBpbnRlcnZhbCB3aXRoIGEgcHJvYmFiaWxpdHkgOTUlLiAqVHJ5IHRvIHNwbGl0IGB5b3V0dWJlYCBpbnRvIHR3byBncm91cHMgYHlvdXR1YmU8MjAwYCBhbmQgYHlvdXR1YmU+MjAwYCBhbmQgY291bnQgdGhlIHBvaW50cyBmYWxsaW5nIGluIHRoZSBwcmVkaWN0aW9uIGJhbmQgZm9yIGVhY2ggZ3JvdXAuIEFkZCBvbmUgb3IgdHdvIGNvbW1lbnRzIG9uIHlvdXIgZmluZGluZ3MuKgoKKipBbnN3ZXIqKjogR3JhcGhpY2FsIGFwcHJvYWNoZXMgYXJlIG5vdCBhdHRyYXRpdmUgdGhpcyB0aW1lIGJ1dCB3ZSBjYW4gc3RpbGwgdXNlIGBmaWx0ZXIoKWAgYW5kIGBzdW1tYXJpc2UoKWAuIFRoZSByZXN1bHRzIGNhbiBiZSBmb3VuZCBpbiB0aGUgZm9sbG93aW5nIFIgY29kZSBjaHVua3MuIFRoZSBjcml0aWNhbCBpc3N1ZSBpcyB0aGF0IHRoZSBwcmVkaWN0aW9uIGJhbmRzIHNlZW0gdG9vIHdpZGUgZm9yIHRoZSBsb3dlciBidWRnZXRzIGFuZCB0b28gbmFycm93IGZvciB0aGUgaGlnaGVyIGJ1ZGdldHMuIFRoZSBiYW5kcyBhcmUgbm90IGNvbnNpc3RlbnQgZm9yIGRpZmZlcmVudCBidWRnZXRzIChgeGApIGFuZCB0aGVyZWZvcmUgdmlvbGF0ZSB0aGUgcG9pbnR3aXNlIGNvbmRpdGlvbi4gCgogICAgYGBge3J9CiAgICBsbS55b3V0dWJlLmZpdC5wcmVkIHw+IGZpbHRlcih5b3V0dWJlPjIwMCkgfD4gZ2dwbG90KGFlcyh4ID0geW91dHViZSAseT1zYWxlcykpICsKICAgICAgICBnZW9tX3BvaW50KCkgKwogICAgICAgIGdlb21fcmliYm9uKGFlcyh5bWluID0gbHdyLCB5bWF4ID0gdXByKSwgZmlsbCA9ICJncmV5NzAiLCBhbHBoYT0wLjUpICsKICAgICAgICBnZW9tX3Ntb290aChtZXRob2Q9J2xtJykKICAgICAgICBsbS55b3V0dWJlLmZpdC5wcmVkIHw+IGZpbHRlcih5b3V0dWJlPDIwMCkgfD4gZ2dwbG90KGFlcyh4ID0geW91dHViZSAseT1zYWxlcykpICsKICAgICAgICBnZW9tX3BvaW50KCkgKwogICAgICAgIGdlb21fcmliYm9uKGFlcyh5bWluID0gbHdyLCB5bWF4ID0gdXByKSwgZmlsbCA9ICJncmV5NzAiLCBhbHBoYT0wLjUpICsKICAgICAgICBnZW9tX3Ntb290aChtZXRob2Q9J2xtJykKICAgIGBgYAoKICAgIGBgYHtyfQogICAgbG0ueW91dHViZS5maXQucHJlZCB8PiBmaWx0ZXIoeW91dHViZT4yMDApIHw+IHN1bW1hcmlzZShuKCkpCiAgICBsbS55b3V0dWJlLmZpdC5wcmVkIHw+IGZpbHRlcih5b3V0dWJlPjIwMCkgfD4gZmlsdGVyKHNhbGVzPHVwcikgfD4gZmlsdGVyKHNhbGVzPmx3cikgfD4gc3VtbWFyaXNlKG4oKSkKICAgIGxtLnlvdXR1YmUuZml0LnByZWQgfD4gZmlsdGVyKHlvdXR1YmU8MjAwKSB8PiBzdW1tYXJpc2UobigpKQogICAgbG0ueW91dHViZS5maXQucHJlZCB8PiBmaWx0ZXIoeW91dHViZTwyMDApIHw+IGZpbHRlcihzYWxlczx1cHIpIHw+IGZpbHRlcihzYWxlcz5sd3IpIHw+IHN1bW1hcmlzZShuKCkpCiAgICBgYGAKCiMjIEV4ZXJjaXNlIDI6IE1vZGVsIGRpYWdub3N0aWNzCgpBcyB3ZSBjYW4gc2VlIGZyb20gRXhlcmNpc2UgMSwgdGhlIHByZWRpY3Rpb24gaW50ZXJ2YWwgdGVuZHMgdG8gYmUgdG9vIHdpZGUgd2hlbiBgeW91dHViZWAgaXMgbGFyZ2UgYW5kIHRvbyBuYXJyb3cgd2hlbiBgeW91dHViZWAgaXMgc21hbGwuIEEgc2ltaWxhciBpc3N1ZSBhbHNvIGFyaXNlcyB3aGVuIHdlIHRyeSB0byBwcmVkaWN0IHRoZSBzYWxlcyBnaXZlbiB6ZXJvIGJ1ZGdldCBpbiBFeGVyY2lzZSAzIG9mIExhYiBDMi4gVGhlIHByZWRpY3Rpb24gYXQgemVybyBidWRnZXQgbWF5IHNpZ25pZmljYW50bHkgb3ZlcmVzdGltYXRlIHRoZSBhY3R1YWwgc2FsZXMuCgpUaGVzZSBpc3N1ZXMgYWxsdWRlIHRvIGEgY3JpdGljYWwgcG9pbnQ6IGRvZXMgb3VyIGxpbmVhciBtb2RlbCBwcm92aWRlIGEgZ29vZCBmaXQgdG8gb3VyIGRhdGE/CgpPZiBjb3Vyc2UsIHdlIGNhbiBhbHdheXMgZ2V0IHNvbWUgY2x1ZXMgZnJvbSB0aGUgc2NhdHRlciBwbG90IGFuZCBzbW9vdGhlZCBjdXJ2ZSBmb3IgYHNhbGVzYCBhZ2FpbnN0IGB5b3V0dWJlYC4gV2UgY2FuIGV2ZW4gY29uY2x1ZGUgdGhhdCB0aGUgZml0dGVkIGxpbmUgZmFpbHMgdG8gY2FwdHVyZSBhIHNpZ25pZmljYW50IHBvcnRpb24gb2YgdW5jZXJ0YWludGllcyBpbiBvdXIgZGF0YS4gU28gYSBsaW5lYXIgbW9kZWwgbWF5IG5vdCBiZSBhbiBhZGVxdWF0ZSBtb2RlbCBmb3IgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGBzYWxlc2AgYW5kIGB5b3V0dWJlYC4KClRoZSBuZXh0IHF1ZXN0aW9uIGlzIGhvdyB0byBpZGVudGlmeSBhbmQgcXVhbnRpZnkgdGhlIGluYWRlcXVhY3kgb2Ygb3VyIGxpbmVhciBtb2RlbD8gCgpUaGUgdmlzdWFsaXNhdGlvbiBvZiBtb2RlbCBpZGVudGlmaWVzIHNvbWUgaWxsLXBvc2VkIHBhdHRlcm5zIGluIG91ciBtb2RlbCBidXQgaXQgZmFpbHMgdG8gaWRlbnRpZnkgdGhlIHJvb3QgY2F1c2Ugb2YgdGhlIHByb2JsZW0uICRSXjIkIGlzIGEgc2ltcGxlIGluZGljYXRvciBidXQgaXQgd29uJ3QgY2FwdHVyZSB0aGUgaXNzdWVzIG9uIHByZWRpY3Rpb24gZGlzY3Vzc2VkIGFib3ZlLiAKCldoYXQgd2UgbmVlZCBhcmUgc29tZSBkaWFnbm9zdGljIHRvb2xzIHRvIGNoZWNrIHRoZSBmaXRuZXNzIG9mIG91ciBsaW5lYXIgbW9kZWwgb24gdGhlIGRhdGEgc2V0IGFuZCBpZGVudGlmeSB0aGUgY3J1eCBpbiBvdXIgZml0dGVkIG1vZGVsLiBIb3dldmVyLCBiZWZvcmUgd2UgY2Fycnkgb3V0IHRoZSBleGFtaW5hdGlvbiBvbiBvdXIgZml0dGVkIGxpbmVhciBtb2RlbCwgd2UgbmVlZCB0byBrbm93IHdoZW4gYSBmaXR0ZWQgbGluZWFyIG1vZGVsIHdpbGwgYmVoYXZlIHdlbGwuIAoKVG8gZW5zdXJlIHRoZSBvdmVyYWxsIGdvb2QgcGVyZm9ybWFuY2Ugb2YgYSBmaXR0ZWQgbGluZWFyIG1vZGVsLCB3ZSBuZWVkIG91ciBkYXRhIHNhdGlzZnkgYSBmZXcgY29uZGl0aW9ucywgaS5lLiBmb3VyIGFzc3VtcHRpb25zIGFzIGZvbGxvd3MgCgogICogKipMKippbmVhcml0eQoKICAgICAgUmVzaWR1YWxzIGRvbid0IGRlcGVuZCBvbiAkeCQuIFRoZSB0cmVuZCBpcyBjb3JyZWN0bHkgbW9kZWxsZWQgYXMgYSBsaW5lLiBBIGxpbmUgd29uJ3QgZml0IGFuIGV4cG9uZW50aWFsIHRyZW5kLiAKCiAgKiAqKkkqKm5kZXBlbmRlbmNlCgogICAgICBSZXNpZHVhbHMgZG9uJ3QgZGVwZW5kIG9uIGVhY2ggb3RoZXIuIElmIHJlc2lkdWFscyBhcmUgZGVwZW5kZW50LCB3ZSBjYW4gZXhwZWN0IHRoYXQgb25lIHJlc2lkdWFsIG1heSBjb250cmlidXRlIHNvbWUgaW5mb3JtYXRpb24gdG8gYW5vdGhlciByZXNpZHVhbCBhbmQgdmljZSB2ZXJzYS4gCgogICogKipOKipvcm1hbGl0eQoKICAgICAgUmVzaWR1YWxzIGFyZSBkaXN0cmlidXRlZCBub3JtYWxseS4gTm9ybWFsaXR5IGVuc3VyZXMgdGhhdCB0aGUgbGVhc3Qgc3F1YXJlIGVzdGltYXRpb24gd2lsbCBjYXRjaCB0aGUgYmVzdCBwb3NzaWJsZSBsaW5lLiAgCgogICogKipFKipxdWFsIHZhcmlhbmNlCgogICAgICBSZXNpZHVhbHMgaGF2ZSBjb25zdGFudCB2YXJpYW5jZS4gVGhlIHZhcmlhdGlvbiBkb2Vzbid0IGNoYW5nZSBhcyB3ZSBtb3ZlIGFsb25nIHRoZSB0cmVuZC4KICAKd2l0aCBMaW5lYXJpdHkgYmVpbmcgdGhlIG1vc3QgaW1wb3J0YW50LiAKCjEuIEZpcnN0bHkgd2UnbGwgYmUgbG9va2luZyBhdCBsaW5lYXJpdHkgYW5kIGVxdWFsIHZhcmlhbmNlLCBib3RoIG9mIHdoaWNoIGNhbiBiZSBhc3Nlc3NlZCB1c2luZyBhIHBsb3Qgb2YgKip0aGUgcmVzaWR1YWxzIHZlcnN1cyB0aGUgZml0dGVkIHZhbHVlKiouCgogICAgSWYgbGluZWFyaXR5IGhvbGRzLCB3ZSdkIGV4cGVjdCBhIHBsb3Qgb2YgcmVzaWR1YWxzIHZzIGZpdHRlZCB2YWx1ZSB0byBzaG93IG5vIHRyZW5kIC0gdGhlIHBvaW50cyBzaG91bGQgYmUgc2NhdHRlcmVkIGZhaXJseSBjb25zdGFudGx5IGFib3ZlIGFuZCBiZWxvdyB0aGUgbGluZSAtIGluIHBhcnRpY3VsYXIgd2UgZG9uJ3Qgd2FudCB0byBzZWUgYSBjdXJ2ZS4KCiAgICBJZiBlcXVhbCB2YXJpYW5jZSBob2xkcywgd2UnZCBleHBlY3QgdGhlIHNjYXR0ZXIgb2YgcG9pbnRzIGFyb3VuZCB0aGUgdHJlbmQgdG8gYmUgY29uc3RhbnQgYXMgdGhlIGZpdHRlZCB2YWx1ZSBjaGFuZ2VzLiBZb3Ugd2FudCBpdCB0byBiZSByZWxhdGl2ZWx5IGV2ZW4sIGFuZCBpbiBwYXJ0aWN1bGFyIG5vdCBpbmNyZWFzaW5nIGZyb20gbGVmdCB0byByaWdodCAoaS5lLiBub3Qgc3ByZWFkaW5nIG91dCkuCgogICAgV2UgY2FuIGRlbW9uc3RyYXRlIHRoaXMgaWRlYSBieSB1c2luZyB0aGUgc3RvY2hhc3RpYyBzaW11bGF0aW9uLiBBbiBleGFtcGxlIG9mIGEgZ29vZCBwbG90IChsZWZ0KSBhbmQgYmFkIHBsb3QgKHJpZ2h0KSBpcyBzaG93biBiZWxvdyBmb3IgdHdvIGFydGlmaWNpYWwgZGF0YSBzZXRzLCBpLmUuLCBsaW5lYXIgYW5kIGV4cG9uZW50aWFsLiAKCiAgICBgYGB7ciwgIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTMsfQogICAgc2V0LnNlZWQoMjAyMCkKICAgIG4gPC0gNTAKICAgIGFiIDwtIGMoMCwyKQogICAgZGVtbyA8LSB0aWJibGUoeD0oMTpuKS9uLCBlPSBybm9ybShuLCBzZD0wLjEpKSB8PiBtdXRhdGUgKHk9YWJbMV0rYWJbMl0qeCtlKSB8PiAKICAgICAgbXV0YXRlICh5Mj1leHAoYWJbMV0rYWJbMl0qeCtlKSkKICAgIGRlbW8gfD4gc2VsZWN0KHgsIHksIHkyKSB8PiBnYXRoZXIoa2V5ID0gIkdyb3VwIiwgdmFsdWUgPSAiWSIsIC14KSB8PgogICAgICBnZ3Bsb3QoYWVzKHg9eCx5PVksY29sPUdyb3VwKSkgKyBnZW9tX3BvaW50KCkKICAgIGcxIDwtIGxtKHl+eCxkYXRhPWRlbW8pIHw+IGF1Z21lbnQoKSB8PiAKICAgICAgZ2dwbG90KGFlcyh4PS5maXR0ZWQseT0ucmVzaWQpKSArIGdlb21fcG9pbnQoKSArZ2VvbV9zbW9vdGgoKStnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9MCkKICAgIGcyIDwtIGxtKHkyfngsZGF0YT1kZW1vKSB8PiBhdWdtZW50KCkgfD4gCiAgICAgIGdncGxvdChhZXMoeD0uZml0dGVkLHk9LnJlc2lkKSkgKyBnZW9tX3BvaW50KCkgK2dlb21fc21vb3RoKCkrZ2VvbV9obGluZSh5aW50ZXJjZXB0PTApCiAgICBsaWJyYXJ5KHBhdGNod29yaykKICAgIGcxK2cyIAogICAgYGBgCgogICAgKkNvbXBhcmUgdGhlIHNjYXR0ZXIgcGxvdHMgYW5kIHRoZSBwbG90cyBvZiByZXNpZHVhbHMgdmVyc3VzIGZpdHRlZCB2YWx1ZXMuIFRoaW5rIGFib3V0IHRoZSByZWFzb24gd2h5IHdlIGluY2x1ZGUgaG9yaXpvbnRhbCBsaW5lcyBhdCAkeT0wJCBhbmQgc21vb3RoZWQgY3VydmVzIHdpdGggY29uZmlkZW5jZSBiYW5kcyBpbiB0aGUgcmVzaWR1YWwgcGxvdHMuKgoKICAgICoqQW5zd2VyKio6IFRoZSByaWdodCBwYW5lbCBzaG93cyBhIGNsZWFyIGN1cnZhdHVyZSBidXQgdGhlIGxlZnQgcGFuZWwgbG9va3MgcXVpdGUgcmFuZG9tLiBUaGUgYmx1ZSBzbW9vdGhlZCBjdXJ2ZXMgdHJ5IHRvIGV4dHJhY3QgZ2VuZXJhbCBwYXR0ZXJucyBmcm9tIHJlc2lkdWFscyB2cyBmaXRzIHBsb3RzIGFuZCB0aGUgY29uZmlkZW5jZSBiYW5kcyBjYW4gaGVscCB0byB0ZWxsIGlmIHRoZSBnZW5lcmFsIHRyZW5kIGRpZmZlcnMgYSBsb3QgZnJvbSB0aGUgYmFzZWxpbmUgKCR5PTAkKS4gCgogICAgWW91IGNhbiBzZWUgbW9yZSBleGFtcGxlcyB1c2luZyB0aGUgaW50ZXJhY3RpdmUgZm91bmQgaGVyZToKIAogICAgaHR0cHM6Ly9zaGlueS5tYXNzZXkuYWMubnovamNtYXJzaGEvbGluZWFyaXR5CgogICAgKllvdSBjYW4gb2YgY291cnNlIG1vZGlmeSBzb21lIHBhcmFtZXRlcnMgaW4gdGhlIGFib3ZlIFIgY29kZSBjaHVuayBhbmQgZXZlbiBjaGFuZ2UgdGhlIGV4cG9uZW50aWFsIGZ1bmN0aW9uIHRvIHNvbWUgb3RoZXIgZnVuY3Rpb25zLiBBIHBvc3NpYmxlIGNob2ljZSBpcyB0byBjaGFuZ2UgYGFiIDwtIGMoMCwyKWAgdG8gYGFiIDwtIGMoMCwwLjUpYC4qCgogICAgYGBge3IsICBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD0zLH0KICAgIHNldC5zZWVkKDIwMjApCiAgICBuIDwtIDUwCiAgICBhYiA8LSBjKDAsMC41KQogICAgZGVtbyA8LSB0aWJibGUoeD0oMTpuKS9uLCBlPSBybm9ybShuLCBzZD0wLjEpKSB8PiBtdXRhdGUgKHk9YWJbMV0rYWJbMl0qeCtlKSB8PiAKICAgICAgbXV0YXRlICh5Mj1leHAoYWJbMV0rYWJbMl0qeCtlKSkKICAgIGRlbW8gfD4gc2VsZWN0KHgsIHksIHkyKSB8PiBnYXRoZXIoa2V5ID0gIkdyb3VwIiwgdmFsdWUgPSAiWSIsIC14KSB8PgogICAgICBnZ3Bsb3QoYWVzKHg9eCx5PVksY29sPUdyb3VwKSkgKyBnZW9tX3BvaW50KCkKICAgIGcxIDwtIGxtKHl+eCxkYXRhPWRlbW8pIHw+IGF1Z21lbnQoKSB8PiAKICAgICAgZ2dwbG90KGFlcyh4PS5maXR0ZWQseT0ucmVzaWQpKSArIGdlb21fcG9pbnQoKSArZ2VvbV9zbW9vdGgoKStnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9MCkKICAgIGcyIDwtIGxtKHkyfngsZGF0YT1kZW1vKSB8PiBhdWdtZW50KCkgfD4gCiAgICAgIGdncGxvdChhZXMoeD0uZml0dGVkLHk9LnJlc2lkKSkgKyBnZW9tX3BvaW50KCkgK2dlb21fc21vb3RoKCkrZ2VvbV9obGluZSh5aW50ZXJjZXB0PTApCiAgICBsaWJyYXJ5KHBhdGNod29yaykKICAgIGcxK2cyIAogICAgYGBgCgogICAgKipBbnN3ZXIqKjogVGhpcyBjYXNlIGlzIGEgbGl0dGxlIHRyaWNreSBhcyB0aGUgcmVzaWR1YWxzIHZzIGZpdHMgcGxvdCBkb2VzIG5vdCByZXZlYWwgYW55IGlzc3VlIGluIHRoZSBleHBvbmVudGlhbCBtb2RlbC4gV2h5PyBUaGlzIGlzIGJlY2F1c2UgdGhlIGV4cG9uZW50aWFsIGZ1bmN0aW9uICRcZXhwKHgpJCBjYW4gYmUgd2VsbCBhcHByb3hpbWF0ZWQgYnkgYSBsaW5lYXIgZnVuY3Rpb24gJDEreCQgaWYgJHgkIGlzIHNtYWxsLiBUaGlzIGZhY3QgYWN0dWFsbHkgcHJvdmlkZXMgYWRkaXRpb25hbCB3YXJyYW50aWVzIG9uIHVzaW5nIGxpbmVhciBtb2RlbHMgaW4gc2NpZW50aWZpYyByZXNlYXJjaC4gVGhvdWdoIG1hbnkgbmF0dXJhbCBwaGVub21lbmEgbWF5IGZvbGxvdyBzb21lIG5vbmxpbmVhciBsYXdzLCB0aG9zZSBjb21wbGljYXRlZCBub25saW5lYXIgZnVuY3Rpb25zIGNhbiBiZSBhcHByb3hpbWF0ZWQgYnkgb3VyIGxpbmVhciBtb2RlbHMgdW5kZXIgY2VydGFpbiBjb25kaXRpb25zIQoKMi4gTGV0J3Mgc2VlIGhvdyB3ZWxsIG91ciBtb2RlbCBmb3Igc2FsZXMgZG9lcyBieSBwcm9kdWNpbmcgdGhlIGRpYWdub3N0aWMgcGxvdCBmb3IgdGhlIGxpbmVhciBtb2RlbCB5b3UgZml0IGFib3ZlIHVzaW5nIHRoZSBmb2xsb3dpbmcuIAogICAgCiAgICBgYGB7cn0KICAgIGxtLnlvdXR1YmUuZml0IHw+IGdncGxvdChhZXMoeD0uZml0dGVkLHk9LnJlc2lkKSkgKwogICAgICBnZW9tX3BvaW50KCkrZ2VvbV9zbW9vdGgoKStnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9MCkKICAgIGBgYAoKICAgICpUYWtlIGEgZ29vZCBsb29rIGF0IHRoZSBwbG90LiBEbyB5b3UgdGhpbmsgbGluZWFyaXR5IGFuZCBlcXVhbCB2YXJpYW5jZSBob2xkPyBBZGQgc29tZSBub3RlcyBhYm91dCBlYWNoIGFzc3VtcHRpb24gdG8geW91ciBub3RlYm9vay4qCiAgICAKICAgICoqQW5zd2VyKio6IExpbmVhcml0eSBsb29rcyBvaywgdGhvdWdoIHdlIHN0aWxsIHdvcnJ5IGFib3V0IHRoZSBsZWZ0IGVuZCBjb3JyZXNwb25kaW5nIHRvIHNtYWxsIGJ1ZGdldHMuIFRoZSBlcXVhbCB2YXJpYW5jZSBpcyBjZXJ0YWlubHkgdmlvbGF0ZWQhCiAgICAKICAgIAozLiBJbiBhZGRpdGlvbiB0byB0aGUgcmVzaWR1YWxzIHZzIGZpdHMgcGxvdCwgd2UgaGF2ZSBhbm90aGVyIHRvb2wsIGkuZS4gKip0aGUgc2NhbGUtbG9jYXRpb24gcGxvdCoqLCB0byBjaGVjayBlcXVhbCB2YXJpYW5jZS4gVGhlIHBsb3QgY2FuIGJlIGdlbmVyYXRlZCBieSB1c2luZyB0aGUgZm9sbG93aW5nIFIgY29kZSBjaHVuazoKICAgIAogICAgYGBge3J9CiAgICBsbS55b3V0dWJlLmZpdCB8PiBtdXRhdGUoLnJvb3QuYWJzLnN0ZC5yZXNpZD1zcXJ0KGFicyguc3RkLnJlc2lkKSkpIHw+CiAgICAgIGdncGxvdChhZXMoeD0uZml0dGVkLHk9LnJvb3QuYWJzLnN0ZC5yZXNpZCkpICsgZ2VvbV9wb2ludCgpK2dlb21fc21vb3RoKCkrZ2VvbV9obGluZSh5aW50ZXJjZXB0PTApCiAgICBgYGAKICAgIAogICAgU2ltaWxhciB0byB0aGUgcmVzaWR1YWwgdmVyc3VzIGZpdHMgcGxvdCwgdGhlIHNjYWxlLUxvY2F0aW9uIHBsb3Qgc2hvd3Mgd2hldGhlciByZXNpZHVhbHMgYXJlIHNwcmVhZCBlcXVhbGx5IGFsb25nIHRoZSByYW5nZXMgb2YgaW5wdXQgdmFyaWFibGVzIChwcmVkaWN0b3IpLiBUaGUgYXNzdW1wdGlvbiBvZiBlcXVhbCB2YXJpYW5jZSAoKipob21vc2NlZGFzdGljaXR5KiopIGNvdWxkIGFsc28gYmUgY2hlY2tlZCB3aXRoIHRoaXMgcGxvdC4gSWYgd2Ugc2VlIGEgaG9yaXpvbnRhbCBsaW5lIHdpdGggcmFuZG9tbHkgc3ByZWFkIHBvaW50cywgaXQgbWVhbnMgdGhhdCB0aGUgbW9kZWwgaXMgZ29vZC4KICAgIAogICAgKlRha2UgYSBnb29kIGxvb2sgYXQgdGhlIHBsb3QuIERvIHlvdSB0aGluayBsaW5lYXJpdHkgYW5kIGVxdWFsIHZhcmlhbmNlIGhvbGQ/IEFkZCBzb21lIG5vdGVzIGFib3V0IGVhY2ggYXNzdW1wdGlvbiB0byB5b3VyIG5vdGVib29rLioKICAgIAogICAgKipBbnN3ZXIqKjogQ2VydGFpbmx5LCBlcXVhbCB2YXJpYW5jZSBmYWlscyB0byBob2xkLiBCdXQgdGhlIGxpbmVhcml0eSBjYW4gbm90IGJlIHZlcmlmaWVkIGJ5IHRoZSBzY2FsZS1sb2NhdGlvbiBwbG90LiAKICAgIAogICAgSW4gdGhlIHNjYWxlLWxvY2F0aW9uIHBsb3Qgd2Ugc3RpbGwgdXNlIHRoZSBmaXR0ZWQgdmFsdWVzIGFzICR4JCBjb29yZGluYXRlcy4gQnV0IGFub3RoZXIgcXVhbnRpdHkgLSB0aGUgc3F1YXJlIHJvb3Qgb2YgYWJzb2x1dGUgdmFsdWUgb2YgKipzdGFuZGFyZGlzZWQgcmVzaWR1YWxzKiotIGlzIHVzZWQgYXMgJHkkIGNvb3JkaW5hdGVzLiBTdGFuZGFyZGlzZWQgcmVzaWR1YWxzIGFyZSBvYnRhaW5lZCBieSBkaXZpZGluZyB0aGUgcmVzaWR1YWxzIGJ5IHRoZSBzdGFuZGFyZCBlcnJvciBvZiByZXNpZHVhbHMgYXMgJHNfaT1lX2kvc2Rfe3Jlc30kLiBBbiBhZHZhbnRhbmdlIG9mIHN0YW5kYXJkaXNlZCByZXNpZHVhbHMgaXMgdGhhdCBib3RoIHRoZSB2YXJpYW5jZSBhbmQgc3RhbmRhcmQgZXJyb3IgYXJlIGp1c3QgMSBmb3IgdGhpcyBzZXF1ZW5jZS4gCiAgICAKICAgIFRoZSBzdGFuZGFyZGlzZWQgcmVzaWR1YWxzIGNhbiBiZSByZWFkaWx5IG9idGFpbmVkIGZyb20gdGhlIGZpdHRlZCBtb2RlbCBieSB0aGUgUiBmdW5jdGlvbiBgcnN0YW5kYXJkKClgLiBPdGhlcndpc2UsIGlmIHlvdSBoYXZlIGNhcmVmdWxseSByZWFkIHRoZSB0aWJibGUgZnJvbSBgYXVnbWVudCgpYCBhbmQgdGhlIFIgY29kZSBjaHVuayBmb3IgdGhlIHNjYWxlLWxvY2F0aW9uIHBsb3QsIHlvdSBzaG91bGQgaGF2ZSBmb3VuZCB0aGUgc3RhbmRhcmRpc2VkIHJlc2lkdWFscyBgLnN0ZC5yZXNpZGAgcmlnaHQgZm9sbG93aW5nIGAucmVzaWRgLgogICAgCiAgICBUaGUgb3BlcmF0aW9uIG9mIGRpdmlkaW5nIGEgc2VxdWVuY2UgYnkgaXRzIHN0YW5kYXJkIGVycm9yIGlzIGNhbGxlZCAqKnN0YW5kYXJkaXNhdGlvbioqIGluIHN0YXRpc3RpY3MuIEl0IGlzIGZyZXF1ZW50bHkgdXNlZCBpbiBzdGF0aXN0aWNzIGFzIHRoZSBkYXRhIG9uIGRpZmZlcmVudCBzY2FsZXMgY2FuIGJlIGhhbmRsZSB3aXRoIHNpbWlsYXIgbWVhc3VyZXMgYWZ0ZXIgdGhlIHN0YW5kYXJkaXNhdG9uLiAKICAgIAogICAgKipPcHRpb25hbCBDaGFsbGVuZ2U6IE1vZGlmeSB0aGUgUiBjb2RlIGNodW5rIGluIFN0ZXAgMSBhbmQgcGVyZm9ybSBhIHNpbXVsYXRpb24gc3R1ZHkgb24gdGhlIHNjYWxlLWxvY2F0aW9uIHBsb3QuKioKICAgIAogICAgKipBbnN3ZXIqKjogVGhlIGZvbGxvd2luZyBSIGNvZGUgY2h1bmsgY2FuIHByb2R1Y2UgdGhlIHNpbXVsYXRpb24gc3R1ZHkuIFdlIGNhbiBub3RpY2UgdGhhdCB0aGUgc2NhbGUtbG9jYXRpb24gcGxvdCBpcyBub3QgcmVhbGx5IGEgZ3JlYXQgdG9vbCBmb3IgZGlhZ25vc3RpY3MuIAogICAgCiAgICBgYGB7ciwgIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTMsfQogICAgc2V0LnNlZWQoMjAyMCkKICAgIG4gPC0gMTAwCiAgICBhYiA8LSBjKDAsMikKICAgIGRlbW8gPC0gdGliYmxlKHg9KDE6bikvbiwgZT0gcm5vcm0obiwgc2Q9MC4yNSkpIHw+IG11dGF0ZSAoeT1hYlsxXSthYlsyXSp4K2UpIHw+IAogICAgICBtdXRhdGUgKHkyPWV4cChhYlsxXSthYlsyXSp4K2UpKQogICAgZGVtbyB8PiBzZWxlY3QoeCwgeSwgeTIpIHw+IGdhdGhlcihrZXkgPSAiR3JvdXAiLCB2YWx1ZSA9ICJZIiwgLXgpIHw+CiAgICAgIGdncGxvdChhZXMoeD14LHk9WSxjb2w9R3JvdXApKSArIGdlb21fcG9pbnQoKQogICAgZzEgPC0gbG0oeX54LGRhdGE9ZGVtbykgfD4gYXVnbWVudCgpIHw+IG11dGF0ZSgucm9vdC5hYnMuc3RkLnJlc2lkPXNxcnQoYWJzKC5zdGQucmVzaWQpKSkgfD4KICAgICAgZ2dwbG90KGFlcyh4PS5maXR0ZWQseT0ucm9vdC5hYnMuc3RkLnJlc2lkKSkgICsgZ2VvbV9wb2ludCgpICtnZW9tX3Ntb290aCgpK2dlb21faGxpbmUoeWludGVyY2VwdD0wKQogICAgZzIgPC0gbG0oeTJ+eCxkYXRhPWRlbW8pIHw+IGF1Z21lbnQoKSB8PiBtdXRhdGUoLnJvb3QuYWJzLnN0ZC5yZXNpZD1zcXJ0KGFicyguc3RkLnJlc2lkKSkpIHw+CiAgICAgIGdncGxvdChhZXMoeD0uZml0dGVkLHk9LnJvb3QuYWJzLnN0ZC5yZXNpZCkpICArIGdlb21fcG9pbnQoKSArZ2VvbV9zbW9vdGgoKStnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9MCkKICAgIGxpYnJhcnkocGF0Y2h3b3JrKQogICAgZzErZzIgCiAgICBgYGAKCgo0LiBUaGUgcmVzaWR1YWxzIHZzIGZpdHMgcGxvdCBhbmQgc2NhbGUtbG9jYXRpb24gcGxvdCBhcmUgdXN1YWxseSBzdWZmaWNpZW50IGZvciB1cyB0byB0ZWxsIHRoZSBpc3N1ZXMgb24gbGluZWFyaXR5IGFuZCBlcXVhbCB2YXJpYW5jZS4gT3VyIG5leHQgdGFzayBpcyB0byB2ZXJpZnkgdGhlIG5vcm1hbGl0eS4gUmVjYWxsIHRoZSBzaW11bGF0aW9uIHN0dWR5IGluIExhYiAyIHdoZXJlIHdlIG1ha2UgYSBzY2F0dGVyIHBsb3Qgb2YgdGhlIHJlc2lkdWFscyB2ZXJzdXMgdHJ1ZSByYW5kb20gZXJyb3JzIHdoaWNoIGNhbiBiZSBhcHByb3hpbWF0ZWQgYnkgdGhlIGxpbmUgJHk9eCQuIEV2ZW4gaWYgd2UgY2FuJ3Qgb2JzZXJ2ZSB0aGUgdHJ1ZSByYW5kb20gZXJyb3IgaW4gYSByZWFsIHByb2JsZW0sIHdlIGNhbiBzdGlsbCBtYWtlIGEgc2ltaWxhciBwbG90IGNhbGxlZCAqKnF1YW50aWxlLXF1YW50aWxlIHBsb3QqKiwgYWthICoqUS1RIHBsb3QqKiwgdmlhIGBnZW9tX3FxKClgIGFuZCBgZ2VvbV9xcV9saW5lYCBhcyBmb2xsb3dzLiAKCiAgICBgYGB7cn0KICAgIGxtLnlvdXR1YmUuZml0IHw+IGdncGxvdChhZXMoc2FtcGxlPS5zdGQucmVzaWQpKSArIGdlb21fcXEoYWxwaGE9MC4zKSArICBnZW9tX3FxX2xpbmUoY29sb3I9J3JlZCcpCiAgICBgYGAKCiAgICBXZSBhcmUgdXNpbmcgdGhlIHN0YW5kYXJkaXNlZCByZXNpZHVhbHMgYWdhaW4uIGBnZW9tX3FxKClgIGFkZHMgdGhlIGJsYWNrIHBvaW50cyB3aGlsZSBgZ2VvbV9xcV9saW5lKClgIGRpc3BpY3RzIHRoZSByZWQgbGluZS4gSWYgbW9zdCBwb2ludHMgZm9sbG93IHRoZSByZWQgbGluZSwgd2UgY2FuIGZlZWwgZnJlZSB0byBhc3N1bWUgdGhlIG5vcm1hbGl0eSBvZiB0aGUgcmVzaWR1YWxzLiBIb3dldmVyLCBvbmUgbXVzdCBjb25maXJtIHRoZSBsaW5lYXJpdHkgYW5kIGVxdWFsIHZhcmlhbmNlIGZpcnN0IGJlZm9yZSB0aGlua2luZyBhYm91dCB0aGUgbm9ybWFsaXR5LiAKICAgIAogICAgT2YgY291cnNlLCB3ZSBuZWVkIHRvIGtub3cgdGhlIGFwcGVhcmFuY2Ugb2YgYSBnb29kIFEtUSBwbG90IGJlZm9yZSB3ZSBtYWtlIGFueSBzb2xpZCBqdWRnZW1lbnQuIFRoZSBub3JtYWxpdHkgYXNzdW1wdGlvbiBzYXlzIHRoZSByZXNpZHVhbHMgZm9sbG93IG5vcm1hbCBkaXN0cmlidXRpb24gYW5kIHRoZSBzdGFuZGFyZGlzZWQgcmVzaWR1YWxzIGhhcyB6ZXJvIG1lYW4gYW5kIHVuaXQgdmFyaWFuY2UuIFdlIGNhbiB0aGVuIHNpbXVsYXRlIGEgc3RhbmRhcmQgbm9ybWFsIHNhbXBsZSB3aXRoIHplcm8gbWVhbiBhbmQgdW5pdCB2YXJpYW5jZSBhbmQgY2hlY2sgaXRzIFEtUSBwbG90IGFzIGZvbGxvd3MuCiAgICAKICAgIGBgYHtyfQogICAgc2V0LnNlZWQoMjAyMCkKICAgIHRpYmJsZShlPXJub3JtKDEwMCkpIHw+IGdncGxvdChhZXMoc2FtcGxlPWUpKSArIGdlb21fcXEoYWxwaGE9MC4zKSArICBnZW9tX3FxX2xpbmUoY29sb3I9J3JlZCcpCiAgICBgYGAKICAgIAogICAgKlRha2UgYSBnb29kIGxvb2sgYXQgdGhlIGFib3ZlIHR3byBwbG90cy4gRG8geW91IHRoaW5rIHRoZSBub3JtYWxpdHkgaG9sZD8gQWRkIHNvbWUgbm90ZXMgYWJvdXQgdGhlIGFzc3VtcHRpb24gdG8geW91ciBub3RlYm9vay4qCgogICAgKipBbnN3ZXIqKjogRnJvbSB0aGUgUS1RIHBsb3QsIG9uZSBjYW4gYXJndWUgdGhhdCB0aGUgbm9ybWFsaXR5IGhvbGRzLiBIb3dldmVyLCBvbmUgbmVlZCB0byBmaXJzdCBjaGVjayB0aGUgbGluZWFyaXR5IGFuZCBlcXVhbCB2YXJpYW5jZS4gSWYgdGhlIGFib3ZlIHR3byBjb25kaXRpb25zIGZhaWwgdG8gaG9sZCwgaXQgaXMgbm9uLXNlbnNlIHRvIGNoZWNrIHRoZSBub3JtYWxpdHkuIAoKNS4gVGhlIGxhc3QgdGhpbmcgdG8gY2hlY2sgaXMgc2xpZ2h0bHkgZGlmZmVyZW50IGZyb20gdGhlIGFib3ZlIGFzc3VtcHRpb25zLiBXZSB3aWxsIGxvb2sgYXQgdGhlICoqb3V0bGllcnMqKiBpbiBsaW5lYXIgbW9kZWwuIEp1c3QgbGlrZSB3ZSBvYnNlcnZlIHNvbWUgZGlzdGFudCBwb2ludHMgaW4gYSBib3hwbG90IGZvciBvbmUgdmFyaWFibGUsIHdlIGNhbiBoYXZlIHNvbWUgb3V0bGllcnMgZm9yICR4JCBhbmQgJHkkLiBUaGUgdHJpY2sgdGhpbmcgaXMgdGhhdCwgZXZlbiBib3RoICR4JCBhbmQgJHkkIGxvb2sgZ29vZCBpbiB0aGVpciBvd24gYm94cGxvdHMsIHRoZWlyIGpvaW50IGVmZm9ydCBtYXkgcHVzaCB0aGUgcG9pbnQgYXdheSBmcm9tIHRoZSBtYWluIGJvZHkgaW4gYSBzY2F0dGVyIHBsb3QuIEEgcG9zc2libGUgcGl0ZmFsbCBpbiBmaXR0aW5nIGEgbGluZWFyIG1vZGVsIGlzIHRoYXQgdGhlIGxpbmVhciBtb2RlbCBmb3VuZCBieSBsZWFzdCBzcXVhcmUgbWV0aG9kIGlzIG5vdCB2ZXJ5IHJvYnVzdCBhZ2FpbnN0IG91dGxpZXJzLiBPbmx5IGEgZmV3IG91dGxpZXJzIGNhbiBkaXN0b3J0IHRoZSBwb2ludCBlc3RpbWF0ZXMgc2lnbmlmaWNhbnRseS4gU28gaXQgaXMgZXNzZW50aWFsIHRvIHNwb3QgdGhvc2UgYmFkIGd1eXMgaGlkZGVuIGluIG91ciBkYXRhLiAKCiAgICBPZiBjb3Vyc2UsIGV2ZW4gYSBzY2F0dGVyIHBsb3QgY2FuIGhlbHAgdXMgaWRlbnRpZnkgdGhlIG91dGxpZXJzLiBJZiB3ZSB3YW50IHNvbWUgbW9yZSBxdWFudGl0YXRpdmUgbWVhc3VyZW1lbnRzLCB0aGVyZSBpcyBhIHNwZWNpZmljIG1lYXN1cmUgY2FsbGVkICoqQ29vaydzIGRpc3RhbmNlKiosIG9yIGp1c3QgQ29vaydzICREJCwgZm9yIGVhY2ggZGF0YSBwb2ludCB0byBtZWFzdXJlIGl0cyBpbmZsdWVuY2Ugb24gdGhlIHJlZ3Jlc3Npb24gbGluZS4gQSBsYXJnZSBDb29rJ3MgZGlzdGFuY2Ugc3VnZ2VzdHMgdGhhdCB0aGlzIHBvaW50IG1heSBiZSBhbiBvdXRsaWVyIHdoaWNoIGhhcyBhIGJpZyBpbmZsdWVuY2Ugb24gdGhlIHdob2xlIHJlZ3Jlc3Npb24gbGluZS4gCiAgICAKICAgIFRoaXMgcGVpY2Ugb2YgaW5mb3JtYWlvbnQgaXMgcmVhZGlseSBjb2xsZWN0ZWQgYnkgYGF1Z21lbnQoKWAgaW4gdGhlIGNvbHVtbiBgLmNvb2tzZGAuIFRoZSBmb2xsb3dpbmcgUiBjb2RlIGNodW5rIHByb2R1Y2VzIGEgcGxvdCBvZiBDb29rJ3MgZGlzdGFuY2UgYWdhaW5zdCB0aGUgaW5kZXguIAogICAgCiAgICBgYGB7cn0KICAgIGxtLnlvdXR1YmUuZml0IHw+IG11dGF0ZSguaW5kZXg9MTpuKCkpIHw+IGdncGxvdChhZXMoeT0uY29va3NkLHg9LmluZGV4KSkgKyBnZW9tX2NvbCgpIAogICAgYGBgCiAgICAKICAgIFRob3NlIHNwaWtlcyBpbiB0aGUgYWJvdmUgcGxvdCBzdWdnZXN0cyBwb3RlbnRpYWwgb3V0bGllcnMuIEhvd2V2ZXIsIG9uZSBuZWVkIGEgY3V0LW9mZiBwb2ludCB0byBkaXN0aW5ndWlzaCB0aGUgb3V0bGllcnMgZnJvbSB0aG9zZSB0YW1lZCBvYnNlcnZhdGlvbnMuIFVuZm9ydHVuYXRlbHksIHRoZXJlIGlzIG5vIGEgZ29sZGVuIHJ1bGUgdG8gc3BsaXQgdGhlc2UgdHdvIGdyb3VwcyBvZiBwb2ludHMgZm9yIGFyYml0cmFyeSBkYXRhIHNldHMuIFNvbWUgcGVvcGxlIHVzZXMgMC41IG9yIDEgYXMgYSBjdXQtb2ZmIHBvaW50IGJ1dCBvdXIgQ29vaydzIGRpc3RhbmNlcyBoZXJlIGFyZSBtdWNoIHNtYWxsZXIuIFdlIHdpbGwgc2VlIGEgcmVmaW5lZCBkaWFnbm9zdGljIHBsb3QgZm9yIG91dGxpZXIgZGV0ZWN0aW9uIGluIHRoZSBuZXh0IHN0ZXAuIAogICAgCjYuICoqV2Ugd2lsbCBza2lwIGluZGVwZW5kZW5jZSBpbiBMYWIgMyBidXQgcmV2aXNpdCBpdCBpbiBMYWIgNCoqLiBCZWZvcmUgSSBjb25jbHVkZSB0aGlzIGV4ZXJjaXNlLCBJIHdvdWxkIGxpa2UgdG8gZ2l2ZSB5b3UgYSBoYW5keSBzaG9ydGN1dCB0byBwcm9kdWNlIGFsbCBhYm92ZSBwbG90cy4gT25lIGNhbiBlYXNpbHkgZ2VuZXJhdGUgYSBzZXQgb2YgcmVzaWR1YWxzIGRpYWdub3N0aWMgcGxvdHMgdmlhIGBwbG90KClgLCBpLmUgdGhlIGRlZmF1bHQgcGxvdCBmdW5jdGlvbiBpbiBSLCBhcyBmb2xsb3dzLgoKICAgIGBgYHtyfQogICAgcGxvdChsbS55b3V0dWJlKQogICAgYGBgCiAgICAKICAgIFRoZSBsYXN0IG9uZSBvZiBhYm92ZSBwbG90cyBpcyBjYWxsZWQgKip0aGUgcmVzaWR1YWxzIHZlcnN1cyBsZXZlcmFnZSBwbG90KiouIFRoZSAqKmxldmVyYWdlKiogaXMgYW5vdGhlciBtZWFzdXJlIGZvciBpZGVudGlmeWluZyBvdXRsaWVycyBhbmQgeW91IHNoYWxsIG5vdGljZSB0aGF0IHRoZXJlIGFyZSBzZXZlcmFsIHBvaW50cyBiZWluZyBmbGFnZ2VkIGFscmVhZHkuIFRoaXMgcGxvdCB3aWxsIGFsc28gY29udGFpbiBhIGN1dC1vZmYgY3VydmUgaW4gdGhlIHJlZCBkYXNoIGxpbmUgZm9yIENvb2sncyBkaXN0YW5jZXMgaWYgdGhlcmUgYXJlIGFueSBwb2ludHMgd2l0aCBDb29rJ3MgJEQ+MC41JC4gCgogICAgQmVzaWRlcyBvZiB0aGUgY29udmVuaWVuY2UsIGFub3RoZXIgYWR2YW50YWdlIG9mIGBwbG90KClgIGlzIHRoYXQgdGhlIGluZGljZXMgb2YgcG90ZW50aWFsIG91dGxpZXJzIHdpbGwgYmUgZmxhZ2dlZCBpbiB0aGUgcGxvdC4gWW91IGNhbiB1c2UgdGhlIGluZm9ybWF0aW9uIGluIHRoZXNlIGRpYWdub3N0aWMgcGxvdHMgdG8gbG9jYXRlIHRoZSB1bnRhbWVkIG91dGxpZXJzIGluIHlvdXIgZGF0YS4gCiAgICAKICAgIEhvd2V2ZXIsIG9uZSBzaGFsbCBub3RpY2UgdGhhdCwgd2UgY2FuJ3QgY3VzdG9taXNlIHRob3NlIHBsb3RzIGJ5IGZvbGxvd2luZyB0aGUgc2FtZSBwcm9jZWR1cmVzIGluIGBnZ3Bsb3QoKWAuIAogICAgCiAgICAqSW4gZmFjdCwgYHBsb3QoKWAgY2FuIHByb2R1Y2Ugc2l4IGRpZmZlcmVudCBkaWFnbm9zdGljIHBsb3RzLiBZb3UgY2FuIG1ha2UgdGhlIGluZGl2aWR1YWwgcGxvdCBieSBzcGVjaWZ5aW5nIGB3aGljaGAgaW4gYHBsb3QoKWAgbGlrZSBgcGxvdChsbS55b3V0dWJlLHdoaWNoPTEpYCBvciBgcGxvdChsbS55b3V0dWJlLHdoaWNoPTQpYC4gSGF2ZSBhIHRyeSB3aXRoIGB3aGljaD0xLDIsMyw0LDUsNmAgIGFuZCBzZWUgd2hpY2ggbnVtYmVyIGdpdmVzIHlvdSB0aGUgZGVzaXJlZCBwbG90LioKCiAgICAqKkFuc3dlcioqOiBKdXN0IG5vdGljZSB0aGF0IHRoZSBsYXN0IG9uZSBpcyBDb29rJ3MgZGlzdGFuY2UgdnMgbGV2ZXJhZ2UgcGxvdCwgYW5vdGhlciB0b29sIGZvciBvdXRsaWVyIGRldGVjdGlvbi4gCiAgICAKICAgIGBgYHtyfQogICAgcGxvdChsbS55b3V0dWJlLHdoaWNoPTEpICMgcmVzaWR1YWxzIHZzIGZpdHMKICAgIHBsb3QobG0ueW91dHViZSx3aGljaD0yKSAjIFEtUSBwbG90CiAgICBwbG90KGxtLnlvdXR1YmUsd2hpY2g9MykgIyBzY2FsZS1sb2NhdGlvbgogICAgcGxvdChsbS55b3V0dWJlLHdoaWNoPTQpICMgQ29vaydzIGRpc3RhbmNlCiAgICBwbG90KGxtLnlvdXR1YmUsd2hpY2g9NSkgIyBSZXNpZHVhbHMgdnMgbGV2ZXJhZ2UKICAgIHBsb3QobG0ueW91dHViZSx3aGljaD02KSAjIENvb2sncyBkaXN0YW5jZSB2cyBsZXZlcmFnZQogICAgYGBgCgojIyBFeGVyY2lzZSAzOiBUcmFuc2Zvcm1pbmcgZm9yIGEgYmV0dGVyIG1vZGVsCgoKV2UgaGF2ZSB0byBhY2tub3dsZWRnZSB0aGF0IHRoZSByZXNpZHVhbHMgcGxvdHMgb2YgYGxtLnlvdXR1YmVgIHN1Z2dlc3QgdGhhdCB0aGUgbGluZWFyIG1vZGVsIGlzIG5vdCBhIGdyZWF0IG9uZSBmb3IgZGVzY3JpYmluZyB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gYHNhbGVzYCBhbmQgYHlvdXR1YmVgLiBBIHRyYW5zZm9ybWF0aW9uIGlzIG9uZSB3YXkgdG8gZGVhbCB3aXRoIHRoZSBub24tbGluZWFyaXR5IGFuZCB1bmVxdWFsIHZhcmlhbmNlIG9mIHRoZSBkYXRhLiBXZSB3aWxsIHRyeSB0aGUgbG9nIHRyYW5zZm9ybWF0aW9uIGluIHRoaXMgZXhlcmNpc2UuIAoKCjEuIEluc3RlYWQgb2YgbW9kZWxsaW5nIGBzYWxlc2AgaW4gdGVybXMgb2YgYHlvdXR1YmVgLCB3ZSBjb3VsZCBpbnN0ZWFkIHRha2UgbG9nIHRyYW5zZm9ybXMgb2YgYHNhbGVzYCBhbmQgYHlvdXR1YmVgIHRvIHNlZSBpZiBpdCBpcyBwb3NzaWJsZSB0byBnZXQgcmlkIG9mIHRoZSBjdXJ2YXR1cmUgaW4gdGhlIHJlbGF0aW9uc2hpcC4KCgogICAgYGBge3J9CiAgICBsbS55b3V0dWJlLmxvZyA8LSBsbShsb2coc2FsZXMpIH4gbG9nKHlvdXR1YmUpLCBkYXRhPW1hcmtldGluZykKICAgIHN1bW1hcnkobG0ueW91dHViZSkKICAgIHN1bW1hcnkobG0ueW91dHViZS5sb2cpCiAgICBgYGAKICAgIAogICAgKkNvbXBhcmUgdGhlIHN1bW1hcnkgb3V0cHV0IG9mIHRoZSB0d28gbW9kZWxzIHlvdSBoYXZlLiBXaGljaCBkbyB5b3UgdGhpbmsgaXMgYmV0dGVyPyBXaHk/KgogICAgCiAgICAqKkFuc3dlcioqOiBNdWx0aXBsZSBSLXNxdWFyZWQgaGFzIGJlZW4gaW1wcm92ZWQgYSBsb3QuIFRoZXJlZm9yZSwgdGhlIGxvZyB0cmFuc2Zvcm1lZCBtb2RlbCBpcyBiZXR0ZXIuIAoKMi4gV2UgYWRkIHRoZSBmb2xsb3dpbmcgY29kZSB0byBwcm9kdWNlIHRoZSByZXNpZHVhbCB2cyBmaXR0ZWQgcGxvdCBmb3IgdGhpcyBuZXcgbW9kZWwuIAoKICAgIGBgYHtyfQogICAgbG0ueW91dHViZS5sb2cuZml0IDwtIGF1Z21lbnQobG0ueW91dHViZS5sb2cpCiAgICBsbS55b3V0dWJlLmxvZy5maXQgfD4gZ2dwbG90KGFlcyh4PS5maXR0ZWQseT0uc3RkLnJlc2lkKSkgKwogICAgICBnZW9tX3BvaW50KCkrZ2VvbV9zbW9vdGgoc3Bhbj0xLjIpK2dlb21faGxpbmUoeWludGVyY2VwdD0wKQogICAgYGBgCiAgICAKICAgICoqV2hhdCB0aGUgaGVsbCBpcyB0aGlzIGN1cnZlIHNtb290aGVkIGJ5IGBsb2Vzc2AgLSB0aGUgd2VpcmRuZXNzIG9mIGl0IGdvaW5nIHRocm91Z2ggdGhlIG91dGxpZXIgaW4gdGhlIGJvdHRvbSBsZWZ0PyoqCiAgICAKICAgICoqQW5zd2VyKio6IGBsb2Vzc2AgbWVhbnMgKipMTyoqY2FsbHkgVyoqRSoqaWdodGVkICoqUyoqY2F0dGVyLXBsb3QgKipTKiptb290aGVyLiBJdCBpcyBhIGdlbmVyYWxpc2F0aW9uIG9mIG91ciBsaW5lYXIgbW9kZWwgdG8gdGhlIG5vbmxpbmVhciBmdW5jdGlvbi4gT25lIHBvdGVudGlhbCBkcmF3YmFjayBvZiBgbG9lc3NgIGlzIHRoYXQgaXQgbWF5IGRlcGVuZCBoZWF2aWx5IG9uIG9uZSAob3IgYSBzbWFsbCBwb3J0aW9uIG9mKSBkYXRhIHBvaW50KHMpIGFzIGl0IHNtb290aHMgdGhlIHNjYXR0ZXItcGxvdCAqKmxvY2FsbHkqKi4gVGhpcyBpcyB0aGUgcm9vdCBjYXVzZSBvZiB0aGUgd2VpcmRuZXNzLiAKICAgIAogICAgVGhlICR5JC1heGlzIGlzIG5vdyB0aGUgc3RhbmRhcmRpc2VkIHJlc2lkdWFscy4gSWYgb25lIGZpdCBhIGxpbmVhciBtb2RlbCB3aXRoIGEgdHJhbnNmb3JtYXRpb24gb24gJHkkLCB0aGUgcmVzaWR1YWxzIHdpbGwgbm90IGJlIHJlcG9ydGVkIGFuZCB0aGUgc3RhbmRhcmRpc2VkIHJlc2lkdWFscyB3aWxsIGJlIHVzZWQgdGhvcm91Z2hseS4gKldoeSBkbyB3ZSBub3QgdXNlIHRoZSByZXNpZHVhbHM/KiAKICAgIAogICAgKkhvdyBpcyB0aGUgYXNzdW1wdGlvbiBvZiBsaW5lYXJpdHkgbm93PyBXaGF0IGFib3V0IGVxdWFsIHZhcmlhbmNlPyBSZW1lbWJlciB0byBhZGQgY29tbWVudHMgdG8geW91ciBub3RlYm9vay4qCiAgICAKICAgICoqQW5zd2VyKio6ICBUaGUgbGluZWFyaXR5IGlzIGdldHRpbmcgYmV0dGVyIGlmIHdlIGlnbm9yZSB0aGUgYm90dG9tIGxlZnQuIEVxdWFsIHZhcmlhbmNlIGRvZXMgbm90IGhvbGQgYW55d2F5LCBidXQgdGhlcmUgaXMgYSBzbGlnaHQgaW1wcm92ZW1lbnQuIAogICAgKlByb2R1Y2Ugb3RoZXIgZGlhZ25vc3RpYyBwbG90cyBhbmQgYWRkIHNvbWUgY29tbWVudHMgb24gdGhlbS4qCiAgICBgYGB7cn0KICAgICAgcGxvdChsbS55b3V0dWJlLmxvZykKICAgIGBgYAoKICAgICoqQW5zd2VyKio6ICBNb3N0IGlzc3VlcyBhcmUgc3RpbGwgdGhlcmUgYnV0IGdldHRpbmcgYmV0dGVyIHNsaWdodGx5LiBBIHBvdGVudGlhbCBvdXRsaWVyICgxMzEpIGNhbiBiZSBpZGVudGlmaWVkIGZyb20gdGhlIHJlc2lkdWFsIHZzIGxldmVyYWdlIHBsb3QuIAogICAgCiAgICAKNC4gRWFybGllciB3ZSBwcm9kdWNlZCBwcmVkaWN0aW9uIGludGVydmFscyBmb3Igc2FsZXMgd2l0aCBgeW91dHViZWAgYnVkZ2V0IGVxdWFsIHRvIHplcm8gZG9sbGFyLiAqUmUtZG8gdGhlc2UgdXNpbmcgeW91ciB0cmFuc2Zvcm1lZCBtb2RlbC4gUmVtZW1iZXIgdGhhdCB5b3UnbGwgbmVlZCB0byBleHBvbmVudGlhdGUgdGhlIHJlc3VsdGluZyBpbnRlcnZhbHMgdXNpbmcgdGhlIGBleHAoKWAgZnVuY3Rpb24gKFdoeT8pKiAKCiAgICAqSG93IGRvIHRoZXNlIGludGVydmFscyBjb21wYXJlIHdpdGggeW91ciBwcmV2aW91cyBvbmVzPyBBZGQgc29tZSBjb21tZW50cyB0byB5b3VyIG5vdGVib29rIGFib3V0IHRoaXMuKgoKICAgICoqQW5zd2VyKio6IFNvcnJ5IHRoYXQgSSBtYWRlIGEgbWlzdGFrZSBoZXJlIHdoZW4gcHJlcGFyaW5nIHRoaXMgbGFiLiBDbGVhcmx5LCB3ZSBjYW4ndCB0YWtlIHRoZSBsb2dhcml0aG0gb2YgemVyby4gU28gaXQgaXMgbm90IHZpYWJsZSB0byBwcm9kdWNlIHRoZSBwcmVkaWN0aW9uIGJ5IHVzaW5nIGBwcmVkaWN0KClgLiBPbmUgdGhpbmcgd2UgY2FuIG5vdGljZSB0aGF0IHRoZSBsb2cgdHJhbnNmb3JtZWQgbW9kZWwgaXMgYSBwb3dlciBsYXcgbW9kZWwgYXQgdGhlIHJhdyBzY2FsZSBhcyAkc2FsZXM9Y1x0aW1lcyh5b3V0dWJlKV5rJC4gU28gdGhlIHNhbGVzIHdpbGwgYmUgZXhhY3RseSB6ZXJvIGdpdmVuIHplcm8gYnVkZ2V0LiBPZiBjb3Vyc2UsIHdlIG1heSBub3Qgd2FudCB0byB1c2UgYSBtb2RlbCBsaWtlIHRoaXMgc2luY2UgdGhlIHNhbGVzIHNob3VsZCBub3QgYmUgemVybyBldmVuIHdlIGhhdmUgbm90IGFkdmVydGlzaW5nIGJ1ZGdldC4gQSBxdWljayByZW1lZHkgaXMgdG8gdXNlIGEgc2hpZnRlZCBsb2cgdHJhbmZvcm1hdGlvbiBhcyAkeF4qPVxsb2coeCsxKSQgYXMgZm9sbG93cy4gVGhpcyB3aWxsIGZpeCB0aGUgcHJvYmxlbSBjYXVzZWQgYnkgdGhlIGxvZ2FyaXRobSBvZiB6ZXJvLiAKCiAgICBgYGB7cn0KICAgIGxtLnlvdXR1YmUubG9nMSA8LSBsbShsb2coc2FsZXMpIH4gbG9nKHlvdXR1YmUrMSksIGRhdGE9bWFya2V0aW5nKQogICAgbmV3ZGF0YSA8LSBkYXRhLmZyYW1lKHlvdXR1YmU9MCkKICAgIGV4cChwcmVkaWN0KGxtLnlvdXR1YmUubG9nMSxuZXdkYXRhLGludGVydmFsID0gJ3ByZWRpY3Rpb24nKSkKICAgIGBgYAogICAgCiAgICBOb3cgd2UgaGF2ZSBhIG11Y2ggbmFycm93IHByZWRpY3Rpb24gaW50ZXJ2YWwgYWZ0ZXIgY29tcGFyaW5nIGl0IHdpdGggU3RlcCA1IG9mIEV4IDMgaW4gTGFiIDIhIAoKNS4gTm93IGxldCdzIHRyeSB2aXN1YWxpc2luZyB0aGUgc2Vjb25kIGxpbmVhciBtb2RlbC4gWW91IHNob3VsZCBub3RpY2UgdGhhdCB0aGUgJHgkLWF4aXMgaXMgb24gdGhlIG5vcm1hbCBzY2FsZSAoZXZlbiB0aG91Z2ggd2UgYXBwbGllZCBhIGxvZyB0cmFuc2Zvcm1hdGlvbiBpbiB0aGUgbW9kZWwgZm9ybXVsYSkgYnV0IHRoZSAkeSQtYXhpcyBpcyBvbiB0aGUgbG9nIHNjYWxlLiAKCiAgICBgYGB7cn0KICAgIGxpYnJhcnkodmlzcmVnKQogICAgdmlzcmVnKGxtLnlvdXR1YmUubG9nLCBnZz1UUlVFKQogICAgYGBgCgogICAgTm9uZXRoZWxlc3MsIHRoZSBtb2RlbCBmaXQgc2hvdWxkIGJlIGEgYml0IGJldHRlciwgYW5kIHdpbGwgYmUgY3VydmVkLiBOb3RpY2UgdGhhdCB3ZSd2ZSB1c2VkIGEgKipsaW5lYXIqKiBtb2RlbCB0byBmaXQgYSBjdXJ2ZWQgcmVsYXRpb25zaGlwLiBUaGUga2V5IGlzIHRoYXQgdGhlIGxpbmVhcml0eSBvZiB0aGUgbW9kZWwgaXMgaW4gdGVybXMgb2YgdGhlIGNvZWZmaWNpZW50cyAoZWFjaCB0ZXJtIGNhbiBjb250YWluIG9ubHkgb25lICRcYmV0YSQgYXMgYSBtdWx0aXBsaWVyLCBhbmQgdGVybXMgbXVzdCBiZSBhZGRlZCB0b2dldGhlcikgbm90IGluIHRlcm1zIG9mIHRoZSB3YXkgJHkkIGFuZCAkeCQgYXJlIHJlbGF0ZWQuIFlvdSBjYW4gYXBwbHkgYW55IHRyYW5zZm9ybWF0aW9uIHlvdSBsaWtlIHRvICR4JCBhbmQgJHkkIGFzIG5lZWRlZCB0byBmaXQgdGhlIGRhdGEuCgo2LiBZb3UgY2FuIGFsc28gdmlzdWFsaXNlIHRoZSBzZWNvbmQgbGluZWFyIG1vZGVsIG9uIHRoZSBuYXR1cmFsIHNjYWxlIGJ5IGFwcGx5aW5nIGEgdHJhbnNmb3JtYXRpb24gaW4gdGhlIGB2aXNyZWdgIGNvbW1hbmQuIFRyeSB0aGUgZm9sbG93aW5nOgoKICAgIGBgYHtyfQogICAgdmlzcmVnKGxtLnlvdXR1YmUubG9nLCB0cmFucz1leHAsIHBhcnRpYWw9VFJVRSwgZ2cgPVRSVUUpCiAgICBgYGAKCiAgICBUaGUgYHRyYW5zPWV4cGAgdXNlcyBleHBvbmVudGlhdGlvbiB0byB0cmFuc2Zvcm0gdGhlIG91dGNvbWUgdmFyaWFibGUuIFRoZSBgcGFydGlhbD1UUlVFYCBtZWFucyB0aGF0IHJlc2lkdWFscyBkZXZpYXRlZCBmcm9tIHRoZSBsaW5lIChhbmQgdGh1cyBkYXRhIHZhbHVlcykgYXJlIHBsb3R0ZWQgYXMgcG9pbnRzIGFzIHdlbGwuIFlvdSBtYXkgd2FudCB0byBjaGFuZ2UgdGhlIHktYXhpcyBsYWJlbCBieSBhZGRpbmcgYHlsYWI9IkJvZHkgd2VpZ2h0IChrZykiYCB0byB0aGUgYWJvdmUgY29tbWFuZC4KCiAgICAqQWRkIHNvbWUgY29tbWVudHMgdG8geW91ciBub3RlYm9vayBhYm91dCB0aGUgbW9kZWwgZml0IGFuZCBob3cgd2VsbCB5b3UgdGhpbmsgaXQgZG9lcy4gTm90aWNlIHRoYXQgdGhlIGNvbmZpZGVuY2UgYmFuZHMgYXQgdGhlIHJpZ2h0IGVuZCBhcmUgbGFyZ2VyIHRoYW4gdGhvc2UgYXQgdGhlIGxlZnQgZW5kIG9yIGluIHRoZSBtaWRkbGUuIFdoeSBpcyB0aGlzPyoKICAgIAogICAgKipBbnN3ZXIqKjogVGhvdWdoIHRoZSB1bmVxdWFsIHZhcmlhbmNlIGlzIHN0aWxsIHRoZXJlLCB0aGUgdmlzdWFsaXNhdGlvbiBzdWdnZXN0cyBhIG11Y2ggYmV0dGVyIGZpdC4gVGhlIGZpdHRlZCBjdXJ2ZSB0cmFjZXMgdGhlIHRyZW5kIG9mIHNhbGVzIHdlbGwgd2l0aCBwb2ludHMgc2NhdHRlcnJpbmcgYXJvdW5kIHRoZSB0d28gc2lkZXMgb2YgdGhlIGN1cnZlIGV2ZW5seS4gVGhlIHdpZGVyIGNvbmZpZGVuY2UgYmFuZHMgaXMgbWFkZSBieSB0aGUgaW52ZXJzZSB0cmFuc2Zvcm1hdGlvbiwgaS5lLiB0aGUgZXhwb25lbnRpYWwgZnVuY3Rpb24uCgo3LiAqKk9wdGlvbmFsIENoYWxsZW5nZTogVHJ5IHRvIGZpbmQgYSBiZXR0ZXIgdHJhbnNmb3JtIGZvciB0aGlzIGRhdGEgc2V0ISoqIAoKICAgICoqQW5zd2VyKio6IE5vIHN0YW5kYXJkIHNvbHV0aW9uLiBCdXQgSSBhbSBoYXBweSB0byByZXZpZXcgeW91ciB3b3JrIGlmIHlvdSBzZW5kIGl0IHRvIG1lIHZpYSBlbWFpbC4g