Overview

In June 2016, the United Kingdom (UK) held a referendum to determine whether the country would “Remain” in the European Union (EU) or “Leave” the EU. This referendum is commonly known as Brexit. Although the media and others interpreted poll results as forecasting “Remain” ( 𝑝>0.5) , the actual proportion that voted “Remain” was only 48.1% (𝑝=0.481) and the UK thus voted to leave the EU. Pollsters in the UK were criticized for overestimating support for “Remain”.

# suggested libraries and options
library(tidyverse)
package ‘tidyverse’ was built under R version 4.0.1Registered S3 methods overwritten by 'dbplyr':
  method         from
  print.tbl_lazy     
  print.tbl_sql      
── Attaching packages ──────── tidyverse 1.3.0 ──
✓ ggplot2 3.3.2     ✓ purrr   0.3.4
✓ tibble  3.0.1     ✓ dplyr   1.0.0
✓ tidyr   1.1.0     ✓ stringr 1.4.0
✓ readr   1.3.1     ✓ forcats 0.5.0
package ‘ggplot2’ was built under R version 4.0.2package ‘readr’ was built under R version 4.0.2── Conflicts ─────────── tidyverse_conflicts() ──
x dplyr::filter() masks stats::filter()
x dplyr::lag()    masks stats::lag()
options(digits = 3)

# load brexit_polls object
library(dslabs)
package ‘dslabs’ was built under R version 4.0.2
data(brexit_polls)

Define 𝑝=0.481 as the actual percent voting “Remain” on the Brexit referendum and 𝑑=2𝑝−1=−0.038 as the actual spread of the Brexit referendum with “Remain” defined as the positive outcome:

p <- 0.481    # official proportion voting "Remain"
d <- 2*p-1    # official spread

Question 1: Expected value and standard error of a poll

The final proportion of voters choosing “Remain” was 𝑝=0.481 . Consider a poll with a sample of 𝑁=1500 voters.

What is the expected total number of voters in the sample choosing “Remain”?

N <- 1500
expected <- N*p
expected
[1] 722

What is the standard error of the total number of voters in the sample choosing “Remain”?

se_remain <- sqrt(N * p * (1 - p))
se_remain
[1] 19.4

What is the expected value of 𝑋̂ , the proportion of “Remain” voters?

x_hat <- p
x_hat
[1] 0.481

What is the standard error of 𝑋̂ , the proportion of “Remain” voters?

standard_error_x_hat <- sqrt(x_hat * (1-x_hat)/N)
standard_error_x_hat
[1] 0.0129

What is the expected value of 𝑑 , the spread between the proportion of “Remain” voters and “Leave” voters?

d <- 2*p -1
d
[1] -0.038

What is the standard error of 𝑑 , the spread between the proportion of “Remain” voters and “Leave” voters?

se_d <- 2 * standard_error_x_hat
se_d
[1] 0.0258

Question 2: Actual Brexit poll estimates

Load and inspect the brexit_polls dataset from dslabs, which contains actual polling data for the 6 months before the Brexit vote. Raw proportions of voters preferring “Remain”, “Leave”, and “Undecided” are available (remain, leave, undecided) The spread is also available (spread), which is the difference in the raw proportion of voters choosing “Remain” and the raw proportion choosing “Leave”.

Calculate x_hat for each poll, the estimate of the proportion of voters choosing “Remain” on the referendum day ( 𝑝=0.481 ), given the observed spread and the relationship 𝑑̂ =2𝑋̂ −1 . Use mutate() to add a variable x_hat to the brexit_polls object by filling in the skeleton code below:

What is the average of the observed spreads (spread)?

brexit_polls <- brexit_polls %>% 
                  mutate(x_hat = (spread + 1) /2)
mean(brexit_polls$spread)
[1] 0.0201

What is the standard deviation of the observed spreads?

sd(brexit_polls$spread)
[1] 0.0588

What is the average of x_hat, the estimates of the parameter

mean(brexit_polls$x_hat)
[1] 0.51

What is the standard deviation of x_hat?

sd(brexit_polls$x_hat)
[1] 0.0294

Question 3: Confidence interval of a Brexit poll

Consider the first poll in brexit_polls, a YouGov poll run on the same day as the Brexit referendum:

you_gov <- brexit_polls[1,]

Use qnorm() to compute the 95% confidence interval for 𝑋̂ .

What is the lower bound of the 95% confidence interval?

x_hat <- 0.52
N <- 4722
se <- sqrt(x_hat * (1-x_hat) / N)
ci <- c(x_hat - qnorm(0.975) * se, x_hat + qnorm(0.975) * se)
ci
[1] 0.506 0.534

Question 4: Confidence intervals for polls in June

Create the data frame june_polls containing only Brexit polls ending in June 2016 (enddate of “2016-06-01” and later). We will calculate confidence intervals for all polls and determine how many cover the true value of 𝑑.

First, use mutate() to calculate a plug-in estimate se_x_hat for the standard error of the estimate SE1 for each poll given its sample size and value of 𝑋̂ (x_hat). Second, use mutate() to calculate an estimate for the standard error of the spread for each poll given the value of se_x_hat. Then, use mutate() to calculate upper and lower bounds for 95% confidence intervals of the spread. Last, add a column hit that indicates whether the confidence interval for each poll covers the correct spread 𝑑=−0.038.

How many polls are in june_polls?

june_polls <- brexit_polls %>% filter(enddate >= '2016-06-1')
nrow(june_polls)
[1] 32

