1. Packages and data import

# Install packages if needed:
# install.packages("readxl")
# install.packages("dplyr")
# install.packages("tidyr")
# install.packages("ggplot2")
# install.packages("lme4")

library(readxl)
library(dplyr)
library(tidyr)
library(ggplot2)
library(lme4)
# Change file path if necessary
file <- "/Users/mette/Desktop/NTNU/Learning analytics/DataSet-ProTuS.xlsx"

learners <- read_excel(file, sheet = "Learners") %>%
  select(where(~ !all(is.na(.))))

actions_raw <- read_excel(file, sheet = "Users actions") %>%
  select(where(~ !all(is.na(.))))

action_types_raw <- read_excel(file, sheet = "Action type") %>%
  select(where(~ !all(is.na(.))))

2. Initial data inspection

dim(learners)
[1] 116   5
dim(actions_raw)
[1] 13247    13
names(learners)
[1] "id"             "year"           "masteryID"      "ELOrating"      "iteraction_num"
names(actions_raw)
 [1] "idUsers_action"        "learner"               "lesson"                "type"                  "menu_item"             "learning_content_name" "category"              "Elo_rating_student"    "Elo_rating_content"   
[10] "session"               "changed_item"          "learning_content_type" "time"                 
nrow(learners)
[1] 116
n_distinct(actions_raw$learner)
[1] 81
sum(duplicated(actions_raw))
[1] 0
colSums(is.na(actions_raw))
       idUsers_action               learner                lesson                  type             menu_item learning_content_name              category    Elo_rating_student    Elo_rating_content               session 
                    0                     0                     0                     1                     1                     1                     1                     1                     1                     1 
         changed_item learning_content_type                  time 
                    1                     1                     1 

The Learners sheet contains 116 learners. Not all learners are represented in the provided action log.

learner_action_coverage <- learners %>%
  mutate(has_action_records = id %in% actions_raw$learner) %>%
  count(has_action_records)

print(learner_action_coverage)
# A tibble: 2 × 2
  has_action_records     n
  <lgl>              <int>
1 FALSE                 35
2 TRUE                  81

3. Data cleaning

Create a clean lookup table for action types.

action_lookup <- action_types_raw %>%
  select(idaction_type, action_type) %>%
  filter(!is.na(idaction_type)) %>%
  distinct(idaction_type, .keep_all = TRUE)

# Verify that each action type ID occurs only once
action_lookup %>%
  count(idaction_type) %>%
  filter(n > 1)

Remove the single malformed action record with a missing action type and add the action labels.

actions_clean <- actions_raw %>%
  filter(!is.na(type)) %>%
  left_join(
    action_lookup,
    by = c("type" = "idaction_type"),
    relationship = "many-to-one"
  ) %>%
  arrange(learner, time, idUsers_action)

Verify the cleaned dataset.

data_summary <- tibble(
  measure = c(
    "Learners in Learners sheet",
    "Learners represented in action log",
    "Raw action records",
    "Clean action records"
  ),
  n = c(
    nrow(learners),
    n_distinct(actions_raw$learner),
    nrow(actions_raw),
    nrow(actions_clean)
  )
)

print(data_summary)
# A tibble: 4 × 2
  measure                                n
  <chr>                              <int>
1 Learners in Learners sheet           116
2 Learners represented in action log    81
3 Raw action records                 13247
4 Clean action records               13246
# Verify that action IDs are unique after cleaning
stopifnot(sum(duplicated(actions_clean$idUsers_action)) == 0)

4. Elo events and exercise data

Check which action types contain non-zero Elo values.

elo_events <- actions_clean %>%
  filter(Elo_rating_student != 0)

elo_events %>%
  count(type, action_type, sort = TRUE) %>%
  print(n = Inf)
# A tibble: 1 × 3
   type action_type          n
  <dbl> <chr>            <int>
1    24 exercise started  2269
nrow(elo_events)
[1] 2269
n_distinct(elo_events$learner)
[1] 62
summary(elo_events$Elo_rating_student)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   1232    1332    1368    1394    1455    1672 

All observed non-zero Elo values occur on coding exercise events (type 24).

Construct the exercise-level data. Elo change is calculated between consecutive observed exercise events within each learner.

exercise_data <- actions_clean %>%
  filter(type == 24) %>%
  arrange(learner, time, idUsers_action) %>%
  group_by(learner) %>%
  mutate(
    exercise_number = row_number(),
    previous_Elo = lag(Elo_rating_student),
    Elo_gain = Elo_rating_student - previous_Elo
  ) %>%
  ungroup()

The first observed exercise event for each learner has no preceding observed Elo value and therefore has no calculable Elo change.

exercise_data %>%
  summarise(
    n_exercises = n(),
    n_learners = n_distinct(learner),
    n_Elo_transitions = sum(!is.na(Elo_gain)),
    mean_gain = mean(Elo_gain, na.rm = TRUE),
    median_gain = median(Elo_gain, na.rm = TRUE),
    sd_gain = sd(Elo_gain, na.rm = TRUE),
    min_gain = min(Elo_gain, na.rm = TRUE),
    max_gain = max(Elo_gain, na.rm = TRUE),
    positive = sum(Elo_gain > 0, na.rm = TRUE),
    zero = sum(Elo_gain == 0, na.rm = TRUE),
    negative = sum(Elo_gain < 0, na.rm = TRUE)
  )

5. Construct preparatory engagement variables

The relevant action types are:

Preparatory engagement is defined as example and challenge engagement occurring after the preceding observed exercise and before the current exercise.

actions_sequence <- actions_clean %>%
  arrange(learner, time, idUsers_action) %>%
  group_by(learner) %>%
  mutate(
    exercises_so_far = cumsum(type == 24),
    upcoming_exercise = if_else(
      type == 24,
      exercises_so_far,
      exercises_so_far + 1
    )
  ) %>%
  ungroup()

Count example and challenge engagement events before each exercise.

Repeated engagement with the same content is retained as additional observed engagement.

preparation <- actions_sequence %>%
  filter(type %in% c(22, 23)) %>%
  group_by(learner, upcoming_exercise) %>%
  summarise(
    examples_before = sum(type == 22),
    challenges_before = sum(type == 23),
    .groups = "drop"
  ) %>%
  mutate(
    preparatory_before = examples_before + challenges_before
  )

Join the preparatory engagement variables to the exercise-level data. Exercises without observed example or challenge engagement in the preceding interval receive a count of zero.

exercise_data <- exercise_data %>%
  left_join(
    preparation,
    by = c(
      "learner",
      "exercise_number" = "upcoming_exercise"
    )
  ) %>%
  mutate(
    examples_before = replace_na(examples_before, 0),
    challenges_before = replace_na(challenges_before, 0),
    preparatory_before = replace_na(preparatory_before, 0)
  )

6. Final analysis dataset

Exclude the first observed exercise for each learner because it has no preceding observed exercise from which an Elo change can be calculated.

Create binary variables used in the robustness analyses.

analysis_data <- exercise_data %>%
  filter(!is.na(Elo_gain)) %>%
  mutate(
    any_examples = examples_before > 0,
    any_preparation = preparatory_before > 0,
    Elo_increased = Elo_gain > 0
  )

Check the final analytical sample.

analysis_sample <- analysis_data %>%
  summarise(
    exercise_transitions = n(),
    learners = n_distinct(learner)
  )

print(analysis_sample)
# A tibble: 1 × 2
  exercise_transitions learners
                 <int>    <int>
1                 2207       57

Five of the 62 learners with observed exercise events have only one observed exercise and therefore cannot contribute to the analysis of between-exercise Elo changes.

exercise_counts <- exercise_data %>%
  group_by(learner) %>%
  summarise(
    n_exercises = n(),
    n_Elo_transitions = sum(!is.na(Elo_gain)),
    .groups = "drop"
  ) %>%
  arrange(n_exercises)

exercise_counts %>%
  filter(n_exercises == 1)

7. Descriptive statistics

descriptive_statistics <- analysis_data %>%
  summarise(
    n_observations = n(),
    n_learners = n_distinct(learner),

    mean_Elo_gain = mean(Elo_gain),
    sd_Elo_gain = sd(Elo_gain),
    median_Elo_gain = median(Elo_gain),
    min_Elo_gain = min(Elo_gain),
    max_Elo_gain = max(Elo_gain),

    mean_examples = mean(examples_before),
    median_examples = median(examples_before),
    max_examples = max(examples_before),

    mean_challenges = mean(challenges_before),
    median_challenges = median(challenges_before),
    max_challenges = max(challenges_before),

    mean_preparatory = mean(preparatory_before),
    median_preparatory = median(preparatory_before),
    max_preparatory = max(preparatory_before),

    pct_examples_before = mean(examples_before > 0) * 100,
    pct_challenges_before = mean(challenges_before > 0) * 100,
    pct_any_preparation = mean(preparatory_before > 0) * 100
  )

print(descriptive_statistics)
# A tibble: 1 × 19
  n_observations n_learners mean_Elo_gain sd_Elo_gain median_Elo_gain min_Elo_gain max_Elo_gain mean_examples median_examples max_examples mean_challenges median_challenges max_challenges mean_preparatory median_preparatory
           <int>      <int>         <dbl>       <dbl>           <dbl>        <dbl>        <dbl>         <dbl>           <int>        <int>           <dbl>             <int>          <int>            <dbl>              <int>
1           2207         57          2.11        9.65               0          -62          156         0.225               0           25          0.0435                 0             19            0.269                  0
# ℹ 4 more variables: max_preparatory <int>, pct_examples_before <dbl>, pct_challenges_before <dbl>, pct_any_preparation <dbl>

Descriptive statistics by type of preparatory engagement.

preparation_summary <- analysis_data %>%
  mutate(
    preparation_group = case_when(
      examples_before == 0 & challenges_before == 0 ~ "None",
      examples_before > 0 & challenges_before == 0 ~ "Examples only",
      examples_before == 0 & challenges_before > 0 ~ "Challenges only",
      examples_before > 0 & challenges_before > 0 ~ "Examples + challenges"
    ),
    preparation_group = factor(
      preparation_group,
      levels = c(
        "None",
        "Examples only",
        "Challenges only",
        "Examples + challenges"
      )
    )
  ) %>%
  group_by(preparation_group) %>%
  summarise(
    n = n(),
    mean_Elo_gain = mean(Elo_gain),
    median_Elo_gain = median(Elo_gain),
    sd_Elo_gain = sd(Elo_gain),
    .groups = "drop"
  )

print(preparation_summary)
# A tibble: 4 × 5
  preparation_group         n mean_Elo_gain median_Elo_gain sd_Elo_gain
  <fct>                 <int>         <dbl>           <dbl>       <dbl>
1 None                   2043          1.71               0        8.76
2 Examples only           127          5.77               0        9.95
3 Challenges only          11         17.5                0       46.7 
4 Examples + challenges    26          9                  0       18.5 

8. Primary inferential analysis

Because each learner contributes multiple exercise transitions, linear mixed-effects models are used with a random intercept for learner.

Exercise number is included as a control for practice volume.

H1a: Example engagement

model_H1a <- lmer(
  Elo_gain ~ examples_before + exercise_number + (1 | learner),
  data = analysis_data,
  REML = FALSE
)

summary(model_H1a)
Linear mixed model fit by maximum likelihood  ['lmerMod']
Formula: Elo_gain ~ examples_before + exercise_number + (1 | learner)
   Data: analysis_data

      AIC       BIC    logLik -2*log(L)  df.resid 
  16215.0   16243.5   -8102.5   16205.0      2202 

Scaled residuals: 
    Min      1Q  Median      3Q     Max 
-6.6634 -0.2307 -0.1696 -0.1127 15.9841 

Random effects:
 Groups   Name        Variance Std.Dev.
 learner  (Intercept)  3.225   1.796   
 Residual             88.840   9.426   
Number of obs: 2207, groups:  learner, 57

Fixed effects:
                 Estimate Std. Error t value
