Introduction
Sample Information: * N1: Normal
(Constitutional DNA) * T1: Primary Tumor (Fresh Sézary
Cells) * X1: In-vivo derived Cell Line (from Mouse) *
XX1: Secondary Cell Line (cultured from X1)
Variant Allele
Frequency (VAF) Distribution Plot
library(vcfR)
library(ggplot2)
library(dplyr)
library(tidyr)
# Define paths
VCF_DIR <- "/home/bioinfo/1-Thesis_Final_Year_2025/2025-Year3_Analysis/1-scRNA_RESULTS-19-11-2025/15-WES_Patient1_L1_L2_Analysis-19-11-2025/Exome_Patient_1/hg19_analysis/somatic_variants"
VIS_DIR <- "/home/bioinfo/1-Thesis_Final_Year_2025/2025-Year3_Analysis/1-scRNA_RESULTS-19-11-2025/15-WES_Patient1_L1_L2_Analysis-19-11-2025/Exome_Patient_1/hg19_analysis/visualizations"
extract_vaf <- function(vcf_file, sample_name) {
vcf <- read.vcfR(vcf_file, verbose = FALSE)
pass_vcf <- vcf[getFILTER(vcf) == "PASS", ]
gt_info <- extract.gt(pass_vcf)
ad <- extract.gt(pass_vcf, element = "AD")
vaf_values <- sapply(ad[, sample_name], function(x) {
if (is.na(x)) return(NA)
depths <- as.numeric(strsplit(x, ",")[[1]])
if (length(depths) < 2 || sum(depths) == 0) return(NA)
return(depths[2] / sum(depths))
})
return(data.frame(sample = sample_name, VAF = vaf_values))
}
# Extract VAF for all samples
vaf_data <- rbind(
extract_vaf(file.path(VCF_DIR, "T1.hg19.filtered.vcf.gz"), "T1"),
extract_vaf(file.path(VCF_DIR, "X1.hg19.filtered.vcf.gz"), "X1"),
extract_vaf(file.path(VCF_DIR, "XX1.hg19.filtered.vcf.gz"), "XX1")
)
vaf_data <- na.omit(vaf_data)
# Create violin plot with jitter
p <- ggplot(vaf_data, aes(x = sample, y = VAF, fill = sample)) +
geom_violin(alpha = 0.6) +
geom_jitter(width = 0.2, alpha = 0.4, size = 1) +
geom_hline(yintercept = 0.5, linetype = "dashed", color = "red") +
labs(
title = "Variant Allele Frequency Distribution",
subtitle = "Red line indicates clonal threshold (VAF = 0.5)",
x = "Sample",
y = "Variant Allele Frequency (VAF)"
) +
theme_bw() +
theme(legend.position = "none")
ggsave(file.path(VIS_DIR, "VAF_distribution.pdf"), p, width = 10, height = 6)
ggsave(file.path(VIS_DIR, "VAF_distribution.png"), p, width = 10, height = 6)
print(p)

Circos Plot for
CNV
#!/bin/bash
source /home/bioinfo/miniconda3/etc/profile.d/conda.sh
conda activate wes_env
BASE_DIR="/home/bioinfo/1-Thesis_Final_Year_2025/2025-Year3_Analysis/1-scRNA_RESULTS-19-11-2025/15-WES_Patient1_L1_L2_Analysis-19-11-2025/Exome_Patient_1"
CNV_DIR="$BASE_DIR/hg19_analysis/cnv_results"
VIS_DIR="$BASE_DIR/hg19_analysis/visualizations"
echo "--- Generating Circos plots for each sample ---"
for SAMPLE in T1 X1 XX1; do
cnvkit.py diagram \
"$CNV_DIR/$SAMPLE/${SAMPLE}.with_rg.cnr" \
-s "$CNV_DIR/$SAMPLE/${SAMPLE}.with_rg.cns" \
-o "$VIS_DIR/${SAMPLE}_circos.pdf"
done
echo "Circos plots saved to $VIS_DIR"
Mutational Signature
Analysis
library(deconstructSigs)
library(vcfR)
library(BSgenome.Hsapiens.UCSC.hg19)
library(ggplot2)
VCF_DIR <- "/home/bioinfo/1-Thesis_Final_Year_2025/2025-Year3_Analysis/1-scRNA_RESULTS-19-11-2025/15-WES_Patient1_L1_L2_Analysis-19-11-2025/Exome_Patient_1/hg19_analysis/somatic_variants"
VIS_DIR <- "/home/bioinfo/1-Thesis_Final_Year_2025/2025-Year3_Analysis/1-scRNA_RESULTS-19-11-2025/15-WES_Patient1_L1_L2_Analysis-19-11-2025/Exome_Patient_1/hg19_analysis/visualizations"
# Convert VCF to deconstructSigs format
vcf_to_sigs_input <- function(vcf_file, sample_name) {
message(paste("Processing", sample_name, "..."))
vcf <- read.vcfR(vcf_file, verbose = FALSE)
# CORRECTED: Use getFILTER() instead of getFIXED()
pass_vcf <- vcf[getFILTER(vcf) == "PASS", ]
message(paste(" Found", nrow(pass_vcf@fix), "PASS variants"))
df <- data.frame(
Sample = sample_name,
chr = getCHROM(pass_vcf),
pos = getPOS(pass_vcf),
ref = getREF(pass_vcf),
alt = getALT(pass_vcf),
stringsAsFactors = FALSE
)
# Filter to only SNVs (single nucleotide variants)
df <- df[nchar(df$ref) == 1 & nchar(df$alt) == 1, ]
message(paste(" Kept", nrow(df), "SNVs for signature analysis"))
return(df)
}
# Load all mutations
message("Loading mutations from VCF files...")
all_muts <- rbind(
vcf_to_sigs_input(file.path(VCF_DIR, "T1.hg19.filtered.vcf.gz"), "T1"),
vcf_to_sigs_input(file.path(VCF_DIR, "X1.hg19.filtered.vcf.gz"), "X1"),
vcf_to_sigs_input(file.path(VCF_DIR, "XX1.hg19.filtered.vcf.gz"), "XX1")
)
message(paste("Total mutations:", nrow(all_muts)))
# Convert to trinucleotide context
message("Computing trinucleotide contexts from hg19 reference...")
sigs_input <- mut.to.sigs.input(
mut.ref = all_muts,
sample.id = "Sample",
chr = "chr",
pos = "pos",
ref = "ref",
alt = "alt",
bsg = BSgenome.Hsapiens.UCSC.hg19
)
message("Trinucleotide matrix dimensions:")
print(dim(sigs_input))
# Determine signatures for each sample
message("Performing signature decomposition...")
pdf(file.path(VIS_DIR, "Mutational_Signatures.pdf"), width = 12, height = 8)
for (sample in c("T1", "X1", "XX1")) {
message(paste(" Analyzing", sample))
output <- whichSignatures(
tumor.ref = sigs_input,
signatures.ref = signatures.cosmic,
sample.id = sample,
contexts.needed = TRUE,
tri.counts.method = "default"
)
plotSignatures(output, sub = paste("Sample:", sample))
makePie(output, sub = paste("Sample:", sample))
}
dev.off()
message("✓ Mutational signature analysis complete!")
message(paste("Results saved to:", file.path(VIS_DIR, "Mutational_Signatures.pdf")))
Ti/Tv Ratio
Analysis
library(vcfR)
library(ggplot2)
VCF_DIR <- "/home/bioinfo/1-Thesis_Final_Year_2025/2025-Year3_Analysis/1-scRNA_RESULTS-19-11-2025/15-WES_Patient1_L1_L2_Analysis-19-11-2025/Exome_Patient_1/hg19_analysis/somatic_variants"
VIS_DIR <- "/home/bioinfo/1-Thesis_Final_Year_2025/2025-Year3_Analysis/1-scRNA_RESULTS-19-11-2025/15-WES_Patient1_L1_L2_Analysis-19-11-2025/Exome_Patient_1/hg19_analysis/visualizations"
calculate_titv <- function(vcf_file, sample_name) {
vcf <- read.vcfR(vcf_file, verbose = FALSE)
pass_vcf <- vcf[getFILTER(vcf) == "PASS", ]
ref <- getREF(pass_vcf)
alt <- getALT(pass_vcf)
# Only keep SNVs
snv_idx <- nchar(ref) == 1 & nchar(alt) == 1
ref <- ref[snv_idx]
alt <- alt[snv_idx]
# Classify transitions and transversions
transitions <- c("AG", "GA", "CT", "TC")
transversions <- c("AC", "CA", "AT", "TA", "GC", "CG", "GT", "TG")
change <- paste0(ref, alt)
ti_count <- sum(change %in% transitions)
tv_count <- sum(change %in% transversions)
return(data.frame(
sample = sample_name,
Transitions = ti_count,
Transversions = tv_count,
TiTv_ratio = ti_count / tv_count
))
}
titv_data <- rbind(
calculate_titv(file.path(VCF_DIR, "T1.hg19.filtered.vcf.gz"), "T1"),
calculate_titv(file.path(VCF_DIR, "X1.hg19.filtered.vcf.gz"), "X1"),
calculate_titv(file.path(VCF_DIR, "XX1.hg19.filtered.vcf.gz"), "XX1")
)
# Plot
p <- ggplot(titv_data, aes(x = sample, y = TiTv_ratio, fill = sample)) +
geom_bar(stat = "identity") +
geom_hline(yintercept = 2, linetype = "dashed", color = "red") +
geom_text(aes(label = round(TiTv_ratio, 2)), vjust = -0.5) +
labs(
title = "Transition/Transversion Ratio",
subtitle = "Red line indicates expected WES ratio (~2.0)",
x = "Sample",
y = "Ti/Tv Ratio"
) +
theme_bw() +
theme(legend.position = "none")
print(p)
ggsave(file.path(VIS_DIR, "TiTv_ratio.pdf"), p, width = 8, height = 6)

