pkgs <- c("tidyverse", "dplyr", "haven", "foreign", "lme4", "plyr", "nlme", "lsr", "emmeans", "afex", "knitr", "kableExtra", "QuantPsyc", "car", "readxl", "pastecs")

packages <- rownames(installed.packages())
p_to_install <- pkgs[!(pkgs %in% packages)]

if(length(p_to_install) > 0){
  install.packages(p_to_install)
}

lapply(pkgs, library, character.only = TRUE)

album_df <- read.delim("Album Sales 2.dat")

A note on assumptions

Here are the 3 assumptions that apply to all parametric tests:

  1. Normally distributed data
    • Shapiro-Wilk test: caution–it’s easy to get a significant result if your sample is large enough
    • Q-Q plot: each value is compared to the expected value. If normally distributed, you get a diagonal line.
    • Plots()
      • REGRESSION
        • Assumption of linearity
      • ANOVA
        • Assumption that distributions within groups are normally distributed
  2. Homogeneity of variance: The variances should be the same throughout the data.
    • REGRESSION
      • Homoscedasticity: inspect plot comparing residuals vs predicted values
    • ANOVA
      • Levene’s or Bartlett’s test (compares variances between groups)
      • Mauchly’s sphericity test (repeated measures ANOVA)
  3. Independence
    • REGRESSION
      • Durbin-Watson: residuals are correlated.
      • Multicollinearity: predictors are highly correlated. Test with VIF and/or correlation matrix

Linear Regression

Does money spent on advertising predict album sales?

Here is the dataset we’ll be using in addition to Madalina’s dataset.

A record company wants to know if they spend $100,000 on advertising, how many albums could they expect to sell?

  • adverts: 1 unit = $1,000
  • sales: albums sold
head(album_df)
##    adverts sales airplay attract
## 1   10.256   330      43      10
## 2  985.685   120      28       7
## 3 1445.563   360      35       7
## 4 1188.193   270      33       7
## 5  574.513   220      44       5
## 6  568.954   170      19       5
# Create the figure
fig <- ggplot(data = album_df, aes(x = adverts, y = sales)) + 
  geom_point(alpha = 0.3, color = "#C06C84") +
  geom_smooth(method = "lm", formula = y ~ x, color = "#7D0552", alpha = .3, size = 1) +
  labs(x = "Advertisements", y = "Sales") +
  theme_bw() +
  labs(title = "Albums Sold Predicted by Money Spent on Advertisements")

# Print the figure
print(fig)

# Save figure
# ggsave("figure.jpg", dpi = 300, width = 5, height = 4, units = "in", type = "cairo")
# Sometimes I find scientific notation super confusing. 
options(scipen = 999)

# Run a linear regression (OLS) using adverts to predict sales
model_lm <- lm(sales ~ adverts, data = album_df)
summary(model_lm)
## 
## Call:
## lm(formula = sales ~ adverts, data = album_df)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -152.949  -43.796   -0.393   37.040  211.866 
## 
## Coefficients:
##               Estimate Std. Error t value            Pr(>|t|)    
## (Intercept) 134.139938   7.536575  17.799 <0.0000000000000002 ***
## adverts       0.096124   0.009632   9.979 <0.0000000000000002 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 65.99 on 198 degrees of freedom
## Multiple R-squared:  0.3346, Adjusted R-squared:  0.3313 
## F-statistic: 99.59 on 1 and 198 DF,  p-value: < 0.00000000000000022

How do we interpret the output?

  1. How do we interpret the estimates? How about the intercept?

  2. What does it mean if R-squared is .33? What does it mean for the remaining .67?

Because there is only one predictor, this value represents the square of the simple correlation between advertising and album sales – we can find the square root of R2 by running:

sqrt(0.3346)
## [1] 0.5784462

So, a 1-unit increase in advertising predicts an increase in .096 albums sold. In other words, for every $1,000 we spend on advertising, we predict we’d sell an extra 96 albums.


What if we spend $100,000?

Remember that the equation for linear regression is:

\[Y_i = b_0 + b_1X_i + e_i\]

If we apply it to our data, the equation becomes:

\[album\ sales_i = b_0 + b_1advertising\ budget_i\] \[album\ sales_i = 134.14 + (.096*advertising\ budget)\] \[= 134.14 + (.096*100)\] \[= 143.74\]

In other words, if they spend $100,000, we predict they’d sell around 144,000 albums


Multiple regression

Let’s go over those assumptions in more detail:

  1. Multicollinearity: there should be no perfect linear relationship between two or more predictors. This is an issue because if the predictors are related, then we can’t tease apart their unique contributions
    1. Otherwise, you may have:
      1. Untrustworthy coefficients: once the variance is accounted for by the first predictor, the second predictor accounts for little unique variance.
  2. Homoscedasticity: at each level of the predictor, the variance of the residuals should be constant
  3. Independent errors: for any two observations, the residual terms should be uncorrelated (use Durbin-Watson test)

Here, we’re going to include the variable airplay, which is how much the songs have been played on the radio and attract, which is the attractiveness of the band.

# Run a multiple linear regression (OLS)
model_mult <- lm(sales ~ adverts + airplay + attract, data = album_df)

# OR

model_mult <- update(model_lm, .~. + airplay + attract) # update prior models instead of writing them out again.

Let’s compare the simple regression to the multiple regression

summary(model_lm)
## 
## Call:
## lm(formula = sales ~ adverts, data = album_df)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -152.949  -43.796   -0.393   37.040  211.866 
## 
## Coefficients:
##               Estimate Std. Error t value            Pr(>|t|)    
## (Intercept) 134.139938   7.536575  17.799 <0.0000000000000002 ***
## adverts       0.096124   0.009632   9.979 <0.0000000000000002 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 65.99 on 198 degrees of freedom
## Multiple R-squared:  0.3346, Adjusted R-squared:  0.3313 
## F-statistic: 99.59 on 1 and 198 DF,  p-value: < 0.00000000000000022
summary(model_mult)
## 
## Call:
## lm(formula = sales ~ adverts + airplay + attract, data = album_df)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -121.324  -28.336   -0.451   28.967  144.132 
## 
## Coefficients:
##               Estimate Std. Error t value             Pr(>|t|)    
## (Intercept) -26.612958  17.350001  -1.534                0.127    
## adverts       0.084885   0.006923  12.261 < 0.0000000000000002 ***
## airplay       3.367425   0.277771  12.123 < 0.0000000000000002 ***
## attract      11.086335   2.437849   4.548           0.00000949 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 47.09 on 196 degrees of freedom
## Multiple R-squared:  0.6647, Adjusted R-squared:  0.6595 
## F-statistic: 129.5 on 3 and 196 DF,  p-value: < 0.00000000000000022

R-squared: how much of the variance in the DV is accounted for by the regression model

Adjusted r-squared: how much of the variance in the DV would be accounted for if the model had been taken from the population (conservative version)

1. What do you think of the model fit?
2. How would you interpret the estimates for each predictor?

Which model is better?

The big problem with R2 is that when you add more variables to the model, it will always go up. If you are deciding which of two models fits the data better, the model with more predictor variables in will always fit better. The Akaike information criterion (AIC) is a measure of fit which penalizes the model for having more variables – a little like adjusted R2.

AIC(model_lm, model_mult)
##            df      AIC
## model_lm    3 2247.375
## model_mult  5 2114.337
anova(model_lm, model_mult)
## Analysis of Variance Table
## 
## Model 1: sales ~ adverts
## Model 2: sales ~ adverts + airplay + attract
##   Res.Df    RSS Df Sum of Sq      F                Pr(>F)    
## 1    198 862264                                              
## 2    196 434575  2    427690 96.447 < 0.00000000000000022 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Standardized betas

These estimates tell us the number of SDs by which the outcome will change as a result of one SD change in the predictor. This makes it easier to compare each effect to each other because they are not dependent on the units of measurements of the variables (e.g., 1 unit adverts = $1k).

For reference, here are the standard deviations of our predictors:

sd_table <- function(v1,v2,v3) {
  sd1 <- sd(v1)
  sd2 <- sd(v2)
  sd3 <- sd(v3)
  
  print(c(sd1, sd2, sd3))}

sd_table(album_df$adverts, album_df$airplay, album_df$attract)
## [1] 485.65521  12.26958   1.39529
# standardizes the betas. do NOT use this with interaction terms.
lm.beta(model_mult)
##   adverts   airplay   attract 
## 0.5108462 0.5119881 0.1916834
  • adverts: as the advertising budget increases by 1 SD ($485,655), album sales increase by 0.511 SD.
  • airplay: as the number of radio plays increases by 1 SD (12.27), album sales increase by 0.512 SD.
  • attract: a band rated 1 SD (1.40 units) higher on attractiveness can expect an additioinal 0.192 SD in sales.

