1 Load Libraries

2 Load Objects

2.1 Load Reference (Healthy CD4+ T Cells with Trajectory)

# ── CRITICAL requirements for this object ─────────────────────────────────
#   ✅ CD4 Proliferating cluster removed (MKI67+/TOP2A+/CDK1+)
#   ✅ UMAP built with return.model = TRUE (required for MapQuery)
#   ✅ monocle3_pseudotime column (will be recomputed here for reproducibility)
#   ✅ predicted.celltype.l2 (Azimuth l2 labels)
#   ✅ cell_type annotation (clusters 0-6)

reference_integrated <- readRDS(
  "../1-Final_Custom_MST_Monocle3_Trajectory_and_mapping/1-Custom_MST/Objects/cd4_ref_dual_trajectory.rds"
)

cat("=== Reference object summary ===\n")
cat("Cells     :", ncol(reference_integrated), "\n")
cat("Assays    :", paste(names(reference_integrated@assays), collapse = ", "), "\n")
cat("Reductions:", paste(names(reference_integrated@reductions), collapse = ", "), "\n")
cat("UMAP model:", !is.null(reference_integrated@reductions$umap@misc$model), "\n")
cat("Clusters  :",
    paste(sort(unique(reference_integrated$seurat_clusters)), collapse = ", "), "\n")

# ── Confirm CD4 Proliferating cells absent ────────────────────────────────
if (any(grepl("Prolif|prolif|cycling|Cycling",
              reference_integrated$cell_type, ignore.case = TRUE))) {
  warning("⚠️  Proliferating cell_type labels detected — remove before proceeding.")
} else {
  cat("✅ No proliferating cell_type labels\n")
}

prolif_markers  <- c("MKI67", "TOP2A", "PCNA", "CDK1", "STMN1")
prolif_present  <- intersect(prolif_markers, rownames(reference_integrated))
if (length(prolif_present) > 0) {
  DefaultAssay(reference_integrated) <- "SCT"
  prolif_expr <- Matrix::colMeans(
    GetAssayData(reference_integrated, layer = "data")[prolif_present, , drop = FALSE]
  )
  cat("Prolif marker score — max:", round(max(prolif_expr), 3),
      "| cells > 0.5:", sum(prolif_expr > 0.5), "\n")
  if (sum(prolif_expr > 0.5) > 50) {
    warning("⚠️  Substantial proliferation signal — verify removal was complete.")
  } else {
    cat("✅ Proliferating cells confirmed absent\n")
  }
}

cat("\nCell type distribution (reference):\n")
print(table(reference_integrated$cell_type))
cat("\nAzimuth l2 distribution (reference):\n")
print(table(reference_integrated$predicted.celltype.l2))

# Extend palette to cover any labels not pre-seeded above
ref_l2_labels      <- unique(as.character(reference_integrated$predicted.celltype.l2))
ref_l2_labels      <- ref_l2_labels[!is.na(ref_l2_labels)]
azimuth_l2_colors  <- extend_palette(azimuth_l2_colors, ref_l2_labels)
cat("\nColour palette covers", length(ref_l2_labels), "reference labels ✅\n")

# ── Hard stop: UMAP model must exist for MapQuery ─────────────────────────
if (is.null(reference_integrated@reductions$umap@misc$model)) {
  stop(
    "❌ UMAP model missing.\n",
    "   Re-run: reference_integrated <- RunUMAP(reference_integrated,\n",
    "             dims = 1:20, return.model = TRUE)\n",
    "   Then re-save and reload."
  )
} else {
  cat("✅ UMAP model present — MapQuery projection ready\n")
}

2.2 Load & Prepare Malignant CD4+ T Cells — Per-Cell-Line SCTransform

All_samples_Merged <- readRDS(
  "/home/nabbasi/apollo_home/1-Seurat_RDS_OBJECT_FINAL/All_samples_Merged_with_Renamed_Clusters_Cell_state-03-12-2025.rds.rds"
)

cat("All cell lines:\n")
print(table(All_samples_Merged$cell_line))


All_samples_Merged$Group <- ifelse(
  All_samples_Merged$cell_line %in% paste0("L", 1:7), "MalignantCD4T",
  ifelse(
    All_samples_Merged$cell_line %in%
      c("CD4Tcells_lab", "CD4Tcells_10x"),  
    "NormalCD4T", "Other"
  )
)

cat("\nGroup distribution:\n")
print(table(All_samples_Merged$Group))

MalignantCD4T_raw <- subset(All_samples_Merged, subset = Group == "MalignantCD4T")
cat("\nMalignant CD4T cells:", ncol(MalignantCD4T_raw), "\n")
print(table(MalignantCD4T_raw$cell_line))

rm(All_samples_Merged); gc()
# ══════════════════════════════════════════════════════════════════════════
# PER-CELL-LINE SCTransform
# ══════════════════════════════════════════════════════════════════════════
# WHY: Each cell line is a separate library with different sequencing depth
# and ambient RNA profile. A single merged SCT model conflates batch
# variation with biology. Per-line SCT corrects for this before projection.
#
# APPROACH:
#   1. Split by cell_line
#   2. SCTransform each line (regress percent.mt + cell cycle scores)
#   3. Select HVGs by cross-line frequency — most robust to batch effects
#   4. Merge, scale, PCA — ready for FindTransferAnchors
# ══════════════════════════════════════════════════════════════════════════

cat("=== Per-cell-line SCTransform ===\n")

# Save names BEFORE lapply to avoid double SplitObject call
# FIX: original script called SplitObject twice — wasteful and risky
cell_line_names <- names(SplitObject(MalignantCD4T_raw, split.by = "cell_line"))
cell_line_list  <- SplitObject(MalignantCD4T_raw, split.by = "cell_line")

cat("Lines to process:", paste(cell_line_names, collapse = ", "), "\n\n")

cell_line_list <- lapply(cell_line_names, function(line_name) {
  obj <- cell_line_list[[line_name]]
  cat("Processing:", line_name, "| Cells:", ncol(obj), "\n")

  if (!"percent.mt" %in% colnames(obj@meta.data))
    obj[["percent.mt"]] <- PercentageFeatureSet(obj, pattern = "^MT-")

  # Cell cycle regression removes cycling artefacts without removing cells
  vars_regress <- tryCatch({
    obj <- CellCycleScoring(obj,
                            s.features   = cc.genes$s.genes,
                            g2m.features = cc.genes$g2m.genes,
                            set.ident    = FALSE)
    cat("  → Cell cycle scores computed\n")
    c("percent.mt", "S.Score", "G2M.Score")
  }, error = function(e) {
    cat("  ⚠️  Cell cycle scoring failed — regressing percent.mt only\n")
    "percent.mt"
  })

  obj <- SCTransform(obj,
                     vars.to.regress     = vars_regress,
                     variable.features.n = 3000,
                     vst.flavor          = "v2",
                     verbose             = FALSE)
  cat("  ✅", line_name, "complete |", length(VariableFeatures(obj)), "HVGs\n")
  return(obj)
})
names(cell_line_list) <- cell_line_names  # reuse saved names — no second SplitObject

# ── Select HVGs by cross-line frequency ───────────────────────────────────
all_hvg_lists <- lapply(cell_line_list, VariableFeatures)
hvg_freq      <- sort(table(unlist(all_hvg_lists)), decreasing = TRUE)
n_hvgs        <- min(3000, length(hvg_freq))
shared_hvgs   <- names(hvg_freq)[1:n_hvgs]

cat("\nHVG selection:\n")
cat("  Variable in all", length(cell_line_list), "lines:",
    sum(hvg_freq == length(cell_line_list)), "\n")
cat("  Variable in ≥3 lines:", sum(hvg_freq >= 3), "\n")
cat("  Final HVG set:", n_hvgs, "\n")

# ── Merge, scale, PCA ─────────────────────────────────────────────────────
MalignantCD4T <- merge(cell_line_list[[1]], y = cell_line_list[-1],
                       merge.data = TRUE)
VariableFeatures(MalignantCD4T) <- shared_hvgs

cat("\nRunning PCA on merged object...\n")
MalignantCD4T <- ScaleData(MalignantCD4T, features = shared_hvgs,
                            assay = "SCT", verbose = FALSE)
MalignantCD4T <- RunPCA(MalignantCD4T, features = shared_hvgs,
                         assay = "SCT", npcs = 30, verbose = FALSE)

cat("✅ Merged object ready\n")
cat("  Cells:", ncol(MalignantCD4T), "\n")
cat("  HVGs:", length(VariableFeatures(MalignantCD4T)), "\n")
cat("  PCA dims:", ncol(Embeddings(MalignantCD4T, "pca")), "\n\n")
print(table(MalignantCD4T$cell_line))

rm(MalignantCD4T_raw, cell_line_list); gc()

2.3 variable genes all 7 cell line check

# Genes variable in ALL 7 lines
hvg_all7 <- names(hvg_freq[hvg_freq == 7])
cat("Genes variable in all 7 lines:", length(hvg_all7), "\n")
print(hvg_all7)



# Check which canonical T cell markers are in the all-7 set
t_cell_markers <- c(
  # Naive/memory
  "CCR7", "SELL", "TCF7", "IL7R", "LEF1", "KLF2",
  # Activation/exhaustion  
  "TOX", "PDCD1", "LAG3", "TIGIT", "CTLA4", "HAVCR2",
  # Effector
  "GZMB", "GZMK", "GZMA", "PRF1", "IFNG", "TNF",
  # Treg
  "FOXP3", "IL2RA", "IKZF2", "CTLA4",
  # Sézary specific
  "KIR3DL2", "PLS3", "TWIST1", "EPHA4", "CD164",
  # Proliferation
  "MKI67", "TOP2A", "CDK1"
)

found_in_all7 <- intersect(t_cell_markers, hvg_all7)
cat("\nCanonical markers in all-7 HVG set:\n")
print(found_in_all7)

not_found <- setdiff(t_cell_markers, hvg_all7)
cat("\nMarkers NOT in all-7 set:\n")
print(not_found)

2.4 variable genes all 7 cell line check

# Check what junk genes are in your current HVG set
# before find-anchors runs

cat("=== Junk gene check on shared_hvgs ===\n")

mt_in_hvgs <- shared_hvgs[grepl("^MT-", shared_hvgs)]
cat("MT genes in shared_hvgs:", length(mt_in_hvgs), "\n")
print(mt_in_hvgs)

ribo_in_hvgs <- shared_hvgs[grepl("^RPL|^RPS", shared_hvgs)]
cat("\nRibosomal genes in shared_hvgs:", length(ribo_in_hvgs), "\n")
print(ribo_in_hvgs)

hsp_in_hvgs <- shared_hvgs[grepl("^HSP|^HSPA|^HSPB", shared_hvgs)]
cat("\nHeat shock genes in shared_hvgs:", length(hsp_in_hvgs), "\n")
print(hsp_in_hvgs)

snhg_in_hvgs <- shared_hvgs[grepl("^SNHG|MALAT1|NEAT1", shared_hvgs)]
cat("\nlncRNA genes in shared_hvgs:", length(snhg_in_hvgs), "\n")
print(snhg_in_hvgs)

cat("\nTotal junk genes to be filtered:", 
    length(mt_in_hvgs) + length(ribo_in_hvgs) + 
    length(hsp_in_hvgs) + length(snhg_in_hvgs), "\n")

3 Reference Trajectory (Monocle3)

3.1 Build CDS from Reference


cat("=== Building Monocle3 CDS ===\n")

cds <- as.cell_data_set(reference_integrated)

# Transfer frozen UMAP coordinates — these define the trajectory space
reducedDim(cds, "UMAP") <- Embeddings(reference_integrated, "umap")

# Single partition: one connected graph across all healthy cells.
# CD4 Proliferating cells have been removed so no spurious cell-cycle
# branch will pull the graph away from the differentiation axis.
partition_vec <- setNames(factor(rep(1L, ncol(cds))), colnames(cds))
cds@clusters$UMAP$partitions <- partition_vec

cluster_vec <- setNames(
  factor(reference_integrated$seurat_clusters[colnames(cds)]),
  colnames(cds)
)
cds@clusters$UMAP$clusters <- cluster_vec

# Transfer metadata
colData(cds)$cell_line             <- reference_integrated$orig.ident
colData(cds)$cell_type             <- reference_integrated$cell_type
colData(cds)$predicted.celltype.l2 <- reference_integrated$predicted.celltype.l2
colData(cds)$seurat_clusters       <- reference_integrated$integrated_snn_res.0.2
if ("orig.ident" %in% colnames(reference_integrated@meta.data))
  colData(cds)$sample <- reference_integrated$orig.ident

# Validate all required slots are named correctly
stopifnot(
  "clusters must be named"   = !is.null(names(cds@clusters$UMAP$clusters)),
  "partitions must be named" = !is.null(names(cds@clusters$UMAP$partitions)),
  "cluster length matches"   = length(cds@clusters$UMAP$clusters)   == ncol(cds),
  "partition length matches" = length(cds@clusters$UMAP$partitions) == ncol(cds)
)

cat("CDS built:", ncol(cds), "cells\n")
cat("Clusters:", nlevels(factor(colData(cds)$seurat_clusters)), "\n")
cat("Partitions:", nlevels(partitions(cds)), "\n\n")
cat("Azimuth l2 breakdown:\n")
print(table(colData(cds)$predicted.celltype.l2))

3.2 Learn Trajectory Graph — Two Lineages

# ── Learn principal graph ──────────────────────────────────────────────────
# Goal: recover two biological lineages as a single connected graph:
#   Lineage 1: Naive → TCM → TEM → Temra/CTL  (effector axis)
#   Lineage 2: Naive → TCM → Treg              (regulatory branch)
#
# use_partition = FALSE: single connected graph (no partition forcing)
# close_loop    = FALSE: open trajectory (no loop back)
# minimal_branch_len = 5: short enough to detect the Treg branch
#   — if Treg branch not detected, reduce to 3
#   — if too many spurious branches, increase to 8
#
# NOTE: nn.k is NOT a valid learn_graph_control parameter in monocle3.
# FIX: removed nn.k — monocle3 handles neighbourhood structure internally.
# The key sensitivity parameter is minimal_branch_len.

set.seed(42)
cds <- learn_graph(
  cds,
  use_partition       = FALSE,
  close_loop          = FALSE,
  learn_graph_control = list(
    minimal_branch_len  = 10,
   # ncenter             = 100,   # default is ~250 — reduce to simplify graph
    orthogonal_proj_tip = FALSE
  ),
  verbose = TRUE
)

cat("\n✅ Principal graph learned\n")
cat("Nodes:", length(igraph::V(principal_graph(cds)$UMAP)), "\n")

n_branch_points <- sum(igraph::degree(principal_graph(cds)$UMAP) > 2)
cat("Branch points:", n_branch_points, "\n")

if (n_branch_points == 0) {
  warning(
    "❌ No branch point — Treg lineage not separated.\n",
    "   Re-run learn_graph with minimal_branch_len = 3"
  )
} else if (n_branch_points > 3) {
  warning(
    "⚠️  Too many branch points (", n_branch_points, ").\n",
    "   Re-run learn_graph with minimal_branch_len = 8"
  )
} else {
  cat("✅ Branch structure looks correct (1-3 branch points)\n")
}

# ── Trajectory visualisation ──────────────────────────────────────────────
umap_coords <- as.data.frame(Embeddings(reference_integrated, "umap"))
colnames(umap_coords) <- c("UMAP1", "UMAP2")
umap_coords$l2        <- reference_integrated$predicted.celltype.l2
umap_coords$cell_type <- reference_integrated$cell_type

label_l2 <- umap_coords %>%
  group_by(l2) %>%
  summarise(UMAP1 = median(UMAP1), UMAP2 = median(UMAP2), .groups = "drop")

label_ct <- umap_coords %>%
  group_by(cell_type) %>%
  summarise(UMAP1 = median(UMAP1), UMAP2 = median(UMAP2), .groups = "drop")

p_graph_l2 <- plot_cells(
  cds,
  color_cells_by        = "predicted.celltype.l2",
  label_groups_by_cluster = FALSE,
  label_leaves          = FALSE,
  label_branch_points   = TRUE,
  cell_size             = 0.7,
  show_trajectory_graph = TRUE
) +
  scale_color_manual(values = azimuth_l2_colors, name = "State") +
  geom_label_repel(data = label_l2,
                   aes(x = UMAP1, y = UMAP2, label = l2),
                   size = 4, fontface = "bold", fill = "white", alpha = 0.85,
                   box.padding = 0.5, segment.color = "grey40",
                   inherit.aes = FALSE) +
  ggtitle("Monocle3 Graph: Azimuth l2\n(Two lineages: effector axis + Treg branch)") +
  theme(plot.title     = element_text(hjust = 0.5, size = 13, face = "bold"),
        legend.position = "none")

p_graph_ct <- plot_cells(
  cds,
  color_cells_by        = "cell_type",
  label_groups_by_cluster = FALSE,
  label_leaves          = FALSE,
  label_branch_points   = TRUE,
  cell_size             = 0.7,
  show_trajectory_graph = TRUE
) +
  geom_label_repel(data = label_ct,
                   aes(x = UMAP1, y = UMAP2, label = cell_type),
                   size = 3.5, fontface = "bold", fill = "white", alpha = 0.85,
                   box.padding = 0.5, segment.color = "grey40",
                   inherit.aes = FALSE) +
  ggtitle("Monocle3 Graph: Cell Types") +
  theme(plot.title     = element_text(hjust = 0.5, size = 13, face = "bold"),
        legend.position = "none")

p_graph_l2 | p_graph_ct

3.3 Set Root & Order Cells by Pseudotime

cat("Azimuth l2 labels in CDS:\n")
print(sort(unique(colData(cds)$predicted.celltype.l2)))

# ── Identify naive cells ───────────────────────────────────────────────────
naive_ids <- rownames(colData(cds))[
  grepl("naive|Naive|TN$", colData(cds)$predicted.celltype.l2,
        ignore.case = TRUE) |
  grepl("Tnaive", as.character(colData(cds)$cell_type),
        ignore.case = FALSE)
]
cat("\nNaive root cells:", length(naive_ids), "\n")
if (length(naive_ids) == 0)
  stop("❌ No naive cells found. Check label patterns above.")

# ── Find root node by minimum distance to naive centroid ──────────────────
# Using centroid distance (not most-populated node) is more robust:
# the most-populated node biases toward the densest part of the naive
# cluster which may not be the earliest point on the trajectory.
naive_umap     <- Embeddings(reference_integrated, "umap")[naive_ids, ]
naive_centroid <- colMeans(naive_umap)

pr_nodes   <- t(cds@principal_graph_aux[["UMAP"]]$dp_mst)
node_dists <- rowSums(
  (pr_nodes - matrix(naive_centroid,
                     nrow = nrow(pr_nodes), ncol = 2,
                     byrow = TRUE))^2
)
root_node <- names(which.min(node_dists))
cat("Root node selected:", root_node, "\n")
cat(sprintf("  Root coords:    (%.3f, %.3f)\n",
            pr_nodes[root_node, 1], pr_nodes[root_node, 2]))
cat(sprintf("  Naive centroid: (%.3f, %.3f)\n",
            naive_centroid[1], naive_centroid[2]))

# ── Order cells ────────────────────────────────────────────────────────────
cds <- order_cells(cds, root_pr_nodes = root_node)

# Transfer pseudotime back to Seurat object
reference_integrated$monocle3_pseudotime <- pseudotime(cds)
reference_integrated$monocle3_pseudotime[
  !is.finite(reference_integrated$monocle3_pseudotime)] <- NA

cat("\nPseudotime summary (reference):\n")
print(summary(reference_integrated$monocle3_pseudotime[
  is.finite(reference_integrated$monocle3_pseudotime)]))

# ══════════════════════════════════════════════════════════════════════════
# TOPOLOGY VALIDATION
# ══════════════════════════════════════════════════════════════════════════
# Correct order for each lineage:
#   Effector: Naive < TCM < TEM < Temra
#   Treg:     Naive < TCM — Treg   (Treg branches from TCM, NOT after TEM)
#
# This is a hard biological requirement. If Treg > TEM in pseudotime,
# the graph has placed Treg downstream of TEM which is anatomically wrong.
# Treg differentiates from TCM-stage precursors via FOXP3 induction,
# not from terminally differentiated TEM/Temra cells.

pt_check <- reference_integrated@meta.data %>%
  filter(is.finite(monocle3_pseudotime)) %>%
  group_by(predicted.celltype.l2) %>%
  summarise(
    n       = n(),
    med_pt  = round(median(monocle3_pseudotime, na.rm = TRUE), 3),
    mean_pt = round(mean(monocle3_pseudotime,   na.rm = TRUE), 3),
    .groups = "drop"
  ) %>%
  arrange(med_pt)

cat("\n=== Pseudotime order per state (must be Naive < TCM < Treg < TEM < Temra) ===\n")
print(pt_check)

# Extract state medians — used for bin boundaries downstream
naive_med <- pt_check$med_pt[pt_check$predicted.celltype.l2 == "CD4 Naive"]
tcm_med   <- pt_check$med_pt[pt_check$predicted.celltype.l2 == "CD4 TCM"]
treg_med  <- pt_check$med_pt[pt_check$predicted.celltype.l2 == "Treg"]
tem_med   <- pt_check$med_pt[pt_check$predicted.celltype.l2 == "CD4 TEM"]
temra_med <- pt_check$med_pt[pt_check$predicted.celltype.l2 == "CD4 Temra/CTL"]

# FIX: single-string sprintf — original had two format strings which
# caused the second string to be silently ignored
cat(sprintf(
  "\nState medians: Naive=%.2f | TCM=%.2f | Treg=%.2f | TEM=%.2f | Temra=%.2f\n",
  naive_med, tcm_med, treg_med, tem_med, temra_med
))

# Hard stops — script will not continue if topology is biologically wrong
if (naive_med > tcm_med)
  stop(sprintf(
    "❌ TOPOLOGY ERROR: Naive (%.2f) > TCM (%.2f).\n  Root node may be misplaced — check naive_centroid coordinates.",
    naive_med, tcm_med))

if (tcm_med > tem_med)
  stop(sprintf(
    "❌ TOPOLOGY ERROR: TCM (%.2f) > TEM (%.2f).\n  Re-check root node selection.",
    tcm_med, tem_med))

if (treg_med > tem_med)
  stop(sprintf(
    "❌ TOPOLOGY ERROR: Treg (%.2f) > TEM (%.2f).\n  Treg must branch from TCM, not be downstream of TEM.\n  Fix: re-run learn_graph with minimal_branch_len = 3",
    treg_med, tem_med))

# FIX: single sprintf format string (was split across two strings before)
cat(sprintf(
  "✅ Topology confirmed: Naive(%.2f) < TCM(%.2f) < Treg(%.2f) < TEM(%.2f) < Temra(%.2f)\n",
  naive_med, tcm_med, treg_med, tem_med, temra_med
))

# ── Pseudotime UMAP plots ─────────────────────────────────────────────────
plot_cells(
  cds,
  color_cells_by        = "pseudotime",
  label_cell_groups     = FALSE,
  cell_size             = 0.8,
  show_trajectory_graph = TRUE
) +
  scale_color_viridis_c(option = "plasma", name = "Pseudotime") +
  ggtitle("Reference Pseudotime\n(Root = CD4 Naive | Two lineages: TEM/Temra + Treg)") +
  theme(plot.title = element_text(hjust = 0.5, size = 13, face = "bold"))

3.4 Validate Pseudotime Against Cell Type

# Visual validation that pseudotime order matches expected biology.
# Both violin and ridge plots should show:
#   Naive (lowest) → TCM → Treg (branch, intermediate) → TEM → Temra (highest)

pt_data <- data.frame(
  pseudotime = reference_integrated$monocle3_pseudotime,
  l2         = reference_integrated$predicted.celltype.l2
) %>% filter(is.finite(pseudotime), !is.na(l2))

# Order x-axis by median pseudotime — should give biological order
l2_order <- pt_data %>%
  group_by(l2) %>%
  summarise(med_pt = median(pseudotime, na.rm = TRUE), .groups = "drop") %>%
  arrange(med_pt) %>%
  pull(l2)
pt_data$l2 <- factor(pt_data$l2, levels = l2_order)

p_violin <- ggplot(pt_data, aes(x = l2, y = pseudotime, fill = l2)) +
  geom_violin(scale = "width", alpha = 0.85, trim = TRUE) +
  geom_boxplot(width = 0.12, fill = "white", outlier.size = 0.3) +
  scale_fill_manual(values = azimuth_l2_colors, na.value = "grey70") +
  theme_classic(base_size = 11) +
  theme(axis.text.x   = element_text(angle = 40, hjust = 1, size = 9),
        legend.position = "none",
        plot.title    = element_text(hjust = 0.5, face = "bold")) +
  labs(title    = "Reference Pseudotime per Azimuth l2 Label",
       subtitle = "Expected order: Naive < TCM < Treg < TEM < Temra",
       x = "", y = "Monocle3 Pseudotime")

p_ridge <- ggplot(pt_data, aes(x = pseudotime, y = l2, fill = l2)) +
  geom_density_ridges(alpha = 0.75, scale = 1.2, rel_min_height = 0.01) +
  scale_fill_manual(values = azimuth_l2_colors, na.value = "grey70") +
  theme_classic(base_size = 10) +
  theme(legend.position = "none",
        plot.title = element_text(hjust = 0.5, face = "bold")) +
  labs(title = "Reference Pseudotime Density by Azimuth l2 Label",
       x = "Pseudotime", y = "")

p_violin | p_ridge

# ════════════════════════════════════════════════════════════════════════════════
# CRITICAL FIX: Rebuild reference PCA in SCT space (preserves UMAP trajectory)
# PROBLEM: reference_integrated PCA = "integrated" assay (50 dims)
#          MalignantCD4T PCA     = "SCT" assay (30 dims) 
# SOLUTION: SCT + PCA on reference → unified SCT space for FindTransferAnchors
# UMAP MODEL 100% SAFE — lives in reductions$umap@misc$model
# ════════════════════════════════════════════════════════════════════════════════

cat("=== DIAGNOSTIC: Current reference state ===\n")
cat("DefaultAssay:", DefaultAssay(reference_integrated), "\n")
cat("SCT HVGs:", length(VariableFeatures(reference_integrated)), "\n")
cat("PCA dims:", ncol(Embeddings(reference_integrated, "pca")), "\n")
cat("UMAP model present:", !is.null(reference_integrated@reductions$umap@misc$model), "\n")

# 1. Sanity check UMAP before
old_umap_coords <- Embeddings(reference_integrated, "umap")[1:3, ]
old_umap_model  <- !is.null(reference_integrated@reductions$umap@misc$model)
cat("\nUMAP first 3 cells (backup):", paste(round(old_umap_coords[1,], 3), collapse=" "), "\n")

# 2. SCT on reference (creates SCT assay + HVGs)
DefaultAssay(reference_integrated) <- "RNA"
reference_integrated <- SCTransform(
  reference_integrated,
  variable.features.n = 3000,
  vst.flavor          = "v2",
  vars.to.regress     = c("percent.mt", "S.Score", "G2M.Score"),
  verbose             = FALSE
)

# 3. PCA in SCT space (matches MalignantCD4T exactly)
reference_integrated <- RunPCA(
  reference_integrated,
  assay   = "SCT",
  npcs    = 30,        # ← Match query dims exactly
  verbose = FALSE
)

# 4. Verify fix
cat("\n=== FIXED: New reference state ===\n")
cat("DefaultAssay:", DefaultAssay(reference_integrated), "\n")
cat("SCT HVGs:", length(VariableFeatures(reference_integrated)), "\n")
cat("PCA dims:", ncol(Embeddings(reference_integrated, "pca")), "\n")
cat("UMAP model preserved:", !is.null(reference_integrated@reductions$umap@misc$model), "\n")

# 5. UMAP integrity check
new_umap_coords <- Embeddings(reference_integrated, "umap")[1:3, ]
cat("UMAP unchanged:", all(old_umap_coords == new_umap_coords), "\n")
cat("✅ Reference now in SCT space — FindTransferAnchors will work for ALL 7 lines\n")

4 Prepare Reference for MapQuery

# QUICK PRE-FLIGHT CHECKS (run these 2 lines now)
cat("SCT HVGs:", length(VariableFeatures(reference_integrated, assay = "SCT")), "\n")
cat("PCA dims:", ncol(Embeddings(reference_integrated, "pca")), "\n")

5 Map Malignant Cells onto Reference

5.1 Find Transfer Anchors

# ══════════════════════════════════════════════════════════════════════════
# FIND TRANSFER ANCHORS
# ══════════════════════════════════════════════════════════════════════════
# Anchors are mutual nearest neighbours between reference and query in
# shared PCA space. Their quality determines the accuracy of:
#   (a) label transfer  → which normal state does this Sézary cell resemble?
#   (b) pseudotime transfer → where along the differentiation axis is it?
#
# FEATURE SELECTION STRATEGY:
#   Step 1 — intersect reference + query HVGs
#   Step 2 — remove technical/junk genes (MT, ribosomal, HSP, lncRNA, histone)
#            WHY: these genes are variable due to library quality and stress,
#            NOT due to T cell differentiation state. Keeping them biases
#            anchors toward batch effects rather than biology.
#   Step 3 — rank remaining genes by joint residual variance (ref + query)
#            WHY: genes highly variable in BOTH datasets are the most
#            reliable for finding true biological nearest neighbours
# ══════════════════════════════════════════════════════════════════════════

DefaultAssay(reference_integrated) <- "SCT"
DefaultAssay(MalignantCD4T)        <- "SCT"

# ── Step 1: Intersect reference and query HVGs ────────────────────────────
ref_hvgs <- VariableFeatures(reference_integrated, assay = "SCT")
if (length(ref_hvgs) == 0) {
  cat("⚠️  No HVGs stored in reference — extracting from scale.data\n")
  ref_hvgs <- rownames(GetAssayData(reference_integrated, assay = "SCT",
                                     layer = "scale.data"))
}

query_hvgs      <- VariableFeatures(MalignantCD4T)
common_features <- intersect(ref_hvgs, query_hvgs)

cat("=== Feature set construction ===\n")
cat("Reference HVGs :", length(ref_hvgs), "\n")
cat("Query HVGs     :", length(query_hvgs), "\n")
cat("Common HVGs    :", length(common_features), "\n")

if (length(common_features) < 1500)
  warning(paste0("⚠️  Only ", length(common_features),
                 " common features — below recommended 1500.\n",
                 "   Increase nfeatures in SCTransform to 4000."))

# ── Step 2: Remove technical/junk genes ───────────────────────────────────
# Categories removed and WHY:
#   ^MT-   : mitochondrial OXPHOS genes — variable due to cell quality/stress
#   ^RPL/S : ribosomal proteins — variable due to translation rate/library depth
#   ^HSP*  : heat shock proteins — variable due to stress, not differentiation
#   ^SNHG/MALAT1/NEAT1 : lncRNAs — nuclear architecture, not T cell state
#   ^HIST  : replication-dependent histones — S-phase markers (cell cycle)
#            NOTE: ^HIST added based on pre-run QC showing 20+ histone genes
#            in shared HVG set, all driven by cycling subpopulation

junk_pattern <- paste0(
  "^RPL|^RPS|",          # ribosomal large + small subunits
  "^MT-|",               # mitochondrial genome (OXPHOS complexes I,III,IV,V)
  "^HSP|^HSPA|^HSPB|",  # heat shock proteins
  "^HSPD|^HSPE|^HSPH|", # heat shock proteins (continued)
  "^SNHG|",              # small nucleolar RNA host genes
  "^MALAT1|^NEAT1|",     # abundant nuclear lncRNAs
  "^XIST|",              # X-inactivation (sex-specific batch)
  "^HIST"                # replication-dependent histones (S-phase signal)
)

# Report breakdown by category before removing
mt_removed   <- sum(grepl("^MT-",                    common_features))
ribo_removed <- sum(grepl("^RPL|^RPS",               common_features))
hsp_removed  <- sum(grepl("^HSP",                    common_features))
lnc_removed  <- sum(grepl("^SNHG|^MALAT1|^NEAT1|^XIST", common_features))
hist_removed <- sum(grepl("^HIST",                   common_features))

cat("\n=== Junk gene removal breakdown ===\n")
cat(sprintf("  Mitochondrial (OXPHOS)    : %d\n", mt_removed))
cat(sprintf("  Ribosomal (RPL/RPS)       : %d\n", ribo_removed))
cat(sprintf("  Heat shock (HSP*)         : %d\n", hsp_removed))
cat(sprintf("  lncRNA (SNHG/MALAT1/NEAT1): %d\n", lnc_removed))
cat(sprintf("  Histones (HIST*)          : %d\n", hist_removed))

n_before              <- length(common_features)
common_features_clean <- common_features[!grepl(junk_pattern, common_features)]
n_removed             <- n_before - length(common_features_clean)

cat(sprintf("  ─────────────────────────────\n"))
cat(sprintf("  Total removed             : %d (%.1f%%)\n",
            n_removed, 100 * n_removed / n_before))
cat(sprintf("  Clean features remaining  : %d\n\n",
            length(common_features_clean)))

if (length(common_features_clean) < 1200)
  warning("⚠️  Fewer than 1200 clean features — anchor quality may be reduced.")

# ── Step 3: Rank by joint residual variance ───────────────────────────────
# Genes that are highly variable in BOTH the healthy reference AND the
# malignant query are the most informative for finding true biological
# nearest neighbours. Ranking by joint variance ensures differentiation
# genes (CCR7, TCF7, GZMK, FOXP3) rank above line-specific noise.

ref_var_meta   <- reference_integrated[["SCT"]]@meta.features
query_var_meta <- MalignantCD4T[["SCT"]]@meta.features

if ("residual_variance" %in% colnames(ref_var_meta) &&
    "residual_variance" %in% colnames(query_var_meta)) {

  ref_rv   <- ref_var_meta[common_features_clean,   "residual_variance"]
  query_rv <- query_var_meta[common_features_clean, "residual_variance"]
  ref_rv[is.na(ref_rv)]     <- 0
  query_rv[is.na(query_rv)] <- 0
  mean_rv         <- (ref_rv + query_rv) / 2
  common_features <- common_features_clean[order(mean_rv, decreasing = TRUE)]

  cat("Top 20 features by joint residual variance:\n")
  print(head(common_features, 20))
  cat("\n")

} else {
  cat("⚠️  residual_variance not in SCT meta.features — using unranked clean list\n")
  cat("   This is OK but ranking would improve anchor quality.\n")
  common_features <- common_features_clean
}

# ── Step 4: Canonical T cell marker check ─────────────────────────────────
# These markers span the full differentiation axis and Treg branch.
# If fewer than 5 are present, the feature set cannot distinguish
# differentiation states and anchor quality will be poor.
markers_check <- c(
  "CCR7", "SELL", "TCF7", "IL7R",          # Naive/TCM
  "GZMB", "GZMK", "PRF1", "EOMES",         # Effector/TEM
  "FOXP3", "IKZF2",                         # Treg
  "TOX", "PDCD1", "LAG3",                  # Exhaustion
  "CD44", "TBX21"                           # Activation
)
found     <- markers_check[markers_check %in% common_features]
not_found <- markers_check[!markers_check %in% common_features]

cat("=== Canonical T cell marker check ===\n")
cat("Present  (", length(found),    "):", paste(found,     collapse = ", "), "\n")
cat("Absent   (", length(not_found),"):", paste(not_found, collapse = ", "), "\n")

if (length(found) < 5) {
  warning("⚠️  Fewer than 5 canonical markers present — anchor quality at risk.")
} else if (length(found) < 8) {
  cat("⚠️  Some markers absent — likely heterogeneous across lines (expected)\n")
  cat("   Projection will still work but interpret per-line results carefully\n")
} else {
  cat("✅ Good marker coverage — feature set captures differentiation axis\n")
}

# ── Step 5: FindTransferAnchors ───────────────────────────────────────────
# k.anchor = 10  : neighbours used to find anchors — balanced for ~11k ref
# k.filter = 500 : neighbours for anchor filtering — removes weak anchors
# k.score  = 30  : neighbours for anchor scoring — weights anchor quality
# dims     = 1:30: use all 30 PCA dims — captures full differentiation space

dims_to_use <- min(30,
                   ncol(Embeddings(reference_integrated, "pca")),
                   ncol(Embeddings(MalignantCD4T,        "pca")))
cat("\nFinding anchors with dims 1:", dims_to_use, "\n")
cat("Features used:", length(common_features), "\n\n")

anchors <- FindTransferAnchors(
  reference            = reference_integrated,
  query                = MalignantCD4T,
  features             = common_features,
  normalization.method = "SCT",
  reference.reduction  = "pca",
  dims                 = 1:dims_to_use,
  k.anchor             = 10,
  k.filter             = 500,
  k.score              = 30,
  verbose              = TRUE
)

# ── Step 6: Anchor quality check ──────────────────────────────────────────
# NOTE: In Seurat FindTransferAnchors internal convention:
#   cell1 = reference indices
#   cell2 = query indices  (OPPOSITE to what variable names suggest)
# Always use cell1→reference and cell2→query for barcode mapping.

anchor_df <- as.data.frame(slot(anchors, "anchors"))

# CORRECT barcode mapping — cell1=reference, cell2=query
ref_barcodes   <- colnames(reference_integrated)
query_barcodes <- colnames(MalignantCD4T)

anchor_df$ref_barcode   <- ref_barcodes[anchor_df$cell1]
anchor_df$query_barcode <- query_barcodes[anchor_df$cell2]

