Differences in slopes or residual variance

library(tidyverse)
library(brms)

Data-generating model

n <- 50
days_per_person <- 50
n_days <- n*days_per_person
set.seed(20191005)
people <- tibble(
  id = 1:n,
  single = rep(0:1, each = n/2),
  single_f = factor(single, 0:1, c("in relationship", "single")),
  average_job_satisfaction = rnorm(n),
  average_relationship_satisfaction = rnorm(n),
  average_well_being = rnorm(n)
)
sd(people$average_well_being)
## [1] 0.94
diary <-  people %>% full_join(
  tibble(
    id = rep(1:n, each = days_per_person),
  ), by = 'id') %>% 
  mutate(
    Job_satisfaction = 0.8 * average_job_satisfaction + 0.6 * rnorm(n_days),
    Relationship_satisfaction = 0.8 * average_relationship_satisfaction + 0.6 * rnorm(n_days),
    residual = rnorm(n_days)
  )
sd(diary$Job_satisfaction)
## [1] 0.99
sd(diary$Relationship_satisfaction)
## [1] 1
sd(diary$residual)
## [1] 0.99

Panel A: Slopes differ

diary <- diary %>%  
  mutate(
    OWB = if_else(single == 1, 0.6, 0.4) * Job_satisfaction + 0.5*average_well_being + 0.7 * residual
  )
sd(diary$OWB)
## [1] 0.96
round(cor(diary %>% select(single, Job_satisfaction, Relationship_satisfaction, OWB)),2)
##                           single Job_satisfaction Relationship_satisfaction
## single                      1.00             0.18                     -0.06
## Job_satisfaction            0.18             1.00                     -0.12
## Relationship_satisfaction  -0.06            -0.12                      1.00
## OWB                         0.03             0.49                      0.00
##                            OWB
## single                    0.03
## Job_satisfaction          0.49
## Relationship_satisfaction 0.00
## OWB                       1.00

Fit LM

model <- brm(OWB ~ Job_satisfaction*single_f + (1 + Job_satisfaction | id), data = diary, cores = 4, backend = "cmdstanr", file = "models/slopes_differ")

diary[,c("estimate__", "std.error__", "lower__", "upper__")] <- fitted(model, re_formula = NA)

Inspect correlations

(cors <- diary %>% group_by(single_f, id) %>%
  summarise(cor = psych::fisherz(cor(Job_satisfaction, OWB)),
            slope = broom::tidy(lm(OWB ~ Job_satisfaction))[2, "estimate"][[1]],
            residual = var(resid(lm(OWB ~ Job_satisfaction))),
            var_prod = var(OWB),
            mean_prod = mean(OWB),
            max_est = max(estimate__),
            max_js = max(Job_satisfaction),
            mean_js = mean(Job_satisfaction),
            var_js = var(Job_satisfaction)) %>% 
  summarise(cor = psych::fisherz2r(mean(cor)),
            slope = mean(slope),
            sd_resid = sqrt(mean(residual)),
            sd_prod = sqrt(mean(var_prod)),
            mean_prod = mean(mean_prod),
            max_js = max(max_js),
            max_est = max(max_est),
            mean_js = mean(mean_js),
            sd_js = sqrt(mean(var_js)))
)
## `summarise()` regrouping output by 'single_f' (override with `.groups` argument)
## `summarise()` ungrouping output (override with `.groups` argument)
## # A tibble: 2 x 10
##   single_f     cor slope sd_resid sd_prod mean_prod max_js max_est mean_js sd_js
##   <fct>      <dbl> <dbl>    <dbl>   <dbl>     <dbl>  <dbl>   <dbl>   <dbl> <dbl>
## 1 in relati… 0.324 0.384    0.678   0.722    0.0912   2.73    1.16 -0.0465 0.606
## 2 single     0.439 0.575    0.685   0.768    0.142    3.27    1.83  0.307  0.581

Scatter plot

m1 <- ggplot(diary, aes(Job_satisfaction, OWB)) +
  facet_wrap(~ single_f) +
  geom_point(alpha = 0.03, color = "#6E9181") +

  geom_smooth(aes(Job_satisfaction, estimate__, ymin = lower__, ymax = upper__), stat = 'identity', color = 'black') +
  
  geom_text(aes(max_js + 0.1, max_est + 0.3, label = sprintf('b==plain("%.2f")', slope)), data = cors, hjust = 0, parse = TRUE, size = 3) +
  scale_color_viridis_d(guide = F, option = "B") +   
  geom_point(alpha = 0.1, color = "#6E9181") +
  
  geom_text(aes(label = sprintf('italic("r")==plain("%.2f")', cor)), x= -3, y=2.5, data = cors, hjust = 0, parse = TRUE, size = 3) +
  
  # geom_text(aes(max_js, y = mean_prod - 0.3, label = sprintf('sigma[y]==plain("%.2f")', sd_prod)),, data = cors, hjust = 0, parse = TRUE, size = 3, angle = 90) +
  # geom_segment(aes(x = max_js + 0.25, y = mean_prod - sd_prod/2, xend = max_js + 0.25, yend = mean_prod + sd_prod/2), data = cors, size = 1, color = "#219FD8") +

  # geom_text(aes(max_js + 0.6, y = mean_prod - 0.5, label = sprintf('sigma[res(y)]==plain("%.2f")', sd_resid)),, data = cors, hjust = 0, parse = TRUE, size = 3, angle = 90) +
  # geom_segment(aes(x = max_js + 0.85, y = mean_prod - sd_resid/2, xend = max_js + 0.85, yend = mean_prod + sd_resid/2), data = cors, size = 1, color = "#219FD8") +

  # geom_text(aes(label = sprintf('sigma[x]==plain("%.2f")', sd_js),  x= mean_js - 0.5, y = -2.8), data = cors, hjust = 0, parse = TRUE, size = 3) +
  # geom_segment(aes(x = mean_js - sd_js/2, y = -3, xend = mean_js + sd_js/2, yend = -3), data = cors, size = 1, color = "#219FD8") +
  
  expand_limits(x = c(4.5), y = c(-3,4)) +
  xlab("Job satisfaction") +
  ylab("Overall well-being") +
  ggthemes::theme_clean()
