Data Science Module

Topic 9B: Writing R Functions


Example R code solutions for the Data Science Module Computer Lab 9B are presented below.

1 R Functions Overview

No answer required.

2 Writing Simple Functions

2.1 Mean Function

Example R code is provided below:

mean_func <- function(values){
  # Argument:
  # values: This is our list of values

  n <- length(values)
  sum(values) / n

}                          

Note that, based on this format, our function can compute the mean of a string of numbers of any non-zero length.

2.1.1

mean_func(c(2:8))
## [1] 5

2.2 Sample standard deviation Function

Example R code is provided below:

Note that I have used quite a few brackets here, to ensure calculations are performed in the correct order.

sample_sd_func <- function(values){
  # Argument:
  # values: This is our list of values
  
  n <- length(values)
  sqrt( (1 / (n-1) ) * sum( (values - mean_func(values) )^2 ))
  
}

2.2.1

sample_sd_func(c(2:8))
## [1] 2.160247

2.2.2

sd(c(2:8))
## [1] 2.160247

Note that both our function and the inbuilt R function provide values of 2.160247. Success!

3 Writing a \(t\)-test Function

3.1 \(t\)-test test statistic

Example R code is provided below:

t_test_func <- function(values, mu){
  # Arguments:
  # values: This is our list of values
  # mu: The mean under H0
  
  n <- length(values)
  ( mean_func(values) - mu ) / (sample_sd_func(values) / sqrt(n) )
  
}

3.2

We run the following commands:

t_test_func(c(2:8), 4)
## [1] 1.224745
t.test(c(2:8), mu = 4)
## 
##  One Sample t-test
## 
## data:  c(2:8)
## t = 1.2247, df = 6, p-value = 0.2666
## alternative hypothesis: true mean is not equal to 4
## 95 percent confidence interval:
##  3.002105 6.997895
## sample estimates:
## mean of x 
##         5

Our t_test_func output provides the same \(t\) test statistic as the t.test function, correct to 4 decimal places.

3.3 Degrees of freedom

Our updated t_test_func could look like this:

t_test_func <- function(values, mu){
  # Arguments:
  # values: This is our list of values
  # mu: The mean under H0
  
  n <- length(values)
  t.val <- ( mean_func(values) - mu ) / (sample_sd_func(values) / sqrt(n) )
  
  df <- n - 1
  
  c("test.stat" = t.val, "df" = df)
  
}

3.4 The concatenate Function

Our updated t_test_func could look like this:

t_test_func <- function(values, mu){
  # Arguments:
  # values: This is our list of values
  # mu: The mean under H0
  
  n <- length(values)
  t.val <- ( mean_func(values) - mu ) / (sample_sd_func(values) / sqrt(n) )
  
  df <- n - 1
  
  cat("The test statistic is", round(t.val, 4), "\n",
      "The degrees of freedom is", df, "\n")
  
}

3.5 \(p\)-value Function

Our updated t_test_func could look like this:

t_test_func <- function(values, mu){
  # Arguments:
  # values: This is our list of values
  # mu: The mean under H0
 
  n <- length(values)
 
  t.val <- (mean_func(values) - mu) / (sample_sd_func(values) / sqrt(n))
 
  df <- n - 1
 
  p.val <- 2*pt(-abs(t.val), df)
 
  cat("The test statistic is", round(t.val, 4), "\n",
      "The degrees of freedom is", df, "\n",
      "The p-value is", round(p.val, 4), "\n")
}

Note here that using 2*pt(-abs(t.val), df) utilises the symmetry property of the Student’s \(t\)-distribution.

3.5.1

t_test_func(c(2:8), 4)
## The test statistic is 1.2247 
##  The degrees of freedom is 6 
##  The p-value is 0.2666
t.test(c(2:8), mu = 4)
## 
##  One Sample t-test
## 
## data:  c(2:8)
## t = 1.2247, df = 6, p-value = 0.2666
## alternative hypothesis: true mean is not equal to 4
## 95 percent confidence interval:
##  3.002105 6.997895
## sample estimates:
## mean of x 
##         5

