1 Load Libraries


2 Load Objects

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

# ── CRITICAL: use the ANNOTATED reference with saved UMAP model ────────────
# This is the cleaned reference object with:
#   - CD4 Proliferating cluster removed (MKI67+/TOP2A+/CDK1+ cells)
#   - Cluster 12 contamination removed
#   - UMAP reduction with return.model = TRUE
#   - monocle3_pseudotime column computed on the clean trajectory
#   - cell_type annotation (clusters 0-6, no proliferating)
#   - predicted.celltype.l2 (Azimuth l2)
# ──────────────────────────────────────────────────────────────────────────


reference_integrated <- readRDS("../Part3_Slingshot_Trajectory/part4_Slingshot+Projection/cd4_ref_dual_trajectory.rds")

cat("=== Reference object summary ===\n")
=== Reference object summary ===
cat("Cells:", ncol(reference_integrated), "\n")
Cells: 11466 
cat("Assays:", paste(names(reference_integrated@assays), collapse = ", "), "\n")
Assays: RNA, ADT, prediction.score.celltype.l1, prediction.score.celltype.l2, prediction.score.celltype.l3, SCT, integrated 
cat("Reductions:", paste(names(reference_integrated@reductions), collapse = ", "), "\n")
Reductions: pca, umap, integrated_dr, ref.umap 
cat("UMAP model present:", !is.null(reference_integrated@reductions$umap@misc$model), "\n")
UMAP model present: TRUE 
# ── Confirm clusters present ───────────────────────────────────────────────
cat("\nClusters present:", paste(sort(unique(reference_integrated$seurat_clusters)),
                                 collapse = ", "), "\n")

Clusters present: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 
# ── Confirm CD4 Proliferating cells are ABSENT ────────────────────────────
# Check 1: cell_type label should not contain "Prolif"
if (any(grepl("Prolif|prolif|cycling|Cycling", reference_integrated$cell_type,
              ignore.case = TRUE))) {
  warning("⚠️  Proliferating cell_type labels still detected! Check removal step.")
} else {
  cat("✅ No proliferating cell_type labels found\n")
}
✅ No proliferating cell_type labels found
# Check 2: Molecular signature — MKI67/TOP2A expression should be near zero
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("Proliferation marker score — max:", round(max(prolif_expr), 3),
      " | mean:", round(mean(prolif_expr), 4), "\n")
  cat("Cells with any prolif marker > 0.5:", sum(prolif_expr > 0.5),
      "(expect near zero after removal)\n")
  if (sum(prolif_expr > 0.5) > 50) {
    warning("⚠️  Substantial proliferation signal remains — verify removal was complete.")
  } else {
    cat("✅ Proliferating cells confirmed removed\n")
  }
}
Proliferation marker score — max: 1.187  | mean: 0.0635 
Cells with any prolif marker > 0.5: 6 (expect near zero after removal)
✅ Proliferating cells confirmed removed
# ── Cell type distribution ─────────────────────────────────────────────────
cat("\nCell type distribution (should be 6 types, no Proliferating):\n")

Cell type distribution (should be 6 types, no Proliferating):
print(table(reference_integrated$cell_type))

   CD4 Tnaive (CCR7+SELL+TCF7+)          CD4 TCM (CD161+/IL7R+)        CD4 TCM (CCR4+/Th2-like) CD4 CTL/Temra (GZMK+GZMA+CCL5+) 
                           5479                            3994                             522                             490 
      CD4 TEM (NF-kB activated)   CD4 Treg (FOXP3+Helios+CD25+)         CD4 Tnaive-RTE (IGF1R+) 
                            412                             336                             233 
cat("\nAzimuth l2 distribution:\n")

Azimuth l2 distribution:
print(table(reference_integrated$predicted.celltype.l2))

    CD4 Naive       CD4 TCM       CD4 TEM CD4 Temra/CTL          Treg 
         2037          9067           145            10           207 
# ── Finalise Azimuth l2 colour palette from actual labels in data ──────────
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("\nAzimuth l2 colour palette covers", length(ref_l2_labels), "reference labels ✅\n")

Azimuth l2 colour palette covers 5 reference labels ✅
# NOTE: monocle3_pseudotime summary is printed AFTER trajectory is built
# (Section 2 below). If this is a freshly loaded reference that already
# has pseudotime stored, the validate-pseudotime chunk will summarise it.

# ── Validate: UMAP model MUST exist for MapQuery ──────────────────────────
if (is.null(reference_integrated@reductions$umap@misc$model)) {
  stop(
    "❌ UMAP model missing! Re-run on the cleaned (no-prolif) object:\n",
    "  reference_integrated <- RunUMAP(reference_integrated, dims = 1:20,\n",
    "                                   return.model = TRUE)\n",
    "Then re-save and reload."
  )
} else {
  cat("✅ UMAP model confirmed — ready for MapQuery projection\n")
}
✅ UMAP model confirmed — ready for MapQuery projection

2.2 Load Malignant CD4+ T Cells (Sézary cells)

# ── Load the full merged object and subset malignant cells ─────────────────
# Adapt path and column names to your actual object
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 samples:\n")
All samples:
print(table(All_samples_Merged$cell_line))

           L1            L2            L3            L4            L5            L6            L7 CD4Tcells_lab CD4Tcells_10x 
         5825          5935          6428          6006          6022          5148          5331          5106          3504 
# ── Define group labels ────────────────────────────────────────────────────
# Adjust the cell_line names to match YOUR object exactly
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_S1", "CD4Tcells_10x_S2"),
    "NormalCD4T",
    "Other"
  )
)

cat("\nGroup distribution:\n")

Group distribution:
print(table(All_samples_Merged$Group))

MalignantCD4T    NormalCD4T         Other 
        40695          5106          3504 
# ── Subset malignant cells ─────────────────────────────────────────────────
MalignantCD4T <- subset(All_samples_Merged, subset = Group == "MalignantCD4T")

cat("\nMalignant CD4T cells:", ncol(MalignantCD4T), "\n")

Malignant CD4T cells: 40695 
cat("Cell lines present:\n")
Cell lines present:
print(table(MalignantCD4T$cell_line))

           L1            L2            L3            L4            L5            L6            L7 CD4Tcells_lab CD4Tcells_10x 
         5825          5935          6428          6006          6022          5148          5331             0             0 
# Clean up large object to free memory
rm(All_samples_Merged)
gc()
             used    (Mb) gc trigger    (Mb)   max used    (Mb)
Ncells    8863699   473.4   15972085   853.1   15972085   853.1
Vcells 1393403835 10630.9 3062219230 23362.9 2634513366 20099.8

3 Reference Trajectory (Monocle3)

3.1 Build CDS from Reference

# ── Convert Seurat reference to CellDataSet ────────────────────────────────
# We use the reference to build/confirm the trajectory.
# If monocle3_pseudotime already exists in the object (from previous run),
# we can skip learn_graph and go straight to projection.
# Here we re-build for completeness and reproducibility.

cat("=== Building Monocle3 CDS from reference ===\n")
=== Building Monocle3 CDS from reference ===
cds <- as.cell_data_set(reference_integrated)

# ── Transfer UMAP coordinates from Seurat (use the frozen reference UMAP) ─
reducedDim(cds, "UMAP") <- Embeddings(reference_integrated, "umap")

# ── Set partitions: single partition = one connected trajectory ────────────
# For CD4 T cell differentiation we expect a linear/branched but connected
# graph — use_partition=FALSE gives cleaner results for a single lineage.
cds@clusters$UMAP$partitions <- factor(rep(1, ncol(cds)))

# ── Set cluster assignments aligned to Seurat clusters ─────────────────────
cluster_vec <- reference_integrated$seurat_clusters[colnames(cds)]
cds@clusters$UMAP$clusters <- factor(cluster_vec)

# ── Transfer cell 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

# Transfer sample/origin column if it exists (name may vary)
if ("orig.ident" %in% colnames(reference_integrated@meta.data)) {
  colData(cds)$sample <- reference_integrated$orig.ident
} else if ("sample" %in% colnames(reference_integrated@meta.data)) {
  colData(cds)$sample <- reference_integrated$sample
}

cat("CDS built:", ncol(cds), "cells\n")
CDS built: 11466 cells
cat("Clusters:", nlevels(factor(colData(cds)$seurat_clusters)), "\n")
Clusters: 8 
cat("Partitions:", nlevels(partitions(cds)), "\n")
Partitions: 1 
cat("\nCell type breakdown in CDS:\n")

Cell type breakdown in CDS:
print(table(colData(cds)$predicted.celltype.l2))

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

3.2 Learn Trajectory Graph

# ── Learn principal graph ──────────────────────────────────────────────────
# use_partition = FALSE: single connected graph across all healthy cells.
#                        This works well here because CD4 Proliferating cells
#                        have been removed — without them, the manifold is a
#                        clean continuous differentiation space with no
#                        spurious cell-cycle branch pulling cells away.
# close_loop    = FALSE: open trajectory (Tnaive → Temra linear axis).
#                        The trajectory should not loop back on itself.
#
# RATIONALE: Removing proliferating cells is key to this step working well.
# Previously (with proliferating cells), Monocle3 would create an artifactual
# branch driven by MKI67/TOP2A/CDK1 rather than differentiation genes.
# Now the graph cleanly follows: Tnaive → TCM → TEM → Temra (+ Treg branch).

# ── FIX PARTITIONS NAMES (CRITICAL) ──────────────────────────────────────
if (is.null(names(cds@clusters$UMAP$partitions))) {
  cds@clusters$UMAP$partitions <- rep(1, ncol(cds))
  names(cds@clusters$UMAP$partitions) <- colnames(cds)
}
cat("✅ Partitions fixed:", table(cds@clusters$UMAP$partitions), "\n")
✅ Partitions fixed: 11466 
# ── FIX CLUSTERS (if needed) ─────────────────────────────────────────────
if (is.null(cds@clusters$UMAP$clusters)) {
  clustervec <- reference_integrated$seurat_clusters[colnames(cds)]
  cds@clusters$UMAP$clusters <- factor(clustervec)
}
cat("✅ Clusters:", length(unique(cds@clusters$UMAP$clusters)), "\n")
✅ Clusters: 12 
# ── LEARN GRAPH ──────────────────────────────────────────────────────────
cds <- learn_graph(cds, use_partition = FALSE, close_loop = FALSE, verbose = TRUE)

cat("\n✅ Principal graph learned!\n")

✅ Principal graph learned!
cat("Nodes:", length(igraph::V(principal_graph(cds)$UMAP)), "\n")
Nodes: 349 
cat("Branch points:", sum(igraph::degree(principal_graph(cds)$UMAP) > 2), "\n")
Branch points: 18 
cat("Expect 1-2 branches (Treg divergence ± Tnaive-RTE)\n")
Expect 1-2 branches (Treg divergence ± Tnaive-RTE)
# ── VISUALIZE: Azimuth l2 (PRIMARY) + cell_type (SECONDARY) ───────────────
p1 <- plot_cells(cds,
                 color_cells_by = "predicted.celltype.l2",  # YOUR 5 labels
                 label_groups_by_cluster = FALSE,
                 label_leaves = TRUE,
                 label_branch_points = TRUE,
                 graph_label_size = 3.5,
                 cell_size = 0.8) +
  scale_color_manual(values = azimuth_l2_colors) +
  ggtitle("Monocle3 Graph: Azimuth l2 (CD4 Naive → Temra/CTL)") +
  theme(plot.title = element_text(hjust = 0.5, size = 14, face = "bold"))

p2 <- plot_cells(cds,
                 color_cells_by = "cell_type",
                 label_groups_by_cluster = FALSE,
                 label_leaves = TRUE,
                 label_branch_points = TRUE,
                 graph_label_size = 3.5,
                 cell_size = 0.8) +
  ggtitle("Monocle3 Graph: Cell Types") +
  theme(plot.title = element_text(hjust = 0.5, size = 14, face = "bold"))

p1 | p2

4 Fixed learn-graph Visualization

umap_coords <- as.data.frame(Embeddings(reference_integrated, "umap"))
colnames(umap_coords) <- c("UMAP1", "UMAP2")   # rename for clarity

umap_coords$l2        <- reference_integrated$predicted.celltype.l2
umap_coords$cell_type <- reference_integrated$cell_type

# Centroid per Azimuth l2 state
label_coords <- umap_coords %>%
  dplyr::group_by(l2) %>%
  dplyr::summarise(
    UMAP1 = median(UMAP1),
    UMAP2 = median(UMAP2),
    .groups = "drop"
  )

# Centroid per cell_type
label_coords2 <- umap_coords %>%
  dplyr::group_by(cell_type) %>%
  dplyr::summarise(
    UMAP1 = median(UMAP1),
    UMAP2 = median(UMAP2),
    .groups = "drop"
  )

library(ggrepel)

# Azimuth l2 plot
p1 <- plot_cells(
        cds,
        color_cells_by = "predicted.celltype.l2",
        label_groups_by_cluster = FALSE,
        label_leaves        = FALSE,
        label_branch_points = FALSE,
        cell_size           = 0.7,
        show_trajectory_graph = TRUE
      ) +
  scale_color_manual(values = azimuth_l2_colors, name = "State") +
  geom_label_repel(
    data = label_coords,
    aes(x = UMAP1, y = UMAP2, label = l2),
    size = 4, fontface = "bold",
    fill = "white", alpha = 0.85,
    box.padding = 0.5, label.padding = 0.3,
    segment.color = "grey40"
  ) +
  ggtitle("Monocle3 Graph: Azimuth l2 (CD4 Naive → Temra/CTL)") +
  theme(plot.title = element_text(hjust = 0.5, size = 14, face = "bold"),
        legend.position = "none")

# cell_type plot
p2 <- plot_cells(
        cds,
        color_cells_by = "cell_type",
        label_groups_by_cluster = FALSE,
        label_leaves        = FALSE,
        label_branch_points = FALSE,
        cell_size           = 0.7,
        show_trajectory_graph = TRUE
      ) +
  geom_label_repel(
    data = label_coords2,
    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"
  ) +
  ggtitle("Monocle3 Graph: Cell Types") +
  theme(plot.title = element_text(hjust = 0.5, size = 14, face = "bold"),
        legend.position = "none")

p1 | p2


p1 


p2

4.1 Set Root & Order Cells by Pseudotime

# ── Identify root node in the naive cluster ────────────────────────────────
# Root = cells in the Tnaive cluster (CCR7+SELL+TCF7+) = cluster 0
# Using the principal graph node closest to the Tnaive centroid ensures
# pseudotime=0 is biologically anchored to the most undifferentiated state.

cat("Available Azimuth l2 labels:\n")
Available Azimuth l2 labels:
print(sort(unique(colData(cds)$predicted.celltype.l2)))
[1] CD4 Naive     CD4 TCM       CD4 TEM       CD4 Temra/CTL Treg         
Levels: CD4 Naive CD4 TCM CD4 TEM CD4 Temra/CTL Treg
# Find naive cells — use both Azimuth label and our cell_type annotation
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 identified:", length(naive_ids), "\n")

Naive root cells identified: 5739 
if (length(naive_ids) == 0) {
  stop("❌ No naive root cells found. Check label strings above and adjust grep patterns.")
}

# ── Function to find the principal graph node closest to naive centroid ────
get_root_node <- function(cds, cell_ids) {
  closest <- cds@principal_graph_aux$UMAP$pr_graph_cell_proj_closest_vertex
  closest <- as.matrix(closest[colnames(cds), ])
  igraph::V(principal_graph(cds)$UMAP)$name[
    as.numeric(names(which.max(table(closest[cell_ids, ]))))
  ]
}

root_node <- get_root_node(cds, naive_ids)
cat("Root node:", root_node, "\n")
Root node: Y_338 
# ── Order cells ────────────────────────────────────────────────────────────
cds <- order_cells(cds, root_pr_nodes = root_node)

# Transfer pseudotime back to Seurat object
reference_integrated$monocle3_pseudotime <- pseudotime(cds)

# Replace Inf (unreachable cells) with NA for clean plotting
reference_integrated$monocle3_pseudotime[
  !is.finite(reference_integrated$monocle3_pseudotime)
] <- NA

cat("\nPseudotime summary (finite values):\n")

Pseudotime summary (finite values):
print(summary(reference_integrated$monocle3_pseudotime[
  is.finite(reference_integrated$monocle3_pseudotime)
]))
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  0.000   9.653  17.619  17.918  26.763  33.713 
# ── Plot pseudotime on trajectory ─────────────────────────────────────────
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 CD4+ T Cell Pseudotime\n(Root = Tnaive, Terminal = Temra/CTL)") +
  theme(plot.title = element_text(hjust = 0.5, size = 14, face = "bold"))


# ── Pseudotime on Seurat UMAP ─────────────────────────────────────────────
FeaturePlot(
  reference_integrated,
  features  = "monocle3_pseudotime",
  reduction = "umap",
  cols      = c("lightblue", "darkblue", "red"),
  label     = TRUE,
  pt.size   = 0.5
) +
  ggtitle("Reference UMAP — Pseudotime") +
  theme(plot.title = element_text(hjust = 0.5))

5 Visualization

# ── Identify root node in the naive cluster ────────────────────────────────
# Root = cells in the Tnaive cluster (CCR7+SELL+TCF7+) = cluster 0
# Using the principal graph node closest to the Tnaive centroid ensures
# pseudotime=0 is biologically anchored to the most undifferentiated state.

cat("Available Azimuth l2 labels:\n")
Available Azimuth l2 labels:
print(sort(unique(colData(cds)$predicted.celltype.l2)))
[1] CD4 Naive     CD4 TCM       CD4 TEM       CD4 Temra/CTL Treg         
Levels: CD4 Naive CD4 TCM CD4 TEM CD4 Temra/CTL Treg
# Find naive cells — use both Azimuth label and our cell_type annotation
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 identified:", length(naive_ids), "\n")

Naive root cells identified: 5739 
if (length(naive_ids) == 0) {
  stop("❌ No naive root cells found. Check label strings above and adjust grep patterns.")
}

# ── Function to find the principal graph node closest to naive centroid ────
get_root_node <- function(cds, cell_ids) {
  closest <- cds@principal_graph_aux$UMAP$pr_graph_cell_proj_closest_vertex
  closest <- as.matrix(closest[colnames(cds), ])
  igraph::V(principal_graph(cds)$UMAP)$name[
    as.numeric(names(which.max(table(closest[cell_ids, ]))))
  ]
}

root_node <- get_root_node(cds, naive_ids)
cat("Root node:", root_node, "\n")
Root node: Y_338 
# ── Order cells ────────────────────────────────────────────────────────────
cds <- order_cells(cds, root_pr_nodes = root_node)

# Transfer pseudotime back to Seurat object
reference_integrated$monocle3_pseudotime <- pseudotime(cds)

# Replace Inf (unreachable cells) with NA for clean plotting
reference_integrated$monocle3_pseudotime[
  !is.finite(reference_integrated$monocle3_pseudotime)
] <- NA

cat("\nPseudotime summary (finite values):\n")

Pseudotime summary (finite values):
print(summary(reference_integrated$monocle3_pseudotime[
  is.finite(reference_integrated$monocle3_pseudotime)
]))
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  0.000   9.653  17.619  17.918  26.763  33.713 
# ── Plot pseudotime on trajectory ─────────────────────────────────────────
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 CD4+ T Cell Pseudotime\n(Root = Tnaive, Terminal = Temra/CTL)") +
  theme(plot.title = element_text(hjust = 0.5, size = 14, face = "bold"))


# ── Pseudotime on Seurat UMAP + state labels ─────────────────────────────
library(dplyr)
library(ggrepel)

umap_df <- as.data.frame(Embeddings(reference_integrated, "umap"))
colnames(umap_df) <- c("UMAP1", "UMAP2")
umap_df$pt <- reference_integrated$monocle3_pseudotime
umap_df$l2 <- reference_integrated$predicted.celltype.l2

umap_df <- umap_df[is.finite(umap_df$pt) & !is.na(umap_df$l2), ]

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

p_umap <- FeaturePlot(
  reference_integrated,
  features  = "monocle3_pseudotime",
  reduction = "umap",
  cols      = c("lightblue", "darkblue", "red"),
  label     = FALSE,
  pt.size   = 0.5
)

p_umap +
  geom_label_repel(
    data = label_coords,
    aes(x = UMAP1, y = UMAP2, label = l2),
    inherit.aes = FALSE,
    size = 4,
    fontface = "bold",
    fill = "white",
    alpha = 0.85,
    box.padding = 0.4,
    label.padding = 0.25,
    segment.color = "grey40"
  ) +
  ggtitle("Reference UMAP — Pseudotime with CD4 Naive / TCM / TEM / Temra/CTL / Treg labels") +
  theme(plot.title = element_text(hjust = 0.5))

# ── DEFINE MILESTONES: Key CD4 states along your trajectory ───────────────
# Based on your Azimuth l2 + pseudotime quantiles

library(dplyr)

# 1. Get state-specific pseudotime ranges
pt_ranges <- reference_integrated@meta.data %>%
  filter(is.finite(monocle3_pseudotime)) %>%
  group_by(predicted.celltype.l2) %>%
  summarise(
    n = n(),
    pt_min = round(min(monocle3_pseudotime), 2),
    pt_max = round(max(monocle3_pseudotime), 2),
    pt_median = round(median(monocle3_pseudotime), 2),
    .groups = "drop"
  ) %>%
  arrange(pt_median)

print("Pseudotime by state (milestone order):")
[1] "Pseudotime by state (milestone order):"
print(pt_ranges)

# 2. Define milestones (pseudotime quantiles + biology)
milestones <- data.frame(
  milestone = c("Naive", "Central_Memory", "Effector_Memory", "Terminal_Effector"),
  pseudotime = c(0.1, 0.8, 1.4, 2.0),  # Your trajectory quantiles
  state = c("CD4 Naive", "CD4 TCM", "CD4 TEM", "CD4 Temra/CTL")
)

print("Milestones defined:")
[1] "Milestones defined:"
print(milestones)

# 3. Assign cells to milestones — FIXED for YOUR 5 states
ref_milestones <- reference_integrated@meta.data %>%
  filter(is.finite(monocle3_pseudotime)) %>%
  mutate(
    milestone = case_when(
      # Naive: pt < 25th percentile
      monocle3_pseudotime <= quantile(monocle3_pseudotime, 0.25, na.rm = TRUE) ~ "Naive",
      
      # Central Memory (TCM): 25th-60th percentile  
      monocle3_pseudotime <= quantile(monocle3_pseudotime, 0.60, na.rm = TRUE) ~ "Central_Memory",
      
      # Treg + TEM: 60th-85th percentile (branch + effector memory)
      predicted.celltype.l2 == "Treg" | 
      (monocle3_pseudotime <= quantile(monocle3_pseudotime, 0.85, na.rm = TRUE)) ~ "Regulatory_Effector",
      
      # Terminal: TEMRA/CTL (top 15%)
      TRUE ~ "Terminal_Effector"
    ),
    
    # Simplified state mapping for coloring
    state_group = case_when(
      predicted.celltype.l2 == "CD4 Naive" ~ "Naive",
      predicted.celltype.l2 == "CD4 TCM" ~ "Central_Memory", 
      predicted.celltype.l2 == "Treg" ~ "Treg",
      predicted.celltype.l2 == "CD4 TEM" ~ "Effector_Memory",
      predicted.celltype.l2 == "CD4 Temra/CTL" ~ "Terminal_Effector"
    )
  )

cat("Milestone distribution:\n")
Milestone distribution:
print(table(ref_milestones$milestone))

     Central_Memory               Naive Regulatory_Effector   Terminal_Effector 
               4013                2867                2866                1720 
cat("\nState validation:\n") 

State validation:
print(table(ref_milestones$predicted.celltype.l2, ref_milestones$state_group))
               
                Central_Memory Effector_Memory Naive Terminal_Effector Treg
  CD4 Naive                  0               0  2037                 0    0
  CD4 TCM                 9067               0     0                 0    0
  CD4 TEM                    0             145     0                 0    0
  CD4 Temra/CTL              0               0     0                10    0
  Treg                       0               0     0                 0  207

5.1 Validate Pseudotime Against Cell Type

# ── Guard: only run if monocle3_pseudotime has already been computed ──────
# This column is created in the trajectory chunk (Section 2).
# If you are running this script for the first time on a freshly loaded
# reference (without pseudotime), this chunk will skip gracefully.
# Re-run after the trajectory section completes.

if (!"monocle3_pseudotime" %in% colnames(reference_integrated@meta.data)) {
  message(
    "⚠️  monocle3_pseudotime not yet computed — skipping validation plots.\n",
    "   Run Section 2 (Reference Trajectory) first, then re-run this chunk."
  )
} else {

# ── Violin: pseudotime distribution per Azimuth l2 label ─────────────────
# CONFIRMED label order in this dataset (low → high pseudotime):
#   CD4 Naive → CD4 TCM → CD4 TEM → CD4 Temra/CTL (highest)
#   Treg: separate branch, variable pseudotime
#
# We use predicted.celltype.l2 as PRIMARY label throughout — consistent
# with the rest of the analysis.

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

# Order l2 labels by median pseudotime
l2_order <- pt_data %>%
  group_by(l2) %>%
  summarise(med_pt = median(pseudotime, na.rm = TRUE)) %>%
  arrange(med_pt) %>%
  pull(l2)

pt_data$l2 <- factor(pt_data$l2, levels = l2_order)

# ── Violin plot coloured by Azimuth l2 ────────────────────────────────────
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 = "Pseudotime Distribution per Azimuth l2 Label (Reference)",
    x = "", y = "Monocle3 Pseudotime"
  )

# ── Ridge plot — same data, different view ────────────────────────────────
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 = "Pseudotime Density by Azimuth l2 Label",
    x = "Pseudotime", y = ""
  )

p_violin | p_ridge

} # end else (monocle3_pseudotime exists)


6 Map Malignant CD4+ T Cells onto Reference

6.1 Find Transfer Anchors


DefaultAssay(reference_integrated) <- "SCT"
# ── FindTransferAnchors ────────────────────────────────────────────────────
# normalization.method = "SCT": both reference and query are SCT-normalised
# reference.reduction = "pca":  uses the reference PCA space (not re-run on query)
# dims = 1:20: consistent with the dims used for reference clustering/UMAP

