For this hands-on exercise, we will use an adaptation of the pig_adg dataset described in Dohoo, Martin and Wayne - Veterinary Epidemiologic Research (second edition) (see (Dohoo, Martin, Stryhn, & others, 2003)).

 

Let’s install all required and suggest package from abn (if not already done)

## dplyr is used for the hands-on exercise

install.package(c("nnet", "lme4", "Rgraphviz", "knitr", "R.rsp", "testthat", "entropy", "moments", "boot", "brglm", "dplyr"))

Let’s load the data into the working environment:

require('abn'); data("adg", package = "abn")

let us work with a data.frame as required by abn. adg <- adg %>% as.data.frame()

 

The data for this exercise consist of 341 observations of 9 variables. Here is an extract of the first rows:

knitr::kable(head(adg)) %>%
  kable_styling(bootstrap_options = c("striped", "hover"))
AR pneumS female livdam eggs wormCount age adg farm
1 0 0 1 0 0 196 453 4
1 0 0 1 0 0 175 501 9
0 0 1 1 0 0 176 545 3
1 0 1 1 0 0 207 441 8
1 1 0 0 0 0 222 482 13
1 0 0 0 0 0 212 423 8

 

The meaning of each variable is the following:

Variable Meaning Distribution
AR presence of atrophic rhinitis Binomial
pneumS presence of moderate to severe pneumonia Binomial
female sex of the pig (1=female, 0=castrated) Binomial
livdam presence of liver damage (parasite-induced white spots) Binomial
eggs presence of fecal/gastrointestinal nematode eggs at time of slaughter Binomial
wormCount count of nematodes in small intestine at time of slaughter Poisson
age days elapsed from birth to slaughter (days) Continuous
adg average daily weight gain (grams) Continuous
farm farm ID Discrete

The distribution of each variable is the following:

 

For this exercise we will drop farm (clustering indicator):

drop <- which(colnames(adg)%in% c("farm"))
abndata <- adg[, -drop]

 

Set all binary variables to factor data type (and check that dataset is complete)

A requirement of abn (Kratzer, Pittavino, Lewis, & Furrer, 2017; Lewis & Ward, 2013) is that all binary variables are coerced into factors.

str(abndata)
'data.frame':   341 obs. of  8 variables:
 $ AR       : num  1 1 0 1 1 1 1 1 1 1 ...
 $ pneumS   : num  0 0 0 0 1 0 0 0 0 0 ...
 $ female   : num  0 0 1 1 0 0 1 1 1 0 ...
 $ livdam   : num  1 1 1 1 0 0 0 1 1 0 ...
 $ eggs     : num  0 0 0 0 0 0 0 0 0 0 ...
 $ wormCount: num  0 0 0 0 0 0 0 0 0 0 ...
 $ age      : num  196 175 176 207 222 212 247 181 191 181 ...
 $ adg      : num  453 501 545 441 482 423 407 580 427 586 ...
abndata[,1:5] <- as.data.frame(lapply(abndata[,1:5], factor))
sum(complete.cases(abndata))
[1] 341

 

Setup the distribution list for each variable

Each variable in the model needs to be associated to a distribution (currently available: binomial, gaussian, poisson) according to the type of data. In this example most of the variables are binary and therefore associated to the binomial distribution. Variables age and adg are continuous, so they will be associated to the Gaussian distribution. Finally, variable wormCount is a count and can be modelled by a Poisson distribution.

The supported distributions are:

dist <- list(AR = "binomial", pneumS = "binomial", female="binomial", 
             livdam= "binomial", eggs = "binomial",wormCount = "poisson",
             age= "gaussian", adg = "gaussian")

 

Create retain and banned matrixes (optional)

Prior knowledge about data structure, that could guide the search for the optimal model, can be included by forcing or banning some specific arcs from being considered in the final DAG. This is done by providing a retain matrix and/or a ban matrix. We will start by creating two empty matrices with the same size as our data (8) and named rows and columns.

retain <- matrix(0, ncol(abndata), ncol(abndata))
colnames(retain) <- rownames(retain) <- names(abndata)

banned <- matrix(0, ncol(abndata), ncol(abndata))
colnames(banned) <- rownames(banned) <- names(abndata)

 

Ban some arcs (optional)

The information encoded in the ban matrix is subjectively chosen to reflect our belief about data structure. In this example, it is reasonable to assume that none of the variables in the model is going to affect the gender of the animal (which is an inborn trait). To encode this information we will ban all the arcs going to female, by setting their value to 1 (banned) as opposite to the default value 0 (non banned).

How does it work?

Rows are children, columns parents:
. b1 b2 b3 b4
b1 0 1 0 0
b2 0 0 0 0
b3 1 0 0 0
b4 0 0 0 0

So ban[1, 2] <- 1 means do not allow the arc from b2 (second column) to b1 (first row) and ban[3, 1] <- 1 means do not allow the arc from b1 (first column) to b3 (third row).

Now, we want to ban the arcs going from any variable (= all columns except the third) to female (= third row):

banned[3,-3] <- 1

Have a look if you want:

banned
          AR pneumS female livdam eggs wormCount age adg
AR         0      0      0      0    0         0   0   0
pneumS     0      0      0      0    0         0   0   0
female     1      1      0      1    1         1   1   1
livdam     0      0      0      0    0         0   0   0
eggs       0      0      0      0    0         0   0   0
wormCount  0      0      0      0    0         0   0   0
age        0      0      0      0    0         0   0   0
adg        0      0      0      0    0         0   0   0

 

Run the exact search across incremental parent limits

Repeat step 5 for incremental parent limit (e.g. 1 to nr.var-1). The optimal DAG is the one where the network score does not improve (i.e. becomes bigger) any longer by allowing more parents.

datadir <- tempdir() 

for (i in 1:7) {
  max.par <- i
  
  mycache <- buildScoreCache(data.df = as.data.frame(abndata), data.dists = dist, 
                             dag.banned = banned, dag.retained = retain, 
                             max.parents = max.par)
  
  mydag <- mostProbable(score.cache = mycache)
  
  fabn <- fitAbn(object = mydag)
  
  cat(paste("network score for", i, "parents =", fabn$mlik, "\n\n"))
      
  save(mycache, mydag, fabn, file = paste(datadir,"mp_", max.par,".RData", sep=""))
 
}
Step1. completed max alpha_i(S) for all i and S
Total sets g(S) to be evaluated over: 256
network score for 1 parents = -2806.49909464438 

Step1. completed max alpha_i(S) for all i and S
Total sets g(S) to be evaluated over: 256
network score for 2 parents = -2714.69171704676 

Step1. completed max alpha_i(S) for all i and S
Total sets g(S) to be evaluated over: 256
network score for 3 parents = -2709.37078842511 

Step1. completed max alpha_i(S) for all i and S
Total sets g(S) to be evaluated over: 256
network score for 4 parents = -2709.25275104164 

Step1. completed max alpha_i(S) for all i and S
Total sets g(S) to be evaluated over: 256
network score for 5 parents = -2709.25275104164 

Step1. completed max alpha_i(S) for all i and S
Total sets g(S) to be evaluated over: 256
network score for 6 parents = -2709.25275104164 

Step1. completed max alpha_i(S) for all i and S
Total sets g(S) to be evaluated over: 256
network score for 7 parents = -2709.25275104164 
# get network score for all parent limits
# ---------------------------------------
mp.mlik <- c()
for (i in 1:max.par) {
  load(paste(datadir,"mp_", i,".RData", sep=""))
  mp.mlik <- c(mp.mlik, fabn$mlik)
}

 

Plot log marginal likelihood in function of the parent limit

# check how it looks
# ------------------
plot(1:max.par, mp.mlik, xlab = "Parent limit", ylab = "Log marginal likelihood", 
     type = "b", col="red", ylim=range(mp.mlik))
abline(v=which(mp.mlik==max(mp.mlik))[1], col="grey", lty=2)

After max.par=4 the maximum log marginal likelihood is constant:

[1] -2806.499 -2714.692 -2709.371 -2709.253 -2709.253 -2709.253 -2709.253

 

Here is what the best fitting DAG looks like using plotAbn() function:

 

Before going ahead interpreting the results, we need one more step. So far, we have identified a DAG which has the maximum possible goodness of fit according to the log marginal likelihood. This is the standard goodness of fit metric in Bayesian modelling and includes an implicit penalty for model complexity. However, the log marginal likelihood is also known to be prone to overfitting (especially with smaller data sets), meaning that it may identify more parameters than can be actually justified by the data. Therefore, it is advisable to check and address potential overfitting before drawing any conclusion based on the model results.

A well established approach for addressing overfitting is to use parametric bootstrapping. Basically, the model chosen from the exact search will be used to generate many bootstrap datasets (e.g. 1000) of equal size to the original dataset (n=341 in our case). Each bootstrap dataset will be then treated as if it were the original data, and a globally optimal DAG will be identified exactly as described before (i.e. exact search). We will therefore get as many DAGs as the number of simulated datasets (eg. 1000). To address overfitting, any arcs in the DAG from the original data which will not be recovered in > 50% of the bootstrap DAGs will be deemed to have insufficient statistical support to be considered robust.

 

Parametric bootstrapping

This step will be done with the aid of a software for Bayesian statistical analysis using Markov Chain Monte Carlo (MCMC) simulations (we will use JAGS (Plummer & others, 2003), but any other is fine). We will use the parameters estimated from our model to build a BUG model to simulate data. In other words, we will do the reverse process: instead of using data to estimate parameters, we will use parameters to estimate data.

 

Extract parameters from best fitting model and save them for MCMC simulations

The parameters at each node are estimated as posterior probability density distributions. These marginal densities are the ones where JAGS needs to sample from in order to simulate data. In order to use these distributions with JAGS, the densities need to be approximated by a discrete distribution over a fine and equally spaced grid. Here is a mock example for a hypothetical parameter \(\beta\):

 

# Fit marginal densities over a fixed grid --> n.grid=1000
# --------------------------------------------------------
  marg.f <- fitAbn(object = mydag, compute.fixed=TRUE, n.grid=1000)

# Extract values 
# --------------
  m <- marg.f$marginals[[1]] 
  for(i in 2: length(marg.f$marginals))
  { m <- c(m, marg.f$marginals[[i]])}
  
# Bind all the marginals for the same node into a matrix
# ------------------------------------------------------
  AR.p <- cbind( m[[ "AR|(Intercept)"]], m[[ "AR|age"]])
  pneumS.p <- cbind( m[[ "pneumS|(Intercept)"]], m[[ "pneumS|age"]])
  female.p <- cbind( m[[ "female|(Intercept)"]])
  livdam.p <- cbind( m[[ "livdam|(Intercept)"]], m[[ "livdam|eggs"]])
  eggs.p <- cbind( m[[ "eggs|(Intercept)"]], m[[ "eggs|adg"]])
  wormCount.p <- cbind( m[[ "wormCount|(Intercept)"]], m[[ "wormCount|AR"]],
                        m[[ "wormCount|eggs"]], m[[ "wormCount|age"]], m[[ "wormCount|adg"]])
  age.p <- cbind( m[[ "age|(Intercept)"]], m[[ "age|female"]])
  prec.age.p <- cbind( m[[ "age|precision" ]])
  adg.p <- cbind( m[[ "adg|(Intercept)"]], m[[ "adg|age"]])
  prec.adg.p <- cbind( m[[ "adg|precision" ]])

# Save it to a file named PostParams to be read by JAGS
# -----------------------------------------------------
  dump(c("AR.p", "pneumS.p", "female.p", "livdam.p", "eggs.p", 
       "wormCount.p", "age.p", "prec.age.p", "adg.p", "prec.adg.p"),
     file="PostParams.R")

 

Write the BUG model

In order to simulate the variables of our dataset we need to provide a model for each of them, using the aforementioned parameters estimates. For instance, the binomial node AR in our DAG has one incoming arc coming from the node age. In a regression notation this would be translated into:

logit(AR) = \(\alpha\) + \(\beta\) x age + \(\epsilon\)

where \(\alpha\) is the intercept and \(\beta\) the regression coefficient for variable age and \(\epsilon\) is the error term modelled by a binomial distribution.

Given that we will simulate the data in a Bayesian framework, AR will be modelled as a probability distribution. Therefore it will look like:

AR ~ dbern(probAR);
logit(probAR)<- alpha + beta*age;

Then, the values of alpha and beta will be sampled (dcat) from our discrete distribution of parameters:

alpha.prob ~ dcat(AR.p[ ,2]); –> sample from the vector of density values f(x) (second column in matrix)
alpha ~ AR.p[alpha.prob,1]; –> corresponding x value for the sampled density (first column in matrix)
beta.prob ~ dcat(AR.p[ ,4]);
beta ~ AR.p[beta.prob,1];

 

The BUG file (model8vPois.bug) can be retrieved from the file directory.

 

Run JAGS and inspect the result of a simulated dataset

library(rjags)

# set inits
# ---------
init <- list(".RNG.name"="base::Mersenne-Twister", ".RNG.seed"=42)

# load data
# ---------
source("PostParams.R")

# run model once
# --------------
jj <- jags.model(file = "model8vPois.bug", 
                 data = list(  'AR.p'=AR.p , 'pneumS.p'=pneumS.p , 'female.p'=female.p, 
                               'livdam.p'=livdam.p , 'eggs.p'=eggs.p , 'wormCount.p'=wormCount.p , 
                               'age.p'=age.p ,'prec.age.p'=prec.age.p, 
                               'adg.p'=adg.p , 'prec.adg.p'=prec.adg.p),
                 inits = init,
                 n.chains = 1, 
                 n.adapt = 500)
Compiling model graph
   Resolving undeclared variables
   Allocating nodes
Graph information:
   Observed stochastic nodes: 0
   Unobserved stochastic nodes: 28
   Total graph size: 4091

Initializing model
# run more iterations
# -------------------
update(jj, 100000)

# set number of observation we want to extract for a dataset 
# ----------------------------------------------------------
n.obs=341

# sample data (same size as original: 341) with a sampling lag (20) to reduce avoid autocorrelation
# -------------------------------------------------------------------------------------------------
samp <- coda.samples(jj, c("AR", "pneumS", "female", "livdam", "eggs", 
                           "wormCount", "age", "prec.age", "adg", "prec.adg"),
                     n.iter= n.obs*20 , thin =20)

Now compare the simulated data with the original data. Observe that Gaussian nodes are by default centred when doing abn search, meaning that the simulated data for those nodes will be centred as well.

Simulated data looks fairly OK (perhaps wormCount is sub-optimal as the simulated data miss to represent the long right tail) so we can proceed with the bootstrapping.

 

Iterate dataset simulation + exact search over and over

What we will do is to create a loop to 1) simulate data, 2) do exact search on such data, and 3) store the best fitting DAG over and over for many times (e.g. 1000 iterations). Bootstrap data need to be saved in a folder to be further inspected.

