Solutions

suppressMessages(require(rms, quietly = TRUE, warn.conflicts = FALSE))
require(splines, quietly = TRUE)
require(plotly, quietly = TRUE, warn.conflicts = FALSE)

Challenge 1

The first time you install a package within a new R session, you must select the CRAN mirror. Usually the best choice is the repository that is geographically closest to you. Once selected subsequent package installations will use this same server.

What command allows you to select a new CRAN mirror?

chooseCRANmirror()

How do you manually specify the default repository?

options(repos = "http://debian.mc.vanderbilt.edu/R/CRAN/")

Challenge 2

gender <- c('M','M','F','M','F','F','M','F','M')
age <- c(34, 64, 38, 63, 40, 73, 27, 51, 47)
smoker <- c('no','yes','no','no','yes','no','no','no','yes')
exercise <- factor(c('moderate','frequent','some','some','moderate','none',
                     'none','moderate','moderate'),
                    levels=c('none','some','moderate','frequent'), ordered=TRUE
)
los <- c(4,8,1,10,6,3,9,4,8)
x <- data.frame(gender, age, smoker, exercise, los)
  1. Create a linear model using x, estimating the association between los and all remaining variables
lm(los ~ gender + age + smoker + exercise, data=x)

Call:
lm(formula = los ~ gender + age + smoker + exercise, data = x)

Coefficients:
(Intercept)      genderM          age    smokeryes   exercise.L   exercise.Q   exercise.C  
   0.588144     4.508675     0.033377     2.966623    -2.749852    -0.710942     0.002393  
  1. Create a new model, this time predicting los by gender; show the model summary
f <- lm(los ~ gender)
summary(f)

Call:
lm(formula = los ~ gender)

Residuals:
   Min     1Q Median     3Q    Max 
  -3.8   -0.5    0.2    1.2    2.5 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)  
(Intercept)    3.500      1.099   3.186   0.0154 *
genderM        4.300      1.474   2.917   0.0224 *
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 2.197 on 7 degrees of freedom
Multiple R-squared:  0.5487,    Adjusted R-squared:  0.4842 
F-statistic:  8.51 on 1 and 7 DF,  p-value: 0.02243
  1. What is the estimate for the intercept? What is the estimate for gender?
coef(f)
(Intercept)     genderM 
        3.5         4.3 
  1. Re-calculate the standard errors, by taking the square root of the diagonal of the variance-covariance matrix of the summary of the linear model
sqrt(diag(vcov(summary(f))))
(Intercept)     genderM 
   1.098701    1.474061 
  1. Predict los with the following new data set
newdat <- data.frame(gender=c('F','M','F'))
predict(f, newdat)
  1   2   3 
3.5 7.8 3.5 
  1. Sum the square of the residuals of the model. Compare this to passing the model to the deviance function.
sum(resid(f)^2)
[1] 33.8
deviance(f)
[1] 33.8
  1. Create a subset of x by taking all records where gender is ‘M’ and assigning it to the variable men. Do the same for the variable women.
men <- x[x[,'gender'] == 'M',]
women <- x[x[,'gender'] == 'F',]
  1. Call the t.test function, where the first argument is los for women and the second argument is los for men. Add the argument var.equal and set it to TRUE. Does this match the p-value computed in the model summary?
t.test(women[,'los'], men[,'los'], var.equal=TRUE)

    Two Sample t-test

data:  women[, "los"] and men[, "los"]
t = -2.9171, df = 7, p-value = 0.02243
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
 -7.7856014 -0.8143986
sample estimates:
mean of x mean of y 
      3.5       7.8 

Challenge 3

x <- seq(0, 3.5*pi, length=50)
dat <- data.frame(x = x, y = 4 * sin(x) + rnorm(length(x)))
  1. Create a scatterplot of dat.
plot(dat)

  1. f <- lm(y ~ ns(x,1), data = dat) will create a linear model where y is predicted with a natural cubic spline of x; in this case it uses a spline with one degree of freedom. Create five models, increasing the degrees of freedom from 1 to 5.
f1 <- lm(y ~ ns(x,1), data = dat)
f2 <- lm(y ~ ns(x,2), data = dat)
f3 <- lm(y ~ ns(x,3), data = dat)
f4 <- lm(y ~ ns(x,4), data = dat)
f5 <- lm(y ~ ns(x,5), data = dat)
  1. Generate predicted values for the five models with the given data set.
newdat <- data.frame(x=seq(x[1]*0.9, x[50]*1.1, length=50))
p1 <- predict(f1, newdat)
p2 <- predict(f2, newdat)
p3 <- predict(f3, newdat)
p4 <- predict(f4, newdat)
p5 <- predict(f5, newdat)
  1. Recreate the scatterplot of dat, but add lines for the predicted values (try it with base, ggplot, and plotly).
plot(dat, xlim=range(newdat[,'x']))
lines(newdat[,'x'], p1, col=1)
lines(newdat[,'x'], p2, col=2)
lines(newdat[,'x'], p3, col=3)
lines(newdat[,'x'], p4, col=4)
lines(newdat[,'x'], p5, col=5)

predat <- data.frame(x=newdat[,'x'], y=c(p1, p2, p3, p4, p5), model=factor(rep(1:5, each=50)))
p <- ggplot(predat) + aes(x = x, y = y, color = model) + geom_line() + geom_point(data = dat, color='black')
p

ggplotly(p)

Challenge 4

  1. Retrieve the titanic3 data set.
getHdata(titanic3)
  1. Describe the data.
html(describe(titanic3))
titanic3

14 Variables   1309 Observations

pclass
image
nmissingdistinct
130903
 Value        1st   2nd   3rd
 Frequency    323   277   709
 Proportion 0.247 0.212 0.542
 

survived: Survived
nmissingdistinctInfoSumMeanGmd
1309020.7085000.3820.4725