Our t_test_func output provides the same \(p\)-value as the t.test function, correct to 4 decimal places.

4 Extension: Adding Additional Details to our \(t\)-test Function

4.1

Example R code with comments is provided below:

t_test_func <- function(values, mu){
 
  n <- length(values) # compute sample size
 
  mean.val <- mean_func(values) # compute mean
 
  s.val <- sample_sd_func(values) # compute sample standard deviation
 
  t.val <- (mean.val - mu) / (s.val / sqrt(n)) # compute t test statistic
 
  df <- n - 1 # compute degrees of freedom
 
  p.val <- 2*pt(-abs(t.val), df) # compute p-value
  
  cat("The test statistic is", round(t.val, 4), "\n",
      "The degrees of freedom is", df, "\n",
      "The p-value is", round(p.val, 4), "\n")
}

4.2

Example R code is provided below:

t_test_func <- function(values, mu){
 
  n <- length(values) # compute sample size
 
  mean.val <- mean_func(values) # compute mean
 
  s.val <- sample_sd_func(values) # compute sample standard deviation
 
  t.val <- (mean.val - mu) / (s.val / sqrt(n)) # compute t test statistic
 
  df <- n - 1 # compute degrees of freedom
 
  p.val <- 2*pt(-abs(t.val), df) # compute p-value
 
  list(t.val = t.val, df = df, p.val = p.val, mean.val = mean.val)
 
}

4.2.1

We can use:

final_check <- t_test_func(c(2:8), 4)
final_check
## $t.val
## [1] 1.224745
## 
## $df
## [1] 6
## 
## $p.val
## [1] 0.2665697
## 
## $mean.val
## [1] 5
names(final_check)
## [1] "t.val"    "df"       "p.val"    "mean.val"
final_check$p.val #etc
## [1] 0.2665697


That’s everything for this computer lab!


These notes have been prepared by Rupert Kuveke and Amanda Shaker. The copyright for the material in these notes resides with the authors named above, with the Department of Mathematical and Physical Sciences and with La Trobe University. Copyright in this work is vested in La Trobe University including all La Trobe University branding and naming. Unless otherwise stated, material within this work is licensed under a Creative Commons Attribution-Non Commercial-Non Derivatives License BY-NC-ND.