Diagnostics

Durbin-Watson: testing the assumption of independence

durbinWatsonTest(model_mult)
##  lag Autocorrelation D-W Statistic p-value
##    1       0.0026951      1.949819   0.702
##  Alternative hypothesis: rho != 0

VIF: testing the assumption of no multicollinearity

VIF is variance inflation factor and used to measure multicollinearity.

tolerance = 1/VIF

  • If the largest VIF is greater 10, then there’s cause for concern
  • If the average VIF is substantially greater than 1, the model may be biased
  • Tolerance below 0.1 means there’s a serious problem
  • Tolerance below 0.2 means there’s a potential problem

Otherwise, there is no collinearity

# vif
vif(model_mult)
##  adverts  airplay  attract 
## 1.014593 1.042504 1.038455
# average vif
mean(vif(model_mult))
## [1] 1.03185
# tolerance
1/vif(model_mult)
##   adverts   airplay   attract 
## 0.9856172 0.9592287 0.9629695

Using plots for diagnostics

Residuals vs Fitted: is used to check the assumptions of linearity. If the residuals are spread equally around a horizontal line without distinct patterns (red line is approximately horizontal at zero), that is a good indication of having a linear relationship.

Normal Q-Q: is used to check the normality of residuals assumption. If the majority of the residuals follow the straight dashed line, then the assumption is fulfilled.

Scale-Location: is used to check the homoscedasticity of residuals (equal variance of residuals). If the residuals are spread randomly and the see a horizontal line with equally (randomly) spread points, then the assumption is fulfilled.

Residuals vs Leverage: is used to identify any influential value in our dataset. Influential values are extreme values that might influence the regression results when included or excluded from the analysis. Look for cases outside of a dashed line (if there is a concerning point, you’ll see red lines pop up).

plot(model_mult)

hist(model_mult$residuals)

shapiro.test(album_df$sales)
## 
##  Shapiro-Wilk normality test
## 
## data:  album_df$sales
## W = 0.98479, p-value = 0.02965

Added variable plots

These plots tell us whether the variables we’re including in the model are relevant to the model. If the slope is not close to 0 then we conclude that the variable is relevant to the model.

The x-axis represents the partial residuals of the predictor variable, and the y-axis represents the partial residuals of the response variable. The line in the plot represents the fitted values from a linear regression model of the partial residuals of the response variable on the partial residuals of the predictor variable.

avPlots(model_mult)

model_cor <- cor(album_df %>% 
      dplyr::select(adverts, airplay, attract))

corrplot::corrplot(model_cor)

What if the assumptions are violated?

  • Violation of linearity: incorporate nonlinear transformation (e.g., log transformation); polynomial terms
  • Violation of independence: use multilevel modeling
  • Violation of homoscedasticity: transform the DV
  • Violation of normality: transform the DV

Transformations

What taking the log of a number does is transform it, essentially shrinking it and making growth at higher numbers non-linear. When you take the log of a series of numbers, it can make them linear if they were formerly non-linear.

Transforming the data won’t change the relationships between variables (the relative differences between people for a given variable stay the same), but it does change the differences between different variables (because it changes the units of measurement).

*Best for data where the 0’s are naturally occurring and not due to error or recording negative values as zero.

Problem Transformation Considerations
positive skew, unequal variances log, sqrt for log, add constant if working with 0’s or negative numbers
negative skew reverse score remember to reverse your reverse when interpreting
skew_df <- read.delim("/Users/kareenadelrosario/Downloads/DownloadFestival(No Outlier).dat")

Day 1

hist.day1 <- ggplot(skew_df, aes(day1)) +
  geom_histogram(aes(y = ..density..), 
                 colour = "black", fill = "white", bins = 40) + 
  labs(x = "Hygiene score on day 1", y = "Density") +
  stat_function(fun = dnorm, args = list(mean = mean(skew_df$day1, na.rm = TRUE), sd = sd(skew_df$day1, na.rm = TRUE)), colour = "black", size = 1)

print(hist.day1)

ggplot(skew_df, aes(sample = day1)) +
  stat_qq() +
  stat_qq_line(colour = "red") + # Add a reference line in red
  theme_minimal() + # Use a minimal theme for aesthetics
  ggtitle("Q-Q Plot")

Day 2

hist.day2 <- ggplot(skew_df, aes(day2)) +
  geom_histogram(aes(y = ..density..), 
                 colour = "black", fill = "white", bins = 40) + 
  labs(x = "Hygiene score on day 2", y = "Density") +
  stat_function(fun = dnorm, args = list(mean = mean(skew_df$day2, na.rm = TRUE), sd = sd(skew_df$day2, na.rm = TRUE)), colour = "black", size = 1)

print(hist.day2)

ggplot(skew_df, aes(sample = day2)) +
  stat_qq() +
  stat_qq_line(colour = "red") + # Add a reference line in red
  theme_minimal() + # Use a minimal theme for aesthetics
  ggtitle("Q-Q Plot")

Skew & kurtosis should be near 0.

  • SKEW: positive score = right skew, negative values = left skew
  • KURTOSIS: positive score = fat tail, negative values = flat tail
pastecs::stat.desc(skew_df[, c("day1", "day2")], basic = FALSE, norm = TRUE)
##                     day1               day2
## median        1.79000000 0.7900000000000000
## mean          1.77113580 0.9609090909090909
## SE.mean       0.02436847 0.0443609457146470
## CI.mean.0.95  0.04783289 0.0873478099781075
## var           0.48099624 0.5195238852402352
## std.dev       0.69353892 0.7207800533035270
## coef.var      0.39157862 0.7501022314417026
## skewness     -0.00442835 1.0828112084309722
## skew.2SE     -0.02577395 3.6115738298309901
## kurtosis     -0.42159405 0.7554615276645142
## kurt.2SE     -1.22838457 1.2645083284699845
## normtest.W    0.99591522 0.9083191225801341
## normtest.p    0.03198482 0.0000000000128163
# log transform (add constant b/c we have zeros)
skew_df$day1_log <- log(skew_df$day1 + 1)
skew_df$day2_log <- log(skew_df$day2 + 1)

# sqrt transformations
skew_df$day1_sqrt <- sqrt(skew_df$day1)
skew_df$day2_sqrt <- sqrt(skew_df$day2)
hist.day2_log <- ggplot(skew_df, aes(day2_log)) +
  geom_histogram(aes(y = ..density..), 
                 colour = "black", fill = "white", bins = 40) + 
  labs(x = "Hygiene score on day 2", y = "Density") +
  stat_function(fun = dnorm, args = list(mean = mean(skew_df$day2_log, na.rm = TRUE), sd = sd(skew_df$day2_log, na.rm = TRUE)), colour = "black", size = 1)

print(hist.day2_log)

hist.day2_sqrt <- ggplot(skew_df, aes(day2_sqrt)) +
  geom_histogram(aes(y = ..density..), 
                 colour = "black", fill = "white", bins = 40) + 
  labs(x = "Hygiene score on day 2", y = "Density") +
  stat_function(fun = dnorm, args = list(mean = mean(skew_df$day2_sqrt, na.rm = TRUE), sd = sd(skew_df$day2_sqrt, na.rm = TRUE)), colour = "black", size = 1)

print(hist.day2_sqrt)

Considerations

Sometimes transformations create more problems than they solve (not shown, but day 1 scores were normal, but have a slight skew after the transformations). What are your other options for non-normal data?

  • Non-parametric tests
  • Bootstrapping
  • For zero-inflated count data, we can use poisson or negative binomial models

Replicating Madalina’s Python code

df <- read_xlsx("/Users/kareenadelrosario/Downloads/data (1).xlsx")
# Create a simple regression plot
ggplot(df, aes(x = Age, y = BELIEFcc)) +
  geom_point(color = "#C06C84", alpha = 0.3) + # Scatter plot
  geom_smooth(method = "lm", color = "#7D0552", size = 1) + # Regression line
  labs(x = "Age", y = "Belief in science") + # Label the x and y axis
  theme_minimal() + # Use a minimal theme
  theme(legend.position = "none") # Remove legend

# Save figure
# ggsave("figure.tif", width = 5, height = 4, dpi = 300, type = "cairo-png")
md <- lm(BELIEFcc ~ Age, data = df)

