Introduction

When the relationship between a predictor and the response appears to be non-linear, we can consider transforming one or both of the variables in hopes that the new variables will exhibit a linear relationship. In this lesson, we will consider two synthetic examples where \(Y\) seems to depend exponentially on \(X\). We will consider the following models for these examples.

Example 1

Load and Explore the Data

We will load the example data from a tab separated file located at the path data/log_ex01.txt.

ex01 <- read.table("data/log_ex01.txt", sep="\t", header=TRUE)
summary(ex01)
       x               y          
 Min.   :2.124   Min.   :  8.333  
 1st Qu.:3.795   1st Qu.: 31.661  
 Median :5.436   Median : 78.320  
 Mean   :5.358   Mean   :133.155  
 3rd Qu.:6.944   3rd Qu.:199.158  
 Max.   :7.979   Max.   :624.952  

We can see from a plot of the data that \(X\) and \(Y\) seem to exhibit a non-linear relationship. We also see that the variance in \(Y\) seems to increase with \(X\).

plot(y ~ x, ex01, pch=21, col="black", bg="cyan")

Model 1: Linear Model

We will begin by creating a fitted linear model of the form: \(\hat{Y} = \hat{\beta}_0 + \hat{\beta}_1 X\)

ex01_m1 <- lm(y ~ x, ex01)
summary(ex01_m1) 

Call:
lm(formula = y ~ x, data = ex01)

Residuals:
    Min      1Q  Median      3Q     Max 
-149.97  -50.98  -10.01   33.54  318.51 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept) -221.754     26.707  -8.303  5.7e-13 ***
x             66.240      4.746  13.958  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 81.69 on 98 degrees of freedom
Multiple R-squared:  0.6653,    Adjusted R-squared:  0.6619 
F-statistic: 194.8 on 1 and 98 DF,  p-value: < 2.2e-16

We plot the fitted line on top of the scatter plot.

plot(y ~ x, ex01, pch=21, col="black", bg="cyan")
abline(ex01_m1$coefficients, col="darkred", lwd=2)

We can see strong evidence of a non-linear trend, as well as heteroskedasticity in the residual plot below.

res1 <- ex01_m1$residuals

plot(res1 ~ ex01$x, pch=21, col="black", bg="cyan", 
     xlab="x", ylab="Residuals", main="Residual Plot")
abline(h=0, col="darkred", lwd=2)

We can assess normality of the residuals using a Shapiro-Wilk test, a histogram, and a QQ-plot.

shapiro.test(res1)

    Shapiro-Wilk normality test

data:  res1
W = 0.92718, p-value = 3.454e-05

The Shapiro-Wilk test strongly suggests non-normality of the residuals. The histogram and QQ-plot reveal that the residuals are right-skewed.

par(mfrow=c(1,2))
hist(res1, col='orchid')
qqnorm(res1)
qqline(res1)
par(mfrow=c(1,1))

Model 2: Exponetial Model

We will now create a fitted model of the form \(\hat{Y} = \hat{\beta}_0 + \hat{\beta}_1 e^X\).

ex01_m2 <- lm(y ~ exp(x), ex01)
summary(ex01_m2) 

Call:
lm(formula = y ~ exp(x), data = ex01)

Residuals:
     Min       1Q   Median       3Q      Max 
-173.688  -22.301   -5.473   21.888  202.380 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept) 32.869993   7.814467   4.206 5.75e-05 ***
exp(x)       0.153758   0.007461  20.607  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 61.14 on 98 degrees of freedom
Multiple R-squared:  0.8125,    Adjusted R-squared:  0.8106 
F-statistic: 424.7 on 1 and 98 DF,  p-value: < 2.2e-16

We plot the fitted line on top of the scatter plot. We see that the fitted line does seem to capture the relationship between X and Y reasonably well.

ptn = seq(from=2,to=8, by=0.1)

plot(y ~ x, ex01, pch=21, col="black", bg="cyan")
lines(ptn, predict(ex01_m2, data.frame(x=ptn)), col="darkred", lwd=2)

The residual plot below shows some evidence of a very slight trend, as well as severe heteroskedasticity.

res2 <- ex01_m2$residuals
plot(res2 ~ ex01$x, pch=21, col="black", bg="cyan", 
     xlab="x", ylab="Residuals", main="Residual Plot")
abline(h=0, col="darkred", lwd=2)

The figure below shows a scatter plot of the data, along with the fitted curve and a 95% prediction band. We can see that the band is too wide in some places, and too narrow in others. This is a direct response of the heteroskedasticity in the residuals.

plot(y ~ x, ex01, pch=21, col="black", bg="cyan")
p2 <- predict(ex01_m2, newdata=data.frame(x=ptn), interval="prediction", level=0.95)
lines(ptn, p2[,"fit"], col="darkred", lwd=2)
lines(ptn, p2[,"lwr"], col="darkorange", lwd=2)
lines(ptn, p2[,"upr"], col="darkorange", lwd=2)

We can assess normality of the residuals using a Shapiro-Wilks test, a histogram, and a QQ-plot.

shapiro.test(res2)

    Shapiro-Wilk normality test

data:  res2
W = 0.89644, p-value = 9.656e-07
par(mfrow=c(1,2))
hist(res2, col='orchid')
qqnorm(res2)
qqline(res2)
par(mfrow=c(1,1))

Model 3: Log-Level Model

We now consider a fitted model of the form: \(\ln(\hat Y) = \hat{\beta}_0 + \hat{\beta}_1 X\).

We will start by plotting our new response, \(\ln(Y)\) against \(X\). This plot exhibits a strong linear relationship.

plot(log(y) ~ x, ex01, pch=21, col="black", bg="cyan")

We will now create our model.

ex01_m3 <- lm(log(y) ~ x, ex01)
summary(ex01_m3) 

Call:
lm(formula = log(y) ~ x, data = ex01)

Residuals:
     Min       1Q   Median       3Q      Max 
-0.81094 -0.23060  0.03466  0.28640  0.67522 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  0.99771    0.11466   8.702 7.91e-14 ***
x            0.62018    0.02037  30.439  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.3507 on 98 degrees of freedom
Multiple R-squared:  0.9043,    Adjusted R-squared:  0.9034 
F-statistic: 926.5 on 1 and 98 DF,  p-value: < 2.2e-16

The residual plot below displays no apparent trend or heteroskedasticity. Note that these are residuals of \(\ln(Y)\), and not \(Y\).

res3 <- ex01_m3$residuals
plot(res3 ~ ex01$x, pch=21, col="black", bg="cyan", 
     xlab="x", ylab="Residuals", main="Residual Plot")

abline(h=0, col="darkred", lwd=2)

We now assess the normality of the residuals with a Shapiro-Wilks test, a histogram, and a QQ-plot.

shapiro.test(res3)

    Shapiro-Wilk normality test

data:  res3
W = 0.98234, p-value = 0.2015
par(mfrow=c(1,2))
hist(res3, col='orchid')
qqnorm(res3)
qqline(res3)
par(mfrow=c(1,1))

These plots suggest that the residuals were likey drawn from a normal (or perhaps very slightly left-skewed) distribution.

We will add the fitted curve and to the scatter plot of \(\ln(Y)\) against \(X\).

plot(log(y) ~ x, ex01, pch=21, col="black", bg="cyan")

p3 <- predict(ex01_m3, newdata=data.frame(x=ptn), interval="prediction", level=0.95)
lines(ptn, p3[,"fit"], col="darkred", lwd=2)
lines(ptn, p3[,"lwr"], col="darkorange", lwd=2)
lines(ptn, p3[,"upr"], col="darkorange", lwd=2)

We can exponentiate the values of \(\ln(Y)\) to transform the vertical axis back in terms of \(Y\). Notice that the fitted curve does seem to provide a good fit for the data, and the transformed prediction band seems to account for the heteroskedasticity.

plot(y ~ x, ex01, pch=21, col="black", bg="cyan")

p3 <- predict(ex01_m3, newdata=data.frame(x=ptn), interval="prediction", level=0.95)
lines(ptn, exp(p3[,"fit"]), col="darkred", lwd=2)
lines(ptn, exp(p3[,"lwr"]), col="darkorange", lwd=2)
lines(ptn, exp(p3[,"upr"]), col="darkorange", lwd=2)

Model 4: Log-Log Model

We now consider a fitted model of the form: \(\ln(\hat Y) = \hat{\beta}_0 + \hat{\beta}_1 \ln(X)\).

We will start by plotting \(\ln(Y)\) against \(\ln(X)\). This plot exhibits a slight non-linearity.

plot(log(y) ~ log(x), ex01, pch=21, col="black", bg="cyan")

We will now create our model.