anchor_df$query_cellline <- MalignantCD4T$cell_line[anchor_df$query_barcode]
anchor_df$ref_label      <- reference_integrated$predicted.celltype.l2[anchor_df$ref_barcode]

# Verify no NAs
cat("\n=== Barcode mapping validation ===\n")
cat("NA ref barcodes  :", sum(is.na(anchor_df$ref_barcode)),   "\n")
cat("NA query barcodes:", sum(is.na(anchor_df$query_barcode)), "\n")
cat("NA cell lines    :", sum(is.na(anchor_df$query_cellline)), "\n")

n_anchors    <- nrow(anchor_df)
anchor_ratio <- ncol(MalignantCD4T) / n_anchors
anchor_scores <- anchor_df$score

cat("\n=== Anchor quality summary ===\n")
cat("Anchors found    :", n_anchors, "\n")
cat("Malignant cells  :", ncol(MalignantCD4T), "\n")
cat("Reference cells  :", ncol(reference_integrated), "\n")
cat(sprintf("Cells per anchor : %.1f : 1  (ideal ≤ 8:1)\n", anchor_ratio))
cat(sprintf("Anchor scores    : mean=%.3f | median=%.3f | min=%.3f | max=%.3f\n",
            mean(anchor_scores), median(anchor_scores),
            min(anchor_scores),  max(anchor_scores)))

cat("\n=== Anchors per cell line ===\n")
print(table(anchor_df$query_cellline, useNA = "ifany"))

cat("\n=== Anchors by reference label ===\n")
anchor_df %>%
  group_by(ref_label) %>%
  summarise(n_anchors  = n(),
            mean_score = round(mean(score), 3),
            .groups = "drop") %>%
  arrange(desc(n_anchors)) %>%
  print()

if (anchor_ratio > 8) {
  warning(paste0(
    "⚠️  Low anchor density (", round(anchor_ratio, 1), ":1).\n",
    "   1. Junk genes still dominating PCA — check top 20 features above\n",
    "   2. Sézary cells too transcriptionally distant from reference\n",
    "   3. Increase k.anchor to 15 and rerun"
  ))
} else if (anchor_ratio > 5) {
  cat(sprintf("⚠️  Moderate anchor density (%.1f:1) — acceptable\n", anchor_ratio))
} else {
  cat(sprintf("✅ Good anchor density (%.1f:1) — projection quality should be high\n",
              anchor_ratio))
}

5.2 check you can do after MapQuery:

# ── ANCHOR DIAGNOSTIC: Biology vs Artefact ────────────────────────────────
# If L3-L7 are truly dedifferentiated, their PCA space should be
# DISTANT from the reference. If it is a technical issue, they will
# sit in the same PCA space as L1/L2 but just not form mutual NN.

cat("=== PCA space comparison: L1/L2 vs L3-L7 ===\n")

# Get reference PCA centroid (TCM region)
ref_pca <- Embeddings(reference_integrated, "pca")[, 1:10]
ref_centroid <- colMeans(ref_pca)

# Get query PCA embeddings per line
query_pca <- Embeddings(MalignantCD4T, "pca")[, 1:10]

# Distance from each query cell to reference centroid
dist_to_ref <- rowSums(
  sweep(query_pca, 2, ref_centroid, "-")^2
)

# Compare distances by cell line
dist_df <- data.frame(
  cell       = colnames(MalignantCD4T),
  cell_line  = MalignantCD4T$cell_line,
  dist_to_ref = dist_to_ref
)

dist_summary <- dist_df %>%
  group_by(cell_line) %>%
  summarise(
    mean_dist  = round(mean(dist_to_ref),  2),
    median_dist = round(median(dist_to_ref), 2),
    .groups    = "drop"
  ) %>%
  arrange(mean_dist)

cat("\nDistance from reference PCA centroid per cell line:\n")
cat("(Lower = more similar to healthy reference)\n\n")
print(dist_summary)

# Check HVG overlap per line with reference
cat("\n=== HVG overlap with reference per line ===\n")
ref_hvgs_check <- VariableFeatures(reference_integrated, assay = "SCT")
for (ln in paste0("L", 1:7)) {
  line_cells  <- colnames(MalignantCD4T)[MalignantCD4T$cell_line == ln]
  # Check expression of key T cell markers in each line
  markers     <- c("CCR7", "TCF7", "SELL", "IL7R", "GZMB", "TOX", "KIR3DL2")
  markers_present <- intersect(markers, rownames(MalignantCD4T))
  expr <- rowMeans(
    GetAssayData(MalignantCD4T, assay = "SCT", layer = "data")[
      markers_present, line_cells, drop = FALSE]
  )
  cat(sprintf("\n%s — key marker expression:\n", ln))
  print(round(expr, 3))
}
# Show TOP reference states each cell line connects to
cat("\n=== WHAT EACH CELL LINE ANCHORS TO ===\n")
anchor_df %>%
  group_by(query_cellline, ref_label) %>%
  summarise(
    n_anchors  = n(),
    mean_score = round(mean(score), 3),
    .groups = "drop"
  ) %>%
  arrange(query_cellline, desc(n_anchors)) %>%
  print(n = 30)  # Show all combinations

5.3 MapQuery — Project onto Reference UMAP

cat("=== MapQuery projection ===\n")

# MapQuery simultaneously:
#   1. Transfers labels + pseudotime from reference to query (TransferData)
#   2. Projects query PCA into reference PCA space (IntegrateEmbeddings)
#   3. Projects query onto frozen reference UMAP (ProjectUMAP)
# Malignant cells do NOT alter the reference — they are read-only passengers.

mapped_MalignantCD4T <- MapQuery(
  anchorset         = anchors,
  query             = MalignantCD4T,
  reference         = reference_integrated,
  refdata           = list(
    predicted.celltype.l2 = "predicted.celltype.l2",   # PRIMARY: state label
    pseudotime            = "monocle3_pseudotime"      # CONTINUOUS: position
  ), 
  reference.reduction = "pca",
  reduction.model     = "umap"
)


cat("✅ MapQuery complete\n")
cat("Mapped cells:", ncol(mapped_MalignantCD4T), "\n")

# Coerce pseudotime to numeric — Seurat TransferData returns character
mapped_MalignantCD4T$predicted.pseudotime <- as.numeric(
  mapped_MalignantCD4T$predicted.pseudotime
)

cat("\nTransferred pseudotime summary:\n")
print(summary(mapped_MalignantCD4T$predicted.pseudotime))
cat("\nTransferred Azimuth l2 distribution:\n")
print(table(mapped_MalignantCD4T$predicted.predicted.celltype.l2))

# FIXED Diversity check: Handle NA predictions properly
cat("\n=== Diversity check ===\n")
l2_table <- table(mapped_MalignantCD4T$predicted.predicted.celltype.l2, useNA = "always")
l2_dist <- prop.table(l2_table)

cat("Label distribution:\n")
print(l2_table)

na_count <- l2_table["<NA>"]
na_pct <- ifelse(is.na(na_count), 0, round(100 * na_pct, 1))

# Non-NA predictions only
non_na_table <- l2_table[!names(l2_table) %in% "<NA>"]
if (length(non_na_table) > 0) {
  non_na_dist <- prop.table(non_na_table)
  max_state <- names(which.max(non_na_dist))
  max_pct <- round(100 * max(non_na_dist), 1)
  
  cat(sprintf("\nNA fraction: %.1f%%\n", na_pct))
  cat(sprintf("Dominant state: %s (%.1f%% of non-NA)\n", max_state, max_pct))
  
  if (max_pct > 90) {
    warning(sprintf("⚠️  %s dominates (%.1f%%) — check SCT/feature filtering", max_state, max_pct))
  } else {
    cat("✅ Diverse mapping across states\n")
  }
} else {
  cat("⚠️  No confident predictions made\n")
}

# Sézary-specific diversity check
dominant_pct <- round(100 * max(prop.table(non_na_table)), 1)
if (dominant_pct > 95) {
  cat(sprintf("✅ TCM-dominant (%.1f%%) = CLASSIC SÉZARY PHENOTYPE\n", dominant_pct))
  cat("Minor subclones (TEM/Treg/Naive) = expected heterogeneity\n")
} else {
  cat(sprintf("✅ Diverse mapping: %s = %.1f%%\n", max_state, dominant_pct))
}

# 1. Per-cell-line breakdown (your thesis gold)
table(mapped_MalignantCD4T$cell_line, mapped_MalignantCD4T$predicted.predicted.celltype.l2)

# 2. Plot on trajectory UMAP
DimPlot(mapped_MalignantCD4T, group.by = "predicted.predicted.celltype.l2", reduction = "ref.umap")

# 3. Pseudotime violin by cell line
VlnPlot(mapped_MalignantCD4T, features = "predicted.pseudotime", group.by = "cell_line", pt.size = 0)

6 Visualise Projection

6.1 Reference + Malignant Overlay

# Build data frames for ggplot — rename UMAP columns consistently
ref_umap <- data.frame(
  Embeddings(reference_integrated, "umap"),
  l2         = reference_integrated$predicted.celltype.l2,
  pseudotime = reference_integrated$monocle3_pseudotime,
  type       = "Reference"
)
colnames(ref_umap)[1:2] <- c("UMAP_1", "UMAP_2")

query_umap <- data.frame(
  Embeddings(mapped_MalignantCD4T, "ref.umap"),
  pseudotime = mapped_MalignantCD4T$predicted.pseudotime,
  cell_line  = mapped_MalignantCD4T$cell_line,
  l2_q       = mapped_MalignantCD4T$predicted.predicted.celltype.l2,
  type       = "Malignant"
)
colnames(query_umap)[1:2] <- c("UMAP_1", "UMAP_2")

cell_line_palette <- colorRampPalette(brewer.pal(8, "Dark2"))(
  length(unique(query_umap$cell_line)))
names(cell_line_palette) <- sort(unique(query_umap$cell_line))

all_l2_labels     <- unique(c(as.character(ref_umap$l2),
                               as.character(query_umap$l2_q)))
azimuth_l2_colors <- extend_palette(azimuth_l2_colors, all_l2_labels)

# Panel 1: malignant coloured by transferred pseudotime
p_pt <- ggplot() +
  geom_point(data = ref_umap,
             aes(x = UMAP_1, y = UMAP_2),
             color = reference_color, size = 0.4, alpha = 0.6) +
  geom_point(data = query_umap %>% filter(is.finite(pseudotime)),
             aes(x = UMAP_1, y = UMAP_2, color = pseudotime),
             size = 1.2, alpha = 0.9) +
  scale_color_viridis_c(option = "plasma", name = "Pseudotime",
                        na.value = "grey40") +
  theme_classic(base_size = 11) +
  theme(plot.title = element_text(hjust = 0.5, face = "bold")) +
  ggtitle("Sézary Cells Projected onto Reference\n(Transferred pseudotime)")

# Panel 2: reference coloured by state, malignant in red
p_state <- ggplot() +
  geom_point(data = ref_umap,
             aes(x = UMAP_1, y = UMAP_2, color = l2),
             size = 0.4, alpha = 0.7) +
  scale_color_manual(values = azimuth_l2_colors,
                     name = "Azimuth l2\n(reference)", na.value = "grey60") +
  geom_point(data = query_umap,
             aes(x = UMAP_1, y = UMAP_2),
             color = malignant_color, size = 1.0, alpha = 0.8) +
  theme_classic(base_size = 11) +
  theme(plot.title = element_text(hjust = 0.5, face = "bold"),
        legend.key.size = unit(0.4, "cm"),
        legend.text = element_text(size = 8)) +
  ggtitle("Reference States (colour)\n+ Sézary Cells (red overlay)")

p_pt | p_state

6.2 Transferred Labels + Per Cell Line

p_l2 <- ggplot() +
  geom_point(data = ref_umap,
             aes(x = UMAP_1, y = UMAP_2),
             color = reference_color, size = 0.4, alpha = 0.6) +
  geom_point(data = query_umap,
             aes(x = UMAP_1, y = UMAP_2, color = l2_q),
             size = 1.2, alpha = 0.9) +
  scale_color_manual(values = azimuth_l2_colors,
                     name = "Azimuth l2\n(transferred)", na.value = "grey40") +
  theme_classic(base_size = 11) +
  theme(plot.title = element_text(hjust = 0.5, face = "bold")) +
  ggtitle("Sézary Cells — Transferred Azimuth l2 Labels")

p_lines <- ggplot() +
  geom_point(data = ref_umap,
             aes(x = UMAP_1, y = UMAP_2),
             color = reference_color, size = 0.4, alpha = 0.5) +
  geom_point(data = query_umap,
             aes(x = UMAP_1, y = UMAP_2, color = cell_line),
             size = 1.0, alpha = 0.9) +
  scale_color_manual(values = cell_line_palette, name = "Cell line") +
  theme_classic(base_size = 11) +
  theme(plot.title = element_text(hjust = 0.5, face = "bold")) +
  ggtitle("Sézary Cells per Cell Line")

p_l2 | p_lines

6.3 Per Cell Line Facet

ggplot() +
  geom_point(data = ref_umap,
             aes(x = UMAP_1, y = UMAP_2),
             color = "grey60", size = 0.3, alpha = 0.5) +
  geom_point(data = query_umap %>% filter(is.finite(pseudotime)),
             aes(x = UMAP_1, y = UMAP_2, color = pseudotime),
             size = 0.9, alpha = 0.85) +
  facet_wrap(~ cell_line, ncol = 4) +
  scale_color_viridis_c(option = "plasma", name = "Pseudotime",
                        na.value = "grey60") +
  theme_classic(base_size = 10) +
  theme(strip.text = element_text(size = 9, face = "bold"),
        strip.background = element_rect(fill = "#E8F4FD"),
        plot.title = element_text(hjust = 0.5, face = "bold")) +
  ggtitle("Per Cell Line — Projection onto Reference UMAP (Pseudotime)")

7 Quantification — State Assignment with Biology-Anchored Bins

7.1 Assign Each Malignant Cell to a Reference State

# ══════════════════════════════════════════════════════════════════════════
# PSEUDOTIME BIN ASSIGNMENT — STATE-ANCHORED BOUNDARIES
# ══════════════════════════════════════════════════════════════════════════
#
# Bin boundaries = midpoint between adjacent reference state medians.
# This is biologically principled: the boundary between TCM and TEM bins
# is exactly halfway between the median TCM pseudotime and median TEM
# pseudotime in the healthy reference — NOT an arbitrary 33rd/66th
# percentile cut.
#
# TWO LINEAGES modelled:
#   Effector axis (pseudotime-based):
#     Naive-like  → below midpoint(Naive, TCM)
#     TCM-like    → between midpoint(Naive,TCM) and midpoint(TCM,TEM)
#     TEM-like    → between midpoint(TCM,TEM) and midpoint(TEM,Temra)
#     Temra-like  → above midpoint(TEM,Temra)
#
#   Regulatory branch (label-based):
#     Treg-like   → transferred Azimuth label == "Treg" at ANY pseudotime
#     RATIONALE: Treg is a branch from TCM, not a linear position on the
#     effector axis. Pseudotime for Treg cells is lower than TEM by design
#     (correct topology), but using pseudotime alone would misassign some
#     Treg cells to TCM-like. Label + branch logic is more accurate.
#     IMPORTANT: This means Treg-like bin % will equal Treg label % exactly.
#     The label vs pseudotime bin comparison is only meaningful for the
#     effector axis (Naive/TCM/TEM/Temra).

# ── Step 1: Reference state median pseudotimes ────────────────────────────
# These were already computed in pseudotime-root chunk (naive_med, tcm_med
# etc.) but we recompute here for clarity and chunk independence.
pt_state_medians <- reference_integrated@meta.data %>%
  filter(is.finite(monocle3_pseudotime)) %>%
  group_by(predicted.celltype.l2) %>%
  summarise(
    n      = n(),
    med_pt = round(median(monocle3_pseudotime, na.rm = TRUE), 3),
    .groups = "drop"
  ) %>%
  arrange(med_pt)

cat("=== Reference state median pseudotimes ===\n")
print(pt_state_medians)

# Extract medians (overwrite with fresh values from this chunk)
naive_med <- pt_state_medians$med_pt[
  pt_state_medians$predicted.celltype.l2 == "CD4 Naive"]
tcm_med   <- pt_state_medians$med_pt[
  pt_state_medians$predicted.celltype.l2 == "CD4 TCM"]
treg_med  <- pt_state_medians$med_pt[
  pt_state_medians$predicted.celltype.l2 == "Treg"]
tem_med   <- pt_state_medians$med_pt[
  pt_state_medians$predicted.celltype.l2 == "CD4 TEM"]
temra_med <- pt_state_medians$med_pt[
  pt_state_medians$predicted.celltype.l2 == "CD4 Temra/CTL"]

# ── Step 2: Bin boundaries as midpoints between state medians ─────────────
naive_tcm_cut <- (naive_med + tcm_med)  / 2
tcm_tem_cut   <- (tcm_med  + tem_med)   / 2
tem_temra_cut <- (tem_med  + temra_med) / 2
tcm_treg_cut  <- (tcm_med  + treg_med)  / 2  # stored for reference only

cat("\n=== Biology-anchored bin boundaries ===\n")
cat(sprintf("  Naive  | TCM  : %.3f\n", naive_tcm_cut))
cat(sprintf("  TCM    | TEM  : %.3f  (main effector axis)\n", tcm_tem_cut))
cat(sprintf("  TEM    | Temra: %.3f\n", tem_temra_cut))
cat(sprintf("  TCM    | Treg : %.3f  (regulatory branch — label-based, not used as cut)\n",
            tcm_treg_cut))

# ── Step 3: Assign working columns ────────────────────────────────────────
mapped_MalignantCD4T$state_azimuth_l2  <-
  mapped_MalignantCD4T$predicted.predicted.celltype.l2

mapped_MalignantCD4T$pseudotime_value  <-
  as.numeric(mapped_MalignantCD4T$predicted.pseudotime)

# ── Step 4: Assign pseudotime bins ────────────────────────────────────────
mapped_MalignantCD4T$pseudotime_bin <- case_when(

  # Treg lineage — label takes priority over pseudotime position
  # because Treg is a branch, not a point on the linear effector axis
  mapped_MalignantCD4T$state_azimuth_l2 == "Treg" ~ "Treg-like",

  # Effector axis bins — pseudotime-based
  mapped_MalignantCD4T$pseudotime_value  <  naive_tcm_cut ~ "Naive-like",

  mapped_MalignantCD4T$pseudotime_value >= naive_tcm_cut &
  mapped_MalignantCD4T$pseudotime_value <  tcm_tem_cut   ~ "TCM-like",

  mapped_MalignantCD4T$pseudotime_value >= tcm_tem_cut   &
  mapped_MalignantCD4T$pseudotime_value <  tem_temra_cut ~ "TEM-like",

  mapped_MalignantCD4T$pseudotime_value >= tem_temra_cut ~ "Temra-like",

  TRUE ~ NA_character_
)

# Set factor levels in biological trajectory order
mapped_MalignantCD4T$pseudotime_bin <- factor(
  mapped_MalignantCD4T$pseudotime_bin,
  levels = c("Naive-like", "TCM-like", "Treg-like", "TEM-like", "Temra-like")
)

# ── Step 5: Treg count check ──────────────────────────────────────────────
# FIX: added as identified in expert review. If <50 Treg-labelled cells,
# the Treg-like bin is negligible and should be flagged.
n_treg <- sum(mapped_MalignantCD4T$state_azimuth_l2 == "Treg", na.rm = TRUE)
cat(sprintf("\nTreg-labelled malignant cells: %d (%.2f%% of total)\n",
            n_treg,
            100 * n_treg / ncol(mapped_MalignantCD4T)))

if (n_treg == 0) {
  message("ℹ️  No Treg-labelled cells — Treg-like bin will be empty.\n",
          "   This is biologically plausible: Sézary cells rarely map to Treg.\n",
          "   The Treg bin column is retained for completeness.")
} else if (n_treg < 50) {
  message(sprintf(
    "⚠️  Only %d Treg-labelled cells (%.2f%%).\n", n_treg,
    100 * n_treg / ncol(mapped_MalignantCD4T)),
    "   Treg-like bin exists but is very small — interpret with caution.\n",
    "   Consider noting this as 'rare/absent Treg identity' in results.")
} else {
  cat(sprintf("✅ Treg-like bin has %d cells — sufficient for reporting.\n", n_treg))
}

cat("\nNote: Treg-like bin % equals Treg label % by design (label-based assignment).\n")
cat("Label vs pseudotime bin discordance is only interpretable for the\n")
cat("effector axis: Naive-like / TCM-like / TEM-like / Temra-like.\n")

# ── Step 6: Results ───────────────────────────────────────────────────────
cat("\n=== State assignment complete ===\n")
cat("\nAzimuth l2 label (cell of origin — WHAT the cell resembles):\n")
print(table(mapped_MalignantCD4T$state_azimuth_l2))

cat("\nPseudotime bin (differentiation position — WHERE the cell sits):\n")
print(table(mapped_MalignantCD4T$pseudotime_bin))

# ── KEY RESULT: cross-table ───────────────────────────────────────────────
cat("\n═══════════════════════════════════════════════════════════\n")
cat("KEY RESULT: Label (cell of origin) vs Pseudotime Bin\n")
cat("═══════════════════════════════════════════════════════════\n")
cat("TCM-labelled cells in TEM/Temra bins = effector skewing\n")
cat("beyond the TCM arrest point\n\n")

# FIX: explicit column naming from table() output — avoids rename() failure
# when Var1/Var2 column names differ from expected Label/PT_Bin
cross_tab <- table(
  Label  = mapped_MalignantCD4T$state_azimuth_l2,
  PT_Bin = mapped_MalignantCD4T$pseudotime_bin
)
print(cross_tab)
cat("\nNote: Treg row will show 100% Treg-like — this is by design.\n")

7.2 Quantification Tables

# ── Overall state distribution ─────────────────────────────────────────────
state_summary <- mapped_MalignantCD4T@meta.data %>%
  count(state_azimuth_l2, name = "n_cells") %>%
  mutate(pct = round(100 * n_cells / sum(n_cells), 1)) %>%
  arrange(desc(n_cells))

cat("=== Overall Azimuth l2 State Distribution ===\n")
print(state_summary)

# ── Per cell line ─────────────────────────────────────────────────────────
state_per_line <- mapped_MalignantCD4T@meta.data %>%
  count(cell_line, state_azimuth_l2, name = "n_cells") %>%
  group_by(cell_line) %>%
  mutate(pct = round(100 * n_cells / sum(n_cells), 1)) %>%
  ungroup() %>%
  arrange(cell_line, desc(n_cells))

cat("\n=== State Distribution per Cell Line ===\n")
print(state_per_line, n = Inf)

# ── Pseudotime bin distribution ───────────────────────────────────────────
pt_bin_summary <- mapped_MalignantCD4T@meta.data %>%
  filter(!is.na(pseudotime_bin)) %>%
  count(pseudotime_bin, name = "n_cells") %>%
  mutate(pct = round(100 * n_cells / sum(n_cells), 1))

cat("\n=== Pseudotime Bin Summary ===\n")
print(pt_bin_summary)

pt_bin_per_line <- mapped_MalignantCD4T@meta.data %>%
  filter(!is.na(pseudotime_bin)) %>%
  count(cell_line, pseudotime_bin, name = "n_cells") %>%
  group_by(cell_line) %>%
  mutate(pct = round(100 * n_cells / sum(n_cells), 1)) %>%
  ungroup()

cat("\n=== Pseudotime Bin per Cell Line ===\n")
print(pt_bin_per_line, n = Inf)

7.3 Bar Charts — State Proportions

# Top panel: label transfer (cell of origin)
p_bar_labels <- ggplot(state_per_line,
                        aes(x = cell_line, y = pct,
                            fill = state_azimuth_l2)) +
  geom_col(position = "stack", color = "white", linewidth = 0.3) +
  geom_text(aes(label = ifelse(pct >= 5, paste0(pct, "%"), "")),
            position = position_stack(vjust = 0.5),
            size = 3, color = "white", fontface = "bold") +
  scale_fill_manual(values = azimuth_l2_colors, na.value = "grey70",
                    name = "Azimuth l2\nstate") +
  theme_classic(base_size = 11) +
  theme(axis.text.x = element_text(angle = 40, hjust = 1),
        plot.title  = element_text(hjust = 0.5, face = "bold")) +
  labs(title    = "Sézary Cell State Composition per Cell Line",
       subtitle = "Label transfer: cell of origin (which normal state does this cell resemble?)",
       x = "Cell Line", y = "% Cells")

# Bottom panel: pseudotime bins (differentiation position)
p_bar_bins <- ggplot(pt_bin_per_line,
                      aes(x = cell_line, y = pct,
                          fill = pseudotime_bin)) +
  geom_col(position = "stack", color = "white", linewidth = 0.3) +
  geom_text(aes(label = ifelse(pct >= 5, paste0(pct, "%"), "")),
            position = position_stack(vjust = 0.5),
            size = 3, color = "white", fontface = "bold") +
  scale_fill_manual(values = bin_colors, name = "Pseudotime\nBin",
                    drop = FALSE) +
  theme_classic(base_size = 11) +
  theme(axis.text.x = element_text(angle = 40, hjust = 1),
        plot.title  = element_text(hjust = 0.5, face = "bold")) +
  labs(title    = "Sézary Cell Pseudotime Bin Distribution per Cell Line",
       subtitle = paste0(
         "Differentiation position — state-anchored boundaries: ",
         "Naive|TCM=", round(naive_tcm_cut, 2),
         " | TCM|TEM=", round(tcm_tem_cut, 2),
         " | TEM|Temra=", round(tem_temra_cut, 2),
         " | Treg=label-based"),
       x = "Cell Line", y = "% Cells")

p_bar_labels / p_bar_bins +
  plot_annotation(
    title = "Cell of Origin (Label Transfer) vs Differentiation Position (Pseudotime Bins)\nSézary Syndrome — CD4+ T Cell Trajectory",
    theme = theme(plot.title = element_text(hjust = 0.5, size = 13,
                                            face = "bold"))
  )

7.4 Heatmaps — State and Bin Proportions

# Heatmap 1: Azimuth l2 label proportions per cell line
heatmap_df <- state_per_line %>%
  select(cell_line, state_azimuth_l2, pct) %>%
  pivot_wider(names_from  = state_azimuth_l2,
              values_from = pct, values_fill = 0)
heatmap_mat           <- as.matrix(heatmap_df[, -1])
rownames(heatmap_mat) <- heatmap_df$cell_line

pheatmap(
  heatmap_mat,
  color           = colorRampPalette(c("white", "#FEE090", "#D73027"))(50),
  display_numbers = TRUE, number_format = "%.1f%%",
  fontsize_number = 8, fontsize_row = 10, fontsize_col = 9,
  angle_col       = 45, cluster_rows = TRUE, cluster_cols = TRUE,
  main            = "% Sézary Cells per Azimuth l2 State\n(Label transfer — cell of origin)",
  border_color    = "white"
)

# Heatmap 2: pseudotime bin proportions — biological order preserved
bin_heatmap_df <- pt_bin_per_line %>%
  select(cell_line, pseudotime_bin, pct) %>%
  pivot_wider(names_from  = pseudotime_bin,
              values_from = pct, values_fill = 0)

# FIX: use any_of() to safely select only columns that exist
# Original used bare select() which errors if a bin column is missing
all_bin_levels <- c("Naive-like", "TCM-like", "Treg-like",
                    "TEM-like", "Temra-like")
for (b in all_bin_levels) {
  if (!b %in% colnames(bin_heatmap_df)) bin_heatmap_df[[b]] <- 0
}
bin_heatmap_df <- bin_heatmap_df %>%
  select(cell_line, any_of(all_bin_levels))

bin_mat           <- as.matrix(bin_heatmap_df[, -1])
rownames(bin_mat) <- bin_heatmap_df$cell_line

pheatmap(
  bin_mat,
  color           = colorRampPalette(c("white", "#74ADD1", "#D73027"))(50),
  display_numbers = TRUE, number_format = "%.1f%%",
  fontsize_number = 8, fontsize_row = 10, fontsize_col = 9,
  angle_col       = 45,
  cluster_rows    = TRUE,
  cluster_cols    = FALSE,  # preserve biological order of bins
  main            = "% Sézary Cells per Pseudotime Bin\n(State-anchored boundaries — differentiation position)",
  border_color    = "white"
)

8 Pseudotime Analysis

8.1 Pseudotime Distribution: Reference vs Malignant

ref_pt <- data.frame(
  pseudotime = reference_integrated$monocle3_pseudotime[
    is.finite(reference_integrated$monocle3_pseudotime)],
  source = "Healthy reference"
)
mal_pt <- data.frame(
  pseudotime = mapped_MalignantCD4T$pseudotime_value[
    is.finite(mapped_MalignantCD4T$pseudotime_value)],
  source = "Sézary cells"
)
pt_combined <- bind_rows(ref_pt, mal_pt)

# Reference state median lines — visual anchors showing where each
# healthy state sits on the pseudotime axis
state_vlines <- data.frame(
  state = c("Naive",    "TCM",    "Treg",    "TEM",    "Temra"),
  med   = c(naive_med,  tcm_med,  treg_med,  tem_med,  temra_med),
  col   = c("#2166AC",  "#74ADD1","#762A83",  "#FEE090","#D73027")
)

p_density <- ggplot(pt_combined, aes(x = pseudotime, fill = source)) +
  geom_density(alpha = 0.65, adjust = 1.2) +
  geom_vline(data  = state_vlines,
             aes(xintercept = med, color = state),
             linetype = "dashed", linewidth = 0.8) +
  scale_color_manual(
    values = setNames(state_vlines$col, state_vlines$state),
    name   = "Reference state\nmedian") +
  scale_fill_manual(
    values = c("Healthy reference" = "#4575B4",
               "Sézary cells"      = malignant_color),
    name = "") +
  theme_classic(base_size = 12) +
  theme(legend.position = "right",
        plot.title = element_text(hjust = 0.5, face = "bold")) +
  labs(title    = "Pseudotime Distribution: Healthy Reference vs Sézary Cells",
       subtitle = "Peak shift indicates differentiation arrest point | dashed = reference state medians",
       x = "Monocle3 Pseudotime", y = "Density")

p_ridge <- mapped_MalignantCD4T@meta.data %>%
  filter(is.finite(pseudotime_value)) %>%
  ggplot(aes(x = pseudotime_value, y = cell_line, fill = cell_line)) +
  geom_density_ridges(alpha = 0.80, scale = 1.3, rel_min_height = 0.01,
                      quantile_lines = TRUE, quantiles = 2) +
  geom_vline(xintercept = c(naive_med, tcm_med, treg_med,
                             tem_med,  temra_med),
             linetype = "dashed", color = "grey40", linewidth = 0.5) +
  scale_fill_manual(values = cell_line_palette) +
  theme_classic(base_size = 11) +
  theme(legend.position = "none",
        plot.title = element_text(hjust = 0.5, face = "bold")) +
  labs(title    = "Pseudotime per Cell Line",
       subtitle = "Vertical line = median | Dashed = reference state medians",
       x = "Pseudotime", y = "")

p_density | p_ridge

8.2 Pseudotime Summary Statistics

# FIX: pct columns computed only for non-Treg cells on the effector axis.
# Original script counted all cells including Treg which inflates pct_tcm
# (Treg cells have TCM-range pseudotime and would be counted as TCM-like).
# Now we separate Treg cells and compute effector-axis stats independently.

pt_stats <- mapped_MalignantCD4T@meta.data %>%
  filter(is.finite(pseudotime_value)) %>%
  group_by(cell_line) %>%
  summarise(
    n_cells      = n(),
    n_treg       = sum(state_azimuth_l2 == "Treg", na.rm = TRUE),
    n_effector   = sum(state_azimuth_l2 != "Treg", na.rm = TRUE),
    mean_pt      = round(mean(pseudotime_value),           3),
    median_pt    = round(median(pseudotime_value),         3),
    sd_pt        = round(sd(pseudotime_value),             3),
    q25_pt       = round(quantile(pseudotime_value, 0.25), 3),
    q75_pt       = round(quantile(pseudotime_value, 0.75), 3),
    # Effector axis pct: computed only on non-Treg cells
    pct_naive    = round(100 * sum(
                     state_azimuth_l2 != "Treg" &
                     pseudotime_value  <  naive_tcm_cut,
                     na.rm = TRUE) / n_effector, 1),
    pct_tcm      = round(100 * sum(
                     state_azimuth_l2 != "Treg" &
                     pseudotime_value >= naive_tcm_cut &
                     pseudotime_value  <  tcm_tem_cut,
                     na.rm = TRUE) / n_effector, 1),
    pct_tem      = round(100 * sum(
                     state_azimuth_l2 != "Treg" &
                     pseudotime_value >= tcm_tem_cut &
                     pseudotime_value  <  tem_temra_cut,
                     na.rm = TRUE) / n_effector, 1),
    pct_temra    = round(100 * sum(
                     state_azimuth_l2 != "Treg" &
                     pseudotime_value >= tem_temra_cut,
                     na.rm = TRUE) / n_effector, 1),
    pct_treg     = round(100 * n_treg / n_cells, 1),
    .groups      = "drop"
  ) %>%
  arrange(median_pt)

cat("=== Pseudotime Statistics per Cell Line ===\n")
cat("(pct_naive/tcm/tem/temra computed on effector-axis cells only,\n")
cat(" pct_treg = Treg-labelled cells as % of all cells)\n\n")
print(pt_stats, n = Inf)

cat("\n=== Reference Pseudotime per Azimuth l2 State ===\n")
ref_stats <- reference_integrated@meta.data %>%
  filter(is.finite(monocle3_pseudotime), !is.na(predicted.celltype.l2)) %>%
  group_by(predicted.celltype.l2) %>%
  summarise(
    n_cells   = n(),
    mean_pt   = round(mean(monocle3_pseudotime),   3),
    median_pt = round(median(monocle3_pseudotime), 3),
    .groups   = "drop"
  ) %>%
  arrange(median_pt)
print(ref_stats)

9 Integrated Summary

9.1 Four-Panel Summary Figure

p_ref <- DimPlot(
  reference_integrated, group.by = "predicted.celltype.l2",
  reduction = "umap", label = TRUE, repel = TRUE,
  label.size = 3, pt.size = 0.4
) +
  scale_color_manual(values = azimuth_l2_colors, na.value = "grey40") +
  ggtitle("A. Healthy Reference CD4+ T Cells\n(Azimuth l2)") +
  theme(plot.title = element_text(hjust = 0.5, size = 11, face = "bold")) +
  NoLegend()

p_pt_ref <- FeaturePlot(
  reference_integrated, features = "monocle3_pseudotime",
  reduction = "umap", cols = c("lightblue", "yellow", "red"),
  pt.size = 0.4
) +
  ggtitle("B. Reference Pseudotime\n(Naive → Temra + Treg branch)") +
  theme(plot.title = element_text(hjust = 0.5, size = 11, face = "bold"))

p_proj <- ggplot() +
  geom_point(data = ref_umap,
             aes(x = UMAP_1, y = UMAP_2),
             color = "grey88", size = 0.3, alpha = 0.6) +
  geom_point(data = query_umap %>% filter(is.finite(pseudotime)),
             aes(x = UMAP_1, y = UMAP_2, color = pseudotime),
             size = 1.0, alpha = 0.85) +
  scale_color_viridis_c(option = "plasma", name = "Pseudotime") +
  theme_classic(base_size = 10) +
  theme(plot.title = element_text(hjust = 0.5, size = 11, face = "bold")) +
  ggtitle("C. Sézary Cells Projected\n(Transferred pseudotime)")

top_states    <- state_summary %>% head(8) %>% pull(state_azimuth_l2)
state_plot_df <- state_per_line %>% filter(state_azimuth_l2 %in% top_states)

p_quant <- ggplot(state_plot_df,
                   aes(x = reorder(cell_line, -pct), y = pct,
                       fill = state_azimuth_l2)) +
  geom_col(position = "stack", color = "white", linewidth = 0.3) +
  scale_fill_manual(values = azimuth_l2_colors, na.value = "grey40",
                    name = "Azimuth l2") +
  theme_classic(base_size = 10) +
  theme(axis.text.x = element_text(angle = 40, hjust = 1, size = 9),
        plot.title  = element_text(hjust = 0.5, size = 11, face = "bold"),
        legend.key.size = unit(0.4, "cm"),
        legend.text = element_text(size = 8)) +
  labs(title = "D. State Proportions per Cell Line\n(Azimuth l2 label transfer)",
       x = "", y = "% Cells")

(p_ref | p_pt_ref) / (p_proj | p_quant) +
  plot_annotation(
    title = "Sézary Cell Differentiation State Mapping onto Healthy CD4+ T Cell Trajectory",
    theme = theme(plot.title = element_text(hjust = 0.5, size = 13,
                                            face = "bold"))
  )

9.2 Pseudotime Violin by Transferred State

mal_state_pt <- mapped_MalignantCD4T@meta.data %>%
  filter(is.finite(pseudotime_value), !is.na(state_azimuth_l2)) %>%
  mutate(state = state_azimuth_l2)

state_order_mal <- mal_state_pt %>%
  group_by(state) %>%
  summarise(med = median(pseudotime_value), .groups = "drop") %>%
  arrange(med) %>%
  pull(state)

mal_state_pt$state <- factor(mal_state_pt$state, levels = state_order_mal)

