1 Load Libraries

library(dplyr)
library(ggplot2)
library(Seurat)
library(patchwork)
library(slingshot)
library(SingleCellExperiment)
library(tidyr)
library(ggrepel)
library(pheatmap)

options(timeout = 300)
set.seed(123)

# Colour palette — consistent throughout
flow_colours <- c(
  "Tnaive" = "#4472C4",
  "Tcm"    = "#ED7D31",
  "Tem"    = "#A9D18E",
  "Temra"  = "#C00000",
  "Treg"   = "#FFD700"
)

2 Load Object

clean_obj <- readRDS("../CD4_reference_clean_Azimuth_ready_for_Slingshot.rds")

cat("Total cells loaded:", ncol(clean_obj), "\n")
Total cells loaded: 11466 
cat("\nAzimuth l2 composition:\n")

Azimuth l2 composition:
print(table(clean_obj$predicted.celltype.l2))

    CD4 Naive       CD4 TCM       CD4 TEM CD4 Temra/CTL          Treg 
         2037          9067           145            10           207 

3 Assign Trajectory States

# Map Azimuth l2 → trajectory state labels
# CD4 CTL merged into Temra:
#   - Only 10 cells — too few for stable Slingshot endpoint
#   - Biologically justified: CD4 CTL is cytotoxic CD4, equivalent to Temra

clean_obj$trajectory_state <- dplyr::recode(
  clean_obj$predicted.celltype.l2,
  "CD4 Naive"     = "Tnaive",
  "CD4 TCM"       = "Tcm",
  "CD4 TEM"       = "Tem",
  "CD4 Temra/CTL"       = "Temra",   # merged into Temra
  "Treg"          = "Treg",
  .default        = NA_character_
)

clean_obj$trajectory_state <- factor(
  clean_obj$trajectory_state,
  levels = c("Tnaive","Tcm","Tem","Temra","Treg")
)

cat("Trajectory state distribution:\n")
Trajectory state distribution:
print(table(clean_obj$trajectory_state, useNA = "ifany"))

Tnaive    Tcm    Tem  Temra   Treg 
  2037   9067    145     10    207 
# Visualise on UMAP
DimPlot(clean_obj,
        group.by = "trajectory_state",
        cols     = flow_colours,
        na.value = "grey90",
        label    = TRUE,
        repel    = TRUE) +
  ggtitle("Trajectory states — Azimuth l2 mapped to flow-equivalent labels")

4 Run Slingshot Trajectory

# ── Prepare input ──
# Use UMAP coordinates directly — no SCE conversion needed
umap_coords <- Embeddings(clean_obj, "umap")

# Only use cells with a confirmed trajectory state
keep_cells  <- !is.na(clean_obj$trajectory_state)
umap_sub    <- umap_coords[keep_cells, ]
cluster_sub <- droplevels(clean_obj$trajectory_state[keep_cells])

cat("Cells entering Slingshot:\n")
Cells entering Slingshot:
print(table(cluster_sub))
cluster_sub
Tnaive    Tcm    Tem  Temra   Treg 
  2037   9067    145     10    207 
# ── Run Slingshot ──
# start.clus = Tnaive (confirmed earliest state)
# end.clus   = Temra (effector/cytotoxic arm) + Treg (regulatory arm)
# Two terminal states = two lineages
sce_sling <- slingshot(
  data          = umap_sub,
  clusterLabels = cluster_sub,
  start.clus    = "Tnaive",
  end.clus      = c("Temra", "Treg"),
  approx_points = 100,      # reduced from 300 — less smoothing
  stretch       = 0         # prevents curves extending beyond terminal clusters
)

cat("\nLineages after fix:\n")

Lineages after fix:
print(slingLineages(sce_sling))
$Lineage1
[1] "Tnaive" "Tcm"    "Tem"    "Temra" 

$Lineage2
[1] "Tnaive" "Tcm"    "Treg"  
cat("\nLineages inferred:\n")

Lineages inferred:
print(slingLineages(sce_sling))
$Lineage1
[1] "Tnaive" "Tcm"    "Tem"    "Temra" 

$Lineage2
[1] "Tnaive" "Tcm"    "Treg"  
# Expected:
# Lineage 1: Tnaive → Tcm → Tem → Temra  (effector arm)
# Lineage 2: Tnaive → Tcm → Treg          (regulatory arm)

5 Extract Pseudotime

# Extract pseudotime matrix (cells × lineages)
pt_matrix           <- slingPseudotime(sce_sling)
colnames(pt_matrix) <- c("PT_effector","PT_regulatory")

cat("Pseudotime summary — effector lineage:\n")
Pseudotime summary — effector lineage:
print(summary(pt_matrix[, "PT_effector"]))
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
  0.000   1.915   4.670   5.409   8.889  12.911    1784 
cat("\nPseudotime summary — regulatory lineage:\n")

Pseudotime summary — regulatory lineage:
print(summary(pt_matrix[, "PT_regulatory"]))
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
  0.000   2.048   6.640   6.974  10.844  17.587    1199 
# Extract pseudotime matrix (cells × lineages)
pt_matrix           <- slingPseudotime(sce_sling)
colnames(pt_matrix) <- c("PT_effector","PT_regulatory")

cat("Pseudotime summary — effector lineage:\n")
Pseudotime summary — effector lineage:
print(summary(pt_matrix[, "PT_effector"]))
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
  0.000   1.915   4.670   5.409   8.889  12.911    1784 
cat("\nPseudotime summary — regulatory lineage:\n")

Pseudotime summary — regulatory lineage:
print(summary(pt_matrix[, "PT_regulatory"]))
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
  0.000   2.048   6.640   6.974  10.844  17.587    1199 
# ── Add pseudotime to metadata via direct dataframe assignment ──
# IMPORTANT: Do NOT use clean_obj$PT_effector <- ... 
# That triggers Seurat's validObject() which fails on large objects
# Instead extract meta.data, modify as plain R dataframe, write back

meta <- clean_obj@meta.data
meta$PT_effector   <- NA_real_
meta$PT_regulatory <- NA_real_

common <- intersect(rownames(pt_matrix), rownames(meta))
meta[common, "PT_effector"]   <- pt_matrix[common, "PT_effector"]
meta[common, "PT_regulatory"] <- pt_matrix[common, "PT_regulatory"]

clean_obj@meta.data <- meta

cat("\nCells with effector pseudotime:  ",
    sum(!is.na(clean_obj@meta.data$PT_effector)), "\n")

Cells with effector pseudotime:   9682 
cat("Cells with regulatory pseudotime:",
    sum(!is.na(clean_obj@meta.data$PT_regulatory)), "\n")
Cells with regulatory pseudotime: 10267 

6 Trajectory Plot — Curves on UMAP

# ── Extract curve coordinates ──
curves <- slingCurves(sce_sling)

# Curve 1 — effector lineage (Tnaive → Tcm → Tem → Temra)
curve1_df           <- as.data.frame(curves[[1]]$s[curves[[1]]$ord, ])
colnames(curve1_df) <- c("UMAP_1","UMAP_2")

# Curve 2 — regulatory lineage (Tnaive → Tcm → Treg)
curve2_df           <- as.data.frame(curves[[2]]$s[curves[[2]]$ord, ])
colnames(curve2_df) <- c("UMAP_1","UMAP_2")

# ── UMAP base dataframe ──
umap_df           <- as.data.frame(umap_coords)
colnames(umap_df) <- c("UMAP_1","UMAP_2")
umap_df$trajectory_state <- clean_obj$trajectory_state
umap_df <- umap_df %>% filter(!is.na(trajectory_state))

# ── Milestone centroids ──
milestones <- umap_df %>%
  group_by(trajectory_state) %>%
  summarise(
    UMAP_1 = median(UMAP_1),
    UMAP_2 = median(UMAP_2),
    .groups = "drop"
  )

# ── Main plot ──
p_traj <- ggplot(umap_df,
                 aes(x = UMAP_1, y = UMAP_2,
                     colour = trajectory_state)) +
  geom_point(size = 0.4, alpha = 0.5) +
  # Effector lineage — solid black line
  geom_path(data        = curve1_df,
            aes(x = UMAP_1, y = UMAP_2),
            colour      = "black",
            linewidth   = 1.5,
            inherit.aes = FALSE) +
  # Regulatory lineage — dashed black line
  geom_path(data        = curve2_df,
            aes(x = UMAP_1, y = UMAP_2),
            colour      = "black",
            linewidth   = 1.5,
            linetype    = "dashed",
            inherit.aes = FALSE) +
  # Milestone labels
  ggrepel::geom_label_repel(
    data        = milestones,
    aes(x     = UMAP_1,
        y     = UMAP_2,
        label = trajectory_state,
        fill  = trajectory_state),
    colour      = "white",
    fontface    = "bold",
    size        = 5,
    box.padding = 0.8,
    inherit.aes = FALSE
  ) +
  scale_colour_manual(values = flow_colours, name = "State") +
  scale_fill_manual(values   = flow_colours, guide = "none") +
  theme_classic() +
  theme(
    plot.title    = element_text(size = 14, face = "bold"),
    plot.subtitle = element_text(size = 11),
    legend.position = "right"
  ) +
  labs(
    title    = "CD4 T-cell Differentiation Trajectory (Slingshot)",
    subtitle = "Solid: Tnaive → Tcm → Tem → Temra | Dashed: Tnaive → Tcm → Treg",
    x        = "UMAP 1",
    y        = "UMAP 2"
  )