What proportion of polls have a confidence interval that covers the value 0?

d <- -0.38
june_polls <- june_polls %>%
  mutate(se_x_hat = sqrt(x_hat * (1-x_hat)/ samplesize), se_spread = 2*se_x_hat,
         lower = spread - qnorm(0.975) * se_spread,
         upper = spread + qnorm(0.975) * se_spread,
         hit = (lower < 2*p-1 & upper > 2*p-1))
mean(june_polls$lower < 0 & june_polls$upper > 0)
[1] 0.625
mean(june_polls$lower > 0 & june_polls$upper > 0)
[1] 0.125

What proportion of polls have a confidence interval covering the true value of 𝑑 ?

head(june_polls)
mean(june_polls$hit)
[1] 0.562

Question 5: Hit rate by pollster

Group and summarize the june_polls object by pollster to find the proportion of hits for each pollster and the number of polls per pollster. Use arrange() to sort by hit rate.

june_polls_grouped <- june_polls %>% group_by(pollster) %>% summarize(hits = n())
`summarise()` ungrouping output (override with `.groups` argument)
head(june_polls_grouped)

Question 6: Boxplot of Brexit polls by poll type

ggplot(data = june_polls, aes(poll_type, spread)) + geom_boxplot()

Question 7: Combined spread across poll type

Calculate the confidence intervals of the spread combined across all polls in june_polls, grouping by poll type. Recall that to determine the standard error of the spread, you will need to double the standard error of the estimate.

Use this code (which determines the total sample size per poll type, gives each spread estimate a weight based on the poll’s sample size, and adds an estimate of p from the combined spread) to begin your analysis:

combined_by_type <- june_polls %>%
        group_by(poll_type) %>%
        summarize(N = sum(samplesize),
                  spread = sum(spread*samplesize)/N,
                  p_hat = (spread + 1)/2,
                  se_spread = 2 * sqrt(p_hat*(1-p_hat)/N),
                  lower = spread - qnorm(0.975) * se_spread,
                  upper = spread + qnorm(0.975) * se_spread)
`summarise()` ungrouping output (override with `.groups` argument)
combined_by_type

Question 8: Interpreting combined spread estimates across poll type

Question 9: Chi-squared p-value

Define brexit_hit, with the following code, which computes the confidence intervals for all Brexit polls in 2016 and then calculates whether the confidence interval covers the actual value of the spread 𝑑=−0.038 :

brexit_hit <- brexit_polls %>%
  mutate(p_hat = (spread + 1)/2,
         se_spread = 2*sqrt(p_hat*(1-p_hat)/samplesize),
         spread_lower = spread - qnorm(.975)*se_spread,
         spread_upper = spread + qnorm(.975)*se_spread,
         hit = spread_lower < -0.038 & spread_upper > -0.038) %>%
  select(poll_type, hit)

Use brexit_hit to make a two-by-two table of poll type and hit status. Then use the chisq.test() function to perform a chi-squared test to determine whether the difference in hit rate is significant.

What is the p-value of the chi-squared test comparing the hit rate of online and telephone polls?

head(brexit_hit)
totals <- brexit_hit %>% summarize(online_yes = sum(hit == 'TRUE' & poll_type == 'Online'), online_no = sum(hit == 'FALSE' & poll_type == 'Online'),
                                   telephone_yes = sum(hit == 'TRUE' & poll_type == 'Telephone'),
                                   telephone_no = sum(hit == 'FALSE' & poll_type == 'Telephone'))
totals

two_by_two <- tibble(hit = c('yes','no'),
                     online = c(totals$online_yes, totals$online_no),
                     telephone = c(totals$telephone_yes, totals$telephone_no))
two_by_two
chisq_test <- two_by_two %>% select(-hit) %>% chisq.test()
chisq_test

    Pearson's Chi-squared test with Yates'
    continuity correction

data:  .
X-squared = 11, df = 1, p-value = 0.001

10 Question 10: Odds ratio of online and telephone poll hit rate

two_by_two$online[1]
[1] 48
two_by_two$online[2]
[1] 37
two_by_two$telephone[1]
[1] 10
two_by_two$telephone[2]
[1] 32
online_yes_odds <- (two_by_two$online[1] / sum(two_by_two$online)) / (two_by_two$online[2] / sum(two_by_two$online))
online_yes_odds
[1] 1.3

Calculate the odds that a telephone poll generates a confidence interval that covers the actual value of the spread.

telephone_yes_odds <- (two_by_two$telephone[1] / sum(two_by_two$telephone)) / (two_by_two$telephone[2] / sum(two_by_two$telephone))
telephone_yes_odds
[1] 0.312

Calculate the odds ratio to determine how many times larger the odds are for online polls to hit versus telephone polls.

online_yes_odds / telephone_yes_odds
[1] 4.15

Question 11: Plotting spread over time

Use brexit_polls to make a plot of the spread (spread) over time (enddate) colored by poll type (poll_type). Use geom_smooth() with method = “loess” to plot smooth curves with a span of 0.4. Include the individual data points colored by poll type. Add a horizontal line indicating the final value of 𝑑=−.038 .

ggplot(data = brexit_polls, aes(enddate, spread, color = poll_type)) + geom_smooth(method = "loess", span = 0.4) + geom_point() + geom_hline(yintercept =  -0.038)

Question 12: Plotting raw percentages over time

Use the following code to create the object brexit_long, which has a column vote containing the three possible votes on a Brexit poll (“remain”, “leave”, “undecided”) and a column proportion containing the raw proportion choosing that vote option on the given poll:

brexit_long <- brexit_polls %>%
    gather(vote, proportion, "remain":"undecided") %>%
    mutate(vote = factor(vote))
head(brexit_long)

Make a graph of proportion over time colored by vote. Add a smooth trendline with geom_smooth() and method = “loess” with a span of 0.3.

ggplot(data = brexit_long, aes(enddate, proportion, color = vote)) + geom_smooth(method = "loess", span = 0.3) + geom_point()


  1. 𝑋

LS0tCnRpdGxlOiAiQnJleGl0IFBvbGwgQW5hbHlzaXMiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCiMgT3ZlcnZpZXcKCkluIEp1bmUgMjAxNiwgdGhlIFVuaXRlZCBLaW5nZG9tIChVSykgaGVsZCBhIHJlZmVyZW5kdW0gdG8gZGV0ZXJtaW5lIHdoZXRoZXIgdGhlIGNvdW50cnkgd291bGQgIlJlbWFpbiIgaW4gdGhlIEV1cm9wZWFuIFVuaW9uIChFVSkgb3IgIkxlYXZlIiB0aGUgRVUuIFRoaXMgcmVmZXJlbmR1bSBpcyBjb21tb25seSBrbm93biBhcyBCcmV4aXQuIEFsdGhvdWdoIHRoZSBtZWRpYSBhbmQgb3RoZXJzIGludGVycHJldGVkIHBvbGwgcmVzdWx0cyBhcyBmb3JlY2FzdGluZyAiUmVtYWluIiAoIPCdkZ0+MC41KSAsIHRoZSBhY3R1YWwgcHJvcG9ydGlvbiB0aGF0IHZvdGVkICJSZW1haW4iIHdhcyBvbmx5IDQ4LjElICAo8J2RnT0wLjQ4MSkgIGFuZCB0aGUgVUsgdGh1cyB2b3RlZCB0byBsZWF2ZSB0aGUgRVUuIFBvbGxzdGVycyBpbiB0aGUgVUsgd2VyZSBjcml0aWNpemVkIGZvciBvdmVyZXN0aW1hdGluZyBzdXBwb3J0IGZvciAiUmVtYWluIi4gCgoKYGBge3J9CiMgc3VnZ2VzdGVkIGxpYnJhcmllcyBhbmQgb3B0aW9ucwpsaWJyYXJ5KHRpZHl2ZXJzZSkKb3B0aW9ucyhkaWdpdHMgPSAzKQoKIyBsb2FkIGJyZXhpdF9wb2xscyBvYmplY3QKbGlicmFyeShkc2xhYnMpCmRhdGEoYnJleGl0X3BvbGxzKQpgYGAKCkRlZmluZSAg8J2RnT0wLjQ4MSAgYXMgdGhlIGFjdHVhbCBwZXJjZW50IHZvdGluZyAiUmVtYWluIiBvbiB0aGUgQnJleGl0IHJlZmVyZW5kdW0gYW5kICDwnZGRPTLwnZGd4oiSMT3iiJIwLjAzOCAgYXMgdGhlIGFjdHVhbCBzcHJlYWQgb2YgdGhlIEJyZXhpdCByZWZlcmVuZHVtIHdpdGggIlJlbWFpbiIgZGVmaW5lZCBhcyB0aGUgcG9zaXRpdmUgb3V0Y29tZToKCmBgYHtyfQpwIDwtIDAuNDgxICAgICMgb2ZmaWNpYWwgcHJvcG9ydGlvbiB2b3RpbmcgIlJlbWFpbiIKZCA8LSAyKnAtMSAgICAjIG9mZmljaWFsIHNwcmVhZApgYGAKCiMgUXVlc3Rpb24gMTogRXhwZWN0ZWQgdmFsdWUgYW5kIHN0YW5kYXJkIGVycm9yIG9mIGEgcG9sbCAKClRoZSBmaW5hbCBwcm9wb3J0aW9uIG9mIHZvdGVycyBjaG9vc2luZyAiUmVtYWluIiB3YXMgIPCdkZ09MC40ODEgLiBDb25zaWRlciBhIHBvbGwgd2l0aCBhIHNhbXBsZSBvZiAg8J2RgT0xNTAwICB2b3RlcnMuCgpXaGF0IGlzIHRoZSBleHBlY3RlZCB0b3RhbCBudW1iZXIgb2Ygdm90ZXJzIGluIHRoZSBzYW1wbGUgY2hvb3NpbmcgIlJlbWFpbiI/CgpgYGB7cn0KTiA8LSAxNTAwCmV4cGVjdGVkIDwtIE4qcApleHBlY3RlZApgYGAKCldoYXQgaXMgdGhlIHN0YW5kYXJkIGVycm9yIG9mIHRoZSB0b3RhbCBudW1iZXIgb2Ygdm90ZXJzIGluIHRoZSBzYW1wbGUgY2hvb3NpbmcgIlJlbWFpbiI/CgpgYGB7cn0Kc2VfcmVtYWluIDwtIHNxcnQoTiAqIHAgKiAoMSAtIHApKQpzZV9yZW1haW4KYGBgCldoYXQgaXMgdGhlIGV4cGVjdGVkIHZhbHVlIG9mICDwnZGLzIIgICwgdGhlIHByb3BvcnRpb24gb2YgIlJlbWFpbiIgdm90ZXJzPwoKYGBge3J9CnhfaGF0IDwtIHAKeF9oYXQKYGBgCgpXaGF0IGlzIHRoZSBzdGFuZGFyZCBlcnJvciBvZiAg8J2Ri8yCICAsIHRoZSBwcm9wb3J0aW9uIG9mICJSZW1haW4iIHZvdGVycz8KCmBgYHtyfQpzdGFuZGFyZF9lcnJvcl94X2hhdCA8LSBzcXJ0KHhfaGF0ICogKDEteF9oYXQpL04pCnN0YW5kYXJkX2Vycm9yX3hfaGF0CmBgYAoKV2hhdCBpcyB0aGUgZXhwZWN0ZWQgdmFsdWUgb2YgIPCdkZEgLCB0aGUgc3ByZWFkIGJldHdlZW4gdGhlIHByb3BvcnRpb24gb2YgIlJlbWFpbiIgdm90ZXJzIGFuZCAiTGVhdmUiIHZvdGVycz8KCmBgYHtyfQpkIDwtIDIqcCAtMQpkCmBgYAoKV2hhdCBpcyB0aGUgc3RhbmRhcmQgZXJyb3Igb2YgIPCdkZEgLCB0aGUgc3ByZWFkIGJldHdlZW4gdGhlIHByb3BvcnRpb24gb2YgIlJlbWFpbiIgdm90ZXJzIGFuZCAiTGVhdmUiIHZvdGVycz8KCmBgYHtyfQpzZV9kIDwtIDIgKiBzdGFuZGFyZF9lcnJvcl94X2hhdApzZV9kCmBgYAoKIyBRdWVzdGlvbiAyOiBBY3R1YWwgQnJleGl0IHBvbGwgZXN0aW1hdGVzCgpMb2FkIGFuZCBpbnNwZWN0IHRoZSBicmV4aXRfcG9sbHMgZGF0YXNldCBmcm9tIGRzbGFicywgd2hpY2ggY29udGFpbnMgYWN0dWFsIHBvbGxpbmcgZGF0YSBmb3IgdGhlIDYgbW9udGhzIGJlZm9yZSB0aGUgQnJleGl0IHZvdGUuIFJhdyBwcm9wb3J0aW9ucyBvZiB2b3RlcnMgcHJlZmVycmluZyAiUmVtYWluIiwgIkxlYXZlIiwgYW5kICJVbmRlY2lkZWQiIGFyZSBhdmFpbGFibGUgKHJlbWFpbiwgbGVhdmUsIHVuZGVjaWRlZCkgVGhlIHNwcmVhZCBpcyBhbHNvIGF2YWlsYWJsZSAoc3ByZWFkKSwgd2hpY2ggaXMgdGhlIGRpZmZlcmVuY2UgaW4gdGhlIHJhdyBwcm9wb3J0aW9uIG9mIHZvdGVycyBjaG9vc2luZyAiUmVtYWluIiBhbmQgdGhlIHJhdyBwcm9wb3J0aW9uIGNob29zaW5nICJMZWF2ZSIuCgpDYWxjdWxhdGUgeF9oYXQgZm9yIGVhY2ggcG9sbCwgdGhlIGVzdGltYXRlIG9mIHRoZSBwcm9wb3J0aW9uIG9mIHZvdGVycyBjaG9vc2luZyAiUmVtYWluIiBvbiB0aGUgcmVmZXJlbmR1bSBkYXkgKCDwnZGdPTAuNDgxICksIGdpdmVuIHRoZSBvYnNlcnZlZCBzcHJlYWQgYW5kIHRoZSByZWxhdGlvbnNoaXAgIPCdkZHMgiA9MvCdkYvMgiDiiJIxIC4gVXNlIG11dGF0ZSgpIHRvIGFkZCBhIHZhcmlhYmxlIHhfaGF0IHRvIHRoZSBicmV4aXRfcG9sbHMgb2JqZWN0IGJ5IGZpbGxpbmcgaW4gdGhlIHNrZWxldG9uIGNvZGUgYmVsb3c6CgpXaGF0IGlzIHRoZSBhdmVyYWdlIG9mIHRoZSBvYnNlcnZlZCBzcHJlYWRzIChzcHJlYWQpPwoKCmBgYHtyfQpicmV4aXRfcG9sbHMgPC0gYnJleGl0X3BvbGxzICU+JSAKICAgICAgICAgICAgICAgICAgbXV0YXRlKHhfaGF0ID0gKHNwcmVhZCArIDEpIC8yKQptZWFuKGJyZXhpdF9wb2xscyRzcHJlYWQpCmBgYAoKV2hhdCBpcyB0aGUgc3RhbmRhcmQgZGV2aWF0aW9uIG9mIHRoZSBvYnNlcnZlZCBzcHJlYWRzPwoKYGBge3J9CnNkKGJyZXhpdF9wb2xscyRzcHJlYWQpCmBgYAoKV2hhdCBpcyB0aGUgYXZlcmFnZSBvZiB4X2hhdCwgdGhlIGVzdGltYXRlcyBvZiB0aGUgcGFyYW1ldGVyICAKCmBgYHtyfQptZWFuKGJyZXhpdF9wb2xscyR4X2hhdCkKYGBgCgpXaGF0IGlzIHRoZSBzdGFuZGFyZCBkZXZpYXRpb24gb2YgeF9oYXQ/CgpgYGB7cn0Kc2QoYnJleGl0X3BvbGxzJHhfaGF0KQpgYGAKCiMgUXVlc3Rpb24gMzogQ29uZmlkZW5jZSBpbnRlcnZhbCBvZiBhIEJyZXhpdCBwb2xsCgpDb25zaWRlciB0aGUgZmlyc3QgcG9sbCBpbiBicmV4aXRfcG9sbHMsIGEgWW91R292IHBvbGwgcnVuIG9uIHRoZSBzYW1lIGRheSBhcyB0aGUgQnJleGl0IHJlZmVyZW5kdW06CgpgYGB7cn0KeW91X2dvdiA8LSBicmV4aXRfcG9sbHNbMSxdCmBgYAoKVXNlIHFub3JtKCkgdG8gY29tcHV0ZSB0aGUgOTUlIGNvbmZpZGVuY2UgaW50ZXJ2YWwgZm9yICDwnZGLzIIgIC4KCldoYXQgaXMgdGhlIGxvd2VyIGJvdW5kIG9mIHRoZSA5NSUgY29uZmlkZW5jZSBpbnRlcnZhbD8KCmBgYHtyfQp4X2hhdCA8LSAwLjUyCk4gPC0gNDcyMgpzZSA8LSBzcXJ0KHhfaGF0ICogKDEteF9oYXQpIC8gTikKY2kgPC0gYyh4X2hhdCAtIHFub3JtKDAuOTc1KSAqIHNlLCB4X2hhdCArIHFub3JtKDAuOTc1KSAqIHNlKQpjaQpgYGAKCiMgUXVlc3Rpb24gNDogQ29uZmlkZW5jZSBpbnRlcnZhbHMgZm9yIHBvbGxzIGluIEp1bmUKCkNyZWF0ZSB0aGUgZGF0YSBmcmFtZSBqdW5lX3BvbGxzIGNvbnRhaW5pbmcgb25seSBCcmV4aXQgcG9sbHMgZW5kaW5nIGluIEp1bmUgMjAxNiAoZW5kZGF0ZSBvZiAiMjAxNi0wNi0wMSIgYW5kIGxhdGVyKS4gV2Ugd2lsbCBjYWxjdWxhdGUgY29uZmlkZW5jZSBpbnRlcnZhbHMgZm9yIGFsbCBwb2xscyBhbmQgZGV0ZXJtaW5lIGhvdyBtYW55IGNvdmVyIHRoZSB0cnVlIHZhbHVlIG9mIPCdkZEuCgpGaXJzdCwgdXNlIG11dGF0ZSgpIHRvIGNhbGN1bGF0ZSBhIHBsdWctaW4gZXN0aW1hdGUgc2VfeF9oYXQgZm9yIHRoZSBzdGFuZGFyZCBlcnJvciBvZiB0aGUgZXN0aW1hdGUgU0VeW/CdkYtdIGZvciBlYWNoIHBvbGwgZ2l2ZW4gaXRzIHNhbXBsZSBzaXplIGFuZCB2YWx1ZSBvZiDwnZGLzIIgICh4X2hhdCkuIFNlY29uZCwgdXNlIG11dGF0ZSgpIHRvIGNhbGN1bGF0ZSBhbiBlc3RpbWF0ZSBmb3IgdGhlIHN0YW5kYXJkIGVycm9yIG9mIHRoZSBzcHJlYWQgZm9yIGVhY2ggcG9sbCBnaXZlbiB0aGUgdmFsdWUgb2Ygc2VfeF9oYXQuIFRoZW4sIHVzZSBtdXRhdGUoKSB0byBjYWxjdWxhdGUgdXBwZXIgYW5kIGxvd2VyIGJvdW5kcyBmb3IgOTUlIGNvbmZpZGVuY2UgaW50ZXJ2YWxzIG9mIHRoZSBzcHJlYWQuIExhc3QsIGFkZCBhIGNvbHVtbiBoaXQgdGhhdCBpbmRpY2F0ZXMgd2hldGhlciB0aGUgY29uZmlkZW5jZSBpbnRlcnZhbCBmb3IgZWFjaCBwb2xsIGNvdmVycyB0aGUgY29ycmVjdCBzcHJlYWQg8J2RkT3iiJIwLjAzOC4KCkhvdyBtYW55IHBvbGxzIGFyZSBpbiBqdW5lX3BvbGxzPwoKYGBge3J9Cmp1bmVfcG9sbHMgPC0gYnJleGl0X3BvbGxzICU+JSBmaWx0ZXIoZW5kZGF0ZSA+PSAnMjAxNi0wNi0xJykKbnJvdyhqdW5lX3BvbGxzKQpgYGAKCldoYXQgcHJvcG9ydGlvbiBvZiBwb2xscyBoYXZlIGEgY29uZmlkZW5jZSBpbnRlcnZhbCB0aGF0IGNvdmVycyB0aGUgdmFsdWUgMD8KCmBgYHtyfQpkIDwtIC0wLjM4Cmp1bmVfcG9sbHMgPC0ganVuZV9wb2xscyAlPiUKICBtdXRhdGUoc2VfeF9oYXQgPSBzcXJ0KHhfaGF0ICogKDEteF9oYXQpLyBzYW1wbGVzaXplKSwgc2Vfc3ByZWFkID0gMipzZV94X2hhdCwKICAgICAgICAgbG93ZXIgPSBzcHJlYWQgLSBxbm9ybSgwLjk3NSkgKiBzZV9zcHJlYWQsCiAgICAgICAgIHVwcGVyID0gc3ByZWFkICsgcW5vcm0oMC45NzUpICogc2Vfc3ByZWFkLAogICAgICAgICBoaXQgPSAobG93ZXIgPCAyKnAtMSAmIHVwcGVyID4gMipwLTEpKQptZWFuKGp1bmVfcG9sbHMkbG93ZXIgPCAwICYganVuZV9wb2xscyR1cHBlciA+IDApCmBgYAoKYGBge3J9Cm1lYW4oanVuZV9wb2xscyRsb3dlciA+IDAgJiBqdW5lX3BvbGxzJHVwcGVyID4gMCkKYGBgCgpXaGF0IHByb3BvcnRpb24gb2YgcG9sbHMgaGF2ZSBhIGNvbmZpZGVuY2UgaW50ZXJ2YWwgY292ZXJpbmcgdGhlIHRydWUgdmFsdWUgb2YgIPCdkZEgPwoKYGBge3J9CmhlYWQoanVuZV9wb2xscykKbWVhbihqdW5lX3BvbGxzJGhpdCkKYGBgCgojIFF1ZXN0aW9uIDU6IEhpdCByYXRlIGJ5IHBvbGxzdGVyCgpHcm91cCBhbmQgc3VtbWFyaXplIHRoZSBqdW5lX3BvbGxzIG9iamVjdCBieSBwb2xsc3RlciB0byBmaW5kIHRoZSBwcm9wb3J0aW9uIG9mIGhpdHMgZm9yIGVhY2ggcG9sbHN0ZXIgYW5kIHRoZSBudW1iZXIgb2YgcG9sbHMgcGVyIHBvbGxzdGVyLiBVc2UgYXJyYW5nZSgpIHRvIHNvcnQgYnkgaGl0IHJhdGUuCgpgYGB7cn0KanVuZV9wb2xsc19ncm91cGVkIDwtIGp1bmVfcG9sbHMgJT4lIGdyb3VwX2J5KHBvbGxzdGVyKSAlPiUgc3VtbWFyaXplKGhpdHMgPSBuKCkpCmhlYWQoanVuZV9wb2xsc19ncm91cGVkKQpgYGAKCiMgUXVlc3Rpb24gNjogQm94cGxvdCBvZiBCcmV4aXQgcG9sbHMgYnkgcG9sbCB0eXBlCgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBqdW5lX3BvbGxzLCBhZXMocG9sbF90eXBlLCBzcHJlYWQpKSArIGdlb21fYm94cGxvdCgpCmBgYAoKIyBRdWVzdGlvbiA3OiBDb21iaW5lZCBzcHJlYWQgYWNyb3NzIHBvbGwgdHlwZQoKQ2FsY3VsYXRlIHRoZSBjb25maWRlbmNlIGludGVydmFscyBvZiB0aGUgc3ByZWFkIGNvbWJpbmVkIGFjcm9zcyBhbGwgcG9sbHMgaW4ganVuZV9wb2xscywgZ3JvdXBpbmcgYnkgcG9sbCB0eXBlLiBSZWNhbGwgdGhhdCB0byBkZXRlcm1pbmUgdGhlIHN0YW5kYXJkIGVycm9yIG9mIHRoZSBzcHJlYWQsIHlvdSB3aWxsIG5lZWQgdG8gZG91YmxlIHRoZSBzdGFuZGFyZCBlcnJvciBvZiB0aGUgZXN0aW1hdGUuCgpVc2UgdGhpcyBjb2RlICh3aGljaCBkZXRlcm1pbmVzIHRoZSB0b3RhbCBzYW1wbGUgc2l6ZSBwZXIgcG9sbCB0eXBlLCBnaXZlcyBlYWNoIHNwcmVhZCBlc3RpbWF0ZSBhIHdlaWdodCBiYXNlZCBvbiB0aGUgcG9sbCdzIHNhbXBsZSBzaXplLCBhbmQgYWRkcyBhbiBlc3RpbWF0ZSBvZiBwIGZyb20gdGhlIGNvbWJpbmVkIHNwcmVhZCkgdG8gYmVnaW4geW91ciBhbmFseXNpczoKCmBgYHtyfQpjb21iaW5lZF9ieV90eXBlIDwtIGp1bmVfcG9sbHMgJT4lCiAgICAgICAgZ3JvdXBfYnkocG9sbF90eXBlKSAlPiUKICAgICAgICBzdW1tYXJpemUoTiA9IHN1bShzYW1wbGVzaXplKSwKICAgICAgICAgICAgICAgICAgc3ByZWFkID0gc3VtKHNwcmVhZCpzYW1wbGVzaXplKS9OLAogICAgICAgICAgICAgICAgICBwX2hhdCA9IChzcHJlYWQgKyAxKS8yLAogICAgICAgICAgICAgICAgICBzZV9zcHJlYWQgPSAyICogc3FydChwX2hhdCooMS1wX2hhdCkvTiksCiAgICAgICAgICAgICAgICAgIGxvd2VyID0gc3ByZWFkIC0gcW5vcm0oMC45NzUpICogc2Vfc3ByZWFkLAogICAgICAgICAgICAgICAgICB1cHBlciA9IHNwcmVhZCArIHFub3JtKDAuOTc1KSAqIHNlX3NwcmVhZCkKY29tYmluZWRfYnlfdHlwZQpgYGAKCiMgUXVlc3Rpb24gODogSW50ZXJwcmV0aW5nIGNvbWJpbmVkIHNwcmVhZCBlc3RpbWF0ZXMgYWNyb3NzIHBvbGwgdHlwZQoKIyBRdWVzdGlvbiA5OiBDaGktc3F1YXJlZCBwLXZhbHVlCgpEZWZpbmUgYnJleGl0X2hpdCwgd2l0aCB0aGUgZm9sbG93aW5nIGNvZGUsIHdoaWNoIGNvbXB1dGVzIHRoZSBjb25maWRlbmNlIGludGVydmFscyBmb3IgYWxsIEJyZXhpdCBwb2xscyBpbiAyMDE2IGFuZCB0aGVuIGNhbGN1bGF0ZXMgd2hldGhlciB0aGUgY29uZmlkZW5jZSBpbnRlcnZhbCBjb3ZlcnMgdGhlIGFjdHVhbCB2YWx1ZSBvZiB0aGUgc3ByZWFkICDwnZGRPeKIkjAuMDM4IDoKCmBgYHtyfQpicmV4aXRfaGl0IDwtIGJyZXhpdF9wb2xscyAlPiUKICBtdXRhdGUocF9oYXQgPSAoc3ByZWFkICsgMSkvMiwKICAgICAgICAgc2Vfc3ByZWFkID0gMipzcXJ0KHBfaGF0KigxLXBfaGF0KS9zYW1wbGVzaXplKSwKICAgICAgICAgc3ByZWFkX2xvd2VyID0gc3ByZWFkIC0gcW5vcm0oLjk3NSkqc2Vfc3ByZWFkLAogICAgICAgICBzcHJlYWRfdXBwZXIgPSBzcHJlYWQgKyBxbm9ybSguOTc1KSpzZV9zcHJlYWQsCiAgICAgICAgIGhpdCA9IHNwcmVhZF9sb3dlciA8IC0wLjAzOCAmIHNwcmVhZF91cHBlciA+IC0wLjAzOCkgJT4lCiAgc2VsZWN0KHBvbGxfdHlwZSwgaGl0KQpgYGAKClVzZSBicmV4aXRfaGl0IHRvIG1ha2UgYSB0d28tYnktdHdvIHRhYmxlIG9mIHBvbGwgdHlwZSBhbmQgaGl0IHN0YXR1cy4gVGhlbiB1c2UgdGhlIGNoaXNxLnRlc3QoKSBmdW5jdGlvbiB0byBwZXJmb3JtIGEgY2hpLXNxdWFyZWQgdGVzdCB0byBkZXRlcm1pbmUgd2hldGhlciB0aGUgZGlmZmVyZW5jZSBpbiBoaXQgcmF0ZSBpcyBzaWduaWZpY2FudC4KCldoYXQgaXMgdGhlIHAtdmFsdWUgb2YgdGhlIGNoaS1zcXVhcmVkIHRlc3QgY29tcGFyaW5nIHRoZSBoaXQgcmF0ZSBvZiBvbmxpbmUgYW5kIHRlbGVwaG9uZSBwb2xscz8KCmBgYHtyfQpoZWFkKGJyZXhpdF9oaXQpCnRvdGFscyA8LSBicmV4aXRfaGl0ICU+JSBzdW1tYXJpemUob25saW5lX3llcyA9IHN1bShoaXQgPT0gJ1RSVUUnICYgcG9sbF90eXBlID09ICdPbmxpbmUnKSwgb25saW5lX25vID0gc3VtKGhpdCA9PSAnRkFMU0UnICYgcG9sbF90eXBlID09ICdPbmxpbmUnKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0ZWxlcGhvbmVfeWVzID0gc3VtKGhpdCA9PSAnVFJVRScgJiBwb2xsX3R5cGUgPT0gJ1RlbGVwaG9uZScpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRlbGVwaG9uZV9ubyA9IHN1bShoaXQgPT0gJ0ZBTFNFJyAmIHBvbGxfdHlwZSA9PSAnVGVsZXBob25lJykpCnRvdGFscwoKdHdvX2J5X3R3byA8LSB0aWJibGUoaGl0ID0gYygneWVzJywnbm8nKSwKICAgICAgICAgICAgICAgICAgICAgb25saW5lID0gYyh0b3RhbHMkb25saW5lX3llcywgdG90YWxzJG9ubGluZV9ubyksCiAgICAgICAgICAgICAgICAgICAgIHRlbGVwaG9uZSA9IGModG90YWxzJHRlbGVwaG9uZV95ZXMsIHRvdGFscyR0ZWxlcGhvbmVfbm8pKQp0d29fYnlfdHdvCmNoaXNxX3Rlc3QgPC0gdHdvX2J5X3R3byAlPiUgc2VsZWN0KC1oaXQpICU+JSBjaGlzcS50ZXN0KCkKY2hpc3FfdGVzdApgYGAKCiMgMTAgUXVlc3Rpb24gMTA6IE9kZHMgcmF0aW8gb2Ygb25saW5lIGFuZCB0ZWxlcGhvbmUgcG9sbCBoaXQgcmF0ZSAKCmBgYHtyfQp0d29fYnlfdHdvJG9ubGluZVsxXQp0d29fYnlfdHdvJG9ubGluZVsyXQp0d29fYnlfdHdvJHRlbGVwaG9uZVsxXQp0d29fYnlfdHdvJHRlbGVwaG9uZVsyXQoKb25saW5lX3llc19vZGRzIDwtICh0d29fYnlfdHdvJG9ubGluZVsxXSAvIHN1bSh0d29fYnlfdHdvJG9ubGluZSkpIC8gKHR3b19ieV90d28kb25saW5lWzJdIC8gc3VtKHR3b19ieV90d28kb25saW5lKSkKb25saW5lX3llc19vZGRzCmBgYAoKQ2FsY3VsYXRlIHRoZSBvZGRzIHRoYXQgYSB0ZWxlcGhvbmUgcG9sbCBnZW5lcmF0ZXMgYSBjb25maWRlbmNlIGludGVydmFsIHRoYXQgY292ZXJzIHRoZSBhY3R1YWwgdmFsdWUgb2YgdGhlIHNwcmVhZC4KCmBgYHtyfQp0ZWxlcGhvbmVfeWVzX29kZHMgPC0gKHR3b19ieV90d28kdGVsZXBob25lWzFdIC8gc3VtKHR3b19ieV90d28kdGVsZXBob25lKSkgLyAodHdvX2J5X3R3byR0ZWxlcGhvbmVbMl0gLyBzdW0odHdvX2J5X3R3byR0ZWxlcGhvbmUpKQp0ZWxlcGhvbmVfeWVzX29kZHMKYGBgCgpDYWxjdWxhdGUgdGhlIG9kZHMgcmF0aW8gdG8gZGV0ZXJtaW5lIGhvdyBtYW55IHRpbWVzIGxhcmdlciB0aGUgb2RkcyBhcmUgZm9yIG9ubGluZSBwb2xscyB0byBoaXQgdmVyc3VzIHRlbGVwaG9uZSBwb2xscy4KCmBgYHtyfQpvbmxpbmVfeWVzX29kZHMgLyB0ZWxlcGhvbmVfeWVzX29kZHMKYGBgCgojIFF1ZXN0aW9uIDExOiBQbG90dGluZyBzcHJlYWQgb3ZlciB0aW1lCgpVc2UgYnJleGl0X3BvbGxzIHRvIG1ha2UgYSBwbG90IG9mIHRoZSBzcHJlYWQgKHNwcmVhZCkgb3ZlciB0aW1lIChlbmRkYXRlKSBjb2xvcmVkIGJ5IHBvbGwgdHlwZSAocG9sbF90eXBlKS4gVXNlIGdlb21fc21vb3RoKCkgd2l0aCBtZXRob2QgPSAibG9lc3MiIHRvIHBsb3Qgc21vb3RoIGN1cnZlcyB3aXRoIGEgc3BhbiBvZiAwLjQuIEluY2x1ZGUgdGhlIGluZGl2aWR1YWwgZGF0YSBwb2ludHMgY29sb3JlZCBieSBwb2xsIHR5cGUuIEFkZCBhIGhvcml6b250YWwgbGluZSBpbmRpY2F0aW5nIHRoZSBmaW5hbCB2YWx1ZSBvZiAg8J2RkT3iiJIuMDM4IC4KCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGJyZXhpdF9wb2xscywgYWVzKGVuZGRhdGUsIHNwcmVhZCwgY29sb3IgPSBwb2xsX3R5cGUpKSArIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsb2VzcyIsIHNwYW4gPSAwLjQpICsgZ2VvbV9wb2ludCgpICsgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gIC0wLjAzOCkKYGBgCgojIFF1ZXN0aW9uIDEyOiBQbG90dGluZyByYXcgcGVyY2VudGFnZXMgb3ZlciB0aW1lCgpVc2UgdGhlIGZvbGxvd2luZyBjb2RlIHRvIGNyZWF0ZSB0aGUgb2JqZWN0IGJyZXhpdF9sb25nLCB3aGljaCBoYXMgYSBjb2x1bW4gdm90ZSBjb250YWluaW5nIHRoZSB0aHJlZSBwb3NzaWJsZSB2b3RlcyBvbiBhIEJyZXhpdCBwb2xsICgicmVtYWluIiwgImxlYXZlIiwgInVuZGVjaWRlZCIpIGFuZCBhIGNvbHVtbiBwcm9wb3J0aW9uIGNvbnRhaW5pbmcgdGhlIHJhdyBwcm9wb3J0aW9uIGNob29zaW5nIHRoYXQgdm90ZSBvcHRpb24gb24gdGhlIGdpdmVuIHBvbGw6CgpgYGB7cn0KYnJleGl0X2xvbmcgPC0gYnJleGl0X3BvbGxzICU+JQogICAgZ2F0aGVyKHZvdGUsIHByb3BvcnRpb24sICJyZW1haW4iOiJ1bmRlY2lkZWQiKSAlPiUKICAgIG11dGF0ZSh2b3RlID0gZmFjdG9yKHZvdGUpKQpoZWFkKGJyZXhpdF9sb25nKQpgYGAKCk1ha2UgYSBncmFwaCBvZiBwcm9wb3J0aW9uIG92ZXIgdGltZSBjb2xvcmVkIGJ5IHZvdGUuIEFkZCBhIHNtb290aCB0cmVuZGxpbmUgd2l0aCBnZW9tX3Ntb290aCgpIGFuZCBtZXRob2QgPSAibG9lc3MiIHdpdGggYSBzcGFuIG9mIDAuMy4KCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGJyZXhpdF9sb25nLCBhZXMoZW5kZGF0ZSwgcHJvcG9ydGlvbiwgY29sb3IgPSB2b3RlKSkgKyBnZW9tX3Ntb290aChtZXRob2QgPSAibG9lc3MiLCBzcGFuID0gMC4zKSArIGdlb21fcG9pbnQoKQpgYGAKCg==