m1

Panel B: Residual variances, not slopes differ

diary <- diary %>% 
  mutate(
    OWB = 0.4 * Job_satisfaction + 0.5*average_well_being + if_else(single == 1, 0, 0.8) * Relationship_satisfaction + 0.5 * residual
  )

sd(diary$OWB)
## [1] 0.99
round(cor(diary %>% select(single, Job_satisfaction, Relationship_satisfaction, OWB)),2)
##                           single Job_satisfaction Relationship_satisfaction
## single                      1.00             0.18                     -0.06
## Job_satisfaction            0.18             1.00                     -0.12
## Relationship_satisfaction  -0.06            -0.12                      1.00
## OWB                        -0.01             0.32                      0.37
##                             OWB
## single                    -0.01
## Job_satisfaction           0.32
## Relationship_satisfaction  0.37
## OWB                        1.00

Fit LM

model <- brm(OWB ~ Job_satisfaction*single_f + (1 + Job_satisfaction | id), data = diary, cores = 4, backend = "cmdstanr", file = "models/corrs_differ_not_slopes")

diary[,c("estimate__", "std.error__", "lower__", "upper__")] <- fitted(model, re_formula = NA)

Inspect correlations

(cors <- diary %>% group_by(single_f, id) %>%
  summarise(cor = psych::fisherz(cor(Job_satisfaction, OWB)),
            slope = broom::tidy(lm(OWB ~ Job_satisfaction))[2, "estimate"][[1]],
            residual = var(resid(lm(OWB ~ Job_satisfaction))),
            var_prod = var(OWB),
            mean_prod = mean(OWB),
            max_est = max(estimate__),
            max_js = max(Job_satisfaction),
            mean_js = mean(Job_satisfaction),
            var_js = var(Job_satisfaction)) %>% 
  summarise(cor = psych::fisherz2r(mean(cor)),
            slope = mean(slope),
            sd_resid = sqrt(mean(residual)),
            sd_prod = sqrt(mean(var_prod)),
            mean_prod = mean(mean_prod),
            max_js = max(max_js),
            max_est = max(max_est),
            mean_js = mean(mean_js),
            sd_js = sqrt(mean(var_js)))
)
## `summarise()` regrouping output by 'single_f' (override with `.groups` argument)
## `summarise()` ungrouping output (override with `.groups` argument)
## # A tibble: 2 x 10
##   single_f     cor slope sd_resid sd_prod mean_prod max_js max_est mean_js sd_js
##   <fct>      <dbl> <dbl>    <dbl>   <dbl>     <dbl>  <dbl>   <dbl>   <dbl> <dbl>
## 1 in relati… 0.310 0.359    0.670   0.707    0.100    2.73    1.07 -0.0465 0.606
## 2 single     0.414 0.382    0.489   0.541    0.0822   3.27    1.22  0.307  0.581

Scatter plot

m2 <- ggplot(diary, aes(Job_satisfaction, OWB)) +
  facet_wrap(~ single_f) +
  geom_point(alpha = 0.03, color = "#6E9181") +
  # geom_line(aes(color = factor(if_else(id > 25, id - 24.5, as.numeric(id)))), stat = "smooth", method = 'lm', alpha = 0.3) +
  geom_smooth(aes(Job_satisfaction, estimate__, ymin = lower__, ymax = upper__), stat = 'identity', color = 'black') +
  geom_text(aes(max_js + 0.1, max_est + 0.3, label = sprintf('b==plain("%.2f")', slope)), data = cors, hjust = 0, parse = TRUE, size = 3) +
  scale_color_viridis_d(guide = F, option = "B") +   
  geom_point(alpha = 0.1, color = "#6E9181") +
  
  geom_text(aes(label = sprintf('italic("r")==plain("%.2f")', cor)), x= -3, y=2.5, data = cors, hjust = 0, parse = TRUE, size = 3) +
  expand_limits(x = c(4.5), y = c(-3,4)) +
  xlab("Job satisfaction") +
  ylab("Overall well-being") +
  ggthemes::theme_clean()
m2

diary <- diary %>% mutate(
    Job_satisfaction_old = Job_satisfaction,
)

Panel C: Restricted variance in predictor

diary <- diary %>% 
  mutate(
    Job_satisfaction = Job_satisfaction_old,
    Job_satisfaction = if_else(single == 0, Job_satisfaction * 0.7, Job_satisfaction),
    OWB = 0.6 * Job_satisfaction + 0.5*average_well_being + 0.7 * residual
  )
sd(diary$Job_satisfaction)
## [1] 0.87
sd(diary$OWB)
## [1] 0.96
round(cor(diary %>% select(single, Job_satisfaction, Relationship_satisfaction, OWB)),2)
##                           single Job_satisfaction Relationship_satisfaction
## single                      1.00             0.20                     -0.06
## Job_satisfaction            0.20             1.00                     -0.12
## Relationship_satisfaction  -0.06            -0.12                      1.00
## OWB                         0.03             0.49                      0.00
##                            OWB
## single                    0.03
## Job_satisfaction          0.49
## Relationship_satisfaction 0.00
## OWB                       1.00

