# Loading the tidyverse package
library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.2.1     ✔ readr     2.2.0
## ✔ forcats   1.0.1     ✔ stringr   1.6.0
## ✔ ggplot2   4.0.3     ✔ tibble    3.3.1
## ✔ lubridate 1.9.5     ✔ tidyr     1.3.2
## ✔ purrr     1.2.2     
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors

Reading in the NFL drives data from github

# Read in the nfl drive data
drives <- read.csv("https://raw.githubusercontent.com/Shammalamala/STA4504/refs/heads/main/data/ch1/nfl%20drives.csv")

# Looking at the different ways drives can end
unique(drives$drive_end)
## [1] "Field Goal" "Turnover"   "Touchdown"  "Punt"

We will be focusing on the variable drive_end

There are 4 ways in the data that a touchdown can end: Touchdown, Field Goal, Punt, Turnover

Hypothesis Test

Let’s perform a hypothesis test to answer the question “Do 20% of all drives end in touchdowns?”

\[H_0: \pi = 0.20 \\ H_1: \pi \ne 0.20\]

For a single parameter, test statistics follow the same general pattern:

\[z = \frac{\textrm{statistic} - \textrm{null value}}{\textrm{standard error}}\]

If we are interested in learning about a single proportion, our test statistic is:

\[z = \frac{\hat{\pi} - \pi_0}{\sqrt{\frac{\pi(1-\pi)}{n}}}\]

We have \(\pi_0\), but need to calculate our sample proportion:

# Calculating the total number of tds
td_total <- sum(drives$drive_end == "Touchdown")

# Calculating the sample proportion using mean(drives$drive_end = "Touchdown")
td_prop <- mean(drives$drive_end == "Touchdown")

td_prop
## [1] 0.24

Let’s save the sample size, \(n\), and the null hypothesis value, \(\pi_0\):

n <- nrow(drives); pi0 <- 0.20

We have a sample proportion of \(\hat{\pi} = 0.24\) (24%). Our null hypothesis is 0.20 (\(\pi_0 = 0.20\)).

So we have the top of our test statistic fraction, but what about the denominator:

\[SE = \sqrt{\frac{\pi(1-\pi)}{n}}\]

We don’t have \(\pi\) (hence the hypothesis test). So what do we replace it with?

There are two choices:

  1. Replace the unknown parameter with the sample statistic: \(\pi \rightarrow \hat{\pi}\)
  1. Replace the unknown parameter with the null hypothesis value: \(\pi \rightarrow \pi_0\)$

For larger sample sizes, the two won’t be that different. But for smaller sample sizes, the standard errors can be very different.

When performing a hypothesis test, a Score standard error is typically used and the test is then called a Score Test (unsurprising!)

Wald Test and Score Tests

Let’s start with the Wald test:

\[z = \frac{\hat{\pi} - \pi_0}{\sqrt{\frac{\hat{\pi}(1-\hat{\pi})}{n}}}\]

wald_se <- sqrt(td_prop * (1 - td_prop)/n)
wald_se
## [1] 0.01207974

Next, we’ll find the test statistic:

wald_z <- (td_prop - pi0) / wald_se
wald_z
## [1] 3.311331

Then we can find the p-value: \(P(|Z| > 3.311)\)

wald_pval <- 2 * pnorm(abs(wald_z), lower.tail = F)
wald_pval
## [1] 0.0009285334

The Wald test rejects the null hypothesis!

Score Test

Up next: Score Test

\[z = \frac{\hat{\pi} - \pi_0}{\sqrt{\frac{\pi_0(1-\pi_0)}{n}}}\]

score_se <- sqrt(pi0 * (1 - pi0)/n)
score_se
## [1] 0.01131371

Next, we’ll find the test statistic:

score_z <- (td_prop - pi0) / score_se
score_z
## [1] 3.535534

Then we can find the p-value: \(P(|Z| > 3.536)\)

score_pval <- 2 * pnorm(abs(score_z), lower.tail = F)
score_pval
## [1] 0.000406952

The test statistic is larger and the p-value smaller for the score test compared to the Wald test!

