library(tidyverse)    # for plotting and summarizing
library(equatiomatic) # for model equations
library(ggridges)     # for ridge plots
library(ggmosaic)     # for mosaic plots
library(moderndive)   # for nice model output
library(broom)        # for nice model output 
library(infer)        # for making inferences about models
theme_set(theme_minimal()) #changes the theme of ggplots to theme_minimal, my personal favorite

GOAL:

By the end of these notes and activities, you should be able to perform the following tasks.

  • Use a bootstrap distribution to understand how model coefficients vary from sample to sample.
  • Apply bootstrap code I have written to a new model.
  • Create a histogram to represent the bootstrap distribution for a model coefficient.

Previously we talked about how a sample proportion varies from sample to sample and were introduced to the bootstrap technique. Now, we would like to apply that to linear models to learn about how coefficient estimates vary from sample to sample.

Let’s once again use the kc_house_data, a modified version of the house_data from the moderndive library. We are going to pretend that this is the entire population of houses.

kc_house_data <-
  house_prices %>% 
  filter(bedrooms<=5, bedrooms>0) %>% 
  mutate(grade_CAT = fct_relevel(ifelse(grade %in% "1":"7", "Low",
                                        ifelse(grade == "8", "Medium","High")),
                                     "Low", "Medium", "High"),
         age=2015-yr_built)

Sampling Distributions

We will examine a really simple model that uses log(sqft_living) to explain log(price). We are going to think of this as the “population model”.

kc_house_data %>% 
  ggplot(aes(x=log(sqft_living), y = log(price))) +
  geom_point(alpha=.5, size = .2)

lm_simple <- lm(log(price) ~ log(sqft_living),
                data=kc_house_data)
tidy(lm_simple)

The “population model” is:

extract_eq(lm_simple, 
           ital_vars = TRUE, 
           wrap = TRUE, 
           use_coefs = TRUE)

\[ \begin{aligned} \widehat{log(price)} &= 6.74 + 0.84(log(sqft\_living)) \end{aligned} \]

Let’s plot that line on the scatterplot of the data.

augment(lm_simple, data=kc_house_data) %>% 
  ggplot(aes(x=log(sqft_living), y = log(price))) +
  geom_point(alpha = .5, size = .2) +
  geom_line(aes(y=.fitted), color="darkred")

Now let’s say that I was going to collect this data myself and I didn’t have time (or the technical skills) to get all 21,613 observations. So, instead, I collect a sample of 500 observations.

I will mimic this by sampling from the entire dataset using the sample_n() function. Reminder: The set.seed() function at the beginning allows you to replicate the random sampling process so that you get the same random sample every time the code runs (including when you knit the document). This also means that if someone else uses that same seed, they will get the same result, as long as they started with the same data. It is very important to use set.seed() anytime we conduct a random process so that we can replicate it. It does not matter what number we put in there.

Then, fit the model that uses log(sqft_living) to explain log(price). How does this model compare to the model that used all the data?

set.seed(327)

samp1 <- kc_house_data %>% 
  sample_n(size=500) 

lm_samp1 <- lm(log(price) ~ log(sqft_living), 
               data=samp1)

tidy(lm_samp1)
extract_eq(lm_samp1, 
           ital_vars = TRUE, 
           wrap = TRUE, 
           use_coefs = TRUE)

\[ \begin{aligned} \widehat{log(price)} &= 6.59 + 0.86(log(sqft\_living)) \end{aligned} \]

Let’s plot the lines. I am going to use the geom_smooth function to do this because it will come in handy later.

augment(lm_simple, data=kc_house_data) %>% 
  ggplot(aes(x=log(sqft_living), y = log(price))) +
  geom_point(alpha=.2, size = .2) +
  geom_point(data = samp1, color="lightblue", alpha = .2, size = .2) +
  geom_line(aes(y=.fitted), color="darkred") +
  geom_smooth(data = samp1, method = "lm", se = FALSE, 
              color="lightblue", size=.5)

Let’s take another sample of size 500 and fit another line. Use a different seed from the first sample so they are different random samples.

set.seed(300)

samp2 <- kc_house_data %>% 
  sample_n(size=500) 

lm_samp2 <- lm(log(price) ~ log(sqft_living), 
               data=samp2)

tidy(lm_samp2)
extract_eq(lm_samp2, 
           ital_vars = TRUE, 
           wrap = TRUE, 
           use_coefs = TRUE)

\[ \begin{aligned} \widehat{log(price)} &= 6.72 + 0.84(log(sqft\_living)) \end{aligned} \]

And plot this line …

augment(lm_simple, data=kc_house_data) %>% 
  ggplot(aes(x=log(sqft_living), y = log(price))) +
  geom_point(alpha=.2, size = .2) +
  geom_line(aes(y=.fitted), color="darkred") +
  geom_smooth(data = samp1, method = "lm", se = FALSE, 
              color="lightblue", size = .5) +
  geom_smooth(data = samp2, method = "lm", se = FALSE, 
              color="lightblue", size = .5) 
## `geom_smooth()` using formula 'y ~ x'
## `geom_smooth()` using formula 'y ~ x'

Now, in order to understand sampling variability we need to be able to this many times. Rather than having us run different models one by one to learn about the variability of the coefficients, we can use simulation to do that for us.

Like in the Reese’s Pieces activity, we will use the rep_sample_n() function to take multiple repeated samples. Below we take 200 samples of size 500 from the “population”, kc_house_data. We are taking samples without replacement this time because we are pretending that we are sampling from the population, like when we sampled Reese’s Pieces using the applets. How many observations are in samples_200?

set.seed(1113)
samples_200 <- rep_sample_n(kc_house_data, #data we're sampling from
                            size = 500, #size of each sample
                            reps = 200, #how many samples to take
                            replace = FALSE #with or without
                                            #replacement?
                            )

GOAL: For each sample, we would like to fit a model that uses log(sqft_living) to predict log(price). We would like to understand how the intercept and slope vary from sample to sample.

First, let’s do this graphically. What do you observe?

#This first section plots the "population" points:
augment(lm_simple, data=kc_house_data) %>% 
  ggplot(aes(x=log(sqft_living), y = log(price))) + 
  geom_point(alpha=.2, color="darkgray", size = .2) + 
