1 Overview

This script projects Sézary syndrome malignant CD4 T cells (cell lines L1–L7) onto the pre-built normal CD4 T cell reference trajectory.

Requires outputs from cd4_ref_dual_trajectory.Rmd: - cd4_ref_dual_trajectory.rds — reference with milestone + pseudotime columns - monocle3_cds.rds — Monocle3 cell_data_set with principal graph - mst_graph.rds — igraph MST object - milestone_centroids.rds — milestone centroid coordinates and metadata


2 Setup

suppressPackageStartupMessages({
  library(Seurat)
  library(SeuratWrappers)
  library(monocle3)
  library(igraph)
  library(RANN)
  library(ggplot2)
  library(ggrepel)
  library(dplyr)
  library(tidyr)
  library(patchwork)
  library(pheatmap)
  library(viridis)
  library(scales)
  library(RColorBrewer)
  library(knitr)
  library(kableExtra)
})

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

METHOD_COLORS <- c(
  "Monocle3"   = "#e74c3c",
  "Custom MST" = "#2980b9"
)

STATE_ORDER <- c("CD4 Naive","CD4 TCM","CD4 TEM","CD4 Temra/CTL","Treg")
UMAP_NAME   <- "umap"
STATE_COL   <- "predicted.celltype.l2"

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

3 Load Reference Objects

# ── Load reference and trajectory objects ─────────────────────────────────
cd4_ref             <- readRDS("../1-Custom_MST/Objects/cd4_ref_dual_trajectory.rds")
cds                 <- readRDS("../1-Custom_MST/Objects/monocle3_cds.rds")
mst_graph           <- readRDS("../1-Custom_MST/Objects/mst_graph.rds")
milestone_centroids <- readRDS("../1-Custom_MST/Objects/milestone_centroids.rds")

cat("Reference loaded:", ncol(cd4_ref), "cells\n")
Reference loaded: 11466 cells
cat("Milestone labels present:", "milestone" %in% colnames(cd4_ref@meta.data), "\n")
Milestone labels present: TRUE 
cat("MST pseudotime present:  ", "mst_pseudotime_norm" %in% colnames(cd4_ref@meta.data), "\n")
MST pseudotime present:   TRUE 
cat("M3 pseudotime present:   ", "monocle3_pseudotime_norm" %in% colnames(cd4_ref@meta.data), "\n")
M3 pseudotime present:    TRUE 
# Validate required columns
stopifnot(
  "milestone missing from cd4_ref"              = "milestone" %in% colnames(cd4_ref@meta.data),
  "mst_pseudotime_norm missing from cd4_ref"    = "mst_pseudotime_norm" %in% colnames(cd4_ref@meta.data),
  "monocle3_pseudotime_norm missing from cd4_ref" = "monocle3_pseudotime_norm" %in% colnames(cd4_ref@meta.data),
  "integrated assay missing"                    = "integrated" %in% names(cd4_ref@assays),
  "PCA loadings missing"                        = ncol(cd4_ref[["pca"]]@feature.loadings) > 0,
  "UMAP model missing"                          = !is.null(cd4_ref[["umap"]]@misc$model)
)
cat("✅ Reference validation passed\n")
✅ Reference validation passed
cat("\nMilestone distribution in reference:\n")

Milestone distribution in reference:
print(table(cd4_ref$milestone))

 M00  M01  M02  M03  M04  M05  M06 
2037 4350 4717  145   10  146   61 
cat("\nAzimuth l2 labels:\n")

Azimuth l2 labels:
print(table(cd4_ref@meta.data[[STATE_COL]]))

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

4 Reconstruct Projection Objects

# ── These objects were created during MST computation in Script 1.
# ── Reconstruct from saved RDS files so this script is fully standalone.

# Milestone order
milestone_order <- sprintf("M%02d", seq_len(nrow(milestone_centroids)) - 1L)

# Graph distances from root M00 (unweighted hop count)
graph_dists <- distances(mst_graph, v = "M00", weights = NA)
graph_dists[is.infinite(graph_dists)] <-
  max(graph_dists[is.finite(graph_dists)]) + 1
g_dists_vec <- setNames(as.numeric(graph_dists), colnames(graph_dists))

# MST pseudotime range (needed for Sézary normalisation)
mst_pt_range <- range(cd4_ref$mst_pseudotime, na.rm = TRUE)

# MST edge data frame for plotting
mst_edges   <- as_edgelist(mst_graph)
mst_edge_df <- do.call(rbind, lapply(seq_len(nrow(mst_edges)), function(i) {
  m1 <- mst_edges[i,1]; m2 <- mst_edges[i,2]
  r1 <- milestone_centroids[milestone_centroids$milestone == m1, ]
  r2 <- milestone_centroids[milestone_centroids$milestone == m2, ]
  if (nrow(r1)==0 || nrow(r2)==0) return(NULL)
  data.frame(x1=r1$UMAP_1, y1=r1$UMAP_2, x2=r2$UMAP_1, y2=r2$UMAP_2)
}))

# Monocle3 edge data frame for plotting
pr_graph_obj <- principal_graph(cds)[["UMAP"]]
pr_layout    <- t(cds@principal_graph_aux[["UMAP"]]$dp_mst)
m3_edge_list <- igraph::as_edgelist(pr_graph_obj)
m3_edge_df   <- do.call(rbind, lapply(seq_len(nrow(m3_edge_list)), function(i) {
  n1 <- m3_edge_list[i,1]; n2 <- m3_edge_list[i,2]
  data.frame(x1=pr_layout[n1,1], y1=pr_layout[n1,2],
             x2=pr_layout[n2,1], y2=pr_layout[n2,2])
}))

# UMAP coordinates data frame for reference background in plots
umap_df           <- as.data.frame(Embeddings(cd4_ref, UMAP_NAME))
colnames(umap_df) <- c("UMAP_1","UMAP_2")
umap_df$cell      <- rownames(umap_df)
umap_df$state     <- cd4_ref@meta.data[[STATE_COL]]
umap_df$milestone <- cd4_ref@meta.data$milestone

# MST projection function (needed for sezary-pseudotime chunk)
project_onto_mst <- function(cell_coords, centroid_mat, mst_graph, graph_dists) {
  edges      <- as_edgelist(mst_graph)
  n_cells    <- nrow(cell_coords)
  pseudotime <- numeric(n_cells)
  nearest_ms <- character(n_cells)

  for (i in seq_len(n_cells)) {
    cx <- cell_coords[i,1]; cy <- cell_coords[i,2]
    best_dist <- Inf; best_pt <- 0; best_ms <- NA_character_

    for (e in seq_len(nrow(edges))) {
      m1 <- edges[e,1]; m2 <- edges[e,2]
      p1 <- centroid_mat[m1,]; p2 <- centroid_mat[m2,]
      dx <- p2[1]-p1[1]; dy <- p2[2]-p1[2]
      len2 <- dx^2 + dy^2
      if (len2 < 1e-10) next
      t    <- max(0, min(1, ((cx-p1[1])*dx + (cy-p1[2])*dy) / len2))
      proj <- c(p1[1]+t*dx, p1[2]+t*dy)
      d    <- sqrt((cx-proj[1])^2 + (cy-proj[2])^2)
      if (d < best_dist) {
        best_dist <- d
        pt_m1     <- graph_dists[m1]
        pt_m2     <- graph_dists[m2]
        best_pt   <- pt_m1 + t * (pt_m2 - pt_m1)
        best_ms   <- if (t < 0.5) m1 else m2
      }
    }
    pseudotime[i] <- best_pt
    nearest_ms[i] <- best_ms
  }
  list(pseudotime = pseudotime, nearest_ms = nearest_ms)
}

# centroid_mat needed for project_onto_mst
centroid_mat <- as.matrix(milestone_centroids[, c("UMAP_1","UMAP_2")])
rownames(centroid_mat) <- milestone_centroids$milestone

cat("✅ All projection objects reconstructed\n")
✅ All projection objects reconstructed
cat("Milestone order:", paste(milestone_order, collapse=" → "), "\n")
Milestone order: M00 → M01 → M02 → M03 → M04 → M05 → M06 
cat("MST pseudotime range:", round(mst_pt_range[1],2), "–", round(mst_pt_range[2],2), "\n")
MST pseudotime range: 0 – 4 

5 Load Sézary Object

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

cat("Sézary object loaded:", ncol(sezary_obj), "cells\n")
Sézary object loaded: 49305 cells
cat("\nAll orig.ident values:\n")

All orig.ident values:
print(table(sezary_obj$orig.ident))

      L1       L2       L3       L4       L5       L6       L7 CD4T_lab CD4T_10x 
    5825     5935     6428     6006     6022     5148     5331     5106     3504 
stopifnot(
  "RNA assay missing from sezary_obj" = "RNA" %in% names(sezary_obj@assays)
)
cat("✅ Sézary object validated\n")
✅ Sézary object validated

6 Subset to Malignant Cell Lines L1-L7

# ── Subset to malignant Sézary cell lines only ────────────────────────────
malignant_idents <- c("L1","L2","L3","L4","L5","L6","L7")
sezary_obj <- subset(sezary_obj, subset = orig.ident %in% malignant_idents)

cat("Cell counts after subsetting to L1-L7:\n")
Cell counts after subsetting to L1-L7:
print(table(sezary_obj$orig.ident))

      L1       L2       L3       L4       L5       L6       L7 CD4T_lab CD4T_10x 
    5825     5935     6428     6006     6022     5148     5331        0        0 
cat("\nTotal malignant cells:", ncol(sezary_obj), "\n")

Total malignant cells: 40695 
stopifnot(
  "No cells after subset — check orig.ident values" = ncol(sezary_obj) > 0,
  "Expected 7 cell lines" = length(unique(sezary_obj$orig.ident)) == 7
)
cat("✅ Malignant subset validated\n")
✅ Malignant subset validated

7 Project Sézary Cells onto Reference Trajectory

7.1 MapQuery

# Just for this step
old_plan <- plan("sequential")
options(future.globals.maxSize = 50 * 1024^3)  # 50 GB

# ══════════════════════════════════════════════════════════════════════════
# SÉZARY CELL PROJECTION ONTO REFERENCE TRAJECTORY
# ══════════════════════════════════════════════════════════════════════════
# Reference was built with:
#   SCTransform (per dataset, vst.flavor="v2") →
#   PrepSCTIntegration → FindIntegrationAnchors (RPCA) →
#   IntegrateData (normalization.method="SCT") →
#   RunPCA (integrated assay, return.model=TRUE) →
#   RunUMAP (return.model=TRUE)
#
# Therefore:
#   normalization.method = "SCT"        (matches reference integration)
#   reference.reduction  = "pca"        (PCA lives on integrated assay)
#   reduction            = "pcaproject" (uses saved PCA rotation matrix)
#   Query must be SCT-normalised per sample to match reference feature space
# ══════════════════════════════════════════════════════════════════════════

# ── Step 1: Set reference default assay ───────────────────────────────────
DefaultAssay(cd4_ref) <- "integrated"
cat("Reference default assay:", DefaultAssay(cd4_ref), "\n")
Reference default assay: integrated 
cat("Reference cells:", ncol(cd4_ref), "\n")
Reference cells: 11466 
cat("Reference PCA dims:", ncol(Embeddings(cd4_ref, "pca")), "\n")
Reference PCA dims: 50 
cat("PCA model present:", ncol(cd4_ref[["pca"]]@feature.loadings) > 0, "\n")
PCA model present: TRUE 
cat("UMAP model present:", !is.null(cd4_ref[["umap"]]@misc$model), "\n")
UMAP model present: TRUE 
# ── Step 2: Cell cycle scoring on query ───────────────────────────────────
DefaultAssay(sezary_obj) <- "RNA"
sezary_obj <- NormalizeData(sezary_obj, verbose = FALSE)
sezary_obj <- CellCycleScoring(
  sezary_obj,
  s.features   = cc.genes.updated.2019$s.genes,
  g2m.features = cc.genes.updated.2019$g2m.genes,
  set.ident    = FALSE
)
cat("\nCell cycle scoring complete\n")

Cell cycle scoring complete
print(table(sezary_obj$Phase))

   G1   G2M     S 
15379 13760 11556 
# ── Step 3: Per-sample SCT on query ───────────────────────────────────────
# Reference was SCT'd per dataset (3 models). Query must match.
# Each cell line is a separate library — per-line SCT prevents batch effects
# from dominating HVG selection and anchor finding.
split_col <- "orig.ident"
cat("\nSplitting query by:", split_col, "\n")

Splitting query by: orig.ident 
print(table(sezary_obj@meta.data[[split_col]]))

  L1   L2   L3   L4   L5   L6   L7 
5825 5935 6428 6006 6022 5148 5331 
sezary_list <- SplitObject(sezary_obj, split.by = split_col)
cat("Running SCT per sample (", length(sezary_list), "samples)...\n")
Running SCT per sample ( 7 samples)...
sezary_list <- lapply(seq_along(sezary_list), function(i) {
  cat("  SCT sample", i, "/", length(sezary_list),
      "-", names(sezary_list)[i],
      "(", ncol(sezary_list[[i]]), "cells)\n")
  SCTransform(
    sezary_list[[i]],
    vst.flavor      = "v2",
    vars.to.regress = c("S.Score", "G2M.Score", "percent.mt"),
    verbose         = FALSE
  )
})
  SCT sample 1 / 7 - L1 ( 5825 cells)
  SCT sample 2 / 7 - L2 ( 5935 cells)
  SCT sample 3 / 7 - L3 ( 6428 cells)
  SCT sample 4 / 7 - L4 ( 6006 cells)
  SCT sample 5 / 7 - L5 ( 6022 cells)
  SCT sample 6 / 7 - L6 ( 5148 cells)
  SCT sample 7 / 7 - L7 ( 5331 cells)
sezary_obj <- merge(sezary_list[[1]], sezary_list[-1], merge.data = TRUE)
cat("✅ SCT complete — merged query:", ncol(sezary_obj), "cells\n")
✅ SCT complete — merged query: 40695 cells
# ── Step 4: Set shared variable features ──────────────────────────────────
# Use reference integration features as the anchor feature set.
# These are the 2902 genes used to build the reference PCA — using the
# same genes ensures the pcaproject rotation is valid.
ref_features    <- rownames(cd4_ref[["integrated"]]@scale.data)
query_genes     <- rownames(sezary_obj[["SCT"]])
shared_features <- intersect(ref_features, query_genes)

cat("\nReference integration features:", length(ref_features), "\n")

Reference integration features: 2902 
cat("Query SCT genes:              ", length(query_genes), "\n")
Query SCT genes:               23460 
cat("Shared features:              ", length(shared_features), "\n")
Shared features:               2893 
if (length(shared_features) < 1500) {
  warning(paste0(
    "⚠️  Only ", length(shared_features), " shared features.\n",
    "   Minimum recommended is 1500.\n",
    "   Check that sezary_obj RNA assay contains the same gene set as reference."
  ))
}

