library(scales) # percent ggplot scale
library(rstatix)  # pipe-ready R functions
library(ggpubr)  # Some ggplot based plots
library(lmerTest)
library(patchwork)
library(MASS) # for boxplot
library(ARTool)
library(emmeans)
library(ez)
library(tidyverse)
library(svglite)
source("./device-setup.R")

H_GRAPH_MARGIN = 10
BASE_WIDTH = (FULL_WIDTH - H_GRAPH_MARGIN) / 4
BASE_HEIGHT = (FULL_WIDTH - H_GRAPH_MARGIN) / 4

normalCheck <- function(model) {
    res = residuals(model)
    qqnorm((res - mean(res)) / sd(res))
    abline(0, 1)
    print (shapiro.test(res))
}

line_graph <- function(completed_trials, measure, min_value=NA, max_value=NA) {
  graph_data <- completed_trials |>
    rename(value = measure) |>
    group_by(participant, accuracy, keytime, suggestions_type) |>
    summarize(
      p_value = mean(value),
      .groups="drop"
    ) |>
    group_by(accuracy, keytime, suggestions_type) |>
    summarize(
      error_value = t_error(p_value),
      mean_value = mean(p_value),
      ci_min = if_else(is.na(min_value), mean_value - error_value, max(min_value, mean_value - error_value)),
      ci_max = if_else(is.na(max_value), mean_value + error_value, min(max_value, mean_value + error_value)),
      .groups="drop"
    )
  ggplot(graph_data, aes(
    x = accuracy |> factor_to_numeric(),
    y = mean_value,
    ymin = ci_min,
    ymax = ci_max,
    color=keytime |> factor_to_numeric(),
    group=keytime |> factor_to_numeric()
  )) +
    scale_x_continuous(breaks=ACCURACY_LEVELS) +
    scale_color_continuous(breaks=keytime_LEVELS) +
    geom_line() +
    geom_pointrange()
}

measured_runs <- read_device_runs(measured_only=TRUE)
measured_trials <- read_device_trials(measured_only=TRUE)

1 Suggestion Usage

1.1 Summaries

p_data_usage <- measured_trials |>
  group_by(participant, device, accuracy, accuracy_numeric) |>
  summarize(mean_suggestion_use = mean(total_suggestion_used),
            .groups = "drop")

summary_usage <- p_data_usage |>
  group_by(accuracy_numeric, device) |>
  summarize(
    error_suggestion_used = t_error(mean_suggestion_use),
    mean_suggestion_used = mean(mean_suggestion_use),
    min_suggestion_used = max(0, mean_suggestion_used - error_suggestion_used),
    max_suggestion_used = mean_suggestion_used + error_suggestion_used,
    .groups = "drop"
  )

summary_usage

1.1.1 Per devices

measured_trials |>
  group_by(device, participant) |>
  summarize(mean_suggestion_use = mean(total_suggestion_used), .groups="drop_last")  |>
  summarize(mean_suggestion_used = mean(mean_suggestion_use), .groups = "drop")

1.1.2 Per accuracy

measured_trials |>
  group_by(accuracy, participant) |>
  summarize(mean_suggestion_use = mean(total_suggestion_used), .groups="drop_last")  |>
  summarize(mean_suggestion_used = mean(mean_suggestion_use), .groups = "drop")

1.2 Graph

pd <- position_dodge(0.025)

suggestion_usage_plot <- ggplot(
  summary_usage,
  aes(
    x = accuracy_numeric,
    y = mean_suggestion_used,
    ymin = min_suggestion_used,
    ymax = max_suggestion_used,
    color = device,
    group = device
  )
) +
  expand_limits(y = c(0)) +
  custom_line(position = pd) +
  SCALE_X_ACCURACY +
  scale_color_manual("Device", values = DEVICE_COLORS) +
  scale_y_continuous("Suggestion Usage per Trial", breaks = seq(0, 6, 1)) +
  custom_pointrange(position = pd) +
  theme(legend.position = "none",
        axis.title.x = element_blank())

ggsave(
  graph_path("suggestions-usage-devices.pdf"),
  units = "mm",
  width = BASE_WIDTH,
  height = BASE_HEIGHT,
  device = cairo_pdf
)

suggestion_usage_plot

1.3 Normality

p_data_usage |>
  group_by(accuracy, device) |>
  shapiro_test(mean_suggestion_use)
ggqqplot(p_data_usage, "mean_suggestion_use", ggtheme = theme_bw()) +
  facet_grid(accuracy ~ device)

Not normal.

# Comes from https://stackoverflow.com/a/34002020.

# We need to shift it because we have non positive values...
bc <-
  boxcox((mean_suggestion_use + 1e-10) ~ device * accuracy, data =
           p_data_usage)

lambda <- bc$x[which.max(bc$y)]

powerTransform <-
  function(y,
           lambda1,
           lambda2 = NULL,
           method = "boxcox") {
    boxcoxTrans <- function(x, lam1, lam2 = NULL) {
      # if we set lambda2 to zero, it becomes the one parameter transformation
      lam2 <- ifelse(is.null(lam2), 0, lam2)
      if (lam1 == 0L) {
        log(y + lam2)
      } else {
        (((y + lam2) ^ lam1) - 1) / lam1
      }
    }
    switch(method,
           boxcox = boxcoxTrans(y, lambda1, lambda2),
           tukey = y ^ lambda1)
  }

p_data_usage <- p_data_usage |>
  mutate(transformed_suggestion_use = powerTransform(mean_suggestion_use, lambda))

ggqqplot(p_data_usage, "transformed_suggestion_use", ggtheme = theme_bw()) +
  facet_grid(accuracy ~ device)

Boxcox won’t work. ART it is.

1.4 ART + ANOVA

m_usage = art(mean_suggestion_use ~ device * accuracy + (1|participant),
                  data = p_data_usage)
anova(m_usage)

1.5 Pairwise comparisons on Device

emmeans(artlm(m_usage, "device"), pairwise ~ device)
NOTE: Results may be misleading due to involvement in interactions
$emmeans
 device  emmean   SE   df lower.CL upper.CL
 desktop   24.4 3.85 74.9     16.7     32.1
 tablet    63.4 3.85 74.9     55.7     71.1
 phone     75.7 3.85 74.9     68.0     83.3

Results are averaged over the levels of: accuracy 
Degrees-of-freedom method: kenward-roger 
Confidence level used: 0.95 

$contrasts
 contrast         estimate   SE df t.ratio p.value
 desktop - tablet    -39.0 4.22 66  -9.248  <.0001
 desktop - phone     -51.2 4.22 66 -12.153  <.0001
 tablet - phone      -12.2 4.22 66  -2.905  0.0137

Results are averaged over the levels of: accuracy 
Degrees-of-freedom method: kenward-roger 
P value adjustment: tukey method for comparing a family of 3 estimates 

1.6 Pairwise comparisons on Accuracy

emmeans(artlm(m_usage, "accuracy"), pairwise ~ accuracy)
NOTE: Results may be misleading due to involvement in interactions
$emmeans
 accuracy emmean   SE df lower.CL upper.CL
 0.1        29.0 4.87 33     19.1     38.9
 0.5        50.2 4.87 33     40.3     60.1
 0.9        84.3 4.87 33     74.4     94.2

Results are averaged over the levels of: device 
Degrees-of-freedom method: kenward-roger 
Confidence level used: 0.95 

$contrasts
 contrast                  estimate   SE df t.ratio p.value
 accuracy0.1 - accuracy0.5    -21.1 6.89 33  -3.068  0.0116
 accuracy0.1 - accuracy0.9    -55.3 6.89 33  -8.022  <.0001
 accuracy0.5 - accuracy0.9    -34.1 6.89 33  -4.954  0.0001

Results are averaged over the levels of: device 
Degrees-of-freedom method: kenward-roger 
P value adjustment: tukey method for comparing a family of 3 estimates 

1.7 Interaction Contrasts on Accuracy X Device

Pairwise comparisons are not reliable with ART. Instead, we perform interaction contrasts. See http://depts.washington.edu/acelab/proj/art/index.html.

contrast(
  emmeans(
    artlm(m_usage, "device:accuracy"),
    ~ device:accuracy
  ),
  method = "pairwise",
  interaction = TRUE
)
 device_pairwise  accuracy_pairwise estimate   SE df t.ratio p.value
 desktop - tablet 0.1 - 0.5            23.33 11.6 66   2.015  0.0479
 desktop - phone  0.1 - 0.5            36.25 11.6 66   3.131  0.0026
 tablet - phone   0.1 - 0.5            12.92 11.6 66   1.116  0.2686
 desktop - tablet 0.1 - 0.9            87.00 11.6 66   7.515  <.0001
 desktop - phone  0.1 - 0.9           104.17 11.6 66   8.998  <.0001
 tablet - phone   0.1 - 0.9            17.17 11.6 66   1.483  0.1429
 desktop - tablet 0.5 - 0.9            63.67 11.6 66   5.499  <.0001
 desktop - phone  0.5 - 0.9            67.92 11.6 66   5.866  <.0001
 tablet - phone   0.5 - 0.9             4.25 11.6 66   0.367  0.7147

Degrees-of-freedom method: kenward-roger 

2 Saved Key Strokes

p_data_sks <- measured_trials |>
  group_by(participant, accuracy, accuracy_numeric, device) |>
  summarize(
    p_mean_sks = mean(actual_sks),
    .groups="drop"
  ) 

summary_sks <- p_data_sks |>
  group_by(accuracy, accuracy_numeric, device) |>
  summarize(
    error_sks = t_error(p_mean_sks),
    mean_sks = mean(p_mean_sks),
    min_sks = mean_sks - error_sks,
    max_sks = mean_sks + error_sks,
    .groups="drop"
  )
theoretical_sks <- measured_trials |>
  group_by(participant, accuracy_numeric, device) |>
  summarize(p_mean_sks = mean(theoretical_sks), .groups="drop") |>
  group_by(accuracy_numeric, device) |>
  summarize(p_mean_sks = mean(p_mean_sks), .groups="drop") |>
  group_by(accuracy_numeric) |>
  summarize(
    error_sks = t_error(p_mean_sks),
    mean_sks = mean(p_mean_sks),
    min_sks = mean_sks - error_sks,
    max_sks = mean_sks + error_sks,
    .groups="drop"
  )
pd <- position_dodge(0.025)
ggplot(summary_sks, aes(
    x = accuracy_numeric,
    y = mean_sks,
    ymin = min_sks,
    ymax = max_sks,
    color = device,
    group = device
  )) +
  geom_line(data=theoretical_sks, color = "red", group = "red") +
  geom_pointrange(data=theoretical_sks, color="red", group="red") +
  scale_x_continuous(breaks=c(0, 0.1, 0.3, 0.5, 0.7, 0.9, 1)) +
  geom_line(position=pd) +
  geom_pointrange(position=pd) +
  labs(x="Accuracy", y="Saved Keystrokes", color="Device")

3 Keystroke Saving

3.1 Summaries

p_data_ks <- measured_trials |>
  group_by(participant, accuracy, device, accuracy_numeric) |>
  summarize(
    p_mean_actual_ks = mean(actual_key_saving_no_editing),
    p_mean_theoretical_ks = mean(theoretical_key_saving),
    .groups="drop"
  ) |>
  group_by(accuracy, device) |>
  mutate(
    is_outlier = is_outlier(p_mean_actual_ks)
  ) |>
  ungroup()

actual_ks_summary <- p_data_ks |>
  group_by(accuracy_numeric, device) |>
  summarize(
    ks_type = "actual",
    error_ks = t_error(p_mean_actual_ks),
    mean_ks = mean(p_mean_actual_ks),
    min_ks = max(0, mean_ks - error_ks),
    max_ks = min(1, mean_ks + error_ks),
    .groups="drop"
  )

theoretical_ks_summary <- p_data_ks |>
  group_by(accuracy_numeric) |>
  summarize(
    ks_type = "theoretical",
    device = NA,
    error_ks = t_error(p_mean_theoretical_ks),
    mean_ks = mean(p_mean_theoretical_ks),
    min_ks = max(0, mean_ks - error_ks),
    max_ks = min(1, mean_ks + error_ks),
    .groups="drop"
  )
  
ks_summary <- union_all(theoretical_ks_summary, actual_ks_summary)

ks_summary

3.1.1 Per devices

measured_trials |>
  group_by(device, participant) |>
  summarize(m_ks = mean(actual_key_saving_no_editing), .groups="drop_last")  |>
  summarize(mean_ks = mean(m_ks), .groups = "drop")

3.1.2 Per accuracy

measured_trials |>
  group_by(accuracy, participant)  |>
  summarize(m_ks = mean(actual_key_saving_no_editing), .groups="drop_last")  |>
  summarize(mean_ks = mean(m_ks), .groups = "drop")

3.2 Graph

p_data_ks <- measured_trials |>
  group_by(participant, accuracy, device, accuracy_numeric) |>
  summarize(
    p_mean_actual_ks = mean(actual_key_saving_no_editing),
    p_mean_theoretical_ks = mean(theoretical_key_saving),
    .groups="drop"
  ) |>
  group_by(accuracy, device) |>
  mutate(
    is_outlier = is_outlier(p_mean_actual_ks)
  ) |>
  ungroup()

actual_ks_summary <- p_data_ks |>
  group_by(accuracy_numeric, device) |>
  summarize(
    error_ks = t_error(p_mean_actual_ks),
    mean_ks = mean(p_mean_actual_ks),
    min_ks = max(0, mean_ks - error_ks),
    max_ks = min(1, mean_ks + error_ks),
    ks_type = "actual",
    .groups="drop"
  )

theoretical_ks_summary <- p_data_ks |>
  group_by(accuracy_numeric) |>
  summarize(
    device=NA,
    error_ks = t_error(p_mean_theoretical_ks),
    mean_ks = mean(p_mean_theoretical_ks),
    min_ks = max(0, mean_ks - error_ks),
    max_ks = min(1, mean_ks + error_ks),
    ks_type = "theoretical",
    .groups="drop"
  )
  
ks_summary <- union_all(theoretical_ks_summary, actual_ks_summary)

pd <- position_dodge(0.025)
key_stroke_saving_plot <- ggplot(ks_summary, aes(
    x = accuracy_numeric,
    y = mean_ks,
    ymin = min_ks,
    ymax = max_ks,
    color = device,
    group = device
  )) +
  custom_line(
    data=theoretical_ks_summary,
    color=THEORETICAL_COLOR,
    group="theoretical"
  ) +
  custom_pointrange(
    data=theoretical_ks_summary,
    color=THEORETICAL_COLOR,
    group="theoretical",
    shape=17
  ) +
  scale_color_manual("Device", values = DEVICE_COLORS) +
  custom_line(data=actual_ks_summary, position=pd) +
  custom_pointrange(data=actual_ks_summary, position=pd) +
  SCALE_X_ACCURACY +
  scale_y_continuous("Keystroke Saving", limits = c(0, 1), labels = percent) +
  theme(legend.position = "none",
        axis.title.x = element_blank())

ggsave(
  graph_path("keystroke-saving-devices.pdf"),
  units = "mm",
  # Manually add 2 mm because axis labels are longer.
  width = BASE_WIDTH + 2, 
  height = BASE_HEIGHT,
  device = cairo_pdf
)

key_stroke_saving_plot

3.3 Normality

p_data_ks |>
  group_by(accuracy, device) |>
  shapiro_test(p_mean_actual_ks)
ggqqplot(p_data_ks, "p_mean_actual_ks", ggtheme = theme_bw()) +
  facet_grid(accuracy ~ device)

Not normal.

# Comes from https://stackoverflow.com/a/34002020.

# We need to shift it because we have non positive values...
bc <- boxcox((p_mean_actual_ks+1e-10)~device*accuracy, data=p_data_ks)

lambda <- bc$x[which.max(bc$y)]

powerTransform <-
  function(y,
           lambda1,
           lambda2 = NULL,
           method = "boxcox") {
    boxcoxTrans <- function(x, lam1, lam2 = NULL) {
      # if we set lambda2 to zero, it becomes the one parameter transformation
      lam2 <- ifelse(is.null(lam2), 0, lam2)
      if (lam1 == 0L) {
        log(y + lam2)
      } else {
        (((y + lam2) ^ lam1) - 1) / lam1
      }
    }
    switch(method,
           boxcox = boxcoxTrans(y, lambda1, lambda2),
           tukey = y ^ lambda1)
  }

p_data_ks <- p_data_ks |> mutate(transformed_ks = powerTransform(p_mean_actual_ks, lambda))

ggqqplot(p_data_ks, "transformed_ks", ggtheme = theme_bw()) +
  facet_grid(accuracy ~ device)

Boxcox won’t work. ART it is.

3.4 ART + ANOVA

m_ks = art(p_mean_actual_ks ~ device * accuracy + (1|participant),
                  data = p_data_ks)
anova(m_ks)

3.5 Pairwise comparisons on Device

emmeans(artlm(m_ks, "device"), pairwise ~ device)
NOTE: Results may be misleading due to involvement in interactions
$emmeans
 device  emmean   SE   df lower.CL upper.CL
 desktop   24.2 3.84 86.4     16.5     31.8
 tablet    64.3 3.84 86.4     56.7     71.9
 phone     75.0 3.84 86.4     67.4     82.7

Results are averaged over the levels of: accuracy 
Degrees-of-freedom method: kenward-roger 
Confidence level used: 0.95 

$contrasts
 contrast         estimate   SE df t.ratio p.value
 desktop - tablet    -40.1 4.65 66  -8.641  <.0001
 desktop - phone     -50.9 4.65 66 -10.949  <.0001
 tablet - phone      -10.7 4.65 66  -2.308  0.0616

Results are averaged over the levels of: accuracy 
Degrees-of-freedom method: kenward-roger 
P value adjustment: tukey method for comparing a family of 3 estimates 

3.6 Pairwise comparisons on Accuracy

