Biological Background
& Rationale
What is Sézary
Syndrome?
Sézary syndrome (SS) is an aggressive cutaneous T-cell
lymphoma (CTCL) characterised by the triad of erythroderma
(full-body skin redness), lymphadenopathy, and circulating malignant T
cells called Sézary cells. These are CD4+ T cells with
cerebriform (brain-like wrinkled) nuclei that have undergone malignant
transformation.
The central unresolved question in SS biology is: which
normal CD4 T cell subtype gave rise to the malignant clone?
This is the “cell of origin” question. Answering it matters because:
- It tells us which transcription factors and signalling
pathways were active when transformation occurred
- It predicts which normal T cell programmes are
retained or corrupted in the malignant cells (explaining why SS cells
home to skin, suppress immunity, etc.)
- It guides therapeutic targeting — if SS arises from
TCM cells, targeting memory T cell survival signals (e.g. IL-15/IL-7
signalling) is rational
Why Project onto a
Trajectory?
A trajectory analysis places cells along a continuous
differentiation path rather than forcing them into discrete
categories. Normal CD4 T cells differentiate from Naive → TCM → TEM →
Temra (effector arm) or Naive → TCM → Treg (regulatory arm).
When we project Sézary cell lines onto this trajectory, we can
ask:
- Where on the trajectory do malignant cells land?
(cell of origin state)
- How far along differentiation are they? (pseudotime
= differentiation maturity)
- Are different cell lines (L1–L7) at the same or different
positions? (heterogeneity in cell of origin across the
disease)
- Do all cells within a line occupy the same position, or is
there intra-line heterogeneity? (clonal evolution vs stable
malignant state)
Why Analyse Each Cell
Line Separately?
You have 7 Sézary cell lines (L1–L7) derived from different patients.
Each cell line represents a biologically independent malignant
clone. Key reasons to analyse them separately:
- If all 7 lines map to the same trajectory position, this supports a
universal cell of origin for SS (strong biological
conclusion)
- If lines differ, this indicates disease
heterogeneity — different patients may have different cells of
origin, explaining clinical variability in drug response
- Comparing cell lines that are drug-sensitive vs drug-resistant (if
known) to their trajectory position can reveal whether
differentiation state predicts drug response
Setup
# ── BIOLOGICAL NOTE ────────────────────────────────────────────────────────
# We load the same libraries as the reference trajectory script.
# RANN provides fast k-nearest-neighbour search (nn2) which we use to transfer
# Monocle3 pseudotime from reference cells to query Sézary cells by weighted
# averaging of their 15 nearest reference neighbours.
# ──────────────────────────────────────────────────────────────────────────
suppressPackageStartupMessages({
library(Seurat)
library(SeuratWrappers)
library(igraph)
library(RANN)
library(ggplot2)
library(ggrepel)
library(dplyr)
library(tidyr)
library(patchwork)
library(scales)
library(RColorBrewer)
library(knitr)
library(kableExtra)
library(future)
})
Registered S3 methods overwritten by 'htmltools':
method from
print.html tools:rstudio
print.shiny.tag tools:rstudio
print.shiny.tag.list tools:rstudio
Registered S3 method overwritten by 'rmarkdown':
method from
print.paged_df
# ── Colour palettes ────────────────────────────────────────────────────────
# These match the reference trajectory script exactly for visual consistency
STATE_COLORS <- c(
"CD4 Naive" = "#4472C4",
"CD4 TCM" = "#70AD47",
"CD4 TEM" = "#ED7D31",
"CD4 Temra/CTL" = "#C00000",
"Treg" = "#7030A0"
)
# 7 visually distinct colours for L1-L7
# Using a combination of Set1 and Dark2 palettes for maximum distinctiveness
CELL_LINE_COLORS <- c(
"L1" = "#E41A1C", # red
"L2" = "#377EB8", # blue
"L3" = "#4DAF4A", # green
"L4" = "#FF7F00", # orange
"L5" = "#984EA3", # purple
"L6" = "#A65628", # brown
"L7" = "#F781BF" # pink
)
METHOD_COLORS <- c(
"Monocle3" = "#e74c3c",
"Custom MST" = "#2980b9"
)
STATE_ORDER <- c("CD4 Naive","CD4 TCM","CD4 TEM","CD4 Temra/CTL","Treg")
CELL_LINE_ORDER <- paste0("L", 1:7)
UMAP_NAME <- "umap"
STATE_COL <- "predicted.celltype.l2"
cat("✅ Setup complete\n")
✅ Setup complete
cat("Cell line colours:\n"); print(CELL_LINE_COLORS)
Cell line colours:
L1 L2 L3 L4 L5 L6 L7
"#E41A1C" "#377EB8" "#4DAF4A" "#FF7F00" "#984EA3" "#A65628" "#F781BF"
Load Objects &
Subset to Malignant Cells
# ── BIOLOGICAL NOTE ────────────────────────────────────────────────────────
# We load the pre-computed reference object (cd4_ref_dual_trajectory.rds)
# which already contains:
# - MST graph, milestone centroids, pseudotime for all reference cells
# - Monocle3 pseudotime
# - All trajectory infrastructure built in the main pipeline
#
# CRITICAL: We subset to L1-L7 ONLY, excluding CD4T_lab and CD4T_10x.
# CD4T_lab and CD4T_10x are healthy donor CD4 T cells included as controls
# in the original dataset. Projecting them onto the reference would be
# circular (healthy cells → healthy reference = trivially perfect mapping)
# and would NOT answer the cell of origin question.
#
# We want ONLY the malignant Sézary cells (L1-L7) for projection.
# ──────────────────────────────────────────────────────────────────────────
# Load pre-computed reference with all trajectory objects
cd4_ref <- readRDS("../cd4_ref_dual_trajectory.rds")
# Load the full Sézary object
sezary_raw <- readRDS("/home/nabbasi/apollo_home/1-Seurat_RDS_OBJECT_FINAL/All_samples_Merged_with_Renamed_Clusters_Cell_state-03-12-2025.rds.rds")
cat("Full Sézary object loaded:", ncol(sezary_raw), "cells\n")
Full Sézary object loaded: 49305 cells
cat("\norig.ident breakdown (ALL samples):\n")
orig.ident breakdown (ALL samples):
print(table(sezary_raw$orig.ident))
L1 L2 L3 L4 L5 L6 L7 CD4T_lab CD4T_10x
5825 5935 6428 6006 6022 5148 5331 5106 3504
# ── Subset to malignant cell lines only ───────────────────────────────────
CELL_LINES <- paste0("L", 1:7)
sezary_obj <- subset(sezary_raw, orig.ident %in% CELL_LINES)
cat(sprintf("\n✅ Malignant cells retained: %d\n", ncol(sezary_obj)))
✅ Malignant cells retained: 40695
cat(sprintf(" Healthy controls excluded: CD4T_lab + CD4T_10x (%d cells)\n",
ncol(sezary_raw) - ncol(sezary_obj)))
Healthy controls excluded: CD4T_lab + CD4T_10x (8610 cells)
cat("\nPer cell line (cells to project):\n")
Per cell line (cells to project):
print(table(sezary_obj$orig.ident))
L1 L2 L3 L4 L5 L6 L7 CD4T_lab CD4T_10x
5825 5935 6428 6006 6022 5148 5331 0 0
# CRITICAL: Remove the full object to free RAM before MapQuery
# On RServer with shared memory, keeping sezary_raw in memory during
# MapQuery can cause the 46 GiB future.globals limit to be exceeded
rm(sezary_raw); gc()
used (Mb) gc trigger (Mb) max used (Mb)
Ncells 4244977 226.8 8237565 440.0 8237565 440.0
Vcells 1385926744 10573.8 3049213733 23263.7 2626985949 20042.4
cat("\n✅ Raw object removed from memory — ready for MapQuery\n")
✅ Raw object removed from memory — ready for MapQuery
# ── Validate both objects ─────────────────────────────────────────────────
stopifnot(
"STATE_COL missing from cd4_ref" = STATE_COL %in% colnames(cd4_ref@meta.data),
"UMAP missing from cd4_ref" = UMAP_NAME %in% names(cd4_ref@reductions),
"milestone missing from cd4_ref" = "milestone" %in% colnames(cd4_ref@meta.data),
"monocle3_pseudotime_norm missing" = "monocle3_pseudotime_norm" %in% colnames(cd4_ref@meta.data),
"RNA missing from cd4_ref" = "RNA" %in% names(cd4_ref@assays),
"RNA missing from sezary_obj" = "RNA" %in% names(sezary_obj@assays),
"UMAP model missing" = !is.null(cd4_ref@reductions$umap@misc$model)
)
cat("✅ All validation checks passed\n")
✅ All validation checks passed
# Print reference trajectory summary for context
cat("\nReference CD4 T cell state distribution:\n")
Reference CD4 T cell state distribution:
print(table(cd4_ref@meta.data[[STATE_COL]]))
CD4 Naive CD4 TCM CD4 TEM CD4 Temra/CTL Treg
2037 9067 145 10 207
cat("\nReference milestone distribution:\n")
Reference milestone distribution:
print(table(cd4_ref@meta.data$milestone))
M00 M01 M02 M03 M04 M05 M06 M07
2037 388 8679 64 81 10 146 61
Reconstruct Trajectory
Infrastructure
# ── SAFE APPROACH: derive everything from the saved reference object ────────
# We do NOT hardcode edges — instead we reconstruct the graph from the
# actual milestone centroid positions in the saved cd4_ref object.
# This guarantees exact consistency with the reference trajectory.
# ── Step 1: Rebuild milestone centroids from reference metadata ────────────
umap_coords <- as.data.frame(Embeddings(cd4_ref, UMAP_NAME))
colnames(umap_coords) <- c("UMAP_1","UMAP_2")
umap_coords$milestone <- cd4_ref@meta.data$milestone
umap_coords$state <- cd4_ref@meta.data[[STATE_COL]]
milestone_centroids <- umap_coords %>%
filter(!is.na(milestone)) %>%
group_by(milestone) %>%
summarise(
UMAP_1 = mean(UMAP_1),
UMAP_2 = mean(UMAP_2),
n_cells = n(),
state = dplyr::first(state),
.groups = "drop"
) %>%
arrange(milestone)
milestone_order <- sort(unique(milestone_centroids$milestone))
milestone_centroids$order_pos <- match(milestone_centroids$milestone, milestone_order)
cat("Milestone centroids from saved reference:\n")
Milestone centroids from saved reference:
print(milestone_centroids[, c("milestone","state","UMAP_1","UMAP_2","n_cells")])
# ── Step 2: Verify milestone-to-state mapping matches expected biology ──────
expected_map <- c(
M00="CD4 Naive",
M01="CD4 TCM", M02="CD4 TCM",
M03="CD4 TEM", M04="CD4 TEM",
M05="CD4 Temra/CTL",
M06="Treg", M07="Treg"
)
actual_map <- setNames(milestone_centroids$state, milestone_centroids$milestone)
mismatches <- names(expected_map)[expected_map != actual_map[names(expected_map)]]
if (length(mismatches) > 0) {
cat("⚠️ WARNING — milestone/state mismatch at:", paste(mismatches, collapse=", "), "\n")
cat(" Expected:", expected_map[mismatches], "\n")
cat(" Actual: ", actual_map[mismatches], "\n")
stop("Fix milestone assignment before proceeding")
} else {
cat("✅ Milestone-to-state mapping verified\n")
}
✅ Milestone-to-state mapping verified
# ── Step 3: Build centroid matrix and distance matrix ──────────────────────
centroid_mat <- as.matrix(milestone_centroids[, c("UMAP_1","UMAP_2")])
rownames(centroid_mat) <- milestone_centroids$milestone
dist_mat <- as.matrix(dist(centroid_mat, method="euclidean"))
# ── Step 4: Rebuild MST with SAME manual topology as main pipeline ─────────
# Edge list derived from your confirmed milestone distribution:
# M00=Naive(2037) M01=TCM_early(388) M02=TCM_late/branch(8679)
# M03=TEM_early(64) M04=TEM_late(81) M05=Temra(10)
# M06=Treg_naive(146) M07=Treg_terminal(61)
edge_list_ordered <- matrix(c(
"M00","M01", # Naive → TCM early
"M01","M02", # TCM early → TCM late (branch point — largest TCM cluster)
"M02","M03", # TCM late → TEM early (effector arm)
"M03","M04", # TEM early → TEM late
"M04","M05", # TEM late → Temra/CTL (terminal effector)
"M02","M06", # TCM late → Treg naive (regulatory arm)
"M06","M07" # Treg naive → Treg terminal
), ncol=2, byrow=TRUE)
edge_weights <- apply(edge_list_ordered, 1, function(e) dist_mat[e[1], e[2]])
if (any(!is.finite(edge_weights))) {
stop("Non-finite edge weights — check milestone names match dist_mat rownames")
}
mst_graph <- graph_from_edgelist(edge_list_ordered, directed=FALSE)
E(mst_graph)$weight <- edge_weights
cat("\nMST edges reconstructed:\n")
MST edges reconstructed:
for (i in seq_len(nrow(edge_list_ordered))) {
s1 <- actual_map[edge_list_ordered[i,1]]
s2 <- actual_map[edge_list_ordered[i,2]]
cat(sprintf(" %s(%s) → %s(%s) dist=%.4f\n",
edge_list_ordered[i,1], s1,
edge_list_ordered[i,2], s2,
edge_weights[i]))
}
M00(CD4 Naive) → M01(CD4 TCM) dist=5.6851
M01(CD4 TCM) → M02(CD4 TCM) dist=5.4642
M02(CD4 TCM) → M03(CD4 TEM) dist=5.6416
M03(CD4 TEM) → M04(CD4 TEM) dist=1.4646
M04(CD4 TEM) → M05(CD4 Temra/CTL) dist=0.7915
M02(CD4 TCM) → M06(Treg) dist=3.5341
M06(Treg) → M07(Treg) dist=2.2321
# ── Step 5: Graph distances from root ─────────────────────────────────────
graph_dists <- distances(mst_graph, v="M00", weights=NA)
g_dists_vec <- setNames(as.numeric(graph_dists), colnames(graph_dists))
# CRITICAL: use same pseudotime range as reference for consistent 0-100 scale
mst_pt_range <- range(cd4_ref@meta.data$mst_pseudotime_norm, na.rm=TRUE)
cat(sprintf("\nReference pseudotime range: %.1f – %.1f\n",
mst_pt_range[1], mst_pt_range[2]))
Reference pseudotime range: 0.0 – 100.0
# ── Step 6: MST edge coordinates for plotting ──────────────────────────────
mst_edges <- as_edgelist(mst_graph)
mst_edge_df <- do.call(rbind, lapply(seq_len(nrow(mst_edges)), function(i) {
m1 <- mst_edges[i,1]; m2 <- mst_edges[i,2]
r1 <- milestone_centroids[milestone_centroids$milestone==m1,]
r2 <- milestone_centroids[milestone_centroids$milestone==m2,]
data.frame(x1=r1$UMAP_1, y1=r1$UMAP_2, x2=r2$UMAP_1, y2=r2$UMAP_2)
}))
# ── Step 7: Reference UMAP background ─────────────────────────────────────
ref_bg <- data.frame(
UMAP_1 = Embeddings(cd4_ref, UMAP_NAME)[,1],
UMAP_2 = Embeddings(cd4_ref, UMAP_NAME)[,2],
state = cd4_ref@meta.data[[STATE_COL]]
)
# ── Step 8: project_onto_mst function ─────────────────────────────────────
project_onto_mst <- function(cell_coords, centroid_mat, mst_graph, graph_dists) {
edges <- as_edgelist(mst_graph)
n_cells <- nrow(cell_coords)
pseudotime <- numeric(n_cells)
nearest_ms <- character(n_cells)
for (i in seq_len(n_cells)) {
cx <- cell_coords[i,1]; cy <- cell_coords[i,2]
best_dist <- Inf; best_pt <- 0; best_ms <- NA_character_
for (e in seq_len(nrow(edges))) {
m1 <- edges[e,1]; m2 <- edges[e,2]
p1 <- centroid_mat[m1,]; p2 <- centroid_mat[m2,]
dx <- p2[1]-p1[1]; dy <- p2[2]-p1[2]
len2 <- dx^2 + dy^2
if (len2 < 1e-10) next
t <- max(0, min(1, ((cx-p1[1])*dx + (cy-p1[2])*dy) / len2))
proj <- c(p1[1]+t*dx, p1[2]+t*dy)
d <- sqrt((cx-proj[1])^2 + (cy-proj[2])^2)
if (d < best_dist) {
best_dist <- d
best_pt <- graph_dists[m1] + t*(graph_dists[m2]-graph_dists[m1])
best_ms <- if (t < 0.5) m1 else m2
}
}
pseudotime[i] <- best_pt
nearest_ms[i] <- best_ms
}
list(pseudotime=pseudotime, nearest_ms=nearest_ms)
}
cat("\n✅ Trajectory infrastructure ready\n")
✅ Trajectory infrastructure ready
cat(sprintf("MST: %d nodes, %d edges | Root: M00 (CD4 Naive)\n",
vcount(mst_graph), ecount(mst_graph)))
MST: 8 nodes, 7 edges | Root: M00 (CD4 Naive)
The key differences from what I had before are that the milestone
verification step checks the actual state assignments from your object
against expected biology and stops with a clear error message if
anything is wrong, the comment on M02 now correctly notes it is the
large TCM cluster (8,679 cells) that acts as the branch
point, and the mst_pt_range is taken directly from the
saved reference pseudotime rather than being recalculated, guaranteeing
the 0–100 scale for Sézary cells is identical to the reference scale.
Everything downstream in the script stays unchanged.
MapQuery: Project
Sézary Cell Lines onto Reference
# ── BIOLOGICAL NOTE ────────────────────────────────────────────────────────
# MapQuery is a Seurat function that performs reference-based cell mapping.
# It does two things simultaneously:
#
# 1. LABEL TRANSFER: Uses weighted nearest-neighbour anchors (cells that
# are mutually similar between reference and query in PCA space) to
# transfer cell type labels from reference → query. Each Sézary cell
# gets a predicted CD4 state label (Naive/TCM/TEM/Temra/Treg) with a
# confidence score (0-1).
#
# 2. UMAP PROJECTION: Projects Sézary cells into the reference UMAP
# coordinate system. This is NOT a new UMAP — it places each Sézary
# cell at the coordinates it would occupy IF it were part of the
# reference dataset. This allows direct visual comparison on the
# same UMAP space.
#
# WHY THIS MATTERS BIOLOGICALLY:
# The predicted state tells us which normal CD4 subtype the malignant cell
# most transcriptomically resembles. If a Sézary cell line projects to
# CD4 TCM space with high confidence, it means the malignant transcriptome
# most closely matches central memory T cells — implying TCM is the cell
# of origin or that the malignant transformation arrested differentiation
# at the TCM stage.
#
# TECHNICAL: We use plan("sequential") to disable parallelism because the
# reference object is ~46 GiB when exported to parallel workers, exceeding
# RServer memory limits. Sequential mode is slower but memory-safe.
# ──────────────────────────────────────────────────────────────────────────
plan("sequential")
options(future.globals.maxSize = 60 * 1024^3)
DefaultAssay(cd4_ref) <- "RNA"
DefaultAssay(sezary_obj) <- "RNA"
# FindVariableFeatures on reference ensures gene selection is current
cd4_ref <- FindVariableFeatures(cd4_ref, nfeatures=3000, verbose=FALSE)
cat("Finding transfer anchors...\n")
Finding transfer anchors...
cat("(This identifies cells that are mutually similar between\n")
(This identifies cells that are mutually similar between
cat(" reference and query in PCA space — the bridge for label transfer)\n\n")
reference and query in PCA space — the bridge for label transfer)
anchors <- FindTransferAnchors(
reference = cd4_ref,
query = sezary_obj,
normalization.method = "LogNormalize",
reference.reduction = "pca",
dims = 1:30,
reduction = "pcaproject",
verbose = FALSE
)
cat(sprintf("✅ Anchors found: %d anchor pairs\n", nrow(anchors@anchors)))
✅ Anchors found: 1499 anchor pairs
cat("\nRunning MapQuery (label transfer + UMAP projection)...\n")
Running MapQuery (label transfer + UMAP projection)...
sezary_obj <- MapQuery(
anchorset = anchors,
query = sezary_obj,
reference = cd4_ref,
refdata = list(
predicted_state = STATE_COL, # transfer CD4 state labels
predicted_milestone = "milestone" # transfer milestone labels
),
reference.reduction = "pca",
reduction.model = "umap", # project into reference UMAP space
verbose = FALSE
)
Using method 'umap'
0% 10 20 30 40 50 60 70 80 90 100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
cat("✅ MapQuery complete\n\n")
✅ MapQuery complete
# ── Quality check ─────────────────────────────────────────────────────────
cat("=== PROJECTION QUALITY CHECK ===\n")
=== PROJECTION QUALITY CHECK ===
cat(sprintf("Cells projected: %d\n", ncol(sezary_obj)))
Cells projected: 40695
cat(sprintf("Mean prediction score: %.3f (>0.5 = reliable, >0.7 = confident)\n",
mean(sezary_obj$predicted.predicted_state.score, na.rm=TRUE)))
Mean prediction score: 0.962 (>0.5 = reliable, >0.7 = confident)
cat(sprintf("Cells with score >0.5: %d (%.1f%%)\n",
sum(sezary_obj$predicted.predicted_state.score > 0.5, na.rm=TRUE),
100*mean(sezary_obj$predicted.predicted_state.score > 0.5, na.rm=TRUE)))
Cells with score >0.5: 40591 (99.7%)
cat(sprintf("Cells with score >0.7: %d (%.1f%%)\n",
sum(sezary_obj$predicted.predicted_state.score > 0.7, na.rm=TRUE),
100*mean(sezary_obj$predicted.predicted_state.score > 0.7, na.rm=TRUE)))
Cells with score >0.7: 39584 (97.3%)
cat("\n=== PREDICTED STATE BY CELL LINE ===\n")
=== PREDICTED STATE BY CELL LINE ===
print(table(sezary_obj$predicted.predicted_state,
sezary_obj$orig.ident,
dnn=c("Predicted state","Cell line")))
Cell line
Predicted state L1 L2 L3 L4 L5 L6 L7 CD4T_lab CD4T_10x
CD4 Naive 6 0 0 3 0 0 1 0 0
CD4 TCM 5639 5933 6428 6003 6022 5148 5330 0 0
CD4 TEM 180 2 0 0 0 0 0 0 0
cat("\n=== PREDICTED MILESTONE BY CELL LINE ===\n")
=== PREDICTED MILESTONE BY CELL LINE ===
print(table(sezary_obj$predicted.predicted_milestone,
sezary_obj$orig.ident,
dnn=c("Predicted milestone","Cell line")))
Cell line
Predicted milestone L1 L2 L3 L4 L5 L6 L7 CD4T_lab CD4T_10x
M00 6 0 0 3 0 0 1 0 0
M01 0 0 1 1 0 0 0 0 0
M02 5726 5935 6427 6002 6022 5148 5330 0 0
M04 93 0 0 0 0 0 0 0 0
Assign Pseudotime to
Sézary Cell Lines
# ── BIOLOGICAL NOTE ────────────────────────────────────────────────────────
# We assign pseudotime to Sézary cells using TWO methods:
#
# METHOD 1 — MST projection:
# Each Sézary cell's ref.umap coordinates (from MapQuery) are projected
# onto the nearest edge of the MST. Pseudotime = interpolated graph distance
# from the root (M00 = CD4 Naive). This gives a biologically interpretable
# value: 0 = most naive-like, 100 = most terminally differentiated.
#
# METHOD 2 — KNN-weighted Monocle3 transfer:
# For each Sézary cell, we find its 15 nearest reference cells in UMAP space,
# then compute a weighted average of their Monocle3 pseudotime values.
# Weights are inverse distance (closer reference cells contribute more).
# This transfers the Monocle3 pseudotime scale without re-running Monocle3
# on the query — appropriate because Monocle3 cannot easily incorporate
# new query cells into an existing principal graph.
#
# INTERPRETATION:
# - High pseudotime (>50): malignant cells resemble mature/differentiated T cells
# - Low pseudotime (<25): malignant cells resemble naive or early memory T cells
# - This tells us the DIFFERENTIATION STAGE at which malignant transformation occurred
# ──────────────────────────────────────────────────────────────────────────
stopifnot("ref.umap missing — MapQuery may have failed" =
"ref.umap" %in% names(sezary_obj@reductions))
# Extract Sézary UMAP coordinates in reference space
sez_umap_coords <- as.matrix(Embeddings(sezary_obj, "ref.umap"))
colnames(sez_umap_coords) <- c("UMAP_1","UMAP_2")
# ── Method 1: MST pseudotime ──────────────────────────────────────────────
cat("Projecting Sézary cells onto MST...\n")
Projecting Sézary cells onto MST...
sez_mst <- project_onto_mst(sez_umap_coords, centroid_mat, mst_graph, g_dists_vec)
# ── Method 2: KNN-weighted Monocle3 pseudotime ───────────────────────────
cat("Transferring Monocle3 pseudotime via KNN (k=15)...\n")
Transferring Monocle3 pseudotime via KNN (k=15)...
ref_umap_mat <- Embeddings(cd4_ref, UMAP_NAME)
ref_m3_pt <- cd4_ref@meta.data$monocle3_pseudotime_norm
nn_result <- nn2(data=ref_umap_mat, query=sez_umap_coords, k=15)
inv_dist <- 1 / (nn_result$nn.dists + 1e-6)
weights <- inv_dist / rowSums(inv_dist)
# ── Assign to metadata ────────────────────────────────────────────────────
meta_sez <- sezary_obj@meta.data # Fixed: remove [mailto: ] syntax
meta_sez$mst_pseudotime <- pmax(0, pmin(100,
100 * (sez_mst$pseudotime - mst_pt_range[1]) /
(mst_pt_range[2] - mst_pt_range[1])
))
meta_sez$nearest_milestone_mst <- sez_mst$nearest_ms
meta_sez$monocle3_pseudotime <- rowSums(
weights * matrix(ref_m3_pt[nn_result$nn.idx],
nrow=nrow(nn_result$nn.idx))
)
sezary_obj@meta.data <- meta_sez # Fixed: remove [mailto: ] syntax
# ── Build plot dataframe with cell line info ──────────────────────────────
sez_plot_df <- data.frame(
UMAP_1 = sez_umap_coords[,"UMAP_1"],
UMAP_2 = sez_umap_coords[,"UMAP_2"],
cell_line = factor(sezary_obj$orig.ident, levels=CELL_LINE_ORDER),
predicted_state = sezary_obj$predicted.predicted_state,
pred_score = sezary_obj$predicted.predicted_state.score,
mst_pt = sezary_obj@meta.data$mst_pseudotime,
m3_pt = sezary_obj@meta.data$monocle3_pseudotime,
milestone_ref = sezary_obj@meta.data$nearest_milestone_mst
)
cat(sprintf("✅ Pseudotime assigned to %d cells\n", nrow(sez_plot_df)))
✅ Pseudotime assigned to 40695 cells
# FIXED: Correct sprintf() usage for range vectors
mst_range <- range(sez_plot_df$mst_pt, na.rm=TRUE)
m3_range <- range(sez_plot_df$m3_pt, na.rm=TRUE)
cat(sprintf("MST pseudotime range: %.1f – %.1f\n", mst_range[1], mst_range[2]))
MST pseudotime range: 0.0 – 5.0
cat(sprintf("Monocle3 pseudotime range: %.1f – %.1f\n", m3_range[1], m3_range[2]))
Monocle3 pseudotime range: 1.9 – 99.3
cat("\nMST pseudotime per cell line (median):\n")
MST pseudotime per cell line (median):
sez_plot_df %>%
group_by(cell_line) %>%
summarise(median_mst = round(median(mst_pt, na.rm=TRUE), 1),
median_m3 = round(median(m3_pt, na.rm=TRUE), 1),
.groups="drop") %>%
print()
NA
Visualisation 1 — All
Cell Lines Together


