Some simple statistics
Characterising a sample
Variance and standard deviation
Distributions and histograms
Revision: some less familiar operators
Integer division: %/%
This gives you the whole number part of a division
7 %/% 2
[1] 3
You can do this with a series of numerals
1:10 %/% 3
[1] 0 0 1 1 1 2 2 2 3 3
Modulo %%
:
This gives you the remainder after you’ve done division.
17 %/% 3
[1] 5
17 %% 3
[1] 2
This is often used to tell if a number is even or odd:
#numbers <- 1:10
numbers <- c(4,45,2,46,96,75,32)
numbers %% 2
[1] 0 1 0 0 0 1 0
numbers %% 2 == 1
[1] FALSE TRUE FALSE FALSE FALSE TRUE FALSE
Selecting the odd numbers
numbers[numbers %% 2 == 1]
[1] 45 75
Selecting the even numbers
numbers[numbers %% 2 == 0]
[1] 4 2 46 96 32
NOTE that this only works for integers (whole numbers); R often works with numeric
values, which can also be decimals.
cf. the is.integer()
and as.integer()
functions
Mean
We’ll look at this again later, but here’s a reminder about the runif()
function to generate random numbers
runif(10) # by default from 0 to 1
[1] 0.3561 0.8718 0.1580 0.7354 0.9886 0.6004 0.7171 0.9770 0.4326 0.7649
runif(10, 0, 100) # or specify min max
[1] 82.735 8.145 84.839 8.281 8.890 9.847 54.866 13.696 51.634 77.782
as.integer(runif(10,0,100))
[1] 1 42 62 31 35 84 84 52 90 31
Make a function to calculate the mean
values <- runif(30, 17, 987)
sum(values) / length(values)
[1] 490.3
# my_func <- function(arg1) {do something with arg1}
custom.mean.func <- function(v) { sum(v)/length(v)}
custom.mean.func(values)
[1] 490.3
custom.mean.func(1:10)
[1] 5.5
mean(values) ## the built-in function
[1] 490.3
Although there is actually a mean()
function that does exactly what you’d expect
Handling NAs
values[3] <- NA
values
[1] 295.28 21.33 NA 243.24 720.07 963.24 494.86 432.98 841.42 801.84 946.57 748.44 92.99 392.27 960.72 423.76 409.10 299.33 839.03 48.92 282.76
[22] 128.44 315.35 496.87 773.54 101.12 652.96 193.01 492.19 412.95
mean(values)
[1] NA
mean(values, na.rm=TRUE)
[1] 476.7
custom.mean.func <- function(v, na.rm=FALSE) {
sum(v, na.rm=na.rm)/length(v[!is.na(v)])}
custom.mean.func(values, na.rm=TRUE)
[1] 476.7
Median
The middle value of a sorted series; if there are an even number of values then take the value halfway between the middle two.
values <- runif(21, 1, 55)
mean(values)
[1] 26.62
median(values)
[1] 23.72
# sort the values
sort(values)
[1] 2.002 4.365 7.692 11.380 13.674 15.092 16.171 17.316 21.775 21.977 23.724 25.052 27.305 31.939 36.334 41.505 43.295 44.519 45.651 53.712 54.597
# the index for the midpoint is integer-half plus one
(length(values) %/% 2) + 1
[1] 11
# use this number to index the sorted vector
sort(values)[11]
[1] 23.72
# put it all together
sort(values)[(length(values) %/% 2) + 1]
[1] 23.72
odd.median <- function(v) {sort(v)[(length(v) %/% 2) + 1]}
# some tests
values <- runif(101, 0, 10)
odd.median(values)
[1] 4.927
median(values)
[1] 4.927
values <- runif(100, 0, 10)
odd.median(values)
[1] 5.355
median(values)
[1] 5.3
even.median <- function(v){
v.1 = sort(v)[length(v) %/% 2]
v.2 = sort(v)[(length(v) %/% 2) + 1]
(v.1 + v.2) / 2
}
even.median(values)
[1] 5.3
median(values)
[1] 5.3
universal.median <- function(v){
if (length(v) %% 2 == 1) { ## ODD
sort(v)[(length(v) %/% 2) + 1]
} else { ## EVEN
v.1 = sort(v)[length(v) %/% 2]
v.2 = sort(v)[(length(v) %/% 2) + 1]
(v.1 + v.2) / 2
}
}
Let’s run some tests
library(testthat)
for (i in 10:20){
values <- runif(i, 0, 100)
#print(universal.median(values) == median(values))
expect_equal(universal.median(values), median(values))
}
Make your own function to calculate the median
sort
function
%%
and %/%
(to carry out calculations over the indexes of the vector)
Compare the output of your function to the built-in median
function using the testthat
package
When is mean useful and when is median useful
income <- runif(100, 1000, 1500)
mean(income)
[1] 1264
median(income)
[1] 1263
But now someone really wealthy joins
income.2 <- c(income, 1000000)
mean(income.2)
[1] 11153
median(income.2)
[1] 1264
Range
Knowing the range of a set of numbers can be useful
Test these all out
values <- runif(10, 0, 100)
min(values)
[1] 8.117
max(values)
[1] 87.95
range(values)
[1] 8.117 87.952
Problem: doesn’t handle outliers well Solution: look at the variance
Population variance
The following shows how much each of these values differs from the mean value
values
[1] 58.553 66.401 53.200 78.226 39.967 49.443 10.270 87.952 8.117 63.363
values - mean(values)
[1] 7.004 14.851 1.651 26.677 -11.583 -2.106 -41.279 36.402 -43.432 11.814
Problem if we want the average difference from the mean, since some are positive and some are negative:
sum(values - mean(values))
[1] 0
Solution is to square the values (in addition this has the generally desirable side effect of emphasising the larger values over the smaller values)
values - mean(values)
[1] 7.004 14.851 1.651 26.677 -11.583 -2.106 -41.279 36.402 -43.432 11.814
(values - mean(values))^2
[1] 49.056 220.567 2.726 711.661 134.156 4.437 1703.935 1325.135 1886.363 139.574
# The average square of the difference from the mean
sum((values - mean(values))^2) / length(a.c)
Error: object 'a.c' not found
The average square of the difference from the mean is called the population variance
This assumes that we are working with population, not a sample. The population is everything, the sample is a random subset.
Summary
\(\mu\) is the population mean (can be difficult or impossible to know)
We could look at how much each value differs from the mean:
The average of this would be a nice statement of how far off the mean the values tend to be: \[\frac{\Sigma(x_i - \mu)}{N}\] But there’s a problem, because these add up to zero
i.e. zero (sometimes this will give an extremely small number instead of zero — the precision of a computer is less than your human precision since the computer has to convert all these values to binary)
The solution: square it \[\sigma^2 = \frac{\Sigma(x_i - \mu)^2}{N}\]
Guaranteed positive values
Big differences from the mean are more heavily weighted than small differences
Note \(\sigma^2\) is the symbol for population variance
Sample variance
What if we don’t know the true mean? If we have a sample of the members of the population rather than the whole population.
We use the sample mean rather than true mean in the equation
We replace N by N - 1 (mathematical proof is hard, but basicially: the sample mean is always closer to the middle of the sample than the true mean, so using the population variance formula on a sample underestimates the value of the variance. Dividing by N-1 rather than by N increases the estimate of the variance slightly to compensate for this)
\[s^2 = \frac{\Sigma(x_i - \bar{x})^2}{N - 1}\]
The symbol for sample variance is \(s^2\)
The r function var()
can be used to calculate the sample variance
Standard deviation (population and sample)
Variance is kind of abstract
The solution is simply to take the square root. This is called the standard deviation
Usually we use the sample standard deviation : \[s = \sqrt{\frac{\Sigma(x_i - \bar{x})^2}{N - 1}}\]
There is also a population standard deviation , for when you are working with all the values that exist: \[\sigma = \sqrt{\frac{\Sigma(x_i - \mu)^2}{N}}\] - The r function sd()
calculates the sample standard deviation (see help(sd)
) - Just for practice, make your own function to calculate sample standard deviation and compare it to the built-in function (using testthat
package) - Make a function to calculate population standard devation and compare the values to sample standard deviation
Normal distribution
Galton Board
VIDEO
In a normal distribution: - 68.2% of values within one standard deviation - 95.4% within two standard deviations - 99.7% within 3 - 99.99% within 4
Normal distributions can be described by two parameters, mean and standard deviation. The rnorm()
function generates vectors of random numbers accordingn to a normal distribution.
Read the help for the rnorm()
function. Generate some vectors of normally distributed random numbers given different means and standard deviations.
Graph them and work out their means and standard deviations from the vectors
Histogram
Here’s how you can visualise these:
A small sample will be jagged, but the larger the sample the smoother it gets:
HOMEWORK NOTES
library(babynames)
library(stringr)
Anna’s name plot compares four dimensions (year, popularity, name, sex)
babynames%>%
filter(name=="Taylor"|name=="Zachary"|name=="Isaac")%>%
ggplot(aes(x=year, y=n, colour=name, linetype=sex)) + geom_line()
Plotting mean name length of boys and girls (adapted from Bror-Magnus)
babynames %>%
group_by(year, sex) %>%
summarise(mean.length=sum(n*nchar(name))/sum(n)) %>%
ggplot(aes(x=year, y=mean.length, colour=sex)) + geom_line()
Maja’s investigation of Chantal variants
testing the regular expression:
babynames$name %>% unique() %>% str_subset("[S|C+]c?+h?+a+n{1,}+t+[a{1,}e{1,}]+l{1,}+e?")
[1] "Chantal" "Chantel" "Chantell" "Chantelle" "Shantel" "Chantele" "Shantell" "Shantelle" "Chantale" "Chantella" "Shantele" "Shantella"
[13] "Shantal" "Shantale" "Chantalle" "Schantel" "Shantala" "Chanteal" "Chantall" "Shantela" "Shantall" "Shanteal" "Shantalle"
plotting:
Linnea’s Game of Thrones plot. Note the use of annotate
to put the label onto an independent layer:
filter(babynames, name=="Daenerys" | name=="Cersei" | name=="Arya" | name=="Sansa" | name=="Margaery" | name=="Ygritte" | name=="Brienne" | name=="Robb" | name=="Theon" | name=="Sandor" | name=="Tormund" | name=="Gendry" | name=="Jorah" | name=="Joffrey")%>% filter(year%in%2000:2015) %>% ggplot(aes(x=year, y=log(n), colour=name, linetype=sex)) + geom_line() + annotate("text", hjust=0, label="GoT release", x=2011, y=5.2) + geom_vline(xintercept=2011)
A slight reworking of Marc’s investigation of presidents and spouses. Note - added a role
columns - the label data is in a independent data frame from the time series/popularity data
library(babynames)
babynames %>%
#find out all the boys named George, Bill, and Barack. Find out all the girls named Barbara, Hillary, Laura, and Michelle.
filter((name %in% c("George","Bill","Barack") & sex=="M")|(name %in% c("Barbara","Hillary","Laura","Michelle") & sex=="F")) %>%
#only take the observations from 1980 to 2015 (take a bit before 1989 as a buffer).
filter(year %in% c(1980:2015)) %>%
mutate(role=if_else(sex=="M", "President", "Spouse")) %>%
#use geom_text to show the starting point of each first lady in the timeline and sync the colors
ggplot(aes(x=year, y=log10(n), colour=name)) +
geom_line() +facet_wrap(~role) -> p
label.data <- tibble(
name=c("George","Bill","George","Barack","Barbara","Hillary","Laura","Michelle"),
sex=c("M","M","M","M","F","F","F","F"),
year=c(1989, 1993, 2001, 2009, 1989, 1993, 2001, 2009),
y.pos=c(3.4,2.3,3.65,2.1,3.35,3.55,3.35,3.75)
) %>% mutate(role=if_else(sex=="M", "President", "Spouse"))
p + geom_text(data=label.data, aes(x=year, y=y.pos, label=name)) + guides(colour=FALSE)
Rima’s look at Hillaries and Monicas, allowing for variant spelling. - str_extract
to filter out only the rows we’re interested in - str_sub
to change normalise all the names to “H” and “M” - recode
to change the “H” and “M” values to something more self-explanatory
babynames %>%
filter(name %in% str_extract(name, "Hill?ary|Moni[ck]a")) %>%
filter(sex=="F") %>%
mutate(name=str_sub(name, 1, 1)) %>%
mutate(name=recode(name, H="Hill?ary", M="Moni[ck]a")) %>%
group_by(year, sex, name) %>%
summarise(total=sum(n)) %>%
ggplot(aes(x=year, y=total, colour=name)) + geom_line()
LS0tCnRpdGxlOiAiU2Vzc2lvbiA2IG5vdGVzLCAyMDE4LTEwLTI5IgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIGRmX3ByaW50OiBwYWdlZAotLS0KClBsYW46IAoKKipSZXZpc2lvbioqIENvbnNvbG9kYXRlIHZpc3VhbGlzYXRpb24gc2tpbGxzIGJ5IGdvaW5nIG92ZXIgaG9tZXdvcmsgc29sdXRpb25zCgoqKk5ldyoqIEV4cGxvcmUgd2F5cyB0byBzdW1tYXJpc2UgbnVtZXJpY2FsIGRhdGEKCiMgU29tZSBzaW1wbGUgc3RhdGlzdGljcwoKLSBDaGFyYWN0ZXJpc2luZyBhIHNhbXBsZQogICogcmFuZ2UKICAqIG1lYW4KICAqIG1lZGlhbgotIFZhcmlhbmNlIGFuZCBzdGFuZGFyZCBkZXZpYXRpb24KICAqIHNkCi0gRGlzdHJpYnV0aW9ucyBhbmQgaGlzdG9ncmFtcwogICogcm5vcm0KICAqIHJ1bmlmCgojIyMgUmV2aXNpb246IHNvbWUgbGVzcyBmYW1pbGlhciBvcGVyYXRvcnMKCkludGVnZXIgZGl2aXNpb246IGAlLyVgCgotIFRoaXMgZ2l2ZXMgeW91IHRoZSB3aG9sZSBudW1iZXIgcGFydCBvZiBhIGRpdmlzaW9uCmBgYHtyfQo3ICUvJSAyCmBgYApZb3UgY2FuIGRvIHRoaXMgd2l0aCBhIHNlcmllcyBvZiBudW1lcmFscwpgYGB7cn0KMToxMCAlLyUgMwpgYGAKTW9kdWxvIGAlJWA6CgotIFRoaXMgZ2l2ZXMgeW91IHRoZSByZW1haW5kZXIgYWZ0ZXIgeW91J3ZlIGRvbmUgZGl2aXNpb24uCmBgYHtyfQoxNyAlLyUgMwoxNyAlJSAzCmBgYApUaGlzIGlzIG9mdGVuIHVzZWQgdG8gdGVsbCBpZiBhIG51bWJlciBpcyBldmVuIG9yIG9kZDoKYGBge3J9CiNudW1iZXJzIDwtIDE6MTAKbnVtYmVycyA8LSBjKDQsNDUsMiw0Niw5Niw3NSwzMikKbnVtYmVycyAlJSAyCmBgYApgYGB7cn0KbnVtYmVycyAlJSAyID09IDEKYGBgClNlbGVjdGluZyB0aGUgb2RkIG51bWJlcnMKYGBge3J9Cm51bWJlcnNbbnVtYmVycyAlJSAyID09IDFdCmBgYApTZWxlY3RpbmcgdGhlIGV2ZW4gbnVtYmVycwpgYGB7cn0KbnVtYmVyc1tudW1iZXJzICUlIDIgPT0gMF0KYGBgCioqTk9URSoqIHRoYXQgdGhpcyBvbmx5IHdvcmtzIGZvciBpbnRlZ2VycyAod2hvbGUgbnVtYmVycyk7IFIgb2Z0ZW4gd29ya3Mgd2l0aCBgbnVtZXJpY2AgdmFsdWVzLCB3aGljaCBjYW4gYWxzbyBiZSBkZWNpbWFscy4KCi0gY2YuIHRoZSBgaXMuaW50ZWdlcigpYCBhbmQgYGFzLmludGVnZXIoKWAgZnVuY3Rpb25zCgojIyBNZWFuCgpXZSdsbCBsb29rIGF0IHRoaXMgYWdhaW4gbGF0ZXIsIGJ1dCBoZXJlJ3MgYSByZW1pbmRlciBhYm91dCB0aGUgYHJ1bmlmKClgIGZ1bmN0aW9uIHRvIGdlbmVyYXRlIHJhbmRvbSBudW1iZXJzCmBgYHtyfQpydW5pZigxMCkgIyBieSBkZWZhdWx0IGZyb20gMCB0byAxCnJ1bmlmKDEwLCAwLCAxMDApICMgb3Igc3BlY2lmeSBtaW4gbWF4CmFzLmludGVnZXIocnVuaWYoMTAsMCwxMDApKQpgYGAKCi0gTWFrZSBhIGZ1bmN0aW9uIHRvIGNhbGN1bGF0ZSB0aGUgbWVhbgogICogbmVlZHMgYHN1bWAgYW5kIGBsZW5ndGhgCmBgYHtyfQp2YWx1ZXMgPC0gcnVuaWYoMzAsIDE3LCA5ODcpCgpgYGAKYGBge3J9CnN1bSh2YWx1ZXMpIC8gbGVuZ3RoKHZhbHVlcykKIyBteV9mdW5jIDwtIGZ1bmN0aW9uKGFyZzEpIHtkbyBzb21ldGhpbmcgd2l0aCBhcmcxfQpjdXN0b20ubWVhbi5mdW5jIDwtIGZ1bmN0aW9uKHYpIHsgc3VtKHYpL2xlbmd0aCh2KX0KYGBgCmBgYHtyfQpjdXN0b20ubWVhbi5mdW5jKHZhbHVlcykKY3VzdG9tLm1lYW4uZnVuYygxOjEwKQptZWFuKHZhbHVlcykgIyMgdGhlIGJ1aWx0LWluIGZ1bmN0aW9uCmBgYAoKLSBBbHRob3VnaCB0aGVyZSBpcyBhY3R1YWxseSBhIGBtZWFuKClgIGZ1bmN0aW9uIHRoYXQgZG9lcyBleGFjdGx5IHdoYXQgeW91J2QgZXhwZWN0Ci0gSGFuZGxpbmcgTkFzCgpgYGB7cn0KdmFsdWVzWzNdIDwtIE5BCnZhbHVlcwpgYGAKYGBge3J9Cm1lYW4odmFsdWVzKQpgYGAKYGBge3J9Cm1lYW4odmFsdWVzLCBuYS5ybT1UUlVFKQpgYGAKYGBge3J9CmN1c3RvbS5tZWFuLmZ1bmMgPC0gZnVuY3Rpb24odiwgbmEucm09RkFMU0UpIHsgCiAgc3VtKHYsIG5hLnJtPW5hLnJtKS9sZW5ndGgodlshaXMubmEodildKX0KY3VzdG9tLm1lYW4uZnVuYyh2YWx1ZXMsIG5hLnJtPVRSVUUpCmBgYAoKIyMgTWVkaWFuCgpUaGUgbWlkZGxlIHZhbHVlIG9mIGEgc29ydGVkIHNlcmllczsgaWYgdGhlcmUgYXJlIGFuIGV2ZW4gbnVtYmVyIG9mIHZhbHVlcyB0aGVuIHRha2UgdGhlIHZhbHVlIGhhbGZ3YXkgYmV0d2VlbiB0aGUgbWlkZGxlIHR3by4KCmBgYHtyfQp2YWx1ZXMgPC0gcnVuaWYoMjEsIDEsIDU1KQptZWFuKHZhbHVlcykKbWVkaWFuKHZhbHVlcykKYGBgCmBgYHtyfQojIHNvcnQgdGhlIHZhbHVlcwpzb3J0KHZhbHVlcykKIyB0aGUgaW5kZXggZm9yIHRoZSBtaWRwb2ludCBpcyBpbnRlZ2VyLWhhbGYgcGx1cyBvbmUKKGxlbmd0aCh2YWx1ZXMpICUvJSAyKSArIDEKIyB1c2UgdGhpcyBudW1iZXIgdG8gaW5kZXggdGhlIHNvcnRlZCB2ZWN0b3IKc29ydCh2YWx1ZXMpWzExXQojIHB1dCBpdCBhbGwgdG9nZXRoZXIKc29ydCh2YWx1ZXMpWyhsZW5ndGgodmFsdWVzKSAlLyUgMikgKyAxXQpgYGAKYGBge3J9Cm9kZC5tZWRpYW4gPC0gZnVuY3Rpb24odikge3NvcnQodilbKGxlbmd0aCh2KSAlLyUgMikgKyAxXX0KIyBzb21lIHRlc3RzCnZhbHVlcyA8LSBydW5pZigxMDEsIDAsIDEwKQpvZGQubWVkaWFuKHZhbHVlcykKbWVkaWFuKHZhbHVlcykKYGBgCmBgYHtyfQp2YWx1ZXMgPC0gcnVuaWYoMTAwLCAwLCAxMCkKb2RkLm1lZGlhbih2YWx1ZXMpCm1lZGlhbih2YWx1ZXMpCmBgYApgYGB7cn0KZXZlbi5tZWRpYW4gPC0gZnVuY3Rpb24odil7CiAgdi4xID0gc29ydCh2KVtsZW5ndGgodikgJS8lIDJdCiAgdi4yID0gc29ydCh2KVsobGVuZ3RoKHYpICUvJSAyKSArIDFdCiAgKHYuMSArIHYuMikgLyAyCn0KZXZlbi5tZWRpYW4odmFsdWVzKQptZWRpYW4odmFsdWVzKQpgYGAKCmBgYHtyfQp1bml2ZXJzYWwubWVkaWFuIDwtIGZ1bmN0aW9uKHYpewogIGlmIChsZW5ndGgodikgJSUgMiA9PSAxKSB7ICMjIE9ERAogICAgc29ydCh2KVsobGVuZ3RoKHYpICUvJSAyKSArIDFdCiAgfSBlbHNlIHsgIyMgRVZFTgogICAgdi4xID0gc29ydCh2KVtsZW5ndGgodikgJS8lIDJdCiAgICB2LjIgPSBzb3J0KHYpWyhsZW5ndGgodikgJS8lIDIpICsgMV0KICAgICh2LjEgKyB2LjIpIC8gMgogIH0KfQpgYGAKTGV0J3MgcnVuIHNvbWUgdGVzdHMKYGBge3J9CmxpYnJhcnkodGVzdHRoYXQpIAoKZm9yIChpIGluIDEwOjIwKXsKICB2YWx1ZXMgPC0gcnVuaWYoaSwgMCwgMTAwKQogICNwcmludCh1bml2ZXJzYWwubWVkaWFuKHZhbHVlcykgPT0gbWVkaWFuKHZhbHVlcykpCiAgZXhwZWN0X2VxdWFsKHVuaXZlcnNhbC5tZWRpYW4odmFsdWVzKSwgbWVkaWFuKHZhbHVlcykpCn0KYGBgCgotIE1ha2UgeW91ciBvd24gZnVuY3Rpb24gdG8gY2FsY3VsYXRlIHRoZSBtZWRpYW4KICAqIGBzb3J0YCBmdW5jdGlvbiAKICAqIGAlJWAgYW5kIGAlLyVgICh0byBjYXJyeSBvdXQgY2FsY3VsYXRpb25zIG92ZXIgdGhlIGluZGV4ZXMgb2YgdGhlIHZlY3RvcikKLSBDb21wYXJlIHRoZSBvdXRwdXQgb2YgeW91ciBmdW5jdGlvbiB0byB0aGUgYnVpbHQtaW4gYG1lZGlhbmAgZnVuY3Rpb24gdXNpbmcgdGhlIGB0ZXN0dGhhdGAgcGFja2FnZQotIFdoZW4gaXMgbWVhbiB1c2VmdWwgYW5kIHdoZW4gaXMgbWVkaWFuIHVzZWZ1bAoKYGBge3J9CmluY29tZSA8LSBydW5pZigxMDAsIDEwMDAsIDE1MDApCm1lYW4oaW5jb21lKQptZWRpYW4oaW5jb21lKQpgYGAKQnV0IG5vdyBzb21lb25lIHJlYWxseSB3ZWFsdGh5IGpvaW5zCmBgYHtyfQppbmNvbWUuMiA8LSBjKGluY29tZSwgMTAwMDAwMCkKbWVhbihpbmNvbWUuMikKbWVkaWFuKGluY29tZS4yKQpgYGAKCiMjIFJhbmdlCgpLbm93aW5nIHRoZSByYW5nZSBvZiBhIHNldCBvZiBudW1iZXJzIGNhbiBiZSB1c2VmdWwKCi0gbWluCi0gbWF4Ci0gcmFuZ2UKClRlc3QgdGhlc2UgYWxsIG91dAoKYGBge3J9CnZhbHVlcyA8LSBydW5pZigxMCwgMCwgMTAwKQptaW4odmFsdWVzKQptYXgodmFsdWVzKQpyYW5nZSh2YWx1ZXMpCmBgYAoKClByb2JsZW06IGRvZXNuJ3QgaGFuZGxlIG91dGxpZXJzIHdlbGwKU29sdXRpb246IGxvb2sgYXQgdGhlIHZhcmlhbmNlCgojIyBQb3B1bGF0aW9uIHZhcmlhbmNlCgpUaGUgZm9sbG93aW5nIHNob3dzIGhvdyBtdWNoIGVhY2ggb2YgdGhlc2UgdmFsdWVzIGRpZmZlcnMgZnJvbSB0aGUgbWVhbiB2YWx1ZQpgYGB7cn0KdmFsdWVzCnZhbHVlcyAtIG1lYW4odmFsdWVzKQpgYGAKUHJvYmxlbSBpZiB3ZSB3YW50IHRoZSBhdmVyYWdlIGRpZmZlcmVuY2UgZnJvbSB0aGUgbWVhbiwgc2luY2Ugc29tZSBhcmUgcG9zaXRpdmUgYW5kIHNvbWUgYXJlIG5lZ2F0aXZlOgpgYGB7cn0Kc3VtKHZhbHVlcyAtIG1lYW4odmFsdWVzKSkKYGBgClNvbHV0aW9uIGlzIHRvIHNxdWFyZSB0aGUgdmFsdWVzIChpbiBhZGRpdGlvbiB0aGlzIGhhcyB0aGUgZ2VuZXJhbGx5IGRlc2lyYWJsZSBzaWRlIGVmZmVjdCBvZiBlbXBoYXNpc2luZyB0aGUgbGFyZ2VyIHZhbHVlcyBvdmVyIHRoZSBzbWFsbGVyIHZhbHVlcykKCmBgYHtyfQp2YWx1ZXMgLSBtZWFuKHZhbHVlcykKKHZhbHVlcyAtIG1lYW4odmFsdWVzKSleMgojIFRoZSBhdmVyYWdlIHNxdWFyZSBvZiB0aGUgZGlmZmVyZW5jZSBmcm9tIHRoZSBtZWFuCnN1bSgodmFsdWVzIC0gbWVhbih2YWx1ZXMpKV4yKSAvIGxlbmd0aCh2YWx1ZXMpCmBgYApUaGUgYXZlcmFnZSBzcXVhcmUgb2YgdGhlIGRpZmZlcmVuY2UgZnJvbSB0aGUgbWVhbiBpcyBjYWxsZWQgdGhlICpwb3B1bGF0aW9uIHZhcmlhbmNlKgoKKipUaGlzIGFzc3VtZXMgdGhhdCB3ZSBhcmUgd29ya2luZyB3aXRoIHBvcHVsYXRpb24sIG5vdCBhIHNhbXBsZS4qKiBUaGUgcG9wdWxhdGlvbiBpcyBldmVyeXRoaW5nLCB0aGUgc2FtcGxlIGlzIGEgcmFuZG9tIHN1YnNldC4KClN1bW1hcnkgCgotICRcbXUkIGlzIHRoZSBwb3B1bGF0aW9uIG1lYW4gKGNhbiBiZSBkaWZmaWN1bHQgb3IgaW1wb3NzaWJsZSB0byBrbm93KQotIFdlIGNvdWxkIGxvb2sgYXQgaG93IG11Y2ggZWFjaCB2YWx1ZSBkaWZmZXJzIGZyb20gdGhlIG1lYW46CmBgYHtyfQp2YWx1ZXMgLSBtZWFuKHZhbHVlcykKYGBgClRoZSBhdmVyYWdlIG9mIHRoaXMgd291bGQgYmUgYSBuaWNlIHN0YXRlbWVudCBvZiBob3cgZmFyIG9mZiB0aGUgbWVhbiB0aGUgdmFsdWVzIHRlbmQgdG8gYmU6CiQkXGZyYWN7XFNpZ21hKHhfaSAtIFxtdSl9e059JCQKQnV0IHRoZXJlJ3MgYSBwcm9ibGVtLCBiZWNhdXNlIHRoZXNlIGFkZCB1cCB0byB6ZXJvCgppLmUuIHplcm8gKHNvbWV0aW1lcyB0aGlzIHdpbGwgZ2l2ZSBhbiBleHRyZW1lbHkgc21hbGwgbnVtYmVyIGluc3RlYWQgb2YgemVybyDigJQgdGhlIHByZWNpc2lvbiBvZiBhIGNvbXB1dGVyIGlzICoqbGVzcyoqIHRoYW4geW91ciBodW1hbiBwcmVjaXNpb24gc2luY2UgdGhlIGNvbXB1dGVyIGhhcyB0byBjb252ZXJ0IGFsbCB0aGVzZSB2YWx1ZXMgdG8gYmluYXJ5KQoKVGhlIHNvbHV0aW9uOiBzcXVhcmUgaXQKJCRcc2lnbWFeMiA9IFxmcmFje1xTaWdtYSh4X2kgLSBcbXUpXjJ9e059JCQKCi0gR3VhcmFudGVlZCBwb3NpdGl2ZSB2YWx1ZXMKLSBCaWcgZGlmZmVyZW5jZXMgZnJvbSB0aGUgbWVhbiBhcmUgbW9yZSBoZWF2aWx5IHdlaWdodGVkIHRoYW4gc21hbGwgZGlmZmVyZW5jZXMKLSBOb3RlICRcc2lnbWFeMiQgaXMgdGhlIHN5bWJvbCBmb3IgKnBvcHVsYXRpb24gdmFyaWFuY2UqCgojIyBTYW1wbGUgdmFyaWFuY2UKCi0gdmFyCgpXaGF0IGlmIHdlIGRvbid0IGtub3cgdGhlIHRydWUgbWVhbj8gSWYgd2UgaGF2ZSBhIHNhbXBsZSBvZiB0aGUgbWVtYmVycyBvZiB0aGUgcG9wdWxhdGlvbiByYXRoZXIgdGhhbiB0aGUgd2hvbGUgcG9wdWxhdGlvbi4KCi0gV2UgdXNlIHRoZSBzYW1wbGUgbWVhbiByYXRoZXIgdGhhbiB0cnVlIG1lYW4gaW4gdGhlIGVxdWF0aW9uCi0gV2UgcmVwbGFjZSBOIGJ5IE4gLSAxIChtYXRoZW1hdGljYWwgcHJvb2YgaXMgaGFyZCwgYnV0IGJhc2ljaWFsbHk6IHRoZSBzYW1wbGUgbWVhbiBpcyBhbHdheXMgY2xvc2VyIHRvIHRoZSBtaWRkbGUgb2YgdGhlIHNhbXBsZSB0aGFuIHRoZSB0cnVlIG1lYW4sIHNvIHVzaW5nIHRoZSBwb3B1bGF0aW9uIHZhcmlhbmNlIGZvcm11bGEgb24gYSBzYW1wbGUgKnVuZGVyZXN0aW1hdGVzKiB0aGUgdmFsdWUgb2YgdGhlIHZhcmlhbmNlLiBEaXZpZGluZyBieSBOLTEgcmF0aGVyIHRoYW4gYnkgTiAqaW5jcmVhc2VzKiB0aGUgZXN0aW1hdGUgb2YgdGhlIHZhcmlhbmNlIHNsaWdodGx5IHRvIGNvbXBlbnNhdGUgZm9yIHRoaXMpCgokJHNeMiA9IFxmcmFje1xTaWdtYSh4X2kgLSBcYmFye3h9KV4yfXtOIC0gMX0kJAoKLSBUaGUgc3ltYm9sIGZvciAqc2FtcGxlIHZhcmlhbmNlKiBpcyAkc14yJAotIFRoZSByIGZ1bmN0aW9uIGB2YXIoKWAgY2FuIGJlIHVzZWQgdG8gY2FsY3VsYXRlIHRoZSBzYW1wbGUgdmFyaWFuY2UKCiMjIFN0YW5kYXJkIGRldmlhdGlvbiAocG9wdWxhdGlvbiBhbmQgc2FtcGxlKQoKLSBzZAoKVmFyaWFuY2UgaXMga2luZCBvZiBhYnN0cmFjdApgYGB7cn0KYWdlcyA8LSBjKDE1LCAxNiwgMTQsIDE1LCAxNywgMTUsIDkpCnZhcihhZ2VzKSAjIGluIHllYXJzwrIgKCE/KQpgYGAKVGhlIHNvbHV0aW9uIGlzIHNpbXBseSB0byB0YWtlIHRoZSBzcXVhcmUgcm9vdC4gVGhpcyBpcyBjYWxsZWQgdGhlICpzdGFuZGFyZCBkZXZpYXRpb24qCgpVc3VhbGx5IHdlIHVzZSB0aGUgKnNhbXBsZSBzdGFuZGFyZCBkZXZpYXRpb24qOgokJHMgPSBcc3FydHtcZnJhY3tcU2lnbWEoeF9pIC0gXGJhcnt4fSleMn17TiAtIDF9fSQkCmBgYHtyfQpzZChhZ2VzKQpgYGAKVGhlcmUgaXMgYWxzbyBhICpwb3B1bGF0aW9uIHN0YW5kYXJkIGRldmlhdGlvbiosIGZvciB3aGVuIHlvdSBhcmUgd29ya2luZyB3aXRoICphbGwqIHRoZSB2YWx1ZXMgdGhhdCBleGlzdDoKJCRcc2lnbWEgPSBcc3FydHtcZnJhY3tcU2lnbWEoeF9pIC0gXG11KV4yfXtOfX0kJAotIFRoZSByIGZ1bmN0aW9uIGBzZCgpYCBjYWxjdWxhdGVzIHRoZSAqc2FtcGxlIHN0YW5kYXJkIGRldmlhdGlvbiogKHNlZSBgaGVscChzZClgKQotIEp1c3QgZm9yIHByYWN0aWNlLCBtYWtlIHlvdXIgb3duIGZ1bmN0aW9uIHRvIGNhbGN1bGF0ZSBzYW1wbGUgc3RhbmRhcmQgZGV2aWF0aW9uIGFuZCBjb21wYXJlIGl0IHRvIHRoZSBidWlsdC1pbiBmdW5jdGlvbiAodXNpbmcgYHRlc3R0aGF0YCBwYWNrYWdlKQotIE1ha2UgYSBmdW5jdGlvbiB0byBjYWxjdWxhdGUgcG9wdWxhdGlvbiBzdGFuZGFyZCBkZXZhdGlvbiBhbmQgY29tcGFyZSB0aGUgdmFsdWVzIHRvIHNhbXBsZSBzdGFuZGFyZCBkZXZpYXRpb24KCiMjIE5vcm1hbCBkaXN0cmlidXRpb24KCkdhbHRvbiBCb2FyZAoKPGlmcmFtZSB3aWR0aD0iODU0IiBoZWlnaHQ9IjQ4MCIgc3JjPSJodHRwczovL3d3dy55b3V0dWJlLmNvbS9lbWJlZC80SHB2QlpuSE9WSSIgZnJhbWVib3JkZXI9IjAiIGFsbG93PSJhdXRvcGxheTsgZW5jcnlwdGVkLW1lZGlhIiBhbGxvd2Z1bGxzY3JlZW4+PC9pZnJhbWU+CgpJbiBhIG5vcm1hbCBkaXN0cmlidXRpb246Ci0gNjguMiUgb2YgdmFsdWVzIHdpdGhpbiBvbmUgc3RhbmRhcmQgZGV2aWF0aW9uCi0gOTUuNCUgd2l0aGluIHR3byBzdGFuZGFyZCBkZXZpYXRpb25zCi0gOTkuNyUgd2l0aGluIDMKLSA5OS45OSUgd2l0aGluIDQKCiFbU3RhbmRhcmQgZGV2aWF0aW9uc10oU3RhbmRhcmRfZGV2aWF0aW9uX2RpYWdyYW0uc3ZnLnBuZykKCk5vcm1hbCBkaXN0cmlidXRpb25zIGNhbiBiZSBkZXNjcmliZWQgYnkgdHdvIHBhcmFtZXRlcnMsIG1lYW4gYW5kIHN0YW5kYXJkIGRldmlhdGlvbi4gVGhlIGBybm9ybSgpYCBmdW5jdGlvbiBnZW5lcmF0ZXMgdmVjdG9ycyBvZiByYW5kb20gbnVtYmVycyBhY2NvcmRpbmduIHRvIGEgbm9ybWFsIGRpc3RyaWJ1dGlvbi4KCi0gUmVhZCB0aGUgaGVscCBmb3IgdGhlIGBybm9ybSgpYCBmdW5jdGlvbi4gR2VuZXJhdGUgc29tZSB2ZWN0b3JzIG9mIG5vcm1hbGx5IGRpc3RyaWJ1dGVkIHJhbmRvbSBudW1iZXJzIGdpdmVuIGRpZmZlcmVudCBtZWFucyBhbmQgc3RhbmRhcmQgZGV2aWF0aW9ucy4KLSBHcmFwaCB0aGVtIGFuZCB3b3JrIG91dCB0aGVpciBtZWFucyBhbmQgc3RhbmRhcmQgZGV2aWF0aW9ucyBmcm9tIHRoZSB2ZWN0b3JzCgpgYGB7cn0KbGlicmFyeSh0aWR5dmVyc2UpCnZhbHVlcyA8LSBybm9ybSgxMDAsIDIwLCAzKSAjIHRlbiB2YWx1ZXMsIG1lYW49MjAsIHNkPTMKdGliYmxlKHg9dmFsdWVzKSAlPiUgZ2dwbG90KGFlcyh4PXgpKSArIGdlb21faGlzdG9ncmFtKCkKbWVhbih2YWx1ZXMpCnNkKHZhbHVlcykKYGBgCgojIyBIaXN0b2dyYW0KCi0gcm5vcm0oTiwgbWVhbiwgc2QpCgpIZXJlJ3MgaG93IHlvdSBjYW4gdmlzdWFsaXNlIHRoZXNlOgpgYGB7ciB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KHRpZHl2ZXJzZSkgIyBzaG91bGQgZG8gdGhpcyBhdCB0aGUgYmVnaW5uaW5nCm15LmRhdGEgPC0gdGliYmxlKHZhbHVlcz1ybm9ybSgxMDAsIDEwLCAyLjUpKQojbXkuZGF0YQojcXBsb3QodmFsdWVzLCBkYXRhPW15LmRhdGEsIGdlb209Imhpc3RvZ3JhbSIsIGJpbndpZHRoPTAuNSkKbXkuZGF0YSAlPiUgZ2dwbG90KGFlcyh4PXZhbHVlcykpICsgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGg9MC41KQpgYGAKCkEgc21hbGwgc2FtcGxlIHdpbGwgYmUgamFnZ2VkLCBidXQgdGhlIGxhcmdlciB0aGUgc2FtcGxlIHRoZSBzbW9vdGhlciBpdCBnZXRzOgpgYGB7cn0KbXkuZGF0YSA8LSB0aWJibGUodmFsdWVzPXJub3JtKDEwMDAwLCAxMCwgMi41KSkKcXBsb3QodmFsdWVzLCBkYXRhPW15LmRhdGEsIGdlb209Imhpc3RvZ3JhbSIsIGJpbndpZHRoPTAuNSkKYGBgCgojIyBVbmlmb3JtIGRpc3RyaWJ1dGlvbgpUaGVyZSBhcmUgbWFueSBraW5kcyBvZiBkaXN0cmlidXRpb25zLgoKLSBydW5pZihOLCBtaW4sIG1heCkKCkluIHRoZSAqVW5pZm9ybSBkaXN0cmlidXRpb24qIHRoZXJlIGlzIGFuIGVxdWFsIGNoYW5jZSBvZiBnZXR0aW5nIGFueSB2YWx1ZSBiZXR3ZWVuIHRoZSBtaW5pbXVtIGFuZCB0aGUgbWF4aW11bQpgYGB7cn0KbXkuZGF0YSA8LSB0aWJibGUodmFsdWVzPXJ1bmlmKDEwMDAsIG1pbj01LCBtYXg9OSkpCnFwbG90KHZhbHVlcywgZGF0YT1teS5kYXRhLCBnZW9tPSJoaXN0b2dyYW0iLCBicmVha3M9MToxMCkKYGBgCgojIEhPTUVXT1JLIE5PVEVTCgpgYGB7cn0KbGlicmFyeShiYWJ5bmFtZXMpCmxpYnJhcnkoc3RyaW5ncikKYGBgCgoKQW5uYSdzIG5hbWUgcGxvdCBjb21wYXJlcyBmb3VyIGRpbWVuc2lvbnMgKHllYXIsIHBvcHVsYXJpdHksIG5hbWUsIHNleCkKYGBge3J9CmJhYnluYW1lcyU+JQogIGZpbHRlcihuYW1lPT0iVGF5bG9yInxuYW1lPT0iWmFjaGFyeSJ8bmFtZT09IklzYWFjIiklPiUKICBnZ3Bsb3QoYWVzKHg9eWVhciwgeT1uLCBjb2xvdXI9bmFtZSwgbGluZXR5cGU9c2V4KSkgKyBnZW9tX2xpbmUoKQpgYGAKCgpQbG90dGluZyBtZWFuIG5hbWUgbGVuZ3RoIG9mIGJveXMgYW5kIGdpcmxzIChhZGFwdGVkIGZyb20gQnJvci1NYWdudXMpCmBgYHtyfQpiYWJ5bmFtZXMgJT4lCiAgZ3JvdXBfYnkoeWVhciwgc2V4KSAlPiUKICBzdW1tYXJpc2UobWVhbi5sZW5ndGg9c3VtKG4qbmNoYXIobmFtZSkpL3N1bShuKSkgJT4lCiAgZ2dwbG90KGFlcyh4PXllYXIsIHk9bWVhbi5sZW5ndGgsIGNvbG91cj1zZXgpKSArIGdlb21fbGluZSgpCmBgYAoKTWFqYSdzIGludmVzdGlnYXRpb24gb2YgQ2hhbnRhbCB2YXJpYW50cwoKKGkpIHRlc3RpbmcgdGhlIHJlZ3VsYXIgZXhwcmVzc2lvbjoKYGBge3J9CmJhYnluYW1lcyRuYW1lICU+JSB1bmlxdWUoKSAlPiUgc3RyX3N1YnNldCgiW1N8QytdYz8raD8rYStuezEsfSt0K1thezEsfWV7MSx9XStsezEsfStlPyIpIApgYGAKCihpaSkgcGxvdHRpbmc6CmBgYHtyfQpiYWJ5bmFtZXMgJT4lIAogIG11dGF0ZShjaGFudHZhcnM9c3RyX2V4dHJhY3QobmFtZSwgIltTfEMrXWM/K2g/K2ErbnsxLH0rdCtbYXsxLH1lezEsfV0rbHsxLH0rZT8iKSkgJT4lIAogIGZpbHRlcighaXMubmEoY2hhbnR2YXJzKSkgJT4lIAogIGdyb3VwX2J5KHllYXIsIHNleCwgY2hhbnR2YXJzKSAlPiUgCiAgc3VtbWFyaXNlKHRvdGFsPXN1bShuKSkgJT4lIAogIGdncGxvdChhZXMoeD15ZWFyLCB5PWxvZzEwKHRvdGFsKSwgY29sb3VyPXNleCkpICsgZ2VvbV9saW5lKCkgKyBmYWNldF93cmFwKH4gY2hhbnR2YXJzKQpgYGAKCkxpbm5lYSdzIEdhbWUgb2YgVGhyb25lcyBwbG90LiBOb3RlIHRoZSB1c2Ugb2YgYGFubm90YXRlYCB0byBwdXQgdGhlIGxhYmVsIG9udG8gYW4gaW5kZXBlbmRlbnQgbGF5ZXI6CmBgYHtyfQpmaWx0ZXIoYmFieW5hbWVzLCBuYW1lPT0iRGFlbmVyeXMiIHwgbmFtZT09IkNlcnNlaSIgfCBuYW1lPT0iQXJ5YSIgfCBuYW1lPT0iU2Fuc2EiIHwgbmFtZT09Ik1hcmdhZXJ5IiB8IG5hbWU9PSJZZ3JpdHRlIiB8IG5hbWU9PSJCcmllbm5lIiB8IG5hbWU9PSJSb2JiIiB8IG5hbWU9PSJUaGVvbiIgfCBuYW1lPT0iU2FuZG9yIiB8IG5hbWU9PSJUb3JtdW5kIiB8IG5hbWU9PSJHZW5kcnkiIHwgbmFtZT09IkpvcmFoIiB8IG5hbWU9PSJKb2ZmcmV5IiklPiUgZmlsdGVyKHllYXIlaW4lMjAwMDoyMDE1KSAlPiUgZ2dwbG90KGFlcyh4PXllYXIsIHk9bG9nKG4pLCBjb2xvdXI9bmFtZSwgbGluZXR5cGU9c2V4KSkgKyBnZW9tX2xpbmUoKSArIGFubm90YXRlKCJ0ZXh0IiwgaGp1c3Q9MCwgbGFiZWw9IkdvVCByZWxlYXNlIiwgeD0yMDExLCB5PTUuMikgKyBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQ9MjAxMSkKYGBgCgpBIHNsaWdodCByZXdvcmtpbmcgb2YgTWFyYydzIGludmVzdGlnYXRpb24gb2YgcHJlc2lkZW50cyBhbmQgc3BvdXNlcy4gTm90ZQotIGFkZGVkIGEgYHJvbGVgIGNvbHVtbnMKLSB0aGUgbGFiZWwgZGF0YSBpcyBpbiBhIGluZGVwZW5kZW50IGRhdGEgZnJhbWUgZnJvbSB0aGUgdGltZSBzZXJpZXMvcG9wdWxhcml0eSBkYXRhCmBgYHtyfQpsaWJyYXJ5KGJhYnluYW1lcykKYmFieW5hbWVzICU+JQogICNmaW5kIG91dCBhbGwgdGhlIGJveXMgbmFtZWQgR2VvcmdlLCBCaWxsLCBhbmQgQmFyYWNrLiBGaW5kIG91dCBhbGwgdGhlIGdpcmxzIG5hbWVkIEJhcmJhcmEsIEhpbGxhcnksIExhdXJhLCBhbmQgTWljaGVsbGUuCiAgZmlsdGVyKChuYW1lICVpbiUgYygiR2VvcmdlIiwiQmlsbCIsIkJhcmFjayIpICYgc2V4PT0iTSIpfChuYW1lICVpbiUgYygiQmFyYmFyYSIsIkhpbGxhcnkiLCJMYXVyYSIsIk1pY2hlbGxlIikgJiBzZXg9PSJGIikpICU+JQogICNvbmx5IHRha2UgdGhlIG9ic2VydmF0aW9ucyBmcm9tIDE5ODAgdG8gMjAxNSAodGFrZSBhIGJpdCBiZWZvcmUgMTk4OSBhcyBhIGJ1ZmZlcikuIAogIGZpbHRlcih5ZWFyICVpbiUgYygxOTgwOjIwMTUpKSAlPiUKICBtdXRhdGUocm9sZT1pZl9lbHNlKHNleD09Ik0iLCAiUHJlc2lkZW50IiwgIlNwb3VzZSIpKSAlPiUKICAjdXNlIGdlb21fdGV4dCB0byBzaG93IHRoZSBzdGFydGluZyBwb2ludCBvZiBlYWNoIGZpcnN0IGxhZHkgaW4gdGhlIHRpbWVsaW5lIGFuZCBzeW5jIHRoZSBjb2xvcnMKICBnZ3Bsb3QoYWVzKHg9eWVhciwgeT1sb2cxMChuKSwgY29sb3VyPW5hbWUpKSArCiAgZ2VvbV9saW5lKCkgK2ZhY2V0X3dyYXAofnJvbGUpIC0+IHAKCmxhYmVsLmRhdGEgPC0gdGliYmxlKAogIG5hbWU9YygiR2VvcmdlIiwiQmlsbCIsIkdlb3JnZSIsIkJhcmFjayIsIkJhcmJhcmEiLCJIaWxsYXJ5IiwiTGF1cmEiLCJNaWNoZWxsZSIpLAogIHNleD1jKCJNIiwiTSIsIk0iLCJNIiwiRiIsIkYiLCJGIiwiRiIpLAogIHllYXI9YygxOTg5LCAxOTkzLCAyMDAxLCAyMDA5LCAxOTg5LCAxOTkzLCAyMDAxLCAyMDA5KSwKICB5LnBvcz1jKDMuNCwyLjMsMy42NSwyLjEsMy4zNSwzLjU1LDMuMzUsMy43NSkKKSAlPiUgbXV0YXRlKHJvbGU9aWZfZWxzZShzZXg9PSJNIiwgIlByZXNpZGVudCIsICJTcG91c2UiKSkKcCArIGdlb21fdGV4dChkYXRhPWxhYmVsLmRhdGEsIGFlcyh4PXllYXIsIHk9eS5wb3MsIGxhYmVsPW5hbWUpKSArIGd1aWRlcyhjb2xvdXI9RkFMU0UpCmBgYAoKUmltYSdzIGxvb2sgYXQgSGlsbGFyaWVzIGFuZCBNb25pY2FzLCBhbGxvd2luZyBmb3IgdmFyaWFudCBzcGVsbGluZy4KLSBgc3RyX2V4dHJhY3RgIHRvIGZpbHRlciBvdXQgb25seSB0aGUgcm93cyB3ZSdyZSBpbnRlcmVzdGVkIGluCi0gYHN0cl9zdWJgIHRvIGNoYW5nZSBub3JtYWxpc2UgYWxsIHRoZSBuYW1lcyB0byAiSCIgYW5kICJNIgotIGByZWNvZGVgIHRvIGNoYW5nZSB0aGUgIkgiIGFuZCAiTSIgdmFsdWVzIHRvIHNvbWV0aGluZyBtb3JlIHNlbGYtZXhwbGFuYXRvcnkKYGBge3J9CmJhYnluYW1lcyAlPiUKICBmaWx0ZXIobmFtZSAlaW4lIHN0cl9leHRyYWN0KG5hbWUsICJIaWxsP2FyeXxNb25pW2NrXWEiKSkgJT4lCiAgZmlsdGVyKHNleD09IkYiKSAlPiUKICBtdXRhdGUobmFtZT1zdHJfc3ViKG5hbWUsIDEsIDEpKSAlPiUKICBtdXRhdGUobmFtZT1yZWNvZGUobmFtZSwgSD0iSGlsbD9hcnkiLCBNPSJNb25pW2NrXWEiKSkgJT4lCiAgZ3JvdXBfYnkoeWVhciwgc2V4LCBuYW1lKSAlPiUKICBzdW1tYXJpc2UodG90YWw9c3VtKG4pKSAlPiUKICBnZ3Bsb3QoYWVzKHg9eWVhciwgeT10b3RhbCwgY29sb3VyPW5hbWUpKSArIGdlb21fbGluZSgpCmBgYA==