Likelihood Ratio Test

The likelihood ratio test (LRT) takes a different approach than the typical \(\frac{\hat{\pi} - pi_0}{SE}\) test statistic. Instead, for a binomial random variable, it is a ratio of two different binomial distributions:

\[\frac{\ell_1}{\ell_0} = \frac{{n \choose y} \hat{\pi}^y(1-\hat{\pi})^{n-y}}{{n \choose y} (\pi_0)^y(1-\pi_0)^{n-y}}\]

We can find the numerator and denominator using dbinom(...) with prob = the corresponding probabilities

# unrestricted likelihood
ell1 <- dbinom(td_total, size = n, prob = td_prop)

# restricted likelihood
ell0 <- dbinom(td_total, size = n, prob = pi0)

# LRT test stat 
lrt_test_stat <- ell1 / ell0
lrt_test_stat
## [1] 390.6599

In order to find a p-value, we need to know that distribution the test statistic follows.

While \(\ell_1 / \ell_0\) itself doesn’t have a defined distribution, we can use Wilk’s Theorem to find a test statistic and distribution:

\[2\log\left(\frac{\ell_1}{\ell_0}\right) \sim \chi^2_v\]

For a single binomial random, there is one ‘unrestrained’ parameter in \(\ell_1\), so \(v = 1\)

The LRT p-value is:

\[P(\chi^2_1 > 11.936)\]

log_lrt_test_stat <- 2 * log(lrt_test_stat)
lrt_pval <- pchisq(q = log_lrt_test_stat, df = 1, lower.tail = F)
lrt_pval
## [1] 0.0005506918

The p-value for the LRT test is similar to the score test statistic.

Comparing the three tests:

If we compare the three exams:

tibble(
  test = c('Wald', 'Score', 'LRT'),
  test_stat = round(c(wald_z, score_z, log_lrt_test_stat), 5),
  p_value = round(c(wald_pval, score_pval, lrt_pval), 5)
)
## # A tibble: 3 × 3
##   test  test_stat p_value
##   <chr>     <dbl>   <dbl>
## 1 Wald       3.31 0.00093
## 2 Score      3.54 0.00041
## 3 LRT       11.9  0.00055

Built-in functions for tests:

Note: Regardless of which test we use, the functions will return the \(\chi^2\) version of the test statistic, \(z^2\).

Wald test:

There isn’t one :(

That’s because using the Wald test for a single proportion is a bad idea. We did it (and you’ll do it on the homework) to show that it exists, but you shouldn’t use it!

Why?

What happens if \(y = n\)?

Score test:

For the score test, you can use prop.test() in base R, or prop_test() in rstatix. prop_test() is very similar to prop.test(), but it gives the results in a data frame that is easier to work with than the object created by prop.test().

Both functions have the following arguments:

  • x = the number of successes (we denote as \(y\))
  • n = the sample size
  • p = the null hypothesis value, \(\pi_0\)
  • correct = F to have it do a score test without a binomial correction
prop.test(
  x = td_total,
  n = n, 
  p = pi0,
  alternative = 'two.sided',
  correct = F
)
## 
##  1-sample proportions test without continuity correction
## 
## data:  td_total out of n, null probability pi0
## X-squared = 12.5, df = 1, p-value = 0.000407
## alternative hypothesis: true p is not equal to 0.2
## 95 percent confidence interval:
##  0.2171436 0.2644495
## sample estimates:
##    p 
## 0.24
rstatix::prop_test(
  x = td_total,
  n = n, 
  p = pi0,
  correct = F
) 
## # A tibble: 1 × 5
##       n statistic    df        p p.signif
## * <int>     <dbl> <int>    <dbl> <chr>   
## 1  1250      12.5     1 0.000407 ***

LRT Built-in:

To conduct a likelihood ratio test using a built-in function, we need to install the DescTools package and use the GTest(...)

The GTest(...) function doesn’t do a one-proportion test, but it will do a goodness-of-fit test. What good is that to us?

If the number of categories in a GoF test is 2, then it is equivalent to a one prop test!

That does mean instead of just giving it the number of successes and the hypothesized probability, we need to give it both \(y\) and \(n-y\) along with \(\pi_0\) and \(1-\pi_0\) in two vectors for x and p, as seen below:

DescTools::GTest(
  x = c(td_total, n - td_total),
  p = c(pi0, 1-pi0)
)
## 
##  Log likelihood ratio (G-test) goodness of fit test
## 
## data:  c(td_total, n - td_total)
## G = 11.936, X-squared df = 1, p-value = 0.0005507
LS0tDQp0aXRsZTogIkluZmVyZW5jZSBmb3IgMSBDYXRlZ29yaWNhbCBWYXJpYWJsZSAtIFNpbmdsZSBQcm9wb3J0aW9uIg0KYXV0aG9yOiAiQ2hhcHRlciAxIg0KZGF0ZTogIlNUQSA0NTA0Ig0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIGZpZ193aWR0aDogNg0KICAgIGZpZ19oZWlnaHQ6IDYNCiAgICBmaWdfY2FwdGlvbjogdHJ1ZQ0KICAgIG51bWJlcl9zZWN0aW9uczogZmFsc2UNCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUNCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQogICAgc21vb3RoX3Njcm9sbDogdHJ1ZQ0KICAgIHRoZW1lOiBsdW1lbg0KICBwZGZfZG9jdW1lbnQ6IGRlZmF1bHQNCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwNCiAgICAgICAgICAgICAgICAgICAgICBmaWcuYWxpZ24gPSAiY2VudGVyIikNCmBgYA0KDQpgYGB7ciBwYWNrYWdlc30NCiMgTG9hZGluZyB0aGUgdGlkeXZlcnNlIHBhY2thZ2UNCmxpYnJhcnkodGlkeXZlcnNlKQ0KYGBgDQoNClJlYWRpbmcgaW4gdGhlIE5GTCBkcml2ZXMgZGF0YSBmcm9tIGdpdGh1Yg0KDQpgYGB7ciBkYXRhfQ0KIyBSZWFkIGluIHRoZSBuZmwgZHJpdmUgZGF0YQ0KZHJpdmVzIDwtIHJlYWQuY3N2KCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vU2hhbW1hbGFtYWxhL1NUQTQ1MDQvcmVmcy9oZWFkcy9tYWluL2RhdGEvY2gxL25mbCUyMGRyaXZlcy5jc3YiKQ0KDQojIExvb2tpbmcgYXQgdGhlIGRpZmZlcmVudCB3YXlzIGRyaXZlcyBjYW4gZW5kDQp1bmlxdWUoZHJpdmVzJGRyaXZlX2VuZCkNCg0KYGBgDQoNCldlIHdpbGwgYmUgZm9jdXNpbmcgb24gdGhlIHZhcmlhYmxlIGBkcml2ZV9lbmRgDQoNClRoZXJlIGFyZSA0IHdheXMgaW4gdGhlIGRhdGEgdGhhdCBhIHRvdWNoZG93biBjYW4gZW5kOiBUb3VjaGRvd24sIEZpZWxkIEdvYWwsIFB1bnQsIFR1cm5vdmVyDQoNCiMjIEh5cG90aGVzaXMgVGVzdA0KDQpMZXQncyBwZXJmb3JtIGEgaHlwb3RoZXNpcyB0ZXN0IHRvIGFuc3dlciB0aGUgcXVlc3Rpb24gIkRvIDIwJSBvZiBhbGwgZHJpdmVzIGVuZCBpbiB0b3VjaGRvd25zPyINCg0KJCRIXzA6IFxwaSA9IDAuMjAgXFwgSF8xOiBccGkgXG5lIDAuMjAkJA0KDQpGb3IgYSBzaW5nbGUgcGFyYW1ldGVyLCB0ZXN0IHN0YXRpc3RpY3MgZm9sbG93IHRoZSBzYW1lIGdlbmVyYWwgcGF0dGVybjoNCg0KJCR6ID0gXGZyYWN7XHRleHRybXtzdGF0aXN0aWN9IC0gXHRleHRybXtudWxsIHZhbHVlfX17XHRleHRybXtzdGFuZGFyZCBlcnJvcn19JCQNCg0KSWYgd2UgYXJlIGludGVyZXN0ZWQgaW4gbGVhcm5pbmcgYWJvdXQgYSBzaW5nbGUgcHJvcG9ydGlvbiwgb3VyIHRlc3Qgc3RhdGlzdGljIGlzOg0KDQokJHogPSBcZnJhY3tcaGF0e1xwaX0gLSBccGlfMH17XHNxcnR7XGZyYWN7XHBpKDEtXHBpKX17bn19fSQkDQoNCldlIGhhdmUgJFxwaV8wJCwgYnV0IG5lZWQgdG8gY2FsY3VsYXRlIG91ciBzYW1wbGUgcHJvcG9ydGlvbjoNCg0KYGBge3IgdGRfcHJvcH0NCiMgQ2FsY3VsYXRpbmcgdGhlIHRvdGFsIG51bWJlciBvZiB0ZHMNCnRkX3RvdGFsIDwtIHN1bShkcml2ZXMkZHJpdmVfZW5kID09ICJUb3VjaGRvd24iKQ0KDQojIENhbGN1bGF0aW5nIHRoZSBzYW1wbGUgcHJvcG9ydGlvbiB1c2luZyBtZWFuKGRyaXZlcyRkcml2ZV9lbmQgPSAiVG91Y2hkb3duIikNCnRkX3Byb3AgPC0gbWVhbihkcml2ZXMkZHJpdmVfZW5kID09ICJUb3VjaGRvd24iKQ0KDQp0ZF9wcm9wDQpgYGANCg0KTGV0J3Mgc2F2ZSB0aGUgc2FtcGxlIHNpemUsICRuJCwgYW5kIHRoZSBudWxsIGh5cG90aGVzaXMgdmFsdWUsICRccGlfMCQ6DQoNCmBgYHtyIHNhbXBsZV9zaXplfQ0KbiA8LSBucm93KGRyaXZlcyk7IHBpMCA8LSAwLjIwDQoNCmBgYA0KDQpXZSBoYXZlIGEgc2FtcGxlIHByb3BvcnRpb24gb2YgJFxoYXR7XHBpfSA9IDAuMjQkICgyNCUpLiBPdXIgbnVsbCBoeXBvdGhlc2lzIGlzIDAuMjAgKCRccGlfMCA9IDAuMjAkKS4NCg0KU28gd2UgaGF2ZSB0aGUgdG9wIG9mIG91ciB0ZXN0IHN0YXRpc3RpYyBmcmFjdGlvbiwgYnV0IHdoYXQgYWJvdXQgdGhlIGRlbm9taW5hdG9yOg0KDQokJFNFID0gXHNxcnR7XGZyYWN7XHBpKDEtXHBpKX17bn19JCQNCg0KV2UgZG9uJ3QgaGF2ZSAkXHBpJCAoaGVuY2UgdGhlIGh5cG90aGVzaXMgdGVzdCkuIFNvIHdoYXQgZG8gd2UgcmVwbGFjZSBpdCB3aXRoPw0KDQpUaGVyZSBhcmUgdHdvIGNob2ljZXM6DQoNCjEpICBSZXBsYWNlIHRoZSB1bmtub3duIHBhcmFtZXRlciB3aXRoIHRoZSBzYW1wbGUgc3RhdGlzdGljOiAkXHBpIFxyaWdodGFycm93IFxoYXR7XHBpfSQNCg0KLSBUaGlzIGlzIG91ciAqKldhbGQqKiBzdGFuZGFyZCBlcnJvcg0KDQoyKSAgUmVwbGFjZSB0aGUgdW5rbm93biBwYXJhbWV0ZXIgd2l0aCB0aGUgbnVsbCBoeXBvdGhlc2lzIHZhbHVlOiAkXHBpIFxyaWdodGFycm93IFxwaV8wJFwkDQoNCi0gVGhpcyBpcyBvdXIgKipTY29yZSoqLCBzb21ldGltZXMgY2FsbGVkICoqV2lsc29uKiosIHN0YW5kYXJkIGVycm9yDQoNCkZvciBsYXJnZXIgc2FtcGxlIHNpemVzLCB0aGUgdHdvIHdvbid0IGJlIHRoYXQgZGlmZmVyZW50LiBCdXQgZm9yIHNtYWxsZXIgc2FtcGxlIHNpemVzLCB0aGUgc3RhbmRhcmQgZXJyb3JzIGNhbiBiZSB2ZXJ5IGRpZmZlcmVudC4NCg0KV2hlbiBwZXJmb3JtaW5nIGEgaHlwb3RoZXNpcyB0ZXN0LCBhIFNjb3JlIHN0YW5kYXJkIGVycm9yIGlzIHR5cGljYWxseSB1c2VkIGFuZCB0aGUgdGVzdCBpcyB0aGVuIGNhbGxlZCBhICoqU2NvcmUgVGVzdCoqICh1bnN1cnByaXNpbmchKQ0KDQojIyMgV2FsZCBUZXN0IGFuZCBTY29yZSBUZXN0cw0KDQpMZXQncyBzdGFydCB3aXRoIHRoZSBXYWxkIHRlc3Q6DQoNCiQkeiA9IFxmcmFje1xoYXR7XHBpfSAtIFxwaV8wfXtcc3FydHtcZnJhY3tcaGF0e1xwaX0oMS1caGF0e1xwaX0pfXtufX19JCQNCg0KYGBge3Igd2FsZF9zZX0NCndhbGRfc2UgPC0gc3FydCh0ZF9wcm9wICogKDEgLSB0ZF9wcm9wKS9uKQ0Kd2FsZF9zZQ0KYGBgDQoNCk5leHQsIHdlJ2xsIGZpbmQgdGhlIHRlc3Qgc3RhdGlzdGljOg0KDQpgYGB7ciBXYWxkX3Rlc3RTdGF0fQ0Kd2FsZF96IDwtICh0ZF9wcm9wIC0gcGkwKSAvIHdhbGRfc2UNCndhbGRfeg0KYGBgDQoNClRoZW4gd2UgY2FuIGZpbmQgdGhlIHAtdmFsdWU6ICRQKHxafCA+IGByIHJvdW5kKHdhbGRfeiwgMylgKSQNCg0KYGBge3Igd2FsZF9wdmFsfQ0Kd2FsZF9wdmFsIDwtIDIgKiBwbm9ybShhYnMod2FsZF96KSwgbG93ZXIudGFpbCA9IEYpDQp3YWxkX3B2YWwNCmBgYA0KDQpUaGUgV2FsZCB0ZXN0IHJlamVjdHMgdGhlIG51bGwgaHlwb3RoZXNpcyENCg0KIyMjIFNjb3JlIFRlc3QNCg0KVXAgbmV4dDogU2NvcmUgVGVzdA0KDQokJHogPSBcZnJhY3tcaGF0e1xwaX0gLSBccGlfMH17XHNxcnR7XGZyYWN7XHBpXzAoMS1ccGlfMCl9e259fX0kJA0KDQpgYGB7ciBzY29yZV9zZX0NCnNjb3JlX3NlIDwtIHNxcnQocGkwICogKDEgLSBwaTApL24pDQpzY29yZV9zZQ0KYGBgDQoNCk5leHQsIHdlJ2xsIGZpbmQgdGhlIHRlc3Qgc3RhdGlzdGljOg0KDQpgYGB7ciBzY29yZV90ZXN0U3RhdH0NCnNjb3JlX3ogPC0gKHRkX3Byb3AgLSBwaTApIC8gc2NvcmVfc2UNCnNjb3JlX3oNCmBgYA0KDQpUaGVuIHdlIGNhbiBmaW5kIHRoZSBwLXZhbHVlOiAkUCh8WnwgPiBgciByb3VuZChzY29yZV96LCAzKWApJA0KDQpgYGB7ciBzY29yZV9wdmFsfQ0Kc2NvcmVfcHZhbCA8LSAyICogcG5vcm0oYWJzKHNjb3JlX3opLCBsb3dlci50YWlsID0gRikNCnNjb3JlX3B2YWwNCmBgYA0KDQpUaGUgdGVzdCBzdGF0aXN0aWMgaXMgbGFyZ2VyIGFuZCB0aGUgcC12YWx1ZSBzbWFsbGVyIGZvciB0aGUgc2NvcmUgdGVzdCBjb21wYXJlZCB0byB0aGUgV2FsZCB0ZXN0IQ0KDQoNCiMjIyBMaWtlbGlob29kIFJhdGlvIFRlc3QNCg0KVGhlIGxpa2VsaWhvb2QgcmF0aW8gdGVzdCAoTFJUKSB0YWtlcyBhIGRpZmZlcmVudCBhcHByb2FjaCB0aGFuIHRoZSB0eXBpY2FsICRcZnJhY3tcaGF0e1xwaX0gLSBwaV8wfXtTRX0kIHRlc3Qgc3RhdGlzdGljLiBJbnN0ZWFkLCBmb3IgYSBiaW5vbWlhbCByYW5kb20gdmFyaWFibGUsIGl0IGlzIGEgcmF0aW8gb2YgdHdvIGRpZmZlcmVudCBiaW5vbWlhbCBkaXN0cmlidXRpb25zOg0KDQokJFxmcmFje1xlbGxfMX17XGVsbF8wfSA9IFxmcmFje3tuIFxjaG9vc2UgeX0gXGhhdHtccGl9XnkoMS1caGF0e1xwaX0pXntuLXl9fXt7biBcY2hvb3NlIHl9IChccGlfMCleeSgxLVxwaV8wKV57bi15fX0kJA0KDQpXZSBjYW4gZmluZCB0aGUgbnVtZXJhdG9yIGFuZCBkZW5vbWluYXRvciB1c2luZyBgZGJpbm9tKC4uLilgIHdpdGggYHByb2IgPSBgIHRoZSBjb3JyZXNwb25kaW5nIHByb2JhYmlsaXRpZXMNCg0KYGBge3IgbHJ0X3Rlc3RTdGF0fQ0KIyB1bnJlc3RyaWN0ZWQgbGlrZWxpaG9vZA0KZWxsMSA8LSBkYmlub20odGRfdG90YWwsIHNpemUgPSBuLCBwcm9iID0gdGRfcHJvcCkNCg0KIyByZXN0cmljdGVkIGxpa2VsaWhvb2QNCmVsbDAgPC0gZGJpbm9tKHRkX3RvdGFsLCBzaXplID0gbiwgcHJvYiA9IHBpMCkNCg0KIyBMUlQgdGVzdCBzdGF0IA0KbHJ0X3Rlc3Rfc3RhdCA8LSBlbGwxIC8gZWxsMA0KbHJ0X3Rlc3Rfc3RhdA0KYGBgDQoNCkluIG9yZGVyIHRvIGZpbmQgYSBwLXZhbHVlLCB3ZSBuZWVkIHRvIGtub3cgdGhhdCBkaXN0cmlidXRpb24gdGhlIHRlc3Qgc3RhdGlzdGljIGZvbGxvd3MuDQoNCldoaWxlICRcZWxsXzEgLyBcZWxsXzAkIGl0c2VsZiBkb2Vzbid0IGhhdmUgYSBkZWZpbmVkIGRpc3RyaWJ1dGlvbiwgd2UgY2FuIHVzZSAqKldpbGsncyBUaGVvcmVtKiogdG8gZmluZCBhIHRlc3Qgc3RhdGlzdGljIGFuZCBkaXN0cmlidXRpb246DQoNCiQkMlxsb2dcbGVmdChcZnJhY3tcZWxsXzF9e1xlbGxfMH1ccmlnaHQpIFxzaW0gXGNoaV4yX3YkJA0KDQpGb3IgYSBzaW5nbGUgYmlub21pYWwgcmFuZG9tLCB0aGVyZSBpcyBvbmUgJ3VucmVzdHJhaW5lZCcgcGFyYW1ldGVyIGluICRcZWxsXzEkLCBzbyAkdiA9IDEkDQoNClRoZSBMUlQgcC12YWx1ZSBpczoNCg0KJCRQKFxjaGleMl8xID4gYHIgcm91bmQoMiAqIGxvZyhscnRfdGVzdF9zdGF0KSwgMylgKSQkDQoNCmBgYHtyIGxydF9wdmFsfQ0KbG9nX2xydF90ZXN0X3N0YXQgPC0gMiAqIGxvZyhscnRfdGVzdF9zdGF0KQ0KbHJ0X3B2YWwgPC0gcGNoaXNxKHEgPSBsb2dfbHJ0X3Rlc3Rfc3RhdCwgZGYgPSAxLCBsb3dlci50YWlsID0gRikNCmxydF9wdmFsDQpgYGANCg0KVGhlIHAtdmFsdWUgZm9yIHRoZSBMUlQgdGVzdCBpcyBzaW1pbGFyIHRvIHRoZSBzY29yZSB0ZXN0IHN0YXRpc3RpYy4NCg0KIyMjIENvbXBhcmluZyB0aGUgdGhyZWUgdGVzdHM6DQoNCklmIHdlIGNvbXBhcmUgdGhlIHRocmVlIGV4YW1zOg0KDQpgYGB7ciB0aHJlZV90ZXN0X2NvbXBhcmV9DQp0aWJibGUoDQogIHRlc3QgPSBjKCdXYWxkJywgJ1Njb3JlJywgJ0xSVCcpLA0KICB0ZXN0X3N0YXQgPSByb3VuZChjKHdhbGRfeiwgc2NvcmVfeiwgbG9nX2xydF90ZXN0X3N0YXQpLCA1KSwNCiAgcF92YWx1ZSA9IHJvdW5kKGMod2FsZF9wdmFsLCBzY29yZV9wdmFsLCBscnRfcHZhbCksIDUpDQopDQpgYGANCg0KDQojIyBCdWlsdC1pbiBmdW5jdGlvbnMgZm9yIHRlc3RzOg0KDQoNCg0KKipOb3RlOioqIFJlZ2FyZGxlc3Mgb2Ygd2hpY2ggdGVzdCB3ZSB1c2UsIHRoZSBmdW5jdGlvbnMgd2lsbCByZXR1cm4gdGhlICRcY2hpXjIkIHZlcnNpb24gb2YgdGhlIHRlc3Qgc3RhdGlzdGljLCAkel4yJC4NCg0KIyMjIFdhbGQgdGVzdDoNCg0KVGhlcmUgaXNuJ3Qgb25lIDooDQoNClRoYXQncyBiZWNhdXNlIHVzaW5nIHRoZSBXYWxkIHRlc3QgZm9yIGEgc2luZ2xlIHByb3BvcnRpb24gaXMgYSBiYWQgaWRlYS4gV2UgZGlkIGl0IChhbmQgeW91J2xsIGRvIGl0IG9uIHRoZSBob21ld29yaykgdG8gc2hvdyB0aGF0IGl0IGV4aXN0cywgYnV0IHlvdSBzaG91bGRuJ3QgdXNlIGl0IQ0KDQpXaHk/DQoNCldoYXQgaGFwcGVucyBpZiAkeSA9IG4kPw0KDQojIyMgU2NvcmUgdGVzdDoNCg0KRm9yIHRoZSBzY29yZSB0ZXN0LCB5b3UgY2FuIHVzZSBgcHJvcC50ZXN0KClgIGluIGBiYXNlYCBSLCBvciBgcHJvcF90ZXN0KClgIGluIGByc3RhdGl4YC4gYHByb3BfdGVzdCgpYCBpcyB2ZXJ5IHNpbWlsYXIgdG8gYHByb3AudGVzdCgpYCwgYnV0IGl0IGdpdmVzIHRoZSByZXN1bHRzIGluIGEgZGF0YSBmcmFtZSB0aGF0IGlzIGVhc2llciB0byB3b3JrIHdpdGggdGhhbiB0aGUgb2JqZWN0IGNyZWF0ZWQgYnkgYHByb3AudGVzdCgpYC4NCg0KQm90aCBmdW5jdGlvbnMgaGF2ZSB0aGUgZm9sbG93aW5nIGFyZ3VtZW50czoNCg0KLSBgeCA9IGAgdGhlIG51bWJlciBvZiBzdWNjZXNzZXMgKHdlIGRlbm90ZSBhcyAkeSQpDQotIGBuID0gYCB0aGUgc2FtcGxlIHNpemUNCi0gYHAgPSBgIHRoZSBudWxsIGh5cG90aGVzaXMgdmFsdWUsICRccGlfMCQNCi0gYGNvcnJlY3QgPSBGYCB0byBoYXZlIGl0IGRvIGEgc2NvcmUgdGVzdCB3aXRob3V0IGEgYmlub21pYWwgY29ycmVjdGlvbg0KDQpgYGB7ciBwcm9wLnRlc3R9DQpwcm9wLnRlc3QoDQogIHggPSB0ZF90b3RhbCwNCiAgbiA9IG4sIA0KICBwID0gcGkwLA0KICBhbHRlcm5hdGl2ZSA9ICd0d28uc2lkZWQnLA0KICBjb3JyZWN0ID0gRg0KKQ0KYGBgDQoNCmBgYHtyIHByb3BfdGVzdH0NCnJzdGF0aXg6OnByb3BfdGVzdCgNCiAgeCA9IHRkX3RvdGFsLA0KICBuID0gbiwgDQogIHAgPSBwaTAsDQogIGNvcnJlY3QgPSBGDQopIA0KDQpgYGANCg0KDQojIyMgTFJUIEJ1aWx0LWluOg0KDQpUbyBjb25kdWN0IGEgbGlrZWxpaG9vZCByYXRpbyB0ZXN0IHVzaW5nIGEgYnVpbHQtaW4gZnVuY3Rpb24sIHdlIG5lZWQgdG8gaW5zdGFsbCB0aGUgYERlc2NUb29sc2AgcGFja2FnZSBhbmQgdXNlIHRoZSBgR1Rlc3QoLi4uKWANCg0KVGhlIGBHVGVzdCguLi4pYCBmdW5jdGlvbiBkb2Vzbid0IGRvIGEgb25lLXByb3BvcnRpb24gdGVzdCwgYnV0IGl0IHdpbGwgZG8gYSBnb29kbmVzcy1vZi1maXQgdGVzdC4gV2hhdCBnb29kIGlzIHRoYXQgdG8gdXM/DQoNCklmIHRoZSBudW1iZXIgb2YgY2F0ZWdvcmllcyBpbiBhIEdvRiB0ZXN0IGlzIDIsIHRoZW4gaXQgaXMgZXF1aXZhbGVudCB0byBhIG9uZSBwcm9wIHRlc3QhDQoNClRoYXQgZG9lcyBtZWFuIGluc3RlYWQgb2YganVzdCBnaXZpbmcgaXQgdGhlIG51bWJlciBvZiBzdWNjZXNzZXMgYW5kIHRoZSBoeXBvdGhlc2l6ZWQgcHJvYmFiaWxpdHksIHdlIG5lZWQgdG8gZ2l2ZSBpdCBib3RoICR5JCBhbmQgJG4teSQgYWxvbmcgd2l0aCAkXHBpXzAkIGFuZCAkMS1ccGlfMCQgaW4gdHdvIHZlY3RvcnMgZm9yIGB4YCBhbmQgYHBgLCBhcyBzZWVuIGJlbG93Og0KDQpgYGB7ciBscnRfYnVpbHRpbn0NCkRlc2NUb29sczo6R1Rlc3QoDQogIHggPSBjKHRkX3RvdGFsLCBuIC0gdGRfdG90YWwpLA0KICBwID0gYyhwaTAsIDEtcGkwKQ0KKQ0KYGBgDQoNCg0KDQoNCg0KDQoNCg0K