emmeans(artlm(m_ks, "accuracy"), pairwise ~ accuracy)
NOTE: Results may be misleading due to involvement in interactions
$emmeans
 accuracy emmean   SE df lower.CL upper.CL
 0.1        26.8 4.25 33     18.1     35.4
 0.5        50.3 4.25 33     41.7     59.0
 0.9        86.4 4.25 33     77.7     95.0

Results are averaged over the levels of: device 
Degrees-of-freedom method: kenward-roger 
Confidence level used: 0.95 

$contrasts
 contrast                  estimate   SE df t.ratio p.value
 accuracy0.1 - accuracy0.5    -23.6 6.01 33  -3.916  0.0012
 accuracy0.1 - accuracy0.9    -59.6 6.01 33  -9.911  <.0001
 accuracy0.5 - accuracy0.9    -36.1 6.01 33  -5.994  <.0001

Results are averaged over the levels of: device 
Degrees-of-freedom method: kenward-roger 
P value adjustment: tukey method for comparing a family of 3 estimates 

3.7 Interaction Contrasts comparisons on Accuracy X Device

Pairwise comparisons are not reliable with ART. Instead, we perform interaction contrasts. See http://depts.washington.edu/acelab/proj/art/index.html.

contrast(emmeans(artlm(m_ks, "device:accuracy"), ~ device:accuracy), method="pairwise", interaction=TRUE)
 device_pairwise  accuracy_pairwise estimate   SE df t.ratio p.value
 desktop - tablet 0.1 - 0.5            16.17 11.8 66   1.367  0.1762
 desktop - phone  0.1 - 0.5            28.58 11.8 66   2.417  0.0184
 tablet - phone   0.1 - 0.5            12.42 11.8 66   1.050  0.2975
 desktop - tablet 0.1 - 0.9            93.92 11.8 66   7.943  <.0001
 desktop - phone  0.1 - 0.9           113.08 11.8 66   9.564  <.0001
 tablet - phone   0.1 - 0.9            19.17 11.8 66   1.621  0.1098
 desktop - tablet 0.5 - 0.9            77.75 11.8 66   6.576  <.0001
 desktop - phone  0.5 - 0.9            84.50 11.8 66   7.147  <.0001
 tablet - phone   0.5 - 0.9             6.75 11.8 66   0.571  0.5700

Degrees-of-freedom method: kenward-roger 

4 Keystroke Saving Ratio

4.1 Summaries

p_data_tsku <- measured_trials |>
  group_by(participant, accuracy_numeric, accuracy, device) |>
  summarize(
    p_mean_ksr = mean((total_final_suggestion_chars / total_chars) / theoretical_key_saving),
    .groups="drop"
  ) 
actual_tsku_data <- p_data_tsku |>
  group_by(accuracy_numeric, accuracy, device) |>
  summarize(
    error_ksr = t_error(p_mean_ksr),
    mean_ksr = mean(p_mean_ksr),
    min_ksr = max(0, mean_ksr - error_ksr),
    max_ksr = min(1, mean_ksr + error_ksr),
    .groups="drop"
  )


actual_tsku_data
measured_trials |>
  group_by(device, participant) |>
  summarize(p_mean_ksr = mean((total_final_suggestion_chars / total_chars) / theoretical_key_saving), .groups="drop_last")  |>
  summarize(mean_ksr = mean(p_mean_ksr), .groups = "drop")

4.1.1 Per accuracy

measured_trials |>
  group_by(accuracy, participant)  |>
  summarize(p_mean_ksr = mean((total_final_suggestion_chars / total_chars) / theoretical_key_saving), .groups="drop_last")  |>
  summarize(mean_ksr = mean(p_mean_ksr), .groups = "drop")

4.2 Graphs

theoretical_tsku_data <- tibble(
  accuracy_numeric = ACCURACY_LEVELS_NUM,
  mean_ksr = 1.0,
  min_ksr = 1.0,
  max_ksr = 1.0
)
pd <- position_dodge(0.025)
keystroke_saving_ratio_plot <- ggplot(actual_tsku_data, aes(
    x = accuracy_numeric,
    y = mean_ksr,
    ymin = min_ksr,
    ymax = max_ksr,
    color = device,
    group = device
  )) +
  custom_line(
    data=theoretical_tsku_data,
    color=THEORETICAL_COLOR,
    group="theoretical"
  ) +
  custom_pointrange(
    data=theoretical_tsku_data,
    color=THEORETICAL_COLOR,
    group="theoretical",
    shape=17
  ) +
  custom_line(position=pd) +
  custom_pointrange(position=pd) +
  SCALE_X_ACCURACY +
  xlab("") +
  scale_color_manual("Device", values = DEVICE_COLORS) +
  scale_y_continuous("Keystroke Saving Ratio", limits = c(0, 1), labels = percent)+
  theme(legend.position = "none",
        axis.title.x = element_blank())

ggsave(
  graph_path("keystroke-saving-ratio-devices.pdf"),
  units = "mm",
  # Manually add 2 mm because axis labels are longer.
  width = BASE_WIDTH + 2, 
  height = BASE_HEIGHT,
  device = cairo_pdf
)

keystroke_saving_ratio_plot 

4.3 ART + ANOVA

m_ksr = art(p_mean_ksr ~ device * accuracy + (1|participant),
            data = p_data_tsku)
anova(m_ks)
emmeans(artlm(m_ksr, "device"), pairwise ~ device)
NOTE: Results may be misleading due to involvement in interactions
$emmeans
 device  emmean   SE   df lower.CL upper.CL
 desktop   25.0 3.94 83.5     17.1     32.8
 tablet    63.7 3.94 83.5     55.8     71.5
 phone     74.9 3.94 83.5     67.0     82.7

Results are averaged over the levels of: accuracy 
Degrees-of-freedom method: kenward-roger 
Confidence level used: 0.95 

$contrasts
 contrast         estimate   SE df t.ratio p.value
 desktop - tablet    -38.7 4.65 66  -8.324  <.0001
 desktop - phone     -49.9 4.65 66 -10.732  <.0001
 tablet - phone      -11.2 4.65 66  -2.408  0.0488

Results are averaged over the levels of: accuracy 
Degrees-of-freedom method: kenward-roger 
P value adjustment: tukey method for comparing a family of 3 estimates 
emmeans(artlm(m_ksr, "accuracy"), pairwise ~ accuracy)
NOTE: Results may be misleading due to involvement in interactions
$emmeans
 accuracy emmean   SE df lower.CL upper.CL
 0.1        39.4 5.29 33     28.7     50.2
 0.5        41.1 5.29 33     30.3     51.9
 0.9        83.0 5.29 33     72.2     93.7

Results are averaged over the levels of: device 
Degrees-of-freedom method: kenward-roger 
Confidence level used: 0.95 

$contrasts
 contrast                  estimate   SE df t.ratio p.value
 accuracy0.1 - accuracy0.5    -1.64 7.49 33  -0.219  0.9739
 accuracy0.1 - accuracy0.9   -43.53 7.49 33  -5.815  <.0001
 accuracy0.5 - accuracy0.9   -41.89 7.49 33  -5.596  <.0001

Results are averaged over the levels of: device 
Degrees-of-freedom method: kenward-roger 
P value adjustment: tukey method for comparing a family of 3 estimates 

5 Trial Duration

actual_graph_data <- measured_trials |>
  group_by(participant, accuracy_numeric, device) |>
  summarize(
    p_value = mean(duration),
    .groups="drop"
  ) |>
  group_by(accuracy_numeric, device) |>
  summarize(
    error_value = t_error(p_value),
    mean_value = mean(p_value),
    min_value = max(0, mean_value - error_value),
    max_value = mean_value + error_value,
    .groups="drop"
  )
pd <- position_dodge(0.025)
ggplot(actual_graph_data, aes(
    x = accuracy_numeric,
    y = mean_value,
    ymin = min_value,
    ymax = max_value,
    color = device,
    group = device
  )) +
  scale_x_continuous(breaks=c(0, 0.1, 0.3, 0.5, 0.7, 0.9, 1)) +
  scale_color_manual("Device", values = DEVICE_COLORS) +
  expand_limits(y = c(0)) +
  geom_line(position=pd) +
  geom_pointrange(position=pd) +
  labs(x="Suggestions Accuracy", y="Trial Duration (seconds)", color="Key Stroke Delay") 

6 Entry Speed

6.1 Summaries

p_data_ts <- measured_trials |>
  group_by(participant, accuracy, device, accuracy_numeric) |>
  summarize(speed = mean(cps) / 5 * 60, .groups = "drop")

summary_ts <- p_data_ts |>
  group_by(accuracy_numeric, device) |>
  summarize(
    error_value = t_error(speed),
    mean_value = mean(speed),
    min_value = max(0, mean_value - error_value),
    max_value = mean_value + error_value,
    .groups = "drop"
  )

# Also calculating this here because we want the same max between natural entry speed and entry
# speed.
summary_nts <- measured_runs |>
  filter(!is.na(run_start_date)) |>
  mutate(accuracy_numeric = accuracy |> as.character() |> as.numeric()) |>
  group_by(accuracy_numeric, device) |>
  summarize(
    error_value = t_error(avg_wpm),
    mean_value = mean(avg_wpm),
    min_value = max(0, mean_value - error_value),
    max_value = mean_value + error_value,
    .groups = "drop"
  )

max_wpm = max(summary_ts$max_value, summary_nts$max_value)
summary_ts

6.1.1 Per devices

measured_trials |>
  group_by(device, participant) |>
  summarize(m_es = mean(cps) / 5 * 60, .groups = "drop_last")  |>
  summarize(mean_wpm = mean(m_es), .groups = "drop")

6.1.2 Per accuracy

measured_trials |>
  group_by(accuracy, participant) |>
  summarize(m_es = mean(cps) / 5 * 60, .groups = "drop_last")  |>
  summarize(mean_wpm = mean(m_es), .groups = "drop")

6.2 Graph

pd <- position_dodge(0.025)
entry_speed_plot <- ggplot(summary_ts, aes(
    x = accuracy_numeric,
    y = mean_value,
    ymin = min_value,
    ymax = max_value,
    color = device,
    group = device
  )) +
  SCALE_X_ACCURACY +
  expand_limits(y = c(0, max_wpm)) +
  scale_y_continuous(
    breaks=seq(0, max_wpm + 20, 20),
    labels=paste(seq(0, max_wpm + 20, 20), "wpm")
  ) +
  scale_color_manual("Device", values = DEVICE_COLORS) +
  custom_line(position=pd) +
  custom_pointrange(position=pd) +
  labs(y="Entry Speed") +
  guides(color = guide_legend(override.aes = list(linetype = 0))) +
  theme(legend.position = "none",
        axis.title.x = element_blank())

ggsave(
  graph_path("entry-speed-devices.pdf"),
  units = "mm",
  # Manually add 2 mm because axis labels are even longer.
  width = BASE_WIDTH + 3, 
  height = BASE_HEIGHT,
  device = cairo_pdf
)

entry_speed_plot

6.3 ART + ANOVA

m_ts = art(speed ~ device * accuracy + (1|participant),
            data = p_data_ts)
anova(m_ts)
emmeans(artlm(m_ts, "device"), pairwise ~ device)
NOTE: Results may be misleading due to involvement in interactions
$emmeans
 device  emmean   SE   df lower.CL upper.CL
 desktop   89.2 3.31 84.7     82.6     95.8
 tablet    40.8 3.31 84.7     34.2     47.4
 phone     33.5 3.31 84.7     27.0     40.1

Results are averaged over the levels of: accuracy 
Degrees-of-freedom method: kenward-roger 
Confidence level used: 0.95 

$contrasts
 contrast         estimate   SE df t.ratio p.value
 desktop - tablet    48.42 3.94 66  12.291  <.0001
 desktop - phone     55.67 3.94 66  14.131  <.0001
 tablet - phone       7.25 3.94 66   1.840  0.1645

Results are averaged over the levels of: accuracy 
Degrees-of-freedom method: kenward-roger 
P value adjustment: tukey method for comparing a family of 3 estimates 
emmeans(artlm(m_ts, "accuracy"), pairwise ~ accuracy)
NOTE: Results may be misleading due to involvement in interactions
$emmeans
 accuracy emmean   SE df lower.CL upper.CL
 0.1        37.8 5.95 33     25.7     49.9
 0.5        53.1 5.95 33     40.9     65.2
 0.9        72.6 5.95 33     60.5     84.7

Results are averaged over the levels of: device 
Degrees-of-freedom method: kenward-roger 
Confidence level used: 0.95 

$contrasts
 contrast                  estimate   SE df t.ratio p.value
 accuracy0.1 - accuracy0.5    -15.2 8.42 33  -1.812  0.1814
 accuracy0.1 - accuracy0.9    -34.8 8.42 33  -4.139  0.0006
 accuracy0.5 - accuracy0.9    -19.6 8.42 33  -2.327  0.0659

Results are averaged over the levels of: device 
Degrees-of-freedom method: kenward-roger 
P value adjustment: tukey method for comparing a family of 3 estimates 

7 Natural Entry Speed

This is the entry speed without suggestions.

ggplot(measured_runs, aes(x = avg_wpm, fill=device)) +
  geom_histogram(binwidth = 5) +
  facet_grid(rows = "device")

# summary_nts is calculated in the cell above.

pd <- position_dodge(0.025)
ggplot(summary_nts, aes(
    x = accuracy_numeric,
    y = mean_value,
    ymin = min_value,
    ymax = max_value,
    color = device,
    group = device
  )) +
  SCALE_X_ACCURACY +
  expand_limits(y = c(0, max_wpm)) +
    scale_y_continuous(
    breaks=seq(0, max_wpm + 20, 20),
    labels=paste(seq(0, max_wpm + 20, 20), "wpm")
  ) +
  custom_line(position=pd) +
  custom_pointrange(position=pd) +
  labs(y="Natural Entry Speed") +
  guides(color = guide_legend(override.aes = list(linetype = 0)))

Ideally, we would want all three lines to be constant.

summary_nts |>
  group_by(device) |>
  summarize(
    .groups = "drop",
    mean_speed = mean(mean_value),
    sd_speed = sd(mean_value),
    diff = max(mean_value) - min(min(mean_value))
  )
ezANOVA(
  measured_runs,
  avg_wpm,
  wid = c("participant"),
  within = c("device")
)
$ANOVA
  Effect DFn DFd        F            p p<.05       ges
2 device   2  70 157.1841 1.294993e-26     * 0.7217376

$`Mauchly's Test for Sphericity`
  Effect         W            p p<.05
2 device 0.2995354 1.257817e-09     *

$`Sphericity Corrections`
  Effect       GGe        p[GG] p[GG]<.05      HFe        p[HF] p[HF]<.05
2 device 0.5880746 1.217766e-16         * 0.596345 7.671883e-17         *
pairwise.t.test(measured_runs$avg_wpm, measured_runs$device, paired = T, p.adjust = "bonf")

    Pairwise comparisons using paired t tests 

data:  measured_runs$avg_wpm and measured_runs$device 

       desktop tablet 
tablet 2.2e-14 -      
phone  2.2e-14 0.00072

P value adjustment method: bonferroni 

8 Keystroke saving / natural entry speed

kses_data <-
  left_join(p_data_ks,
            measured_runs,
            by = c("participant", "device", "accuracy")) |>
  select(participant,
         device,
         accuracy,
         avg_wpm,
         p_mean_actual_ks) |>
  mutate(
    acc_label = paste0("accuracy ", accuracy),
    accuracy_numeric = as.numeric(accuracy),
    device = device |> recode_factor(laptop = "desktop")
  )

h_line_data <-
  tibble(acc = c(0.1, 0.5, 0.9), x = 110) |> mutate(acc_label = paste("accuracy", acc))

dp1 <- ggplot(kses_data, aes(x = avg_wpm, y = p_mean_actual_ks)) +
  geom_point(aes(color = device, shape = device)) +
  facet_grid(cols = vars(acc_label)) +
  geom_text(
    aes(y = acc, x = x),
    color = "#404040",
    label = "max",
    data = h_line_data,
    size = 2,
    hjust = 0,
    vjust = -0.75,
    family = "Linux Libertine"
  ) +
  geom_hline(aes(yintercept = acc), size = 0.25, data = h_line_data) +
  labs(
    x = "Natural Entry Speed (wpm)",
    y = "Keystroke Saving",
    color = "Device",
    shape = "Device"
  ) +
  scale_color_manual(values = DEVICE_COLORS) +
  expand_limits(x = 0) +
  scale_y_continuous(limits = c(0, 1), labels = percent)

ggsave(
  graph_path("keystroke-saving-vs-entry-speed.pdf"),
  plot = dp1,
  units = "mm",
  # Manually add 2 mm because axis labels are even longer.
  width = FULL_WIDTH,
  height = BASE_HEIGHT,
  device=cairo_pdf
)

dp1

m <-
  lmer(
    p_mean_actual_ks ~ avg_wpm + accuracy_numeric +
      (1 + avg_wpm | participant) + (1 + avg_wpm | device),
    data = kses_data
  )
boundary (singular) fit: see help('isSingular')
qqnorm(residuals(m))
qqline(residuals(m))

shapiro.test(residuals(m))

    Shapiro-Wilk normality test

data:  residuals(m)
W = 0.91902, p-value = 6.038e-06

Residuals from Linear Mixed Model are not normally distributed. Simple Pearson correlations will do.

for (x in c(0.1,0.5, 0.9)) {
  d <- kses_data |> filter(accuracy == x)
  message(paste0("------ A = ", x, " ------"))
  print(cor.test(d$avg_wpm, d$p_mean_actual_ks, method="kendall"))
}
------ A = 0.1 ------

    Kendall's rank correlation tau

data:  d$avg_wpm and d$p_mean_actual_ks
z = -2.6829, p-value = 0.007298
alternative hypothesis: true tau is not equal to 0
sample estimates:
       tau 
-0.3255828 
------ A = 0.5 ------

    Kendall's rank correlation tau