Clonal Evolution Tree
(Phylogenetic)
library(vcfR)
library(ape)
library(phangorn)
VCF_DIR <- "/home/bioinfo/1-Thesis_Final_Year_2025/2025-Year3_Analysis/1-scRNA_RESULTS-19-11-2025/15-WES_Patient1_L1_L2_Analysis-19-11-2025/Exome_Patient_1/hg19_analysis/somatic_variants"
VIS_DIR <- "/home/bioinfo/1-Thesis_Final_Year_2025/2025-Year3_Analysis/1-scRNA_RESULTS-19-11-2025/15-WES_Patient1_L1_L2_Analysis-19-11-2025/Exome_Patient_1/hg19_analysis/visualizations"
# Create binary mutation matrix (present=1, absent=0)
create_mutation_matrix <- function(vcf_files, sample_names) {
all_variants <- list()
for (i in seq_along(vcf_files)) {
vcf <- read.vcfR(vcf_files[i], verbose = FALSE)
pass_vcf <- vcf[getFILTER(vcf) == "PASS", ]
variants <- paste(getCHROM(pass_vcf), getPOS(pass_vcf), sep = ":")
all_variants[[sample_names[i]]] <- variants
}
# Get all unique variants
unique_variants <- unique(unlist(all_variants))
# Create binary matrix
mat <- matrix(0, nrow = length(sample_names), ncol = length(unique_variants))
rownames(mat) <- sample_names
colnames(mat) <- unique_variants
for (i in seq_along(sample_names)) {
mat[i, colnames(mat) %in% all_variants[[sample_names[i]]]] <- 1
}
return(mat)
}
# Create matrix
vcf_files <- c(
file.path(VCF_DIR, "T1.hg19.filtered.vcf.gz"),
file.path(VCF_DIR, "X1.hg19.filtered.vcf.gz"),
file.path(VCF_DIR, "XX1.hg19.filtered.vcf.gz")
)
mutation_matrix <- create_mutation_matrix(vcf_files, c("T1", "X1", "XX1"))
# Calculate distance and build tree
dist_matrix <- dist(mutation_matrix, method = "binary")
tree <- nj(dist_matrix)
# Root the tree with Normal (N1 as outgroup conceptually)
# Plot
pdf(file.path(VIS_DIR, "Clonal_Evolution_Tree.pdf"), width = 10, height = 8)
plot(tree, type = "phylogram", edge.width = 2,
main = "Clonal Evolution: T1 → X1 → XX1",
cex = 1.5)
add.scale.bar()
dev.off()
tree
library(vcfR)
library(ape)
library(phangorn)
VCF_DIR <- "/home/bioinfo/1-Thesis_Final_Year_2025/2025-Year3_Analysis/1-scRNA_RESULTS-19-11-2025/15-WES_Patient1_L1_L2_Analysis-19-11-2025/Exome_Patient_1/hg19_analysis/somatic_variants"
VIS_DIR <- "/home/bioinfo/1-Thesis_Final_Year_2025/2025-Year3_Analysis/1-scRNA_RESULTS-19-11-2025/15-WES_Patient1_L1_L2_Analysis-19-11-2025/Exome_Patient_1/hg19_analysis/visualizations"
# Create binary mutation matrix (present=1, absent=0)
create_mutation_matrix <- function(vcf_files, sample_names) {
all_variants <- list()
message("Loading variants from VCF files...")
for (i in seq_along(vcf_files)) {
vcf <- read.vcfR(vcf_files[i], verbose = FALSE)
pass_vcf <- vcf[getFILTER(vcf) == "PASS", ]
# Use chr:pos:ref:alt as unique identifier
variants <- paste(getCHROM(pass_vcf), getPOS(pass_vcf),
getREF(pass_vcf), getALT(pass_vcf), sep = ":")
all_variants[[sample_names[i]]] <- variants
message(paste(" ", sample_names[i], ":", length(variants), "somatic mutations"))
}
# Get all unique variants across all samples
unique_variants <- unique(unlist(all_variants))
message(paste("Total unique somatic variants:", length(unique_variants)))
# Create binary matrix
mat <- matrix(0, nrow = length(sample_names), ncol = length(unique_variants))
rownames(mat) <- sample_names
colnames(mat) <- unique_variants
for (i in seq_along(sample_names)) {
mat[i, colnames(mat) %in% all_variants[[sample_names[i]]]] <- 1
}
return(list(matrix = mat, variants = all_variants))
}
# Load mutation data
vcf_files <- c(
file.path(VCF_DIR, "T1.hg19.filtered.vcf.gz"),
file.path(VCF_DIR, "X1.hg19.filtered.vcf.gz"),
file.path(VCF_DIR, "XX1.hg19.filtered.vcf.gz")
)
sample_names <- c("T1", "X1", "XX1")
result <- create_mutation_matrix(vcf_files, sample_names)
mutation_matrix <- result$matrix
all_variants <- result$variants
# Add N1 (Normal) as all zeros - representing germline state (no somatic mutations)
mutation_matrix_with_normal <- rbind(
N1 = rep(0, ncol(mutation_matrix)), # N1 has ZERO somatic mutations by definition
mutation_matrix
)
message("\nMutation matrix summary:")
print(rowSums(mutation_matrix_with_normal))
# Calculate shared mutations
shared_T1_X1 <- sum(mutation_matrix["T1", ] == 1 & mutation_matrix["X1", ] == 1)
shared_X1_XX1 <- sum(mutation_matrix["X1", ] == 1 & mutation_matrix["XX1", ] == 1)
shared_T1_XX1 <- sum(mutation_matrix["T1", ] == 1 & mutation_matrix["XX1", ] == 1)
shared_all <- sum(mutation_matrix["T1", ] == 1 &
mutation_matrix["X1", ] == 1 &
mutation_matrix["XX1", ] == 1)
message("\nShared mutations analysis:")
message(paste(" T1 ∩ X1:", shared_T1_X1))
message(paste(" X1 ∩ XX1:", shared_X1_XX1))
message(paste(" T1 ∩ XX1:", shared_T1_XX1))
message(paste(" All three (T1 ∩ X1 ∩ XX1):", shared_all, "- TRUNK mutations"))
# Private mutations
private_T1 <- sum(mutation_matrix["T1", ] == 1 &
mutation_matrix["X1", ] == 0 &
mutation_matrix["XX1", ] == 0)
private_X1 <- sum(mutation_matrix["X1", ] == 1 &
mutation_matrix["T1", ] == 0 &
mutation_matrix["XX1", ] == 0)
private_XX1 <- sum(mutation_matrix["XX1", ] == 1 &
mutation_matrix["T1", ] == 0 &
mutation_matrix["X1", ] == 0)
message("\nPrivate mutations:")
message(paste(" T1 only:", private_T1))
message(paste(" X1 only:", private_X1))
message(paste(" XX1 only:", private_XX1))
# Calculate distance matrix
dist_matrix <- dist(mutation_matrix_with_normal, method = "binary")
# Build neighbor-joining tree
tree <- nj(dist_matrix)
# Root at N1
rooted_tree <- root(tree, outgroup = "N1", resolve.root = TRUE)
# Create better plot
pdf(file.path(VIS_DIR, "Clonal_Evolution_Tree_Rooted.pdf"), width = 12, height = 10)
par(mar = c(5, 4, 4, 2) + 0.1)
plot(rooted_tree,
type = "phylogram",
direction = "rightwards",
edge.width = 4,
cex = 1.8,
font = 2,
tip.color = c("black", "red", "blue", "darkgreen"),
edge.color = "grey30",
main = "Clonal Evolution: N1 (Germline) → T1 → X1 → XX1",
cex.main = 1.5)
# Add node point at root
nodelabels(text = "Root",
node = length(rooted_tree$tip.label) + 1,
frame = "circle",
bg = "lightblue",
cex = 1.2,
font = 2)
# Add scale bar
add.scale.bar(x = 0, y = 1, cex = 1.2, lwd = 2)
mtext("Genetic Distance (Binary)", side = 1, line = 3, cex = 0.9)
# Add legend with mutation counts
# Change legend position to bottom right
legend("bottomright",
legend = c(
paste0("N1 (Normal): 0 somatic"),
paste0("T1 (Primary): ", sum(mutation_matrix["T1", ]), " somatic"),
paste0("X1 (In-vivo): ", sum(mutation_matrix["X1", ]), " somatic"),
paste0("XX1 (Secondary): ", sum(mutation_matrix["XX1", ]), " somatic")
),
col = c("black", "red", "blue", "darkgreen"),
pch = 19,
cex = 1.1,
bty = "n",
pt.cex = 2)
# Add text box with shared mutation info
text(x = max(rooted_tree$edge[,2]) * 0.6,
y = 0.5,
labels = paste0(
"Trunk mutations (all 3): ", shared_all, "\n",
"T1-X1 shared: ", shared_T1_X1, "\n",
"X1-XX1 shared: ", shared_X1_XX1
),
cex = 1,
adj = 0,
font = 2,
col = "darkred")
dev.off()
message("\n✓ Rooted phylogenetic tree created successfully!")
message(paste("Output:", file.path(VIS_DIR, "Clonal_Evolution_Tree_Rooted.pdf")))
# Print tree structure
print(rooted_tree)
Pathway Enrichment
Bubble Plot

CNV-Mutation
Integration Plot

Python Alternative for
Custom Multi-Panel Plot
library(ggplot2)
library(dplyr)
library(patchwork)
library(vcfR)
BASE_DIR <- "/home/bioinfo/1-Thesis_Final_Year_2025/2025-Year3_Analysis/1-scRNA_RESULTS-19-11-2025/15-WES_Patient1_L1_L2_Analysis-19-11-2025/Exome_Patient_1"
CNV_DIR <- paste0(BASE_DIR, "/hg19_analysis/cnv_results")
VCF_DIR <- paste0(BASE_DIR, "/hg19_analysis/somatic_variants")
VIS_DIR <- paste0(BASE_DIR, "/hg19_analysis/visualizations")
create_comprehensive_plot <- function(sample_id) {
# Load CNV data
cnr_file <- file.path(CNV_DIR, sample_id, paste0(sample_id, ".with_rg.cnr"))
cns_file <- file.path(CNV_DIR, sample_id, paste0(sample_id, ".with_rg.cns"))
cnr_data <- read.delim(cnr_file, header = TRUE)
cns_data <- read.delim(cns_file, header = TRUE)
# Prepare chromosome order
cnr_data$chromosome <- factor(cnr_data$chromosome,
levels = paste0("chr", c(1:22, "X", "Y")))
cns_data$chromosome <- factor(cns_data$chromosome,
levels = paste0("chr", c(1:22, "X", "Y")))
# Panel 1: Coverage depth
p1 <- ggplot(cnr_data, aes(x = start, y = depth, color = chromosome)) +
geom_point(size = 0.3, alpha = 0.5) +
facet_grid(. ~ chromosome, scales = "free_x", space = "free_x") +
labs(title = paste(sample_id, "- Coverage Depth"), y = "Coverage") +
theme_minimal() +
theme(
axis.text.x = element_blank(),
axis.ticks.x = element_blank(),
legend.position = "none",
panel.spacing = unit(0.1, "lines")
)
# Panel 2: VAF (from VCF)
vcf_file <- file.path(VCF_DIR, paste0(sample_id, ".hg19.filtered.vcf.gz"))
vcf <- read.vcfR(vcf_file, verbose = FALSE)
# Extract VAF
ad <- extract.gt(vcf, element = "AD")
vaf <- sapply(ad[, sample_id], function(x) {
if (is.na(x)) return(NA)
depths <- as.numeric(strsplit(x, ",")[[1]])
if (length(depths) < 2) return(NA)
return(depths[2] / sum(depths))
})
vaf_data <- data.frame(
chromosome = factor(getCHROM(vcf), levels = paste0("chr", c(1:22, "X", "Y"))),
position = getPOS(vcf),
VAF = vaf
)
p2 <- ggplot(vaf_data, aes(x = position, y = VAF, color = chromosome)) +
geom_point(size = 0.5, alpha = 0.6) +
facet_grid(. ~ chromosome, scales = "free_x", space = "free_x") +
labs(y = "VAF") +
ylim(0, 1) +
theme_minimal() +
theme(
axis.text.x = element_blank(),
axis.ticks.x = element_blank(),
legend.position = "none",
panel.spacing = unit(0.1, "lines")
)
# Panel 3: Copy ratio (log2)
p3 <- ggplot(cnr_data, aes(x = start, y = log2, color = chromosome)) +
geom_point(size = 0.3, alpha = 0.3) +
geom_segment(data = cns_data,
aes(x = start, xend = end, y = log2, yend = log2),
color = "red", linewidth = 1) +
facet_grid(. ~ chromosome, scales = "free_x", space = "free_x") +
geom_hline(yintercept = 0, linetype = "dashed", color = "black") +
labs(y = "Copy ratio (log2)") +
theme_minimal() +
theme(
axis.text.x = element_blank(),
axis.ticks.x = element_blank(),
legend.position = "none",
panel.spacing = unit(0.1, "lines")
)
# Panel 4: Tumor fraction (from segments)
# Calculate estimated tumor fraction per segment
cns_data$tumor_fraction <- pmin(abs(cns_data$log2) * 0.5, 1)
p4 <- ggplot(cns_data, aes(x = start, xend = end, y = tumor_fraction, yend = tumor_fraction)) +
geom_segment(aes(color = chromosome), linewidth = 2) +
facet_grid(. ~ chromosome, scales = "free_x", space = "free_x") +
labs(y = "Tumor fraction", x = "Chromosome") +
ylim(0, 1) +
theme_minimal() +
theme(
legend.position = "none",
panel.spacing = unit(0.1, "lines")
)
# Combine all panels
combined_plot <- p1 / p2 / p3 / p4 +
plot_annotation(
title = paste("Comprehensive Genomic Profile -", sample_id),
theme = theme(plot.title = element_text(size = 16, face = "bold"))
)
# Save
ggsave(
file.path(VIS_DIR, paste0(sample_id, "_comprehensive_profile.pdf")),
combined_plot,
width = 16,
height = 12
)
return(combined_plot)
}
# Generate for all samples
for (sample in c("T1", "X1", "XX1")) {
print(create_comprehensive_plot(sample))
}