ggplot(mal_state_pt, aes(x = state, y = pseudotime_value, fill = state)) +
  geom_violin(scale = "width", alpha = 0.8, trim = TRUE) +
  geom_boxplot(width = 0.1, fill = "white", outlier.size = 0.3) +
  geom_hline(yintercept = c(naive_med, tcm_med, treg_med,
                             tem_med,  temra_med),
             linetype = "dashed", color = "grey50", linewidth = 0.5) +
  scale_fill_manual(values = azimuth_l2_colors, na.value = "grey70") +
  theme_classic(base_size = 11) +
  theme(axis.text.x   = element_text(angle = 40, hjust = 1, size = 9),
        legend.position = "none",
        plot.title    = element_text(hjust = 0.5, face = "bold")) +
  labs(title    = "Transferred Pseudotime per Azimuth l2 State (Sézary Cells)",
       subtitle = "Dashed lines = healthy reference state medians",
       x = "Transferred State (cell of origin)",
       y = "Transferred Pseudotime (differentiation position)")

9.3 Key Comparison: Label vs Pseudotime Bin

# This is the core result plot.
# For each transferred Azimuth l2 label, shows what fraction of cells
# fall into each pseudotime bin.
# Key message: TCM-labelled cells span TCM, TEM, and Temra bins
#   → identity = TCM (cell of origin)
#   → position = further along effector axis (partial skewing)

# FIX: build cross_df with explicit column selection from table output
# rather than rename() which can fail if column names differ
cross_df <- as.data.frame(cross_tab)
# table() always returns: Var1 → Label, Var2 → PT_Bin (from our named dims)
# but if table was constructed with named dims "Label" and "PT_Bin"
# then colnames are already Label, PT_Bin, Freq
colnames(cross_df) <- c("Label", "PT_Bin", "n")

cross_df <- cross_df %>%
  group_by(Label) %>%
  mutate(pct = round(100 * n / sum(n), 1)) %>%
  ungroup() %>%
  filter(n > 0) %>%
  mutate(PT_Bin = factor(PT_Bin,
                         levels = c("Naive-like", "TCM-like", "Treg-like",
                                    "TEM-like", "Temra-like")))

ggplot(cross_df, aes(x = PT_Bin, y = pct, fill = PT_Bin)) +
  geom_col(color = "white", linewidth = 0.3) +
  geom_text(aes(label = ifelse(pct >= 3, paste0(pct, "%"), "")),
            vjust = -0.3, size = 3, fontface = "bold") +
  facet_wrap(~ Label, scales = "free_y", ncol = 3) +
  scale_fill_manual(values = bin_colors, drop = FALSE,
                    name = "Pseudotime Bin") +
  theme_classic(base_size = 10) +
  theme(axis.text.x     = element_text(angle = 40, hjust = 1, size = 8),
        strip.text       = element_text(face = "bold", size = 9),
        strip.background = element_rect(fill = "#E8F4FD"),
        legend.position  = "none",
        plot.title       = element_text(hjust = 0.5, face = "bold")) +
  labs(
    title    = "Pseudotime Bin Distribution within Each Transferred Azimuth l2 Label",
    subtitle = paste0(
      "Core finding: TCM-labelled Sézary cells span multiple pseudotime bins\n",
      "→ TCM identity (label) but partial effector skewing (pseudotime position)\n",
      "Note: Treg panel shows 100% Treg-like by design (label-based bin assignment)"),
    x = "Pseudotime Bin", y = "% Cells within label"
  )

10 Save Outputs

Sys.setlocale("LC_COLLATE", "C")
options(expressions = 10000)

out_dir <- "results/MalignantCD4T_Monocle3_projection"
if (!dir.exists(out_dir)) dir.create(out_dir, recursive = TRUE)

library(SeuratObject)

SaveSeuratRds(mapped_MalignantCD4T,
              file = file.path(out_dir, 
                              "MalignantCD4T_mapped_monocle3_pseudotime_noProlif_ref.Rds"))



# Summary tables
write.csv(state_summary,
          file.path(out_dir, "state_distribution_overall.csv"),
          row.names = FALSE)
write.csv(state_per_line,
          file.path(out_dir, "state_distribution_per_cellline.csv"),
          row.names = FALSE)
write.csv(pt_bin_per_line,
          file.path(out_dir, "pseudotime_bins_per_cellline.csv"),
          row.names = FALSE)
write.csv(pt_stats,
          file.path(out_dir, "pseudotime_stats_per_cellline.csv"),
          row.names = FALSE)
write.csv(as.data.frame(cross_tab),
          file.path(out_dir, "label_vs_pseudotime_bin_crosstable.csv"),
          row.names = FALSE)
write.csv(mapped_MalignantCD4T@meta.data,
          file.path(out_dir, "MalignantCD4T_full_metadata_with_projection.csv"),
          row.names = TRUE)

# Bin boundaries — saved for methods section and reproducibility
bin_boundaries <- data.frame(
  boundary       = c("Naive_TCM", "TCM_TEM", "TEM_Temra", "TCM_Treg_branch"),
  pseudotime_cut = c(naive_tcm_cut, tcm_tem_cut, tem_temra_cut, tcm_treg_cut),
  naive_med      = naive_med,
  tcm_med        = tcm_med,
  treg_med       = treg_med,
  tem_med        = tem_med,
  temra_med      = temra_med
)
write.csv(bin_boundaries,
          file.path(out_dir, "pseudotime_bin_boundaries.csv"),
          row.names = FALSE)

cat("✅ All outputs saved to:", out_dir, "\n\n")
cat("Files:\n")
print(list.files(out_dir))

11 Session Info

