Advantages of R for data analysis

  1. Cutting edge analyses. In psychology R is the standard package to run mixed effects models. It also interfaces with Stan (Bayesian Analysis), via a package called brms
  2. Variety of statistical methods. It’s huge and much larger than commercial packages, e.g. SPSS
  3. Transparency of code. The code resembles the underlying mathematical model.
  4. Statistical models are stored in memory and are open to exploration/interrogation. For example, it is easy to inspect the residuals of our model.

Correlations

Simple pairwise correlation

Main function for a pairwise correlation is cor.test. Have a look at the example. Note that we have used the apa library to turn this into a nice output.

df = read_csv("WHR_2017.csv")
Rows: 155 Columns: 13── Column specification ──────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr  (2): country, region
dbl (11): happiness_rank, happiness_score, whisker_high, whisker_low, gdp_per_capita, family, ...
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.

Okay, we have reported this, but it doesn’t look very “publication-ready”. R has a range of packages which convert the results of analyses into publication-ready formats.

cor_apa(cor)
r(153) = .81, p < .001

Often, you will need to report your results “in-line” (i.e. in the body of your text). To do this in R you use backticks. There are 3 backticks for a block, but only 1 for in-line code (and no curly brackets).

For example… the correlation between GDP per capita and happiness scores is .

EXERCISE - EXPLORING CORRELATIONS

Explore other correlations in the data!!

Multiple pairwise comparisons

This is a bit more tricky. The best approach is to create a dataframe containing only those variables that you wish to conduct the pairwise correlations on. The cor function from base R creates a correlation matrix. But results are not very publication-ready. They can be tidied up via the kable package (in knitr). There is also the fantastic corrplot which will give you a great graphical representation.

Running a model

Basic syntax of a regression model

R regression syntax


df = read_csv("WHR_2017.csv")

mod_hs_gdp <- lm(happiness_score ~ gdp_per_capita, data = df)

# "mod" = model.
# "hs_gdp" = hs is predicted by gdp

mod_hs_gdp # This shows you the key statistics, but it's not very informative

summary(mod_hs_gdp) # This is much more informative but it's not pretty to look at

tidy(summary(mod_hs_gdp)) # `tidy` from the `broom` package makes it into a very nice table

# or to make this a bit more "publication-ready"

knitr::kable(tidy(summary(mod_hs_gdp)))

The table looks nice, but often, at least in Psychology, results are reported in-line. A good way to do this is to create your own function.


report_coef = function(mod){
  sm = summary(mod)
  results_vector = rep("", length(mod$coefficients))
  for (i in 1:length(mod$coefficients)){
    results_vector[i] = paste0("β = ", round(sm$coefficients[i, 1], 2), ", t = ", round(sm$coefficients[i, 3], 2), ", p = ", sprintf("%.3f", round(sm$coefficients[i, 4], 3)))
  }
  return(results_vector)
}
  

There was a significant effect of GDP per capita: β = 3.2, t = 23.62, p = 0.000

NB the code to create the function is tricky. It took me 20 minutes to write with a lot of googling. A newbie would spend much longer. However, if you are writing a thesis which involves the in-line reporting of the results of a large number of linear models, this could save you a lot of time in the long run.

Model diagnostics

But is it a good model for the data. Let’s do a plot to find out.

Residuals are close to normally distributed, according to both the histograms and QQ-plot. The relationship between residuals and fitted values does not suggest a problem.

EXERCISE - IMPACT OF GDP ON LIFE EXPECTANCY

Now switch the dependent variable to life expectancy. Do you think that the relationship will be linear? Why? Why not?

Test your assumptions by

  1. running a linear model
  2. running regression diagnostics.

You can use the above code as a crib, but try to refer to it as little as possible. The answer is below, but try not to look at it when doing this exercise.


df = read_csv("WHR_2017.csv")
Rows: 155 Columns: 13── Column specification ──────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr  (2): country, region
dbl (11): happiness_rank, happiness_score, whisker_high, whisker_low, gdp_per_capita, family, ...
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
mod_le_gdp = lm(life_expectancy ~ gdp_per_capita, data = df)

# "mod" = model.
# "hs_gdp" = hs is predicted by gdp

mod_le_gdp # This shows you the key statistics, but it's not very informative

Call:
lm(formula = life_expectancy ~ gdp_per_capita, data = df)

Coefficients:
   (Intercept)  gdp_per_capita  
       0.08361         0.47499  
summary(mod_le_gdp) # This is much more informative but it's not pretty to look at

Call:
lm(formula = life_expectancy ~ gdp_per_capita, data = df)

Residuals:
     Min       1Q   Median       3Q      Max 
-0.44149 -0.06442  0.02448  0.08073  0.21928 

Coefficients:
               Estimate Std. Error t value Pr(>|t|)    
(Intercept)     0.08361    0.02622   3.189  0.00173 ** 
gdp_per_capita  0.47499    0.02450  19.391  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.1279 on 153 degrees of freedom
Multiple R-squared:  0.7108,    Adjusted R-squared:  0.7089 
F-statistic:   376 on 1 and 153 DF,  p-value: < 2.2e-16
tidy(summary(mod_le_gdp)) # `tidy` from the `broom` package makes it into a very nice table
NA
residuals = resid(mod_le_gdp)

hist(residuals, breaks = 20)


plot(fitted(mod_le_gdp), residuals); abline(0,0)


qqnorm(residuals); qqline(residuals)


g = ggplot(data = df, aes(x = gdp_per_capita, y = life_expectancy))
g = g + geom_point()
g = g + geom_smooth()
g


g = ggplot(data = df, aes(x = gdp_per_capita, y = life_expectancy, label = country))
g = g + geom_point(colour = "light blue")
g = g + geom_smooth(se = FALSE)
g = g + geom_text(size = 2.5, nudge_y = -0.02, alpha = 0.6)

g

So, the relationship is fairly linear, but there are a couple of issues. First of all there is a natural ceiling to life expectancy so that it does not improve beyond a certain GDP. Secondly, there are some countries where life expectancy is much lower than their GDP would predict. Can you guess which countries fall into this category?

Piping within models / ggplot

You can add piping within models / plots, e.g.



g = ggplot(data = df %>% filter(region != "Africa"),
           aes(x = gdp_per_capita, y = life_expectancy))
g = g + geom_point()
g = g + geom_smooth()
g

NA
NA

Generalised linear model

A variety of models which are designed for data where distributions depart radically from the normal distribution.

  1. Logistic regression for dichotomous (binary) data
  2. Poisson regression for count data (which tends to have a right skew)
  3. Negative binonmial for poisson-type data where rightward skew is extremely severe

We’re going to look at logistic regression.

Picture of sigmoid from Wikipedia Therefore we need to use a special mathematical transformation to turn it into a straight line. This transformation is “built into” the model.

Dichotomising a variable

We’re going to turn one of our variables into a dichotomous variable using a “median-split”

as.numeric(df$happiness_score >= median(df$happiness_score))
  [1] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
 [47] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 [93] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
[139] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

Now we’re going to run our regression model. We’re going to look at whether GDP influences whether happiness is above or below the mdedian.

EXERCISE - USING GOOGLE TO RUN A LOGISTIC REGRESSION

summary(mod)

Call:
glm(formula = happiness_score_ms ~ scale(gdp_per_capita), family = "binomial", 
    data = df)

Deviance Residuals: 
     Min        1Q    Median        3Q       Max  
-2.07237  -0.39310   0.07613   0.49431   2.06556  

Coefficients:
                      Estimate Std. Error z value Pr(>|z|)    
(Intercept)            -0.2485     0.2514  -0.988    0.323    
scale(gdp_per_capita)   2.8926     0.4629   6.248 4.15e-10 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 214.87  on 154  degrees of freedom
Residual deviance: 109.48  on 153  degrees of freedom
AIC: 113.48

Number of Fisher Scoring iterations: 6

Because the coefficient is hard to interpret, we need to convert it into odds ratios. This value will be 1 if there is no relationship, > 1 if there is a positive relationship and < 1 if there is a negative relationship. We used the exp function to do this.


exp(coef(mod))
          (Intercept) scale(gdp_per_capita) 
            0.7800031            18.0402769 
exp(mod$coefficients)
          (Intercept) scale(gdp_per_capita) 
            0.7800031            18.0402769 

NB This odds ratio is far too large. A possible issue is that we have not “scaled” our independent variable. Scaling will give a mean of zero and a standard deviation of plus or minus one.


mod = glm(happiness_score_ms ~ scale(gdp_per_capita), data = df, family = "binomial")

exp(coef(mod))

This is a more “sensible” odds ratio.

LS0tCnRpdGxlOiAiU2Vzc2lvbiAxIC0gSW50cm9kdWN0aW9uLlJtZCIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICBudW1iZXJfc2VjdGlvbnM6IHllcwogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogJzInCiAgICBkZl9wcmludDogcGFnZWQKICAgIHRvY19mbG9hdDoKICAgICAgY29sbGFwc2VkOiB5ZXMKLS0tCgoKCmBgYHtyIHNldHVwfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShicm9vbSkKbGlicmFyeShncmFwaGljcykKbGlicmFyeShwYWNtYW4pCnRoZW1lX3NldCh0aGVtZV9idygpKSAjIFNldCB0aGUgc3RhbmRhcmQgYmxhY2sgYW5kIHdoaXRlIHRoZW1lCmBgYAoKIyBBZHZhbnRhZ2VzIG9mIFIgZm9yIGRhdGEgYW5hbHlzaXMKCjEuICpDdXR0aW5nIGVkZ2UqIGFuYWx5c2VzLiBJbiBwc3ljaG9sb2d5IFIgaXMgdGhlIHN0YW5kYXJkIHBhY2thZ2UgdG8gcnVuIF9taXhlZCBlZmZlY3RzIG1vZGVsc18uIEl0IGFsc28gaW50ZXJmYWNlcyB3aXRoIF9TdGFuXyAoQmF5ZXNpYW4gQW5hbHlzaXMpLCB2aWEgYSBwYWNrYWdlIGNhbGxlZCBfYnJtc18KMi4gKlZhcmlldHkqIG9mIHN0YXRpc3RpY2FsIG1ldGhvZHMuIEl0J3MgaHVnZSBhbmQgbXVjaCBsYXJnZXIgdGhhbiBjb21tZXJjaWFsIHBhY2thZ2VzLCBlLmcuIFNQU1MKMy4gKlRyYW5zcGFyZW5jeSogb2YgY29kZS4gVGhlIGNvZGUgcmVzZW1ibGVzIHRoZSB1bmRlcmx5aW5nIG1hdGhlbWF0aWNhbCBtb2RlbC4KNC4gU3RhdGlzdGljYWwgbW9kZWxzIGFyZSAqc3RvcmVkIGluIG1lbW9yeSAqYW5kIGFyZSBvcGVuIHRvIGV4cGxvcmF0aW9uL2ludGVycm9nYXRpb24uIEZvciBleGFtcGxlLCBpdCBpcyBlYXN5IHRvIGluc3BlY3QgdGhlIHJlc2lkdWFscyBvZiBvdXIgbW9kZWwuCgojIENvcnJlbGF0aW9ucwoKIyMgU2ltcGxlIHBhaXJ3aXNlIGNvcnJlbGF0aW9uCgpNYWluIGZ1bmN0aW9uIGZvciBhIHBhaXJ3aXNlIGNvcnJlbGF0aW9uIGlzIGBjb3IudGVzdGAuIEhhdmUgYSBsb29rIGF0IHRoZSBleGFtcGxlLiBOb3RlIHRoYXQgd2UgaGF2ZSB1c2VkIHRoZSBgYXBhYCBsaWJyYXJ5IHRvIHR1cm4gdGhpcyBpbnRvIGEgbmljZSBvdXRwdXQuCgpgYGB7ciByZWFkaW5nIGluIGRhdGEgYW5kIHJ1bm5pbmcgYSBwYWlyd2lzZSBjb3JyZWxhdGlvbn0KCmRmID0gcmVhZF9jc3YoIldIUl8yMDE3LmNzdiIpCgpjb3IgPSBjb3IudGVzdChkZiRnZHBfcGVyX2NhcGl0YSwgZGYkaGFwcGluZXNzX3Njb3JlKQoKIyBOb3RlIHRoYXQgdGhlcmUgaXMgYW4gb3B0aW9uIHRvIGNob29zZSB0aGUgbWV0aG9kOiBgbWV0aG9kID0gYygicGVhcnNvbiIsICJrZW5kYWxsIiwgInNwZWFybWFuIilgCgpjb3IKCmBgYAoKT2theSwgd2UgaGF2ZSByZXBvcnRlZCB0aGlzLCBidXQgaXQgZG9lc24ndCBsb29rIHZlcnkgInB1YmxpY2F0aW9uLXJlYWR5Ii4gUiBoYXMgYSByYW5nZSBvZiBwYWNrYWdlcyB3aGljaCBjb252ZXJ0IHRoZSByZXN1bHRzIG9mIGFuYWx5c2VzIGludG8gcHVibGljYXRpb24tcmVhZHkgZm9ybWF0cy4KCgpgYGB7ciByZXBvcnRpbmcgY29ycmVsYXRpb24gcmVzdWx0cyAiaW4tbGluZSJ9CgpsaWJyYXJ5KGFwYSkgIyBBUEEtc3R5bGUgcmVwb3J0aW5nIG9mIHN0YXRpc3RpY2FsIHRlc3RzCgpjb3JfYXBhKGNvcikKCmBgYAoKT2Z0ZW4sIHlvdSB3aWxsIG5lZWQgdG8gcmVwb3J0IHlvdXIgcmVzdWx0cyAiaW4tbGluZSIgKGkuZS4gaW4gdGhlIGJvZHkgb2YgeW91ciB0ZXh0KS4gVG8gZG8gdGhpcyBpbiBSIHlvdSB1c2UgYmFja3RpY2tzLiBUaGVyZSBhcmUgMyBiYWNrdGlja3MgZm9yIGEgYmxvY2ssIGJ1dCBvbmx5IDEgZm9yIGluLWxpbmUgY29kZSAoYW5kIG5vIGN1cmx5IGJyYWNrZXRzKS4KCkZvciBleGFtcGxlLi4uIHRoZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIEdEUCBwZXIgY2FwaXRhIGFuZCBoYXBwaW5lc3Mgc2NvcmVzIGlzIGByIGNvcl9hcGEoY29yKWAuCgoKIyMgRVhFUkNJU0UgLSBFWFBMT1JJTkcgQ09SUkVMQVRJT05TCgpFeHBsb3JlIG90aGVyIGNvcnJlbGF0aW9ucyBpbiB0aGUgZGF0YSEhCgpgYGB7ciBHTyBFWFBMT1JFIX0KCgoKYGBgCgoKIyMgTXVsdGlwbGUgcGFpcndpc2UgY29tcGFyaXNvbnMKClRoaXMgaXMgYSBiaXQgbW9yZSB0cmlja3kuIFRoZSBiZXN0IGFwcHJvYWNoIGlzIHRvIGNyZWF0ZSBhIGRhdGFmcmFtZSBjb250YWluaW5nIG9ubHkgdGhvc2UgdmFyaWFibGVzIHRoYXQgeW91IHdpc2ggdG8gY29uZHVjdCB0aGUgcGFpcndpc2UgY29ycmVsYXRpb25zIG9uLiBUaGUgYGNvcmAgZnVuY3Rpb24gZnJvbSBiYXNlIFIgY3JlYXRlcyBhIGNvcnJlbGF0aW9uIG1hdHJpeC4gQnV0IHJlc3VsdHMgYXJlIG5vdCB2ZXJ5IHB1YmxpY2F0aW9uLXJlYWR5LiBUaGV5IGNhbiBiZSB0aWRpZWQgdXAgdmlhIHRoZSBga2FibGVgIHBhY2thZ2UgKGluIGBrbml0cmApLiBUaGVyZSBpcyBhbHNvIHRoZSBmYW50YXN0aWMgYGNvcnJwbG90YCB3aGljaCB3aWxsIGdpdmUgeW91IGEgZ3JlYXQgZ3JhcGhpY2FsIHJlcHJlc2VudGF0aW9uLgoKYGBge3IgY3JlYXRpbmcgY29ycmVsYXRpb24gdGFibGVzfQoKZGYgJT4lIAogIHNlbGVjdChoYXBwaW5lc3Nfc2NvcmUsIGdkcF9wZXJfY2FwaXRhLCBsaWZlX2V4cGVjdGFuY3ksIGZhbWlseSwgZnJlZWRvbSwgZ2VuZXJvc2l0eSwgdHJ1c3RfaW5fZ292ZXJubWVudCkgLT4KICBkZi5jb3IKCmNvcihkZi5jb3IpCgpsaWJyYXJ5KGNvcnJ0YWJsZSkKCmNtID0gICBjb3JyZWxhdGlvbl9tYXRyaXgoCiAgICAgICAgICBkZi5jb3IsCiAgICAgICAgICB0eXBlID0gInBlYXJzb24iLAogICAgICAgICAgZGlnaXRzID0gMiwKICAgICAgICAgIGRlY2ltYWwubWFyayA9ICIuIiwKICAgICAgICAgIHVzZSA9ICJhbGwiLAogICAgICAgICAgc2hvd19zaWduaWZpY2FuY2UgPSBUUlVFLAogICAgICAgICAgcmVwbGFjZV9kaWFnb25hbCA9IFRSVUUsCiAgICAgICAgICByZXBsYWNlbWVudCA9ICIiCikKCmNtCgprbml0cjo6a2FibGUoY20sICJodG1sIikKCmxpYnJhcnkoY29ycnBsb3QpCgpjb3JycGxvdChjb3IoZGYuY29yKSkKCmBgYAoKCgojIFJ1bm5pbmcgYSBtb2RlbAoKIyMgQmFzaWMgc3ludGF4IG9mIGEgcmVncmVzc2lvbiBtb2RlbAoKIVtSIHJlZ3Jlc3Npb24gc3ludGF4XShpbWFnZXMvcmVncmVzc2lvbl9zeW50YXgucG5nKQoKYGBge3IgcnVubmluZyBhbmQgcmVwb3J0aW5nIGEgbGluZWFyIG1vZGVsfQoKZGYgPSByZWFkX2NzdigiV0hSXzIwMTcuY3N2IikKCm1vZF9oc19nZHAgPC0gbG0oaGFwcGluZXNzX3Njb3JlIH4gZ2RwX3Blcl9jYXBpdGEsIGRhdGEgPSBkZikKCiMgIm1vZCIgPSBtb2RlbC4KIyAiaHNfZ2RwIiA9IGhzIGlzIHByZWRpY3RlZCBieSBnZHAKCm1vZF9oc19nZHAgIyBUaGlzIHNob3dzIHlvdSB0aGUga2V5IHN0YXRpc3RpY3MsIGJ1dCBpdCdzIG5vdCB2ZXJ5IGluZm9ybWF0aXZlCgpzdW1tYXJ5KG1vZF9oc19nZHApICMgVGhpcyBpcyBtdWNoIG1vcmUgaW5mb3JtYXRpdmUgYnV0IGl0J3Mgbm90IHByZXR0eSB0byBsb29rIGF0Cgp0aWR5KHN1bW1hcnkobW9kX2hzX2dkcCkpICMgYHRpZHlgIGZyb20gdGhlIGBicm9vbWAgcGFja2FnZSBtYWtlcyBpdCBpbnRvIGEgdmVyeSBuaWNlIHRhYmxlCgojIG9yIHRvIG1ha2UgdGhpcyBhIGJpdCBtb3JlICJwdWJsaWNhdGlvbi1yZWFkeSIKCmtuaXRyOjprYWJsZSh0aWR5KHN1bW1hcnkobW9kX2hzX2dkcCkpKQoKYGBgCgpUaGUgdGFibGUgbG9va3MgbmljZSwgYnV0IG9mdGVuLCBhdCBsZWFzdCBpbiBQc3ljaG9sb2d5LCByZXN1bHRzIGFyZSByZXBvcnRlZCBpbi1saW5lLiBBIGdvb2Qgd2F5IHRvIGRvIHRoaXMgaXMgdG8gY3JlYXRlIHlvdXIgb3duIGZ1bmN0aW9uLgoKYGBge3IgY3JlYXRlIGZ1bmN0aW9uIHRvIHJlcG9ydCB0ZXJtcyBpbiBtb2RlbH0KCnJlcG9ydF9jb2VmID0gZnVuY3Rpb24obW9kKXsKICBzbSA9IHN1bW1hcnkobW9kKQogIHJlc3VsdHNfdmVjdG9yID0gcmVwKCIiLCBsZW5ndGgobW9kJGNvZWZmaWNpZW50cykpCiAgZm9yIChpIGluIDE6bGVuZ3RoKG1vZCRjb2VmZmljaWVudHMpKXsKICAgIHJlc3VsdHNfdmVjdG9yW2ldID0gcGFzdGUwKCLOsiA9ICIsIHJvdW5kKHNtJGNvZWZmaWNpZW50c1tpLCAxXSwgMiksICIsIHQgPSAiLCByb3VuZChzbSRjb2VmZmljaWVudHNbaSwgM10sIDIpLCAiLCBwID0gIiwgc3ByaW50ZigiJS4zZiIsIHJvdW5kKHNtJGNvZWZmaWNpZW50c1tpLCA0XSwgMykpKQogIH0KICByZXR1cm4ocmVzdWx0c192ZWN0b3IpCn0KICAKYGBgCgpUaGVyZSB3YXMgYSBzaWduaWZpY2FudCBlZmZlY3Qgb2YgR0RQIHBlciBjYXBpdGE6IGByIHJlcG9ydF9jb2VmKG1vZF9oc19nZHApWzFdYAoKTkIgdGhlIGNvZGUgdG8gY3JlYXRlIHRoZSBmdW5jdGlvbiBpcyB0cmlja3kuIEl0IHRvb2sgbWUgMjAgbWludXRlcyB0byB3cml0ZSB3aXRoIGEgbG90IG9mIGdvb2dsaW5nLiBBIG5ld2JpZSB3b3VsZCBzcGVuZCBtdWNoIGxvbmdlci4gSG93ZXZlciwgaWYgeW91IGFyZSB3cml0aW5nIGEgdGhlc2lzIHdoaWNoIGludm9sdmVzIHRoZSBpbi1saW5lIHJlcG9ydGluZyBvZiB0aGUgcmVzdWx0cyBvZiBhIGxhcmdlIG51bWJlciBvZiBsaW5lYXIgbW9kZWxzLCB0aGlzIGNvdWxkIHNhdmUgeW91IGEgbG90IG9mIHRpbWUgaW4gdGhlIGxvbmcgcnVuLgoKIyMgTW9kZWwgZGlhZ25vc3RpY3MKCkJ1dCBpcyBpdCBhIGdvb2QgbW9kZWwgZm9yIHRoZSBkYXRhLiBMZXQncyBkbyBhIHBsb3QgdG8gZmluZCBvdXQuCgpgYGB7ciBwbG90dGluZyB0aGUgZGF0YX0KCmcgPSBnZ3Bsb3QoZGF0YSA9IGRmLCBhZXMoeCA9IGdkcF9wZXJfY2FwaXRhLCB5ID0gaGFwcGluZXNzX3Njb3JlKSkKZyA9IGcgKyBnZW9tX3BvaW50KCkKZyA9IGcgKyBnZW9tX3Ntb290aCgpCmcKCmBgYAoKCmBgYHtyIHBsb3R0aW5nIHRoZSByZXNpZHVhbHN9CnJlc2lkdWFscyA9IHJlc2lkKG1vZF9oc19nZHApCgpoaXN0KHJlc2lkdWFscywgYnJlYWtzID0gMjApCgojIE5CIHJlc2lkdWFscyBjYW4gYWxzbyBiZSBvYnRhaW5lZCBieSBwZWVraW5nIGluc2lkZSB0aGUgc3RvcmVkIG1vZGVsLgoKaGlzdChtb2RfaHNfZ2RwJHJlc2lkdWFscywgYnJlYWtzID0gMjApCgpwbG90KGZpdHRlZChtb2RfaHNfZ2RwKSwgcmVzaWR1YWxzKQphYmxpbmUoMCwwKQoKIyBOb3RlIHRoYXQgYm90aCBsaW5lcyBuZWVkIHRvIGJlIHJ1biBhdCB0aGUgc2FtZSB0aW1lLgojIFRvIGRvIHRoaXMsIHlvdSBjYW4gdXNlIGEgc2VtaS1jb2xvbi4KCnBsb3QoZml0dGVkKG1vZF9oc19nZHApLCByZXNpZHVhbHMpOyBhYmxpbmUoMCwwKQoKIyBBbmQgaGVyZSBpcyBhIHFxLXBsb3QuIEl0IHBsb3RzIHRoZSBhY3R1YWwgcmVzaWR1YWxzIGFnYWluc3QgInRoZW9yZXRpY2FsIiByZXNpZHVhbHMgYmFzZWQgb24gdGhlIGFzc3VtcHRpb24gdGhhdCB0aGV5IGFyZSBub3JtYWxseSBkaXN0cmlidXRlZC4KCnFxbm9ybShyZXNpZHVhbHMpOyBxcWxpbmUocmVzaWR1YWxzKQoKYGBgCgpSZXNpZHVhbHMgYXJlIGNsb3NlIHRvIG5vcm1hbGx5IGRpc3RyaWJ1dGVkLCBhY2NvcmRpbmcgdG8gYm90aCB0aGUgaGlzdG9ncmFtcyBhbmQgUVEtcGxvdC4gVGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHJlc2lkdWFscyBhbmQgZml0dGVkIHZhbHVlcyBkb2VzIG5vdCBzdWdnZXN0IGEgcHJvYmxlbS4KCiFbXShpbWFnZXMvcmVzaWR1YWxzX3Bsb3QucG5nKQoKIyMgRVhFUkNJU0UgLSBJTVBBQ1QgT0YgR0RQIE9OIExJRkUgRVhQRUNUQU5DWQoKTm93IHN3aXRjaCB0aGUgZGVwZW5kZW50IHZhcmlhYmxlIHRvIGxpZmUgZXhwZWN0YW5jeS4gRG8geW91IHRoaW5rIHRoYXQgdGhlIHJlbGF0aW9uc2hpcCB3aWxsIGJlIGxpbmVhcj8gV2h5PyBXaHkgbm90PwoKVGVzdCB5b3VyIGFzc3VtcHRpb25zIGJ5CgooYSkgcnVubmluZyBhIGxpbmVhciBtb2RlbAooYikgcnVubmluZyByZWdyZXNzaW9uIGRpYWdub3N0aWNzLgoKWW91IGNhbiB1c2UgdGhlIGFib3ZlIGNvZGUgYXMgYSBjcmliLCBidXQgdHJ5IHRvIHJlZmVyIHRvIGl0IGFzIGxpdHRsZSBhcyBwb3NzaWJsZS4gVGhlIGFuc3dlciBpcyBiZWxvdywgYnV0IHRyeSBub3QgdG8gbG9vayBhdCBpdCB3aGVuIGRvaW5nIHRoaXMgZXhlcmNpc2UuCgpgYGB7ciBPVkVSIFRPIFlPVSEhIX0KCgoKCgoKYGBgCgoKIVtdKGltYWdlcy9ub19wZWVraW5nLnBuZykKCmBgYHtyIH0KCmRmID0gcmVhZF9jc3YoIldIUl8yMDE3LmNzdiIpCgptb2RfbGVfZ2RwID0gbG0obGlmZV9leHBlY3RhbmN5IH4gZ2RwX3Blcl9jYXBpdGEsIGRhdGEgPSBkZikKCiMgIm1vZCIgPSBtb2RlbC4KIyAiaHNfZ2RwIiA9IGhzIGlzIHByZWRpY3RlZCBieSBnZHAKCm1vZF9sZV9nZHAgIyBUaGlzIHNob3dzIHlvdSB0aGUga2V5IHN0YXRpc3RpY3MsIGJ1dCBpdCdzIG5vdCB2ZXJ5IGluZm9ybWF0aXZlCgpzdW1tYXJ5KG1vZF9sZV9nZHApICMgVGhpcyBpcyBtdWNoIG1vcmUgaW5mb3JtYXRpdmUgYnV0IGl0J3Mgbm90IHByZXR0eSB0byBsb29rIGF0Cgp0aWR5KHN1bW1hcnkobW9kX2xlX2dkcCkpICMgYHRpZHlgIGZyb20gdGhlIGBicm9vbWAgcGFja2FnZSBtYWtlcyBpdCBpbnRvIGEgdmVyeSBuaWNlIHRhYmxlCgpgYGAKCmBgYHtyfQpyZXNpZHVhbHMgPSByZXNpZChtb2RfbGVfZ2RwKQoKaGlzdChyZXNpZHVhbHMsIGJyZWFrcyA9IDIwKQoKcGxvdChmaXR0ZWQobW9kX2xlX2dkcCksIHJlc2lkdWFscyk7IGFibGluZSgwLDApCgpxcW5vcm0ocmVzaWR1YWxzKTsgcXFsaW5lKHJlc2lkdWFscykKCmBgYAoKCmBgYHtyfQoKZyA9IGdncGxvdChkYXRhID0gZGYsIGFlcyh4ID0gZ2RwX3Blcl9jYXBpdGEsIHkgPSBsaWZlX2V4cGVjdGFuY3kpKQpnID0gZyArIGdlb21fcG9pbnQoKQpnID0gZyArIGdlb21fc21vb3RoKCkKZwoKYGBgCgpgYGB7cn0KCmcgPSBnZ3Bsb3QoZGF0YSA9IGRmLCBhZXMoeCA9IGdkcF9wZXJfY2FwaXRhLCB5ID0gbGlmZV9leHBlY3RhbmN5LCBsYWJlbCA9IGNvdW50cnkpKQpnID0gZyArIGdlb21fcG9pbnQoY29sb3VyID0gImxpZ2h0IGJsdWUiKQpnID0gZyArIGdlb21fc21vb3RoKHNlID0gRkFMU0UpCmcgPSBnICsgZ2VvbV90ZXh0KHNpemUgPSAyLjUsIG51ZGdlX3kgPSAtMC4wMiwgYWxwaGEgPSAwLjYpCgpnCgpgYGAKCgpTbywgdGhlIHJlbGF0aW9uc2hpcCBpcyBmYWlybHkgbGluZWFyLCBidXQgdGhlcmUgYXJlIGEgY291cGxlIG9mIGlzc3Vlcy4gRmlyc3Qgb2YgYWxsIHRoZXJlIGlzIGEgbmF0dXJhbCBjZWlsaW5nIHRvIGxpZmUgZXhwZWN0YW5jeSBzbyB0aGF0IGl0IGRvZXMgbm90IGltcHJvdmUgYmV5b25kIGEgY2VydGFpbiBHRFAuIFNlY29uZGx5LCB0aGVyZSBhcmUgc29tZSBjb3VudHJpZXMgd2hlcmUgbGlmZSBleHBlY3RhbmN5IGlzIG11Y2ggbG93ZXIgdGhhbiB0aGVpciBHRFAgd291bGQgcHJlZGljdC4gQ2FuIHlvdSBndWVzcyB3aGljaCBjb3VudHJpZXMgZmFsbCBpbnRvIHRoaXMgY2F0ZWdvcnk/CgojIyBQaXBpbmcgd2l0aGluIG1vZGVscyAvIGdncGxvdAoKWW91IGNhbiBhZGQgcGlwaW5nIHdpdGhpbiBtb2RlbHMgLyBwbG90cywgZS5nLgoKYGBge3IgcGlwaW5nIHdpdGhpbiBtb2RlbHMgYW5kIHBsb3RzfQoKCmcgPSBnZ3Bsb3QoZGF0YSA9IGRmICU+JSBmaWx0ZXIocmVnaW9uICE9ICJFdXJvcGUiKSwKICAgICAgICAgICBhZXMoeCA9IGdkcF9wZXJfY2FwaXRhLCB5ID0gbGlmZV9leHBlY3RhbmN5KSkKZyA9IGcgKyBnZW9tX3BvaW50KCkKZyA9IGcgKyBnZW9tX3Ntb290aCgpCmcKCm1vZF9sZV9nZHBfbm9fZXVyb3BlID0gbG0obGlmZV9leHBlY3RhbmN5IH4gZ2RwX3Blcl9jYXBpdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IGRmICU+JSBmaWx0ZXIocmVnaW9uICE9ICJFdXJvcGUiKSkKCmBgYAoKCiMgR2VuZXJhbGlzZWQgbGluZWFyIG1vZGVsCgpBIHZhcmlldHkgb2YgbW9kZWxzIHdoaWNoIGFyZSBkZXNpZ25lZCBmb3IgZGF0YSB3aGVyZSBkaXN0cmlidXRpb25zIGRlcGFydCByYWRpY2FsbHkgZnJvbSB0aGUgbm9ybWFsIGRpc3RyaWJ1dGlvbi4KCjEuICpMb2dpc3RpYyByZWdyZXNzaW9uKiBmb3IgZGljaG90b21vdXMgKGJpbmFyeSkgZGF0YQoyLiAqUG9pc3NvbiByZWdyZXNzaW9uKiBmb3IgY291bnQgZGF0YSAod2hpY2ggdGVuZHMgdG8gaGF2ZSBhIHJpZ2h0IHNrZXcpCjMuICpOZWdhdGl2ZSBiaW5vbm1pYWwqIGZvciBwb2lzc29uLXR5cGUgZGF0YSB3aGVyZSByaWdodHdhcmQgc2tldyBpcyBleHRyZW1lbHkgc2V2ZXJlCgpXZSdyZSBnb2luZyB0byBsb29rIGF0IGxvZ2lzdGljIHJlZ3Jlc3Npb24uCgohW1BpY3R1cmUgb2Ygc2lnbW9pZCBmcm9tIFdpa2lwZWRpYV0oaW1hZ2VzL3NpZ21vaWQucG5nKQpUaGVyZWZvcmUgd2UgbmVlZCB0byB1c2UgYSBzcGVjaWFsIG1hdGhlbWF0aWNhbCB0cmFuc2Zvcm1hdGlvbiB0byB0dXJuIGl0IGludG8gYSBzdHJhaWdodCBsaW5lLiBUaGlzIHRyYW5zZm9ybWF0aW9uIGlzICJidWlsdCBpbnRvIiB0aGUgbW9kZWwuCgojIyBEaWNob3RvbWlzaW5nIGEgdmFyaWFibGUKCldlJ3JlIGdvaW5nIHRvIHR1cm4gb25lIG9mIG91ciB2YXJpYWJsZXMgaW50byBhIGRpY2hvdG9tb3VzIHZhcmlhYmxlIHVzaW5nIGEgIm1lZGlhbi1zcGxpdCIKCmBgYHtyIGNyZWF0aW5nIGEgbWVkaWFuIHNwbGl0fQoKZGYkaGFwcGluZXNzX3Njb3JlX21zID0gYXMubnVtZXJpYyhkZiRoYXBwaW5lc3Nfc2NvcmUgPj0gbWVkaWFuKGRmJGhhcHBpbmVzc19zY29yZSkpCgpgYGAKCk5vdyB3ZSdyZSBnb2luZyB0byBydW4gb3VyIHJlZ3Jlc3Npb24gbW9kZWwuIFdlJ3JlIGdvaW5nIHRvIGxvb2sgYXQgd2hldGhlciBHRFAgaW5mbHVlbmNlcyB3aGV0aGVyIGhhcHBpbmVzcyBpcyBhYm92ZSBvciBiZWxvdyB0aGUgbWRlZGlhbi4KCiMjIEVYRVJDSVNFIC0gVVNJTkcgR09PR0xFIFRPIFJVTiBBIExPR0lTVElDIFJFR1JFU1NJT04KCgpgYGB7ciBGSUdVUkUgT1VUIEhPVyBUTyBSVU4gQSBMT0dJU1RJQyBSRUdSRVNTSU9OIX0KCgoKCgpgYGAKCgohW10oaW1hZ2VzL25vX3BlZWtpbmcucG5nKQoKYGBge3J9Cgptb2QgPSBnbG0oaGFwcGluZXNzX3Njb3JlX21zIH4gZ2RwX3Blcl9jYXBpdGEsIGRhdGEgPSBkZiwgZmFtaWx5ID0gImJpbm9taWFsIikKCm1vZAoKc3VtbWFyeShtb2QpCgpgYGAKCkJlY2F1c2UgdGhlIGNvZWZmaWNpZW50IGlzIGhhcmQgdG8gaW50ZXJwcmV0LCB3ZSBuZWVkIHRvIGNvbnZlcnQgaXQgaW50byBvZGRzIHJhdGlvcy4gVGhpcyB2YWx1ZSB3aWxsIGJlIDEgaWYgdGhlcmUgaXMgbm8gcmVsYXRpb25zaGlwLCA+IDEgaWYgdGhlcmUgaXMgYSBwb3NpdGl2ZSByZWxhdGlvbnNoaXAgYW5kIDwgMSBpZiB0aGVyZSBpcyBhIG5lZ2F0aXZlIHJlbGF0aW9uc2hpcC4gV2UgdXNlZCB0aGUgYGV4cGAgZnVuY3Rpb24gdG8gZG8gdGhpcy4KCmBgYHtyfQoKZXhwKGNvZWYobW9kKSkKCmV4cChtb2QkY29lZmZpY2llbnRzKQoKYGBgCgpOQiBUaGlzIG9kZHMgcmF0aW8gaXMgZmFyIHRvbyBsYXJnZS4gQSBwb3NzaWJsZSBpc3N1ZSBpcyB0aGF0IHdlIGhhdmUgbm90ICJzY2FsZWQiIG91ciBpbmRlcGVuZGVudCB2YXJpYWJsZS4gU2NhbGluZyB3aWxsIGdpdmUgYSBtZWFuIG9mIHplcm8gYW5kIGEgc3RhbmRhcmQgZGV2aWF0aW9uIG9mIHBsdXMgb3IgbWludXMgb25lLgoKCmBgYHtyfQoKbW9kID0gZ2xtKGhhcHBpbmVzc19zY29yZV9tcyB+IHNjYWxlKGdkcF9wZXJfY2FwaXRhKSwgZGF0YSA9IGRmLCBmYW1pbHkgPSAiYmlub21pYWwiKQoKZXhwKGNvZWYobW9kKSkKCmBgYAoKVGhpcyBpcyBhIG1vcmUgInNlbnNpYmxlIiBvZGRzIHJhdGlvLgoKCgo=