ex01_m4 <- lm(log(y) ~ log(x), ex01)
summary(ex01_m4)

Call:
lm(formula = log(y) ~ log(x), data = ex01)

Residuals:
     Min       1Q   Median       3Q      Max 
-0.92268 -0.30417  0.01803  0.28857  0.93462 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  -0.4137     0.1889   -2.19   0.0309 *  
log(x)        2.9236     0.1139   25.67   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.408 on 98 degrees of freedom
Multiple R-squared:  0.8705,    Adjusted R-squared:  0.8692 
F-statistic:   659 on 1 and 98 DF,  p-value: < 2.2e-16

The previously observed non-linearity is readily apparent in the residual plot.

res4 <- ex01_m4$residuals
plot(res4 ~ ex01$x, pch=21, col="black", bg="cyan", 
     xlab="x", ylab="Residuals", main="Residual Plot")

abline(h=0, col="darkred", lwd=2)

We assess normality of the residuals using a Shapiro-Wilks test, a histogram, and a QQ-plot.

shapiro.test(res4)

    Shapiro-Wilk normality test

data:  res4
W = 0.98982, p-value = 0.6502
par(mfrow=c(1,2))
hist(res4, col='orchid')
qqnorm(res4)
qqline(res4)
par(mfrow=c(1,1))

Comparison of Models

Of the models we have considered, Model 3 has the highest \(r^2\) value. Note, however, that since the response (\(Y\)) in the first two models is different from the response in the second pair of models (\(\ln(Y)\)), these \(r^2\) values are not directly comparable.

m1_r2 <- summary(ex01_m1)$r.squared
m2_r2 <- summary(ex01_m2)$r.squared
m3_r2 <- summary(ex01_m3)$r.squared
m4_r2 <- summary(ex01_m4)$r.squared

cbind(m1_r2, m2_r2, m3_r2, m4_r2)
         m1_r2     m2_r2     m3_r2     m4_r2
[1,] 0.6653207 0.8124973 0.9043473 0.8705464

If we are careful about performing the appropriate transformations in the log-level and log-log models, we can directly compare the SSE scores. However, keep in mind that these values only tell part of the story. As we see, Model 2 has the lowest SSE score, but as we noted, it has serious issues resulting from heteroskedasticity.

sse1 <- sum((ex01$y - ex01_m1$fitted.values)^2)
sse2 <- sum((ex01$y - ex01_m2$fitted.values)^2)
sse3 <- sum((ex01$y - exp(ex01_m3$fitted.values))^2)
sse4 <- sum((ex01$y - exp(ex01_m4$fitted.values))^2)

cbind(sse1, sse2, sse3, sse4)
         sse1     sse2     sse3     sse4
[1,] 653919.3 366355.6 383890.1 582747.8

We will use Model 3 going forward. It has a high \(r^2\) value, and seems to satisfy the Gauss-Markov assumptions.

Generating Predictions with the Log-Level Model

When using a log-level model to create predictions for \(Y\), we need to first generate a prediction for \(\ln(Y)\), and then exponentiate. Let’s construct a 95% prediction interval for \(Y\) when \(X = 6.5\).

ln_yhat = predict(ex01_m3, data.frame(x=c(6.5)), interval="prediction", level=0.95)
yhat = exp(ln_yhat)
yhat
       fit      lwr     upr
1 152.7606 75.78804 307.909

Example 2

Load and Explore the Data

We will load the example data from a tab separated file located at the path data/log_ex02.txt.

ex02 <- read.table("data/log_ex02.txt", sep="\t", header=TRUE)
summary(ex02)
       x               y          
 Min.   :2.283   Min.   :  46.79  
 1st Qu.:3.767   1st Qu.: 381.51  
 Median :4.985   Median : 953.84  
 Mean   :5.085   Mean   :1862.38  
 3rd Qu.:6.416   3rd Qu.:2888.49  
 Max.   :7.918   Max.   :7982.39  

We can see from a plot of the data that \(X\) and \(Y\) seem to exhibit a non-linear relationship. We also see that the variance in \(Y\) seems to increase with \(X\).

plot(y ~ x, ex02, pch=21, col="black", bg="cyan")

Model 1: Linear Model

We will begin by creating a fitted linear model of the form: \(\hat{Y} = \hat{\beta}_0 + \hat{\beta}_1 X\)

ex02_m1 <- lm(y ~ x, ex02)
summary(ex02_m1) 

Call:
lm(formula = y ~ x, data = ex02)

Residuals:
    Min      1Q  Median      3Q     Max 
-2189.4  -671.4  -170.1   516.8  3842.2 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept) -3286.97     351.15  -9.361 2.96e-15 ***
x            1012.61      65.69  15.416  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 1083 on 98 degrees of freedom
Multiple R-squared:  0.708, Adjusted R-squared:  0.705 
F-statistic: 237.6 on 1 and 98 DF,  p-value: < 2.2e-16

We plot the fitted line on top of the scatter plot.

plot(y ~ x, ex02, pch=21, col="black", bg="cyan")
abline(ex02_m1$coefficients, col="darkred", lwd=2)

We can see strong evidence of a non-linear trend, as well as heteroskedasticity in the residual plot below.

res1 <- ex02_m1$residuals
plot(res1 ~ ex02$x, pch=21, col="black", bg="cyan", 
     xlab="x", ylab="Residuals", main="Residual Plot")

abline(h=0, col="darkred", lwd=2)

We can assess normality of the residuals using a Shapiro-Wilk test, a histogram, and a QQ-plot.

shapiro.test(res1)

    Shapiro-Wilk normality test

data:  res1
W = 0.92902, p-value = 4.378e-05

The Shapiro-Wilk test strongly suggests non-normality of the residuals. The histogram and QQ-plot reveal that the residuals are right-skewed.


par(mfrow=c(1,2))
hist(res1, col='orchid')
qqnorm(res1)
qqline(res1)
par(mfrow=c(1,1))

Model 2: Log-Level Model

Noting the apparent heteroskedasticity in the model, we will now consider a fitted model of the form: \(\ln(\hat{Y}) = \hat{\beta}_0 + \hat{\beta}_1 X\).

We will start by plotting the response \(\ln(Y)\) against \(X\). This plot exhibits a somewhat non-linear relationship.

plot(log(y) ~ x, ex02, pch=21, col="black", bg="cyan")

A summary of the log-level model is shown below.

ex02_m2 <- lm(log(y) ~ x, ex02)
summary(ex02_m2) 

Call:
lm(formula = log(y) ~ x, data = ex02)

Residuals:
     Min       1Q   Median       3Q      Max 
-1.00249 -0.26117  0.03103  0.26751  0.99605 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)   3.1290     0.1465   21.36   <2e-16 ***
x             0.7335     0.0274   26.77   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.4517 on 98 degrees of freedom
Multiple R-squared:  0.8797,    Adjusted R-squared:  0.8785 
F-statistic: 716.6 on 1 and 98 DF,  p-value: < 2.2e-16

Let’s consider the residual plot. Note that these are residuals of \(\ln(Y)\), and not \(Y\).

The previously observed non-linearity is apparent in the residual plot.

res2 <- ex02_m2$residuals

plot(res2 ~ ex02$x, pch=21, col="black", bg="cyan", 
     xlab="x", ylab="Residuals", main="Residual Plot")

abline(h=0, col="darkred", lwd=2)

We now assess the normality of the residuals with a histogram and a QQ-plot.

shapiro.test(res2)

    Shapiro-Wilk normality test

data:  res2
W = 0.9872, p-value = 0.4512

The figure below shows a scatter plot of \(\ln(Y)\) against \(X\). It also displays the fitted curve, as well as a 95% prediction band.

We can exponentiate the values of \(\ln(Y)\) to transform the vertical axis back in terms of \(Y\).

plot(y ~ x, ex02, pch=21, col="black", bg="cyan")

p2 <- predict(ex02_m2, newdata=data.frame(x=ptn), interval="prediction", level=0.95)
lines(ptn, exp(p2[,"fit"]), col="darkred", lwd=2)
lines(ptn, exp(p2[,"lwr"]), col="darkorange", lwd=2)
lines(ptn, exp(p2[,"upr"]), col="darkorange", lwd=2)

Model 3: Log-Log Model

We now consider a fitted model of the form: \(\ln(\hat Y) = \hat{\beta}_0 + \hat{\beta}_1 \ln(X)\).

We start by plotting \(\ln(Y)\) against \(\ln(X)\). This plot exhibits a strong linear relationship.

plot(log(y) ~ log(x), ex02, pch=21, col="black", bg="cyan")

A summary of the fitted model is shown below.

ex02_m3 <- lm(log(y) ~ log(x), ex02)
summary(ex02_m3) 