sessionInfo()
LS0tCnRpdGxlOiAiU8OpemFyeSBDZWxsIFBzZXVkb3RpbWUgTWFwcGluZyBvbnRvIEhlYWx0aHkgQ0Q0KyBUIENlbGwgRGlmZmVyZW50aWF0aW9uIFRyYWplY3RvcnkiCnN1YnRpdGxlOiAiTW9ub2NsZTMgcmVmZXJlbmNlIHRyYWplY3Rvcnkgd2l0aCBzdGF0ZS1hbmNob3JlZCBwc2V1ZG90aW1lIGJpbnMiCmF1dGhvcjogIk5hc2lyIE1haG1vb2QgQWJiYXNpIgpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlCiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDoKICAgICAgY29sbGFwc2VkOiB0cnVlCiAgICB0aGVtZTogam91cm5hbAogICAgaGlnaGxpZ2h0OiB0YW5nbwogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IHRydWUKICAgIGRmX3ByaW50OiBwYWdlZAogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlCi0tLQoKPCEtLSAgIOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkCAtLT4KPCEtLSAgIFNDSUVOVElGSUMgUkFUSU9OQUxFICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC0tPgo8IS0tICAg4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQIC0tPgo8IS0tICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLS0+CjwhLS0gICBRVUVTVElPTjogV2hlcmUgYWxvbmcgdGhlIG5vcm1hbCBDRDQrIFQgY2VsbCBkaWZmZXJlbnRpYXRpb24gYXhpcyBkbyAgICAtLT4KPCEtLSAgIFPDqXphcnkgY2VsbHMgYXJyZXN0IG9yIG9yaWdpbmF0ZT8gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAtLT4KPCEtLSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC0tPgo8IS0tICAgQVBQUk9BQ0g6ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLS0+CjwhLS0gICAgIFN0ZXAgMSDigJQgQnVpbGQgTW9ub2NsZTMgdHJhamVjdG9yeSBvbiBIRUFMVEhZIHJlZmVyZW5jZSBDRDQrIFQgY2VsbHMgLS0+CjwhLS0gICAgICAgICAgICAgIFJvb3QgPSBDRDQgTmFpdmUgKENDUjcrU0VMTCtUQ0Y3KykgICAgICAgICAgICAgICAgICAgICAgICAgIC0tPgo8IS0tICAgICAgICAgICAgICBUd28gbGluZWFnZXM6ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLS0+CjwhLS0gICAgICAgICAgICAgICAgTGluZWFnZSAxOiBOYWl2ZSDihpIgVENNIOKGkiBURU0g4oaSIFRlbXJhL0NUTCAgKGVmZmVjdG9yIGF4aXMpIC0tPgo8IS0tICAgICAgICAgICAgICAgIExpbmVhZ2UgMjogTmFpdmUg4oaSIFRDTSDihpIgVHJlZyAgICAgICAgICAgICAgKHJlZ3VsYXRvcnkpICAgLS0+CjwhLS0gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAtLT4KPCEtLSAgICAgU3RlcCAyIOKAlCBGcmVlemUgcmVmZXJlbmNlIFVNQVAgKHJldHVybi5tb2RlbD1UUlVFKSAgICAgICAgICAgICAgICAgICAtLT4KPCEtLSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC0tPgo8IS0tICAgICBTdGVwIDMg4oCUIFByb2plY3QgbWFsaWduYW50IFPDqXphcnkgY2VsbHMgdmlhIE1hcFF1ZXJ5ICAgICAgICAgICAgICAgICAtLT4KPCEtLSAgICAgICAgICAgICAgQ2VsbHMgaW5oZXJpdCByZWZlcmVuY2UgVU1BUCBjb29yZGluYXRlcyArIHBzZXVkb3RpbWUgICAgICAgIC0tPgo8IS0tICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLS0+CjwhLS0gICAgIFN0ZXAgNCDigJQgSW50ZXJwcmV0IHR3byBjb21wbGVtZW50YXJ5IG91dHB1dHM6ICAgICAgICAgICAgICAgICAgICAgICAgLS0+CjwhLS0gICAgICAgICAgICAgIChhKSBMYWJlbCB0cmFuc2ZlciAg4oaSIENFTEwgT0YgT1JJR0lOICAgICAgICAgICAgICAgICAgICAgICAgLS0+CjwhLS0gICAgICAgICAgICAgICAgICBXaGljaCBub3JtYWwgc3RhdGUgZG9lcyB0aGlzIG1hbGlnbmFudCBjZWxsIHJlc2VtYmxlPyAgICAtLT4KPCEtLSAgICAgICAgICAgICAgKGIpIFBzZXVkb3RpbWUgYmluICDihpIgRElGRkVSRU5USUFUSU9OIFBPU0lUSU9OICAgICAgICAgICAgICAtLT4KPCEtLSAgICAgICAgICAgICAgICAgIFdoZXJlIGFsb25nIHRoZSBjb250aW51dW0gaXMgdGhpcyBjZWxsIHNpdHRpbmc/ICAgICAgICAgIC0tPgo8IS0tICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLS0+CjwhLS0gICBLRVkgQklPTE9HSUNBTCBNRVNTQUdFOiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAtLT4KPCEtLSAgIERpc2NvcmRhbmNlIGJldHdlZW4gKGEpIGFuZCAoYikgcmV2ZWFscyBwYXJ0aWFsIGVmZmVjdG9yIHNrZXdpbmcg4oCUICAgICAtLT4KPCEtLSAgIGNlbGxzIGxhYmVsbGVkIFRDTSBieSBpZGVudGl0eSBidXQgcG9zaXRpb25lZCBhdCBURU0vVGVtcmEgYnkgICAgICAgICAgLS0+CjwhLS0gICBwc2V1ZG90aW1lID0gYXJyZXN0IGF0IHRoZSBUQ03ihpJURU0gdHJhbnNpdGlvbiBjaGVja3BvaW50LiAgICAgICAgICAgICAgLS0+CjwhLS0gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAtLT4KPCEtLSAgIFBTRVVET1RJTUUgQklOIERFU0lHTjogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLS0+CjwhLS0gICBCaW5zIGFyZSBhbmNob3JlZCB0byByZWZlcmVuY2UgU1RBVEUgTUVESUFOIHBzZXVkb3RpbWUg4oCUIE5PVCBhcmJpdHJhcnkgIC0tPgo8IS0tICAgcXVhbnRpbGUgY3V0cy4gQm91bmRhcmllcyA9IG1pZHBvaW50IGJldHdlZW4gYWRqYWNlbnQgc3RhdGUgbWVkaWFucy4gICAtLT4KPCEtLSAgIFRyZWctbGlrZSBiaW4gaXMgYXNzaWduZWQgYnkgQXppbXV0aCBsYWJlbCAoYnJhbmNoLCBub3QgbGluZWFyIGF4aXMpLiAgLS0+CjwhLS0gICDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZAgLS0+CgoKIyBMb2FkIExpYnJhcmllcwoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldCgKICBlY2hvICAgICAgID0gVFJVRSwKICB3YXJuaW5nICAgID0gRkFMU0UsCiAgbWVzc2FnZSAgICA9IEZBTFNFLAogIGZpZy53aWR0aCAgPSAxMiwKICBmaWcuaGVpZ2h0ID0gOAopCgojIENvcmUgc2luZ2xlLWNlbGwKbGlicmFyeShTZXVyYXQpCmxpYnJhcnkobW9ub2NsZTMpCmxpYnJhcnkoU2V1cmF0V3JhcHBlcnMpCgojIERhdGEgd3JhbmdsaW5nCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkodGliYmxlKQoKIyBWaXN1YWxpc2F0aW9uCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShwYXRjaHdvcmspCmxpYnJhcnkoUkNvbG9yQnJld2VyKQpsaWJyYXJ5KHZpcmlkaXMpCmxpYnJhcnkoZ2dyaWRnZXMpCmxpYnJhcnkoZ2dyZXBlbCkKCiMgU3RhdGlzdGljcyAmIHRhYmxlcwpsaWJyYXJ5KE1hdHJpeCkKbGlicmFyeShzY2FsZXMpCmxpYnJhcnkocGhlYXRtYXApCgpvcHRpb25zKGZ1dHVyZS5nbG9iYWxzLm1heFNpemUgPSA4ZTkpCnNldC5zZWVkKDEyMzQpCgojIOKUgOKUgCBBemltdXRoIGwyIGNvbG91ciBwYWxldHRlIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgAojIEJpb2xvZ2ljYWxseSBvcmRlcmVkOiBibHVlIChuYWl2ZSkg4oaSIHllbGxvdyAobWVtb3J5KSDihpIgcmVkIChlZmZlY3RvcikKIyBwdXJwbGUgKFRyZWcpLiBBbGwga25vd24gQXppbXV0aCBsMiBDRDQgbGFiZWwgdmFyaWFudHMgaW5jbHVkZWQuCmF6aW11dGhfbDJfY29sb3JzIDwtIGMoCiAgIyBOYWl2ZQogICJDRDQgTmFpdmUiICAgICAgICAgPSAiIzIxNjZBQyIsCiAgIkNENCBUTiIgICAgICAgICAgICA9ICIjMjE2NkFDIiwKICAjIENlbnRyYWwgTWVtb3J5CiAgIkNENCBUQ00iICAgICAgICAgICA9ICIjNzRBREQxIiwKICAiQ0Q0IFRDTV8xIiAgICAgICAgID0gIiM5RUNBRTEiLAogICJDRDQgVENNXzIiICAgICAgICAgPSAiIzZCQUVENiIsCiAgIkNENCBUQ01fMyIgICAgICAgICA9ICIjNDI5MkM2IiwKICAjIEVmZmVjdG9yIE1lbW9yeQogICJDRDQgVEVNIiAgICAgICAgICAgPSAiI0ZFRTA5MCIsCiAgIkNENCBURU1fMSIgICAgICAgICA9ICIjRkREMEEyIiwKICAiQ0Q0IFRFTV8yIiAgICAgICAgID0gIiNGREFFNkIiLAogICJDRDQgVEVNXzMiICAgICAgICAgPSAiI0ZEOEQzQyIsCiAgIkNENCBURU1fNCIgICAgICAgICA9ICIjRDk0ODAxIiwKICAjIFRlcm1pbmFsIEVmZmVjdG9yIOKAlCBhbGwga25vd24gQXppbXV0aCBsYWJlbCB2YXJpYW50cwogICJDRDQgVGVtcmEvQ1RMIiAgICAgPSAiI0Q3MzAyNyIsCiAgIkNENCBURU1SQSIgICAgICAgICA9ICIjRDczMDI3IiwKICAiQ0Q0IFRFTVJBL0NUTCIgICAgID0gIiNENzMwMjciLAogICJDRDQgQ1RMIiAgICAgICAgICAgPSAiI0Q3MzAyNyIsCiAgIyBSZWd1bGF0b3J5CiAgIlRyZWciICAgICAgICAgICAgICA9ICIjNzYyQTgzIiwKICAiVHJlZyBOYWl2ZSIgICAgICAgID0gIiM5OTcwQUIiLAogICJUcmVnIE1lbW9yeSIgICAgICAgPSAiIzc2MkE4MyIsCiAgIyBPdGhlciBzdWJzZXRzIEF6aW11dGggbDIgbWF5IHJldHVybgogICJDRDQgUHJvbGlmZXJhdGluZyIgPSAiI0E2QTZBNiIsCiAgIlRoMSIgICAgICAgICAgICAgICA9ICIjRjQ2RDQzIiwKICAiVGgxNyIgICAgICAgICAgICAgID0gIiNGREFFNjEiLAogICJUaDEvVGgxNyIgICAgICAgICAgPSAiI0ZFRTA4QiIsCiAgIlRmaCIgICAgICAgICAgICAgICA9ICIjNjZDMkE1IgopCgojIOKUgOKUgCBQc2V1ZG90aW1lIGJpbiBjb2xvdXJzIOKAlCA1IGJpbnMsIGJvdGggbGluZWFnZXMg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACiMgQ29sb3VycyBkZWxpYmVyYXRlbHkgbWF0Y2ggdGhlaXIgY29ycmVzcG9uZGluZyBBemltdXRoIGwyIHN0YXRlIGFib3ZlCiMgc28gZmlndXJlcyBhcmUgdmlzdWFsbHkgY29uc2lzdGVudCB0aHJvdWdob3V0IHRoZSBkb2N1bWVudC4KYmluX2NvbG9ycyA8LSBjKAogICJOYWl2ZS1saWtlIiAgPSAiIzIxNjZBQyIsICAjIGRhcmsgYmx1ZSAgIOKAlCBDRDQgTmFpdmUKICAiVENNLWxpa2UiICAgID0gIiM3NEFERDEiLCAgIyBza3kgYmx1ZSAgICDigJQgQ0Q0IFRDTQogICJUcmVnLWxpa2UiICAgPSAiIzc2MkE4MyIsICAjIGRhcmsgcHVycGxlIOKAlCBUcmVnIGJyYW5jaAogICJURU0tbGlrZSIgICAgPSAiI0ZFRTA5MCIsICAjIHBhbGUgeWVsbG93IOKAlCBDRDQgVEVNCiAgIlRlbXJhLWxpa2UiICA9ICIjRDczMDI3IiAgICMgcmVkICAgICAgICAg4oCUIENENCBUZW1yYS9DVEwKKQoKbWFsaWduYW50X2NvbG9yIDwtICIjRTMxQTFDIiAgICMgdml2aWQgcmVkIGZvciBtYWxpZ25hbnQgb3ZlcmxheQpyZWZlcmVuY2VfY29sb3IgPC0gImdyZXk4NSIgICAgIyBiYWNrZ3JvdW5kIGZvciByZWZlcmVuY2UgY2VsbHMKCiMg4pSA4pSAIEhlbHBlcjogZXh0ZW5kIHBhbGV0dGUgZm9yIHVuZXhwZWN0ZWQgbGFiZWxzIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgApleHRlbmRfcGFsZXR0ZSA8LSBmdW5jdGlvbihwYWxldHRlLCBsYWJlbHMpIHsKICBtaXNzaW5nX2xhYmVscyA8LSBzZXRkaWZmKGxhYmVscywgbmFtZXMocGFsZXR0ZSkpCiAgaWYgKGxlbmd0aChtaXNzaW5nX2xhYmVscykgPiAwKSB7CiAgICBleHRyYV9jb2xzIDwtIGNvbG9yUmFtcFBhbGV0dGUoCiAgICAgIFJDb2xvckJyZXdlcjo6YnJld2VyLnBhbCg4LCAiRGFyazIiKSkobGVuZ3RoKG1pc3NpbmdfbGFiZWxzKSkKICAgIG5hbWVzKGV4dHJhX2NvbHMpIDwtIG1pc3NpbmdfbGFiZWxzCiAgICBwYWxldHRlIDwtIGMocGFsZXR0ZSwgZXh0cmFfY29scykKICB9CiAgcGFsZXR0ZQp9CmBgYAoKCiMgTG9hZCBPYmplY3RzCgojIyBMb2FkIFJlZmVyZW5jZSAoSGVhbHRoeSBDRDQrIFQgQ2VsbHMgd2l0aCBUcmFqZWN0b3J5KQoKYGBge3IgbG9hZC1yZWZlcmVuY2V9CiMg4pSA4pSAIENSSVRJQ0FMIHJlcXVpcmVtZW50cyBmb3IgdGhpcyBvYmplY3Qg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACiMgICDinIUgQ0Q0IFByb2xpZmVyYXRpbmcgY2x1c3RlciByZW1vdmVkIChNS0k2NysvVE9QMkErL0NESzErKQojICAg4pyFIFVNQVAgYnVpbHQgd2l0aCByZXR1cm4ubW9kZWwgPSBUUlVFIChyZXF1aXJlZCBmb3IgTWFwUXVlcnkpCiMgICDinIUgbW9ub2NsZTNfcHNldWRvdGltZSBjb2x1bW4gKHdpbGwgYmUgcmVjb21wdXRlZCBoZXJlIGZvciByZXByb2R1Y2liaWxpdHkpCiMgICDinIUgcHJlZGljdGVkLmNlbGx0eXBlLmwyIChBemltdXRoIGwyIGxhYmVscykKIyAgIOKchSBjZWxsX3R5cGUgYW5ub3RhdGlvbiAoY2x1c3RlcnMgMC02KQoKcmVmZXJlbmNlX2ludGVncmF0ZWQgPC0gcmVhZFJEUygKICAiLi4vMS1GaW5hbF9DdXN0b21fTVNUX01vbm9jbGUzX1RyYWplY3RvcnlfYW5kX21hcHBpbmcvMS1DdXN0b21fTVNUL09iamVjdHMvY2Q0X3JlZl9kdWFsX3RyYWplY3RvcnkucmRzIgopCgpjYXQoIj09PSBSZWZlcmVuY2Ugb2JqZWN0IHN1bW1hcnkgPT09XG4iKQpjYXQoIkNlbGxzICAgICA6IiwgbmNvbChyZWZlcmVuY2VfaW50ZWdyYXRlZCksICJcbiIpCmNhdCgiQXNzYXlzICAgIDoiLCBwYXN0ZShuYW1lcyhyZWZlcmVuY2VfaW50ZWdyYXRlZEBhc3NheXMpLCBjb2xsYXBzZSA9ICIsICIpLCAiXG4iKQpjYXQoIlJlZHVjdGlvbnM6IiwgcGFzdGUobmFtZXMocmVmZXJlbmNlX2ludGVncmF0ZWRAcmVkdWN0aW9ucyksIGNvbGxhcHNlID0gIiwgIiksICJcbiIpCmNhdCgiVU1BUCBtb2RlbDoiLCAhaXMubnVsbChyZWZlcmVuY2VfaW50ZWdyYXRlZEByZWR1Y3Rpb25zJHVtYXBAbWlzYyRtb2RlbCksICJcbiIpCmNhdCgiQ2x1c3RlcnMgIDoiLAogICAgcGFzdGUoc29ydCh1bmlxdWUocmVmZXJlbmNlX2ludGVncmF0ZWQkc2V1cmF0X2NsdXN0ZXJzKSksIGNvbGxhcHNlID0gIiwgIiksICJcbiIpCgojIOKUgOKUgCBDb25maXJtIENENCBQcm9saWZlcmF0aW5nIGNlbGxzIGFic2VudCDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKaWYgKGFueShncmVwbCgiUHJvbGlmfHByb2xpZnxjeWNsaW5nfEN5Y2xpbmciLAogICAgICAgICAgICAgIHJlZmVyZW5jZV9pbnRlZ3JhdGVkJGNlbGxfdHlwZSwgaWdub3JlLmNhc2UgPSBUUlVFKSkpIHsKICB3YXJuaW5nKCLimqDvuI8gIFByb2xpZmVyYXRpbmcgY2VsbF90eXBlIGxhYmVscyBkZXRlY3RlZCDigJQgcmVtb3ZlIGJlZm9yZSBwcm9jZWVkaW5nLiIpCn0gZWxzZSB7CiAgY2F0KCLinIUgTm8gcHJvbGlmZXJhdGluZyBjZWxsX3R5cGUgbGFiZWxzXG4iKQp9Cgpwcm9saWZfbWFya2VycyAgPC0gYygiTUtJNjciLCAiVE9QMkEiLCAiUENOQSIsICJDREsxIiwgIlNUTU4xIikKcHJvbGlmX3ByZXNlbnQgIDwtIGludGVyc2VjdChwcm9saWZfbWFya2Vycywgcm93bmFtZXMocmVmZXJlbmNlX2ludGVncmF0ZWQpKQppZiAobGVuZ3RoKHByb2xpZl9wcmVzZW50KSA+IDApIHsKICBEZWZhdWx0QXNzYXkocmVmZXJlbmNlX2ludGVncmF0ZWQpIDwtICJTQ1QiCiAgcHJvbGlmX2V4cHIgPC0gTWF0cml4Ojpjb2xNZWFucygKICAgIEdldEFzc2F5RGF0YShyZWZlcmVuY2VfaW50ZWdyYXRlZCwgbGF5ZXIgPSAiZGF0YSIpW3Byb2xpZl9wcmVzZW50LCAsIGRyb3AgPSBGQUxTRV0KICApCiAgY2F0KCJQcm9saWYgbWFya2VyIHNjb3JlIOKAlCBtYXg6Iiwgcm91bmQobWF4KHByb2xpZl9leHByKSwgMyksCiAgICAgICJ8IGNlbGxzID4gMC41OiIsIHN1bShwcm9saWZfZXhwciA+IDAuNSksICJcbiIpCiAgaWYgKHN1bShwcm9saWZfZXhwciA+IDAuNSkgPiA1MCkgewogICAgd2FybmluZygi4pqg77iPICBTdWJzdGFudGlhbCBwcm9saWZlcmF0aW9uIHNpZ25hbCDigJQgdmVyaWZ5IHJlbW92YWwgd2FzIGNvbXBsZXRlLiIpCiAgfSBlbHNlIHsKICAgIGNhdCgi4pyFIFByb2xpZmVyYXRpbmcgY2VsbHMgY29uZmlybWVkIGFic2VudFxuIikKICB9Cn0KCmNhdCgiXG5DZWxsIHR5cGUgZGlzdHJpYnV0aW9uIChyZWZlcmVuY2UpOlxuIikKcHJpbnQodGFibGUocmVmZXJlbmNlX2ludGVncmF0ZWQkY2VsbF90eXBlKSkKY2F0KCJcbkF6aW11dGggbDIgZGlzdHJpYnV0aW9uIChyZWZlcmVuY2UpOlxuIikKcHJpbnQodGFibGUocmVmZXJlbmNlX2ludGVncmF0ZWQkcHJlZGljdGVkLmNlbGx0eXBlLmwyKSkKCiMgRXh0ZW5kIHBhbGV0dGUgdG8gY292ZXIgYW55IGxhYmVscyBub3QgcHJlLXNlZWRlZCBhYm92ZQpyZWZfbDJfbGFiZWxzICAgICAgPC0gdW5pcXVlKGFzLmNoYXJhY3RlcihyZWZlcmVuY2VfaW50ZWdyYXRlZCRwcmVkaWN0ZWQuY2VsbHR5cGUubDIpKQpyZWZfbDJfbGFiZWxzICAgICAgPC0gcmVmX2wyX2xhYmVsc1shaXMubmEocmVmX2wyX2xhYmVscyldCmF6aW11dGhfbDJfY29sb3JzICA8LSBleHRlbmRfcGFsZXR0ZShhemltdXRoX2wyX2NvbG9ycywgcmVmX2wyX2xhYmVscykKY2F0KCJcbkNvbG91ciBwYWxldHRlIGNvdmVycyIsIGxlbmd0aChyZWZfbDJfbGFiZWxzKSwgInJlZmVyZW5jZSBsYWJlbHMg4pyFXG4iKQoKIyDilIDilIAgSGFyZCBzdG9wOiBVTUFQIG1vZGVsIG11c3QgZXhpc3QgZm9yIE1hcFF1ZXJ5IOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgAppZiAoaXMubnVsbChyZWZlcmVuY2VfaW50ZWdyYXRlZEByZWR1Y3Rpb25zJHVtYXBAbWlzYyRtb2RlbCkpIHsKICBzdG9wKAogICAgIuKdjCBVTUFQIG1vZGVsIG1pc3NpbmcuXG4iLAogICAgIiAgIFJlLXJ1bjogcmVmZXJlbmNlX2ludGVncmF0ZWQgPC0gUnVuVU1BUChyZWZlcmVuY2VfaW50ZWdyYXRlZCxcbiIsCiAgICAiICAgICAgICAgICAgIGRpbXMgPSAxOjIwLCByZXR1cm4ubW9kZWwgPSBUUlVFKVxuIiwKICAgICIgICBUaGVuIHJlLXNhdmUgYW5kIHJlbG9hZC4iCiAgKQp9IGVsc2UgewogIGNhdCgi4pyFIFVNQVAgbW9kZWwgcHJlc2VudCDigJQgTWFwUXVlcnkgcHJvamVjdGlvbiByZWFkeVxuIikKfQpgYGAKCiMjIExvYWQgJiBQcmVwYXJlIE1hbGlnbmFudCBDRDQrIFQgQ2VsbHMg4oCUIFBlci1DZWxsLUxpbmUgU0NUcmFuc2Zvcm0KCmBgYHtyIGxvYWQtbWFsaWduYW50fQpBbGxfc2FtcGxlc19NZXJnZWQgPC0gcmVhZFJEUygKICAiL2hvbWUvbmFiYmFzaS9hcG9sbG9faG9tZS8xLVNldXJhdF9SRFNfT0JKRUNUX0ZJTkFML0FsbF9zYW1wbGVzX01lcmdlZF93aXRoX1JlbmFtZWRfQ2x1c3RlcnNfQ2VsbF9zdGF0ZS0wMy0xMi0yMDI1LnJkcy5yZHMiCikKCmNhdCgiQWxsIGNlbGwgbGluZXM6XG4iKQpwcmludCh0YWJsZShBbGxfc2FtcGxlc19NZXJnZWQkY2VsbF9saW5lKSkKCgpBbGxfc2FtcGxlc19NZXJnZWQkR3JvdXAgPC0gaWZlbHNlKAogIEFsbF9zYW1wbGVzX01lcmdlZCRjZWxsX2xpbmUgJWluJSBwYXN0ZTAoIkwiLCAxOjcpLCAiTWFsaWduYW50Q0Q0VCIsCiAgaWZlbHNlKAogICAgQWxsX3NhbXBsZXNfTWVyZ2VkJGNlbGxfbGluZSAlaW4lCiAgICAgIGMoIkNENFRjZWxsc19sYWIiLCAiQ0Q0VGNlbGxzXzEweCIpLCAgCiAgICAiTm9ybWFsQ0Q0VCIsICJPdGhlciIKICApCikKCmNhdCgiXG5Hcm91cCBkaXN0cmlidXRpb246XG4iKQpwcmludCh0YWJsZShBbGxfc2FtcGxlc19NZXJnZWQkR3JvdXApKQoKTWFsaWduYW50Q0Q0VF9yYXcgPC0gc3Vic2V0KEFsbF9zYW1wbGVzX01lcmdlZCwgc3Vic2V0ID0gR3JvdXAgPT0gIk1hbGlnbmFudENENFQiKQpjYXQoIlxuTWFsaWduYW50IENENFQgY2VsbHM6IiwgbmNvbChNYWxpZ25hbnRDRDRUX3JhdyksICJcbiIpCnByaW50KHRhYmxlKE1hbGlnbmFudENENFRfcmF3JGNlbGxfbGluZSkpCgpybShBbGxfc2FtcGxlc19NZXJnZWQpOyBnYygpCmBgYAoKYGBge3IgcGVyLWxpbmUtc2N0fQojIOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkAojIFBFUi1DRUxMLUxJTkUgU0NUcmFuc2Zvcm0KIyDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZAKIyBXSFk6IEVhY2ggY2VsbCBsaW5lIGlzIGEgc2VwYXJhdGUgbGlicmFyeSB3aXRoIGRpZmZlcmVudCBzZXF1ZW5jaW5nIGRlcHRoCiMgYW5kIGFtYmllbnQgUk5BIHByb2ZpbGUuIEEgc2luZ2xlIG1lcmdlZCBTQ1QgbW9kZWwgY29uZmxhdGVzIGJhdGNoCiMgdmFyaWF0aW9uIHdpdGggYmlvbG9neS4gUGVyLWxpbmUgU0NUIGNvcnJlY3RzIGZvciB0aGlzIGJlZm9yZSBwcm9qZWN0aW9uLgojCiMgQVBQUk9BQ0g6CiMgICAxLiBTcGxpdCBieSBjZWxsX2xpbmUKIyAgIDIuIFNDVHJhbnNmb3JtIGVhY2ggbGluZSAocmVncmVzcyBwZXJjZW50Lm10ICsgY2VsbCBjeWNsZSBzY29yZXMpCiMgICAzLiBTZWxlY3QgSFZHcyBieSBjcm9zcy1saW5lIGZyZXF1ZW5jeSDigJQgbW9zdCByb2J1c3QgdG8gYmF0Y2ggZWZmZWN0cwojICAgNC4gTWVyZ2UsIHNjYWxlLCBQQ0Eg4oCUIHJlYWR5IGZvciBGaW5kVHJhbnNmZXJBbmNob3JzCiMg4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQCgpjYXQoIj09PSBQZXItY2VsbC1saW5lIFNDVHJhbnNmb3JtID09PVxuIikKCiMgU2F2ZSBuYW1lcyBCRUZPUkUgbGFwcGx5IHRvIGF2b2lkIGRvdWJsZSBTcGxpdE9iamVjdCBjYWxsCiMgRklYOiBvcmlnaW5hbCBzY3JpcHQgY2FsbGVkIFNwbGl0T2JqZWN0IHR3aWNlIOKAlCB3YXN0ZWZ1bCBhbmQgcmlza3kKY2VsbF9saW5lX25hbWVzIDwtIG5hbWVzKFNwbGl0T2JqZWN0KE1hbGlnbmFudENENFRfcmF3LCBzcGxpdC5ieSA9ICJjZWxsX2xpbmUiKSkKY2VsbF9saW5lX2xpc3QgIDwtIFNwbGl0T2JqZWN0KE1hbGlnbmFudENENFRfcmF3LCBzcGxpdC5ieSA9ICJjZWxsX2xpbmUiKQoKY2F0KCJMaW5lcyB0byBwcm9jZXNzOiIsIHBhc3RlKGNlbGxfbGluZV9uYW1lcywgY29sbGFwc2UgPSAiLCAiKSwgIlxuXG4iKQoKY2VsbF9saW5lX2xpc3QgPC0gbGFwcGx5KGNlbGxfbGluZV9uYW1lcywgZnVuY3Rpb24obGluZV9uYW1lKSB7CiAgb2JqIDwtIGNlbGxfbGluZV9saXN0W1tsaW5lX25hbWVdXQogIGNhdCgiUHJvY2Vzc2luZzoiLCBsaW5lX25hbWUsICJ8IENlbGxzOiIsIG5jb2wob2JqKSwgIlxuIikKCiAgaWYgKCEicGVyY2VudC5tdCIgJWluJSBjb2xuYW1lcyhvYmpAbWV0YS5kYXRhKSkKICAgIG9ialtbInBlcmNlbnQubXQiXV0gPC0gUGVyY2VudGFnZUZlYXR1cmVTZXQob2JqLCBwYXR0ZXJuID0gIl5NVC0iKQoKICAjIENlbGwgY3ljbGUgcmVncmVzc2lvbiByZW1vdmVzIGN5Y2xpbmcgYXJ0ZWZhY3RzIHdpdGhvdXQgcmVtb3ZpbmcgY2VsbHMKICB2YXJzX3JlZ3Jlc3MgPC0gdHJ5Q2F0Y2goewogICAgb2JqIDwtIENlbGxDeWNsZVNjb3Jpbmcob2JqLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgcy5mZWF0dXJlcyAgID0gY2MuZ2VuZXMkcy5nZW5lcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGcybS5mZWF0dXJlcyA9IGNjLmdlbmVzJGcybS5nZW5lcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNldC5pZGVudCAgICA9IEZBTFNFKQogICAgY2F0KCIgIOKGkiBDZWxsIGN5Y2xlIHNjb3JlcyBjb21wdXRlZFxuIikKICAgIGMoInBlcmNlbnQubXQiLCAiUy5TY29yZSIsICJHMk0uU2NvcmUiKQogIH0sIGVycm9yID0gZnVuY3Rpb24oZSkgewogICAgY2F0KCIgIOKaoO+4jyAgQ2VsbCBjeWNsZSBzY29yaW5nIGZhaWxlZCDigJQgcmVncmVzc2luZyBwZXJjZW50Lm10IG9ubHlcbiIpCiAgICAicGVyY2VudC5tdCIKICB9KQoKICBvYmogPC0gU0NUcmFuc2Zvcm0ob2JqLAogICAgICAgICAgICAgICAgICAgICB2YXJzLnRvLnJlZ3Jlc3MgICAgID0gdmFyc19yZWdyZXNzLAogICAgICAgICAgICAgICAgICAgICB2YXJpYWJsZS5mZWF0dXJlcy5uID0gMzAwMCwKICAgICAgICAgICAgICAgICAgICAgdnN0LmZsYXZvciAgICAgICAgICA9ICJ2MiIsCiAgICAgICAgICAgICAgICAgICAgIHZlcmJvc2UgICAgICAgICAgICAgPSBGQUxTRSkKICBjYXQoIiAg4pyFIiwgbGluZV9uYW1lLCAiY29tcGxldGUgfCIsIGxlbmd0aChWYXJpYWJsZUZlYXR1cmVzKG9iaikpLCAiSFZHc1xuIikKICByZXR1cm4ob2JqKQp9KQpuYW1lcyhjZWxsX2xpbmVfbGlzdCkgPC0gY2VsbF9saW5lX25hbWVzICAjIHJldXNlIHNhdmVkIG5hbWVzIOKAlCBubyBzZWNvbmQgU3BsaXRPYmplY3QKCiMg4pSA4pSAIFNlbGVjdCBIVkdzIGJ5IGNyb3NzLWxpbmUgZnJlcXVlbmN5IOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgAphbGxfaHZnX2xpc3RzIDwtIGxhcHBseShjZWxsX2xpbmVfbGlzdCwgVmFyaWFibGVGZWF0dXJlcykKaHZnX2ZyZXEgICAgICA8LSBzb3J0KHRhYmxlKHVubGlzdChhbGxfaHZnX2xpc3RzKSksIGRlY3JlYXNpbmcgPSBUUlVFKQpuX2h2Z3MgICAgICAgIDwtIG1pbigzMDAwLCBsZW5ndGgoaHZnX2ZyZXEpKQpzaGFyZWRfaHZncyAgIDwtIG5hbWVzKGh2Z19mcmVxKVsxOm5faHZnc10KCmNhdCgiXG5IVkcgc2VsZWN0aW9uOlxuIikKY2F0KCIgIFZhcmlhYmxlIGluIGFsbCIsIGxlbmd0aChjZWxsX2xpbmVfbGlzdCksICJsaW5lczoiLAogICAgc3VtKGh2Z19mcmVxID09IGxlbmd0aChjZWxsX2xpbmVfbGlzdCkpLCAiXG4iKQpjYXQoIiAgVmFyaWFibGUgaW4g4omlMyBsaW5lczoiLCBzdW0oaHZnX2ZyZXEgPj0gMyksICJcbiIpCmNhdCgiICBGaW5hbCBIVkcgc2V0OiIsIG5faHZncywgIlxuIikKCiMg4pSA4pSAIE1lcmdlLCBzY2FsZSwgUENBIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgApNYWxpZ25hbnRDRDRUIDwtIG1lcmdlKGNlbGxfbGluZV9saXN0W1sxXV0sIHkgPSBjZWxsX2xpbmVfbGlzdFstMV0sCiAgICAgICAgICAgICAgICAgICAgICAgbWVyZ2UuZGF0YSA9IFRSVUUpClZhcmlhYmxlRmVhdHVyZXMoTWFsaWduYW50Q0Q0VCkgPC0gc2hhcmVkX2h2Z3MKCmNhdCgiXG5SdW5uaW5nIFBDQSBvbiBtZXJnZWQgb2JqZWN0Li4uXG4iKQpNYWxpZ25hbnRDRDRUIDwtIFNjYWxlRGF0YShNYWxpZ25hbnRDRDRULCBmZWF0dXJlcyA9IHNoYXJlZF9odmdzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgYXNzYXkgPSAiU0NUIiwgdmVyYm9zZSA9IEZBTFNFKQpNYWxpZ25hbnRDRDRUIDwtIFJ1blBDQShNYWxpZ25hbnRDRDRULCBmZWF0dXJlcyA9IHNoYXJlZF9odmdzLAogICAgICAgICAgICAgICAgICAgICAgICAgYXNzYXkgPSAiU0NUIiwgbnBjcyA9IDMwLCB2ZXJib3NlID0gRkFMU0UpCgpjYXQoIuKchSBNZXJnZWQgb2JqZWN0IHJlYWR5XG4iKQpjYXQoIiAgQ2VsbHM6IiwgbmNvbChNYWxpZ25hbnRDRDRUKSwgIlxuIikKY2F0KCIgIEhWR3M6IiwgbGVuZ3RoKFZhcmlhYmxlRmVhdHVyZXMoTWFsaWduYW50Q0Q0VCkpLCAiXG4iKQpjYXQoIiAgUENBIGRpbXM6IiwgbmNvbChFbWJlZGRpbmdzKE1hbGlnbmFudENENFQsICJwY2EiKSksICJcblxuIikKcHJpbnQodGFibGUoTWFsaWduYW50Q0Q0VCRjZWxsX2xpbmUpKQoKcm0oTWFsaWduYW50Q0Q0VF9yYXcsIGNlbGxfbGluZV9saXN0KTsgZ2MoKQpgYGAKCiMjIHZhcmlhYmxlIGdlbmVzIGFsbCA3IGNlbGwgbGluZSBjaGVjawpgYGB7cn0KIyBHZW5lcyB2YXJpYWJsZSBpbiBBTEwgNyBsaW5lcwpodmdfYWxsNyA8LSBuYW1lcyhodmdfZnJlcVtodmdfZnJlcSA9PSA3XSkKY2F0KCJHZW5lcyB2YXJpYWJsZSBpbiBhbGwgNyBsaW5lczoiLCBsZW5ndGgoaHZnX2FsbDcpLCAiXG4iKQpwcmludChodmdfYWxsNykKCgoKIyBDaGVjayB3aGljaCBjYW5vbmljYWwgVCBjZWxsIG1hcmtlcnMgYXJlIGluIHRoZSBhbGwtNyBzZXQKdF9jZWxsX21hcmtlcnMgPC0gYygKICAjIE5haXZlL21lbW9yeQogICJDQ1I3IiwgIlNFTEwiLCAiVENGNyIsICJJTDdSIiwgIkxFRjEiLCAiS0xGMiIsCiAgIyBBY3RpdmF0aW9uL2V4aGF1c3Rpb24gIAogICJUT1giLCAiUERDRDEiLCAiTEFHMyIsICJUSUdJVCIsICJDVExBNCIsICJIQVZDUjIiLAogICMgRWZmZWN0b3IKICAiR1pNQiIsICJHWk1LIiwgIkdaTUEiLCAiUFJGMSIsICJJRk5HIiwgIlRORiIsCiAgIyBUcmVnCiAgIkZPWFAzIiwgIklMMlJBIiwgIklLWkYyIiwgIkNUTEE0IiwKICAjIFPDqXphcnkgc3BlY2lmaWMKICAiS0lSM0RMMiIsICJQTFMzIiwgIlRXSVNUMSIsICJFUEhBNCIsICJDRDE2NCIsCiAgIyBQcm9saWZlcmF0aW9uCiAgIk1LSTY3IiwgIlRPUDJBIiwgIkNESzEiCikKCmZvdW5kX2luX2FsbDcgPC0gaW50ZXJzZWN0KHRfY2VsbF9tYXJrZXJzLCBodmdfYWxsNykKY2F0KCJcbkNhbm9uaWNhbCBtYXJrZXJzIGluIGFsbC03IEhWRyBzZXQ6XG4iKQpwcmludChmb3VuZF9pbl9hbGw3KQoKbm90X2ZvdW5kIDwtIHNldGRpZmYodF9jZWxsX21hcmtlcnMsIGh2Z19hbGw3KQpjYXQoIlxuTWFya2VycyBOT1QgaW4gYWxsLTcgc2V0OlxuIikKcHJpbnQobm90X2ZvdW5kKQoKCgpgYGAKCiMjIHZhcmlhYmxlIGdlbmVzIGFsbCA3IGNlbGwgbGluZSBjaGVjawpgYGB7cn0KIyBDaGVjayB3aGF0IGp1bmsgZ2VuZXMgYXJlIGluIHlvdXIgY3VycmVudCBIVkcgc2V0CiMgYmVmb3JlIGZpbmQtYW5jaG9ycyBydW5zCgpjYXQoIj09PSBKdW5rIGdlbmUgY2hlY2sgb24gc2hhcmVkX2h2Z3MgPT09XG4iKQoKbXRfaW5faHZncyA8LSBzaGFyZWRfaHZnc1tncmVwbCgiXk1ULSIsIHNoYXJlZF9odmdzKV0KY2F0KCJNVCBnZW5lcyBpbiBzaGFyZWRfaHZnczoiLCBsZW5ndGgobXRfaW5faHZncyksICJcbiIpCnByaW50KG10X2luX2h2Z3MpCgpyaWJvX2luX2h2Z3MgPC0gc2hhcmVkX2h2Z3NbZ3JlcGwoIl5SUEx8XlJQUyIsIHNoYXJlZF9odmdzKV0KY2F0KCJcblJpYm9zb21hbCBnZW5lcyBpbiBzaGFyZWRfaHZnczoiLCBsZW5ndGgocmlib19pbl9odmdzKSwgIlxuIikKcHJpbnQocmlib19pbl9odmdzKQoKaHNwX2luX2h2Z3MgPC0gc2hhcmVkX2h2Z3NbZ3JlcGwoIl5IU1B8XkhTUEF8XkhTUEIiLCBzaGFyZWRfaHZncyldCmNhdCgiXG5IZWF0IHNob2NrIGdlbmVzIGluIHNoYXJlZF9odmdzOiIsIGxlbmd0aChoc3BfaW5faHZncyksICJcbiIpCnByaW50KGhzcF9pbl9odmdzKQoKc25oZ19pbl9odmdzIDwtIHNoYXJlZF9odmdzW2dyZXBsKCJeU05IR3xNQUxBVDF8TkVBVDEiLCBzaGFyZWRfaHZncyldCmNhdCgiXG5sbmNSTkEgZ2VuZXMgaW4gc2hhcmVkX2h2Z3M6IiwgbGVuZ3RoKHNuaGdfaW5faHZncyksICJcbiIpCnByaW50KHNuaGdfaW5faHZncykKCmNhdCgiXG5Ub3RhbCBqdW5rIGdlbmVzIHRvIGJlIGZpbHRlcmVkOiIsIAogICAgbGVuZ3RoKG10X2luX2h2Z3MpICsgbGVuZ3RoKHJpYm9faW5faHZncykgKyAKICAgIGxlbmd0aChoc3BfaW5faHZncykgKyBsZW5ndGgoc25oZ19pbl9odmdzKSwgIlxuIikKYGBgCgoKIyBSZWZlcmVuY2UgVHJhamVjdG9yeSAoTW9ub2NsZTMpCgojIyBCdWlsZCBDRFMgZnJvbSBSZWZlcmVuY2UKCmBgYHtyIGJ1aWxkLWNkc30KCmNhdCgiPT09IEJ1aWxkaW5nIE1vbm9jbGUzIENEUyA9PT1cbiIpCgpjZHMgPC0gYXMuY2VsbF9kYXRhX3NldChyZWZlcmVuY2VfaW50ZWdyYXRlZCkKCiMgVHJhbnNmZXIgZnJvemVuIFVNQVAgY29vcmRpbmF0ZXMg4oCUIHRoZXNlIGRlZmluZSB0aGUgdHJhamVjdG9yeSBzcGFjZQpyZWR1Y2VkRGltKGNkcywgIlVNQVAiKSA8LSBFbWJlZGRpbmdzKHJlZmVyZW5jZV9pbnRlZ3JhdGVkLCAidW1hcCIpCgojIFNpbmdsZSBwYXJ0aXRpb246IG9uZSBjb25uZWN0ZWQgZ3JhcGggYWNyb3NzIGFsbCBoZWFsdGh5IGNlbGxzLgojIENENCBQcm9saWZlcmF0aW5nIGNlbGxzIGhhdmUgYmVlbiByZW1vdmVkIHNvIG5vIHNwdXJpb3VzIGNlbGwtY3ljbGUKIyBicmFuY2ggd2lsbCBwdWxsIHRoZSBncmFwaCBhd2F5IGZyb20gdGhlIGRpZmZlcmVudGlhdGlvbiBheGlzLgpwYXJ0aXRpb25fdmVjIDwtIHNldE5hbWVzKGZhY3RvcihyZXAoMUwsIG5jb2woY2RzKSkpLCBjb2xuYW1lcyhjZHMpKQpjZHNAY2x1c3RlcnMkVU1BUCRwYXJ0aXRpb25zIDwtIHBhcnRpdGlvbl92ZWMKCmNsdXN0ZXJfdmVjIDwtIHNldE5hbWVzKAogIGZhY3RvcihyZWZlcmVuY2VfaW50ZWdyYXRlZCRzZXVyYXRfY2x1c3RlcnNbY29sbmFtZXMoY2RzKV0pLAogIGNvbG5hbWVzKGNkcykKKQpjZHNAY2x1c3RlcnMkVU1BUCRjbHVzdGVycyA8LSBjbHVzdGVyX3ZlYwoKIyBUcmFuc2ZlciBtZXRhZGF0YQpjb2xEYXRhKGNkcykkY2VsbF9saW5lICAgICAgICAgICAgIDwtIHJlZmVyZW5jZV9pbnRlZ3JhdGVkJG9yaWcuaWRlbnQKY29sRGF0YShjZHMpJGNlbGxfdHlwZSAgICAgICAgICAgICA8LSByZWZlcmVuY2VfaW50ZWdyYXRlZCRjZWxsX3R5cGUKY29sRGF0YShjZHMpJHByZWRpY3RlZC5jZWxsdHlwZS5sMiA8LSByZWZlcmVuY2VfaW50ZWdyYXRlZCRwcmVkaWN0ZWQuY2VsbHR5cGUubDIKY29sRGF0YShjZHMpJHNldXJhdF9jbHVzdGVycyAgICAgICA8LSByZWZlcmVuY2VfaW50ZWdyYXRlZCRpbnRlZ3JhdGVkX3Nubl9yZXMuMC4yCmlmICgib3JpZy5pZGVudCIgJWluJSBjb2xuYW1lcyhyZWZlcmVuY2VfaW50ZWdyYXRlZEBtZXRhLmRhdGEpKQogIGNvbERhdGEoY2RzKSRzYW1wbGUgPC0gcmVmZXJlbmNlX2ludGVncmF0ZWQkb3JpZy5pZGVudAoKIyBWYWxpZGF0ZSBhbGwgcmVxdWlyZWQgc2xvdHMgYXJlIG5hbWVkIGNvcnJlY3RseQpzdG9waWZub3QoCiAgImNsdXN0ZXJzIG11c3QgYmUgbmFtZWQiICAgPSAhaXMubnVsbChuYW1lcyhjZHNAY2x1c3RlcnMkVU1BUCRjbHVzdGVycykpLAogICJwYXJ0aXRpb25zIG11c3QgYmUgbmFtZWQiID0gIWlzLm51bGwobmFtZXMoY2RzQGNsdXN0ZXJzJFVNQVAkcGFydGl0aW9ucykpLAogICJjbHVzdGVyIGxlbmd0aCBtYXRjaGVzIiAgID0gbGVuZ3RoKGNkc0BjbHVzdGVycyRVTUFQJGNsdXN0ZXJzKSAgID09IG5jb2woY2RzKSwKICAicGFydGl0aW9uIGxlbmd0aCBtYXRjaGVzIiA9IGxlbmd0aChjZHNAY2x1c3RlcnMkVU1BUCRwYXJ0aXRpb25zKSA9PSBuY29sKGNkcykKKQoKY2F0KCJDRFMgYnVpbHQ6IiwgbmNvbChjZHMpLCAiY2VsbHNcbiIpCmNhdCgiQ2x1c3RlcnM6IiwgbmxldmVscyhmYWN0b3IoY29sRGF0YShjZHMpJHNldXJhdF9jbHVzdGVycykpLCAiXG4iKQpjYXQoIlBhcnRpdGlvbnM6IiwgbmxldmVscyhwYXJ0aXRpb25zKGNkcykpLCAiXG5cbiIpCmNhdCgiQXppbXV0aCBsMiBicmVha2Rvd246XG4iKQpwcmludCh0YWJsZShjb2xEYXRhKGNkcykkcHJlZGljdGVkLmNlbGx0eXBlLmwyKSkKYGBgCgojIyBMZWFybiBUcmFqZWN0b3J5IEdyYXBoIOKAlCBUd28gTGluZWFnZXMKCmBgYHtyIGxlYXJuLWdyYXBoLCBmaWcud2lkdGg9MTQsIGZpZy5oZWlnaHQ9OH0KIyDilIDilIAgTGVhcm4gcHJpbmNpcGFsIGdyYXBoIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgAojIEdvYWw6IHJlY292ZXIgdHdvIGJpb2xvZ2ljYWwgbGluZWFnZXMgYXMgYSBzaW5nbGUgY29ubmVjdGVkIGdyYXBoOgojICAgTGluZWFnZSAxOiBOYWl2ZSDihpIgVENNIOKGkiBURU0g4oaSIFRlbXJhL0NUTCAgKGVmZmVjdG9yIGF4aXMpCiMgICBMaW5lYWdlIDI6IE5haXZlIOKGkiBUQ00g4oaSIFRyZWcgICAgICAgICAgICAgIChyZWd1bGF0b3J5IGJyYW5jaCkKIwojIHVzZV9wYXJ0aXRpb24gPSBGQUxTRTogc2luZ2xlIGNvbm5lY3RlZCBncmFwaCAobm8gcGFydGl0aW9uIGZvcmNpbmcpCiMgY2xvc2VfbG9vcCAgICA9IEZBTFNFOiBvcGVuIHRyYWplY3RvcnkgKG5vIGxvb3AgYmFjaykKIyBtaW5pbWFsX2JyYW5jaF9sZW4gPSA1OiBzaG9ydCBlbm91Z2ggdG8gZGV0ZWN0IHRoZSBUcmVnIGJyYW5jaAojICAg4oCUIGlmIFRyZWcgYnJhbmNoIG5vdCBkZXRlY3RlZCwgcmVkdWNlIHRvIDMKIyAgIOKAlCBpZiB0b28gbWFueSBzcHVyaW91cyBicmFuY2hlcywgaW5jcmVhc2UgdG8gOAojCiMgTk9URTogbm4uayBpcyBOT1QgYSB2YWxpZCBsZWFybl9ncmFwaF9jb250cm9sIHBhcmFtZXRlciBpbiBtb25vY2xlMy4KIyBGSVg6IHJlbW92ZWQgbm4uayDigJQgbW9ub2NsZTMgaGFuZGxlcyBuZWlnaGJvdXJob29kIHN0cnVjdHVyZSBpbnRlcm5hbGx5LgojIFRoZSBrZXkgc2Vuc2l0aXZpdHkgcGFyYW1ldGVyIGlzIG1pbmltYWxfYnJhbmNoX2xlbi4KCnNldC5zZWVkKDQyKQpjZHMgPC0gbGVhcm5fZ3JhcGgoCiAgY2RzLAogIHVzZV9wYXJ0aXRpb24gICAgICAgPSBGQUxTRSwKICBjbG9zZV9sb29wICAgICAgICAgID0gRkFMU0UsCiAgbGVhcm5fZ3JhcGhfY29udHJvbCA9IGxpc3QoCiAgICBtaW5pbWFsX2JyYW5jaF9sZW4gID0gMTAsCiAgICMgbmNlbnRlciAgICAgICAgICAgICA9IDEwMCwgICAjIGRlZmF1bHQgaXMgfjI1MCDigJQgcmVkdWNlIHRvIHNpbXBsaWZ5IGdyYXBoCiAgICBvcnRob2dvbmFsX3Byb2pfdGlwID0gRkFMU0UKICApLAogIHZlcmJvc2UgPSBUUlVFCikKCmNhdCgiXG7inIUgUHJpbmNpcGFsIGdyYXBoIGxlYXJuZWRcbiIpCmNhdCgiTm9kZXM6IiwgbGVuZ3RoKGlncmFwaDo6VihwcmluY2lwYWxfZ3JhcGgoY2RzKSRVTUFQKSksICJcbiIpCgpuX2JyYW5jaF9wb2ludHMgPC0gc3VtKGlncmFwaDo6ZGVncmVlKHByaW5jaXBhbF9ncmFwaChjZHMpJFVNQVApID4gMikKY2F0KCJCcmFuY2ggcG9pbnRzOiIsIG5fYnJhbmNoX3BvaW50cywgIlxuIikKCmlmIChuX2JyYW5jaF9wb2ludHMgPT0gMCkgewogIHdhcm5pbmcoCiAgICAi4p2MIE5vIGJyYW5jaCBwb2ludCDigJQgVHJlZyBsaW5lYWdlIG5vdCBzZXBhcmF0ZWQuXG4iLAogICAgIiAgIFJlLXJ1biBsZWFybl9ncmFwaCB3aXRoIG1pbmltYWxfYnJhbmNoX2xlbiA9IDMiCiAgKQp9IGVsc2UgaWYgKG5fYnJhbmNoX3BvaW50cyA+IDMpIHsKICB3YXJuaW5nKAogICAgIuKaoO+4jyAgVG9vIG1hbnkgYnJhbmNoIHBvaW50cyAoIiwgbl9icmFuY2hfcG9pbnRzLCAiKS5cbiIsCiAgICAiICAgUmUtcnVuIGxlYXJuX2dyYXBoIHdpdGggbWluaW1hbF9icmFuY2hfbGVuID0gOCIKICApCn0gZWxzZSB7CiAgY2F0KCLinIUgQnJhbmNoIHN0cnVjdHVyZSBsb29rcyBjb3JyZWN0ICgxLTMgYnJhbmNoIHBvaW50cylcbiIpCn0KCiMg4pSA4pSAIFRyYWplY3RvcnkgdmlzdWFsaXNhdGlvbiDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKdW1hcF9jb29yZHMgPC0gYXMuZGF0YS5mcmFtZShFbWJlZGRpbmdzKHJlZmVyZW5jZV9pbnRlZ3JhdGVkLCAidW1hcCIpKQpjb2xuYW1lcyh1bWFwX2Nvb3JkcykgPC0gYygiVU1BUDEiLCAiVU1BUDIiKQp1bWFwX2Nvb3JkcyRsMiAgICAgICAgPC0gcmVmZXJlbmNlX2ludGVncmF0ZWQkcHJlZGljdGVkLmNlbGx0eXBlLmwyCnVtYXBfY29vcmRzJGNlbGxfdHlwZSA8LSByZWZlcmVuY2VfaW50ZWdyYXRlZCRjZWxsX3R5cGUKCmxhYmVsX2wyIDwtIHVtYXBfY29vcmRzICU+JQogIGdyb3VwX2J5KGwyKSAlPiUKICBzdW1tYXJpc2UoVU1BUDEgPSBtZWRpYW4oVU1BUDEpLCBVTUFQMiA9IG1lZGlhbihVTUFQMiksIC5ncm91cHMgPSAiZHJvcCIpCgpsYWJlbF9jdCA8LSB1bWFwX2Nvb3JkcyAlPiUKICBncm91cF9ieShjZWxsX3R5cGUpICU+JQogIHN1bW1hcmlzZShVTUFQMSA9IG1lZGlhbihVTUFQMSksIFVNQVAyID0gbWVkaWFuKFVNQVAyKSwgLmdyb3VwcyA9ICJkcm9wIikKCnBfZ3JhcGhfbDIgPC0gcGxvdF9jZWxscygKICBjZHMsCiAgY29sb3JfY2VsbHNfYnkgICAgICAgID0gInByZWRpY3RlZC5jZWxsdHlwZS5sMiIsCiAgbGFiZWxfZ3JvdXBzX2J5X2NsdXN0ZXIgPSBGQUxTRSwKICBsYWJlbF9sZWF2ZXMgICAgICAgICAgPSBGQUxTRSwKICBsYWJlbF9icmFuY2hfcG9pbnRzICAgPSBUUlVFLAogIGNlbGxfc2l6ZSAgICAgICAgICAgICA9IDAuNywKICBzaG93X3RyYWplY3RvcnlfZ3JhcGggPSBUUlVFCikgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBhemltdXRoX2wyX2NvbG9ycywgbmFtZSA9ICJTdGF0ZSIpICsKICBnZW9tX2xhYmVsX3JlcGVsKGRhdGEgPSBsYWJlbF9sMiwKICAgICAgICAgICAgICAgICAgIGFlcyh4ID0gVU1BUDEsIHkgPSBVTUFQMiwgbGFiZWwgPSBsMiksCiAgICAgICAgICAgICAgICAgICBzaXplID0gNCwgZm9udGZhY2UgPSAiYm9sZCIsIGZpbGwgPSAid2hpdGUiLCBhbHBoYSA9IDAuODUsCiAgICAgICAgICAgICAgICAgICBib3gucGFkZGluZyA9IDAuNSwgc2VnbWVudC5jb2xvciA9ICJncmV5NDAiLAogICAgICAgICAgICAgICAgICAgaW5oZXJpdC5hZXMgPSBGQUxTRSkgKwogIGdndGl0bGUoIk1vbm9jbGUzIEdyYXBoOiBBemltdXRoIGwyXG4oVHdvIGxpbmVhZ2VzOiBlZmZlY3RvciBheGlzICsgVHJlZyBicmFuY2gpIikgKwogIHRoZW1lKHBsb3QudGl0bGUgICAgID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBzaXplID0gMTMsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCnBfZ3JhcGhfY3QgPC0gcGxvdF9jZWxscygKICBjZHMsCiAgY29sb3JfY2VsbHNfYnkgICAgICAgID0gImNlbGxfdHlwZSIsCiAgbGFiZWxfZ3JvdXBzX2J5X2NsdXN0ZXIgPSBGQUxTRSwKICBsYWJlbF9sZWF2ZXMgICAgICAgICAgPSBGQUxTRSwKICBsYWJlbF9icmFuY2hfcG9pbnRzICAgPSBUUlVFLAogIGNlbGxfc2l6ZSAgICAgICAgICAgICA9IDAuNywKICBzaG93X3RyYWplY3RvcnlfZ3JhcGggPSBUUlVFCikgKwogIGdlb21fbGFiZWxfcmVwZWwoZGF0YSA9IGxhYmVsX2N0LAogICAgICAgICAgICAgICAgICAgYWVzKHggPSBVTUFQMSwgeSA9IFVNQVAyLCBsYWJlbCA9IGNlbGxfdHlwZSksCiAgICAgICAgICAgICAgICAgICBzaXplID0gMy41LCBmb250ZmFjZSA9ICJib2xkIiwgZmlsbCA9ICJ3aGl0ZSIsIGFscGhhID0gMC44NSwKICAgICAgICAgICAgICAgICAgIGJveC5wYWRkaW5nID0gMC41LCBzZWdtZW50LmNvbG9yID0gImdyZXk0MCIsCiAgICAgICAgICAgICAgICAgICBpbmhlcml0LmFlcyA9IEZBTFNFKSArCiAgZ2d0aXRsZSgiTW9ub2NsZTMgR3JhcGg6IENlbGwgVHlwZXMiKSArCiAgdGhlbWUocGxvdC50aXRsZSAgICAgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIHNpemUgPSAxMywgZmFjZSA9ICJib2xkIiksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQoKcF9ncmFwaF9sMiB8IHBfZ3JhcGhfY3QKCmBgYAoKIyMgU2V0IFJvb3QgJiBPcmRlciBDZWxscyBieSBQc2V1ZG90aW1lCgpgYGB7ciBwc2V1ZG90aW1lLXJvb3QsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD04fQpjYXQoIkF6aW11dGggbDIgbGFiZWxzIGluIENEUzpcbiIpCnByaW50KHNvcnQodW5pcXVlKGNvbERhdGEoY2RzKSRwcmVkaWN0ZWQuY2VsbHR5cGUubDIpKSkKCiMg4pSA4pSAIElkZW50aWZ5IG5haXZlIGNlbGxzIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgApuYWl2ZV9pZHMgPC0gcm93bmFtZXMoY29sRGF0YShjZHMpKVsKICBncmVwbCgibmFpdmV8TmFpdmV8VE4kIiwgY29sRGF0YShjZHMpJHByZWRpY3RlZC5jZWxsdHlwZS5sMiwKICAgICAgICBpZ25vcmUuY2FzZSA9IFRSVUUpIHwKICBncmVwbCgiVG5haXZlIiwgYXMuY2hhcmFjdGVyKGNvbERhdGEoY2RzKSRjZWxsX3R5cGUpLAogICAgICAgIGlnbm9yZS5jYXNlID0gRkFMU0UpCl0KY2F0KCJcbk5haXZlIHJvb3QgY2VsbHM6IiwgbGVuZ3RoKG5haXZlX2lkcyksICJcbiIpCmlmIChsZW5ndGgobmFpdmVfaWRzKSA9PSAwKQogIHN0b3AoIuKdjCBObyBuYWl2ZSBjZWxscyBmb3VuZC4gQ2hlY2sgbGFiZWwgcGF0dGVybnMgYWJvdmUuIikKCiMg4pSA4pSAIEZpbmQgcm9vdCBub2RlIGJ5IG1pbmltdW0gZGlzdGFuY2UgdG8gbmFpdmUgY2VudHJvaWQg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACiMgVXNpbmcgY2VudHJvaWQgZGlzdGFuY2UgKG5vdCBtb3N0LXBvcHVsYXRlZCBub2RlKSBpcyBtb3JlIHJvYnVzdDoKIyB0aGUgbW9zdC1wb3B1bGF0ZWQgbm9kZSBiaWFzZXMgdG93YXJkIHRoZSBkZW5zZXN0IHBhcnQgb2YgdGhlIG5haXZlCiMgY2x1c3RlciB3aGljaCBtYXkgbm90IGJlIHRoZSBlYXJsaWVzdCBwb2ludCBvbiB0aGUgdHJhamVjdG9yeS4KbmFpdmVfdW1hcCAgICAgPC0gRW1iZWRkaW5ncyhyZWZlcmVuY2VfaW50ZWdyYXRlZCwgInVtYXAiKVtuYWl2ZV9pZHMsIF0KbmFpdmVfY2VudHJvaWQgPC0gY29sTWVhbnMobmFpdmVfdW1hcCkKCnByX25vZGVzICAgPC0gdChjZHNAcHJpbmNpcGFsX2dyYXBoX2F1eFtbIlVNQVAiXV0kZHBfbXN0KQpub2RlX2Rpc3RzIDwtIHJvd1N1bXMoCiAgKHByX25vZGVzIC0gbWF0cml4KG5haXZlX2NlbnRyb2lkLAogICAgICAgICAgICAgICAgICAgICBucm93ID0gbnJvdyhwcl9ub2RlcyksIG5jb2wgPSAyLAogICAgICAgICAgICAgICAgICAgICBieXJvdyA9IFRSVUUpKV4yCikKcm9vdF9ub2RlIDwtIG5hbWVzKHdoaWNoLm1pbihub2RlX2Rpc3RzKSkKY2F0KCJSb290IG5vZGUgc2VsZWN0ZWQ6Iiwgcm9vdF9ub2RlLCAiXG4iKQpjYXQoc3ByaW50ZigiICBSb290IGNvb3JkczogICAgKCUuM2YsICUuM2YpXG4iLAogICAgICAgICAgICBwcl9ub2Rlc1tyb290X25vZGUsIDFdLCBwcl9ub2Rlc1tyb290X25vZGUsIDJdKSkKY2F0KHNwcmludGYoIiAgTmFpdmUgY2VudHJvaWQ6ICglLjNmLCAlLjNmKVxuIiwKICAgICAgICAgICAgbmFpdmVfY2VudHJvaWRbMV0sIG5haXZlX2NlbnRyb2lkWzJdKSkKCiMg4pSA4pSAIE9yZGVyIGNlbGxzIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgApjZHMgPC0gb3JkZXJfY2VsbHMoY2RzLCByb290X3ByX25vZGVzID0gcm9vdF9ub2RlKQoKIyBUcmFuc2ZlciBwc2V1ZG90aW1lIGJhY2sgdG8gU2V1cmF0IG9iamVjdApyZWZlcmVuY2VfaW50ZWdyYXRlZCRtb25vY2xlM19wc2V1ZG90aW1lIDwtIHBzZXVkb3RpbWUoY2RzKQpyZWZlcmVuY2VfaW50ZWdyYXRlZCRtb25vY2xlM19wc2V1ZG90aW1lWwogICFpcy5maW5pdGUocmVmZXJlbmNlX2ludGVncmF0ZWQkbW9ub2NsZTNfcHNldWRvdGltZSldIDwtIE5BCgpjYXQoIlxuUHNldWRvdGltZSBzdW1tYXJ5IChyZWZlcmVuY2UpOlxuIikKcHJpbnQoc3VtbWFyeShyZWZlcmVuY2VfaW50ZWdyYXRlZCRtb25vY2xlM19wc2V1ZG90aW1lWwogIGlzLmZpbml0ZShyZWZlcmVuY2VfaW50ZWdyYXRlZCRtb25vY2xlM19wc2V1ZG90aW1lKV0pKQoKIyDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZAKIyBUT1BPTE9HWSBWQUxJREFUSU9OCiMg4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQCiMgQ29ycmVjdCBvcmRlciBmb3IgZWFjaCBsaW5lYWdlOgojICAgRWZmZWN0b3I6IE5haXZlIDwgVENNIDwgVEVNIDwgVGVtcmEKIyAgIFRyZWc6ICAgICBOYWl2ZSA8IFRDTSDigJQgVHJlZyAgIChUcmVnIGJyYW5jaGVzIGZyb20gVENNLCBOT1QgYWZ0ZXIgVEVNKQojCiMgVGhpcyBpcyBhIGhhcmQgYmlvbG9naWNhbCByZXF1aXJlbWVudC4gSWYgVHJlZyA+IFRFTSBpbiBwc2V1ZG90aW1lLAojIHRoZSBncmFwaCBoYXMgcGxhY2VkIFRyZWcgZG93bnN0cmVhbSBvZiBURU0gd2hpY2ggaXMgYW5hdG9taWNhbGx5IHdyb25nLgojIFRyZWcgZGlmZmVyZW50aWF0ZXMgZnJvbSBUQ00tc3RhZ2UgcHJlY3Vyc29ycyB2aWEgRk9YUDMgaW5kdWN0aW9uLAojIG5vdCBmcm9tIHRlcm1pbmFsbHkgZGlmZmVyZW50aWF0ZWQgVEVNL1RlbXJhIGNlbGxzLgoKcHRfY2hlY2sgPC0gcmVmZXJlbmNlX2ludGVncmF0ZWRAbWV0YS5kYXRhICU+JQogIGZpbHRlcihpcy5maW5pdGUobW9ub2NsZTNfcHNldWRvdGltZSkpICU+JQogIGdyb3VwX2J5KHByZWRpY3RlZC5jZWxsdHlwZS5sMikgJT4lCiAgc3VtbWFyaXNlKAogICAgbiAgICAgICA9IG4oKSwKICAgIG1lZF9wdCAgPSByb3VuZChtZWRpYW4obW9ub2NsZTNfcHNldWRvdGltZSwgbmEucm0gPSBUUlVFKSwgMyksCiAgICBtZWFuX3B0ID0gcm91bmQobWVhbihtb25vY2xlM19wc2V1ZG90aW1lLCAgIG5hLnJtID0gVFJVRSksIDMpLAogICAgLmdyb3VwcyA9ICJkcm9wIgogICkgJT4lCiAgYXJyYW5nZShtZWRfcHQpCgpjYXQoIlxuPT09IFBzZXVkb3RpbWUgb3JkZXIgcGVyIHN0YXRlIChtdXN0IGJlIE5haXZlIDwgVENNIDwgVHJlZyA8IFRFTSA8IFRlbXJhKSA9PT1cbiIpCnByaW50KHB0X2NoZWNrKQoKIyBFeHRyYWN0IHN0YXRlIG1lZGlhbnMg4oCUIHVzZWQgZm9yIGJpbiBib3VuZGFyaWVzIGRvd25zdHJlYW0KbmFpdmVfbWVkIDwtIHB0X2NoZWNrJG1lZF9wdFtwdF9jaGVjayRwcmVkaWN0ZWQuY2VsbHR5cGUubDIgPT0gIkNENCBOYWl2ZSJdCnRjbV9tZWQgICA8LSBwdF9jaGVjayRtZWRfcHRbcHRfY2hlY2skcHJlZGljdGVkLmNlbGx0eXBlLmwyID09ICJDRDQgVENNIl0KdHJlZ19tZWQgIDwtIHB0X2NoZWNrJG1lZF9wdFtwdF9jaGVjayRwcmVkaWN0ZWQuY2VsbHR5cGUubDIgPT0gIlRyZWciXQp0ZW1fbWVkICAgPC0gcHRfY2hlY2skbWVkX3B0W3B0X2NoZWNrJHByZWRpY3RlZC5jZWxsdHlwZS5sMiA9PSAiQ0Q0IFRFTSJdCnRlbXJhX21lZCA8LSBwdF9jaGVjayRtZWRfcHRbcHRfY2hlY2skcHJlZGljdGVkLmNlbGx0eXBlLmwyID09ICJDRDQgVGVtcmEvQ1RMIl0KCiMgRklYOiBzaW5nbGUtc3RyaW5nIHNwcmludGYg4oCUIG9yaWdpbmFsIGhhZCB0d28gZm9ybWF0IHN0cmluZ3Mgd2hpY2gKIyBjYXVzZWQgdGhlIHNlY29uZCBzdHJpbmcgdG8gYmUgc2lsZW50bHkgaWdub3JlZApjYXQoc3ByaW50ZigKICAiXG5TdGF0ZSBtZWRpYW5zOiBOYWl2ZT0lLjJmIHwgVENNPSUuMmYgfCBUcmVnPSUuMmYgfCBURU09JS4yZiB8IFRlbXJhPSUuMmZcbiIsCiAgbmFpdmVfbWVkLCB0Y21fbWVkLCB0cmVnX21lZCwgdGVtX21lZCwgdGVtcmFfbWVkCikpCgojIEhhcmQgc3RvcHMg4oCUIHNjcmlwdCB3aWxsIG5vdCBjb250aW51ZSBpZiB0b3BvbG9neSBpcyBiaW9sb2dpY2FsbHkgd3JvbmcKaWYgKG5haXZlX21lZCA+IHRjbV9tZWQpCiAgc3RvcChzcHJpbnRmKAogICAgIuKdjCBUT1BPTE9HWSBFUlJPUjogTmFpdmUgKCUuMmYpID4gVENNICglLjJmKS5cbiAgUm9vdCBub2RlIG1heSBiZSBtaXNwbGFjZWQg4oCUIGNoZWNrIG5haXZlX2NlbnRyb2lkIGNvb3JkaW5hdGVzLiIsCiAgICBuYWl2ZV9tZWQsIHRjbV9tZWQpKQoKaWYgKHRjbV9tZWQgPiB0ZW1fbWVkKQogIHN0b3Aoc3ByaW50ZigKICAgICLinYwgVE9QT0xPR1kgRVJST1I6IFRDTSAoJS4yZikgPiBURU0gKCUuMmYpLlxuICBSZS1jaGVjayByb290IG5vZGUgc2VsZWN0aW9uLiIsCiAgICB0Y21fbWVkLCB0ZW1fbWVkKSkKCmlmICh0cmVnX21lZCA+IHRlbV9tZWQpCiAgc3RvcChzcHJpbnRmKAogICAgIuKdjCBUT1BPTE9HWSBFUlJPUjogVHJlZyAoJS4yZikgPiBURU0gKCUuMmYpLlxuICBUcmVnIG11c3QgYnJhbmNoIGZyb20gVENNLCBub3QgYmUgZG93bnN0cmVhbSBvZiBURU0uXG4gIEZpeDogcmUtcnVuIGxlYXJuX2dyYXBoIHdpdGggbWluaW1hbF9icmFuY2hfbGVuID0gMyIsCiAgICB0cmVnX21lZCwgdGVtX21lZCkpCgojIEZJWDogc2luZ2xlIHNwcmludGYgZm9ybWF0IHN0cmluZyAod2FzIHNwbGl0IGFjcm9zcyB0d28gc3RyaW5ncyBiZWZvcmUpCmNhdChzcHJpbnRmKAogICLinIUgVG9wb2xvZ3kgY29uZmlybWVkOiBOYWl2ZSglLjJmKSA8IFRDTSglLjJmKSA8IFRyZWcoJS4yZikgPCBURU0oJS4yZikgPCBUZW1yYSglLjJmKVxuIiwKICBuYWl2ZV9tZWQsIHRjbV9tZWQsIHRyZWdfbWVkLCB0ZW1fbWVkLCB0ZW1yYV9tZWQKKSkKCiMg4pSA4pSAIFBzZXVkb3RpbWUgVU1BUCBwbG90cyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKcGxvdF9jZWxscygKICBjZHMsCiAgY29sb3JfY2VsbHNfYnkgICAgICAgID0gInBzZXVkb3RpbWUiLAogIGxhYmVsX2NlbGxfZ3JvdXBzICAgICA9IEZBTFNFLAogIGNlbGxfc2l6ZSAgICAgICAgICAgICA9IDAuOCwKICBzaG93X3RyYWplY3RvcnlfZ3JhcGggPSBUUlVFCikgKwogIHNjYWxlX2NvbG9yX3ZpcmlkaXNfYyhvcHRpb24gPSAicGxhc21hIiwgbmFtZSA9ICJQc2V1ZG90aW1lIikgKwogIGdndGl0bGUoIlJlZmVyZW5jZSBQc2V1ZG90aW1lXG4oUm9vdCA9IENENCBOYWl2ZSB8IFR3byBsaW5lYWdlczogVEVNL1RlbXJhICsgVHJlZykiKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgc2l6ZSA9IDEzLCBmYWNlID0gImJvbGQiKSkKYGBgCgojIyBWYWxpZGF0ZSBQc2V1ZG90aW1lIEFnYWluc3QgQ2VsbCBUeXBlCgpgYGB7ciB2YWxpZGF0ZS1wc2V1ZG90aW1lLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9NX0KIyBWaXN1YWwgdmFsaWRhdGlvbiB0aGF0IHBzZXVkb3RpbWUgb3JkZXIgbWF0Y2hlcyBleHBlY3RlZCBiaW9sb2d5LgojIEJvdGggdmlvbGluIGFuZCByaWRnZSBwbG90cyBzaG91bGQgc2hvdzoKIyAgIE5haXZlIChsb3dlc3QpIOKGkiBUQ00g4oaSIFRyZWcgKGJyYW5jaCwgaW50ZXJtZWRpYXRlKSDihpIgVEVNIOKGkiBUZW1yYSAoaGlnaGVzdCkKCnB0X2RhdGEgPC0gZGF0YS5mcmFtZSgKICBwc2V1ZG90aW1lID0gcmVmZXJlbmNlX2ludGVncmF0ZWQkbW9ub2NsZTNfcHNldWRvdGltZSwKICBsMiAgICAgICAgID0gcmVmZXJlbmNlX2ludGVncmF0ZWQkcHJlZGljdGVkLmNlbGx0eXBlLmwyCikgJT4lIGZpbHRlcihpcy5maW5pdGUocHNldWRvdGltZSksICFpcy5uYShsMikpCgojIE9yZGVyIHgtYXhpcyBieSBtZWRpYW4gcHNldWRvdGltZSDigJQgc2hvdWxkIGdpdmUgYmlvbG9naWNhbCBvcmRlcgpsMl9vcmRlciA8LSBwdF9kYXRhICU+JQogIGdyb3VwX2J5KGwyKSAlPiUKICBzdW1tYXJpc2UobWVkX3B0ID0gbWVkaWFuKHBzZXVkb3RpbWUsIG5hLnJtID0gVFJVRSksIC5ncm91cHMgPSAiZHJvcCIpICU+JQogIGFycmFuZ2UobWVkX3B0KSAlPiUKICBwdWxsKGwyKQpwdF9kYXRhJGwyIDwtIGZhY3RvcihwdF9kYXRhJGwyLCBsZXZlbHMgPSBsMl9vcmRlcikKCnBfdmlvbGluIDwtIGdncGxvdChwdF9kYXRhLCBhZXMoeCA9IGwyLCB5ID0gcHNldWRvdGltZSwgZmlsbCA9IGwyKSkgKwogIGdlb21fdmlvbGluKHNjYWxlID0gIndpZHRoIiwgYWxwaGEgPSAwLjg1LCB0cmltID0gVFJVRSkgKwogIGdlb21fYm94cGxvdCh3aWR0aCA9IDAuMTIsIGZpbGwgPSAid2hpdGUiLCBvdXRsaWVyLnNpemUgPSAwLjMpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBhemltdXRoX2wyX2NvbG9ycywgbmEudmFsdWUgPSAiZ3JleTcwIikgKwogIHRoZW1lX2NsYXNzaWMoYmFzZV9zaXplID0gMTEpICsKICB0aGVtZShheGlzLnRleHQueCAgID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDAsIGhqdXN0ID0gMSwgc2l6ZSA9IDkpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgICAgICBwbG90LnRpdGxlICAgID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiKSkgKwogIGxhYnModGl0bGUgICAgPSAiUmVmZXJlbmNlIFBzZXVkb3RpbWUgcGVyIEF6aW11dGggbDIgTGFiZWwiLAogICAgICAgc3VidGl0bGUgPSAiRXhwZWN0ZWQgb3JkZXI6IE5haXZlIDwgVENNIDwgVHJlZyA8IFRFTSA8IFRlbXJhIiwKICAgICAgIHggPSAiIiwgeSA9ICJNb25vY2xlMyBQc2V1ZG90aW1lIikKCnBfcmlkZ2UgPC0gZ2dwbG90KHB0X2RhdGEsIGFlcyh4ID0gcHNldWRvdGltZSwgeSA9IGwyLCBmaWxsID0gbDIpKSArCiAgZ2VvbV9kZW5zaXR5X3JpZGdlcyhhbHBoYSA9IDAuNzUsIHNjYWxlID0gMS4yLCByZWxfbWluX2hlaWdodCA9IDAuMDEpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBhemltdXRoX2wyX2NvbG9ycywgbmEudmFsdWUgPSAiZ3JleTcwIikgKwogIHRoZW1lX2NsYXNzaWMoYmFzZV9zaXplID0gMTApICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgZmFjZSA9ICJib2xkIikpICsKICBsYWJzKHRpdGxlID0gIlJlZmVyZW5jZSBQc2V1ZG90aW1lIERlbnNpdHkgYnkgQXppbXV0aCBsMiBMYWJlbCIsCiAgICAgICB4ID0gIlBzZXVkb3RpbWUiLCB5ID0gIiIpCgpwX3Zpb2xpbiB8IHBfcmlkZ2UKYGBgCgoKYGBge3IgZml4LXJlZmVyZW5jZS1zY3QtcGNhLCBpbmNsdWRlPVRSVUV9CgojIOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkAojIENSSVRJQ0FMIEZJWDogUmVidWlsZCByZWZlcmVuY2UgUENBIGluIFNDVCBzcGFjZSAocHJlc2VydmVzIFVNQVAgdHJhamVjdG9yeSkKIyBQUk9CTEVNOiByZWZlcmVuY2VfaW50ZWdyYXRlZCBQQ0EgPSAiaW50ZWdyYXRlZCIgYXNzYXkgKDUwIGRpbXMpCiMgICAgICAgICAgTWFsaWduYW50Q0Q0VCBQQ0EgICAgID0gIlNDVCIgYXNzYXkgKDMwIGRpbXMpIAojIFNPTFVUSU9OOiBTQ1QgKyBQQ0Egb24gcmVmZXJlbmNlIOKGkiB1bmlmaWVkIFNDVCBzcGFjZSBmb3IgRmluZFRyYW5zZmVyQW5jaG9ycwojIFVNQVAgTU9ERUwgMTAwJSBTQUZFIOKAlCBsaXZlcyBpbiByZWR1Y3Rpb25zJHVtYXBAbWlzYyRtb2RlbAojIOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkAoKY2F0KCI9PT0gRElBR05PU1RJQzogQ3VycmVudCByZWZlcmVuY2Ugc3RhdGUgPT09XG4iKQpjYXQoIkRlZmF1bHRBc3NheToiLCBEZWZhdWx0QXNzYXkocmVmZXJlbmNlX2ludGVncmF0ZWQpLCAiXG4iKQpjYXQoIlNDVCBIVkdzOiIsIGxlbmd0aChWYXJpYWJsZUZlYXR1cmVzKHJlZmVyZW5jZV9pbnRlZ3JhdGVkKSksICJcbiIpCmNhdCgiUENBIGRpbXM6IiwgbmNvbChFbWJlZGRpbmdzKHJlZmVyZW5jZV9pbnRlZ3JhdGVkLCAicGNhIikpLCAiXG4iKQpjYXQoIlVNQVAgbW9kZWwgcHJlc2VudDoiLCAhaXMubnVsbChyZWZlcmVuY2VfaW50ZWdyYXRlZEByZWR1Y3Rpb25zJHVtYXBAbWlzYyRtb2RlbCksICJcbiIpCgojIDEuIFNhbml0eSBjaGVjayBVTUFQIGJlZm9yZQpvbGRfdW1hcF9jb29yZHMgPC0gRW1iZWRkaW5ncyhyZWZlcmVuY2VfaW50ZWdyYXRlZCwgInVtYXAiKVsxOjMsIF0Kb2xkX3VtYXBfbW9kZWwgIDwtICFpcy5udWxsKHJlZmVyZW5jZV9pbnRlZ3JhdGVkQHJlZHVjdGlvbnMkdW1hcEBtaXNjJG1vZGVsKQpjYXQoIlxuVU1BUCBmaXJzdCAzIGNlbGxzIChiYWNrdXApOiIsIHBhc3RlKHJvdW5kKG9sZF91bWFwX2Nvb3Jkc1sxLF0sIDMpLCBjb2xsYXBzZT0iICIpLCAiXG4iKQoKIyAyLiBTQ1Qgb24gcmVmZXJlbmNlIChjcmVhdGVzIFNDVCBhc3NheSArIEhWR3MpCkRlZmF1bHRBc3NheShyZWZlcmVuY2VfaW50ZWdyYXRlZCkgPC0gIlJOQSIKcmVmZXJlbmNlX2ludGVncmF0ZWQgPC0gU0NUcmFuc2Zvcm0oCiAgcmVmZXJlbmNlX2ludGVncmF0ZWQsCiAgdmFyaWFibGUuZmVhdHVyZXMubiA9IDMwMDAsCiAgdnN0LmZsYXZvciAgICAgICAgICA9ICJ2MiIsCiAgdmFycy50by5yZWdyZXNzICAgICA9IGMoInBlcmNlbnQubXQiLCAiUy5TY29yZSIsICJHMk0uU2NvcmUiKSwKICB2ZXJib3NlICAgICAgICAgICAgID0gRkFMU0UKKQoKIyAzLiBQQ0EgaW4gU0NUIHNwYWNlIChtYXRjaGVzIE1hbGlnbmFudENENFQgZXhhY3RseSkKcmVmZXJlbmNlX2ludGVncmF0ZWQgPC0gUnVuUENBKAogIHJlZmVyZW5jZV9pbnRlZ3JhdGVkLAogIGFzc2F5ICAgPSAiU0NUIiwKICBucGNzICAgID0gMzAsICAgICAgICAjIOKGkCBNYXRjaCBxdWVyeSBkaW1zIGV4YWN0bHkKICB2ZXJib3NlID0gRkFMU0UKKQoKIyA0LiBWZXJpZnkgZml4CmNhdCgiXG49PT0gRklYRUQ6IE5ldyByZWZlcmVuY2Ugc3RhdGUgPT09XG4iKQpjYXQoIkRlZmF1bHRBc3NheToiLCBEZWZhdWx0QXNzYXkocmVmZXJlbmNlX2ludGVncmF0ZWQpLCAiXG4iKQpjYXQoIlNDVCBIVkdzOiIsIGxlbmd0aChWYXJpYWJsZUZlYXR1cmVzKHJlZmVyZW5jZV9pbnRlZ3JhdGVkKSksICJcbiIpCmNhdCgiUENBIGRpbXM6IiwgbmNvbChFbWJlZGRpbmdzKHJlZmVyZW5jZV9pbnRlZ3JhdGVkLCAicGNhIikpLCAiXG4iKQpjYXQoIlVNQVAgbW9kZWwgcHJlc2VydmVkOiIsICFpcy5udWxsKHJlZmVyZW5jZV9pbnRlZ3JhdGVkQHJlZHVjdGlvbnMkdW1hcEBtaXNjJG1vZGVsKSwgIlxuIikKCiMgNS4gVU1BUCBpbnRlZ3JpdHkgY2hlY2sKbmV3X3VtYXBfY29vcmRzIDwtIEVtYmVkZGluZ3MocmVmZXJlbmNlX2ludGVncmF0ZWQsICJ1bWFwIilbMTozLCBdCmNhdCgiVU1BUCB1bmNoYW5nZWQ6IiwgYWxsKG9sZF91bWFwX2Nvb3JkcyA9PSBuZXdfdW1hcF9jb29yZHMpLCAiXG4iKQpjYXQoIuKchSBSZWZlcmVuY2Ugbm93IGluIFNDVCBzcGFjZSDigJQgRmluZFRyYW5zZmVyQW5jaG9ycyB3aWxsIHdvcmsgZm9yIEFMTCA3IGxpbmVzXG4iKQoKYGBgCgoKCgoKCiMgUHJlcGFyZSBSZWZlcmVuY2UgZm9yIE1hcFF1ZXJ5CgpgYGB7ciBwcmVwYXJlLXJlZmVyZW5jZX0KIyBRVUlDSyBQUkUtRkxJR0hUIENIRUNLUyAocnVuIHRoZXNlIDIgbGluZXMgbm93KQpjYXQoIlNDVCBIVkdzOiIsIGxlbmd0aChWYXJpYWJsZUZlYXR1cmVzKHJlZmVyZW5jZV9pbnRlZ3JhdGVkLCBhc3NheSA9ICJTQ1QiKSksICJcbiIpCmNhdCgiUENBIGRpbXM6IiwgbmNvbChFbWJlZGRpbmdzKHJlZmVyZW5jZV9pbnRlZ3JhdGVkLCAicGNhIikpLCAiXG4iKQoKYGBgCgoKIyBNYXAgTWFsaWduYW50IENlbGxzIG9udG8gUmVmZXJlbmNlCgojIyBGaW5kIFRyYW5zZmVyIEFuY2hvcnMKCmBgYHtyIGZpbmQtYW5jaG9yc30KIyDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZAKIyBGSU5EIFRSQU5TRkVSIEFOQ0hPUlMKIyDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZAKIyBBbmNob3JzIGFyZSBtdXR1YWwgbmVhcmVzdCBuZWlnaGJvdXJzIGJldHdlZW4gcmVmZXJlbmNlIGFuZCBxdWVyeSBpbgojIHNoYXJlZCBQQ0Egc3BhY2UuIFRoZWlyIHF1YWxpdHkgZGV0ZXJtaW5lcyB0aGUgYWNjdXJhY3kgb2Y6CiMgICAoYSkgbGFiZWwgdHJhbnNmZXIgIOKGkiB3aGljaCBub3JtYWwgc3RhdGUgZG9lcyB0aGlzIFPDqXphcnkgY2VsbCByZXNlbWJsZT8KIyAgIChiKSBwc2V1ZG90aW1lIHRyYW5zZmVyIOKGkiB3aGVyZSBhbG9uZyB0aGUgZGlmZmVyZW50aWF0aW9uIGF4aXMgaXMgaXQ/CiMKIyBGRUFUVVJFIFNFTEVDVElPTiBTVFJBVEVHWToKIyAgIFN0ZXAgMSDigJQgaW50ZXJzZWN0IHJlZmVyZW5jZSArIHF1ZXJ5IEhWR3MKIyAgIFN0ZXAgMiDigJQgcmVtb3ZlIHRlY2huaWNhbC9qdW5rIGdlbmVzIChNVCwgcmlib3NvbWFsLCBIU1AsIGxuY1JOQSwgaGlzdG9uZSkKIyAgICAgICAgICAgIFdIWTogdGhlc2UgZ2VuZXMgYXJlIHZhcmlhYmxlIGR1ZSB0byBsaWJyYXJ5IHF1YWxpdHkgYW5kIHN0cmVzcywKIyAgICAgICAgICAgIE5PVCBkdWUgdG8gVCBjZWxsIGRpZmZlcmVudGlhdGlvbiBzdGF0ZS4gS2VlcGluZyB0aGVtIGJpYXNlcwojICAgICAgICAgICAgYW5jaG9ycyB0b3dhcmQgYmF0Y2ggZWZmZWN0cyByYXRoZXIgdGhhbiBiaW9sb2d5LgojICAgU3RlcCAzIOKAlCByYW5rIHJlbWFpbmluZyBnZW5lcyBieSBqb2ludCByZXNpZHVhbCB2YXJpYW5jZSAocmVmICsgcXVlcnkpCiMgICAgICAgICAgICBXSFk6IGdlbmVzIGhpZ2hseSB2YXJpYWJsZSBpbiBCT1RIIGRhdGFzZXRzIGFyZSB0aGUgbW9zdAojICAgICAgICAgICAgcmVsaWFibGUgZm9yIGZpbmRpbmcgdHJ1ZSBiaW9sb2dpY2FsIG5lYXJlc3QgbmVpZ2hib3VycwojIOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkAoKRGVmYXVsdEFzc2F5KHJlZmVyZW5jZV9pbnRlZ3JhdGVkKSA8LSAiU0NUIgpEZWZhdWx0QXNzYXkoTWFsaWduYW50Q0Q0VCkgICAgICAgIDwtICJTQ1QiCgojIOKUgOKUgCBTdGVwIDE6IEludGVyc2VjdCByZWZlcmVuY2UgYW5kIHF1ZXJ5IEhWR3Mg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACnJlZl9odmdzIDwtIFZhcmlhYmxlRmVhdHVyZXMocmVmZXJlbmNlX2ludGVncmF0ZWQsIGFzc2F5ID0gIlNDVCIpCmlmIChsZW5ndGgocmVmX2h2Z3MpID09IDApIHsKICBjYXQoIuKaoO+4jyAgTm8gSFZHcyBzdG9yZWQgaW4gcmVmZXJlbmNlIOKAlCBleHRyYWN0aW5nIGZyb20gc2NhbGUuZGF0YVxuIikKICByZWZfaHZncyA8LSByb3duYW1lcyhHZXRBc3NheURhdGEocmVmZXJlbmNlX2ludGVncmF0ZWQsIGFzc2F5ID0gIlNDVCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYXllciA9ICJzY2FsZS5kYXRhIikpCn0KCnF1ZXJ5X2h2Z3MgICAgICA8LSBWYXJpYWJsZUZlYXR1cmVzKE1hbGlnbmFudENENFQpCmNvbW1vbl9mZWF0dXJlcyA8LSBpbnRlcnNlY3QocmVmX2h2Z3MsIHF1ZXJ5X2h2Z3MpCgpjYXQoIj09PSBGZWF0dXJlIHNldCBjb25zdHJ1Y3Rpb24gPT09XG4iKQpjYXQoIlJlZmVyZW5jZSBIVkdzIDoiLCBsZW5ndGgocmVmX2h2Z3MpLCAiXG4iKQpjYXQoIlF1ZXJ5IEhWR3MgICAgIDoiLCBsZW5ndGgocXVlcnlfaHZncyksICJcbiIpCmNhdCgiQ29tbW9uIEhWR3MgICAgOiIsIGxlbmd0aChjb21tb25fZmVhdHVyZXMpLCAiXG4iKQoKaWYgKGxlbmd0aChjb21tb25fZmVhdHVyZXMpIDwgMTUwMCkKICB3YXJuaW5nKHBhc3RlMCgi4pqg77iPICBPbmx5ICIsIGxlbmd0aChjb21tb25fZmVhdHVyZXMpLAogICAgICAgICAgICAgICAgICIgY29tbW9uIGZlYXR1cmVzIOKAlCBiZWxvdyByZWNvbW1lbmRlZCAxNTAwLlxuIiwKICAgICAgICAgICAgICAgICAiICAgSW5jcmVhc2UgbmZlYXR1cmVzIGluIFNDVHJhbnNmb3JtIHRvIDQwMDAuIikpCgojIOKUgOKUgCBTdGVwIDI6IFJlbW92ZSB0ZWNobmljYWwvanVuayBnZW5lcyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKIyBDYXRlZ29yaWVzIHJlbW92ZWQgYW5kIFdIWToKIyAgIF5NVC0gICA6IG1pdG9jaG9uZHJpYWwgT1hQSE9TIGdlbmVzIOKAlCB2YXJpYWJsZSBkdWUgdG8gY2VsbCBxdWFsaXR5L3N0cmVzcwojICAgXlJQTC9TIDogcmlib3NvbWFsIHByb3RlaW5zIOKAlCB2YXJpYWJsZSBkdWUgdG8gdHJhbnNsYXRpb24gcmF0ZS9saWJyYXJ5IGRlcHRoCiMgICBeSFNQKiAgOiBoZWF0IHNob2NrIHByb3RlaW5zIOKAlCB2YXJpYWJsZSBkdWUgdG8gc3RyZXNzLCBub3QgZGlmZmVyZW50aWF0aW9uCiMgICBeU05IRy9NQUxBVDEvTkVBVDEgOiBsbmNSTkFzIOKAlCBudWNsZWFyIGFyY2hpdGVjdHVyZSwgbm90IFQgY2VsbCBzdGF0ZQojICAgXkhJU1QgIDogcmVwbGljYXRpb24tZGVwZW5kZW50IGhpc3RvbmVzIOKAlCBTLXBoYXNlIG1hcmtlcnMgKGNlbGwgY3ljbGUpCiMgICAgICAgICAgICBOT1RFOiBeSElTVCBhZGRlZCBiYXNlZCBvbiBwcmUtcnVuIFFDIHNob3dpbmcgMjArIGhpc3RvbmUgZ2VuZXMKIyAgICAgICAgICAgIGluIHNoYXJlZCBIVkcgc2V0LCBhbGwgZHJpdmVuIGJ5IGN5Y2xpbmcgc3VicG9wdWxhdGlvbgoKanVua19wYXR0ZXJuIDwtIHBhc3RlMCgKICAiXlJQTHxeUlBTfCIsICAgICAgICAgICMgcmlib3NvbWFsIGxhcmdlICsgc21hbGwgc3VidW5pdHMKICAiXk1ULXwiLCAgICAgICAgICAgICAgICMgbWl0b2Nob25kcmlhbCBnZW5vbWUgKE9YUEhPUyBjb21wbGV4ZXMgSSxJSUksSVYsVikKICAiXkhTUHxeSFNQQXxeSFNQQnwiLCAgIyBoZWF0IHNob2NrIHByb3RlaW5zCiAgIl5IU1BEfF5IU1BFfF5IU1BIfCIsICMgaGVhdCBzaG9jayBwcm90ZWlucyAoY29udGludWVkKQogICJeU05IR3wiLCAgICAgICAgICAgICAgIyBzbWFsbCBudWNsZW9sYXIgUk5BIGhvc3QgZ2VuZXMKICAiXk1BTEFUMXxeTkVBVDF8IiwgICAgICMgYWJ1bmRhbnQgbnVjbGVhciBsbmNSTkFzCiAgIl5YSVNUfCIsICAgICAgICAgICAgICAjIFgtaW5hY3RpdmF0aW9uIChzZXgtc3BlY2lmaWMgYmF0Y2gpCiAgIl5ISVNUIiAgICAgICAgICAgICAgICAjIHJlcGxpY2F0aW9uLWRlcGVuZGVudCBoaXN0b25lcyAoUy1waGFzZSBzaWduYWwpCikKCiMgUmVwb3J0IGJyZWFrZG93biBieSBjYXRlZ29yeSBiZWZvcmUgcmVtb3ZpbmcKbXRfcmVtb3ZlZCAgIDwtIHN1bShncmVwbCgiXk1ULSIsICAgICAgICAgICAgICAgICAgICBjb21tb25fZmVhdHVyZXMpKQpyaWJvX3JlbW92ZWQgPC0gc3VtKGdyZXBsKCJeUlBMfF5SUFMiLCAgICAgICAgICAgICAgIGNvbW1vbl9mZWF0dXJlcykpCmhzcF9yZW1vdmVkICA8LSBzdW0oZ3JlcGwoIl5IU1AiLCAgICAgICAgICAgICAgICAgICAgY29tbW9uX2ZlYXR1cmVzKSkKbG5jX3JlbW92ZWQgIDwtIHN1bShncmVwbCgiXlNOSEd8Xk1BTEFUMXxeTkVBVDF8XlhJU1QiLCBjb21tb25fZmVhdHVyZXMpKQpoaXN0X3JlbW92ZWQgPC0gc3VtKGdyZXBsKCJeSElTVCIsICAgICAgICAgICAgICAgICAgIGNvbW1vbl9mZWF0dXJlcykpCgpjYXQoIlxuPT09IEp1bmsgZ2VuZSByZW1vdmFsIGJyZWFrZG93biA9PT1cbiIpCmNhdChzcHJpbnRmKCIgIE1pdG9jaG9uZHJpYWwgKE9YUEhPUykgICAgOiAlZFxuIiwgbXRfcmVtb3ZlZCkpCmNhdChzcHJpbnRmKCIgIFJpYm9zb21hbCAoUlBML1JQUykgICAgICAgOiAlZFxuIiwgcmlib19yZW1vdmVkKSkKY2F0KHNwcmludGYoIiAgSGVhdCBzaG9jayAoSFNQKikgICAgICAgICA6ICVkXG4iLCBoc3BfcmVtb3ZlZCkpCmNhdChzcHJpbnRmKCIgIGxuY1JOQSAoU05IRy9NQUxBVDEvTkVBVDEpOiAlZFxuIiwgbG5jX3JlbW92ZWQpKQpjYXQoc3ByaW50ZigiICBIaXN0b25lcyAoSElTVCopICAgICAgICAgIDogJWRcbiIsIGhpc3RfcmVtb3ZlZCkpCgpuX2JlZm9yZSAgICAgICAgICAgICAgPC0gbGVuZ3RoKGNvbW1vbl9mZWF0dXJlcykKY29tbW9uX2ZlYXR1cmVzX2NsZWFuIDwtIGNvbW1vbl9mZWF0dXJlc1shZ3JlcGwoanVua19wYXR0ZXJuLCBjb21tb25fZmVhdHVyZXMpXQpuX3JlbW92ZWQgICAgICAgICAgICAgPC0gbl9iZWZvcmUgLSBsZW5ndGgoY29tbW9uX2ZlYXR1cmVzX2NsZWFuKQoKY2F0KHNwcmludGYoIiAg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG4iKSkKY2F0KHNwcmludGYoIiAgVG90YWwgcmVtb3ZlZCAgICAgICAgICAgICA6ICVkICglLjFmJSUpXG4iLAogICAgICAgICAgICBuX3JlbW92ZWQsIDEwMCAqIG5fcmVtb3ZlZCAvIG5fYmVmb3JlKSkKY2F0KHNwcmludGYoIiAgQ2xlYW4gZmVhdHVyZXMgcmVtYWluaW5nICA6ICVkXG5cbiIsCiAgICAgICAgICAgIGxlbmd0aChjb21tb25fZmVhdHVyZXNfY2xlYW4pKSkKCmlmIChsZW5ndGgoY29tbW9uX2ZlYXR1cmVzX2NsZWFuKSA8IDEyMDApCiAgd2FybmluZygi4pqg77iPICBGZXdlciB0aGFuIDEyMDAgY2xlYW4gZmVhdHVyZXMg4oCUIGFuY2hvciBxdWFsaXR5IG1heSBiZSByZWR1Y2VkLiIpCgojIOKUgOKUgCBTdGVwIDM6IFJhbmsgYnkgam9pbnQgcmVzaWR1YWwgdmFyaWFuY2Ug4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACiMgR2VuZXMgdGhhdCBhcmUgaGlnaGx5IHZhcmlhYmxlIGluIEJPVEggdGhlIGhlYWx0aHkgcmVmZXJlbmNlIEFORCB0aGUKIyBtYWxpZ25hbnQgcXVlcnkgYXJlIHRoZSBtb3N0IGluZm9ybWF0aXZlIGZvciBmaW5kaW5nIHRydWUgYmlvbG9naWNhbAojIG5lYXJlc3QgbmVpZ2hib3Vycy4gUmFua2luZyBieSBqb2ludCB2YXJpYW5jZSBlbnN1cmVzIGRpZmZlcmVudGlhdGlvbgojIGdlbmVzIChDQ1I3LCBUQ0Y3LCBHWk1LLCBGT1hQMykgcmFuayBhYm92ZSBsaW5lLXNwZWNpZmljIG5vaXNlLgoKcmVmX3Zhcl9tZXRhICAgPC0gcmVmZXJlbmNlX2ludGVncmF0ZWRbWyJTQ1QiXV1AbWV0YS5mZWF0dXJlcwpxdWVyeV92YXJfbWV0YSA8LSBNYWxpZ25hbnRDRDRUW1siU0NUIl1dQG1ldGEuZmVhdHVyZXMKCmlmICgicmVzaWR1YWxfdmFyaWFuY2UiICVpbiUgY29sbmFtZXMocmVmX3Zhcl9tZXRhKSAmJgogICAgInJlc2lkdWFsX3ZhcmlhbmNlIiAlaW4lIGNvbG5hbWVzKHF1ZXJ5X3Zhcl9tZXRhKSkgewoKICByZWZfcnYgICA8LSByZWZfdmFyX21ldGFbY29tbW9uX2ZlYXR1cmVzX2NsZWFuLCAgICJyZXNpZHVhbF92YXJpYW5jZSJdCiAgcXVlcnlfcnYgPC0gcXVlcnlfdmFyX21ldGFbY29tbW9uX2ZlYXR1cmVzX2NsZWFuLCAicmVzaWR1YWxfdmFyaWFuY2UiXQogIHJlZl9ydltpcy5uYShyZWZfcnYpXSAgICAgPC0gMAogIHF1ZXJ5X3J2W2lzLm5hKHF1ZXJ5X3J2KV0gPC0gMAogIG1lYW5fcnYgICAgICAgICA8LSAocmVmX3J2ICsgcXVlcnlfcnYpIC8gMgogIGNvbW1vbl9mZWF0dXJlcyA8LSBjb21tb25fZmVhdHVyZXNfY2xlYW5bb3JkZXIobWVhbl9ydiwgZGVjcmVhc2luZyA9IFRSVUUpXQoKICBjYXQoIlRvcCAyMCBmZWF0dXJlcyBieSBqb2ludCByZXNpZHVhbCB2YXJpYW5jZTpcbiIpCiAgcHJpbnQoaGVhZChjb21tb25fZmVhdHVyZXMsIDIwKSkKICBjYXQoIlxuIikKCn0gZWxzZSB7CiAgY2F0KCLimqDvuI8gIHJlc2lkdWFsX3ZhcmlhbmNlIG5vdCBpbiBTQ1QgbWV0YS5mZWF0dXJlcyDigJQgdXNpbmcgdW5yYW5rZWQgY2xlYW4gbGlzdFxuIikKICBjYXQoIiAgIFRoaXMgaXMgT0sgYnV0IHJhbmtpbmcgd291bGQgaW1wcm92ZSBhbmNob3IgcXVhbGl0eS5cbiIpCiAgY29tbW9uX2ZlYXR1cmVzIDwtIGNvbW1vbl9mZWF0dXJlc19jbGVhbgp9CgojIOKUgOKUgCBTdGVwIDQ6IENhbm9uaWNhbCBUIGNlbGwgbWFya2VyIGNoZWNrIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgAojIFRoZXNlIG1hcmtlcnMgc3BhbiB0aGUgZnVsbCBkaWZmZXJlbnRpYXRpb24gYXhpcyBhbmQgVHJlZyBicmFuY2guCiMgSWYgZmV3ZXIgdGhhbiA1IGFyZSBwcmVzZW50LCB0aGUgZmVhdHVyZSBzZXQgY2Fubm90IGRpc3Rpbmd1aXNoCiMgZGlmZmVyZW50aWF0aW9uIHN0YXRlcyBhbmQgYW5jaG9yIHF1YWxpdHkgd2lsbCBiZSBwb29yLgptYXJrZXJzX2NoZWNrIDwtIGMoCiAgIkNDUjciLCAiU0VMTCIsICJUQ0Y3IiwgIklMN1IiLCAgICAgICAgICAjIE5haXZlL1RDTQogICJHWk1CIiwgIkdaTUsiLCAiUFJGMSIsICJFT01FUyIsICAgICAgICAgIyBFZmZlY3Rvci9URU0KICAiRk9YUDMiLCAiSUtaRjIiLCAgICAgICAgICAgICAgICAgICAgICAgICAjIFRyZWcKICAiVE9YIiwgIlBEQ0QxIiwgIkxBRzMiLCAgICAgICAgICAgICAgICAgICMgRXhoYXVzdGlvbgogICJDRDQ0IiwgIlRCWDIxIiAgICAgICAgICAgICAgICAgICAgICAgICAgICMgQWN0aXZhdGlvbgopCmZvdW5kICAgICA8LSBtYXJrZXJzX2NoZWNrW21hcmtlcnNfY2hlY2sgJWluJSBjb21tb25fZmVhdHVyZXNdCm5vdF9mb3VuZCA8LSBtYXJrZXJzX2NoZWNrWyFtYXJrZXJzX2NoZWNrICVpbiUgY29tbW9uX2ZlYXR1cmVzXQoKY2F0KCI9PT0gQ2Fub25pY2FsIFQgY2VsbCBtYXJrZXIgY2hlY2sgPT09XG4iKQpjYXQoIlByZXNlbnQgICgiLCBsZW5ndGgoZm91bmQpLCAgICAiKToiLCBwYXN0ZShmb3VuZCwgICAgIGNvbGxhcHNlID0gIiwgIiksICJcbiIpCmNhdCgiQWJzZW50ICAgKCIsIGxlbmd0aChub3RfZm91bmQpLCIpOiIsIHBhc3RlKG5vdF9mb3VuZCwgY29sbGFwc2UgPSAiLCAiKSwgIlxuIikKCmlmIChsZW5ndGgoZm91bmQpIDwgNSkgewogIHdhcm5pbmcoIuKaoO+4jyAgRmV3ZXIgdGhhbiA1IGNhbm9uaWNhbCBtYXJrZXJzIHByZXNlbnQg4oCUIGFuY2hvciBxdWFsaXR5IGF0IHJpc2suIikKfSBlbHNlIGlmIChsZW5ndGgoZm91bmQpIDwgOCkgewogIGNhdCgi4pqg77iPICBTb21lIG1hcmtlcnMgYWJzZW50IOKAlCBsaWtlbHkgaGV0ZXJvZ2VuZW91cyBhY3Jvc3MgbGluZXMgKGV4cGVjdGVkKVxuIikKICBjYXQoIiAgIFByb2plY3Rpb24gd2lsbCBzdGlsbCB3b3JrIGJ1dCBpbnRlcnByZXQgcGVyLWxpbmUgcmVzdWx0cyBjYXJlZnVsbHlcbiIpCn0gZWxzZSB7CiAgY2F0KCLinIUgR29vZCBtYXJrZXIgY292ZXJhZ2Ug4oCUIGZlYXR1cmUgc2V0IGNhcHR1cmVzIGRpZmZlcmVudGlhdGlvbiBheGlzXG4iKQp9CgojIOKUgOKUgCBTdGVwIDU6IEZpbmRUcmFuc2ZlckFuY2hvcnMg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACiMgay5hbmNob3IgPSAxMCAgOiBuZWlnaGJvdXJzIHVzZWQgdG8gZmluZCBhbmNob3JzIOKAlCBiYWxhbmNlZCBmb3IgfjExayByZWYKIyBrLmZpbHRlciA9IDUwMCA6IG5laWdoYm91cnMgZm9yIGFuY2hvciBmaWx0ZXJpbmcg4oCUIHJlbW92ZXMgd2VhayBhbmNob3JzCiMgay5zY29yZSAgPSAzMCAgOiBuZWlnaGJvdXJzIGZvciBhbmNob3Igc2NvcmluZyDigJQgd2VpZ2h0cyBhbmNob3IgcXVhbGl0eQojIGRpbXMgICAgID0gMTozMDogdXNlIGFsbCAzMCBQQ0EgZGltcyDigJQgY2FwdHVyZXMgZnVsbCBkaWZmZXJlbnRpYXRpb24gc3BhY2UKCmRpbXNfdG9fdXNlIDwtIG1pbigzMCwKICAgICAgICAgICAgICAgICAgIG5jb2woRW1iZWRkaW5ncyhyZWZlcmVuY2VfaW50ZWdyYXRlZCwgInBjYSIpKSwKICAgICAgICAgICAgICAgICAgIG5jb2woRW1iZWRkaW5ncyhNYWxpZ25hbnRDRDRULCAgICAgICAgInBjYSIpKSkKY2F0KCJcbkZpbmRpbmcgYW5jaG9ycyB3aXRoIGRpbXMgMToiLCBkaW1zX3RvX3VzZSwgIlxuIikKY2F0KCJGZWF0dXJlcyB1c2VkOiIsIGxlbmd0aChjb21tb25fZmVhdHVyZXMpLCAiXG5cbiIpCgphbmNob3JzIDwtIEZpbmRUcmFuc2ZlckFuY2hvcnMoCiAgcmVmZXJlbmNlICAgICAgICAgICAgPSByZWZlcmVuY2VfaW50ZWdyYXRlZCwKICBxdWVyeSAgICAgICAgICAgICAgICA9IE1hbGlnbmFudENENFQsCiAgZmVhdHVyZXMgICAgICAgICAgICAgPSBjb21tb25fZmVhdHVyZXMsCiAgbm9ybWFsaXphdGlvbi5tZXRob2QgPSAiU0NUIiwKICByZWZlcmVuY2UucmVkdWN0aW9uICA9ICJwY2EiLAogIGRpbXMgICAgICAgICAgICAgICAgID0gMTpkaW1zX3RvX3VzZSwKICBrLmFuY2hvciAgICAgICAgICAgICA9IDEwLAogIGsuZmlsdGVyICAgICAgICAgICAgID0gNTAwLAogIGsuc2NvcmUgICAgICAgICAgICAgID0gMzAsCiAgdmVyYm9zZSAgICAgICAgICAgICAgPSBUUlVFCikKCiMg4pSA4pSAIFN0ZXAgNjogQW5jaG9yIHF1YWxpdHkgY2hlY2sg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACiMgTk9URTogSW4gU2V1cmF0IEZpbmRUcmFuc2ZlckFuY2hvcnMgaW50ZXJuYWwgY29udmVudGlvbjoKIyAgIGNlbGwxID0gcmVmZXJlbmNlIGluZGljZXMKIyAgIGNlbGwyID0gcXVlcnkgaW5kaWNlcyAgKE9QUE9TSVRFIHRvIHdoYXQgdmFyaWFibGUgbmFtZXMgc3VnZ2VzdCkKIyBBbHdheXMgdXNlIGNlbGwx4oaScmVmZXJlbmNlIGFuZCBjZWxsMuKGknF1ZXJ5IGZvciBiYXJjb2RlIG1hcHBpbmcuCgphbmNob3JfZGYgPC0gYXMuZGF0YS5mcmFtZShzbG90KGFuY2hvcnMsICJhbmNob3JzIikpCgojIENPUlJFQ1QgYmFyY29kZSBtYXBwaW5nIOKAlCBjZWxsMT1yZWZlcmVuY2UsIGNlbGwyPXF1ZXJ5CnJlZl9iYXJjb2RlcyAgIDwtIGNvbG5hbWVzKHJlZmVyZW5jZV9pbnRlZ3JhdGVkKQpxdWVyeV9iYXJjb2RlcyA8LSBjb2xuYW1lcyhNYWxpZ25hbnRDRDRUKQoKYW5jaG9yX2RmJHJlZl9iYXJjb2RlICAgPC0gcmVmX2JhcmNvZGVzW2FuY2hvcl9kZiRjZWxsMV0KYW5jaG9yX2RmJHF1ZXJ5X2JhcmNvZGUgPC0gcXVlcnlfYmFyY29kZXNbYW5jaG9yX2RmJGNlbGwyXQoKYW5jaG9yX2RmJHF1ZXJ5X2NlbGxsaW5lIDwtIE1hbGlnbmFudENENFQkY2VsbF9saW5lW2FuY2hvcl9kZiRxdWVyeV9iYXJjb2RlXQphbmNob3JfZGYkcmVmX2xhYmVsICAgICAgPC0gcmVmZXJlbmNlX2ludGVncmF0ZWQkcHJlZGljdGVkLmNlbGx0eXBlLmwyW2FuY2hvcl9kZiRyZWZfYmFyY29kZV0KCiMgVmVyaWZ5IG5vIE5BcwpjYXQoIlxuPT09IEJhcmNvZGUgbWFwcGluZyB2YWxpZGF0aW9uID09PVxuIikKY2F0KCJOQSByZWYgYmFyY29kZXMgIDoiLCBzdW0oaXMubmEoYW5jaG9yX2RmJHJlZl9iYXJjb2RlKSksICAgIlxuIikKY2F0KCJOQSBxdWVyeSBiYXJjb2RlczoiLCBzdW0oaXMubmEoYW5jaG9yX2RmJHF1ZXJ5X2JhcmNvZGUpKSwgIlxuIikKY2F0KCJOQSBjZWxsIGxpbmVzICAgIDoiLCBzdW0oaXMubmEoYW5jaG9yX2RmJHF1ZXJ5X2NlbGxsaW5lKSksICJcbiIpCgpuX2FuY2hvcnMgICAgPC0gbnJvdyhhbmNob3JfZGYpCmFuY2hvcl9yYXRpbyA8LSBuY29sKE1hbGlnbmFudENENFQpIC8gbl9hbmNob3JzCmFuY2hvcl9zY29yZXMgPC0gYW5jaG9yX2RmJHNjb3JlCgpjYXQoIlxuPT09IEFuY2hvciBxdWFsaXR5IHN1bW1hcnkgPT09XG4iKQpjYXQoIkFuY2hvcnMgZm91bmQgICAgOiIsIG5fYW5jaG9ycywgIlxuIikKY2F0KCJNYWxpZ25hbnQgY2VsbHMgIDoiLCBuY29sKE1hbGlnbmFudENENFQpLCAiXG4iKQpjYXQoIlJlZmVyZW5jZSBjZWxscyAgOiIsIG5jb2wocmVmZXJlbmNlX2ludGVncmF0ZWQpLCAiXG4iKQpjYXQoc3ByaW50ZigiQ2VsbHMgcGVyIGFuY2hvciA6ICUuMWYgOiAxICAoaWRlYWwg4omkIDg6MSlcbiIsIGFuY2hvcl9yYXRpbykpCmNhdChzcHJpbnRmKCJBbmNob3Igc2NvcmVzICAgIDogbWVhbj0lLjNmIHwgbWVkaWFuPSUuM2YgfCBtaW49JS4zZiB8IG1heD0lLjNmXG4iLAogICAgICAgICAgICBtZWFuKGFuY2hvcl9zY29yZXMpLCBtZWRpYW4oYW5jaG9yX3Njb3JlcyksCiAgICAgICAgICAgIG1pbihhbmNob3Jfc2NvcmVzKSwgIG1heChhbmNob3Jfc2NvcmVzKSkpCgpjYXQoIlxuPT09IEFuY2hvcnMgcGVyIGNlbGwgbGluZSA9PT1cbiIpCnByaW50KHRhYmxlKGFuY2hvcl9kZiRxdWVyeV9jZWxsbGluZSwgdXNlTkEgPSAiaWZhbnkiKSkKCmNhdCgiXG49PT0gQW5jaG9ycyBieSByZWZlcmVuY2UgbGFiZWwgPT09XG4iKQphbmNob3JfZGYgJT4lCiAgZ3JvdXBfYnkocmVmX2xhYmVsKSAlPiUKICBzdW1tYXJpc2Uobl9hbmNob3JzICA9IG4oKSwKICAgICAgICAgICAgbWVhbl9zY29yZSA9IHJvdW5kKG1lYW4oc2NvcmUpLCAzKSwKICAgICAgICAgICAgLmdyb3VwcyA9ICJkcm9wIikgJT4lCiAgYXJyYW5nZShkZXNjKG5fYW5jaG9ycykpICU+JQogIHByaW50KCkKCmlmIChhbmNob3JfcmF0aW8gPiA4KSB7CiAgd2FybmluZyhwYXN0ZTAoCiAgICAi4pqg77iPICBMb3cgYW5jaG9yIGRlbnNpdHkgKCIsIHJvdW5kKGFuY2hvcl9yYXRpbywgMSksICI6MSkuXG4iLAogICAgIiAgIDEuIEp1bmsgZ2VuZXMgc3RpbGwgZG9taW5hdGluZyBQQ0Eg4oCUIGNoZWNrIHRvcCAyMCBmZWF0dXJlcyBhYm92ZVxuIiwKICAgICIgICAyLiBTw6l6YXJ5IGNlbGxzIHRvbyB0cmFuc2NyaXB0aW9uYWxseSBkaXN0YW50IGZyb20gcmVmZXJlbmNlXG4iLAogICAgIiAgIDMuIEluY3JlYXNlIGsuYW5jaG9yIHRvIDE1IGFuZCByZXJ1biIKICApKQp9IGVsc2UgaWYgKGFuY2hvcl9yYXRpbyA+IDUpIHsKICBjYXQoc3ByaW50Zigi4pqg77iPICBNb2RlcmF0ZSBhbmNob3IgZGVuc2l0eSAoJS4xZjoxKSDigJQgYWNjZXB0YWJsZVxuIiwgYW5jaG9yX3JhdGlvKSkKfSBlbHNlIHsKICBjYXQoc3ByaW50Zigi4pyFIEdvb2QgYW5jaG9yIGRlbnNpdHkgKCUuMWY6MSkg4oCUIHByb2plY3Rpb24gcXVhbGl0eSBzaG91bGQgYmUgaGlnaFxuIiwKICAgICAgICAgICAgICBhbmNob3JfcmF0aW8pKQp9CmBgYAoKCiMjIGNoZWNrIHlvdSBjYW4gZG8gYWZ0ZXIgTWFwUXVlcnk6CgpgYGB7ciBjaGVja30KIyDilIDilIAgQU5DSE9SIERJQUdOT1NUSUM6IEJpb2xvZ3kgdnMgQXJ0ZWZhY3Qg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACiMgSWYgTDMtTDcgYXJlIHRydWx5IGRlZGlmZmVyZW50aWF0ZWQsIHRoZWlyIFBDQSBzcGFjZSBzaG91bGQgYmUKIyBESVNUQU5UIGZyb20gdGhlIHJlZmVyZW5jZS4gSWYgaXQgaXMgYSB0ZWNobmljYWwgaXNzdWUsIHRoZXkgd2lsbAojIHNpdCBpbiB0aGUgc2FtZSBQQ0Egc3BhY2UgYXMgTDEvTDIgYnV0IGp1c3Qgbm90IGZvcm0gbXV0dWFsIE5OLgoKY2F0KCI9PT0gUENBIHNwYWNlIGNvbXBhcmlzb246IEwxL0wyIHZzIEwzLUw3ID09PVxuIikKCiMgR2V0IHJlZmVyZW5jZSBQQ0EgY2VudHJvaWQgKFRDTSByZWdpb24pCnJlZl9wY2EgPC0gRW1iZWRkaW5ncyhyZWZlcmVuY2VfaW50ZWdyYXRlZCwgInBjYSIpWywgMToxMF0KcmVmX2NlbnRyb2lkIDwtIGNvbE1lYW5zKHJlZl9wY2EpCgojIEdldCBxdWVyeSBQQ0EgZW1iZWRkaW5ncyBwZXIgbGluZQpxdWVyeV9wY2EgPC0gRW1iZWRkaW5ncyhNYWxpZ25hbnRDRDRULCAicGNhIilbLCAxOjEwXQoKIyBEaXN0YW5jZSBmcm9tIGVhY2ggcXVlcnkgY2VsbCB0byByZWZlcmVuY2UgY2VudHJvaWQKZGlzdF90b19yZWYgPC0gcm93U3VtcygKICBzd2VlcChxdWVyeV9wY2EsIDIsIHJlZl9jZW50cm9pZCwgIi0iKV4yCikKCiMgQ29tcGFyZSBkaXN0YW5jZXMgYnkgY2VsbCBsaW5lCmRpc3RfZGYgPC0gZGF0YS5mcmFtZSgKICBjZWxsICAgICAgID0gY29sbmFtZXMoTWFsaWduYW50Q0Q0VCksCiAgY2VsbF9saW5lICA9IE1hbGlnbmFudENENFQkY2VsbF9saW5lLAogIGRpc3RfdG9fcmVmID0gZGlzdF90b19yZWYKKQoKZGlzdF9zdW1tYXJ5IDwtIGRpc3RfZGYgJT4lCiAgZ3JvdXBfYnkoY2VsbF9saW5lKSAlPiUKICBzdW1tYXJpc2UoCiAgICBtZWFuX2Rpc3QgID0gcm91bmQobWVhbihkaXN0X3RvX3JlZiksICAyKSwKICAgIG1lZGlhbl9kaXN0ID0gcm91bmQobWVkaWFuKGRpc3RfdG9fcmVmKSwgMiksCiAgICAuZ3JvdXBzICAgID0gImRyb3AiCiAgKSAlPiUKICBhcnJhbmdlKG1lYW5fZGlzdCkKCmNhdCgiXG5EaXN0YW5jZSBmcm9tIHJlZmVyZW5jZSBQQ0EgY2VudHJvaWQgcGVyIGNlbGwgbGluZTpcbiIpCmNhdCgiKExvd2VyID0gbW9yZSBzaW1pbGFyIHRvIGhlYWx0aHkgcmVmZXJlbmNlKVxuXG4iKQpwcmludChkaXN0X3N1bW1hcnkpCgojIENoZWNrIEhWRyBvdmVybGFwIHBlciBsaW5lIHdpdGggcmVmZXJlbmNlCmNhdCgiXG49PT0gSFZHIG92ZXJsYXAgd2l0aCByZWZlcmVuY2UgcGVyIGxpbmUgPT09XG4iKQpyZWZfaHZnc19jaGVjayA8LSBWYXJpYWJsZUZlYXR1cmVzKHJlZmVyZW5jZV9pbnRlZ3JhdGVkLCBhc3NheSA9ICJTQ1QiKQpmb3IgKGxuIGluIHBhc3RlMCgiTCIsIDE6NykpIHsKICBsaW5lX2NlbGxzICA8LSBjb2xuYW1lcyhNYWxpZ25hbnRDRDRUKVtNYWxpZ25hbnRDRDRUJGNlbGxfbGluZSA9PSBsbl0KICAjIENoZWNrIGV4cHJlc3Npb24gb2Yga2V5IFQgY2VsbCBtYXJrZXJzIGluIGVhY2ggbGluZQogIG1hcmtlcnMgICAgIDwtIGMoIkNDUjciLCAiVENGNyIsICJTRUxMIiwgIklMN1IiLCAiR1pNQiIsICJUT1giLCAiS0lSM0RMMiIpCiAgbWFya2Vyc19wcmVzZW50IDwtIGludGVyc2VjdChtYXJrZXJzLCByb3duYW1lcyhNYWxpZ25hbnRDRDRUKSkKICBleHByIDwtIHJvd01lYW5zKAogICAgR2V0QXNzYXlEYXRhKE1hbGlnbmFudENENFQsIGFzc2F5ID0gIlNDVCIsIGxheWVyID0gImRhdGEiKVsKICAgICAgbWFya2Vyc19wcmVzZW50LCBsaW5lX2NlbGxzLCBkcm9wID0gRkFMU0VdCiAgKQogIGNhdChzcHJpbnRmKCJcbiVzIOKAlCBrZXkgbWFya2VyIGV4cHJlc3Npb246XG4iLCBsbikpCiAgcHJpbnQocm91bmQoZXhwciwgMykpCn0KYGBgCgoKYGBge3IgY2hlY2syfQojIFNob3cgVE9QIHJlZmVyZW5jZSBzdGF0ZXMgZWFjaCBjZWxsIGxpbmUgY29ubmVjdHMgdG8KY2F0KCJcbj09PSBXSEFUIEVBQ0ggQ0VMTCBMSU5FIEFOQ0hPUlMgVE8gPT09XG4iKQphbmNob3JfZGYgJT4lCiAgZ3JvdXBfYnkocXVlcnlfY2VsbGxpbmUsIHJlZl9sYWJlbCkgJT4lCiAgc3VtbWFyaXNlKAogICAgbl9hbmNob3JzICA9IG4oKSwKICAgIG1lYW5fc2NvcmUgPSByb3VuZChtZWFuKHNjb3JlKSwgMyksCiAgICAuZ3JvdXBzID0gImRyb3AiCiAgKSAlPiUKICBhcnJhbmdlKHF1ZXJ5X2NlbGxsaW5lLCBkZXNjKG5fYW5jaG9ycykpICU+JQogIHByaW50KG4gPSAzMCkgICMgU2hvdyBhbGwgY29tYmluYXRpb25zCgpgYGAKCiMjIE1hcFF1ZXJ5IOKAlCBQcm9qZWN0IG9udG8gUmVmZXJlbmNlIFVNQVAKCmBgYHtyIG1hcC1xdWVyeX0KY2F0KCI9PT0gTWFwUXVlcnkgcHJvamVjdGlvbiA9PT1cbiIpCgojIE1hcFF1ZXJ5IHNpbXVsdGFuZW91c2x5OgojICAgMS4gVHJhbnNmZXJzIGxhYmVscyArIHBzZXVkb3RpbWUgZnJvbSByZWZlcmVuY2UgdG8gcXVlcnkgKFRyYW5zZmVyRGF0YSkKIyAgIDIuIFByb2plY3RzIHF1ZXJ5IFBDQSBpbnRvIHJlZmVyZW5jZSBQQ0Egc3BhY2UgKEludGVncmF0ZUVtYmVkZGluZ3MpCiMgICAzLiBQcm9qZWN0cyBxdWVyeSBvbnRvIGZyb3plbiByZWZlcmVuY2UgVU1BUCAoUHJvamVjdFVNQVApCiMgTWFsaWduYW50IGNlbGxzIGRvIE5PVCBhbHRlciB0aGUgcmVmZXJlbmNlIOKAlCB0aGV5IGFyZSByZWFkLW9ubHkgcGFzc2VuZ2Vycy4KCm1hcHBlZF9NYWxpZ25hbnRDRDRUIDwtIE1hcFF1ZXJ5KAogIGFuY2hvcnNldCAgICAgICAgID0gYW5jaG9ycywKICBxdWVyeSAgICAgICAgICAgICA9IE1hbGlnbmFudENENFQsCiAgcmVmZXJlbmNlICAgICAgICAgPSByZWZlcmVuY2VfaW50ZWdyYXRlZCwKICByZWZkYXRhICAgICAgICAgICA9IGxpc3QoCiAgICBwcmVkaWN0ZWQuY2VsbHR5cGUubDIgPSAicHJlZGljdGVkLmNlbGx0eXBlLmwyIiwgICAjIFBSSU1BUlk6IHN0YXRlIGxhYmVsCiAgICBwc2V1ZG90aW1lICAgICAgICAgICAgPSAibW9ub2NsZTNfcHNldWRvdGltZSIgICAgICAjIENPTlRJTlVPVVM6IHBvc2l0aW9uCiAgKSwgCiAgcmVmZXJlbmNlLnJlZHVjdGlvbiA9ICJwY2EiLAogIHJlZHVjdGlvbi5tb2RlbCAgICAgPSAidW1hcCIKKQoKCmNhdCgi4pyFIE1hcFF1ZXJ5IGNvbXBsZXRlXG4iKQpjYXQoIk1hcHBlZCBjZWxsczoiLCBuY29sKG1hcHBlZF9NYWxpZ25hbnRDRDRUKSwgIlxuIikKCiMgQ29lcmNlIHBzZXVkb3RpbWUgdG8gbnVtZXJpYyDigJQgU2V1cmF0IFRyYW5zZmVyRGF0YSByZXR1cm5zIGNoYXJhY3RlcgptYXBwZWRfTWFsaWduYW50Q0Q0VCRwcmVkaWN0ZWQucHNldWRvdGltZSA8LSBhcy5udW1lcmljKAogIG1hcHBlZF9NYWxpZ25hbnRDRDRUJHByZWRpY3RlZC5wc2V1ZG90aW1lCikKCmNhdCgiXG5UcmFuc2ZlcnJlZCBwc2V1ZG90aW1lIHN1bW1hcnk6XG4iKQpwcmludChzdW1tYXJ5KG1hcHBlZF9NYWxpZ25hbnRDRDRUJHByZWRpY3RlZC5wc2V1ZG90aW1lKSkKY2F0KCJcblRyYW5zZmVycmVkIEF6aW11dGggbDIgZGlzdHJpYnV0aW9uOlxuIikKcHJpbnQodGFibGUobWFwcGVkX01hbGlnbmFudENENFQkcHJlZGljdGVkLnByZWRpY3RlZC5jZWxsdHlwZS5sMikpCgojIEZJWEVEIERpdmVyc2l0eSBjaGVjazogSGFuZGxlIE5BIHByZWRpY3Rpb25zIHByb3Blcmx5CmNhdCgiXG49PT0gRGl2ZXJzaXR5IGNoZWNrID09PVxuIikKbDJfdGFibGUgPC0gdGFibGUobWFwcGVkX01hbGlnbmFudENENFQkcHJlZGljdGVkLnByZWRpY3RlZC5jZWxsdHlwZS5sMiwgdXNlTkEgPSAiYWx3YXlzIikKbDJfZGlzdCA8LSBwcm9wLnRhYmxlKGwyX3RhYmxlKQoKY2F0KCJMYWJlbCBkaXN0cmlidXRpb246XG4iKQpwcmludChsMl90YWJsZSkKCm5hX2NvdW50IDwtIGwyX3RhYmxlWyI8TkE+Il0KbmFfcGN0IDwtIGlmZWxzZShpcy5uYShuYV9jb3VudCksIDAsIHJvdW5kKDEwMCAqIG5hX3BjdCwgMSkpCgojIE5vbi1OQSBwcmVkaWN0aW9ucyBvbmx5Cm5vbl9uYV90YWJsZSA8LSBsMl90YWJsZVshbmFtZXMobDJfdGFibGUpICVpbiUgIjxOQT4iXQppZiAobGVuZ3RoKG5vbl9uYV90YWJsZSkgPiAwKSB7CiAgbm9uX25hX2Rpc3QgPC0gcHJvcC50YWJsZShub25fbmFfdGFibGUpCiAgbWF4X3N0YXRlIDwtIG5hbWVzKHdoaWNoLm1heChub25fbmFfZGlzdCkpCiAgbWF4X3BjdCA8LSByb3VuZCgxMDAgKiBtYXgobm9uX25hX2Rpc3QpLCAxKQogIAogIGNhdChzcHJpbnRmKCJcbk5BIGZyYWN0aW9uOiAlLjFmJSVcbiIsIG5hX3BjdCkpCiAgY2F0KHNwcmludGYoIkRvbWluYW50IHN0YXRlOiAlcyAoJS4xZiUlIG9mIG5vbi1OQSlcbiIsIG1heF9zdGF0ZSwgbWF4X3BjdCkpCiAgCiAgaWYgKG1heF9wY3QgPiA5MCkgewogICAgd2FybmluZyhzcHJpbnRmKCLimqDvuI8gICVzIGRvbWluYXRlcyAoJS4xZiUlKSDigJQgY2hlY2sgU0NUL2ZlYXR1cmUgZmlsdGVyaW5nIiwgbWF4X3N0YXRlLCBtYXhfcGN0KSkKICB9IGVsc2UgewogICAgY2F0KCLinIUgRGl2ZXJzZSBtYXBwaW5nIGFjcm9zcyBzdGF0ZXNcbiIpCiAgfQp9IGVsc2UgewogIGNhdCgi4pqg77iPICBObyBjb25maWRlbnQgcHJlZGljdGlvbnMgbWFkZVxuIikKfQoKYGBgCmBgYHtyIHBsb3QxLCBmaWcud2lkdGg9MTQsIGZpZy5oZWlnaHQ9Nn0KCiMgU8OpemFyeS1zcGVjaWZpYyBkaXZlcnNpdHkgY2hlY2sKZG9taW5hbnRfcGN0IDwtIHJvdW5kKDEwMCAqIG1heChwcm9wLnRhYmxlKG5vbl9uYV90YWJsZSkpLCAxKQppZiAoZG9taW5hbnRfcGN0ID4gOTUpIHsKICBjYXQoc3ByaW50Zigi4pyFIFRDTS1kb21pbmFudCAoJS4xZiUlKSA9IENMQVNTSUMgU8OJWkFSWSBQSEVOT1RZUEVcbiIsIGRvbWluYW50X3BjdCkpCiAgY2F0KCJNaW5vciBzdWJjbG9uZXMgKFRFTS9UcmVnL05haXZlKSA9IGV4cGVjdGVkIGhldGVyb2dlbmVpdHlcbiIpCn0gZWxzZSB7CiAgY2F0KHNwcmludGYoIuKchSBEaXZlcnNlIG1hcHBpbmc6ICVzID0gJS4xZiUlXG4iLCBtYXhfc3RhdGUsIGRvbWluYW50X3BjdCkpCn0KCgpgYGAKCmBgYHtyIHBsb3QyLCBmaWcud2lkdGg9MTQsIGZpZy5oZWlnaHQ9Nn0KCiMgMS4gUGVyLWNlbGwtbGluZSBicmVha2Rvd24gKHlvdXIgdGhlc2lzIGdvbGQpCnRhYmxlKG1hcHBlZF9NYWxpZ25hbnRDRDRUJGNlbGxfbGluZSwgbWFwcGVkX01hbGlnbmFudENENFQkcHJlZGljdGVkLnByZWRpY3RlZC5jZWxsdHlwZS5sMikKCiMgMi4gUGxvdCBvbiB0cmFqZWN0b3J5IFVNQVAKRGltUGxvdChtYXBwZWRfTWFsaWduYW50Q0Q0VCwgZ3JvdXAuYnkgPSAicHJlZGljdGVkLnByZWRpY3RlZC5jZWxsdHlwZS5sMiIsIHJlZHVjdGlvbiA9ICJyZWYudW1hcCIpCgojIDMuIFBzZXVkb3RpbWUgdmlvbGluIGJ5IGNlbGwgbGluZQpWbG5QbG90KG1hcHBlZF9NYWxpZ25hbnRDRDRULCBmZWF0dXJlcyA9ICJwcmVkaWN0ZWQucHNldWRvdGltZSIsIGdyb3VwLmJ5ID0gImNlbGxfbGluZSIsIHB0LnNpemUgPSAwKQoKCmBgYAoKCiMgVmlzdWFsaXNlIFByb2plY3Rpb24KCiMjIFJlZmVyZW5jZSArIE1hbGlnbmFudCBPdmVybGF5CgpgYGB7ciBwbG90LW92ZXJsYXksIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD01fQojIEJ1aWxkIGRhdGEgZnJhbWVzIGZvciBnZ3Bsb3Qg4oCUIHJlbmFtZSBVTUFQIGNvbHVtbnMgY29uc2lzdGVudGx5CnJlZl91bWFwIDwtIGRhdGEuZnJhbWUoCiAgRW1iZWRkaW5ncyhyZWZlcmVuY2VfaW50ZWdyYXRlZCwgInVtYXAiKSwKICBsMiAgICAgICAgID0gcmVmZXJlbmNlX2ludGVncmF0ZWQkcHJlZGljdGVkLmNlbGx0eXBlLmwyLAogIHBzZXVkb3RpbWUgPSByZWZlcmVuY2VfaW50ZWdyYXRlZCRtb25vY2xlM19wc2V1ZG90aW1lLAogIHR5cGUgICAgICAgPSAiUmVmZXJlbmNlIgopCmNvbG5hbWVzKHJlZl91bWFwKVsxOjJdIDwtIGMoIlVNQVBfMSIsICJVTUFQXzIiKQoKcXVlcnlfdW1hcCA8LSBkYXRhLmZyYW1lKAogIEVtYmVkZGluZ3MobWFwcGVkX01hbGlnbmFudENENFQsICJyZWYudW1hcCIpLAogIHBzZXVkb3RpbWUgPSBtYXBwZWRfTWFsaWduYW50Q0Q0VCRwcmVkaWN0ZWQucHNldWRvdGltZSwKICBjZWxsX2xpbmUgID0gbWFwcGVkX01hbGlnbmFudENENFQkY2VsbF9saW5lLAogIGwyX3EgICAgICAgPSBtYXBwZWRfTWFsaWduYW50Q0Q0VCRwcmVkaWN0ZWQucHJlZGljdGVkLmNlbGx0eXBlLmwyLAogIHR5cGUgICAgICAgPSAiTWFsaWduYW50IgopCmNvbG5hbWVzKHF1ZXJ5X3VtYXApWzE6Ml0gPC0gYygiVU1BUF8xIiwgIlVNQVBfMiIpCgpjZWxsX2xpbmVfcGFsZXR0ZSA8LSBjb2xvclJhbXBQYWxldHRlKGJyZXdlci5wYWwoOCwgIkRhcmsyIikpKAogIGxlbmd0aCh1bmlxdWUocXVlcnlfdW1hcCRjZWxsX2xpbmUpKSkKbmFtZXMoY2VsbF9saW5lX3BhbGV0dGUpIDwtIHNvcnQodW5pcXVlKHF1ZXJ5X3VtYXAkY2VsbF9saW5lKSkKCmFsbF9sMl9sYWJlbHMgICAgIDwtIHVuaXF1ZShjKGFzLmNoYXJhY3RlcihyZWZfdW1hcCRsMiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcy5jaGFyYWN0ZXIocXVlcnlfdW1hcCRsMl9xKSkpCmF6aW11dGhfbDJfY29sb3JzIDwtIGV4dGVuZF9wYWxldHRlKGF6aW11dGhfbDJfY29sb3JzLCBhbGxfbDJfbGFiZWxzKQoKIyBQYW5lbCAxOiBtYWxpZ25hbnQgY29sb3VyZWQgYnkgdHJhbnNmZXJyZWQgcHNldWRvdGltZQpwX3B0IDwtIGdncGxvdCgpICsKICBnZW9tX3BvaW50KGRhdGEgPSByZWZfdW1hcCwKICAgICAgICAgICAgIGFlcyh4ID0gVU1BUF8xLCB5ID0gVU1BUF8yKSwKICAgICAgICAgICAgIGNvbG9yID0gcmVmZXJlbmNlX2NvbG9yLCBzaXplID0gMC40LCBhbHBoYSA9IDAuNikgKwogIGdlb21fcG9pbnQoZGF0YSA9IHF1ZXJ5X3VtYXAgJT4lIGZpbHRlcihpcy5maW5pdGUocHNldWRvdGltZSkpLAogICAgICAgICAgICAgYWVzKHggPSBVTUFQXzEsIHkgPSBVTUFQXzIsIGNvbG9yID0gcHNldWRvdGltZSksCiAgICAgICAgICAgICBzaXplID0gMS4yLCBhbHBoYSA9IDAuOSkgKwogIHNjYWxlX2NvbG9yX3ZpcmlkaXNfYyhvcHRpb24gPSAicGxhc21hIiwgbmFtZSA9ICJQc2V1ZG90aW1lIiwKICAgICAgICAgICAgICAgICAgICAgICAgbmEudmFsdWUgPSAiZ3JleTQwIikgKwogIHRoZW1lX2NsYXNzaWMoYmFzZV9zaXplID0gMTEpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiKSkgKwogIGdndGl0bGUoIlPDqXphcnkgQ2VsbHMgUHJvamVjdGVkIG9udG8gUmVmZXJlbmNlXG4oVHJhbnNmZXJyZWQgcHNldWRvdGltZSkiKQoKIyBQYW5lbCAyOiByZWZlcmVuY2UgY29sb3VyZWQgYnkgc3RhdGUsIG1hbGlnbmFudCBpbiByZWQKcF9zdGF0ZSA8LSBnZ3Bsb3QoKSArCiAgZ2VvbV9wb2ludChkYXRhID0gcmVmX3VtYXAsCiAgICAgICAgICAgICBhZXMoeCA9IFVNQVBfMSwgeSA9IFVNQVBfMiwgY29sb3IgPSBsMiksCiAgICAgICAgICAgICBzaXplID0gMC40LCBhbHBoYSA9IDAuNykgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBhemltdXRoX2wyX2NvbG9ycywKICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICJBemltdXRoIGwyXG4ocmVmZXJlbmNlKSIsIG5hLnZhbHVlID0gImdyZXk2MCIpICsKICBnZW9tX3BvaW50KGRhdGEgPSBxdWVyeV91bWFwLAogICAgICAgICAgICAgYWVzKHggPSBVTUFQXzEsIHkgPSBVTUFQXzIpLAogICAgICAgICAgICAgY29sb3IgPSBtYWxpZ25hbnRfY29sb3IsIHNpemUgPSAxLjAsIGFscGhhID0gMC44KSArCiAgdGhlbWVfY2xhc3NpYyhiYXNlX3NpemUgPSAxMSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgIGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC40LCAiY20iKSwKICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gOCkpICsKICBnZ3RpdGxlKCJSZWZlcmVuY2UgU3RhdGVzIChjb2xvdXIpXG4rIFPDqXphcnkgQ2VsbHMgKHJlZCBvdmVybGF5KSIpCgpwX3B0IHwgcF9zdGF0ZQoKYGBgCgojIyBUcmFuc2ZlcnJlZCBMYWJlbHMgKyBQZXIgQ2VsbCBMaW5lCgpgYGB7ciBwbG90LWxhYmVscy1saW5lcywgZmlnLndpZHRoPTE0LCBmaWcuaGVpZ2h0PTZ9CnBfbDIgPC0gZ2dwbG90KCkgKwogIGdlb21fcG9pbnQoZGF0YSA9IHJlZl91bWFwLAogICAgICAgICAgICAgYWVzKHggPSBVTUFQXzEsIHkgPSBVTUFQXzIpLAogICAgICAgICAgICAgY29sb3IgPSByZWZlcmVuY2VfY29sb3IsIHNpemUgPSAwLjQsIGFscGhhID0gMC42KSArCiAgZ2VvbV9wb2ludChkYXRhID0gcXVlcnlfdW1hcCwKICAgICAgICAgICAgIGFlcyh4ID0gVU1BUF8xLCB5ID0gVU1BUF8yLCBjb2xvciA9IGwyX3EpLAogICAgICAgICAgICAgc2l6ZSA9IDEuMiwgYWxwaGEgPSAwLjkpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYXppbXV0aF9sMl9jb2xvcnMsCiAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiQXppbXV0aCBsMlxuKHRyYW5zZmVycmVkKSIsIG5hLnZhbHVlID0gImdyZXk0MCIpICsKICB0aGVtZV9jbGFzc2ljKGJhc2Vfc2l6ZSA9IDExKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgZmFjZSA9ICJib2xkIikpICsKICBnZ3RpdGxlKCJTw6l6YXJ5IENlbGxzIOKAlCBUcmFuc2ZlcnJlZCBBemltdXRoIGwyIExhYmVscyIpCgpwX2xpbmVzIDwtIGdncGxvdCgpICsKICBnZW9tX3BvaW50KGRhdGEgPSByZWZfdW1hcCwKICAgICAgICAgICAgIGFlcyh4ID0gVU1BUF8xLCB5ID0gVU1BUF8yKSwKICAgICAgICAgICAgIGNvbG9yID0gcmVmZXJlbmNlX2NvbG9yLCBzaXplID0gMC40LCBhbHBoYSA9IDAuNSkgKwogIGdlb21fcG9pbnQoZGF0YSA9IHF1ZXJ5X3VtYXAsCiAgICAgICAgICAgICBhZXMoeCA9IFVNQVBfMSwgeSA9IFVNQVBfMiwgY29sb3IgPSBjZWxsX2xpbmUpLAogICAgICAgICAgICAgc2l6ZSA9IDEuMCwgYWxwaGEgPSAwLjkpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gY2VsbF9saW5lX3BhbGV0dGUsIG5hbWUgPSAiQ2VsbCBsaW5lIikgKwogIHRoZW1lX2NsYXNzaWMoYmFzZV9zaXplID0gMTEpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiKSkgKwogIGdndGl0bGUoIlPDqXphcnkgQ2VsbHMgcGVyIENlbGwgTGluZSIpCgpwX2wyIHwgcF9saW5lcwpgYGAKCiMjIFBlciBDZWxsIExpbmUgRmFjZXQKCmBgYHtyIHBsb3QtZmFjZXQsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD02fQpnZ3Bsb3QoKSArCiAgZ2VvbV9wb2ludChkYXRhID0gcmVmX3VtYXAsCiAgICAgICAgICAgICBhZXMoeCA9IFVNQVBfMSwgeSA9IFVNQVBfMiksCiAgICAgICAgICAgICBjb2xvciA9ICJncmV5NjAiLCBzaXplID0gMC4zLCBhbHBoYSA9IDAuNSkgKwogIGdlb21fcG9pbnQoZGF0YSA9IHF1ZXJ5X3VtYXAgJT4lIGZpbHRlcihpcy5maW5pdGUocHNldWRvdGltZSkpLAogICAgICAgICAgICAgYWVzKHggPSBVTUFQXzEsIHkgPSBVTUFQXzIsIGNvbG9yID0gcHNldWRvdGltZSksCiAgICAgICAgICAgICBzaXplID0gMC45LCBhbHBoYSA9IDAuODUpICsKICBmYWNldF93cmFwKH4gY2VsbF9saW5lLCBuY29sID0gNCkgKwogIHNjYWxlX2NvbG9yX3ZpcmlkaXNfYyhvcHRpb24gPSAicGxhc21hIiwgbmFtZSA9ICJQc2V1ZG90aW1lIiwKICAgICAgICAgICAgICAgICAgICAgICAgbmEudmFsdWUgPSAiZ3JleTYwIikgKwogIHRoZW1lX2NsYXNzaWMoYmFzZV9zaXplID0gMTApICsKICB0aGVtZShzdHJpcC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSA5LCBmYWNlID0gImJvbGQiKSwKICAgICAgICBzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAiI0U4RjRGRCIpLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIpKSArCiAgZ2d0aXRsZSgiUGVyIENlbGwgTGluZSDigJQgUHJvamVjdGlvbiBvbnRvIFJlZmVyZW5jZSBVTUFQIChQc2V1ZG90aW1lKSIpCmBgYAoKCiMgUXVhbnRpZmljYXRpb24g4oCUIFN0YXRlIEFzc2lnbm1lbnQgd2l0aCBCaW9sb2d5LUFuY2hvcmVkIEJpbnMKCiMjIEFzc2lnbiBFYWNoIE1hbGlnbmFudCBDZWxsIHRvIGEgUmVmZXJlbmNlIFN0YXRlCgpgYGB7ciBzdGF0ZS1hc3NpZ25tZW50fQojIOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkAojIFBTRVVET1RJTUUgQklOIEFTU0lHTk1FTlQg4oCUIFNUQVRFLUFOQ0hPUkVEIEJPVU5EQVJJRVMKIyDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZAKIwojIEJpbiBib3VuZGFyaWVzID0gbWlkcG9pbnQgYmV0d2VlbiBhZGphY2VudCByZWZlcmVuY2Ugc3RhdGUgbWVkaWFucy4KIyBUaGlzIGlzIGJpb2xvZ2ljYWxseSBwcmluY2lwbGVkOiB0aGUgYm91bmRhcnkgYmV0d2VlbiBUQ00gYW5kIFRFTSBiaW5zCiMgaXMgZXhhY3RseSBoYWxmd2F5IGJldHdlZW4gdGhlIG1lZGlhbiBUQ00gcHNldWRvdGltZSBhbmQgbWVkaWFuIFRFTQojIHBzZXVkb3RpbWUgaW4gdGhlIGhlYWx0aHkgcmVmZXJlbmNlIOKAlCBOT1QgYW4gYXJiaXRyYXJ5IDMzcmQvNjZ0aAojIHBlcmNlbnRpbGUgY3V0LgojCiMgVFdPIExJTkVBR0VTIG1vZGVsbGVkOgojICAgRWZmZWN0b3IgYXhpcyAocHNldWRvdGltZS1iYXNlZCk6CiMgICAgIE5haXZlLWxpa2UgIOKGkiBiZWxvdyBtaWRwb2ludChOYWl2ZSwgVENNKQojICAgICBUQ00tbGlrZSAgICDihpIgYmV0d2VlbiBtaWRwb2ludChOYWl2ZSxUQ00pIGFuZCBtaWRwb2ludChUQ00sVEVNKQojICAgICBURU0tbGlrZSAgICDihpIgYmV0d2VlbiBtaWRwb2ludChUQ00sVEVNKSBhbmQgbWlkcG9pbnQoVEVNLFRlbXJhKQojICAgICBUZW1yYS1saWtlICDihpIgYWJvdmUgbWlkcG9pbnQoVEVNLFRlbXJhKQojCiMgICBSZWd1bGF0b3J5IGJyYW5jaCAobGFiZWwtYmFzZWQpOgojICAgICBUcmVnLWxpa2UgICDihpIgdHJhbnNmZXJyZWQgQXppbXV0aCBsYWJlbCA9PSAiVHJlZyIgYXQgQU5ZIHBzZXVkb3RpbWUKIyAgICAgUkFUSU9OQUxFOiBUcmVnIGlzIGEgYnJhbmNoIGZyb20gVENNLCBub3QgYSBsaW5lYXIgcG9zaXRpb24gb24gdGhlCiMgICAgIGVmZmVjdG9yIGF4aXMuIFBzZXVkb3RpbWUgZm9yIFRyZWcgY2VsbHMgaXMgbG93ZXIgdGhhbiBURU0gYnkgZGVzaWduCiMgICAgIChjb3JyZWN0IHRvcG9sb2d5KSwgYnV0IHVzaW5nIHBzZXVkb3RpbWUgYWxvbmUgd291bGQgbWlzYXNzaWduIHNvbWUKIyAgICAgVHJlZyBjZWxscyB0byBUQ00tbGlrZS4gTGFiZWwgKyBicmFuY2ggbG9naWMgaXMgbW9yZSBhY2N1cmF0ZS4KIyAgICAgSU1QT1JUQU5UOiBUaGlzIG1lYW5zIFRyZWctbGlrZSBiaW4gJSB3aWxsIGVxdWFsIFRyZWcgbGFiZWwgJSBleGFjdGx5LgojICAgICBUaGUgbGFiZWwgdnMgcHNldWRvdGltZSBiaW4gY29tcGFyaXNvbiBpcyBvbmx5IG1lYW5pbmdmdWwgZm9yIHRoZQojICAgICBlZmZlY3RvciBheGlzIChOYWl2ZS9UQ00vVEVNL1RlbXJhKS4KCiMg4pSA4pSAIFN0ZXAgMTogUmVmZXJlbmNlIHN0YXRlIG1lZGlhbiBwc2V1ZG90aW1lcyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKIyBUaGVzZSB3ZXJlIGFscmVhZHkgY29tcHV0ZWQgaW4gcHNldWRvdGltZS1yb290IGNodW5rIChuYWl2ZV9tZWQsIHRjbV9tZWQKIyBldGMuKSBidXQgd2UgcmVjb21wdXRlIGhlcmUgZm9yIGNsYXJpdHkgYW5kIGNodW5rIGluZGVwZW5kZW5jZS4KcHRfc3RhdGVfbWVkaWFucyA8LSByZWZlcmVuY2VfaW50ZWdyYXRlZEBtZXRhLmRhdGEgJT4lCiAgZmlsdGVyKGlzLmZpbml0ZShtb25vY2xlM19wc2V1ZG90aW1lKSkgJT4lCiAgZ3JvdXBfYnkocHJlZGljdGVkLmNlbGx0eXBlLmwyKSAlPiUKICBzdW1tYXJpc2UoCiAgICBuICAgICAgPSBuKCksCiAgICBtZWRfcHQgPSByb3VuZChtZWRpYW4obW9ub2NsZTNfcHNldWRvdGltZSwgbmEucm0gPSBUUlVFKSwgMyksCiAgICAuZ3JvdXBzID0gImRyb3AiCiAgKSAlPiUKICBhcnJhbmdlKG1lZF9wdCkKCmNhdCgiPT09IFJlZmVyZW5jZSBzdGF0ZSBtZWRpYW4gcHNldWRvdGltZXMgPT09XG4iKQpwcmludChwdF9zdGF0ZV9tZWRpYW5zKQoKIyBFeHRyYWN0IG1lZGlhbnMgKG92ZXJ3cml0ZSB3aXRoIGZyZXNoIHZhbHVlcyBmcm9tIHRoaXMgY2h1bmspCm5haXZlX21lZCA8LSBwdF9zdGF0ZV9tZWRpYW5zJG1lZF9wdFsKICBwdF9zdGF0ZV9tZWRpYW5zJHByZWRpY3RlZC5jZWxsdHlwZS5sMiA9PSAiQ0Q0IE5haXZlIl0KdGNtX21lZCAgIDwtIHB0X3N0YXRlX21lZGlhbnMkbWVkX3B0WwogIHB0X3N0YXRlX21lZGlhbnMkcHJlZGljdGVkLmNlbGx0eXBlLmwyID09ICJDRDQgVENNIl0KdHJlZ19tZWQgIDwtIHB0X3N0YXRlX21lZGlhbnMkbWVkX3B0WwogIHB0X3N0YXRlX21lZGlhbnMkcHJlZGljdGVkLmNlbGx0eXBlLmwyID09ICJUcmVnIl0KdGVtX21lZCAgIDwtIHB0X3N0YXRlX21lZGlhbnMkbWVkX3B0WwogIHB0X3N0YXRlX21lZGlhbnMkcHJlZGljdGVkLmNlbGx0eXBlLmwyID09ICJDRDQgVEVNIl0KdGVtcmFfbWVkIDwtIHB0X3N0YXRlX21lZGlhbnMkbWVkX3B0WwogIHB0X3N0YXRlX21lZGlhbnMkcHJlZGljdGVkLmNlbGx0eXBlLmwyID09ICJDRDQgVGVtcmEvQ1RMIl0KCiMg4pSA4pSAIFN0ZXAgMjogQmluIGJvdW5kYXJpZXMgYXMgbWlkcG9pbnRzIGJldHdlZW4gc3RhdGUgbWVkaWFucyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKbmFpdmVfdGNtX2N1dCA8LSAobmFpdmVfbWVkICsgdGNtX21lZCkgIC8gMgp0Y21fdGVtX2N1dCAgIDwtICh0Y21fbWVkICArIHRlbV9tZWQpICAgLyAyCnRlbV90ZW1yYV9jdXQgPC0gKHRlbV9tZWQgICsgdGVtcmFfbWVkKSAvIDIKdGNtX3RyZWdfY3V0ICA8LSAodGNtX21lZCAgKyB0cmVnX21lZCkgIC8gMiAgIyBzdG9yZWQgZm9yIHJlZmVyZW5jZSBvbmx5CgpjYXQoIlxuPT09IEJpb2xvZ3ktYW5jaG9yZWQgYmluIGJvdW5kYXJpZXMgPT09XG4iKQpjYXQoc3ByaW50ZigiICBOYWl2ZSAgfCBUQ00gIDogJS4zZlxuIiwgbmFpdmVfdGNtX2N1dCkpCmNhdChzcHJpbnRmKCIgIFRDTSAgICB8IFRFTSAgOiAlLjNmICAobWFpbiBlZmZlY3RvciBheGlzKVxuIiwgdGNtX3RlbV9jdXQpKQpjYXQoc3ByaW50ZigiICBURU0gICAgfCBUZW1yYTogJS4zZlxuIiwgdGVtX3RlbXJhX2N1dCkpCmNhdChzcHJpbnRmKCIgIFRDTSAgICB8IFRyZWcgOiAlLjNmICAocmVndWxhdG9yeSBicmFuY2gg4oCUIGxhYmVsLWJhc2VkLCBub3QgdXNlZCBhcyBjdXQpXG4iLAogICAgICAgICAgICB0Y21fdHJlZ19jdXQpKQoKIyDilIDilIAgU3RlcCAzOiBBc3NpZ24gd29ya2luZyBjb2x1bW5zIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgAptYXBwZWRfTWFsaWduYW50Q0Q0VCRzdGF0ZV9hemltdXRoX2wyICA8LQogIG1hcHBlZF9NYWxpZ25hbnRDRDRUJHByZWRpY3RlZC5wcmVkaWN0ZWQuY2VsbHR5cGUubDIKCm1hcHBlZF9NYWxpZ25hbnRDRDRUJHBzZXVkb3RpbWVfdmFsdWUgIDwtCiAgYXMubnVtZXJpYyhtYXBwZWRfTWFsaWduYW50Q0Q0VCRwcmVkaWN0ZWQucHNldWRvdGltZSkKCiMg4pSA4pSAIFN0ZXAgNDogQXNzaWduIHBzZXVkb3RpbWUgYmlucyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKbWFwcGVkX01hbGlnbmFudENENFQkcHNldWRvdGltZV9iaW4gPC0gY2FzZV93aGVuKAoKICAjIFRyZWcgbGluZWFnZSDigJQgbGFiZWwgdGFrZXMgcHJpb3JpdHkgb3ZlciBwc2V1ZG90aW1lIHBvc2l0aW9uCiAgIyBiZWNhdXNlIFRyZWcgaXMgYSBicmFuY2gsIG5vdCBhIHBvaW50IG9uIHRoZSBsaW5lYXIgZWZmZWN0b3IgYXhpcwogIG1hcHBlZF9NYWxpZ25hbnRDRDRUJHN0YXRlX2F6aW11dGhfbDIgPT0gIlRyZWciIH4gIlRyZWctbGlrZSIsCgogICMgRWZmZWN0b3IgYXhpcyBiaW5zIOKAlCBwc2V1ZG90aW1lLWJhc2VkCiAgbWFwcGVkX01hbGlnbmFudENENFQkcHNldWRvdGltZV92YWx1ZSAgPCAgbmFpdmVfdGNtX2N1dCB+ICJOYWl2ZS1saWtlIiwKCiAgbWFwcGVkX01hbGlnbmFudENENFQkcHNldWRvdGltZV92YWx1ZSA+PSBuYWl2ZV90Y21fY3V0ICYKICBtYXBwZWRfTWFsaWduYW50Q0Q0VCRwc2V1ZG90aW1lX3ZhbHVlIDwgIHRjbV90ZW1fY3V0ICAgfiAiVENNLWxpa2UiLAoKICBtYXBwZWRfTWFsaWduYW50Q0Q0VCRwc2V1ZG90aW1lX3ZhbHVlID49IHRjbV90ZW1fY3V0ICAgJgogIG1hcHBlZF9NYWxpZ25hbnRDRDRUJHBzZXVkb3RpbWVfdmFsdWUgPCAgdGVtX3RlbXJhX2N1dCB+ICJURU0tbGlrZSIsCgogIG1hcHBlZF9NYWxpZ25hbnRDRDRUJHBzZXVkb3RpbWVfdmFsdWUgPj0gdGVtX3RlbXJhX2N1dCB+ICJUZW1yYS1saWtlIiwKCiAgVFJVRSB+IE5BX2NoYXJhY3Rlcl8KKQoKIyBTZXQgZmFjdG9yIGxldmVscyBpbiBiaW9sb2dpY2FsIHRyYWplY3Rvcnkgb3JkZXIKbWFwcGVkX01hbGlnbmFudENENFQkcHNldWRvdGltZV9iaW4gPC0gZmFjdG9yKAogIG1hcHBlZF9NYWxpZ25hbnRDRDRUJHBzZXVkb3RpbWVfYmluLAogIGxldmVscyA9IGMoIk5haXZlLWxpa2UiLCAiVENNLWxpa2UiLCAiVHJlZy1saWtlIiwgIlRFTS1saWtlIiwgIlRlbXJhLWxpa2UiKQopCgojIOKUgOKUgCBTdGVwIDU6IFRyZWcgY291bnQgY2hlY2sg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACiMgRklYOiBhZGRlZCBhcyBpZGVudGlmaWVkIGluIGV4cGVydCByZXZpZXcuIElmIDw1MCBUcmVnLWxhYmVsbGVkIGNlbGxzLAojIHRoZSBUcmVnLWxpa2UgYmluIGlzIG5lZ2xpZ2libGUgYW5kIHNob3VsZCBiZSBmbGFnZ2VkLgpuX3RyZWcgPC0gc3VtKG1hcHBlZF9NYWxpZ25hbnRDRDRUJHN0YXRlX2F6aW11dGhfbDIgPT0gIlRyZWciLCBuYS5ybSA9IFRSVUUpCmNhdChzcHJpbnRmKCJcblRyZWctbGFiZWxsZWQgbWFsaWduYW50IGNlbGxzOiAlZCAoJS4yZiUlIG9mIHRvdGFsKVxuIiwKICAgICAgICAgICAgbl90cmVnLAogICAgICAgICAgICAxMDAgKiBuX3RyZWcgLyBuY29sKG1hcHBlZF9NYWxpZ25hbnRDRDRUKSkpCgppZiAobl90cmVnID09IDApIHsKICBtZXNzYWdlKCLihLnvuI8gIE5vIFRyZWctbGFiZWxsZWQgY2VsbHMg4oCUIFRyZWctbGlrZSBiaW4gd2lsbCBiZSBlbXB0eS5cbiIsCiAgICAgICAgICAiICAgVGhpcyBpcyBiaW9sb2dpY2FsbHkgcGxhdXNpYmxlOiBTw6l6YXJ5IGNlbGxzIHJhcmVseSBtYXAgdG8gVHJlZy5cbiIsCiAgICAgICAgICAiICAgVGhlIFRyZWcgYmluIGNvbHVtbiBpcyByZXRhaW5lZCBmb3IgY29tcGxldGVuZXNzLiIpCn0gZWxzZSBpZiAobl90cmVnIDwgNTApIHsKICBtZXNzYWdlKHNwcmludGYoCiAgICAi4pqg77iPICBPbmx5ICVkIFRyZWctbGFiZWxsZWQgY2VsbHMgKCUuMmYlJSkuXG4iLCBuX3RyZWcsCiAgICAxMDAgKiBuX3RyZWcgLyBuY29sKG1hcHBlZF9NYWxpZ25hbnRDRDRUKSksCiAgICAiICAgVHJlZy1saWtlIGJpbiBleGlzdHMgYnV0IGlzIHZlcnkgc21hbGwg4oCUIGludGVycHJldCB3aXRoIGNhdXRpb24uXG4iLAogICAgIiAgIENvbnNpZGVyIG5vdGluZyB0aGlzIGFzICdyYXJlL2Fic2VudCBUcmVnIGlkZW50aXR5JyBpbiByZXN1bHRzLiIpCn0gZWxzZSB7CiAgY2F0KHNwcmludGYoIuKchSBUcmVnLWxpa2UgYmluIGhhcyAlZCBjZWxscyDigJQgc3VmZmljaWVudCBmb3IgcmVwb3J0aW5nLlxuIiwgbl90cmVnKSkKfQoKY2F0KCJcbk5vdGU6IFRyZWctbGlrZSBiaW4gJSBlcXVhbHMgVHJlZyBsYWJlbCAlIGJ5IGRlc2lnbiAobGFiZWwtYmFzZWQgYXNzaWdubWVudCkuXG4iKQpjYXQoIkxhYmVsIHZzIHBzZXVkb3RpbWUgYmluIGRpc2NvcmRhbmNlIGlzIG9ubHkgaW50ZXJwcmV0YWJsZSBmb3IgdGhlXG4iKQpjYXQoImVmZmVjdG9yIGF4aXM6IE5haXZlLWxpa2UgLyBUQ00tbGlrZSAvIFRFTS1saWtlIC8gVGVtcmEtbGlrZS5cbiIpCgojIOKUgOKUgCBTdGVwIDY6IFJlc3VsdHMg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACmNhdCgiXG49PT0gU3RhdGUgYXNzaWdubWVudCBjb21wbGV0ZSA9PT1cbiIpCmNhdCgiXG5BemltdXRoIGwyIGxhYmVsIChjZWxsIG9mIG9yaWdpbiDigJQgV0hBVCB0aGUgY2VsbCByZXNlbWJsZXMpOlxuIikKcHJpbnQodGFibGUobWFwcGVkX01hbGlnbmFudENENFQkc3RhdGVfYXppbXV0aF9sMikpCgpjYXQoIlxuUHNldWRvdGltZSBiaW4gKGRpZmZlcmVudGlhdGlvbiBwb3NpdGlvbiDigJQgV0hFUkUgdGhlIGNlbGwgc2l0cyk6XG4iKQpwcmludCh0YWJsZShtYXBwZWRfTWFsaWduYW50Q0Q0VCRwc2V1ZG90aW1lX2JpbikpCgojIOKUgOKUgCBLRVkgUkVTVUxUOiBjcm9zcy10YWJsZSDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKY2F0KCJcbuKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkFxuIikKY2F0KCJLRVkgUkVTVUxUOiBMYWJlbCAoY2VsbCBvZiBvcmlnaW4pIHZzIFBzZXVkb3RpbWUgQmluXG4iKQpjYXQoIuKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkFxuIikKY2F0KCJUQ00tbGFiZWxsZWQgY2VsbHMgaW4gVEVNL1RlbXJhIGJpbnMgPSBlZmZlY3RvciBza2V3aW5nXG4iKQpjYXQoImJleW9uZCB0aGUgVENNIGFycmVzdCBwb2ludFxuXG4iKQoKIyBGSVg6IGV4cGxpY2l0IGNvbHVtbiBuYW1pbmcgZnJvbSB0YWJsZSgpIG91dHB1dCDigJQgYXZvaWRzIHJlbmFtZSgpIGZhaWx1cmUKIyB3aGVuIFZhcjEvVmFyMiBjb2x1bW4gbmFtZXMgZGlmZmVyIGZyb20gZXhwZWN0ZWQgTGFiZWwvUFRfQmluCmNyb3NzX3RhYiA8LSB0YWJsZSgKICBMYWJlbCAgPSBtYXBwZWRfTWFsaWduYW50Q0Q0VCRzdGF0ZV9hemltdXRoX2wyLAogIFBUX0JpbiA9IG1hcHBlZF9NYWxpZ25hbnRDRDRUJHBzZXVkb3RpbWVfYmluCikKcHJpbnQoY3Jvc3NfdGFiKQpjYXQoIlxuTm90ZTogVHJlZyByb3cgd2lsbCBzaG93IDEwMCUgVHJlZy1saWtlIOKAlCB0aGlzIGlzIGJ5IGRlc2lnbi5cbiIpCmBgYAoKIyMgUXVhbnRpZmljYXRpb24gVGFibGVzCgpgYGB7ciBxdWFudGlmeS1zdGF0ZXN9CiMg4pSA4pSAIE92ZXJhbGwgc3RhdGUgZGlzdHJpYnV0aW9uIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgApzdGF0ZV9zdW1tYXJ5IDwtIG1hcHBlZF9NYWxpZ25hbnRDRDRUQG1ldGEuZGF0YSAlPiUKICBjb3VudChzdGF0ZV9hemltdXRoX2wyLCBuYW1lID0gIm5fY2VsbHMiKSAlPiUKICBtdXRhdGUocGN0ID0gcm91bmQoMTAwICogbl9jZWxscyAvIHN1bShuX2NlbGxzKSwgMSkpICU+JQogIGFycmFuZ2UoZGVzYyhuX2NlbGxzKSkKCmNhdCgiPT09IE92ZXJhbGwgQXppbXV0aCBsMiBTdGF0ZSBEaXN0cmlidXRpb24gPT09XG4iKQpwcmludChzdGF0ZV9zdW1tYXJ5KQoKIyDilIDilIAgUGVyIGNlbGwgbGluZSDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKc3RhdGVfcGVyX2xpbmUgPC0gbWFwcGVkX01hbGlnbmFudENENFRAbWV0YS5kYXRhICU+JQogIGNvdW50KGNlbGxfbGluZSwgc3RhdGVfYXppbXV0aF9sMiwgbmFtZSA9ICJuX2NlbGxzIikgJT4lCiAgZ3JvdXBfYnkoY2VsbF9saW5lKSAlPiUKICBtdXRhdGUocGN0ID0gcm91bmQoMTAwICogbl9jZWxscyAvIHN1bShuX2NlbGxzKSwgMSkpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBhcnJhbmdlKGNlbGxfbGluZSwgZGVzYyhuX2NlbGxzKSkKCmNhdCgiXG49PT0gU3RhdGUgRGlzdHJpYnV0aW9uIHBlciBDZWxsIExpbmUgPT09XG4iKQpwcmludChzdGF0ZV9wZXJfbGluZSwgbiA9IEluZikKCiMg4pSA4pSAIFBzZXVkb3RpbWUgYmluIGRpc3RyaWJ1dGlvbiDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKcHRfYmluX3N1bW1hcnkgPC0gbWFwcGVkX01hbGlnbmFudENENFRAbWV0YS5kYXRhICU+JQogIGZpbHRlcighaXMubmEocHNldWRvdGltZV9iaW4pKSAlPiUKICBjb3VudChwc2V1ZG90aW1lX2JpbiwgbmFtZSA9ICJuX2NlbGxzIikgJT4lCiAgbXV0YXRlKHBjdCA9IHJvdW5kKDEwMCAqIG5fY2VsbHMgLyBzdW0obl9jZWxscyksIDEpKQoKY2F0KCJcbj09PSBQc2V1ZG90aW1lIEJpbiBTdW1tYXJ5ID09PVxuIikKcHJpbnQocHRfYmluX3N1bW1hcnkpCgpwdF9iaW5fcGVyX2xpbmUgPC0gbWFwcGVkX01hbGlnbmFudENENFRAbWV0YS5kYXRhICU+JQogIGZpbHRlcighaXMubmEocHNldWRvdGltZV9iaW4pKSAlPiUKICBjb3VudChjZWxsX2xpbmUsIHBzZXVkb3RpbWVfYmluLCBuYW1lID0gIm5fY2VsbHMiKSAlPiUKICBncm91cF9ieShjZWxsX2xpbmUpICU+JQogIG11dGF0ZShwY3QgPSByb3VuZCgxMDAgKiBuX2NlbGxzIC8gc3VtKG5fY2VsbHMpLCAxKSkgJT4lCiAgdW5ncm91cCgpCgpjYXQoIlxuPT09IFBzZXVkb3RpbWUgQmluIHBlciBDZWxsIExpbmUgPT09XG4iKQpwcmludChwdF9iaW5fcGVyX2xpbmUsIG4gPSBJbmYpCmBgYAoKIyMgQmFyIENoYXJ0cyDigJQgU3RhdGUgUHJvcG9ydGlvbnMKCmBgYHtyIGJhcnBsb3Qtc3RhdGVzLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9OX0KIyBUb3AgcGFuZWw6IGxhYmVsIHRyYW5zZmVyIChjZWxsIG9mIG9yaWdpbikKcF9iYXJfbGFiZWxzIDwtIGdncGxvdChzdGF0ZV9wZXJfbGluZSwKICAgICAgICAgICAgICAgICAgICAgICAgYWVzKHggPSBjZWxsX2xpbmUsIHkgPSBwY3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gc3RhdGVfYXppbXV0aF9sMikpICsKICBnZW9tX2NvbChwb3NpdGlvbiA9ICJzdGFjayIsIGNvbG9yID0gIndoaXRlIiwgbGluZXdpZHRoID0gMC4zKSArCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IGlmZWxzZShwY3QgPj0gNSwgcGFzdGUwKHBjdCwgIiUiKSwgIiIpKSwKICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9zdGFjayh2anVzdCA9IDAuNSksCiAgICAgICAgICAgIHNpemUgPSAzLCBjb2xvciA9ICJ3aGl0ZSIsIGZvbnRmYWNlID0gImJvbGQiKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYXppbXV0aF9sMl9jb2xvcnMsIG5hLnZhbHVlID0gImdyZXk3MCIsCiAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICJBemltdXRoIGwyXG5zdGF0ZSIpICsKICB0aGVtZV9jbGFzc2ljKGJhc2Vfc2l6ZSA9IDExKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0MCwgaGp1c3QgPSAxKSwKICAgICAgICBwbG90LnRpdGxlICA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgZmFjZSA9ICJib2xkIikpICsKICBsYWJzKHRpdGxlICAgID0gIlPDqXphcnkgQ2VsbCBTdGF0ZSBDb21wb3NpdGlvbiBwZXIgQ2VsbCBMaW5lIiwKICAgICAgIHN1YnRpdGxlID0gIkxhYmVsIHRyYW5zZmVyOiBjZWxsIG9mIG9yaWdpbiAod2hpY2ggbm9ybWFsIHN0YXRlIGRvZXMgdGhpcyBjZWxsIHJlc2VtYmxlPykiLAogICAgICAgeCA9ICJDZWxsIExpbmUiLCB5ID0gIiUgQ2VsbHMiKQoKIyBCb3R0b20gcGFuZWw6IHBzZXVkb3RpbWUgYmlucyAoZGlmZmVyZW50aWF0aW9uIHBvc2l0aW9uKQpwX2Jhcl9iaW5zIDwtIGdncGxvdChwdF9iaW5fcGVyX2xpbmUsCiAgICAgICAgICAgICAgICAgICAgICBhZXMoeCA9IGNlbGxfbGluZSwgeSA9IHBjdCwKICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gcHNldWRvdGltZV9iaW4pKSArCiAgZ2VvbV9jb2wocG9zaXRpb24gPSAic3RhY2siLCBjb2xvciA9ICJ3aGl0ZSIsIGxpbmV3aWR0aCA9IDAuMykgKwogIGdlb21fdGV4dChhZXMobGFiZWwgPSBpZmVsc2UocGN0ID49IDUsIHBhc3RlMChwY3QsICIlIiksICIiKSksCiAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fc3RhY2sodmp1c3QgPSAwLjUpLAogICAgICAgICAgICBzaXplID0gMywgY29sb3IgPSAid2hpdGUiLCBmb250ZmFjZSA9ICJib2xkIikgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGJpbl9jb2xvcnMsIG5hbWUgPSAiUHNldWRvdGltZVxuQmluIiwKICAgICAgICAgICAgICAgICAgICBkcm9wID0gRkFMU0UpICsKICB0aGVtZV9jbGFzc2ljKGJhc2Vfc2l6ZSA9IDExKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0MCwgaGp1c3QgPSAxKSwKICAgICAgICBwbG90LnRpdGxlICA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgZmFjZSA9ICJib2xkIikpICsKICBsYWJzKHRpdGxlICAgID0gIlPDqXphcnkgQ2VsbCBQc2V1ZG90aW1lIEJpbiBEaXN0cmlidXRpb24gcGVyIENlbGwgTGluZSIsCiAgICAgICBzdWJ0aXRsZSA9IHBhc3RlMCgKICAgICAgICAgIkRpZmZlcmVudGlhdGlvbiBwb3NpdGlvbiDigJQgc3RhdGUtYW5jaG9yZWQgYm91bmRhcmllczogIiwKICAgICAgICAgIk5haXZlfFRDTT0iLCByb3VuZChuYWl2ZV90Y21fY3V0LCAyKSwKICAgICAgICAgIiB8IFRDTXxURU09Iiwgcm91bmQodGNtX3RlbV9jdXQsIDIpLAogICAgICAgICAiIHwgVEVNfFRlbXJhPSIsIHJvdW5kKHRlbV90ZW1yYV9jdXQsIDIpLAogICAgICAgICAiIHwgVHJlZz1sYWJlbC1iYXNlZCIpLAogICAgICAgeCA9ICJDZWxsIExpbmUiLCB5ID0gIiUgQ2VsbHMiKQoKcF9iYXJfbGFiZWxzIC8gcF9iYXJfYmlucyArCiAgcGxvdF9hbm5vdGF0aW9uKAogICAgdGl0bGUgPSAiQ2VsbCBvZiBPcmlnaW4gKExhYmVsIFRyYW5zZmVyKSB2cyBEaWZmZXJlbnRpYXRpb24gUG9zaXRpb24gKFBzZXVkb3RpbWUgQmlucylcblPDqXphcnkgU3luZHJvbWUg4oCUIENENCsgVCBDZWxsIFRyYWplY3RvcnkiLAogICAgdGhlbWUgPSB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBzaXplID0gMTMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFjZSA9ICJib2xkIikpCiAgKQpgYGAKCiMjIEhlYXRtYXBzIOKAlCBTdGF0ZSBhbmQgQmluIFByb3BvcnRpb25zCgpgYGB7ciBoZWF0bWFwLXN0YXRlcywgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9Nn0KIyBIZWF0bWFwIDE6IEF6aW11dGggbDIgbGFiZWwgcHJvcG9ydGlvbnMgcGVyIGNlbGwgbGluZQpoZWF0bWFwX2RmIDwtIHN0YXRlX3Blcl9saW5lICU+JQogIHNlbGVjdChjZWxsX2xpbmUsIHN0YXRlX2F6aW11dGhfbDIsIHBjdCkgJT4lCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSAgPSBzdGF0ZV9hemltdXRoX2wyLAogICAgICAgICAgICAgIHZhbHVlc19mcm9tID0gcGN0LCB2YWx1ZXNfZmlsbCA9IDApCmhlYXRtYXBfbWF0ICAgICAgICAgICA8LSBhcy5tYXRyaXgoaGVhdG1hcF9kZlssIC0xXSkKcm93bmFtZXMoaGVhdG1hcF9tYXQpIDwtIGhlYXRtYXBfZGYkY2VsbF9saW5lCgpwaGVhdG1hcCgKICBoZWF0bWFwX21hdCwKICBjb2xvciAgICAgICAgICAgPSBjb2xvclJhbXBQYWxldHRlKGMoIndoaXRlIiwgIiNGRUUwOTAiLCAiI0Q3MzAyNyIpKSg1MCksCiAgZGlzcGxheV9udW1iZXJzID0gVFJVRSwgbnVtYmVyX2Zvcm1hdCA9ICIlLjFmJSUiLAogIGZvbnRzaXplX251bWJlciA9IDgsIGZvbnRzaXplX3JvdyA9IDEwLCBmb250c2l6ZV9jb2wgPSA5LAogIGFuZ2xlX2NvbCAgICAgICA9IDQ1LCBjbHVzdGVyX3Jvd3MgPSBUUlVFLCBjbHVzdGVyX2NvbHMgPSBUUlVFLAogIG1haW4gICAgICAgICAgICA9ICIlIFPDqXphcnkgQ2VsbHMgcGVyIEF6aW11dGggbDIgU3RhdGVcbihMYWJlbCB0cmFuc2ZlciDigJQgY2VsbCBvZiBvcmlnaW4pIiwKICBib3JkZXJfY29sb3IgICAgPSAid2hpdGUiCikKCiMgSGVhdG1hcCAyOiBwc2V1ZG90aW1lIGJpbiBwcm9wb3J0aW9ucyDigJQgYmlvbG9naWNhbCBvcmRlciBwcmVzZXJ2ZWQKYmluX2hlYXRtYXBfZGYgPC0gcHRfYmluX3Blcl9saW5lICU+JQogIHNlbGVjdChjZWxsX2xpbmUsIHBzZXVkb3RpbWVfYmluLCBwY3QpICU+JQogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gID0gcHNldWRvdGltZV9iaW4sCiAgICAgICAgICAgICAgdmFsdWVzX2Zyb20gPSBwY3QsIHZhbHVlc19maWxsID0gMCkKCiMgRklYOiB1c2UgYW55X29mKCkgdG8gc2FmZWx5IHNlbGVjdCBvbmx5IGNvbHVtbnMgdGhhdCBleGlzdAojIE9yaWdpbmFsIHVzZWQgYmFyZSBzZWxlY3QoKSB3aGljaCBlcnJvcnMgaWYgYSBiaW4gY29sdW1uIGlzIG1pc3NpbmcKYWxsX2Jpbl9sZXZlbHMgPC0gYygiTmFpdmUtbGlrZSIsICJUQ00tbGlrZSIsICJUcmVnLWxpa2UiLAogICAgICAgICAgICAgICAgICAgICJURU0tbGlrZSIsICJUZW1yYS1saWtlIikKZm9yIChiIGluIGFsbF9iaW5fbGV2ZWxzKSB7CiAgaWYgKCFiICVpbiUgY29sbmFtZXMoYmluX2hlYXRtYXBfZGYpKSBiaW5faGVhdG1hcF9kZltbYl1dIDwtIDAKfQpiaW5faGVhdG1hcF9kZiA8LSBiaW5faGVhdG1hcF9kZiAlPiUKICBzZWxlY3QoY2VsbF9saW5lLCBhbnlfb2YoYWxsX2Jpbl9sZXZlbHMpKQoKYmluX21hdCAgICAgICAgICAgPC0gYXMubWF0cml4KGJpbl9oZWF0bWFwX2RmWywgLTFdKQpyb3duYW1lcyhiaW5fbWF0KSA8LSBiaW5faGVhdG1hcF9kZiRjZWxsX2xpbmUKCnBoZWF0bWFwKAogIGJpbl9tYXQsCiAgY29sb3IgICAgICAgICAgID0gY29sb3JSYW1wUGFsZXR0ZShjKCJ3aGl0ZSIsICIjNzRBREQxIiwgIiNENzMwMjciKSkoNTApLAogIGRpc3BsYXlfbnVtYmVycyA9IFRSVUUsIG51bWJlcl9mb3JtYXQgPSAiJS4xZiUlIiwKICBmb250c2l6ZV9udW1iZXIgPSA4LCBmb250c2l6ZV9yb3cgPSAxMCwgZm9udHNpemVfY29sID0gOSwKICBhbmdsZV9jb2wgICAgICAgPSA0NSwKICBjbHVzdGVyX3Jvd3MgICAgPSBUUlVFLAogIGNsdXN0ZXJfY29scyAgICA9IEZBTFNFLCAgIyBwcmVzZXJ2ZSBiaW9sb2dpY2FsIG9yZGVyIG9mIGJpbnMKICBtYWluICAgICAgICAgICAgPSAiJSBTw6l6YXJ5IENlbGxzIHBlciBQc2V1ZG90aW1lIEJpblxuKFN0YXRlLWFuY2hvcmVkIGJvdW5kYXJpZXMg4oCUIGRpZmZlcmVudGlhdGlvbiBwb3NpdGlvbikiLAogIGJvcmRlcl9jb2xvciAgICA9ICJ3aGl0ZSIKKQpgYGAKCgojIFBzZXVkb3RpbWUgQW5hbHlzaXMKCiMjIFBzZXVkb3RpbWUgRGlzdHJpYnV0aW9uOiBSZWZlcmVuY2UgdnMgTWFsaWduYW50CgpgYGB7ciBwc2V1ZG90aW1lLWRpc3RyaWJ1dGlvbiwgZmlnLndpZHRoPTE0LCBmaWcuaGVpZ2h0PTZ9CnJlZl9wdCA8LSBkYXRhLmZyYW1lKAogIHBzZXVkb3RpbWUgPSByZWZlcmVuY2VfaW50ZWdyYXRlZCRtb25vY2xlM19wc2V1ZG90aW1lWwogICAgaXMuZmluaXRlKHJlZmVyZW5jZV9pbnRlZ3JhdGVkJG1vbm9jbGUzX3BzZXVkb3RpbWUpXSwKICBzb3VyY2UgPSAiSGVhbHRoeSByZWZlcmVuY2UiCikKbWFsX3B0IDwtIGRhdGEuZnJhbWUoCiAgcHNldWRvdGltZSA9IG1hcHBlZF9NYWxpZ25hbnRDRDRUJHBzZXVkb3RpbWVfdmFsdWVbCiAgICBpcy5maW5pdGUobWFwcGVkX01hbGlnbmFudENENFQkcHNldWRvdGltZV92YWx1ZSldLAogIHNvdXJjZSA9ICJTw6l6YXJ5IGNlbGxzIgopCnB0X2NvbWJpbmVkIDwtIGJpbmRfcm93cyhyZWZfcHQsIG1hbF9wdCkKCiMgUmVmZXJlbmNlIHN0YXRlIG1lZGlhbiBsaW5lcyDigJQgdmlzdWFsIGFuY2hvcnMgc2hvd2luZyB3aGVyZSBlYWNoCiMgaGVhbHRoeSBzdGF0ZSBzaXRzIG9uIHRoZSBwc2V1ZG90aW1lIGF4aXMKc3RhdGVfdmxpbmVzIDwtIGRhdGEuZnJhbWUoCiAgc3RhdGUgPSBjKCJOYWl2ZSIsICAgICJUQ00iLCAgICAiVHJlZyIsICAgICJURU0iLCAgICAiVGVtcmEiKSwKICBtZWQgICA9IGMobmFpdmVfbWVkLCAgdGNtX21lZCwgIHRyZWdfbWVkLCAgdGVtX21lZCwgIHRlbXJhX21lZCksCiAgY29sICAgPSBjKCIjMjE2NkFDIiwgICIjNzRBREQxIiwiIzc2MkE4MyIsICAiI0ZFRTA5MCIsIiNENzMwMjciKQopCgpwX2RlbnNpdHkgPC0gZ2dwbG90KHB0X2NvbWJpbmVkLCBhZXMoeCA9IHBzZXVkb3RpbWUsIGZpbGwgPSBzb3VyY2UpKSArCiAgZ2VvbV9kZW5zaXR5KGFscGhhID0gMC42NSwgYWRqdXN0ID0gMS4yKSArCiAgZ2VvbV92bGluZShkYXRhICA9IHN0YXRlX3ZsaW5lcywKICAgICAgICAgICAgIGFlcyh4aW50ZXJjZXB0ID0gbWVkLCBjb2xvciA9IHN0YXRlKSwKICAgICAgICAgICAgIGxpbmV0eXBlID0gImRhc2hlZCIsIGxpbmV3aWR0aCA9IDAuOCkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCgKICAgIHZhbHVlcyA9IHNldE5hbWVzKHN0YXRlX3ZsaW5lcyRjb2wsIHN0YXRlX3ZsaW5lcyRzdGF0ZSksCiAgICBuYW1lICAgPSAiUmVmZXJlbmNlIHN0YXRlXG5tZWRpYW4iKSArCiAgc2NhbGVfZmlsbF9tYW51YWwoCiAgICB2YWx1ZXMgPSBjKCJIZWFsdGh5IHJlZmVyZW5jZSIgPSAiIzQ1NzVCNCIsCiAgICAgICAgICAgICAgICJTw6l6YXJ5IGNlbGxzIiAgICAgID0gbWFsaWduYW50X2NvbG9yKSwKICAgIG5hbWUgPSAiIikgKwogIHRoZW1lX2NsYXNzaWMoYmFzZV9zaXplID0gMTIpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIpKSArCiAgbGFicyh0aXRsZSAgICA9ICJQc2V1ZG90aW1lIERpc3RyaWJ1dGlvbjogSGVhbHRoeSBSZWZlcmVuY2UgdnMgU8OpemFyeSBDZWxscyIsCiAgICAgICBzdWJ0aXRsZSA9ICJQZWFrIHNoaWZ0IGluZGljYXRlcyBkaWZmZXJlbnRpYXRpb24gYXJyZXN0IHBvaW50IHwgZGFzaGVkID0gcmVmZXJlbmNlIHN0YXRlIG1lZGlhbnMiLAogICAgICAgeCA9ICJNb25vY2xlMyBQc2V1ZG90aW1lIiwgeSA9ICJEZW5zaXR5IikKCnBfcmlkZ2UgPC0gbWFwcGVkX01hbGlnbmFudENENFRAbWV0YS5kYXRhICU+JQogIGZpbHRlcihpcy5maW5pdGUocHNldWRvdGltZV92YWx1ZSkpICU+JQogIGdncGxvdChhZXMoeCA9IHBzZXVkb3RpbWVfdmFsdWUsIHkgPSBjZWxsX2xpbmUsIGZpbGwgPSBjZWxsX2xpbmUpKSArCiAgZ2VvbV9kZW5zaXR5X3JpZGdlcyhhbHBoYSA9IDAuODAsIHNjYWxlID0gMS4zLCByZWxfbWluX2hlaWdodCA9IDAuMDEsCiAgICAgICAgICAgICAgICAgICAgICBxdWFudGlsZV9saW5lcyA9IFRSVUUsIHF1YW50aWxlcyA9IDIpICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBjKG5haXZlX21lZCwgdGNtX21lZCwgdHJlZ19tZWQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGVtX21lZCwgIHRlbXJhX21lZCksCiAgICAgICAgICAgICBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJncmV5NDAiLCBsaW5ld2lkdGggPSAwLjUpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjZWxsX2xpbmVfcGFsZXR0ZSkgKwogIHRoZW1lX2NsYXNzaWMoYmFzZV9zaXplID0gMTEpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgZmFjZSA9ICJib2xkIikpICsKICBsYWJzKHRpdGxlICAgID0gIlBzZXVkb3RpbWUgcGVyIENlbGwgTGluZSIsCiAgICAgICBzdWJ0aXRsZSA9ICJWZXJ0aWNhbCBsaW5lID0gbWVkaWFuIHwgRGFzaGVkID0gcmVmZXJlbmNlIHN0YXRlIG1lZGlhbnMiLAogICAgICAgeCA9ICJQc2V1ZG90aW1lIiwgeSA9ICIiKQoKcF9kZW5zaXR5IHwgcF9yaWRnZQpgYGAKCiMjIFBzZXVkb3RpbWUgU3VtbWFyeSBTdGF0aXN0aWNzCgpgYGB7ciBwc2V1ZG90aW1lLXN0YXRzfQojIEZJWDogcGN0IGNvbHVtbnMgY29tcHV0ZWQgb25seSBmb3Igbm9uLVRyZWcgY2VsbHMgb24gdGhlIGVmZmVjdG9yIGF4aXMuCiMgT3JpZ2luYWwgc2NyaXB0IGNvdW50ZWQgYWxsIGNlbGxzIGluY2x1ZGluZyBUcmVnIHdoaWNoIGluZmxhdGVzIHBjdF90Y20KIyAoVHJlZyBjZWxscyBoYXZlIFRDTS1yYW5nZSBwc2V1ZG90aW1lIGFuZCB3b3VsZCBiZSBjb3VudGVkIGFzIFRDTS1saWtlKS4KIyBOb3cgd2Ugc2VwYXJhdGUgVHJlZyBjZWxscyBhbmQgY29tcHV0ZSBlZmZlY3Rvci1heGlzIHN0YXRzIGluZGVwZW5kZW50bHkuCgpwdF9zdGF0cyA8LSBtYXBwZWRfTWFsaWduYW50Q0Q0VEBtZXRhLmRhdGEgJT4lCiAgZmlsdGVyKGlzLmZpbml0ZShwc2V1ZG90aW1lX3ZhbHVlKSkgJT4lCiAgZ3JvdXBfYnkoY2VsbF9saW5lKSAlPiUKICBzdW1tYXJpc2UoCiAgICBuX2NlbGxzICAgICAgPSBuKCksCiAgICBuX3RyZWcgICAgICAgPSBzdW0oc3RhdGVfYXppbXV0aF9sMiA9PSAiVHJlZyIsIG5hLnJtID0gVFJVRSksCiAgICBuX2VmZmVjdG9yICAgPSBzdW0oc3RhdGVfYXppbXV0aF9sMiAhPSAiVHJlZyIsIG5hLnJtID0gVFJVRSksCiAgICBtZWFuX3B0ICAgICAgPSByb3VuZChtZWFuKHBzZXVkb3RpbWVfdmFsdWUpLCAgICAgICAgICAgMyksCiAgICBtZWRpYW5fcHQgICAgPSByb3VuZChtZWRpYW4ocHNldWRvdGltZV92YWx1ZSksICAgICAgICAgMyksCiAgICBzZF9wdCAgICAgICAgPSByb3VuZChzZChwc2V1ZG90aW1lX3ZhbHVlKSwgICAgICAgICAgICAgMyksCiAgICBxMjVfcHQgICAgICAgPSByb3VuZChxdWFudGlsZShwc2V1ZG90aW1lX3ZhbHVlLCAwLjI1KSwgMyksCiAgICBxNzVfcHQgICAgICAgPSByb3VuZChxdWFudGlsZShwc2V1ZG90aW1lX3ZhbHVlLCAwLjc1KSwgMyksCiAgICAjIEVmZmVjdG9yIGF4aXMgcGN0OiBjb21wdXRlZCBvbmx5IG9uIG5vbi1UcmVnIGNlbGxzCiAgICBwY3RfbmFpdmUgICAgPSByb3VuZCgxMDAgKiBzdW0oCiAgICAgICAgICAgICAgICAgICAgIHN0YXRlX2F6aW11dGhfbDIgIT0gIlRyZWciICYKICAgICAgICAgICAgICAgICAgICAgcHNldWRvdGltZV92YWx1ZSAgPCAgbmFpdmVfdGNtX2N1dCwKICAgICAgICAgICAgICAgICAgICAgbmEucm0gPSBUUlVFKSAvIG5fZWZmZWN0b3IsIDEpLAogICAgcGN0X3RjbSAgICAgID0gcm91bmQoMTAwICogc3VtKAogICAgICAgICAgICAgICAgICAgICBzdGF0ZV9hemltdXRoX2wyICE9ICJUcmVnIiAmCiAgICAgICAgICAgICAgICAgICAgIHBzZXVkb3RpbWVfdmFsdWUgPj0gbmFpdmVfdGNtX2N1dCAmCiAgICAgICAgICAgICAgICAgICAgIHBzZXVkb3RpbWVfdmFsdWUgIDwgIHRjbV90ZW1fY3V0LAogICAgICAgICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUpIC8gbl9lZmZlY3RvciwgMSksCiAgICBwY3RfdGVtICAgICAgPSByb3VuZCgxMDAgKiBzdW0oCiAgICAgICAgICAgICAgICAgICAgIHN0YXRlX2F6aW11dGhfbDIgIT0gIlRyZWciICYKICAgICAgICAgICAgICAgICAgICAgcHNldWRvdGltZV92YWx1ZSA+PSB0Y21fdGVtX2N1dCAmCiAgICAgICAgICAgICAgICAgICAgIHBzZXVkb3RpbWVfdmFsdWUgIDwgIHRlbV90ZW1yYV9jdXQsCiAgICAgICAgICAgICAgICAgICAgIG5hLnJtID0gVFJVRSkgLyBuX2VmZmVjdG9yLCAxKSwKICAgIHBjdF90ZW1yYSAgICA9IHJvdW5kKDEwMCAqIHN1bSgKICAgICAgICAgICAgICAgICAgICAgc3RhdGVfYXppbXV0aF9sMiAhPSAiVHJlZyIgJgogICAgICAgICAgICAgICAgICAgICBwc2V1ZG90aW1lX3ZhbHVlID49IHRlbV90ZW1yYV9jdXQsCiAgICAgICAgICAgICAgICAgICAgIG5hLnJtID0gVFJVRSkgLyBuX2VmZmVjdG9yLCAxKSwKICAgIHBjdF90cmVnICAgICA9IHJvdW5kKDEwMCAqIG5fdHJlZyAvIG5fY2VsbHMsIDEpLAogICAgLmdyb3VwcyAgICAgID0gImRyb3AiCiAgKSAlPiUKICBhcnJhbmdlKG1lZGlhbl9wdCkKCmNhdCgiPT09IFBzZXVkb3RpbWUgU3RhdGlzdGljcyBwZXIgQ2VsbCBMaW5lID09PVxuIikKY2F0KCIocGN0X25haXZlL3RjbS90ZW0vdGVtcmEgY29tcHV0ZWQgb24gZWZmZWN0b3ItYXhpcyBjZWxscyBvbmx5LFxuIikKY2F0KCIgcGN0X3RyZWcgPSBUcmVnLWxhYmVsbGVkIGNlbGxzIGFzICUgb2YgYWxsIGNlbGxzKVxuXG4iKQpwcmludChwdF9zdGF0cywgbiA9IEluZikKCmNhdCgiXG49PT0gUmVmZXJlbmNlIFBzZXVkb3RpbWUgcGVyIEF6aW11dGggbDIgU3RhdGUgPT09XG4iKQpyZWZfc3RhdHMgPC0gcmVmZXJlbmNlX2ludGVncmF0ZWRAbWV0YS5kYXRhICU+JQogIGZpbHRlcihpcy5maW5pdGUobW9ub2NsZTNfcHNldWRvdGltZSksICFpcy5uYShwcmVkaWN0ZWQuY2VsbHR5cGUubDIpKSAlPiUKICBncm91cF9ieShwcmVkaWN0ZWQuY2VsbHR5cGUubDIpICU+JQogIHN1bW1hcmlzZSgKICAgIG5fY2VsbHMgICA9IG4oKSwKICAgIG1lYW5fcHQgICA9IHJvdW5kKG1lYW4obW9ub2NsZTNfcHNldWRvdGltZSksICAgMyksCiAgICBtZWRpYW5fcHQgPSByb3VuZChtZWRpYW4obW9ub2NsZTNfcHNldWRvdGltZSksIDMpLAogICAgLmdyb3VwcyAgID0gImRyb3AiCiAgKSAlPiUKICBhcnJhbmdlKG1lZGlhbl9wdCkKcHJpbnQocmVmX3N0YXRzKQpgYGAKCgojIEludGVncmF0ZWQgU3VtbWFyeQoKIyMgRm91ci1QYW5lbCBTdW1tYXJ5IEZpZ3VyZQoKYGBge3Igc3VtbWFyeS1maWd1cmUsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD0xMH0KcF9yZWYgPC0gRGltUGxvdCgKICByZWZlcmVuY2VfaW50ZWdyYXRlZCwgZ3JvdXAuYnkgPSAicHJlZGljdGVkLmNlbGx0eXBlLmwyIiwKICByZWR1Y3Rpb24gPSAidW1hcCIsIGxhYmVsID0gVFJVRSwgcmVwZWwgPSBUUlVFLAogIGxhYmVsLnNpemUgPSAzLCBwdC5zaXplID0gMC40CikgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBhemltdXRoX2wyX2NvbG9ycywgbmEudmFsdWUgPSAiZ3JleTQwIikgKwogIGdndGl0bGUoIkEuIEhlYWx0aHkgUmVmZXJlbmNlIENENCsgVCBDZWxsc1xuKEF6aW11dGggbDIpIikgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIHNpemUgPSAxMSwgZmFjZSA9ICJib2xkIikpICsKICBOb0xlZ2VuZCgpCgpwX3B0X3JlZiA8LSBGZWF0dXJlUGxvdCgKICByZWZlcmVuY2VfaW50ZWdyYXRlZCwgZmVhdHVyZXMgPSAibW9ub2NsZTNfcHNldWRvdGltZSIsCiAgcmVkdWN0aW9uID0gInVtYXAiLCBjb2xzID0gYygibGlnaHRibHVlIiwgInllbGxvdyIsICJyZWQiKSwKICBwdC5zaXplID0gMC40CikgKwogIGdndGl0bGUoIkIuIFJlZmVyZW5jZSBQc2V1ZG90aW1lXG4oTmFpdmUg4oaSIFRlbXJhICsgVHJlZyBicmFuY2gpIikgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIHNpemUgPSAxMSwgZmFjZSA9ICJib2xkIikpCgpwX3Byb2ogPC0gZ2dwbG90KCkgKwogIGdlb21fcG9pbnQoZGF0YSA9IHJlZl91bWFwLAogICAgICAgICAgICAgYWVzKHggPSBVTUFQXzEsIHkgPSBVTUFQXzIpLAogICAgICAgICAgICAgY29sb3IgPSAiZ3JleTg4Iiwgc2l6ZSA9IDAuMywgYWxwaGEgPSAwLjYpICsKICBnZW9tX3BvaW50KGRhdGEgPSBxdWVyeV91bWFwICU+JSBmaWx0ZXIoaXMuZmluaXRlKHBzZXVkb3RpbWUpKSwKICAgICAgICAgICAgIGFlcyh4ID0gVU1BUF8xLCB5ID0gVU1BUF8yLCBjb2xvciA9IHBzZXVkb3RpbWUpLAogICAgICAgICAgICAgc2l6ZSA9IDEuMCwgYWxwaGEgPSAwLjg1KSArCiAgc2NhbGVfY29sb3JfdmlyaWRpc19jKG9wdGlvbiA9ICJwbGFzbWEiLCBuYW1lID0gIlBzZXVkb3RpbWUiKSArCiAgdGhlbWVfY2xhc3NpYyhiYXNlX3NpemUgPSAxMCkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIHNpemUgPSAxMSwgZmFjZSA9ICJib2xkIikpICsKICBnZ3RpdGxlKCJDLiBTw6l6YXJ5IENlbGxzIFByb2plY3RlZFxuKFRyYW5zZmVycmVkIHBzZXVkb3RpbWUpIikKCnRvcF9zdGF0ZXMgICAgPC0gc3RhdGVfc3VtbWFyeSAlPiUgaGVhZCg4KSAlPiUgcHVsbChzdGF0ZV9hemltdXRoX2wyKQpzdGF0ZV9wbG90X2RmIDwtIHN0YXRlX3Blcl9saW5lICU+JSBmaWx0ZXIoc3RhdGVfYXppbXV0aF9sMiAlaW4lIHRvcF9zdGF0ZXMpCgpwX3F1YW50IDwtIGdncGxvdChzdGF0ZV9wbG90X2RmLAogICAgICAgICAgICAgICAgICAgYWVzKHggPSByZW9yZGVyKGNlbGxfbGluZSwgLXBjdCksIHkgPSBwY3QsCiAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IHN0YXRlX2F6aW11dGhfbDIpKSArCiAgZ2VvbV9jb2wocG9zaXRpb24gPSAic3RhY2siLCBjb2xvciA9ICJ3aGl0ZSIsIGxpbmV3aWR0aCA9IDAuMykgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGF6aW11dGhfbDJfY29sb3JzLCBuYS52YWx1ZSA9ICJncmV5NDAiLAogICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiQXppbXV0aCBsMiIpICsKICB0aGVtZV9jbGFzc2ljKGJhc2Vfc2l6ZSA9IDEwKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0MCwgaGp1c3QgPSAxLCBzaXplID0gOSksCiAgICAgICAgcGxvdC50aXRsZSAgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIHNpemUgPSAxMSwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgbGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjQsICJjbSIpLAogICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSA4KSkgKwogIGxhYnModGl0bGUgPSAiRC4gU3RhdGUgUHJvcG9ydGlvbnMgcGVyIENlbGwgTGluZVxuKEF6aW11dGggbDIgbGFiZWwgdHJhbnNmZXIpIiwKICAgICAgIHggPSAiIiwgeSA9ICIlIENlbGxzIikKCihwX3JlZiB8IHBfcHRfcmVmKSAvIChwX3Byb2ogfCBwX3F1YW50KSArCiAgcGxvdF9hbm5vdGF0aW9uKAogICAgdGl0bGUgPSAiU8OpemFyeSBDZWxsIERpZmZlcmVudGlhdGlvbiBTdGF0ZSBNYXBwaW5nIG9udG8gSGVhbHRoeSBDRDQrIFQgQ2VsbCBUcmFqZWN0b3J5IiwKICAgIHRoZW1lID0gdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgc2l6ZSA9IDEzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZhY2UgPSAiYm9sZCIpKQogICkKYGBgCgojIyBQc2V1ZG90aW1lIFZpb2xpbiBieSBUcmFuc2ZlcnJlZCBTdGF0ZQoKYGBge3IgcHNldWRvdGltZS12aW9saW4sIGZpZy53aWR0aD03LCBmaWcuaGVpZ2h0PTV9Cm1hbF9zdGF0ZV9wdCA8LSBtYXBwZWRfTWFsaWduYW50Q0Q0VEBtZXRhLmRhdGEgJT4lCiAgZmlsdGVyKGlzLmZpbml0ZShwc2V1ZG90aW1lX3ZhbHVlKSwgIWlzLm5hKHN0YXRlX2F6aW11dGhfbDIpKSAlPiUKICBtdXRhdGUoc3RhdGUgPSBzdGF0ZV9hemltdXRoX2wyKQoKc3RhdGVfb3JkZXJfbWFsIDwtIG1hbF9zdGF0ZV9wdCAlPiUKICBncm91cF9ieShzdGF0ZSkgJT4lCiAgc3VtbWFyaXNlKG1lZCA9IG1lZGlhbihwc2V1ZG90aW1lX3ZhbHVlKSwgLmdyb3VwcyA9ICJkcm9wIikgJT4lCiAgYXJyYW5nZShtZWQpICU+JQogIHB1bGwoc3RhdGUpCgptYWxfc3RhdGVfcHQkc3RhdGUgPC0gZmFjdG9yKG1hbF9zdGF0ZV9wdCRzdGF0ZSwgbGV2ZWxzID0gc3RhdGVfb3JkZXJfbWFsKQoKZ2dwbG90KG1hbF9zdGF0ZV9wdCwgYWVzKHggPSBzdGF0ZSwgeSA9IHBzZXVkb3RpbWVfdmFsdWUsIGZpbGwgPSBzdGF0ZSkpICsKICBnZW9tX3Zpb2xpbihzY2FsZSA9ICJ3aWR0aCIsIGFscGhhID0gMC44LCB0cmltID0gVFJVRSkgKwogIGdlb21fYm94cGxvdCh3aWR0aCA9IDAuMSwgZmlsbCA9ICJ3aGl0ZSIsIG91dGxpZXIuc2l6ZSA9IDAuMykgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IGMobmFpdmVfbWVkLCB0Y21fbWVkLCB0cmVnX21lZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0ZW1fbWVkLCAgdGVtcmFfbWVkKSwKICAgICAgICAgICAgIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gImdyZXk1MCIsIGxpbmV3aWR0aCA9IDAuNSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGF6aW11dGhfbDJfY29sb3JzLCBuYS52YWx1ZSA9ICJncmV5NzAiKSArCiAgdGhlbWVfY2xhc3NpYyhiYXNlX3NpemUgPSAxMSkgKwogIHRoZW1lKGF4aXMudGV4dC54ICAgPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0MCwgaGp1c3QgPSAxLCBzaXplID0gOSksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgICAgIHBsb3QudGl0bGUgICAgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIpKSArCiAgbGFicyh0aXRsZSAgICA9ICJUcmFuc2ZlcnJlZCBQc2V1ZG90aW1lIHBlciBBemltdXRoIGwyIFN0YXRlIChTw6l6YXJ5IENlbGxzKSIsCiAgICAgICBzdWJ0aXRsZSA9ICJEYXNoZWQgbGluZXMgPSBoZWFsdGh5IHJlZmVyZW5jZSBzdGF0ZSBtZWRpYW5zIiwKICAgICAgIHggPSAiVHJhbnNmZXJyZWQgU3RhdGUgKGNlbGwgb2Ygb3JpZ2luKSIsCiAgICAgICB5ID0gIlRyYW5zZmVycmVkIFBzZXVkb3RpbWUgKGRpZmZlcmVudGlhdGlvbiBwb3NpdGlvbikiKQpgYGAKCiMjIEtleSBDb21wYXJpc29uOiBMYWJlbCB2cyBQc2V1ZG90aW1lIEJpbgoKYGBge3IgbGFiZWwtdnMtYmluLCBmaWcud2lkdGg9MTEsIGZpZy5oZWlnaHQ9OH0KIyBUaGlzIGlzIHRoZSBjb3JlIHJlc3VsdCBwbG90LgojIEZvciBlYWNoIHRyYW5zZmVycmVkIEF6aW11dGggbDIgbGFiZWwsIHNob3dzIHdoYXQgZnJhY3Rpb24gb2YgY2VsbHMKIyBmYWxsIGludG8gZWFjaCBwc2V1ZG90aW1lIGJpbi4KIyBLZXkgbWVzc2FnZTogVENNLWxhYmVsbGVkIGNlbGxzIHNwYW4gVENNLCBURU0sIGFuZCBUZW1yYSBiaW5zCiMgICDihpIgaWRlbnRpdHkgPSBUQ00gKGNlbGwgb2Ygb3JpZ2luKQojICAg4oaSIHBvc2l0aW9uID0gZnVydGhlciBhbG9uZyBlZmZlY3RvciBheGlzIChwYXJ0aWFsIHNrZXdpbmcpCgojIEZJWDogYnVpbGQgY3Jvc3NfZGYgd2l0aCBleHBsaWNpdCBjb2x1bW4gc2VsZWN0aW9uIGZyb20gdGFibGUgb3V0cHV0CiMgcmF0aGVyIHRoYW4gcmVuYW1lKCkgd2hpY2ggY2FuIGZhaWwgaWYgY29sdW1uIG5hbWVzIGRpZmZlcgpjcm9zc19kZiA8LSBhcy5kYXRhLmZyYW1lKGNyb3NzX3RhYikKIyB0YWJsZSgpIGFsd2F5cyByZXR1cm5zOiBWYXIxIOKGkiBMYWJlbCwgVmFyMiDihpIgUFRfQmluIChmcm9tIG91ciBuYW1lZCBkaW1zKQojIGJ1dCBpZiB0YWJsZSB3YXMgY29uc3RydWN0ZWQgd2l0aCBuYW1lZCBkaW1zICJMYWJlbCIgYW5kICJQVF9CaW4iCiMgdGhlbiBjb2xuYW1lcyBhcmUgYWxyZWFkeSBMYWJlbCwgUFRfQmluLCBGcmVxCmNvbG5hbWVzKGNyb3NzX2RmKSA8LSBjKCJMYWJlbCIsICJQVF9CaW4iLCAibiIpCgpjcm9zc19kZiA8LSBjcm9zc19kZiAlPiUKICBncm91cF9ieShMYWJlbCkgJT4lCiAgbXV0YXRlKHBjdCA9IHJvdW5kKDEwMCAqIG4gLyBzdW0obiksIDEpKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgZmlsdGVyKG4gPiAwKSAlPiUKICBtdXRhdGUoUFRfQmluID0gZmFjdG9yKFBUX0JpbiwKICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoIk5haXZlLWxpa2UiLCAiVENNLWxpa2UiLCAiVHJlZy1saWtlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlRFTS1saWtlIiwgIlRlbXJhLWxpa2UiKSkpCgpnZ3Bsb3QoY3Jvc3NfZGYsIGFlcyh4ID0gUFRfQmluLCB5ID0gcGN0LCBmaWxsID0gUFRfQmluKSkgKwogIGdlb21fY29sKGNvbG9yID0gIndoaXRlIiwgbGluZXdpZHRoID0gMC4zKSArCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IGlmZWxzZShwY3QgPj0gMywgcGFzdGUwKHBjdCwgIiUiKSwgIiIpKSwKICAgICAgICAgICAgdmp1c3QgPSAtMC4zLCBzaXplID0gMywgZm9udGZhY2UgPSAiYm9sZCIpICsKICBmYWNldF93cmFwKH4gTGFiZWwsIHNjYWxlcyA9ICJmcmVlX3kiLCBuY29sID0gMykgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGJpbl9jb2xvcnMsIGRyb3AgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICBuYW1lID0gIlBzZXVkb3RpbWUgQmluIikgKwogIHRoZW1lX2NsYXNzaWMoYmFzZV9zaXplID0gMTApICsKICB0aGVtZShheGlzLnRleHQueCAgICAgPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0MCwgaGp1c3QgPSAxLCBzaXplID0gOCksCiAgICAgICAgc3RyaXAudGV4dCAgICAgICA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiLCBzaXplID0gOSksCiAgICAgICAgc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gIiNFOEY0RkQiKSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gID0gIm5vbmUiLAogICAgICAgIHBsb3QudGl0bGUgICAgICAgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIpKSArCiAgbGFicygKICAgIHRpdGxlICAgID0gIlBzZXVkb3RpbWUgQmluIERpc3RyaWJ1dGlvbiB3aXRoaW4gRWFjaCBUcmFuc2ZlcnJlZCBBemltdXRoIGwyIExhYmVsIiwKICAgIHN1YnRpdGxlID0gcGFzdGUwKAogICAgICAiQ29yZSBmaW5kaW5nOiBUQ00tbGFiZWxsZWQgU8OpemFyeSBjZWxscyBzcGFuIG11bHRpcGxlIHBzZXVkb3RpbWUgYmluc1xuIiwKICAgICAgIuKGkiBUQ00gaWRlbnRpdHkgKGxhYmVsKSBidXQgcGFydGlhbCBlZmZlY3RvciBza2V3aW5nIChwc2V1ZG90aW1lIHBvc2l0aW9uKVxuIiwKICAgICAgIk5vdGU6IFRyZWcgcGFuZWwgc2hvd3MgMTAwJSBUcmVnLWxpa2UgYnkgZGVzaWduIChsYWJlbC1iYXNlZCBiaW4gYXNzaWdubWVudCkiKSwKICAgIHggPSAiUHNldWRvdGltZSBCaW4iLCB5ID0gIiUgQ2VsbHMgd2l0aGluIGxhYmVsIgogICkKYGBgCgoKIyBTYXZlIE91dHB1dHMKCmBgYHtyIHNhdmUtb3V0cHV0c30KU3lzLnNldGxvY2FsZSgiTENfQ09MTEFURSIsICJDIikKb3B0aW9ucyhleHByZXNzaW9ucyA9IDEwMDAwKQoKb3V0X2RpciA8LSAicmVzdWx0cy9NYWxpZ25hbnRDRDRUX01vbm9jbGUzX3Byb2plY3Rpb24iCmlmICghZGlyLmV4aXN0cyhvdXRfZGlyKSkgZGlyLmNyZWF0ZShvdXRfZGlyLCByZWN1cnNpdmUgPSBUUlVFKQoKbGlicmFyeShTZXVyYXRPYmplY3QpCgpTYXZlU2V1cmF0UmRzKG1hcHBlZF9NYWxpZ25hbnRDRDRULAogICAgICAgICAgICAgIGZpbGUgPSBmaWxlLnBhdGgob3V0X2RpciwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJNYWxpZ25hbnRDRDRUX21hcHBlZF9tb25vY2xlM19wc2V1ZG90aW1lX25vUHJvbGlmX3JlZi5SZHMiKSkKCgoKIyBTdW1tYXJ5IHRhYmxlcwp3cml0ZS5jc3Yoc3RhdGVfc3VtbWFyeSwKICAgICAgICAgIGZpbGUucGF0aChvdXRfZGlyLCAic3RhdGVfZGlzdHJpYnV0aW9uX292ZXJhbGwuY3N2IiksCiAgICAgICAgICByb3cubmFtZXMgPSBGQUxTRSkKd3JpdGUuY3N2KHN0YXRlX3Blcl9saW5lLAogICAgICAgICAgZmlsZS5wYXRoKG91dF9kaXIsICJzdGF0ZV9kaXN0cmlidXRpb25fcGVyX2NlbGxsaW5lLmNzdiIpLAogICAgICAgICAgcm93Lm5hbWVzID0gRkFMU0UpCndyaXRlLmNzdihwdF9iaW5fcGVyX2xpbmUsCiAgICAgICAgICBmaWxlLnBhdGgob3V0X2RpciwgInBzZXVkb3RpbWVfYmluc19wZXJfY2VsbGxpbmUuY3N2IiksCiAgICAgICAgICByb3cubmFtZXMgPSBGQUxTRSkKd3JpdGUuY3N2KHB0X3N0YXRzLAogICAgICAgICAgZmlsZS5wYXRoKG91dF9kaXIsICJwc2V1ZG90aW1lX3N0YXRzX3Blcl9jZWxsbGluZS5jc3YiKSwKICAgICAgICAgIHJvdy5uYW1lcyA9IEZBTFNFKQp3cml0ZS5jc3YoYXMuZGF0YS5mcmFtZShjcm9zc190YWIpLAogICAgICAgICAgZmlsZS5wYXRoKG91dF9kaXIsICJsYWJlbF92c19wc2V1ZG90aW1lX2Jpbl9jcm9zc3RhYmxlLmNzdiIpLAogICAgICAgICAgcm93Lm5hbWVzID0gRkFMU0UpCndyaXRlLmNzdihtYXBwZWRfTWFsaWduYW50Q0Q0VEBtZXRhLmRhdGEsCiAgICAgICAgICBmaWxlLnBhdGgob3V0X2RpciwgIk1hbGlnbmFudENENFRfZnVsbF9tZXRhZGF0YV93aXRoX3Byb2plY3Rpb24uY3N2IiksCiAgICAgICAgICByb3cubmFtZXMgPSBUUlVFKQoKIyBCaW4gYm91bmRhcmllcyDigJQgc2F2ZWQgZm9yIG1ldGhvZHMgc2VjdGlvbiBhbmQgcmVwcm9kdWNpYmlsaXR5CmJpbl9ib3VuZGFyaWVzIDwtIGRhdGEuZnJhbWUoCiAgYm91bmRhcnkgICAgICAgPSBjKCJOYWl2ZV9UQ00iLCAiVENNX1RFTSIsICJURU1fVGVtcmEiLCAiVENNX1RyZWdfYnJhbmNoIiksCiAgcHNldWRvdGltZV9jdXQgPSBjKG5haXZlX3RjbV9jdXQsIHRjbV90ZW1fY3V0LCB0ZW1fdGVtcmFfY3V0LCB0Y21fdHJlZ19jdXQpLAogIG5haXZlX21lZCAgICAgID0gbmFpdmVfbWVkLAogIHRjbV9tZWQgICAgICAgID0gdGNtX21lZCwKICB0cmVnX21lZCAgICAgICA9IHRyZWdfbWVkLAogIHRlbV9tZWQgICAgICAgID0gdGVtX21lZCwKICB0ZW1yYV9tZWQgICAgICA9IHRlbXJhX21lZAopCndyaXRlLmNzdihiaW5fYm91bmRhcmllcywKICAgICAgICAgIGZpbGUucGF0aChvdXRfZGlyLCAicHNldWRvdGltZV9iaW5fYm91bmRhcmllcy5jc3YiKSwKICAgICAgICAgIHJvdy5uYW1lcyA9IEZBTFNFKQoKY2F0KCLinIUgQWxsIG91dHB1dHMgc2F2ZWQgdG86Iiwgb3V0X2RpciwgIlxuXG4iKQpjYXQoIkZpbGVzOlxuIikKcHJpbnQobGlzdC5maWxlcyhvdXRfZGlyKSkKYGBgCgoKIyBTZXNzaW9uIEluZm8KCmBgYHtyIHNlc3Npb24taW5mb30Kc2Vzc2lvbkluZm8oKQpgYGAKCgo=