load libraries
# Data Processing
library(dplyr)
library(Seurat)
library(tibble)
library(tidyr)
library(stringr)
# Visualization
library(ggplot2)
library(ComplexHeatmap)
library(patchwork)
library(SCpubr)
# Regulatory Network Inference
library(decoupleR)
library(dorothea)
data(dorothea_hs, package = "dorothea")
library(tictoc)
Load Seurat Object
library(Seurat)
library(reticulate)
library(ggplot2)
# 1. Load Object
seurat_obj <- readRDS("temp_seurat_obj.rds")
Verification of TF
Availability
library(Seurat)
library(dplyr)
cat("=== CHECKING TF AVAILABILITY IN DOROTHEA ASSAY ===\n\n")
=== CHECKING TF AVAILABILITY IN DOROTHEA ASSAY ===
# 1. Identify the TF assay
available_assays <- Assays(seurat_obj)
tf_assay_name <- grep("dorothea|viper|TF", available_assays, value=TRUE)[1]
if(is.na(tf_assay_name)) {
stop("❌ No TF assay found! Please run DoRothEA first.")
}
cat("✓ Found TF assay:", tf_assay_name, "\n")
✓ Found TF assay: dorothea
# 2. Get all available TFs
all_tfs <- rownames(seurat_obj[[tf_assay_name]])
cat("✓ Total TFs inferred:", length(all_tfs), "\n\n")
✓ Total TFs inferred: 271
# 3. Define our Desired Panel (Literature-Based)
desired_tfs <- list(
"Stemness" = c("TCF7", "LEF1", "BACH2", "FOXO1", "MYC"),
"Malignancy" = c("TOX", "STAT5A", "STAT3", "STAT5B", "GATA3", "IKZF2"),
"Exhaustion" = c("EOMES", "TBX21", "PRDM1", "NFATC1", "BATF"),
"Treg_Like" = c("FOXP3", "IKZF2", "SATB1")
)
# 4. Check Presence
available_panel <- list()
missing_tfs <- c()
cat("--- PANEL VERIFICATION ---\n")
--- PANEL VERIFICATION ---
for(category in names(desired_tfs)) {
tfs <- desired_tfs[[category]]
present <- intersect(tfs, all_tfs)
missing <- setdiff(tfs, all_tfs)
available_panel[[category]] <- present
missing_tfs <- c(missing_tfs, missing)
cat(sprintf("%s: %d/%d found\n", category, length(present), length(tfs)))
if(length(present) > 0) cat(" ✅ Present:", paste(present, collapse=", "), "\n")
if(length(missing) > 0) cat(" ❌ Missing:", paste(missing, collapse=", "), "\n")
cat("\n")
}
Stemness: 5/5 found
✅ Present: TCF7, LEF1, BACH2, FOXO1, MYC
Malignancy: 4/6 found
✅ Present: STAT5A, STAT3, STAT5B, GATA3
❌ Missing: TOX, IKZF2
Exhaustion: 5/5 found
✅ Present: EOMES, TBX21, PRDM1, NFATC1, BATF
Treg_Like: 0/3 found
❌ Missing: FOXP3, IKZF2, SATB1
# 5. Summary
final_tf_list <- unlist(available_panel)
cat("=== FINAL TF LIST FOR TRAJECTORY ===\n")
=== FINAL TF LIST FOR TRAJECTORY ===
cat("Total TFs to plot:", length(final_tf_list), "\n")
Total TFs to plot: 14
print(final_tf_list)
Stemness1 Stemness2 Stemness3 Stemness4 Stemness5 Malignancy1 Malignancy2 Malignancy3 Malignancy4 Exhaustion1 Exhaustion2 Exhaustion3
"TCF7" "LEF1" "BACH2" "FOXO1" "MYC" "STAT5A" "STAT3" "STAT5B" "GATA3" "EOMES" "TBX21" "PRDM1"
Exhaustion4 Exhaustion5
"NFATC1" "BATF"
if("TOX" %in% final_tf_list) cat("\n✓ CRITICAL: TOX is available!\n")
if("TCF7" %in% final_tf_list) cat("✓ CRITICAL: TCF7 is available!\n")
✓ CRITICAL: TCF7 is available!
if("STAT5A" %in% final_tf_list) cat("✓ CRITICAL: STAT5A is available!\n")
✓ CRITICAL: STAT5A is available!
# 6. Save valid list for next step
saveRDS(final_tf_list, "valid_tf_panel.rds")
PAGA Gene Dynamics for
Malignant Sézary Cells (TF)
library(reticulate)
library(Seurat)
library(dplyr)
# Setup Python
sc <- import("scanpy")
ad <- import("anndata")
pl <- import("matplotlib.pyplot")
cat("=== TF REGULATORY DYNAMICS (5 TRAJECTORIES) ===\n\n")
=== TF REGULATORY DYNAMICS (5 TRAJECTORIES) ===
# 1. IDENTIFY TF ASSAY & CHECK AVAILABILITY
available_assays <- Assays(seurat_obj)
tf_assay_name <- grep("dorothea|viper|TF", available_assays, value=TRUE)[1]
if(is.na(tf_assay_name)) {
stop("❌ No TF assay found! Please run DoRothEA first.")
}
cat("✓ Using TF assay:", tf_assay_name, "\n")
✓ Using TF assay: dorothea
# Get all available TFs
all_tfs_in_object <- rownames(seurat_obj[[tf_assay_name]])
cat("✓ Total TFs inferred:", length(all_tfs_in_object), "\n\n")
✓ Total TFs inferred: 271
# 2. DEFINE & VALIDATE TF PANEL
desired_tfs <- list(
"Stemness" = c("TCF7", "LEF1", "BACH2", "FOXO1", "MYC"),
"Malignancy" = c("TOX", "STAT5A", "STAT3", "STAT5B", "GATA3", "IKZF2"),
"Exhaustion" = c("EOMES", "TBX21", "PRDM1", "NFATC1", "BATF"),
"Treg_Like" = c("FOXP3", "SATB1")
)
flat_valid_tfs <- c()
cat("--- TF PANEL VALIDATION ---\n")
--- TF PANEL VALIDATION ---
for(category in names(desired_tfs)) {
tfs <- desired_tfs[[category]]
present <- intersect(tfs, all_tfs_in_object)
missing <- setdiff(tfs, all_tfs_in_object)
if(length(present) > 0) {
flat_valid_tfs <- c(flat_valid_tfs, present)
cat(sprintf("✅ %s: %s\n", category, paste(present, collapse=", ")))
}
if(length(missing) > 0) {
cat(sprintf("❌ %s (Missing): %s\n", category, paste(missing, collapse=", ")))
}
}
✅ Stemness: TCF7, LEF1, BACH2, FOXO1, MYC
✅ Malignancy: STAT5A, STAT3, STAT5B, GATA3
❌ Malignancy (Missing): TOX, IKZF2
✅ Exhaustion: EOMES, TBX21, PRDM1, NFATC1, BATF
❌ Treg_Like (Missing): FOXP3, SATB1
if(length(flat_valid_tfs) == 0) stop("No TFs found!")
cat("\n✓ Using", length(flat_valid_tfs), "TFs\n\n")
✓ Using 14 TFs
# 3. PREPARE DATA (Malignant Only) - CLEAN RECREATION
malignant_clusters <- setdiff(unique(seurat_obj$seurat_clusters), c(3, 10))
seurat_malignant <- subset(seurat_obj, subset = seurat_clusters %in% malignant_clusters)
DefaultAssay(seurat_malignant) <- tf_assay_name
tf_matrix <- GetAssayData(seurat_malignant, assay = tf_assay_name, layer = "data")
tf_matrix <- tf_matrix[flat_valid_tfs, ]
# Convert to dense matrix and transpose
expr_matrix <- t(as.matrix(tf_matrix))
# Create clean obs dataframe
obs_df <- data.frame(
seurat_clusters = as.character(seurat_malignant$seurat_clusters),
row.names = colnames(seurat_malignant),
stringsAsFactors = FALSE
)
# Create AnnData using Python directly to avoid reticulate issues
cat("Creating AnnData object...\n")
Creating AnnData object...
# Export data to Python environment
py$expr_matrix <- expr_matrix
py$obs_df <- obs_df
py$var_names <- flat_valid_tfs
py_run_string("
import scanpy as sc
import anndata as ad
import pandas as pd
import numpy as np
# Create AnnData cleanly in Python
adata_tf = ad.AnnData(
X=r.expr_matrix,
obs=r.obs_df
)
# Set variable names
adata_tf.var_names = r.var_names
print(f'✓ AnnData created: {adata_tf.n_obs} cells x {adata_tf.n_vars} TFs')
")
Error in eval(parse(text = as_r_value(code)), envir = envir) :
RuntimeError: object 'var_names' not found
Run ]8;;rstudio:run:reticulate::py_last_error()`reticulate::py_last_error()`]8;; for details.
PAGA Gene Dynamics for
Malignant Sézary Cells (SCT)
LS0tCnRpdGxlOiAiUEFHQSB1c2luZyBURiBBY3Rpdml0eSIKYXV0aG9yOiAiTmFzaXIgTWFobW9vZCBBYmJhc2kiCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OgogICAgICBjb2xsYXBzZWQ6IHRydWUKICAgIHRoZW1lOiBqb3VybmFsCi0tLQoKCiMgbG9hZCBsaWJyYXJpZXMKYGBge3Igc2V0dXAsIGluY2x1ZGU9VFJVRX0KIyBEYXRhIFByb2Nlc3NpbmcKbGlicmFyeShkcGx5cikKbGlicmFyeShTZXVyYXQpCmxpYnJhcnkodGliYmxlKQpsaWJyYXJ5KHRpZHlyKQpsaWJyYXJ5KHN0cmluZ3IpCgojIFZpc3VhbGl6YXRpb24KbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KENvbXBsZXhIZWF0bWFwKQpsaWJyYXJ5KHBhdGNod29yaykKbGlicmFyeShTQ3B1YnIpCgojIFJlZ3VsYXRvcnkgTmV0d29yayBJbmZlcmVuY2UKbGlicmFyeShkZWNvdXBsZVIpCmxpYnJhcnkoZG9yb3RoZWEpCmRhdGEoZG9yb3RoZWFfaHMsIHBhY2thZ2UgPSAiZG9yb3RoZWEiKQpsaWJyYXJ5KHRpY3RvYykKCgpgYGAKCiMgTG9hZCBTZXVyYXQgT2JqZWN0IApgYGB7cn0KbGlicmFyeShTZXVyYXQpCmxpYnJhcnkocmV0aWN1bGF0ZSkKbGlicmFyeShnZ3Bsb3QyKQoKIyAxLiBMb2FkIE9iamVjdApzZXVyYXRfb2JqIDwtIHJlYWRSRFMoInRlbXBfc2V1cmF0X29iai5yZHMiKQoKCgpgYGAKCiMgU1VQUExFTUVOVEFSWSBGSUdVUkU6IFBBR0EgVHJhamVjdG9yeSBBbmFseXNpcyAodmlhIFJldGljdWxhdGUvU2NhbnB5KS1URiBiYXNlZApgYGB7ciwgZmlnLmhlaWdodD0xNiwgZmlnLndpZHRoPTE2fQpsaWJyYXJ5KFNldXJhdCkKbGlicmFyeShyZXRpY3VsYXRlKQpsaWJyYXJ5KGdncGxvdDIpCgojIDEuIEZPUkNFIFVTRSBvZiB0aGUgc3BlY2lmaWMgcHl0aG9uIGJpbmFyeSB3aGVyZSBzY2FucHkgaXMgaW5zdGFsbGVkCiMgVGhpcyBvdmVycmlkZXMgdGhlIGRlZmF1bHQgImF1dG8iIGRpc2NvdmVyeSB3aGljaCBpcyBmYWlsaW5nIHlvdQpTeXMuc2V0ZW52KFJFVElDVUxBVEVfUFlUSE9OID0gIi9ob21lL2Jpb2luZm8vLnZpcnR1YWxlbnZzL3ItcmV0aWN1bGF0ZS9iaW4vcHl0aG9uIikKdXNlX3B5dGhvbigiL2hvbWUvYmlvaW5mby8udmlydHVhbGVudnMvci1yZXRpY3VsYXRlL2Jpbi9weXRob24iLCByZXF1aXJlZCA9IFRSVUUpCgojIDIuIFZlcmlmeSBpdCdzIHdvcmtpbmcgKE9wdGlvbmFsIGJ1dCBnb29kIGZvciBkZWJ1Z2dpbmcpCmNhdCgiVXNpbmcgUHl0aG9uOiIsIHB5X2NvbmZpZygpJHB5dGhvbiwgIlxuIikKCiMgMy4gSW1wb3J0IExpYnJhcmllcwpzYyA8LSBpbXBvcnQoInNjYW5weSIpCmFkIDwtIGltcG9ydCgiYW5uZGF0YSIpCgpjYXQoIuKckyBQeXRob24gbGlicmFyaWVzIGltcG9ydGVkIHN1Y2Nlc3NmdWxseSFcbiIpCgojIDQuIFByZXBhcmUgRGF0YSBmb3IgUEFHQQpjYXQoIkNvbnZlcnRpbmcgU2V1cmF0IG9iamVjdCB0byBBbm5EYXRhLi4uXG4iKQp0Zl9tYXRyaXggPC0gdChHZXRBc3NheURhdGEoc2V1cmF0X29iaiwgYXNzYXkgPSAiZG9yb3RoZWEiLCBsYXllciA9ICJkYXRhIikpCm9icyA8LSBkYXRhLmZyYW1lKHJvdy5uYW1lcyA9IGNvbG5hbWVzKHNldXJhdF9vYmopLCAKICAgICAgICAgICAgICAgICAgbG91dmFpbiA9IGFzLmNoYXJhY3RlcihzZXVyYXRfb2JqJHNldXJhdF9jbHVzdGVycykpCgphZGF0YSA8LSBhZCRBbm5EYXRhKFggPSB0Zl9tYXRyaXgsIG9icyA9IG9icykKCiMgNS4gUnVuIFNjYW5weSBXb3JrZmxvdwpjYXQoIlJ1bm5pbmcgUEFHQS4uLlxuIikKc2MkcHAkbmVpZ2hib3JzKGFkYXRhLCB1c2VfcmVwID0gIlgiLCBuX25laWdoYm9ycyA9IDE1TCkKc2MkdGwkcGFnYShhZGF0YSwgZ3JvdXBzID0gImxvdXZhaW4iKQoKIyA2LiBQbG90IGFuZCBTYXZlCm91dHB1dF9maWxlIDwtICJPdXRwdXRfRmlndXJlcy9TdXBwbGVtZW50YXJ5X1BBR0FfVEZfQWN0aXZpdHkucG5nIgppZighZGlyLmV4aXN0cygiT3V0cHV0X0ZpZ3VyZXMiKSkgZGlyLmNyZWF0ZSgiT3V0cHV0X0ZpZ3VyZXMiKQoKIyBDT1JSRUNUIE9SREVSOiBEcmF3IFBBR0EgZmlyc3QgdG8gY29tcHV0ZSBwb3NpdGlvbnMKc2MkcGwkcGFnYShhZGF0YSwgdGhyZXNob2xkID0gMC4xNSwgc2hvdyA9IEZBTFNFKQoKIyBOb3cgVU1BUCBjYW4gdXNlIHRob3NlIHBvc2l0aW9ucwpzYyR0bCR1bWFwKGFkYXRhLCBpbml0X3BvcyA9ICJwYWdhIikKCiMgUGxvdCBQQUdBIHdpdGggY29ubmVjdGl2aXR5IHRocmVzaG9sZApzYyRwbCRwYWdhKGFkYXRhLCB0aHJlc2hvbGQgPSAwLjE1LCBzaG93ID0gRkFMU0UsIHNhdmUgPSAiX1RGX0FjdGl2aXR5LnBuZyIpCgojIFNjYW5weSBzYXZlcyB0byAiZmlndXJlcy8iIGJ5IGRlZmF1bHQKaWYoZmlsZS5leGlzdHMoImZpZ3VyZXMvcGFnYV9URl9BY3Rpdml0eS5wbmciKSl7CiAgZmlsZS5yZW5hbWUoImZpZ3VyZXMvcGFnYV9URl9BY3Rpdml0eS5wbmciLCBvdXRwdXRfZmlsZSkKICBjYXQoc3ByaW50Zigi4pyTIFBBR0EgcGxvdCBzYXZlZCB0bzogJXNcbiIsIG91dHB1dF9maWxlKSkKfSBlbHNlIHsKICBjYXQoIk5vdGU6IENoZWNrICcuL2ZpZ3VyZXMvJyBkaXJlY3RvcnkgZm9yIG91dHB1dFxuIikKfQoKIyBEaXNwbGF5IGluIG5vdGVib29rCmlmKGZpbGUuZXhpc3RzKG91dHB1dF9maWxlKSl7CiAga25pdHI6OmluY2x1ZGVfZ3JhcGhpY3Mob3V0cHV0X2ZpbGUpCn0KCmBgYAoKIyBTVVBQTEVNRU5UQVJZIEZJR1VSRTogUEFHQSBvbiBHZW5lIEV4cHJlc3Npb24gKFNDVCBBc3NheSkKYGBge3IsIGZpZy5oZWlnaHQ9MTYsIGZpZy53aWR0aD0xNn0KbGlicmFyeShTZXVyYXQpCmxpYnJhcnkocmV0aWN1bGF0ZSkKCiMgVXNlIHNhbWUgcHl0aG9uIGVudmlyb25tZW50ClN5cy5zZXRlbnYoUkVUSUNVTEFURV9QWVRIT04gPSAiL2hvbWUvYmlvaW5mby8udmlydHVhbGVudnMvci1yZXRpY3VsYXRlL2Jpbi9weXRob24iKQp1c2VfcHl0aG9uKCIvaG9tZS9iaW9pbmZvLy52aXJ0dWFsZW52cy9yLXJldGljdWxhdGUvYmluL3B5dGhvbiIsIHJlcXVpcmVkID0gVFJVRSkKCnNjIDwtIGltcG9ydCgic2NhbnB5IikKYWQgPC0gaW1wb3J0KCJhbm5kYXRhIikKCmNhdCgi4pyTIFB5dGhvbiBsaWJyYXJpZXMgaW1wb3J0ZWRcbiIpCgojIDEuIEV4dHJhY3QgU0NULW5vcm1hbGl6ZWQgZ2VuZSBleHByZXNzaW9uCkRlZmF1bHRBc3NheShzZXVyYXRfb2JqKSA8LSAiU0NUIgoKIyBVc2UgaGlnaGx5IHZhcmlhYmxlIGZlYXR1cmVzIGZyb20gU0NUCmh2Z3MgPC0gVmFyaWFibGVGZWF0dXJlcyhzZXVyYXRfb2JqKQoKIyBHZXQgbm9ybWFsaXplZCBkYXRhIChsb2cxcCBvZiBjb3JyZWN0ZWQgY291bnRzKQpnZW5lX21hdHJpeCA8LSB0KEdldEFzc2F5RGF0YShzZXVyYXRfb2JqLCBhc3NheSA9ICJTQ1QiLCBsYXllciA9ICJkYXRhIilbaHZncywgXSkKCmNhdChzcHJpbnRmKCJVc2luZyAlZCBoaWdobHkgdmFyaWFibGUgZ2VuZXMgZnJvbSBTQ1QgYXNzYXlcbiIsIGxlbmd0aChodmdzKSkpCgojIDIuIFByZXBhcmUgQW5uRGF0YSBvYmplY3QKb2JzIDwtIGRhdGEuZnJhbWUoCiAgcm93Lm5hbWVzID0gY29sbmFtZXMoc2V1cmF0X29iaiksCiAgbG91dmFpbiA9IGFzLmNoYXJhY3RlcihzZXVyYXRfb2JqJHNldXJhdF9jbHVzdGVycykKKQoKYWRhdGFfc2N0IDwtIGFkJEFubkRhdGEoWCA9IGdlbmVfbWF0cml4LCBvYnMgPSBvYnMpCgojIDMuIFJ1biBQQUdBIHdvcmtmbG93CmNhdCgiQ29tcHV0aW5nIG5laWdoYm9ycyBhbmQgUEFHQSBvbiBTQ1Qtbm9ybWFsaXplZCBleHByZXNzaW9uLi4uXG4iKQpzYyRwcCRuZWlnaGJvcnMoYWRhdGFfc2N0LCB1c2VfcmVwID0gIlgiLCBuX25laWdoYm9ycyA9IDE1TCkKc2MkdGwkcGFnYShhZGF0YV9zY3QsIGdyb3VwcyA9ICJsb3V2YWluIikKCiMgNC4gUGxvdCBhbmQgc2F2ZQpvdXRwdXRfZmlsZV9zY3QgPC0gIk91dHB1dF9GaWd1cmVzL1N1cHBsZW1lbnRhcnlfUEFHQV9HZW5lRXhwcmVzc2lvbl9TQ1QucG5nIgoKIyBEcmF3IFBBR0EgZmlyc3QgdG8gY29tcHV0ZSBwb3NpdGlvbnMKc2MkcGwkcGFnYShhZGF0YV9zY3QsIHRocmVzaG9sZCA9IDAuMTUsIHNob3cgPSBGQUxTRSkKCiMgQ29tcHV0ZSBVTUFQIGluaXRpYWxpemVkIGJ5IFBBR0EKc2MkdGwkdW1hcChhZGF0YV9zY3QsIGluaXRfcG9zID0gInBhZ2EiKQoKIyBGaW5hbCBQQUdBIHBsb3QKc2MkcGwkcGFnYShhZGF0YV9zY3QsIHRocmVzaG9sZCA9IDAuMTUsIHNob3cgPSBGQUxTRSwgc2F2ZSA9ICJfR2VuZUV4cHJlc3Npb25fU0NULnBuZyIpCgojIE1vdmUgZnJvbSBmaWd1cmVzLyB0byBPdXRwdXRfRmlndXJlcy8KaWYoZmlsZS5leGlzdHMoImZpZ3VyZXMvcGFnYV9HZW5lRXhwcmVzc2lvbl9TQ1QucG5nIikpewogIGZpbGUucmVuYW1lKCJmaWd1cmVzL3BhZ2FfR2VuZUV4cHJlc3Npb25fU0NULnBuZyIsIG91dHB1dF9maWxlX3NjdCkKICBjYXQoc3ByaW50Zigi4pyTIFNDVCBHZW5lIEV4cHJlc3Npb24gUEFHQSBzYXZlZCB0bzogJXNcbiIsIG91dHB1dF9maWxlX3NjdCkpCn0KCiMgRGlzcGxheQppZihmaWxlLmV4aXN0cyhvdXRwdXRfZmlsZV9zY3QpKXsKICBrbml0cjo6aW5jbHVkZV9ncmFwaGljcyhvdXRwdXRfZmlsZV9zY3QpCn0KYGBgCiMgU1VQUExFTUVOVEFSWSBGSUdVUkU6IE1hbGlnbmFudC1Pbmx5IFBBR0EtU0NUIEFzc2F5CmBgYHtyLCBmaWcuaGVpZ2h0PTE2LCBmaWcud2lkdGg9MTZ9CiMgUnVuIFBBR0Egb24gbWFsaWduYW50IGNlbGxzIG9ubHkKbGlicmFyeShyZXRpY3VsYXRlKQpsaWJyYXJ5KFNldXJhdCkKCiMgMS4gU3Vic2V0IHRvIG1hbGlnbmFudCBjZWxscwptYWxpZ25hbnRfY2x1c3RlcnMgPC0gc2V0ZGlmZih1bmlxdWUoc2V1cmF0X29iaiRzZXVyYXRfY2x1c3RlcnMpLCBjKDMsIDEwKSkKc2V1cmF0X21hbGlnbmFudCA8LSBzdWJzZXQoc2V1cmF0X29iaiwgc3Vic2V0ID0gc2V1cmF0X2NsdXN0ZXJzICVpbiUgbWFsaWduYW50X2NsdXN0ZXJzKQoKIyAyLiBQcmVwYXJlIGZvciBQQUdBCnNjIDwtIGltcG9ydCgic2NhbnB5IikKYWQgPC0gaW1wb3J0KCJhbm5kYXRhIikKCiMgR2V0IGV4cHJlc3Npb24gbWF0cml4IGFuZCBtZXRhZGF0YSAobWFsaWduYW50IG9ubHkpCmV4cHJfbWF0cml4IDwtIHQoR2V0QXNzYXlEYXRhKHNldXJhdF9tYWxpZ25hbnQsIGFzc2F5ID0gIlNDVCIsIGxheWVyID0gImRhdGEiKSkKb2JzIDwtIGRhdGEuZnJhbWUoCiAgcm93Lm5hbWVzID0gY29sbmFtZXMoc2V1cmF0X21hbGlnbmFudCksCiAgbG91dmFpbiA9IGFzLmNoYXJhY3RlcihzZXVyYXRfbWFsaWduYW50JHNldXJhdF9jbHVzdGVycykKKQoKYWRhdGFfbWFsaWduYW50IDwtIGFkJEFubkRhdGEoWCA9IGV4cHJfbWF0cml4LCBvYnMgPSBvYnMpCgojIDMuIFJ1biBQQUdBCnNjJHBwJG5laWdoYm9ycyhhZGF0YV9tYWxpZ25hbnQsIHVzZV9yZXAgPSAiWCIsIG5fbmVpZ2hib3JzID0gMTVMKQpzYyR0bCRwYWdhKGFkYXRhX21hbGlnbmFudCwgZ3JvdXBzID0gImxvdXZhaW4iKQoKIyA0LiBQbG90CnNjJHBsJHBhZ2EoYWRhdGFfbWFsaWduYW50LCB0aHJlc2hvbGQgPSAwLjE1LCBzaG93ID0gRkFMU0UpCgojIENvbXB1dGUgVU1BUCBpbml0aWFsaXplZCBieSBQQUdBCnNjJHRsJHVtYXAoYWRhdGFfbWFsaWduYW50LCBpbml0X3BvcyA9ICJwYWdhIikKCiMgU2F2ZSBmaWd1cmUKcGx0IDwtIGltcG9ydCgibWF0cGxvdGxpYi5weXBsb3QiKQpzYyRwbCRwYWdhKGFkYXRhX21hbGlnbmFudCwgdGhyZXNob2xkID0gMC4xNSwgc2hvdyA9IEZBTFNFKSAgIyBEcmF3IHRoZSBwbG90CnBsdCRzYXZlZmlnKCJPdXRwdXRfRmlndXJlcy9QQUdBX01hbGlnbmFudF9Pbmx5LnBuZyIsIGRwaSA9IDMwMEwsIGJib3hfaW5jaGVzID0gInRpZ2h0IikKcGx0JGNsb3NlKCkKCmNhdCgi4pyTIE1hbGlnbmFudC1vbmx5IFBBR0Egc2F2ZWQgdG8gT3V0cHV0X0ZpZ3VyZXMvUEFHQV9NYWxpZ25hbnRfT25seS5wbmdcbiIpCgoKb3V0cHV0X2ZpbGUgPC0gIk91dHB1dF9GaWd1cmVzL1BBR0FfTWFsaWduYW50X09ubHkucG5nIgoKaWYgKGZpbGUuZXhpc3RzKG91dHB1dF9maWxlKSkgewogIGtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKG91dHB1dF9maWxlKQp9CgpgYGAKIyBTVVBQTEVNRU5UQVJZIEZJR1VSRTogTWFsaWduYW50LU9ubHkgUEFHQS1URiBBc3NheQpgYGB7ciwgZmlnLmhlaWdodD0xNiwgZmlnLndpZHRoPTE2fQojIFJ1biBQQUdBIG9uIG1hbGlnbmFudCBjZWxscyB1c2luZyBURiBhY3Rpdml0eQpsaWJyYXJ5KHJldGljdWxhdGUpCmxpYnJhcnkoU2V1cmF0KQoKY2F0KCI9PT0gUEFHQSBBTkFMWVNJUyBVU0lORyBURiBBQ1RJVklUWSA9PT1cblxuIikKCiMgMS4gU3Vic2V0IHRvIG1hbGlnbmFudCBjZWxscwptYWxpZ25hbnRfY2x1c3RlcnMgPC0gc2V0ZGlmZih1bmlxdWUoc2V1cmF0X29iaiRzZXVyYXRfY2x1c3RlcnMpLCBjKDMsIDEwKSkKc2V1cmF0X21hbGlnbmFudCA8LSBzdWJzZXQoc2V1cmF0X29iaiwgc3Vic2V0ID0gc2V1cmF0X2NsdXN0ZXJzICVpbiUgbWFsaWduYW50X2NsdXN0ZXJzKQoKY2F0KCJNYWxpZ25hbnQgY2VsbHM6IiwgbmNvbChzZXVyYXRfbWFsaWduYW50KSwgIlxuIikKY2F0KCJDbHVzdGVyczoiLCBwYXN0ZShzb3J0KHVuaXF1ZShzZXVyYXRfbWFsaWduYW50JHNldXJhdF9jbHVzdGVycykpLCBjb2xsYXBzZSA9ICIsICIpLCAiXG5cbiIpCgojIDIuIENoZWNrIGZvciBURiBhc3NheQphdmFpbGFibGVfYXNzYXlzIDwtIEFzc2F5cyhzZXVyYXRfbWFsaWduYW50KQp0Zl9hc3NheV9uYW1lIDwtIE5VTEwKcG9zc2libGVfdGZfbmFtZXMgPC0gYygiZG9yb3RoZWEiLCAiRG9Sb3RoRUEiLCAiVEYiLCAidmlwZXIiLCAiVklQRVIiLCAicmVndWxvbiIpCgpmb3IobmFtZSBpbiBwb3NzaWJsZV90Zl9uYW1lcykgewogIGlmKG5hbWUgJWluJSBhdmFpbGFibGVfYXNzYXlzKSB7CiAgICB0Zl9hc3NheV9uYW1lIDwtIG5hbWUKICAgIGJyZWFrCiAgfQp9CgppZihpcy5udWxsKHRmX2Fzc2F5X25hbWUpKSB7CiAgY2F0KCLimqDvuI8gTm8gVEYgYXNzYXkgZm91bmQuIEF2YWlsYWJsZSBhc3NheXM6IiwgcGFzdGUoYXZhaWxhYmxlX2Fzc2F5cywgY29sbGFwc2UgPSAiLCAiKSwgIlxuIikKICBjYXQoIlBsZWFzZSBydW4gRG9Sb3RoRUEgZmlyc3QuIFN0b3BwaW5nLi4uXG4iKQogIGtuaXRyOjprbml0X2V4aXQoKQp9CgpjYXQoIuKckyBVc2luZyBURiBhc3NheToiLCB0Zl9hc3NheV9uYW1lLCAiXG4iKQpjYXQoIlRGcyBhdmFpbGFibGU6IiwgbnJvdyhzZXVyYXRfbWFsaWduYW50W1t0Zl9hc3NheV9uYW1lXV0pLCAiXG5cbiIpCgojIDMuIFByZXBhcmUgZm9yIFBBR0EKc2MgPC0gaW1wb3J0KCJzY2FucHkiKQphZCA8LSBpbXBvcnQoImFubmRhdGEiKQoKIyBHZXQgVEYgYWN0aXZpdHkgbWF0cml4IGFuZCBtZXRhZGF0YSAobWFsaWduYW50IG9ubHkpCkRlZmF1bHRBc3NheShzZXVyYXRfbWFsaWduYW50KSA8LSB0Zl9hc3NheV9uYW1lCnRmX21hdHJpeCA8LSBHZXRBc3NheURhdGEoc2V1cmF0X21hbGlnbmFudCwgYXNzYXkgPSB0Zl9hc3NheV9uYW1lLCBsYXllciA9ICJkYXRhIikKCiMgVHJhbnNwb3NlOiBjZWxscyB4IFRGcwpleHByX21hdHJpeCA8LSB0KGFzLm1hdHJpeCh0Zl9tYXRyaXgpKQoKb2JzIDwtIGRhdGEuZnJhbWUoCiAgcm93Lm5hbWVzID0gY29sbmFtZXMoc2V1cmF0X21hbGlnbmFudCksCiAgbG91dmFpbiA9IGFzLmNoYXJhY3RlcihzZXVyYXRfbWFsaWduYW50JHNldXJhdF9jbHVzdGVycykKKQoKYWRhdGFfbWFsaWduYW50X3RmIDwtIGFkJEFubkRhdGEoWCA9IGV4cHJfbWF0cml4LCBvYnMgPSBvYnMpCgpjYXQoIkFubkRhdGEgY3JlYXRlZDoiLCBucm93KGFkYXRhX21hbGlnbmFudF90ZiRvYnMpLCAiY2VsbHMgeCIsIAogICAgbmNvbChhZGF0YV9tYWxpZ25hbnRfdGYkWCksICJURnNcblxuIikKCiMgNC4gUnVuIFBBR0Egb24gVEYgYWN0aXZpdHkKY2F0KCJDb21wdXRpbmcgUEFHQSBvbiBURiBhY3Rpdml0eS4uLlxuIikKc2MkcHAkbmVpZ2hib3JzKGFkYXRhX21hbGlnbmFudF90ZiwgdXNlX3JlcCA9ICJYIiwgbl9uZWlnaGJvcnMgPSAxNUwpCnNjJHRsJHBhZ2EoYWRhdGFfbWFsaWduYW50X3RmLCBncm91cHMgPSAibG91dmFpbiIpCgojIDUuIFBsb3QKc2MkcGwkcGFnYShhZGF0YV9tYWxpZ25hbnRfdGYsIHRocmVzaG9sZCA9IDAuMTUsIHNob3cgPSBGQUxTRSkKCiMgQ29tcHV0ZSBVTUFQIGluaXRpYWxpemVkIGJ5IFBBR0EKY2F0KCJDb21wdXRpbmcgUEFHQS1pbml0aWFsaXplZCBVTUFQLi4uXG4iKQpzYyR0bCR1bWFwKGFkYXRhX21hbGlnbmFudF90ZiwgaW5pdF9wb3MgPSAicGFnYSIpCgojIFNhdmUgZmlndXJlCnBsdCA8LSBpbXBvcnQoIm1hdHBsb3RsaWIucHlwbG90IikKc2MkcGwkcGFnYShhZGF0YV9tYWxpZ25hbnRfdGYsIHRocmVzaG9sZCA9IDAuMTUsIHNob3cgPSBGQUxTRSkgICMgRHJhdyB0aGUgcGxvdApwbHQkc2F2ZWZpZygiT3V0cHV0X0ZpZ3VyZXMvUEFHQV9NYWxpZ25hbnRfVEYucG5nIiwgZHBpID0gMzAwTCwgYmJveF9pbmNoZXMgPSAidGlnaHQiKQpwbHQkY2xvc2UoKQoKY2F0KCLinJMgTWFsaWduYW50LW9ubHkgUEFHQSAoVEYpIHNhdmVkIHRvIE91dHB1dF9GaWd1cmVzL1BBR0FfTWFsaWduYW50X1RGLnBuZ1xuXG4iKQoKIyBEaXNwbGF5Cm91dHB1dF9maWxlIDwtICJPdXRwdXRfRmlndXJlcy9QQUdBX01hbGlnbmFudF9URi5wbmciCgppZiAoZmlsZS5leGlzdHMob3V0cHV0X2ZpbGUpKSB7CiAga25pdHI6OmluY2x1ZGVfZ3JhcGhpY3Mob3V0cHV0X2ZpbGUpCn0KCmNhdCgiPT09IFRGLUJBU0VEIFBBR0EgQ09NUExFVEUgPT09XG4iKQoKYGBgCgoKIyBWZXJpZmljYXRpb24gb2YgVEYgQXZhaWxhYmlsaXR5CmBgYHtyICwgZmlnLmhlaWdodD0xNiwgZmlnLndpZHRoPTE2fQpsaWJyYXJ5KFNldXJhdCkKbGlicmFyeShkcGx5cikKCmNhdCgiPT09IENIRUNLSU5HIFRGIEFWQUlMQUJJTElUWSBJTiBET1JPVEhFQSBBU1NBWSA9PT1cblxuIikKCiMgMS4gSWRlbnRpZnkgdGhlIFRGIGFzc2F5CmF2YWlsYWJsZV9hc3NheXMgPC0gQXNzYXlzKHNldXJhdF9vYmopCnRmX2Fzc2F5X25hbWUgPC0gZ3JlcCgiZG9yb3RoZWF8dmlwZXJ8VEYiLCBhdmFpbGFibGVfYXNzYXlzLCB2YWx1ZT1UUlVFKVsxXQoKaWYoaXMubmEodGZfYXNzYXlfbmFtZSkpIHsKICBzdG9wKCLinYwgTm8gVEYgYXNzYXkgZm91bmQhIFBsZWFzZSBydW4gRG9Sb3RoRUEgZmlyc3QuIikKfQpjYXQoIuKckyBGb3VuZCBURiBhc3NheToiLCB0Zl9hc3NheV9uYW1lLCAiXG4iKQoKIyAyLiBHZXQgYWxsIGF2YWlsYWJsZSBURnMKYWxsX3RmcyA8LSByb3duYW1lcyhzZXVyYXRfb2JqW1t0Zl9hc3NheV9uYW1lXV0pCmNhdCgi4pyTIFRvdGFsIFRGcyBpbmZlcnJlZDoiLCBsZW5ndGgoYWxsX3RmcyksICJcblxuIikKCiMgMy4gRGVmaW5lIG91ciBEZXNpcmVkIFBhbmVsIChMaXRlcmF0dXJlLUJhc2VkKQpkZXNpcmVkX3RmcyA8LSBsaXN0KAogICJTdGVtbmVzcyIgPSBjKCJUQ0Y3IiwgIkxFRjEiLCAiQkFDSDIiLCAiRk9YTzEiLCAiTVlDIiksCiAgIk1hbGlnbmFuY3kiID0gYygiVE9YIiwgIlNUQVQ1QSIsICJTVEFUMyIsICJTVEFUNUIiLCAiR0FUQTMiLCAiSUtaRjIiKSwKICAiRXhoYXVzdGlvbiIgPSBjKCJFT01FUyIsICJUQlgyMSIsICJQUkRNMSIsICJORkFUQzEiLCAiQkFURiIpLAogICJUcmVnX0xpa2UiID0gYygiRk9YUDMiLCAiSUtaRjIiLCAiU0FUQjEiKSAKKQoKIyA0LiBDaGVjayBQcmVzZW5jZQphdmFpbGFibGVfcGFuZWwgPC0gbGlzdCgpCm1pc3NpbmdfdGZzIDwtIGMoKQoKY2F0KCItLS0gUEFORUwgVkVSSUZJQ0FUSU9OIC0tLVxuIikKZm9yKGNhdGVnb3J5IGluIG5hbWVzKGRlc2lyZWRfdGZzKSkgewogIHRmcyA8LSBkZXNpcmVkX3Rmc1tbY2F0ZWdvcnldXQogIHByZXNlbnQgPC0gaW50ZXJzZWN0KHRmcywgYWxsX3RmcykKICBtaXNzaW5nIDwtIHNldGRpZmYodGZzLCBhbGxfdGZzKQogIAogIGF2YWlsYWJsZV9wYW5lbFtbY2F0ZWdvcnldXSA8LSBwcmVzZW50CiAgbWlzc2luZ190ZnMgPC0gYyhtaXNzaW5nX3RmcywgbWlzc2luZykKICAKICBjYXQoc3ByaW50ZigiJXM6ICVkLyVkIGZvdW5kXG4iLCBjYXRlZ29yeSwgbGVuZ3RoKHByZXNlbnQpLCBsZW5ndGgodGZzKSkpCiAgaWYobGVuZ3RoKHByZXNlbnQpID4gMCkgY2F0KCIgIOKchSBQcmVzZW50OiIsIHBhc3RlKHByZXNlbnQsIGNvbGxhcHNlPSIsICIpLCAiXG4iKQogIGlmKGxlbmd0aChtaXNzaW5nKSA+IDApIGNhdCgiICDinYwgTWlzc2luZzoiLCBwYXN0ZShtaXNzaW5nLCBjb2xsYXBzZT0iLCAiKSwgIlxuIikKICBjYXQoIlxuIikKfQoKIyA1LiBTdW1tYXJ5CmZpbmFsX3RmX2xpc3QgPC0gdW5saXN0KGF2YWlsYWJsZV9wYW5lbCkKY2F0KCI9PT0gRklOQUwgVEYgTElTVCBGT1IgVFJBSkVDVE9SWSA9PT1cbiIpCmNhdCgiVG90YWwgVEZzIHRvIHBsb3Q6IiwgbGVuZ3RoKGZpbmFsX3RmX2xpc3QpLCAiXG4iKQpwcmludChmaW5hbF90Zl9saXN0KQoKaWYoIlRPWCIgJWluJSBmaW5hbF90Zl9saXN0KSBjYXQoIlxu4pyTIENSSVRJQ0FMOiBUT1ggaXMgYXZhaWxhYmxlIVxuIikKaWYoIlRDRjciICVpbiUgZmluYWxfdGZfbGlzdCkgY2F0KCLinJMgQ1JJVElDQUw6IFRDRjcgaXMgYXZhaWxhYmxlIVxuIikKaWYoIlNUQVQ1QSIgJWluJSBmaW5hbF90Zl9saXN0KSBjYXQoIuKckyBDUklUSUNBTDogU1RBVDVBIGlzIGF2YWlsYWJsZSFcbiIpCgojIDYuIFNhdmUgdmFsaWQgbGlzdCBmb3IgbmV4dCBzdGVwCnNhdmVSRFMoZmluYWxfdGZfbGlzdCwgInZhbGlkX3RmX3BhbmVsLnJkcyIpCgpgYGAKCgojIFBBR0EgR2VuZSBEeW5hbWljcyBmb3IgTWFsaWduYW50IFPDqXphcnkgQ2VsbHMgKFRGKQpgYGB7ciAsIGZpZy5oZWlnaHQ9MTQsIGZpZy53aWR0aD0yNX0KbGlicmFyeShyZXRpY3VsYXRlKQpsaWJyYXJ5KFNldXJhdCkKbGlicmFyeShkcGx5cikKCiMgU2V0dXAgUHl0aG9uCnNjIDwtIGltcG9ydCgic2NhbnB5IikKYWQgPC0gaW1wb3J0KCJhbm5kYXRhIikKcGwgPC0gaW1wb3J0KCJtYXRwbG90bGliLnB5cGxvdCIpCgpjYXQoIj09PSBURiBSRUdVTEFUT1JZIERZTkFNSUNTICg1IFRSQUpFQ1RPUklFUykgPT09XG5cbiIpCgojIDEuIElERU5USUZZIFRGIEFTU0FZICYgQ0hFQ0sgQVZBSUxBQklMSVRZCmF2YWlsYWJsZV9hc3NheXMgPC0gQXNzYXlzKHNldXJhdF9vYmopCnRmX2Fzc2F5X25hbWUgPC0gZ3JlcCgiZG9yb3RoZWF8dmlwZXJ8VEYiLCBhdmFpbGFibGVfYXNzYXlzLCB2YWx1ZT1UUlVFKVsxXQoKaWYoaXMubmEodGZfYXNzYXlfbmFtZSkpIHsKICBzdG9wKCLinYwgTm8gVEYgYXNzYXkgZm91bmQhIFBsZWFzZSBydW4gRG9Sb3RoRUEgZmlyc3QuIikKfQpjYXQoIuKckyBVc2luZyBURiBhc3NheToiLCB0Zl9hc3NheV9uYW1lLCAiXG4iKQoKIyBHZXQgYWxsIGF2YWlsYWJsZSBURnMKYWxsX3Rmc19pbl9vYmplY3QgPC0gcm93bmFtZXMoc2V1cmF0X29ialtbdGZfYXNzYXlfbmFtZV1dKQpjYXQoIuKckyBUb3RhbCBURnMgaW5mZXJyZWQ6IiwgbGVuZ3RoKGFsbF90ZnNfaW5fb2JqZWN0KSwgIlxuXG4iKQoKIyAyLiBERUZJTkUgJiBWQUxJREFURSBURiBQQU5FTApkZXNpcmVkX3RmcyA8LSBsaXN0KAogICJTdGVtbmVzcyIgPSBjKCJUQ0Y3IiwgIkxFRjEiLCAiQkFDSDIiLCAiRk9YTzEiLCAiTVlDIiksCiAgIk1hbGlnbmFuY3kiID0gYygiVE9YIiwgIlNUQVQ1QSIsICJTVEFUMyIsICJTVEFUNUIiLCAiR0FUQTMiLCAiSUtaRjIiKSwKICAiRXhoYXVzdGlvbiIgPSBjKCJFT01FUyIsICJUQlgyMSIsICJQUkRNMSIsICJORkFUQzEiLCAiQkFURiIpLAogICJUcmVnX0xpa2UiID0gYygiRk9YUDMiLCAiU0FUQjEiKQopCgpmbGF0X3ZhbGlkX3RmcyA8LSBjKCkKY2F0KCItLS0gVEYgUEFORUwgVkFMSURBVElPTiAtLS1cbiIpCmZvcihjYXRlZ29yeSBpbiBuYW1lcyhkZXNpcmVkX3RmcykpIHsKICB0ZnMgPC0gZGVzaXJlZF90ZnNbW2NhdGVnb3J5XV0KICBwcmVzZW50IDwtIGludGVyc2VjdCh0ZnMsIGFsbF90ZnNfaW5fb2JqZWN0KQogIG1pc3NpbmcgPC0gc2V0ZGlmZih0ZnMsIGFsbF90ZnNfaW5fb2JqZWN0KQogIAogIGlmKGxlbmd0aChwcmVzZW50KSA+IDApIHsKICAgIGZsYXRfdmFsaWRfdGZzIDwtIGMoZmxhdF92YWxpZF90ZnMsIHByZXNlbnQpCiAgICBjYXQoc3ByaW50Zigi4pyFICVzOiAlc1xuIiwgY2F0ZWdvcnksIHBhc3RlKHByZXNlbnQsIGNvbGxhcHNlPSIsICIpKSkKICB9CiAgaWYobGVuZ3RoKG1pc3NpbmcpID4gMCkgewogICAgY2F0KHNwcmludGYoIuKdjCAlcyAoTWlzc2luZyk6ICVzXG4iLCBjYXRlZ29yeSwgcGFzdGUobWlzc2luZywgY29sbGFwc2U9IiwgIikpKQogIH0KfQoKaWYobGVuZ3RoKGZsYXRfdmFsaWRfdGZzKSA9PSAwKSBzdG9wKCJObyBURnMgZm91bmQhIikKY2F0KCJcbuKckyBVc2luZyIsIGxlbmd0aChmbGF0X3ZhbGlkX3RmcyksICJURnNcblxuIikKCiMgMy4gUFJFUEFSRSBEQVRBIChNYWxpZ25hbnQgT25seSkgLSBDTEVBTiBSRUNSRUFUSU9OCm1hbGlnbmFudF9jbHVzdGVycyA8LSBzZXRkaWZmKHVuaXF1ZShzZXVyYXRfb2JqJHNldXJhdF9jbHVzdGVycyksIGMoMywgMTApKQpzZXVyYXRfbWFsaWduYW50IDwtIHN1YnNldChzZXVyYXRfb2JqLCBzdWJzZXQgPSBzZXVyYXRfY2x1c3RlcnMgJWluJSBtYWxpZ25hbnRfY2x1c3RlcnMpCgpEZWZhdWx0QXNzYXkoc2V1cmF0X21hbGlnbmFudCkgPC0gdGZfYXNzYXlfbmFtZQp0Zl9tYXRyaXggPC0gR2V0QXNzYXlEYXRhKHNldXJhdF9tYWxpZ25hbnQsIGFzc2F5ID0gdGZfYXNzYXlfbmFtZSwgbGF5ZXIgPSAiZGF0YSIpCnRmX21hdHJpeCA8LSB0Zl9tYXRyaXhbZmxhdF92YWxpZF90ZnMsIF0KCiMgQ29udmVydCB0byBkZW5zZSBtYXRyaXggYW5kIHRyYW5zcG9zZQpleHByX21hdHJpeCA8LSB0KGFzLm1hdHJpeCh0Zl9tYXRyaXgpKQoKIyBDcmVhdGUgY2xlYW4gb2JzIGRhdGFmcmFtZQpvYnNfZGYgPC0gZGF0YS5mcmFtZSgKICBzZXVyYXRfY2x1c3RlcnMgPSBhcy5jaGFyYWN0ZXIoc2V1cmF0X21hbGlnbmFudCRzZXVyYXRfY2x1c3RlcnMpLAogIHJvdy5uYW1lcyA9IGNvbG5hbWVzKHNldXJhdF9tYWxpZ25hbnQpLAogIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRQopCgojIENyZWF0ZSBBbm5EYXRhIHVzaW5nIFB5dGhvbiBkaXJlY3RseSB0byBhdm9pZCByZXRpY3VsYXRlIGlzc3VlcwpjYXQoIkNyZWF0aW5nIEFubkRhdGEgb2JqZWN0Li4uXG4iKQoKIyBFeHBvcnQgZGF0YSB0byBQeXRob24gZW52aXJvbm1lbnQKcHkkZXhwcl9tYXRyaXggPC0gZXhwcl9tYXRyaXgKcHkkb2JzX2RmIDwtIG9ic19kZgpweSR2YXJfbmFtZXMgPC0gZmxhdF92YWxpZF90ZnMKCnB5X3J1bl9zdHJpbmcoIgppbXBvcnQgc2NhbnB5IGFzIHNjCmltcG9ydCBhbm5kYXRhIGFzIGFkCmltcG9ydCBwYW5kYXMgYXMgcGQKaW1wb3J0IG51bXB5IGFzIG5wCgojIENyZWF0ZSBBbm5EYXRhIGNsZWFubHkgaW4gUHl0aG9uCmFkYXRhX3RmID0gYWQuQW5uRGF0YSgKICAgIFg9ci5leHByX21hdHJpeCwKICAgIG9icz1yLm9ic19kZgopCgojIFNldCB2YXJpYWJsZSBuYW1lcwphZGF0YV90Zi52YXJfbmFtZXMgPSByLnZhcl9uYW1lcwoKcHJpbnQoZifinJMgQW5uRGF0YSBjcmVhdGVkOiB7YWRhdGFfdGYubl9vYnN9IGNlbGxzIHgge2FkYXRhX3RmLm5fdmFyc30gVEZzJykKIikKCiMgUmV0cmlldmUgdGhlIGNsZWFuIGFkYXRhIG9iamVjdCBiYWNrIHRvIFIKYWRhdGFfdGYgPC0gcHkkYWRhdGFfdGYKCmNhdCgi4pyTIEFubkRhdGEgb2JqZWN0IHJlYWR5XG5cbiIpCgojIDQuIFJVTiBQQUdBICYgUFNFVURPVElNRSAtIEFMTCBJTiBQWVRIT04KY2F0KCJDb21wdXRpbmcgUEFHQSBvbiBURiBhY3Rpdml0eS4uLlxuIikKCnB5X3J1bl9zdHJpbmcoIgppbXBvcnQgc2NhbnB5IGFzIHNjCgphZGF0YSA9IGFkYXRhX3RmICAjIFVzZSB0aGUgY2xlYW4gb2JqZWN0IHdlIGp1c3QgY3JlYXRlZAoKIyBDb21wdXRlIG5laWdoYm9ycwpzYy5wcC5uZWlnaGJvcnMoYWRhdGEsIG5fbmVpZ2hib3JzPTE1LCB1c2VfcmVwPSdYJykKcHJpbnQoJ+KckyBOZWlnaGJvcnMgY29tcHV0ZWQnKQoKIyBDb21wdXRlIFBBR0EKc2MudGwucGFnYShhZGF0YSwgZ3JvdXBzPSdzZXVyYXRfY2x1c3RlcnMnKQpwcmludCgn4pyTIFBBR0EgY29tcHV0ZWQnKQoKIyBQbG90IFBBR0EgdG8gZ2VuZXJhdGUgcG9zaXRpb25zCnNjLnBsLnBhZ2EoYWRhdGEsIHRocmVzaG9sZD0wLjE1LCBzaG93PUZhbHNlKQpwcmludCgn4pyTIFBBR0EgbGF5b3V0IGNvbXB1dGVkJykKCiMgQ29tcHV0ZSBVTUFQIHdpdGggUEFHQSBpbml0aWFsaXphdGlvbgpzYy50bC51bWFwKGFkYXRhLCBpbml0X3Bvcz0ncGFnYScpCnByaW50KCfinJMgVU1BUCBjb21wdXRlZCcpCgojIENvbXB1dGUgZm9yY2UtZGlyZWN0ZWQgbGF5b3V0CnNjLnRsLmRyYXdfZ3JhcGgoYWRhdGEsIGxheW91dD0nZnInKQpwcmludCgn4pyTIEdyYXBoIGxheW91dCBjb21wdXRlZCcpCiIpCgojIFVwZGF0ZSBSIHJlZmVyZW5jZQphZGF0YV90ZiA8LSBweSRhZGF0YV90ZgoKIyBFTkhBTkNFRCBST09UIFNFTEVDVElPTiAtIEZJWEVECmNhdCgiXG4tLS0gUk9PVCBDRUxMIFNFTEVDVElPTiAtLS1cbiIpCnN0ZW1fdGZzX2Zvcl9yb290IDwtIGludGVyc2VjdChjKCdUQ0Y3JywgJ0xFRjEnLCAnQkFDSDInLCAnRk9YTzEnKSwgZmxhdF92YWxpZF90ZnMpCmNhdCgiU3RlbSBURnMgYXZhaWxhYmxlOiIsIHBhc3RlKHN0ZW1fdGZzX2Zvcl9yb290LCBjb2xsYXBzZT0iLCAiKSwgIlxuIikKCmNsdXN0ZXJfNV9pbmRpY2VzIDwtIHdoaWNoKGFkYXRhX3RmJG9icyRzZXVyYXRfY2x1c3RlcnMgPT0gIjUiKQoKaWYobGVuZ3RoKGNsdXN0ZXJfNV9pbmRpY2VzKSA+IDAgJiYgbGVuZ3RoKHN0ZW1fdGZzX2Zvcl9yb290KSA+IDApIHsKICBzdGVtX2V4cHIgPC0gYWRhdGFfdGYkWFtjbHVzdGVyXzVfaW5kaWNlcywgXQogIGlmKGluaGVyaXRzKHN0ZW1fZXhwciwgImRnQ01hdHJpeCIpKSBzdGVtX2V4cHIgPC0gYXMubWF0cml4KHN0ZW1fZXhwcikKICAKICBpZihsZW5ndGgoc3RlbV90ZnNfZm9yX3Jvb3QpID4gMSkgewogICAgc3RlbV9pbmRpY2VzIDwtIG1hdGNoKHN0ZW1fdGZzX2Zvcl9yb290LCBjb2xuYW1lcyhhZGF0YV90ZiRYKSkKICAgIGF2Z19leHByIDwtIHJvd01lYW5zKHN0ZW1fZXhwclssIHN0ZW1faW5kaWNlc10pCiAgfSBlbHNlIHsKICAgIGF2Z19leHByIDwtIHN0ZW1fZXhwclssIG1hdGNoKHN0ZW1fdGZzX2Zvcl9yb290LCBjb2xuYW1lcyhhZGF0YV90ZiRYKSldCiAgfQogIAogIHJvb3RfY2VsbCA8LSBjbHVzdGVyXzVfaW5kaWNlc1t3aGljaC5tYXgoYXZnX2V4cHIpXSAtIDFMCiAgCiAgIyBTZXQgaXJvb3QgaW4gUHl0aG9uCiAgcHlfcnVuX3N0cmluZyhzcHJpbnRmKCJhZGF0YV90Zi51bnNbJ2lyb290J10gPSAlZCIsIHJvb3RfY2VsbCkpCiAgCiAgY2F0KCLinJMgUm9vdCBjZWxsOiIsIHJvb3RfY2VsbCwgIihDbHVzdGVyIDUpXG4iKQp9IGVsc2UgewogIHB5X3J1bl9zdHJpbmcoImFkYXRhX3RmLnVuc1snaXJvb3QnXSA9IDAiKQogIGNhdCgi4pqg77iPIFVzaW5nIGNlbGwgMCBhcyBmYWxsYmFja1xuIikKfQoKIyBESUZGVVNJT04gUFNFVURPVElNRQpjYXQoIkNvbXB1dGluZyBkaWZmdXNpb24gcHNldWRvdGltZS4uLlxuIikKCnB5X3J1bl9zdHJpbmcoIgppbXBvcnQgc2NhbnB5IGFzIHNjCgojIENvbXB1dGUgZGlmZnVzaW9uIG1hcCBhbmQgcHNldWRvdGltZQpzYy50bC5kaWZmbWFwKGFkYXRhX3RmLCBuX2NvbXBzPTE1KQpzYy50bC5kcHQoYWRhdGFfdGYpCgpwcmludCgn4pyTIERpZmZ1c2lvbiBwc2V1ZG90aW1lIGNvbXB1dGVkJykKIikKCiMgVXBkYXRlIFIgcmVmZXJlbmNlCmFkYXRhX3RmIDwtIHB5JGFkYXRhX3RmCgpjYXQoIuKckyBQc2V1ZG90aW1lIGNvbXB1dGF0aW9uIGNvbXBsZXRlXG5cbiIpCgojIDUuIFZJU1VBTElaRSBQU0VVRE9USU1FCmNhdCgiUGxvdHRpbmcgcHNldWRvdGltZS4uLlxuIikKc2MkcGwkZHJhd19ncmFwaCgKICBhZGF0YV90ZiwKICBjb2xvciA9IGxpc3QoJ3NldXJhdF9jbHVzdGVycycsICdkcHRfcHNldWRvdGltZScpLAogIGxlZ2VuZF9sb2MgPSAnb24gZGF0YScsCiAgbGVnZW5kX2ZvbnRzaXplID0gMTAsCiAgY21hcCA9ICdwbGFzbWEnLAogIHNpemUgPSA1MCwKICBhbHBoYSA9IDAuOCwKICBzaG93ID0gRkFMU0UKKQoKcGx0X2ZpbGUgPC0gIk91dHB1dF9GaWd1cmVzL1BBR0FfVEZfUHNldWRvdGltZV81cGF0aHMucG5nIgppZighZGlyLmV4aXN0cygiT3V0cHV0X0ZpZ3VyZXMiKSkgZGlyLmNyZWF0ZSgiT3V0cHV0X0ZpZ3VyZXMiKQpwbCRzYXZlZmlnKHBsdF9maWxlLCBkcGkgPSAzMDBMLCBiYm94X2luY2hlcyA9ICJ0aWdodCIpCnBsJGNsb3NlKCkKY2F0KCLinJMgUHNldWRvdGltZSBzYXZlZFxuXG4iKQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhwbHRfZmlsZSkKCiMgNi4gREVGSU5FIDUgVFJBSkVDVE9SSUVTCnBhdGhzIDwtIGxpc3QoCiAgbGlzdCgnU3RlbV90b19UZXJtaW5hbF92aWFfSHViJywgYygnNScsICcxMScsICcxJykpLAogIGxpc3QoJ1N0ZW1fdG9fRXhoYXVzdGVkX1N0YXRlJywgYygnNScsICc3JywgJzknKSksCiAgbGlzdCgnU3RlbV90b19BY3RpdmF0ZWRfRWZmZWN0b3InLCBjKCc1JywgJzEyJywgJzAnLCAnNCcpKSwKICBsaXN0KCdTdGVtX3ZpYV9IdWI4X3RvX1Rlcm1pbmFsJywgYygnNScsICcxMScsICc4JywgJzInKSksCiAgbGlzdCgnU3RlbV90b19UcmVnX0xpa2UnLCBjKCc1JywgJzExJywgJzInLCAnNicpKQopCgpjYXQoIj09PSBERUZJTkVEIFRSQUpFQ1RPUklFUyA9PT1cbiIpCmZvcihpIGluIHNlcV9hbG9uZyhwYXRocykpIHsKICBjYXQoc3ByaW50ZigiJWQuICVzOiAlc1xuIiwgaSwgcGF0aHNbW2ldXVtdLCBwYXN0ZShwYXRoc1tbaV1dW10sIGNvbGxhcHNlID0gIiDihpIgIikpKVsxXVsyXQp9CmNhdCgiXG4iKQoKIyA3LiBQTE9UIFRGIERZTkFNSUNTCmNhdCgiUGxvdHRpbmcgVEYgZHluYW1pY3MuLi5cbiIpCgpuX3BhdGhzIDwtIGxlbmd0aChwYXRocykKZmlnX3dpZHRoIDwtIDUgKiBuX3BhdGhzCgpmaWdfb2JqIDwtIHBsJHN1YnBsb3RzKAogIG5jb2xzID0gYXMuaW50ZWdlcihuX3BhdGhzKSwKICBmaWdzaXplID0gdHVwbGUoZmlnX3dpZHRoLCAxMiksCiAgZ3JpZHNwZWNfa3cgPSBkaWN0KHdzcGFjZSA9IDAuMTIsIGxlZnQgPSAwLjA4KQopCgpheHNfbGlzdCA8LSBpZihuX3BhdGhzID09IDEpIGxpc3QoZmlnX29ialtdKSBlbHNlIGZpZ19vYmpbXVsyXQpwbCRzdWJwbG90c19hZGp1c3QodG9wID0gMC45MiwgYm90dG9tID0gMC4xKQoKZm9yKGlwYXRoIGluIHNlcV9hbG9uZyhwYXRocykpIHsKICBwYXRoX2luZm8gPC0gcGF0aHNbW2lwYXRoXV0KICBwYXRoX25hbWUgPC0gcGF0aF9pbmZvW11bMV0KICBwYXRoX25vZGVzIDwtIHBhdGhfaW5mb1tdWzJdCiAgCiAgY2F0KHNwcmludGYoIiAgUGF0aCAlZDogJXNcbiIsIGlwYXRoLCBwYXRoX25hbWUpKQogIAogIGN1cnJlbnRfYXggPC0gaWYobl9wYXRocyA9PSAxKSBheHNfbGlzdFtdIGVsc2UgYXhzX2xpc3RbW2lwYXRoXV1bMV0KICAKICBzYyRwbCRwYWdhX3BhdGgoCiAgICBhZGF0YSA9IGFkYXRhX3RmLAogICAgbm9kZXMgPSBwYXRoX25vZGVzLAogICAga2V5cyA9IGZsYXRfdmFsaWRfdGZzLAogICAgc2hvd19ub2RlX25hbWVzID0gRkFMU0UsCiAgICBheCA9IGN1cnJlbnRfYXgsCiAgICB5dGlja19mb250c2l6ZSA9IDEwTCwKICAgIGxlZnRfbWFyZ2luID0gMC4xNSwKICAgIG5fYXZnID0gNTBMLAogICAgYW5ub3RhdGlvbnMgPSBsaXN0KCdkcHRfcHNldWRvdGltZScpLAogICAgc2hvd195dGlja3MgPSBpZihpcGF0aCA9PSAxKSBUUlVFIGVsc2UgRkFMU0UsCiAgICBzaG93X2NvbG9yYmFyID0gRkFMU0UsCiAgICBjb2xvcl9tYXAgPSAnUmRCdV9yJywKICAgIGdyb3Vwc19rZXkgPSAnc2V1cmF0X2NsdXN0ZXJzJywKICAgIGNvbG9yX21hcHNfYW5ub3RhdGlvbnMgPSBkaWN0KGRwdF9wc2V1ZG90aW1lID0gJ3ZpcmlkaXMnKSwKICAgIHRpdGxlID0gZ3N1YigiXyIsICIgIiwgcGF0aF9uYW1lKSwKICAgIHVzZV9yYXcgPSBGQUxTRSwKICAgIHNob3cgPSBGQUxTRQogICkKfQoKIyBTYXZlCm91dHB1dF9maWxlIDwtICJPdXRwdXRfRmlndXJlcy9QQUdBX1RGX0R5bmFtaWNzXzVQYXRocy5wbmciCnBsJHNhdmVmaWcob3V0cHV0X2ZpbGUsIGRwaSA9IDMwMEwsIGJib3hfaW5jaGVzID0gInRpZ2h0IikKcGwkY2xvc2UoKQoKY2F0KCJcbuKckyBURiBkeW5hbWljcyBzYXZlZDoiLCBvdXRwdXRfZmlsZSwgIlxuIikKa25pdHI6OmluY2x1ZGVfZ3JhcGhpY3Mob3V0cHV0X2ZpbGUpCgpjYXQoIlxuPT09IFRGIFRSQUpFQ1RPUlkgQU5BTFlTSVMgQ09NUExFVEUgPT09XG4iKQoKYGBgCgojIFBBR0EgR2VuZSBEeW5hbWljcyBmb3IgTWFsaWduYW50IFPDqXphcnkgQ2VsbHMgKFNDVCkKYGBge3IgLCBmaWcuaGVpZ2h0PTE2LCBmaWcud2lkdGg9MTZ9CgoKYGBgCgoKCgo=