Session 07: Introduction to Probability Theory in R. Random Variables + Probability Functions

Feedback should be send to goran.milovanovic@datakolektiv.com. These notebooks accompany the Intro to Data Science: Non-Technical Background course 2020/21.


What do we want to do today?

We are entering the core part of the course with an introduction to Probability Theory and Mathematical Statistics in R! In this Session, we will learn about probability and probability functions: why are discrete and continuous random outcomes different, what is a statistical experiment, how to perform numerical simulations of statistical experiments in R, and what is the difference between the Probability Density Function (pdf), the Probability Mass Function (pmf), and the Cumulative Distribution Function (cdf)… Then we introduce important two important probability distributions: Binomial (discrete) and Normal (i.e. Gaussian, continuous).

0. Prerequisits

library(tidyverse)
Registered S3 methods overwritten by 'dbplyr':
  method         from
  print.tbl_lazy     
  print.tbl_sql      
── Attaching packages ───────────────────────────────────────────── tidyverse 1.3.1 ──
✓ ggplot2 3.3.5     ✓ purrr   0.3.4
✓ tibble  3.1.6     ✓ dplyr   1.0.7
✓ tidyr   1.1.4     ✓ stringr 1.4.0
✓ readr   2.0.2     ✓ forcats 0.5.1
── Conflicts ──────────────────────────────────────────────── tidyverse_conflicts() ──
x dplyr::filter() masks stats::filter()
x dplyr::lag()    masks stats::lag()
set.seed(9999)

The set.seed(9999) code will ensure the reproducibility of the numerical simulations that will be run here; to be explained in our session.

1. Probability: Theoretical and Experimental

Imagine we toss a fair coin ten times and write down the outcomes as (H)ead or (T)ail each time:

tosses <- c('H', 'H', 'T', 'T', 'H', 'T', 'T', 'H', 'T', 'T')
table(tosses)
tosses
H T 
4 6 

In this statistical experiment I have made up the results. I told you that we will be tossing a fair coin: the one with an equal probability (say: 50/50, for now) to result in H or T. However, what we have observed is maybe a bit unusual: four H and six T. Now, is that a fair coin? The answer is: it still might be.

From my statistical experiment, what is the probability of observing H - P(H) - and the probability of observing T - P(T)?

table(tosses)/length(tosses)
tosses
  H   T 
0.4 0.6 

Yes. From our observations, we estimate P(H) to be .4 and P(T) to be .6. However… we have assumed already that the coin is fair. Let’s try to build statistical experiments by relying on that assumption.

tosses <- sample(c('H', 'T'), size = 10, replace = TRUE, prob = c(.5, .5))
table(tosses)
tosses
H T 
8 2 
tosses <- sample(c('H', 'T'), size = 10, replace = TRUE, prob = c(.5, .5))
table(tosses)
tosses
H T 
5 5 
tosses <- sample(c('H', 'T'), size = 10, replace = TRUE, prob = c(.5, .5))
table(tosses)
tosses
H T 
3 7 

1. A coin, fair or not

Wait - sample() does not always return the same result? No, of course not: the story of random variables and random sampling begins! What we will do next is to perform a large number - say 10000 - of identical statistical experiments of the following form: each time we toss a fair coin - defined by the value of the prob = c(.5, .5) argument in our sample() call - and record how many times we observe H or T:

domain <- c('H', 'T')
trials = 10
distribution <- c(.5, .5)
experiment <- lapply(1:10000, function(x) {
  sample(x = domain,
         size = trials,
         replace = T,
         prob = distribution)
  
})
experiment <- Reduce(rbind, experiment)
colnames(experiment) <- paste0('t_', 1:dim(experiment)[2])
rownames(experiment) <- paste0('exp_', 1:dim(experiment)[1])
head(experiment, 20)
       t_1 t_2 t_3 t_4 t_5 t_6 t_7 t_8 t_9 t_10
exp_1  "T" "H" "T" "T" "H" "T" "T" "H" "H" "H" 
exp_2  "H" "H" "H" "T" "H" "T" "T" "H" "T" "H" 
exp_3  "T" "H" "T" "T" "H" "T" "T" "H" "H" "T" 
exp_4  "T" "T" "T" "H" "T" "H" "H" "T" "H" "H" 
exp_5  "H" "H" "T" "T" "T" "H" "T" "T" "T" "H" 
exp_6  "H" "T" "H" "H" "H" "H" "T" "H" "T" "H" 
exp_7  "H" "T" "T" "T" "T" "H" "T" "T" "T" "T" 
exp_8  "H" "H" "T" "H" "H" "H" "T" "T" "H" "H" 
exp_9  "T" "T" "H" "H" "T" "T" "H" "H" "H" "T" 
exp_10 "H" "T" "H" "T" "T" "T" "H" "T" "T" "T" 
exp_11 "T" "T" "T" "T" "T" "T" "T" "H" "H" "H" 
exp_12 "T" "T" "T" "T" "H" "T" "H" "H" "H" "T" 
exp_13 "T" "H" "T" "H" "H" "T" "T" "T" "T" "H" 
exp_14 "H" "T" "H" "H" "T" "T" "T" "H" "T" "T" 
exp_15 "T" "T" "H" "T" "T" "H" "H" "H" "H" "T" 
exp_16 "H" "H" "T" "T" "T" "T" "T" "H" "T" "T" 
exp_17 "T" "H" "H" "T" "H" "H" "H" "H" "T" "T" 
exp_18 "T" "T" "H" "H" "H" "T" "T" "H" "H" "H" 
exp_19 "T" "T" "H" "H" "H" "H" "H" "T" "T" "H" 
exp_20 "T" "H" "T" "H" "H" "T" "H" "T" "H" "H" 

Let’s estimate P(H) and P(T) from the experiment matrix from each of the 10000 statistical experiments:

probability <- apply(experiment, 1, table)
head(probability)
$exp_1

H T 
5 5 

$exp_2

H T 
6 4 

$exp_3

H T 
4 6 

$exp_4

H T 
5 5 

$exp_5

H T 
4 6 

$exp_6

H T 
7 3 

Ok:

probability <- sapply(probability, function(x) {
  x/sum(x)
})
head(probability)
$exp_1

  H   T 
0.5 0.5 

$exp_2

  H   T 
0.6 0.4 

$exp_3

  H   T 
0.4 0.6 

$exp_4

  H   T 
0.5 0.5 

$exp_5

  H   T 
0.4 0.6 

$exp_6

  H   T 
0.7 0.3 

Now:

probability <- as.data.frame(purrr::reduce(probability, rbind), 
                             stringsAsFactors = F)
head(probability)

Note. I have used the reduce() function from {purrr} in place of base R Reduce(); {purrr} is a part of {tidyverse}. The motivation to use the {purrr} versions of some basic Functional Programming functions in R is nicely explained in the following blog post: TO PURRR OR NOT TO PURRR.

And now we would want to plot the histograms of the H and T values:

pH <- as.data.frame(table(probability$H), 
                    stringsAsFactors = F)
pH$Result <- 'H'
pT <- as.data.frame(table(probability$T), 
                    stringsAsFactors = F)
pT$Result <- 'T'
probabilityDistribution <- rbind(pH, pT)
colnames(probabilityDistribution) <- c('Estimate', 'Frequency', 'Result')

Finally:

ggplot(probabilityDistribution, 
       aes(x = Estimate, y = Frequency, 
           color = Result, 
           fill = Result)) + 
  geom_bar(stat = "identity") + 
  scale_color_manual(values = c('cadetblue4', 'cadetblue2')) +
  scale_fill_manual(values = c('cadetblue4', 'cadetblue2')) + 
  facet_wrap(~Result) + 
  theme_bw() + 
  theme(panel.border = element_blank()) + 
  theme(strip.background = element_blank()) + 
  theme(legend.position = "none")

Q. Is the coin fair or not?

What are the most frequently observed estimates of P(H) and P(T) in these two distributions?

probabilityDistribution %>% 
  dplyr::select(Estimate, Frequency, Result) %>% 
  dplyr::group_by(Result) %>% 
  dplyr::summarise(mode = Estimate[which.max(Frequency)])

What are the mean estimates of P(H) and P(T) in these two distributions?

probabilityDistribution %>% 
  dplyr::select(Estimate, Frequency, Result) %>% 
  dplyr::group_by(Result) %>% 
  dplyr::summarise(mean = sum(as.numeric(Estimate)*Frequency)/sum(Frequency))

Well it does look like a fair coin in the end.

Let’s play the following game:

  • we role out ~20,000 statistical experiments with a fair coin,
  • each time increasing the number of coin tosses as 10:20000;
  • each time we perform an experiment, we compute the mean of the observations,
  • which we will code as 1 for H and 0 for T:
sample_sizes <- 10:20000
meanProbs <- sapply(sample_sizes, function(y) {
  s <- sample(x = c(0, 1), size = y, replace = TRUE, prob = rep(.5, 2))
  return(mean(s))
})
meanProbsPlot <- as.data.frame(meanProbs)
colnames(meanProbsPlot) <- "Probability"
ggplot(meanProbsPlot,
       aes(x = Probability)) + 
  geom_histogram(bins = 500, fill = "red") + 
  theme_bw() + 
  theme(panel.border = element_blank())

Q. What is the difference between each mean in meanProbs and the value of p = .5?

diffs <- .5 - meanProbs
diffs <- as.data.frame(diffs)
diffs$sample_size <- sample_sizes
ggplot(diffs, 
       aes(x = sample_size, 
           y = diffs)) + 
  geom_line(color = "cadetblue2", size = .25) + 
  theme_bw() + 
  theme(axis.text.x = element_text(angle = 90)) + 
  theme(panel.border = element_blank())

Interesting. Q. Do you believe in computer generated random numbers?

Let’s play the following game:

  • we role out ~20,000 statistical experiments with an unfair coin,
  • the one which results in 1 with p = .7 and 0 with p = .3,
  • each time increasing the number of coin tosses as 10:20000;
  • each time we perform an experiment, we compute the mean of the observations,
  • which we will code as 1 for H and 0 for T:
sample_sizes <- 10:20000
meanProbs <- sapply(sample_sizes, function(y) {
  s <- sample(x = c(0, 1), size = y, replace = TRUE, prob = c(.3, .7))
  mean(s)
})
meanProbsPlot <- as.data.frame(meanProbs)
colnames(meanProbsPlot) <- "Probability"
ggplot(meanProbsPlot,
       aes(x = Probability)) + 
  geom_histogram(bins = 500, fill = "blue") + 
  theme_bw() + 
  theme(panel.border = element_blank())

Q. What is the difference between each mean in meanProbs and the value of p = .5?

diffs <- .5 - meanProbs
diffs <- as.data.frame(diffs)
diffs$sample_size <- sample_sizes
ggplot(diffs, 
       aes(x = sample_size, 
           y = diffs)) + 
  geom_line(color = "cadetblue4", size = .25) + 
  theme_bw() + 
  theme(axis.text.x = element_text(angle = 90)) + 
  theme(panel.border = element_blank())

Note. Whatch the y axis carefully… ^^

Q. Oh, wait: what is the difference between each mean in meanProbs and the value of p = .7?

diffs <- .7 - meanProbs
diffs <- as.data.frame(diffs)
diffs$sample_size <- sample_sizes
ggplot(diffs, 
       aes(x = sample_size, 
           y = diffs)) + 
  geom_line(color = "cadetblue4", size = .25) + 
  theme_bw() + 
  theme(axis.text.x = element_text(angle = 90)) + 
  theme(panel.border = element_blank())

2. A dice

The following does not even need an introduction:

results <- sample(x = 1:6, size = 100, replace = T, prob = rep(1/6, 6))
table(results)
results
 1  2  3  4  5  6 
15 12 21 15 19 18 

A fair dice? I don’t know (but… prob = rep(1/6, 6)). Let’ see:

Ok:

  • a set of statistical experiments is performed,
  • each time we toss a fair dice,
  • each time increasing the number of tosses as seq(1000, 9600, by = 500).
domain <- 1:6
sample_sizes <- seq(100, 9600, by = 500)
probs = rep(1/6, 6)
results <- lapply(sample_sizes, function(y) {
  s <- sample(x = domain, size = y, replace = T, prob = probs)
  s <- as.data.frame(table(s))
  colnames(s) <- c('Outcome', 'Frequency')
  s$sample_size <- y
  return(s)
})
results <- reduce(results, rbind)
print(results)
ggplot(results, 
       aes(x = Outcome, 
           y = Frequency,)) + 
  geom_bar(stat = "identity", 
           fill = "black", 
           color = "black", 
           width = .5) + 
  facet_wrap(~sample_size, ncol = 5, scales = "free") + 
  ylab("Probability") + 
  theme_bw() + 
  theme(panel.border = element_blank()) + 
  theme(strip.background = element_blank()) + 
  theme(axis.text.y = element_text(size = 8))

Note. In the previous coin tossing experiments we have varied two things: p, the probability of obtaining H or T, and the number of coin tosses in the experiment, which we can term: n. Now, observe how P(H) = 1 - P(T), simply because a coin always turns either H or T. So we are not working with two probabilities, P(H) and P(T), really, but with only one: p, and say that p stands for P(H) because we can always know P(T) if we know the former. In other words, there are two numbers that we can use to describe each statistical experiment that encompasses coin tosses: p, and n. Remember them well.

2. Mean, Variance, and Standard Deviation

We already know about the mean - or the expected value, as it is also called - of a set of observations:

observations <- c(5.5, 1.9, 7.3, 4.4, 6.2, 6.5, 2, 8.3, .1, 5.7, 1.3)
meanObservations <- sum(observations)/length(observations)
meanObservations == mean(observations)
[1] TRUE

2.1 Central Tendency and Dispersion

Mean, median (or the 50th percentile), and mode (the most frequently observed value in a set of observations) are all measures of central tendency in statistics. We have also learned about some measures of dispersion already, without even introducing the term. For example, in Session06 on EDA we have used the Interquartile Range, IQR, which is the difference between the 3rd and the 1st quartile in the data, as well as the range: the difference between the maximum and minimum of the observations. Now we introduce two new measures of dispersion: the variance and standard deviation.