data:  d$avg_wpm and d$p_mean_actual_ks
z = -5.2076, p-value = 1.913e-07
alternative hypothesis: true tau is not equal to 0
sample estimates:
       tau 
-0.6170425 
------ A = 0.9 ------

    Kendall's rank correlation tau

data:  d$avg_wpm and d$p_mean_actual_ks
z = -4.8098, p-value = 1.511e-06
alternative hypothesis: true tau is not equal to 0
sample estimates:
       tau 
-0.5616563 

9 Keystroke saving ratio / natural entry speed

ksnes_data <-
  left_join(p_data_tsku,
            measured_runs,
            by = c("participant", "device", "accuracy")) |>
  select(participant, device, accuracy, avg_wpm, p_mean_ksr) |>
  mutate(
    acc_label = paste("accuracy", accuracy),
    device = device |> recode_factor(laptop = "desktop")
  )

ggplot(ksnes_data,
       aes(
         color = device,
         shape = device,
         x = avg_wpm,
         y = p_mean_ksr
       )) +
  geom_point() +
  facet_grid(cols = vars(acc_label)) +
  labs(
    x = "Natural Entry Speed (wpm)",
    y = "Keystroke Saving Ratio",
    color = "Device",
    shape = "Device"
  ) +
  expand_limits(x = 0) +
  scale_color_manual(values = DEVICE_COLORS) +
  scale_y_continuous(limits = c(0, 1), labels = percent)

ggsave(
  graph_path("keystroke-saving-ratio-vs-entry-speed.pdf"),
  units = "mm",
  # Manually add 2 mm because axis labels are even longer.
  width = FULL_WIDTH,
  height = BASE_HEIGHT,
  device=cairo_pdf
)
for (a in c(0.1, 0.5, 0.9)){
  d <- ksnes_data |> filter(accuracy == a)
  message(paste0("------ A = ", a, " ------"))
  print(cor.test(d$avg_wpm, d$p_mean_ksr, method = "kendall"))
}
------ A = 0.1 ------

    Kendall's rank correlation tau

data:  d$avg_wpm and d$p_mean_ksr
z = -2.6413, p-value = 0.00826
alternative hypothesis: true tau is not equal to 0
sample estimates:
       tau 
-0.3207862 
------ A = 0.5 ------

    Kendall's rank correlation tau

data:  d$avg_wpm and d$p_mean_ksr
z = -5.1802, p-value = 2.217e-07
alternative hypothesis: true tau is not equal to 0
sample estimates:
       tau 
-0.6137949 
------ A = 0.9 ------

    Kendall's rank correlation tau

data:  d$avg_wpm and d$p_mean_ksr
z = -4.7826, p-value = 1.731e-06
alternative hypothesis: true tau is not equal to 0
sample estimates:
       tau 
-0.5584741 

10 Natural entry speed vs entry speed

p_data_ts <- measured_trials |>
  group_by(participant, accuracy, device) |>
  summarize(speed = mean(cps) / 5 * 60, .groups = "drop") |>
  left_join(
    measured_runs |> select(participant, accuracy, device, avg_wpm),
    by = c("participant", "device", "accuracy")
  ) |>
  mutate(
    acc_label = paste0("accuracy ", accuracy),
    improvement = speed - avg_wpm,
    device =  recode_factor(device, laptop = "desktop")
  )

dp2 <- ggplot(p_data_ts,
              aes(
                color = device,
                shape = device,
                x = avg_wpm,
                y = improvement
              )) +
  facet_grid(cols = vars(acc_label)) +
  geom_point() +
  labs(
    y = "Entry Speed Improvement (wpm)",
    x = "Natural Entry Speed (wpm)",
    color = "Device",
    shape = "Device"
  ) +
  expand_limits(x = 0) +
  scale_color_manual(values = DEVICE_COLORS) +
  theme(legend.position = "bottom")
# scale_y_continuous(
#   breaks = seq(-90, 90, 30),
#   labels = str_pad(as.character(seq(-90, 90, 30)), 6, side = "left", pad = " ")
# ) +

ggsave(
  graph_path("entry-speed-improvement.pdf"),
  plot = dp2,
  width = FULL_WIDTH*0.6,
  height = BASE_HEIGHT*3*0.6,
  units = "mm",
  device = cairo_pdf
)

dp2

# Export dp1 and dp2 together
entry_speed_comparisons_combined_plot <- dp1 / dp2 + plot_layout(guides = 'collect')

ggsave(
  graph_path("entry-speed-comparisons-combined.pdf"),
  plot = entry_speed_comparisons_combined_plot,
  units = "mm",
  # Manually add 2 mm because axis labels are even longer.
  width = FULL_WIDTH, 
  height = (BASE_HEIGHT + 10) * 2,
  device = cairo_pdf
)
for (a in c(0.1, 0.5, 0.9)){
  d <- p_data_ts |> filter(accuracy == a)
  message(paste0("------ A = ", a, " ------"))
  print(cor.test(d$avg_wpm, d$improvement, method="kendall"))
}
------ A = 0.1 ------

    Kendall's rank correlation tau

data:  d$avg_wpm and d$improvement
T = 200, p-value = 0.001491
alternative hypothesis: true tau is not equal to 0
sample estimates:
       tau 
-0.3650794 
------ A = 0.5 ------

    Kendall's rank correlation tau

data:  d$avg_wpm and d$improvement
T = 259, p-value = 0.1312
alternative hypothesis: true tau is not equal to 0
sample estimates:
       tau 
-0.1777778 
------ A = 0.9 ------

    Kendall's rank correlation tau

data:  d$avg_wpm and d$improvement
T = 157, p-value = 7.3e-06
alternative hypothesis: true tau is not equal to 0
sample estimates:
       tau 
-0.5015873 

11 Suggestions delay

p_data_suggestion_delay <- measured_trials |>
  group_by(participant, accuracy, device, accuracy_numeric) |>
  summarize(
    suggestion_delay = mean(avg_suggestion_delay) * 1000,
    # For ezANOVA.
    suggestion_delay_num = as.numeric(suggestion_delay),
    .groups = "drop"
  )

summary_suggestion_delay <- p_data_suggestion_delay |>
  group_by(accuracy_numeric, device) |>
  summarize(
    error_value = t_error(suggestion_delay),
    mean_value = mean(suggestion_delay),
    min_value = max(150, mean_value - error_value),
    max_value = mean_value + error_value,
    .groups = "drop"
  )

pd <- position_dodge(0.025)
ggplot(
  summary_suggestion_delay,
  aes(
    x = accuracy_numeric,
    y = mean_value,
    ymin = min_value,
    ymax = max_value,
    color = device,
    group = device
  )
) +
  SCALE_X_ACCURACY +
  expand_limits(y = c(0)) +
  custom_line(position = pd) +
  custom_pointrange(position = pd) +
  labs(y = "Suggestion delay (ms)") +
  guides(color = guide_legend(override.aes = list(linetype = 0)))

m_delay = art(suggestion_delay_num ~ device * accuracy + (1|participant),
            data = p_data_suggestion_delay)
anova(m_ks, type = 3)

11.1 Pairwise comparisons on Device

emmeans(artlm(m_delay, "device"), pairwise ~ device)
NOTE: Results may be misleading due to involvement in interactions
$emmeans
 device  emmean   SE   df lower.CL upper.CL
 desktop   60.0 4.86 95.3     50.4     69.7
 tablet    46.7 4.86 95.3     37.0     56.3
 phone     56.8 4.86 95.3     47.2     66.5

Results are averaged over the levels of: accuracy 
Degrees-of-freedom method: kenward-roger 
Confidence level used: 0.95 

$contrasts
 contrast         estimate   SE df t.ratio p.value
 desktop - tablet    13.36 6.38 66   2.094  0.0988
 desktop - phone      3.22 6.38 66   0.505  0.8691
 tablet - phone     -10.14 6.38 66  -1.589  0.2574

Results are averaged over the levels of: accuracy 
Degrees-of-freedom method: kenward-roger 
P value adjustment: tukey method for comparing a family of 3 estimates 

11.2 Pairwise comparisons on Accuracy

emmeans(artlm(m_delay, "accuracy"), pairwise ~ accuracy)
NOTE: Results may be misleading due to involvement in interactions
$emmeans
 accuracy emmean   SE df lower.CL upper.CL
 0.1        50.8 5.35 33     39.9     61.7
 0.5        63.4 5.35 33     52.6     74.3
 0.9        49.2 5.35 33     38.4     60.1

Results are averaged over the levels of: device 
Degrees-of-freedom method: kenward-roger 
Confidence level used: 0.95 

$contrasts
 contrast                  estimate   SE df t.ratio p.value
 accuracy0.1 - accuracy0.5   -12.64 7.57 33  -1.670  0.2317
 accuracy0.1 - accuracy0.9     1.56 7.57 33   0.206  0.9770
 accuracy0.5 - accuracy0.9    14.19 7.57 33   1.876  0.1617

Results are averaged over the levels of: device 
Degrees-of-freedom method: kenward-roger 
P value adjustment: tukey method for comparing a family of 3 estimates 

11.3 Interaction Contrasts comparisons on Accuracy X Device

Pairwise comparisons are not reliable with ART. Instead, we perform interaction contrasts. See http://depts.washington.edu/acelab/proj/art/index.html.

contrast(emmeans(artlm(m_delay, "device:accuracy"), ~ device:accuracy), method="pairwise", interaction=TRUE)
 device_pairwise  accuracy_pairwise estimate   SE df t.ratio p.value
 desktop - tablet 0.1 - 0.5           -17.42 15.7 66  -1.106  0.2726
 desktop - phone  0.1 - 0.5             9.33 15.7 66   0.593  0.5553
 tablet - phone   0.1 - 0.5            26.75 15.7 66   1.699  0.0940
 desktop - tablet 0.1 - 0.9            16.00 15.7 66   1.016  0.3132
 desktop - phone  0.1 - 0.9             4.58 15.7 66   0.291  0.7719
 tablet - phone   0.1 - 0.9           -11.42 15.7 66  -0.725  0.4709
 desktop - tablet 0.5 - 0.9            33.42 15.7 66   2.123  0.0375
 desktop - phone  0.5 - 0.9            -4.75 15.7 66  -0.302  0.7638
 tablet - phone   0.5 - 0.9           -38.17 15.7 66  -2.424  0.0181

Degrees-of-freedom method: kenward-roger 

12 Letter frequencies

measured_trials |>
  select(participant, device, accuracy, trial_id, phrase) |>
  mutate(letter = strsplit("abcdefghijklmnopqrstuvwxyz", "")) |>
  unnest(letter) |>
  mutate(n_letter =  str_count(phrase, letter)) |>
  group_by(letter) |>
  summarize(n_letter = sum(n_letter), .groups="drop") |>
  mutate(f = n_letter / sum(n_letter)) |>
  arrange(desc(f))
measured_trials |>
  select(participant, device, accuracy, trial_id, phrase) |>
  mutate(letter = strsplit("abcdefghijklmnopqrstuvwxyz", "")) |>
  unnest(letter) |>
  mutate(n_letter =  str_count(phrase, letter)) |>
  group_by(letter, accuracy, device) |>
  summarize(n_letter = sum(n_letter), .groups="drop") |>
  group_by(accuracy, device) |>
  mutate(f = n_letter / sum(n_letter)) |>
  arrange(desc(f))

13 Learning

We investigate learning in term of speed.

13.1 Graph

p_data_ks <- measured_trials |>
  group_by(participant, accuracy, device, accuracy_numeric, trial_number) |>
  summarize(
    p_mean_actual_ks = mean(actual_key_saving_no_editing),
    p_mean_theoretical_ks = mean(theoretical_key_saving),
    .groups="drop"
  ) |>
  group_by(accuracy, device, trial_number) |>
  mutate(
    is_outlier = is_outlier(p_mean_actual_ks)
  ) |>
  ungroup()

actual_ks_summary <- p_data_ks |>
  group_by(accuracy_numeric, device, trial_number) |>
  summarize(
    error_ks = t_error(p_mean_actual_ks),
    mean_ks = mean(p_mean_actual_ks),
    min_ks = max(0, mean_ks - error_ks),
    max_ks = min(1, mean_ks + error_ks),
    ks_type = "actual",
    .groups="drop"
  )

theoretical_ks_summary <- p_data_ks |>
  group_by(accuracy_numeric, trial_number) |>
  summarize(
    device = NA,
    error_ks = t_error(p_mean_theoretical_ks),
    mean_ks = mean(p_mean_theoretical_ks),
    min_ks = max(0, mean_ks - error_ks),
    max_ks = min(1, mean_ks + error_ks),
    ks_type = "theoretical",
    .groups="drop"
  )
  
ks_summary <- union_all(theoretical_ks_summary, actual_ks_summary)

pd <- position_dodge(0.2)
ggplot(ks_summary, aes(
    x = trial_number,
    y = mean_ks,
    ymin = min_ks,
    ymax = max_ks,
    color = device,
    group = device
  )) +
  facet_wrap(vars(accuracy_numeric)) +
  custom_line(
    data=theoretical_ks_summary,
    color=THEORETICAL_COLOR,
    group="theoretical"
  ) +
  custom_pointrange(
    data=theoretical_ks_summary,
    color=THEORETICAL_COLOR,
    group="theoretical",
    shape=17
  ) +
  scale_color_manual("Device", values = DEVICE_COLORS) +
  custom_line(data=actual_ks_summary, position=pd) +
  custom_pointrange(data=actual_ks_summary, position=pd) +
  scale_y_continuous("Keystroke Saving", limits = c(0, 1), labels = percent) +
  theme(legend.position = "bottom")

ggsave(
  graph_path("learning-keystroke-saving-devices.pdf"),
  units = "mm",
  # Manually add 2 mm because axis labels are longer.
  width = 150, 
  height = 75,
  device = cairo_pdf
)
theme_margin <-
  theme(plot.margin = margin(r = 2, b = 2, unit = "mm"))

objective_combined <- (suggestion_usage_plot + theme_margin)  +
  entry_speed_plot +
  key_stroke_saving_plot + keystroke_saving_ratio_plot


ggsave(
  plot = objective_combined,
  graph_path("objectives-multi.pdf"),
  units = "mm",
  width = FULL_WIDTH,
  height = FULL_WIDTH / GOLDEN_RATIO,
  device = cairo_pdf
)

objective_combined

