1 Load Libraries

2 Load Objects

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

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

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

cat("=== Reference object summary ===\n")
=== Reference object summary ===
cat("Cells     :", ncol(reference_integrated), "\n")
Cells     : 11466 
cat("Assays    :", paste(names(reference_integrated@assays), collapse = ", "), "\n")
Assays    : RNA, ADT, prediction.score.celltype.l1, prediction.score.celltype.l2, prediction.score.celltype.l3, SCT, integrated 
cat("Reductions:", paste(names(reference_integrated@reductions), collapse = ", "), "\n")
Reductions: pca, umap, integrated_dr, ref.umap 
cat("UMAP model:", !is.null(reference_integrated@reductions$umap@misc$model), "\n")
UMAP model: TRUE 
cat("Clusters  :",
    paste(sort(unique(reference_integrated$seurat_clusters)), collapse = ", "), "\n")
Clusters  : 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 
# ── Confirm CD4 Proliferating cells absent ────────────────────────────────
if (any(grepl("Prolif|prolif|cycling|Cycling",
              reference_integrated$cell_type, ignore.case = TRUE))) {
  warning("⚠️  Proliferating cell_type labels detected — remove before proceeding.")
} else {
  cat("✅ No proliferating cell_type labels\n")
}
✅ No proliferating cell_type labels
prolif_markers  <- c("MKI67", "TOP2A", "PCNA", "CDK1", "STMN1")
prolif_present  <- intersect(prolif_markers, rownames(reference_integrated))
if (length(prolif_present) > 0) {
  DefaultAssay(reference_integrated) <- "SCT"
  prolif_expr <- Matrix::colMeans(
    GetAssayData(reference_integrated, layer = "data")[prolif_present, , drop = FALSE]
  )
  cat("Prolif marker score — max:", round(max(prolif_expr), 3),
      "| cells > 0.5:", sum(prolif_expr > 0.5), "\n")
  if (sum(prolif_expr > 0.5) > 50) {
    warning("⚠️  Substantial proliferation signal — verify removal was complete.")
  } else {
    cat("✅ Proliferating cells confirmed absent\n")
  }
}
Prolif marker score — max: 1.187 | cells > 0.5: 6 
✅ Proliferating cells confirmed absent
cat("\nCell type distribution (reference):\n")

Cell type distribution (reference):
print(table(reference_integrated$cell_type))

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

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

    CD4 Naive       CD4 TCM       CD4 TEM CD4 Temra/CTL          Treg 
         2037          9067           145            10           207 
# Extend palette to cover any labels not pre-seeded above
ref_l2_labels      <- unique(as.character(reference_integrated$predicted.celltype.l2))
ref_l2_labels      <- ref_l2_labels[!is.na(ref_l2_labels)]
azimuth_l2_colors  <- extend_palette(azimuth_l2_colors, ref_l2_labels)
cat("\nColour palette covers", length(ref_l2_labels), "reference labels ✅\n")

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

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

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

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

           L1            L2            L3            L4            L5            L6            L7 CD4Tcells_lab CD4Tcells_10x 
         5825          5935          6428          6006          6022          5148          5331          5106          3504 
All_samples_Merged$Group <- ifelse(
  All_samples_Merged$cell_line %in% paste0("L", 1:7), "MalignantCD4T",
  ifelse(
    All_samples_Merged$cell_line %in%
      c("CD4Tcells_lab", "CD4Tcells_10x"),  
    "NormalCD4T", "Other"
  )
)

cat("\nGroup distribution:\n")

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

MalignantCD4T    NormalCD4T 
        40695          8610 
MalignantCD4T_raw <- subset(All_samples_Merged, subset = Group == "MalignantCD4T")
cat("\nMalignant CD4T cells:", ncol(MalignantCD4T_raw), "\n")

Malignant CD4T cells: 40695 
print(table(MalignantCD4T_raw$cell_line))

           L1            L2            L3            L4            L5            L6            L7 CD4Tcells_lab CD4Tcells_10x 
         5825          5935          6428          6006          6022          5148          5331             0             0 
rm(All_samples_Merged); gc()
             used    (Mb) gc trigger    (Mb)   max used    (Mb)
Ncells    9068604   484.4   15929212   850.8   15929212   850.8
Vcells 1394672850 10640.6 4458683922 34017.1 3722027787 28396.9
# ══════════════════════════════════════════════════════════════════════════
# PER-CELL-LINE SCTransform
# ══════════════════════════════════════════════════════════════════════════
# WHY: Each cell line is a separate library with different sequencing depth
# and ambient RNA profile. A single merged SCT model conflates batch
# variation with biology. Per-line SCT corrects for this before projection.
#
# APPROACH:
#   1. Split by cell_line
#   2. SCTransform each line (regress percent.mt + cell cycle scores)
#   3. Select HVGs by cross-line frequency — most robust to batch effects
#   4. Merge, scale, PCA — ready for FindTransferAnchors
# ══════════════════════════════════════════════════════════════════════════

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

cat("Lines to process:", paste(cell_line_names, collapse = ", "), "\n\n")
Lines to process: L1, L2, L3, L4, L5, L6, L7 
cell_line_list <- lapply(cell_line_names, function(line_name) {
  obj <- cell_line_list[[line_name]]
  cat("Processing:", line_name, "| Cells:", ncol(obj), "\n")

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

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

  obj <- SCTransform(obj,
                     vars.to.regress     = vars_regress,
                     variable.features.n = 3000,
                     vst.flavor          = "v2",
                     verbose             = FALSE)
  cat("  ✅", line_name, "complete |", length(VariableFeatures(obj)), "HVGs\n")
  return(obj)
})
Processing: L1 | Cells: 5825 
  → Cell cycle scores computed
  ✅ L1 complete | 3000 HVGs
Processing: L2 | Cells: 5935 
  → Cell cycle scores computed
  ✅ L2 complete | 3000 HVGs
Processing: L3 | Cells: 6428 
  → Cell cycle scores computed
  ✅ L3 complete | 3000 HVGs
Processing: L4 | Cells: 6006 
  → Cell cycle scores computed
  ✅ L4 complete | 3000 HVGs
Processing: L5 | Cells: 6022 
  → Cell cycle scores computed
  ✅ L5 complete | 3000 HVGs
Processing: L6 | Cells: 5148 
  → Cell cycle scores computed
  ✅ L6 complete | 3000 HVGs
Processing: L7 | Cells: 5331 
  → Cell cycle scores computed
  ✅ L7 complete | 3000 HVGs
names(cell_line_list) <- cell_line_names  # reuse saved names — no second SplitObject

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

cat("\nHVG selection:\n")

HVG selection:
cat("  Variable in all", length(cell_line_list), "lines:",
    sum(hvg_freq == length(cell_line_list)), "\n")
  Variable in all 7 lines: 689 
cat("  Variable in ≥3 lines:", sum(hvg_freq >= 3), "\n")
  Variable in ≥3 lines: 3271 
cat("  Final HVG set:", n_hvgs, "\n")
  Final HVG set: 3000 
# ── Merge, scale, PCA ─────────────────────────────────────────────────────
MalignantCD4T <- merge(cell_line_list[[1]], y = cell_line_list[-1],
                       merge.data = TRUE)
VariableFeatures(MalignantCD4T) <- shared_hvgs

cat("\nRunning PCA on merged object...\n")

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

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

  L1   L2   L3   L4   L5   L6   L7 
5825 5935 6428 6006 6022 5148 5331 
rm(MalignantCD4T_raw, cell_line_list); gc()
             used    (Mb) gc trigger    (Mb)   max used    (Mb)
Ncells    9453241   504.9   15929212   850.8   15929212   850.8
Vcells 1387730132 10587.6 4280400565 32656.9 5350467741 40820.9

2.3 variable genes all 7 cell line check

# Genes variable in ALL 7 lines
hvg_all7 <- names(hvg_freq[hvg_freq == 7])
cat("Genes variable in all 7 lines:", length(hvg_all7), "\n")
Genes variable in all 7 lines: 689 
print(hvg_all7)
  [1] "AARS"        "ABCC1"       "ABHD3"       "AC004687.1"  "AC006064.4"  "AC008105.3"  "AC011603.2"  "AC020916.1"  "ACAT2"       "ACLY"        "ACTB"       
 [12] "ACTG1"       "ACTN4"       "ADA"         "ADAM19"      "ADGRE5"      "AHNAK"       "AHR"         "AKAP13"      "AL133415.1"  "AL138963.4"  "AL662797.1" 
 [23] "ALOX5AP"     "ANKRD17"     "ANKRD44"     "ANLN"        "ANXA1"       "ANXA2"       "ANXA6"       "APP"         "ARGLU1"      "ARHGAP11A"   "ARHGAP15"   
 [34] "ARHGDIB"     "ARHGEF6"     "ARL4C"       "ARL6IP1"     "ASF1B"       "ASPM"        "ATAD2"       "ATP1A1"      "ATP2A2"      "ATP2B1"      "ATP5MC1"    
 [45] "ATXN1"       "AURKA"       "AURKB"       "B2M"         "BACH2"       "BCAT1"       "BCL2"        "BHLHE40"     "BIRC3"       "BIRC5"       "BIRC6"      
 [56] "BLVRA"       "BRCA1"       "BRIP1"       "BTG1"        "BTG2"        "BUB1"        "BUB1B"       "C12orf75"    "C21orf58"    "CALM1"       "CALM2"      
 [67] "CALR"        "CAMK4"       "CANX"        "CAPN2"       "CARHSP1"     "CASK"        "CASP8"       "CAST"        "CBR3"        "CCDC28B"     "CCDC86"     
 [78] "CCL5"        "CCNA2"       "CCNB1"       "CCNB2"       "CCND2"       "CCND3"       "CCNE1"       "CCNE2"       "CCNF"        "CCR7"        "CCT5"       
 [89] "CD151"       "CD3D"        "CD48"        "CD52"        "CD55"        "CD59"        "CD69"        "CD70"        "CD74"        "CD96"        "CDC20"      
[100] "CDC6"        "CDCA2"       "CDCA3"       "CDCA5"       "CDCA7"       "CDCA8"       "CDK1"        "CDK2AP2"     "CDK6"        "CDKAL1"      "CDKN1A"     
[111] "CDKN2D"      "CDKN3"       "CDT1"        "CEBPB"       "CELF2"       "CENPA"       "CENPE"       "CENPF"       "CENPU"       "CEP128"      "CEP55"      
[122] "CHAC1"       "CHAF1A"      "CHCHD10"     "CHST11"      "CISH"        "CKAP2"       "CKAP2L"      "CKAP5"       "CKS1B"       "CKS2"        "CLDND1"     
[133] "CLSPN"       "CLTC"        "CNN2"        "CORO1A"      "CORO1B"      "COTL1"       "CRIP1"       "CTSC"        "CTSD"        "CXCR4"       "CYBA"       
[144] "CYLD"        "CYP51A1"     "CYTH1"       "CYTIP"       "CYTOR"       "DANCR"       "DCTN1"       "DDIT3"       "DDIT4"       "DDX21"       "DDX3X"      
[155] "DDX6"        "DENND1B"     "DENND4C"     "DEPDC1"      "DEPDC1B"     "DIAPH3"      "DLG1"        "DLGAP5"      "DNAJA1"      "DNAJB1"      "DOCK10"     
[166] "DOCK2"       "DOCK8"       "DSCC1"       "DTL"         "DUSP2"       "DYNLL1"      "E2F1"        "ECT2"        "EEF1A1"      "EEF2"        "EFHD2"      
[177] "EIF1"        "EIF4G1"      "ELMO1"       "EMP3"        "ENO1"        "ERC1"        "ERN1"        "ESCO2"       "ESYT1"       "ESYT2"       "ETS1"       
[188] "EVL"         "EXOC4"       "EZH2"        "FABP5"       "FAM107B"     "FAM111A"     "FAM111B"     "FAM83D"      "FASN"        "FBXL17"      "FBXL20"     
[199] "FBXO5"       "FDFT1"       "FEN1"        "FKBP11"      "FKBP4"       "FKBP5"       "FLI1"        "FLNA"        "FLOT1"       "FNDC3A"      "FOS"        
[210] "FOXN3"       "FOXP2"       "FTL"         "FTX"         "FUS"         "FXYD5"       "FYN"         "GABPB1-AS1"  "GAPDH"       "GARS"        "GAS5"       
[221] "GATA3"       "GINS2"       "GNG2"        "GOLGB1"      "GPHN"        "GPR15"       "GPR171"      "GPRIN3"      "GRN"         "GSTP1"       "GTSE1"      
[232] "H1FX"        "H2AFX"       "H2AFZ"       "H3F3B"       "HCST"        "HDAC9"       "HELLS"       "HERPUD1"     "HIPK2"       "HIST1H1A"    "HIST1H1B"   
[243] "HIST1H1C"    "HIST1H1D"    "HIST1H1E"    "HIST1H2AC"   "HIST1H2AE"   "HIST1H2AG"   "HIST1H2AL"   "HIST1H2BC"   "HIST1H2BJ"   "HIST1H3B"    "HIST1H3D"   
[254] "HIST1H3H"    "HIST1H3I"    "HIST1H4C"    "HIST2H2AB"   "HIST2H2AC"   "HIST2H2BF"   "HJURP"       "HLA-A"       "HLA-B"       "HLA-C"       "HLA-E"      
[265] "HMGB2"       "HMGCR"       "HMGCS1"      "HMGN2"       "HMMR"        "HNRNPA3"     "HNRNPAB"     "HNRNPH1"     "HNRNPU"      "HNRNPUL2"    "HP1BP3"     
[276] "HPGD"        "HSP90AA1"    "HSP90AB1"    "HSP90B1"     "HSPA1A"      "HSPA1B"      "HSPA5"       "HSPA8"       "HSPA9"       "HSPB1"       "HSPD1"      
[287] "HSPE1"       "HSPH1"       "HUWE1"       "IARS"        "ID2"         "IDH2"        "IDI1"        "IER2"        "IER3"        "IFITM1"      "IGF2R"      
[298] "IKZF1"       "IKZF2"       "IL10RA"      "IL2RB"       "IL32"        "IL4R"        "IL9R"        "ILF3-DT"     "IMMP2L"      "INCENP"      "INPP4B"     
[309] "INSIG1"      "IPO5"        "IQGAP1"      "IRF1"        "ISG20"       "ITGA4"       "ITGAL"       "ITGB2"       "ITGB7"       "ITK"         "ITM2B"      
[320] "ITM2C"       "JPT1"        "JUN"         "JUNB"        "JUND"        "KCNQ5"       "KIF11"       "KIF14"       "KIF15"       "KIF18B"      "KIF20A"     
[331] "KIF20B"      "KIF21B"      "KIF23"       "KIF2C"       "KIF4A"       "KIFC1"       "KLF6"        "KMT2C"       "KNL1"        "KNSTRN"      "KPNA2"      
[342] "LARP1"       "LASP1"       "LAT"         "LBR"         "LCP1"        "LCP2"        "LDHA"        "LDLR"        "LDLRAD4"     "LGALS1"      "LIME1"      
[353] "LINC00892"   "LINC01572"   "LMNA"        "LMNB1"       "LMO4"        "LRBA"        "LRPPRC"      "LSP1"        "LTA"         "LTB"         "LY6E"       
[364] "LYST"        "MACF1"       "MALAT1"      "MANF"        "MAP3K8"      "MARCKSL1"    "MAT2A"       "MBD5"        "MBNL1"       "MBP"         "MCL1"       
[375] "MCM10"       "MCM2"        "MCM3"        "MCM4"        "MCM5"        "MCM6"        "MCM7"        "MDFIC"       "MDN1"        "MGST3"       "MIB1"       
[386] "MIF"         "MIR4435-2HG" "MIS18BP1"    "MKI67"       "MKNK2"       "MMP25"       "MSH6"        "MSI2"        "MSMO1"       "MSN"         "MT-ATP6"    
[397] "MT-ATP8"     "MT-CO1"      "MT-CO2"      "MT-CO3"      "MT-CYB"      "MT-ND1"      "MT-ND2"      "MT-ND3"      "MT-ND4"      "MT-ND4L"     "MT-ND5"     
[408] "MT-ND6"      "MT1X"        "MT2A"        "MTHFD2"      "MTRNR2L12"   "MXD3"        "MYB"         "MYC"         "MYH9"        "MYL6"        "MYO1F"      
[419] "MYO1G"       "MZB1"        "NAMPT"       "NCAPD2"      "NCAPG"       "NCAPG2"      "NCL"         "NCOA3"       "NDC80"       "NEAT1"       "NEIL3"      
[430] "NEK2"        "NEK7"        "NFAT5"       "NFATC2"      "NFE2L3"      "NFKB1"       "NFKBIA"      "NIBAN1"      "NINJ1"       "NKTR"        "NME1"       
[441] "NOLC1"       "NOP16"       "NORAD"       "NPM1"        "NQO1"        "NR3C1"       "NSD2"        "NSMCE2"      "NUCB2"       "NUDT8"       "NUF2"       
[452] "NUFIP2"      "NUMA1"       "NUSAP1"      "ODC1"        "OPTN"        "OSBPL3"      "OSTF1"       "P2RY8"       "PALM2-AKAP2" "PARP14"      "PCLAF"      
[463] "PCNA"        "PDE3B"       "PDE4D"       "PDE7A"       "PGAM1"       "PGK1"        "PHACTR2"     "PHF19"       "PHLDA1"      "PIK3CD"      "PIK3R1"     
[474] "PIM1"        "PIM2"        "PIM3"        "PKM"         "PKMYT1"      "PLAAT4"      "PLEC"        "PLK1"        "PLP2"        "PLPP1"       "PMAIP1"     
[485] "PNN"         "PNRC1"       "POLR2A"      "PPDPF"       "PPP1R15A"    "PPP3CA"      "PRC1"        "PRDX1"       "PREX1"       "PRKCA"       "PRKDC"      
[496] "PRNP"        "PRR11"       "PSAT1"       "PTMA"        "PTMS"        "PTPN6"       "PTPN7"       "PTPRC"       "PTTG1"       "PUM3"        "PUS7"       
[507] "PVT1"        "PYCARD"      "RAB11FIP1"   "RAB37"       "RABGAP1L"    "RACGAP1"     "RAD21"       "RAD51B"      "RASGRP1"     "RBL1"        "RBM38"      
[518] "RBPJ"        "RCC2"        "RCSD1"       "REEP5"       "RELB"        "RERE"        "RHBDD2"      "RHOC"        "RNF213"      "RORA"        "RPL10"      
[529] "RPL11"       "RPL12"       "RPL13"       "RPL19"       "RPL22L1"     "RPL32"       "RPL41"       "RPLP0"       "RPLP1"       "RPS12"       "RPS14"      
[540] "RPS18"       "RPS2"        "RPS23"       "RPS3"        "RPS3A"       "RPS6KA5"     "RPS8"        "RRM2"        "RSRP1"       "RUNX3"       "S100A10"    
[551] "S100A11"     "S100A4"      "S100A6"      "S100P"       "SAC3D1"      "SAMD9"       "SASH3"       "SCLT1"       "SCPEP1"      "SDCBP"       "SEC14L1"    
[562] "SELENOW"     "SELPLG"      "SEMA4D"      "SEPTIN9"     "SERPINB1"    "SETX"        "SFPQ"        "SGO2"        "SH2D2A"      "SH3BGRL3"    "SH3BP1"     
[573] "SIK3"        "SIT1"        "SKAP1"       "SLBP"        "SLC16A1-AS1" "SLC1A5"      "SLC20A1"     "SLC25A32"    "SLC2A3"      "SLC38A2"     "SLC3A2"     
[584] "SLC43A3"     "SLC4A7"      "SLC7A5"      "SLC9A3R1"    "SLFN12L"     "SMARCA2"     "SMC1A"       "SMC4"        "SMG1"        "SMYD3"       "SNHG12"     
[595] "SNHG15"      "SNHG3"       "SNHG7"       "SNHG8"       "SNRNP200"    "SOCS1"       "SORL1"       "SOS1"        "SP140"       "SPAG5"       "SPIDR"      
[606] "SPOCK2"      "SPTAN1"      "SPTBN1"      "SQLE"        "SQSTM1"      "SREBF2"      "SRGN"        "SRM"         "SRRT"        "SRSF7"       "SSBP2"      
[617] "ST8SIA4"     "STAT1"       "STAT3"       "STAT4"       "STK10"       "STK17B"      "STMN1"       "SUN2"        "SYNE2"       "SYTL3"       "TACC3"      
[628] "TAF15"       "TAGLN2"      "TBC1D5"      "TBL1X"       "TCF12"       "TCP1"        "TFRC"        "TIMP1"       "TK1"         "TMBIM1"      "TMEM173"    
[639] "TMPO"        "TMSB10"      "TMSB4X"      "TMTC2"       "TMX4"        "TNFRSF1B"    "TNFSF10"     "TNIK"        "TOP2A"       "TPM4"        "TPX2"       
[650] "TRAF1"       "TRAF3IP3"    "TRBV20-1"    "TRG-AS1"     "TRIM44"      "TRIM56"      "TRIM59"      "TRIO"        "TROAP"       "TSC22D3"     "TTK"        
[661] "TUBA1A"      "TUBA1B"      "TUBA1C"      "TUBA4A"      "TUBB"        "TUBB4B"      "TYMS"        "UBALD2"      "UBC"         "UBE2C"       "UBE2S"      
[672] "UBR4"        "UCP2"        "UHRF1"       "UNG"         "VIM"         "WARS"        "WDR76"       "WWOX"        "XBP1"        "YWHAG"       "ZC3HAV1"    
[683] "ZEB1"        "ZFAND3"      "ZFAS1"       "ZFP36"       "ZFP36L1"     "ZFP36L2"     "ZYX"        
# Check which canonical T cell markers are in the all-7 set
t_cell_markers <- c(
  # Naive/memory
  "CCR7", "SELL", "TCF7", "IL7R", "LEF1", "KLF2",
  # Activation/exhaustion  
  "TOX", "PDCD1", "LAG3", "TIGIT", "CTLA4", "HAVCR2",
  # Effector
  "GZMB", "GZMK", "GZMA", "PRF1", "IFNG", "TNF",
  # Treg
  "FOXP3", "IL2RA", "IKZF2", "CTLA4",
  # Sézary specific
  "KIR3DL2", "PLS3", "TWIST1", "EPHA4", "CD164",
  # Proliferation
  "MKI67", "TOP2A", "CDK1"
)

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

Canonical markers in all-7 HVG set:
print(found_in_all7)
[1] "CCR7"  "IKZF2" "MKI67" "TOP2A" "CDK1" 
not_found <- setdiff(t_cell_markers, hvg_all7)
cat("\nMarkers NOT in all-7 set:\n")

Markers NOT in all-7 set:
print(not_found)
 [1] "SELL"    "TCF7"    "IL7R"    "LEF1"    "KLF2"    "TOX"     "PDCD1"   "LAG3"    "TIGIT"   "CTLA4"   "HAVCR2"  "GZMB"    "GZMK"    "GZMA"    "PRF1"    "IFNG"   
[17] "TNF"     "FOXP3"   "IL2RA"   "KIR3DL2" "PLS3"    "TWIST1"  "EPHA4"   "CD164"  

2.4 variable genes all 7 cell line check

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

cat("=== Junk gene check on shared_hvgs ===\n")
=== Junk gene check on shared_hvgs ===
mt_in_hvgs <- shared_hvgs[grepl("^MT-", shared_hvgs)]
cat("MT genes in shared_hvgs:", length(mt_in_hvgs), "\n")
MT genes in shared_hvgs: 13 
print(mt_in_hvgs)
 [1] "MT-ATP6" "MT-ATP8" "MT-CO1"  "MT-CO2"  "MT-CO3"  "MT-CYB"  "MT-ND1"  "MT-ND2"  "MT-ND3"  "MT-ND4"  "MT-ND4L" "MT-ND5"  "MT-ND6" 
ribo_in_hvgs <- shared_hvgs[grepl("^RPL|^RPS", shared_hvgs)]
cat("\nRibosomal genes in shared_hvgs:", length(ribo_in_hvgs), "\n")

Ribosomal genes in shared_hvgs: 53 
print(ribo_in_hvgs)
 [1] "RPL10"   "RPL11"   "RPL12"   "RPL13"   "RPL19"   "RPL22L1" "RPL32"   "RPL41"   "RPLP0"   "RPLP1"   "RPS12"   "RPS14"   "RPS18"   "RPS2"    "RPS23"   "RPS3"   
[17] "RPS3A"   "RPS6KA5" "RPS8"    "RPL13A"  "RPL18A"  "RPL28"   "RPL29"   "RPL35A"  "RPL5"    "RPL7A"   "RPL8"    "RPS13"   "RPS6"    "RPS6KA3" "RPS7"    "RPL17"  
[33] "RPL23"   "RPL30"   "RPL35"   "RPL6"    "RPL7"    "RPS15A"  "RPS27L"  "RPS4X"   "RPL14"   "RPL23A"  "RPL26"   "RPL27A"  "RPL3"    "RPL37"   "RPL4"    "RPS17"  
[49] "RPS20"   "RPS24"   "RPS27A"  "RPS6KA1" "RPS6KC1"
hsp_in_hvgs <- shared_hvgs[grepl("^HSP|^HSPA|^HSPB", shared_hvgs)]
cat("\nHeat shock genes in shared_hvgs:", length(hsp_in_hvgs), "\n")

Heat shock genes in shared_hvgs: 13 
print(hsp_in_hvgs)
 [1] "HSP90AA1" "HSP90AB1" "HSP90B1"  "HSPA1A"   "HSPA1B"   "HSPA5"    "HSPA8"    "HSPA9"    "HSPB1"    "HSPD1"    "HSPE1"    "HSPH1"    "HSPA4"   
snhg_in_hvgs <- shared_hvgs[grepl("^SNHG|MALAT1|NEAT1", shared_hvgs)]
cat("\nlncRNA genes in shared_hvgs:", length(snhg_in_hvgs), "\n")

lncRNA genes in shared_hvgs: 15 
print(snhg_in_hvgs)
 [1] "MALAT1" "NEAT1"  "SNHG12" "SNHG15" "SNHG3"  "SNHG7"  "SNHG8"  "SNHG1"  "SNHG29" "SNHG16" "SNHG25" "SNHG17" "SNHG30" "SNHG32" "SNHG5" 
cat("\nTotal junk genes to be filtered:", 
    length(mt_in_hvgs) + length(ribo_in_hvgs) + 
    length(hsp_in_hvgs) + length(snhg_in_hvgs), "\n")

Total junk genes to be filtered: 94 

3 Reference Trajectory (Monocle3)

3.1 Build CDS from Reference


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

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

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

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

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

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

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

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

3.2 Learn Trajectory Graph — Two Lineages

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

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

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

✅ Principal graph learned
cat("Nodes:", length(igraph::V(principal_graph(cds)$UMAP)), "\n")
Nodes: 349 
n_branch_points <- sum(igraph::degree(principal_graph(cds)$UMAP) > 2)
cat("Branch points:", n_branch_points, "\n")
Branch points: 18 
if (n_branch_points == 0) {
  warning(
    "❌ No branch point — Treg lineage not separated.\n",
    "   Re-run learn_graph with minimal_branch_len = 3"
  )
} else if (n_branch_points > 3) {
  warning(
    "⚠️  Too many branch points (", n_branch_points, ").\n",
    "   Re-run learn_graph with minimal_branch_len = 8"
  )
} else {
  cat("✅ Branch structure looks correct (1-3 branch points)\n")
}

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

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

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

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

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

p_graph_l2 | p_graph_ct

3.3 Set Root & Order Cells by Pseudotime

cat("Azimuth l2 labels in CDS:\n")
Azimuth l2 labels in CDS:
print(sort(unique(colData(cds)$predicted.celltype.l2)))
[1] CD4 Naive     CD4 TCM       CD4 TEM       CD4 Temra/CTL Treg         
Levels: CD4 Naive CD4 TCM CD4 TEM CD4 Temra/CTL Treg
# ── Identify naive cells ───────────────────────────────────────────────────
naive_ids <- rownames(colData(cds))[
  grepl("naive|Naive|TN$", colData(cds)$predicted.celltype.l2,
        ignore.case = TRUE) |
  grepl("Tnaive", as.character(colData(cds)$cell_type),
        ignore.case = FALSE)
]
cat("\nNaive root cells:", length(naive_ids), "\n")

Naive root cells: 5739 
if (length(naive_ids) == 0)
  stop("❌ No naive cells found. Check label patterns above.")

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

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

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

cat("\nPseudotime summary (reference):\n")

Pseudotime summary (reference):
print(summary(reference_integrated$monocle3_pseudotime[
  is.finite(reference_integrated$monocle3_pseudotime)]))
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  0.000   4.226   9.459  11.396  18.602  25.553 
# ══════════════════════════════════════════════════════════════════════════
# TOPOLOGY VALIDATION
# ══════════════════════════════════════════════════════════════════════════
# Correct order for each lineage:
#   Effector: Naive < TCM < TEM < Temra
#   Treg:     Naive < TCM — Treg   (Treg branches from TCM, NOT after TEM)
#
# This is a hard biological requirement. If Treg > TEM in pseudotime,
# the graph has placed Treg downstream of TEM which is anatomically wrong.
# Treg differentiates from TCM-stage precursors via FOXP3 induction,
# not from terminally differentiated TEM/Temra cells.

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

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

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

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

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

State medians: Naive=3.99 | TCM=13.36 | Treg=18.75 | TEM=24.78 | Temra=25.25
# Hard stops — script will not continue if topology is biologically wrong
if (naive_med > tcm_med)
  stop(sprintf(
    "❌ TOPOLOGY ERROR: Naive (%.2f) > TCM (%.2f).\n  Root node may be misplaced — check naive_centroid coordinates.",
    naive_med, tcm_med))

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

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

# FIX: single sprintf format string (was split across two strings before)
cat(sprintf(
  "✅ Topology confirmed: Naive(%.2f) < TCM(%.2f) < Treg(%.2f) < TEM(%.2f) < Temra(%.2f)\n",
  naive_med, tcm_med, treg_med, tem_med, temra_med
))
✅ Topology confirmed: Naive(3.99) < TCM(13.36) < Treg(18.75) < TEM(24.78) < Temra(25.25)
# ── Pseudotime UMAP plots ─────────────────────────────────────────────────
plot_cells(
  cds,
  color_cells_by        = "pseudotime",
  label_cell_groups     = FALSE,
  cell_size             = 0.8,
  show_trajectory_graph = TRUE
) +
  scale_color_viridis_c(option = "plasma", name = "Pseudotime") +
  ggtitle("Reference Pseudotime\n(Root = CD4 Naive | Two lineages: TEM/Temra + Treg)") +
  theme(plot.title = element_text(hjust = 0.5, size = 13, face = "bold"))

3.4 Validate Pseudotime Against Cell Type

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

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

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

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

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

p_violin | p_ridge


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

cat("=== DIAGNOSTIC: Current reference state ===\n")
=== DIAGNOSTIC: Current reference state ===
cat("DefaultAssay:", DefaultAssay(reference_integrated), "\n")
DefaultAssay: SCT 
cat("SCT HVGs:", length(VariableFeatures(reference_integrated)), "\n")
SCT HVGs: 0 
cat("PCA dims:", ncol(Embeddings(reference_integrated, "pca")), "\n")
PCA dims: 50 
cat("UMAP model present:", !is.null(reference_integrated@reductions$umap@misc$model), "\n")
UMAP model present: TRUE 
# 1. Sanity check UMAP before
old_umap_coords <- Embeddings(reference_integrated, "umap")[1:3, ]
old_umap_model  <- !is.null(reference_integrated@reductions$umap@misc$model)
cat("\nUMAP first 3 cells (backup):", paste(round(old_umap_coords[1,], 3), collapse=" "), "\n")

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

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

# 4. Verify fix
cat("\n=== FIXED: New reference state ===\n")

=== FIXED: New reference state ===
cat("DefaultAssay:", DefaultAssay(reference_integrated), "\n")
DefaultAssay: SCT 
cat("SCT HVGs:", length(VariableFeatures(reference_integrated)), "\n")
SCT HVGs: 3000 
cat("PCA dims:", ncol(Embeddings(reference_integrated, "pca")), "\n")
PCA dims: 30 
cat("UMAP model preserved:", !is.null(reference_integrated@reductions$umap@misc$model), "\n")
UMAP model preserved: TRUE 
# 5. UMAP integrity check
new_umap_coords <- Embeddings(reference_integrated, "umap")[1:3, ]
cat("UMAP unchanged:", all(old_umap_coords == new_umap_coords), "\n")
UMAP unchanged: TRUE 
cat("✅ Reference now in SCT space — FindTransferAnchors will work for ALL 7 lines\n")
✅ Reference now in SCT space — FindTransferAnchors will work for ALL 7 lines

4 Prepare Reference for MapQuery

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

5 Map Malignant Cells onto Reference

5.1 Find Transfer Anchors

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

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

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

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

cat("=== Feature set construction ===\n")
=== Feature set construction ===
cat("Reference HVGs :", length(ref_hvgs), "\n")
Reference HVGs : 3000 
cat("Query HVGs     :", length(query_hvgs), "\n")
Query HVGs     : 3000 
cat("Common HVGs    :", length(common_features), "\n")
Common HVGs    : 1443 
if (length(common_features) < 1500)
  warning(paste0("⚠️  Only ", length(common_features),
                 " common features — below recommended 1500.\n",
                 "   Increase nfeatures in SCTransform to 4000."))

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

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

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

cat("\n=== Junk gene removal breakdown ===\n")

=== Junk gene removal breakdown ===
cat(sprintf("  Mitochondrial (OXPHOS)    : %d\n", mt_removed))
  Mitochondrial (OXPHOS)    : 13
