1 Overview

This script regenerates all manuscript figures from the saved projection object. The reference object is loaded solely to extract UMAP coordinates for the grey background.

1.1 Objects required

Object Path Role
reference_integrated ../../1-Final_Custom_MST_Monocle3_Trajectory_and_mapping/1-Custom_MST/Objects/cd4_ref_dual_trajectory.rds UMAP background only — grey reference dots
mapped_MalignantCD4T results/MalignantCD4T_Monocle3_projection/MalignantCD4T_mapped_monocle3_pseudotime_noProlif_ref.Rds All biology — pseudotime, labels, bins, colour scales

1.2 Key design decisions

Item Source
Pseudotime colour-scale limits (PT_LIMITS) mapped_MalignantCD4T$predicted.pseudotime
Bin boundaries Midpoints between actual reference state medians — computed at runtime from ref_bg
State medians (REF_MEDIANS) Computed at runtime from ref_bgnever hard-coded
All bar charts, violins, density plots mapped_MalignantCD4T only

1.3 Panel B fix

Previously called FeaturePlot(reference_integrated, ...) without limits, producing a spurious 1.0–2.0 colour scale. Fixed: Panel B plots Sézary cells coloured by predicted.pseudotime with limits = PT_LIMITS (correct 0–25 scale).

1.4 Bin design

Bins are anchored to actual reference state medians computed from ref_bg. Boundaries = midpoint between adjacent state medians (biology-principled, not arbitrary). Treg-like bin is assigned by Azimuth label (Treg is a branch, not a linear position).


2 1. Setup & Libraries

suppressPackageStartupMessages({
  library(Seurat)
  library(ggplot2)
  library(dplyr)
  library(tidyr)
  library(forcats)
  library(patchwork)
  library(scales)
  library(RColorBrewer)
  library(ggridges)
  library(knitr)
  library(kableExtra)
})
Registered S3 methods overwritten by 'htmltools':
  method               from         
  print.html           tools:rstudio
  print.shiny.tag      tools:rstudio
  print.shiny.tag.list tools:rstudio
Registered S3 method overwritten by 'rmarkdown':
  method         from
  print.paged_df     
dir.create("Figures/PNG", recursive = TRUE, showWarnings = FALSE)
dir.create("Figures/PDF", recursive = TRUE, showWarnings = FALSE)

save_fig <- function(p, name, w = 10, h = 8) {
  ggsave(file.path("Figures/PNG", paste0(name, ".png")),
         p, width = w, height = h, dpi = 300, bg = "white")
  ggsave(file.path("Figures/PDF", paste0(name, ".pdf")),
         p, width = w, height = h, useDingbats = FALSE)
  invisible(cat(sprintf("  \u2713  %s  [PNG + PDF]\n", name)))
}

STATE_COLORS <- c(
  "CD4 Naive"     = "#4472C4",
  "CD4 TCM"       = "#70AD47",
  "CD4 TEM"       = "#ED7D31",
  "CD4 Temra/CTL" = "#C00000",
  "Treg"          = "#7030A0"
)

BIN_COLORS <- c(
  "Naive-like"  = "#4472C4",
  "TCM-like"    = "#70AD47",
  "TEM-like"    = "#ED7D31",
  "Temra-like"  = "#C00000",
  "Treg-like"   = "#7030A0"
)

STATE_ORDER <- c("CD4 Naive", "CD4 TCM", "CD4 TEM", "CD4 Temra/CTL", "Treg")
BIN_ORDER   <- c("Naive-like", "TCM-like", "TEM-like", "Temra-like", "Treg-like")
LINE_ORDER  <- paste0("L", 1:7)

cat("Setup complete.\n")
Setup complete.

3 2. Load & Validate Objects

ref_path <- paste0(
  "../../1-Final_Custom_MST_Monocle3_Trajectory_and_mapping/",
  "1-Custom_MST/Objects/cd4_ref_dual_trajectory.rds"
)
reference_integrated <- readRDS(ref_path)
cat("Reference loaded:", ncol(reference_integrated), "cells\n")
Reference loaded: 11466 cells
stopifnot(
  "umap reduction missing from reference" =
    "umap" %in% names(reference_integrated@reductions)
)

obj_path <- paste0(
  "../results/MalignantCD4T_Monocle3_projection/",
  "MalignantCD4T_mapped_monocle3_pseudotime_noProlif_ref.Rds"
)
mapped_MalignantCD4T <- readRDS(obj_path)
cat("Query loaded:    ", ncol(mapped_MalignantCD4T), "cells\n")
Query loaded:     40695 cells
stopifnot(
  "ref.umap reduction missing — re-run MapQuery" =
    "ref.umap" %in% names(mapped_MalignantCD4T@reductions),
  "predicted.pseudotime column missing" =
    "predicted.pseudotime" %in% colnames(mapped_MalignantCD4T@meta.data),
  "predicted.predicted.celltype.l2 column missing" =
    "predicted.predicted.celltype.l2" %in% colnames(mapped_MalignantCD4T@meta.data)
)

if (!"cell_line" %in% colnames(mapped_MalignantCD4T@meta.data)) {
  if ("orig.ident" %in% colnames(mapped_MalignantCD4T@meta.data)) {
    mapped_MalignantCD4T$cell_line <- mapped_MalignantCD4T$orig.ident
    cat("NOTE: using orig.ident as cell_line\n")
  } else {
    stop("Cannot find cell_line or orig.ident in metadata")
  }
}

cat("\npredicted.pseudotime summary (expect Min~0, Max~25):\n")

predicted.pseudotime summary (expect Min~0, Max~25):
print(summary(mapped_MalignantCD4T$predicted.pseudotime))
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  1.156  16.431  17.703  17.655  19.487  25.553 
cat("\nTransferred label distribution:\n")

Transferred label distribution:
print(sort(table(mapped_MalignantCD4T$predicted.predicted.celltype.l2,
                 useNA = "ifany"), decreasing = TRUE))

  CD4 TCM   CD4 TEM      Treg CD4 Naive 
    40291       304        79        21 
cat("\nCell line sizes:\n")

Cell line sizes:
print(sort(table(mapped_MalignantCD4T$cell_line, useNA = "ifany")))

  L6   L7   L1   L2   L4   L5   L3 
5148 5331 5825 5935 6006 6022 6428 

4 3. Build Reference Background (ref_bg)

# ref_bg serves two purposes:
#   1. Grey background dots in all projection UMAPs
#   2. Source for computing actual reference state medians (REF_MEDIANS)
ref_bg <- data.frame(
  Embeddings(reference_integrated, "umap"),
  state      = reference_integrated$predicted.celltype.l2,
  pseudotime = reference_integrated$monocle3_pseudotime,
  stringsAsFactors = FALSE
)
colnames(ref_bg)[1:2] <- c("UMAP_1", "UMAP_2")
ref_bg$state <- factor(ref_bg$state, levels = STATE_ORDER)

cat("Reference monocle3_pseudotime (should be 0-25):\n")
Reference monocle3_pseudotime (should be 0-25):
print(summary(ref_bg$pseudotime))
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  0.000   3.855   7.404   9.926  15.525  25.356 
ref_bg_plot <- ref_bg[sample(nrow(ref_bg)), ]
cat(sprintf("\nref_bg ready: %d reference cells\n", nrow(ref_bg)))

ref_bg ready: 11466 reference cells

5 4. Compute Reference State Medians & Bin Boundaries

# ══════════════════════════════════════════════════════════════════════════
# REF_MEDIANS computed at runtime from actual ref_bg — NOT hard-coded.
#
# Diagnostic confirmed these actual values:
#   CD4 Naive     median =  3.293
#   CD4 TCM       median =  9.377  (was hard-coded as 13.36 — WRONG)
#   CD4 TEM       median = 24.840
#   CD4 Temra/CTL median = 24.840  (only 10 cells)
#   Treg          median = 11.150  (was hard-coded as 18.75 — above Treg max of 17.40, WRONG)
#
# Bin boundaries derived as midpoints between adjacent state medians.
# ══════════════════════════════════════════════════════════════════════════

ref_state_medians <- ref_bg %>%
  filter(!is.na(pseudotime), !is.na(state)) %>%
  group_by(state) %>%
  summarise(
    n      = n(),
    med_pt = round(median(pseudotime, na.rm = TRUE), 3),
    .groups = "drop"
  ) %>%
  arrange(med_pt)

cat("=== Reference state medians (data-derived) ===\n")
=== Reference state medians (data-derived) ===
print(ref_state_medians)

get_med <- function(s) {
  v <- ref_state_medians$med_pt[ref_state_medians$state == s]
  if (length(v) == 0) stop(paste("State not found in ref_bg:", s))
  v
}

naive_med  <- get_med("CD4 Naive")
tcm_med    <- get_med("CD4 TCM")
tem_med    <- get_med("CD4 TEM")
temra_med  <- get_med("CD4 Temra/CTL")
treg_med   <- get_med("Treg")

BIN_NAIVE_TCM  <- round((naive_med + tcm_med)  / 2, 3)
BIN_TCM_TEM    <- round((tcm_med   + tem_med)   / 2, 3)
BIN_TEM_TEMRA  <- round((tem_med   + temra_med) / 2, 3)

cat("\n=== Bin boundaries (midpoints between adjacent state medians) ===\n")

=== Bin boundaries (midpoints between adjacent state medians) ===
cat(sprintf("  Naive | TCM   : %.3f\n", BIN_NAIVE_TCM))
  Naive | TCM   : 6.335
cat(sprintf("  TCM   | TEM   : %.3f\n", BIN_TCM_TEM))
  TCM   | TEM   : 17.108
cat(sprintf("  TEM   | Temra : %.3f\n", BIN_TEM_TEMRA))
  TEM   | Temra : 24.840
cat(sprintf("  Treg           : label-based (branch logic)\n"))
  Treg           : label-based (branch logic)
if (abs(tem_med - temra_med) < 0.5) {
  cat("\nNOTE: TEM and Temra medians are very close (n=10 Temra cells in reference).\n")
  cat("      Temra-like bin will be negligible — biologically expected.\n")
}

NOTE: TEM and Temra medians are very close (n=10 Temra cells in reference).
      Temra-like bin will be negligible — biologically expected.
REF_MEDIANS <- data.frame(
  state  = factor(STATE_ORDER, levels = STATE_ORDER),
  med_pt = c(naive_med, tcm_med, tem_med, temra_med, treg_med)
)

cat("\n=== REF_MEDIANS (used for reference lines in all figures) ===\n")

=== REF_MEDIANS (used for reference lines in all figures) ===
print(REF_MEDIANS)
# ── REF_MEDIANS_EFFECTOR: Treg excluded ───────────────────────────────────
# Treg (median PT=11.15) and TCM (median PT=9.38) overlap entirely in
# pseudotime space — both states share the Naive→TCM graph segment.
# Showing a Treg line at PT=11.15 implies a pseudotime boundary that does
# not exist and misleads interpretation. Treg-like cells are assigned by
# transferred Azimuth label (KNN), not by pseudotime position.
REF_MEDIANS_EFFECTOR <- REF_MEDIANS %>%
  filter(state != "Treg")

LINETYPE_EFFECTOR <- c(
  "CD4 Naive"     = "dotted",
  "CD4 TCM"       = "dashed",
  "CD4 TEM"       = "longdash",
  "CD4 Temra/CTL" = "solid"
)

cat("=== REF_MEDIANS_EFFECTOR (Treg excluded) ===\n")
=== REF_MEDIANS_EFFECTOR (Treg excluded) ===
print(REF_MEDIANS_EFFECTOR)

6 5. Assign Pseudotime Bins

assign_bin <- function(pt, lbl) {
  if (isTRUE(grepl("Treg", lbl, ignore.case = TRUE))) return("Treg-like")
  if (is.na(pt))             return(NA_character_)
  if (pt <= BIN_NAIVE_TCM)   return("Naive-like")
  if (pt <= BIN_TCM_TEM)     return("TCM-like")
  if (pt <= BIN_TEM_TEMRA)   return("TEM-like")
  return("Temra-like")
}

mapped_MalignantCD4T$pseudotime_bin <- factor(
  mapply(assign_bin,
         pt  = mapped_MalignantCD4T$predicted.pseudotime,
         lbl = mapped_MalignantCD4T$predicted.predicted.celltype.l2),
  levels = BIN_ORDER
)

cat("Pseudotime bin distribution (all cells):\n")
Pseudotime bin distribution (all cells):
print(table(mapped_MalignantCD4T$pseudotime_bin, useNA = "ifany"))

Naive-like   TCM-like   TEM-like Temra-like  Treg-like 
      2287      13298      23485       1546         79 
cat("\nPseudotime bin per cell line:\n")

Pseudotime bin per cell line:
print(table(mapped_MalignantCD4T$cell_line,
            mapped_MalignantCD4T$pseudotime_bin, useNA = "ifany"))
    
     Naive-like TCM-like TEM-like Temra-like Treg-like
  L1       1407     1104     2630        682         2
  L2        833     1593     3324        185         0
  L3          4     2357     3613        447         7
  L4         37     1384     4569         11         5
  L5          3     3419     2414        178         8
  L6          0     1815     3244         43        46
  L7          3     1626     3691          0        11

7 6. Build Shared Data Frames

query_df <- data.frame(
  Embeddings(mapped_MalignantCD4T, "ref.umap"),
  pseudotime     = mapped_MalignantCD4T$predicted.pseudotime,
  cell_line      = factor(mapped_MalignantCD4T$cell_line, levels = LINE_ORDER),
  label          = mapped_MalignantCD4T$predicted.predicted.celltype.l2,
  pseudotime_bin = mapped_MalignantCD4T$pseudotime_bin,
  stringsAsFactors = FALSE
)
colnames(query_df)[1:2] <- c("UMAP_1", "UMAP_2")

PT_LIMITS <- range(query_df$pseudotime, na.rm = TRUE)
cat(sprintf("Pseudotime colour limits: %.3f to %.3f\n", PT_LIMITS[1], PT_LIMITS[2]))
Pseudotime colour limits: 1.156 to 25.553
label_df <- query_df %>%
  filter(!is.na(label), !is.na(cell_line)) %>%
  count(cell_line, label) %>%
  group_by(cell_line) %>%
  mutate(pct = 100 * n / sum(n)) %>%
  ungroup() %>%
  mutate(label = factor(label, levels = STATE_ORDER))

bin_df <- query_df %>%
  filter(!is.na(pseudotime_bin), !is.na(cell_line)) %>%
  count(cell_line, pseudotime_bin) %>%
  group_by(cell_line) %>%
  mutate(pct = 100 * n / sum(n)) %>%
  ungroup()

cat("Data frames ready.\n")
Data frames ready.

8 7. Figure 7 — Four-Panel Summary


bg_layer <- geom_point(
  data = ref_bg_plot, aes(x = UMAP_1, y = UMAP_2),
  colour = "grey68", size = 0.25, alpha = 0.45, inherit.aes = FALSE
)

p7a <- ggplot(query_df[sample(nrow(query_df)), ],
              aes(UMAP_1, UMAP_2, colour = factor(label, levels = STATE_ORDER))) +
  bg_layer +
  geom_point(size = 0.35, alpha = 0.7) +
  scale_colour_manual(values = STATE_COLORS, name = "Transferred\nLabel",
                      na.value = "grey80") +
  guides(colour = guide_legend(override.aes = list(size = 3, alpha = 1))) +
  theme_classic() +
  labs(x = "UMAP-1", y = "UMAP-2",
       title = "A. Sézary Cells — Transferred CD4 T Cell Labels") +
  theme(plot.title = element_text(hjust = 0.5, size = 12, face = "bold"))

p7b <- ggplot(query_df[order(query_df$pseudotime, na.last = FALSE), ],
              aes(UMAP_1, UMAP_2, colour = pseudotime)) +
  bg_layer +
  geom_point(size = 0.35, alpha = 0.8) +
  scale_colour_gradientn(
    colours  = c("lightblue", "yellow", "red"),
    name     = "Pseudotime",
    limits   = PT_LIMITS,
    na.value = "grey85"
  ) +
  theme_classic() +
  labs(x = "UMAP-1", y = "UMAP-2",
       title = "B. Sézary Cells — Transferred Pseudotime  [FIXED]") +
  theme(plot.title = element_text(hjust = 0.5, size = 12, face = "bold"))

p7c <- ggplot(query_df[sample(nrow(query_df)), ],
              aes(UMAP_1, UMAP_2, colour = pseudotime_bin)) +
  bg_layer +
  geom_point(size = 0.35, alpha = 0.7) +
  scale_colour_manual(values = BIN_COLORS, name = "Pseudotime\nBin",
                      na.value = "grey80") +
  guides(colour = guide_legend(override.aes = list(size = 3, alpha = 1))) +
  theme_classic() +
  labs(x = "UMAP-1", y = "UMAP-2",
       title = "C. Sézary Cells — Pseudotime Bin") +
  theme(plot.title = element_text(hjust = 0.5, size = 12, face = "bold"))

p7d <- ggplot(label_df, aes(cell_line, pct, fill = label)) +
  geom_col(width = 0.75) +
  geom_text(data = label_df %>% filter(pct > 3),
            aes(label = sprintf("%.1f%%", pct)),
            position = position_stack(vjust = 0.5),
            size = 3, colour = "white", fontface = "bold") +
  scale_fill_manual(values = STATE_COLORS, name = "Label") +
  scale_y_continuous(expand = c(0, 0), limits = c(0, 101)) +
  theme_classic() +
  labs(x = "Cell Line", y = "% Cells",
       title = "D. Transferred Label per Cell Line") +
  theme(plot.title  = element_text(hjust = 0.5, size = 12, face = "bold"),
        axis.text.x = element_text(size = 10, face = "bold"))

fig7 <- (p7a | p7b) / (p7c | p7d)
print(fig7)

save_fig(fig7, "Fig7_FourPanel_Summary_FIXED",       w = 18, h = 14)
  ✓  Fig7_FourPanel_Summary_FIXED  [PNG + PDF]
save_fig(p7a,  "Fig7A_Transferred_Labels",           w = 9,  h = 7)
  ✓  Fig7A_Transferred_Labels  [PNG + PDF]
save_fig(p7b,  "Fig7B_Transferred_Pseudotime_FIXED", w = 9,  h = 7)
  ✓  Fig7B_Transferred_Pseudotime_FIXED  [PNG + PDF]
save_fig(p7c,  "Fig7C_Pseudotime_Bins_UMAP",         w = 9,  h = 7)
  ✓  Fig7C_Pseudotime_Bins_UMAP  [PNG + PDF]
save_fig(p7d,  "Fig7D_Label_BarChart_per_Line",      w = 9,  h = 7)
  ✓  Fig7D_Label_BarChart_per_Line  [PNG + PDF]

9 8. Figure 1 — Pseudotime Violin per Cell Line


fig1 <- ggplot(
    query_df %>% filter(!is.na(pseudotime), !is.na(cell_line)),
    aes(cell_line, pseudotime, fill = cell_line)
  ) +
  geom_violin(scale = "width", trim = FALSE, alpha = 0.85, colour = "white") +
  geom_boxplot(width = 0.06, fill = "white", colour = "grey40",
               outlier.size = 0.3, outlier.alpha = 0.3) +
  geom_hline(data = REF_MEDIANS,
             aes(yintercept = med_pt, colour = state, linetype = state),
             linewidth = 0.7, alpha = 0.9) +
  scale_fill_brewer(palette = "Set2", guide = "none") +
  scale_colour_manual(values = STATE_COLORS, name = "Reference\nmedian") +
  scale_linetype_manual(
    values = c("CD4 Naive" = "dotted", "CD4 TCM" = "dashed",
               "CD4 TEM" = "longdash", "CD4 Temra/CTL" = "solid",
               "Treg" = "twodash"),
    name = "Reference\nmedian"
  ) +
  scale_y_continuous(breaks = seq(0, 25, 5)) +
  theme_classic() +
  labs(
    x        = "Cell Line",
    y        = "Transferred Pseudotime",
    title    = "Figure 1 — Pseudotime Distribution per Sézary Cell Line",
    subtitle = sprintf(
      "Reference medians: Naive=%.2f | TCM=%.2f | Treg=%.2f | TEM=%.2f | Temra=%.2f",
      naive_med, tcm_med, treg_med, tem_med, temra_med)
  ) +
  theme(plot.title    = element_text(size = 13, face = "bold"),
        plot.subtitle = element_text(size = 9,  colour = "grey40"),
        axis.text.x   = element_text(size = 11, face = "bold"))

print(fig1)

save_fig(fig1, "Fig1_Pseudotime_Violin_per_Line", w = 12, h = 6)
  ✓  Fig1_Pseudotime_Violin_per_Line  [PNG + PDF]
fig1v2 <- ggplot(
    query_df %>% filter(!is.na(pseudotime), !is.na(cell_line)),
    aes(cell_line, pseudotime, fill = cell_line)
  ) +
  geom_violin(scale = "width", trim = FALSE, alpha = 0.85, colour = "white") +
  geom_boxplot(width = 0.06, fill = "white", colour = "grey40",
               outlier.size = 0.3, outlier.alpha = 0.3) +
  geom_hline(
    data      = REF_MEDIANS_EFFECTOR,
    aes(yintercept = med_pt, colour = state, linetype = state),
    linewidth = 0.7, alpha = 0.9
  ) +
  scale_fill_brewer(palette = "Set2", guide = "none") +
  scale_colour_manual(
    values = STATE_COLORS[names(STATE_COLORS) != "Treg"],
    name   = "Reference\nmedian"
  ) +
  scale_linetype_manual(
    values = LINETYPE_EFFECTOR,
    name   = "Reference\nmedian"
  ) +
  scale_y_continuous(breaks = seq(0, 25, 5)) +
  theme_classic() +
  labs(
    x        = "Cell Line",
    y        = "Transferred Pseudotime",
    title    = "Figure 1 — Pseudotime Distribution per Sézary Cell Line",
    subtitle = sprintf(
      "Effector axis medians: Naive=%.2f | TCM=%.2f | TEM=%.2f | Temra=%.2f\nTreg line omitted — Treg (PT=%.2f) overlaps TCM (PT=%.2f); Treg-like assigned by label",
      naive_med, tcm_med, tem_med, temra_med, treg_med, tcm_med)
  ) +
  theme(
    plot.title    = element_text(size = 13, face = "bold"),
    plot.subtitle = element_text(size = 9,  colour = "grey40"),
    axis.text.x   = element_text(size = 11, face = "bold")
  )

print(fig1v2)