summary(md)
## 
## Call:
## lm(formula = BELIEFcc ~ Age, data = df)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -70.718 -14.050   6.061  20.728  28.975 
## 
## Coefficients:
##             Estimate Std. Error t value            Pr(>|t|)    
## (Intercept) 76.41025    5.68958  13.430 <0.0000000000000002 ***
## Age         -0.07277    0.10968  -0.664               0.508    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 25.68 on 219 degrees of freedom
## Multiple R-squared:  0.002006,   Adjusted R-squared:  -0.002551 
## F-statistic: 0.4402 on 1 and 219 DF,  p-value: 0.5077
df_plot <- df %>% 
  mutate(Gender.f = case_when(
    Gender == 1 ~ "Female",
    Gender == 2 ~ "Male")) %>%
  mutate(Gender.f = factor(Gender.f, levels = c("Female", "Male"))) %>% 
  filter(!is.na(Gender.f))

ggplot(df_plot, aes(x=Gender.f, y=BELIEFcc, fill = Gender.f)) +
  geom_bar(stat="summary", position = "dodge") +
  ylim(0, 100) +
  theme_minimal() + # Optional: Adds a minimal theme for aesthetics
  labs(x="Party", y="Trust") # Labeling axes

Numeric vs Categorical Predictors

# Numeric
df$Gender <- as.numeric(df$Gender)
model <- lm(BELIEFcc ~ Age + Gender, data = df)
summary(model) 
## 
## Call:
## lm(formula = BELIEFcc ~ Age + Gender, data = df)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -74.366 -12.143   5.994  21.255  33.830 
## 
## Coefficients:
##             Estimate Std. Error t value          Pr(>|t|)    
## (Intercept) 60.79657    7.74482   7.850 0.000000000000186 ***
## Age         -0.07665    0.10786  -0.711           0.47808    
## Gender       9.70256    3.32806   2.915           0.00392 ** 
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 25.25 on 218 degrees of freedom
## Multiple R-squared:  0.03946,    Adjusted R-squared:  0.03064 
## F-statistic: 4.477 on 2 and 218 DF,  p-value: 0.01243
# Factor
df$Gender <- factor(df$Gender)
model <- lm(BELIEFcc ~ Age + Gender, data = df)
summary(model) 
## 
## Call:
## lm(formula = BELIEFcc ~ Age + Gender, data = df)
## 
## Residuals:
##    Min     1Q Median     3Q    Max 
## -74.85 -12.13   6.14  20.93  34.36 
## 
## Coefficients:
##             Estimate Std. Error t value             Pr(>|t|)    
## (Intercept) 70.62086    5.96097  11.847 < 0.0000000000000002 ***
## Age         -0.08698    0.10909  -0.797              0.42615    
## Gender2     10.45065    3.51412   2.974              0.00327 ** 
## Gender4     13.28167   25.63469   0.518              0.60491    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 25.28 on 217 degrees of freedom
## Multiple R-squared:  0.04144,    Adjusted R-squared:  0.02819 
## F-statistic: 3.127 on 3 and 217 DF,  p-value: 0.02668

What if I want to change the reference group? How can I do that?