name: Name
nmissingdistinct
130901307
lowest :Abbing, Mr. Anthony Abbott, Master. Eugene Joseph Abbott, Mr. Rossmore Edward Abbott, Mrs. Stanton (Rosa HuntAbelseth, Miss. Karen Marie
highest:Zabour, Miss. Hileni Zabour, Miss. Thamine Zakarian, Mr. Mapriededer Zakarian, Mr. Ortin Zimmerman, Mr. Leo

sex
nmissingdistinct
130902
 Value      female   male
 Frequency     466    843
 Proportion  0.356  0.644
 

age: Age Year
image
nmissingdistinctInfoMeanGmd.05.10.25.50.75.90.95
1046263980.99929.8816.06 5142128395057
lowest : 0.1667 0.3333 0.4167 0.6667 0.7500 , highest: 70.5000 71.0000 74.0000 76.0000 80.0000
sibsp: Number of Siblings/Spouses Aboard
image
nmissingdistinctInfoMeanGmd
1309070.670.49890.777
 Value          0     1     2     3     4     5     8
 Frequency    891   319    42    20    22     6     9
 Proportion 0.681 0.244 0.032 0.015 0.017 0.005 0.007
 

parch: Number of Parents/Children Aboard
image
nmissingdistinctInfoMeanGmd
1309080.5490.3850.6375
 Value          0     1     2     3     4     5     6     9
 Frequency   1002   170   113     8     6     6     2     2
 Proportion 0.765 0.130 0.086 0.006 0.005 0.005 0.002 0.002
 

ticket: Ticket Number
nmissingdistinct
13090929
lowest :110152 110413 110465 110469 110489
highest:W./C. 6607 W./C. 6608 W./C. 6609 W.E.P. 5734WE/P 5735

fare: Passenger Fare British Pound (¤)
image
        n  missing distinct     Info     Mean      Gmd      .05      .10      .25 
     1308        1      281        1     33.3    38.61    7.225    7.567    7.896 
      .50      .75      .90      .95 
   14.454   31.275   78.051  133.650 
 
lowest : 0.0000 3.1708 4.0125 5.0000 6.2375 , highest: 227.5250 247.5208 262.3750 263.0000 512.3292
cabin
nmissingdistinct
13090187
lowest : A10 A11 A14 A16 , highest: F33 F38 F4 G6 T
embarked
image
nmissingdistinct
130723
 Value        Cherbourg  Queenstown Southampton
 Frequency          270         123         914
 Proportion       0.207       0.094       0.699
 

boat
image
nmissingdistinct
1309028
lowest : 1 10 11 12 , highest: A B C C D D
body: Body Identification Number
image
nmissingdistinctInfoMeanGmd.05.10.25.50.75.90.95
12111881211160.8113 16 35 72155256297307
lowest : 1 4 7 9 14 , highest: 312 314 322 327 328
home.dest: Home/Destination
nmissingdistinct
745564368
lowest :Aberdeen / Portland, OR Albany, NY Altdorf, Switzerland Amenia, ND Antwerp, Belgium / Stanton, OH
highest:Worcester, England Worcester, MA Yoevil, England / Cottage GroveYoungstown, OH Zurich, Switzerland

  1. naclus can be used to examine missing data. Use it on the dataset and plot the results.
plot(naclus(titanic3))

  1. Describe age, sex, pclass, and embarked with the summaryM function while stratifying on survived. Show the output as a table and plot.
s <- summaryM(age + sex + pclass + embarked ~ survived, data=titanic3)
plot(s)

html(s)
Descriptive Statistics (N=1309).
N
0
N=809
1
N=500
Age
Year
1046 21 28 39 20 28 38
sex : male 1309 0.84 (682) 0.32 (161)
pclass : 1st 1309 0.15 (123) 0.40 (200)
  2nd 0.20 (158) 0.24 (119)
  3rd 0.65 (528) 0.36 (181)
embarked : Cherbourg 1307 0.15 (120) 0.30 (150)
  Queenstown 0.10 ( 79) 0.09 ( 44)
  Southampton 0.75 (610) 0.61 (304)
a b c represent the lower quartile a, the median b, and the upper quartile c for continuous variables.   N is the number of non-missing values. Numbers after proportions are frequencies.
  1. Use a logistic regression model (lrm) with the formula survived ~ rcs(age,5)*sex+pclass. rcs(age,5) indicates that age will be represented by a restricted cubic spline with five knots. Save the fit object as f and print it.
dd <- datadist(titanic3)
options(datadist = "dd")
f <- lrm(survived ~ rcs(age,5)*sex+pclass, data=titanic3)
f
Frequencies of Missing Values Due to Each Variable
survived      age      sex   pclass 
       0      263        0        0 

Logistic Regression Model
 
 lrm(formula = survived ~ rcs(age, 5) * sex + pclass, data = titanic3)
 
 
                       Model Likelihood     Discrimination    Rank Discrim.    
                          Ratio Test           Indexes           Indexes       
 Obs          1046    LR chi2     465.62    R2       0.485    C       0.854    
  0            619    d.f.            11    g        1.964    Dxy     0.708    
  1            427    Pr(> chi2) <0.0001    gr       7.125    gamma   0.714    
 max |deriv| 8e-08                          gp       0.347    tau-a   0.343    
                                            Brier    0.144                     
 
                   Coef    S.E.   Wald Z Pr(>|Z|)
 Intercept          2.6792 0.5103  5.25  <0.0001 
 age                0.0015 0.0404  0.04  0.9699  
 age'              -0.0341 0.2444 -0.14  0.8891  
 age''              0.2303 1.7373  0.13  0.8945  
 age'''            -0.3131 2.5798 -0.12  0.9034  
 sex=male           0.2338 0.6343  0.37  0.7124  
 pclass=2nd        -1.3745 0.2434 -5.65  <0.0001 
 pclass=3rd        -2.3129 0.2382 -9.71  <0.0001 
 age * sex=male    -0.1892 0.0561 -3.37  0.0007  
 age' * sex=male    0.7244 0.3320  2.18  0.0291  
 age'' * sex=male  -4.1784 2.2962 -1.82  0.0688  
 age''' * sex=male  5.1827 3.3417  1.55  0.1209  
 
  1. Obtain predictions for a data.frame that contains variables for age, sex, and pclass. Provide your own values for three or four observations. Pass the predicted values to plogis to perform a logistic transformation.
dat <- expand.grid(age=30, sex=c('female','male'), pclass=c('1st','2nd'))
plogis(predict(f, dat))
        1         2         3         4 
0.9301634 0.5549355 0.7711294 0.2397830 
  1. Plot the nomogram of the fit object. Add fun=plogis as an argument to nomogram.
plot(nomogram(f, fun=plogis))

  1. Use ggplot and Predict to plot the predictors age, sex, and pclass. Include fun=plogis as an argument to Predict.
ggplot(Predict(f, age, sex, pclass, fun=plogis))

Challenge 5

The CDC has many available data sets. Take a look at the demographics data file for the NHANES National Youth Fitness Survey. This is an XPT file, or SAS export file. The code book can be viewed here.

  1. Download, then import this dataset.
dat <- foreign::read.xport("~/Downloads/Y_DEMO.XPT")
  1. Keep only the following variables: RIAGENDR, RIDAGEYR, INDHHIND2, DMDHHSIZ. Describe the data.
dat <- dat[,c('RIAGENDR','RIDAGEYR','INDHHIN2','DMDHHSIZ')]
html(describe(dat))
dat

4 Variables   1640 Observations

RIAGENDR
nmissingdistinctInfoMeanGmd
1640020.751.4980.5003
 Value          1     2
 Frequency    823   817
 Proportion 0.502 0.498
 

RIDAGEYR
image
nmissingdistinctInfoMeanGmd.05.10.25.50.75.90.95
16400130.9948.9784.255 3 4 6 9121415
 Value          3     4     5     6     7     8     9    10    11    12    13    14
 Frequency    118   123   127   140   134   129   110   132   117   140   129   136
 Proportion 0.072 0.075 0.077 0.085 0.082 0.079 0.067 0.080 0.071 0.085 0.079 0.083
                 
 Value         15
 Frequency    105
 Proportion 0.064
 

INDHHIN2
image
nmissingdistinctInfoMeanGmd.05.10.25.50.75.90.95
16346160.98810.988.935 2 3 5 8141515
 Value          1     2     3     4     5     6     7     8     9    10    12    13
 Frequency     44    57   100   101   138   171   117   101   110    82    63    23
 Proportion 0.027 0.035 0.061 0.062 0.084 0.105 0.072 0.062 0.067 0.050 0.039 0.014
                                   
 Value         14    15    77    99
 Frequency    158   325    26    18
 Proportion 0.097 0.199 0.016 0.011
 

DMDHHSIZ
image
nmissingdistinctInfoMeanGmd
1640060.9374.6121.38
 Value          2     3     4     5     6     7
 Frequency     50   233   572   384   251   150
 Proportion 0.030 0.142 0.349 0.234 0.153 0.091
 

  1. Turn RIAGENDR into a factor variable with the proper value labels.
dat[,'RIAGENDR'] <- factor(dat[,'RIAGENDR'], labels=c('Male','Female'))
  1. Modify INDHHIN2 by collapsing:
  • 1,2,3,4,13 into 1
  • 5,6,7,8 into 2
  • 9,10,14 into 3
  • 15 into 4
  • 12,77,99 into NA

Then turn it into an ordered factor variable with the labels $0-$19999, $20000-$54999, $55000-$99999, $100000+.

tmpinc <- dat[,'INDHHIN2']
tmpinc[tmpinc %in% c(1,2,3,4,13)] <- 1
tmpinc[tmpinc %in% c(5,6,7,8)] <- 2
tmpinc[tmpinc %in% c(9,10,14)] <- 3
tmpinc[tmpinc == 15] <- 4
tmpinc[!(tmpinc %in% c(1,2,3,4))] <- NA
dat[,'INDHHIN2'] <- factor(tmpinc,
                           labels=c('$0-$19999', '$20000-$54999', '$55000-$99999', '$100000+'),
                           ordered=TRUE)
  1. Show the table of gender against age, and household income against size.
with(dat, table(RIAGENDR, RIDAGEYR))
        RIDAGEYR
RIAGENDR  3  4  5  6  7  8  9 10 11 12 13 14 15
  Male   60 65 62 75 64 62 56 66 54 74 62 63 60
  Female 58 58 65 65 70 67 54 66 63 66 67 73 45
with(dat, table(INDHHIN2, DMDHHSIZ))
               DMDHHSIZ
INDHHIN2          2   3   4   5   6   7
  $0-$19999      18  52  82  92  47  34
  $20000-$54999  21  81 184  75 105  61
  $55000-$99999   8  52 147  91  34  18
  $100000+        2  35 132  97  38  21

Challenge 6

Suppose you have a sample of N=100 from the Gamma distribution with shape=2 and scale=1. Calculate the 75th percentile with a basic bootstrapped 95% confidence interval for the upper quartile (75th percentile).

N <- 100
G <- rgamma(N, 2, 1)
# point estimate
PE <- quantile(G, 0.75)
# Bootstrap the 75th percentile
loops <- 1000
x <- numeric(loops)
for(i in seq(loops)) {
  x[i] <- quantile(sample(G, replace=TRUE), 0.75)
}
lb <- quantile(x, 0.025)
ub <- quantile(x, 0.975)
myCI <- unname(2 * PE - c(ub, lb))
myCI
[1] 2.015070 3.145078

Did the CI work?

truth <- quantile(rgamma(1e6, 2, 1), 0.75)
truth > myCI[1] & truth < myCI[2]
 75% 
TRUE 

boot package solution

require(boot, quietly = TRUE, warn.conflicts = FALSE)
boot.ci(boot(G, function(i, j) quantile(i[j], 0.75), R = 1000))
bootstrap variances needed for studentized intervals
BOOTSTRAP CONFIDENCE INTERVAL CALCULATIONS
Based on 1000 bootstrap replicates

CALL : 
boot.ci(boot.out = boot(G, function(i, j) quantile(i[j], 0.75), 
    R = 1000))

Intervals : 
Level      Normal              Basic         
95%   ( 2.195,  3.317 )   ( 2.015,  3.161 )  

Level     Percentile            BCa          
95%   ( 2.375,  3.521 )   ( 2.375,  3.521 )  
Calculations and Intervals on Original Scale

Challenge 7

https://fivethirtyeight.com/features/can-you-solve-these-colorful-puzzles/

You play a game with four balls: One ball is red, one is blue, one is green and one is yellow. They are placed in a box. You draw a ball out of the box at random and note its color. Without replacing the first ball, you draw a second ball and then paint it to match the color of the first. Replace both balls, and repeat the process. The game ends when all four balls have become the same color. What is the expected number of turns to finish the game?

Simulate the answer.

draw <- function(x) sample(x)[c(1,1,3,4)]
test <- function(ii=c('r','b','g','y')) {
  i <- 1
  while(length(unique(ii<-draw(ii))) > 1) i <- i+1
  i
}
N <- 1000
ans <- numeric(N)
for(i in seq(N)) {
  ans[i] <- test()
}
mean(ans)
[1] 9.001
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKLS0tCgojIFNvbHV0aW9ucwoKYGBge3Igc2V0dXB9CnN1cHByZXNzTWVzc2FnZXMocmVxdWlyZShybXMsIHF1aWV0bHkgPSBUUlVFLCB3YXJuLmNvbmZsaWN0cyA9IEZBTFNFKSkKcmVxdWlyZShzcGxpbmVzLCBxdWlldGx5ID0gVFJVRSkKcmVxdWlyZShwbG90bHksIHF1aWV0bHkgPSBUUlVFLCB3YXJuLmNvbmZsaWN0cyA9IEZBTFNFKQpgYGAKCiMjIENoYWxsZW5nZSAxCgpUaGUgZmlyc3QgdGltZSB5b3UgaW5zdGFsbCBhIHBhY2thZ2Ugd2l0aGluIGEgbmV3IFIgc2Vzc2lvbiwgeW91IG11c3Qgc2VsZWN0IHRoZSBDUkFOIG1pcnJvci4gIFVzdWFsbHkgdGhlIGJlc3QgY2hvaWNlIGlzIHRoZSByZXBvc2l0b3J5IHRoYXQgaXMgZ2VvZ3JhcGhpY2FsbHkgY2xvc2VzdCB0byB5b3UuICBPbmNlIHNlbGVjdGVkIHN1YnNlcXVlbnQgcGFja2FnZSBpbnN0YWxsYXRpb25zIHdpbGwgdXNlIHRoaXMgc2FtZSBzZXJ2ZXIuCgpXaGF0IGNvbW1hbmQgYWxsb3dzIHlvdSB0byBzZWxlY3QgYSBuZXcgQ1JBTiBtaXJyb3I/CgpgYGB7ciwgZXZhbD1GQUxTRX0KY2hvb3NlQ1JBTm1pcnJvcigpCmBgYAoKSG93IGRvIHlvdSBtYW51YWxseSBzcGVjaWZ5IHRoZSBkZWZhdWx0IHJlcG9zaXRvcnk/CgpgYGB7ciwgZXZhbD1GQUxTRX0Kb3B0aW9ucyhyZXBvcyA9ICJodHRwOi8vZGViaWFuLm1jLnZhbmRlcmJpbHQuZWR1L1IvQ1JBTi8iKQpgYGAKCiMjIENoYWxsZW5nZSAyCgpgYGB7cn0KZ2VuZGVyIDwtIGMoJ00nLCdNJywnRicsJ00nLCdGJywnRicsJ00nLCdGJywnTScpCmFnZSA8LSBjKDM0LCA2NCwgMzgsIDYzLCA0MCwgNzMsIDI3LCA1MSwgNDcpCnNtb2tlciA8LSBjKCdubycsJ3llcycsJ25vJywnbm8nLCd5ZXMnLCdubycsJ25vJywnbm8nLCd5ZXMnKQpleGVyY2lzZSA8LSBmYWN0b3IoYygnbW9kZXJhdGUnLCdmcmVxdWVudCcsJ3NvbWUnLCdzb21lJywnbW9kZXJhdGUnLCdub25lJywKICAgICAgICAgICAgICAgICAgICAgJ25vbmUnLCdtb2RlcmF0ZScsJ21vZGVyYXRlJyksCiAgICAgICAgICAgICAgICAgICAgbGV2ZWxzPWMoJ25vbmUnLCdzb21lJywnbW9kZXJhdGUnLCdmcmVxdWVudCcpLCBvcmRlcmVkPVRSVUUKKQpsb3MgPC0gYyg0LDgsMSwxMCw2LDMsOSw0LDgpCnggPC0gZGF0YS5mcmFtZShnZW5kZXIsIGFnZSwgc21va2VyLCBleGVyY2lzZSwgbG9zKQpgYGAKCjEuIENyZWF0ZSBhIGxpbmVhciBtb2RlbCB1c2luZyBgeGAsIGVzdGltYXRpbmcgdGhlIGFzc29jaWF0aW9uIGJldHdlZW4gYGxvc2AgYW5kIGFsbCByZW1haW5pbmcgdmFyaWFibGVzCgpgYGB7cn0KbG0obG9zIH4gZ2VuZGVyICsgYWdlICsgc21va2VyICsgZXhlcmNpc2UsIGRhdGE9eCkKYGBgCgoyLiBDcmVhdGUgYSBuZXcgbW9kZWwsIHRoaXMgdGltZSBwcmVkaWN0aW5nIGBsb3NgIGJ5IGBnZW5kZXJgOyBzaG93IHRoZSBtb2RlbCBzdW1tYXJ5CgpgYGB7cn0KZiA8LSBsbShsb3MgfiBnZW5kZXIpCnN1bW1hcnkoZikKYGBgCgozLiBXaGF0IGlzIHRoZSBlc3RpbWF0ZSBmb3IgdGhlIGludGVyY2VwdD8gIFdoYXQgaXMgdGhlIGVzdGltYXRlIGZvciBnZW5kZXI/CgpgYGB7cn0KY29lZihmKQpgYGAKCjQuIFJlLWNhbGN1bGF0ZSB0aGUgc3RhbmRhcmQgZXJyb3JzLCBieSB0YWtpbmcgdGhlIHNxdWFyZSByb290IG9mIHRoZSBkaWFnb25hbCBvZiB0aGUgdmFyaWFuY2UtY292YXJpYW5jZSBtYXRyaXggb2YgdGhlIHN1bW1hcnkgb2YgdGhlIGxpbmVhciBtb2RlbAoKYGBge3J9CnNxcnQoZGlhZyh2Y292KHN1bW1hcnkoZikpKSkKYGBgCgo1LiBQcmVkaWN0IGBsb3NgIHdpdGggdGhlIGZvbGxvd2luZyBuZXcgZGF0YSBzZXQKCmBgYHtyfQpuZXdkYXQgPC0gZGF0YS5mcmFtZShnZW5kZXI9YygnRicsJ00nLCdGJykpCnByZWRpY3QoZiwgbmV3ZGF0KQpgYGAKCjYuIFN1bSB0aGUgc3F1YXJlIG9mIHRoZSByZXNpZHVhbHMgb2YgdGhlIG1vZGVsLiAgQ29tcGFyZSB0aGlzIHRvIHBhc3NpbmcgdGhlIG1vZGVsIHRvIHRoZSBgZGV2aWFuY2VgIGZ1bmN0aW9uLgoKYGBge3J9CnN1bShyZXNpZChmKV4yKQpkZXZpYW5jZShmKQpgYGAKCjcuIENyZWF0ZSBhIHN1YnNldCBvZiBgeGAgYnkgdGFraW5nIGFsbCByZWNvcmRzIHdoZXJlIGBnZW5kZXJgIGlzICdNJyBhbmQgYXNzaWduaW5nIGl0IHRvIHRoZSB2YXJpYWJsZSBgbWVuYC4gRG8gdGhlIHNhbWUgZm9yIHRoZSB2YXJpYWJsZSBgd29tZW5gLgoKYGBge3J9Cm1lbiA8LSB4W3hbLCdnZW5kZXInXSA9PSAnTScsXQp3b21lbiA8LSB4W3hbLCdnZW5kZXInXSA9PSAnRicsXQpgYGAKCjguIENhbGwgdGhlIGB0LnRlc3RgIGZ1bmN0aW9uLCB3aGVyZSB0aGUgZmlyc3QgYXJndW1lbnQgaXMgbG9zIGZvciB3b21lbiBhbmQgdGhlIHNlY29uZCBhcmd1bWVudCBpcyBsb3MgZm9yIG1lbi4gIEFkZCB0aGUgYXJndW1lbnQgdmFyLmVxdWFsIGFuZCBzZXQgaXQgdG8gVFJVRS4gIERvZXMgdGhpcyBtYXRjaCB0aGUgcC12YWx1ZSBjb21wdXRlZCBpbiB0aGUgbW9kZWwgc3VtbWFyeT8KCmBgYHtyfQp0LnRlc3Qod29tZW5bLCdsb3MnXSwgbWVuWywnbG9zJ10sIHZhci5lcXVhbD1UUlVFKQpgYGAKCiMjIENoYWxsZW5nZSAzCgpgYGB7cn0KeCA8LSBzZXEoMCwgMy41KnBpLCBsZW5ndGg9NTApCmRhdCA8LSBkYXRhLmZyYW1lKHggPSB4LCB5ID0gNCAqIHNpbih4KSArIHJub3JtKGxlbmd0aCh4KSkpCmBgYAoKMS4gQ3JlYXRlIGEgc2NhdHRlcnBsb3Qgb2YgYGRhdGAuCgpgYGB7cn0KcGxvdChkYXQpCmBgYAoKMi4gYGYgPC0gbG0oeSB+IG5zKHgsMSksIGRhdGEgPSBkYXQpYCB3aWxsIGNyZWF0ZSBhIGxpbmVhciBtb2RlbCB3aGVyZSBgeWAgaXMgcHJlZGljdGVkIHdpdGggYSBuYXR1cmFsIGN1YmljIHNwbGluZSBvZiBgeGA7IGluIHRoaXMgY2FzZSBpdCB1c2VzIGEgc3BsaW5lIHdpdGggb25lIGRlZ3JlZSBvZiBmcmVlZG9tLiAgQ3JlYXRlIGZpdmUgbW9kZWxzLCBpbmNyZWFzaW5nIHRoZSBkZWdyZWVzIG9mIGZyZWVkb20gZnJvbSAxIHRvIDUuCgpgYGB7cn0KZjEgPC0gbG0oeSB+IG5zKHgsMSksIGRhdGEgPSBkYXQpCmYyIDwtIGxtKHkgfiBucyh4LDIpLCBkYXRhID0gZGF0KQpmMyA8LSBsbSh5IH4gbnMoeCwzKSwgZGF0YSA9IGRhdCkKZjQgPC0gbG0oeSB+IG5zKHgsNCksIGRhdGEgPSBkYXQpCmY1IDwtIGxtKHkgfiBucyh4LDUpLCBkYXRhID0gZGF0KQpgYGAKCjMuIEdlbmVyYXRlIHByZWRpY3RlZCB2YWx1ZXMgZm9yIHRoZSBmaXZlIG1vZGVscyB3aXRoIHRoZSBnaXZlbiBkYXRhIHNldC4KCmBgYHtyfQpuZXdkYXQgPC0gZGF0YS5mcmFtZSh4PXNlcSh4WzFdKjAuOSwgeFs1MF0qMS4xLCBsZW5ndGg9NTApKQpgYGAKCmBgYHtyfQpwMSA8LSBwcmVkaWN0KGYxLCBuZXdkYXQpCnAyIDwtIHByZWRpY3QoZjIsIG5ld2RhdCkKcDMgPC0gcHJlZGljdChmMywgbmV3ZGF0KQpwNCA8LSBwcmVkaWN0KGY0LCBuZXdkYXQpCnA1IDwtIHByZWRpY3QoZjUsIG5ld2RhdCkKYGBgCgo0LiBSZWNyZWF0ZSB0aGUgc2NhdHRlcnBsb3Qgb2YgYGRhdGAsIGJ1dCBhZGQgbGluZXMgZm9yIHRoZSBwcmVkaWN0ZWQgdmFsdWVzICh0cnkgaXQgd2l0aCBiYXNlLCBnZ3Bsb3QsIGFuZCBwbG90bHkpLgoKYGBge3J9CnBsb3QoZGF0LCB4bGltPXJhbmdlKG5ld2RhdFssJ3gnXSkpCmxpbmVzKG5ld2RhdFssJ3gnXSwgcDEsIGNvbD0xKQpsaW5lcyhuZXdkYXRbLCd4J10sIHAyLCBjb2w9MikKbGluZXMobmV3ZGF0WywneCddLCBwMywgY29sPTMpCmxpbmVzKG5ld2RhdFssJ3gnXSwgcDQsIGNvbD00KQpsaW5lcyhuZXdkYXRbLCd4J10sIHA1LCBjb2w9NSkKYGBgCgpgYGB7cn0KcHJlZGF0IDwtIGRhdGEuZnJhbWUoeD1uZXdkYXRbLCd4J10sIHk9YyhwMSwgcDIsIHAzLCBwNCwgcDUpLCBtb2RlbD1mYWN0b3IocmVwKDE6NSwgZWFjaD01MCkpKQpwIDwtIGdncGxvdChwcmVkYXQpICsgYWVzKHggPSB4LCB5ID0geSwgY29sb3IgPSBtb2RlbCkgKyBnZW9tX2xpbmUoKSArIGdlb21fcG9pbnQoZGF0YSA9IGRhdCwgY29sb3I9J2JsYWNrJykKcApgYGAKCmBgYHtyfQpnZ3Bsb3RseShwKQpgYGAKCiMjIENoYWxsZW5nZSA0CgoxLiBSZXRyaWV2ZSB0aGUgYHRpdGFuaWMzYCBkYXRhIHNldC4KCmBgYHtyfQpnZXRIZGF0YSh0aXRhbmljMykKYGBgCgoyLiBEZXNjcmliZSB0aGUgZGF0YS4KCmBgYHtyfQpodG1sKGRlc2NyaWJlKHRpdGFuaWMzKSkKYGBgCgozLiBgbmFjbHVzYCBjYW4gYmUgdXNlZCB0byBleGFtaW5lIG1pc3NpbmcgZGF0YS4gIFVzZSBpdCBvbiB0aGUgZGF0YXNldCBhbmQgcGxvdCB0aGUgcmVzdWx0cy4KCmBgYHtyfQpwbG90KG5hY2x1cyh0aXRhbmljMykpCmBgYAoKNC4gRGVzY3JpYmUgYGFnZWAsIGBzZXhgLCBgcGNsYXNzYCwgYW5kIGBlbWJhcmtlZGAgd2l0aCB0aGUgYHN1bW1hcnlNYCBmdW5jdGlvbiB3aGlsZSBzdHJhdGlmeWluZyBvbiBgc3Vydml2ZWRgLiAgU2hvdyB0aGUgb3V0cHV0IGFzIGEgdGFibGUgYW5kIHBsb3QuCgpgYGB7cn0KcyA8LSBzdW1tYXJ5TShhZ2UgKyBzZXggKyBwY2xhc3MgKyBlbWJhcmtlZCB+IHN1cnZpdmVkLCBkYXRhPXRpdGFuaWMzKQpwbG90KHMpCmh0bWwocykKYGBgCgo1LiBVc2UgYSBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsIChgbHJtYCkgd2l0aCB0aGUgZm9ybXVsYSBgc3Vydml2ZWQgfiByY3MoYWdlLDUpKnNleCtwY2xhc3NgLiAgYHJjcyhhZ2UsNSlgIGluZGljYXRlcyB0aGF0IGFnZSB3aWxsIGJlIHJlcHJlc2VudGVkIGJ5IGEgcmVzdHJpY3RlZCBjdWJpYyBzcGxpbmUgd2l0aCBmaXZlIGtub3RzLiAgU2F2ZSB0aGUgZml0IG9iamVjdCBhcyBgZmAgYW5kIHByaW50IGl0LgoKYGBge3J9CmRkIDwtIGRhdGFkaXN0KHRpdGFuaWMzKQpvcHRpb25zKGRhdGFkaXN0ID0gImRkIikKZiA8LSBscm0oc3Vydml2ZWQgfiByY3MoYWdlLDUpKnNleCtwY2xhc3MsIGRhdGE9dGl0YW5pYzMpCmYKYGBgCgo2LiBPYnRhaW4gcHJlZGljdGlvbnMgZm9yIGEgZGF0YS5mcmFtZSB0aGF0IGNvbnRhaW5zIHZhcmlhYmxlcyBmb3IgYGFnZWAsIGBzZXhgLCBhbmQgYHBjbGFzc2AuICBQcm92aWRlIHlvdXIgb3duIHZhbHVlcyBmb3IgdGhyZWUgb3IgZm91ciBvYnNlcnZhdGlvbnMuICBQYXNzIHRoZSBwcmVkaWN0ZWQgdmFsdWVzIHRvIGBwbG9naXNgIHRvIHBlcmZvcm0gYSBsb2dpc3RpYyB0cmFuc2Zvcm1hdGlvbi4KCmBgYHtyfQpkYXQgPC0gZXhwYW5kLmdyaWQoYWdlPTMwLCBzZXg9YygnZmVtYWxlJywnbWFsZScpLCBwY2xhc3M9YygnMXN0JywnMm5kJykpCnBsb2dpcyhwcmVkaWN0KGYsIGRhdCkpCmBgYAoKNy4gUGxvdCB0aGUgYG5vbW9ncmFtYCBvZiB0aGUgZml0IG9iamVjdC4gIEFkZCBgZnVuPXBsb2dpc2AgYXMgYW4gYXJndW1lbnQgdG8gYG5vbW9ncmFtYC4KCmBgYHtyfQpwbG90KG5vbW9ncmFtKGYsIGZ1bj1wbG9naXMpKQpgYGAKCjguIFVzZSBgZ2dwbG90YCBhbmQgYFByZWRpY3RgIHRvIHBsb3QgdGhlIHByZWRpY3RvcnMgYGFnZWAsIGBzZXhgLCBhbmQgYHBjbGFzc2AuICBJbmNsdWRlIGBmdW49cGxvZ2lzYCBhcyBhbiBhcmd1bWVudCB0byBgUHJlZGljdGAuCgpgYGB7cn0KZ2dwbG90KFByZWRpY3QoZiwgYWdlLCBzZXgsIHBjbGFzcywgZnVuPXBsb2dpcykpCmBgYAoKIyMgQ2hhbGxlbmdlIDUKClRoZSBDREMgaGFzIG1hbnkgYXZhaWxhYmxlIGRhdGEgc2V0cy4gIFRha2UgYSBsb29rIGF0IHRoZSBkZW1vZ3JhcGhpY3MgZGF0YSBmaWxlIGZvciB0aGUgW05IQU5FUyBOYXRpb25hbCBZb3V0aCBGaXRuZXNzIFN1cnZleV0oaHR0cHM6Ly93d3duLmNkYy5nb3YvbmNocy9uaGFuZXMvc2VhcmNoL25ueWZzZGF0YS5hc3B4P0NvbXBvbmVudD1EZW1vZ3JhcGhpY3MpLiAgVGhpcyBpcyBhbiBYUFQgZmlsZSwgb3IgU0FTIGV4cG9ydCBmaWxlLiAgVGhlIGNvZGUgYm9vayBjYW4gYmUgdmlld2VkIFtoZXJlXShodHRwczovL3d3d24uY2RjLmdvdi9OY2hzL05ueWZzL1lfREVNTy5odG0pLgoKMS4gRG93bmxvYWQsIHRoZW4gaW1wb3J0IHRoaXMgZGF0YXNldC4KCmBgYHtyfQpkYXQgPC0gZm9yZWlnbjo6cmVhZC54cG9ydCgifi9Eb3dubG9hZHMvWV9ERU1PLlhQVCIpCmBgYAoKMi4gS2VlcCBvbmx5IHRoZSBmb2xsb3dpbmcgdmFyaWFibGVzOiBgUklBR0VORFIsIFJJREFHRVlSLCBJTkRISElORDIsIERNREhIU0laYC4gIERlc2NyaWJlIHRoZSBkYXRhLgoKYGBge3J9CmRhdCA8LSBkYXRbLGMoJ1JJQUdFTkRSJywnUklEQUdFWVInLCdJTkRISElOMicsJ0RNREhIU0laJyldCmh0bWwoZGVzY3JpYmUoZGF0KSkKYGBgCgozLiBUdXJuIGBSSUFHRU5EUmAgaW50byBhIGZhY3RvciB2YXJpYWJsZSB3aXRoIHRoZSBwcm9wZXIgdmFsdWUgbGFiZWxzLgoKYGBge3J9CmRhdFssJ1JJQUdFTkRSJ10gPC0gZmFjdG9yKGRhdFssJ1JJQUdFTkRSJ10sIGxhYmVscz1jKCdNYWxlJywnRmVtYWxlJykpCmBgYAoKNC4gTW9kaWZ5IGBJTkRISElOMmAgYnkgY29sbGFwc2luZzoKCiogMSwyLDMsNCwxMyBpbnRvIDEKKiA1LDYsNyw4IGludG8gMgoqIDksMTAsMTQgaW50byAzCiogMTUgaW50byA0CiogMTIsNzcsOTkgaW50byBOQQoKVGhlbiB0dXJuIGl0IGludG8gYW4gb3JkZXJlZCBmYWN0b3IgdmFyaWFibGUgd2l0aCB0aGUgbGFiZWxzIGAkMC0kMTk5OTksICQyMDAwMC0kNTQ5OTksICQ1NTAwMC0kOTk5OTksICQxMDAwMDArYC4KCmBgYHtyfQp0bXBpbmMgPC0gZGF0WywnSU5ESEhJTjInXQp0bXBpbmNbdG1waW5jICVpbiUgYygxLDIsMyw0LDEzKV0gPC0gMQp0bXBpbmNbdG1waW5jICVpbiUgYyg1LDYsNyw4KV0gPC0gMgp0bXBpbmNbdG1waW5jICVpbiUgYyg5LDEwLDE0KV0gPC0gMwp0bXBpbmNbdG1waW5jID09IDE1XSA8LSA0CnRtcGluY1shKHRtcGluYyAlaW4lIGMoMSwyLDMsNCkpXSA8LSBOQQpkYXRbLCdJTkRISElOMiddIDwtIGZhY3Rvcih0bXBpbmMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCckMC0kMTk5OTknLCAnJDIwMDAwLSQ1NDk5OScsICckNTUwMDAtJDk5OTk5JywgJyQxMDAwMDArJyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG9yZGVyZWQ9VFJVRSkKYGBgCgo1LiBTaG93IHRoZSBgdGFibGVgIG9mIGdlbmRlciBhZ2FpbnN0IGFnZSwgYW5kIGhvdXNlaG9sZCBpbmNvbWUgYWdhaW5zdCBzaXplLgoKYGBge3J9CndpdGgoZGF0LCB0YWJsZShSSUFHRU5EUiwgUklEQUdFWVIpKQp3aXRoKGRhdCwgdGFibGUoSU5ESEhJTjIsIERNREhIU0laKSkKYGBgCgojIyBDaGFsbGVuZ2UgNgoKU3VwcG9zZSB5b3UgaGF2ZSBhIHNhbXBsZSBvZiBOPTEwMCBmcm9tIHRoZSBHYW1tYSBkaXN0cmlidXRpb24gd2l0aCBzaGFwZT0yIGFuZCBzY2FsZT0xLiAgQ2FsY3VsYXRlIHRoZSA3NXRoIHBlcmNlbnRpbGUgd2l0aCBhIGJhc2ljIGJvb3RzdHJhcHBlZCA5NSUgY29uZmlkZW5jZSBpbnRlcnZhbCBmb3IgdGhlIHVwcGVyIHF1YXJ0aWxlICg3NXRoIHBlcmNlbnRpbGUpLgoKYGBge3J9Ck4gPC0gMTAwCkcgPC0gcmdhbW1hKE4sIDIsIDEpCiMgcG9pbnQgZXN0aW1hdGUKUEUgPC0gcXVhbnRpbGUoRywgMC43NSkKIyBCb290c3RyYXAgdGhlIDc1dGggcGVyY2VudGlsZQpsb29wcyA8LSAxMDAwCnggPC0gbnVtZXJpYyhsb29wcykKZm9yKGkgaW4gc2VxKGxvb3BzKSkgewogIHhbaV0gPC0gcXVhbnRpbGUoc2FtcGxlKEcsIHJlcGxhY2U9VFJVRSksIDAuNzUpCn0KbGIgPC0gcXVhbnRpbGUoeCwgMC4wMjUpCnViIDwtIHF1YW50aWxlKHgsIDAuOTc1KQpteUNJIDwtIHVubmFtZSgyICogUEUgLSBjKHViLCBsYikpCm15Q0kKYGBgCgpEaWQgdGhlIENJIHdvcms/CgpgYGB7cn0KdHJ1dGggPC0gcXVhbnRpbGUocmdhbW1hKDFlNiwgMiwgMSksIDAuNzUpCnRydXRoID4gbXlDSVsxXSAmIHRydXRoIDwgbXlDSVsyXQpgYGAKCmBib290YCBwYWNrYWdlIHNvbHV0aW9uCgpgYGB7cn0KcmVxdWlyZShib290LCBxdWlldGx5ID0gVFJVRSwgd2Fybi5jb25mbGljdHMgPSBGQUxTRSkKYm9vdC5jaShib290KEcsIGZ1bmN0aW9uKGksIGopIHF1YW50aWxlKGlbal0sIDAuNzUpLCBSID0gMTAwMCkpCmBgYAoKIyMgQ2hhbGxlbmdlIDcKCltodHRwczovL2ZpdmV0aGlydHllaWdodC5jb20vZmVhdHVyZXMvY2FuLXlvdS1zb2x2ZS10aGVzZS1jb2xvcmZ1bC1wdXp6bGVzL10oUHV6emxlcikKCllvdSBwbGF5IGEgZ2FtZSB3aXRoIGZvdXIgYmFsbHM6IE9uZSBiYWxsIGlzIHJlZCwgb25lIGlzIGJsdWUsIG9uZSBpcyBncmVlbiBhbmQgb25lIGlzIHllbGxvdy4gVGhleSBhcmUgcGxhY2VkIGluIGEgYm94LiBZb3UgZHJhdyBhIGJhbGwgb3V0IG9mIHRoZSBib3ggYXQgcmFuZG9tIGFuZCBub3RlIGl0cyBjb2xvci4gV2l0aG91dCByZXBsYWNpbmcgdGhlIGZpcnN0IGJhbGwsIHlvdSBkcmF3IGEgc2Vjb25kIGJhbGwgYW5kIHRoZW4gcGFpbnQgaXQgdG8gbWF0Y2ggdGhlIGNvbG9yIG9mIHRoZSBmaXJzdC4gUmVwbGFjZSBib3RoIGJhbGxzLCBhbmQgcmVwZWF0IHRoZSBwcm9jZXNzLiBUaGUgZ2FtZSBlbmRzIHdoZW4gYWxsIGZvdXIgYmFsbHMgaGF2ZSBiZWNvbWUgdGhlIHNhbWUgY29sb3IuIFdoYXQgaXMgdGhlIGV4cGVjdGVkIG51bWJlciBvZiB0dXJucyB0byBmaW5pc2ggdGhlIGdhbWU/CgpTaW11bGF0ZSB0aGUgYW5zd2VyLgoKYGBge3J9CmRyYXcgPC0gZnVuY3Rpb24oeCkgc2FtcGxlKHgpW2MoMSwxLDMsNCldCnRlc3QgPC0gZnVuY3Rpb24oaWk9YygncicsJ2InLCdnJywneScpKSB7CiAgaSA8LSAxCiAgd2hpbGUobGVuZ3RoKHVuaXF1ZShpaTwtZHJhdyhpaSkpKSA+IDEpIGkgPC0gaSsxCiAgaQp9Ck4gPC0gMTAwMAphbnMgPC0gbnVtZXJpYyhOKQpmb3IoaSBpbiBzZXEoTikpIHsKICBhbnNbaV0gPC0gdGVzdCgpCn0KbWVhbihhbnMpCmBgYAo=