(Intercept)      2.507127   0.409561   6.121
examples_before  1.153487   0.173649   6.643
exercise_number -0.006196   0.005340  -1.160

Correlation of Fixed Effects:
            (Intr) exmpl_
exampls_bfr -0.157       
exercs_nmbr -0.441  0.077

H1b: Preparatory engagement

model_H1b <- lmer(
  Elo_gain ~ preparatory_before + exercise_number + (1 | learner),
  data = analysis_data,
  REML = FALSE
)

summary(model_H1b)
Linear mixed model fit by maximum likelihood  ['lmerMod']
Formula: Elo_gain ~ preparatory_before + exercise_number + (1 | learner)
   Data: analysis_data

      AIC       BIC    logLik -2*log(L)  df.resid 
  16194.1   16222.6   -8092.1   16184.1      2202 

Scaled residuals: 
    Min      1Q  Median      3Q     Max 
-6.6916 -0.2305 -0.1698 -0.1128 15.8619 

Random effects:
 Groups   Name        Variance Std.Dev.
 learner  (Intercept)  2.572   1.604   
 Residual             88.207   9.392   
Number of obs: 2207, groups:  learner, 57

Fixed effects:
                    Estimate Std. Error t value
(Intercept)         2.415102   0.390273   6.188
preparatory_before  1.101194   0.135372   8.135
exercise_number    -0.005961   0.005248  -1.136

Correlation of Fixed Effects:
            (Intr) prprt_
prprtry_bfr -0.156       
exercs_nmbr -0.471  0.079

9. Model diagnostics

Residual diagnostics are examined for the primary mixed-effects model.

par(mfrow = c(1, 2))

plot(
  fitted(model_H1b),
  resid(model_H1b),
  xlab = "Fitted values",
  ylab = "Residuals",
  main = "Residuals vs fitted"
)

abline(h = 0, lty = 2)

qqnorm(
  resid(model_H1b),
  main = "Normal Q-Q plot"
)

qqline(resid(model_H1b))

par(mfrow = c(1, 1))

The residual diagnostics indicate substantial deviation from normality. The Elo-change distribution also contains a large proportion of zero changes. Logistic mixed-effects models are therefore used below as a complementary robustness analysis.

10. Sensitivity analysis

The largest observed preparatory-engagement count is 44 events. A sensitivity analysis excludes this single extreme observation to assess its influence on the continuous mixed-model estimates.

analysis_sensitivity <- analysis_data %>%
  filter(preparatory_before < 44)

model_H1a_sensitivity <- lmer(
  Elo_gain ~ examples_before + exercise_number + (1 | learner),
  data = analysis_sensitivity,
  REML = FALSE
)

model_H1b_sensitivity <- lmer(
  Elo_gain ~ preparatory_before + exercise_number + (1 | learner),
  data = analysis_sensitivity,
  REML = FALSE
)

summary(model_H1a_sensitivity)
Linear mixed model fit by maximum likelihood  ['lmerMod']
Formula: Elo_gain ~ examples_before + exercise_number + (1 | learner)
   Data: analysis_sensitivity

      AIC       BIC    logLik -2*log(L)  df.resid 
  16177.2   16205.7   -8083.6   16167.2      2201 

Scaled residuals: 
    Min      1Q  Median      3Q     Max 
-6.7118 -0.2449 -0.1847 -0.1219 16.1169 

Random effects:
 Groups   Name        Variance Std.Dev.
 learner  (Intercept)  2.508   1.584   
 Residual             87.845   9.373   
Number of obs: 2206, groups:  learner, 57

Fixed effects:
                 Estimate Std. Error t value
(Intercept)      2.598682   0.388928   6.682
examples_before  0.674130   0.192743   3.498
exercise_number -0.007559   0.005233  -1.445

Correlation of Fixed Effects:
            (Intr) exmpl_
exampls_bfr -0.170       
exercs_nmbr -0.474  0.085
summary(model_H1b_sensitivity)
Linear mixed model fit by maximum likelihood  ['lmerMod']
Formula: Elo_gain ~ preparatory_before + exercise_number + (1 | learner)
   Data: analysis_sensitivity

      AIC       BIC    logLik -2*log(L)  df.resid 
  16173.1   16201.5   -8081.5   16163.1      2201 

Scaled residuals: 
    Min      1Q  Median      3Q     Max 
-6.7167 -0.2427 -0.1822 -0.1216 15.9958 

Random effects:
 Groups   Name        Variance Std.Dev.
 learner  (Intercept)  2.375   1.541   
 Residual             87.724   9.366   
Number of obs: 2206, groups:  learner, 57

Fixed effects:
                    Estimate Std. Error t value
(Intercept)         2.543141   0.385531   6.596
preparatory_before  0.696274   0.171450   4.061
exercise_number    -0.007171   0.005216  -1.375

Correlation of Fixed Effects:
            (Intr) prprt_
prprtry_bfr -0.180       
exercs_nmbr -0.483  0.096

Compare the primary and sensitivity estimates.

fixef(model_H1a)
    (Intercept) examples_before exercise_number 
    2.507127432     1.153486703    -0.006195978 
fixef(model_H1a_sensitivity)
    (Intercept) examples_before exercise_number 
    2.598682080     0.674129935    -0.007559097 
fixef(model_H1b)
       (Intercept) preparatory_before    exercise_number 
       2.415101609        1.101193953       -0.005960955 
fixef(model_H1b_sensitivity)
       (Intercept) preparatory_before    exercise_number 
       2.543141007        0.696273513       -0.007170756 

11. Logistic robustness analysis

The logistic robustness analysis examines whether engagement is associated with the probability of a positive Elo change.

Here, Elo_increased = TRUE when Elo_gain > 0. Zero and negative changes are therefore classified as no positive increase.

Event-count models

model_H1a_logistic <- glmer(
  Elo_increased ~ examples_before + exercise_number + (1 | learner),
  data = analysis_data,
  family = binomial
)

model_H1b_logistic <- glmer(
  Elo_increased ~ preparatory_before + exercise_number + (1 | learner),
  data = analysis_data,
  family = binomial
)

summary(model_H1a_logistic)
Generalized linear mixed model fit by maximum likelihood (Laplace Approximation) ['glmerMod']
 Family: binomial  ( logit )
Formula: Elo_increased ~ examples_before + exercise_number + (1 | learner)
   Data: analysis_data

      AIC       BIC    logLik -2*log(L)  df.resid 
   1582.3    1605.1    -787.2    1574.3      2203 

Scaled residuals: 
    Min      1Q  Median      3Q     Max 
-2.5193 -0.3867 -0.3129 -0.2578  5.0246 

Random effects:
 Groups  Name        Variance Std.Dev.
 learner (Intercept) 0.4086   0.6392  
Number of obs: 2207, groups:  learner, 57

Fixed effects:
                 Estimate Std. Error z value Pr(>|z|)    
(Intercept)     -2.004839   0.144031 -13.919  < 2e-16 ***
examples_before  0.209197   0.047729   4.383 1.17e-05 ***
exercise_number -0.002765   0.001929  -1.434    0.152    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Correlation of Fixed Effects:
            (Intr) exmpl_
exampls_bfr -0.200       
exercs_nmbr -0.430  0.099
summary(model_H1b_logistic)
Generalized linear mixed model fit by maximum likelihood (Laplace Approximation) ['glmerMod']
 Family: binomial  ( logit )
Formula: Elo_increased ~ preparatory_before + exercise_number + (1 | learner)
   Data: analysis_data

      AIC       BIC    logLik -2*log(L)  df.resid 
   1579.9    1602.7    -786.0    1571.9      2203 

Scaled residuals: 
    Min      1Q  Median      3Q     Max 
-2.2700 -0.3859 -0.3130 -0.2587  5.0790 

Random effects:
 Groups  Name        Variance Std.Dev.
 learner (Intercept) 0.3986   0.6313  
Number of obs: 2207, groups:  learner, 57

Fixed effects:
                    Estimate Std. Error z value Pr(>|z|)    
(Intercept)        -2.016464   0.143615 -14.041  < 2e-16 ***
preparatory_before  0.196596   0.042392   4.638 3.52e-06 ***
exercise_number    -0.002578   0.001928  -1.337    0.181    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Correlation of Fixed Effects:
            (Intr) prprt_
prprtry_bfr -0.212       
exercs_nmbr -0.437  0.122

Any engagement versus no engagement

model_H1a_logistic_any <- glmer(
  Elo_increased ~ any_examples + exercise_number + (1 | learner),
  data = analysis_data,
  family = binomial
)

model_H1b_logistic_any <- glmer(
  Elo_increased ~ any_preparation + exercise_number + (1 | learner),
  data = analysis_data,
  family = binomial
)

summary(model_H1a_logistic_any)
Generalized linear mixed model fit by maximum likelihood (Laplace Approximation) ['glmerMod']
 Family: binomial  ( logit )
Formula: Elo_increased ~ any_examples + exercise_number + (1 | learner)
   Data: analysis_data

      AIC       BIC    logLik -2*log(L)  df.resid 
   1564.3    1587.1    -778.2    1556.3      2203 

Scaled residuals: 
    Min      1Q  Median      3Q     Max 
-1.2968 -0.3799 -0.2985 -0.2489  4.9066 

Random effects:
 Groups  Name        Variance Std.Dev.
 learner (Intercept) 0.4212   0.649   
Number of obs: 2207, groups:  learner, 57

Fixed effects:
                  Estimate Std. Error z value Pr(>|z|)    
(Intercept)      -2.113753   0.148887 -14.197  < 2e-16 ***
any_examplesTRUE  1.315045   0.203150   6.473 9.59e-11 ***
exercise_number  -0.002168   0.001934  -1.121    0.262    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Correlation of Fixed Effects:
            (Intr) a_TRUE
any_xmpTRUE -0.288       
exercs_nmbr -0.430  0.126
summary(model_H1b_logistic_any)
Generalized linear mixed model fit by maximum likelihood (Laplace Approximation) ['glmerMod']
 Family: binomial  ( logit )
Formula: Elo_increased ~ any_preparation + exercise_number + (1 | learner)
   Data: analysis_data

      AIC       BIC    logLik -2*log(L)  df.resid 
   1559.8    1582.6    -775.9    1551.8      2203 

Scaled residuals: 
    Min      1Q  Median      3Q     Max 
-1.3139 -0.3796 -0.2960 -0.2475  4.9103 

Random effects:
 Groups  Name        Variance Std.Dev.
 learner (Intercept) 0.4215   0.6492  
Number of obs: 2207, groups:  learner, 57

Fixed effects:
                     Estimate Std. Error z value Pr(>|z|)    
(Intercept)         -2.140276   0.149844 -14.283  < 2e-16 ***
any_preparationTRUE  1.354819   0.197975   6.843 7.74e-12 ***
exercise_number     -0.001952   0.001933  -1.010    0.313    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Correlation of Fixed Effects:
            (Intr) a_TRUE
any_prpTRUE -0.305       
exercs_nmbr -0.433  0.139

12. Tables for the paper

Table 1: Analytical sample and Elo changes

table1 <- analysis_data %>%
  summarise(
    `Exercise transitions` = n(),
    `Learners` = n_distinct(learner),
    `Mean Elo change` = round(mean(Elo_gain), 2),
    `SD Elo change` = round(sd(Elo_gain), 2),
    `Median Elo change` = median(Elo_gain),
    `Minimum` = min(Elo_gain),
    `Maximum` = max(Elo_gain),
    `Positive, n (%)` = paste0(
      sum(Elo_gain > 0),
      " (",
      round(mean(Elo_gain > 0) * 100, 1),
      "%)"
    ),
    `Zero, n (%)` = paste0(
      sum(Elo_gain == 0),
      " (",
      round(mean(Elo_gain == 0) * 100, 1),
      "%)"
    ),
    `Negative, n (%)` = paste0(
      sum(Elo_gain < 0),
      " (",
      round(mean(Elo_gain < 0) * 100, 1),
      "%)"
    )
  )