LS0tCnRpdGxlOiAiV29yZC1TdWdnZXN0aW9uczogRGV2aWNlIE9iamVjdGl2ZSBSZXN1bHRzIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICB0aGVtZTogbHVtZW4KICAgIGhpZ2hsaWdodDogZGVmYXVsdAogICAgY29kZV9mb2xkaW5nOiBoaWRlCiAgICBudW1iZXJfc2VjdGlvbnM6IFRSVUUKLS0tCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KHNjYWxlcykgIyBwZXJjZW50IGdncGxvdCBzY2FsZQpsaWJyYXJ5KHJzdGF0aXgpICAjIHBpcGUtcmVhZHkgUiBmdW5jdGlvbnMKbGlicmFyeShnZ3B1YnIpICAjIFNvbWUgZ2dwbG90IGJhc2VkIHBsb3RzCmxpYnJhcnkobG1lclRlc3QpCmxpYnJhcnkocGF0Y2h3b3JrKQpsaWJyYXJ5KE1BU1MpICMgZm9yIGJveHBsb3QKbGlicmFyeShBUlRvb2wpCmxpYnJhcnkoZW1tZWFucykKbGlicmFyeShleikKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoc3ZnbGl0ZSkKc291cmNlKCIuL2RldmljZS1zZXR1cC5SIikKCkhfR1JBUEhfTUFSR0lOID0gMTAKQkFTRV9XSURUSCA9IChGVUxMX1dJRFRIIC0gSF9HUkFQSF9NQVJHSU4pIC8gNApCQVNFX0hFSUdIVCA9IChGVUxMX1dJRFRIIC0gSF9HUkFQSF9NQVJHSU4pIC8gNAoKbm9ybWFsQ2hlY2sgPC0gZnVuY3Rpb24obW9kZWwpIHsKICAgIHJlcyA9IHJlc2lkdWFscyhtb2RlbCkKICAgIHFxbm9ybSgocmVzIC0gbWVhbihyZXMpKSAvIHNkKHJlcykpCiAgICBhYmxpbmUoMCwgMSkKICAgIHByaW50IChzaGFwaXJvLnRlc3QocmVzKSkKfQoKbGluZV9ncmFwaCA8LSBmdW5jdGlvbihjb21wbGV0ZWRfdHJpYWxzLCBtZWFzdXJlLCBtaW5fdmFsdWU9TkEsIG1heF92YWx1ZT1OQSkgewogIGdyYXBoX2RhdGEgPC0gY29tcGxldGVkX3RyaWFscyB8PgogICAgcmVuYW1lKHZhbHVlID0gbWVhc3VyZSkgfD4KICAgIGdyb3VwX2J5KHBhcnRpY2lwYW50LCBhY2N1cmFjeSwga2V5dGltZSwgc3VnZ2VzdGlvbnNfdHlwZSkgfD4KICAgIHN1bW1hcml6ZSgKICAgICAgcF92YWx1ZSA9IG1lYW4odmFsdWUpLAogICAgICAuZ3JvdXBzPSJkcm9wIgogICAgKSB8PgogICAgZ3JvdXBfYnkoYWNjdXJhY3ksIGtleXRpbWUsIHN1Z2dlc3Rpb25zX3R5cGUpIHw+CiAgICBzdW1tYXJpemUoCiAgICAgIGVycm9yX3ZhbHVlID0gdF9lcnJvcihwX3ZhbHVlKSwKICAgICAgbWVhbl92YWx1ZSA9IG1lYW4ocF92YWx1ZSksCiAgICAgIGNpX21pbiA9IGlmX2Vsc2UoaXMubmEobWluX3ZhbHVlKSwgbWVhbl92YWx1ZSAtIGVycm9yX3ZhbHVlLCBtYXgobWluX3ZhbHVlLCBtZWFuX3ZhbHVlIC0gZXJyb3JfdmFsdWUpKSwKICAgICAgY2lfbWF4ID0gaWZfZWxzZShpcy5uYShtYXhfdmFsdWUpLCBtZWFuX3ZhbHVlICsgZXJyb3JfdmFsdWUsIG1pbihtYXhfdmFsdWUsIG1lYW5fdmFsdWUgKyBlcnJvcl92YWx1ZSkpLAogICAgICAuZ3JvdXBzPSJkcm9wIgogICAgKQogIGdncGxvdChncmFwaF9kYXRhLCBhZXMoCiAgICB4ID0gYWNjdXJhY3kgfD4gZmFjdG9yX3RvX251bWVyaWMoKSwKICAgIHkgPSBtZWFuX3ZhbHVlLAogICAgeW1pbiA9IGNpX21pbiwKICAgIHltYXggPSBjaV9tYXgsCiAgICBjb2xvcj1rZXl0aW1lIHw+IGZhY3Rvcl90b19udW1lcmljKCksCiAgICBncm91cD1rZXl0aW1lIHw+IGZhY3Rvcl90b19udW1lcmljKCkKICApKSArCiAgICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzPUFDQ1VSQUNZX0xFVkVMUykgKwogICAgc2NhbGVfY29sb3JfY29udGludW91cyhicmVha3M9a2V5dGltZV9MRVZFTFMpICsKICAgIGdlb21fbGluZSgpICsKICAgIGdlb21fcG9pbnRyYW5nZSgpCn0KCm1lYXN1cmVkX3J1bnMgPC0gcmVhZF9kZXZpY2VfcnVucyhtZWFzdXJlZF9vbmx5PVRSVUUpCm1lYXN1cmVkX3RyaWFscyA8LSByZWFkX2RldmljZV90cmlhbHMobWVhc3VyZWRfb25seT1UUlVFKQpgYGAKCiMgU3VnZ2VzdGlvbiBVc2FnZQoKIyMgU3VtbWFyaWVzCgpgYGB7ciB3YXJuaW5nPUZBTFNFLCBkZXY9InN2Z2xpdGUifQpwX2RhdGFfdXNhZ2UgPC0gbWVhc3VyZWRfdHJpYWxzIHw+CiAgZ3JvdXBfYnkocGFydGljaXBhbnQsIGRldmljZSwgYWNjdXJhY3ksIGFjY3VyYWN5X251bWVyaWMpIHw+CiAgc3VtbWFyaXplKG1lYW5fc3VnZ2VzdGlvbl91c2UgPSBtZWFuKHRvdGFsX3N1Z2dlc3Rpb25fdXNlZCksCiAgICAgICAgICAgIC5ncm91cHMgPSAiZHJvcCIpCgpzdW1tYXJ5X3VzYWdlIDwtIHBfZGF0YV91c2FnZSB8PgogIGdyb3VwX2J5KGFjY3VyYWN5X251bWVyaWMsIGRldmljZSkgfD4KICBzdW1tYXJpemUoCiAgICBlcnJvcl9zdWdnZXN0aW9uX3VzZWQgPSB0X2Vycm9yKG1lYW5fc3VnZ2VzdGlvbl91c2UpLAogICAgbWVhbl9zdWdnZXN0aW9uX3VzZWQgPSBtZWFuKG1lYW5fc3VnZ2VzdGlvbl91c2UpLAogICAgbWluX3N1Z2dlc3Rpb25fdXNlZCA9IG1heCgwLCBtZWFuX3N1Z2dlc3Rpb25fdXNlZCAtIGVycm9yX3N1Z2dlc3Rpb25fdXNlZCksCiAgICBtYXhfc3VnZ2VzdGlvbl91c2VkID0gbWVhbl9zdWdnZXN0aW9uX3VzZWQgKyBlcnJvcl9zdWdnZXN0aW9uX3VzZWQsCiAgICAuZ3JvdXBzID0gImRyb3AiCiAgKQoKc3VtbWFyeV91c2FnZQpgYGAKCiMjIyBQZXIgZGV2aWNlcwoKYGBge3IsIHdhcm5pbmc9RkFMU0UsIGRldj0ic3ZnbGl0ZSJ9Cm1lYXN1cmVkX3RyaWFscyB8PgogIGdyb3VwX2J5KGRldmljZSwgcGFydGljaXBhbnQpIHw+CiAgc3VtbWFyaXplKG1lYW5fc3VnZ2VzdGlvbl91c2UgPSBtZWFuKHRvdGFsX3N1Z2dlc3Rpb25fdXNlZCksIC5ncm91cHM9ImRyb3BfbGFzdCIpICB8PgogIHN1bW1hcml6ZShtZWFuX3N1Z2dlc3Rpb25fdXNlZCA9IG1lYW4obWVhbl9zdWdnZXN0aW9uX3VzZSksIC5ncm91cHMgPSAiZHJvcCIpCmBgYAoKIyMjIFBlciBhY2N1cmFjeQoKYGBge3IsIHdhcm5pbmc9RkFMU0UsIGRldj0ic3ZnbGl0ZSJ9Cm1lYXN1cmVkX3RyaWFscyB8PgogIGdyb3VwX2J5KGFjY3VyYWN5LCBwYXJ0aWNpcGFudCkgfD4KICBzdW1tYXJpemUobWVhbl9zdWdnZXN0aW9uX3VzZSA9IG1lYW4odG90YWxfc3VnZ2VzdGlvbl91c2VkKSwgLmdyb3Vwcz0iZHJvcF9sYXN0IikgIHw+CiAgc3VtbWFyaXplKG1lYW5fc3VnZ2VzdGlvbl91c2VkID0gbWVhbihtZWFuX3N1Z2dlc3Rpb25fdXNlKSwgLmdyb3VwcyA9ICJkcm9wIikKYGBgCgojIyBHcmFwaAoKYGBge3IsIHdhcm5pbmc9RkFMU0UsIGRldj0ic3ZnbGl0ZSJ9CnBkIDwtIHBvc2l0aW9uX2RvZGdlKDAuMDI1KQoKc3VnZ2VzdGlvbl91c2FnZV9wbG90IDwtIGdncGxvdCgKICBzdW1tYXJ5X3VzYWdlLAogIGFlcygKICAgIHggPSBhY2N1cmFjeV9udW1lcmljLAogICAgeSA9IG1lYW5fc3VnZ2VzdGlvbl91c2VkLAogICAgeW1pbiA9IG1pbl9zdWdnZXN0aW9uX3VzZWQsCiAgICB5bWF4ID0gbWF4X3N1Z2dlc3Rpb25fdXNlZCwKICAgIGNvbG9yID0gZGV2aWNlLAogICAgZ3JvdXAgPSBkZXZpY2UKICApCikgKwogIGV4cGFuZF9saW1pdHMoeSA9IGMoMCkpICsKICBjdXN0b21fbGluZShwb3NpdGlvbiA9IHBkKSArCiAgU0NBTEVfWF9BQ0NVUkFDWSArCiAgc2NhbGVfY29sb3JfbWFudWFsKCJEZXZpY2UiLCB2YWx1ZXMgPSBERVZJQ0VfQ09MT1JTKSArCiAgc2NhbGVfeV9jb250aW51b3VzKCJTdWdnZXN0aW9uIFVzYWdlIHBlciBUcmlhbCIsIGJyZWFrcyA9IHNlcSgwLCA2LCAxKSkgKwogIGN1c3RvbV9wb2ludHJhbmdlKHBvc2l0aW9uID0gcGQpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpKQoKZ2dzYXZlKAogIGdyYXBoX3BhdGgoInN1Z2dlc3Rpb25zLXVzYWdlLWRldmljZXMucGRmIiksCiAgdW5pdHMgPSAibW0iLAogIHdpZHRoID0gQkFTRV9XSURUSCwKICBoZWlnaHQgPSBCQVNFX0hFSUdIVCwKICBkZXZpY2UgPSBjYWlyb19wZGYKKQoKc3VnZ2VzdGlvbl91c2FnZV9wbG90CmBgYAoKIyMgTm9ybWFsaXR5CgpgYGB7ciwgd2FybmluZz1GQUxTRSwgZGV2PSJzdmdsaXRlIn0KcF9kYXRhX3VzYWdlIHw+CiAgZ3JvdXBfYnkoYWNjdXJhY3ksIGRldmljZSkgfD4KICBzaGFwaXJvX3Rlc3QobWVhbl9zdWdnZXN0aW9uX3VzZSkKYGBgCgpgYGB7ciwgd2FybmluZz1GQUxTRSwgZGV2PSJzdmdsaXRlIn0KZ2dxcXBsb3QocF9kYXRhX3VzYWdlLCAibWVhbl9zdWdnZXN0aW9uX3VzZSIsIGdndGhlbWUgPSB0aGVtZV9idygpKSArCiAgZmFjZXRfZ3JpZChhY2N1cmFjeSB+IGRldmljZSkKYGBgCgpOb3Qgbm9ybWFsLgoKYGBge3IsIHdhcm5pbmc9RkFMU0UsIGRldj0ic3ZnbGl0ZSJ9CiMgQ29tZXMgZnJvbSBodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL2EvMzQwMDIwMjAuCgojIFdlIG5lZWQgdG8gc2hpZnQgaXQgYmVjYXVzZSB3ZSBoYXZlIG5vbiBwb3NpdGl2ZSB2YWx1ZXMuLi4KYmMgPC0KICBib3hjb3goKG1lYW5fc3VnZ2VzdGlvbl91c2UgKyAxZS0xMCkgfiBkZXZpY2UgKiBhY2N1cmFjeSwgZGF0YSA9CiAgICAgICAgICAgcF9kYXRhX3VzYWdlKQoKbGFtYmRhIDwtIGJjJHhbd2hpY2gubWF4KGJjJHkpXQoKcG93ZXJUcmFuc2Zvcm0gPC0KICBmdW5jdGlvbih5LAogICAgICAgICAgIGxhbWJkYTEsCiAgICAgICAgICAgbGFtYmRhMiA9IE5VTEwsCiAgICAgICAgICAgbWV0aG9kID0gImJveGNveCIpIHsKICAgIGJveGNveFRyYW5zIDwtIGZ1bmN0aW9uKHgsIGxhbTEsIGxhbTIgPSBOVUxMKSB7CiAgICAgICMgaWYgd2Ugc2V0IGxhbWJkYTIgdG8gemVybywgaXQgYmVjb21lcyB0aGUgb25lIHBhcmFtZXRlciB0cmFuc2Zvcm1hdGlvbgogICAgICBsYW0yIDwtIGlmZWxzZShpcy5udWxsKGxhbTIpLCAwLCBsYW0yKQogICAgICBpZiAobGFtMSA9PSAwTCkgewogICAgICAgIGxvZyh5ICsgbGFtMikKICAgICAgfSBlbHNlIHsKICAgICAgICAoKCh5ICsgbGFtMikgXiBsYW0xKSAtIDEpIC8gbGFtMQogICAgICB9CiAgICB9CiAgICBzd2l0Y2gobWV0aG9kLAogICAgICAgICAgIGJveGNveCA9IGJveGNveFRyYW5zKHksIGxhbWJkYTEsIGxhbWJkYTIpLAogICAgICAgICAgIHR1a2V5ID0geSBeIGxhbWJkYTEpCiAgfQoKcF9kYXRhX3VzYWdlIDwtIHBfZGF0YV91c2FnZSB8PgogIG11dGF0ZSh0cmFuc2Zvcm1lZF9zdWdnZXN0aW9uX3VzZSA9IHBvd2VyVHJhbnNmb3JtKG1lYW5fc3VnZ2VzdGlvbl91c2UsIGxhbWJkYSkpCgpnZ3FxcGxvdChwX2RhdGFfdXNhZ2UsICJ0cmFuc2Zvcm1lZF9zdWdnZXN0aW9uX3VzZSIsIGdndGhlbWUgPSB0aGVtZV9idygpKSArCiAgZmFjZXRfZ3JpZChhY2N1cmFjeSB+IGRldmljZSkKYGBgCgpCb3hjb3ggd29uJ3Qgd29yay4gQVJUIGl0IGlzLgoKIyMgQVJUICsgQU5PVkEKCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBkZXY9InN2Z2xpdGUifQptX3VzYWdlID0gYXJ0KG1lYW5fc3VnZ2VzdGlvbl91c2UgfiBkZXZpY2UgKiBhY2N1cmFjeSArICgxfHBhcnRpY2lwYW50KSwKICAgICAgICAgICAgICAgICAgZGF0YSA9IHBfZGF0YV91c2FnZSkKYW5vdmEobV91c2FnZSkKYGBgCgojIyBQYWlyd2lzZSBjb21wYXJpc29ucyBvbiBEZXZpY2UKCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBkZXY9InN2Z2xpdGUifQplbW1lYW5zKGFydGxtKG1fdXNhZ2UsICJkZXZpY2UiKSwgcGFpcndpc2UgfiBkZXZpY2UpCmBgYAoKIyMgUGFpcndpc2UgY29tcGFyaXNvbnMgb24gQWNjdXJhY3kKCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBkZXY9InN2Z2xpdGUifQplbW1lYW5zKGFydGxtKG1fdXNhZ2UsICJhY2N1cmFjeSIpLCBwYWlyd2lzZSB+IGFjY3VyYWN5KQpgYGAKCiMjIEludGVyYWN0aW9uIENvbnRyYXN0cyBvbiBBY2N1cmFjeSBYIERldmljZQoKUGFpcndpc2UgY29tcGFyaXNvbnMgYXJlIG5vdCByZWxpYWJsZSB3aXRoIEFSVC4gSW5zdGVhZCwgd2UgcGVyZm9ybSBpbnRlcmFjdGlvbiBjb250cmFzdHMuIFNlZSA8aHR0cDovL2RlcHRzLndhc2hpbmd0b24uZWR1L2FjZWxhYi9wcm9qL2FydC9pbmRleC5odG1sPi4KCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBkZXY9InN2Z2xpdGUifQpjb250cmFzdCgKICBlbW1lYW5zKAogICAgYXJ0bG0obV91c2FnZSwgImRldmljZTphY2N1cmFjeSIpLAogICAgfiBkZXZpY2U6YWNjdXJhY3kKICApLAogIG1ldGhvZCA9ICJwYWlyd2lzZSIsCiAgaW50ZXJhY3Rpb24gPSBUUlVFCikKYGBgCgojIFNhdmVkIEtleSBTdHJva2VzCgpgYGB7ciwgd2FybmluZz1GQUxTRSwgZGV2PSJzdmdsaXRlIn0KcF9kYXRhX3NrcyA8LSBtZWFzdXJlZF90cmlhbHMgfD4KICBncm91cF9ieShwYXJ0aWNpcGFudCwgYWNjdXJhY3ksIGFjY3VyYWN5X251bWVyaWMsIGRldmljZSkgfD4KICBzdW1tYXJpemUoCiAgICBwX21lYW5fc2tzID0gbWVhbihhY3R1YWxfc2tzKSwKICAgIC5ncm91cHM9ImRyb3AiCiAgKSAKCnN1bW1hcnlfc2tzIDwtIHBfZGF0YV9za3MgfD4KICBncm91cF9ieShhY2N1cmFjeSwgYWNjdXJhY3lfbnVtZXJpYywgZGV2aWNlKSB8PgogIHN1bW1hcml6ZSgKICAgIGVycm9yX3NrcyA9IHRfZXJyb3IocF9tZWFuX3NrcyksCiAgICBtZWFuX3NrcyA9IG1lYW4ocF9tZWFuX3NrcyksCiAgICBtaW5fc2tzID0gbWVhbl9za3MgLSBlcnJvcl9za3MsCiAgICBtYXhfc2tzID0gbWVhbl9za3MgKyBlcnJvcl9za3MsCiAgICAuZ3JvdXBzPSJkcm9wIgogICkKdGhlb3JldGljYWxfc2tzIDwtIG1lYXN1cmVkX3RyaWFscyB8PgogIGdyb3VwX2J5KHBhcnRpY2lwYW50LCBhY2N1cmFjeV9udW1lcmljLCBkZXZpY2UpIHw+CiAgc3VtbWFyaXplKHBfbWVhbl9za3MgPSBtZWFuKHRoZW9yZXRpY2FsX3NrcyksIC5ncm91cHM9ImRyb3AiKSB8PgogIGdyb3VwX2J5KGFjY3VyYWN5X251bWVyaWMsIGRldmljZSkgfD4KICBzdW1tYXJpemUocF9tZWFuX3NrcyA9IG1lYW4ocF9tZWFuX3NrcyksIC5ncm91cHM9ImRyb3AiKSB8PgogIGdyb3VwX2J5KGFjY3VyYWN5X251bWVyaWMpIHw+CiAgc3VtbWFyaXplKAogICAgZXJyb3Jfc2tzID0gdF9lcnJvcihwX21lYW5fc2tzKSwKICAgIG1lYW5fc2tzID0gbWVhbihwX21lYW5fc2tzKSwKICAgIG1pbl9za3MgPSBtZWFuX3NrcyAtIGVycm9yX3NrcywKICAgIG1heF9za3MgPSBtZWFuX3NrcyArIGVycm9yX3NrcywKICAgIC5ncm91cHM9ImRyb3AiCiAgKQpwZCA8LSBwb3NpdGlvbl9kb2RnZSgwLjAyNSkKZ2dwbG90KHN1bW1hcnlfc2tzLCBhZXMoCiAgICB4ID0gYWNjdXJhY3lfbnVtZXJpYywKICAgIHkgPSBtZWFuX3NrcywKICAgIHltaW4gPSBtaW5fc2tzLAogICAgeW1heCA9IG1heF9za3MsCiAgICBjb2xvciA9IGRldmljZSwKICAgIGdyb3VwID0gZGV2aWNlCiAgKSkgKwogIGdlb21fbGluZShkYXRhPXRoZW9yZXRpY2FsX3NrcywgY29sb3IgPSAicmVkIiwgZ3JvdXAgPSAicmVkIikgKwogIGdlb21fcG9pbnRyYW5nZShkYXRhPXRoZW9yZXRpY2FsX3NrcywgY29sb3I9InJlZCIsIGdyb3VwPSJyZWQiKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcz1jKDAsIDAuMSwgMC4zLCAwLjUsIDAuNywgMC45LCAxKSkgKwogIGdlb21fbGluZShwb3NpdGlvbj1wZCkgKwogIGdlb21fcG9pbnRyYW5nZShwb3NpdGlvbj1wZCkgKwogIGxhYnMoeD0iQWNjdXJhY3kiLCB5PSJTYXZlZCBLZXlzdHJva2VzIiwgY29sb3I9IkRldmljZSIpCmBgYAoKIyBLZXlzdHJva2UgU2F2aW5nCgojIyBTdW1tYXJpZXMKCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBkZXY9InN2Z2xpdGUifQpwX2RhdGFfa3MgPC0gbWVhc3VyZWRfdHJpYWxzIHw+CiAgZ3JvdXBfYnkocGFydGljaXBhbnQsIGFjY3VyYWN5LCBkZXZpY2UsIGFjY3VyYWN5X251bWVyaWMpIHw+CiAgc3VtbWFyaXplKAogICAgcF9tZWFuX2FjdHVhbF9rcyA9IG1lYW4oYWN0dWFsX2tleV9zYXZpbmdfbm9fZWRpdGluZyksCiAgICBwX21lYW5fdGhlb3JldGljYWxfa3MgPSBtZWFuKHRoZW9yZXRpY2FsX2tleV9zYXZpbmcpLAogICAgLmdyb3Vwcz0iZHJvcCIKICApIHw+CiAgZ3JvdXBfYnkoYWNjdXJhY3ksIGRldmljZSkgfD4KICBtdXRhdGUoCiAgICBpc19vdXRsaWVyID0gaXNfb3V0bGllcihwX21lYW5fYWN0dWFsX2tzKQogICkgfD4KICB1bmdyb3VwKCkKCmFjdHVhbF9rc19zdW1tYXJ5IDwtIHBfZGF0YV9rcyB8PgogIGdyb3VwX2J5KGFjY3VyYWN5X251bWVyaWMsIGRldmljZSkgfD4KICBzdW1tYXJpemUoCiAgICBrc190eXBlID0gImFjdHVhbCIsCiAgICBlcnJvcl9rcyA9IHRfZXJyb3IocF9tZWFuX2FjdHVhbF9rcyksCiAgICBtZWFuX2tzID0gbWVhbihwX21lYW5fYWN0dWFsX2tzKSwKICAgIG1pbl9rcyA9IG1heCgwLCBtZWFuX2tzIC0gZXJyb3Jfa3MpLAogICAgbWF4X2tzID0gbWluKDEsIG1lYW5fa3MgKyBlcnJvcl9rcyksCiAgICAuZ3JvdXBzPSJkcm9wIgogICkKCnRoZW9yZXRpY2FsX2tzX3N1bW1hcnkgPC0gcF9kYXRhX2tzIHw+CiAgZ3JvdXBfYnkoYWNjdXJhY3lfbnVtZXJpYykgfD4KICBzdW1tYXJpemUoCiAgICBrc190eXBlID0gInRoZW9yZXRpY2FsIiwKICAgIGRldmljZSA9IE5BLAogICAgZXJyb3Jfa3MgPSB0X2Vycm9yKHBfbWVhbl90aGVvcmV0aWNhbF9rcyksCiAgICBtZWFuX2tzID0gbWVhbihwX21lYW5fdGhlb3JldGljYWxfa3MpLAogICAgbWluX2tzID0gbWF4KDAsIG1lYW5fa3MgLSBlcnJvcl9rcyksCiAgICBtYXhfa3MgPSBtaW4oMSwgbWVhbl9rcyArIGVycm9yX2tzKSwKICAgIC5ncm91cHM9ImRyb3AiCiAgKQogIAprc19zdW1tYXJ5IDwtIHVuaW9uX2FsbCh0aGVvcmV0aWNhbF9rc19zdW1tYXJ5LCBhY3R1YWxfa3Nfc3VtbWFyeSkKCmtzX3N1bW1hcnkKYGBgCgojIyMgUGVyIGRldmljZXMKCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBkZXY9InN2Z2xpdGUifQptZWFzdXJlZF90cmlhbHMgfD4KICBncm91cF9ieShkZXZpY2UsIHBhcnRpY2lwYW50KSB8PgogIHN1bW1hcml6ZShtX2tzID0gbWVhbihhY3R1YWxfa2V5X3NhdmluZ19ub19lZGl0aW5nKSwgLmdyb3Vwcz0iZHJvcF9sYXN0IikgIHw+CiAgc3VtbWFyaXplKG1lYW5fa3MgPSBtZWFuKG1fa3MpLCAuZ3JvdXBzID0gImRyb3AiKQpgYGAKCiMjIyBQZXIgYWNjdXJhY3kKCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBkZXY9InN2Z2xpdGUifQptZWFzdXJlZF90cmlhbHMgfD4KICBncm91cF9ieShhY2N1cmFjeSwgcGFydGljaXBhbnQpICB8PgogIHN1bW1hcml6ZShtX2tzID0gbWVhbihhY3R1YWxfa2V5X3NhdmluZ19ub19lZGl0aW5nKSwgLmdyb3Vwcz0iZHJvcF9sYXN0IikgIHw+CiAgc3VtbWFyaXplKG1lYW5fa3MgPSBtZWFuKG1fa3MpLCAuZ3JvdXBzID0gImRyb3AiKQpgYGAKCiMjIEdyYXBoCgpgYGB7ciwgd2FybmluZz1GQUxTRSwgZGV2PSJzdmdsaXRlIn0KcF9kYXRhX2tzIDwtIG1lYXN1cmVkX3RyaWFscyB8PgogIGdyb3VwX2J5KHBhcnRpY2lwYW50LCBhY2N1cmFjeSwgZGV2aWNlLCBhY2N1cmFjeV9udW1lcmljKSB8PgogIHN1bW1hcml6ZSgKICAgIHBfbWVhbl9hY3R1YWxfa3MgPSBtZWFuKGFjdHVhbF9rZXlfc2F2aW5nX25vX2VkaXRpbmcpLAogICAgcF9tZWFuX3RoZW9yZXRpY2FsX2tzID0gbWVhbih0aGVvcmV0aWNhbF9rZXlfc2F2aW5nKSwKICAgIC5ncm91cHM9ImRyb3AiCiAgKSB8PgogIGdyb3VwX2J5KGFjY3VyYWN5LCBkZXZpY2UpIHw+CiAgbXV0YXRlKAogICAgaXNfb3V0bGllciA9IGlzX291dGxpZXIocF9tZWFuX2FjdHVhbF9rcykKICApIHw+CiAgdW5ncm91cCgpCgphY3R1YWxfa3Nfc3VtbWFyeSA8LSBwX2RhdGFfa3MgfD4KICBncm91cF9ieShhY2N1cmFjeV9udW1lcmljLCBkZXZpY2UpIHw+CiAgc3VtbWFyaXplKAogICAgZXJyb3Jfa3MgPSB0X2Vycm9yKHBfbWVhbl9hY3R1YWxfa3MpLAogICAgbWVhbl9rcyA9IG1lYW4ocF9tZWFuX2FjdHVhbF9rcyksCiAgICBtaW5fa3MgPSBtYXgoMCwgbWVhbl9rcyAtIGVycm9yX2tzKSwKICAgIG1heF9rcyA9IG1pbigxLCBtZWFuX2tzICsgZXJyb3Jfa3MpLAogICAga3NfdHlwZSA9ICJhY3R1YWwiLAogICAgLmdyb3Vwcz0iZHJvcCIKICApCgp0aGVvcmV0aWNhbF9rc19zdW1tYXJ5IDwtIHBfZGF0YV9rcyB8PgogIGdyb3VwX2J5KGFjY3VyYWN5X251bWVyaWMpIHw+CiAgc3VtbWFyaXplKAogICAgZGV2aWNlPU5BLAogICAgZXJyb3Jfa3MgPSB0X2Vycm9yKHBfbWVhbl90aGVvcmV0aWNhbF9rcyksCiAgICBtZWFuX2tzID0gbWVhbihwX21lYW5fdGhlb3JldGljYWxfa3MpLAogICAgbWluX2tzID0gbWF4KDAsIG1lYW5fa3MgLSBlcnJvcl9rcyksCiAgICBtYXhfa3MgPSBtaW4oMSwgbWVhbl9rcyArIGVycm9yX2tzKSwKICAgIGtzX3R5cGUgPSAidGhlb3JldGljYWwiLAogICAgLmdyb3Vwcz0iZHJvcCIKICApCiAgCmtzX3N1bW1hcnkgPC0gdW5pb25fYWxsKHRoZW9yZXRpY2FsX2tzX3N1bW1hcnksIGFjdHVhbF9rc19zdW1tYXJ5KQoKcGQgPC0gcG9zaXRpb25fZG9kZ2UoMC4wMjUpCmtleV9zdHJva2Vfc2F2aW5nX3Bsb3QgPC0gZ2dwbG90KGtzX3N1bW1hcnksIGFlcygKICAgIHggPSBhY2N1cmFjeV9udW1lcmljLAogICAgeSA9IG1lYW5fa3MsCiAgICB5bWluID0gbWluX2tzLAogICAgeW1heCA9IG1heF9rcywKICAgIGNvbG9yID0gZGV2aWNlLAogICAgZ3JvdXAgPSBkZXZpY2UKICApKSArCiAgY3VzdG9tX2xpbmUoCiAgICBkYXRhPXRoZW9yZXRpY2FsX2tzX3N1bW1hcnksCiAgICBjb2xvcj1USEVPUkVUSUNBTF9DT0xPUiwKICAgIGdyb3VwPSJ0aGVvcmV0aWNhbCIKICApICsKICBjdXN0b21fcG9pbnRyYW5nZSgKICAgIGRhdGE9dGhlb3JldGljYWxfa3Nfc3VtbWFyeSwKICAgIGNvbG9yPVRIRU9SRVRJQ0FMX0NPTE9SLAogICAgZ3JvdXA9InRoZW9yZXRpY2FsIiwKICAgIHNoYXBlPTE3CiAgKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKCJEZXZpY2UiLCB2YWx1ZXMgPSBERVZJQ0VfQ09MT1JTKSArCiAgY3VzdG9tX2xpbmUoZGF0YT1hY3R1YWxfa3Nfc3VtbWFyeSwgcG9zaXRpb249cGQpICsKICBjdXN0b21fcG9pbnRyYW5nZShkYXRhPWFjdHVhbF9rc19zdW1tYXJ5LCBwb3NpdGlvbj1wZCkgKwogIFNDQUxFX1hfQUNDVVJBQ1kgKwogIHNjYWxlX3lfY29udGludW91cygiS2V5c3Ryb2tlIFNhdmluZyIsIGxpbWl0cyA9IGMoMCwgMSksIGxhYmVscyA9IHBlcmNlbnQpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpKQoKZ2dzYXZlKAogIGdyYXBoX3BhdGgoImtleXN0cm9rZS1zYXZpbmctZGV2aWNlcy5wZGYiKSwKICB1bml0cyA9ICJtbSIsCiAgIyBNYW51YWxseSBhZGQgMiBtbSBiZWNhdXNlIGF4aXMgbGFiZWxzIGFyZSBsb25nZXIuCiAgd2lkdGggPSBCQVNFX1dJRFRIICsgMiwgCiAgaGVpZ2h0ID0gQkFTRV9IRUlHSFQsCiAgZGV2aWNlID0gY2Fpcm9fcGRmCikKCmtleV9zdHJva2Vfc2F2aW5nX3Bsb3QKYGBgCgojIyBOb3JtYWxpdHkKCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBkZXY9InN2Z2xpdGUifQpwX2RhdGFfa3MgfD4KICBncm91cF9ieShhY2N1cmFjeSwgZGV2aWNlKSB8PgogIHNoYXBpcm9fdGVzdChwX21lYW5fYWN0dWFsX2tzKQoKZ2dxcXBsb3QocF9kYXRhX2tzLCAicF9tZWFuX2FjdHVhbF9rcyIsIGdndGhlbWUgPSB0aGVtZV9idygpKSArCiAgZmFjZXRfZ3JpZChhY2N1cmFjeSB+IGRldmljZSkKYGBgCgpOb3Qgbm9ybWFsLgoKYGBge3IsIHdhcm5pbmc9RkFMU0UsIGRldj0ic3ZnbGl0ZSJ9CiMgQ29tZXMgZnJvbSBodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL2EvMzQwMDIwMjAuCgojIFdlIG5lZWQgdG8gc2hpZnQgaXQgYmVjYXVzZSB3ZSBoYXZlIG5vbiBwb3NpdGl2ZSB2YWx1ZXMuLi4KYmMgPC0gYm94Y294KChwX21lYW5fYWN0dWFsX2tzKzFlLTEwKX5kZXZpY2UqYWNjdXJhY3ksIGRhdGE9cF9kYXRhX2tzKQoKbGFtYmRhIDwtIGJjJHhbd2hpY2gubWF4KGJjJHkpXQoKcG93ZXJUcmFuc2Zvcm0gPC0KICBmdW5jdGlvbih5LAogICAgICAgICAgIGxhbWJkYTEsCiAgICAgICAgICAgbGFtYmRhMiA9IE5VTEwsCiAgICAgICAgICAgbWV0aG9kID0gImJveGNveCIpIHsKICAgIGJveGNveFRyYW5zIDwtIGZ1bmN0aW9uKHgsIGxhbTEsIGxhbTIgPSBOVUxMKSB7CiAgICAgICMgaWYgd2Ugc2V0IGxhbWJkYTIgdG8gemVybywgaXQgYmVjb21lcyB0aGUgb25lIHBhcmFtZXRlciB0cmFuc2Zvcm1hdGlvbgogICAgICBsYW0yIDwtIGlmZWxzZShpcy5udWxsKGxhbTIpLCAwLCBsYW0yKQogICAgICBpZiAobGFtMSA9PSAwTCkgewogICAgICAgIGxvZyh5ICsgbGFtMikKICAgICAgfSBlbHNlIHsKICAgICAgICAoKCh5ICsgbGFtMikgXiBsYW0xKSAtIDEpIC8gbGFtMQogICAgICB9CiAgICB9CiAgICBzd2l0Y2gobWV0aG9kLAogICAgICAgICAgIGJveGNveCA9IGJveGNveFRyYW5zKHksIGxhbWJkYTEsIGxhbWJkYTIpLAogICAgICAgICAgIHR1a2V5ID0geSBeIGxhbWJkYTEpCiAgfQoKcF9kYXRhX2tzIDwtIHBfZGF0YV9rcyB8PiBtdXRhdGUodHJhbnNmb3JtZWRfa3MgPSBwb3dlclRyYW5zZm9ybShwX21lYW5fYWN0dWFsX2tzLCBsYW1iZGEpKQoKZ2dxcXBsb3QocF9kYXRhX2tzLCAidHJhbnNmb3JtZWRfa3MiLCBnZ3RoZW1lID0gdGhlbWVfYncoKSkgKwogIGZhY2V0X2dyaWQoYWNjdXJhY3kgfiBkZXZpY2UpCmBgYAoKQm94Y294IHdvbid0IHdvcmsuIEFSVCBpdCBpcy4KCiMjIEFSVCArIEFOT1ZBCgpgYGB7ciwgd2FybmluZz1GQUxTRSwgZGV2PSJzdmdsaXRlIn0KbV9rcyA9IGFydChwX21lYW5fYWN0dWFsX2tzIH4gZGV2aWNlICogYWNjdXJhY3kgKyAoMXxwYXJ0aWNpcGFudCksCiAgICAgICAgICAgICAgICAgIGRhdGEgPSBwX2RhdGFfa3MpCmFub3ZhKG1fa3MpCmBgYAoKIyMgUGFpcndpc2UgY29tcGFyaXNvbnMgb24gRGV2aWNlCgpgYGB7ciwgd2FybmluZz1GQUxTRSwgZGV2PSJzdmdsaXRlIn0KZW1tZWFucyhhcnRsbShtX2tzLCAiZGV2aWNlIiksIHBhaXJ3aXNlIH4gZGV2aWNlKQpgYGAKCiMjIFBhaXJ3aXNlIGNvbXBhcmlzb25zIG9uIEFjY3VyYWN5CgpgYGB7ciwgd2FybmluZz1GQUxTRSwgZGV2PSJzdmdsaXRlIn0KZW1tZWFucyhhcnRsbShtX2tzLCAiYWNjdXJhY3kiKSwgcGFpcndpc2UgfiBhY2N1cmFjeSkKYGBgCgojIyBJbnRlcmFjdGlvbiBDb250cmFzdHMgY29tcGFyaXNvbnMgb24gQWNjdXJhY3kgWCBEZXZpY2UKClBhaXJ3aXNlIGNvbXBhcmlzb25zIGFyZSBub3QgcmVsaWFibGUgd2l0aCBBUlQuIEluc3RlYWQsIHdlIHBlcmZvcm0gaW50ZXJhY3Rpb24gY29udHJhc3RzLiBTZWUgPGh0dHA6Ly9kZXB0cy53YXNoaW5ndG9uLmVkdS9hY2VsYWIvcHJvai9hcnQvaW5kZXguaHRtbD4uCgpgYGB7ciwgd2FybmluZz1GQUxTRSwgZGV2PSJzdmdsaXRlIn0KY29udHJhc3QoZW1tZWFucyhhcnRsbShtX2tzLCAiZGV2aWNlOmFjY3VyYWN5IiksIH4gZGV2aWNlOmFjY3VyYWN5KSwgbWV0aG9kPSJwYWlyd2lzZSIsIGludGVyYWN0aW9uPVRSVUUpCmBgYAoKIyBLZXlzdHJva2UgU2F2aW5nIFJhdGlvCgojIyBTdW1tYXJpZXMKCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBkZXY9InN2Z2xpdGUifQpwX2RhdGFfdHNrdSA8LSBtZWFzdXJlZF90cmlhbHMgfD4KICBncm91cF9ieShwYXJ0aWNpcGFudCwgYWNjdXJhY3lfbnVtZXJpYywgYWNjdXJhY3ksIGRldmljZSkgfD4KICBzdW1tYXJpemUoCiAgICBwX21lYW5fa3NyID0gbWVhbigodG90YWxfZmluYWxfc3VnZ2VzdGlvbl9jaGFycyAvIHRvdGFsX2NoYXJzKSAvIHRoZW9yZXRpY2FsX2tleV9zYXZpbmcpLAogICAgLmdyb3Vwcz0iZHJvcCIKICApIAphY3R1YWxfdHNrdV9kYXRhIDwtIHBfZGF0YV90c2t1IHw+CiAgZ3JvdXBfYnkoYWNjdXJhY3lfbnVtZXJpYywgYWNjdXJhY3ksIGRldmljZSkgfD4KICBzdW1tYXJpemUoCiAgICBlcnJvcl9rc3IgPSB0X2Vycm9yKHBfbWVhbl9rc3IpLAogICAgbWVhbl9rc3IgPSBtZWFuKHBfbWVhbl9rc3IpLAogICAgbWluX2tzciA9IG1heCgwLCBtZWFuX2tzciAtIGVycm9yX2tzciksCiAgICBtYXhfa3NyID0gbWluKDEsIG1lYW5fa3NyICsgZXJyb3Jfa3NyKSwKICAgIC5ncm91cHM9ImRyb3AiCiAgKQoKCmFjdHVhbF90c2t1X2RhdGEKYGBgCgpgYGB7ciwgd2FybmluZz1GQUxTRSwgZGV2PSJzdmdsaXRlIn0KCm1lYXN1cmVkX3RyaWFscyB8PgogIGdyb3VwX2J5KGRldmljZSwgcGFydGljaXBhbnQpIHw+CiAgc3VtbWFyaXplKHBfbWVhbl9rc3IgPSBtZWFuKCh0b3RhbF9maW5hbF9zdWdnZXN0aW9uX2NoYXJzIC8gdG90YWxfY2hhcnMpIC8gdGhlb3JldGljYWxfa2V5X3NhdmluZyksIC5ncm91cHM9ImRyb3BfbGFzdCIpICB8PgogIHN1bW1hcml6ZShtZWFuX2tzciA9IG1lYW4ocF9tZWFuX2tzciksIC5ncm91cHMgPSAiZHJvcCIpCmBgYAoKIyMjIFBlciBhY2N1cmFjeQoKYGBge3IsIHdhcm5pbmc9RkFMU0UsIGRldj0ic3ZnbGl0ZSJ9Cm1lYXN1cmVkX3RyaWFscyB8PgogIGdyb3VwX2J5KGFjY3VyYWN5LCBwYXJ0aWNpcGFudCkgIHw+CiAgc3VtbWFyaXplKHBfbWVhbl9rc3IgPSBtZWFuKCh0b3RhbF9maW5hbF9zdWdnZXN0aW9uX2NoYXJzIC8gdG90YWxfY2hhcnMpIC8gdGhlb3JldGljYWxfa2V5X3NhdmluZyksIC5ncm91cHM9ImRyb3BfbGFzdCIpICB8PgogIHN1bW1hcml6ZShtZWFuX2tzciA9IG1lYW4ocF9tZWFuX2tzciksIC5ncm91cHMgPSAiZHJvcCIpCmBgYAoKIyMgR3JhcGhzCgpgYGB7ciwgd2FybmluZz1GQUxTRSwgZGV2PSJzdmdsaXRlIn0KdGhlb3JldGljYWxfdHNrdV9kYXRhIDwtIHRpYmJsZSgKICBhY2N1cmFjeV9udW1lcmljID0gQUNDVVJBQ1lfTEVWRUxTX05VTSwKICBtZWFuX2tzciA9IDEuMCwKICBtaW5fa3NyID0gMS4wLAogIG1heF9rc3IgPSAxLjAKKQpwZCA8LSBwb3NpdGlvbl9kb2RnZSgwLjAyNSkKa2V5c3Ryb2tlX3NhdmluZ19yYXRpb19wbG90IDwtIGdncGxvdChhY3R1YWxfdHNrdV9kYXRhLCBhZXMoCiAgICB4ID0gYWNjdXJhY3lfbnVtZXJpYywKICAgIHkgPSBtZWFuX2tzciwKICAgIHltaW4gPSBtaW5fa3NyLAogICAgeW1heCA9IG1heF9rc3IsCiAgICBjb2xvciA9IGRldmljZSwKICAgIGdyb3VwID0gZGV2aWNlCiAgKSkgKwogIGN1c3RvbV9saW5lKAogICAgZGF0YT10aGVvcmV0aWNhbF90c2t1X2RhdGEsCiAgICBjb2xvcj1USEVPUkVUSUNBTF9DT0xPUiwKICAgIGdyb3VwPSJ0aGVvcmV0aWNhbCIKICApICsKICBjdXN0b21fcG9pbnRyYW5nZSgKICAgIGRhdGE9dGhlb3JldGljYWxfdHNrdV9kYXRhLAogICAgY29sb3I9VEhFT1JFVElDQUxfQ09MT1IsCiAgICBncm91cD0idGhlb3JldGljYWwiLAogICAgc2hhcGU9MTcKICApICsKICBjdXN0b21fbGluZShwb3NpdGlvbj1wZCkgKwogIGN1c3RvbV9wb2ludHJhbmdlKHBvc2l0aW9uPXBkKSArCiAgU0NBTEVfWF9BQ0NVUkFDWSArCiAgeGxhYigiIikgKwogIHNjYWxlX2NvbG9yX21hbnVhbCgiRGV2aWNlIiwgdmFsdWVzID0gREVWSUNFX0NPTE9SUykgKwogIHNjYWxlX3lfY29udGludW91cygiS2V5c3Ryb2tlIFNhdmluZyBSYXRpbyIsIGxpbWl0cyA9IGMoMCwgMSksIGxhYmVscyA9IHBlcmNlbnQpKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCkpCgpnZ3NhdmUoCiAgZ3JhcGhfcGF0aCgia2V5c3Ryb2tlLXNhdmluZy1yYXRpby1kZXZpY2VzLnBkZiIpLAogIHVuaXRzID0gIm1tIiwKICAjIE1hbnVhbGx5IGFkZCAyIG1tIGJlY2F1c2UgYXhpcyBsYWJlbHMgYXJlIGxvbmdlci4KICB3aWR0aCA9IEJBU0VfV0lEVEggKyAyLCAKICBoZWlnaHQgPSBCQVNFX0hFSUdIVCwKICBkZXZpY2UgPSBjYWlyb19wZGYKKQoKa2V5c3Ryb2tlX3NhdmluZ19yYXRpb19wbG90IApgYGAKCiMjIEFSVCArIEFOT1ZBCgpgYGB7ciwgd2FybmluZz1GQUxTRSwgZGV2PSJzdmdsaXRlIn0KbV9rc3IgPSBhcnQocF9tZWFuX2tzciB+IGRldmljZSAqIGFjY3VyYWN5ICsgKDF8cGFydGljaXBhbnQpLAogICAgICAgICAgICBkYXRhID0gcF9kYXRhX3Rza3UpCmFub3ZhKG1fa3MpCmBgYAoKYGBge3IsIHdhcm5pbmc9RkFMU0UsIGRldj0ic3ZnbGl0ZSJ9CmVtbWVhbnMoYXJ0bG0obV9rc3IsICJkZXZpY2UiKSwgcGFpcndpc2UgfiBkZXZpY2UpCmBgYAoKYGBge3IsIHdhcm5pbmc9RkFMU0UsIGRldj0ic3ZnbGl0ZSJ9CmVtbWVhbnMoYXJ0bG0obV9rc3IsICJhY2N1cmFjeSIpLCBwYWlyd2lzZSB+IGFjY3VyYWN5KQpgYGAKCiMgVHJpYWwgRHVyYXRpb24KCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBkZXY9InN2Z2xpdGUifQphY3R1YWxfZ3JhcGhfZGF0YSA8LSBtZWFzdXJlZF90cmlhbHMgfD4KICBncm91cF9ieShwYXJ0aWNpcGFudCwgYWNjdXJhY3lfbnVtZXJpYywgZGV2aWNlKSB8PgogIHN1bW1hcml6ZSgKICAgIHBfdmFsdWUgPSBtZWFuKGR1cmF0aW9uKSwKICAgIC5ncm91cHM9ImRyb3AiCiAgKSB8PgogIGdyb3VwX2J5KGFjY3VyYWN5X251bWVyaWMsIGRldmljZSkgfD4KICBzdW1tYXJpemUoCiAgICBlcnJvcl92YWx1ZSA9IHRfZXJyb3IocF92YWx1ZSksCiAgICBtZWFuX3ZhbHVlID0gbWVhbihwX3ZhbHVlKSwKICAgIG1pbl92YWx1ZSA9IG1heCgwLCBtZWFuX3ZhbHVlIC0gZXJyb3JfdmFsdWUpLAogICAgbWF4X3ZhbHVlID0gbWVhbl92YWx1ZSArIGVycm9yX3ZhbHVlLAogICAgLmdyb3Vwcz0iZHJvcCIKICApCnBkIDwtIHBvc2l0aW9uX2RvZGdlKDAuMDI1KQpnZ3Bsb3QoYWN0dWFsX2dyYXBoX2RhdGEsIGFlcygKICAgIHggPSBhY2N1cmFjeV9udW1lcmljLAogICAgeSA9IG1lYW5fdmFsdWUsCiAgICB5bWluID0gbWluX3ZhbHVlLAogICAgeW1heCA9IG1heF92YWx1ZSwKICAgIGNvbG9yID0gZGV2aWNlLAogICAgZ3JvdXAgPSBkZXZpY2UKICApKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcz1jKDAsIDAuMSwgMC4zLCAwLjUsIDAuNywgMC45LCAxKSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCgiRGV2aWNlIiwgdmFsdWVzID0gREVWSUNFX0NPTE9SUykgKwogIGV4cGFuZF9saW1pdHMoeSA9IGMoMCkpICsKICBnZW9tX2xpbmUocG9zaXRpb249cGQpICsKICBnZW9tX3BvaW50cmFuZ2UocG9zaXRpb249cGQpICsKICBsYWJzKHg9IlN1Z2dlc3Rpb25zIEFjY3VyYWN5IiwgeT0iVHJpYWwgRHVyYXRpb24gKHNlY29uZHMpIiwgY29sb3I9IktleSBTdHJva2UgRGVsYXkiKSAKYGBgCgojIEVudHJ5IFNwZWVkCgojIyBTdW1tYXJpZXMKCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBkZXY9InN2Z2xpdGUifQpwX2RhdGFfdHMgPC0gbWVhc3VyZWRfdHJpYWxzIHw+CiAgZ3JvdXBfYnkocGFydGljaXBhbnQsIGFjY3VyYWN5LCBkZXZpY2UsIGFjY3VyYWN5X251bWVyaWMpIHw+CiAgc3VtbWFyaXplKHNwZWVkID0gbWVhbihjcHMpIC8gNSAqIDYwLCAuZ3JvdXBzID0gImRyb3AiKQoKc3VtbWFyeV90cyA8LSBwX2RhdGFfdHMgfD4KICBncm91cF9ieShhY2N1cmFjeV9udW1lcmljLCBkZXZpY2UpIHw+CiAgc3VtbWFyaXplKAogICAgZXJyb3JfdmFsdWUgPSB0X2Vycm9yKHNwZWVkKSwKICAgIG1lYW5fdmFsdWUgPSBtZWFuKHNwZWVkKSwKICAgIG1pbl92YWx1ZSA9IG1heCgwLCBtZWFuX3ZhbHVlIC0gZXJyb3JfdmFsdWUpLAogICAgbWF4X3ZhbHVlID0gbWVhbl92YWx1ZSArIGVycm9yX3ZhbHVlLAogICAgLmdyb3VwcyA9ICJkcm9wIgogICkKCiMgQWxzbyBjYWxjdWxhdGluZyB0aGlzIGhlcmUgYmVjYXVzZSB3ZSB3YW50IHRoZSBzYW1lIG1heCBiZXR3ZWVuIG5hdHVyYWwgZW50cnkgc3BlZWQgYW5kIGVudHJ5CiMgc3BlZWQuCnN1bW1hcnlfbnRzIDwtIG1lYXN1cmVkX3J1bnMgfD4KICBmaWx0ZXIoIWlzLm5hKHJ1bl9zdGFydF9kYXRlKSkgfD4KICBtdXRhdGUoYWNjdXJhY3lfbnVtZXJpYyA9IGFjY3VyYWN5IHw+IGFzLmNoYXJhY3RlcigpIHw+IGFzLm51bWVyaWMoKSkgfD4KICBncm91cF9ieShhY2N1cmFjeV9udW1lcmljLCBkZXZpY2UpIHw+CiAgc3VtbWFyaXplKAogICAgZXJyb3JfdmFsdWUgPSB0X2Vycm9yKGF2Z193cG0pLAogICAgbWVhbl92YWx1ZSA9IG1lYW4oYXZnX3dwbSksCiAgICBtaW5fdmFsdWUgPSBtYXgoMCwgbWVhbl92YWx1ZSAtIGVycm9yX3ZhbHVlKSwKICAgIG1heF92YWx1ZSA9IG1lYW5fdmFsdWUgKyBlcnJvcl92YWx1ZSwKICAgIC5ncm91cHMgPSAiZHJvcCIKICApCgptYXhfd3BtID0gbWF4KHN1bW1hcnlfdHMkbWF4X3ZhbHVlLCBzdW1tYXJ5X250cyRtYXhfdmFsdWUpCnN1bW1hcnlfdHMKYGBgCgojIyMgUGVyIGRldmljZXMKCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBkZXY9InN2Z2xpdGUifQptZWFzdXJlZF90cmlhbHMgfD4KICBncm91cF9ieShkZXZpY2UsIHBhcnRpY2lwYW50KSB8PgogIHN1bW1hcml6ZShtX2VzID0gbWVhbihjcHMpIC8gNSAqIDYwLCAuZ3JvdXBzID0gImRyb3BfbGFzdCIpICB8PgogIHN1bW1hcml6ZShtZWFuX3dwbSA9IG1lYW4obV9lcyksIC5ncm91cHMgPSAiZHJvcCIpCmBgYAoKIyMjIFBlciBhY2N1cmFjeQoKYGBge3IsIHdhcm5pbmc9RkFMU0UsIGRldj0ic3ZnbGl0ZSJ9Cm1lYXN1cmVkX3RyaWFscyB8PgogIGdyb3VwX2J5KGFjY3VyYWN5LCBwYXJ0aWNpcGFudCkgfD4KICBzdW1tYXJpemUobV9lcyA9IG1lYW4oY3BzKSAvIDUgKiA2MCwgLmdyb3VwcyA9ICJkcm9wX2xhc3QiKSAgfD4KICBzdW1tYXJpemUobWVhbl93cG0gPSBtZWFuKG1fZXMpLCAuZ3JvdXBzID0gImRyb3AiKQpgYGAKCiMjIEdyYXBoCgpgYGB7ciwgd2FybmluZz1GQUxTRSwgZGV2PSJzdmdsaXRlIn0KcGQgPC0gcG9zaXRpb25fZG9kZ2UoMC4wMjUpCmVudHJ5X3NwZWVkX3Bsb3QgPC0gZ2dwbG90KHN1bW1hcnlfdHMsIGFlcygKICAgIHggPSBhY2N1cmFjeV9udW1lcmljLAogICAgeSA9IG1lYW5fdmFsdWUsCiAgICB5bWluID0gbWluX3ZhbHVlLAogICAgeW1heCA9IG1heF92YWx1ZSwKICAgIGNvbG9yID0gZGV2aWNlLAogICAgZ3JvdXAgPSBkZXZpY2UKICApKSArCiAgU0NBTEVfWF9BQ0NVUkFDWSArCiAgZXhwYW5kX2xpbWl0cyh5ID0gYygwLCBtYXhfd3BtKSkgKwogIHNjYWxlX3lfY29udGludW91cygKICAgIGJyZWFrcz1zZXEoMCwgbWF4X3dwbSArIDIwLCAyMCksCiAgICBsYWJlbHM9cGFzdGUoc2VxKDAsIG1heF93cG0gKyAyMCwgMjApLCAid3BtIikKICApICsKICBzY2FsZV9jb2xvcl9tYW51YWwoIkRldmljZSIsIHZhbHVlcyA9IERFVklDRV9DT0xPUlMpICsKICBjdXN0b21fbGluZShwb3NpdGlvbj1wZCkgKwogIGN1c3RvbV9wb2ludHJhbmdlKHBvc2l0aW9uPXBkKSArCiAgbGFicyh5PSJFbnRyeSBTcGVlZCIpICsKICBndWlkZXMoY29sb3IgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChsaW5ldHlwZSA9IDApKSkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCkpCgpnZ3NhdmUoCiAgZ3JhcGhfcGF0aCgiZW50cnktc3BlZWQtZGV2aWNlcy5wZGYiKSwKICB1bml0cyA9ICJtbSIsCiAgIyBNYW51YWxseSBhZGQgMiBtbSBiZWNhdXNlIGF4aXMgbGFiZWxzIGFyZSBldmVuIGxvbmdlci4KICB3aWR0aCA9IEJBU0VfV0lEVEggKyAzLCAKICBoZWlnaHQgPSBCQVNFX0hFSUdIVCwKICBkZXZpY2UgPSBjYWlyb19wZGYKKQoKZW50cnlfc3BlZWRfcGxvdApgYGAKCiMjIEFSVCArIEFOT1ZBCgpgYGB7ciwgd2FybmluZz1GQUxTRSwgZGV2PSJzdmdsaXRlIn0KbV90cyA9IGFydChzcGVlZCB+IGRldmljZSAqIGFjY3VyYWN5ICsgKDF8cGFydGljaXBhbnQpLAogICAgICAgICAgICBkYXRhID0gcF9kYXRhX3RzKQphbm92YShtX3RzKQpgYGAKCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBkZXY9InN2Z2xpdGUifQplbW1lYW5zKGFydGxtKG1fdHMsICJkZXZpY2UiKSwgcGFpcndpc2UgfiBkZXZpY2UpCmBgYAoKYGBge3IsIHdhcm5pbmc9RkFMU0UsIGRldj0ic3ZnbGl0ZSJ9CmVtbWVhbnMoYXJ0bG0obV90cywgImFjY3VyYWN5IiksIHBhaXJ3aXNlIH4gYWNjdXJhY3kpCmBgYAoKIyBOYXR1cmFsIEVudHJ5IFNwZWVkCgpUaGlzIGlzIHRoZSBlbnRyeSBzcGVlZCB3aXRob3V0IHN1Z2dlc3Rpb25zLgoKYGBge3IsIHdhcm5pbmc9RkFMU0UsIGRldj0ic3ZnbGl0ZSJ9CmdncGxvdChtZWFzdXJlZF9ydW5zLCBhZXMoeCA9IGF2Z193cG0sIGZpbGw9ZGV2aWNlKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gNSkgKwogIGZhY2V0X2dyaWQocm93cyA9ICJkZXZpY2UiKQpgYGAKCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBkZXY9InN2Z2xpdGUifQoKIyBzdW1tYXJ5X250cyBpcyBjYWxjdWxhdGVkIGluIHRoZSBjZWxsIGFib3ZlLgoKcGQgPC0gcG9zaXRpb25fZG9kZ2UoMC4wMjUpCmdncGxvdChzdW1tYXJ5X250cywgYWVzKAogICAgeCA9IGFjY3VyYWN5X251bWVyaWMsCiAgICB5ID0gbWVhbl92YWx1ZSwKICAgIHltaW4gPSBtaW5fdmFsdWUsCiAgICB5bWF4ID0gbWF4X3ZhbHVlLAogICAgY29sb3IgPSBkZXZpY2UsCiAgICBncm91cCA9IGRldmljZQogICkpICsKICBTQ0FMRV9YX0FDQ1VSQUNZICsKICBleHBhbmRfbGltaXRzKHkgPSBjKDAsIG1heF93cG0pKSArCiAgICBzY2FsZV95X2NvbnRpbnVvdXMoCiAgICBicmVha3M9c2VxKDAsIG1heF93cG0gKyAyMCwgMjApLAogICAgbGFiZWxzPXBhc3RlKHNlcSgwLCBtYXhfd3BtICsgMjAsIDIwKSwgIndwbSIpCiAgKSArCiAgY3VzdG9tX2xpbmUocG9zaXRpb249cGQpICsKICBjdXN0b21fcG9pbnRyYW5nZShwb3NpdGlvbj1wZCkgKwogIGxhYnMoeT0iTmF0dXJhbCBFbnRyeSBTcGVlZCIpICsKICBndWlkZXMoY29sb3IgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChsaW5ldHlwZSA9IDApKSkKYGBgCgpJZGVhbGx5LCB3ZSB3b3VsZCB3YW50IGFsbCB0aHJlZSBsaW5lcyB0byBiZSBjb25zdGFudC4KCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBkZXY9InN2Z2xpdGUifQpzdW1tYXJ5X250cyB8PgogIGdyb3VwX2J5KGRldmljZSkgfD4KICBzdW1tYXJpemUoCiAgICAuZ3JvdXBzID0gImRyb3AiLAogICAgbWVhbl9zcGVlZCA9IG1lYW4obWVhbl92YWx1ZSksCiAgICBzZF9zcGVlZCA9IHNkKG1lYW5fdmFsdWUpLAogICAgZGlmZiA9IG1heChtZWFuX3ZhbHVlKSAtIG1pbihtaW4obWVhbl92YWx1ZSkpCiAgKQpgYGAKCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBkZXY9InN2Z2xpdGUifQplekFOT1ZBKAogIG1lYXN1cmVkX3J1bnMsCiAgYXZnX3dwbSwKICB3aWQgPSBjKCJwYXJ0aWNpcGFudCIpLAogIHdpdGhpbiA9IGMoImRldmljZSIpCikKYGBgCgpgYGB7ciwgd2FybmluZz1GQUxTRSwgZGV2PSJzdmdsaXRlIn0KcGFpcndpc2UudC50ZXN0KG1lYXN1cmVkX3J1bnMkYXZnX3dwbSwgbWVhc3VyZWRfcnVucyRkZXZpY2UsIHBhaXJlZCA9IFQsIHAuYWRqdXN0ID0gImJvbmYiKQpgYGAKCiMgS2V5c3Ryb2tlIHNhdmluZyAvIG5hdHVyYWwgZW50cnkgc3BlZWQKCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBkZXY9InN2Z2xpdGUifQprc2VzX2RhdGEgPC0KICBsZWZ0X2pvaW4ocF9kYXRhX2tzLAogICAgICAgICAgICBtZWFzdXJlZF9ydW5zLAogICAgICAgICAgICBieSA9IGMoInBhcnRpY2lwYW50IiwgImRldmljZSIsICJhY2N1cmFjeSIpKSB8PgogIHNlbGVjdChwYXJ0aWNpcGFudCwKICAgICAgICAgZGV2aWNlLAogICAgICAgICBhY2N1cmFjeSwKICAgICAgICAgYXZnX3dwbSwKICAgICAgICAgcF9tZWFuX2FjdHVhbF9rcykgfD4KICBtdXRhdGUoCiAgICBhY2NfbGFiZWwgPSBwYXN0ZTAoImFjY3VyYWN5ICIsIGFjY3VyYWN5KSwKICAgIGFjY3VyYWN5X251bWVyaWMgPSBhcy5udW1lcmljKGFjY3VyYWN5KSwKICAgIGRldmljZSA9IGRldmljZSB8PiByZWNvZGVfZmFjdG9yKGxhcHRvcCA9ICJkZXNrdG9wIikKICApCgpoX2xpbmVfZGF0YSA8LQogIHRpYmJsZShhY2MgPSBjKDAuMSwgMC41LCAwLjkpLCB4ID0gMTEwKSB8PiBtdXRhdGUoYWNjX2xhYmVsID0gcGFzdGUoImFjY3VyYWN5IiwgYWNjKSkKCmRwMSA8LSBnZ3Bsb3Qoa3Nlc19kYXRhLCBhZXMoeCA9IGF2Z193cG0sIHkgPSBwX21lYW5fYWN0dWFsX2tzKSkgKwogIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gZGV2aWNlLCBzaGFwZSA9IGRldmljZSkpICsKICBmYWNldF9ncmlkKGNvbHMgPSB2YXJzKGFjY19sYWJlbCkpICsKICBnZW9tX3RleHQoCiAgICBhZXMoeSA9IGFjYywgeCA9IHgpLAogICAgY29sb3IgPSAiIzQwNDA0MCIsCiAgICBsYWJlbCA9ICJtYXgiLAogICAgZGF0YSA9IGhfbGluZV9kYXRhLAogICAgc2l6ZSA9IDIsCiAgICBoanVzdCA9IDAsCiAgICB2anVzdCA9IC0wLjc1LAogICAgZmFtaWx5ID0gIkxpbnV4IExpYmVydGluZSIKICApICsKICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0ID0gYWNjKSwgc2l6ZSA9IDAuMjUsIGRhdGEgPSBoX2xpbmVfZGF0YSkgKwogIGxhYnMoCiAgICB4ID0gIk5hdHVyYWwgRW50cnkgU3BlZWQgKHdwbSkiLAogICAgeSA9ICJLZXlzdHJva2UgU2F2aW5nIiwKICAgIGNvbG9yID0gIkRldmljZSIsCiAgICBzaGFwZSA9ICJEZXZpY2UiCiAgKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IERFVklDRV9DT0xPUlMpICsKICBleHBhbmRfbGltaXRzKHggPSAwKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoMCwgMSksIGxhYmVscyA9IHBlcmNlbnQpCgpnZ3NhdmUoCiAgZ3JhcGhfcGF0aCgia2V5c3Ryb2tlLXNhdmluZy12cy1lbnRyeS1zcGVlZC5wZGYiKSwKICBwbG90ID0gZHAxLAogIHVuaXRzID0gIm1tIiwKICAjIE1hbnVhbGx5IGFkZCAyIG1tIGJlY2F1c2UgYXhpcyBsYWJlbHMgYXJlIGV2ZW4gbG9uZ2VyLgogIHdpZHRoID0gRlVMTF9XSURUSCwKICBoZWlnaHQgPSBCQVNFX0hFSUdIVCwKICBkZXZpY2U9Y2Fpcm9fcGRmCikKCmRwMQpgYGAKCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBkZXY9InN2Z2xpdGUifQptIDwtCiAgbG1lcigKICAgIHBfbWVhbl9hY3R1YWxfa3MgfiBhdmdfd3BtICsgYWNjdXJhY3lfbnVtZXJpYyArCiAgICAgICgxICsgYXZnX3dwbSB8IHBhcnRpY2lwYW50KSArICgxICsgYXZnX3dwbSB8IGRldmljZSksCiAgICBkYXRhID0ga3Nlc19kYXRhCiAgKQoKcXFub3JtKHJlc2lkdWFscyhtKSkKcXFsaW5lKHJlc2lkdWFscyhtKSkKCnNoYXBpcm8udGVzdChyZXNpZHVhbHMobSkpCmBgYAoKUmVzaWR1YWxzIGZyb20gTGluZWFyIE1peGVkIE1vZGVsIGFyZSBub3Qgbm9ybWFsbHkgZGlzdHJpYnV0ZWQuIFNpbXBsZSBQZWFyc29uIGNvcnJlbGF0aW9ucyB3aWxsIGRvLgoKYGBge3IsIHdhcm5pbmc9RkFMU0UsIGRldj0ic3ZnbGl0ZSJ9CmZvciAoeCBpbiBjKDAuMSwwLjUsIDAuOSkpIHsKICBkIDwtIGtzZXNfZGF0YSB8PiBmaWx0ZXIoYWNjdXJhY3kgPT0geCkKICBtZXNzYWdlKHBhc3RlMCgiLS0tLS0tIEEgPSAiLCB4LCAiIC0tLS0tLSIpKQogIHByaW50KGNvci50ZXN0KGQkYXZnX3dwbSwgZCRwX21lYW5fYWN0dWFsX2tzLCBtZXRob2Q9ImtlbmRhbGwiKSkKfQpgYGAKCiMgS2V5c3Ryb2tlIHNhdmluZyByYXRpbyAvIG5hdHVyYWwgZW50cnkgc3BlZWQKCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBkZXY9InN2Z2xpdGUifQprc25lc19kYXRhIDwtCiAgbGVmdF9qb2luKHBfZGF0YV90c2t1LAogICAgICAgICAgICBtZWFzdXJlZF9ydW5zLAogICAgICAgICAgICBieSA9IGMoInBhcnRpY2lwYW50IiwgImRldmljZSIsICJhY2N1cmFjeSIpKSB8PgogIHNlbGVjdChwYXJ0aWNpcGFudCwgZGV2aWNlLCBhY2N1cmFjeSwgYXZnX3dwbSwgcF9tZWFuX2tzcikgfD4KICBtdXRhdGUoCiAgICBhY2NfbGFiZWwgPSBwYXN0ZSgiYWNjdXJhY3kiLCBhY2N1cmFjeSksCiAgICBkZXZpY2UgPSBkZXZpY2UgfD4gcmVjb2RlX2ZhY3RvcihsYXB0b3AgPSAiZGVza3RvcCIpCiAgKQoKZ2dwbG90KGtzbmVzX2RhdGEsCiAgICAgICBhZXMoCiAgICAgICAgIGNvbG9yID0gZGV2aWNlLAogICAgICAgICBzaGFwZSA9IGRldmljZSwKICAgICAgICAgeCA9IGF2Z193cG0sCiAgICAgICAgIHkgPSBwX21lYW5fa3NyCiAgICAgICApKSArCiAgZ2VvbV9wb2ludCgpICsKICBmYWNldF9ncmlkKGNvbHMgPSB2YXJzKGFjY19sYWJlbCkpICsKICBsYWJzKAogICAgeCA9ICJOYXR1cmFsIEVudHJ5IFNwZWVkICh3cG0pIiwKICAgIHkgPSAiS2V5c3Ryb2tlIFNhdmluZyBSYXRpbyIsCiAgICBjb2xvciA9ICJEZXZpY2UiLAogICAgc2hhcGUgPSAiRGV2aWNlIgogICkgKwogIGV4cGFuZF9saW1pdHMoeCA9IDApICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gREVWSUNFX0NPTE9SUykgKwogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKDAsIDEpLCBsYWJlbHMgPSBwZXJjZW50KQoKZ2dzYXZlKAogIGdyYXBoX3BhdGgoImtleXN0cm9rZS1zYXZpbmctcmF0aW8tdnMtZW50cnktc3BlZWQucGRmIiksCiAgdW5pdHMgPSAibW0iLAogICMgTWFudWFsbHkgYWRkIDIgbW0gYmVjYXVzZSBheGlzIGxhYmVscyBhcmUgZXZlbiBsb25nZXIuCiAgd2lkdGggPSBGVUxMX1dJRFRILAogIGhlaWdodCA9IEJBU0VfSEVJR0hULAogIGRldmljZT1jYWlyb19wZGYKKQpgYGAKCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBkZXY9InN2Z2xpdGUifQpmb3IgKGEgaW4gYygwLjEsIDAuNSwgMC45KSl7CiAgZCA8LSBrc25lc19kYXRhIHw+IGZpbHRlcihhY2N1cmFjeSA9PSBhKQogIG1lc3NhZ2UocGFzdGUwKCItLS0tLS0gQSA9ICIsIGEsICIgLS0tLS0tIikpCiAgcHJpbnQoY29yLnRlc3QoZCRhdmdfd3BtLCBkJHBfbWVhbl9rc3IsIG1ldGhvZCA9ICJrZW5kYWxsIikpCn0KYGBgCgojIE5hdHVyYWwgZW50cnkgc3BlZWQgdnMgZW50cnkgc3BlZWQKCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBkZXY9InN2Z2xpdGUifQpwX2RhdGFfdHMgPC0gbWVhc3VyZWRfdHJpYWxzIHw+CiAgZ3JvdXBfYnkocGFydGljaXBhbnQsIGFjY3VyYWN5LCBkZXZpY2UpIHw+CiAgc3VtbWFyaXplKHNwZWVkID0gbWVhbihjcHMpIC8gNSAqIDYwLCAuZ3JvdXBzID0gImRyb3AiKSB8PgogIGxlZnRfam9pbigKICAgIG1lYXN1cmVkX3J1bnMgfD4gc2VsZWN0KHBhcnRpY2lwYW50LCBhY2N1cmFjeSwgZGV2aWNlLCBhdmdfd3BtKSwKICAgIGJ5ID0gYygicGFydGljaXBhbnQiLCAiZGV2aWNlIiwgImFjY3VyYWN5IikKICApIHw+CiAgbXV0YXRlKAogICAgYWNjX2xhYmVsID0gcGFzdGUwKCJhY2N1cmFjeSAiLCBhY2N1cmFjeSksCiAgICBpbXByb3ZlbWVudCA9IHNwZWVkIC0gYXZnX3dwbSwKICAgIGRldmljZSA9ICByZWNvZGVfZmFjdG9yKGRldmljZSwgbGFwdG9wID0gImRlc2t0b3AiKQogICkKCmRwMiA8LSBnZ3Bsb3QocF9kYXRhX3RzLAogICAgICAgICAgICAgIGFlcygKICAgICAgICAgICAgICAgIGNvbG9yID0gZGV2aWNlLAogICAgICAgICAgICAgICAgc2hhcGUgPSBkZXZpY2UsCiAgICAgICAgICAgICAgICB4ID0gYXZnX3dwbSwKICAgICAgICAgICAgICAgIHkgPSBpbXByb3ZlbWVudAogICAgICAgICAgICAgICkpICsKICBmYWNldF9ncmlkKGNvbHMgPSB2YXJzKGFjY19sYWJlbCkpICsKICBnZW9tX3BvaW50KCkgKwogIGxhYnMoCiAgICB5ID0gIkVudHJ5IFNwZWVkIEltcHJvdmVtZW50ICh3cG0pIiwKICAgIHggPSAiTmF0dXJhbCBFbnRyeSBTcGVlZCAod3BtKSIsCiAgICBjb2xvciA9ICJEZXZpY2UiLAogICAgc2hhcGUgPSAiRGV2aWNlIgogICkgKwogIGV4cGFuZF9saW1pdHMoeCA9IDApICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gREVWSUNFX0NPTE9SUykgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQojIHNjYWxlX3lfY29udGludW91cygKIyAgIGJyZWFrcyA9IHNlcSgtOTAsIDkwLCAzMCksCiMgICBsYWJlbHMgPSBzdHJfcGFkKGFzLmNoYXJhY3RlcihzZXEoLTkwLCA5MCwgMzApKSwgNiwgc2lkZSA9ICJsZWZ0IiwgcGFkID0gIiAiKQojICkgKwoKZ2dzYXZlKAogIGdyYXBoX3BhdGgoImVudHJ5LXNwZWVkLWltcHJvdmVtZW50LnBkZiIpLAogIHBsb3QgPSBkcDIsCiAgd2lkdGggPSBGVUxMX1dJRFRIKjAuNiwKICBoZWlnaHQgPSBCQVNFX0hFSUdIVCozKjAuNiwKICB1bml0cyA9ICJtbSIsCiAgZGV2aWNlID0gY2Fpcm9fcGRmCikKCmRwMgpgYGAKCmBgYHtyfQojIEV4cG9ydCBkcDEgYW5kIGRwMiB0b2dldGhlcgplbnRyeV9zcGVlZF9jb21wYXJpc29uc19jb21iaW5lZF9wbG90IDwtIGRwMSAvIGRwMiArIHBsb3RfbGF5b3V0KGd1aWRlcyA9ICdjb2xsZWN0JykKCmdnc2F2ZSgKICBncmFwaF9wYXRoKCJlbnRyeS1zcGVlZC1jb21wYXJpc29ucy1jb21iaW5lZC5wZGYiKSwKICBwbG90ID0gZW50cnlfc3BlZWRfY29tcGFyaXNvbnNfY29tYmluZWRfcGxvdCwKICB1bml0cyA9ICJtbSIsCiAgIyBNYW51YWxseSBhZGQgMiBtbSBiZWNhdXNlIGF4aXMgbGFiZWxzIGFyZSBldmVuIGxvbmdlci4KICB3aWR0aCA9IEZVTExfV0lEVEgsIAogIGhlaWdodCA9IChCQVNFX0hFSUdIVCArIDEwKSAqIDIsCiAgZGV2aWNlID0gY2Fpcm9fcGRmCikKYGBgCgpgYGB7ciwgd2FybmluZz1GQUxTRSwgZGV2PSJzdmdsaXRlIn0KZm9yIChhIGluIGMoMC4xLCAwLjUsIDAuOSkpewogIGQgPC0gcF9kYXRhX3RzIHw+IGZpbHRlcihhY2N1cmFjeSA9PSBhKQogIG1lc3NhZ2UocGFzdGUwKCItLS0tLS0gQSA9ICIsIGEsICIgLS0tLS0tIikpCiAgcHJpbnQoY29yLnRlc3QoZCRhdmdfd3BtLCBkJGltcHJvdmVtZW50LCBtZXRob2Q9ImtlbmRhbGwiKSkKfQpgYGAKCiMgU3VnZ2VzdGlvbnMgZGVsYXkKCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBkZXY9InN2Z2xpdGUifQpwX2RhdGFfc3VnZ2VzdGlvbl9kZWxheSA8LSBtZWFzdXJlZF90cmlhbHMgfD4KICBncm91cF9ieShwYXJ0aWNpcGFudCwgYWNjdXJhY3ksIGRldmljZSwgYWNjdXJhY3lfbnVtZXJpYykgfD4KICBzdW1tYXJpemUoCiAgICBzdWdnZXN0aW9uX2RlbGF5ID0gbWVhbihhdmdfc3VnZ2VzdGlvbl9kZWxheSkgKiAxMDAwLAogICAgIyBGb3IgZXpBTk9WQS4KICAgIHN1Z2dlc3Rpb25fZGVsYXlfbnVtID0gYXMubnVtZXJpYyhzdWdnZXN0aW9uX2RlbGF5KSwKICAgIC5ncm91cHMgPSAiZHJvcCIKICApCgpzdW1tYXJ5X3N1Z2dlc3Rpb25fZGVsYXkgPC0gcF9kYXRhX3N1Z2dlc3Rpb25fZGVsYXkgfD4KICBncm91cF9ieShhY2N1cmFjeV9udW1lcmljLCBkZXZpY2UpIHw+CiAgc3VtbWFyaXplKAogICAgZXJyb3JfdmFsdWUgPSB0X2Vycm9yKHN1Z2dlc3Rpb25fZGVsYXkpLAogICAgbWVhbl92YWx1ZSA9IG1lYW4oc3VnZ2VzdGlvbl9kZWxheSksCiAgICBtaW5fdmFsdWUgPSBtYXgoMTUwLCBtZWFuX3ZhbHVlIC0gZXJyb3JfdmFsdWUpLAogICAgbWF4X3ZhbHVlID0gbWVhbl92YWx1ZSArIGVycm9yX3ZhbHVlLAogICAgLmdyb3VwcyA9ICJkcm9wIgogICkKCnBkIDwtIHBvc2l0aW9uX2RvZGdlKDAuMDI1KQpnZ3Bsb3QoCiAgc3VtbWFyeV9zdWdnZXN0aW9uX2RlbGF5LAogIGFlcygKICAgIHggPSBhY2N1cmFjeV9udW1lcmljLAogICAgeSA9IG1lYW5fdmFsdWUsCiAgICB5bWluID0gbWluX3ZhbHVlLAogICAgeW1heCA9IG1heF92YWx1ZSwKICAgIGNvbG9yID0gZGV2aWNlLAogICAgZ3JvdXAgPSBkZXZpY2UKICApCikgKwogIFNDQUxFX1hfQUNDVVJBQ1kgKwogIGV4cGFuZF9saW1pdHMoeSA9IGMoMCkpICsKICBjdXN0b21fbGluZShwb3NpdGlvbiA9IHBkKSArCiAgY3VzdG9tX3BvaW50cmFuZ2UocG9zaXRpb24gPSBwZCkgKwogIGxhYnMoeSA9ICJTdWdnZXN0aW9uIGRlbGF5IChtcykiKSArCiAgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3QobGluZXR5cGUgPSAwKSkpCmBgYAoKYGBge3IsIHdhcm5pbmc9RkFMU0UsIGRldj0ic3ZnbGl0ZSJ9CgptX2RlbGF5ID0gYXJ0KHN1Z2dlc3Rpb25fZGVsYXlfbnVtIH4gZGV2aWNlICogYWNjdXJhY3kgKyAoMXxwYXJ0aWNpcGFudCksCiAgICAgICAgICAgIGRhdGEgPSBwX2RhdGFfc3VnZ2VzdGlvbl9kZWxheSkKYW5vdmEobV9rcywgdHlwZSA9IDMpCgpgYGAKCiMjIFBhaXJ3aXNlIGNvbXBhcmlzb25zIG9uIERldmljZQoKYGBge3IsIHdhcm5pbmc9RkFMU0UsIGRldj0ic3ZnbGl0ZSJ9CmVtbWVhbnMoYXJ0bG0obV9kZWxheSwgImRldmljZSIpLCBwYWlyd2lzZSB+IGRldmljZSkKYGBgCgojIyBQYWlyd2lzZSBjb21wYXJpc29ucyBvbiBBY2N1cmFjeQoKYGBge3IsIHdhcm5pbmc9RkFMU0UsIGRldj0ic3ZnbGl0ZSJ9CmVtbWVhbnMoYXJ0bG0obV9kZWxheSwgImFjY3VyYWN5IiksIHBhaXJ3aXNlIH4gYWNjdXJhY3kpCmBgYAoKIyMgSW50ZXJhY3Rpb24gQ29udHJhc3RzIGNvbXBhcmlzb25zIG9uIEFjY3VyYWN5IFggRGV2aWNlCgpQYWlyd2lzZSBjb21wYXJpc29ucyBhcmUgbm90IHJlbGlhYmxlIHdpdGggQVJULiBJbnN0ZWFkLCB3ZSBwZXJmb3JtIGludGVyYWN0aW9uIGNvbnRyYXN0cy4gU2VlIDxodHRwOi8vZGVwdHMud2FzaGluZ3Rvbi5lZHUvYWNlbGFiL3Byb2ovYXJ0L2luZGV4Lmh0bWw+LgoKYGBge3IsIHdhcm5pbmc9RkFMU0UsIGRldj0ic3ZnbGl0ZSJ9CmNvbnRyYXN0KGVtbWVhbnMoYXJ0bG0obV9kZWxheSwgImRldmljZTphY2N1cmFjeSIpLCB+IGRldmljZTphY2N1cmFjeSksIG1ldGhvZD0icGFpcndpc2UiLCBpbnRlcmFjdGlvbj1UUlVFKQpgYGAKCiMgTGV0dGVyIGZyZXF1ZW5jaWVzCgpgYGB7ciwgd2FybmluZz1GQUxTRSwgZGV2PSJzdmdsaXRlIn0KbWVhc3VyZWRfdHJpYWxzIHw+CiAgc2VsZWN0KHBhcnRpY2lwYW50LCBkZXZpY2UsIGFjY3VyYWN5LCB0cmlhbF9pZCwgcGhyYXNlKSB8PgogIG11dGF0ZShsZXR0ZXIgPSBzdHJzcGxpdCgiYWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoiLCAiIikpIHw+CiAgdW5uZXN0KGxldHRlcikgfD4KICBtdXRhdGUobl9sZXR0ZXIgPSAgc3RyX2NvdW50KHBocmFzZSwgbGV0dGVyKSkgfD4KICBncm91cF9ieShsZXR0ZXIpIHw+CiAgc3VtbWFyaXplKG5fbGV0dGVyID0gc3VtKG5fbGV0dGVyKSwgLmdyb3Vwcz0iZHJvcCIpIHw+CiAgbXV0YXRlKGYgPSBuX2xldHRlciAvIHN1bShuX2xldHRlcikpIHw+CiAgYXJyYW5nZShkZXNjKGYpKQpgYGAKCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBkZXY9InN2Z2xpdGUifQptZWFzdXJlZF90cmlhbHMgfD4KICBzZWxlY3QocGFydGljaXBhbnQsIGRldmljZSwgYWNjdXJhY3ksIHRyaWFsX2lkLCBwaHJhc2UpIHw+CiAgbXV0YXRlKGxldHRlciA9IHN0cnNwbGl0KCJhYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5eiIsICIiKSkgfD4KICB1bm5lc3QobGV0dGVyKSB8PgogIG11dGF0ZShuX2xldHRlciA9ICBzdHJfY291bnQocGhyYXNlLCBsZXR0ZXIpKSB8PgogIGdyb3VwX2J5KGxldHRlciwgYWNjdXJhY3ksIGRldmljZSkgfD4KICBzdW1tYXJpemUobl9sZXR0ZXIgPSBzdW0obl9sZXR0ZXIpLCAuZ3JvdXBzPSJkcm9wIikgfD4KICBncm91cF9ieShhY2N1cmFjeSwgZGV2aWNlKSB8PgogIG11dGF0ZShmID0gbl9sZXR0ZXIgLyBzdW0obl9sZXR0ZXIpKSB8PgogIGFycmFuZ2UoZGVzYyhmKSkKYGBgCgojIExlYXJuaW5nCgpXZSBpbnZlc3RpZ2F0ZSBsZWFybmluZyBpbiB0ZXJtIG9mIHNwZWVkLgoKIyMgR3JhcGgKCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBkZXY9InN2Z2xpdGUifQpwX2RhdGFfa3MgPC0gbWVhc3VyZWRfdHJpYWxzIHw+CiAgZ3JvdXBfYnkocGFydGljaXBhbnQsIGFjY3VyYWN5LCBkZXZpY2UsIGFjY3VyYWN5X251bWVyaWMsIHRyaWFsX251bWJlcikgfD4KICBzdW1tYXJpemUoCiAgICBwX21lYW5fYWN0dWFsX2tzID0gbWVhbihhY3R1YWxfa2V5X3NhdmluZ19ub19lZGl0aW5nKSwKICAgIHBfbWVhbl90aGVvcmV0aWNhbF9rcyA9IG1lYW4odGhlb3JldGljYWxfa2V5X3NhdmluZyksCiAgICAuZ3JvdXBzPSJkcm9wIgogICkgfD4KICBncm91cF9ieShhY2N1cmFjeSwgZGV2aWNlLCB0cmlhbF9udW1iZXIpIHw+CiAgbXV0YXRlKAogICAgaXNfb3V0bGllciA9IGlzX291dGxpZXIocF9tZWFuX2FjdHVhbF9rcykKICApIHw+CiAgdW5ncm91cCgpCgphY3R1YWxfa3Nfc3VtbWFyeSA8LSBwX2RhdGFfa3MgfD4KICBncm91cF9ieShhY2N1cmFjeV9udW1lcmljLCBkZXZpY2UsIHRyaWFsX251bWJlcikgfD4KICBzdW1tYXJpemUoCiAgICBlcnJvcl9rcyA9IHRfZXJyb3IocF9tZWFuX2FjdHVhbF9rcyksCiAgICBtZWFuX2tzID0gbWVhbihwX21lYW5fYWN0dWFsX2tzKSwKICAgIG1pbl9rcyA9IG1heCgwLCBtZWFuX2tzIC0gZXJyb3Jfa3MpLAogICAgbWF4X2tzID0gbWluKDEsIG1lYW5fa3MgKyBlcnJvcl9rcyksCiAgICBrc190eXBlID0gImFjdHVhbCIsCiAgICAuZ3JvdXBzPSJkcm9wIgogICkKCnRoZW9yZXRpY2FsX2tzX3N1bW1hcnkgPC0gcF9kYXRhX2tzIHw+CiAgZ3JvdXBfYnkoYWNjdXJhY3lfbnVtZXJpYywgdHJpYWxfbnVtYmVyKSB8PgogIHN1bW1hcml6ZSgKICAgIGRldmljZSA9IE5BLAogICAgZXJyb3Jfa3MgPSB0X2Vycm9yKHBfbWVhbl90aGVvcmV0aWNhbF9rcyksCiAgICBtZWFuX2tzID0gbWVhbihwX21lYW5fdGhlb3JldGljYWxfa3MpLAogICAgbWluX2tzID0gbWF4KDAsIG1lYW5fa3MgLSBlcnJvcl9rcyksCiAgICBtYXhfa3MgPSBtaW4oMSwgbWVhbl9rcyArIGVycm9yX2tzKSwKICAgIGtzX3R5cGUgPSAidGhlb3JldGljYWwiLAogICAgLmdyb3Vwcz0iZHJvcCIKICApCiAgCmtzX3N1bW1hcnkgPC0gdW5pb25fYWxsKHRoZW9yZXRpY2FsX2tzX3N1bW1hcnksIGFjdHVhbF9rc19zdW1tYXJ5KQoKcGQgPC0gcG9zaXRpb25fZG9kZ2UoMC4yKQpnZ3Bsb3Qoa3Nfc3VtbWFyeSwgYWVzKAogICAgeCA9IHRyaWFsX251bWJlciwKICAgIHkgPSBtZWFuX2tzLAogICAgeW1pbiA9IG1pbl9rcywKICAgIHltYXggPSBtYXhfa3MsCiAgICBjb2xvciA9IGRldmljZSwKICAgIGdyb3VwID0gZGV2aWNlCiAgKSkgKwogIGZhY2V0X3dyYXAodmFycyhhY2N1cmFjeV9udW1lcmljKSkgKwogIGN1c3RvbV9saW5lKAogICAgZGF0YT10aGVvcmV0aWNhbF9rc19zdW1tYXJ5LAogICAgY29sb3I9VEhFT1JFVElDQUxfQ09MT1IsCiAgICBncm91cD0idGhlb3JldGljYWwiCiAgKSArCiAgY3VzdG9tX3BvaW50cmFuZ2UoCiAgICBkYXRhPXRoZW9yZXRpY2FsX2tzX3N1bW1hcnksCiAgICBjb2xvcj1USEVPUkVUSUNBTF9DT0xPUiwKICAgIGdyb3VwPSJ0aGVvcmV0aWNhbCIsCiAgICBzaGFwZT0xNwogICkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCgiRGV2aWNlIiwgdmFsdWVzID0gREVWSUNFX0NPTE9SUykgKwogIGN1c3RvbV9saW5lKGRhdGE9YWN0dWFsX2tzX3N1bW1hcnksIHBvc2l0aW9uPXBkKSArCiAgY3VzdG9tX3BvaW50cmFuZ2UoZGF0YT1hY3R1YWxfa3Nfc3VtbWFyeSwgcG9zaXRpb249cGQpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoIktleXN0cm9rZSBTYXZpbmciLCBsaW1pdHMgPSBjKDAsIDEpLCBsYWJlbHMgPSBwZXJjZW50KSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpCgpnZ3NhdmUoCiAgZ3JhcGhfcGF0aCgibGVhcm5pbmcta2V5c3Ryb2tlLXNhdmluZy1kZXZpY2VzLnBkZiIpLAogIHVuaXRzID0gIm1tIiwKICAjIE1hbnVhbGx5IGFkZCAyIG1tIGJlY2F1c2UgYXhpcyBsYWJlbHMgYXJlIGxvbmdlci4KICB3aWR0aCA9IDE1MCwgCiAgaGVpZ2h0ID0gNzUsCiAgZGV2aWNlID0gY2Fpcm9fcGRmCikKYGBgCgpgYGB7cn0KdGhlbWVfbWFyZ2luIDwtCiAgdGhlbWUocGxvdC5tYXJnaW4gPSBtYXJnaW4ociA9IDIsIGIgPSAyLCB1bml0ID0gIm1tIikpCgpvYmplY3RpdmVfY29tYmluZWQgPC0gKHN1Z2dlc3Rpb25fdXNhZ2VfcGxvdCArIHRoZW1lX21hcmdpbikgICsKICBlbnRyeV9zcGVlZF9wbG90ICsKICBrZXlfc3Ryb2tlX3NhdmluZ19wbG90ICsga2V5c3Ryb2tlX3NhdmluZ19yYXRpb19wbG90CgoKZ2dzYXZlKAogIHBsb3QgPSBvYmplY3RpdmVfY29tYmluZWQsCiAgZ3JhcGhfcGF0aCgib2JqZWN0aXZlcy1tdWx0aS5wZGYiKSwKICB1bml0cyA9ICJtbSIsCiAgd2lkdGggPSBGVUxMX1dJRFRILAogIGhlaWdodCA9IEZVTExfV0lEVEggLyBHT0xERU5fUkFUSU8sCiAgZGV2aWNlID0gY2Fpcm9fcGRmCikKCm9iamVjdGl2ZV9jb21iaW5lZApgYGAK