cat("=== Finding transfer anchors ===\n")
=== Finding transfer anchors ===
cat("(This may take 5-15 minutes depending on cell numbers)\n")
(This may take 5-15 minutes depending on cell numbers)
anchors <- FindTransferAnchors(
  reference           = reference_integrated,
  query               = MalignantCD4T,
  normalization.method = "SCT",
  reference.reduction = "pca",
  dims                = 1:20,
  verbose             = TRUE
)
[1] "Given reference assay has multiple sct models, selecting model with most cells for finding transfer anchors"
Error: No features to use in finding transfer anchors. To troubleshoot, try explicitly providing features to the features parameter and ensure that they are present in both reference and query assays.

6.2 MapQuery — Project onto Reference UMAP

# ── MapQuery ───────────────────────────────────────────────────────────────
# This does three things simultaneously:
#   1. TransferData: transfers labels + pseudotime from reference to query
#   2. IntegrateEmbeddings: projects query PCA into reference PCA space
#   3. ProjectUMAP: projects query cells onto the FROZEN reference UMAP
#
# refdata transfers:
#   - monocle3_pseudotime: continuous pseudotime value (via weighted anchors)
#   - seurat_clusters:     reference cluster ID (0-6)
#   - cell_type:           our annotated cell type label
#   - predicted.celltype.l2: Azimuth l2 label (KEY for state interpretation)

cat("=== Projecting malignant cells onto reference (MapQuery) ===\n")

mapped_MalignantCD4T <- MapQuery(
  anchorset         = anchors,
  query             = MalignantCD4T,
  reference         = reference_integrated,
  refdata           = list(
    # PRIMARY — Azimuth l2 label: the main state annotation used throughout
    predicted.celltype.l2 = "predicted.celltype.l2",
    # CONTINUOUS — pseudotime value from Monocle3 trajectory
    pseudotime            = "monocle3_pseudotime",
    # SECONDARY — reference cluster ID (integer, for cross-check only)
    seurat_clusters       = "seurat_clusters"
  ),
  reference.reduction = "pca",
  reduction.model     = "umap"
)

cat("✅ MapQuery complete\n")
cat("Mapped malignant cells:", ncol(mapped_MalignantCD4T), "\n")
cat("\nTransferred metadata columns:\n")
transferred_cols <- grep("^predicted\\.", colnames(mapped_MalignantCD4T@meta.data), value = TRUE)
print(transferred_cols)

cat("\nTransferred pseudotime summary:\n")
print(summary(mapped_MalignantCD4T$predicted.pseudotime))

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

cat("\nTransferred cluster distribution (secondary reference):\n")
print(table(mapped_MalignantCD4T$predicted.seurat_clusters))

7 Visualise Projection on Reference UMAP

7.1 Reference + Malignant Overlay (Pseudotime)

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

# Extend palette to include any malignant l2 labels after MapQuery
# (done again here in case new labels appear after projection)
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")

# Extend palette to cover any new l2 labels from malignant cells
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)

# ── Plot 1: Reference grey, malignant coloured by pseudotime ──────────────
p1 <- 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 = pseudotime),
             size = 1.2, alpha = 0.9) +
  scale_color_viridis_c(option = "plasma", name = "Pseudotime",
                        na.value = "grey60") +
  theme_classic(base_size = 11) +
  theme(plot.title = element_text(hjust = 0.5, face = "bold")) +
  ggtitle("Malignant CD4T Projected onto Reference\n(Coloured by transferred pseudotime)")

# ── Plot 2: Reference coloured by Azimuth l2, malignant cells in red ──────
p2 <- 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 = "grey70") +
  geom_point(data = query_umap,
             aes(x = UMAP_1, y = UMAP_2),
             color = malignant_color, size = 1.0, alpha = 0.8, shape = 16) +
  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 Azimuth l2 (colour)\n+ Malignant Cells (red overlay)")

p1 | p2

7.2 Azimuth l2 Annotation Overlay

# ── Plot 3: Malignant coloured by transferred Azimuth l2 label ────────────
# azimuth_l2_colors already extended to cover all labels in both ref + query
p3 <- 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 = "grey70") +
  theme_classic(base_size = 11) +
  theme(plot.title = element_text(hjust = 0.5, face = "bold")) +
  ggtitle("Malignant CD4T — Transferred Azimuth l2 Labels")

# ── Plot 4: Per cell line ─────────────────────────────────────────────────
cell_line_palette <- colorRampPalette(brewer.pal(8, "Dark2"))(
  length(unique(query_umap$cell_line))
)
names(cell_line_palette) <- sort(unique(query_umap$cell_line))

p4 <- 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("Malignant CD4T per Cell Line")

p3 | p4

7.3 Per Cell Line Facet — State Mapping

# ── Facet by cell line to see line-specific projection patterns ───────────
ggplot() +
  geom_point(data = ref_umap,
             aes(x = UMAP_1, y = UMAP_2),
             color = "grey90", size = 0.3, alpha = 0.5) +
  geom_point(data = query_umap,
             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)")

8 Quantification — State Assignment

8.1 Assign Each Malignant Cell to a Reference State

# ── State assignment strategy ─────────────────────────────────────────────
# For each malignant cell we have TWO sources of state information:
#   1. predicted.predicted.celltype.l2 → Azimuth l2 label transferred via
#      MapQuery anchors — PRIMARY label for all plots and quantification
#   2. predicted.seurat_clusters       → reference cluster integer ID —
#      SECONDARY, for cross-validation only
#
# Azimuth l2 is used exclusively for colour coding, bar charts, heatmaps,
# and pseudotime plots. It is unbiased, automated, and consistent with the
# analysis framework used throughout this notebook.

# ── State assignment ──────────────────────────────────────────────────────
# PRIMARY label: predicted.predicted.celltype.l2 — Azimuth l2 transferred
#                via MapQuery anchors. This is the main state for all plots
#                and quantification tables.
# SECONDARY:     predicted.seurat_clusters — reference cluster integer ID,
#                kept only for cross-reference / sanity checking.

mapped_MalignantCD4T$state_azimuth_l2   <- mapped_MalignantCD4T$predicted.predicted.celltype.l2
mapped_MalignantCD4T$state_ref_cluster  <- mapped_MalignantCD4T$predicted.seurat_clusters
mapped_MalignantCD4T$pseudotime_value   <- mapped_MalignantCD4T$predicted.pseudotime

# ── Pseudotime bins ────────────────────────────────────────────────────────
# Bin into Early (Tnaive-like), Mid (TCM/TEM-like), Late (Temra-like)
# based on reference pseudotime range tertiles
pt_vals <- reference_integrated$monocle3_pseudotime
pt_vals <- pt_vals[is.finite(pt_vals)]
pt_breaks <- quantile(pt_vals, probs = c(0, 1/3, 2/3, 1))

mapped_MalignantCD4T$pseudotime_bin <- cut(
  mapped_MalignantCD4T$pseudotime_value,
  breaks = pt_breaks,
  labels = c("Early (Tnaive-like)", "Mid (TCM/TEM-like)", "Late (Temra-like)"),
  include.lowest = TRUE
)

cat("=== State assignment complete ===\n")
cat("\nAzimuth l2 state (PRIMARY — used for all quantification):\n")
print(table(mapped_MalignantCD4T$state_azimuth_l2))
cat("\nReference cluster (secondary cross-check):\n")
print(table(mapped_MalignantCD4T$state_ref_cluster))
cat("\nPseudotime bin:\n")
print(table(mapped_MalignantCD4T$pseudotime_bin))

8.2 Quantification Tables

# ── Table 1: 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),
    label     = paste0(state_azimuth_l2, "\n(", pct, "%)")
  ) %>%
  arrange(desc(n_cells))

cat("=== Overall Malignant CD4T State Distribution ===\n")
print(state_summary)

# ── Table 2: 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)

# ── Table 3: Reference cluster cross-check ────────────────────────────────
# Which reference clusters do malignant cells map to? For cross-validation
# against the Azimuth l2 label above — they should be concordant.
cluster_summary <- mapped_MalignantCD4T@meta.data %>%
  count(state_ref_cluster, name = "n_cells") %>%
  mutate(pct = round(100 * n_cells / sum(n_cells), 1)) %>%
  arrange(as.numeric(as.character(state_ref_cluster)))

cat("\n=== Reference Cluster Distribution (cross-check) ===\n")
print(cluster_summary)

# ── Table 4: Pseudotime bin summary ───────────────────────────────────────
pt_bin_summary <- mapped_MalignantCD4T@meta.data %>%
  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)

8.3 Bar Chart — State Proportions

# ── Plot 1: Overall stacked bar per cell line ─────────────────────────────
p_bar_overall <- 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"),
    legend.key.size = unit(0.5, "cm")
  ) +
  labs(
    title = "Malignant CD4T — State Composition per Cell Line\n(Azimuth l2 transferred labels)",
    x = "Cell Line", y = "% Cells"
  )

# ── Plot 2: Grouped bar — pseudotime bins per cell line ───────────────────
pt_bin_line <- mapped_MalignantCD4T@meta.data %>%
  filter(!is.na(pseudotime_bin)) %>%
  count(cell_line, pseudotime_bin) %>%
  group_by(cell_line) %>%
  mutate(pct = round(100 * n / sum(n), 1)) %>%
  ungroup()

bin_colors <- c(
  "Early (Tnaive-like)" = "#4575B4",
  "Mid (TCM/TEM-like)"  = "#FEE090",
  "Late (Temra-like)"   = "#D73027"
)

p_bar_bins <- ggplot(pt_bin_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 >= 8, paste0(pct, "%"), "")),
            position = position_stack(vjust = 0.5),
            size = 3, color = "white", fontface = "bold") +
  scale_fill_manual(values = bin_colors, name = "Pseudotime\nBin") +
  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 = "Malignant CD4T — Pseudotime Bin Distribution per Cell Line",
    x = "Cell Line", y = "% Cells"
  )

p_bar_overall / p_bar_bins

8.4 Heatmap — State Proportions Across Cell Lines

# ── Proportion heatmap: cell line × Azimuth l2 state ─────────────────────
# This is the most compact quantification figure — publication-ready.

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

# Use pheatmap for clean heatmap
library(pheatmap)
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           = "% Malignant Cells per State (Azimuth l2)\nClustered by cell line similarity",
  border_color   = "white"
)

9 Pseudotime Analysis of Malignant Cells

9.1 Pseudotime Distribution

# ── Density plot: pseudotime distribution of malignant vs reference ───────

# Reference pseudotime
ref_pt <- data.frame(
  pseudotime = reference_integrated$monocle3_pseudotime[
    is.finite(reference_integrated$monocle3_pseudotime)
  ],
  source = "Healthy reference"
)

# Malignant pseudotime
mal_pt <- data.frame(
  pseudotime = mapped_MalignantCD4T$pseudotime_value[
    is.finite(mapped_MalignantCD4T$pseudotime_value)
  ],
  source = "Malignant CD4T"
)

pt_combined <- bind_rows(ref_pt, mal_pt)

p_density <- ggplot(pt_combined, aes(x = pseudotime, fill = source)) +
  geom_density(alpha = 0.65, adjust = 1.2) +
  scale_fill_manual(
    values = c("Healthy reference" = "#4575B4", "Malignant CD4T" = malignant_color),
    name = ""
  ) +
  theme_classic(base_size = 12) +
  theme(
    legend.position  = "top",
    plot.title       = element_text(hjust = 0.5, face = "bold")
  ) +
  labs(
    title = "Pseudotime Distribution: Healthy Reference vs Malignant CD4T",
    subtitle = "Peak shift indicates differentiation arrest point",
    x = "Monocle3 Pseudotime", y = "Density"
  )

# ── Per cell line ridge plot ──────────────────────────────────────────────
mal_pt_line <- mapped_MalignantCD4T@meta.data %>%
  filter(is.finite(pseudotime_value)) %>%
  select(cell_line, pseudotime_value)

p_ridge_lines <- ggplot(mal_pt_line,
                         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) +
  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 Distribution per Cell Line",
    subtitle = "Vertical line = median",
    x = "Pseudotime", y = ""
  )

p_density | p_ridge_lines

9.2 Pseudotime Summary Statistics Table

# ── Detailed pseudotime statistics per cell line ──────────────────────────
pt_stats <- mapped_MalignantCD4T@meta.data %>%
  filter(is.finite(pseudotime_value)) %>%
  group_by(cell_line) %>%
  summarise(
    n_cells    = n(),
    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),
    pct_early  = round(100 * mean(pseudotime_value < pt_breaks[2], na.rm = TRUE), 1),
    pct_mid    = round(100 * mean(pseudotime_value >= pt_breaks[2] &
                                   pseudotime_value < pt_breaks[3], na.rm = TRUE), 1),
    pct_late   = round(100 * mean(pseudotime_value >= pt_breaks[3], na.rm = TRUE), 1)
  ) %>%
  arrange(median_pt)

cat("=== Pseudotime Statistics per Cell Line ===\n")
print(pt_stats, n = Inf)

# ── Add reference l2 pseudotime for comparison ────────────────────────────
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)
  ) %>%
  arrange(median_pt)

cat("\n=== Reference Pseudotime per Azimuth l2 State (for comparison) ===\n")
print(ref_stats)

10 Integrated Summary Visualisation

10.1 Four-Panel Summary Figure

# ── Panel 1: Reference UMAP coloured by Azimuth l2 ───────────────────────
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 = "grey70") +
  ggtitle("A. Reference CD4+ T Cells\n(Azimuth l2 — Proliferating removed)") +
  theme(
    plot.title      = element_text(hjust = 0.5, size = 11, face = "bold"),
    legend.position = "none"
  ) + NoLegend()

# ── Panel 2: Reference pseudotime ────────────────────────────────────────
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 axis)") +
  theme(plot.title = element_text(hjust = 0.5, size = 11, face = "bold"))

# ── Panel 3: Malignant projection coloured by pseudotime ──────────────────
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, 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. Malignant CD4T Projected\n(pseudotime transferred)")

# ── Panel 4: State proportion bar chart ──────────────────────────────────
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 = "grey70", 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 transferred)",
    x = "", y = "% Cells"
  )

(p_ref | p_pt_ref) / (p_proj | p_quant) +
  plot_annotation(
    title    = "Malignant CD4+ T Cell Differentiation State Mapping\n(Sézary Syndrome — Monocle3 Reference Trajectory)",
    theme    = theme(plot.title = element_text(hjust = 0.5, size = 14, face = "bold"))
  )

10.2 Pseudotime vs State Dotplot (publication-ready)

# ── Dot/boxplot: pseudotime of malignant cells grouped 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)) %>%
  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) +
  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 = "Pseudotime of Malignant CD4T per Transferred State\n(Azimuth l2 labels)",
    x = "Transferred Differentiation State",
    y = "Transferred Pseudotime"
  )

11 Save Outputs

# ── Save mapped malignant object ──────────────────────────────────────────
out_dir <- "results/MalignantCD4T_Monocle3_projection"
if (!dir.exists(out_dir)) dir.create(out_dir, recursive = TRUE)

saveRDS(
  mapped_MalignantCD4T,
  file.path(out_dir, "MalignantCD4T_mapped_monocle3_pseudotime_noProlif_ref.rds")
)

# ── Save quantification tables as CSV ─────────────────────────────────────
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_stats,
  file.path(out_dir, "pseudotime_stats_per_cellline.csv"),
  row.names = FALSE
)

# ── Save full metadata for downstream analysis ─────────────────────────────
write.csv(
  mapped_MalignantCD4T@meta.data,
  file.path(out_dir, "MalignantCD4T_full_metadata_with_projection.csv"),
  row.names = TRUE
)

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

12 Session Info