cat(sprintf("  Ribosomal (RPL/RPS)       : %d\n", ribo_removed))
  Ribosomal (RPL/RPS)       : 32
cat(sprintf("  Heat shock (HSP*)         : %d\n", hsp_removed))
  Heat shock (HSP*)         : 12
cat(sprintf("  lncRNA (SNHG/MALAT1/NEAT1): %d\n", lnc_removed))
  lncRNA (SNHG/MALAT1/NEAT1): 12
cat(sprintf("  Histones (HIST*)          : %d\n", hist_removed))
  Histones (HIST*)          : 10
n_before              <- length(common_features)
common_features_clean <- common_features[!grepl(junk_pattern, common_features)]
n_removed             <- n_before - length(common_features_clean)

cat(sprintf("  ─────────────────────────────\n"))
  ─────────────────────────────
cat(sprintf("  Total removed             : %d (%.1f%%)\n",
            n_removed, 100 * n_removed / n_before))
  Total removed             : 79 (5.5%)
cat(sprintf("  Clean features remaining  : %d\n\n",
            length(common_features_clean)))
  Clean features remaining  : 1364
if (length(common_features_clean) < 1200)
  warning("⚠️  Fewer than 1200 clean features — anchor quality may be reduced.")

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

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

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

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

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

} else {
  cat("⚠️  residual_variance not in SCT meta.features — using unranked clean list\n")
  cat("   This is OK but ranking would improve anchor quality.\n")
  common_features <- common_features_clean
}
⚠️  residual_variance not in SCT meta.features — using unranked clean list
   This is OK but ranking would improve anchor quality.
# ── Step 4: Canonical T cell marker check ─────────────────────────────────
# These markers span the full differentiation axis and Treg branch.
# If fewer than 5 are present, the feature set cannot distinguish
# differentiation states and anchor quality will be poor.
markers_check <- c(
  "CCR7", "SELL", "TCF7", "IL7R",          # Naive/TCM
  "GZMB", "GZMK", "PRF1", "EOMES",         # Effector/TEM
  "FOXP3", "IKZF2",                         # Treg
  "TOX", "PDCD1", "LAG3",                  # Exhaustion
  "CD44", "TBX21"                           # Activation
)
found     <- markers_check[markers_check %in% common_features]
not_found <- markers_check[!markers_check %in% common_features]

cat("=== Canonical T cell marker check ===\n")
=== Canonical T cell marker check ===
cat("Present  (", length(found),    "):", paste(found,     collapse = ", "), "\n")
Present  ( 8 ): CCR7, SELL, TCF7, IL7R, PRF1, IKZF2, TOX, CD44 
cat("Absent   (", length(not_found),"):", paste(not_found, collapse = ", "), "\n")
Absent   ( 7 ): GZMB, GZMK, EOMES, FOXP3, PDCD1, LAG3, TBX21 
if (length(found) < 5) {
  warning("⚠️  Fewer than 5 canonical markers present — anchor quality at risk.")
} else if (length(found) < 8) {
  cat("⚠️  Some markers absent — likely heterogeneous across lines (expected)\n")
  cat("   Projection will still work but interpret per-line results carefully\n")
} else {
  cat("✅ Good marker coverage — feature set captures differentiation axis\n")
}
✅ Good marker coverage — feature set captures differentiation axis
# ── Step 5: FindTransferAnchors ───────────────────────────────────────────
# k.anchor = 10  : neighbours used to find anchors — balanced for ~11k ref
# k.filter = 500 : neighbours for anchor filtering — removes weak anchors
# k.score  = 30  : neighbours for anchor scoring — weights anchor quality
# dims     = 1:30: use all 30 PCA dims — captures full differentiation space

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

Finding anchors with dims 1: 30 
cat("Features used:", length(common_features), "\n\n")
Features used: 1364 
anchors <- FindTransferAnchors(
  reference            = reference_integrated,
  query                = MalignantCD4T,
  features             = common_features,
  normalization.method = "SCT",
  reference.reduction  = "pca",
  dims                 = 1:dims_to_use,
  k.anchor             = 10,
  k.filter             = 500,
  k.score              = 30,
  verbose              = TRUE
)

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

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

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

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

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

# Verify no NAs
cat("\n=== Barcode mapping validation ===\n")

=== Barcode mapping validation ===
cat("NA ref barcodes  :", sum(is.na(anchor_df$ref_barcode)),   "\n")
NA ref barcodes  : 0 
cat("NA query barcodes:", sum(is.na(anchor_df$query_barcode)), "\n")
NA query barcodes: 0 
cat("NA cell lines    :", sum(is.na(anchor_df$query_cellline)), "\n")
NA cell lines    : 0 
n_anchors    <- nrow(anchor_df)
anchor_ratio <- ncol(MalignantCD4T) / n_anchors
anchor_scores <- anchor_df$score

cat("\n=== Anchor quality summary ===\n")

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

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

  L1   L2   L3   L4   L5   L6   L7 
2062 1699  869 1025  706  926  734 
cat("\n=== Anchors by reference label ===\n")

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

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

5.2 check you can do after MapQuery:

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

cat("=== PCA space comparison: L1/L2 vs L3-L7 ===\n")
=== PCA space comparison: L1/L2 vs L3-L7 ===
# Get reference PCA centroid (TCM region)
ref_pca <- Embeddings(reference_integrated, "pca")[, 1:10]
ref_centroid <- colMeans(ref_pca)

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

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

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

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

cat("\nDistance from reference PCA centroid per cell line:\n")

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

# Check HVG overlap per line with reference
cat("\n=== HVG overlap with reference per line ===\n")

=== HVG overlap with reference per line ===
ref_hvgs_check <- VariableFeatures(reference_integrated, assay = "SCT")
for (ln in paste0("L", 1:7)) {
  line_cells  <- colnames(MalignantCD4T)[MalignantCD4T$cell_line == ln]
  # Check expression of key T cell markers in each line
  markers     <- c("CCR7", "TCF7", "SELL", "IL7R", "GZMB", "TOX", "KIR3DL2")
  markers_present <- intersect(markers, rownames(MalignantCD4T))
  expr <- rowMeans(
    GetAssayData(MalignantCD4T, assay = "SCT", layer = "data")[
      markers_present, line_cells, drop = FALSE]
  )
  cat(sprintf("\n%s — key marker expression:\n", ln))
  print(round(expr, 3))
}

L1 — key marker expression:
   CCR7    TCF7    SELL    IL7R    GZMB     TOX KIR3DL2 
  0.627   0.465   0.634   0.217   0.026   0.359   1.068 

L2 — key marker expression:
   CCR7    TCF7    SELL    IL7R    GZMB     TOX KIR3DL2 
  0.148   0.437   0.000   0.062   0.088   1.222   1.871 

L3 — key marker expression:
   CCR7    TCF7    SELL    IL7R    GZMB     TOX KIR3DL2 
  1.015   0.060   0.018   0.042   0.002   0.020   0.000 

L4 — key marker expression:
   CCR7    TCF7    SELL    IL7R    GZMB     TOX KIR3DL2 
  1.090   0.001   0.221   0.034   0.007   0.422   0.000 

L5 — key marker expression:
   CCR7    TCF7    SELL    IL7R    GZMB     TOX KIR3DL2 
  1.218   0.011   0.135   0.311   0.000   0.147   0.624 

L6 — key marker expression:
   CCR7    TCF7    SELL    IL7R    GZMB     TOX KIR3DL2 
  0.713   0.020   0.092   0.401   0.425   0.171   0.880 

L7 — key marker expression:
   CCR7    TCF7    SELL    IL7R    GZMB     TOX KIR3DL2 
  0.849   0.000   0.247   0.145   0.028   0.191   0.855 
# Show TOP reference states each cell line connects to
cat("\n=== WHAT EACH CELL LINE ANCHORS TO ===\n")

=== WHAT EACH CELL LINE ANCHORS TO ===
anchor_df %>%
  group_by(query_cellline, ref_label) %>%
  summarise(
    n_anchors  = n(),
    mean_score = round(mean(score), 3),
    .groups = "drop"
  ) %>%
  arrange(query_cellline, desc(n_anchors)) %>%
  print(n = 30)  # Show all combinations
NA

5.3 MapQuery — Project onto Reference UMAP

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

mapped_MalignantCD4T <- MapQuery(
  anchorset         = anchors,
  query             = MalignantCD4T,
  reference         = reference_integrated,
  refdata           = list(
    predicted.celltype.l2 = "predicted.celltype.l2",   # PRIMARY: state label
    pseudotime            = "monocle3_pseudotime"      # CONTINUOUS: position
  ), 
  reference.reduction = "pca",
  reduction.model     = "umap"
)
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|

  |                                                  | 0 % ~calculating  
  |++++++++++++++++++++++++++++++++++++++++++++++++++| 100% elapsed=07s  
Using method 'umap'
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
cat("✅ MapQuery complete\n")
✅ MapQuery complete
cat("Mapped cells:", ncol(mapped_MalignantCD4T), "\n")
Mapped cells: 40695 
# Coerce pseudotime to numeric — Seurat TransferData returns character
mapped_MalignantCD4T$predicted.pseudotime <- as.numeric(
  mapped_MalignantCD4T$predicted.pseudotime
)

cat("\nTransferred pseudotime summary:\n")

Transferred pseudotime summary:
print(summary(mapped_MalignantCD4T$predicted.pseudotime))
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  1.156  16.431  17.703  17.655  19.487  25.553 
cat("\nTransferred Azimuth l2 distribution:\n")

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

CD4 Naive   CD4 TCM   CD4 TEM      Treg 
       21     40291       304        79 
# FIXED Diversity check: Handle NA predictions properly
cat("\n=== Diversity check ===\n")

=== Diversity check ===
l2_table <- table(mapped_MalignantCD4T$predicted.predicted.celltype.l2, useNA = "always")
l2_dist <- prop.table(l2_table)

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

CD4 Naive   CD4 TCM   CD4 TEM      Treg      <NA> 
       21     40291       304        79         0 
na_count <- l2_table["<NA>"]
na_pct <- ifelse(is.na(na_count), 0, round(100 * na_pct, 1))

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

NA fraction: 0.0%
Dominant state: CD4 TCM (99.0% of non-NA)

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

# 1. Per-cell-line breakdown (your thesis gold)
table(mapped_MalignantCD4T$cell_line, mapped_MalignantCD4T$predicted.predicted.celltype.l2)
    
     CD4 Naive CD4 TCM CD4 TEM Treg
  L1        21    5800       2    2
  L2         0    5893      42    0
  L3         0    6168     253    7
  L4         0    6000       1    5
  L5         0    6008       6    8
  L6         0    5102       0   46
  L7         0    5320       0   11
# 2. Plot on trajectory UMAP
DimPlot(mapped_MalignantCD4T, group.by = "predicted.predicted.celltype.l2", reduction = "ref.umap")


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

NA
NA

6 Visualise Projection

6.1 Reference + Malignant Overlay

6.2 Transferred Labels + Per Cell Line

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

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

p_l2 | p_lines

6.3 Per Cell Line Facet

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

7 Quantification — State Assignment with Biology-Anchored Bins

7.1 Assign Each Malignant Cell to a Reference State

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

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

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

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

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

cat("\n=== Biology-anchored bin boundaries ===\n")

=== Biology-anchored bin boundaries ===
cat(sprintf("  Naive  | TCM  : %.3f\n", naive_tcm_cut))
  Naive  | TCM  : 8.677
cat(sprintf("  TCM    | TEM  : %.3f  (main effector axis)\n", tcm_tem_cut))
  TCM    | TEM  : 19.070  (main effector axis)
cat(sprintf("  TEM    | Temra: %.3f\n", tem_temra_cut))
  TEM    | Temra: 25.017
cat(sprintf("  TCM    | Treg : %.3f  (regulatory branch — label-based, not used as cut)\n",
            tcm_treg_cut))
  TCM    | Treg : 16.057  (regulatory branch — label-based, not used as cut)
# ── Step 3: Assign working columns ────────────────────────────────────────
mapped_MalignantCD4T$state_azimuth_l2  <-
  mapped_MalignantCD4T$predicted.predicted.celltype.l2

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

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

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

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

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

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

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

  TRUE ~ NA_character_
)

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

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

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

Note: Treg-like bin % equals Treg label % by design (label-based assignment).
cat("Label vs pseudotime bin discordance is only interpretable for the\n")
Label vs pseudotime bin discordance is only interpretable for the
cat("effector axis: Naive-like / TCM-like / TEM-like / Temra-like.\n")
effector axis: Naive-like / TCM-like / TEM-like / Temra-like.
# ── Step 6: Results ───────────────────────────────────────────────────────
cat("\n=== State assignment complete ===\n")

=== State assignment complete ===
cat("\nAzimuth l2 label (cell of origin — WHAT the cell resembles):\n")

Azimuth l2 label (cell of origin — WHAT the cell resembles):
print(table(mapped_MalignantCD4T$state_azimuth_l2))

CD4 Naive   CD4 TCM   CD4 TEM      Treg 
       21     40291       304        79 
cat("\nPseudotime bin (differentiation position — WHERE the cell sits):\n")

Pseudotime bin (differentiation position — WHERE the cell sits):
print(table(mapped_MalignantCD4T$pseudotime_bin))

Naive-like   TCM-like  Treg-like   TEM-like Temra-like 
      2734      26389         79      10415       1078 
# ── KEY RESULT: cross-table ───────────────────────────────────────────────
cat("\n═══════════════════════════════════════════════════════════\n")

═══════════════════════════════════════════════════════════
cat("KEY RESULT: Label (cell of origin) vs Pseudotime Bin\n")
KEY RESULT: Label (cell of origin) vs Pseudotime Bin
cat("═══════════════════════════════════════════════════════════\n")
═══════════════════════════════════════════════════════════
cat("TCM-labelled cells in TEM/Temra bins = effector skewing\n")
TCM-labelled cells in TEM/Temra bins = effector skewing
cat("beyond the TCM arrest point\n\n")
beyond the TCM arrest point
# FIX: explicit column naming from table() output — avoids rename() failure
# when Var1/Var2 column names differ from expected Label/PT_Bin
cross_tab <- table(
  Label  = mapped_MalignantCD4T$state_azimuth_l2,
  PT_Bin = mapped_MalignantCD4T$pseudotime_bin
)
print(cross_tab)
           PT_Bin
Label       Naive-like TCM-like Treg-like TEM-like Temra-like
  CD4 Naive         21        0         0        0          0
  CD4 TCM         2713    26357         0    10365        856
  CD4 TEM            0       32         0       50        222
  Treg               0        0        79        0          0
cat("\nNote: Treg row will show 100% Treg-like — this is by design.\n")

Note: Treg row will show 100% Treg-like — this is by design.

7.2 Quantification Tables

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

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

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

cat("\n=== State Distribution per Cell Line ===\n")

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

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

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

=== Pseudotime Bin Summary ===
print(pt_bin_summary)

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

cat("\n=== Pseudotime Bin per Cell Line ===\n")

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

7.3 Bar Charts — State Proportions

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

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

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

7.4 Heatmaps — State and Bin Proportions

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

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


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

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

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

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

8 Pseudotime Analysis

8.1 Pseudotime Distribution: Reference vs Malignant

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

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

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

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

p_density | p_ridge

8.2 Pseudotime Summary Statistics

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

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

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

cat("\n=== Reference Pseudotime per Azimuth l2 State ===\n")

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

9 Integrated Summary

9.1 Four-Panel Summary Figure

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

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

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

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

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

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

9.2 Pseudotime Violin by Transferred State

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

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

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

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

9.3 Key Comparison: Label vs Pseudotime Bin

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

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

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

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

10 Save Outputs

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

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

library(SeuratObject)

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



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

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

cat("✅ All outputs saved to:", out_dir, "\n\n")
✅ All outputs saved to: results/MalignantCD4T_Monocle3_projection 
cat("Files:\n")
Files:
print(list.files(out_dir))
[1] "MalignantCD4T_full_metadata_with_projection.csv"           "MalignantCD4T_mapped_monocle3_pseudotime_noProlif_ref.Rds"
[3] "label_vs_pseudotime_bin_crosstable.csv"                    "pseudotime_bin_boundaries.csv"                            
[5] "pseudotime_bins_per_cellline.csv"                          "pseudotime_stats_per_cellline.csv"                        
[7] "state_distribution_overall.csv"                            "state_distribution_per_cellline.csv"                      

11 Session Info

