1 Biological Background & Rationale

1.1 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

1.2 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:

  1. Where on the trajectory do malignant cells land? (cell of origin state)
  2. How far along differentiation are they? (pseudotime = differentiation maturity)
  3. Are different cell lines (L1–L7) at the same or different positions? (heterogeneity in cell of origin across the disease)
  4. Do all cells within a line occupy the same position, or is there intra-line heterogeneity? (clonal evolution vs stable malignant state)

1.3 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

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

3 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 

4 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.


5 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

6 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

7 Visualisation 1 — All Cell Lines Together


8 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

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


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


11 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")]])
}


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


13 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

14 Prediction Score Quality Check


15 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

16 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

17 Session Information

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

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

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

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

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

other attached packages:
 [1] future_1.69.0        kableExtra_1.4.0     knitr_1.51           RColorBrewer_1.1-3   scales_1.4.0         patchwork_1.3.2     
 [7] tidyr_1.3.2          dplyr_1.2.0          ggrepel_0.9.6        ggplot2_4.0.2        RANN_2.6.2           igraph_2.2.2        
[13] SeuratWrappers_0.4.0 Seurat_5.4.0         SeuratObject_5.3.0   sp_2.2-1            

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

18 Metadata Reference

Metadata fields added to sezary_obj by this pipeline
Column Description Interpretation
orig.ident Cell line identity: L1–L7 (malignant Sézary lines) Use to split per-cell-line analysis
predicted.predicted_state MapQuery label transfer: most similar CD4 T cell state from reference Primary cell of origin indicator
predicted.predicted_state.score MapQuery confidence score (0–1): higher = more confident prediction >0.7 high confidence &#124; 0.5-0.7 moderate &#124; <0.5 interpret cautiously
predicted.predicted_milestone MapQuery label transfer: most similar milestone (M00–M07) from reference Most precise trajectory position (M02 = branch point)
mst_pseudotime MST pseudotime (0–100): 0=Naive-like, 100=Temra/Terminal Treg-like Differentiation maturity: lower = more naive-like at time of transformation
nearest_milestone_mst Nearest MST milestone after orthogonal edge projection Confirms trajectory position; M01-M02 = TCM, M03-M04 = TEM, M05 = Temra
monocle3_pseudotime KNN-weighted Monocle3 pseudotime from 15 nearest reference neighbours (0–100) Independent pseudotime estimate for robustness validation
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=