Introduction
R is designed to only use one cpu (or core) when running tasks.
However, you may have access to a computer cluster that allows you to
access more RAM and cpus. The use of more than one cpu is known as
parallel computing in R. The goal of this tutorial is to provide the
basics of using the batchtools
package and utilizing more
cores in a cluster.
This tutorial is built off the information provided by UCR’s High
Performance Computing Center tutorial of batchtools
package. This tutorial uses a simulation study to show you the power of
batchtools
. For more information visit their help
documentation for batchtools.
This tutorial is meant to be run on the HPCC cluster at UCR. You will need an
account to the HPCC cluster. If you are a graduate student in UCR’s Statistics Department, contact
the UCR Statistics Graduate Student Association to gain access.
This tutorial conducts different simulation scenarios to be submitted
as jobs. It requires an amount of set up to be effective. There is an an
R script that you can use that may provide better insight on using
batchtools
. You can access R script here
Simulation Example
To demonstrate how to use the batchtools
package in R,
we conduct several simulation studies showing how the estimates from the
ordinary least squares estimator leads to unbiased results. We will
simulate data from the following models:
\[
Y \sim N(20+30X,\ 3)
\] \[
Y \sim N(5-8X_1+2X_2,\ 3)
\]
\[
Y \sim N(5+4X_1-5X_2-3X_3,\ 3)
\]
\[
Y \sim N(5+4X_1+-5X_2-3X_3+6X_4,\ 3)
\] Each simulation scenario with have 500 data sets with 200
observations. The values for the predictor variables will be simulated
by multivariate normal distributions. The mean vector for the predictors
simulation are \((-2, 0)^T\), \((-2, 0)^T\), \((-2, 0, 2)^T\), \((-2, 0, 2 8)^T\). Each covariance for the
predictor simulation will be an identity matrix.
Simulation Parameters
The simulation parameters will be stored in a list. Each element in
the list will contain information of the about the simulation and the
formula for the lm()
function.
sim_list <- list(list(N = 500, # Number of Data sets
nobs = 200, # Number of observations
beta = c(20, 30), # beta parameters
xmeans = c(0), # Means for predictors
xsigs = diag(rep(1, 1)), # Variance for predictor
sigma = 3, # Variance for error term
formula = y ~ x), #Formula
list(N = 500, # Number of Data sets
nobs = 200, # Number of observations
beta = c(5, -8, 2), # beta parameters
xmeans = c(-2, 0), # Means for predictors
xsigs = diag(rep(1, 2)), # Variance for predictor
sigma = 3, # Variance for error term
formula = y ~ x.1 + x.2), #Formula
list(N = 500, # Number of Data sets
nobs = 200, # Number of observations
beta = c(5, 4, -5, -3), # beta parameters
xmeans = c(-2, 0, 2), # Means for predictors
xsigs = diag(rep(1, 3)), # Variance for predictor
sigma = 3, # Variance for error term
formula = y ~ x.1 + x.2 + x.3), #Formula
list(N = 500, # Number of Data sets
nobs = 200, # Number of observations
beta = c(5, 4, -5, -3, 6), # beta parameters
xmeans = c(-2, 0, 2, 8), # Means for predictors
xsigs = diag(rep(1, 4)), # Variance for predictor
sigma = 3, # Variance for error term
formula = y ~ x.1 + x.2 + x.3 + x.4) #Formula
)
Simulation Functions
The function below generates 1 data set from a simulation scenario
above and returns a data frame.
data_set_sim <- function(seed, nobs, beta, sigma, xmeans, xsigs){ # Simulates the data set
set.seed(seed) # Sets a seed
xrn <- rmvnorm(nobs, mean = xmeans, sigma = xsigs) # Simulates Predictors
xped <- cbind(rep(1,nobs),xrn) # Creating Design Matrix
y <- xped %*% beta + rnorm(nobs ,0, sigma) # Simulating Y
df <- data.frame(x=xrn, y=y) # Creating Data Frame
return(df)
}
The function needs the following arguments:
seed
: the value to set for the random number
generator
nobs
: number of observations
beta
: a vector specifying the true values for the
regression coefficients (\(\beta_0\),
\(\beta_1\), \(\beta_2\), \(\beta_3\))
sigma
: the variance for the model above
xmeans
: a vector of means used to generate the values
for \(X_1\), \(X_2\), and \(X_3\)
xsigs
: a matrix for the covariance for \(X_1\), \(X_2\), and \(X_3\)
The function below generates the data for a simulation scenario and
returns a list of data (in a list) and the formula to assess the data
for the lm()
function
data_sim <- function(data){ # Simulates the data set
df_list <- lapply(1:data$N, data_set_sim,
nobs = data$nobs, beta = data$beta, sigma = data$sigma,
xmeans = data$xmeans, xsigs = data$xsigs)
return(list(df_list = df_list, formula = data$formula))
}
The function below takes the data generated from the
data_sim()
and fits a linear regression model. The function
wraps around the lm()
function and returns estimated
regression coefficients.
Results
Once your jobs are completed, you can check the results. First you
will need to extract the results from the registry and store it in an R
object.
parallel_results <- lapply(1:length(standard_data), loadResult) #obtains the results of each job adds them as an element in a list
Now use the colMeans()
function to see if the simulation
study worked.
lapply(parallel_results, colMeans)
LS0tCnRpdGxlOiAiUGFyYWxsZWwgQ29tcHV0aW5nIGluIFIgd2l0aCBgYmF0Y2h0b29sc2AiCmF1dGhvcjogIklzYWFjIFF1aW50YW5pbGxhIFNhbGluYXMiCmRlc2NyaXB0aW9uOiBUdXRvcmlhbCBmb3IgcGFyYWxsZWwgcHJvY2Vzc2luZyB1c2luZyB0aGUgUiBwYWNrYWdlIGJhdGNodG9vbHMuCmRhdGU6ICIxMi0yMC0yMDIwIgpvdXRwdXQ6IAogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZXZhbCA9IEZBTFNFKQpgYGAKCiMgSW50cm9kdWN0aW9uCgpSIGlzIGRlc2lnbmVkIHRvIG9ubHkgdXNlIG9uZSBjcHUgKG9yIGNvcmUpIHdoZW4gcnVubmluZyB0YXNrcy4gSG93ZXZlciwgeW91IG1heSBoYXZlIGFjY2VzcyB0byBhIGNvbXB1dGVyIGNsdXN0ZXJbXjFdIHRoYXQgYWxsb3dzIHlvdSB0byBhY2Nlc3MgbW9yZSBSQU0gYW5kIGNwdXMuIFRoZSB1c2Ugb2YgbW9yZSB0aGFuIG9uZSBjcHUgaXMga25vd24gYXMgcGFyYWxsZWwgY29tcHV0aW5nIGluIFIuIFRoZSBnb2FsIG9mIHRoaXMgdHV0b3JpYWwgaXMgdG8gcHJvdmlkZSB0aGUgYmFzaWNzIG9mIHVzaW5nIHRoZSBgYmF0Y2h0b29sc2AgcGFja2FnZSBhbmQgdXRpbGl6aW5nIG1vcmUgY29yZXMgaW4gYSBjbHVzdGVyLgoKW14xXTogVGhlIGNsdXN0ZXIgaGFzIGEgc2NoZWR1bGluZyBzeXN0ZW0gaW1wbGVtZW50ZWQuCgpUaGlzIHR1dG9yaWFsIGlzIGJ1aWx0IG9mZiB0aGUgaW5mb3JtYXRpb24gcHJvdmlkZWQgYnkgVUNSJ3MgSGlnaCBQZXJmb3JtYW5jZSBDb21wdXRpbmcgQ2VudGVyIHR1dG9yaWFsIG9mIGBiYXRjaHRvb2xzYCBwYWNrYWdlLiBUaGlzIHR1dG9yaWFsIHVzZXMgYSBzaW11bGF0aW9uIHN0dWR5IHRvIHNob3cgeW91IHRoZSBwb3dlciBvZiBgYmF0Y2h0b29sc2AuIEZvciBtb3JlIGluZm9ybWF0aW9uIHZpc2l0IHRoZWlyIGhlbHAgZG9jdW1lbnRhdGlvbiBmb3IgW2JhdGNodG9vbHNdKGh0dHBzOi8vaHBjYy51Y3IuZWR1L21hbnVhbHNfbGludXgtY2x1c3Rlcl9wYXJhbGxlbFIuaHRtbCkuCgpUaGlzIHR1dG9yaWFsIGlzIG1lYW50IHRvIGJlIHJ1biBvbiB0aGUgW0hQQ0NdKGh0dHBzOi8vaHBjYy51Y3IuZWR1LykgY2x1c3RlciBhdCBVQ1IuIFlvdSB3aWxsIG5lZWQgYW4gYWNjb3VudCB0byB0aGUgSFBDQyBjbHVzdGVyLiBJZiB5b3UgYXJlIGEgZ3JhZHVhdGUgc3R1ZGVudCBpbiBVQ1IncyBbU3RhdGlzdGljcyBEZXBhcnRtZW50XShodHRwczovL3N0YXRpc3RpY3MudWNyLmVkdS8pLCBjb250YWN0IHRoZSBVQ1IgU3RhdGlzdGljcyBHcmFkdWF0ZSBTdHVkZW50IEFzc29jaWF0aW9uIHRvIGdhaW4gYWNjZXNzLgoKVGhpcyB0dXRvcmlhbCBjb25kdWN0cyBkaWZmZXJlbnQgc2ltdWxhdGlvbiBzY2VuYXJpb3MgdG8gYmUgc3VibWl0dGVkIGFzIGpvYnMuIEl0IHJlcXVpcmVzIGFuIGFtb3VudCBvZiBzZXQgdXAgdG8gYmUgZWZmZWN0aXZlLiBUaGVyZSBpcyBhbiBhbiBSIHNjcmlwdCB0aGF0IHlvdSBjYW4gdXNlIHRoYXQgbWF5IHByb3ZpZGUgYmV0dGVyIGluc2lnaHQgb24gdXNpbmcgYGJhdGNodG9vbHNgLiBZb3UgY2FuIGFjY2VzcyBSIHNjcmlwdCBbaGVyZV0oYmF0Y2h0b29scy5SKQoKIyBGaWxlcywgRnVuY3Rpb25zIGFuZCBQYWNrYWdlcwoKIyMgRmlsZXMKCkJlZm9yZSB5b3UgYmVnaW4sIGRvd25sb2FkIHRoZXNlIGZpbGVzIHRvIHRoZSBkaXJlY3RvcnkgeW91IGFyZSB3b3JraW5nIHdpdGguIFRoZSBgc2x1cm0udG1wbGAgZmlsZSBwcm92aWRlcyBpbmZvcm1hdGlvbiB0byB0aGUgc2x1cm0gc2NoZWR1bGVyLiBUaGUgYC5iYXRjaHRvb2xzLmNvbmYuUmAgZmlsZSB0ZWxscyBSIHRvIGNyZWF0ZSBhIGNsdXN0ZXIgZnVuY3Rpb24gZm9yIHNsdXJtLiBUaGUgcGVyaW9kIGluIGZyb250IG9mIGAuYmF0Y2h0b29scy5jb25mLlJgIHdpbGwgaGlkZSB0aGUgZmlsZSBpbiB5b3VyIGRpcmVjdG9yeS4gSXQgaXMgdGhlcmUsIGl0IGlzIGp1c3Qgbm90IGRpc3BsYXllZC4KCmBgYHtyfQpkb3dubG9hZC5maWxlKCJodHRwczovL2hwY2MudWNyLmVkdS9fc3VwcG9ydF9kb2NzL3R1dG9yaWFscy9zbHVybS50bXBsIiwgInNsdXJtLnRtcGwiKQpkb3dubG9hZC5maWxlKCJodHRwczovL2hwY2MudWNyLmVkdS9fc3VwcG9ydF9kb2NzL3R1dG9yaWFscy8uYmF0Y2h0b29scy5jb25mLlIiLCAiLmJhdGNodG9vbHMuY29uZi5SIikKYGBgCgojIyBQYWNrYWdlcwoKWW91IHdpbGwgbmVlZCB0byB1c2UgMiBSIHBhY2thZ2VzOiBgUmVudk1vZHVsZWAgYW5kIGBiYXRjaHRvb2xzYC4gVGhlIGBSZW52TW9kdWxlYFteMl0gcGFja2FnZSBwcm92aWRlcyB0aGUgdG9vbHMgdG8gaW50ZXJhY3Qgd2l0aCBtb2R1bGVzIGluIHRoZSBjbHVzdGVyLiBUaGUgYGJhdGNodG9vbHNgIHBhY2thZ2UgcHJvdmlkZXMgdGhlIHRvb2xzIHRvIHBhcmFsbGVsaXplIHlvdXIgY29kZS4gQm90aCBhcmUgbmVlZGVkLiBGb3IgdGhpcyB0dXRvcmlhbCwgeW91IHdpbGwgYWxzbyBuZWVkIHRvIGhhdmUgdGhlIGBtdnRybm9ybWAgcGFja2FnZSBpbnN0YWxsZWQuCgpbXjJdOiBGdW4gRmFjdDogVGhpcyBwYWNrYWdlIGlzIGZyb20gSFBDQwoKYGBge3J9CmxpYnJhcnkoUmVudk1vZHVsZSkKbGlicmFyeShiYXRjaHRvb2xzKQpsaWJyYXJ5KG12dG5vcm0pCmBgYAoKIyMgRnVuY3Rpb25zCgojIyMgYG1vZHVsZSgpYAoKVGhlIGBtb2R1bGUoKWAgZnVuY3Rpb24gbG9hZHMgZGlmZmVyZW50IG1vZHVsZXMgaW50byB0aGUgUiBlbnZpcm9ubWVudC4KCiMjIyBgbWFrZVJlZ2lzdHJ5KClgCgpUaGUgYG1ha2VSZWdpc3RyeSgpYCBmdW5jdGlvbiBjcmVhdGVzIGEgcmVnaXN0cnkgZm9yIGBiYXRjaHRvb2xzYC4gWW91IGNhbiB0aGluayBvZiB0aGUgcmVnaXN0cnkgYXMgdGhlIGNvbW11bmljYXRpb24gY2VudGVyIGZvciBSLCB5b3VyIGZ1bmN0aW9ucywgYW5kIHRoZSBjbHVzdGVyLiBJdCB3aWxsIHN0b3JlIGV2ZXJ5dGhpbmcgaW4gYSBkaXJlY3RvcnkuIFRoZSBgbWFrZVJlZ2lzdHJ5KClgIGZ1bmN0aW9uIG5lZWRzIHRoZSBhcmd1bWVudCBgZmlsZS5kaXJgIGFyZ3VtZW50LCB0aGUgbG9jYXRpb24gdG8gc3RvcmUgdGhlIHJlZ2lzdHJ5LCBhbmQgYGNvbmYuZmlsZT0uYmF0Y2h0b29scy5jb25mLlJgLiBTdG9yZSB0aGUgb3V0cHV0IGZyb20gdGhlIGZ1bmN0aW9uIGludG8gYW4gUiBvYmplY3QuCgojIyMgYGJhdGNoTWFwKClgCgpUaGUgYGJhdGNoTWFwKClgIGZ1bmN0aW9uIGNyZWF0ZXMgam9icyB0byBiZSBzdWJtaXR0ZWQgZnJvbSBhIGNsdXN0ZXIuIFRoZSBmaXJzdCBhcmd1bWVudCwgYGZ1bmAsIGlzIHRoZSB1c2VyLWdlbmVyYXRlZCBmdW5jdGlvbiBhbmQgdGhlIGFkZGl0aW9uYWwgYXJndW1lbnRzIGFyZSBmb3IgdGhlIHVzZXItZ2VuZXJhdGVkIGZ1bmN0aW9uLiBUaGUgYGJhdGNoTWFwKClgIGZ1bmN0aW9uIHdpbGwgcHJvZHVjZSBhIGRhdGEgZnJhbWUgb2YgaWRzIGZvciBlYWNoIGpvYiBhcyBhbiBvdXRwdXQuIFRoZSBqb2IgaWRzIGFyZSB0aGUgbnVtZXJpYyBpbmRleCBvZiBlYWNoIGVsZW1lbnQgaW4gYSBsaXN0L3ZlY3Rvci4KCiMjIyBgc3VibWl0Sm9icygpYAoKVGhlIGBzdWJtaXRKb2JzKClgIGZ1bmN0aW9uIHdpbGwgc3VibWl0IGpvYnMgdG8gdGhlIGNsdXN0ZXIuIEl0IHdpbGwgbmVlZCBvdXRwdXQgZnJvbSB0aGUgYGJhdGNoTWFwKClgIGZ1bmN0aW9uIGFzIHRoZSBmaXJzdCBhcmd1bWVudC4gQWRkaXRpb25hbGx5LCBpdCB3aWxsIG5lZWQgdGhlIGxvY2F0aW9uIG9nIHRoZSByZWdpc3RyeSAoYHJlZ2AgYXJndW1lbnQpLCBmcm9tIGEgc3RvcmVkIFIgb2JqZWN0LCBhbmQgYSBsaXN0IG9mIHJlc291cmNlcyAoYHJlc291cmNlc2AgYXJndW1lbnQpLgoKIyMjIGBnZXRTdGF0dXMoKWAKClRoZSBgZ2V0U3RhdHVzKClgIGZ1bmN0aW9uIGNoZWNrcyB0aGUgc3RhdHVzIG9mIHlvdXIgam9icyBhbmQgcHJvdmlkZXMgaW5mb3JtYXRpb24gb2YgZWFjaCBqb2JzIHN0YXRlLgoKIyMjIGBraWxsSm9icygpYAoKVGhlIGBraWxsSm9icygpYCBmdW5jdGlvbiB3aWxsIGtpbGwgYWxsIHlvdXIgam9icy4gVGhpcyBpcyBlcXVpdmFsZW50IHRvIGBzY2FuY2VsYC4KCiMjIyBgbG9hZFJlc3VsdCgpYAoKVGhlIGBsb2FkUmVzdWx0KClgIGZ1bmN0aW9uIHdpbGwgbG9hZCB0aGUgcmVzdWx0cyBvZiBlYWNoIGpvYi4gWW91IHdpbGwgbmVlZCB0byBzcGVjaWZ5IHRoZSBpZCB0byBsb2FkIHRoZSByZXN1bHRzLgoKIyMjIGBjbGVhclJlZ2lzdHJ5KClgCgpUaGUgYGNsZWFyUmVnaXN0cnkoKWAgZnVuY3Rpb24gd2lsbCBkZWxldGUgZmlsZXMgaW4gdGhlIHJlZ2lzdHJ5IHRvIHJlLXN1Ym1pdCBqb2JzLgoKIyMjIGByZW1vdmVSZWdpc3RyeSgpYAoKVGhlIGByZW1vdmVSZWdpc3RyeSgpYCBmdW5jdGlvbiB3aWxsIGRlbGV0ZSB0aGUgZW50aXJlIHJlZ2lzdHJ5LgoKIyBTaW11bGF0aW9uIEV4YW1wbGUKClRvIGRlbW9uc3RyYXRlIGhvdyB0byB1c2UgdGhlIGBiYXRjaHRvb2xzYCBwYWNrYWdlIGluIFIsIHdlIGNvbmR1Y3Qgc2V2ZXJhbCBzaW11bGF0aW9uIHN0dWRpZXMgc2hvd2luZyBob3cgdGhlIGVzdGltYXRlcyBmcm9tIHRoZSBvcmRpbmFyeSBsZWFzdCBzcXVhcmVzIGVzdGltYXRvciBsZWFkcyB0byB1bmJpYXNlZCByZXN1bHRzLiBXZSB3aWxsIHNpbXVsYXRlIGRhdGEgZnJvbSB0aGUgZm9sbG93aW5nIG1vZGVsczoKCiQkClkgXHNpbSBOKDIwKzMwWCxcIDMpCiQkICQkClkgXHNpbSBOKDUtOFhfMSsyWF8yLFwgMykKJCQKCiQkClkgXHNpbSBOKDUrNFhfMS01WF8yLTNYXzMsXCAzKQokJAoKJCQKWSBcc2ltIE4oNSs0WF8xKy01WF8yLTNYXzMrNlhfNCxcIDMpCiQkIEVhY2ggc2ltdWxhdGlvbiBzY2VuYXJpbyB3aXRoIGhhdmUgNTAwIGRhdGEgc2V0cyB3aXRoIDIwMCBvYnNlcnZhdGlvbnMuIFRoZSB2YWx1ZXMgZm9yIHRoZSBwcmVkaWN0b3IgdmFyaWFibGVzIHdpbGwgYmUgc2ltdWxhdGVkIGJ5IG11bHRpdmFyaWF0ZSBub3JtYWwgZGlzdHJpYnV0aW9ucy4gVGhlIG1lYW4gdmVjdG9yIGZvciB0aGUgcHJlZGljdG9ycyBzaW11bGF0aW9uIGFyZSAkKC0yLCAwKV5UJCwgJCgtMiwgMCleVCQsICQoLTIsIDAsIDIpXlQkLCAkKC0yLCAwLCAyIDgpXlQkLiBFYWNoIGNvdmFyaWFuY2UgZm9yIHRoZSBwcmVkaWN0b3Igc2ltdWxhdGlvbiB3aWxsIGJlIGFuIGlkZW50aXR5IG1hdHJpeC4KCiMjIFNpbXVsYXRpb24gUGFyYW1ldGVycwoKVGhlIHNpbXVsYXRpb24gcGFyYW1ldGVycyB3aWxsIGJlIHN0b3JlZCBpbiBhIGxpc3QuIEVhY2ggZWxlbWVudCBpbiB0aGUgbGlzdCB3aWxsIGNvbnRhaW4gaW5mb3JtYXRpb24gb2YgdGhlIGFib3V0IHRoZSBzaW11bGF0aW9uIGFuZCB0aGUgZm9ybXVsYSBmb3IgdGhlIGBsbSgpYCBmdW5jdGlvbi4KCmBgYHtyfQpzaW1fbGlzdCA8LSBsaXN0KGxpc3QoTiA9IDUwMCwgIyBOdW1iZXIgb2YgRGF0YSBzZXRzCiAgICAgICAgICAgICAgICAgICAgICBub2JzID0gMjAwLCAjIE51bWJlciBvZiBvYnNlcnZhdGlvbnMKICAgICAgICAgICAgICAgICAgICAgIGJldGEgPSBjKDIwLCAzMCksICMgYmV0YSBwYXJhbWV0ZXJzCiAgICAgICAgICAgICAgICAgICAgICB4bWVhbnMgPSBjKDApLCAjIE1lYW5zIGZvciBwcmVkaWN0b3JzCiAgICAgICAgICAgICAgICAgICAgICB4c2lncyA9IGRpYWcocmVwKDEsIDEpKSwgIyBWYXJpYW5jZSBmb3IgcHJlZGljdG9yCiAgICAgICAgICAgICAgICAgICAgICBzaWdtYSA9IDMsICMgVmFyaWFuY2UgZm9yIGVycm9yIHRlcm0KICAgICAgICAgICAgICAgICAgICAgIGZvcm11bGEgPSB5IH4geCksICNGb3JtdWxhCiAgICAgICAgICAgICAgICAgbGlzdChOID0gNTAwLCAjIE51bWJlciBvZiBEYXRhIHNldHMKICAgICAgICAgICAgICAgICAgICAgIG5vYnMgPSAyMDAsICMgTnVtYmVyIG9mIG9ic2VydmF0aW9ucwogICAgICAgICAgICAgICAgICAgICAgYmV0YSA9IGMoNSwgLTgsIDIpLCAjIGJldGEgcGFyYW1ldGVycwogICAgICAgICAgICAgICAgICAgICAgeG1lYW5zID0gYygtMiwgMCksICMgTWVhbnMgZm9yIHByZWRpY3RvcnMKICAgICAgICAgICAgICAgICAgICAgIHhzaWdzID0gZGlhZyhyZXAoMSwgMikpLCAjIFZhcmlhbmNlIGZvciBwcmVkaWN0b3IKICAgICAgICAgICAgICAgICAgICAgIHNpZ21hID0gMywgIyBWYXJpYW5jZSBmb3IgZXJyb3IgdGVybQogICAgICAgICAgICAgICAgICAgICAgZm9ybXVsYSA9IHkgfiB4LjEgKyB4LjIpLCAjRm9ybXVsYQogICAgICAgICAgICAgICAgIGxpc3QoTiA9IDUwMCwgIyBOdW1iZXIgb2YgRGF0YSBzZXRzCiAgICAgICAgICAgICAgICAgICAgICBub2JzID0gMjAwLCAjIE51bWJlciBvZiBvYnNlcnZhdGlvbnMKICAgICAgICAgICAgICAgICAgICAgIGJldGEgPSBjKDUsIDQsIC01LCAtMyksICMgYmV0YSBwYXJhbWV0ZXJzCiAgICAgICAgICAgICAgICAgICAgICB4bWVhbnMgPSBjKC0yLCAwLCAyKSwgIyBNZWFucyBmb3IgcHJlZGljdG9ycwogICAgICAgICAgICAgICAgICAgICAgeHNpZ3MgPSBkaWFnKHJlcCgxLCAzKSksICMgVmFyaWFuY2UgZm9yIHByZWRpY3RvcgogICAgICAgICAgICAgICAgICAgICAgc2lnbWEgPSAzLCAjIFZhcmlhbmNlIGZvciBlcnJvciB0ZXJtCiAgICAgICAgICAgICAgICAgICAgICBmb3JtdWxhID0geSB+IHguMSArIHguMiArIHguMyksICAjRm9ybXVsYQogICAgICAgICAgICAgICAgIGxpc3QoTiA9IDUwMCwgIyBOdW1iZXIgb2YgRGF0YSBzZXRzCiAgICAgICAgICAgICAgICAgICAgICBub2JzID0gMjAwLCAjIE51bWJlciBvZiBvYnNlcnZhdGlvbnMKICAgICAgICAgICAgICAgICAgICAgIGJldGEgPSBjKDUsIDQsIC01LCAtMywgNiksICMgYmV0YSBwYXJhbWV0ZXJzCiAgICAgICAgICAgICAgICAgICAgICB4bWVhbnMgPSBjKC0yLCAwLCAyLCA4KSwgIyBNZWFucyBmb3IgcHJlZGljdG9ycwogICAgICAgICAgICAgICAgICAgICAgeHNpZ3MgPSBkaWFnKHJlcCgxLCA0KSksICMgVmFyaWFuY2UgZm9yIHByZWRpY3RvcgogICAgICAgICAgICAgICAgICAgICAgc2lnbWEgPSAzLCAjIFZhcmlhbmNlIGZvciBlcnJvciB0ZXJtCiAgICAgICAgICAgICAgICAgICAgICBmb3JtdWxhID0geSB+IHguMSArIHguMiArIHguMyArIHguNCkgI0Zvcm11bGEKKQpgYGAKCiMjIFNpbXVsYXRpb24gRnVuY3Rpb25zCgpUaGUgZnVuY3Rpb24gYmVsb3cgZ2VuZXJhdGVzIDEgZGF0YSBzZXQgZnJvbSBhIHNpbXVsYXRpb24gc2NlbmFyaW8gYWJvdmUgYW5kIHJldHVybnMgYSBkYXRhIGZyYW1lLgoKYGBge3J9CmRhdGFfc2V0X3NpbSA8LSBmdW5jdGlvbihzZWVkLCBub2JzLCBiZXRhLCBzaWdtYSwgeG1lYW5zLCB4c2lncyl7ICMgU2ltdWxhdGVzIHRoZSBkYXRhIHNldAogIHNldC5zZWVkKHNlZWQpICMgU2V0cyBhIHNlZWQKICB4cm4gPC0gcm12bm9ybShub2JzLCBtZWFuID0geG1lYW5zLCBzaWdtYSA9IHhzaWdzKSAjIFNpbXVsYXRlcyBQcmVkaWN0b3JzCiAgeHBlZCA8LSBjYmluZChyZXAoMSxub2JzKSx4cm4pICMgQ3JlYXRpbmcgRGVzaWduIE1hdHJpeAogIHkgPC0geHBlZCAlKiUgYmV0YSArIHJub3JtKG5vYnMgLDAsIHNpZ21hKSAjIFNpbXVsYXRpbmcgWQogIGRmIDwtIGRhdGEuZnJhbWUoeD14cm4sIHk9eSkgIyBDcmVhdGluZyBEYXRhIEZyYW1lCiAgcmV0dXJuKGRmKQp9CmBgYAoKVGhlIGZ1bmN0aW9uIG5lZWRzIHRoZSBmb2xsb3dpbmcgYXJndW1lbnRzOgoKLSAgIGBzZWVkYDogdGhlIHZhbHVlIHRvIHNldCBmb3IgdGhlIHJhbmRvbSBudW1iZXIgZ2VuZXJhdG9yCi0gICBgbm9ic2A6IG51bWJlciBvZiBvYnNlcnZhdGlvbnMKLSAgIGBiZXRhYDogYSB2ZWN0b3Igc3BlY2lmeWluZyB0aGUgdHJ1ZSB2YWx1ZXMgZm9yIHRoZSByZWdyZXNzaW9uIGNvZWZmaWNpZW50cyAoJFxiZXRhXzAkLCAkXGJldGFfMSQsICRcYmV0YV8yJCwgJFxiZXRhXzMkKQotICAgYHNpZ21hYDogdGhlIHZhcmlhbmNlIGZvciB0aGUgbW9kZWwgYWJvdmUKLSAgIGB4bWVhbnNgOiBhIHZlY3RvciBvZiBtZWFucyB1c2VkIHRvIGdlbmVyYXRlIHRoZSB2YWx1ZXMgZm9yICRYXzEkLCAkWF8yJCwgYW5kICRYXzMkCi0gICBgeHNpZ3NgOiBhIG1hdHJpeCBmb3IgdGhlIGNvdmFyaWFuY2UgZm9yICRYXzEkLCAkWF8yJCwgYW5kICRYXzMkCgpUaGUgZnVuY3Rpb24gYmVsb3cgZ2VuZXJhdGVzIHRoZSBkYXRhIGZvciBhIHNpbXVsYXRpb24gc2NlbmFyaW8gYW5kIHJldHVybnMgYSBsaXN0IG9mIGRhdGEgKGluIGEgbGlzdCkgYW5kIHRoZSBmb3JtdWxhIHRvIGFzc2VzcyB0aGUgZGF0YSBmb3IgdGhlIGBsbSgpYCBmdW5jdGlvbgoKYGBge3J9CmRhdGFfc2ltIDwtIGZ1bmN0aW9uKGRhdGEpeyAjIFNpbXVsYXRlcyB0aGUgZGF0YSBzZXQKICBkZl9saXN0IDwtIGxhcHBseSgxOmRhdGEkTiwgZGF0YV9zZXRfc2ltLAogICAgICAgICAgICAgICAgICAgIG5vYnMgPSBkYXRhJG5vYnMsIGJldGEgPSBkYXRhJGJldGEsIHNpZ21hID0gZGF0YSRzaWdtYSwKICAgICAgICAgICAgICAgICAgICB4bWVhbnMgPSBkYXRhJHhtZWFucywgeHNpZ3MgPSBkYXRhJHhzaWdzKQogIHJldHVybihsaXN0KGRmX2xpc3QgPSBkZl9saXN0LCBmb3JtdWxhID0gZGF0YSRmb3JtdWxhKSkKfQoKYGBgCgpUaGUgZnVuY3Rpb24gYmVsb3cgdGFrZXMgdGhlIGRhdGEgZ2VuZXJhdGVkIGZyb20gdGhlIGBkYXRhX3NpbSgpYCBhbmQgZml0cyBhIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsLiBUaGUgZnVuY3Rpb24gd3JhcHMgYXJvdW5kIHRoZSBgbG0oKWAgZnVuY3Rpb24gYW5kIHJldHVybnMgZXN0aW1hdGVkIHJlZ3Jlc3Npb24gY29lZmZpY2llbnRzLgoKIyBQYXJhbGxlbGl6YXRpb24KCiMjIEZ1bmN0aW9uCgpUaGUgZnVuY3Rpb24gYmVsb3cgcHJvY2VzcyB0aGUgZGF0YSBhbmQgaW1wbGVtZW50cyB0aGUgYGxtKClgIHRvIHRoZSBkYXRhLiBUaGlzIGlzIHRoZSBmdW5jdGlvbiB0aGF0IHdpbGwgYmUgdXNlZCBpbiBgYmF0Y2h0b29sc2AuIEZpcnN0LCB0aGUgZnVuY3Rpb24gb2J0YWlucyB0aGUgbnVtYmVyIG9mIGRhdGEgc2V0cyBpdCB3aWxsIHByb2Nlc3MuIFNlY29uZCwgaXQgY3JlYXRlcyB0aGUgYGxtX2NvZWYoKWAgZnVuY3Rpb24gd2hpY2ggYXBwbGllcyB0aGUgYGxtKClgIGZ1bmN0aW9uIHRvIHRoZSBkYXRhW14zXS4gVGhpcmQsIGEgcGFyYWxsZWwgcHJvY2Vzc29yIGlzIGFwcGxpZWQgdG8gZ28gdGhyb3VnaCB0aGUgZGF0YSBbXjRdLiBMYXN0bHksIHRoZSByZXN1bHRzIGFyZSBjb252ZXJ0ZWQgdG8gYSBtYXRyaXggYW5kIHJldHVybmVkIGFzIHRoZSBvdXRwdXQuCgpbXjNdOiBVc2VyIGNyZWF0ZWQgZnVuY3Rpb25zIG5lZWRzIHRvIGJlIGNyZWF0ZWQgd2l0aGluIHRoZSBmdW5jdGlvbiB0byBiZSBsb2FkZWQgaW4gYGJhdGNodG9vbHNgCgpbXjRdOiBGb3IgbW9yZSBpbmZvcm1hdGlvbiBhYm91dCBwYXJhbGxlbCBwcm9jZXNzaW5nIHdpdGggdGhlIGBwYXJhbGxlbGAgcGFja2FnZSwgdmlzaXQgW2hlcmVdKHBhcmFsbGVsX25vdGVib29rLm5iLmh0bWwpCgpgYGB7cn0KcGFyYWxsZWxfbG08LWZ1bmN0aW9uKGRhdGEpewogIGxsPC1sZW5ndGgoZGF0YSRkZl9saXN0KQogIAogIGxtX2NvZWYgPC0gZnVuY3Rpb24oZm9ybXVsYSwgZGF0YSl7ICMgQXBwbHlpbmcgYSBPcmRpbmFyeSBMZWFzdCBTcXVhcmVzIAogICAgbG1fcmVzIDwtIGxtKGZvcm11bGEsIGRhdGEgPSBkYXRhKSAjIEZpbmQgT0xTIEVzdGltYXRlcwogICAgcmV0dXJuKGFzLnZlY3Rvcihjb2VmKGxtX3JlcykpKSMgT2J0YWluaW5nIAogIH0KICAKICByZXN1bHRzIDwtIHBhcmFsbGVsOjptY2xhcHBseShkYXRhJGRmX2xpc3QsIGxtX2NvZWYsIGZvcm11bGEgPSBkYXRhJGZvcm11bGEsICMgQXBwbGllcyBsbV9jb2VmIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1jLmNvcmVzID0gOCkgIyBVc2luZyB0aGUgcGFyYWxsZWwgcGFja2FnZSB0byBwYXJhbGxlbGl6ZSAKICAKICBtYXQ8LW1hdHJpeCh1bmxpc3QocmVzdWx0cyksIG5yb3cgPSBsbCwgYnlyb3cgPSBUKQogIHJldHVybihtYXQpCn0KYGBgCgojIyBFeGVjdXRpb24KCiMjIyBNb2R1bGVzCgpMb2FkIHRoZSBzbHVybSBtb2R1bGUgaW50byBSLgoKYGBge3J9Cm1vZHVsZSgnbG9hZCcsJ3NsdXJtJykKYGBgCgojIyMgUmVnaXN0cnkKCkNyZWF0ZSBhIHJlZ2lzdHJ5IGFuZCBzdG9yZSBpdCBpbiBhbiBSIG9iamVjdC4KCmBgYHtyfQpyZWcgPC0gbWFrZVJlZ2lzdHJ5KGZpbGUuZGlyPSJteXJlZ2RpciIsIGNvbmYuZmlsZT0iLmJhdGNodG9vbHMuY29uZi5SIikKYGBgCgojIyMgUmVzb3VyY2VzCgpDcmVhdGUgYSBsaXN0IHdpdGggdGhlIGZvbGxvd2luZyBlbGVtZW50cyBhbmQgc3RvcmUgaXQgaW4gYW4gUiBvYmplY3Q6CgotICAgYHBhcnRpdGlvbmA6IFRoZSBjbHVzdGVyIHBhcnRpdGlvbiB0byB1c2UgKGAic3Rhc3RkZXB0ImApCgotICAgYHdhbGx0aW1lYDogVGhlIHRpbWUgdG8gcnVuIHRoZSBqb2IgaW4gc2Vjb25kcyAoYDEyMGApCgotICAgYG50YXNrc2A6IFRoZSBudW1iZXIgb2YgdGFza3MgdG8gY29tcGxldGUgKGAxYCkKCi0gICBgbmNwdXNgOiBUaGUgbnVtYmVyIG9mIGNwdXMgdG8gY29tcGxldGUgdGhlIHRhc2sgKGA4YCkKCi0gICBgbWVtb3J5YDogSG93IG11Y2ggbWVtb3J5IGlzIGJlaW5nIHVzZWQgaW4gTUIgKGAxMDI0YCkKCmBgYHtyfQpyZXMgPC0gbGlzdChwYXJ0aXRpb249InN0YXRzZGVwdCIsIHdhbGx0aW1lPTEyMCwgbnRhc2tzPTEsIG5jcHVzPTgsIG1lbW9yeT0xMDI0KQpgYGAKCiMjIyBCYXRjaCBKb2JzIFByZXAKClVzZSB0byBgYmF0Y2hNYXAoKWAgZnVuY3Rpb24gdG8gY3JlYXRlIHRoZSBqb2JzIHRoYXQgbmVlZCB0byBiZSBzdWJtaXR0ZWQgaW50byB0aGUgY2x1c3RlciBhbmQgc3RvcmUgaXQgYW4gUiBvYmplY3QuCgpgYGB7cn0Kc3VibWlzc2lvbl9pZCA8LSBiYXRjaE1hcChwYXJhbGxlbF9sbSwgZGF0YSA9IHN0YW5kYXJkX2RhdGEpCmBgYAoKIyMjIFN1Ym1pdHRpbmcgSm9icwoKU3VibWl0IHRoZSBqb2JzIHVzaW5nIHRoZSBgc3VibWl0Sm9icygpYCBhbmQgc3RvcmUgaXQgaW4gYW4gUiBvYmplY3QuCgpgYGB7cn0KZG9uZSA8LSBzdWJtaXRKb2JzKHN1Ym1pc3Npb25faWQsIHJlZz1yZWcsIHJlc291cmNlcz1yZXMpCmBgYAoKIyMjIEdldHRpbmcgSm9iIFN0YXR1cwoKVXNlIHRoZSBgZ2V0U3RhdHVzKClgIGZ1bmN0aW9uIHRvIGNoZWNrIG9uIHRoZSBzdGF0dXMgb2YgeW91ciBqb2JzLgoKYGBge3J9CmdldFN0YXR1cygpCmBgYAoKIyBSZXN1bHRzCgpPbmNlIHlvdXIgam9icyBhcmUgY29tcGxldGVkLCB5b3UgY2FuIGNoZWNrIHRoZSByZXN1bHRzLiBGaXJzdCB5b3Ugd2lsbCBuZWVkIHRvIGV4dHJhY3QgdGhlIHJlc3VsdHMgZnJvbSB0aGUgcmVnaXN0cnkgYW5kIHN0b3JlIGl0IGluIGFuIFIgb2JqZWN0LgoKYGBge3J9CnBhcmFsbGVsX3Jlc3VsdHMgPC0gbGFwcGx5KDE6bGVuZ3RoKHN0YW5kYXJkX2RhdGEpLCBsb2FkUmVzdWx0KSAjb2J0YWlucyB0aGUgcmVzdWx0cyBvZiBlYWNoIGpvYiBhZGRzIHRoZW0gYXMgYW4gZWxlbWVudCBpbiBhIGxpc3QKYGBgCgpOb3cgdXNlIHRoZSBgY29sTWVhbnMoKWAgZnVuY3Rpb24gdG8gc2VlIGlmIHRoZSBzaW11bGF0aW9uIHN0dWR5IHdvcmtlZC4KCmBgYHtyfQpsYXBwbHkocGFyYWxsZWxfcmVzdWx0cywgY29sTWVhbnMpCmBgYAoKIyBOb3RlcwoKIyMgRXJyb3I6IFJlYWNoZWQgU3VibWlzc2lvbiBMaW1pdCBvciBSZXNvdXJjZXMgTGltaXQKCklmIFlvdSByZWNlaXZlIGFuIGVycm9yIGFib3V0IHJlYWNoaW5nIGxpbWl0cywgZG8gbm90IHdvcnJ5IGFib3V0IGl0LiBUaGUgc2x1cm0gc3lzdGVtIHdpbGwgdGFrZSBjYXJlIG9mIHRoaXMuIFR5cGUgdGhlIGZvbGxvd2luZyBiZWxvdyB0byBjYW5jZWwgYWxsIHlvdXIgam9icyBhbmQgdHJ5IGFnYWluLgoKYGBge2Jhc2h9CnNxdWV1ZSAtLXVzZXIgJFVTRVIgLS1ub2hlYWRlciAtLWZvcm1hdCAnJWknIHwgeGFyZ3Mgc2NhbmNlbApgYGAK