# ── Step 5: Clean shared features ─────────────────────────────────────────
# Remove ribosomal, mitochondrial, TCR, HLA and other batch-driven genes.
junk_pattern <- "^RPL|^RPS|^MT-|^HSP|^HSPA|^HSPB|^HSPD|^HSPE|^HSPH|^SNHG|^MALAT1|^NEAT1|^XIST|^TRBV|^TRAV|^TRGV|^TRDV|^HLA-"
shared_features_clean <- shared_features[!grepl(junk_pattern, shared_features)]
cat("\nFeatures before cleaning:", length(shared_features), "\n")

Features before cleaning: 2893 
cat("Features after cleaning: ", length(shared_features_clean), "\n")
Features after cleaning:  2823 
cat("Junk genes removed:      ", length(shared_features) - length(shared_features_clean), "\n")
Junk genes removed:       70 
# Sanity check: key T cell markers present?
markers_check <- c("CCR7","SELL","TCF7","IL7R","GZMB","PRF1",
                   "FOXP3","TOX","PDCD1","CD44","CD69","TNF",
                   "TIGIT","HAVCR2","EOMES","TBX21","CXCR3","GZMK")
found_markers <- markers_check[markers_check %in% shared_features_clean]
cat("\nKey T cell markers in feature set:",
    length(found_markers), "/", length(markers_check), "\n")

Key T cell markers in feature set: 13 / 18 
print(found_markers)
 [1] "CCR7"  "SELL"  "TCF7"  "IL7R"  "PRF1"  "FOXP3" "TOX"   "CD44"  "CD69"  "TNF"   "TIGIT" "CXCR3" "GZMK" 
if (length(found_markers) < 8) {
  warning(paste0(
    "⚠️  Fewer than 8 T cell markers in shared features.\n",
    "   Feature set may be dominated by non-specific genes.\n",
    "   Inspect head(shared_features_clean, 30)."
  ))
}

VariableFeatures(sezary_obj) <- shared_features_clean
DefaultAssay(sezary_obj)     <- "SCT"

# ── Step 6: Find transfer anchors ─────────────────────────────────────────
cat("\n=== Finding transfer anchors ===\n")

=== Finding transfer anchors ===
cat("Reference cells:", ncol(cd4_ref), "\n")
Reference cells: 11466 
cat("Query cells:    ", ncol(sezary_obj), "\n")
Query cells:     40695 
cat("Features:       ", length(shared_features_clean), "\n")
Features:        2823 
# dims: use reference PCA only — pcaproject does not require query PCA
dims_to_use <- min(30, ncol(Embeddings(cd4_ref, "pca")))
cat("Using dims 1:", dims_to_use, "\n")
Using dims 1: 30 
anchors <- FindTransferAnchors(
  reference            = cd4_ref,
  query                = sezary_obj,
  features             = shared_features_clean,
  normalization.method = "SCT",        # ✅ matches SCT-RPCA reference
  reference.reduction  = "pca",        # ✅ PCA on integrated assay
  reduction            = "pcaproject", # ✅ project into reference PCA space
  dims                 = 1:dims_to_use,
  k.anchor             = 10,           # default 5  → more anchor candidates
  k.filter             = 500,          # default 200 → less pruning on large query
  k.score              = 30,           # default 30  → unchanged
  verbose              = TRUE
)

n_anchors    <- nrow(anchors@anchors)
anchor_ratio <- ncol(sezary_obj) / n_anchors
cat("\nAnchors found:    ", n_anchors, "\n")

Anchors found:     8661 
cat("Cells per anchor: ", round(anchor_ratio, 1), "\n")
Cells per anchor:  4.7 
if (anchor_ratio > 8) {
  warning(paste0(
    "⚠️  Low anchor density (1:", round(anchor_ratio, 1), ").\n",
    "   Ideal is 1:5 or better.\n",
    "   Check T cell markers found above — want ≥ 8/18.\n",
    "   Inspect head(shared_features_clean, 30) for batch genes."
  ))
} else {
  cat("✅ Anchor density acceptable\n")
}
✅ Anchor density acceptable
# ── Step 7: MapQuery ──────────────────────────────────────────────────────
cat("\n=== Running MapQuery ===\n")

=== Running MapQuery ===
sezary_obj <- MapQuery(
  anchorset           = anchors,
  query               = sezary_obj,
  reference           = cd4_ref,
  refdata             = list(
    predicted_state     = STATE_COL,     # predicted.celltype.l2
    predicted_milestone = "milestone"    # MST milestone labels (M00-M06)
  ),
  reference.reduction = "pca",           # basis for UMAP projection
  reduction.model     = "umap",          # uses saved UMAP model
  verbose             = FALSE
)
Using method 'umap'
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
cat("✅ MapQuery complete\n")
✅ MapQuery complete
cat("Mapped cells:", ncol(sezary_obj), "\n")
Mapped cells: 40695 
# ── Step 8: Coerce numeric transfers from character ───────────────────────
for (col in colnames(sezary_obj@meta.data)) {
  if (grepl("score$", col, ignore.case = TRUE)) {
    sezary_obj@meta.data[[col]] <- as.numeric(sezary_obj@meta.data[[col]])
  }
}

# ── Step 9: Validate results ──────────────────────────────────────────────
cat("\nPredicted state distribution:\n")

Predicted state distribution:
print(table(sezary_obj$predicted.predicted_state))

CD4 TCM CD4 TEM    Treg 
  40628      66       1 
cat("\nPrediction score summary:\n")

Prediction score summary:
print(summary(sezary_obj$predicted.predicted_state.score))
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
 0.4108  1.0000  1.0000  0.9837  1.0000  1.0000 
cat("\nPredicted milestone distribution:\n")

Predicted milestone distribution:
print(table(sezary_obj$predicted.predicted_milestone))

  M01   M02   M03   M05 
 1713 38893    86     3 
state_pct <- prop.table(table(sezary_obj$predicted.predicted_state)) * 100
dominant  <- names(which(state_pct > 60))
if (length(dominant) > 0) {
  warning(paste0(
    "⚠️  State collapse detected: ", dominant,
    " contains ", round(max(state_pct), 1), "% of cells.\n",
    "   Check anchor density and shared feature quality above."
  ))
} else {
  cat("✅ State distribution is diverse — no collapse detected\n")
}

7.2 Assign Pseudotime via Both Methods

sez_umap_coords           <- as.matrix(Embeddings(sezary_obj, "ref.umap"))
colnames(sez_umap_coords) <- c("UMAP_1","UMAP_2")

cat("Projecting Sézary cells onto MST...\n")
Projecting Sézary cells onto MST...
sez_mst <- project_onto_mst(sez_umap_coords, centroid_mat, mst_graph, g_dists_vec)

ref_umap_mat <- Embeddings(cd4_ref, UMAP_NAME)
ref_m3_pt    <- cd4_ref@meta.data$monocle3_pseudotime_norm

nn_result <- nn2(data=ref_umap_mat, query=sez_umap_coords, k=15)
inv_dist  <- 1 / (nn_result$nn.dists + 1e-6)
weights   <- inv_dist / rowSums(inv_dist)

meta_sez <- sezary_obj@meta.data
meta_sez$mst_pseudotime <- pmax(0, pmin(100,
  100 * (sez_mst$pseudotime - mst_pt_range[1]) /
         (mst_pt_range[2] - mst_pt_range[1])
))
meta_sez$nearest_milestone_mst <- sez_mst$nearest_ms
meta_sez$monocle3_pseudotime   <- rowSums(
  weights * matrix(ref_m3_pt[nn_result$nn.idx], nrow=nrow(nn_result$nn.idx))
)
sezary_obj@meta.data <- meta_sez

cat("MST pseudotime:", round(range(sezary_obj@meta.data$mst_pseudotime, na.rm=TRUE), 1), "\n")
MST pseudotime: 0 100 
cat("Monocle3 pseudotime:", round(range(sezary_obj@meta.data$monocle3_pseudotime, na.rm=TRUE), 1), "\n")
Monocle3 pseudotime: 1.9 99.3 

8 Projection Visualisation

8.1 Custom MST

8.2 Monocle3

p_sez_m3_state <- ggplot() +
  geom_point(data=ref_bg[sample(nrow(ref_bg)),],
             aes(x=UMAP_1,y=UMAP_2), colour="grey85", size=.3, alpha=.4) +
  geom_segment(data=m3_edge_df, aes(x=x1,y=y1,xend=x2,yend=y2),
               colour="grey30", linewidth=.6, alpha=.7) +
  geom_point(data=sez_plot_df,
             aes(x=UMAP_1,y=UMAP_2,colour=predicted_state), size=1.8, alpha=.85) +
  scale_colour_manual(values=STATE_COLORS, name="Predicted state") +
  theme_classic() +
  labs(x="UMAP-1", y="UMAP-2",
       title="Sézary cells — Monocle3 projection (predicted state)") +
  theme(plot.title=element_text(size=13, face="bold"))

p_sez_m3_pt <- ggplot() +
  geom_point(data=ref_bg[sample(nrow(ref_bg)),],
             aes(x=UMAP_1,y=UMAP_2), colour="grey85", size=.3, alpha=.4) +
  geom_segment(data=m3_edge_df, aes(x=x1,y=y1,xend=x2,yend=y2),
               colour="grey30", linewidth=.5, alpha=.6) +
  geom_point(data=sez_plot_df,
             aes(x=UMAP_1,y=UMAP_2,colour=m3_pt), size=1.8, alpha=.9) +
  scale_colour_gradientn(
    colours=c("#0D0887","#6A00A8","#B12A90","#E16462","#FCA636","#F0F921"),
    name="Monocle3\npseudotime", limits=c(0,100)) +
  theme_classic() +
  labs(x="UMAP-1", y="UMAP-2", title="Sézary — Monocle3 projected pseudotime") +
  theme(plot.title=element_text(size=13, face="bold"))

print(p_sez_m3_state / p_sez_m3_pt)

8.3 Method Agreement

sez_cor <- cor(sez_plot_df$mst_pt, sez_plot_df$m3_pt,
               method="spearman", use="complete.obs")

p_sez_scatter <- ggplot(sez_plot_df, aes(x=mst_pt, y=m3_pt, colour=predicted_state)) +
  geom_point(size=1.2, alpha=.7) +
  geom_abline(slope=1, intercept=0, linetype="dashed", colour="grey40") +
  geom_smooth(method="lm", se=FALSE, colour="black", linewidth=.8) +
  scale_colour_manual(values=STATE_COLORS, name="Predicted state") +
  annotate("text", x=5, y=90, label=sprintf("Spearman ρ = %.3f", sez_cor),
           size=4.5, fontface="bold", hjust=0) +
  theme_classic() +
  labs(x="Custom MST pseudotime", y="Monocle3 pseudotime",
       title="Sézary cells: pseudotime method agreement") +
  theme(plot.title=element_text(size=12, face="bold"))

p_sez_violin <- ggplot(
  sez_plot_df %>%
    pivot_longer(c(mst_pt,m3_pt), names_to="method", values_to="pt") %>%
    mutate(method=recode(method, mst_pt="Custom MST", m3_pt="Monocle3")),
  aes(x=predicted_state, y=pt, fill=method)
) +
  geom_violin(scale="width", trim=FALSE, alpha=.7, position=position_dodge(.8)) +
  geom_boxplot(width=.08, fill="white", outlier.size=.3, position=position_dodge(.8)) +
  scale_fill_manual(values=METHOD_COLORS, name="Method") +
  theme_classic() +
  labs(x="Predicted state", y="Pseudotime (0–100)",
       title="Sézary pseudotime per state: method comparison") +
  theme(plot.title=element_text(size=12, face="bold"),
        axis.text.x=element_text(angle=30, hjust=1))

print(p_sez_scatter | p_sez_violin)

8.4 Cell Line Breakdown

# State distribution per cell line
cell_line_state <- sez_plot_df %>%
  count(cell_line, predicted_state) %>%
  group_by(cell_line) %>%
  mutate(pct = round(100 * n / sum(n), 1)) %>%
  ungroup()

p_cell_line <- ggplot(cell_line_state,
                       aes(x=cell_line, y=pct, fill=predicted_state)) +
  geom_col(width=.7) +
  scale_fill_manual(values=STATE_COLORS, name="Predicted state") +
  theme_classic() +
  labs(x="Cell line", y="% cells",
       title="Predicted state distribution per cell line") +
  theme(plot.title=element_text(size=12, face="bold"))

# Pseudotime per cell line
p_pt_cell_line <- ggplot(sez_plot_df,
                          aes(x=cell_line, y=mst_pt, fill=cell_line)) +
  geom_violin(scale="width", trim=FALSE, alpha=.8) +
  geom_boxplot(width=.08, fill="white", outlier.size=.3) +
  theme_classic() +
  labs(x="Cell line", y="MST pseudotime (0–100)",
       title="Pseudotime distribution per cell line") +
  theme(plot.title=element_text(size=12, face="bold"),
        legend.position="none")

print(p_cell_line | p_pt_cell_line)


9 Summary Tables

sez_summary <- sez_plot_df %>%
  group_by(predicted_state) %>%
  summarise(
    n_cells         = n(),
    pct             = round(100*n()/nrow(sez_plot_df), 1),
    mean_pred_score = round(mean(pred_score), 3),
    mst_mean_pt     = round(mean(mst_pt, na.rm=TRUE), 1),
    mst_sd_pt       = round(sd(mst_pt,   na.rm=TRUE), 1),
    m3_mean_pt      = round(mean(m3_pt,  na.rm=TRUE), 1),
    m3_sd_pt        = round(sd(m3_pt,    na.rm=TRUE), 1),
    .groups         = "drop"
  ) %>% arrange(mst_mean_pt)

kable(sez_summary,
      col.names = c("State","N","% of Sézary","Pred. score",
                    "MST mean pt","MST SD","M3 mean pt","M3 SD"),
      caption = "Sézary cell trajectory summary — both methods") %>%
  kable_styling(bootstrap_options=c("striped","hover"), full_width=FALSE) %>%
  column_spec(5:6, color="white", background=METHOD_COLORS["Custom MST"]) %>%
  column_spec(7:8, color="white", background=METHOD_COLORS["Monocle3"])
Sézary cell trajectory summary — both methods
State N % of Sézary Pred. score MST mean pt MST SD M3 mean pt M3 SD
CD4 TCM 40628 99.8 0.984 68.4 20.1 64.4 15.8
Treg 1 0.0 0.480 96.0 NA 44.0 NA
CD4 TEM 66 0.2 0.526 99.4 2.4 98.0 0.0

ms_dist <- sez_plot_df %>%
  count(milestone_ref) %>%
  left_join(milestone_centroids[,c("milestone","state","order_pos")],
            by=c("milestone_ref"="milestone")) %>%
  mutate(pct=round(100*n/sum(n),1),
         milestone_ref=factor(milestone_ref, levels=milestone_order)) %>%
  arrange(order_pos)

