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"
)
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
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")

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)
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")

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)
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()
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

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

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