knitr::opts_knit$set(root.dir = rprojroot::find_rstudio_root_file()) # Set WD to Root
here::i_am("lab_mod/ch5-bootstrap.Rmd")
## here() starts at /Users/kittipos/Desktop/R_Programming/edx/ISLR
library(tidyverse)
## ── Attaching packages ─────────────────────────────────────── tidyverse 1.3.1 ──
## ✓ ggplot2 3.3.5     ✓ purrr   0.3.4
## ✓ tibble  3.1.3     ✓ dplyr   1.0.7
## ✓ tidyr   1.1.3     ✓ stringr 1.4.0
## ✓ readr   2.0.1     ✓ forcats 0.5.1
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## x dplyr::filter() masks stats::filter()
## x dplyr::lag()    masks stats::lag()
library(here)
theme_set(theme_bw())

I will demonstrate how to do block bootstrap using Xy dataset from ISLR’s practice questions in the chapter 5.

Load Data

Xy <- get(load(here("data/5.R.RData")))

Let’s Explore Data

Data Frame Xy has 3 numeric variables

str(Xy)
## 'data.frame':    1000 obs. of  3 variables:
##  $ X1: num  1.3 1.27 1.24 1.21 1.18 ...
##  $ X2: num  0.806 0.799 0.792 0.785 0.778 ...
##  $ y : num  0.299 0.318 0.337 0.356 0.375 ...
summary(Xy)
##        X1                X2                y           
##  Min.   :-0.8068   Min.   :-1.1753   Min.   :-0.75293  
##  1st Qu.:-0.1674   1st Qu.:-0.2339   1st Qu.:-0.06136  
##  Median : 0.2798   Median : 0.1824   Median : 0.30452  
##  Mean   : 0.3337   Mean   : 0.1288   Mean   : 0.35471  
##  3rd Qu.: 0.7017   3rd Qu.: 0.4646   3rd Qu.: 0.73283  
##  Max.   : 1.8531   Max.   : 1.7658   Max.   : 2.03922

Fit Linear Model

Supposed we want to fit a linear regression model of y on X1 and X2.

Q1: What is the standard error for coefficients of X2 or (\(\beta_2\)) ?

Xy_lm.fit <- lm(y ~ X1 + X2, data = Xy)

summary(Xy_lm.fit)
## 
## Call:
## lm(formula = y ~ X1 + X2, data = Xy)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -1.44171 -0.25468 -0.01736  0.33081  1.45860 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  0.26583    0.01988  13.372  < 2e-16 ***
## X1           0.14533    0.02593   5.604 2.71e-08 ***
## X2           0.31337    0.02923  10.722  < 2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.5451 on 997 degrees of freedom
## Multiple R-squared:  0.1171, Adjusted R-squared:  0.1154 
## F-statistic: 66.14 on 2 and 997 DF,  p-value: < 2.2e-16

As you can see that \(S.E.\) of \(\beta_2\) was 0.0292267

Helper function

Write a helper function to shuffles rows of Xy, fits the Linear model and return \(\beta_2\).

Xy_lm_boot_fn <- function(data, index){
  
  lm_coef <- coef(lm(y ~ X1 + X2, data = data, subset = index))
  lm_coef["X2"]
  
}

Let’s test the some iterations of the bootstapped \(\beta_2\).

set.seed(123)

index1 <- sample(1000, 1000, replace = TRUE)
index2 <- sample(1000, 1000, replace = TRUE)

Xy_lm_boot_fn(Xy, index1)
##        X2 
## 0.3113108
Xy_lm_boot_fn(Xy, index2)
##       X2 
## 0.324674

OK, It works!

Regular Bootstrap

I will use {boot} package to compute bootstrap of 1000 replicates using Xy_lm_boot_fn() as statistic argument in boot().

library(boot)
Xy_lm_boot <- boot(Xy, Xy_lm_boot_fn, R = 1000)

Xy_lm_boot
## 
## ORDINARY NONPARAMETRIC BOOTSTRAP
## 
## 
## Call:
## boot(data = Xy, statistic = Xy_lm_boot_fn, R = 1000)
## 
## 
## Bootstrap Statistics :
##     original        bias    std. error
## t1* 0.313367 -0.0009827422  0.03595101

The \(S.E.\) of the bootstapped \(\beta_2\) is 0.035951

Auto-Correlation

Why we need to do block bootstrap ?