LS0tDQp0aXRsZTogIlNUTTEwMDE6IENvbXB1dGVyIExhYiA5QiBTb2x1dGlvbnMiDQpvdXRwdXQ6DQogIGJvb2tkb3duOjpodG1sX2RvY3VtZW50MjogDQogICAgdG9jOiB0cnVlDQogICAgdG9jX2Zsb2F0OiB0cnVlDQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KICAgIHRoZW1lOiByZWFkYWJsZQ0KICAgIGNvZGVfZm9sZGluZzogc2hvdw0KYmlibGlvZ3JhcGh5OiBTVE0xMDAxX0RTX0NMX3JlZmVyZW5jZXMuYmliIA0KbGluay1jaXRhdGlvbnM6IHllcw0KLS0tDQoNCjxzdHlsZT4NCiNUT0Mgew0KICBiYWNrZ3JvdW5kOiB1cmwoImh0dHBzOi8vd3d3LmxhdHJvYmUuZWR1LmF1L19tZWRpYS9sYS10cm9iZS1hcGkvdjUvaW1nL2xvZ28uc3ZnIik7DQogIGJhY2tncm91bmQtc2l6ZTogY29udGFpbjsNCiAgcGFkZGluZy10b3A6IDgwcHggIWltcG9ydGFudDsNCiAgYmFja2dyb3VuZC1yZXBlYXQ6IG5vLXJlcGVhdDsNCn0NCjwvc3R5bGU+DQoNCiMjIyBEYXRhIFNjaWVuY2UgTW9kdWxlIHstfQ0KDQojIyMgVG9waWMgOUI6IFdyaXRpbmcgUiBGdW5jdGlvbnMgey19DQoNCjxicj4NCg0KRXhhbXBsZSBSIGNvZGUgc29sdXRpb25zIGZvciB0aGUgW0RhdGEgU2NpZW5jZSBNb2R1bGUgQ29tcHV0ZXIgTGFiIDlCXShodHRwczovL3JwdWJzLmNvbS9MVFVfU1RNMTAwMS9EU01DTDlfUykgYXJlIHByZXNlbnRlZCBiZWxvdy4NCg0KDQojIFIgRnVuY3Rpb25zIE92ZXJ2aWV3DQoNCk5vIGFuc3dlciByZXF1aXJlZC4NCg0KIyBXcml0aW5nIFNpbXBsZSBGdW5jdGlvbnMNCg0KIyMgTWVhbiBGdW5jdGlvbiB7I21lYW5mdW5jfQ0KDQpFeGFtcGxlIFIgY29kZSBpcyBwcm92aWRlZCBiZWxvdzoNCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBULCBlY2hvID0gVCwgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGfQ0KbWVhbl9mdW5jIDwtIGZ1bmN0aW9uKHZhbHVlcyl7DQogICMgQXJndW1lbnQ6DQogICMgdmFsdWVzOiBUaGlzIGlzIG91ciBsaXN0IG9mIHZhbHVlcw0KDQogIG4gPC0gbGVuZ3RoKHZhbHVlcykNCiAgc3VtKHZhbHVlcykgLyBuDQoNCn0gICAgICAgICAgICAgICAgICAgICAgICAgIA0KYGBgDQoNCipOb3RlIHRoYXQsIGJhc2VkIG9uIHRoaXMgZm9ybWF0LCBvdXIgZnVuY3Rpb24gY2FuIGNvbXB1dGUgdGhlIG1lYW4gb2YgYSBzdHJpbmcgb2YgbnVtYmVycyBvZiBhbnkgbm9uLXplcm8gbGVuZ3RoLioNCg0KIyMjDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gVCwgZWNobyA9IFQsIHdhcm5pbmcgPSBGLCBtZXNzYWdlID0gRn0NCm1lYW5fZnVuYyhjKDI6OCkpDQpgYGANCg0KIyMgU2FtcGxlIHN0YW5kYXJkIGRldmlhdGlvbiBGdW5jdGlvbg0KDQpFeGFtcGxlIFIgY29kZSBpcyBwcm92aWRlZCBiZWxvdzoNCg0KKk5vdGUgdGhhdCBJIGhhdmUgdXNlZCBxdWl0ZSBhIGZldyBicmFja2V0cyBoZXJlLCB0byBlbnN1cmUgY2FsY3VsYXRpb25zIGFyZSBwZXJmb3JtZWQgaW4gdGhlIGNvcnJlY3Qgb3JkZXIuKg0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IFQsIGVjaG8gPSBULCB3YXJuaW5nID0gRiwgbWVzc2FnZSA9IEZ9DQpzYW1wbGVfc2RfZnVuYyA8LSBmdW5jdGlvbih2YWx1ZXMpew0KICAjIEFyZ3VtZW50Og0KICAjIHZhbHVlczogVGhpcyBpcyBvdXIgbGlzdCBvZiB2YWx1ZXMNCiAgDQogIG4gPC0gbGVuZ3RoKHZhbHVlcykNCiAgc3FydCggKDEgLyAobi0xKSApICogc3VtKCAodmFsdWVzIC0gbWVhbl9mdW5jKHZhbHVlcykgKV4yICkpDQogIA0KfQ0KYGBgDQoNCiMjIw0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IFQsIGVjaG8gPSBULCB3YXJuaW5nID0gRiwgbWVzc2FnZSA9IEZ9DQpzYW1wbGVfc2RfZnVuYyhjKDI6OCkpDQpgYGANCg0KIyMjDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gVCwgZWNobyA9IFQsIHdhcm5pbmcgPSBGLCBtZXNzYWdlID0gRn0NCnNkKGMoMjo4KSkNCmBgYA0KDQpOb3RlIHRoYXQgYm90aCBvdXIgZnVuY3Rpb24gYW5kIHRoZSBpbmJ1aWx0IFIgZnVuY3Rpb24gcHJvdmlkZSB2YWx1ZXMgb2YgYDIuMTYwMjQ3YC4gU3VjY2VzcyENCg0KIyBXcml0aW5nIGEgJHQkLXRlc3QgRnVuY3Rpb24geyN0dGVzdGZ1bmN9DQoNCiMjICR0JC10ZXN0IHRlc3Qgc3RhdGlzdGljDQoNCkV4YW1wbGUgUiBjb2RlIGlzIHByb3ZpZGVkIGJlbG93Og0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IFQsIGVjaG8gPSBULCB3YXJuaW5nID0gRiwgbWVzc2FnZSA9IEZ9DQp0X3Rlc3RfZnVuYyA8LSBmdW5jdGlvbih2YWx1ZXMsIG11KXsNCiAgIyBBcmd1bWVudHM6DQogICMgdmFsdWVzOiBUaGlzIGlzIG91ciBsaXN0IG9mIHZhbHVlcw0KICAjIG11OiBUaGUgbWVhbiB1bmRlciBIMA0KICANCiAgbiA8LSBsZW5ndGgodmFsdWVzKQ0KICAoIG1lYW5fZnVuYyh2YWx1ZXMpIC0gbXUgKSAvIChzYW1wbGVfc2RfZnVuYyh2YWx1ZXMpIC8gc3FydChuKSApDQogIA0KfQ0KYGBgDQoNCiMjIHsjdHRlc3R9DQoNCldlIHJ1biB0aGUgZm9sbG93aW5nIGNvbW1hbmRzOg0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IFQsIGVjaG8gPSBULCB3YXJuaW5nID0gRiwgbWVzc2FnZSA9IEZ9DQp0X3Rlc3RfZnVuYyhjKDI6OCksIDQpDQpgYGANCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBULCBlY2hvID0gVCwgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGfQ0KdC50ZXN0KGMoMjo4KSwgbXUgPSA0KQ0KYGBgDQoNCk91ciBgdF90ZXN0X2Z1bmNgIG91dHB1dCBwcm92aWRlcyB0aGUgc2FtZSAkdCQgdGVzdCBzdGF0aXN0aWMgYXMgdGhlIGB0LnRlc3RgIGZ1bmN0aW9uLCBjb3JyZWN0IHRvIDQgZGVjaW1hbCBwbGFjZXMuDQoNCiMjIERlZ3JlZXMgb2YgZnJlZWRvbQ0KDQpPdXIgdXBkYXRlZCBgdF90ZXN0X2Z1bmNgIGNvdWxkIGxvb2sgbGlrZSB0aGlzOg0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IFQsIGVjaG8gPSBULCB3YXJuaW5nID0gRiwgbWVzc2FnZSA9IEZ9DQp0X3Rlc3RfZnVuYyA8LSBmdW5jdGlvbih2YWx1ZXMsIG11KXsNCiAgIyBBcmd1bWVudHM6DQogICMgdmFsdWVzOiBUaGlzIGlzIG91ciBsaXN0IG9mIHZhbHVlcw0KICAjIG11OiBUaGUgbWVhbiB1bmRlciBIMA0KICANCiAgbiA8LSBsZW5ndGgodmFsdWVzKQ0KICB0LnZhbCA8LSAoIG1lYW5fZnVuYyh2YWx1ZXMpIC0gbXUgKSAvIChzYW1wbGVfc2RfZnVuYyh2YWx1ZXMpIC8gc3FydChuKSApDQogIA0KICBkZiA8LSBuIC0gMQ0KICANCiAgYygidGVzdC5zdGF0IiA9IHQudmFsLCAiZGYiID0gZGYpDQogIA0KfQ0KYGBgDQoNCiMjIFRoZSBjb25jYXRlbmF0ZSBGdW5jdGlvbg0KDQpPdXIgdXBkYXRlZCBgdF90ZXN0X2Z1bmNgIGNvdWxkIGxvb2sgbGlrZSB0aGlzOg0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IFQsIGVjaG8gPSBULCB3YXJuaW5nID0gRiwgbWVzc2FnZSA9IEZ9DQp0X3Rlc3RfZnVuYyA8LSBmdW5jdGlvbih2YWx1ZXMsIG11KXsNCiAgIyBBcmd1bWVudHM6DQogICMgdmFsdWVzOiBUaGlzIGlzIG91ciBsaXN0IG9mIHZhbHVlcw0KICAjIG11OiBUaGUgbWVhbiB1bmRlciBIMA0KICANCiAgbiA8LSBsZW5ndGgodmFsdWVzKQ0KICB0LnZhbCA8LSAoIG1lYW5fZnVuYyh2YWx1ZXMpIC0gbXUgKSAvIChzYW1wbGVfc2RfZnVuYyh2YWx1ZXMpIC8gc3FydChuKSApDQogIA0KICBkZiA8LSBuIC0gMQ0KICANCiAgY2F0KCJUaGUgdGVzdCBzdGF0aXN0aWMgaXMiLCByb3VuZCh0LnZhbCwgNCksICJcbiIsDQogICAgICAiVGhlIGRlZ3JlZXMgb2YgZnJlZWRvbSBpcyIsIGRmLCAiXG4iKQ0KICANCn0NCmBgYA0KDQojIyAkcCQtdmFsdWUgRnVuY3Rpb24NCg0KT3VyIHVwZGF0ZWQgYHRfdGVzdF9mdW5jYCBjb3VsZCBsb29rIGxpa2UgdGhpczoNCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBULCBlY2hvID0gVCwgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGfQ0KdF90ZXN0X2Z1bmMgPC0gZnVuY3Rpb24odmFsdWVzLCBtdSl7DQogICMgQXJndW1lbnRzOg0KICAjIHZhbHVlczogVGhpcyBpcyBvdXIgbGlzdCBvZiB2YWx1ZXMNCiAgIyBtdTogVGhlIG1lYW4gdW5kZXIgSDANCiANCiAgbiA8LSBsZW5ndGgodmFsdWVzKQ0KIA0KICB0LnZhbCA8LSAobWVhbl9mdW5jKHZhbHVlcykgLSBtdSkgLyAoc2FtcGxlX3NkX2Z1bmModmFsdWVzKSAvIHNxcnQobikpDQogDQogIGRmIDwtIG4gLSAxDQogDQogIHAudmFsIDwtIDIqcHQoLWFicyh0LnZhbCksIGRmKQ0KIA0KICBjYXQoIlRoZSB0ZXN0IHN0YXRpc3RpYyBpcyIsIHJvdW5kKHQudmFsLCA0KSwgIlxuIiwNCiAgICAgICJUaGUgZGVncmVlcyBvZiBmcmVlZG9tIGlzIiwgZGYsICJcbiIsDQogICAgICAiVGhlIHAtdmFsdWUgaXMiLCByb3VuZChwLnZhbCwgNCksICJcbiIpDQp9DQpgYGANCg0KTm90ZSBoZXJlIHRoYXQgdXNpbmcgYDIqcHQoLWFicyh0LnZhbCksIGRmKWAgdXRpbGlzZXMgdGhlIHN5bW1ldHJ5IHByb3BlcnR5IG9mIHRoZSBTdHVkZW50J3MgJHQkLWRpc3RyaWJ1dGlvbi4NCg0KIyMjIA0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IFQsIGVjaG8gPSBULCB3YXJuaW5nID0gRiwgbWVzc2FnZSA9IEZ9DQp0X3Rlc3RfZnVuYyhjKDI6OCksIDQpDQpgYGANCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBULCBlY2hvID0gVCwgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGfQ0KdC50ZXN0KGMoMjo4KSwgbXUgPSA0KQ0KYGBgDQoNCk91ciBgdF90ZXN0X2Z1bmNgIG91dHB1dCBwcm92aWRlcyB0aGUgc2FtZSAkcCQtdmFsdWUgYXMgdGhlIGB0LnRlc3RgIGZ1bmN0aW9uLCBjb3JyZWN0IHRvIDQgZGVjaW1hbCBwbGFjZXMuDQoNCiMgRXh0ZW5zaW9uOiBBZGRpbmcgQWRkaXRpb25hbCBEZXRhaWxzIHRvIG91ciAkdCQtdGVzdCBGdW5jdGlvbg0KDQojIw0KDQpFeGFtcGxlIFIgY29kZSB3aXRoIGNvbW1lbnRzIGlzIHByb3ZpZGVkIGJlbG93Og0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IEYsIGVjaG8gPSBULCB3YXJuaW5nID0gRiwgbWVzc2FnZSA9IEZ9DQp0X3Rlc3RfZnVuYyA8LSBmdW5jdGlvbih2YWx1ZXMsIG11KXsNCiANCiAgbiA8LSBsZW5ndGgodmFsdWVzKSAjIGNvbXB1dGUgc2FtcGxlIHNpemUNCiANCiAgbWVhbi52YWwgPC0gbWVhbl9mdW5jKHZhbHVlcykgIyBjb21wdXRlIG1lYW4NCiANCiAgcy52YWwgPC0gc2FtcGxlX3NkX2Z1bmModmFsdWVzKSAjIGNvbXB1dGUgc2FtcGxlIHN0YW5kYXJkIGRldmlhdGlvbg0KIA0KICB0LnZhbCA8LSAobWVhbi52YWwgLSBtdSkgLyAocy52YWwgLyBzcXJ0KG4pKSAjIGNvbXB1dGUgdCB0ZXN0IHN0YXRpc3RpYw0KIA0KICBkZiA8LSBuIC0gMSAjIGNvbXB1dGUgZGVncmVlcyBvZiBmcmVlZG9tDQogDQogIHAudmFsIDwtIDIqcHQoLWFicyh0LnZhbCksIGRmKSAjIGNvbXB1dGUgcC12YWx1ZQ0KICANCiAgY2F0KCJUaGUgdGVzdCBzdGF0aXN0aWMgaXMiLCByb3VuZCh0LnZhbCwgNCksICJcbiIsDQogICAgICAiVGhlIGRlZ3JlZXMgb2YgZnJlZWRvbSBpcyIsIGRmLCAiXG4iLA0KICAgICAgIlRoZSBwLXZhbHVlIGlzIiwgcm91bmQocC52YWwsIDQpLCAiXG4iKQ0KfQ0KYGBgDQoNCiMjIA0KDQpFeGFtcGxlIFIgY29kZSBpcyBwcm92aWRlZCBiZWxvdzoNCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBULCBlY2hvID0gVCwgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGfQ0KdF90ZXN0X2Z1bmMgPC0gZnVuY3Rpb24odmFsdWVzLCBtdSl7DQogDQogIG4gPC0gbGVuZ3RoKHZhbHVlcykgIyBjb21wdXRlIHNhbXBsZSBzaXplDQogDQogIG1lYW4udmFsIDwtIG1lYW5fZnVuYyh2YWx1ZXMpICMgY29tcHV0ZSBtZWFuDQogDQogIHMudmFsIDwtIHNhbXBsZV9zZF9mdW5jKHZhbHVlcykgIyBjb21wdXRlIHNhbXBsZSBzdGFuZGFyZCBkZXZpYXRpb24NCiANCiAgdC52YWwgPC0gKG1lYW4udmFsIC0gbXUpIC8gKHMudmFsIC8gc3FydChuKSkgIyBjb21wdXRlIHQgdGVzdCBzdGF0aXN0aWMNCiANCiAgZGYgPC0gbiAtIDEgIyBjb21wdXRlIGRlZ3JlZXMgb2YgZnJlZWRvbQ0KIA0KICBwLnZhbCA8LSAyKnB0KC1hYnModC52YWwpLCBkZikgIyBjb21wdXRlIHAtdmFsdWUNCiANCiAgbGlzdCh0LnZhbCA9IHQudmFsLCBkZiA9IGRmLCBwLnZhbCA9IHAudmFsLCBtZWFuLnZhbCA9IG1lYW4udmFsKQ0KIA0KfQ0KYGBgDQoNCiMjIw0KDQpXZSBjYW4gdXNlOg0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IFQsIGVjaG8gPSBULCB3YXJuaW5nID0gRiwgbWVzc2FnZSA9IEZ9DQpmaW5hbF9jaGVjayA8LSB0X3Rlc3RfZnVuYyhjKDI6OCksIDQpDQpmaW5hbF9jaGVjaw0KbmFtZXMoZmluYWxfY2hlY2spDQpmaW5hbF9jaGVjayRwLnZhbCAjZXRjDQpgYGANCg0KPGJyPg0KDQojIyMjIFRoYXQncyBldmVyeXRoaW5nIGZvciB0aGlzIGNvbXB1dGVyIGxhYiEgIyMjIyB7LX0NCg0KPGJyPg0KDQo8Zm9udCBjb2xvciA9ICJncmV5Ij4NClRoZXNlIG5vdGVzIGhhdmUgYmVlbiBwcmVwYXJlZCBieSBSdXBlcnQgS3V2ZWtlIGFuZCBBbWFuZGEgU2hha2VyLiBUaGUgY29weXJpZ2h0IGZvciB0aGUgbWF0ZXJpYWwgaW4gdGhlc2Ugbm90ZXMgcmVzaWRlcyB3aXRoIHRoZSBhdXRob3JzIG5hbWVkIGFib3ZlLCB3aXRoIHRoZSBEZXBhcnRtZW50IG9mIE1hdGhlbWF0aWNhbCBhbmQgUGh5c2ljYWwgU2NpZW5jZXMgYW5kIHdpdGggTGEgVHJvYmUgVW5pdmVyc2l0eS4gQ29weXJpZ2h0IGluIHRoaXMgd29yayBpcyB2ZXN0ZWQgaW4gTGEgVHJvYmUgVW5pdmVyc2l0eSBpbmNsdWRpbmcgYWxsIExhIFRyb2JlIFVuaXZlcnNpdHkgYnJhbmRpbmcgYW5kIG5hbWluZy4gVW5sZXNzIG90aGVyd2lzZSBzdGF0ZWQsIG1hdGVyaWFsIHdpdGhpbiB0aGlzIHdvcmsgaXMgbGljZW5zZWQgdW5kZXIgYSBDcmVhdGl2ZSBDb21tb25zIEF0dHJpYnV0aW9uLU5vbiBDb21tZXJjaWFsLU5vbiBEZXJpdmF0aXZlcyBMaWNlbnNlIA0KPGEgaHJlZiA9ICJodHRwczovL2NyZWF0aXZlY29tbW9ucy5vcmcvbGljZW5zZXMvYnktbmMtbmQvNC4wL0NDIiB0YXJnZXQ9Il9ibGFuayI+IEJZLU5DLU5ELiA8L2E+DQo8L2ZvbnQ+