print(p_traj)


ggsave("cd4_trajectory_slingshot.png",
       plot   = p_traj,
       width  = 14, height = 8, dpi = 300)


# Get Treg centroid
treg_centroid <- umap_df %>%
  filter(trajectory_state == "Treg") %>%
  summarise(UMAP_1 = median(UMAP_1), UMAP_2 = median(UMAP_2))

# Get last point of dashed curve
curve2_end <- tail(curve2_df, 1)

p_traj +
  # Add arrow from curve endpoint to Treg centroid
  annotate("segment",
           x      = curve2_end$UMAP_1,
           y      = curve2_end$UMAP_2,
           xend   = treg_centroid$UMAP_1,
           yend   = treg_centroid$UMAP_2,
           colour = "black",
           linewidth = 1.2,
           linetype  = "dashed",
           arrow  = arrow(length = unit(0.3,"cm"),
                          type   = "closed")) +
  labs(subtitle = "Solid: Tnaive→Tcm→Tem→Temra | Dashed: Tnaive→Tcm→Treg")

7 Pseudotime Feature Plots

# Fix: add na.rm handling by filtering NAs before quantile calculation
# Use a numeric cutoff instead of "q95"

# Calculate q95 manually excluding NAs
eff_q95 <- quantile(clean_obj@meta.data$PT_effector, 
                     probs = 0.95, na.rm = TRUE)
reg_q95 <- quantile(clean_obj@meta.data$PT_regulatory, 
                     probs = 0.95, na.rm = TRUE)

cat("Effector q95 cutoff:",   round(eff_q95, 2), "\n")
Effector q95 cutoff: 12.29 
cat("Regulatory q95 cutoff:", round(reg_q95, 2), "\n")
Regulatory q95 cutoff: 17.04 
p_eff <- FeaturePlot(clean_obj,
                     features   = "PT_effector",
                     reduction  = "umap",
                     cols       = c("grey90","#C00000"),
                     pt.size    = 0.5,
                     min.cutoff = 0,
                     max.cutoff = eff_q95) +   # numeric value not "q95"
  ggtitle("Effector pseudotime\nTnaive → Tcm → Tem → Temra") +
  theme(plot.title = element_text(size = 11, face = "bold"))

p_reg <- FeaturePlot(clean_obj,
                     features   = "PT_regulatory",
                     reduction  = "umap",
                     cols       = c("grey90","#4472C4"),
                     pt.size    = 0.5,
                     min.cutoff = 0,
                     max.cutoff = reg_q95) +   # numeric value not "q95"
  ggtitle("Regulatory pseudotime\nTnaive → Tcm → Treg") +
  theme(plot.title = element_text(size = 11, face = "bold"))

p_eff | p_reg


ggsave("pseudotime_featureplots.png",
       plot  = p_eff | p_reg,
       width = 14, height = 6, dpi = 300)

8 Pseudotime Violin per State per Lineage

# Build long-format dataframe for plotting
pt_df <- data.frame(
  trajectory_state = clean_obj$trajectory_state[keep_cells],
  PT_effector      = pt_matrix[, "PT_effector"],
  PT_regulatory    = pt_matrix[, "PT_regulatory"]
) %>%
  tidyr::pivot_longer(
    cols      = c(PT_effector, PT_regulatory),
    names_to  = "lineage",
    values_to = "pseudotime"
  ) %>%
  dplyr::filter(
    !is.na(trajectory_state),
    !is.na(pseudotime)
  )

ggplot(pt_df,
       aes(x    = factor(trajectory_state,
                          levels = c("Tnaive","Tcm","Tem","Temra","Treg")),
           y    = pseudotime,
           fill = trajectory_state)) +
  geom_violin(scale = "width", trim = TRUE) +
  geom_boxplot(width = 0.08, fill = "white", outlier.size = 0.3) +
  facet_wrap(~ lineage,
             ncol     = 2,
             labeller = labeller(lineage = c(
               "PT_effector"   = "Effector lineage (→ Temra)",
               "PT_regulatory" = "Regulatory lineage (→ Treg)"
             ))) +
  scale_fill_manual(values = flow_colours) +
  theme_bw() +
  theme(
    legend.position = "none",
    axis.text.x     = element_text(angle = 45, hjust = 1, size = 10),
    strip.text      = element_text(size = 11, face = "bold")
  ) +
  labs(
    title    = "Pseudotime per state per lineage",
    subtitle = "Pseudotime should increase monotonically: Tnaive → Temra / Treg",
    x        = NULL,
    y        = "Slingshot pseudotime"
  )


# Confirm monotonic increase — key validation
cat("Median pseudotime per state — effector lineage:\n")
Median pseudotime per state — effector lineage:
pt_df %>%
  filter(lineage == "PT_effector") %>%
  group_by(trajectory_state) %>%
  summarise(median_pt = round(median(pseudotime, na.rm = TRUE), 2),
            n         = n(),
            .groups   = "drop") %>%
  arrange(median_pt) %>%
  print()

9 Gene Expression Along Pseudotime Heatmap

DefaultAssay(clean_obj) <- "RNA"

key_genes <- c(
  "LEF1","TCF7","KLF2","SELL",        # Tnaive
  "IL7R","S100A4","ITGB1","BCL2",     # Tcm
  "GZMK","CXCR3","CCR5",             # Tem
  "CX3CR1","GNLY","FGFBP2","PRF1",   # Temra
  "FOXP3","IL2RA","CTLA4","IKZF2"    # Treg
)
key_genes <- intersect(key_genes, rownames(clean_obj))
cat("Genes found:", length(key_genes), "\n")
Genes found: 19 
# Order cells by effector pseudotime
eff_pt       <- clean_obj$PT_effector
eff_cells    <- !is.na(eff_pt)
eff_order    <- order(eff_pt[eff_cells])
eff_barcodes <- colnames(clean_obj)[eff_cells][eff_order]

expr_mat <- as.matrix(
  GetAssayData(clean_obj, assay = "RNA", layer = "data")[key_genes, eff_barcodes]
)

# Smooth into 50 pseudotime bins
n_bins   <- 50
bin_size <- floor(ncol(expr_mat) / n_bins)
bin_mat  <- sapply(seq_len(n_bins), function(i) {
  idx <- ((i-1)*bin_size + 1):min(i*bin_size, ncol(expr_mat))
  rowMeans(expr_mat[, idx, drop = FALSE])
})

# Z-score per gene and clip to [-2, 2]
bin_z            <- t(scale(t(bin_mat)))
bin_z[is.nan(bin_z)] <- 0
bin_z            <- pmin(pmax(bin_z, -2), 2)

# Draw in notebook — remove filename parameter
pheatmap::pheatmap(
  bin_z,
  cluster_cols  = FALSE,
  cluster_rows  = FALSE,
  color         = colorRampPalette(c("navy","white","firebrick3"))(100),
  breaks        = seq(-2, 2, length.out = 101),
  main          = "Gene expression along effector pseudotime\nTnaive → Tcm → Tem → Temra",
  fontsize_row  = 9,
  border_color  = NA,
  show_colnames = FALSE,
  gaps_row      = c(4, 8, 11)
)

# Save separately AFTER displaying
dev.copy(png, "pseudotime_heatmap.png", width = 12, height = 7, units = "in", res = 300)
png 
  3 
dev.off()
png 
  2 

10 Gene Expression Along Pseudotime Heatmap

# ── Regulatory lineage heatmap (Tnaive → Tcm → Treg) ──

# Treg lineage specific genes — include Treg markers prominently
reg_genes <- c(
  "LEF1","TCF7","KLF2","SELL",          # Tnaive — should decline
  "IL7R","S100A4","ITGB1","BCL2",       # Tcm — intermediate
  "FOXP3","IL2RA","CTLA4","IKZF2",      # Treg core — should rise
  "TIGIT","TNFRSF18","TNFRSF4","CCR8"   # Treg effector — rise at end
)
reg_genes <- intersect(reg_genes, rownames(clean_obj))
cat("Regulatory lineage genes found:", length(reg_genes), "\n")
Regulatory lineage genes found: 16 
# Order cells by REGULATORY pseudotime (not effector)
reg_pt       <- clean_obj$PT_regulatory
reg_cells    <- !is.na(reg_pt)
reg_order    <- order(reg_pt[reg_cells])
reg_barcodes <- colnames(clean_obj)[reg_cells][reg_order]