Fit LM

model <- brm(OWB ~ Job_satisfaction*single_f + (1 + Job_satisfaction | id), data = diary, cores = 4, backend = "cmdstanr", file = "models/restricted_pred")

diary[,c("estimate__", "std.error__", "lower__", "upper__")] <- fitted(model, re_formula = NA)

Inspect correlations

(cors <- diary %>% group_by(single_f, id) %>%
  summarise(cor = psych::fisherz(cor(Job_satisfaction, OWB)),
            slope = broom::tidy(lm(OWB ~ Job_satisfaction))[2, "estimate"][[1]],
            residual = var(resid(lm(OWB ~ Job_satisfaction))),
            var_prod = var(OWB),
            mean_prod = mean(OWB),
            max_est = max(estimate__),
            min_est = min(estimate__),
            max_js = max(Job_satisfaction),
            min_js = min(Job_satisfaction),
            mean_js = mean(Job_satisfaction),
            var_js = var(Job_satisfaction)) %>% 
  summarise(cor = psych::fisherz2r(mean(cor)),
            slope = mean(slope),
            sd_resid = sqrt(mean(residual)),
            sd_prod = sqrt(mean(var_prod)),
            mean_prod = mean(mean_prod),
            max_js = max(max_js),
            min_js = min(min_js),
            max_est = max(max_est),
            min_est = min(min_est),
            mean_js = mean(mean_js),
            sd_js = sqrt(mean(var_js)))
)
## `summarise()` regrouping output by 'single_f' (override with `.groups` argument)
## `summarise()` ungrouping output (override with `.groups` argument)
## # A tibble: 2 x 12
##   single_f   cor slope sd_resid sd_prod mean_prod max_js min_js max_est min_est
##   <fct>    <dbl> <dbl>    <dbl>   <dbl>     <dbl>  <dbl>  <dbl>   <dbl>   <dbl>
## 1 in rela… 0.339 0.577    0.678   0.726    0.0903   1.91  -1.92    1.22   -1.01
## 2 single   0.439 0.575    0.685   0.768    0.142    3.27  -3.18    1.83   -1.84
## # … with 2 more variables: mean_js <dbl>, sd_js <dbl>

Scatter plot

m3 <- ggplot(diary, aes(Job_satisfaction, OWB)) +
  facet_wrap(~ single_f) +
  geom_point(alpha = 0.03, color = "#6E9181") +
  # geom_line(aes(color = factor(if_else(id > 25, id - 24.5, as.numeric(id)))), stat = "smooth", method = 'lm', alpha = 0.3) +
  geom_smooth(aes(Job_satisfaction, estimate__, ymin = lower__, ymax = upper__), stat = 'identity', color = 'black') +
  geom_text(aes(max_js + 0.1, max_est + 0.3, label = sprintf('b==plain("%.2f")', slope)), data = cors, hjust = 0, parse = TRUE, size = 3) +
  scale_color_viridis_d(guide = F, option = "B") +   
  geom_point(alpha = 0.1, color = "#6E9181") +
  
  geom_text(aes(label = sprintf('italic("r")==plain("%.2f")', cor)), x= -3, y=2.5, data = cors, hjust = 0, parse = TRUE, size = 3) +
  
  expand_limits(x = c(-3.7, 4.5), y = c(-3,4)) +
  xlab("Job satisfaction") +
  ylab("Overall well-being") +
  ggthemes::theme_clean()
m3

Altogether

library(cowplot)
plot_grid(m1, m2, m3, ncol = 1, labels = "AUTO")