# load data 
data("adg", package = "abn")

df <- adg %>%
  dplyr::select(-farm) %>%
  as.data.frame()

vars <- colnames(df)

# load data for jags
source("PostParams.R")

# select nr. bootstrap samples to run
# -----------------------------------
set.seed(46846)

n <- sample(1:100000, 1000)


# specify max number of parents based on previous search
# ------------------------------------------------------
max.par <- 4


# Simulate data and run ABN on such dataset
# -----------------------------------------
boot.save <- list()

for (i in 1:length(n)) {

  print(paste("Running simulation", i))

  # pick initials
  init <- list(".RNG.name"="base::Mersenne-Twister", ".RNG.seed"=n[i])

  # run model
  jj <- jags.model(file = "model8vPois.bug",
                   data = list(  'AR.p'=AR.p , 'pneumS.p'=pneumS.p , 'female.p'=female.p,
                                 'livdam.p'=livdam.p , 'eggs.p'=eggs.p , 'wormCount.p'=wormCount.p ,
                                 'age.p'=age.p ,'prec.age.p'=prec.age.p,
                                 'adg.p'=adg.p , 'prec.adg.p'=prec.adg.p),
                   inits = init,
                   n.chains = 1,
                   n.adapt = 500)

  # run more iterations
  update(jj, 100000)

  # sample data (same size as original: 341) with a sampling lag (20) to reduce avoid autocorrelation
  samp <- coda.samples(jj, c("AR", "pneumS", "female", "livdam", "eggs",
                             "wormCount", "age", "prec.age", "adg", "prec.adg"),
                       n.iter= 6820 , thin =20)

  # build dataframe in the same shape as the original one
  dt.boot <- as.data.frame(as.matrix(samp)) # pay attention at order names

  dt.boot<- dt.boot[, vars]


  # now coerce to factors if need be and set levels - NOTES setting levels works as
  # "0" "1" is in the same order as "absent" "present" from original data
  #abndata <- as.data.frame(abndata)

  dt.boot[,1:5] <- as.data.frame(lapply(dt.boot[,1:5], factor)) 
  
  
# Build a cache of all local computations
# ---------------------------------------
 mycache <- buildScoreCache(data.df = dt.boot, data.dists = dist, dag.banned = banned,
                             dag.retained = retain, max.parents = max.par)

# Run the EXACT SEARCH
# --------------------
 mp.dag <- mostProbable(score.cache = mycache)
 fabn <- fitAbn(object = mp.dag)

# Save the results obtained
# -------------------------
 
 #boot.save[[i]] <- list(mp.dag, fabn, dt.boot) 
 
 boot.save[[i]] <- mp.dag$dag

}

save(boot.save, file = sprintf('BootData/dt.boot.RData'))

 

Find the final pruned DAG

First we need to load all the bootstrap DAGs.

# set nr bootstrap samples
# ------------------------
n <- 1000

# load dags and boostrap data
# ------------------------------
dags <- list()
boot <- list()

load(file = 'BootData/dt.boot.RData')

for(i in 1:n){
dags[[i]] <- boot.save[[i]]

}

 

Then we will check what was the most frequent number of arcs:

# count total number of arcs in each dag
# --------------------------------------
arcs <- sapply(dags, sum)
barplot(table(arcs))

For comparison, in the original DAG there were 10 arcs, so it seems that there might have been some overfitting.

 

Finally, we will count how many times each arc appears in the bootstrapped data. The final pruned DAG will include only arcs present in at least 50% of bootstrap samples (e.g. 500/1000 in our case).

# Count how many times each arc appear in the bootstrap data
# ----------------------------------------------------------
# function Reduce sums ("+") each element of each matrix in the list and store 
# results in a new matrix of same size

alldag <- Reduce("+", dags)  

# express it in percentage
perdag <- alldag/length(dags)


# Keep only arcs that appears in at least 50% of samples
# ------------------------------------------------------
trim.dag <- (alldag >=(n*0.5))*1

# Send  final pruned DAG to Graphvis for visualization 
# -----------------------------------------------------
toGraphviz(dag = trim.dag, data.df = abndata, data.dists = dist,
           outfile = paste0("TrimDAG",n,".dot"))

This is the percentage of arcs retrieved within the bootstrap sample:

          AR pneumS female livdam eggs wormCount age adg
AR         0      0      0      0    0         0  76   5
pneumS     1      0      1      0    0         0  39   8
female     0      0      0      0    0         0   0   0
livdam     0      0      0      0   50         1   0   0
eggs       0      0      1     27    0         0   9  72
wormCount 69      0      0      0  100         0 100  61
age       19     17     57      0    4         0   0  28
adg        1      2     12      0    7         0  72   0

This is how the final pruned DAG looks like:

 

Now that we have a robust model (encoded by the pruned DAG), we can extract the parameters to (a) appreciate the magnitude (and precision) of the the associations and (b) further refine the DAG including whether the associations are positive or negative.

 

Extract marginal posterior density for each parameter

The marginal posterior densities (marginals) represent the density distribution of the parameters at each arc.

marg.f <- fitAbn(dag = trim.dag, data.df = as.data.frame(abndata),
                 data.dists = dist, compute.fixed=TRUE, n.grid=1000)

 

Visually inspect the marginal posterior distributions of the parameters

par(mfrow=c(1,4), mar=c(2,2,1.5,1))
for(i in 1:length(marg.f$marginals)){

# get the marginal for current node, which is a matrix [x, f(x)]
  cur.node <- marg.f$marginals[i]
  nom1 <- names(marg.f$marginals)[i]

# pick the first value (for models wothout random effects)
  cur.node <- cur.node[[1]]
  for(j in 1:length(cur.node) ) {
    nom2<-names(cur.node)[j]
    cur.param <- cur.node[[j]]
    plot(cur.param,type="l",main=paste(nom1, ":", nom2), cex=0.7)
  }
}

 

Get the table of quantiles for the marginals

# extract marginals adjusted for grouped data
marg.dists <- marg.f$marginals[[1]]
for (i in 2:length(marg.f$marginals)) {
  marg.dists <- c(marg.dists, marg.f$marginals[[i]])
}

mat <- matrix(rep(NA, length(marg.dists)*3), ncol=3)
rownames(mat) <- names(marg.dists)
colnames(mat) <- c("2.5%", "50%", "97.5%")
ignore.me <- union(grep("\\(Int", names(marg.dists)), grep("prec", names(marg.dists))) # take away background k and precision
comment <- rep("", length(marg.dists))
for (i in 1:length(marg.dists)) {
  tmp <- marg.dists[[i]]
  tmp2 <- cumsum(tmp[,2])/sum(tmp[,2])
  mat[i, ] <-c(tmp[which(tmp2>0.025)[1],1],## -1 is so use value on the left of the 2.5%
               tmp[which(tmp2>0.5)[1],1],
               tmp[which(tmp2>0.975)[1],1])
  vec <- mat[i,]

  if( !(i%in%ignore.me) &&  (vec[1]<0 && vec[3]>0)){comment[i]<-"not sig. at 5%"}

  ## truncate for printing
  mat[i,]<-as.numeric(formatC(mat[i,],digits=3,format="f"))
}

knitr::kable(cbind(mat), row.names = TRUE, digits = 3, align = "rrrr", "html") %>%
  kable_styling(bootstrap_options = "striped", full_width = F, position = "left") %>%
  column_spec(3, bold = TRUE)
2.5% 50% 97.5%
AR|(Intercept) 0.890 1.158 1.438
AR|age 0.504 0.797 1.104
pneumS|(Intercept) -2.129 -1.809 -1.515
female|(Intercept) -0.196 0.022 0.231
livdam|(Intercept) 0.980 1.260 1.565
livdam|eggs 0.662 1.554 2.649
eggs|(Intercept) -1.504 -1.229 -0.967
eggs|adg 0.353 0.609 0.889
wormCount|(Intercept) -1.642 -1.397 -1.172
wormCount|AR 0.154 0.272 0.396
wormCount|eggs 3.284 3.507 3.741
wormCount|age -0.823 -0.684 -0.545
wormCount|adg -0.371 -0.243 -0.109
age|(Intercept) -0.346 -0.197 -0.055
age|female 0.190 0.393 0.605
age|precision 0.896 1.040 1.204
adg|(Intercept) -0.052 0.001 0.052
adg|age -0.920 -0.867 -0.816
adg|precision 3.500 4.068 4.711

As said, the marginals represent estimates of the parameters at each node (i.e. the arcs in the DAG). Being the variables of different nature, they have different meaning. Marginals represent correlation coefficients for Gaussian nodes (when the default centering of variables is kept), log rate ratios for Poisson nodes, and log odds ratios for binomial nodes. Therefore, the second and the latter need to be exponentiated, to get the odds ratios or rate ratios respectively.

2.5Q median 97.5Q Interpretation
AR|age 1.655 2.219 3.016 odds ratio
livdam|eggs 1.939 4.730 14.140 odds ratio
eggs|adg 1.423 1.839 2.433 odds ratio
wormCount|AR 0.154 0.272 0.396 rate ratio
wormCount|eggs 3.284 3.507 3.741 rate ratio
wormCount|age -0.823 -0.684 -0.545 rate ratio
wormCount|adg -0.371 -0.243 -0.109 rate ratio
age|female 0.190 0.393 0.605 correlation
adg|age -0.920 -0.867 -0.816 correlation

 

Present final results

As a final step we will refine the DAG by (a) changing the style of the arrows according to the type of association (i.e. solid arrows for positive association and dashed arrows for negative association), and (b) changing the thickness of the arrows according to their link strength. These steps are done in Graphviz, using the DOT language. Details can be found at https://www.graphviz.org/doc/info/attrs.html.

 

2.5Q median 97.5Q Interpretation
AR|age 1.655 2.219 3.016 odds ratio
livdam|eggs 1.939 4.730 14.140 odds ratio
eggs|adg 1.423 1.839 2.433 odds ratio
wormCount|AR 0.154 0.272 0.396 rate ratio
wormCount|eggs 3.284 3.507 3.741 rate ratio
wormCount|age -0.823 -0.684 -0.545 rate ratio
wormCount|adg -0.371 -0.243 -0.109 rate ratio
age|female 0.190 0.393 0.605 correlation
adg|age -0.920 -0.867 -0.816 correlation

For an example in a recent peer reviewed publication see (Comin, Jeremiasson, Kratzer, & Keeling, 2019) or (Kratzer et al., 2020)

References