Visualisation 2 —
Predicted State per Cell Line

# ── Stacked bar: proportion of cells per state per line ───────────────────
p_state_stack <- ggplot(state_per_line,
aes(x=cell_line, y=pct, fill=predicted_state)) +
geom_col(width=.75, colour="white", linewidth=.3) +
geom_text(aes(label=ifelse(pct >= 5, paste0(pct,"%"), "")),
position=position_stack(vjust=0.5),
size=3.2, colour="white", fontface="bold") +
scale_fill_manual(values=STATE_COLORS, name="Predicted CD4 state") +
scale_y_continuous(expand=c(0,0), limits=c(0,105)) +
theme_classic() +
labs(x="Cell line", y="% of cells",
title="Predicted CD4 state composition per Sézary cell line",
subtitle="Each bar = 100% of that cell line's cells | Labels shown for proportions ≥5%") +
theme(plot.title = element_text(size=13, face="bold"),
axis.text.x = element_text(size=12, face="bold"),
legend.title = element_text(size=10, face="bold"))
print(p_state_stack)
# ── Print summary table ───────────────────────────────────────────────────
cat("\nState proportions per cell line:\n")
State proportions per cell line:
state_per_line %>%
select(cell_line, predicted_state, n, pct) %>%
arrange(cell_line, desc(pct)) %>%
kable(col.names=c("Cell line","Predicted state","N cells","% cells"),
caption="Predicted CD4 state composition per Sézary cell line") %>%
kable_styling(bootstrap_options=c("striped","hover"), full_width=FALSE) %>%
print()
Predicted CD4 state composition per Sézary cell line
| Cell line |
Predicted state |
N cells |
% cells |
| L1 |
CD4 TCM |
5639 |
96.8 |
| L1 |
CD4 TEM |
180 |
3.1 |
| L1 |
CD4 Naive |
6 |
0.1 |
| L2 |
CD4 TCM |
5933 |
100.0 |
| L2 |
CD4 TEM |
2 |
0.0 |
| L3 |
CD4 TCM |
6428 |
100.0 |
| L4 |
CD4 TCM |
6003 |
100.0 |
| L4 |
CD4 Naive |
3 |
0.0 |
| L5 |
CD4 TCM |
6022 |
100.0 |
| L6 |
CD4 TCM |
5148 |
100.0 |
| L7 |
CD4 TCM |
5330 |
100.0 |
| L7 |
CD4 Naive |
1 |
0.0 |
NULL
Visualisation 3 —
Pseudotime per Cell Line
# ── BIOLOGICAL NOTE ────────────────────────────────────────────────────────
# Pseudotime measures DIFFERENTIATION MATURITY on a 0-100 scale where:
# 0 = most naive-like (undifferentiated, CD4 Naive = M00)
# 100 = most terminally differentiated (Temra or terminal Treg)
#
# For Sézary cells, the pseudotime distribution tells us:
#
# LOW pseudotime (0-30):
# Malignant cells resemble undifferentiated/early memory cells.
# Suggests transformation occurred at an early differentiation stage.
# These tumours may express naive/TCM survival factors (IL-7R, CCR7).
#
# INTERMEDIATE pseudotime (30-60):
# Cells resemble central/transitional memory T cells (TCM stage).
# Most SS literature suggests this — Sézary cells often retain CCR4,
# CD27, and other TCM markers while losing CCR7.
#
# HIGH pseudotime (60-100):
# Cells resemble TEM or Temra — unusual for SS as most SS cells
# do not express high levels of GZMB, PRF1, or CX3CR1.
# If a line maps here, it may represent a more cytotoxic variant.
#
# Comparing lines: if L1 has pseudotime ~30 and L5 has pseudotime ~60,
# this means L5 cells are more differentiated than L1 cells, possibly
# explaining differences in disease aggressiveness or drug sensitivity.
# ──────────────────────────────────────────────────────────────────────────
# ── Panel A: MST pseudotime violin per cell line ──────────────────────────
p_violin_mst <- ggplot(sez_plot_df,
aes(x=cell_line, y=mst_pt, fill=cell_line)) +
geom_violin(scale="width", trim=FALSE, alpha=.85, colour="white") +
geom_boxplot(width=.08, fill="white", outlier.size=.8,
outlier.alpha=.4, colour="grey30") +
# Reference lines for biological interpretation
geom_hline(yintercept=33, linetype="dashed", colour="grey60", linewidth=.5) +
geom_hline(yintercept=66, linetype="dashed", colour="grey60", linewidth=.5) +
annotate("text", x=0.4, y=16, label="Naive/\nearly TCM", size=2.8,
colour="grey40", fontface="italic", hjust=0) +
annotate("text", x=0.4, y=50, label="TCM/\nearly TEM", size=2.8,
colour="grey40", fontface="italic", hjust=0) +
annotate("text", x=0.4, y=83, label="TEM/\nTemra/Treg", size=2.8,
colour="grey40", fontface="italic", hjust=0) +
scale_fill_manual(values=CELL_LINE_COLORS, guide="none") +
scale_y_continuous(limits=c(0,100), breaks=seq(0,100,25)) +
theme_classic() +
labs(x="Cell line", y="MST pseudotime (0–100)",
title="Differentiation maturity per Sézary cell line (Custom MST)",
subtitle="Dashed lines = trajectory thirds | 0=Naive, 100=Temra/Terminal Treg") +
theme(plot.title = element_text(size=12, face="bold"),
axis.text.x = element_text(size=11, face="bold"))
# ── Panel B: Monocle3 pseudotime violin ───────────────────────────────────
p_violin_m3 <- ggplot(sez_plot_df,
aes(x=cell_line, y=m3_pt, fill=cell_line)) +
geom_violin(scale="width", trim=FALSE, alpha=.85, colour="white") +
geom_boxplot(width=.08, fill="white", outlier.size=.8,
outlier.alpha=.4, colour="grey30") +
geom_hline(yintercept=33, linetype="dashed", colour="grey60", linewidth=.5) +
geom_hline(yintercept=66, linetype="dashed", colour="grey60", linewidth=.5) +
scale_fill_manual(values=CELL_LINE_COLORS, guide="none") +
scale_y_continuous(limits=c(0,100), breaks=seq(0,100,25)) +
theme_classic() +
labs(x="Cell line", y="Monocle3 pseudotime (0–100)",
title="Differentiation maturity per Sézary cell line (Monocle3)",
subtitle="KNN-weighted transfer from 15 nearest reference neighbours") +
theme(plot.title = element_text(size=12, face="bold"),
axis.text.x = element_text(size=11, face="bold"))
print(p_violin_mst / p_violin_m3)

Visualisation 4 —
Nearest Milestone per Cell Line
# ── BIOLOGICAL NOTE ────────────────────────────────────────────────────────
# Nearest milestone is the most PRECISE cell of origin indicator because
# it places cells at a specific point on the differentiation path, not just
# a broad state category. The milestone system has 8 positions:
#
# M00 = CD4 Naive (root)
# M01 = TCM early (CCR7+, S100A4+, AQP3 low)
# M02 = TCM late (branch point — CCR4+, ITGB1+) ← KEY BRANCH POINT
# M03 = TEM early (GZMK+, CCL5+, CXCR3+)
# M04 = TEM late (EOMES+, HOPX+)
# M05 = Temra/CTL (GZMB+, PRF1+, CX3CR1+, NKG7+)
# M06 = Treg naive (FOXP3+, IL2RA+, IKZF2+)
# M07 = Treg terminal (CTLA4+, TIGIT+, TNFRSF18+)
#
# M02 is the BRANCH POINT — cells at M02 are at the decision point between
# becoming effector memory (TEM arm) or regulatory (Treg arm). If Sézary
# cells cluster at M02, this is biologically very interesting — it suggests
# the malignant transformation captured a cell that was "deciding" its fate.
# ──────────────────────────────────────────────────────────────────────────
# Milestone labels with biological annotation
milestone_labels <- c(
"M00" = "M00\nNaive",
"M01" = "M01\nTCM early",
"M02" = "M02\nTCM late\n(branch)",
"M03" = "M03\nTEM early",
"M04" = "M04\nTEM late",
"M05" = "M05\nTemra/CTL",
"M06" = "M06\nTreg naive",
"M07" = "M07\nTreg terminal"
)
# Compute milestone distribution per cell line
ms_per_line <- sez_plot_df %>%
filter(!is.na(milestone_ref)) %>%
count(cell_line, milestone_ref) %>%
group_by(cell_line) %>%
mutate(pct = round(100 * n / sum(n), 1)) %>%
ungroup() %>%
mutate(
milestone_ref = factor(milestone_ref, levels=milestone_order),
ms_state = milestone_centroids$state[
match(milestone_ref, milestone_centroids$milestone)]
)
# ── Heatmap-style tile plot: cell line × milestone ────────────────────────
p_ms_heatmap <- ggplot(ms_per_line,
aes(x=milestone_ref, y=cell_line, fill=pct)) +
geom_tile(colour="white", linewidth=.5) +
geom_text(aes(label=ifelse(pct >= 2, paste0(pct,"%"), "")),
size=3.2, fontface="bold",
colour=ifelse(ms_per_line$pct > 40, "white", "grey20")) +
scale_fill_gradientn(
colours = c("#F7FBFF","#DEEBF7","#9ECAE1","#4292C6","#08519C","#08306B"),
name = "% of cell\nline cells",
limits = c(0,100)
) +
scale_x_discrete(labels=milestone_labels) +
theme_classic() +
labs(x="Reference milestone", y="Sézary cell line",
title="Nearest reference milestone distribution per cell line",
subtitle="Darker = more cells mapping to that milestone | M02 = branch point between effector and regulatory arms") +
theme(plot.title = element_text(size=13, face="bold"),
axis.text.x = element_text(size=9),
axis.text.y = element_text(size=11, face="bold"),
legend.title = element_text(size=9))
print(p_ms_heatmap)

# ── Stacked bar: milestone per cell line ──────────────────────────────────
ms_state_colors <- setNames(
STATE_COLORS[milestone_centroids$state[match(milestone_order, milestone_centroids$milestone)]],
milestone_order
)
p_ms_stack <- ggplot(ms_per_line,
aes(x=cell_line, y=pct, fill=milestone_ref)) +
geom_col(width=.75, colour="white", linewidth=.3) +
geom_text(aes(label=ifelse(pct >= 5, paste0(milestone_ref,"\n",pct,"%"), "")),
position=position_stack(vjust=0.5),
size=2.8, colour="white", fontface="bold") +
scale_fill_manual(values=ms_state_colors,
labels=milestone_labels,
name="Milestone") +
scale_y_continuous(expand=c(0,0), limits=c(0,105)) +
theme_classic() +
labs(x="Cell line", y="% of cells",
title="Milestone composition per cell line",
subtitle="Colour = CD4 state | Label = milestone ID and % | shown if ≥5%") +
theme(plot.title = element_text(size=13, face="bold"),
axis.text.x = element_text(size=12, face="bold"))
print(p_ms_stack)

Visualisation 5 —
Individual Cell Line UMAP Panels
# ── BIOLOGICAL NOTE ────────────────────────────────────────────────────────
# This produces a detailed per-cell-line plot showing:
# - Top panel: cells coloured by predicted state (what state they resemble)
# - Bottom panel: cells coloured by MST pseudotime (how mature they are)
#
# For each cell line, you want to ask:
# 1. Is the cell line HOMOGENEOUS (all cells in one state/region) or
# HETEROGENEOUS (scattered across multiple states)?
# 2. Does the cell line sit closer to the root (low pseudotime = naive-like)
# or the tips (high pseudotime = mature effector or Treg-like)?
# 3. Are there TWO subpopulations within one line? This could indicate
# clonal evolution or technical mixing.
# ──────────────────────────────────────────────────────────────────────────
plot_list <- list()
for (cl in CELL_LINE_ORDER) {
df_cl <- sez_plot_df %>% filter(cell_line == cl)
n_cl <- nrow(df_cl)
# State plot for this cell line
p_state <- ggplot() +
geom_point(data=ref_bg[sample(nrow(ref_bg), min(3000, nrow(ref_bg))),],
aes(x=UMAP_1, y=UMAP_2), colour="grey60", size=.2, alpha=.4) +
geom_segment(data=mst_edge_df, aes(x=x1,y=y1,xend=x2,yend=y2),
colour="grey40", linewidth=.6, alpha=.6) +
geom_point(data=df_cl,
aes(x=UMAP_1, y=UMAP_2, colour=predicted_state),
size=1.2, alpha=.85) +
geom_text_repel(data=milestone_centroids,
aes(x=UMAP_1, y=UMAP_2, label=milestone),
size=2, colour="grey20", fontface="bold",
max.overlaps=8, segment.size=.2) +
scale_colour_manual(values=STATE_COLORS, name="State",
drop=FALSE, na.value="grey40") +
theme_classic() +
labs(x="UMAP-1", y="UMAP-2",
title=sprintf("%s — Predicted state (n=%s cells)",
cl, scales::comma(n_cl))) +
theme(plot.title = element_text(size=10, face="bold",
colour=CELL_LINE_COLORS[cl]),
legend.text = element_text(size=7),
legend.title= element_text(size=8))
# Pseudotime plot for this cell line
p_pt <- ggplot() +
geom_point(data=ref_bg[sample(nrow(ref_bg), min(3000, nrow(ref_bg))),],
aes(x=UMAP_1, y=UMAP_2), colour="grey60", size=.2, alpha=.4) +
geom_segment(data=mst_edge_df, aes(x=x1,y=y1,xend=x2,yend=y2),
colour="grey40", linewidth=.6, alpha=.6) +
geom_point(data=df_cl,
aes(x=UMAP_1, y=UMAP_2, colour=mst_pt),
size=1.2, alpha=.9) +
geom_text_repel(data=milestone_centroids,
aes(x=UMAP_1, y=UMAP_2, label=milestone),
size=2, colour="grey40", fontface="bold",
max.overlaps=8, segment.size=.2) +
scale_colour_gradientn(
colours = c("#0D0887","#6A00A8","#B12A90","#E16462","#FCA636","#F0F921"),
name="Pseudotime\n(0–100)", limits=c(0,100)) +
theme_classic() +
labs(x="UMAP-1", y="UMAP-2",
title=sprintf("%s — MST pseudotime", cl)) +
theme(plot.title = element_text(size=10, face="bold",
colour=CELL_LINE_COLORS[cl]),
legend.text = element_text(size=7),
legend.title= element_text(size=8))
plot_list[[paste0(cl,"_state")]] <- p_state
plot_list[[paste0(cl,"_pt")]] <- p_pt
}
# Print in 2-column layout (state | pseudotime) per line
for (cl in CELL_LINE_ORDER) {
print(plot_list[[paste0(cl,"_state")]] | plot_list[[paste0(cl,"_pt")]])
}







Visualisation 6 —
Method Agreement per Cell Line
# ── BIOLOGICAL NOTE ────────────────────────────────────────────────────────
# We compare MST and Monocle3 pseudotime PER CELL LINE to assess robustness.
# If both methods agree on a cell line's pseudotime position, the result is
# robust. If they disagree for a specific line, that line may sit in a
# trajectory region where the two methods handle geometry differently
# (e.g. near the branch point M02).
# ──────────────────────────────────────────────────────────────────────────
# Per-line Spearman correlation
line_cors <- sez_plot_df %>%
group_by(cell_line) %>%
summarise(
spearman = round(cor(mst_pt, m3_pt, method="spearman",
use="complete.obs"), 3),
n = n(),
.groups = "drop"
)
cat("Method agreement (Spearman ρ) per cell line:\n")
Method agreement (Spearman ρ) per cell line:
print(line_cors)
p_method_scatter <- ggplot(sez_plot_df,
aes(x=mst_pt, y=m3_pt, colour=cell_line)) +
geom_point(size=.5, alpha=.4) +
geom_abline(slope=1, intercept=0, linetype="dashed",
colour="grey40", linewidth=.7) +
geom_smooth(method="lm", se=FALSE, linewidth=.8) +
geom_text(data=line_cors,
aes(x=5, y=95, label=sprintf("ρ=%.2f", spearman)),
size=3, fontface="bold", hjust=0, colour="grey20") +
scale_colour_manual(values=CELL_LINE_COLORS, guide="none") +
facet_wrap(~cell_line, ncol=4) +
theme_classic() +
labs(x="Custom MST pseudotime (0–100)",
y="Monocle3 pseudotime (0–100)",
title="Method agreement per cell line: Custom MST vs Monocle3 pseudotime",
subtitle="Dashed = perfect agreement | ρ = Spearman correlation | Higher ρ = more reliable pseudotime assignment") +
theme(plot.title = element_text(size=12, face="bold"),
strip.text = element_text(size=10, face="bold"),
strip.background = element_rect(fill="grey95"))
print(p_method_scatter)

Summary Table —
Complete Cell Line Analysis
# ── BIOLOGICAL NOTE ────────────────────────────────────────────────────────
# This comprehensive table is your primary result table for the paper.
# Key columns to interpret:
#
# dominant_state: the most common predicted CD4 state for that cell line
# dominant_state_pct: how confident — if >80%, strong cell of origin signal
# mean_pred_score: MapQuery confidence — >0.7 is reliable
# mst_mean_pt: average differentiation maturity (0=naive, 100=terminal)
# dominant_milestone: the single most common trajectory position
#
# For the paper, you will want to state something like:
# "X of 7 cell lines predominantly mapped to CD4 TCM (milestones M01-M02),
# with mean MST pseudotime of Y ± Z, indicating that Sézary syndrome
# cell lines transcriptomically resemble central memory CD4 T cells at
# an intermediate stage of differentiation."
# ──────────────────────────────────────────────────────────────────────────
sez_summary <- sez_plot_df %>%
group_by(cell_line) %>%
summarise(
n_cells = n(),
mean_pred_score = round(mean(pred_score, na.rm=TRUE), 3),
high_conf_pct = round(100 * mean(pred_score > 0.7, na.rm=TRUE), 1),
dominant_state = names(sort(table(predicted_state), decreasing=TRUE))[1],
dominant_state_pct = round(100 * max(table(predicted_state)) / n(), 1),
dominant_milestone = names(sort(table(milestone_ref), decreasing=TRUE))[1],
mst_mean_pt = round(mean(mst_pt, na.rm=TRUE), 1),
mst_sd_pt = round(sd(mst_pt, na.rm=TRUE), 1),
mst_median_pt = round(median(mst_pt, na.rm=TRUE), 1),
m3_mean_pt = round(mean(m3_pt, na.rm=TRUE), 1),
m3_sd_pt = round(sd(m3_pt, na.rm=TRUE), 1),
.groups = "drop"
) %>%
arrange(mst_mean_pt)
kable(sez_summary,
col.names = c("Cell line","N cells","Mean pred.\nscore",
"% score\n>0.7",
"Dominant\nstate","Dominant\nstate %",
"Dominant\nmilestone",
"MST mean\npt","MST SD",
"MST median\npt",
"M3 mean\npt","M3 SD"),
caption = "Complete per cell line trajectory summary — cell of origin analysis") %>%
kable_styling(bootstrap_options=c("striped","hover","condensed"),
full_width=TRUE) %>%
column_spec(5, bold=TRUE) %>%
column_spec(6, bold=TRUE) %>%
column_spec(8:10, color="white", background=METHOD_COLORS["Custom MST"]) %>%
column_spec(11:12, color="white", background=METHOD_COLORS["Monocle3"])
Complete per cell line trajectory summary — cell of origin analysis
| Cell line |
N cells |
Mean pred.
scor |
| % score
>0 |
7|Dominant
sta |
e | Dominant
sta |
e %|Dominant
miles |
one | MST m |
an
pt| |
ST SD| MST me |
ian
pt| M3 |
mean
p |
| L2 |
5935 |
0.986 |
99.7 |
CD4 TCM |
100.0 |
M03 |
3.1 |
0.6 |
2.7 |
61.6 |
6.0 |
| L1 |
5825 |
0.841 |
82.4 |
CD4 TCM |
96.8 |
M02 |
3.2 |
1.0 |
2.7 |
72.3 |
21.9 |
| L3 |
6428 |
0.974 |
99.8 |
CD4 TCM |
100.0 |
M03 |
3.2 |
0.7 |
2.7 |
61.8 |
9.1 |
| L5 |
6022 |
0.967 |
99.8 |
CD4 TCM |
100.0 |
M03 |
3.2 |
0.7 |
2.7 |
59.6 |
6.5 |
| L6 |
5148 |
0.989 |
99.8 |
CD4 TCM |
100.0 |
M07 |
3.5 |
0.6 |
4.0 |
61.9 |
9.6 |
| L7 |
5331 |
0.990 |
99.9 |
CD4 TCM |
100.0 |
M07 |
3.5 |
0.7 |
4.0 |
62.3 |
10.5 |
| L4 |
6006 |
0.988 |
99.5 |
CD4 TCM |
100.0 |
M07 |
3.7 |
0.6 |
4.0 |
59.2 |
5.5 |
# ── Per-line milestone distribution table ─────────────────────────────────
ms_dist_all <- sez_plot_df %>%
filter(!is.na(milestone_ref)) %>%
count(cell_line, milestone_ref) %>%
left_join(milestone_centroids[,c("milestone","state","order_pos")],
by=c("milestone_ref"="milestone")) %>%
filter(!is.na(order_pos)) %>%
group_by(cell_line) %>%
mutate(pct = round(100 * n / sum(n), 1)) %>%
ungroup() %>%
mutate(milestone_ref = factor(milestone_ref, levels=milestone_order)) %>%
arrange(cell_line, order_pos)
cat("\n\nMilestone distribution per cell line:\n")
Milestone distribution per cell line:
ms_dist_all %>%
select(cell_line, milestone_ref, state, n, pct) %>%
kable(col.names=c("Cell line","Milestone","State","N cells","% cells"),
caption="Per cell line milestone distribution") %>%
kable_styling(bootstrap_options=c("striped","condensed"), full_width=FALSE) %>%
print()
Per cell line milestone distribution
| Cell line |
Milestone |
State |
N cells |
% cells |
| L1 |
M00 |
CD4 Naive |
142 |
2.4 |
| L1 |
M01 |
CD4 TCM |
2 |
0.0 |
| L1 |
M02 |
CD4 TCM |
1515 |
26.0 |
| L1 |
M03 |
CD4 TEM |
1443 |
24.8 |
| L1 |
M04 |
CD4 TEM |
1375 |
23.6 |
| L1 |
M05 |
CD4 Temra/CTL |
290 |
5.0 |
| L1 |
M06 |
Treg |
7 |
0.1 |
| L1 |
M07 |
Treg |
1051 |
18.0 |
| L2 |
M00 |
CD4 Naive |
4 |
0.1 |
| L2 |
M01 |
CD4 TCM |
3 |
0.1 |
| L2 |
M02 |
CD4 TCM |
53 |
0.9 |
| L2 |
M03 |
CD4 TEM |
3877 |
65.3 |
| L2 |
M04 |
CD4 TEM |
61 |
1.0 |
| L2 |
M05 |
CD4 Temra/CTL |
20 |
0.3 |
| L2 |
M06 |
Treg |
7 |
0.1 |
| L2 |
M07 |
Treg |
1910 |
32.2 |
| L3 |
M00 |
CD4 Naive |
7 |
0.1 |
| L3 |
M01 |
CD4 TCM |
4 |
0.1 |
| L3 |
M02 |
CD4 TCM |
508 |
7.9 |
| L3 |
M03 |
CD4 TEM |
3316 |
51.6 |
| L3 |
M04 |
CD4 TEM |
9 |
0.1 |
| L3 |
M06 |
Treg |
13 |
0.2 |
| L3 |
M07 |
Treg |
2571 |
40.0 |
| L4 |
M00 |
CD4 Naive |
42 |
0.7 |
| L4 |
M01 |
CD4 TCM |
1 |
0.0 |
| L4 |
M02 |
CD4 TCM |
32 |
0.5 |
| L4 |
M03 |
CD4 TEM |
1278 |
21.3 |
| L4 |
M04 |
CD4 TEM |
1 |
0.0 |
| L4 |
M06 |
Treg |
10 |
0.2 |
| L4 |
M07 |
Treg |
4642 |
77.3 |
| L5 |
M00 |
CD4 Naive |
4 |
0.1 |
| L5 |
M01 |
CD4 TCM |
1 |
0.0 |
| L5 |
M02 |
CD4 TCM |
262 |
4.4 |
| L5 |
M03 |
CD4 TEM |
3295 |
54.7 |
| L5 |
M04 |
CD4 TEM |
3 |
0.0 |
| L5 |
M07 |
Treg |
2457 |
40.8 |
| L6 |
M00 |
CD4 Naive |
1 |
0.0 |
| L6 |
M01 |
CD4 TCM |
1 |
0.0 |
| L6 |
M02 |
CD4 TCM |
41 |
0.8 |
| L6 |
M03 |
CD4 TEM |
1774 |
34.5 |
| L6 |
M04 |
CD4 TEM |
110 |
2.1 |
| L6 |
M05 |
CD4 Temra/CTL |
2 |
0.0 |
| L6 |
M06 |
Treg |
46 |
0.9 |
| L6 |
M07 |
Treg |
3173 |
61.6 |
| L7 |
M00 |
CD4 Naive |
2 |
0.0 |
| L7 |
M01 |
CD4 TCM |
1 |
0.0 |
| L7 |
M02 |
CD4 TCM |
61 |
1.1 |
| L7 |
M03 |
CD4 TEM |
1901 |
35.7 |
| L7 |
M04 |
CD4 TEM |
1 |
0.0 |
| L7 |
M06 |
Treg |
12 |
0.2 |
| L7 |
M07 |
Treg |
3353 |
62.9 |
NULL
Prediction Score
Quality Check