save_fig(fig1v2, "Fig1v2_Pseudotime_Violin_NoTreg", w = 12, h = 6)
  ✓  Fig1v2_Pseudotime_Violin_NoTreg  [PNG + PDF]

10 9. Figure 2 — UMAP: Pseudotime + Label Side by Side


p2a <- ggplot(query_df[order(query_df$pseudotime, na.last = FALSE), ],
              aes(UMAP_1, UMAP_2, colour = pseudotime)) +
  geom_point(data = ref_bg_plot, aes(UMAP_1, UMAP_2),
             colour = "grey68", size = 0.25, alpha = 0.45, inherit.aes = FALSE) +
  geom_point(size = 0.25, alpha = 0.8) +
  scale_colour_gradientn(
    colours = c("#0D0887","#6A00A8","#B12A90","#E16462","#FCA636","#F0F921"),
    name = "Pseudotime", limits = PT_LIMITS, na.value = "grey85"
  ) +
  theme_classic() +
  labs(x = "UMAP-1", y = "UMAP-2",
       title    = "A. Transferred pseudotime gradient",
       subtitle = "Grey = healthy reference  |  Coloured = Sézary cells") +
  theme(plot.title = element_text(size = 12, face = "bold"))

p2b <- ggplot(query_df[sample(nrow(query_df)), ],
              aes(UMAP_1, UMAP_2, colour = factor(label, levels = STATE_ORDER))) +
  geom_point(data = ref_bg_plot, aes(UMAP_1, UMAP_2),
             colour = "grey88", size = 0.25, alpha = 0.45, inherit.aes = FALSE) +
  geom_point(size = 0.25, alpha = 0.7) +
  scale_colour_manual(values = STATE_COLORS, name = "Transferred\nlabel",
                      na.value = "grey80") +
  guides(colour = guide_legend(override.aes = list(size = 3, alpha = 1))) +
  theme_classic() +
  labs(x = "UMAP-1", y = "UMAP-2",
       title    = "B. Transferred label identity",
       subtitle = "96-99.9% CD4 TCM across all seven lines") +
  theme(plot.title = element_text(size = 12, face = "bold"))

fig2 <- p2a | p2b
print(fig2)

save_fig(fig2, "Fig2_UMAP_Pseudotime_and_Label", w = 16, h = 7)
  ✓  Fig2_UMAP_Pseudotime_and_Label  [PNG + PDF]

11 10. Figure 3 — Label Bar Chart + Per Cell Line UMAP Facets


p3a <- ggplot(label_df, aes(cell_line, pct, fill = label)) +
  geom_col(width = 0.75) +
  geom_text(data = label_df %>% filter(pct > 5),
            aes(label = sprintf("%.0f%%", pct)),
            position = position_stack(vjust = 0.5),
            size = 3, colour = "white", fontface = "bold") +
  scale_fill_manual(values = STATE_COLORS, name = "Label") +
  scale_y_continuous(expand = c(0, 0), limits = c(0, 101)) +
  theme_classic() +
  labs(x = "Cell Line", y = "% Cells", title = "A. Transferred Azimuth l2 Labels") +
  theme(plot.title  = element_text(size = 12, face = "bold"),
        axis.text.x = element_text(size = 10, face = "bold"))

p3b <- ggplot(
    query_df %>% filter(!is.na(cell_line)) %>% arrange(pseudotime),
    aes(UMAP_1, UMAP_2, colour = pseudotime)
  ) +
  geom_point(
    data = ref_bg_plot %>%
      slice(rep(1:n(), times = length(levels(query_df$cell_line)))),
    aes(UMAP_1, UMAP_2), colour = "grey88", size = 0.2, alpha = 0.4,
    inherit.aes = FALSE
  ) +
  geom_point(size = 0.2, alpha = 0.8) +
  scale_colour_gradientn(
    colours = c("#0D0887","#6A00A8","#B12A90","#E16462","#FCA636","#F0F921"),
    name = "Pseudotime", limits = PT_LIMITS, na.value = "grey85"
  ) +
  facet_wrap(~ cell_line, nrow = 2) +
  theme_classic() +
  theme(strip.text = element_text(face = "bold", size = 10),
        plot.title = element_text(size = 12, face = "bold"),
        axis.text  = element_blank(), axis.ticks = element_blank()) +
  labs(x = NULL, y = NULL,
       title = "B. Per Cell Line UMAP — Projected Pseudotime  (Grey = healthy reference)")

fig3 <- p3a + p3b + plot_layout(widths = c(1, 2))
print(fig3)

save_fig(fig3, "Fig3_Labels_PerLine_UMAP", w = 18, h = 6)
  ✓  Fig3_Labels_PerLine_UMAP  [PNG + PDF]

12 11. Figure 4 — Per Cell Line Facet UMAP (Key Heterogeneity Figure)


fig4 <- ggplot(
    query_df %>% filter(!is.na(cell_line)) %>% arrange(pseudotime),
    aes(UMAP_1, UMAP_2, colour = pseudotime)
  ) +
  geom_point(
    data = ref_bg_plot %>%
      slice(rep(1:n(), times = length(levels(query_df$cell_line)))),
    aes(UMAP_1, UMAP_2), colour = "grey88", size = 0.2, alpha = 0.4,
    inherit.aes = FALSE
  ) +
  geom_point(size = 0.25, alpha = 0.8) +
  scale_colour_gradientn(
    colours = c("#0D0887","#6A00A8","#B12A90","#E16462","#FCA636","#F0F921"),
    name = "Pseudotime\n(Naive->Temra)", limits = PT_LIMITS, na.value = "grey85"
  ) +
  facet_wrap(~ cell_line, nrow = 2) +
  theme_classic() +
  theme(
    strip.text       = element_text(face = "bold", size = 12),
    strip.background = element_rect(fill = "grey95", colour = "grey60"),
    plot.title       = element_text(size = 14, face = "bold"),
    plot.subtitle    = element_text(size = 10, colour = "grey40"),
    axis.text = element_blank(), axis.ticks = element_blank(),
    legend.position = "right"
  ) +
  labs(x = NULL, y = NULL,
       title    = "Figure 4 — Per Cell Line Facet UMAP: Differentiation Position",
       subtitle = "L1 most heterogeneous (spans full trajectory)  |  L6 most arrested (tight TCM cluster)")

print(fig4)

save_fig(fig4, "Fig4_PerLine_Facet_UMAP", w = 18, h = 10)
  ✓  Fig4_PerLine_Facet_UMAP  [PNG + PDF]

13 12. Figure 5 — Label vs Pseudotime Bins Bar Charts


p5a <- ggplot(label_df, aes(cell_line, pct, fill = label)) +
  geom_col(width = 0.7) +
  geom_text(data = label_df %>% filter(pct > 3),
            aes(label = sprintf("%.1f%%", pct)),
            position = position_stack(vjust = 0.5),
            size = 3.2, colour = "white", fontface = "bold") +
  scale_fill_manual(values = STATE_COLORS, name = "Label") +
  scale_y_continuous(expand = c(0, 0), limits = c(0, 101)) +
  theme_classic() +
  labs(x = NULL, y = "% Cells",
       title    = "A. Transferred Azimuth l2 Label (Cell of Origin)",
       subtitle = "96-99.9% CD4 TCM across all lines — TCM origin confirmed") +
  theme(plot.title    = element_text(size = 12, face = "bold"),
        plot.subtitle = element_text(size = 10, colour = "grey40"),
        axis.text.x   = element_blank(), axis.ticks.x = element_blank())

p5b <- ggplot(bin_df, aes(cell_line, pct, fill = pseudotime_bin)) +
  geom_col(width = 0.7) +
  geom_text(data = bin_df %>% filter(pct > 3),
            aes(label = sprintf("%.1f%%", pct)),
            position = position_stack(vjust = 0.5),
            size = 3.2, colour = "white", fontface = "bold") +
  scale_fill_manual(values = BIN_COLORS, name = "Pseudotime bin") +
  scale_y_continuous(expand = c(0, 0), limits = c(0, 101)) +
  theme_classic() +
  labs(x = "Cell Line", y = "% Cells",
       title    = "B. Pseudotime Bin Assignment (Differentiation Position)",
       subtitle = sprintf(
         "Bin boundaries — Naive|TCM: %.2f  |  TCM|TEM: %.2f  |  TEM|Temra: %.2f  |  Treg: label-based",
         BIN_NAIVE_TCM, BIN_TCM_TEM, BIN_TEM_TEMRA)) +
  theme(plot.title    = element_text(size = 12, face = "bold"),
        plot.subtitle = element_text(size = 9,  colour = "grey40"),
        axis.text.x   = element_text(size = 11, face = "bold"))

fig5 <- p5a / p5b
print(fig5)

save_fig(fig5, "Fig5_Label_vs_Pseudotime_Bins",  w = 16, h = 12)
  ✓  Fig5_Label_vs_Pseudotime_Bins  [PNG + PDF]
save_fig(p5a,  "Fig5A_Label_Transfer_BarChart",  w = 10, h = 5)
  ✓  Fig5A_Label_Transfer_BarChart  [PNG + PDF]
save_fig(p5b,  "Fig5B_Pseudotime_Bins_BarChart", w = 10, h = 5)
  ✓  Fig5B_Pseudotime_Bins_BarChart  [PNG + PDF]

14 13. Figure 6 — Pseudotime Density + Ridgeplot


p6a <- ggplot(query_df %>% filter(!is.na(pseudotime)), aes(x = pseudotime)) +
  geom_density(fill = "#c0392b", colour = "#c0392b", alpha = 0.4, linewidth = 0.9) +
  geom_vline(data = REF_MEDIANS,
             aes(xintercept = med_pt, colour = state),
             linetype = "dashed", linewidth = 0.7) +
  geom_text(data = REF_MEDIANS,
            aes(x = med_pt, y = Inf,
                label = gsub("CD4 ", "", as.character(state))),
            inherit.aes = FALSE,
            angle = 90, hjust = 1.1, vjust = -0.3, size = 2.8, colour = "grey30") +
  scale_colour_manual(values = STATE_COLORS, name = "Reference\nstate median") +
  scale_x_continuous(limits = c(0, 26), breaks = seq(0, 25, 5)) +
  theme_classic() +
  labs(
    x        = "Transferred Pseudotime",
    y        = "Density",
    title    = "A. Sézary Pseudotime Distribution — All Lines Combined",
    subtitle = sprintf(
      "Reference medians: Naive=%.2f | TCM=%.2f | Treg=%.2f | TEM=%.2f | Temra=%.2f",
      naive_med, tcm_med, treg_med, tem_med, temra_med)
  ) +
  theme(plot.title    = element_text(size = 12, face = "bold"),
        plot.subtitle = element_text(size = 9,  colour = "grey40"),
        legend.position = "right")

p6b <- ggplot(
    query_df %>% filter(!is.na(pseudotime), !is.na(cell_line)),
    aes(x = pseudotime, y = fct_rev(cell_line), fill = cell_line)
  ) +
  geom_density_ridges(scale = 0.9, rel_min_height = 0.01,
                      alpha = 0.85, colour = "white") +
  geom_vline(xintercept = c(BIN_NAIVE_TCM, BIN_TCM_TEM, BIN_TEM_TEMRA),
             linetype = "dashed", colour = "grey50", linewidth = 0.5) +
  annotate("text", x = BIN_NAIVE_TCM, y = 0.5, label = "Naive|TCM",
           size = 2.5, hjust = 1.05, colour = "grey40", angle = 90) +
  annotate("text", x = BIN_TCM_TEM,   y = 0.5, label = "TCM|TEM",
           size = 2.5, hjust = 1.05, colour = "grey40", angle = 90) +
  annotate("text", x = BIN_TEM_TEMRA, y = 0.5, label = "TEM|Temra",
           size = 2.5, hjust = 1.05, colour = "grey40", angle = 90) +
  scale_fill_brewer(palette = "Set2", guide = "none") +
  scale_x_continuous(limits = c(0, 26), breaks = seq(0, 25, 5)) +
  theme_ridges(grid = FALSE) +
  labs(x = "Transferred Pseudotime", y = "Cell Line",
       title    = "B. Per Cell Line Pseudotime Ridgeplot",
       subtitle = "L1 multimodal  |  L3-L7 narrow peaks at TCM-TEM boundary") +
  theme(plot.title    = element_text(size = 12, face = "bold"),
        plot.subtitle = element_text(size = 10, colour = "grey40"))

fig6 <- p6a | p6b
print(fig6)

save_fig(fig6, "Fig6_Density_and_Ridgeplot", w = 16, h = 7)
  ✓  Fig6_Density_and_Ridgeplot  [PNG + PDF]
save_fig(p6a,  "Fig6A_Density_Overall",     w = 8,  h = 6)
  ✓  Fig6A_Density_Overall  [PNG + PDF]
save_fig(p6b,  "Fig6B_Ridgeplot_PerLine",   w = 8,  h = 6)
  ✓  Fig6B_Ridgeplot_PerLine  [PNG + PDF]
p6a_v2 <- ggplot(
    query_df %>% filter(!is.na(pseudotime)),
    aes(x = pseudotime)
  ) +
  geom_density(fill = "#c0392b", colour = "#c0392b", alpha = 0.4, linewidth = 0.9) +
  geom_vline(
    data     = REF_MEDIANS_EFFECTOR,
    aes(xintercept = med_pt, colour = state),
    linetype = "dashed", linewidth = 0.7
  ) +
  geom_text(
    data        = REF_MEDIANS_EFFECTOR,
    aes(x = med_pt, y = Inf,
        label = gsub("CD4 ", "", as.character(state))),
    inherit.aes = FALSE,
    angle = 90, hjust = 1.1, vjust = -0.3, size = 2.8, colour = "grey30"
  ) +
  scale_colour_manual(
    values = STATE_COLORS[names(STATE_COLORS) != "Treg"],
    name   = "Reference\nstate median"
  ) +
  scale_x_continuous(limits = c(0, 26), breaks = seq(0, 25, 5)) +
  theme_classic() +
  labs(
    x        = "Transferred Pseudotime",
    y        = "Density",
    title    = "A. Sézary Pseudotime Distribution — All Lines Combined",
    subtitle = sprintf(
      "Effector axis medians: Naive=%.2f | TCM=%.2f | TEM=%.2f | Temra=%.2f\nTreg line omitted — overlaps TCM; Treg-like assigned by label not pseudotime",
      naive_med, tcm_med, tem_med, temra_med)
  ) +
  theme(
    plot.title      = element_text(size = 12, face = "bold"),
    plot.subtitle   = element_text(size = 9,  colour = "grey40"),
    legend.position = "right"
  )

print(p6a_v2)

save_fig(p6a_v2, "Fig6Av2_Density_NoTreg", w = 8, h = 6)
  ✓  Fig6Av2_Density_NoTreg  [PNG + PDF]

15 14. Figure 8 — Violin: Transferred Pseudotime per Label


fig8 <- ggplot(
    query_df %>%
      filter(!is.na(pseudotime), !is.na(label)) %>%
      mutate(label = factor(label, levels = STATE_ORDER)),
    aes(label, pseudotime, fill = label)
  ) +
  geom_violin(scale = "width", trim = FALSE, alpha = 0.85, colour = "white") +
  geom_boxplot(width = 0.07, fill = "white", colour = "grey40",
               outlier.size = 0.4, outlier.alpha = 0.3) +
  geom_hline(data = REF_MEDIANS, aes(yintercept = med_pt),
             linetype = "dotted", colour = "grey55", linewidth = 0.6) +
  # Annotations driven entirely from REF_MEDIANS — no hard-coded y values
  geom_text(
    data        = REF_MEDIANS,
    aes(x       = 0.55,
        y       = med_pt,
        label   = sprintf("Ref: %s (%.2f)",
                          gsub("CD4 ", "", as.character(state)), med_pt)),
    inherit.aes = FALSE,
    hjust = 0, size = 2.8, colour = "grey40"
  ) +
  scale_fill_manual(values = STATE_COLORS, guide = "none") +
  scale_y_continuous(breaks = seq(0, 25, 5)) +
  theme_classic() +
  labs(
    x        = "Transferred Azimuth l2 Label",
    y        = "Transferred Pseudotime",
    title    = "Figure 8 — Transferred Pseudotime per Label (All Cell Lines)",
    subtitle = "CD4 Naive internal control at PT~4  |  TCM broad distribution  |  TEM at terminal PT~25"
  ) +
  theme(plot.title    = element_text(size = 13, face = "bold"),
        plot.subtitle = element_text(size = 10, colour = "grey40"),
        axis.text.x   = element_text(size = 10, face = "bold"))

print(fig8)

save_fig(fig8, "Fig8_Violin_Pseudotime_per_Label", w = 12, h = 6)
  ✓  Fig8_Violin_Pseudotime_per_Label  [PNG + PDF]

# For TEM and Temra: median=24.84 for both (Temra n=10, all at same PT).
# Use actual reference max values to separate the lines visually:
#   TEM   max = 25.00  (from diagnostic)
#   Temra max = 24.84  (all 10 cells identical)
# Better approach: show TEM median and Temra median separately but
# acknowledge in subtitle they are nearly identical.

# Override: use mean instead of median for TEM/Temra to separate lines
REF_MEDIANS_EFFECTOR_PLOT <- REF_MEDIANS_EFFECTOR %>%
  mutate(med_pt = case_when(
    state == "CD4 TEM"       ~ 23.962,  # mean from diagnostic (median=24.84 same as Temra)
    state == "CD4 Temra/CTL" ~ 24.840,  # all 10 cells identical
    TRUE ~ med_pt
  ),
  label_text = case_when(
    state == "CD4 TEM"       ~ sprintf("Ref: TEM (mean=23.96)"),
    state == "CD4 Temra/CTL" ~ sprintf("Ref: Temra (24.84, n=10)"),
    state == "CD4 Naive"     ~ sprintf("Ref: Naive (%.2f)", med_pt),
    state == "CD4 TCM"       ~ sprintf("Ref: TCM (%.2f)", med_pt),
    TRUE ~ as.character(state)
  ))

fig8v2 <- ggplot(
    query_df %>%
      filter(!is.na(pseudotime), !is.na(label)) %>%
      mutate(label = factor(label, levels = STATE_ORDER)),  # drop=FALSE keeps Temra axis
    aes(label, pseudotime, fill = label)
  ) +
  geom_violin(scale = "width", trim = FALSE, alpha = 0.85, colour = "white") +
  geom_boxplot(width = 0.07, fill = "white", colour = "grey40",
               outlier.size = 0.4, outlier.alpha = 0.3) +
  geom_hline(
    data      = REF_MEDIANS_EFFECTOR_PLOT,
    aes(yintercept = med_pt),
    linetype  = "dotted", colour = "grey55", linewidth = 0.6
  ) +
  geom_text(
    data        = REF_MEDIANS_EFFECTOR_PLOT,
    aes(x       = 0.55,
        y       = med_pt,
        label   = label_text),
    inherit.aes = FALSE,
    hjust = 0, size = 2.8, colour = "grey40"
  ) +
  scale_fill_manual(values = STATE_COLORS, na.value = "grey80", guide = "none",
                    drop = FALSE) +
  scale_x_discrete(drop = FALSE) +   # ← forces CD4 Temra/CTL to appear
  scale_y_continuous(breaks = seq(0, 25, 5)) +
  theme_classic() +
  labs(
    x        = "Transferred Azimuth l2 Label",
    y        = "Transferred Pseudotime",
    title    = "Figure 8 — Transferred Pseudotime per Label (All Cell Lines)",
    subtitle = sprintf(
      "Effector axis: Naive=%.2f | TCM=%.2f | TEM~24.84 | Temra~24.84 (n=10, identical)\nCD4 Temra/CTL: no Sézary cells received this label — consistent with TCM arrest phenotype\nTreg line omitted — overlaps TCM; Treg-like assigned by label",
      naive_med, tcm_med)
  ) +
  theme(
    plot.title    = element_text(size = 13, face = "bold"),
    plot.subtitle = element_text(size = 9,  colour = "grey40"),
    axis.text.x   = element_text(size = 10, face = "bold")
  )

print(fig8v2)

save_fig(fig8v2, "Fig8v2_Violin_NoTreg", w = 12, h = 6)
  ✓  Fig8v2_Violin_NoTreg  [PNG + PDF]
fig8v2 <- ggplot(
    query_df %>%
      filter(!is.na(pseudotime), !is.na(label)) %>%
      mutate(label = factor(label, levels = STATE_ORDER)),
    aes(label, pseudotime, fill = label)
  ) +
  geom_violin(scale = "width", trim = FALSE, alpha = 0.85, colour = "white") +
  geom_boxplot(width = 0.07, fill = "white", colour = "grey40",
               outlier.size = 0.4, outlier.alpha = 0.3) +
  geom_hline(
    data      = REF_MEDIANS_EFFECTOR,
    aes(yintercept = med_pt),
    linetype  = "dotted", colour = "grey55", linewidth = 0.6
  ) +
  geom_text(
    data        = REF_MEDIANS_EFFECTOR,
    aes(x       = 0.55,
        y       = med_pt,
        label   = sprintf("Ref: %s (%.2f)",
                          gsub("CD4 ", "", as.character(state)), med_pt)),
    inherit.aes = FALSE,
    hjust = 0, size = 2.8, colour = "grey40"
  ) +
  scale_fill_manual(values = STATE_COLORS, guide = "none") +
  scale_y_continuous(breaks = seq(0, 25, 5)) +
  theme_classic() +
  labs(
    x        = "Transferred Azimuth l2 Label",
    y        = "Transferred Pseudotime",
    title    = "Figure 8 — Transferred Pseudotime per Label (All Cell Lines)",
    subtitle = sprintf(
      "Effector axis reference lines: Naive=%.2f | TCM=%.2f | TEM=%.2f | Temra=%.2f\nTreg line omitted — Treg (PT=%.2f) overlaps TCM; Treg-like violin shown as biological confirmation",
      naive_med, tcm_med, tem_med, temra_med, treg_med)
  ) +
  theme(
    plot.title    = element_text(size = 13, face = "bold"),
    plot.subtitle = element_text(size = 9,  colour = "grey40"),
    axis.text.x   = element_text(size = 10, face = "bold")
  )

print(fig8v2)

save_fig(fig8v2, "Fig8v2_Violin_NoTreg", w = 12, h = 6)
  ✓  Fig8v2_Violin_NoTreg  [PNG + PDF]

16 15. Figure 9 — Cross-Table: Label vs Pseudotime Bin (PRIMARY RESULT)