sessionInfo()
LS0tCnRpdGxlOiAiTWFsaWduYW50IENENCsgVCBDZWxsIFRyYWplY3RvcnkgQW5hbHlzaXMgJiBTdGF0ZSBNYXBwaW5nIChNb25vY2xlMykiCnN1YnRpdGxlOiAiUmVmZXJlbmNlLWJhc2VkIHBzZXVkb3RpbWUgfCBTw6l6YXJ5IFN5bmRyb21lIG1hbGlnbmFudCBDRDRUIHByb2plY3Rpb24gfCBObyBDRDQgUHJvbGlmZXJhdGluZyIKYXV0aG9yOiAiTmFzaXIgTWFobW9vZCBBYmJhc2kiCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OgogICAgICBjb2xsYXBzZWQ6IGZhbHNlCiAgICB0aGVtZTogam91cm5hbAogICAgaGlnaGxpZ2h0OiB0YW5nbwogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IHRydWUKICAgIGRmX3ByaW50OiBwYWdlZAogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlCi0tLQoKPCEtLSDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZAKICAgICBTQ0lFTlRJRklDIFJBVElPTkFMRSAmIEFQUFJPQUNIIENPTU1FTlRBUlkKICAgICDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZAKCiAgV0hZIFRISVMgQVBQUk9BQ0g/CiAg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACiAgU8OpemFyeSBTeW5kcm9tZSAoU1MpIGlzIGEgbGV1a2VtaWMgZm9ybSBvZiBjdXRhbmVvdXMgVC1jZWxsIGx5bXBob21hIGluIHdoaWNoCiAgbWFsaWduYW50IENENCsgVCBjZWxscyAoU8OpemFyeSBjZWxscykgYWNjdW11bGF0ZSBpbiBibG9vZCwgc2tpbiwgYW5kIGx5bXBoIG5vZGVzLgogIEEgY2VudHJhbCB1bnJlc29sdmVkIHF1ZXN0aW9uIGlzOiBXSEVSRSBhbG9uZyB0aGUgbm9ybWFsIENENCsgVCBjZWxsIGRpZmZlcmVudGlhdGlvbgogIGNvbnRpbnV1bSBkbyB0aGVzZSBtYWxpZ25hbnQgY2VsbHMgYXJyZXN0IG9yIG9yaWdpbmF0ZT8KCiAgQ2xhc3NpY2FsIGJ1bGsgYXBwcm9hY2hlcyAobWljcm9hcnJheSwgYnVsayBSTkEtc2VxKSBjYW5ub3QgcmVzb2x2ZSB0aGlzIGJlY2F1c2UgdGhleQogIGF2ZXJhZ2UgYWNyb3NzIG1pbGxpb25zIG9mIGNlbGxzLiBTaW5nbGUtY2VsbCBSTkEtc2VxICsgcHNldWRvdGltZSB0cmFqZWN0b3J5IGFuYWx5c2lzCiAgYWxsb3dzIHVzIHRvOgoKICAxLiBCVUlMRCBhIHJlZmVyZW5jZSBkaWZmZXJlbnRpYXRpb24gbWFwIGZyb20gSEVBTFRIWSBDRDQrIFQgY2VsbHMgc3Bhbm5pbmcgYWxsCiAgICAga25vd24gc3RhdGVzOiBUbmFpdmUg4oaSIFRDTSDihpIgVEVNIOKGkiBUZW1yYSwgd2l0aCBUcmVnIGFzIGEgc2lkZSBicmFuY2guCiAgICAgVGhpcyByZWZlcmVuY2UgaXMgdGhlICJub3JtYWwgYXRsYXMiIG9mIENENCsgVCBjZWxsIGJpb2xvZ3kuCgogIDIuIFBST0pFQ1QgbWFsaWduYW50IFPDqXphcnkgY2VsbHMgb250byB0aGlzIGhlYWx0aHkgYXRsYXMgdXNpbmcgU2V1cmF0IE1hcFF1ZXJ5CiAgICAgKGFuY2hvcmVkIFJQQ0EtYmFzZWQgdHJhbnNmZXIpLiBFYWNoIG1hbGlnbmFudCBjZWxsIGdldHMgY29vcmRpbmF0ZXMgaW4gdGhlCiAgICAgcmVmZXJlbmNlIFVNQVAgc3BhY2Ug4oCUIG1lYW5pbmcgd2UgY2FuIHNlZSBleGFjdGx5IFdIRVJFIGl0IGZhbGxzIHJlbGF0aXZlCiAgICAgdG8gaGVhbHRoeSBkaWZmZXJlbnRpYXRpb24gc3RhdGVzLgoKICAzLiBUUkFOU0ZFUiBwc2V1ZG90aW1lIGZyb20gcmVmZXJlbmNlIHRvIHF1ZXJ5LiBCZWNhdXNlIHRoZSBoZWFsdGh5IHRyYWplY3RvcnkKICAgICBoYXMgYSBjb250aW51b3VzIHBzZXVkb3RpbWUgYXhpcyAoTmFpdmU9MCDihpIgVGVtcmE9bWF4KSwgZWFjaCBwcm9qZWN0ZWQKICAgICBtYWxpZ25hbnQgY2VsbCBpbmhlcml0cyBhIHBzZXVkb3RpbWUgdmFsdWUuIFRoaXMgdGVsbHMgdXM6CiAgICAgICAtIExvdyBwc2V1ZG90aW1lIOKGkiBtYWxpZ25hbnQgY2VsbCByZXNlbWJsZXMgbmHDr3ZlL2Vhcmx5IG1lbW9yeSBzdGF0ZQogICAgICAgLSBIaWdoIHBzZXVkb3RpbWUg4oaSIG1hbGlnbmFudCBjZWxsIHJlc2VtYmxlcyBsYXRlIGVmZmVjdG9yL1RlbXJhIHN0YXRlCiAgICAgICAtIEludGVybWVkaWF0ZSDihpIgbWFsaWduYW50IGNlbGwgaXMgYXJyZXN0ZWQgYXQgYSBzcGVjaWZpYyBtZW1vcnkgY2hlY2twb2ludAoKICA0LiBRVUFOVElGWSBzdGF0ZSBkaXN0cmlidXRpb246IEJ5IGNvbWJpbmluZyBwcm9qZWN0ZWQgY29vcmRpbmF0ZXMgd2l0aCBBemltdXRoCiAgICAgcHJlZGljdGVkLmNlbGx0eXBlLmwyIGxhYmVscyAodHJhbnNmZXJyZWQgZnJvbSByZWZlcmVuY2UpLCB3ZSBjYW4gY291bnQgd2hhdAogICAgIGZyYWN0aW9uIG9mIG1hbGlnbmFudCBjZWxscyBtYXAgdG8gZWFjaCBoZWFsdGh5IHN0YXRlIChUbmFpdmUsIFRDTSwgVEVNLCBUZW1yYSwKICAgICBUcmVnKS4gVGhpcyBwcm92aWRlcyBhIG1vbGVjdWxhciAiZmluZ2VycHJpbnQiIG9mIHRoZSBtYWxpZ25hbnQgcG9wdWxhdGlvbi4KCiAgV0hZIE1PTk9DTEUzIE9OIFRIRSBSRUZFUkVOQ0UgKE5PVCBPTiBNQUxJR05BTlQgQ0VMTFMpPwogIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgAogIFJ1bm5pbmcgTW9ub2NsZTMgb24gbWFsaWduYW50IGNlbGxzIGFsb25lIHdvdWxkIHByb2R1Y2UgYSB0cmFqZWN0b3J5IHJlZmxlY3RpbmcKICB0dW1vdXIgaGV0ZXJvZ2VuZWl0eSDigJQgbm90IHRoZSBiaW9sb2dpY2FsIGRpZmZlcmVudGlhdGlvbiBheGlzIHdlIHdhbnQgdG8gbWVhc3VyZQogIGFnYWluc3QuIEluc3RlYWQ6CgogICAgU3RlcCAxOiBMZWFybiB0aGUgdHJhamVjdG9yeSBvbiBIRUFMVEhZIHJlZmVyZW5jZSBjZWxscyBvbmx5LgogICAgICAgICAgICBNb25vY2xlMyBidWlsZHMgYSBwcmluY2lwYWwgZ3JhcGggdGhyb3VnaCB0aGUgaGVhbHRoeSBtYW5pZm9sZC4KICAgICAgICAgICAgUm9vdCA9IENENCBUbmFpdmUgY2x1c3RlciAoQ0NSNytTRUxMK1RDRjcrKS4KCiAgICBTdGVwIDI6IEZyZWV6ZSB0aGUgcmVmZXJlbmNlIFVNQVAgbW9kZWwgKHJldHVybi5tb2RlbCA9IFRSVUUgaW4gUnVuVU1BUCkuCgogICAgU3RlcCAzOiBQcm9qZWN0IG1hbGlnbmFudCBjZWxscyBpbnRvIHRoaXMgZnJvemVuIHNwYWNlIHZpYSBNYXBRdWVyeS4KICAgICAgICAgICAgTWFsaWduYW50IGNlbGxzIERPIE5PVCBhbHRlciB0aGUgcmVmZXJlbmNlIHRyYWplY3Rvcnkg4oCUCiAgICAgICAgICAgIHRoZXkgYXJlIG92ZXJsYWlkIGFzICJwYXNzZW5nZXJzIiBvbiB0aGUgaGVhbHRoeSB0b3BvbG9neS4KCiAgICBTdGVwIDQ6IFRyYW5zZmVyIHBzZXVkb3RpbWUgYW5kIEF6aW11dGggbDIgbGFiZWxzIHRvIHByb2plY3RlZCBjZWxscy4KCiAgVGhpcyBpcyBtZXRob2RvbG9naWNhbGx5IGVxdWl2YWxlbnQgdG8gd2hhdCBDZXJhcGlvIGV0IGFsLiBkaWQ6CiAgYnVpbGRpbmcgYSByZWZlcmVuY2UgQ0Q0KyB0cmFqZWN0b3J5IGFuZCBwcm9qZWN0aW5nIHR1bW91ciBjZWxscyB0byBpZGVudGlmeQogIHRoZSBkaWZmZXJlbnRpYXRpb24gc3RhdGUgb2YgYXJyZXN0IGluIFPDqXphcnkgY2VsbHMuCgogIFdISUNIIE9CSkVDVCBUTyBVU0U/CiAg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACiAgVXNlIHRoZSBBTk5PVEFURUQgcmVmZXJlbmNlIHdpdGggTW9ub2NsZTMgdHJhamVjdG9yeSBhbHJlYWR5IGNvbXB1dGVkCiAgQU5EIHdpdGggQ0Q0IFByb2xpZmVyYXRpbmcgY2VsbHMgcmVtb3ZlZC4gVGhpcyBvYmplY3QgaGFzOgogICAgLSBJbnRlZ3JhdGVkIFVNQVAgd2l0aCByZXR1cm4ubW9kZWwgPSBUUlVFICAgICAgICAgIOKchQogICAgLSBTZXVyYXQgY2x1c3RlcnMgMC02IHdpdGggY2VsbF90eXBlIGFubm90YXRpb24gICAgIOKchQogICAgLSBtb25vY2xlM19wc2V1ZG90aW1lIGNvbHVtbiBhbHJlYWR5IGNvbXB1dGVkICAgICAgIOKchQogICAgLSBwcmVkaWN0ZWQuY2VsbHR5cGUubDIgKEF6aW11dGgpIGxhYmVscyAgICAgICAgICAgIOKchQogICAgLSBDRDQgUHJvbGlmZXJhdGluZyBjbHVzdGVyIHJlbW92ZWQgICAgICAgICAgICAgICAgIOKchQogICAgLSBDbHVzdGVyIDEyIGNvbnRhbWluYXRpb24gcmVtb3ZlZCAgICAgICAgICAgICAgICAgIOKchQoKICBXSFkgUkVNT1ZFIENENCBQUk9MSUZFUkFUSU5HIENFTExTIEZST00gVEhFIFJFRkVSRU5DRT8KICDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKICBQcm9saWZlcmF0aW5nIENENCsgVCBjZWxscyAoTUtJNjcrLCBUT1AyQSssIENESzErKSByZXByZXNlbnQgY2VsbHMKICBhY3RpdmVseSBjeWNsaW5nIHRocm91Z2ggdGhlIGNlbGwgY3ljbGUg4oCUIG5vdCBhIHN0YWJsZSBkaWZmZXJlbnRpYXRpb24KICBzdGF0ZS4gSW5jbHVkaW5nIHRoZW0gaW4gdGhlIHRyYWplY3Rvcnkgd291bGQ6CiAgICAxLiBDcmVhdGUgYSBzcHVyaW91cyBicmFuY2ggcHVsbGVkIGJ5IGNlbGwgY3ljbGUgc2lnbmF0dXJlIChTL0cyTSBnZW5lcykKICAgICAgIHJhdGhlciB0aGFuIGRpZmZlcmVudGlhdGlvbiBnZW5lcyAoQ0NSNywgR1pNSywgRk9YUDMgZXRjLikKICAgIDIuIERpc3RvcnQgdGhlIHBzZXVkb3RpbWUgYXhpcyDigJQgcHJvbGlmZXJhdGluZyBjZWxscyBnZXQgaW50ZXJtZWRpYXRlCiAgICAgICBwc2V1ZG90aW1lIHZhbHVlcyB0aGF0IGRvbid0IHJlZmxlY3QgdGhlaXIgdHJ1ZSBkaWZmZXJlbnRpYXRpb24gc3RhdGUKICAgIDMuIENvbmZvdW5kIHRoZSBtYWxpZ25hbnQgY2VsbCBwcm9qZWN0aW9uIOKAlCBTw6l6YXJ5IGNlbGxzIHRoYXQgaGFwcGVuCiAgICAgICB0byBiZSBwcm9saWZlcmF0aW5nIHdvdWxkIG1hcCB0byB0aGUgd3JvbmcgcmVmZXJlbmNlIHN0YXRlCiAgUmVtb3ZpbmcgdGhlbSBwcm9kdWNlcyBhIGNsZWFuIFRuYWl2ZSDihpIgVENNIOKGkiBURU0g4oaSIFRlbXJhIGF4aXMuCgogIERPIE5PVCByZS1ydW4gU0NUcmFuc2Zvcm0vaW50ZWdyYXRpb24gb24gdGhlIHJlZmVyZW5jZSBhdCB0aGlzIHN0ZXAuCiAgRE8gTk9UIHVzZSB0aGUgcmF3IHByZS10cmFqZWN0b3J5IG9iamVjdCDigJQgaXQgbGFja3MgdGhlIFVNQVAgbW9kZWwgbmVlZGVkCiAgZm9yIE1hcFF1ZXJ5IHByb2plY3Rpb24uCiAgRE8gTk9UIHVzZSB0aGUgc2xpbmdzaG90LXJlYWR5IG9iamVjdCDigJQgc2FtZSBpc3N1ZSwgbm8gZnJvemVuIFVNQVAgbW9kZWwuCgrilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZAgLS0+CgotLS0KCiMgTG9hZCBMaWJyYXJpZXMKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldCgKICBlY2hvICAgID0gVFJVRSwKICB3YXJuaW5nID0gRkFMU0UsCiAgbWVzc2FnZSA9IEZBTFNFLAogIGZpZy53aWR0aCAgPSAxMiwKICBmaWcuaGVpZ2h0ID0gOAopCgojIENvcmUgc2luZ2xlLWNlbGwKbGlicmFyeShTZXVyYXQpCmxpYnJhcnkobW9ub2NsZTMpCmxpYnJhcnkoU2V1cmF0V3JhcHBlcnMpCgojIERhdGEgd3JhbmdsaW5nCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkodGliYmxlKQoKIyBWaXN1YWxpc2F0aW9uCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShwYXRjaHdvcmspCmxpYnJhcnkoUkNvbG9yQnJld2VyKQpsaWJyYXJ5KHZpcmlkaXMpCmxpYnJhcnkoZ2dyaWRnZXMpICAgICMgZm9yIHBzZXVkb3RpbWUgZGVuc2l0eSByaWRnZSBwbG90cwpsaWJyYXJ5KGdncmVwZWwpICAgICAjIGZvciBsYWJlbCByZXB1bHNpb24KCiMgU3RhdGlzdGljcyAmIHRhYmxlcwpsaWJyYXJ5KE1hdHJpeCkKbGlicmFyeShzY2FsZXMpICAgICAgIyBmb3IgcGVyY2VudCBmb3JtYXR0aW5nCgpvcHRpb25zKGZ1dHVyZS5nbG9iYWxzLm1heFNpemUgPSA4ZTkpCnNldC5zZWVkKDEyMzQpCgojIOKUgOKUgCBDb2xvdXIgcGFsZXR0ZXMg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACiMgUFJJTUFSWTogQXppbXV0aCBwcmVkaWN0ZWQuY2VsbHR5cGUubDIgY29sb3VycyDigJQgYnVpbHQgRFlOQU1JQ0FMTFkgZnJvbQojIGFjdHVhbCBsYWJlbHMgaW4gdGhlIGRhdGEgKGFmdGVyIGxvYWRpbmcgdGhlIHJlZmVyZW5jZSBvYmplY3QpLgojIFRoZXNlIGtub3duIEF6aW11dGggbDIgQ0Q0IFQgY2VsbCBsYWJlbHMgYXJlIHByZS1zZWVkZWQgaGVyZSBzbyBjb2xvdXJzCiMgYXJlIGNvbnNpc3RlbnQgYW5kIGJpb2xvZ2ljYWxseSBvcmRlcmVkIChibHVlPW5haXZlIOKGkiByZWQ9ZWZmZWN0b3IpLgojIEFueSBleHRyYSBsYWJlbHMgbm90IGxpc3RlZCBoZXJlIHdpbGwgYmUgYXNzaWduZWQgY29sb3VycyBhdXRvbWF0aWNhbGx5LgoKYXppbXV0aF9sMl9jb2xvcnMgPC0gYygKICAjIOKUgOKUgCBOYWl2ZSDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKICAiQ0Q0IE5haXZlIiAgICAgICAgICA9ICIjMjE2NkFDIiwgICAjIGRhcmsgYmx1ZQogICJDRDQgVE4iICAgICAgICAgICAgID0gIiMyMTY2QUMiLCAgICMgYWxpYXMKICAjIOKUgOKUgCBDZW50cmFsIE1lbW9yeSDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKICAiQ0Q0IFRDTSIgICAgICAgICAgICA9ICIjNzRBREQxIiwgICAjIHNreSBibHVlCiAgIyDilIDilIAgRWZmZWN0b3IgTWVtb3J5IOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgAogICJDRDQgVEVNIiAgICAgICAgICAgID0gIiNGRUUwOTAiLCAgICMgcGFsZSB5ZWxsb3cKICAjIOKaoO+4jyAgQ09ORklSTUVEIGxhYmVsIGluIHRoaXMgZGF0YXNldDogIkNENCBUZW1yYS9DVEwiIChBemltdXRoIHYxLjErKQogICJDRDQgVGVtcmEvQ1RMIiAgICAgID0gIiNENzMwMjciLCAgICMgcmVkICDihpAgZXhhY3QgbWF0Y2ggdG8gZGF0YQogICJDRDQgVEVNUkEiICAgICAgICAgID0gIiNENzMwMjciLCAgICMgYWxpYXMg4oCUIG9sZGVyIEF6aW11dGggKGFsbCBjYXBzKQogICJDRDQgVEVNUkEvQ1RMIiAgICAgID0gIiNENzMwMjciLCAgICMgYWxpYXMg4oCUIHVwcGVyY2FzZSB2YXJpYW50CiAgIkNENCBDVEwiICAgICAgICAgICAgPSAiI0Q3MzAyNyIsICAgIyBhbGlhcyDigJQgc29tZSByZWZlcmVuY2UgYnVpbGRzCiAgIyDilIDilIAgUmVndWxhdG9yeSDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKICAiVHJlZyIgICAgICAgICAgICAgICA9ICIjNzYyQTgzIiwgICAjIGRhcmsgcHVycGxlICDihpAgY29uZmlybWVkIGxhYmVsIGluIHRoaXMgZGF0YQogICJUcmVnIE5haXZlIiAgICAgICAgID0gIiM5OTcwQUIiLCAgICMgbWVkaXVtIHB1cnBsZSDigJQgYWxpYXMKICAiVHJlZyBNZW1vcnkiICAgICAgICA9ICIjNzYyQTgzIiwgICAjIGFsaWFzCiAgIyDilIDilIAgT3RoZXIgQ0Q0IHN1YnNldHMgdGhhdCBBemltdXRoIGwyIG1heSByZXR1cm4g4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACiAgIkNENCBQcm9saWZlcmF0aW5nIiAgPSAiI0E2QTZBNiIsICAgIyBncmV5ICDigJQgTk9URTogcHJlc2VudCBpbiBxdWVyeSBjZWxscyBvbmx5CiAgIlRoMSIgICAgICAgICAgICAgICAgPSAiI0Y0NkQ0MyIsCiAgIlRoMTciICAgICAgICAgICAgICAgPSAiI0ZEQUU2MSIsCiAgIlRoMS9UaDE3IiAgICAgICAgICAgPSAiI0ZFRTA4QiIsCiAgIlRmaCIgICAgICAgICAgICAgICAgPSAiIzY2QzJBNSIsCiAgIkNENCBUQ01fMSIgICAgICAgICAgPSAiIzlFQ0FFMSIsCiAgIkNENCBUQ01fMiIgICAgICAgICAgPSAiIzZCQUVENiIsCiAgIkNENCBUQ01fMyIgICAgICAgICAgPSAiIzQyOTJDNiIsCiAgIkNENCBURU1fMSIgICAgICAgICAgPSAiI0ZERDBBMiIsCiAgIkNENCBURU1fMiIgICAgICAgICAgPSAiI0ZEQUU2QiIsCiAgIkNENCBURU1fMyIgICAgICAgICAgPSAiI0ZEOEQzQyIsCiAgIkNENCBURU1fNCIgICAgICAgICAgPSAiI0Q5NDgwMSIKKQojIE5PVEU6IENENCBQcm9saWZlcmF0aW5nIGlzIGtlcHQgaW4gdGhpcyBwYWxldHRlIChncmV5KSBiZWNhdXNlIGl0IG1heQojIGFwcGVhciBpbiB0aGUgTUFMSUdOQU5UIGNlbGxzIGFmdGVyIHByb2plY3Rpb24g4oCUIG1hbGlnbmFudCBTw6l6YXJ5IGNlbGxzCiMgY2FuIGJlIGN5Y2xpbmcuIEl0IGlzIGFic2VudCBmcm9tIHRoZSBSRUZFUkVOQ0Ugb25seS4KCm1hbGlnbmFudF9jb2xvciA8LSAiI0UzMUExQyIgICAjIHZpdmlkIHJlZCBmb3IgbWFsaWduYW50IGNlbGwgb3ZlcmxheQpyZWZlcmVuY2VfY29sb3IgPC0gImdyZXk4NSIgICAgIyByZWZlcmVuY2UgYmFja2dyb3VuZCBpbiBvdmVybGF5IHBsb3RzCgojIOKUgOKUgCBIZWxwZXI6IGV4dGVuZCBwYWxldHRlIGZvciBhbnkgdW5leHBlY3RlZCBsMiBsYWJlbHMg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACiMgQ2FsbGVkIGFmdGVyIGxvYWRpbmcgZGF0YSB0byBmaWxsIGluIGFueSBsYWJlbHMgbm90IGluIGF6aW11dGhfbDJfY29sb3JzCmV4dGVuZF9wYWxldHRlIDwtIGZ1bmN0aW9uKHBhbGV0dGUsIGxhYmVscykgewogIG1pc3NpbmdfbGFiZWxzIDwtIHNldGRpZmYobGFiZWxzLCBuYW1lcyhwYWxldHRlKSkKICBpZiAobGVuZ3RoKG1pc3NpbmdfbGFiZWxzKSA+IDApIHsKICAgIGV4dHJhX2NvbHMgPC0gY29sb3JSYW1wUGFsZXR0ZShSQ29sb3JCcmV3ZXI6OmJyZXdlci5wYWwoOCwgIkRhcmsyIikpKGxlbmd0aChtaXNzaW5nX2xhYmVscykpCiAgICBuYW1lcyhleHRyYV9jb2xzKSA8LSBtaXNzaW5nX2xhYmVscwogICAgcGFsZXR0ZSA8LSBjKHBhbGV0dGUsIGV4dHJhX2NvbHMpCiAgfQogIHBhbGV0dGUKfQpgYGAKCi0tLQoKIyBMb2FkIE9iamVjdHMKCiMjIExvYWQgUmVmZXJlbmNlIChIZWFsdGh5IENENCsgVCBDZWxscyB3aXRoIFRyYWplY3RvcnkpCgpgYGB7ciBsb2FkLXJlZmVyZW5jZX0KIyDilIDilIAgQ1JJVElDQUw6IHVzZSB0aGUgQU5OT1RBVEVEIHJlZmVyZW5jZSB3aXRoIHNhdmVkIFVNQVAgbW9kZWwg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACiMgVGhpcyBpcyB0aGUgY2xlYW5lZCByZWZlcmVuY2Ugb2JqZWN0IHdpdGg6CiMgICAtIENENCBQcm9saWZlcmF0aW5nIGNsdXN0ZXIgcmVtb3ZlZCAoTUtJNjcrL1RPUDJBKy9DREsxKyBjZWxscykKIyAgIC0gQ2x1c3RlciAxMiBjb250YW1pbmF0aW9uIHJlbW92ZWQKIyAgIC0gVU1BUCByZWR1Y3Rpb24gd2l0aCByZXR1cm4ubW9kZWwgPSBUUlVFCiMgICAtIG1vbm9jbGUzX3BzZXVkb3RpbWUgY29sdW1uIGNvbXB1dGVkIG9uIHRoZSBjbGVhbiB0cmFqZWN0b3J5CiMgICAtIGNlbGxfdHlwZSBhbm5vdGF0aW9uIChjbHVzdGVycyAwLTYsIG5vIHByb2xpZmVyYXRpbmcpCiMgICAtIHByZWRpY3RlZC5jZWxsdHlwZS5sMiAoQXppbXV0aCBsMikKIyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKCgpyZWZlcmVuY2VfaW50ZWdyYXRlZCA8LSByZWFkUkRTKCIuLi9QYXJ0M19TbGluZ3Nob3RfVHJhamVjdG9yeS9wYXJ0NF9TbGluZ3Nob3QrUHJvamVjdGlvbi9jZDRfcmVmX2R1YWxfdHJhamVjdG9yeS5yZHMiKQoKY2F0KCI9PT0gUmVmZXJlbmNlIG9iamVjdCBzdW1tYXJ5ID09PVxuIikKY2F0KCJDZWxsczoiLCBuY29sKHJlZmVyZW5jZV9pbnRlZ3JhdGVkKSwgIlxuIikKY2F0KCJBc3NheXM6IiwgcGFzdGUobmFtZXMocmVmZXJlbmNlX2ludGVncmF0ZWRAYXNzYXlzKSwgY29sbGFwc2UgPSAiLCAiKSwgIlxuIikKY2F0KCJSZWR1Y3Rpb25zOiIsIHBhc3RlKG5hbWVzKHJlZmVyZW5jZV9pbnRlZ3JhdGVkQHJlZHVjdGlvbnMpLCBjb2xsYXBzZSA9ICIsICIpLCAiXG4iKQpjYXQoIlVNQVAgbW9kZWwgcHJlc2VudDoiLCAhaXMubnVsbChyZWZlcmVuY2VfaW50ZWdyYXRlZEByZWR1Y3Rpb25zJHVtYXBAbWlzYyRtb2RlbCksICJcbiIpCgojIOKUgOKUgCBDb25maXJtIGNsdXN0ZXJzIHByZXNlbnQg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACmNhdCgiXG5DbHVzdGVycyBwcmVzZW50OiIsIHBhc3RlKHNvcnQodW5pcXVlKHJlZmVyZW5jZV9pbnRlZ3JhdGVkJHNldXJhdF9jbHVzdGVycykpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xsYXBzZSA9ICIsICIpLCAiXG4iKQoKIyDilIDilIAgQ29uZmlybSBDRDQgUHJvbGlmZXJhdGluZyBjZWxscyBhcmUgQUJTRU5UIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgAojIENoZWNrIDE6IGNlbGxfdHlwZSBsYWJlbCBzaG91bGQgbm90IGNvbnRhaW4gIlByb2xpZiIKaWYgKGFueShncmVwbCgiUHJvbGlmfHByb2xpZnxjeWNsaW5nfEN5Y2xpbmciLCByZWZlcmVuY2VfaW50ZWdyYXRlZCRjZWxsX3R5cGUsCiAgICAgICAgICAgICAgaWdub3JlLmNhc2UgPSBUUlVFKSkpIHsKICB3YXJuaW5nKCLimqDvuI8gIFByb2xpZmVyYXRpbmcgY2VsbF90eXBlIGxhYmVscyBzdGlsbCBkZXRlY3RlZCEgQ2hlY2sgcmVtb3ZhbCBzdGVwLiIpCn0gZWxzZSB7CiAgY2F0KCLinIUgTm8gcHJvbGlmZXJhdGluZyBjZWxsX3R5cGUgbGFiZWxzIGZvdW5kXG4iKQp9CgojIENoZWNrIDI6IE1vbGVjdWxhciBzaWduYXR1cmUg4oCUIE1LSTY3L1RPUDJBIGV4cHJlc3Npb24gc2hvdWxkIGJlIG5lYXIgemVybwpwcm9saWZfbWFya2VycyA8LSBjKCJNS0k2NyIsICJUT1AyQSIsICJQQ05BIiwgIkNESzEiLCAiU1RNTjEiKQpwcm9saWZfcHJlc2VudCA8LSBpbnRlcnNlY3QocHJvbGlmX21hcmtlcnMsIHJvd25hbWVzKHJlZmVyZW5jZV9pbnRlZ3JhdGVkKSkKCmlmIChsZW5ndGgocHJvbGlmX3ByZXNlbnQpID4gMCkgewogIERlZmF1bHRBc3NheShyZWZlcmVuY2VfaW50ZWdyYXRlZCkgPC0gIlNDVCIKICBwcm9saWZfZXhwciA8LSBNYXRyaXg6OmNvbE1lYW5zKAogICAgR2V0QXNzYXlEYXRhKHJlZmVyZW5jZV9pbnRlZ3JhdGVkLCBsYXllciA9ICJkYXRhIilbcHJvbGlmX3ByZXNlbnQsICwgZHJvcCA9IEZBTFNFXQogICkKICBjYXQoIlByb2xpZmVyYXRpb24gbWFya2VyIHNjb3JlIOKAlCBtYXg6Iiwgcm91bmQobWF4KHByb2xpZl9leHByKSwgMyksCiAgICAgICIgfCBtZWFuOiIsIHJvdW5kKG1lYW4ocHJvbGlmX2V4cHIpLCA0KSwgIlxuIikKICBjYXQoIkNlbGxzIHdpdGggYW55IHByb2xpZiBtYXJrZXIgPiAwLjU6Iiwgc3VtKHByb2xpZl9leHByID4gMC41KSwKICAgICAgIihleHBlY3QgbmVhciB6ZXJvIGFmdGVyIHJlbW92YWwpXG4iKQogIGlmIChzdW0ocHJvbGlmX2V4cHIgPiAwLjUpID4gNTApIHsKICAgIHdhcm5pbmcoIuKaoO+4jyAgU3Vic3RhbnRpYWwgcHJvbGlmZXJhdGlvbiBzaWduYWwgcmVtYWlucyDigJQgdmVyaWZ5IHJlbW92YWwgd2FzIGNvbXBsZXRlLiIpCiAgfSBlbHNlIHsKICAgIGNhdCgi4pyFIFByb2xpZmVyYXRpbmcgY2VsbHMgY29uZmlybWVkIHJlbW92ZWRcbiIpCiAgfQp9CgojIOKUgOKUgCBDZWxsIHR5cGUgZGlzdHJpYnV0aW9uIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgApjYXQoIlxuQ2VsbCB0eXBlIGRpc3RyaWJ1dGlvbiAoc2hvdWxkIGJlIDYgdHlwZXMsIG5vIFByb2xpZmVyYXRpbmcpOlxuIikKcHJpbnQodGFibGUocmVmZXJlbmNlX2ludGVncmF0ZWQkY2VsbF90eXBlKSkKCmNhdCgiXG5BemltdXRoIGwyIGRpc3RyaWJ1dGlvbjpcbiIpCnByaW50KHRhYmxlKHJlZmVyZW5jZV9pbnRlZ3JhdGVkJHByZWRpY3RlZC5jZWxsdHlwZS5sMikpCgojIOKUgOKUgCBGaW5hbGlzZSBBemltdXRoIGwyIGNvbG91ciBwYWxldHRlIGZyb20gYWN0dWFsIGxhYmVscyBpbiBkYXRhIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgApyZWZfbDJfbGFiZWxzIDwtIHVuaXF1ZShhcy5jaGFyYWN0ZXIocmVmZXJlbmNlX2ludGVncmF0ZWQkcHJlZGljdGVkLmNlbGx0eXBlLmwyKSkKcmVmX2wyX2xhYmVscyA8LSByZWZfbDJfbGFiZWxzWyFpcy5uYShyZWZfbDJfbGFiZWxzKV0KYXppbXV0aF9sMl9jb2xvcnMgPC0gZXh0ZW5kX3BhbGV0dGUoYXppbXV0aF9sMl9jb2xvcnMsIHJlZl9sMl9sYWJlbHMpCmNhdCgiXG5BemltdXRoIGwyIGNvbG91ciBwYWxldHRlIGNvdmVycyIsIGxlbmd0aChyZWZfbDJfbGFiZWxzKSwgInJlZmVyZW5jZSBsYWJlbHMg4pyFXG4iKQoKIyBOT1RFOiBtb25vY2xlM19wc2V1ZG90aW1lIHN1bW1hcnkgaXMgcHJpbnRlZCBBRlRFUiB0cmFqZWN0b3J5IGlzIGJ1aWx0CiMgKFNlY3Rpb24gMiBiZWxvdykuIElmIHRoaXMgaXMgYSBmcmVzaGx5IGxvYWRlZCByZWZlcmVuY2UgdGhhdCBhbHJlYWR5CiMgaGFzIHBzZXVkb3RpbWUgc3RvcmVkLCB0aGUgdmFsaWRhdGUtcHNldWRvdGltZSBjaHVuayB3aWxsIHN1bW1hcmlzZSBpdC4KCiMg4pSA4pSAIFZhbGlkYXRlOiBVTUFQIG1vZGVsIE1VU1QgZXhpc3QgZm9yIE1hcFF1ZXJ5IOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgAppZiAoaXMubnVsbChyZWZlcmVuY2VfaW50ZWdyYXRlZEByZWR1Y3Rpb25zJHVtYXBAbWlzYyRtb2RlbCkpIHsKICBzdG9wKAogICAgIuKdjCBVTUFQIG1vZGVsIG1pc3NpbmchIFJlLXJ1biBvbiB0aGUgY2xlYW5lZCAobm8tcHJvbGlmKSBvYmplY3Q6XG4iLAogICAgIiAgcmVmZXJlbmNlX2ludGVncmF0ZWQgPC0gUnVuVU1BUChyZWZlcmVuY2VfaW50ZWdyYXRlZCwgZGltcyA9IDE6MjAsXG4iLAogICAgIiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuLm1vZGVsID0gVFJVRSlcbiIsCiAgICAiVGhlbiByZS1zYXZlIGFuZCByZWxvYWQuIgogICkKfSBlbHNlIHsKICBjYXQoIuKchSBVTUFQIG1vZGVsIGNvbmZpcm1lZCDigJQgcmVhZHkgZm9yIE1hcFF1ZXJ5IHByb2plY3Rpb25cbiIpCn0KYGBgCgojIyBMb2FkIE1hbGlnbmFudCBDRDQrIFQgQ2VsbHMgKFPDqXphcnkgY2VsbHMpCgpgYGB7ciBsb2FkLW1hbGlnbmFudH0KIyDilIDilIAgTG9hZCB0aGUgZnVsbCBtZXJnZWQgb2JqZWN0IGFuZCBzdWJzZXQgbWFsaWduYW50IGNlbGxzIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgAojIEFkYXB0IHBhdGggYW5kIGNvbHVtbiBuYW1lcyB0byB5b3VyIGFjdHVhbCBvYmplY3QKQWxsX3NhbXBsZXNfTWVyZ2VkIDwtIHJlYWRSRFMoIi9ob21lL25hYmJhc2kvYXBvbGxvX2hvbWUvMS1TZXVyYXRfUkRTX09CSkVDVF9GSU5BTC9BbGxfc2FtcGxlc19NZXJnZWRfd2l0aF9SZW5hbWVkX0NsdXN0ZXJzX0NlbGxfc3RhdGUtMDMtMTItMjAyNS5yZHMucmRzIikKCmNhdCgiQWxsIHNhbXBsZXM6XG4iKQpwcmludCh0YWJsZShBbGxfc2FtcGxlc19NZXJnZWQkY2VsbF9saW5lKSkKCiMg4pSA4pSAIERlZmluZSBncm91cCBsYWJlbHMg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACiMgQWRqdXN0IHRoZSBjZWxsX2xpbmUgbmFtZXMgdG8gbWF0Y2ggWU9VUiBvYmplY3QgZXhhY3RseQpBbGxfc2FtcGxlc19NZXJnZWQkR3JvdXAgPC0gaWZlbHNlKAogIEFsbF9zYW1wbGVzX01lcmdlZCRjZWxsX2xpbmUgJWluJSBwYXN0ZTAoIkwiLCAxOjcpLAogICJNYWxpZ25hbnRDRDRUIiwKICBpZmVsc2UoCiAgICBBbGxfc2FtcGxlc19NZXJnZWQkY2VsbF9saW5lICVpbiUgYygiQ0Q0VGNlbGxzX2xhYiIsICJDRDRUY2VsbHNfMTB4X1MxIiwgIkNENFRjZWxsc18xMHhfUzIiKSwKICAgICJOb3JtYWxDRDRUIiwKICAgICJPdGhlciIKICApCikKCmNhdCgiXG5Hcm91cCBkaXN0cmlidXRpb246XG4iKQpwcmludCh0YWJsZShBbGxfc2FtcGxlc19NZXJnZWQkR3JvdXApKQoKIyDilIDilIAgU3Vic2V0IG1hbGlnbmFudCBjZWxscyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKTWFsaWduYW50Q0Q0VCA8LSBzdWJzZXQoQWxsX3NhbXBsZXNfTWVyZ2VkLCBzdWJzZXQgPSBHcm91cCA9PSAiTWFsaWduYW50Q0Q0VCIpCgpjYXQoIlxuTWFsaWduYW50IENENFQgY2VsbHM6IiwgbmNvbChNYWxpZ25hbnRDRDRUKSwgIlxuIikKY2F0KCJDZWxsIGxpbmVzIHByZXNlbnQ6XG4iKQpwcmludCh0YWJsZShNYWxpZ25hbnRDRDRUJGNlbGxfbGluZSkpCgojIENsZWFuIHVwIGxhcmdlIG9iamVjdCB0byBmcmVlIG1lbW9yeQpybShBbGxfc2FtcGxlc19NZXJnZWQpCmdjKCkKYGBgCgotLS0KCiMgUmVmZXJlbmNlIFRyYWplY3RvcnkgKE1vbm9jbGUzKQoKIyMgQnVpbGQgQ0RTIGZyb20gUmVmZXJlbmNlCgpgYGB7ciBidWlsZC1jZHN9CiMg4pSA4pSAIENvbnZlcnQgU2V1cmF0IHJlZmVyZW5jZSB0byBDZWxsRGF0YVNldCDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKIyBXZSB1c2UgdGhlIHJlZmVyZW5jZSB0byBidWlsZC9jb25maXJtIHRoZSB0cmFqZWN0b3J5LgojIElmIG1vbm9jbGUzX3BzZXVkb3RpbWUgYWxyZWFkeSBleGlzdHMgaW4gdGhlIG9iamVjdCAoZnJvbSBwcmV2aW91cyBydW4pLAojIHdlIGNhbiBza2lwIGxlYXJuX2dyYXBoIGFuZCBnbyBzdHJhaWdodCB0byBwcm9qZWN0aW9uLgojIEhlcmUgd2UgcmUtYnVpbGQgZm9yIGNvbXBsZXRlbmVzcyBhbmQgcmVwcm9kdWNpYmlsaXR5LgoKY2F0KCI9PT0gQnVpbGRpbmcgTW9ub2NsZTMgQ0RTIGZyb20gcmVmZXJlbmNlID09PVxuIikKCmNkcyA8LSBhcy5jZWxsX2RhdGFfc2V0KHJlZmVyZW5jZV9pbnRlZ3JhdGVkKQoKIyDilIDilIAgVHJhbnNmZXIgVU1BUCBjb29yZGluYXRlcyBmcm9tIFNldXJhdCAodXNlIHRoZSBmcm96ZW4gcmVmZXJlbmNlIFVNQVApIOKUgApyZWR1Y2VkRGltKGNkcywgIlVNQVAiKSA8LSBFbWJlZGRpbmdzKHJlZmVyZW5jZV9pbnRlZ3JhdGVkLCAidW1hcCIpCgojIOKUgOKUgCBTZXQgcGFydGl0aW9uczogc2luZ2xlIHBhcnRpdGlvbiA9IG9uZSBjb25uZWN0ZWQgdHJhamVjdG9yeSDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKIyBGb3IgQ0Q0IFQgY2VsbCBkaWZmZXJlbnRpYXRpb24gd2UgZXhwZWN0IGEgbGluZWFyL2JyYW5jaGVkIGJ1dCBjb25uZWN0ZWQKIyBncmFwaCDigJQgdXNlX3BhcnRpdGlvbj1GQUxTRSBnaXZlcyBjbGVhbmVyIHJlc3VsdHMgZm9yIGEgc2luZ2xlIGxpbmVhZ2UuCmNkc0BjbHVzdGVycyRVTUFQJHBhcnRpdGlvbnMgPC0gZmFjdG9yKHJlcCgxLCBuY29sKGNkcykpKQoKIyDilIDilIAgU2V0IGNsdXN0ZXIgYXNzaWdubWVudHMgYWxpZ25lZCB0byBTZXVyYXQgY2x1c3RlcnMg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACmNsdXN0ZXJfdmVjIDwtIHJlZmVyZW5jZV9pbnRlZ3JhdGVkJHNldXJhdF9jbHVzdGVyc1tjb2xuYW1lcyhjZHMpXQpjZHNAY2x1c3RlcnMkVU1BUCRjbHVzdGVycyA8LSBmYWN0b3IoY2x1c3Rlcl92ZWMpCgojIOKUgOKUgCBUcmFuc2ZlciBjZWxsIG1ldGFkYXRhIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgApjb2xEYXRhKGNkcykkY2VsbF9saW5lICAgICAgICAgICAgIDwtIHJlZmVyZW5jZV9pbnRlZ3JhdGVkJG9yaWcuaWRlbnQKY29sRGF0YShjZHMpJGNlbGxfdHlwZSAgICAgICAgICAgICA8LSByZWZlcmVuY2VfaW50ZWdyYXRlZCRjZWxsX3R5cGUKY29sRGF0YShjZHMpJHByZWRpY3RlZC5jZWxsdHlwZS5sMiA8LSByZWZlcmVuY2VfaW50ZWdyYXRlZCRwcmVkaWN0ZWQuY2VsbHR5cGUubDIKY29sRGF0YShjZHMpJHNldXJhdF9jbHVzdGVycyAgICAgICA8LSByZWZlcmVuY2VfaW50ZWdyYXRlZCRpbnRlZ3JhdGVkX3Nubl9yZXMuMC4yCgojIFRyYW5zZmVyIHNhbXBsZS9vcmlnaW4gY29sdW1uIGlmIGl0IGV4aXN0cyAobmFtZSBtYXkgdmFyeSkKaWYgKCJvcmlnLmlkZW50IiAlaW4lIGNvbG5hbWVzKHJlZmVyZW5jZV9pbnRlZ3JhdGVkQG1ldGEuZGF0YSkpIHsKICBjb2xEYXRhKGNkcykkc2FtcGxlIDwtIHJlZmVyZW5jZV9pbnRlZ3JhdGVkJG9yaWcuaWRlbnQKfSBlbHNlIGlmICgic2FtcGxlIiAlaW4lIGNvbG5hbWVzKHJlZmVyZW5jZV9pbnRlZ3JhdGVkQG1ldGEuZGF0YSkpIHsKICBjb2xEYXRhKGNkcykkc2FtcGxlIDwtIHJlZmVyZW5jZV9pbnRlZ3JhdGVkJHNhbXBsZQp9CgpjYXQoIkNEUyBidWlsdDoiLCBuY29sKGNkcyksICJjZWxsc1xuIikKY2F0KCJDbHVzdGVyczoiLCBubGV2ZWxzKGZhY3Rvcihjb2xEYXRhKGNkcykkc2V1cmF0X2NsdXN0ZXJzKSksICJcbiIpCmNhdCgiUGFydGl0aW9uczoiLCBubGV2ZWxzKHBhcnRpdGlvbnMoY2RzKSksICJcbiIpCmNhdCgiXG5DZWxsIHR5cGUgYnJlYWtkb3duIGluIENEUzpcbiIpCnByaW50KHRhYmxlKGNvbERhdGEoY2RzKSRwcmVkaWN0ZWQuY2VsbHR5cGUubDIpKQpgYGAKCiMjIExlYXJuIFRyYWplY3RvcnkgR3JhcGgKCmBgYHtyIGxlYXJuLWdyYXBoLCBmaWcud2lkdGg9MTQsIGZpZy5oZWlnaHQ9OH0KIyDilIDilIAgTGVhcm4gcHJpbmNpcGFsIGdyYXBoIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgAojIHVzZV9wYXJ0aXRpb24gPSBGQUxTRTogc2luZ2xlIGNvbm5lY3RlZCBncmFwaCBhY3Jvc3MgYWxsIGhlYWx0aHkgY2VsbHMuCiMgICAgICAgICAgICAgICAgICAgICAgICBUaGlzIHdvcmtzIHdlbGwgaGVyZSBiZWNhdXNlIENENCBQcm9saWZlcmF0aW5nIGNlbGxzCiMgICAgICAgICAgICAgICAgICAgICAgICBoYXZlIGJlZW4gcmVtb3ZlZCDigJQgd2l0aG91dCB0aGVtLCB0aGUgbWFuaWZvbGQgaXMgYQojICAgICAgICAgICAgICAgICAgICAgICAgY2xlYW4gY29udGludW91cyBkaWZmZXJlbnRpYXRpb24gc3BhY2Ugd2l0aCBubwojICAgICAgICAgICAgICAgICAgICAgICAgc3B1cmlvdXMgY2VsbC1jeWNsZSBicmFuY2ggcHVsbGluZyBjZWxscyBhd2F5LgojIGNsb3NlX2xvb3AgICAgPSBGQUxTRTogb3BlbiB0cmFqZWN0b3J5IChUbmFpdmUg4oaSIFRlbXJhIGxpbmVhciBheGlzKS4KIyAgICAgICAgICAgICAgICAgICAgICAgIFRoZSB0cmFqZWN0b3J5IHNob3VsZCBub3QgbG9vcCBiYWNrIG9uIGl0c2VsZi4KIwojIFJBVElPTkFMRTogUmVtb3ZpbmcgcHJvbGlmZXJhdGluZyBjZWxscyBpcyBrZXkgdG8gdGhpcyBzdGVwIHdvcmtpbmcgd2VsbC4KIyBQcmV2aW91c2x5ICh3aXRoIHByb2xpZmVyYXRpbmcgY2VsbHMpLCBNb25vY2xlMyB3b3VsZCBjcmVhdGUgYW4gYXJ0aWZhY3R1YWwKIyBicmFuY2ggZHJpdmVuIGJ5IE1LSTY3L1RPUDJBL0NESzEgcmF0aGVyIHRoYW4gZGlmZmVyZW50aWF0aW9uIGdlbmVzLgojIE5vdyB0aGUgZ3JhcGggY2xlYW5seSBmb2xsb3dzOiBUbmFpdmUg4oaSIFRDTSDihpIgVEVNIOKGkiBUZW1yYSAoKyBUcmVnIGJyYW5jaCkuCgojIOKUgOKUgCBGSVggUEFSVElUSU9OUyBOQU1FUyAoQ1JJVElDQUwpIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgAppZiAoaXMubnVsbChuYW1lcyhjZHNAY2x1c3RlcnMkVU1BUCRwYXJ0aXRpb25zKSkpIHsKICBjZHNAY2x1c3RlcnMkVU1BUCRwYXJ0aXRpb25zIDwtIHJlcCgxLCBuY29sKGNkcykpCiAgbmFtZXMoY2RzQGNsdXN0ZXJzJFVNQVAkcGFydGl0aW9ucykgPC0gY29sbmFtZXMoY2RzKQp9CmNhdCgi4pyFIFBhcnRpdGlvbnMgZml4ZWQ6IiwgdGFibGUoY2RzQGNsdXN0ZXJzJFVNQVAkcGFydGl0aW9ucyksICJcbiIpCgojIOKUgOKUgCBGSVggQ0xVU1RFUlMgKGlmIG5lZWRlZCkg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACmlmIChpcy5udWxsKGNkc0BjbHVzdGVycyRVTUFQJGNsdXN0ZXJzKSkgewogIGNsdXN0ZXJ2ZWMgPC0gcmVmZXJlbmNlX2ludGVncmF0ZWQkc2V1cmF0X2NsdXN0ZXJzW2NvbG5hbWVzKGNkcyldCiAgY2RzQGNsdXN0ZXJzJFVNQVAkY2x1c3RlcnMgPC0gZmFjdG9yKGNsdXN0ZXJ2ZWMpCn0KY2F0KCLinIUgQ2x1c3RlcnM6IiwgbGVuZ3RoKHVuaXF1ZShjZHNAY2x1c3RlcnMkVU1BUCRjbHVzdGVycykpLCAiXG4iKQoKIyDilIDilIAgTEVBUk4gR1JBUEgg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACmNkcyA8LSBsZWFybl9ncmFwaChjZHMsIHVzZV9wYXJ0aXRpb24gPSBGQUxTRSwgY2xvc2VfbG9vcCA9IEZBTFNFLCB2ZXJib3NlID0gVFJVRSkKCmNhdCgiXG7inIUgUHJpbmNpcGFsIGdyYXBoIGxlYXJuZWQhXG4iKQpjYXQoIk5vZGVzOiIsIGxlbmd0aChpZ3JhcGg6OlYocHJpbmNpcGFsX2dyYXBoKGNkcykkVU1BUCkpLCAiXG4iKQpjYXQoIkJyYW5jaCBwb2ludHM6Iiwgc3VtKGlncmFwaDo6ZGVncmVlKHByaW5jaXBhbF9ncmFwaChjZHMpJFVNQVApID4gMiksICJcbiIpCmNhdCgiRXhwZWN0IDEtMiBicmFuY2hlcyAoVHJlZyBkaXZlcmdlbmNlIMKxIFRuYWl2ZS1SVEUpXG4iKQoKIyDilIDilIAgVklTVUFMSVpFOiBBemltdXRoIGwyIChQUklNQVJZKSArIGNlbGxfdHlwZSAoU0VDT05EQVJZKSDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKcDEgPC0gcGxvdF9jZWxscyhjZHMsCiAgICAgICAgICAgICAgICAgY29sb3JfY2VsbHNfYnkgPSAicHJlZGljdGVkLmNlbGx0eXBlLmwyIiwgICMgWU9VUiA1IGxhYmVscwogICAgICAgICAgICAgICAgIGxhYmVsX2dyb3Vwc19ieV9jbHVzdGVyID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgbGFiZWxfbGVhdmVzID0gVFJVRSwKICAgICAgICAgICAgICAgICBsYWJlbF9icmFuY2hfcG9pbnRzID0gVFJVRSwKICAgICAgICAgICAgICAgICBncmFwaF9sYWJlbF9zaXplID0gMy41LAogICAgICAgICAgICAgICAgIGNlbGxfc2l6ZSA9IDAuOCkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBhemltdXRoX2wyX2NvbG9ycykgKwogIGdndGl0bGUoIk1vbm9jbGUzIEdyYXBoOiBBemltdXRoIGwyIChDRDQgTmFpdmUg4oaSIFRlbXJhL0NUTCkiKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgc2l6ZSA9IDE0LCBmYWNlID0gImJvbGQiKSkKCnAyIDwtIHBsb3RfY2VsbHMoY2RzLAogICAgICAgICAgICAgICAgIGNvbG9yX2NlbGxzX2J5ID0gImNlbGxfdHlwZSIsCiAgICAgICAgICAgICAgICAgbGFiZWxfZ3JvdXBzX2J5X2NsdXN0ZXIgPSBGQUxTRSwKICAgICAgICAgICAgICAgICBsYWJlbF9sZWF2ZXMgPSBUUlVFLAogICAgICAgICAgICAgICAgIGxhYmVsX2JyYW5jaF9wb2ludHMgPSBUUlVFLAogICAgICAgICAgICAgICAgIGdyYXBoX2xhYmVsX3NpemUgPSAzLjUsCiAgICAgICAgICAgICAgICAgY2VsbF9zaXplID0gMC44KSArCiAgZ2d0aXRsZSgiTW9ub2NsZTMgR3JhcGg6IENlbGwgVHlwZXMiKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgc2l6ZSA9IDE0LCBmYWNlID0gImJvbGQiKSkKCnAxIHwgcDIKCmBgYAojIEZpeGVkIGxlYXJuLWdyYXBoIFZpc3VhbGl6YXRpb24KYGBge3IgVmlzdWFsaXphdGlvbnMsIGZpZy53aWR0aD0xNCwgZmlnLmhlaWdodD04fQp1bWFwX2Nvb3JkcyA8LSBhcy5kYXRhLmZyYW1lKEVtYmVkZGluZ3MocmVmZXJlbmNlX2ludGVncmF0ZWQsICJ1bWFwIikpCmNvbG5hbWVzKHVtYXBfY29vcmRzKSA8LSBjKCJVTUFQMSIsICJVTUFQMiIpICAgIyByZW5hbWUgZm9yIGNsYXJpdHkKCnVtYXBfY29vcmRzJGwyICAgICAgICA8LSByZWZlcmVuY2VfaW50ZWdyYXRlZCRwcmVkaWN0ZWQuY2VsbHR5cGUubDIKdW1hcF9jb29yZHMkY2VsbF90eXBlIDwtIHJlZmVyZW5jZV9pbnRlZ3JhdGVkJGNlbGxfdHlwZQoKIyBDZW50cm9pZCBwZXIgQXppbXV0aCBsMiBzdGF0ZQpsYWJlbF9jb29yZHMgPC0gdW1hcF9jb29yZHMgJT4lCiAgZHBseXI6Omdyb3VwX2J5KGwyKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKAogICAgVU1BUDEgPSBtZWRpYW4oVU1BUDEpLAogICAgVU1BUDIgPSBtZWRpYW4oVU1BUDIpLAogICAgLmdyb3VwcyA9ICJkcm9wIgogICkKCiMgQ2VudHJvaWQgcGVyIGNlbGxfdHlwZQpsYWJlbF9jb29yZHMyIDwtIHVtYXBfY29vcmRzICU+JQogIGRwbHlyOjpncm91cF9ieShjZWxsX3R5cGUpICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoCiAgICBVTUFQMSA9IG1lZGlhbihVTUFQMSksCiAgICBVTUFQMiA9IG1lZGlhbihVTUFQMiksCiAgICAuZ3JvdXBzID0gImRyb3AiCiAgKQoKbGlicmFyeShnZ3JlcGVsKQoKIyBBemltdXRoIGwyIHBsb3QKcDEgPC0gcGxvdF9jZWxscygKICAgICAgICBjZHMsCiAgICAgICAgY29sb3JfY2VsbHNfYnkgPSAicHJlZGljdGVkLmNlbGx0eXBlLmwyIiwKICAgICAgICBsYWJlbF9ncm91cHNfYnlfY2x1c3RlciA9IEZBTFNFLAogICAgICAgIGxhYmVsX2xlYXZlcyAgICAgICAgPSBGQUxTRSwKICAgICAgICBsYWJlbF9icmFuY2hfcG9pbnRzID0gRkFMU0UsCiAgICAgICAgY2VsbF9zaXplICAgICAgICAgICA9IDAuNywKICAgICAgICBzaG93X3RyYWplY3RvcnlfZ3JhcGggPSBUUlVFCiAgICAgICkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBhemltdXRoX2wyX2NvbG9ycywgbmFtZSA9ICJTdGF0ZSIpICsKICBnZW9tX2xhYmVsX3JlcGVsKAogICAgZGF0YSA9IGxhYmVsX2Nvb3JkcywKICAgIGFlcyh4ID0gVU1BUDEsIHkgPSBVTUFQMiwgbGFiZWwgPSBsMiksCiAgICBzaXplID0gNCwgZm9udGZhY2UgPSAiYm9sZCIsCiAgICBmaWxsID0gIndoaXRlIiwgYWxwaGEgPSAwLjg1LAogICAgYm94LnBhZGRpbmcgPSAwLjUsIGxhYmVsLnBhZGRpbmcgPSAwLjMsCiAgICBzZWdtZW50LmNvbG9yID0gImdyZXk0MCIKICApICsKICBnZ3RpdGxlKCJNb25vY2xlMyBHcmFwaDogQXppbXV0aCBsMiAoQ0Q0IE5haXZlIOKGkiBUZW1yYS9DVEwpIikgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIHNpemUgPSAxNCwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQoKIyBjZWxsX3R5cGUgcGxvdApwMiA8LSBwbG90X2NlbGxzKAogICAgICAgIGNkcywKICAgICAgICBjb2xvcl9jZWxsc19ieSA9ICJjZWxsX3R5cGUiLAogICAgICAgIGxhYmVsX2dyb3Vwc19ieV9jbHVzdGVyID0gRkFMU0UsCiAgICAgICAgbGFiZWxfbGVhdmVzICAgICAgICA9IEZBTFNFLAogICAgICAgIGxhYmVsX2JyYW5jaF9wb2ludHMgPSBGQUxTRSwKICAgICAgICBjZWxsX3NpemUgICAgICAgICAgID0gMC43LAogICAgICAgIHNob3dfdHJhamVjdG9yeV9ncmFwaCA9IFRSVUUKICAgICAgKSArCiAgZ2VvbV9sYWJlbF9yZXBlbCgKICAgIGRhdGEgPSBsYWJlbF9jb29yZHMyLAogICAgYWVzKHggPSBVTUFQMSwgeSA9IFVNQVAyLCBsYWJlbCA9IGNlbGxfdHlwZSksCiAgICBzaXplID0gMy41LCBmb250ZmFjZSA9ICJib2xkIiwKICAgIGZpbGwgPSAid2hpdGUiLCBhbHBoYSA9IDAuODUsCiAgICBib3gucGFkZGluZyA9IDAuNSwKICAgIHNlZ21lbnQuY29sb3IgPSAiZ3JleTQwIgogICkgKwogIGdndGl0bGUoIk1vbm9jbGUzIEdyYXBoOiBDZWxsIFR5cGVzIikgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIHNpemUgPSAxNCwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQoKcDEgfCBwMgoKcDEgCgpwMgpgYGAKCgojIyBTZXQgUm9vdCAmIE9yZGVyIENlbGxzIGJ5IFBzZXVkb3RpbWUKCmBgYHtyIHBzZXVkb3RpbWUtcm9vdCwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTh9CiMg4pSA4pSAIElkZW50aWZ5IHJvb3Qgbm9kZSBpbiB0aGUgbmFpdmUgY2x1c3RlciDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKIyBSb290ID0gY2VsbHMgaW4gdGhlIFRuYWl2ZSBjbHVzdGVyIChDQ1I3K1NFTEwrVENGNyspID0gY2x1c3RlciAwCiMgVXNpbmcgdGhlIHByaW5jaXBhbCBncmFwaCBub2RlIGNsb3Nlc3QgdG8gdGhlIFRuYWl2ZSBjZW50cm9pZCBlbnN1cmVzCiMgcHNldWRvdGltZT0wIGlzIGJpb2xvZ2ljYWxseSBhbmNob3JlZCB0byB0aGUgbW9zdCB1bmRpZmZlcmVudGlhdGVkIHN0YXRlLgoKY2F0KCJBdmFpbGFibGUgQXppbXV0aCBsMiBsYWJlbHM6XG4iKQpwcmludChzb3J0KHVuaXF1ZShjb2xEYXRhKGNkcykkcHJlZGljdGVkLmNlbGx0eXBlLmwyKSkpCgojIEZpbmQgbmFpdmUgY2VsbHMg4oCUIHVzZSBib3RoIEF6aW11dGggbGFiZWwgYW5kIG91ciBjZWxsX3R5cGUgYW5ub3RhdGlvbgpuYWl2ZV9pZHMgPC0gcm93bmFtZXMoY29sRGF0YShjZHMpKVsKICBncmVwbCgibmFpdmV8TmFpdmV8VE4kIiwgY29sRGF0YShjZHMpJHByZWRpY3RlZC5jZWxsdHlwZS5sMiwgaWdub3JlLmNhc2UgPSBUUlVFKSB8CiAgZ3JlcGwoIlRuYWl2ZSIsIGFzLmNoYXJhY3Rlcihjb2xEYXRhKGNkcykkY2VsbF90eXBlKSwgaWdub3JlLmNhc2UgPSBGQUxTRSkKXQoKY2F0KCJcbk5haXZlIHJvb3QgY2VsbHMgaWRlbnRpZmllZDoiLCBsZW5ndGgobmFpdmVfaWRzKSwgIlxuIikKaWYgKGxlbmd0aChuYWl2ZV9pZHMpID09IDApIHsKICBzdG9wKCLinYwgTm8gbmFpdmUgcm9vdCBjZWxscyBmb3VuZC4gQ2hlY2sgbGFiZWwgc3RyaW5ncyBhYm92ZSBhbmQgYWRqdXN0IGdyZXAgcGF0dGVybnMuIikKfQoKIyDilIDilIAgRnVuY3Rpb24gdG8gZmluZCB0aGUgcHJpbmNpcGFsIGdyYXBoIG5vZGUgY2xvc2VzdCB0byBuYWl2ZSBjZW50cm9pZCDilIDilIDilIDilIAKZ2V0X3Jvb3Rfbm9kZSA8LSBmdW5jdGlvbihjZHMsIGNlbGxfaWRzKSB7CiAgY2xvc2VzdCA8LSBjZHNAcHJpbmNpcGFsX2dyYXBoX2F1eCRVTUFQJHByX2dyYXBoX2NlbGxfcHJval9jbG9zZXN0X3ZlcnRleAogIGNsb3Nlc3QgPC0gYXMubWF0cml4KGNsb3Nlc3RbY29sbmFtZXMoY2RzKSwgXSkKICBpZ3JhcGg6OlYocHJpbmNpcGFsX2dyYXBoKGNkcykkVU1BUCkkbmFtZVsKICAgIGFzLm51bWVyaWMobmFtZXMod2hpY2gubWF4KHRhYmxlKGNsb3Nlc3RbY2VsbF9pZHMsIF0pKSkpCiAgXQp9Cgpyb290X25vZGUgPC0gZ2V0X3Jvb3Rfbm9kZShjZHMsIG5haXZlX2lkcykKY2F0KCJSb290IG5vZGU6Iiwgcm9vdF9ub2RlLCAiXG4iKQoKIyDilIDilIAgT3JkZXIgY2VsbHMg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACmNkcyA8LSBvcmRlcl9jZWxscyhjZHMsIHJvb3RfcHJfbm9kZXMgPSByb290X25vZGUpCgojIFRyYW5zZmVyIHBzZXVkb3RpbWUgYmFjayB0byBTZXVyYXQgb2JqZWN0CnJlZmVyZW5jZV9pbnRlZ3JhdGVkJG1vbm9jbGUzX3BzZXVkb3RpbWUgPC0gcHNldWRvdGltZShjZHMpCgojIFJlcGxhY2UgSW5mICh1bnJlYWNoYWJsZSBjZWxscykgd2l0aCBOQSBmb3IgY2xlYW4gcGxvdHRpbmcKcmVmZXJlbmNlX2ludGVncmF0ZWQkbW9ub2NsZTNfcHNldWRvdGltZVsKICAhaXMuZmluaXRlKHJlZmVyZW5jZV9pbnRlZ3JhdGVkJG1vbm9jbGUzX3BzZXVkb3RpbWUpCl0gPC0gTkEKCmNhdCgiXG5Qc2V1ZG90aW1lIHN1bW1hcnkgKGZpbml0ZSB2YWx1ZXMpOlxuIikKcHJpbnQoc3VtbWFyeShyZWZlcmVuY2VfaW50ZWdyYXRlZCRtb25vY2xlM19wc2V1ZG90aW1lWwogIGlzLmZpbml0ZShyZWZlcmVuY2VfaW50ZWdyYXRlZCRtb25vY2xlM19wc2V1ZG90aW1lKQpdKSkKCiMg4pSA4pSAIFBsb3QgcHNldWRvdGltZSBvbiB0cmFqZWN0b3J5IOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgApwbG90X2NlbGxzKAogIGNkcywKICBjb2xvcl9jZWxsc19ieSAgICA9ICJwc2V1ZG90aW1lIiwKICBsYWJlbF9jZWxsX2dyb3VwcyA9IEZBTFNFLAogIGNlbGxfc2l6ZSAgICAgICAgID0gMC44LAogIHNob3dfdHJhamVjdG9yeV9ncmFwaCA9IFRSVUUKKSArCiAgc2NhbGVfY29sb3JfdmlyaWRpc19jKG9wdGlvbiA9ICJwbGFzbWEiLCBuYW1lID0gIlBzZXVkb3RpbWUiKSArCiAgZ2d0aXRsZSgiUmVmZXJlbmNlIENENCsgVCBDZWxsIFBzZXVkb3RpbWVcbihSb290ID0gVG5haXZlLCBUZXJtaW5hbCA9IFRlbXJhL0NUTCkiKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgc2l6ZSA9IDE0LCBmYWNlID0gImJvbGQiKSkKCiMg4pSA4pSAIFBzZXVkb3RpbWUgb24gU2V1cmF0IFVNQVAg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACkZlYXR1cmVQbG90KAogIHJlZmVyZW5jZV9pbnRlZ3JhdGVkLAogIGZlYXR1cmVzICA9ICJtb25vY2xlM19wc2V1ZG90aW1lIiwKICByZWR1Y3Rpb24gPSAidW1hcCIsCiAgY29scyAgICAgID0gYygibGlnaHRibHVlIiwgImRhcmtibHVlIiwgInJlZCIpLAogIGxhYmVsICAgICA9IFRSVUUsCiAgcHQuc2l6ZSAgID0gMC41CikgKwogIGdndGl0bGUoIlJlZmVyZW5jZSBVTUFQIOKAlCBQc2V1ZG90aW1lIikgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQoKYGBgCiMgVmlzdWFsaXphdGlvbgpgYGB7ciAsIGZpZy53aWR0aD0xNCwgZmlnLmhlaWdodD04fQojIOKUgOKUgCBJZGVudGlmeSByb290IG5vZGUgaW4gdGhlIG5haXZlIGNsdXN0ZXIg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACiMgUm9vdCA9IGNlbGxzIGluIHRoZSBUbmFpdmUgY2x1c3RlciAoQ0NSNytTRUxMK1RDRjcrKSA9IGNsdXN0ZXIgMAojIFVzaW5nIHRoZSBwcmluY2lwYWwgZ3JhcGggbm9kZSBjbG9zZXN0IHRvIHRoZSBUbmFpdmUgY2VudHJvaWQgZW5zdXJlcwojIHBzZXVkb3RpbWU9MCBpcyBiaW9sb2dpY2FsbHkgYW5jaG9yZWQgdG8gdGhlIG1vc3QgdW5kaWZmZXJlbnRpYXRlZCBzdGF0ZS4KCmNhdCgiQXZhaWxhYmxlIEF6aW11dGggbDIgbGFiZWxzOlxuIikKcHJpbnQoc29ydCh1bmlxdWUoY29sRGF0YShjZHMpJHByZWRpY3RlZC5jZWxsdHlwZS5sMikpKQoKIyBGaW5kIG5haXZlIGNlbGxzIOKAlCB1c2UgYm90aCBBemltdXRoIGxhYmVsIGFuZCBvdXIgY2VsbF90eXBlIGFubm90YXRpb24KbmFpdmVfaWRzIDwtIHJvd25hbWVzKGNvbERhdGEoY2RzKSlbCiAgZ3JlcGwoIm5haXZlfE5haXZlfFROJCIsIGNvbERhdGEoY2RzKSRwcmVkaWN0ZWQuY2VsbHR5cGUubDIsIGlnbm9yZS5jYXNlID0gVFJVRSkgfAogIGdyZXBsKCJUbmFpdmUiLCBhcy5jaGFyYWN0ZXIoY29sRGF0YShjZHMpJGNlbGxfdHlwZSksIGlnbm9yZS5jYXNlID0gRkFMU0UpCl0KCmNhdCgiXG5OYWl2ZSByb290IGNlbGxzIGlkZW50aWZpZWQ6IiwgbGVuZ3RoKG5haXZlX2lkcyksICJcbiIpCmlmIChsZW5ndGgobmFpdmVfaWRzKSA9PSAwKSB7CiAgc3RvcCgi4p2MIE5vIG5haXZlIHJvb3QgY2VsbHMgZm91bmQuIENoZWNrIGxhYmVsIHN0cmluZ3MgYWJvdmUgYW5kIGFkanVzdCBncmVwIHBhdHRlcm5zLiIpCn0KCiMg4pSA4pSAIEZ1bmN0aW9uIHRvIGZpbmQgdGhlIHByaW5jaXBhbCBncmFwaCBub2RlIGNsb3Nlc3QgdG8gbmFpdmUgY2VudHJvaWQg4pSA4pSA4pSA4pSACmdldF9yb290X25vZGUgPC0gZnVuY3Rpb24oY2RzLCBjZWxsX2lkcykgewogIGNsb3Nlc3QgPC0gY2RzQHByaW5jaXBhbF9ncmFwaF9hdXgkVU1BUCRwcl9ncmFwaF9jZWxsX3Byb2pfY2xvc2VzdF92ZXJ0ZXgKICBjbG9zZXN0IDwtIGFzLm1hdHJpeChjbG9zZXN0W2NvbG5hbWVzKGNkcyksIF0pCiAgaWdyYXBoOjpWKHByaW5jaXBhbF9ncmFwaChjZHMpJFVNQVApJG5hbWVbCiAgICBhcy5udW1lcmljKG5hbWVzKHdoaWNoLm1heCh0YWJsZShjbG9zZXN0W2NlbGxfaWRzLCBdKSkpKQogIF0KfQoKcm9vdF9ub2RlIDwtIGdldF9yb290X25vZGUoY2RzLCBuYWl2ZV9pZHMpCmNhdCgiUm9vdCBub2RlOiIsIHJvb3Rfbm9kZSwgIlxuIikKCiMg4pSA4pSAIE9yZGVyIGNlbGxzIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgApjZHMgPC0gb3JkZXJfY2VsbHMoY2RzLCByb290X3ByX25vZGVzID0gcm9vdF9ub2RlKQoKIyBUcmFuc2ZlciBwc2V1ZG90aW1lIGJhY2sgdG8gU2V1cmF0IG9iamVjdApyZWZlcmVuY2VfaW50ZWdyYXRlZCRtb25vY2xlM19wc2V1ZG90aW1lIDwtIHBzZXVkb3RpbWUoY2RzKQoKIyBSZXBsYWNlIEluZiAodW5yZWFjaGFibGUgY2VsbHMpIHdpdGggTkEgZm9yIGNsZWFuIHBsb3R0aW5nCnJlZmVyZW5jZV9pbnRlZ3JhdGVkJG1vbm9jbGUzX3BzZXVkb3RpbWVbCiAgIWlzLmZpbml0ZShyZWZlcmVuY2VfaW50ZWdyYXRlZCRtb25vY2xlM19wc2V1ZG90aW1lKQpdIDwtIE5BCgpjYXQoIlxuUHNldWRvdGltZSBzdW1tYXJ5IChmaW5pdGUgdmFsdWVzKTpcbiIpCnByaW50KHN1bW1hcnkocmVmZXJlbmNlX2ludGVncmF0ZWQkbW9ub2NsZTNfcHNldWRvdGltZVsKICBpcy5maW5pdGUocmVmZXJlbmNlX2ludGVncmF0ZWQkbW9ub2NsZTNfcHNldWRvdGltZSkKXSkpCgojIOKUgOKUgCBQbG90IHBzZXVkb3RpbWUgb24gdHJhamVjdG9yeSDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKcGxvdF9jZWxscygKICBjZHMsCiAgY29sb3JfY2VsbHNfYnkgICAgPSAicHNldWRvdGltZSIsCiAgbGFiZWxfY2VsbF9ncm91cHMgPSBGQUxTRSwKICBjZWxsX3NpemUgICAgICAgICA9IDAuOCwKICBzaG93X3RyYWplY3RvcnlfZ3JhcGggPSBUUlVFCikgKwogIHNjYWxlX2NvbG9yX3ZpcmlkaXNfYyhvcHRpb24gPSAicGxhc21hIiwgbmFtZSA9ICJQc2V1ZG90aW1lIikgKwogIGdndGl0bGUoIlJlZmVyZW5jZSBDRDQrIFQgQ2VsbCBQc2V1ZG90aW1lXG4oUm9vdCA9IFRuYWl2ZSwgVGVybWluYWwgPSBUZW1yYS9DVEwpIikgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIHNpemUgPSAxNCwgZmFjZSA9ICJib2xkIikpCgojIOKUgOKUgCBQc2V1ZG90aW1lIG9uIFNldXJhdCBVTUFQICsgc3RhdGUgbGFiZWxzIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgApsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGdncmVwZWwpCgp1bWFwX2RmIDwtIGFzLmRhdGEuZnJhbWUoRW1iZWRkaW5ncyhyZWZlcmVuY2VfaW50ZWdyYXRlZCwgInVtYXAiKSkKY29sbmFtZXModW1hcF9kZikgPC0gYygiVU1BUDEiLCAiVU1BUDIiKQp1bWFwX2RmJHB0IDwtIHJlZmVyZW5jZV9pbnRlZ3JhdGVkJG1vbm9jbGUzX3BzZXVkb3RpbWUKdW1hcF9kZiRsMiA8LSByZWZlcmVuY2VfaW50ZWdyYXRlZCRwcmVkaWN0ZWQuY2VsbHR5cGUubDIKCnVtYXBfZGYgPC0gdW1hcF9kZltpcy5maW5pdGUodW1hcF9kZiRwdCkgJiAhaXMubmEodW1hcF9kZiRsMiksIF0KCmxhYmVsX2Nvb3JkcyA8LSB1bWFwX2RmICU+JQogIGdyb3VwX2J5KGwyKSAlPiUKICBzdW1tYXJpc2UoCiAgICBVTUFQMSA9IG1lZGlhbihVTUFQMSksCiAgICBVTUFQMiA9IG1lZGlhbihVTUFQMiksCiAgICAuZ3JvdXBzID0gImRyb3AiCiAgKQoKcF91bWFwIDwtIEZlYXR1cmVQbG90KAogIHJlZmVyZW5jZV9pbnRlZ3JhdGVkLAogIGZlYXR1cmVzICA9ICJtb25vY2xlM19wc2V1ZG90aW1lIiwKICByZWR1Y3Rpb24gPSAidW1hcCIsCiAgY29scyAgICAgID0gYygibGlnaHRibHVlIiwgImRhcmtibHVlIiwgInJlZCIpLAogIGxhYmVsICAgICA9IEZBTFNFLAogIHB0LnNpemUgICA9IDAuNQopCgpwX3VtYXAgKwogIGdlb21fbGFiZWxfcmVwZWwoCiAgICBkYXRhID0gbGFiZWxfY29vcmRzLAogICAgYWVzKHggPSBVTUFQMSwgeSA9IFVNQVAyLCBsYWJlbCA9IGwyKSwKICAgIGluaGVyaXQuYWVzID0gRkFMU0UsCiAgICBzaXplID0gNCwKICAgIGZvbnRmYWNlID0gImJvbGQiLAogICAgZmlsbCA9ICJ3aGl0ZSIsCiAgICBhbHBoYSA9IDAuODUsCiAgICBib3gucGFkZGluZyA9IDAuNCwKICAgIGxhYmVsLnBhZGRpbmcgPSAwLjI1LAogICAgc2VnbWVudC5jb2xvciA9ICJncmV5NDAiCiAgKSArCiAgZ2d0aXRsZSgiUmVmZXJlbmNlIFVNQVAg4oCUIFBzZXVkb3RpbWUgd2l0aCBDRDQgTmFpdmUgLyBUQ00gLyBURU0gLyBUZW1yYS9DVEwgLyBUcmVnIGxhYmVscyIpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkKCmBgYAoKYGBge3IgbWlsZXN0b25lcywgZmlnLndpZHRoPTE0LCBmaWcuaGVpZ2h0PTZ9CiMg4pSA4pSAIERFRklORSBNSUxFU1RPTkVTOiBLZXkgQ0Q0IHN0YXRlcyBhbG9uZyB5b3VyIHRyYWplY3Rvcnkg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACiMgQmFzZWQgb24geW91ciBBemltdXRoIGwyICsgcHNldWRvdGltZSBxdWFudGlsZXMKCmxpYnJhcnkoZHBseXIpCgojIDEuIEdldCBzdGF0ZS1zcGVjaWZpYyBwc2V1ZG90aW1lIHJhbmdlcwpwdF9yYW5nZXMgPC0gcmVmZXJlbmNlX2ludGVncmF0ZWRAbWV0YS5kYXRhICU+JQogIGZpbHRlcihpcy5maW5pdGUobW9ub2NsZTNfcHNldWRvdGltZSkpICU+JQogIGdyb3VwX2J5KHByZWRpY3RlZC5jZWxsdHlwZS5sMikgJT4lCiAgc3VtbWFyaXNlKAogICAgbiA9IG4oKSwKICAgIHB0X21pbiA9IHJvdW5kKG1pbihtb25vY2xlM19wc2V1ZG90aW1lKSwgMiksCiAgICBwdF9tYXggPSByb3VuZChtYXgobW9ub2NsZTNfcHNldWRvdGltZSksIDIpLAogICAgcHRfbWVkaWFuID0gcm91bmQobWVkaWFuKG1vbm9jbGUzX3BzZXVkb3RpbWUpLCAyKSwKICAgIC5ncm91cHMgPSAiZHJvcCIKICApICU+JQogIGFycmFuZ2UocHRfbWVkaWFuKQoKcHJpbnQoIlBzZXVkb3RpbWUgYnkgc3RhdGUgKG1pbGVzdG9uZSBvcmRlcik6IikKcHJpbnQocHRfcmFuZ2VzKQoKIyAyLiBEZWZpbmUgbWlsZXN0b25lcyAocHNldWRvdGltZSBxdWFudGlsZXMgKyBiaW9sb2d5KQptaWxlc3RvbmVzIDwtIGRhdGEuZnJhbWUoCiAgbWlsZXN0b25lID0gYygiTmFpdmUiLCAiQ2VudHJhbF9NZW1vcnkiLCAiRWZmZWN0b3JfTWVtb3J5IiwgIlRlcm1pbmFsX0VmZmVjdG9yIiksCiAgcHNldWRvdGltZSA9IGMoMC4xLCAwLjgsIDEuNCwgMi4wKSwgICMgWW91ciB0cmFqZWN0b3J5IHF1YW50aWxlcwogIHN0YXRlID0gYygiQ0Q0IE5haXZlIiwgIkNENCBUQ00iLCAiQ0Q0IFRFTSIsICJDRDQgVGVtcmEvQ1RMIikKKQoKcHJpbnQoIk1pbGVzdG9uZXMgZGVmaW5lZDoiKQpwcmludChtaWxlc3RvbmVzKQoKIyAzLiBBc3NpZ24gY2VsbHMgdG8gbWlsZXN0b25lcyDigJQgRklYRUQgZm9yIFlPVVIgNSBzdGF0ZXMKcmVmX21pbGVzdG9uZXMgPC0gcmVmZXJlbmNlX2ludGVncmF0ZWRAbWV0YS5kYXRhICU+JQogIGZpbHRlcihpcy5maW5pdGUobW9ub2NsZTNfcHNldWRvdGltZSkpICU+JQogIG11dGF0ZSgKICAgIG1pbGVzdG9uZSA9IGNhc2Vfd2hlbigKICAgICAgIyBOYWl2ZTogcHQgPCAyNXRoIHBlcmNlbnRpbGUKICAgICAgbW9ub2NsZTNfcHNldWRvdGltZSA8PSBxdWFudGlsZShtb25vY2xlM19wc2V1ZG90aW1lLCAwLjI1LCBuYS5ybSA9IFRSVUUpIH4gIk5haXZlIiwKICAgICAgCiAgICAgICMgQ2VudHJhbCBNZW1vcnkgKFRDTSk6IDI1dGgtNjB0aCBwZXJjZW50aWxlICAKICAgICAgbW9ub2NsZTNfcHNldWRvdGltZSA8PSBxdWFudGlsZShtb25vY2xlM19wc2V1ZG90aW1lLCAwLjYwLCBuYS5ybSA9IFRSVUUpIH4gIkNlbnRyYWxfTWVtb3J5IiwKICAgICAgCiAgICAgICMgVHJlZyArIFRFTTogNjB0aC04NXRoIHBlcmNlbnRpbGUgKGJyYW5jaCArIGVmZmVjdG9yIG1lbW9yeSkKICAgICAgcHJlZGljdGVkLmNlbGx0eXBlLmwyID09ICJUcmVnIiB8IAogICAgICAobW9ub2NsZTNfcHNldWRvdGltZSA8PSBxdWFudGlsZShtb25vY2xlM19wc2V1ZG90aW1lLCAwLjg1LCBuYS5ybSA9IFRSVUUpKSB+ICJSZWd1bGF0b3J5X0VmZmVjdG9yIiwKICAgICAgCiAgICAgICMgVGVybWluYWw6IFRFTVJBL0NUTCAodG9wIDE1JSkKICAgICAgVFJVRSB+ICJUZXJtaW5hbF9FZmZlY3RvciIKICAgICksCiAgICAKICAgICMgU2ltcGxpZmllZCBzdGF0ZSBtYXBwaW5nIGZvciBjb2xvcmluZwogICAgc3RhdGVfZ3JvdXAgPSBjYXNlX3doZW4oCiAgICAgIHByZWRpY3RlZC5jZWxsdHlwZS5sMiA9PSAiQ0Q0IE5haXZlIiB+ICJOYWl2ZSIsCiAgICAgIHByZWRpY3RlZC5jZWxsdHlwZS5sMiA9PSAiQ0Q0IFRDTSIgfiAiQ2VudHJhbF9NZW1vcnkiLCAKICAgICAgcHJlZGljdGVkLmNlbGx0eXBlLmwyID09ICJUcmVnIiB+ICJUcmVnIiwKICAgICAgcHJlZGljdGVkLmNlbGx0eXBlLmwyID09ICJDRDQgVEVNIiB+ICJFZmZlY3Rvcl9NZW1vcnkiLAogICAgICBwcmVkaWN0ZWQuY2VsbHR5cGUubDIgPT0gIkNENCBUZW1yYS9DVEwiIH4gIlRlcm1pbmFsX0VmZmVjdG9yIgogICAgKQogICkKCmNhdCgiTWlsZXN0b25lIGRpc3RyaWJ1dGlvbjpcbiIpCnByaW50KHRhYmxlKHJlZl9taWxlc3RvbmVzJG1pbGVzdG9uZSkpCmNhdCgiXG5TdGF0ZSB2YWxpZGF0aW9uOlxuIikgCnByaW50KHRhYmxlKHJlZl9taWxlc3RvbmVzJHByZWRpY3RlZC5jZWxsdHlwZS5sMiwgcmVmX21pbGVzdG9uZXMkc3RhdGVfZ3JvdXApKQoKYGBgCgoKIyMgVmFsaWRhdGUgUHNldWRvdGltZSBBZ2FpbnN0IENlbGwgVHlwZQoKYGBge3IgdmFsaWRhdGUtcHNldWRvdGltZSwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTV9CiMg4pSA4pSAIEd1YXJkOiBvbmx5IHJ1biBpZiBtb25vY2xlM19wc2V1ZG90aW1lIGhhcyBhbHJlYWR5IGJlZW4gY29tcHV0ZWQg4pSA4pSA4pSA4pSA4pSA4pSACiMgVGhpcyBjb2x1bW4gaXMgY3JlYXRlZCBpbiB0aGUgdHJhamVjdG9yeSBjaHVuayAoU2VjdGlvbiAyKS4KIyBJZiB5b3UgYXJlIHJ1bm5pbmcgdGhpcyBzY3JpcHQgZm9yIHRoZSBmaXJzdCB0aW1lIG9uIGEgZnJlc2hseSBsb2FkZWQKIyByZWZlcmVuY2UgKHdpdGhvdXQgcHNldWRvdGltZSksIHRoaXMgY2h1bmsgd2lsbCBza2lwIGdyYWNlZnVsbHkuCiMgUmUtcnVuIGFmdGVyIHRoZSB0cmFqZWN0b3J5IHNlY3Rpb24gY29tcGxldGVzLgoKaWYgKCEibW9ub2NsZTNfcHNldWRvdGltZSIgJWluJSBjb2xuYW1lcyhyZWZlcmVuY2VfaW50ZWdyYXRlZEBtZXRhLmRhdGEpKSB7CiAgbWVzc2FnZSgKICAgICLimqDvuI8gIG1vbm9jbGUzX3BzZXVkb3RpbWUgbm90IHlldCBjb21wdXRlZCDigJQgc2tpcHBpbmcgdmFsaWRhdGlvbiBwbG90cy5cbiIsCiAgICAiICAgUnVuIFNlY3Rpb24gMiAoUmVmZXJlbmNlIFRyYWplY3RvcnkpIGZpcnN0LCB0aGVuIHJlLXJ1biB0aGlzIGNodW5rLiIKICApCn0gZWxzZSB7CgojIOKUgOKUgCBWaW9saW46IHBzZXVkb3RpbWUgZGlzdHJpYnV0aW9uIHBlciBBemltdXRoIGwyIGxhYmVsIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgAojIENPTkZJUk1FRCBsYWJlbCBvcmRlciBpbiB0aGlzIGRhdGFzZXQgKGxvdyDihpIgaGlnaCBwc2V1ZG90aW1lKToKIyAgIENENCBOYWl2ZSDihpIgQ0Q0IFRDTSDihpIgQ0Q0IFRFTSDihpIgQ0Q0IFRlbXJhL0NUTCAoaGlnaGVzdCkKIyAgIFRyZWc6IHNlcGFyYXRlIGJyYW5jaCwgdmFyaWFibGUgcHNldWRvdGltZQojCiMgV2UgdXNlIHByZWRpY3RlZC5jZWxsdHlwZS5sMiBhcyBQUklNQVJZIGxhYmVsIHRocm91Z2hvdXQg4oCUIGNvbnNpc3RlbnQKIyB3aXRoIHRoZSByZXN0IG9mIHRoZSBhbmFseXNpcy4KCnB0X2RhdGEgPC0gZGF0YS5mcmFtZSgKICBwc2V1ZG90aW1lID0gcmVmZXJlbmNlX2ludGVncmF0ZWQkbW9ub2NsZTNfcHNldWRvdGltZSwKICBsMiAgICAgICAgID0gcmVmZXJlbmNlX2ludGVncmF0ZWQkcHJlZGljdGVkLmNlbGx0eXBlLmwyCikgJT4lCiAgZmlsdGVyKGlzLmZpbml0ZShwc2V1ZG90aW1lKSwgIWlzLm5hKGwyKSkKCiMgT3JkZXIgbDIgbGFiZWxzIGJ5IG1lZGlhbiBwc2V1ZG90aW1lCmwyX29yZGVyIDwtIHB0X2RhdGEgJT4lCiAgZ3JvdXBfYnkobDIpICU+JQogIHN1bW1hcmlzZShtZWRfcHQgPSBtZWRpYW4ocHNldWRvdGltZSwgbmEucm0gPSBUUlVFKSkgJT4lCiAgYXJyYW5nZShtZWRfcHQpICU+JQogIHB1bGwobDIpCgpwdF9kYXRhJGwyIDwtIGZhY3RvcihwdF9kYXRhJGwyLCBsZXZlbHMgPSBsMl9vcmRlcikKCiMg4pSA4pSAIFZpb2xpbiBwbG90IGNvbG91cmVkIGJ5IEF6aW11dGggbDIg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACnBfdmlvbGluIDwtIGdncGxvdChwdF9kYXRhLCBhZXMoeCA9IGwyLCB5ID0gcHNldWRvdGltZSwgZmlsbCA9IGwyKSkgKwogIGdlb21fdmlvbGluKHNjYWxlID0gIndpZHRoIiwgYWxwaGEgPSAwLjg1LCB0cmltID0gVFJVRSkgKwogIGdlb21fYm94cGxvdCh3aWR0aCA9IDAuMTIsIGZpbGwgPSAid2hpdGUiLCBvdXRsaWVyLnNpemUgPSAwLjMpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBhemltdXRoX2wyX2NvbG9ycywgbmEudmFsdWUgPSAiZ3JleTcwIikgKwogIHRoZW1lX2NsYXNzaWMoYmFzZV9zaXplID0gMTEpICsKICB0aGVtZSgKICAgIGF4aXMudGV4dC54ICA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQwLCBoanVzdCA9IDEsIHNpemUgPSA5KSwKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgIHBsb3QudGl0bGUgICA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgZmFjZSA9ICJib2xkIikKICApICsKICBsYWJzKAogICAgdGl0bGUgPSAiUHNldWRvdGltZSBEaXN0cmlidXRpb24gcGVyIEF6aW11dGggbDIgTGFiZWwgKFJlZmVyZW5jZSkiLAogICAgeCA9ICIiLCB5ID0gIk1vbm9jbGUzIFBzZXVkb3RpbWUiCiAgKQoKIyDilIDilIAgUmlkZ2UgcGxvdCDigJQgc2FtZSBkYXRhLCBkaWZmZXJlbnQgdmlldyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKcF9yaWRnZSA8LSBnZ3Bsb3QocHRfZGF0YSwgYWVzKHggPSBwc2V1ZG90aW1lLCB5ID0gbDIsIGZpbGwgPSBsMikpICsKICBnZW9tX2RlbnNpdHlfcmlkZ2VzKGFscGhhID0gMC43NSwgc2NhbGUgPSAxLjIsIHJlbF9taW5faGVpZ2h0ID0gMC4wMSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGF6aW11dGhfbDJfY29sb3JzLCBuYS52YWx1ZSA9ICJncmV5NzAiKSArCiAgdGhlbWVfY2xhc3NpYyhiYXNlX3NpemUgPSAxMCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiKSkgKwogIGxhYnMoCiAgICB0aXRsZSA9ICJQc2V1ZG90aW1lIERlbnNpdHkgYnkgQXppbXV0aCBsMiBMYWJlbCIsCiAgICB4ID0gIlBzZXVkb3RpbWUiLCB5ID0gIiIKICApCgpwX3Zpb2xpbiB8IHBfcmlkZ2UKCn0gIyBlbmQgZWxzZSAobW9ub2NsZTNfcHNldWRvdGltZSBleGlzdHMpCmBgYAoKCi0tLQoKIyBNYXAgTWFsaWduYW50IENENCsgVCBDZWxscyBvbnRvIFJlZmVyZW5jZQoKIyMgRmluZCBUcmFuc2ZlciBBbmNob3JzCgpgYGB7ciBmaW5kLWFuY2hvcnN9CgpEZWZhdWx0QXNzYXkocmVmZXJlbmNlX2ludGVncmF0ZWQpIDwtICJTQ1QiCiMg4pSA4pSAIEZpbmRUcmFuc2ZlckFuY2hvcnMg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACiMgbm9ybWFsaXphdGlvbi5tZXRob2QgPSAiU0NUIjogYm90aCByZWZlcmVuY2UgYW5kIHF1ZXJ5IGFyZSBTQ1Qtbm9ybWFsaXNlZAojIHJlZmVyZW5jZS5yZWR1Y3Rpb24gPSAicGNhIjogIHVzZXMgdGhlIHJlZmVyZW5jZSBQQ0Egc3BhY2UgKG5vdCByZS1ydW4gb24gcXVlcnkpCiMgZGltcyA9IDE6MjA6IGNvbnNpc3RlbnQgd2l0aCB0aGUgZGltcyB1c2VkIGZvciByZWZlcmVuY2UgY2x1c3RlcmluZy9VTUFQCgogIyBVc2UgaW50ZWdyYXRlZCBhc3NheSBmZWF0dXJlcyAoY29tbW9uIGdlbmVzKQogY29tbW9uX2ZlYXR1cmVzIDwtIGludGVyc2VjdCgKICAgICByb3duYW1lcyhyZWZlcmVuY2VfaW50ZWdyYXRlZFtbIlNDVCJdXSksCiAgICAgcm93bmFtZXMoTWFsaWduYW50Q0Q0VFtbIlNDVCJdXSkKICkKIAogY2F0KCJDb21tb24gZmVhdHVyZXM6IiwgbGVuZ3RoKGNvbW1vbl9mZWF0dXJlcyksICJcbiIpCiBjYXQoIlRvcCAxMDoiLCBoZWFkKGNvbW1vbl9mZWF0dXJlcywgMTApLCAiXG4iKQoKCgpjYXQoIj09PSBGaW5kaW5nIHRyYW5zZmVyIGFuY2hvcnMgPT09XG4iKQpjYXQoIihUaGlzIG1heSB0YWtlIDUtMTUgbWludXRlcyBkZXBlbmRpbmcgb24gY2VsbCBudW1iZXJzKVxuIikKCmFuY2hvcnMgPC0gRmluZFRyYW5zZmVyQW5jaG9ycygKICByZWZlcmVuY2UgICAgICAgICAgID0gcmVmZXJlbmNlX2ludGVncmF0ZWQsCiAgcXVlcnkgICAgICAgICAgICAgICA9IE1hbGlnbmFudENENFQsCiAgIGZlYXR1cmVzID0gY29tbW9uX2ZlYXR1cmVzLCAKICBub3JtYWxpemF0aW9uLm1ldGhvZCA9ICJTQ1QiLAogIHJlZmVyZW5jZS5yZWR1Y3Rpb24gPSAicGNhIiwKICBkaW1zICAgICAgICAgICAgICAgID0gMToyMCwKICB2ZXJib3NlICAgICAgICAgICAgID0gVFJVRQopCgpjYXQoIuKchSBBbmNob3JzIGZvdW5kOiIsIG5yb3coYW5jaG9yc0BhbmNob3JzKSwgIlxuIikKYGBgCgojIyBNYXBRdWVyeSDigJQgUHJvamVjdCBvbnRvIFJlZmVyZW5jZSBVTUFQCgpgYGB7ciBtYXAtcXVlcnl9CiMg4pSA4pSAIE1hcFF1ZXJ5IOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgAojIFRoaXMgZG9lcyB0aHJlZSB0aGluZ3Mgc2ltdWx0YW5lb3VzbHk6CiMgICAxLiBUcmFuc2ZlckRhdGE6IHRyYW5zZmVycyBsYWJlbHMgKyBwc2V1ZG90aW1lIGZyb20gcmVmZXJlbmNlIHRvIHF1ZXJ5CiMgICAyLiBJbnRlZ3JhdGVFbWJlZGRpbmdzOiBwcm9qZWN0cyBxdWVyeSBQQ0EgaW50byByZWZlcmVuY2UgUENBIHNwYWNlCiMgICAzLiBQcm9qZWN0VU1BUDogcHJvamVjdHMgcXVlcnkgY2VsbHMgb250byB0aGUgRlJPWkVOIHJlZmVyZW5jZSBVTUFQCiMKIyByZWZkYXRhIHRyYW5zZmVyczoKIyAgIC0gbW9ub2NsZTNfcHNldWRvdGltZTogY29udGludW91cyBwc2V1ZG90aW1lIHZhbHVlICh2aWEgd2VpZ2h0ZWQgYW5jaG9ycykKIyAgIC0gc2V1cmF0X2NsdXN0ZXJzOiAgICAgcmVmZXJlbmNlIGNsdXN0ZXIgSUQgKDAtNikKIyAgIC0gY2VsbF90eXBlOiAgICAgICAgICAgb3VyIGFubm90YXRlZCBjZWxsIHR5cGUgbGFiZWwKIyAgIC0gcHJlZGljdGVkLmNlbGx0eXBlLmwyOiBBemltdXRoIGwyIGxhYmVsIChLRVkgZm9yIHN0YXRlIGludGVycHJldGF0aW9uKQoKY2F0KCI9PT0gUHJvamVjdGluZyBtYWxpZ25hbnQgY2VsbHMgb250byByZWZlcmVuY2UgKE1hcFF1ZXJ5KSA9PT1cbiIpCgptYXBwZWRfTWFsaWduYW50Q0Q0VCA8LSBNYXBRdWVyeSgKICBhbmNob3JzZXQgICAgICAgICA9IGFuY2hvcnMsCiAgcXVlcnkgICAgICAgICAgICAgPSBNYWxpZ25hbnRDRDRULAogIHJlZmVyZW5jZSAgICAgICAgID0gcmVmZXJlbmNlX2ludGVncmF0ZWQsCiAgcmVmZGF0YSAgICAgICAgICAgPSBsaXN0KAogICAgIyBQUklNQVJZIOKAlCBBemltdXRoIGwyIGxhYmVsOiB0aGUgbWFpbiBzdGF0ZSBhbm5vdGF0aW9uIHVzZWQgdGhyb3VnaG91dAogICAgcHJlZGljdGVkLmNlbGx0eXBlLmwyID0gInByZWRpY3RlZC5jZWxsdHlwZS5sMiIsCiAgICAjIENPTlRJTlVPVVMg4oCUIHBzZXVkb3RpbWUgdmFsdWUgZnJvbSBNb25vY2xlMyB0cmFqZWN0b3J5CiAgICBwc2V1ZG90aW1lICAgICAgICAgICAgPSAibW9ub2NsZTNfcHNldWRvdGltZSIsCiAgICAjIFNFQ09OREFSWSDigJQgcmVmZXJlbmNlIGNsdXN0ZXIgSUQgKGludGVnZXIsIGZvciBjcm9zcy1jaGVjayBvbmx5KQogICAgc2V1cmF0X2NsdXN0ZXJzICAgICAgID0gInNldXJhdF9jbHVzdGVycyIKICApLAogIHJlZmVyZW5jZS5yZWR1Y3Rpb24gPSAicGNhIiwKICByZWR1Y3Rpb24ubW9kZWwgICAgID0gInVtYXAiCikKCmNhdCgi4pyFIE1hcFF1ZXJ5IGNvbXBsZXRlXG4iKQpjYXQoIk1hcHBlZCBtYWxpZ25hbnQgY2VsbHM6IiwgbmNvbChtYXBwZWRfTWFsaWduYW50Q0Q0VCksICJcbiIpCmNhdCgiXG5UcmFuc2ZlcnJlZCBtZXRhZGF0YSBjb2x1bW5zOlxuIikKdHJhbnNmZXJyZWRfY29scyA8LSBncmVwKCJecHJlZGljdGVkXFwuIiwgY29sbmFtZXMobWFwcGVkX01hbGlnbmFudENENFRAbWV0YS5kYXRhKSwgdmFsdWUgPSBUUlVFKQpwcmludCh0cmFuc2ZlcnJlZF9jb2xzKQoKY2F0KCJcblRyYW5zZmVycmVkIHBzZXVkb3RpbWUgc3VtbWFyeTpcbiIpCnByaW50KHN1bW1hcnkobWFwcGVkX01hbGlnbmFudENENFQkcHJlZGljdGVkLnBzZXVkb3RpbWUpKQoKY2F0KCJcblRyYW5zZmVycmVkIEF6aW11dGggbDIgZGlzdHJpYnV0aW9uIChQUklNQVJZKTpcbiIpCnByaW50KHRhYmxlKG1hcHBlZF9NYWxpZ25hbnRDRDRUJHByZWRpY3RlZC5wcmVkaWN0ZWQuY2VsbHR5cGUubDIpKQoKY2F0KCJcblRyYW5zZmVycmVkIGNsdXN0ZXIgZGlzdHJpYnV0aW9uIChzZWNvbmRhcnkgcmVmZXJlbmNlKTpcbiIpCnByaW50KHRhYmxlKG1hcHBlZF9NYWxpZ25hbnRDRDRUJHByZWRpY3RlZC5zZXVyYXRfY2x1c3RlcnMpKQpgYGAKCi0tLQoKIyBWaXN1YWxpc2UgUHJvamVjdGlvbiBvbiBSZWZlcmVuY2UgVU1BUAoKIyMgUmVmZXJlbmNlICsgTWFsaWduYW50IE92ZXJsYXkgKFBzZXVkb3RpbWUpCgpgYGB7ciBwbG90LXBzZXVkb3RpbWUtb3ZlcmxheSwgZmlnLndpZHRoPTE0LCBmaWcuaGVpZ2h0PTZ9CiMg4pSA4pSAIEJ1aWxkIGRhdGEgZnJhbWVzIGZvciBnZ3Bsb3Qg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACnJlZl91bWFwIDwtIGRhdGEuZnJhbWUoCiAgRW1iZWRkaW5ncyhyZWZlcmVuY2VfaW50ZWdyYXRlZCwgInVtYXAiKSwKICBsMiAgICAgICAgID0gcmVmZXJlbmNlX2ludGVncmF0ZWQkcHJlZGljdGVkLmNlbGx0eXBlLmwyLCAgICMgUFJJTUFSWTogQXppbXV0aCBsMgogIHBzZXVkb3RpbWUgPSByZWZlcmVuY2VfaW50ZWdyYXRlZCRtb25vY2xlM19wc2V1ZG90aW1lLAogIHR5cGUgICAgICAgPSAiUmVmZXJlbmNlIgopCmNvbG5hbWVzKHJlZl91bWFwKVsxOjJdIDwtIGMoIlVNQVBfMSIsICJVTUFQXzIiKQoKIyBFeHRlbmQgcGFsZXR0ZSB0byBpbmNsdWRlIGFueSBtYWxpZ25hbnQgbDIgbGFiZWxzIGFmdGVyIE1hcFF1ZXJ5CiMgKGRvbmUgYWdhaW4gaGVyZSBpbiBjYXNlIG5ldyBsYWJlbHMgYXBwZWFyIGFmdGVyIHByb2plY3Rpb24pCnF1ZXJ5X3VtYXAgPC0gZGF0YS5mcmFtZSgKICBFbWJlZGRpbmdzKG1hcHBlZF9NYWxpZ25hbnRDRDRULCAicmVmLnVtYXAiKSwKICBwc2V1ZG90aW1lICA9IG1hcHBlZF9NYWxpZ25hbnRDRDRUJHByZWRpY3RlZC5wc2V1ZG90aW1lLAogIGNlbGxfbGluZSAgID0gbWFwcGVkX01hbGlnbmFudENENFQkY2VsbF9saW5lLAogIGwyX3EgICAgICAgID0gbWFwcGVkX01hbGlnbmFudENENFQkcHJlZGljdGVkLnByZWRpY3RlZC5jZWxsdHlwZS5sMiwKICB0eXBlICAgICAgICA9ICJNYWxpZ25hbnQiCikKY29sbmFtZXMocXVlcnlfdW1hcClbMToyXSA8LSBjKCJVTUFQXzEiLCAiVU1BUF8yIikKCiMgRXh0ZW5kIHBhbGV0dGUgdG8gY292ZXIgYW55IG5ldyBsMiBsYWJlbHMgZnJvbSBtYWxpZ25hbnQgY2VsbHMKYWxsX2wyX2xhYmVscyA8LSB1bmlxdWUoYyhhcy5jaGFyYWN0ZXIocmVmX3VtYXAkbDIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICBhcy5jaGFyYWN0ZXIocXVlcnlfdW1hcCRsMl9xKSkpCmF6aW11dGhfbDJfY29sb3JzIDwtIGV4dGVuZF9wYWxldHRlKGF6aW11dGhfbDJfY29sb3JzLCBhbGxfbDJfbGFiZWxzKQoKIyDilIDilIAgUGxvdCAxOiBSZWZlcmVuY2UgZ3JleSwgbWFsaWduYW50IGNvbG91cmVkIGJ5IHBzZXVkb3RpbWUg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACnAxIDwtIGdncGxvdCgpICsKICBnZW9tX3BvaW50KGRhdGEgPSByZWZfdW1hcCwKICAgICAgICAgICAgIGFlcyh4ID0gVU1BUF8xLCB5ID0gVU1BUF8yKSwKICAgICAgICAgICAgIGNvbG9yID0gcmVmZXJlbmNlX2NvbG9yLCBzaXplID0gMC40LCBhbHBoYSA9IDAuNikgKwogIGdlb21fcG9pbnQoZGF0YSA9IHF1ZXJ5X3VtYXAsCiAgICAgICAgICAgICBhZXMoeCA9IFVNQVBfMSwgeSA9IFVNQVBfMiwgY29sb3IgPSBwc2V1ZG90aW1lKSwKICAgICAgICAgICAgIHNpemUgPSAxLjIsIGFscGhhID0gMC45KSArCiAgc2NhbGVfY29sb3JfdmlyaWRpc19jKG9wdGlvbiA9ICJwbGFzbWEiLCBuYW1lID0gIlBzZXVkb3RpbWUiLAogICAgICAgICAgICAgICAgICAgICAgICBuYS52YWx1ZSA9ICJncmV5NjAiKSArCiAgdGhlbWVfY2xhc3NpYyhiYXNlX3NpemUgPSAxMSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIpKSArCiAgZ2d0aXRsZSgiTWFsaWduYW50IENENFQgUHJvamVjdGVkIG9udG8gUmVmZXJlbmNlXG4oQ29sb3VyZWQgYnkgdHJhbnNmZXJyZWQgcHNldWRvdGltZSkiKQoKIyDilIDilIAgUGxvdCAyOiBSZWZlcmVuY2UgY29sb3VyZWQgYnkgQXppbXV0aCBsMiwgbWFsaWduYW50IGNlbGxzIGluIHJlZCDilIDilIDilIDilIDilIDilIAKcDIgPC0gZ2dwbG90KCkgKwogIGdlb21fcG9pbnQoZGF0YSA9IHJlZl91bWFwLAogICAgICAgICAgICAgYWVzKHggPSBVTUFQXzEsIHkgPSBVTUFQXzIsIGNvbG9yID0gbDIpLAogICAgICAgICAgICAgc2l6ZSA9IDAuNCwgYWxwaGEgPSAwLjcpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYXppbXV0aF9sMl9jb2xvcnMsCiAgICAgICAgICAgICAgICAgICAgIG5hbWUgICA9ICJBemltdXRoIGwyXG4ocmVmZXJlbmNlKSIsCiAgICAgICAgICAgICAgICAgICAgIG5hLnZhbHVlID0gImdyZXk3MCIpICsKICBnZW9tX3BvaW50KGRhdGEgPSBxdWVyeV91bWFwLAogICAgICAgICAgICAgYWVzKHggPSBVTUFQXzEsIHkgPSBVTUFQXzIpLAogICAgICAgICAgICAgY29sb3IgPSBtYWxpZ25hbnRfY29sb3IsIHNpemUgPSAxLjAsIGFscGhhID0gMC44LCBzaGFwZSA9IDE2KSArCiAgdGhlbWVfY2xhc3NpYyhiYXNlX3NpemUgPSAxMSkgKwogIHRoZW1lKAogICAgcGxvdC50aXRsZSAgICAgID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiKSwKICAgIGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC40LCAiY20iKSwKICAgIGxlZ2VuZC50ZXh0ICAgICA9IGVsZW1lbnRfdGV4dChzaXplID0gOCkKICApICsKICBnZ3RpdGxlKCJSZWZlcmVuY2UgQXppbXV0aCBsMiAoY29sb3VyKVxuKyBNYWxpZ25hbnQgQ2VsbHMgKHJlZCBvdmVybGF5KSIpCgpwMSB8IHAyCmBgYAoKIyMgQXppbXV0aCBsMiBBbm5vdGF0aW9uIE92ZXJsYXkKCmBgYHtyIHBsb3QtYXppbXV0aC1vdmVybGF5LCBmaWcud2lkdGg9MTQsIGZpZy5oZWlnaHQ9Nn0KIyDilIDilIAgUGxvdCAzOiBNYWxpZ25hbnQgY29sb3VyZWQgYnkgdHJhbnNmZXJyZWQgQXppbXV0aCBsMiBsYWJlbCDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKIyBhemltdXRoX2wyX2NvbG9ycyBhbHJlYWR5IGV4dGVuZGVkIHRvIGNvdmVyIGFsbCBsYWJlbHMgaW4gYm90aCByZWYgKyBxdWVyeQpwMyA8LSBnZ3Bsb3QoKSArCiAgZ2VvbV9wb2ludChkYXRhID0gcmVmX3VtYXAsCiAgICAgICAgICAgICBhZXMoeCA9IFVNQVBfMSwgeSA9IFVNQVBfMiksCiAgICAgICAgICAgICBjb2xvciA9IHJlZmVyZW5jZV9jb2xvciwgc2l6ZSA9IDAuNCwgYWxwaGEgPSAwLjYpICsKICBnZW9tX3BvaW50KGRhdGEgPSBxdWVyeV91bWFwLAogICAgICAgICAgICAgYWVzKHggPSBVTUFQXzEsIHkgPSBVTUFQXzIsIGNvbG9yID0gbDJfcSksCiAgICAgICAgICAgICBzaXplID0gMS4yLCBhbHBoYSA9IDAuOSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBhemltdXRoX2wyX2NvbG9ycywKICAgICAgICAgICAgICAgICAgICAgbmFtZSAgID0gIkF6aW11dGggbDJcbih0cmFuc2ZlcnJlZCkiLAogICAgICAgICAgICAgICAgICAgICBuYS52YWx1ZSA9ICJncmV5NzAiKSArCiAgdGhlbWVfY2xhc3NpYyhiYXNlX3NpemUgPSAxMSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIpKSArCiAgZ2d0aXRsZSgiTWFsaWduYW50IENENFQg4oCUIFRyYW5zZmVycmVkIEF6aW11dGggbDIgTGFiZWxzIikKCiMg4pSA4pSAIFBsb3QgNDogUGVyIGNlbGwgbGluZSDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKY2VsbF9saW5lX3BhbGV0dGUgPC0gY29sb3JSYW1wUGFsZXR0ZShicmV3ZXIucGFsKDgsICJEYXJrMiIpKSgKICBsZW5ndGgodW5pcXVlKHF1ZXJ5X3VtYXAkY2VsbF9saW5lKSkKKQpuYW1lcyhjZWxsX2xpbmVfcGFsZXR0ZSkgPC0gc29ydCh1bmlxdWUocXVlcnlfdW1hcCRjZWxsX2xpbmUpKQoKcDQgPC0gZ2dwbG90KCkgKwogIGdlb21fcG9pbnQoZGF0YSA9IHJlZl91bWFwLAogICAgICAgICAgICAgYWVzKHggPSBVTUFQXzEsIHkgPSBVTUFQXzIpLAogICAgICAgICAgICAgY29sb3IgPSByZWZlcmVuY2VfY29sb3IsIHNpemUgPSAwLjQsIGFscGhhID0gMC41KSArCiAgZ2VvbV9wb2ludChkYXRhID0gcXVlcnlfdW1hcCwKICAgICAgICAgICAgIGFlcyh4ID0gVU1BUF8xLCB5ID0gVU1BUF8yLCBjb2xvciA9IGNlbGxfbGluZSksCiAgICAgICAgICAgICBzaXplID0gMS4wLCBhbHBoYSA9IDAuOSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjZWxsX2xpbmVfcGFsZXR0ZSwgbmFtZSA9ICJDZWxsIGxpbmUiKSArCiAgdGhlbWVfY2xhc3NpYyhiYXNlX3NpemUgPSAxMSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIpKSArCiAgZ2d0aXRsZSgiTWFsaWduYW50IENENFQgcGVyIENlbGwgTGluZSIpCgpwMyB8IHA0CmBgYAoKIyMgUGVyIENlbGwgTGluZSBGYWNldCDigJQgU3RhdGUgTWFwcGluZwoKYGBge3IgcGxvdC1wZXItY2VsbGxpbmUsIGZpZy53aWR0aD0xNiwgZmlnLmhlaWdodD0xMH0KIyDilIDilIAgRmFjZXQgYnkgY2VsbCBsaW5lIHRvIHNlZSBsaW5lLXNwZWNpZmljIHByb2plY3Rpb24gcGF0dGVybnMg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACmdncGxvdCgpICsKICBnZW9tX3BvaW50KGRhdGEgPSByZWZfdW1hcCwKICAgICAgICAgICAgIGFlcyh4ID0gVU1BUF8xLCB5ID0gVU1BUF8yKSwKICAgICAgICAgICAgIGNvbG9yID0gImdyZXk5MCIsIHNpemUgPSAwLjMsIGFscGhhID0gMC41KSArCiAgZ2VvbV9wb2ludChkYXRhID0gcXVlcnlfdW1hcCwKICAgICAgICAgICAgIGFlcyh4ID0gVU1BUF8xLCB5ID0gVU1BUF8yLCBjb2xvciA9IHBzZXVkb3RpbWUpLAogICAgICAgICAgICAgc2l6ZSA9IDAuOSwgYWxwaGEgPSAwLjg1KSArCiAgZmFjZXRfd3JhcCh+IGNlbGxfbGluZSwgbmNvbCA9IDQpICsKICBzY2FsZV9jb2xvcl92aXJpZGlzX2Mob3B0aW9uID0gInBsYXNtYSIsIG5hbWUgPSAiUHNldWRvdGltZSIsIG5hLnZhbHVlID0gImdyZXk2MCIpICsKICB0aGVtZV9jbGFzc2ljKGJhc2Vfc2l6ZSA9IDEwKSArCiAgdGhlbWUoCiAgICBzdHJpcC50ZXh0ICAgICAgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDksIGZhY2UgPSAiYm9sZCIpLAogICAgc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gIiNFOEY0RkQiKSwKICAgIHBsb3QudGl0bGUgICAgICA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgZmFjZSA9ICJib2xkIikKICApICsKICBnZ3RpdGxlKCJQZXIgQ2VsbCBMaW5lOiBQcm9qZWN0aW9uIG9udG8gUmVmZXJlbmNlIFVNQVAgKFBzZXVkb3RpbWUpIikKYGBgCgotLS0KCiMgUXVhbnRpZmljYXRpb24g4oCUIFN0YXRlIEFzc2lnbm1lbnQKCiMjIEFzc2lnbiBFYWNoIE1hbGlnbmFudCBDZWxsIHRvIGEgUmVmZXJlbmNlIFN0YXRlCgpgYGB7ciBzdGF0ZS1hc3NpZ25tZW50fQojIOKUgOKUgCBTdGF0ZSBhc3NpZ25tZW50IHN0cmF0ZWd5IOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgAojIEZvciBlYWNoIG1hbGlnbmFudCBjZWxsIHdlIGhhdmUgVFdPIHNvdXJjZXMgb2Ygc3RhdGUgaW5mb3JtYXRpb246CiMgICAxLiBwcmVkaWN0ZWQucHJlZGljdGVkLmNlbGx0eXBlLmwyIOKGkiBBemltdXRoIGwyIGxhYmVsIHRyYW5zZmVycmVkIHZpYQojICAgICAgTWFwUXVlcnkgYW5jaG9ycyDigJQgUFJJTUFSWSBsYWJlbCBmb3IgYWxsIHBsb3RzIGFuZCBxdWFudGlmaWNhdGlvbgojICAgMi4gcHJlZGljdGVkLnNldXJhdF9jbHVzdGVycyAgICAgICDihpIgcmVmZXJlbmNlIGNsdXN0ZXIgaW50ZWdlciBJRCDigJQKIyAgICAgIFNFQ09OREFSWSwgZm9yIGNyb3NzLXZhbGlkYXRpb24gb25seQojCiMgQXppbXV0aCBsMiBpcyB1c2VkIGV4Y2x1c2l2ZWx5IGZvciBjb2xvdXIgY29kaW5nLCBiYXIgY2hhcnRzLCBoZWF0bWFwcywKIyBhbmQgcHNldWRvdGltZSBwbG90cy4gSXQgaXMgdW5iaWFzZWQsIGF1dG9tYXRlZCwgYW5kIGNvbnNpc3RlbnQgd2l0aCB0aGUKIyBhbmFseXNpcyBmcmFtZXdvcmsgdXNlZCB0aHJvdWdob3V0IHRoaXMgbm90ZWJvb2suCgojIOKUgOKUgCBTdGF0ZSBhc3NpZ25tZW50IOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgAojIFBSSU1BUlkgbGFiZWw6IHByZWRpY3RlZC5wcmVkaWN0ZWQuY2VsbHR5cGUubDIg4oCUIEF6aW11dGggbDIgdHJhbnNmZXJyZWQKIyAgICAgICAgICAgICAgICB2aWEgTWFwUXVlcnkgYW5jaG9ycy4gVGhpcyBpcyB0aGUgbWFpbiBzdGF0ZSBmb3IgYWxsIHBsb3RzCiMgICAgICAgICAgICAgICAgYW5kIHF1YW50aWZpY2F0aW9uIHRhYmxlcy4KIyBTRUNPTkRBUlk6ICAgICBwcmVkaWN0ZWQuc2V1cmF0X2NsdXN0ZXJzIOKAlCByZWZlcmVuY2UgY2x1c3RlciBpbnRlZ2VyIElELAojICAgICAgICAgICAgICAgIGtlcHQgb25seSBmb3IgY3Jvc3MtcmVmZXJlbmNlIC8gc2FuaXR5IGNoZWNraW5nLgoKbWFwcGVkX01hbGlnbmFudENENFQkc3RhdGVfYXppbXV0aF9sMiAgIDwtIG1hcHBlZF9NYWxpZ25hbnRDRDRUJHByZWRpY3RlZC5wcmVkaWN0ZWQuY2VsbHR5cGUubDIKbWFwcGVkX01hbGlnbmFudENENFQkc3RhdGVfcmVmX2NsdXN0ZXIgIDwtIG1hcHBlZF9NYWxpZ25hbnRDRDRUJHByZWRpY3RlZC5zZXVyYXRfY2x1c3RlcnMKbWFwcGVkX01hbGlnbmFudENENFQkcHNldWRvdGltZV92YWx1ZSAgIDwtIG1hcHBlZF9NYWxpZ25hbnRDRDRUJHByZWRpY3RlZC5wc2V1ZG90aW1lCgojIOKUgOKUgCBQc2V1ZG90aW1lIGJpbnMg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACiMgQmluIGludG8gRWFybHkgKFRuYWl2ZS1saWtlKSwgTWlkIChUQ00vVEVNLWxpa2UpLCBMYXRlIChUZW1yYS1saWtlKQojIGJhc2VkIG9uIHJlZmVyZW5jZSBwc2V1ZG90aW1lIHJhbmdlIHRlcnRpbGVzCnB0X3ZhbHMgPC0gcmVmZXJlbmNlX2ludGVncmF0ZWQkbW9ub2NsZTNfcHNldWRvdGltZQpwdF92YWxzIDwtIHB0X3ZhbHNbaXMuZmluaXRlKHB0X3ZhbHMpXQpwdF9icmVha3MgPC0gcXVhbnRpbGUocHRfdmFscywgcHJvYnMgPSBjKDAsIDEvMywgMi8zLCAxKSkKCm1hcHBlZF9NYWxpZ25hbnRDRDRUJHBzZXVkb3RpbWVfYmluIDwtIGN1dCgKICBtYXBwZWRfTWFsaWduYW50Q0Q0VCRwc2V1ZG90aW1lX3ZhbHVlLAogIGJyZWFrcyA9IHB0X2JyZWFrcywKICBsYWJlbHMgPSBjKCJFYXJseSAoVG5haXZlLWxpa2UpIiwgIk1pZCAoVENNL1RFTS1saWtlKSIsICJMYXRlIChUZW1yYS1saWtlKSIpLAogIGluY2x1ZGUubG93ZXN0ID0gVFJVRQopCgpjYXQoIj09PSBTdGF0ZSBhc3NpZ25tZW50IGNvbXBsZXRlID09PVxuIikKY2F0KCJcbkF6aW11dGggbDIgc3RhdGUgKFBSSU1BUlkg4oCUIHVzZWQgZm9yIGFsbCBxdWFudGlmaWNhdGlvbik6XG4iKQpwcmludCh0YWJsZShtYXBwZWRfTWFsaWduYW50Q0Q0VCRzdGF0ZV9hemltdXRoX2wyKSkKY2F0KCJcblJlZmVyZW5jZSBjbHVzdGVyIChzZWNvbmRhcnkgY3Jvc3MtY2hlY2spOlxuIikKcHJpbnQodGFibGUobWFwcGVkX01hbGlnbmFudENENFQkc3RhdGVfcmVmX2NsdXN0ZXIpKQpjYXQoIlxuUHNldWRvdGltZSBiaW46XG4iKQpwcmludCh0YWJsZShtYXBwZWRfTWFsaWduYW50Q0Q0VCRwc2V1ZG90aW1lX2JpbikpCmBgYAoKIyMgUXVhbnRpZmljYXRpb24gVGFibGVzCgpgYGB7ciBxdWFudGlmeS1zdGF0ZXMsIGZpZy53aWR0aD0xNCwgZmlnLmhlaWdodD02fQojIOKUgOKUgCBUYWJsZSAxOiBPdmVyYWxsIHN0YXRlIGRpc3RyaWJ1dGlvbiDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKc3RhdGVfc3VtbWFyeSA8LSBtYXBwZWRfTWFsaWduYW50Q0Q0VEBtZXRhLmRhdGEgJT4lCiAgY291bnQoc3RhdGVfYXppbXV0aF9sMiwgbmFtZSA9ICJuX2NlbGxzIikgJT4lCiAgbXV0YXRlKAogICAgcGN0ICAgICAgID0gcm91bmQoMTAwICogbl9jZWxscyAvIHN1bShuX2NlbGxzKSwgMSksCiAgICBsYWJlbCAgICAgPSBwYXN0ZTAoc3RhdGVfYXppbXV0aF9sMiwgIlxuKCIsIHBjdCwgIiUpIikKICApICU+JQogIGFycmFuZ2UoZGVzYyhuX2NlbGxzKSkKCmNhdCgiPT09IE92ZXJhbGwgTWFsaWduYW50IENENFQgU3RhdGUgRGlzdHJpYnV0aW9uID09PVxuIikKcHJpbnQoc3RhdGVfc3VtbWFyeSkKCiMg4pSA4pSAIFRhYmxlIDI6IFBlciBjZWxsIGxpbmUg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACnN0YXRlX3Blcl9saW5lIDwtIG1hcHBlZF9NYWxpZ25hbnRDRDRUQG1ldGEuZGF0YSAlPiUKICBjb3VudChjZWxsX2xpbmUsIHN0YXRlX2F6aW11dGhfbDIsIG5hbWUgPSAibl9jZWxscyIpICU+JQogIGdyb3VwX2J5KGNlbGxfbGluZSkgJT4lCiAgbXV0YXRlKHBjdCA9IHJvdW5kKDEwMCAqIG5fY2VsbHMgLyBzdW0obl9jZWxscyksIDEpKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgYXJyYW5nZShjZWxsX2xpbmUsIGRlc2Mobl9jZWxscykpCgpjYXQoIlxuPT09IFN0YXRlIERpc3RyaWJ1dGlvbiBwZXIgQ2VsbCBMaW5lID09PVxuIikKcHJpbnQoc3RhdGVfcGVyX2xpbmUsIG4gPSBJbmYpCgojIOKUgOKUgCBUYWJsZSAzOiBSZWZlcmVuY2UgY2x1c3RlciBjcm9zcy1jaGVjayDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKIyBXaGljaCByZWZlcmVuY2UgY2x1c3RlcnMgZG8gbWFsaWduYW50IGNlbGxzIG1hcCB0bz8gRm9yIGNyb3NzLXZhbGlkYXRpb24KIyBhZ2FpbnN0IHRoZSBBemltdXRoIGwyIGxhYmVsIGFib3ZlIOKAlCB0aGV5IHNob3VsZCBiZSBjb25jb3JkYW50LgpjbHVzdGVyX3N1bW1hcnkgPC0gbWFwcGVkX01hbGlnbmFudENENFRAbWV0YS5kYXRhICU+JQogIGNvdW50KHN0YXRlX3JlZl9jbHVzdGVyLCBuYW1lID0gIm5fY2VsbHMiKSAlPiUKICBtdXRhdGUocGN0ID0gcm91bmQoMTAwICogbl9jZWxscyAvIHN1bShuX2NlbGxzKSwgMSkpICU+JQogIGFycmFuZ2UoYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoc3RhdGVfcmVmX2NsdXN0ZXIpKSkKCmNhdCgiXG49PT0gUmVmZXJlbmNlIENsdXN0ZXIgRGlzdHJpYnV0aW9uIChjcm9zcy1jaGVjaykgPT09XG4iKQpwcmludChjbHVzdGVyX3N1bW1hcnkpCgojIOKUgOKUgCBUYWJsZSA0OiBQc2V1ZG90aW1lIGJpbiBzdW1tYXJ5IOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgApwdF9iaW5fc3VtbWFyeSA8LSBtYXBwZWRfTWFsaWduYW50Q0Q0VEBtZXRhLmRhdGEgJT4lCiAgY291bnQocHNldWRvdGltZV9iaW4sIG5hbWUgPSAibl9jZWxscyIpICU+JQogIG11dGF0ZShwY3QgPSByb3VuZCgxMDAgKiBuX2NlbGxzIC8gc3VtKG5fY2VsbHMpLCAxKSkKCmNhdCgiXG49PT0gUHNldWRvdGltZSBCaW4gU3VtbWFyeSA9PT1cbiIpCnByaW50KHB0X2Jpbl9zdW1tYXJ5KQpgYGAKCiMjIEJhciBDaGFydCDigJQgU3RhdGUgUHJvcG9ydGlvbnMKCmBgYHtyIGJhcnBsb3Qtc3RhdGVzLCBmaWcud2lkdGg9MTQsIGZpZy5oZWlnaHQ9OH0KIyDilIDilIAgUGxvdCAxOiBPdmVyYWxsIHN0YWNrZWQgYmFyIHBlciBjZWxsIGxpbmUg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACnBfYmFyX292ZXJhbGwgPC0gZ2dwbG90KHN0YXRlX3Blcl9saW5lLAogICAgICAgICAgICAgICAgICAgICAgICAgYWVzKHggPSBjZWxsX2xpbmUsIHkgPSBwY3QsIGZpbGwgPSBzdGF0ZV9hemltdXRoX2wyKSkgKwogIGdlb21fY29sKHBvc2l0aW9uID0gInN0YWNrIiwgY29sb3IgPSAid2hpdGUiLCBsaW5ld2lkdGggPSAwLjMpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gaWZlbHNlKHBjdCA+PSA1LCBwYXN0ZTAocGN0LCAiJSIpLCAiIikpLAogICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX3N0YWNrKHZqdXN0ID0gMC41KSwKICAgICAgICAgICAgc2l6ZSA9IDMsIGNvbG9yID0gIndoaXRlIiwgZm9udGZhY2UgPSAiYm9sZCIpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBhemltdXRoX2wyX2NvbG9ycywgbmEudmFsdWUgPSAiZ3JleTcwIiwKICAgICAgICAgICAgICAgICAgICBuYW1lID0gIkF6aW11dGggbDJcbnN0YXRlIikgKwogIHRoZW1lX2NsYXNzaWMoYmFzZV9zaXplID0gMTEpICsKICB0aGVtZSgKICAgIGF4aXMudGV4dC54ICA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQwLCBoanVzdCA9IDEpLAogICAgcGxvdC50aXRsZSAgID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiKSwKICAgIGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41LCAiY20iKQogICkgKwogIGxhYnMoCiAgICB0aXRsZSA9ICJNYWxpZ25hbnQgQ0Q0VCDigJQgU3RhdGUgQ29tcG9zaXRpb24gcGVyIENlbGwgTGluZVxuKEF6aW11dGggbDIgdHJhbnNmZXJyZWQgbGFiZWxzKSIsCiAgICB4ID0gIkNlbGwgTGluZSIsIHkgPSAiJSBDZWxscyIKICApCgojIOKUgOKUgCBQbG90IDI6IEdyb3VwZWQgYmFyIOKAlCBwc2V1ZG90aW1lIGJpbnMgcGVyIGNlbGwgbGluZSDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKcHRfYmluX2xpbmUgPC0gbWFwcGVkX01hbGlnbmFudENENFRAbWV0YS5kYXRhICU+JQogIGZpbHRlcighaXMubmEocHNldWRvdGltZV9iaW4pKSAlPiUKICBjb3VudChjZWxsX2xpbmUsIHBzZXVkb3RpbWVfYmluKSAlPiUKICBncm91cF9ieShjZWxsX2xpbmUpICU+JQogIG11dGF0ZShwY3QgPSByb3VuZCgxMDAgKiBuIC8gc3VtKG4pLCAxKSkgJT4lCiAgdW5ncm91cCgpCgpiaW5fY29sb3JzIDwtIGMoCiAgIkVhcmx5IChUbmFpdmUtbGlrZSkiID0gIiM0NTc1QjQiLAogICJNaWQgKFRDTS9URU0tbGlrZSkiICA9ICIjRkVFMDkwIiwKICAiTGF0ZSAoVGVtcmEtbGlrZSkiICAgPSAiI0Q3MzAyNyIKKQoKcF9iYXJfYmlucyA8LSBnZ3Bsb3QocHRfYmluX2xpbmUsCiAgICAgICAgICAgICAgICAgICAgICBhZXMoeCA9IGNlbGxfbGluZSwgeSA9IHBjdCwgZmlsbCA9IHBzZXVkb3RpbWVfYmluKSkgKwogIGdlb21fY29sKHBvc2l0aW9uID0gInN0YWNrIiwgY29sb3IgPSAid2hpdGUiLCBsaW5ld2lkdGggPSAwLjMpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gaWZlbHNlKHBjdCA+PSA4LCBwYXN0ZTAocGN0LCAiJSIpLCAiIikpLAogICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX3N0YWNrKHZqdXN0ID0gMC41KSwKICAgICAgICAgICAgc2l6ZSA9IDMsIGNvbG9yID0gIndoaXRlIiwgZm9udGZhY2UgPSAiYm9sZCIpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBiaW5fY29sb3JzLCBuYW1lID0gIlBzZXVkb3RpbWVcbkJpbiIpICsKICB0aGVtZV9jbGFzc2ljKGJhc2Vfc2l6ZSA9IDExKSArCiAgdGhlbWUoCiAgICBheGlzLnRleHQueCAgPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0MCwgaGp1c3QgPSAxKSwKICAgIHBsb3QudGl0bGUgICA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgZmFjZSA9ICJib2xkIikKICApICsKICBsYWJzKAogICAgdGl0bGUgPSAiTWFsaWduYW50IENENFQg4oCUIFBzZXVkb3RpbWUgQmluIERpc3RyaWJ1dGlvbiBwZXIgQ2VsbCBMaW5lIiwKICAgIHggPSAiQ2VsbCBMaW5lIiwgeSA9ICIlIENlbGxzIgogICkKCnBfYmFyX292ZXJhbGwgLyBwX2Jhcl9iaW5zCmBgYAoKIyMgSGVhdG1hcCDigJQgU3RhdGUgUHJvcG9ydGlvbnMgQWNyb3NzIENlbGwgTGluZXMKCmBgYHtyIGhlYXRtYXAtc3RhdGVzLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9Nn0KIyDilIDilIAgUHJvcG9ydGlvbiBoZWF0bWFwOiBjZWxsIGxpbmUgw5cgQXppbXV0aCBsMiBzdGF0ZSDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKIyBUaGlzIGlzIHRoZSBtb3N0IGNvbXBhY3QgcXVhbnRpZmljYXRpb24gZmlndXJlIOKAlCBwdWJsaWNhdGlvbi1yZWFkeS4KCmhlYXRtYXBfZGYgPC0gc3RhdGVfcGVyX2xpbmUgJT4lCiAgc2VsZWN0KGNlbGxfbGluZSwgc3RhdGVfYXppbXV0aF9sMiwgcGN0KSAlPiUKICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gc3RhdGVfYXppbXV0aF9sMiwgdmFsdWVzX2Zyb20gPSBwY3QsIHZhbHVlc19maWxsID0gMCkKCmhlYXRtYXBfbWF0IDwtIGFzLm1hdHJpeChoZWF0bWFwX2RmWywgLTFdKQpyb3duYW1lcyhoZWF0bWFwX21hdCkgPC0gaGVhdG1hcF9kZiRjZWxsX2xpbmUKCiMgVXNlIHBoZWF0bWFwIGZvciBjbGVhbiBoZWF0bWFwCmxpYnJhcnkocGhlYXRtYXApCnBoZWF0bWFwKAogIGhlYXRtYXBfbWF0LAogIGNvbG9yICAgICAgICAgID0gY29sb3JSYW1wUGFsZXR0ZShjKCJ3aGl0ZSIsICIjRkVFMDkwIiwgIiNENzMwMjciKSkoNTApLAogIGRpc3BsYXlfbnVtYmVycyA9IFRSVUUsCiAgbnVtYmVyX2Zvcm1hdCAgPSAiJS4xZiUlIiwKICBmb250c2l6ZV9udW1iZXIgPSA4LAogIGZvbnRzaXplX3JvdyAgID0gMTAsCiAgZm9udHNpemVfY29sICAgPSA5LAogIGFuZ2xlX2NvbCAgICAgID0gNDUsCiAgY2x1c3Rlcl9yb3dzICAgPSBUUlVFLAogIGNsdXN0ZXJfY29scyAgID0gVFJVRSwKICBtYWluICAgICAgICAgICA9ICIlIE1hbGlnbmFudCBDZWxscyBwZXIgU3RhdGUgKEF6aW11dGggbDIpXG5DbHVzdGVyZWQgYnkgY2VsbCBsaW5lIHNpbWlsYXJpdHkiLAogIGJvcmRlcl9jb2xvciAgID0gIndoaXRlIgopCmBgYAoKLS0tCgojIFBzZXVkb3RpbWUgQW5hbHlzaXMgb2YgTWFsaWduYW50IENlbGxzCgojIyBQc2V1ZG90aW1lIERpc3RyaWJ1dGlvbgoKYGBge3IgcHNldWRvdGltZS1kaXN0cmlidXRpb24sIGZpZy53aWR0aD0xNCwgZmlnLmhlaWdodD02fQojIOKUgOKUgCBEZW5zaXR5IHBsb3Q6IHBzZXVkb3RpbWUgZGlzdHJpYnV0aW9uIG9mIG1hbGlnbmFudCB2cyByZWZlcmVuY2Ug4pSA4pSA4pSA4pSA4pSA4pSA4pSACgojIFJlZmVyZW5jZSBwc2V1ZG90aW1lCnJlZl9wdCA8LSBkYXRhLmZyYW1lKAogIHBzZXVkb3RpbWUgPSByZWZlcmVuY2VfaW50ZWdyYXRlZCRtb25vY2xlM19wc2V1ZG90aW1lWwogICAgaXMuZmluaXRlKHJlZmVyZW5jZV9pbnRlZ3JhdGVkJG1vbm9jbGUzX3BzZXVkb3RpbWUpCiAgXSwKICBzb3VyY2UgPSAiSGVhbHRoeSByZWZlcmVuY2UiCikKCiMgTWFsaWduYW50IHBzZXVkb3RpbWUKbWFsX3B0IDwtIGRhdGEuZnJhbWUoCiAgcHNldWRvdGltZSA9IG1hcHBlZF9NYWxpZ25hbnRDRDRUJHBzZXVkb3RpbWVfdmFsdWVbCiAgICBpcy5maW5pdGUobWFwcGVkX01hbGlnbmFudENENFQkcHNldWRvdGltZV92YWx1ZSkKICBdLAogIHNvdXJjZSA9ICJNYWxpZ25hbnQgQ0Q0VCIKKQoKcHRfY29tYmluZWQgPC0gYmluZF9yb3dzKHJlZl9wdCwgbWFsX3B0KQoKcF9kZW5zaXR5IDwtIGdncGxvdChwdF9jb21iaW5lZCwgYWVzKHggPSBwc2V1ZG90aW1lLCBmaWxsID0gc291cmNlKSkgKwogIGdlb21fZGVuc2l0eShhbHBoYSA9IDAuNjUsIGFkanVzdCA9IDEuMikgKwogIHNjYWxlX2ZpbGxfbWFudWFsKAogICAgdmFsdWVzID0gYygiSGVhbHRoeSByZWZlcmVuY2UiID0gIiM0NTc1QjQiLCAiTWFsaWduYW50IENENFQiID0gbWFsaWduYW50X2NvbG9yKSwKICAgIG5hbWUgPSAiIgogICkgKwogIHRoZW1lX2NsYXNzaWMoYmFzZV9zaXplID0gMTIpICsKICB0aGVtZSgKICAgIGxlZ2VuZC5wb3NpdGlvbiAgPSAidG9wIiwKICAgIHBsb3QudGl0bGUgICAgICAgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIpCiAgKSArCiAgbGFicygKICAgIHRpdGxlID0gIlBzZXVkb3RpbWUgRGlzdHJpYnV0aW9uOiBIZWFsdGh5IFJlZmVyZW5jZSB2cyBNYWxpZ25hbnQgQ0Q0VCIsCiAgICBzdWJ0aXRsZSA9ICJQZWFrIHNoaWZ0IGluZGljYXRlcyBkaWZmZXJlbnRpYXRpb24gYXJyZXN0IHBvaW50IiwKICAgIHggPSAiTW9ub2NsZTMgUHNldWRvdGltZSIsIHkgPSAiRGVuc2l0eSIKICApCgojIOKUgOKUgCBQZXIgY2VsbCBsaW5lIHJpZGdlIHBsb3Qg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACm1hbF9wdF9saW5lIDwtIG1hcHBlZF9NYWxpZ25hbnRDRDRUQG1ldGEuZGF0YSAlPiUKICBmaWx0ZXIoaXMuZmluaXRlKHBzZXVkb3RpbWVfdmFsdWUpKSAlPiUKICBzZWxlY3QoY2VsbF9saW5lLCBwc2V1ZG90aW1lX3ZhbHVlKQoKcF9yaWRnZV9saW5lcyA8LSBnZ3Bsb3QobWFsX3B0X2xpbmUsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZXMoeCA9IHBzZXVkb3RpbWVfdmFsdWUsIHkgPSBjZWxsX2xpbmUsIGZpbGwgPSBjZWxsX2xpbmUpKSArCiAgZ2VvbV9kZW5zaXR5X3JpZGdlcyhhbHBoYSA9IDAuODAsIHNjYWxlID0gMS4zLCByZWxfbWluX2hlaWdodCA9IDAuMDEsCiAgICAgICAgICAgICAgICAgICAgICBxdWFudGlsZV9saW5lcyA9IFRSVUUsIHF1YW50aWxlcyA9IDIpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjZWxsX2xpbmVfcGFsZXR0ZSkgKwogIHRoZW1lX2NsYXNzaWMoYmFzZV9zaXplID0gMTEpICsKICB0aGVtZSgKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgIHBsb3QudGl0bGUgICAgICA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgZmFjZSA9ICJib2xkIikKICApICsKICBsYWJzKAogICAgdGl0bGUgPSAiUHNldWRvdGltZSBEaXN0cmlidXRpb24gcGVyIENlbGwgTGluZSIsCiAgICBzdWJ0aXRsZSA9ICJWZXJ0aWNhbCBsaW5lID0gbWVkaWFuIiwKICAgIHggPSAiUHNldWRvdGltZSIsIHkgPSAiIgogICkKCnBfZGVuc2l0eSB8IHBfcmlkZ2VfbGluZXMKYGBgCgojIyBQc2V1ZG90aW1lIFN1bW1hcnkgU3RhdGlzdGljcyBUYWJsZQoKYGBge3IgcHNldWRvdGltZS1zdGF0c30KIyDilIDilIAgRGV0YWlsZWQgcHNldWRvdGltZSBzdGF0aXN0aWNzIHBlciBjZWxsIGxpbmUg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACnB0X3N0YXRzIDwtIG1hcHBlZF9NYWxpZ25hbnRDRDRUQG1ldGEuZGF0YSAlPiUKICBmaWx0ZXIoaXMuZmluaXRlKHBzZXVkb3RpbWVfdmFsdWUpKSAlPiUKICBncm91cF9ieShjZWxsX2xpbmUpICU+JQogIHN1bW1hcmlzZSgKICAgIG5fY2VsbHMgICAgPSBuKCksCiAgICBtZWFuX3B0ICAgID0gcm91bmQobWVhbihwc2V1ZG90aW1lX3ZhbHVlKSwgMyksCiAgICBtZWRpYW5fcHQgID0gcm91bmQobWVkaWFuKHBzZXVkb3RpbWVfdmFsdWUpLCAzKSwKICAgIHNkX3B0ICAgICAgPSByb3VuZChzZChwc2V1ZG90aW1lX3ZhbHVlKSwgMyksCiAgICBxMjVfcHQgICAgID0gcm91bmQocXVhbnRpbGUocHNldWRvdGltZV92YWx1ZSwgMC4yNSksIDMpLAogICAgcTc1X3B0ICAgICA9IHJvdW5kKHF1YW50aWxlKHBzZXVkb3RpbWVfdmFsdWUsIDAuNzUpLCAzKSwKICAgIHBjdF9lYXJseSAgPSByb3VuZCgxMDAgKiBtZWFuKHBzZXVkb3RpbWVfdmFsdWUgPCBwdF9icmVha3NbMl0sIG5hLnJtID0gVFJVRSksIDEpLAogICAgcGN0X21pZCAgICA9IHJvdW5kKDEwMCAqIG1lYW4ocHNldWRvdGltZV92YWx1ZSA+PSBwdF9icmVha3NbMl0gJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBzZXVkb3RpbWVfdmFsdWUgPCBwdF9icmVha3NbM10sIG5hLnJtID0gVFJVRSksIDEpLAogICAgcGN0X2xhdGUgICA9IHJvdW5kKDEwMCAqIG1lYW4ocHNldWRvdGltZV92YWx1ZSA+PSBwdF9icmVha3NbM10sIG5hLnJtID0gVFJVRSksIDEpCiAgKSAlPiUKICBhcnJhbmdlKG1lZGlhbl9wdCkKCmNhdCgiPT09IFBzZXVkb3RpbWUgU3RhdGlzdGljcyBwZXIgQ2VsbCBMaW5lID09PVxuIikKcHJpbnQocHRfc3RhdHMsIG4gPSBJbmYpCgojIOKUgOKUgCBBZGQgcmVmZXJlbmNlIGwyIHBzZXVkb3RpbWUgZm9yIGNvbXBhcmlzb24g4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACnJlZl9zdGF0cyA8LSByZWZlcmVuY2VfaW50ZWdyYXRlZEBtZXRhLmRhdGEgJT4lCiAgZmlsdGVyKGlzLmZpbml0ZShtb25vY2xlM19wc2V1ZG90aW1lKSwgIWlzLm5hKHByZWRpY3RlZC5jZWxsdHlwZS5sMikpICU+JQogIGdyb3VwX2J5KHByZWRpY3RlZC5jZWxsdHlwZS5sMikgJT4lCiAgc3VtbWFyaXNlKAogICAgbl9jZWxscyAgID0gbigpLAogICAgbWVhbl9wdCAgID0gcm91bmQobWVhbihtb25vY2xlM19wc2V1ZG90aW1lKSwgMyksCiAgICBtZWRpYW5fcHQgPSByb3VuZChtZWRpYW4obW9ub2NsZTNfcHNldWRvdGltZSksIDMpCiAgKSAlPiUKICBhcnJhbmdlKG1lZGlhbl9wdCkKCmNhdCgiXG49PT0gUmVmZXJlbmNlIFBzZXVkb3RpbWUgcGVyIEF6aW11dGggbDIgU3RhdGUgKGZvciBjb21wYXJpc29uKSA9PT1cbiIpCnByaW50KHJlZl9zdGF0cykKYGBgCgotLS0KCiMgSW50ZWdyYXRlZCBTdW1tYXJ5IFZpc3VhbGlzYXRpb24KCiMjIEZvdXItUGFuZWwgU3VtbWFyeSBGaWd1cmUKCmBgYHtyIHN1bW1hcnktZmlndXJlLCBmaWcud2lkdGg9MTYsIGZpZy5oZWlnaHQ9MTR9CiMg4pSA4pSAIFBhbmVsIDE6IFJlZmVyZW5jZSBVTUFQIGNvbG91cmVkIGJ5IEF6aW11dGggbDIg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACnBfcmVmIDwtIERpbVBsb3QoCiAgcmVmZXJlbmNlX2ludGVncmF0ZWQsIGdyb3VwLmJ5ID0gInByZWRpY3RlZC5jZWxsdHlwZS5sMiIsCiAgcmVkdWN0aW9uID0gInVtYXAiLCBsYWJlbCA9IFRSVUUsIHJlcGVsID0gVFJVRSwKICBsYWJlbC5zaXplID0gMywgcHQuc2l6ZSA9IDAuNAopICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYXppbXV0aF9sMl9jb2xvcnMsIG5hLnZhbHVlID0gImdyZXk3MCIpICsKICBnZ3RpdGxlKCJBLiBSZWZlcmVuY2UgQ0Q0KyBUIENlbGxzXG4oQXppbXV0aCBsMiDigJQgUHJvbGlmZXJhdGluZyByZW1vdmVkKSIpICsKICB0aGVtZSgKICAgIHBsb3QudGl0bGUgICAgICA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgc2l6ZSA9IDExLCBmYWNlID0gImJvbGQiKSwKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIgogICkgKyBOb0xlZ2VuZCgpCgojIOKUgOKUgCBQYW5lbCAyOiBSZWZlcmVuY2UgcHNldWRvdGltZSDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKcF9wdF9yZWYgPC0gRmVhdHVyZVBsb3QoCiAgcmVmZXJlbmNlX2ludGVncmF0ZWQsIGZlYXR1cmVzID0gIm1vbm9jbGUzX3BzZXVkb3RpbWUiLAogIHJlZHVjdGlvbiA9ICJ1bWFwIiwgY29scyA9IGMoImxpZ2h0Ymx1ZSIsInllbGxvdyIsInJlZCIpLAogIHB0LnNpemUgPSAwLjQKKSArCiAgZ2d0aXRsZSgiQi4gUmVmZXJlbmNlIFBzZXVkb3RpbWVcbihOYWl2ZSDihpIgVGVtcmEgYXhpcykiKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgc2l6ZSA9IDExLCBmYWNlID0gImJvbGQiKSkKCiMg4pSA4pSAIFBhbmVsIDM6IE1hbGlnbmFudCBwcm9qZWN0aW9uIGNvbG91cmVkIGJ5IHBzZXVkb3RpbWUg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACnBfcHJvaiA8LSBnZ3Bsb3QoKSArCiAgZ2VvbV9wb2ludChkYXRhID0gcmVmX3VtYXAsIGFlcyh4ID0gVU1BUF8xLCB5ID0gVU1BUF8yKSwKICAgICAgICAgICAgIGNvbG9yID0gImdyZXk4OCIsIHNpemUgPSAwLjMsIGFscGhhID0gMC42KSArCiAgZ2VvbV9wb2ludChkYXRhID0gcXVlcnlfdW1hcCwgYWVzKHggPSBVTUFQXzEsIHkgPSBVTUFQXzIsIGNvbG9yID0gcHNldWRvdGltZSksCiAgICAgICAgICAgICBzaXplID0gMS4wLCBhbHBoYSA9IDAuODUpICsKICBzY2FsZV9jb2xvcl92aXJpZGlzX2Mob3B0aW9uID0gInBsYXNtYSIsIG5hbWUgPSAiUHNldWRvdGltZSIpICsKICB0aGVtZV9jbGFzc2ljKGJhc2Vfc2l6ZSA9IDEwKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgc2l6ZSA9IDExLCBmYWNlID0gImJvbGQiKSkgKwogIGdndGl0bGUoIkMuIE1hbGlnbmFudCBDRDRUIFByb2plY3RlZFxuKHBzZXVkb3RpbWUgdHJhbnNmZXJyZWQpIikKCiMg4pSA4pSAIFBhbmVsIDQ6IFN0YXRlIHByb3BvcnRpb24gYmFyIGNoYXJ0IOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgAp0b3Bfc3RhdGVzIDwtIHN0YXRlX3N1bW1hcnkgJT4lIGhlYWQoOCkgJT4lIHB1bGwoc3RhdGVfYXppbXV0aF9sMikKc3RhdGVfcGxvdF9kZiA8LSBzdGF0ZV9wZXJfbGluZSAlPiUKICBmaWx0ZXIoc3RhdGVfYXppbXV0aF9sMiAlaW4lIHRvcF9zdGF0ZXMpCgpwX3F1YW50IDwtIGdncGxvdChzdGF0ZV9wbG90X2RmLAogICAgICAgICAgICAgICAgICAgYWVzKHggPSByZW9yZGVyKGNlbGxfbGluZSwgLXBjdCksIHkgPSBwY3QsIGZpbGwgPSBzdGF0ZV9hemltdXRoX2wyKSkgKwogIGdlb21fY29sKHBvc2l0aW9uID0gInN0YWNrIiwgY29sb3IgPSAid2hpdGUiLCBsaW5ld2lkdGggPSAwLjMpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBhemltdXRoX2wyX2NvbG9ycywgbmEudmFsdWUgPSAiZ3JleTcwIiwgbmFtZSA9ICJBemltdXRoIGwyIikgKwogIHRoZW1lX2NsYXNzaWMoYmFzZV9zaXplID0gMTApICsKICB0aGVtZSgKICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDAsIGhqdXN0ID0gMSwgc2l6ZSA9IDkpLAogICAgcGxvdC50aXRsZSAgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIHNpemUgPSAxMSwgZmFjZSA9ICJib2xkIiksCiAgICBsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNCwgImNtIiksCiAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gOCkKICApICsKICBsYWJzKAogICAgdGl0bGUgPSAiRC4gU3RhdGUgUHJvcG9ydGlvbnMgcGVyIENlbGwgTGluZVxuKEF6aW11dGggbDIgdHJhbnNmZXJyZWQpIiwKICAgIHggPSAiIiwgeSA9ICIlIENlbGxzIgogICkKCihwX3JlZiB8IHBfcHRfcmVmKSAvIChwX3Byb2ogfCBwX3F1YW50KSArCiAgcGxvdF9hbm5vdGF0aW9uKAogICAgdGl0bGUgICAgPSAiTWFsaWduYW50IENENCsgVCBDZWxsIERpZmZlcmVudGlhdGlvbiBTdGF0ZSBNYXBwaW5nXG4oU8OpemFyeSBTeW5kcm9tZSDigJQgTW9ub2NsZTMgUmVmZXJlbmNlIFRyYWplY3RvcnkpIiwKICAgIHRoZW1lICAgID0gdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgc2l6ZSA9IDE0LCBmYWNlID0gImJvbGQiKSkKICApCmBgYAoKIyMgUHNldWRvdGltZSB2cyBTdGF0ZSBEb3RwbG90IChwdWJsaWNhdGlvbi1yZWFkeSkKCmBgYHtyIHBzZXVkb3RpbWUtdnMtc3RhdGUsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD01fQojIOKUgOKUgCBEb3QvYm94cGxvdDogcHNldWRvdGltZSBvZiBtYWxpZ25hbnQgY2VsbHMgZ3JvdXBlZCBieSB0cmFuc2ZlcnJlZCBzdGF0ZSDilIAKbWFsX3N0YXRlX3B0IDwtIG1hcHBlZF9NYWxpZ25hbnRDRDRUQG1ldGEuZGF0YSAlPiUKICBmaWx0ZXIoaXMuZmluaXRlKHBzZXVkb3RpbWVfdmFsdWUpLCAhaXMubmEoc3RhdGVfYXppbXV0aF9sMikpICU+JQogIG11dGF0ZShzdGF0ZSA9IHN0YXRlX2F6aW11dGhfbDIpCgpzdGF0ZV9vcmRlcl9tYWwgPC0gbWFsX3N0YXRlX3B0ICU+JQogIGdyb3VwX2J5KHN0YXRlKSAlPiUKICBzdW1tYXJpc2UobWVkID0gbWVkaWFuKHBzZXVkb3RpbWVfdmFsdWUpKSAlPiUKICBhcnJhbmdlKG1lZCkgJT4lCiAgcHVsbChzdGF0ZSkKCm1hbF9zdGF0ZV9wdCRzdGF0ZSA8LSBmYWN0b3IobWFsX3N0YXRlX3B0JHN0YXRlLCBsZXZlbHMgPSBzdGF0ZV9vcmRlcl9tYWwpCgpnZ3Bsb3QobWFsX3N0YXRlX3B0LCBhZXMoeCA9IHN0YXRlLCB5ID0gcHNldWRvdGltZV92YWx1ZSwgZmlsbCA9IHN0YXRlKSkgKwogIGdlb21fdmlvbGluKHNjYWxlID0gIndpZHRoIiwgYWxwaGEgPSAwLjgsIHRyaW0gPSBUUlVFKSArCiAgZ2VvbV9ib3hwbG90KHdpZHRoID0gMC4xLCBmaWxsID0gIndoaXRlIiwgb3V0bGllci5zaXplID0gMC4zKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYXppbXV0aF9sMl9jb2xvcnMsIG5hLnZhbHVlID0gImdyZXk3MCIpICsKICB0aGVtZV9jbGFzc2ljKGJhc2Vfc2l6ZSA9IDExKSArCiAgdGhlbWUoCiAgICBheGlzLnRleHQueCAgPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0MCwgaGp1c3QgPSAxLCBzaXplID0gOSksCiAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICBwbG90LnRpdGxlICAgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIpCiAgKSArCiAgbGFicygKICAgIHRpdGxlID0gIlBzZXVkb3RpbWUgb2YgTWFsaWduYW50IENENFQgcGVyIFRyYW5zZmVycmVkIFN0YXRlXG4oQXppbXV0aCBsMiBsYWJlbHMpIiwKICAgIHggPSAiVHJhbnNmZXJyZWQgRGlmZmVyZW50aWF0aW9uIFN0YXRlIiwKICAgIHkgPSAiVHJhbnNmZXJyZWQgUHNldWRvdGltZSIKICApCmBgYAoKLS0tCgojIFNhdmUgT3V0cHV0cwoKYGBge3Igc2F2ZS1vdXRwdXRzfQojIOKUgOKUgCBTYXZlIG1hcHBlZCBtYWxpZ25hbnQgb2JqZWN0IOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgApvdXRfZGlyIDwtICJyZXN1bHRzL01hbGlnbmFudENENFRfTW9ub2NsZTNfcHJvamVjdGlvbiIKaWYgKCFkaXIuZXhpc3RzKG91dF9kaXIpKSBkaXIuY3JlYXRlKG91dF9kaXIsIHJlY3Vyc2l2ZSA9IFRSVUUpCgpzYXZlUkRTKAogIG1hcHBlZF9NYWxpZ25hbnRDRDRULAogIGZpbGUucGF0aChvdXRfZGlyLCAiTWFsaWduYW50Q0Q0VF9tYXBwZWRfbW9ub2NsZTNfcHNldWRvdGltZV9ub1Byb2xpZl9yZWYucmRzIikKKQoKIyDilIDilIAgU2F2ZSBxdWFudGlmaWNhdGlvbiB0YWJsZXMgYXMgQ1NWIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgAp3cml0ZS5jc3YoCiAgc3RhdGVfc3VtbWFyeSwKICBmaWxlLnBhdGgob3V0X2RpciwgInN0YXRlX2Rpc3RyaWJ1dGlvbl9vdmVyYWxsLmNzdiIpLAogIHJvdy5uYW1lcyA9IEZBTFNFCikKd3JpdGUuY3N2KAogIHN0YXRlX3Blcl9saW5lLAogIGZpbGUucGF0aChvdXRfZGlyLCAic3RhdGVfZGlzdHJpYnV0aW9uX3Blcl9jZWxsbGluZS5jc3YiKSwKICByb3cubmFtZXMgPSBGQUxTRQopCndyaXRlLmNzdigKICBwdF9zdGF0cywKICBmaWxlLnBhdGgob3V0X2RpciwgInBzZXVkb3RpbWVfc3RhdHNfcGVyX2NlbGxsaW5lLmNzdiIpLAogIHJvdy5uYW1lcyA9IEZBTFNFCikKCiMg4pSA4pSAIFNhdmUgZnVsbCBtZXRhZGF0YSBmb3IgZG93bnN0cmVhbSBhbmFseXNpcyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKd3JpdGUuY3N2KAogIG1hcHBlZF9NYWxpZ25hbnRDRDRUQG1ldGEuZGF0YSwKICBmaWxlLnBhdGgob3V0X2RpciwgIk1hbGlnbmFudENENFRfZnVsbF9tZXRhZGF0YV93aXRoX3Byb2plY3Rpb24uY3N2IiksCiAgcm93Lm5hbWVzID0gVFJVRQopCgpjYXQoIuKchSBBbGwgb3V0cHV0cyBzYXZlZCB0bzoiLCBvdXRfZGlyLCAiXG4iKQpjYXQoIlxuRmlsZXMgc2F2ZWQ6XG4iKQpwcmludChsaXN0LmZpbGVzKG91dF9kaXIpKQpgYGAKCi0tLQoKIyBTZXNzaW9uIEluZm8KCmBgYHtyIHNlc3Npb24taW5mb30Kc2Vzc2lvbkluZm8oKQpgYGAKCjwhLS0g4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQCiAgICAgSU5URVJQUkVUQVRJT04gR1VJREUKICAgICDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZAKCiAgSE9XIFRPIElOVEVSUFJFVCBZT1VSIFJFU1VMVFM6CiAg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACgogIDEuIFdIRVJFIERPIE1BTElHTkFOVCBDRUxMUyBQUk9KRUNUPwogICAgIExvb2sgYXQgUGFuZWwgQyBvZiB0aGUgc3VtbWFyeSBmaWd1cmUuIElmIG1hbGlnbmFudCBjZWxscyBjbHVzdGVyCiAgICAgcHJlZG9taW5hbnRseSBvdmVyOgogICAgICAgLSBUbmFpdmUvVENNIHRlcnJpdG9yeSDihpIgZWFybHkvY2VudHJhbCBtZW1vcnkgYXJyZXN0IChjb21tb24gaW4gU1MpCiAgICAgICAtIFRFTSB0ZXJyaXRvcnkgICAgICAgICDihpIgZWZmZWN0b3IgbWVtb3J5IHBoZW5vdHlwZQogICAgICAgLSBUZW1yYSB0ZXJyaXRvcnkgICAgICAg4oaSIGxhdGUgZWZmZWN0b3IvY3l0b3RveGljIHBoZW5vdHlwZQogICAgICAgLSBNaXhlZC9zcHJlYWQgICAgICAgICAg4oaSIGhldGVyb2dlbmVvdXMgcG9wdWxhdGlvbgoKICAyLiBQU0VVRE9USU1FIFZBTFVFUzoKICAgICBDb21wYXJlIG1hbGlnbmFudCBtZWRpYW4gcHNldWRvdGltZSB0byByZWZlcmVuY2UgdmFsdWVzIGZyb20gcmVmX3N0YXRzLgogICAgIElmIG1hbGlnbmFudCBtZWRpYW4g4omIIHJlZmVyZW5jZSBUQ00gbWVkaWFuIOKGkiBUQ00tbGlrZSBhcnJlc3QuCiAgICAgQSBsZWZ0LXNoaWZ0ZWQgZGlzdHJpYnV0aW9uIChsb3cgcHNldWRvdGltZSkgPSBtb3JlIG5haXZlLWxpa2UgbWFsaWduYW50IGNlbGxzLgogICAgIFRoaXMgaXMgdGhlIGtleSBxdWFudGl0YXRpdmUgbWV0cmljIHRvIHJlcG9ydC4KCiAgMy4gQ0VMTCBMSU5FIERJRkZFUkVOQ0VTOgogICAgIFRoZSBwZXItY2VsbC1saW5lIGhlYXRtYXAgKHBoZWF0bWFwKSBhbmQgZmFjZXQgcGxvdCByZXZlYWwgd2hldGhlcgogICAgIGRpZmZlcmVudCBTw6l6YXJ5IGNlbGwgbGluZXMgaGF2ZSBkaXN0aW5jdCBkaWZmZXJlbnRpYXRpb24gc3RhdGVzLgogICAgIFRoaXMgaXMgYmlvbG9naWNhbGx5IGltcG9ydGFudCDigJQgZGlmZmVyZW50IGxpbmVzIG1heSBtb2RlbCBkaWZmZXJlbnQKICAgICBzdGFnZXMgb2YgdGhlIGRpc2Vhc2UuCgogIDQuIEFaSU1VVEggbDIgdnMgUkVGRVJFTkNFIENMVVNURVI6CiAgICAgQm90aCBzaG91bGQgYWdyZWUuIElmIHRoZXkgZGlzYWdyZWUgZm9yID4yMCUgb2YgY2VsbHMsIGNvbnNpZGVyOgogICAgICAgLSBSZS1jaGVja2luZyB0aGUgcmVmZXJlbmNlIGFubm90YXRpb24KICAgICAgIC0gTG93IHByZWRpY3Rpb24uc2NvcmUuY2VsbHR5cGUubDIgdmFsdWVzIChjaGVjayBkaXN0cmlidXRpb24pCiAgICAgICAtIE1hbGlnbmFudCBjZWxscyB0aGF0IGRvbid0IGNsZWFubHkgbWFwIHRvIGFueSBoZWFsdGh5IHN0YXRlCgogIDUuIFBTRVVET1RJTUUgQklOIERJU1RSSUJVVElPTjoKICAgICBFYXJseSAoVG5haXZlLWxpa2UpOiBtYWxpZ25hbnQgY2VsbHMgcmVzZW1ibGUgdW5kaWZmZXJlbnRpYXRlZCBzdGF0ZQogICAgIE1pZCAoVENNL1RFTS1saWtlKTogIG1hbGlnbmFudCBjZWxscyByZXNlbWJsZSBtZW1vcnkgc3RhdGVzIChtb3N0IFNTKQogICAgIExhdGUgKFRlbXJhLWxpa2UpOiAgIG1hbGlnbmFudCBjZWxscyByZXNlbWJsZSB0ZXJtaW5hbCBlZmZlY3RvcnMKCiAgS05PV04gQklPTE9HWSAoU8OpemFyeSBTeW5kcm9tZSk6CiAg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACiAgUHVibGlzaGVkIGxpdGVyYXR1cmUgKENlcmFwaW8gZXQgYWwuLCBhbmQgb3RoZXJzKSBzaG93cyBTw6l6YXJ5IGNlbGxzCiAgcHJlZG9taW5hbnRseSByZXNlbWJsZSBjZW50cmFsIG1lbW9yeSAoVENNKSBvciBza2luLWhvbWluZyBURU0gY2VsbHMuCiAgRk9YUDMrIFRyZWctbGlrZSBmZWF0dXJlcyBhcmUgYWxzbyByZXBvcnRlZCBpbiBzb21lIFNTIGNhc2VzLgogIElmIHlvdXIgbWFsaWduYW50IGNlbGxzIG1hcCBwcmVkb21pbmFudGx5IHRvIFRDTS9URU0gdGVycml0b3J5IHdpdGgKICBsb3ctdG8tbWVkaXVtIHBzZXVkb3RpbWUsIHRoaXMgaXMgY29uc2lzdGVudCB3aXRoIHB1Ymxpc2hlZCBmaW5kaW5ncy4KCiAgTk9URSBPTiBQUk9MSUZFUkFUSU5HIENFTEwgUkVNT1ZBTDoKICDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKICBCZWNhdXNlIENENCBQcm9saWZlcmF0aW5nIGNlbGxzIHdlcmUgcmVtb3ZlZCBmcm9tIHRoZSByZWZlcmVuY2UsIG1hbGlnbmFudAogIFPDqXphcnkgY2VsbHMgdGhhdCBhcmUgYWN0aXZlbHkgY3ljbGluZyB3aWxsIHByb2plY3QgdG8gdGhlaXIgbmVhcmVzdAogIGRpZmZlcmVudGlhdGlvbiBzdGF0ZSAoZS5nLiBUQ00gb3IgVEVNKSByYXRoZXIgdGhhbiB0byBhIGNlbGwtY3ljbGUKICBhcnRlZmFjdCBjbHVzdGVyLiBUaGlzIGlzIHRoZSBDT1JSRUNUIGJpb2xvZ2ljYWwgaW50ZXJwcmV0YXRpb246IGEKICBwcm9saWZlcmF0aW5nIFPDqXphcnkgY2VsbCBpcyBzdGlsbCBhIFRDTS1saWtlIG9yIFRFTS1saWtlIGNlbGwg4oCUIGl0IGp1c3QKICBoYXBwZW5zIHRvIGJlIGRpdmlkaW5nLiBUaGUgcHJvbGlmZXJhdGlvbiBzdGF0dXMgaXMgYSBzZXBhcmF0ZSBheGlzIGZyb20KICBkaWZmZXJlbnRpYXRpb24gc3RhdGUgYW5kIHNob3VsZCBub3QgY29uZmxhdGUgcHNldWRvdGltZSBhc3NpZ25tZW50LgoK4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQIC0tPg==