Setup

Load packages

library(cmdstanr)
# If running in Aalto JupyterHub the path should automatically be set to
# '/coursedata/cmdstan'
options(mc.cores = 1)
library(posterior)
options(posterior.num_args=list(sigfig=2)) # by default summaries with 2 significant digits
library(loo)
library(tidyr)
library(dplyr)
options(pillar.neg=FALSE)
library(ggplot2)
library(gridExtra)
library(bayesplot)
library(ggdist)
theme_set(bayesplot::theme_default(base_family = "sans"))
library(rprojroot)
root<-has_file(".BDA_R_demos_root")$make_fix_file()
SEED <- 48927 # set random seed for reproducability

1 Introduction

This notebook contains several examples of how to use Stan in R with cmdstanr. This notebook assumes basic knowledge of Bayesian inference and MCMC. The Stan models are stored in separate .stan-files. The examples are related to Bayesian data analysis course.

2 Bernoulli model

Toy data with sequence of failures (0) and successes (1). We would like to learn about the unknown probability of success.

data_bern <- list(N = 10, y = c(1, 1, 1, 0, 1, 1, 1, 0, 1, 0))

Bernoulli model with a proper Beta(1,1) (uniform) prior

code_bern <- root("demos_rstan", "bern.stan")
writeLines(readLines(code_bern))
// Bernoulli model
data {
  int<lower=0> N; // number of observations
  array[N] int<lower=0, upper=1> y; // vector of binary observations
}
parameters {
  real<lower=0, upper=1> theta; // probability of success
}
model {
  // model block creates the log density to be sampled
  theta ~ beta(1, 1); // prior
  y ~ bernoulli(theta); // observation model / likelihood
  // the notation using ~ is syntactic sugar for
  //  target += beta_lpdf(theta | 1, 1);   // lpdf for continuous theta
  // target += bernoulli_lpmf(y | theta); // lpmf for discrete y
  // target is the log density to be sampled
  //
  // y is an array of integers and
  //  y ~ bernoulli(theta);
  // is equivalent to
  //  for (i in 1:N) {
  //    y[i] ~ bernoulli(theta);
  //  }
  // which is equivalent to
  //  for (i in 1:N) {
  //    target += bernoulli_lpmf(y[i] | theta);
  //  }
}

Sample form the posterior and show the summary

mod_bern <- cmdstan_model(stan_file = code_bern)
fit_bern <- mod_bern$sample(data = data_bern, seed = SEED, refresh=1000)

fit_bern$summary()

Plot a histogram of the posterior draws with bayesplot (uses ggplot)

draws <- fit_bern$draws(format = "df")
mcmc_hist(draws, pars='theta') + xlim(c(0,1))

Plot a dots plot of the posterior draws with ggplot + ggdist

draws |>
  ggplot(aes(x=theta)) + 
  stat_dotsinterval() + 
  xlim(c(0,1))

3 Binomial model

Instead of sequence of 0’s and 1’s, we can summarize the data with the number of experiments and the number successes:

data_bin <- list(N = 10, y = 7)

And then we use Binomial model with Beta(1,1) prior for the probability of success.

code_binom <- root("demos_rstan","binom.stan")
writeLines(readLines(code_binom))
// Binomial model with beta(1,1) prior
data {
  int<lower=0> N; // number of experiments
  int<lower=0> y; // number of successes
}
parameters {
  real<lower=0, upper=1> theta; // probability of success in range (0,1)
}
model {
  // model block creates the log density to be sampled
  theta ~ beta(1, 1); // prior
  y ~ binomial(N, theta); // observation model / likelihood
  // the notation using ~ is syntactic sugar for
  //  target += beta_lpdf(theta | 1, 1);     // lpdf for continuous theta
  //  target += binomial_lpmf(y | N, theta); // lpmf for discrete y
  // target is the log density to be sampled
}

Sample from the posterior and plot the posterior. The histogram should look similar as in the Bernoulli case.

mod_bin <- cmdstan_model(stan_file = code_binom)
fit_bin <- mod_bin$sample(data = data_bin, seed = SEED, refresh=1000)

fit_bin$summary()

draws <- fit_bin$draws(format = "df")
mcmc_hist(draws, pars = 'theta') + xlim(c(0,1))

Re-run the model with a new data. The compiled Stan program is re-used making the re-use faster.

data_bin <- list(N = 100, y = 70)
fit_bin <- mod_bin$sample(data = data_bin, seed = SEED, refresh=1000)

fit_bin$summary()

draws <- fit_bin$draws(format = "df")
mcmc_hist(draws, pars = 'theta', binwidth = 0.01) + xlim(c(0,1))

3.1 Explicit transformation of variables

In the above examples the probability of success \(\theta\) was declared as

real<lower=0,upper=1> theta;

Stan makes automatic transformation of the variable to the unconstrained space using logit transofrmation for interval constrained and log transformation for half constraints.

The following example shows how we can also make an explicit transformation and use binomial_logit function which takes the unconstrained parameter as an argument and uses logit transformation internally. This form can be useful for better numerical stability.

code_binomb <- root("demos_rstan", "binomb.stan")
writeLines(readLines(code_binomb))
// Binomial model with a roughly uniform prior for
// the probability of success (theta)
data {
  int<lower=0> N; // number of experiments
  int<lower=0> y; // number of successes
}
parameters {
  // sampling is done for the parameters
  real alpha; // logit of probability of success in rang (-Inf,Inf)
}
transformed parameters {
  // transformed parameters are deterministic transformations of parameters (and data)
  real theta = inv_logit(alpha); // probability of success in range (0,1)
}
model {
  // model block creates the log density to be sampled
  alpha ~ normal(0, 1.5); // roughly uniform prior for theta
  y ~ binomial_logit(N, alpha); // model parameterized with logit of probability
  // the notation using ~ is syntactic sugar for
  //  target += normal_lpdf(alpha | 0, 1.5);       // lpdf for continuous theta
  //  target += binomial_logit_lpmf(y | N, alpha); // lpmf for discrete y
  // target is the log density to be sampled
}

Here we have used Gaussian prior in the unconstrained space, which produces close to uniform prior for theta.

Sample from the posterior and plot the posterior. The histogram should look similar as with the previous models.

data_bin <- list(N = 100, y = 70)
mod_binb <- cmdstan_model(stan_file = code_binomb)
fit_binb <- mod_bin$sample(data = data_bin, seed = SEED, refresh=1000)

fit_binb$summary()

draws <- fit_binb$draws(format = "df")
mcmc_hist(draws, pars = 'theta', binwidth = 0.01) + xlim(c(0,1))

4 Comparison of two groups with Binomial

An experiment was performed to estimate the effect of beta-blockers on mortality of cardiac patients. A group of patients were randomly assigned to treatment and control groups:

  • out of 674 patients receiving the control, 39 died
  • out of 680 receiving the treatment, 22 died

Data:

data_bin2 <- list(N1 = 674, y1 = 39, N2 = 680, y2 = 22)

To analyse whether the treatment is useful, we can use Binomial model for both groups and compute odds-ratio:

code_binom2 <- root("demos_rstan", "binom2.stan")
writeLines(readLines(code_binom2))
//  Comparison of two groups with Binomial
data {
  int<lower=0> N1; // number of experiments in group 1
  int<lower=0> y1; // number of deaths in group 1
  int<lower=0> N2; // number of experiments in group 2
  int<lower=0> y2; // number of deaths in group 2
}
parameters {
  real<lower=0, upper=1> theta1; // probability of death in group 1
  real<lower=0, upper=1> theta2; // probability of death in group 2
}
model {
  // model block creates the log density to be sampled
  theta1 ~ beta(1, 1); // prior
  theta2 ~ beta(1, 1); // prior
  y1 ~ binomial(N1, theta1); // observation model / likelihood
  y2 ~ binomial(N2, theta2); // observation model / likelihood
  // the notation using ~ is syntactic sugar for
  //  target += beta_lpdf(theta1 | 1, 1);       // lpdf for continuous theta1
  //  target += beta_lpdf(theta2 | 1, 1);       // lpdf for continuous theta2
  //  target += binomial_lpmf(y1 | N1, theta1); // lpmf for discrete y1
  //  target += binomial_lpmf(y2 | N2, theta2); // lpmf for discrete y2
  // target is the log density to be sampled
}
generated quantities {
  // generated quantities are computed after sampling
  real oddsratio = (theta2 / (1 - theta2)) / (theta1 / (1 - theta1));
}

Sample from the posterior and plot the posterior

mod_bin2 <- cmdstan_model(stan_file = code_binom2)
fit_bin2 <- mod_bin2$sample(data = data_bin2, seed = SEED, refresh=1000)

fit_bin2$summary()

Histogram

draws <- fit_bin2$draws(format = "df")
mcmc_hist(draws, pars = 'oddsratio') +
  geom_vline(xintercept = 1) +
  scale_x_continuous(breaks = c(seq(0.25,1.5,by=0.25)))

Dots plot with median and 66% and 95% intervals

draws |>
  ggplot(aes(x=oddsratio)) + 
    geom_dotsinterval() + 
    geom_vline(xintercept = 1) +
    scale_x_continuous(breaks = c(seq(0.25,1.5,by=0.25)))+
    labs(x='Odds ratio', y='')

Probability (and corresponding MCSE) that oddsratio<1

draws |>
  mutate_variables(p_oddsratio_lt_1 = as.numeric(oddsratio<1)) |>
  subset_draws("p_oddsratio_lt_1") |>
  summarise_draws(prob=mean, MCSE=mcse_mean)
# A tibble: 1 × 3
  variable            prob    MCSE
  <chr>            <num:2> <num:2>
1 p_oddsratio_lt_1    0.99  0.0023

5 Linear Gaussian model

The following file has Kilpisjärvi summer month temperatures 1952-2022 (data by Finnish Meteorological Institute, CC-BY 4.0).

data_kilpis <- read.delim(root("demos_rstan","kilpisjarvi-summer-temp-2022.csv"), sep = ";")
data_lin <-list(N = nrow(data_kilpis),
             x = data_kilpis$year,
             xpred = 2016,
             y = data_kilpis[,5])

Plot the data

ggplot() +
  geom_point(aes(x, y), data = data.frame(data_lin), size = 1) +
  labs(y = 'Summer temp. @Kilpisjärvi', x= "Year") +
  guides(linetype = "none")

To analyse whether the average summer month temperature is rising, we use a linear model with Gaussian model for the unexplained variation.

5.1 Gaussian linear model with adjustable priors

The folloing Stan code allows also setting hyperparameter values as data allowing easier way to use different priors in different analyses:

code_lin <- root("demos_rstan", "lin.stan")
writeLines(readLines(code_lin))
// Gaussian linear model with adjustable priors
data {
  int<lower=0> N; // number of data points
  vector[N] x; // covariate / predictor
  vector[N] y; // target
  real xpred; // new covariate value to make predictions
  real pmualpha; // prior mean for alpha
  real psalpha; // prior std for alpha
  real pmubeta; // prior mean for beta
  real psbeta; // prior std for beta
  real pssigma; // prior std for half-normal prior for sigma
}
parameters {
  real alpha; // intercept
  real beta; // slope
  real<lower=0> sigma; // standard deviation is constrained to be positive
}
transformed parameters {
  // deterministic transformation of parameters and data
  vector[N] mu = alpha + beta * x; // linear model
}
model {
  alpha ~ normal(pmualpha, psalpha); // prior
  beta ~ normal(pmubeta, psbeta); // prior
  sigma ~ normal(0, pssigma); // as sigma is constrained to be positive,
  // this is same as half-normal prior
  y ~ normal(mu, sigma); // observation model / likelihood
  // the notation using ~ is syntactic sugar for
  //  target += normal_lpdf(alpha | pmualpha, psalpha);
  //  target += normal_lpdf(beta | pmubeta, psbeta);
  //  target += normal_lpdf(y | mu, sigma);
  // target is the log density to be sampled
}
generated quantities {
  // sample from the predictive distribution
  real ypred = normal_rng(alpha + beta * xpred, sigma);
  // compute log predictive densities to be used for LOO-CV
  vector[N] log_lik;
  for (i in 1 : N) {
    log_lik[i] = normal_lpdf(y[i] | mu[i], sigma);
  }
}

Create another list with data and priors

data_lin_priors <- c(list(
    pmualpha = mean(unlist(data_kilpis[,5])), # centered
    psalpha = 100, # weakly informative
    pmubeta = 0, # a priori incr. and decr. as likely
    psbeta = (.1--.1)/6, # avg temp prob does does not incr. more than a degree per 10 years
    pssigma = 1), # total variation in summer average temperatures is less +-3 degrees
  data_lin)

Run Stan

mod_lin <- cmdstan_model(stan_file = code_lin)
fit_lin <- mod_lin$sample(data = data_lin_priors, seed = SEED, refresh=1000)

Stan gives a warning: There were X transitions after warmup that exceeded the maximum treedepth.

We can check the generic convergence diagnostics as follows

fit_lin$summary()
# A tibble: 147 × 10
   variable    mean  median      sd     mad      q5     q95    rhat ess_bulk
   <chr>    <num:2> <num:2> <num:2> <num:2> <num:2> <num:2> <num:2>  <num:2>
 1 lp__     -41.    -41.     1.3     1.0    -44.    -40.        1.0    1004.
 2 alpha    -32.    -32.    12.     12.     -53.    -12.        1.0     919.
 3 beta       0.021   0.021  0.0062  0.0061   0.011   0.031     1.0     919.
 4 sigma      1.1     1.1    0.095   0.097    0.93    1.2       1.0    1342.
 5 mu[1]      8.7     8.7    0.25    0.26     8.3     9.1       1.0    1131.
 6 mu[2]      8.7     8.7    0.25    0.25     8.3     9.1       1.0    1143.
 7 mu[3]      8.7     8.7    0.24    0.24     8.3     9.1       1.0    1156.
 8 mu[4]      8.8     8.8    0.24    0.24     8.4     9.1       1.0    1168.
 9 mu[5]      8.8     8.8    0.23    0.23     8.4     9.2       1.0    1181.
10 mu[6]      8.8     8.8    0.23    0.23     8.4     9.2       1.0    1195.
# ℹ 137 more rows
# ℹ 1 more variable: ess_tail <num:2>

We can check the HMC specific diagnostics as follows

fit_lin$diagnostic_summary(diagnostics = c("divergences", "treedepth"))
Warning: 1 of 4000 (0.0%) transitions hit the maximum treedepth limit of 10.
See https://mc-stan.org/misc/warnings for details.
$num_divergent
[1] 0 0 0 0

$num_max_treedepth
[1] 0 0 1 0

The high number of max treedepth exceedence is due to high posterior dependency, which in this case reduces efficiency, but doesn’t invalidate the results

draws_lin <- fit_lin$draws(format = "df")
draws_lin |>
  mcmc_scatter(pars=c("alpha","beta"))

Compute the probability that the summer temperature is increasing.

mean(draws_lin[,"beta"]>0) # probability that beta > 0
[1] 0.99975

Plot the data, the model fit and prediction for year 2016.

mu <- draws_lin |>
  as_draws_df() |>
  as_tibble() |>
  select(starts_with("mu")) |>
  apply(2, quantile, c(0.05, 0.5, 0.95)) |>
  t() %>%
  data.frame(x = data_lin$x, .)  |> 
  gather(pct, y, -x)

pfit <- ggplot() +
  geom_point(aes(x, y), data = data.frame(data_lin), size = 1) +
  geom_line(aes(x, y, linetype = pct), data = mu, color = 'red') +
  scale_linetype_manual(values = c(2,1,2)) +
  labs(y = 'Summer temp. @Kilpisjärvi', x= "Year") +
  guides(linetype = "none")
phist <- mcmc_hist(draws_lin, pars = c('beta','sigma','ypred'))
grid.arrange(pfit, phist, nrow = 2)

5.2 Gaussian linear model with standardized data

In the above we used the unnormalized data and as x values are far away from zero, this will cause very strong posterior dependency between alpha and beta (did you use ShinyStan for the above model?). The strong posterior dependency can be removed by normalizing the data to have zero mean. The following Stan code makes it in Stan. In generated quantities we do correspnding transformation back to the original scale.

code_lin_std <- root("demos_rstan", "lin_std.stan")
writeLines(readLines(code_lin_std))
// Gaussian linear model with standardized data
data {
  int<lower=0> N; // number of data points
  vector[N] x; // covariate / predictor
  vector[N] y; // target
  real xpred; // new covariate value to make predictions
}
transformed data {
  // deterministic transformations of data
  vector[N] x_std = (x - mean(x)) / sd(x);
  vector[N] y_std = (y - mean(y)) / sd(y);
  real xpred_std = (xpred - mean(x)) / sd(x);
}
parameters {
  real alpha; // intercept
  real beta; // slope
  real<lower=0> sigma_std; // standard deviation is constrained to be positive
}
transformed parameters {
  // deterministic transformation of parameters and data
  vector[N] mu_std = alpha + beta * x_std; // linear model
}
model {
  alpha ~ normal(0, 1); // weakly informative prior (given standardized data)
  beta ~ normal(0, 1); // weakly informative prior (given standardized data)
  sigma_std ~ normal(0, 1); // weakly informative prior (given standardized data)
  y_std ~ normal(mu_std, sigma_std); // observation model / likelihood
}
generated quantities {
  // transform to the original data scale
  vector[N] mu = mu_std * sd(y) + mean(y);
  real<lower=0> sigma = sigma_std * sd(y);
  // sample from the predictive distribution
  real ypred = normal_rng((alpha + beta * xpred_std) * sd(y) + mean(y),
                          sigma_std * sd(y));
  // compute log predictive densities to be used for LOO-CV
  // to make appropriate comparison to other models, this log density is computed
  // using the original data scale (y, mu, sigma)
  vector[N] log_lik;
  for (i in 1 : N) {
    log_lik[i] = normal_lpdf(y[i] | mu[i], sigma);
  }
}
mod_lin_std <- cmdstan_model(stan_file = code_lin_std)
fit_lin_std <- mod_lin_std$sample(data = data_lin, seed = SEED, refresh=1000)

Now there were no warnings. We can check diagnostics with the following commands.

fit_lin_std$summary()
# A tibble: 219 × 10
   variable      mean   median    sd   mad     q5    q95  rhat ess_bulk ess_tail
   <chr>      <num:2>  <num:2> <num> <num> <num:> <num:> <num>  <num:2>  <num:2>
 1 lp__      -31.      -3.1e+1 1.3   1.0   -34.   -30.     1.0    1893.    2281.
 2 alpha       0.0012   8.3e-4 0.11  0.11   -0.18   0.18   1.0    3872.    2616.
 3 beta        0.39     3.9e-1 0.11  0.11    0.21   0.57   1.0    3770.    2396.
 4 sigma_std   0.93     9.3e-1 0.081 0.080   0.81   1.1    1.0    3544.    2711.
 5 mu_std[1]  -0.66    -6.6e-1 0.22  0.21   -1.0   -0.31   1.0    3642.    2228.
 6 mu_std[2]  -0.64    -6.4e-1 0.21  0.21   -0.99  -0.30   1.0    3645.    2229.
 7 mu_std[3]  -0.62    -6.3e-1 0.21  0.20   -0.97  -0.29   1.0    3645.    2249.
 8 mu_std[4]  -0.60    -6.1e-1 0.20  0.20   -0.94  -0.27   1.0    3644.    2263.
 9 mu_std[5]  -0.59    -5.9e-1 0.20  0.19   -0.91  -0.26   1.0    3650.    2263.
10 mu_std[6]  -0.57    -5.7e-1 0.19  0.19   -0.89  -0.25   1.0    3650.    2333.
# ℹ 209 more rows
fit_lin_std$diagnostic_summary(diagnostics = c("divergences", "treedepth"))
$num_divergent
[1] 0 0 0 0

$num_max_treedepth
[1] 0 0 0 0

We see that there are no warnings by diagnostics and ESS’s are higher than with the previous case with non-standardized data. The posterior has no high dependency.

draws_lin_std <- fit_lin_std$draws(format = "df")
draws_lin_std |>
  mcmc_scatter(pars=c("alpha","beta"))

Next we check that we get similar probability for beta>0.

draws_lin_std <- fit_lin_std$draws(format = "df")
mean(draws_lin_std[,"beta"]>0) # probability that beta > 0
[1] 0.9995

6 Linear Student’s \(t\) model.

The temperatures used in the above analyses are averages over three months, which makes it more likely that they are normally distributed, but there can be extreme events in the feather and we can check whether more robust Student’s \(t\) observation model woul give different results.

code_lin_std_t <- root("demos_rstan", "lin_std_t.stan")
writeLines(readLines(code_lin_std_t))
// Linear student-t model
data {
  int<lower=0> N; // number of data points
  vector[N] x; // covariate / predictor
  vector[N] y; // target
  real xpred; // new covariate value to make predictions
}
transformed data {
  // deterministic transformations of data
  vector[N] x_std = (x - mean(x)) / sd(x);
  vector[N] y_std = (y - mean(y)) / sd(y);
  real xpred_std = (xpred - mean(x)) / sd(x);
}
parameters {
  real alpha; // intercept
  real beta; // slope
  real<lower=0> sigma_std; // standard deviation is constrained to be positive
  real<lower=1> nu; // degrees of freedom is constrained >1
}
transformed parameters {
  // deterministic transformation of parameters and data
  vector[N] mu_std = alpha + beta * x_std; // linear model
}
model {
  alpha ~ normal(0, 1); // weakly informative prior (given standardized data)
  beta ~ normal(0, 1); // weakly informative prior (given standardized data)
  sigma_std ~ normal(0, 1); // weakly informative prior (given standardized data)
  nu ~ gamma(2, 0.1); // Juárez and Steel(2010)
  y_std ~ student_t(nu, mu_std, sigma_std); // observation model / likelihood
}
generated quantities {
  // transform to the original data scale
  vector[N] mu = mu_std * sd(y) + mean(y);
  real<lower=0> sigma = sigma_std * sd(y);
  // sample from the predictive distribution
  real ypred = student_t_rng(nu,
                             (alpha + beta * xpred_std) * sd(y) + mean(y),
                             sigma_std * sd(y));
  // compute log predictive densities to be used for LOO-CV
  // to make appropriate comparison to other models, this log density is computed
  // using the original data scale (y, mu, sigma)
  vector[N] log_lik;
  for (i in 1 : N) {
    log_lik[i] = student_t_lpdf(y[i] | nu, mu[i], sigma);
  }
}
mod_lin_std_t <- cmdstan_model(stan_file = code_lin_std_t)
fit_lin_std_t <- mod_lin_std_t$sample(data = data_lin, seed = SEED, refresh=1000)