Save All Outputs
# Create output directories if they don't exist
dir.create("Objects", showWarnings=FALSE)
dir.create("Figures", showWarnings=FALSE)
dir.create("Tables", showWarnings=FALSE)
# ── RDS objects ────────────────────────────────────────────────────────────
saveRDS(sezary_obj, "Objects/sezary_celllines_projected.rds")
saveRDS(mst_graph, "Objects/mst_graph.rds")
saveRDS(milestone_centroids, "Objects/milestone_centroids.rds")
# ── CSV tables ─────────────────────────────────────────────────────────────
write.csv(sez_summary, "Tables/cellline_trajectory_summary.csv", row.names=FALSE)
write.csv(state_per_line, "Tables/cellline_state_proportions.csv", row.names=FALSE)
write.csv(ms_dist_all, "Tables/cellline_milestone_distribution.csv",row.names=FALSE)
write.csv(line_cors, "Tables/cellline_method_agreement.csv", row.names=FALSE)
write.csv(sez_plot_df, "Tables/cellline_all_cells_metadata.csv", row.names=FALSE)
# ── Figures ────────────────────────────────────────────────────────────────
ggsave("Figures/fig_all_lines_umap.pdf", p_all_lines, width=14, height=12)
ggsave("Figures/fig_faceted_lines.pdf", p_faceted, width=18, height=10)
ggsave("Figures/fig_state_per_line.pdf", p_state_stack, width=14, height=6)
ggsave("Figures/fig_pseudotime_mst.pdf", p_violin_mst, width=14, height=6)
ggsave("Figures/fig_pseudotime_m3.pdf", p_violin_m3, width=14, height=6)
ggsave("Figures/fig_milestone_heatmap.pdf", p_ms_heatmap, width=14, height=7)
ggsave("Figures/fig_milestone_stack.pdf", p_ms_stack, width=14, height=7)
ggsave("Figures/fig_method_agreement.pdf", p_method_scatter,width=14, height=6)
ggsave("Figures/fig_prediction_scores.pdf", p_scores, width=12, height=5)
# Individual line plots
for (cl in CELL_LINE_ORDER) {
p_combined <- plot_list[[paste0(cl,"_state")]] | plot_list[[paste0(cl,"_pt")]]
ggsave(sprintf("Figures/fig_%s_umap.pdf", cl),
p_combined, width=16, height=7)
}
cat("✅ All outputs saved\n")
✅ All outputs saved
cat(" Objects/ — RDS files\n")
Objects/ — RDS files
cat(" Figures/ — PDF plots\n")
Figures/ — PDF plots
cat(" Tables/ — CSV data files\n")
Tables/ — CSV data files
Biological
Interpretation Guide
╔══════════════════════════════════════════════════════════════════════╗
║ BIOLOGICAL INTERPRETATION GUIDE FOR YOUR RESULTS ║
╚══════════════════════════════════════════════════════════════════════╝
STEP 1: CHECK DOMINANT STATE (sez_summary table, dominant_state column)
─────────────────────────────────────────────────────────────────────────
• If most lines show CD4 TCM (dominant_state_pct >70%):
→ CONCLUSION: Sézary syndrome arises from Central Memory T cells
→ IMPLICATION: TCM survival signals (IL-7, IL-15, CCR7-independent
homing) drive malignant cell persistence
→ LITERATURE: Consistent with Wang et al. 2023, Woollard et al. 2022
• If most lines show CD4 TEM:
→ CONCLUSION: Transformation occurred at the effector memory stage
→ IMPLICATION: Malignant cells have acquired partial effector
programmes (GZMK, CCL5) but not terminal cytotoxicity
• If lines split between TCM and TEM:
→ CONCLUSION: Disease heterogeneity — different patients have
different cells of origin
→ IMPLICATION: May explain clinical variability in drug response
STEP 2: CHECK DOMINANT MILESTONE (dominant_milestone column)
─────────────────────────────────────────────────────────────────────────
• M01 or M02 dominant → early-to-late TCM (most likely for SS)
• M02 dominant (branch point) → arrested at fate decision point
• M03 or M04 dominant → TEM commitment despite TCM-like label
• M06 dominant → Treg-like features (check FOXP3 expression)
STEP 3: CHECK PREDICTION SCORES (mean_pred_score column)
─────────────────────────────────────────────────────────────────────────
• >0.7: High confidence — strong cell of origin conclusion
• 0.5-0.7: Moderate — mention in limitations
• <0.5: Low — malignant transcriptome substantially deviates from
all normal reference states; discuss as 'partial resemblance'
STEP 4: CHECK PSEUDOTIME (mst_mean_pt column)
─────────────────────────────────────────────────────────────────────────
• 0-33: Naive/early TCM differentiation stage
• 33-66: TCM/TEM transition stage
• 66-100: Mature effector or Treg stage
• Compare across lines: if L1 pseudotime < L5 pseudotime, L5 is more
mature/differentiated — may correlate with aggressiveness
STEP 5: LOOK AT THE FACETED UMAP
─────────────────────────────────────────────────────────────────────────
• Tight clusters in one region → homogeneous cell line
• Scattered cells → heterogeneous line (may have subclones)
• Lines overlapping the same UMAP region → same cell of origin
• Lines in different UMAP regions → different cell of origin
LS0tCnRpdGxlOiAiU8OpemFyeSBDZWxsIExpbmUgUHJvamVjdGlvbiBvbnRvIENENCBUIENlbGwgUmVmZXJlbmNlIFRyYWplY3RvcnkiCnN1YnRpdGxlOiAiUGVyIGNlbGwgbGluZSB0cmFqZWN0b3J5IG1hcHBpbmcgfCBDZWxsIG9mIG9yaWdpbiBhbmFseXNpcyB8IEwx4oCTTDcgU8OpemFyeSBsaW5lcyIKYXV0aG9yOiAiTkFTSVIgTUFITU9PRCBBQkJBU0kiCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OgogICAgICBjb2xsYXBzZWQ6IGZhbHNlCiAgICB0aGVtZTogam91cm5hbAotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoCiAgZWNobyAgICAgID0gVFJVRSwKICBtZXNzYWdlICAgPSBGQUxTRSwKICB3YXJuaW5nICAgPSBGQUxTRSwKICBmaWcuYWxpZ24gPSAiY2VudGVyIiwKICBkcGkgICAgICAgPSAxNTAKKQpgYGAKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBCaW9sb2dpY2FsIEJhY2tncm91bmQgJiBSYXRpb25hbGUKCiMjIFdoYXQgaXMgU8OpemFyeSBTeW5kcm9tZT8KClPDqXphcnkgc3luZHJvbWUgKFNTKSBpcyBhbiBhZ2dyZXNzaXZlICoqY3V0YW5lb3VzIFQtY2VsbCBseW1waG9tYSAoQ1RDTCkqKiBjaGFyYWN0ZXJpc2VkCmJ5IHRoZSB0cmlhZCBvZiBlcnl0aHJvZGVybWEgKGZ1bGwtYm9keSBza2luIHJlZG5lc3MpLCBseW1waGFkZW5vcGF0aHksIGFuZCBjaXJjdWxhdGluZwptYWxpZ25hbnQgVCBjZWxscyBjYWxsZWQgKipTw6l6YXJ5IGNlbGxzKiouIFRoZXNlIGFyZSBDRDQrIFQgY2VsbHMgd2l0aCBjZXJlYnJpZm9ybQooYnJhaW4tbGlrZSB3cmlua2xlZCkgbnVjbGVpIHRoYXQgaGF2ZSB1bmRlcmdvbmUgbWFsaWduYW50IHRyYW5zZm9ybWF0aW9uLgoKVGhlIGNlbnRyYWwgdW5yZXNvbHZlZCBxdWVzdGlvbiBpbiBTUyBiaW9sb2d5IGlzOiAqKndoaWNoIG5vcm1hbCBDRDQgVCBjZWxsIHN1YnR5cGUKZ2F2ZSByaXNlIHRvIHRoZSBtYWxpZ25hbnQgY2xvbmU/KiogVGhpcyBpcyB0aGUgImNlbGwgb2Ygb3JpZ2luIiBxdWVzdGlvbi4KQW5zd2VyaW5nIGl0IG1hdHRlcnMgYmVjYXVzZToKCi0gSXQgdGVsbHMgdXMgKip3aGljaCB0cmFuc2NyaXB0aW9uIGZhY3RvcnMgYW5kIHNpZ25hbGxpbmcgcGF0aHdheXMqKiB3ZXJlIGFjdGl2ZQogIHdoZW4gdHJhbnNmb3JtYXRpb24gb2NjdXJyZWQKLSBJdCBwcmVkaWN0cyAqKndoaWNoIG5vcm1hbCBUIGNlbGwgcHJvZ3JhbW1lcyoqIGFyZSByZXRhaW5lZCBvciBjb3JydXB0ZWQgaW4gdGhlCiAgbWFsaWduYW50IGNlbGxzIChleHBsYWluaW5nIHdoeSBTUyBjZWxscyBob21lIHRvIHNraW4sIHN1cHByZXNzIGltbXVuaXR5LCBldGMuKQotIEl0IGd1aWRlcyAqKnRoZXJhcGV1dGljIHRhcmdldGluZyoqIOKAlCBpZiBTUyBhcmlzZXMgZnJvbSBUQ00gY2VsbHMsIHRhcmdldGluZwogIG1lbW9yeSBUIGNlbGwgc3Vydml2YWwgc2lnbmFscyAoZS5nLiBJTC0xNS9JTC03IHNpZ25hbGxpbmcpIGlzIHJhdGlvbmFsCgojIyBXaHkgUHJvamVjdCBvbnRvIGEgVHJhamVjdG9yeT8KCkEgdHJhamVjdG9yeSBhbmFseXNpcyBwbGFjZXMgY2VsbHMgYWxvbmcgYSAqKmNvbnRpbnVvdXMgZGlmZmVyZW50aWF0aW9uIHBhdGgqKgpyYXRoZXIgdGhhbiBmb3JjaW5nIHRoZW0gaW50byBkaXNjcmV0ZSBjYXRlZ29yaWVzLiBOb3JtYWwgQ0Q0IFQgY2VsbHMgZGlmZmVyZW50aWF0ZQpmcm9tIE5haXZlIOKGkiBUQ00g4oaSIFRFTSDihpIgVGVtcmEgKGVmZmVjdG9yIGFybSkgb3IgTmFpdmUg4oaSIFRDTSDihpIgVHJlZyAocmVndWxhdG9yeSBhcm0pLgoKV2hlbiB3ZSBwcm9qZWN0IFPDqXphcnkgY2VsbCBsaW5lcyBvbnRvIHRoaXMgdHJhamVjdG9yeSwgd2UgY2FuIGFzazoKCjEuICoqV2hlcmUgb24gdGhlIHRyYWplY3RvcnkgZG8gbWFsaWduYW50IGNlbGxzIGxhbmQ/KiogKGNlbGwgb2Ygb3JpZ2luIHN0YXRlKQoyLiAqKkhvdyBmYXIgYWxvbmcgZGlmZmVyZW50aWF0aW9uIGFyZSB0aGV5PyoqIChwc2V1ZG90aW1lID0gZGlmZmVyZW50aWF0aW9uIG1hdHVyaXR5KQozLiAqKkFyZSBkaWZmZXJlbnQgY2VsbCBsaW5lcyAoTDHigJNMNykgYXQgdGhlIHNhbWUgb3IgZGlmZmVyZW50IHBvc2l0aW9ucz8qKgogICAoaGV0ZXJvZ2VuZWl0eSBpbiBjZWxsIG9mIG9yaWdpbiBhY3Jvc3MgdGhlIGRpc2Vhc2UpCjQuICoqRG8gYWxsIGNlbGxzIHdpdGhpbiBhIGxpbmUgb2NjdXB5IHRoZSBzYW1lIHBvc2l0aW9uLCBvciBpcyB0aGVyZSBpbnRyYS1saW5lCiAgIGhldGVyb2dlbmVpdHk/KiogKGNsb25hbCBldm9sdXRpb24gdnMgc3RhYmxlIG1hbGlnbmFudCBzdGF0ZSkKCiMjIFdoeSBBbmFseXNlIEVhY2ggQ2VsbCBMaW5lIFNlcGFyYXRlbHk/CgpZb3UgaGF2ZSA3IFPDqXphcnkgY2VsbCBsaW5lcyAoTDHigJNMNykgZGVyaXZlZCBmcm9tIGRpZmZlcmVudCBwYXRpZW50cy4gRWFjaCBjZWxsIGxpbmUKcmVwcmVzZW50cyBhICoqYmlvbG9naWNhbGx5IGluZGVwZW5kZW50IG1hbGlnbmFudCBjbG9uZSoqLiBLZXkgcmVhc29ucyB0byBhbmFseXNlIHRoZW0Kc2VwYXJhdGVseToKCi0gSWYgYWxsIDcgbGluZXMgbWFwIHRvIHRoZSBzYW1lIHRyYWplY3RvcnkgcG9zaXRpb24sIHRoaXMgc3VwcG9ydHMgYSAqKnVuaXZlcnNhbAogIGNlbGwgb2Ygb3JpZ2luKiogZm9yIFNTIChzdHJvbmcgYmlvbG9naWNhbCBjb25jbHVzaW9uKQotIElmIGxpbmVzIGRpZmZlciwgdGhpcyBpbmRpY2F0ZXMgKipkaXNlYXNlIGhldGVyb2dlbmVpdHkqKiDigJQgZGlmZmVyZW50IHBhdGllbnRzCiAgbWF5IGhhdmUgZGlmZmVyZW50IGNlbGxzIG9mIG9yaWdpbiwgZXhwbGFpbmluZyBjbGluaWNhbCB2YXJpYWJpbGl0eSBpbiBkcnVnIHJlc3BvbnNlCi0gQ29tcGFyaW5nIGNlbGwgbGluZXMgdGhhdCBhcmUgZHJ1Zy1zZW5zaXRpdmUgdnMgZHJ1Zy1yZXNpc3RhbnQgKGlmIGtub3duKSB0byB0aGVpcgogIHRyYWplY3RvcnkgcG9zaXRpb24gY2FuIHJldmVhbCB3aGV0aGVyICoqZGlmZmVyZW50aWF0aW9uIHN0YXRlIHByZWRpY3RzIGRydWcgcmVzcG9uc2UqKgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIFNldHVwCgpgYGB7ciBsaWJyYXJpZXN9CiMg4pSA4pSAIEJJT0xPR0lDQUwgTk9URSDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKIyBXZSBsb2FkIHRoZSBzYW1lIGxpYnJhcmllcyBhcyB0aGUgcmVmZXJlbmNlIHRyYWplY3Rvcnkgc2NyaXB0LgojIFJBTk4gcHJvdmlkZXMgZmFzdCBrLW5lYXJlc3QtbmVpZ2hib3VyIHNlYXJjaCAobm4yKSB3aGljaCB3ZSB1c2UgdG8gdHJhbnNmZXIKIyBNb25vY2xlMyBwc2V1ZG90aW1lIGZyb20gcmVmZXJlbmNlIGNlbGxzIHRvIHF1ZXJ5IFPDqXphcnkgY2VsbHMgYnkgd2VpZ2h0ZWQKIyBhdmVyYWdpbmcgb2YgdGhlaXIgMTUgbmVhcmVzdCByZWZlcmVuY2UgbmVpZ2hib3Vycy4KIyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKCnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyh7CiAgbGlicmFyeShTZXVyYXQpCiAgbGlicmFyeShTZXVyYXRXcmFwcGVycykKICBsaWJyYXJ5KGlncmFwaCkKICBsaWJyYXJ5KFJBTk4pCiAgbGlicmFyeShnZ3Bsb3QyKQogIGxpYnJhcnkoZ2dyZXBlbCkKICBsaWJyYXJ5KGRwbHlyKQogIGxpYnJhcnkodGlkeXIpCiAgbGlicmFyeShwYXRjaHdvcmspCiAgbGlicmFyeShzY2FsZXMpCiAgbGlicmFyeShSQ29sb3JCcmV3ZXIpCiAgbGlicmFyeShrbml0cikKICBsaWJyYXJ5KGthYmxlRXh0cmEpCiAgbGlicmFyeShmdXR1cmUpCn0pCgojIOKUgOKUgCBDb2xvdXIgcGFsZXR0ZXMg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACiMgVGhlc2UgbWF0Y2ggdGhlIHJlZmVyZW5jZSB0cmFqZWN0b3J5IHNjcmlwdCBleGFjdGx5IGZvciB2aXN1YWwgY29uc2lzdGVuY3kKU1RBVEVfQ09MT1JTIDwtIGMoCiAgIkNENCBOYWl2ZSIgICAgID0gIiM0NDcyQzQiLAogICJDRDQgVENNIiAgICAgICA9ICIjNzBBRDQ3IiwKICAiQ0Q0IFRFTSIgICAgICAgPSAiI0VEN0QzMSIsCiAgIkNENCBUZW1yYS9DVEwiID0gIiNDMDAwMDAiLAogICJUcmVnIiAgICAgICAgICA9ICIjNzAzMEEwIgopCgojIDcgdmlzdWFsbHkgZGlzdGluY3QgY29sb3VycyBmb3IgTDEtTDcKIyBVc2luZyBhIGNvbWJpbmF0aW9uIG9mIFNldDEgYW5kIERhcmsyIHBhbGV0dGVzIGZvciBtYXhpbXVtIGRpc3RpbmN0aXZlbmVzcwpDRUxMX0xJTkVfQ09MT1JTIDwtIGMoCiAgIkwxIiA9ICIjRTQxQTFDIiwgICAjIHJlZAogICJMMiIgPSAiIzM3N0VCOCIsICAgIyBibHVlCiAgIkwzIiA9ICIjNERBRjRBIiwgICAjIGdyZWVuCiAgIkw0IiA9ICIjRkY3RjAwIiwgICAjIG9yYW5nZQogICJMNSIgPSAiIzk4NEVBMyIsICAgIyBwdXJwbGUKICAiTDYiID0gIiNBNjU2MjgiLCAgICMgYnJvd24KICAiTDciID0gIiNGNzgxQkYiICAgICMgcGluawopCgpNRVRIT0RfQ09MT1JTIDwtIGMoCiAgIk1vbm9jbGUzIiAgID0gIiNlNzRjM2MiLAogICJDdXN0b20gTVNUIiA9ICIjMjk4MGI5IgopCgpTVEFURV9PUkRFUiAgICA8LSBjKCJDRDQgTmFpdmUiLCJDRDQgVENNIiwiQ0Q0IFRFTSIsIkNENCBUZW1yYS9DVEwiLCJUcmVnIikKQ0VMTF9MSU5FX09SREVSIDwtIHBhc3RlMCgiTCIsIDE6NykKVU1BUF9OQU1FICAgICAgPC0gInVtYXAiClNUQVRFX0NPTCAgICAgIDwtICJwcmVkaWN0ZWQuY2VsbHR5cGUubDIiCgpjYXQoIuKchSBTZXR1cCBjb21wbGV0ZVxuIikKY2F0KCJDZWxsIGxpbmUgY29sb3VyczpcbiIpOyBwcmludChDRUxMX0xJTkVfQ09MT1JTKQpgYGAKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBMb2FkIE9iamVjdHMgJiBTdWJzZXQgdG8gTWFsaWduYW50IENlbGxzCgpgYGB7ciBsb2FkLW9iamVjdHN9CiMg4pSA4pSAIEJJT0xPR0lDQUwgTk9URSDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKIyBXZSBsb2FkIHRoZSBwcmUtY29tcHV0ZWQgcmVmZXJlbmNlIG9iamVjdCAoY2Q0X3JlZl9kdWFsX3RyYWplY3RvcnkucmRzKQojIHdoaWNoIGFscmVhZHkgY29udGFpbnM6CiMgICAtIE1TVCBncmFwaCwgbWlsZXN0b25lIGNlbnRyb2lkcywgcHNldWRvdGltZSBmb3IgYWxsIHJlZmVyZW5jZSBjZWxscwojICAgLSBNb25vY2xlMyBwc2V1ZG90aW1lCiMgICAtIEFsbCB0cmFqZWN0b3J5IGluZnJhc3RydWN0dXJlIGJ1aWx0IGluIHRoZSBtYWluIHBpcGVsaW5lCiMKIyBDUklUSUNBTDogV2Ugc3Vic2V0IHRvIEwxLUw3IE9OTFksIGV4Y2x1ZGluZyBDRDRUX2xhYiBhbmQgQ0Q0VF8xMHguCiMgQ0Q0VF9sYWIgYW5kIENENFRfMTB4IGFyZSBoZWFsdGh5IGRvbm9yIENENCBUIGNlbGxzIGluY2x1ZGVkIGFzIGNvbnRyb2xzCiMgaW4gdGhlIG9yaWdpbmFsIGRhdGFzZXQuIFByb2plY3RpbmcgdGhlbSBvbnRvIHRoZSByZWZlcmVuY2Ugd291bGQgYmUKIyBjaXJjdWxhciAoaGVhbHRoeSBjZWxscyDihpIgaGVhbHRoeSByZWZlcmVuY2UgPSB0cml2aWFsbHkgcGVyZmVjdCBtYXBwaW5nKQojIGFuZCB3b3VsZCBOT1QgYW5zd2VyIHRoZSBjZWxsIG9mIG9yaWdpbiBxdWVzdGlvbi4KIwojIFdlIHdhbnQgT05MWSB0aGUgbWFsaWduYW50IFPDqXphcnkgY2VsbHMgKEwxLUw3KSBmb3IgcHJvamVjdGlvbi4KIyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKCiMgTG9hZCBwcmUtY29tcHV0ZWQgcmVmZXJlbmNlIHdpdGggYWxsIHRyYWplY3Rvcnkgb2JqZWN0cwpjZDRfcmVmIDwtIHJlYWRSRFMoIi4uL2NkNF9yZWZfZHVhbF90cmFqZWN0b3J5LnJkcyIpCgojIExvYWQgdGhlIGZ1bGwgU8OpemFyeSBvYmplY3QKc2V6YXJ5X3JhdyA8LSByZWFkUkRTKCIvaG9tZS9uYWJiYXNpL2Fwb2xsb19ob21lLzEtU2V1cmF0X1JEU19PQkpFQ1RfRklOQUwvQWxsX3NhbXBsZXNfTWVyZ2VkX3dpdGhfUmVuYW1lZF9DbHVzdGVyc19DZWxsX3N0YXRlLTAzLTEyLTIwMjUucmRzLnJkcyIpCgpjYXQoIkZ1bGwgU8OpemFyeSBvYmplY3QgbG9hZGVkOiIsIG5jb2woc2V6YXJ5X3JhdyksICJjZWxsc1xuIikKY2F0KCJcbm9yaWcuaWRlbnQgYnJlYWtkb3duIChBTEwgc2FtcGxlcyk6XG4iKQpwcmludCh0YWJsZShzZXphcnlfcmF3JG9yaWcuaWRlbnQpKQoKIyDilIDilIAgU3Vic2V0IHRvIG1hbGlnbmFudCBjZWxsIGxpbmVzIG9ubHkg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACkNFTExfTElORVMgPC0gcGFzdGUwKCJMIiwgMTo3KQpzZXphcnlfb2JqIDwtIHN1YnNldChzZXphcnlfcmF3LCBvcmlnLmlkZW50ICVpbiUgQ0VMTF9MSU5FUykKCmNhdChzcHJpbnRmKCJcbuKchSBNYWxpZ25hbnQgY2VsbHMgcmV0YWluZWQ6ICVkXG4iLCBuY29sKHNlemFyeV9vYmopKSkKY2F0KHNwcmludGYoIiAgIEhlYWx0aHkgY29udHJvbHMgZXhjbHVkZWQ6IENENFRfbGFiICsgQ0Q0VF8xMHggKCVkIGNlbGxzKVxuIiwKICAgICAgICAgICAgbmNvbChzZXphcnlfcmF3KSAtIG5jb2woc2V6YXJ5X29iaikpKQpjYXQoIlxuUGVyIGNlbGwgbGluZSAoY2VsbHMgdG8gcHJvamVjdCk6XG4iKQpwcmludCh0YWJsZShzZXphcnlfb2JqJG9yaWcuaWRlbnQpKQoKIyBDUklUSUNBTDogUmVtb3ZlIHRoZSBmdWxsIG9iamVjdCB0byBmcmVlIFJBTSBiZWZvcmUgTWFwUXVlcnkKIyBPbiBSU2VydmVyIHdpdGggc2hhcmVkIG1lbW9yeSwga2VlcGluZyBzZXphcnlfcmF3IGluIG1lbW9yeSBkdXJpbmcKIyBNYXBRdWVyeSBjYW4gY2F1c2UgdGhlIDQ2IEdpQiBmdXR1cmUuZ2xvYmFscyBsaW1pdCB0byBiZSBleGNlZWRlZApybShzZXphcnlfcmF3KTsgZ2MoKQpjYXQoIlxu4pyFIFJhdyBvYmplY3QgcmVtb3ZlZCBmcm9tIG1lbW9yeSDigJQgcmVhZHkgZm9yIE1hcFF1ZXJ5XG4iKQoKIyDilIDilIAgVmFsaWRhdGUgYm90aCBvYmplY3RzIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgApzdG9waWZub3QoCiAgIlNUQVRFX0NPTCBtaXNzaW5nIGZyb20gY2Q0X3JlZiIgICA9IFNUQVRFX0NPTCAlaW4lIGNvbG5hbWVzKGNkNF9yZWZAbWV0YS5kYXRhKSwKICAiVU1BUCBtaXNzaW5nIGZyb20gY2Q0X3JlZiIgICAgICAgID0gVU1BUF9OQU1FICVpbiUgbmFtZXMoY2Q0X3JlZkByZWR1Y3Rpb25zKSwKICAibWlsZXN0b25lIG1pc3NpbmcgZnJvbSBjZDRfcmVmIiAgID0gIm1pbGVzdG9uZSIgJWluJSBjb2xuYW1lcyhjZDRfcmVmQG1ldGEuZGF0YSksCiAgIm1vbm9jbGUzX3BzZXVkb3RpbWVfbm9ybSBtaXNzaW5nIiA9ICJtb25vY2xlM19wc2V1ZG90aW1lX25vcm0iICVpbiUgY29sbmFtZXMoY2Q0X3JlZkBtZXRhLmRhdGEpLAogICJSTkEgbWlzc2luZyBmcm9tIGNkNF9yZWYiICAgICAgICAgPSAiUk5BIiAlaW4lIG5hbWVzKGNkNF9yZWZAYXNzYXlzKSwKICAiUk5BIG1pc3NpbmcgZnJvbSBzZXphcnlfb2JqIiAgICAgID0gIlJOQSIgJWluJSBuYW1lcyhzZXphcnlfb2JqQGFzc2F5cyksCiAgIlVNQVAgbW9kZWwgbWlzc2luZyIgICAgICAgICAgICAgICA9ICFpcy5udWxsKGNkNF9yZWZAcmVkdWN0aW9ucyR1bWFwQG1pc2MkbW9kZWwpCikKY2F0KCLinIUgQWxsIHZhbGlkYXRpb24gY2hlY2tzIHBhc3NlZFxuIikKCiMgUHJpbnQgcmVmZXJlbmNlIHRyYWplY3Rvcnkgc3VtbWFyeSBmb3IgY29udGV4dApjYXQoIlxuUmVmZXJlbmNlIENENCBUIGNlbGwgc3RhdGUgZGlzdHJpYnV0aW9uOlxuIikKcHJpbnQodGFibGUoY2Q0X3JlZkBtZXRhLmRhdGFbW1NUQVRFX0NPTF1dKSkKY2F0KCJcblJlZmVyZW5jZSBtaWxlc3RvbmUgZGlzdHJpYnV0aW9uOlxuIikKcHJpbnQodGFibGUoY2Q0X3JlZkBtZXRhLmRhdGEkbWlsZXN0b25lKSkKYGBgCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgUmVjb25zdHJ1Y3QgVHJhamVjdG9yeSBJbmZyYXN0cnVjdHVyZQoKYGBge3IgcmVjb25zdHJ1Y3QtdHJhamVjdG9yeX0KIyDilIDilIAgU0FGRSBBUFBST0FDSDogZGVyaXZlIGV2ZXJ5dGhpbmcgZnJvbSB0aGUgc2F2ZWQgcmVmZXJlbmNlIG9iamVjdCDilIDilIDilIDilIDilIDilIDilIDilIAKIyBXZSBkbyBOT1QgaGFyZGNvZGUgZWRnZXMg4oCUIGluc3RlYWQgd2UgcmVjb25zdHJ1Y3QgdGhlIGdyYXBoIGZyb20gdGhlCiMgYWN0dWFsIG1pbGVzdG9uZSBjZW50cm9pZCBwb3NpdGlvbnMgaW4gdGhlIHNhdmVkIGNkNF9yZWYgb2JqZWN0LgojIFRoaXMgZ3VhcmFudGVlcyBleGFjdCBjb25zaXN0ZW5jeSB3aXRoIHRoZSByZWZlcmVuY2UgdHJhamVjdG9yeS4KCiMg4pSA4pSAIFN0ZXAgMTogUmVidWlsZCBtaWxlc3RvbmUgY2VudHJvaWRzIGZyb20gcmVmZXJlbmNlIG1ldGFkYXRhIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgAp1bWFwX2Nvb3JkcyA8LSBhcy5kYXRhLmZyYW1lKEVtYmVkZGluZ3MoY2Q0X3JlZiwgVU1BUF9OQU1FKSkKY29sbmFtZXModW1hcF9jb29yZHMpIDwtIGMoIlVNQVBfMSIsIlVNQVBfMiIpCnVtYXBfY29vcmRzJG1pbGVzdG9uZSA8LSBjZDRfcmVmQG1ldGEuZGF0YSRtaWxlc3RvbmUKdW1hcF9jb29yZHMkc3RhdGUgICAgIDwtIGNkNF9yZWZAbWV0YS5kYXRhW1tTVEFURV9DT0xdXQoKbWlsZXN0b25lX2NlbnRyb2lkcyA8LSB1bWFwX2Nvb3JkcyAlPiUKICBmaWx0ZXIoIWlzLm5hKG1pbGVzdG9uZSkpICU+JQogIGdyb3VwX2J5KG1pbGVzdG9uZSkgJT4lCiAgc3VtbWFyaXNlKAogICAgVU1BUF8xICA9IG1lYW4oVU1BUF8xKSwKICAgIFVNQVBfMiAgPSBtZWFuKFVNQVBfMiksCiAgICBuX2NlbGxzID0gbigpLAogICAgc3RhdGUgICA9IGRwbHlyOjpmaXJzdChzdGF0ZSksCiAgICAuZ3JvdXBzID0gImRyb3AiCiAgKSAlPiUKICBhcnJhbmdlKG1pbGVzdG9uZSkKCm1pbGVzdG9uZV9vcmRlciAgICAgICAgICAgPC0gc29ydCh1bmlxdWUobWlsZXN0b25lX2NlbnRyb2lkcyRtaWxlc3RvbmUpKQptaWxlc3RvbmVfY2VudHJvaWRzJG9yZGVyX3BvcyA8LSBtYXRjaChtaWxlc3RvbmVfY2VudHJvaWRzJG1pbGVzdG9uZSwgbWlsZXN0b25lX29yZGVyKQoKY2F0KCJNaWxlc3RvbmUgY2VudHJvaWRzIGZyb20gc2F2ZWQgcmVmZXJlbmNlOlxuIikKcHJpbnQobWlsZXN0b25lX2NlbnRyb2lkc1ssIGMoIm1pbGVzdG9uZSIsInN0YXRlIiwiVU1BUF8xIiwiVU1BUF8yIiwibl9jZWxscyIpXSkKCiMg4pSA4pSAIFN0ZXAgMjogVmVyaWZ5IG1pbGVzdG9uZS10by1zdGF0ZSBtYXBwaW5nIG1hdGNoZXMgZXhwZWN0ZWQgYmlvbG9neSDilIDilIDilIDilIDilIDilIAKZXhwZWN0ZWRfbWFwIDwtIGMoCiAgTTAwPSJDRDQgTmFpdmUiLAogIE0wMT0iQ0Q0IFRDTSIsIE0wMj0iQ0Q0IFRDTSIsCiAgTTAzPSJDRDQgVEVNIiwgTTA0PSJDRDQgVEVNIiwKICBNMDU9IkNENCBUZW1yYS9DVEwiLAogIE0wNj0iVHJlZyIsICAgIE0wNz0iVHJlZyIKKQoKYWN0dWFsX21hcCA8LSBzZXROYW1lcyhtaWxlc3RvbmVfY2VudHJvaWRzJHN0YXRlLCBtaWxlc3RvbmVfY2VudHJvaWRzJG1pbGVzdG9uZSkKbWlzbWF0Y2hlcyA8LSBuYW1lcyhleHBlY3RlZF9tYXApW2V4cGVjdGVkX21hcCAhPSBhY3R1YWxfbWFwW25hbWVzKGV4cGVjdGVkX21hcCldXQoKaWYgKGxlbmd0aChtaXNtYXRjaGVzKSA+IDApIHsKICBjYXQoIuKaoO+4jyAgV0FSTklORyDigJQgbWlsZXN0b25lL3N0YXRlIG1pc21hdGNoIGF0OiIsIHBhc3RlKG1pc21hdGNoZXMsIGNvbGxhcHNlPSIsICIpLCAiXG4iKQogIGNhdCgiICAgRXhwZWN0ZWQ6IiwgZXhwZWN0ZWRfbWFwW21pc21hdGNoZXNdLCAiXG4iKQogIGNhdCgiICAgQWN0dWFsOiAgIiwgYWN0dWFsX21hcFttaXNtYXRjaGVzXSwgIlxuIikKICBzdG9wKCJGaXggbWlsZXN0b25lIGFzc2lnbm1lbnQgYmVmb3JlIHByb2NlZWRpbmciKQp9IGVsc2UgewogIGNhdCgi4pyFIE1pbGVzdG9uZS10by1zdGF0ZSBtYXBwaW5nIHZlcmlmaWVkXG4iKQp9CgojIOKUgOKUgCBTdGVwIDM6IEJ1aWxkIGNlbnRyb2lkIG1hdHJpeCBhbmQgZGlzdGFuY2UgbWF0cml4IOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgApjZW50cm9pZF9tYXQgPC0gYXMubWF0cml4KG1pbGVzdG9uZV9jZW50cm9pZHNbLCBjKCJVTUFQXzEiLCJVTUFQXzIiKV0pCnJvd25hbWVzKGNlbnRyb2lkX21hdCkgPC0gbWlsZXN0b25lX2NlbnRyb2lkcyRtaWxlc3RvbmUKZGlzdF9tYXQgPC0gYXMubWF0cml4KGRpc3QoY2VudHJvaWRfbWF0LCBtZXRob2Q9ImV1Y2xpZGVhbiIpKQoKIyDilIDilIAgU3RlcCA0OiBSZWJ1aWxkIE1TVCB3aXRoIFNBTUUgbWFudWFsIHRvcG9sb2d5IGFzIG1haW4gcGlwZWxpbmUg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACiMgRWRnZSBsaXN0IGRlcml2ZWQgZnJvbSB5b3VyIGNvbmZpcm1lZCBtaWxlc3RvbmUgZGlzdHJpYnV0aW9uOgojIE0wMD1OYWl2ZSgyMDM3KSBNMDE9VENNX2Vhcmx5KDM4OCkgTTAyPVRDTV9sYXRlL2JyYW5jaCg4Njc5KQojIE0wMz1URU1fZWFybHkoNjQpIE0wND1URU1fbGF0ZSg4MSkgTTA1PVRlbXJhKDEwKQojIE0wNj1UcmVnX25haXZlKDE0NikgTTA3PVRyZWdfdGVybWluYWwoNjEpCmVkZ2VfbGlzdF9vcmRlcmVkIDwtIG1hdHJpeChjKAogICJNMDAiLCJNMDEiLCAgICMgTmFpdmUg4oaSIFRDTSBlYXJseQogICJNMDEiLCJNMDIiLCAgICMgVENNIGVhcmx5IOKGkiBUQ00gbGF0ZSAoYnJhbmNoIHBvaW50IOKAlCBsYXJnZXN0IFRDTSBjbHVzdGVyKQogICJNMDIiLCJNMDMiLCAgICMgVENNIGxhdGUg4oaSIFRFTSBlYXJseSAgKGVmZmVjdG9yIGFybSkKICAiTTAzIiwiTTA0IiwgICAjIFRFTSBlYXJseSDihpIgVEVNIGxhdGUKICAiTTA0IiwiTTA1IiwgICAjIFRFTSBsYXRlIOKGkiBUZW1yYS9DVEwgICh0ZXJtaW5hbCBlZmZlY3RvcikKICAiTTAyIiwiTTA2IiwgICAjIFRDTSBsYXRlIOKGkiBUcmVnIG5haXZlIChyZWd1bGF0b3J5IGFybSkKICAiTTA2IiwiTTA3IiAgICAjIFRyZWcgbmFpdmUg4oaSIFRyZWcgdGVybWluYWwKKSwgbmNvbD0yLCBieXJvdz1UUlVFKQoKZWRnZV93ZWlnaHRzIDwtIGFwcGx5KGVkZ2VfbGlzdF9vcmRlcmVkLCAxLCBmdW5jdGlvbihlKSBkaXN0X21hdFtlWzFdLCBlWzJdXSkKCmlmIChhbnkoIWlzLmZpbml0ZShlZGdlX3dlaWdodHMpKSkgewogIHN0b3AoIk5vbi1maW5pdGUgZWRnZSB3ZWlnaHRzIOKAlCBjaGVjayBtaWxlc3RvbmUgbmFtZXMgbWF0Y2ggZGlzdF9tYXQgcm93bmFtZXMiKQp9Cgptc3RfZ3JhcGggPC0gZ3JhcGhfZnJvbV9lZGdlbGlzdChlZGdlX2xpc3Rfb3JkZXJlZCwgZGlyZWN0ZWQ9RkFMU0UpCkUobXN0X2dyYXBoKSR3ZWlnaHQgPC0gZWRnZV93ZWlnaHRzCgpjYXQoIlxuTVNUIGVkZ2VzIHJlY29uc3RydWN0ZWQ6XG4iKQpmb3IgKGkgaW4gc2VxX2xlbihucm93KGVkZ2VfbGlzdF9vcmRlcmVkKSkpIHsKICBzMSA8LSBhY3R1YWxfbWFwW2VkZ2VfbGlzdF9vcmRlcmVkW2ksMV1dCiAgczIgPC0gYWN0dWFsX21hcFtlZGdlX2xpc3Rfb3JkZXJlZFtpLDJdXQogIGNhdChzcHJpbnRmKCIgICVzKCVzKSDihpIgJXMoJXMpICBkaXN0PSUuNGZcbiIsCiAgICAgICAgICAgICAgZWRnZV9saXN0X29yZGVyZWRbaSwxXSwgczEsCiAgICAgICAgICAgICAgZWRnZV9saXN0X29yZGVyZWRbaSwyXSwgczIsCiAgICAgICAgICAgICAgZWRnZV93ZWlnaHRzW2ldKSkKfQoKIyDilIDilIAgU3RlcCA1OiBHcmFwaCBkaXN0YW5jZXMgZnJvbSByb290IOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgApncmFwaF9kaXN0cyAgPC0gZGlzdGFuY2VzKG1zdF9ncmFwaCwgdj0iTTAwIiwgd2VpZ2h0cz1OQSkKZ19kaXN0c192ZWMgIDwtIHNldE5hbWVzKGFzLm51bWVyaWMoZ3JhcGhfZGlzdHMpLCBjb2xuYW1lcyhncmFwaF9kaXN0cykpCgojIENSSVRJQ0FMOiB1c2Ugc2FtZSBwc2V1ZG90aW1lIHJhbmdlIGFzIHJlZmVyZW5jZSBmb3IgY29uc2lzdGVudCAwLTEwMCBzY2FsZQptc3RfcHRfcmFuZ2UgPC0gcmFuZ2UoY2Q0X3JlZkBtZXRhLmRhdGEkbXN0X3BzZXVkb3RpbWVfbm9ybSwgbmEucm09VFJVRSkKY2F0KHNwcmludGYoIlxuUmVmZXJlbmNlIHBzZXVkb3RpbWUgcmFuZ2U6ICUuMWYg4oCTICUuMWZcbiIsCiAgICAgICAgICAgIG1zdF9wdF9yYW5nZVsxXSwgbXN0X3B0X3JhbmdlWzJdKSkKCiMg4pSA4pSAIFN0ZXAgNjogTVNUIGVkZ2UgY29vcmRpbmF0ZXMgZm9yIHBsb3R0aW5nIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgAptc3RfZWRnZXMgICA8LSBhc19lZGdlbGlzdChtc3RfZ3JhcGgpCm1zdF9lZGdlX2RmIDwtIGRvLmNhbGwocmJpbmQsIGxhcHBseShzZXFfbGVuKG5yb3cobXN0X2VkZ2VzKSksIGZ1bmN0aW9uKGkpIHsKICBtMSA8LSBtc3RfZWRnZXNbaSwxXTsgbTIgPC0gbXN0X2VkZ2VzW2ksMl0KICByMSA8LSBtaWxlc3RvbmVfY2VudHJvaWRzW21pbGVzdG9uZV9jZW50cm9pZHMkbWlsZXN0b25lPT1tMSxdCiAgcjIgPC0gbWlsZXN0b25lX2NlbnRyb2lkc1ttaWxlc3RvbmVfY2VudHJvaWRzJG1pbGVzdG9uZT09bTIsXQogIGRhdGEuZnJhbWUoeDE9cjEkVU1BUF8xLCB5MT1yMSRVTUFQXzIsIHgyPXIyJFVNQVBfMSwgeTI9cjIkVU1BUF8yKQp9KSkKCiMg4pSA4pSAIFN0ZXAgNzogUmVmZXJlbmNlIFVNQVAgYmFja2dyb3VuZCDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKcmVmX2JnIDwtIGRhdGEuZnJhbWUoCiAgVU1BUF8xID0gRW1iZWRkaW5ncyhjZDRfcmVmLCBVTUFQX05BTUUpWywxXSwKICBVTUFQXzIgPSBFbWJlZGRpbmdzKGNkNF9yZWYsIFVNQVBfTkFNRSlbLDJdLAogIHN0YXRlICA9IGNkNF9yZWZAbWV0YS5kYXRhW1tTVEFURV9DT0xdXQopCgojIOKUgOKUgCBTdGVwIDg6IHByb2plY3Rfb250b19tc3QgZnVuY3Rpb24g4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACnByb2plY3Rfb250b19tc3QgPC0gZnVuY3Rpb24oY2VsbF9jb29yZHMsIGNlbnRyb2lkX21hdCwgbXN0X2dyYXBoLCBncmFwaF9kaXN0cykgewogIGVkZ2VzICAgICAgPC0gYXNfZWRnZWxpc3QobXN0X2dyYXBoKQogIG5fY2VsbHMgICAgPC0gbnJvdyhjZWxsX2Nvb3JkcykKICBwc2V1ZG90aW1lIDwtIG51bWVyaWMobl9jZWxscykKICBuZWFyZXN0X21zIDwtIGNoYXJhY3RlcihuX2NlbGxzKQoKICBmb3IgKGkgaW4gc2VxX2xlbihuX2NlbGxzKSkgewogICAgY3ggPC0gY2VsbF9jb29yZHNbaSwxXTsgY3kgPC0gY2VsbF9jb29yZHNbaSwyXQogICAgYmVzdF9kaXN0IDwtIEluZjsgYmVzdF9wdCA8LSAwOyBiZXN0X21zIDwtIE5BX2NoYXJhY3Rlcl8KCiAgICBmb3IgKGUgaW4gc2VxX2xlbihucm93KGVkZ2VzKSkpIHsKICAgICAgbTEgPC0gZWRnZXNbZSwxXTsgbTIgPC0gZWRnZXNbZSwyXQogICAgICBwMSA8LSBjZW50cm9pZF9tYXRbbTEsXTsgcDIgPC0gY2VudHJvaWRfbWF0W20yLF0KICAgICAgZHggPC0gcDJbMV0tcDFbMV07IGR5IDwtIHAyWzJdLXAxWzJdCiAgICAgIGxlbjIgPC0gZHheMiArIGR5XjIKICAgICAgaWYgKGxlbjIgPCAxZS0xMCkgbmV4dAogICAgICB0ICAgIDwtIG1heCgwLCBtaW4oMSwgKChjeC1wMVsxXSkqZHggKyAoY3ktcDFbMl0pKmR5KSAvIGxlbjIpKQogICAgICBwcm9qIDwtIGMocDFbMV0rdCpkeCwgcDFbMl0rdCpkeSkKICAgICAgZCAgICA8LSBzcXJ0KChjeC1wcm9qWzFdKV4yICsgKGN5LXByb2pbMl0pXjIpCiAgICAgIGlmIChkIDwgYmVzdF9kaXN0KSB7CiAgICAgICAgYmVzdF9kaXN0IDwtIGQKICAgICAgICBiZXN0X3B0ICAgPC0gZ3JhcGhfZGlzdHNbbTFdICsgdCooZ3JhcGhfZGlzdHNbbTJdLWdyYXBoX2Rpc3RzW20xXSkKICAgICAgICBiZXN0X21zICAgPC0gaWYgKHQgPCAwLjUpIG0xIGVsc2UgbTIKICAgICAgfQogICAgfQogICAgcHNldWRvdGltZVtpXSA8LSBiZXN0X3B0CiAgICBuZWFyZXN0X21zW2ldIDwtIGJlc3RfbXMKICB9CiAgbGlzdChwc2V1ZG90aW1lPXBzZXVkb3RpbWUsIG5lYXJlc3RfbXM9bmVhcmVzdF9tcykKfQoKY2F0KCJcbuKchSBUcmFqZWN0b3J5IGluZnJhc3RydWN0dXJlIHJlYWR5XG4iKQpjYXQoc3ByaW50ZigiTVNUOiAlZCBub2RlcywgJWQgZWRnZXMgfCBSb290OiBNMDAgKENENCBOYWl2ZSlcbiIsCiAgICAgICAgICAgIHZjb3VudChtc3RfZ3JhcGgpLCBlY291bnQobXN0X2dyYXBoKSkpCmBgYAoKVGhlIGtleSBkaWZmZXJlbmNlcyBmcm9tIHdoYXQgSSBoYWQgYmVmb3JlIGFyZSB0aGF0IHRoZSBtaWxlc3RvbmUgdmVyaWZpY2F0aW9uIHN0ZXAgY2hlY2tzIHRoZSBhY3R1YWwgc3RhdGUgYXNzaWdubWVudHMgZnJvbSB5b3VyIG9iamVjdCBhZ2FpbnN0IGV4cGVjdGVkIGJpb2xvZ3kgYW5kIHN0b3BzIHdpdGggYSBjbGVhciBlcnJvciBtZXNzYWdlIGlmIGFueXRoaW5nIGlzIHdyb25nLCB0aGUgY29tbWVudCBvbiBNMDIgbm93IGNvcnJlY3RseSBub3RlcyBpdCBpcyB0aGUgKipsYXJnZSoqIFRDTSBjbHVzdGVyICg4LDY3OSBjZWxscykgdGhhdCBhY3RzIGFzIHRoZSBicmFuY2ggcG9pbnQsIGFuZCB0aGUgYG1zdF9wdF9yYW5nZWAgaXMgdGFrZW4gZGlyZWN0bHkgZnJvbSB0aGUgc2F2ZWQgcmVmZXJlbmNlIHBzZXVkb3RpbWUgcmF0aGVyIHRoYW4gYmVpbmcgcmVjYWxjdWxhdGVkLCBndWFyYW50ZWVpbmcgdGhlIDDigJMxMDAgc2NhbGUgZm9yIFPDqXphcnkgY2VsbHMgaXMgaWRlbnRpY2FsIHRvIHRoZSByZWZlcmVuY2Ugc2NhbGUuIEV2ZXJ5dGhpbmcgZG93bnN0cmVhbSBpbiB0aGUgc2NyaXB0IHN0YXlzIHVuY2hhbmdlZC4KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBNYXBRdWVyeTogUHJvamVjdCBTw6l6YXJ5IENlbGwgTGluZXMgb250byBSZWZlcmVuY2UKCmBgYHtyIHNlemFyeS1tYXBxdWVyeX0KIyDilIDilIAgQklPTE9HSUNBTCBOT1RFIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgAojIE1hcFF1ZXJ5IGlzIGEgU2V1cmF0IGZ1bmN0aW9uIHRoYXQgcGVyZm9ybXMgcmVmZXJlbmNlLWJhc2VkIGNlbGwgbWFwcGluZy4KIyBJdCBkb2VzIHR3byB0aGluZ3Mgc2ltdWx0YW5lb3VzbHk6CiMKIyAxLiBMQUJFTCBUUkFOU0ZFUjogVXNlcyB3ZWlnaHRlZCBuZWFyZXN0LW5laWdoYm91ciBhbmNob3JzIChjZWxscyB0aGF0CiMgICAgYXJlIG11dHVhbGx5IHNpbWlsYXIgYmV0d2VlbiByZWZlcmVuY2UgYW5kIHF1ZXJ5IGluIFBDQSBzcGFjZSkgdG8KIyAgICB0cmFuc2ZlciBjZWxsIHR5cGUgbGFiZWxzIGZyb20gcmVmZXJlbmNlIOKGkiBxdWVyeS4gRWFjaCBTw6l6YXJ5IGNlbGwKIyAgICBnZXRzIGEgcHJlZGljdGVkIENENCBzdGF0ZSBsYWJlbCAoTmFpdmUvVENNL1RFTS9UZW1yYS9UcmVnKSB3aXRoIGEKIyAgICBjb25maWRlbmNlIHNjb3JlICgwLTEpLgojCiMgMi4gVU1BUCBQUk9KRUNUSU9OOiBQcm9qZWN0cyBTw6l6YXJ5IGNlbGxzIGludG8gdGhlIHJlZmVyZW5jZSBVTUFQCiMgICAgY29vcmRpbmF0ZSBzeXN0ZW0uIFRoaXMgaXMgTk9UIGEgbmV3IFVNQVAg4oCUIGl0IHBsYWNlcyBlYWNoIFPDqXphcnkKIyAgICBjZWxsIGF0IHRoZSBjb29yZGluYXRlcyBpdCB3b3VsZCBvY2N1cHkgSUYgaXQgd2VyZSBwYXJ0IG9mIHRoZQojICAgIHJlZmVyZW5jZSBkYXRhc2V0LiBUaGlzIGFsbG93cyBkaXJlY3QgdmlzdWFsIGNvbXBhcmlzb24gb24gdGhlCiMgICAgc2FtZSBVTUFQIHNwYWNlLgojCiMgV0hZIFRISVMgTUFUVEVSUyBCSU9MT0dJQ0FMTFk6CiMgVGhlIHByZWRpY3RlZCBzdGF0ZSB0ZWxscyB1cyB3aGljaCBub3JtYWwgQ0Q0IHN1YnR5cGUgdGhlIG1hbGlnbmFudCBjZWxsCiMgbW9zdCB0cmFuc2NyaXB0b21pY2FsbHkgcmVzZW1ibGVzLiBJZiBhIFPDqXphcnkgY2VsbCBsaW5lIHByb2plY3RzIHRvCiMgQ0Q0IFRDTSBzcGFjZSB3aXRoIGhpZ2ggY29uZmlkZW5jZSwgaXQgbWVhbnMgdGhlIG1hbGlnbmFudCB0cmFuc2NyaXB0b21lCiMgbW9zdCBjbG9zZWx5IG1hdGNoZXMgY2VudHJhbCBtZW1vcnkgVCBjZWxscyDigJQgaW1wbHlpbmcgVENNIGlzIHRoZSBjZWxsCiMgb2Ygb3JpZ2luIG9yIHRoYXQgdGhlIG1hbGlnbmFudCB0cmFuc2Zvcm1hdGlvbiBhcnJlc3RlZCBkaWZmZXJlbnRpYXRpb24KIyBhdCB0aGUgVENNIHN0YWdlLgojCiMgVEVDSE5JQ0FMOiBXZSB1c2UgcGxhbigic2VxdWVudGlhbCIpIHRvIGRpc2FibGUgcGFyYWxsZWxpc20gYmVjYXVzZSB0aGUKIyByZWZlcmVuY2Ugb2JqZWN0IGlzIH40NiBHaUIgd2hlbiBleHBvcnRlZCB0byBwYXJhbGxlbCB3b3JrZXJzLCBleGNlZWRpbmcKIyBSU2VydmVyIG1lbW9yeSBsaW1pdHMuIFNlcXVlbnRpYWwgbW9kZSBpcyBzbG93ZXIgYnV0IG1lbW9yeS1zYWZlLgojIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgAoKcGxhbigic2VxdWVudGlhbCIpCm9wdGlvbnMoZnV0dXJlLmdsb2JhbHMubWF4U2l6ZSA9IDYwICogMTAyNF4zKQoKRGVmYXVsdEFzc2F5KGNkNF9yZWYpICAgIDwtICJSTkEiCkRlZmF1bHRBc3NheShzZXphcnlfb2JqKSA8LSAiUk5BIgoKIyBGaW5kVmFyaWFibGVGZWF0dXJlcyBvbiByZWZlcmVuY2UgZW5zdXJlcyBnZW5lIHNlbGVjdGlvbiBpcyBjdXJyZW50CmNkNF9yZWYgPC0gRmluZFZhcmlhYmxlRmVhdHVyZXMoY2Q0X3JlZiwgbmZlYXR1cmVzPTMwMDAsIHZlcmJvc2U9RkFMU0UpCgpjYXQoIkZpbmRpbmcgdHJhbnNmZXIgYW5jaG9ycy4uLlxuIikKY2F0KCIoVGhpcyBpZGVudGlmaWVzIGNlbGxzIHRoYXQgYXJlIG11dHVhbGx5IHNpbWlsYXIgYmV0d2VlblxuIikKY2F0KCIgcmVmZXJlbmNlIGFuZCBxdWVyeSBpbiBQQ0Egc3BhY2Ug4oCUIHRoZSBicmlkZ2UgZm9yIGxhYmVsIHRyYW5zZmVyKVxuXG4iKQoKYW5jaG9ycyA8LSBGaW5kVHJhbnNmZXJBbmNob3JzKAogIHJlZmVyZW5jZSAgICAgICAgICAgID0gY2Q0X3JlZiwKICBxdWVyeSAgICAgICAgICAgICAgICA9IHNlemFyeV9vYmosCiAgbm9ybWFsaXphdGlvbi5tZXRob2QgPSAiTG9nTm9ybWFsaXplIiwKICByZWZlcmVuY2UucmVkdWN0aW9uICA9ICJwY2EiLAogIGRpbXMgICAgICAgICAgICAgICAgID0gMTozMCwKICByZWR1Y3Rpb24gICAgICAgICAgICA9ICJwY2Fwcm9qZWN0IiwKICB2ZXJib3NlICAgICAgICAgICAgICA9IEZBTFNFCikKCmNhdChzcHJpbnRmKCLinIUgQW5jaG9ycyBmb3VuZDogJWQgYW5jaG9yIHBhaXJzXG4iLCBucm93KGFuY2hvcnNAYW5jaG9ycykpKQpjYXQoIlxuUnVubmluZyBNYXBRdWVyeSAobGFiZWwgdHJhbnNmZXIgKyBVTUFQIHByb2plY3Rpb24pLi4uXG4iKQoKc2V6YXJ5X29iaiA8LSBNYXBRdWVyeSgKICBhbmNob3JzZXQgICAgICAgICAgID0gYW5jaG9ycywKICBxdWVyeSAgICAgICAgICAgICAgID0gc2V6YXJ5X29iaiwKICByZWZlcmVuY2UgICAgICAgICAgID0gY2Q0X3JlZiwKICByZWZkYXRhICAgICAgICAgICAgID0gbGlzdCgKICAgIHByZWRpY3RlZF9zdGF0ZSAgICAgPSBTVEFURV9DT0wsICAgICMgdHJhbnNmZXIgQ0Q0IHN0YXRlIGxhYmVscwogICAgcHJlZGljdGVkX21pbGVzdG9uZSA9ICJtaWxlc3RvbmUiICAgIyB0cmFuc2ZlciBtaWxlc3RvbmUgbGFiZWxzCiAgKSwKICByZWZlcmVuY2UucmVkdWN0aW9uID0gInBjYSIsCiAgcmVkdWN0aW9uLm1vZGVsICAgICA9ICJ1bWFwIiwgICAgICAgICAjIHByb2plY3QgaW50byByZWZlcmVuY2UgVU1BUCBzcGFjZQogIHZlcmJvc2UgICAgICAgICAgICAgPSBGQUxTRQopCgpjYXQoIuKchSBNYXBRdWVyeSBjb21wbGV0ZVxuXG4iKQoKIyDilIDilIAgUXVhbGl0eSBjaGVjayDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKY2F0KCI9PT0gUFJPSkVDVElPTiBRVUFMSVRZIENIRUNLID09PVxuIikKY2F0KHNwcmludGYoIkNlbGxzIHByb2plY3RlZDogJWRcbiIsIG5jb2woc2V6YXJ5X29iaikpKQpjYXQoc3ByaW50ZigiTWVhbiBwcmVkaWN0aW9uIHNjb3JlOiAlLjNmICg+MC41ID0gcmVsaWFibGUsID4wLjcgPSBjb25maWRlbnQpXG4iLAogICAgICAgICAgICBtZWFuKHNlemFyeV9vYmokcHJlZGljdGVkLnByZWRpY3RlZF9zdGF0ZS5zY29yZSwgbmEucm09VFJVRSkpKQpjYXQoc3ByaW50ZigiQ2VsbHMgd2l0aCBzY29yZSA+MC41OiAlZCAoJS4xZiUlKVxuIiwKICAgICAgICAgICAgc3VtKHNlemFyeV9vYmokcHJlZGljdGVkLnByZWRpY3RlZF9zdGF0ZS5zY29yZSA+IDAuNSwgbmEucm09VFJVRSksCiAgICAgICAgICAgIDEwMCptZWFuKHNlemFyeV9vYmokcHJlZGljdGVkLnByZWRpY3RlZF9zdGF0ZS5zY29yZSA+IDAuNSwgbmEucm09VFJVRSkpKQpjYXQoc3ByaW50ZigiQ2VsbHMgd2l0aCBzY29yZSA+MC43OiAlZCAoJS4xZiUlKVxuIiwKICAgICAgICAgICAgc3VtKHNlemFyeV9vYmokcHJlZGljdGVkLnByZWRpY3RlZF9zdGF0ZS5zY29yZSA+IDAuNywgbmEucm09VFJVRSksCiAgICAgICAgICAgIDEwMCptZWFuKHNlemFyeV9vYmokcHJlZGljdGVkLnByZWRpY3RlZF9zdGF0ZS5zY29yZSA+IDAuNywgbmEucm09VFJVRSkpKQoKY2F0KCJcbj09PSBQUkVESUNURUQgU1RBVEUgQlkgQ0VMTCBMSU5FID09PVxuIikKcHJpbnQodGFibGUoc2V6YXJ5X29iaiRwcmVkaWN0ZWQucHJlZGljdGVkX3N0YXRlLAogICAgICAgICAgICBzZXphcnlfb2JqJG9yaWcuaWRlbnQsCiAgICAgICAgICAgIGRubj1jKCJQcmVkaWN0ZWQgc3RhdGUiLCJDZWxsIGxpbmUiKSkpCgpjYXQoIlxuPT09IFBSRURJQ1RFRCBNSUxFU1RPTkUgQlkgQ0VMTCBMSU5FID09PVxuIikKcHJpbnQodGFibGUoc2V6YXJ5X29iaiRwcmVkaWN0ZWQucHJlZGljdGVkX21pbGVzdG9uZSwKICAgICAgICAgICAgc2V6YXJ5X29iaiRvcmlnLmlkZW50LAogICAgICAgICAgICBkbm49YygiUHJlZGljdGVkIG1pbGVzdG9uZSIsIkNlbGwgbGluZSIpKSkKYGBgCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgQXNzaWduIFBzZXVkb3RpbWUgdG8gU8OpemFyeSBDZWxsIExpbmVzCgpgYGB7ciBzZXphcnktcHNldWRvdGltZX0KIyDilIDilIAgQklPTE9HSUNBTCBOT1RFIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgAojIFdlIGFzc2lnbiBwc2V1ZG90aW1lIHRvIFPDqXphcnkgY2VsbHMgdXNpbmcgVFdPIG1ldGhvZHM6CiMKIyBNRVRIT0QgMSDigJQgTVNUIHByb2plY3Rpb246CiMgRWFjaCBTw6l6YXJ5IGNlbGwncyByZWYudW1hcCBjb29yZGluYXRlcyAoZnJvbSBNYXBRdWVyeSkgYXJlIHByb2plY3RlZAojIG9udG8gdGhlIG5lYXJlc3QgZWRnZSBvZiB0aGUgTVNULiBQc2V1ZG90aW1lID0gaW50ZXJwb2xhdGVkIGdyYXBoIGRpc3RhbmNlCiMgZnJvbSB0aGUgcm9vdCAoTTAwID0gQ0Q0IE5haXZlKS4gVGhpcyBnaXZlcyBhIGJpb2xvZ2ljYWxseSBpbnRlcnByZXRhYmxlCiMgdmFsdWU6IDAgPSBtb3N0IG5haXZlLWxpa2UsIDEwMCA9IG1vc3QgdGVybWluYWxseSBkaWZmZXJlbnRpYXRlZC4KIwojIE1FVEhPRCAyIOKAlCBLTk4td2VpZ2h0ZWQgTW9ub2NsZTMgdHJhbnNmZXI6CiMgRm9yIGVhY2ggU8OpemFyeSBjZWxsLCB3ZSBmaW5kIGl0cyAxNSBuZWFyZXN0IHJlZmVyZW5jZSBjZWxscyBpbiBVTUFQIHNwYWNlLAojIHRoZW4gY29tcHV0ZSBhIHdlaWdodGVkIGF2ZXJhZ2Ugb2YgdGhlaXIgTW9ub2NsZTMgcHNldWRvdGltZSB2YWx1ZXMuCiMgV2VpZ2h0cyBhcmUgaW52ZXJzZSBkaXN0YW5jZSAoY2xvc2VyIHJlZmVyZW5jZSBjZWxscyBjb250cmlidXRlIG1vcmUpLgojIFRoaXMgdHJhbnNmZXJzIHRoZSBNb25vY2xlMyBwc2V1ZG90aW1lIHNjYWxlIHdpdGhvdXQgcmUtcnVubmluZyBNb25vY2xlMwojIG9uIHRoZSBxdWVyeSDigJQgYXBwcm9wcmlhdGUgYmVjYXVzZSBNb25vY2xlMyBjYW5ub3QgZWFzaWx5IGluY29ycG9yYXRlCiMgbmV3IHF1ZXJ5IGNlbGxzIGludG8gYW4gZXhpc3RpbmcgcHJpbmNpcGFsIGdyYXBoLgojCiMgSU5URVJQUkVUQVRJT046CiMgLSBIaWdoIHBzZXVkb3RpbWUgKD41MCk6IG1hbGlnbmFudCBjZWxscyByZXNlbWJsZSBtYXR1cmUvZGlmZmVyZW50aWF0ZWQgVCBjZWxscwojIC0gTG93IHBzZXVkb3RpbWUgKDwyNSk6IG1hbGlnbmFudCBjZWxscyByZXNlbWJsZSBuYWl2ZSBvciBlYXJseSBtZW1vcnkgVCBjZWxscwojIC0gVGhpcyB0ZWxscyB1cyB0aGUgRElGRkVSRU5USUFUSU9OIFNUQUdFIGF0IHdoaWNoIG1hbGlnbmFudCB0cmFuc2Zvcm1hdGlvbiBvY2N1cnJlZAojIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgAoKc3RvcGlmbm90KCJyZWYudW1hcCBtaXNzaW5nIOKAlCBNYXBRdWVyeSBtYXkgaGF2ZSBmYWlsZWQiID0KICAgICAgICAgICJyZWYudW1hcCIgJWluJSBuYW1lcyhzZXphcnlfb2JqQHJlZHVjdGlvbnMpKQoKIyBFeHRyYWN0IFPDqXphcnkgVU1BUCBjb29yZGluYXRlcyBpbiByZWZlcmVuY2Ugc3BhY2UKc2V6X3VtYXBfY29vcmRzICAgICAgICAgICA8LSBhcy5tYXRyaXgoRW1iZWRkaW5ncyhzZXphcnlfb2JqLCAicmVmLnVtYXAiKSkKY29sbmFtZXMoc2V6X3VtYXBfY29vcmRzKSA8LSBjKCJVTUFQXzEiLCJVTUFQXzIiKQoKIyDilIDilIAgTWV0aG9kIDE6IE1TVCBwc2V1ZG90aW1lIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgApjYXQoIlByb2plY3RpbmcgU8OpemFyeSBjZWxscyBvbnRvIE1TVC4uLlxuIikKc2V6X21zdCA8LSBwcm9qZWN0X29udG9fbXN0KHNlel91bWFwX2Nvb3JkcywgY2VudHJvaWRfbWF0LCBtc3RfZ3JhcGgsIGdfZGlzdHNfdmVjKQoKIyDilIDilIAgTWV0aG9kIDI6IEtOTi13ZWlnaHRlZCBNb25vY2xlMyBwc2V1ZG90aW1lIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgApjYXQoIlRyYW5zZmVycmluZyBNb25vY2xlMyBwc2V1ZG90aW1lIHZpYSBLTk4gKGs9MTUpLi4uXG4iKQpyZWZfdW1hcF9tYXQgPC0gRW1iZWRkaW5ncyhjZDRfcmVmLCBVTUFQX05BTUUpCnJlZl9tM19wdCAgICA8LSBjZDRfcmVmQG1ldGEuZGF0YSRtb25vY2xlM19wc2V1ZG90aW1lX25vcm0KCm5uX3Jlc3VsdCA8LSBubjIoZGF0YT1yZWZfdW1hcF9tYXQsIHF1ZXJ5PXNlel91bWFwX2Nvb3Jkcywgaz0xNSkKaW52X2Rpc3QgIDwtIDEgLyAobm5fcmVzdWx0JG5uLmRpc3RzICsgMWUtNikKd2VpZ2h0cyAgIDwtIGludl9kaXN0IC8gcm93U3VtcyhpbnZfZGlzdCkKCiMg4pSA4pSAIEFzc2lnbiB0byBtZXRhZGF0YSDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKbWV0YV9zZXogPC0gc2V6YXJ5X29iakBtZXRhLmRhdGEgICMgRml4ZWQ6IHJlbW92ZSBbbWFpbHRvOiBdIHN5bnRheAoKbWV0YV9zZXokbXN0X3BzZXVkb3RpbWUgPC0gcG1heCgwLCBwbWluKDEwMCwKICAxMDAgKiAoc2V6X21zdCRwc2V1ZG90aW1lIC0gbXN0X3B0X3JhbmdlWzFdKSAvCiAgICAgICAgIChtc3RfcHRfcmFuZ2VbMl0gIC0gbXN0X3B0X3JhbmdlWzFdKQopKQptZXRhX3NleiRuZWFyZXN0X21pbGVzdG9uZV9tc3QgPC0gc2V6X21zdCRuZWFyZXN0X21zCm1ldGFfc2V6JG1vbm9jbGUzX3BzZXVkb3RpbWUgICA8LSByb3dTdW1zKAogIHdlaWdodHMgKiBtYXRyaXgocmVmX20zX3B0W25uX3Jlc3VsdCRubi5pZHhdLAogICAgICAgICAgICAgICAgICAgbnJvdz1ucm93KG5uX3Jlc3VsdCRubi5pZHgpKQopCgpzZXphcnlfb2JqQG1ldGEuZGF0YSA8LSBtZXRhX3NleiAgIyBGaXhlZDogcmVtb3ZlIFttYWlsdG86IF0gc3ludGF4CgojIOKUgOKUgCBCdWlsZCBwbG90IGRhdGFmcmFtZSB3aXRoIGNlbGwgbGluZSBpbmZvIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgApzZXpfcGxvdF9kZiA8LSBkYXRhLmZyYW1lKAogIFVNQVBfMSAgICAgICAgICA9IHNlel91bWFwX2Nvb3Jkc1ssIlVNQVBfMSJdLAogIFVNQVBfMiAgICAgICAgICA9IHNlel91bWFwX2Nvb3Jkc1ssIlVNQVBfMiJdLAogIGNlbGxfbGluZSAgICAgICA9IGZhY3RvcihzZXphcnlfb2JqJG9yaWcuaWRlbnQsIGxldmVscz1DRUxMX0xJTkVfT1JERVIpLAogIHByZWRpY3RlZF9zdGF0ZSA9IHNlemFyeV9vYmokcHJlZGljdGVkLnByZWRpY3RlZF9zdGF0ZSwKICBwcmVkX3Njb3JlICAgICAgPSBzZXphcnlfb2JqJHByZWRpY3RlZC5wcmVkaWN0ZWRfc3RhdGUuc2NvcmUsCiAgbXN0X3B0ICAgICAgICAgID0gc2V6YXJ5X29iakBtZXRhLmRhdGEkbXN0X3BzZXVkb3RpbWUsCiAgbTNfcHQgICAgICAgICAgID0gc2V6YXJ5X29iakBtZXRhLmRhdGEkbW9ub2NsZTNfcHNldWRvdGltZSwKICBtaWxlc3RvbmVfcmVmICAgPSBzZXphcnlfb2JqQG1ldGEuZGF0YSRuZWFyZXN0X21pbGVzdG9uZV9tc3QKKQoKY2F0KHNwcmludGYoIuKchSBQc2V1ZG90aW1lIGFzc2lnbmVkIHRvICVkIGNlbGxzXG4iLCBucm93KHNlel9wbG90X2RmKSkpCgojIEZJWEVEOiBDb3JyZWN0IHNwcmludGYoKSB1c2FnZSBmb3IgcmFuZ2UgdmVjdG9ycwptc3RfcmFuZ2UgPC0gcmFuZ2Uoc2V6X3Bsb3RfZGYkbXN0X3B0LCBuYS5ybT1UUlVFKQptM19yYW5nZSAgPC0gcmFuZ2Uoc2V6X3Bsb3RfZGYkbTNfcHQsIG5hLnJtPVRSVUUpCgpjYXQoc3ByaW50ZigiTVNUIHBzZXVkb3RpbWUgcmFuZ2U6ICAgICAgJS4xZiDigJMgJS4xZlxuIiwgbXN0X3JhbmdlWzFdLCBtc3RfcmFuZ2VbMl0pKQpjYXQoc3ByaW50ZigiTW9ub2NsZTMgcHNldWRvdGltZSByYW5nZTogJS4xZiDigJMgJS4xZlxuIiwgbTNfcmFuZ2VbMV0sIG0zX3JhbmdlWzJdKSkKCmNhdCgiXG5NU1QgcHNldWRvdGltZSBwZXIgY2VsbCBsaW5lIChtZWRpYW4pOlxuIikKc2V6X3Bsb3RfZGYgJT4lCiAgZ3JvdXBfYnkoY2VsbF9saW5lKSAlPiUKICBzdW1tYXJpc2UobWVkaWFuX21zdCA9IHJvdW5kKG1lZGlhbihtc3RfcHQsIG5hLnJtPVRSVUUpLCAxKSwKICAgICAgICAgICAgbWVkaWFuX20zICA9IHJvdW5kKG1lZGlhbihtM19wdCwgIG5hLnJtPVRSVUUpLCAxKSwKICAgICAgICAgICAgLmdyb3Vwcz0iZHJvcCIpICU+JQogIHByaW50KCkKCmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIFZpc3VhbGlzYXRpb24gMSDigJQgQWxsIENlbGwgTGluZXMgVG9nZXRoZXIKCmBgYHtyIGFsbC1saW5lcy11bWFwLCBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD02fQojIOKUgOKUgCBCSU9MT0dJQ0FMIE5PVEUg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACiMgVGhpcyBvdmVydmlldyBwbG90IHNob3dzIEFMTCA3IGNlbGwgbGluZXMgc2ltdWx0YW5lb3VzbHkgb24gdGhlIHJlZmVyZW5jZQojIFVNQVAuIFRoaXMgYW5zd2VyczogIkFzIGEgZ3JvdXAsIHdoZXJlIGRvIFPDqXphcnkgY2VsbCBsaW5lcyBtYXA/IgojIFRoZSBmYWNldGVkIHZlcnNpb24gc2hvd3MgZWFjaCBsaW5lIGluZGl2aWR1YWxseSBmb3IgZGV0YWlsZWQgY29tcGFyaXNvbi4KIwojIFdoYXQgdG8gbG9vayBmb3I6CiMgLSBDTFVTVEVSSU5HOiBEbyBhbGwgY2VsbCBsaW5lcyBjbHVzdGVyIHRvZ2V0aGVyIChzYW1lIGNlbGwgb2Ygb3JpZ2luKT8KIyAgIE9yIGRvIHRoZXkgc2NhdHRlciB0byBkaWZmZXJlbnQgcmVnaW9ucyAoaGV0ZXJvZ2VuZW91cyBjZWxsIG9mIG9yaWdpbik/CiMgLSBPVkVSTEFQIFdJVEggUkVGRVJFTkNFIFNUQVRFUzogV2hpY2ggZ3JleSByZWZlcmVuY2UgcmVnaW9uIGRvIHRoZQojICAgY29sb3VyZWQgU8OpemFyeSBjZWxscyBvdmVybGFwIHdpdGg/CiMgLSBNSUxFU1RPTkUgUFJPWElNSVRZOiBXaGljaCBtaWxlc3RvbmUgbGFiZWxzIChNMDAtTTA3KSBhcmUgdGhlIGNlbGxzCiMgICBuZWFyZXN0IHRvPyBUaGlzIGdpdmVzIHRoZSBtb3N0IHByZWNpc2UgdHJhamVjdG9yeSBwb3NpdGlvbi4KIyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKCiMg4pSA4pSAIFBhbmVsIEE6IEFsbCBsaW5lcyBvbiByZWZlcmVuY2UgVU1BUCDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKcF9hbGxfbGluZXMgPC0gZ2dwbG90KCkgKwogICMgUmVmZXJlbmNlIGNlbGxzIGFzIGdyZXkgYmFja2dyb3VuZCDigJQgc2hvd3MgdGhlIG5vcm1hbCBDRDQgbGFuZHNjYXBlCiAgZ2VvbV9wb2ludChkYXRhID0gcmVmX2JnW3NhbXBsZShucm93KHJlZl9iZykpLF0sCiAgICAgICAgICAgICBhZXMoeD1VTUFQXzEsIHk9VU1BUF8yKSwgY29sb3VyPSJncmV5ODgiLCBzaXplPS4yNSwgYWxwaGE9LjQpICsKICAjIE1TVCB0cmFqZWN0b3J5IGxpbmVzCiAgZ2VvbV9zZWdtZW50KGRhdGEgPSBtc3RfZWRnZV9kZiwKICAgICAgICAgICAgICAgYWVzKHg9eDEsIHk9eTEsIHhlbmQ9eDIsIHllbmQ9eTIpLAogICAgICAgICAgICAgICBjb2xvdXI9ImdyZXkzMCIsIGxpbmV3aWR0aD0xLCBhbHBoYT0uOCwgbGluZWVuZD0icm91bmQiKSArCiAgIyBTw6l6YXJ5IGNlbGxzIGNvbG91cmVkIGJ5IGNlbGwgbGluZQogIGdlb21fcG9pbnQoZGF0YSA9IHNlel9wbG90X2RmW3NhbXBsZShucm93KHNlel9wbG90X2RmKSksXSwKICAgICAgICAgICAgIGFlcyh4PVVNQVBfMSwgeT1VTUFQXzIsIGNvbG91cj1jZWxsX2xpbmUpLAogICAgICAgICAgICAgc2l6ZT0xLjUsIGFscGhhPS43NSkgKwogICMgTWlsZXN0b25lIGxhYmVscyB3aXRoIHN0YXRlIGFubm90YXRpb24KICBnZW9tX2xhYmVsX3JlcGVsKGRhdGEgPSBtaWxlc3RvbmVfY2VudHJvaWRzLAogICAgICAgICAgICAgICAgICAgYWVzKHg9VU1BUF8xLCB5PVVNQVBfMiwKICAgICAgICAgICAgICAgICAgICAgICBsYWJlbD1wYXN0ZTAobWlsZXN0b25lLCAiXG4iLCBzdGF0ZSkpLAogICAgICAgICAgICAgICAgICAgc2l6ZT0yLjUsIGZvbnRmYWNlPSJib2xkIiwgZmlsbD0id2hpdGUiLAogICAgICAgICAgICAgICAgICAgY29sb3VyPSJncmV5MTUiLCBsYWJlbC5zaXplPTAuMiwKICAgICAgICAgICAgICAgICAgIG1heC5vdmVybGFwcz0yMCwgYm94LnBhZGRpbmc9MC41KSArCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXM9Q0VMTF9MSU5FX0NPTE9SUywgbmFtZT0iQ2VsbCBsaW5lIikgKwogIHRoZW1lX2NsYXNzaWMoKSArCiAgbGFicyh4PSJVTUFQLTEiLCB5PSJVTUFQLTIiLAogICAgICAgdGl0bGU9IlPDqXphcnkgY2VsbCBsaW5lcyDigJQgcHJvamVjdGlvbiBvbnRvIGhlYWx0aHkgQ0Q0IFQgY2VsbCByZWZlcmVuY2UiLAogICAgICAgc3VidGl0bGU9IkdyZXkgPSBoZWFsdGh5IENENCByZWZlcmVuY2UgKDExLDQ2NiBjZWxscykgfCBDb2xvdXJlZCA9IFPDqXphcnkgY2VsbCBsaW5lcyAoTDHigJNMNykgfCBMaW5lcyA9IE1TVCB0cmFqZWN0b3J5IikgKwogIHRoZW1lKHBsb3QudGl0bGUgICAgPSBlbGVtZW50X3RleHQoc2l6ZT0xNCwgZmFjZT0iYm9sZCIpLAogICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0xMCwgY29sb3VyPSJncmV5NDAiKSwKICAgICAgICBsZWdlbmQudGl0bGUgID0gZWxlbWVudF90ZXh0KHNpemU9MTEsIGZhY2U9ImJvbGQiKSwKICAgICAgICBsZWdlbmQudGV4dCAgID0gZWxlbWVudF90ZXh0KHNpemU9MTApKSArCiAgZ3VpZGVzKGNvbG91ciA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXM9bGlzdChzaXplPTQsIGFscGhhPTEpKSkKCnByaW50KHBfYWxsX2xpbmVzKQpgYGAKCgoKCmBgYHtyIGZhY2V0ZWQtbGluZXMsIGZpZy53aWR0aD0xMywgZmlnLmhlaWdodD05fQojIOKUgOKUgCBCSU9MT0dJQ0FMIE5PVEUg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACiMgRmFjZXRpbmcgYnkgY2VsbCBsaW5lIGlzIHRoZSBtb3N0IGltcG9ydGFudCB2aXN1YWxpc2F0aW9uIGZvciBjZWxsIG9mCiMgb3JpZ2luIGFuYWx5c2lzLiBFYWNoIHBhbmVsIHNob3dzIE9ORSBjZWxsIGxpbmUgcHJvamVjdGVkIG9udG8gdGhlCiMgc2FtZSByZWZlcmVuY2UgbGFuZHNjYXBlLiBUaGlzIGFsbG93cyB5b3UgdG86CiMgMS4gU2VlIGV4YWN0bHkgd2hlcmUgZWFjaCBsaW5lJ3MgY2VsbHMgY29uY2VudHJhdGUgb24gdGhlIFVNQVAKIyAyLiBDb21wYXJlIGxpbmVzIGRpcmVjdGx5IOKAlCBkbyBMMSBhbmQgTDIgb3ZlcmxhcD8gRG8gTDUgYW5kIEw2IGRpZmZlcj8KIyAzLiBJZGVudGlmeSBpbnRyYS1saW5lIGhldGVyb2dlbmVpdHkg4oCUIGlzIHRoZSBsaW5lIGEgc2luZ2xlIHRpZ2h0IGNsdXN0ZXIKIyAgICBvciBzY2F0dGVyZWQgYWNyb3NzIG11bHRpcGxlIHN0YXRlcz8KIyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKCnBfZmFjZXRlZCA8LSBnZ3Bsb3QoKSArCiAgZ2VvbV9wb2ludChkYXRhID0gcmVmX2JnW3NhbXBsZShucm93KHJlZl9iZykpLF0sCiAgICAgICAgICAgICBhZXMoeD1VTUFQXzEsIHk9VU1BUF8yKSwgY29sb3VyPSJncmV5ODgiLCBzaXplPS4yLCBhbHBoYT0uMzUpICsKICBnZW9tX3NlZ21lbnQoZGF0YSA9IG1zdF9lZGdlX2RmLAogICAgICAgICAgICAgICBhZXMoeD14MSwgeT15MSwgeGVuZD14MiwgeWVuZD15MiksCiAgICAgICAgICAgICAgIGNvbG91cj0iZ3JleTQwIiwgbGluZXdpZHRoPS43LCBhbHBoYT0uNywgbGluZWVuZD0icm91bmQiKSArCiAgZ2VvbV9wb2ludChkYXRhID0gc2V6X3Bsb3RfZGYsCiAgICAgICAgICAgICBhZXMoeD1VTUFQXzEsIHk9VU1BUF8yLCBjb2xvdXI9Y2VsbF9saW5lKSwKICAgICAgICAgICAgIHNpemU9MSwgYWxwaGE9LjgpICsKICBnZW9tX3RleHRfcmVwZWwoZGF0YSA9IG1pbGVzdG9uZV9jZW50cm9pZHMsCiAgICAgICAgICAgICAgICAgIGFlcyh4PVVNQVBfMSwgeT1VTUFQXzIsIGxhYmVsPW1pbGVzdG9uZSksCiAgICAgICAgICAgICAgICAgIHNpemU9Mi4yLCBmb250ZmFjZT0iYm9sZCIsIGNvbG91cj0iZ3JleTIwIiwKICAgICAgICAgICAgICAgICAgbWF4Lm92ZXJsYXBzPTgsIHNlZ21lbnQuc2l6ZT0uMiwgYm94LnBhZGRpbmc9MC4zKSArCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXM9Q0VMTF9MSU5FX0NPTE9SUywgZ3VpZGU9Im5vbmUiKSArCiAgZmFjZXRfd3JhcCh+Y2VsbF9saW5lLCBuY29sPTQpICsKICB0aGVtZV9jbGFzc2ljKCkgKwogIGxhYnMoeD0iVU1BUC0xIiwgeT0iVU1BUC0yIiwKICAgICAgIHRpdGxlPSJQZXIgY2VsbCBsaW5lIHByb2plY3Rpb24g4oCUIGVhY2ggcGFuZWwgc2hvd3Mgb25lIFPDqXphcnkgY2VsbCBsaW5lIG9uIHRoZSBDRDQgcmVmZXJlbmNlIiwKICAgICAgIHN1YnRpdGxlPSJHcmV5ID0gaGVhbHRoeSBDRDQgcmVmZXJlbmNlIHwgQ29sb3VyZWQgPSB0aGF0IGNlbGwgbGluZSdzIGNlbGxzIHwgTTAwPU5haXZlLCBNMDEtTTAyPVRDTSwgTTAzLU0wND1URU0sIE0wNT1UZW1yYSwgTTA2LU0wNz1UcmVnIikgKwogIHRoZW1lKHBsb3QudGl0bGUgICA9IGVsZW1lbnRfdGV4dChzaXplPTEzLCBmYWNlPSJib2xkIiksCiAgICAgICAgc3RyaXAudGV4dCAgID0gZWxlbWVudF90ZXh0KHNpemU9MTIsIGZhY2U9ImJvbGQiKSwKICAgICAgICBzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGw9ImdyZXk5NSIsIGNvbG91cj0iZ3JleTcwIikpCgpwcmludChwX2ZhY2V0ZWQpCgpgYGAKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBWaXN1YWxpc2F0aW9uIDIg4oCUIFByZWRpY3RlZCBTdGF0ZSBwZXIgQ2VsbCBMaW5lCgpgYGB7ciBzdGF0ZS1wZXItbGluZSwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTZ9CiMg4pSA4pSAIEJJT0xPR0lDQUwgTk9URSDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKIyBUaGUgc3RhY2tlZCBiYXIgY2hhcnQgc2hvd3Mgd2hhdCBQUk9QT1JUSU9OIG9mIGVhY2ggY2VsbCBsaW5lJ3MgY2VsbHMKIyBtYXAgdG8gZWFjaCBDRDQgc3RhdGUuIFRoaXMgaXMgdGhlIHByaW1hcnkgY2VsbCBvZiBvcmlnaW4gc3VtbWFyeS4KIwojIElkZWFsIG91dGNvbWUgZm9yIGEgc3Ryb25nIGNvbmNsdXNpb246CiMgLSBPTkUgc3RhdGUgZG9taW5hdGVzICg+NzAlKSBhY3Jvc3MgbW9zdCBjZWxsIGxpbmVzIOKGkiBjbGVhciBjZWxsIG9mIG9yaWdpbgojIC0gZS5nLiBpZiBMMS1MNyBhbGwgc2hvdyA+ODAlIENENCBUQ00g4oaSIFPDqXphcnkgb3JpZ2luYXRlcyBmcm9tIFRDTSBjZWxscwojCiMgTWl4ZWQgb3V0Y29tZSAoYmlvbG9naWNhbGx5IGFsc28gaW5mb3JtYXRpdmUpOgojIC0gU29tZSBsaW5lcyBzaG93IFRDTSBkb21pbmFuY2UsIG90aGVycyBURU0g4oaSIGRpc2Vhc2UgaGV0ZXJvZ2VuZWl0eQojIC0gVGhpcyB3b3VsZCBzdWdnZXN0IGRpZmZlcmVudCBwYXRpZW50cyBoYXZlIGRpZmZlcmVudCBjZWxscyBvZiBvcmlnaW4sCiMgICBwb3NzaWJseSBjb3JyZWxhdGluZyB3aXRoIGRpZmZlcmVudCBjbGluaWNhbCBwcmVzZW50YXRpb25zIG9yIGRydWcgcmVzcG9uc2VzCiMg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACgojIENvbXB1dGUgcGVyLWxpbmUgc3RhdGUgcHJvcG9ydGlvbnMKc3RhdGVfcGVyX2xpbmUgPC0gc2V6X3Bsb3RfZGYgJT4lCiAgZmlsdGVyKCFpcy5uYShwcmVkaWN0ZWRfc3RhdGUpKSAlPiUKICBjb3VudChjZWxsX2xpbmUsIHByZWRpY3RlZF9zdGF0ZSkgJT4lCiAgZ3JvdXBfYnkoY2VsbF9saW5lKSAlPiUKICBtdXRhdGUocGN0ID0gcm91bmQoMTAwICogbiAvIHN1bShuKSwgMSkpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBtdXRhdGUocHJlZGljdGVkX3N0YXRlID0gZmFjdG9yKHByZWRpY3RlZF9zdGF0ZSwgbGV2ZWxzPVNUQVRFX09SREVSKSkKCiMg4pSA4pSAIFN0YWNrZWQgYmFyOiBwcm9wb3J0aW9uIG9mIGNlbGxzIHBlciBzdGF0ZSBwZXIgbGluZSDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKcF9zdGF0ZV9zdGFjayA8LSBnZ3Bsb3Qoc3RhdGVfcGVyX2xpbmUsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZXMoeD1jZWxsX2xpbmUsIHk9cGN0LCBmaWxsPXByZWRpY3RlZF9zdGF0ZSkpICsKICBnZW9tX2NvbCh3aWR0aD0uNzUsIGNvbG91cj0id2hpdGUiLCBsaW5ld2lkdGg9LjMpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsPWlmZWxzZShwY3QgPj0gNSwgcGFzdGUwKHBjdCwiJSIpLCAiIikpLAogICAgICAgICAgICBwb3NpdGlvbj1wb3NpdGlvbl9zdGFjayh2anVzdD0wLjUpLAogICAgICAgICAgICBzaXplPTMuMiwgY29sb3VyPSJ3aGl0ZSIsIGZvbnRmYWNlPSJib2xkIikgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1TVEFURV9DT0xPUlMsIG5hbWU9IlByZWRpY3RlZCBDRDQgc3RhdGUiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZD1jKDAsMCksIGxpbWl0cz1jKDAsMTA1KSkgKwogIHRoZW1lX2NsYXNzaWMoKSArCiAgbGFicyh4PSJDZWxsIGxpbmUiLCB5PSIlIG9mIGNlbGxzIiwKICAgICAgIHRpdGxlPSJQcmVkaWN0ZWQgQ0Q0IHN0YXRlIGNvbXBvc2l0aW9uIHBlciBTw6l6YXJ5IGNlbGwgbGluZSIsCiAgICAgICBzdWJ0aXRsZT0iRWFjaCBiYXIgPSAxMDAlIG9mIHRoYXQgY2VsbCBsaW5lJ3MgY2VsbHMgfCBMYWJlbHMgc2hvd24gZm9yIHByb3BvcnRpb25zIOKJpTUlIikgKwogIHRoZW1lKHBsb3QudGl0bGUgID0gZWxlbWVudF90ZXh0KHNpemU9MTMsIGZhY2U9ImJvbGQiKSwKICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplPTEyLCBmYWNlPSJib2xkIiksCiAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MTAsIGZhY2U9ImJvbGQiKSkKCnByaW50KHBfc3RhdGVfc3RhY2spCgojIOKUgOKUgCBQcmludCBzdW1tYXJ5IHRhYmxlIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgApjYXQoIlxuU3RhdGUgcHJvcG9ydGlvbnMgcGVyIGNlbGwgbGluZTpcbiIpCnN0YXRlX3Blcl9saW5lICU+JQogIHNlbGVjdChjZWxsX2xpbmUsIHByZWRpY3RlZF9zdGF0ZSwgbiwgcGN0KSAlPiUKICBhcnJhbmdlKGNlbGxfbGluZSwgZGVzYyhwY3QpKSAlPiUKICBrYWJsZShjb2wubmFtZXM9YygiQ2VsbCBsaW5lIiwiUHJlZGljdGVkIHN0YXRlIiwiTiBjZWxscyIsIiUgY2VsbHMiKSwKICAgICAgICBjYXB0aW9uPSJQcmVkaWN0ZWQgQ0Q0IHN0YXRlIGNvbXBvc2l0aW9uIHBlciBTw6l6YXJ5IGNlbGwgbGluZSIpICU+JQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnM9Yygic3RyaXBlZCIsImhvdmVyIiksIGZ1bGxfd2lkdGg9RkFMU0UpICU+JQogIHByaW50KCkKYGBgCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgVmlzdWFsaXNhdGlvbiAzIOKAlCBQc2V1ZG90aW1lIHBlciBDZWxsIExpbmUKCmBgYHtyIHBzZXVkb3RpbWUtcGVyLWxpbmUsIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTEwfQojIOKUgOKUgCBCSU9MT0dJQ0FMIE5PVEUg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACiMgUHNldWRvdGltZSBtZWFzdXJlcyBESUZGRVJFTlRJQVRJT04gTUFUVVJJVFkgb24gYSAwLTEwMCBzY2FsZSB3aGVyZToKIyAgIDAgID0gbW9zdCBuYWl2ZS1saWtlICh1bmRpZmZlcmVudGlhdGVkLCBDRDQgTmFpdmUgPSBNMDApCiMgICAxMDAgPSBtb3N0IHRlcm1pbmFsbHkgZGlmZmVyZW50aWF0ZWQgKFRlbXJhIG9yIHRlcm1pbmFsIFRyZWcpCiMKIyBGb3IgU8OpemFyeSBjZWxscywgdGhlIHBzZXVkb3RpbWUgZGlzdHJpYnV0aW9uIHRlbGxzIHVzOgojCiMgTE9XIHBzZXVkb3RpbWUgKDAtMzApOgojICAgTWFsaWduYW50IGNlbGxzIHJlc2VtYmxlIHVuZGlmZmVyZW50aWF0ZWQvZWFybHkgbWVtb3J5IGNlbGxzLgojICAgU3VnZ2VzdHMgdHJhbnNmb3JtYXRpb24gb2NjdXJyZWQgYXQgYW4gZWFybHkgZGlmZmVyZW50aWF0aW9uIHN0YWdlLgojICAgVGhlc2UgdHVtb3VycyBtYXkgZXhwcmVzcyBuYWl2ZS9UQ00gc3Vydml2YWwgZmFjdG9ycyAoSUwtN1IsIENDUjcpLgojCiMgSU5URVJNRURJQVRFIHBzZXVkb3RpbWUgKDMwLTYwKToKIyAgIENlbGxzIHJlc2VtYmxlIGNlbnRyYWwvdHJhbnNpdGlvbmFsIG1lbW9yeSBUIGNlbGxzIChUQ00gc3RhZ2UpLgojICAgTW9zdCBTUyBsaXRlcmF0dXJlIHN1Z2dlc3RzIHRoaXMg4oCUIFPDqXphcnkgY2VsbHMgb2Z0ZW4gcmV0YWluIENDUjQsCiMgICBDRDI3LCBhbmQgb3RoZXIgVENNIG1hcmtlcnMgd2hpbGUgbG9zaW5nIENDUjcuCiMKIyBISUdIIHBzZXVkb3RpbWUgKDYwLTEwMCk6CiMgICBDZWxscyByZXNlbWJsZSBURU0gb3IgVGVtcmEg4oCUIHVudXN1YWwgZm9yIFNTIGFzIG1vc3QgU1MgY2VsbHMKIyAgIGRvIG5vdCBleHByZXNzIGhpZ2ggbGV2ZWxzIG9mIEdaTUIsIFBSRjEsIG9yIENYM0NSMS4KIyAgIElmIGEgbGluZSBtYXBzIGhlcmUsIGl0IG1heSByZXByZXNlbnQgYSBtb3JlIGN5dG90b3hpYyB2YXJpYW50LgojCiMgQ29tcGFyaW5nIGxpbmVzOiBpZiBMMSBoYXMgcHNldWRvdGltZSB+MzAgYW5kIEw1IGhhcyBwc2V1ZG90aW1lIH42MCwKIyB0aGlzIG1lYW5zIEw1IGNlbGxzIGFyZSBtb3JlIGRpZmZlcmVudGlhdGVkIHRoYW4gTDEgY2VsbHMsIHBvc3NpYmx5CiMgZXhwbGFpbmluZyBkaWZmZXJlbmNlcyBpbiBkaXNlYXNlIGFnZ3Jlc3NpdmVuZXNzIG9yIGRydWcgc2Vuc2l0aXZpdHkuCiMg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACgojIOKUgOKUgCBQYW5lbCBBOiBNU1QgcHNldWRvdGltZSB2aW9saW4gcGVyIGNlbGwgbGluZSDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKcF92aW9saW5fbXN0IDwtIGdncGxvdChzZXpfcGxvdF9kZiwKICAgICAgICAgICAgICAgICAgICAgICAgYWVzKHg9Y2VsbF9saW5lLCB5PW1zdF9wdCwgZmlsbD1jZWxsX2xpbmUpKSArCiAgZ2VvbV92aW9saW4oc2NhbGU9IndpZHRoIiwgdHJpbT1GQUxTRSwgYWxwaGE9Ljg1LCBjb2xvdXI9IndoaXRlIikgKwogIGdlb21fYm94cGxvdCh3aWR0aD0uMDgsIGZpbGw9IndoaXRlIiwgb3V0bGllci5zaXplPS44LAogICAgICAgICAgICAgICBvdXRsaWVyLmFscGhhPS40LCBjb2xvdXI9ImdyZXkzMCIpICsKICAjIFJlZmVyZW5jZSBsaW5lcyBmb3IgYmlvbG9naWNhbCBpbnRlcnByZXRhdGlvbgogIGdlb21faGxpbmUoeWludGVyY2VwdD0zMywgbGluZXR5cGU9ImRhc2hlZCIsIGNvbG91cj0iZ3JleTYwIiwgbGluZXdpZHRoPS41KSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0PTY2LCBsaW5ldHlwZT0iZGFzaGVkIiwgY29sb3VyPSJncmV5NjAiLCBsaW5ld2lkdGg9LjUpICsKICBhbm5vdGF0ZSgidGV4dCIsIHg9MC40LCB5PTE2LCAgbGFiZWw9Ik5haXZlL1xuZWFybHkgVENNIiwgc2l6ZT0yLjgsCiAgICAgICAgICAgY29sb3VyPSJncmV5NDAiLCBmb250ZmFjZT0iaXRhbGljIiwgaGp1c3Q9MCkgKwogIGFubm90YXRlKCJ0ZXh0IiwgeD0wLjQsIHk9NTAsICBsYWJlbD0iVENNL1xuZWFybHkgVEVNIiwgc2l6ZT0yLjgsCiAgICAgICAgICAgY29sb3VyPSJncmV5NDAiLCBmb250ZmFjZT0iaXRhbGljIiwgaGp1c3Q9MCkgKwogIGFubm90YXRlKCJ0ZXh0IiwgeD0wLjQsIHk9ODMsICBsYWJlbD0iVEVNL1xuVGVtcmEvVHJlZyIsIHNpemU9Mi44LAogICAgICAgICAgIGNvbG91cj0iZ3JleTQwIiwgZm9udGZhY2U9Iml0YWxpYyIsIGhqdXN0PTApICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9Q0VMTF9MSU5FX0NPTE9SUywgZ3VpZGU9Im5vbmUiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cz1jKDAsMTAwKSwgYnJlYWtzPXNlcSgwLDEwMCwyNSkpICsKICB0aGVtZV9jbGFzc2ljKCkgKwogIGxhYnMoeD0iQ2VsbCBsaW5lIiwgeT0iTVNUIHBzZXVkb3RpbWUgKDDigJMxMDApIiwKICAgICAgIHRpdGxlPSJEaWZmZXJlbnRpYXRpb24gbWF0dXJpdHkgcGVyIFPDqXphcnkgY2VsbCBsaW5lIChDdXN0b20gTVNUKSIsCiAgICAgICBzdWJ0aXRsZT0iRGFzaGVkIGxpbmVzID0gdHJhamVjdG9yeSB0aGlyZHMgfCAwPU5haXZlLCAxMDA9VGVtcmEvVGVybWluYWwgVHJlZyIpICsKICB0aGVtZShwbG90LnRpdGxlICA9IGVsZW1lbnRfdGV4dChzaXplPTEyLCBmYWNlPSJib2xkIiksCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZT0xMSwgZmFjZT0iYm9sZCIpKQoKIyDilIDilIAgUGFuZWwgQjogTW9ub2NsZTMgcHNldWRvdGltZSB2aW9saW4g4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACnBfdmlvbGluX20zIDwtIGdncGxvdChzZXpfcGxvdF9kZiwKICAgICAgICAgICAgICAgICAgICAgICBhZXMoeD1jZWxsX2xpbmUsIHk9bTNfcHQsIGZpbGw9Y2VsbF9saW5lKSkgKwogIGdlb21fdmlvbGluKHNjYWxlPSJ3aWR0aCIsIHRyaW09RkFMU0UsIGFscGhhPS44NSwgY29sb3VyPSJ3aGl0ZSIpICsKICBnZW9tX2JveHBsb3Qod2lkdGg9LjA4LCBmaWxsPSJ3aGl0ZSIsIG91dGxpZXIuc2l6ZT0uOCwKICAgICAgICAgICAgICAgb3V0bGllci5hbHBoYT0uNCwgY29sb3VyPSJncmV5MzAiKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0PTMzLCBsaW5ldHlwZT0iZGFzaGVkIiwgY29sb3VyPSJncmV5NjAiLCBsaW5ld2lkdGg9LjUpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9NjYsIGxpbmV0eXBlPSJkYXNoZWQiLCBjb2xvdXI9ImdyZXk2MCIsIGxpbmV3aWR0aD0uNSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1DRUxMX0xJTkVfQ09MT1JTLCBndWlkZT0ibm9uZSIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzPWMoMCwxMDApLCBicmVha3M9c2VxKDAsMTAwLDI1KSkgKwogIHRoZW1lX2NsYXNzaWMoKSArCiAgbGFicyh4PSJDZWxsIGxpbmUiLCB5PSJNb25vY2xlMyBwc2V1ZG90aW1lICgw4oCTMTAwKSIsCiAgICAgICB0aXRsZT0iRGlmZmVyZW50aWF0aW9uIG1hdHVyaXR5IHBlciBTw6l6YXJ5IGNlbGwgbGluZSAoTW9ub2NsZTMpIiwKICAgICAgIHN1YnRpdGxlPSJLTk4td2VpZ2h0ZWQgdHJhbnNmZXIgZnJvbSAxNSBuZWFyZXN0IHJlZmVyZW5jZSBuZWlnaGJvdXJzIikgKwogIHRoZW1lKHBsb3QudGl0bGUgID0gZWxlbWVudF90ZXh0KHNpemU9MTIsIGZhY2U9ImJvbGQiKSwKICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplPTExLCBmYWNlPSJib2xkIikpCgpwcmludChwX3Zpb2xpbl9tc3QgLyBwX3Zpb2xpbl9tMykKYGBgCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgVmlzdWFsaXNhdGlvbiA0IOKAlCBOZWFyZXN0IE1pbGVzdG9uZSBwZXIgQ2VsbCBMaW5lCgpgYGB7ciBtaWxlc3RvbmUtcGVyLWxpbmUsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD02fQojIOKUgOKUgCBCSU9MT0dJQ0FMIE5PVEUg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACiMgTmVhcmVzdCBtaWxlc3RvbmUgaXMgdGhlIG1vc3QgUFJFQ0lTRSBjZWxsIG9mIG9yaWdpbiBpbmRpY2F0b3IgYmVjYXVzZQojIGl0IHBsYWNlcyBjZWxscyBhdCBhIHNwZWNpZmljIHBvaW50IG9uIHRoZSBkaWZmZXJlbnRpYXRpb24gcGF0aCwgbm90IGp1c3QKIyBhIGJyb2FkIHN0YXRlIGNhdGVnb3J5LiBUaGUgbWlsZXN0b25lIHN5c3RlbSBoYXMgOCBwb3NpdGlvbnM6CiMKIyAgIE0wMCA9IENENCBOYWl2ZSAocm9vdCkKIyAgIE0wMSA9IFRDTSBlYXJseSAgKENDUjcrLCBTMTAwQTQrLCBBUVAzIGxvdykKIyAgIE0wMiA9IFRDTSBsYXRlICAgKGJyYW5jaCBwb2ludCDigJQgQ0NSNCssIElUR0IxKykg4oaQIEtFWSBCUkFOQ0ggUE9JTlQKIyAgIE0wMyA9IFRFTSBlYXJseSAgKEdaTUsrLCBDQ0w1KywgQ1hDUjMrKQojICAgTTA0ID0gVEVNIGxhdGUgICAoRU9NRVMrLCBIT1BYKykKIyAgIE0wNSA9IFRlbXJhL0NUTCAgKEdaTUIrLCBQUkYxKywgQ1gzQ1IxKywgTktHNyspCiMgICBNMDYgPSBUcmVnIG5haXZlIChGT1hQMyssIElMMlJBKywgSUtaRjIrKQojICAgTTA3ID0gVHJlZyB0ZXJtaW5hbCAoQ1RMQTQrLCBUSUdJVCssIFRORlJTRjE4KykKIwojIE0wMiBpcyB0aGUgQlJBTkNIIFBPSU5UIOKAlCBjZWxscyBhdCBNMDIgYXJlIGF0IHRoZSBkZWNpc2lvbiBwb2ludCBiZXR3ZWVuCiMgYmVjb21pbmcgZWZmZWN0b3IgbWVtb3J5IChURU0gYXJtKSBvciByZWd1bGF0b3J5IChUcmVnIGFybSkuIElmIFPDqXphcnkKIyBjZWxscyBjbHVzdGVyIGF0IE0wMiwgdGhpcyBpcyBiaW9sb2dpY2FsbHkgdmVyeSBpbnRlcmVzdGluZyDigJQgaXQgc3VnZ2VzdHMKIyB0aGUgbWFsaWduYW50IHRyYW5zZm9ybWF0aW9uIGNhcHR1cmVkIGEgY2VsbCB0aGF0IHdhcyAiZGVjaWRpbmciIGl0cyBmYXRlLgojIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgAoKIyBNaWxlc3RvbmUgbGFiZWxzIHdpdGggYmlvbG9naWNhbCBhbm5vdGF0aW9uCm1pbGVzdG9uZV9sYWJlbHMgPC0gYygKICAiTTAwIiA9ICJNMDBcbk5haXZlIiwKICAiTTAxIiA9ICJNMDFcblRDTSBlYXJseSIsCiAgIk0wMiIgPSAiTTAyXG5UQ00gbGF0ZVxuKGJyYW5jaCkiLAogICJNMDMiID0gIk0wM1xuVEVNIGVhcmx5IiwKICAiTTA0IiA9ICJNMDRcblRFTSBsYXRlIiwKICAiTTA1IiA9ICJNMDVcblRlbXJhL0NUTCIsCiAgIk0wNiIgPSAiTTA2XG5UcmVnIG5haXZlIiwKICAiTTA3IiA9ICJNMDdcblRyZWcgdGVybWluYWwiCikKCiMgQ29tcHV0ZSBtaWxlc3RvbmUgZGlzdHJpYnV0aW9uIHBlciBjZWxsIGxpbmUKbXNfcGVyX2xpbmUgPC0gc2V6X3Bsb3RfZGYgJT4lCiAgZmlsdGVyKCFpcy5uYShtaWxlc3RvbmVfcmVmKSkgJT4lCiAgY291bnQoY2VsbF9saW5lLCBtaWxlc3RvbmVfcmVmKSAlPiUKICBncm91cF9ieShjZWxsX2xpbmUpICU+JQogIG11dGF0ZShwY3QgPSByb3VuZCgxMDAgKiBuIC8gc3VtKG4pLCAxKSkgJT4lCiAgdW5ncm91cCgpICU+JQogIG11dGF0ZSgKICAgIG1pbGVzdG9uZV9yZWYgPSBmYWN0b3IobWlsZXN0b25lX3JlZiwgbGV2ZWxzPW1pbGVzdG9uZV9vcmRlciksCiAgICBtc19zdGF0ZSA9IG1pbGVzdG9uZV9jZW50cm9pZHMkc3RhdGVbCiAgICAgIG1hdGNoKG1pbGVzdG9uZV9yZWYsIG1pbGVzdG9uZV9jZW50cm9pZHMkbWlsZXN0b25lKV0KICApCgojIOKUgOKUgCBIZWF0bWFwLXN0eWxlIHRpbGUgcGxvdDogY2VsbCBsaW5lIMOXIG1pbGVzdG9uZSDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKcF9tc19oZWF0bWFwIDwtIGdncGxvdChtc19wZXJfbGluZSwKICAgICAgICAgICAgICAgICAgICAgICAgYWVzKHg9bWlsZXN0b25lX3JlZiwgeT1jZWxsX2xpbmUsIGZpbGw9cGN0KSkgKwogIGdlb21fdGlsZShjb2xvdXI9IndoaXRlIiwgbGluZXdpZHRoPS41KSArCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbD1pZmVsc2UocGN0ID49IDIsIHBhc3RlMChwY3QsIiUiKSwgIiIpKSwKICAgICAgICAgICAgc2l6ZT0zLjIsIGZvbnRmYWNlPSJib2xkIiwKICAgICAgICAgICAgY29sb3VyPWlmZWxzZShtc19wZXJfbGluZSRwY3QgPiA0MCwgIndoaXRlIiwgImdyZXkyMCIpKSArCiAgc2NhbGVfZmlsbF9ncmFkaWVudG4oCiAgICBjb2xvdXJzID0gYygiI0Y3RkJGRiIsIiNERUVCRjciLCIjOUVDQUUxIiwiIzQyOTJDNiIsIiMwODUxOUMiLCIjMDgzMDZCIiksCiAgICBuYW1lICAgID0gIiUgb2YgY2VsbFxubGluZSBjZWxscyIsCiAgICBsaW1pdHMgID0gYygwLDEwMCkKICApICsKICBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscz1taWxlc3RvbmVfbGFiZWxzKSArCiAgdGhlbWVfY2xhc3NpYygpICsKICBsYWJzKHg9IlJlZmVyZW5jZSBtaWxlc3RvbmUiLCB5PSJTw6l6YXJ5IGNlbGwgbGluZSIsCiAgICAgICB0aXRsZT0iTmVhcmVzdCByZWZlcmVuY2UgbWlsZXN0b25lIGRpc3RyaWJ1dGlvbiBwZXIgY2VsbCBsaW5lIiwKICAgICAgIHN1YnRpdGxlPSJEYXJrZXIgPSBtb3JlIGNlbGxzIG1hcHBpbmcgdG8gdGhhdCBtaWxlc3RvbmUgfCBNMDIgPSBicmFuY2ggcG9pbnQgYmV0d2VlbiBlZmZlY3RvciBhbmQgcmVndWxhdG9yeSBhcm1zIikgKwogIHRoZW1lKHBsb3QudGl0bGUgICA9IGVsZW1lbnRfdGV4dChzaXplPTEzLCBmYWNlPSJib2xkIiksCiAgICAgICAgYXhpcy50ZXh0LnggID0gZWxlbWVudF90ZXh0KHNpemU9OSksCiAgICAgICAgYXhpcy50ZXh0LnkgID0gZWxlbWVudF90ZXh0KHNpemU9MTEsIGZhY2U9ImJvbGQiKSwKICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT05KSkKCnByaW50KHBfbXNfaGVhdG1hcCkKCiMg4pSA4pSAIFN0YWNrZWQgYmFyOiBtaWxlc3RvbmUgcGVyIGNlbGwgbGluZSDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKbXNfc3RhdGVfY29sb3JzIDwtIHNldE5hbWVzKAogIFNUQVRFX0NPTE9SU1ttaWxlc3RvbmVfY2VudHJvaWRzJHN0YXRlW21hdGNoKG1pbGVzdG9uZV9vcmRlciwgbWlsZXN0b25lX2NlbnRyb2lkcyRtaWxlc3RvbmUpXV0sCiAgbWlsZXN0b25lX29yZGVyCikKCnBfbXNfc3RhY2sgPC0gZ2dwbG90KG1zX3Blcl9saW5lLAogICAgICAgICAgICAgICAgICAgICAgYWVzKHg9Y2VsbF9saW5lLCB5PXBjdCwgZmlsbD1taWxlc3RvbmVfcmVmKSkgKwogIGdlb21fY29sKHdpZHRoPS43NSwgY29sb3VyPSJ3aGl0ZSIsIGxpbmV3aWR0aD0uMykgKwogIGdlb21fdGV4dChhZXMobGFiZWw9aWZlbHNlKHBjdCA+PSA1LCBwYXN0ZTAobWlsZXN0b25lX3JlZiwiXG4iLHBjdCwiJSIpLCAiIikpLAogICAgICAgICAgICBwb3NpdGlvbj1wb3NpdGlvbl9zdGFjayh2anVzdD0wLjUpLAogICAgICAgICAgICBzaXplPTIuOCwgY29sb3VyPSJ3aGl0ZSIsIGZvbnRmYWNlPSJib2xkIikgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1tc19zdGF0ZV9jb2xvcnMsCiAgICAgICAgICAgICAgICAgICAgbGFiZWxzPW1pbGVzdG9uZV9sYWJlbHMsCiAgICAgICAgICAgICAgICAgICAgbmFtZT0iTWlsZXN0b25lIikgKwogIHNjYWxlX3lfY29udGludW91cyhleHBhbmQ9YygwLDApLCBsaW1pdHM9YygwLDEwNSkpICsKICB0aGVtZV9jbGFzc2ljKCkgKwogIGxhYnMoeD0iQ2VsbCBsaW5lIiwgeT0iJSBvZiBjZWxscyIsCiAgICAgICB0aXRsZT0iTWlsZXN0b25lIGNvbXBvc2l0aW9uIHBlciBjZWxsIGxpbmUiLAogICAgICAgc3VidGl0bGU9IkNvbG91ciA9IENENCBzdGF0ZSB8IExhYmVsID0gbWlsZXN0b25lIElEIGFuZCAlIHwgc2hvd24gaWYg4omlNSUiKSArCiAgdGhlbWUocGxvdC50aXRsZSAgPSBlbGVtZW50X3RleHQoc2l6ZT0xMywgZmFjZT0iYm9sZCIpLAogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemU9MTIsIGZhY2U9ImJvbGQiKSkKCnByaW50KHBfbXNfc3RhY2spCmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIFZpc3VhbGlzYXRpb24gNSDigJQgSW5kaXZpZHVhbCBDZWxsIExpbmUgVU1BUCBQYW5lbHMKCmBgYHtyIGluZGl2aWR1YWwtbGluZS1wbG90cywgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTR9CiMg4pSA4pSAIEJJT0xPR0lDQUwgTk9URSDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKIyBUaGlzIHByb2R1Y2VzIGEgZGV0YWlsZWQgcGVyLWNlbGwtbGluZSBwbG90IHNob3dpbmc6CiMgLSBUb3AgcGFuZWw6IGNlbGxzIGNvbG91cmVkIGJ5IHByZWRpY3RlZCBzdGF0ZSAod2hhdCBzdGF0ZSB0aGV5IHJlc2VtYmxlKQojIC0gQm90dG9tIHBhbmVsOiBjZWxscyBjb2xvdXJlZCBieSBNU1QgcHNldWRvdGltZSAoaG93IG1hdHVyZSB0aGV5IGFyZSkKIwojIEZvciBlYWNoIGNlbGwgbGluZSwgeW91IHdhbnQgdG8gYXNrOgojIDEuIElzIHRoZSBjZWxsIGxpbmUgSE9NT0dFTkVPVVMgKGFsbCBjZWxscyBpbiBvbmUgc3RhdGUvcmVnaW9uKSBvcgojICAgIEhFVEVST0dFTkVPVVMgKHNjYXR0ZXJlZCBhY3Jvc3MgbXVsdGlwbGUgc3RhdGVzKT8KIyAyLiBEb2VzIHRoZSBjZWxsIGxpbmUgc2l0IGNsb3NlciB0byB0aGUgcm9vdCAobG93IHBzZXVkb3RpbWUgPSBuYWl2ZS1saWtlKQojICAgIG9yIHRoZSB0aXBzIChoaWdoIHBzZXVkb3RpbWUgPSBtYXR1cmUgZWZmZWN0b3Igb3IgVHJlZy1saWtlKT8KIyAzLiBBcmUgdGhlcmUgVFdPIHN1YnBvcHVsYXRpb25zIHdpdGhpbiBvbmUgbGluZT8gVGhpcyBjb3VsZCBpbmRpY2F0ZQojICAgIGNsb25hbCBldm9sdXRpb24gb3IgdGVjaG5pY2FsIG1peGluZy4KIyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKCnBsb3RfbGlzdCA8LSBsaXN0KCkKCmZvciAoY2wgaW4gQ0VMTF9MSU5FX09SREVSKSB7CiAgZGZfY2wgPC0gc2V6X3Bsb3RfZGYgJT4lIGZpbHRlcihjZWxsX2xpbmUgPT0gY2wpCiAgbl9jbCAgPC0gbnJvdyhkZl9jbCkKCiAgIyBTdGF0ZSBwbG90IGZvciB0aGlzIGNlbGwgbGluZQogIHBfc3RhdGUgPC0gZ2dwbG90KCkgKwogICAgZ2VvbV9wb2ludChkYXRhPXJlZl9iZ1tzYW1wbGUobnJvdyhyZWZfYmcpLCBtaW4oMzAwMCwgbnJvdyhyZWZfYmcpKSksXSwKICAgICAgICAgICAgICAgYWVzKHg9VU1BUF8xLCB5PVVNQVBfMiksIGNvbG91cj0iZ3JleTYwIiwgc2l6ZT0uMiwgYWxwaGE9LjQpICsKICAgIGdlb21fc2VnbWVudChkYXRhPW1zdF9lZGdlX2RmLCBhZXMoeD14MSx5PXkxLHhlbmQ9eDIseWVuZD15MiksCiAgICAgICAgICAgICAgICAgY29sb3VyPSJncmV5NDAiLCBsaW5ld2lkdGg9LjYsIGFscGhhPS42KSArCiAgICBnZW9tX3BvaW50KGRhdGE9ZGZfY2wsCiAgICAgICAgICAgICAgIGFlcyh4PVVNQVBfMSwgeT1VTUFQXzIsIGNvbG91cj1wcmVkaWN0ZWRfc3RhdGUpLAogICAgICAgICAgICAgICBzaXplPTEuMiwgYWxwaGE9Ljg1KSArCiAgICBnZW9tX3RleHRfcmVwZWwoZGF0YT1taWxlc3RvbmVfY2VudHJvaWRzLAogICAgICAgICAgICAgICAgICAgIGFlcyh4PVVNQVBfMSwgeT1VTUFQXzIsIGxhYmVsPW1pbGVzdG9uZSksCiAgICAgICAgICAgICAgICAgICAgc2l6ZT0yLCBjb2xvdXI9ImdyZXkyMCIsIGZvbnRmYWNlPSJib2xkIiwKICAgICAgICAgICAgICAgICAgICBtYXgub3ZlcmxhcHM9OCwgc2VnbWVudC5zaXplPS4yKSArCiAgICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcz1TVEFURV9DT0xPUlMsIG5hbWU9IlN0YXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgZHJvcD1GQUxTRSwgbmEudmFsdWU9ImdyZXk0MCIpICsKICAgIHRoZW1lX2NsYXNzaWMoKSArCiAgICBsYWJzKHg9IlVNQVAtMSIsIHk9IlVNQVAtMiIsCiAgICAgICAgIHRpdGxlPXNwcmludGYoIiVzIOKAlCBQcmVkaWN0ZWQgc3RhdGUgKG49JXMgY2VsbHMpIiwKICAgICAgICAgICAgICAgICAgICAgICBjbCwgc2NhbGVzOjpjb21tYShuX2NsKSkpICsKICAgIHRoZW1lKHBsb3QudGl0bGUgID0gZWxlbWVudF90ZXh0KHNpemU9MTAsIGZhY2U9ImJvbGQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3VyPUNFTExfTElORV9DT0xPUlNbY2xdKSwKICAgICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9NyksCiAgICAgICAgICBsZWdlbmQudGl0bGU9IGVsZW1lbnRfdGV4dChzaXplPTgpKQoKICAjIFBzZXVkb3RpbWUgcGxvdCBmb3IgdGhpcyBjZWxsIGxpbmUKICBwX3B0IDwtIGdncGxvdCgpICsKICAgIGdlb21fcG9pbnQoZGF0YT1yZWZfYmdbc2FtcGxlKG5yb3cocmVmX2JnKSwgbWluKDMwMDAsIG5yb3cocmVmX2JnKSkpLF0sCiAgICAgICAgICAgICAgIGFlcyh4PVVNQVBfMSwgeT1VTUFQXzIpLCBjb2xvdXI9ImdyZXk2MCIsIHNpemU9LjIsIGFscGhhPS40KSArCiAgICBnZW9tX3NlZ21lbnQoZGF0YT1tc3RfZWRnZV9kZiwgYWVzKHg9eDEseT15MSx4ZW5kPXgyLHllbmQ9eTIpLAogICAgICAgICAgICAgICAgIGNvbG91cj0iZ3JleTQwIiwgbGluZXdpZHRoPS42LCBhbHBoYT0uNikgKwogICAgZ2VvbV9wb2ludChkYXRhPWRmX2NsLAogICAgICAgICAgICAgICBhZXMoeD1VTUFQXzEsIHk9VU1BUF8yLCBjb2xvdXI9bXN0X3B0KSwKICAgICAgICAgICAgICAgc2l6ZT0xLjIsIGFscGhhPS45KSArCiAgICBnZW9tX3RleHRfcmVwZWwoZGF0YT1taWxlc3RvbmVfY2VudHJvaWRzLAogICAgICAgICAgICAgICAgICAgIGFlcyh4PVVNQVBfMSwgeT1VTUFQXzIsIGxhYmVsPW1pbGVzdG9uZSksCiAgICAgICAgICAgICAgICAgICAgc2l6ZT0yLCBjb2xvdXI9ImdyZXk0MCIsIGZvbnRmYWNlPSJib2xkIiwKICAgICAgICAgICAgICAgICAgICBtYXgub3ZlcmxhcHM9OCwgc2VnbWVudC5zaXplPS4yKSArCiAgICBzY2FsZV9jb2xvdXJfZ3JhZGllbnRuKAogICAgICBjb2xvdXJzID0gYygiIzBEMDg4NyIsIiM2QTAwQTgiLCIjQjEyQTkwIiwiI0UxNjQ2MiIsIiNGQ0E2MzYiLCIjRjBGOTIxIiksCiAgICAgIG5hbWU9IlBzZXVkb3RpbWVcbigw4oCTMTAwKSIsIGxpbWl0cz1jKDAsMTAwKSkgKwogICAgdGhlbWVfY2xhc3NpYygpICsKICAgIGxhYnMoeD0iVU1BUC0xIiwgeT0iVU1BUC0yIiwKICAgICAgICAgdGl0bGU9c3ByaW50ZigiJXMg4oCUIE1TVCBwc2V1ZG90aW1lIiwgY2wpKSArCiAgICB0aGVtZShwbG90LnRpdGxlICA9IGVsZW1lbnRfdGV4dChzaXplPTEwLCBmYWNlPSJib2xkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG91cj1DRUxMX0xJTkVfQ09MT1JTW2NsXSksCiAgICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTcpLAogICAgICAgICAgbGVnZW5kLnRpdGxlPSBlbGVtZW50X3RleHQoc2l6ZT04KSkKCiAgcGxvdF9saXN0W1twYXN0ZTAoY2wsIl9zdGF0ZSIpXV0gPC0gcF9zdGF0ZQogIHBsb3RfbGlzdFtbcGFzdGUwKGNsLCJfcHQiKV1dICAgIDwtIHBfcHQKfQoKIyBQcmludCBpbiAyLWNvbHVtbiBsYXlvdXQgKHN0YXRlIHwgcHNldWRvdGltZSkgcGVyIGxpbmUKZm9yIChjbCBpbiBDRUxMX0xJTkVfT1JERVIpIHsKICBwcmludChwbG90X2xpc3RbW3Bhc3RlMChjbCwiX3N0YXRlIildXSB8IHBsb3RfbGlzdFtbcGFzdGUwKGNsLCJfcHQiKV1dKQp9CmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIFZpc3VhbGlzYXRpb24gNiDigJQgTWV0aG9kIEFncmVlbWVudCBwZXIgQ2VsbCBMaW5lCgpgYGB7ciBtZXRob2QtYWdyZWVtZW50LWxpbmVzLCBmaWcud2lkdGg9MTQsIGZpZy5oZWlnaHQ9Nn0KIyDilIDilIAgQklPTE9HSUNBTCBOT1RFIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgAojIFdlIGNvbXBhcmUgTVNUIGFuZCBNb25vY2xlMyBwc2V1ZG90aW1lIFBFUiBDRUxMIExJTkUgdG8gYXNzZXNzIHJvYnVzdG5lc3MuCiMgSWYgYm90aCBtZXRob2RzIGFncmVlIG9uIGEgY2VsbCBsaW5lJ3MgcHNldWRvdGltZSBwb3NpdGlvbiwgdGhlIHJlc3VsdCBpcwojIHJvYnVzdC4gSWYgdGhleSBkaXNhZ3JlZSBmb3IgYSBzcGVjaWZpYyBsaW5lLCB0aGF0IGxpbmUgbWF5IHNpdCBpbiBhCiMgdHJhamVjdG9yeSByZWdpb24gd2hlcmUgdGhlIHR3byBtZXRob2RzIGhhbmRsZSBnZW9tZXRyeSBkaWZmZXJlbnRseQojIChlLmcuIG5lYXIgdGhlIGJyYW5jaCBwb2ludCBNMDIpLgojIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgAoKIyBQZXItbGluZSBTcGVhcm1hbiBjb3JyZWxhdGlvbgpsaW5lX2NvcnMgPC0gc2V6X3Bsb3RfZGYgJT4lCiAgZ3JvdXBfYnkoY2VsbF9saW5lKSAlPiUKICBzdW1tYXJpc2UoCiAgICBzcGVhcm1hbiA9IHJvdW5kKGNvcihtc3RfcHQsIG0zX3B0LCBtZXRob2Q9InNwZWFybWFuIiwKICAgICAgICAgICAgICAgICAgICAgICAgIHVzZT0iY29tcGxldGUub2JzIiksIDMpLAogICAgbiAgICAgICAgPSBuKCksCiAgICAuZ3JvdXBzICA9ICJkcm9wIgogICkKCmNhdCgiTWV0aG9kIGFncmVlbWVudCAoU3BlYXJtYW4gz4EpIHBlciBjZWxsIGxpbmU6XG4iKQpwcmludChsaW5lX2NvcnMpCgpwX21ldGhvZF9zY2F0dGVyIDwtIGdncGxvdChzZXpfcGxvdF9kZiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFlcyh4PW1zdF9wdCwgeT1tM19wdCwgY29sb3VyPWNlbGxfbGluZSkpICsKICBnZW9tX3BvaW50KHNpemU9LjUsIGFscGhhPS40KSArCiAgZ2VvbV9hYmxpbmUoc2xvcGU9MSwgaW50ZXJjZXB0PTAsIGxpbmV0eXBlPSJkYXNoZWQiLAogICAgICAgICAgICAgIGNvbG91cj0iZ3JleTQwIiwgbGluZXdpZHRoPS43KSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsbSIsIHNlPUZBTFNFLCBsaW5ld2lkdGg9LjgpICsKICBnZW9tX3RleHQoZGF0YT1saW5lX2NvcnMsCiAgICAgICAgICAgIGFlcyh4PTUsIHk9OTUsIGxhYmVsPXNwcmludGYoIs+BPSUuMmYiLCBzcGVhcm1hbikpLAogICAgICAgICAgICBzaXplPTMsIGZvbnRmYWNlPSJib2xkIiwgaGp1c3Q9MCwgY29sb3VyPSJncmV5MjAiKSArCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXM9Q0VMTF9MSU5FX0NPTE9SUywgZ3VpZGU9Im5vbmUiKSArCiAgZmFjZXRfd3JhcCh+Y2VsbF9saW5lLCBuY29sPTQpICsKICB0aGVtZV9jbGFzc2ljKCkgKwogIGxhYnMoeD0iQ3VzdG9tIE1TVCBwc2V1ZG90aW1lICgw4oCTMTAwKSIsCiAgICAgICB5PSJNb25vY2xlMyBwc2V1ZG90aW1lICgw4oCTMTAwKSIsCiAgICAgICB0aXRsZT0iTWV0aG9kIGFncmVlbWVudCBwZXIgY2VsbCBsaW5lOiBDdXN0b20gTVNUIHZzIE1vbm9jbGUzIHBzZXVkb3RpbWUiLAogICAgICAgc3VidGl0bGU9IkRhc2hlZCA9IHBlcmZlY3QgYWdyZWVtZW50IHwgz4EgPSBTcGVhcm1hbiBjb3JyZWxhdGlvbiB8IEhpZ2hlciDPgSA9IG1vcmUgcmVsaWFibGUgcHNldWRvdGltZSBhc3NpZ25tZW50IikgKwogIHRoZW1lKHBsb3QudGl0bGUgID0gZWxlbWVudF90ZXh0KHNpemU9MTIsIGZhY2U9ImJvbGQiKSwKICAgICAgICBzdHJpcC50ZXh0ICA9IGVsZW1lbnRfdGV4dChzaXplPTEwLCBmYWNlPSJib2xkIiksCiAgICAgICAgc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsPSJncmV5OTUiKSkKCnByaW50KHBfbWV0aG9kX3NjYXR0ZXIpCmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIFN1bW1hcnkgVGFibGUg4oCUIENvbXBsZXRlIENlbGwgTGluZSBBbmFseXNpcwoKYGBge3Igc3VtbWFyeS10YWJsZX0KIyDilIDilIAgQklPTE9HSUNBTCBOT1RFIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgAojIFRoaXMgY29tcHJlaGVuc2l2ZSB0YWJsZSBpcyB5b3VyIHByaW1hcnkgcmVzdWx0IHRhYmxlIGZvciB0aGUgcGFwZXIuCiMgS2V5IGNvbHVtbnMgdG8gaW50ZXJwcmV0OgojCiMgZG9taW5hbnRfc3RhdGU6IHRoZSBtb3N0IGNvbW1vbiBwcmVkaWN0ZWQgQ0Q0IHN0YXRlIGZvciB0aGF0IGNlbGwgbGluZQojIGRvbWluYW50X3N0YXRlX3BjdDogaG93IGNvbmZpZGVudCDigJQgaWYgPjgwJSwgc3Ryb25nIGNlbGwgb2Ygb3JpZ2luIHNpZ25hbAojIG1lYW5fcHJlZF9zY29yZTogTWFwUXVlcnkgY29uZmlkZW5jZSDigJQgPjAuNyBpcyByZWxpYWJsZQojIG1zdF9tZWFuX3B0OiBhdmVyYWdlIGRpZmZlcmVudGlhdGlvbiBtYXR1cml0eSAoMD1uYWl2ZSwgMTAwPXRlcm1pbmFsKQojIGRvbWluYW50X21pbGVzdG9uZTogdGhlIHNpbmdsZSBtb3N0IGNvbW1vbiB0cmFqZWN0b3J5IHBvc2l0aW9uCiMKIyBGb3IgdGhlIHBhcGVyLCB5b3Ugd2lsbCB3YW50IHRvIHN0YXRlIHNvbWV0aGluZyBsaWtlOgojICJYIG9mIDcgY2VsbCBsaW5lcyBwcmVkb21pbmFudGx5IG1hcHBlZCB0byBDRDQgVENNIChtaWxlc3RvbmVzIE0wMS1NMDIpLAojIHdpdGggbWVhbiBNU1QgcHNldWRvdGltZSBvZiBZIMKxIFosIGluZGljYXRpbmcgdGhhdCBTw6l6YXJ5IHN5bmRyb21lCiMgY2VsbCBsaW5lcyB0cmFuc2NyaXB0b21pY2FsbHkgcmVzZW1ibGUgY2VudHJhbCBtZW1vcnkgQ0Q0IFQgY2VsbHMgYXQKIyBhbiBpbnRlcm1lZGlhdGUgc3RhZ2Ugb2YgZGlmZmVyZW50aWF0aW9uLiIKIyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKCnNlel9zdW1tYXJ5IDwtIHNlel9wbG90X2RmICU+JQogIGdyb3VwX2J5KGNlbGxfbGluZSkgJT4lCiAgc3VtbWFyaXNlKAogICAgbl9jZWxscyAgICAgICAgICAgID0gbigpLAogICAgbWVhbl9wcmVkX3Njb3JlICAgID0gcm91bmQobWVhbihwcmVkX3Njb3JlLCBuYS5ybT1UUlVFKSwgMyksCiAgICBoaWdoX2NvbmZfcGN0ICAgICAgPSByb3VuZCgxMDAgKiBtZWFuKHByZWRfc2NvcmUgPiAwLjcsIG5hLnJtPVRSVUUpLCAxKSwKICAgIGRvbWluYW50X3N0YXRlICAgICA9IG5hbWVzKHNvcnQodGFibGUocHJlZGljdGVkX3N0YXRlKSwgZGVjcmVhc2luZz1UUlVFKSlbMV0sCiAgICBkb21pbmFudF9zdGF0ZV9wY3QgPSByb3VuZCgxMDAgKiBtYXgodGFibGUocHJlZGljdGVkX3N0YXRlKSkgLyBuKCksIDEpLAogICAgZG9taW5hbnRfbWlsZXN0b25lID0gbmFtZXMoc29ydCh0YWJsZShtaWxlc3RvbmVfcmVmKSwgZGVjcmVhc2luZz1UUlVFKSlbMV0sCiAgICBtc3RfbWVhbl9wdCAgICAgICAgPSByb3VuZChtZWFuKG1zdF9wdCwgbmEucm09VFJVRSksIDEpLAogICAgbXN0X3NkX3B0ICAgICAgICAgID0gcm91bmQoc2QobXN0X3B0LCBuYS5ybT1UUlVFKSwgMSksCiAgICBtc3RfbWVkaWFuX3B0ICAgICAgPSByb3VuZChtZWRpYW4obXN0X3B0LCBuYS5ybT1UUlVFKSwgMSksCiAgICBtM19tZWFuX3B0ICAgICAgICAgPSByb3VuZChtZWFuKG0zX3B0LCBuYS5ybT1UUlVFKSwgMSksCiAgICBtM19zZF9wdCAgICAgICAgICAgPSByb3VuZChzZChtM19wdCwgbmEucm09VFJVRSksIDEpLAogICAgLmdyb3VwcyAgICAgICAgICAgID0gImRyb3AiCiAgKSAlPiUKICBhcnJhbmdlKG1zdF9tZWFuX3B0KQoKa2FibGUoc2V6X3N1bW1hcnksCiAgICAgIGNvbC5uYW1lcyA9IGMoIkNlbGwgbGluZSIsIk4gY2VsbHMiLCJNZWFuIHByZWQuXG5zY29yZSIsCiAgICAgICAgICAgICAgICAgICAgIiUgc2NvcmVcbj4wLjciLAogICAgICAgICAgICAgICAgICAgICJEb21pbmFudFxuc3RhdGUiLCJEb21pbmFudFxuc3RhdGUgJSIsCiAgICAgICAgICAgICAgICAgICAgIkRvbWluYW50XG5taWxlc3RvbmUiLAogICAgICAgICAgICAgICAgICAgICJNU1QgbWVhblxucHQiLCJNU1QgU0QiLAogICAgICAgICAgICAgICAgICAgICJNU1QgbWVkaWFuXG5wdCIsCiAgICAgICAgICAgICAgICAgICAgIk0zIG1lYW5cbnB0IiwiTTMgU0QiKSwKICAgICAgY2FwdGlvbiA9ICJDb21wbGV0ZSBwZXIgY2VsbCBsaW5lIHRyYWplY3Rvcnkgc3VtbWFyeSDigJQgY2VsbCBvZiBvcmlnaW4gYW5hbHlzaXMiKSAlPiUKICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zPWMoInN0cmlwZWQiLCJob3ZlciIsImNvbmRlbnNlZCIpLAogICAgICAgICAgICAgICAgZnVsbF93aWR0aD1UUlVFKSAlPiUKICBjb2x1bW5fc3BlYyg1LCBib2xkPVRSVUUpICU+JQogIGNvbHVtbl9zcGVjKDYsIGJvbGQ9VFJVRSkgJT4lCiAgY29sdW1uX3NwZWMoODoxMCwgY29sb3I9IndoaXRlIiwgYmFja2dyb3VuZD1NRVRIT0RfQ09MT1JTWyJDdXN0b20gTVNUIl0pICU+JQogIGNvbHVtbl9zcGVjKDExOjEyLCBjb2xvcj0id2hpdGUiLCBiYWNrZ3JvdW5kPU1FVEhPRF9DT0xPUlNbIk1vbm9jbGUzIl0pCgojIOKUgOKUgCBQZXItbGluZSBtaWxlc3RvbmUgZGlzdHJpYnV0aW9uIHRhYmxlIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgAptc19kaXN0X2FsbCA8LSBzZXpfcGxvdF9kZiAlPiUKICBmaWx0ZXIoIWlzLm5hKG1pbGVzdG9uZV9yZWYpKSAlPiUKICBjb3VudChjZWxsX2xpbmUsIG1pbGVzdG9uZV9yZWYpICU+JQogIGxlZnRfam9pbihtaWxlc3RvbmVfY2VudHJvaWRzWyxjKCJtaWxlc3RvbmUiLCJzdGF0ZSIsIm9yZGVyX3BvcyIpXSwKICAgICAgICAgICAgYnk9YygibWlsZXN0b25lX3JlZiI9Im1pbGVzdG9uZSIpKSAlPiUKICBmaWx0ZXIoIWlzLm5hKG9yZGVyX3BvcykpICU+JQogIGdyb3VwX2J5KGNlbGxfbGluZSkgJT4lCiAgbXV0YXRlKHBjdCA9IHJvdW5kKDEwMCAqIG4gLyBzdW0obiksIDEpKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgbXV0YXRlKG1pbGVzdG9uZV9yZWYgPSBmYWN0b3IobWlsZXN0b25lX3JlZiwgbGV2ZWxzPW1pbGVzdG9uZV9vcmRlcikpICU+JQogIGFycmFuZ2UoY2VsbF9saW5lLCBvcmRlcl9wb3MpCgpjYXQoIlxuXG5NaWxlc3RvbmUgZGlzdHJpYnV0aW9uIHBlciBjZWxsIGxpbmU6XG4iKQptc19kaXN0X2FsbCAlPiUKICBzZWxlY3QoY2VsbF9saW5lLCBtaWxlc3RvbmVfcmVmLCBzdGF0ZSwgbiwgcGN0KSAlPiUKICBrYWJsZShjb2wubmFtZXM9YygiQ2VsbCBsaW5lIiwiTWlsZXN0b25lIiwiU3RhdGUiLCJOIGNlbGxzIiwiJSBjZWxscyIpLAogICAgICAgIGNhcHRpb249IlBlciBjZWxsIGxpbmUgbWlsZXN0b25lIGRpc3RyaWJ1dGlvbiIpICU+JQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnM9Yygic3RyaXBlZCIsImNvbmRlbnNlZCIpLCBmdWxsX3dpZHRoPUZBTFNFKSAlPiUKICBwcmludCgpCmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIFByZWRpY3Rpb24gU2NvcmUgUXVhbGl0eSBDaGVjawoKYGBge3IgcHJlZC1zY29yZS1xdWFsaXR5LCBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD01fQojIOKUgOKUgCBCSU9MT0dJQ0FMIE5PVEUg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACiMgVGhlIHByZWRpY3Rpb24gc2NvcmUgKDAtMSkgdGVsbHMgeW91IGhvdyBjb25maWRlbnRseSBNYXBRdWVyeSBtYXRjaGVkCiMgZWFjaCBTw6l6YXJ5IGNlbGwgdG8gYSByZWZlcmVuY2UgQ0Q0IHN0YXRlLiBUaGlzIGlzIGNyaXRpY2FsIGZvcgojIGludGVycHJldGluZyB5b3VyIHJlc3VsdHM6CiMKIyBTY29yZSA+MC43OiBIaWdoIGNvbmZpZGVuY2Ug4oCUIHRoZSBjZWxsIGNsZWFybHkgcmVzZW1ibGVzIG9uZSBDRDQgc3RhdGUKIyBTY29yZSAwLjUtMC43OiBNb2RlcmF0ZSBjb25maWRlbmNlIOKAlCBjZWxsIGhhcyBtaXhlZCBmZWF0dXJlcwojIFNjb3JlIDwwLjU6IExvdyBjb25maWRlbmNlIOKAlCBjZWxsIGRvZXNuJ3QgY2xlYXJseSBtYXRjaCBhbnkgcmVmZXJlbmNlIHN0YXRlCiMKIyBMT1cgU0NPUkVTIEFSRSBCSU9MT0dJQ0FMTFkgSU5URVJFU1RJTkcgaW4gY2FuY2VyOgojIE1hbGlnbmFudCBjZWxscyBvZnRlbiBoYXZlIGFiZXJyYW50IHRyYW5zY3JpcHRvbWVzIHRoYXQgZG9uJ3QgcGVyZmVjdGx5CiMgbWF0Y2ggYW55IG5vcm1hbCBjZWxsIHR5cGUuIExvdyBzY29yZXMgY291bGQgaW5kaWNhdGU6CiMgMS4gVGhlIGNlbGwgaGFzIGFjcXVpcmVkIG9uY29nZW5pYyBnZW5lIGV4cHJlc3Npb24gbm90IHNlZW4gaW4gbm9ybWFsIFQgY2VsbHMKIyAyLiBUaGUgY2VsbCBzcGFucyB0d28gbm9ybWFsIHN0YXRlcyAoZS5nLiBtaXhlZCBUQ00vVEVNIGZlYXR1cmVzKQojIDMuIFRoZSBjZWxsIGhhcyBkb3ducmVndWxhdGVkIG5vcm1hbCBUIGNlbGwgcHJvZ3JhbW1lcyBlbnRpcmVseQojCiMgWW91IHNob3VsZCByZXBvcnQgdGhlIG1lYW4gcHJlZGljdGlvbiBzY29yZSBpbiB5b3VyIG1ldGhvZHMvcmVzdWx0cy4KIyBJZiBzY29yZXMgYXJlIGdlbmVyYWxseSBoaWdoICg+MC43KSwgeW91ciBjb25jbHVzaW9uIGFib3V0IGNlbGwgb2Ygb3JpZ2luCiMgaXMgb24gc29saWQgZ3JvdW5kLiBJZiBzY29yZXMgYXJlIGxvdyAoPDAuNSksIGludGVycHJldCB3aXRoIGNhdXRpb24uCiMg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACgpwX3Njb3JlcyA8LSBnZ3Bsb3Qoc2V6X3Bsb3RfZGYsCiAgICAgICAgICAgICAgICAgICAgYWVzKHg9Y2VsbF9saW5lLCB5PXByZWRfc2NvcmUsIGZpbGw9Y2VsbF9saW5lKSkgKwogIGdlb21fdmlvbGluKHNjYWxlPSJ3aWR0aCIsIHRyaW09RkFMU0UsIGFscGhhPS44NSwgY29sb3VyPSJ3aGl0ZSIpICsKICBnZW9tX2JveHBsb3Qod2lkdGg9LjA4LCBmaWxsPSJ3aGl0ZSIsIG91dGxpZXIuc2l6ZT0uNSwKICAgICAgICAgICAgICAgb3V0bGllci5hbHBoYT0uMywgY29sb3VyPSJncmV5MzAiKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0PTAuNywgbGluZXR5cGU9ImRhc2hlZCIsCiAgICAgICAgICAgICBjb2xvdXI9ImRhcmtncmVlbiIsIGxpbmV3aWR0aD0uOCkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdD0wLjUsIGxpbmV0eXBlPSJkYXNoZWQiLAogICAgICAgICAgICAgY29sb3VyPSJvcmFuZ2UiLCBsaW5ld2lkdGg9LjgpICsKICBhbm5vdGF0ZSgidGV4dCIsIHg9Ny41LCB5PTAuNzIsIGxhYmVsPSJIaWdoIGNvbmZpZGVuY2UgKDAuNykiLAogICAgICAgICAgIHNpemU9MywgY29sb3VyPSJkYXJrZ3JlZW4iLCBoanVzdD0xLCBmb250ZmFjZT0iaXRhbGljIikgKwogIGFubm90YXRlKCJ0ZXh0IiwgeD03LjUsIHk9MC41MiwgbGFiZWw9Ik1vZGVyYXRlIGNvbmZpZGVuY2UgKDAuNSkiLAogICAgICAgICAgIHNpemU9MywgY29sb3VyPSJvcmFuZ2UiLCBoanVzdD0xLCBmb250ZmFjZT0iaXRhbGljIikgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1DRUxMX0xJTkVfQ09MT1JTLCBndWlkZT0ibm9uZSIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzPWMoMCwxKSwgYnJlYWtzPXNlcSgwLDEsLjIpKSArCiAgdGhlbWVfY2xhc3NpYygpICsKICBsYWJzKHg9IkNlbGwgbGluZSIsIHk9Ik1hcFF1ZXJ5IHByZWRpY3Rpb24gc2NvcmUgKDDigJMxKSIsCiAgICAgICB0aXRsZT0iUHJvamVjdGlvbiBjb25maWRlbmNlIHBlciBTw6l6YXJ5IGNlbGwgbGluZSIsCiAgICAgICBzdWJ0aXRsZT0iSGlnaGVyIHNjb3JlID0gY2VsbCBtb3JlIGNsZWFybHkgcmVzZW1ibGVzIG9uZSBzcGVjaWZpYyBDRDQgVCBjZWxsIHN0YXRlIikgKwogIHRoZW1lKHBsb3QudGl0bGUgID0gZWxlbWVudF90ZXh0KHNpemU9MTIsIGZhY2U9ImJvbGQiKSwKICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplPTExLCBmYWNlPSJib2xkIikpCgpwcmludChwX3Njb3JlcykKYGBgCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgU2F2ZSBBbGwgT3V0cHV0cwoKYGBge3Igc2F2ZS1hbGx9CiMgQ3JlYXRlIG91dHB1dCBkaXJlY3RvcmllcyBpZiB0aGV5IGRvbid0IGV4aXN0CmRpci5jcmVhdGUoIk9iamVjdHMiLCBzaG93V2FybmluZ3M9RkFMU0UpCmRpci5jcmVhdGUoIkZpZ3VyZXMiLCBzaG93V2FybmluZ3M9RkFMU0UpCmRpci5jcmVhdGUoIlRhYmxlcyIsICBzaG93V2FybmluZ3M9RkFMU0UpCgojIOKUgOKUgCBSRFMgb2JqZWN0cyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKc2F2ZVJEUyhzZXphcnlfb2JqLCAgICAgICAgICAiT2JqZWN0cy9zZXphcnlfY2VsbGxpbmVzX3Byb2plY3RlZC5yZHMiKQpzYXZlUkRTKG1zdF9ncmFwaCwgICAgICAgICAgICJPYmplY3RzL21zdF9ncmFwaC5yZHMiKQpzYXZlUkRTKG1pbGVzdG9uZV9jZW50cm9pZHMsICJPYmplY3RzL21pbGVzdG9uZV9jZW50cm9pZHMucmRzIikKCiMg4pSA4pSAIENTViB0YWJsZXMg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACndyaXRlLmNzdihzZXpfc3VtbWFyeSwgICAgIlRhYmxlcy9jZWxsbGluZV90cmFqZWN0b3J5X3N1bW1hcnkuY3N2IiwgICByb3cubmFtZXM9RkFMU0UpCndyaXRlLmNzdihzdGF0ZV9wZXJfbGluZSwgIlRhYmxlcy9jZWxsbGluZV9zdGF0ZV9wcm9wb3J0aW9ucy5jc3YiLCAgICByb3cubmFtZXM9RkFMU0UpCndyaXRlLmNzdihtc19kaXN0X2FsbCwgICAgIlRhYmxlcy9jZWxsbGluZV9taWxlc3RvbmVfZGlzdHJpYnV0aW9uLmNzdiIscm93Lm5hbWVzPUZBTFNFKQp3cml0ZS5jc3YobGluZV9jb3JzLCAgICAgICJUYWJsZXMvY2VsbGxpbmVfbWV0aG9kX2FncmVlbWVudC5jc3YiLCAgICAgcm93Lm5hbWVzPUZBTFNFKQp3cml0ZS5jc3Yoc2V6X3Bsb3RfZGYsICAgICJUYWJsZXMvY2VsbGxpbmVfYWxsX2NlbGxzX21ldGFkYXRhLmNzdiIsICAgcm93Lm5hbWVzPUZBTFNFKQoKIyDilIDilIAgRmlndXJlcyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKZ2dzYXZlKCJGaWd1cmVzL2ZpZ19hbGxfbGluZXNfdW1hcC5wZGYiLCAgICAgIHBfYWxsX2xpbmVzLCAgICB3aWR0aD0xNCwgaGVpZ2h0PTEyKQpnZ3NhdmUoIkZpZ3VyZXMvZmlnX2ZhY2V0ZWRfbGluZXMucGRmIiwgICAgICAgcF9mYWNldGVkLCAgICAgIHdpZHRoPTE4LCBoZWlnaHQ9MTApCmdnc2F2ZSgiRmlndXJlcy9maWdfc3RhdGVfcGVyX2xpbmUucGRmIiwgICAgICBwX3N0YXRlX3N0YWNrLCAgd2lkdGg9MTQsIGhlaWdodD02KQpnZ3NhdmUoIkZpZ3VyZXMvZmlnX3BzZXVkb3RpbWVfbXN0LnBkZiIsICAgICAgcF92aW9saW5fbXN0LCAgIHdpZHRoPTE0LCBoZWlnaHQ9NikKZ2dzYXZlKCJGaWd1cmVzL2ZpZ19wc2V1ZG90aW1lX20zLnBkZiIsICAgICAgIHBfdmlvbGluX20zLCAgICB3aWR0aD0xNCwgaGVpZ2h0PTYpCmdnc2F2ZSgiRmlndXJlcy9maWdfbWlsZXN0b25lX2hlYXRtYXAucGRmIiwgICBwX21zX2hlYXRtYXAsICAgd2lkdGg9MTQsIGhlaWdodD03KQpnZ3NhdmUoIkZpZ3VyZXMvZmlnX21pbGVzdG9uZV9zdGFjay5wZGYiLCAgICAgcF9tc19zdGFjaywgICAgIHdpZHRoPTE0LCBoZWlnaHQ9NykKZ2dzYXZlKCJGaWd1cmVzL2ZpZ19tZXRob2RfYWdyZWVtZW50LnBkZiIsICAgIHBfbWV0aG9kX3NjYXR0ZXIsd2lkdGg9MTQsIGhlaWdodD02KQpnZ3NhdmUoIkZpZ3VyZXMvZmlnX3ByZWRpY3Rpb25fc2NvcmVzLnBkZiIsICAgcF9zY29yZXMsICAgICAgIHdpZHRoPTEyLCBoZWlnaHQ9NSkKCiMgSW5kaXZpZHVhbCBsaW5lIHBsb3RzCmZvciAoY2wgaW4gQ0VMTF9MSU5FX09SREVSKSB7CiAgcF9jb21iaW5lZCA8LSBwbG90X2xpc3RbW3Bhc3RlMChjbCwiX3N0YXRlIildXSB8IHBsb3RfbGlzdFtbcGFzdGUwKGNsLCJfcHQiKV1dCiAgZ2dzYXZlKHNwcmludGYoIkZpZ3VyZXMvZmlnXyVzX3VtYXAucGRmIiwgY2wpLAogICAgICAgICBwX2NvbWJpbmVkLCB3aWR0aD0xNiwgaGVpZ2h0PTcpCn0KCmNhdCgi4pyFIEFsbCBvdXRwdXRzIHNhdmVkXG4iKQpjYXQoIiAgT2JqZWN0cy8g4oCUIFJEUyBmaWxlc1xuIikKY2F0KCIgIEZpZ3VyZXMvIOKAlCBQREYgcGxvdHNcbiIpCmNhdCgiICBUYWJsZXMvICDigJQgQ1NWIGRhdGEgZmlsZXNcbiIpCmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIEJpb2xvZ2ljYWwgSW50ZXJwcmV0YXRpb24gR3VpZGUKCmBgYHtyIGludGVycHJldGF0aW9uLWd1aWRlLCBlY2hvPUZBTFNFfQojIFRoaXMgY2h1bmsgcHJpbnRzIGEgZ3VpZGUgZm9yIGludGVycHJldGluZyB5b3VyIHNwZWNpZmljIHJlc3VsdHMKIyBhZnRlciBhbGwgdGhlIGFuYWx5c2VzIGFib3ZlIGhhdmUgcnVuCgpjYXQoIgrilZTilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZcK4pWRICAgICAgICAgQklPTE9HSUNBTCBJTlRFUlBSRVRBVElPTiBHVUlERSBGT1IgWU9VUiBSRVNVTFRTICAgICAgICAgICAg4pWRCuKVmuKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVnQoKU1RFUCAxOiBDSEVDSyBET01JTkFOVCBTVEFURSAoc2V6X3N1bW1hcnkgdGFibGUsIGRvbWluYW50X3N0YXRlIGNvbHVtbikK4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACuKAoiBJZiBtb3N0IGxpbmVzIHNob3cgQ0Q0IFRDTSAoZG9taW5hbnRfc3RhdGVfcGN0ID43MCUpOgogIOKGkiBDT05DTFVTSU9OOiBTw6l6YXJ5IHN5bmRyb21lIGFyaXNlcyBmcm9tIENlbnRyYWwgTWVtb3J5IFQgY2VsbHMKICDihpIgSU1QTElDQVRJT046IFRDTSBzdXJ2aXZhbCBzaWduYWxzIChJTC03LCBJTC0xNSwgQ0NSNy1pbmRlcGVuZGVudAogICAgaG9taW5nKSBkcml2ZSBtYWxpZ25hbnQgY2VsbCBwZXJzaXN0ZW5jZQogIOKGkiBMSVRFUkFUVVJFOiBDb25zaXN0ZW50IHdpdGggV2FuZyBldCBhbC4gMjAyMywgV29vbGxhcmQgZXQgYWwuIDIwMjIKCuKAoiBJZiBtb3N0IGxpbmVzIHNob3cgQ0Q0IFRFTToKICDihpIgQ09OQ0xVU0lPTjogVHJhbnNmb3JtYXRpb24gb2NjdXJyZWQgYXQgdGhlIGVmZmVjdG9yIG1lbW9yeSBzdGFnZQogIOKGkiBJTVBMSUNBVElPTjogTWFsaWduYW50IGNlbGxzIGhhdmUgYWNxdWlyZWQgcGFydGlhbCBlZmZlY3RvcgogICAgcHJvZ3JhbW1lcyAoR1pNSywgQ0NMNSkgYnV0IG5vdCB0ZXJtaW5hbCBjeXRvdG94aWNpdHkKCuKAoiBJZiBsaW5lcyBzcGxpdCBiZXR3ZWVuIFRDTSBhbmQgVEVNOgogIOKGkiBDT05DTFVTSU9OOiBEaXNlYXNlIGhldGVyb2dlbmVpdHkg4oCUIGRpZmZlcmVudCBwYXRpZW50cyBoYXZlCiAgICBkaWZmZXJlbnQgY2VsbHMgb2Ygb3JpZ2luCiAg4oaSIElNUExJQ0FUSU9OOiBNYXkgZXhwbGFpbiBjbGluaWNhbCB2YXJpYWJpbGl0eSBpbiBkcnVnIHJlc3BvbnNlCgpTVEVQIDI6IENIRUNLIERPTUlOQU5UIE1JTEVTVE9ORSAoZG9taW5hbnRfbWlsZXN0b25lIGNvbHVtbikK4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACuKAoiBNMDEgb3IgTTAyIGRvbWluYW50IOKGkiBlYXJseS10by1sYXRlIFRDTSAobW9zdCBsaWtlbHkgZm9yIFNTKQrigKIgTTAyIGRvbWluYW50IChicmFuY2ggcG9pbnQpIOKGkiBhcnJlc3RlZCBhdCBmYXRlIGRlY2lzaW9uIHBvaW50CuKAoiBNMDMgb3IgTTA0IGRvbWluYW50IOKGkiBURU0gY29tbWl0bWVudCBkZXNwaXRlIFRDTS1saWtlIGxhYmVsCuKAoiBNMDYgZG9taW5hbnQg4oaSIFRyZWctbGlrZSBmZWF0dXJlcyAoY2hlY2sgRk9YUDMgZXhwcmVzc2lvbikKClNURVAgMzogQ0hFQ0sgUFJFRElDVElPTiBTQ09SRVMgKG1lYW5fcHJlZF9zY29yZSBjb2x1bW4pCuKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgArigKIgPjAuNzogSGlnaCBjb25maWRlbmNlIOKAlCBzdHJvbmcgY2VsbCBvZiBvcmlnaW4gY29uY2x1c2lvbgrigKIgMC41LTAuNzogTW9kZXJhdGUg4oCUIG1lbnRpb24gaW4gbGltaXRhdGlvbnMK4oCiIDwwLjU6IExvdyDigJQgbWFsaWduYW50IHRyYW5zY3JpcHRvbWUgc3Vic3RhbnRpYWxseSBkZXZpYXRlcyBmcm9tCiAgYWxsIG5vcm1hbCByZWZlcmVuY2Ugc3RhdGVzOyBkaXNjdXNzIGFzICdwYXJ0aWFsIHJlc2VtYmxhbmNlJwoKU1RFUCA0OiBDSEVDSyBQU0VVRE9USU1FIChtc3RfbWVhbl9wdCBjb2x1bW4pCuKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgArigKIgMC0zMzogTmFpdmUvZWFybHkgVENNIGRpZmZlcmVudGlhdGlvbiBzdGFnZQrigKIgMzMtNjY6IFRDTS9URU0gdHJhbnNpdGlvbiBzdGFnZQrigKIgNjYtMTAwOiBNYXR1cmUgZWZmZWN0b3Igb3IgVHJlZyBzdGFnZQrigKIgQ29tcGFyZSBhY3Jvc3MgbGluZXM6IGlmIEwxIHBzZXVkb3RpbWUgPCBMNSBwc2V1ZG90aW1lLCBMNSBpcyBtb3JlCiAgbWF0dXJlL2RpZmZlcmVudGlhdGVkIOKAlCBtYXkgY29ycmVsYXRlIHdpdGggYWdncmVzc2l2ZW5lc3MKClNURVAgNTogTE9PSyBBVCBUSEUgRkFDRVRFRCBVTUFQCuKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgArigKIgVGlnaHQgY2x1c3RlcnMgaW4gb25lIHJlZ2lvbiDihpIgaG9tb2dlbmVvdXMgY2VsbCBsaW5lCuKAoiBTY2F0dGVyZWQgY2VsbHMg4oaSIGhldGVyb2dlbmVvdXMgbGluZSAobWF5IGhhdmUgc3ViY2xvbmVzKQrigKIgTGluZXMgb3ZlcmxhcHBpbmcgdGhlIHNhbWUgVU1BUCByZWdpb24g4oaSIHNhbWUgY2VsbCBvZiBvcmlnaW4K4oCiIExpbmVzIGluIGRpZmZlcmVudCBVTUFQIHJlZ2lvbnMg4oaSIGRpZmZlcmVudCBjZWxsIG9mIG9yaWdpbgoiKQpgYGAKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBTZXNzaW9uIEluZm9ybWF0aW9uCgpgYGB7ciBzZXNzaW9uLWluZm99CnNlc3Npb25JbmZvKCkKYGBgCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgTWV0YWRhdGEgUmVmZXJlbmNlCgpgYGB7ciBtZXRhZGF0YS10YWJsZSwgZWNobz1GQUxTRX0KbWV0YV90YmwgPC0gZGF0YS5mcmFtZSgKICBDb2x1bW4gPSBjKAogICAgIm9yaWcuaWRlbnQiLAogICAgInByZWRpY3RlZC5wcmVkaWN0ZWRfc3RhdGUiLAogICAgInByZWRpY3RlZC5wcmVkaWN0ZWRfc3RhdGUuc2NvcmUiLAogICAgInByZWRpY3RlZC5wcmVkaWN0ZWRfbWlsZXN0b25lIiwKICAgICJtc3RfcHNldWRvdGltZSIsCiAgICAibmVhcmVzdF9taWxlc3RvbmVfbXN0IiwKICAgICJtb25vY2xlM19wc2V1ZG90aW1lIgogICksCiAgRGVzY3JpcHRpb24gPSBjKAogICAgIkNlbGwgbGluZSBpZGVudGl0eTogTDHigJNMNyAobWFsaWduYW50IFPDqXphcnkgbGluZXMpIiwKICAgICJNYXBRdWVyeSBsYWJlbCB0cmFuc2ZlcjogbW9zdCBzaW1pbGFyIENENCBUIGNlbGwgc3RhdGUgZnJvbSByZWZlcmVuY2UiLAogICAgIk1hcFF1ZXJ5IGNvbmZpZGVuY2Ugc2NvcmUgKDDigJMxKTogaGlnaGVyID0gbW9yZSBjb25maWRlbnQgcHJlZGljdGlvbiIsCiAgICAiTWFwUXVlcnkgbGFiZWwgdHJhbnNmZXI6IG1vc3Qgc2ltaWxhciBtaWxlc3RvbmUgKE0wMOKAk00wNykgZnJvbSByZWZlcmVuY2UiLAogICAgIk1TVCBwc2V1ZG90aW1lICgw4oCTMTAwKTogMD1OYWl2ZS1saWtlLCAxMDA9VGVtcmEvVGVybWluYWwgVHJlZy1saWtlIiwKICAgICJOZWFyZXN0IE1TVCBtaWxlc3RvbmUgYWZ0ZXIgb3J0aG9nb25hbCBlZGdlIHByb2plY3Rpb24iLAogICAgIktOTi13ZWlnaHRlZCBNb25vY2xlMyBwc2V1ZG90aW1lIGZyb20gMTUgbmVhcmVzdCByZWZlcmVuY2UgbmVpZ2hib3VycyAoMOKAkzEwMCkiCiAgKSwKICBJbnRlcnByZXRhdGlvbiA9IGMoCiAgICAiVXNlIHRvIHNwbGl0IHBlci1jZWxsLWxpbmUgYW5hbHlzaXMiLAogICAgIlByaW1hcnkgY2VsbCBvZiBvcmlnaW4gaW5kaWNhdG9yIiwKICAgICI+MC43IGhpZ2ggY29uZmlkZW5jZSB8IDAuNS0wLjcgbW9kZXJhdGUgfCA8MC41IGludGVycHJldCBjYXV0aW91c2x5IiwKICAgICJNb3N0IHByZWNpc2UgdHJhamVjdG9yeSBwb3NpdGlvbiAoTTAyID0gYnJhbmNoIHBvaW50KSIsCiAgICAiRGlmZmVyZW50aWF0aW9uIG1hdHVyaXR5OiBsb3dlciA9IG1vcmUgbmFpdmUtbGlrZSBhdCB0aW1lIG9mIHRyYW5zZm9ybWF0aW9uIiwKICAgICJDb25maXJtcyB0cmFqZWN0b3J5IHBvc2l0aW9uOyBNMDEtTTAyID0gVENNLCBNMDMtTTA0ID0gVEVNLCBNMDUgPSBUZW1yYSIsCiAgICAiSW5kZXBlbmRlbnQgcHNldWRvdGltZSBlc3RpbWF0ZSBmb3Igcm9idXN0bmVzcyB2YWxpZGF0aW9uIgogICkKKQprYWJsZShtZXRhX3RibCwKICAgICAgY2FwdGlvbj0iTWV0YWRhdGEgZmllbGRzIGFkZGVkIHRvIHNlemFyeV9vYmogYnkgdGhpcyBwaXBlbGluZSIpICU+JQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnM9Yygic3RyaXBlZCIsImNvbmRlbnNlZCIpLCBmdWxsX3dpZHRoPVRSVUUpCmBgYAo=