#This code plots each of the lines  
  geom_smooth(data = samples_200, #Use the samples_200 data
              aes(group = replicate), #"For each replicate"
              method = "lm", se = FALSE, #Use lm() to fit a model
                                         #where log(sqft_living)
                                         #explains log(price)
              color="lightblue", size = .5) + #color the lines lightblue
  geom_line(aes(y=.fitted), color="darkred")  #add the "population" line
## `geom_smooth()` using formula 'y ~ x'

Now, let’s actually fit the model 200 times. Look carefully at the output. What is the code doing on the line with “???”?

model_200_times <-
  samples_200 %>% #start with the 200 samples of size 500
  group_by(replicate) %>% #for each replicate 
  summarize(lm(log(price) ~ log(sqft_living)) %>% tidy()) %>% #???
  ungroup() #ungroup the data - good to do this when finished with
## `summarise()` has grouped output by 'replicate'. You can override using the `.groups` argument.
            #grouped calculations

model_200_times

We can now look at the sampling distributions of the estimated slope and intercept. These show us how we expect those statistics to vary from sample to sample. I also put vertical lines where the actual intercept and slope (from the “population model”) are located.

#slope
model_200_times %>% #200 models
  filter(term == "log(sqft_living)") %>% #filter to the term we're interested in
  ggplot(aes(x = estimate)) + 
  geom_histogram(bins = 20) + #create a histogram of the coefficients
  geom_vline(xintercept = 0.8351142, color = "darkred") +
  labs(title = bquote("Distribution of" ~ hat(beta)[1]))

#intercept
model_200_times %>% 
  filter(term == "(Intercept)") %>% # LOOK CAREFULLY!!
  ggplot(aes(x = estimate)) +
  geom_histogram(bins = 20) +
  geom_vline(xintercept = 6.7431084, color = "darkred") +
  labs(title = bquote("Distribution of" ~ hat(beta)[0]))

YOUR TURN!

  1. What is an observation in the histograms above?

  2. How would you describe these distributions in terms of shape?

  3. Compute the mean and standard deviation of the slope and intercept estimates. The standard deviation here has a special name, a standard error, or SE for short. A standard error is the standard deviation of a statistic. I’ve started the code for you below. Remove the eval=FALSE.

model_200_times %>% 
  group_by(___) %>% # We want to compute the mean and standard deviation for each what?
  summarize(mean_est = ___, # The mean of the estimate
            se_est = ___)   # The standard deviation of the estimate 
  1. What would happen to the sampling distribution of the slope if, instead of taking samples of size 500, we took samples of size 100? Redo the simulation taking samples of size 100 rather than 500. I have started the code for you below. Fill in the “???”’s and also compute the mean and standard error. Remove the eval=FALSE.
#Sample from the "population"
set.seed(327)
size100_200 <- rep_sample_n(kc_house_data, #data we're sampling from
                            size = ???, #size of each sample
                            reps = ???, #how many samples to take
                            replace = ??? #with or without
                                            #replacement?
                            )

#Fit the model 200 times
model_size100_200_times <-
  size100_200 %>% #start with the 200 samples of size 500
  group_by(???) %>% #for each replicate 
  summarize(lm(??? ~ ???) %>% tidy()) %>% #???
  ungroup() #ungroup the data - good to do this when finished with
            #grouped calculations

model_size100_200_times

#Create the histogram of the slopes
model_size100_200_times %>% #200 models
  filter(term == "???") %>% #filter to the term we're interested in
  ggplot(aes(x = estimate)) + 
  geom_histogram(bins = 20) + #create a histogram of the coefficients
  geom_vline(xintercept = 0.8351142, color = "darkred") +
  labs(title = bquote("Distribution of" ~ hat(beta)[1]))
  1. Now try doing what you did in #3 with samples of size 1000. How does that change the distribution?

Bootstrap Distributions

Remember initially I said to pretend that I couldn’t collect the entire population of data. I could only collect 500 observations. But, if I couldn’t collect the entire sample of data to begin with, why would I be able to collect 200 different samples of size 500? I couldn’t!

This is where bootstrapping comes in to save the day! We can use bootstrapping to estimate the sampling distribution of the slope (we could also do this for the intercept, in a similar way) using only the original sample we took.

Recall, the general bootstrapping algorithm:

  • Collect a random sample. (The original sample, which in this class will always be given to you.)
  • Take a sample, WITH REPLACEMENT, of the original sample that is the same size as the original sample. This is called the resample or the bootstrap sample.
  • Compute the statistic you are interested in on the bootstrap sample. What should that be in this example?
  • Repeat the previous two steps a reasonable number of times, say at least 100 but more if you can. This is the number of samples or replicates.

YOUR TURN!

Assume that we only have the initial sample we took, samp1. and construct the bootstrap distribution of the slope. How does it compare to the actual sampling distribution of the slope above? Find the mean and standard error of the slopes, in addition to plotting the distribution.

I have started the code for you below. Remove the eval=FALSE when finished.

set.seed(1113)
resamples_200 <- rep_sample_n(_____,        # where do we sample from?
                            size = _____,   # what is the size of each sample?
                            reps = _____,   # how many timed do we repeat the whole sampling process?
                            replace = _____) # do we sample with or without replacement?

boot_model_200_times <-
  resamples_200 %>% 
  group_by(_____) %>%     
  summarize(lm(_____ ~ _____) %>% tidy())

boot_model_200_times %>% 
  filter(term == "_____") %>% 
  ggplot(aes(x = _____)) +
  geom_histogram()