observations <- c(5.5, 1.9, 7.3, 4.4, 6.2, 6.5, 2, 8.3, .1, 5.7, 1.3)
n <- length(observations)
meanObservations <- mean(observations)
squared_residuals <- (observations - mean(observations))^2
varianceObservations <- sum(squared_residuals)/(n-1)
print(varianceObservations)
[1] 7.422182

And of course there is the var() function in base R:

var(observations)
[1] 7.422182

Again: how is variance computed?

\[\sigma^2 = \frac{\sum_{i=1}^{N}(x_i - \overline{x})^2}{N-1}\] And we also use the square root of the variance, standard deviation, to describe the dispersion of measurements:

\[\sigma = \sqrt{\frac{\sum_{i=1}^{N}(x_i - \overline{x})^2}{N-1}}\]

A handy R function sd() does exactly that:

sd(observations)
[1] 2.724368

2.2 Descriptive statistics for mtcars

Now when we know this, we can complete the descriptive statistics of mtcars that we have used in Session06 on EDA, just for an exercise:

data(mtcars)
descriptives <- mtcars %>%
  summarise(across(.cols = everything(),
                   .fns = list(mean = mean,
                               median = median, 
                               min = min, 
                               max = max, 
                               range = ~ max(.x) - min(.x),
                               IQR = IQR, 
                               var = var, 
                               stddev = sd))
            )
print(descriptives)

We need to tidy up, obviously:

descriptives <- as.data.frame(t(descriptives))
colnames(descriptives) <- 'value'
descriptives$measurement <- rownames(descriptives)
head(descriptives)
descriptives <- descriptives %>% 
  tidyr::separate(measurement,
                  into = c('feature', 'statistic'),
                  sep = "_")
head(descriptives)

And finally:

descriptives <- descriptives %>% 
  tidyr::pivot_wider(names_from = 'statistic',
                     values_from = 'value')
print(descriptives)

2.3 Probability and Probability Functions: an overview

3. The Binomial Distribution: p, n

3.1 Binomial Distribution

The Binomial distribution models the following, basic statistical experiment: (1) toss a coin that has a probability \(p\) for Heads; repeat the experiment \(n\) times. The distribution models the number of “successes” (e.g. obtaining Heads) in \(n\) repeated tosses; each coin toss is known as a Bernoulli trial - and constitutes an even more elementary statistical experiment on its own.

The probability of obtaining \(k\) successes (conventionally: Heads) with probability \(p\) from \(n\) trials is given by:

\[{P(X=k;n,k)} = {{n}\choose{k}}p^{k}(1-p)^{n-k}\] where

\[{{n}\choose{k}} = \frac{n!}{k!(n-k)!}\] is the binomial coefficient.

Consider the following experiment: a person rolls a fair dice ten times. Q: What is the probability of obtaining five or less sixes at random?

We know that R’s dbinom() represents the binomial probability mass function (p.m.f.). Let’s see: the probability of getting exactly five sixes at random is:

pFiveSixes <- dbinom(5, size = 10, p = 1/6)
pFiveSixes
[1] 0.01302381

Do not be confused by our attempt to model dice rolls by a binomial distribution: in fact, there are only two outcomes here, “6 is obtained” with \(p = 1/6\) and “everything else” with \(1-p = 5/6\)!

Then, the probability of getting five or less than five sixes from ten statistical experiments is:

pFiveAndLessSixes <- sum(
  dbinom(0, size = 10, p = 1/6),
  dbinom(1, size = 10, p = 1/6),
  dbinom(2, size = 10, p = 1/6),
  dbinom(3, size = 10, p = 1/6),
  dbinom(4, size = 10, p = 1/6),
  dbinom(5, size = 10, p = 1/6)
)
pFiveAndLessSixes
[1] 0.9975618

in order to remind ourselves that the probabilities of all outcomes from a discrete probability distribution - in our case, that “0 sixes”, “1 six”, “2 sixes”, “3 sixes”, “4 sixes”, or “5 sixes” etc. obtain - will eventually sum up to one. However, let’s wrap this up elegantly by using sapply()

pFiveAndLessSixes <- sum(sapply(seq(0,5), function(x) {
  dbinom(x, size = 10, p = 1/6)
}))
pFiveAndLessSixes
[1] 0.9975618

or, even better, by recalling that we are working with a vectorized programming language:

pFiveAndLessSixes <- sum(dbinom(seq(0,5), size = 10, p =1/6))
pFiveAndLessSixes
[1] 0.9975618

Of course, we could have used a cummulative distribution function (c.d.f) to figure out this as well:

pFiveAndLessSixes <- pbinom(5, size = 10, p = 1/6)
pFiveAndLessSixes
[1] 0.9975618

Again, do not forget: the binomial distribution models a statistical experiment with two outcomes only. In the present example, its parameter, \(p = 1/6\), has a complement of \(1-p = 5/6\), and the following semantics: either 5 comes out, OR everything else. The binomial distribution does not model dice rolls, but (fair or unfair) coin tosses. To model a dice, you need the multinomial distribution, which is the multivariate generalization of the binomial. We will cover only univariate distributions here.

3.2 Random Number Generation from the Binomial

rbinom() will provide a vector of random deviates from the Binomial distribution with the desired parameter, e.g.:

# Generate a sample of random binomial variates:
randomBinomials <- rbinom(n = 100, size = 1, p = .5)
randomBinomials
  [1] 1 0 1 1 0 0 1 1 1 1 0 0 1 1 0 1 1 1 0 1 0 1 0 0 0 1 1 1 1 1 1 1 1 0 1 0 1 0 0 1
 [41] 0 0 1 1 1 1 0 0 1 1 0 1 1 0 0 1 0 1 0 1 1 0 0 0 0 0 1 1 0 1 0 1 1 0 0 0 0 0 0 1
 [81] 0 1 1 1 0 0 0 0 0 1 1 0 1 0 1 1 0 0 0 1

Now, if each experiment encompasses 100 coin tosses:

randomBinomials <- rbinom(n = 100, size = 100, p = .5)
randomBinomials # see the difference?
  [1] 52 50 53 51 52 43 53 47 49 52 50 49 41 53 45 51 53 56 42 45 52 45 53 47 37 54 51
 [28] 41 42 48 51 51 38 50 47 48 44 51 55 40 55 48 50 47 55 46 42 62 47 53 57 47 56 57
 [55] 58 58 45 48 48 51 53 46 55 41 56 44 50 43 46 44 51 51 51 44 52 54 51 52 53 56 50
 [82] 53 54 43 50 46 52 56 53 50 49 49 50 46 48 46 52 40 53 47
randomBinomials <- rbinom(n = 100, size = 10000, p = .5)
randomBinomials
  [1] 5040 5005 4945 5027 5006 5036 5029 5044 5037 5006 4949 5076 4996 4961 4994 5032
 [17] 5041 5004 4952 4989 5010 5032 5118 4901 4973 5042 4978 4995 5065 4979 5048 5033
 [33] 4988 5018 5025 5056 5072 4927 5055 4901 4996 5047 4999 4994 5041 4956 4961 4998
 [49] 4954 5011 4891 5053 4995 4966 5007 4995 5002 5015 4962 4967 5079 4962 4960 5025
 [65] 4990 5014 5049 5022 4892 5004 5020 5011 4969 5006 5022 5076 5032 5080 4985 4982
 [81] 5081 4967 4924 4969 4954 4981 4970 4972 4971 4991 4984 4899 4983 4942 4955 4956
 [97] 5030 4997 5081 4962

Let’s plot the distribution of the previous experiment:

randomBinomialsPlot <- data.frame(success = randomBinomials)
ggplot(randomBinomialsPlot, 
       aes(x = success)) + 
  geom_histogram(binwidth = 10, 
                 fill = 'deepskyblue', 
                 color = 'deepskyblue') +
  theme_bw() + 
  theme(panel.border = element_blank())

Interpretation: we were running 100 statistical experiments, each time drawing a sample of 10000 observations of a fair coin (\(p = .5\)). And now,

randomBinomials <- rbinom(100000, size = 100000, p = .5)
randomBinomialsPlot <- data.frame(success = randomBinomials)
ggplot(randomBinomialsPlot, 
       aes(x = success)) + 
  geom_histogram(binwidth = 10, 
                 fill = 'deepskyblue', 
                 color = 'deepskyblue') +
  theme_bw() + 
  theme(panel.border = element_blank())

… we were running 10000 statistical experiments, each time drawing a sample of 100000 observations of a fair coin (\(p = .5\))

3.3 Quantile Function of the Binomial distribution

The quantile is defined as the smallest value \(x\) such that \(F(x) ≥ p\), where \(F\) is the distribution function (c.d.f.):

qbinom(p = .01, size = 100, prob = .5)
[1] 38
qbinom(p = .99, size = 100, prob = .5)
[1] 62
qbinom(p = .01, size = 200, prob = .5)
[1] 84
qbinom(p = .99, size = 200, prob = .5)
[1] 116

Similarly, we could have obtained Q1, Q3, and the median, for say n of 100 (that is size in qbinom()) and p of .5 (that is prob in qbinom()):

qbinom(p = .25, size = 100, prob = .5)
[1] 47
qbinom(p = .5, size = 100, prob = .5)
[1] 50
qbinom(p = .75, size = 100, prob = .5)
[1] 53
qbinom(p = .25, size = 1000, prob = .5)
[1] 489
qbinom(p = .5, size = 1000, prob = .5)
[1] 500
qbinom(p = .75, size = 1000, prob = .5)
[1] 511

4. The Normal Distribution: \(\mu\), \(\sigma\)

Back to school: what is the probability that a person is 185 cm tall if we draw her at random from a population with mean height = 178 cm, standard deviation = 15 cm…

4.1 The Gaussian (Normal) Distribution

p185cm <- dnorm(185, mean = 178, sd = 15) #?
p185cm
[1] 0.02385223

No, it is not 0.02385223… that is the probability density scale! The normal distribution is continuous: it doesn’t really make sense to ask for a probability of a data point from its domain. Try this: what is the probability that a person is between 180 cm and 185 cm tall if we draw a person at random from a population with mean height = 178 cm, standard deviation = 15 cm?

p180_185cm <- dnorm(185, mean = 178, sd = 15) - dnorm(180, mean = 178, sd = 15)
p180_185cm # oops...
[1] -0.002508561

Ooops. Maybe:

p180_185cm <- pnorm(185, mean = 178, sd = 15, lower.tail = T) - 
  pnorm(180, mean = 178, sd = 15, lower.tail = T)
p180_185cm # yes, that is correct: 0.1265957
[1] 0.1265957

That is correct: 0.1265957. Note the usage of dnorm() and pnorm() for density and the c.d.f. (short for: Cumulative Distribution Function) respectively, as for any other probability function in R.

Gaussian, the bell curve, the famous one (this is going to take half an hour in \(\LaTeX\) to write out):

\[f(x|\mu,\sigma^2) = \frac{1}{\sqrt{2\sigma^2\pi}}e^{-\frac{(x-\mu)^2}{2\sigma^2}}\]

(ohh…) where \(\mu\) is the distribution mean, \(\sigma^2\) its variance, and be very, very careful to notice how R’s functions for the normal distribution use \(\sigma\) - the standard deviation instead of variance:

\[\sigma = \sqrt{\frac{\sum_{i=1}^{n}(x_i-\overline{x})^2}{n-1}}\]

Let’s plot it:

empiricalNormal <- function(x) dnorm(x, mean = 178, sd = 15)
normalData <- rnorm(1000, mean = 178, sd = 15)
normalData = data.frame(data = normalData)
ggplot(normalData, aes(x = data)) +
  geom_density(colour = "black",
               fill = "aliceblue",
               alpha = .75) + 
  stat_function(fun = empiricalNormal, 
                colour = "red", 
                size = 1) + 
  theme_bw() + 
  theme(panel.border = element_blank())

Use 100,000 random deviates instead:

empiricalNormal <- function(x) dnorm(x, mean = 178, sd = 15)
normalData <- rnorm(100000, mean = 178, sd = 15)
normalData = data.frame(data = normalData)
ggplot(normalData, aes(x = data)) +
  geom_density(colour = "black",
               fill = "aliceblue",
               alpha = .75) + 
  stat_function(fun = empiricalNormal, 
                colour = "red", 
                size = 1) + 
  theme_bw() + 
  theme(panel.border = element_blank())

Now, the cumulative distribution:

normalData <- seq(120, 240)
normalData <- data.frame(data = normalData, 
                         p = pnorm(normalData, mean = 178, sd = 15))
empiricalNormalCumulative <- function(x) pnorm(x, mean = 178, sd = 15)
ggplot(normalData, aes(x = data, 
                       y = p)) + 
  geom_line(colour = "black", size = .25) + 
  geom_point(size = 1, color = "black", fill = "black") + 
  stat_function(fun = empiricalNormalCumulative, 
                colour = "red", 
                size = 1) + 
  theme_bw() + 
  theme(panel.border = element_blank())

4.2 Checking if a variable has a normal distribution

Very often, one needs to check for the normality assumption in some data: whether the data are normally distributed, or not. That might sound as an easy task to perform, especially when knowing that many statisticians have worked on the problem in the past, but in practice it turns out to be trickier than expected.

We will first use the famous Kolmogorov-Smirnov Test of normality (short: the K-S test) on the Sepal.Length variable in iris:

data(iris)
# The Kolmogorov-Smirnov Test
ksSLength <- ks.test(iris$Sepal.Length,
                     "pnorm",
                     mean(iris$Sepal.Length),
                     sd(iris$Sepal.Length),
                     alternative = "two.sided",
                     exact = NULL)