cat("Cells on regulatory lineage:", length(reg_barcodes), "\n")
Cells on regulatory lineage: 10267 
# Extract expression matrix ordered by regulatory pseudotime
expr_mat_reg <- as.matrix(
  GetAssayData(clean_obj, assay = "RNA", layer = "data")[reg_genes, reg_barcodes]
)

# Smooth into 50 bins
n_bins        <- 50
bin_size_reg  <- floor(ncol(expr_mat_reg) / n_bins)
bin_mat_reg   <- sapply(seq_len(n_bins), function(i) {
  idx <- ((i-1)*bin_size_reg + 1):min(i*bin_size_reg, ncol(expr_mat_reg))
  rowMeans(expr_mat_reg[, idx, drop = FALSE])
})

# Z-score and clip
bin_z_reg             <- t(scale(t(bin_mat_reg)))
bin_z_reg[is.nan(bin_z_reg)] <- 0
bin_z_reg             <- pmin(pmax(bin_z_reg, -2), 2)

# Add pseudotime axis annotation (Early → Late)
col_annotation <- data.frame(
  Pseudotime = seq(0, 1, length.out = n_bins),
  row.names  = paste0("bin", seq_len(n_bins))
)
colnames(bin_z_reg) <- paste0("bin", seq_len(n_bins))

annotation_colors <- list(
  Pseudotime = colorRampPalette(c("grey90","#FFD700"))(100)
)

# Plot regulatory lineage heatmap
pheatmap::pheatmap(
  bin_z_reg,
  cluster_cols      = FALSE,
  cluster_rows      = FALSE,
  color             = colorRampPalette(c("navy","white","firebrick3"))(100),
  breaks            = seq(-2, 2, length.out = 101),
  main              = "Gene expression along regulatory pseudotime\nTnaive → Tcm → Treg",
  fontsize_row      = 9,
  border_color      = NA,
  show_colnames     = FALSE,
  annotation_col    = col_annotation,
  annotation_colors = annotation_colors,
  annotation_names_col = FALSE,
  gaps_row          = c(4, 8, 12)  # Tnaive | Tcm | Treg core | Treg effector
)

# Save
dev.copy(png, "pseudotime_heatmap_regulatory.png",
         width = 12, height = 7, units = "in", res = 300)
png 
  3 
dev.off()
png 
  2 

11 Save Final Object

DefaultAssay(clean_obj) <- "integrated"

saveRDS(
  clean_obj,
  "CD4_reference_clean_Azimuth_Slingshot.rds",
  compress = FALSE
)

cat("✅ Saved:", ncol(clean_obj), "cells\n")
✅ Saved: 11466 cells
cat("\nFinal trajectory state distribution:\n")

Final trajectory state distribution:
print(table(clean_obj$trajectory_state, useNA = "ifany"))

Tnaive    Tcm    Tem  Temra   Treg 
  2037   9067    145     10    207 
cat("\nPseudotime columns added:\n")

Pseudotime columns added:
cat("  - PT_effector   (Tnaive → Tcm → Tem → Temra)\n")
  - PT_effector   (Tnaive → Tcm → Tem → Temra)
cat("  - PT_regulatory (Tnaive → Tcm → Treg)\n")
  - PT_regulatory (Tnaive → Tcm → Treg)
cat("\nObject ready for malignant cell projection via MapQuery\n")

