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)

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