cross_df <- query_df %>%
  filter(!is.na(label), !is.na(pseudotime_bin)) %>%
  mutate(label = factor(label, levels = STATE_ORDER)) %>%
  count(label, pseudotime_bin) %>%
  group_by(label) %>%
  mutate(pct = 100 * n / sum(n)) %>%
  ungroup()

tcm_discordant <- cross_df %>%
  filter(label == "CD4 TCM",
         pseudotime_bin %in% c("TEM-like", "Temra-like")) %>%
  summarise(pct = sum(pct)) %>%
  pull(pct)

fig9 <- ggplot(cross_df, aes(pseudotime_bin, pct, fill = pseudotime_bin)) +
  geom_col(width = 0.7) +
  geom_text(aes(label = sprintf("%.1f%%", pct)),
            vjust = -0.3, size = 3.5, fontface = "bold") +
  scale_fill_manual(values = BIN_COLORS, guide = "none") +
  scale_y_continuous(expand = c(0, 0), limits = c(0, 115)) +
  facet_wrap(~ label, nrow = 1, scales = "free_x") +
  theme_classic() +
  theme(
    strip.text       = element_text(face = "bold", size = 11),
    strip.background = element_rect(fill = "grey95", colour = "grey60"),
    axis.text.x      = element_text(size = 9, face = "bold", angle = 30, hjust = 1),
    plot.title       = element_text(size = 14, face = "bold"),
    plot.subtitle    = element_text(size = 10, colour = "grey40")
  ) +
  labs(
    x        = "Pseudotime Bin",
    y        = "% of Cells with This Label",
    title    = "Figure 9 — Cross-Table: Transferred Label vs Pseudotime Bin  [PRIMARY RESULT]",
    subtitle = sprintf(
      "TCM-labelled cells: %.1f%% discordant (TEM-like or Temra-like)\nTreg panel: 100%% Treg-like by design",
      tcm_discordant)
  )

print(fig9)

save_fig(fig9, "Fig9_CrossTable_Label_vs_Bin_PRIMARY", w = 18, h = 7)
  ✓  Fig9_CrossTable_Label_vs_Bin_PRIMARY  [PNG + PDF]

17 16. Supplementary Figures

17.1 Supp S1 — Per Cell Line Pseudotime Density (Faceted)


suppS1 <- ggplot(
    query_df %>% filter(!is.na(pseudotime), !is.na(cell_line)),
    aes(x = pseudotime, fill = cell_line, colour = cell_line)
  ) +
  geom_density(alpha = 0.35, linewidth = 0.8) +
  geom_vline(data = REF_MEDIANS, aes(xintercept = med_pt),
             colour = "grey45", linetype = "dashed", linewidth = 0.5) +
  scale_fill_brewer(palette = "Set2", guide = "none") +
  scale_colour_brewer(palette = "Set2", guide = "none") +
  scale_x_continuous(limits = c(0, 26), breaks = seq(0, 25, 5)) +
  facet_wrap(~ cell_line, nrow = 2) +
  theme_classic() +
  theme(strip.text    = element_text(face = "bold"),
        plot.title    = element_text(size = 12, face = "bold"),
        plot.subtitle = element_text(size = 9,  colour = "grey40")) +
  labs(x = "Transferred Pseudotime", y = "Density",
       title    = "Supp S1 — Per Cell Line Pseudotime Density",
       subtitle = "Grey dashed lines = data-derived reference state medians")

print(suppS1)

save_fig(suppS1, "SuppS1_Density_PerLine_Facet", w = 12, h = 7)
  ✓  SuppS1_Density_PerLine_Facet  [PNG + PDF]

17.2 Supp S2 — Ridgeplot Standalone


suppS2 <- p6b +
  ggtitle(
    "Supp S2 — Per Cell Line Pseudotime Ridgeplot",
    subtitle = sprintf(
      "Bin boundaries: Naive|TCM=%.2f | TCM|TEM=%.2f | TEM|Temra=%.2f",
      BIN_NAIVE_TCM, BIN_TCM_TEM, BIN_TEM_TEMRA)
  )

print(suppS2)

save_fig(suppS2, "SuppS2_Ridgeplot_PerLine", w = 10, h = 7)
  ✓  SuppS2_Ridgeplot_PerLine  [PNG + PDF]

17.3 Supp S3 — Violin per Label x Cell Line (Faceted)


suppS3 <- ggplot(
    query_df %>%
      filter(!is.na(label), !is.na(cell_line), !is.na(pseudotime)) %>%
      mutate(label = factor(label, levels = STATE_ORDER)),
    aes(cell_line, pseudotime, fill = cell_line)
  ) +
  geom_violin(scale = "width", trim = FALSE, alpha = 0.8, colour = "white") +
  geom_boxplot(width = 0.07, fill = "white", colour = "grey40",
               outlier.size = 0.2, outlier.alpha = 0.3) +
  scale_fill_brewer(palette = "Set2", guide = "none") +
  scale_y_continuous(breaks = seq(0, 25, 5)) +
  facet_wrap(~ label, nrow = 1) +
  theme_classic() +
  theme(
    strip.text       = element_text(face = "bold", size = 10),
    strip.background = element_rect(fill = "grey95", colour = "grey60"),
    axis.text.x      = element_text(size = 9, face = "bold", angle = 30, hjust = 1),
    plot.title       = element_text(size = 12, face = "bold"),
    plot.subtitle    = element_text(size = 9,  colour = "grey40")
  ) +
  labs(x = "Cell Line", y = "Transferred Pseudotime",
       title    = "Supp S3 — Violin: Pseudotime per Label x Cell Line",
       subtitle = "Complements Fig 9 — shows within-state per-line heterogeneity")

print(suppS3)

save_fig(suppS3, "SuppS3_Violin_Label_x_Line", w = 16, h = 10)
  ✓  SuppS3_Violin_Label_x_Line  [PNG + PDF]

17.4 Supp S4 — Pseudotime Bin Bar Chart with Full Percentages


suppS4 <- ggplot(bin_df, aes(cell_line, pct, fill = pseudotime_bin)) +
  geom_col(width = 0.7) +
  geom_text(data = bin_df %>% filter(pct > 2),
            aes(label = sprintf("%.1f%%", pct)),
            position = position_stack(vjust = 0.5),
            size = 3.5, colour = "white", fontface = "bold") +
  scale_fill_manual(values = BIN_COLORS, name = "Pseudotime bin") +
  scale_y_continuous(expand = c(0, 0), limits = c(0, 101)) +
  theme_classic() +
  labs(
    x        = "Cell Line",
    y        = "% Cells",
    title    = "Supp S4 — Pseudotime Bin Composition per Cell Line",
    subtitle = sprintf(
      "Bin boundaries: Naive|TCM=%.2f  |  TCM|TEM=%.2f  |  TEM|Temra=%.2f  |  Treg=label-based",
      BIN_NAIVE_TCM, BIN_TCM_TEM, BIN_TEM_TEMRA)
  ) +
  theme(plot.title    = element_text(size = 12, face = "bold"),
        plot.subtitle = element_text(size = 9,  colour = "grey40"),
        axis.text.x   = element_text(size = 11, face = "bold"))

print(suppS4)

save_fig(suppS4, "SuppS4_Bins_BarChart_Detailed", w = 14, h = 6)
  ✓  SuppS4_Bins_BarChart_Detailed  [PNG + PDF]

18 17. Figure Manifest