Object ready for malignant cell projection via MapQuery
cat("return.model = TRUE confirmed — MapQuery will work\n")
return.model = TRUE confirmed — MapQuery will work
LS0tCnRpdGxlOiAiQ0Q0IFQtY2VsbCBSZWZlcmVuY2Ug4oCUIFNsaW5nc2hvdCBUcmFqZWN0b3J5IgphdXRob3I6IE5hc2lyIE1haG1vb2QgQWJiYXNpCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OgogICAgICBjb2xsYXBzZWQ6IHRydWUKICAgIHRoZW1lOiBqb3VybmFsCi0tLQoKIyBMb2FkIExpYnJhcmllcwpgYGB7ciBzZXR1cCwgaW5jbHVkZT1UUlVFfQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoU2V1cmF0KQpsaWJyYXJ5KHBhdGNod29yaykKbGlicmFyeShzbGluZ3Nob3QpCmxpYnJhcnkoU2luZ2xlQ2VsbEV4cGVyaW1lbnQpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkoZ2dyZXBlbCkKbGlicmFyeShwaGVhdG1hcCkKCm9wdGlvbnModGltZW91dCA9IDMwMCkKc2V0LnNlZWQoMTIzKQoKIyBDb2xvdXIgcGFsZXR0ZSDigJQgY29uc2lzdGVudCB0aHJvdWdob3V0CmZsb3dfY29sb3VycyA8LSBjKAogICJUbmFpdmUiID0gIiM0NDcyQzQiLAogICJUY20iICAgID0gIiNFRDdEMzEiLAogICJUZW0iICAgID0gIiNBOUQxOEUiLAogICJUZW1yYSIgID0gIiNDMDAwMDAiLAogICJUcmVnIiAgID0gIiNGRkQ3MDAiCikKYGBgCgojIExvYWQgT2JqZWN0CmBgYHtyIGxvYWQtb2JqZWN0fQpjbGVhbl9vYmogPC0gcmVhZFJEUygiLi4vQ0Q0X3JlZmVyZW5jZV9jbGVhbl9BemltdXRoX3JlYWR5X2Zvcl9TbGluZ3Nob3QucmRzIikKCmNhdCgiVG90YWwgY2VsbHMgbG9hZGVkOiIsIG5jb2woY2xlYW5fb2JqKSwgIlxuIikKY2F0KCJcbkF6aW11dGggbDIgY29tcG9zaXRpb246XG4iKQpwcmludCh0YWJsZShjbGVhbl9vYmokcHJlZGljdGVkLmNlbGx0eXBlLmwyKSkKYGBgCgojIEFzc2lnbiBUcmFqZWN0b3J5IFN0YXRlcwpgYGB7ciB0cmFqZWN0b3J5LXN0YXRlc30KIyBNYXAgQXppbXV0aCBsMiDihpIgdHJhamVjdG9yeSBzdGF0ZSBsYWJlbHMKIyBDRDQgQ1RMIG1lcmdlZCBpbnRvIFRlbXJhOgojICAgLSBPbmx5IDEwIGNlbGxzIOKAlCB0b28gZmV3IGZvciBzdGFibGUgU2xpbmdzaG90IGVuZHBvaW50CiMgICAtIEJpb2xvZ2ljYWxseSBqdXN0aWZpZWQ6IENENCBDVEwgaXMgY3l0b3RveGljIENENCwgZXF1aXZhbGVudCB0byBUZW1yYQoKY2xlYW5fb2JqJHRyYWplY3Rvcnlfc3RhdGUgPC0gZHBseXI6OnJlY29kZSgKICBjbGVhbl9vYmokcHJlZGljdGVkLmNlbGx0eXBlLmwyLAogICJDRDQgTmFpdmUiICAgICA9ICJUbmFpdmUiLAogICJDRDQgVENNIiAgICAgICA9ICJUY20iLAogICJDRDQgVEVNIiAgICAgICA9ICJUZW0iLAogICJDRDQgVGVtcmEvQ1RMIiAgICAgICA9ICJUZW1yYSIsICAgIyBtZXJnZWQgaW50byBUZW1yYQogICJUcmVnIiAgICAgICAgICA9ICJUcmVnIiwKICAuZGVmYXVsdCAgICAgICAgPSBOQV9jaGFyYWN0ZXJfCikKCmNsZWFuX29iaiR0cmFqZWN0b3J5X3N0YXRlIDwtIGZhY3RvcigKICBjbGVhbl9vYmokdHJhamVjdG9yeV9zdGF0ZSwKICBsZXZlbHMgPSBjKCJUbmFpdmUiLCJUY20iLCJUZW0iLCJUZW1yYSIsIlRyZWciKQopCgpjYXQoIlRyYWplY3Rvcnkgc3RhdGUgZGlzdHJpYnV0aW9uOlxuIikKcHJpbnQodGFibGUoY2xlYW5fb2JqJHRyYWplY3Rvcnlfc3RhdGUsIHVzZU5BID0gImlmYW55IikpCgojIFZpc3VhbGlzZSBvbiBVTUFQCkRpbVBsb3QoY2xlYW5fb2JqLAogICAgICAgIGdyb3VwLmJ5ID0gInRyYWplY3Rvcnlfc3RhdGUiLAogICAgICAgIGNvbHMgICAgID0gZmxvd19jb2xvdXJzLAogICAgICAgIG5hLnZhbHVlID0gImdyZXk5MCIsCiAgICAgICAgbGFiZWwgICAgPSBUUlVFLAogICAgICAgIHJlcGVsICAgID0gVFJVRSkgKwogIGdndGl0bGUoIlRyYWplY3Rvcnkgc3RhdGVzIOKAlCBBemltdXRoIGwyIG1hcHBlZCB0byBmbG93LWVxdWl2YWxlbnQgbGFiZWxzIikKYGBgCgojIFJ1biBTbGluZ3Nob3QgVHJhamVjdG9yeQpgYGB7ciBzbGluZ3Nob3QtcnVufQojIOKUgOKUgCBQcmVwYXJlIGlucHV0IOKUgOKUgAojIFVzZSBVTUFQIGNvb3JkaW5hdGVzIGRpcmVjdGx5IOKAlCBubyBTQ0UgY29udmVyc2lvbiBuZWVkZWQKdW1hcF9jb29yZHMgPC0gRW1iZWRkaW5ncyhjbGVhbl9vYmosICJ1bWFwIikKCiMgT25seSB1c2UgY2VsbHMgd2l0aCBhIGNvbmZpcm1lZCB0cmFqZWN0b3J5IHN0YXRlCmtlZXBfY2VsbHMgIDwtICFpcy5uYShjbGVhbl9vYmokdHJhamVjdG9yeV9zdGF0ZSkKdW1hcF9zdWIgICAgPC0gdW1hcF9jb29yZHNba2VlcF9jZWxscywgXQpjbHVzdGVyX3N1YiA8LSBkcm9wbGV2ZWxzKGNsZWFuX29iaiR0cmFqZWN0b3J5X3N0YXRlW2tlZXBfY2VsbHNdKQoKY2F0KCJDZWxscyBlbnRlcmluZyBTbGluZ3Nob3Q6XG4iKQpwcmludCh0YWJsZShjbHVzdGVyX3N1YikpCgojIOKUgOKUgCBSdW4gU2xpbmdzaG90IOKUgOKUgAojIHN0YXJ0LmNsdXMgPSBUbmFpdmUgKGNvbmZpcm1lZCBlYXJsaWVzdCBzdGF0ZSkKIyBlbmQuY2x1cyAgID0gVGVtcmEgKGVmZmVjdG9yL2N5dG90b3hpYyBhcm0pICsgVHJlZyAocmVndWxhdG9yeSBhcm0pCiMgVHdvIHRlcm1pbmFsIHN0YXRlcyA9IHR3byBsaW5lYWdlcwpzY2Vfc2xpbmcgPC0gc2xpbmdzaG90KAogIGRhdGEgICAgICAgICAgPSB1bWFwX3N1YiwKICBjbHVzdGVyTGFiZWxzID0gY2x1c3Rlcl9zdWIsCiAgc3RhcnQuY2x1cyAgICA9ICJUbmFpdmUiLAogIGVuZC5jbHVzICAgICAgPSBjKCJUZW1yYSIsICJUcmVnIiksCiAgYXBwcm94X3BvaW50cyA9IDEwMCwgICAgICAjIHJlZHVjZWQgZnJvbSAzMDAg4oCUIGxlc3Mgc21vb3RoaW5nCiAgc3RyZXRjaCAgICAgICA9IDAgICAgICAgICAjIHByZXZlbnRzIGN1cnZlcyBleHRlbmRpbmcgYmV5b25kIHRlcm1pbmFsIGNsdXN0ZXJzCikKCmNhdCgiXG5MaW5lYWdlcyBhZnRlciBmaXg6XG4iKQpwcmludChzbGluZ0xpbmVhZ2VzKHNjZV9zbGluZykpCmNhdCgiXG5MaW5lYWdlcyBpbmZlcnJlZDpcbiIpCnByaW50KHNsaW5nTGluZWFnZXMoc2NlX3NsaW5nKSkKIyBFeHBlY3RlZDoKIyBMaW5lYWdlIDE6IFRuYWl2ZSDihpIgVGNtIOKGkiBUZW0g4oaSIFRlbXJhICAoZWZmZWN0b3IgYXJtKQojIExpbmVhZ2UgMjogVG5haXZlIOKGkiBUY20g4oaSIFRyZWcgICAgICAgICAgKHJlZ3VsYXRvcnkgYXJtKQpgYGAKCiMgRXh0cmFjdCBQc2V1ZG90aW1lCmBgYHtyIHBzZXVkb3RpbWUtZXh0cmFjdH0KIyBFeHRyYWN0IHBzZXVkb3RpbWUgbWF0cml4IChjZWxscyDDlyBsaW5lYWdlcykKcHRfbWF0cml4ICAgICAgICAgICA8LSBzbGluZ1BzZXVkb3RpbWUoc2NlX3NsaW5nKQpjb2xuYW1lcyhwdF9tYXRyaXgpIDwtIGMoIlBUX2VmZmVjdG9yIiwiUFRfcmVndWxhdG9yeSIpCgpjYXQoIlBzZXVkb3RpbWUgc3VtbWFyeSDigJQgZWZmZWN0b3IgbGluZWFnZTpcbiIpCnByaW50KHN1bW1hcnkocHRfbWF0cml4WywgIlBUX2VmZmVjdG9yIl0pKQoKY2F0KCJcblBzZXVkb3RpbWUgc3VtbWFyeSDigJQgcmVndWxhdG9yeSBsaW5lYWdlOlxuIikKcHJpbnQoc3VtbWFyeShwdF9tYXRyaXhbLCAiUFRfcmVndWxhdG9yeSJdKSkKCiMgRXh0cmFjdCBwc2V1ZG90aW1lIG1hdHJpeCAoY2VsbHMgw5cgbGluZWFnZXMpCnB0X21hdHJpeCAgICAgICAgICAgPC0gc2xpbmdQc2V1ZG90aW1lKHNjZV9zbGluZykKY29sbmFtZXMocHRfbWF0cml4KSA8LSBjKCJQVF9lZmZlY3RvciIsIlBUX3JlZ3VsYXRvcnkiKQoKY2F0KCJQc2V1ZG90aW1lIHN1bW1hcnkg4oCUIGVmZmVjdG9yIGxpbmVhZ2U6XG4iKQpwcmludChzdW1tYXJ5KHB0X21hdHJpeFssICJQVF9lZmZlY3RvciJdKSkKY2F0KCJcblBzZXVkb3RpbWUgc3VtbWFyeSDigJQgcmVndWxhdG9yeSBsaW5lYWdlOlxuIikKcHJpbnQoc3VtbWFyeShwdF9tYXRyaXhbLCAiUFRfcmVndWxhdG9yeSJdKSkKCiMg4pSA4pSAIEFkZCBwc2V1ZG90aW1lIHRvIG1ldGFkYXRhIHZpYSBkaXJlY3QgZGF0YWZyYW1lIGFzc2lnbm1lbnQg4pSA4pSACiMgSU1QT1JUQU5UOiBEbyBOT1QgdXNlIGNsZWFuX29iaiRQVF9lZmZlY3RvciA8LSAuLi4gCiMgVGhhdCB0cmlnZ2VycyBTZXVyYXQncyB2YWxpZE9iamVjdCgpIHdoaWNoIGZhaWxzIG9uIGxhcmdlIG9iamVjdHMKIyBJbnN0ZWFkIGV4dHJhY3QgbWV0YS5kYXRhLCBtb2RpZnkgYXMgcGxhaW4gUiBkYXRhZnJhbWUsIHdyaXRlIGJhY2sKCm1ldGEgPC0gY2xlYW5fb2JqQG1ldGEuZGF0YQptZXRhJFBUX2VmZmVjdG9yICAgPC0gTkFfcmVhbF8KbWV0YSRQVF9yZWd1bGF0b3J5IDwtIE5BX3JlYWxfCgpjb21tb24gPC0gaW50ZXJzZWN0KHJvd25hbWVzKHB0X21hdHJpeCksIHJvd25hbWVzKG1ldGEpKQptZXRhW2NvbW1vbiwgIlBUX2VmZmVjdG9yIl0gICA8LSBwdF9tYXRyaXhbY29tbW9uLCAiUFRfZWZmZWN0b3IiXQptZXRhW2NvbW1vbiwgIlBUX3JlZ3VsYXRvcnkiXSA8LSBwdF9tYXRyaXhbY29tbW9uLCAiUFRfcmVndWxhdG9yeSJdCgpjbGVhbl9vYmpAbWV0YS5kYXRhIDwtIG1ldGEKCmNhdCgiXG5DZWxscyB3aXRoIGVmZmVjdG9yIHBzZXVkb3RpbWU6ICAiLAogICAgc3VtKCFpcy5uYShjbGVhbl9vYmpAbWV0YS5kYXRhJFBUX2VmZmVjdG9yKSksICJcbiIpCmNhdCgiQ2VsbHMgd2l0aCByZWd1bGF0b3J5IHBzZXVkb3RpbWU6IiwKICAgIHN1bSghaXMubmEoY2xlYW5fb2JqQG1ldGEuZGF0YSRQVF9yZWd1bGF0b3J5KSksICJcbiIpCmBgYAoKIyBUcmFqZWN0b3J5IFBsb3Qg4oCUIEN1cnZlcyBvbiBVTUFQCmBgYHtyIHRyYWplY3RvcnktcGxvdCwgZmlnLndpZHRoPTE0LCBmaWcuaGVpZ2h0PTh9CiMg4pSA4pSAIEV4dHJhY3QgY3VydmUgY29vcmRpbmF0ZXMg4pSA4pSACmN1cnZlcyA8LSBzbGluZ0N1cnZlcyhzY2Vfc2xpbmcpCgojIEN1cnZlIDEg4oCUIGVmZmVjdG9yIGxpbmVhZ2UgKFRuYWl2ZSDihpIgVGNtIOKGkiBUZW0g4oaSIFRlbXJhKQpjdXJ2ZTFfZGYgICAgICAgICAgIDwtIGFzLmRhdGEuZnJhbWUoY3VydmVzW1sxXV0kc1tjdXJ2ZXNbWzFdXSRvcmQsIF0pCmNvbG5hbWVzKGN1cnZlMV9kZikgPC0gYygiVU1BUF8xIiwiVU1BUF8yIikKCiMgQ3VydmUgMiDigJQgcmVndWxhdG9yeSBsaW5lYWdlIChUbmFpdmUg4oaSIFRjbSDihpIgVHJlZykKY3VydmUyX2RmICAgICAgICAgICA8LSBhcy5kYXRhLmZyYW1lKGN1cnZlc1tbMl1dJHNbY3VydmVzW1syXV0kb3JkLCBdKQpjb2xuYW1lcyhjdXJ2ZTJfZGYpIDwtIGMoIlVNQVBfMSIsIlVNQVBfMiIpCgojIOKUgOKUgCBVTUFQIGJhc2UgZGF0YWZyYW1lIOKUgOKUgAp1bWFwX2RmICAgICAgICAgICA8LSBhcy5kYXRhLmZyYW1lKHVtYXBfY29vcmRzKQpjb2xuYW1lcyh1bWFwX2RmKSA8LSBjKCJVTUFQXzEiLCJVTUFQXzIiKQp1bWFwX2RmJHRyYWplY3Rvcnlfc3RhdGUgPC0gY2xlYW5fb2JqJHRyYWplY3Rvcnlfc3RhdGUKdW1hcF9kZiA8LSB1bWFwX2RmICU+JSBmaWx0ZXIoIWlzLm5hKHRyYWplY3Rvcnlfc3RhdGUpKQoKIyDilIDilIAgTWlsZXN0b25lIGNlbnRyb2lkcyDilIDilIAKbWlsZXN0b25lcyA8LSB1bWFwX2RmICU+JQogIGdyb3VwX2J5KHRyYWplY3Rvcnlfc3RhdGUpICU+JQogIHN1bW1hcmlzZSgKICAgIFVNQVBfMSA9IG1lZGlhbihVTUFQXzEpLAogICAgVU1BUF8yID0gbWVkaWFuKFVNQVBfMiksCiAgICAuZ3JvdXBzID0gImRyb3AiCiAgKQoKIyDilIDilIAgTWFpbiBwbG90IOKUgOKUgApwX3RyYWogPC0gZ2dwbG90KHVtYXBfZGYsCiAgICAgICAgICAgICAgICAgYWVzKHggPSBVTUFQXzEsIHkgPSBVTUFQXzIsCiAgICAgICAgICAgICAgICAgICAgIGNvbG91ciA9IHRyYWplY3Rvcnlfc3RhdGUpKSArCiAgZ2VvbV9wb2ludChzaXplID0gMC40LCBhbHBoYSA9IDAuNSkgKwogICMgRWZmZWN0b3IgbGluZWFnZSDigJQgc29saWQgYmxhY2sgbGluZQogIGdlb21fcGF0aChkYXRhICAgICAgICA9IGN1cnZlMV9kZiwKICAgICAgICAgICAgYWVzKHggPSBVTUFQXzEsIHkgPSBVTUFQXzIpLAogICAgICAgICAgICBjb2xvdXIgICAgICA9ICJibGFjayIsCiAgICAgICAgICAgIGxpbmV3aWR0aCAgID0gMS41LAogICAgICAgICAgICBpbmhlcml0LmFlcyA9IEZBTFNFKSArCiAgIyBSZWd1bGF0b3J5IGxpbmVhZ2Ug4oCUIGRhc2hlZCBibGFjayBsaW5lCiAgZ2VvbV9wYXRoKGRhdGEgICAgICAgID0gY3VydmUyX2RmLAogICAgICAgICAgICBhZXMoeCA9IFVNQVBfMSwgeSA9IFVNQVBfMiksCiAgICAgICAgICAgIGNvbG91ciAgICAgID0gImJsYWNrIiwKICAgICAgICAgICAgbGluZXdpZHRoICAgPSAxLjUsCiAgICAgICAgICAgIGxpbmV0eXBlICAgID0gImRhc2hlZCIsCiAgICAgICAgICAgIGluaGVyaXQuYWVzID0gRkFMU0UpICsKICAjIE1pbGVzdG9uZSBsYWJlbHMKICBnZ3JlcGVsOjpnZW9tX2xhYmVsX3JlcGVsKAogICAgZGF0YSAgICAgICAgPSBtaWxlc3RvbmVzLAogICAgYWVzKHggICAgID0gVU1BUF8xLAogICAgICAgIHkgICAgID0gVU1BUF8yLAogICAgICAgIGxhYmVsID0gdHJhamVjdG9yeV9zdGF0ZSwKICAgICAgICBmaWxsICA9IHRyYWplY3Rvcnlfc3RhdGUpLAogICAgY29sb3VyICAgICAgPSAid2hpdGUiLAogICAgZm9udGZhY2UgICAgPSAiYm9sZCIsCiAgICBzaXplICAgICAgICA9IDUsCiAgICBib3gucGFkZGluZyA9IDAuOCwKICAgIGluaGVyaXQuYWVzID0gRkFMU0UKICApICsKICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IGZsb3dfY29sb3VycywgbmFtZSA9ICJTdGF0ZSIpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgICA9IGZsb3dfY29sb3VycywgZ3VpZGUgPSAibm9uZSIpICsKICB0aGVtZV9jbGFzc2ljKCkgKwogIHRoZW1lKAogICAgcGxvdC50aXRsZSAgICA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGZhY2UgPSAiYm9sZCIpLAogICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTEpLAogICAgbGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IgogICkgKwogIGxhYnMoCiAgICB0aXRsZSAgICA9ICJDRDQgVC1jZWxsIERpZmZlcmVudGlhdGlvbiBUcmFqZWN0b3J5IChTbGluZ3Nob3QpIiwKICAgIHN1YnRpdGxlID0gIlNvbGlkOiBUbmFpdmUg4oaSIFRjbSDihpIgVGVtIOKGkiBUZW1yYSB8IERhc2hlZDogVG5haXZlIOKGkiBUY20g4oaSIFRyZWciLAogICAgeCAgICAgICAgPSAiVU1BUCAxIiwKICAgIHkgICAgICAgID0gIlVNQVAgMiIKICApCgpwcmludChwX3RyYWopCgpnZ3NhdmUoImNkNF90cmFqZWN0b3J5X3NsaW5nc2hvdC5wbmciLAogICAgICAgcGxvdCAgID0gcF90cmFqLAogICAgICAgd2lkdGggID0gMTQsIGhlaWdodCA9IDgsIGRwaSA9IDMwMCkKCgojIEdldCBUcmVnIGNlbnRyb2lkCnRyZWdfY2VudHJvaWQgPC0gdW1hcF9kZiAlPiUKICBmaWx0ZXIodHJhamVjdG9yeV9zdGF0ZSA9PSAiVHJlZyIpICU+JQogIHN1bW1hcmlzZShVTUFQXzEgPSBtZWRpYW4oVU1BUF8xKSwgVU1BUF8yID0gbWVkaWFuKFVNQVBfMikpCgojIEdldCBsYXN0IHBvaW50IG9mIGRhc2hlZCBjdXJ2ZQpjdXJ2ZTJfZW5kIDwtIHRhaWwoY3VydmUyX2RmLCAxKQoKcF90cmFqICsKICAjIEFkZCBhcnJvdyBmcm9tIGN1cnZlIGVuZHBvaW50IHRvIFRyZWcgY2VudHJvaWQKICBhbm5vdGF0ZSgic2VnbWVudCIsCiAgICAgICAgICAgeCAgICAgID0gY3VydmUyX2VuZCRVTUFQXzEsCiAgICAgICAgICAgeSAgICAgID0gY3VydmUyX2VuZCRVTUFQXzIsCiAgICAgICAgICAgeGVuZCAgID0gdHJlZ19jZW50cm9pZCRVTUFQXzEsCiAgICAgICAgICAgeWVuZCAgID0gdHJlZ19jZW50cm9pZCRVTUFQXzIsCiAgICAgICAgICAgY29sb3VyID0gImJsYWNrIiwKICAgICAgICAgICBsaW5ld2lkdGggPSAxLjIsCiAgICAgICAgICAgbGluZXR5cGUgID0gImRhc2hlZCIsCiAgICAgICAgICAgYXJyb3cgID0gYXJyb3cobGVuZ3RoID0gdW5pdCgwLjMsImNtIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZSAgID0gImNsb3NlZCIpKSArCiAgbGFicyhzdWJ0aXRsZSA9ICJTb2xpZDogVG5haXZl4oaSVGNt4oaSVGVt4oaSVGVtcmEgfCBEYXNoZWQ6IFRuYWl2ZeKGklRjbeKGklRyZWciKQpgYGAKCiMgUHNldWRvdGltZSBGZWF0dXJlIFBsb3RzCmBgYHtyIHBzZXVkb3RpbWUtZmVhdHVyZXBsb3RzLCBmaWcud2lkdGg9MTQsIGZpZy5oZWlnaHQ9Nn0KIyBGaXg6IGFkZCBuYS5ybSBoYW5kbGluZyBieSBmaWx0ZXJpbmcgTkFzIGJlZm9yZSBxdWFudGlsZSBjYWxjdWxhdGlvbgojIFVzZSBhIG51bWVyaWMgY3V0b2ZmIGluc3RlYWQgb2YgInE5NSIKCiMgQ2FsY3VsYXRlIHE5NSBtYW51YWxseSBleGNsdWRpbmcgTkFzCmVmZl9xOTUgPC0gcXVhbnRpbGUoY2xlYW5fb2JqQG1ldGEuZGF0YSRQVF9lZmZlY3RvciwgCiAgICAgICAgICAgICAgICAgICAgIHByb2JzID0gMC45NSwgbmEucm0gPSBUUlVFKQpyZWdfcTk1IDwtIHF1YW50aWxlKGNsZWFuX29iakBtZXRhLmRhdGEkUFRfcmVndWxhdG9yeSwgCiAgICAgICAgICAgICAgICAgICAgIHByb2JzID0gMC45NSwgbmEucm0gPSBUUlVFKQoKY2F0KCJFZmZlY3RvciBxOTUgY3V0b2ZmOiIsICAgcm91bmQoZWZmX3E5NSwgMiksICJcbiIpCmNhdCgiUmVndWxhdG9yeSBxOTUgY3V0b2ZmOiIsIHJvdW5kKHJlZ19xOTUsIDIpLCAiXG4iKQoKcF9lZmYgPC0gRmVhdHVyZVBsb3QoY2xlYW5fb2JqLAogICAgICAgICAgICAgICAgICAgICBmZWF0dXJlcyAgID0gIlBUX2VmZmVjdG9yIiwKICAgICAgICAgICAgICAgICAgICAgcmVkdWN0aW9uICA9ICJ1bWFwIiwKICAgICAgICAgICAgICAgICAgICAgY29scyAgICAgICA9IGMoImdyZXk5MCIsIiNDMDAwMDAiKSwKICAgICAgICAgICAgICAgICAgICAgcHQuc2l6ZSAgICA9IDAuNSwKICAgICAgICAgICAgICAgICAgICAgbWluLmN1dG9mZiA9IDAsCiAgICAgICAgICAgICAgICAgICAgIG1heC5jdXRvZmYgPSBlZmZfcTk1KSArICAgIyBudW1lcmljIHZhbHVlIG5vdCAicTk1IgogIGdndGl0bGUoIkVmZmVjdG9yIHBzZXVkb3RpbWVcblRuYWl2ZSDihpIgVGNtIOKGkiBUZW0g4oaSIFRlbXJhIikgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDExLCBmYWNlID0gImJvbGQiKSkKCnBfcmVnIDwtIEZlYXR1cmVQbG90KGNsZWFuX29iaiwKICAgICAgICAgICAgICAgICAgICAgZmVhdHVyZXMgICA9ICJQVF9yZWd1bGF0b3J5IiwKICAgICAgICAgICAgICAgICAgICAgcmVkdWN0aW9uICA9ICJ1bWFwIiwKICAgICAgICAgICAgICAgICAgICAgY29scyAgICAgICA9IGMoImdyZXk5MCIsIiM0NDcyQzQiKSwKICAgICAgICAgICAgICAgICAgICAgcHQuc2l6ZSAgICA9IDAuNSwKICAgICAgICAgICAgICAgICAgICAgbWluLmN1dG9mZiA9IDAsCiAgICAgICAgICAgICAgICAgICAgIG1heC5jdXRvZmYgPSByZWdfcTk1KSArICAgIyBudW1lcmljIHZhbHVlIG5vdCAicTk1IgogIGdndGl0bGUoIlJlZ3VsYXRvcnkgcHNldWRvdGltZVxuVG5haXZlIOKGkiBUY20g4oaSIFRyZWciKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTEsIGZhY2UgPSAiYm9sZCIpKQoKcF9lZmYgfCBwX3JlZwoKZ2dzYXZlKCJwc2V1ZG90aW1lX2ZlYXR1cmVwbG90cy5wbmciLAogICAgICAgcGxvdCAgPSBwX2VmZiB8IHBfcmVnLAogICAgICAgd2lkdGggPSAxNCwgaGVpZ2h0ID0gNiwgZHBpID0gMzAwKQpgYGAKCiMgUHNldWRvdGltZSBWaW9saW4gcGVyIFN0YXRlIHBlciBMaW5lYWdlCmBgYHtyIHBzZXVkb3RpbWUtdmlvbGluLCBmaWcud2lkdGg9MTQsIGZpZy5oZWlnaHQ9Nn0KIyBCdWlsZCBsb25nLWZvcm1hdCBkYXRhZnJhbWUgZm9yIHBsb3R0aW5nCnB0X2RmIDwtIGRhdGEuZnJhbWUoCiAgdHJhamVjdG9yeV9zdGF0ZSA9IGNsZWFuX29iaiR0cmFqZWN0b3J5X3N0YXRlW2tlZXBfY2VsbHNdLAogIFBUX2VmZmVjdG9yICAgICAgPSBwdF9tYXRyaXhbLCAiUFRfZWZmZWN0b3IiXSwKICBQVF9yZWd1bGF0b3J5ICAgID0gcHRfbWF0cml4WywgIlBUX3JlZ3VsYXRvcnkiXQopICU+JQogIHRpZHlyOjpwaXZvdF9sb25nZXIoCiAgICBjb2xzICAgICAgPSBjKFBUX2VmZmVjdG9yLCBQVF9yZWd1bGF0b3J5KSwKICAgIG5hbWVzX3RvICA9ICJsaW5lYWdlIiwKICAgIHZhbHVlc190byA9ICJwc2V1ZG90aW1lIgogICkgJT4lCiAgZHBseXI6OmZpbHRlcigKICAgICFpcy5uYSh0cmFqZWN0b3J5X3N0YXRlKSwKICAgICFpcy5uYShwc2V1ZG90aW1lKQogICkKCmdncGxvdChwdF9kZiwKICAgICAgIGFlcyh4ICAgID0gZmFjdG9yKHRyYWplY3Rvcnlfc3RhdGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYygiVG5haXZlIiwiVGNtIiwiVGVtIiwiVGVtcmEiLCJUcmVnIikpLAogICAgICAgICAgIHkgICAgPSBwc2V1ZG90aW1lLAogICAgICAgICAgIGZpbGwgPSB0cmFqZWN0b3J5X3N0YXRlKSkgKwogIGdlb21fdmlvbGluKHNjYWxlID0gIndpZHRoIiwgdHJpbSA9IFRSVUUpICsKICBnZW9tX2JveHBsb3Qod2lkdGggPSAwLjA4LCBmaWxsID0gIndoaXRlIiwgb3V0bGllci5zaXplID0gMC4zKSArCiAgZmFjZXRfd3JhcCh+IGxpbmVhZ2UsCiAgICAgICAgICAgICBuY29sICAgICA9IDIsCiAgICAgICAgICAgICBsYWJlbGxlciA9IGxhYmVsbGVyKGxpbmVhZ2UgPSBjKAogICAgICAgICAgICAgICAiUFRfZWZmZWN0b3IiICAgPSAiRWZmZWN0b3IgbGluZWFnZSAo4oaSIFRlbXJhKSIsCiAgICAgICAgICAgICAgICJQVF9yZWd1bGF0b3J5IiA9ICJSZWd1bGF0b3J5IGxpbmVhZ2UgKOKGkiBUcmVnKSIKICAgICAgICAgICAgICkpKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gZmxvd19jb2xvdXJzKSArCiAgdGhlbWVfYncoKSArCiAgdGhlbWUoCiAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICBheGlzLnRleHQueCAgICAgPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxLCBzaXplID0gMTApLAogICAgc3RyaXAudGV4dCAgICAgID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMSwgZmFjZSA9ICJib2xkIikKICApICsKICBsYWJzKAogICAgdGl0bGUgICAgPSAiUHNldWRvdGltZSBwZXIgc3RhdGUgcGVyIGxpbmVhZ2UiLAogICAgc3VidGl0bGUgPSAiUHNldWRvdGltZSBzaG91bGQgaW5jcmVhc2UgbW9ub3RvbmljYWxseTogVG5haXZlIOKGkiBUZW1yYSAvIFRyZWciLAogICAgeCAgICAgICAgPSBOVUxMLAogICAgeSAgICAgICAgPSAiU2xpbmdzaG90IHBzZXVkb3RpbWUiCiAgKQoKIyBDb25maXJtIG1vbm90b25pYyBpbmNyZWFzZSDigJQga2V5IHZhbGlkYXRpb24KY2F0KCJNZWRpYW4gcHNldWRvdGltZSBwZXIgc3RhdGUg4oCUIGVmZmVjdG9yIGxpbmVhZ2U6XG4iKQpwdF9kZiAlPiUKICBmaWx0ZXIobGluZWFnZSA9PSAiUFRfZWZmZWN0b3IiKSAlPiUKICBncm91cF9ieSh0cmFqZWN0b3J5X3N0YXRlKSAlPiUKICBzdW1tYXJpc2UobWVkaWFuX3B0ID0gcm91bmQobWVkaWFuKHBzZXVkb3RpbWUsIG5hLnJtID0gVFJVRSksIDIpLAogICAgICAgICAgICBuICAgICAgICAgPSBuKCksCiAgICAgICAgICAgIC5ncm91cHMgICA9ICJkcm9wIikgJT4lCiAgYXJyYW5nZShtZWRpYW5fcHQpICU+JQogIHByaW50KCkKYGBgCgojIEdlbmUgRXhwcmVzc2lvbiBBbG9uZyBQc2V1ZG90aW1lIEhlYXRtYXAKYGBge3IgZ2VuZS1oZWF0bWFwLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9N30KRGVmYXVsdEFzc2F5KGNsZWFuX29iaikgPC0gIlJOQSIKCmtleV9nZW5lcyA8LSBjKAogICJMRUYxIiwiVENGNyIsIktMRjIiLCJTRUxMIiwgICAgICAgICMgVG5haXZlCiAgIklMN1IiLCJTMTAwQTQiLCJJVEdCMSIsIkJDTDIiLCAgICAgIyBUY20KICAiR1pNSyIsIkNYQ1IzIiwiQ0NSNSIsICAgICAgICAgICAgICMgVGVtCiAgIkNYM0NSMSIsIkdOTFkiLCJGR0ZCUDIiLCJQUkYxIiwgICAjIFRlbXJhCiAgIkZPWFAzIiwiSUwyUkEiLCJDVExBNCIsIklLWkYyIiAgICAjIFRyZWcKKQprZXlfZ2VuZXMgPC0gaW50ZXJzZWN0KGtleV9nZW5lcywgcm93bmFtZXMoY2xlYW5fb2JqKSkKY2F0KCJHZW5lcyBmb3VuZDoiLCBsZW5ndGgoa2V5X2dlbmVzKSwgIlxuIikKCiMgT3JkZXIgY2VsbHMgYnkgZWZmZWN0b3IgcHNldWRvdGltZQplZmZfcHQgICAgICAgPC0gY2xlYW5fb2JqJFBUX2VmZmVjdG9yCmVmZl9jZWxscyAgICA8LSAhaXMubmEoZWZmX3B0KQplZmZfb3JkZXIgICAgPC0gb3JkZXIoZWZmX3B0W2VmZl9jZWxsc10pCmVmZl9iYXJjb2RlcyA8LSBjb2xuYW1lcyhjbGVhbl9vYmopW2VmZl9jZWxsc11bZWZmX29yZGVyXQoKZXhwcl9tYXQgPC0gYXMubWF0cml4KAogIEdldEFzc2F5RGF0YShjbGVhbl9vYmosIGFzc2F5ID0gIlJOQSIsIGxheWVyID0gImRhdGEiKVtrZXlfZ2VuZXMsIGVmZl9iYXJjb2Rlc10KKQoKIyBTbW9vdGggaW50byA1MCBwc2V1ZG90aW1lIGJpbnMKbl9iaW5zICAgPC0gNTAKYmluX3NpemUgPC0gZmxvb3IobmNvbChleHByX21hdCkgLyBuX2JpbnMpCmJpbl9tYXQgIDwtIHNhcHBseShzZXFfbGVuKG5fYmlucyksIGZ1bmN0aW9uKGkpIHsKICBpZHggPC0gKChpLTEpKmJpbl9zaXplICsgMSk6bWluKGkqYmluX3NpemUsIG5jb2woZXhwcl9tYXQpKQogIHJvd01lYW5zKGV4cHJfbWF0WywgaWR4LCBkcm9wID0gRkFMU0VdKQp9KQoKIyBaLXNjb3JlIHBlciBnZW5lIGFuZCBjbGlwIHRvIFstMiwgMl0KYmluX3ogICAgICAgICAgICA8LSB0KHNjYWxlKHQoYmluX21hdCkpKQpiaW5feltpcy5uYW4oYmluX3opXSA8LSAwCmJpbl96ICAgICAgICAgICAgPC0gcG1pbihwbWF4KGJpbl96LCAtMiksIDIpCgojIERyYXcgaW4gbm90ZWJvb2sg4oCUIHJlbW92ZSBmaWxlbmFtZSBwYXJhbWV0ZXIKcGhlYXRtYXA6OnBoZWF0bWFwKAogIGJpbl96LAogIGNsdXN0ZXJfY29scyAgPSBGQUxTRSwKICBjbHVzdGVyX3Jvd3MgID0gRkFMU0UsCiAgY29sb3IgICAgICAgICA9IGNvbG9yUmFtcFBhbGV0dGUoYygibmF2eSIsIndoaXRlIiwiZmlyZWJyaWNrMyIpKSgxMDApLAogIGJyZWFrcyAgICAgICAgPSBzZXEoLTIsIDIsIGxlbmd0aC5vdXQgPSAxMDEpLAogIG1haW4gICAgICAgICAgPSAiR2VuZSBleHByZXNzaW9uIGFsb25nIGVmZmVjdG9yIHBzZXVkb3RpbWVcblRuYWl2ZSDihpIgVGNtIOKGkiBUZW0g4oaSIFRlbXJhIiwKICBmb250c2l6ZV9yb3cgID0gOSwKICBib3JkZXJfY29sb3IgID0gTkEsCiAgc2hvd19jb2xuYW1lcyA9IEZBTFNFLAogIGdhcHNfcm93ICAgICAgPSBjKDQsIDgsIDExKQopCgojIFNhdmUgc2VwYXJhdGVseSBBRlRFUiBkaXNwbGF5aW5nCmRldi5jb3B5KHBuZywgInBzZXVkb3RpbWVfaGVhdG1hcC5wbmciLCB3aWR0aCA9IDEyLCBoZWlnaHQgPSA3LCB1bml0cyA9ICJpbiIsIHJlcyA9IDMwMCkKZGV2Lm9mZigpCgpgYGAKCgoKIyBHZW5lIEV4cHJlc3Npb24gQWxvbmcgUHNldWRvdGltZSBIZWF0bWFwCmBgYHtyIGdlbmUtaGVhdG1hcC1UcmVnLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9N30KIyDilIDilIAgUmVndWxhdG9yeSBsaW5lYWdlIGhlYXRtYXAgKFRuYWl2ZSDihpIgVGNtIOKGkiBUcmVnKSDilIDilIAKCiMgVHJlZyBsaW5lYWdlIHNwZWNpZmljIGdlbmVzIOKAlCBpbmNsdWRlIFRyZWcgbWFya2VycyBwcm9taW5lbnRseQpyZWdfZ2VuZXMgPC0gYygKICAiTEVGMSIsIlRDRjciLCJLTEYyIiwiU0VMTCIsICAgICAgICAgICMgVG5haXZlIOKAlCBzaG91bGQgZGVjbGluZQogICJJTDdSIiwiUzEwMEE0IiwiSVRHQjEiLCJCQ0wyIiwgICAgICAgIyBUY20g4oCUIGludGVybWVkaWF0ZQogICJGT1hQMyIsIklMMlJBIiwiQ1RMQTQiLCJJS1pGMiIsICAgICAgIyBUcmVnIGNvcmUg4oCUIHNob3VsZCByaXNlCiAgIlRJR0lUIiwiVE5GUlNGMTgiLCJUTkZSU0Y0IiwiQ0NSOCIgICAjIFRyZWcgZWZmZWN0b3Ig4oCUIHJpc2UgYXQgZW5kCikKcmVnX2dlbmVzIDwtIGludGVyc2VjdChyZWdfZ2VuZXMsIHJvd25hbWVzKGNsZWFuX29iaikpCmNhdCgiUmVndWxhdG9yeSBsaW5lYWdlIGdlbmVzIGZvdW5kOiIsIGxlbmd0aChyZWdfZ2VuZXMpLCAiXG4iKQoKIyBPcmRlciBjZWxscyBieSBSRUdVTEFUT1JZIHBzZXVkb3RpbWUgKG5vdCBlZmZlY3RvcikKcmVnX3B0ICAgICAgIDwtIGNsZWFuX29iaiRQVF9yZWd1bGF0b3J5CnJlZ19jZWxscyAgICA8LSAhaXMubmEocmVnX3B0KQpyZWdfb3JkZXIgICAgPC0gb3JkZXIocmVnX3B0W3JlZ19jZWxsc10pCnJlZ19iYXJjb2RlcyA8LSBjb2xuYW1lcyhjbGVhbl9vYmopW3JlZ19jZWxsc11bcmVnX29yZGVyXQoKY2F0KCJDZWxscyBvbiByZWd1bGF0b3J5IGxpbmVhZ2U6IiwgbGVuZ3RoKHJlZ19iYXJjb2RlcyksICJcbiIpCgojIEV4dHJhY3QgZXhwcmVzc2lvbiBtYXRyaXggb3JkZXJlZCBieSByZWd1bGF0b3J5IHBzZXVkb3RpbWUKZXhwcl9tYXRfcmVnIDwtIGFzLm1hdHJpeCgKICBHZXRBc3NheURhdGEoY2xlYW5fb2JqLCBhc3NheSA9ICJSTkEiLCBsYXllciA9ICJkYXRhIilbcmVnX2dlbmVzLCByZWdfYmFyY29kZXNdCikKCiMgU21vb3RoIGludG8gNTAgYmlucwpuX2JpbnMgICAgICAgIDwtIDUwCmJpbl9zaXplX3JlZyAgPC0gZmxvb3IobmNvbChleHByX21hdF9yZWcpIC8gbl9iaW5zKQpiaW5fbWF0X3JlZyAgIDwtIHNhcHBseShzZXFfbGVuKG5fYmlucyksIGZ1bmN0aW9uKGkpIHsKICBpZHggPC0gKChpLTEpKmJpbl9zaXplX3JlZyArIDEpOm1pbihpKmJpbl9zaXplX3JlZywgbmNvbChleHByX21hdF9yZWcpKQogIHJvd01lYW5zKGV4cHJfbWF0X3JlZ1ssIGlkeCwgZHJvcCA9IEZBTFNFXSkKfSkKCiMgWi1zY29yZSBhbmQgY2xpcApiaW5fel9yZWcgICAgICAgICAgICAgPC0gdChzY2FsZSh0KGJpbl9tYXRfcmVnKSkpCmJpbl96X3JlZ1tpcy5uYW4oYmluX3pfcmVnKV0gPC0gMApiaW5fel9yZWcgICAgICAgICAgICAgPC0gcG1pbihwbWF4KGJpbl96X3JlZywgLTIpLCAyKQoKIyBBZGQgcHNldWRvdGltZSBheGlzIGFubm90YXRpb24gKEVhcmx5IOKGkiBMYXRlKQpjb2xfYW5ub3RhdGlvbiA8LSBkYXRhLmZyYW1lKAogIFBzZXVkb3RpbWUgPSBzZXEoMCwgMSwgbGVuZ3RoLm91dCA9IG5fYmlucyksCiAgcm93Lm5hbWVzICA9IHBhc3RlMCgiYmluIiwgc2VxX2xlbihuX2JpbnMpKQopCmNvbG5hbWVzKGJpbl96X3JlZykgPC0gcGFzdGUwKCJiaW4iLCBzZXFfbGVuKG5fYmlucykpCgphbm5vdGF0aW9uX2NvbG9ycyA8LSBsaXN0KAogIFBzZXVkb3RpbWUgPSBjb2xvclJhbXBQYWxldHRlKGMoImdyZXk5MCIsIiNGRkQ3MDAiKSkoMTAwKQopCgojIFBsb3QgcmVndWxhdG9yeSBsaW5lYWdlIGhlYXRtYXAKcGhlYXRtYXA6OnBoZWF0bWFwKAogIGJpbl96X3JlZywKICBjbHVzdGVyX2NvbHMgICAgICA9IEZBTFNFLAogIGNsdXN0ZXJfcm93cyAgICAgID0gRkFMU0UsCiAgY29sb3IgICAgICAgICAgICAgPSBjb2xvclJhbXBQYWxldHRlKGMoIm5hdnkiLCJ3aGl0ZSIsImZpcmVicmljazMiKSkoMTAwKSwKICBicmVha3MgICAgICAgICAgICA9IHNlcSgtMiwgMiwgbGVuZ3RoLm91dCA9IDEwMSksCiAgbWFpbiAgICAgICAgICAgICAgPSAiR2VuZSBleHByZXNzaW9uIGFsb25nIHJlZ3VsYXRvcnkgcHNldWRvdGltZVxuVG5haXZlIOKGkiBUY20g4oaSIFRyZWciLAogIGZvbnRzaXplX3JvdyAgICAgID0gOSwKICBib3JkZXJfY29sb3IgICAgICA9IE5BLAogIHNob3dfY29sbmFtZXMgICAgID0gRkFMU0UsCiAgYW5ub3RhdGlvbl9jb2wgICAgPSBjb2xfYW5ub3RhdGlvbiwKICBhbm5vdGF0aW9uX2NvbG9ycyA9IGFubm90YXRpb25fY29sb3JzLAogIGFubm90YXRpb25fbmFtZXNfY29sID0gRkFMU0UsCiAgZ2Fwc19yb3cgICAgICAgICAgPSBjKDQsIDgsIDEyKSAgIyBUbmFpdmUgfCBUY20gfCBUcmVnIGNvcmUgfCBUcmVnIGVmZmVjdG9yCikKCiMgU2F2ZQpkZXYuY29weShwbmcsICJwc2V1ZG90aW1lX2hlYXRtYXBfcmVndWxhdG9yeS5wbmciLAogICAgICAgICB3aWR0aCA9IDEyLCBoZWlnaHQgPSA3LCB1bml0cyA9ICJpbiIsIHJlcyA9IDMwMCkKZGV2Lm9mZigpCmBgYAoKCgoKCgoKCgoKCgoKCgoKCgojIFNhdmUgRmluYWwgT2JqZWN0CmBgYHtyIHNhdmV9CkRlZmF1bHRBc3NheShjbGVhbl9vYmopIDwtICJpbnRlZ3JhdGVkIgoKc2F2ZVJEUygKICBjbGVhbl9vYmosCiAgIkNENF9yZWZlcmVuY2VfY2xlYW5fQXppbXV0aF9TbGluZ3Nob3QucmRzIiwKICBjb21wcmVzcyA9IEZBTFNFCikKCmNhdCgi4pyFIFNhdmVkOiIsIG5jb2woY2xlYW5fb2JqKSwgImNlbGxzXG4iKQpjYXQoIlxuRmluYWwgdHJhamVjdG9yeSBzdGF0ZSBkaXN0cmlidXRpb246XG4iKQpwcmludCh0YWJsZShjbGVhbl9vYmokdHJhamVjdG9yeV9zdGF0ZSwgdXNlTkEgPSAiaWZhbnkiKSkKY2F0KCJcblBzZXVkb3RpbWUgY29sdW1ucyBhZGRlZDpcbiIpCmNhdCgiICAtIFBUX2VmZmVjdG9yICAgKFRuYWl2ZSDihpIgVGNtIOKGkiBUZW0g4oaSIFRlbXJhKVxuIikKY2F0KCIgIC0gUFRfcmVndWxhdG9yeSAoVG5haXZlIOKGkiBUY20g4oaSIFRyZWcpXG4iKQpjYXQoIlxuT2JqZWN0IHJlYWR5IGZvciBtYWxpZ25hbnQgY2VsbCBwcm9qZWN0aW9uIHZpYSBNYXBRdWVyeVxuIikKY2F0KCJyZXR1cm4ubW9kZWwgPSBUUlVFIGNvbmZpcm1lZCDigJQgTWFwUXVlcnkgd2lsbCB3b3JrXG4iKQpgYGAK