We get some warnings, but these specific warnings are not critical if counts are small as here.

Let’s examine further diagnostics.

fit_lin_std_t$summary()
# A tibble: 220 × 10
   variable     mean  median     sd    mad     q5    q95  rhat ess_bulk ess_tail
   <chr>     <num:2> <num:2> <num:> <num:> <num:> <num:> <num>  <num:2>  <num:2>
 1 lp__      -5.2e+1 -5.2e+1  1.4    1.2   -55.   -51.     1.0    2078.    2594.
 2 alpha      1.7e-3  1.6e-3  0.11   0.11   -0.18   0.19   1.0    3756.    2852.
 3 beta       4.0e-1  4.1e-1  0.11   0.12    0.21   0.59   1.0    3967.    2853.
 4 sigma_std  9.0e-1  8.9e-1  0.083  0.082   0.77   1.0    1.0    3207.    2995.
 5 nu         2.4e+1  2.1e+1 14.    12.      7.7   51.     1.0    3833.    2698.
 6 mu_std[1] -6.8e-1 -6.8e-1  0.23   0.23   -1.1   -0.31   1.0    3842.    3129.
 7 mu_std[2] -6.6e-1 -6.6e-1  0.23   0.22   -1.0   -0.30   1.0    3839.    3154.
 8 mu_std[3] -6.4e-1 -6.5e-1  0.22   0.22   -1.0   -0.28   1.0    3836.    3004.
 9 mu_std[4] -6.2e-1 -6.3e-1  0.22   0.21   -0.97  -0.27   1.0    3834.    2986.
10 mu_std[5] -6.1e-1 -6.1e-1  0.21   0.21   -0.94  -0.26   1.0    3832.    3075.
# ℹ 210 more rows
fit_lin_std_t$diagnostic_summary(diagnostics = c("divergences", "treedepth"))
$num_divergent
[1] 0 0 0 0

$num_max_treedepth
[1] 0 0 0 0

We get similar diagnostics as for the linear Gaussian model with non-standardised data.

Compute the probability that the summer temperature is increasing.

draws_lin_std_t <- fit_lin_std_t$draws(format = "df")
mean(draws_lin_std_t[,"beta"]>0) # probability that beta > 0
[1] 0.99975

We get similar probability as with Gaussian obervation model.

Plot data and the model fit

mu <- draws_lin_std_t |>
  as_tibble() |>
  select(starts_with("mu[")) |>
  apply(2, quantile, c(0.05, 0.5, 0.95)) |>
  t() %>%
  data.frame(x = data_lin$x, .)  |> 
  gather(pct, y, -x)

pfit <- ggplot() +
  geom_point(aes(x, y), data = data.frame(data_lin), size = 1) +
  geom_line(aes(x, y, linetype = pct), data = mu, color = 'red') +
  scale_linetype_manual(values = c(2,1,2)) +
  labs(y = 'Summer temp. @Kilpisjärvi', x= "Year") +
  guides(linetype = "none")
phist <- mcmc_hist(draws_lin_std_t, pars = c('beta','sigma','ypred'))
grid.arrange(pfit, phist, nrow = 2)

We see also that the marginal posterior of nu is wide with lot of mass for values producing distrbution really close to Gaussian.

7 Pareto-smoothed importance-sampling leave-one-out cross-validation (PSIS-LOO)

We can use leave-one-out cross-validation to compare the expected predictive performance. For the following lines to work, the log-likelihood needs to be evaluated in the stan code. For an example, see lin.stan and Computing approximate leave-one-out cross-validation usig PSIS-LOO.

# At this moment there is not yet method for cmdstanr fit, so we use this helper
loocmd <- function(fit, ...) {
  loo(fit$draws("log_lik"), r_eff=relative_eff(fit$draws("log_lik")), ...)
}
loo_lin_std <- loocmd(fit_lin_std)
loo_lin_std_t <- loocmd(fit_lin_std_t)
loo_compare(loo_lin_std, loo_lin_std_t)
       elpd_diff se_diff
model1  0.0       0.0   
model2 -0.4       0.4   

There is no practical difference between Gaussian and Student’s \(t\) observation model for this data.

8 Comparison of \(k\) groups with hierarchical models

Let’s compare the temperatures in three summer months.

data_grp <-list(N = 3*nrow(data_kilpis),
             K = 3,
             x = rep(1:3, nrow(data_kilpis)),
             y = c(t(data_kilpis[,2:4])))

8.1 Common variance (ANOVA) model

code_grp_aov <- root("demos_rstan", "grp_aov.stan")
writeLines(readLines(code_grp_aov))
// Comparison of k groups with common variance (ANOVA)
data {
  int<lower=0> N; // number of observations
  int<lower=0> K; // number of groups
  array[N] int<lower=1, upper=K> x; // discrete group indicators
  vector[N] y; // real valued observations
}
parameters {
  vector[K] mu;        // group means
  real<lower=0> sigma; // common standard deviation constrained to be positive
}
model {
  mu ~ normal(0, 100); // weakly informative prior
  sigma ~ normal(0, 1); // weakly informative prior
  y ~ normal(mu[x], sigma); // observation model / likelihood
}

Fit the model

mod_grp <- cmdstan_model(stan_file = code_grp_aov)
fit_grp <- mod_grp$sample(data = data_grp, seed = SEED, refresh=1000)

fit_grp$summary()
fit_grp$diagnostic_summary(diagnostics = c("divergences", "treedepth"))

8.2 Common variance and hierarchical prior for mean.

Results do not differ much from the previous, because there is only few groups and quite much data per group, but this works as an example of a hierarchical model.

code_grp_prior_mean <- root("demos_rstan", "grp_prior_mean.stan")
writeLines(readLines(code_grp_prior_mean))
// Comparison of k groups with common variance and
// hierarchical prior for the mean
data {
  int<lower=0> N; // number of observations
  int<lower=0> K; // number of groups
  array[N] int<lower=1, upper=K> x; // discrete group indicators
  vector[N] y; // real valued observations
}
parameters {
  real mu0; // prior mean
  real<lower=0> sigma0; // prior std constrained to be positive
  vector[K] mu; // group means
  real<lower=0> sigma; // common std constrained to be positive
}
model {
  mu0 ~ normal(10, 10); // weakly informative prior
  sigma0 ~ normal(0, 10); // weakly informative prior
  mu ~ normal(mu0, sigma0); // population prior with unknown parameters
  // log-normal prior sets normal prior on logarithm of the paremeter,
  // which is useful for positive parameters that shouldn't be very
  // close to 0. BDA3 Chapter 5 uses scaled inverse Chi^2 prior, but
  // as these don't need to be (semi-)conjugate, thinking in terms of
  // log-normal can be easier.
  sigma ~ lognormal(0, .5); // weakly informative prior
  y ~ normal(mu[x], sigma); // observation model / likelihood
}

Fit the model

mod_grp <- cmdstan_model(stan_file = code_grp_prior_mean)
fit_grp <- mod_grp$sample(data = data_grp, seed = SEED, refresh=1000)

fit_grp$summary()
fit_grp$diagnostic_summary(diagnostics = c("divergences", "treedepth"))

We got a small number of divergences, so we increase adapt_delta=0.95. Note that thisis useful only in case of small number of divergences, and increasing adapt_delta>0.99 doesn’t usually make sense, and in such cases there is need to investigate the identifiability or parameter transformations.

mod_grp <- cmdstan_model(stan_file = code_grp_prior_mean)
fit_grp <- mod_grp$sample(data = data_grp, seed = SEED, refresh=1000, adapt_delta=0.95)


fit_grp$summary()
fit_grp$diagnostic_summary(diagnostics = c("divergences", "treedepth"))

8.3 Unequal variance and hierarchical prior for mean and variance

code_grp_prior_mean_var <- root("demos_rstan", "grp_prior_mean_var.stan")
writeLines(readLines(code_grp_prior_mean_var))
// Comparison of k groups with unequal variance and
// hierarchical priors for the mean and the variance
data {
  int<lower=0> N; // number of data points
  int<lower=0> K; // number of groups
  array[N] int<lower=1, upper=K> x; // group indicator
  vector[N] y; //
}
parameters {
  real mu0; // prior mean
  real<lower=0> musigma0; // prior std
  vector[K] mu; // group means
  real lsigma0; // prior mean
  real<lower=0> lsigma0s; // prior std
  vector<lower=0>[K] sigma; // group stds
}
model {
  mu0 ~ normal(10, 10); // weakly informative prior
  musigma0 ~ normal(0, 10); // weakly informative prior
  mu ~ normal(mu0, musigma0); // population prior with unknown parameters
  // lsigma0 is prior mean in log scale
  lsigma0 ~ normal(0, 1); // weakly informative prior
  // log-normal prior sets normal prior on logarithm of the paremeter,
  // which is useful for positive parameters that shouldn't be very
  // close to 0. BDA3 Chapter 5 uses scaled inverse Chi^2 prior, but
  // as these don't need to be (semi-)conjugate thinking in terms of
  // log-normal can be easier.
  lsigma0s ~ lognormal(log(0.1), .5); // weakly informative prior
  sigma ~ lognormal(lsigma0, lsigma0s); // population prior with unknown parameters
  y ~ normal(mu[x], sigma[x]); // observation model / likelihood
}

Fit the model

mod_grp <- cmdstan_model(stan_file = code_grp_prior_mean_var)
fit_grp <- mod_grp$sample(data = data_grp, seed = SEED, refresh=1000)

fit_grp$summary()
fit_grp$diagnostic_summary(diagnostics = c("divergences", "treedepth"))

We got a small number of divergences, so we increase adapt_delta=0.95. Note that thisis useful only in case of small number of divergences, and increasing adapt_delta>0.99 doesn’t usually make sense, and in such cases there is need to investigate the identifiability or parameter transformations.