Let’s see plots of observation in x-axis and each variables in y-axis. I will use {ggplot2} to do this.

First I need to add rownames to column and pivot to long format.

Xy_to_long <- function(data) {
  
  data %>% 
    tibble::rownames_to_column("Observations") %>% 
    dplyr::mutate(Observations = as.numeric(Observations)) %>% 
    tidyr::pivot_longer(cols = X1:y, names_to = "Variables", values_to = "Values") %>% 
    dplyr::mutate(Variables = factor(Variables, levels = c( "y", "X1", "X2")))
    
}
Xy_long <- Xy_to_long(Xy)
head(Xy_long)

And then plot using geom_line:

Xy_long %>%   
  ggplot(aes(Observations, Values, color = Variables, linetype = Variables)) +
  geom_line(aes(group = Variables)) +
  labs(title = "Auto-Correlation between Variables", 
       caption = "\"Xy\" dataset from ISLR")

(You can also do the same thing by using base R plot by matplot(Xy,type="l") as in ISLR’s example.)

From this plot, It can be seen that there are correlation between observations (rows). A given data point (e.g. \((X1_i, X2_i, Y_i)\)) would influence the value of next data points (e.g. \((X1_{i+1}, X2_{i+1}, Y_{i+1})\)). This is called auto-correlation.

Thus, using regular bootstap might rearrange the consecutive rows that are correlated and modify the result.

Let’s plot observation vs values after bootstapped.

set.seed(123)

## Shuffle Rows
index1 <- sample(1000, 1000, replace = TRUE)
Xy_row_shuffle1 <- Xy[index1, ]


rownames(Xy_row_shuffle1) <- 1:1000
Xy_to_long(Xy_row_shuffle1) %>%   
  ggplot(aes(Observations, Values, color = Variables, linetype = Variables)) +
  geom_line(aes(group = Variables)) +
  labs(title = "Lost Auto-Correlation after Bootstrapped", 
       caption = "bootstapped \"Xy\" dataset from ISLR")

Block Bootstrap

Helper function

I’ve created a helper function to obtain an indexes of block bootstrap with following arguments.

  • n_block: Total number of blocks
  • size: Number of observation in each block
get_block_index <- function(n_block, # Number of block
                            size # Number of observation in each block
                            ) {
  
  block1 <- 1:size
  
  step <- 0:(n_block-1)*size
  step_boot <- sample(step, n_block, replace = TRUE)
    
  step_expand <- rep(step_boot, each = size)
  block_expand <- rep(block1, n_block)
  
  block_expand + step_expand

}

Let’s test get_block_index() function.

For example: 3 blocks with 5 observations each.

set.seed(123)

get_block_index(n_block =  3,size =  5)
##  [1] 11 12 13 14 15 11 12 13 14 15 11 12 13 14 15
get_block_index(n_block =  3,size =  5)
##  [1]  6  7  8  9 10 11 12 13 14 15  6  7  8  9 10

Ok, Looks like it works!

Create Block Bootstrap Dataset

I will add “block” column containing numeric vector form 1-10 to flag each block so we can keep tract of data when we performed a block bootstrap.

Xy_block10 <- Xy
# Add "block" column to keep tract
Xy_block10$block <- factor(rep(1:10, each = 100))

summary(Xy_block10)
##        X1                X2                y                block    
##  Min.   :-0.8068   Min.   :-1.1753   Min.   :-0.75293   1      :100  
##  1st Qu.:-0.1674   1st Qu.:-0.2339   1st Qu.:-0.06136   2      :100  
##  Median : 0.2798   Median : 0.1824   Median : 0.30452   3      :100  
##  Mean   : 0.3337   Mean   : 0.1288   Mean   : 0.35471   4      :100  
##  3rd Qu.: 0.7017   3rd Qu.: 0.4646   3rd Qu.: 0.73283   5      :100  
##  Max.   : 1.8531   Max.   : 1.7658   Max.   : 2.03922   6      :100  
##                                                         (Other):400

Now, try simulate block bootstapped dataset:

set.seed(123)
index_block10_a <- get_block_index(n_block = 10, size = 100)
index_block10_b <- get_block_index(n_block = 10, size = 100)

Xy_block10[index_block10_a, ]$block %>% unique()
## [1] 3  10 2  6  5  4  9 
## Levels: 1 2 3 4 5 6 7 8 9 10
Xy_block10[index_block10_b, ]$block %>% unique()
## [1] 5  3  9  8  10 7 
## Levels: 1 2 3 4 5 6 7 8 9 10