sessionInfo()
LS0tCnRpdGxlOiAiU8OpemFyeSBDZWxsIFBzZXVkb3RpbWUgTWFwcGluZyBvbnRvIEhlYWx0aHkgQ0Q0KyBUIENlbGwgRGlmZmVyZW50aWF0aW9uIFRyYWplY3RvcnkiCnN1YnRpdGxlOiAiTW9ub2NsZTMgcmVmZXJlbmNlIHRyYWplY3Rvcnkgd2l0aCBzdGF0ZS1hbmNob3JlZCBwc2V1ZG90aW1lIGJpbnMiCmF1dGhvcjogIk5hc2lyIE1haG1vb2QgQWJiYXNpIgpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlCiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDoKICAgICAgY29sbGFwc2VkOiB0cnVlCiAgICB0aGVtZTogam91cm5hbAogICAgaGlnaGxpZ2h0OiB0YW5nbwogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IHRydWUKICAgIGRmX3ByaW50OiBwYWdlZAogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlCi0tLQoKPCEtLSAgIOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkCAtLT4KPCEtLSAgIFNDSUVOVElGSUMgUkFUSU9OQUxFICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC0tPgo8IS0tICAg4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQIC0tPgo8IS0tICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLS0+CjwhLS0gICBRVUVTVElPTjogV2hlcmUgYWxvbmcgdGhlIG5vcm1hbCBDRDQrIFQgY2VsbCBkaWZmZXJlbnRpYXRpb24gYXhpcyBkbyAgICAtLT4KPCEtLSAgIFPDqXphcnkgY2VsbHMgYXJyZXN0IG9yIG9yaWdpbmF0ZT8gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAtLT4KPCEtLSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC0tPgo8IS0tICAgQVBQUk9BQ0g6ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLS0+CjwhLS0gICAgIFN0ZXAgMSDigJQgQnVpbGQgTW9ub2NsZTMgdHJhamVjdG9yeSBvbiBIRUFMVEhZIHJlZmVyZW5jZSBDRDQrIFQgY2VsbHMgLS0+CjwhLS0gICAgICAgICAgICAgIFJvb3QgPSBDRDQgTmFpdmUgKENDUjcrU0VMTCtUQ0Y3KykgICAgICAgICAgICAgICAgICAgICAgICAgIC0tPgo8IS0tICAgICAgICAgICAgICBUd28gbGluZWFnZXM6ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLS0+CjwhLS0gICAgICAgICAgICAgICAgTGluZWFnZSAxOiBOYWl2ZSDihpIgVENNIOKGkiBURU0g4oaSIFRlbXJhL0NUTCAgKGVmZmVjdG9yIGF4aXMpIC0tPgo8IS0tICAgICAgICAgICAgICAgIExpbmVhZ2UgMjogTmFpdmUg4oaSIFRDTSDihpIgVHJlZyAgICAgICAgICAgICAgKHJlZ3VsYXRvcnkpICAgLS0+CjwhLS0gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAtLT4KPCEtLSAgICAgU3RlcCAyIOKAlCBGcmVlemUgcmVmZXJlbmNlIFVNQVAgKHJldHVybi5tb2RlbD1UUlVFKSAgICAgICAgICAgICAgICAgICAtLT4KPCEtLSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC0tPgo8IS0tICAgICBTdGVwIDMg4oCUIFByb2plY3QgbWFsaWduYW50IFPDqXphcnkgY2VsbHMgdmlhIE1hcFF1ZXJ5ICAgICAgICAgICAgICAgICAtLT4KPCEtLSAgICAgICAgICAgICAgQ2VsbHMgaW5oZXJpdCByZWZlcmVuY2UgVU1BUCBjb29yZGluYXRlcyArIHBzZXVkb3RpbWUgICAgICAgIC0tPgo8IS0tICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLS0+CjwhLS0gICAgIFN0ZXAgNCDigJQgSW50ZXJwcmV0IHR3byBjb21wbGVtZW50YXJ5IG91dHB1dHM6ICAgICAgICAgICAgICAgICAgICAgICAgLS0+CjwhLS0gICAgICAgICAgICAgIChhKSBMYWJlbCB0cmFuc2ZlciAg4oaSIENFTEwgT0YgT1JJR0lOICAgICAgICAgICAgICAgICAgICAgICAgLS0+CjwhLS0gICAgICAgICAgICAgICAgICBXaGljaCBub3JtYWwgc3RhdGUgZG9lcyB0aGlzIG1hbGlnbmFudCBjZWxsIHJlc2VtYmxlPyAgICAtLT4KPCEtLSAgICAgICAgICAgICAgKGIpIFBzZXVkb3RpbWUgYmluICDihpIgRElGRkVSRU5USUFUSU9OIFBPU0lUSU9OICAgICAgICAgICAgICAtLT4KPCEtLSAgICAgICAgICAgICAgICAgIFdoZXJlIGFsb25nIHRoZSBjb250aW51dW0gaXMgdGhpcyBjZWxsIHNpdHRpbmc/ICAgICAgICAgIC0tPgo8IS0tICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLS0+CjwhLS0gICBLRVkgQklPTE9HSUNBTCBNRVNTQUdFOiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAtLT4KPCEtLSAgIERpc2NvcmRhbmNlIGJldHdlZW4gKGEpIGFuZCAoYikgcmV2ZWFscyBwYXJ0aWFsIGVmZmVjdG9yIHNrZXdpbmcg4oCUICAgICAtLT4KPCEtLSAgIGNlbGxzIGxhYmVsbGVkIFRDTSBieSBpZGVudGl0eSBidXQgcG9zaXRpb25lZCBhdCBURU0vVGVtcmEgYnkgICAgICAgICAgLS0+CjwhLS0gICBwc2V1ZG90aW1lID0gYXJyZXN0IGF0IHRoZSBUQ03ihpJURU0gdHJhbnNpdGlvbiBjaGVja3BvaW50LiAgICAgICAgICAgICAgLS0+CjwhLS0gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAtLT4KPCEtLSAgIFBTRVVET1RJTUUgQklOIERFU0lHTjogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLS0+CjwhLS0gICBCaW5zIGFyZSBhbmNob3JlZCB0byByZWZlcmVuY2UgU1RBVEUgTUVESUFOIHBzZXVkb3RpbWUg4oCUIE5PVCBhcmJpdHJhcnkgIC0tPgo8IS0tICAgcXVhbnRpbGUgY3V0cy4gQm91bmRhcmllcyA9IG1pZHBvaW50IGJldHdlZW4gYWRqYWNlbnQgc3RhdGUgbWVkaWFucy4gICAtLT4KPCEtLSAgIFRyZWctbGlrZSBiaW4gaXMgYXNzaWduZWQgYnkgQXppbXV0aCBsYWJlbCAoYnJhbmNoLCBub3QgbGluZWFyIGF4aXMpLiAgLS0+CjwhLS0gICDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZAgLS0+CgoKIyBMb2FkIExpYnJhcmllcwoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldCgKICBlY2hvICAgICAgID0gVFJVRSwKICB3YXJuaW5nICAgID0gRkFMU0UsCiAgbWVzc2FnZSAgICA9IEZBTFNFLAogIGZpZy53aWR0aCAgPSAxMiwKICBmaWcuaGVpZ2h0ID0gOAopCgojIENvcmUgc2luZ2xlLWNlbGwKbGlicmFyeShTZXVyYXQpCmxpYnJhcnkobW9ub2NsZTMpCmxpYnJhcnkoU2V1cmF0V3JhcHBlcnMpCgojIERhdGEgd3JhbmdsaW5nCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkodGliYmxlKQoKIyBWaXN1YWxpc2F0aW9uCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShwYXRjaHdvcmspCmxpYnJhcnkoUkNvbG9yQnJld2VyKQpsaWJyYXJ5KHZpcmlkaXMpCmxpYnJhcnkoZ2dyaWRnZXMpCmxpYnJhcnkoZ2dyZXBlbCkKCiMgU3RhdGlzdGljcyAmIHRhYmxlcwpsaWJyYXJ5KE1hdHJpeCkKbGlicmFyeShzY2FsZXMpCmxpYnJhcnkocGhlYXRtYXApCgpvcHRpb25zKGZ1dHVyZS5nbG9iYWxzLm1heFNpemUgPSA4ZTkpCnNldC5zZWVkKDEyMzQpCgojIOKUgOKUgCBBemltdXRoIGwyIGNvbG91ciBwYWxldHRlIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgAojIEJpb2xvZ2ljYWxseSBvcmRlcmVkOiBibHVlIChuYWl2ZSkg4oaSIHllbGxvdyAobWVtb3J5KSDihpIgcmVkIChlZmZlY3RvcikKIyBwdXJwbGUgKFRyZWcpLiBBbGwga25vd24gQXppbXV0aCBsMiBDRDQgbGFiZWwgdmFyaWFudHMgaW5jbHVkZWQuCmF6aW11dGhfbDJfY29sb3JzIDwtIGMoCiAgIyBOYWl2ZQogICJDRDQgTmFpdmUiICAgICAgICAgPSAiIzIxNjZBQyIsCiAgIkNENCBUTiIgICAgICAgICAgICA9ICIjMjE2NkFDIiwKICAjIENlbnRyYWwgTWVtb3J5CiAgIkNENCBUQ00iICAgICAgICAgICA9ICIjNzRBREQxIiwKICAiQ0Q0IFRDTV8xIiAgICAgICAgID0gIiM5RUNBRTEiLAogICJDRDQgVENNXzIiICAgICAgICAgPSAiIzZCQUVENiIsCiAgIkNENCBUQ01fMyIgICAgICAgICA9ICIjNDI5MkM2IiwKICAjIEVmZmVjdG9yIE1lbW9yeQogICJDRDQgVEVNIiAgICAgICAgICAgPSAiI0ZFRTA5MCIsCiAgIkNENCBURU1fMSIgICAgICAgICA9ICIjRkREMEEyIiwKICAiQ0Q0IFRFTV8yIiAgICAgICAgID0gIiNGREFFNkIiLAogICJDRDQgVEVNXzMiICAgICAgICAgPSAiI0ZEOEQzQyIsCiAgIkNENCBURU1fNCIgICAgICAgICA9ICIjRDk0ODAxIiwKICAjIFRlcm1pbmFsIEVmZmVjdG9yIOKAlCBhbGwga25vd24gQXppbXV0aCBsYWJlbCB2YXJpYW50cwogICJDRDQgVGVtcmEvQ1RMIiAgICAgPSAiI0Q3MzAyNyIsCiAgIkNENCBURU1SQSIgICAgICAgICA9ICIjRDczMDI3IiwKICAiQ0Q0IFRFTVJBL0NUTCIgICAgID0gIiNENzMwMjciLAogICJDRDQgQ1RMIiAgICAgICAgICAgPSAiI0Q3MzAyNyIsCiAgIyBSZWd1bGF0b3J5CiAgIlRyZWciICAgICAgICAgICAgICA9ICIjNzYyQTgzIiwKICAiVHJlZyBOYWl2ZSIgICAgICAgID0gIiM5OTcwQUIiLAogICJUcmVnIE1lbW9yeSIgICAgICAgPSAiIzc2MkE4MyIsCiAgIyBPdGhlciBzdWJzZXRzIEF6aW11dGggbDIgbWF5IHJldHVybgogICJDRDQgUHJvbGlmZXJhdGluZyIgPSAiI0E2QTZBNiIsCiAgIlRoMSIgICAgICAgICAgICAgICA9ICIjRjQ2RDQzIiwKICAiVGgxNyIgICAgICAgICAgICAgID0gIiNGREFFNjEiLAogICJUaDEvVGgxNyIgICAgICAgICAgPSAiI0ZFRTA4QiIsCiAgIlRmaCIgICAgICAgICAgICAgICA9ICIjNjZDMkE1IgopCgojIOKUgOKUgCBQc2V1ZG90aW1lIGJpbiBjb2xvdXJzIOKAlCA1IGJpbnMsIGJvdGggbGluZWFnZXMg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACiMgQ29sb3VycyBkZWxpYmVyYXRlbHkgbWF0Y2ggdGhlaXIgY29ycmVzcG9uZGluZyBBemltdXRoIGwyIHN0YXRlIGFib3ZlCiMgc28gZmlndXJlcyBhcmUgdmlzdWFsbHkgY29uc2lzdGVudCB0aHJvdWdob3V0IHRoZSBkb2N1bWVudC4KYmluX2NvbG9ycyA8LSBjKAogICJOYWl2ZS1saWtlIiAgPSAiIzIxNjZBQyIsICAjIGRhcmsgYmx1ZSAgIOKAlCBDRDQgTmFpdmUKICAiVENNLWxpa2UiICAgID0gIiM3NEFERDEiLCAgIyBza3kgYmx1ZSAgICDigJQgQ0Q0IFRDTQogICJUcmVnLWxpa2UiICAgPSAiIzc2MkE4MyIsICAjIGRhcmsgcHVycGxlIOKAlCBUcmVnIGJyYW5jaAogICJURU0tbGlrZSIgICAgPSAiI0ZFRTA5MCIsICAjIHBhbGUgeWVsbG93IOKAlCBDRDQgVEVNCiAgIlRlbXJhLWxpa2UiICA9ICIjRDczMDI3IiAgICMgcmVkICAgICAgICAg4oCUIENENCBUZW1yYS9DVEwKKQoKbWFsaWduYW50X2NvbG9yIDwtICIjRTMxQTFDIiAgICMgdml2aWQgcmVkIGZvciBtYWxpZ25hbnQgb3ZlcmxheQpyZWZlcmVuY2VfY29sb3IgPC0gImdyZXk4NSIgICAgIyBiYWNrZ3JvdW5kIGZvciByZWZlcmVuY2UgY2VsbHMKCiMg4pSA4pSAIEhlbHBlcjogZXh0ZW5kIHBhbGV0dGUgZm9yIHVuZXhwZWN0ZWQgbGFiZWxzIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgApleHRlbmRfcGFsZXR0ZSA8LSBmdW5jdGlvbihwYWxldHRlLCBsYWJlbHMpIHsKICBtaXNzaW5nX2xhYmVscyA8LSBzZXRkaWZmKGxhYmVscywgbmFtZXMocGFsZXR0ZSkpCiAgaWYgKGxlbmd0aChtaXNzaW5nX2xhYmVscykgPiAwKSB7CiAgICBleHRyYV9jb2xzIDwtIGNvbG9yUmFtcFBhbGV0dGUoCiAgICAgIFJDb2xvckJyZXdlcjo6YnJld2VyLnBhbCg4LCAiRGFyazIiKSkobGVuZ3RoKG1pc3NpbmdfbGFiZWxzKSkKICAgIG5hbWVzKGV4dHJhX2NvbHMpIDwtIG1pc3NpbmdfbGFiZWxzCiAgICBwYWxldHRlIDwtIGMocGFsZXR0ZSwgZXh0cmFfY29scykKICB9CiAgcGFsZXR0ZQp9CmBgYAoKCiMgTG9hZCBPYmplY3RzCgojIyBMb2FkIFJlZmVyZW5jZSAoSGVhbHRoeSBDRDQrIFQgQ2VsbHMgd2l0aCBUcmFqZWN0b3J5KQoKYGBge3IgbG9hZC1yZWZlcmVuY2V9CiMg4pSA4pSAIENSSVRJQ0FMIHJlcXVpcmVtZW50cyBmb3IgdGhpcyBvYmplY3Qg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACiMgICDinIUgQ0Q0IFByb2xpZmVyYXRpbmcgY2x1c3RlciByZW1vdmVkIChNS0k2NysvVE9QMkErL0NESzErKQojICAg4pyFIFVNQVAgYnVpbHQgd2l0aCByZXR1cm4ubW9kZWwgPSBUUlVFIChyZXF1aXJlZCBmb3IgTWFwUXVlcnkpCiMgICDinIUgbW9ub2NsZTNfcHNldWRvdGltZSBjb2x1bW4gKHdpbGwgYmUgcmVjb21wdXRlZCBoZXJlIGZvciByZXByb2R1Y2liaWxpdHkpCiMgICDinIUgcHJlZGljdGVkLmNlbGx0eXBlLmwyIChBemltdXRoIGwyIGxhYmVscykKIyAgIOKchSBjZWxsX3R5cGUgYW5ub3RhdGlvbiAoY2x1c3RlcnMgMC02KQoKcmVmZXJlbmNlX2ludGVncmF0ZWQgPC0gcmVhZFJEUygKICAiLi4vMS1GaW5hbF9DdXN0b21fTVNUX01vbm9jbGUzX1RyYWplY3RvcnlfYW5kX21hcHBpbmcvMS1DdXN0b21fTVNUL09iamVjdHMvY2Q0X3JlZl9kdWFsX3RyYWplY3RvcnkucmRzIgopCgpjYXQoIj09PSBSZWZlcmVuY2Ugb2JqZWN0IHN1bW1hcnkgPT09XG4iKQpjYXQoIkNlbGxzICAgICA6IiwgbmNvbChyZWZlcmVuY2VfaW50ZWdyYXRlZCksICJcbiIpCmNhdCgiQXNzYXlzICAgIDoiLCBwYXN0ZShuYW1lcyhyZWZlcmVuY2VfaW50ZWdyYXRlZEBhc3NheXMpLCBjb2xsYXBzZSA9ICIsICIpLCAiXG4iKQpjYXQoIlJlZHVjdGlvbnM6IiwgcGFzdGUobmFtZXMocmVmZXJlbmNlX2ludGVncmF0ZWRAcmVkdWN0aW9ucyksIGNvbGxhcHNlID0gIiwgIiksICJcbiIpCmNhdCgiVU1BUCBtb2RlbDoiLCAhaXMubnVsbChyZWZlcmVuY2VfaW50ZWdyYXRlZEByZWR1Y3Rpb25zJHVtYXBAbWlzYyRtb2RlbCksICJcbiIpCmNhdCgiQ2x1c3RlcnMgIDoiLAogICAgcGFzdGUoc29ydCh1bmlxdWUocmVmZXJlbmNlX2ludGVncmF0ZWQkc2V1cmF0X2NsdXN0ZXJzKSksIGNvbGxhcHNlID0gIiwgIiksICJcbiIpCgojIOKUgOKUgCBDb25maXJtIENENCBQcm9saWZlcmF0aW5nIGNlbGxzIGFic2VudCDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKaWYgKGFueShncmVwbCgiUHJvbGlmfHByb2xpZnxjeWNsaW5nfEN5Y2xpbmciLAogICAgICAgICAgICAgIHJlZmVyZW5jZV9pbnRlZ3JhdGVkJGNlbGxfdHlwZSwgaWdub3JlLmNhc2UgPSBUUlVFKSkpIHsKICB3YXJuaW5nKCLimqDvuI8gIFByb2xpZmVyYXRpbmcgY2VsbF90eXBlIGxhYmVscyBkZXRlY3RlZCDigJQgcmVtb3ZlIGJlZm9yZSBwcm9jZWVkaW5nLiIpCn0gZWxzZSB7CiAgY2F0KCLinIUgTm8gcHJvbGlmZXJhdGluZyBjZWxsX3R5cGUgbGFiZWxzXG4iKQp9Cgpwcm9saWZfbWFya2VycyAgPC0gYygiTUtJNjciLCAiVE9QMkEiLCAiUENOQSIsICJDREsxIiwgIlNUTU4xIikKcHJvbGlmX3ByZXNlbnQgIDwtIGludGVyc2VjdChwcm9saWZfbWFya2Vycywgcm93bmFtZXMocmVmZXJlbmNlX2ludGVncmF0ZWQpKQppZiAobGVuZ3RoKHByb2xpZl9wcmVzZW50KSA+IDApIHsKICBEZWZhdWx0QXNzYXkocmVmZXJlbmNlX2ludGVncmF0ZWQpIDwtICJTQ1QiCiAgcHJvbGlmX2V4cHIgPC0gTWF0cml4Ojpjb2xNZWFucygKICAgIEdldEFzc2F5RGF0YShyZWZlcmVuY2VfaW50ZWdyYXRlZCwgbGF5ZXIgPSAiZGF0YSIpW3Byb2xpZl9wcmVzZW50LCAsIGRyb3AgPSBGQUxTRV0KICApCiAgY2F0KCJQcm9saWYgbWFya2VyIHNjb3JlIOKAlCBtYXg6Iiwgcm91bmQobWF4KHByb2xpZl9leHByKSwgMyksCiAgICAgICJ8IGNlbGxzID4gMC41OiIsIHN1bShwcm9saWZfZXhwciA+IDAuNSksICJcbiIpCiAgaWYgKHN1bShwcm9saWZfZXhwciA+IDAuNSkgPiA1MCkgewogICAgd2FybmluZygi4pqg77iPICBTdWJzdGFudGlhbCBwcm9saWZlcmF0aW9uIHNpZ25hbCDigJQgdmVyaWZ5IHJlbW92YWwgd2FzIGNvbXBsZXRlLiIpCiAgfSBlbHNlIHsKICAgIGNhdCgi4pyFIFByb2xpZmVyYXRpbmcgY2VsbHMgY29uZmlybWVkIGFic2VudFxuIikKICB9Cn0KCmNhdCgiXG5DZWxsIHR5cGUgZGlzdHJpYnV0aW9uIChyZWZlcmVuY2UpOlxuIikKcHJpbnQodGFibGUocmVmZXJlbmNlX2ludGVncmF0ZWQkY2VsbF90eXBlKSkKY2F0KCJcbkF6aW11dGggbDIgZGlzdHJpYnV0aW9uIChyZWZlcmVuY2UpOlxuIikKcHJpbnQodGFibGUocmVmZXJlbmNlX2ludGVncmF0ZWQkcHJlZGljdGVkLmNlbGx0eXBlLmwyKSkKCiMgRXh0ZW5kIHBhbGV0dGUgdG8gY292ZXIgYW55IGxhYmVscyBub3QgcHJlLXNlZWRlZCBhYm92ZQpyZWZfbDJfbGFiZWxzICAgICAgPC0gdW5pcXVlKGFzLmNoYXJhY3RlcihyZWZlcmVuY2VfaW50ZWdyYXRlZCRwcmVkaWN0ZWQuY2VsbHR5cGUubDIpKQpyZWZfbDJfbGFiZWxzICAgICAgPC0gcmVmX2wyX2xhYmVsc1shaXMubmEocmVmX2wyX2xhYmVscyldCmF6aW11dGhfbDJfY29sb3JzICA8LSBleHRlbmRfcGFsZXR0ZShhemltdXRoX2wyX2NvbG9ycywgcmVmX2wyX2xhYmVscykKY2F0KCJcbkNvbG91ciBwYWxldHRlIGNvdmVycyIsIGxlbmd0aChyZWZfbDJfbGFiZWxzKSwgInJlZmVyZW5jZSBsYWJlbHMg4pyFXG4iKQoKIyDilIDilIAgSGFyZCBzdG9wOiBVTUFQIG1vZGVsIG11c3QgZXhpc3QgZm9yIE1hcFF1ZXJ5IOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgAppZiAoaXMubnVsbChyZWZlcmVuY2VfaW50ZWdyYXRlZEByZWR1Y3Rpb25zJHVtYXBAbWlzYyRtb2RlbCkpIHsKICBzdG9wKAogICAgIuKdjCBVTUFQIG1vZGVsIG1pc3NpbmcuXG4iLAogICAgIiAgIFJlLXJ1bjogcmVmZXJlbmNlX2ludGVncmF0ZWQgPC0gUnVuVU1BUChyZWZlcmVuY2VfaW50ZWdyYXRlZCxcbiIsCiAgICAiICAgICAgICAgICAgIGRpbXMgPSAxOjIwLCByZXR1cm4ubW9kZWwgPSBUUlVFKVxuIiwKICAgICIgICBUaGVuIHJlLXNhdmUgYW5kIHJlbG9hZC4iCiAgKQp9IGVsc2UgewogIGNhdCgi4pyFIFVNQVAgbW9kZWwgcHJlc2VudCDigJQgTWFwUXVlcnkgcHJvamVjdGlvbiByZWFkeVxuIikKfQpgYGAKCiMjIExvYWQgJiBQcmVwYXJlIE1hbGlnbmFudCBDRDQrIFQgQ2VsbHMg4oCUIFBlci1DZWxsLUxpbmUgU0NUcmFuc2Zvcm0KCmBgYHtyIGxvYWQtbWFsaWduYW50fQpBbGxfc2FtcGxlc19NZXJnZWQgPC0gcmVhZFJEUygKICAiL2hvbWUvbmFiYmFzaS9hcG9sbG9faG9tZS8xLVNldXJhdF9SRFNfT0JKRUNUX0ZJTkFML0FsbF9zYW1wbGVzX01lcmdlZF93aXRoX1JlbmFtZWRfQ2x1c3RlcnNfQ2VsbF9zdGF0ZS0wMy0xMi0yMDI1LnJkcy5yZHMiCikKCmNhdCgiQWxsIGNlbGwgbGluZXM6XG4iKQpwcmludCh0YWJsZShBbGxfc2FtcGxlc19NZXJnZWQkY2VsbF9saW5lKSkKCgpBbGxfc2FtcGxlc19NZXJnZWQkR3JvdXAgPC0gaWZlbHNlKAogIEFsbF9zYW1wbGVzX01lcmdlZCRjZWxsX2xpbmUgJWluJSBwYXN0ZTAoIkwiLCAxOjcpLCAiTWFsaWduYW50Q0Q0VCIsCiAgaWZlbHNlKAogICAgQWxsX3NhbXBsZXNfTWVyZ2VkJGNlbGxfbGluZSAlaW4lCiAgICAgIGMoIkNENFRjZWxsc19sYWIiLCAiQ0Q0VGNlbGxzXzEweCIpLCAgCiAgICAiTm9ybWFsQ0Q0VCIsICJPdGhlciIKICApCikKCmNhdCgiXG5Hcm91cCBkaXN0cmlidXRpb246XG4iKQpwcmludCh0YWJsZShBbGxfc2FtcGxlc19NZXJnZWQkR3JvdXApKQoKTWFsaWduYW50Q0Q0VF9yYXcgPC0gc3Vic2V0KEFsbF9zYW1wbGVzX01lcmdlZCwgc3Vic2V0ID0gR3JvdXAgPT0gIk1hbGlnbmFudENENFQiKQpjYXQoIlxuTWFsaWduYW50IENENFQgY2VsbHM6IiwgbmNvbChNYWxpZ25hbnRDRDRUX3JhdyksICJcbiIpCnByaW50KHRhYmxlKE1hbGlnbmFudENENFRfcmF3JGNlbGxfbGluZSkpCgpybShBbGxfc2FtcGxlc19NZXJnZWQpOyBnYygpCmBgYAoKYGBge3IgcGVyLWxpbmUtc2N0fQojIOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkAojIFBFUi1DRUxMLUxJTkUgU0NUcmFuc2Zvcm0KIyDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZAKIyBXSFk6IEVhY2ggY2VsbCBsaW5lIGlzIGEgc2VwYXJhdGUgbGlicmFyeSB3aXRoIGRpZmZlcmVudCBzZXF1ZW5jaW5nIGRlcHRoCiMgYW5kIGFtYmllbnQgUk5BIHByb2ZpbGUuIEEgc2luZ2xlIG1lcmdlZCBTQ1QgbW9kZWwgY29uZmxhdGVzIGJhdGNoCiMgdmFyaWF0aW9uIHdpdGggYmlvbG9neS4gUGVyLWxpbmUgU0NUIGNvcnJlY3RzIGZvciB0aGlzIGJlZm9yZSBwcm9qZWN0aW9uLgojCiMgQVBQUk9BQ0g6CiMgICAxLiBTcGxpdCBieSBjZWxsX2xpbmUKIyAgIDIuIFNDVHJhbnNmb3JtIGVhY2ggbGluZSAocmVncmVzcyBwZXJjZW50Lm10ICsgY2VsbCBjeWNsZSBzY29yZXMpCiMgICAzLiBTZWxlY3QgSFZHcyBieSBjcm9zcy1saW5lIGZyZXF1ZW5jeSDigJQgbW9zdCByb2J1c3QgdG8gYmF0Y2ggZWZmZWN0cwojICAgNC4gTWVyZ2UsIHNjYWxlLCBQQ0Eg4oCUIHJlYWR5IGZvciBGaW5kVHJhbnNmZXJBbmNob3JzCiMg4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQCgpjYXQoIj09PSBQZXItY2VsbC1saW5lIFNDVHJhbnNmb3JtID09PVxuIikKCiMgU2F2ZSBuYW1lcyBCRUZPUkUgbGFwcGx5IHRvIGF2b2lkIGRvdWJsZSBTcGxpdE9iamVjdCBjYWxsCiMgRklYOiBvcmlnaW5hbCBzY3JpcHQgY2FsbGVkIFNwbGl0T2JqZWN0IHR3aWNlIOKAlCB3YXN0ZWZ1bCBhbmQgcmlza3kKY2VsbF9saW5lX25hbWVzIDwtIG5hbWVzKFNwbGl0T2JqZWN0KE1hbGlnbmFudENENFRfcmF3LCBzcGxpdC5ieSA9ICJjZWxsX2xpbmUiKSkKY2VsbF9saW5lX2xpc3QgIDwtIFNwbGl0T2JqZWN0KE1hbGlnbmFudENENFRfcmF3LCBzcGxpdC5ieSA9ICJjZWxsX2xpbmUiKQoKY2F0KCJMaW5lcyB0byBwcm9jZXNzOiIsIHBhc3RlKGNlbGxfbGluZV9uYW1lcywgY29sbGFwc2UgPSAiLCAiKSwgIlxuXG4iKQoKY2VsbF9saW5lX2xpc3QgPC0gbGFwcGx5KGNlbGxfbGluZV9uYW1lcywgZnVuY3Rpb24obGluZV9uYW1lKSB7CiAgb2JqIDwtIGNlbGxfbGluZV9saXN0W1tsaW5lX25hbWVdXQogIGNhdCgiUHJvY2Vzc2luZzoiLCBsaW5lX25hbWUsICJ8IENlbGxzOiIsIG5jb2wob2JqKSwgIlxuIikKCiAgaWYgKCEicGVyY2VudC5tdCIgJWluJSBjb2xuYW1lcyhvYmpAbWV0YS5kYXRhKSkKICAgIG9ialtbInBlcmNlbnQubXQiXV0gPC0gUGVyY2VudGFnZUZlYXR1cmVTZXQob2JqLCBwYXR0ZXJuID0gIl5NVC0iKQoKICAjIENlbGwgY3ljbGUgcmVncmVzc2lvbiByZW1vdmVzIGN5Y2xpbmcgYXJ0ZWZhY3RzIHdpdGhvdXQgcmVtb3ZpbmcgY2VsbHMKICB2YXJzX3JlZ3Jlc3MgPC0gdHJ5Q2F0Y2goewogICAgb2JqIDwtIENlbGxDeWNsZVNjb3Jpbmcob2JqLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgcy5mZWF0dXJlcyAgID0gY2MuZ2VuZXMkcy5nZW5lcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGcybS5mZWF0dXJlcyA9IGNjLmdlbmVzJGcybS5nZW5lcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNldC5pZGVudCAgICA9IEZBTFNFKQogICAgY2F0KCIgIOKGkiBDZWxsIGN5Y2xlIHNjb3JlcyBjb21wdXRlZFxuIikKICAgIGMoInBlcmNlbnQubXQiLCAiUy5TY29yZSIsICJHMk0uU2NvcmUiKQogIH0sIGVycm9yID0gZnVuY3Rpb24oZSkgewogICAgY2F0KCIgIOKaoO+4jyAgQ2VsbCBjeWNsZSBzY29yaW5nIGZhaWxlZCDigJQgcmVncmVzc2luZyBwZXJjZW50Lm10IG9ubHlcbiIpCiAgICAicGVyY2VudC5tdCIKICB9KQoKICBvYmogPC0gU0NUcmFuc2Zvcm0ob2JqLAogICAgICAgICAgICAgICAgICAgICB2YXJzLnRvLnJlZ3Jlc3MgICAgID0gdmFyc19yZWdyZXNzLAogICAgICAgICAgICAgICAgICAgICB2YXJpYWJsZS5mZWF0dXJlcy5uID0gMzAwMCwKICAgICAgICAgICAgICAgICAgICAgdnN0LmZsYXZvciAgICAgICAgICA9ICJ2MiIsCiAgICAgICAgICAgICAgICAgICAgIHZlcmJvc2UgICAgICAgICAgICAgPSBGQUxTRSkKICBjYXQoIiAg4pyFIiwgbGluZV9uYW1lLCAiY29tcGxldGUgfCIsIGxlbmd0aChWYXJpYWJsZUZlYXR1cmVzKG9iaikpLCAiSFZHc1xuIikKICByZXR1cm4ob2JqKQp9KQpuYW1lcyhjZWxsX2xpbmVfbGlzdCkgPC0gY2VsbF9saW5lX25hbWVzICAjIHJldXNlIHNhdmVkIG5hbWVzIOKAlCBubyBzZWNvbmQgU3BsaXRPYmplY3QKCiMg4pSA4pSAIFNlbGVjdCBIVkdzIGJ5IGNyb3NzLWxpbmUgZnJlcXVlbmN5IOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgAphbGxfaHZnX2xpc3RzIDwtIGxhcHBseShjZWxsX2xpbmVfbGlzdCwgVmFyaWFibGVGZWF0dXJlcykKaHZnX2ZyZXEgICAgICA8LSBzb3J0KHRhYmxlKHVubGlzdChhbGxfaHZnX2xpc3RzKSksIGRlY3JlYXNpbmcgPSBUUlVFKQpuX2h2Z3MgICAgICAgIDwtIG1pbigzMDAwLCBsZW5ndGgoaHZnX2ZyZXEpKQpzaGFyZWRfaHZncyAgIDwtIG5hbWVzKGh2Z19mcmVxKVsxOm5faHZnc10KCmNhdCgiXG5IVkcgc2VsZWN0aW9uOlxuIikKY2F0KCIgIFZhcmlhYmxlIGluIGFsbCIsIGxlbmd0aChjZWxsX2xpbmVfbGlzdCksICJsaW5lczoiLAogICAgc3VtKGh2Z19mcmVxID09IGxlbmd0aChjZWxsX2xpbmVfbGlzdCkpLCAiXG4iKQpjYXQoIiAgVmFyaWFibGUgaW4g4omlMyBsaW5lczoiLCBzdW0oaHZnX2ZyZXEgPj0gMyksICJcbiIpCmNhdCgiICBGaW5hbCBIVkcgc2V0OiIsIG5faHZncywgIlxuIikKCiMg4pSA4pSAIE1lcmdlLCBzY2FsZSwgUENBIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgApNYWxpZ25hbnRDRDRUIDwtIG1lcmdlKGNlbGxfbGluZV9saXN0W1sxXV0sIHkgPSBjZWxsX2xpbmVfbGlzdFstMV0sCiAgICAgICAgICAgICAgICAgICAgICAgbWVyZ2UuZGF0YSA9IFRSVUUpClZhcmlhYmxlRmVhdHVyZXMoTWFsaWduYW50Q0Q0VCkgPC0gc2hhcmVkX2h2Z3MKCmNhdCgiXG5SdW5uaW5nIFBDQSBvbiBtZXJnZWQgb2JqZWN0Li4uXG4iKQpNYWxpZ25hbnRDRDRUIDwtIFNjYWxlRGF0YShNYWxpZ25hbnRDRDRULCBmZWF0dXJlcyA9IHNoYXJlZF9odmdzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgYXNzYXkgPSAiU0NUIiwgdmVyYm9zZSA9IEZBTFNFKQpNYWxpZ25hbnRDRDRUIDwtIFJ1blBDQShNYWxpZ25hbnRDRDRULCBmZWF0dXJlcyA9IHNoYXJlZF9odmdzLAogICAgICAgICAgICAgICAgICAgICAgICAgYXNzYXkgPSAiU0NUIiwgbnBjcyA9IDMwLCB2ZXJib3NlID0gRkFMU0UpCgpjYXQoIuKchSBNZXJnZWQgb2JqZWN0IHJlYWR5XG4iKQpjYXQoIiAgQ2VsbHM6IiwgbmNvbChNYWxpZ25hbnRDRDRUKSwgIlxuIikKY2F0KCIgIEhWR3M6IiwgbGVuZ3RoKFZhcmlhYmxlRmVhdHVyZXMoTWFsaWduYW50Q0Q0VCkpLCAiXG4iKQpjYXQoIiAgUENBIGRpbXM6IiwgbmNvbChFbWJlZGRpbmdzKE1hbGlnbmFudENENFQsICJwY2EiKSksICJcblxuIikKcHJpbnQodGFibGUoTWFsaWduYW50Q0Q0VCRjZWxsX2xpbmUpKQoKcm0oTWFsaWduYW50Q0Q0VF9yYXcsIGNlbGxfbGluZV9saXN0KTsgZ2MoKQpgYGAKIyMgdmFyaWFibGUgZ2VuZXMgYWxsIDcgY2VsbCBsaW5lIGNoZWNrCmBgYHtyfQojIEdlbmVzIHZhcmlhYmxlIGluIEFMTCA3IGxpbmVzCmh2Z19hbGw3IDwtIG5hbWVzKGh2Z19mcmVxW2h2Z19mcmVxID09IDddKQpjYXQoIkdlbmVzIHZhcmlhYmxlIGluIGFsbCA3IGxpbmVzOiIsIGxlbmd0aChodmdfYWxsNyksICJcbiIpCnByaW50KGh2Z19hbGw3KQoKCgojIENoZWNrIHdoaWNoIGNhbm9uaWNhbCBUIGNlbGwgbWFya2VycyBhcmUgaW4gdGhlIGFsbC03IHNldAp0X2NlbGxfbWFya2VycyA8LSBjKAogICMgTmFpdmUvbWVtb3J5CiAgIkNDUjciLCAiU0VMTCIsICJUQ0Y3IiwgIklMN1IiLCAiTEVGMSIsICJLTEYyIiwKICAjIEFjdGl2YXRpb24vZXhoYXVzdGlvbiAgCiAgIlRPWCIsICJQRENEMSIsICJMQUczIiwgIlRJR0lUIiwgIkNUTEE0IiwgIkhBVkNSMiIsCiAgIyBFZmZlY3RvcgogICJHWk1CIiwgIkdaTUsiLCAiR1pNQSIsICJQUkYxIiwgIklGTkciLCAiVE5GIiwKICAjIFRyZWcKICAiRk9YUDMiLCAiSUwyUkEiLCAiSUtaRjIiLCAiQ1RMQTQiLAogICMgU8OpemFyeSBzcGVjaWZpYwogICJLSVIzREwyIiwgIlBMUzMiLCAiVFdJU1QxIiwgIkVQSEE0IiwgIkNEMTY0IiwKICAjIFByb2xpZmVyYXRpb24KICAiTUtJNjciLCAiVE9QMkEiLCAiQ0RLMSIKKQoKZm91bmRfaW5fYWxsNyA8LSBpbnRlcnNlY3QodF9jZWxsX21hcmtlcnMsIGh2Z19hbGw3KQpjYXQoIlxuQ2Fub25pY2FsIG1hcmtlcnMgaW4gYWxsLTcgSFZHIHNldDpcbiIpCnByaW50KGZvdW5kX2luX2FsbDcpCgpub3RfZm91bmQgPC0gc2V0ZGlmZih0X2NlbGxfbWFya2VycywgaHZnX2FsbDcpCmNhdCgiXG5NYXJrZXJzIE5PVCBpbiBhbGwtNyBzZXQ6XG4iKQpwcmludChub3RfZm91bmQpCgoKCmBgYAoKIyMgdmFyaWFibGUgZ2VuZXMgYWxsIDcgY2VsbCBsaW5lIGNoZWNrCmBgYHtyfQojIENoZWNrIHdoYXQganVuayBnZW5lcyBhcmUgaW4geW91ciBjdXJyZW50IEhWRyBzZXQKIyBiZWZvcmUgZmluZC1hbmNob3JzIHJ1bnMKCmNhdCgiPT09IEp1bmsgZ2VuZSBjaGVjayBvbiBzaGFyZWRfaHZncyA9PT1cbiIpCgptdF9pbl9odmdzIDwtIHNoYXJlZF9odmdzW2dyZXBsKCJeTVQtIiwgc2hhcmVkX2h2Z3MpXQpjYXQoIk1UIGdlbmVzIGluIHNoYXJlZF9odmdzOiIsIGxlbmd0aChtdF9pbl9odmdzKSwgIlxuIikKcHJpbnQobXRfaW5faHZncykKCnJpYm9faW5faHZncyA8LSBzaGFyZWRfaHZnc1tncmVwbCgiXlJQTHxeUlBTIiwgc2hhcmVkX2h2Z3MpXQpjYXQoIlxuUmlib3NvbWFsIGdlbmVzIGluIHNoYXJlZF9odmdzOiIsIGxlbmd0aChyaWJvX2luX2h2Z3MpLCAiXG4iKQpwcmludChyaWJvX2luX2h2Z3MpCgpoc3BfaW5faHZncyA8LSBzaGFyZWRfaHZnc1tncmVwbCgiXkhTUHxeSFNQQXxeSFNQQiIsIHNoYXJlZF9odmdzKV0KY2F0KCJcbkhlYXQgc2hvY2sgZ2VuZXMgaW4gc2hhcmVkX2h2Z3M6IiwgbGVuZ3RoKGhzcF9pbl9odmdzKSwgIlxuIikKcHJpbnQoaHNwX2luX2h2Z3MpCgpzbmhnX2luX2h2Z3MgPC0gc2hhcmVkX2h2Z3NbZ3JlcGwoIl5TTkhHfE1BTEFUMXxORUFUMSIsIHNoYXJlZF9odmdzKV0KY2F0KCJcbmxuY1JOQSBnZW5lcyBpbiBzaGFyZWRfaHZnczoiLCBsZW5ndGgoc25oZ19pbl9odmdzKSwgIlxuIikKcHJpbnQoc25oZ19pbl9odmdzKQoKY2F0KCJcblRvdGFsIGp1bmsgZ2VuZXMgdG8gYmUgZmlsdGVyZWQ6IiwgCiAgICBsZW5ndGgobXRfaW5faHZncykgKyBsZW5ndGgocmlib19pbl9odmdzKSArIAogICAgbGVuZ3RoKGhzcF9pbl9odmdzKSArIGxlbmd0aChzbmhnX2luX2h2Z3MpLCAiXG4iKQpgYGAKCgojIFJlZmVyZW5jZSBUcmFqZWN0b3J5IChNb25vY2xlMykKCiMjIEJ1aWxkIENEUyBmcm9tIFJlZmVyZW5jZQoKYGBge3IgYnVpbGQtY2RzfQoKY2F0KCI9PT0gQnVpbGRpbmcgTW9ub2NsZTMgQ0RTID09PVxuIikKCmNkcyA8LSBhcy5jZWxsX2RhdGFfc2V0KHJlZmVyZW5jZV9pbnRlZ3JhdGVkKQoKIyBUcmFuc2ZlciBmcm96ZW4gVU1BUCBjb29yZGluYXRlcyDigJQgdGhlc2UgZGVmaW5lIHRoZSB0cmFqZWN0b3J5IHNwYWNlCnJlZHVjZWREaW0oY2RzLCAiVU1BUCIpIDwtIEVtYmVkZGluZ3MocmVmZXJlbmNlX2ludGVncmF0ZWQsICJ1bWFwIikKCiMgU2luZ2xlIHBhcnRpdGlvbjogb25lIGNvbm5lY3RlZCBncmFwaCBhY3Jvc3MgYWxsIGhlYWx0aHkgY2VsbHMuCiMgQ0Q0IFByb2xpZmVyYXRpbmcgY2VsbHMgaGF2ZSBiZWVuIHJlbW92ZWQgc28gbm8gc3B1cmlvdXMgY2VsbC1jeWNsZQojIGJyYW5jaCB3aWxsIHB1bGwgdGhlIGdyYXBoIGF3YXkgZnJvbSB0aGUgZGlmZmVyZW50aWF0aW9uIGF4aXMuCnBhcnRpdGlvbl92ZWMgPC0gc2V0TmFtZXMoZmFjdG9yKHJlcCgxTCwgbmNvbChjZHMpKSksIGNvbG5hbWVzKGNkcykpCmNkc0BjbHVzdGVycyRVTUFQJHBhcnRpdGlvbnMgPC0gcGFydGl0aW9uX3ZlYwoKY2x1c3Rlcl92ZWMgPC0gc2V0TmFtZXMoCiAgZmFjdG9yKHJlZmVyZW5jZV9pbnRlZ3JhdGVkJHNldXJhdF9jbHVzdGVyc1tjb2xuYW1lcyhjZHMpXSksCiAgY29sbmFtZXMoY2RzKQopCmNkc0BjbHVzdGVycyRVTUFQJGNsdXN0ZXJzIDwtIGNsdXN0ZXJfdmVjCgojIFRyYW5zZmVyIG1ldGFkYXRhCmNvbERhdGEoY2RzKSRjZWxsX2xpbmUgICAgICAgICAgICAgPC0gcmVmZXJlbmNlX2ludGVncmF0ZWQkb3JpZy5pZGVudApjb2xEYXRhKGNkcykkY2VsbF90eXBlICAgICAgICAgICAgIDwtIHJlZmVyZW5jZV9pbnRlZ3JhdGVkJGNlbGxfdHlwZQpjb2xEYXRhKGNkcykkcHJlZGljdGVkLmNlbGx0eXBlLmwyIDwtIHJlZmVyZW5jZV9pbnRlZ3JhdGVkJHByZWRpY3RlZC5jZWxsdHlwZS5sMgpjb2xEYXRhKGNkcykkc2V1cmF0X2NsdXN0ZXJzICAgICAgIDwtIHJlZmVyZW5jZV9pbnRlZ3JhdGVkJGludGVncmF0ZWRfc25uX3Jlcy4wLjIKaWYgKCJvcmlnLmlkZW50IiAlaW4lIGNvbG5hbWVzKHJlZmVyZW5jZV9pbnRlZ3JhdGVkQG1ldGEuZGF0YSkpCiAgY29sRGF0YShjZHMpJHNhbXBsZSA8LSByZWZlcmVuY2VfaW50ZWdyYXRlZCRvcmlnLmlkZW50CgojIFZhbGlkYXRlIGFsbCByZXF1aXJlZCBzbG90cyBhcmUgbmFtZWQgY29ycmVjdGx5CnN0b3BpZm5vdCgKICAiY2x1c3RlcnMgbXVzdCBiZSBuYW1lZCIgICA9ICFpcy5udWxsKG5hbWVzKGNkc0BjbHVzdGVycyRVTUFQJGNsdXN0ZXJzKSksCiAgInBhcnRpdGlvbnMgbXVzdCBiZSBuYW1lZCIgPSAhaXMubnVsbChuYW1lcyhjZHNAY2x1c3RlcnMkVU1BUCRwYXJ0aXRpb25zKSksCiAgImNsdXN0ZXIgbGVuZ3RoIG1hdGNoZXMiICAgPSBsZW5ndGgoY2RzQGNsdXN0ZXJzJFVNQVAkY2x1c3RlcnMpICAgPT0gbmNvbChjZHMpLAogICJwYXJ0aXRpb24gbGVuZ3RoIG1hdGNoZXMiID0gbGVuZ3RoKGNkc0BjbHVzdGVycyRVTUFQJHBhcnRpdGlvbnMpID09IG5jb2woY2RzKQopCgpjYXQoIkNEUyBidWlsdDoiLCBuY29sKGNkcyksICJjZWxsc1xuIikKY2F0KCJDbHVzdGVyczoiLCBubGV2ZWxzKGZhY3Rvcihjb2xEYXRhKGNkcykkc2V1cmF0X2NsdXN0ZXJzKSksICJcbiIpCmNhdCgiUGFydGl0aW9uczoiLCBubGV2ZWxzKHBhcnRpdGlvbnMoY2RzKSksICJcblxuIikKY2F0KCJBemltdXRoIGwyIGJyZWFrZG93bjpcbiIpCnByaW50KHRhYmxlKGNvbERhdGEoY2RzKSRwcmVkaWN0ZWQuY2VsbHR5cGUubDIpKQpgYGAKCiMjIExlYXJuIFRyYWplY3RvcnkgR3JhcGgg4oCUIFR3byBMaW5lYWdlcwoKYGBge3IgbGVhcm4tZ3JhcGgsIGZpZy53aWR0aD0xNCwgZmlnLmhlaWdodD04fQojIOKUgOKUgCBMZWFybiBwcmluY2lwYWwgZ3JhcGgg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACiMgR29hbDogcmVjb3ZlciB0d28gYmlvbG9naWNhbCBsaW5lYWdlcyBhcyBhIHNpbmdsZSBjb25uZWN0ZWQgZ3JhcGg6CiMgICBMaW5lYWdlIDE6IE5haXZlIOKGkiBUQ00g4oaSIFRFTSDihpIgVGVtcmEvQ1RMICAoZWZmZWN0b3IgYXhpcykKIyAgIExpbmVhZ2UgMjogTmFpdmUg4oaSIFRDTSDihpIgVHJlZyAgICAgICAgICAgICAgKHJlZ3VsYXRvcnkgYnJhbmNoKQojCiMgdXNlX3BhcnRpdGlvbiA9IEZBTFNFOiBzaW5nbGUgY29ubmVjdGVkIGdyYXBoIChubyBwYXJ0aXRpb24gZm9yY2luZykKIyBjbG9zZV9sb29wICAgID0gRkFMU0U6IG9wZW4gdHJhamVjdG9yeSAobm8gbG9vcCBiYWNrKQojIG1pbmltYWxfYnJhbmNoX2xlbiA9IDU6IHNob3J0IGVub3VnaCB0byBkZXRlY3QgdGhlIFRyZWcgYnJhbmNoCiMgICDigJQgaWYgVHJlZyBicmFuY2ggbm90IGRldGVjdGVkLCByZWR1Y2UgdG8gMwojICAg4oCUIGlmIHRvbyBtYW55IHNwdXJpb3VzIGJyYW5jaGVzLCBpbmNyZWFzZSB0byA4CiMKIyBOT1RFOiBubi5rIGlzIE5PVCBhIHZhbGlkIGxlYXJuX2dyYXBoX2NvbnRyb2wgcGFyYW1ldGVyIGluIG1vbm9jbGUzLgojIEZJWDogcmVtb3ZlZCBubi5rIOKAlCBtb25vY2xlMyBoYW5kbGVzIG5laWdoYm91cmhvb2Qgc3RydWN0dXJlIGludGVybmFsbHkuCiMgVGhlIGtleSBzZW5zaXRpdml0eSBwYXJhbWV0ZXIgaXMgbWluaW1hbF9icmFuY2hfbGVuLgoKc2V0LnNlZWQoNDIpCmNkcyA8LSBsZWFybl9ncmFwaCgKICBjZHMsCiAgdXNlX3BhcnRpdGlvbiAgICAgICA9IEZBTFNFLAogIGNsb3NlX2xvb3AgICAgICAgICAgPSBGQUxTRSwKICBsZWFybl9ncmFwaF9jb250cm9sID0gbGlzdCgKICAgIG1pbmltYWxfYnJhbmNoX2xlbiAgPSAxMCwKICAgIyBuY2VudGVyICAgICAgICAgICAgID0gMTAwLCAgICMgZGVmYXVsdCBpcyB+MjUwIOKAlCByZWR1Y2UgdG8gc2ltcGxpZnkgZ3JhcGgKICAgIG9ydGhvZ29uYWxfcHJval90aXAgPSBGQUxTRQogICksCiAgdmVyYm9zZSA9IFRSVUUKKQoKY2F0KCJcbuKchSBQcmluY2lwYWwgZ3JhcGggbGVhcm5lZFxuIikKY2F0KCJOb2RlczoiLCBsZW5ndGgoaWdyYXBoOjpWKHByaW5jaXBhbF9ncmFwaChjZHMpJFVNQVApKSwgIlxuIikKCm5fYnJhbmNoX3BvaW50cyA8LSBzdW0oaWdyYXBoOjpkZWdyZWUocHJpbmNpcGFsX2dyYXBoKGNkcykkVU1BUCkgPiAyKQpjYXQoIkJyYW5jaCBwb2ludHM6Iiwgbl9icmFuY2hfcG9pbnRzLCAiXG4iKQoKaWYgKG5fYnJhbmNoX3BvaW50cyA9PSAwKSB7CiAgd2FybmluZygKICAgICLinYwgTm8gYnJhbmNoIHBvaW50IOKAlCBUcmVnIGxpbmVhZ2Ugbm90IHNlcGFyYXRlZC5cbiIsCiAgICAiICAgUmUtcnVuIGxlYXJuX2dyYXBoIHdpdGggbWluaW1hbF9icmFuY2hfbGVuID0gMyIKICApCn0gZWxzZSBpZiAobl9icmFuY2hfcG9pbnRzID4gMykgewogIHdhcm5pbmcoCiAgICAi4pqg77iPICBUb28gbWFueSBicmFuY2ggcG9pbnRzICgiLCBuX2JyYW5jaF9wb2ludHMsICIpLlxuIiwKICAgICIgICBSZS1ydW4gbGVhcm5fZ3JhcGggd2l0aCBtaW5pbWFsX2JyYW5jaF9sZW4gPSA4IgogICkKfSBlbHNlIHsKICBjYXQoIuKchSBCcmFuY2ggc3RydWN0dXJlIGxvb2tzIGNvcnJlY3QgKDEtMyBicmFuY2ggcG9pbnRzKVxuIikKfQoKIyDilIDilIAgVHJhamVjdG9yeSB2aXN1YWxpc2F0aW9uIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgAp1bWFwX2Nvb3JkcyA8LSBhcy5kYXRhLmZyYW1lKEVtYmVkZGluZ3MocmVmZXJlbmNlX2ludGVncmF0ZWQsICJ1bWFwIikpCmNvbG5hbWVzKHVtYXBfY29vcmRzKSA8LSBjKCJVTUFQMSIsICJVTUFQMiIpCnVtYXBfY29vcmRzJGwyICAgICAgICA8LSByZWZlcmVuY2VfaW50ZWdyYXRlZCRwcmVkaWN0ZWQuY2VsbHR5cGUubDIKdW1hcF9jb29yZHMkY2VsbF90eXBlIDwtIHJlZmVyZW5jZV9pbnRlZ3JhdGVkJGNlbGxfdHlwZQoKbGFiZWxfbDIgPC0gdW1hcF9jb29yZHMgJT4lCiAgZ3JvdXBfYnkobDIpICU+JQogIHN1bW1hcmlzZShVTUFQMSA9IG1lZGlhbihVTUFQMSksIFVNQVAyID0gbWVkaWFuKFVNQVAyKSwgLmdyb3VwcyA9ICJkcm9wIikKCmxhYmVsX2N0IDwtIHVtYXBfY29vcmRzICU+JQogIGdyb3VwX2J5KGNlbGxfdHlwZSkgJT4lCiAgc3VtbWFyaXNlKFVNQVAxID0gbWVkaWFuKFVNQVAxKSwgVU1BUDIgPSBtZWRpYW4oVU1BUDIpLCAuZ3JvdXBzID0gImRyb3AiKQoKcF9ncmFwaF9sMiA8LSBwbG90X2NlbGxzKAogIGNkcywKICBjb2xvcl9jZWxsc19ieSAgICAgICAgPSAicHJlZGljdGVkLmNlbGx0eXBlLmwyIiwKICBsYWJlbF9ncm91cHNfYnlfY2x1c3RlciA9IEZBTFNFLAogIGxhYmVsX2xlYXZlcyAgICAgICAgICA9IEZBTFNFLAogIGxhYmVsX2JyYW5jaF9wb2ludHMgICA9IFRSVUUsCiAgY2VsbF9zaXplICAgICAgICAgICAgID0gMC43LAogIHNob3dfdHJhamVjdG9yeV9ncmFwaCA9IFRSVUUKKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGF6aW11dGhfbDJfY29sb3JzLCBuYW1lID0gIlN0YXRlIikgKwogIGdlb21fbGFiZWxfcmVwZWwoZGF0YSA9IGxhYmVsX2wyLAogICAgICAgICAgICAgICAgICAgYWVzKHggPSBVTUFQMSwgeSA9IFVNQVAyLCBsYWJlbCA9IGwyKSwKICAgICAgICAgICAgICAgICAgIHNpemUgPSA0LCBmb250ZmFjZSA9ICJib2xkIiwgZmlsbCA9ICJ3aGl0ZSIsIGFscGhhID0gMC44NSwKICAgICAgICAgICAgICAgICAgIGJveC5wYWRkaW5nID0gMC41LCBzZWdtZW50LmNvbG9yID0gImdyZXk0MCIsCiAgICAgICAgICAgICAgICAgICBpbmhlcml0LmFlcyA9IEZBTFNFKSArCiAgZ2d0aXRsZSgiTW9ub2NsZTMgR3JhcGg6IEF6aW11dGggbDJcbihUd28gbGluZWFnZXM6IGVmZmVjdG9yIGF4aXMgKyBUcmVnIGJyYW5jaCkiKSArCiAgdGhlbWUocGxvdC50aXRsZSAgICAgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIHNpemUgPSAxMywgZmFjZSA9ICJib2xkIiksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQoKcF9ncmFwaF9jdCA8LSBwbG90X2NlbGxzKAogIGNkcywKICBjb2xvcl9jZWxsc19ieSAgICAgICAgPSAiY2VsbF90eXBlIiwKICBsYWJlbF9ncm91cHNfYnlfY2x1c3RlciA9IEZBTFNFLAogIGxhYmVsX2xlYXZlcyAgICAgICAgICA9IEZBTFNFLAogIGxhYmVsX2JyYW5jaF9wb2ludHMgICA9IFRSVUUsCiAgY2VsbF9zaXplICAgICAgICAgICAgID0gMC43LAogIHNob3dfdHJhamVjdG9yeV9ncmFwaCA9IFRSVUUKKSArCiAgZ2VvbV9sYWJlbF9yZXBlbChkYXRhID0gbGFiZWxfY3QsCiAgICAgICAgICAgICAgICAgICBhZXMoeCA9IFVNQVAxLCB5ID0gVU1BUDIsIGxhYmVsID0gY2VsbF90eXBlKSwKICAgICAgICAgICAgICAgICAgIHNpemUgPSAzLjUsIGZvbnRmYWNlID0gImJvbGQiLCBmaWxsID0gIndoaXRlIiwgYWxwaGEgPSAwLjg1LAogICAgICAgICAgICAgICAgICAgYm94LnBhZGRpbmcgPSAwLjUsIHNlZ21lbnQuY29sb3IgPSAiZ3JleTQwIiwKICAgICAgICAgICAgICAgICAgIGluaGVyaXQuYWVzID0gRkFMU0UpICsKICBnZ3RpdGxlKCJNb25vY2xlMyBHcmFwaDogQ2VsbCBUeXBlcyIpICsKICB0aGVtZShwbG90LnRpdGxlICAgICA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgc2l6ZSA9IDEzLCBmYWNlID0gImJvbGQiKSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgpwX2dyYXBoX2wyIHwgcF9ncmFwaF9jdAoKYGBgCgojIyBTZXQgUm9vdCAmIE9yZGVyIENlbGxzIGJ5IFBzZXVkb3RpbWUKCmBgYHtyIHBzZXVkb3RpbWUtcm9vdCwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTh9CmNhdCgiQXppbXV0aCBsMiBsYWJlbHMgaW4gQ0RTOlxuIikKcHJpbnQoc29ydCh1bmlxdWUoY29sRGF0YShjZHMpJHByZWRpY3RlZC5jZWxsdHlwZS5sMikpKQoKIyDilIDilIAgSWRlbnRpZnkgbmFpdmUgY2VsbHMg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACm5haXZlX2lkcyA8LSByb3duYW1lcyhjb2xEYXRhKGNkcykpWwogIGdyZXBsKCJuYWl2ZXxOYWl2ZXxUTiQiLCBjb2xEYXRhKGNkcykkcHJlZGljdGVkLmNlbGx0eXBlLmwyLAogICAgICAgIGlnbm9yZS5jYXNlID0gVFJVRSkgfAogIGdyZXBsKCJUbmFpdmUiLCBhcy5jaGFyYWN0ZXIoY29sRGF0YShjZHMpJGNlbGxfdHlwZSksCiAgICAgICAgaWdub3JlLmNhc2UgPSBGQUxTRSkKXQpjYXQoIlxuTmFpdmUgcm9vdCBjZWxsczoiLCBsZW5ndGgobmFpdmVfaWRzKSwgIlxuIikKaWYgKGxlbmd0aChuYWl2ZV9pZHMpID09IDApCiAgc3RvcCgi4p2MIE5vIG5haXZlIGNlbGxzIGZvdW5kLiBDaGVjayBsYWJlbCBwYXR0ZXJucyBhYm92ZS4iKQoKIyDilIDilIAgRmluZCByb290IG5vZGUgYnkgbWluaW11bSBkaXN0YW5jZSB0byBuYWl2ZSBjZW50cm9pZCDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKIyBVc2luZyBjZW50cm9pZCBkaXN0YW5jZSAobm90IG1vc3QtcG9wdWxhdGVkIG5vZGUpIGlzIG1vcmUgcm9idXN0OgojIHRoZSBtb3N0LXBvcHVsYXRlZCBub2RlIGJpYXNlcyB0b3dhcmQgdGhlIGRlbnNlc3QgcGFydCBvZiB0aGUgbmFpdmUKIyBjbHVzdGVyIHdoaWNoIG1heSBub3QgYmUgdGhlIGVhcmxpZXN0IHBvaW50IG9uIHRoZSB0cmFqZWN0b3J5LgpuYWl2ZV91bWFwICAgICA8LSBFbWJlZGRpbmdzKHJlZmVyZW5jZV9pbnRlZ3JhdGVkLCAidW1hcCIpW25haXZlX2lkcywgXQpuYWl2ZV9jZW50cm9pZCA8LSBjb2xNZWFucyhuYWl2ZV91bWFwKQoKcHJfbm9kZXMgICA8LSB0KGNkc0BwcmluY2lwYWxfZ3JhcGhfYXV4W1siVU1BUCJdXSRkcF9tc3QpCm5vZGVfZGlzdHMgPC0gcm93U3VtcygKICAocHJfbm9kZXMgLSBtYXRyaXgobmFpdmVfY2VudHJvaWQsCiAgICAgICAgICAgICAgICAgICAgIG5yb3cgPSBucm93KHByX25vZGVzKSwgbmNvbCA9IDIsCiAgICAgICAgICAgICAgICAgICAgIGJ5cm93ID0gVFJVRSkpXjIKKQpyb290X25vZGUgPC0gbmFtZXMod2hpY2gubWluKG5vZGVfZGlzdHMpKQpjYXQoIlJvb3Qgbm9kZSBzZWxlY3RlZDoiLCByb290X25vZGUsICJcbiIpCmNhdChzcHJpbnRmKCIgIFJvb3QgY29vcmRzOiAgICAoJS4zZiwgJS4zZilcbiIsCiAgICAgICAgICAgIHByX25vZGVzW3Jvb3Rfbm9kZSwgMV0sIHByX25vZGVzW3Jvb3Rfbm9kZSwgMl0pKQpjYXQoc3ByaW50ZigiICBOYWl2ZSBjZW50cm9pZDogKCUuM2YsICUuM2YpXG4iLAogICAgICAgICAgICBuYWl2ZV9jZW50cm9pZFsxXSwgbmFpdmVfY2VudHJvaWRbMl0pKQoKIyDilIDilIAgT3JkZXIgY2VsbHMg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACmNkcyA8LSBvcmRlcl9jZWxscyhjZHMsIHJvb3RfcHJfbm9kZXMgPSByb290X25vZGUpCgojIFRyYW5zZmVyIHBzZXVkb3RpbWUgYmFjayB0byBTZXVyYXQgb2JqZWN0CnJlZmVyZW5jZV9pbnRlZ3JhdGVkJG1vbm9jbGUzX3BzZXVkb3RpbWUgPC0gcHNldWRvdGltZShjZHMpCnJlZmVyZW5jZV9pbnRlZ3JhdGVkJG1vbm9jbGUzX3BzZXVkb3RpbWVbCiAgIWlzLmZpbml0ZShyZWZlcmVuY2VfaW50ZWdyYXRlZCRtb25vY2xlM19wc2V1ZG90aW1lKV0gPC0gTkEKCmNhdCgiXG5Qc2V1ZG90aW1lIHN1bW1hcnkgKHJlZmVyZW5jZSk6XG4iKQpwcmludChzdW1tYXJ5KHJlZmVyZW5jZV9pbnRlZ3JhdGVkJG1vbm9jbGUzX3BzZXVkb3RpbWVbCiAgaXMuZmluaXRlKHJlZmVyZW5jZV9pbnRlZ3JhdGVkJG1vbm9jbGUzX3BzZXVkb3RpbWUpXSkpCgojIOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkAojIFRPUE9MT0dZIFZBTElEQVRJT04KIyDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZAKIyBDb3JyZWN0IG9yZGVyIGZvciBlYWNoIGxpbmVhZ2U6CiMgICBFZmZlY3RvcjogTmFpdmUgPCBUQ00gPCBURU0gPCBUZW1yYQojICAgVHJlZzogICAgIE5haXZlIDwgVENNIOKAlCBUcmVnICAgKFRyZWcgYnJhbmNoZXMgZnJvbSBUQ00sIE5PVCBhZnRlciBURU0pCiMKIyBUaGlzIGlzIGEgaGFyZCBiaW9sb2dpY2FsIHJlcXVpcmVtZW50LiBJZiBUcmVnID4gVEVNIGluIHBzZXVkb3RpbWUsCiMgdGhlIGdyYXBoIGhhcyBwbGFjZWQgVHJlZyBkb3duc3RyZWFtIG9mIFRFTSB3aGljaCBpcyBhbmF0b21pY2FsbHkgd3JvbmcuCiMgVHJlZyBkaWZmZXJlbnRpYXRlcyBmcm9tIFRDTS1zdGFnZSBwcmVjdXJzb3JzIHZpYSBGT1hQMyBpbmR1Y3Rpb24sCiMgbm90IGZyb20gdGVybWluYWxseSBkaWZmZXJlbnRpYXRlZCBURU0vVGVtcmEgY2VsbHMuCgpwdF9jaGVjayA8LSByZWZlcmVuY2VfaW50ZWdyYXRlZEBtZXRhLmRhdGEgJT4lCiAgZmlsdGVyKGlzLmZpbml0ZShtb25vY2xlM19wc2V1ZG90aW1lKSkgJT4lCiAgZ3JvdXBfYnkocHJlZGljdGVkLmNlbGx0eXBlLmwyKSAlPiUKICBzdW1tYXJpc2UoCiAgICBuICAgICAgID0gbigpLAogICAgbWVkX3B0ICA9IHJvdW5kKG1lZGlhbihtb25vY2xlM19wc2V1ZG90aW1lLCBuYS5ybSA9IFRSVUUpLCAzKSwKICAgIG1lYW5fcHQgPSByb3VuZChtZWFuKG1vbm9jbGUzX3BzZXVkb3RpbWUsICAgbmEucm0gPSBUUlVFKSwgMyksCiAgICAuZ3JvdXBzID0gImRyb3AiCiAgKSAlPiUKICBhcnJhbmdlKG1lZF9wdCkKCmNhdCgiXG49PT0gUHNldWRvdGltZSBvcmRlciBwZXIgc3RhdGUgKG11c3QgYmUgTmFpdmUgPCBUQ00gPCBUcmVnIDwgVEVNIDwgVGVtcmEpID09PVxuIikKcHJpbnQocHRfY2hlY2spCgojIEV4dHJhY3Qgc3RhdGUgbWVkaWFucyDigJQgdXNlZCBmb3IgYmluIGJvdW5kYXJpZXMgZG93bnN0cmVhbQpuYWl2ZV9tZWQgPC0gcHRfY2hlY2skbWVkX3B0W3B0X2NoZWNrJHByZWRpY3RlZC5jZWxsdHlwZS5sMiA9PSAiQ0Q0IE5haXZlIl0KdGNtX21lZCAgIDwtIHB0X2NoZWNrJG1lZF9wdFtwdF9jaGVjayRwcmVkaWN0ZWQuY2VsbHR5cGUubDIgPT0gIkNENCBUQ00iXQp0cmVnX21lZCAgPC0gcHRfY2hlY2skbWVkX3B0W3B0X2NoZWNrJHByZWRpY3RlZC5jZWxsdHlwZS5sMiA9PSAiVHJlZyJdCnRlbV9tZWQgICA8LSBwdF9jaGVjayRtZWRfcHRbcHRfY2hlY2skcHJlZGljdGVkLmNlbGx0eXBlLmwyID09ICJDRDQgVEVNIl0KdGVtcmFfbWVkIDwtIHB0X2NoZWNrJG1lZF9wdFtwdF9jaGVjayRwcmVkaWN0ZWQuY2VsbHR5cGUubDIgPT0gIkNENCBUZW1yYS9DVEwiXQoKIyBGSVg6IHNpbmdsZS1zdHJpbmcgc3ByaW50ZiDigJQgb3JpZ2luYWwgaGFkIHR3byBmb3JtYXQgc3RyaW5ncyB3aGljaAojIGNhdXNlZCB0aGUgc2Vjb25kIHN0cmluZyB0byBiZSBzaWxlbnRseSBpZ25vcmVkCmNhdChzcHJpbnRmKAogICJcblN0YXRlIG1lZGlhbnM6IE5haXZlPSUuMmYgfCBUQ009JS4yZiB8IFRyZWc9JS4yZiB8IFRFTT0lLjJmIHwgVGVtcmE9JS4yZlxuIiwKICBuYWl2ZV9tZWQsIHRjbV9tZWQsIHRyZWdfbWVkLCB0ZW1fbWVkLCB0ZW1yYV9tZWQKKSkKCiMgSGFyZCBzdG9wcyDigJQgc2NyaXB0IHdpbGwgbm90IGNvbnRpbnVlIGlmIHRvcG9sb2d5IGlzIGJpb2xvZ2ljYWxseSB3cm9uZwppZiAobmFpdmVfbWVkID4gdGNtX21lZCkKICBzdG9wKHNwcmludGYoCiAgICAi4p2MIFRPUE9MT0dZIEVSUk9SOiBOYWl2ZSAoJS4yZikgPiBUQ00gKCUuMmYpLlxuICBSb290IG5vZGUgbWF5IGJlIG1pc3BsYWNlZCDigJQgY2hlY2sgbmFpdmVfY2VudHJvaWQgY29vcmRpbmF0ZXMuIiwKICAgIG5haXZlX21lZCwgdGNtX21lZCkpCgppZiAodGNtX21lZCA+IHRlbV9tZWQpCiAgc3RvcChzcHJpbnRmKAogICAgIuKdjCBUT1BPTE9HWSBFUlJPUjogVENNICglLjJmKSA+IFRFTSAoJS4yZikuXG4gIFJlLWNoZWNrIHJvb3Qgbm9kZSBzZWxlY3Rpb24uIiwKICAgIHRjbV9tZWQsIHRlbV9tZWQpKQoKaWYgKHRyZWdfbWVkID4gdGVtX21lZCkKICBzdG9wKHNwcmludGYoCiAgICAi4p2MIFRPUE9MT0dZIEVSUk9SOiBUcmVnICglLjJmKSA+IFRFTSAoJS4yZikuXG4gIFRyZWcgbXVzdCBicmFuY2ggZnJvbSBUQ00sIG5vdCBiZSBkb3duc3RyZWFtIG9mIFRFTS5cbiAgRml4OiByZS1ydW4gbGVhcm5fZ3JhcGggd2l0aCBtaW5pbWFsX2JyYW5jaF9sZW4gPSAzIiwKICAgIHRyZWdfbWVkLCB0ZW1fbWVkKSkKCiMgRklYOiBzaW5nbGUgc3ByaW50ZiBmb3JtYXQgc3RyaW5nICh3YXMgc3BsaXQgYWNyb3NzIHR3byBzdHJpbmdzIGJlZm9yZSkKY2F0KHNwcmludGYoCiAgIuKchSBUb3BvbG9neSBjb25maXJtZWQ6IE5haXZlKCUuMmYpIDwgVENNKCUuMmYpIDwgVHJlZyglLjJmKSA8IFRFTSglLjJmKSA8IFRlbXJhKCUuMmYpXG4iLAogIG5haXZlX21lZCwgdGNtX21lZCwgdHJlZ19tZWQsIHRlbV9tZWQsIHRlbXJhX21lZAopKQoKIyDilIDilIAgUHNldWRvdGltZSBVTUFQIHBsb3RzIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgApwbG90X2NlbGxzKAogIGNkcywKICBjb2xvcl9jZWxsc19ieSAgICAgICAgPSAicHNldWRvdGltZSIsCiAgbGFiZWxfY2VsbF9ncm91cHMgICAgID0gRkFMU0UsCiAgY2VsbF9zaXplICAgICAgICAgICAgID0gMC44LAogIHNob3dfdHJhamVjdG9yeV9ncmFwaCA9IFRSVUUKKSArCiAgc2NhbGVfY29sb3JfdmlyaWRpc19jKG9wdGlvbiA9ICJwbGFzbWEiLCBuYW1lID0gIlBzZXVkb3RpbWUiKSArCiAgZ2d0aXRsZSgiUmVmZXJlbmNlIFBzZXVkb3RpbWVcbihSb290ID0gQ0Q0IE5haXZlIHwgVHdvIGxpbmVhZ2VzOiBURU0vVGVtcmEgKyBUcmVnKSIpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBzaXplID0gMTMsIGZhY2UgPSAiYm9sZCIpKQpgYGAKCiMjIFZhbGlkYXRlIFBzZXVkb3RpbWUgQWdhaW5zdCBDZWxsIFR5cGUKCmBgYHtyIHZhbGlkYXRlLXBzZXVkb3RpbWUsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD01fQojIFZpc3VhbCB2YWxpZGF0aW9uIHRoYXQgcHNldWRvdGltZSBvcmRlciBtYXRjaGVzIGV4cGVjdGVkIGJpb2xvZ3kuCiMgQm90aCB2aW9saW4gYW5kIHJpZGdlIHBsb3RzIHNob3VsZCBzaG93OgojICAgTmFpdmUgKGxvd2VzdCkg4oaSIFRDTSDihpIgVHJlZyAoYnJhbmNoLCBpbnRlcm1lZGlhdGUpIOKGkiBURU0g4oaSIFRlbXJhIChoaWdoZXN0KQoKcHRfZGF0YSA8LSBkYXRhLmZyYW1lKAogIHBzZXVkb3RpbWUgPSByZWZlcmVuY2VfaW50ZWdyYXRlZCRtb25vY2xlM19wc2V1ZG90aW1lLAogIGwyICAgICAgICAgPSByZWZlcmVuY2VfaW50ZWdyYXRlZCRwcmVkaWN0ZWQuY2VsbHR5cGUubDIKKSAlPiUgZmlsdGVyKGlzLmZpbml0ZShwc2V1ZG90aW1lKSwgIWlzLm5hKGwyKSkKCiMgT3JkZXIgeC1heGlzIGJ5IG1lZGlhbiBwc2V1ZG90aW1lIOKAlCBzaG91bGQgZ2l2ZSBiaW9sb2dpY2FsIG9yZGVyCmwyX29yZGVyIDwtIHB0X2RhdGEgJT4lCiAgZ3JvdXBfYnkobDIpICU+JQogIHN1bW1hcmlzZShtZWRfcHQgPSBtZWRpYW4ocHNldWRvdGltZSwgbmEucm0gPSBUUlVFKSwgLmdyb3VwcyA9ICJkcm9wIikgJT4lCiAgYXJyYW5nZShtZWRfcHQpICU+JQogIHB1bGwobDIpCnB0X2RhdGEkbDIgPC0gZmFjdG9yKHB0X2RhdGEkbDIsIGxldmVscyA9IGwyX29yZGVyKQoKcF92aW9saW4gPC0gZ2dwbG90KHB0X2RhdGEsIGFlcyh4ID0gbDIsIHkgPSBwc2V1ZG90aW1lLCBmaWxsID0gbDIpKSArCiAgZ2VvbV92aW9saW4oc2NhbGUgPSAid2lkdGgiLCBhbHBoYSA9IDAuODUsIHRyaW0gPSBUUlVFKSArCiAgZ2VvbV9ib3hwbG90KHdpZHRoID0gMC4xMiwgZmlsbCA9ICJ3aGl0ZSIsIG91dGxpZXIuc2l6ZSA9IDAuMykgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGF6aW11dGhfbDJfY29sb3JzLCBuYS52YWx1ZSA9ICJncmV5NzAiKSArCiAgdGhlbWVfY2xhc3NpYyhiYXNlX3NpemUgPSAxMSkgKwogIHRoZW1lKGF4aXMudGV4dC54ICAgPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0MCwgaGp1c3QgPSAxLCBzaXplID0gOSksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgICAgIHBsb3QudGl0bGUgICAgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIpKSArCiAgbGFicyh0aXRsZSAgICA9ICJSZWZlcmVuY2UgUHNldWRvdGltZSBwZXIgQXppbXV0aCBsMiBMYWJlbCIsCiAgICAgICBzdWJ0aXRsZSA9ICJFeHBlY3RlZCBvcmRlcjogTmFpdmUgPCBUQ00gPCBUcmVnIDwgVEVNIDwgVGVtcmEiLAogICAgICAgeCA9ICIiLCB5ID0gIk1vbm9jbGUzIFBzZXVkb3RpbWUiKQoKcF9yaWRnZSA8LSBnZ3Bsb3QocHRfZGF0YSwgYWVzKHggPSBwc2V1ZG90aW1lLCB5ID0gbDIsIGZpbGwgPSBsMikpICsKICBnZW9tX2RlbnNpdHlfcmlkZ2VzKGFscGhhID0gMC43NSwgc2NhbGUgPSAxLjIsIHJlbF9taW5faGVpZ2h0ID0gMC4wMSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGF6aW11dGhfbDJfY29sb3JzLCBuYS52YWx1ZSA9ICJncmV5NzAiKSArCiAgdGhlbWVfY2xhc3NpYyhiYXNlX3NpemUgPSAxMCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiKSkgKwogIGxhYnModGl0bGUgPSAiUmVmZXJlbmNlIFBzZXVkb3RpbWUgRGVuc2l0eSBieSBBemltdXRoIGwyIExhYmVsIiwKICAgICAgIHggPSAiUHNldWRvdGltZSIsIHkgPSAiIikKCnBfdmlvbGluIHwgcF9yaWRnZQpgYGAKCgpgYGB7ciBmaXgtcmVmZXJlbmNlLXNjdC1wY2EsIGluY2x1ZGU9VFJVRX0KCiMg4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQCiMgQ1JJVElDQUwgRklYOiBSZWJ1aWxkIHJlZmVyZW5jZSBQQ0EgaW4gU0NUIHNwYWNlIChwcmVzZXJ2ZXMgVU1BUCB0cmFqZWN0b3J5KQojIFBST0JMRU06IHJlZmVyZW5jZV9pbnRlZ3JhdGVkIFBDQSA9ICJpbnRlZ3JhdGVkIiBhc3NheSAoNTAgZGltcykKIyAgICAgICAgICBNYWxpZ25hbnRDRDRUIFBDQSAgICAgPSAiU0NUIiBhc3NheSAoMzAgZGltcykgCiMgU09MVVRJT046IFNDVCArIFBDQSBvbiByZWZlcmVuY2Ug4oaSIHVuaWZpZWQgU0NUIHNwYWNlIGZvciBGaW5kVHJhbnNmZXJBbmNob3JzCiMgVU1BUCBNT0RFTCAxMDAlIFNBRkUg4oCUIGxpdmVzIGluIHJlZHVjdGlvbnMkdW1hcEBtaXNjJG1vZGVsCiMg4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQCgpjYXQoIj09PSBESUFHTk9TVElDOiBDdXJyZW50IHJlZmVyZW5jZSBzdGF0ZSA9PT1cbiIpCmNhdCgiRGVmYXVsdEFzc2F5OiIsIERlZmF1bHRBc3NheShyZWZlcmVuY2VfaW50ZWdyYXRlZCksICJcbiIpCmNhdCgiU0NUIEhWR3M6IiwgbGVuZ3RoKFZhcmlhYmxlRmVhdHVyZXMocmVmZXJlbmNlX2ludGVncmF0ZWQpKSwgIlxuIikKY2F0KCJQQ0EgZGltczoiLCBuY29sKEVtYmVkZGluZ3MocmVmZXJlbmNlX2ludGVncmF0ZWQsICJwY2EiKSksICJcbiIpCmNhdCgiVU1BUCBtb2RlbCBwcmVzZW50OiIsICFpcy5udWxsKHJlZmVyZW5jZV9pbnRlZ3JhdGVkQHJlZHVjdGlvbnMkdW1hcEBtaXNjJG1vZGVsKSwgIlxuIikKCiMgMS4gU2FuaXR5IGNoZWNrIFVNQVAgYmVmb3JlCm9sZF91bWFwX2Nvb3JkcyA8LSBFbWJlZGRpbmdzKHJlZmVyZW5jZV9pbnRlZ3JhdGVkLCAidW1hcCIpWzE6MywgXQpvbGRfdW1hcF9tb2RlbCAgPC0gIWlzLm51bGwocmVmZXJlbmNlX2ludGVncmF0ZWRAcmVkdWN0aW9ucyR1bWFwQG1pc2MkbW9kZWwpCmNhdCgiXG5VTUFQIGZpcnN0IDMgY2VsbHMgKGJhY2t1cCk6IiwgcGFzdGUocm91bmQob2xkX3VtYXBfY29vcmRzWzEsXSwgMyksIGNvbGxhcHNlPSIgIiksICJcbiIpCgojIDIuIFNDVCBvbiByZWZlcmVuY2UgKGNyZWF0ZXMgU0NUIGFzc2F5ICsgSFZHcykKRGVmYXVsdEFzc2F5KHJlZmVyZW5jZV9pbnRlZ3JhdGVkKSA8LSAiUk5BIgpyZWZlcmVuY2VfaW50ZWdyYXRlZCA8LSBTQ1RyYW5zZm9ybSgKICByZWZlcmVuY2VfaW50ZWdyYXRlZCwKICB2YXJpYWJsZS5mZWF0dXJlcy5uID0gMzAwMCwKICB2c3QuZmxhdm9yICAgICAgICAgID0gInYyIiwKICB2YXJzLnRvLnJlZ3Jlc3MgICAgID0gYygicGVyY2VudC5tdCIsICJTLlNjb3JlIiwgIkcyTS5TY29yZSIpLAogIHZlcmJvc2UgICAgICAgICAgICAgPSBGQUxTRQopCgojIDMuIFBDQSBpbiBTQ1Qgc3BhY2UgKG1hdGNoZXMgTWFsaWduYW50Q0Q0VCBleGFjdGx5KQpyZWZlcmVuY2VfaW50ZWdyYXRlZCA8LSBSdW5QQ0EoCiAgcmVmZXJlbmNlX2ludGVncmF0ZWQsCiAgYXNzYXkgICA9ICJTQ1QiLAogIG5wY3MgICAgPSAzMCwgICAgICAgICMg4oaQIE1hdGNoIHF1ZXJ5IGRpbXMgZXhhY3RseQogIHZlcmJvc2UgPSBGQUxTRQopCgojIDQuIFZlcmlmeSBmaXgKY2F0KCJcbj09PSBGSVhFRDogTmV3IHJlZmVyZW5jZSBzdGF0ZSA9PT1cbiIpCmNhdCgiRGVmYXVsdEFzc2F5OiIsIERlZmF1bHRBc3NheShyZWZlcmVuY2VfaW50ZWdyYXRlZCksICJcbiIpCmNhdCgiU0NUIEhWR3M6IiwgbGVuZ3RoKFZhcmlhYmxlRmVhdHVyZXMocmVmZXJlbmNlX2ludGVncmF0ZWQpKSwgIlxuIikKY2F0KCJQQ0EgZGltczoiLCBuY29sKEVtYmVkZGluZ3MocmVmZXJlbmNlX2ludGVncmF0ZWQsICJwY2EiKSksICJcbiIpCmNhdCgiVU1BUCBtb2RlbCBwcmVzZXJ2ZWQ6IiwgIWlzLm51bGwocmVmZXJlbmNlX2ludGVncmF0ZWRAcmVkdWN0aW9ucyR1bWFwQG1pc2MkbW9kZWwpLCAiXG4iKQoKIyA1LiBVTUFQIGludGVncml0eSBjaGVjawpuZXdfdW1hcF9jb29yZHMgPC0gRW1iZWRkaW5ncyhyZWZlcmVuY2VfaW50ZWdyYXRlZCwgInVtYXAiKVsxOjMsIF0KY2F0KCJVTUFQIHVuY2hhbmdlZDoiLCBhbGwob2xkX3VtYXBfY29vcmRzID09IG5ld191bWFwX2Nvb3JkcyksICJcbiIpCmNhdCgi4pyFIFJlZmVyZW5jZSBub3cgaW4gU0NUIHNwYWNlIOKAlCBGaW5kVHJhbnNmZXJBbmNob3JzIHdpbGwgd29yayBmb3IgQUxMIDcgbGluZXNcbiIpCgpgYGAKCgoKCgoKIyBQcmVwYXJlIFJlZmVyZW5jZSBmb3IgTWFwUXVlcnkKCmBgYHtyIHByZXBhcmUtcmVmZXJlbmNlfQojIFFVSUNLIFBSRS1GTElHSFQgQ0hFQ0tTIChydW4gdGhlc2UgMiBsaW5lcyBub3cpCmNhdCgiU0NUIEhWR3M6IiwgbGVuZ3RoKFZhcmlhYmxlRmVhdHVyZXMocmVmZXJlbmNlX2ludGVncmF0ZWQsIGFzc2F5ID0gIlNDVCIpKSwgIlxuIikKY2F0KCJQQ0EgZGltczoiLCBuY29sKEVtYmVkZGluZ3MocmVmZXJlbmNlX2ludGVncmF0ZWQsICJwY2EiKSksICJcbiIpCgpgYGAKCgojIE1hcCBNYWxpZ25hbnQgQ2VsbHMgb250byBSZWZlcmVuY2UKCiMjIEZpbmQgVHJhbnNmZXIgQW5jaG9ycwoKYGBge3IgZmluZC1hbmNob3JzfQojIOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkAojIEZJTkQgVFJBTlNGRVIgQU5DSE9SUwojIOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkAojIEFuY2hvcnMgYXJlIG11dHVhbCBuZWFyZXN0IG5laWdoYm91cnMgYmV0d2VlbiByZWZlcmVuY2UgYW5kIHF1ZXJ5IGluCiMgc2hhcmVkIFBDQSBzcGFjZS4gVGhlaXIgcXVhbGl0eSBkZXRlcm1pbmVzIHRoZSBhY2N1cmFjeSBvZjoKIyAgIChhKSBsYWJlbCB0cmFuc2ZlciAg4oaSIHdoaWNoIG5vcm1hbCBzdGF0ZSBkb2VzIHRoaXMgU8OpemFyeSBjZWxsIHJlc2VtYmxlPwojICAgKGIpIHBzZXVkb3RpbWUgdHJhbnNmZXIg4oaSIHdoZXJlIGFsb25nIHRoZSBkaWZmZXJlbnRpYXRpb24gYXhpcyBpcyBpdD8KIwojIEZFQVRVUkUgU0VMRUNUSU9OIFNUUkFURUdZOgojICAgU3RlcCAxIOKAlCBpbnRlcnNlY3QgcmVmZXJlbmNlICsgcXVlcnkgSFZHcwojICAgU3RlcCAyIOKAlCByZW1vdmUgdGVjaG5pY2FsL2p1bmsgZ2VuZXMgKE1ULCByaWJvc29tYWwsIEhTUCwgbG5jUk5BLCBoaXN0b25lKQojICAgICAgICAgICAgV0hZOiB0aGVzZSBnZW5lcyBhcmUgdmFyaWFibGUgZHVlIHRvIGxpYnJhcnkgcXVhbGl0eSBhbmQgc3RyZXNzLAojICAgICAgICAgICAgTk9UIGR1ZSB0byBUIGNlbGwgZGlmZmVyZW50aWF0aW9uIHN0YXRlLiBLZWVwaW5nIHRoZW0gYmlhc2VzCiMgICAgICAgICAgICBhbmNob3JzIHRvd2FyZCBiYXRjaCBlZmZlY3RzIHJhdGhlciB0aGFuIGJpb2xvZ3kuCiMgICBTdGVwIDMg4oCUIHJhbmsgcmVtYWluaW5nIGdlbmVzIGJ5IGpvaW50IHJlc2lkdWFsIHZhcmlhbmNlIChyZWYgKyBxdWVyeSkKIyAgICAgICAgICAgIFdIWTogZ2VuZXMgaGlnaGx5IHZhcmlhYmxlIGluIEJPVEggZGF0YXNldHMgYXJlIHRoZSBtb3N0CiMgICAgICAgICAgICByZWxpYWJsZSBmb3IgZmluZGluZyB0cnVlIGJpb2xvZ2ljYWwgbmVhcmVzdCBuZWlnaGJvdXJzCiMg4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQCgpEZWZhdWx0QXNzYXkocmVmZXJlbmNlX2ludGVncmF0ZWQpIDwtICJTQ1QiCkRlZmF1bHRBc3NheShNYWxpZ25hbnRDRDRUKSAgICAgICAgPC0gIlNDVCIKCiMg4pSA4pSAIFN0ZXAgMTogSW50ZXJzZWN0IHJlZmVyZW5jZSBhbmQgcXVlcnkgSFZHcyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKcmVmX2h2Z3MgPC0gVmFyaWFibGVGZWF0dXJlcyhyZWZlcmVuY2VfaW50ZWdyYXRlZCwgYXNzYXkgPSAiU0NUIikKaWYgKGxlbmd0aChyZWZfaHZncykgPT0gMCkgewogIGNhdCgi4pqg77iPICBObyBIVkdzIHN0b3JlZCBpbiByZWZlcmVuY2Ug4oCUIGV4dHJhY3RpbmcgZnJvbSBzY2FsZS5kYXRhXG4iKQogIHJlZl9odmdzIDwtIHJvd25hbWVzKEdldEFzc2F5RGF0YShyZWZlcmVuY2VfaW50ZWdyYXRlZCwgYXNzYXkgPSAiU0NUIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxheWVyID0gInNjYWxlLmRhdGEiKSkKfQoKcXVlcnlfaHZncyAgICAgIDwtIFZhcmlhYmxlRmVhdHVyZXMoTWFsaWduYW50Q0Q0VCkKY29tbW9uX2ZlYXR1cmVzIDwtIGludGVyc2VjdChyZWZfaHZncywgcXVlcnlfaHZncykKCmNhdCgiPT09IEZlYXR1cmUgc2V0IGNvbnN0cnVjdGlvbiA9PT1cbiIpCmNhdCgiUmVmZXJlbmNlIEhWR3MgOiIsIGxlbmd0aChyZWZfaHZncyksICJcbiIpCmNhdCgiUXVlcnkgSFZHcyAgICAgOiIsIGxlbmd0aChxdWVyeV9odmdzKSwgIlxuIikKY2F0KCJDb21tb24gSFZHcyAgICA6IiwgbGVuZ3RoKGNvbW1vbl9mZWF0dXJlcyksICJcbiIpCgppZiAobGVuZ3RoKGNvbW1vbl9mZWF0dXJlcykgPCAxNTAwKQogIHdhcm5pbmcocGFzdGUwKCLimqDvuI8gIE9ubHkgIiwgbGVuZ3RoKGNvbW1vbl9mZWF0dXJlcyksCiAgICAgICAgICAgICAgICAgIiBjb21tb24gZmVhdHVyZXMg4oCUIGJlbG93IHJlY29tbWVuZGVkIDE1MDAuXG4iLAogICAgICAgICAgICAgICAgICIgICBJbmNyZWFzZSBuZmVhdHVyZXMgaW4gU0NUcmFuc2Zvcm0gdG8gNDAwMC4iKSkKCiMg4pSA4pSAIFN0ZXAgMjogUmVtb3ZlIHRlY2huaWNhbC9qdW5rIGdlbmVzIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgAojIENhdGVnb3JpZXMgcmVtb3ZlZCBhbmQgV0hZOgojICAgXk1ULSAgIDogbWl0b2Nob25kcmlhbCBPWFBIT1MgZ2VuZXMg4oCUIHZhcmlhYmxlIGR1ZSB0byBjZWxsIHF1YWxpdHkvc3RyZXNzCiMgICBeUlBML1MgOiByaWJvc29tYWwgcHJvdGVpbnMg4oCUIHZhcmlhYmxlIGR1ZSB0byB0cmFuc2xhdGlvbiByYXRlL2xpYnJhcnkgZGVwdGgKIyAgIF5IU1AqICA6IGhlYXQgc2hvY2sgcHJvdGVpbnMg4oCUIHZhcmlhYmxlIGR1ZSB0byBzdHJlc3MsIG5vdCBkaWZmZXJlbnRpYXRpb24KIyAgIF5TTkhHL01BTEFUMS9ORUFUMSA6IGxuY1JOQXMg4oCUIG51Y2xlYXIgYXJjaGl0ZWN0dXJlLCBub3QgVCBjZWxsIHN0YXRlCiMgICBeSElTVCAgOiByZXBsaWNhdGlvbi1kZXBlbmRlbnQgaGlzdG9uZXMg4oCUIFMtcGhhc2UgbWFya2VycyAoY2VsbCBjeWNsZSkKIyAgICAgICAgICAgIE5PVEU6IF5ISVNUIGFkZGVkIGJhc2VkIG9uIHByZS1ydW4gUUMgc2hvd2luZyAyMCsgaGlzdG9uZSBnZW5lcwojICAgICAgICAgICAgaW4gc2hhcmVkIEhWRyBzZXQsIGFsbCBkcml2ZW4gYnkgY3ljbGluZyBzdWJwb3B1bGF0aW9uCgpqdW5rX3BhdHRlcm4gPC0gcGFzdGUwKAogICJeUlBMfF5SUFN8IiwgICAgICAgICAgIyByaWJvc29tYWwgbGFyZ2UgKyBzbWFsbCBzdWJ1bml0cwogICJeTVQtfCIsICAgICAgICAgICAgICAgIyBtaXRvY2hvbmRyaWFsIGdlbm9tZSAoT1hQSE9TIGNvbXBsZXhlcyBJLElJSSxJVixWKQogICJeSFNQfF5IU1BBfF5IU1BCfCIsICAjIGhlYXQgc2hvY2sgcHJvdGVpbnMKICAiXkhTUER8XkhTUEV8XkhTUEh8IiwgIyBoZWF0IHNob2NrIHByb3RlaW5zIChjb250aW51ZWQpCiAgIl5TTkhHfCIsICAgICAgICAgICAgICAjIHNtYWxsIG51Y2xlb2xhciBSTkEgaG9zdCBnZW5lcwogICJeTUFMQVQxfF5ORUFUMXwiLCAgICAgIyBhYnVuZGFudCBudWNsZWFyIGxuY1JOQXMKICAiXlhJU1R8IiwgICAgICAgICAgICAgICMgWC1pbmFjdGl2YXRpb24gKHNleC1zcGVjaWZpYyBiYXRjaCkKICAiXkhJU1QiICAgICAgICAgICAgICAgICMgcmVwbGljYXRpb24tZGVwZW5kZW50IGhpc3RvbmVzIChTLXBoYXNlIHNpZ25hbCkKKQoKIyBSZXBvcnQgYnJlYWtkb3duIGJ5IGNhdGVnb3J5IGJlZm9yZSByZW1vdmluZwptdF9yZW1vdmVkICAgPC0gc3VtKGdyZXBsKCJeTVQtIiwgICAgICAgICAgICAgICAgICAgIGNvbW1vbl9mZWF0dXJlcykpCnJpYm9fcmVtb3ZlZCA8LSBzdW0oZ3JlcGwoIl5SUEx8XlJQUyIsICAgICAgICAgICAgICAgY29tbW9uX2ZlYXR1cmVzKSkKaHNwX3JlbW92ZWQgIDwtIHN1bShncmVwbCgiXkhTUCIsICAgICAgICAgICAgICAgICAgICBjb21tb25fZmVhdHVyZXMpKQpsbmNfcmVtb3ZlZCAgPC0gc3VtKGdyZXBsKCJeU05IR3xeTUFMQVQxfF5ORUFUMXxeWElTVCIsIGNvbW1vbl9mZWF0dXJlcykpCmhpc3RfcmVtb3ZlZCA8LSBzdW0oZ3JlcGwoIl5ISVNUIiwgICAgICAgICAgICAgICAgICAgY29tbW9uX2ZlYXR1cmVzKSkKCmNhdCgiXG49PT0gSnVuayBnZW5lIHJlbW92YWwgYnJlYWtkb3duID09PVxuIikKY2F0KHNwcmludGYoIiAgTWl0b2Nob25kcmlhbCAoT1hQSE9TKSAgICA6ICVkXG4iLCBtdF9yZW1vdmVkKSkKY2F0KHNwcmludGYoIiAgUmlib3NvbWFsIChSUEwvUlBTKSAgICAgICA6ICVkXG4iLCByaWJvX3JlbW92ZWQpKQpjYXQoc3ByaW50ZigiICBIZWF0IHNob2NrIChIU1AqKSAgICAgICAgIDogJWRcbiIsIGhzcF9yZW1vdmVkKSkKY2F0KHNwcmludGYoIiAgbG5jUk5BIChTTkhHL01BTEFUMS9ORUFUMSk6ICVkXG4iLCBsbmNfcmVtb3ZlZCkpCmNhdChzcHJpbnRmKCIgIEhpc3RvbmVzIChISVNUKikgICAgICAgICAgOiAlZFxuIiwgaGlzdF9yZW1vdmVkKSkKCm5fYmVmb3JlICAgICAgICAgICAgICA8LSBsZW5ndGgoY29tbW9uX2ZlYXR1cmVzKQpjb21tb25fZmVhdHVyZXNfY2xlYW4gPC0gY29tbW9uX2ZlYXR1cmVzWyFncmVwbChqdW5rX3BhdHRlcm4sIGNvbW1vbl9mZWF0dXJlcyldCm5fcmVtb3ZlZCAgICAgICAgICAgICA8LSBuX2JlZm9yZSAtIGxlbmd0aChjb21tb25fZmVhdHVyZXNfY2xlYW4pCgpjYXQoc3ByaW50ZigiICDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcbiIpKQpjYXQoc3ByaW50ZigiICBUb3RhbCByZW1vdmVkICAgICAgICAgICAgIDogJWQgKCUuMWYlJSlcbiIsCiAgICAgICAgICAgIG5fcmVtb3ZlZCwgMTAwICogbl9yZW1vdmVkIC8gbl9iZWZvcmUpKQpjYXQoc3ByaW50ZigiICBDbGVhbiBmZWF0dXJlcyByZW1haW5pbmcgIDogJWRcblxuIiwKICAgICAgICAgICAgbGVuZ3RoKGNvbW1vbl9mZWF0dXJlc19jbGVhbikpKQoKaWYgKGxlbmd0aChjb21tb25fZmVhdHVyZXNfY2xlYW4pIDwgMTIwMCkKICB3YXJuaW5nKCLimqDvuI8gIEZld2VyIHRoYW4gMTIwMCBjbGVhbiBmZWF0dXJlcyDigJQgYW5jaG9yIHF1YWxpdHkgbWF5IGJlIHJlZHVjZWQuIikKCiMg4pSA4pSAIFN0ZXAgMzogUmFuayBieSBqb2ludCByZXNpZHVhbCB2YXJpYW5jZSDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKIyBHZW5lcyB0aGF0IGFyZSBoaWdobHkgdmFyaWFibGUgaW4gQk9USCB0aGUgaGVhbHRoeSByZWZlcmVuY2UgQU5EIHRoZQojIG1hbGlnbmFudCBxdWVyeSBhcmUgdGhlIG1vc3QgaW5mb3JtYXRpdmUgZm9yIGZpbmRpbmcgdHJ1ZSBiaW9sb2dpY2FsCiMgbmVhcmVzdCBuZWlnaGJvdXJzLiBSYW5raW5nIGJ5IGpvaW50IHZhcmlhbmNlIGVuc3VyZXMgZGlmZmVyZW50aWF0aW9uCiMgZ2VuZXMgKENDUjcsIFRDRjcsIEdaTUssIEZPWFAzKSByYW5rIGFib3ZlIGxpbmUtc3BlY2lmaWMgbm9pc2UuCgpyZWZfdmFyX21ldGEgICA8LSByZWZlcmVuY2VfaW50ZWdyYXRlZFtbIlNDVCJdXUBtZXRhLmZlYXR1cmVzCnF1ZXJ5X3Zhcl9tZXRhIDwtIE1hbGlnbmFudENENFRbWyJTQ1QiXV1AbWV0YS5mZWF0dXJlcwoKaWYgKCJyZXNpZHVhbF92YXJpYW5jZSIgJWluJSBjb2xuYW1lcyhyZWZfdmFyX21ldGEpICYmCiAgICAicmVzaWR1YWxfdmFyaWFuY2UiICVpbiUgY29sbmFtZXMocXVlcnlfdmFyX21ldGEpKSB7CgogIHJlZl9ydiAgIDwtIHJlZl92YXJfbWV0YVtjb21tb25fZmVhdHVyZXNfY2xlYW4sICAgInJlc2lkdWFsX3ZhcmlhbmNlIl0KICBxdWVyeV9ydiA8LSBxdWVyeV92YXJfbWV0YVtjb21tb25fZmVhdHVyZXNfY2xlYW4sICJyZXNpZHVhbF92YXJpYW5jZSJdCiAgcmVmX3J2W2lzLm5hKHJlZl9ydildICAgICA8LSAwCiAgcXVlcnlfcnZbaXMubmEocXVlcnlfcnYpXSA8LSAwCiAgbWVhbl9ydiAgICAgICAgIDwtIChyZWZfcnYgKyBxdWVyeV9ydikgLyAyCiAgY29tbW9uX2ZlYXR1cmVzIDwtIGNvbW1vbl9mZWF0dXJlc19jbGVhbltvcmRlcihtZWFuX3J2LCBkZWNyZWFzaW5nID0gVFJVRSldCgogIGNhdCgiVG9wIDIwIGZlYXR1cmVzIGJ5IGpvaW50IHJlc2lkdWFsIHZhcmlhbmNlOlxuIikKICBwcmludChoZWFkKGNvbW1vbl9mZWF0dXJlcywgMjApKQogIGNhdCgiXG4iKQoKfSBlbHNlIHsKICBjYXQoIuKaoO+4jyAgcmVzaWR1YWxfdmFyaWFuY2Ugbm90IGluIFNDVCBtZXRhLmZlYXR1cmVzIOKAlCB1c2luZyB1bnJhbmtlZCBjbGVhbiBsaXN0XG4iKQogIGNhdCgiICAgVGhpcyBpcyBPSyBidXQgcmFua2luZyB3b3VsZCBpbXByb3ZlIGFuY2hvciBxdWFsaXR5LlxuIikKICBjb21tb25fZmVhdHVyZXMgPC0gY29tbW9uX2ZlYXR1cmVzX2NsZWFuCn0KCiMg4pSA4pSAIFN0ZXAgNDogQ2Fub25pY2FsIFQgY2VsbCBtYXJrZXIgY2hlY2sg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACiMgVGhlc2UgbWFya2VycyBzcGFuIHRoZSBmdWxsIGRpZmZlcmVudGlhdGlvbiBheGlzIGFuZCBUcmVnIGJyYW5jaC4KIyBJZiBmZXdlciB0aGFuIDUgYXJlIHByZXNlbnQsIHRoZSBmZWF0dXJlIHNldCBjYW5ub3QgZGlzdGluZ3Vpc2gKIyBkaWZmZXJlbnRpYXRpb24gc3RhdGVzIGFuZCBhbmNob3IgcXVhbGl0eSB3aWxsIGJlIHBvb3IuCm1hcmtlcnNfY2hlY2sgPC0gYygKICAiQ0NSNyIsICJTRUxMIiwgIlRDRjciLCAiSUw3UiIsICAgICAgICAgICMgTmFpdmUvVENNCiAgIkdaTUIiLCAiR1pNSyIsICJQUkYxIiwgIkVPTUVTIiwgICAgICAgICAjIEVmZmVjdG9yL1RFTQogICJGT1hQMyIsICJJS1pGMiIsICAgICAgICAgICAgICAgICAgICAgICAgICMgVHJlZwogICJUT1giLCAiUERDRDEiLCAiTEFHMyIsICAgICAgICAgICAgICAgICAgIyBFeGhhdXN0aW9uCiAgIkNENDQiLCAiVEJYMjEiICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBBY3RpdmF0aW9uCikKZm91bmQgICAgIDwtIG1hcmtlcnNfY2hlY2tbbWFya2Vyc19jaGVjayAlaW4lIGNvbW1vbl9mZWF0dXJlc10Kbm90X2ZvdW5kIDwtIG1hcmtlcnNfY2hlY2tbIW1hcmtlcnNfY2hlY2sgJWluJSBjb21tb25fZmVhdHVyZXNdCgpjYXQoIj09PSBDYW5vbmljYWwgVCBjZWxsIG1hcmtlciBjaGVjayA9PT1cbiIpCmNhdCgiUHJlc2VudCAgKCIsIGxlbmd0aChmb3VuZCksICAgICIpOiIsIHBhc3RlKGZvdW5kLCAgICAgY29sbGFwc2UgPSAiLCAiKSwgIlxuIikKY2F0KCJBYnNlbnQgICAoIiwgbGVuZ3RoKG5vdF9mb3VuZCksIik6IiwgcGFzdGUobm90X2ZvdW5kLCBjb2xsYXBzZSA9ICIsICIpLCAiXG4iKQoKaWYgKGxlbmd0aChmb3VuZCkgPCA1KSB7CiAgd2FybmluZygi4pqg77iPICBGZXdlciB0aGFuIDUgY2Fub25pY2FsIG1hcmtlcnMgcHJlc2VudCDigJQgYW5jaG9yIHF1YWxpdHkgYXQgcmlzay4iKQp9IGVsc2UgaWYgKGxlbmd0aChmb3VuZCkgPCA4KSB7CiAgY2F0KCLimqDvuI8gIFNvbWUgbWFya2VycyBhYnNlbnQg4oCUIGxpa2VseSBoZXRlcm9nZW5lb3VzIGFjcm9zcyBsaW5lcyAoZXhwZWN0ZWQpXG4iKQogIGNhdCgiICAgUHJvamVjdGlvbiB3aWxsIHN0aWxsIHdvcmsgYnV0IGludGVycHJldCBwZXItbGluZSByZXN1bHRzIGNhcmVmdWxseVxuIikKfSBlbHNlIHsKICBjYXQoIuKchSBHb29kIG1hcmtlciBjb3ZlcmFnZSDigJQgZmVhdHVyZSBzZXQgY2FwdHVyZXMgZGlmZmVyZW50aWF0aW9uIGF4aXNcbiIpCn0KCiMg4pSA4pSAIFN0ZXAgNTogRmluZFRyYW5zZmVyQW5jaG9ycyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKIyBrLmFuY2hvciA9IDEwICA6IG5laWdoYm91cnMgdXNlZCB0byBmaW5kIGFuY2hvcnMg4oCUIGJhbGFuY2VkIGZvciB+MTFrIHJlZgojIGsuZmlsdGVyID0gNTAwIDogbmVpZ2hib3VycyBmb3IgYW5jaG9yIGZpbHRlcmluZyDigJQgcmVtb3ZlcyB3ZWFrIGFuY2hvcnMKIyBrLnNjb3JlICA9IDMwICA6IG5laWdoYm91cnMgZm9yIGFuY2hvciBzY29yaW5nIOKAlCB3ZWlnaHRzIGFuY2hvciBxdWFsaXR5CiMgZGltcyAgICAgPSAxOjMwOiB1c2UgYWxsIDMwIFBDQSBkaW1zIOKAlCBjYXB0dXJlcyBmdWxsIGRpZmZlcmVudGlhdGlvbiBzcGFjZQoKZGltc190b191c2UgPC0gbWluKDMwLAogICAgICAgICAgICAgICAgICAgbmNvbChFbWJlZGRpbmdzKHJlZmVyZW5jZV9pbnRlZ3JhdGVkLCAicGNhIikpLAogICAgICAgICAgICAgICAgICAgbmNvbChFbWJlZGRpbmdzKE1hbGlnbmFudENENFQsICAgICAgICAicGNhIikpKQpjYXQoIlxuRmluZGluZyBhbmNob3JzIHdpdGggZGltcyAxOiIsIGRpbXNfdG9fdXNlLCAiXG4iKQpjYXQoIkZlYXR1cmVzIHVzZWQ6IiwgbGVuZ3RoKGNvbW1vbl9mZWF0dXJlcyksICJcblxuIikKCmFuY2hvcnMgPC0gRmluZFRyYW5zZmVyQW5jaG9ycygKICByZWZlcmVuY2UgICAgICAgICAgICA9IHJlZmVyZW5jZV9pbnRlZ3JhdGVkLAogIHF1ZXJ5ICAgICAgICAgICAgICAgID0gTWFsaWduYW50Q0Q0VCwKICBmZWF0dXJlcyAgICAgICAgICAgICA9IGNvbW1vbl9mZWF0dXJlcywKICBub3JtYWxpemF0aW9uLm1ldGhvZCA9ICJTQ1QiLAogIHJlZmVyZW5jZS5yZWR1Y3Rpb24gID0gInBjYSIsCiAgZGltcyAgICAgICAgICAgICAgICAgPSAxOmRpbXNfdG9fdXNlLAogIGsuYW5jaG9yICAgICAgICAgICAgID0gMTAsCiAgay5maWx0ZXIgICAgICAgICAgICAgPSA1MDAsCiAgay5zY29yZSAgICAgICAgICAgICAgPSAzMCwKICB2ZXJib3NlICAgICAgICAgICAgICA9IFRSVUUKKQoKIyDilIDilIAgU3RlcCA2OiBBbmNob3IgcXVhbGl0eSBjaGVjayDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKIyBOT1RFOiBJbiBTZXVyYXQgRmluZFRyYW5zZmVyQW5jaG9ycyBpbnRlcm5hbCBjb252ZW50aW9uOgojICAgY2VsbDEgPSByZWZlcmVuY2UgaW5kaWNlcwojICAgY2VsbDIgPSBxdWVyeSBpbmRpY2VzICAoT1BQT1NJVEUgdG8gd2hhdCB2YXJpYWJsZSBuYW1lcyBzdWdnZXN0KQojIEFsd2F5cyB1c2UgY2VsbDHihpJyZWZlcmVuY2UgYW5kIGNlbGwy4oaScXVlcnkgZm9yIGJhcmNvZGUgbWFwcGluZy4KCmFuY2hvcl9kZiA8LSBhcy5kYXRhLmZyYW1lKHNsb3QoYW5jaG9ycywgImFuY2hvcnMiKSkKCiMgQ09SUkVDVCBiYXJjb2RlIG1hcHBpbmcg4oCUIGNlbGwxPXJlZmVyZW5jZSwgY2VsbDI9cXVlcnkKcmVmX2JhcmNvZGVzICAgPC0gY29sbmFtZXMocmVmZXJlbmNlX2ludGVncmF0ZWQpCnF1ZXJ5X2JhcmNvZGVzIDwtIGNvbG5hbWVzKE1hbGlnbmFudENENFQpCgphbmNob3JfZGYkcmVmX2JhcmNvZGUgICA8LSByZWZfYmFyY29kZXNbYW5jaG9yX2RmJGNlbGwxXQphbmNob3JfZGYkcXVlcnlfYmFyY29kZSA8LSBxdWVyeV9iYXJjb2Rlc1thbmNob3JfZGYkY2VsbDJdCgphbmNob3JfZGYkcXVlcnlfY2VsbGxpbmUgPC0gTWFsaWduYW50Q0Q0VCRjZWxsX2xpbmVbYW5jaG9yX2RmJHF1ZXJ5X2JhcmNvZGVdCmFuY2hvcl9kZiRyZWZfbGFiZWwgICAgICA8LSByZWZlcmVuY2VfaW50ZWdyYXRlZCRwcmVkaWN0ZWQuY2VsbHR5cGUubDJbYW5jaG9yX2RmJHJlZl9iYXJjb2RlXQoKIyBWZXJpZnkgbm8gTkFzCmNhdCgiXG49PT0gQmFyY29kZSBtYXBwaW5nIHZhbGlkYXRpb24gPT09XG4iKQpjYXQoIk5BIHJlZiBiYXJjb2RlcyAgOiIsIHN1bShpcy5uYShhbmNob3JfZGYkcmVmX2JhcmNvZGUpKSwgICAiXG4iKQpjYXQoIk5BIHF1ZXJ5IGJhcmNvZGVzOiIsIHN1bShpcy5uYShhbmNob3JfZGYkcXVlcnlfYmFyY29kZSkpLCAiXG4iKQpjYXQoIk5BIGNlbGwgbGluZXMgICAgOiIsIHN1bShpcy5uYShhbmNob3JfZGYkcXVlcnlfY2VsbGxpbmUpKSwgIlxuIikKCm5fYW5jaG9ycyAgICA8LSBucm93KGFuY2hvcl9kZikKYW5jaG9yX3JhdGlvIDwtIG5jb2woTWFsaWduYW50Q0Q0VCkgLyBuX2FuY2hvcnMKYW5jaG9yX3Njb3JlcyA8LSBhbmNob3JfZGYkc2NvcmUKCmNhdCgiXG49PT0gQW5jaG9yIHF1YWxpdHkgc3VtbWFyeSA9PT1cbiIpCmNhdCgiQW5jaG9ycyBmb3VuZCAgICA6Iiwgbl9hbmNob3JzLCAiXG4iKQpjYXQoIk1hbGlnbmFudCBjZWxscyAgOiIsIG5jb2woTWFsaWduYW50Q0Q0VCksICJcbiIpCmNhdCgiUmVmZXJlbmNlIGNlbGxzICA6IiwgbmNvbChyZWZlcmVuY2VfaW50ZWdyYXRlZCksICJcbiIpCmNhdChzcHJpbnRmKCJDZWxscyBwZXIgYW5jaG9yIDogJS4xZiA6IDEgIChpZGVhbCDiiaQgODoxKVxuIiwgYW5jaG9yX3JhdGlvKSkKY2F0KHNwcmludGYoIkFuY2hvciBzY29yZXMgICAgOiBtZWFuPSUuM2YgfCBtZWRpYW49JS4zZiB8IG1pbj0lLjNmIHwgbWF4PSUuM2ZcbiIsCiAgICAgICAgICAgIG1lYW4oYW5jaG9yX3Njb3JlcyksIG1lZGlhbihhbmNob3Jfc2NvcmVzKSwKICAgICAgICAgICAgbWluKGFuY2hvcl9zY29yZXMpLCAgbWF4KGFuY2hvcl9zY29yZXMpKSkKCmNhdCgiXG49PT0gQW5jaG9ycyBwZXIgY2VsbCBsaW5lID09PVxuIikKcHJpbnQodGFibGUoYW5jaG9yX2RmJHF1ZXJ5X2NlbGxsaW5lLCB1c2VOQSA9ICJpZmFueSIpKQoKY2F0KCJcbj09PSBBbmNob3JzIGJ5IHJlZmVyZW5jZSBsYWJlbCA9PT1cbiIpCmFuY2hvcl9kZiAlPiUKICBncm91cF9ieShyZWZfbGFiZWwpICU+JQogIHN1bW1hcmlzZShuX2FuY2hvcnMgID0gbigpLAogICAgICAgICAgICBtZWFuX3Njb3JlID0gcm91bmQobWVhbihzY29yZSksIDMpLAogICAgICAgICAgICAuZ3JvdXBzID0gImRyb3AiKSAlPiUKICBhcnJhbmdlKGRlc2Mobl9hbmNob3JzKSkgJT4lCiAgcHJpbnQoKQoKaWYgKGFuY2hvcl9yYXRpbyA+IDgpIHsKICB3YXJuaW5nKHBhc3RlMCgKICAgICLimqDvuI8gIExvdyBhbmNob3IgZGVuc2l0eSAoIiwgcm91bmQoYW5jaG9yX3JhdGlvLCAxKSwgIjoxKS5cbiIsCiAgICAiICAgMS4gSnVuayBnZW5lcyBzdGlsbCBkb21pbmF0aW5nIFBDQSDigJQgY2hlY2sgdG9wIDIwIGZlYXR1cmVzIGFib3ZlXG4iLAogICAgIiAgIDIuIFPDqXphcnkgY2VsbHMgdG9vIHRyYW5zY3JpcHRpb25hbGx5IGRpc3RhbnQgZnJvbSByZWZlcmVuY2VcbiIsCiAgICAiICAgMy4gSW5jcmVhc2Ugay5hbmNob3IgdG8gMTUgYW5kIHJlcnVuIgogICkpCn0gZWxzZSBpZiAoYW5jaG9yX3JhdGlvID4gNSkgewogIGNhdChzcHJpbnRmKCLimqDvuI8gIE1vZGVyYXRlIGFuY2hvciBkZW5zaXR5ICglLjFmOjEpIOKAlCBhY2NlcHRhYmxlXG4iLCBhbmNob3JfcmF0aW8pKQp9IGVsc2UgewogIGNhdChzcHJpbnRmKCLinIUgR29vZCBhbmNob3IgZGVuc2l0eSAoJS4xZjoxKSDigJQgcHJvamVjdGlvbiBxdWFsaXR5IHNob3VsZCBiZSBoaWdoXG4iLAogICAgICAgICAgICAgIGFuY2hvcl9yYXRpbykpCn0KYGBgCgoKIyMgY2hlY2sgeW91IGNhbiBkbyBhZnRlciBNYXBRdWVyeToKCmBgYHtyIGNoZWNrfQojIOKUgOKUgCBBTkNIT1IgRElBR05PU1RJQzogQmlvbG9neSB2cyBBcnRlZmFjdCDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKIyBJZiBMMy1MNyBhcmUgdHJ1bHkgZGVkaWZmZXJlbnRpYXRlZCwgdGhlaXIgUENBIHNwYWNlIHNob3VsZCBiZQojIERJU1RBTlQgZnJvbSB0aGUgcmVmZXJlbmNlLiBJZiBpdCBpcyBhIHRlY2huaWNhbCBpc3N1ZSwgdGhleSB3aWxsCiMgc2l0IGluIHRoZSBzYW1lIFBDQSBzcGFjZSBhcyBMMS9MMiBidXQganVzdCBub3QgZm9ybSBtdXR1YWwgTk4uCgpjYXQoIj09PSBQQ0Egc3BhY2UgY29tcGFyaXNvbjogTDEvTDIgdnMgTDMtTDcgPT09XG4iKQoKIyBHZXQgcmVmZXJlbmNlIFBDQSBjZW50cm9pZCAoVENNIHJlZ2lvbikKcmVmX3BjYSA8LSBFbWJlZGRpbmdzKHJlZmVyZW5jZV9pbnRlZ3JhdGVkLCAicGNhIilbLCAxOjEwXQpyZWZfY2VudHJvaWQgPC0gY29sTWVhbnMocmVmX3BjYSkKCiMgR2V0IHF1ZXJ5IFBDQSBlbWJlZGRpbmdzIHBlciBsaW5lCnF1ZXJ5X3BjYSA8LSBFbWJlZGRpbmdzKE1hbGlnbmFudENENFQsICJwY2EiKVssIDE6MTBdCgojIERpc3RhbmNlIGZyb20gZWFjaCBxdWVyeSBjZWxsIHRvIHJlZmVyZW5jZSBjZW50cm9pZApkaXN0X3RvX3JlZiA8LSByb3dTdW1zKAogIHN3ZWVwKHF1ZXJ5X3BjYSwgMiwgcmVmX2NlbnRyb2lkLCAiLSIpXjIKKQoKIyBDb21wYXJlIGRpc3RhbmNlcyBieSBjZWxsIGxpbmUKZGlzdF9kZiA8LSBkYXRhLmZyYW1lKAogIGNlbGwgICAgICAgPSBjb2xuYW1lcyhNYWxpZ25hbnRDRDRUKSwKICBjZWxsX2xpbmUgID0gTWFsaWduYW50Q0Q0VCRjZWxsX2xpbmUsCiAgZGlzdF90b19yZWYgPSBkaXN0X3RvX3JlZgopCgpkaXN0X3N1bW1hcnkgPC0gZGlzdF9kZiAlPiUKICBncm91cF9ieShjZWxsX2xpbmUpICU+JQogIHN1bW1hcmlzZSgKICAgIG1lYW5fZGlzdCAgPSByb3VuZChtZWFuKGRpc3RfdG9fcmVmKSwgIDIpLAogICAgbWVkaWFuX2Rpc3QgPSByb3VuZChtZWRpYW4oZGlzdF90b19yZWYpLCAyKSwKICAgIC5ncm91cHMgICAgPSAiZHJvcCIKICApICU+JQogIGFycmFuZ2UobWVhbl9kaXN0KQoKY2F0KCJcbkRpc3RhbmNlIGZyb20gcmVmZXJlbmNlIFBDQSBjZW50cm9pZCBwZXIgY2VsbCBsaW5lOlxuIikKY2F0KCIoTG93ZXIgPSBtb3JlIHNpbWlsYXIgdG8gaGVhbHRoeSByZWZlcmVuY2UpXG5cbiIpCnByaW50KGRpc3Rfc3VtbWFyeSkKCiMgQ2hlY2sgSFZHIG92ZXJsYXAgcGVyIGxpbmUgd2l0aCByZWZlcmVuY2UKY2F0KCJcbj09PSBIVkcgb3ZlcmxhcCB3aXRoIHJlZmVyZW5jZSBwZXIgbGluZSA9PT1cbiIpCnJlZl9odmdzX2NoZWNrIDwtIFZhcmlhYmxlRmVhdHVyZXMocmVmZXJlbmNlX2ludGVncmF0ZWQsIGFzc2F5ID0gIlNDVCIpCmZvciAobG4gaW4gcGFzdGUwKCJMIiwgMTo3KSkgewogIGxpbmVfY2VsbHMgIDwtIGNvbG5hbWVzKE1hbGlnbmFudENENFQpW01hbGlnbmFudENENFQkY2VsbF9saW5lID09IGxuXQogICMgQ2hlY2sgZXhwcmVzc2lvbiBvZiBrZXkgVCBjZWxsIG1hcmtlcnMgaW4gZWFjaCBsaW5lCiAgbWFya2VycyAgICAgPC0gYygiQ0NSNyIsICJUQ0Y3IiwgIlNFTEwiLCAiSUw3UiIsICJHWk1CIiwgIlRPWCIsICJLSVIzREwyIikKICBtYXJrZXJzX3ByZXNlbnQgPC0gaW50ZXJzZWN0KG1hcmtlcnMsIHJvd25hbWVzKE1hbGlnbmFudENENFQpKQogIGV4cHIgPC0gcm93TWVhbnMoCiAgICBHZXRBc3NheURhdGEoTWFsaWduYW50Q0Q0VCwgYXNzYXkgPSAiU0NUIiwgbGF5ZXIgPSAiZGF0YSIpWwogICAgICBtYXJrZXJzX3ByZXNlbnQsIGxpbmVfY2VsbHMsIGRyb3AgPSBGQUxTRV0KICApCiAgY2F0KHNwcmludGYoIlxuJXMg4oCUIGtleSBtYXJrZXIgZXhwcmVzc2lvbjpcbiIsIGxuKSkKICBwcmludChyb3VuZChleHByLCAzKSkKfQpgYGAKCgpgYGB7ciBjaGVjazJ9CiMgU2hvdyBUT1AgcmVmZXJlbmNlIHN0YXRlcyBlYWNoIGNlbGwgbGluZSBjb25uZWN0cyB0bwpjYXQoIlxuPT09IFdIQVQgRUFDSCBDRUxMIExJTkUgQU5DSE9SUyBUTyA9PT1cbiIpCmFuY2hvcl9kZiAlPiUKICBncm91cF9ieShxdWVyeV9jZWxsbGluZSwgcmVmX2xhYmVsKSAlPiUKICBzdW1tYXJpc2UoCiAgICBuX2FuY2hvcnMgID0gbigpLAogICAgbWVhbl9zY29yZSA9IHJvdW5kKG1lYW4oc2NvcmUpLCAzKSwKICAgIC5ncm91cHMgPSAiZHJvcCIKICApICU+JQogIGFycmFuZ2UocXVlcnlfY2VsbGxpbmUsIGRlc2Mobl9hbmNob3JzKSkgJT4lCiAgcHJpbnQobiA9IDMwKSAgIyBTaG93IGFsbCBjb21iaW5hdGlvbnMKCmBgYAoKIyMgTWFwUXVlcnkg4oCUIFByb2plY3Qgb250byBSZWZlcmVuY2UgVU1BUAoKYGBge3IgbWFwLXF1ZXJ5fQpjYXQoIj09PSBNYXBRdWVyeSBwcm9qZWN0aW9uID09PVxuIikKCiMgTWFwUXVlcnkgc2ltdWx0YW5lb3VzbHk6CiMgICAxLiBUcmFuc2ZlcnMgbGFiZWxzICsgcHNldWRvdGltZSBmcm9tIHJlZmVyZW5jZSB0byBxdWVyeSAoVHJhbnNmZXJEYXRhKQojICAgMi4gUHJvamVjdHMgcXVlcnkgUENBIGludG8gcmVmZXJlbmNlIFBDQSBzcGFjZSAoSW50ZWdyYXRlRW1iZWRkaW5ncykKIyAgIDMuIFByb2plY3RzIHF1ZXJ5IG9udG8gZnJvemVuIHJlZmVyZW5jZSBVTUFQIChQcm9qZWN0VU1BUCkKIyBNYWxpZ25hbnQgY2VsbHMgZG8gTk9UIGFsdGVyIHRoZSByZWZlcmVuY2Ug4oCUIHRoZXkgYXJlIHJlYWQtb25seSBwYXNzZW5nZXJzLgoKbWFwcGVkX01hbGlnbmFudENENFQgPC0gTWFwUXVlcnkoCiAgYW5jaG9yc2V0ICAgICAgICAgPSBhbmNob3JzLAogIHF1ZXJ5ICAgICAgICAgICAgID0gTWFsaWduYW50Q0Q0VCwKICByZWZlcmVuY2UgICAgICAgICA9IHJlZmVyZW5jZV9pbnRlZ3JhdGVkLAogIHJlZmRhdGEgICAgICAgICAgID0gbGlzdCgKICAgIHByZWRpY3RlZC5jZWxsdHlwZS5sMiA9ICJwcmVkaWN0ZWQuY2VsbHR5cGUubDIiLCAgICMgUFJJTUFSWTogc3RhdGUgbGFiZWwKICAgIHBzZXVkb3RpbWUgICAgICAgICAgICA9ICJtb25vY2xlM19wc2V1ZG90aW1lIiAgICAgICMgQ09OVElOVU9VUzogcG9zaXRpb24KICApLCAKICByZWZlcmVuY2UucmVkdWN0aW9uID0gInBjYSIsCiAgcmVkdWN0aW9uLm1vZGVsICAgICA9ICJ1bWFwIgopCgoKY2F0KCLinIUgTWFwUXVlcnkgY29tcGxldGVcbiIpCmNhdCgiTWFwcGVkIGNlbGxzOiIsIG5jb2wobWFwcGVkX01hbGlnbmFudENENFQpLCAiXG4iKQoKIyBDb2VyY2UgcHNldWRvdGltZSB0byBudW1lcmljIOKAlCBTZXVyYXQgVHJhbnNmZXJEYXRhIHJldHVybnMgY2hhcmFjdGVyCm1hcHBlZF9NYWxpZ25hbnRDRDRUJHByZWRpY3RlZC5wc2V1ZG90aW1lIDwtIGFzLm51bWVyaWMoCiAgbWFwcGVkX01hbGlnbmFudENENFQkcHJlZGljdGVkLnBzZXVkb3RpbWUKKQoKY2F0KCJcblRyYW5zZmVycmVkIHBzZXVkb3RpbWUgc3VtbWFyeTpcbiIpCnByaW50KHN1bW1hcnkobWFwcGVkX01hbGlnbmFudENENFQkcHJlZGljdGVkLnBzZXVkb3RpbWUpKQpjYXQoIlxuVHJhbnNmZXJyZWQgQXppbXV0aCBsMiBkaXN0cmlidXRpb246XG4iKQpwcmludCh0YWJsZShtYXBwZWRfTWFsaWduYW50Q0Q0VCRwcmVkaWN0ZWQucHJlZGljdGVkLmNlbGx0eXBlLmwyKSkKCiMgRklYRUQgRGl2ZXJzaXR5IGNoZWNrOiBIYW5kbGUgTkEgcHJlZGljdGlvbnMgcHJvcGVybHkKY2F0KCJcbj09PSBEaXZlcnNpdHkgY2hlY2sgPT09XG4iKQpsMl90YWJsZSA8LSB0YWJsZShtYXBwZWRfTWFsaWduYW50Q0Q0VCRwcmVkaWN0ZWQucHJlZGljdGVkLmNlbGx0eXBlLmwyLCB1c2VOQSA9ICJhbHdheXMiKQpsMl9kaXN0IDwtIHByb3AudGFibGUobDJfdGFibGUpCgpjYXQoIkxhYmVsIGRpc3RyaWJ1dGlvbjpcbiIpCnByaW50KGwyX3RhYmxlKQoKbmFfY291bnQgPC0gbDJfdGFibGVbIjxOQT4iXQpuYV9wY3QgPC0gaWZlbHNlKGlzLm5hKG5hX2NvdW50KSwgMCwgcm91bmQoMTAwICogbmFfcGN0LCAxKSkKCiMgTm9uLU5BIHByZWRpY3Rpb25zIG9ubHkKbm9uX25hX3RhYmxlIDwtIGwyX3RhYmxlWyFuYW1lcyhsMl90YWJsZSkgJWluJSAiPE5BPiJdCmlmIChsZW5ndGgobm9uX25hX3RhYmxlKSA+IDApIHsKICBub25fbmFfZGlzdCA8LSBwcm9wLnRhYmxlKG5vbl9uYV90YWJsZSkKICBtYXhfc3RhdGUgPC0gbmFtZXMod2hpY2gubWF4KG5vbl9uYV9kaXN0KSkKICBtYXhfcGN0IDwtIHJvdW5kKDEwMCAqIG1heChub25fbmFfZGlzdCksIDEpCiAgCiAgY2F0KHNwcmludGYoIlxuTkEgZnJhY3Rpb246ICUuMWYlJVxuIiwgbmFfcGN0KSkKICBjYXQoc3ByaW50ZigiRG9taW5hbnQgc3RhdGU6ICVzICglLjFmJSUgb2Ygbm9uLU5BKVxuIiwgbWF4X3N0YXRlLCBtYXhfcGN0KSkKICAKICBpZiAobWF4X3BjdCA+IDkwKSB7CiAgICB3YXJuaW5nKHNwcmludGYoIuKaoO+4jyAgJXMgZG9taW5hdGVzICglLjFmJSUpIOKAlCBjaGVjayBTQ1QvZmVhdHVyZSBmaWx0ZXJpbmciLCBtYXhfc3RhdGUsIG1heF9wY3QpKQogIH0gZWxzZSB7CiAgICBjYXQoIuKchSBEaXZlcnNlIG1hcHBpbmcgYWNyb3NzIHN0YXRlc1xuIikKICB9Cn0gZWxzZSB7CiAgY2F0KCLimqDvuI8gIE5vIGNvbmZpZGVudCBwcmVkaWN0aW9ucyBtYWRlXG4iKQp9CgpgYGAKYGBge3IgcGxvdDEsIGZpZy53aWR0aD0xNCwgZmlnLmhlaWdodD02fQoKIyBTw6l6YXJ5LXNwZWNpZmljIGRpdmVyc2l0eSBjaGVjawpkb21pbmFudF9wY3QgPC0gcm91bmQoMTAwICogbWF4KHByb3AudGFibGUobm9uX25hX3RhYmxlKSksIDEpCmlmIChkb21pbmFudF9wY3QgPiA5NSkgewogIGNhdChzcHJpbnRmKCLinIUgVENNLWRvbWluYW50ICglLjFmJSUpID0gQ0xBU1NJQyBTw4laQVJZIFBIRU5PVFlQRVxuIiwgZG9taW5hbnRfcGN0KSkKICBjYXQoIk1pbm9yIHN1YmNsb25lcyAoVEVNL1RyZWcvTmFpdmUpID0gZXhwZWN0ZWQgaGV0ZXJvZ2VuZWl0eVxuIikKfSBlbHNlIHsKICBjYXQoc3ByaW50Zigi4pyFIERpdmVyc2UgbWFwcGluZzogJXMgPSAlLjFmJSVcbiIsIG1heF9zdGF0ZSwgZG9taW5hbnRfcGN0KSkKfQoKCmBgYAoKYGBge3IgcGxvdDIsIGZpZy53aWR0aD0xNCwgZmlnLmhlaWdodD02fQoKIyAxLiBQZXItY2VsbC1saW5lIGJyZWFrZG93biAoeW91ciB0aGVzaXMgZ29sZCkKdGFibGUobWFwcGVkX01hbGlnbmFudENENFQkY2VsbF9saW5lLCBtYXBwZWRfTWFsaWduYW50Q0Q0VCRwcmVkaWN0ZWQucHJlZGljdGVkLmNlbGx0eXBlLmwyKQoKIyAyLiBQbG90IG9uIHRyYWplY3RvcnkgVU1BUApEaW1QbG90KG1hcHBlZF9NYWxpZ25hbnRDRDRULCBncm91cC5ieSA9ICJwcmVkaWN0ZWQucHJlZGljdGVkLmNlbGx0eXBlLmwyIiwgcmVkdWN0aW9uID0gInJlZi51bWFwIikKCiMgMy4gUHNldWRvdGltZSB2aW9saW4gYnkgY2VsbCBsaW5lClZsblBsb3QobWFwcGVkX01hbGlnbmFudENENFQsIGZlYXR1cmVzID0gInByZWRpY3RlZC5wc2V1ZG90aW1lIiwgZ3JvdXAuYnkgPSAiY2VsbF9saW5lIiwgcHQuc2l6ZSA9IDApCgoKYGBgCgoKIyBWaXN1YWxpc2UgUHJvamVjdGlvbgoKIyMgUmVmZXJlbmNlICsgTWFsaWduYW50IE92ZXJsYXkKCmBgYHtyIHBsb3Qtb3ZlcmxheSwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTV9CiMgQnVpbGQgZGF0YSBmcmFtZXMgZm9yIGdncGxvdCDigJQgcmVuYW1lIFVNQVAgY29sdW1ucyBjb25zaXN0ZW50bHkKcmVmX3VtYXAgPC0gZGF0YS5mcmFtZSgKICBFbWJlZGRpbmdzKHJlZmVyZW5jZV9pbnRlZ3JhdGVkLCAidW1hcCIpLAogIGwyICAgICAgICAgPSByZWZlcmVuY2VfaW50ZWdyYXRlZCRwcmVkaWN0ZWQuY2VsbHR5cGUubDIsCiAgcHNldWRvdGltZSA9IHJlZmVyZW5jZV9pbnRlZ3JhdGVkJG1vbm9jbGUzX3BzZXVkb3RpbWUsCiAgdHlwZSAgICAgICA9ICJSZWZlcmVuY2UiCikKY29sbmFtZXMocmVmX3VtYXApWzE6Ml0gPC0gYygiVU1BUF8xIiwgIlVNQVBfMiIpCgpxdWVyeV91bWFwIDwtIGRhdGEuZnJhbWUoCiAgRW1iZWRkaW5ncyhtYXBwZWRfTWFsaWduYW50Q0Q0VCwgInJlZi51bWFwIiksCiAgcHNldWRvdGltZSA9IG1hcHBlZF9NYWxpZ25hbnRDRDRUJHByZWRpY3RlZC5wc2V1ZG90aW1lLAogIGNlbGxfbGluZSAgPSBtYXBwZWRfTWFsaWduYW50Q0Q0VCRjZWxsX2xpbmUsCiAgbDJfcSAgICAgICA9IG1hcHBlZF9NYWxpZ25hbnRDRDRUJHByZWRpY3RlZC5wcmVkaWN0ZWQuY2VsbHR5cGUubDIsCiAgdHlwZSAgICAgICA9ICJNYWxpZ25hbnQiCikKY29sbmFtZXMocXVlcnlfdW1hcClbMToyXSA8LSBjKCJVTUFQXzEiLCAiVU1BUF8yIikKCmNlbGxfbGluZV9wYWxldHRlIDwtIGNvbG9yUmFtcFBhbGV0dGUoYnJld2VyLnBhbCg4LCAiRGFyazIiKSkoCiAgbGVuZ3RoKHVuaXF1ZShxdWVyeV91bWFwJGNlbGxfbGluZSkpKQpuYW1lcyhjZWxsX2xpbmVfcGFsZXR0ZSkgPC0gc29ydCh1bmlxdWUocXVlcnlfdW1hcCRjZWxsX2xpbmUpKQoKYWxsX2wyX2xhYmVscyAgICAgPC0gdW5pcXVlKGMoYXMuY2hhcmFjdGVyKHJlZl91bWFwJGwyKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzLmNoYXJhY3RlcihxdWVyeV91bWFwJGwyX3EpKSkKYXppbXV0aF9sMl9jb2xvcnMgPC0gZXh0ZW5kX3BhbGV0dGUoYXppbXV0aF9sMl9jb2xvcnMsIGFsbF9sMl9sYWJlbHMpCgojIFBhbmVsIDE6IG1hbGlnbmFudCBjb2xvdXJlZCBieSB0cmFuc2ZlcnJlZCBwc2V1ZG90aW1lCnBfcHQgPC0gZ2dwbG90KCkgKwogIGdlb21fcG9pbnQoZGF0YSA9IHJlZl91bWFwLAogICAgICAgICAgICAgYWVzKHggPSBVTUFQXzEsIHkgPSBVTUFQXzIpLAogICAgICAgICAgICAgY29sb3IgPSByZWZlcmVuY2VfY29sb3IsIHNpemUgPSAwLjQsIGFscGhhID0gMC42KSArCiAgZ2VvbV9wb2ludChkYXRhID0gcXVlcnlfdW1hcCAlPiUgZmlsdGVyKGlzLmZpbml0ZShwc2V1ZG90aW1lKSksCiAgICAgICAgICAgICBhZXMoeCA9IFVNQVBfMSwgeSA9IFVNQVBfMiwgY29sb3IgPSBwc2V1ZG90aW1lKSwKICAgICAgICAgICAgIHNpemUgPSAxLjIsIGFscGhhID0gMC45KSArCiAgc2NhbGVfY29sb3JfdmlyaWRpc19jKG9wdGlvbiA9ICJwbGFzbWEiLCBuYW1lID0gIlBzZXVkb3RpbWUiLAogICAgICAgICAgICAgICAgICAgICAgICBuYS52YWx1ZSA9ICJncmV5NDAiKSArCiAgdGhlbWVfY2xhc3NpYyhiYXNlX3NpemUgPSAxMSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIpKSArCiAgZ2d0aXRsZSgiU8OpemFyeSBDZWxscyBQcm9qZWN0ZWQgb250byBSZWZlcmVuY2VcbihUcmFuc2ZlcnJlZCBwc2V1ZG90aW1lKSIpCgojIFBhbmVsIDI6IHJlZmVyZW5jZSBjb2xvdXJlZCBieSBzdGF0ZSwgbWFsaWduYW50IGluIHJlZApwX3N0YXRlIDwtIGdncGxvdCgpICsKICBnZW9tX3BvaW50KGRhdGEgPSByZWZfdW1hcCwKICAgICAgICAgICAgIGFlcyh4ID0gVU1BUF8xLCB5ID0gVU1BUF8yLCBjb2xvciA9IGwyKSwKICAgICAgICAgICAgIHNpemUgPSAwLjQsIGFscGhhID0gMC43KSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGF6aW11dGhfbDJfY29sb3JzLAogICAgICAgICAgICAgICAgICAgICBuYW1lID0gIkF6aW11dGggbDJcbihyZWZlcmVuY2UpIiwgbmEudmFsdWUgPSAiZ3JleTYwIikgKwogIGdlb21fcG9pbnQoZGF0YSA9IHF1ZXJ5X3VtYXAsCiAgICAgICAgICAgICBhZXMoeCA9IFVNQVBfMSwgeSA9IFVNQVBfMiksCiAgICAgICAgICAgICBjb2xvciA9IG1hbGlnbmFudF9jb2xvciwgc2l6ZSA9IDEuMCwgYWxwaGEgPSAwLjgpICsKICB0aGVtZV9jbGFzc2ljKGJhc2Vfc2l6ZSA9IDExKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgbGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjQsICJjbSIpLAogICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSA4KSkgKwogIGdndGl0bGUoIlJlZmVyZW5jZSBTdGF0ZXMgKGNvbG91cilcbisgU8OpemFyeSBDZWxscyAocmVkIG92ZXJsYXkpIikKCnBfcHQgfCBwX3N0YXRlCgpgYGAKCiMjIFRyYW5zZmVycmVkIExhYmVscyArIFBlciBDZWxsIExpbmUKCmBgYHtyIHBsb3QtbGFiZWxzLWxpbmVzLCBmaWcud2lkdGg9MTQsIGZpZy5oZWlnaHQ9Nn0KcF9sMiA8LSBnZ3Bsb3QoKSArCiAgZ2VvbV9wb2ludChkYXRhID0gcmVmX3VtYXAsCiAgICAgICAgICAgICBhZXMoeCA9IFVNQVBfMSwgeSA9IFVNQVBfMiksCiAgICAgICAgICAgICBjb2xvciA9IHJlZmVyZW5jZV9jb2xvciwgc2l6ZSA9IDAuNCwgYWxwaGEgPSAwLjYpICsKICBnZW9tX3BvaW50KGRhdGEgPSBxdWVyeV91bWFwLAogICAgICAgICAgICAgYWVzKHggPSBVTUFQXzEsIHkgPSBVTUFQXzIsIGNvbG9yID0gbDJfcSksCiAgICAgICAgICAgICBzaXplID0gMS4yLCBhbHBoYSA9IDAuOSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBhemltdXRoX2wyX2NvbG9ycywKICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICJBemltdXRoIGwyXG4odHJhbnNmZXJyZWQpIiwgbmEudmFsdWUgPSAiZ3JleTQwIikgKwogIHRoZW1lX2NsYXNzaWMoYmFzZV9zaXplID0gMTEpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiKSkgKwogIGdndGl0bGUoIlPDqXphcnkgQ2VsbHMg4oCUIFRyYW5zZmVycmVkIEF6aW11dGggbDIgTGFiZWxzIikKCnBfbGluZXMgPC0gZ2dwbG90KCkgKwogIGdlb21fcG9pbnQoZGF0YSA9IHJlZl91bWFwLAogICAgICAgICAgICAgYWVzKHggPSBVTUFQXzEsIHkgPSBVTUFQXzIpLAogICAgICAgICAgICAgY29sb3IgPSByZWZlcmVuY2VfY29sb3IsIHNpemUgPSAwLjQsIGFscGhhID0gMC41KSArCiAgZ2VvbV9wb2ludChkYXRhID0gcXVlcnlfdW1hcCwKICAgICAgICAgICAgIGFlcyh4ID0gVU1BUF8xLCB5ID0gVU1BUF8yLCBjb2xvciA9IGNlbGxfbGluZSksCiAgICAgICAgICAgICBzaXplID0gMS4wLCBhbHBoYSA9IDAuOSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjZWxsX2xpbmVfcGFsZXR0ZSwgbmFtZSA9ICJDZWxsIGxpbmUiKSArCiAgdGhlbWVfY2xhc3NpYyhiYXNlX3NpemUgPSAxMSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIpKSArCiAgZ2d0aXRsZSgiU8OpemFyeSBDZWxscyBwZXIgQ2VsbCBMaW5lIikKCnBfbDIgfCBwX2xpbmVzCmBgYAoKIyMgUGVyIENlbGwgTGluZSBGYWNldAoKYGBge3IgcGxvdC1mYWNldCwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTZ9CmdncGxvdCgpICsKICBnZW9tX3BvaW50KGRhdGEgPSByZWZfdW1hcCwKICAgICAgICAgICAgIGFlcyh4ID0gVU1BUF8xLCB5ID0gVU1BUF8yKSwKICAgICAgICAgICAgIGNvbG9yID0gImdyZXk2MCIsIHNpemUgPSAwLjMsIGFscGhhID0gMC41KSArCiAgZ2VvbV9wb2ludChkYXRhID0gcXVlcnlfdW1hcCAlPiUgZmlsdGVyKGlzLmZpbml0ZShwc2V1ZG90aW1lKSksCiAgICAgICAgICAgICBhZXMoeCA9IFVNQVBfMSwgeSA9IFVNQVBfMiwgY29sb3IgPSBwc2V1ZG90aW1lKSwKICAgICAgICAgICAgIHNpemUgPSAwLjksIGFscGhhID0gMC44NSkgKwogIGZhY2V0X3dyYXAofiBjZWxsX2xpbmUsIG5jb2wgPSA0KSArCiAgc2NhbGVfY29sb3JfdmlyaWRpc19jKG9wdGlvbiA9ICJwbGFzbWEiLCBuYW1lID0gIlBzZXVkb3RpbWUiLAogICAgICAgICAgICAgICAgICAgICAgICBuYS52YWx1ZSA9ICJncmV5NjAiKSArCiAgdGhlbWVfY2xhc3NpYyhiYXNlX3NpemUgPSAxMCkgKwogIHRoZW1lKHN0cmlwLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDksIGZhY2UgPSAiYm9sZCIpLAogICAgICAgIHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICIjRThGNEZEIiksCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgZmFjZSA9ICJib2xkIikpICsKICBnZ3RpdGxlKCJQZXIgQ2VsbCBMaW5lIOKAlCBQcm9qZWN0aW9uIG9udG8gUmVmZXJlbmNlIFVNQVAgKFBzZXVkb3RpbWUpIikKYGBgCgoKIyBRdWFudGlmaWNhdGlvbiDigJQgU3RhdGUgQXNzaWdubWVudCB3aXRoIEJpb2xvZ3ktQW5jaG9yZWQgQmlucwoKIyMgQXNzaWduIEVhY2ggTWFsaWduYW50IENlbGwgdG8gYSBSZWZlcmVuY2UgU3RhdGUKCmBgYHtyIHN0YXRlLWFzc2lnbm1lbnR9CiMg4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQCiMgUFNFVURPVElNRSBCSU4gQVNTSUdOTUVOVCDigJQgU1RBVEUtQU5DSE9SRUQgQk9VTkRBUklFUwojIOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkOKVkAojCiMgQmluIGJvdW5kYXJpZXMgPSBtaWRwb2ludCBiZXR3ZWVuIGFkamFjZW50IHJlZmVyZW5jZSBzdGF0ZSBtZWRpYW5zLgojIFRoaXMgaXMgYmlvbG9naWNhbGx5IHByaW5jaXBsZWQ6IHRoZSBib3VuZGFyeSBiZXR3ZWVuIFRDTSBhbmQgVEVNIGJpbnMKIyBpcyBleGFjdGx5IGhhbGZ3YXkgYmV0d2VlbiB0aGUgbWVkaWFuIFRDTSBwc2V1ZG90aW1lIGFuZCBtZWRpYW4gVEVNCiMgcHNldWRvdGltZSBpbiB0aGUgaGVhbHRoeSByZWZlcmVuY2Ug4oCUIE5PVCBhbiBhcmJpdHJhcnkgMzNyZC82NnRoCiMgcGVyY2VudGlsZSBjdXQuCiMKIyBUV08gTElORUFHRVMgbW9kZWxsZWQ6CiMgICBFZmZlY3RvciBheGlzIChwc2V1ZG90aW1lLWJhc2VkKToKIyAgICAgTmFpdmUtbGlrZSAg4oaSIGJlbG93IG1pZHBvaW50KE5haXZlLCBUQ00pCiMgICAgIFRDTS1saWtlICAgIOKGkiBiZXR3ZWVuIG1pZHBvaW50KE5haXZlLFRDTSkgYW5kIG1pZHBvaW50KFRDTSxURU0pCiMgICAgIFRFTS1saWtlICAgIOKGkiBiZXR3ZWVuIG1pZHBvaW50KFRDTSxURU0pIGFuZCBtaWRwb2ludChURU0sVGVtcmEpCiMgICAgIFRlbXJhLWxpa2UgIOKGkiBhYm92ZSBtaWRwb2ludChURU0sVGVtcmEpCiMKIyAgIFJlZ3VsYXRvcnkgYnJhbmNoIChsYWJlbC1iYXNlZCk6CiMgICAgIFRyZWctbGlrZSAgIOKGkiB0cmFuc2ZlcnJlZCBBemltdXRoIGxhYmVsID09ICJUcmVnIiBhdCBBTlkgcHNldWRvdGltZQojICAgICBSQVRJT05BTEU6IFRyZWcgaXMgYSBicmFuY2ggZnJvbSBUQ00sIG5vdCBhIGxpbmVhciBwb3NpdGlvbiBvbiB0aGUKIyAgICAgZWZmZWN0b3IgYXhpcy4gUHNldWRvdGltZSBmb3IgVHJlZyBjZWxscyBpcyBsb3dlciB0aGFuIFRFTSBieSBkZXNpZ24KIyAgICAgKGNvcnJlY3QgdG9wb2xvZ3kpLCBidXQgdXNpbmcgcHNldWRvdGltZSBhbG9uZSB3b3VsZCBtaXNhc3NpZ24gc29tZQojICAgICBUcmVnIGNlbGxzIHRvIFRDTS1saWtlLiBMYWJlbCArIGJyYW5jaCBsb2dpYyBpcyBtb3JlIGFjY3VyYXRlLgojICAgICBJTVBPUlRBTlQ6IFRoaXMgbWVhbnMgVHJlZy1saWtlIGJpbiAlIHdpbGwgZXF1YWwgVHJlZyBsYWJlbCAlIGV4YWN0bHkuCiMgICAgIFRoZSBsYWJlbCB2cyBwc2V1ZG90aW1lIGJpbiBjb21wYXJpc29uIGlzIG9ubHkgbWVhbmluZ2Z1bCBmb3IgdGhlCiMgICAgIGVmZmVjdG9yIGF4aXMgKE5haXZlL1RDTS9URU0vVGVtcmEpLgoKIyDilIDilIAgU3RlcCAxOiBSZWZlcmVuY2Ugc3RhdGUgbWVkaWFuIHBzZXVkb3RpbWVzIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgAojIFRoZXNlIHdlcmUgYWxyZWFkeSBjb21wdXRlZCBpbiBwc2V1ZG90aW1lLXJvb3QgY2h1bmsgKG5haXZlX21lZCwgdGNtX21lZAojIGV0Yy4pIGJ1dCB3ZSByZWNvbXB1dGUgaGVyZSBmb3IgY2xhcml0eSBhbmQgY2h1bmsgaW5kZXBlbmRlbmNlLgpwdF9zdGF0ZV9tZWRpYW5zIDwtIHJlZmVyZW5jZV9pbnRlZ3JhdGVkQG1ldGEuZGF0YSAlPiUKICBmaWx0ZXIoaXMuZmluaXRlKG1vbm9jbGUzX3BzZXVkb3RpbWUpKSAlPiUKICBncm91cF9ieShwcmVkaWN0ZWQuY2VsbHR5cGUubDIpICU+JQogIHN1bW1hcmlzZSgKICAgIG4gICAgICA9IG4oKSwKICAgIG1lZF9wdCA9IHJvdW5kKG1lZGlhbihtb25vY2xlM19wc2V1ZG90aW1lLCBuYS5ybSA9IFRSVUUpLCAzKSwKICAgIC5ncm91cHMgPSAiZHJvcCIKICApICU+JQogIGFycmFuZ2UobWVkX3B0KQoKY2F0KCI9PT0gUmVmZXJlbmNlIHN0YXRlIG1lZGlhbiBwc2V1ZG90aW1lcyA9PT1cbiIpCnByaW50KHB0X3N0YXRlX21lZGlhbnMpCgojIEV4dHJhY3QgbWVkaWFucyAob3ZlcndyaXRlIHdpdGggZnJlc2ggdmFsdWVzIGZyb20gdGhpcyBjaHVuaykKbmFpdmVfbWVkIDwtIHB0X3N0YXRlX21lZGlhbnMkbWVkX3B0WwogIHB0X3N0YXRlX21lZGlhbnMkcHJlZGljdGVkLmNlbGx0eXBlLmwyID09ICJDRDQgTmFpdmUiXQp0Y21fbWVkICAgPC0gcHRfc3RhdGVfbWVkaWFucyRtZWRfcHRbCiAgcHRfc3RhdGVfbWVkaWFucyRwcmVkaWN0ZWQuY2VsbHR5cGUubDIgPT0gIkNENCBUQ00iXQp0cmVnX21lZCAgPC0gcHRfc3RhdGVfbWVkaWFucyRtZWRfcHRbCiAgcHRfc3RhdGVfbWVkaWFucyRwcmVkaWN0ZWQuY2VsbHR5cGUubDIgPT0gIlRyZWciXQp0ZW1fbWVkICAgPC0gcHRfc3RhdGVfbWVkaWFucyRtZWRfcHRbCiAgcHRfc3RhdGVfbWVkaWFucyRwcmVkaWN0ZWQuY2VsbHR5cGUubDIgPT0gIkNENCBURU0iXQp0ZW1yYV9tZWQgPC0gcHRfc3RhdGVfbWVkaWFucyRtZWRfcHRbCiAgcHRfc3RhdGVfbWVkaWFucyRwcmVkaWN0ZWQuY2VsbHR5cGUubDIgPT0gIkNENCBUZW1yYS9DVEwiXQoKIyDilIDilIAgU3RlcCAyOiBCaW4gYm91bmRhcmllcyBhcyBtaWRwb2ludHMgYmV0d2VlbiBzdGF0ZSBtZWRpYW5zIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgApuYWl2ZV90Y21fY3V0IDwtIChuYWl2ZV9tZWQgKyB0Y21fbWVkKSAgLyAyCnRjbV90ZW1fY3V0ICAgPC0gKHRjbV9tZWQgICsgdGVtX21lZCkgICAvIDIKdGVtX3RlbXJhX2N1dCA8LSAodGVtX21lZCAgKyB0ZW1yYV9tZWQpIC8gMgp0Y21fdHJlZ19jdXQgIDwtICh0Y21fbWVkICArIHRyZWdfbWVkKSAgLyAyICAjIHN0b3JlZCBmb3IgcmVmZXJlbmNlIG9ubHkKCmNhdCgiXG49PT0gQmlvbG9neS1hbmNob3JlZCBiaW4gYm91bmRhcmllcyA9PT1cbiIpCmNhdChzcHJpbnRmKCIgIE5haXZlICB8IFRDTSAgOiAlLjNmXG4iLCBuYWl2ZV90Y21fY3V0KSkKY2F0KHNwcmludGYoIiAgVENNICAgIHwgVEVNICA6ICUuM2YgIChtYWluIGVmZmVjdG9yIGF4aXMpXG4iLCB0Y21fdGVtX2N1dCkpCmNhdChzcHJpbnRmKCIgIFRFTSAgICB8IFRlbXJhOiAlLjNmXG4iLCB0ZW1fdGVtcmFfY3V0KSkKY2F0KHNwcmludGYoIiAgVENNICAgIHwgVHJlZyA6ICUuM2YgIChyZWd1bGF0b3J5IGJyYW5jaCDigJQgbGFiZWwtYmFzZWQsIG5vdCB1c2VkIGFzIGN1dClcbiIsCiAgICAgICAgICAgIHRjbV90cmVnX2N1dCkpCgojIOKUgOKUgCBTdGVwIDM6IEFzc2lnbiB3b3JraW5nIGNvbHVtbnMg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACm1hcHBlZF9NYWxpZ25hbnRDRDRUJHN0YXRlX2F6aW11dGhfbDIgIDwtCiAgbWFwcGVkX01hbGlnbmFudENENFQkcHJlZGljdGVkLnByZWRpY3RlZC5jZWxsdHlwZS5sMgoKbWFwcGVkX01hbGlnbmFudENENFQkcHNldWRvdGltZV92YWx1ZSAgPC0KICBhcy5udW1lcmljKG1hcHBlZF9NYWxpZ25hbnRDRDRUJHByZWRpY3RlZC5wc2V1ZG90aW1lKQoKIyDilIDilIAgU3RlcCA0OiBBc3NpZ24gcHNldWRvdGltZSBiaW5zIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgAptYXBwZWRfTWFsaWduYW50Q0Q0VCRwc2V1ZG90aW1lX2JpbiA8LSBjYXNlX3doZW4oCgogICMgVHJlZyBsaW5lYWdlIOKAlCBsYWJlbCB0YWtlcyBwcmlvcml0eSBvdmVyIHBzZXVkb3RpbWUgcG9zaXRpb24KICAjIGJlY2F1c2UgVHJlZyBpcyBhIGJyYW5jaCwgbm90IGEgcG9pbnQgb24gdGhlIGxpbmVhciBlZmZlY3RvciBheGlzCiAgbWFwcGVkX01hbGlnbmFudENENFQkc3RhdGVfYXppbXV0aF9sMiA9PSAiVHJlZyIgfiAiVHJlZy1saWtlIiwKCiAgIyBFZmZlY3RvciBheGlzIGJpbnMg4oCUIHBzZXVkb3RpbWUtYmFzZWQKICBtYXBwZWRfTWFsaWduYW50Q0Q0VCRwc2V1ZG90aW1lX3ZhbHVlICA8ICBuYWl2ZV90Y21fY3V0IH4gIk5haXZlLWxpa2UiLAoKICBtYXBwZWRfTWFsaWduYW50Q0Q0VCRwc2V1ZG90aW1lX3ZhbHVlID49IG5haXZlX3RjbV9jdXQgJgogIG1hcHBlZF9NYWxpZ25hbnRDRDRUJHBzZXVkb3RpbWVfdmFsdWUgPCAgdGNtX3RlbV9jdXQgICB+ICJUQ00tbGlrZSIsCgogIG1hcHBlZF9NYWxpZ25hbnRDRDRUJHBzZXVkb3RpbWVfdmFsdWUgPj0gdGNtX3RlbV9jdXQgICAmCiAgbWFwcGVkX01hbGlnbmFudENENFQkcHNldWRvdGltZV92YWx1ZSA8ICB0ZW1fdGVtcmFfY3V0IH4gIlRFTS1saWtlIiwKCiAgbWFwcGVkX01hbGlnbmFudENENFQkcHNldWRvdGltZV92YWx1ZSA+PSB0ZW1fdGVtcmFfY3V0IH4gIlRlbXJhLWxpa2UiLAoKICBUUlVFIH4gTkFfY2hhcmFjdGVyXwopCgojIFNldCBmYWN0b3IgbGV2ZWxzIGluIGJpb2xvZ2ljYWwgdHJhamVjdG9yeSBvcmRlcgptYXBwZWRfTWFsaWduYW50Q0Q0VCRwc2V1ZG90aW1lX2JpbiA8LSBmYWN0b3IoCiAgbWFwcGVkX01hbGlnbmFudENENFQkcHNldWRvdGltZV9iaW4sCiAgbGV2ZWxzID0gYygiTmFpdmUtbGlrZSIsICJUQ00tbGlrZSIsICJUcmVnLWxpa2UiLCAiVEVNLWxpa2UiLCAiVGVtcmEtbGlrZSIpCikKCiMg4pSA4pSAIFN0ZXAgNTogVHJlZyBjb3VudCBjaGVjayDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKIyBGSVg6IGFkZGVkIGFzIGlkZW50aWZpZWQgaW4gZXhwZXJ0IHJldmlldy4gSWYgPDUwIFRyZWctbGFiZWxsZWQgY2VsbHMsCiMgdGhlIFRyZWctbGlrZSBiaW4gaXMgbmVnbGlnaWJsZSBhbmQgc2hvdWxkIGJlIGZsYWdnZWQuCm5fdHJlZyA8LSBzdW0obWFwcGVkX01hbGlnbmFudENENFQkc3RhdGVfYXppbXV0aF9sMiA9PSAiVHJlZyIsIG5hLnJtID0gVFJVRSkKY2F0KHNwcmludGYoIlxuVHJlZy1sYWJlbGxlZCBtYWxpZ25hbnQgY2VsbHM6ICVkICglLjJmJSUgb2YgdG90YWwpXG4iLAogICAgICAgICAgICBuX3RyZWcsCiAgICAgICAgICAgIDEwMCAqIG5fdHJlZyAvIG5jb2wobWFwcGVkX01hbGlnbmFudENENFQpKSkKCmlmIChuX3RyZWcgPT0gMCkgewogIG1lc3NhZ2UoIuKEue+4jyAgTm8gVHJlZy1sYWJlbGxlZCBjZWxscyDigJQgVHJlZy1saWtlIGJpbiB3aWxsIGJlIGVtcHR5LlxuIiwKICAgICAgICAgICIgICBUaGlzIGlzIGJpb2xvZ2ljYWxseSBwbGF1c2libGU6IFPDqXphcnkgY2VsbHMgcmFyZWx5IG1hcCB0byBUcmVnLlxuIiwKICAgICAgICAgICIgICBUaGUgVHJlZyBiaW4gY29sdW1uIGlzIHJldGFpbmVkIGZvciBjb21wbGV0ZW5lc3MuIikKfSBlbHNlIGlmIChuX3RyZWcgPCA1MCkgewogIG1lc3NhZ2Uoc3ByaW50ZigKICAgICLimqDvuI8gIE9ubHkgJWQgVHJlZy1sYWJlbGxlZCBjZWxscyAoJS4yZiUlKS5cbiIsIG5fdHJlZywKICAgIDEwMCAqIG5fdHJlZyAvIG5jb2wobWFwcGVkX01hbGlnbmFudENENFQpKSwKICAgICIgICBUcmVnLWxpa2UgYmluIGV4aXN0cyBidXQgaXMgdmVyeSBzbWFsbCDigJQgaW50ZXJwcmV0IHdpdGggY2F1dGlvbi5cbiIsCiAgICAiICAgQ29uc2lkZXIgbm90aW5nIHRoaXMgYXMgJ3JhcmUvYWJzZW50IFRyZWcgaWRlbnRpdHknIGluIHJlc3VsdHMuIikKfSBlbHNlIHsKICBjYXQoc3ByaW50Zigi4pyFIFRyZWctbGlrZSBiaW4gaGFzICVkIGNlbGxzIOKAlCBzdWZmaWNpZW50IGZvciByZXBvcnRpbmcuXG4iLCBuX3RyZWcpKQp9CgpjYXQoIlxuTm90ZTogVHJlZy1saWtlIGJpbiAlIGVxdWFscyBUcmVnIGxhYmVsICUgYnkgZGVzaWduIChsYWJlbC1iYXNlZCBhc3NpZ25tZW50KS5cbiIpCmNhdCgiTGFiZWwgdnMgcHNldWRvdGltZSBiaW4gZGlzY29yZGFuY2UgaXMgb25seSBpbnRlcnByZXRhYmxlIGZvciB0aGVcbiIpCmNhdCgiZWZmZWN0b3IgYXhpczogTmFpdmUtbGlrZSAvIFRDTS1saWtlIC8gVEVNLWxpa2UgLyBUZW1yYS1saWtlLlxuIikKCiMg4pSA4pSAIFN0ZXAgNjogUmVzdWx0cyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKY2F0KCJcbj09PSBTdGF0ZSBhc3NpZ25tZW50IGNvbXBsZXRlID09PVxuIikKY2F0KCJcbkF6aW11dGggbDIgbGFiZWwgKGNlbGwgb2Ygb3JpZ2luIOKAlCBXSEFUIHRoZSBjZWxsIHJlc2VtYmxlcyk6XG4iKQpwcmludCh0YWJsZShtYXBwZWRfTWFsaWduYW50Q0Q0VCRzdGF0ZV9hemltdXRoX2wyKSkKCmNhdCgiXG5Qc2V1ZG90aW1lIGJpbiAoZGlmZmVyZW50aWF0aW9uIHBvc2l0aW9uIOKAlCBXSEVSRSB0aGUgY2VsbCBzaXRzKTpcbiIpCnByaW50KHRhYmxlKG1hcHBlZF9NYWxpZ25hbnRDRDRUJHBzZXVkb3RpbWVfYmluKSkKCiMg4pSA4pSAIEtFWSBSRVNVTFQ6IGNyb3NzLXRhYmxlIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgApjYXQoIlxu4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQXG4iKQpjYXQoIktFWSBSRVNVTFQ6IExhYmVsIChjZWxsIG9mIG9yaWdpbikgdnMgUHNldWRvdGltZSBCaW5cbiIpCmNhdCgi4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQXG4iKQpjYXQoIlRDTS1sYWJlbGxlZCBjZWxscyBpbiBURU0vVGVtcmEgYmlucyA9IGVmZmVjdG9yIHNrZXdpbmdcbiIpCmNhdCgiYmV5b25kIHRoZSBUQ00gYXJyZXN0IHBvaW50XG5cbiIpCgojIEZJWDogZXhwbGljaXQgY29sdW1uIG5hbWluZyBmcm9tIHRhYmxlKCkgb3V0cHV0IOKAlCBhdm9pZHMgcmVuYW1lKCkgZmFpbHVyZQojIHdoZW4gVmFyMS9WYXIyIGNvbHVtbiBuYW1lcyBkaWZmZXIgZnJvbSBleHBlY3RlZCBMYWJlbC9QVF9CaW4KY3Jvc3NfdGFiIDwtIHRhYmxlKAogIExhYmVsICA9IG1hcHBlZF9NYWxpZ25hbnRDRDRUJHN0YXRlX2F6aW11dGhfbDIsCiAgUFRfQmluID0gbWFwcGVkX01hbGlnbmFudENENFQkcHNldWRvdGltZV9iaW4KKQpwcmludChjcm9zc190YWIpCmNhdCgiXG5Ob3RlOiBUcmVnIHJvdyB3aWxsIHNob3cgMTAwJSBUcmVnLWxpa2Ug4oCUIHRoaXMgaXMgYnkgZGVzaWduLlxuIikKYGBgCgojIyBRdWFudGlmaWNhdGlvbiBUYWJsZXMKCmBgYHtyIHF1YW50aWZ5LXN0YXRlc30KIyDilIDilIAgT3ZlcmFsbCBzdGF0ZSBkaXN0cmlidXRpb24g4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACnN0YXRlX3N1bW1hcnkgPC0gbWFwcGVkX01hbGlnbmFudENENFRAbWV0YS5kYXRhICU+JQogIGNvdW50KHN0YXRlX2F6aW11dGhfbDIsIG5hbWUgPSAibl9jZWxscyIpICU+JQogIG11dGF0ZShwY3QgPSByb3VuZCgxMDAgKiBuX2NlbGxzIC8gc3VtKG5fY2VsbHMpLCAxKSkgJT4lCiAgYXJyYW5nZShkZXNjKG5fY2VsbHMpKQoKY2F0KCI9PT0gT3ZlcmFsbCBBemltdXRoIGwyIFN0YXRlIERpc3RyaWJ1dGlvbiA9PT1cbiIpCnByaW50KHN0YXRlX3N1bW1hcnkpCgojIOKUgOKUgCBQZXIgY2VsbCBsaW5lIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgApzdGF0ZV9wZXJfbGluZSA8LSBtYXBwZWRfTWFsaWduYW50Q0Q0VEBtZXRhLmRhdGEgJT4lCiAgY291bnQoY2VsbF9saW5lLCBzdGF0ZV9hemltdXRoX2wyLCBuYW1lID0gIm5fY2VsbHMiKSAlPiUKICBncm91cF9ieShjZWxsX2xpbmUpICU+JQogIG11dGF0ZShwY3QgPSByb3VuZCgxMDAgKiBuX2NlbGxzIC8gc3VtKG5fY2VsbHMpLCAxKSkgJT4lCiAgdW5ncm91cCgpICU+JQogIGFycmFuZ2UoY2VsbF9saW5lLCBkZXNjKG5fY2VsbHMpKQoKY2F0KCJcbj09PSBTdGF0ZSBEaXN0cmlidXRpb24gcGVyIENlbGwgTGluZSA9PT1cbiIpCnByaW50KHN0YXRlX3Blcl9saW5lLCBuID0gSW5mKQoKIyDilIDilIAgUHNldWRvdGltZSBiaW4gZGlzdHJpYnV0aW9uIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgApwdF9iaW5fc3VtbWFyeSA8LSBtYXBwZWRfTWFsaWduYW50Q0Q0VEBtZXRhLmRhdGEgJT4lCiAgZmlsdGVyKCFpcy5uYShwc2V1ZG90aW1lX2JpbikpICU+JQogIGNvdW50KHBzZXVkb3RpbWVfYmluLCBuYW1lID0gIm5fY2VsbHMiKSAlPiUKICBtdXRhdGUocGN0ID0gcm91bmQoMTAwICogbl9jZWxscyAvIHN1bShuX2NlbGxzKSwgMSkpCgpjYXQoIlxuPT09IFBzZXVkb3RpbWUgQmluIFN1bW1hcnkgPT09XG4iKQpwcmludChwdF9iaW5fc3VtbWFyeSkKCnB0X2Jpbl9wZXJfbGluZSA8LSBtYXBwZWRfTWFsaWduYW50Q0Q0VEBtZXRhLmRhdGEgJT4lCiAgZmlsdGVyKCFpcy5uYShwc2V1ZG90aW1lX2JpbikpICU+JQogIGNvdW50KGNlbGxfbGluZSwgcHNldWRvdGltZV9iaW4sIG5hbWUgPSAibl9jZWxscyIpICU+JQogIGdyb3VwX2J5KGNlbGxfbGluZSkgJT4lCiAgbXV0YXRlKHBjdCA9IHJvdW5kKDEwMCAqIG5fY2VsbHMgLyBzdW0obl9jZWxscyksIDEpKSAlPiUKICB1bmdyb3VwKCkKCmNhdCgiXG49PT0gUHNldWRvdGltZSBCaW4gcGVyIENlbGwgTGluZSA9PT1cbiIpCnByaW50KHB0X2Jpbl9wZXJfbGluZSwgbiA9IEluZikKYGBgCgojIyBCYXIgQ2hhcnRzIOKAlCBTdGF0ZSBQcm9wb3J0aW9ucwoKYGBge3IgYmFycGxvdC1zdGF0ZXMsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD05fQojIFRvcCBwYW5lbDogbGFiZWwgdHJhbnNmZXIgKGNlbGwgb2Ygb3JpZ2luKQpwX2Jhcl9sYWJlbHMgPC0gZ2dwbG90KHN0YXRlX3Blcl9saW5lLAogICAgICAgICAgICAgICAgICAgICAgICBhZXMoeCA9IGNlbGxfbGluZSwgeSA9IHBjdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSBzdGF0ZV9hemltdXRoX2wyKSkgKwogIGdlb21fY29sKHBvc2l0aW9uID0gInN0YWNrIiwgY29sb3IgPSAid2hpdGUiLCBsaW5ld2lkdGggPSAwLjMpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gaWZlbHNlKHBjdCA+PSA1LCBwYXN0ZTAocGN0LCAiJSIpLCAiIikpLAogICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX3N0YWNrKHZqdXN0ID0gMC41KSwKICAgICAgICAgICAgc2l6ZSA9IDMsIGNvbG9yID0gIndoaXRlIiwgZm9udGZhY2UgPSAiYm9sZCIpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBhemltdXRoX2wyX2NvbG9ycywgbmEudmFsdWUgPSAiZ3JleTcwIiwKICAgICAgICAgICAgICAgICAgICBuYW1lID0gIkF6aW11dGggbDJcbnN0YXRlIikgKwogIHRoZW1lX2NsYXNzaWMoYmFzZV9zaXplID0gMTEpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQwLCBoanVzdCA9IDEpLAogICAgICAgIHBsb3QudGl0bGUgID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiKSkgKwogIGxhYnModGl0bGUgICAgPSAiU8OpemFyeSBDZWxsIFN0YXRlIENvbXBvc2l0aW9uIHBlciBDZWxsIExpbmUiLAogICAgICAgc3VidGl0bGUgPSAiTGFiZWwgdHJhbnNmZXI6IGNlbGwgb2Ygb3JpZ2luICh3aGljaCBub3JtYWwgc3RhdGUgZG9lcyB0aGlzIGNlbGwgcmVzZW1ibGU/KSIsCiAgICAgICB4ID0gIkNlbGwgTGluZSIsIHkgPSAiJSBDZWxscyIpCgojIEJvdHRvbSBwYW5lbDogcHNldWRvdGltZSBiaW5zIChkaWZmZXJlbnRpYXRpb24gcG9zaXRpb24pCnBfYmFyX2JpbnMgPC0gZ2dwbG90KHB0X2Jpbl9wZXJfbGluZSwKICAgICAgICAgICAgICAgICAgICAgIGFlcyh4ID0gY2VsbF9saW5lLCB5ID0gcGN0LAogICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSBwc2V1ZG90aW1lX2JpbikpICsKICBnZW9tX2NvbChwb3NpdGlvbiA9ICJzdGFjayIsIGNvbG9yID0gIndoaXRlIiwgbGluZXdpZHRoID0gMC4zKSArCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IGlmZWxzZShwY3QgPj0gNSwgcGFzdGUwKHBjdCwgIiUiKSwgIiIpKSwKICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9zdGFjayh2anVzdCA9IDAuNSksCiAgICAgICAgICAgIHNpemUgPSAzLCBjb2xvciA9ICJ3aGl0ZSIsIGZvbnRmYWNlID0gImJvbGQiKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYmluX2NvbG9ycywgbmFtZSA9ICJQc2V1ZG90aW1lXG5CaW4iLAogICAgICAgICAgICAgICAgICAgIGRyb3AgPSBGQUxTRSkgKwogIHRoZW1lX2NsYXNzaWMoYmFzZV9zaXplID0gMTEpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQwLCBoanVzdCA9IDEpLAogICAgICAgIHBsb3QudGl0bGUgID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiKSkgKwogIGxhYnModGl0bGUgICAgPSAiU8OpemFyeSBDZWxsIFBzZXVkb3RpbWUgQmluIERpc3RyaWJ1dGlvbiBwZXIgQ2VsbCBMaW5lIiwKICAgICAgIHN1YnRpdGxlID0gcGFzdGUwKAogICAgICAgICAiRGlmZmVyZW50aWF0aW9uIHBvc2l0aW9uIOKAlCBzdGF0ZS1hbmNob3JlZCBib3VuZGFyaWVzOiAiLAogICAgICAgICAiTmFpdmV8VENNPSIsIHJvdW5kKG5haXZlX3RjbV9jdXQsIDIpLAogICAgICAgICAiIHwgVENNfFRFTT0iLCByb3VuZCh0Y21fdGVtX2N1dCwgMiksCiAgICAgICAgICIgfCBURU18VGVtcmE9Iiwgcm91bmQodGVtX3RlbXJhX2N1dCwgMiksCiAgICAgICAgICIgfCBUcmVnPWxhYmVsLWJhc2VkIiksCiAgICAgICB4ID0gIkNlbGwgTGluZSIsIHkgPSAiJSBDZWxscyIpCgpwX2Jhcl9sYWJlbHMgLyBwX2Jhcl9iaW5zICsKICBwbG90X2Fubm90YXRpb24oCiAgICB0aXRsZSA9ICJDZWxsIG9mIE9yaWdpbiAoTGFiZWwgVHJhbnNmZXIpIHZzIERpZmZlcmVudGlhdGlvbiBQb3NpdGlvbiAoUHNldWRvdGltZSBCaW5zKVxuU8OpemFyeSBTeW5kcm9tZSDigJQgQ0Q0KyBUIENlbGwgVHJhamVjdG9yeSIsCiAgICB0aGVtZSA9IHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIHNpemUgPSAxMywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYWNlID0gImJvbGQiKSkKICApCmBgYAoKIyMgSGVhdG1hcHMg4oCUIFN0YXRlIGFuZCBCaW4gUHJvcG9ydGlvbnMKCmBgYHtyIGhlYXRtYXAtc3RhdGVzLCBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD02fQojIEhlYXRtYXAgMTogQXppbXV0aCBsMiBsYWJlbCBwcm9wb3J0aW9ucyBwZXIgY2VsbCBsaW5lCmhlYXRtYXBfZGYgPC0gc3RhdGVfcGVyX2xpbmUgJT4lCiAgc2VsZWN0KGNlbGxfbGluZSwgc3RhdGVfYXppbXV0aF9sMiwgcGN0KSAlPiUKICBwaXZvdF93aWRlcihuYW1lc19mcm9tICA9IHN0YXRlX2F6aW11dGhfbDIsCiAgICAgICAgICAgICAgdmFsdWVzX2Zyb20gPSBwY3QsIHZhbHVlc19maWxsID0gMCkKaGVhdG1hcF9tYXQgICAgICAgICAgIDwtIGFzLm1hdHJpeChoZWF0bWFwX2RmWywgLTFdKQpyb3duYW1lcyhoZWF0bWFwX21hdCkgPC0gaGVhdG1hcF9kZiRjZWxsX2xpbmUKCnBoZWF0bWFwKAogIGhlYXRtYXBfbWF0LAogIGNvbG9yICAgICAgICAgICA9IGNvbG9yUmFtcFBhbGV0dGUoYygid2hpdGUiLCAiI0ZFRTA5MCIsICIjRDczMDI3IikpKDUwKSwKICBkaXNwbGF5X251bWJlcnMgPSBUUlVFLCBudW1iZXJfZm9ybWF0ID0gIiUuMWYlJSIsCiAgZm9udHNpemVfbnVtYmVyID0gOCwgZm9udHNpemVfcm93ID0gMTAsIGZvbnRzaXplX2NvbCA9IDksCiAgYW5nbGVfY29sICAgICAgID0gNDUsIGNsdXN0ZXJfcm93cyA9IFRSVUUsIGNsdXN0ZXJfY29scyA9IFRSVUUsCiAgbWFpbiAgICAgICAgICAgID0gIiUgU8OpemFyeSBDZWxscyBwZXIgQXppbXV0aCBsMiBTdGF0ZVxuKExhYmVsIHRyYW5zZmVyIOKAlCBjZWxsIG9mIG9yaWdpbikiLAogIGJvcmRlcl9jb2xvciAgICA9ICJ3aGl0ZSIKKQoKIyBIZWF0bWFwIDI6IHBzZXVkb3RpbWUgYmluIHByb3BvcnRpb25zIOKAlCBiaW9sb2dpY2FsIG9yZGVyIHByZXNlcnZlZApiaW5faGVhdG1hcF9kZiA8LSBwdF9iaW5fcGVyX2xpbmUgJT4lCiAgc2VsZWN0KGNlbGxfbGluZSwgcHNldWRvdGltZV9iaW4sIHBjdCkgJT4lCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSAgPSBwc2V1ZG90aW1lX2JpbiwKICAgICAgICAgICAgICB2YWx1ZXNfZnJvbSA9IHBjdCwgdmFsdWVzX2ZpbGwgPSAwKQoKIyBGSVg6IHVzZSBhbnlfb2YoKSB0byBzYWZlbHkgc2VsZWN0IG9ubHkgY29sdW1ucyB0aGF0IGV4aXN0CiMgT3JpZ2luYWwgdXNlZCBiYXJlIHNlbGVjdCgpIHdoaWNoIGVycm9ycyBpZiBhIGJpbiBjb2x1bW4gaXMgbWlzc2luZwphbGxfYmluX2xldmVscyA8LSBjKCJOYWl2ZS1saWtlIiwgIlRDTS1saWtlIiwgIlRyZWctbGlrZSIsCiAgICAgICAgICAgICAgICAgICAgIlRFTS1saWtlIiwgIlRlbXJhLWxpa2UiKQpmb3IgKGIgaW4gYWxsX2Jpbl9sZXZlbHMpIHsKICBpZiAoIWIgJWluJSBjb2xuYW1lcyhiaW5faGVhdG1hcF9kZikpIGJpbl9oZWF0bWFwX2RmW1tiXV0gPC0gMAp9CmJpbl9oZWF0bWFwX2RmIDwtIGJpbl9oZWF0bWFwX2RmICU+JQogIHNlbGVjdChjZWxsX2xpbmUsIGFueV9vZihhbGxfYmluX2xldmVscykpCgpiaW5fbWF0ICAgICAgICAgICA8LSBhcy5tYXRyaXgoYmluX2hlYXRtYXBfZGZbLCAtMV0pCnJvd25hbWVzKGJpbl9tYXQpIDwtIGJpbl9oZWF0bWFwX2RmJGNlbGxfbGluZQoKcGhlYXRtYXAoCiAgYmluX21hdCwKICBjb2xvciAgICAgICAgICAgPSBjb2xvclJhbXBQYWxldHRlKGMoIndoaXRlIiwgIiM3NEFERDEiLCAiI0Q3MzAyNyIpKSg1MCksCiAgZGlzcGxheV9udW1iZXJzID0gVFJVRSwgbnVtYmVyX2Zvcm1hdCA9ICIlLjFmJSUiLAogIGZvbnRzaXplX251bWJlciA9IDgsIGZvbnRzaXplX3JvdyA9IDEwLCBmb250c2l6ZV9jb2wgPSA5LAogIGFuZ2xlX2NvbCAgICAgICA9IDQ1LAogIGNsdXN0ZXJfcm93cyAgICA9IFRSVUUsCiAgY2x1c3Rlcl9jb2xzICAgID0gRkFMU0UsICAjIHByZXNlcnZlIGJpb2xvZ2ljYWwgb3JkZXIgb2YgYmlucwogIG1haW4gICAgICAgICAgICA9ICIlIFPDqXphcnkgQ2VsbHMgcGVyIFBzZXVkb3RpbWUgQmluXG4oU3RhdGUtYW5jaG9yZWQgYm91bmRhcmllcyDigJQgZGlmZmVyZW50aWF0aW9uIHBvc2l0aW9uKSIsCiAgYm9yZGVyX2NvbG9yICAgID0gIndoaXRlIgopCmBgYAoKCiMgUHNldWRvdGltZSBBbmFseXNpcwoKIyMgUHNldWRvdGltZSBEaXN0cmlidXRpb246IFJlZmVyZW5jZSB2cyBNYWxpZ25hbnQKCmBgYHtyIHBzZXVkb3RpbWUtZGlzdHJpYnV0aW9uLCBmaWcud2lkdGg9MTQsIGZpZy5oZWlnaHQ9Nn0KcmVmX3B0IDwtIGRhdGEuZnJhbWUoCiAgcHNldWRvdGltZSA9IHJlZmVyZW5jZV9pbnRlZ3JhdGVkJG1vbm9jbGUzX3BzZXVkb3RpbWVbCiAgICBpcy5maW5pdGUocmVmZXJlbmNlX2ludGVncmF0ZWQkbW9ub2NsZTNfcHNldWRvdGltZSldLAogIHNvdXJjZSA9ICJIZWFsdGh5IHJlZmVyZW5jZSIKKQptYWxfcHQgPC0gZGF0YS5mcmFtZSgKICBwc2V1ZG90aW1lID0gbWFwcGVkX01hbGlnbmFudENENFQkcHNldWRvdGltZV92YWx1ZVsKICAgIGlzLmZpbml0ZShtYXBwZWRfTWFsaWduYW50Q0Q0VCRwc2V1ZG90aW1lX3ZhbHVlKV0sCiAgc291cmNlID0gIlPDqXphcnkgY2VsbHMiCikKcHRfY29tYmluZWQgPC0gYmluZF9yb3dzKHJlZl9wdCwgbWFsX3B0KQoKIyBSZWZlcmVuY2Ugc3RhdGUgbWVkaWFuIGxpbmVzIOKAlCB2aXN1YWwgYW5jaG9ycyBzaG93aW5nIHdoZXJlIGVhY2gKIyBoZWFsdGh5IHN0YXRlIHNpdHMgb24gdGhlIHBzZXVkb3RpbWUgYXhpcwpzdGF0ZV92bGluZXMgPC0gZGF0YS5mcmFtZSgKICBzdGF0ZSA9IGMoIk5haXZlIiwgICAgIlRDTSIsICAgICJUcmVnIiwgICAgIlRFTSIsICAgICJUZW1yYSIpLAogIG1lZCAgID0gYyhuYWl2ZV9tZWQsICB0Y21fbWVkLCAgdHJlZ19tZWQsICB0ZW1fbWVkLCAgdGVtcmFfbWVkKSwKICBjb2wgICA9IGMoIiMyMTY2QUMiLCAgIiM3NEFERDEiLCIjNzYyQTgzIiwgICIjRkVFMDkwIiwiI0Q3MzAyNyIpCikKCnBfZGVuc2l0eSA8LSBnZ3Bsb3QocHRfY29tYmluZWQsIGFlcyh4ID0gcHNldWRvdGltZSwgZmlsbCA9IHNvdXJjZSkpICsKICBnZW9tX2RlbnNpdHkoYWxwaGEgPSAwLjY1LCBhZGp1c3QgPSAxLjIpICsKICBnZW9tX3ZsaW5lKGRhdGEgID0gc3RhdGVfdmxpbmVzLAogICAgICAgICAgICAgYWVzKHhpbnRlcmNlcHQgPSBtZWQsIGNvbG9yID0gc3RhdGUpLAogICAgICAgICAgICAgbGluZXR5cGUgPSAiZGFzaGVkIiwgbGluZXdpZHRoID0gMC44KSArCiAgc2NhbGVfY29sb3JfbWFudWFsKAogICAgdmFsdWVzID0gc2V0TmFtZXMoc3RhdGVfdmxpbmVzJGNvbCwgc3RhdGVfdmxpbmVzJHN0YXRlKSwKICAgIG5hbWUgICA9ICJSZWZlcmVuY2Ugc3RhdGVcbm1lZGlhbiIpICsKICBzY2FsZV9maWxsX21hbnVhbCgKICAgIHZhbHVlcyA9IGMoIkhlYWx0aHkgcmVmZXJlbmNlIiA9ICIjNDU3NUI0IiwKICAgICAgICAgICAgICAgIlPDqXphcnkgY2VsbHMiICAgICAgPSBtYWxpZ25hbnRfY29sb3IpLAogICAgbmFtZSA9ICIiKSArCiAgdGhlbWVfY2xhc3NpYyhiYXNlX3NpemUgPSAxMikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIsCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgZmFjZSA9ICJib2xkIikpICsKICBsYWJzKHRpdGxlICAgID0gIlBzZXVkb3RpbWUgRGlzdHJpYnV0aW9uOiBIZWFsdGh5IFJlZmVyZW5jZSB2cyBTw6l6YXJ5IENlbGxzIiwKICAgICAgIHN1YnRpdGxlID0gIlBlYWsgc2hpZnQgaW5kaWNhdGVzIGRpZmZlcmVudGlhdGlvbiBhcnJlc3QgcG9pbnQgfCBkYXNoZWQgPSByZWZlcmVuY2Ugc3RhdGUgbWVkaWFucyIsCiAgICAgICB4ID0gIk1vbm9jbGUzIFBzZXVkb3RpbWUiLCB5ID0gIkRlbnNpdHkiKQoKcF9yaWRnZSA8LSBtYXBwZWRfTWFsaWduYW50Q0Q0VEBtZXRhLmRhdGEgJT4lCiAgZmlsdGVyKGlzLmZpbml0ZShwc2V1ZG90aW1lX3ZhbHVlKSkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gcHNldWRvdGltZV92YWx1ZSwgeSA9IGNlbGxfbGluZSwgZmlsbCA9IGNlbGxfbGluZSkpICsKICBnZW9tX2RlbnNpdHlfcmlkZ2VzKGFscGhhID0gMC44MCwgc2NhbGUgPSAxLjMsIHJlbF9taW5faGVpZ2h0ID0gMC4wMSwKICAgICAgICAgICAgICAgICAgICAgIHF1YW50aWxlX2xpbmVzID0gVFJVRSwgcXVhbnRpbGVzID0gMikgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGMobmFpdmVfbWVkLCB0Y21fbWVkLCB0cmVnX21lZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0ZW1fbWVkLCAgdGVtcmFfbWVkKSwKICAgICAgICAgICAgIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gImdyZXk0MCIsIGxpbmV3aWR0aCA9IDAuNSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGNlbGxfbGluZV9wYWxldHRlKSArCiAgdGhlbWVfY2xhc3NpYyhiYXNlX3NpemUgPSAxMSkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiKSkgKwogIGxhYnModGl0bGUgICAgPSAiUHNldWRvdGltZSBwZXIgQ2VsbCBMaW5lIiwKICAgICAgIHN1YnRpdGxlID0gIlZlcnRpY2FsIGxpbmUgPSBtZWRpYW4gfCBEYXNoZWQgPSByZWZlcmVuY2Ugc3RhdGUgbWVkaWFucyIsCiAgICAgICB4ID0gIlBzZXVkb3RpbWUiLCB5ID0gIiIpCgpwX2RlbnNpdHkgfCBwX3JpZGdlCmBgYAoKIyMgUHNldWRvdGltZSBTdW1tYXJ5IFN0YXRpc3RpY3MKCmBgYHtyIHBzZXVkb3RpbWUtc3RhdHN9CiMgRklYOiBwY3QgY29sdW1ucyBjb21wdXRlZCBvbmx5IGZvciBub24tVHJlZyBjZWxscyBvbiB0aGUgZWZmZWN0b3IgYXhpcy4KIyBPcmlnaW5hbCBzY3JpcHQgY291bnRlZCBhbGwgY2VsbHMgaW5jbHVkaW5nIFRyZWcgd2hpY2ggaW5mbGF0ZXMgcGN0X3RjbQojIChUcmVnIGNlbGxzIGhhdmUgVENNLXJhbmdlIHBzZXVkb3RpbWUgYW5kIHdvdWxkIGJlIGNvdW50ZWQgYXMgVENNLWxpa2UpLgojIE5vdyB3ZSBzZXBhcmF0ZSBUcmVnIGNlbGxzIGFuZCBjb21wdXRlIGVmZmVjdG9yLWF4aXMgc3RhdHMgaW5kZXBlbmRlbnRseS4KCnB0X3N0YXRzIDwtIG1hcHBlZF9NYWxpZ25hbnRDRDRUQG1ldGEuZGF0YSAlPiUKICBmaWx0ZXIoaXMuZmluaXRlKHBzZXVkb3RpbWVfdmFsdWUpKSAlPiUKICBncm91cF9ieShjZWxsX2xpbmUpICU+JQogIHN1bW1hcmlzZSgKICAgIG5fY2VsbHMgICAgICA9IG4oKSwKICAgIG5fdHJlZyAgICAgICA9IHN1bShzdGF0ZV9hemltdXRoX2wyID09ICJUcmVnIiwgbmEucm0gPSBUUlVFKSwKICAgIG5fZWZmZWN0b3IgICA9IHN1bShzdGF0ZV9hemltdXRoX2wyICE9ICJUcmVnIiwgbmEucm0gPSBUUlVFKSwKICAgIG1lYW5fcHQgICAgICA9IHJvdW5kKG1lYW4ocHNldWRvdGltZV92YWx1ZSksICAgICAgICAgICAzKSwKICAgIG1lZGlhbl9wdCAgICA9IHJvdW5kKG1lZGlhbihwc2V1ZG90aW1lX3ZhbHVlKSwgICAgICAgICAzKSwKICAgIHNkX3B0ICAgICAgICA9IHJvdW5kKHNkKHBzZXVkb3RpbWVfdmFsdWUpLCAgICAgICAgICAgICAzKSwKICAgIHEyNV9wdCAgICAgICA9IHJvdW5kKHF1YW50aWxlKHBzZXVkb3RpbWVfdmFsdWUsIDAuMjUpLCAzKSwKICAgIHE3NV9wdCAgICAgICA9IHJvdW5kKHF1YW50aWxlKHBzZXVkb3RpbWVfdmFsdWUsIDAuNzUpLCAzKSwKICAgICMgRWZmZWN0b3IgYXhpcyBwY3Q6IGNvbXB1dGVkIG9ubHkgb24gbm9uLVRyZWcgY2VsbHMKICAgIHBjdF9uYWl2ZSAgICA9IHJvdW5kKDEwMCAqIHN1bSgKICAgICAgICAgICAgICAgICAgICAgc3RhdGVfYXppbXV0aF9sMiAhPSAiVHJlZyIgJgogICAgICAgICAgICAgICAgICAgICBwc2V1ZG90aW1lX3ZhbHVlICA8ICBuYWl2ZV90Y21fY3V0LAogICAgICAgICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUpIC8gbl9lZmZlY3RvciwgMSksCiAgICBwY3RfdGNtICAgICAgPSByb3VuZCgxMDAgKiBzdW0oCiAgICAgICAgICAgICAgICAgICAgIHN0YXRlX2F6aW11dGhfbDIgIT0gIlRyZWciICYKICAgICAgICAgICAgICAgICAgICAgcHNldWRvdGltZV92YWx1ZSA+PSBuYWl2ZV90Y21fY3V0ICYKICAgICAgICAgICAgICAgICAgICAgcHNldWRvdGltZV92YWx1ZSAgPCAgdGNtX3RlbV9jdXQsCiAgICAgICAgICAgICAgICAgICAgIG5hLnJtID0gVFJVRSkgLyBuX2VmZmVjdG9yLCAxKSwKICAgIHBjdF90ZW0gICAgICA9IHJvdW5kKDEwMCAqIHN1bSgKICAgICAgICAgICAgICAgICAgICAgc3RhdGVfYXppbXV0aF9sMiAhPSAiVHJlZyIgJgogICAgICAgICAgICAgICAgICAgICBwc2V1ZG90aW1lX3ZhbHVlID49IHRjbV90ZW1fY3V0ICYKICAgICAgICAgICAgICAgICAgICAgcHNldWRvdGltZV92YWx1ZSAgPCAgdGVtX3RlbXJhX2N1dCwKICAgICAgICAgICAgICAgICAgICAgbmEucm0gPSBUUlVFKSAvIG5fZWZmZWN0b3IsIDEpLAogICAgcGN0X3RlbXJhICAgID0gcm91bmQoMTAwICogc3VtKAogICAgICAgICAgICAgICAgICAgICBzdGF0ZV9hemltdXRoX2wyICE9ICJUcmVnIiAmCiAgICAgICAgICAgICAgICAgICAgIHBzZXVkb3RpbWVfdmFsdWUgPj0gdGVtX3RlbXJhX2N1dCwKICAgICAgICAgICAgICAgICAgICAgbmEucm0gPSBUUlVFKSAvIG5fZWZmZWN0b3IsIDEpLAogICAgcGN0X3RyZWcgICAgID0gcm91bmQoMTAwICogbl90cmVnIC8gbl9jZWxscywgMSksCiAgICAuZ3JvdXBzICAgICAgPSAiZHJvcCIKICApICU+JQogIGFycmFuZ2UobWVkaWFuX3B0KQoKY2F0KCI9PT0gUHNldWRvdGltZSBTdGF0aXN0aWNzIHBlciBDZWxsIExpbmUgPT09XG4iKQpjYXQoIihwY3RfbmFpdmUvdGNtL3RlbS90ZW1yYSBjb21wdXRlZCBvbiBlZmZlY3Rvci1heGlzIGNlbGxzIG9ubHksXG4iKQpjYXQoIiBwY3RfdHJlZyA9IFRyZWctbGFiZWxsZWQgY2VsbHMgYXMgJSBvZiBhbGwgY2VsbHMpXG5cbiIpCnByaW50KHB0X3N0YXRzLCBuID0gSW5mKQoKY2F0KCJcbj09PSBSZWZlcmVuY2UgUHNldWRvdGltZSBwZXIgQXppbXV0aCBsMiBTdGF0ZSA9PT1cbiIpCnJlZl9zdGF0cyA8LSByZWZlcmVuY2VfaW50ZWdyYXRlZEBtZXRhLmRhdGEgJT4lCiAgZmlsdGVyKGlzLmZpbml0ZShtb25vY2xlM19wc2V1ZG90aW1lKSwgIWlzLm5hKHByZWRpY3RlZC5jZWxsdHlwZS5sMikpICU+JQogIGdyb3VwX2J5KHByZWRpY3RlZC5jZWxsdHlwZS5sMikgJT4lCiAgc3VtbWFyaXNlKAogICAgbl9jZWxscyAgID0gbigpLAogICAgbWVhbl9wdCAgID0gcm91bmQobWVhbihtb25vY2xlM19wc2V1ZG90aW1lKSwgICAzKSwKICAgIG1lZGlhbl9wdCA9IHJvdW5kKG1lZGlhbihtb25vY2xlM19wc2V1ZG90aW1lKSwgMyksCiAgICAuZ3JvdXBzICAgPSAiZHJvcCIKICApICU+JQogIGFycmFuZ2UobWVkaWFuX3B0KQpwcmludChyZWZfc3RhdHMpCmBgYAoKCiMgSW50ZWdyYXRlZCBTdW1tYXJ5CgojIyBGb3VyLVBhbmVsIFN1bW1hcnkgRmlndXJlCgpgYGB7ciBzdW1tYXJ5LWZpZ3VyZSwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTEwfQpwX3JlZiA8LSBEaW1QbG90KAogIHJlZmVyZW5jZV9pbnRlZ3JhdGVkLCBncm91cC5ieSA9ICJwcmVkaWN0ZWQuY2VsbHR5cGUubDIiLAogIHJlZHVjdGlvbiA9ICJ1bWFwIiwgbGFiZWwgPSBUUlVFLCByZXBlbCA9IFRSVUUsCiAgbGFiZWwuc2l6ZSA9IDMsIHB0LnNpemUgPSAwLjQKKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGF6aW11dGhfbDJfY29sb3JzLCBuYS52YWx1ZSA9ICJncmV5NDAiKSArCiAgZ2d0aXRsZSgiQS4gSGVhbHRoeSBSZWZlcmVuY2UgQ0Q0KyBUIENlbGxzXG4oQXppbXV0aCBsMikiKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgc2l6ZSA9IDExLCBmYWNlID0gImJvbGQiKSkgKwogIE5vTGVnZW5kKCkKCnBfcHRfcmVmIDwtIEZlYXR1cmVQbG90KAogIHJlZmVyZW5jZV9pbnRlZ3JhdGVkLCBmZWF0dXJlcyA9ICJtb25vY2xlM19wc2V1ZG90aW1lIiwKICByZWR1Y3Rpb24gPSAidW1hcCIsIGNvbHMgPSBjKCJsaWdodGJsdWUiLCAieWVsbG93IiwgInJlZCIpLAogIHB0LnNpemUgPSAwLjQKKSArCiAgZ2d0aXRsZSgiQi4gUmVmZXJlbmNlIFBzZXVkb3RpbWVcbihOYWl2ZSDihpIgVGVtcmEgKyBUcmVnIGJyYW5jaCkiKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgc2l6ZSA9IDExLCBmYWNlID0gImJvbGQiKSkKCnBfcHJvaiA8LSBnZ3Bsb3QoKSArCiAgZ2VvbV9wb2ludChkYXRhID0gcmVmX3VtYXAsCiAgICAgICAgICAgICBhZXMoeCA9IFVNQVBfMSwgeSA9IFVNQVBfMiksCiAgICAgICAgICAgICBjb2xvciA9ICJncmV5ODgiLCBzaXplID0gMC4zLCBhbHBoYSA9IDAuNikgKwogIGdlb21fcG9pbnQoZGF0YSA9IHF1ZXJ5X3VtYXAgJT4lIGZpbHRlcihpcy5maW5pdGUocHNldWRvdGltZSkpLAogICAgICAgICAgICAgYWVzKHggPSBVTUFQXzEsIHkgPSBVTUFQXzIsIGNvbG9yID0gcHNldWRvdGltZSksCiAgICAgICAgICAgICBzaXplID0gMS4wLCBhbHBoYSA9IDAuODUpICsKICBzY2FsZV9jb2xvcl92aXJpZGlzX2Mob3B0aW9uID0gInBsYXNtYSIsIG5hbWUgPSAiUHNldWRvdGltZSIpICsKICB0aGVtZV9jbGFzc2ljKGJhc2Vfc2l6ZSA9IDEwKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgc2l6ZSA9IDExLCBmYWNlID0gImJvbGQiKSkgKwogIGdndGl0bGUoIkMuIFPDqXphcnkgQ2VsbHMgUHJvamVjdGVkXG4oVHJhbnNmZXJyZWQgcHNldWRvdGltZSkiKQoKdG9wX3N0YXRlcyAgICA8LSBzdGF0ZV9zdW1tYXJ5ICU+JSBoZWFkKDgpICU+JSBwdWxsKHN0YXRlX2F6aW11dGhfbDIpCnN0YXRlX3Bsb3RfZGYgPC0gc3RhdGVfcGVyX2xpbmUgJT4lIGZpbHRlcihzdGF0ZV9hemltdXRoX2wyICVpbiUgdG9wX3N0YXRlcykKCnBfcXVhbnQgPC0gZ2dwbG90KHN0YXRlX3Bsb3RfZGYsCiAgICAgICAgICAgICAgICAgICBhZXMoeCA9IHJlb3JkZXIoY2VsbF9saW5lLCAtcGN0KSwgeSA9IHBjdCwKICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gc3RhdGVfYXppbXV0aF9sMikpICsKICBnZW9tX2NvbChwb3NpdGlvbiA9ICJzdGFjayIsIGNvbG9yID0gIndoaXRlIiwgbGluZXdpZHRoID0gMC4zKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYXppbXV0aF9sMl9jb2xvcnMsIG5hLnZhbHVlID0gImdyZXk0MCIsCiAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICJBemltdXRoIGwyIikgKwogIHRoZW1lX2NsYXNzaWMoYmFzZV9zaXplID0gMTApICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQwLCBoanVzdCA9IDEsIHNpemUgPSA5KSwKICAgICAgICBwbG90LnRpdGxlICA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgc2l6ZSA9IDExLCBmYWNlID0gImJvbGQiKSwKICAgICAgICBsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNCwgImNtIiksCiAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgpKSArCiAgbGFicyh0aXRsZSA9ICJELiBTdGF0ZSBQcm9wb3J0aW9ucyBwZXIgQ2VsbCBMaW5lXG4oQXppbXV0aCBsMiBsYWJlbCB0cmFuc2ZlcikiLAogICAgICAgeCA9ICIiLCB5ID0gIiUgQ2VsbHMiKQoKKHBfcmVmIHwgcF9wdF9yZWYpIC8gKHBfcHJvaiB8IHBfcXVhbnQpICsKICBwbG90X2Fubm90YXRpb24oCiAgICB0aXRsZSA9ICJTw6l6YXJ5IENlbGwgRGlmZmVyZW50aWF0aW9uIFN0YXRlIE1hcHBpbmcgb250byBIZWFsdGh5IENENCsgVCBDZWxsIFRyYWplY3RvcnkiLAogICAgdGhlbWUgPSB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBzaXplID0gMTMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFjZSA9ICJib2xkIikpCiAgKQpgYGAKCiMjIFBzZXVkb3RpbWUgVmlvbGluIGJ5IFRyYW5zZmVycmVkIFN0YXRlCgpgYGB7ciBwc2V1ZG90aW1lLXZpb2xpbiwgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9NX0KbWFsX3N0YXRlX3B0IDwtIG1hcHBlZF9NYWxpZ25hbnRDRDRUQG1ldGEuZGF0YSAlPiUKICBmaWx0ZXIoaXMuZmluaXRlKHBzZXVkb3RpbWVfdmFsdWUpLCAhaXMubmEoc3RhdGVfYXppbXV0aF9sMikpICU+JQogIG11dGF0ZShzdGF0ZSA9IHN0YXRlX2F6aW11dGhfbDIpCgpzdGF0ZV9vcmRlcl9tYWwgPC0gbWFsX3N0YXRlX3B0ICU+JQogIGdyb3VwX2J5KHN0YXRlKSAlPiUKICBzdW1tYXJpc2UobWVkID0gbWVkaWFuKHBzZXVkb3RpbWVfdmFsdWUpLCAuZ3JvdXBzID0gImRyb3AiKSAlPiUKICBhcnJhbmdlKG1lZCkgJT4lCiAgcHVsbChzdGF0ZSkKCm1hbF9zdGF0ZV9wdCRzdGF0ZSA8LSBmYWN0b3IobWFsX3N0YXRlX3B0JHN0YXRlLCBsZXZlbHMgPSBzdGF0ZV9vcmRlcl9tYWwpCgpnZ3Bsb3QobWFsX3N0YXRlX3B0LCBhZXMoeCA9IHN0YXRlLCB5ID0gcHNldWRvdGltZV92YWx1ZSwgZmlsbCA9IHN0YXRlKSkgKwogIGdlb21fdmlvbGluKHNjYWxlID0gIndpZHRoIiwgYWxwaGEgPSAwLjgsIHRyaW0gPSBUUlVFKSArCiAgZ2VvbV9ib3hwbG90KHdpZHRoID0gMC4xLCBmaWxsID0gIndoaXRlIiwgb3V0bGllci5zaXplID0gMC4zKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gYyhuYWl2ZV9tZWQsIHRjbV9tZWQsIHRyZWdfbWVkLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRlbV9tZWQsICB0ZW1yYV9tZWQpLAogICAgICAgICAgICAgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAiZ3JleTUwIiwgbGluZXdpZHRoID0gMC41KSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYXppbXV0aF9sMl9jb2xvcnMsIG5hLnZhbHVlID0gImdyZXk3MCIpICsKICB0aGVtZV9jbGFzc2ljKGJhc2Vfc2l6ZSA9IDExKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggICA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQwLCBoanVzdCA9IDEsIHNpemUgPSA5KSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICAgICAgcGxvdC50aXRsZSAgICA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgZmFjZSA9ICJib2xkIikpICsKICBsYWJzKHRpdGxlICAgID0gIlRyYW5zZmVycmVkIFBzZXVkb3RpbWUgcGVyIEF6aW11dGggbDIgU3RhdGUgKFPDqXphcnkgQ2VsbHMpIiwKICAgICAgIHN1YnRpdGxlID0gIkRhc2hlZCBsaW5lcyA9IGhlYWx0aHkgcmVmZXJlbmNlIHN0YXRlIG1lZGlhbnMiLAogICAgICAgeCA9ICJUcmFuc2ZlcnJlZCBTdGF0ZSAoY2VsbCBvZiBvcmlnaW4pIiwKICAgICAgIHkgPSAiVHJhbnNmZXJyZWQgUHNldWRvdGltZSAoZGlmZmVyZW50aWF0aW9uIHBvc2l0aW9uKSIpCmBgYAoKIyMgS2V5IENvbXBhcmlzb246IExhYmVsIHZzIFBzZXVkb3RpbWUgQmluCgpgYGB7ciBsYWJlbC12cy1iaW4sIGZpZy53aWR0aD0xMSwgZmlnLmhlaWdodD04fQojIFRoaXMgaXMgdGhlIGNvcmUgcmVzdWx0IHBsb3QuCiMgRm9yIGVhY2ggdHJhbnNmZXJyZWQgQXppbXV0aCBsMiBsYWJlbCwgc2hvd3Mgd2hhdCBmcmFjdGlvbiBvZiBjZWxscwojIGZhbGwgaW50byBlYWNoIHBzZXVkb3RpbWUgYmluLgojIEtleSBtZXNzYWdlOiBUQ00tbGFiZWxsZWQgY2VsbHMgc3BhbiBUQ00sIFRFTSwgYW5kIFRlbXJhIGJpbnMKIyAgIOKGkiBpZGVudGl0eSA9IFRDTSAoY2VsbCBvZiBvcmlnaW4pCiMgICDihpIgcG9zaXRpb24gPSBmdXJ0aGVyIGFsb25nIGVmZmVjdG9yIGF4aXMgKHBhcnRpYWwgc2tld2luZykKCiMgRklYOiBidWlsZCBjcm9zc19kZiB3aXRoIGV4cGxpY2l0IGNvbHVtbiBzZWxlY3Rpb24gZnJvbSB0YWJsZSBvdXRwdXQKIyByYXRoZXIgdGhhbiByZW5hbWUoKSB3aGljaCBjYW4gZmFpbCBpZiBjb2x1bW4gbmFtZXMgZGlmZmVyCmNyb3NzX2RmIDwtIGFzLmRhdGEuZnJhbWUoY3Jvc3NfdGFiKQojIHRhYmxlKCkgYWx3YXlzIHJldHVybnM6IFZhcjEg4oaSIExhYmVsLCBWYXIyIOKGkiBQVF9CaW4gKGZyb20gb3VyIG5hbWVkIGRpbXMpCiMgYnV0IGlmIHRhYmxlIHdhcyBjb25zdHJ1Y3RlZCB3aXRoIG5hbWVkIGRpbXMgIkxhYmVsIiBhbmQgIlBUX0JpbiIKIyB0aGVuIGNvbG5hbWVzIGFyZSBhbHJlYWR5IExhYmVsLCBQVF9CaW4sIEZyZXEKY29sbmFtZXMoY3Jvc3NfZGYpIDwtIGMoIkxhYmVsIiwgIlBUX0JpbiIsICJuIikKCmNyb3NzX2RmIDwtIGNyb3NzX2RmICU+JQogIGdyb3VwX2J5KExhYmVsKSAlPiUKICBtdXRhdGUocGN0ID0gcm91bmQoMTAwICogbiAvIHN1bShuKSwgMSkpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBmaWx0ZXIobiA+IDApICU+JQogIG11dGF0ZShQVF9CaW4gPSBmYWN0b3IoUFRfQmluLAogICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYygiTmFpdmUtbGlrZSIsICJUQ00tbGlrZSIsICJUcmVnLWxpa2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVEVNLWxpa2UiLCAiVGVtcmEtbGlrZSIpKSkKCmdncGxvdChjcm9zc19kZiwgYWVzKHggPSBQVF9CaW4sIHkgPSBwY3QsIGZpbGwgPSBQVF9CaW4pKSArCiAgZ2VvbV9jb2woY29sb3IgPSAid2hpdGUiLCBsaW5ld2lkdGggPSAwLjMpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gaWZlbHNlKHBjdCA+PSAzLCBwYXN0ZTAocGN0LCAiJSIpLCAiIikpLAogICAgICAgICAgICB2anVzdCA9IC0wLjMsIHNpemUgPSAzLCBmb250ZmFjZSA9ICJib2xkIikgKwogIGZhY2V0X3dyYXAofiBMYWJlbCwgc2NhbGVzID0gImZyZWVfeSIsIG5jb2wgPSAzKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYmluX2NvbG9ycywgZHJvcCA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiUHNldWRvdGltZSBCaW4iKSArCiAgdGhlbWVfY2xhc3NpYyhiYXNlX3NpemUgPSAxMCkgKwogIHRoZW1lKGF4aXMudGV4dC54ICAgICA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQwLCBoanVzdCA9IDEsIHNpemUgPSA4KSwKICAgICAgICBzdHJpcC50ZXh0ICAgICAgID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIsIHNpemUgPSA5KSwKICAgICAgICBzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAiI0U4RjRGRCIpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiAgPSAibm9uZSIsCiAgICAgICAgcGxvdC50aXRsZSAgICAgICA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgZmFjZSA9ICJib2xkIikpICsKICBsYWJzKAogICAgdGl0bGUgICAgPSAiUHNldWRvdGltZSBCaW4gRGlzdHJpYnV0aW9uIHdpdGhpbiBFYWNoIFRyYW5zZmVycmVkIEF6aW11dGggbDIgTGFiZWwiLAogICAgc3VidGl0bGUgPSBwYXN0ZTAoCiAgICAgICJDb3JlIGZpbmRpbmc6IFRDTS1sYWJlbGxlZCBTw6l6YXJ5IGNlbGxzIHNwYW4gbXVsdGlwbGUgcHNldWRvdGltZSBiaW5zXG4iLAogICAgICAi4oaSIFRDTSBpZGVudGl0eSAobGFiZWwpIGJ1dCBwYXJ0aWFsIGVmZmVjdG9yIHNrZXdpbmcgKHBzZXVkb3RpbWUgcG9zaXRpb24pXG4iLAogICAgICAiTm90ZTogVHJlZyBwYW5lbCBzaG93cyAxMDAlIFRyZWctbGlrZSBieSBkZXNpZ24gKGxhYmVsLWJhc2VkIGJpbiBhc3NpZ25tZW50KSIpLAogICAgeCA9ICJQc2V1ZG90aW1lIEJpbiIsIHkgPSAiJSBDZWxscyB3aXRoaW4gbGFiZWwiCiAgKQpgYGAKCgojIFNhdmUgT3V0cHV0cwoKYGBge3Igc2F2ZS1vdXRwdXRzfQpTeXMuc2V0bG9jYWxlKCJMQ19DT0xMQVRFIiwgIkMiKQpvcHRpb25zKGV4cHJlc3Npb25zID0gMTAwMDApCgpvdXRfZGlyIDwtICJyZXN1bHRzL01hbGlnbmFudENENFRfTW9ub2NsZTNfcHJvamVjdGlvbiIKaWYgKCFkaXIuZXhpc3RzKG91dF9kaXIpKSBkaXIuY3JlYXRlKG91dF9kaXIsIHJlY3Vyc2l2ZSA9IFRSVUUpCgpsaWJyYXJ5KFNldXJhdE9iamVjdCkKClNhdmVTZXVyYXRSZHMobWFwcGVkX01hbGlnbmFudENENFQsCiAgICAgICAgICAgICAgZmlsZSA9IGZpbGUucGF0aChvdXRfZGlyLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk1hbGlnbmFudENENFRfbWFwcGVkX21vbm9jbGUzX3BzZXVkb3RpbWVfbm9Qcm9saWZfcmVmLlJkcyIpKQoKCgojIFN1bW1hcnkgdGFibGVzCndyaXRlLmNzdihzdGF0ZV9zdW1tYXJ5LAogICAgICAgICAgZmlsZS5wYXRoKG91dF9kaXIsICJzdGF0ZV9kaXN0cmlidXRpb25fb3ZlcmFsbC5jc3YiKSwKICAgICAgICAgIHJvdy5uYW1lcyA9IEZBTFNFKQp3cml0ZS5jc3Yoc3RhdGVfcGVyX2xpbmUsCiAgICAgICAgICBmaWxlLnBhdGgob3V0X2RpciwgInN0YXRlX2Rpc3RyaWJ1dGlvbl9wZXJfY2VsbGxpbmUuY3N2IiksCiAgICAgICAgICByb3cubmFtZXMgPSBGQUxTRSkKd3JpdGUuY3N2KHB0X2Jpbl9wZXJfbGluZSwKICAgICAgICAgIGZpbGUucGF0aChvdXRfZGlyLCAicHNldWRvdGltZV9iaW5zX3Blcl9jZWxsbGluZS5jc3YiKSwKICAgICAgICAgIHJvdy5uYW1lcyA9IEZBTFNFKQp3cml0ZS5jc3YocHRfc3RhdHMsCiAgICAgICAgICBmaWxlLnBhdGgob3V0X2RpciwgInBzZXVkb3RpbWVfc3RhdHNfcGVyX2NlbGxsaW5lLmNzdiIpLAogICAgICAgICAgcm93Lm5hbWVzID0gRkFMU0UpCndyaXRlLmNzdihhcy5kYXRhLmZyYW1lKGNyb3NzX3RhYiksCiAgICAgICAgICBmaWxlLnBhdGgob3V0X2RpciwgImxhYmVsX3ZzX3BzZXVkb3RpbWVfYmluX2Nyb3NzdGFibGUuY3N2IiksCiAgICAgICAgICByb3cubmFtZXMgPSBGQUxTRSkKd3JpdGUuY3N2KG1hcHBlZF9NYWxpZ25hbnRDRDRUQG1ldGEuZGF0YSwKICAgICAgICAgIGZpbGUucGF0aChvdXRfZGlyLCAiTWFsaWduYW50Q0Q0VF9mdWxsX21ldGFkYXRhX3dpdGhfcHJvamVjdGlvbi5jc3YiKSwKICAgICAgICAgIHJvdy5uYW1lcyA9IFRSVUUpCgojIEJpbiBib3VuZGFyaWVzIOKAlCBzYXZlZCBmb3IgbWV0aG9kcyBzZWN0aW9uIGFuZCByZXByb2R1Y2liaWxpdHkKYmluX2JvdW5kYXJpZXMgPC0gZGF0YS5mcmFtZSgKICBib3VuZGFyeSAgICAgICA9IGMoIk5haXZlX1RDTSIsICJUQ01fVEVNIiwgIlRFTV9UZW1yYSIsICJUQ01fVHJlZ19icmFuY2giKSwKICBwc2V1ZG90aW1lX2N1dCA9IGMobmFpdmVfdGNtX2N1dCwgdGNtX3RlbV9jdXQsIHRlbV90ZW1yYV9jdXQsIHRjbV90cmVnX2N1dCksCiAgbmFpdmVfbWVkICAgICAgPSBuYWl2ZV9tZWQsCiAgdGNtX21lZCAgICAgICAgPSB0Y21fbWVkLAogIHRyZWdfbWVkICAgICAgID0gdHJlZ19tZWQsCiAgdGVtX21lZCAgICAgICAgPSB0ZW1fbWVkLAogIHRlbXJhX21lZCAgICAgID0gdGVtcmFfbWVkCikKd3JpdGUuY3N2KGJpbl9ib3VuZGFyaWVzLAogICAgICAgICAgZmlsZS5wYXRoKG91dF9kaXIsICJwc2V1ZG90aW1lX2Jpbl9ib3VuZGFyaWVzLmNzdiIpLAogICAgICAgICAgcm93Lm5hbWVzID0gRkFMU0UpCgpjYXQoIuKchSBBbGwgb3V0cHV0cyBzYXZlZCB0bzoiLCBvdXRfZGlyLCAiXG5cbiIpCmNhdCgiRmlsZXM6XG4iKQpwcmludChsaXN0LmZpbGVzKG91dF9kaXIpKQpgYGAKCgojIFNlc3Npb24gSW5mbwoKYGBge3Igc2Vzc2lvbi1pbmZvfQpzZXNzaW9uSW5mbygpCmBgYAoKCg==