Simple random sampling

The simplest method of sampling a population, is known as simple random sampling (sometimes abbreviated to “SRS”), and involves picking rows at random, one at a time, where each row has the same chance of being picked as any other.

To make it easier to see which rows end up in the sample, it’s helpful to include a row ID column in the dataset before you take the sample.

We’ll look at sampling methods using a synthetic (fictional) employee attrition dataset from IBM, where “attrition” means leaving the company.

# Load libraries
library(tidyr)
library(dplyr)
library(ggplot2)
library(fst)
library(tibble)

Load dataset attrition_pop

path_attr <- ruta_fst <- "C:/Users/JuanFer Mosquera/Documents/datasets/attrition.fst"
attrition_pop <- read_fst(path_attr)
colnames(attrition_pop)
 [1] "Age"                      "Attrition"                "BusinessTravel"           "DailyRate"                "Department"              
 [6] "DistanceFromHome"         "Education"                "EducationField"           "EnvironmentSatisfaction"  "Gender"                  
[11] "HourlyRate"               "JobInvolvement"           "JobLevel"                 "JobRole"                  "JobSatisfaction"         
[16] "MaritalStatus"            "MonthlyIncome"            "MonthlyRate"              "NumCompaniesWorked"       "OverTime"                
[21] "PercentSalaryHike"        "PerformanceRating"        "RelationshipSatisfaction" "StockOptionLevel"         "TotalWorkingYears"       
[26] "TrainingTimesLastYear"    "WorkLifeBalance"          "YearsAtCompany"           "YearsInCurrentRole"       "YearsSinceLastPromotion" 
[31] "YearsWithCurrManager"    
attrition_pop
set.seed(590497)
attrition_samp <- attrition_pop %>%
  # Add a row id column
  rowid_to_column() %>%
  # Get 200 rows using simple random sampling
  slice_sample(n = 200)

# View the attrition_samp dataset
attrition_samp

Systematic sample

One sampling method that avoids randomness is called systematic sampling. Here, you pick rows from the population at regular intervals.

For example, if the population dataset had \(1000\) rows and you wanted a sample size of five, you’d pick rows 200, 400, 600, 800, and 1000.

# Set the sample size
sample_size_atr <- 200

# Get the population size from attrition_pop
pop_size_atr <- nrow(attrition_pop)

# Calculate the interval
interval_atr <- pop_size_atr %/% sample_size_atr
# Get row indexes for the sample 
row_indexes_atr <- seq_len(sample_size_atr) * interval_atr

attrition_sys_samp <- attrition_pop %>%
  # Add a row id column
  rowid_to_column() %>%
  # Get 200 rows using systematic sampling
  slice(row_indexes_atr)

# View the results
attrition_sys_samp
NA

Is systematic sampling ok?

Systematic sampling has a problem: if the data has been sorted, or there is some sort of pattern or meaning behind the row order, then the resulting sample may not be representative of the whole population. The problem can be solved by shuffling the rows, but then systematic sampling is equivalent to simple random sampling.

Here you’ll look at how to determine whether or not there is a problem.

# Add a row ID column to attrition_pop
attrition_pop_id <- attrition_pop %>%
  rowid_to_column()

# Using attrition_pop_id, plot YearsAtCompany vs. rowid
ggplot(attrition_pop_id, aes(x = rowid, y = YearsAtCompany)) +
  # Make it a scatter plot
  geom_point() + 
  # Add a smooth trend line
  geom_smooth()

# Shuffle the rows of attrition_pop then add row IDs
attrition_shuffled <- attrition_pop %>%
  slice_sample(prop = 1) %>%
  rowid_to_column()

# Using attrition_shuffled, plot YearsAtCompany vs. rowid
attrition_shuffled %>%
  ggplot(aes(x = rowid, y = YearsAtCompany)) +
  geom_point() + 
  geom_smooth()

A systematic sample does not always produce a sample similar to a simple random sample.

Proportional stratified sampling

If you are interested in subgroups within the population, then you may need to carefully control the counts of each subgroup within the population. Proportional stratified sampling results in subgroup sizes within the sample that are representative of the subgroup sizes within the population. It is equivalent to performing a simple random sample on each subgroup.

education_counts_pop <- attrition_pop %>%
  # Count the employees by education level
  count(Education, sort = TRUE) %>%
  # Add a percent column
  mutate(percent = 100 * n / sum(n))

education_counts_pop
# Use proportional stratified sampling to get 40% of each Education group 
attrition_strat <- attrition_pop %>%
  group_by(Education) %>%
  slice_sample(prop = 0.4)

attrition_strat
# Get the counts and percents from attrition strat
education_counts_strat <- attrition_strat %>%
  count(Education, sort = TRUE) %>%
  mutate(percent = 100 * n / sum(n))

education_counts_strat

Equal counts stratified sampling

f one subgroup is larger than another subgroup in the population, but you don’t want to reflect that difference in your analysis, then you can use equal counts stratified sampling to generate samples where each subgroup has the same amount of data. For example, if you are analyzing blood types, O is the most common blood type worldwide, but you may wish to have equal amounts of O, A, B, and AB in your sample.

# Use equal counts stratified sampling to get 30 employees from each education group
attrition_eq <- attrition_pop %>%
  group_by(Education) %>%
  slice_sample(n = 30) %>%
  ungroup() 

# See the results
attrition_eq
# Get the counts and percent of attrition_eq
education_counts_eq <- attrition_eq %>%
  count(Education) %>%
  mutate(percent = 100 * n / sum(n))

# See the results 
education_counts_eq

Weighted sampling

Stratified sampling provides rules about the probability of picking rows from your dataset at the subgroup level. A generalization of this is weighted sampling, which lets you specify rules about the probability of picking rows at the row level. The probability of picking any given row is proportional to the weight value for that row.

# Using attrition_pop plot YearsAtCompany as a histogram with binwidth = 1
ggplot(attrition_pop, aes(YearsAtCompany)) + 
  geom_histogram(binwidth = 1)

# Sample 400 employees weighted by YearsAtCompany
attrition_weight <- attrition_pop %>%
  slice_sample(n = 400, weight_by = YearsAtCompany)

# See the result 
attrition_weight
# Using attrition weight, plot YearsAtCompany as a histogram with binwidth = 1
ggplot(attrition_weight, aes(YearsAtCompany)) +
  geom_histogram(binwidth = 1)

Performing cluster sampling

Now that you know when to use cluster sampling, it’s time to put it into action. In this exercise you’ll explore the JobRole column of the attrition dataset. You can think of each job role as a subgroup of the whole population of employees.

# Get unique JobRole values
job_roles_pop <- unique(attrition_pop$JobRole)

# Randomly sample 4 JobRole values
job_roles_samp <- sample(job_roles_pop, size = 4)

# see the result
job_roles_samp
[1] Laboratory_Technician Research_Scientist    Sales_Executive       Manager              
9 Levels: Healthcare_Representative Human_Resources Laboratory_Technician Manager Manufacturing_Director Research_Director ... Sales_Representative
# Filter for rows where JobRole is in job_roles_samp
attrition_filtered <- attrition_pop %>%
  filter(JobRole %in% job_roles_samp) %>%
  group_by(JobRole)

# Randomly sample 10 employees from each sampled job role
 attrition_clus <-  attrition_filtered %>%
   slice_sample(n = 10)
  
 attrition_clus

3 kinds of samping

Let’s compare the performance of point estimates using simple, stratified, and cluster sampling. Before we do that, you’ll have to set up the samples. We’ll use the RelationshipSatisfaction column of the attrition dataset, which categorizes the employee’s relationship with the company. It’s an ordered factor with four levels: Low, Medium, High, and Very_High.

attrition_srs <- attrition_pop %>%
  slice_sample(prop = 0.25)

# Perform stratified sampling to get 0.25 of each relationship group
attrition_strt <- attrition_pop %>%
  group_by(RelationshipSatisfaction) %>%
  slice_sample(prop = 0.25)

# Get unique values of RelationshipSatisfaction 
satisfaction_unique <- unique(attrition_pop$RelationshipSatisfaction)

# Ramdomly sample for 2 of the unique satisfaction values 
satisfaction_samp <- sample(satisfaction_unique)

# Perform cluster sampling on the selected group getting 0.25 of the population
attrition_clust <- attrition_pop %>%
  filter(RelationshipSatisfaction %in% satisfaction_samp) %>%
  group_by(RelationshipSatisfaction) %>%
  slice_sample(n = round(nrow(attrition_pop) / 4)) %>%
  ungroup()

Summary statistics on different kinds of sample

Now you have three types of sample (simple, stratified, cluster), you can compare point estimates from each sample to the population parameter. That is, you can calculate the same summary statistic on each sample and see how it compares to the summary statistic for the population.