p_ms_bar <- ggplot(ms_dist, aes(x=milestone_ref, y=pct, fill=state)) +
  geom_col(width=.7) +
  geom_text(aes(label=sprintf("%.0f%%",pct)), vjust=-.3, size=3) +
  scale_fill_manual(values=STATE_COLORS, name="State") +
  scale_x_discrete(limits=milestone_order) +
  theme_classic() +
  labs(x="Nearest reference milestone", y="% Sézary cells",
       title="Sézary cell distribution across reference milestones") +
  theme(axis.text.x=element_text(angle=45,hjust=1),
        plot.title=element_text(size=12,face="bold"))

print(p_ms_bar)


10 Save All Outputs

# ── RDS ───────────────────────────────────────────────────────────────────
saveRDS(sezary_obj, "Objects/sezary_projected_dual.rds")
Error: C stack usage  7969460 is too close to the limit
# ── CSV tables ────────────────────────────────────────────────────────────
write.csv(sez_summary, "Tables/sezary_trajectory_summary.csv",     row.names=FALSE)
write.csv(ms_dist,     "Tables/sezary_milestone_distribution.csv", row.names=FALSE)

# ── Figures ───────────────────────────────────────────────────────────────
ggsave("Figures/fig_sezary_mst_state.pdf",   p_sez_mst_state, width=12, height=8)
ggsave("Figures/fig_sezary_mst_pt.pdf",      p_sez_mst_pt,    width=12, height=8)
ggsave("Figures/fig_sezary_m3_state.pdf",    p_sez_m3_state,  width=12, height=8)
ggsave("Figures/fig_sezary_m3_pt.pdf",       p_sez_m3_pt,     width=12, height=8)
ggsave("Figures/fig_sezary_agreement.pdf",   p_sez_scatter,   width=12, height=5)
`geom_smooth()` using formula = 'y ~ x'
ggsave("Figures/fig_sezary_cell_lines.pdf",  p_cell_line,     width=12, height=5)
ggsave("Figures/fig_milestone_bar.pdf",      p_ms_bar,        width=12, height=5)

cat("✅ All outputs saved\n")
✅ All outputs saved

11 Session Information


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

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

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

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

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

other attached packages:
 [1] future_1.69.0               kableExtra_1.4.0            knitr_1.51                  RColorBrewer_1.1-3         
 [5] scales_1.4.0                viridis_0.6.5               viridisLite_0.4.3           pheatmap_1.0.13            
 [9] patchwork_1.3.2             tidyr_1.3.2                 dplyr_1.2.0                 ggrepel_0.9.6              
[13] ggplot2_4.0.2               RANN_2.6.2                  igraph_2.2.2                monocle3_1.4.26            
[17] SingleCellExperiment_1.32.0 SummarizedExperiment_1.40.0 GenomicRanges_1.62.1        Seqinfo_1.0.0              
[21] IRanges_2.44.0              S4Vectors_0.48.0            MatrixGenerics_1.22.0       matrixStats_1.5.0          
[25] Biobase_2.70.0              BiocGenerics_0.56.0         generics_0.1.4              SeuratWrappers_0.4.0       
[29] Seurat_5.4.0                SeuratObject_5.3.0          sp_2.2-1                   

loaded via a namespace (and not attached):
  [1] RcppAnnoy_0.0.23          splines_4.5.2             later_1.4.6               tibble_3.3.1              R.oo_1.27.1              
  [6] polyclip_1.10-7           fastDummies_1.7.5         lifecycle_1.0.5           Rdpack_2.6.6              globals_0.19.0           
 [11] lattice_0.22-9            MASS_7.3-65               magrittr_2.0.4            plotly_4.12.0             sass_0.4.10              
 [16] rmarkdown_2.30            jquerylib_0.1.4           yaml_2.3.12               remotes_2.5.0             httpuv_1.6.16            
 [21] otel_0.2.0                glmGamPoi_1.22.0          sctransform_0.4.3         spam_2.11-3               spatstat.sparse_3.1-0    
 [26] reticulate_1.45.0         cowplot_1.2.0             pbapply_1.7-4             minqa_1.2.8               abind_1.4-8              
 [31] Rtsne_0.17                purrr_1.2.1               R.utils_2.13.0            irlba_2.3.7               listenv_0.10.0           
 [36] spatstat.utils_3.2-1      goftest_1.2-3             RSpectra_0.16-2           spatstat.random_3.4-4     fitdistrplus_1.2-6       
 [41] parallelly_1.46.1         svglite_2.2.2             DelayedMatrixStats_1.32.0 codetools_0.2-20          DelayedArray_0.36.0      
 [46] xml2_1.5.2                tidyselect_1.2.1          farver_2.1.2              lme4_1.1-38               spatstat.explore_3.7-0   
 [51] jsonlite_2.0.0            progressr_0.18.0          ggridges_0.5.7            survival_3.8-6            systemfonts_1.3.1        
 [56] tools_4.5.2               ragg_1.5.0                ica_1.0-3                 Rcpp_1.1.1                glue_1.8.0               
 [61] gridExtra_2.3             SparseArray_1.10.8        mgcv_1.9-4                xfun_0.56                 withr_3.0.2              
 [66] BiocManager_1.30.27       fastmap_1.2.0             boot_1.3-32               digest_0.6.39             rsvd_1.0.5               
 [71] R6_2.6.1                  mime_0.13                 textshaping_1.0.4         scattermore_1.2           tensor_1.5.1             
 [76] dichromat_2.0-0.1         spatstat.data_3.1-9       R.methodsS3_1.8.2         data.table_1.18.2.1       httr_1.4.8               
 [81] htmlwidgets_1.6.4         S4Arrays_1.10.1           uwot_0.2.4                pkgconfig_2.0.3           gtable_0.3.6             
 [86] rsconnect_1.7.0           lmtest_0.9-40             S7_0.2.1                  XVector_0.50.0            htmltools_0.5.9          
 [91] dotCall64_1.2             png_0.1-8                 spatstat.univar_3.1-6     reformulas_0.4.4          rstudioapi_0.18.0        
 [96] reshape2_1.4.5            nlme_3.1-168              nloptr_2.2.1              cachem_1.1.0              zoo_1.8-15               
[101] stringr_1.6.0             KernSmooth_2.23-26        parallel_4.5.2            miniUI_0.1.2              pillar_1.11.1            
[106] grid_4.5.2                vctrs_0.7.1               promises_1.5.0            beachmat_2.26.0           xtable_1.8-4             
[111] cluster_2.1.8.2           evaluate_1.0.5            cli_3.6.5                 compiler_4.5.2            rlang_1.1.7              
[116] future.apply_1.20.1       labeling_0.4.3            plyr_1.8.9                stringi_1.8.7             deldir_2.0-4             
[121] lazyeval_0.2.2            spatstat.geom_3.7-0       Matrix_1.7-4              RcppHNSW_0.6.0            sparseMatrixStats_1.22.0 
[126] shiny_1.12.1              rbibutils_2.4.1           ROCR_1.0-12               bslib_0.10.0             

12 Metadata Reference