mod_grp <- cmdstan_model(stan_file = code_grp_prior_mean_var)
fit_grp <- mod_grp$sample(data = data_grp, seed = SEED, refresh=1000, adapt_delta=0.95);


fit_grp$summary()
fit_grp$diagnostic_summary(diagnostics = c("divergences", "treedepth"))

Plot the results

temps <- fit_grp$draws(format = "df") |>
  as_tibble() |>
  select(starts_with("mu[")) |>
  setNames(c('June','July','August'))
mcmc_areas(temps) + xlab('Temperature')

Probabilities that June is hotter than July, June is hotter than August and July is hotter than August:

paste('p(TempJun > TempJul) = ', mean(temps$June > temps$July))
[1] "p(TempJun > TempJul) =  0"
paste('p(TempJun > TempAug) = ', mean(temps$June > temps$August))
[1] "p(TempJun > TempAug) =  0"
paste('p(TempJul > TempAug) = ', mean(temps$July > temps$August))
[1] "p(TempJul > TempAug) =  1"


Licenses

  • Code © 2017-2020, Aki Vehtari, 2017 Markus Paasiniemi, licensed under BSD-3.
  • Text © 2017-2020, Aki Vehtari, licensed under CC-BY-NC 4.0.
LS0tCnRpdGxlOiAiQmF5ZXNpYW4gZGF0YSBhbmFseXNpcyAtIENtZFN0YW5SIGRlbW9zIgphdXRob3I6ICJBa2kgVmVodGFyaSwgTWFya3VzIFBhYXNpbmllbWkiCmRhdGU6ICJGaXJzdCB2ZXJzaW9uIDIwMTctMDctMTcuIExhc3QgbW9kaWZpZWQgYHIgZm9ybWF0KFN5cy5EYXRlKCkpYC4iCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgZmlnX2NhcHRpb246IHllcwogICAgdG9jOiBUUlVFCiAgICB0b2NfZGVwdGg6IDIKICAgIG51bWJlcl9zZWN0aW9uczogVFJVRQogICAgdG9jX2Zsb2F0OgogICAgICBzbW9vdGhfc2Nyb2xsOiBGQUxTRQogICAgdGhlbWU6IHJlYWRhYmxlCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCi0tLQojIFNldHVwICB7LnVubnVtYmVyZWR9CgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGNhY2hlPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBlcnJvcj1GQUxTRSwgd2FybmluZz1UUlVFLCBjb21tZW50PU5BLCBvdXQud2lkdGg9Jzk1JScpCmBgYAoKKipMb2FkIHBhY2thZ2VzKioKCmBgYHtyIH0KbGlicmFyeShjbWRzdGFucikKIyBJZiBydW5uaW5nIGluIEFhbHRvIEp1cHl0ZXJIdWIgdGhlIHBhdGggc2hvdWxkIGF1dG9tYXRpY2FsbHkgYmUgc2V0IHRvCiMgJy9jb3Vyc2VkYXRhL2NtZHN0YW4nCm9wdGlvbnMobWMuY29yZXMgPSAxKQpsaWJyYXJ5KHBvc3RlcmlvcikKb3B0aW9ucyhwb3N0ZXJpb3IubnVtX2FyZ3M9bGlzdChzaWdmaWc9MikpICMgYnkgZGVmYXVsdCBzdW1tYXJpZXMgd2l0aCAyIHNpZ25pZmljYW50IGRpZ2l0cwpsaWJyYXJ5KGxvbykKbGlicmFyeSh0aWR5cikKbGlicmFyeShkcGx5cikKb3B0aW9ucyhwaWxsYXIubmVnPUZBTFNFKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZ3JpZEV4dHJhKQpsaWJyYXJ5KGJheWVzcGxvdCkKbGlicmFyeShnZ2Rpc3QpCnRoZW1lX3NldChiYXllc3Bsb3Q6OnRoZW1lX2RlZmF1bHQoYmFzZV9mYW1pbHkgPSAic2FucyIpKQpsaWJyYXJ5KHJwcm9qcm9vdCkKcm9vdDwtaGFzX2ZpbGUoIi5CREFfUl9kZW1vc19yb290IikkbWFrZV9maXhfZmlsZSgpClNFRUQgPC0gNDg5MjcgIyBzZXQgcmFuZG9tIHNlZWQgZm9yIHJlcHJvZHVjYWJpbGl0eQpgYGAKCiMgSW50cm9kdWN0aW9uCgpUaGlzIG5vdGVib29rIGNvbnRhaW5zIHNldmVyYWwgZXhhbXBsZXMgb2YgaG93IHRvIHVzZSBbU3Rhbl0oaHR0cHM6Ly9tYy1zdGFuLm9yZykgaW4gUiB3aXRoIF9fY21kc3RhbnJfXy4gVGhpcyBub3RlYm9vayBhc3N1bWVzIGJhc2ljIGtub3dsZWRnZSBvZiBCYXllc2lhbiBpbmZlcmVuY2UgYW5kIE1DTUMuIFRoZSBTdGFuIG1vZGVscyBhcmUgc3RvcmVkIGluIHNlcGFyYXRlIC5zdGFuLWZpbGVzLiBUaGUgZXhhbXBsZXMgYXJlIHJlbGF0ZWQgdG8gW0JheWVzaWFuIGRhdGEgYW5hbHlzaXMgY291cnNlXShodHRwczovL2F2ZWh0YXJpLmdpdGh1Yi5pby9CREFfY291cnNlX0FhbHRvLykuCgojIEJlcm5vdWxsaSBtb2RlbAoKVG95IGRhdGEgd2l0aCBzZXF1ZW5jZSBvZiBmYWlsdXJlcyAoMCkgYW5kIHN1Y2Nlc3NlcyAoMSkuIFdlIHdvdWxkIGxpa2UgdG8gbGVhcm4gYWJvdXQgdGhlIHVua25vd24gcHJvYmFiaWxpdHkgb2Ygc3VjY2Vzcy4KCmBgYHtyIH0KZGF0YV9iZXJuIDwtIGxpc3QoTiA9IDEwLCB5ID0gYygxLCAxLCAxLCAwLCAxLCAxLCAxLCAwLCAxLCAwKSkKYGBgCgpCZXJub3VsbGkgbW9kZWwgd2l0aCBhIHByb3BlciBCZXRhKDEsMSkgKHVuaWZvcm0pIHByaW9yCgpgYGB7ciB9CmNvZGVfYmVybiA8LSByb290KCJkZW1vc19yc3RhbiIsICJiZXJuLnN0YW4iKQp3cml0ZUxpbmVzKHJlYWRMaW5lcyhjb2RlX2Jlcm4pKQpgYGAKClNhbXBsZSBmb3JtIHRoZSBwb3N0ZXJpb3IgYW5kIHNob3cgdGhlIHN1bW1hcnkKCmBgYHtyIHJlc3VsdHM9J2hpZGUnfQptb2RfYmVybiA8LSBjbWRzdGFuX21vZGVsKHN0YW5fZmlsZSA9IGNvZGVfYmVybikKZml0X2Jlcm4gPC0gbW9kX2Jlcm4kc2FtcGxlKGRhdGEgPSBkYXRhX2Jlcm4sIHNlZWQgPSBTRUVELCByZWZyZXNoPTEwMDApCgpmaXRfYmVybiRzdW1tYXJ5KCkKYGBgCgpQbG90IGEgaGlzdG9ncmFtIG9mIHRoZSBwb3N0ZXJpb3IgZHJhd3Mgd2l0aCBiYXllc3Bsb3QgKHVzZXMgZ2dwbG90KQoKYGBge3IgfQpkcmF3cyA8LSBmaXRfYmVybiRkcmF3cyhmb3JtYXQgPSAiZGYiKQptY21jX2hpc3QoZHJhd3MsIHBhcnM9J3RoZXRhJykgKyB4bGltKGMoMCwxKSkKYGBgCgpQbG90IGEgZG90cyBwbG90IG9mIHRoZSBwb3N0ZXJpb3IgZHJhd3Mgd2l0aCBnZ3Bsb3QgKyBnZ2Rpc3QKCmBgYHtyIHdhcm5pbmc9RkFMU0V9CmRyYXdzIHw+CiAgZ2dwbG90KGFlcyh4PXRoZXRhKSkgKyAKICBzdGF0X2RvdHNpbnRlcnZhbCgpICsgCiAgeGxpbShjKDAsMSkpCmBgYAoKIyBCaW5vbWlhbCBtb2RlbAoKSW5zdGVhZCBvZiBzZXF1ZW5jZSBvZiAwJ3MgYW5kIDEncywgd2UgY2FuIHN1bW1hcml6ZSB0aGUgZGF0YSB3aXRoIHRoZSBudW1iZXIgb2YgZXhwZXJpbWVudHMgYW5kIHRoZSBudW1iZXIgc3VjY2Vzc2VzOgoKYGBge3IgfQpkYXRhX2JpbiA8LSBsaXN0KE4gPSAxMCwgeSA9IDcpCmBgYAoKQW5kIHRoZW4gd2UgdXNlIEJpbm9taWFsIG1vZGVsIHdpdGggQmV0YSgxLDEpIHByaW9yIGZvciB0aGUgcHJvYmFiaWxpdHkgb2Ygc3VjY2Vzcy4KCmBgYHtyIH0KY29kZV9iaW5vbSA8LSByb290KCJkZW1vc19yc3RhbiIsImJpbm9tLnN0YW4iKQp3cml0ZUxpbmVzKHJlYWRMaW5lcyhjb2RlX2Jpbm9tKSkKYGBgCgpTYW1wbGUgZnJvbSB0aGUgcG9zdGVyaW9yIGFuZCBwbG90IHRoZSBwb3N0ZXJpb3IuIFRoZSBoaXN0b2dyYW0gc2hvdWxkIGxvb2sgc2ltaWxhciBhcyBpbiB0aGUgQmVybm91bGxpIGNhc2UuCgoKYGBge3IgcmVzdWx0cz0naGlkZSd9Cm1vZF9iaW4gPC0gY21kc3Rhbl9tb2RlbChzdGFuX2ZpbGUgPSBjb2RlX2Jpbm9tKQpmaXRfYmluIDwtIG1vZF9iaW4kc2FtcGxlKGRhdGEgPSBkYXRhX2Jpbiwgc2VlZCA9IFNFRUQsIHJlZnJlc2g9MTAwMCkKCmZpdF9iaW4kc3VtbWFyeSgpCgpkcmF3cyA8LSBmaXRfYmluJGRyYXdzKGZvcm1hdCA9ICJkZiIpCm1jbWNfaGlzdChkcmF3cywgcGFycyA9ICd0aGV0YScpICsgeGxpbShjKDAsMSkpCmBgYAoKUmUtcnVuIHRoZSBtb2RlbCB3aXRoIGEgbmV3IGRhdGEuIFRoZSBjb21waWxlZCBTdGFuIHByb2dyYW0gaXMgcmUtdXNlZCBtYWtpbmcgdGhlIHJlLXVzZSBmYXN0ZXIuCgoKYGBge3IgcmVzdWx0cz0naGlkZSd9CmRhdGFfYmluIDwtIGxpc3QoTiA9IDEwMCwgeSA9IDcwKQpmaXRfYmluIDwtIG1vZF9iaW4kc2FtcGxlKGRhdGEgPSBkYXRhX2Jpbiwgc2VlZCA9IFNFRUQsIHJlZnJlc2g9MTAwMCkKCmZpdF9iaW4kc3VtbWFyeSgpCgpkcmF3cyA8LSBmaXRfYmluJGRyYXdzKGZvcm1hdCA9ICJkZiIpCm1jbWNfaGlzdChkcmF3cywgcGFycyA9ICd0aGV0YScsIGJpbndpZHRoID0gMC4wMSkgKyB4bGltKGMoMCwxKSkKYGBgCgojIyBFeHBsaWNpdCB0cmFuc2Zvcm1hdGlvbiBvZiB2YXJpYWJsZXMKCkluIHRoZSBhYm92ZSBleGFtcGxlcyB0aGUgcHJvYmFiaWxpdHkgb2Ygc3VjY2VzcyAkXHRoZXRhJCB3YXMgZGVjbGFyZWQgYXMKCmByZWFsPGxvd2VyPTAsdXBwZXI9MT4gdGhldGE7YAoKU3RhbiBtYWtlcyBhdXRvbWF0aWMgdHJhbnNmb3JtYXRpb24gb2YgdGhlIHZhcmlhYmxlIHRvIHRoZSB1bmNvbnN0cmFpbmVkIHNwYWNlIHVzaW5nIGxvZ2l0IHRyYW5zb2ZybWF0aW9uIGZvciBpbnRlcnZhbCBjb25zdHJhaW5lZCBhbmQgbG9nIHRyYW5zZm9ybWF0aW9uIGZvciBoYWxmIGNvbnN0cmFpbnRzLgoKVGhlIGZvbGxvd2luZyBleGFtcGxlIHNob3dzIGhvdyB3ZSBjYW4gYWxzbyBtYWtlIGFuIGV4cGxpY2l0IHRyYW5zZm9ybWF0aW9uIGFuZCB1c2UgYmlub21pYWxfbG9naXQgZnVuY3Rpb24gd2hpY2ggdGFrZXMgdGhlIHVuY29uc3RyYWluZWQgcGFyYW1ldGVyIGFzIGFuIGFyZ3VtZW50IGFuZCB1c2VzIGxvZ2l0IHRyYW5zZm9ybWF0aW9uIGludGVybmFsbHkuIFRoaXMgZm9ybSBjYW4gYmUgdXNlZnVsIGZvciBiZXR0ZXIgbnVtZXJpY2FsIHN0YWJpbGl0eS4KCmBgYHtyIH0KY29kZV9iaW5vbWIgPC0gcm9vdCgiZGVtb3NfcnN0YW4iLCAiYmlub21iLnN0YW4iKQp3cml0ZUxpbmVzKHJlYWRMaW5lcyhjb2RlX2Jpbm9tYikpCmBgYAoKSGVyZSB3ZSBoYXZlIHVzZWQgR2F1c3NpYW4gcHJpb3IgaW4gdGhlIHVuY29uc3RyYWluZWQgc3BhY2UsIHdoaWNoIHByb2R1Y2VzIGNsb3NlIHRvIHVuaWZvcm0gcHJpb3IgZm9yIHRoZXRhLgoKU2FtcGxlIGZyb20gdGhlIHBvc3RlcmlvciBhbmQgcGxvdCB0aGUgcG9zdGVyaW9yLiBUaGUgaGlzdG9ncmFtIHNob3VsZCBsb29rIHNpbWlsYXIgYXMgd2l0aCB0aGUgcHJldmlvdXMgbW9kZWxzLgoKCmBgYHtyIHJlc3VsdHM9J2hpZGUnfQpkYXRhX2JpbiA8LSBsaXN0KE4gPSAxMDAsIHkgPSA3MCkKbW9kX2JpbmIgPC0gY21kc3Rhbl9tb2RlbChzdGFuX2ZpbGUgPSBjb2RlX2Jpbm9tYikKZml0X2JpbmIgPC0gbW9kX2JpbiRzYW1wbGUoZGF0YSA9IGRhdGFfYmluLCBzZWVkID0gU0VFRCwgcmVmcmVzaD0xMDAwKQoKZml0X2JpbmIkc3VtbWFyeSgpCgpkcmF3cyA8LSBmaXRfYmluYiRkcmF3cyhmb3JtYXQgPSAiZGYiKQptY21jX2hpc3QoZHJhd3MsIHBhcnMgPSAndGhldGEnLCBiaW53aWR0aCA9IDAuMDEpICsgeGxpbShjKDAsMSkpCmBgYAoKCiMgQ29tcGFyaXNvbiBvZiB0d28gZ3JvdXBzIHdpdGggQmlub21pYWwKCkFuIGV4cGVyaW1lbnQgd2FzIHBlcmZvcm1lZCB0byBlc3RpbWF0ZSB0aGUgZWZmZWN0IG9mIGJldGEtYmxvY2tlcnMgb24gbW9ydGFsaXR5IG9mIGNhcmRpYWMgcGF0aWVudHMuIEEgZ3JvdXAgb2YgcGF0aWVudHMgd2VyZSByYW5kb21seSBhc3NpZ25lZCB0byB0cmVhdG1lbnQgYW5kIGNvbnRyb2wgZ3JvdXBzOgoKLSBvdXQgb2YgNjc0IHBhdGllbnRzIHJlY2VpdmluZyB0aGUgY29udHJvbCwgMzkgZGllZAotIG91dCBvZiA2ODAgcmVjZWl2aW5nIHRoZSB0cmVhdG1lbnQsIDIyIGRpZWQKCkRhdGE6CgpgYGB7ciB9CmRhdGFfYmluMiA8LSBsaXN0KE4xID0gNjc0LCB5MSA9IDM5LCBOMiA9IDY4MCwgeTIgPSAyMikKYGBgCgpUbyBhbmFseXNlIHdoZXRoZXIgdGhlIHRyZWF0bWVudCBpcyB1c2VmdWwsIHdlIGNhbiB1c2UgQmlub21pYWwgbW9kZWwgZm9yIGJvdGggZ3JvdXBzIGFuZCBjb21wdXRlIG9kZHMtcmF0aW86CgpgYGB7ciB9CmNvZGVfYmlub20yIDwtIHJvb3QoImRlbW9zX3JzdGFuIiwgImJpbm9tMi5zdGFuIikKd3JpdGVMaW5lcyhyZWFkTGluZXMoY29kZV9iaW5vbTIpKQpgYGAKClNhbXBsZSBmcm9tIHRoZSBwb3N0ZXJpb3IgYW5kIHBsb3QgdGhlIHBvc3RlcmlvcgoKYGBge3IgcmVzdWx0cz0naGlkZSd9Cm1vZF9iaW4yIDwtIGNtZHN0YW5fbW9kZWwoc3Rhbl9maWxlID0gY29kZV9iaW5vbTIpCmZpdF9iaW4yIDwtIG1vZF9iaW4yJHNhbXBsZShkYXRhID0gZGF0YV9iaW4yLCBzZWVkID0gU0VFRCwgcmVmcmVzaD0xMDAwKQoKZml0X2JpbjIkc3VtbWFyeSgpCmBgYAoKSGlzdG9ncmFtCgpgYGB7ciB3YXJuaW5nPUZBTFNFfQpkcmF3cyA8LSBmaXRfYmluMiRkcmF3cyhmb3JtYXQgPSAiZGYiKQptY21jX2hpc3QoZHJhd3MsIHBhcnMgPSAnb2Rkc3JhdGlvJykgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDEpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gYyhzZXEoMC4yNSwxLjUsYnk9MC4yNSkpKQpgYGAKCkRvdHMgcGxvdCB3aXRoIG1lZGlhbiBhbmQgNjYlIGFuZCA5NSUgaW50ZXJ2YWxzCgpgYGB7ciB3YXJuaW5nPUZBTFNFfQpkcmF3cyB8PgogIGdncGxvdChhZXMoeD1vZGRzcmF0aW8pKSArIAogICAgZ2VvbV9kb3RzaW50ZXJ2YWwoKSArIAogICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMSkgKwogICAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IGMoc2VxKDAuMjUsMS41LGJ5PTAuMjUpKSkrCiAgICBsYWJzKHg9J09kZHMgcmF0aW8nLCB5PScnKQpgYGAKClByb2JhYmlsaXR5IChhbmQgY29ycmVzcG9uZGluZyBNQ1NFKSB0aGF0IG9kZHNyYXRpbzwxCgpgYGB7ciB9CmRyYXdzIHw+CiAgbXV0YXRlX3ZhcmlhYmxlcyhwX29kZHNyYXRpb19sdF8xID0gYXMubnVtZXJpYyhvZGRzcmF0aW88MSkpIHw+CiAgc3Vic2V0X2RyYXdzKCJwX29kZHNyYXRpb19sdF8xIikgfD4KICBzdW1tYXJpc2VfZHJhd3MocHJvYj1tZWFuLCBNQ1NFPW1jc2VfbWVhbikKYGBgCgojIExpbmVhciBHYXVzc2lhbiBtb2RlbAoKVGhlIGZvbGxvd2luZyBmaWxlIGhhcyBLaWxwaXNqw6Rydmkgc3VtbWVyIG1vbnRoIHRlbXBlcmF0dXJlcyAxOTUyLTIwMjIKKGRhdGEgYnkgRmlubmlzaCBNZXRlb3JvbG9naWNhbCBJbnN0aXR1dGUsIENDLUJZIDQuMCkuIAoKYGBge3IgfQpkYXRhX2tpbHBpcyA8LSByZWFkLmRlbGltKHJvb3QoImRlbW9zX3JzdGFuIiwia2lscGlzamFydmktc3VtbWVyLXRlbXAtMjAyMi5jc3YiKSwgc2VwID0gIjsiKQpkYXRhX2xpbiA8LWxpc3QoTiA9IG5yb3coZGF0YV9raWxwaXMpLAogICAgICAgICAgICAgeCA9IGRhdGFfa2lscGlzJHllYXIsCiAgICAgICAgICAgICB4cHJlZCA9IDIwMTYsCiAgICAgICAgICAgICB5ID0gZGF0YV9raWxwaXNbLDVdKQpgYGAKClBsb3QgdGhlIGRhdGEKCmBgYHtyIH0KZ2dwbG90KCkgKwogIGdlb21fcG9pbnQoYWVzKHgsIHkpLCBkYXRhID0gZGF0YS5mcmFtZShkYXRhX2xpbiksIHNpemUgPSAxKSArCiAgbGFicyh5ID0gJ1N1bW1lciB0ZW1wLiBAS2lscGlzasOkcnZpJywgeD0gIlllYXIiKSArCiAgZ3VpZGVzKGxpbmV0eXBlID0gIm5vbmUiKQpgYGAKClRvIGFuYWx5c2Ugd2hldGhlciB0aGUgYXZlcmFnZSBzdW1tZXIgbW9udGggdGVtcGVyYXR1cmUgaXMgcmlzaW5nLCB3ZSB1c2UgYSBsaW5lYXIgbW9kZWwgd2l0aCBHYXVzc2lhbiBtb2RlbCBmb3IgdGhlIHVuZXhwbGFpbmVkIHZhcmlhdGlvbi4gCgojIyBHYXVzc2lhbiBsaW5lYXIgbW9kZWwgd2l0aCBhZGp1c3RhYmxlIHByaW9ycwoKVGhlIGZvbGxvaW5nIFN0YW4gY29kZSBhbGxvd3MgYWxzbyBzZXR0aW5nIGh5cGVycGFyYW1ldGVyIHZhbHVlcyBhcyBkYXRhIGFsbG93aW5nIGVhc2llciB3YXkgdG8gdXNlIGRpZmZlcmVudCBwcmlvcnMgaW4gZGlmZmVyZW50IGFuYWx5c2VzOgoKYGBge3IgfQpjb2RlX2xpbiA8LSByb290KCJkZW1vc19yc3RhbiIsICJsaW4uc3RhbiIpCndyaXRlTGluZXMocmVhZExpbmVzKGNvZGVfbGluKSkKYGBgCgpDcmVhdGUgYW5vdGhlciBsaXN0IHdpdGggZGF0YSBhbmQgcHJpb3JzCgpgYGB7ciB9CmRhdGFfbGluX3ByaW9ycyA8LSBjKGxpc3QoCiAgICBwbXVhbHBoYSA9IG1lYW4odW5saXN0KGRhdGFfa2lscGlzWyw1XSkpLCAjIGNlbnRlcmVkCiAgICBwc2FscGhhID0gMTAwLCAjIHdlYWtseSBpbmZvcm1hdGl2ZQogICAgcG11YmV0YSA9IDAsICMgYSBwcmlvcmkgaW5jci4gYW5kIGRlY3IuIGFzIGxpa2VseQogICAgcHNiZXRhID0gKC4xLS0uMSkvNiwgIyBhdmcgdGVtcCBwcm9iIGRvZXMgZG9lcyBub3QgaW5jci4gbW9yZSB0aGFuIGEgZGVncmVlIHBlciAxMCB5ZWFycwogICAgcHNzaWdtYSA9IDEpLCAjIHRvdGFsIHZhcmlhdGlvbiBpbiBzdW1tZXIgYXZlcmFnZSB0ZW1wZXJhdHVyZXMgaXMgbGVzcyArLTMgZGVncmVlcwogIGRhdGFfbGluKQpgYGAKClJ1biBTdGFuCgpgYGB7ciByZXN1bHRzPSdoaWRlJ30KbW9kX2xpbiA8LSBjbWRzdGFuX21vZGVsKHN0YW5fZmlsZSA9IGNvZGVfbGluKQpmaXRfbGluIDwtIG1vZF9saW4kc2FtcGxlKGRhdGEgPSBkYXRhX2xpbl9wcmlvcnMsIHNlZWQgPSBTRUVELCByZWZyZXNoPTEwMDApCmBgYAoKU3RhbiBnaXZlcyBhIHdhcm5pbmc6IFRoZXJlIHdlcmUgWCB0cmFuc2l0aW9ucyBhZnRlciB3YXJtdXAgdGhhdCBleGNlZWRlZCB0aGUgbWF4aW11bSB0cmVlZGVwdGguIAoKV2UgY2FuIGNoZWNrIHRoZSBnZW5lcmljIGNvbnZlcmdlbmNlIGRpYWdub3N0aWNzIGFzIGZvbGxvd3MKCmBgYHtyIH0KZml0X2xpbiRzdW1tYXJ5KCkKYGBgCgpXZSBjYW4gY2hlY2sgdGhlIEhNQyBzcGVjaWZpYyBkaWFnbm9zdGljcyBhcyBmb2xsb3dzCgpgYGB7ciBtZXNzYWdlPVRSVUV9CmZpdF9saW4kZGlhZ25vc3RpY19zdW1tYXJ5KGRpYWdub3N0aWNzID0gYygiZGl2ZXJnZW5jZXMiLCAidHJlZWRlcHRoIikpCmBgYAoKVGhlIGhpZ2ggbnVtYmVyIG9mIG1heCB0cmVlZGVwdGggZXhjZWVkZW5jZSBpcyBkdWUgdG8gaGlnaCBwb3N0ZXJpb3IgZGVwZW5kZW5jeSwKd2hpY2ggaW4gdGhpcyBjYXNlIHJlZHVjZXMgZWZmaWNpZW5jeSwgYnV0IGRvZXNuJ3QgaW52YWxpZGF0ZSB0aGUgcmVzdWx0cwoKYGBge3IgfQpkcmF3c19saW4gPC0gZml0X2xpbiRkcmF3cyhmb3JtYXQgPSAiZGYiKQpkcmF3c19saW4gfD4KICBtY21jX3NjYXR0ZXIocGFycz1jKCJhbHBoYSIsImJldGEiKSkKYGBgCgoKQ29tcHV0ZSB0aGUgcHJvYmFiaWxpdHkgdGhhdCB0aGUgc3VtbWVyIHRlbXBlcmF0dXJlIGlzIGluY3JlYXNpbmcuCgpgYGB7ciB3YXJuaW5nPUZBTFNFfQptZWFuKGRyYXdzX2xpblssImJldGEiXT4wKSAjIHByb2JhYmlsaXR5IHRoYXQgYmV0YSA+IDAKYGBgCgpQbG90IHRoZSBkYXRhLCB0aGUgbW9kZWwgZml0IGFuZCBwcmVkaWN0aW9uIGZvciB5ZWFyIDIwMTYuCgpgYGB7ciB3YXJuaW5nPUZBTFNFfQptdSA8LSBkcmF3c19saW4gfD4KICBhc19kcmF3c19kZigpIHw+CiAgYXNfdGliYmxlKCkgfD4KICBzZWxlY3Qoc3RhcnRzX3dpdGgoIm11IikpIHw+CiAgYXBwbHkoMiwgcXVhbnRpbGUsIGMoMC4wNSwgMC41LCAwLjk1KSkgfD4KICB0KCkgJT4lCiAgZGF0YS5mcmFtZSh4ID0gZGF0YV9saW4keCwgLikgIHw+IAogIGdhdGhlcihwY3QsIHksIC14KQoKcGZpdCA8LSBnZ3Bsb3QoKSArCiAgZ2VvbV9wb2ludChhZXMoeCwgeSksIGRhdGEgPSBkYXRhLmZyYW1lKGRhdGFfbGluKSwgc2l6ZSA9IDEpICsKICBnZW9tX2xpbmUoYWVzKHgsIHksIGxpbmV0eXBlID0gcGN0KSwgZGF0YSA9IG11LCBjb2xvciA9ICdyZWQnKSArCiAgc2NhbGVfbGluZXR5cGVfbWFudWFsKHZhbHVlcyA9IGMoMiwxLDIpKSArCiAgbGFicyh5ID0gJ1N1bW1lciB0ZW1wLiBAS2lscGlzasOkcnZpJywgeD0gIlllYXIiKSArCiAgZ3VpZGVzKGxpbmV0eXBlID0gIm5vbmUiKQpwaGlzdCA8LSBtY21jX2hpc3QoZHJhd3NfbGluLCBwYXJzID0gYygnYmV0YScsJ3NpZ21hJywneXByZWQnKSkKZ3JpZC5hcnJhbmdlKHBmaXQsIHBoaXN0LCBucm93ID0gMikKYGBgCgojIyBHYXVzc2lhbiBsaW5lYXIgbW9kZWwgd2l0aCBzdGFuZGFyZGl6ZWQgZGF0YQoKSW4gdGhlIGFib3ZlIHdlIHVzZWQgdGhlIHVubm9ybWFsaXplZCBkYXRhIGFuZCBhcyB4IHZhbHVlcyBhcmUgZmFyIGF3YXkgZnJvbSB6ZXJvLCB0aGlzIHdpbGwgY2F1c2UgdmVyeSBzdHJvbmcgcG9zdGVyaW9yIGRlcGVuZGVuY3kgYmV0d2VlbiBhbHBoYSBhbmQgYmV0YSAoZGlkIHlvdSB1c2UgU2hpbnlTdGFuIGZvciB0aGUgYWJvdmUgbW9kZWw/KS4gVGhlIHN0cm9uZyBwb3N0ZXJpb3IgZGVwZW5kZW5jeSBjYW4gYmUgcmVtb3ZlZCBieSBub3JtYWxpemluZyB0aGUgZGF0YSB0byBoYXZlIHplcm8gbWVhbi4gVGhlIGZvbGxvd2luZyBTdGFuIGNvZGUgbWFrZXMgaXQgaW4gU3Rhbi4gSW4gZ2VuZXJhdGVkIHF1YW50aXRpZXMgd2UgZG8gY29ycmVzcG5kaW5nIHRyYW5zZm9ybWF0aW9uIGJhY2sgdG8gdGhlIG9yaWdpbmFsIHNjYWxlLgoKYGBge3IgfQpjb2RlX2xpbl9zdGQgPC0gcm9vdCgiZGVtb3NfcnN0YW4iLCAibGluX3N0ZC5zdGFuIikKd3JpdGVMaW5lcyhyZWFkTGluZXMoY29kZV9saW5fc3RkKSkKCgpgYGAKYGBge3IgcmVzdWx0cz0naGlkZSd9Cm1vZF9saW5fc3RkIDwtIGNtZHN0YW5fbW9kZWwoc3Rhbl9maWxlID0gY29kZV9saW5fc3RkKQpmaXRfbGluX3N0ZCA8LSBtb2RfbGluX3N0ZCRzYW1wbGUoZGF0YSA9IGRhdGFfbGluLCBzZWVkID0gU0VFRCwgcmVmcmVzaD0xMDAwKQpgYGAKCk5vdyB0aGVyZSB3ZXJlIG5vIHdhcm5pbmdzLiBXZSBjYW4gY2hlY2sgZGlhZ25vc3RpY3Mgd2l0aCB0aGUgZm9sbG93aW5nIGNvbW1hbmRzLgoKYGBge3IgbWVzc2FnZT1UUlVFfQpmaXRfbGluX3N0ZCRzdW1tYXJ5KCkKZml0X2xpbl9zdGQkZGlhZ25vc3RpY19zdW1tYXJ5KGRpYWdub3N0aWNzID0gYygiZGl2ZXJnZW5jZXMiLCAidHJlZWRlcHRoIikpCmBgYAoKV2Ugc2VlIHRoYXQgdGhlcmUgYXJlIG5vIHdhcm5pbmdzIGJ5IGRpYWdub3N0aWNzIGFuZCBFU1MncyBhcmUgaGlnaGVyIHRoYW4gd2l0aCB0aGUgcHJldmlvdXMgY2FzZSB3aXRoIG5vbi1zdGFuZGFyZGl6ZWQgZGF0YS4gVGhlIHBvc3RlcmlvciBoYXMgbm8gaGlnaCBkZXBlbmRlbmN5LgoKCmBgYHtyIH0KZHJhd3NfbGluX3N0ZCA8LSBmaXRfbGluX3N0ZCRkcmF3cyhmb3JtYXQgPSAiZGYiKQpkcmF3c19saW5fc3RkIHw+CiAgbWNtY19zY2F0dGVyKHBhcnM9YygiYWxwaGEiLCJiZXRhIikpCmBgYAoKCk5leHQgd2UgY2hlY2sgdGhhdCB3ZSBnZXQgc2ltaWxhciBwcm9iYWJpbGl0eSBmb3IgYmV0YT4wLgoKYGBge3Igd2FybmluZz1GQUxTRX0KZHJhd3NfbGluX3N0ZCA8LSBmaXRfbGluX3N0ZCRkcmF3cyhmb3JtYXQgPSAiZGYiKQptZWFuKGRyYXdzX2xpbl9zdGRbLCJiZXRhIl0+MCkgIyBwcm9iYWJpbGl0eSB0aGF0IGJldGEgPiAwCmBgYAoKIyBMaW5lYXIgU3R1ZGVudCdzICR0JCBtb2RlbC4KClRoZSB0ZW1wZXJhdHVyZXMgdXNlZCBpbiB0aGUgYWJvdmUgYW5hbHlzZXMgYXJlIGF2ZXJhZ2VzIG92ZXIgdGhyZWUgbW9udGhzLCB3aGljaCBtYWtlcyBpdCBtb3JlIGxpa2VseSB0aGF0IHRoZXkgYXJlIG5vcm1hbGx5IGRpc3RyaWJ1dGVkLCBidXQgdGhlcmUgY2FuIGJlIGV4dHJlbWUgZXZlbnRzIGluIHRoZSBmZWF0aGVyIGFuZCB3ZSBjYW4gY2hlY2sgd2hldGhlciBtb3JlIHJvYnVzdCBTdHVkZW50J3MgJHQkIG9ic2VydmF0aW9uIG1vZGVsIHdvdWwgZ2l2ZSBkaWZmZXJlbnQgcmVzdWx0cy4KCmBgYHtyIH0KY29kZV9saW5fc3RkX3QgPC0gcm9vdCgiZGVtb3NfcnN0YW4iLCAibGluX3N0ZF90LnN0YW4iKQp3cml0ZUxpbmVzKHJlYWRMaW5lcyhjb2RlX2xpbl9zdGRfdCkpCgoKYGBgCmBgYHtyIHJlc3VsdHM9J2hpZGUnfQptb2RfbGluX3N0ZF90IDwtIGNtZHN0YW5fbW9kZWwoc3Rhbl9maWxlID0gY29kZV9saW5fc3RkX3QpCmZpdF9saW5fc3RkX3QgPC0gbW9kX2xpbl9zdGRfdCRzYW1wbGUoZGF0YSA9IGRhdGFfbGluLCBzZWVkID0gU0VFRCwgcmVmcmVzaD0xMDAwKQpgYGAKCldlIGdldCBzb21lIHdhcm5pbmdzLCBidXQgdGhlc2Ugc3BlY2lmaWMgd2FybmluZ3MgYXJlIG5vdCBjcml0aWNhbCBpZiBjb3VudHMgYXJlIHNtYWxsIGFzIGhlcmUuCgpMZXQncyBleGFtaW5lIGZ1cnRoZXIgZGlhZ25vc3RpY3MuCgpgYGB7ciBtZXNzYWdlPVRSVUV9CmZpdF9saW5fc3RkX3Qkc3VtbWFyeSgpCmZpdF9saW5fc3RkX3QkZGlhZ25vc3RpY19zdW1tYXJ5KGRpYWdub3N0aWNzID0gYygiZGl2ZXJnZW5jZXMiLCAidHJlZWRlcHRoIikpCmBgYAoKV2UgZ2V0IHNpbWlsYXIgZGlhZ25vc3RpY3MgYXMgZm9yIHRoZSBsaW5lYXIgR2F1c3NpYW4gbW9kZWwgd2l0aCBub24tc3RhbmRhcmRpc2VkIGRhdGEuCgpDb21wdXRlIHRoZSBwcm9iYWJpbGl0eSB0aGF0IHRoZSBzdW1tZXIgdGVtcGVyYXR1cmUgaXMgaW5jcmVhc2luZy4KCmBgYHtyIHdhcm5pbmc9RkFMU0V9CmRyYXdzX2xpbl9zdGRfdCA8LSBmaXRfbGluX3N0ZF90JGRyYXdzKGZvcm1hdCA9ICJkZiIpCm1lYW4oZHJhd3NfbGluX3N0ZF90WywiYmV0YSJdPjApICMgcHJvYmFiaWxpdHkgdGhhdCBiZXRhID4gMApgYGAKCldlIGdldCBzaW1pbGFyIHByb2JhYmlsaXR5IGFzIHdpdGggR2F1c3NpYW4gb2JlcnZhdGlvbiBtb2RlbC4KCgpQbG90IGRhdGEgYW5kIHRoZSBtb2RlbCBmaXQKCmBgYHtyIHdhcm5pbmc9RkFMU0V9Cm11IDwtIGRyYXdzX2xpbl9zdGRfdCB8PgogIGFzX3RpYmJsZSgpIHw+CiAgc2VsZWN0KHN0YXJ0c193aXRoKCJtdVsiKSkgfD4KICBhcHBseSgyLCBxdWFudGlsZSwgYygwLjA1LCAwLjUsIDAuOTUpKSB8PgogIHQoKSAlPiUKICBkYXRhLmZyYW1lKHggPSBkYXRhX2xpbiR4LCAuKSAgfD4gCiAgZ2F0aGVyKHBjdCwgeSwgLXgpCgpwZml0IDwtIGdncGxvdCgpICsKICBnZW9tX3BvaW50KGFlcyh4LCB5KSwgZGF0YSA9IGRhdGEuZnJhbWUoZGF0YV9saW4pLCBzaXplID0gMSkgKwogIGdlb21fbGluZShhZXMoeCwgeSwgbGluZXR5cGUgPSBwY3QpLCBkYXRhID0gbXUsIGNvbG9yID0gJ3JlZCcpICsKICBzY2FsZV9saW5ldHlwZV9tYW51YWwodmFsdWVzID0gYygyLDEsMikpICsKICBsYWJzKHkgPSAnU3VtbWVyIHRlbXAuIEBLaWxwaXNqw6RydmknLCB4PSAiWWVhciIpICsKICBndWlkZXMobGluZXR5cGUgPSAibm9uZSIpCnBoaXN0IDwtIG1jbWNfaGlzdChkcmF3c19saW5fc3RkX3QsIHBhcnMgPSBjKCdiZXRhJywnc2lnbWEnLCd5cHJlZCcpKQpncmlkLmFycmFuZ2UocGZpdCwgcGhpc3QsIG5yb3cgPSAyKQpgYGAKCldlIHNlZSBhbHNvIHRoYXQgdGhlIG1hcmdpbmFsIHBvc3RlcmlvciBvZiBudSBpcyB3aWRlIHdpdGggbG90IG9mIG1hc3MgZm9yIHZhbHVlcyBwcm9kdWNpbmcgZGlzdHJidXRpb24gcmVhbGx5IGNsb3NlIHRvIEdhdXNzaWFuLgoKIyBQYXJldG8tc21vb3RoZWQgaW1wb3J0YW5jZS1zYW1wbGluZyBsZWF2ZS1vbmUtb3V0IGNyb3NzLXZhbGlkYXRpb24gKFBTSVMtTE9PKQoKV2UgY2FuIHVzZSBsZWF2ZS1vbmUtb3V0IGNyb3NzLXZhbGlkYXRpb24gdG8gY29tcGFyZSB0aGUgZXhwZWN0ZWQgcHJlZGljdGl2ZSBwZXJmb3JtYW5jZS4gRm9yIHRoZSBmb2xsb3dpbmcgbGluZXMgdG8gd29yaywgdGhlIGxvZy1saWtlbGlob29kIG5lZWRzIHRvIGJlIGV2YWx1YXRlZCBpbiB0aGUgc3RhbiBjb2RlLiBGb3IgYW4gZXhhbXBsZSwgc2VlIGxpbi5zdGFuIGFuZCBbQ29tcHV0aW5nIGFwcHJveGltYXRlIGxlYXZlLW9uZS1vdXQgY3Jvc3MtdmFsaWRhdGlvbiB1c2lnIFBTSVMtTE9PXShodHRwOi8vbWMtc3Rhbi5vcmcvbG9vL2FydGljbGVzL2xvbzItd2l0aC1yc3Rhbi5odG1sKS4KCmBgYHtyIH0KIyBBdCB0aGlzIG1vbWVudCB0aGVyZSBpcyBub3QgeWV0IG1ldGhvZCBmb3IgY21kc3RhbnIgZml0LCBzbyB3ZSB1c2UgdGhpcyBoZWxwZXIKbG9vY21kIDwtIGZ1bmN0aW9uKGZpdCwgLi4uKSB7CiAgbG9vKGZpdCRkcmF3cygibG9nX2xpayIpLCByX2VmZj1yZWxhdGl2ZV9lZmYoZml0JGRyYXdzKCJsb2dfbGlrIikpLCAuLi4pCn0KbG9vX2xpbl9zdGQgPC0gbG9vY21kKGZpdF9saW5fc3RkKQpsb29fbGluX3N0ZF90IDwtIGxvb2NtZChmaXRfbGluX3N0ZF90KQpsb29fY29tcGFyZShsb29fbGluX3N0ZCwgbG9vX2xpbl9zdGRfdCkKYGBgCgpUaGVyZSBpcyBubyBwcmFjdGljYWwgZGlmZmVyZW5jZSBiZXR3ZWVuIEdhdXNzaWFuIGFuZCBTdHVkZW50J3MgJHQkIG9ic2VydmF0aW9uIG1vZGVsIGZvciB0aGlzIGRhdGEuCgoKIyBDb21wYXJpc29uIG9mICRrJCBncm91cHMgd2l0aCBoaWVyYXJjaGljYWwgbW9kZWxzCgpMZXQncyBjb21wYXJlIHRoZSB0ZW1wZXJhdHVyZXMgaW4gdGhyZWUgc3VtbWVyIG1vbnRocy4KCmBgYHtyIH0KZGF0YV9ncnAgPC1saXN0KE4gPSAzKm5yb3coZGF0YV9raWxwaXMpLAogICAgICAgICAgICAgSyA9IDMsCiAgICAgICAgICAgICB4ID0gcmVwKDE6MywgbnJvdyhkYXRhX2tpbHBpcykpLAogICAgICAgICAgICAgeSA9IGModChkYXRhX2tpbHBpc1ssMjo0XSkpKQpgYGAKCiMjIENvbW1vbiB2YXJpYW5jZSAoQU5PVkEpIG1vZGVsCgpgYGB7ciB9CmNvZGVfZ3JwX2FvdiA8LSByb290KCJkZW1vc19yc3RhbiIsICJncnBfYW92LnN0YW4iKQp3cml0ZUxpbmVzKHJlYWRMaW5lcyhjb2RlX2dycF9hb3YpKQpgYGAKCkZpdCB0aGUgbW9kZWwKCmBgYHtyIHJlc3VsdHM9J2hpZGUnfQptb2RfZ3JwIDwtIGNtZHN0YW5fbW9kZWwoc3Rhbl9maWxlID0gY29kZV9ncnBfYW92KQpmaXRfZ3JwIDwtIG1vZF9ncnAkc2FtcGxlKGRhdGEgPSBkYXRhX2dycCwgc2VlZCA9IFNFRUQsIHJlZnJlc2g9MTAwMCkKCmZpdF9ncnAkc3VtbWFyeSgpCmZpdF9ncnAkZGlhZ25vc3RpY19zdW1tYXJ5KGRpYWdub3N0aWNzID0gYygiZGl2ZXJnZW5jZXMiLCAidHJlZWRlcHRoIikpCmBgYAoKIyMgQ29tbW9uIHZhcmlhbmNlIGFuZCBoaWVyYXJjaGljYWwgcHJpb3IgZm9yIG1lYW4uCgpSZXN1bHRzIGRvIG5vdCBkaWZmZXIgbXVjaCBmcm9tIHRoZSBwcmV2aW91cywgYmVjYXVzZSB0aGVyZSBpcyBvbmx5CmZldyBncm91cHMgYW5kIHF1aXRlIG11Y2ggZGF0YSBwZXIgZ3JvdXAsIGJ1dCB0aGlzIHdvcmtzIGFzIGFuIGV4YW1wbGUgb2YgYSBoaWVyYXJjaGljYWwgbW9kZWwuCgpgYGB7ciB9CmNvZGVfZ3JwX3ByaW9yX21lYW4gPC0gcm9vdCgiZGVtb3NfcnN0YW4iLCAiZ3JwX3ByaW9yX21lYW4uc3RhbiIpCndyaXRlTGluZXMocmVhZExpbmVzKGNvZGVfZ3JwX3ByaW9yX21lYW4pKQpgYGAKCkZpdCB0aGUgbW9kZWwKCmBgYHtyIHJlc3VsdHM9J2hpZGUnfQptb2RfZ3JwIDwtIGNtZHN0YW5fbW9kZWwoc3Rhbl9maWxlID0gY29kZV9ncnBfcHJpb3JfbWVhbikKZml0X2dycCA8LSBtb2RfZ3JwJHNhbXBsZShkYXRhID0gZGF0YV9ncnAsIHNlZWQgPSBTRUVELCByZWZyZXNoPTEwMDApCgpmaXRfZ3JwJHN1bW1hcnkoKQpmaXRfZ3JwJGRpYWdub3N0aWNfc3VtbWFyeShkaWFnbm9zdGljcyA9IGMoImRpdmVyZ2VuY2VzIiwgInRyZWVkZXB0aCIpKQpgYGAKCldlIGdvdCBhIHNtYWxsIG51bWJlciBvZiBkaXZlcmdlbmNlcywgc28gd2UgaW5jcmVhc2UKYGFkYXB0X2RlbHRhPTAuOTVgLiBOb3RlIHRoYXQgdGhpc2lzIHVzZWZ1bCBvbmx5IGluIGNhc2Ugb2Ygc21hbGwKbnVtYmVyIG9mIGRpdmVyZ2VuY2VzLCBhbmQgaW5jcmVhc2luZyBgYWRhcHRfZGVsdGE+MC45OWAgZG9lc24ndAp1c3VhbGx5IG1ha2Ugc2Vuc2UsIGFuZCBpbiBzdWNoIGNhc2VzIHRoZXJlIGlzIG5lZWQgdG8gaW52ZXN0aWdhdGUgdGhlCmlkZW50aWZpYWJpbGl0eSBvciBwYXJhbWV0ZXIgdHJhbnNmb3JtYXRpb25zLgoKYGBge3IgcmVzdWx0cz0naGlkZSd9Cm1vZF9ncnAgPC0gY21kc3Rhbl9tb2RlbChzdGFuX2ZpbGUgPSBjb2RlX2dycF9wcmlvcl9tZWFuKQpmaXRfZ3JwIDwtIG1vZF9ncnAkc2FtcGxlKGRhdGEgPSBkYXRhX2dycCwgc2VlZCA9IFNFRUQsIHJlZnJlc2g9MTAwMCwgYWRhcHRfZGVsdGE9MC45NSkKCgpmaXRfZ3JwJHN1bW1hcnkoKQpmaXRfZ3JwJGRpYWdub3N0aWNfc3VtbWFyeShkaWFnbm9zdGljcyA9IGMoImRpdmVyZ2VuY2VzIiwgInRyZWVkZXB0aCIpKQpgYGAKCiMjIFVuZXF1YWwgdmFyaWFuY2UgYW5kIGhpZXJhcmNoaWNhbCBwcmlvciBmb3IgbWVhbiBhbmQgdmFyaWFuY2UKCmBgYHtyIH0KY29kZV9ncnBfcHJpb3JfbWVhbl92YXIgPC0gcm9vdCgiZGVtb3NfcnN0YW4iLCAiZ3JwX3ByaW9yX21lYW5fdmFyLnN0YW4iKQp3cml0ZUxpbmVzKHJlYWRMaW5lcyhjb2RlX2dycF9wcmlvcl9tZWFuX3ZhcikpCmBgYAoKRml0IHRoZSBtb2RlbAoKYGBge3IgcmVzdWx0cz0naGlkZSd9Cm1vZF9ncnAgPC0gY21kc3Rhbl9tb2RlbChzdGFuX2ZpbGUgPSBjb2RlX2dycF9wcmlvcl9tZWFuX3ZhcikKZml0X2dycCA8LSBtb2RfZ3JwJHNhbXBsZShkYXRhID0gZGF0YV9ncnAsIHNlZWQgPSBTRUVELCByZWZyZXNoPTEwMDApCgpmaXRfZ3JwJHN1bW1hcnkoKQpmaXRfZ3JwJGRpYWdub3N0aWNfc3VtbWFyeShkaWFnbm9zdGljcyA9IGMoImRpdmVyZ2VuY2VzIiwgInRyZWVkZXB0aCIpKQpgYGAKCldlIGdvdCBhIHNtYWxsIG51bWJlciBvZiBkaXZlcmdlbmNlcywgc28gd2UgaW5jcmVhc2UKYGFkYXB0X2RlbHRhPTAuOTVgLiBOb3RlIHRoYXQgdGhpc2lzIHVzZWZ1bCBvbmx5IGluIGNhc2Ugb2Ygc21hbGwKbnVtYmVyIG9mIGRpdmVyZ2VuY2VzLCBhbmQgaW5jcmVhc2luZyBgYWRhcHRfZGVsdGE+MC45OWAgZG9lc24ndAp1c3VhbGx5IG1ha2Ugc2Vuc2UsIGFuZCBpbiBzdWNoIGNhc2VzIHRoZXJlIGlzIG5lZWQgdG8gaW52ZXN0aWdhdGUgdGhlCmlkZW50aWZpYWJpbGl0eSBvciBwYXJhbWV0ZXIgdHJhbnNmb3JtYXRpb25zLgoKYGBge3IgcmVzdWx0cz0naGlkZSd9Cm1vZF9ncnAgPC0gY21kc3Rhbl9tb2RlbChzdGFuX2ZpbGUgPSBjb2RlX2dycF9wcmlvcl9tZWFuX3ZhcikKZml0X2dycCA8LSBtb2RfZ3JwJHNhbXBsZShkYXRhID0gZGF0YV9ncnAsIHNlZWQgPSBTRUVELCByZWZyZXNoPTEwMDAsIGFkYXB0X2RlbHRhPTAuOTUpOwoKCmZpdF9ncnAkc3VtbWFyeSgpCmZpdF9ncnAkZGlhZ25vc3RpY19zdW1tYXJ5KGRpYWdub3N0aWNzID0gYygiZGl2ZXJnZW5jZXMiLCAidHJlZWRlcHRoIikpCmBgYAoKUGxvdCB0aGUgcmVzdWx0cwoKYGBge3Igd2FybmluZz1GQUxTRX0KdGVtcHMgPC0gZml0X2dycCRkcmF3cyhmb3JtYXQgPSAiZGYiKSB8PgogIGFzX3RpYmJsZSgpIHw+CiAgc2VsZWN0KHN0YXJ0c193aXRoKCJtdVsiKSkgfD4KICBzZXROYW1lcyhjKCdKdW5lJywnSnVseScsJ0F1Z3VzdCcpKQptY21jX2FyZWFzKHRlbXBzKSArIHhsYWIoJ1RlbXBlcmF0dXJlJykKYGBgCgpQcm9iYWJpbGl0aWVzIHRoYXQgSnVuZSBpcyBob3R0ZXIgdGhhbiBKdWx5LCBKdW5lIGlzIGhvdHRlciB0aGFuIEF1Z3VzdAphbmQgSnVseSBpcyBob3R0ZXIgdGhhbiBBdWd1c3Q6CgpgYGB7ciB3YXJuaW5nPUZBTFNFfQpwYXN0ZSgncChUZW1wSnVuID4gVGVtcEp1bCkgPSAnLCBtZWFuKHRlbXBzJEp1bmUgPiB0ZW1wcyRKdWx5KSkKcGFzdGUoJ3AoVGVtcEp1biA+IFRlbXBBdWcpID0gJywgbWVhbih0ZW1wcyRKdW5lID4gdGVtcHMkQXVndXN0KSkKcGFzdGUoJ3AoVGVtcEp1bCA+IFRlbXBBdWcpID0gJywgbWVhbih0ZW1wcyRKdWx5ID4gdGVtcHMkQXVndXN0KSkKYGBgCgo8YnIgLz4KCiMgTGljZW5zZXMgey51bm51bWJlcmVkfQoKKiBDb2RlICZjb3B5OyAyMDE3LTIwMjAsIEFraSBWZWh0YXJpLCAyMDE3IE1hcmt1cyBQYWFzaW5pZW1pLCBsaWNlbnNlZCB1bmRlciBCU0QtMy4KKiBUZXh0ICZjb3B5OyAyMDE3LTIwMjAsIEFraSBWZWh0YXJpLCBsaWNlbnNlZCB1bmRlciBDQy1CWS1OQyA0LjAuCg==