Comin, A., Jeremiasson, A., Kratzer, G., & Keeling, L. (2019). Revealing the structure of the associations between housing system, facilities, management and welfare of commercial laying hens using additive bayesian networks. Preventive Veterinary Medicine, 164, 23–32.
Dohoo, I. R., Martin, W., Stryhn, H., & others. (2003). Veterinary epidemiologic research. AVC Incorporated Charlottetown, Canada.
Kratzer, G., Lewis, F. I., Willi, B., Meli, M. L., Boretti, F. S., Hofmann-Lehmann, R., … Hartnack, S. (2020). Bayesian network modeling applied to feline calicivirus infection among cats in Switzerland. Frontiers in Veterinary Science, 7, 73.
Kratzer, G., Pittavino, M., Lewis, F., & Furrer, R. (2017). Abn: An r package for modelling multivariate data using additive bayesian networks. R Package Vignette.
Lewis, F. I., & Ward, M. P. (2013). Improving epidemiologic data analyses through multivariate regression modelling. Emerging Themes in Epidemiology, 10(1), 4.
Plummer, M., & others. (2003). JAGS: A program for analysis of bayesian graphical models using gibbs sampling. In Proceedings of the 3rd international workshop on distributed statistical computing (Vol. 124). Vienna, Austria.
LS0tCnRpdGxlOiAiSGFuZHMtb24gZXhlcmNpc2UgLSB1c2VSISBDb25mZXJlbmNlIDIwMjEiCmZvbnRzaXplOiAxNHB0Cm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB0cnVlCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCmJpYmxpb2dyYXBoeTogYmliLmJpYgpjc2w6IGFwYS5jc2wKCi0tLQoKJm5ic3A7CgpGb3IgdGhpcyBoYW5kcy1vbiBleGVyY2lzZSwgd2Ugd2lsbCB1c2UgYW4gYWRhcHRhdGlvbiBvZiB0aGUgKipwaWdfYWRnKiogZGF0YXNldCBkZXNjcmliZWQgaW4gKkRvaG9vLCBNYXJ0aW4gYW5kIFdheW5lIC0gVmV0ZXJpbmFyeSBFcGlkZW1pb2xvZ2ljIFJlc2VhcmNoIChzZWNvbmQgZWRpdGlvbikqIChzZWUgW0Bkb2hvbzIwMDN2ZXRlcmluYXJ5XSkuCgo8IS0tIGF2YWlsYmxlIGF0OiBodHRwOi8vcHJvamVjdHMudXBlaS5jYS92ZXIvZGF0YS1hbmQtc2FtcGxlcy8gLS0+CgombmJzcDsKCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IEZBTFNFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBjb2xsYXBzZT1GQUxTRSwKICAgICAgICAgICAgICAgICAgICAgIGZpZy5hbGlnbj0nY2VudGVyJywgZmlnLmhlaWdodD0zLCBmaWcud2lkdGg9NSwgY29tbWVudCA9IE5BKQoKCgpvcHRpb25zKHNjaXBlbj05OTkpCmxpYnJhcnkoYWJuKQpsaWJyYXJ5KGtuaXRyKQpsaWJyYXJ5KGthYmxlRXh0cmEpCmxpYnJhcnkoZHBseXIpCmBgYAoKTGV0J3MgaW5zdGFsbCBhbGwgcmVxdWlyZWQgYW5kIHN1Z2dlc3QgcGFja2FnZSBmcm9tICoqYWJuKiogKGlmIG5vdCBhbHJlYWR5IGRvbmUpCgpgYGB7ciBpbnN0YWxscGFja2FnZXMsIGVjaG89VFJVRSwgZXZhbD1GQUxTRX0KIyMgZHBseXIgaXMgdXNlZCBmb3IgdGhlIGhhbmRzLW9uIGV4ZXJjaXNlCgppbnN0YWxsLnBhY2thZ2UoYygibm5ldCIsICJsbWU0IiwgIlJncmFwaHZpeiIsICJrbml0ciIsICJSLnJzcCIsICJ0ZXN0dGhhdCIsICJlbnRyb3B5IiwgIm1vbWVudHMiLCAiYm9vdCIsICJicmdsbSIsICJkcGx5ciIpKQoKYGBgCgpMZXQncyBsb2FkIHRoZSBkYXRhIGludG8gdGhlIHdvcmtpbmcgZW52aXJvbm1lbnQ6ICAKCmByZXF1aXJlKCdhYm4nKTsgZGF0YSgiYWRnIiwgcGFja2FnZSA9ICJhYm4iKWAKCmxldCB1cyB3b3JrIHdpdGggYSBkYXRhLmZyYW1lIGFzIHJlcXVpcmVkIGJ5IGFibi4KYGFkZyA8LSBhZGcgJT4lIGFzLmRhdGEuZnJhbWUoKWAKCgombmJzcDsKClRoZSBkYXRhIGZvciB0aGlzIGV4ZXJjaXNlIGNvbnNpc3Qgb2YgKipgciBucm93KGFkZylgKiogb2JzZXJ2YXRpb25zIG9mICoqYHIgbmNvbChhZGcpYCoqIHZhcmlhYmxlcy4gSGVyZSBpcyBhbiBleHRyYWN0IG9mIHRoZSBmaXJzdCByb3dzOiAgCgoKCmBgYHtyIGRhdGFvdmVydmlldywgZWNobz1UUlVFLCBjYWNoZT1GQUxTRX0Ka25pdHI6OmthYmxlKGhlYWQoYWRnKSkgJT4lCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiKSkKCmBgYAombmJzcDsKClRoZSBtZWFuaW5nIG9mIGVhY2ggdmFyaWFibGUgaXMgdGhlIGZvbGxvd2luZzogCgpgYGB7ciBkYXRhTWVhbmluZywgZWNobz1GQUxTRSwgY2FjaGU9RkFMU0V9Cm1tIDwtIGRhdGEuZnJhbWUoVmFyaWFibGUgPSBjb2xuYW1lcyhhZGcpLAogICAgICAgICAgICAgICAgIE1lYW5pbmcgPSBjKCJwcmVzZW5jZSBvZiBhdHJvcGhpYyByaGluaXRpcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInByZXNlbmNlIG9mIG1vZGVyYXRlIHRvIHNldmVyZSBwbmV1bW9uaWEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzZXggb2YgdGhlIHBpZyAoMT1mZW1hbGUsIDA9Y2FzdHJhdGVkKSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInByZXNlbmNlIG9mIGxpdmVyIGRhbWFnZSAocGFyYXNpdGUtaW5kdWNlZCB3aGl0ZSBzcG90cykiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicHJlc2VuY2Ugb2YgZmVjYWwvZ2FzdHJvaW50ZXN0aW5hbCBuZW1hdG9kZSBlZ2dzIGF0IHRpbWUgb2Ygc2xhdWdodGVyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiY291bnQgb2YgbmVtYXRvZGVzIGluIHNtYWxsIGludGVzdGluZSBhdCB0aW1lIG9mIHNsYXVnaHRlciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImRheXMgZWxhcHNlZCBmcm9tIGJpcnRoIHRvIHNsYXVnaHRlciAoZGF5cykiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJhdmVyYWdlIGRhaWx5IHdlaWdodCBnYWluIChncmFtcykiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJmYXJtIElEIiksIERpc3RyaWJ1dGlvbiA9IGMoIkJpbm9taWFsIiwgIkJpbm9taWFsIiwiQmlub21pYWwiLCJCaW5vbWlhbCIsIkJpbm9taWFsIiwgIlBvaXNzb24iLCAiQ29udGludW91cyIsICJDb250aW51b3VzIiwgIkRpc2NyZXRlIikpCgprbml0cjo6a2FibGUobW0sIHJvdy5uYW1lcyA9IEZBTFNFLCBkaWdpdHMgPSAyLCBhbGlnbiA9ICJsbCIsICJodG1sIikgJT4lCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiKSxmdWxsX3dpZHRoID0gRkFMU0UsIHBvc2l0aW9uID0gImxlZnQiKQoKYGBgCgpUaGUgZGlzdHJpYnV0aW9uIG9mIGVhY2ggdmFyaWFibGUgaXMgdGhlIGZvbGxvd2luZzogCgpgYGB7ciBkYXRhRGlzdCwgZWNobz1GQUxTRSwgY2FjaGU9RkFMU0UsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD0xMH0KCnBhcihtZnJvdz1jKDMsMyksIG1hcj1jKDIsNCwxLjUsMSkpCnh4IDwtIGJhcnBsb3QodGFibGUoYWRnJEFSKS8zNDEsIHlsaW09YygwLDEpLCBtYWluPSJBUiIsIHlsYWI9InByb3BvcnRpb24iKQp0ZXh0KHggPSB4eCwgeSA9IHRhYmxlKGFkZyRBUikvMzQxLCBsYWJlbCA9IHRhYmxlKGFkZyRBUiksIHBvcyA9IDMsIGNleCA9IDAuOCwgY29sID0gInJlZCIpCnh4IDwtIGJhcnBsb3QodGFibGUoYWRnJHBuZXVtUykvMzQxLCB5bGltPWMoMCwxKSwgbWFpbj0icG5ldW1TIiwgeWxhYj0icHJvcG9ydGlvbiIpCnRleHQoeCA9IHh4LCB5ID0gdGFibGUoYWRnJHBuZXVtUykvMzQxLCBsYWJlbCA9IHRhYmxlKGFkZyRwbmV1bVMpLCBwb3MgPSAzLCBjZXggPSAwLjgsIGNvbCA9ICJyZWQiKQp4eCA8LSBiYXJwbG90KHRhYmxlKGFkZyRmZW1hbGUpLzM0MSwgeWxpbT1jKDAsMSksIG1haW49ImZlbWFsZSIsIHlsYWI9InByb3BvcnRpb24iKQp0ZXh0KHggPSB4eCwgeSA9IHRhYmxlKGFkZyRmZW1hbGUpLzM0MSwgbGFiZWwgPSB0YWJsZShhZGckZmVtYWxlKSwgcG9zID0gMywgY2V4ID0gMC44LCBjb2wgPSAicmVkIikKeHggPC0gYmFycGxvdCh0YWJsZShhZGckbGl2ZGFtKS8zNDEsIHlsaW09YygwLDEpLCBtYWluPSJsaXZkYW0iLCB5bGFiPSJwcm9wb3J0aW9uIikKdGV4dCh4ID0geHgsIHkgPSB0YWJsZShhZGckbGl2ZGFtKS8zNDEsIGxhYmVsID0gdGFibGUoYWRnJGxpdmRhbSksIHBvcyA9IDMsIGNleCA9IDAuOCwgY29sID0gInJlZCIpCnh4IDwtIGJhcnBsb3QodGFibGUoYWRnJGVnZ3MpLzM0MSwgeWxpbT1jKDAsMSksIG1haW49ImVnZ3MiLCB5bGFiPSJwcm9wb3J0aW9uIikKdGV4dCh4ID0geHgsIHkgPSB0YWJsZShhZGckZWdncykvMzQxLCBsYWJlbCA9IHRhYmxlKGFkZyRlZ2dzKSwgcG9zID0gMywgY2V4ID0gMC44LCBjb2wgPSAicmVkIikKaGlzdChhZGckd29ybUNvdW50LCBtYWluPSJ3b3JtcyIscHJvYj1UUlVFLGNvbD0iZ3JleSIsYm9yZGVyPSJ3aGl0ZSIsIHlsaW09IGMoMCwwLjYpKQpsaW5lcyhkZW5zaXR5KGFkZyR3b3JtQ291bnQpLGx3ZD0xLjUpCmhpc3QoYWRnJGFnZSwgeGxhYj0iIiwgbWFpbj0iYWdlIixwcm9iPVRSVUUsY29sPSJncmV5Iixib3JkZXI9IndoaXRlIikKICBsaW5lcyhkZW5zaXR5KGFkZyRhZ2UpLGx3ZD0xLjUpCmhpc3QoYWRnJGFkZywgeGxhYj0iIiwgbWFpbj0iYWRnIixwcm9iPVRSVUUsY29sPSJncmV5Iixib3JkZXI9IndoaXRlIikKICBsaW5lcyhkZW5zaXR5KGFkZyRhZGcpLGx3ZD0xLjUpCmJhcnBsb3QodGFibGUoYWRnJGZhcm0pLCBtYWluPSJGYXJtIElEIiwgeWxpbT1jKDAsNDApLCB5bGFiPSJjb3VudCIsIGNvbC5tYWluID0gImdyYXk1MCIpCgoKYGBgCgombmJzcDsKCkZvciB0aGlzIGV4ZXJjaXNlIHdlIHdpbGwgZHJvcCAqZmFybSogKGNsdXN0ZXJpbmcgaW5kaWNhdG9yKTogIAoKCmBgYHtyIGRhdGFmaWx0ZXIsIGVjaG89VFJVRX0KCmRyb3AgPC0gd2hpY2goY29sbmFtZXMoYWRnKSVpbiUgYygiZmFybSIpKQphYm5kYXRhIDwtIGFkZ1ssIC1kcm9wXQoKYGBgCgombmJzcDsKCiMgU2V0IGFsbCBiaW5hcnkgdmFyaWFibGVzIHRvIGZhY3RvciBkYXRhIHR5cGUgKGFuZCBjaGVjayB0aGF0IGRhdGFzZXQgaXMgY29tcGxldGUpIAoKQSByZXF1aXJlbWVudCBvZiAqYWJuKiBbQGxld2lzMjAxM2ltcHJvdmluZzsgQGtyYXR6ZXIyMDE3YWJuXSBpcyB0aGF0IGFsbCBiaW5hcnkgdmFyaWFibGVzIGFyZSBjb2VyY2VkIGludG8gZmFjdG9ycy4gIAoKYGBge3IgZGF0YUZhY3RvciwgZWNobz1UUlVFfQoKc3RyKGFibmRhdGEpCmFibmRhdGFbLDE6NV0gPC0gYXMuZGF0YS5mcmFtZShsYXBwbHkoYWJuZGF0YVssMTo1XSwgZmFjdG9yKSkKc3VtKGNvbXBsZXRlLmNhc2VzKGFibmRhdGEpKQoKYGBgCgombmJzcDsKCiMgU2V0dXAgdGhlIGRpc3RyaWJ1dGlvbiBsaXN0IGZvciBlYWNoIHZhcmlhYmxlICAKCkVhY2ggdmFyaWFibGUgaW4gdGhlIG1vZGVsIG5lZWRzIHRvIGJlIGFzc29jaWF0ZWQgdG8gYSBkaXN0cmlidXRpb24gKGN1cnJlbnRseSBhdmFpbGFibGU6IGJpbm9taWFsLCBnYXVzc2lhbiwgcG9pc3NvbikgYWNjb3JkaW5nIHRvIHRoZSB0eXBlIG9mIGRhdGEuIEluIHRoaXMgZXhhbXBsZSBtb3N0IG9mIHRoZSB2YXJpYWJsZXMgYXJlIGJpbmFyeSBhbmQgdGhlcmVmb3JlIGFzc29jaWF0ZWQgdG8gdGhlICoqYmlub21pYWwqKiBkaXN0cmlidXRpb24uIFZhcmlhYmxlcyAqYWdlKiBhbmQgKmFkZyogYXJlIGNvbnRpbnVvdXMsIHNvIHRoZXkgd2lsbCBiZSBhc3NvY2lhdGVkIHRvIHRoZSAqKkdhdXNzaWFuKiogZGlzdHJpYnV0aW9uLiBGaW5hbGx5LCB2YXJpYWJsZSAqKndvcm1Db3VudCoqIGlzIGEgY291bnQgYW5kIGNhbiBiZSBtb2RlbGxlZCBieSBhICoqUG9pc3NvbioqIGRpc3RyaWJ1dGlvbi4gCgpUaGUgc3VwcG9ydGVkIGRpc3RyaWJ1dGlvbnMgYXJlOgoKKiBnYXVzc2lhbiB3aXRoIGlkZW50aXR5IGxpbmsgZnVuY3Rpb24KKiBiaW5vbWlhbCB3aXRoIGxvZ2l0IGxpbmsgZnVuY3Rpb24gKHJlcXVpcmVkIGRhdGEgdG8gYmUgZmFjdG9yKQoqIFBvaXNzb24gZGlzdHJpYnV0ZWQgd2l0aCBsb2cgbGluayBmdW5jdGlvbgoqIG11bHRpbm9taWFsIHdpdGggbG9naXQgbGluayBmdW5jdGlvbiAocmVxdWlyZWQgZGF0YSB0byBiZSBmYWN0b3IuIE9ubHkgYXZhaWxhYmxlIHdpdGggbWxlIGZpdHRpbmcpCgoKYGBge3IgZGlzdHMsIGVjaG89VFJVRX0KCmRpc3QgPC0gbGlzdChBUiA9ICJiaW5vbWlhbCIsIHBuZXVtUyA9ICJiaW5vbWlhbCIsIGZlbWFsZT0iYmlub21pYWwiLCAKICAgICAgICAgICAgIGxpdmRhbT0gImJpbm9taWFsIiwgZWdncyA9ICJiaW5vbWlhbCIsd29ybUNvdW50ID0gInBvaXNzb24iLAogICAgICAgICAgICAgYWdlPSAiZ2F1c3NpYW4iLCBhZGcgPSAiZ2F1c3NpYW4iKQoKYGBgCgombmJzcDsKCiMgQ3JlYXRlIHJldGFpbiBhbmQgYmFubmVkIG1hdHJpeGVzIChvcHRpb25hbCkgIAoKUHJpb3Iga25vd2xlZGdlIGFib3V0IGRhdGEgc3RydWN0dXJlLCB0aGF0IGNvdWxkIGd1aWRlIHRoZSBzZWFyY2ggZm9yIHRoZSBvcHRpbWFsIG1vZGVsLCBjYW4gYmUgaW5jbHVkZWQgYnkgZm9yY2luZyBvciBiYW5uaW5nIHNvbWUgc3BlY2lmaWMgYXJjcyBmcm9tIGJlaW5nIGNvbnNpZGVyZWQgaW4gdGhlIGZpbmFsIERBRy4gVGhpcyBpcyBkb25lIGJ5IHByb3ZpZGluZyBhICoqcmV0YWluIG1hdHJpeCoqIGFuZC9vciBhICoqYmFuIG1hdHJpeCoqLiAKV2Ugd2lsbCBzdGFydCBieSBjcmVhdGluZyB0d28gZW1wdHkgbWF0cmljZXMgd2l0aCB0aGUgc2FtZSBzaXplIGFzIG91ciBkYXRhICg4KSBhbmQgbmFtZWQgcm93cyBhbmQgY29sdW1ucy4KCmBgYHtyIGJhbl9yZXQsIGVjaG89VFJVRX0KCnJldGFpbiA8LSBtYXRyaXgoMCwgbmNvbChhYm5kYXRhKSwgbmNvbChhYm5kYXRhKSkKY29sbmFtZXMocmV0YWluKSA8LSByb3duYW1lcyhyZXRhaW4pIDwtIG5hbWVzKGFibmRhdGEpCgpiYW5uZWQgPC0gbWF0cml4KDAsIG5jb2woYWJuZGF0YSksIG5jb2woYWJuZGF0YSkpCmNvbG5hbWVzKGJhbm5lZCkgPC0gcm93bmFtZXMoYmFubmVkKSA8LSBuYW1lcyhhYm5kYXRhKQoKYGBgCgombmJzcDsKCiMgQmFuIHNvbWUgYXJjcyAob3B0aW9uYWwpClRoZSBpbmZvcm1hdGlvbiBlbmNvZGVkIGluIHRoZSBiYW4gbWF0cml4IGlzICpzdWJqZWN0aXZlbHkgY2hvc2VuKiB0byByZWZsZWN0IG91ciAqKmJlbGllZiBhYm91dCBkYXRhIHN0cnVjdHVyZSoqLiBJbiB0aGlzIGV4YW1wbGUsIGl0IGlzIHJlYXNvbmFibGUgdG8gYXNzdW1lIHRoYXQgbm9uZSBvZiB0aGUgdmFyaWFibGVzIGluIHRoZSBtb2RlbCBpcyBnb2luZyB0byBhZmZlY3QgdGhlIGdlbmRlciBvZiB0aGUgYW5pbWFsICh3aGljaCBpcyBhbiBpbmJvcm4gdHJhaXQpLiBUbyBlbmNvZGUgdGhpcyBpbmZvcm1hdGlvbiB3ZSB3aWxsIGJhbiBhbGwgdGhlIGFyY3MgZ29pbmcgdG8gKmZlbWFsZSosIGJ5IHNldHRpbmcgdGhlaXIgdmFsdWUgdG8gMSAoYmFubmVkKSBhcyBvcHBvc2l0ZSB0byB0aGUgZGVmYXVsdCB2YWx1ZSAwIChub24gYmFubmVkKS4gIAoKSG93IGRvZXMgaXQgd29yaz8KClJvd3MgYXJlIGNoaWxkcmVuLCBjb2x1bW5zIHBhcmVudHM6ICAgCmAuICAgYjEgYjIgYjMgYjRgICAKYGIxICAgMCAgMSAgMCAgMGAgIApgYjIgICAwICAwICAwICAwYCAgCmBiMyAgIDEgIDAgIDAgIDBgICAKYGI0ICAgMCAgMCAgMCAgMGAgIAoKU28gYGJhblsxLCAyXSA8LSAxYCAgbWVhbnMgZG8gbm90IGFsbG93IHRoZSBhcmMgZnJvbSBiMiAoc2Vjb25kIGNvbHVtbikgdG8gYjEgKGZpcnN0IHJvdykgYW5kIGBiYW5bMywgMV0gPC0gMWAgbWVhbnMgZG8gbm90IGFsbG93IHRoZSBhcmMgZnJvbSBiMSAoZmlyc3QgY29sdW1uKSB0byBiMyAodGhpcmQgcm93KS4KCk5vdywgd2Ugd2FudCB0byBiYW4gdGhlIGFyY3MgZ29pbmcgZnJvbSBhbnkgdmFyaWFibGUgKD0gYWxsIGNvbHVtbnMgZXhjZXB0IHRoZSB0aGlyZCkgdG8gKmZlbWFsZSogKD0gdGhpcmQgcm93KTogIApgYGB7ciBiYW4sIGVjaG89VFJVRX0KCmJhbm5lZFszLC0zXSA8LSAxCgpgYGAKCkhhdmUgYSBsb29rIGlmIHlvdSB3YW50OiAgCmBgYHtyIGJhbkNoZWNrLCBlY2hvPVRSVUV9CgpiYW5uZWQKCmBgYAoKJm5ic3A7CgojIFRyeSB0byBydW4gb25lIHNlYXJjaCAgCiZuYnNwOwoKIyMgU3RhcnQgd2l0aCAxIHBhcmVudCBhcyBtYXhpbXVtIGxpbWl0CgphYm4gc3VwcG9ydHMgYW4gbWxlIGFuZCBCYXllc2lhbiBpbXBsZW1lbnRhdGlvbi4gV2Ugd2lsbCBydW4gYSBCYXllc2lhbiAoZGVmYXVsdCkgYW5hbHlzaXMuIAoKVG8gbWFrZSB0aGUgc2VhcmNoIG1vcmUgZWZmaWNpZW50LCB3ZSBjb25zdHJhaW4gdGhlIG1heGltdW0gbnVtYmVyIG9mIHBhcmVudHMgYWxsb3dlZCBmb3IgZWFjaCBub2RlLiBXZSB3aWxsIHN0YXJ0IGZyb20gMSBhbmQgaW5jcmVhc2Ugc3Vic2VxdWVudGx5IHVudGlsIHRoZSAqbmV0d29yayBzY29yZSogZG9lcyBub3QgaW1wcm92ZSBmdXJ0aGVyIGV2ZW4gd2hlbiBtb3JlIHBhcmVudHMgYXJlIGFsbG93ZWQuIAoKYGBge3IgNS4xLCBlY2hvPVRSVUV9CgptYXgucGFyIDwtIDEgCgpgYGAKJm5ic3A7CgojIyBCdWlsZCBhIGNhY2hlIG9mIGFsbCBsb2NhbCBjb21wdXRhdGlvbnMKCmBgYHtyIDUuMiwgZWNobz1UUlVFfQpsaWJyYXJ5KGFibikKCm15Y2FjaGUgPC0gYnVpbGRTY29yZUNhY2hlKGRhdGEuZGYgPSBhcy5kYXRhLmZyYW1lKGFibmRhdGEpLCBkYXRhLmRpc3RzID0gZGlzdCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhZy5iYW5uZWQgPSBiYW5uZWQsIGRhZy5yZXRhaW5lZCA9IHJldGFpbiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG1heC5wYXJlbnRzID0gbWF4LnBhcikKCmBgYAoKVGhlIGZ1bmN0aW9uICpidWlsZFNjb3JlQ2FjaGUoKSogcmV0dXJucyBhIG5hbWVkIGxpc3Qgb2YgY2xhc3MgKmFibkNhY2hlKiB0aGF0IGNvbnRhaW5zIGFsbCBuZWNlc3NhcnkgZGF0YSBhbmQgaW5mcm9tYXRpb24gdG8gYmUgdXNlZCBpbiAqbW9zdFByb2JhYmxlKCkqLiBGb3IgZWFjaCBub2RlLCB0aGUgKmJ1aWxkU2NvcmVDYWNoZSgpKiBmdW5jdGlvbiB0cmllcyBldmVyeSBjb21iaW5hdGlvbiBvZiBwYXJlbnQgbm9kZXMgKGFsd2F5cyBzYXRpc2Z5aW5nIHRoZSBjb25zdHJhaW50cyAqbWF4LnBhciosICpiYW5uZWQqLCAqcmV0YWluZWQqKSBhbmQgY29tcHV0ZXMgYSBzY29yZS4gVGhlIHBhcmVudCBjaGlsZHJlbiBjb21iaW5hdGlvbnMgYXJlIHRoZW4gc3RvcmVkLCB0b2dldGhlciB3aXRoIHRoZWlyIHNjb3Jlcy4gVGhpcyBvYmplY3QgY29udGFpbnMgYWxsIHRoZSBpbmZvcm1hdGlvbiBuZWVkZWQgZm9yIGRpZmZlcmVudCBmdW5jdGlvbnMsIHRoYXQgdHJ5IHRvIGxlYXJuIHRoZSBzdHJ1Y3R1cmUgb2YgdGhlIG5ldHdvcmsuIAoKJm5ic3A7IAoKCiMjIFJ1biB0aGUgZXhhY3Qgc2VhcmNoIGZvciBhIHNwZWNpZmllZCBwYXJlbnQgbGltaXQgIAoKYSkgRmluZCB0aGUgb3B0aW1hbCBEQUcgZ2l2ZW4gdGhlIHBhcmVudCBsaW1pdAoKYGBge3IgNS4zYSwgZWNobz1UUlVFfQpteWRhZyA8LSBtb3N0UHJvYmFibGUoc2NvcmUuY2FjaGUgPSBteWNhY2hlKQpgYGAKICAKYikgRml0IG1hcmdpbmFsIGRlbnNpdGllcwpgYGB7ciA1LjNiLCBlY2hvPVRSVUV9CmZhYm4gPC0gZml0QWJuKG9iamVjdCA9IG15ZGFnKQpgYGAKICAKYykgY2hlY2sgbmV0d29yayBzY29yZQpgYGB7ciA1LjNjLCBlY2hvPVRSVUV9CmZhYm4kbWxpawpgYGAKClRoZSBmdW5jdGlvbiAqbW9zdFByb2JhYmxlKCkqIHJldHVybnMgYW4gb2JqZWN0IG9mIGNsYXNzICphYm5Nb3N0cHJvYmFibGUqLCB3aGljaCBpcyBhIGxpc3QgY29udGFpbmluZzogYSBtYXRyaXggZ2l2aW5nIHRoZSBEQUcgZGVmaW5pdGlvbiBvZiB0aGUgbW9zdCBwcm9iYWJsZSBwb3N0ZXJpb3Igc3RydWN0dXJlLCB0aGUgY2FjaGUgb2YgcHJlLWNvbXB1dGVkIHNjb3JlcyBhbmQgdGhlIHNjb3JlIHVzZWQgZm9yIHNlbGVjdGlvbi4KClRoZSBmdW5jdGlvbiAqZml0QWJuKCkqIHJldHVybnMgYW4gb2JqZWN0IG9mIGNsYXNzICphYm5GaXQqLiBBIG5hbWVkIGxpc3QuIE9uZSBlbnRyeSBmb3IgZWFjaCBvZiB0aGUgdmFyaWFibGVzIGluIGRhdGEuZGYgKGV4Y2x1ZGluZyB0aGUgZ3JvdXBpbmcgdmFyaWFibGUsIGlmIHByZXNlbnQpIHdoaWNoIGNvbnRhaW5zIGFuIGVzdGltYXRlIG9mIHRoZSBsb2cgbWFyZ2luYWwgbGlrZWxpaG9vZCBmb3IgdGhhdCBpbmRpdmlkdWFsIG5vZGUuIEFuIGVudHJ5ICJtbGlrIiB3aGljaCBpcyB0aGUgdG90YWwgbG9nIG1hcmdpbmFsIGxpa2VsaWhvb2QgZm9yIHRoZSBmdWxsIEFCTiBtb2RlbC4gQSB2ZWN0b3Igb2YgZXJyb3IuY29kZXMgLSBub24temVybyBpZiBhIG51bWVyaWNhbCBlcnJvciBvciB3YXJuaW5nIG9jY3VycmVkLCBhbmQgYSB2ZWN0b3IgZXJyb3IuY29kZS5kZXNjIGdpdmluZyBhIHRleHQgZGVzY3JpcHRpb24gb2YgdGhlIGVycm9yLiBBIGxpc3QgbW9kZXMsIHdoaWNoIGNvbnRhaW5zIGFsbCB0aGUgbW9kZSBlc3RpbWF0ZXMgZm9yIGVhY2ggcGFyYW1ldGVyIGF0IGVhY2ggbm9kZS4gQSB2ZWN0b3IgY2FsbGVkIEhlc3NpYW4gYWNjdXJhY3ksIHdoaWNoIGlzIHRoZSBlc3RpbWF0ZWQgbG9jYWwgZXJyb3IgaW4gdGhlIGxvZyBtYXJnaW5hbCBsaWtlbGlob29kIGZvciBlYWNoIG5vZGUuIElmIGNvbXB1dGUuZml4ZWQ9VFJVRSB0aGVuIGEgbGlzdCBlbnRyeSBjYWxsZWQgbWFyZ2luYWxzIHdoaWNoIGNvbnRhaW5zIGEgbmFtZWQgZW50cnkgZm9yIGV2ZXJ5IHBhcmFtZXRlciBpbiB0aGUgQUJOIGFuZCBlYWNoIGVudHJ5IGluIHRoaXMgbGlzdCBpcyBhIHR3by1jb2x1bW4gbWF0cml4IHdoZXJlIHRoZSBmaXJzdCBjb2x1bW4gaXMgdGhlIHZhbHVlIG9mIHRoZSBtYXJnaW5hbCBwYXJhbWV0ZXIsIHNheSB4LCBhbmQgdGhlIHNlY29uZCBjb2x1bW4gaXMgdGhlIHJlc3BlY3RpdmUgZGVuc2l0eSB2YWx1ZSwgcGRmKHgpLiBBbHNvLCBhIGxpc3QgY2FsbGVkIG1hcmdpbmFsLnF1YW50aWxlcyBpcyBwcm9kdWNlZCwgZ2l2aW5nIHRoZSBxdWFudGlsZXMgZm9yIGVhY2ggbWFyZ2luYWwgcG9zdGVyaW9yIGRpc3RyaWJ1dGlvbi4gSWYgY3JlYXRlLmdyYXBoPVRSVUUgdGhlbiBhbiBhZGRpdGlvbmFsIGVudHJ5IGdyYXBoIHdoaWNoIGlzIG9mIHR5cGUgY2xhc3MgKmdyYXBoQU0tY2xhc3MqIChSZ3JhcGh2aXopIGlzIGNyZWF0ZWQuCgombmJzcDsKCiMgUnVuIHRoZSBleGFjdCBzZWFyY2ggYWNyb3NzIGluY3JlbWVudGFsIHBhcmVudCBsaW1pdHMgClJlcGVhdCBzdGVwIDUgZm9yIGluY3JlbWVudGFsIHBhcmVudCBsaW1pdCAoZS5nLiAxIHRvIG5yLnZhci0xKS4gVGhlIG9wdGltYWwgREFHIGlzIHRoZSBvbmUgd2hlcmUgdGhlIG5ldHdvcmsgc2NvcmUgZG9lcyBub3QKaW1wcm92ZSAoaS5lLiBiZWNvbWVzIGJpZ2dlcikgYW55IGxvbmdlciBieSBhbGxvd2luZyBtb3JlIHBhcmVudHMuICAKCmBgYHtyIHNlYXJjaCwgZWNobz1UUlVFfQoKZGF0YWRpciA8LSB0ZW1wZGlyKCkgCgpmb3IgKGkgaW4gMTo3KSB7CiAgbWF4LnBhciA8LSBpCiAgCiAgbXljYWNoZSA8LSBidWlsZFNjb3JlQ2FjaGUoZGF0YS5kZiA9IGFzLmRhdGEuZnJhbWUoYWJuZGF0YSksIGRhdGEuZGlzdHMgPSBkaXN0LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYWcuYmFubmVkID0gYmFubmVkLCBkYWcucmV0YWluZWQgPSByZXRhaW4sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1heC5wYXJlbnRzID0gbWF4LnBhcikKICAKICBteWRhZyA8LSBtb3N0UHJvYmFibGUoc2NvcmUuY2FjaGUgPSBteWNhY2hlKQogIAogIGZhYm4gPC0gZml0QWJuKG9iamVjdCA9IG15ZGFnKQogIAogIGNhdChwYXN0ZSgibmV0d29yayBzY29yZSBmb3IiLCBpLCAicGFyZW50cyA9IiwgZmFibiRtbGlrLCAiXG5cbiIpKQogICAgICAKICBzYXZlKG15Y2FjaGUsIG15ZGFnLCBmYWJuLCBmaWxlID0gcGFzdGUoZGF0YWRpciwibXBfIiwgbWF4LnBhciwiLlJEYXRhIiwgc2VwPSIiKSkKIAp9CgojIGdldCBuZXR3b3JrIHNjb3JlIGZvciBhbGwgcGFyZW50IGxpbWl0cwojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQptcC5tbGlrIDwtIGMoKQpmb3IgKGkgaW4gMTptYXgucGFyKSB7CiAgbG9hZChwYXN0ZShkYXRhZGlyLCJtcF8iLCBpLCIuUkRhdGEiLCBzZXA9IiIpKQogIG1wLm1saWsgPC0gYyhtcC5tbGlrLCBmYWJuJG1saWspCn0KCmBgYAoKJm5ic3A7CgojIFBsb3QgbG9nIG1hcmdpbmFsIGxpa2VsaWhvb2QgaW4gZnVuY3Rpb24gb2YgdGhlIHBhcmVudCBsaW1pdAoKYGBge3IgbWxpaywgZWNobz1UUlVFfQoKIyBjaGVjayBob3cgaXQgbG9va3MKIyAtLS0tLS0tLS0tLS0tLS0tLS0KcGxvdCgxOm1heC5wYXIsIG1wLm1saWssIHhsYWIgPSAiUGFyZW50IGxpbWl0IiwgeWxhYiA9ICJMb2cgbWFyZ2luYWwgbGlrZWxpaG9vZCIsIAogICAgIHR5cGUgPSAiYiIsIGNvbD0icmVkIiwgeWxpbT1yYW5nZShtcC5tbGlrKSkKYWJsaW5lKHY9d2hpY2gobXAubWxpaz09bWF4KG1wLm1saWspKVsxXSwgY29sPSJncmV5IiwgbHR5PTIpCgpgYGAKCkFmdGVyIG1heC5wYXI9YHIgd2hpY2gobXAubWxpaz09bWF4KG1wLm1saWspKVsxXWAgdGhlIG1heGltdW0gbG9nIG1hcmdpbmFsIGxpa2VsaWhvb2QgaXMgY29uc3RhbnQ6ICAKCmBgYHtyIG1saWsyLCBlY2hvPUZBTFNFfQoKbXAubWxpawogCmBgYAoKJm5ic3A7CgpIZXJlIGlzIHdoYXQgdGhlIGJlc3QgZml0dGluZyBEQUcgbG9va3MgbGlrZSB1c2luZyAqcGxvdEFibigpKiBmdW5jdGlvbjoKCmBgYHtyIERBR3Nob3d9CgpwbG90QWJuKG15ZGFnKQoKYGBgCgombmJzcDsKCkJlZm9yZSBnb2luZyBhaGVhZCBpbnRlcnByZXRpbmcgdGhlIHJlc3VsdHMsIHdlIG5lZWQgb25lIG1vcmUgc3RlcC4gU28gZmFyLCB3ZSBoYXZlIGlkZW50aWZpZWQgYSBEQUcgd2hpY2ggaGFzIHRoZSBtYXhpbXVtIHBvc3NpYmxlIGdvb2RuZXNzIG9mIGZpdCBhY2NvcmRpbmcgdG8gdGhlIGxvZyBtYXJnaW5hbCBsaWtlbGlob29kLiBUaGlzIGlzIHRoZSBzdGFuZGFyZCBnb29kbmVzcyBvZiBmaXQgbWV0cmljIGluIEJheWVzaWFuIG1vZGVsbGluZyBhbmQgaW5jbHVkZXMgYW4gaW1wbGljaXQgcGVuYWx0eSBmb3IgbW9kZWwgY29tcGxleGl0eS4gSG93ZXZlciwgdGhlIGxvZyBtYXJnaW5hbCBsaWtlbGlob29kIGlzIGFsc28ga25vd24gdG8gYmUgcHJvbmUgdG8gKipvdmVyZml0dGluZyoqIChlc3BlY2lhbGx5IHdpdGggc21hbGxlciBkYXRhIHNldHMpLCBtZWFuaW5nIHRoYXQgaXQgbWF5IGlkZW50aWZ5IG1vcmUgcGFyYW1ldGVycyB0aGFuIGNhbiBiZSBhY3R1YWxseSBqdXN0aWZpZWQgYnkgdGhlIGRhdGEuIFRoZXJlZm9yZSwgaXQgaXMgYWR2aXNhYmxlIHRvIGNoZWNrIGFuZCBhZGRyZXNzIHBvdGVudGlhbCBvdmVyZml0dGluZyBiZWZvcmUgZHJhd2luZyBhbnkgY29uY2x1c2lvbiBiYXNlZCBvbiB0aGUgbW9kZWwgcmVzdWx0cy4gCgpBIHdlbGwgZXN0YWJsaXNoZWQgYXBwcm9hY2ggZm9yIGFkZHJlc3Npbmcgb3ZlcmZpdHRpbmcgaXMgdG8gdXNlICoqcGFyYW1ldHJpYyBib290c3RyYXBwaW5nKiouIEJhc2ljYWxseSwgdGhlIG1vZGVsIGNob3NlbiBmcm9tIHRoZSBleGFjdCBzZWFyY2ggd2lsbCBiZSB1c2VkIHRvIGdlbmVyYXRlIG1hbnkgYm9vdHN0cmFwIGRhdGFzZXRzIChlLmcuIDEwMDApIG9mIGVxdWFsIHNpemUgdG8gdGhlIG9yaWdpbmFsIGRhdGFzZXQgKG49YHIgbnJvdyhhYm5kYXRhKWAgaW4gb3VyIGNhc2UpLiBFYWNoIGJvb3RzdHJhcCBkYXRhc2V0IHdpbGwgYmUgdGhlbiB0cmVhdGVkIGFzIGlmIGl0IHdlcmUgdGhlIG9yaWdpbmFsIGRhdGEsIGFuZCBhIGdsb2JhbGx5IG9wdGltYWwgREFHIHdpbGwgYmUgaWRlbnRpZmllZCBleGFjdGx5IGFzIGRlc2NyaWJlZCBiZWZvcmUgKGkuZS4gZXhhY3Qgc2VhcmNoKS4gV2Ugd2lsbCB0aGVyZWZvcmUgZ2V0IGFzIG1hbnkgREFHcyBhcyB0aGUgbnVtYmVyIG9mIHNpbXVsYXRlZCBkYXRhc2V0cyAoZWcuIDEwMDApLiBUbyBhZGRyZXNzIG92ZXJmaXR0aW5nLCBhbnkgYXJjcyBpbiB0aGUgREFHIGZyb20gdGhlIG9yaWdpbmFsIGRhdGEgd2hpY2ggd2lsbCBub3QgYmUgcmVjb3ZlcmVkIGluID4gNTAlIG9mIHRoZSBib290c3RyYXAgREFHcyB3aWxsIGJlIGRlZW1lZCB0byBoYXZlIGluc3VmZmljaWVudCBzdGF0aXN0aWNhbCBzdXBwb3J0IHRvIGJlIGNvbnNpZGVyZWQgcm9idXN0LiAKCiZuYnNwOwoKIyBQYXJhbWV0cmljIGJvb3RzdHJhcHBpbmcgCgpUaGlzIHN0ZXAgd2lsbCBiZSBkb25lIHdpdGggdGhlIGFpZCBvZiBhIHNvZnR3YXJlIGZvciBCYXllc2lhbiBzdGF0aXN0aWNhbCBhbmFseXNpcyB1c2luZyBNYXJrb3YgQ2hhaW4gTW9udGUgQ2FybG8gKE1DTUMpIHNpbXVsYXRpb25zICh3ZSB3aWxsIHVzZSAqKkpBR1MqKiBbQHBsdW1tZXIyMDAzamFnc10sIGJ1dCBhbnkgb3RoZXIgaXMgZmluZSkuIFdlIHdpbGwgdXNlIHRoZSBwYXJhbWV0ZXJzIGVzdGltYXRlZCBmcm9tIG91ciBtb2RlbCB0byBidWlsZCBhICpCVUcqIG1vZGVsIHRvIHNpbXVsYXRlIGRhdGEuIEluIG90aGVyIHdvcmRzLCB3ZSB3aWxsIGRvIHRoZSByZXZlcnNlIHByb2Nlc3M6IGluc3RlYWQgb2YgdXNpbmcgZGF0YSB0byBlc3RpbWF0ZSBwYXJhbWV0ZXJzLCB3ZSB3aWxsIHVzZSBwYXJhbWV0ZXJzIHRvIGVzdGltYXRlIGRhdGEuIAoKJm5ic3A7CgojIyBFeHRyYWN0IHBhcmFtZXRlcnMgZnJvbSBiZXN0IGZpdHRpbmcgbW9kZWwgYW5kIHNhdmUgdGhlbSBmb3IgTUNNQyBzaW11bGF0aW9ucyAgCgpUaGUgcGFyYW1ldGVycyBhdCBlYWNoIG5vZGUgYXJlIGVzdGltYXRlZCBhcyBwb3N0ZXJpb3IgcHJvYmFiaWxpdHkgZGVuc2l0eSBkaXN0cmlidXRpb25zLiBUaGVzZSBtYXJnaW5hbCBkZW5zaXRpZXMgYXJlIHRoZSBvbmVzIHdoZXJlIEpBR1MgbmVlZHMgdG8gc2FtcGxlIGZyb20gaW4gb3JkZXIgdG8gc2ltdWxhdGUgZGF0YS4gSW4gb3JkZXIgdG8gdXNlIHRoZXNlIGRpc3RyaWJ1dGlvbnMgd2l0aCBKQUdTLCB0aGUgZGVuc2l0aWVzIG5lZWQgdG8gYmUgYXBwcm94aW1hdGVkIGJ5IGEgZGlzY3JldGUgZGlzdHJpYnV0aW9uIG92ZXIgYSBmaW5lIGFuZCBlcXVhbGx5IHNwYWNlZCBncmlkLiBIZXJlIGlzIGEgbW9jayBleGFtcGxlIGZvciBhIGh5cG90aGV0aWNhbCBwYXJhbWV0ZXIgJFxiZXRhJDoKCmBgYHtyIGV4R3JpZCwgLCBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD04fQoKdjEgPC0gcm5vcm0oMTAwMDAsIDAsIDEpCnBhcihtZnJvdz1jKDEsMiksIG1hcj1jKDQsNCwyLDEpKSAKCnBsb3QoZGVuc2l0eSh2MSksIG1haW49Ik1hcmdpbmFsIGRlbnNpdHkiLCB4bGFiPWV4cHJlc3Npb24ocGFzdGUoInBhcmFtZXRlciAiLCBiZXRhKSksIAogICAgIGNleC5heGlzPTAuOSwgY2V4Lm1haW49MSkKaGlzdCh2MSwgYnJlYWtzPTEwMCwgbWFpbj0iRGlzY3JldGl6YXRpb24gb2YgbWFyZ2luYWwgZGVuc2l0eSIsIHhsYWI9ZXhwcmVzc2lvbihwYXN0ZSgicGFyYW1ldGVyICIsIGJldGEpKSwgCiAgICAgY2V4LmF4aXM9MC45LCBjZXgubWFpbj0xKQoKYGBgCgombmJzcDsKCmBgYHtyIHByZXBCb290LCBlY2hvPVRSVUV9CiMgRml0IG1hcmdpbmFsIGRlbnNpdGllcyBvdmVyIGEgZml4ZWQgZ3JpZCAtLT4gbi5ncmlkPTEwMDAKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQogIG1hcmcuZiA8LSBmaXRBYm4ob2JqZWN0ID0gbXlkYWcsIGNvbXB1dGUuZml4ZWQ9VFJVRSwgbi5ncmlkPTEwMDApCgojIEV4dHJhY3QgdmFsdWVzIAojIC0tLS0tLS0tLS0tLS0tCiAgbSA8LSBtYXJnLmYkbWFyZ2luYWxzW1sxXV0gCiAgZm9yKGkgaW4gMjogbGVuZ3RoKG1hcmcuZiRtYXJnaW5hbHMpKQogIHsgbSA8LSBjKG0sIG1hcmcuZiRtYXJnaW5hbHNbW2ldXSl9CiAgCiMgQmluZCBhbGwgdGhlIG1hcmdpbmFscyBmb3IgdGhlIHNhbWUgbm9kZSBpbnRvIGEgbWF0cml4CiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiAgQVIucCA8LSBjYmluZCggbVtbICJBUnwoSW50ZXJjZXB0KSJdXSwgbVtbICJBUnxhZ2UiXV0pCiAgcG5ldW1TLnAgPC0gY2JpbmQoIG1bWyAicG5ldW1TfChJbnRlcmNlcHQpIl1dLCBtW1sgInBuZXVtU3xhZ2UiXV0pCiAgZmVtYWxlLnAgPC0gY2JpbmQoIG1bWyAiZmVtYWxlfChJbnRlcmNlcHQpIl1dKQogIGxpdmRhbS5wIDwtIGNiaW5kKCBtW1sgImxpdmRhbXwoSW50ZXJjZXB0KSJdXSwgbVtbICJsaXZkYW18ZWdncyJdXSkKICBlZ2dzLnAgPC0gY2JpbmQoIG1bWyAiZWdnc3woSW50ZXJjZXB0KSJdXSwgbVtbICJlZ2dzfGFkZyJdXSkKICB3b3JtQ291bnQucCA8LSBjYmluZCggbVtbICJ3b3JtQ291bnR8KEludGVyY2VwdCkiXV0sIG1bWyAid29ybUNvdW50fEFSIl1dLAogICAgICAgICAgICAgICAgICAgICAgICBtW1sgIndvcm1Db3VudHxlZ2dzIl1dLCBtW1sgIndvcm1Db3VudHxhZ2UiXV0sIG1bWyAid29ybUNvdW50fGFkZyJdXSkKICBhZ2UucCA8LSBjYmluZCggbVtbICJhZ2V8KEludGVyY2VwdCkiXV0sIG1bWyAiYWdlfGZlbWFsZSJdXSkKICBwcmVjLmFnZS5wIDwtIGNiaW5kKCBtW1sgImFnZXxwcmVjaXNpb24iIF1dKQogIGFkZy5wIDwtIGNiaW5kKCBtW1sgImFkZ3woSW50ZXJjZXB0KSJdXSwgbVtbICJhZGd8YWdlIl1dKQogIHByZWMuYWRnLnAgPC0gY2JpbmQoIG1bWyAiYWRnfHByZWNpc2lvbiIgXV0pCgojIFNhdmUgaXQgdG8gYSBmaWxlIG5hbWVkIFBvc3RQYXJhbXMgdG8gYmUgcmVhZCBieSBKQUdTCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KICBkdW1wKGMoIkFSLnAiLCAicG5ldW1TLnAiLCAiZmVtYWxlLnAiLCAibGl2ZGFtLnAiLCAiZWdncy5wIiwgCiAgICAgICAid29ybUNvdW50LnAiLCAiYWdlLnAiLCAicHJlYy5hZ2UucCIsICJhZGcucCIsICJwcmVjLmFkZy5wIiksCiAgICAgZmlsZT0iUG9zdFBhcmFtcy5SIikKCmBgYAoKJm5ic3A7CgojIyBXcml0ZSB0aGUgQlVHIG1vZGVsCkluIG9yZGVyIHRvIHNpbXVsYXRlIHRoZSB2YXJpYWJsZXMgb2Ygb3VyIGRhdGFzZXQgd2UgbmVlZCB0byBwcm92aWRlIGEgbW9kZWwgZm9yIGVhY2ggb2YgdGhlbSwgdXNpbmcgdGhlIGFmb3JlbWVudGlvbmVkIHBhcmFtZXRlcnMgZXN0aW1hdGVzLiBGb3IgaW5zdGFuY2UsIHRoZSBiaW5vbWlhbCBub2RlICpBUiogaW4gb3VyIERBRyBoYXMgb25lIGluY29taW5nIGFyYyBjb21pbmcgZnJvbSB0aGUgbm9kZSAqYWdlKi4gSW4gYSByZWdyZXNzaW9uIG5vdGF0aW9uIHRoaXMgd291bGQgYmUgdHJhbnNsYXRlZCBpbnRvOiAgCgo8cCBzdHlsZT0idGV4dC1hbGlnbjogY2VudGVyOyI+bG9naXQoKkFSKikgPSAkXGFscGhhJCArICRcYmV0YSQgeCAqYWdlKiArICRcZXBzaWxvbiQ8L2E+PC9wPgogICAgICAgIAogICAgICAKd2hlcmUgJFxhbHBoYSQgaXMgdGhlIGludGVyY2VwdCBhbmQgJFxiZXRhJCB0aGUgcmVncmVzc2lvbiBjb2VmZmljaWVudCBmb3IgdmFyaWFibGUgKmFnZSogYW5kICRcZXBzaWxvbiQgaXMgdGhlIGVycm9yIHRlcm0gbW9kZWxsZWQgYnkgYSBiaW5vbWlhbCBkaXN0cmlidXRpb24uICAKCkdpdmVuIHRoYXQgd2Ugd2lsbCBzaW11bGF0ZSB0aGUgZGF0YSBpbiBhIEJheWVzaWFuIGZyYW1ld29yaywgQVIgd2lsbCBiZSBtb2RlbGxlZCBhcyBhIHByb2JhYmlsaXR5IGRpc3RyaWJ1dGlvbi4gVGhlcmVmb3JlIGl0IHdpbGwgbG9vayBsaWtlOiAgCgpgQVIgfiBkYmVybihwcm9iQVIpO2AgIApgbG9naXQocHJvYkFSKTwtIGFscGhhICsgYmV0YSphZ2U7YCAgCgpUaGVuLCB0aGUgdmFsdWVzIG9mIGFscGhhIGFuZCBiZXRhIHdpbGwgYmUgc2FtcGxlZCAoZGNhdCkgZnJvbSBvdXIgZGlzY3JldGUgZGlzdHJpYnV0aW9uIG9mIHBhcmFtZXRlcnM6ICAKCmBhbHBoYS5wcm9iIH4gZGNhdChBUi5wWyAsMl0pO2AgLS0+IHNhbXBsZSBmcm9tIHRoZSB2ZWN0b3Igb2YgZGVuc2l0eSB2YWx1ZXMgZih4KSAoc2Vjb25kIGNvbHVtbiBpbiBtYXRyaXgpICAKYGFscGhhIH4gQVIucFthbHBoYS5wcm9iLDFdO2AgICAtLT4gY29ycmVzcG9uZGluZyB4IHZhbHVlIGZvciB0aGUgc2FtcGxlZCBkZW5zaXR5IChmaXJzdCBjb2x1bW4gaW4gbWF0cml4KSAgCmBiZXRhLnByb2IgfiBkY2F0KEFSLnBbICw0XSk7YCAgCmBiZXRhIH4gQVIucFtiZXRhLnByb2IsMV07YCAgIAogCiZuYnNwOyAKClRoZSBCVUcgZmlsZSAoKm1vZGVsOHZQb2lzLmJ1ZyopIGNhbiBiZSByZXRyaWV2ZWQgZnJvbSB0aGUgKmZpbGUqIGRpcmVjdG9yeS4KCiZuYnNwOwoKIyMgUnVuIEpBR1MgYW5kIGluc3BlY3QgdGhlIHJlc3VsdCBvZiBhIHNpbXVsYXRlZCBkYXRhc2V0CgpgYGB7ciB0ZXN0SkFHUywgZWNobz1UUlVFfQoKbGlicmFyeShyamFncykKCiMgc2V0IGluaXRzCiMgLS0tLS0tLS0tCmluaXQgPC0gbGlzdCgiLlJORy5uYW1lIj0iYmFzZTo6TWVyc2VubmUtVHdpc3RlciIsICIuUk5HLnNlZWQiPTQyKQoKIyBsb2FkIGRhdGEKIyAtLS0tLS0tLS0Kc291cmNlKCJQb3N0UGFyYW1zLlIiKQoKIyBydW4gbW9kZWwgb25jZQojIC0tLS0tLS0tLS0tLS0tCmpqIDwtIGphZ3MubW9kZWwoZmlsZSA9ICJtb2RlbDh2UG9pcy5idWciLCAKICAgICAgICAgICAgICAgICBkYXRhID0gbGlzdCggICdBUi5wJz1BUi5wICwgJ3BuZXVtUy5wJz1wbmV1bVMucCAsICdmZW1hbGUucCc9ZmVtYWxlLnAsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ2xpdmRhbS5wJz1saXZkYW0ucCAsICdlZ2dzLnAnPWVnZ3MucCAsICd3b3JtQ291bnQucCc9d29ybUNvdW50LnAgLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdhZ2UucCc9YWdlLnAgLCdwcmVjLmFnZS5wJz1wcmVjLmFnZS5wLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdhZGcucCc9YWRnLnAgLCAncHJlYy5hZGcucCc9cHJlYy5hZGcucCksCiAgICAgICAgICAgICAgICAgaW5pdHMgPSBpbml0LAogICAgICAgICAgICAgICAgIG4uY2hhaW5zID0gMSwgCiAgICAgICAgICAgICAgICAgbi5hZGFwdCA9IDUwMCkKCiMgcnVuIG1vcmUgaXRlcmF0aW9ucwojIC0tLS0tLS0tLS0tLS0tLS0tLS0KdXBkYXRlKGpqLCAxMDAwMDApCgojIHNldCBudW1iZXIgb2Ygb2JzZXJ2YXRpb24gd2Ugd2FudCB0byBleHRyYWN0IGZvciBhIGRhdGFzZXQgCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQpuLm9icz0zNDEKCiMgc2FtcGxlIGRhdGEgKHNhbWUgc2l6ZSBhcyBvcmlnaW5hbDogMzQxKSB3aXRoIGEgc2FtcGxpbmcgbGFnICgyMCkgdG8gcmVkdWNlIGF2b2lkIGF1dG9jb3JyZWxhdGlvbgojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0Kc2FtcCA8LSBjb2RhLnNhbXBsZXMoamosIGMoIkFSIiwgInBuZXVtUyIsICJmZW1hbGUiLCAibGl2ZGFtIiwgImVnZ3MiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIndvcm1Db3VudCIsICJhZ2UiLCAicHJlYy5hZ2UiLCAiYWRnIiwgInByZWMuYWRnIiksCiAgICAgICAgICAgICAgICAgICAgIG4uaXRlcj0gbi5vYnMqMjAgLCB0aGluID0yMCkKCmBgYAoKTm93IGNvbXBhcmUgdGhlIHNpbXVsYXRlZCBkYXRhIHdpdGggdGhlIG9yaWdpbmFsIGRhdGEuIE9ic2VydmUgdGhhdCBHYXVzc2lhbiBub2RlcyBhcmUgYnkgZGVmYXVsdCBjZW50cmVkIHdoZW4gZG9pbmcgYWJuIHNlYXJjaCwgbWVhbmluZyB0aGF0IHRoZSBzaW11bGF0ZWQgZGF0YSBmb3IgdGhvc2Ugbm9kZXMgd2lsbCBiZSBjZW50cmVkIGFzIHdlbGwuIAoKYGBge3IgdGVzdEpBR1MyLCBlY2hvPUZBTFNFLCBmaWcuaGVpZ2h0PTIsIGZpZy53aWR0aD0xMH0KIyBleHRyYWN0IHBvc3RlcmlvciBkZW5zaXRpZXMgYW5kIHB1dCBpbiBhIGRhdGFmcmFtZQojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCnBvc3QuZHQgPC0gZGF0YS5mcmFtZShBUiA9IHVubGlzdChzYW1wWywiQVIiXSksCiAgICAgICAgICAgICAgICAgICAgICBwbmV1bVMgPSB1bmxpc3Qoc2FtcFssInBuZXVtUyJdKSwKICAgICAgICAgICAgICAgICAgICAgIGZlbWFsZSA9IHVubGlzdChzYW1wWywiZmVtYWxlIl0pLAogICAgICAgICAgICAgICAgICAgICAgbGl2ZGFtID0gdW5saXN0KHNhbXBbLCJsaXZkYW0iXSksCiAgICAgICAgICAgICAgICAgICAgICBlZ2dzID0gdW5saXN0KHNhbXBbLCJlZ2dzIl0pLAogICAgICAgICAgICAgICAgICAgICAgd29ybUNvdW50ID0gdW5saXN0KHNhbXBbLCJ3b3JtQ291bnQiXSksCiAgICAgICAgICAgICAgICAgICAgICBhZ2UgPSB1bmxpc3Qoc2FtcFssImFnZSJdKSwKICAgICAgICAgICAgICAgICAgICAgIGFkZyA9IHVubGlzdChzYW1wWywiYWRnIl0pKQoKCiMgY29tcGFyZSB3aXRoIG9yaWdpbmFsIGRhdGEKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQpkdDwtYWJuZGF0YQoKIyBnZXQgY2VudGVyZWQgdmVyc2lvbiBvZiBhZ2UgYW5kIGFkZyB0byBjb21wYXJlIHRvIGJvb3RzdHJhcCBkYXRhCmR0JGFnZS5jIDwtIChkdCRhZ2UgLSBtZWFuKGR0JGFnZSkpL3NkKGR0JGFnZSkgCmR0JGFkZy5jIDwtIChkdCRhZGcgLSBtZWFuKGR0JGFkZykpL3NkKGR0JGFkZykgCgojIENvbXBhcmUgZGlzdHJpYnV0aW9uIG9mIG9yaWdpbmFsIGFuZCBzaW11bGF0ZWQgZGF0YQojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQpwYXIobWZyb3c9YygxLDQpLCBtYXI9YygyLDIsMS41LDEpKQoKYmFycGxvdCh0YWJsZShkdCRBUikvMzQxLCB5bGltPWMoMCwxKSwgbWFpbj0iQVIgLSBvcmlnaW5hbCIpCmJhcnBsb3QodGFibGUocG9zdC5kdCRBUikvMzQxLCAgeWxpbT1jKDAsMSksIG1haW49IkFSIC0gc2ltdWxhdGVkIiwgCiAgICAgICAgY29sLm1haW4gPSAiYmx1ZSIsIGJvcmRlcj0iYmx1ZSIpIAoKYmFycGxvdCh0YWJsZShkdCRwbmV1bVMpLzM0MSwgeWxpbT1jKDAsMSksIG1haW49InBuZXVtUyAtIG9yaWdpbmFsIikKYmFycGxvdCh0YWJsZShwb3N0LmR0JHBuZXVtUykvMzQxLCAgeWxpbT1jKDAsMSksIG1haW49InBuZXVtUyAtIHNpbXVsYXRlZCIsIAogICAgICAgIGNvbC5tYWluID0gImJsdWUiLCBib3JkZXI9ImJsdWUiKSAKCmJhcnBsb3QodGFibGUoZHQkZmVtYWxlKS8zNDEsIHlsaW09YygwLDEpLCBtYWluPSJmZW1hbGUgLSBvcmlnaW5hbCIpCmJhcnBsb3QodGFibGUocG9zdC5kdCRmZW1hbGUpLzM0MSwgIHlsaW09YygwLDEpLCBtYWluPSJmZW1hbGUgLSBzaW11bGF0ZWQiLCAKICAgICAgICBjb2wubWFpbiA9ICJibHVlIiwgYm9yZGVyPSJibHVlIikgCgpiYXJwbG90KHRhYmxlKGR0JGxpdmRhbSkvMzQxLCB5bGltPWMoMCwxKSwgbWFpbj0ibGl2ZGFtIC0gb3JpZ2luYWwiKQpiYXJwbG90KHRhYmxlKHBvc3QuZHQkbGl2ZGFtKS8zNDEsICB5bGltPWMoMCwxKSwgbWFpbj0ibGl2ZGFtIC0gc2ltdWxhdGVkIiwgCiAgICAgICAgY29sLm1haW4gPSAiYmx1ZSIsIGJvcmRlcj0iYmx1ZSIpCgpiYXJwbG90KHRhYmxlKGR0JGVnZ3MpLzM0MSwgeWxpbT1jKDAsMSksIG1haW49ImVnZ3MgLSBvcmlnaW5hbCIpCmJhcnBsb3QodGFibGUocG9zdC5kdCRlZ2dzKS8zNDEsICB5bGltPWMoMCwxKSwgbWFpbj0iZWdncyAtIHNpbXVsYXRlZCIsIAogICAgICAgIGNvbC5tYWluID0gImJsdWUiLCBib3JkZXI9ImJsdWUiKSAKCmhpc3QoZHQkd29ybUNvdW50LCB4bGFiPSIiLCBtYWluPSJ3b3JtQ291bnQgLSBvcmlnaW5hbCIsCiAgICAgcHJvYj1UUlVFLGNvbD0iZ3JleSIsYm9yZGVyPSJ3aGl0ZSIsIHlsaW09YygwLDAuNikpCiAgICAgbGluZXMoZGVuc2l0eShkdCR3b3JtQ291bnQpLGx3ZD0xLjUpCiAgICAgCmhpc3QocG9zdC5kdCR3b3JtQ291bnQsIHhsYWI9IiIsIG1haW49Indvcm1Db3VudCAtIHNpbXVsYXRlZCIsIGNvbC5tYWluID0gImJsdWUiLCAKICAgICBwcm9iPVRSVUUsY29sPSJncmV5Iixib3JkZXI9IndoaXRlIiwgeGxpbT1jKDAsODApLCB5bGltPWMoMCwwLjYpKQogICAgIGxpbmVzKGRlbnNpdHkocG9zdC5kdCR3b3JtQ291bnQpLGx3ZD0xLjUsIGNvbD0iYmx1ZSIpCgpoaXN0KGR0JGFnZS5jLCB4bGFiPSIiLCBtYWluPSJhZ2UgLSBvcmlnaW5hbCIsCiAgICAgcHJvYj1UUlVFLGNvbD0iZ3JleSIsYm9yZGVyPSJ3aGl0ZSIpCiAgICAgbGluZXMoZGVuc2l0eShkdCRhZ2UuYyksbHdkPTEuNSkKaGlzdChwb3N0LmR0JGFnZSwgeGxhYj0iIiwgbWFpbj0iYWdlIC0gc2ltdWxhdGVkIiwgY29sLm1haW4gPSAiYmx1ZSIsCiAgICAgcHJvYj1UUlVFLGNvbD0iZ3JleSIsYm9yZGVyPSJ3aGl0ZSIpCiAgICAgbGluZXMoZGVuc2l0eShwb3N0LmR0JGFnZSksbHdkPTEuNSwgY29sPSJibHVlIikKCmhpc3QoZHQkYWRnLmMsIHhsYWI9IiIsIG1haW49ImFkZyAtIG9yaWdpbmFsIiwKICAgICBwcm9iPVRSVUUsY29sPSJncmV5Iixib3JkZXI9IndoaXRlIikKICAgICBsaW5lcyhkZW5zaXR5KGR0JGFkZy5jKSxsd2Q9MS41KQpoaXN0KHBvc3QuZHQkYWRnLCB4bGFiPSIiLCBtYWluPSJhZGcgLSBzaW11bGF0ZWQiLCBjb2wubWFpbiA9ICJibHVlIiwKICAgICBwcm9iPVRSVUUsY29sPSJncmV5Iixib3JkZXI9IndoaXRlIikKICAgICBsaW5lcyhkZW5zaXR5KHBvc3QuZHQkYWRnKSxsd2Q9MS41LCBjb2w9ImJsdWUiKQoKYGBgCgpTaW11bGF0ZWQgZGF0YSBsb29rcyBmYWlybHkgT0sgKHBlcmhhcHMgd29ybUNvdW50IGlzIHN1Yi1vcHRpbWFsIGFzIHRoZSBzaW11bGF0ZWQgZGF0YSBtaXNzIHRvIHJlcHJlc2VudCB0aGUgbG9uZyByaWdodCB0YWlsKSBzbyB3ZSBjYW4gcHJvY2VlZCB3aXRoIHRoZSBib290c3RyYXBwaW5nLiAgCiAKJm5ic3A7CgojIyBJdGVyYXRlIGRhdGFzZXQgc2ltdWxhdGlvbiArIGV4YWN0IHNlYXJjaCBvdmVyIGFuZCBvdmVyCgpXaGF0IHdlIHdpbGwgZG8gaXMgdG8gY3JlYXRlIGEgbG9vcCB0byAxKSBzaW11bGF0ZSBkYXRhLCAyKSBkbyBleGFjdCBzZWFyY2ggb24gc3VjaCBkYXRhLCBhbmQgMykgc3RvcmUgdGhlIGJlc3QgZml0dGluZyBEQUcgIG92ZXIgYW5kIG92ZXIgZm9yIG1hbnkgdGltZXMgKGUuZy4gMTAwMCBpdGVyYXRpb25zKS4gQm9vdHN0cmFwIGRhdGEgbmVlZCB0byBiZSBzYXZlZCBpbiBhIGZvbGRlciB0byBiZSBmdXJ0aGVyIGluc3BlY3RlZC4KCmBgYHtyIGJvb3RBQk4sIGVjaG89VFJVRSwgZXZhbD1GQUxTRX0KCiMgbG9hZCBkYXRhIApkYXRhKCJhZGciLCBwYWNrYWdlID0gImFibiIpCgpkZiA8LSBhZGcgJT4lCiAgZHBseXI6OnNlbGVjdCgtZmFybSkgJT4lCiAgYXMuZGF0YS5mcmFtZSgpCgp2YXJzIDwtIGNvbG5hbWVzKGRmKQoKIyBsb2FkIGRhdGEgZm9yIGphZ3MKc291cmNlKCJQb3N0UGFyYW1zLlIiKQoKIyBzZWxlY3QgbnIuIGJvb3RzdHJhcCBzYW1wbGVzIHRvIHJ1bgojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCnNldC5zZWVkKDQ2ODQ2KQoKbiA8LSBzYW1wbGUoMToxMDAwMDAsIDEwMDApCgoKIyBzcGVjaWZ5IG1heCBudW1iZXIgb2YgcGFyZW50cyBiYXNlZCBvbiBwcmV2aW91cyBzZWFyY2gKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KbWF4LnBhciA8LSA0CgoKIyBTaW11bGF0ZSBkYXRhIGFuZCBydW4gQUJOIG9uIHN1Y2ggZGF0YXNldAojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCmJvb3Quc2F2ZSA8LSBsaXN0KCkKCmZvciAoaSBpbiAxOmxlbmd0aChuKSkgewoKICBwcmludChwYXN0ZSgiUnVubmluZyBzaW11bGF0aW9uIiwgaSkpCgogICMgcGljayBpbml0aWFscwogIGluaXQgPC0gbGlzdCgiLlJORy5uYW1lIj0iYmFzZTo6TWVyc2VubmUtVHdpc3RlciIsICIuUk5HLnNlZWQiPW5baV0pCgogICMgcnVuIG1vZGVsCiAgamogPC0gamFncy5tb2RlbChmaWxlID0gIm1vZGVsOHZQb2lzLmJ1ZyIsCiAgICAgICAgICAgICAgICAgICBkYXRhID0gbGlzdCggICdBUi5wJz1BUi5wICwgJ3BuZXVtUy5wJz1wbmV1bVMucCAsICdmZW1hbGUucCc9ZmVtYWxlLnAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdsaXZkYW0ucCc9bGl2ZGFtLnAgLCAnZWdncy5wJz1lZ2dzLnAgLCAnd29ybUNvdW50LnAnPXdvcm1Db3VudC5wICwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ2FnZS5wJz1hZ2UucCAsJ3ByZWMuYWdlLnAnPXByZWMuYWdlLnAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdhZGcucCc9YWRnLnAgLCAncHJlYy5hZGcucCc9cHJlYy5hZGcucCksCiAgICAgICAgICAgICAgICAgICBpbml0cyA9IGluaXQsCiAgICAgICAgICAgICAgICAgICBuLmNoYWlucyA9IDEsCiAgICAgICAgICAgICAgICAgICBuLmFkYXB0ID0gNTAwKQoKICAjIHJ1biBtb3JlIGl0ZXJhdGlvbnMKICB1cGRhdGUoamosIDEwMDAwMCkKCiAgIyBzYW1wbGUgZGF0YSAoc2FtZSBzaXplIGFzIG9yaWdpbmFsOiAzNDEpIHdpdGggYSBzYW1wbGluZyBsYWcgKDIwKSB0byByZWR1Y2UgYXZvaWQgYXV0b2NvcnJlbGF0aW9uCiAgc2FtcCA8LSBjb2RhLnNhbXBsZXMoamosIGMoIkFSIiwgInBuZXVtUyIsICJmZW1hbGUiLCAibGl2ZGFtIiwgImVnZ3MiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ3b3JtQ291bnQiLCAiYWdlIiwgInByZWMuYWdlIiwgImFkZyIsICJwcmVjLmFkZyIpLAogICAgICAgICAgICAgICAgICAgICAgIG4uaXRlcj0gNjgyMCAsIHRoaW4gPTIwKQoKICAjIGJ1aWxkIGRhdGFmcmFtZSBpbiB0aGUgc2FtZSBzaGFwZSBhcyB0aGUgb3JpZ2luYWwgb25lCiAgZHQuYm9vdCA8LSBhcy5kYXRhLmZyYW1lKGFzLm1hdHJpeChzYW1wKSkgIyBwYXkgYXR0ZW50aW9uIGF0IG9yZGVyIG5hbWVzCgogIGR0LmJvb3Q8LSBkdC5ib290WywgdmFyc10KCgogICMgbm93IGNvZXJjZSB0byBmYWN0b3JzIGlmIG5lZWQgYmUgYW5kIHNldCBsZXZlbHMgLSBOT1RFUyBzZXR0aW5nIGxldmVscyB3b3JrcyBhcwogICMgIjAiICIxIiBpcyBpbiB0aGUgc2FtZSBvcmRlciBhcyAiYWJzZW50IiAicHJlc2VudCIgZnJvbSBvcmlnaW5hbCBkYXRhCiAgI2FibmRhdGEgPC0gYXMuZGF0YS5mcmFtZShhYm5kYXRhKQoKICBkdC5ib290WywxOjVdIDwtIGFzLmRhdGEuZnJhbWUobGFwcGx5KGR0LmJvb3RbLDE6NV0sIGZhY3RvcikpIAogIAogIAojIEJ1aWxkIGEgY2FjaGUgb2YgYWxsIGxvY2FsIGNvbXB1dGF0aW9ucwojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQogbXljYWNoZSA8LSBidWlsZFNjb3JlQ2FjaGUoZGF0YS5kZiA9IGR0LmJvb3QsIGRhdGEuZGlzdHMgPSBkaXN0LCBkYWcuYmFubmVkID0gYmFubmVkLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhZy5yZXRhaW5lZCA9IHJldGFpbiwgbWF4LnBhcmVudHMgPSBtYXgucGFyKQoKIyBSdW4gdGhlIEVYQUNUIFNFQVJDSAojIC0tLS0tLS0tLS0tLS0tLS0tLS0tCiBtcC5kYWcgPC0gbW9zdFByb2JhYmxlKHNjb3JlLmNhY2hlID0gbXljYWNoZSkKIGZhYm4gPC0gZml0QWJuKG9iamVjdCA9IG1wLmRhZykKCiMgU2F2ZSB0aGUgcmVzdWx0cyBvYnRhaW5lZAojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIAogI2Jvb3Quc2F2ZVtbaV1dIDwtIGxpc3QobXAuZGFnLCBmYWJuLCBkdC5ib290KSAKIAogYm9vdC5zYXZlW1tpXV0gPC0gbXAuZGFnJGRhZwoKfQoKc2F2ZShib290LnNhdmUsIGZpbGUgPSBzcHJpbnRmKCdCb290RGF0YS9kdC5ib290LlJEYXRhJykpCgpgYGAKCiZuYnNwOwoKIyMgRmluZCB0aGUgZmluYWwgcHJ1bmVkIERBRwoKRmlyc3Qgd2UgbmVlZCB0byBsb2FkIGFsbCB0aGUgYm9vdHN0cmFwIERBR3MuCgpgYGB7ciBsb2FkQm9vdCwgZWNobz1UUlVFLCBldmFsPVRSVUV9CgojIHNldCBuciBib290c3RyYXAgc2FtcGxlcwojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQpuIDwtIDEwMDAKCiMgbG9hZCBkYWdzIGFuZCBib29zdHJhcCBkYXRhCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCmRhZ3MgPC0gbGlzdCgpCmJvb3QgPC0gbGlzdCgpCgpsb2FkKGZpbGUgPSAnQm9vdERhdGEvZHQuYm9vdC5SRGF0YScpCgpmb3IoaSBpbiAxOm4pewpkYWdzW1tpXV0gPC0gYm9vdC5zYXZlW1tpXV0KCn0KCgpgYGAKCgombmJzcDsKClRoZW4gd2Ugd2lsbCBjaGVjayB3aGF0IHdhcyB0aGUgbW9zdCBmcmVxdWVudCBudW1iZXIgb2YgYXJjczoKCmBgYHtyIGNoZWNrQm9vdCwgZWNobz1UUlVFfQoKIyBjb3VudCB0b3RhbCBudW1iZXIgb2YgYXJjcyBpbiBlYWNoIGRhZwojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCmFyY3MgPC0gc2FwcGx5KGRhZ3MsIHN1bSkKYmFycGxvdCh0YWJsZShhcmNzKSkKCmBgYAoKRm9yIGNvbXBhcmlzb24sIGluIHRoZSBvcmlnaW5hbCBEQUcgdGhlcmUgd2VyZSBgciBzdW0obXlkYWckZGFnKWAgYXJjcywgc28gaXQgc2VlbXMgdGhhdCB0aGVyZSBtaWdodCBoYXZlIGJlZW4gc29tZSBvdmVyZml0dGluZy4KCiZuYnNwOwoKRmluYWxseSwgd2Ugd2lsbCBjb3VudCBob3cgbWFueSB0aW1lcyAqKmVhY2ggYXJjKiogYXBwZWFycyBpbiB0aGUgYm9vdHN0cmFwcGVkIGRhdGEuIFRoZSBmaW5hbCAqKnBydW5lZCBEQUcqKiB3aWxsIGluY2x1ZGUgb25seSBhcmNzIHByZXNlbnQgaW4gYXQgbGVhc3QgNTAlIG9mIGJvb3RzdHJhcCBzYW1wbGVzIChlLmcuIDUwMC8xMDAwIGluIG91ciBjYXNlKS4KCgpgYGB7ciBwcnVuZWQsIGVjaG89VFJVRX0KCiMgQ291bnQgaG93IG1hbnkgdGltZXMgZWFjaCBhcmMgYXBwZWFyIGluIHRoZSBib290c3RyYXAgZGF0YQojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyBmdW5jdGlvbiBSZWR1Y2Ugc3VtcyAoIisiKSBlYWNoIGVsZW1lbnQgb2YgZWFjaCBtYXRyaXggaW4gdGhlIGxpc3QgYW5kIHN0b3JlIAojIHJlc3VsdHMgaW4gYSBuZXcgbWF0cml4IG9mIHNhbWUgc2l6ZQoKYWxsZGFnIDwtIFJlZHVjZSgiKyIsIGRhZ3MpICAKCiMgZXhwcmVzcyBpdCBpbiBwZXJjZW50YWdlCnBlcmRhZyA8LSBhbGxkYWcvbGVuZ3RoKGRhZ3MpCgoKIyBLZWVwIG9ubHkgYXJjcyB0aGF0IGFwcGVhcnMgaW4gYXQgbGVhc3QgNTAlIG9mIHNhbXBsZXMKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KdHJpbS5kYWcgPC0gKGFsbGRhZyA+PShuKjAuNSkpKjEKCiMgU2VuZCAgZmluYWwgcHJ1bmVkIERBRyB0byBHcmFwaHZpcyBmb3IgdmlzdWFsaXphdGlvbiAKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQp0b0dyYXBodml6KGRhZyA9IHRyaW0uZGFnLCBkYXRhLmRmID0gYWJuZGF0YSwgZGF0YS5kaXN0cyA9IGRpc3QsCiAgICAgICAgICAgb3V0ZmlsZSA9IHBhc3RlMCgiVHJpbURBRyIsbiwiLmRvdCIpKQoKYGBgCgogIApUaGlzIGlzIHRoZSBwZXJjZW50YWdlIG9mIGFyY3MgcmV0cmlldmVkIHdpdGhpbiB0aGUgYm9vdHN0cmFwIHNhbXBsZToKCmBgYHtyIHBlcmMsIGVjaG89RkFMU0V9Cgpyb3VuZChwZXJkYWcqMTAwLDApCgpgYGAKCgpUaGlzIGlzIGhvdyB0aGUgZmluYWwgKipwcnVuZWQgREFHKiogbG9va3MgbGlrZToKCmBgYHtyIHByREFHc2hvd30KCnBsb3RBYm4oZGFnID0gdHJpbS5kYWcsIGRhdGEuZGlzdHMgPSBkaXN0KQoKYGBgCgombmJzcDsKCk5vdyB0aGF0IHdlIGhhdmUgYSByb2J1c3QgbW9kZWwgKGVuY29kZWQgYnkgdGhlIHBydW5lZCBEQUcpLCB3ZSBjYW4gZXh0cmFjdCB0aGUgcGFyYW1ldGVycyB0byAoYSkgYXBwcmVjaWF0ZSB0aGUgbWFnbml0dWRlIChhbmQgcHJlY2lzaW9uKSBvZiB0aGUgdGhlIGFzc29jaWF0aW9ucyBhbmQgKGIpIGZ1cnRoZXIgcmVmaW5lIHRoZSBEQUcgaW5jbHVkaW5nIHdoZXRoZXIgdGhlIGFzc29jaWF0aW9ucyBhcmUgcG9zaXRpdmUgb3IgbmVnYXRpdmUuIAoKJm5ic3A7CgojIEV4dHJhY3QgbWFyZ2luYWwgcG9zdGVyaW9yIGRlbnNpdHkgZm9yIGVhY2ggcGFyYW1ldGVyCgpUaGUgbWFyZ2luYWwgcG9zdGVyaW9yIGRlbnNpdGllcyAobWFyZ2luYWxzKSByZXByZXNlbnQgdGhlIGRlbnNpdHkgZGlzdHJpYnV0aW9uIG9mIHRoZSBwYXJhbWV0ZXJzIGF0IGVhY2ggYXJjLgoKYGBge3IgbXBkLCBlY2hvPVRSVUV9CgptYXJnLmYgPC0gZml0QWJuKGRhZyA9IHRyaW0uZGFnLCBkYXRhLmRmID0gYXMuZGF0YS5mcmFtZShhYm5kYXRhKSwKICAgICAgICAgICAgICAgICBkYXRhLmRpc3RzID0gZGlzdCwgY29tcHV0ZS5maXhlZD1UUlVFLCBuLmdyaWQ9MTAwMCkKCmBgYAoKJm5ic3A7CgojIyBWaXN1YWxseSBpbnNwZWN0IHRoZSBtYXJnaW5hbCBwb3N0ZXJpb3IgZGlzdHJpYnV0aW9ucyBvZiB0aGUgcGFyYW1ldGVycwpgYGB7ciBtcGRfcGxvdCwgZWNobz1UUlVFLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9Mn0KCnBhcihtZnJvdz1jKDEsNCksIG1hcj1jKDIsMiwxLjUsMSkpCmZvcihpIGluIDE6bGVuZ3RoKG1hcmcuZiRtYXJnaW5hbHMpKXsKCiMgZ2V0IHRoZSBtYXJnaW5hbCBmb3IgY3VycmVudCBub2RlLCB3aGljaCBpcyBhIG1hdHJpeCBbeCwgZih4KV0KICBjdXIubm9kZSA8LSBtYXJnLmYkbWFyZ2luYWxzW2ldCiAgbm9tMSA8LSBuYW1lcyhtYXJnLmYkbWFyZ2luYWxzKVtpXQoKIyBwaWNrIHRoZSBmaXJzdCB2YWx1ZSAoZm9yIG1vZGVscyB3b3Rob3V0IHJhbmRvbSBlZmZlY3RzKQogIGN1ci5ub2RlIDwtIGN1ci5ub2RlW1sxXV0KICBmb3IoaiBpbiAxOmxlbmd0aChjdXIubm9kZSkgKSB7CiAgICBub20yPC1uYW1lcyhjdXIubm9kZSlbal0KICAgIGN1ci5wYXJhbSA8LSBjdXIubm9kZVtbal1dCiAgICBwbG90KGN1ci5wYXJhbSx0eXBlPSJsIixtYWluPXBhc3RlKG5vbTEsICI6Iiwgbm9tMiksIGNleD0wLjcpCiAgfQp9CgpgYGAKCgombmJzcDsKCiMgR2V0IHRoZSB0YWJsZSBvZiBxdWFudGlsZXMgZm9yIHRoZSBtYXJnaW5hbHMKYGBge3IgbXBkX3RhYiwgZWNobz1UUlVFfQoKIyBleHRyYWN0IG1hcmdpbmFscyBhZGp1c3RlZCBmb3IgZ3JvdXBlZCBkYXRhCm1hcmcuZGlzdHMgPC0gbWFyZy5mJG1hcmdpbmFsc1tbMV1dCmZvciAoaSBpbiAyOmxlbmd0aChtYXJnLmYkbWFyZ2luYWxzKSkgewogIG1hcmcuZGlzdHMgPC0gYyhtYXJnLmRpc3RzLCBtYXJnLmYkbWFyZ2luYWxzW1tpXV0pCn0KCm1hdCA8LSBtYXRyaXgocmVwKE5BLCBsZW5ndGgobWFyZy5kaXN0cykqMyksIG5jb2w9MykKcm93bmFtZXMobWF0KSA8LSBuYW1lcyhtYXJnLmRpc3RzKQpjb2xuYW1lcyhtYXQpIDwtIGMoIjIuNSUiLCAiNTAlIiwgIjk3LjUlIikKaWdub3JlLm1lIDwtIHVuaW9uKGdyZXAoIlxcKEludCIsIG5hbWVzKG1hcmcuZGlzdHMpKSwgZ3JlcCgicHJlYyIsIG5hbWVzKG1hcmcuZGlzdHMpKSkgIyB0YWtlIGF3YXkgYmFja2dyb3VuZCBrIGFuZCBwcmVjaXNpb24KY29tbWVudCA8LSByZXAoIiIsIGxlbmd0aChtYXJnLmRpc3RzKSkKZm9yIChpIGluIDE6bGVuZ3RoKG1hcmcuZGlzdHMpKSB7CiAgdG1wIDwtIG1hcmcuZGlzdHNbW2ldXQogIHRtcDIgPC0gY3Vtc3VtKHRtcFssMl0pL3N1bSh0bXBbLDJdKQogIG1hdFtpLCBdIDwtYyh0bXBbd2hpY2godG1wMj4wLjAyNSlbMV0sMV0sIyMgLTEgaXMgc28gdXNlIHZhbHVlIG9uIHRoZSBsZWZ0IG9mIHRoZSAyLjUlCiAgICAgICAgICAgICAgIHRtcFt3aGljaCh0bXAyPjAuNSlbMV0sMV0sCiAgICAgICAgICAgICAgIHRtcFt3aGljaCh0bXAyPjAuOTc1KVsxXSwxXSkKICB2ZWMgPC0gbWF0W2ksXQoKICBpZiggIShpJWluJWlnbm9yZS5tZSkgJiYgICh2ZWNbMV08MCAmJiB2ZWNbM10+MCkpe2NvbW1lbnRbaV08LSJub3Qgc2lnLiBhdCA1JSJ9CgogICMjIHRydW5jYXRlIGZvciBwcmludGluZwogIG1hdFtpLF08LWFzLm51bWVyaWMoZm9ybWF0QyhtYXRbaSxdLGRpZ2l0cz0zLGZvcm1hdD0iZiIpKQp9Cgprbml0cjo6a2FibGUoY2JpbmQobWF0KSwgcm93Lm5hbWVzID0gVFJVRSwgZGlnaXRzID0gMywgYWxpZ24gPSAicnJyciIsICJodG1sIikgJT4lCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9ICJzdHJpcGVkIiwgZnVsbF93aWR0aCA9IEYsIHBvc2l0aW9uID0gImxlZnQiKSAlPiUKICBjb2x1bW5fc3BlYygzLCBib2xkID0gVFJVRSkKCgpgYGAKCkFzIHNhaWQsIHRoZSBtYXJnaW5hbHMgcmVwcmVzZW50IGVzdGltYXRlcyBvZiB0aGUgcGFyYW1ldGVycyBhdCBlYWNoIG5vZGUgKGkuZS4gdGhlIGFyY3MgaW4gdGhlIERBRykuIEJlaW5nIHRoZSB2YXJpYWJsZXMgb2YgZGlmZmVyZW50IG5hdHVyZSwgdGhleSBoYXZlIGRpZmZlcmVudCBtZWFuaW5nLiBNYXJnaW5hbHMgcmVwcmVzZW50ICoqY29ycmVsYXRpb24gY29lZmZpY2llbnRzKiogZm9yIEdhdXNzaWFuIG5vZGVzICh3aGVuIHRoZSBkZWZhdWx0ICpjZW50ZXJpbmcqIG9mIHZhcmlhYmxlcyBpcyBrZXB0KSwgKipsb2cgcmF0ZSByYXRpb3MqKiBmb3IgUG9pc3NvbiBub2RlcywgYW5kICoqbG9nIG9kZHMgcmF0aW9zKiogZm9yIGJpbm9taWFsIG5vZGVzLiBUaGVyZWZvcmUsIHRoZSBzZWNvbmQgYW5kIHRoZSBsYXR0ZXIgbmVlZCB0byBiZSBleHBvbmVudGlhdGVkLCB0byBnZXQgdGhlIG9kZHMgcmF0aW9zIG9yIHJhdGUgcmF0aW9zIHJlc3BlY3RpdmVseS4KCmBgYHtyIHBhcmFtZXRlcnMsIGVjaG89RkFMU0UsIGNhY2hlPUZBTFNFfQptYXI8LSBkYXRhLmZyYW1lKGNiaW5kKG1hdCkpCm5hbWVzKG1hcikgPC0gYygiMi41USIsICJtZWRpYW4iLCAiOTcuNVEiKQoKbWFyPC0gbWFyWy1pZ25vcmUubWUsXQptYXJbMTozLF08LWV4cChtYXJbMTozLF0pCm1hciRJbnRlcnByZXRhdGlvbiA8LSBjKHJlcCgib2RkcyByYXRpbyIsMyksCiAgICAgICAgICAgICAgICAgICAgICAgIHJlcCgicmF0ZSByYXRpbyIsIDQpLAogICAgICAgICAgICAgICAgICAgICAgICByZXAoImNvcnJlbGF0aW9uIiwgMikpCgoKa25pdHI6OmthYmxlKG1hciwgcm93Lm5hbWVzID0gVFJVRSwgZGlnaXRzID0gMywgYWxpZ24gPSAicnJyIiwgImh0bWwiKSAlPiUKICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gInN0cmlwZWQiLCBmdWxsX3dpZHRoID0gRkFMU0UsIHBvc2l0aW9uID0gImxlZnQiKQoKYGBgCgombmJzcDsKCiMgUHJlc2VudCBmaW5hbCByZXN1bHRzCkFzIGEgZmluYWwgc3RlcCB3ZSB3aWxsIHJlZmluZSB0aGUgREFHIGJ5IChhKSBjaGFuZ2luZyB0aGUgc3R5bGUgb2YgdGhlIGFycm93cyBhY2NvcmRpbmcgdG8gdGhlIHR5cGUgb2YgYXNzb2NpYXRpb24gKGkuZS4gKnNvbGlkKiBhcnJvd3MgZm9yIHBvc2l0aXZlIGFzc29jaWF0aW9uIGFuZCAqZGFzaGVkKiBhcnJvd3MgZm9yIG5lZ2F0aXZlIGFzc29jaWF0aW9uKSwgYW5kIChiKSBjaGFuZ2luZyB0aGUgdGhpY2tuZXNzIG9mIHRoZSBhcnJvd3MgYWNjb3JkaW5nIHRvIHRoZWlyIGxpbmsgc3RyZW5ndGguIFRoZXNlIHN0ZXBzIGFyZSBkb25lIGluIEdyYXBodml6LCB1c2luZyB0aGUgKipET1QgbGFuZ3VhZ2UqKi4gRGV0YWlscyBjYW4gYmUgZm91bmQgYXQKaHR0cHM6Ly93d3cuZ3JhcGh2aXoub3JnL2RvYy9pbmZvL2F0dHJzLmh0bWwuCgombmJzcDsKCjxzdHlsZT4KICAuY29sMiB7CiAgICBjb2x1bW5zOiAyIDIwMHB4OyAgICAgICAgIC8qIG51bWJlciBvZiBjb2x1bW5zIGFuZCB3aWR0aCBpbiBwaXhlbHMqLwogICAgLXdlYmtpdC1jb2x1bW5zOiAyIDIwMHB4OyAvKiBjaHJvbWUsIHNhZmFyaSAqLwogICAgLW1vei1jb2x1bW5zOiAyIDIwMHB4OyAgICAvKiBmaXJlZm94ICovCiAgfQo8L3N0eWxlPgoKPGRpdiBjbGFzcz0iY29sMiI+CgpgYGB7ciBmaW5hbCwgZWNobz1GQUxTRSwgY2FjaGU9RkFMU0V9CgppbmNsdWRlX2dyYXBoaWNzKCJmaW5hbC1ncmFwaC5wbmciKQoKa25pdHI6OmthYmxlKG1hciwgcm93Lm5hbWVzID0gVFJVRSwgZGlnaXRzID0gMywgYWxpZ24gPSAicnJyciIsICJodG1sIikgJT4lCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9ICJzdHJpcGVkIiwgZnVsbF93aWR0aCA9IEZBTFNFLCBwb3NpdGlvbiA9ICJsZWZ0IikgJT4lCiAgY29sdW1uX3NwZWMoMywgYm9sZCA9IFRSVUUpCgoKYGBgCjwvZGl2PgoKRm9yIGFuIGV4YW1wbGUgaW4gYSByZWNlbnQgcGVlciByZXZpZXdlZCBwdWJsaWNhdGlvbiBzZWUgW0Bjb21pbjIwMTlyZXZlYWxpbmddIG9yIFtAa3JhdHplcjIwMjBiYXllc2lhbl0KCiMgUmVmZXJlbmNlcwoK