Block bootstrapped of Linear Model

Finally, I will perform block bootstrapped of the coefficient \(\beta_2\).

Recall that \(\beta_2\) is a coefficient of X2 in the linear regression model of y on X1 and X2.

I will need to create this final wrapper function Xy_lm_boot_fn_block(). The main argument were the followings:

  • data: Xy dataset
  • n_block: Total number of blocks to bootstrap
  • size: Number of observation in each block
  • R: The number of bootstrap replicates
Xy_lm_boot_fn_block <- function(data,
                                n_block = 10, size = 100, 
                                R,
                                return_raw = FALSE
                                ) {
  
  coeffs <- numeric(n_block)
  for (i in 1:R) {
    
    index <- get_block_index(n_block, size)
    coeffs[i] <- Xy_lm_boot_fn(data = data, index = index)
    
  }

  if(return_raw) return(coeffs)
  list(original = mean(coeffs),
       std.err = sd(coeffs))
  
}

Final Results

Block bootstrapped of linear model y on X1 and X2; then extract mean and standard error of bootstrapped coefficient X2 (\(\beta_2\)).

  • Number of block = 10
  • Size of each block = 100
  • Replicate = 1000
Xy_lm_boot_fn_block(Xy_block10, n_block = 10, size = 100, R = 1000)
## $original
## [1] 0.3862743
## 
## $std.err
## [1] 0.3200023

Compare it with regular bootstrap

Xy_lm_boot
## 
## ORDINARY NONPARAMETRIC BOOTSTRAP
## 
## 
## Call:
## boot(data = Xy, statistic = Xy_lm_boot_fn, R = 1000)
## 
## 
## Bootstrap Statistics :
##     original        bias    std. error
## t1* 0.313367 -0.0009827422  0.03595101

And with no bootstrap (X2)

summary(Xy_lm.fit)
## 
## Call:
## lm(formula = y ~ X1 + X2, data = Xy)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -1.44171 -0.25468 -0.01736  0.33081  1.45860 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  0.26583    0.01988  13.372  < 2e-16 ***
## X1           0.14533    0.02593   5.604 2.71e-08 ***
## X2           0.31337    0.02923  10.722  < 2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.5451 on 997 degrees of freedom
## Multiple R-squared:  0.1171, Adjusted R-squared:  0.1154 
## F-statistic: 66.14 on 2 and 997 DF,  p-value: < 2.2e-16

Plot of bootstrapped coefficients

Xy_boot_coeffs <- Xy_lm_boot_fn_block(Xy_block10, n_block = 10, size = 100, R = 1000, 
                    return_raw = TRUE)
library(latex2exp)

data.frame(beta2 = Xy_boot_coeffs) %>% 
  ggplot(aes(beta2)) +
  geom_histogram(fill = "grey", color = "black") +
  
  geom_vline(aes(xintercept = mean(Xy_boot_coeffs)), color = "red") +
  annotate("label", x = mean(Xy_boot_coeffs), y = 125, 
           label = "mean", 
           color = "red") +
  
  labs(x = TeX("$\\hat{\\beta^*_2}$"), y = "Count", 
       title = TeX("Distribution of block bootstrapped $\\beta_2$")) 