All 21 figures — Figures/PNG/*.png (300 dpi) and Figures/PDF/*.pdf
File Description Manuscript
Fig7_FourPanel_Summary_FIXED Four-panel: labels &#124; pseudotime (FIXED) &#124; bins &#124; label bar Fig 1 combined
Fig7A_Transferred_Labels Sezary UMAP — transferred Azimuth l2 label Fig 1A
Fig7B_Transferred_Pseudotime_FIXED Sezary UMAP — transferred pseudotime, FIXED 0-25 colour scale Fig 1B (FIXED)
Fig7C_Pseudotime_Bins_UMAP Sezary UMAP — pseudotime bin colour Fig 1C
Fig7D_Label_BarChart_per_Line Transferred label proportions per cell line bar chart Fig 1D
Fig1_Pseudotime_Violin_per_Line Pseudotime violin per cell line with data-derived reference medians Supp
Fig2_UMAP_Pseudotime_and_Label UMAP pseudotime gradient + label identity side by side Fig 2
Fig3_Labels_PerLine_UMAP Label bar chart + per cell line UMAP facets Fig 3
Fig4_PerLine_Facet_UMAP Per cell line facet UMAP — key inter-line heterogeneity figure Fig 4 (KEY)
Fig5_Label_vs_Pseudotime_Bins Dual bar chart: labels (top) and pseudotime bins (bottom) Fig 5
Fig5A_Label_Transfer_BarChart Label transfer proportions bar chart only
Fig5B_Pseudotime_Bins_BarChart Pseudotime bin proportions bar chart only Fig 5B
Fig6_Density_and_Ridgeplot Overall pseudotime density + per-line ridgeplot Supp
Fig6A_Density_Overall Overall pseudotime density with data-derived reference median lines Supp S3
Fig6B_Ridgeplot_PerLine Per cell line pseudotime ridgeplot with bin boundaries Supp S4
Fig8_Violin_Pseudotime_per_Label Violin: transferred pseudotime per label — data-derived annotations Supp S5
Fig9_CrossTable_Label_vs_Bin_PRIMARY Cross-table: label vs pseudotime bin — PRIMARY RESULT Fig 2 (PRIMARY)
SuppS1_Density_PerLine_Facet Per cell line pseudotime density faceted Supp S1
SuppS2_Ridgeplot_PerLine Per cell line ridgeplot standalone Supp S2
SuppS3_Violin_Label_x_Line Violin: pseudotime per label x cell line faceted Supp S6
SuppS4_Bins_BarChart_Detailed Pseudotime bin bar chart with full percentages labelled Supp

19 18. Session Info

sessionInfo()
R version 4.5.2 (2025-10-31)
Platform: x86_64-redhat-linux-gnu
Running under: Rocky Linux 9.7 (Blue Onyx)

Matrix products: default
BLAS/LAPACK: FlexiBLAS OPENBLAS-OPENMP;  LAPACK version 3.9.0

locale:
 [1] LC_CTYPE=C.UTF-8       LC_NUMERIC=C           LC_TIME=C.UTF-8        LC_COLLATE=C.UTF-8     LC_MONETARY=C.UTF-8    LC_MESSAGES=C.UTF-8   
 [7] LC_PAPER=C.UTF-8       LC_NAME=C              LC_ADDRESS=C           LC_TELEPHONE=C         LC_MEASUREMENT=C.UTF-8 LC_IDENTIFICATION=C   

time zone: Europe/Paris
tzcode source: system (glibc)

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
 [1] kableExtra_1.4.0   knitr_1.51         ggridges_0.5.7     RColorBrewer_1.1-3 scales_1.4.0       patchwork_1.3.2    forcats_1.0.1     
 [8] tidyr_1.3.2        dplyr_1.2.0        ggplot2_4.0.2      Seurat_5.4.0       SeuratObject_5.3.0 sp_2.2-1          

loaded via a namespace (and not attached):
  [1] rstudioapi_0.18.0      jsonlite_2.0.0         magrittr_2.0.4         spatstat.utils_3.2-1   farver_2.1.2           rmarkdown_2.30        
  [7] ragg_1.5.0             vctrs_0.7.1            ROCR_1.0-12            spatstat.explore_3.7-0 htmltools_0.5.9        sass_0.4.10           
 [13] sctransform_0.4.3      parallelly_1.46.1      KernSmooth_2.23-26     bslib_0.10.0           htmlwidgets_1.6.4      ica_1.0-3             
 [19] plyr_1.8.9             plotly_4.12.0          zoo_1.8-15             cachem_1.1.0           igraph_2.2.2           mime_0.13             
 [25] lifecycle_1.0.5        pkgconfig_2.0.3        Matrix_1.7-4           R6_2.6.1               fastmap_1.2.0          fitdistrplus_1.2-6    
 [31] future_1.69.0          shiny_1.12.1           digest_0.6.39          S4Vectors_0.48.0       tensor_1.5.1           RSpectra_0.16-2       
 [37] irlba_2.3.7            textshaping_1.0.4      labeling_0.4.3         progressr_0.18.0       spatstat.sparse_3.1-0  httr_1.4.8            
 [43] polyclip_1.10-7        abind_1.4-8            compiler_4.5.2         withr_3.0.2            S7_0.2.1               fastDummies_1.7.5     
 [49] MASS_7.3-65            tools_4.5.2            lmtest_0.9-40          otel_0.2.0             httpuv_1.6.16          future.apply_1.20.1   
 [55] goftest_1.2-3          glue_1.8.0             nlme_3.1-168           promises_1.5.0         grid_4.5.2             Rtsne_0.17            
 [61] cluster_2.1.8.2        reshape2_1.4.5         generics_0.1.4         gtable_0.3.6           spatstat.data_3.1-9    data.table_1.18.2.1   
 [67] xml2_1.5.2             XVector_0.50.0         BiocGenerics_0.56.0    spatstat.geom_3.7-0    RcppAnnoy_0.0.23       ggrepel_0.9.6         
 [73] RANN_2.6.2             pillar_1.11.1          stringr_1.6.0          spam_2.11-3            RcppHNSW_0.6.0         later_1.4.6           
 [79] splines_4.5.2          lattice_0.22-9         survival_3.8-6         deldir_2.0-4           tidyselect_1.2.1       miniUI_0.1.2          
 [85] pbapply_1.7-4          gridExtra_2.3          IRanges_2.44.0         svglite_2.2.2          scattermore_1.2        stats4_4.5.2          
 [91] xfun_0.56              Biobase_2.70.0         matrixStats_1.5.0      stringi_1.8.7          lazyeval_0.2.2         yaml_2.3.12           
 [97] evaluate_1.0.5         codetools_0.2-20       tibble_3.3.1           cli_3.6.5              uwot_0.2.4             xtable_1.8-4          
[103] reticulate_1.45.0      systemfonts_1.3.1      jquerylib_0.1.4        dichromat_2.0-0.1      Rcpp_1.1.1             globals_0.19.0        
[109] spatstat.random_3.4-4  png_0.1-8              spatstat.univar_3.1-6  parallel_4.5.2         dotCall64_1.2          listenv_0.10.0        
[115] viridisLite_0.4.3      purrr_1.2.1            rlang_1.1.7            cowplot_1.2.0         
LS0tCnRpdGxlOiAiU8OpemFyeSBTeW5kcm9tZSDigJQgUHNldWRvdGltZSBUcmFqZWN0b3J5IEZpZ3VyZXMiCnN1YnRpdGxlOiAiQ0Q0KyBUIENlbGwgUmVmZXJlbmNlIFByb2plY3Rpb24gfCBTdGF0ZS1BbmNob3JlZCBQc2V1ZG90aW1lIEJpbnMgfCBQTkcgKyBQREYgb3V0cHV0IgphdXRob3I6ICJOYXNpciBNYWhtb29kIEFiYmFzaSIKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6CiAgICAgIGNvbGxhcHNlZDogZmFsc2UKICAgIHRoZW1lOiBqb3VybmFsCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldCgKICBlY2hvICAgICAgPSBUUlVFLAogIG1lc3NhZ2UgICA9IEZBTFNFLAogIHdhcm5pbmcgICA9IEZBTFNFLAogIGZpZy5hbGlnbiA9ICJjZW50ZXIiLAogIGRwaSAgICAgICA9IDE1MAopCmBgYAoKLS0tCgojIE92ZXJ2aWV3CgpUaGlzIHNjcmlwdCByZWdlbmVyYXRlcyAqKmFsbCBtYW51c2NyaXB0IGZpZ3VyZXMqKiBmcm9tIHRoZSBzYXZlZCBwcm9qZWN0aW9uIG9iamVjdC4KVGhlIHJlZmVyZW5jZSBvYmplY3QgaXMgbG9hZGVkICoqc29sZWx5KiogdG8gZXh0cmFjdCBVTUFQIGNvb3JkaW5hdGVzIGZvciB0aGUgZ3JleSBiYWNrZ3JvdW5kLgoKIyMgT2JqZWN0cyByZXF1aXJlZAoKfCBPYmplY3QgfCBQYXRoIHwgUm9sZSB8CnwtLS0tLS0tLXwtLS0tLS18LS0tLS0tfAp8IGByZWZlcmVuY2VfaW50ZWdyYXRlZGAgfCBgLi4vLi4vMS1GaW5hbF9DdXN0b21fTVNUX01vbm9jbGUzX1RyYWplY3RvcnlfYW5kX21hcHBpbmcvMS1DdXN0b21fTVNUL09iamVjdHMvY2Q0X3JlZl9kdWFsX3RyYWplY3RvcnkucmRzYCB8ICoqVU1BUCBiYWNrZ3JvdW5kIG9ubHkqKiDigJQgZ3JleSByZWZlcmVuY2UgZG90cyB8CnwgYG1hcHBlZF9NYWxpZ25hbnRDRDRUYCB8IGByZXN1bHRzL01hbGlnbmFudENENFRfTW9ub2NsZTNfcHJvamVjdGlvbi9NYWxpZ25hbnRDRDRUX21hcHBlZF9tb25vY2xlM19wc2V1ZG90aW1lX25vUHJvbGlmX3JlZi5SZHNgIHwgKipBbGwgYmlvbG9neSoqIOKAlCBwc2V1ZG90aW1lLCBsYWJlbHMsIGJpbnMsIGNvbG91ciBzY2FsZXMgfAoKIyMgS2V5IGRlc2lnbiBkZWNpc2lvbnMKCnwgSXRlbSB8IFNvdXJjZSB8CnwtLS0tLS18LS0tLS0tLS18CnwgUHNldWRvdGltZSBjb2xvdXItc2NhbGUgbGltaXRzIChgUFRfTElNSVRTYCkgfCBgbWFwcGVkX01hbGlnbmFudENENFQkcHJlZGljdGVkLnBzZXVkb3RpbWVgIHwKfCBCaW4gYm91bmRhcmllcyB8IE1pZHBvaW50cyBiZXR3ZWVuICoqYWN0dWFsKiogcmVmZXJlbmNlIHN0YXRlIG1lZGlhbnMg4oCUIGNvbXB1dGVkIGF0IHJ1bnRpbWUgZnJvbSBgcmVmX2JnYCB8CnwgU3RhdGUgbWVkaWFucyAoYFJFRl9NRURJQU5TYCkgfCBDb21wdXRlZCBhdCBydW50aW1lIGZyb20gYHJlZl9iZ2Ag4oCUICoqbmV2ZXIgaGFyZC1jb2RlZCoqIHwKfCBBbGwgYmFyIGNoYXJ0cywgdmlvbGlucywgZGVuc2l0eSBwbG90cyB8IGBtYXBwZWRfTWFsaWduYW50Q0Q0VGAgb25seSB8CgojIyBQYW5lbCBCIGZpeAoKUHJldmlvdXNseSBjYWxsZWQgYEZlYXR1cmVQbG90KHJlZmVyZW5jZV9pbnRlZ3JhdGVkLCAuLi4pYCB3aXRob3V0IGBsaW1pdHNgLApwcm9kdWNpbmcgYSBzcHVyaW91cyAxLjDigJMyLjAgY29sb3VyIHNjYWxlLiBGaXhlZDogUGFuZWwgQiBwbG90cyBTw6l6YXJ5IGNlbGxzCmNvbG91cmVkIGJ5IGBwcmVkaWN0ZWQucHNldWRvdGltZWAgd2l0aCBgbGltaXRzID0gUFRfTElNSVRTYCAoY29ycmVjdCAw4oCTMjUgc2NhbGUpLgoKIyMgQmluIGRlc2lnbgoKQmlucyBhcmUgYW5jaG9yZWQgdG8gKiphY3R1YWwgcmVmZXJlbmNlIHN0YXRlIG1lZGlhbnMqKiBjb21wdXRlZCBmcm9tIGByZWZfYmdgLgpCb3VuZGFyaWVzID0gbWlkcG9pbnQgYmV0d2VlbiBhZGphY2VudCBzdGF0ZSBtZWRpYW5zIChiaW9sb2d5LXByaW5jaXBsZWQsIG5vdCBhcmJpdHJhcnkpLgpUcmVnLWxpa2UgYmluIGlzIGFzc2lnbmVkIGJ5IEF6aW11dGggbGFiZWwgKFRyZWcgaXMgYSBicmFuY2gsIG5vdCBhIGxpbmVhciBwb3NpdGlvbikuCgotLS0KCiMgMS4gU2V0dXAgJiBMaWJyYXJpZXMKCmBgYHtyIGxpYnJhcmllc30Kc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKHsKICBsaWJyYXJ5KFNldXJhdCkKICBsaWJyYXJ5KGdncGxvdDIpCiAgbGlicmFyeShkcGx5cikKICBsaWJyYXJ5KHRpZHlyKQogIGxpYnJhcnkoZm9yY2F0cykKICBsaWJyYXJ5KHBhdGNod29yaykKICBsaWJyYXJ5KHNjYWxlcykKICBsaWJyYXJ5KFJDb2xvckJyZXdlcikKICBsaWJyYXJ5KGdncmlkZ2VzKQogIGxpYnJhcnkoa25pdHIpCiAgbGlicmFyeShrYWJsZUV4dHJhKQp9KQoKZGlyLmNyZWF0ZSgiRmlndXJlcy9QTkciLCByZWN1cnNpdmUgPSBUUlVFLCBzaG93V2FybmluZ3MgPSBGQUxTRSkKZGlyLmNyZWF0ZSgiRmlndXJlcy9QREYiLCByZWN1cnNpdmUgPSBUUlVFLCBzaG93V2FybmluZ3MgPSBGQUxTRSkKCnNhdmVfZmlnIDwtIGZ1bmN0aW9uKHAsIG5hbWUsIHcgPSAxMCwgaCA9IDgpIHsKICBnZ3NhdmUoZmlsZS5wYXRoKCJGaWd1cmVzL1BORyIsIHBhc3RlMChuYW1lLCAiLnBuZyIpKSwKICAgICAgICAgcCwgd2lkdGggPSB3LCBoZWlnaHQgPSBoLCBkcGkgPSAzMDAsIGJnID0gIndoaXRlIikKICBnZ3NhdmUoZmlsZS5wYXRoKCJGaWd1cmVzL1BERiIsIHBhc3RlMChuYW1lLCAiLnBkZiIpKSwKICAgICAgICAgcCwgd2lkdGggPSB3LCBoZWlnaHQgPSBoLCB1c2VEaW5nYmF0cyA9IEZBTFNFKQogIGludmlzaWJsZShjYXQoc3ByaW50ZigiICBcdTI3MTMgICVzICBbUE5HICsgUERGXVxuIiwgbmFtZSkpKQp9CgpTVEFURV9DT0xPUlMgPC0gYygKICAiQ0Q0IE5haXZlIiAgICAgPSAiIzQ0NzJDNCIsCiAgIkNENCBUQ00iICAgICAgID0gIiM3MEFENDciLAogICJDRDQgVEVNIiAgICAgICA9ICIjRUQ3RDMxIiwKICAiQ0Q0IFRlbXJhL0NUTCIgPSAiI0MwMDAwMCIsCiAgIlRyZWciICAgICAgICAgID0gIiM3MDMwQTAiCikKCkJJTl9DT0xPUlMgPC0gYygKICAiTmFpdmUtbGlrZSIgID0gIiM0NDcyQzQiLAogICJUQ00tbGlrZSIgICAgPSAiIzcwQUQ0NyIsCiAgIlRFTS1saWtlIiAgICA9ICIjRUQ3RDMxIiwKICAiVGVtcmEtbGlrZSIgID0gIiNDMDAwMDAiLAogICJUcmVnLWxpa2UiICAgPSAiIzcwMzBBMCIKKQoKU1RBVEVfT1JERVIgPC0gYygiQ0Q0IE5haXZlIiwgIkNENCBUQ00iLCAiQ0Q0IFRFTSIsICJDRDQgVGVtcmEvQ1RMIiwgIlRyZWciKQpCSU5fT1JERVIgICA8LSBjKCJOYWl2ZS1saWtlIiwgIlRDTS1saWtlIiwgIlRFTS1saWtlIiwgIlRlbXJhLWxpa2UiLCAiVHJlZy1saWtlIikKTElORV9PUkRFUiAgPC0gcGFzdGUwKCJMIiwgMTo3KQoKY2F0KCJTZXR1cCBjb21wbGV0ZS5cbiIpCmBgYAoKLS0tCgojIDIuIExvYWQgJiBWYWxpZGF0ZSBPYmplY3RzCgpgYGB7ciBsb2FkLW9iamVjdHN9CnJlZl9wYXRoIDwtIHBhc3RlMCgKICAiLi4vLi4vMS1GaW5hbF9DdXN0b21fTVNUX01vbm9jbGUzX1RyYWplY3RvcnlfYW5kX21hcHBpbmcvIiwKICAiMS1DdXN0b21fTVNUL09iamVjdHMvY2Q0X3JlZl9kdWFsX3RyYWplY3RvcnkucmRzIgopCnJlZmVyZW5jZV9pbnRlZ3JhdGVkIDwtIHJlYWRSRFMocmVmX3BhdGgpCmNhdCgiUmVmZXJlbmNlIGxvYWRlZDoiLCBuY29sKHJlZmVyZW5jZV9pbnRlZ3JhdGVkKSwgImNlbGxzXG4iKQoKc3RvcGlmbm90KAogICJ1bWFwIHJlZHVjdGlvbiBtaXNzaW5nIGZyb20gcmVmZXJlbmNlIiA9CiAgICAidW1hcCIgJWluJSBuYW1lcyhyZWZlcmVuY2VfaW50ZWdyYXRlZEByZWR1Y3Rpb25zKQopCgpvYmpfcGF0aCA8LSBwYXN0ZTAoCiAgIi4uL3Jlc3VsdHMvTWFsaWduYW50Q0Q0VF9Nb25vY2xlM19wcm9qZWN0aW9uLyIsCiAgIk1hbGlnbmFudENENFRfbWFwcGVkX21vbm9jbGUzX3BzZXVkb3RpbWVfbm9Qcm9saWZfcmVmLlJkcyIKKQptYXBwZWRfTWFsaWduYW50Q0Q0VCA8LSByZWFkUkRTKG9ial9wYXRoKQpjYXQoIlF1ZXJ5IGxvYWRlZDogICAgIiwgbmNvbChtYXBwZWRfTWFsaWduYW50Q0Q0VCksICJjZWxsc1xuIikKCnN0b3BpZm5vdCgKICAicmVmLnVtYXAgcmVkdWN0aW9uIG1pc3Npbmcg4oCUIHJlLXJ1biBNYXBRdWVyeSIgPQogICAgInJlZi51bWFwIiAlaW4lIG5hbWVzKG1hcHBlZF9NYWxpZ25hbnRDRDRUQHJlZHVjdGlvbnMpLAogICJwcmVkaWN0ZWQucHNldWRvdGltZSBjb2x1bW4gbWlzc2luZyIgPQogICAgInByZWRpY3RlZC5wc2V1ZG90aW1lIiAlaW4lIGNvbG5hbWVzKG1hcHBlZF9NYWxpZ25hbnRDRDRUQG1ldGEuZGF0YSksCiAgInByZWRpY3RlZC5wcmVkaWN0ZWQuY2VsbHR5cGUubDIgY29sdW1uIG1pc3NpbmciID0KICAgICJwcmVkaWN0ZWQucHJlZGljdGVkLmNlbGx0eXBlLmwyIiAlaW4lIGNvbG5hbWVzKG1hcHBlZF9NYWxpZ25hbnRDRDRUQG1ldGEuZGF0YSkKKQoKaWYgKCEiY2VsbF9saW5lIiAlaW4lIGNvbG5hbWVzKG1hcHBlZF9NYWxpZ25hbnRDRDRUQG1ldGEuZGF0YSkpIHsKICBpZiAoIm9yaWcuaWRlbnQiICVpbiUgY29sbmFtZXMobWFwcGVkX01hbGlnbmFudENENFRAbWV0YS5kYXRhKSkgewogICAgbWFwcGVkX01hbGlnbmFudENENFQkY2VsbF9saW5lIDwtIG1hcHBlZF9NYWxpZ25hbnRDRDRUJG9yaWcuaWRlbnQKICAgIGNhdCgiTk9URTogdXNpbmcgb3JpZy5pZGVudCBhcyBjZWxsX2xpbmVcbiIpCiAgfSBlbHNlIHsKICAgIHN0b3AoIkNhbm5vdCBmaW5kIGNlbGxfbGluZSBvciBvcmlnLmlkZW50IGluIG1ldGFkYXRhIikKICB9Cn0KCmNhdCgiXG5wcmVkaWN0ZWQucHNldWRvdGltZSBzdW1tYXJ5IChleHBlY3QgTWlufjAsIE1heH4yNSk6XG4iKQpwcmludChzdW1tYXJ5KG1hcHBlZF9NYWxpZ25hbnRDRDRUJHByZWRpY3RlZC5wc2V1ZG90aW1lKSkKY2F0KCJcblRyYW5zZmVycmVkIGxhYmVsIGRpc3RyaWJ1dGlvbjpcbiIpCnByaW50KHNvcnQodGFibGUobWFwcGVkX01hbGlnbmFudENENFQkcHJlZGljdGVkLnByZWRpY3RlZC5jZWxsdHlwZS5sMiwKICAgICAgICAgICAgICAgICB1c2VOQSA9ICJpZmFueSIpLCBkZWNyZWFzaW5nID0gVFJVRSkpCmNhdCgiXG5DZWxsIGxpbmUgc2l6ZXM6XG4iKQpwcmludChzb3J0KHRhYmxlKG1hcHBlZF9NYWxpZ25hbnRDRDRUJGNlbGxfbGluZSwgdXNlTkEgPSAiaWZhbnkiKSkpCmBgYAoKLS0tCgojIDMuIEJ1aWxkIFJlZmVyZW5jZSBCYWNrZ3JvdW5kIChgcmVmX2JnYCkKCmBgYHtyIGJ1aWxkLXJlZi1iZ30KIyByZWZfYmcgc2VydmVzIHR3byBwdXJwb3NlczoKIyAgIDEuIEdyZXkgYmFja2dyb3VuZCBkb3RzIGluIGFsbCBwcm9qZWN0aW9uIFVNQVBzCiMgICAyLiBTb3VyY2UgZm9yIGNvbXB1dGluZyBhY3R1YWwgcmVmZXJlbmNlIHN0YXRlIG1lZGlhbnMgKFJFRl9NRURJQU5TKQpyZWZfYmcgPC0gZGF0YS5mcmFtZSgKICBFbWJlZGRpbmdzKHJlZmVyZW5jZV9pbnRlZ3JhdGVkLCAidW1hcCIpLAogIHN0YXRlICAgICAgPSByZWZlcmVuY2VfaW50ZWdyYXRlZCRwcmVkaWN0ZWQuY2VsbHR5cGUubDIsCiAgcHNldWRvdGltZSA9IHJlZmVyZW5jZV9pbnRlZ3JhdGVkJG1vbm9jbGUzX3BzZXVkb3RpbWUsCiAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFCikKY29sbmFtZXMocmVmX2JnKVsxOjJdIDwtIGMoIlVNQVBfMSIsICJVTUFQXzIiKQpyZWZfYmckc3RhdGUgPC0gZmFjdG9yKHJlZl9iZyRzdGF0ZSwgbGV2ZWxzID0gU1RBVEVfT1JERVIpCgpjYXQoIlJlZmVyZW5jZSBtb25vY2xlM19wc2V1ZG90aW1lIChzaG91bGQgYmUgMC0yNSk6XG4iKQpwcmludChzdW1tYXJ5KHJlZl9iZyRwc2V1ZG90aW1lKSkKCnJlZl9iZ19wbG90IDwtIHJlZl9iZ1tzYW1wbGUobnJvdyhyZWZfYmcpKSwgXQpjYXQoc3ByaW50ZigiXG5yZWZfYmcgcmVhZHk6ICVkIHJlZmVyZW5jZSBjZWxsc1xuIiwgbnJvdyhyZWZfYmcpKSkKYGBgCgotLS0KCiMgNC4gQ29tcHV0ZSBSZWZlcmVuY2UgU3RhdGUgTWVkaWFucyAmIEJpbiBCb3VuZGFyaWVzCgpgYGB7ciBjb21wdXRlLW1lZGlhbnMtYmluc30KIyDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZAKIyBSRUZfTUVESUFOUyBjb21wdXRlZCBhdCBydW50aW1lIGZyb20gYWN0dWFsIHJlZl9iZyDigJQgTk9UIGhhcmQtY29kZWQuCiMKIyBEaWFnbm9zdGljIGNvbmZpcm1lZCB0aGVzZSBhY3R1YWwgdmFsdWVzOgojICAgQ0Q0IE5haXZlICAgICBtZWRpYW4gPSAgMy4yOTMKIyAgIENENCBUQ00gICAgICAgbWVkaWFuID0gIDkuMzc3ICAod2FzIGhhcmQtY29kZWQgYXMgMTMuMzYg4oCUIFdST05HKQojICAgQ0Q0IFRFTSAgICAgICBtZWRpYW4gPSAyNC44NDAKIyAgIENENCBUZW1yYS9DVEwgbWVkaWFuID0gMjQuODQwICAob25seSAxMCBjZWxscykKIyAgIFRyZWcgICAgICAgICAgbWVkaWFuID0gMTEuMTUwICAod2FzIGhhcmQtY29kZWQgYXMgMTguNzUg4oCUIGFib3ZlIFRyZWcgbWF4IG9mIDE3LjQwLCBXUk9ORykKIwojIEJpbiBib3VuZGFyaWVzIGRlcml2ZWQgYXMgbWlkcG9pbnRzIGJldHdlZW4gYWRqYWNlbnQgc3RhdGUgbWVkaWFucy4KIyDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZAKCnJlZl9zdGF0ZV9tZWRpYW5zIDwtIHJlZl9iZyAlPiUKICBmaWx0ZXIoIWlzLm5hKHBzZXVkb3RpbWUpLCAhaXMubmEoc3RhdGUpKSAlPiUKICBncm91cF9ieShzdGF0ZSkgJT4lCiAgc3VtbWFyaXNlKAogICAgbiAgICAgID0gbigpLAogICAgbWVkX3B0ID0gcm91bmQobWVkaWFuKHBzZXVkb3RpbWUsIG5hLnJtID0gVFJVRSksIDMpLAogICAgLmdyb3VwcyA9ICJkcm9wIgogICkgJT4lCiAgYXJyYW5nZShtZWRfcHQpCgpjYXQoIj09PSBSZWZlcmVuY2Ugc3RhdGUgbWVkaWFucyAoZGF0YS1kZXJpdmVkKSA9PT1cbiIpCnByaW50KHJlZl9zdGF0ZV9tZWRpYW5zKQoKZ2V0X21lZCA8LSBmdW5jdGlvbihzKSB7CiAgdiA8LSByZWZfc3RhdGVfbWVkaWFucyRtZWRfcHRbcmVmX3N0YXRlX21lZGlhbnMkc3RhdGUgPT0gc10KICBpZiAobGVuZ3RoKHYpID09IDApIHN0b3AocGFzdGUoIlN0YXRlIG5vdCBmb3VuZCBpbiByZWZfYmc6IiwgcykpCiAgdgp9CgpuYWl2ZV9tZWQgIDwtIGdldF9tZWQoIkNENCBOYWl2ZSIpCnRjbV9tZWQgICAgPC0gZ2V0X21lZCgiQ0Q0IFRDTSIpCnRlbV9tZWQgICAgPC0gZ2V0X21lZCgiQ0Q0IFRFTSIpCnRlbXJhX21lZCAgPC0gZ2V0X21lZCgiQ0Q0IFRlbXJhL0NUTCIpCnRyZWdfbWVkICAgPC0gZ2V0X21lZCgiVHJlZyIpCgpCSU5fTkFJVkVfVENNICA8LSByb3VuZCgobmFpdmVfbWVkICsgdGNtX21lZCkgIC8gMiwgMykKQklOX1RDTV9URU0gICAgPC0gcm91bmQoKHRjbV9tZWQgICArIHRlbV9tZWQpICAgLyAyLCAzKQpCSU5fVEVNX1RFTVJBICA8LSByb3VuZCgodGVtX21lZCAgICsgdGVtcmFfbWVkKSAvIDIsIDMpCgpjYXQoIlxuPT09IEJpbiBib3VuZGFyaWVzIChtaWRwb2ludHMgYmV0d2VlbiBhZGphY2VudCBzdGF0ZSBtZWRpYW5zKSA9PT1cbiIpCmNhdChzcHJpbnRmKCIgIE5haXZlIHwgVENNICAgOiAlLjNmXG4iLCBCSU5fTkFJVkVfVENNKSkKY2F0KHNwcmludGYoIiAgVENNICAgfCBURU0gICA6ICUuM2ZcbiIsIEJJTl9UQ01fVEVNKSkKY2F0KHNwcmludGYoIiAgVEVNICAgfCBUZW1yYSA6ICUuM2ZcbiIsIEJJTl9URU1fVEVNUkEpKQpjYXQoc3ByaW50ZigiICBUcmVnICAgICAgICAgICA6IGxhYmVsLWJhc2VkIChicmFuY2ggbG9naWMpXG4iKSkKCmlmIChhYnModGVtX21lZCAtIHRlbXJhX21lZCkgPCAwLjUpIHsKICBjYXQoIlxuTk9URTogVEVNIGFuZCBUZW1yYSBtZWRpYW5zIGFyZSB2ZXJ5IGNsb3NlIChuPTEwIFRlbXJhIGNlbGxzIGluIHJlZmVyZW5jZSkuXG4iKQogIGNhdCgiICAgICAgVGVtcmEtbGlrZSBiaW4gd2lsbCBiZSBuZWdsaWdpYmxlIOKAlCBiaW9sb2dpY2FsbHkgZXhwZWN0ZWQuXG4iKQp9CgpSRUZfTUVESUFOUyA8LSBkYXRhLmZyYW1lKAogIHN0YXRlICA9IGZhY3RvcihTVEFURV9PUkRFUiwgbGV2ZWxzID0gU1RBVEVfT1JERVIpLAogIG1lZF9wdCA9IGMobmFpdmVfbWVkLCB0Y21fbWVkLCB0ZW1fbWVkLCB0ZW1yYV9tZWQsIHRyZWdfbWVkKQopCgpjYXQoIlxuPT09IFJFRl9NRURJQU5TICh1c2VkIGZvciByZWZlcmVuY2UgbGluZXMgaW4gYWxsIGZpZ3VyZXMpID09PVxuIikKcHJpbnQoUkVGX01FRElBTlMpCmBgYAoKYGBge3J9CiMg4pSA4pSAIFJFRl9NRURJQU5TX0VGRkVDVE9SOiBUcmVnIGV4Y2x1ZGVkIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgAojIFRyZWcgKG1lZGlhbiBQVD0xMS4xNSkgYW5kIFRDTSAobWVkaWFuIFBUPTkuMzgpIG92ZXJsYXAgZW50aXJlbHkgaW4KIyBwc2V1ZG90aW1lIHNwYWNlIOKAlCBib3RoIHN0YXRlcyBzaGFyZSB0aGUgTmFpdmXihpJUQ00gZ3JhcGggc2VnbWVudC4KIyBTaG93aW5nIGEgVHJlZyBsaW5lIGF0IFBUPTExLjE1IGltcGxpZXMgYSBwc2V1ZG90aW1lIGJvdW5kYXJ5IHRoYXQgZG9lcwojIG5vdCBleGlzdCBhbmQgbWlzbGVhZHMgaW50ZXJwcmV0YXRpb24uIFRyZWctbGlrZSBjZWxscyBhcmUgYXNzaWduZWQgYnkKIyB0cmFuc2ZlcnJlZCBBemltdXRoIGxhYmVsIChLTk4pLCBub3QgYnkgcHNldWRvdGltZSBwb3NpdGlvbi4KUkVGX01FRElBTlNfRUZGRUNUT1IgPC0gUkVGX01FRElBTlMgJT4lCiAgZmlsdGVyKHN0YXRlICE9ICJUcmVnIikKCkxJTkVUWVBFX0VGRkVDVE9SIDwtIGMoCiAgIkNENCBOYWl2ZSIgICAgID0gImRvdHRlZCIsCiAgIkNENCBUQ00iICAgICAgID0gImRhc2hlZCIsCiAgIkNENCBURU0iICAgICAgID0gImxvbmdkYXNoIiwKICAiQ0Q0IFRlbXJhL0NUTCIgPSAic29saWQiCikKCmNhdCgiPT09IFJFRl9NRURJQU5TX0VGRkVDVE9SIChUcmVnIGV4Y2x1ZGVkKSA9PT1cbiIpCnByaW50KFJFRl9NRURJQU5TX0VGRkVDVE9SKQpgYGAKCgojIDUuIEFzc2lnbiBQc2V1ZG90aW1lIEJpbnMKCmBgYHtyIGFzc2lnbi1iaW5zfQphc3NpZ25fYmluIDwtIGZ1bmN0aW9uKHB0LCBsYmwpIHsKICBpZiAoaXNUUlVFKGdyZXBsKCJUcmVnIiwgbGJsLCBpZ25vcmUuY2FzZSA9IFRSVUUpKSkgcmV0dXJuKCJUcmVnLWxpa2UiKQogIGlmIChpcy5uYShwdCkpICAgICAgICAgICAgIHJldHVybihOQV9jaGFyYWN0ZXJfKQogIGlmIChwdCA8PSBCSU5fTkFJVkVfVENNKSAgIHJldHVybigiTmFpdmUtbGlrZSIpCiAgaWYgKHB0IDw9IEJJTl9UQ01fVEVNKSAgICAgcmV0dXJuKCJUQ00tbGlrZSIpCiAgaWYgKHB0IDw9IEJJTl9URU1fVEVNUkEpICAgcmV0dXJuKCJURU0tbGlrZSIpCiAgcmV0dXJuKCJUZW1yYS1saWtlIikKfQoKbWFwcGVkX01hbGlnbmFudENENFQkcHNldWRvdGltZV9iaW4gPC0gZmFjdG9yKAogIG1hcHBseShhc3NpZ25fYmluLAogICAgICAgICBwdCAgPSBtYXBwZWRfTWFsaWduYW50Q0Q0VCRwcmVkaWN0ZWQucHNldWRvdGltZSwKICAgICAgICAgbGJsID0gbWFwcGVkX01hbGlnbmFudENENFQkcHJlZGljdGVkLnByZWRpY3RlZC5jZWxsdHlwZS5sMiksCiAgbGV2ZWxzID0gQklOX09SREVSCikKCmNhdCgiUHNldWRvdGltZSBiaW4gZGlzdHJpYnV0aW9uIChhbGwgY2VsbHMpOlxuIikKcHJpbnQodGFibGUobWFwcGVkX01hbGlnbmFudENENFQkcHNldWRvdGltZV9iaW4sIHVzZU5BID0gImlmYW55IikpCmNhdCgiXG5Qc2V1ZG90aW1lIGJpbiBwZXIgY2VsbCBsaW5lOlxuIikKcHJpbnQodGFibGUobWFwcGVkX01hbGlnbmFudENENFQkY2VsbF9saW5lLAogICAgICAgICAgICBtYXBwZWRfTWFsaWduYW50Q0Q0VCRwc2V1ZG90aW1lX2JpbiwgdXNlTkEgPSAiaWZhbnkiKSkKYGBgCgotLS0KCiMgNi4gQnVpbGQgU2hhcmVkIERhdGEgRnJhbWVzCgpgYGB7ciBidWlsZC1kYXRhZnJhbWVzfQpxdWVyeV9kZiA8LSBkYXRhLmZyYW1lKAogIEVtYmVkZGluZ3MobWFwcGVkX01hbGlnbmFudENENFQsICJyZWYudW1hcCIpLAogIHBzZXVkb3RpbWUgICAgID0gbWFwcGVkX01hbGlnbmFudENENFQkcHJlZGljdGVkLnBzZXVkb3RpbWUsCiAgY2VsbF9saW5lICAgICAgPSBmYWN0b3IobWFwcGVkX01hbGlnbmFudENENFQkY2VsbF9saW5lLCBsZXZlbHMgPSBMSU5FX09SREVSKSwKICBsYWJlbCAgICAgICAgICA9IG1hcHBlZF9NYWxpZ25hbnRDRDRUJHByZWRpY3RlZC5wcmVkaWN0ZWQuY2VsbHR5cGUubDIsCiAgcHNldWRvdGltZV9iaW4gPSBtYXBwZWRfTWFsaWduYW50Q0Q0VCRwc2V1ZG90aW1lX2JpbiwKICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UKKQpjb2xuYW1lcyhxdWVyeV9kZilbMToyXSA8LSBjKCJVTUFQXzEiLCAiVU1BUF8yIikKClBUX0xJTUlUUyA8LSByYW5nZShxdWVyeV9kZiRwc2V1ZG90aW1lLCBuYS5ybSA9IFRSVUUpCmNhdChzcHJpbnRmKCJQc2V1ZG90aW1lIGNvbG91ciBsaW1pdHM6ICUuM2YgdG8gJS4zZlxuIiwgUFRfTElNSVRTWzFdLCBQVF9MSU1JVFNbMl0pKQoKbGFiZWxfZGYgPC0gcXVlcnlfZGYgJT4lCiAgZmlsdGVyKCFpcy5uYShsYWJlbCksICFpcy5uYShjZWxsX2xpbmUpKSAlPiUKICBjb3VudChjZWxsX2xpbmUsIGxhYmVsKSAlPiUKICBncm91cF9ieShjZWxsX2xpbmUpICU+JQogIG11dGF0ZShwY3QgPSAxMDAgKiBuIC8gc3VtKG4pKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgbXV0YXRlKGxhYmVsID0gZmFjdG9yKGxhYmVsLCBsZXZlbHMgPSBTVEFURV9PUkRFUikpCgpiaW5fZGYgPC0gcXVlcnlfZGYgJT4lCiAgZmlsdGVyKCFpcy5uYShwc2V1ZG90aW1lX2JpbiksICFpcy5uYShjZWxsX2xpbmUpKSAlPiUKICBjb3VudChjZWxsX2xpbmUsIHBzZXVkb3RpbWVfYmluKSAlPiUKICBncm91cF9ieShjZWxsX2xpbmUpICU+JQogIG11dGF0ZShwY3QgPSAxMDAgKiBuIC8gc3VtKG4pKSAlPiUKICB1bmdyb3VwKCkKCmNhdCgiRGF0YSBmcmFtZXMgcmVhZHkuXG4iKQpgYGAKCi0tLQoKIyA3LiBGaWd1cmUgNyDigJQgRm91ci1QYW5lbCBTdW1tYXJ5CgpgYGB7ciBmaWc3LWZvdXItcGFuZWwsIGZpZy53aWR0aD0xOCwgZmlnLmhlaWdodD0xNH0KCmJnX2xheWVyIDwtIGdlb21fcG9pbnQoCiAgZGF0YSA9IHJlZl9iZ19wbG90LCBhZXMoeCA9IFVNQVBfMSwgeSA9IFVNQVBfMiksCiAgY29sb3VyID0gImdyZXk2OCIsIHNpemUgPSAwLjI1LCBhbHBoYSA9IDAuNDUsIGluaGVyaXQuYWVzID0gRkFMU0UKKQoKcDdhIDwtIGdncGxvdChxdWVyeV9kZltzYW1wbGUobnJvdyhxdWVyeV9kZikpLCBdLAogICAgICAgICAgICAgIGFlcyhVTUFQXzEsIFVNQVBfMiwgY29sb3VyID0gZmFjdG9yKGxhYmVsLCBsZXZlbHMgPSBTVEFURV9PUkRFUikpKSArCiAgYmdfbGF5ZXIgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDAuMzUsIGFscGhhID0gMC43KSArCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBTVEFURV9DT0xPUlMsIG5hbWUgPSAiVHJhbnNmZXJyZWRcbkxhYmVsIiwKICAgICAgICAgICAgICAgICAgICAgIG5hLnZhbHVlID0gImdyZXk4MCIpICsKICBndWlkZXMoY29sb3VyID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2l6ZSA9IDMsIGFscGhhID0gMSkpKSArCiAgdGhlbWVfY2xhc3NpYygpICsKICBsYWJzKHggPSAiVU1BUC0xIiwgeSA9ICJVTUFQLTIiLAogICAgICAgdGl0bGUgPSAiQS4gU8OpemFyeSBDZWxscyDigJQgVHJhbnNmZXJyZWQgQ0Q0IFQgQ2VsbCBMYWJlbHMiKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgc2l6ZSA9IDEyLCBmYWNlID0gImJvbGQiKSkKCnA3YiA8LSBnZ3Bsb3QocXVlcnlfZGZbb3JkZXIocXVlcnlfZGYkcHNldWRvdGltZSwgbmEubGFzdCA9IEZBTFNFKSwgXSwKICAgICAgICAgICAgICBhZXMoVU1BUF8xLCBVTUFQXzIsIGNvbG91ciA9IHBzZXVkb3RpbWUpKSArCiAgYmdfbGF5ZXIgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDAuMzUsIGFscGhhID0gMC44KSArCiAgc2NhbGVfY29sb3VyX2dyYWRpZW50bigKICAgIGNvbG91cnMgID0gYygibGlnaHRibHVlIiwgInllbGxvdyIsICJyZWQiKSwKICAgIG5hbWUgICAgID0gIlBzZXVkb3RpbWUiLAogICAgbGltaXRzICAgPSBQVF9MSU1JVFMsCiAgICBuYS52YWx1ZSA9ICJncmV5ODUiCiAgKSArCiAgdGhlbWVfY2xhc3NpYygpICsKICBsYWJzKHggPSAiVU1BUC0xIiwgeSA9ICJVTUFQLTIiLAogICAgICAgdGl0bGUgPSAiQi4gU8OpemFyeSBDZWxscyDigJQgVHJhbnNmZXJyZWQgUHNldWRvdGltZSAgW0ZJWEVEXSIpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBzaXplID0gMTIsIGZhY2UgPSAiYm9sZCIpKQoKcDdjIDwtIGdncGxvdChxdWVyeV9kZltzYW1wbGUobnJvdyhxdWVyeV9kZikpLCBdLAogICAgICAgICAgICAgIGFlcyhVTUFQXzEsIFVNQVBfMiwgY29sb3VyID0gcHNldWRvdGltZV9iaW4pKSArCiAgYmdfbGF5ZXIgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDAuMzUsIGFscGhhID0gMC43KSArCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBCSU5fQ09MT1JTLCBuYW1lID0gIlBzZXVkb3RpbWVcbkJpbiIsCiAgICAgICAgICAgICAgICAgICAgICBuYS52YWx1ZSA9ICJncmV5ODAiKSArCiAgZ3VpZGVzKGNvbG91ciA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KHNpemUgPSAzLCBhbHBoYSA9IDEpKSkgKwogIHRoZW1lX2NsYXNzaWMoKSArCiAgbGFicyh4ID0gIlVNQVAtMSIsIHkgPSAiVU1BUC0yIiwKICAgICAgIHRpdGxlID0gIkMuIFPDqXphcnkgQ2VsbHMg4oCUIFBzZXVkb3RpbWUgQmluIikgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIHNpemUgPSAxMiwgZmFjZSA9ICJib2xkIikpCgpwN2QgPC0gZ2dwbG90KGxhYmVsX2RmLCBhZXMoY2VsbF9saW5lLCBwY3QsIGZpbGwgPSBsYWJlbCkpICsKICBnZW9tX2NvbCh3aWR0aCA9IDAuNzUpICsKICBnZW9tX3RleHQoZGF0YSA9IGxhYmVsX2RmICU+JSBmaWx0ZXIocGN0ID4gMyksCiAgICAgICAgICAgIGFlcyhsYWJlbCA9IHNwcmludGYoIiUuMWYlJSIsIHBjdCkpLAogICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX3N0YWNrKHZqdXN0ID0gMC41KSwKICAgICAgICAgICAgc2l6ZSA9IDMsIGNvbG91ciA9ICJ3aGl0ZSIsIGZvbnRmYWNlID0gImJvbGQiKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gU1RBVEVfQ09MT1JTLCBuYW1lID0gIkxhYmVsIikgKwogIHNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBjKDAsIDApLCBsaW1pdHMgPSBjKDAsIDEwMSkpICsKICB0aGVtZV9jbGFzc2ljKCkgKwogIGxhYnMoeCA9ICJDZWxsIExpbmUiLCB5ID0gIiUgQ2VsbHMiLAogICAgICAgdGl0bGUgPSAiRC4gVHJhbnNmZXJyZWQgTGFiZWwgcGVyIENlbGwgTGluZSIpICsKICB0aGVtZShwbG90LnRpdGxlICA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgc2l6ZSA9IDEyLCBmYWNlID0gImJvbGQiKSwKICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTAsIGZhY2UgPSAiYm9sZCIpKQoKZmlnNyA8LSAocDdhIHwgcDdiKSAvIChwN2MgfCBwN2QpCnByaW50KGZpZzcpCnNhdmVfZmlnKGZpZzcsICJGaWc3X0ZvdXJQYW5lbF9TdW1tYXJ5X0ZJWEVEIiwgICAgICAgdyA9IDE4LCBoID0gMTQpCnNhdmVfZmlnKHA3YSwgICJGaWc3QV9UcmFuc2ZlcnJlZF9MYWJlbHMiLCAgICAgICAgICAgdyA9IDksICBoID0gNykKc2F2ZV9maWcocDdiLCAgIkZpZzdCX1RyYW5zZmVycmVkX1BzZXVkb3RpbWVfRklYRUQiLCB3ID0gOSwgIGggPSA3KQpzYXZlX2ZpZyhwN2MsICAiRmlnN0NfUHNldWRvdGltZV9CaW5zX1VNQVAiLCAgICAgICAgIHcgPSA5LCAgaCA9IDcpCnNhdmVfZmlnKHA3ZCwgICJGaWc3RF9MYWJlbF9CYXJDaGFydF9wZXJfTGluZSIsICAgICAgdyA9IDksICBoID0gNykKYGBgCgotLS0KCiMgOC4gRmlndXJlIDEg4oCUIFBzZXVkb3RpbWUgVmlvbGluIHBlciBDZWxsIExpbmUKCmBgYHtyIGZpZzEtdmlvbGluLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9Nn0KCmZpZzEgPC0gZ2dwbG90KAogICAgcXVlcnlfZGYgJT4lIGZpbHRlcighaXMubmEocHNldWRvdGltZSksICFpcy5uYShjZWxsX2xpbmUpKSwKICAgIGFlcyhjZWxsX2xpbmUsIHBzZXVkb3RpbWUsIGZpbGwgPSBjZWxsX2xpbmUpCiAgKSArCiAgZ2VvbV92aW9saW4oc2NhbGUgPSAid2lkdGgiLCB0cmltID0gRkFMU0UsIGFscGhhID0gMC44NSwgY29sb3VyID0gIndoaXRlIikgKwogIGdlb21fYm94cGxvdCh3aWR0aCA9IDAuMDYsIGZpbGwgPSAid2hpdGUiLCBjb2xvdXIgPSAiZ3JleTQwIiwKICAgICAgICAgICAgICAgb3V0bGllci5zaXplID0gMC4zLCBvdXRsaWVyLmFscGhhID0gMC4zKSArCiAgZ2VvbV9obGluZShkYXRhID0gUkVGX01FRElBTlMsCiAgICAgICAgICAgICBhZXMoeWludGVyY2VwdCA9IG1lZF9wdCwgY29sb3VyID0gc3RhdGUsIGxpbmV0eXBlID0gc3RhdGUpLAogICAgICAgICAgICAgbGluZXdpZHRoID0gMC43LCBhbHBoYSA9IDAuOSkgKwogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiU2V0MiIsIGd1aWRlID0gIm5vbmUiKSArCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBTVEFURV9DT0xPUlMsIG5hbWUgPSAiUmVmZXJlbmNlXG5tZWRpYW4iKSArCiAgc2NhbGVfbGluZXR5cGVfbWFudWFsKAogICAgdmFsdWVzID0gYygiQ0Q0IE5haXZlIiA9ICJkb3R0ZWQiLCAiQ0Q0IFRDTSIgPSAiZGFzaGVkIiwKICAgICAgICAgICAgICAgIkNENCBURU0iID0gImxvbmdkYXNoIiwgIkNENCBUZW1yYS9DVEwiID0gInNvbGlkIiwKICAgICAgICAgICAgICAgIlRyZWciID0gInR3b2Rhc2giKSwKICAgIG5hbWUgPSAiUmVmZXJlbmNlXG5tZWRpYW4iCiAgKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCAyNSwgNSkpICsKICB0aGVtZV9jbGFzc2ljKCkgKwogIGxhYnMoCiAgICB4ICAgICAgICA9ICJDZWxsIExpbmUiLAogICAgeSAgICAgICAgPSAiVHJhbnNmZXJyZWQgUHNldWRvdGltZSIsCiAgICB0aXRsZSAgICA9ICJGaWd1cmUgMSDigJQgUHNldWRvdGltZSBEaXN0cmlidXRpb24gcGVyIFPDqXphcnkgQ2VsbCBMaW5lIiwKICAgIHN1YnRpdGxlID0gc3ByaW50ZigKICAgICAgIlJlZmVyZW5jZSBtZWRpYW5zOiBOYWl2ZT0lLjJmIHwgVENNPSUuMmYgfCBUcmVnPSUuMmYgfCBURU09JS4yZiB8IFRlbXJhPSUuMmYiLAogICAgICBuYWl2ZV9tZWQsIHRjbV9tZWQsIHRyZWdfbWVkLCB0ZW1fbWVkLCB0ZW1yYV9tZWQpCiAgKSArCiAgdGhlbWUocGxvdC50aXRsZSAgICA9IGVsZW1lbnRfdGV4dChzaXplID0gMTMsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDksICBjb2xvdXIgPSAiZ3JleTQwIiksCiAgICAgICAgYXhpcy50ZXh0LnggICA9IGVsZW1lbnRfdGV4dChzaXplID0gMTEsIGZhY2UgPSAiYm9sZCIpKQoKcHJpbnQoZmlnMSkKc2F2ZV9maWcoZmlnMSwgIkZpZzFfUHNldWRvdGltZV9WaW9saW5fcGVyX0xpbmUiLCB3ID0gMTIsIGggPSA2KQpgYGAKCmBgYHtyICwgZmlnLndpZHRoPTE2LCBmaWcuaGVpZ2h0PTd9CmZpZzF2MiA8LSBnZ3Bsb3QoCiAgICBxdWVyeV9kZiAlPiUgZmlsdGVyKCFpcy5uYShwc2V1ZG90aW1lKSwgIWlzLm5hKGNlbGxfbGluZSkpLAogICAgYWVzKGNlbGxfbGluZSwgcHNldWRvdGltZSwgZmlsbCA9IGNlbGxfbGluZSkKICApICsKICBnZW9tX3Zpb2xpbihzY2FsZSA9ICJ3aWR0aCIsIHRyaW0gPSBGQUxTRSwgYWxwaGEgPSAwLjg1LCBjb2xvdXIgPSAid2hpdGUiKSArCiAgZ2VvbV9ib3hwbG90KHdpZHRoID0gMC4wNiwgZmlsbCA9ICJ3aGl0ZSIsIGNvbG91ciA9ICJncmV5NDAiLAogICAgICAgICAgICAgICBvdXRsaWVyLnNpemUgPSAwLjMsIG91dGxpZXIuYWxwaGEgPSAwLjMpICsKICBnZW9tX2hsaW5lKAogICAgZGF0YSAgICAgID0gUkVGX01FRElBTlNfRUZGRUNUT1IsCiAgICBhZXMoeWludGVyY2VwdCA9IG1lZF9wdCwgY29sb3VyID0gc3RhdGUsIGxpbmV0eXBlID0gc3RhdGUpLAogICAgbGluZXdpZHRoID0gMC43LCBhbHBoYSA9IDAuOQogICkgKwogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiU2V0MiIsIGd1aWRlID0gIm5vbmUiKSArCiAgc2NhbGVfY29sb3VyX21hbnVhbCgKICAgIHZhbHVlcyA9IFNUQVRFX0NPTE9SU1tuYW1lcyhTVEFURV9DT0xPUlMpICE9ICJUcmVnIl0sCiAgICBuYW1lICAgPSAiUmVmZXJlbmNlXG5tZWRpYW4iCiAgKSArCiAgc2NhbGVfbGluZXR5cGVfbWFudWFsKAogICAgdmFsdWVzID0gTElORVRZUEVfRUZGRUNUT1IsCiAgICBuYW1lICAgPSAiUmVmZXJlbmNlXG5tZWRpYW4iCiAgKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCAyNSwgNSkpICsKICB0aGVtZV9jbGFzc2ljKCkgKwogIGxhYnMoCiAgICB4ICAgICAgICA9ICJDZWxsIExpbmUiLAogICAgeSAgICAgICAgPSAiVHJhbnNmZXJyZWQgUHNldWRvdGltZSIsCiAgICB0aXRsZSAgICA9ICJGaWd1cmUgMSDigJQgUHNldWRvdGltZSBEaXN0cmlidXRpb24gcGVyIFPDqXphcnkgQ2VsbCBMaW5lIiwKICAgIHN1YnRpdGxlID0gc3ByaW50ZigKICAgICAgIkVmZmVjdG9yIGF4aXMgbWVkaWFuczogTmFpdmU9JS4yZiB8IFRDTT0lLjJmIHwgVEVNPSUuMmYgfCBUZW1yYT0lLjJmXG5UcmVnIGxpbmUgb21pdHRlZCDigJQgVHJlZyAoUFQ9JS4yZikgb3ZlcmxhcHMgVENNIChQVD0lLjJmKTsgVHJlZy1saWtlIGFzc2lnbmVkIGJ5IGxhYmVsIiwKICAgICAgbmFpdmVfbWVkLCB0Y21fbWVkLCB0ZW1fbWVkLCB0ZW1yYV9tZWQsIHRyZWdfbWVkLCB0Y21fbWVkKQogICkgKwogIHRoZW1lKAogICAgcGxvdC50aXRsZSAgICA9IGVsZW1lbnRfdGV4dChzaXplID0gMTMsIGZhY2UgPSAiYm9sZCIpLAogICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gOSwgIGNvbG91ciA9ICJncmV5NDAiKSwKICAgIGF4aXMudGV4dC54ICAgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDExLCBmYWNlID0gImJvbGQiKQogICkKCnByaW50KGZpZzF2MikKc2F2ZV9maWcoZmlnMXYyLCAiRmlnMXYyX1BzZXVkb3RpbWVfVmlvbGluX05vVHJlZyIsIHcgPSAxMiwgaCA9IDYpCmBgYAoKCi0tLQoKIyA5LiBGaWd1cmUgMiDigJQgVU1BUDogUHNldWRvdGltZSArIExhYmVsIFNpZGUgYnkgU2lkZQoKYGBge3IgZmlnMi11bWFwLCBmaWcud2lkdGg9MTYsIGZpZy5oZWlnaHQ9N30KCnAyYSA8LSBnZ3Bsb3QocXVlcnlfZGZbb3JkZXIocXVlcnlfZGYkcHNldWRvdGltZSwgbmEubGFzdCA9IEZBTFNFKSwgXSwKICAgICAgICAgICAgICBhZXMoVU1BUF8xLCBVTUFQXzIsIGNvbG91ciA9IHBzZXVkb3RpbWUpKSArCiAgZ2VvbV9wb2ludChkYXRhID0gcmVmX2JnX3Bsb3QsIGFlcyhVTUFQXzEsIFVNQVBfMiksCiAgICAgICAgICAgICBjb2xvdXIgPSAiZ3JleTY4Iiwgc2l6ZSA9IDAuMjUsIGFscGhhID0gMC40NSwgaW5oZXJpdC5hZXMgPSBGQUxTRSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDAuMjUsIGFscGhhID0gMC44KSArCiAgc2NhbGVfY29sb3VyX2dyYWRpZW50bigKICAgIGNvbG91cnMgPSBjKCIjMEQwODg3IiwiIzZBMDBBOCIsIiNCMTJBOTAiLCIjRTE2NDYyIiwiI0ZDQTYzNiIsIiNGMEY5MjEiKSwKICAgIG5hbWUgPSAiUHNldWRvdGltZSIsIGxpbWl0cyA9IFBUX0xJTUlUUywgbmEudmFsdWUgPSAiZ3JleTg1IgogICkgKwogIHRoZW1lX2NsYXNzaWMoKSArCiAgbGFicyh4ID0gIlVNQVAtMSIsIHkgPSAiVU1BUC0yIiwKICAgICAgIHRpdGxlICAgID0gIkEuIFRyYW5zZmVycmVkIHBzZXVkb3RpbWUgZ3JhZGllbnQiLAogICAgICAgc3VidGl0bGUgPSAiR3JleSA9IGhlYWx0aHkgcmVmZXJlbmNlICB8ICBDb2xvdXJlZCA9IFPDqXphcnkgY2VsbHMiKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGZhY2UgPSAiYm9sZCIpKQoKcDJiIDwtIGdncGxvdChxdWVyeV9kZltzYW1wbGUobnJvdyhxdWVyeV9kZikpLCBdLAogICAgICAgICAgICAgIGFlcyhVTUFQXzEsIFVNQVBfMiwgY29sb3VyID0gZmFjdG9yKGxhYmVsLCBsZXZlbHMgPSBTVEFURV9PUkRFUikpKSArCiAgZ2VvbV9wb2ludChkYXRhID0gcmVmX2JnX3Bsb3QsIGFlcyhVTUFQXzEsIFVNQVBfMiksCiAgICAgICAgICAgICBjb2xvdXIgPSAiZ3JleTg4Iiwgc2l6ZSA9IDAuMjUsIGFscGhhID0gMC40NSwgaW5oZXJpdC5hZXMgPSBGQUxTRSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDAuMjUsIGFscGhhID0gMC43KSArCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBTVEFURV9DT0xPUlMsIG5hbWUgPSAiVHJhbnNmZXJyZWRcbmxhYmVsIiwKICAgICAgICAgICAgICAgICAgICAgIG5hLnZhbHVlID0gImdyZXk4MCIpICsKICBndWlkZXMoY29sb3VyID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2l6ZSA9IDMsIGFscGhhID0gMSkpKSArCiAgdGhlbWVfY2xhc3NpYygpICsKICBsYWJzKHggPSAiVU1BUC0xIiwgeSA9ICJVTUFQLTIiLAogICAgICAgdGl0bGUgICAgPSAiQi4gVHJhbnNmZXJyZWQgbGFiZWwgaWRlbnRpdHkiLAogICAgICAgc3VidGl0bGUgPSAiOTYtOTkuOSUgQ0Q0IFRDTSBhY3Jvc3MgYWxsIHNldmVuIGxpbmVzIikgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBmYWNlID0gImJvbGQiKSkKCmZpZzIgPC0gcDJhIHwgcDJiCnByaW50KGZpZzIpCnNhdmVfZmlnKGZpZzIsICJGaWcyX1VNQVBfUHNldWRvdGltZV9hbmRfTGFiZWwiLCB3ID0gMTYsIGggPSA3KQpgYGAKCi0tLQoKIyAxMC4gRmlndXJlIDMg4oCUIExhYmVsIEJhciBDaGFydCArIFBlciBDZWxsIExpbmUgVU1BUCBGYWNldHMKCmBgYHtyIGZpZzMtbGFiZWwtdW1hcCwgZmlnLndpZHRoPTE4LCBmaWcuaGVpZ2h0PTZ9CgpwM2EgPC0gZ2dwbG90KGxhYmVsX2RmLCBhZXMoY2VsbF9saW5lLCBwY3QsIGZpbGwgPSBsYWJlbCkpICsKICBnZW9tX2NvbCh3aWR0aCA9IDAuNzUpICsKICBnZW9tX3RleHQoZGF0YSA9IGxhYmVsX2RmICU+JSBmaWx0ZXIocGN0ID4gNSksCiAgICAgICAgICAgIGFlcyhsYWJlbCA9IHNwcmludGYoIiUuMGYlJSIsIHBjdCkpLAogICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX3N0YWNrKHZqdXN0ID0gMC41KSwKICAgICAgICAgICAgc2l6ZSA9IDMsIGNvbG91ciA9ICJ3aGl0ZSIsIGZvbnRmYWNlID0gImJvbGQiKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gU1RBVEVfQ09MT1JTLCBuYW1lID0gIkxhYmVsIikgKwogIHNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBjKDAsIDApLCBsaW1pdHMgPSBjKDAsIDEwMSkpICsKICB0aGVtZV9jbGFzc2ljKCkgKwogIGxhYnMoeCA9ICJDZWxsIExpbmUiLCB5ID0gIiUgQ2VsbHMiLCB0aXRsZSA9ICJBLiBUcmFuc2ZlcnJlZCBBemltdXRoIGwyIExhYmVscyIpICsKICB0aGVtZShwbG90LnRpdGxlICA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCwgZmFjZSA9ICJib2xkIikpCgpwM2IgPC0gZ2dwbG90KAogICAgcXVlcnlfZGYgJT4lIGZpbHRlcighaXMubmEoY2VsbF9saW5lKSkgJT4lIGFycmFuZ2UocHNldWRvdGltZSksCiAgICBhZXMoVU1BUF8xLCBVTUFQXzIsIGNvbG91ciA9IHBzZXVkb3RpbWUpCiAgKSArCiAgZ2VvbV9wb2ludCgKICAgIGRhdGEgPSByZWZfYmdfcGxvdCAlPiUKICAgICAgc2xpY2UocmVwKDE6bigpLCB0aW1lcyA9IGxlbmd0aChsZXZlbHMocXVlcnlfZGYkY2VsbF9saW5lKSkpKSwKICAgIGFlcyhVTUFQXzEsIFVNQVBfMiksIGNvbG91ciA9ICJncmV5ODgiLCBzaXplID0gMC4yLCBhbHBoYSA9IDAuNCwKICAgIGluaGVyaXQuYWVzID0gRkFMU0UKICApICsKICBnZW9tX3BvaW50KHNpemUgPSAwLjIsIGFscGhhID0gMC44KSArCiAgc2NhbGVfY29sb3VyX2dyYWRpZW50bigKICAgIGNvbG91cnMgPSBjKCIjMEQwODg3IiwiIzZBMDBBOCIsIiNCMTJBOTAiLCIjRTE2NDYyIiwiI0ZDQTYzNiIsIiNGMEY5MjEiKSwKICAgIG5hbWUgPSAiUHNldWRvdGltZSIsIGxpbWl0cyA9IFBUX0xJTUlUUywgbmEudmFsdWUgPSAiZ3JleTg1IgogICkgKwogIGZhY2V0X3dyYXAofiBjZWxsX2xpbmUsIG5yb3cgPSAyKSArCiAgdGhlbWVfY2xhc3NpYygpICsKICB0aGVtZShzdHJpcC50ZXh0ID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIsIHNpemUgPSAxMCksCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgIGF4aXMudGV4dCAgPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCkpICsKICBsYWJzKHggPSBOVUxMLCB5ID0gTlVMTCwKICAgICAgIHRpdGxlID0gIkIuIFBlciBDZWxsIExpbmUgVU1BUCDigJQgUHJvamVjdGVkIFBzZXVkb3RpbWUgIChHcmV5ID0gaGVhbHRoeSByZWZlcmVuY2UpIikKCmZpZzMgPC0gcDNhICsgcDNiICsgcGxvdF9sYXlvdXQod2lkdGhzID0gYygxLCAyKSkKcHJpbnQoZmlnMykKc2F2ZV9maWcoZmlnMywgIkZpZzNfTGFiZWxzX1BlckxpbmVfVU1BUCIsIHcgPSAxOCwgaCA9IDYpCmBgYAoKLS0tCgojIDExLiBGaWd1cmUgNCDigJQgUGVyIENlbGwgTGluZSBGYWNldCBVTUFQIChLZXkgSGV0ZXJvZ2VuZWl0eSBGaWd1cmUpCgpgYGB7ciBmaWc0LWZhY2V0LXVtYXAsIGZpZy53aWR0aD0xOCwgZmlnLmhlaWdodD0xMH0KCmZpZzQgPC0gZ2dwbG90KAogICAgcXVlcnlfZGYgJT4lIGZpbHRlcighaXMubmEoY2VsbF9saW5lKSkgJT4lIGFycmFuZ2UocHNldWRvdGltZSksCiAgICBhZXMoVU1BUF8xLCBVTUFQXzIsIGNvbG91ciA9IHBzZXVkb3RpbWUpCiAgKSArCiAgZ2VvbV9wb2ludCgKICAgIGRhdGEgPSByZWZfYmdfcGxvdCAlPiUKICAgICAgc2xpY2UocmVwKDE6bigpLCB0aW1lcyA9IGxlbmd0aChsZXZlbHMocXVlcnlfZGYkY2VsbF9saW5lKSkpKSwKICAgIGFlcyhVTUFQXzEsIFVNQVBfMiksIGNvbG91ciA9ICJncmV5ODgiLCBzaXplID0gMC4yLCBhbHBoYSA9IDAuNCwKICAgIGluaGVyaXQuYWVzID0gRkFMU0UKICApICsKICBnZW9tX3BvaW50KHNpemUgPSAwLjI1LCBhbHBoYSA9IDAuOCkgKwogIHNjYWxlX2NvbG91cl9ncmFkaWVudG4oCiAgICBjb2xvdXJzID0gYygiIzBEMDg4NyIsIiM2QTAwQTgiLCIjQjEyQTkwIiwiI0UxNjQ2MiIsIiNGQ0E2MzYiLCIjRjBGOTIxIiksCiAgICBuYW1lID0gIlBzZXVkb3RpbWVcbihOYWl2ZS0+VGVtcmEpIiwgbGltaXRzID0gUFRfTElNSVRTLCBuYS52YWx1ZSA9ICJncmV5ODUiCiAgKSArCiAgZmFjZXRfd3JhcCh+IGNlbGxfbGluZSwgbnJvdyA9IDIpICsKICB0aGVtZV9jbGFzc2ljKCkgKwogIHRoZW1lKAogICAgc3RyaXAudGV4dCAgICAgICA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiLCBzaXplID0gMTIpLAogICAgc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gImdyZXk5NSIsIGNvbG91ciA9ICJncmV5NjAiKSwKICAgIHBsb3QudGl0bGUgICAgICAgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBmYWNlID0gImJvbGQiKSwKICAgIHBsb3Quc3VidGl0bGUgICAgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwLCBjb2xvdXIgPSAiZ3JleTQwIiksCiAgICBheGlzLnRleHQgPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCksCiAgICBsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiCiAgKSArCiAgbGFicyh4ID0gTlVMTCwgeSA9IE5VTEwsCiAgICAgICB0aXRsZSAgICA9ICJGaWd1cmUgNCDigJQgUGVyIENlbGwgTGluZSBGYWNldCBVTUFQOiBEaWZmZXJlbnRpYXRpb24gUG9zaXRpb24iLAogICAgICAgc3VidGl0bGUgPSAiTDEgbW9zdCBoZXRlcm9nZW5lb3VzIChzcGFucyBmdWxsIHRyYWplY3RvcnkpICB8ICBMNiBtb3N0IGFycmVzdGVkICh0aWdodCBUQ00gY2x1c3RlcikiKQoKcHJpbnQoZmlnNCkKc2F2ZV9maWcoZmlnNCwgIkZpZzRfUGVyTGluZV9GYWNldF9VTUFQIiwgdyA9IDE4LCBoID0gMTApCmBgYAoKLS0tCgojIDEyLiBGaWd1cmUgNSDigJQgTGFiZWwgdnMgUHNldWRvdGltZSBCaW5zIEJhciBDaGFydHMKCmBgYHtyIGZpZzUtYmFyY2hhcnRzLCBmaWcud2lkdGg9MTYsIGZpZy5oZWlnaHQ9MTJ9CgpwNWEgPC0gZ2dwbG90KGxhYmVsX2RmLCBhZXMoY2VsbF9saW5lLCBwY3QsIGZpbGwgPSBsYWJlbCkpICsKICBnZW9tX2NvbCh3aWR0aCA9IDAuNykgKwogIGdlb21fdGV4dChkYXRhID0gbGFiZWxfZGYgJT4lIGZpbHRlcihwY3QgPiAzKSwKICAgICAgICAgICAgYWVzKGxhYmVsID0gc3ByaW50ZigiJS4xZiUlIiwgcGN0KSksCiAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fc3RhY2sodmp1c3QgPSAwLjUpLAogICAgICAgICAgICBzaXplID0gMy4yLCBjb2xvdXIgPSAid2hpdGUiLCBmb250ZmFjZSA9ICJib2xkIikgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IFNUQVRFX0NPTE9SUywgbmFtZSA9ICJMYWJlbCIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gYygwLCAwKSwgbGltaXRzID0gYygwLCAxMDEpKSArCiAgdGhlbWVfY2xhc3NpYygpICsKICBsYWJzKHggPSBOVUxMLCB5ID0gIiUgQ2VsbHMiLAogICAgICAgdGl0bGUgICAgPSAiQS4gVHJhbnNmZXJyZWQgQXppbXV0aCBsMiBMYWJlbCAoQ2VsbCBvZiBPcmlnaW4pIiwKICAgICAgIHN1YnRpdGxlID0gIjk2LTk5LjklIENENCBUQ00gYWNyb3NzIGFsbCBsaW5lcyDigJQgVENNIG9yaWdpbiBjb25maXJtZWQiKSArCiAgdGhlbWUocGxvdC50aXRsZSAgICA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwLCBjb2xvdXIgPSAiZ3JleTQwIiksCiAgICAgICAgYXhpcy50ZXh0LnggICA9IGVsZW1lbnRfYmxhbmsoKSwgYXhpcy50aWNrcy54ID0gZWxlbWVudF9ibGFuaygpKQoKcDViIDwtIGdncGxvdChiaW5fZGYsIGFlcyhjZWxsX2xpbmUsIHBjdCwgZmlsbCA9IHBzZXVkb3RpbWVfYmluKSkgKwogIGdlb21fY29sKHdpZHRoID0gMC43KSArCiAgZ2VvbV90ZXh0KGRhdGEgPSBiaW5fZGYgJT4lIGZpbHRlcihwY3QgPiAzKSwKICAgICAgICAgICAgYWVzKGxhYmVsID0gc3ByaW50ZigiJS4xZiUlIiwgcGN0KSksCiAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fc3RhY2sodmp1c3QgPSAwLjUpLAogICAgICAgICAgICBzaXplID0gMy4yLCBjb2xvdXIgPSAid2hpdGUiLCBmb250ZmFjZSA9ICJib2xkIikgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IEJJTl9DT0xPUlMsIG5hbWUgPSAiUHNldWRvdGltZSBiaW4iKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwgMCksIGxpbWl0cyA9IGMoMCwgMTAxKSkgKwogIHRoZW1lX2NsYXNzaWMoKSArCiAgbGFicyh4ID0gIkNlbGwgTGluZSIsIHkgPSAiJSBDZWxscyIsCiAgICAgICB0aXRsZSAgICA9ICJCLiBQc2V1ZG90aW1lIEJpbiBBc3NpZ25tZW50IChEaWZmZXJlbnRpYXRpb24gUG9zaXRpb24pIiwKICAgICAgIHN1YnRpdGxlID0gc3ByaW50ZigKICAgICAgICAgIkJpbiBib3VuZGFyaWVzIOKAlCBOYWl2ZXxUQ006ICUuMmYgIHwgIFRDTXxURU06ICUuMmYgIHwgIFRFTXxUZW1yYTogJS4yZiAgfCAgVHJlZzogbGFiZWwtYmFzZWQiLAogICAgICAgICBCSU5fTkFJVkVfVENNLCBCSU5fVENNX1RFTSwgQklOX1RFTV9URU1SQSkpICsKICB0aGVtZShwbG90LnRpdGxlICAgID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gOSwgIGNvbG91ciA9ICJncmV5NDAiKSwKICAgICAgICBheGlzLnRleHQueCAgID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMSwgZmFjZSA9ICJib2xkIikpCgpmaWc1IDwtIHA1YSAvIHA1YgpwcmludChmaWc1KQpzYXZlX2ZpZyhmaWc1LCAiRmlnNV9MYWJlbF92c19Qc2V1ZG90aW1lX0JpbnMiLCAgdyA9IDE2LCBoID0gMTIpCnNhdmVfZmlnKHA1YSwgICJGaWc1QV9MYWJlbF9UcmFuc2Zlcl9CYXJDaGFydCIsICB3ID0gMTAsIGggPSA1KQpzYXZlX2ZpZyhwNWIsICAiRmlnNUJfUHNldWRvdGltZV9CaW5zX0JhckNoYXJ0IiwgdyA9IDEwLCBoID0gNSkKYGBgCgotLS0KCiMgMTMuIEZpZ3VyZSA2IOKAlCBQc2V1ZG90aW1lIERlbnNpdHkgKyBSaWRnZXBsb3QKCmBgYHtyIGZpZzYtZGVuc2l0eSwgZmlnLndpZHRoPTE2LCBmaWcuaGVpZ2h0PTd9CgpwNmEgPC0gZ2dwbG90KHF1ZXJ5X2RmICU+JSBmaWx0ZXIoIWlzLm5hKHBzZXVkb3RpbWUpKSwgYWVzKHggPSBwc2V1ZG90aW1lKSkgKwogIGdlb21fZGVuc2l0eShmaWxsID0gIiNjMDM5MmIiLCBjb2xvdXIgPSAiI2MwMzkyYiIsIGFscGhhID0gMC40LCBsaW5ld2lkdGggPSAwLjkpICsKICBnZW9tX3ZsaW5lKGRhdGEgPSBSRUZfTUVESUFOUywKICAgICAgICAgICAgIGFlcyh4aW50ZXJjZXB0ID0gbWVkX3B0LCBjb2xvdXIgPSBzdGF0ZSksCiAgICAgICAgICAgICBsaW5ldHlwZSA9ICJkYXNoZWQiLCBsaW5ld2lkdGggPSAwLjcpICsKICBnZW9tX3RleHQoZGF0YSA9IFJFRl9NRURJQU5TLAogICAgICAgICAgICBhZXMoeCA9IG1lZF9wdCwgeSA9IEluZiwKICAgICAgICAgICAgICAgIGxhYmVsID0gZ3N1YigiQ0Q0ICIsICIiLCBhcy5jaGFyYWN0ZXIoc3RhdGUpKSksCiAgICAgICAgICAgIGluaGVyaXQuYWVzID0gRkFMU0UsCiAgICAgICAgICAgIGFuZ2xlID0gOTAsIGhqdXN0ID0gMS4xLCB2anVzdCA9IC0wLjMsIHNpemUgPSAyLjgsIGNvbG91ciA9ICJncmV5MzAiKSArCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBTVEFURV9DT0xPUlMsIG5hbWUgPSAiUmVmZXJlbmNlXG5zdGF0ZSBtZWRpYW4iKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cyA9IGMoMCwgMjYpLCBicmVha3MgPSBzZXEoMCwgMjUsIDUpKSArCiAgdGhlbWVfY2xhc3NpYygpICsKICBsYWJzKAogICAgeCAgICAgICAgPSAiVHJhbnNmZXJyZWQgUHNldWRvdGltZSIsCiAgICB5ICAgICAgICA9ICJEZW5zaXR5IiwKICAgIHRpdGxlICAgID0gIkEuIFPDqXphcnkgUHNldWRvdGltZSBEaXN0cmlidXRpb24g4oCUIEFsbCBMaW5lcyBDb21iaW5lZCIsCiAgICBzdWJ0aXRsZSA9IHNwcmludGYoCiAgICAgICJSZWZlcmVuY2UgbWVkaWFuczogTmFpdmU9JS4yZiB8IFRDTT0lLjJmIHwgVHJlZz0lLjJmIHwgVEVNPSUuMmYgfCBUZW1yYT0lLjJmIiwKICAgICAgbmFpdmVfbWVkLCB0Y21fbWVkLCB0cmVnX21lZCwgdGVtX21lZCwgdGVtcmFfbWVkKQogICkgKwogIHRoZW1lKHBsb3QudGl0bGUgICAgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBmYWNlID0gImJvbGQiKSwKICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSA5LCAgY29sb3VyID0gImdyZXk0MCIpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpCgpwNmIgPC0gZ2dwbG90KAogICAgcXVlcnlfZGYgJT4lIGZpbHRlcighaXMubmEocHNldWRvdGltZSksICFpcy5uYShjZWxsX2xpbmUpKSwKICAgIGFlcyh4ID0gcHNldWRvdGltZSwgeSA9IGZjdF9yZXYoY2VsbF9saW5lKSwgZmlsbCA9IGNlbGxfbGluZSkKICApICsKICBnZW9tX2RlbnNpdHlfcmlkZ2VzKHNjYWxlID0gMC45LCByZWxfbWluX2hlaWdodCA9IDAuMDEsCiAgICAgICAgICAgICAgICAgICAgICBhbHBoYSA9IDAuODUsIGNvbG91ciA9ICJ3aGl0ZSIpICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBjKEJJTl9OQUlWRV9UQ00sIEJJTl9UQ01fVEVNLCBCSU5fVEVNX1RFTVJBKSwKICAgICAgICAgICAgIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG91ciA9ICJncmV5NTAiLCBsaW5ld2lkdGggPSAwLjUpICsKICBhbm5vdGF0ZSgidGV4dCIsIHggPSBCSU5fTkFJVkVfVENNLCB5ID0gMC41LCBsYWJlbCA9ICJOYWl2ZXxUQ00iLAogICAgICAgICAgIHNpemUgPSAyLjUsIGhqdXN0ID0gMS4wNSwgY29sb3VyID0gImdyZXk0MCIsIGFuZ2xlID0gOTApICsKICBhbm5vdGF0ZSgidGV4dCIsIHggPSBCSU5fVENNX1RFTSwgICB5ID0gMC41LCBsYWJlbCA9ICJUQ018VEVNIiwKICAgICAgICAgICBzaXplID0gMi41LCBoanVzdCA9IDEuMDUsIGNvbG91ciA9ICJncmV5NDAiLCBhbmdsZSA9IDkwKSArCiAgYW5ub3RhdGUoInRleHQiLCB4ID0gQklOX1RFTV9URU1SQSwgeSA9IDAuNSwgbGFiZWwgPSAiVEVNfFRlbXJhIiwKICAgICAgICAgICBzaXplID0gMi41LCBoanVzdCA9IDEuMDUsIGNvbG91ciA9ICJncmV5NDAiLCBhbmdsZSA9IDkwKSArCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJTZXQyIiwgZ3VpZGUgPSAibm9uZSIpICsKICBzY2FsZV94X2NvbnRpbnVvdXMobGltaXRzID0gYygwLCAyNiksIGJyZWFrcyA9IHNlcSgwLCAyNSwgNSkpICsKICB0aGVtZV9yaWRnZXMoZ3JpZCA9IEZBTFNFKSArCiAgbGFicyh4ID0gIlRyYW5zZmVycmVkIFBzZXVkb3RpbWUiLCB5ID0gIkNlbGwgTGluZSIsCiAgICAgICB0aXRsZSAgICA9ICJCLiBQZXIgQ2VsbCBMaW5lIFBzZXVkb3RpbWUgUmlkZ2VwbG90IiwKICAgICAgIHN1YnRpdGxlID0gIkwxIG11bHRpbW9kYWwgIHwgIEwzLUw3IG5hcnJvdyBwZWFrcyBhdCBUQ00tVEVNIGJvdW5kYXJ5IikgKwogIHRoZW1lKHBsb3QudGl0bGUgICAgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBmYWNlID0gImJvbGQiKSwKICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCwgY29sb3VyID0gImdyZXk0MCIpKQoKZmlnNiA8LSBwNmEgfCBwNmIKcHJpbnQoZmlnNikKc2F2ZV9maWcoZmlnNiwgIkZpZzZfRGVuc2l0eV9hbmRfUmlkZ2VwbG90IiwgdyA9IDE2LCBoID0gNykKc2F2ZV9maWcocDZhLCAgIkZpZzZBX0RlbnNpdHlfT3ZlcmFsbCIsICAgICB3ID0gOCwgIGggPSA2KQpzYXZlX2ZpZyhwNmIsICAiRmlnNkJfUmlkZ2VwbG90X1BlckxpbmUiLCAgIHcgPSA4LCAgaCA9IDYpCmBgYAoKCgpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTZ9CnA2YV92MiA8LSBnZ3Bsb3QoCiAgICBxdWVyeV9kZiAlPiUgZmlsdGVyKCFpcy5uYShwc2V1ZG90aW1lKSksCiAgICBhZXMoeCA9IHBzZXVkb3RpbWUpCiAgKSArCiAgZ2VvbV9kZW5zaXR5KGZpbGwgPSAiI2MwMzkyYiIsIGNvbG91ciA9ICIjYzAzOTJiIiwgYWxwaGEgPSAwLjQsIGxpbmV3aWR0aCA9IDAuOSkgKwogIGdlb21fdmxpbmUoCiAgICBkYXRhICAgICA9IFJFRl9NRURJQU5TX0VGRkVDVE9SLAogICAgYWVzKHhpbnRlcmNlcHQgPSBtZWRfcHQsIGNvbG91ciA9IHN0YXRlKSwKICAgIGxpbmV0eXBlID0gImRhc2hlZCIsIGxpbmV3aWR0aCA9IDAuNwogICkgKwogIGdlb21fdGV4dCgKICAgIGRhdGEgICAgICAgID0gUkVGX01FRElBTlNfRUZGRUNUT1IsCiAgICBhZXMoeCA9IG1lZF9wdCwgeSA9IEluZiwKICAgICAgICBsYWJlbCA9IGdzdWIoIkNENCAiLCAiIiwgYXMuY2hhcmFjdGVyKHN0YXRlKSkpLAogICAgaW5oZXJpdC5hZXMgPSBGQUxTRSwKICAgIGFuZ2xlID0gOTAsIGhqdXN0ID0gMS4xLCB2anVzdCA9IC0wLjMsIHNpemUgPSAyLjgsIGNvbG91ciA9ICJncmV5MzAiCiAgKSArCiAgc2NhbGVfY29sb3VyX21hbnVhbCgKICAgIHZhbHVlcyA9IFNUQVRFX0NPTE9SU1tuYW1lcyhTVEFURV9DT0xPUlMpICE9ICJUcmVnIl0sCiAgICBuYW1lICAgPSAiUmVmZXJlbmNlXG5zdGF0ZSBtZWRpYW4iCiAgKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cyA9IGMoMCwgMjYpLCBicmVha3MgPSBzZXEoMCwgMjUsIDUpKSArCiAgdGhlbWVfY2xhc3NpYygpICsKICBsYWJzKAogICAgeCAgICAgICAgPSAiVHJhbnNmZXJyZWQgUHNldWRvdGltZSIsCiAgICB5ICAgICAgICA9ICJEZW5zaXR5IiwKICAgIHRpdGxlICAgID0gIkEuIFPDqXphcnkgUHNldWRvdGltZSBEaXN0cmlidXRpb24g4oCUIEFsbCBMaW5lcyBDb21iaW5lZCIsCiAgICBzdWJ0aXRsZSA9IHNwcmludGYoCiAgICAgICJFZmZlY3RvciBheGlzIG1lZGlhbnM6IE5haXZlPSUuMmYgfCBUQ009JS4yZiB8IFRFTT0lLjJmIHwgVGVtcmE9JS4yZlxuVHJlZyBsaW5lIG9taXR0ZWQg4oCUIG92ZXJsYXBzIFRDTTsgVHJlZy1saWtlIGFzc2lnbmVkIGJ5IGxhYmVsIG5vdCBwc2V1ZG90aW1lIiwKICAgICAgbmFpdmVfbWVkLCB0Y21fbWVkLCB0ZW1fbWVkLCB0ZW1yYV9tZWQpCiAgKSArCiAgdGhlbWUoCiAgICBwbG90LnRpdGxlICAgICAgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBmYWNlID0gImJvbGQiKSwKICAgIHBsb3Quc3VidGl0bGUgICA9IGVsZW1lbnRfdGV4dChzaXplID0gOSwgIGNvbG91ciA9ICJncmV5NDAiKSwKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIKICApCgpwcmludChwNmFfdjIpCnNhdmVfZmlnKHA2YV92MiwgIkZpZzZBdjJfRGVuc2l0eV9Ob1RyZWciLCB3ID0gOCwgaCA9IDYpCgpgYGAKCi0tLQoKIyAxNC4gRmlndXJlIDgg4oCUIFZpb2xpbjogVHJhbnNmZXJyZWQgUHNldWRvdGltZSBwZXIgTGFiZWwKCmBgYHtyIGZpZzgtdmlvbGluLXBlci1sYWJlbCwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTZ9CgpmaWc4IDwtIGdncGxvdCgKICAgIHF1ZXJ5X2RmICU+JQogICAgICBmaWx0ZXIoIWlzLm5hKHBzZXVkb3RpbWUpLCAhaXMubmEobGFiZWwpKSAlPiUKICAgICAgbXV0YXRlKGxhYmVsID0gZmFjdG9yKGxhYmVsLCBsZXZlbHMgPSBTVEFURV9PUkRFUikpLAogICAgYWVzKGxhYmVsLCBwc2V1ZG90aW1lLCBmaWxsID0gbGFiZWwpCiAgKSArCiAgZ2VvbV92aW9saW4oc2NhbGUgPSAid2lkdGgiLCB0cmltID0gRkFMU0UsIGFscGhhID0gMC44NSwgY29sb3VyID0gIndoaXRlIikgKwogIGdlb21fYm94cGxvdCh3aWR0aCA9IDAuMDcsIGZpbGwgPSAid2hpdGUiLCBjb2xvdXIgPSAiZ3JleTQwIiwKICAgICAgICAgICAgICAgb3V0bGllci5zaXplID0gMC40LCBvdXRsaWVyLmFscGhhID0gMC4zKSArCiAgZ2VvbV9obGluZShkYXRhID0gUkVGX01FRElBTlMsIGFlcyh5aW50ZXJjZXB0ID0gbWVkX3B0KSwKICAgICAgICAgICAgIGxpbmV0eXBlID0gImRvdHRlZCIsIGNvbG91ciA9ICJncmV5NTUiLCBsaW5ld2lkdGggPSAwLjYpICsKICAjIEFubm90YXRpb25zIGRyaXZlbiBlbnRpcmVseSBmcm9tIFJFRl9NRURJQU5TIOKAlCBubyBoYXJkLWNvZGVkIHkgdmFsdWVzCiAgZ2VvbV90ZXh0KAogICAgZGF0YSAgICAgICAgPSBSRUZfTUVESUFOUywKICAgIGFlcyh4ICAgICAgID0gMC41NSwKICAgICAgICB5ICAgICAgID0gbWVkX3B0LAogICAgICAgIGxhYmVsICAgPSBzcHJpbnRmKCJSZWY6ICVzICglLjJmKSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgZ3N1YigiQ0Q0ICIsICIiLCBhcy5jaGFyYWN0ZXIoc3RhdGUpKSwgbWVkX3B0KSksCiAgICBpbmhlcml0LmFlcyA9IEZBTFNFLAogICAgaGp1c3QgPSAwLCBzaXplID0gMi44LCBjb2xvdXIgPSAiZ3JleTQwIgogICkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IFNUQVRFX0NPTE9SUywgZ3VpZGUgPSAibm9uZSIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDI1LCA1KSkgKwogIHRoZW1lX2NsYXNzaWMoKSArCiAgbGFicygKICAgIHggICAgICAgID0gIlRyYW5zZmVycmVkIEF6aW11dGggbDIgTGFiZWwiLAogICAgeSAgICAgICAgPSAiVHJhbnNmZXJyZWQgUHNldWRvdGltZSIsCiAgICB0aXRsZSAgICA9ICJGaWd1cmUgOCDigJQgVHJhbnNmZXJyZWQgUHNldWRvdGltZSBwZXIgTGFiZWwgKEFsbCBDZWxsIExpbmVzKSIsCiAgICBzdWJ0aXRsZSA9ICJDRDQgTmFpdmUgaW50ZXJuYWwgY29udHJvbCBhdCBQVH40ICB8ICBUQ00gYnJvYWQgZGlzdHJpYnV0aW9uICB8ICBURU0gYXQgdGVybWluYWwgUFR+MjUiCiAgKSArCiAgdGhlbWUocGxvdC50aXRsZSAgICA9IGVsZW1lbnRfdGV4dChzaXplID0gMTMsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwLCBjb2xvdXIgPSAiZ3JleTQwIiksCiAgICAgICAgYXhpcy50ZXh0LnggICA9IGVsZW1lbnRfdGV4dChzaXplID0gMTAsIGZhY2UgPSAiYm9sZCIpKQoKcHJpbnQoZmlnOCkKc2F2ZV9maWcoZmlnOCwgIkZpZzhfVmlvbGluX1BzZXVkb3RpbWVfcGVyX0xhYmVsIiwgdyA9IDEyLCBoID0gNikKYGBgCgoKCgpgYGB7ciAsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD02fQoKIyBGb3IgVEVNIGFuZCBUZW1yYTogbWVkaWFuPTI0Ljg0IGZvciBib3RoIChUZW1yYSBuPTEwLCBhbGwgYXQgc2FtZSBQVCkuCiMgVXNlIGFjdHVhbCByZWZlcmVuY2UgbWF4IHZhbHVlcyB0byBzZXBhcmF0ZSB0aGUgbGluZXMgdmlzdWFsbHk6CiMgICBURU0gICBtYXggPSAyNS4wMCAgKGZyb20gZGlhZ25vc3RpYykKIyAgIFRlbXJhIG1heCA9IDI0Ljg0ICAoYWxsIDEwIGNlbGxzIGlkZW50aWNhbCkKIyBCZXR0ZXIgYXBwcm9hY2g6IHNob3cgVEVNIG1lZGlhbiBhbmQgVGVtcmEgbWVkaWFuIHNlcGFyYXRlbHkgYnV0CiMgYWNrbm93bGVkZ2UgaW4gc3VidGl0bGUgdGhleSBhcmUgbmVhcmx5IGlkZW50aWNhbC4KCiMgT3ZlcnJpZGU6IHVzZSBtZWFuIGluc3RlYWQgb2YgbWVkaWFuIGZvciBURU0vVGVtcmEgdG8gc2VwYXJhdGUgbGluZXMKUkVGX01FRElBTlNfRUZGRUNUT1JfUExPVCA8LSBSRUZfTUVESUFOU19FRkZFQ1RPUiAlPiUKICBtdXRhdGUobWVkX3B0ID0gY2FzZV93aGVuKAogICAgc3RhdGUgPT0gIkNENCBURU0iICAgICAgIH4gMjMuOTYyLCAgIyBtZWFuIGZyb20gZGlhZ25vc3RpYyAobWVkaWFuPTI0Ljg0IHNhbWUgYXMgVGVtcmEpCiAgICBzdGF0ZSA9PSAiQ0Q0IFRlbXJhL0NUTCIgfiAyNC44NDAsICAjIGFsbCAxMCBjZWxscyBpZGVudGljYWwKICAgIFRSVUUgfiBtZWRfcHQKICApLAogIGxhYmVsX3RleHQgPSBjYXNlX3doZW4oCiAgICBzdGF0ZSA9PSAiQ0Q0IFRFTSIgICAgICAgfiBzcHJpbnRmKCJSZWY6IFRFTSAobWVhbj0yMy45NikiKSwKICAgIHN0YXRlID09ICJDRDQgVGVtcmEvQ1RMIiB+IHNwcmludGYoIlJlZjogVGVtcmEgKDI0Ljg0LCBuPTEwKSIpLAogICAgc3RhdGUgPT0gIkNENCBOYWl2ZSIgICAgIH4gc3ByaW50ZigiUmVmOiBOYWl2ZSAoJS4yZikiLCBtZWRfcHQpLAogICAgc3RhdGUgPT0gIkNENCBUQ00iICAgICAgIH4gc3ByaW50ZigiUmVmOiBUQ00gKCUuMmYpIiwgbWVkX3B0KSwKICAgIFRSVUUgfiBhcy5jaGFyYWN0ZXIoc3RhdGUpCiAgKSkKCmZpZzh2MiA8LSBnZ3Bsb3QoCiAgICBxdWVyeV9kZiAlPiUKICAgICAgZmlsdGVyKCFpcy5uYShwc2V1ZG90aW1lKSwgIWlzLm5hKGxhYmVsKSkgJT4lCiAgICAgIG11dGF0ZShsYWJlbCA9IGZhY3RvcihsYWJlbCwgbGV2ZWxzID0gU1RBVEVfT1JERVIpKSwgICMgZHJvcD1GQUxTRSBrZWVwcyBUZW1yYSBheGlzCiAgICBhZXMobGFiZWwsIHBzZXVkb3RpbWUsIGZpbGwgPSBsYWJlbCkKICApICsKICBnZW9tX3Zpb2xpbihzY2FsZSA9ICJ3aWR0aCIsIHRyaW0gPSBGQUxTRSwgYWxwaGEgPSAwLjg1LCBjb2xvdXIgPSAid2hpdGUiKSArCiAgZ2VvbV9ib3hwbG90KHdpZHRoID0gMC4wNywgZmlsbCA9ICJ3aGl0ZSIsIGNvbG91ciA9ICJncmV5NDAiLAogICAgICAgICAgICAgICBvdXRsaWVyLnNpemUgPSAwLjQsIG91dGxpZXIuYWxwaGEgPSAwLjMpICsKICBnZW9tX2hsaW5lKAogICAgZGF0YSAgICAgID0gUkVGX01FRElBTlNfRUZGRUNUT1JfUExPVCwKICAgIGFlcyh5aW50ZXJjZXB0ID0gbWVkX3B0KSwKICAgIGxpbmV0eXBlICA9ICJkb3R0ZWQiLCBjb2xvdXIgPSAiZ3JleTU1IiwgbGluZXdpZHRoID0gMC42CiAgKSArCiAgZ2VvbV90ZXh0KAogICAgZGF0YSAgICAgICAgPSBSRUZfTUVESUFOU19FRkZFQ1RPUl9QTE9ULAogICAgYWVzKHggICAgICAgPSAwLjU1LAogICAgICAgIHkgICAgICAgPSBtZWRfcHQsCiAgICAgICAgbGFiZWwgICA9IGxhYmVsX3RleHQpLAogICAgaW5oZXJpdC5hZXMgPSBGQUxTRSwKICAgIGhqdXN0ID0gMCwgc2l6ZSA9IDIuOCwgY29sb3VyID0gImdyZXk0MCIKICApICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBTVEFURV9DT0xPUlMsIG5hLnZhbHVlID0gImdyZXk4MCIsIGd1aWRlID0gIm5vbmUiLAogICAgICAgICAgICAgICAgICAgIGRyb3AgPSBGQUxTRSkgKwogIHNjYWxlX3hfZGlzY3JldGUoZHJvcCA9IEZBTFNFKSArICAgIyDihpAgZm9yY2VzIENENCBUZW1yYS9DVEwgdG8gYXBwZWFyCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCAyNSwgNSkpICsKICB0aGVtZV9jbGFzc2ljKCkgKwogIGxhYnMoCiAgICB4ICAgICAgICA9ICJUcmFuc2ZlcnJlZCBBemltdXRoIGwyIExhYmVsIiwKICAgIHkgICAgICAgID0gIlRyYW5zZmVycmVkIFBzZXVkb3RpbWUiLAogICAgdGl0bGUgICAgPSAiRmlndXJlIDgg4oCUIFRyYW5zZmVycmVkIFBzZXVkb3RpbWUgcGVyIExhYmVsIChBbGwgQ2VsbCBMaW5lcykiLAogICAgc3VidGl0bGUgPSBzcHJpbnRmKAogICAgICAiRWZmZWN0b3IgYXhpczogTmFpdmU9JS4yZiB8IFRDTT0lLjJmIHwgVEVNfjI0Ljg0IHwgVGVtcmF+MjQuODQgKG49MTAsIGlkZW50aWNhbClcbkNENCBUZW1yYS9DVEw6IG5vIFPDqXphcnkgY2VsbHMgcmVjZWl2ZWQgdGhpcyBsYWJlbCDigJQgY29uc2lzdGVudCB3aXRoIFRDTSBhcnJlc3QgcGhlbm90eXBlXG5UcmVnIGxpbmUgb21pdHRlZCDigJQgb3ZlcmxhcHMgVENNOyBUcmVnLWxpa2UgYXNzaWduZWQgYnkgbGFiZWwiLAogICAgICBuYWl2ZV9tZWQsIHRjbV9tZWQpCiAgKSArCiAgdGhlbWUoCiAgICBwbG90LnRpdGxlICAgID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMywgZmFjZSA9ICJib2xkIiksCiAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSA5LCAgY29sb3VyID0gImdyZXk0MCIpLAogICAgYXhpcy50ZXh0LnggICA9IGVsZW1lbnRfdGV4dChzaXplID0gMTAsIGZhY2UgPSAiYm9sZCIpCiAgKQoKcHJpbnQoZmlnOHYyKQpzYXZlX2ZpZyhmaWc4djIsICJGaWc4djJfVmlvbGluX05vVHJlZyIsIHcgPSAxMiwgaCA9IDYpCgpgYGAKCmBgYHtyICwgZmlnLndpZHRoPTE4LCBmaWcuaGVpZ2h0PTd9CmZpZzh2MiA8LSBnZ3Bsb3QoCiAgICBxdWVyeV9kZiAlPiUKICAgICAgZmlsdGVyKCFpcy5uYShwc2V1ZG90aW1lKSwgIWlzLm5hKGxhYmVsKSkgJT4lCiAgICAgIG11dGF0ZShsYWJlbCA9IGZhY3RvcihsYWJlbCwgbGV2ZWxzID0gU1RBVEVfT1JERVIpKSwKICAgIGFlcyhsYWJlbCwgcHNldWRvdGltZSwgZmlsbCA9IGxhYmVsKQogICkgKwogIGdlb21fdmlvbGluKHNjYWxlID0gIndpZHRoIiwgdHJpbSA9IEZBTFNFLCBhbHBoYSA9IDAuODUsIGNvbG91ciA9ICJ3aGl0ZSIpICsKICBnZW9tX2JveHBsb3Qod2lkdGggPSAwLjA3LCBmaWxsID0gIndoaXRlIiwgY29sb3VyID0gImdyZXk0MCIsCiAgICAgICAgICAgICAgIG91dGxpZXIuc2l6ZSA9IDAuNCwgb3V0bGllci5hbHBoYSA9IDAuMykgKwogIGdlb21faGxpbmUoCiAgICBkYXRhICAgICAgPSBSRUZfTUVESUFOU19FRkZFQ1RPUiwKICAgIGFlcyh5aW50ZXJjZXB0ID0gbWVkX3B0KSwKICAgIGxpbmV0eXBlICA9ICJkb3R0ZWQiLCBjb2xvdXIgPSAiZ3JleTU1IiwgbGluZXdpZHRoID0gMC42CiAgKSArCiAgZ2VvbV90ZXh0KAogICAgZGF0YSAgICAgICAgPSBSRUZfTUVESUFOU19FRkZFQ1RPUiwKICAgIGFlcyh4ICAgICAgID0gMC41NSwKICAgICAgICB5ICAgICAgID0gbWVkX3B0LAogICAgICAgIGxhYmVsICAgPSBzcHJpbnRmKCJSZWY6ICVzICglLjJmKSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgZ3N1YigiQ0Q0ICIsICIiLCBhcy5jaGFyYWN0ZXIoc3RhdGUpKSwgbWVkX3B0KSksCiAgICBpbmhlcml0LmFlcyA9IEZBTFNFLAogICAgaGp1c3QgPSAwLCBzaXplID0gMi44LCBjb2xvdXIgPSAiZ3JleTQwIgogICkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IFNUQVRFX0NPTE9SUywgZ3VpZGUgPSAibm9uZSIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDI1LCA1KSkgKwogIHRoZW1lX2NsYXNzaWMoKSArCiAgbGFicygKICAgIHggICAgICAgID0gIlRyYW5zZmVycmVkIEF6aW11dGggbDIgTGFiZWwiLAogICAgeSAgICAgICAgPSAiVHJhbnNmZXJyZWQgUHNldWRvdGltZSIsCiAgICB0aXRsZSAgICA9ICJGaWd1cmUgOCDigJQgVHJhbnNmZXJyZWQgUHNldWRvdGltZSBwZXIgTGFiZWwgKEFsbCBDZWxsIExpbmVzKSIsCiAgICBzdWJ0aXRsZSA9IHNwcmludGYoCiAgICAgICJFZmZlY3RvciBheGlzIHJlZmVyZW5jZSBsaW5lczogTmFpdmU9JS4yZiB8IFRDTT0lLjJmIHwgVEVNPSUuMmYgfCBUZW1yYT0lLjJmXG5UcmVnIGxpbmUgb21pdHRlZCDigJQgVHJlZyAoUFQ9JS4yZikgb3ZlcmxhcHMgVENNOyBUcmVnLWxpa2UgdmlvbGluIHNob3duIGFzIGJpb2xvZ2ljYWwgY29uZmlybWF0aW9uIiwKICAgICAgbmFpdmVfbWVkLCB0Y21fbWVkLCB0ZW1fbWVkLCB0ZW1yYV9tZWQsIHRyZWdfbWVkKQogICkgKwogIHRoZW1lKAogICAgcGxvdC50aXRsZSAgICA9IGVsZW1lbnRfdGV4dChzaXplID0gMTMsIGZhY2UgPSAiYm9sZCIpLAogICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gOSwgIGNvbG91ciA9ICJncmV5NDAiKSwKICAgIGF4aXMudGV4dC54ICAgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwLCBmYWNlID0gImJvbGQiKQogICkKCnByaW50KGZpZzh2MikKc2F2ZV9maWcoZmlnOHYyLCAiRmlnOHYyX1Zpb2xpbl9Ob1RyZWciLCB3ID0gMTIsIGggPSA2KQoKYGBgCgoKLS0tCgojIDE1LiBGaWd1cmUgOSDigJQgQ3Jvc3MtVGFibGU6IExhYmVsIHZzIFBzZXVkb3RpbWUgQmluIChQUklNQVJZIFJFU1VMVCkKCmBgYHtyICwgZmlnLndpZHRoPTE4LCBmaWcuaGVpZ2h0PTd9Cgpjcm9zc19kZiA8LSBxdWVyeV9kZiAlPiUKICBmaWx0ZXIoIWlzLm5hKGxhYmVsKSwgIWlzLm5hKHBzZXVkb3RpbWVfYmluKSkgJT4lCiAgbXV0YXRlKGxhYmVsID0gZmFjdG9yKGxhYmVsLCBsZXZlbHMgPSBTVEFURV9PUkRFUikpICU+JQogIGNvdW50KGxhYmVsLCBwc2V1ZG90aW1lX2JpbikgJT4lCiAgZ3JvdXBfYnkobGFiZWwpICU+JQogIG11dGF0ZShwY3QgPSAxMDAgKiBuIC8gc3VtKG4pKSAlPiUKICB1bmdyb3VwKCkKCnRjbV9kaXNjb3JkYW50IDwtIGNyb3NzX2RmICU+JQogIGZpbHRlcihsYWJlbCA9PSAiQ0Q0IFRDTSIsCiAgICAgICAgIHBzZXVkb3RpbWVfYmluICVpbiUgYygiVEVNLWxpa2UiLCAiVGVtcmEtbGlrZSIpKSAlPiUKICBzdW1tYXJpc2UocGN0ID0gc3VtKHBjdCkpICU+JQogIHB1bGwocGN0KQoKZmlnOSA8LSBnZ3Bsb3QoY3Jvc3NfZGYsIGFlcyhwc2V1ZG90aW1lX2JpbiwgcGN0LCBmaWxsID0gcHNldWRvdGltZV9iaW4pKSArCiAgZ2VvbV9jb2wod2lkdGggPSAwLjcpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gc3ByaW50ZigiJS4xZiUlIiwgcGN0KSksCiAgICAgICAgICAgIHZqdXN0ID0gLTAuMywgc2l6ZSA9IDMuNSwgZm9udGZhY2UgPSAiYm9sZCIpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBCSU5fQ09MT1JTLCBndWlkZSA9ICJub25lIikgKwogIHNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBjKDAsIDApLCBsaW1pdHMgPSBjKDAsIDExNSkpICsKICBmYWNldF93cmFwKH4gbGFiZWwsIG5yb3cgPSAxLCBzY2FsZXMgPSAiZnJlZV94IikgKwogIHRoZW1lX2NsYXNzaWMoKSArCiAgdGhlbWUoCiAgICBzdHJpcC50ZXh0ICAgICAgID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIsIHNpemUgPSAxMSksCiAgICBzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAiZ3JleTk1IiwgY29sb3VyID0gImdyZXk2MCIpLAogICAgYXhpcy50ZXh0LnggICAgICA9IGVsZW1lbnRfdGV4dChzaXplID0gOSwgZmFjZSA9ICJib2xkIiwgYW5nbGUgPSAzMCwgaGp1c3QgPSAxKSwKICAgIHBsb3QudGl0bGUgICAgICAgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBmYWNlID0gImJvbGQiKSwKICAgIHBsb3Quc3VidGl0bGUgICAgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwLCBjb2xvdXIgPSAiZ3JleTQwIikKICApICsKICBsYWJzKAogICAgeCAgICAgICAgPSAiUHNldWRvdGltZSBCaW4iLAogICAgeSAgICAgICAgPSAiJSBvZiBDZWxscyB3aXRoIFRoaXMgTGFiZWwiLAogICAgdGl0bGUgICAgPSAiRmlndXJlIDkg4oCUIENyb3NzLVRhYmxlOiBUcmFuc2ZlcnJlZCBMYWJlbCB2cyBQc2V1ZG90aW1lIEJpbiAgW1BSSU1BUlkgUkVTVUxUXSIsCiAgICBzdWJ0aXRsZSA9IHNwcmludGYoCiAgICAgICJUQ00tbGFiZWxsZWQgY2VsbHM6ICUuMWYlJSBkaXNjb3JkYW50IChURU0tbGlrZSBvciBUZW1yYS1saWtlKVxuVHJlZyBwYW5lbDogMTAwJSUgVHJlZy1saWtlIGJ5IGRlc2lnbiIsCiAgICAgIHRjbV9kaXNjb3JkYW50KQogICkKCnByaW50KGZpZzkpCnNhdmVfZmlnKGZpZzksICJGaWc5X0Nyb3NzVGFibGVfTGFiZWxfdnNfQmluX1BSSU1BUlkiLCB3ID0gMTgsIGggPSA3KQpgYGAKCi0tLQoKIyAxNi4gU3VwcGxlbWVudGFyeSBGaWd1cmVzCgojIyBTdXBwIFMxIOKAlCBQZXIgQ2VsbCBMaW5lIFBzZXVkb3RpbWUgRGVuc2l0eSAoRmFjZXRlZCkKCmBgYHtyIHN1cHBTMS1kZW5zaXR5LWZhY2V0LCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9N30KCnN1cHBTMSA8LSBnZ3Bsb3QoCiAgICBxdWVyeV9kZiAlPiUgZmlsdGVyKCFpcy5uYShwc2V1ZG90aW1lKSwgIWlzLm5hKGNlbGxfbGluZSkpLAogICAgYWVzKHggPSBwc2V1ZG90aW1lLCBmaWxsID0gY2VsbF9saW5lLCBjb2xvdXIgPSBjZWxsX2xpbmUpCiAgKSArCiAgZ2VvbV9kZW5zaXR5KGFscGhhID0gMC4zNSwgbGluZXdpZHRoID0gMC44KSArCiAgZ2VvbV92bGluZShkYXRhID0gUkVGX01FRElBTlMsIGFlcyh4aW50ZXJjZXB0ID0gbWVkX3B0KSwKICAgICAgICAgICAgIGNvbG91ciA9ICJncmV5NDUiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBsaW5ld2lkdGggPSAwLjUpICsKICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIlNldDIiLCBndWlkZSA9ICJub25lIikgKwogIHNjYWxlX2NvbG91cl9icmV3ZXIocGFsZXR0ZSA9ICJTZXQyIiwgZ3VpZGUgPSAibm9uZSIpICsKICBzY2FsZV94X2NvbnRpbnVvdXMobGltaXRzID0gYygwLCAyNiksIGJyZWFrcyA9IHNlcSgwLCAyNSwgNSkpICsKICBmYWNldF93cmFwKH4gY2VsbF9saW5lLCBucm93ID0gMikgKwogIHRoZW1lX2NsYXNzaWMoKSArCiAgdGhlbWUoc3RyaXAudGV4dCAgICA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiKSwKICAgICAgICBwbG90LnRpdGxlICAgID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gOSwgIGNvbG91ciA9ICJncmV5NDAiKSkgKwogIGxhYnMoeCA9ICJUcmFuc2ZlcnJlZCBQc2V1ZG90aW1lIiwgeSA9ICJEZW5zaXR5IiwKICAgICAgIHRpdGxlICAgID0gIlN1cHAgUzEg4oCUIFBlciBDZWxsIExpbmUgUHNldWRvdGltZSBEZW5zaXR5IiwKICAgICAgIHN1YnRpdGxlID0gIkdyZXkgZGFzaGVkIGxpbmVzID0gZGF0YS1kZXJpdmVkIHJlZmVyZW5jZSBzdGF0ZSBtZWRpYW5zIikKCnByaW50KHN1cHBTMSkKc2F2ZV9maWcoc3VwcFMxLCAiU3VwcFMxX0RlbnNpdHlfUGVyTGluZV9GYWNldCIsIHcgPSAxMiwgaCA9IDcpCmBgYAoKIyMgU3VwcCBTMiDigJQgUmlkZ2VwbG90IFN0YW5kYWxvbmUKCmBgYHtyIHN1cHBTMi1yaWRnZXBsb3QsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD03fQoKc3VwcFMyIDwtIHA2YiArCiAgZ2d0aXRsZSgKICAgICJTdXBwIFMyIOKAlCBQZXIgQ2VsbCBMaW5lIFBzZXVkb3RpbWUgUmlkZ2VwbG90IiwKICAgIHN1YnRpdGxlID0gc3ByaW50ZigKICAgICAgIkJpbiBib3VuZGFyaWVzOiBOYWl2ZXxUQ009JS4yZiB8IFRDTXxURU09JS4yZiB8IFRFTXxUZW1yYT0lLjJmIiwKICAgICAgQklOX05BSVZFX1RDTSwgQklOX1RDTV9URU0sIEJJTl9URU1fVEVNUkEpCiAgKQoKcHJpbnQoc3VwcFMyKQpzYXZlX2ZpZyhzdXBwUzIsICJTdXBwUzJfUmlkZ2VwbG90X1BlckxpbmUiLCB3ID0gMTAsIGggPSA3KQpgYGAKCiMjIFN1cHAgUzMg4oCUIFZpb2xpbiBwZXIgTGFiZWwgeCBDZWxsIExpbmUgKEZhY2V0ZWQpCgpgYGB7ciBzdXBwUzMtdmlvbGluLWxhYmVsLWxpbmUsIGZpZy53aWR0aD0xNiwgZmlnLmhlaWdodD0xMH0KCnN1cHBTMyA8LSBnZ3Bsb3QoCiAgICBxdWVyeV9kZiAlPiUKICAgICAgZmlsdGVyKCFpcy5uYShsYWJlbCksICFpcy5uYShjZWxsX2xpbmUpLCAhaXMubmEocHNldWRvdGltZSkpICU+JQogICAgICBtdXRhdGUobGFiZWwgPSBmYWN0b3IobGFiZWwsIGxldmVscyA9IFNUQVRFX09SREVSKSksCiAgICBhZXMoY2VsbF9saW5lLCBwc2V1ZG90aW1lLCBmaWxsID0gY2VsbF9saW5lKQogICkgKwogIGdlb21fdmlvbGluKHNjYWxlID0gIndpZHRoIiwgdHJpbSA9IEZBTFNFLCBhbHBoYSA9IDAuOCwgY29sb3VyID0gIndoaXRlIikgKwogIGdlb21fYm94cGxvdCh3aWR0aCA9IDAuMDcsIGZpbGwgPSAid2hpdGUiLCBjb2xvdXIgPSAiZ3JleTQwIiwKICAgICAgICAgICAgICAgb3V0bGllci5zaXplID0gMC4yLCBvdXRsaWVyLmFscGhhID0gMC4zKSArCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJTZXQyIiwgZ3VpZGUgPSAibm9uZSIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDI1LCA1KSkgKwogIGZhY2V0X3dyYXAofiBsYWJlbCwgbnJvdyA9IDEpICsKICB0aGVtZV9jbGFzc2ljKCkgKwogIHRoZW1lKAogICAgc3RyaXAudGV4dCAgICAgICA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiLCBzaXplID0gMTApLAogICAgc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gImdyZXk5NSIsIGNvbG91ciA9ICJncmV5NjAiKSwKICAgIGF4aXMudGV4dC54ICAgICAgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDksIGZhY2UgPSAiYm9sZCIsIGFuZ2xlID0gMzAsIGhqdXN0ID0gMSksCiAgICBwbG90LnRpdGxlICAgICAgID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgZmFjZSA9ICJib2xkIiksCiAgICBwbG90LnN1YnRpdGxlICAgID0gZWxlbWVudF90ZXh0KHNpemUgPSA5LCAgY29sb3VyID0gImdyZXk0MCIpCiAgKSArCiAgbGFicyh4ID0gIkNlbGwgTGluZSIsIHkgPSAiVHJhbnNmZXJyZWQgUHNldWRvdGltZSIsCiAgICAgICB0aXRsZSAgICA9ICJTdXBwIFMzIOKAlCBWaW9saW46IFBzZXVkb3RpbWUgcGVyIExhYmVsIHggQ2VsbCBMaW5lIiwKICAgICAgIHN1YnRpdGxlID0gIkNvbXBsZW1lbnRzIEZpZyA5IOKAlCBzaG93cyB3aXRoaW4tc3RhdGUgcGVyLWxpbmUgaGV0ZXJvZ2VuZWl0eSIpCgpwcmludChzdXBwUzMpCnNhdmVfZmlnKHN1cHBTMywgIlN1cHBTM19WaW9saW5fTGFiZWxfeF9MaW5lIiwgdyA9IDE2LCBoID0gMTApCmBgYAoKIyMgU3VwcCBTNCDigJQgUHNldWRvdGltZSBCaW4gQmFyIENoYXJ0IHdpdGggRnVsbCBQZXJjZW50YWdlcwoKYGBge3Igc3VwcFM0LWJpbnMtZGV0YWlsZWQsIGZpZy53aWR0aD0xNCwgZmlnLmhlaWdodD02fQoKc3VwcFM0IDwtIGdncGxvdChiaW5fZGYsIGFlcyhjZWxsX2xpbmUsIHBjdCwgZmlsbCA9IHBzZXVkb3RpbWVfYmluKSkgKwogIGdlb21fY29sKHdpZHRoID0gMC43KSArCiAgZ2VvbV90ZXh0KGRhdGEgPSBiaW5fZGYgJT4lIGZpbHRlcihwY3QgPiAyKSwKICAgICAgICAgICAgYWVzKGxhYmVsID0gc3ByaW50ZigiJS4xZiUlIiwgcGN0KSksCiAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fc3RhY2sodmp1c3QgPSAwLjUpLAogICAgICAgICAgICBzaXplID0gMy41LCBjb2xvdXIgPSAid2hpdGUiLCBmb250ZmFjZSA9ICJib2xkIikgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IEJJTl9DT0xPUlMsIG5hbWUgPSAiUHNldWRvdGltZSBiaW4iKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwgMCksIGxpbWl0cyA9IGMoMCwgMTAxKSkgKwogIHRoZW1lX2NsYXNzaWMoKSArCiAgbGFicygKICAgIHggICAgICAgID0gIkNlbGwgTGluZSIsCiAgICB5ICAgICAgICA9ICIlIENlbGxzIiwKICAgIHRpdGxlICAgID0gIlN1cHAgUzQg4oCUIFBzZXVkb3RpbWUgQmluIENvbXBvc2l0aW9uIHBlciBDZWxsIExpbmUiLAogICAgc3VidGl0bGUgPSBzcHJpbnRmKAogICAgICAiQmluIGJvdW5kYXJpZXM6IE5haXZlfFRDTT0lLjJmICB8ICBUQ018VEVNPSUuMmYgIHwgIFRFTXxUZW1yYT0lLjJmICB8ICBUcmVnPWxhYmVsLWJhc2VkIiwKICAgICAgQklOX05BSVZFX1RDTSwgQklOX1RDTV9URU0sIEJJTl9URU1fVEVNUkEpCiAgKSArCiAgdGhlbWUocGxvdC50aXRsZSAgICA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDksICBjb2xvdXIgPSAiZ3JleTQwIiksCiAgICAgICAgYXhpcy50ZXh0LnggICA9IGVsZW1lbnRfdGV4dChzaXplID0gMTEsIGZhY2UgPSAiYm9sZCIpKQoKcHJpbnQoc3VwcFM0KQpzYXZlX2ZpZyhzdXBwUzQsICJTdXBwUzRfQmluc19CYXJDaGFydF9EZXRhaWxlZCIsIHcgPSAxNCwgaCA9IDYpCmBgYAoKLS0tCgojIDE3LiBGaWd1cmUgTWFuaWZlc3QKCmBgYHtyIG1hbmlmZXN0LCBlY2hvPUZBTFNFfQptYW5pZmVzdCA8LSBkYXRhLmZyYW1lKAogIEZpbGUgPSBjKAogICAgIkZpZzdfRm91clBhbmVsX1N1bW1hcnlfRklYRUQiLAogICAgIkZpZzdBX1RyYW5zZmVycmVkX0xhYmVscyIsCiAgICAiRmlnN0JfVHJhbnNmZXJyZWRfUHNldWRvdGltZV9GSVhFRCIsCiAgICAiRmlnN0NfUHNldWRvdGltZV9CaW5zX1VNQVAiLAogICAgIkZpZzdEX0xhYmVsX0JhckNoYXJ0X3Blcl9MaW5lIiwKICAgICJGaWcxX1BzZXVkb3RpbWVfVmlvbGluX3Blcl9MaW5lIiwKICAgICJGaWcyX1VNQVBfUHNldWRvdGltZV9hbmRfTGFiZWwiLAogICAgIkZpZzNfTGFiZWxzX1BlckxpbmVfVU1BUCIsCiAgICAiRmlnNF9QZXJMaW5lX0ZhY2V0X1VNQVAiLAogICAgIkZpZzVfTGFiZWxfdnNfUHNldWRvdGltZV9CaW5zIiwKICAgICJGaWc1QV9MYWJlbF9UcmFuc2Zlcl9CYXJDaGFydCIsCiAgICAiRmlnNUJfUHNldWRvdGltZV9CaW5zX0JhckNoYXJ0IiwKICAgICJGaWc2X0RlbnNpdHlfYW5kX1JpZGdlcGxvdCIsCiAgICAiRmlnNkFfRGVuc2l0eV9PdmVyYWxsIiwKICAgICJGaWc2Ql9SaWRnZXBsb3RfUGVyTGluZSIsCiAgICAiRmlnOF9WaW9saW5fUHNldWRvdGltZV9wZXJfTGFiZWwiLAogICAgIkZpZzlfQ3Jvc3NUYWJsZV9MYWJlbF92c19CaW5fUFJJTUFSWSIsCiAgICAiU3VwcFMxX0RlbnNpdHlfUGVyTGluZV9GYWNldCIsCiAgICAiU3VwcFMyX1JpZGdlcGxvdF9QZXJMaW5lIiwKICAgICJTdXBwUzNfVmlvbGluX0xhYmVsX3hfTGluZSIsCiAgICAiU3VwcFM0X0JpbnNfQmFyQ2hhcnRfRGV0YWlsZWQiCiAgKSwKICBEZXNjcmlwdGlvbiA9IGMoCiAgICAiRm91ci1wYW5lbDogbGFiZWxzIHwgcHNldWRvdGltZSAoRklYRUQpIHwgYmlucyB8IGxhYmVsIGJhciIsCiAgICAiU2V6YXJ5IFVNQVAg4oCUIHRyYW5zZmVycmVkIEF6aW11dGggbDIgbGFiZWwiLAogICAgIlNlemFyeSBVTUFQIOKAlCB0cmFuc2ZlcnJlZCBwc2V1ZG90aW1lLCBGSVhFRCAwLTI1IGNvbG91ciBzY2FsZSIsCiAgICAiU2V6YXJ5IFVNQVAg4oCUIHBzZXVkb3RpbWUgYmluIGNvbG91ciIsCiAgICAiVHJhbnNmZXJyZWQgbGFiZWwgcHJvcG9ydGlvbnMgcGVyIGNlbGwgbGluZSBiYXIgY2hhcnQiLAogICAgIlBzZXVkb3RpbWUgdmlvbGluIHBlciBjZWxsIGxpbmUgd2l0aCBkYXRhLWRlcml2ZWQgcmVmZXJlbmNlIG1lZGlhbnMiLAogICAgIlVNQVAgcHNldWRvdGltZSBncmFkaWVudCArIGxhYmVsIGlkZW50aXR5IHNpZGUgYnkgc2lkZSIsCiAgICAiTGFiZWwgYmFyIGNoYXJ0ICsgcGVyIGNlbGwgbGluZSBVTUFQIGZhY2V0cyIsCiAgICAiUGVyIGNlbGwgbGluZSBmYWNldCBVTUFQIOKAlCBrZXkgaW50ZXItbGluZSBoZXRlcm9nZW5laXR5IGZpZ3VyZSIsCiAgICAiRHVhbCBiYXIgY2hhcnQ6IGxhYmVscyAodG9wKSBhbmQgcHNldWRvdGltZSBiaW5zIChib3R0b20pIiwKICAgICJMYWJlbCB0cmFuc2ZlciBwcm9wb3J0aW9ucyBiYXIgY2hhcnQgb25seSIsCiAgICAiUHNldWRvdGltZSBiaW4gcHJvcG9ydGlvbnMgYmFyIGNoYXJ0IG9ubHkiLAogICAgIk92ZXJhbGwgcHNldWRvdGltZSBkZW5zaXR5ICsgcGVyLWxpbmUgcmlkZ2VwbG90IiwKICAgICJPdmVyYWxsIHBzZXVkb3RpbWUgZGVuc2l0eSB3aXRoIGRhdGEtZGVyaXZlZCByZWZlcmVuY2UgbWVkaWFuIGxpbmVzIiwKICAgICJQZXIgY2VsbCBsaW5lIHBzZXVkb3RpbWUgcmlkZ2VwbG90IHdpdGggYmluIGJvdW5kYXJpZXMiLAogICAgIlZpb2xpbjogdHJhbnNmZXJyZWQgcHNldWRvdGltZSBwZXIgbGFiZWwg4oCUIGRhdGEtZGVyaXZlZCBhbm5vdGF0aW9ucyIsCiAgICAiQ3Jvc3MtdGFibGU6IGxhYmVsIHZzIHBzZXVkb3RpbWUgYmluIOKAlCBQUklNQVJZIFJFU1VMVCIsCiAgICAiUGVyIGNlbGwgbGluZSBwc2V1ZG90aW1lIGRlbnNpdHkgZmFjZXRlZCIsCiAgICAiUGVyIGNlbGwgbGluZSByaWRnZXBsb3Qgc3RhbmRhbG9uZSIsCiAgICAiVmlvbGluOiBwc2V1ZG90aW1lIHBlciBsYWJlbCB4IGNlbGwgbGluZSBmYWNldGVkIiwKICAgICJQc2V1ZG90aW1lIGJpbiBiYXIgY2hhcnQgd2l0aCBmdWxsIHBlcmNlbnRhZ2VzIGxhYmVsbGVkIgogICksCiAgTWFudXNjcmlwdCA9IGMoCiAgICAiRmlnIDEgY29tYmluZWQiLCAiRmlnIDFBIiwgIkZpZyAxQiAoRklYRUQpIiwgIkZpZyAxQyIsICJGaWcgMUQiLAogICAgIlN1cHAiLCAiRmlnIDIiLCAiRmlnIDMiLCAiRmlnIDQgKEtFWSkiLAogICAgIkZpZyA1IiwgIuKAlCIsICJGaWcgNUIiLAogICAgIlN1cHAiLCAiU3VwcCBTMyIsICJTdXBwIFM0IiwKICAgICJTdXBwIFM1IiwgIkZpZyAyIChQUklNQVJZKSIsCiAgICAiU3VwcCBTMSIsICJTdXBwIFMyIiwgIlN1cHAgUzYiLCAiU3VwcCIKICApLAogIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRQopCgprYWJsZShtYW5pZmVzdCwKICAgICAgY2FwdGlvbiA9ICJBbGwgMjEgZmlndXJlcyDigJQgRmlndXJlcy9QTkcvKi5wbmcgKDMwMCBkcGkpIGFuZCBGaWd1cmVzL1BERi8qLnBkZiIpICU+JQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwiY29uZGVuc2VkIiwiaG92ZXIiKSwKICAgICAgICAgICAgICAgIGZ1bGxfd2lkdGggPSBUUlVFKSAlPiUKICByb3dfc3BlYyh3aGljaChncmVwbCgiRklYRUR8UFJJTUFSWSIsIG1hbmlmZXN0JEZpbGUpKSwKICAgICAgICAgICBib2xkID0gVFJVRSwgYmFja2dyb3VuZCA9ICIjZmZmM2NkIikKYGBgCgotLS0KCiMgMTguIFNlc3Npb24gSW5mbwoKYGBge3Igc2Vzc2lvbi1pbmZvfQpzZXNzaW9uSW5mbygpCmBgYAo=