Warning in ks.test(iris$Sepal.Length, "pnorm", mean(iris$Sepal.Length),  :
  ties should not be present for the Kolmogorov-Smirnov test
ksSLength

    One-sample Kolmogorov-Smirnov test

data:  iris$Sepal.Length
D = 0.088654, p-value = 0.1891
alternative hypothesis: two-sided

In order to understand the result of the K-S test, look at the p-value in the output. We did not start the discussion of statistical estimation and hypothesis testing yet, so it is natural that you wonder what the p values is. For now, just remember: the distribution of some variable is normal according to the K-S test if the p value associated with the test statistic D (also found in the output above^^) is larger than .05.

Good. The K-S test says this is a normal distribution:

ggplot(iris,
       aes(x = Sepal.Length)) + 
  geom_histogram(binwidth = .25, fill = "darkorange", color = "black") + 
  theme_bw() + 
  theme(panel.border = element_blank())

And what do you think?

We will try to test for normality by using the Shapiro-Wilk Test (short: S-W test): it is better for small samples, but unlike K-S, it applies to the Normal distribution only.

swPLength <- shapiro.test(iris$Sepal.Length)
swPLength

    Shapiro-Wilk normality test

data:  iris$Sepal.Length
W = 0.97609, p-value = 0.01018

Note. The p-value > 0.05 implies that the distribution of the data is not significantly different from the normal distribution. Please: do not panic about these p things. All this will become clear in the sessions to follow.

More often than not, you will want to take a look at the Q-Q plot to determine whether something is any similar to a target distribution or not:

qqnorm(iris$Sepal.Length, pch = 1, frame = FALSE)
qqline(iris$Sepal.Length, col = "darkblue", lwd = 2)

What would you say now: is Sepal.Length from iris normally distributed?

Further Readings

R Markdown

R Markdown is what I have used to produce this beautiful Notebook. We will learn more about it near the end of the course, but if you already feel ready to dive deep, here’s a book: R Markdown: The Definitive Guide, Yihui Xie, J. J. Allaire, Garrett Grolemunds.


Goran S. Milovanović

DataKolektiv, 2020/21

contact:


License: GPLv3 This Notebook is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This Notebook is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this Notebook. If not, see http://www.gnu.org/licenses/.


LS0tCnRpdGxlOiBJbnRybyB0byBEYXRhIFNjaWVuY2UgKE5vbi1UZWNobmljYWwgQmFja2dyb3VuZCwgUikgLSBTZXNzaW9uMDcKYXV0aG9yOgotIG5hbWU6IEdvcmFuIFMuIE1pbG92YW5vdmnEhywgUGhECiAgYWZmaWxpYXRpb246IERhdGFLb2xla3RpdiwgQ2hpZWYgU2NpZW50aXN0ICYgT3duZXI7IERhdGEgU2NpZW50aXN0IGZvciBXaWtpZGF0YSwgV01ERQphYnN0cmFjdDogCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgY29kZV9mb2xkaW5nOiBzaG93CiAgICB0aGVtZTogc3BhY2VsYWIKICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHllcwogICAgdG9jX2RlcHRoOiA1CiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogeWVzCiAgICB0b2NfZGVwdGg6IDUKLS0tCgohW10oLi4vX2ltZy9ES19Mb2dvXzEwMC5wbmcpCgoqKioKIyBTZXNzaW9uIDA3OiBJbnRyb2R1Y3Rpb24gdG8gUHJvYmFiaWxpdHkgVGhlb3J5IGluIFIuIFJhbmRvbSBWYXJpYWJsZXMgKyBQcm9iYWJpbGl0eSBGdW5jdGlvbnMKIAoqKkZlZWRiYWNrKiogc2hvdWxkIGJlIHNlbmQgdG8gYGdvcmFuLm1pbG92YW5vdmljQGRhdGFrb2xla3Rpdi5jb21gLiAKVGhlc2Ugbm90ZWJvb2tzIGFjY29tcGFueSB0aGUgSW50cm8gdG8gRGF0YSBTY2llbmNlOiBOb24tVGVjaG5pY2FsIEJhY2tncm91bmQgY291cnNlIDIwMjAvMjEuCgoqKioKCiMjIyBXaGF0IGRvIHdlIHdhbnQgdG8gZG8gdG9kYXk/CgpXZSBhcmUgZW50ZXJpbmcgdGhlIGNvcmUgcGFydCBvZiB0aGUgY291cnNlIHdpdGggYW4gaW50cm9kdWN0aW9uIHRvIFByb2JhYmlsaXR5IFRoZW9yeSBhbmQgTWF0aGVtYXRpY2FsIFN0YXRpc3RpY3MgaW4gUiEgSW4gdGhpcyBTZXNzaW9uLCB3ZSB3aWxsIGxlYXJuIGFib3V0IHByb2JhYmlsaXR5IGFuZCAqcHJvYmFiaWxpdHkgZnVuY3Rpb25zKjogd2h5IGFyZSAqZGlzY3JldGUqIGFuZCAqY29udGludW91cyByYW5kb20gb3V0Y29tZXMqIGRpZmZlcmVudCwgd2hhdCBpcyBhICpzdGF0aXN0aWNhbCBleHBlcmltZW50KiwgaG93IHRvIHBlcmZvcm0gKm51bWVyaWNhbCBzaW11bGF0aW9ucyogb2Ygc3RhdGlzdGljYWwgZXhwZXJpbWVudHMgaW4gUiwgYW5kIHdoYXQgaXMgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgKlByb2JhYmlsaXR5IERlbnNpdHkgRnVuY3Rpb24gKHBkZikqLCB0aGUgKlByb2JhYmlsaXR5IE1hc3MgRnVuY3Rpb24gKHBtZikqLCBhbmQgdGhlICpDdW11bGF0aXZlIERpc3RyaWJ1dGlvbiBGdW5jdGlvbiAoY2RmKSouLi4gVGhlbiB3ZSBpbnRyb2R1Y2UgaW1wb3J0YW50IHR3byBpbXBvcnRhbnQgcHJvYmFiaWxpdHkgZGlzdHJpYnV0aW9uczogQmlub21pYWwgKGRpc2NyZXRlKSBhbmQgTm9ybWFsIChpLmUuIEdhdXNzaWFuLCBjb250aW51b3VzKS4KCgojIyMgMC4gUHJlcmVxdWlzaXRzCgpgYGB7ciBlY2hvID0gVCwgbWVzc2FnZSA9IEYsIHdhcm5pbmcgPSBGfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKc2V0LnNlZWQoOTk5OSkKYGBgCgpUaGUgYHNldC5zZWVkKDk5OTkpYCBjb2RlIHdpbGwgZW5zdXJlIHRoZSByZXByb2R1Y2liaWxpdHkgb2YgdGhlIG51bWVyaWNhbCBzaW11bGF0aW9ucyB0aGF0IHdpbGwgYmUgcnVuIGhlcmU7IHRvIGJlIGV4cGxhaW5lZCBpbiBvdXIgc2Vzc2lvbi4KCiMjIyAxLiBQcm9iYWJpbGl0eTogVGhlb3JldGljYWwgYW5kIEV4cGVyaW1lbnRhbAoKSW1hZ2luZSB3ZSB0b3NzIGEgZmFpciBjb2luIHRlbiB0aW1lcyBhbmQgd3JpdGUgZG93biB0aGUgb3V0Y29tZXMgYXMgYChIKWVhZGAgb3IgYChUKWFpbGAgZWFjaCB0aW1lOgoKYGBge3IgZWNobyA9IFR9CnRvc3NlcyA8LSBjKCdIJywgJ0gnLCAnVCcsICdUJywgJ0gnLCAnVCcsICdUJywgJ0gnLCAnVCcsICdUJykKdGFibGUodG9zc2VzKQpgYGAKCkluIHRoaXMgKnN0YXRpc3RpY2FsIGV4cGVyaW1lbnQqIEkgaGF2ZSBtYWRlIHVwIHRoZSByZXN1bHRzLiBJIHRvbGQgeW91IHRoYXQgd2Ugd2lsbCBiZSB0b3NzaW5nIGEgKmZhaXIqIGNvaW46IHRoZSBvbmUgd2l0aCBhbiBlcXVhbCBwcm9iYWJpbGl0eSAoc2F5OiA1MC81MCwgZm9yIG5vdykgdG8gcmVzdWx0IGluIGBIYCBvciBgVGAuIEhvd2V2ZXIsIHdoYXQgd2UgaGF2ZSBvYnNlcnZlZCBpcyBtYXliZSBhIGJpdCB1bnVzdWFsOiBmb3VyIGBIYCBhbmQgc2l4IGBUYC4gTm93LCBpcyB0aGF0IGEgZmFpciBjb2luPyBUaGUgYW5zd2VyIGlzOiBpdCBzdGlsbCBtaWdodCBiZS4gCgpGcm9tIG15IHN0YXRpc3RpY2FsIGV4cGVyaW1lbnQsIHdoYXQgaXMgdGhlIHByb2JhYmlsaXR5IG9mIG9ic2VydmluZyBgSGAgLSBgUChIKWAgLSBhbmQgdGhlIHByb2JhYmlsaXR5IG9mIG9ic2VydmluZyBgVGAgLSBgUChUKWA/CgpgYGB7ciBlY2hvID0gVH0KdGFibGUodG9zc2VzKS9sZW5ndGgodG9zc2VzKQpgYGAKClllcy4gRnJvbSBvdXIgb2JzZXJ2YXRpb25zLCB3ZSAqZXN0aW1hdGUqIGBQKEgpYCB0byBiZSBgLjRgIGFuZCBgUChUKWAgdG8gYmUgYC42YC4gSG93ZXZlci4uLiB3ZSBoYXZlICphc3N1bWVkKiBhbHJlYWR5IHRoYXQgdGhlIGNvaW4gaXMgZmFpci4gTGV0J3MgdHJ5IHRvIGJ1aWxkIHN0YXRpc3RpY2FsIGV4cGVyaW1lbnRzIGJ5IHJlbHlpbmcgb24gdGhhdCBhc3N1bXB0aW9uLgoKYGBge3IgZWNobyA9IFR9CnRvc3NlcyA8LSBzYW1wbGUoYygnSCcsICdUJyksIHNpemUgPSAxMCwgcmVwbGFjZSA9IFRSVUUsIHByb2IgPSBjKC41LCAuNSkpCnRhYmxlKHRvc3NlcykKYGBgCgpgYGB7ciBlY2hvID0gVH0KdG9zc2VzIDwtIHNhbXBsZShjKCdIJywgJ1QnKSwgc2l6ZSA9IDEwLCByZXBsYWNlID0gVFJVRSwgcHJvYiA9IGMoLjUsIC41KSkKdGFibGUodG9zc2VzKQpgYGAKCmBgYHtyIGVjaG8gPSBUfQp0b3NzZXMgPC0gc2FtcGxlKGMoJ0gnLCAnVCcpLCBzaXplID0gMTAsIHJlcGxhY2UgPSBUUlVFLCBwcm9iID0gYyguNSwgLjUpKQp0YWJsZSh0b3NzZXMpCmBgYAoKIyMjIyAxLiBBIGNvaW4sIGZhaXIgb3Igbm90CgpXYWl0IC0gYHNhbXBsZSgpYCBkb2VzIG5vdCBhbHdheXMgcmV0dXJuIHRoZSBzYW1lIHJlc3VsdD8gTm8sIG9mIGNvdXJzZSBub3Q6IHRoZSBzdG9yeSBvZiAqcmFuZG9tIHZhcmlhYmxlcyogYW5kICpyYW5kb20gc2FtcGxpbmcqIGJlZ2lucyEgV2hhdCB3ZSB3aWxsIGRvIG5leHQgaXMgdG8gcGVyZm9ybSBhIGxhcmdlIG51bWJlciAtIHNheSAxMDAwMCAtIG9mIGlkZW50aWNhbCBzdGF0aXN0aWNhbCBleHBlcmltZW50cyBvZiB0aGUgZm9sbG93aW5nIGZvcm06IGVhY2ggdGltZSB3ZSB0b3NzIGEgZmFpciBjb2luIC0gZGVmaW5lZCBieSB0aGUgdmFsdWUgb2YgdGhlIGBwcm9iID0gYyguNSwgLjUpYCBhcmd1bWVudCBpbiBvdXIgYHNhbXBsZSgpYCBjYWxsIC0gYW5kIHJlY29yZCBob3cgbWFueSB0aW1lcyB3ZSBvYnNlcnZlIGBIYCBvciBgVGA6CgpgYGB7ciBlY2hvID0gVH0KZG9tYWluIDwtIGMoJ0gnLCAnVCcpCnRyaWFscyA9IDEwCmRpc3RyaWJ1dGlvbiA8LSBjKC41LCAuNSkKZXhwZXJpbWVudCA8LSBsYXBwbHkoMToxMDAwMCwgZnVuY3Rpb24oeCkgewogIHNhbXBsZSh4ID0gZG9tYWluLAogICAgICAgICBzaXplID0gdHJpYWxzLAogICAgICAgICByZXBsYWNlID0gVCwKICAgICAgICAgcHJvYiA9IGRpc3RyaWJ1dGlvbikKICAKfSkKZXhwZXJpbWVudCA8LSBSZWR1Y2UocmJpbmQsIGV4cGVyaW1lbnQpCmNvbG5hbWVzKGV4cGVyaW1lbnQpIDwtIHBhc3RlMCgndF8nLCAxOmRpbShleHBlcmltZW50KVsyXSkKcm93bmFtZXMoZXhwZXJpbWVudCkgPC0gcGFzdGUwKCdleHBfJywgMTpkaW0oZXhwZXJpbWVudClbMV0pCmhlYWQoZXhwZXJpbWVudCwgMjApCmBgYAoKTGV0J3MgZXN0aW1hdGUgYFAoSClgIGFuZCBgUChUKWAgZnJvbSB0aGUgYGV4cGVyaW1lbnRgIG1hdHJpeCBmcm9tIGVhY2ggb2YgdGhlIDEwMDAwIHN0YXRpc3RpY2FsIGV4cGVyaW1lbnRzOgoKYGBge3IgZWNobyA9IFR9CnByb2JhYmlsaXR5IDwtIGFwcGx5KGV4cGVyaW1lbnQsIDEsIHRhYmxlKQpoZWFkKHByb2JhYmlsaXR5KQpgYGAKT2s6CgpgYGB7ciBlY2hvID0gVH0KcHJvYmFiaWxpdHkgPC0gc2FwcGx5KHByb2JhYmlsaXR5LCBmdW5jdGlvbih4KSB7CiAgeC9zdW0oeCkKfSkKaGVhZChwcm9iYWJpbGl0eSkKYGBgCk5vdzoKCmBgYHtyIGVjaG8gPSBUfQpwcm9iYWJpbGl0eSA8LSBhcy5kYXRhLmZyYW1lKHB1cnJyOjpyZWR1Y2UocHJvYmFiaWxpdHksIHJiaW5kKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCmhlYWQocHJvYmFiaWxpdHkpCmBgYAoKKipOb3RlLioqIEkgaGF2ZSB1c2VkIHRoZSBgcmVkdWNlKClgIGZ1bmN0aW9uIGZyb20gYHtwdXJycn1gIGluIHBsYWNlIG9mIGJhc2UgUiBgUmVkdWNlKClgOyBge3B1cnJyfWAgaXMgYSBwYXJ0IG9mIGB7dGlkeXZlcnNlfWAuIFRoZSBtb3RpdmF0aW9uIHRvIHVzZSB0aGUgYHtwdXJycn1gIHZlcnNpb25zIG9mIHNvbWUgYmFzaWMgRnVuY3Rpb25hbCBQcm9ncmFtbWluZyBmdW5jdGlvbnMgaW4gUiBpcyBuaWNlbHkgZXhwbGFpbmVkIGluIHRoZSBmb2xsb3dpbmcgYmxvZyBwb3N0OiBbVE8gUFVSUlIgT1IgTk9UIFRPIFBVUlJSXShodHRwczovL3d3dy5tYW5nby1zb2x1dGlvbnMuY29tL3RvLXB1cnJyLW9yLW5vdC10by1wdXJyci8pLgoKQW5kIG5vdyB3ZSB3b3VsZCB3YW50IHRvIHBsb3QgdGhlIGhpc3RvZ3JhbXMgb2YgdGhlIGBIYCBhbmQgYFRgIHZhbHVlczoKCmBgYHtyIGVjaG8gPSBUfQpwSCA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKHByb2JhYmlsaXR5JEgpLCAKICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKcEgkUmVzdWx0IDwtICdIJwpwVCA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKHByb2JhYmlsaXR5JFQpLCAKICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKcFQkUmVzdWx0IDwtICdUJwpwcm9iYWJpbGl0eURpc3RyaWJ1dGlvbiA8LSByYmluZChwSCwgcFQpCmNvbG5hbWVzKHByb2JhYmlsaXR5RGlzdHJpYnV0aW9uKSA8LSBjKCdFc3RpbWF0ZScsICdGcmVxdWVuY3knLCAnUmVzdWx0JykKYGBgCgpGaW5hbGx5OgoKYGBge3IgZWNobyA9IFQsIG1lc3NhZ2UgPSBGLCB3YXJuaW5nID0gRn0KZ2dwbG90KHByb2JhYmlsaXR5RGlzdHJpYnV0aW9uLCAKICAgICAgIGFlcyh4ID0gRXN0aW1hdGUsIHkgPSBGcmVxdWVuY3ksIAogICAgICAgICAgIGNvbG9yID0gUmVzdWx0LCAKICAgICAgICAgICBmaWxsID0gUmVzdWx0KSkgKyAKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKyAKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygnY2FkZXRibHVlNCcsICdjYWRldGJsdWUyJykpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCdjYWRldGJsdWU0JywgJ2NhZGV0Ymx1ZTInKSkgKyAKICBmYWNldF93cmFwKH5SZXN1bHQpICsgCiAgdGhlbWVfYncoKSArIAogIHRoZW1lKHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICB0aGVtZShzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKYGBgCioqUS4qKiBJcyB0aGUgY29pbiBmYWlyIG9yIG5vdD8KCldoYXQgYXJlICp0aGUgbW9zdCBmcmVxdWVudGx5IG9ic2VydmVkKiBlc3RpbWF0ZXMgb2YgYFAoSClgIGFuZCBgUChUKWAgaW4gdGhlc2UgdHdvIGRpc3RyaWJ1dGlvbnM/CgpgYGB7ciBlY2hvID0gVCwgbWVzc2FnZSA9IEYsIHdhcm5pbmcgPSBGfQpwcm9iYWJpbGl0eURpc3RyaWJ1dGlvbiAlPiUgCiAgZHBseXI6OnNlbGVjdChFc3RpbWF0ZSwgRnJlcXVlbmN5LCBSZXN1bHQpICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkoUmVzdWx0KSAlPiUgCiAgZHBseXI6OnN1bW1hcmlzZShtb2RlID0gRXN0aW1hdGVbd2hpY2gubWF4KEZyZXF1ZW5jeSldKQpgYGAKCldoYXQgYXJlICp0aGUgbWVhbiogZXN0aW1hdGVzIG9mIGBQKEgpYCBhbmQgYFAoVClgIGluIHRoZXNlIHR3byBkaXN0cmlidXRpb25zPwoKYGBge3IgZWNobyA9IFQsIG1lc3NhZ2UgPSBGLCB3YXJuaW5nID0gRn0KcHJvYmFiaWxpdHlEaXN0cmlidXRpb24gJT4lIAogIGRwbHlyOjpzZWxlY3QoRXN0aW1hdGUsIEZyZXF1ZW5jeSwgUmVzdWx0KSAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KFJlc3VsdCkgJT4lIAogIGRwbHlyOjpzdW1tYXJpc2UobWVhbiA9IHN1bShhcy5udW1lcmljKEVzdGltYXRlKSpGcmVxdWVuY3kpL3N1bShGcmVxdWVuY3kpKQpgYGAKCldlbGwgaXQgZG9lcyBsb29rIGxpa2UgYSBmYWlyIGNvaW4gaW4gdGhlIGVuZC4gCgpMZXQncyBwbGF5IHRoZSBmb2xsb3dpbmcgZ2FtZTogCgotIHdlIHJvbGUgb3V0IH4yMCwwMDAgc3RhdGlzdGljYWwgZXhwZXJpbWVudHMgd2l0aCBhIGZhaXIgY29pbiwgCi0gZWFjaCB0aW1lIGluY3JlYXNpbmcgdGhlIG51bWJlciBvZiBjb2luIHRvc3NlcyBhcyAxMDoyMDAwMDsKLSBlYWNoIHRpbWUgd2UgcGVyZm9ybSBhbiBleHBlcmltZW50LCB3ZSBjb21wdXRlIHRoZSBtZWFuIG9mIHRoZSBvYnNlcnZhdGlvbnMsCi0gd2hpY2ggd2Ugd2lsbCBjb2RlIGFzIGAxYCBmb3IgYEhgIGFuZCBgMGAgZm9yIGBUYDoKCmBgYHtyIGVjaG8gPSBUfQpzYW1wbGVfc2l6ZXMgPC0gMTA6MjAwMDAKbWVhblByb2JzIDwtIHNhcHBseShzYW1wbGVfc2l6ZXMsIGZ1bmN0aW9uKHkpIHsKICBzIDwtIHNhbXBsZSh4ID0gYygwLCAxKSwgc2l6ZSA9IHksIHJlcGxhY2UgPSBUUlVFLCBwcm9iID0gcmVwKC41LCAyKSkKICByZXR1cm4obWVhbihzKSkKfSkKbWVhblByb2JzUGxvdCA8LSBhcy5kYXRhLmZyYW1lKG1lYW5Qcm9icykKY29sbmFtZXMobWVhblByb2JzUGxvdCkgPC0gIlByb2JhYmlsaXR5IgpnZ3Bsb3QobWVhblByb2JzUGxvdCwKICAgICAgIGFlcyh4ID0gUHJvYmFiaWxpdHkpKSArIAogIGdlb21faGlzdG9ncmFtKGJpbnMgPSA1MDAsIGZpbGwgPSAicmVkIikgKyAKICB0aGVtZV9idygpICsgCiAgdGhlbWUocGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpKQpgYGAKCioqUS4qKiBXaGF0IGlzIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gZWFjaCBtZWFuIGluIGBtZWFuUHJvYnNgIGFuZCB0aGUgdmFsdWUgb2YgYHAgPSAuNWA/CgpgYGB7ciBlY2hvID0gVH0KZGlmZnMgPC0gLjUgLSBtZWFuUHJvYnMKZGlmZnMgPC0gYXMuZGF0YS5mcmFtZShkaWZmcykKZGlmZnMkc2FtcGxlX3NpemUgPC0gc2FtcGxlX3NpemVzCmdncGxvdChkaWZmcywgCiAgICAgICBhZXMoeCA9IHNhbXBsZV9zaXplLCAKICAgICAgICAgICB5ID0gZGlmZnMpKSArIAogIGdlb21fbGluZShjb2xvciA9ICJjYWRldGJsdWUyIiwgc2l6ZSA9IC4yNSkgKyAKICB0aGVtZV9idygpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCkpICsgCiAgdGhlbWUocGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpKQpgYGAKSW50ZXJlc3RpbmcuICoqUS4qKiBEbyB5b3UgYmVsaWV2ZSBpbiBjb21wdXRlciBnZW5lcmF0ZWQgcmFuZG9tIG51bWJlcnM/CgpMZXQncyBwbGF5IHRoZSBmb2xsb3dpbmcgZ2FtZTogCgotIHdlIHJvbGUgb3V0IH4yMCwwMDAgc3RhdGlzdGljYWwgZXhwZXJpbWVudHMgd2l0aCBhbiAqKnVuZmFpciBjb2luKiosCi0gdGhlIG9uZSB3aGljaCByZXN1bHRzIGluIGAxYCB3aXRoIGBwID0gLjdgIGFuZCBgMGAgd2l0aCBgcCA9IC4zYCwKLSBlYWNoIHRpbWUgaW5jcmVhc2luZyB0aGUgbnVtYmVyIG9mIGNvaW4gdG9zc2VzIGFzIDEwOjIwMDAwOwotIGVhY2ggdGltZSB3ZSBwZXJmb3JtIGFuIGV4cGVyaW1lbnQsIHdlIGNvbXB1dGUgdGhlIG1lYW4gb2YgdGhlIG9ic2VydmF0aW9ucywKLSB3aGljaCB3ZSB3aWxsIGNvZGUgYXMgYDFgIGZvciBgSGAgYW5kIGAwYCBmb3IgYFRgOgoKYGBge3IgZWNobyA9IFR9CnNhbXBsZV9zaXplcyA8LSAxMDoyMDAwMAptZWFuUHJvYnMgPC0gc2FwcGx5KHNhbXBsZV9zaXplcywgZnVuY3Rpb24oeSkgewogIHMgPC0gc2FtcGxlKHggPSBjKDAsIDEpLCBzaXplID0geSwgcmVwbGFjZSA9IFRSVUUsIHByb2IgPSBjKC4zLCAuNykpCiAgbWVhbihzKQp9KQptZWFuUHJvYnNQbG90IDwtIGFzLmRhdGEuZnJhbWUobWVhblByb2JzKQpjb2xuYW1lcyhtZWFuUHJvYnNQbG90KSA8LSAiUHJvYmFiaWxpdHkiCmdncGxvdChtZWFuUHJvYnNQbG90LAogICAgICAgYWVzKHggPSBQcm9iYWJpbGl0eSkpICsgCiAgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDUwMCwgZmlsbCA9ICJibHVlIikgKyAKICB0aGVtZV9idygpICsgCiAgdGhlbWUocGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpKQpgYGAKCioqUS4qKiBXaGF0IGlzIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gZWFjaCBtZWFuIGluIGBtZWFuUHJvYnNgIGFuZCB0aGUgdmFsdWUgb2YgYHAgPSAuNWA/CgpgYGB7ciBlY2hvID0gVH0KZGlmZnMgPC0gLjUgLSBtZWFuUHJvYnMKZGlmZnMgPC0gYXMuZGF0YS5mcmFtZShkaWZmcykKZGlmZnMkc2FtcGxlX3NpemUgPC0gc2FtcGxlX3NpemVzCmdncGxvdChkaWZmcywgCiAgICAgICBhZXMoeCA9IHNhbXBsZV9zaXplLCAKICAgICAgICAgICB5ID0gZGlmZnMpKSArIAogIGdlb21fbGluZShjb2xvciA9ICJjYWRldGJsdWU0Iiwgc2l6ZSA9IC4yNSkgKyAKICB0aGVtZV9idygpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCkpICsgCiAgdGhlbWUocGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpKQpgYGAKKipOb3RlLioqIFdoYXRjaCB0aGUgYHlgIGF4aXMgY2FyZWZ1bGx5Li4uIF5eCgoqKlEuKiogKk9oLCB3YWl0Oiogd2hhdCBpcyB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIGVhY2ggbWVhbiBpbiBgbWVhblByb2JzYCBhbmQgdGhlIHZhbHVlIG9mIGBwID0gLjdgPwoKYGBge3IgZWNobyA9IFR9CmRpZmZzIDwtIC43IC0gbWVhblByb2JzCmRpZmZzIDwtIGFzLmRhdGEuZnJhbWUoZGlmZnMpCmRpZmZzJHNhbXBsZV9zaXplIDwtIHNhbXBsZV9zaXplcwpnZ3Bsb3QoZGlmZnMsIAogICAgICAgYWVzKHggPSBzYW1wbGVfc2l6ZSwgCiAgICAgICAgICAgeSA9IGRpZmZzKSkgKyAKICBnZW9tX2xpbmUoY29sb3IgPSAiY2FkZXRibHVlNCIsIHNpemUgPSAuMjUpICsgCiAgdGhlbWVfYncoKSArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTApKSArIAogIHRoZW1lKHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSkKYGBgCgojIyMjIDIuIEEgZGljZQoKVGhlIGZvbGxvd2luZyBkb2VzIG5vdCBldmVuIG5lZWQgYW4gaW50cm9kdWN0aW9uOgoKYGBge3IgZWNobyA9IFR9CnJlc3VsdHMgPC0gc2FtcGxlKHggPSAxOjYsIHNpemUgPSAxMDAsIHJlcGxhY2UgPSBULCBwcm9iID0gcmVwKDEvNiwgNikpCnRhYmxlKHJlc3VsdHMpCmBgYApBIGZhaXIgZGljZT8gSSBkb24ndCBrbm93IChidXQuLi4gYHByb2IgPSByZXAoMS82LCA2KWApLiBMZXQnIHNlZToKCk9rOgoKLSBhIHNldCBvZiBzdGF0aXN0aWNhbCBleHBlcmltZW50cyBpcyBwZXJmb3JtZWQsIAotIGVhY2ggdGltZSB3ZSB0b3NzIGEgZmFpciBkaWNlLCAKLSBlYWNoIHRpbWUgaW5jcmVhc2luZyB0aGUgbnVtYmVyIG9mIHRvc3NlcyBhcyBgc2VxKDEwMDAsIDk2MDAsIGJ5ID0gNTAwKWAuCgpgYGB7ciBlY2hvID0gVH0KZG9tYWluIDwtIDE6NgpzYW1wbGVfc2l6ZXMgPC0gc2VxKDEwMCwgOTYwMCwgYnkgPSA1MDApCnByb2JzID0gcmVwKDEvNiwgNikKcmVzdWx0cyA8LSBsYXBwbHkoc2FtcGxlX3NpemVzLCBmdW5jdGlvbih5KSB7CiAgcyA8LSBzYW1wbGUoeCA9IGRvbWFpbiwgc2l6ZSA9IHksIHJlcGxhY2UgPSBULCBwcm9iID0gcHJvYnMpCiAgcyA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKHMpKQogIGNvbG5hbWVzKHMpIDwtIGMoJ091dGNvbWUnLCAnRnJlcXVlbmN5JykKICBzJHNhbXBsZV9zaXplIDwtIHkKICByZXR1cm4ocykKfSkKcmVzdWx0cyA8LSByZWR1Y2UocmVzdWx0cywgcmJpbmQpCnByaW50KHJlc3VsdHMpCmBgYAoKYGBge3IgZWNobyA9IFR9CmdncGxvdChyZXN1bHRzLCAKICAgICAgIGFlcyh4ID0gT3V0Y29tZSwgCiAgICAgICAgICAgeSA9IEZyZXF1ZW5jeSwpKSArIAogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCAKICAgICAgICAgICBmaWxsID0gImJsYWNrIiwgCiAgICAgICAgICAgY29sb3IgPSAiYmxhY2siLCAKICAgICAgICAgICB3aWR0aCA9IC41KSArIAogIGZhY2V0X3dyYXAofnNhbXBsZV9zaXplLCBuY29sID0gNSwgc2NhbGVzID0gImZyZWUiKSArIAogIHlsYWIoIlByb2JhYmlsaXR5IikgKyAKICB0aGVtZV9idygpICsgCiAgdGhlbWUocGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgpKQpgYGAKKipOb3RlLioqIEluIHRoZSBwcmV2aW91cyBjb2luIHRvc3NpbmcgZXhwZXJpbWVudHMgd2UgaGF2ZSB2YXJpZWQgdHdvIHRoaW5nczogKipwKiosIHRoZSBwcm9iYWJpbGl0eSBvZiBvYnRhaW5pbmcgYEhgIG9yIGBUYCwgYW5kIHRoZSBudW1iZXIgb2YgY29pbiB0b3NzZXMgaW4gdGhlIGV4cGVyaW1lbnQsIHdoaWNoIHdlIGNhbiB0ZXJtOiAqKm4qKi4gTm93LCBvYnNlcnZlIGhvdyBgUChIKSA9IDEgLSBQKFQpYCwgc2ltcGx5IGJlY2F1c2UgYSBjb2luIGFsd2F5cyB0dXJucyBlaXRoZXIgYEhgIG9yIGBUYC4gU28gd2UgYXJlIG5vdCB3b3JraW5nIHdpdGggdHdvIHByb2JhYmlsaXRpZXMsIGBQKEgpYCBhbmQgYFAoVClgLCByZWFsbHksIGJ1dCB3aXRoIG9ubHkgb25lOiAqKnAqKiwgYW5kIHNheSB0aGF0ICoqcCoqIHN0YW5kcyBmb3IgYFAoSClgIGJlY2F1c2Ugd2UgY2FuIGFsd2F5cyBrbm93IGBQKFQpYCBpZiB3ZSBrbm93IHRoZSBmb3JtZXIuIEluIG90aGVyIHdvcmRzLCB0aGVyZSBhcmUgdHdvIG51bWJlcnMgdGhhdCB3ZSBjYW4gdXNlIHRvIGRlc2NyaWJlIGVhY2ggc3RhdGlzdGljYWwgZXhwZXJpbWVudCB0aGF0IGVuY29tcGFzc2VzIGNvaW4gdG9zc2VzOiAqKnAqKiwgYW5kICoqbioqLiBSZW1lbWJlciB0aGVtIHdlbGwuCgojIyMgMi4gTWVhbiwgVmFyaWFuY2UsIGFuZCBTdGFuZGFyZCBEZXZpYXRpb24KCldlIGFscmVhZHkga25vdyBhYm91dCB0aGUgKm1lYW4qIC0gb3IgdGhlICpleHBlY3RlZCB2YWx1ZSosIGFzIGl0IGlzIGFsc28gY2FsbGVkIC0gb2YgYSBzZXQgb2Ygb2JzZXJ2YXRpb25zOgoKYGBge3IgZWNobyA9IFR9Cm9ic2VydmF0aW9ucyA8LSBjKDUuNSwgMS45LCA3LjMsIDQuNCwgNi4yLCA2LjUsIDIsIDguMywgLjEsIDUuNywgMS4zKQptZWFuT2JzZXJ2YXRpb25zIDwtIHN1bShvYnNlcnZhdGlvbnMpL2xlbmd0aChvYnNlcnZhdGlvbnMpCm1lYW5PYnNlcnZhdGlvbnMgPT0gbWVhbihvYnNlcnZhdGlvbnMpCmBgYAoKIyMjIyAyLjEgQ2VudHJhbCBUZW5kZW5jeSBhbmQgRGlzcGVyc2lvbgoKKk1lYW4qLCAqbWVkaWFuKiAob3IgdGhlIDUwdGggcGVyY2VudGlsZSksIGFuZCAqbW9kZSogKHRoZSBtb3N0IGZyZXF1ZW50bHkgb2JzZXJ2ZWQgdmFsdWUgaW4gYSBzZXQgb2Ygb2JzZXJ2YXRpb25zKSBhcmUgYWxsICptZWFzdXJlcyBvZiBjZW50cmFsIHRlbmRlbmN5KiBpbiBzdGF0aXN0aWNzLiBXZSBoYXZlIGFsc28gbGVhcm5lZCBhYm91dCBzb21lICptZWFzdXJlcyBvZiBkaXNwZXJzaW9uKiBhbHJlYWR5LCB3aXRob3V0IGV2ZW4gaW50cm9kdWNpbmcgdGhlIHRlcm0uIEZvciBleGFtcGxlLCBpbiBTZXNzaW9uMDYgb24gRURBIHdlIGhhdmUgdXNlZCB0aGUgKkludGVycXVhcnRpbGUgUmFuZ2UqLCBgSVFSYCwgd2hpY2ggaXMgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgM3JkIGFuZCB0aGUgMXN0IHF1YXJ0aWxlIGluIHRoZSBkYXRhLCBhcyB3ZWxsIGFzIHRoZSAqcmFuZ2UqOiB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSBtYXhpbXVtIGFuZCBtaW5pbXVtIG9mIHRoZSBvYnNlcnZhdGlvbnMuIE5vdyB3ZSBpbnRyb2R1Y2UgdHdvIG5ldyBtZWFzdXJlcyBvZiBkaXNwZXJzaW9uOiB0aGUgKnZhcmlhbmNlKiBhbmQgKnN0YW5kYXJkIGRldmlhdGlvbiouCgpgYGB7ciBlY2hvID0gVH0Kb2JzZXJ2YXRpb25zIDwtIGMoNS41LCAxLjksIDcuMywgNC40LCA2LjIsIDYuNSwgMiwgOC4zLCAuMSwgNS43LCAxLjMpCm4gPC0gbGVuZ3RoKG9ic2VydmF0aW9ucykKbWVhbk9ic2VydmF0aW9ucyA8LSBtZWFuKG9ic2VydmF0aW9ucykKc3F1YXJlZF9yZXNpZHVhbHMgPC0gKG9ic2VydmF0aW9ucyAtIG1lYW4ob2JzZXJ2YXRpb25zKSleMgp2YXJpYW5jZU9ic2VydmF0aW9ucyA8LSBzdW0oc3F1YXJlZF9yZXNpZHVhbHMpLyhuLTEpCnByaW50KHZhcmlhbmNlT2JzZXJ2YXRpb25zKQpgYGAKCkFuZCBvZiBjb3Vyc2UgdGhlcmUgaXMgdGhlIGB2YXIoKWAgZnVuY3Rpb24gaW4gYmFzZSBSOgoKYGBge3IgZWNobyA9IFR9CnZhcihvYnNlcnZhdGlvbnMpCmBgYAoKQWdhaW46IGhvdyBpcyB2YXJpYW5jZSBjb21wdXRlZD8KCiQkXHNpZ21hXjIgPSBcZnJhY3tcc3VtX3tpPTF9XntOfSh4X2kgLSBcb3ZlcmxpbmV7eH0pXjJ9e04tMX0kJApBbmQgd2UgYWxzbyB1c2UgdGhlIHNxdWFyZSByb290IG9mIHRoZSB2YXJpYW5jZSwgKnN0YW5kYXJkIGRldmlhdGlvbiosIHRvIGRlc2NyaWJlIHRoZSBkaXNwZXJzaW9uIG9mIG1lYXN1cmVtZW50czoKCiQkXHNpZ21hID0gXHNxcnR7XGZyYWN7XHN1bV97aT0xfV57Tn0oeF9pIC0gXG92ZXJsaW5le3h9KV4yfXtOLTF9fSQkCgpBIGhhbmR5IFIgZnVuY3Rpb24gYHNkKClgIGRvZXMgZXhhY3RseSB0aGF0OgoKYGBge3IgZWNobyA9IFR9CnNkKG9ic2VydmF0aW9ucykKYGBgCgojIyMjIDIuMiBEZXNjcmlwdGl2ZSBzdGF0aXN0aWNzIGZvciBgbXRjYXJzYAoKTm93IHdoZW4gd2Uga25vdyB0aGlzLCB3ZSBjYW4gY29tcGxldGUgdGhlIGRlc2NyaXB0aXZlIHN0YXRpc3RpY3Mgb2YgYG10Y2Fyc2AgdGhhdCB3ZSBoYXZlIHVzZWQgaW4gU2Vzc2lvbjA2IG9uIEVEQSwganVzdCBmb3IgYW4gZXhlcmNpc2U6CgpgYGB7ciBlY2hvID0gVH0KZGF0YShtdGNhcnMpCmRlc2NyaXB0aXZlcyA8LSBtdGNhcnMgJT4lCiAgc3VtbWFyaXNlKGFjcm9zcyguY29scyA9IGV2ZXJ5dGhpbmcoKSwKICAgICAgICAgICAgICAgICAgIC5mbnMgPSBsaXN0KG1lYW4gPSBtZWFuLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWVkaWFuID0gbWVkaWFuLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pbiA9IG1pbiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXggPSBtYXgsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmFuZ2UgPSB+IG1heCgueCkgLSBtaW4oLngpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSVFSID0gSVFSLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhciA9IHZhciwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdGRkZXYgPSBzZCkpCiAgICAgICAgICAgICkKcHJpbnQoZGVzY3JpcHRpdmVzKQpgYGAKCldlIG5lZWQgdG8gdGlkeSB1cCwgb2J2aW91c2x5OgoKYGBge3IgZWNobyA9IFR9CmRlc2NyaXB0aXZlcyA8LSBhcy5kYXRhLmZyYW1lKHQoZGVzY3JpcHRpdmVzKSkKY29sbmFtZXMoZGVzY3JpcHRpdmVzKSA8LSAndmFsdWUnCmRlc2NyaXB0aXZlcyRtZWFzdXJlbWVudCA8LSByb3duYW1lcyhkZXNjcmlwdGl2ZXMpCmhlYWQoZGVzY3JpcHRpdmVzKQpgYGAKCmBgYHtyIGVjaG8gPSBUfQpkZXNjcmlwdGl2ZXMgPC0gZGVzY3JpcHRpdmVzICU+JSAKICB0aWR5cjo6c2VwYXJhdGUobWVhc3VyZW1lbnQsCiAgICAgICAgICAgICAgICAgIGludG8gPSBjKCdmZWF0dXJlJywgJ3N0YXRpc3RpYycpLAogICAgICAgICAgICAgICAgICBzZXAgPSAiXyIpCmhlYWQoZGVzY3JpcHRpdmVzKQpgYGAKCkFuZCBmaW5hbGx5OgoKYGBge3IgZWNobyA9IFR9CmRlc2NyaXB0aXZlcyA8LSBkZXNjcmlwdGl2ZXMgJT4lIAogIHRpZHlyOjpwaXZvdF93aWRlcihuYW1lc19mcm9tID0gJ3N0YXRpc3RpYycsCiAgICAgICAgICAgICAgICAgICAgIHZhbHVlc19mcm9tID0gJ3ZhbHVlJykKcHJpbnQoZGVzY3JpcHRpdmVzKQpgYGAKCiMjIyMgMi4zIFByb2JhYmlsaXR5IGFuZCBQcm9iYWJpbGl0eSBGdW5jdGlvbnM6IGFuIG92ZXJ2aWV3CgohW10oLi4vX2ltZy9TMDdfMDFfUHJvYmFiaWxpdHlGdW5jdGlvbnMuanBlZykKCiMjIyAzLiBUaGUgQmlub21pYWwgRGlzdHJpYnV0aW9uOiAqKnAqKiwgKipuKioKCiMjIyMgMy4xIEJpbm9taWFsIERpc3RyaWJ1dGlvbgoKVGhlIEJpbm9taWFsIGRpc3RyaWJ1dGlvbiBtb2RlbHMgdGhlIGZvbGxvd2luZywgYmFzaWMgc3RhdGlzdGljYWwgZXhwZXJpbWVudDogKDEpIHRvc3MgYSBjb2luIHRoYXQgaGFzIGEgcHJvYmFiaWxpdHkgJHAkIGZvciBIZWFkczsgcmVwZWF0IHRoZSBleHBlcmltZW50ICRuJCB0aW1lcy4gVGhlIGRpc3RyaWJ1dGlvbiBtb2RlbHMgdGhlIG51bWJlciBvZiAiKnN1Y2Nlc3NlcyoiIChlLmcuIG9idGFpbmluZyBIZWFkcykgaW4gJG4kIHJlcGVhdGVkIHRvc3NlczsgZWFjaCBjb2luIHRvc3MgaXMga25vd24gYXMgYSAqQmVybm91bGxpIHRyaWFsKiAtIGFuZCBjb25zdGl0dXRlcyBhbiBldmVuIG1vcmUgZWxlbWVudGFyeSBzdGF0aXN0aWNhbCBleHBlcmltZW50IG9uIGl0cyBvd24uCgpUaGUgcHJvYmFiaWxpdHkgb2Ygb2J0YWluaW5nICRrJCBzdWNjZXNzZXMgKGNvbnZlbnRpb25hbGx5OiAqSGVhZHMqKSB3aXRoIHByb2JhYmlsaXR5ICRwJCBmcm9tICRuJCB0cmlhbHMgaXMgZ2l2ZW4gYnk6CgokJHtQKFg9aztuLGspfSA9IHt7bn1cY2hvb3Nle2t9fXBee2t9KDEtcClee24ta30kJAp3aGVyZSAKCiQke3tufVxjaG9vc2V7a319ID0gXGZyYWN7biF9e2shKG4taykhfSQkCmlzIHRoZSBiaW5vbWlhbCBjb2VmZmljaWVudC4KCkNvbnNpZGVyIHRoZSBmb2xsb3dpbmcgZXhwZXJpbWVudDogYSBwZXJzb24gcm9sbHMgYSBmYWlyIGRpY2UgdGVuIHRpbWVzLiAqKlE6KiogV2hhdCBpcyB0aGUgcHJvYmFiaWxpdHkgb2Ygb2J0YWluaW5nIGZpdmUgKm9yIGxlc3MqIHNpeGVzIGF0IHJhbmRvbT8KCldlIGtub3cgdGhhdCBSJ3MgYGRiaW5vbSgpYCByZXByZXNlbnRzIHRoZSBiaW5vbWlhbCBwcm9iYWJpbGl0eSBtYXNzIGZ1bmN0aW9uIChwLm0uZi4pLiBMZXQncyBzZWU6IHRoZSBwcm9iYWJpbGl0eSBvZiBnZXR0aW5nICpleGFjdGx5KiBmaXZlIHNpeGVzIGF0IHJhbmRvbSBpczoKCmBgYCB7ciBlY2hvID0gVH0KcEZpdmVTaXhlcyA8LSBkYmlub20oNSwgc2l6ZSA9IDEwLCBwID0gMS82KQpwRml2ZVNpeGVzCmBgYAoKRG8gbm90IGJlIGNvbmZ1c2VkIGJ5IG91ciBhdHRlbXB0IHRvIG1vZGVsIGRpY2Ugcm9sbHMgYnkgYSBiaW5vbWlhbCBkaXN0cmlidXRpb246IGluIGZhY3QsIHRoZXJlIGFyZSBvbmx5IHR3byBvdXRjb21lcyBoZXJlLCAiKjYgaXMgb2J0YWluZWQqIiB3aXRoICRwID0gMS82JCBhbmQgIipldmVyeXRoaW5nIGVsc2UqIiB3aXRoICQxLXAgPSA1LzYkIQoKVGhlbiwgdGhlIHByb2JhYmlsaXR5IG9mIGdldHRpbmcgZml2ZSBvciBsZXNzIHRoYW4gZml2ZSBzaXhlcyBmcm9tIHRlbiBzdGF0aXN0aWNhbCBleHBlcmltZW50cyBpczoKCmBgYCB7ciBlY2hvID0gVH0KcEZpdmVBbmRMZXNzU2l4ZXMgPC0gc3VtKAogIGRiaW5vbSgwLCBzaXplID0gMTAsIHAgPSAxLzYpLAogIGRiaW5vbSgxLCBzaXplID0gMTAsIHAgPSAxLzYpLAogIGRiaW5vbSgyLCBzaXplID0gMTAsIHAgPSAxLzYpLAogIGRiaW5vbSgzLCBzaXplID0gMTAsIHAgPSAxLzYpLAogIGRiaW5vbSg0LCBzaXplID0gMTAsIHAgPSAxLzYpLAogIGRiaW5vbSg1LCBzaXplID0gMTAsIHAgPSAxLzYpCikKcEZpdmVBbmRMZXNzU2l4ZXMKYGBgCgppbiBvcmRlciB0byByZW1pbmQgb3Vyc2VsdmVzIHRoYXQgdGhlIHByb2JhYmlsaXRpZXMgb2YgYWxsIG91dGNvbWVzIGZyb20gYSBkaXNjcmV0ZSBwcm9iYWJpbGl0eSBkaXN0cmlidXRpb24gLSBpbiBvdXIgY2FzZSwgdGhhdCAiKjAgc2l4ZXMqIiwgIioxIHNpeCoiLCAiKjIgc2l4ZXMqIiwgIiozIHNpeGVzKiIsICIqNCBzaXhlcyoiLCBvciAiKjUgc2l4ZXMqIiBldGMuIG9idGFpbiAtIHdpbGwgZXZlbnR1YWxseSBzdW0gdXAgdG8gb25lLiBIb3dldmVyLCBsZXQncyB3cmFwIHRoaXMgdXAgZWxlZ2FudGx5IGJ5IHVzaW5nIGBzYXBwbHkoKWAKCmBgYCB7ciBlY2hvID0gVH0KcEZpdmVBbmRMZXNzU2l4ZXMgPC0gc3VtKHNhcHBseShzZXEoMCw1KSwgZnVuY3Rpb24oeCkgewogIGRiaW5vbSh4LCBzaXplID0gMTAsIHAgPSAxLzYpCn0pKQpwRml2ZUFuZExlc3NTaXhlcwpgYGAKCm9yLCBldmVuIGJldHRlciwgYnkgcmVjYWxsaW5nIHRoYXQgd2UgYXJlIHdvcmtpbmcgd2l0aCBhIHZlY3Rvcml6ZWQgcHJvZ3JhbW1pbmcgbGFuZ3VhZ2U6CgpgYGAge3IgZWNobyA9IFR9CnBGaXZlQW5kTGVzc1NpeGVzIDwtIHN1bShkYmlub20oc2VxKDAsNSksIHNpemUgPSAxMCwgcCA9MS82KSkKcEZpdmVBbmRMZXNzU2l4ZXMKYGBgCgpPZiBjb3Vyc2UsIHdlIGNvdWxkIGhhdmUgdXNlZCBhICpjdW1tdWxhdGl2ZSBkaXN0cmlidXRpb24gZnVuY3Rpb24qIChjLmQuZikgdG8gZmlndXJlIG91dCB0aGlzIGFzIHdlbGw6CgpgYGB7ciBlY2hvID0gVH0KcEZpdmVBbmRMZXNzU2l4ZXMgPC0gcGJpbm9tKDUsIHNpemUgPSAxMCwgcCA9IDEvNikKcEZpdmVBbmRMZXNzU2l4ZXMKYGBgCgpBZ2FpbiwgZG8gbm90IGZvcmdldDogdGhlIGJpbm9taWFsIGRpc3RyaWJ1dGlvbiBtb2RlbHMgYSBzdGF0aXN0aWNhbCBleHBlcmltZW50IHdpdGggdHdvIG91dGNvbWVzIG9ubHkuIEluIHRoZSBwcmVzZW50IGV4YW1wbGUsIGl0cyBwYXJhbWV0ZXIsICRwID0gMS82JCwgaGFzIGEgY29tcGxlbWVudCBvZiAkMS1wID0gNS82JCwgYW5kIHRoZSBmb2xsb3dpbmcgc2VtYW50aWNzOiBlaXRoZXIgNSBjb21lcyBvdXQsIE9SIGV2ZXJ5dGhpbmcgZWxzZS4gVGhlIGJpbm9taWFsIGRpc3RyaWJ1dGlvbiAqZG9lcyBub3QgbW9kZWwgZGljZSByb2xscyosIGJ1dCAoZmFpciBvciB1bmZhaXIpICpjb2luIHRvc3NlcyouIFRvIG1vZGVsIGEgZGljZSwgeW91IG5lZWQgdGhlICptdWx0aW5vbWlhbCBkaXN0cmlidXRpb24qLCB3aGljaCBpcyB0aGUgbXVsdGl2YXJpYXRlIGdlbmVyYWxpemF0aW9uIG9mIHRoZSBiaW5vbWlhbC4gV2Ugd2lsbCBjb3ZlciBvbmx5IHVuaXZhcmlhdGUgZGlzdHJpYnV0aW9ucyBoZXJlLgoKIyMjIyAzLjIgUmFuZG9tIE51bWJlciBHZW5lcmF0aW9uIGZyb20gdGhlIEJpbm9taWFsCgpgcmJpbm9tKClgIHdpbGwgcHJvdmlkZSBhIHZlY3RvciBvZiByYW5kb20gZGV2aWF0ZXMgZnJvbSB0aGUgQmlub21pYWwgZGlzdHJpYnV0aW9uIHdpdGggdGhlIGRlc2lyZWQgcGFyYW1ldGVyLCBlLmcuOgoKYGBgIHtyIGVjaG8gPSBUfQojIEdlbmVyYXRlIGEgc2FtcGxlIG9mIHJhbmRvbSBiaW5vbWlhbCB2YXJpYXRlczoKcmFuZG9tQmlub21pYWxzIDwtIHJiaW5vbShuID0gMTAwLCBzaXplID0gMSwgcCA9IC41KQpyYW5kb21CaW5vbWlhbHMKYGBgCgpOb3csIGlmIGVhY2ggZXhwZXJpbWVudCBlbmNvbXBhc3NlcyAxMDAgY29pbiB0b3NzZXM6CgpgYGAge3IgZWNobyA9IFR9CnJhbmRvbUJpbm9taWFscyA8LSByYmlub20obiA9IDEwMCwgc2l6ZSA9IDEwMCwgcCA9IC41KQpyYW5kb21CaW5vbWlhbHMgIyBzZWUgdGhlIGRpZmZlcmVuY2U/CmBgYAoKYGBge3IgZWNobyA9IFR9CnJhbmRvbUJpbm9taWFscyA8LSByYmlub20obiA9IDEwMCwgc2l6ZSA9IDEwMDAwLCBwID0gLjUpCnJhbmRvbUJpbm9taWFscwpgYGAKCkxldCdzIHBsb3QgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgcHJldmlvdXMgZXhwZXJpbWVudDoKCmBgYHtyIGVjaG8gPSBUfQpyYW5kb21CaW5vbWlhbHNQbG90IDwtIGRhdGEuZnJhbWUoc3VjY2VzcyA9IHJhbmRvbUJpbm9taWFscykKZ2dwbG90KHJhbmRvbUJpbm9taWFsc1Bsb3QsIAogICAgICAgYWVzKHggPSBzdWNjZXNzKSkgKyAKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDEwLCAKICAgICAgICAgICAgICAgICBmaWxsID0gJ2RlZXBza3libHVlJywgCiAgICAgICAgICAgICAgICAgY29sb3IgPSAnZGVlcHNreWJsdWUnKSArCiAgdGhlbWVfYncoKSArIAogIHRoZW1lKHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSkKYGBgCgpJbnRlcnByZXRhdGlvbjogd2Ugd2VyZSBydW5uaW5nICoqMTAwKiogc3RhdGlzdGljYWwgZXhwZXJpbWVudHMsIGVhY2ggdGltZSBkcmF3aW5nIGEgc2FtcGxlIG9mIDEwMDAwIG9ic2VydmF0aW9ucyBvZiBhIGZhaXIgY29pbiAoJHAgPSAuNSQpLiBBbmQgbm93LAoKYGBgIHtyIGVjaG8gPSBUfQpyYW5kb21CaW5vbWlhbHMgPC0gcmJpbm9tKDEwMDAwMCwgc2l6ZSA9IDEwMDAwMCwgcCA9IC41KQpyYW5kb21CaW5vbWlhbHNQbG90IDwtIGRhdGEuZnJhbWUoc3VjY2VzcyA9IHJhbmRvbUJpbm9taWFscykKZ2dwbG90KHJhbmRvbUJpbm9taWFsc1Bsb3QsIAogICAgICAgYWVzKHggPSBzdWNjZXNzKSkgKyAKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDEwLCAKICAgICAgICAgICAgICAgICBmaWxsID0gJ2RlZXBza3libHVlJywgCiAgICAgICAgICAgICAgICAgY29sb3IgPSAnZGVlcHNreWJsdWUnKSArCiAgdGhlbWVfYncoKSArIAogIHRoZW1lKHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSkKYGBgCgouLi4gd2Ugd2VyZSBydW5uaW5nICoqMTAwMDAqKiBzdGF0aXN0aWNhbCBleHBlcmltZW50cywgZWFjaCB0aW1lIGRyYXdpbmcgYSBzYW1wbGUgb2YgMTAwMDAwIG9ic2VydmF0aW9ucyBvZiBhIGZhaXIgY29pbiAoJHAgPSAuNSQpCgojIyMjIDMuMyBRdWFudGlsZSBGdW5jdGlvbiBvZiB0aGUgQmlub21pYWwgZGlzdHJpYnV0aW9uCgpUaGUgKnF1YW50aWxlKiBpcyBkZWZpbmVkIGFzIHRoZSBzbWFsbGVzdCB2YWx1ZSAkeCQgc3VjaCB0aGF0ICRGKHgpIOKJpSBwJCwgIHdoZXJlICRGJCBpcyB0aGUgZGlzdHJpYnV0aW9uIGZ1bmN0aW9uIChjLmQuZi4pOgoKYGBgIHtyIGVjaG8gPSBUfQpxYmlub20ocCA9IC4wMSwgc2l6ZSA9IDEwMCwgcHJvYiA9IC41KQpxYmlub20ocCA9IC45OSwgc2l6ZSA9IDEwMCwgcHJvYiA9IC41KQpxYmlub20ocCA9IC4wMSwgc2l6ZSA9IDIwMCwgcHJvYiA9IC41KQpxYmlub20ocCA9IC45OSwgc2l6ZSA9IDIwMCwgcHJvYiA9IC41KQpgYGAKClNpbWlsYXJseSwgd2UgY291bGQgaGF2ZSBvYnRhaW5lZCBgUTFgLCBgUTNgLCBhbmQgdGhlIG1lZGlhbiwgZm9yIHNheSAqKm4qKiBvZiAxMDAgKHRoYXQgaXMgYHNpemVgIGluIGBxYmlub20oKWApIGFuZCAqKnAqKiBvZiAuNSAodGhhdCBpcyBgcHJvYmAgaW4gYHFiaW5vbSgpYCk6CgpgYGAge3IgZWNobyA9IFR9CnFiaW5vbShwID0gLjI1LCBzaXplID0gMTAwLCBwcm9iID0gLjUpCnFiaW5vbShwID0gLjUsIHNpemUgPSAxMDAsIHByb2IgPSAuNSkKcWJpbm9tKHAgPSAuNzUsIHNpemUgPSAxMDAsIHByb2IgPSAuNSkKYGBgCgpgYGAge3IgZWNobyA9IFR9CnFiaW5vbShwID0gLjI1LCBzaXplID0gMTAwMCwgcHJvYiA9IC41KQpxYmlub20ocCA9IC41LCBzaXplID0gMTAwMCwgcHJvYiA9IC41KQpxYmlub20ocCA9IC43NSwgc2l6ZSA9IDEwMDAsIHByb2IgPSAuNSkKYGBgCgojIyMgNC4gVGhlIE5vcm1hbCBEaXN0cmlidXRpb246ICoqJFxtdSQqKiwgKiokXHNpZ21hJCoqCgpCYWNrIHRvIHNjaG9vbDogd2hhdCBpcyB0aGUgcHJvYmFiaWxpdHkgdGhhdCBhIHBlcnNvbiBpcyAxODUgY20gdGFsbCBpZiB3ZSBkcmF3IGhlciBhdCByYW5kb20gZnJvbSBhIHBvcHVsYXRpb24gd2l0aCBtZWFuIGhlaWdodCA9IDE3OCBjbSwgc3RhbmRhcmQgZGV2aWF0aW9uID0gMTUgY20uLi4KCiMjIyMgNC4xIFRoZSBHYXVzc2lhbiAoTm9ybWFsKSBEaXN0cmlidXRpb24KCmBgYCB7ciBlY2hvID0gVH0KcDE4NWNtIDwtIGRub3JtKDE4NSwgbWVhbiA9IDE3OCwgc2QgPSAxNSkgIz8KcDE4NWNtCmBgYAoKTm8sIGl0IGlzIG5vdCAwLjAyMzg1MjIzLi4uIHRoYXQgaXMgdGhlICpwcm9iYWJpbGl0eSBkZW5zaXR5KiBzY2FsZSEgVGhlIG5vcm1hbCBkaXN0cmlidXRpb24gaXMgY29udGludW91czogaXQgZG9lc24ndCByZWFsbHkgbWFrZSBzZW5zZSB0byBhc2sgZm9yIGEgcHJvYmFiaWxpdHkgb2YgYSBkYXRhIHBvaW50IGZyb20gaXRzIGRvbWFpbi4gVHJ5IHRoaXM6IHdoYXQgaXMgdGhlIHByb2JhYmlsaXR5IHRoYXQgYSBwZXJzb24gaXMgYmV0d2VlbiAxODAgY20gYW5kIDE4NSBjbSB0YWxsIGlmIHdlIGRyYXcgYSBwZXJzb24gYXQgcmFuZG9tIGZyb20gYSBwb3B1bGF0aW9uIHdpdGggbWVhbiBoZWlnaHQgPSAxNzggY20sIHN0YW5kYXJkIGRldmlhdGlvbiA9IDE1IGNtPwoKYGBgIHtyIGVjaG8gPSBUfQpwMTgwXzE4NWNtIDwtIGRub3JtKDE4NSwgbWVhbiA9IDE3OCwgc2QgPSAxNSkgLSBkbm9ybSgxODAsIG1lYW4gPSAxNzgsIHNkID0gMTUpCnAxODBfMTg1Y20gIyBvb3BzLi4uCmBgYAoKT29vcHMuIE1heWJlOgoKYGBgIHtyIGVjaG8gPSBUfQpwMTgwXzE4NWNtIDwtIHBub3JtKDE4NSwgbWVhbiA9IDE3OCwgc2QgPSAxNSwgbG93ZXIudGFpbCA9IFQpIC0gCiAgcG5vcm0oMTgwLCBtZWFuID0gMTc4LCBzZCA9IDE1LCBsb3dlci50YWlsID0gVCkKcDE4MF8xODVjbSAjIHllcywgdGhhdCBpcyBjb3JyZWN0OiAwLjEyNjU5NTcKYGBgCgpUaGF0IGlzIGNvcnJlY3Q6IDAuMTI2NTk1Ny4gTm90ZSB0aGUgdXNhZ2Ugb2YgYGRub3JtKClgIGFuZCBgcG5vcm0oKWAgZm9yIGRlbnNpdHkgYW5kIHRoZSBjLmQuZi4gKHNob3J0IGZvcjogQ3VtdWxhdGl2ZSBEaXN0cmlidXRpb24gRnVuY3Rpb24pIHJlc3BlY3RpdmVseSwgYXMgZm9yIGFueSBvdGhlciBwcm9iYWJpbGl0eSBmdW5jdGlvbiBpbiBSLgoKR2F1c3NpYW4sIHRoZSBiZWxsIGN1cnZlLCB0aGUgZmFtb3VzIG9uZSAodGhpcyBpcyBnb2luZyB0byB0YWtlIGhhbGYgYW4gaG91ciBpbiAkXExhVGVYJCB0byB3cml0ZSBvdXQpOgoKJCRmKHh8XG11LFxzaWdtYV4yKSA9IFxmcmFjezF9e1xzcXJ0ezJcc2lnbWFeMlxwaX19ZV57LVxmcmFjeyh4LVxtdSleMn17MlxzaWdtYV4yfX0kJAoKKG9oaC4uLikgd2hlcmUgJFxtdSQgaXMgdGhlIGRpc3RyaWJ1dGlvbiBtZWFuLCAkXHNpZ21hXjIkIGl0cyB2YXJpYW5jZSwgKiphbmQgYmUgdmVyeSwgdmVyeSBjYXJlZnVsIHRvIG5vdGljZSoqIGhvdyBSJ3MgZnVuY3Rpb25zIGZvciB0aGUgbm9ybWFsIGRpc3RyaWJ1dGlvbiB1c2UgJFxzaWdtYSQgLSB0aGUgKnN0YW5kYXJkIGRldmlhdGlvbiogaW5zdGVhZCBvZiB2YXJpYW5jZToKCiQkXHNpZ21hID0gXHNxcnR7XGZyYWN7XHN1bV97aT0xfV57bn0oeF9pLVxvdmVybGluZXt4fSleMn17bi0xfX0kJAoKTGV0J3MgcGxvdCBpdDogCgpgYGAge3IgZWNobyA9IFR9CmVtcGlyaWNhbE5vcm1hbCA8LSBmdW5jdGlvbih4KSBkbm9ybSh4LCBtZWFuID0gMTc4LCBzZCA9IDE1KQpub3JtYWxEYXRhIDwtIHJub3JtKDEwMDAsIG1lYW4gPSAxNzgsIHNkID0gMTUpCm5vcm1hbERhdGEgPSBkYXRhLmZyYW1lKGRhdGEgPSBub3JtYWxEYXRhKQpnZ3Bsb3Qobm9ybWFsRGF0YSwgYWVzKHggPSBkYXRhKSkgKwogIGdlb21fZGVuc2l0eShjb2xvdXIgPSAiYmxhY2siLAogICAgICAgICAgICAgICBmaWxsID0gImFsaWNlYmx1ZSIsCiAgICAgICAgICAgICAgIGFscGhhID0gLjc1KSArIAogIHN0YXRfZnVuY3Rpb24oZnVuID0gZW1waXJpY2FsTm9ybWFsLCAKICAgICAgICAgICAgICAgIGNvbG91ciA9ICJyZWQiLCAKICAgICAgICAgICAgICAgIHNpemUgPSAxKSArIAogIHRoZW1lX2J3KCkgKyAKICB0aGVtZShwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCkpCmBgYAoKVXNlIDEwMCwwMDAgcmFuZG9tIGRldmlhdGVzIGluc3RlYWQ6CgpgYGAge3IgZWNobyA9IFR9CmVtcGlyaWNhbE5vcm1hbCA8LSBmdW5jdGlvbih4KSBkbm9ybSh4LCBtZWFuID0gMTc4LCBzZCA9IDE1KQpub3JtYWxEYXRhIDwtIHJub3JtKDEwMDAwMCwgbWVhbiA9IDE3OCwgc2QgPSAxNSkKbm9ybWFsRGF0YSA9IGRhdGEuZnJhbWUoZGF0YSA9IG5vcm1hbERhdGEpCmdncGxvdChub3JtYWxEYXRhLCBhZXMoeCA9IGRhdGEpKSArCiAgZ2VvbV9kZW5zaXR5KGNvbG91ciA9ICJibGFjayIsCiAgICAgICAgICAgICAgIGZpbGwgPSAiYWxpY2VibHVlIiwKICAgICAgICAgICAgICAgYWxwaGEgPSAuNzUpICsgCiAgc3RhdF9mdW5jdGlvbihmdW4gPSBlbXBpcmljYWxOb3JtYWwsIAogICAgICAgICAgICAgICAgY29sb3VyID0gInJlZCIsIAogICAgICAgICAgICAgICAgc2l6ZSA9IDEpICsgCiAgdGhlbWVfYncoKSArIAogIHRoZW1lKHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSkKYGBgCgpOb3csIHRoZSBjdW11bGF0aXZlIGRpc3RyaWJ1dGlvbjoKCmBgYCB7ciBlY2hvID0gVH0Kbm9ybWFsRGF0YSA8LSBzZXEoMTIwLCAyNDApCm5vcm1hbERhdGEgPC0gZGF0YS5mcmFtZShkYXRhID0gbm9ybWFsRGF0YSwgCiAgICAgICAgICAgICAgICAgICAgICAgICBwID0gcG5vcm0obm9ybWFsRGF0YSwgbWVhbiA9IDE3OCwgc2QgPSAxNSkpCmVtcGlyaWNhbE5vcm1hbEN1bXVsYXRpdmUgPC0gZnVuY3Rpb24oeCkgcG5vcm0oeCwgbWVhbiA9IDE3OCwgc2QgPSAxNSkKZ2dwbG90KG5vcm1hbERhdGEsIGFlcyh4ID0gZGF0YSwgCiAgICAgICAgICAgICAgICAgICAgICAgeSA9IHApKSArIAogIGdlb21fbGluZShjb2xvdXIgPSAiYmxhY2siLCBzaXplID0gLjI1KSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDEsIGNvbG9yID0gImJsYWNrIiwgZmlsbCA9ICJibGFjayIpICsgCiAgc3RhdF9mdW5jdGlvbihmdW4gPSBlbXBpcmljYWxOb3JtYWxDdW11bGF0aXZlLCAKICAgICAgICAgICAgICAgIGNvbG91ciA9ICJyZWQiLCAKICAgICAgICAgICAgICAgIHNpemUgPSAxKSArIAogIHRoZW1lX2J3KCkgKyAKICB0aGVtZShwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCkpCmBgYAoKIyMjIyA0LjIgQ2hlY2tpbmcgaWYgYSB2YXJpYWJsZSBoYXMgYSBub3JtYWwgZGlzdHJpYnV0aW9uCgpWZXJ5IG9mdGVuLCBvbmUgbmVlZHMgdG8gY2hlY2sgZm9yIHRoZSBub3JtYWxpdHkgYXNzdW1wdGlvbiBpbiBzb21lIGRhdGE6IHdoZXRoZXIgdGhlIGRhdGEgYXJlIG5vcm1hbGx5IGRpc3RyaWJ1dGVkLCBvciBub3QuIFRoYXQgbWlnaHQgc291bmQgYXMgYW4gZWFzeSB0YXNrIHRvIHBlcmZvcm0sIGVzcGVjaWFsbHkgd2hlbiBrbm93aW5nIHRoYXQgbWFueSBzdGF0aXN0aWNpYW5zIGhhdmUgd29ya2VkIG9uIHRoZSBwcm9ibGVtIGluIHRoZSBwYXN0LCBidXQgaW4gcHJhY3RpY2UgaXQgdHVybnMgb3V0IHRvIGJlIHRyaWNraWVyIHRoYW4gZXhwZWN0ZWQuCgpXZSB3aWxsIGZpcnN0IHVzZSB0aGUgZmFtb3VzICoqS29sbW9nb3Jvdi1TbWlybm92IFRlc3QqKiBvZiBub3JtYWxpdHkgKHNob3J0OiB0aGUgSy1TIHRlc3QpIG9uIHRoZSBgU2VwYWwuTGVuZ3RoYCB2YXJpYWJsZSBpbiBgaXJpc2A6CgpgYGB7ciBlY2hvID0gVH0KZGF0YShpcmlzKQojIFRoZSBLb2xtb2dvcm92LVNtaXJub3YgVGVzdAprc1NMZW5ndGggPC0ga3MudGVzdChpcmlzJFNlcGFsLkxlbmd0aCwKICAgICAgICAgICAgICAgICAgICAgInBub3JtIiwKICAgICAgICAgICAgICAgICAgICAgbWVhbihpcmlzJFNlcGFsLkxlbmd0aCksCiAgICAgICAgICAgICAgICAgICAgIHNkKGlyaXMkU2VwYWwuTGVuZ3RoKSwKICAgICAgICAgICAgICAgICAgICAgYWx0ZXJuYXRpdmUgPSAidHdvLnNpZGVkIiwKICAgICAgICAgICAgICAgICAgICAgZXhhY3QgPSBOVUxMKQprc1NMZW5ndGgKYGBgCgpJbiBvcmRlciB0byB1bmRlcnN0YW5kIHRoZSByZXN1bHQgb2YgdGhlIEstUyB0ZXN0LCBsb29rIGF0IHRoZSBgcC12YWx1ZWAgaW4gdGhlIG91dHB1dC4gV2UgZGlkIG5vdCBzdGFydCB0aGUgZGlzY3Vzc2lvbiBvZiBzdGF0aXN0aWNhbCBlc3RpbWF0aW9uIGFuZCBoeXBvdGhlc2lzIHRlc3RpbmcgeWV0LCBzbyBpdCBpcyBuYXR1cmFsIHRoYXQgeW91IHdvbmRlciB3aGF0IHRoZSAqcCogdmFsdWVzIGlzLiBGb3Igbm93LCBqdXN0IHJlbWVtYmVyOiB0aGUgZGlzdHJpYnV0aW9uIG9mIHNvbWUgdmFyaWFibGUgaXMgbm9ybWFsIGFjY29yZGluZyB0byB0aGUgSy1TIHRlc3QgaWYgdGhlIHAgdmFsdWUgYXNzb2NpYXRlZCB3aXRoIHRoZSB0ZXN0IHN0YXRpc3RpYyAqRCogKGFsc28gZm91bmQgaW4gdGhlIG91dHB1dCAgYWJvdmVeXikgaXMgKmxhcmdlciogdGhhbiAuMDUuCgpHb29kLiBUaGUgSy1TIHRlc3Qgc2F5cyB0aGlzIGlzIGEgbm9ybWFsIGRpc3RyaWJ1dGlvbjoKCmBgYCB7ciBlY2hvID0gVH0KZ2dwbG90KGlyaXMsCiAgICAgICBhZXMoeCA9IFNlcGFsLkxlbmd0aCkpICsgCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAuMjUsIGZpbGwgPSAiZGFya29yYW5nZSIsIGNvbG9yID0gImJsYWNrIikgKyAKICB0aGVtZV9idygpICsgCiAgdGhlbWUocGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpKQpgYGAKCkFuZCB3aGF0IGRvIHlvdSB0aGluaz8KCldlIHdpbGwgdHJ5IHRvIHRlc3QgZm9yIG5vcm1hbGl0eSBieSB1c2luZyB0aGUgKipTaGFwaXJvLVdpbGsgVGVzdCoqIChzaG9ydDogUy1XIHRlc3QpOiBpdCBpcyBiZXR0ZXIgZm9yIHNtYWxsIHNhbXBsZXMsIGJ1dCB1bmxpa2UgSy1TLCBpdCBhcHBsaWVzIHRvIHRoZSBOb3JtYWwgZGlzdHJpYnV0aW9uIG9ubHkuCgpgYGAge3IgZWNobyA9IFR9CnN3UExlbmd0aCA8LSBzaGFwaXJvLnRlc3QoaXJpcyRTZXBhbC5MZW5ndGgpCnN3UExlbmd0aApgYGAKCioqTm90ZS4qKiBUaGUgcC12YWx1ZSA+IDAuMDUgaW1wbGllcyB0aGF0IHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIGRhdGEgKippcyBub3QqKiBzaWduaWZpY2FudGx5IGRpZmZlcmVudCBmcm9tIHRoZSBub3JtYWwgZGlzdHJpYnV0aW9uLiBQbGVhc2U6IGRvIG5vdCBwYW5pYyBhYm91dCB0aGVzZSAqcCogdGhpbmdzLiBBbGwgdGhpcyB3aWxsIGJlY29tZSBjbGVhciBpbiB0aGUgc2Vzc2lvbnMgdG8gZm9sbG93LgoKTW9yZSBvZnRlbiB0aGFuIG5vdCwgeW91IHdpbGwgd2FudCB0byB0YWtlIGEgbG9vayBhdCB0aGUgUS1RIHBsb3QgdG8gZGV0ZXJtaW5lIHdoZXRoZXIgc29tZXRoaW5nIGlzIGFueSBzaW1pbGFyIHRvIGEgdGFyZ2V0IGRpc3RyaWJ1dGlvbiBvciBub3Q6CgpgYGAge3IgZWNobyA9IFQsIGZpZy53aWR0aCA9IDMsIGZpZy5oZWlnaHQgPSAzfQpxcW5vcm0oaXJpcyRTZXBhbC5MZW5ndGgsIHBjaCA9IDEsIGZyYW1lID0gRkFMU0UpCnFxbGluZShpcmlzJFNlcGFsLkxlbmd0aCwgY29sID0gImRhcmtibHVlIiwgbHdkID0gMikKYGBgCgpXaGF0IHdvdWxkIHlvdSBzYXkgbm93OiBpcyBgU2VwYWwuTGVuZ3RoYCBmcm9tIGBpcmlzYCBub3JtYWxseSBkaXN0cmlidXRlZD8KCiMjIyBGdXJ0aGVyIFJlYWRpbmdzCgotIFtQcm9iYWJpbGl0eSBjb25jZXB0cyBleHBsYWluZWQ6IHByb2JhYmlsaXR5IGRpc3RyaWJ1dGlvbnMgKGludHJvZHVjdGlvbiBwYXJ0IDMpIGJ5IEpvbm55IEJyb29rcy1CYXJ0bGV0dCwgZnJvbSBUb3dhcmRzIERhdGEgU2NpZW5jZV0oaHR0cHM6Ly90b3dhcmRzZGF0YXNjaWVuY2UuY29tL3Byb2JhYmlsaXR5LWNvbmNlcHRzLWV4cGxhaW5lZC1wcm9iYWJpbGl0eS1kaXN0cmlidXRpb25zLWludHJvZHVjdGlvbi1wYXJ0LTMtNGE1ZGI4MTg1OGRjKQotIFtHcmluc3RlYWQgYW5kIFNuZWxs4oCZcyBJbnRyb2R1Y3Rpb24gdG8gUHJvYmFiaWxpdHkgKE5PVEU6IFRoZSBCaWJsZSBvZiBQcm9iYWJpbGl0eSBUaGVvcnkpXShodHRwczovL21hdGguZGFydG1vdXRoLmVkdS9+cHJvYi9wcm9iL3Byb2IucGRmKS4gRGVmaW5pdGVseSBub3QgYW4gaW50cm9kdWN0b3J5IG1hdGVyaWFsLCBidXQgZXZlcnl0aGluZyBmcm9tIENoYXB0ZXIgMS4gYW5kIHVwIHRvIENoYXB0ZXIgOS4gYXQgbGVhc3QgaXMgKGEpIHN1cGVyLWludGVyZXN0aW5nIHRvIGxlYXJuLCAoYikgc3VwZXItdXNlZnVsIGluIERhdGEgU2NpZW5jZSwgYW5kIChjKSBtb3N0IERhdGEgU2NpZW5jZSBwcmFjdGl0aW9uZXJzIGFscmVhZHkga25vdyBpdCAob3Igc2hvdWxkIGtub3cgaXQpLiBFbmpveSEKLSBbTW9yZSBvbiB0aGUgS29sbW9nb3Jvdi1TbWlybm92IFRlc3RdKGh0dHBzOi8vd3d3Lml0bC5uaXN0Lmdvdi9kaXY4OTgvaGFuZGJvb2svZWRhL3NlY3Rpb24zL2VkYTM1Zy5odG0pCi0gW01vcmUgb24gUVEgcGxvdHMgdy4ge2dncGxvdDJ9XShodHRwczovL2dncGxvdDIudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2UvZ2VvbV9xcS5odG1sKQotIFtNb3JlIG9uIFByb2JhYmlsaXR5IEZ1bmN0aW9ucyBpbiBSOiAyLjEgUmFuZG9tIFZhcmlhYmxlcyBhbmQgUHJvYmFiaWxpdHkgRGlzdHJpYnV0aW9ucyBmcm9tIEludHJvZHVjdGlvbiB0byBFY29ub21ldHJpY3Mgd2l0aCBSXShodHRwczovL3d3dy5lY29ub21ldHJpY3Mtd2l0aC1yLm9yZy8yLTEtcmFuZG9tLXZhcmlhYmxlcy1hbmQtcHJvYmFiaWxpdHktZGlzdHJpYnV0aW9ucy5odG1sKQoKCiMjIyBTb21lIEludHJvZHVjdG9yeSBWaWRlbyBNYXRlcmlhbAoKLSBbS2hhbiBBY2FkZW15OiBGdW5jdGlvbnNdKGh0dHBzOi8vd3d3LmtoYW5hY2FkZW15Lm9yZy9tYXRoL2FsZ2VicmEveDJmOGJiMTE1OTViNjFjODY6ZnVuY3Rpb25zKQotIFtLaGFuIEFjYWRlbXk6IEJhc2ljIHRoZW9yZXRpY2FsIHByb2JhYmlsaXR5XShodHRwczovL3d3dy5raGFuYWNhZGVteS5vcmcvbWF0aC9zdGF0aXN0aWNzLXByb2JhYmlsaXR5L3Byb2JhYmlsaXR5LWxpYnJhcnkvYmFzaWMtdGhlb3JldGljYWwtcHJvYmFiaWxpdHkvdi9iYXNpYy1wcm9iYWJpbGl0eSkKLSBbS2hhbiBBY2FkZW15OiBQcm9iYWJpbGl0eSBVc2luZyBTYW1wbGUgU3BhY2VzXShodHRwczovL3d3dy5raGFuYWNhZGVteS5vcmcvbWF0aC9zdGF0aXN0aWNzLXByb2JhYmlsaXR5L3Byb2JhYmlsaXR5LWxpYnJhcnkvcHJvYmFiaWxpdHktc2FtcGxlLXNwYWNlcy92L2V2ZW50cy1hbmQtb3V0Y29tZXMtMykKLSBbS2hhbiBBY2FkZW15OiBEaXNjcmV0ZSBSYW5kb20gVmFyaWFibGVzXShodHRwczovL3d3dy5raGFuYWNhZGVteS5vcmcvbWF0aC9zdGF0aXN0aWNzLXByb2JhYmlsaXR5L3JhbmRvbS12YXJpYWJsZXMtc3RhdHMtbGlicmFyeS9yYW5kb20tdmFyaWFibGVzLWRpc2NyZXRlL3YvcmFuZG9tLXZhcmlhYmxlcykKLSBbS2hhbiBBY2FkZW15OiBDb250aW51b3VzIFJhbmRvbSBWYXJpYWJsZXNdKGh0dHBzOi8vd3d3LmtoYW5hY2FkZW15Lm9yZy9tYXRoL3N0YXRpc3RpY3MtcHJvYmFiaWxpdHkvcmFuZG9tLXZhcmlhYmxlcy1zdGF0cy1saWJyYXJ5L3JhbmRvbS12YXJpYWJsZXMtY29udGludW91cy92L3Byb2JhYmlsaXR5LWRlbnNpdHktZnVuY3Rpb25zKQoKIyMjIFIgTWFya2Rvd24KCltSIE1hcmtkb3duXShodHRwczovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbS8pIGlzIHdoYXQgSSBoYXZlIHVzZWQgdG8gcHJvZHVjZSB0aGlzIGJlYXV0aWZ1bCBOb3RlYm9vay4gV2Ugd2lsbCBsZWFybiBtb3JlIGFib3V0IGl0IG5lYXIgdGhlIGVuZCBvZiB0aGUgY291cnNlLCBidXQgaWYgeW91IGFscmVhZHkgZmVlbCByZWFkeSB0byBkaXZlIGRlZXAsIGhlcmUncyBhIGJvb2s6IFtSIE1hcmtkb3duOiBUaGUgRGVmaW5pdGl2ZSBHdWlkZSwgWWlodWkgWGllLCBKLiBKLiBBbGxhaXJlLCBHYXJyZXR0IEdyb2xlbXVuZHMuXShodHRwczovL2Jvb2tkb3duLm9yZy95aWh1aS9ybWFya2Rvd24vKSAKCgoqKioKR29yYW4gUy4gTWlsb3Zhbm92acSHCgpEYXRhS29sZWt0aXYsIDIwMjAvMjEKCmNvbnRhY3Q6IGdvcmFuLm1pbG92YW5vdmljQGRhdGFrb2xla3Rpdi5jb20KCiFbXSguLi9faW1nL0RLX0xvZ29fMTAwLnBuZykKCioqKgpMaWNlbnNlOiBbR1BMdjNdKGh0dHA6Ly93d3cuZ251Lm9yZy9saWNlbnNlcy9ncGwtMy4wLnR4dCkKVGhpcyBOb3RlYm9vayBpcyBmcmVlIHNvZnR3YXJlOiB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IgbW9kaWZ5IGl0IHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgYXMgcHVibGlzaGVkIGJ5IHRoZSBGcmVlIFNvZnR3YXJlIEZvdW5kYXRpb24sIGVpdGhlciB2ZXJzaW9uIDMgb2YgdGhlIExpY2Vuc2UsIG9yIChhdCB5b3VyIG9wdGlvbikgYW55IGxhdGVyIHZlcnNpb24uClRoaXMgTm90ZWJvb2sgaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCwgYnV0IFdJVEhPVVQgQU5ZIFdBUlJBTlRZOyB3aXRob3V0IGV2ZW4gdGhlIGltcGxpZWQgd2FycmFudHkgb2YgTUVSQ0hBTlRBQklMSVRZIG9yIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFLiAgU2VlIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLgpZb3Ugc2hvdWxkIGhhdmUgcmVjZWl2ZWQgYSBjb3B5IG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBhbG9uZyB3aXRoIHRoaXMgTm90ZWJvb2suIElmIG5vdCwgc2VlIDxodHRwOi8vd3d3LmdudS5vcmcvbGljZW5zZXMvPi4KCioqKgoK