print(table1)
# A tibble: 1 × 10
  `Exercise transitions` Learners `Mean Elo change` `SD Elo change` `Median Elo change` Minimum Maximum `Positive, n (%)` `Zero, n (%)` `Negative, n (%)`
                   <int>    <int>             <dbl>           <dbl>               <dbl>   <dbl>   <dbl> <chr>             <chr>         <chr>            
1                   2207       57              2.11            9.65                   0     -62     156 272 (12.3%)       1852 (83.9%)  83 (3.8%)        

Table 2: Elo change by preparatory engagement

table2 <- analysis_data %>%
  mutate(
    preparation_group = case_when(
      examples_before == 0 & challenges_before == 0 ~ "None",
      examples_before > 0 & challenges_before == 0 ~ "Examples only",
      examples_before == 0 & challenges_before > 0 ~ "Challenges only",
      examples_before > 0 & challenges_before > 0 ~ "Examples + challenges"
    ),
    preparation_group = factor(
      preparation_group,
      levels = c(
        "None",
        "Examples only",
        "Challenges only",
        "Examples + challenges"
      )
    )
  ) %>%
  group_by(preparation_group) %>%
  summarise(
    n = n(),
    mean_Elo_change = round(mean(Elo_gain), 2),
    SD = round(sd(Elo_gain), 2),
    median_Elo_change = median(Elo_gain),
    .groups = "drop"
  )

print(table2)
# A tibble: 4 × 5
  preparation_group         n mean_Elo_change    SD median_Elo_change
  <fct>                 <int>           <dbl> <dbl>             <dbl>
1 None                   2043            1.71  8.76                 0
2 Examples only           127            5.77  9.95                 0
3 Challenges only          11           17.4  46.7                  0
4 Examples + challenges    26            9    18.5                  0

Table 3: Primary linear mixed-effects models

Extract coefficients and 95% Wald confidence intervals directly from the fitted models.

coef_H1a <- as.data.frame(coef(summary(model_H1a)))
coef_H1b <- as.data.frame(coef(summary(model_H1b)))

ci_H1a <- as.data.frame(
  confint(
    model_H1a,
    parm = "beta_",
    method = "Wald"
  )
)

ci_H1b <- as.data.frame(
  confint(
    model_H1b,
    parm = "beta_",
    method = "Wald"
  )
)

Create Table 3.

The intercepts are omitted from the paper table so that the table focuses on the predictors relevant to H1a and H1b.

table3_H1a <- data.frame(
  Model = "H1a",
  Predictor = rownames(coef_H1a),
  Beta = coef_H1a$Estimate,
  SE = coef_H1a$`Std. Error`,
  t = coef_H1a$`t value`,
  CI_low = ci_H1a$`2.5 %`,
  CI_high = ci_H1a$`97.5 %`
)

table3_H1b <- data.frame(
  Model = "H1b",
  Predictor = rownames(coef_H1b),
  Beta = coef_H1b$Estimate,
  SE = coef_H1b$`Std. Error`,
  t = coef_H1b$`t value`,
  CI_low = ci_H1b$`2.5 %`,
  CI_high = ci_H1b$`97.5 %`
)

table3 <- bind_rows(
  table3_H1a,
  table3_H1b
) %>%
  filter(Predictor != "(Intercept)") %>%
  mutate(
    Predictor = recode(
      Predictor,
      examples_before = "Example engagement events",
      preparatory_before = "Preparatory engagement events",
      exercise_number = "Exercise number"
    ),
    across(
      c(Beta, SE, t, CI_low, CI_high),
      ~ round(.x, 3)
    )
  )

print(table3)
  Model                     Predictor   Beta    SE      t CI_low CI_high
1   H1a     Example engagement events  1.153 0.174  6.643  0.813   1.494
2   H1a               Exercise number -0.006 0.005 -1.160 -0.017   0.004
3   H1b Preparatory engagement events  1.101 0.135  8.135  0.836   1.367
4   H1b               Exercise number -0.006 0.005 -1.136 -0.016   0.004

Table 4: Logistic robustness analysis

Function for extracting odds ratios, Wald confidence intervals, and p-values.

extract_logistic_result <- function(model, predictor, label) {

  model_summary <- coef(summary(model))

  beta <- model_summary[predictor, "Estimate"]
  se <- model_summary[predictor, "Std. Error"]
  p_value <- model_summary[predictor, "Pr(>|z|)"]

  data.frame(
    Predictor = label,
    OR = exp(beta),
    CI_low = exp(beta - 1.96 * se),
    CI_high = exp(beta + 1.96 * se),
    p = p_value
  )
}

Create Table 4.

table4 <- bind_rows(
  extract_logistic_result(
    model_H1a_logistic,
    "examples_before",
    "Example-event count"
  ),
  extract_logistic_result(
    model_H1b_logistic,
    "preparatory_before",
    "Preparatory-event count"
  ),
  extract_logistic_result(
    model_H1a_logistic_any,
    "any_examplesTRUE",
    "Any example engagement"
  ),
  extract_logistic_result(
    model_H1b_logistic_any,
    "any_preparationTRUE",
    "Any preparatory engagement"
  )
) %>%
  mutate(
    across(
      c(OR, CI_low, CI_high),
      ~ round(.x, 3)
    ),
    p = if_else(
      p < .001,
      "< .001",
      format(round(p, 3), nsmall = 3)
    )
  )

print(table4)
                   Predictor    OR CI_low CI_high      p
1        Example-event count 1.233  1.123   1.354 < .001
2    Preparatory-event count 1.217  1.120   1.323 < .001
3     Any example engagement 3.725  2.501   5.547 < .001
4 Any preparatory engagement 3.876  2.629   5.714 < .001

13. Figures for the paper

Figure 1: Distribution of Elo rating changes

ggplot(analysis_data, aes(x = Elo_gain)) +
  geom_histogram(
    binwidth = 5,
    boundary = 0,
    color = "black"
  ) +
  labs(
    title = "Distribution of Elo rating changes",
    subtitle = paste0(
      "Changes between consecutive observed exercise events (N = ",
      nrow(analysis_data),
      ")"
    ),
    x = "Elo rating change",
    y = "Number of exercise transitions"
  ) +
  theme_minimal()

Figure 2: Mean Elo change by preparatory engagement

plot_data <- analysis_data %>%
  mutate(
    preparation_group = case_when(
      examples_before == 0 & challenges_before == 0 ~ "None",
      examples_before > 0 & challenges_before == 0 ~ "Examples only",
      examples_before == 0 & challenges_before > 0 ~ "Challenges only",
      examples_before > 0 & challenges_before > 0 ~ "Examples + challenges"
    ),
    preparation_group = factor(
      preparation_group,
      levels = c(
        "None",
        "Examples only",
        "Challenges only",
        "Examples + challenges"
      )
    )
  ) %>%
  group_by(preparation_group) %>%
  summarise(
    n = n(),
    mean = mean(Elo_gain),
    SE = sd(Elo_gain) / sqrt(n),
    lower = mean - 1.96 * SE,
    upper = mean + 1.96 * SE,
    .groups = "drop"
  ) %>%
  mutate(
    group_label = paste0(
      preparation_group,
      "\n(n = ",
      n,
      ")"
    )
  )

ggplot(
  plot_data,
  aes(x = group_label, y = mean)
) +
  geom_point(size = 3) +
  geom_errorbar(
    aes(
      ymin = lower,
      ymax = upper
    ),
    width = 0.15
  ) +
  geom_hline(
    yintercept = 0,
    linetype = "dashed"
  ) +
  labs(
    title = "Mean Elo change by preparatory engagement",
    subtitle = "Points show group means; error bars show 95% confidence intervals",
    x = "Engagement between consecutive exercise events",
    y = "Mean Elo rating change"
  ) +
  theme_minimal()

Figure 3: Predicted probability of a positive Elo change

Predictions are shown for 0–8 preparatory events because observations become sparse at higher engagement counts.

prediction_data <- data.frame(
  preparatory_before = 0:8,
  exercise_number = median(analysis_data$exercise_number)
)

prediction_data$predicted_probability <- predict(
  model_H1b_logistic,
  newdata = prediction_data,
  type = "response",
  re.form = NA
)

ggplot(
  prediction_data,
  aes(
    x = preparatory_before,
    y = predicted_probability
  )
) +
  geom_line(linewidth = 1) +
  geom_point(size = 2) +
  scale_y_continuous(
    labels = scales::percent_format(accuracy = 1)
  ) +
  labs(
    title = "Predicted probability of a positive Elo change",
    subtitle = paste0(
      "Exercise position held at its median; N = ",
      nrow(analysis_data),
      " transitions"
    ),
    x = "Preparatory engagement events before exercise",
    y = "Predicted probability"
  ) +
  theme_minimal()

Figure 4: Positive Elo changes with and without preparation

any_prep_plot <- analysis_data %>%
  mutate(
    preparation = if_else(
      preparatory_before > 0,
      "Any preparation",
      "No preparation"
    ),
    preparation = factor(
      preparation,
      levels = c(
        "No preparation",
        "Any preparation"
      )
    )
  ) %>%
  group_by(preparation) %>%
  summarise(
    n = n(),
    positive_n = sum(Elo_gain > 0),
    positive_pct = mean(Elo_gain > 0) * 100,
    .groups = "drop"
  ) %>%
  mutate(
    label = paste0(
      preparation,
      "\n(n = ",
      n,
      ")"
    )
  )

print(any_prep_plot)
# A tibble: 2 × 5
  preparation         n positive_n positive_pct label                       
  <fct>           <int>      <int>        <dbl> <chr>                       
1 No preparation   2043        219         10.7 "No preparation\n(n = 2043)"
2 Any preparation   164         53         32.3 "Any preparation\n(n = 164)"
ggplot(
  any_prep_plot,
  aes(x = label, y = positive_pct)
) +
  geom_col(width = 0.6) +
  geom_text(
    aes(
      label = paste0(
        round(positive_pct, 1),
        "%"
      )
    ),
    vjust = -0.5
  ) +
  labs(
    title = "Positive Elo changes by preparatory engagement",
    x = NULL,
    y = "Exercise transitions with positive Elo change (%)"
  ) +
  theme_minimal()