LS0tCnRpdGxlOiAiQmxvY2sgQm9vdHN0cmFwIgpkYXRlOiAiMjAyMS0wOS0xMCIKYXV0aG9yOiAiS2l0dGlwb3MgU2lyaXZvbmdydW5nc29uIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIGRmX3ByaW50OiBwYWdlZAogICAgY29kZV9mb2xkaW5nOiAic2hvdyIKICAgIHRvYzogVFJVRQogICAgdG9jX2Zsb2F0OiBUUlVFCiAgICBjb2RlX2Rvd25sb2FkOiBUUlVFCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9VFJVRX0Ka25pdHI6Om9wdHNfa25pdCRzZXQocm9vdC5kaXIgPSBycHJvanJvb3Q6OmZpbmRfcnN0dWRpb19yb290X2ZpbGUoKSkgIyBTZXQgV0QgdG8gUm9vdApoZXJlOjppX2FtKCJsYWJfbW9kL2NoNS1ib290c3RyYXAuUm1kIikKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoaGVyZSkKdGhlbWVfc2V0KHRoZW1lX2J3KCkpCmBgYAoKSSB3aWxsIGRlbW9uc3RyYXRlIGhvdyB0byBkbyBibG9jayBib290c3RyYXAgdXNpbmcgKipYeSoqIGRhdGFzZXQgZnJvbSBJU0xSJ3MgcHJhY3RpY2UgcXVlc3Rpb25zIGluIHRoZSBjaGFwdGVyIDUuCgojIyBMb2FkIERhdGEKCmBgYHtyIGxvYWR9Clh5IDwtIGdldChsb2FkKGhlcmUoImRhdGEvNS5SLlJEYXRhIikpKQpgYGAKCgojIExldCdzIEV4cGxvcmUgRGF0YQoKRGF0YSBGcmFtZSBgWHlgIGhhcyAzIG51bWVyaWMgdmFyaWFibGVzIAoKYGBge3J9CnN0cihYeSkKYGBgCgpgYGB7cn0Kc3VtbWFyeShYeSkKYGBgCgojIEZpdCBMaW5lYXIgTW9kZWwKClN1cHBvc2VkIHdlIHdhbnQgdG8gZml0IGEgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwgb2YgYHlgIG9uIGBYMWAgYW5kIGBYMmAuIAoKIyMjIFExOiBXaGF0IGlzIHRoZSBzdGFuZGFyZCBlcnJvciBmb3IgY29lZmZpY2llbnRzIG9mIFgyIG9yICgkXGJldGFfMiQpID8KCmBgYHtyIFh5X2xtLmZpdH0KWHlfbG0uZml0IDwtIGxtKHkgfiBYMSArIFgyLCBkYXRhID0gWHkpCgpzdW1tYXJ5KFh5X2xtLmZpdCkKYGBgCgpgYGB7ciwgaW5jbHVkZT1GQUxTRX0KWDJfc3RkLmVyciA8LSBzcXJ0KGRpYWcodmNvdihYeV9sbS5maXQpKSlbIlgyIl0KYGBgCgpBcyB5b3UgY2FuIHNlZSB0aGF0ICRTLkUuJCBvZiAkXGJldGFfMiQgd2FzIGByIFgyX3N0ZC5lcnJgCgojIyMgSGVscGVyIGZ1bmN0aW9uIAoKV3JpdGUgYSBoZWxwZXIgZnVuY3Rpb24gdG8gc2h1ZmZsZXMgcm93cyBvZiBgWHlgLCBmaXRzIHRoZSBMaW5lYXIgbW9kZWwgYW5kIHJldHVybiAkXGJldGFfMiQuCgpgYGB7ciBYeV9sbV9ib290X2ZufQpYeV9sbV9ib290X2ZuIDwtIGZ1bmN0aW9uKGRhdGEsIGluZGV4KXsKICAKICBsbV9jb2VmIDwtIGNvZWYobG0oeSB+IFgxICsgWDIsIGRhdGEgPSBkYXRhLCBzdWJzZXQgPSBpbmRleCkpCiAgbG1fY29lZlsiWDIiXQogIAp9CmBgYAoKTGV0J3MgdGVzdCB0aGUgc29tZSBpdGVyYXRpb25zIG9mIHRoZSBib290c3RhcHBlZCAkXGJldGFfMiQuCgpgYGB7cn0Kc2V0LnNlZWQoMTIzKQoKaW5kZXgxIDwtIHNhbXBsZSgxMDAwLCAxMDAwLCByZXBsYWNlID0gVFJVRSkKaW5kZXgyIDwtIHNhbXBsZSgxMDAwLCAxMDAwLCByZXBsYWNlID0gVFJVRSkKClh5X2xtX2Jvb3RfZm4oWHksIGluZGV4MSkKWHlfbG1fYm9vdF9mbihYeSwgaW5kZXgyKQpgYGAKCk9LLCBJdCB3b3JrcyEKCiMgUmVndWxhciBCb290c3RyYXAKCkkgd2lsbCB1c2UgYHtib290fWAgcGFja2FnZSB0byBjb21wdXRlIGJvb3RzdHJhcCBvZiAxMDAwIHJlcGxpY2F0ZXMgdXNpbmcgYFh5X2xtX2Jvb3RfZm4oKWAgYXMgYHN0YXRpc3RpY2AgYXJndW1lbnQgaW4gYGJvb3QoKWAuCgpgYGB7cn0KbGlicmFyeShib290KQpgYGAKCmBgYHtyfQpYeV9sbV9ib290IDwtIGJvb3QoWHksIFh5X2xtX2Jvb3RfZm4sIFIgPSAxMDAwKQoKWHlfbG1fYm9vdApgYGAKClRoZSAkUy5FLiQgb2YgdGhlIGJvb3RzdGFwcGVkICRcYmV0YV8yJCBpcyBgciBzZChYeV9sbV9ib290JHQpYAoKYGBge3IgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0Kc2QoWHlfbG1fYm9vdCR0KQpgYGAKCgojIEF1dG8tQ29ycmVsYXRpb24KCioqV2h5IHdlIG5lZWQgdG8gZG8gYmxvY2sgYm9vdHN0cmFwID8qKiAKCkxldCdzIHNlZSBwbG90cyBvZiBvYnNlcnZhdGlvbiBpbiB4LWF4aXMgYW5kIGVhY2ggdmFyaWFibGVzIGluIHktYXhpcy4KSSB3aWxsIHVzZSBge2dncGxvdDJ9YCB0byBkbyB0aGlzLiAKCkZpcnN0IEkgbmVlZCB0byBhZGQgcm93bmFtZXMgdG8gY29sdW1uIGFuZCBwaXZvdCB0byBsb25nIGZvcm1hdC4KCmBgYHtyIFh5X3RvX2xvbmd9Clh5X3RvX2xvbmcgPC0gZnVuY3Rpb24oZGF0YSkgewogIAogIGRhdGEgJT4lIAogICAgdGliYmxlOjpyb3duYW1lc190b19jb2x1bW4oIk9ic2VydmF0aW9ucyIpICU+JSAKICAgIGRwbHlyOjptdXRhdGUoT2JzZXJ2YXRpb25zID0gYXMubnVtZXJpYyhPYnNlcnZhdGlvbnMpKSAlPiUgCiAgICB0aWR5cjo6cGl2b3RfbG9uZ2VyKGNvbHMgPSBYMTp5LCBuYW1lc190byA9ICJWYXJpYWJsZXMiLCB2YWx1ZXNfdG8gPSAiVmFsdWVzIikgJT4lIAogICAgZHBseXI6Om11dGF0ZShWYXJpYWJsZXMgPSBmYWN0b3IoVmFyaWFibGVzLCBsZXZlbHMgPSBjKCAieSIsICJYMSIsICJYMiIpKSkKICAgIAp9CmBgYAoKCmBgYHtyIFh5X2xvbmd9Clh5X2xvbmcgPC0gWHlfdG9fbG9uZyhYeSkKaGVhZChYeV9sb25nKQpgYGAKCkFuZCB0aGVuIHBsb3QgdXNpbmcgYGdlb21fbGluZWA6CgpgYGB7ciBvYnNfcGxvdDF9Clh5X2xvbmcgJT4lICAgCiAgZ2dwbG90KGFlcyhPYnNlcnZhdGlvbnMsIFZhbHVlcywgY29sb3IgPSBWYXJpYWJsZXMsIGxpbmV0eXBlID0gVmFyaWFibGVzKSkgKwogIGdlb21fbGluZShhZXMoZ3JvdXAgPSBWYXJpYWJsZXMpKSArCiAgbGFicyh0aXRsZSA9ICJBdXRvLUNvcnJlbGF0aW9uIGJldHdlZW4gVmFyaWFibGVzIiwgCiAgICAgICBjYXB0aW9uID0gIlwiWHlcIiBkYXRhc2V0IGZyb20gSVNMUiIpCmBgYAoKKFlvdSBjYW4gYWxzbyBkbyB0aGUgc2FtZSB0aGluZyBieSB1c2luZyBiYXNlIFIgcGxvdCBieSBgbWF0cGxvdChYeSx0eXBlPSJsIilgIGFzIGluIElTTFIncyBleGFtcGxlLikKCkZyb20gdGhpcyBwbG90LCBJdCBjYW4gYmUgc2VlbiB0aGF0IHRoZXJlIGFyZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIG9ic2VydmF0aW9ucyAocm93cykuCkEgZ2l2ZW4gZGF0YSBwb2ludCAoZS5nLiAkKFgxX2ksIFgyX2ksIFlfaSkkKSB3b3VsZCBpbmZsdWVuY2UgdGhlIHZhbHVlIG9mIG5leHQgZGF0YSBwb2ludHMgKGUuZy4gJChYMV97aSsxfSwgWDJfe2krMX0sIFlfe2krMX0pJCkuIFRoaXMgaXMgY2FsbGVkIGF1dG8tY29ycmVsYXRpb24uCgpUaHVzLCB1c2luZyByZWd1bGFyIGJvb3RzdGFwIG1pZ2h0IHJlYXJyYW5nZSB0aGUgY29uc2VjdXRpdmUgcm93cyB0aGF0IGFyZSBjb3JyZWxhdGVkIGFuZCBtb2RpZnkgdGhlIHJlc3VsdC4KCioqTGV0J3MgcGxvdCBvYnNlcnZhdGlvbiB2cyB2YWx1ZXMgYWZ0ZXIgYm9vdHN0YXBwZWQuKioKCmBgYHtyfQpzZXQuc2VlZCgxMjMpCgojIyBTaHVmZmxlIFJvd3MKaW5kZXgxIDwtIHNhbXBsZSgxMDAwLCAxMDAwLCByZXBsYWNlID0gVFJVRSkKWHlfcm93X3NodWZmbGUxIDwtIFh5W2luZGV4MSwgXQoKCnJvd25hbWVzKFh5X3Jvd19zaHVmZmxlMSkgPC0gMToxMDAwCmBgYAoKCmBgYHtyIG9ic19wbG90Mn0KWHlfdG9fbG9uZyhYeV9yb3dfc2h1ZmZsZTEpICU+JSAgIAogIGdncGxvdChhZXMoT2JzZXJ2YXRpb25zLCBWYWx1ZXMsIGNvbG9yID0gVmFyaWFibGVzLCBsaW5ldHlwZSA9IFZhcmlhYmxlcykpICsKICBnZW9tX2xpbmUoYWVzKGdyb3VwID0gVmFyaWFibGVzKSkgKwogIGxhYnModGl0bGUgPSAiTG9zdCBBdXRvLUNvcnJlbGF0aW9uIGFmdGVyIEJvb3RzdHJhcHBlZCIsIAogICAgICAgY2FwdGlvbiA9ICJib290c3RhcHBlZCBcIlh5XCIgZGF0YXNldCBmcm9tIElTTFIiKQpgYGAKCgojIEJsb2NrIEJvb3RzdHJhcAoKIyMjIEhlbHBlciBmdW5jdGlvbgoKSSd2ZSBjcmVhdGVkIGEgaGVscGVyIGZ1bmN0aW9uIHRvIG9idGFpbiBhbiBpbmRleGVzIG9mIGJsb2NrIGJvb3RzdHJhcCB3aXRoIGZvbGxvd2luZyBhcmd1bWVudHMuCgotICAgYG5fYmxvY2tgOiBUb3RhbCBudW1iZXIgb2YgYmxvY2tzCi0gICBgc2l6ZWA6IE51bWJlciBvZiBvYnNlcnZhdGlvbiBpbiBlYWNoIGJsb2NrCgpgYGB7ciBnZXRfYmxvY2tfaW5kZXh9CmdldF9ibG9ja19pbmRleCA8LSBmdW5jdGlvbihuX2Jsb2NrLCAjIE51bWJlciBvZiBibG9jawogICAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZSAjIE51bWJlciBvZiBvYnNlcnZhdGlvbiBpbiBlYWNoIGJsb2NrCiAgICAgICAgICAgICAgICAgICAgICAgICAgICApIHsKICAKICBibG9jazEgPC0gMTpzaXplCiAgCiAgc3RlcCA8LSAwOihuX2Jsb2NrLTEpKnNpemUKICBzdGVwX2Jvb3QgPC0gc2FtcGxlKHN0ZXAsIG5fYmxvY2ssIHJlcGxhY2UgPSBUUlVFKQogICAgCiAgc3RlcF9leHBhbmQgPC0gcmVwKHN0ZXBfYm9vdCwgZWFjaCA9IHNpemUpCiAgYmxvY2tfZXhwYW5kIDwtIHJlcChibG9jazEsIG5fYmxvY2spCiAgCiAgYmxvY2tfZXhwYW5kICsgc3RlcF9leHBhbmQKCn0KYGBgCgpMZXQncyB0ZXN0IGBnZXRfYmxvY2tfaW5kZXgoKWAgZnVuY3Rpb24uIAoKRm9yIGV4YW1wbGU6IDMgYmxvY2tzIHdpdGggNSBvYnNlcnZhdGlvbnMgZWFjaC4KCmBgYHtyfQpzZXQuc2VlZCgxMjMpCgpnZXRfYmxvY2tfaW5kZXgobl9ibG9jayA9ICAzLHNpemUgPSAgNSkKZ2V0X2Jsb2NrX2luZGV4KG5fYmxvY2sgPSAgMyxzaXplID0gIDUpCmBgYAoKT2ssIExvb2tzIGxpa2UgaXQgd29ya3MhCgojIyMgQ3JlYXRlIEJsb2NrIEJvb3RzdHJhcCBEYXRhc2V0CgpJIHdpbGwgYWRkICJibG9jayIgY29sdW1uIGNvbnRhaW5pbmcgbnVtZXJpYyB2ZWN0b3IgZm9ybSAxLTEwIHRvIGZsYWcgZWFjaCBibG9jayBzbyB3ZSBjYW4ga2VlcCB0cmFjdCBvZiBkYXRhIHdoZW4gd2UgcGVyZm9ybWVkIGEgYmxvY2sgYm9vdHN0cmFwLgoKYGBge3IgWHlfYmxvY2sxMH0KWHlfYmxvY2sxMCA8LSBYeQojIEFkZCAiYmxvY2siIGNvbHVtbiB0byBrZWVwIHRyYWN0Clh5X2Jsb2NrMTAkYmxvY2sgPC0gZmFjdG9yKHJlcCgxOjEwLCBlYWNoID0gMTAwKSkKCnN1bW1hcnkoWHlfYmxvY2sxMCkKYGBgCgpOb3csIHRyeSBzaW11bGF0ZSBibG9jayBib290c3RhcHBlZCBkYXRhc2V0OgoKYGBge3J9CnNldC5zZWVkKDEyMykKaW5kZXhfYmxvY2sxMF9hIDwtIGdldF9ibG9ja19pbmRleChuX2Jsb2NrID0gMTAsIHNpemUgPSAxMDApCmluZGV4X2Jsb2NrMTBfYiA8LSBnZXRfYmxvY2tfaW5kZXgobl9ibG9jayA9IDEwLCBzaXplID0gMTAwKQoKWHlfYmxvY2sxMFtpbmRleF9ibG9jazEwX2EsIF0kYmxvY2sgJT4lIHVuaXF1ZSgpClh5X2Jsb2NrMTBbaW5kZXhfYmxvY2sxMF9iLCBdJGJsb2NrICU+JSB1bmlxdWUoKQpgYGAKCiMjIEJsb2NrIGJvb3RzdHJhcHBlZCBvZiBMaW5lYXIgTW9kZWwKCkZpbmFsbHksIEkgd2lsbCBwZXJmb3JtIGJsb2NrIGJvb3RzdHJhcHBlZCBvZiB0aGUgY29lZmZpY2llbnQgJFxiZXRhXzIkLgoKUmVjYWxsIHRoYXQgJFxiZXRhXzIkIGlzIGEgY29lZmZpY2llbnQgb2YgYFgyYCBpbiB0aGUgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwgb2YgYHlgIG9uIGBYMWAgYW5kIGBYMmAuCgpJIHdpbGwgbmVlZCB0byBjcmVhdGUgdGhpcyBmaW5hbCB3cmFwcGVyIGZ1bmN0aW9uIGBYeV9sbV9ib290X2ZuX2Jsb2NrKClgLgpUaGUgbWFpbiBhcmd1bWVudCB3ZXJlIHRoZSBmb2xsb3dpbmdzOgoKLSAgIGBkYXRhYDogWHkgZGF0YXNldAotICAgYG5fYmxvY2tgOiBUb3RhbCBudW1iZXIgb2YgYmxvY2tzIHRvIGJvb3RzdHJhcAotICAgYHNpemVgOiBOdW1iZXIgb2Ygb2JzZXJ2YXRpb24gaW4gZWFjaCBibG9jayAKLSAgIGBSYDogVGhlIG51bWJlciBvZiBib290c3RyYXAgcmVwbGljYXRlcwoKCmBgYHtyIFh5X2xtX2Jvb3RfZm5fYmxvY2t9Clh5X2xtX2Jvb3RfZm5fYmxvY2sgPC0gZnVuY3Rpb24oZGF0YSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuX2Jsb2NrID0gMTAsIHNpemUgPSAxMDAsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuX3JhdyA9IEZBTFNFCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSB7CiAgCiAgY29lZmZzIDwtIG51bWVyaWMobl9ibG9jaykKICBmb3IgKGkgaW4gMTpSKSB7CiAgICAKICAgIGluZGV4IDwtIGdldF9ibG9ja19pbmRleChuX2Jsb2NrLCBzaXplKQogICAgY29lZmZzW2ldIDwtIFh5X2xtX2Jvb3RfZm4oZGF0YSA9IGRhdGEsIGluZGV4ID0gaW5kZXgpCiAgICAKICB9CgogIGlmKHJldHVybl9yYXcpIHJldHVybihjb2VmZnMpCiAgbGlzdChvcmlnaW5hbCA9IG1lYW4oY29lZmZzKSwKICAgICAgIHN0ZC5lcnIgPSBzZChjb2VmZnMpKQogIAp9CmBgYAoKCiMjIEZpbmFsIFJlc3VsdHMKCkJsb2NrIGJvb3RzdHJhcHBlZCBvZiBsaW5lYXIgbW9kZWwgYHlgIG9uIGBYMWAgYW5kIGBYMmA7IHRoZW4gZXh0cmFjdCBtZWFuIGFuZCBzdGFuZGFyZCBlcnJvciBvZiBib290c3RyYXBwZWQgY29lZmZpY2llbnQgYFgyYCAoJFxiZXRhXzIkKS4KCi0gICBOdW1iZXIgb2YgYmxvY2sgPSAxMAotICAgU2l6ZSBvZiBlYWNoIGJsb2NrID0gMTAwCi0gICBSZXBsaWNhdGUgPSAxMDAwCgpgYGB7cn0KWHlfbG1fYm9vdF9mbl9ibG9jayhYeV9ibG9jazEwLCBuX2Jsb2NrID0gMTAsIHNpemUgPSAxMDAsIFIgPSAxMDAwKQpgYGAKCkNvbXBhcmUgaXQgd2l0aCByZWd1bGFyIGJvb3RzdHJhcAoKYGBge3J9Clh5X2xtX2Jvb3QKYGBgCgpBbmQgd2l0aCBubyBib290c3RyYXAgKGBYMmApCgpgYGB7cn0Kc3VtbWFyeShYeV9sbS5maXQpCmBgYAoKIyMjIFBsb3Qgb2YgYm9vdHN0cmFwcGVkIGNvZWZmaWNpZW50cwoKYGBge3IgWHlfYm9vdF9jb2VmZnN9Clh5X2Jvb3RfY29lZmZzIDwtIFh5X2xtX2Jvb3RfZm5fYmxvY2soWHlfYmxvY2sxMCwgbl9ibG9jayA9IDEwLCBzaXplID0gMTAwLCBSID0gMTAwMCwgCiAgICAgICAgICAgICAgICAgICAgcmV0dXJuX3JhdyA9IFRSVUUpCmBgYAoKCmBgYHtyIGhpc3QsIG1lc3NhZ2U9RkFMU0V9CmxpYnJhcnkobGF0ZXgyZXhwKQoKZGF0YS5mcmFtZShiZXRhMiA9IFh5X2Jvb3RfY29lZmZzKSAlPiUgCiAgZ2dwbG90KGFlcyhiZXRhMikpICsKICBnZW9tX2hpc3RvZ3JhbShmaWxsID0gImdyZXkiLCBjb2xvciA9ICJibGFjayIpICsKICAKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gbWVhbihYeV9ib290X2NvZWZmcykpLCBjb2xvciA9ICJyZWQiKSArCiAgYW5ub3RhdGUoImxhYmVsIiwgeCA9IG1lYW4oWHlfYm9vdF9jb2VmZnMpLCB5ID0gMTI1LCAKICAgICAgICAgICBsYWJlbCA9ICJtZWFuIiwgCiAgICAgICAgICAgY29sb3IgPSAicmVkIikgKwogIAogIGxhYnMoeCA9IFRlWCgiJFxcaGF0e1xcYmV0YV4qXzJ9JCIpLCB5ID0gIkNvdW50IiwgCiAgICAgICB0aXRsZSA9IFRlWCgiRGlzdHJpYnV0aW9uIG9mIGJsb2NrIGJvb3RzdHJhcHBlZCAkXFxiZXRhXzIkIikpIApgYGAKCgoK