Call:
lm(formula = log(y) ~ log(x), data = ex02)

Residuals:
     Min       1Q   Median       3Q      Max 
-0.96888 -0.24472 -0.01224  0.23851  0.91477 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)   1.2986     0.1855   7.001  3.2e-10 ***
log(x)        3.5432     0.1154  30.699  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.3997 on 98 degrees of freedom
Multiple R-squared:  0.9058,    Adjusted R-squared:  0.9048 
F-statistic: 942.4 on 1 and 98 DF,  p-value: < 2.2e-16

The residual plot below displays no apparent trend or heteroskedasticity.

res3 <- ex02_m3$residuals

plot(res3 ~ ex02$x, pch=21, col="black", bg="cyan", 
     xlab="x", ylab="Residuals", main="Residual Plot")

abline(h=0, col="darkred", lwd=2)

There is no apparent heteroskedasticity or trend in this residual plot.

We now assess the normality of the residuals.

shapiro.test(res3)

    Shapiro-Wilk normality test

data:  res3
W = 0.99368, p-value = 0.9253
par(mfrow=c(1,2))
hist(res3, col='orchid')
qqnorm(res3)
qqline(res3)
par(mfrow=c(1,1))

We will add the fitted curve and to the scatter plot of \(\ln(Y)\) against \(X\).

plot(log(y) ~ log(x), ex02, pch=21, col="black", bg="cyan")

p3 <- predict(ex02_m3, newdata=data.frame(x=ptn), interval="prediction", level=0.95)
lines(log(ptn), p3[,"fit"], col="darkred", lwd=2)
lines(log(ptn), p3[,"lwr"], col="darkorange", lwd=2)
lines(log(ptn), p3[,"upr"], col="darkorange", lwd=2)

We can exponentiate the values of \(ln(Y)\) to transform back to be in terms of \(Y\).

plot(y ~ x, ex02, pch=21, col="black", bg="cyan")

p3 <- predict(ex02_m3, newdata=data.frame(x=ptn), interval="prediction", level=0.95)
lines(ptn, exp(p3[,"fit"]), col="darkred", lwd=2)
lines(ptn, exp(p3[,"lwr"]), col="darkorange", lwd=2)
lines(ptn, exp(p3[,"upr"]), col="darkorange", lwd=2)

Generating Predictions with the Log-Log Model

When using a log-log model to create predictions for \(Y\), we need to first generate a prediction for \(\ln(Y)\), and then exponentiate. Let’s construct a 95% prediction interval for \(Y\) when \(X = 6.5\).

ln_yhat = predict(ex02_m3, data.frame(x=c(6.5)), interval="prediction", level=0.95)
yhat = exp(ln_yhat)
yhat
       fit      lwr      upr
1 2781.552 1249.637 6191.424

Alternate Forms for Transformed Models

Log-Level Model

If we solve for \(Y\) in the log-level model, we see that the following forms for the model are equivalent:

\[\ln(Y) = \beta_0 + \beta_1 X + \varepsilon\]

\[Y = e^{\beta_0} e^{\beta_1 X} e^{\varepsilon}\] Since \(\beta_0\) is a constant, \(e^{\beta_1 X}\) is a positive constant. If we call this constant \(K\), we get the following expression for the log-level model:

\[Y = K e^{\beta_1 X} e^{\varepsilon}\]

Log-Log Model

If we solve for \(Y\) in the log-log model, we see that the following forms for the model are equivalent:

\[\ln(Y) = \beta_0 + \beta_1 \ln(X) + \varepsilon\]

\[Y = e^{\beta_0} X^{\beta_1} e^{\varepsilon}\]

Since \(\beta_0\) is a constant, \(e^{\beta_1 X}\) is a positive constant. If we call this constant \(K\), we get the following expression for the log-level model:

\[Y = K X^{\beta_1} e^{\varepsilon}\]