LS0tCnRpdGxlOiAiSW50cm9kdWN0aW9uIHRvIExpbmVhciBSZWdyZXNzaW9uIgphdXRob3I6ICJLYXJlZW5hIGRlbCBSb3NhcmlvIgpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiCm91dHB1dDogCiAgaHRtbF9kb2N1bWVudDoKICAgIHRoZW1lOiByZWFkYWJsZQogICAgaGlnaGxpZ2h0OiBhcnJvdwogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIGNvbGxhcHNlZDogdHJ1ZQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQotLS0KCgpgYGB7ciwgbG9hZCBkYXRhLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCByZXN1bHRzPSdoaWRlJ30KcGtncyA8LSBjKCJ0aWR5dmVyc2UiLCAiZHBseXIiLCAiaGF2ZW4iLCAiZm9yZWlnbiIsICJsbWU0IiwgInBseXIiLCAibmxtZSIsICJsc3IiLCAiZW1tZWFucyIsICJhZmV4IiwgImtuaXRyIiwgImthYmxlRXh0cmEiLCAiUXVhbnRQc3ljIiwgImNhciIsICJyZWFkeGwiLCAicGFzdGVjcyIpCgpwYWNrYWdlcyA8LSByb3duYW1lcyhpbnN0YWxsZWQucGFja2FnZXMoKSkKcF90b19pbnN0YWxsIDwtIHBrZ3NbIShwa2dzICVpbiUgcGFja2FnZXMpXQoKaWYobGVuZ3RoKHBfdG9faW5zdGFsbCkgPiAwKXsKICBpbnN0YWxsLnBhY2thZ2VzKHBfdG9faW5zdGFsbCkKfQoKbGFwcGx5KHBrZ3MsIGxpYnJhcnksIGNoYXJhY3Rlci5vbmx5ID0gVFJVRSkKCmFsYnVtX2RmIDwtIHJlYWQuZGVsaW0oIkFsYnVtIFNhbGVzIDIuZGF0IikKYGBgCgotLS0tLS0tLS0tLS0tCgojIyBBIG5vdGUgb24gYXNzdW1wdGlvbnMKCkhlcmUgYXJlIHRoZSAzIGFzc3VtcHRpb25zIHRoYXQgYXBwbHkgdG8gYWxsIHBhcmFtZXRyaWMgdGVzdHM6CgogIDEuICoqTm9ybWFsbHkgZGlzdHJpYnV0ZWQgZGF0YSoqCiAgICAgICogU2hhcGlyby1XaWxrIHRlc3Q6IGNhdXRpb24tLWl0J3MgZWFzeSB0byBnZXQgYSBzaWduaWZpY2FudCByZXN1bHQgaWYgeW91ciBzYW1wbGUgaXMgbGFyZ2UgZW5vdWdoCiAgICAgICogUS1RIHBsb3Q6IGVhY2ggdmFsdWUgaXMgY29tcGFyZWQgdG8gdGhlIGV4cGVjdGVkIHZhbHVlLiBJZiBub3JtYWxseSBkaXN0cmlidXRlZCwgeW91IGdldCBhIGRpYWdvbmFsIGxpbmUuCiAgICAgICogUGxvdHMoKQogICAgICAgIC0gUkVHUkVTU0lPTgogICAgICAgICAgLSBBc3N1bXB0aW9uIG9mIGxpbmVhcml0eQogICAgICAgIC0gQU5PVkEKICAgICAgICAgIC0gQXNzdW1wdGlvbiB0aGF0IGRpc3RyaWJ1dGlvbnMgd2l0aGluIGdyb3VwcyBhcmUgbm9ybWFsbHkgZGlzdHJpYnV0ZWQKICAgICAgCiAgMi4gKipIb21vZ2VuZWl0eSBvZiB2YXJpYW5jZSoqOiBUaGUgdmFyaWFuY2VzIHNob3VsZCBiZSB0aGUgc2FtZSB0aHJvdWdob3V0IHRoZSBkYXRhLgogICAgICAqIFJFR1JFU1NJT04KICAgICAgICAtIEhvbW9zY2VkYXN0aWNpdHk6IGluc3BlY3QgcGxvdCBjb21wYXJpbmcgcmVzaWR1YWxzIHZzIHByZWRpY3RlZCB2YWx1ZXMKICAgICAgKiBBTk9WQSAKICAgICAgICAtIExldmVuZSdzIG9yIEJhcnRsZXR0J3MgdGVzdCAoY29tcGFyZXMgdmFyaWFuY2VzIGJldHdlZW4gZ3JvdXBzKQogICAgICAgIC0gTWF1Y2hseSdzIHNwaGVyaWNpdHkgdGVzdCAocmVwZWF0ZWQgbWVhc3VyZXMgQU5PVkEpCgogIDMuICoqSW5kZXBlbmRlbmNlKiogCiAgICAgICogUkVHUkVTU0lPTgogICAgICAgIC0gRHVyYmluLVdhdHNvbjogcmVzaWR1YWxzIGFyZSBjb3JyZWxhdGVkLgogICAgICAgIC0gTXVsdGljb2xsaW5lYXJpdHk6IHByZWRpY3RvcnMgYXJlIGhpZ2hseSBjb3JyZWxhdGVkLiBUZXN0IHdpdGggVklGIGFuZC9vciBjb3JyZWxhdGlvbiBtYXRyaXgKCi0tLS0tLS0tLS0KCiMgTGluZWFyIFJlZ3Jlc3Npb24KIyMgRG9lcyBtb25leSBzcGVudCBvbiBhZHZlcnRpc2luZyBwcmVkaWN0IGFsYnVtIHNhbGVzPwoKSGVyZSBpcyB0aGUgW2RhdGFzZXRdKGh0dHBzOi8vZ2l0aHViLmNvbS9rYXJlZW5hLWRlbHJvc2FyaW8vcmVncmVzc2lvbl9yY29kZS9ibG9iL21haW4vQWxidW0lMjBTYWxlcyUyMDIuZGF0KSB3ZSdsbCBiZSB1c2luZyBpbiBhZGRpdGlvbiB0byBNYWRhbGluYSdzIGRhdGFzZXQuCgpBIHJlY29yZCBjb21wYW55IHdhbnRzIHRvIGtub3cgaWYgdGhleSBzcGVuZCAkMTAwLDAwMCBvbiBhZHZlcnRpc2luZywgaG93IG1hbnkgYWxidW1zIGNvdWxkIHRoZXkgZXhwZWN0IHRvIHNlbGw/CgogIC0gYWR2ZXJ0czogMSB1bml0ID0gJDEsMDAwCiAgLSBzYWxlczogYWxidW1zIHNvbGQKCmBgYHtyfQpoZWFkKGFsYnVtX2RmKQpgYGAKCmBgYHtyIHZpc3VhbGl6aW5nIHJlZ3Jlc3Npb24sIHdhcm5pbmc9RkFMU0V9CiMgQ3JlYXRlIHRoZSBmaWd1cmUKZmlnIDwtIGdncGxvdChkYXRhID0gYWxidW1fZGYsIGFlcyh4ID0gYWR2ZXJ0cywgeSA9IHNhbGVzKSkgKyAKICBnZW9tX3BvaW50KGFscGhhID0gMC4zLCBjb2xvciA9ICIjQzA2Qzg0IikgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIGZvcm11bGEgPSB5IH4geCwgY29sb3IgPSAiIzdEMDU1MiIsIGFscGhhID0gLjMsIHNpemUgPSAxKSArCiAgbGFicyh4ID0gIkFkdmVydGlzZW1lbnRzIiwgeSA9ICJTYWxlcyIpICsKICB0aGVtZV9idygpICsKICBsYWJzKHRpdGxlID0gIkFsYnVtcyBTb2xkIFByZWRpY3RlZCBieSBNb25leSBTcGVudCBvbiBBZHZlcnRpc2VtZW50cyIpCgojIFByaW50IHRoZSBmaWd1cmUKcHJpbnQoZmlnKQoKIyBTYXZlIGZpZ3VyZQojIGdnc2F2ZSgiZmlndXJlLmpwZyIsIGRwaSA9IDMwMCwgd2lkdGggPSA1LCBoZWlnaHQgPSA0LCB1bml0cyA9ICJpbiIsIHR5cGUgPSAiY2Fpcm8iKQoKYGBgCgpgYGB7ciB0ZXN0aW5nIHJlZ3Jlc3Npb259CiMgU29tZXRpbWVzIEkgZmluZCBzY2llbnRpZmljIG5vdGF0aW9uIHN1cGVyIGNvbmZ1c2luZy4gCm9wdGlvbnMoc2NpcGVuID0gOTk5KQoKIyBSdW4gYSBsaW5lYXIgcmVncmVzc2lvbiAoT0xTKSB1c2luZyBhZHZlcnRzIHRvIHByZWRpY3Qgc2FsZXMKbW9kZWxfbG0gPC0gbG0oc2FsZXMgfiBhZHZlcnRzLCBkYXRhID0gYWxidW1fZGYpCnN1bW1hcnkobW9kZWxfbG0pCmBgYAoKIyMjIyBIb3cgZG8gd2UgaW50ZXJwcmV0IHRoZSBvdXRwdXQ/CgoxLiBIb3cgZG8gd2UgaW50ZXJwcmV0IHRoZSBlc3RpbWF0ZXM/IEhvdyBhYm91dCB0aGUgaW50ZXJjZXB0PwoKMi4gV2hhdCBkb2VzIGl0IG1lYW4gaWYgUi1zcXVhcmVkIGlzIC4zMz8gV2hhdCBkb2VzIGl0IG1lYW4gZm9yIHRoZSByZW1haW5pbmcgLjY3PwoKQmVjYXVzZSB0aGVyZSBpcyBvbmx5IG9uZSBwcmVkaWN0b3IsIHRoaXMgdmFsdWUgcmVwcmVzZW50cyB0aGUgc3F1YXJlIG9mIHRoZSBzaW1wbGUgY29ycmVsYXRpb24gYmV0d2VlbiBhZHZlcnRpc2luZyBhbmQgYWxidW0gc2FsZXMg4oCTIHdlIGNhbiBmaW5kIHRoZSBzcXVhcmUgcm9vdCBvZiBSMiBieSBydW5uaW5nOgoKYGBge3J9CnNxcnQoMC4zMzQ2KQpgYGAKClNvLCBhIDEtdW5pdCBpbmNyZWFzZSBpbiBhZHZlcnRpc2luZyBwcmVkaWN0cyBhbiBpbmNyZWFzZSBpbiAuMDk2IGFsYnVtcyBzb2xkLiBJbiBvdGhlciB3b3JkcywgZm9yIGV2ZXJ5ICQxLDAwMCB3ZSBzcGVuZCBvbiBhZHZlcnRpc2luZywgd2UgcHJlZGljdCB3ZSdkIHNlbGwgYW4gZXh0cmEgOTYgYWxidW1zLiAKCi0tLS0tLS0tLS0tLS0tLQoKKldoYXQgaWYgd2Ugc3BlbmQgJDEwMCwwMDA/KgoKClJlbWVtYmVyIHRoYXQgdGhlIGVxdWF0aW9uIGZvciBsaW5lYXIgcmVncmVzc2lvbiBpczoKCiQkWV9pID0gYl8wICsgYl8xWF9pICsgZV9pJCQKCklmIHdlIGFwcGx5IGl0IHRvIG91ciBkYXRhLCB0aGUgZXF1YXRpb24gYmVjb21lczoKCiQkYWxidW1cIHNhbGVzX2kgID0gYl8wICsgYl8xYWR2ZXJ0aXNpbmdcIGJ1ZGdldF9pJCQKJCRhbGJ1bVwgc2FsZXNfaSAgPSAxMzQuMTQgKyAoLjA5NiphZHZlcnRpc2luZ1wgYnVkZ2V0KSQkCiQkPSAxMzQuMTQgKyAoLjA5NioxMDApJCQKJCQ9IDE0My43NCQkCgoqKkluIG90aGVyIHdvcmRzLCBpZiB0aGV5IHNwZW5kICQxMDAsMDAwLCB3ZSBwcmVkaWN0IHRoZXknZCBzZWxsIGFyb3VuZCAxNDQsMDAwIGFsYnVtcyoqCgotLS0tLS0tCgojIE11bHRpcGxlIHJlZ3Jlc3Npb24KCkxldCdzIGdvIG92ZXIgdGhvc2UgYXNzdW1wdGlvbnMgaW4gbW9yZSBkZXRhaWw6CgogIDEuICoqTXVsdGljb2xsaW5lYXJpdHk6KiogdGhlcmUgc2hvdWxkIGJlIG5vIHBlcmZlY3QgbGluZWFyIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHR3byBvciBtb3JlIHByZWRpY3RvcnMuIFRoaXMgaXMgYW4gaXNzdWUgYmVjYXVzZSBpZiB0aGUgcHJlZGljdG9ycyBhcmUgcmVsYXRlZCwgdGhlbiB3ZSBjYW4ndCB0ZWFzZSBhcGFydCB0aGVpciB1bmlxdWUgY29udHJpYnV0aW9ucwogICAgICBhLiBPdGhlcndpc2UsIHlvdSBtYXkgaGF2ZToKICAgICAgICAgIDEuIFVudHJ1c3R3b3J0aHkgY29lZmZpY2llbnRzOiBvbmNlIHRoZSB2YXJpYW5jZSBpcyBhY2NvdW50ZWQgZm9yIGJ5IHRoZSBmaXJzdCBwcmVkaWN0b3IsIHRoZSBzZWNvbmQgcHJlZGljdG9yIGFjY291bnRzIGZvciBsaXR0bGUgdW5pcXVlIHZhcmlhbmNlLgogIDIuICoqSG9tb3NjZWRhc3RpY2l0eToqKiBhdCBlYWNoIGxldmVsIG9mIHRoZSBwcmVkaWN0b3IsIHRoZSB2YXJpYW5jZSBvZiB0aGUgcmVzaWR1YWxzIHNob3VsZCBiZSBjb25zdGFudAogIDMuICoqSW5kZXBlbmRlbnQgZXJyb3JzOioqIGZvciBhbnkgdHdvIG9ic2VydmF0aW9ucywgdGhlIHJlc2lkdWFsIHRlcm1zIHNob3VsZCBiZSB1bmNvcnJlbGF0ZWQgKHVzZSBEdXJiaW4tV2F0c29uIHRlc3QpCgoKLS0tLS0tCgpIZXJlLCB3ZSdyZSBnb2luZyB0byBpbmNsdWRlIHRoZSB2YXJpYWJsZSAqKmFpcnBsYXkqKiwgd2hpY2ggaXMgaG93IG11Y2ggdGhlIHNvbmdzIGhhdmUgYmVlbiBwbGF5ZWQgb24gdGhlIHJhZGlvIGFuZCAqKmF0dHJhY3QqKiwgd2hpY2ggaXMgdGhlIGF0dHJhY3RpdmVuZXNzIG9mIHRoZSBiYW5kLgoKYGBge3J9CiMgUnVuIGEgbXVsdGlwbGUgbGluZWFyIHJlZ3Jlc3Npb24gKE9MUykKbW9kZWxfbXVsdCA8LSBsbShzYWxlcyB+IGFkdmVydHMgKyBhaXJwbGF5ICsgYXR0cmFjdCwgZGF0YSA9IGFsYnVtX2RmKQoKIyBPUgoKbW9kZWxfbXVsdCA8LSB1cGRhdGUobW9kZWxfbG0sIC5+LiArIGFpcnBsYXkgKyBhdHRyYWN0KSAjIHVwZGF0ZSBwcmlvciBtb2RlbHMgaW5zdGVhZCBvZiB3cml0aW5nIHRoZW0gb3V0IGFnYWluLgpgYGAKCiMjIyBMZXQncyBjb21wYXJlIHRoZSBzaW1wbGUgcmVncmVzc2lvbiB0byB0aGUgbXVsdGlwbGUgcmVncmVzc2lvbgpgYGB7cn0Kc3VtbWFyeShtb2RlbF9sbSkKYGBgCgpgYGB7cn0Kc3VtbWFyeShtb2RlbF9tdWx0KQpgYGAKICAKICAqKlItc3F1YXJlZDoqKiBob3cgbXVjaCBvZiB0aGUgdmFyaWFuY2UgaW4gdGhlIERWIGlzIGFjY291bnRlZCBmb3IgYnkgdGhlIHJlZ3Jlc3Npb24gbW9kZWwKICAKICAqKkFkanVzdGVkIHItc3F1YXJlZDoqKiBob3cgbXVjaCBvZiB0aGUgdmFyaWFuY2UgaW4gdGhlIERWIHdvdWxkIGJlIGFjY291bnRlZCBmb3IgaWYgdGhlIG1vZGVsIGhhZCBiZWVuIHRha2VuIGZyb20gdGhlIHBvcHVsYXRpb24gKGNvbnNlcnZhdGl2ZSB2ZXJzaW9uKQoKICAgIDEuIFdoYXQgZG8geW91IHRoaW5rIG9mIHRoZSBtb2RlbCBmaXQ/CiAgICAyLiBIb3cgd291bGQgeW91IGludGVycHJldCB0aGUgZXN0aW1hdGVzIGZvciBlYWNoIHByZWRpY3Rvcj8KICAgIAotLS0tLS0tLS0tLS0tLS0tLS0KCgojIyMjIFdoaWNoIG1vZGVsIGlzIGJldHRlcj8KClRoZSBiaWcgcHJvYmxlbSB3aXRoIFIyIGlzIHRoYXQgd2hlbiB5b3UgYWRkIG1vcmUgdmFyaWFibGVzIHRvIHRoZSBtb2RlbCwgaXQgd2lsbCBhbHdheXMgZ28gdXAuIElmIHlvdSBhcmUgZGVjaWRpbmcgd2hpY2ggb2YgdHdvIG1vZGVscyBmaXRzIHRoZSBkYXRhIGJldHRlciwgdGhlIG1vZGVsIHdpdGggbW9yZSBwcmVkaWN0b3IgdmFyaWFibGVzIGluIHdpbGwgYWx3YXlzIGZpdCBiZXR0ZXIuIFRoZSBBa2Fpa2UgaW5mb3JtYXRpb24gY3JpdGVyaW9uIChBSUMpIGlzIGEgbWVhc3VyZSBvZiBmaXQgd2hpY2ggcGVuYWxpemVzIHRoZSBtb2RlbCBmb3IgaGF2aW5nIG1vcmUgdmFyaWFibGVzIOKAkyBhIGxpdHRsZSBsaWtlIGFkanVzdGVkIFIyLgoKYGBge3J9CkFJQyhtb2RlbF9sbSwgbW9kZWxfbXVsdCkKYGBgCgpgYGB7cn0KYW5vdmEobW9kZWxfbG0sIG1vZGVsX211bHQpCmBgYAoKIyMjIyBTdGFuZGFyZGl6ZWQgYmV0YXMKClRoZXNlIGVzdGltYXRlcyB0ZWxsIHVzIHRoZSBudW1iZXIgb2YgU0RzIGJ5IHdoaWNoIHRoZSBvdXRjb21lIHdpbGwgY2hhbmdlIGFzIGEgcmVzdWx0IG9mIG9uZSBTRCBjaGFuZ2UgaW4gdGhlIHByZWRpY3Rvci4gVGhpcyBtYWtlcyBpdCBlYXNpZXIgdG8gY29tcGFyZSBlYWNoIGVmZmVjdCB0byBlYWNoIG90aGVyIGJlY2F1c2UgdGhleSBhcmUgbm90IGRlcGVuZGVudCBvbiB0aGUgdW5pdHMgb2YgbWVhc3VyZW1lbnRzIG9mIHRoZSB2YXJpYWJsZXMgKGUuZy4sIDEgdW5pdCBhZHZlcnRzID0gJDFrKS4gCgpGb3IgcmVmZXJlbmNlLCBoZXJlIGFyZSB0aGUgc3RhbmRhcmQgZGV2aWF0aW9ucyBvZiBvdXIgcHJlZGljdG9yczoKYGBge3J9CnNkX3RhYmxlIDwtIGZ1bmN0aW9uKHYxLHYyLHYzKSB7CiAgc2QxIDwtIHNkKHYxKQogIHNkMiA8LSBzZCh2MikKICBzZDMgPC0gc2QodjMpCiAgCiAgcHJpbnQoYyhzZDEsIHNkMiwgc2QzKSl9CgpzZF90YWJsZShhbGJ1bV9kZiRhZHZlcnRzLCBhbGJ1bV9kZiRhaXJwbGF5LCBhbGJ1bV9kZiRhdHRyYWN0KQpgYGAKCgpgYGB7cn0KIyBzdGFuZGFyZGl6ZXMgdGhlIGJldGFzLiBkbyBOT1QgdXNlIHRoaXMgd2l0aCBpbnRlcmFjdGlvbiB0ZXJtcy4KbG0uYmV0YShtb2RlbF9tdWx0KQpgYGAKCiAgKiAqKmFkdmVydHM6KiogYXMgdGhlIGFkdmVydGlzaW5nIGJ1ZGdldCBpbmNyZWFzZXMgYnkgMSBTRCAoJDQ4NSw2NTUpLCBhbGJ1bSBzYWxlcyBpbmNyZWFzZSBieSAwLjUxMSBTRC4KICAqICoqYWlycGxheToqKiBhcyB0aGUgbnVtYmVyIG9mIHJhZGlvIHBsYXlzIGluY3JlYXNlcyBieSAxIFNEICgxMi4yNyksIGFsYnVtIHNhbGVzIGluY3JlYXNlIGJ5IDAuNTEyIFNELgogICogKiphdHRyYWN0OioqIGEgYmFuZCByYXRlZCAxIFNEICgxLjQwIHVuaXRzKSBoaWdoZXIgb24gYXR0cmFjdGl2ZW5lc3MgY2FuIGV4cGVjdCBhbiBhZGRpdGlvaW5hbCAwLjE5MiBTRCBpbiBzYWxlcy4gCgojIyBEaWFnbm9zdGljcwoKIyMjIER1cmJpbi1XYXRzb246IHRlc3RpbmcgdGhlIGFzc3VtcHRpb24gb2YgaW5kZXBlbmRlbmNlCgpgYGB7cn0KZHVyYmluV2F0c29uVGVzdChtb2RlbF9tdWx0KQpgYGAKCiMjIyBWSUY6IHRlc3RpbmcgdGhlIGFzc3VtcHRpb24gb2Ygbm8gbXVsdGljb2xsaW5lYXJpdHkKClZJRiBpcyB2YXJpYW5jZSBpbmZsYXRpb24gZmFjdG9yIGFuZCB1c2VkIHRvIG1lYXN1cmUgbXVsdGljb2xsaW5lYXJpdHkuCgp0b2xlcmFuY2UgPSAxL1ZJRgoKICAqIElmIHRoZSBsYXJnZXN0IFZJRiBpcyBncmVhdGVyIDEwLCB0aGVuIHRoZXJlJ3MgY2F1c2UgZm9yIGNvbmNlcm4KICAqIElmIHRoZSBhdmVyYWdlIFZJRiBpcyBzdWJzdGFudGlhbGx5IGdyZWF0ZXIgdGhhbiAxLCB0aGUgbW9kZWwgbWF5IGJlIGJpYXNlZAogICogVG9sZXJhbmNlIGJlbG93IDAuMSBtZWFucyB0aGVyZSdzIGEgc2VyaW91cyBwcm9ibGVtCiAgKiBUb2xlcmFuY2UgYmVsb3cgMC4yIG1lYW5zIHRoZXJlJ3MgYSBwb3RlbnRpYWwgcHJvYmxlbQogIApPdGhlcndpc2UsIHRoZXJlIGlzIG5vIGNvbGxpbmVhcml0eQoKYGBge3J9CiMgdmlmCnZpZihtb2RlbF9tdWx0KQoKIyBhdmVyYWdlIHZpZgptZWFuKHZpZihtb2RlbF9tdWx0KSkKCiMgdG9sZXJhbmNlCjEvdmlmKG1vZGVsX211bHQpCmBgYAoKIyMjIFVzaW5nIHBsb3RzIGZvciBkaWFnbm9zdGljcwoKKipSZXNpZHVhbHMgdnMgRml0dGVkOioqIGlzIHVzZWQgdG8gY2hlY2sgdGhlIGFzc3VtcHRpb25zIG9mIGxpbmVhcml0eS4gSWYgdGhlIHJlc2lkdWFscyBhcmUgc3ByZWFkIGVxdWFsbHkgYXJvdW5kIGEgaG9yaXpvbnRhbCBsaW5lIHdpdGhvdXQgZGlzdGluY3QgcGF0dGVybnMgKHJlZCBsaW5lIGlzIGFwcHJveGltYXRlbHkgaG9yaXpvbnRhbCBhdCB6ZXJvKSwgdGhhdCBpcyBhIGdvb2QgaW5kaWNhdGlvbiBvZiBoYXZpbmcgYSBsaW5lYXIgcmVsYXRpb25zaGlwLgoKKipOb3JtYWwgUS1ROioqIGlzIHVzZWQgdG8gY2hlY2sgdGhlIG5vcm1hbGl0eSBvZiByZXNpZHVhbHMgYXNzdW1wdGlvbi4gSWYgdGhlIG1ham9yaXR5IG9mIHRoZSByZXNpZHVhbHMgZm9sbG93IHRoZSBzdHJhaWdodCBkYXNoZWQgbGluZSwgdGhlbiB0aGUgYXNzdW1wdGlvbiBpcyBmdWxmaWxsZWQuCgoqKlNjYWxlLUxvY2F0aW9uOioqIGlzIHVzZWQgdG8gY2hlY2sgdGhlIGhvbW9zY2VkYXN0aWNpdHkgb2YgcmVzaWR1YWxzIChlcXVhbCB2YXJpYW5jZSBvZiByZXNpZHVhbHMpLiBJZiB0aGUgcmVzaWR1YWxzIGFyZSBzcHJlYWQgcmFuZG9tbHkgYW5kIHRoZSBzZWUgYSBob3Jpem9udGFsIGxpbmUgd2l0aCBlcXVhbGx5IChyYW5kb21seSkgc3ByZWFkIHBvaW50cywgdGhlbiB0aGUgYXNzdW1wdGlvbiBpcyBmdWxmaWxsZWQuCgoqKlJlc2lkdWFscyB2cyBMZXZlcmFnZToqKiBpcyB1c2VkIHRvIGlkZW50aWZ5IGFueSBpbmZsdWVudGlhbCB2YWx1ZSBpbiBvdXIgZGF0YXNldC4gSW5mbHVlbnRpYWwgdmFsdWVzIGFyZSBleHRyZW1lIHZhbHVlcyB0aGF0IG1pZ2h0IGluZmx1ZW5jZSB0aGUgcmVncmVzc2lvbiByZXN1bHRzIHdoZW4gaW5jbHVkZWQgb3IgZXhjbHVkZWQgZnJvbSB0aGUgYW5hbHlzaXMuIExvb2sgZm9yIGNhc2VzIG91dHNpZGUgb2YgYSBkYXNoZWQgbGluZSAoaWYgdGhlcmUgaXMgYSBjb25jZXJuaW5nIHBvaW50LCB5b3UnbGwgc2VlIHJlZCBsaW5lcyBwb3AgdXApLgoKYGBge3J9CnBsb3QobW9kZWxfbXVsdCkKYGBgCgpgYGB7cn0KaGlzdChtb2RlbF9tdWx0JHJlc2lkdWFscykKYGBgCgpgYGB7cn0Kc2hhcGlyby50ZXN0KGFsYnVtX2RmJHNhbGVzKQpgYGAKCiMjIyMgQWRkZWQgdmFyaWFibGUgcGxvdHMKClRoZXNlIHBsb3RzIHRlbGwgdXMgd2hldGhlciB0aGUgdmFyaWFibGVzIHdlJ3JlIGluY2x1ZGluZyBpbiB0aGUgbW9kZWwgYXJlIHJlbGV2YW50IHRvIHRoZSBtb2RlbC4gSWYgdGhlIHNsb3BlIGlzIG5vdCBjbG9zZSB0byAwIHRoZW4gd2UgY29uY2x1ZGUgdGhhdCB0aGUgdmFyaWFibGUgaXMgcmVsZXZhbnQgdG8gdGhlIG1vZGVsLgoKVGhlIHgtYXhpcyByZXByZXNlbnRzIHRoZSBwYXJ0aWFsIHJlc2lkdWFscyBvZiB0aGUgcHJlZGljdG9yIHZhcmlhYmxlLCBhbmQgdGhlIHktYXhpcyByZXByZXNlbnRzIHRoZSBwYXJ0aWFsIHJlc2lkdWFscyBvZiB0aGUgcmVzcG9uc2UgdmFyaWFibGUuIFRoZSBsaW5lIGluIHRoZSBwbG90IHJlcHJlc2VudHMgdGhlIGZpdHRlZCB2YWx1ZXMgZnJvbSBhIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsIG9mIHRoZSBwYXJ0aWFsIHJlc2lkdWFscyBvZiB0aGUgcmVzcG9uc2UgdmFyaWFibGUgb24gdGhlIHBhcnRpYWwgcmVzaWR1YWxzIG9mIHRoZSBwcmVkaWN0b3IgdmFyaWFibGUuCgpgYGB7cn0KYXZQbG90cyhtb2RlbF9tdWx0KQpgYGAKCmBgYHtyfQptb2RlbF9jb3IgPC0gY29yKGFsYnVtX2RmICU+JSAKICAgICAgZHBseXI6OnNlbGVjdChhZHZlcnRzLCBhaXJwbGF5LCBhdHRyYWN0KSkKCmNvcnJwbG90Ojpjb3JycGxvdChtb2RlbF9jb3IpCmBgYAoKIyMjIFdoYXQgaWYgdGhlIGFzc3VtcHRpb25zIGFyZSB2aW9sYXRlZD8KCiAgKiBWaW9sYXRpb24gb2YgbGluZWFyaXR5OiBpbmNvcnBvcmF0ZSBub25saW5lYXIgdHJhbnNmb3JtYXRpb24gKGUuZy4sIGxvZyB0cmFuc2Zvcm1hdGlvbik7IHBvbHlub21pYWwgdGVybXMKICAqIFZpb2xhdGlvbiBvZiBpbmRlcGVuZGVuY2U6IHVzZSBtdWx0aWxldmVsIG1vZGVsaW5nCiAgKiBWaW9sYXRpb24gb2YgaG9tb3NjZWRhc3RpY2l0eTogdHJhbnNmb3JtIHRoZSBEVgogICogVmlvbGF0aW9uIG9mIG5vcm1hbGl0eTogdHJhbnNmb3JtIHRoZSBEVgoKIyMjIFRyYW5zZm9ybWF0aW9ucwoKV2hhdCB0YWtpbmcgdGhlIGxvZyBvZiBhIG51bWJlciBkb2VzIGlzIHRyYW5zZm9ybSBpdCwgZXNzZW50aWFsbHkgc2hyaW5raW5nIGl0IGFuZCBtYWtpbmcgZ3Jvd3RoIGF0IGhpZ2hlciBudW1iZXJzIG5vbi1saW5lYXIuIFdoZW4geW91IHRha2UgdGhlIGxvZyBvZiBhIHNlcmllcyBvZiBudW1iZXJzLCBpdCBjYW4gbWFrZSB0aGVtIGxpbmVhciBpZiB0aGV5IHdlcmUgZm9ybWVybHkgbm9uLWxpbmVhci4KClRyYW5zZm9ybWluZyB0aGUgZGF0YSB3b27igJl0IGNoYW5nZSB0aGUgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIHZhcmlhYmxlcyAodGhlIHJlbGF0aXZlIGRpZmZlcmVuY2VzIGJldHdlZW4gcGVvcGxlIGZvciBhIGdpdmVuIHZhcmlhYmxlIHN0YXkgdGhlIHNhbWUpLCBidXQgaXQgZG9lcyBjaGFuZ2UgdGhlIGRpZmZlcmVuY2VzIGJldHdlZW4gZGlmZmVyZW50IHZhcmlhYmxlcyAoYmVjYXVzZSBpdCBjaGFuZ2VzIHRoZSB1bml0cyBvZiBtZWFzdXJlbWVudCkuIAoKKkJlc3QgZm9yIGRhdGEgd2hlcmUgdGhlIDAncyBhcmUgbmF0dXJhbGx5IG9jY3VycmluZyBhbmQgbm90IGR1ZSB0byBlcnJvciBvciByZWNvcmRpbmcgbmVnYXRpdmUgdmFsdWVzIGFzIHplcm8uCgoKfFByb2JsZW0gICAgICAgICAgICAgICAgICAgICAgICAgIHwgVHJhbnNmb3JtYXRpb24gIHwgQ29uc2lkZXJhdGlvbnMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwKfHBvc2l0aXZlIHNrZXcsIHVuZXF1YWwgdmFyaWFuY2VzIHwgbG9nLCBzcXJ0ICAgICAgIHwgZm9yIGxvZywgYWRkIGNvbnN0YW50IGlmIHdvcmtpbmcgd2l0aCAwJ3Mgb3IgbmVnYXRpdmUgbnVtYmVyc3wKfG5lZ2F0aXZlIHNrZXcgICAgICAgICAgICAgICAgICAgIHwgcmV2ZXJzZSBzY29yZSAgIHwgcmVtZW1iZXIgdG8gcmV2ZXJzZSB5b3VyIHJldmVyc2Ugd2hlbiBpbnRlcnByZXRpbmcgICAgICAgICAgIHwKCgpgYGB7cn0Kc2tld19kZiA8LSByZWFkLmRlbGltKCIvVXNlcnMva2FyZWVuYWRlbHJvc2FyaW8vRG93bmxvYWRzL0Rvd25sb2FkRmVzdGl2YWwoTm8gT3V0bGllcikuZGF0IikKYGBgCgojIyMjIERheSAxCgpgYGB7ciwgd2FybmluZyA9IEZBTFNFfQpoaXN0LmRheTEgPC0gZ2dwbG90KHNrZXdfZGYsIGFlcyhkYXkxKSkgKwogIGdlb21faGlzdG9ncmFtKGFlcyh5ID0gLi5kZW5zaXR5Li4pLCAKICAgICAgICAgICAgICAgICBjb2xvdXIgPSAiYmxhY2siLCBmaWxsID0gIndoaXRlIiwgYmlucyA9IDQwKSArIAogIGxhYnMoeCA9ICJIeWdpZW5lIHNjb3JlIG9uIGRheSAxIiwgeSA9ICJEZW5zaXR5IikgKwogIHN0YXRfZnVuY3Rpb24oZnVuID0gZG5vcm0sIGFyZ3MgPSBsaXN0KG1lYW4gPSBtZWFuKHNrZXdfZGYkZGF5MSwgbmEucm0gPSBUUlVFKSwgc2QgPSBzZChza2V3X2RmJGRheTEsIG5hLnJtID0gVFJVRSkpLCBjb2xvdXIgPSAiYmxhY2siLCBzaXplID0gMSkKCnByaW50KGhpc3QuZGF5MSkKYGBgCgpgYGB7ciwgd2FybmluZyA9IEZBTFNFfQpnZ3Bsb3Qoc2tld19kZiwgYWVzKHNhbXBsZSA9IGRheTEpKSArCiAgc3RhdF9xcSgpICsKICBzdGF0X3FxX2xpbmUoY29sb3VyID0gInJlZCIpICsgIyBBZGQgYSByZWZlcmVuY2UgbGluZSBpbiByZWQKICB0aGVtZV9taW5pbWFsKCkgKyAjIFVzZSBhIG1pbmltYWwgdGhlbWUgZm9yIGFlc3RoZXRpY3MKICBnZ3RpdGxlKCJRLVEgUGxvdCIpCmBgYAoKIyMjIyBEYXkgMgoKYGBge3IsIHdhcm5pbmc9RkFMU0V9Cmhpc3QuZGF5MiA8LSBnZ3Bsb3Qoc2tld19kZiwgYWVzKGRheTIpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHkgPSAuLmRlbnNpdHkuLiksIAogICAgICAgICAgICAgICAgIGNvbG91ciA9ICJibGFjayIsIGZpbGwgPSAid2hpdGUiLCBiaW5zID0gNDApICsgCiAgbGFicyh4ID0gIkh5Z2llbmUgc2NvcmUgb24gZGF5IDIiLCB5ID0gIkRlbnNpdHkiKSArCiAgc3RhdF9mdW5jdGlvbihmdW4gPSBkbm9ybSwgYXJncyA9IGxpc3QobWVhbiA9IG1lYW4oc2tld19kZiRkYXkyLCBuYS5ybSA9IFRSVUUpLCBzZCA9IHNkKHNrZXdfZGYkZGF5MiwgbmEucm0gPSBUUlVFKSksIGNvbG91ciA9ICJibGFjayIsIHNpemUgPSAxKQoKcHJpbnQoaGlzdC5kYXkyKQpgYGAKCmBgYHtyLCB3YXJuaW5nPUZBTFNFfQpnZ3Bsb3Qoc2tld19kZiwgYWVzKHNhbXBsZSA9IGRheTIpKSArCiAgc3RhdF9xcSgpICsKICBzdGF0X3FxX2xpbmUoY29sb3VyID0gInJlZCIpICsgIyBBZGQgYSByZWZlcmVuY2UgbGluZSBpbiByZWQKICB0aGVtZV9taW5pbWFsKCkgKyAjIFVzZSBhIG1pbmltYWwgdGhlbWUgZm9yIGFlc3RoZXRpY3MKICBnZ3RpdGxlKCJRLVEgUGxvdCIpCmBgYAoKU2tldyAmIGt1cnRvc2lzIHNob3VsZCBiZSBuZWFyIDAuIAoKICAqIFNLRVc6IHBvc2l0aXZlIHNjb3JlID0gcmlnaHQgc2tldywgbmVnYXRpdmUgdmFsdWVzID0gbGVmdCBza2V3CiAgKiBLVVJUT1NJUzogcG9zaXRpdmUgc2NvcmUgPSBmYXQgdGFpbCwgbmVnYXRpdmUgdmFsdWVzID0gZmxhdCB0YWlsCgpgYGB7cn0KcGFzdGVjczo6c3RhdC5kZXNjKHNrZXdfZGZbLCBjKCJkYXkxIiwgImRheTIiKV0sIGJhc2ljID0gRkFMU0UsIG5vcm0gPSBUUlVFKQpgYGAKCmBgYHtyfQojIGxvZyB0cmFuc2Zvcm0gKGFkZCBjb25zdGFudCBiL2Mgd2UgaGF2ZSB6ZXJvcykKc2tld19kZiRkYXkxX2xvZyA8LSBsb2coc2tld19kZiRkYXkxICsgMSkKc2tld19kZiRkYXkyX2xvZyA8LSBsb2coc2tld19kZiRkYXkyICsgMSkKCiMgc3FydCB0cmFuc2Zvcm1hdGlvbnMKc2tld19kZiRkYXkxX3NxcnQgPC0gc3FydChza2V3X2RmJGRheTEpCnNrZXdfZGYkZGF5Ml9zcXJ0IDwtIHNxcnQoc2tld19kZiRkYXkyKQpgYGAKCmBgYHtyLCB3YXJuaW5nPUZBTFNFfQpoaXN0LmRheTJfbG9nIDwtIGdncGxvdChza2V3X2RmLCBhZXMoZGF5Ml9sb2cpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHkgPSAuLmRlbnNpdHkuLiksIAogICAgICAgICAgICAgICAgIGNvbG91ciA9ICJibGFjayIsIGZpbGwgPSAid2hpdGUiLCBiaW5zID0gNDApICsgCiAgbGFicyh4ID0gIkh5Z2llbmUgc2NvcmUgb24gZGF5IDIiLCB5ID0gIkRlbnNpdHkiKSArCiAgc3RhdF9mdW5jdGlvbihmdW4gPSBkbm9ybSwgYXJncyA9IGxpc3QobWVhbiA9IG1lYW4oc2tld19kZiRkYXkyX2xvZywgbmEucm0gPSBUUlVFKSwgc2QgPSBzZChza2V3X2RmJGRheTJfbG9nLCBuYS5ybSA9IFRSVUUpKSwgY29sb3VyID0gImJsYWNrIiwgc2l6ZSA9IDEpCgpwcmludChoaXN0LmRheTJfbG9nKQpgYGAKCmBgYHtyLCB3YXJuaW5nPUZBTFNFfQpoaXN0LmRheTJfc3FydCA8LSBnZ3Bsb3Qoc2tld19kZiwgYWVzKGRheTJfc3FydCkpICsKICBnZW9tX2hpc3RvZ3JhbShhZXMoeSA9IC4uZGVuc2l0eS4uKSwgCiAgICAgICAgICAgICAgICAgY29sb3VyID0gImJsYWNrIiwgZmlsbCA9ICJ3aGl0ZSIsIGJpbnMgPSA0MCkgKyAKICBsYWJzKHggPSAiSHlnaWVuZSBzY29yZSBvbiBkYXkgMiIsIHkgPSAiRGVuc2l0eSIpICsKICBzdGF0X2Z1bmN0aW9uKGZ1biA9IGRub3JtLCBhcmdzID0gbGlzdChtZWFuID0gbWVhbihza2V3X2RmJGRheTJfc3FydCwgbmEucm0gPSBUUlVFKSwgc2QgPSBzZChza2V3X2RmJGRheTJfc3FydCwgbmEucm0gPSBUUlVFKSksIGNvbG91ciA9ICJibGFjayIsIHNpemUgPSAxKQoKcHJpbnQoaGlzdC5kYXkyX3NxcnQpCmBgYAoKIyMjIyBDb25zaWRlcmF0aW9ucwoKU29tZXRpbWVzIHRyYW5zZm9ybWF0aW9ucyBjcmVhdGUgbW9yZSBwcm9ibGVtcyB0aGFuIHRoZXkgc29sdmUgKG5vdCBzaG93biwgYnV0IGRheSAxIHNjb3JlcyB3ZXJlIG5vcm1hbCwgYnV0IGhhdmUgYSBzbGlnaHQgc2tldyBhZnRlciB0aGUgdHJhbnNmb3JtYXRpb25zKS4gV2hhdCBhcmUgeW91ciBvdGhlciBvcHRpb25zIGZvciBub24tbm9ybWFsIGRhdGE/CiAgCiAgKiBOb24tcGFyYW1ldHJpYyB0ZXN0cwogICogQm9vdHN0cmFwcGluZwogICogRm9yIHplcm8taW5mbGF0ZWQgY291bnQgZGF0YSwgd2UgY2FuIHVzZSBwb2lzc29uIG9yIG5lZ2F0aXZlIGJpbm9taWFsIG1vZGVscwoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBSZXBsaWNhdGluZyBNYWRhbGluYSdzIFB5dGhvbiBjb2RlCgpgYGB7cn0KZGYgPC0gcmVhZF94bHN4KCIvVXNlcnMva2FyZWVuYWRlbHJvc2FyaW8vRG93bmxvYWRzL2RhdGEgKDEpLnhsc3giKQpgYGAKCmBgYHtyLCBtZXNzYWdlPUZBTFNFfQojIENyZWF0ZSBhIHNpbXBsZSByZWdyZXNzaW9uIHBsb3QKZ2dwbG90KGRmLCBhZXMoeCA9IEFnZSwgeSA9IEJFTElFRmNjKSkgKwogIGdlb21fcG9pbnQoY29sb3IgPSAiI0MwNkM4NCIsIGFscGhhID0gMC4zKSArICMgU2NhdHRlciBwbG90CiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgY29sb3IgPSAiIzdEMDU1MiIsIHNpemUgPSAxKSArICMgUmVncmVzc2lvbiBsaW5lCiAgbGFicyh4ID0gIkFnZSIsIHkgPSAiQmVsaWVmIGluIHNjaWVuY2UiKSArICMgTGFiZWwgdGhlIHggYW5kIHkgYXhpcwogIHRoZW1lX21pbmltYWwoKSArICMgVXNlIGEgbWluaW1hbCB0aGVtZQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgIyBSZW1vdmUgbGVnZW5kCgojIFNhdmUgZmlndXJlCiMgZ2dzYXZlKCJmaWd1cmUudGlmIiwgd2lkdGggPSA1LCBoZWlnaHQgPSA0LCBkcGkgPSAzMDAsIHR5cGUgPSAiY2Fpcm8tcG5nIikKYGBgCgpgYGB7cn0KbWQgPC0gbG0oQkVMSUVGY2MgfiBBZ2UsIGRhdGEgPSBkZikKCnN1bW1hcnkobWQpCmBgYAoKYGBge3IsIG1lc3NhZ2U9RkFMU0V9CmRmX3Bsb3QgPC0gZGYgJT4lIAogIG11dGF0ZShHZW5kZXIuZiA9IGNhc2Vfd2hlbigKICAgIEdlbmRlciA9PSAxIH4gIkZlbWFsZSIsCiAgICBHZW5kZXIgPT0gMiB+ICJNYWxlIikpICU+JQogIG11dGF0ZShHZW5kZXIuZiA9IGZhY3RvcihHZW5kZXIuZiwgbGV2ZWxzID0gYygiRmVtYWxlIiwgIk1hbGUiKSkpICU+JSAKICBmaWx0ZXIoIWlzLm5hKEdlbmRlci5mKSkKCmdncGxvdChkZl9wbG90LCBhZXMoeD1HZW5kZXIuZiwgeT1CRUxJRUZjYywgZmlsbCA9IEdlbmRlci5mKSkgKwogIGdlb21fYmFyKHN0YXQ9InN1bW1hcnkiLCBwb3NpdGlvbiA9ICJkb2RnZSIpICsKICB5bGltKDAsIDEwMCkgKwogIHRoZW1lX21pbmltYWwoKSArICMgT3B0aW9uYWw6IEFkZHMgYSBtaW5pbWFsIHRoZW1lIGZvciBhZXN0aGV0aWNzCiAgbGFicyh4PSJQYXJ0eSIsIHk9IlRydXN0IikgIyBMYWJlbGluZyBheGVzCmBgYAoKTnVtZXJpYyB2cyBDYXRlZ29yaWNhbCBQcmVkaWN0b3JzCmBgYHtyIGNhdGVnb3JpY2FsIHByZWRpY3RvcnN9CiMgTnVtZXJpYwpkZiRHZW5kZXIgPC0gYXMubnVtZXJpYyhkZiRHZW5kZXIpCm1vZGVsIDwtIGxtKEJFTElFRmNjIH4gQWdlICsgR2VuZGVyLCBkYXRhID0gZGYpCnN1bW1hcnkobW9kZWwpIApgYGAKCmBgYHtyfQojIEZhY3RvcgpkZiRHZW5kZXIgPC0gZmFjdG9yKGRmJEdlbmRlcikKbW9kZWwgPC0gbG0oQkVMSUVGY2MgfiBBZ2UgKyBHZW5kZXIsIGRhdGEgPSBkZikKc3VtbWFyeShtb2RlbCkgCmBgYAoKV2hhdCBpZiBJIHdhbnQgdG8gY2hhbmdlIHRoZSByZWZlcmVuY2UgZ3JvdXA/IEhvdyBjYW4gSSBkbyB0aGF0PwoKCgoK