Metadata columns added to sezary_obj by this pipeline
Column Description
predicted.predicted_state MapQuery label transfer: predicted.celltype.l2 from reference (Naive/TCM/TEM/Temra/Treg)
predicted.predicted_state.score MapQuery prediction confidence score (0–1)
predicted.predicted_milestone MapQuery label transfer: milestone label from reference (M00–M06)
nearest_milestone_mst Nearest MST milestone after edge projection in ref.umap space
mst_pseudotime MST pseudotime on reference 0–100 scale (capped 0–100)
monocle3_pseudotime KNN-weighted Monocle3 pseudotime from 15 nearest reference neighbours
LS0tCnRpdGxlOiAiU8OpemFyeSBDRDQgVCBDZWxsIFByb2plY3Rpb24gb250byBSZWZlcmVuY2UgVHJhamVjdG9yeSIKc3VidGl0bGU6ICJSZWZlcmVuY2UtYmFzZWQgcHNldWRvdGltZSB8IE1TVCArIE1vbm9jbGUzIHwgTDEtTDcgY2VsbCBsaW5lcyIKYXV0aG9yOiAiTkFTSVIgTUFITU9PRCBBQkJBU0kiCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OgogICAgICBjb2xsYXBzZWQ6IHRydWUKICAgIHRoZW1lOiBqb3VybmFsCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldCgKICBlY2hvICAgICAgPSBUUlVFLAogIG1lc3NhZ2UgICA9IEZBTFNFLAogIHdhcm5pbmcgICA9IEZBTFNFLAogIGZpZy5hbGlnbiA9ICJjZW50ZXIiLAogIGRwaSAgICAgICA9IDE1MAopCmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIE92ZXJ2aWV3CgpUaGlzIHNjcmlwdCBwcm9qZWN0cyBTw6l6YXJ5IHN5bmRyb21lIG1hbGlnbmFudCBDRDQgVCBjZWxscyAoY2VsbCBsaW5lcyBMMeKAk0w3KQpvbnRvIHRoZSBwcmUtYnVpbHQgbm9ybWFsIENENCBUIGNlbGwgcmVmZXJlbmNlIHRyYWplY3RvcnkuCgoqKlJlcXVpcmVzIG91dHB1dHMgZnJvbSBgY2Q0X3JlZl9kdWFsX3RyYWplY3RvcnkuUm1kYDoqKgotIGBjZDRfcmVmX2R1YWxfdHJhamVjdG9yeS5yZHNgIOKAlCByZWZlcmVuY2Ugd2l0aCBtaWxlc3RvbmUgKyBwc2V1ZG90aW1lIGNvbHVtbnMKLSBgbW9ub2NsZTNfY2RzLnJkc2AgICAgICAgICAgIOKAlCBNb25vY2xlMyBjZWxsX2RhdGFfc2V0IHdpdGggcHJpbmNpcGFsIGdyYXBoCi0gYG1zdF9ncmFwaC5yZHNgICAgICAgICAgICAgICDigJQgaWdyYXBoIE1TVCBvYmplY3QKLSBgbWlsZXN0b25lX2NlbnRyb2lkcy5yZHNgICAgIOKAlCBtaWxlc3RvbmUgY2VudHJvaWQgY29vcmRpbmF0ZXMgYW5kIG1ldGFkYXRhCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgU2V0dXAKCmBgYHtyIGxpYnJhcmllc30Kc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKHsKICBsaWJyYXJ5KFNldXJhdCkKICBsaWJyYXJ5KFNldXJhdFdyYXBwZXJzKQogIGxpYnJhcnkobW9ub2NsZTMpCiAgbGlicmFyeShpZ3JhcGgpCiAgbGlicmFyeShSQU5OKQogIGxpYnJhcnkoZ2dwbG90MikKICBsaWJyYXJ5KGdncmVwZWwpCiAgbGlicmFyeShkcGx5cikKICBsaWJyYXJ5KHRpZHlyKQogIGxpYnJhcnkocGF0Y2h3b3JrKQogIGxpYnJhcnkocGhlYXRtYXApCiAgbGlicmFyeSh2aXJpZGlzKQogIGxpYnJhcnkoc2NhbGVzKQogIGxpYnJhcnkoUkNvbG9yQnJld2VyKQogIGxpYnJhcnkoa25pdHIpCiAgbGlicmFyeShrYWJsZUV4dHJhKQp9KQoKU1RBVEVfQ09MT1JTIDwtIGMoCiAgIkNENCBOYWl2ZSIgICAgID0gIiM0NDcyQzQiLAogICJDRDQgVENNIiAgICAgICA9ICIjNzBBRDQ3IiwKICAiQ0Q0IFRFTSIgICAgICAgPSAiI0VEN0QzMSIsCiAgIkNENCBUZW1yYS9DVEwiID0gIiNDMDAwMDAiLAogICJUcmVnIiAgICAgICAgICA9ICIjNzAzMEEwIgopCgpNRVRIT0RfQ09MT1JTIDwtIGMoCiAgIk1vbm9jbGUzIiAgID0gIiNlNzRjM2MiLAogICJDdXN0b20gTVNUIiA9ICIjMjk4MGI5IgopCgpTVEFURV9PUkRFUiA8LSBjKCJDRDQgTmFpdmUiLCJDRDQgVENNIiwiQ0Q0IFRFTSIsIkNENCBUZW1yYS9DVEwiLCJUcmVnIikKVU1BUF9OQU1FICAgPC0gInVtYXAiClNUQVRFX0NPTCAgIDwtICJwcmVkaWN0ZWQuY2VsbHR5cGUubDIiCgpjYXQoIuKchSBTZXR1cCBjb21wbGV0ZVxuIikKYGBgCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgTG9hZCBSZWZlcmVuY2UgT2JqZWN0cwoKYGBge3IgbG9hZC1yZWZlcmVuY2V9CiMg4pSA4pSAIExvYWQgcmVmZXJlbmNlIGFuZCB0cmFqZWN0b3J5IG9iamVjdHMg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACmNkNF9yZWYgICAgICAgICAgICAgPC0gcmVhZFJEUygiLi4vMS1DdXN0b21fTVNUL09iamVjdHMvY2Q0X3JlZl9kdWFsX3RyYWplY3RvcnkucmRzIikKY2RzICAgICAgICAgICAgICAgICA8LSByZWFkUkRTKCIuLi8xLUN1c3RvbV9NU1QvT2JqZWN0cy9tb25vY2xlM19jZHMucmRzIikKbXN0X2dyYXBoICAgICAgICAgICA8LSByZWFkUkRTKCIuLi8xLUN1c3RvbV9NU1QvT2JqZWN0cy9tc3RfZ3JhcGgucmRzIikKbWlsZXN0b25lX2NlbnRyb2lkcyA8LSByZWFkUkRTKCIuLi8xLUN1c3RvbV9NU1QvT2JqZWN0cy9taWxlc3RvbmVfY2VudHJvaWRzLnJkcyIpCgpjYXQoIlJlZmVyZW5jZSBsb2FkZWQ6IiwgbmNvbChjZDRfcmVmKSwgImNlbGxzXG4iKQpjYXQoIk1pbGVzdG9uZSBsYWJlbHMgcHJlc2VudDoiLCAibWlsZXN0b25lIiAlaW4lIGNvbG5hbWVzKGNkNF9yZWZAbWV0YS5kYXRhKSwgIlxuIikKY2F0KCJNU1QgcHNldWRvdGltZSBwcmVzZW50OiAgIiwgIm1zdF9wc2V1ZG90aW1lX25vcm0iICVpbiUgY29sbmFtZXMoY2Q0X3JlZkBtZXRhLmRhdGEpLCAiXG4iKQpjYXQoIk0zIHBzZXVkb3RpbWUgcHJlc2VudDogICAiLCAibW9ub2NsZTNfcHNldWRvdGltZV9ub3JtIiAlaW4lIGNvbG5hbWVzKGNkNF9yZWZAbWV0YS5kYXRhKSwgIlxuIikKCiMgVmFsaWRhdGUgcmVxdWlyZWQgY29sdW1ucwpzdG9waWZub3QoCiAgIm1pbGVzdG9uZSBtaXNzaW5nIGZyb20gY2Q0X3JlZiIgICAgICAgICAgICAgID0gIm1pbGVzdG9uZSIgJWluJSBjb2xuYW1lcyhjZDRfcmVmQG1ldGEuZGF0YSksCiAgIm1zdF9wc2V1ZG90aW1lX25vcm0gbWlzc2luZyBmcm9tIGNkNF9yZWYiICAgID0gIm1zdF9wc2V1ZG90aW1lX25vcm0iICVpbiUgY29sbmFtZXMoY2Q0X3JlZkBtZXRhLmRhdGEpLAogICJtb25vY2xlM19wc2V1ZG90aW1lX25vcm0gbWlzc2luZyBmcm9tIGNkNF9yZWYiID0gIm1vbm9jbGUzX3BzZXVkb3RpbWVfbm9ybSIgJWluJSBjb2xuYW1lcyhjZDRfcmVmQG1ldGEuZGF0YSksCiAgImludGVncmF0ZWQgYXNzYXkgbWlzc2luZyIgICAgICAgICAgICAgICAgICAgID0gImludGVncmF0ZWQiICVpbiUgbmFtZXMoY2Q0X3JlZkBhc3NheXMpLAogICJQQ0EgbG9hZGluZ3MgbWlzc2luZyIgICAgICAgICAgICAgICAgICAgICAgICA9IG5jb2woY2Q0X3JlZltbInBjYSJdXUBmZWF0dXJlLmxvYWRpbmdzKSA+IDAsCiAgIlVNQVAgbW9kZWwgbWlzc2luZyIgICAgICAgICAgICAgICAgICAgICAgICAgID0gIWlzLm51bGwoY2Q0X3JlZltbInVtYXAiXV1AbWlzYyRtb2RlbCkKKQpjYXQoIuKchSBSZWZlcmVuY2UgdmFsaWRhdGlvbiBwYXNzZWRcbiIpCgpjYXQoIlxuTWlsZXN0b25lIGRpc3RyaWJ1dGlvbiBpbiByZWZlcmVuY2U6XG4iKQpwcmludCh0YWJsZShjZDRfcmVmJG1pbGVzdG9uZSkpCgpjYXQoIlxuQXppbXV0aCBsMiBsYWJlbHM6XG4iKQpwcmludCh0YWJsZShjZDRfcmVmQG1ldGEuZGF0YVtbU1RBVEVfQ09MXV0pKQpgYGAKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBSZWNvbnN0cnVjdCBQcm9qZWN0aW9uIE9iamVjdHMKCmBgYHtyIHJlY29uc3RydWN0LW9iamVjdHN9CiMg4pSA4pSAIFRoZXNlIG9iamVjdHMgd2VyZSBjcmVhdGVkIGR1cmluZyBNU1QgY29tcHV0YXRpb24gaW4gU2NyaXB0IDEuCiMg4pSA4pSAIFJlY29uc3RydWN0IGZyb20gc2F2ZWQgUkRTIGZpbGVzIHNvIHRoaXMgc2NyaXB0IGlzIGZ1bGx5IHN0YW5kYWxvbmUuCgojIE1pbGVzdG9uZSBvcmRlcgptaWxlc3RvbmVfb3JkZXIgPC0gc3ByaW50ZigiTSUwMmQiLCBzZXFfbGVuKG5yb3cobWlsZXN0b25lX2NlbnRyb2lkcykpIC0gMUwpCgojIEdyYXBoIGRpc3RhbmNlcyBmcm9tIHJvb3QgTTAwICh1bndlaWdodGVkIGhvcCBjb3VudCkKZ3JhcGhfZGlzdHMgPC0gZGlzdGFuY2VzKG1zdF9ncmFwaCwgdiA9ICJNMDAiLCB3ZWlnaHRzID0gTkEpCmdyYXBoX2Rpc3RzW2lzLmluZmluaXRlKGdyYXBoX2Rpc3RzKV0gPC0KICBtYXgoZ3JhcGhfZGlzdHNbaXMuZmluaXRlKGdyYXBoX2Rpc3RzKV0pICsgMQpnX2Rpc3RzX3ZlYyA8LSBzZXROYW1lcyhhcy5udW1lcmljKGdyYXBoX2Rpc3RzKSwgY29sbmFtZXMoZ3JhcGhfZGlzdHMpKQoKIyBNU1QgcHNldWRvdGltZSByYW5nZSAobmVlZGVkIGZvciBTw6l6YXJ5IG5vcm1hbGlzYXRpb24pCm1zdF9wdF9yYW5nZSA8LSByYW5nZShjZDRfcmVmJG1zdF9wc2V1ZG90aW1lLCBuYS5ybSA9IFRSVUUpCgojIE1TVCBlZGdlIGRhdGEgZnJhbWUgZm9yIHBsb3R0aW5nCm1zdF9lZGdlcyAgIDwtIGFzX2VkZ2VsaXN0KG1zdF9ncmFwaCkKbXN0X2VkZ2VfZGYgPC0gZG8uY2FsbChyYmluZCwgbGFwcGx5KHNlcV9sZW4obnJvdyhtc3RfZWRnZXMpKSwgZnVuY3Rpb24oaSkgewogIG0xIDwtIG1zdF9lZGdlc1tpLDFdOyBtMiA8LSBtc3RfZWRnZXNbaSwyXQogIHIxIDwtIG1pbGVzdG9uZV9jZW50cm9pZHNbbWlsZXN0b25lX2NlbnRyb2lkcyRtaWxlc3RvbmUgPT0gbTEsIF0KICByMiA8LSBtaWxlc3RvbmVfY2VudHJvaWRzW21pbGVzdG9uZV9jZW50cm9pZHMkbWlsZXN0b25lID09IG0yLCBdCiAgaWYgKG5yb3cocjEpPT0wIHx8IG5yb3cocjIpPT0wKSByZXR1cm4oTlVMTCkKICBkYXRhLmZyYW1lKHgxPXIxJFVNQVBfMSwgeTE9cjEkVU1BUF8yLCB4Mj1yMiRVTUFQXzEsIHkyPXIyJFVNQVBfMikKfSkpCgojIE1vbm9jbGUzIGVkZ2UgZGF0YSBmcmFtZSBmb3IgcGxvdHRpbmcKcHJfZ3JhcGhfb2JqIDwtIHByaW5jaXBhbF9ncmFwaChjZHMpW1siVU1BUCJdXQpwcl9sYXlvdXQgICAgPC0gdChjZHNAcHJpbmNpcGFsX2dyYXBoX2F1eFtbIlVNQVAiXV0kZHBfbXN0KQptM19lZGdlX2xpc3QgPC0gaWdyYXBoOjphc19lZGdlbGlzdChwcl9ncmFwaF9vYmopCm0zX2VkZ2VfZGYgICA8LSBkby5jYWxsKHJiaW5kLCBsYXBwbHkoc2VxX2xlbihucm93KG0zX2VkZ2VfbGlzdCkpLCBmdW5jdGlvbihpKSB7CiAgbjEgPC0gbTNfZWRnZV9saXN0W2ksMV07IG4yIDwtIG0zX2VkZ2VfbGlzdFtpLDJdCiAgZGF0YS5mcmFtZSh4MT1wcl9sYXlvdXRbbjEsMV0sIHkxPXByX2xheW91dFtuMSwyXSwKICAgICAgICAgICAgIHgyPXByX2xheW91dFtuMiwxXSwgeTI9cHJfbGF5b3V0W24yLDJdKQp9KSkKCiMgVU1BUCBjb29yZGluYXRlcyBkYXRhIGZyYW1lIGZvciByZWZlcmVuY2UgYmFja2dyb3VuZCBpbiBwbG90cwp1bWFwX2RmICAgICAgICAgICA8LSBhcy5kYXRhLmZyYW1lKEVtYmVkZGluZ3MoY2Q0X3JlZiwgVU1BUF9OQU1FKSkKY29sbmFtZXModW1hcF9kZikgPC0gYygiVU1BUF8xIiwiVU1BUF8yIikKdW1hcF9kZiRjZWxsICAgICAgPC0gcm93bmFtZXModW1hcF9kZikKdW1hcF9kZiRzdGF0ZSAgICAgPC0gY2Q0X3JlZkBtZXRhLmRhdGFbW1NUQVRFX0NPTF1dCnVtYXBfZGYkbWlsZXN0b25lIDwtIGNkNF9yZWZAbWV0YS5kYXRhJG1pbGVzdG9uZQoKIyBNU1QgcHJvamVjdGlvbiBmdW5jdGlvbiAobmVlZGVkIGZvciBzZXphcnktcHNldWRvdGltZSBjaHVuaykKcHJvamVjdF9vbnRvX21zdCA8LSBmdW5jdGlvbihjZWxsX2Nvb3JkcywgY2VudHJvaWRfbWF0LCBtc3RfZ3JhcGgsIGdyYXBoX2Rpc3RzKSB7CiAgZWRnZXMgICAgICA8LSBhc19lZGdlbGlzdChtc3RfZ3JhcGgpCiAgbl9jZWxscyAgICA8LSBucm93KGNlbGxfY29vcmRzKQogIHBzZXVkb3RpbWUgPC0gbnVtZXJpYyhuX2NlbGxzKQogIG5lYXJlc3RfbXMgPC0gY2hhcmFjdGVyKG5fY2VsbHMpCgogIGZvciAoaSBpbiBzZXFfbGVuKG5fY2VsbHMpKSB7CiAgICBjeCA8LSBjZWxsX2Nvb3Jkc1tpLDFdOyBjeSA8LSBjZWxsX2Nvb3Jkc1tpLDJdCiAgICBiZXN0X2Rpc3QgPC0gSW5mOyBiZXN0X3B0IDwtIDA7IGJlc3RfbXMgPC0gTkFfY2hhcmFjdGVyXwoKICAgIGZvciAoZSBpbiBzZXFfbGVuKG5yb3coZWRnZXMpKSkgewogICAgICBtMSA8LSBlZGdlc1tlLDFdOyBtMiA8LSBlZGdlc1tlLDJdCiAgICAgIHAxIDwtIGNlbnRyb2lkX21hdFttMSxdOyBwMiA8LSBjZW50cm9pZF9tYXRbbTIsXQogICAgICBkeCA8LSBwMlsxXS1wMVsxXTsgZHkgPC0gcDJbMl0tcDFbMl0KICAgICAgbGVuMiA8LSBkeF4yICsgZHleMgogICAgICBpZiAobGVuMiA8IDFlLTEwKSBuZXh0CiAgICAgIHQgICAgPC0gbWF4KDAsIG1pbigxLCAoKGN4LXAxWzFdKSpkeCArIChjeS1wMVsyXSkqZHkpIC8gbGVuMikpCiAgICAgIHByb2ogPC0gYyhwMVsxXSt0KmR4LCBwMVsyXSt0KmR5KQogICAgICBkICAgIDwtIHNxcnQoKGN4LXByb2pbMV0pXjIgKyAoY3ktcHJvalsyXSleMikKICAgICAgaWYgKGQgPCBiZXN0X2Rpc3QpIHsKICAgICAgICBiZXN0X2Rpc3QgPC0gZAogICAgICAgIHB0X20xICAgICA8LSBncmFwaF9kaXN0c1ttMV0KICAgICAgICBwdF9tMiAgICAgPC0gZ3JhcGhfZGlzdHNbbTJdCiAgICAgICAgYmVzdF9wdCAgIDwtIHB0X20xICsgdCAqIChwdF9tMiAtIHB0X20xKQogICAgICAgIGJlc3RfbXMgICA8LSBpZiAodCA8IDAuNSkgbTEgZWxzZSBtMgogICAgICB9CiAgICB9CiAgICBwc2V1ZG90aW1lW2ldIDwtIGJlc3RfcHQKICAgIG5lYXJlc3RfbXNbaV0gPC0gYmVzdF9tcwogIH0KICBsaXN0KHBzZXVkb3RpbWUgPSBwc2V1ZG90aW1lLCBuZWFyZXN0X21zID0gbmVhcmVzdF9tcykKfQoKIyBjZW50cm9pZF9tYXQgbmVlZGVkIGZvciBwcm9qZWN0X29udG9fbXN0CmNlbnRyb2lkX21hdCA8LSBhcy5tYXRyaXgobWlsZXN0b25lX2NlbnRyb2lkc1ssIGMoIlVNQVBfMSIsIlVNQVBfMiIpXSkKcm93bmFtZXMoY2VudHJvaWRfbWF0KSA8LSBtaWxlc3RvbmVfY2VudHJvaWRzJG1pbGVzdG9uZQoKY2F0KCLinIUgQWxsIHByb2plY3Rpb24gb2JqZWN0cyByZWNvbnN0cnVjdGVkXG4iKQpjYXQoIk1pbGVzdG9uZSBvcmRlcjoiLCBwYXN0ZShtaWxlc3RvbmVfb3JkZXIsIGNvbGxhcHNlPSIg4oaSICIpLCAiXG4iKQpjYXQoIk1TVCBwc2V1ZG90aW1lIHJhbmdlOiIsIHJvdW5kKG1zdF9wdF9yYW5nZVsxXSwyKSwgIuKAkyIsIHJvdW5kKG1zdF9wdF9yYW5nZVsyXSwyKSwgIlxuIikKYGBgCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgTG9hZCBTw6l6YXJ5IE9iamVjdAoKYGBge3IgbG9hZC1zZXphcnl9CnNlemFyeV9vYmogPC0gcmVhZFJEUygiL2hvbWUvbmFiYmFzaS9hcG9sbG9faG9tZS8xLVNldXJhdF9SRFNfT0JKRUNUX0ZJTkFML0FsbF9zYW1wbGVzX01lcmdlZF93aXRoX1JlbmFtZWRfQ2x1c3RlcnNfQ2VsbF9zdGF0ZS0wMy0xMi0yMDI1LnJkcy5yZHMiKQoKY2F0KCJTw6l6YXJ5IG9iamVjdCBsb2FkZWQ6IiwgbmNvbChzZXphcnlfb2JqKSwgImNlbGxzXG4iKQpjYXQoIlxuQWxsIG9yaWcuaWRlbnQgdmFsdWVzOlxuIikKcHJpbnQodGFibGUoc2V6YXJ5X29iaiRvcmlnLmlkZW50KSkKCnN0b3BpZm5vdCgKICAiUk5BIGFzc2F5IG1pc3NpbmcgZnJvbSBzZXphcnlfb2JqIiA9ICJSTkEiICVpbiUgbmFtZXMoc2V6YXJ5X29iakBhc3NheXMpCikKY2F0KCLinIUgU8OpemFyeSBvYmplY3QgdmFsaWRhdGVkXG4iKQpgYGAKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBTdWJzZXQgdG8gTWFsaWduYW50IENlbGwgTGluZXMgTDEtTDcKCmBgYHtyIHN1YnNldC1tYWxpZ25hbnR9CiMg4pSA4pSAIFN1YnNldCB0byBtYWxpZ25hbnQgU8OpemFyeSBjZWxsIGxpbmVzIG9ubHkg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACm1hbGlnbmFudF9pZGVudHMgPC0gYygiTDEiLCJMMiIsIkwzIiwiTDQiLCJMNSIsIkw2IiwiTDciKQpzZXphcnlfb2JqIDwtIHN1YnNldChzZXphcnlfb2JqLCBzdWJzZXQgPSBvcmlnLmlkZW50ICVpbiUgbWFsaWduYW50X2lkZW50cykKCmNhdCgiQ2VsbCBjb3VudHMgYWZ0ZXIgc3Vic2V0dGluZyB0byBMMS1MNzpcbiIpCnByaW50KHRhYmxlKHNlemFyeV9vYmokb3JpZy5pZGVudCkpCmNhdCgiXG5Ub3RhbCBtYWxpZ25hbnQgY2VsbHM6IiwgbmNvbChzZXphcnlfb2JqKSwgIlxuIikKCnN0b3BpZm5vdCgKICAiTm8gY2VsbHMgYWZ0ZXIgc3Vic2V0IOKAlCBjaGVjayBvcmlnLmlkZW50IHZhbHVlcyIgPSBuY29sKHNlemFyeV9vYmopID4gMCwKICAiRXhwZWN0ZWQgNyBjZWxsIGxpbmVzIiA9IGxlbmd0aCh1bmlxdWUoc2V6YXJ5X29iaiRvcmlnLmlkZW50KSkgPT0gNwopCmNhdCgi4pyFIE1hbGlnbmFudCBzdWJzZXQgdmFsaWRhdGVkXG4iKQpgYGAKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBQcm9qZWN0IFPDqXphcnkgQ2VsbHMgb250byBSZWZlcmVuY2UgVHJhamVjdG9yeQoKIyMgTWFwUXVlcnkKCmBgYHtyIHNlemFyeS1tYXBxdWVyeX0KIyBKdXN0IGZvciB0aGlzIHN0ZXAKb3B0aW9ucyhmdXR1cmUuZ2xvYmFscy5tYXhTaXplID0gNTAgKiAxMDI0XjMpICAjIGluY3JlYXNlIHRvIDUwIEdpQgpwbGFuKCJzZXF1ZW50aWFsIikgICMgZGlzYWJsZSBwYXJhbGxlbGlzbSBlbnRpcmVseSDigJQgc2FmZXN0IGZpeAoKIyDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZAKIyBTw4laQVJZIENFTEwgUFJPSkVDVElPTiBPTlRPIFJFRkVSRU5DRSBUUkFKRUNUT1JZCiMg4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQCiMgUmVmZXJlbmNlIHdhcyBidWlsdCB3aXRoOgojICAgU0NUcmFuc2Zvcm0gKHBlciBkYXRhc2V0LCB2c3QuZmxhdm9yPSJ2MiIpIOKGkgojICAgUHJlcFNDVEludGVncmF0aW9uIOKGkiBGaW5kSW50ZWdyYXRpb25BbmNob3JzIChSUENBKSDihpIKIyAgIEludGVncmF0ZURhdGEgKG5vcm1hbGl6YXRpb24ubWV0aG9kPSJTQ1QiKSDihpIKIyAgIFJ1blBDQSAoaW50ZWdyYXRlZCBhc3NheSwgcmV0dXJuLm1vZGVsPVRSVUUpIOKGkgojICAgUnVuVU1BUCAocmV0dXJuLm1vZGVsPVRSVUUpCiMKIyBUaGVyZWZvcmU6CiMgICBub3JtYWxpemF0aW9uLm1ldGhvZCA9ICJTQ1QiICAgICAgICAobWF0Y2hlcyByZWZlcmVuY2UgaW50ZWdyYXRpb24pCiMgICByZWZlcmVuY2UucmVkdWN0aW9uICA9ICJwY2EiICAgICAgICAoUENBIGxpdmVzIG9uIGludGVncmF0ZWQgYXNzYXkpCiMgICByZWR1Y3Rpb24gICAgICAgICAgICA9ICJwY2Fwcm9qZWN0IiAodXNlcyBzYXZlZCBQQ0Egcm90YXRpb24gbWF0cml4KQojICAgUXVlcnkgbXVzdCBiZSBTQ1Qtbm9ybWFsaXNlZCBwZXIgc2FtcGxlIHRvIG1hdGNoIHJlZmVyZW5jZSBmZWF0dXJlIHNwYWNlCiMg4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQCgojIOKUgOKUgCBTdGVwIDE6IFNldCByZWZlcmVuY2UgZGVmYXVsdCBhc3NheSDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKRGVmYXVsdEFzc2F5KGNkNF9yZWYpIDwtICJpbnRlZ3JhdGVkIgpjYXQoIlJlZmVyZW5jZSBkZWZhdWx0IGFzc2F5OiIsIERlZmF1bHRBc3NheShjZDRfcmVmKSwgIlxuIikKY2F0KCJSZWZlcmVuY2UgY2VsbHM6IiwgbmNvbChjZDRfcmVmKSwgIlxuIikKY2F0KCJSZWZlcmVuY2UgUENBIGRpbXM6IiwgbmNvbChFbWJlZGRpbmdzKGNkNF9yZWYsICJwY2EiKSksICJcbiIpCmNhdCgiUENBIG1vZGVsIHByZXNlbnQ6IiwgbmNvbChjZDRfcmVmW1sicGNhIl1dQGZlYXR1cmUubG9hZGluZ3MpID4gMCwgIlxuIikKY2F0KCJVTUFQIG1vZGVsIHByZXNlbnQ6IiwgIWlzLm51bGwoY2Q0X3JlZltbInVtYXAiXV1AbWlzYyRtb2RlbCksICJcbiIpCgojIOKUgOKUgCBTdGVwIDI6IENlbGwgY3ljbGUgc2NvcmluZyBvbiBxdWVyeSDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKRGVmYXVsdEFzc2F5KHNlemFyeV9vYmopIDwtICJSTkEiCnNlemFyeV9vYmogPC0gTm9ybWFsaXplRGF0YShzZXphcnlfb2JqLCB2ZXJib3NlID0gRkFMU0UpCnNlemFyeV9vYmogPC0gQ2VsbEN5Y2xlU2NvcmluZygKICBzZXphcnlfb2JqLAogIHMuZmVhdHVyZXMgICA9IGNjLmdlbmVzLnVwZGF0ZWQuMjAxOSRzLmdlbmVzLAogIGcybS5mZWF0dXJlcyA9IGNjLmdlbmVzLnVwZGF0ZWQuMjAxOSRnMm0uZ2VuZXMsCiAgc2V0LmlkZW50ICAgID0gRkFMU0UKKQpjYXQoIlxuQ2VsbCBjeWNsZSBzY29yaW5nIGNvbXBsZXRlXG4iKQpwcmludCh0YWJsZShzZXphcnlfb2JqJFBoYXNlKSkKCiMg4pSA4pSAIFN0ZXAgMzogUGVyLXNhbXBsZSBTQ1Qgb24gcXVlcnkg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACiMgUmVmZXJlbmNlIHdhcyBTQ1QnZCBwZXIgZGF0YXNldCAoMyBtb2RlbHMpLiBRdWVyeSBtdXN0IG1hdGNoLgojIEVhY2ggY2VsbCBsaW5lIGlzIGEgc2VwYXJhdGUgbGlicmFyeSDigJQgcGVyLWxpbmUgU0NUIHByZXZlbnRzIGJhdGNoIGVmZmVjdHMKIyBmcm9tIGRvbWluYXRpbmcgSFZHIHNlbGVjdGlvbiBhbmQgYW5jaG9yIGZpbmRpbmcuCnNwbGl0X2NvbCA8LSAib3JpZy5pZGVudCIKY2F0KCJcblNwbGl0dGluZyBxdWVyeSBieToiLCBzcGxpdF9jb2wsICJcbiIpCnByaW50KHRhYmxlKHNlemFyeV9vYmpAbWV0YS5kYXRhW1tzcGxpdF9jb2xdXSkpCgpzZXphcnlfbGlzdCA8LSBTcGxpdE9iamVjdChzZXphcnlfb2JqLCBzcGxpdC5ieSA9IHNwbGl0X2NvbCkKY2F0KCJSdW5uaW5nIFNDVCBwZXIgc2FtcGxlICgiLCBsZW5ndGgoc2V6YXJ5X2xpc3QpLCAic2FtcGxlcykuLi5cbiIpCgpzZXphcnlfbGlzdCA8LSBsYXBwbHkoc2VxX2Fsb25nKHNlemFyeV9saXN0KSwgZnVuY3Rpb24oaSkgewogIGNhdCgiICBTQ1Qgc2FtcGxlIiwgaSwgIi8iLCBsZW5ndGgoc2V6YXJ5X2xpc3QpLAogICAgICAiLSIsIG5hbWVzKHNlemFyeV9saXN0KVtpXSwKICAgICAgIigiLCBuY29sKHNlemFyeV9saXN0W1tpXV0pLCAiY2VsbHMpXG4iKQogIFNDVHJhbnNmb3JtKAogICAgc2V6YXJ5X2xpc3RbW2ldXSwKICAgIHZzdC5mbGF2b3IgICAgICA9ICJ2MiIsCiAgICB2YXJzLnRvLnJlZ3Jlc3MgPSBjKCJTLlNjb3JlIiwgIkcyTS5TY29yZSIsICJwZXJjZW50Lm10IiksCiAgICB2ZXJib3NlICAgICAgICAgPSBGQUxTRQogICkKfSkKCnNlemFyeV9vYmogPC0gbWVyZ2Uoc2V6YXJ5X2xpc3RbWzFdXSwgc2V6YXJ5X2xpc3RbLTFdLCBtZXJnZS5kYXRhID0gVFJVRSkKY2F0KCLinIUgU0NUIGNvbXBsZXRlIOKAlCBtZXJnZWQgcXVlcnk6IiwgbmNvbChzZXphcnlfb2JqKSwgImNlbGxzXG4iKQoKIyDilIDilIAgU3RlcCA0OiBTZXQgc2hhcmVkIHZhcmlhYmxlIGZlYXR1cmVzIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgAojIFVzZSByZWZlcmVuY2UgaW50ZWdyYXRpb24gZmVhdHVyZXMgYXMgdGhlIGFuY2hvciBmZWF0dXJlIHNldC4KIyBUaGVzZSBhcmUgdGhlIDI5MDIgZ2VuZXMgdXNlZCB0byBidWlsZCB0aGUgcmVmZXJlbmNlIFBDQSDigJQgdXNpbmcgdGhlCiMgc2FtZSBnZW5lcyBlbnN1cmVzIHRoZSBwY2Fwcm9qZWN0IHJvdGF0aW9uIGlzIHZhbGlkLgpyZWZfZmVhdHVyZXMgICAgPC0gcm93bmFtZXMoY2Q0X3JlZltbImludGVncmF0ZWQiXV1Ac2NhbGUuZGF0YSkKcXVlcnlfZ2VuZXMgICAgIDwtIHJvd25hbWVzKHNlemFyeV9vYmpbWyJTQ1QiXV0pCnNoYXJlZF9mZWF0dXJlcyA8LSBpbnRlcnNlY3QocmVmX2ZlYXR1cmVzLCBxdWVyeV9nZW5lcykKCmNhdCgiXG5SZWZlcmVuY2UgaW50ZWdyYXRpb24gZmVhdHVyZXM6IiwgbGVuZ3RoKHJlZl9mZWF0dXJlcyksICJcbiIpCmNhdCgiUXVlcnkgU0NUIGdlbmVzOiAgICAgICAgICAgICAgIiwgbGVuZ3RoKHF1ZXJ5X2dlbmVzKSwgIlxuIikKY2F0KCJTaGFyZWQgZmVhdHVyZXM6ICAgICAgICAgICAgICAiLCBsZW5ndGgoc2hhcmVkX2ZlYXR1cmVzKSwgIlxuIikKCmlmIChsZW5ndGgoc2hhcmVkX2ZlYXR1cmVzKSA8IDE1MDApIHsKICB3YXJuaW5nKHBhc3RlMCgKICAgICLimqDvuI8gIE9ubHkgIiwgbGVuZ3RoKHNoYXJlZF9mZWF0dXJlcyksICIgc2hhcmVkIGZlYXR1cmVzLlxuIiwKICAgICIgICBNaW5pbXVtIHJlY29tbWVuZGVkIGlzIDE1MDAuXG4iLAogICAgIiAgIENoZWNrIHRoYXQgc2V6YXJ5X29iaiBSTkEgYXNzYXkgY29udGFpbnMgdGhlIHNhbWUgZ2VuZSBzZXQgYXMgcmVmZXJlbmNlLiIKICApKQp9CgojIOKUgOKUgCBTdGVwIDU6IENsZWFuIHNoYXJlZCBmZWF0dXJlcyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKIyBSZW1vdmUgcmlib3NvbWFsLCBtaXRvY2hvbmRyaWFsLCBUQ1IsIEhMQSBhbmQgb3RoZXIgYmF0Y2gtZHJpdmVuIGdlbmVzLgpqdW5rX3BhdHRlcm4gPC0gIl5SUEx8XlJQU3xeTVQtfF5IU1B8XkhTUEF8XkhTUEJ8XkhTUER8XkhTUEV8XkhTUEh8XlNOSEd8Xk1BTEFUMXxeTkVBVDF8XlhJU1R8XlRSQlZ8XlRSQVZ8XlRSR1Z8XlRSRFZ8XkhMQS0iCnNoYXJlZF9mZWF0dXJlc19jbGVhbiA8LSBzaGFyZWRfZmVhdHVyZXNbIWdyZXBsKGp1bmtfcGF0dGVybiwgc2hhcmVkX2ZlYXR1cmVzKV0KY2F0KCJcbkZlYXR1cmVzIGJlZm9yZSBjbGVhbmluZzoiLCBsZW5ndGgoc2hhcmVkX2ZlYXR1cmVzKSwgIlxuIikKY2F0KCJGZWF0dXJlcyBhZnRlciBjbGVhbmluZzogIiwgbGVuZ3RoKHNoYXJlZF9mZWF0dXJlc19jbGVhbiksICJcbiIpCmNhdCgiSnVuayBnZW5lcyByZW1vdmVkOiAgICAgICIsIGxlbmd0aChzaGFyZWRfZmVhdHVyZXMpIC0gbGVuZ3RoKHNoYXJlZF9mZWF0dXJlc19jbGVhbiksICJcbiIpCgojIFNhbml0eSBjaGVjazoga2V5IFQgY2VsbCBtYXJrZXJzIHByZXNlbnQ/Cm1hcmtlcnNfY2hlY2sgPC0gYygiQ0NSNyIsIlNFTEwiLCJUQ0Y3IiwiSUw3UiIsIkdaTUIiLCJQUkYxIiwKICAgICAgICAgICAgICAgICAgICJGT1hQMyIsIlRPWCIsIlBEQ0QxIiwiQ0Q0NCIsIkNENjkiLCJUTkYiLAogICAgICAgICAgICAgICAgICAgIlRJR0lUIiwiSEFWQ1IyIiwiRU9NRVMiLCJUQlgyMSIsIkNYQ1IzIiwiR1pNSyIpCmZvdW5kX21hcmtlcnMgPC0gbWFya2Vyc19jaGVja1ttYXJrZXJzX2NoZWNrICVpbiUgc2hhcmVkX2ZlYXR1cmVzX2NsZWFuXQpjYXQoIlxuS2V5IFQgY2VsbCBtYXJrZXJzIGluIGZlYXR1cmUgc2V0OiIsCiAgICBsZW5ndGgoZm91bmRfbWFya2VycyksICIvIiwgbGVuZ3RoKG1hcmtlcnNfY2hlY2spLCAiXG4iKQpwcmludChmb3VuZF9tYXJrZXJzKQoKaWYgKGxlbmd0aChmb3VuZF9tYXJrZXJzKSA8IDgpIHsKICB3YXJuaW5nKHBhc3RlMCgKICAgICLimqDvuI8gIEZld2VyIHRoYW4gOCBUIGNlbGwgbWFya2VycyBpbiBzaGFyZWQgZmVhdHVyZXMuXG4iLAogICAgIiAgIEZlYXR1cmUgc2V0IG1heSBiZSBkb21pbmF0ZWQgYnkgbm9uLXNwZWNpZmljIGdlbmVzLlxuIiwKICAgICIgICBJbnNwZWN0IGhlYWQoc2hhcmVkX2ZlYXR1cmVzX2NsZWFuLCAzMCkuIgogICkpCn0KClZhcmlhYmxlRmVhdHVyZXMoc2V6YXJ5X29iaikgPC0gc2hhcmVkX2ZlYXR1cmVzX2NsZWFuCkRlZmF1bHRBc3NheShzZXphcnlfb2JqKSAgICAgPC0gIlNDVCIKCiMg4pSA4pSAIFN0ZXAgNjogRmluZCB0cmFuc2ZlciBhbmNob3JzIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgApjYXQoIlxuPT09IEZpbmRpbmcgdHJhbnNmZXIgYW5jaG9ycyA9PT1cbiIpCmNhdCgiUmVmZXJlbmNlIGNlbGxzOiIsIG5jb2woY2Q0X3JlZiksICJcbiIpCmNhdCgiUXVlcnkgY2VsbHM6ICAgICIsIG5jb2woc2V6YXJ5X29iaiksICJcbiIpCmNhdCgiRmVhdHVyZXM6ICAgICAgICIsIGxlbmd0aChzaGFyZWRfZmVhdHVyZXNfY2xlYW4pLCAiXG4iKQoKIyBkaW1zOiB1c2UgcmVmZXJlbmNlIFBDQSBvbmx5IOKAlCBwY2Fwcm9qZWN0IGRvZXMgbm90IHJlcXVpcmUgcXVlcnkgUENBCmRpbXNfdG9fdXNlIDwtIG1pbigzMCwgbmNvbChFbWJlZGRpbmdzKGNkNF9yZWYsICJwY2EiKSkpCmNhdCgiVXNpbmcgZGltcyAxOiIsIGRpbXNfdG9fdXNlLCAiXG4iKQoKYW5jaG9ycyA8LSBGaW5kVHJhbnNmZXJBbmNob3JzKAogIHJlZmVyZW5jZSAgICAgICAgICAgID0gY2Q0X3JlZiwKICBxdWVyeSAgICAgICAgICAgICAgICA9IHNlemFyeV9vYmosCiAgZmVhdHVyZXMgICAgICAgICAgICAgPSBzaGFyZWRfZmVhdHVyZXNfY2xlYW4sCiAgbm9ybWFsaXphdGlvbi5tZXRob2QgPSAiU0NUIiwgICAgICAgICMg4pyFIG1hdGNoZXMgU0NULVJQQ0EgcmVmZXJlbmNlCiAgcmVmZXJlbmNlLnJlZHVjdGlvbiAgPSAicGNhIiwgICAgICAgICMg4pyFIFBDQSBvbiBpbnRlZ3JhdGVkIGFzc2F5CiAgcmVkdWN0aW9uICAgICAgICAgICAgPSAicGNhcHJvamVjdCIsICMg4pyFIHByb2plY3QgaW50byByZWZlcmVuY2UgUENBIHNwYWNlCiAgZGltcyAgICAgICAgICAgICAgICAgPSAxOmRpbXNfdG9fdXNlLAogIGsuYW5jaG9yICAgICAgICAgICAgID0gMTAsICAgICAgICAgICAjIGRlZmF1bHQgNSAg4oaSIG1vcmUgYW5jaG9yIGNhbmRpZGF0ZXMKICBrLmZpbHRlciAgICAgICAgICAgICA9IDUwMCwgICAgICAgICAgIyBkZWZhdWx0IDIwMCDihpIgbGVzcyBwcnVuaW5nIG9uIGxhcmdlIHF1ZXJ5CiAgay5zY29yZSAgICAgICAgICAgICAgPSAzMCwgICAgICAgICAgICMgZGVmYXVsdCAzMCAg4oaSIHVuY2hhbmdlZAogIHZlcmJvc2UgICAgICAgICAgICAgID0gVFJVRQopCgpuX2FuY2hvcnMgICAgPC0gbnJvdyhhbmNob3JzQGFuY2hvcnMpCmFuY2hvcl9yYXRpbyA8LSBuY29sKHNlemFyeV9vYmopIC8gbl9hbmNob3JzCmNhdCgiXG5BbmNob3JzIGZvdW5kOiAgICAiLCBuX2FuY2hvcnMsICJcbiIpCmNhdCgiQ2VsbHMgcGVyIGFuY2hvcjogIiwgcm91bmQoYW5jaG9yX3JhdGlvLCAxKSwgIlxuIikKCmlmIChhbmNob3JfcmF0aW8gPiA4KSB7CiAgd2FybmluZyhwYXN0ZTAoCiAgICAi4pqg77iPICBMb3cgYW5jaG9yIGRlbnNpdHkgKDE6Iiwgcm91bmQoYW5jaG9yX3JhdGlvLCAxKSwgIikuXG4iLAogICAgIiAgIElkZWFsIGlzIDE6NSBvciBiZXR0ZXIuXG4iLAogICAgIiAgIENoZWNrIFQgY2VsbCBtYXJrZXJzIGZvdW5kIGFib3ZlIOKAlCB3YW50IOKJpSA4LzE4LlxuIiwKICAgICIgICBJbnNwZWN0IGhlYWQoc2hhcmVkX2ZlYXR1cmVzX2NsZWFuLCAzMCkgZm9yIGJhdGNoIGdlbmVzLiIKICApKQp9IGVsc2UgewogIGNhdCgi4pyFIEFuY2hvciBkZW5zaXR5IGFjY2VwdGFibGVcbiIpCn0KCiMg4pSA4pSAIFN0ZXAgNzogTWFwUXVlcnkg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACmNhdCgiXG49PT0gUnVubmluZyBNYXBRdWVyeSA9PT1cbiIpCnNlemFyeV9vYmogPC0gTWFwUXVlcnkoCiAgYW5jaG9yc2V0ICAgICAgICAgICA9IGFuY2hvcnMsCiAgcXVlcnkgICAgICAgICAgICAgICA9IHNlemFyeV9vYmosCiAgcmVmZXJlbmNlICAgICAgICAgICA9IGNkNF9yZWYsCiAgcmVmZGF0YSAgICAgICAgICAgICA9IGxpc3QoCiAgICBwcmVkaWN0ZWRfc3RhdGUgICAgID0gU1RBVEVfQ09MLCAgICAgIyBwcmVkaWN0ZWQuY2VsbHR5cGUubDIKICAgIHByZWRpY3RlZF9taWxlc3RvbmUgPSAibWlsZXN0b25lIiAgICAjIE1TVCBtaWxlc3RvbmUgbGFiZWxzIChNMDAtTTA2KQogICksCiAgcmVmZXJlbmNlLnJlZHVjdGlvbiA9ICJwY2EiLCAgICAgICAgICAgIyBiYXNpcyBmb3IgVU1BUCBwcm9qZWN0aW9uCiAgcmVkdWN0aW9uLm1vZGVsICAgICA9ICJ1bWFwIiwgICAgICAgICAgIyB1c2VzIHNhdmVkIFVNQVAgbW9kZWwKICB2ZXJib3NlICAgICAgICAgICAgID0gRkFMU0UKKQpjYXQoIuKchSBNYXBRdWVyeSBjb21wbGV0ZVxuIikKY2F0KCJNYXBwZWQgY2VsbHM6IiwgbmNvbChzZXphcnlfb2JqKSwgIlxuIikKCiMg4pSA4pSAIFN0ZXAgODogQ29lcmNlIG51bWVyaWMgdHJhbnNmZXJzIGZyb20gY2hhcmFjdGVyIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgApmb3IgKGNvbCBpbiBjb2xuYW1lcyhzZXphcnlfb2JqQG1ldGEuZGF0YSkpIHsKICBpZiAoZ3JlcGwoInNjb3JlJCIsIGNvbCwgaWdub3JlLmNhc2UgPSBUUlVFKSkgewogICAgc2V6YXJ5X29iakBtZXRhLmRhdGFbW2NvbF1dIDwtIGFzLm51bWVyaWMoc2V6YXJ5X29iakBtZXRhLmRhdGFbW2NvbF1dKQogIH0KfQoKIyDilIDilIAgU3RlcCA5OiBWYWxpZGF0ZSByZXN1bHRzIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgApjYXQoIlxuUHJlZGljdGVkIHN0YXRlIGRpc3RyaWJ1dGlvbjpcbiIpCnByaW50KHRhYmxlKHNlemFyeV9vYmokcHJlZGljdGVkLnByZWRpY3RlZF9zdGF0ZSkpCgpjYXQoIlxuUHJlZGljdGlvbiBzY29yZSBzdW1tYXJ5OlxuIikKcHJpbnQoc3VtbWFyeShzZXphcnlfb2JqJHByZWRpY3RlZC5wcmVkaWN0ZWRfc3RhdGUuc2NvcmUpKQoKY2F0KCJcblByZWRpY3RlZCBtaWxlc3RvbmUgZGlzdHJpYnV0aW9uOlxuIikKcHJpbnQodGFibGUoc2V6YXJ5X29iaiRwcmVkaWN0ZWQucHJlZGljdGVkX21pbGVzdG9uZSkpCgpzdGF0ZV9wY3QgPC0gcHJvcC50YWJsZSh0YWJsZShzZXphcnlfb2JqJHByZWRpY3RlZC5wcmVkaWN0ZWRfc3RhdGUpKSAqIDEwMApkb21pbmFudCAgPC0gbmFtZXMod2hpY2goc3RhdGVfcGN0ID4gNjApKQppZiAobGVuZ3RoKGRvbWluYW50KSA+IDApIHsKICB3YXJuaW5nKHBhc3RlMCgKICAgICLimqDvuI8gIFN0YXRlIGNvbGxhcHNlIGRldGVjdGVkOiAiLCBkb21pbmFudCwKICAgICIgY29udGFpbnMgIiwgcm91bmQobWF4KHN0YXRlX3BjdCksIDEpLCAiJSBvZiBjZWxscy5cbiIsCiAgICAiICAgQ2hlY2sgYW5jaG9yIGRlbnNpdHkgYW5kIHNoYXJlZCBmZWF0dXJlIHF1YWxpdHkgYWJvdmUuIgogICkpCn0gZWxzZSB7CiAgY2F0KCLinIUgU3RhdGUgZGlzdHJpYnV0aW9uIGlzIGRpdmVyc2Ug4oCUIG5vIGNvbGxhcHNlIGRldGVjdGVkXG4iKQp9CmBgYAoKIyMgQXNzaWduIFBzZXVkb3RpbWUgdmlhIEJvdGggTWV0aG9kcwoKYGBge3Igc2V6YXJ5LXBzZXVkb3RpbWV9CnNlel91bWFwX2Nvb3JkcyAgICAgICAgICAgPC0gYXMubWF0cml4KEVtYmVkZGluZ3Moc2V6YXJ5X29iaiwgInJlZi51bWFwIikpCmNvbG5hbWVzKHNlel91bWFwX2Nvb3JkcykgPC0gYygiVU1BUF8xIiwiVU1BUF8yIikKCmNhdCgiUHJvamVjdGluZyBTw6l6YXJ5IGNlbGxzIG9udG8gTVNULi4uXG4iKQpzZXpfbXN0IDwtIHByb2plY3Rfb250b19tc3Qoc2V6X3VtYXBfY29vcmRzLCBjZW50cm9pZF9tYXQsIG1zdF9ncmFwaCwgZ19kaXN0c192ZWMpCgpyZWZfdW1hcF9tYXQgPC0gRW1iZWRkaW5ncyhjZDRfcmVmLCBVTUFQX05BTUUpCnJlZl9tM19wdCAgICA8LSBjZDRfcmVmQG1ldGEuZGF0YSRtb25vY2xlM19wc2V1ZG90aW1lX25vcm0KCm5uX3Jlc3VsdCA8LSBubjIoZGF0YT1yZWZfdW1hcF9tYXQsIHF1ZXJ5PXNlel91bWFwX2Nvb3Jkcywgaz0xNSkKaW52X2Rpc3QgIDwtIDEgLyAobm5fcmVzdWx0JG5uLmRpc3RzICsgMWUtNikKd2VpZ2h0cyAgIDwtIGludl9kaXN0IC8gcm93U3VtcyhpbnZfZGlzdCkKCm1ldGFfc2V6IDwtIHNlemFyeV9vYmpAbWV0YS5kYXRhCm1ldGFfc2V6JG1zdF9wc2V1ZG90aW1lIDwtIHBtYXgoMCwgcG1pbigxMDAsCiAgMTAwICogKHNlel9tc3QkcHNldWRvdGltZSAtIG1zdF9wdF9yYW5nZVsxXSkgLwogICAgICAgICAobXN0X3B0X3JhbmdlWzJdIC0gbXN0X3B0X3JhbmdlWzFdKQopKQptZXRhX3NleiRuZWFyZXN0X21pbGVzdG9uZV9tc3QgPC0gc2V6X21zdCRuZWFyZXN0X21zCm1ldGFfc2V6JG1vbm9jbGUzX3BzZXVkb3RpbWUgICA8LSByb3dTdW1zKAogIHdlaWdodHMgKiBtYXRyaXgocmVmX20zX3B0W25uX3Jlc3VsdCRubi5pZHhdLCBucm93PW5yb3cobm5fcmVzdWx0JG5uLmlkeCkpCikKc2V6YXJ5X29iakBtZXRhLmRhdGEgPC0gbWV0YV9zZXoKCmNhdCgiTVNUIHBzZXVkb3RpbWU6Iiwgcm91bmQocmFuZ2Uoc2V6YXJ5X29iakBtZXRhLmRhdGEkbXN0X3BzZXVkb3RpbWUsIG5hLnJtPVRSVUUpLCAxKSwgIlxuIikKY2F0KCJNb25vY2xlMyBwc2V1ZG90aW1lOiIsIHJvdW5kKHJhbmdlKHNlemFyeV9vYmpAbWV0YS5kYXRhJG1vbm9jbGUzX3BzZXVkb3RpbWUsIG5hLnJtPVRSVUUpLCAxKSwgIlxuIikKCmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIFByb2plY3Rpb24gVmlzdWFsaXNhdGlvbgoKIyMgQ3VzdG9tIE1TVAoKYGBge3Igc2V6LXBsb3QtbXN0LCBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD0xMH0KcmVmX2JnIDwtIGRhdGEuZnJhbWUoCiAgVU1BUF8xID0gRW1iZWRkaW5ncyhjZDRfcmVmLCBVTUFQX05BTUUpWywxXSwKICBVTUFQXzIgPSBFbWJlZGRpbmdzKGNkNF9yZWYsIFVNQVBfTkFNRSlbLDJdCikKCnNlel9wbG90X2RmIDwtIGRhdGEuZnJhbWUoCiAgVU1BUF8xICAgICAgICAgID0gc2V6X3VtYXBfY29vcmRzWywiVU1BUF8xIl0sCiAgVU1BUF8yICAgICAgICAgID0gc2V6X3VtYXBfY29vcmRzWywiVU1BUF8yIl0sCiAgcHJlZGljdGVkX3N0YXRlID0gc2V6YXJ5X29iakBtZXRhLmRhdGEkcHJlZGljdGVkLnByZWRpY3RlZF9zdGF0ZSwKICBwcmVkX3Njb3JlICAgICAgPSBzZXphcnlfb2JqQG1ldGEuZGF0YSRwcmVkaWN0ZWQucHJlZGljdGVkX3N0YXRlLnNjb3JlLAogIG1zdF9wdCAgICAgICAgICA9IHNlemFyeV9vYmpAbWV0YS5kYXRhJG1zdF9wc2V1ZG90aW1lLAogIG0zX3B0ICAgICAgICAgICA9IHNlemFyeV9vYmpAbWV0YS5kYXRhJG1vbm9jbGUzX3BzZXVkb3RpbWUsCiAgbWlsZXN0b25lX3JlZiAgID0gc2V6YXJ5X29iakBtZXRhLmRhdGEkbmVhcmVzdF9taWxlc3RvbmVfbXN0LAogIGNlbGxfbGluZSAgICAgICA9IHNlemFyeV9vYmpAbWV0YS5kYXRhJG9yaWcuaWRlbnQKKQoKcF9zZXpfbXN0X3N0YXRlIDwtIGdncGxvdCgpICsKICBnZW9tX3BvaW50KGRhdGE9cmVmX2JnW3NhbXBsZShucm93KHJlZl9iZykpLF0sCiAgICAgICAgICAgICBhZXMoeD1VTUFQXzEseT1VTUFQXzIpLCBjb2xvdXI9ImdyZXk4NSIsIHNpemU9LjMsIGFscGhhPS40KSArCiAgZ2VvbV9zZWdtZW50KGRhdGE9bXN0X2VkZ2VfZGYsIGFlcyh4PXgxLHk9eTEseGVuZD14Mix5ZW5kPXkyKSwKICAgICAgICAgICAgICAgY29sb3VyPSJncmV5MzAiLCBsaW5ld2lkdGg9LjgsIGFscGhhPS43KSArCiAgZ2VvbV9wb2ludChkYXRhPXNlel9wbG90X2RmLAogICAgICAgICAgICAgYWVzKHg9VU1BUF8xLHk9VU1BUF8yLGNvbG91cj1wcmVkaWN0ZWRfc3RhdGUpLCBzaXplPTEuOCwgYWxwaGE9Ljg1KSArCiAgZ2VvbV90ZXh0X3JlcGVsKGRhdGE9bWlsZXN0b25lX2NlbnRyb2lkcywKICAgICAgICAgICAgICAgICAgYWVzKHg9VU1BUF8xLHk9VU1BUF8yLGxhYmVsPW1pbGVzdG9uZSksCiAgICAgICAgICAgICAgICAgIHNpemU9Mi41LCBjb2xvdXI9ImdyZXkyMCIsIGZvbnRmYWNlPSJib2xkIiwKICAgICAgICAgICAgICAgICAgbWF4Lm92ZXJsYXBzPTIwLCBzZWdtZW50LnNpemU9LjMpICsKICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcz1TVEFURV9DT0xPUlMsIG5hbWU9IlByZWRpY3RlZCBzdGF0ZSIpICsKICB0aGVtZV9jbGFzc2ljKCkgKwogIGxhYnMoeD0iVU1BUC0xIiwgeT0iVU1BUC0yIiwKICAgICAgIHRpdGxlPSJTw6l6YXJ5IGNlbGxzIOKAlCBDdXN0b20gTVNUIHByb2plY3Rpb24gKHByZWRpY3RlZCBzdGF0ZSkiLAogICAgICAgc3VidGl0bGU9IkdyZXkgPSBub3JtYWwgQ0Q0IHJlZmVyZW5jZSB8IENvbG91cmVkID0gU8OpemFyeSB8IExpbmVzID0gTVNUIikgKwogIHRoZW1lKHBsb3QudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9MTMsIGZhY2U9ImJvbGQiKSkKCnBfc2V6X21zdF9wdCA8LSBnZ3Bsb3QoKSArCiAgZ2VvbV9wb2ludChkYXRhPXJlZl9iZ1tzYW1wbGUobnJvdyhyZWZfYmcpKSxdLAogICAgICAgICAgICAgYWVzKHg9VU1BUF8xLHk9VU1BUF8yKSwgY29sb3VyPSJncmV5ODUiLCBzaXplPS4zLCBhbHBoYT0uNCkgKwogIGdlb21fc2VnbWVudChkYXRhPW1zdF9lZGdlX2RmLCBhZXMoeD14MSx5PXkxLHhlbmQ9eDIseWVuZD15MiksCiAgICAgICAgICAgICAgIGNvbG91cj0iZ3JleTMwIiwgbGluZXdpZHRoPS43LCBhbHBoYT0uNikgKwogIGdlb21fcG9pbnQoZGF0YT1zZXpfcGxvdF9kZiwKICAgICAgICAgICAgIGFlcyh4PVVNQVBfMSx5PVVNQVBfMixjb2xvdXI9bXN0X3B0KSwgc2l6ZT0xLjgsIGFscGhhPS45KSArCiAgc2NhbGVfY29sb3VyX2dyYWRpZW50bigKICAgIGNvbG91cnM9YygiIzBEMDg4NyIsIiM2QTAwQTgiLCIjQjEyQTkwIiwiI0UxNjQ2MiIsIiNGQ0E2MzYiLCIjRjBGOTIxIiksCiAgICBuYW1lPSJNU1RcbnBzZXVkb3RpbWUiLCBsaW1pdHM9YygwLDEwMCkpICsKICB0aGVtZV9jbGFzc2ljKCkgKwogIGxhYnMoeD0iVU1BUC0xIiwgeT0iVU1BUC0yIiwgdGl0bGU9IlPDqXphcnkg4oCUIE1TVCBwcm9qZWN0ZWQgcHNldWRvdGltZSIpICsKICB0aGVtZShwbG90LnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTEzLCBmYWNlPSJib2xkIikpCgpwcmludChwX3Nlel9tc3Rfc3RhdGUgLyBwX3Nlel9tc3RfcHQpCmBgYAoKIyMgTW9ub2NsZTMKCmBgYHtyIHNlei1wbG90LW0zLCBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD0xMH0KcF9zZXpfbTNfc3RhdGUgPC0gZ2dwbG90KCkgKwogIGdlb21fcG9pbnQoZGF0YT1yZWZfYmdbc2FtcGxlKG5yb3cocmVmX2JnKSksXSwKICAgICAgICAgICAgIGFlcyh4PVVNQVBfMSx5PVVNQVBfMiksIGNvbG91cj0iZ3JleTg1Iiwgc2l6ZT0uMywgYWxwaGE9LjQpICsKICBnZW9tX3NlZ21lbnQoZGF0YT1tM19lZGdlX2RmLCBhZXMoeD14MSx5PXkxLHhlbmQ9eDIseWVuZD15MiksCiAgICAgICAgICAgICAgIGNvbG91cj0iZ3JleTMwIiwgbGluZXdpZHRoPS42LCBhbHBoYT0uNykgKwogIGdlb21fcG9pbnQoZGF0YT1zZXpfcGxvdF9kZiwKICAgICAgICAgICAgIGFlcyh4PVVNQVBfMSx5PVVNQVBfMixjb2xvdXI9cHJlZGljdGVkX3N0YXRlKSwgc2l6ZT0xLjgsIGFscGhhPS44NSkgKwogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzPVNUQVRFX0NPTE9SUywgbmFtZT0iUHJlZGljdGVkIHN0YXRlIikgKwogIHRoZW1lX2NsYXNzaWMoKSArCiAgbGFicyh4PSJVTUFQLTEiLCB5PSJVTUFQLTIiLAogICAgICAgdGl0bGU9IlPDqXphcnkgY2VsbHMg4oCUIE1vbm9jbGUzIHByb2plY3Rpb24gKHByZWRpY3RlZCBzdGF0ZSkiKSArCiAgdGhlbWUocGxvdC50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xMywgZmFjZT0iYm9sZCIpKQoKcF9zZXpfbTNfcHQgPC0gZ2dwbG90KCkgKwogIGdlb21fcG9pbnQoZGF0YT1yZWZfYmdbc2FtcGxlKG5yb3cocmVmX2JnKSksXSwKICAgICAgICAgICAgIGFlcyh4PVVNQVBfMSx5PVVNQVBfMiksIGNvbG91cj0iZ3JleTg1Iiwgc2l6ZT0uMywgYWxwaGE9LjQpICsKICBnZW9tX3NlZ21lbnQoZGF0YT1tM19lZGdlX2RmLCBhZXMoeD14MSx5PXkxLHhlbmQ9eDIseWVuZD15MiksCiAgICAgICAgICAgICAgIGNvbG91cj0iZ3JleTMwIiwgbGluZXdpZHRoPS41LCBhbHBoYT0uNikgKwogIGdlb21fcG9pbnQoZGF0YT1zZXpfcGxvdF9kZiwKICAgICAgICAgICAgIGFlcyh4PVVNQVBfMSx5PVVNQVBfMixjb2xvdXI9bTNfcHQpLCBzaXplPTEuOCwgYWxwaGE9LjkpICsKICBzY2FsZV9jb2xvdXJfZ3JhZGllbnRuKAogICAgY29sb3Vycz1jKCIjMEQwODg3IiwiIzZBMDBBOCIsIiNCMTJBOTAiLCIjRTE2NDYyIiwiI0ZDQTYzNiIsIiNGMEY5MjEiKSwKICAgIG5hbWU9Ik1vbm9jbGUzXG5wc2V1ZG90aW1lIiwgbGltaXRzPWMoMCwxMDApKSArCiAgdGhlbWVfY2xhc3NpYygpICsKICBsYWJzKHg9IlVNQVAtMSIsIHk9IlVNQVAtMiIsIHRpdGxlPSJTw6l6YXJ5IOKAlCBNb25vY2xlMyBwcm9qZWN0ZWQgcHNldWRvdGltZSIpICsKICB0aGVtZShwbG90LnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTEzLCBmYWNlPSJib2xkIikpCgpwcmludChwX3Nlel9tM19zdGF0ZSAvIHBfc2V6X20zX3B0KQpgYGAKCiMjIE1ldGhvZCBBZ3JlZW1lbnQKCmBgYHtyIHNlei1tZXRob2QtYWdyZWVtZW50LCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9NX0Kc2V6X2NvciA8LSBjb3Ioc2V6X3Bsb3RfZGYkbXN0X3B0LCBzZXpfcGxvdF9kZiRtM19wdCwKICAgICAgICAgICAgICAgbWV0aG9kPSJzcGVhcm1hbiIsIHVzZT0iY29tcGxldGUub2JzIikKCnBfc2V6X3NjYXR0ZXIgPC0gZ2dwbG90KHNlel9wbG90X2RmLCBhZXMoeD1tc3RfcHQsIHk9bTNfcHQsIGNvbG91cj1wcmVkaWN0ZWRfc3RhdGUpKSArCiAgZ2VvbV9wb2ludChzaXplPTEuMiwgYWxwaGE9LjcpICsKICBnZW9tX2FibGluZShzbG9wZT0xLCBpbnRlcmNlcHQ9MCwgbGluZXR5cGU9ImRhc2hlZCIsIGNvbG91cj0iZ3JleTQwIikgKwogIGdlb21fc21vb3RoKG1ldGhvZD0ibG0iLCBzZT1GQUxTRSwgY29sb3VyPSJibGFjayIsIGxpbmV3aWR0aD0uOCkgKwogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzPVNUQVRFX0NPTE9SUywgbmFtZT0iUHJlZGljdGVkIHN0YXRlIikgKwogIGFubm90YXRlKCJ0ZXh0IiwgeD01LCB5PTkwLCBsYWJlbD1zcHJpbnRmKCJTcGVhcm1hbiDPgSA9ICUuM2YiLCBzZXpfY29yKSwKICAgICAgICAgICBzaXplPTQuNSwgZm9udGZhY2U9ImJvbGQiLCBoanVzdD0wKSArCiAgdGhlbWVfY2xhc3NpYygpICsKICBsYWJzKHg9IkN1c3RvbSBNU1QgcHNldWRvdGltZSIsIHk9Ik1vbm9jbGUzIHBzZXVkb3RpbWUiLAogICAgICAgdGl0bGU9IlPDqXphcnkgY2VsbHM6IHBzZXVkb3RpbWUgbWV0aG9kIGFncmVlbWVudCIpICsKICB0aGVtZShwbG90LnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTEyLCBmYWNlPSJib2xkIikpCgpwX3Nlel92aW9saW4gPC0gZ2dwbG90KAogIHNlel9wbG90X2RmICU+JQogICAgcGl2b3RfbG9uZ2VyKGMobXN0X3B0LG0zX3B0KSwgbmFtZXNfdG89Im1ldGhvZCIsIHZhbHVlc190bz0icHQiKSAlPiUKICAgIG11dGF0ZShtZXRob2Q9cmVjb2RlKG1ldGhvZCwgbXN0X3B0PSJDdXN0b20gTVNUIiwgbTNfcHQ9Ik1vbm9jbGUzIikpLAogIGFlcyh4PXByZWRpY3RlZF9zdGF0ZSwgeT1wdCwgZmlsbD1tZXRob2QpCikgKwogIGdlb21fdmlvbGluKHNjYWxlPSJ3aWR0aCIsIHRyaW09RkFMU0UsIGFscGhhPS43LCBwb3NpdGlvbj1wb3NpdGlvbl9kb2RnZSguOCkpICsKICBnZW9tX2JveHBsb3Qod2lkdGg9LjA4LCBmaWxsPSJ3aGl0ZSIsIG91dGxpZXIuc2l6ZT0uMywgcG9zaXRpb249cG9zaXRpb25fZG9kZ2UoLjgpKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPU1FVEhPRF9DT0xPUlMsIG5hbWU9Ik1ldGhvZCIpICsKICB0aGVtZV9jbGFzc2ljKCkgKwogIGxhYnMoeD0iUHJlZGljdGVkIHN0YXRlIiwgeT0iUHNldWRvdGltZSAoMOKAkzEwMCkiLAogICAgICAgdGl0bGU9IlPDqXphcnkgcHNldWRvdGltZSBwZXIgc3RhdGU6IG1ldGhvZCBjb21wYXJpc29uIikgKwogIHRoZW1lKHBsb3QudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9MTIsIGZhY2U9ImJvbGQiKSwKICAgICAgICBheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9MzAsIGhqdXN0PTEpKQoKcHJpbnQocF9zZXpfc2NhdHRlciB8IHBfc2V6X3Zpb2xpbikKYGBgCgojIyBDZWxsIExpbmUgQnJlYWtkb3duCgpgYGB7ciBzZXotY2VsbC1saW5lLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9NX0KIyBTdGF0ZSBkaXN0cmlidXRpb24gcGVyIGNlbGwgbGluZQpjZWxsX2xpbmVfc3RhdGUgPC0gc2V6X3Bsb3RfZGYgJT4lCiAgY291bnQoY2VsbF9saW5lLCBwcmVkaWN0ZWRfc3RhdGUpICU+JQogIGdyb3VwX2J5KGNlbGxfbGluZSkgJT4lCiAgbXV0YXRlKHBjdCA9IHJvdW5kKDEwMCAqIG4gLyBzdW0obiksIDEpKSAlPiUKICB1bmdyb3VwKCkKCnBfY2VsbF9saW5lIDwtIGdncGxvdChjZWxsX2xpbmVfc3RhdGUsCiAgICAgICAgICAgICAgICAgICAgICAgYWVzKHg9Y2VsbF9saW5lLCB5PXBjdCwgZmlsbD1wcmVkaWN0ZWRfc3RhdGUpKSArCiAgZ2VvbV9jb2wod2lkdGg9LjcpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9U1RBVEVfQ09MT1JTLCBuYW1lPSJQcmVkaWN0ZWQgc3RhdGUiKSArCiAgdGhlbWVfY2xhc3NpYygpICsKICBsYWJzKHg9IkNlbGwgbGluZSIsIHk9IiUgY2VsbHMiLAogICAgICAgdGl0bGU9IlByZWRpY3RlZCBzdGF0ZSBkaXN0cmlidXRpb24gcGVyIGNlbGwgbGluZSIpICsKICB0aGVtZShwbG90LnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTEyLCBmYWNlPSJib2xkIikpCgojIFBzZXVkb3RpbWUgcGVyIGNlbGwgbGluZQpwX3B0X2NlbGxfbGluZSA8LSBnZ3Bsb3Qoc2V6X3Bsb3RfZGYsCiAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKHg9Y2VsbF9saW5lLCB5PW1zdF9wdCwgZmlsbD1jZWxsX2xpbmUpKSArCiAgZ2VvbV92aW9saW4oc2NhbGU9IndpZHRoIiwgdHJpbT1GQUxTRSwgYWxwaGE9LjgpICsKICBnZW9tX2JveHBsb3Qod2lkdGg9LjA4LCBmaWxsPSJ3aGl0ZSIsIG91dGxpZXIuc2l6ZT0uMykgKwogIHRoZW1lX2NsYXNzaWMoKSArCiAgbGFicyh4PSJDZWxsIGxpbmUiLCB5PSJNU1QgcHNldWRvdGltZSAoMOKAkzEwMCkiLAogICAgICAgdGl0bGU9IlBzZXVkb3RpbWUgZGlzdHJpYnV0aW9uIHBlciBjZWxsIGxpbmUiKSArCiAgdGhlbWUocGxvdC50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xMiwgZmFjZT0iYm9sZCIpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpCgpwcmludChwX2NlbGxfbGluZSB8IHBfcHRfY2VsbF9saW5lKQpgYGAKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBTdW1tYXJ5IFRhYmxlcwoKYGBge3Igc2V6YXJ5LXN1bW1hcnksIGZpZy53aWR0aD03LCBmaWcuaGVpZ2h0PTV9CnNlel9zdW1tYXJ5IDwtIHNlel9wbG90X2RmICU+JQogIGdyb3VwX2J5KHByZWRpY3RlZF9zdGF0ZSkgJT4lCiAgc3VtbWFyaXNlKAogICAgbl9jZWxscyAgICAgICAgID0gbigpLAogICAgcGN0ICAgICAgICAgICAgID0gcm91bmQoMTAwKm4oKS9ucm93KHNlel9wbG90X2RmKSwgMSksCiAgICBtZWFuX3ByZWRfc2NvcmUgPSByb3VuZChtZWFuKHByZWRfc2NvcmUpLCAzKSwKICAgIG1zdF9tZWFuX3B0ICAgICA9IHJvdW5kKG1lYW4obXN0X3B0LCBuYS5ybT1UUlVFKSwgMSksCiAgICBtc3Rfc2RfcHQgICAgICAgPSByb3VuZChzZChtc3RfcHQsICAgbmEucm09VFJVRSksIDEpLAogICAgbTNfbWVhbl9wdCAgICAgID0gcm91bmQobWVhbihtM19wdCwgIG5hLnJtPVRSVUUpLCAxKSwKICAgIG0zX3NkX3B0ICAgICAgICA9IHJvdW5kKHNkKG0zX3B0LCAgICBuYS5ybT1UUlVFKSwgMSksCiAgICAuZ3JvdXBzICAgICAgICAgPSAiZHJvcCIKICApICU+JSBhcnJhbmdlKG1zdF9tZWFuX3B0KQoKa2FibGUoc2V6X3N1bW1hcnksCiAgICAgIGNvbC5uYW1lcyA9IGMoIlN0YXRlIiwiTiIsIiUgb2YgU8OpemFyeSIsIlByZWQuIHNjb3JlIiwKICAgICAgICAgICAgICAgICAgICAiTVNUIG1lYW4gcHQiLCJNU1QgU0QiLCJNMyBtZWFuIHB0IiwiTTMgU0QiKSwKICAgICAgY2FwdGlvbiA9ICJTw6l6YXJ5IGNlbGwgdHJhamVjdG9yeSBzdW1tYXJ5IOKAlCBib3RoIG1ldGhvZHMiKSAlPiUKICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zPWMoInN0cmlwZWQiLCJob3ZlciIpLCBmdWxsX3dpZHRoPUZBTFNFKSAlPiUKICBjb2x1bW5fc3BlYyg1OjYsIGNvbG9yPSJ3aGl0ZSIsIGJhY2tncm91bmQ9TUVUSE9EX0NPTE9SU1siQ3VzdG9tIE1TVCJdKSAlPiUKICBjb2x1bW5fc3BlYyg3OjgsIGNvbG9yPSJ3aGl0ZSIsIGJhY2tncm91bmQ9TUVUSE9EX0NPTE9SU1siTW9ub2NsZTMiXSkKCm1zX2Rpc3QgPC0gc2V6X3Bsb3RfZGYgJT4lCiAgY291bnQobWlsZXN0b25lX3JlZikgJT4lCiAgbGVmdF9qb2luKG1pbGVzdG9uZV9jZW50cm9pZHNbLGMoIm1pbGVzdG9uZSIsInN0YXRlIiwib3JkZXJfcG9zIildLAogICAgICAgICAgICBieT1jKCJtaWxlc3RvbmVfcmVmIj0ibWlsZXN0b25lIikpICU+JQogIG11dGF0ZShwY3Q9cm91bmQoMTAwKm4vc3VtKG4pLDEpLAogICAgICAgICBtaWxlc3RvbmVfcmVmPWZhY3RvcihtaWxlc3RvbmVfcmVmLCBsZXZlbHM9bWlsZXN0b25lX29yZGVyKSkgJT4lCiAgYXJyYW5nZShvcmRlcl9wb3MpCgpwX21zX2JhciA8LSBnZ3Bsb3QobXNfZGlzdCwgYWVzKHg9bWlsZXN0b25lX3JlZiwgeT1wY3QsIGZpbGw9c3RhdGUpKSArCiAgZ2VvbV9jb2wod2lkdGg9LjcpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsPXNwcmludGYoIiUuMGYlJSIscGN0KSksIHZqdXN0PS0uMywgc2l6ZT0zKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPVNUQVRFX0NPTE9SUywgbmFtZT0iU3RhdGUiKSArCiAgc2NhbGVfeF9kaXNjcmV0ZShsaW1pdHM9bWlsZXN0b25lX29yZGVyKSArCiAgdGhlbWVfY2xhc3NpYygpICsKICBsYWJzKHg9Ik5lYXJlc3QgcmVmZXJlbmNlIG1pbGVzdG9uZSIsIHk9IiUgU8OpemFyeSBjZWxscyIsCiAgICAgICB0aXRsZT0iU8OpemFyeSBjZWxsIGRpc3RyaWJ1dGlvbiBhY3Jvc3MgcmVmZXJlbmNlIG1pbGVzdG9uZXMiKSArCiAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LGhqdXN0PTEpLAogICAgICAgIHBsb3QudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9MTIsZmFjZT0iYm9sZCIpKQoKcHJpbnQocF9tc19iYXIpCmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIFNhdmUgQWxsIE91dHB1dHMKCmBgYHtyIHNhdmUtYWxsfQojIOKUgOKUgCBSRFMg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACnNhdmVSRFMoc2V6YXJ5X29iaiwgIk9iamVjdHMvc2V6YXJ5X3Byb2plY3RlZF9kdWFsLnJkcyIpCgojIOKUgOKUgCBDU1YgdGFibGVzIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgAp3cml0ZS5jc3Yoc2V6X3N1bW1hcnksICJUYWJsZXMvc2V6YXJ5X3RyYWplY3Rvcnlfc3VtbWFyeS5jc3YiLCAgICAgcm93Lm5hbWVzPUZBTFNFKQp3cml0ZS5jc3YobXNfZGlzdCwgICAgICJUYWJsZXMvc2V6YXJ5X21pbGVzdG9uZV9kaXN0cmlidXRpb24uY3N2Iiwgcm93Lm5hbWVzPUZBTFNFKQoKIyDilIDilIAgRmlndXJlcyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKZ2dzYXZlKCJGaWd1cmVzL2ZpZ19zZXphcnlfbXN0X3N0YXRlLnBkZiIsICAgcF9zZXpfbXN0X3N0YXRlLCB3aWR0aD0xMiwgaGVpZ2h0PTgpCmdnc2F2ZSgiRmlndXJlcy9maWdfc2V6YXJ5X21zdF9wdC5wZGYiLCAgICAgIHBfc2V6X21zdF9wdCwgICAgd2lkdGg9MTIsIGhlaWdodD04KQpnZ3NhdmUoIkZpZ3VyZXMvZmlnX3NlemFyeV9tM19zdGF0ZS5wZGYiLCAgICBwX3Nlel9tM19zdGF0ZSwgIHdpZHRoPTEyLCBoZWlnaHQ9OCkKZ2dzYXZlKCJGaWd1cmVzL2ZpZ19zZXphcnlfbTNfcHQucGRmIiwgICAgICAgcF9zZXpfbTNfcHQsICAgICB3aWR0aD0xMiwgaGVpZ2h0PTgpCmdnc2F2ZSgiRmlndXJlcy9maWdfc2V6YXJ5X2FncmVlbWVudC5wZGYiLCAgIHBfc2V6X3NjYXR0ZXIsICAgd2lkdGg9MTIsIGhlaWdodD01KQpnZ3NhdmUoIkZpZ3VyZXMvZmlnX3NlemFyeV9jZWxsX2xpbmVzLnBkZiIsICBwX2NlbGxfbGluZSwgICAgIHdpZHRoPTEyLCBoZWlnaHQ9NSkKZ2dzYXZlKCJGaWd1cmVzL2ZpZ19taWxlc3RvbmVfYmFyLnBkZiIsICAgICAgcF9tc19iYXIsICAgICAgICB3aWR0aD0xMiwgaGVpZ2h0PTUpCgpjYXQoIuKchSBBbGwgb3V0cHV0cyBzYXZlZFxuIikKYGBgCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgU2Vzc2lvbiBJbmZvcm1hdGlvbgoKYGBge3Igc2Vzc2lvbi1pbmZvfQoKc2Vzc2lvbkluZm8oKQoKYGBgCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgTWV0YWRhdGEgUmVmZXJlbmNlCgpgYGB7ciBtZXRhZGF0YS10YWJsZSwgZWNobz1GQUxTRX0KbWV0YV90YmwgPC0gZGF0YS5mcmFtZSgKICBDb2x1bW4gPSBjKAogICAgInByZWRpY3RlZC5wcmVkaWN0ZWRfc3RhdGUiLAogICAgInByZWRpY3RlZC5wcmVkaWN0ZWRfc3RhdGUuc2NvcmUiLAogICAgInByZWRpY3RlZC5wcmVkaWN0ZWRfbWlsZXN0b25lIiwKICAgICJuZWFyZXN0X21pbGVzdG9uZV9tc3QiLAogICAgIm1zdF9wc2V1ZG90aW1lIiwKICAgICJtb25vY2xlM19wc2V1ZG90aW1lIgogICksCiAgRGVzY3JpcHRpb24gPSBjKAogICAgIk1hcFF1ZXJ5IGxhYmVsIHRyYW5zZmVyOiBwcmVkaWN0ZWQuY2VsbHR5cGUubDIgZnJvbSByZWZlcmVuY2UgKE5haXZlL1RDTS9URU0vVGVtcmEvVHJlZykiLAogICAgIk1hcFF1ZXJ5IHByZWRpY3Rpb24gY29uZmlkZW5jZSBzY29yZSAoMOKAkzEpIiwKICAgICJNYXBRdWVyeSBsYWJlbCB0cmFuc2ZlcjogbWlsZXN0b25lIGxhYmVsIGZyb20gcmVmZXJlbmNlIChNMDDigJNNMDYpIiwKICAgICJOZWFyZXN0IE1TVCBtaWxlc3RvbmUgYWZ0ZXIgZWRnZSBwcm9qZWN0aW9uIGluIHJlZi51bWFwIHNwYWNlIiwKICAgICJNU1QgcHNldWRvdGltZSBvbiByZWZlcmVuY2UgMOKAkzEwMCBzY2FsZSAoY2FwcGVkIDDigJMxMDApIiwKICAgICJLTk4td2VpZ2h0ZWQgTW9ub2NsZTMgcHNldWRvdGltZSBmcm9tIDE1IG5lYXJlc3QgcmVmZXJlbmNlIG5laWdoYm91cnMiCiAgKQopCmthYmxlKG1ldGFfdGJsLCBjYXB0aW9uPSJNZXRhZGF0YSBjb2x1bW5zIGFkZGVkIHRvIHNlemFyeV9vYmogYnkgdGhpcyBwaXBlbGluZSIpICU+JQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnM9Yygic3RyaXBlZCIsImNvbmRlbnNlZCIpLCBmdWxsX3dpZHRoPVRSVUUpCmBgYAo=