Here, we’ll look at how satisfaction with the company affects whether or not the employee leaves the company. That is, you’ll calculate the proportion of employees who left the company (they have an Attrition value of "Yes"), for each value of RelationshipSatisfaction.

Whole population dataset

# Use the whole population dataset
mean_attrition_pop <- attrition_pop %>%
  # Group by relationship satisfaction level
  group_by(RelationshipSatisfaction) %>%
  # Calculate the proportion of employee attrition
  summarise(mean_attrition = mean(Attrition == "Yes"))

# See the result
mean_attrition_pop

attrition_srs sataset

mean_attrition_srs <- attrition_srs %>%
  group_by(RelationshipSatisfaction) %>%
  summarise(mean_attrition = mean(Attrition == "Yes"))

#See the result
mean_attrition_srs

attrition_strat dataset

mean_attrition_strt <- attrition_strt %>%
  group_by(RelationshipSatisfaction) %>%
  summarise(mean_attrition = mean(Attrition == "Yes"))

# See the result
mean_attrition_strt

attrition_clust dataset

mean_attrition_clust <- attrition_clust %>%
  group_by(RelationshipSatisfaction) %>%
  summarise(mean_attrition = mean(Attrition == "Yes"))

# See the results
mean_attrition_clust

Calculate relative errors

The size of the sample you take affects how accurately the point estimates reflect the corresponding population parameter. For example, when you calculate a sample mean, you want it to be close to the population mean. However, if your sample is too small, this might not be the case.

The most common metric for assessing accuracy is relative error. This is the absolute difference between the population parameter and the point estimate, all divided by the population parameter. It is sometimes expressed as a percentage.

# Generate a simple random sample of 10 rows 
attrition_srs10 <- attrition_pop %>%
  slice_sample(n = 10)

# Calculate the proportion of employee attrition in the sample
  mean_attrition_srs10 <- attrition_srs10 %>%
    summarise(mean_attrition = mean(Attrition == "Yes")) %>%
    pull(mean_attrition)
  
# Calculate the relative error percentage
rel_error_pct10 <- 100 * abs(mean_attrition_pop - mean_attrition_srs10) / mean_attrition_pop
Warning: '-' is not meaningful for ordered factorsWarning: '/' is not meaningful for ordered factors
# See the results
rel_error_pct10

Calculate the relative error percentage again. This time, use a simple random sample of one hundred rows of attrition_pop.

attrition_srs100 <- attrition_pop %>%
  slice_sample(n = 100)

  mean_attrition_srs100 <- attrition_srs100 %>%
    summarise(mean_attrition = mean(Attrition == "Yes")) %>%
    pull(mean_attrition)
  
rel_error_pct100 <- 100 * abs(mean_attrition_pop - mean_attrition_srs10) / mean_attrition_pop
Warning: '-' is not meaningful for ordered factorsWarning: '/' is not meaningful for ordered factors
rel_error_pct100

Replicating samples

When you calculate a point estimate such as a sample mean, the value you calculate depends on the rows that were included in the sample. That means that there is some randomness in the answer. In order to quantify the variation caused by this randomness, you can create many samples and calculate the sample mean (or other statistic) for each sample.

# Replicate this code 500 times
mean_attritions <- replicate(n = 500, 
  expr = attrition_pop %>%
    slice_sample(n = 20) %>%
    summarise(mean_attrition = mean(Attrition == "Yes")) %>%
    pull(mean_attrition)
)

# see the result
head(mean_attritions)
[1] 0.25 0.35 0.05 0.20 0.20 0.00
# Store mean attritions in a tibble in a column named sample_mean
sample_means_att <- tibble(sample_mean = mean_attritions)

# Plot a histogram of the "sample_mean" column, binwidth 0.05
ggplot(sample_means_att, aes(sample_mean)) + 
  geom_histogram(binwidth = 0.05)

Population and sampling distributions means

One of the useful features of sampling distributions is that we can quantify them. In particular, we can calculate summary statistics on them. Here, we’ll look at the relationship between the mean of the sampling distribution and the population parameter that the sampling is supposed to estimate.

Three sampling distributions are provided. In each case, the employee attrition dataset was sampled using simple random sampling, then the mean attrition was calculated. This was done 1000 times to get a sampling distribution of mean attritions. One sampling distribution used a sample size of 5 for each replicate, one used 50, and one used 500.

Let’s create the datasets of sample distributions:

num_replc <- 1000

# Replicate this code 1000 times
samp_distribution_5 <- replicate(n = num_replc, 
  expr = attrition_pop %>%
    slice_sample(n = 5) %>%
    summarise(mean_attrition = mean(Attrition == "Yes")) %>%
    pull(mean_attrition), simplify = FALSE
)

# Store mean attritions in a tibble in a column named sample_mean
samp_distribution_5_tibl <- tibble(replicate = 1:num_replc, mean_attrition = unlist(samp_distribution_5))
samp_distribution_5_tibl

# For 50 samples
samp_distribution_50 <- replicate(n = num_replc, 
  expr = attrition_pop %>%
    slice_sample(n = 50) %>%
    summarise(mean_attrition = mean(Attrition == "Yes")) %>%
    pull(mean_attrition), simplify = FALSE
)
samp_distribution_50_tibl <- tibble(replicate = 1:num_replc, mean_attrition = unlist(samp_distribution_50))
samp_distribution_50_tibl

# For 500 samples
samp_distribution_500 <- replicate(n = num_replc, 
  expr = attrition_pop %>%
    slice_sample(n = 50) %>%
    summarise(mean_attrition = mean(Attrition == "Yes")) %>%
    pull(mean_attrition), simplify = FALSE
)
samp_distribution_500_tibl <- tibble(replicate = 1:num_replc, mean_attrition = unlist(samp_distribution_500))
samp_distribution_500_tibl
# Calculate the mean across replicates of the mean attritions in samp_distribution_5_tibl, samp_distribution_50_tibl, samp_distribution_500_tibl
mean_of_means5 <- samp_distribution_5_tibl %>%
  summarise(mean_mean_attrition = mean(mean_attrition))

mean_of_means50 <- samp_distribution_50_tibl %>%
  summarise(mean_mean_attrition = mean(mean_attrition))

mean_of_means500 <- samp_distribution_500_tibl %>%
  summarise(mean_mean_attrition = mean(mean_attrition))

mean_of_means5
mean_of_means50
mean_of_means500

Population and sampling distribution variation

You just calculated the mean of the sampling distribution and saw how it is an estimate of the corresponding population parameter. Similarly, as a result of the central limit theorem, the standard deviation of the sampling distribution has an interesting relationship with the population parameter’s standard deviation and the sample size.

sd_of_means5 <- samp_distribution_5_tibl %>%
  summarise(sd_mean_attrition = sd(mean_attrition))

sd_of_means50 <- samp_distribution_50_tibl %>%
  summarise(sd_mean_attrition = sd(mean_attrition))

sd_of_means500 <- samp_distribution_500_tibl %>%
  summarise(sd_mean_attrition = sd(mean_attrition))

sd_of_means5
sd_of_means50
sd_of_means500
coffee_focus <- coffee_ratings %>%
  select(variety, country_of_origin, flavor) %>%
  rowid_to_column()
Error in select(., variety, country_of_origin, flavor) : 
  object 'coffee_ratings' not found

Resample with slice_sample()

To sample with replacement, you call slice_sample() as usual, but set the replace argument to TRUE. Setting prop to 1 gives a sample with the same size as the original dataset.

Repeated coffees

Counting the rowid shows how many times each coffee ended up in the resampled dataset. Some coffees are present five times in the new dataset.

Missing coffees

That means that some coffees didn’t end up in the resampled dataset. By taking the number of distinct row IDs in the resampled dataset using dplyr’s n_distinct, you can see that 834 different coffees were included, and 505 coffees weren’t included.