LS0tDQp0aXRsZTogIkxlc3NvbiAzLjYgLSBMb2dhcml0aG1pYyBUcmFuc2Zvcm1hdGlvbnMiDQphdXRob3I6ICJSb2JiaWUgQmVhbmUiDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6DQogICAgdGhlbWU6IGZsYXRseQ0KICAgIHRvYzogeWVzDQogICAgdG9jX2RlcHRoOiA0DQotLS0NCg0KDQojIyMgKipJbnRyb2R1Y3Rpb24qKg0KDQpXaGVuIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBhIHByZWRpY3RvciBhbmQgdGhlIHJlc3BvbnNlIGFwcGVhcnMgdG8gYmUgbm9uLWxpbmVhciwgd2UgY2FuIGNvbnNpZGVyIHRyYW5zZm9ybWluZyBvbmUgb3IgYm90aCBvZiB0aGUgdmFyaWFibGVzIGluIGhvcGVzIHRoYXQgdGhlIG5ldyB2YXJpYWJsZXMgd2lsbCBleGhpYml0IGEgbGluZWFyIHJlbGF0aW9uc2hpcC4gSW4gdGhpcyBsZXNzb24sIHdlIHdpbGwgY29uc2lkZXIgdHdvIHN5bnRoZXRpYyBleGFtcGxlcyB3aGVyZSAkWSQgc2VlbXMgdG8gZGVwZW5kIGV4cG9uZW50aWFsbHkgb24gJFgkLiBXZSB3aWxsIGNvbnNpZGVyIHRoZSBmb2xsb3dpbmcgbW9kZWxzIGZvciB0aGVzZSBleGFtcGxlcy4NCg0KKiBMaW5lYXIgTW9kZWw6ICRZID0gXGJldGFfMCArIFxiZXRhXzEgWCArIFx2YXJlcHNpbG9uJC4NCg0KKiBFeHBvbmVudGlhbCBNb2RlbDogJFkgPSBcYmV0YV8wICsgXGJldGFfMSBlXlggKyBcdmFyZXBzaWxvbiQuDQoNCiogTG9nLUxldmVsIE1vZGVsOiAkXGxuKFkpID0gXGJldGFfMCArIFxiZXRhXzEgWCArIFx2YXJlcHNpbG9uJA0KDQoqIExvZy1Mb2cgTW9kZWw6ICRcbG4oWSkgPSBcYmV0YV8wICsgXGJldGFfMSBcbG4oWCkgKyBcdmFyZXBzaWxvbiQNCg0KIyMjICoqRXhhbXBsZSAxKioNCg0KIyMjIyAqKkxvYWQgYW5kIEV4cGxvcmUgdGhlIERhdGEqKg0KDQpXZSB3aWxsIGxvYWQgdGhlIGV4YW1wbGUgZGF0YSBmcm9tIGEgdGFiIHNlcGFyYXRlZCBmaWxlIGxvY2F0ZWQgYXQgdGhlIHBhdGggYGRhdGEvbG9nX2V4MDEudHh0YC4gDQoNCmBgYHtyfQ0KZXgwMSA8LSByZWFkLnRhYmxlKCJkYXRhL2xvZ19leDAxLnR4dCIsIHNlcD0iXHQiLCBoZWFkZXI9VFJVRSkNCnN1bW1hcnkoZXgwMSkNCmBgYA0KDQpXZSBjYW4gc2VlIGZyb20gYSBwbG90IG9mIHRoZSBkYXRhIHRoYXQgJFgkIGFuZCAkWSQgc2VlbSB0byBleGhpYml0IGEgbm9uLWxpbmVhciByZWxhdGlvbnNoaXAuIFdlIGFsc28gc2VlIHRoYXQgdGhlIHZhcmlhbmNlIGluICRZJCBzZWVtcyB0byBpbmNyZWFzZSB3aXRoICRYJC4gDQoNCmBgYHtyfQ0KcGxvdCh5IH4geCwgZXgwMSwgcGNoPTIxLCBjb2w9ImJsYWNrIiwgYmc9ImN5YW4iKQ0KYGBgDQoNCiMjIyMgKipNb2RlbCAxOiBMaW5lYXIgTW9kZWwqKg0KDQpXZSB3aWxsIGJlZ2luIGJ5IGNyZWF0aW5nIGEgZml0dGVkIGxpbmVhciBtb2RlbCBvZiB0aGUgZm9ybTogJFxoYXR7WX0gPSBcaGF0e1xiZXRhfV8wICsgXGhhdHtcYmV0YX1fMSBYJCANCg0KYGBge3J9DQpleDAxX20xIDwtIGxtKHkgfiB4LCBleDAxKQ0Kc3VtbWFyeShleDAxX20xKSANCmBgYA0KDQpXZSBwbG90IHRoZSBmaXR0ZWQgbGluZSBvbiB0b3Agb2YgdGhlIHNjYXR0ZXIgcGxvdC4gDQoNCmBgYHtyfQ0KcGxvdCh5IH4geCwgZXgwMSwgcGNoPTIxLCBjb2w9ImJsYWNrIiwgYmc9ImN5YW4iKQ0KYWJsaW5lKGV4MDFfbTEkY29lZmZpY2llbnRzLCBjb2w9ImRhcmtyZWQiLCBsd2Q9MikNCmBgYA0KDQoNCldlIGNhbiBzZWUgc3Ryb25nIGV2aWRlbmNlIG9mIGEgbm9uLWxpbmVhciB0cmVuZCwgYXMgd2VsbCBhcyBoZXRlcm9za2VkYXN0aWNpdHkgaW4gdGhlIHJlc2lkdWFsIHBsb3QgYmVsb3cuIA0KDQpgYGB7cn0NCnJlczEgPC0gZXgwMV9tMSRyZXNpZHVhbHMNCg0KcGxvdChyZXMxIH4gZXgwMSR4LCBwY2g9MjEsIGNvbD0iYmxhY2siLCBiZz0iY3lhbiIsIA0KICAgICB4bGFiPSJ4IiwgeWxhYj0iUmVzaWR1YWxzIiwgbWFpbj0iUmVzaWR1YWwgUGxvdCIpDQphYmxpbmUoaD0wLCBjb2w9ImRhcmtyZWQiLCBsd2Q9MikNCmBgYA0KDQpXZSBjYW4gYXNzZXNzIG5vcm1hbGl0eSBvZiB0aGUgcmVzaWR1YWxzIHVzaW5nIGEgU2hhcGlyby1XaWxrIHRlc3QsIGEgaGlzdG9ncmFtLCBhbmQgYSBRUS1wbG90LiANCg0KYGBge3J9DQpzaGFwaXJvLnRlc3QocmVzMSkNCmBgYA0KDQpUaGUgU2hhcGlyby1XaWxrIHRlc3Qgc3Ryb25nbHkgc3VnZ2VzdHMgbm9uLW5vcm1hbGl0eSBvZiB0aGUgcmVzaWR1YWxzLiBUaGUgaGlzdG9ncmFtIGFuZCBRUS1wbG90IHJldmVhbCB0aGF0IHRoZSByZXNpZHVhbHMgYXJlIHJpZ2h0LXNrZXdlZC4gDQoNCmBgYHtyfQ0KcGFyKG1mcm93PWMoMSwyKSkNCmhpc3QocmVzMSwgY29sPSdvcmNoaWQnKQ0KcXFub3JtKHJlczEpDQpxcWxpbmUocmVzMSkNCnBhcihtZnJvdz1jKDEsMSkpDQpgYGANCg0KIyMjIyAqKk1vZGVsIDI6IEV4cG9uZXRpYWwgTW9kZWwqKg0KDQpXZSB3aWxsIG5vdyBjcmVhdGUgYSBmaXR0ZWQgbW9kZWwgb2YgdGhlIGZvcm0gJFxoYXR7WX0gPSBcaGF0e1xiZXRhfV8wICsgXGhhdHtcYmV0YX1fMSBlXlgkLg0KDQpgYGB7cn0NCmV4MDFfbTIgPC0gbG0oeSB+IGV4cCh4KSwgZXgwMSkNCnN1bW1hcnkoZXgwMV9tMikgDQpgYGANCg0KV2UgcGxvdCB0aGUgZml0dGVkIGxpbmUgb24gdG9wIG9mIHRoZSBzY2F0dGVyIHBsb3QuIFdlIHNlZSB0aGF0IHRoZSBmaXR0ZWQgbGluZSBkb2VzIHNlZW0gdG8gY2FwdHVyZSB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gYFhgIGFuZCBgWWAgcmVhc29uYWJseSB3ZWxsLiANCg0KDQpgYGB7cn0NCnB0biA9IHNlcShmcm9tPTIsdG89OCwgYnk9MC4xKQ0KDQpwbG90KHkgfiB4LCBleDAxLCBwY2g9MjEsIGNvbD0iYmxhY2siLCBiZz0iY3lhbiIpDQpsaW5lcyhwdG4sIHByZWRpY3QoZXgwMV9tMiwgZGF0YS5mcmFtZSh4PXB0bikpLCBjb2w9ImRhcmtyZWQiLCBsd2Q9MikNCmBgYA0KDQpUaGUgcmVzaWR1YWwgcGxvdCBiZWxvdyBzaG93cyBzb21lIGV2aWRlbmNlIG9mIGEgdmVyeSBzbGlnaHQgdHJlbmQsIGFzIHdlbGwgYXMgc2V2ZXJlIGhldGVyb3NrZWRhc3RpY2l0eS4gDQoNCg0KYGBge3J9DQpyZXMyIDwtIGV4MDFfbTIkcmVzaWR1YWxzDQpwbG90KHJlczIgfiBleDAxJHgsIHBjaD0yMSwgY29sPSJibGFjayIsIGJnPSJjeWFuIiwgDQogICAgIHhsYWI9IngiLCB5bGFiPSJSZXNpZHVhbHMiLCBtYWluPSJSZXNpZHVhbCBQbG90IikNCmFibGluZShoPTAsIGNvbD0iZGFya3JlZCIsIGx3ZD0yKQ0KYGBgDQoNClRoZSBmaWd1cmUgYmVsb3cgc2hvd3MgYSBzY2F0dGVyIHBsb3Qgb2YgdGhlIGRhdGEsIGFsb25nIHdpdGggdGhlIGZpdHRlZCBjdXJ2ZSBhbmQgYSA5NSUgcHJlZGljdGlvbiBiYW5kLiBXZSBjYW4gc2VlIHRoYXQgdGhlIGJhbmQgaXMgdG9vIHdpZGUgaW4gc29tZSBwbGFjZXMsIGFuZCB0b28gbmFycm93IGluIG90aGVycy4gVGhpcyBpcyBhIGRpcmVjdCByZXNwb25zZSBvZiB0aGUgaGV0ZXJvc2tlZGFzdGljaXR5IGluIHRoZSByZXNpZHVhbHMuIA0KDQpgYGB7cn0NCnBsb3QoeSB+IHgsIGV4MDEsIHBjaD0yMSwgY29sPSJibGFjayIsIGJnPSJjeWFuIikNCnAyIDwtIHByZWRpY3QoZXgwMV9tMiwgbmV3ZGF0YT1kYXRhLmZyYW1lKHg9cHRuKSwgaW50ZXJ2YWw9InByZWRpY3Rpb24iLCBsZXZlbD0wLjk1KQ0KbGluZXMocHRuLCBwMlssImZpdCJdLCBjb2w9ImRhcmtyZWQiLCBsd2Q9MikNCmxpbmVzKHB0biwgcDJbLCJsd3IiXSwgY29sPSJkYXJrb3JhbmdlIiwgbHdkPTIpDQpsaW5lcyhwdG4sIHAyWywidXByIl0sIGNvbD0iZGFya29yYW5nZSIsIGx3ZD0yKQ0KYGBgDQoNCldlIGNhbiBhc3Nlc3Mgbm9ybWFsaXR5IG9mIHRoZSByZXNpZHVhbHMgdXNpbmcgYSBTaGFwaXJvLVdpbGtzIHRlc3QsIGEgaGlzdG9ncmFtLCBhbmQgYSBRUS1wbG90LiANCg0KYGBge3J9DQpzaGFwaXJvLnRlc3QocmVzMikNCmBgYA0KDQoNCmBgYHtyfQ0KcGFyKG1mcm93PWMoMSwyKSkNCmhpc3QocmVzMiwgY29sPSdvcmNoaWQnKQ0KcXFub3JtKHJlczIpDQpxcWxpbmUocmVzMikNCnBhcihtZnJvdz1jKDEsMSkpDQpgYGANCg0KDQojIyMjICoqTW9kZWwgMzogTG9nLUxldmVsIE1vZGVsKioNCg0KV2Ugbm93IGNvbnNpZGVyIGEgZml0dGVkIG1vZGVsIG9mIHRoZSBmb3JtOiAkXGxuKFxoYXQgWSkgPSBcaGF0e1xiZXRhfV8wICsgXGhhdHtcYmV0YX1fMSBYJC4gDQoNCldlIHdpbGwgc3RhcnQgYnkgcGxvdHRpbmcgb3VyIG5ldyByZXNwb25zZSwgJFxsbihZKSQgYWdhaW5zdCAkWCQuIFRoaXMgcGxvdCBleGhpYml0cyBhIHN0cm9uZyBsaW5lYXIgcmVsYXRpb25zaGlwLiANCg0KYGBge3J9DQpwbG90KGxvZyh5KSB+IHgsIGV4MDEsIHBjaD0yMSwgY29sPSJibGFjayIsIGJnPSJjeWFuIikNCmBgYA0KDQpXZSB3aWxsIG5vdyBjcmVhdGUgb3VyIG1vZGVsLiANCg0KYGBge3J9DQpleDAxX20zIDwtIGxtKGxvZyh5KSB+IHgsIGV4MDEpDQpzdW1tYXJ5KGV4MDFfbTMpIA0KYGBgDQoNClRoZSByZXNpZHVhbCBwbG90IGJlbG93IGRpc3BsYXlzIG5vIGFwcGFyZW50IHRyZW5kIG9yIGhldGVyb3NrZWRhc3RpY2l0eS4gTm90ZSB0aGF0IHRoZXNlIGFyZSByZXNpZHVhbHMgb2YgJFxsbihZKSQsIGFuZCBub3QgJFkkLiANCg0KYGBge3J9DQpyZXMzIDwtIGV4MDFfbTMkcmVzaWR1YWxzDQpwbG90KHJlczMgfiBleDAxJHgsIHBjaD0yMSwgY29sPSJibGFjayIsIGJnPSJjeWFuIiwgDQogICAgIHhsYWI9IngiLCB5bGFiPSJSZXNpZHVhbHMiLCBtYWluPSJSZXNpZHVhbCBQbG90IikNCg0KYWJsaW5lKGg9MCwgY29sPSJkYXJrcmVkIiwgbHdkPTIpDQpgYGANCg0KV2Ugbm93IGFzc2VzcyB0aGUgbm9ybWFsaXR5IG9mIHRoZSByZXNpZHVhbHMgd2l0aCBhIFNoYXBpcm8tV2lsa3MgdGVzdCwgYSBoaXN0b2dyYW0sIGFuZCBhIFFRLXBsb3QuDQoNCmBgYHtyfQ0Kc2hhcGlyby50ZXN0KHJlczMpDQpgYGANCg0KDQpgYGB7cn0NCnBhcihtZnJvdz1jKDEsMikpDQpoaXN0KHJlczMsIGNvbD0nb3JjaGlkJykNCnFxbm9ybShyZXMzKQ0KcXFsaW5lKHJlczMpDQpwYXIobWZyb3c9YygxLDEpKQ0KYGBgDQoNClRoZXNlIHBsb3RzIHN1Z2dlc3QgdGhhdCB0aGUgcmVzaWR1YWxzIHdlcmUgbGlrZXkgZHJhd24gZnJvbSBhIG5vcm1hbCAob3IgcGVyaGFwcyB2ZXJ5IHNsaWdodGx5IGxlZnQtc2tld2VkKSBkaXN0cmlidXRpb24uIA0KIA0KV2Ugd2lsbCBhZGQgdGhlIGZpdHRlZCBjdXJ2ZSBhbmQgIHRvIHRoZSBzY2F0dGVyIHBsb3Qgb2YgJFxsbihZKSQgYWdhaW5zdCAkWCQuIA0KDQoNCmBgYHtyfQ0KcGxvdChsb2coeSkgfiB4LCBleDAxLCBwY2g9MjEsIGNvbD0iYmxhY2siLCBiZz0iY3lhbiIpDQoNCnAzIDwtIHByZWRpY3QoZXgwMV9tMywgbmV3ZGF0YT1kYXRhLmZyYW1lKHg9cHRuKSwgaW50ZXJ2YWw9InByZWRpY3Rpb24iLCBsZXZlbD0wLjk1KQ0KbGluZXMocHRuLCBwM1ssImZpdCJdLCBjb2w9ImRhcmtyZWQiLCBsd2Q9MikNCmxpbmVzKHB0biwgcDNbLCJsd3IiXSwgY29sPSJkYXJrb3JhbmdlIiwgbHdkPTIpDQpsaW5lcyhwdG4sIHAzWywidXByIl0sIGNvbD0iZGFya29yYW5nZSIsIGx3ZD0yKQ0KYGBgDQoNCldlIGNhbiBleHBvbmVudGlhdGUgdGhlIHZhbHVlcyBvZiAkXGxuKFkpJCB0byB0cmFuc2Zvcm0gdGhlIHZlcnRpY2FsIGF4aXMgYmFjayBpbiB0ZXJtcyBvZiAkWSQuIE5vdGljZSB0aGF0IHRoZSBmaXR0ZWQgY3VydmUgZG9lcyBzZWVtIHRvIHByb3ZpZGUgYSBnb29kIGZpdCBmb3IgdGhlIGRhdGEsIGFuZCB0aGUgdHJhbnNmb3JtZWQgcHJlZGljdGlvbiBiYW5kIHNlZW1zIHRvIGFjY291bnQgZm9yIHRoZSBoZXRlcm9za2VkYXN0aWNpdHkuIA0KDQpgYGB7cn0NCnBsb3QoeSB+IHgsIGV4MDEsIHBjaD0yMSwgY29sPSJibGFjayIsIGJnPSJjeWFuIikNCg0KcDMgPC0gcHJlZGljdChleDAxX20zLCBuZXdkYXRhPWRhdGEuZnJhbWUoeD1wdG4pLCBpbnRlcnZhbD0icHJlZGljdGlvbiIsIGxldmVsPTAuOTUpDQpsaW5lcyhwdG4sIGV4cChwM1ssImZpdCJdKSwgY29sPSJkYXJrcmVkIiwgbHdkPTIpDQpsaW5lcyhwdG4sIGV4cChwM1ssImx3ciJdKSwgY29sPSJkYXJrb3JhbmdlIiwgbHdkPTIpDQpsaW5lcyhwdG4sIGV4cChwM1ssInVwciJdKSwgY29sPSJkYXJrb3JhbmdlIiwgbHdkPTIpDQpgYGANCg0KIyMjIyAqKk1vZGVsIDQ6IExvZy1Mb2cgTW9kZWwqKg0KDQpXZSBub3cgY29uc2lkZXIgYSBmaXR0ZWQgbW9kZWwgb2YgdGhlIGZvcm06ICRcbG4oXGhhdCBZKSA9IFxoYXR7XGJldGF9XzAgKyBcaGF0e1xiZXRhfV8xIFxsbihYKSQuIA0KDQpXZSB3aWxsIHN0YXJ0IGJ5IHBsb3R0aW5nICRcbG4oWSkkIGFnYWluc3QgJFxsbihYKSQuIFRoaXMgcGxvdCBleGhpYml0cyBhIHNsaWdodCBub24tbGluZWFyaXR5LiANCg0KYGBge3J9DQpwbG90KGxvZyh5KSB+IGxvZyh4KSwgZXgwMSwgcGNoPTIxLCBjb2w9ImJsYWNrIiwgYmc9ImN5YW4iKQ0KYGBgDQoNCldlIHdpbGwgbm93IGNyZWF0ZSBvdXIgbW9kZWwuIA0KDQpgYGB7cn0NCmV4MDFfbTQgPC0gbG0obG9nKHkpIH4gbG9nKHgpLCBleDAxKQ0Kc3VtbWFyeShleDAxX200KQ0KYGBgDQoNClRoZSBwcmV2aW91c2x5IG9ic2VydmVkIG5vbi1saW5lYXJpdHkgaXMgcmVhZGlseSBhcHBhcmVudCBpbiB0aGUgcmVzaWR1YWwgcGxvdC4gDQoNCmBgYHtyfQ0KcmVzNCA8LSBleDAxX200JHJlc2lkdWFscw0KcGxvdChyZXM0IH4gZXgwMSR4LCBwY2g9MjEsIGNvbD0iYmxhY2siLCBiZz0iY3lhbiIsIA0KICAgICB4bGFiPSJ4IiwgeWxhYj0iUmVzaWR1YWxzIiwgbWFpbj0iUmVzaWR1YWwgUGxvdCIpDQoNCmFibGluZShoPTAsIGNvbD0iZGFya3JlZCIsIGx3ZD0yKQ0KYGBgDQoNCldlIGFzc2VzcyBub3JtYWxpdHkgb2YgdGhlIHJlc2lkdWFscyB1c2luZyBhIFNoYXBpcm8tV2lsa3MgdGVzdCwgYSBoaXN0b2dyYW0sIGFuZCBhIFFRLXBsb3QuDQoNCg0KDQpgYGB7cn0NCnNoYXBpcm8udGVzdChyZXM0KQ0KYGBgDQoNCg0KYGBge3J9DQpwYXIobWZyb3c9YygxLDIpKQ0KaGlzdChyZXM0LCBjb2w9J29yY2hpZCcpDQpxcW5vcm0ocmVzNCkNCnFxbGluZShyZXM0KQ0KcGFyKG1mcm93PWMoMSwxKSkNCmBgYA0KDQojIyMjICoqQ29tcGFyaXNvbiBvZiBNb2RlbHMqKg0KDQpPZiB0aGUgbW9kZWxzIHdlIGhhdmUgY29uc2lkZXJlZCwgTW9kZWwgMyBoYXMgdGhlIGhpZ2hlc3QgJHJeMiQgdmFsdWUuIE5vdGUsIGhvd2V2ZXIsIHRoYXQgc2luY2UgdGhlIHJlc3BvbnNlICgkWSQpIGluIHRoZSBmaXJzdCB0d28gbW9kZWxzIGlzIGRpZmZlcmVudCBmcm9tIHRoZSByZXNwb25zZSBpbiB0aGUgc2Vjb25kIHBhaXIgb2YgbW9kZWxzICgkXGxuKFkpJCksIHRoZXNlICRyXjIkIHZhbHVlcyBhcmUgbm90IGRpcmVjdGx5IGNvbXBhcmFibGUuIA0KDQpgYGB7cn0NCm0xX3IyIDwtIHN1bW1hcnkoZXgwMV9tMSkkci5zcXVhcmVkDQptMl9yMiA8LSBzdW1tYXJ5KGV4MDFfbTIpJHIuc3F1YXJlZA0KbTNfcjIgPC0gc3VtbWFyeShleDAxX20zKSRyLnNxdWFyZWQNCm00X3IyIDwtIHN1bW1hcnkoZXgwMV9tNCkkci5zcXVhcmVkDQoNCmNiaW5kKG0xX3IyLCBtMl9yMiwgbTNfcjIsIG00X3IyKQ0KYGBgDQoNCklmIHdlIGFyZSBjYXJlZnVsIGFib3V0IHBlcmZvcm1pbmcgdGhlIGFwcHJvcHJpYXRlIHRyYW5zZm9ybWF0aW9ucyBpbiB0aGUgbG9nLWxldmVsIGFuZCBsb2ctbG9nIG1vZGVscywgd2UgY2FuIGRpcmVjdGx5IGNvbXBhcmUgdGhlIFNTRSBzY29yZXMuIEhvd2V2ZXIsIGtlZXAgaW4gbWluZCB0aGF0IHRoZXNlIHZhbHVlcyBvbmx5IHRlbGwgcGFydCBvZiB0aGUgc3RvcnkuIEFzIHdlIHNlZSwgTW9kZWwgMiBoYXMgdGhlIGxvd2VzdCBTU0Ugc2NvcmUsIGJ1dCBhcyB3ZSBub3RlZCwgaXQgaGFzIHNlcmlvdXMgaXNzdWVzIHJlc3VsdGluZyBmcm9tIGhldGVyb3NrZWRhc3RpY2l0eS4gDQoNCmBgYHtyfQ0Kc3NlMSA8LSBzdW0oKGV4MDEkeSAtIGV4MDFfbTEkZml0dGVkLnZhbHVlcyleMikNCnNzZTIgPC0gc3VtKChleDAxJHkgLSBleDAxX20yJGZpdHRlZC52YWx1ZXMpXjIpDQpzc2UzIDwtIHN1bSgoZXgwMSR5IC0gZXhwKGV4MDFfbTMkZml0dGVkLnZhbHVlcykpXjIpDQpzc2U0IDwtIHN1bSgoZXgwMSR5IC0gZXhwKGV4MDFfbTQkZml0dGVkLnZhbHVlcykpXjIpDQoNCmNiaW5kKHNzZTEsIHNzZTIsIHNzZTMsIHNzZTQpDQpgYGANCg0KV2Ugd2lsbCB1c2UgTW9kZWwgMyBnb2luZyBmb3J3YXJkLiBJdCBoYXMgYSBoaWdoICRyXjIkIHZhbHVlLCBhbmQgc2VlbXMgdG8gc2F0aXNmeSB0aGUgR2F1c3MtTWFya292IGFzc3VtcHRpb25zLiANCg0KDQojIyMjICoqR2VuZXJhdGluZyBQcmVkaWN0aW9ucyB3aXRoIHRoZSBMb2ctTGV2ZWwgTW9kZWwqKiANCg0KV2hlbiB1c2luZyBhIGxvZy1sZXZlbCBtb2RlbCB0byBjcmVhdGUgcHJlZGljdGlvbnMgZm9yICRZJCwgd2UgbmVlZCB0byBmaXJzdCBnZW5lcmF0ZSBhIHByZWRpY3Rpb24gZm9yICRcbG4oWSkkLCBhbmQgdGhlbiBleHBvbmVudGlhdGUuIExldCdzIGNvbnN0cnVjdCBhIDk1JSBwcmVkaWN0aW9uIGludGVydmFsIGZvciAkWSQgd2hlbiAkWCA9IDYuNSQuDQoNCg0KYGBge3J9DQpsbl95aGF0ID0gcHJlZGljdChleDAxX20zLCBkYXRhLmZyYW1lKHg9Yyg2LjUpKSwgaW50ZXJ2YWw9InByZWRpY3Rpb24iLCBsZXZlbD0wLjk1KQ0KeWhhdCA9IGV4cChsbl95aGF0KQ0KeWhhdA0KYGBgDQoNCiMjIyAqKkV4YW1wbGUgMioqDQoNCiMjIyMgKipMb2FkIGFuZCBFeHBsb3JlIHRoZSBEYXRhKioNCg0KV2Ugd2lsbCBsb2FkIHRoZSBleGFtcGxlIGRhdGEgZnJvbSBhIHRhYiBzZXBhcmF0ZWQgZmlsZSBsb2NhdGVkIGF0IHRoZSBwYXRoIGBkYXRhL2xvZ19leDAyLnR4dGAuDQoNCmBgYHtyfQ0KZXgwMiA8LSByZWFkLnRhYmxlKCJkYXRhL2xvZ19leDAyLnR4dCIsIHNlcD0iXHQiLCBoZWFkZXI9VFJVRSkNCnN1bW1hcnkoZXgwMikNCmBgYA0KDQpXZSBjYW4gc2VlIGZyb20gYSBwbG90IG9mIHRoZSBkYXRhIHRoYXQgJFgkIGFuZCAkWSQgc2VlbSB0byBleGhpYml0IGEgbm9uLWxpbmVhciByZWxhdGlvbnNoaXAuIFdlIGFsc28gc2VlIHRoYXQgdGhlIHZhcmlhbmNlIGluICRZJCBzZWVtcyB0byBpbmNyZWFzZSB3aXRoICRYJC4NCg0KYGBge3J9DQpwbG90KHkgfiB4LCBleDAyLCBwY2g9MjEsIGNvbD0iYmxhY2siLCBiZz0iY3lhbiIpDQpgYGANCg0KDQoNCiMjIyMgKipNb2RlbCAxOiBMaW5lYXIgTW9kZWwqKg0KDQpXZSB3aWxsIGJlZ2luIGJ5IGNyZWF0aW5nIGEgZml0dGVkIGxpbmVhciBtb2RlbCBvZiB0aGUgZm9ybTogJFxoYXR7WX0gPSBcaGF0e1xiZXRhfV8wICsgXGhhdHtcYmV0YX1fMSBYJCANCg0KYGBge3J9DQpleDAyX20xIDwtIGxtKHkgfiB4LCBleDAyKQ0Kc3VtbWFyeShleDAyX20xKSANCmBgYA0KDQpXZSBwbG90IHRoZSBmaXR0ZWQgbGluZSBvbiB0b3Agb2YgdGhlIHNjYXR0ZXIgcGxvdC4NCg0KDQpgYGB7cn0NCnBsb3QoeSB+IHgsIGV4MDIsIHBjaD0yMSwgY29sPSJibGFjayIsIGJnPSJjeWFuIikNCmFibGluZShleDAyX20xJGNvZWZmaWNpZW50cywgY29sPSJkYXJrcmVkIiwgbHdkPTIpDQpgYGANCg0KDQpXZSBjYW4gc2VlIHN0cm9uZyBldmlkZW5jZSBvZiBhIG5vbi1saW5lYXIgdHJlbmQsIGFzIHdlbGwgYXMgaGV0ZXJvc2tlZGFzdGljaXR5IGluIHRoZSByZXNpZHVhbCBwbG90IGJlbG93Lg0KDQoNCmBgYHtyfQ0KcmVzMSA8LSBleDAyX20xJHJlc2lkdWFscw0KcGxvdChyZXMxIH4gZXgwMiR4LCBwY2g9MjEsIGNvbD0iYmxhY2siLCBiZz0iY3lhbiIsIA0KICAgICB4bGFiPSJ4IiwgeWxhYj0iUmVzaWR1YWxzIiwgbWFpbj0iUmVzaWR1YWwgUGxvdCIpDQoNCmFibGluZShoPTAsIGNvbD0iZGFya3JlZCIsIGx3ZD0yKQ0KYGBgDQoNCldlIGNhbiBhc3Nlc3Mgbm9ybWFsaXR5IG9mIHRoZSByZXNpZHVhbHMgdXNpbmcgYSBTaGFwaXJvLVdpbGsgdGVzdCwgYSBoaXN0b2dyYW0sIGFuZCBhIFFRLXBsb3QuDQoNCmBgYHtyfQ0Kc2hhcGlyby50ZXN0KHJlczEpDQpgYGANCg0KVGhlIFNoYXBpcm8tV2lsayB0ZXN0IHN0cm9uZ2x5IHN1Z2dlc3RzIG5vbi1ub3JtYWxpdHkgb2YgdGhlIHJlc2lkdWFscy4gVGhlIGhpc3RvZ3JhbSBhbmQgUVEtcGxvdCByZXZlYWwgdGhhdCB0aGUgcmVzaWR1YWxzIGFyZSByaWdodC1za2V3ZWQuDQoNCmBgYHtyfQ0KDQpwYXIobWZyb3c9YygxLDIpKQ0KaGlzdChyZXMxLCBjb2w9J29yY2hpZCcpDQpxcW5vcm0ocmVzMSkNCnFxbGluZShyZXMxKQ0KcGFyKG1mcm93PWMoMSwxKSkNCmBgYA0KDQoNCiMjIyMgKipNb2RlbCAyOiBMb2ctTGV2ZWwgTW9kZWwqKg0KDQpOb3RpbmcgdGhlIGFwcGFyZW50IGhldGVyb3NrZWRhc3RpY2l0eSBpbiB0aGUgbW9kZWwsIHdlIHdpbGwgbm93IGNvbnNpZGVyIGEgZml0dGVkIG1vZGVsIG9mIHRoZSBmb3JtOiAkXGxuKFxoYXR7WX0pID0gXGhhdHtcYmV0YX1fMCArIFxoYXR7XGJldGF9XzEgWCQuICANCg0KV2Ugd2lsbCBzdGFydCBieSBwbG90dGluZyB0aGUgcmVzcG9uc2UgJFxsbihZKSQgYWdhaW5zdCAkWCQuIFRoaXMgcGxvdCBleGhpYml0cyBhIHNvbWV3aGF0IG5vbi1saW5lYXIgcmVsYXRpb25zaGlwLg0KDQoNCmBgYHtyfQ0KcGxvdChsb2coeSkgfiB4LCBleDAyLCBwY2g9MjEsIGNvbD0iYmxhY2siLCBiZz0iY3lhbiIpDQpgYGANCg0KQSBzdW1tYXJ5IG9mIHRoZSBsb2ctbGV2ZWwgbW9kZWwgaXMgc2hvd24gYmVsb3cuIA0KDQpgYGB7cn0NCmV4MDJfbTIgPC0gbG0obG9nKHkpIH4geCwgZXgwMikNCnN1bW1hcnkoZXgwMl9tMikgDQpgYGANCg0KTGV0J3MgY29uc2lkZXIgdGhlIHJlc2lkdWFsIHBsb3QuIE5vdGUgdGhhdCB0aGVzZSBhcmUgcmVzaWR1YWxzIG9mICRcbG4oWSkkLCBhbmQgbm90ICRZJC4gDQoNClRoZSBwcmV2aW91c2x5IG9ic2VydmVkIG5vbi1saW5lYXJpdHkgaXMgYXBwYXJlbnQgaW4gdGhlIHJlc2lkdWFsIHBsb3QuDQoNCmBgYHtyfQ0KcmVzMiA8LSBleDAyX20yJHJlc2lkdWFscw0KDQpwbG90KHJlczIgfiBleDAyJHgsIHBjaD0yMSwgY29sPSJibGFjayIsIGJnPSJjeWFuIiwgDQogICAgIHhsYWI9IngiLCB5bGFiPSJSZXNpZHVhbHMiLCBtYWluPSJSZXNpZHVhbCBQbG90IikNCg0KYWJsaW5lKGg9MCwgY29sPSJkYXJrcmVkIiwgbHdkPTIpDQpgYGANCg0KV2Ugbm93IGFzc2VzcyB0aGUgbm9ybWFsaXR5IG9mIHRoZSByZXNpZHVhbHMgd2l0aCBhIGhpc3RvZ3JhbSBhbmQgYSBRUS1wbG90Lg0KDQpgYGB7cn0NCnNoYXBpcm8udGVzdChyZXMyKQ0KYGBgDQoNClRoZSBmaWd1cmUgYmVsb3cgc2hvd3MgYSBzY2F0dGVyIHBsb3Qgb2YgJFxsbihZKSQgYWdhaW5zdCAkWCQuIEl0IGFsc28gZGlzcGxheXMgdGhlIGZpdHRlZCBjdXJ2ZSwgYXMgd2VsbCBhcyBhIDk1JSBwcmVkaWN0aW9uIGJhbmQuIA0KDQoNCmBgYHtyfQ0KcGxvdChsb2coeSkgfiB4LCBleDAyLCBwY2g9MjEsIGNvbD0iYmxhY2siLCBiZz0iY3lhbiIpDQoNCnAyIDwtIHByZWRpY3QoZXgwMl9tMiwgbmV3ZGF0YT1kYXRhLmZyYW1lKHg9cHRuKSwgaW50ZXJ2YWw9InByZWRpY3Rpb24iLCBsZXZlbD0wLjk1KQ0KbGluZXMocHRuLCBwMlssImZpdCJdLCBjb2w9ImRhcmtyZWQiLCBsd2Q9MikNCmxpbmVzKHB0biwgcDJbLCJsd3IiXSwgY29sPSJkYXJrb3JhbmdlIiwgbHdkPTIpDQpsaW5lcyhwdG4sIHAyWywidXByIl0sIGNvbD0iZGFya29yYW5nZSIsIGx3ZD0yKQ0KYGBgDQoNCldlIGNhbiBleHBvbmVudGlhdGUgdGhlIHZhbHVlcyBvZiAkXGxuKFkpJCB0byB0cmFuc2Zvcm0gdGhlIHZlcnRpY2FsIGF4aXMgYmFjayBpbiB0ZXJtcyBvZiAkWSQuIA0KDQpgYGB7cn0NCnBsb3QoeSB+IHgsIGV4MDIsIHBjaD0yMSwgY29sPSJibGFjayIsIGJnPSJjeWFuIikNCg0KcDIgPC0gcHJlZGljdChleDAyX20yLCBuZXdkYXRhPWRhdGEuZnJhbWUoeD1wdG4pLCBpbnRlcnZhbD0icHJlZGljdGlvbiIsIGxldmVsPTAuOTUpDQpsaW5lcyhwdG4sIGV4cChwMlssImZpdCJdKSwgY29sPSJkYXJrcmVkIiwgbHdkPTIpDQpsaW5lcyhwdG4sIGV4cChwMlssImx3ciJdKSwgY29sPSJkYXJrb3JhbmdlIiwgbHdkPTIpDQpsaW5lcyhwdG4sIGV4cChwMlssInVwciJdKSwgY29sPSJkYXJrb3JhbmdlIiwgbHdkPTIpDQpgYGANCg0KDQojIyMjICoqTW9kZWwgMzogTG9nLUxvZyBNb2RlbCoqDQoNCldlIG5vdyBjb25zaWRlciBhIGZpdHRlZCBtb2RlbCBvZiB0aGUgZm9ybTogJFxsbihcaGF0IFkpID0gXGhhdHtcYmV0YX1fMCArIFxoYXR7XGJldGF9XzEgXGxuKFgpJC4gDQoNCldlIHN0YXJ0IGJ5IHBsb3R0aW5nICRcbG4oWSkkIGFnYWluc3QgJFxsbihYKSQuIFRoaXMgcGxvdCBleGhpYml0cyBhIHN0cm9uZyBsaW5lYXIgcmVsYXRpb25zaGlwLiANCg0KYGBge3J9DQpwbG90KGxvZyh5KSB+IGxvZyh4KSwgZXgwMiwgcGNoPTIxLCBjb2w9ImJsYWNrIiwgYmc9ImN5YW4iKQ0KYGBgDQoNCkEgc3VtbWFyeSBvZiB0aGUgZml0dGVkIG1vZGVsIGlzIHNob3duIGJlbG93LiANCg0KDQpgYGB7cn0NCmV4MDJfbTMgPC0gbG0obG9nKHkpIH4gbG9nKHgpLCBleDAyKQ0Kc3VtbWFyeShleDAyX20zKSANCmBgYA0KDQpUaGUgcmVzaWR1YWwgcGxvdCBiZWxvdyBkaXNwbGF5cyBubyBhcHBhcmVudCB0cmVuZCBvciBoZXRlcm9za2VkYXN0aWNpdHkuDQoNCg0KYGBge3J9DQpyZXMzIDwtIGV4MDJfbTMkcmVzaWR1YWxzDQoNCnBsb3QocmVzMyB+IGV4MDIkeCwgcGNoPTIxLCBjb2w9ImJsYWNrIiwgYmc9ImN5YW4iLCANCiAgICAgeGxhYj0ieCIsIHlsYWI9IlJlc2lkdWFscyIsIG1haW49IlJlc2lkdWFsIFBsb3QiKQ0KDQphYmxpbmUoaD0wLCBjb2w9ImRhcmtyZWQiLCBsd2Q9MikNCmBgYA0KDQpUaGVyZSBpcyBubyBhcHBhcmVudCBoZXRlcm9za2VkYXN0aWNpdHkgb3IgdHJlbmQgaW4gdGhpcyByZXNpZHVhbCBwbG90LiAgDQoNCg0KV2Ugbm93IGFzc2VzcyB0aGUgbm9ybWFsaXR5IG9mIHRoZSByZXNpZHVhbHMuDQoNCmBgYHtyfQ0Kc2hhcGlyby50ZXN0KHJlczMpDQpgYGANCg0KDQpgYGB7cn0NCnBhcihtZnJvdz1jKDEsMikpDQpoaXN0KHJlczMsIGNvbD0nb3JjaGlkJykNCnFxbm9ybShyZXMzKQ0KcXFsaW5lKHJlczMpDQpwYXIobWZyb3c9YygxLDEpKQ0KYGBgDQoNCg0KV2Ugd2lsbCBhZGQgdGhlIGZpdHRlZCBjdXJ2ZSBhbmQgIHRvIHRoZSBzY2F0dGVyIHBsb3Qgb2YgJFxsbihZKSQgYWdhaW5zdCAkWCQuIA0KDQoNCg0KYGBge3J9DQpwbG90KGxvZyh5KSB+IGxvZyh4KSwgZXgwMiwgcGNoPTIxLCBjb2w9ImJsYWNrIiwgYmc9ImN5YW4iKQ0KDQpwMyA8LSBwcmVkaWN0KGV4MDJfbTMsIG5ld2RhdGE9ZGF0YS5mcmFtZSh4PXB0biksIGludGVydmFsPSJwcmVkaWN0aW9uIiwgbGV2ZWw9MC45NSkNCmxpbmVzKGxvZyhwdG4pLCBwM1ssImZpdCJdLCBjb2w9ImRhcmtyZWQiLCBsd2Q9MikNCmxpbmVzKGxvZyhwdG4pLCBwM1ssImx3ciJdLCBjb2w9ImRhcmtvcmFuZ2UiLCBsd2Q9MikNCmxpbmVzKGxvZyhwdG4pLCBwM1ssInVwciJdLCBjb2w9ImRhcmtvcmFuZ2UiLCBsd2Q9MikNCmBgYA0KDQpXZSBjYW4gZXhwb25lbnRpYXRlIHRoZSB2YWx1ZXMgb2YgJGxuKFkpJCB0byB0cmFuc2Zvcm0gYmFjayB0byBiZSBpbiB0ZXJtcyBvZiAkWSQuIA0KDQpgYGB7cn0NCnBsb3QoeSB+IHgsIGV4MDIsIHBjaD0yMSwgY29sPSJibGFjayIsIGJnPSJjeWFuIikNCg0KcDMgPC0gcHJlZGljdChleDAyX20zLCBuZXdkYXRhPWRhdGEuZnJhbWUoeD1wdG4pLCBpbnRlcnZhbD0icHJlZGljdGlvbiIsIGxldmVsPTAuOTUpDQpsaW5lcyhwdG4sIGV4cChwM1ssImZpdCJdKSwgY29sPSJkYXJrcmVkIiwgbHdkPTIpDQpsaW5lcyhwdG4sIGV4cChwM1ssImx3ciJdKSwgY29sPSJkYXJrb3JhbmdlIiwgbHdkPTIpDQpsaW5lcyhwdG4sIGV4cChwM1ssInVwciJdKSwgY29sPSJkYXJrb3JhbmdlIiwgbHdkPTIpDQpgYGANCg0KDQojIyMjICoqR2VuZXJhdGluZyBQcmVkaWN0aW9ucyB3aXRoIHRoZSBMb2ctTG9nIE1vZGVsKiogDQoNCldoZW4gdXNpbmcgYSBsb2ctbG9nIG1vZGVsIHRvIGNyZWF0ZSBwcmVkaWN0aW9ucyBmb3IgJFkkLCB3ZSBuZWVkIHRvIGZpcnN0IGdlbmVyYXRlIGEgcHJlZGljdGlvbiBmb3IgJFxsbihZKSQsIGFuZCB0aGVuIGV4cG9uZW50aWF0ZS4gTGV0J3MgY29uc3RydWN0IGEgOTUlIHByZWRpY3Rpb24gaW50ZXJ2YWwgZm9yICRZJCB3aGVuICRYID0gNi41JC4NCg0KDQpgYGB7cn0NCmxuX3loYXQgPSBwcmVkaWN0KGV4MDJfbTMsIGRhdGEuZnJhbWUoeD1jKDYuNSkpLCBpbnRlcnZhbD0icHJlZGljdGlvbiIsIGxldmVsPTAuOTUpDQp5aGF0ID0gZXhwKGxuX3loYXQpDQp5aGF0DQpgYGANCg0KDQojIyMgKipBbHRlcm5hdGUgRm9ybXMgZm9yIFRyYW5zZm9ybWVkIE1vZGVscyoqDQoNCiMjIyMgKipMb2ctTGV2ZWwgTW9kZWwqKg0KDQpJZiB3ZSBzb2x2ZSBmb3IgJFkkIGluIHRoZSBsb2ctbGV2ZWwgbW9kZWwsIHdlIHNlZSB0aGF0IHRoZSBmb2xsb3dpbmcgZm9ybXMgZm9yIHRoZSBtb2RlbCBhcmUgZXF1aXZhbGVudDoNCg0KDQokJFxsbihZKSA9IFxiZXRhXzAgKyBcYmV0YV8xIFggKyBcdmFyZXBzaWxvbiQkDQoNCiQkWSA9IGVee1xiZXRhXzB9IGVee1xiZXRhXzEgWH0gZV57XHZhcmVwc2lsb259JCQNClNpbmNlICRcYmV0YV8wJCBpcyBhIGNvbnN0YW50LCAkZV57XGJldGFfMSBYfSQgaXMgYSBwb3NpdGl2ZSBjb25zdGFudC4gSWYgd2UgY2FsbCB0aGlzIGNvbnN0YW50ICRLJCwgd2UgZ2V0IHRoZSBmb2xsb3dpbmcgZXhwcmVzc2lvbiBmb3IgdGhlIGxvZy1sZXZlbCBtb2RlbDoNCg0KJCRZID0gSyBlXntcYmV0YV8xIFh9IGVee1x2YXJlcHNpbG9ufSQkDQoNCiMjIyMgKipMb2ctTG9nIE1vZGVsKioNCg0KSWYgd2Ugc29sdmUgZm9yICRZJCBpbiB0aGUgbG9nLWxvZyBtb2RlbCwgd2Ugc2VlIHRoYXQgdGhlIGZvbGxvd2luZyBmb3JtcyBmb3IgdGhlIG1vZGVsIGFyZSBlcXVpdmFsZW50Og0KDQoNCiQkXGxuKFkpID0gXGJldGFfMCArIFxiZXRhXzEgXGxuKFgpICsgXHZhcmVwc2lsb24kJA0KDQokJFkgPSBlXntcYmV0YV8wfSBYXntcYmV0YV8xfSBlXntcdmFyZXBzaWxvbn0kJA0KDQpTaW5jZSAkXGJldGFfMCQgaXMgYSBjb25zdGFudCwgJGVee1xiZXRhXzEgWH0kIGlzIGEgcG9zaXRpdmUgY29uc3RhbnQuIElmIHdlIGNhbGwgdGhpcyBjb25zdGFudCAkSyQsIHdlIGdldCB0aGUgZm9sbG93aW5nIGV4cHJlc3Npb24gZm9yIHRoZSBsb2ctbGV2ZWwgbW9kZWw6DQoNCiQkWSA9IEsgWF57XGJldGFfMX0gZV57XHZhcmVwc2lsb259JCQNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg==