The Exercise

Our goal is to simulate data with the following relationship between Y and X: \(Y = 2x + e\)

…where \(e =\) random variable with normal distribution (0, 1).

We will simulate this data such that:
\(E(X) = 2\)
\(V(X) = 4\)

Using expectation operators, we can also compute the following:
\(E(Y) = 4\)
\(V(Y) = 17\)
\(Cov(X,Y) = 8\)
\(Corr(X,Y) = .97\)

Loading Packages & Data

From Personal/Lab Computer

Load tidyverse to get access to ggplot2, dplyr, and tibble. Load broom for easy bootstrapping.

ipak <- function(pkg){
  new.pkg <- pkg[!(pkg %in% installed.packages()[, "Package"])]
  if (length(new.pkg)) 
    install.packages(new.pkg, dependencies = TRUE)
  sapply(pkg, require, character.only = TRUE)
}
packages <- c("tidyverse", "broom")
ipak(packages)
# Change ggplot theme to light background
old <- theme_set(theme_light(base_size = 12))

From Lab Computer

library(dplyr)
library(tibble)
library(broom)

Simulating the Data

First we create a dataframe using the tibble() function. This basically creates a dataframe with a more user-friendly output. Each argument for tibble() begins with the column name (or variable) you want to create for your dataframe (in this case “SubjID” to indicate the Subject ID number). Since we are simulating 1,000 subjects – we will set this variable equal to 1 through 1000. Instead of typing this out manually using the c() function (e.g., c(1, 2, 3, 4, …, 1000)) we will use the sequence operator : to create a vector of integers starting with 1 and ending with 1000.

tibble(SubjID = 1:1000)


We can create a second variable simply by adding a new argument. Let’s start by creating a continuous predictor variable XN (X normal) We can do this using the rnorm() function, which takes three arguments: (1) The number of random numbers to generate; (2) The mean of the normal distribution from which to draw from; and (3) The SD of the distribution from which to draw from. Specifically, let’s draw 1,000 numbers (since we have 1,000 subjects) from a normal distribution with mean = 2 and SD = 2.

tibble(SubjID = 1:1000,
       XN = rnorm(1000,2,2))


In addition, let’s create YN (Y normal) which will be equal to 2 times the value of XN plus random noise with mean = 0 and SD = 1.

tibble(SubjID = 1:1000,
       XN = rnorm(1000,2,2),
       YN = 2*XN + rnorm(1000,0,1))


Now let’s assign this dataframe to an object called lab_data. In addition, lets set the random number generator to today’s date so that we can reproduce this exact dataframe when we revisit this page.

set.seed(11092016) # Set seed to today's date
lab_data <- tibble(SubjID = 1:1000,
                   XN = rnorm(1000,2,2),
                   YN = 2*XN + rnorm(1000,0,1))
lab_data

BONUS: Export to SPSS

For those who are more comfortable conducting tests in SPSS, feel free to export your new simulated data into a .csv, which can then be easily imported into SPSS. We can do this with the write.csv() function which takes 3 important arguments: (1) The name of the dataframe you want to export (in this case, lab_data); (2) The name of the file you want to save your dataframe as (unless you specify the full path, it will export this file into your working directory); and (3) Whether you want the first column to indicate the row names of your data (e.g., 1, 2, 3, 4, … 1000). This row.names argument defaults to TRUE (which is often a nuisance) so let’s change that to FALSE so that we do not export row names.

## This is commented out at the moment but simply remove the hashtag in front of the line in order to export the data.
# write.csv(lab_data, "lab09Wdata.csv", row.names=FALSE)

Verifying Data

Now that we’ve simulated our data, let’s make sure that the means and variances match what we would expect from expectation operators.

We should see that E(X) is approximately equal to 2 and V(X) is approximately 4.

mean(lab_data$XN)
[1] 2.046836
var(lab_data$XN)
[1] 3.745371


Success! Now let’s see if E(Y) is approximately equal to 4.5 and V(Y) is approximately 17.

mean(lab_data$YN)
[1] 4.025967
var(lab_data$YN)
[1] 16.14966


Finally, let’s see if the empirical covariance and correlation match the theoretical values:
\(Cov(X, Y) = 8\)
\(Corr(X, Y) = .97\)

We can use the cov() function to compute the covariance and the cor() function to compute the correlation. For both functions, the first two arguments must reference a vector of values, in this case: (1) lab_data\(XN** and (2) **lab_data\)YN.

cov(lab_data$XN, lab_data$YN)
[1] 7.519566
cor(lab_data$XN, lab_data$YN)
[1] 0.96686

BONUS: Efficient Alternative

The syntax below provides a more compact solution to the lab exercise.

set.seed(11092016) # Set seed to today's date
lab_data <- tibble(SubjID = 1:1000) %>% 
  mutate(XN = rnorm(n(),2,2),
         YN = 2*XN + rnorm(n(),0,1),
         XD = if_else(SubjID > 500, 4, 0),
         XND = if_else(XN >= 2, 4, 0))
summarize_at(lab_data, vars(XN, YN), funs(mean))
summarize_at(lab_data, vars(XN, YN), funs(var))
cov(lab_data$XN, lab_data$YN)
[1] 7.519566
cor(lab_data$XN, lab_data$YN)
[1] 0.96686

Computing New Variables

In the homework assignment you will be asked to compute additional variables. We can do this using the mutate() function which takes a dataframe as its first argument, and then each additional argument creates a new variable (much like the syntax for tibble()).

Let’s start by making a dichotomous predictor variable called XD, where the first 500 subjects have XD = 0 and the last 500 subjects have XD = 4. This way, the mean of XD should be roughly equivalent to the mean of XN. We can do this using the ifelse() function, which takes 3 arguments: (1) The conditional statement that either evaluates to TRUE or FALSE; (2) The value returned when the conditional statement is TRUE; and (3) The value returned when the conditional statement is FALSE (This will look familiar to Excel users). So let’s set (1) the conditional statement to SubjID > 500, such that (2) XD will equal 1 for all subject IDs greater than 500 whereas (3) XD will equal 0 for all subject IDs less than or equal to zero.

mutate(lab_data, XD = ifelse(SubjID > 500, 4, 0))


Since we want to add more than one variable, we can do so within one call of the mutate() function by simply adding extra arguments. Let’s create a dichotomized version of our continuous predictor variable (XND) by performing a mean split. Again, we use the ifelse() function such that when XN is greater than it’s mean (2), we set XND equal to 1 (or 0 otherwise). Finally, let’s save this to a new dataframe called lab_data2.

lab_data2 <- mutate(lab_data, 
                    XD = ifelse(SubjID > 500, 4, 0),
                    XND = ifelse(XN >= 2, 4, 0))
lab_data2

We can see our code worked by looking at the output above. For Subject 1, where XN = 3.56 (which is greater than the mean), we see XND = 1. For Subject 2, where XN = 0.66 (which is less than the mean), we see XND = 0.

Regression (OLS)

Let’s begin by predicting our continuous outcome variable (YN) as a function of our continuous predictor (XN). First we create a model named m1 which we assign to the linear model (using lm()) with the formula \(YN = XN\), which we express with the following model notation: YN ~ XN. Finally, we need to tell the lm() function which data we are working with, so we specify lab_data2. We then use the summary() function on the model we just created to print out the main results.

m1 <- lm(YN ~ XN, data=lab_data2)
summary(m1)

Call:
lm(formula = YN ~ XN, data = lab_data2)

Residuals:
    Min      1Q  Median      3Q     Max 
-3.6142 -0.7099 -0.0232  0.6928  3.1653 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept) -0.08346    0.04726  -1.766   0.0777 .  
XN           2.00770    0.01678 119.637   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 1.027 on 998 degrees of freedom
Multiple R-squared:  0.9348,    Adjusted R-squared:  0.9348 
F-statistic: 1.431e+04 on 1 and 998 DF,  p-value: < 2.2e-16

How well do these estimates correspond with our structural model \(Y = 2X + e\) ?

Now let’s predict our continuous outcome variable (YN) as a function of our dichotomized predictor variable (XND). This time we will call our model m2 and we will change the formula to YN ~ XND. In addition to printing the summary of this model, we will also use the confint() function to print out the confidence intervals for the regression estimates.

m2 <- lm(YN ~ XND, data=lab_data2)
summary(m2) # Model Summary

Call:
lm(formula = YN ~ XND, data = lab_data2)

Residuals:
    Min      1Q  Median      3Q     Max 
-8.7767 -1.7469 -0.0702  1.7574  8.9486 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  0.91652    0.11608   7.896 7.57e-15 ***
XND          1.53933    0.04084  37.695  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 2.583 on 998 degrees of freedom
Multiple R-squared:  0.5874,    Adjusted R-squared:  0.587 
F-statistic:  1421 on 1 and 998 DF,  p-value: < 2.2e-16
confint(m2) # Confidence Intervals
               2.5 %   97.5 %
(Intercept) 0.688737 1.144306
XND         1.459195 1.619464

How well do these estimates correspond with our structural model \(Y = 2X + e\) ?
What do you notice about the confidence interval?
What does this say about using a mean split for a regression?

BONUS: Plotting Residuals

m1 <- lm(YN ~ XN, data=lab_data2)
summary(m1)

Call:
lm(formula = YN ~ XN, data = lab_data2)

Residuals:
    Min      1Q  Median      3Q     Max 
-3.6142 -0.7099 -0.0232  0.6928  3.1653 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept) -0.08346    0.04726  -1.766   0.0777 .  
XN           2.00770    0.01678 119.637   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 1.027 on 998 degrees of freedom
Multiple R-squared:  0.9348,    Adjusted R-squared:  0.9348 
F-statistic: 1.431e+04 on 1 and 998 DF,  p-value: < 2.2e-16
# base R
hist(m1$residuals)

## ggplot solution
m1_augment <- augment(m1) # augment() from the broom package
ggplot(m1_augment, aes(.resid)) + geom_histogram(fill="light grey",color="black") + theme_light()

Correlation

Now let’s create a dataset based on the same structural equation, but this time with far fewer observations (15 instead of 1,000).

lab_data3 <- tibble(SubjID = 1:25,
                    XN = rnorm(25, 2, 2),
                    YN = 2*XN + rnorm(25, 0, 1),
                    XND = ifelse(XN >= 2, 1, 0),
                    YND = ifelse(YN >= 4, 1, 0))
lab_data3

Boostrapped Correlation

First let’s create a function to compute a boostrapped confidence interval for a correlation. Don’t worry about knowing the details about how this function works. We just need to run the code below so that we can reference the function in the next section

# Create function
cor.CI.bootstrap <- function(df, var1, var2, nSims=1000, seed=NA) {
  
  if (!is.na(seed)) {set.seed(seed)}
  cat("\n Bootstrapped 95% CI for",var1,"&",var2,"with",nSims,"simulations:")
  cat("\n")
  cat("\n")
  
  df %>% 
    bootstrap(nSims) %>% 
    do(tidy(cor(.[var1], .[var2]))) %>% 
    ungroup() %>% select_(pearson_r = var2) %>% 
    arrange(pearson_r) %>% 
    slice( c(nSims*.025, nSims*.975)) %>% 
    mutate_all(funs(round(.,4))) %>% 
    add_column(bound = c("lower", "upper")) %>% print()
  
}

Now let’s apply this function to our lab data. The cor.CI.boostrap() function we just created takes three mandatory arguments (and one that is optional): (1) The dataframe that you will be working with; (2) One (of the two) variables that you want to correlate; and (3) The variable you want to correlate with the variable you specified in the previous argument. For this function to work you must ensure that the variable names you supply are wrapped in quotation marks (" " or ‘’); otherwise, the function will not work. (NOTE: There’s also an optional argument to specify the seed of the random number generator, so let’s set seed = 11092016 so we’re all on the same page.)

Let’s try this out on the data we’ve just created.

cor.CI.bootstrap(lab_data3, "XN", "YN", seed=11092016)

 Bootstrapped 95% CI for XN & YN with 1000 simulations:


|================================================================================================          | 91% ~0 s remaining     
|===================================================================================================       | 94% ~0 s remaining     
|======================================================================================================    | 96% ~0 s remaining     
|========================================================================================================= |100% ~0 s remaining     

Now let’s compute the traditional confidence interval.

cor.test(lab_data3$XN, lab_data3$YN)

    Pearson's product-moment correlation

data:  lab_data3$XN and lab_data3$YN
t = 13.481, df = 23, p-value = 0.000000000002095
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.8714457 0.9745047
sample estimates:
      cor 
0.9421584 

How do these estimates differ from the boostrapped confidence intervals?

Now let’s do the same with our dichotomized predictor and outcome variables (XND, YND).

cor.CI.bootstrap(lab_data3, "XND", "YND", seed=11092016)

 Bootstrapped 95% CI for XND & YND with 1000 simulations:


|=======================================================================================================   | 98% ~0 s remaining     
cor.test(lab_data3$XND, lab_data3$YND)

    Pearson's product-moment correlation

data:  lab_data3$XND and lab_data3$YND
t = 4.2895, df = 23, p-value = 0.0002736
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.3686443 0.8404150
sample estimates:
      cor 
0.6666667 

Which of these confidence bounds are more appropriate?

BONUS: More on boostrapping

First lets create a variable called N which will be equal to the sample size of our most recent data simulation (25 observations). We can do this using the nrow() function which will return the number of rows in our dataframe lab_data3.

Now lets use the sample() function to randomly sample the indices (or subject IDs) of these observations. We provide the following argument: (1) the sequence of numbers we want to sample, (2) the number of times we want to sample, and (3) whether we want to sample with replacement. When we set replace = FALSE, notice that each index (1 - 25) is sampled exactly once.

Unsurprisingly, if we were to sample our data (without replacement) 25 times, and created a bar chart for the number of times each subject was sampled… we would see a row of bars = 1.

N <- nrow(lab_data3)
1:N  #ordered sequence
 [1]  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
sample(1:N, N, replace = FALSE) #randomly sampled sequence
 [1] 25 18 20 22  9 10 15  6  3 24 21 16 13 14  1  7 11 12  4 19  5  8  2 23 17
slice(lab_data3, sample(1:N, N, replace = FALSE)) %>% ggplot(aes(SubjID)) + geom_bar() + xlim(0,25)

Now let’s set replace = TRUE and see what happens. Notice we notice that some subjects might get sampled more than once, whereas others might never get sampled.

1:N  #ordered sequence
 [1]  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
idx <- sample(1:N, N, replace = TRUE)
idx
 [1] 17 21  2  8  4 12 10 14 22 14 15 16 18 13 20 23  9 24 21  1  1  5  2  4 11
slice(lab_data3,idx) %>% ggplot(aes(SubjID)) + geom_bar() + xlim(0,25)

Here’s how our resampled data would look.

lab_data3_r1 <- slice(lab_data3, idx)
lab_data3_r1

For a correlation

The blue lines are the standard confidence bounds whereas the red lines are the boostrapped confidence bounds.

  
cor_bs %>% arrange(replicate)
cor_bs %>% 
  ggplot(aes(pearson_r)) + 
  geom_histogram(fill="light grey", color="black") + 
  geom_vline(aes(xintercept=pearson_r), data= slice(cor_bs,25), color="red", linetype="dashed") + 
  geom_vline(aes(xintercept=pearson_r), data= slice(cor_bs,975), color="red", linetype="dashed") + 
  geom_vline(aes(xintercept=conf.low), data= cor_reg, color="blue", linetype="dashed") + 
  geom_vline(aes(xintercept=conf.high), data= cor_reg, color="blue", linetype="dashed")

Here’s what the regression line would reveal for this resampled data.

lab_data3 %>% 
  slice(sample(1:n(), n(), replace = TRUE)) %>% 
  ggplot(aes(XND, YND)) + geom_jitter(width=.1, height=.1) + geom_smooth(method=lm, se=F)

Here’s what the regression lines would look like if we repeated this procedure 1,000 times.

bootnls_aug <- lab_data3 %>% bootstrap(1000) %>%
    do(augment(lm(YND ~ XND, data=.)))
ggplot(bootnls_aug, aes(XND, YND)) + geom_point() +
    geom_line(aes(y=.fitted, group=replicate), alpha=.1, color="blue")

LS0tDQp0aXRsZTogIkxhYjEwVyINCmF1dGhvcjogIkp1bGlhbiBXaWxscyINCmRhdGU6ICJOb3ZlbWJlciA5dGgsIDIwMTYiDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6DQogICAgdGhlbWU6IHJlYWRhYmxlDQogICAgdG9jOiB5ZXMNCi0tLQ0KDQojIyBUaGUgRXhlcmNpc2UNCg0KT3VyIGdvYWwgaXMgdG8gc2ltdWxhdGUgZGF0YSB3aXRoIHRoZSBmb2xsb3dpbmcgcmVsYXRpb25zaGlwIGJldHdlZW4gWSBhbmQgWDoNCiRZID0gMnggKyBlJCAgDQoNCi4uLndoZXJlICRlID0kIHJhbmRvbSB2YXJpYWJsZSB3aXRoIG5vcm1hbCBkaXN0cmlidXRpb24gKDAsIDEpLg0KDQpXZSB3aWxsIHNpbXVsYXRlIHRoaXMgZGF0YSBzdWNoIHRoYXQ6ICAgDQokRShYKSA9IDIkICANCiRWKFgpID0gNCQNCg0KVXNpbmcgZXhwZWN0YXRpb24gb3BlcmF0b3JzLCB3ZSBjYW4gYWxzbyBjb21wdXRlIHRoZSBmb2xsb3dpbmc6ICANCiRFKFkpID0gNCQgICAgDQokVihZKSA9IDE3JCAgDQokQ292KFgsWSkgPSA4JCAgICANCiRDb3JyKFgsWSkgPSAuOTckICANCg0KIyMgTG9hZGluZyBQYWNrYWdlcyAmIERhdGENCg0KIyMjIEZyb20gUGVyc29uYWwvTGFiIENvbXB1dGVyDQpMb2FkICoqdGlkeXZlcnNlKiogdG8gZ2V0IGFjY2VzcyB0byBnZ3Bsb3QyLCBkcGx5ciwgYW5kIHRpYmJsZS4gTG9hZCAqKmJyb29tKiogZm9yIGVhc3kgYm9vdHN0cmFwcGluZy4gDQpgYGB7ciwgcmVzdWx0cz0naGlkZScsIG1lc3NhZ2U9RkFMU0V9DQppcGFrIDwtIGZ1bmN0aW9uKHBrZyl7DQogIG5ldy5wa2cgPC0gcGtnWyEocGtnICVpbiUgaW5zdGFsbGVkLnBhY2thZ2VzKClbLCAiUGFja2FnZSJdKV0NCiAgaWYgKGxlbmd0aChuZXcucGtnKSkgDQogICAgaW5zdGFsbC5wYWNrYWdlcyhuZXcucGtnLCBkZXBlbmRlbmNpZXMgPSBUUlVFKQ0KICBzYXBwbHkocGtnLCByZXF1aXJlLCBjaGFyYWN0ZXIub25seSA9IFRSVUUpDQp9DQoNCnBhY2thZ2VzIDwtIGMoInRpZHl2ZXJzZSIsICJicm9vbSIpDQppcGFrKHBhY2thZ2VzKQ0KDQojIENoYW5nZSBnZ3Bsb3QgdGhlbWUgdG8gbGlnaHQgYmFja2dyb3VuZA0Kb2xkIDwtIHRoZW1lX3NldCh0aGVtZV9saWdodChiYXNlX3NpemUgPSAxMikpDQpgYGANCg0KIyMjIEZyb20gTGFiIENvbXB1dGVyDQoNCmBgYHtyfQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkodGliYmxlKQ0KbGlicmFyeShicm9vbSkNCmBgYA0KDQoNCiMjIFNpbXVsYXRpbmcgdGhlIERhdGENCg0KRmlyc3Qgd2UgY3JlYXRlIGEgZGF0YWZyYW1lIHVzaW5nIHRoZSAqKnRpYmJsZSgpKiogZnVuY3Rpb24uIFRoaXMgYmFzaWNhbGx5IGNyZWF0ZXMgYSBkYXRhZnJhbWUgd2l0aCBhIG1vcmUgdXNlci1mcmllbmRseSBvdXRwdXQuIEVhY2ggYXJndW1lbnQgZm9yIHRpYmJsZSgpIGJlZ2lucyB3aXRoIHRoZSBjb2x1bW4gbmFtZSAob3IgKnZhcmlhYmxlKikgeW91IHdhbnQgdG8gY3JlYXRlIGZvciB5b3VyIGRhdGFmcmFtZSAoaW4gdGhpcyBjYXNlICJTdWJqSUQiIHRvIGluZGljYXRlIHRoZSBTdWJqZWN0IElEIG51bWJlcikuIFNpbmNlIHdlIGFyZSBzaW11bGF0aW5nIDEsMDAwIHN1YmplY3RzIC0tIHdlIHdpbGwgc2V0IHRoaXMgdmFyaWFibGUgZXF1YWwgdG8gMSB0aHJvdWdoIDEwMDAuIEluc3RlYWQgb2YgdHlwaW5nIHRoaXMgb3V0IG1hbnVhbGx5IHVzaW5nIHRoZSBjKCkgZnVuY3Rpb24gKGUuZy4sIGMoMSwgMiwgMywgNCwgLi4uLCAxMDAwKSkgd2Ugd2lsbCB1c2UgdGhlIHNlcXVlbmNlIG9wZXJhdG9yICoqOioqIHRvIGNyZWF0ZSBhIHZlY3RvciBvZiBpbnRlZ2VycyBzdGFydGluZyB3aXRoIDEgYW5kIGVuZGluZyB3aXRoIDEwMDAuIA0KYGBge3J9DQoNCnRpYmJsZShTdWJqSUQgPSAxOjEwMDApDQoNCmBgYA0KDQo8YnIgLz4NCldlIGNhbiBjcmVhdGUgYSBzZWNvbmQgdmFyaWFibGUgc2ltcGx5IGJ5IGFkZGluZyBhIG5ldyBhcmd1bWVudC4gTGV0J3Mgc3RhcnQgYnkgY3JlYXRpbmcgYSBjb250aW51b3VzIHByZWRpY3RvciB2YXJpYWJsZSAqKlhOKiogKFggbm9ybWFsKSBXZSBjYW4gZG8gdGhpcyB1c2luZyB0aGUgKipybm9ybSgpKiogZnVuY3Rpb24sIHdoaWNoIHRha2VzIHRocmVlIGFyZ3VtZW50czogKDEpIFRoZSBudW1iZXIgb2YgcmFuZG9tIG51bWJlcnMgdG8gZ2VuZXJhdGU7ICgyKSBUaGUgbWVhbiBvZiB0aGUgbm9ybWFsIGRpc3RyaWJ1dGlvbiBmcm9tIHdoaWNoIHRvIGRyYXcgZnJvbTsgYW5kICgzKSBUaGUgU0Qgb2YgdGhlIGRpc3RyaWJ1dGlvbiBmcm9tIHdoaWNoIHRvIGRyYXcgZnJvbS4gU3BlY2lmaWNhbGx5LCBsZXQncyBkcmF3ICoqMSwwMDAqKiBudW1iZXJzIChzaW5jZSB3ZSBoYXZlIDEsMDAwIHN1YmplY3RzKSBmcm9tIGEgbm9ybWFsIGRpc3RyaWJ1dGlvbiB3aXRoIG1lYW4gPSAyIGFuZCBTRCA9IDIuIA0KDQpgYGB7cn0NCnRpYmJsZShTdWJqSUQgPSAxOjEwMDAsDQogICAgICAgWE4gPSBybm9ybSgxMDAwLDIsMikpDQoNCmBgYA0KDQo8YnIgLz4NCkluIGFkZGl0aW9uLCBsZXQncyBjcmVhdGUgKipZTioqIChZIG5vcm1hbCkgd2hpY2ggd2lsbCBiZSBlcXVhbCB0byAyIHRpbWVzIHRoZSB2YWx1ZSBvZiBYTiBwbHVzIHJhbmRvbSBub2lzZSB3aXRoIG1lYW4gPSAwIGFuZCBTRCA9IDEuIA0KYGBge3J9DQp0aWJibGUoU3ViaklEID0gMToxMDAwLA0KICAgICAgIFhOID0gcm5vcm0oMTAwMCwyLDIpLA0KICAgICAgIFlOID0gMipYTiArIHJub3JtKDEwMDAsMCwxKSkNCg0KYGBgDQoNCjxiciAvPg0KTm93IGxldCdzIGFzc2lnbiB0aGlzIGRhdGFmcmFtZSB0byBhbiBvYmplY3QgY2FsbGVkICoqbGFiX2RhdGEqKi4gSW4gYWRkaXRpb24sIGxldHMgc2V0IHRoZSByYW5kb20gbnVtYmVyIGdlbmVyYXRvciB0byB0b2RheSdzIGRhdGUgc28gdGhhdCB3ZSBjYW4gcmVwcm9kdWNlIHRoaXMgZXhhY3QgZGF0YWZyYW1lIHdoZW4gd2UgcmV2aXNpdCB0aGlzIHBhZ2UuIA0KYGBge3J9DQoNCnNldC5zZWVkKDExMDkyMDE2KSAjIFNldCBzZWVkIHRvIHRvZGF5J3MgZGF0ZQ0KDQpsYWJfZGF0YSA8LSB0aWJibGUoU3ViaklEID0gMToxMDAwLA0KICAgICAgICAgICAgICAgICAgIFhOID0gcm5vcm0oMTAwMCwyLDIpLA0KICAgICAgICAgICAgICAgICAgIFlOID0gMipYTiArIHJub3JtKDEwMDAsMCwxKSkNCmxhYl9kYXRhDQoNCmBgYA0KDQoNCiMjIyBCT05VUzogRXhwb3J0IHRvIFNQU1MNCg0KRm9yIHRob3NlIHdobyBhcmUgbW9yZSBjb21mb3J0YWJsZSBjb25kdWN0aW5nIHRlc3RzIGluIFNQU1MsIGZlZWwgZnJlZSB0byBleHBvcnQgeW91ciBuZXcgc2ltdWxhdGVkIGRhdGEgaW50byBhIC5jc3YsIHdoaWNoIGNhbiB0aGVuIGJlIGVhc2lseSBpbXBvcnRlZCBpbnRvIFNQU1MuIFdlIGNhbiBkbyB0aGlzIHdpdGggdGhlICoqd3JpdGUuY3N2KCkqKiBmdW5jdGlvbiB3aGljaCB0YWtlcyAzIGltcG9ydGFudCBhcmd1bWVudHM6ICgxKSBUaGUgbmFtZSBvZiB0aGUgZGF0YWZyYW1lIHlvdSB3YW50IHRvIGV4cG9ydCAoaW4gdGhpcyBjYXNlLCAqbGFiX2RhdGEqKTsgKDIpIFRoZSBuYW1lIG9mIHRoZSBmaWxlIHlvdSB3YW50IHRvIHNhdmUgeW91ciBkYXRhZnJhbWUgYXMgKHVubGVzcyB5b3Ugc3BlY2lmeSB0aGUgZnVsbCBwYXRoLCBpdCB3aWxsIGV4cG9ydCB0aGlzIGZpbGUgaW50byB5b3VyIHdvcmtpbmcgZGlyZWN0b3J5KTsgYW5kICgzKSBXaGV0aGVyIHlvdSB3YW50IHRoZSBmaXJzdCBjb2x1bW4gdG8gaW5kaWNhdGUgdGhlIHJvdyBuYW1lcyBvZiB5b3VyIGRhdGEgKGUuZy4sIDEsIDIsIDMsIDQsIC4uLiAxMDAwKS4gVGhpcyByb3cubmFtZXMgYXJndW1lbnQgZGVmYXVsdHMgdG8gVFJVRSAod2hpY2ggaXMgb2Z0ZW4gYSBudWlzYW5jZSkgc28gbGV0J3MgY2hhbmdlIHRoYXQgdG8gRkFMU0Ugc28gdGhhdCB3ZSBkbyBub3QgZXhwb3J0IHJvdyBuYW1lcy4gDQpgYGB7cn0NCg0KIyMgVGhpcyBpcyBjb21tZW50ZWQgb3V0IGF0IHRoZSBtb21lbnQgYnV0IHNpbXBseSByZW1vdmUgdGhlIGhhc2h0YWcgaW4gZnJvbnQgb2YgdGhlIGxpbmUgaW4gb3JkZXIgdG8gZXhwb3J0IHRoZSBkYXRhLg0KDQojIHdyaXRlLmNzdihsYWJfZGF0YSwgImxhYjA5V2RhdGEuY3N2Iiwgcm93Lm5hbWVzPUZBTFNFKQ0KDQpgYGANCg0KDQojIyBWZXJpZnlpbmcgRGF0YQ0KDQpOb3cgdGhhdCB3ZSd2ZSBzaW11bGF0ZWQgb3VyIGRhdGEsIGxldCdzIG1ha2Ugc3VyZSB0aGF0IHRoZSBtZWFucyBhbmQgdmFyaWFuY2VzIG1hdGNoIHdoYXQgd2Ugd291bGQgZXhwZWN0IGZyb20gZXhwZWN0YXRpb24gb3BlcmF0b3JzLiAgDQoNCldlIHNob3VsZCBzZWUgdGhhdCBFKFgpIGlzIGFwcHJveGltYXRlbHkgZXF1YWwgdG8gMiBhbmQgVihYKSBpcyBhcHByb3hpbWF0ZWx5IDQuIA0KYGBge3J9DQoNCm1lYW4obGFiX2RhdGEkWE4pDQp2YXIobGFiX2RhdGEkWE4pDQoNCmBgYA0KDQo8YnIgLz4NClN1Y2Nlc3MhIE5vdyBsZXQncyBzZWUgaWYgRShZKSBpcyBhcHByb3hpbWF0ZWx5IGVxdWFsIHRvIDQuNSBhbmQgVihZKSBpcyBhcHByb3hpbWF0ZWx5IDE3LiANCmBgYHtyfQ0KDQptZWFuKGxhYl9kYXRhJFlOKQ0KdmFyKGxhYl9kYXRhJFlOKQ0KDQpgYGANCg0KPGJyIC8+DQpGaW5hbGx5LCBsZXQncyBzZWUgaWYgdGhlIGVtcGlyaWNhbCBjb3ZhcmlhbmNlIGFuZCBjb3JyZWxhdGlvbiBtYXRjaCB0aGUgdGhlb3JldGljYWwgdmFsdWVzOiAgDQokQ292KFgsIFkpID0gOCQgIA0KJENvcnIoWCwgWSkgPSAuOTckICANCg0KV2UgY2FuIHVzZSB0aGUgKipjb3YoKSoqIGZ1bmN0aW9uIHRvIGNvbXB1dGUgdGhlIGNvdmFyaWFuY2UgYW5kIHRoZSAqKmNvcigpKiogZnVuY3Rpb24gdG8gY29tcHV0ZSB0aGUgY29ycmVsYXRpb24uIEZvciBib3RoIGZ1bmN0aW9ucywgdGhlIGZpcnN0IHR3byBhcmd1bWVudHMgbXVzdCByZWZlcmVuY2UgYSB2ZWN0b3Igb2YgdmFsdWVzLCBpbiB0aGlzIGNhc2U6ICgxKSAqKmxhYl9kYXRhJFhOKiogYW5kICgyKSAqKmxhYl9kYXRhJFlOKiouIA0KYGBge3J9DQoNCmNvdihsYWJfZGF0YSRYTiwgbGFiX2RhdGEkWU4pDQpjb3IobGFiX2RhdGEkWE4sIGxhYl9kYXRhJFlOKQ0KDQpgYGANCg0KDQojIyMgQk9OVVM6IEVmZmljaWVudCBBbHRlcm5hdGl2ZQ0KDQpUaGUgc3ludGF4IGJlbG93IHByb3ZpZGVzIGEgbW9yZSBjb21wYWN0IHNvbHV0aW9uIHRvIHRoZSBsYWIgZXhlcmNpc2UuIA0KYGBge3J9DQoNCnNldC5zZWVkKDExMDkyMDE2KSAjIFNldCBzZWVkIHRvIHRvZGF5J3MgZGF0ZQ0KDQpsYWJfZGF0YSA8LSB0aWJibGUoU3ViaklEID0gMToxMDAwKSAlPiUgDQogIG11dGF0ZShYTiA9IHJub3JtKG4oKSwyLDIpLA0KICAgICAgICAgWU4gPSAyKlhOICsgcm5vcm0obigpLDAsMSksDQogICAgICAgICBYRCA9IGlmX2Vsc2UoU3ViaklEID4gNTAwLCA0LCAwKSwNCiAgICAgICAgIFhORCA9IGlmX2Vsc2UoWE4gPj0gMiwgNCwgMCkpDQoNCnN1bW1hcml6ZV9hdChsYWJfZGF0YSwgdmFycyhYTiwgWU4pLCBmdW5zKG1lYW4pKQ0Kc3VtbWFyaXplX2F0KGxhYl9kYXRhLCB2YXJzKFhOLCBZTiksIGZ1bnModmFyKSkNCg0KY292KGxhYl9kYXRhJFhOLCBsYWJfZGF0YSRZTikNCmNvcihsYWJfZGF0YSRYTiwgbGFiX2RhdGEkWU4pDQoNCmBgYA0KDQoNCiMjIENvbXB1dGluZyBOZXcgVmFyaWFibGVzDQoNCkluIHRoZSBob21ld29yayBhc3NpZ25tZW50IHlvdSB3aWxsIGJlIGFza2VkIHRvIGNvbXB1dGUgYWRkaXRpb25hbCB2YXJpYWJsZXMuIFdlIGNhbiBkbyB0aGlzIHVzaW5nIHRoZSAqKm11dGF0ZSgpKiogZnVuY3Rpb24gd2hpY2ggdGFrZXMgYSBkYXRhZnJhbWUgYXMgaXRzIGZpcnN0IGFyZ3VtZW50LCBhbmQgdGhlbiBlYWNoIGFkZGl0aW9uYWwgYXJndW1lbnQgY3JlYXRlcyBhIG5ldyB2YXJpYWJsZSAobXVjaCBsaWtlIHRoZSBzeW50YXggZm9yICoqdGliYmxlKCkqKikuIA0KDQpMZXQncyBzdGFydCBieSBtYWtpbmcgYSBkaWNob3RvbW91cyBwcmVkaWN0b3IgdmFyaWFibGUgY2FsbGVkICoqWEQqKiwgd2hlcmUgdGhlIGZpcnN0IDUwMCBzdWJqZWN0cyBoYXZlIFhEID0gMCBhbmQgdGhlIGxhc3QgNTAwIHN1YmplY3RzIGhhdmUgWEQgPSA0LiBUaGlzIHdheSwgdGhlIG1lYW4gb2YgWEQgc2hvdWxkIGJlIHJvdWdobHkgZXF1aXZhbGVudCB0byB0aGUgbWVhbiBvZiBYTi4gV2UgY2FuIGRvIHRoaXMgdXNpbmcgdGhlICoqaWZlbHNlKCkqKiBmdW5jdGlvbiwgd2hpY2ggdGFrZXMgMyBhcmd1bWVudHM6ICgxKSBUaGUgKmNvbmRpdGlvbmFsIHN0YXRlbWVudCogdGhhdCBlaXRoZXIgZXZhbHVhdGVzIHRvIFRSVUUgb3IgRkFMU0U7ICgyKSBUaGUgdmFsdWUgcmV0dXJuZWQgd2hlbiB0aGUgY29uZGl0aW9uYWwgc3RhdGVtZW50IGlzIFRSVUU7IGFuZCAoMykgVGhlIHZhbHVlIHJldHVybmVkIHdoZW4gdGhlIGNvbmRpdGlvbmFsIHN0YXRlbWVudCBpcyBGQUxTRSAoVGhpcyB3aWxsIGxvb2sgZmFtaWxpYXIgdG8gRXhjZWwgdXNlcnMpLiBTbyBsZXQncyBzZXQgKDEpIHRoZSBjb25kaXRpb25hbCBzdGF0ZW1lbnQgdG8gKipTdWJqSUQgPiA1MDAqKiwgc3VjaCB0aGF0ICgyKSBYRCB3aWxsIGVxdWFsIDEgZm9yIGFsbCBzdWJqZWN0IElEcyBncmVhdGVyIHRoYW4gNTAwIHdoZXJlYXMgKDMpIFhEIHdpbGwgZXF1YWwgMCBmb3IgYWxsIHN1YmplY3QgSURzIGxlc3MgdGhhbiBvciBlcXVhbCB0byB6ZXJvLiANCmBgYHtyfQ0KbXV0YXRlKGxhYl9kYXRhLCBYRCA9IGlmZWxzZShTdWJqSUQgPiA1MDAsIDQsIDApKQ0KYGBgDQoNCjxiciAvPg0KU2luY2Ugd2Ugd2FudCB0byBhZGQgbW9yZSB0aGFuIG9uZSB2YXJpYWJsZSwgd2UgY2FuIGRvIHNvIHdpdGhpbiBvbmUgKmNhbGwqIG9mIHRoZSBtdXRhdGUoKSBmdW5jdGlvbiBieSBzaW1wbHkgYWRkaW5nIGV4dHJhIGFyZ3VtZW50cy4gTGV0J3MgY3JlYXRlIGEgZGljaG90b21pemVkIHZlcnNpb24gb2Ygb3VyIGNvbnRpbnVvdXMgcHJlZGljdG9yIHZhcmlhYmxlICgqKlhORCoqKSBieSBwZXJmb3JtaW5nIGEgbWVhbiBzcGxpdC4gQWdhaW4sIHdlIHVzZSB0aGUgKippZmVsc2UoKSoqIGZ1bmN0aW9uIHN1Y2ggdGhhdCB3aGVuIFhOIGlzIGdyZWF0ZXIgdGhhbiBpdCdzIG1lYW4gKDIpLCB3ZSBzZXQgWE5EIGVxdWFsIHRvIDEgKG9yIDAgb3RoZXJ3aXNlKS4gRmluYWxseSwgbGV0J3Mgc2F2ZSB0aGlzIHRvIGEgbmV3IGRhdGFmcmFtZSBjYWxsZWQgKipsYWJfZGF0YTIqKi4gDQpgYGB7cn0NCg0KbGFiX2RhdGEyIDwtIG11dGF0ZShsYWJfZGF0YSwgDQogICAgICAgICAgICAgICAgICAgIFhEID0gaWZlbHNlKFN1YmpJRCA+IDUwMCwgNCwgMCksDQogICAgICAgICAgICAgICAgICAgIFhORCA9IGlmZWxzZShYTiA+PSAyLCA0LCAwKSkNCg0KbGFiX2RhdGEyDQoNCmBgYA0KDQpXZSBjYW4gc2VlIG91ciBjb2RlIHdvcmtlZCBieSBsb29raW5nIGF0IHRoZSBvdXRwdXQgYWJvdmUuIEZvciBTdWJqZWN0IDEsIHdoZXJlIFhOID0gMy41NiAod2hpY2ggaXMgZ3JlYXRlciB0aGFuIHRoZSBtZWFuKSwgd2Ugc2VlIFhORCA9IDEuIEZvciBTdWJqZWN0IDIsIHdoZXJlIFhOID0gMC42NiAod2hpY2ggaXMgbGVzcyB0aGFuIHRoZSBtZWFuKSwgd2Ugc2VlIFhORCA9IDAuIA0KDQojIyBSZWdyZXNzaW9uIChPTFMpDQoNCkxldCdzIGJlZ2luIGJ5IHByZWRpY3Rpbmcgb3VyIGNvbnRpbnVvdXMgb3V0Y29tZSB2YXJpYWJsZSAoWU4pIGFzIGEgZnVuY3Rpb24gb2Ygb3VyIGNvbnRpbnVvdXMgcHJlZGljdG9yIChYTikuIEZpcnN0IHdlIGNyZWF0ZSBhIG1vZGVsIG5hbWVkICoqbTEqKiB3aGljaCB3ZSBhc3NpZ24gdG8gdGhlIGxpbmVhciBtb2RlbCAodXNpbmcgKipsbSgpKiopIHdpdGggdGhlIGZvcm11bGEgJFlOID0gWE4kLCB3aGljaCB3ZSBleHByZXNzIHdpdGggdGhlIGZvbGxvd2luZyBtb2RlbCBub3RhdGlvbjogKipZTiB+IFhOKiouIEZpbmFsbHksIHdlIG5lZWQgdG8gdGVsbCB0aGUgbG0oKSBmdW5jdGlvbiB3aGljaCAqKmRhdGEqKiB3ZSBhcmUgd29ya2luZyB3aXRoLCBzbyB3ZSBzcGVjaWZ5ICpsYWJfZGF0YTIqLiBXZSB0aGVuIHVzZSB0aGUgKipzdW1tYXJ5KCkqKiBmdW5jdGlvbiBvbiB0aGUgbW9kZWwgd2UganVzdCBjcmVhdGVkIHRvIHByaW50IG91dCB0aGUgbWFpbiByZXN1bHRzLiANCmBgYHtyfQ0KDQptMSA8LSBsbShZTiB+IFhOLCBkYXRhPWxhYl9kYXRhMikNCnN1bW1hcnkobTEpDQoNCmBgYA0KSG93IHdlbGwgZG8gdGhlc2UgZXN0aW1hdGVzIGNvcnJlc3BvbmQgd2l0aCBvdXIgc3RydWN0dXJhbCBtb2RlbCAkWSA9IDJYICsgZSQgPw0KDQpOb3cgbGV0J3MgcHJlZGljdCBvdXIgY29udGludW91cyBvdXRjb21lIHZhcmlhYmxlIChZTikgYXMgYSBmdW5jdGlvbiBvZiBvdXIgZGljaG90b21pemVkIHByZWRpY3RvciB2YXJpYWJsZSAoWE5EKS4gVGhpcyB0aW1lIHdlIHdpbGwgY2FsbCBvdXIgbW9kZWwgKiptMioqIGFuZCB3ZSB3aWxsIGNoYW5nZSB0aGUgZm9ybXVsYSB0byAqKllOIH4gWE5EKiouIEluIGFkZGl0aW9uIHRvIHByaW50aW5nIHRoZSBzdW1tYXJ5IG9mIHRoaXMgbW9kZWwsIHdlIHdpbGwgYWxzbyB1c2UgdGhlICoqY29uZmludCgpKiogZnVuY3Rpb24gdG8gcHJpbnQgb3V0IHRoZSBjb25maWRlbmNlIGludGVydmFscyBmb3IgdGhlIHJlZ3Jlc3Npb24gZXN0aW1hdGVzLiANCmBgYHtyfQ0KDQptMiA8LSBsbShZTiB+IFhORCwgZGF0YT1sYWJfZGF0YTIpDQpzdW1tYXJ5KG0yKSAjIE1vZGVsIFN1bW1hcnkNCmNvbmZpbnQobTIpICMgQ29uZmlkZW5jZSBJbnRlcnZhbHMNCg0KYGBgDQpIb3cgd2VsbCBkbyB0aGVzZSBlc3RpbWF0ZXMgY29ycmVzcG9uZCB3aXRoIG91ciBzdHJ1Y3R1cmFsIG1vZGVsICRZID0gMlggKyBlJCA/ICANCldoYXQgZG8geW91IG5vdGljZSBhYm91dCB0aGUgY29uZmlkZW5jZSBpbnRlcnZhbD8gIA0KV2hhdCBkb2VzIHRoaXMgc2F5IGFib3V0IHVzaW5nIGEgbWVhbiBzcGxpdCBmb3IgYSByZWdyZXNzaW9uPyAgDQoNCiMjIyBCT05VUzogUGxvdHRpbmcgUmVzaWR1YWxzIA0KDQpgYGB7cn0NCg0KbTEgPC0gbG0oWU4gfiBYTiwgZGF0YT1sYWJfZGF0YTIpDQpzdW1tYXJ5KG0xKQ0KDQojIGJhc2UgUg0KaGlzdChtMSRyZXNpZHVhbHMpDQoNCiMjIGdncGxvdCBzb2x1dGlvbg0KbTFfYXVnbWVudCA8LSBhdWdtZW50KG0xKSAjIGF1Z21lbnQoKSBmcm9tIHRoZSBicm9vbSBwYWNrYWdlDQpnZ3Bsb3QobTFfYXVnbWVudCwgYWVzKC5yZXNpZCkpICsgZ2VvbV9oaXN0b2dyYW0oZmlsbD0ibGlnaHQgZ3JleSIsY29sb3I9ImJsYWNrIikgKyB0aGVtZV9saWdodCgpDQoNCmBgYA0KDQojIyBDb3JyZWxhdGlvbg0KDQpOb3cgbGV0J3MgY3JlYXRlIGEgZGF0YXNldCBiYXNlZCBvbiB0aGUgc2FtZSBzdHJ1Y3R1cmFsIGVxdWF0aW9uLCBidXQgdGhpcyB0aW1lIHdpdGggZmFyIGZld2VyIG9ic2VydmF0aW9ucyAoMTUgaW5zdGVhZCBvZiAxLDAwMCkuDQpgYGB7cn0NCmxhYl9kYXRhMyA8LSB0aWJibGUoU3ViaklEID0gMToyNSwNCiAgICAgICAgICAgICAgICAgICAgWE4gPSBybm9ybSgyNSwgMiwgMiksDQogICAgICAgICAgICAgICAgICAgIFlOID0gMipYTiArIHJub3JtKDI1LCAwLCAxKSwNCiAgICAgICAgICAgICAgICAgICAgWE5EID0gaWZlbHNlKFhOID49IDIsIDEsIDApLA0KICAgICAgICAgICAgICAgICAgICBZTkQgPSBpZmVsc2UoWU4gPj0gNCwgMSwgMCkpDQoNCg0KbGFiX2RhdGEzDQoNCmBgYA0KDQoNCiMjIyBCb29zdHJhcHBlZCBDb3JyZWxhdGlvbg0KDQpGaXJzdCBsZXQncyBjcmVhdGUgYSBmdW5jdGlvbiB0byBjb21wdXRlIGEgYm9vc3RyYXBwZWQgY29uZmlkZW5jZSBpbnRlcnZhbCBmb3IgYSBjb3JyZWxhdGlvbi4gRG9uJ3Qgd29ycnkgYWJvdXQga25vd2luZyB0aGUgZGV0YWlscyBhYm91dCBob3cgdGhpcyBmdW5jdGlvbiB3b3Jrcy4gV2UganVzdCBuZWVkIHRvIHJ1biB0aGUgY29kZSBiZWxvdyBzbyB0aGF0IHdlIGNhbiByZWZlcmVuY2UgdGhlIGZ1bmN0aW9uIGluIHRoZSBuZXh0IHNlY3Rpb24NCmBgYHtyfQ0KDQojIENyZWF0ZSBmdW5jdGlvbg0KY29yLkNJLmJvb3RzdHJhcCA8LSBmdW5jdGlvbihkZiwgdmFyMSwgdmFyMiwgblNpbXM9MTAwMCwgc2VlZD1OQSkgew0KICANCiAgaWYgKCFpcy5uYShzZWVkKSkge3NldC5zZWVkKHNlZWQpfQ0KICBjYXQoIlxuIEJvb3RzdHJhcHBlZCA5NSUgQ0kgZm9yIix2YXIxLCImIix2YXIyLCJ3aXRoIixuU2ltcywic2ltdWxhdGlvbnM6IikNCiAgY2F0KCJcbiIpDQogIGNhdCgiXG4iKQ0KICANCiAgZGYgJT4lIA0KICAgIGJvb3RzdHJhcChuU2ltcykgJT4lIA0KICAgIGRvKHRpZHkoY29yKC5bdmFyMV0sIC5bdmFyMl0pKSkgJT4lIA0KICAgIHVuZ3JvdXAoKSAlPiUgc2VsZWN0XyhwZWFyc29uX3IgPSB2YXIyKSAlPiUgDQogICAgYXJyYW5nZShwZWFyc29uX3IpICU+JSANCiAgICBzbGljZSggYyhuU2ltcyouMDI1LCBuU2ltcyouOTc1KSkgJT4lIA0KICAgIG11dGF0ZV9hbGwoZnVucyhyb3VuZCguLDQpKSkgJT4lIA0KICAgIGFkZF9jb2x1bW4oYm91bmQgPSBjKCJsb3dlciIsICJ1cHBlciIpKSAlPiUgcHJpbnQoKQ0KICANCn0NCg0KYGBgDQogIA0KDQpOb3cgbGV0J3MgYXBwbHkgdGhpcyBmdW5jdGlvbiB0byBvdXIgbGFiIGRhdGEuIFRoZSAqKmNvci5DSS5ib29zdHJhcCgpKiogZnVuY3Rpb24gd2UganVzdCBjcmVhdGVkIHRha2VzIHRocmVlIG1hbmRhdG9yeSBhcmd1bWVudHMgKGFuZCBvbmUgdGhhdCBpcyBvcHRpb25hbCk6ICgxKSBUaGUgZGF0YWZyYW1lIHRoYXQgeW91IHdpbGwgYmUgd29ya2luZyB3aXRoOyAoMikgT25lIChvZiB0aGUgdHdvKSB2YXJpYWJsZXMgdGhhdCB5b3Ugd2FudCB0byBjb3JyZWxhdGU7IGFuZCAoMykgVGhlIHZhcmlhYmxlIHlvdSB3YW50IHRvIGNvcnJlbGF0ZSB3aXRoIHRoZSB2YXJpYWJsZSB5b3Ugc3BlY2lmaWVkIGluIHRoZSBwcmV2aW91cyBhcmd1bWVudC4gRm9yIHRoaXMgZnVuY3Rpb24gdG8gd29yayB5b3UgKm11c3QqIGVuc3VyZSB0aGF0IHRoZSB2YXJpYWJsZSBuYW1lcyB5b3Ugc3VwcGx5IGFyZSB3cmFwcGVkIGluIHF1b3RhdGlvbiBtYXJrcyAoIiAiIG9yICcgJyk7IG90aGVyd2lzZSwgdGhlIGZ1bmN0aW9uIHdpbGwgbm90IHdvcmsuIChOT1RFOiBUaGVyZSdzIGFsc28gYW4gb3B0aW9uYWwgYXJndW1lbnQgdG8gc3BlY2lmeSB0aGUgKnNlZWQqIG9mIHRoZSByYW5kb20gbnVtYmVyIGdlbmVyYXRvciwgc28gbGV0J3Mgc2V0ICoqc2VlZCA9IDExMDkyMDE2Kiogc28gd2UncmUgYWxsIG9uIHRoZSBzYW1lIHBhZ2UuKQ0KDQpMZXQncyB0cnkgdGhpcyBvdXQgb24gdGhlIGRhdGEgd2UndmUganVzdCBjcmVhdGVkLiANCmBgYHtyfQ0KDQpjb3IuQ0kuYm9vdHN0cmFwKGxhYl9kYXRhMywgIlhOIiwgIllOIiwgc2VlZD0xMTA5MjAxNikNCg0KYGBgDQogIA0KICANCk5vdyBsZXQncyBjb21wdXRlIHRoZSB0cmFkaXRpb25hbCBjb25maWRlbmNlIGludGVydmFsLiANCmBgYHtyfQ0KDQpjb3IudGVzdChsYWJfZGF0YTMkWE4sIGxhYl9kYXRhMyRZTikNCg0KYGBgDQpIb3cgZG8gdGhlc2UgZXN0aW1hdGVzIGRpZmZlciBmcm9tIHRoZSBib29zdHJhcHBlZCBjb25maWRlbmNlIGludGVydmFscz8gICANCg0KDQpOb3cgbGV0J3MgZG8gdGhlIHNhbWUgd2l0aCBvdXIgZGljaG90b21pemVkIHByZWRpY3RvciBhbmQgb3V0Y29tZSB2YXJpYWJsZXMgKCoqWE5ELCBZTkQqKikuIA0KYGBge3J9DQpjb3IuQ0kuYm9vdHN0cmFwKGxhYl9kYXRhMywgIlhORCIsICJZTkQiLCBzZWVkPTExMDkyMDE2KQ0KY29yLnRlc3QobGFiX2RhdGEzJFhORCwgbGFiX2RhdGEzJFlORCkNCg0KYGBgDQpXaGljaCBvZiB0aGVzZSBjb25maWRlbmNlIGJvdW5kcyBhcmUgbW9yZSBhcHByb3ByaWF0ZT8gICANCg0KDQojIyBCT05VUzogTW9yZSBvbiBib29zdHJhcHBpbmcNCg0KDQpGaXJzdCBsZXRzIGNyZWF0ZSBhIHZhcmlhYmxlIGNhbGxlZCAqKk4qKiB3aGljaCB3aWxsIGJlIGVxdWFsIHRvIHRoZSBzYW1wbGUgc2l6ZSBvZiBvdXIgbW9zdCByZWNlbnQgZGF0YSBzaW11bGF0aW9uICgyNSBvYnNlcnZhdGlvbnMpLiBXZSBjYW4gZG8gdGhpcyB1c2luZyB0aGUgKipucm93KCkqKiBmdW5jdGlvbiB3aGljaCB3aWxsIHJldHVybiB0aGUgbnVtYmVyIG9mIHJvd3MgaW4gb3VyIGRhdGFmcmFtZSAqbGFiX2RhdGEzKi4gDQoNCk5vdyBsZXRzIHVzZSB0aGUgKipzYW1wbGUoKSoqIGZ1bmN0aW9uIHRvIHJhbmRvbWx5IHNhbXBsZSB0aGUgaW5kaWNlcyAob3Igc3ViamVjdCBJRHMpIG9mIHRoZXNlIG9ic2VydmF0aW9ucy4gV2UgcHJvdmlkZSB0aGUgZm9sbG93aW5nIGFyZ3VtZW50OiAoMSkgdGhlIHNlcXVlbmNlIG9mIG51bWJlcnMgd2Ugd2FudCB0byBzYW1wbGUsICgyKSB0aGUgbnVtYmVyIG9mIHRpbWVzIHdlIHdhbnQgdG8gc2FtcGxlLCBhbmQgKDMpIHdoZXRoZXIgd2Ugd2FudCB0byBzYW1wbGUgd2l0aCByZXBsYWNlbWVudC4gV2hlbiB3ZSBzZXQgKipyZXBsYWNlID0gRkFMU0UqKiwgbm90aWNlIHRoYXQgZWFjaCBpbmRleCAoMSAtIDI1KSBpcyBzYW1wbGVkIGV4YWN0bHkgb25jZS4gDQoNClVuc3VycHJpc2luZ2x5LCBpZiB3ZSB3ZXJlIHRvIHNhbXBsZSBvdXIgZGF0YSAod2l0aG91dCByZXBsYWNlbWVudCkgMjUgdGltZXMsIGFuZCBjcmVhdGVkIGEgYmFyIGNoYXJ0IGZvciB0aGUgbnVtYmVyIG9mIHRpbWVzIGVhY2ggc3ViamVjdCB3YXMgc2FtcGxlZC4uLiB3ZSB3b3VsZCBzZWUgYSByb3cgb2YgYmFycyA9IDEuIA0KYGBge3J9DQpOIDwtIG5yb3cobGFiX2RhdGEzKQ0KMTpOICAjb3JkZXJlZCBzZXF1ZW5jZQ0Kc2FtcGxlKDE6TiwgTiwgcmVwbGFjZSA9IEZBTFNFKSAjcmFuZG9tbHkgc2FtcGxlZCBzZXF1ZW5jZQ0KDQpzbGljZShsYWJfZGF0YTMsIHNhbXBsZSgxOk4sIE4sIHJlcGxhY2UgPSBGQUxTRSkpICU+JSBnZ3Bsb3QoYWVzKFN1YmpJRCkpICsgZ2VvbV9iYXIoKSArIHhsaW0oMCwyNSkNCg0KYGBgDQogIA0KICANCk5vdyBsZXQncyBzZXQgKipyZXBsYWNlID0gVFJVRSoqIGFuZCBzZWUgd2hhdCBoYXBwZW5zLiBOb3RpY2Ugd2Ugbm90aWNlIHRoYXQgc29tZSBzdWJqZWN0cyBtaWdodCBnZXQgc2FtcGxlZCBtb3JlIHRoYW4gb25jZSwgd2hlcmVhcyBvdGhlcnMgbWlnaHQgbmV2ZXIgZ2V0IHNhbXBsZWQuIA0KYGBge3J9DQoxOk4gICNvcmRlcmVkIHNlcXVlbmNlDQppZHggPC0gc2FtcGxlKDE6TiwgTiwgcmVwbGFjZSA9IFRSVUUpDQppZHgNCg0Kc2xpY2UobGFiX2RhdGEzLGlkeCkgJT4lIGdncGxvdChhZXMoU3ViaklEKSkgKyBnZW9tX2JhcigpICsgeGxpbSgwLDI1KQ0KYGBgDQogIA0KIA0KSGVyZSdzIGhvdyBvdXIgcmVzYW1wbGVkIGRhdGEgd291bGQgbG9vay4gDQpgYGB7cn0NCmxhYl9kYXRhM19yMSA8LSBzbGljZShsYWJfZGF0YTMsIGlkeCkNCmxhYl9kYXRhM19yMQ0KYGBgDQogICAgDQoNCg0KRm9yIGEgY29ycmVsYXRpb24NCmBgYHtyLCByZXN1bHQ9J2hpZGUnfQ0KY29yX2JzIDwtIGxhYl9kYXRhMyAlPiUgDQogIGJvb3RzdHJhcCgxMDAwKSAlPiUgDQogIGRvKHRpZHkoY29yKC4kWU5ELCAuJFhORCkpKSAlPiUgDQogIHJlbmFtZShwZWFyc29uX3IgPSB4KSAlPiUgDQogIHVuZ3JvdXAoKSAlPiUgDQogIGFycmFuZ2UocGVhcnNvbl9yKQ0KDQpjb3JfcmVnIDwtIGNvci50ZXN0KGxhYl9kYXRhMyRYTkQsIGxhYl9kYXRhMyRZTkQpICU+JSB0aWR5KCkNCmBgYA0KDQpUaGUgYmx1ZSBsaW5lcyBhcmUgdGhlIHN0YW5kYXJkIGNvbmZpZGVuY2UgYm91bmRzIHdoZXJlYXMgdGhlIHJlZCBsaW5lcyBhcmUgdGhlIGJvb3N0cmFwcGVkIGNvbmZpZGVuY2UgYm91bmRzLiANCmBgYHtyfQ0KICANCmNvcl9icyAlPiUgYXJyYW5nZShyZXBsaWNhdGUpDQpjb3JfYnMgJT4lIA0KICBnZ3Bsb3QoYWVzKHBlYXJzb25fcikpICsgDQogIGdlb21faGlzdG9ncmFtKGZpbGw9ImxpZ2h0IGdyZXkiLCBjb2xvcj0iYmxhY2siKSArIA0KICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0PXBlYXJzb25fciksIGRhdGE9IHNsaWNlKGNvcl9icywyNSksIGNvbG9yPSJyZWQiLCBsaW5ldHlwZT0iZGFzaGVkIikgKyANCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdD1wZWFyc29uX3IpLCBkYXRhPSBzbGljZShjb3JfYnMsOTc1KSwgY29sb3I9InJlZCIsIGxpbmV0eXBlPSJkYXNoZWQiKSArIA0KICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0PWNvbmYubG93KSwgZGF0YT0gY29yX3JlZywgY29sb3I9ImJsdWUiLCBsaW5ldHlwZT0iZGFzaGVkIikgKyANCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdD1jb25mLmhpZ2gpLCBkYXRhPSBjb3JfcmVnLCBjb2xvcj0iYmx1ZSIsIGxpbmV0eXBlPSJkYXNoZWQiKQ0KDQpgYGANCg0KDQpIZXJlJ3Mgd2hhdCB0aGUgcmVncmVzc2lvbiBsaW5lIHdvdWxkIHJldmVhbCBmb3IgdGhpcyByZXNhbXBsZWQgZGF0YS4gDQpgYGB7cn0NCmxhYl9kYXRhMyAlPiUgDQogIHNsaWNlKHNhbXBsZSgxOm4oKSwgbigpLCByZXBsYWNlID0gVFJVRSkpICU+JSANCiAgZ2dwbG90KGFlcyhYTkQsIFlORCkpICsgZ2VvbV9qaXR0ZXIod2lkdGg9LjEsIGhlaWdodD0uMSkgKyBnZW9tX3Ntb290aChtZXRob2Q9bG0sIHNlPUYpDQpgYGANCiAgDQogIA0KSGVyZSdzIHdoYXQgdGhlIHJlZ3Jlc3Npb24gbGluZXMgd291bGQgbG9vayBsaWtlIGlmIHdlIHJlcGVhdGVkIHRoaXMgcHJvY2VkdXJlIDEsMDAwIHRpbWVzLiANCg0KYGBge3IsIHJlc3VsdHM9J2hpZGUnfQ0KYm9vdG5sc19hdWcgPC0gbGFiX2RhdGEzICU+JSBib290c3RyYXAoMTAwMCkgJT4lDQogICAgZG8oYXVnbWVudChsbShZTkQgfiBYTkQsIGRhdGE9LikpKQ0KYGBgDQoNCmBgYHtyfQ0KDQpnZ3Bsb3QoYm9vdG5sc19hdWcsIGFlcyhYTkQsIFlORCkpICsgZ2VvbV9wb2ludCgpICsNCiAgICBnZW9tX2xpbmUoYWVzKHk9LmZpdHRlZCwgZ3JvdXA9cmVwbGljYXRlKSwgYWxwaGE9LjEsIGNvbG9yPSJibHVlIikNCg0KYGBgDQo=