ggsave("plots/job_satisfaction_singles.pdf", width = 5, height = 7)
ggsave("plots/job_satisfaction_singles.png", width = 6, height = 7)
IyBEaWZmZXJlbmNlcyBpbiBzbG9wZXMgb3IgcmVzaWR1YWwgdmFyaWFuY2UKCmBgYHtyfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShicm1zKQpgYGAKCiMjIyBEYXRhLWdlbmVyYXRpbmcgbW9kZWwKYGBge3J9Cm4gPC0gNTAKZGF5c19wZXJfcGVyc29uIDwtIDUwCm5fZGF5cyA8LSBuKmRheXNfcGVyX3BlcnNvbgpzZXQuc2VlZCgyMDE5MTAwNSkKcGVvcGxlIDwtIHRpYmJsZSgKICBpZCA9IDE6biwKICBzaW5nbGUgPSByZXAoMDoxLCBlYWNoID0gbi8yKSwKICBzaW5nbGVfZiA9IGZhY3RvcihzaW5nbGUsIDA6MSwgYygiaW4gcmVsYXRpb25zaGlwIiwgInNpbmdsZSIpKSwKICBhdmVyYWdlX2pvYl9zYXRpc2ZhY3Rpb24gPSBybm9ybShuKSwKICBhdmVyYWdlX3JlbGF0aW9uc2hpcF9zYXRpc2ZhY3Rpb24gPSBybm9ybShuKSwKICBhdmVyYWdlX3dlbGxfYmVpbmcgPSBybm9ybShuKQopCnNkKHBlb3BsZSRhdmVyYWdlX3dlbGxfYmVpbmcpCgpkaWFyeSA8LSAgcGVvcGxlICU+JSBmdWxsX2pvaW4oCiAgdGliYmxlKAogICAgaWQgPSByZXAoMTpuLCBlYWNoID0gZGF5c19wZXJfcGVyc29uKSwKICApLCBieSA9ICdpZCcpICU+JSAKICBtdXRhdGUoCiAgICBKb2Jfc2F0aXNmYWN0aW9uID0gMC44ICogYXZlcmFnZV9qb2Jfc2F0aXNmYWN0aW9uICsgMC42ICogcm5vcm0obl9kYXlzKSwKICAgIFJlbGF0aW9uc2hpcF9zYXRpc2ZhY3Rpb24gPSAwLjggKiBhdmVyYWdlX3JlbGF0aW9uc2hpcF9zYXRpc2ZhY3Rpb24gKyAwLjYgKiBybm9ybShuX2RheXMpLAogICAgcmVzaWR1YWwgPSBybm9ybShuX2RheXMpCiAgKQpzZChkaWFyeSRKb2Jfc2F0aXNmYWN0aW9uKQpzZChkaWFyeSRSZWxhdGlvbnNoaXBfc2F0aXNmYWN0aW9uKQpzZChkaWFyeSRyZXNpZHVhbCkKYGBgCgojIyBQYW5lbCBBOiBTbG9wZXMgZGlmZmVyCmBgYHtyfQpkaWFyeSA8LSBkaWFyeSAlPiUgIAogIG11dGF0ZSgKICAgIE9XQiA9IGlmX2Vsc2Uoc2luZ2xlID09IDEsIDAuNiwgMC40KSAqIEpvYl9zYXRpc2ZhY3Rpb24gKyAwLjUqYXZlcmFnZV93ZWxsX2JlaW5nICsgMC43ICogcmVzaWR1YWwKICApCnNkKGRpYXJ5JE9XQikKcm91bmQoY29yKGRpYXJ5ICU+JSBzZWxlY3Qoc2luZ2xlLCBKb2Jfc2F0aXNmYWN0aW9uLCBSZWxhdGlvbnNoaXBfc2F0aXNmYWN0aW9uLCBPV0IpKSwyKQpgYGAKCiMjIyBGaXQgTE0KYGBge3J9Cm1vZGVsIDwtIGJybShPV0IgfiBKb2Jfc2F0aXNmYWN0aW9uKnNpbmdsZV9mICsgKDEgKyBKb2Jfc2F0aXNmYWN0aW9uIHwgaWQpLCBkYXRhID0gZGlhcnksIGNvcmVzID0gNCwgYmFja2VuZCA9ICJjbWRzdGFuciIsIGZpbGUgPSAibW9kZWxzL3Nsb3Blc19kaWZmZXIiKQoKZGlhcnlbLGMoImVzdGltYXRlX18iLCAic3RkLmVycm9yX18iLCAibG93ZXJfXyIsICJ1cHBlcl9fIildIDwtIGZpdHRlZChtb2RlbCwgcmVfZm9ybXVsYSA9IE5BKQpgYGAKCgojIyMgSW5zcGVjdCBjb3JyZWxhdGlvbnMKYGBge3J9Cihjb3JzIDwtIGRpYXJ5ICU+JSBncm91cF9ieShzaW5nbGVfZiwgaWQpICU+JQogIHN1bW1hcmlzZShjb3IgPSBwc3ljaDo6ZmlzaGVyeihjb3IoSm9iX3NhdGlzZmFjdGlvbiwgT1dCKSksCiAgICAgICAgICAgIHNsb3BlID0gYnJvb206OnRpZHkobG0oT1dCIH4gSm9iX3NhdGlzZmFjdGlvbikpWzIsICJlc3RpbWF0ZSJdW1sxXV0sCiAgICAgICAgICAgIHJlc2lkdWFsID0gdmFyKHJlc2lkKGxtKE9XQiB+IEpvYl9zYXRpc2ZhY3Rpb24pKSksCiAgICAgICAgICAgIHZhcl9wcm9kID0gdmFyKE9XQiksCiAgICAgICAgICAgIG1lYW5fcHJvZCA9IG1lYW4oT1dCKSwKICAgICAgICAgICAgbWF4X2VzdCA9IG1heChlc3RpbWF0ZV9fKSwKICAgICAgICAgICAgbWF4X2pzID0gbWF4KEpvYl9zYXRpc2ZhY3Rpb24pLAogICAgICAgICAgICBtZWFuX2pzID0gbWVhbihKb2Jfc2F0aXNmYWN0aW9uKSwKICAgICAgICAgICAgdmFyX2pzID0gdmFyKEpvYl9zYXRpc2ZhY3Rpb24pKSAlPiUgCiAgc3VtbWFyaXNlKGNvciA9IHBzeWNoOjpmaXNoZXJ6MnIobWVhbihjb3IpKSwKICAgICAgICAgICAgc2xvcGUgPSBtZWFuKHNsb3BlKSwKICAgICAgICAgICAgc2RfcmVzaWQgPSBzcXJ0KG1lYW4ocmVzaWR1YWwpKSwKICAgICAgICAgICAgc2RfcHJvZCA9IHNxcnQobWVhbih2YXJfcHJvZCkpLAogICAgICAgICAgICBtZWFuX3Byb2QgPSBtZWFuKG1lYW5fcHJvZCksCiAgICAgICAgICAgIG1heF9qcyA9IG1heChtYXhfanMpLAogICAgICAgICAgICBtYXhfZXN0ID0gbWF4KG1heF9lc3QpLAogICAgICAgICAgICBtZWFuX2pzID0gbWVhbihtZWFuX2pzKSwKICAgICAgICAgICAgc2RfanMgPSBzcXJ0KG1lYW4odmFyX2pzKSkpCikKYGBgCgojIyMgU2NhdHRlciBwbG90CmBgYHtyfQoKbTEgPC0gZ2dwbG90KGRpYXJ5LCBhZXMoSm9iX3NhdGlzZmFjdGlvbiwgT1dCKSkgKwogIGZhY2V0X3dyYXAofiBzaW5nbGVfZikgKwogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjAzLCBjb2xvciA9ICIjNkU5MTgxIikgKwoKICBnZW9tX3Ntb290aChhZXMoSm9iX3NhdGlzZmFjdGlvbiwgZXN0aW1hdGVfXywgeW1pbiA9IGxvd2VyX18sIHltYXggPSB1cHBlcl9fKSwgc3RhdCA9ICdpZGVudGl0eScsIGNvbG9yID0gJ2JsYWNrJykgKwogIAogIGdlb21fdGV4dChhZXMobWF4X2pzICsgMC4xLCBtYXhfZXN0ICsgMC4zLCBsYWJlbCA9IHNwcmludGYoJ2I9PXBsYWluKCIlLjJmIiknLCBzbG9wZSkpLCBkYXRhID0gY29ycywgaGp1c3QgPSAwLCBwYXJzZSA9IFRSVUUsIHNpemUgPSAzKSArCiAgc2NhbGVfY29sb3JfdmlyaWRpc19kKGd1aWRlID0gRiwgb3B0aW9uID0gIkIiKSArICAgCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuMSwgY29sb3IgPSAiIzZFOTE4MSIpICsKICAKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gc3ByaW50ZignaXRhbGljKCJyIik9PXBsYWluKCIlLjJmIiknLCBjb3IpKSwgeD0gLTMsIHk9Mi41LCBkYXRhID0gY29ycywgaGp1c3QgPSAwLCBwYXJzZSA9IFRSVUUsIHNpemUgPSAzKSArCiAgCiAgIyBnZW9tX3RleHQoYWVzKG1heF9qcywgeSA9IG1lYW5fcHJvZCAtIDAuMywgbGFiZWwgPSBzcHJpbnRmKCdzaWdtYVt5XT09cGxhaW4oIiUuMmYiKScsIHNkX3Byb2QpKSwsIGRhdGEgPSBjb3JzLCBoanVzdCA9IDAsIHBhcnNlID0gVFJVRSwgc2l6ZSA9IDMsIGFuZ2xlID0gOTApICsKICAjIGdlb21fc2VnbWVudChhZXMoeCA9IG1heF9qcyArIDAuMjUsIHkgPSBtZWFuX3Byb2QgLSBzZF9wcm9kLzIsIHhlbmQgPSBtYXhfanMgKyAwLjI1LCB5ZW5kID0gbWVhbl9wcm9kICsgc2RfcHJvZC8yKSwgZGF0YSA9IGNvcnMsIHNpemUgPSAxLCBjb2xvciA9ICIjMjE5RkQ4IikgKwoKICAjIGdlb21fdGV4dChhZXMobWF4X2pzICsgMC42LCB5ID0gbWVhbl9wcm9kIC0gMC41LCBsYWJlbCA9IHNwcmludGYoJ3NpZ21hW3Jlcyh5KV09PXBsYWluKCIlLjJmIiknLCBzZF9yZXNpZCkpLCwgZGF0YSA9IGNvcnMsIGhqdXN0ID0gMCwgcGFyc2UgPSBUUlVFLCBzaXplID0gMywgYW5nbGUgPSA5MCkgKwogICMgZ2VvbV9zZWdtZW50KGFlcyh4ID0gbWF4X2pzICsgMC44NSwgeSA9IG1lYW5fcHJvZCAtIHNkX3Jlc2lkLzIsIHhlbmQgPSBtYXhfanMgKyAwLjg1LCB5ZW5kID0gbWVhbl9wcm9kICsgc2RfcmVzaWQvMiksIGRhdGEgPSBjb3JzLCBzaXplID0gMSwgY29sb3IgPSAiIzIxOUZEOCIpICsKCiAgIyBnZW9tX3RleHQoYWVzKGxhYmVsID0gc3ByaW50Zignc2lnbWFbeF09PXBsYWluKCIlLjJmIiknLCBzZF9qcyksICB4PSBtZWFuX2pzIC0gMC41LCB5ID0gLTIuOCksIGRhdGEgPSBjb3JzLCBoanVzdCA9IDAsIHBhcnNlID0gVFJVRSwgc2l6ZSA9IDMpICsKICAjIGdlb21fc2VnbWVudChhZXMoeCA9IG1lYW5fanMgLSBzZF9qcy8yLCB5ID0gLTMsIHhlbmQgPSBtZWFuX2pzICsgc2RfanMvMiwgeWVuZCA9IC0zKSwgZGF0YSA9IGNvcnMsIHNpemUgPSAxLCBjb2xvciA9ICIjMjE5RkQ4IikgKwogIAogIGV4cGFuZF9saW1pdHMoeCA9IGMoNC41KSwgeSA9IGMoLTMsNCkpICsKICB4bGFiKCJKb2Igc2F0aXNmYWN0aW9uIikgKwogIHlsYWIoIk92ZXJhbGwgd2VsbC1iZWluZyIpICsKICBnZ3RoZW1lczo6dGhlbWVfY2xlYW4oKQptMQpgYGAKCgojIyBQYW5lbCBCOiBSZXNpZHVhbCB2YXJpYW5jZXMsIG5vdCBzbG9wZXMgZGlmZmVyCmBgYHtyfQpkaWFyeSA8LSBkaWFyeSAlPiUgCiAgbXV0YXRlKAogICAgT1dCID0gMC40ICogSm9iX3NhdGlzZmFjdGlvbiArIDAuNSphdmVyYWdlX3dlbGxfYmVpbmcgKyBpZl9lbHNlKHNpbmdsZSA9PSAxLCAwLCAwLjgpICogUmVsYXRpb25zaGlwX3NhdGlzZmFjdGlvbiArIDAuNSAqIHJlc2lkdWFsCiAgKQoKc2QoZGlhcnkkT1dCKQpyb3VuZChjb3IoZGlhcnkgJT4lIHNlbGVjdChzaW5nbGUsIEpvYl9zYXRpc2ZhY3Rpb24sIFJlbGF0aW9uc2hpcF9zYXRpc2ZhY3Rpb24sIE9XQikpLDIpCmBgYAoKIyMjIEZpdCBMTQpgYGB7cn0KbW9kZWwgPC0gYnJtKE9XQiB+IEpvYl9zYXRpc2ZhY3Rpb24qc2luZ2xlX2YgKyAoMSArIEpvYl9zYXRpc2ZhY3Rpb24gfCBpZCksIGRhdGEgPSBkaWFyeSwgY29yZXMgPSA0LCBiYWNrZW5kID0gImNtZHN0YW5yIiwgZmlsZSA9ICJtb2RlbHMvY29ycnNfZGlmZmVyX25vdF9zbG9wZXMiKQoKZGlhcnlbLGMoImVzdGltYXRlX18iLCAic3RkLmVycm9yX18iLCAibG93ZXJfXyIsICJ1cHBlcl9fIildIDwtIGZpdHRlZChtb2RlbCwgcmVfZm9ybXVsYSA9IE5BKQpgYGAKCgojIyMgSW5zcGVjdCBjb3JyZWxhdGlvbnMKYGBge3J9Cihjb3JzIDwtIGRpYXJ5ICU+JSBncm91cF9ieShzaW5nbGVfZiwgaWQpICU+JQogIHN1bW1hcmlzZShjb3IgPSBwc3ljaDo6ZmlzaGVyeihjb3IoSm9iX3NhdGlzZmFjdGlvbiwgT1dCKSksCiAgICAgICAgICAgIHNsb3BlID0gYnJvb206OnRpZHkobG0oT1dCIH4gSm9iX3NhdGlzZmFjdGlvbikpWzIsICJlc3RpbWF0ZSJdW1sxXV0sCiAgICAgICAgICAgIHJlc2lkdWFsID0gdmFyKHJlc2lkKGxtKE9XQiB+IEpvYl9zYXRpc2ZhY3Rpb24pKSksCiAgICAgICAgICAgIHZhcl9wcm9kID0gdmFyKE9XQiksCiAgICAgICAgICAgIG1lYW5fcHJvZCA9IG1lYW4oT1dCKSwKICAgICAgICAgICAgbWF4X2VzdCA9IG1heChlc3RpbWF0ZV9fKSwKICAgICAgICAgICAgbWF4X2pzID0gbWF4KEpvYl9zYXRpc2ZhY3Rpb24pLAogICAgICAgICAgICBtZWFuX2pzID0gbWVhbihKb2Jfc2F0aXNmYWN0aW9uKSwKICAgICAgICAgICAgdmFyX2pzID0gdmFyKEpvYl9zYXRpc2ZhY3Rpb24pKSAlPiUgCiAgc3VtbWFyaXNlKGNvciA9IHBzeWNoOjpmaXNoZXJ6MnIobWVhbihjb3IpKSwKICAgICAgICAgICAgc2xvcGUgPSBtZWFuKHNsb3BlKSwKICAgICAgICAgICAgc2RfcmVzaWQgPSBzcXJ0KG1lYW4ocmVzaWR1YWwpKSwKICAgICAgICAgICAgc2RfcHJvZCA9IHNxcnQobWVhbih2YXJfcHJvZCkpLAogICAgICAgICAgICBtZWFuX3Byb2QgPSBtZWFuKG1lYW5fcHJvZCksCiAgICAgICAgICAgIG1heF9qcyA9IG1heChtYXhfanMpLAogICAgICAgICAgICBtYXhfZXN0ID0gbWF4KG1heF9lc3QpLAogICAgICAgICAgICBtZWFuX2pzID0gbWVhbihtZWFuX2pzKSwKICAgICAgICAgICAgc2RfanMgPSBzcXJ0KG1lYW4odmFyX2pzKSkpCikKYGBgCgojIyMgU2NhdHRlciBwbG90CmBgYHtyfQoKbTIgPC0gZ2dwbG90KGRpYXJ5LCBhZXMoSm9iX3NhdGlzZmFjdGlvbiwgT1dCKSkgKwogIGZhY2V0X3dyYXAofiBzaW5nbGVfZikgKwogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjAzLCBjb2xvciA9ICIjNkU5MTgxIikgKwogICMgZ2VvbV9saW5lKGFlcyhjb2xvciA9IGZhY3RvcihpZl9lbHNlKGlkID4gMjUsIGlkIC0gMjQuNSwgYXMubnVtZXJpYyhpZCkpKSksIHN0YXQgPSAic21vb3RoIiwgbWV0aG9kID0gJ2xtJywgYWxwaGEgPSAwLjMpICsKICBnZW9tX3Ntb290aChhZXMoSm9iX3NhdGlzZmFjdGlvbiwgZXN0aW1hdGVfXywgeW1pbiA9IGxvd2VyX18sIHltYXggPSB1cHBlcl9fKSwgc3RhdCA9ICdpZGVudGl0eScsIGNvbG9yID0gJ2JsYWNrJykgKwogIGdlb21fdGV4dChhZXMobWF4X2pzICsgMC4xLCBtYXhfZXN0ICsgMC4zLCBsYWJlbCA9IHNwcmludGYoJ2I9PXBsYWluKCIlLjJmIiknLCBzbG9wZSkpLCBkYXRhID0gY29ycywgaGp1c3QgPSAwLCBwYXJzZSA9IFRSVUUsIHNpemUgPSAzKSArCiAgc2NhbGVfY29sb3JfdmlyaWRpc19kKGd1aWRlID0gRiwgb3B0aW9uID0gIkIiKSArICAgCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuMSwgY29sb3IgPSAiIzZFOTE4MSIpICsKICAKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gc3ByaW50ZignaXRhbGljKCJyIik9PXBsYWluKCIlLjJmIiknLCBjb3IpKSwgeD0gLTMsIHk9Mi41LCBkYXRhID0gY29ycywgaGp1c3QgPSAwLCBwYXJzZSA9IFRSVUUsIHNpemUgPSAzKSArCiAgZXhwYW5kX2xpbWl0cyh4ID0gYyg0LjUpLCB5ID0gYygtMyw0KSkgKwogIHhsYWIoIkpvYiBzYXRpc2ZhY3Rpb24iKSArCiAgeWxhYigiT3ZlcmFsbCB3ZWxsLWJlaW5nIikgKwogIGdndGhlbWVzOjp0aGVtZV9jbGVhbigpCm0yCmBgYAoKCgpgYGB7cn0KZGlhcnkgPC0gZGlhcnkgJT4lIG11dGF0ZSgKICAgIEpvYl9zYXRpc2ZhY3Rpb25fb2xkID0gSm9iX3NhdGlzZmFjdGlvbiwKKQpgYGAKCiMjIFBhbmVsIEM6IFJlc3RyaWN0ZWQgdmFyaWFuY2UgaW4gcHJlZGljdG9yCmBgYHtyfQpkaWFyeSA8LSBkaWFyeSAlPiUgCiAgbXV0YXRlKAogICAgSm9iX3NhdGlzZmFjdGlvbiA9IEpvYl9zYXRpc2ZhY3Rpb25fb2xkLAogICAgSm9iX3NhdGlzZmFjdGlvbiA9IGlmX2Vsc2Uoc2luZ2xlID09IDAsIEpvYl9zYXRpc2ZhY3Rpb24gKiAwLjcsIEpvYl9zYXRpc2ZhY3Rpb24pLAogICAgT1dCID0gMC42ICogSm9iX3NhdGlzZmFjdGlvbiArIDAuNSphdmVyYWdlX3dlbGxfYmVpbmcgKyAwLjcgKiByZXNpZHVhbAogICkKc2QoZGlhcnkkSm9iX3NhdGlzZmFjdGlvbikKc2QoZGlhcnkkT1dCKQpyb3VuZChjb3IoZGlhcnkgJT4lIHNlbGVjdChzaW5nbGUsIEpvYl9zYXRpc2ZhY3Rpb24sIFJlbGF0aW9uc2hpcF9zYXRpc2ZhY3Rpb24sIE9XQikpLDIpCmBgYAoKCiMjIyBGaXQgTE0KYGBge3J9Cm1vZGVsIDwtIGJybShPV0IgfiBKb2Jfc2F0aXNmYWN0aW9uKnNpbmdsZV9mICsgKDEgKyBKb2Jfc2F0aXNmYWN0aW9uIHwgaWQpLCBkYXRhID0gZGlhcnksIGNvcmVzID0gNCwgYmFja2VuZCA9ICJjbWRzdGFuciIsIGZpbGUgPSAibW9kZWxzL3Jlc3RyaWN0ZWRfcHJlZCIpCgpkaWFyeVssYygiZXN0aW1hdGVfXyIsICJzdGQuZXJyb3JfXyIsICJsb3dlcl9fIiwgInVwcGVyX18iKV0gPC0gZml0dGVkKG1vZGVsLCByZV9mb3JtdWxhID0gTkEpCmBgYAoKCiMjIyBJbnNwZWN0IGNvcnJlbGF0aW9ucwpgYGB7cn0KKGNvcnMgPC0gZGlhcnkgJT4lIGdyb3VwX2J5KHNpbmdsZV9mLCBpZCkgJT4lCiAgc3VtbWFyaXNlKGNvciA9IHBzeWNoOjpmaXNoZXJ6KGNvcihKb2Jfc2F0aXNmYWN0aW9uLCBPV0IpKSwKICAgICAgICAgICAgc2xvcGUgPSBicm9vbTo6dGlkeShsbShPV0IgfiBKb2Jfc2F0aXNmYWN0aW9uKSlbMiwgImVzdGltYXRlIl1bWzFdXSwKICAgICAgICAgICAgcmVzaWR1YWwgPSB2YXIocmVzaWQobG0oT1dCIH4gSm9iX3NhdGlzZmFjdGlvbikpKSwKICAgICAgICAgICAgdmFyX3Byb2QgPSB2YXIoT1dCKSwKICAgICAgICAgICAgbWVhbl9wcm9kID0gbWVhbihPV0IpLAogICAgICAgICAgICBtYXhfZXN0ID0gbWF4KGVzdGltYXRlX18pLAogICAgICAgICAgICBtaW5fZXN0ID0gbWluKGVzdGltYXRlX18pLAogICAgICAgICAgICBtYXhfanMgPSBtYXgoSm9iX3NhdGlzZmFjdGlvbiksCiAgICAgICAgICAgIG1pbl9qcyA9IG1pbihKb2Jfc2F0aXNmYWN0aW9uKSwKICAgICAgICAgICAgbWVhbl9qcyA9IG1lYW4oSm9iX3NhdGlzZmFjdGlvbiksCiAgICAgICAgICAgIHZhcl9qcyA9IHZhcihKb2Jfc2F0aXNmYWN0aW9uKSkgJT4lIAogIHN1bW1hcmlzZShjb3IgPSBwc3ljaDo6ZmlzaGVyejJyKG1lYW4oY29yKSksCiAgICAgICAgICAgIHNsb3BlID0gbWVhbihzbG9wZSksCiAgICAgICAgICAgIHNkX3Jlc2lkID0gc3FydChtZWFuKHJlc2lkdWFsKSksCiAgICAgICAgICAgIHNkX3Byb2QgPSBzcXJ0KG1lYW4odmFyX3Byb2QpKSwKICAgICAgICAgICAgbWVhbl9wcm9kID0gbWVhbihtZWFuX3Byb2QpLAogICAgICAgICAgICBtYXhfanMgPSBtYXgobWF4X2pzKSwKICAgICAgICAgICAgbWluX2pzID0gbWluKG1pbl9qcyksCiAgICAgICAgICAgIG1heF9lc3QgPSBtYXgobWF4X2VzdCksCiAgICAgICAgICAgIG1pbl9lc3QgPSBtaW4obWluX2VzdCksCiAgICAgICAgICAgIG1lYW5fanMgPSBtZWFuKG1lYW5fanMpLAogICAgICAgICAgICBzZF9qcyA9IHNxcnQobWVhbih2YXJfanMpKSkKKQpgYGAKCiMjIyBTY2F0dGVyIHBsb3QKYGBge3J9CgptMyA8LSBnZ3Bsb3QoZGlhcnksIGFlcyhKb2Jfc2F0aXNmYWN0aW9uLCBPV0IpKSArCiAgZmFjZXRfd3JhcCh+IHNpbmdsZV9mKSArCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuMDMsIGNvbG9yID0gIiM2RTkxODEiKSArCiAgIyBnZW9tX2xpbmUoYWVzKGNvbG9yID0gZmFjdG9yKGlmX2Vsc2UoaWQgPiAyNSwgaWQgLSAyNC41LCBhcy5udW1lcmljKGlkKSkpKSwgc3RhdCA9ICJzbW9vdGgiLCBtZXRob2QgPSAnbG0nLCBhbHBoYSA9IDAuMykgKwogIGdlb21fc21vb3RoKGFlcyhKb2Jfc2F0aXNmYWN0aW9uLCBlc3RpbWF0ZV9fLCB5bWluID0gbG93ZXJfXywgeW1heCA9IHVwcGVyX18pLCBzdGF0ID0gJ2lkZW50aXR5JywgY29sb3IgPSAnYmxhY2snKSArCiAgZ2VvbV90ZXh0KGFlcyhtYXhfanMgKyAwLjEsIG1heF9lc3QgKyAwLjMsIGxhYmVsID0gc3ByaW50ZignYj09cGxhaW4oIiUuMmYiKScsIHNsb3BlKSksIGRhdGEgPSBjb3JzLCBoanVzdCA9IDAsIHBhcnNlID0gVFJVRSwgc2l6ZSA9IDMpICsKICBzY2FsZV9jb2xvcl92aXJpZGlzX2QoZ3VpZGUgPSBGLCBvcHRpb24gPSAiQiIpICsgICAKICBnZW9tX3BvaW50KGFscGhhID0gMC4xLCBjb2xvciA9ICIjNkU5MTgxIikgKwogIAogIGdlb21fdGV4dChhZXMobGFiZWwgPSBzcHJpbnRmKCdpdGFsaWMoInIiKT09cGxhaW4oIiUuMmYiKScsIGNvcikpLCB4PSAtMywgeT0yLjUsIGRhdGEgPSBjb3JzLCBoanVzdCA9IDAsIHBhcnNlID0gVFJVRSwgc2l6ZSA9IDMpICsKICAKICBleHBhbmRfbGltaXRzKHggPSBjKC0zLjcsIDQuNSksIHkgPSBjKC0zLDQpKSArCiAgeGxhYigiSm9iIHNhdGlzZmFjdGlvbiIpICsKICB5bGFiKCJPdmVyYWxsIHdlbGwtYmVpbmciKSArCiAgZ2d0aGVtZXM6OnRoZW1lX2NsZWFuKCkKbTMKYGBgCgoKIyMgQWx0b2dldGhlcgpgYGB7cn0KbGlicmFyeShjb3dwbG90KQpwbG90X2dyaWQobTEsIG0yLCBtMywgbmNvbCA9IDEsIGxhYmVscyA9ICJBVVRPIikKZ2dzYXZlKCJwbG90cy9qb2Jfc2F0aXNmYWN0aW9uX3NpbmdsZXMucGRmIiwgd2lkdGggPSA1LCBoZWlnaHQgPSA3KQpnZ3NhdmUoInBsb3RzL2pvYl9zYXRpc2ZhY3Rpb25fc2luZ2xlcy5wbmciLCB3aWR0aCA9IDYsIGhlaWdodCA9IDcpCmBgYAoK