LS0tCnRpdGxlOiAiU2FtcGxpbmcgdmFyaWF0aW9uIGluIExpbmVhciBNb2RlbHMiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIGRmX3ByaW50OiBwYWdlZAogICAgY29kZV9kb3dubG9hZDogdHJ1ZQotLS0KCgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeSh0aWR5dmVyc2UpICAgICMgZm9yIHBsb3R0aW5nIGFuZCBzdW1tYXJpemluZwpsaWJyYXJ5KGVxdWF0aW9tYXRpYykgIyBmb3IgbW9kZWwgZXF1YXRpb25zCmxpYnJhcnkoZ2dyaWRnZXMpICAgICAjIGZvciByaWRnZSBwbG90cwpsaWJyYXJ5KGdnbW9zYWljKSAgICAgIyBmb3IgbW9zYWljIHBsb3RzCmxpYnJhcnkobW9kZXJuZGl2ZSkgICAjIGZvciBuaWNlIG1vZGVsIG91dHB1dApsaWJyYXJ5KGJyb29tKSAgICAgICAgIyBmb3IgbmljZSBtb2RlbCBvdXRwdXQgCmxpYnJhcnkoaW5mZXIpICAgICAgICAjIGZvciBtYWtpbmcgaW5mZXJlbmNlcyBhYm91dCBtb2RlbHMKdGhlbWVfc2V0KHRoZW1lX21pbmltYWwoKSkgI2NoYW5nZXMgdGhlIHRoZW1lIG9mIGdncGxvdHMgdG8gdGhlbWVfbWluaW1hbCwgbXkgcGVyc29uYWwgZmF2b3JpdGUKYGBgCgoKPGRpdiBjbGFzcz0iYWxlcnQgYWxlcnQtc3VjY2VzcyI+CiAgPHN0cm9uZz5HT0FMOjwvc3Ryb25nPgoKQnkgdGhlIGVuZCBvZiB0aGVzZSBub3RlcyBhbmQgYWN0aXZpdGllcywgeW91IHNob3VsZCBiZSBhYmxlIHRvIHBlcmZvcm0gdGhlIGZvbGxvd2luZyB0YXNrcy4KCiogVXNlIGEgYm9vdHN0cmFwIGRpc3RyaWJ1dGlvbiB0byB1bmRlcnN0YW5kIGhvdyBtb2RlbCBjb2VmZmljaWVudHMgdmFyeSBmcm9tIHNhbXBsZSB0byBzYW1wbGUuICAKKiBBcHBseSBib290c3RyYXAgY29kZSBJIGhhdmUgd3JpdHRlbiB0byBhIG5ldyBtb2RlbC4gIAoqIENyZWF0ZSBhIGhpc3RvZ3JhbSB0byByZXByZXNlbnQgdGhlIGJvb3RzdHJhcCBkaXN0cmlidXRpb24gZm9yIGEgbW9kZWwgY29lZmZpY2llbnQuICAKCjwvZGl2PgoKClByZXZpb3VzbHkgd2UgdGFsa2VkIGFib3V0IGhvdyBhIHNhbXBsZSBwcm9wb3J0aW9uIHZhcmllcyBmcm9tIHNhbXBsZSB0byBzYW1wbGUgYW5kIHdlcmUgaW50cm9kdWNlZCB0byB0aGUgYm9vdHN0cmFwIHRlY2huaXF1ZS4gTm93LCB3ZSB3b3VsZCBsaWtlIHRvIGFwcGx5IHRoYXQgdG8gbGluZWFyIG1vZGVscyB0byBsZWFybiBhYm91dCBob3cgY29lZmZpY2llbnQgZXN0aW1hdGVzIHZhcnkgZnJvbSBzYW1wbGUgdG8gc2FtcGxlLiAKCgpMZXQncyBvbmNlIGFnYWluIHVzZSB0aGUgYGtjX2hvdXNlX2RhdGFgLCBhIG1vZGlmaWVkIHZlcnNpb24gb2YgdGhlIGBob3VzZV9kYXRhYCBmcm9tIHRoZSBgbW9kZXJuZGl2ZWAgbGlicmFyeS4gV2UgYXJlIGdvaW5nIHRvIHByZXRlbmQgdGhhdCB0aGlzIGlzIHRoZSBlbnRpcmUgKipwb3B1bGF0aW9uKiogb2YgaG91c2VzLgoKYGBge3IsIGVycm9yPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQprY19ob3VzZV9kYXRhIDwtCiAgaG91c2VfcHJpY2VzICU+JSAKICBmaWx0ZXIoYmVkcm9vbXM8PTUsIGJlZHJvb21zPjApICU+JSAKICBtdXRhdGUoZ3JhZGVfQ0FUID0gZmN0X3JlbGV2ZWwoaWZlbHNlKGdyYWRlICVpbiUgIjEiOiI3IiwgIkxvdyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoZ3JhZGUgPT0gIjgiLCAiTWVkaXVtIiwiSGlnaCIpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJMb3ciLCAiTWVkaXVtIiwgIkhpZ2giKSwKICAgICAgICAgYWdlPTIwMTUteXJfYnVpbHQpCgpgYGAKCgojIyBTYW1wbGluZyBEaXN0cmlidXRpb25zCgpXZSB3aWxsIGV4YW1pbmUgYSByZWFsbHkgc2ltcGxlIG1vZGVsIHRoYXQgdXNlcyBgbG9nKHNxZnRfbGl2aW5nKWAgdG8gZXhwbGFpbiBgbG9nKHByaWNlKWAuIFdlIGFyZSBnb2luZyB0byB0aGluayBvZiB0aGlzIGFzIHRoZSAicG9wdWxhdGlvbiBtb2RlbCIuIAoKYGBge3J9CmtjX2hvdXNlX2RhdGEgJT4lIAogIGdncGxvdChhZXMoeD1sb2coc3FmdF9saXZpbmcpLCB5ID0gbG9nKHByaWNlKSkpICsKICBnZW9tX3BvaW50KGFscGhhPS41LCBzaXplID0gLjIpCmBgYAoKCmBgYHtyfQpsbV9zaW1wbGUgPC0gbG0obG9nKHByaWNlKSB+IGxvZyhzcWZ0X2xpdmluZyksCiAgICAgICAgICAgICAgICBkYXRhPWtjX2hvdXNlX2RhdGEpCnRpZHkobG1fc2ltcGxlKQpgYGAKClRoZSAicG9wdWxhdGlvbiBtb2RlbCIgaXM6CgpgYGB7cn0KZXh0cmFjdF9lcShsbV9zaW1wbGUsIAogICAgICAgICAgIGl0YWxfdmFycyA9IFRSVUUsIAogICAgICAgICAgIHdyYXAgPSBUUlVFLCAKICAgICAgICAgICB1c2VfY29lZnMgPSBUUlVFKQpgYGAKCgpMZXQncyBwbG90IHRoYXQgbGluZSBvbiB0aGUgc2NhdHRlcnBsb3Qgb2YgdGhlIGRhdGEuCgpgYGB7cn0KYXVnbWVudChsbV9zaW1wbGUsIGRhdGE9a2NfaG91c2VfZGF0YSkgJT4lIAogIGdncGxvdChhZXMoeD1sb2coc3FmdF9saXZpbmcpLCB5ID0gbG9nKHByaWNlKSkpICsKICBnZW9tX3BvaW50KGFscGhhID0gLjUsIHNpemUgPSAuMikgKwogIGdlb21fbGluZShhZXMoeT0uZml0dGVkKSwgY29sb3I9ImRhcmtyZWQiKQpgYGAKCgoKTm93IGxldCdzIHNheSB0aGF0IEkgd2FzIGdvaW5nIHRvIGNvbGxlY3QgdGhpcyBkYXRhIG15c2VsZiBhbmQgSSBkaWRuJ3QgaGF2ZSB0aW1lIChvciB0aGUgdGVjaG5pY2FsIHNraWxscykgdG8gZ2V0IGFsbCAyMSw2MTMgb2JzZXJ2YXRpb25zLiBTbywgaW5zdGVhZCwgSSBjb2xsZWN0IGEgc2FtcGxlIG9mIDUwMCBvYnNlcnZhdGlvbnMuIAoKSSB3aWxsIG1pbWljIHRoaXMgYnkgc2FtcGxpbmcgZnJvbSB0aGUgZW50aXJlIGRhdGFzZXQgdXNpbmcgdGhlIGBzYW1wbGVfbigpYCBmdW5jdGlvbi4gKlJlbWluZGVyKjogIFRoZSBgc2V0LnNlZWQoKWAgZnVuY3Rpb24gYXQgdGhlIGJlZ2lubmluZyBhbGxvd3MgeW91IHRvIHJlcGxpY2F0ZSB0aGUgcmFuZG9tIHNhbXBsaW5nIHByb2Nlc3Mgc28gdGhhdCB5b3UgZ2V0IHRoZSBzYW1lIHJhbmRvbSBzYW1wbGUgZXZlcnkgdGltZSB0aGUgY29kZSBydW5zIChpbmNsdWRpbmcgd2hlbiB5b3Uga25pdCB0aGUgZG9jdW1lbnQpLiBUaGlzIGFsc28gbWVhbnMgdGhhdCBpZiBzb21lb25lIGVsc2UgdXNlcyB0aGF0IHNhbWUgc2VlZCwgdGhleSB3aWxsIGdldCB0aGUgc2FtZSByZXN1bHQsIGFzIGxvbmcgYXMgdGhleSBzdGFydGVkIHdpdGggdGhlIHNhbWUgZGF0YS4gSXQgaXMgdmVyeSBpbXBvcnRhbnQgdG8gdXNlIGBzZXQuc2VlZCgpYCBhbnl0aW1lIHdlIGNvbmR1Y3QgYSByYW5kb20gcHJvY2VzcyBzbyB0aGF0IHdlIGNhbiByZXBsaWNhdGUgaXQuIEl0IGRvZXMgbm90IG1hdHRlciB3aGF0IG51bWJlciB3ZSBwdXQgaW4gdGhlcmUuCgpUaGVuLCBmaXQgdGhlIG1vZGVsIHRoYXQgdXNlcyBgbG9nKHNxZnRfbGl2aW5nKWAgdG8gZXhwbGFpbiBgbG9nKHByaWNlKWAuICoqSG93IGRvZXMgdGhpcyBtb2RlbCBjb21wYXJlIHRvIHRoZSBtb2RlbCB0aGF0IHVzZWQgYWxsIHRoZSBkYXRhPyoqCgpgYGB7cn0Kc2V0LnNlZWQoMzI3KQoKc2FtcDEgPC0ga2NfaG91c2VfZGF0YSAlPiUgCiAgc2FtcGxlX24oc2l6ZT01MDApIAoKbG1fc2FtcDEgPC0gbG0obG9nKHByaWNlKSB+IGxvZyhzcWZ0X2xpdmluZyksIAogICAgICAgICAgICAgICBkYXRhPXNhbXAxKQoKdGlkeShsbV9zYW1wMSkKYGBgCgpgYGB7cn0KZXh0cmFjdF9lcShsbV9zYW1wMSwgCiAgICAgICAgICAgaXRhbF92YXJzID0gVFJVRSwgCiAgICAgICAgICAgd3JhcCA9IFRSVUUsIAogICAgICAgICAgIHVzZV9jb2VmcyA9IFRSVUUpCmBgYAoKCkxldCdzIHBsb3QgdGhlIGxpbmVzLiBJIGFtIGdvaW5nIHRvIHVzZSB0aGUgYGdlb21fc21vb3RoYCBmdW5jdGlvbiB0byBkbyB0aGlzIGJlY2F1c2UgaXQgd2lsbCBjb21lIGluIGhhbmR5IGxhdGVyLgoKYGBge3IsIG1lc3NhZ2U9RkFMU0V9CmF1Z21lbnQobG1fc2ltcGxlLCBkYXRhPWtjX2hvdXNlX2RhdGEpICU+JSAKICBnZ3Bsb3QoYWVzKHg9bG9nKHNxZnRfbGl2aW5nKSwgeSA9IGxvZyhwcmljZSkpKSArCiAgZ2VvbV9wb2ludChhbHBoYT0uMiwgc2l6ZSA9IC4yKSArCiAgZ2VvbV9wb2ludChkYXRhID0gc2FtcDEsIGNvbG9yPSJsaWdodGJsdWUiLCBhbHBoYSA9IC4yLCBzaXplID0gLjIpICsKICBnZW9tX2xpbmUoYWVzKHk9LmZpdHRlZCksIGNvbG9yPSJkYXJrcmVkIikgKwogIGdlb21fc21vb3RoKGRhdGEgPSBzYW1wMSwgbWV0aG9kID0gImxtIiwgc2UgPSBGQUxTRSwgCiAgICAgICAgICAgICAgY29sb3I9ImxpZ2h0Ymx1ZSIsIHNpemU9LjUpCmBgYAoKTGV0J3MgdGFrZSBhbm90aGVyIHNhbXBsZSBvZiBzaXplIDUwMCBhbmQgZml0IGFub3RoZXIgbGluZS4gVXNlIGEgZGlmZmVyZW50IHNlZWQgZnJvbSB0aGUgZmlyc3Qgc2FtcGxlIHNvIHRoZXkgYXJlIGRpZmZlcmVudCByYW5kb20gc2FtcGxlcy4KCmBgYHtyfQpzZXQuc2VlZCgzMDApCgpzYW1wMiA8LSBrY19ob3VzZV9kYXRhICU+JSAKICBzYW1wbGVfbihzaXplPTUwMCkgCgpsbV9zYW1wMiA8LSBsbShsb2cocHJpY2UpIH4gbG9nKHNxZnRfbGl2aW5nKSwgCiAgICAgICAgICAgICAgIGRhdGE9c2FtcDIpCgp0aWR5KGxtX3NhbXAyKQpgYGAKCmBgYHtyfQpleHRyYWN0X2VxKGxtX3NhbXAyLCAKICAgICAgICAgICBpdGFsX3ZhcnMgPSBUUlVFLCAKICAgICAgICAgICB3cmFwID0gVFJVRSwgCiAgICAgICAgICAgdXNlX2NvZWZzID0gVFJVRSkKYGBgCgpBbmQgcGxvdCB0aGlzIGxpbmUgLi4uCgpgYGB7cn0KYXVnbWVudChsbV9zaW1wbGUsIGRhdGE9a2NfaG91c2VfZGF0YSkgJT4lIAogIGdncGxvdChhZXMoeD1sb2coc3FmdF9saXZpbmcpLCB5ID0gbG9nKHByaWNlKSkpICsKICBnZW9tX3BvaW50KGFscGhhPS4yLCBzaXplID0gLjIpICsKICBnZW9tX2xpbmUoYWVzKHk9LmZpdHRlZCksIGNvbG9yPSJkYXJrcmVkIikgKwogIGdlb21fc21vb3RoKGRhdGEgPSBzYW1wMSwgbWV0aG9kID0gImxtIiwgc2UgPSBGQUxTRSwgCiAgICAgICAgICAgICAgY29sb3I9ImxpZ2h0Ymx1ZSIsIHNpemUgPSAuNSkgKwogIGdlb21fc21vb3RoKGRhdGEgPSBzYW1wMiwgbWV0aG9kID0gImxtIiwgc2UgPSBGQUxTRSwgCiAgICAgICAgICAgICAgY29sb3I9ImxpZ2h0Ymx1ZSIsIHNpemUgPSAuNSkgCmBgYAoKTm93LCBpbiBvcmRlciB0byB1bmRlcnN0YW5kIHNhbXBsaW5nIHZhcmlhYmlsaXR5IHdlIG5lZWQgdG8gYmUgYWJsZSB0byB0aGlzIG1hbnkgdGltZXMuIFJhdGhlciB0aGFuIGhhdmluZyB1cyBydW4gZGlmZmVyZW50IG1vZGVscyBvbmUgYnkgb25lIHRvIGxlYXJuIGFib3V0IHRoZSB2YXJpYWJpbGl0eSBvZiB0aGUgY29lZmZpY2llbnRzLCB3ZSBjYW4gdXNlIHNpbXVsYXRpb24gdG8gZG8gdGhhdCBmb3IgdXMuIAoKTGlrZSBpbiB0aGUgUmVlc2UncyBQaWVjZXMgYWN0aXZpdHksIHdlIHdpbGwgdXNlIHRoZSBgcmVwX3NhbXBsZV9uKClgIGZ1bmN0aW9uIHRvIHRha2UgbXVsdGlwbGUgcmVwZWF0ZWQgc2FtcGxlcy4gQmVsb3cgd2UgdGFrZSAyMDAgc2FtcGxlcyBvZiBzaXplIDUwMCBmcm9tIHRoZSAicG9wdWxhdGlvbiIsIGBrY19ob3VzZV9kYXRhYC4gV2UgYXJlIHRha2luZyBzYW1wbGVzICp3aXRob3V0KiByZXBsYWNlbWVudCB0aGlzIHRpbWUgYmVjYXVzZSB3ZSBhcmUgcHJldGVuZGluZyB0aGF0IHdlIGFyZSBzYW1wbGluZyBmcm9tIHRoZSBwb3B1bGF0aW9uLCBsaWtlIHdoZW4gd2Ugc2FtcGxlZCBSZWVzZSdzIFBpZWNlcyB1c2luZyB0aGUgYXBwbGV0cy4gKipIb3cgbWFueSBvYnNlcnZhdGlvbnMgYXJlIGluIGBzYW1wbGVzXzIwMGA/KiogCgpgYGB7cn0Kc2V0LnNlZWQoMTExMykKc2FtcGxlc18yMDAgPC0gcmVwX3NhbXBsZV9uKGtjX2hvdXNlX2RhdGEsICNkYXRhIHdlJ3JlIHNhbXBsaW5nIGZyb20KICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSA1MDAsICNzaXplIG9mIGVhY2ggc2FtcGxlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXBzID0gMjAwLCAjaG93IG1hbnkgc2FtcGxlcyB0byB0YWtlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXBsYWNlID0gRkFMU0UgI3dpdGggb3Igd2l0aG91dAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICNyZXBsYWNlbWVudD8KICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKYGBgCgoqR09BTCo6IEZvciBlYWNoIHNhbXBsZSwgd2Ugd291bGQgbGlrZSB0byBmaXQgYSBtb2RlbCB0aGF0IHVzZXMgYGxvZyhzcWZ0X2xpdmluZylgIHRvIHByZWRpY3QgYGxvZyhwcmljZSlgLiBXZSB3b3VsZCBsaWtlIHRvIHVuZGVyc3RhbmQgaG93IHRoZSBpbnRlcmNlcHQgYW5kIHNsb3BlIHZhcnkgZnJvbSBzYW1wbGUgdG8gc2FtcGxlLgoKRmlyc3QsIGxldCdzIGRvIHRoaXMgZ3JhcGhpY2FsbHkuICoqV2hhdCBkbyB5b3Ugb2JzZXJ2ZT8qKgoKYGBge3J9CiNUaGlzIGZpcnN0IHNlY3Rpb24gcGxvdHMgdGhlICJwb3B1bGF0aW9uIiBwb2ludHM6CmF1Z21lbnQobG1fc2ltcGxlLCBkYXRhPWtjX2hvdXNlX2RhdGEpICU+JSAKICBnZ3Bsb3QoYWVzKHg9bG9nKHNxZnRfbGl2aW5nKSwgeSA9IGxvZyhwcmljZSkpKSArIAogIGdlb21fcG9pbnQoYWxwaGE9LjIsIGNvbG9yPSJkYXJrZ3JheSIsIHNpemUgPSAuMikgKyAKI1RoaXMgY29kZSBwbG90cyBlYWNoIG9mIHRoZSBsaW5lcyAgCiAgZ2VvbV9zbW9vdGgoZGF0YSA9IHNhbXBsZXNfMjAwLCAjVXNlIHRoZSBzYW1wbGVzXzIwMCBkYXRhCiAgICAgICAgICAgICAgYWVzKGdyb3VwID0gcmVwbGljYXRlKSwgIyJGb3IgZWFjaCByZXBsaWNhdGUiCiAgICAgICAgICAgICAgbWV0aG9kID0gImxtIiwgc2UgPSBGQUxTRSwgI1VzZSBsbSgpIHRvIGZpdCBhIG1vZGVsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI3doZXJlIGxvZyhzcWZ0X2xpdmluZykKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjZXhwbGFpbnMgbG9nKHByaWNlKQogICAgICAgICAgICAgIGNvbG9yPSJsaWdodGJsdWUiLCBzaXplID0gLjUpICsgI2NvbG9yIHRoZSBsaW5lcyBsaWdodGJsdWUKICBnZW9tX2xpbmUoYWVzKHk9LmZpdHRlZCksIGNvbG9yPSJkYXJrcmVkIikgICNhZGQgdGhlICJwb3B1bGF0aW9uIiBsaW5lCmBgYAoKCk5vdywgbGV0J3MgYWN0dWFsbHkgZml0IHRoZSBtb2RlbCAyMDAgdGltZXMuIExvb2sgY2FyZWZ1bGx5IGF0IHRoZSBvdXRwdXQuICoqV2hhdCBpcyB0aGUgY29kZSBkb2luZyBvbiB0aGUgbGluZSB3aXRoICI/Pz8iPyoqCgpgYGB7cn0KbW9kZWxfMjAwX3RpbWVzIDwtCiAgc2FtcGxlc18yMDAgJT4lICNzdGFydCB3aXRoIHRoZSAyMDAgc2FtcGxlcyBvZiBzaXplIDUwMAogIGdyb3VwX2J5KHJlcGxpY2F0ZSkgJT4lICNmb3IgZWFjaCByZXBsaWNhdGUgCiAgc3VtbWFyaXplKGxtKGxvZyhwcmljZSkgfiBsb2coc3FmdF9saXZpbmcpKSAlPiUgdGlkeSgpKSAlPiUgIz8/PwogIHVuZ3JvdXAoKSAjdW5ncm91cCB0aGUgZGF0YSAtIGdvb2QgdG8gZG8gdGhpcyB3aGVuIGZpbmlzaGVkIHdpdGgKICAgICAgICAgICAgI2dyb3VwZWQgY2FsY3VsYXRpb25zCgptb2RlbF8yMDBfdGltZXMKYGBgCgoKV2UgY2FuIG5vdyBsb29rIGF0IHRoZSAqc2FtcGxpbmcgZGlzdHJpYnV0aW9ucyogb2YgdGhlIGVzdGltYXRlZCBzbG9wZSBhbmQgaW50ZXJjZXB0LiBUaGVzZSBzaG93IHVzIGhvdyB3ZSBleHBlY3QgdGhvc2Ugc3RhdGlzdGljcyB0byB2YXJ5IGZyb20gc2FtcGxlIHRvIHNhbXBsZS4gSSBhbHNvIHB1dCB2ZXJ0aWNhbCBsaW5lcyB3aGVyZSB0aGUgYWN0dWFsIGludGVyY2VwdCBhbmQgc2xvcGUgKGZyb20gdGhlICJwb3B1bGF0aW9uIG1vZGVsIikgYXJlIGxvY2F0ZWQuIAoKYGBge3J9CiNzbG9wZQptb2RlbF8yMDBfdGltZXMgJT4lICMyMDAgbW9kZWxzCiAgZmlsdGVyKHRlcm0gPT0gImxvZyhzcWZ0X2xpdmluZykiKSAlPiUgI2ZpbHRlciB0byB0aGUgdGVybSB3ZSdyZSBpbnRlcmVzdGVkIGluCiAgZ2dwbG90KGFlcyh4ID0gZXN0aW1hdGUpKSArIAogIGdlb21faGlzdG9ncmFtKGJpbnMgPSAyMCkgKyAjY3JlYXRlIGEgaGlzdG9ncmFtIG9mIHRoZSBjb2VmZmljaWVudHMKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAwLjgzNTExNDIsIGNvbG9yID0gImRhcmtyZWQiKSArCiAgbGFicyh0aXRsZSA9IGJxdW90ZSgiRGlzdHJpYnV0aW9uIG9mIiB+IGhhdChiZXRhKVsxXSkpCmBgYAoKYGBge3J9CiNpbnRlcmNlcHQKbW9kZWxfMjAwX3RpbWVzICU+JSAKICBmaWx0ZXIodGVybSA9PSAiKEludGVyY2VwdCkiKSAlPiUgIyBMT09LIENBUkVGVUxMWSEhCiAgZ2dwbG90KGFlcyh4ID0gZXN0aW1hdGUpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDIwKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gNi43NDMxMDg0LCBjb2xvciA9ICJkYXJrcmVkIikgKwogIGxhYnModGl0bGUgPSBicXVvdGUoIkRpc3RyaWJ1dGlvbiBvZiIgfiBoYXQoYmV0YSlbMF0pKQpgYGAKCgo8ZGl2IGNsYXNzPSJhbGVydCBhbGVydC1pbmZvIj4KICA8c3Ryb25nPllPVVIgVFVSTiE8L3N0cm9uZz4KICAKMS4gV2hhdCBpcyBhbiBvYnNlcnZhdGlvbiBpbiB0aGUgaGlzdG9ncmFtcyBhYm92ZT8KCjIuIEhvdyB3b3VsZCB5b3UgZGVzY3JpYmUgdGhlc2UgZGlzdHJpYnV0aW9ucyBpbiB0ZXJtcyBvZiBzaGFwZT8KCjMuIENvbXB1dGUgdGhlIG1lYW4gYW5kIHN0YW5kYXJkIGRldmlhdGlvbiBvZiB0aGUgc2xvcGUgYW5kIGludGVyY2VwdCBlc3RpbWF0ZXMuIFRoZSBzdGFuZGFyZCBkZXZpYXRpb24gaGVyZSBoYXMgYSBzcGVjaWFsIG5hbWUsIGEgKnN0YW5kYXJkIGVycm9yKiwgb3IgU0UgZm9yIHNob3J0LiBBIHN0YW5kYXJkIGVycm9yIGlzIHRoZSBzdGFuZGFyZCBkZXZpYXRpb24gb2YgYSBzdGF0aXN0aWMuIEkndmUgc3RhcnRlZCB0aGUgY29kZSBmb3IgeW91IGJlbG93LiBSZW1vdmUgdGhlIGBldmFsPUZBTFNFYC4KCmBgYHtyLCBldmFsPUZBTFNFfQptb2RlbF8yMDBfdGltZXMgJT4lIAogIGdyb3VwX2J5KF9fXykgJT4lICMgV2Ugd2FudCB0byBjb21wdXRlIHRoZSBtZWFuIGFuZCBzdGFuZGFyZCBkZXZpYXRpb24gZm9yIGVhY2ggd2hhdD8KICBzdW1tYXJpemUobWVhbl9lc3QgPSBfX18sICMgVGhlIG1lYW4gb2YgdGhlIGVzdGltYXRlCiAgICAgICAgICAgIHNlX2VzdCA9IF9fXykgICAjIFRoZSBzdGFuZGFyZCBkZXZpYXRpb24gb2YgdGhlIGVzdGltYXRlIApgYGAKCjMuIFdoYXQgd291bGQgaGFwcGVuIHRvIHRoZSBzYW1wbGluZyBkaXN0cmlidXRpb24gb2YgdGhlIHNsb3BlIGlmLCBpbnN0ZWFkIG9mIHRha2luZyBzYW1wbGVzIG9mIHNpemUgNTAwLCB3ZSB0b29rIHNhbXBsZXMgb2Ygc2l6ZSAxMDA/IFJlZG8gdGhlIHNpbXVsYXRpb24gdGFraW5nIHNhbXBsZXMgb2Ygc2l6ZSAxMDAgcmF0aGVyIHRoYW4gNTAwLiBJIGhhdmUgc3RhcnRlZCB0aGUgY29kZSBmb3IgeW91IGJlbG93LiBGaWxsIGluIHRoZSAiPz8/IidzIGFuZCBhbHNvIGNvbXB1dGUgdGhlIG1lYW4gYW5kIHN0YW5kYXJkIGVycm9yLiBSZW1vdmUgdGhlIGBldmFsPUZBTFNFYC4KCmBgYHtyLCBldmFsPUZBTFNFfQojU2FtcGxlIGZyb20gdGhlICJwb3B1bGF0aW9uIgpzZXQuc2VlZCgzMjcpCnNpemUxMDBfMjAwIDwtIHJlcF9zYW1wbGVfbihrY19ob3VzZV9kYXRhLCAjZGF0YSB3ZSdyZSBzYW1wbGluZyBmcm9tCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gPz8/LCAjc2l6ZSBvZiBlYWNoIHNhbXBsZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVwcyA9ID8/PywgI2hvdyBtYW55IHNhbXBsZXMgdG8gdGFrZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVwbGFjZSA9ID8/PyAjd2l0aCBvciB3aXRob3V0CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI3JlcGxhY2VtZW50PwogICAgICAgICAgICAgICAgICAgICAgICAgICAgKQoKI0ZpdCB0aGUgbW9kZWwgMjAwIHRpbWVzCm1vZGVsX3NpemUxMDBfMjAwX3RpbWVzIDwtCiAgc2l6ZTEwMF8yMDAgJT4lICNzdGFydCB3aXRoIHRoZSAyMDAgc2FtcGxlcyBvZiBzaXplIDUwMAogIGdyb3VwX2J5KD8/PykgJT4lICNmb3IgZWFjaCByZXBsaWNhdGUgCiAgc3VtbWFyaXplKGxtKD8/PyB+ID8/PykgJT4lIHRpZHkoKSkgJT4lICM/Pz8KICB1bmdyb3VwKCkgI3VuZ3JvdXAgdGhlIGRhdGEgLSBnb29kIHRvIGRvIHRoaXMgd2hlbiBmaW5pc2hlZCB3aXRoCiAgICAgICAgICAgICNncm91cGVkIGNhbGN1bGF0aW9ucwoKbW9kZWxfc2l6ZTEwMF8yMDBfdGltZXMKCiNDcmVhdGUgdGhlIGhpc3RvZ3JhbSBvZiB0aGUgc2xvcGVzCm1vZGVsX3NpemUxMDBfMjAwX3RpbWVzICU+JSAjMjAwIG1vZGVscwogIGZpbHRlcih0ZXJtID09ICI/Pz8iKSAlPiUgI2ZpbHRlciB0byB0aGUgdGVybSB3ZSdyZSBpbnRlcmVzdGVkIGluCiAgZ2dwbG90KGFlcyh4ID0gZXN0aW1hdGUpKSArIAogIGdlb21faGlzdG9ncmFtKGJpbnMgPSAyMCkgKyAjY3JlYXRlIGEgaGlzdG9ncmFtIG9mIHRoZSBjb2VmZmljaWVudHMKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAwLjgzNTExNDIsIGNvbG9yID0gImRhcmtyZWQiKSArCiAgbGFicyh0aXRsZSA9IGJxdW90ZSgiRGlzdHJpYnV0aW9uIG9mIiB+IGhhdChiZXRhKVsxXSkpCmBgYAoKNC4gTm93IHRyeSBkb2luZyB3aGF0IHlvdSBkaWQgaW4gIzMgd2l0aCBzYW1wbGVzIG9mIHNpemUgMTAwMC4gSG93IGRvZXMgdGhhdCBjaGFuZ2UgdGhlIGRpc3RyaWJ1dGlvbj8KCjwvZGl2PgoKCiMjIEJvb3RzdHJhcCBEaXN0cmlidXRpb25zCgpSZW1lbWJlciBpbml0aWFsbHkgSSBzYWlkIHRvIHByZXRlbmQgdGhhdCBJIGNvdWxkbid0IGNvbGxlY3QgdGhlIGVudGlyZSBwb3B1bGF0aW9uIG9mIGRhdGEuIEkgY291bGQgb25seSBjb2xsZWN0IDUwMCBvYnNlcnZhdGlvbnMuIEJ1dCwgaWYgSSBjb3VsZG4ndCBjb2xsZWN0IHRoZSBlbnRpcmUgc2FtcGxlIG9mIGRhdGEgdG8gYmVnaW4gd2l0aCwgd2h5IHdvdWxkIEkgYmUgYWJsZSB0byBjb2xsZWN0IDIwMCBkaWZmZXJlbnQgc2FtcGxlcyBvZiBzaXplIDUwMD8gSSBjb3VsZG4ndCEgCgpUaGlzIGlzIHdoZXJlICoqYm9vdHN0cmFwcGluZyoqIGNvbWVzIGluIHRvIHNhdmUgdGhlIGRheSEgV2UgY2FuIHVzZSBib290c3RyYXBwaW5nIHRvIGVzdGltYXRlIHRoZSBzYW1wbGluZyBkaXN0cmlidXRpb24gb2YgdGhlIHNsb3BlICh3ZSBjb3VsZCBhbHNvIGRvIHRoaXMgZm9yIHRoZSBpbnRlcmNlcHQsIGluIGEgc2ltaWxhciB3YXkpIHVzaW5nIG9ubHkgdGhlIG9yaWdpbmFsIHNhbXBsZSB3ZSB0b29rLgoKUmVjYWxsLCB0aGUgZ2VuZXJhbCBib290c3RyYXBwaW5nIGFsZ29yaXRobToKCiogQ29sbGVjdCBhIHJhbmRvbSBzYW1wbGUuIChUaGUgb3JpZ2luYWwgc2FtcGxlLCB3aGljaCBpbiB0aGlzIGNsYXNzIHdpbGwgYWx3YXlzIGJlIGdpdmVuIHRvIHlvdS4pIAoqIFRha2UgYSBzYW1wbGUsIFdJVEggUkVQTEFDRU1FTlQsIG9mIHRoZSBvcmlnaW5hbCBzYW1wbGUgdGhhdCBpcyB0aGUgc2FtZSBzaXplIGFzIHRoZSBvcmlnaW5hbCBzYW1wbGUuIFRoaXMgaXMgY2FsbGVkIHRoZSByZXNhbXBsZSBvciB0aGUgYm9vdHN0cmFwIHNhbXBsZS4gIAoqIENvbXB1dGUgdGhlIHN0YXRpc3RpYyB5b3UgYXJlIGludGVyZXN0ZWQgaW4gb24gdGhlIGJvb3RzdHJhcCBzYW1wbGUuICoqV2hhdCBzaG91bGQgdGhhdCBiZSBpbiB0aGlzIGV4YW1wbGU/KiogICAgCiogUmVwZWF0IHRoZSBwcmV2aW91cyB0d28gc3RlcHMgYSByZWFzb25hYmxlIG51bWJlciBvZiB0aW1lcywgc2F5IGF0IGxlYXN0IDEwMCBidXQgbW9yZSBpZiB5b3UgY2FuLiBUaGlzIGlzIHRoZSBudW1iZXIgb2Ygc2FtcGxlcyBvciByZXBsaWNhdGVzLgoKCjxkaXYgY2xhc3M9ImFsZXJ0IGFsZXJ0LWluZm8iPgogIDxzdHJvbmc+WU9VUiBUVVJOITwvc3Ryb25nPgoKQXNzdW1lIHRoYXQgd2Ugb25seSBoYXZlIHRoZSBpbml0aWFsIHNhbXBsZSB3ZSB0b29rLCAqc2FtcDEqLiBhbmQgY29uc3RydWN0IHRoZSBib290c3RyYXAgZGlzdHJpYnV0aW9uIG9mIHRoZSBzbG9wZS4gSG93IGRvZXMgaXQgY29tcGFyZSB0byB0aGUgYWN0dWFsIHNhbXBsaW5nIGRpc3RyaWJ1dGlvbiBvZiB0aGUgc2xvcGUgYWJvdmU/IEZpbmQgdGhlIG1lYW4gYW5kIHN0YW5kYXJkIGVycm9yIG9mIHRoZSBzbG9wZXMsIGluIGFkZGl0aW9uIHRvIHBsb3R0aW5nIHRoZSBkaXN0cmlidXRpb24uCgpJIGhhdmUgc3RhcnRlZCB0aGUgY29kZSBmb3IgeW91IGJlbG93LiBSZW1vdmUgdGhlIGBldmFsPUZBTFNFYCB3aGVuIGZpbmlzaGVkLgoKYGBge3IsIGV2YWw9RkFMU0V9CnNldC5zZWVkKDExMTMpCnJlc2FtcGxlc18yMDAgPC0gcmVwX3NhbXBsZV9uKF9fX19fLCAgICAgICAgIyB3aGVyZSBkbyB3ZSBzYW1wbGUgZnJvbT8KICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSBfX19fXywgICAjIHdoYXQgaXMgdGhlIHNpemUgb2YgZWFjaCBzYW1wbGU/CiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXBzID0gX19fX18sICAgIyBob3cgbWFueSB0aW1lZCBkbyB3ZSByZXBlYXQgdGhlIHdob2xlIHNhbXBsaW5nIHByb2Nlc3M/CiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXBsYWNlID0gX19fX18pICMgZG8gd2Ugc2FtcGxlIHdpdGggb3Igd2l0aG91dCByZXBsYWNlbWVudD8KCmJvb3RfbW9kZWxfMjAwX3RpbWVzIDwtCiAgcmVzYW1wbGVzXzIwMCAlPiUgCiAgZ3JvdXBfYnkoX19fX18pICU+JSAgICAgCiAgc3VtbWFyaXplKGxtKF9fX19fIH4gX19fX18pICU+JSB0aWR5KCkpCgpib290X21vZGVsXzIwMF90aW1lcyAlPiUgCiAgZmlsdGVyKHRlcm0gPT0gIl9fX19fIikgJT4lIAogIGdncGxvdChhZXMoeCA9IF9fX19fKSkgKwogIGdlb21faGlzdG9ncmFtKCkKYGBgCgo8L2Rpdj4KCgoKCgoKCgoKCgoKCgoKCg==