LS0tDQp0aXRsZTogIlNhbXBsaW5nIGluIFIiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCmF1dGhvcjogSnVhbiBGZXJuYW5kbyBNb3NxdWVyYSBBcmF1am8NCmRhdGU6IDIwMjQtMDMtMDYNCi0tLQ0KDQojIyMgKipTaW1wbGUgcmFuZG9tIHNhbXBsaW5nKioNCg0KVGhlIHNpbXBsZXN0IG1ldGhvZCBvZiBzYW1wbGluZyBhIHBvcHVsYXRpb24sIGlzIGtub3duIGFzwqAqc2ltcGxlIHJhbmRvbSBzYW1wbGluZyrCoChzb21ldGltZXMgYWJicmV2aWF0ZWQgdG8gIlNSUyIpLCBhbmQgaW52b2x2ZXMgcGlja2luZyByb3dzIGF0IHJhbmRvbSwgb25lIGF0IGEgdGltZSwgd2hlcmUgZWFjaCByb3cgaGFzIHRoZSBzYW1lIGNoYW5jZSBvZiBiZWluZyBwaWNrZWQgYXMgYW55IG90aGVyLg0KDQpUbyBtYWtlIGl0IGVhc2llciB0byBzZWUgd2hpY2ggcm93cyBlbmQgdXAgaW4gdGhlIHNhbXBsZSwgaXQncyBoZWxwZnVsIHRvIGluY2x1ZGUgYSByb3cgSUQgY29sdW1uIGluIHRoZSBkYXRhc2V0IGJlZm9yZSB5b3UgdGFrZSB0aGUgc2FtcGxlLg0KDQpXZSdsbCBsb29rIGF0IHNhbXBsaW5nIG1ldGhvZHMgdXNpbmcgYSBzeW50aGV0aWMgKGZpY3Rpb25hbCkgZW1wbG95ZWUgYXR0cml0aW9uIGRhdGFzZXQgZnJvbSAqKklCTSwqKiB3aGVyZSAiYXR0cml0aW9uIiBtZWFucyBsZWF2aW5nIHRoZSBjb21wYW55Lg0KDQpgYGB7cn0NCiMgTG9hZCBsaWJyYXJpZXMNCmxpYnJhcnkodGlkeXIpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShmc3QpDQpsaWJyYXJ5KHRpYmJsZSkNCmBgYA0KDQojIyMgKipMb2FkIGRhdGFzZXQgYXR0cml0aW9uX3BvcCoqDQoNCmBgYHtyfQ0KcGF0aF9hdHRyIDwtIHJ1dGFfZnN0IDwtICJDOi9Vc2Vycy9KdWFuRmVyIE1vc3F1ZXJhL0RvY3VtZW50cy9kYXRhc2V0cy9hdHRyaXRpb24uZnN0Ig0KYXR0cml0aW9uX3BvcCA8LSByZWFkX2ZzdChwYXRoX2F0dHIpDQpjb2xuYW1lcyhhdHRyaXRpb25fcG9wKQ0KYXR0cml0aW9uX3BvcA0KYGBgDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoNTkwNDk3KQ0KYXR0cml0aW9uX3NhbXAgPC0gYXR0cml0aW9uX3BvcCAlPiUNCiAgIyBBZGQgYSByb3cgaWQgY29sdW1uDQogIHJvd2lkX3RvX2NvbHVtbigpICU+JQ0KICAjIEdldCAyMDAgcm93cyB1c2luZyBzaW1wbGUgcmFuZG9tIHNhbXBsaW5nDQogIHNsaWNlX3NhbXBsZShuID0gMjAwKQ0KDQojIFZpZXcgdGhlIGF0dHJpdGlvbl9zYW1wIGRhdGFzZXQNCmF0dHJpdGlvbl9zYW1wDQpgYGANCg0KIyMjICoqU3lzdGVtYXRpYyBzYW1wbGUqKg0KDQpPbmUgc2FtcGxpbmcgbWV0aG9kIHRoYXQgYXZvaWRzIHJhbmRvbW5lc3MgaXMgY2FsbGVkICpzeXN0ZW1hdGljIHNhbXBsaW5nKi4gSGVyZSwgeW91IHBpY2sgcm93cyBmcm9tIHRoZSBwb3B1bGF0aW9uIGF0IHJlZ3VsYXIgaW50ZXJ2YWxzLg0KDQpGb3IgZXhhbXBsZSwgaWYgdGhlIHBvcHVsYXRpb24gZGF0YXNldCBoYWQgJDEwMDAkIHJvd3MgYW5kIHlvdSB3YW50ZWQgYSBzYW1wbGUgc2l6ZSBvZiBmaXZlLCB5b3UnZCBwaWNrIHJvd3MgYDIwMGAsIGA0MDBgLCBgNjAwYCwgYDgwMGAsIGFuZCBgMTAwMGAuDQoNCmBgYHtyfQ0KIyBTZXQgdGhlIHNhbXBsZSBzaXplDQpzYW1wbGVfc2l6ZV9hdHIgPC0gMjAwDQoNCiMgR2V0IHRoZSBwb3B1bGF0aW9uIHNpemUgZnJvbSBhdHRyaXRpb25fcG9wDQpwb3Bfc2l6ZV9hdHIgPC0gbnJvdyhhdHRyaXRpb25fcG9wKQ0KDQojIENhbGN1bGF0ZSB0aGUgaW50ZXJ2YWwNCmludGVydmFsX2F0ciA8LSBwb3Bfc2l6ZV9hdHIgJS8lIHNhbXBsZV9zaXplX2F0cg0KYGBgDQoNCmBgYHtyfQ0KIyBHZXQgcm93IGluZGV4ZXMgZm9yIHRoZSBzYW1wbGUgDQpyb3dfaW5kZXhlc19hdHIgPC0gc2VxX2xlbihzYW1wbGVfc2l6ZV9hdHIpICogaW50ZXJ2YWxfYXRyDQoNCmF0dHJpdGlvbl9zeXNfc2FtcCA8LSBhdHRyaXRpb25fcG9wICU+JQ0KICAjIEFkZCBhIHJvdyBpZCBjb2x1bW4NCiAgcm93aWRfdG9fY29sdW1uKCkgJT4lDQogICMgR2V0IDIwMCByb3dzIHVzaW5nIHN5c3RlbWF0aWMgc2FtcGxpbmcNCiAgc2xpY2Uocm93X2luZGV4ZXNfYXRyKQ0KDQojIFZpZXcgdGhlIHJlc3VsdHMNCmF0dHJpdGlvbl9zeXNfc2FtcA0KDQpgYGANCg0KIyMjICoqSXMgc3lzdGVtYXRpYyBzYW1wbGluZyBvaz8qKg0KDQpTeXN0ZW1hdGljIHNhbXBsaW5nIGhhcyBhIHByb2JsZW06IGlmIHRoZSBkYXRhIGhhcyBiZWVuIHNvcnRlZCwgb3IgdGhlcmUgaXMgc29tZSBzb3J0IG9mIHBhdHRlcm4gb3IgbWVhbmluZyBiZWhpbmQgdGhlIHJvdyBvcmRlciwgdGhlbiB0aGUgcmVzdWx0aW5nIHNhbXBsZSBtYXkgbm90IGJlIHJlcHJlc2VudGF0aXZlIG9mIHRoZSB3aG9sZSBwb3B1bGF0aW9uLiBUaGUgcHJvYmxlbSBjYW4gYmUgc29sdmVkIGJ5IHNodWZmbGluZyB0aGUgcm93cywgYnV0IHRoZW4gc3lzdGVtYXRpYyBzYW1wbGluZyBpcyBlcXVpdmFsZW50IHRvIHNpbXBsZSByYW5kb20gc2FtcGxpbmcuDQoNCkhlcmUgeW91J2xsIGxvb2sgYXQgaG93IHRvIGRldGVybWluZSB3aGV0aGVyIG9yIG5vdCB0aGVyZSBpcyBhIHByb2JsZW0uDQoNCmBgYHtyfQ0KIyBBZGQgYSByb3cgSUQgY29sdW1uIHRvIGF0dHJpdGlvbl9wb3ANCmF0dHJpdGlvbl9wb3BfaWQgPC0gYXR0cml0aW9uX3BvcCAlPiUNCiAgcm93aWRfdG9fY29sdW1uKCkNCg0KIyBVc2luZyBhdHRyaXRpb25fcG9wX2lkLCBwbG90IFllYXJzQXRDb21wYW55IHZzLiByb3dpZA0KZ2dwbG90KGF0dHJpdGlvbl9wb3BfaWQsIGFlcyh4ID0gcm93aWQsIHkgPSBZZWFyc0F0Q29tcGFueSkpICsNCiAgIyBNYWtlIGl0IGEgc2NhdHRlciBwbG90DQogIGdlb21fcG9pbnQoKSArIA0KICAjIEFkZCBhIHNtb290aCB0cmVuZCBsaW5lDQogIGdlb21fc21vb3RoKCkNCmBgYA0KDQpgYGB7cn0NCiMgU2h1ZmZsZSB0aGUgcm93cyBvZiBhdHRyaXRpb25fcG9wIHRoZW4gYWRkIHJvdyBJRHMNCmF0dHJpdGlvbl9zaHVmZmxlZCA8LSBhdHRyaXRpb25fcG9wICU+JQ0KICBzbGljZV9zYW1wbGUocHJvcCA9IDEpICU+JQ0KICByb3dpZF90b19jb2x1bW4oKQ0KDQojIFVzaW5nIGF0dHJpdGlvbl9zaHVmZmxlZCwgcGxvdCBZZWFyc0F0Q29tcGFueSB2cy4gcm93aWQNCmF0dHJpdGlvbl9zaHVmZmxlZCAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gcm93aWQsIHkgPSBZZWFyc0F0Q29tcGFueSkpICsNCiAgZ2VvbV9wb2ludCgpICsgDQogIGdlb21fc21vb3RoKCkNCmBgYA0KDQoqKkEgc3lzdGVtYXRpYyBzYW1wbGUgZG9lcyBub3QgYWx3YXlzIHByb2R1Y2UgYSBzYW1wbGUgc2ltaWxhciB0byBhIHNpbXBsZSByYW5kb20gc2FtcGxlLioqDQoNCiMjIyAqKlByb3BvcnRpb25hbCBzdHJhdGlmaWVkIHNhbXBsaW5nKioNCg0KSWYgeW91IGFyZSBpbnRlcmVzdGVkIGluIHN1Ymdyb3VwcyB3aXRoaW4gdGhlIHBvcHVsYXRpb24sIHRoZW4geW91IG1heSBuZWVkIHRvIGNhcmVmdWxseSBjb250cm9sIHRoZSBjb3VudHMgb2YgZWFjaCBzdWJncm91cCB3aXRoaW4gdGhlIHBvcHVsYXRpb24uwqAqUHJvcG9ydGlvbmFsIHN0cmF0aWZpZWQgc2FtcGxpbmcqwqByZXN1bHRzIGluIHN1Ymdyb3VwIHNpemVzIHdpdGhpbiB0aGUgc2FtcGxlIHRoYXQgYXJlIHJlcHJlc2VudGF0aXZlIG9mIHRoZSBzdWJncm91cCBzaXplcyB3aXRoaW4gdGhlIHBvcHVsYXRpb24uIEl0IGlzIGVxdWl2YWxlbnQgdG8gcGVyZm9ybWluZyBhIHNpbXBsZSByYW5kb20gc2FtcGxlIG9uIGVhY2ggc3ViZ3JvdXAuDQoNCmBgYHtyfQ0KZWR1Y2F0aW9uX2NvdW50c19wb3AgPC0gYXR0cml0aW9uX3BvcCAlPiUNCiAgIyBDb3VudCB0aGUgZW1wbG95ZWVzIGJ5IGVkdWNhdGlvbiBsZXZlbA0KICBjb3VudChFZHVjYXRpb24sIHNvcnQgPSBUUlVFKSAlPiUNCiAgIyBBZGQgYSBwZXJjZW50IGNvbHVtbg0KICBtdXRhdGUocGVyY2VudCA9IDEwMCAqIG4gLyBzdW0obikpDQoNCmVkdWNhdGlvbl9jb3VudHNfcG9wDQpgYGANCg0KYGBge3J9DQojIFVzZSBwcm9wb3J0aW9uYWwgc3RyYXRpZmllZCBzYW1wbGluZyB0byBnZXQgNDAlIG9mIGVhY2ggRWR1Y2F0aW9uIGdyb3VwIA0KYXR0cml0aW9uX3N0cmF0IDwtIGF0dHJpdGlvbl9wb3AgJT4lDQogIGdyb3VwX2J5KEVkdWNhdGlvbikgJT4lDQogIHNsaWNlX3NhbXBsZShwcm9wID0gMC40KQ0KDQphdHRyaXRpb25fc3RyYXQNCmBgYA0KDQpgYGB7cn0NCiMgR2V0IHRoZSBjb3VudHMgYW5kIHBlcmNlbnRzIGZyb20gYXR0cml0aW9uIHN0cmF0DQplZHVjYXRpb25fY291bnRzX3N0cmF0IDwtIGF0dHJpdGlvbl9zdHJhdCAlPiUNCiAgY291bnQoRWR1Y2F0aW9uLCBzb3J0ID0gVFJVRSkgJT4lDQogIG11dGF0ZShwZXJjZW50ID0gMTAwICogbiAvIHN1bShuKSkNCg0KZWR1Y2F0aW9uX2NvdW50c19zdHJhdA0KYGBgDQoNCiMjIyAqKkVxdWFsIGNvdW50cyBzdHJhdGlmaWVkIHNhbXBsaW5nKioNCg0KZiBvbmUgc3ViZ3JvdXAgaXMgbGFyZ2VyIHRoYW4gYW5vdGhlciBzdWJncm91cCBpbiB0aGUgcG9wdWxhdGlvbiwgYnV0IHlvdSBkb24ndCB3YW50IHRvIHJlZmxlY3QgdGhhdCBkaWZmZXJlbmNlIGluIHlvdXIgYW5hbHlzaXMsIHRoZW4geW91IGNhbiB1c2XCoCplcXVhbCBjb3VudHMgc3RyYXRpZmllZCBzYW1wbGluZyrCoHRvIGdlbmVyYXRlIHNhbXBsZXMgd2hlcmUgZWFjaCBzdWJncm91cCBoYXMgdGhlIHNhbWUgYW1vdW50IG9mIGRhdGEuIEZvciBleGFtcGxlLCBpZiB5b3UgYXJlIGFuYWx5emluZyBibG9vZCB0eXBlcywgTyBpcyB0aGUgbW9zdCBjb21tb24gYmxvb2QgdHlwZSB3b3JsZHdpZGUsIGJ1dCB5b3UgbWF5IHdpc2ggdG8gaGF2ZSBlcXVhbCBhbW91bnRzIG9mIE8sIEEsIEIsIGFuZCBBQiBpbiB5b3VyIHNhbXBsZS4NCg0KYGBge3J9DQojIFVzZSBlcXVhbCBjb3VudHMgc3RyYXRpZmllZCBzYW1wbGluZyB0byBnZXQgMzAgZW1wbG95ZWVzIGZyb20gZWFjaCBlZHVjYXRpb24gZ3JvdXANCmF0dHJpdGlvbl9lcSA8LSBhdHRyaXRpb25fcG9wICU+JQ0KICBncm91cF9ieShFZHVjYXRpb24pICU+JQ0KICBzbGljZV9zYW1wbGUobiA9IDMwKSAlPiUNCiAgdW5ncm91cCgpIA0KDQojIFNlZSB0aGUgcmVzdWx0cw0KYXR0cml0aW9uX2VxDQpgYGANCg0KYGBge3J9DQojIEdldCB0aGUgY291bnRzIGFuZCBwZXJjZW50IG9mIGF0dHJpdGlvbl9lcQ0KZWR1Y2F0aW9uX2NvdW50c19lcSA8LSBhdHRyaXRpb25fZXEgJT4lDQogIGNvdW50KEVkdWNhdGlvbikgJT4lDQogIG11dGF0ZShwZXJjZW50ID0gMTAwICogbiAvIHN1bShuKSkNCg0KIyBTZWUgdGhlIHJlc3VsdHMgDQplZHVjYXRpb25fY291bnRzX2VxDQpgYGANCg0KIyMjICoqV2VpZ2h0ZWQgc2FtcGxpbmcqKg0KDQpTdHJhdGlmaWVkIHNhbXBsaW5nIHByb3ZpZGVzIHJ1bGVzIGFib3V0IHRoZSBwcm9iYWJpbGl0eSBvZiBwaWNraW5nIHJvd3MgZnJvbSB5b3VyIGRhdGFzZXQgYXQgdGhlIHN1Ymdyb3VwIGxldmVsLiBBIGdlbmVyYWxpemF0aW9uIG9mIHRoaXMgaXPCoCoqd2VpZ2h0ZWQgc2FtcGxpbmcqKiwgd2hpY2ggbGV0cyB5b3Ugc3BlY2lmeSBydWxlcyBhYm91dCB0aGUgcHJvYmFiaWxpdHkgb2YgcGlja2luZyByb3dzIGF0IHRoZSByb3cgbGV2ZWwuIFRoZSBwcm9iYWJpbGl0eSBvZiBwaWNraW5nIGFueSBnaXZlbiByb3cgaXMgcHJvcG9ydGlvbmFsIHRvIHRoZSB3ZWlnaHQgdmFsdWUgZm9yIHRoYXQgcm93Lg0KDQpgYGB7cn0NCiMgVXNpbmcgYXR0cml0aW9uX3BvcCBwbG90IFllYXJzQXRDb21wYW55IGFzIGEgaGlzdG9ncmFtIHdpdGggYmlud2lkdGggPSAxDQpnZ3Bsb3QoYXR0cml0aW9uX3BvcCwgYWVzKFllYXJzQXRDb21wYW55KSkgKyANCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAxKQ0KYGBgDQoNCmBgYHtyfQ0KIyBTYW1wbGUgNDAwIGVtcGxveWVlcyB3ZWlnaHRlZCBieSBZZWFyc0F0Q29tcGFueQ0KYXR0cml0aW9uX3dlaWdodCA8LSBhdHRyaXRpb25fcG9wICU+JQ0KICBzbGljZV9zYW1wbGUobiA9IDQwMCwgd2VpZ2h0X2J5ID0gWWVhcnNBdENvbXBhbnkpDQoNCiMgU2VlIHRoZSByZXN1bHQgDQphdHRyaXRpb25fd2VpZ2h0DQpgYGANCg0KYGBge3J9DQojIFVzaW5nIGF0dHJpdGlvbiB3ZWlnaHQsIHBsb3QgWWVhcnNBdENvbXBhbnkgYXMgYSBoaXN0b2dyYW0gd2l0aCBiaW53aWR0aCA9IDENCmdncGxvdChhdHRyaXRpb25fd2VpZ2h0LCBhZXMoWWVhcnNBdENvbXBhbnkpKSArDQogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMSkNCmBgYA0KDQojIyMgKipQZXJmb3JtaW5nIGNsdXN0ZXIgc2FtcGxpbmcqKg0KDQpOb3cgdGhhdCB5b3Uga25vdyB3aGVuIHRvIHVzZSBjbHVzdGVyIHNhbXBsaW5nLCBpdCdzIHRpbWUgdG8gcHV0IGl0IGludG8gYWN0aW9uLiBJbiB0aGlzIGV4ZXJjaXNlIHlvdSdsbCBleHBsb3JlIHRoZcKgYEpvYlJvbGVgwqBjb2x1bW4gb2YgdGhlIGF0dHJpdGlvbiBkYXRhc2V0LiBZb3UgY2FuIHRoaW5rIG9mIGVhY2ggam9iIHJvbGUgYXMgYSBzdWJncm91cCBvZiB0aGUgd2hvbGUgcG9wdWxhdGlvbiBvZiBlbXBsb3llZXMuDQoNCmBgYHtyfQ0KIyBHZXQgdW5pcXVlIEpvYlJvbGUgdmFsdWVzDQpqb2Jfcm9sZXNfcG9wIDwtIHVuaXF1ZShhdHRyaXRpb25fcG9wJEpvYlJvbGUpDQoNCiMgUmFuZG9tbHkgc2FtcGxlIDQgSm9iUm9sZSB2YWx1ZXMNCmpvYl9yb2xlc19zYW1wIDwtIHNhbXBsZShqb2Jfcm9sZXNfcG9wLCBzaXplID0gNCkNCg0KIyBzZWUgdGhlIHJlc3VsdA0Kam9iX3JvbGVzX3NhbXANCmBgYA0KDQpgYGB7cn0NCiMgRmlsdGVyIGZvciByb3dzIHdoZXJlIEpvYlJvbGUgaXMgaW4gam9iX3JvbGVzX3NhbXANCmF0dHJpdGlvbl9maWx0ZXJlZCA8LSBhdHRyaXRpb25fcG9wICU+JQ0KICBmaWx0ZXIoSm9iUm9sZSAlaW4lIGpvYl9yb2xlc19zYW1wKSAlPiUNCiAgZ3JvdXBfYnkoSm9iUm9sZSkNCg0KIyBSYW5kb21seSBzYW1wbGUgMTAgZW1wbG95ZWVzIGZyb20gZWFjaCBzYW1wbGVkIGpvYiByb2xlDQogYXR0cml0aW9uX2NsdXMgPC0gIGF0dHJpdGlvbl9maWx0ZXJlZCAlPiUNCiAgIHNsaWNlX3NhbXBsZShuID0gMTApDQogIA0KIGF0dHJpdGlvbl9jbHVzDQpgYGANCg0KIyMjICoqMyBraW5kcyBvZiBzYW1waW5nKioNCg0KTGV0J3MgY29tcGFyZSB0aGUgcGVyZm9ybWFuY2Ugb2YgcG9pbnQgZXN0aW1hdGVzIHVzaW5nIHNpbXBsZSwgc3RyYXRpZmllZCwgYW5kIGNsdXN0ZXIgc2FtcGxpbmcuIEJlZm9yZSB3ZSBkbyB0aGF0LCB5b3UnbGwgaGF2ZSB0byBzZXQgdXAgdGhlIHNhbXBsZXMuIFdlJ2xsIHVzZSB0aGUgYFJlbGF0aW9uc2hpcFNhdGlzZmFjdGlvbmAgY29sdW1uIG9mIHRoZSBhdHRyaXRpb24gZGF0YXNldCwgd2hpY2ggY2F0ZWdvcml6ZXMgdGhlIGVtcGxveWVlJ3MgcmVsYXRpb25zaGlwIHdpdGggdGhlIGNvbXBhbnkuIEl0J3MgYW4gb3JkZXJlZCBmYWN0b3Igd2l0aCBmb3VyIGxldmVsczogYExvd2AsIGBNZWRpdW1gLCBgSGlnaGAsIGFuZCBgVmVyeV9IaWdoYC4NCg0KYGBge3J9DQphdHRyaXRpb25fc3JzIDwtIGF0dHJpdGlvbl9wb3AgJT4lDQogIHNsaWNlX3NhbXBsZShwcm9wID0gMC4yNSkNCg0KIyBQZXJmb3JtIHN0cmF0aWZpZWQgc2FtcGxpbmcgdG8gZ2V0IDAuMjUgb2YgZWFjaCByZWxhdGlvbnNoaXAgZ3JvdXANCmF0dHJpdGlvbl9zdHJ0IDwtIGF0dHJpdGlvbl9wb3AgJT4lDQogIGdyb3VwX2J5KFJlbGF0aW9uc2hpcFNhdGlzZmFjdGlvbikgJT4lDQogIHNsaWNlX3NhbXBsZShwcm9wID0gMC4yNSkNCg0KIyBHZXQgdW5pcXVlIHZhbHVlcyBvZiBSZWxhdGlvbnNoaXBTYXRpc2ZhY3Rpb24gDQpzYXRpc2ZhY3Rpb25fdW5pcXVlIDwtIHVuaXF1ZShhdHRyaXRpb25fcG9wJFJlbGF0aW9uc2hpcFNhdGlzZmFjdGlvbikNCg0KIyBSYW1kb21seSBzYW1wbGUgZm9yIDIgb2YgdGhlIHVuaXF1ZSBzYXRpc2ZhY3Rpb24gdmFsdWVzIA0Kc2F0aXNmYWN0aW9uX3NhbXAgPC0gc2FtcGxlKHNhdGlzZmFjdGlvbl91bmlxdWUpDQoNCiMgUGVyZm9ybSBjbHVzdGVyIHNhbXBsaW5nIG9uIHRoZSBzZWxlY3RlZCBncm91cCBnZXR0aW5nIDAuMjUgb2YgdGhlIHBvcHVsYXRpb24NCmF0dHJpdGlvbl9jbHVzdCA8LSBhdHRyaXRpb25fcG9wICU+JQ0KICBmaWx0ZXIoUmVsYXRpb25zaGlwU2F0aXNmYWN0aW9uICVpbiUgc2F0aXNmYWN0aW9uX3NhbXApICU+JQ0KICBncm91cF9ieShSZWxhdGlvbnNoaXBTYXRpc2ZhY3Rpb24pICU+JQ0KICBzbGljZV9zYW1wbGUobiA9IHJvdW5kKG5yb3coYXR0cml0aW9uX3BvcCkgLyA0KSkgJT4lDQogIHVuZ3JvdXAoKQ0KYGBgDQoNCiMjIyAqKlN1bW1hcnkgc3RhdGlzdGljcyBvbiBkaWZmZXJlbnQga2luZHMgb2Ygc2FtcGxlKioNCg0KTm93IHlvdSBoYXZlIHRocmVlIHR5cGVzIG9mIHNhbXBsZSAoc2ltcGxlLCBzdHJhdGlmaWVkLCBjbHVzdGVyKSwgeW91IGNhbiBjb21wYXJlIHBvaW50IGVzdGltYXRlcyBmcm9tIGVhY2ggc2FtcGxlIHRvIHRoZSBwb3B1bGF0aW9uIHBhcmFtZXRlci4gVGhhdCBpcywgeW91IGNhbiBjYWxjdWxhdGUgdGhlIHNhbWUgc3VtbWFyeSBzdGF0aXN0aWMgb24gZWFjaCBzYW1wbGUgYW5kIHNlZSBob3cgaXQgY29tcGFyZXMgdG8gdGhlIHN1bW1hcnkgc3RhdGlzdGljIGZvciB0aGUgcG9wdWxhdGlvbi4NCg0KSGVyZSwgd2UnbGwgbG9vayBhdCBob3cgc2F0aXNmYWN0aW9uIHdpdGggdGhlIGNvbXBhbnkgYWZmZWN0cyB3aGV0aGVyIG9yIG5vdCB0aGUgZW1wbG95ZWUgbGVhdmVzIHRoZSBjb21wYW55LiBUaGF0IGlzLCB5b3UnbGwgY2FsY3VsYXRlIHRoZSBwcm9wb3J0aW9uIG9mIGVtcGxveWVlcyB3aG8gbGVmdCB0aGUgY29tcGFueSAodGhleSBoYXZlIGFuIGBBdHRyaXRpb25gIHZhbHVlIG9mIGAiWWVzImApLCBmb3IgZWFjaCB2YWx1ZSBvZiBgUmVsYXRpb25zaGlwU2F0aXNmYWN0aW9uYC4NCg0KIyMjICoqV2hvbGUgcG9wdWxhdGlvbiBkYXRhc2V0KioNCg0KYGBge3J9DQojIFVzZSB0aGUgd2hvbGUgcG9wdWxhdGlvbiBkYXRhc2V0DQptZWFuX2F0dHJpdGlvbl9wb3AgPC0gYXR0cml0aW9uX3BvcCAlPiUNCiAgIyBHcm91cCBieSByZWxhdGlvbnNoaXAgc2F0aXNmYWN0aW9uIGxldmVsDQogIGdyb3VwX2J5KFJlbGF0aW9uc2hpcFNhdGlzZmFjdGlvbikgJT4lDQogICMgQ2FsY3VsYXRlIHRoZSBwcm9wb3J0aW9uIG9mIGVtcGxveWVlIGF0dHJpdGlvbg0KICBzdW1tYXJpc2UobWVhbl9hdHRyaXRpb24gPSBtZWFuKEF0dHJpdGlvbiA9PSAiWWVzIikpDQoNCiMgU2VlIHRoZSByZXN1bHQNCm1lYW5fYXR0cml0aW9uX3BvcA0KYGBgDQoNCiMjIyAqKmF0dHJpdGlvbl9zcnMgc2F0YXNldCoqDQoNCmBgYHtyfQ0KbWVhbl9hdHRyaXRpb25fc3JzIDwtIGF0dHJpdGlvbl9zcnMgJT4lDQogIGdyb3VwX2J5KFJlbGF0aW9uc2hpcFNhdGlzZmFjdGlvbikgJT4lDQogIHN1bW1hcmlzZShtZWFuX2F0dHJpdGlvbiA9IG1lYW4oQXR0cml0aW9uID09ICJZZXMiKSkNCg0KI1NlZSB0aGUgcmVzdWx0DQptZWFuX2F0dHJpdGlvbl9zcnMNCmBgYA0KDQojIyMgKiphdHRyaXRpb25fc3RyYXQgZGF0YXNldCoqDQoNCmBgYHtyfQ0KbWVhbl9hdHRyaXRpb25fc3RydCA8LSBhdHRyaXRpb25fc3RydCAlPiUNCiAgZ3JvdXBfYnkoUmVsYXRpb25zaGlwU2F0aXNmYWN0aW9uKSAlPiUNCiAgc3VtbWFyaXNlKG1lYW5fYXR0cml0aW9uID0gbWVhbihBdHRyaXRpb24gPT0gIlllcyIpKQ0KDQojIFNlZSB0aGUgcmVzdWx0DQptZWFuX2F0dHJpdGlvbl9zdHJ0DQpgYGANCg0KIyMjICoqYXR0cml0aW9uX2NsdXN0IGRhdGFzZXQqKg0KDQpgYGB7cn0NCm1lYW5fYXR0cml0aW9uX2NsdXN0IDwtIGF0dHJpdGlvbl9jbHVzdCAlPiUNCiAgZ3JvdXBfYnkoUmVsYXRpb25zaGlwU2F0aXNmYWN0aW9uKSAlPiUNCiAgc3VtbWFyaXNlKG1lYW5fYXR0cml0aW9uID0gbWVhbihBdHRyaXRpb24gPT0gIlllcyIpKQ0KDQojIFNlZSB0aGUgcmVzdWx0cw0KbWVhbl9hdHRyaXRpb25fY2x1c3QNCmBgYA0KDQojIyMgKipDYWxjdWxhdGUgcmVsYXRpdmUgZXJyb3JzKioNCg0KVGhlIHNpemUgb2YgdGhlIHNhbXBsZSB5b3UgdGFrZSBhZmZlY3RzIGhvdyBhY2N1cmF0ZWx5IHRoZSBwb2ludCBlc3RpbWF0ZXMgcmVmbGVjdCB0aGUgY29ycmVzcG9uZGluZyBwb3B1bGF0aW9uIHBhcmFtZXRlci4gRm9yIGV4YW1wbGUsIHdoZW4geW91IGNhbGN1bGF0ZSBhIHNhbXBsZSBtZWFuLCB5b3Ugd2FudCBpdCB0byBiZSBjbG9zZSB0byB0aGUgcG9wdWxhdGlvbiBtZWFuLiBIb3dldmVyLCBpZiB5b3VyIHNhbXBsZSBpcyB0b28gc21hbGwsIHRoaXMgbWlnaHQgbm90IGJlIHRoZSBjYXNlLg0KDQpUaGUgbW9zdCBjb21tb24gbWV0cmljIGZvciBhc3Nlc3NpbmcgYWNjdXJhY3kgaXMgKnJlbGF0aXZlIGVycm9yKi4gVGhpcyBpcyB0aGUgYWJzb2x1dGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSBwb3B1bGF0aW9uIHBhcmFtZXRlciBhbmQgdGhlIHBvaW50IGVzdGltYXRlLCBhbGwgZGl2aWRlZCBieSB0aGUgcG9wdWxhdGlvbiBwYXJhbWV0ZXIuIEl0IGlzIHNvbWV0aW1lcyBleHByZXNzZWQgYXMgYSBwZXJjZW50YWdlLg0KDQpgYGB7cn0NCiMgR2VuZXJhdGUgYSBzaW1wbGUgcmFuZG9tIHNhbXBsZSBvZiAxMCByb3dzIA0KYXR0cml0aW9uX3NyczEwIDwtIGF0dHJpdGlvbl9wb3AgJT4lDQogIHNsaWNlX3NhbXBsZShuID0gMTApDQoNCiMgQ2FsY3VsYXRlIHRoZSBwcm9wb3J0aW9uIG9mIGVtcGxveWVlIGF0dHJpdGlvbiBpbiB0aGUgc2FtcGxlDQogIG1lYW5fYXR0cml0aW9uX3NyczEwIDwtIGF0dHJpdGlvbl9zcnMxMCAlPiUNCiAgICBzdW1tYXJpc2UobWVhbl9hdHRyaXRpb24gPSBtZWFuKEF0dHJpdGlvbiA9PSAiWWVzIikpICU+JQ0KICAgIHB1bGwobWVhbl9hdHRyaXRpb24pDQogIA0KIyBDYWxjdWxhdGUgdGhlIHJlbGF0aXZlIGVycm9yIHBlcmNlbnRhZ2UNCnJlbF9lcnJvcl9wY3QxMCA8LSAxMDAgKiBhYnMobWVhbl9hdHRyaXRpb25fcG9wIC0gbWVhbl9hdHRyaXRpb25fc3JzMTApIC8gbWVhbl9hdHRyaXRpb25fcG9wDQoNCiMgU2VlIHRoZSByZXN1bHRzDQpyZWxfZXJyb3JfcGN0MTANCmBgYA0KDQpDYWxjdWxhdGUgdGhlIHJlbGF0aXZlIGVycm9yIHBlcmNlbnRhZ2UgYWdhaW4uIFRoaXMgdGltZSwgdXNlIGEgc2ltcGxlIHJhbmRvbSBzYW1wbGUgb2Ygb25lIGh1bmRyZWQgcm93cyBvZiBgYXR0cml0aW9uX3BvcGAuDQoNCmBgYHtyfQ0KYXR0cml0aW9uX3NyczEwMCA8LSBhdHRyaXRpb25fcG9wICU+JQ0KICBzbGljZV9zYW1wbGUobiA9IDEwMCkNCg0KICBtZWFuX2F0dHJpdGlvbl9zcnMxMDAgPC0gYXR0cml0aW9uX3NyczEwMCAlPiUNCiAgICBzdW1tYXJpc2UobWVhbl9hdHRyaXRpb24gPSBtZWFuKEF0dHJpdGlvbiA9PSAiWWVzIikpICU+JQ0KICAgIHB1bGwobWVhbl9hdHRyaXRpb24pDQogIA0KcmVsX2Vycm9yX3BjdDEwMCA8LSAxMDAgKiBhYnMobWVhbl9hdHRyaXRpb25fcG9wIC0gbWVhbl9hdHRyaXRpb25fc3JzMTApIC8gbWVhbl9hdHRyaXRpb25fcG9wDQoNCnJlbF9lcnJvcl9wY3QxMDANCmBgYA0KDQojIyMgKipSZXBsaWNhdGluZyBzYW1wbGVzKioNCg0KV2hlbiB5b3UgY2FsY3VsYXRlIGEgcG9pbnQgZXN0aW1hdGUgc3VjaCBhcyBhIHNhbXBsZSBtZWFuLCB0aGUgdmFsdWUgeW91IGNhbGN1bGF0ZSBkZXBlbmRzIG9uIHRoZSByb3dzIHRoYXQgd2VyZSBpbmNsdWRlZCBpbiB0aGUgc2FtcGxlLiBUaGF0IG1lYW5zIHRoYXQgdGhlcmUgaXMgc29tZSByYW5kb21uZXNzIGluIHRoZSBhbnN3ZXIuIEluIG9yZGVyIHRvIHF1YW50aWZ5IHRoZSB2YXJpYXRpb24gY2F1c2VkIGJ5IHRoaXMgcmFuZG9tbmVzcywgeW91IGNhbiBjcmVhdGUgbWFueSBzYW1wbGVzIGFuZCBjYWxjdWxhdGUgdGhlIHNhbXBsZSBtZWFuIChvciBvdGhlciBzdGF0aXN0aWMpIGZvciBlYWNoIHNhbXBsZS4NCg0KYGBge3J9DQojIFJlcGxpY2F0ZSB0aGlzIGNvZGUgNTAwIHRpbWVzDQptZWFuX2F0dHJpdGlvbnMgPC0gcmVwbGljYXRlKG4gPSA1MDAsIA0KICBleHByID0gYXR0cml0aW9uX3BvcCAlPiUNCiAgICBzbGljZV9zYW1wbGUobiA9IDIwKSAlPiUNCiAgICBzdW1tYXJpc2UobWVhbl9hdHRyaXRpb24gPSBtZWFuKEF0dHJpdGlvbiA9PSAiWWVzIikpICU+JQ0KICAgIHB1bGwobWVhbl9hdHRyaXRpb24pDQopDQoNCiMgc2VlIHRoZSByZXN1bHQNCmhlYWQobWVhbl9hdHRyaXRpb25zKQ0KYGBgDQoNCmBgYHtyfQ0KIyBTdG9yZSBtZWFuIGF0dHJpdGlvbnMgaW4gYSB0aWJibGUgaW4gYSBjb2x1bW4gbmFtZWQgc2FtcGxlX21lYW4NCnNhbXBsZV9tZWFuc19hdHQgPC0gdGliYmxlKHNhbXBsZV9tZWFuID0gbWVhbl9hdHRyaXRpb25zKQ0KDQojIFBsb3QgYSBoaXN0b2dyYW0gb2YgdGhlICJzYW1wbGVfbWVhbiIgY29sdW1uLCBiaW53aWR0aCAwLjA1DQpnZ3Bsb3Qoc2FtcGxlX21lYW5zX2F0dCwgYWVzKHNhbXBsZV9tZWFuKSkgKyANCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAwLjA1KQ0KYGBgDQoNCiMjIyAqKlBvcHVsYXRpb24gYW5kIHNhbXBsaW5nIGRpc3RyaWJ1dGlvbnMgbWVhbnMqKg0KDQpPbmUgb2YgdGhlIHVzZWZ1bCBmZWF0dXJlcyBvZiBzYW1wbGluZyBkaXN0cmlidXRpb25zIGlzIHRoYXQgd2UgY2FuIHF1YW50aWZ5IHRoZW0uIEluIHBhcnRpY3VsYXIsIHdlIGNhbiBjYWxjdWxhdGUgc3VtbWFyeSBzdGF0aXN0aWNzIG9uIHRoZW0uIEhlcmUsIHdlJ2xsIGxvb2sgYXQgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZSBtZWFuIG9mIHRoZSBzYW1wbGluZyBkaXN0cmlidXRpb24gYW5kIHRoZSBwb3B1bGF0aW9uIHBhcmFtZXRlciB0aGF0IHRoZSBzYW1wbGluZyBpcyBzdXBwb3NlZCB0byBlc3RpbWF0ZS4NCg0KVGhyZWUgc2FtcGxpbmcgZGlzdHJpYnV0aW9ucyBhcmUgcHJvdmlkZWQuIEluIGVhY2ggY2FzZSwgdGhlIGVtcGxveWVlIGF0dHJpdGlvbiBkYXRhc2V0IHdhcyBzYW1wbGVkIHVzaW5nIHNpbXBsZSByYW5kb20gc2FtcGxpbmcsIHRoZW4gdGhlIG1lYW4gYXR0cml0aW9uIHdhcyBjYWxjdWxhdGVkLiBUaGlzIHdhcyBkb25lIDEwMDAgdGltZXMgdG8gZ2V0IGEgc2FtcGxpbmcgZGlzdHJpYnV0aW9uIG9mIG1lYW4gYXR0cml0aW9ucy4gT25lIHNhbXBsaW5nIGRpc3RyaWJ1dGlvbiB1c2VkIGEgc2FtcGxlIHNpemUgb2YgNSBmb3IgZWFjaCByZXBsaWNhdGUsIG9uZSB1c2VkIDUwLCBhbmQgb25lIHVzZWQgNTAwLg0KDQpMZXQncyBjcmVhdGUgdGhlIGRhdGFzZXRzIG9mIHNhbXBsZSBkaXN0cmlidXRpb25zOg0KDQpgYGB7cn0NCm51bV9yZXBsYyA8LSAxMDAwDQoNCiMgUmVwbGljYXRlIHRoaXMgY29kZSAxMDAwIHRpbWVzDQpzYW1wX2Rpc3RyaWJ1dGlvbl81IDwtIHJlcGxpY2F0ZShuID0gbnVtX3JlcGxjLCANCiAgZXhwciA9IGF0dHJpdGlvbl9wb3AgJT4lDQogICAgc2xpY2Vfc2FtcGxlKG4gPSA1KSAlPiUNCiAgICBzdW1tYXJpc2UobWVhbl9hdHRyaXRpb24gPSBtZWFuKEF0dHJpdGlvbiA9PSAiWWVzIikpICU+JQ0KICAgIHB1bGwobWVhbl9hdHRyaXRpb24pLCBzaW1wbGlmeSA9IEZBTFNFDQopDQoNCiMgU3RvcmUgbWVhbiBhdHRyaXRpb25zIGluIGEgdGliYmxlIGluIGEgY29sdW1uIG5hbWVkIHNhbXBsZV9tZWFuDQpzYW1wX2Rpc3RyaWJ1dGlvbl81X3RpYmwgPC0gdGliYmxlKHJlcGxpY2F0ZSA9IDE6bnVtX3JlcGxjLCBtZWFuX2F0dHJpdGlvbiA9IHVubGlzdChzYW1wX2Rpc3RyaWJ1dGlvbl81KSkNCnNhbXBfZGlzdHJpYnV0aW9uXzVfdGlibA0KDQojIEZvciA1MCBzYW1wbGVzDQpzYW1wX2Rpc3RyaWJ1dGlvbl81MCA8LSByZXBsaWNhdGUobiA9IG51bV9yZXBsYywgDQogIGV4cHIgPSBhdHRyaXRpb25fcG9wICU+JQ0KICAgIHNsaWNlX3NhbXBsZShuID0gNTApICU+JQ0KICAgIHN1bW1hcmlzZShtZWFuX2F0dHJpdGlvbiA9IG1lYW4oQXR0cml0aW9uID09ICJZZXMiKSkgJT4lDQogICAgcHVsbChtZWFuX2F0dHJpdGlvbiksIHNpbXBsaWZ5ID0gRkFMU0UNCikNCnNhbXBfZGlzdHJpYnV0aW9uXzUwX3RpYmwgPC0gdGliYmxlKHJlcGxpY2F0ZSA9IDE6bnVtX3JlcGxjLCBtZWFuX2F0dHJpdGlvbiA9IHVubGlzdChzYW1wX2Rpc3RyaWJ1dGlvbl81MCkpDQpzYW1wX2Rpc3RyaWJ1dGlvbl81MF90aWJsDQoNCiMgRm9yIDUwMCBzYW1wbGVzDQpzYW1wX2Rpc3RyaWJ1dGlvbl81MDAgPC0gcmVwbGljYXRlKG4gPSBudW1fcmVwbGMsIA0KICBleHByID0gYXR0cml0aW9uX3BvcCAlPiUNCiAgICBzbGljZV9zYW1wbGUobiA9IDUwKSAlPiUNCiAgICBzdW1tYXJpc2UobWVhbl9hdHRyaXRpb24gPSBtZWFuKEF0dHJpdGlvbiA9PSAiWWVzIikpICU+JQ0KICAgIHB1bGwobWVhbl9hdHRyaXRpb24pLCBzaW1wbGlmeSA9IEZBTFNFDQopDQpzYW1wX2Rpc3RyaWJ1dGlvbl81MDBfdGlibCA8LSB0aWJibGUocmVwbGljYXRlID0gMTpudW1fcmVwbGMsIG1lYW5fYXR0cml0aW9uID0gdW5saXN0KHNhbXBfZGlzdHJpYnV0aW9uXzUwMCkpDQpzYW1wX2Rpc3RyaWJ1dGlvbl81MDBfdGlibA0KYGBgDQoNCmBgYHtyfQ0KIyBDYWxjdWxhdGUgdGhlIG1lYW4gYWNyb3NzIHJlcGxpY2F0ZXMgb2YgdGhlIG1lYW4gYXR0cml0aW9ucyBpbiBzYW1wX2Rpc3RyaWJ1dGlvbl81X3RpYmwsIHNhbXBfZGlzdHJpYnV0aW9uXzUwX3RpYmwsIHNhbXBfZGlzdHJpYnV0aW9uXzUwMF90aWJsDQptZWFuX29mX21lYW5zNSA8LSBzYW1wX2Rpc3RyaWJ1dGlvbl81X3RpYmwgJT4lDQogIHN1bW1hcmlzZShtZWFuX21lYW5fYXR0cml0aW9uID0gbWVhbihtZWFuX2F0dHJpdGlvbikpDQoNCm1lYW5fb2ZfbWVhbnM1MCA8LSBzYW1wX2Rpc3RyaWJ1dGlvbl81MF90aWJsICU+JQ0KICBzdW1tYXJpc2UobWVhbl9tZWFuX2F0dHJpdGlvbiA9IG1lYW4obWVhbl9hdHRyaXRpb24pKQ0KDQptZWFuX29mX21lYW5zNTAwIDwtIHNhbXBfZGlzdHJpYnV0aW9uXzUwMF90aWJsICU+JQ0KICBzdW1tYXJpc2UobWVhbl9tZWFuX2F0dHJpdGlvbiA9IG1lYW4obWVhbl9hdHRyaXRpb24pKQ0KDQptZWFuX29mX21lYW5zNQ0KbWVhbl9vZl9tZWFuczUwDQptZWFuX29mX21lYW5zNTAwDQpgYGANCg0KIyMjICoqUG9wdWxhdGlvbiBhbmQgc2FtcGxpbmcgZGlzdHJpYnV0aW9uIHZhcmlhdGlvbioqDQoNCllvdSBqdXN0IGNhbGN1bGF0ZWQgdGhlIG1lYW4gb2YgdGhlIHNhbXBsaW5nIGRpc3RyaWJ1dGlvbiBhbmQgc2F3IGhvdyBpdCBpcyBhbiBlc3RpbWF0ZSBvZiB0aGUgY29ycmVzcG9uZGluZyBwb3B1bGF0aW9uIHBhcmFtZXRlci4gU2ltaWxhcmx5LCBhcyBhIHJlc3VsdCBvZiB0aGUgY2VudHJhbCBsaW1pdCB0aGVvcmVtLCB0aGUgc3RhbmRhcmQgZGV2aWF0aW9uIG9mIHRoZSBzYW1wbGluZyBkaXN0cmlidXRpb24gaGFzIGFuIGludGVyZXN0aW5nIHJlbGF0aW9uc2hpcCB3aXRoIHRoZSBwb3B1bGF0aW9uIHBhcmFtZXRlcidzIHN0YW5kYXJkIGRldmlhdGlvbiBhbmQgdGhlIHNhbXBsZSBzaXplLg0KDQpgYGB7cn0NCnNkX29mX21lYW5zNSA8LSBzYW1wX2Rpc3RyaWJ1dGlvbl81X3RpYmwgJT4lDQogIHN1bW1hcmlzZShzZF9tZWFuX2F0dHJpdGlvbiA9IHNkKG1lYW5fYXR0cml0aW9uKSkNCg0Kc2Rfb2ZfbWVhbnM1MCA8LSBzYW1wX2Rpc3RyaWJ1dGlvbl81MF90aWJsICU+JQ0KICBzdW1tYXJpc2Uoc2RfbWVhbl9hdHRyaXRpb24gPSBzZChtZWFuX2F0dHJpdGlvbikpDQoNCnNkX29mX21lYW5zNTAwIDwtIHNhbXBfZGlzdHJpYnV0aW9uXzUwMF90aWJsICU+JQ0KICBzdW1tYXJpc2Uoc2RfbWVhbl9hdHRyaXRpb24gPSBzZChtZWFuX2F0dHJpdGlvbikpDQoNCnNkX29mX21lYW5zNQ0Kc2Rfb2ZfbWVhbnM1MA0Kc2Rfb2ZfbWVhbnM1MDANCmBgYA0KDQpgYGB7cn0NCmNvZmZlZV9mb2N1cyA8LSBjb2ZmZWVfcmF0aW5ncyAlPiUNCiAgc2VsZWN0KHZhcmlldHksIGNvdW50cnlfb2Zfb3JpZ2luLCBmbGF2b3IpICU+JQ0KICByb3dpZF90b19jb2x1bW4oKQ0KDQpnbGltcHNlKGNvZmZlZV9mb2N1cykNCmBgYA0KDQojIyMgKipSZXNhbXBsZSB3aXRoIGBzbGljZV9zYW1wbGUoKWAqKg0KDQpUbyBzYW1wbGUgd2l0aCByZXBsYWNlbWVudCwgeW91IGNhbGwgYHNsaWNlX3NhbXBsZSgpYCBhcyB1c3VhbCwgYnV0IHNldCB0aGUgYHJlcGxhY2VgIGFyZ3VtZW50IHRvIGBUUlVFYC4gU2V0dGluZyBwcm9wIHRvIDEgZ2l2ZXMgYSBzYW1wbGUgd2l0aCB0aGUgc2FtZSBzaXplIGFzIHRoZSBvcmlnaW5hbCBkYXRhc2V0Lg0KDQpgYGB7cn0NCmNvZmZlZV9yZXNhbXAgPC0gY29mZmVlX2ZvY3VzICU+JQ0KICBzbGljZV9zYW1wbGUocHJvcCA9IDEsIHJlcGxhY2UgPSBUUlVFKQ0KYGBgDQoNCiMjIyAqKlJlcGVhdGVkIGNvZmZlZXMqKg0KDQpDb3VudGluZyB0aGUgYHJvd2lkYCBzaG93cyBob3cgbWFueSB0aW1lcyBlYWNoIGNvZmZlZSBlbmRlZCB1cCBpbiB0aGUgcmVzYW1wbGVkIGRhdGFzZXQuIFNvbWUgY29mZmVlcyBhcmUgcHJlc2VudCBmaXZlIHRpbWVzIGluIHRoZSBuZXcgZGF0YXNldC4NCg0KYGBge3J9DQpjb2ZmZWVfcmVzYW1wICU+JQ0KICBjb3VudChyb3dpZCwgc29ydCA9IFRSVUUpDQpgYGANCg0KIyMjICoqTWlzc2luZyBjb2ZmZWVzKioNCg0KVGhhdCBtZWFucyB0aGF0IHNvbWUgY29mZmVlcyBkaWRuJ3QgZW5kIHVwIGluIHRoZSByZXNhbXBsZWQgZGF0YXNldC4gQnkgdGFraW5nIHRoZSBudW1iZXIgb2YgZGlzdGluY3Qgcm93IElEcyBpbiB0aGUgcmVzYW1wbGVkIGRhdGFzZXQgdXNpbmcgZHBseXIncyBuX2Rpc3RpbmN0LCB5b3UgY2FuIHNlZSB0aGF0IDgzNCBkaWZmZXJlbnQgY29mZmVlcyB3ZXJlIGluY2x1ZGVkLCBhbmQgNTA1IGNvZmZlZXMgd2VyZW4ndCBpbmNsdWRlZC4NCg0KYGBge3J9DQoNCmBgYA0K