NA
NA
LS0tCnRpdGxlOiAiV0VTIEFuYWx5c2lzIGZvciBQYXRpZW50IDEgQ05WIFZpc3VhbGl6YXRpb25zIGFuZCBTb21hdGljIE11dGF0aW9ucyIKYXV0aG9yOiAiTmFzaXIgTWFobW9vZCBBYmJhc2kiCmRhdGU6ICJgciBmb3JtYXQoU3lzLnRpbWUoKSwgJyVCICVkLCAlWScpYCIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OgogICAgICBjb2xsYXBzZWQ6IHRydWUKICAgIHRoZW1lOiBqb3VybmFsCi0tLQoKIyBJbnRyb2R1Y3Rpb24KCioqU2FtcGxlIEluZm9ybWF0aW9uOioqCiogICAqKk4xOioqIE5vcm1hbCAoQ29uc3RpdHV0aW9uYWwgRE5BKQoqICAgKipUMToqKiBQcmltYXJ5IFR1bW9yIChGcmVzaCBTw6l6YXJ5IENlbGxzKQoqICAgKipYMToqKiBJbi12aXZvIGRlcml2ZWQgQ2VsbCBMaW5lIChmcm9tIE1vdXNlKQoqICAgKipYWDE6KiogU2Vjb25kYXJ5IENlbGwgTGluZSAoY3VsdHVyZWQgZnJvbSBYMSkKCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0KIyBTZXQgZGVmYXVsdCBvcHRpb25zIGZvciBjb2RlIGNodW5rcwprbml0cjo6b3B0c19jaHVuayRzZXQoCiAgZWNobyA9IFRSVUUsCiAgZXZhbCA9IEZBTFNFLCAjIElNUE9SVEFOVDogU2V0IHRvIEZBTFNFIHRvIHByZXZlbnQgYWNjaWRlbnRhbCBleGVjdXRpb24uCiAgZW5naW5lID0gImJhc2giCikKYGBgCgoKIyBWYXJpYW50IEFsbGVsZSBGcmVxdWVuY3kgKFZBRikgRGlzdHJpYnV0aW9uIFBsb3QKCmBgYHtyIFZBRiwgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBldmFsPVRSVUV9CmxpYnJhcnkodmNmUikKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHRpZHlyKQoKIyBEZWZpbmUgcGF0aHMKVkNGX0RJUiA8LSAiL2hvbWUvYmlvaW5mby8xLVRoZXNpc19GaW5hbF9ZZWFyXzIwMjUvMjAyNS1ZZWFyM19BbmFseXNpcy8xLXNjUk5BX1JFU1VMVFMtMTktMTEtMjAyNS8xNS1XRVNfUGF0aWVudDFfTDFfTDJfQW5hbHlzaXMtMTktMTEtMjAyNS9FeG9tZV9QYXRpZW50XzEvaGcxOV9hbmFseXNpcy9zb21hdGljX3ZhcmlhbnRzIgpWSVNfRElSIDwtICIvaG9tZS9iaW9pbmZvLzEtVGhlc2lzX0ZpbmFsX1llYXJfMjAyNS8yMDI1LVllYXIzX0FuYWx5c2lzLzEtc2NSTkFfUkVTVUxUUy0xOS0xMS0yMDI1LzE1LVdFU19QYXRpZW50MV9MMV9MMl9BbmFseXNpcy0xOS0xMS0yMDI1L0V4b21lX1BhdGllbnRfMS9oZzE5X2FuYWx5c2lzL3Zpc3VhbGl6YXRpb25zIgoKZXh0cmFjdF92YWYgPC0gZnVuY3Rpb24odmNmX2ZpbGUsIHNhbXBsZV9uYW1lKSB7CiAgdmNmIDwtIHJlYWQudmNmUih2Y2ZfZmlsZSwgdmVyYm9zZSA9IEZBTFNFKQogIHBhc3NfdmNmIDwtIHZjZltnZXRGSUxURVIodmNmKSA9PSAiUEFTUyIsIF0KICAKICBndF9pbmZvIDwtIGV4dHJhY3QuZ3QocGFzc192Y2YpCiAgYWQgPC0gZXh0cmFjdC5ndChwYXNzX3ZjZiwgZWxlbWVudCA9ICJBRCIpCiAgCiAgdmFmX3ZhbHVlcyA8LSBzYXBwbHkoYWRbLCBzYW1wbGVfbmFtZV0sIGZ1bmN0aW9uKHgpIHsKICAgIGlmIChpcy5uYSh4KSkgcmV0dXJuKE5BKQogICAgZGVwdGhzIDwtIGFzLm51bWVyaWMoc3Ryc3BsaXQoeCwgIiwiKVtbMV1dKQogICAgaWYgKGxlbmd0aChkZXB0aHMpIDwgMiB8fCBzdW0oZGVwdGhzKSA9PSAwKSByZXR1cm4oTkEpCiAgICByZXR1cm4oZGVwdGhzWzJdIC8gc3VtKGRlcHRocykpCiAgfSkKICAKICByZXR1cm4oZGF0YS5mcmFtZShzYW1wbGUgPSBzYW1wbGVfbmFtZSwgVkFGID0gdmFmX3ZhbHVlcykpCn0KCiMgRXh0cmFjdCBWQUYgZm9yIGFsbCBzYW1wbGVzCnZhZl9kYXRhIDwtIHJiaW5kKAogIGV4dHJhY3RfdmFmKGZpbGUucGF0aChWQ0ZfRElSLCAiVDEuaGcxOS5maWx0ZXJlZC52Y2YuZ3oiKSwgIlQxIiksCiAgZXh0cmFjdF92YWYoZmlsZS5wYXRoKFZDRl9ESVIsICJYMS5oZzE5LmZpbHRlcmVkLnZjZi5neiIpLCAiWDEiKSwKICBleHRyYWN0X3ZhZihmaWxlLnBhdGgoVkNGX0RJUiwgIlhYMS5oZzE5LmZpbHRlcmVkLnZjZi5neiIpLCAiWFgxIikKKQoKdmFmX2RhdGEgPC0gbmEub21pdCh2YWZfZGF0YSkKCiMgQ3JlYXRlIHZpb2xpbiBwbG90IHdpdGggaml0dGVyCnAgPC0gZ2dwbG90KHZhZl9kYXRhLCBhZXMoeCA9IHNhbXBsZSwgeSA9IFZBRiwgZmlsbCA9IHNhbXBsZSkpICsKICBnZW9tX3Zpb2xpbihhbHBoYSA9IDAuNikgKwogIGdlb21faml0dGVyKHdpZHRoID0gMC4yLCBhbHBoYSA9IDAuNCwgc2l6ZSA9IDEpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLjUsIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gInJlZCIpICsKICBsYWJzKAogICAgdGl0bGUgPSAiVmFyaWFudCBBbGxlbGUgRnJlcXVlbmN5IERpc3RyaWJ1dGlvbiIsCiAgICBzdWJ0aXRsZSA9ICJSZWQgbGluZSBpbmRpY2F0ZXMgY2xvbmFsIHRocmVzaG9sZCAoVkFGID0gMC41KSIsCiAgICB4ID0gIlNhbXBsZSIsCiAgICB5ID0gIlZhcmlhbnQgQWxsZWxlIEZyZXF1ZW5jeSAoVkFGKSIKICApICsKICB0aGVtZV9idygpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgpnZ3NhdmUoZmlsZS5wYXRoKFZJU19ESVIsICJWQUZfZGlzdHJpYnV0aW9uLnBkZiIpLCBwLCB3aWR0aCA9IDEwLCBoZWlnaHQgPSA2KQpnZ3NhdmUoZmlsZS5wYXRoKFZJU19ESVIsICJWQUZfZGlzdHJpYnV0aW9uLnBuZyIpLCBwLCB3aWR0aCA9IDEwLCBoZWlnaHQgPSA2KQoKCnByaW50KHApCmBgYAoKIyBDaXJjb3MgUGxvdCBmb3IgQ05WCgpgYGB7YmFzaCwgY2h1bmtfbmFtZT0iQ2lyY29zX3Bsb3RfY252In0KIyEvYmluL2Jhc2gKc291cmNlIC9ob21lL2Jpb2luZm8vbWluaWNvbmRhMy9ldGMvcHJvZmlsZS5kL2NvbmRhLnNoCmNvbmRhIGFjdGl2YXRlIHdlc19lbnYKCkJBU0VfRElSPSIvaG9tZS9iaW9pbmZvLzEtVGhlc2lzX0ZpbmFsX1llYXJfMjAyNS8yMDI1LVllYXIzX0FuYWx5c2lzLzEtc2NSTkFfUkVTVUxUUy0xOS0xMS0yMDI1LzE1LVdFU19QYXRpZW50MV9MMV9MMl9BbmFseXNpcy0xOS0xMS0yMDI1L0V4b21lX1BhdGllbnRfMSIKQ05WX0RJUj0iJEJBU0VfRElSL2hnMTlfYW5hbHlzaXMvY252X3Jlc3VsdHMiClZJU19ESVI9IiRCQVNFX0RJUi9oZzE5X2FuYWx5c2lzL3Zpc3VhbGl6YXRpb25zIgoKZWNobyAiLS0tIEdlbmVyYXRpbmcgQ2lyY29zIHBsb3RzIGZvciBlYWNoIHNhbXBsZSAtLS0iCgpmb3IgU0FNUExFIGluIFQxIFgxIFhYMTsgZG8KICBjbnZraXQucHkgZGlhZ3JhbSBcCiAgICAiJENOVl9ESVIvJFNBTVBMRS8ke1NBTVBMRX0ud2l0aF9yZy5jbnIiIFwKICAgIC1zICIkQ05WX0RJUi8kU0FNUExFLyR7U0FNUExFfS53aXRoX3JnLmNucyIgXAogICAgLW8gIiRWSVNfRElSLyR7U0FNUExFfV9jaXJjb3MucGRmIgpkb25lCgplY2hvICJDaXJjb3MgcGxvdHMgc2F2ZWQgdG8gJFZJU19ESVIiCgpgYGAKCiMgTXV0YXRpb25hbCBTaWduYXR1cmUgQW5hbHlzaXMKCmBgYHtyLCBjaHVua19uYW1lPSJNdXRhdGlvbmFsX1NpZ25hdHVyZSJ9CmxpYnJhcnkoZGVjb25zdHJ1Y3RTaWdzKQpsaWJyYXJ5KHZjZlIpCmxpYnJhcnkoQlNnZW5vbWUuSHNhcGllbnMuVUNTQy5oZzE5KQpsaWJyYXJ5KGdncGxvdDIpCgpWQ0ZfRElSIDwtICIvaG9tZS9iaW9pbmZvLzEtVGhlc2lzX0ZpbmFsX1llYXJfMjAyNS8yMDI1LVllYXIzX0FuYWx5c2lzLzEtc2NSTkFfUkVTVUxUUy0xOS0xMS0yMDI1LzE1LVdFU19QYXRpZW50MV9MMV9MMl9BbmFseXNpcy0xOS0xMS0yMDI1L0V4b21lX1BhdGllbnRfMS9oZzE5X2FuYWx5c2lzL3NvbWF0aWNfdmFyaWFudHMiClZJU19ESVIgPC0gIi9ob21lL2Jpb2luZm8vMS1UaGVzaXNfRmluYWxfWWVhcl8yMDI1LzIwMjUtWWVhcjNfQW5hbHlzaXMvMS1zY1JOQV9SRVNVTFRTLTE5LTExLTIwMjUvMTUtV0VTX1BhdGllbnQxX0wxX0wyX0FuYWx5c2lzLTE5LTExLTIwMjUvRXhvbWVfUGF0aWVudF8xL2hnMTlfYW5hbHlzaXMvdmlzdWFsaXphdGlvbnMiCgojIENvbnZlcnQgVkNGIHRvIGRlY29uc3RydWN0U2lncyBmb3JtYXQKdmNmX3RvX3NpZ3NfaW5wdXQgPC0gZnVuY3Rpb24odmNmX2ZpbGUsIHNhbXBsZV9uYW1lKSB7CiAgbWVzc2FnZShwYXN0ZSgiUHJvY2Vzc2luZyIsIHNhbXBsZV9uYW1lLCAiLi4uIikpCiAgdmNmIDwtIHJlYWQudmNmUih2Y2ZfZmlsZSwgdmVyYm9zZSA9IEZBTFNFKQogIAogICMgQ09SUkVDVEVEOiBVc2UgZ2V0RklMVEVSKCkgaW5zdGVhZCBvZiBnZXRGSVhFRCgpCiAgcGFzc192Y2YgPC0gdmNmW2dldEZJTFRFUih2Y2YpID09ICJQQVNTIiwgXQogIAogIG1lc3NhZ2UocGFzdGUoIiAgRm91bmQiLCBucm93KHBhc3NfdmNmQGZpeCksICJQQVNTIHZhcmlhbnRzIikpCiAgCiAgZGYgPC0gZGF0YS5mcmFtZSgKICAgIFNhbXBsZSA9IHNhbXBsZV9uYW1lLAogICAgY2hyID0gZ2V0Q0hST00ocGFzc192Y2YpLAogICAgcG9zID0gZ2V0UE9TKHBhc3NfdmNmKSwKICAgIHJlZiA9IGdldFJFRihwYXNzX3ZjZiksCiAgICBhbHQgPSBnZXRBTFQocGFzc192Y2YpLAogICAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFCiAgKQogIAogICMgRmlsdGVyIHRvIG9ubHkgU05WcyAoc2luZ2xlIG51Y2xlb3RpZGUgdmFyaWFudHMpCiAgZGYgPC0gZGZbbmNoYXIoZGYkcmVmKSA9PSAxICYgbmNoYXIoZGYkYWx0KSA9PSAxLCBdCiAgbWVzc2FnZShwYXN0ZSgiICBLZXB0IiwgbnJvdyhkZiksICJTTlZzIGZvciBzaWduYXR1cmUgYW5hbHlzaXMiKSkKICAKICByZXR1cm4oZGYpCn0KCiMgTG9hZCBhbGwgbXV0YXRpb25zCm1lc3NhZ2UoIkxvYWRpbmcgbXV0YXRpb25zIGZyb20gVkNGIGZpbGVzLi4uIikKYWxsX211dHMgPC0gcmJpbmQoCiAgdmNmX3RvX3NpZ3NfaW5wdXQoZmlsZS5wYXRoKFZDRl9ESVIsICJUMS5oZzE5LmZpbHRlcmVkLnZjZi5neiIpLCAiVDEiKSwKICB2Y2ZfdG9fc2lnc19pbnB1dChmaWxlLnBhdGgoVkNGX0RJUiwgIlgxLmhnMTkuZmlsdGVyZWQudmNmLmd6IiksICJYMSIpLAogIHZjZl90b19zaWdzX2lucHV0KGZpbGUucGF0aChWQ0ZfRElSLCAiWFgxLmhnMTkuZmlsdGVyZWQudmNmLmd6IiksICJYWDEiKQopCgptZXNzYWdlKHBhc3RlKCJUb3RhbCBtdXRhdGlvbnM6IiwgbnJvdyhhbGxfbXV0cykpKQoKIyBDb252ZXJ0IHRvIHRyaW51Y2xlb3RpZGUgY29udGV4dAptZXNzYWdlKCJDb21wdXRpbmcgdHJpbnVjbGVvdGlkZSBjb250ZXh0cyBmcm9tIGhnMTkgcmVmZXJlbmNlLi4uIikKc2lnc19pbnB1dCA8LSBtdXQudG8uc2lncy5pbnB1dCgKICBtdXQucmVmID0gYWxsX211dHMsCiAgc2FtcGxlLmlkID0gIlNhbXBsZSIsCiAgY2hyID0gImNociIsCiAgcG9zID0gInBvcyIsCiAgcmVmID0gInJlZiIsCiAgYWx0ID0gImFsdCIsCiAgYnNnID0gQlNnZW5vbWUuSHNhcGllbnMuVUNTQy5oZzE5CikKCm1lc3NhZ2UoIlRyaW51Y2xlb3RpZGUgbWF0cml4IGRpbWVuc2lvbnM6IikKcHJpbnQoZGltKHNpZ3NfaW5wdXQpKQoKIyBEZXRlcm1pbmUgc2lnbmF0dXJlcyBmb3IgZWFjaCBzYW1wbGUKbWVzc2FnZSgiUGVyZm9ybWluZyBzaWduYXR1cmUgZGVjb21wb3NpdGlvbi4uLiIpCnBkZihmaWxlLnBhdGgoVklTX0RJUiwgIk11dGF0aW9uYWxfU2lnbmF0dXJlcy5wZGYiKSwgd2lkdGggPSAxMiwgaGVpZ2h0ID0gOCkKCmZvciAoc2FtcGxlIGluIGMoIlQxIiwgIlgxIiwgIlhYMSIpKSB7CiAgbWVzc2FnZShwYXN0ZSgiICBBbmFseXppbmciLCBzYW1wbGUpKQogIAogIG91dHB1dCA8LSB3aGljaFNpZ25hdHVyZXMoCiAgICB0dW1vci5yZWYgPSBzaWdzX2lucHV0LAogICAgc2lnbmF0dXJlcy5yZWYgPSBzaWduYXR1cmVzLmNvc21pYywKICAgIHNhbXBsZS5pZCA9IHNhbXBsZSwKICAgIGNvbnRleHRzLm5lZWRlZCA9IFRSVUUsCiAgICB0cmkuY291bnRzLm1ldGhvZCA9ICJkZWZhdWx0IgogICkKICAKICBwbG90U2lnbmF0dXJlcyhvdXRwdXQsIHN1YiA9IHBhc3RlKCJTYW1wbGU6Iiwgc2FtcGxlKSkKICBtYWtlUGllKG91dHB1dCwgc3ViID0gcGFzdGUoIlNhbXBsZToiLCBzYW1wbGUpKQp9CgpkZXYub2ZmKCkKCm1lc3NhZ2UoIuKckyBNdXRhdGlvbmFsIHNpZ25hdHVyZSBhbmFseXNpcyBjb21wbGV0ZSEiKQptZXNzYWdlKHBhc3RlKCJSZXN1bHRzIHNhdmVkIHRvOiIsIGZpbGUucGF0aChWSVNfRElSLCAiTXV0YXRpb25hbF9TaWduYXR1cmVzLnBkZiIpKSkKCmBgYAoKCgojIFRpL1R2IFJhdGlvIEFuYWx5c2lzCgpgYGB7ciwgY2h1bmtfbmFtZT0iVGkvVHYiLCBldmFsPVRSVUV9CgpsaWJyYXJ5KHZjZlIpCmxpYnJhcnkoZ2dwbG90MikKClZDRl9ESVIgPC0gIi9ob21lL2Jpb2luZm8vMS1UaGVzaXNfRmluYWxfWWVhcl8yMDI1LzIwMjUtWWVhcjNfQW5hbHlzaXMvMS1zY1JOQV9SRVNVTFRTLTE5LTExLTIwMjUvMTUtV0VTX1BhdGllbnQxX0wxX0wyX0FuYWx5c2lzLTE5LTExLTIwMjUvRXhvbWVfUGF0aWVudF8xL2hnMTlfYW5hbHlzaXMvc29tYXRpY192YXJpYW50cyIKVklTX0RJUiA8LSAiL2hvbWUvYmlvaW5mby8xLVRoZXNpc19GaW5hbF9ZZWFyXzIwMjUvMjAyNS1ZZWFyM19BbmFseXNpcy8xLXNjUk5BX1JFU1VMVFMtMTktMTEtMjAyNS8xNS1XRVNfUGF0aWVudDFfTDFfTDJfQW5hbHlzaXMtMTktMTEtMjAyNS9FeG9tZV9QYXRpZW50XzEvaGcxOV9hbmFseXNpcy92aXN1YWxpemF0aW9ucyIKCmNhbGN1bGF0ZV90aXR2IDwtIGZ1bmN0aW9uKHZjZl9maWxlLCBzYW1wbGVfbmFtZSkgewogIHZjZiA8LSByZWFkLnZjZlIodmNmX2ZpbGUsIHZlcmJvc2UgPSBGQUxTRSkKICBwYXNzX3ZjZiA8LSB2Y2ZbZ2V0RklMVEVSKHZjZikgPT0gIlBBU1MiLCBdCiAgCiAgcmVmIDwtIGdldFJFRihwYXNzX3ZjZikKICBhbHQgPC0gZ2V0QUxUKHBhc3NfdmNmKQogIAogICMgT25seSBrZWVwIFNOVnMKICBzbnZfaWR4IDwtIG5jaGFyKHJlZikgPT0gMSAmIG5jaGFyKGFsdCkgPT0gMQogIHJlZiA8LSByZWZbc252X2lkeF0KICBhbHQgPC0gYWx0W3Nudl9pZHhdCiAgCiAgIyBDbGFzc2lmeSB0cmFuc2l0aW9ucyBhbmQgdHJhbnN2ZXJzaW9ucwogIHRyYW5zaXRpb25zIDwtIGMoIkFHIiwgIkdBIiwgIkNUIiwgIlRDIikKICB0cmFuc3ZlcnNpb25zIDwtIGMoIkFDIiwgIkNBIiwgIkFUIiwgIlRBIiwgIkdDIiwgIkNHIiwgIkdUIiwgIlRHIikKICAKICBjaGFuZ2UgPC0gcGFzdGUwKHJlZiwgYWx0KQogIHRpX2NvdW50IDwtIHN1bShjaGFuZ2UgJWluJSB0cmFuc2l0aW9ucykKICB0dl9jb3VudCA8LSBzdW0oY2hhbmdlICVpbiUgdHJhbnN2ZXJzaW9ucykKICAKICByZXR1cm4oZGF0YS5mcmFtZSgKICAgIHNhbXBsZSA9IHNhbXBsZV9uYW1lLAogICAgVHJhbnNpdGlvbnMgPSB0aV9jb3VudCwKICAgIFRyYW5zdmVyc2lvbnMgPSB0dl9jb3VudCwKICAgIFRpVHZfcmF0aW8gPSB0aV9jb3VudCAvIHR2X2NvdW50CiAgKSkKfQoKdGl0dl9kYXRhIDwtIHJiaW5kKAogIGNhbGN1bGF0ZV90aXR2KGZpbGUucGF0aChWQ0ZfRElSLCAiVDEuaGcxOS5maWx0ZXJlZC52Y2YuZ3oiKSwgIlQxIiksCiAgY2FsY3VsYXRlX3RpdHYoZmlsZS5wYXRoKFZDRl9ESVIsICJYMS5oZzE5LmZpbHRlcmVkLnZjZi5neiIpLCAiWDEiKSwKICBjYWxjdWxhdGVfdGl0dihmaWxlLnBhdGgoVkNGX0RJUiwgIlhYMS5oZzE5LmZpbHRlcmVkLnZjZi5neiIpLCAiWFgxIikKKQoKIyBQbG90CnAgPC0gZ2dwbG90KHRpdHZfZGF0YSwgYWVzKHggPSBzYW1wbGUsIHkgPSBUaVR2X3JhdGlvLCBmaWxsID0gc2FtcGxlKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAicmVkIikgKwogIGdlb21fdGV4dChhZXMobGFiZWwgPSByb3VuZChUaVR2X3JhdGlvLCAyKSksIHZqdXN0ID0gLTAuNSkgKwogIGxhYnMoCiAgICB0aXRsZSA9ICJUcmFuc2l0aW9uL1RyYW5zdmVyc2lvbiBSYXRpbyIsCiAgICBzdWJ0aXRsZSA9ICJSZWQgbGluZSBpbmRpY2F0ZXMgZXhwZWN0ZWQgV0VTIHJhdGlvICh+Mi4wKSIsCiAgICB4ID0gIlNhbXBsZSIsCiAgICB5ID0gIlRpL1R2IFJhdGlvIgogICkgKwogIHRoZW1lX2J3KCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCnByaW50KHApCmdnc2F2ZShmaWxlLnBhdGgoVklTX0RJUiwgIlRpVHZfcmF0aW8ucGRmIiksIHAsIHdpZHRoID0gOCwgaGVpZ2h0ID0gNikKYGBgCgoKCgojIENsb25hbCBFdm9sdXRpb24gVHJlZSAoUGh5bG9nZW5ldGljKQoKYGBge3IsIGNodW5rX25hbWU9IlRyZWUifQoKbGlicmFyeSh2Y2ZSKQpsaWJyYXJ5KGFwZSkKbGlicmFyeShwaGFuZ29ybikKClZDRl9ESVIgPC0gIi9ob21lL2Jpb2luZm8vMS1UaGVzaXNfRmluYWxfWWVhcl8yMDI1LzIwMjUtWWVhcjNfQW5hbHlzaXMvMS1zY1JOQV9SRVNVTFRTLTE5LTExLTIwMjUvMTUtV0VTX1BhdGllbnQxX0wxX0wyX0FuYWx5c2lzLTE5LTExLTIwMjUvRXhvbWVfUGF0aWVudF8xL2hnMTlfYW5hbHlzaXMvc29tYXRpY192YXJpYW50cyIKVklTX0RJUiA8LSAiL2hvbWUvYmlvaW5mby8xLVRoZXNpc19GaW5hbF9ZZWFyXzIwMjUvMjAyNS1ZZWFyM19BbmFseXNpcy8xLXNjUk5BX1JFU1VMVFMtMTktMTEtMjAyNS8xNS1XRVNfUGF0aWVudDFfTDFfTDJfQW5hbHlzaXMtMTktMTEtMjAyNS9FeG9tZV9QYXRpZW50XzEvaGcxOV9hbmFseXNpcy92aXN1YWxpemF0aW9ucyIKCiMgQ3JlYXRlIGJpbmFyeSBtdXRhdGlvbiBtYXRyaXggKHByZXNlbnQ9MSwgYWJzZW50PTApCmNyZWF0ZV9tdXRhdGlvbl9tYXRyaXggPC0gZnVuY3Rpb24odmNmX2ZpbGVzLCBzYW1wbGVfbmFtZXMpIHsKICBhbGxfdmFyaWFudHMgPC0gbGlzdCgpCiAgCiAgZm9yIChpIGluIHNlcV9hbG9uZyh2Y2ZfZmlsZXMpKSB7CiAgICB2Y2YgPC0gcmVhZC52Y2ZSKHZjZl9maWxlc1tpXSwgdmVyYm9zZSA9IEZBTFNFKQogICAgcGFzc192Y2YgPC0gdmNmW2dldEZJTFRFUih2Y2YpID09ICJQQVNTIiwgXQogICAgCiAgICB2YXJpYW50cyA8LSBwYXN0ZShnZXRDSFJPTShwYXNzX3ZjZiksIGdldFBPUyhwYXNzX3ZjZiksIHNlcCA9ICI6IikKICAgIGFsbF92YXJpYW50c1tbc2FtcGxlX25hbWVzW2ldXV0gPC0gdmFyaWFudHMKICB9CiAgCiAgIyBHZXQgYWxsIHVuaXF1ZSB2YXJpYW50cwogIHVuaXF1ZV92YXJpYW50cyA8LSB1bmlxdWUodW5saXN0KGFsbF92YXJpYW50cykpCiAgCiAgIyBDcmVhdGUgYmluYXJ5IG1hdHJpeAogIG1hdCA8LSBtYXRyaXgoMCwgbnJvdyA9IGxlbmd0aChzYW1wbGVfbmFtZXMpLCBuY29sID0gbGVuZ3RoKHVuaXF1ZV92YXJpYW50cykpCiAgcm93bmFtZXMobWF0KSA8LSBzYW1wbGVfbmFtZXMKICBjb2xuYW1lcyhtYXQpIDwtIHVuaXF1ZV92YXJpYW50cwogIAogIGZvciAoaSBpbiBzZXFfYWxvbmcoc2FtcGxlX25hbWVzKSkgewogICAgbWF0W2ksIGNvbG5hbWVzKG1hdCkgJWluJSBhbGxfdmFyaWFudHNbW3NhbXBsZV9uYW1lc1tpXV1dXSA8LSAxCiAgfQogIAogIHJldHVybihtYXQpCn0KCiMgQ3JlYXRlIG1hdHJpeAp2Y2ZfZmlsZXMgPC0gYygKICBmaWxlLnBhdGgoVkNGX0RJUiwgIlQxLmhnMTkuZmlsdGVyZWQudmNmLmd6IiksCiAgZmlsZS5wYXRoKFZDRl9ESVIsICJYMS5oZzE5LmZpbHRlcmVkLnZjZi5neiIpLAogIGZpbGUucGF0aChWQ0ZfRElSLCAiWFgxLmhnMTkuZmlsdGVyZWQudmNmLmd6IikKKQoKbXV0YXRpb25fbWF0cml4IDwtIGNyZWF0ZV9tdXRhdGlvbl9tYXRyaXgodmNmX2ZpbGVzLCBjKCJUMSIsICJYMSIsICJYWDEiKSkKCiMgQ2FsY3VsYXRlIGRpc3RhbmNlIGFuZCBidWlsZCB0cmVlCmRpc3RfbWF0cml4IDwtIGRpc3QobXV0YXRpb25fbWF0cml4LCBtZXRob2QgPSAiYmluYXJ5IikKdHJlZSA8LSBuaihkaXN0X21hdHJpeCkKCiMgUm9vdCB0aGUgdHJlZSB3aXRoIE5vcm1hbCAoTjEgYXMgb3V0Z3JvdXAgY29uY2VwdHVhbGx5KQojIFBsb3QKcGRmKGZpbGUucGF0aChWSVNfRElSLCAiQ2xvbmFsX0V2b2x1dGlvbl9UcmVlLnBkZiIpLCB3aWR0aCA9IDEwLCBoZWlnaHQgPSA4KQpwbG90KHRyZWUsIHR5cGUgPSAicGh5bG9ncmFtIiwgZWRnZS53aWR0aCA9IDIsIAogICAgIG1haW4gPSAiQ2xvbmFsIEV2b2x1dGlvbjogVDEg4oaSIFgxIOKGkiBYWDEiLAogICAgIGNleCA9IDEuNSkKYWRkLnNjYWxlLmJhcigpCmRldi5vZmYoKQoKdHJlZQpgYGAKCmBgYHtyLCBjaHVua19uYW1lPSJUcmVlIn0KbGlicmFyeSh2Y2ZSKQpsaWJyYXJ5KGFwZSkKbGlicmFyeShwaGFuZ29ybikKClZDRl9ESVIgPC0gIi9ob21lL2Jpb2luZm8vMS1UaGVzaXNfRmluYWxfWWVhcl8yMDI1LzIwMjUtWWVhcjNfQW5hbHlzaXMvMS1zY1JOQV9SRVNVTFRTLTE5LTExLTIwMjUvMTUtV0VTX1BhdGllbnQxX0wxX0wyX0FuYWx5c2lzLTE5LTExLTIwMjUvRXhvbWVfUGF0aWVudF8xL2hnMTlfYW5hbHlzaXMvc29tYXRpY192YXJpYW50cyIKVklTX0RJUiA8LSAiL2hvbWUvYmlvaW5mby8xLVRoZXNpc19GaW5hbF9ZZWFyXzIwMjUvMjAyNS1ZZWFyM19BbmFseXNpcy8xLXNjUk5BX1JFU1VMVFMtMTktMTEtMjAyNS8xNS1XRVNfUGF0aWVudDFfTDFfTDJfQW5hbHlzaXMtMTktMTEtMjAyNS9FeG9tZV9QYXRpZW50XzEvaGcxOV9hbmFseXNpcy92aXN1YWxpemF0aW9ucyIKCiMgQ3JlYXRlIGJpbmFyeSBtdXRhdGlvbiBtYXRyaXggKHByZXNlbnQ9MSwgYWJzZW50PTApCmNyZWF0ZV9tdXRhdGlvbl9tYXRyaXggPC0gZnVuY3Rpb24odmNmX2ZpbGVzLCBzYW1wbGVfbmFtZXMpIHsKICBhbGxfdmFyaWFudHMgPC0gbGlzdCgpCiAgCiAgbWVzc2FnZSgiTG9hZGluZyB2YXJpYW50cyBmcm9tIFZDRiBmaWxlcy4uLiIpCiAgZm9yIChpIGluIHNlcV9hbG9uZyh2Y2ZfZmlsZXMpKSB7CiAgICB2Y2YgPC0gcmVhZC52Y2ZSKHZjZl9maWxlc1tpXSwgdmVyYm9zZSA9IEZBTFNFKQogICAgcGFzc192Y2YgPC0gdmNmW2dldEZJTFRFUih2Y2YpID09ICJQQVNTIiwgXQogICAgCiAgICAjIFVzZSBjaHI6cG9zOnJlZjphbHQgYXMgdW5pcXVlIGlkZW50aWZpZXIKICAgIHZhcmlhbnRzIDwtIHBhc3RlKGdldENIUk9NKHBhc3NfdmNmKSwgZ2V0UE9TKHBhc3NfdmNmKSwgCiAgICAgICAgICAgICAgICAgICAgIGdldFJFRihwYXNzX3ZjZiksIGdldEFMVChwYXNzX3ZjZiksIHNlcCA9ICI6IikKICAgIGFsbF92YXJpYW50c1tbc2FtcGxlX25hbWVzW2ldXV0gPC0gdmFyaWFudHMKICAgIG1lc3NhZ2UocGFzdGUoIiAgIiwgc2FtcGxlX25hbWVzW2ldLCAiOiIsIGxlbmd0aCh2YXJpYW50cyksICJzb21hdGljIG11dGF0aW9ucyIpKQogIH0KICAKICAjIEdldCBhbGwgdW5pcXVlIHZhcmlhbnRzIGFjcm9zcyBhbGwgc2FtcGxlcwogIHVuaXF1ZV92YXJpYW50cyA8LSB1bmlxdWUodW5saXN0KGFsbF92YXJpYW50cykpCiAgbWVzc2FnZShwYXN0ZSgiVG90YWwgdW5pcXVlIHNvbWF0aWMgdmFyaWFudHM6IiwgbGVuZ3RoKHVuaXF1ZV92YXJpYW50cykpKQogIAogICMgQ3JlYXRlIGJpbmFyeSBtYXRyaXgKICBtYXQgPC0gbWF0cml4KDAsIG5yb3cgPSBsZW5ndGgoc2FtcGxlX25hbWVzKSwgbmNvbCA9IGxlbmd0aCh1bmlxdWVfdmFyaWFudHMpKQogIHJvd25hbWVzKG1hdCkgPC0gc2FtcGxlX25hbWVzCiAgY29sbmFtZXMobWF0KSA8LSB1bmlxdWVfdmFyaWFudHMKICAKICBmb3IgKGkgaW4gc2VxX2Fsb25nKHNhbXBsZV9uYW1lcykpIHsKICAgIG1hdFtpLCBjb2xuYW1lcyhtYXQpICVpbiUgYWxsX3ZhcmlhbnRzW1tzYW1wbGVfbmFtZXNbaV1dXV0gPC0gMQogIH0KICAKICByZXR1cm4obGlzdChtYXRyaXggPSBtYXQsIHZhcmlhbnRzID0gYWxsX3ZhcmlhbnRzKSkKfQoKIyBMb2FkIG11dGF0aW9uIGRhdGEKdmNmX2ZpbGVzIDwtIGMoCiAgZmlsZS5wYXRoKFZDRl9ESVIsICJUMS5oZzE5LmZpbHRlcmVkLnZjZi5neiIpLAogIGZpbGUucGF0aChWQ0ZfRElSLCAiWDEuaGcxOS5maWx0ZXJlZC52Y2YuZ3oiKSwKICBmaWxlLnBhdGgoVkNGX0RJUiwgIlhYMS5oZzE5LmZpbHRlcmVkLnZjZi5neiIpCikKCnNhbXBsZV9uYW1lcyA8LSBjKCJUMSIsICJYMSIsICJYWDEiKQpyZXN1bHQgPC0gY3JlYXRlX211dGF0aW9uX21hdHJpeCh2Y2ZfZmlsZXMsIHNhbXBsZV9uYW1lcykKbXV0YXRpb25fbWF0cml4IDwtIHJlc3VsdCRtYXRyaXgKYWxsX3ZhcmlhbnRzIDwtIHJlc3VsdCR2YXJpYW50cwoKIyBBZGQgTjEgKE5vcm1hbCkgYXMgYWxsIHplcm9zIC0gcmVwcmVzZW50aW5nIGdlcm1saW5lIHN0YXRlIChubyBzb21hdGljIG11dGF0aW9ucykKbXV0YXRpb25fbWF0cml4X3dpdGhfbm9ybWFsIDwtIHJiaW5kKAogIE4xID0gcmVwKDAsIG5jb2wobXV0YXRpb25fbWF0cml4KSksICAjIE4xIGhhcyBaRVJPIHNvbWF0aWMgbXV0YXRpb25zIGJ5IGRlZmluaXRpb24KICBtdXRhdGlvbl9tYXRyaXgKKQoKbWVzc2FnZSgiXG5NdXRhdGlvbiBtYXRyaXggc3VtbWFyeToiKQpwcmludChyb3dTdW1zKG11dGF0aW9uX21hdHJpeF93aXRoX25vcm1hbCkpCgojIENhbGN1bGF0ZSBzaGFyZWQgbXV0YXRpb25zCnNoYXJlZF9UMV9YMSA8LSBzdW0obXV0YXRpb25fbWF0cml4WyJUMSIsIF0gPT0gMSAmIG11dGF0aW9uX21hdHJpeFsiWDEiLCBdID09IDEpCnNoYXJlZF9YMV9YWDEgPC0gc3VtKG11dGF0aW9uX21hdHJpeFsiWDEiLCBdID09IDEgJiBtdXRhdGlvbl9tYXRyaXhbIlhYMSIsIF0gPT0gMSkKc2hhcmVkX1QxX1hYMSA8LSBzdW0obXV0YXRpb25fbWF0cml4WyJUMSIsIF0gPT0gMSAmIG11dGF0aW9uX21hdHJpeFsiWFgxIiwgXSA9PSAxKQpzaGFyZWRfYWxsIDwtIHN1bShtdXRhdGlvbl9tYXRyaXhbIlQxIiwgXSA9PSAxICYgCiAgICAgICAgICAgICAgICAgIG11dGF0aW9uX21hdHJpeFsiWDEiLCBdID09IDEgJiAKICAgICAgICAgICAgICAgICAgbXV0YXRpb25fbWF0cml4WyJYWDEiLCBdID09IDEpCgptZXNzYWdlKCJcblNoYXJlZCBtdXRhdGlvbnMgYW5hbHlzaXM6IikKbWVzc2FnZShwYXN0ZSgiICBUMSDiiKkgWDE6Iiwgc2hhcmVkX1QxX1gxKSkKbWVzc2FnZShwYXN0ZSgiICBYMSDiiKkgWFgxOiIsIHNoYXJlZF9YMV9YWDEpKQptZXNzYWdlKHBhc3RlKCIgIFQxIOKIqSBYWDE6Iiwgc2hhcmVkX1QxX1hYMSkpCm1lc3NhZ2UocGFzdGUoIiAgQWxsIHRocmVlIChUMSDiiKkgWDEg4oipIFhYMSk6Iiwgc2hhcmVkX2FsbCwgIi0gVFJVTksgbXV0YXRpb25zIikpCgojIFByaXZhdGUgbXV0YXRpb25zCnByaXZhdGVfVDEgPC0gc3VtKG11dGF0aW9uX21hdHJpeFsiVDEiLCBdID09IDEgJiAKICAgICAgICAgICAgICAgICAgbXV0YXRpb25fbWF0cml4WyJYMSIsIF0gPT0gMCAmIAogICAgICAgICAgICAgICAgICBtdXRhdGlvbl9tYXRyaXhbIlhYMSIsIF0gPT0gMCkKcHJpdmF0ZV9YMSA8LSBzdW0obXV0YXRpb25fbWF0cml4WyJYMSIsIF0gPT0gMSAmIAogICAgICAgICAgICAgICAgICBtdXRhdGlvbl9tYXRyaXhbIlQxIiwgXSA9PSAwICYgCiAgICAgICAgICAgICAgICAgIG11dGF0aW9uX21hdHJpeFsiWFgxIiwgXSA9PSAwKQpwcml2YXRlX1hYMSA8LSBzdW0obXV0YXRpb25fbWF0cml4WyJYWDEiLCBdID09IDEgJiAKICAgICAgICAgICAgICAgICAgIG11dGF0aW9uX21hdHJpeFsiVDEiLCBdID09IDAgJiAKICAgICAgICAgICAgICAgICAgIG11dGF0aW9uX21hdHJpeFsiWDEiLCBdID09IDApCgptZXNzYWdlKCJcblByaXZhdGUgbXV0YXRpb25zOiIpCm1lc3NhZ2UocGFzdGUoIiAgVDEgb25seToiLCBwcml2YXRlX1QxKSkKbWVzc2FnZShwYXN0ZSgiICBYMSBvbmx5OiIsIHByaXZhdGVfWDEpKQptZXNzYWdlKHBhc3RlKCIgIFhYMSBvbmx5OiIsIHByaXZhdGVfWFgxKSkKCiMgQ2FsY3VsYXRlIGRpc3RhbmNlIG1hdHJpeApkaXN0X21hdHJpeCA8LSBkaXN0KG11dGF0aW9uX21hdHJpeF93aXRoX25vcm1hbCwgbWV0aG9kID0gImJpbmFyeSIpCgojIEJ1aWxkIG5laWdoYm9yLWpvaW5pbmcgdHJlZQp0cmVlIDwtIG5qKGRpc3RfbWF0cml4KQoKIyBSb290IGF0IE4xCnJvb3RlZF90cmVlIDwtIHJvb3QodHJlZSwgb3V0Z3JvdXAgPSAiTjEiLCByZXNvbHZlLnJvb3QgPSBUUlVFKQoKIyBDcmVhdGUgYmV0dGVyIHBsb3QKcGRmKGZpbGUucGF0aChWSVNfRElSLCAiQ2xvbmFsX0V2b2x1dGlvbl9UcmVlX1Jvb3RlZC5wZGYiKSwgd2lkdGggPSAxMiwgaGVpZ2h0ID0gMTApCgpwYXIobWFyID0gYyg1LCA0LCA0LCAyKSArIDAuMSkKCnBsb3Qocm9vdGVkX3RyZWUsIAogICAgIHR5cGUgPSAicGh5bG9ncmFtIiwKICAgICBkaXJlY3Rpb24gPSAicmlnaHR3YXJkcyIsCiAgICAgZWRnZS53aWR0aCA9IDQsCiAgICAgY2V4ID0gMS44LAogICAgIGZvbnQgPSAyLAogICAgIHRpcC5jb2xvciA9IGMoImJsYWNrIiwgInJlZCIsICJibHVlIiwgImRhcmtncmVlbiIpLAogICAgIGVkZ2UuY29sb3IgPSAiZ3JleTMwIiwKICAgICBtYWluID0gIkNsb25hbCBFdm9sdXRpb246IE4xIChHZXJtbGluZSkg4oaSIFQxIOKGkiBYMSDihpIgWFgxIiwKICAgICBjZXgubWFpbiA9IDEuNSkKCiMgQWRkIG5vZGUgcG9pbnQgYXQgcm9vdApub2RlbGFiZWxzKHRleHQgPSAiUm9vdCIsIAogICAgICAgICAgbm9kZSA9IGxlbmd0aChyb290ZWRfdHJlZSR0aXAubGFiZWwpICsgMSwKICAgICAgICAgIGZyYW1lID0gImNpcmNsZSIsCiAgICAgICAgICBiZyA9ICJsaWdodGJsdWUiLAogICAgICAgICAgY2V4ID0gMS4yLAogICAgICAgICAgZm9udCA9IDIpCgojIEFkZCBzY2FsZSBiYXIKYWRkLnNjYWxlLmJhcih4ID0gMCwgeSA9IDEsIGNleCA9IDEuMiwgbHdkID0gMikKbXRleHQoIkdlbmV0aWMgRGlzdGFuY2UgKEJpbmFyeSkiLCBzaWRlID0gMSwgbGluZSA9IDMsIGNleCA9IDAuOSkKCiMgQWRkIGxlZ2VuZCB3aXRoIG11dGF0aW9uIGNvdW50cwojIENoYW5nZSBsZWdlbmQgcG9zaXRpb24gdG8gYm90dG9tIHJpZ2h0CmxlZ2VuZCgiYm90dG9tcmlnaHQiLAogICAgICAgbGVnZW5kID0gYygKICAgICAgICAgcGFzdGUwKCJOMSAoTm9ybWFsKTogMCBzb21hdGljIiksCiAgICAgICAgIHBhc3RlMCgiVDEgKFByaW1hcnkpOiAiLCBzdW0obXV0YXRpb25fbWF0cml4WyJUMSIsIF0pLCAiIHNvbWF0aWMiKSwKICAgICAgICAgcGFzdGUwKCJYMSAoSW4tdml2byk6ICIsIHN1bShtdXRhdGlvbl9tYXRyaXhbIlgxIiwgXSksICIgc29tYXRpYyIpLAogICAgICAgICBwYXN0ZTAoIlhYMSAoU2Vjb25kYXJ5KTogIiwgc3VtKG11dGF0aW9uX21hdHJpeFsiWFgxIiwgXSksICIgc29tYXRpYyIpCiAgICAgICApLAogICAgICAgY29sID0gYygiYmxhY2siLCAicmVkIiwgImJsdWUiLCAiZGFya2dyZWVuIiksCiAgICAgICBwY2ggPSAxOSwKICAgICAgIGNleCA9IDEuMSwKICAgICAgIGJ0eSA9ICJuIiwKICAgICAgIHB0LmNleCA9IDIpCgojIEFkZCB0ZXh0IGJveCB3aXRoIHNoYXJlZCBtdXRhdGlvbiBpbmZvCnRleHQoeCA9IG1heChyb290ZWRfdHJlZSRlZGdlWywyXSkgKiAwLjYsIAogICAgIHkgPSAwLjUsCiAgICAgbGFiZWxzID0gcGFzdGUwKAogICAgICAgIlRydW5rIG11dGF0aW9ucyAoYWxsIDMpOiAiLCBzaGFyZWRfYWxsLCAiXG4iLAogICAgICAgIlQxLVgxIHNoYXJlZDogIiwgc2hhcmVkX1QxX1gxLCAiXG4iLAogICAgICAgIlgxLVhYMSBzaGFyZWQ6ICIsIHNoYXJlZF9YMV9YWDEKICAgICApLAogICAgIGNleCA9IDEsCiAgICAgYWRqID0gMCwKICAgICBmb250ID0gMiwKICAgICBjb2wgPSAiZGFya3JlZCIpCgpkZXYub2ZmKCkKCm1lc3NhZ2UoIlxu4pyTIFJvb3RlZCBwaHlsb2dlbmV0aWMgdHJlZSBjcmVhdGVkIHN1Y2Nlc3NmdWxseSEiKQptZXNzYWdlKHBhc3RlKCJPdXRwdXQ6IiwgZmlsZS5wYXRoKFZJU19ESVIsICJDbG9uYWxfRXZvbHV0aW9uX1RyZWVfUm9vdGVkLnBkZiIpKSkKCiMgUHJpbnQgdHJlZSBzdHJ1Y3R1cmUKcHJpbnQocm9vdGVkX3RyZWUpCgoKYGBgCgoKIyBQYXRod2F5IEVucmljaG1lbnQgQnViYmxlIFBsb3QKCmBgYHtyLCBjaHVua19uYW1lPSJQYXRod2F5X0VucmljaG1lbnQiLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9OCwgZXZhbD1UUlVFfQpsaWJyYXJ5KGNsdXN0ZXJQcm9maWxlcikKbGlicmFyeShvcmcuSHMuZWcuZGIpCmxpYnJhcnkoZW5yaWNocGxvdCkKbGlicmFyeShnZ3Bsb3QyKQoKVkNGX0RJUiA8LSAiL2hvbWUvYmlvaW5mby8xLVRoZXNpc19GaW5hbF9ZZWFyXzIwMjUvMjAyNS1ZZWFyM19BbmFseXNpcy8xLXNjUk5BX1JFU1VMVFMtMTktMTEtMjAyNS8xNS1XRVNfUGF0aWVudDFfTDFfTDJfQW5hbHlzaXMtMTktMTEtMjAyNS9FeG9tZV9QYXRpZW50XzEvaGcxOV9hbmFseXNpcy9zb21hdGljX3ZhcmlhbnRzIgpWSVNfRElSIDwtICIvaG9tZS9iaW9pbmZvLzEtVGhlc2lzX0ZpbmFsX1llYXJfMjAyNS8yMDI1LVllYXIzX0FuYWx5c2lzLzEtc2NSTkFfUkVTVUxUUy0xOS0xMS0yMDI1LzE1LVdFU19QYXRpZW50MV9MMV9MMl9BbmFseXNpcy0xOS0xMS0yMDI1L0V4b21lX1BhdGllbnRfMS9oZzE5X2FuYWx5c2lzL3Zpc3VhbGl6YXRpb25zIgoKIyBVc2UgdGhlIG11dGF0ZWQgZ2VuZXMgZnJvbSB5b3VyIE9uY29QcmludCBhbmFseXNpcwojIEV4dHJhY3QgYWxsIG11dGF0ZWQgZ2VuZXMgYWNyb3NzIHNhbXBsZXMKbGlicmFyeSh2Y2ZSKQoKZXh0cmFjdF9nZW5lcyA8LSBmdW5jdGlvbih2Y2ZfZmlsZSkgewogIHZjZiA8LSByZWFkLnZjZlIodmNmX2ZpbGUsIHZlcmJvc2UgPSBGQUxTRSkKICBwYXNzX3ZjZiA8LSB2Y2ZbZ2V0RklMVEVSKHZjZikgPT0gIlBBU1MiLCBdCiAgCiAgY3NxIDwtIGV4dHJhY3QuaW5mbyhwYXNzX3ZjZiwgIkNTUSIpCiAgZ2VuZXMgPC0gdW5pcXVlKHNhcHBseShjc3EsIGZ1bmN0aW9uKHgpIHsKICAgIGlmIChpcy5uYSh4KSkgcmV0dXJuKE5BKQogICAgc3Ryc3BsaXQoc3Ryc3BsaXQoeCwgIiwiKVtbMV1dWzFdLCAiXFx8IilbWzFdXVs0XQogIH0pKQogIAogIHJldHVybihnZW5lc1shaXMubmEoZ2VuZXMpICYgZ2VuZXMgIT0gIiJdKQp9CgphbGxfZ2VuZXMgPC0gdW5pcXVlKGMoCiAgZXh0cmFjdF9nZW5lcyhmaWxlLnBhdGgoVkNGX0RJUiwgIlQxLmhnMTkuYW5ub3RhdGVkLnZjZiIpKSwKICBleHRyYWN0X2dlbmVzKGZpbGUucGF0aChWQ0ZfRElSLCAiWDEuaGcxOS5hbm5vdGF0ZWQudmNmIikpLAogIGV4dHJhY3RfZ2VuZXMoZmlsZS5wYXRoKFZDRl9ESVIsICJYWDEuaGcxOS5hbm5vdGF0ZWQudmNmIikpCikpCgojIENvbnZlcnQgZ2VuZSBzeW1ib2xzIHRvIEVudHJleiBJRHMKZ2VuZV9lbnRyZXogPC0gYml0cihhbGxfZ2VuZXMsIGZyb21UeXBlID0gIlNZTUJPTCIsIAogICAgICAgICAgICAgICAgICAgIHRvVHlwZSA9ICJFTlRSRVpJRCIsIAogICAgICAgICAgICAgICAgICAgIE9yZ0RiID0gb3JnLkhzLmVnLmRiKQoKIyBLRUdHIHBhdGh3YXkgZW5yaWNobWVudAprZWdnX2VucmljaCA8LSBlbnJpY2hLRUdHKGdlbmUgPSBnZW5lX2VudHJleiRFTlRSRVpJRCwKICAgICAgICAgICAgICAgICAgICAgICAgICBvcmdhbmlzbSA9ICdoc2EnLAogICAgICAgICAgICAgICAgICAgICAgICAgIHB2YWx1ZUN1dG9mZiA9IDAuMDUpCgojIFBsb3QKcCA8LSBkb3RwbG90KGtlZ2dfZW5yaWNoLCBzaG93Q2F0ZWdvcnkgPSAyMCkgKwogIGdndGl0bGUoIktFR0cgUGF0aHdheSBFbnJpY2htZW50IG9mIE11dGF0ZWQgR2VuZXMiKQoKZ2dzYXZlKGZpbGUucGF0aChWSVNfRElSLCAiUGF0aHdheV9FbnJpY2htZW50LnBkZiIpLCBwLCB3aWR0aCA9IDEyLCBoZWlnaHQgPSA4KQoKcApgYGAKCgoKIyBDTlYtTXV0YXRpb24gSW50ZWdyYXRpb24gUGxvdAoKYGBge3IsIGNodW5rX25hbWU9IkNOVi1NdXRhdGlvbiIsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD04LCBldmFsPVRSVUV9CmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShkcGx5cikKbGlicmFyeShwYXRjaHdvcmspCgpMSUZUT1ZFUl9ESVIgPC0gIi9ob21lL2Jpb2luZm8vMS1UaGVzaXNfRmluYWxfWWVhcl8yMDI1LzIwMjUtWWVhcjNfQW5hbHlzaXMvMS1zY1JOQV9SRVNVTFRTLTE5LTExLTIwMjUvMTUtV0VTX1BhdGllbnQxX0wxX0wyX0FuYWx5c2lzLTE5LTExLTIwMjUvRXhvbWVfUGF0aWVudF8xL2hnMTlfYW5hbHlzaXMvbGlmdG92ZXJfcmVzdWx0cyIKVkNGX0RJUiA8LSAiL2hvbWUvYmlvaW5mby8xLVRoZXNpc19GaW5hbF9ZZWFyXzIwMjUvMjAyNS1ZZWFyM19BbmFseXNpcy8xLXNjUk5BX1JFU1VMVFMtMTktMTEtMjAyNS8xNS1XRVNfUGF0aWVudDFfTDFfTDJfQW5hbHlzaXMtMTktMTEtMjAyNS9FeG9tZV9QYXRpZW50XzEvaGcxOV9hbmFseXNpcy9zb21hdGljX3ZhcmlhbnRzIgpWSVNfRElSIDwtICIvaG9tZS9iaW9pbmZvLzEtVGhlc2lzX0ZpbmFsX1llYXJfMjAyNS8yMDI1LVllYXIzX0FuYWx5c2lzLzEtc2NSTkFfUkVTVUxUUy0xOS0xMS0yMDI1LzE1LVdFU19QYXRpZW50MV9MMV9MMl9BbmFseXNpcy0xOS0xMS0yMDI1L0V4b21lX1BhdGllbnRfMS9oZzE5X2FuYWx5c2lzL3Zpc3VhbGl6YXRpb25zIgoKIyBUaGlzIHNjcmlwdCBvdmVybGF5cyBoaWdoLWltcGFjdCBtdXRhdGlvbnMgb24gdG9wIG9mIENOViB0cmFja3MKIyBzaG93aW5nIHdoaWNoIGdlbmVzIGhhdmUgQk9USCBDTlYgYWx0ZXJhdGlvbnMgQU5EIHNvbWF0aWMgbXV0YXRpb25zCgojIExvYWQgQ05WIGRhdGEgKGFscmVhZHkgY3JlYXRlZCBpbiB5b3VyIHNjcmlwdCkKc2FtcGxlcyA8LSBjKCJUMSIsICJYMSIsICJYWDEiKQphbGxfY25zX2RhdGEgPC0gbGlzdCgpCgpmb3IgKHNhbXBsZV9pZCBpbiBzYW1wbGVzKSB7CiAgZmlsZV9wYXRoIDwtIGZpbGUucGF0aChMSUZUT1ZFUl9ESVIsIHBhc3RlMChzYW1wbGVfaWQsICIuaGczOC5jbnMiKSkKICBjbnNfZGF0YSA8LSByZWFkLmRlbGltKGZpbGVfcGF0aCwgaGVhZGVyID0gVFJVRSwgc2VwID0gIlx0IikgJT4lCiAgICBtdXRhdGUoc2FtcGxlID0gc2FtcGxlX2lkKQogIGFsbF9jbnNfZGF0YVtbc2FtcGxlX2lkXV0gPC0gY25zX2RhdGEKfQoKY29tYmluZWRfY252IDwtIGJpbmRfcm93cyhhbGxfY25zX2RhdGEpCmNvbWJpbmVkX2NudiRjaHJvbW9zb21lIDwtIGZhY3Rvcihjb21iaW5lZF9jbnYkY2hyb21vc29tZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBwYXN0ZTAoImNociIsIGMoMToyMiwgIlgiLCAiWSIpKSkKCiMgRXh0cmFjdCBtdXRhdGlvbiBwb3NpdGlvbnMKIyAoUmV1c2UgeW91ciBwYXJzZSBmdW5jdGlvbiBmcm9tIE9uY29QcmludCkKIyBGb3Igc2ltcGxpY2l0eSwgbWFya2luZyBvbmx5IGhpZ2gtaW1wYWN0IGdlbmVzCgpoaWdoX2ltcGFjdF9nZW5lcyA8LSBjKCJUUDUzIiwgIk5PVENIMSIsICJDREtOMkEiLCAiUFRFTiIsICJNWUMiKSAgIyBBZGQgeW91ciB0b3AgZ2VuZXMKCnBfY252X211dCA8LSBnZ3Bsb3QoY29tYmluZWRfY252LCBhZXMoeCA9IHN0YXJ0IC8gMWU2LCB5ID0gbG9nMikpICsKICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGxvZzIpLCBzaXplID0gMC41LCBhbHBoYSA9IDAuNykgKwogIGZhY2V0X2dyaWQoc2FtcGxlIH4gY2hyb21vc29tZSwgc2NhbGVzID0gImZyZWVfeCIsIHNwYWNlID0gImZyZWVfeCIpICsKICBzY2FsZV9jb2xvcl9ncmFkaWVudDIobG93ID0gImJsdWUiLCBtaWQgPSAiZ3JleSIsIGhpZ2ggPSAicmVkIiwgbWlkcG9pbnQgPSAwKSArCiAgbGFicygKICAgIHRpdGxlID0gIkludGVncmF0ZWQgQ05WIGFuZCBIaWdoLUltcGFjdCBNdXRhdGlvbnMiLAogICAgeCA9ICJHZW5vbWljIFBvc2l0aW9uIChNYikiLAogICAgeSA9ICJDb3B5IE51bWJlciAobG9nMikiCiAgKSArCiAgdGhlbWVfYncoKSArCiAgdGhlbWUoCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGF4aXMudGlja3MueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gNyksCiAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIgogICkKCmdnc2F2ZShmaWxlLnBhdGgoVklTX0RJUiwgIkNOVl9NdXRhdGlvbl9JbnRlZ3JhdGlvbi5wZGYiKSwgCiAgICAgICBwX2Nudl9tdXQsIHdpZHRoID0gMTQsIGhlaWdodCA9IDEwKQpwX2Nudl9tdXQKYGBgCgoKCiMgUHl0aG9uIEFsdGVybmF0aXZlIGZvciBDdXN0b20gTXVsdGktUGFuZWwgUGxvdAoKYGBge3IsIGNodW5rX25hbWU9Ik11bHRpLVBhbmVsLVBsb3QiLCBmaWcud2lkdGg9MTYsIGZpZy5oZWlnaHQ9MTIsIGV2YWw9VFJVRX0KbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHBhdGNod29yaykKbGlicmFyeSh2Y2ZSKQoKQkFTRV9ESVIgPC0gIi9ob21lL2Jpb2luZm8vMS1UaGVzaXNfRmluYWxfWWVhcl8yMDI1LzIwMjUtWWVhcjNfQW5hbHlzaXMvMS1zY1JOQV9SRVNVTFRTLTE5LTExLTIwMjUvMTUtV0VTX1BhdGllbnQxX0wxX0wyX0FuYWx5c2lzLTE5LTExLTIwMjUvRXhvbWVfUGF0aWVudF8xIgpDTlZfRElSIDwtIHBhc3RlMChCQVNFX0RJUiwgIi9oZzE5X2FuYWx5c2lzL2Nudl9yZXN1bHRzIikKVkNGX0RJUiA8LSBwYXN0ZTAoQkFTRV9ESVIsICIvaGcxOV9hbmFseXNpcy9zb21hdGljX3ZhcmlhbnRzIikKVklTX0RJUiA8LSBwYXN0ZTAoQkFTRV9ESVIsICIvaGcxOV9hbmFseXNpcy92aXN1YWxpemF0aW9ucyIpCgpjcmVhdGVfY29tcHJlaGVuc2l2ZV9wbG90IDwtIGZ1bmN0aW9uKHNhbXBsZV9pZCkgewogICMgTG9hZCBDTlYgZGF0YQogIGNucl9maWxlIDwtIGZpbGUucGF0aChDTlZfRElSLCBzYW1wbGVfaWQsIHBhc3RlMChzYW1wbGVfaWQsICIud2l0aF9yZy5jbnIiKSkKICBjbnNfZmlsZSA8LSBmaWxlLnBhdGgoQ05WX0RJUiwgc2FtcGxlX2lkLCBwYXN0ZTAoc2FtcGxlX2lkLCAiLndpdGhfcmcuY25zIikpCiAgCiAgY25yX2RhdGEgPC0gcmVhZC5kZWxpbShjbnJfZmlsZSwgaGVhZGVyID0gVFJVRSkKICBjbnNfZGF0YSA8LSByZWFkLmRlbGltKGNuc19maWxlLCBoZWFkZXIgPSBUUlVFKQogIAogICMgUHJlcGFyZSBjaHJvbW9zb21lIG9yZGVyCiAgY25yX2RhdGEkY2hyb21vc29tZSA8LSBmYWN0b3IoY25yX2RhdGEkY2hyb21vc29tZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gcGFzdGUwKCJjaHIiLCBjKDE6MjIsICJYIiwgIlkiKSkpCiAgY25zX2RhdGEkY2hyb21vc29tZSA8LSBmYWN0b3IoY25zX2RhdGEkY2hyb21vc29tZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gcGFzdGUwKCJjaHIiLCBjKDE6MjIsICJYIiwgIlkiKSkpCiAgCiAgIyBQYW5lbCAxOiBDb3ZlcmFnZSBkZXB0aAogIHAxIDwtIGdncGxvdChjbnJfZGF0YSwgYWVzKHggPSBzdGFydCwgeSA9IGRlcHRoLCBjb2xvciA9IGNocm9tb3NvbWUpKSArCiAgICBnZW9tX3BvaW50KHNpemUgPSAwLjMsIGFscGhhID0gMC41KSArCiAgICBmYWNldF9ncmlkKC4gfiBjaHJvbW9zb21lLCBzY2FsZXMgPSAiZnJlZV94Iiwgc3BhY2UgPSAiZnJlZV94IikgKwogICAgbGFicyh0aXRsZSA9IHBhc3RlKHNhbXBsZV9pZCwgIi0gQ292ZXJhZ2UgRGVwdGgiKSwgeSA9ICJDb3ZlcmFnZSIpICsKICAgIHRoZW1lX21pbmltYWwoKSArCiAgICB0aGVtZSgKICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgIGF4aXMudGlja3MueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgICBwYW5lbC5zcGFjaW5nID0gdW5pdCgwLjEsICJsaW5lcyIpCiAgICApCiAgCiAgIyBQYW5lbCAyOiBWQUYgKGZyb20gVkNGKQogIHZjZl9maWxlIDwtIGZpbGUucGF0aChWQ0ZfRElSLCBwYXN0ZTAoc2FtcGxlX2lkLCAiLmhnMTkuZmlsdGVyZWQudmNmLmd6IikpCiAgdmNmIDwtIHJlYWQudmNmUih2Y2ZfZmlsZSwgdmVyYm9zZSA9IEZBTFNFKQogIAogICMgRXh0cmFjdCBWQUYKICBhZCA8LSBleHRyYWN0Lmd0KHZjZiwgZWxlbWVudCA9ICJBRCIpCiAgdmFmIDwtIHNhcHBseShhZFssIHNhbXBsZV9pZF0sIGZ1bmN0aW9uKHgpIHsKICAgIGlmIChpcy5uYSh4KSkgcmV0dXJuKE5BKQogICAgZGVwdGhzIDwtIGFzLm51bWVyaWMoc3Ryc3BsaXQoeCwgIiwiKVtbMV1dKQogICAgaWYgKGxlbmd0aChkZXB0aHMpIDwgMikgcmV0dXJuKE5BKQogICAgcmV0dXJuKGRlcHRoc1syXSAvIHN1bShkZXB0aHMpKQogIH0pCiAgCiAgdmFmX2RhdGEgPC0gZGF0YS5mcmFtZSgKICAgIGNocm9tb3NvbWUgPSBmYWN0b3IoZ2V0Q0hST00odmNmKSwgbGV2ZWxzID0gcGFzdGUwKCJjaHIiLCBjKDE6MjIsICJYIiwgIlkiKSkpLAogICAgcG9zaXRpb24gPSBnZXRQT1ModmNmKSwKICAgIFZBRiA9IHZhZgogICkKICAKICBwMiA8LSBnZ3Bsb3QodmFmX2RhdGEsIGFlcyh4ID0gcG9zaXRpb24sIHkgPSBWQUYsIGNvbG9yID0gY2hyb21vc29tZSkpICsKICAgIGdlb21fcG9pbnQoc2l6ZSA9IDAuNSwgYWxwaGEgPSAwLjYpICsKICAgIGZhY2V0X2dyaWQoLiB+IGNocm9tb3NvbWUsIHNjYWxlcyA9ICJmcmVlX3giLCBzcGFjZSA9ICJmcmVlX3giKSArCiAgICBsYWJzKHkgPSAiVkFGIikgKwogICAgeWxpbSgwLCAxKSArCiAgICB0aGVtZV9taW5pbWFsKCkgKwogICAgdGhlbWUoCiAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICBheGlzLnRpY2tzLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgICAgcGFuZWwuc3BhY2luZyA9IHVuaXQoMC4xLCAibGluZXMiKQogICAgKQogIAogICMgUGFuZWwgMzogQ29weSByYXRpbyAobG9nMikKICBwMyA8LSBnZ3Bsb3QoY25yX2RhdGEsIGFlcyh4ID0gc3RhcnQsIHkgPSBsb2cyLCBjb2xvciA9IGNocm9tb3NvbWUpKSArCiAgICBnZW9tX3BvaW50KHNpemUgPSAwLjMsIGFscGhhID0gMC4zKSArCiAgICBnZW9tX3NlZ21lbnQoZGF0YSA9IGNuc19kYXRhLCAKICAgICAgICAgICAgICAgICBhZXMoeCA9IHN0YXJ0LCB4ZW5kID0gZW5kLCB5ID0gbG9nMiwgeWVuZCA9IGxvZzIpLAogICAgICAgICAgICAgICAgIGNvbG9yID0gInJlZCIsIGxpbmV3aWR0aCA9IDEpICsKICAgIGZhY2V0X2dyaWQoLiB+IGNocm9tb3NvbWUsIHNjYWxlcyA9ICJmcmVlX3giLCBzcGFjZSA9ICJmcmVlX3giKSArCiAgICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJibGFjayIpICsKICAgIGxhYnMoeSA9ICJDb3B5IHJhdGlvIChsb2cyKSIpICsKICAgIHRoZW1lX21pbmltYWwoKSArCiAgICB0aGVtZSgKICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgIGF4aXMudGlja3MueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgICBwYW5lbC5zcGFjaW5nID0gdW5pdCgwLjEsICJsaW5lcyIpCiAgICApCiAgCiAgIyBQYW5lbCA0OiBUdW1vciBmcmFjdGlvbiAoZnJvbSBzZWdtZW50cykKICAjIENhbGN1bGF0ZSBlc3RpbWF0ZWQgdHVtb3IgZnJhY3Rpb24gcGVyIHNlZ21lbnQKICBjbnNfZGF0YSR0dW1vcl9mcmFjdGlvbiA8LSBwbWluKGFicyhjbnNfZGF0YSRsb2cyKSAqIDAuNSwgMSkKICAKICBwNCA8LSBnZ3Bsb3QoY25zX2RhdGEsIGFlcyh4ID0gc3RhcnQsIHhlbmQgPSBlbmQsIHkgPSB0dW1vcl9mcmFjdGlvbiwgeWVuZCA9IHR1bW9yX2ZyYWN0aW9uKSkgKwogICAgZ2VvbV9zZWdtZW50KGFlcyhjb2xvciA9IGNocm9tb3NvbWUpLCBsaW5ld2lkdGggPSAyKSArCiAgICBmYWNldF9ncmlkKC4gfiBjaHJvbW9zb21lLCBzY2FsZXMgPSAiZnJlZV94Iiwgc3BhY2UgPSAiZnJlZV94IikgKwogICAgbGFicyh5ID0gIlR1bW9yIGZyYWN0aW9uIiwgeCA9ICJDaHJvbW9zb21lIikgKwogICAgeWxpbSgwLCAxKSArCiAgICB0aGVtZV9taW5pbWFsKCkgKwogICAgdGhlbWUoCiAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgICAgcGFuZWwuc3BhY2luZyA9IHVuaXQoMC4xLCAibGluZXMiKQogICAgKQogIAogICMgQ29tYmluZSBhbGwgcGFuZWxzCiAgY29tYmluZWRfcGxvdCA8LSBwMSAvIHAyIC8gcDMgLyBwNCArCiAgICBwbG90X2Fubm90YXRpb24oCiAgICAgIHRpdGxlID0gcGFzdGUoIkNvbXByZWhlbnNpdmUgR2Vub21pYyBQcm9maWxlIC0iLCBzYW1wbGVfaWQpLAogICAgICB0aGVtZSA9IHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2LCBmYWNlID0gImJvbGQiKSkKICAgICkKICAKICAjIFNhdmUKICBnZ3NhdmUoCiAgICBmaWxlLnBhdGgoVklTX0RJUiwgcGFzdGUwKHNhbXBsZV9pZCwgIl9jb21wcmVoZW5zaXZlX3Byb2ZpbGUucGRmIikpLAogICAgY29tYmluZWRfcGxvdCwKICAgIHdpZHRoID0gMTYsCiAgICBoZWlnaHQgPSAxMgogICkKICAKICByZXR1cm4oY29tYmluZWRfcGxvdCkKfQoKIyBHZW5lcmF0ZSBmb3IgYWxsIHNhbXBsZXMKZm9yIChzYW1wbGUgaW4gYygiVDEiLCAiWDEiLCAiWFgxIikpIHsKICBwcmludChjcmVhdGVfY29tcHJlaGVuc2l2ZV9wbG90KHNhbXBsZSkpCn0KYGBgCgoKCgoKCgoK