LS0tCnRpdGxlOiAiUHJvVHVTIEFuYWx5c2lzIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgojIDEuIFBhY2thZ2VzIGFuZCBkYXRhIGltcG9ydAoKYGBge3J9CiMgSW5zdGFsbCBwYWNrYWdlcyBpZiBuZWVkZWQ6CiMgaW5zdGFsbC5wYWNrYWdlcygicmVhZHhsIikKIyBpbnN0YWxsLnBhY2thZ2VzKCJkcGx5ciIpCiMgaW5zdGFsbC5wYWNrYWdlcygidGlkeXIiKQojIGluc3RhbGwucGFja2FnZXMoImdncGxvdDIiKQojIGluc3RhbGwucGFja2FnZXMoImxtZTQiKQoKbGlicmFyeShyZWFkeGwpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShsbWU0KQpgYGAKCmBgYHtyfQojIENoYW5nZSBmaWxlIHBhdGggaWYgbmVjZXNzYXJ5CmZpbGUgPC0gIi9Vc2Vycy9tZXR0ZS9EZXNrdG9wL05UTlUvTGVhcm5pbmcgYW5hbHl0aWNzL0RhdGFTZXQtUHJvVHVTLnhsc3giCgpsZWFybmVycyA8LSByZWFkX2V4Y2VsKGZpbGUsIHNoZWV0ID0gIkxlYXJuZXJzIikgJT4lCiAgc2VsZWN0KHdoZXJlKH4gIWFsbChpcy5uYSguKSkpKQoKYWN0aW9uc19yYXcgPC0gcmVhZF9leGNlbChmaWxlLCBzaGVldCA9ICJVc2VycyBhY3Rpb25zIikgJT4lCiAgc2VsZWN0KHdoZXJlKH4gIWFsbChpcy5uYSguKSkpKQoKYWN0aW9uX3R5cGVzX3JhdyA8LSByZWFkX2V4Y2VsKGZpbGUsIHNoZWV0ID0gIkFjdGlvbiB0eXBlIikgJT4lCiAgc2VsZWN0KHdoZXJlKH4gIWFsbChpcy5uYSguKSkpKQpgYGAKCiMgMi4gSW5pdGlhbCBkYXRhIGluc3BlY3Rpb24KCmBgYHtyfQpkaW0obGVhcm5lcnMpCmRpbShhY3Rpb25zX3JhdykKCm5hbWVzKGxlYXJuZXJzKQpuYW1lcyhhY3Rpb25zX3JhdykKCm5yb3cobGVhcm5lcnMpCm5fZGlzdGluY3QoYWN0aW9uc19yYXckbGVhcm5lcikKCnN1bShkdXBsaWNhdGVkKGFjdGlvbnNfcmF3KSkKY29sU3Vtcyhpcy5uYShhY3Rpb25zX3JhdykpCmBgYAoKVGhlIExlYXJuZXJzIHNoZWV0IGNvbnRhaW5zIDExNiBsZWFybmVycy4gTm90IGFsbCBsZWFybmVycyBhcmUgcmVwcmVzZW50ZWQgaW4gdGhlIHByb3ZpZGVkIGFjdGlvbiBsb2cuCgpgYGB7cn0KbGVhcm5lcl9hY3Rpb25fY292ZXJhZ2UgPC0gbGVhcm5lcnMgJT4lCiAgbXV0YXRlKGhhc19hY3Rpb25fcmVjb3JkcyA9IGlkICVpbiUgYWN0aW9uc19yYXckbGVhcm5lcikgJT4lCiAgY291bnQoaGFzX2FjdGlvbl9yZWNvcmRzKQoKcHJpbnQobGVhcm5lcl9hY3Rpb25fY292ZXJhZ2UpCmBgYAoKIyAzLiBEYXRhIGNsZWFuaW5nCgpDcmVhdGUgYSBjbGVhbiBsb29rdXAgdGFibGUgZm9yIGFjdGlvbiB0eXBlcy4KCmBgYHtyfQphY3Rpb25fbG9va3VwIDwtIGFjdGlvbl90eXBlc19yYXcgJT4lCiAgc2VsZWN0KGlkYWN0aW9uX3R5cGUsIGFjdGlvbl90eXBlKSAlPiUKICBmaWx0ZXIoIWlzLm5hKGlkYWN0aW9uX3R5cGUpKSAlPiUKICBkaXN0aW5jdChpZGFjdGlvbl90eXBlLCAua2VlcF9hbGwgPSBUUlVFKQoKIyBWZXJpZnkgdGhhdCBlYWNoIGFjdGlvbiB0eXBlIElEIG9jY3VycyBvbmx5IG9uY2UKYWN0aW9uX2xvb2t1cCAlPiUKICBjb3VudChpZGFjdGlvbl90eXBlKSAlPiUKICBmaWx0ZXIobiA+IDEpCmBgYAoKUmVtb3ZlIHRoZSBzaW5nbGUgbWFsZm9ybWVkIGFjdGlvbiByZWNvcmQgd2l0aCBhIG1pc3NpbmcgYWN0aW9uIHR5cGUgYW5kIGFkZCB0aGUgYWN0aW9uIGxhYmVscy4KCmBgYHtyfQphY3Rpb25zX2NsZWFuIDwtIGFjdGlvbnNfcmF3ICU+JQogIGZpbHRlcighaXMubmEodHlwZSkpICU+JQogIGxlZnRfam9pbigKICAgIGFjdGlvbl9sb29rdXAsCiAgICBieSA9IGMoInR5cGUiID0gImlkYWN0aW9uX3R5cGUiKSwKICAgIHJlbGF0aW9uc2hpcCA9ICJtYW55LXRvLW9uZSIKICApICU+JQogIGFycmFuZ2UobGVhcm5lciwgdGltZSwgaWRVc2Vyc19hY3Rpb24pCmBgYAoKVmVyaWZ5IHRoZSBjbGVhbmVkIGRhdGFzZXQuCgpgYGB7cn0KZGF0YV9zdW1tYXJ5IDwtIHRpYmJsZSgKICBtZWFzdXJlID0gYygKICAgICJMZWFybmVycyBpbiBMZWFybmVycyBzaGVldCIsCiAgICAiTGVhcm5lcnMgcmVwcmVzZW50ZWQgaW4gYWN0aW9uIGxvZyIsCiAgICAiUmF3IGFjdGlvbiByZWNvcmRzIiwKICAgICJDbGVhbiBhY3Rpb24gcmVjb3JkcyIKICApLAogIG4gPSBjKAogICAgbnJvdyhsZWFybmVycyksCiAgICBuX2Rpc3RpbmN0KGFjdGlvbnNfcmF3JGxlYXJuZXIpLAogICAgbnJvdyhhY3Rpb25zX3JhdyksCiAgICBucm93KGFjdGlvbnNfY2xlYW4pCiAgKQopCgpwcmludChkYXRhX3N1bW1hcnkpCgojIFZlcmlmeSB0aGF0IGFjdGlvbiBJRHMgYXJlIHVuaXF1ZSBhZnRlciBjbGVhbmluZwpzdG9waWZub3Qoc3VtKGR1cGxpY2F0ZWQoYWN0aW9uc19jbGVhbiRpZFVzZXJzX2FjdGlvbikpID09IDApCmBgYAoKIyA0LiBFbG8gZXZlbnRzIGFuZCBleGVyY2lzZSBkYXRhCgpDaGVjayB3aGljaCBhY3Rpb24gdHlwZXMgY29udGFpbiBub24temVybyBFbG8gdmFsdWVzLgoKYGBge3J9CmVsb19ldmVudHMgPC0gYWN0aW9uc19jbGVhbiAlPiUKICBmaWx0ZXIoRWxvX3JhdGluZ19zdHVkZW50ICE9IDApCgplbG9fZXZlbnRzICU+JQogIGNvdW50KHR5cGUsIGFjdGlvbl90eXBlLCBzb3J0ID0gVFJVRSkgJT4lCiAgcHJpbnQobiA9IEluZikKCm5yb3coZWxvX2V2ZW50cykKbl9kaXN0aW5jdChlbG9fZXZlbnRzJGxlYXJuZXIpCnN1bW1hcnkoZWxvX2V2ZW50cyRFbG9fcmF0aW5nX3N0dWRlbnQpCmBgYAoKQWxsIG9ic2VydmVkIG5vbi16ZXJvIEVsbyB2YWx1ZXMgb2NjdXIgb24gY29kaW5nIGV4ZXJjaXNlIGV2ZW50cyAodHlwZSAyNCkuCgpDb25zdHJ1Y3QgdGhlIGV4ZXJjaXNlLWxldmVsIGRhdGEuIEVsbyBjaGFuZ2UgaXMgY2FsY3VsYXRlZCBiZXR3ZWVuIGNvbnNlY3V0aXZlIG9ic2VydmVkIGV4ZXJjaXNlIGV2ZW50cyB3aXRoaW4gZWFjaCBsZWFybmVyLgoKYGBge3J9CmV4ZXJjaXNlX2RhdGEgPC0gYWN0aW9uc19jbGVhbiAlPiUKICBmaWx0ZXIodHlwZSA9PSAyNCkgJT4lCiAgYXJyYW5nZShsZWFybmVyLCB0aW1lLCBpZFVzZXJzX2FjdGlvbikgJT4lCiAgZ3JvdXBfYnkobGVhcm5lcikgJT4lCiAgbXV0YXRlKAogICAgZXhlcmNpc2VfbnVtYmVyID0gcm93X251bWJlcigpLAogICAgcHJldmlvdXNfRWxvID0gbGFnKEVsb19yYXRpbmdfc3R1ZGVudCksCiAgICBFbG9fZ2FpbiA9IEVsb19yYXRpbmdfc3R1ZGVudCAtIHByZXZpb3VzX0VsbwogICkgJT4lCiAgdW5ncm91cCgpCmBgYAoKVGhlIGZpcnN0IG9ic2VydmVkIGV4ZXJjaXNlIGV2ZW50IGZvciBlYWNoIGxlYXJuZXIgaGFzIG5vIHByZWNlZGluZyBvYnNlcnZlZCBFbG8gdmFsdWUgYW5kIHRoZXJlZm9yZSBoYXMgbm8gY2FsY3VsYWJsZSBFbG8gY2hhbmdlLgoKYGBge3J9CmV4ZXJjaXNlX2RhdGEgJT4lCiAgc3VtbWFyaXNlKAogICAgbl9leGVyY2lzZXMgPSBuKCksCiAgICBuX2xlYXJuZXJzID0gbl9kaXN0aW5jdChsZWFybmVyKSwKICAgIG5fRWxvX3RyYW5zaXRpb25zID0gc3VtKCFpcy5uYShFbG9fZ2FpbikpLAogICAgbWVhbl9nYWluID0gbWVhbihFbG9fZ2FpbiwgbmEucm0gPSBUUlVFKSwKICAgIG1lZGlhbl9nYWluID0gbWVkaWFuKEVsb19nYWluLCBuYS5ybSA9IFRSVUUpLAogICAgc2RfZ2FpbiA9IHNkKEVsb19nYWluLCBuYS5ybSA9IFRSVUUpLAogICAgbWluX2dhaW4gPSBtaW4oRWxvX2dhaW4sIG5hLnJtID0gVFJVRSksCiAgICBtYXhfZ2FpbiA9IG1heChFbG9fZ2FpbiwgbmEucm0gPSBUUlVFKSwKICAgIHBvc2l0aXZlID0gc3VtKEVsb19nYWluID4gMCwgbmEucm0gPSBUUlVFKSwKICAgIHplcm8gPSBzdW0oRWxvX2dhaW4gPT0gMCwgbmEucm0gPSBUUlVFKSwKICAgIG5lZ2F0aXZlID0gc3VtKEVsb19nYWluIDwgMCwgbmEucm0gPSBUUlVFKQogICkKYGBgCgojIDUuIENvbnN0cnVjdCBwcmVwYXJhdG9yeSBlbmdhZ2VtZW50IHZhcmlhYmxlcwoKVGhlIHJlbGV2YW50IGFjdGlvbiB0eXBlcyBhcmU6CgotIDIyID0gY29kaW5nIGV4YW1wbGUKLSAyMyA9IGNvZGluZyBjaGFsbGVuZ2UKLSAyNCA9IGNvZGluZyBleGVyY2lzZQoKUHJlcGFyYXRvcnkgZW5nYWdlbWVudCBpcyBkZWZpbmVkIGFzIGV4YW1wbGUgYW5kIGNoYWxsZW5nZSBlbmdhZ2VtZW50IG9jY3VycmluZyBhZnRlciB0aGUgcHJlY2VkaW5nIG9ic2VydmVkIGV4ZXJjaXNlIGFuZCBiZWZvcmUgdGhlIGN1cnJlbnQgZXhlcmNpc2UuCgpgYGB7cn0KYWN0aW9uc19zZXF1ZW5jZSA8LSBhY3Rpb25zX2NsZWFuICU+JQogIGFycmFuZ2UobGVhcm5lciwgdGltZSwgaWRVc2Vyc19hY3Rpb24pICU+JQogIGdyb3VwX2J5KGxlYXJuZXIpICU+JQogIG11dGF0ZSgKICAgIGV4ZXJjaXNlc19zb19mYXIgPSBjdW1zdW0odHlwZSA9PSAyNCksCiAgICB1cGNvbWluZ19leGVyY2lzZSA9IGlmX2Vsc2UoCiAgICAgIHR5cGUgPT0gMjQsCiAgICAgIGV4ZXJjaXNlc19zb19mYXIsCiAgICAgIGV4ZXJjaXNlc19zb19mYXIgKyAxCiAgICApCiAgKSAlPiUKICB1bmdyb3VwKCkKYGBgCgpDb3VudCBleGFtcGxlIGFuZCBjaGFsbGVuZ2UgZW5nYWdlbWVudCBldmVudHMgYmVmb3JlIGVhY2ggZXhlcmNpc2UuCgpSZXBlYXRlZCBlbmdhZ2VtZW50IHdpdGggdGhlIHNhbWUgY29udGVudCBpcyByZXRhaW5lZCBhcyBhZGRpdGlvbmFsIG9ic2VydmVkIGVuZ2FnZW1lbnQuCgpgYGB7cn0KcHJlcGFyYXRpb24gPC0gYWN0aW9uc19zZXF1ZW5jZSAlPiUKICBmaWx0ZXIodHlwZSAlaW4lIGMoMjIsIDIzKSkgJT4lCiAgZ3JvdXBfYnkobGVhcm5lciwgdXBjb21pbmdfZXhlcmNpc2UpICU+JQogIHN1bW1hcmlzZSgKICAgIGV4YW1wbGVzX2JlZm9yZSA9IHN1bSh0eXBlID09IDIyKSwKICAgIGNoYWxsZW5nZXNfYmVmb3JlID0gc3VtKHR5cGUgPT0gMjMpLAogICAgLmdyb3VwcyA9ICJkcm9wIgogICkgJT4lCiAgbXV0YXRlKAogICAgcHJlcGFyYXRvcnlfYmVmb3JlID0gZXhhbXBsZXNfYmVmb3JlICsgY2hhbGxlbmdlc19iZWZvcmUKICApCmBgYAoKSm9pbiB0aGUgcHJlcGFyYXRvcnkgZW5nYWdlbWVudCB2YXJpYWJsZXMgdG8gdGhlIGV4ZXJjaXNlLWxldmVsIGRhdGEuIEV4ZXJjaXNlcyB3aXRob3V0IG9ic2VydmVkIGV4YW1wbGUgb3IgY2hhbGxlbmdlIGVuZ2FnZW1lbnQgaW4gdGhlIHByZWNlZGluZyBpbnRlcnZhbCByZWNlaXZlIGEgY291bnQgb2YgemVyby4KCmBgYHtyfQpleGVyY2lzZV9kYXRhIDwtIGV4ZXJjaXNlX2RhdGEgJT4lCiAgbGVmdF9qb2luKAogICAgcHJlcGFyYXRpb24sCiAgICBieSA9IGMoCiAgICAgICJsZWFybmVyIiwKICAgICAgImV4ZXJjaXNlX251bWJlciIgPSAidXBjb21pbmdfZXhlcmNpc2UiCiAgICApCiAgKSAlPiUKICBtdXRhdGUoCiAgICBleGFtcGxlc19iZWZvcmUgPSByZXBsYWNlX25hKGV4YW1wbGVzX2JlZm9yZSwgMCksCiAgICBjaGFsbGVuZ2VzX2JlZm9yZSA9IHJlcGxhY2VfbmEoY2hhbGxlbmdlc19iZWZvcmUsIDApLAogICAgcHJlcGFyYXRvcnlfYmVmb3JlID0gcmVwbGFjZV9uYShwcmVwYXJhdG9yeV9iZWZvcmUsIDApCiAgKQpgYGAKCiMgNi4gRmluYWwgYW5hbHlzaXMgZGF0YXNldAoKRXhjbHVkZSB0aGUgZmlyc3Qgb2JzZXJ2ZWQgZXhlcmNpc2UgZm9yIGVhY2ggbGVhcm5lciBiZWNhdXNlIGl0IGhhcyBubyBwcmVjZWRpbmcgb2JzZXJ2ZWQgZXhlcmNpc2UgZnJvbSB3aGljaCBhbiBFbG8gY2hhbmdlIGNhbiBiZSBjYWxjdWxhdGVkLgoKQ3JlYXRlIGJpbmFyeSB2YXJpYWJsZXMgdXNlZCBpbiB0aGUgcm9idXN0bmVzcyBhbmFseXNlcy4KCmBgYHtyfQphbmFseXNpc19kYXRhIDwtIGV4ZXJjaXNlX2RhdGEgJT4lCiAgZmlsdGVyKCFpcy5uYShFbG9fZ2FpbikpICU+JQogIG11dGF0ZSgKICAgIGFueV9leGFtcGxlcyA9IGV4YW1wbGVzX2JlZm9yZSA+IDAsCiAgICBhbnlfcHJlcGFyYXRpb24gPSBwcmVwYXJhdG9yeV9iZWZvcmUgPiAwLAogICAgRWxvX2luY3JlYXNlZCA9IEVsb19nYWluID4gMAogICkKYGBgCgpDaGVjayB0aGUgZmluYWwgYW5hbHl0aWNhbCBzYW1wbGUuCgpgYGB7cn0KYW5hbHlzaXNfc2FtcGxlIDwtIGFuYWx5c2lzX2RhdGEgJT4lCiAgc3VtbWFyaXNlKAogICAgZXhlcmNpc2VfdHJhbnNpdGlvbnMgPSBuKCksCiAgICBsZWFybmVycyA9IG5fZGlzdGluY3QobGVhcm5lcikKICApCgpwcmludChhbmFseXNpc19zYW1wbGUpCmBgYAoKRml2ZSBvZiB0aGUgNjIgbGVhcm5lcnMgd2l0aCBvYnNlcnZlZCBleGVyY2lzZSBldmVudHMgaGF2ZSBvbmx5IG9uZSBvYnNlcnZlZCBleGVyY2lzZSBhbmQgdGhlcmVmb3JlIGNhbm5vdCBjb250cmlidXRlIHRvIHRoZSBhbmFseXNpcyBvZiBiZXR3ZWVuLWV4ZXJjaXNlIEVsbyBjaGFuZ2VzLgoKYGBge3J9CmV4ZXJjaXNlX2NvdW50cyA8LSBleGVyY2lzZV9kYXRhICU+JQogIGdyb3VwX2J5KGxlYXJuZXIpICU+JQogIHN1bW1hcmlzZSgKICAgIG5fZXhlcmNpc2VzID0gbigpLAogICAgbl9FbG9fdHJhbnNpdGlvbnMgPSBzdW0oIWlzLm5hKEVsb19nYWluKSksCiAgICAuZ3JvdXBzID0gImRyb3AiCiAgKSAlPiUKICBhcnJhbmdlKG5fZXhlcmNpc2VzKQoKZXhlcmNpc2VfY291bnRzICU+JQogIGZpbHRlcihuX2V4ZXJjaXNlcyA9PSAxKQpgYGAKCiMgNy4gRGVzY3JpcHRpdmUgc3RhdGlzdGljcwoKYGBge3J9CmRlc2NyaXB0aXZlX3N0YXRpc3RpY3MgPC0gYW5hbHlzaXNfZGF0YSAlPiUKICBzdW1tYXJpc2UoCiAgICBuX29ic2VydmF0aW9ucyA9IG4oKSwKICAgIG5fbGVhcm5lcnMgPSBuX2Rpc3RpbmN0KGxlYXJuZXIpLAoKICAgIG1lYW5fRWxvX2dhaW4gPSBtZWFuKEVsb19nYWluKSwKICAgIHNkX0Vsb19nYWluID0gc2QoRWxvX2dhaW4pLAogICAgbWVkaWFuX0Vsb19nYWluID0gbWVkaWFuKEVsb19nYWluKSwKICAgIG1pbl9FbG9fZ2FpbiA9IG1pbihFbG9fZ2FpbiksCiAgICBtYXhfRWxvX2dhaW4gPSBtYXgoRWxvX2dhaW4pLAoKICAgIG1lYW5fZXhhbXBsZXMgPSBtZWFuKGV4YW1wbGVzX2JlZm9yZSksCiAgICBtZWRpYW5fZXhhbXBsZXMgPSBtZWRpYW4oZXhhbXBsZXNfYmVmb3JlKSwKICAgIG1heF9leGFtcGxlcyA9IG1heChleGFtcGxlc19iZWZvcmUpLAoKICAgIG1lYW5fY2hhbGxlbmdlcyA9IG1lYW4oY2hhbGxlbmdlc19iZWZvcmUpLAogICAgbWVkaWFuX2NoYWxsZW5nZXMgPSBtZWRpYW4oY2hhbGxlbmdlc19iZWZvcmUpLAogICAgbWF4X2NoYWxsZW5nZXMgPSBtYXgoY2hhbGxlbmdlc19iZWZvcmUpLAoKICAgIG1lYW5fcHJlcGFyYXRvcnkgPSBtZWFuKHByZXBhcmF0b3J5X2JlZm9yZSksCiAgICBtZWRpYW5fcHJlcGFyYXRvcnkgPSBtZWRpYW4ocHJlcGFyYXRvcnlfYmVmb3JlKSwKICAgIG1heF9wcmVwYXJhdG9yeSA9IG1heChwcmVwYXJhdG9yeV9iZWZvcmUpLAoKICAgIHBjdF9leGFtcGxlc19iZWZvcmUgPSBtZWFuKGV4YW1wbGVzX2JlZm9yZSA+IDApICogMTAwLAogICAgcGN0X2NoYWxsZW5nZXNfYmVmb3JlID0gbWVhbihjaGFsbGVuZ2VzX2JlZm9yZSA+IDApICogMTAwLAogICAgcGN0X2FueV9wcmVwYXJhdGlvbiA9IG1lYW4ocHJlcGFyYXRvcnlfYmVmb3JlID4gMCkgKiAxMDAKICApCgpwcmludChkZXNjcmlwdGl2ZV9zdGF0aXN0aWNzKQpgYGAKCkRlc2NyaXB0aXZlIHN0YXRpc3RpY3MgYnkgdHlwZSBvZiBwcmVwYXJhdG9yeSBlbmdhZ2VtZW50LgoKYGBge3J9CnByZXBhcmF0aW9uX3N1bW1hcnkgPC0gYW5hbHlzaXNfZGF0YSAlPiUKICBtdXRhdGUoCiAgICBwcmVwYXJhdGlvbl9ncm91cCA9IGNhc2Vfd2hlbigKICAgICAgZXhhbXBsZXNfYmVmb3JlID09IDAgJiBjaGFsbGVuZ2VzX2JlZm9yZSA9PSAwIH4gIk5vbmUiLAogICAgICBleGFtcGxlc19iZWZvcmUgPiAwICYgY2hhbGxlbmdlc19iZWZvcmUgPT0gMCB+ICJFeGFtcGxlcyBvbmx5IiwKICAgICAgZXhhbXBsZXNfYmVmb3JlID09IDAgJiBjaGFsbGVuZ2VzX2JlZm9yZSA+IDAgfiAiQ2hhbGxlbmdlcyBvbmx5IiwKICAgICAgZXhhbXBsZXNfYmVmb3JlID4gMCAmIGNoYWxsZW5nZXNfYmVmb3JlID4gMCB+ICJFeGFtcGxlcyArIGNoYWxsZW5nZXMiCiAgICApLAogICAgcHJlcGFyYXRpb25fZ3JvdXAgPSBmYWN0b3IoCiAgICAgIHByZXBhcmF0aW9uX2dyb3VwLAogICAgICBsZXZlbHMgPSBjKAogICAgICAgICJOb25lIiwKICAgICAgICAiRXhhbXBsZXMgb25seSIsCiAgICAgICAgIkNoYWxsZW5nZXMgb25seSIsCiAgICAgICAgIkV4YW1wbGVzICsgY2hhbGxlbmdlcyIKICAgICAgKQogICAgKQogICkgJT4lCiAgZ3JvdXBfYnkocHJlcGFyYXRpb25fZ3JvdXApICU+JQogIHN1bW1hcmlzZSgKICAgIG4gPSBuKCksCiAgICBtZWFuX0Vsb19nYWluID0gbWVhbihFbG9fZ2FpbiksCiAgICBtZWRpYW5fRWxvX2dhaW4gPSBtZWRpYW4oRWxvX2dhaW4pLAogICAgc2RfRWxvX2dhaW4gPSBzZChFbG9fZ2FpbiksCiAgICAuZ3JvdXBzID0gImRyb3AiCiAgKQoKcHJpbnQocHJlcGFyYXRpb25fc3VtbWFyeSkKYGBgCgojIDguIFByaW1hcnkgaW5mZXJlbnRpYWwgYW5hbHlzaXMKCkJlY2F1c2UgZWFjaCBsZWFybmVyIGNvbnRyaWJ1dGVzIG11bHRpcGxlIGV4ZXJjaXNlIHRyYW5zaXRpb25zLCBsaW5lYXIgbWl4ZWQtZWZmZWN0cyBtb2RlbHMgYXJlIHVzZWQgd2l0aCBhIHJhbmRvbSBpbnRlcmNlcHQgZm9yIGxlYXJuZXIuCgpFeGVyY2lzZSBudW1iZXIgaXMgaW5jbHVkZWQgYXMgYSBjb250cm9sIGZvciBwcmFjdGljZSB2b2x1bWUuCgojIyBIMWE6IEV4YW1wbGUgZW5nYWdlbWVudAoKYGBge3J9Cm1vZGVsX0gxYSA8LSBsbWVyKAogIEVsb19nYWluIH4gZXhhbXBsZXNfYmVmb3JlICsgZXhlcmNpc2VfbnVtYmVyICsgKDEgfCBsZWFybmVyKSwKICBkYXRhID0gYW5hbHlzaXNfZGF0YSwKICBSRU1MID0gRkFMU0UKKQoKc3VtbWFyeShtb2RlbF9IMWEpCmBgYAoKIyMgSDFiOiBQcmVwYXJhdG9yeSBlbmdhZ2VtZW50CgpgYGB7cn0KbW9kZWxfSDFiIDwtIGxtZXIoCiAgRWxvX2dhaW4gfiBwcmVwYXJhdG9yeV9iZWZvcmUgKyBleGVyY2lzZV9udW1iZXIgKyAoMSB8IGxlYXJuZXIpLAogIGRhdGEgPSBhbmFseXNpc19kYXRhLAogIFJFTUwgPSBGQUxTRQopCgpzdW1tYXJ5KG1vZGVsX0gxYikKYGBgCgojIDkuIE1vZGVsIGRpYWdub3N0aWNzCgpSZXNpZHVhbCBkaWFnbm9zdGljcyBhcmUgZXhhbWluZWQgZm9yIHRoZSBwcmltYXJ5IG1peGVkLWVmZmVjdHMgbW9kZWwuCgpgYGB7cn0KcGFyKG1mcm93ID0gYygxLCAyKSkKCnBsb3QoCiAgZml0dGVkKG1vZGVsX0gxYiksCiAgcmVzaWQobW9kZWxfSDFiKSwKICB4bGFiID0gIkZpdHRlZCB2YWx1ZXMiLAogIHlsYWIgPSAiUmVzaWR1YWxzIiwKICBtYWluID0gIlJlc2lkdWFscyB2cyBmaXR0ZWQiCikKCmFibGluZShoID0gMCwgbHR5ID0gMikKCnFxbm9ybSgKICByZXNpZChtb2RlbF9IMWIpLAogIG1haW4gPSAiTm9ybWFsIFEtUSBwbG90IgopCgpxcWxpbmUocmVzaWQobW9kZWxfSDFiKSkKCnBhcihtZnJvdyA9IGMoMSwgMSkpCmBgYAoKVGhlIHJlc2lkdWFsIGRpYWdub3N0aWNzIGluZGljYXRlIHN1YnN0YW50aWFsIGRldmlhdGlvbiBmcm9tIG5vcm1hbGl0eS4gVGhlIEVsby1jaGFuZ2UgZGlzdHJpYnV0aW9uIGFsc28gY29udGFpbnMgYSBsYXJnZSBwcm9wb3J0aW9uIG9mIHplcm8gY2hhbmdlcy4gTG9naXN0aWMgbWl4ZWQtZWZmZWN0cyBtb2RlbHMgYXJlIHRoZXJlZm9yZSB1c2VkIGJlbG93IGFzIGEgY29tcGxlbWVudGFyeSByb2J1c3RuZXNzIGFuYWx5c2lzLgoKIyAxMC4gU2Vuc2l0aXZpdHkgYW5hbHlzaXMKClRoZSBsYXJnZXN0IG9ic2VydmVkIHByZXBhcmF0b3J5LWVuZ2FnZW1lbnQgY291bnQgaXMgNDQgZXZlbnRzLiBBIHNlbnNpdGl2aXR5IGFuYWx5c2lzIGV4Y2x1ZGVzIHRoaXMgc2luZ2xlIGV4dHJlbWUgb2JzZXJ2YXRpb24gdG8gYXNzZXNzIGl0cyBpbmZsdWVuY2Ugb24gdGhlIGNvbnRpbnVvdXMgbWl4ZWQtbW9kZWwgZXN0aW1hdGVzLgoKYGBge3J9CmFuYWx5c2lzX3NlbnNpdGl2aXR5IDwtIGFuYWx5c2lzX2RhdGEgJT4lCiAgZmlsdGVyKHByZXBhcmF0b3J5X2JlZm9yZSA8IDQ0KQoKbW9kZWxfSDFhX3NlbnNpdGl2aXR5IDwtIGxtZXIoCiAgRWxvX2dhaW4gfiBleGFtcGxlc19iZWZvcmUgKyBleGVyY2lzZV9udW1iZXIgKyAoMSB8IGxlYXJuZXIpLAogIGRhdGEgPSBhbmFseXNpc19zZW5zaXRpdml0eSwKICBSRU1MID0gRkFMU0UKKQoKbW9kZWxfSDFiX3NlbnNpdGl2aXR5IDwtIGxtZXIoCiAgRWxvX2dhaW4gfiBwcmVwYXJhdG9yeV9iZWZvcmUgKyBleGVyY2lzZV9udW1iZXIgKyAoMSB8IGxlYXJuZXIpLAogIGRhdGEgPSBhbmFseXNpc19zZW5zaXRpdml0eSwKICBSRU1MID0gRkFMU0UKKQoKc3VtbWFyeShtb2RlbF9IMWFfc2Vuc2l0aXZpdHkpCnN1bW1hcnkobW9kZWxfSDFiX3NlbnNpdGl2aXR5KQpgYGAKCkNvbXBhcmUgdGhlIHByaW1hcnkgYW5kIHNlbnNpdGl2aXR5IGVzdGltYXRlcy4KCmBgYHtyfQpmaXhlZihtb2RlbF9IMWEpCmZpeGVmKG1vZGVsX0gxYV9zZW5zaXRpdml0eSkKCmZpeGVmKG1vZGVsX0gxYikKZml4ZWYobW9kZWxfSDFiX3NlbnNpdGl2aXR5KQpgYGAKCiMgMTEuIExvZ2lzdGljIHJvYnVzdG5lc3MgYW5hbHlzaXMKClRoZSBsb2dpc3RpYyByb2J1c3RuZXNzIGFuYWx5c2lzIGV4YW1pbmVzIHdoZXRoZXIgZW5nYWdlbWVudCBpcyBhc3NvY2lhdGVkIHdpdGggdGhlIHByb2JhYmlsaXR5IG9mIGEgcG9zaXRpdmUgRWxvIGNoYW5nZS4KCkhlcmUsIGBFbG9faW5jcmVhc2VkID0gVFJVRWAgd2hlbiBgRWxvX2dhaW4gPiAwYC4gWmVybyBhbmQgbmVnYXRpdmUgY2hhbmdlcyBhcmUgdGhlcmVmb3JlIGNsYXNzaWZpZWQgYXMgbm8gcG9zaXRpdmUgaW5jcmVhc2UuCgojIyBFdmVudC1jb3VudCBtb2RlbHMKCmBgYHtyfQptb2RlbF9IMWFfbG9naXN0aWMgPC0gZ2xtZXIoCiAgRWxvX2luY3JlYXNlZCB+IGV4YW1wbGVzX2JlZm9yZSArIGV4ZXJjaXNlX251bWJlciArICgxIHwgbGVhcm5lciksCiAgZGF0YSA9IGFuYWx5c2lzX2RhdGEsCiAgZmFtaWx5ID0gYmlub21pYWwKKQoKbW9kZWxfSDFiX2xvZ2lzdGljIDwtIGdsbWVyKAogIEVsb19pbmNyZWFzZWQgfiBwcmVwYXJhdG9yeV9iZWZvcmUgKyBleGVyY2lzZV9udW1iZXIgKyAoMSB8IGxlYXJuZXIpLAogIGRhdGEgPSBhbmFseXNpc19kYXRhLAogIGZhbWlseSA9IGJpbm9taWFsCikKCnN1bW1hcnkobW9kZWxfSDFhX2xvZ2lzdGljKQpzdW1tYXJ5KG1vZGVsX0gxYl9sb2dpc3RpYykKYGBgCgojIyBBbnkgZW5nYWdlbWVudCB2ZXJzdXMgbm8gZW5nYWdlbWVudAoKYGBge3J9Cm1vZGVsX0gxYV9sb2dpc3RpY19hbnkgPC0gZ2xtZXIoCiAgRWxvX2luY3JlYXNlZCB+IGFueV9leGFtcGxlcyArIGV4ZXJjaXNlX251bWJlciArICgxIHwgbGVhcm5lciksCiAgZGF0YSA9IGFuYWx5c2lzX2RhdGEsCiAgZmFtaWx5ID0gYmlub21pYWwKKQoKbW9kZWxfSDFiX2xvZ2lzdGljX2FueSA8LSBnbG1lcigKICBFbG9faW5jcmVhc2VkIH4gYW55X3ByZXBhcmF0aW9uICsgZXhlcmNpc2VfbnVtYmVyICsgKDEgfCBsZWFybmVyKSwKICBkYXRhID0gYW5hbHlzaXNfZGF0YSwKICBmYW1pbHkgPSBiaW5vbWlhbAopCgpzdW1tYXJ5KG1vZGVsX0gxYV9sb2dpc3RpY19hbnkpCnN1bW1hcnkobW9kZWxfSDFiX2xvZ2lzdGljX2FueSkKYGBgCgojIDEyLiBUYWJsZXMgZm9yIHRoZSBwYXBlcgoKIyMgVGFibGUgMTogQW5hbHl0aWNhbCBzYW1wbGUgYW5kIEVsbyBjaGFuZ2VzCgpgYGB7cn0KdGFibGUxIDwtIGFuYWx5c2lzX2RhdGEgJT4lCiAgc3VtbWFyaXNlKAogICAgYEV4ZXJjaXNlIHRyYW5zaXRpb25zYCA9IG4oKSwKICAgIGBMZWFybmVyc2AgPSBuX2Rpc3RpbmN0KGxlYXJuZXIpLAogICAgYE1lYW4gRWxvIGNoYW5nZWAgPSByb3VuZChtZWFuKEVsb19nYWluKSwgMiksCiAgICBgU0QgRWxvIGNoYW5nZWAgPSByb3VuZChzZChFbG9fZ2FpbiksIDIpLAogICAgYE1lZGlhbiBFbG8gY2hhbmdlYCA9IG1lZGlhbihFbG9fZ2FpbiksCiAgICBgTWluaW11bWAgPSBtaW4oRWxvX2dhaW4pLAogICAgYE1heGltdW1gID0gbWF4KEVsb19nYWluKSwKICAgIGBQb3NpdGl2ZSwgbiAoJSlgID0gcGFzdGUwKAogICAgICBzdW0oRWxvX2dhaW4gPiAwKSwKICAgICAgIiAoIiwKICAgICAgcm91bmQobWVhbihFbG9fZ2FpbiA+IDApICogMTAwLCAxKSwKICAgICAgIiUpIgogICAgKSwKICAgIGBaZXJvLCBuICglKWAgPSBwYXN0ZTAoCiAgICAgIHN1bShFbG9fZ2FpbiA9PSAwKSwKICAgICAgIiAoIiwKICAgICAgcm91bmQobWVhbihFbG9fZ2FpbiA9PSAwKSAqIDEwMCwgMSksCiAgICAgICIlKSIKICAgICksCiAgICBgTmVnYXRpdmUsIG4gKCUpYCA9IHBhc3RlMCgKICAgICAgc3VtKEVsb19nYWluIDwgMCksCiAgICAgICIgKCIsCiAgICAgIHJvdW5kKG1lYW4oRWxvX2dhaW4gPCAwKSAqIDEwMCwgMSksCiAgICAgICIlKSIKICAgICkKICApCgpwcmludCh0YWJsZTEpCmBgYAoKIyMgVGFibGUgMjogRWxvIGNoYW5nZSBieSBwcmVwYXJhdG9yeSBlbmdhZ2VtZW50CgpgYGB7cn0KdGFibGUyIDwtIGFuYWx5c2lzX2RhdGEgJT4lCiAgbXV0YXRlKAogICAgcHJlcGFyYXRpb25fZ3JvdXAgPSBjYXNlX3doZW4oCiAgICAgIGV4YW1wbGVzX2JlZm9yZSA9PSAwICYgY2hhbGxlbmdlc19iZWZvcmUgPT0gMCB+ICJOb25lIiwKICAgICAgZXhhbXBsZXNfYmVmb3JlID4gMCAmIGNoYWxsZW5nZXNfYmVmb3JlID09IDAgfiAiRXhhbXBsZXMgb25seSIsCiAgICAgIGV4YW1wbGVzX2JlZm9yZSA9PSAwICYgY2hhbGxlbmdlc19iZWZvcmUgPiAwIH4gIkNoYWxsZW5nZXMgb25seSIsCiAgICAgIGV4YW1wbGVzX2JlZm9yZSA+IDAgJiBjaGFsbGVuZ2VzX2JlZm9yZSA+IDAgfiAiRXhhbXBsZXMgKyBjaGFsbGVuZ2VzIgogICAgKSwKICAgIHByZXBhcmF0aW9uX2dyb3VwID0gZmFjdG9yKAogICAgICBwcmVwYXJhdGlvbl9ncm91cCwKICAgICAgbGV2ZWxzID0gYygKICAgICAgICAiTm9uZSIsCiAgICAgICAgIkV4YW1wbGVzIG9ubHkiLAogICAgICAgICJDaGFsbGVuZ2VzIG9ubHkiLAogICAgICAgICJFeGFtcGxlcyArIGNoYWxsZW5nZXMiCiAgICAgICkKICAgICkKICApICU+JQogIGdyb3VwX2J5KHByZXBhcmF0aW9uX2dyb3VwKSAlPiUKICBzdW1tYXJpc2UoCiAgICBuID0gbigpLAogICAgbWVhbl9FbG9fY2hhbmdlID0gcm91bmQobWVhbihFbG9fZ2FpbiksIDIpLAogICAgU0QgPSByb3VuZChzZChFbG9fZ2FpbiksIDIpLAogICAgbWVkaWFuX0Vsb19jaGFuZ2UgPSBtZWRpYW4oRWxvX2dhaW4pLAogICAgLmdyb3VwcyA9ICJkcm9wIgogICkKCnByaW50KHRhYmxlMikKYGBgCgojIyBUYWJsZSAzOiBQcmltYXJ5IGxpbmVhciBtaXhlZC1lZmZlY3RzIG1vZGVscwoKRXh0cmFjdCBjb2VmZmljaWVudHMgYW5kIDk1JSBXYWxkIGNvbmZpZGVuY2UgaW50ZXJ2YWxzIGRpcmVjdGx5IGZyb20gdGhlIGZpdHRlZCBtb2RlbHMuCgpgYGB7cn0KY29lZl9IMWEgPC0gYXMuZGF0YS5mcmFtZShjb2VmKHN1bW1hcnkobW9kZWxfSDFhKSkpCmNvZWZfSDFiIDwtIGFzLmRhdGEuZnJhbWUoY29lZihzdW1tYXJ5KG1vZGVsX0gxYikpKQoKY2lfSDFhIDwtIGFzLmRhdGEuZnJhbWUoCiAgY29uZmludCgKICAgIG1vZGVsX0gxYSwKICAgIHBhcm0gPSAiYmV0YV8iLAogICAgbWV0aG9kID0gIldhbGQiCiAgKQopCgpjaV9IMWIgPC0gYXMuZGF0YS5mcmFtZSgKICBjb25maW50KAogICAgbW9kZWxfSDFiLAogICAgcGFybSA9ICJiZXRhXyIsCiAgICBtZXRob2QgPSAiV2FsZCIKICApCikKYGBgCgpDcmVhdGUgVGFibGUgMy4KClRoZSBpbnRlcmNlcHRzIGFyZSBvbWl0dGVkIGZyb20gdGhlIHBhcGVyIHRhYmxlIHNvIHRoYXQgdGhlIHRhYmxlIGZvY3VzZXMgb24gdGhlIHByZWRpY3RvcnMgcmVsZXZhbnQgdG8gSDFhIGFuZCBIMWIuCgpgYGB7cn0KdGFibGUzX0gxYSA8LSBkYXRhLmZyYW1lKAogIE1vZGVsID0gIkgxYSIsCiAgUHJlZGljdG9yID0gcm93bmFtZXMoY29lZl9IMWEpLAogIEJldGEgPSBjb2VmX0gxYSRFc3RpbWF0ZSwKICBTRSA9IGNvZWZfSDFhJGBTdGQuIEVycm9yYCwKICB0ID0gY29lZl9IMWEkYHQgdmFsdWVgLAogIENJX2xvdyA9IGNpX0gxYSRgMi41ICVgLAogIENJX2hpZ2ggPSBjaV9IMWEkYDk3LjUgJWAKKQoKdGFibGUzX0gxYiA8LSBkYXRhLmZyYW1lKAogIE1vZGVsID0gIkgxYiIsCiAgUHJlZGljdG9yID0gcm93bmFtZXMoY29lZl9IMWIpLAogIEJldGEgPSBjb2VmX0gxYiRFc3RpbWF0ZSwKICBTRSA9IGNvZWZfSDFiJGBTdGQuIEVycm9yYCwKICB0ID0gY29lZl9IMWIkYHQgdmFsdWVgLAogIENJX2xvdyA9IGNpX0gxYiRgMi41ICVgLAogIENJX2hpZ2ggPSBjaV9IMWIkYDk3LjUgJWAKKQoKdGFibGUzIDwtIGJpbmRfcm93cygKICB0YWJsZTNfSDFhLAogIHRhYmxlM19IMWIKKSAlPiUKICBmaWx0ZXIoUHJlZGljdG9yICE9ICIoSW50ZXJjZXB0KSIpICU+JQogIG11dGF0ZSgKICAgIFByZWRpY3RvciA9IHJlY29kZSgKICAgICAgUHJlZGljdG9yLAogICAgICBleGFtcGxlc19iZWZvcmUgPSAiRXhhbXBsZSBlbmdhZ2VtZW50IGV2ZW50cyIsCiAgICAgIHByZXBhcmF0b3J5X2JlZm9yZSA9ICJQcmVwYXJhdG9yeSBlbmdhZ2VtZW50IGV2ZW50cyIsCiAgICAgIGV4ZXJjaXNlX251bWJlciA9ICJFeGVyY2lzZSBudW1iZXIiCiAgICApLAogICAgYWNyb3NzKAogICAgICBjKEJldGEsIFNFLCB0LCBDSV9sb3csIENJX2hpZ2gpLAogICAgICB+IHJvdW5kKC54LCAzKQogICAgKQogICkKCnByaW50KHRhYmxlMykKYGBgCgojIyBUYWJsZSA0OiBMb2dpc3RpYyByb2J1c3RuZXNzIGFuYWx5c2lzCgpGdW5jdGlvbiBmb3IgZXh0cmFjdGluZyBvZGRzIHJhdGlvcywgV2FsZCBjb25maWRlbmNlIGludGVydmFscywgYW5kIHAtdmFsdWVzLgoKYGBge3J9CmV4dHJhY3RfbG9naXN0aWNfcmVzdWx0IDwtIGZ1bmN0aW9uKG1vZGVsLCBwcmVkaWN0b3IsIGxhYmVsKSB7CgogIG1vZGVsX3N1bW1hcnkgPC0gY29lZihzdW1tYXJ5KG1vZGVsKSkKCiAgYmV0YSA8LSBtb2RlbF9zdW1tYXJ5W3ByZWRpY3RvciwgIkVzdGltYXRlIl0KICBzZSA8LSBtb2RlbF9zdW1tYXJ5W3ByZWRpY3RvciwgIlN0ZC4gRXJyb3IiXQogIHBfdmFsdWUgPC0gbW9kZWxfc3VtbWFyeVtwcmVkaWN0b3IsICJQcig+fHp8KSJdCgogIGRhdGEuZnJhbWUoCiAgICBQcmVkaWN0b3IgPSBsYWJlbCwKICAgIE9SID0gZXhwKGJldGEpLAogICAgQ0lfbG93ID0gZXhwKGJldGEgLSAxLjk2ICogc2UpLAogICAgQ0lfaGlnaCA9IGV4cChiZXRhICsgMS45NiAqIHNlKSwKICAgIHAgPSBwX3ZhbHVlCiAgKQp9CmBgYAoKQ3JlYXRlIFRhYmxlIDQuCgpgYGB7cn0KdGFibGU0IDwtIGJpbmRfcm93cygKICBleHRyYWN0X2xvZ2lzdGljX3Jlc3VsdCgKICAgIG1vZGVsX0gxYV9sb2dpc3RpYywKICAgICJleGFtcGxlc19iZWZvcmUiLAogICAgIkV4YW1wbGUtZXZlbnQgY291bnQiCiAgKSwKICBleHRyYWN0X2xvZ2lzdGljX3Jlc3VsdCgKICAgIG1vZGVsX0gxYl9sb2dpc3RpYywKICAgICJwcmVwYXJhdG9yeV9iZWZvcmUiLAogICAgIlByZXBhcmF0b3J5LWV2ZW50IGNvdW50IgogICksCiAgZXh0cmFjdF9sb2dpc3RpY19yZXN1bHQoCiAgICBtb2RlbF9IMWFfbG9naXN0aWNfYW55LAogICAgImFueV9leGFtcGxlc1RSVUUiLAogICAgIkFueSBleGFtcGxlIGVuZ2FnZW1lbnQiCiAgKSwKICBleHRyYWN0X2xvZ2lzdGljX3Jlc3VsdCgKICAgIG1vZGVsX0gxYl9sb2dpc3RpY19hbnksCiAgICAiYW55X3ByZXBhcmF0aW9uVFJVRSIsCiAgICAiQW55IHByZXBhcmF0b3J5IGVuZ2FnZW1lbnQiCiAgKQopICU+JQogIG11dGF0ZSgKICAgIGFjcm9zcygKICAgICAgYyhPUiwgQ0lfbG93LCBDSV9oaWdoKSwKICAgICAgfiByb3VuZCgueCwgMykKICAgICksCiAgICBwID0gaWZfZWxzZSgKICAgICAgcCA8IC4wMDEsCiAgICAgICI8IC4wMDEiLAogICAgICBmb3JtYXQocm91bmQocCwgMyksIG5zbWFsbCA9IDMpCiAgICApCiAgKQoKcHJpbnQodGFibGU0KQpgYGAKCiMgMTMuIEZpZ3VyZXMgZm9yIHRoZSBwYXBlcgoKIyMgRmlndXJlIDE6IERpc3RyaWJ1dGlvbiBvZiBFbG8gcmF0aW5nIGNoYW5nZXMKCmBgYHtyfQpnZ3Bsb3QoYW5hbHlzaXNfZGF0YSwgYWVzKHggPSBFbG9fZ2FpbikpICsKICBnZW9tX2hpc3RvZ3JhbSgKICAgIGJpbndpZHRoID0gNSwKICAgIGJvdW5kYXJ5ID0gMCwKICAgIGNvbG9yID0gImJsYWNrIgogICkgKwogIGxhYnMoCiAgICB0aXRsZSA9ICJEaXN0cmlidXRpb24gb2YgRWxvIHJhdGluZyBjaGFuZ2VzIiwKICAgIHN1YnRpdGxlID0gcGFzdGUwKAogICAgICAiQ2hhbmdlcyBiZXR3ZWVuIGNvbnNlY3V0aXZlIG9ic2VydmVkIGV4ZXJjaXNlIGV2ZW50cyAoTiA9ICIsCiAgICAgIG5yb3coYW5hbHlzaXNfZGF0YSksCiAgICAgICIpIgogICAgKSwKICAgIHggPSAiRWxvIHJhdGluZyBjaGFuZ2UiLAogICAgeSA9ICJOdW1iZXIgb2YgZXhlcmNpc2UgdHJhbnNpdGlvbnMiCiAgKSArCiAgdGhlbWVfbWluaW1hbCgpCmBgYAoKIyMgRmlndXJlIDI6IE1lYW4gRWxvIGNoYW5nZSBieSBwcmVwYXJhdG9yeSBlbmdhZ2VtZW50CgpgYGB7cn0KcGxvdF9kYXRhIDwtIGFuYWx5c2lzX2RhdGEgJT4lCiAgbXV0YXRlKAogICAgcHJlcGFyYXRpb25fZ3JvdXAgPSBjYXNlX3doZW4oCiAgICAgIGV4YW1wbGVzX2JlZm9yZSA9PSAwICYgY2hhbGxlbmdlc19iZWZvcmUgPT0gMCB+ICJOb25lIiwKICAgICAgZXhhbXBsZXNfYmVmb3JlID4gMCAmIGNoYWxsZW5nZXNfYmVmb3JlID09IDAgfiAiRXhhbXBsZXMgb25seSIsCiAgICAgIGV4YW1wbGVzX2JlZm9yZSA9PSAwICYgY2hhbGxlbmdlc19iZWZvcmUgPiAwIH4gIkNoYWxsZW5nZXMgb25seSIsCiAgICAgIGV4YW1wbGVzX2JlZm9yZSA+IDAgJiBjaGFsbGVuZ2VzX2JlZm9yZSA+IDAgfiAiRXhhbXBsZXMgKyBjaGFsbGVuZ2VzIgogICAgKSwKICAgIHByZXBhcmF0aW9uX2dyb3VwID0gZmFjdG9yKAogICAgICBwcmVwYXJhdGlvbl9ncm91cCwKICAgICAgbGV2ZWxzID0gYygKICAgICAgICAiTm9uZSIsCiAgICAgICAgIkV4YW1wbGVzIG9ubHkiLAogICAgICAgICJDaGFsbGVuZ2VzIG9ubHkiLAogICAgICAgICJFeGFtcGxlcyArIGNoYWxsZW5nZXMiCiAgICAgICkKICAgICkKICApICU+JQogIGdyb3VwX2J5KHByZXBhcmF0aW9uX2dyb3VwKSAlPiUKICBzdW1tYXJpc2UoCiAgICBuID0gbigpLAogICAgbWVhbiA9IG1lYW4oRWxvX2dhaW4pLAogICAgU0UgPSBzZChFbG9fZ2FpbikgLyBzcXJ0KG4pLAogICAgbG93ZXIgPSBtZWFuIC0gMS45NiAqIFNFLAogICAgdXBwZXIgPSBtZWFuICsgMS45NiAqIFNFLAogICAgLmdyb3VwcyA9ICJkcm9wIgogICkgJT4lCiAgbXV0YXRlKAogICAgZ3JvdXBfbGFiZWwgPSBwYXN0ZTAoCiAgICAgIHByZXBhcmF0aW9uX2dyb3VwLAogICAgICAiXG4obiA9ICIsCiAgICAgIG4sCiAgICAgICIpIgogICAgKQogICkKCmdncGxvdCgKICBwbG90X2RhdGEsCiAgYWVzKHggPSBncm91cF9sYWJlbCwgeSA9IG1lYW4pCikgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDMpICsKICBnZW9tX2Vycm9yYmFyKAogICAgYWVzKAogICAgICB5bWluID0gbG93ZXIsCiAgICAgIHltYXggPSB1cHBlcgogICAgKSwKICAgIHdpZHRoID0gMC4xNQogICkgKwogIGdlb21faGxpbmUoCiAgICB5aW50ZXJjZXB0ID0gMCwKICAgIGxpbmV0eXBlID0gImRhc2hlZCIKICApICsKICBsYWJzKAogICAgdGl0bGUgPSAiTWVhbiBFbG8gY2hhbmdlIGJ5IHByZXBhcmF0b3J5IGVuZ2FnZW1lbnQiLAogICAgc3VidGl0bGUgPSAiUG9pbnRzIHNob3cgZ3JvdXAgbWVhbnM7IGVycm9yIGJhcnMgc2hvdyA5NSUgY29uZmlkZW5jZSBpbnRlcnZhbHMiLAogICAgeCA9ICJFbmdhZ2VtZW50IGJldHdlZW4gY29uc2VjdXRpdmUgZXhlcmNpc2UgZXZlbnRzIiwKICAgIHkgPSAiTWVhbiBFbG8gcmF0aW5nIGNoYW5nZSIKICApICsKICB0aGVtZV9taW5pbWFsKCkKYGBgCgojIyBGaWd1cmUgMzogUHJlZGljdGVkIHByb2JhYmlsaXR5IG9mIGEgcG9zaXRpdmUgRWxvIGNoYW5nZQoKUHJlZGljdGlvbnMgYXJlIHNob3duIGZvciAw4oCTOCBwcmVwYXJhdG9yeSBldmVudHMgYmVjYXVzZSBvYnNlcnZhdGlvbnMgYmVjb21lIHNwYXJzZSBhdCBoaWdoZXIgZW5nYWdlbWVudCBjb3VudHMuCgpgYGB7cn0KcHJlZGljdGlvbl9kYXRhIDwtIGRhdGEuZnJhbWUoCiAgcHJlcGFyYXRvcnlfYmVmb3JlID0gMDo4LAogIGV4ZXJjaXNlX251bWJlciA9IG1lZGlhbihhbmFseXNpc19kYXRhJGV4ZXJjaXNlX251bWJlcikKKQoKcHJlZGljdGlvbl9kYXRhJHByZWRpY3RlZF9wcm9iYWJpbGl0eSA8LSBwcmVkaWN0KAogIG1vZGVsX0gxYl9sb2dpc3RpYywKICBuZXdkYXRhID0gcHJlZGljdGlvbl9kYXRhLAogIHR5cGUgPSAicmVzcG9uc2UiLAogIHJlLmZvcm0gPSBOQQopCgpnZ3Bsb3QoCiAgcHJlZGljdGlvbl9kYXRhLAogIGFlcygKICAgIHggPSBwcmVwYXJhdG9yeV9iZWZvcmUsCiAgICB5ID0gcHJlZGljdGVkX3Byb2JhYmlsaXR5CiAgKQopICsKICBnZW9tX2xpbmUobGluZXdpZHRoID0gMSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoCiAgICBsYWJlbHMgPSBzY2FsZXM6OnBlcmNlbnRfZm9ybWF0KGFjY3VyYWN5ID0gMSkKICApICsKICBsYWJzKAogICAgdGl0bGUgPSAiUHJlZGljdGVkIHByb2JhYmlsaXR5IG9mIGEgcG9zaXRpdmUgRWxvIGNoYW5nZSIsCiAgICBzdWJ0aXRsZSA9IHBhc3RlMCgKICAgICAgIkV4ZXJjaXNlIHBvc2l0aW9uIGhlbGQgYXQgaXRzIG1lZGlhbjsgTiA9ICIsCiAgICAgIG5yb3coYW5hbHlzaXNfZGF0YSksCiAgICAgICIgdHJhbnNpdGlvbnMiCiAgICApLAogICAgeCA9ICJQcmVwYXJhdG9yeSBlbmdhZ2VtZW50IGV2ZW50cyBiZWZvcmUgZXhlcmNpc2UiLAogICAgeSA9ICJQcmVkaWN0ZWQgcHJvYmFiaWxpdHkiCiAgKSArCiAgdGhlbWVfbWluaW1hbCgpCmBgYAoKIyMgRmlndXJlIDQ6IFBvc2l0aXZlIEVsbyBjaGFuZ2VzIHdpdGggYW5kIHdpdGhvdXQgcHJlcGFyYXRpb24KCmBgYHtyfQphbnlfcHJlcF9wbG90IDwtIGFuYWx5c2lzX2RhdGEgJT4lCiAgbXV0YXRlKAogICAgcHJlcGFyYXRpb24gPSBpZl9lbHNlKAogICAgICBwcmVwYXJhdG9yeV9iZWZvcmUgPiAwLAogICAgICAiQW55IHByZXBhcmF0aW9uIiwKICAgICAgIk5vIHByZXBhcmF0aW9uIgogICAgKSwKICAgIHByZXBhcmF0aW9uID0gZmFjdG9yKAogICAgICBwcmVwYXJhdGlvbiwKICAgICAgbGV2ZWxzID0gYygKICAgICAgICAiTm8gcHJlcGFyYXRpb24iLAogICAgICAgICJBbnkgcHJlcGFyYXRpb24iCiAgICAgICkKICAgICkKICApICU+JQogIGdyb3VwX2J5KHByZXBhcmF0aW9uKSAlPiUKICBzdW1tYXJpc2UoCiAgICBuID0gbigpLAogICAgcG9zaXRpdmVfbiA9IHN1bShFbG9fZ2FpbiA+IDApLAogICAgcG9zaXRpdmVfcGN0ID0gbWVhbihFbG9fZ2FpbiA+IDApICogMTAwLAogICAgLmdyb3VwcyA9ICJkcm9wIgogICkgJT4lCiAgbXV0YXRlKAogICAgbGFiZWwgPSBwYXN0ZTAoCiAgICAgIHByZXBhcmF0aW9uLAogICAgICAiXG4obiA9ICIsCiAgICAgIG4sCiAgICAgICIpIgogICAgKQogICkKCnByaW50KGFueV9wcmVwX3Bsb3QpCgpnZ3Bsb3QoCiAgYW55X3ByZXBfcGxvdCwKICBhZXMoeCA9IGxhYmVsLCB5ID0gcG9zaXRpdmVfcGN0KQopICsKICBnZW9tX2NvbCh3aWR0aCA9IDAuNikgKwogIGdlb21fdGV4dCgKICAgIGFlcygKICAgICAgbGFiZWwgPSBwYXN0ZTAoCiAgICAgICAgcm91bmQocG9zaXRpdmVfcGN0LCAxKSwKICAgICAgICAiJSIKICAgICAgKQogICAgKSwKICAgIHZqdXN0ID0gLTAuNQogICkgKwogIGxhYnMoCiAgICB0aXRsZSA9ICJQb3NpdGl2ZSBFbG8gY2hhbmdlcyBieSBwcmVwYXJhdG9yeSBlbmdhZ2VtZW50IiwKICAgIHggPSBOVUxMLAogICAgeSA9ICJFeGVyY2lzZSB0cmFuc2l0aW9ucyB3aXRoIHBvc2l0aXZlIEVsbyBjaGFuZ2UgKCUpIgogICkgKwogIHRoZW1lX21pbmltYWwoKQpgYGA=