For the first step for removing the XCI escape genes, we need to annotate our variants. To make things easier, Jonathan’s code (as seen in the “Plotting VCF” file) filters out the SNPs that match our criteria. These SNPs are biallelic - which serves our purpose. Now we start with loading required libraries.
# Load libraries
library(magrittr)
library(readr)
library(ggplot2)
library(tibble)
library(tidyr)
library(dplyr)
library(readr)
library(rtracklayer)
library(stringr)
library(plyranges)
library(GenomicFeatures)
library(plyranges)
library(cowplot)
library(Seurat)
library(SeuratDisk)
library(pheatmap)
library(reshape2)
library(RColorBrewer)
library(rhdf5)
library(matrixStats)
library(EGAD)
Setting options('download.file.method.GEOquery'='auto')
Setting options('GEOquery.inmemory.gpl'=FALSE)
library(GO.db)
library(org.Hs.eg.db)
# installation
# BiocManager::install("plyranges")
Let’s regenerate the combined dataframe that includes the XCI skew information.
# Let's do this analysis for both Pilot and Second data
Warning message:
In fun(libname, pkgname) : couldn't connect to display ":0"
merged_df_1 <- read.csv("/home/youkim/Bulk_RNA_seq/XCI_analysis/Pilot_Data/Merged_analysis/20230920_XCI_genes(Pilot_Data).csv")
merged_df_2 <- read.csv("/home/youkim/Bulk_RNA_seq/XCI_analysis/Second_Data/Merged_analysis_After_QC/20230921_XCI_genes(Second_Data).csv")
merged_df <- read.csv("/home/youkim/Bulk_RNA_seq/XCI_analysis/Third_Data/XCI_genes(Third_Data).csv")
merged_df <- merged_df %>%
dplyr::select(-X.1)
combined <- rbind(merged_df_1, merged_df_2) %>% dplyr::select(-X.1)
# merge it with the third data
combined <- rbind(combined, merged_df)
Now let’s see if we could generate a heatmap that could encompass all 22 samples based on their genes
Since there were too many NAs in the first heatmap, let’s generate a new one that has a specific NA count filter.
heatmap_data <- dcast(combined, gene_name ~ sample_id, value.var = "mean_XCI_ratio")
rownames(heatmap_data) <- heatmap_data$gene_name # Set row names to gene names
heatmap_data <- heatmap_data[, -1] # Remove the gene_name column
# Let's remove B5_12764 and see what happens
heatmap_data <- heatmap_data %>%
dplyr::select(-B5_12764)
# Count the number of NAs in each row
na_count <- rowSums(is.na(heatmap_data))
# Create a list to store the heatmaps
heatmap_list <- list()
# Create a function to generate a heatmap for a given threshold
generate_heatmap <- function(threshold) {
# Filter rows with a number of NAs less than or equal to the threshold
filtered_heatmap_data <- heatmap_data[na_count <= threshold, ]
drows_3 <- dist(filtered_heatmap_data, method = "euclidean")
# Check that we have no NAs in our distance matrix now!
which(is.na(drows_3), arr.ind = T)
colors <- colorRampPalette(rev(brewer.pal(11, "RdYlBu")))(100)
annotation_helper <- combined %>%
filter(gene_name %in% rownames(filtered_heatmap_data)) %>%
arrange(gene_name) %>%
dplyr::select(gene_name, mean_exprs_ratio)
rownames(annotation_helper) <- NULL
interim <- annotation_helper$gene_name %>% duplicated()
annotation_helper <- annotation_helper[!interim,]
# double check the order
summary(rownames(filtered_heatmap_data) == annotation_helper$gene_name)
# Color for annotations
expression_ratio_palette <- colorRampPalette(c("blue", "white", "red"))(100)
# Annotation dataframe
annotation_df <- data.frame(Expression_Ratio = log10(annotation_helper$mean_exprs_ratio + 1))
rownames(annotation_df) <- rownames(filtered_heatmap_data)
p <- pheatmap(
filtered_heatmap_data,
clustering_distance_rows = drows_3, # You can choose a different clustering method if needed
clustering_distance_cols = "euclidean", # You can choose a different clustering method if needed
annotation_row = annotation_df, # Add the expression ratio annotation
annotation_colors = list(Expression_Ratio = expression_ratio_palette),
col = colorRampPalette(rev(brewer.pal(11, "RdYlBu")))(100), # Color palette
na_col = "grey", # Set the color for NA values
main = "Gene Expression Heatmap",
clustering_method = "complete",
fontsize_row = 6, # Adjust row label size
fontsize_col = 9, # Adjust column label size
cutree_rows = 2
)
return(p)
}
# Use lapply to generate the heatmaps for different thresholds
heatmaps <- lapply(thresholds, generate_heatmap)
pdf(file = "Heatmap_22_samples_NA_thresholds(20231019).pdf",
height = 14,
width = 11)
generate_heatmap(11)
generate_heatmap(10)
generate_heatmap(9)
generate_heatmap(8)
generate_heatmap(7)
generate_heatmap(6)
generate_heatmap(5)
generate_heatmap(4)
generate_heatmap(3)
generate_heatmap(2)
generate_heatmap(1)
generate_heatmap(0)
dev.off()
# Create the heatmap using pheatmap
pdf(file = "Heatmap_22_samples_fitered_NA(20231019).pdf",
height = 15,
width = 12)
p <- pheatmap(
filtered_heatmap_data,
clustering_distance_rows = drows_3, # You can choose a different clustering method if needed
clustering_distance_cols = "euclidean", # You can choose a different clustering method if needed
annotation_row = annotation_df, # Add the expression ratio annotation
annotation_colors = list(Expression_Ratio = expression_ratio_palette),
col = colorRampPalette(rev(brewer.pal(11, "RdYlBu")))(100), # Color palette
na_col = "grey", # Set the color for NA values
main = "Gene Expression Heatmap",
clustering_method = "complete",
fontsize_row = 4, # Adjust row label size
fontsize_col = 9, # Adjust column label size
cutree_rows = 2
)
p
dev.off()
object <- generate_heatmap(11)
Some major notes from the Supplementary Material of the EGAD publication: - The neighbor voting method takes a gene network and a set of annotations (gene label vectors) as input, and outputs prediction scores for each of the annotation sets, as well as the performance metric of the network.
Let’s try to bring out the EGAD RMarkdown file and try to run it in this file!
testing[[1]]
auc avg_node_degree degree_null_auc
[1,] 0.6306979 15228.83 0.6945955
Let’s also test it for the gene list derived based on the NA count filter!
read.csv("NA_filter11_genes.csv",header = 0) %>% pull(V1)
[1] "CHRDL1" "MIR223" "RAP2C−AS1" "FHL1" "MAP7D3" "GPC4" "PHKA2" "RTL8B" "BCAP31"
[10] "TAB3" "RLIM" "SMS" "MTMR1" "CD99L2" "TAF1" "RPS6KA3" "TMEM164" "SOWAHD"
[19] "MOSPD1" "SLC35A2" "WWC3" "RBM41" "RTL8A" "RTL5" "ZNF185" "DLG3" "MORF4L2"
[28] "IL13RA1" "OTUD5" "MAGT1" "GAB3" "XIAP" "GK" "PLXNA3" "ACOT9" "TLR8"
[37] "GK−AS1" "UBE2A" "TFE3" "SSR4" "IDS" "TMEM185A" "IRAK1" "ELF4" "SLC9A7"
[46] "TBC1D25" "BEX4" "NBDY" "EBP" "MAGED2" "TSPYL2" "SEPTIN6" "WDR13" "SLC25A5"
[55] "AIFM1" "FAM3A" "PDHA1" "MAMLD1" "PRPS2" "CCDC120" "GNL3L" "CRLF2" "TRMT2B"
[64] "STAG2" "ANOS1" "UXT" "CCDC22" "CYSLTR1" "PGK1" "TIMM17B" "TSIX" "SLC6A8"
[73] "FMR1" "ZBTB33" "ITM2A" "TSPAN7" "DIPK2B" "ZNF275" "GRIPAP1" "PIGA" "WDR44"
[82] "BCOR" "WASH6P" "FAM199X" "LONRF3" "TLR7" "SLC25A43" "SAT1" "ARAF" "HUWE1"
[91] "SASH3" "TMEM187" "LAMP2" "FTX" "SYN1" "ABCD1" "MECP2" "BTK" "MPP1"
[100] "ZXDB" "FLNA" "INE1" "TAF9B" "MBTPS2" "TSC22D3" "BRWD3" "PUDP" "TMSB4X"
[109] "ZNF75D" "ARSD−AS1" "RPL10" "NXT2" "VMA21" "DOCK11" "SNX12"
testing_3[[1]]
auc avg_node_degree degree_null_auc
[1,] 0.6938566 16374.84 0.7693189
Let’s check the co-expression for genes showing high XCI skew from the heatmap when NA count filter = 11
testing_4[[1]]
auc avg_node_degree degree_null_auc
[1,] 0.6301435 15577.32 0.7222064
Let’s also compare when we set the read count filter as 5 (NA counts <= 6)
testing_5[[1]]
auc avg_node_degree degree_null_auc
[1,] 0.7194659 14861.01 0.6549716
As a last point of reference, let’s perform EGAD on all genes in the heatmap for read count filter = 5.
Compare the expression values of these genes from the count matrix!
# Let's check for the expression from mapping_XCI_5
merged_exprs[mapping_XCI_5$ENSEMBL,] %>%
rowSums() %>%
mean()
[1] 567950.8
LS0tCnRpdGxlOiAiWENJX0VzY2FwZV9SZW1vdmFsICgyMDIzMTAxOCkuUm1kIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpGb3IgdGhlIGZpcnN0IHN0ZXAgZm9yIHJlbW92aW5nIHRoZSBYQ0kgZXNjYXBlIGdlbmVzLCB3ZSBuZWVkIHRvIGFubm90YXRlIG91ciB2YXJpYW50cy4KVG8gbWFrZSB0aGluZ3MgZWFzaWVyLCBKb25hdGhhbidzIGNvZGUgKGFzIHNlZW4gaW4gdGhlICJQbG90dGluZyBWQ0YiIGZpbGUpIGZpbHRlcnMgb3V0IHRoZSBTTlBzIHRoYXQgbWF0Y2ggb3VyIGNyaXRlcmlhLgpUaGVzZSBTTlBzIGFyZSBiaWFsbGVsaWMgLSB3aGljaCBzZXJ2ZXMgb3VyIHB1cnBvc2UuCk5vdyB3ZSBzdGFydCB3aXRoIGxvYWRpbmcgcmVxdWlyZWQgbGlicmFyaWVzLgoKYGBge3J9CiMgTG9hZCBsaWJyYXJpZXMKbGlicmFyeShtYWdyaXR0cikKbGlicmFyeShyZWFkcikKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHRpYmJsZSkKbGlicmFyeSh0aWR5cikKbGlicmFyeShkcGx5cikKbGlicmFyeShyZWFkcikKbGlicmFyeShydHJhY2tsYXllcikKbGlicmFyeShzdHJpbmdyKQpsaWJyYXJ5KHBseXJhbmdlcykKbGlicmFyeShHZW5vbWljRmVhdHVyZXMpCmxpYnJhcnkocGx5cmFuZ2VzKQpsaWJyYXJ5KGNvd3Bsb3QpCmxpYnJhcnkoU2V1cmF0KQpsaWJyYXJ5KFNldXJhdERpc2spCmxpYnJhcnkocGhlYXRtYXApCmxpYnJhcnkocmVzaGFwZTIpCmxpYnJhcnkoUkNvbG9yQnJld2VyKQpsaWJyYXJ5KHJoZGY1KQpsaWJyYXJ5KG1hdHJpeFN0YXRzKQpsaWJyYXJ5KEVHQUQpCmxpYnJhcnkoR08uZGIpCmxpYnJhcnkob3JnLkhzLmVnLmRiKQoKIyBpbnN0YWxsYXRpb24KIyBCaW9jTWFuYWdlcjo6aW5zdGFsbCgicGx5cmFuZ2VzIikKYGBgCgpMZXQncyByZWdlbmVyYXRlIHRoZSBjb21iaW5lZCBkYXRhZnJhbWUgdGhhdCBpbmNsdWRlcyB0aGUgWENJIHNrZXcgaW5mb3JtYXRpb24uCgpgYGB7cn0KIyBMZXQncyBkbyB0aGlzIGFuYWx5c2lzIGZvciBib3RoIFBpbG90IGFuZCBTZWNvbmQgZGF0YQptZXJnZWRfZGZfMSA8LSByZWFkLmNzdigiL2hvbWUveW91a2ltL0J1bGtfUk5BX3NlcS9YQ0lfYW5hbHlzaXMvUGlsb3RfRGF0YS9NZXJnZWRfYW5hbHlzaXMvMjAyMzA5MjBfWENJX2dlbmVzKFBpbG90X0RhdGEpLmNzdiIpCgptZXJnZWRfZGZfMiA8LSByZWFkLmNzdigiL2hvbWUveW91a2ltL0J1bGtfUk5BX3NlcS9YQ0lfYW5hbHlzaXMvU2Vjb25kX0RhdGEvTWVyZ2VkX2FuYWx5c2lzX0FmdGVyX1FDLzIwMjMwOTIxX1hDSV9nZW5lcyhTZWNvbmRfRGF0YSkuY3N2IikKCm1lcmdlZF9kZiA8LSByZWFkLmNzdigiL2hvbWUveW91a2ltL0J1bGtfUk5BX3NlcS9YQ0lfYW5hbHlzaXMvVGhpcmRfRGF0YS9YQ0lfZ2VuZXMoVGhpcmRfRGF0YSkuY3N2IikKbWVyZ2VkX2RmIDwtIG1lcmdlZF9kZiAlPiUgCiAgZHBseXI6OnNlbGVjdCgtWC4xKQoKY29tYmluZWQgPC0gcmJpbmQobWVyZ2VkX2RmXzEsIG1lcmdlZF9kZl8yKSAlPiUgZHBseXI6OnNlbGVjdCgtWC4xKQoKIyBtZXJnZSBpdCB3aXRoIHRoZSB0aGlyZCBkYXRhCmNvbWJpbmVkIDwtIHJiaW5kKGNvbWJpbmVkLCBtZXJnZWRfZGYpCgpgYGAKCk5vdyBsZXQncyBzZWUgaWYgd2UgY291bGQgZ2VuZXJhdGUgYSBoZWF0bWFwIHRoYXQgY291bGQgZW5jb21wYXNzIGFsbCAyMiBzYW1wbGVzIGJhc2VkIG9uIHRoZWlyIGdlbmVzCgpgYGB7cn0KIyBQaXZvdCB0aGUgZGF0YWZyYW1lIGludG8gYSBnZW5lcy1ieS1zYW1wbGVzIG1hdHJpeApoZWF0bWFwX2RhdGEgPC0gZGNhc3QoY29tYmluZWQsIGdlbmVfbmFtZSB+IHNhbXBsZV9pZCwgdmFsdWUudmFyID0gIm1lYW5fWENJX3JhdGlvIikKcm93bmFtZXMoaGVhdG1hcF9kYXRhKSA8LSBoZWF0bWFwX2RhdGEkZ2VuZV9uYW1lICAjIFNldCByb3cgbmFtZXMgdG8gZ2VuZSBuYW1lcwpoZWF0bWFwX2RhdGEgPC0gaGVhdG1hcF9kYXRhWywgLTFdICAjIFJlbW92ZSB0aGUgZ2VuZV9uYW1lIGNvbHVtbgoKIyBMZXQncyByZW1vdmUgQjVfMTI3NjQgYW5kIHNlZSB3aGF0IGhhcHBlbnMKaGVhdG1hcF9kYXRhIDwtIGhlYXRtYXBfZGF0YSAlPiUgCiAgZHBseXI6OnNlbGVjdCgtQjVfMTI3NjQpCgojIGhlYXRtYXBfZGF0YVtpcy5uYShoZWF0bWFwX2RhdGEpXSA8LSAiZ3JleSIKCmRyb3dzXzEgPC0gZGlzdChoZWF0bWFwX2RhdGEsIG1ldGhvZCA9ICJldWNsaWRlYW4iKQoKZHJvd3NfMVtpcy5uYShkcm93c18xKV0gPC0gbWF4KGRyb3dzXzEsbmEucm09VCkKd2hpY2goaXMubmEoZHJvd3NfMSksIGFyci5pbmQgPSBUKQpwaGVhdG1hcChkcm93c18xKQojIGhlYXRtYXBfZGF0YV9ub05BIDwtIGhlYXRtYXBfZGF0YSAlPiUKIyAgIGZpbHRlcihyb3dTdW1zKGlzLm5hKGhlYXRtYXBfZGF0YSkpID09IDApCgojIGhlYXRtYXBfbWF0cml4IDwtIGFzLm1hdHJpeChoZWF0bWFwX2RhdGEpCiMgCiMgZ2l2ZU5BcyA9IHdoaWNoKGlzLm5hKGFzLm1hdHJpeChkaXN0KGhlYXRtYXBfZGF0YSkpKSxhcnIuaW5kPVRSVUUpCiMgZ2l2ZU5BcwojIAojIHRhYiA9IHNvcnQodGFibGUoYyhnaXZlTkFzKSksZGVjcmVhc2luZz1UUlVFKQojIGNoZWNrTkEgPSBzYXBwbHkoMTpsZW5ndGgodGFiKSxmdW5jdGlvbihpKXsKIyBzdW0oaXMubmEoYXMubWF0cml4KGRpc3QoaGVhdG1hcF9kYXRhWy1hcy5udW1lcmljKG5hbWVzKHRhYlsxOmldKSksXSkpKSkKIyB9KQojIHJtdiA9IG5hbWVzKHRhYilbMTptaW4od2hpY2goY2hlY2tOQT09MCkpXQojIAojIGhlYXRtYXBfZGF0YSA9IGhlYXRtYXBfZGF0YVstYXMubnVtZXJpYyhybXYpLF0KCiMgRGVmaW5lIGEgY29sb3IgcGFsZXR0ZSBmb3IgdGhlIGhlYXRtYXAKY29sb3JzIDwtIGNvbG9yUmFtcFBhbGV0dGUocmV2KGJyZXdlci5wYWwoMTEsICJSZFlsQnUiKSkpKDEwMCkKCmFubm90YXRpb25faGVscGVyXzEgPC0gY29tYmluZWQgJT4lIAogIGZpbHRlcihnZW5lX25hbWUgJWluJSByb3duYW1lcyhoZWF0bWFwX2RhdGEpKSAlPiUgCiAgYXJyYW5nZShnZW5lX25hbWUpICU+JSAKICBkcGx5cjo6c2VsZWN0KGdlbmVfbmFtZSwgbWVhbl9leHByc19yYXRpbykgCgpyb3duYW1lcyhhbm5vdGF0aW9uX2hlbHBlcl8xKSA8LSBOVUxMCmludGVyaW0gPC0gYW5ub3RhdGlvbl9oZWxwZXJfMSRnZW5lX25hbWUgJT4lIGR1cGxpY2F0ZWQoKQphbm5vdGF0aW9uX2hlbHBlcl8xIDwtIGFubm90YXRpb25faGVscGVyXzFbIWludGVyaW0sXQoKIyBkb3VibGUgY2hlY2sgdGhlIG9yZGVyCnN1bW1hcnkocm93bmFtZXMoaGVhdG1hcF9kYXRhKSA9PSBhbm5vdGF0aW9uX2hlbHBlcl8xJGdlbmVfbmFtZSkKCiMgQ29sb3IgZm9yIGFubm90YXRpb25zCmV4cHJlc3Npb25fcmF0aW9fcGFsZXR0ZSA8LSBjb2xvclJhbXBQYWxldHRlKGMoImJsdWUiLCAid2hpdGUiLCAicmVkIikpKDEwMCkKCiMgQW5ub3RhdGlvbiBkYXRhZnJhbWUKYW5ub3RhdGlvbl9kZl8xIDwtIGRhdGEuZnJhbWUoRXhwcmVzc2lvbl9SYXRpbyA9IGxvZzEwKGFubm90YXRpb25faGVscGVyXzEkbWVhbl9leHByc19yYXRpbyArIDEpKQpyb3duYW1lcyhhbm5vdGF0aW9uX2RmXzEpIDwtIHJvd25hbWVzKGhlYXRtYXBfZGF0YSkKCiMgQ3JlYXRlIHRoZSBoZWF0bWFwIHVzaW5nIHBoZWF0bWFwCnBkZihmaWxlID0gIkhlYXRtYXBfMjJfc2FtcGxlcygyMDIzMTAxOCkucGRmIiwKICAgIGhlaWdodCA9IDEyLAogICAgd2lkdGggPSAxNSkKcCA8LSBwaGVhdG1hcCgKICBoZWF0bWFwX2RhdGEsCiAgY2x1c3RlcmluZ19kaXN0YW5jZV9yb3dzID0gZHJvd3NfMSwgICMgWW91IGNhbiBjaG9vc2UgYSBkaWZmZXJlbnQgY2x1c3RlcmluZyBtZXRob2QgaWYgbmVlZGVkCiAgY2x1c3RlcmluZ19kaXN0YW5jZV9jb2xzID0gImV1Y2xpZGVhbiIsICAjIFlvdSBjYW4gY2hvb3NlIGEgZGlmZmVyZW50IGNsdXN0ZXJpbmcgbWV0aG9kIGlmIG5lZWRlZAogIGFubm90YXRpb25fcm93ID0gYW5ub3RhdGlvbl9kZl8xLCAgIyBBZGQgdGhlIGV4cHJlc3Npb24gcmF0aW8gYW5ub3RhdGlvbgogIGFubm90YXRpb25fY29sb3JzID0gbGlzdChFeHByZXNzaW9uX1JhdGlvID0gZXhwcmVzc2lvbl9yYXRpb19wYWxldHRlKSwKICBjb2wgPSBjb2xvclJhbXBQYWxldHRlKHJldihicmV3ZXIucGFsKDExLCAiUmRZbEJ1IikpKSgxMDApLCAgIyBDb2xvciBwYWxldHRlCiAgbmFfY29sID0gImdyZXkiLCAgIyBTZXQgdGhlIGNvbG9yIGZvciBOQSB2YWx1ZXMKICBtYWluID0gIkdlbmUgRXhwcmVzc2lvbiBIZWF0bWFwIiwKICBjbHVzdGVyaW5nX21ldGhvZCA9ICJjb21wbGV0ZSIsCiAgZm9udHNpemVfcm93ID0gMywgICMgQWRqdXN0IHJvdyBsYWJlbCBzaXplCiAgZm9udHNpemVfY29sID0gOSAgIyBBZGp1c3QgY29sdW1uIGxhYmVsIHNpemUKKQpwCmRldi5vZmYoKQoKcm93X29yZGVyaW5nXzEgPC0gcCR0cmVlX3JvdwpnZW5lX2xpc3RfMSA8LSByb3dfb3JkZXJpbmdfMSRsYWJlbHNbcm93X29yZGVyaW5nXzEkb3JkZXJdCmFubm90YXRpb25fZGZfMSA8LSByb3duYW1lc190b19jb2x1bW4oYW5ub3RhdGlvbl9kZl8xLCAiZ2VuZXMiKQp0aWJibGVfMSA8LSB0aWJibGUoZ2VuZXMgPSBnZW5lX2xpc3RfMSkKdGliYmxlXzEgPC0gaW5uZXJfam9pbih0aWJibGVfMSwgYW5ub3RhdGlvbl9kZl8xKQoKcGRmKGZpbGUgPSAiSGVhdG1hcF9Bbm5vdGF0aW9uX2ZpbHRlcjUoMjAyMzA5MjkpLnBkZiIsCiAgICBoZWlnaHQgPSAxNSwKICAgIHdpZHRoID0gMykKdGliYmxlXzEgJT4lCiAgbXV0YXRlKGdlbmVzID0gZmFjdG9yKGdlbmVzLCBsZXZlbHMgPSByZXYoZ2VuZXMpKSkgJT4lIAogIGdncGxvdChhZXMoeCA9IDEsIHkgPSBnZW5lcywgZmlsbCA9IEV4cHJlc3Npb25fUmF0aW8pKSArIAogIGdlb21fdGlsZSgpICsKICBzY2FsZV9maWxsX2dyYWRpZW50KGxvdyA9ICJ3aGl0ZSIsIGhpZ2ggPSAicHVycGxlIikgKyAgIyBBZGp1c3QgdGhlIGNvbG9yIHNjYWxlCiAgY29vcmRfY2FydGVzaWFuKGV4cGFuZCA9IEZBTFNFKSArCiAgdGhlbWVfYncoKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCkpCmRldi5vZmYoKQoKYGBgCgpTaW5jZSB0aGVyZSB3ZXJlIHRvbyBtYW55IE5BcyBpbiB0aGUgZmlyc3QgaGVhdG1hcCwgbGV0J3MgZ2VuZXJhdGUgYSBuZXcgb25lIHRoYXQgaGFzIGEgc3BlY2lmaWMgTkEgY291bnQgZmlsdGVyLgoKYGBge3J9CmhlYXRtYXBfZGF0YSA8LSBkY2FzdChjb21iaW5lZCwgZ2VuZV9uYW1lIH4gc2FtcGxlX2lkLCB2YWx1ZS52YXIgPSAibWVhbl9YQ0lfcmF0aW8iKQpyb3duYW1lcyhoZWF0bWFwX2RhdGEpIDwtIGhlYXRtYXBfZGF0YSRnZW5lX25hbWUgICMgU2V0IHJvdyBuYW1lcyB0byBnZW5lIG5hbWVzCmhlYXRtYXBfZGF0YSA8LSBoZWF0bWFwX2RhdGFbLCAtMV0gICMgUmVtb3ZlIHRoZSBnZW5lX25hbWUgY29sdW1uCgojIExldCdzIHJlbW92ZSBCNV8xMjc2NCBhbmQgc2VlIHdoYXQgaGFwcGVucwpoZWF0bWFwX2RhdGEgPC0gaGVhdG1hcF9kYXRhICU+JSAKICBkcGx5cjo6c2VsZWN0KC1CNV8xMjc2NCkKCiMgQ291bnQgdGhlIG51bWJlciBvZiBOQXMgaW4gZWFjaCByb3cKbmFfY291bnQgPC0gcm93U3Vtcyhpcy5uYShoZWF0bWFwX2RhdGEpKQoKIyBDcmVhdGUgYSBsaXN0IHRvIHN0b3JlIHRoZSBoZWF0bWFwcwpoZWF0bWFwX2xpc3QgPC0gbGlzdCgpCgojIENyZWF0ZSBhIGZ1bmN0aW9uIHRvIGdlbmVyYXRlIGEgaGVhdG1hcCBmb3IgYSBnaXZlbiB0aHJlc2hvbGQKZ2VuZXJhdGVfaGVhdG1hcCA8LSBmdW5jdGlvbih0aHJlc2hvbGQpIHsKICAgIAogICAgIyBGaWx0ZXIgcm93cyB3aXRoIGEgbnVtYmVyIG9mIE5BcyBsZXNzIHRoYW4gb3IgZXF1YWwgdG8gdGhlIHRocmVzaG9sZAogICAgZmlsdGVyZWRfaGVhdG1hcF9kYXRhIDwtIGhlYXRtYXBfZGF0YVtuYV9jb3VudCA8PSB0aHJlc2hvbGQsIF0KICAgIAogICAgZHJvd3NfMyA8LSBkaXN0KGZpbHRlcmVkX2hlYXRtYXBfZGF0YSwgbWV0aG9kID0gImV1Y2xpZGVhbiIpCiAgICAjIENoZWNrIHRoYXQgd2UgaGF2ZSBubyBOQXMgaW4gb3VyIGRpc3RhbmNlIG1hdHJpeCBub3chCiAgICB3aGljaChpcy5uYShkcm93c18zKSwgYXJyLmluZCA9IFQpCiAgICAKICAgIGNvbG9ycyA8LSBjb2xvclJhbXBQYWxldHRlKHJldihicmV3ZXIucGFsKDExLCAiUmRZbEJ1IikpKSgxMDApCiAgICAKICAgIGFubm90YXRpb25faGVscGVyIDwtIGNvbWJpbmVkICU+JSAKICAgICAgZmlsdGVyKGdlbmVfbmFtZSAlaW4lIHJvd25hbWVzKGZpbHRlcmVkX2hlYXRtYXBfZGF0YSkpICU+JSAKICAgICAgYXJyYW5nZShnZW5lX25hbWUpICU+JSAKICAgICAgZHBseXI6OnNlbGVjdChnZW5lX25hbWUsIG1lYW5fZXhwcnNfcmF0aW8pIAogICAgCiAgICByb3duYW1lcyhhbm5vdGF0aW9uX2hlbHBlcikgPC0gTlVMTAogICAgaW50ZXJpbSA8LSBhbm5vdGF0aW9uX2hlbHBlciRnZW5lX25hbWUgJT4lIGR1cGxpY2F0ZWQoKQogICAgYW5ub3RhdGlvbl9oZWxwZXIgPC0gYW5ub3RhdGlvbl9oZWxwZXJbIWludGVyaW0sXQogICAgCiAgICAjIGRvdWJsZSBjaGVjayB0aGUgb3JkZXIKICAgIHN1bW1hcnkocm93bmFtZXMoZmlsdGVyZWRfaGVhdG1hcF9kYXRhKSA9PSBhbm5vdGF0aW9uX2hlbHBlciRnZW5lX25hbWUpCiAgICAKICAgICMgQ29sb3IgZm9yIGFubm90YXRpb25zCiAgICBleHByZXNzaW9uX3JhdGlvX3BhbGV0dGUgPC0gY29sb3JSYW1wUGFsZXR0ZShjKCJibHVlIiwgIndoaXRlIiwgInJlZCIpKSgxMDApCiAgICAKICAgICMgQW5ub3RhdGlvbiBkYXRhZnJhbWUKICAgIGFubm90YXRpb25fZGYgPC0gZGF0YS5mcmFtZShFeHByZXNzaW9uX1JhdGlvID0gbG9nMTAoYW5ub3RhdGlvbl9oZWxwZXIkbWVhbl9leHByc19yYXRpbyArIDEpKQogICAgcm93bmFtZXMoYW5ub3RhdGlvbl9kZikgPC0gcm93bmFtZXMoZmlsdGVyZWRfaGVhdG1hcF9kYXRhKQogICAgCiAgICBwIDwtIHBoZWF0bWFwKAogICAgICBmaWx0ZXJlZF9oZWF0bWFwX2RhdGEsCiAgICAgIGNsdXN0ZXJpbmdfZGlzdGFuY2Vfcm93cyA9IGRyb3dzXzMsICAjIFlvdSBjYW4gY2hvb3NlIGEgZGlmZmVyZW50IGNsdXN0ZXJpbmcgbWV0aG9kIGlmIG5lZWRlZAogICAgICBjbHVzdGVyaW5nX2Rpc3RhbmNlX2NvbHMgPSAiZXVjbGlkZWFuIiwgICMgWW91IGNhbiBjaG9vc2UgYSBkaWZmZXJlbnQgY2x1c3RlcmluZyBtZXRob2QgaWYgbmVlZGVkCiAgICAgIGFubm90YXRpb25fcm93ID0gYW5ub3RhdGlvbl9kZiwgICMgQWRkIHRoZSBleHByZXNzaW9uIHJhdGlvIGFubm90YXRpb24KICAgICAgYW5ub3RhdGlvbl9jb2xvcnMgPSBsaXN0KEV4cHJlc3Npb25fUmF0aW8gPSBleHByZXNzaW9uX3JhdGlvX3BhbGV0dGUpLAogICAgICBjb2wgPSBjb2xvclJhbXBQYWxldHRlKHJldihicmV3ZXIucGFsKDExLCAiUmRZbEJ1IikpKSgxMDApLCAgIyBDb2xvciBwYWxldHRlCiAgICAgIG5hX2NvbCA9ICJncmV5IiwgICMgU2V0IHRoZSBjb2xvciBmb3IgTkEgdmFsdWVzCiAgICAgIG1haW4gPSAiR2VuZSBFeHByZXNzaW9uIEhlYXRtYXAiLAogICAgICBjbHVzdGVyaW5nX21ldGhvZCA9ICJjb21wbGV0ZSIsCiAgICAgIGZvbnRzaXplX3JvdyA9IDYsICAjIEFkanVzdCByb3cgbGFiZWwgc2l6ZQogICAgICBmb250c2l6ZV9jb2wgPSA5LCAgIyBBZGp1c3QgY29sdW1uIGxhYmVsIHNpemUKICAgICAgY3V0cmVlX3Jvd3MgPSAyCiAgICApCiAgcmV0dXJuKHApCn0KCiMgVXNlIGxhcHBseSB0byBnZW5lcmF0ZSB0aGUgaGVhdG1hcHMgZm9yIGRpZmZlcmVudCB0aHJlc2hvbGRzCmhlYXRtYXBzIDwtIGxhcHBseSh0aHJlc2hvbGRzLCBnZW5lcmF0ZV9oZWF0bWFwKQoKCnBkZihmaWxlID0gIkhlYXRtYXBfMjJfc2FtcGxlc19OQV90aHJlc2hvbGRzKDIwMjMxMDE5KS5wZGYiLAogICAgaGVpZ2h0ID0gMTQsCiAgICB3aWR0aCA9IDExKQpnZW5lcmF0ZV9oZWF0bWFwKDExKQpnZW5lcmF0ZV9oZWF0bWFwKDEwKQpnZW5lcmF0ZV9oZWF0bWFwKDkpCmdlbmVyYXRlX2hlYXRtYXAoOCkKZ2VuZXJhdGVfaGVhdG1hcCg3KQpnZW5lcmF0ZV9oZWF0bWFwKDYpCmdlbmVyYXRlX2hlYXRtYXAoNSkKZ2VuZXJhdGVfaGVhdG1hcCg0KQpnZW5lcmF0ZV9oZWF0bWFwKDMpCmdlbmVyYXRlX2hlYXRtYXAoMikKZ2VuZXJhdGVfaGVhdG1hcCgxKQpnZW5lcmF0ZV9oZWF0bWFwKDApCiAgZGV2Lm9mZigpCgojIENyZWF0ZSB0aGUgaGVhdG1hcCB1c2luZyBwaGVhdG1hcApwZGYoZmlsZSA9ICJIZWF0bWFwXzIyX3NhbXBsZXNfZml0ZXJlZF9OQSgyMDIzMTAxOSkucGRmIiwKICAgIGhlaWdodCA9IDE1LAogICAgd2lkdGggPSAxMikKcCA8LSBwaGVhdG1hcCgKICBmaWx0ZXJlZF9oZWF0bWFwX2RhdGEsCiAgY2x1c3RlcmluZ19kaXN0YW5jZV9yb3dzID0gZHJvd3NfMywgICMgWW91IGNhbiBjaG9vc2UgYSBkaWZmZXJlbnQgY2x1c3RlcmluZyBtZXRob2QgaWYgbmVlZGVkCiAgY2x1c3RlcmluZ19kaXN0YW5jZV9jb2xzID0gImV1Y2xpZGVhbiIsICAjIFlvdSBjYW4gY2hvb3NlIGEgZGlmZmVyZW50IGNsdXN0ZXJpbmcgbWV0aG9kIGlmIG5lZWRlZAogIGFubm90YXRpb25fcm93ID0gYW5ub3RhdGlvbl9kZiwgICMgQWRkIHRoZSBleHByZXNzaW9uIHJhdGlvIGFubm90YXRpb24KICBhbm5vdGF0aW9uX2NvbG9ycyA9IGxpc3QoRXhwcmVzc2lvbl9SYXRpbyA9IGV4cHJlc3Npb25fcmF0aW9fcGFsZXR0ZSksCiAgY29sID0gY29sb3JSYW1wUGFsZXR0ZShyZXYoYnJld2VyLnBhbCgxMSwgIlJkWWxCdSIpKSkoMTAwKSwgICMgQ29sb3IgcGFsZXR0ZQogIG5hX2NvbCA9ICJncmV5IiwgICMgU2V0IHRoZSBjb2xvciBmb3IgTkEgdmFsdWVzCiAgbWFpbiA9ICJHZW5lIEV4cHJlc3Npb24gSGVhdG1hcCIsCiAgY2x1c3RlcmluZ19tZXRob2QgPSAiY29tcGxldGUiLAogIGZvbnRzaXplX3JvdyA9IDQsICAjIEFkanVzdCByb3cgbGFiZWwgc2l6ZQogIGZvbnRzaXplX2NvbCA9IDksICAjIEFkanVzdCBjb2x1bW4gbGFiZWwgc2l6ZQogIGN1dHJlZV9yb3dzID0gMgopCnAKZGV2Lm9mZigpCgpvYmplY3QgPC0gZ2VuZXJhdGVfaGVhdG1hcCgxMSkKCmBgYAoKClNvbWUgbWFqb3Igbm90ZXMgZnJvbSB0aGUgU3VwcGxlbWVudGFyeSBNYXRlcmlhbCBvZiB0aGUgRUdBRCBwdWJsaWNhdGlvbjoKLSBUaGUgbmVpZ2hib3Igdm90aW5nIG1ldGhvZCB0YWtlcyBhIGdlbmUgbmV0d29yayBhbmQgYSBzZXQgb2YgYW5ub3RhdGlvbnMgKGdlbmUgbGFiZWwgdmVjdG9ycykgYXMgaW5wdXQsIGFuZCBvdXRwdXRzIHByZWRpY3Rpb24gc2NvcmVzIGZvciBlYWNoIG9mIHRoZSBhbm5vdGF0aW9uIHNldHMsIGFzIHdlbGwgYXMgdGhlIHBlcmZvcm1hbmNlIG1ldHJpYyBvZiB0aGUgbmV0d29yay4KCmBgYHtyfQojbGlzdCBzdHJ1Y3R1cmUKZ2V0d2QoKQpoNWxzKCJ+L0VHQUQvaHVtYW5fSENfQWdnTmV0LmhkZjUiKQoKY29jb25ldCA8LSBoNXJlYWQoIn4vRUdBRC9odW1hbl9IQ19BZ2dOZXQuaGRmNSIsICIvYWdnIikKY29jb25ldCA8LSBhcy5kYXRhLmZyYW1lKGNvY29uZXQpCgojIENoZWNrIHRoZSBkaW1lbnNpb24gb2YgdGhlIGh1bWFuIGdlbmUgY28tZXhwcmVzc2lvbiBtYXRyaXgKZGltKGNvY29uZXQpCgpjb2xuYW1lcyhjb2NvbmV0KSA8LSBoNXJlYWQoIn4vRUdBRC9odW1hbl9IQ19BZ2dOZXQuaGRmNSIsICIvY29sIikKcm93bmFtZXMoY29jb25ldCkgPC0gaDVyZWFkKCJ+L0VHQUQvaHVtYW5fSENfQWdnTmV0LmhkZjUiLCAiL2NvbCIpCgojIFRoZSBmb2xsb3dpbmcgaXMgYSB0ZW1wbGF0ZSBvZiBob3cgdG8gdXNlIHRoaXMgdG9vbAptYXBwaW5nIDwtIG1hcElkcyhvcmcuSHMuZWcuZGIsIAogICAgICAgICAgICAgICAgICBjb2xuYW1lcyhjb2NvbmV0KSwgCiAgICAgICAgICAgICAgICAgICdTWU1CT0wnLAogICAgICAgICAgICAgICAgICAnRU5TRU1CTCcpCgptYXBwaW5nICU8PiUgCiAgYXNfdGliYmxlKHJvd25hbWVzID0gIkVOU0VNQkwiKSAlPiUgCiAgZHBseXI6OnJlbmFtZShuY2JpID0gdmFsdWUpIAoKIyBjaGVjayBmb3IgY29sdW1uIG5hbWVzCmNvbG5hbWVzKG1hcHBpbmcpCgojIFJlbW92ZSB0aGUgTkFzCm1hcHBpbmcgJTw+JSBmaWx0ZXIoIWlzLm5hKG5jYmkpKQoKIyByZW1vdmUgMSBuY2JpIHRvIG1hbnkgZ2VuZXMgKHJlbW92ZSB0aGUgZHVwbGljYXRlcykKZGV0YWNoKHBhY2thZ2U6cGx5cmFuZ2VzLHVubG9hZD1UUlVFKQoKZHVwX2lkcyA8LSBtYXBwaW5nICU+JSAKICBncm91cF9ieShuY2JpKSAlPiUgCiAgZHBseXI6OnN1bW1hcml6ZShjb3VudHMgPSBuKCkpICU+JSAKICBkcGx5cjo6ZmlsdGVyKGNvdW50cyAhPSAxKSAlPiUgCiAgZHBseXI6OnB1bGwobmNiaSkKCm1hcHBpbmcgJTw+JSAKICBmaWx0ZXIoIShuY2JpICVpbiUgZHVwX2lkcykpCgojY2hlY2sgZm9yIDEtbWFueSBvciBtYW55LTEKbWFwcGluZyAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KG5jYmkpICU+JSAKICBkcGx5cjo6Y291bnQoKSAlPiUgCiAgZmlsdGVyKG4gIT0gMSkKCm1hcHBpbmcgJT4lIAogIGdyb3VwX2J5KEVOU0VNQkwpICU+JSAKICBkcGx5cjo6Y291bnQoKSAlPiUgCiAgZmlsdGVyKG4gIT0gMSkKCmRpbShtYXBwaW5nKQpgYGAKCkxldCdzIHRyeSB0byBicmluZyBvdXQgdGhlIEVHQUQgUk1hcmtkb3duIGZpbGUgYW5kIHRyeSB0byBydW4gaXQgaW4gdGhpcyBmaWxlIQoKYGBge3J9CiMgQ2FsbCBhbGwgdGhlIFhDSSBza2V3IGdlbmVzClhDSV9nZW5lcyA8LSByb3duYW1lcyhoZWF0bWFwX2RhdGEpCgptYXBwaW5nX1hDSSA8LSBtYXBJZHMob3JnLkhzLmVnLmRiLCAKICAgICAgICAgICAgICAgICAgWENJX2dlbmVzLCAKICAgICAgICAgICAgICAgICAgJ0VOU0VNQkwnLAogICAgICAgICAgICAgICAgICAnU1lNQk9MJykKCm1hcHBpbmdfWENJICU8PiUgCiAgYXNfdGliYmxlKHJvd25hbWVzID0gImdlbmUiKSAlPiUgCiAgZHBseXI6OnJlbmFtZShFTlNFTUJMID0gdmFsdWUpCgojIFJlbW92ZSB0aGUgTkFzCm1hcHBpbmdfWENJICU8PiUgZmlsdGVyKCFpcy5uYShFTlNFTUJMKSkKCiMgUmVtb3ZlIGR1cGxpY2F0ZXMKZHVwX2lkcyA8LSBtYXBwaW5nX1hDSSAlPiUgCiAgZ3JvdXBfYnkoZ2VuZSkgJT4lIAogIGRwbHlyOjpzdW1tYXJpemUoY291bnRzID0gbigpKSAlPiUgCiAgZHBseXI6OmZpbHRlcihjb3VudHMgIT0gMSkgJT4lIAogIGRwbHlyOjpwdWxsKGdlbmUpCgojIE5vIGR1cGxpY2F0ZXMKbWFwcGluZyAlPD4lIAogIGZpbHRlcighKG5jYmkgJWluJSBkdXBfaWRzKSkKCiNjaGVjayBmb3IgMS1tYW55IG9yIG1hbnktMQptYXBwaW5nX1hDSSAlPiUgCiAgZ3JvdXBfYnkoRU5TRU1CTCkgJT4lIAogIGRwbHlyOjpjb3VudCgpICU+JSAKICBmaWx0ZXIobiAhPSAxKQptYXBwaW5nX1hDSSAlPiUgCiAgZ3JvdXBfYnkoZ2VuZSkgJT4lIAogIGRwbHlyOjpjb3VudCgpICU+JSAKICBmaWx0ZXIobiAhPSAxKQoKYGBgCgpgYGB7cn0KYW5ub3RhdGlvbnNfMSA8LSB0aWJibGUoZ2VuZSA9IHJvd25hbWVzKGNvY29uZXQpKQphbm5vdGF0aW9uc18xIDwtIGFubm90YXRpb25zXzEgJT4lIAogIG11dGF0ZShYQ0lfc2tldyA9IGdlbmUgJWluJSAobWFwcGluZ19YQ0kgJT4lIHB1bGwoRU5TRU1CTCkpKSAlPiUKICBtdXRhdGUoWENJX3NrZXcgPSBhcy5udW1lcmljKFhDSV9za2V3KSkgCmhlYWQoYW5ub3RhdGlvbnNfMSkKCmFubm90YXRpb25zXzEgPC0gYW5ub3RhdGlvbnNfMSAlPiUgCiAgYXMuZGF0YS5mcmFtZSgpCgpyb3duYW1lcyhhbm5vdGF0aW9uc18xKSA8LSBhbm5vdGF0aW9uc18xJGdlbmUKYW5ub3RhdGlvbnNfMSAlPD4lIAogIGRwbHlyOjpzZWxlY3QoLWdlbmUpCgojIDIuMy41IEJ1aWxkaW5nIG5ldHdvcmtzCiMgaGlzdCA8LSBwbG90X2Rpc3RyaWJ1dGlvbihub2RlX2RlZ3JlZShjb2NvbmV0KSwgCiMgICAgICAgICAgICAgICAgICAgICAgICAgICBiPTEwLAojICAgICAgICAgICAgICAgICAgICAgICAgICAgeGxpbT1jKDAsMTQpLCAKIyAgICAgICAgICAgICAgICAgICAgICAgICAgIHlsaW09YygwLDIpLCAKIyAgICAgICAgICAgICAgICAgICAgICAgICAgIHhsYWI9Ik5vZGUgZGVncmVlIikKCiMgTGV0J3MgcnVuIHRoaXMgb24gRUdBRAp0ZXN0aW5nIDwtIHJ1bl9HQkEoYXMubWF0cml4KGNvY29uZXQpLCAKICAgICAgICAgICAgICAgICAgIGFzLm1hdHJpeChhbm5vdGF0aW9uc18xKSkKdGVzdGluZ1tbMV1dCmBgYAoKTGV0J3MgYWxzbyB0ZXN0IGl0IGZvciB0aGUgZ2VuZSBsaXN0IGRlcml2ZWQgYmFzZWQgb24gdGhlIE5BIGNvdW50IGZpbHRlciEKCmBgYHtyfQpOQV9maWx0ZXIxMV9nZW5lcyA8LSByZWFkLmNzdigiTkFfZmlsdGVyMTFfZ2VuZXMuY3N2IixoZWFkZXIgPSAwKSAlPiUgcHVsbChWMSkKbWFwcGluZ19YQ0lfMiA8LSBtYXBJZHMob3JnLkhzLmVnLmRiLCAKICAgICAgICAgICAgICAgICAgTkFfZmlsdGVyMTFfZ2VuZXMsIAogICAgICAgICAgICAgICAgICAnRU5TRU1CTCcsCiAgICAgICAgICAgICAgICAgICdTWU1CT0wnKQoKbWFwcGluZ19YQ0lfMiAlPD4lIAogIGFzX3RpYmJsZShyb3duYW1lcyA9ICJnZW5lIikgJT4lIAogIGRwbHlyOjpyZW5hbWUoRU5TRU1CTCA9IHZhbHVlKQoKIyBSZW1vdmUgdGhlIE5BcwptYXBwaW5nX1hDSV8yICU8PiUgZmlsdGVyKCFpcy5uYShFTlNFTUJMKSkKCiNjaGVjayBmb3IgMS1tYW55IG9yIG1hbnktMQptYXBwaW5nX1hDSV8yICU+JSAKICBncm91cF9ieShFTlNFTUJMKSAlPiUgCiAgZHBseXI6OmNvdW50KCkgJT4lIAogIGZpbHRlcihuICE9IDEpCm1hcHBpbmdfWENJXzIgJT4lIAogIGdyb3VwX2J5KGdlbmUpICU+JSAKICBkcGx5cjo6Y291bnQoKSAlPiUgCiAgZmlsdGVyKG4gIT0gMSkKCmFubm90YXRpb25zXzIgPC0gdGliYmxlKGdlbmUgPSByb3duYW1lcyhjb2NvbmV0KSkKYW5ub3RhdGlvbnNfMiA8LSBhbm5vdGF0aW9uc18yICU+JSAKICBtdXRhdGUoWENJX3NrZXcgPSBnZW5lICVpbiUgKG1hcHBpbmdfWENJXzIgJT4lIAogICAgICAgICAgICAgICAgICAgICAgICAgIHB1bGwoRU5TRU1CTCkpKSAlPiUKICBtdXRhdGUoWENJX3NrZXcgPSBhcy5udW1lcmljKFhDSV9za2V3KSkgCmhlYWQoYW5ub3RhdGlvbnNfMikKCmFubm90YXRpb25zXzIgPC0gYW5ub3RhdGlvbnNfMiAlPiUgCiAgYXMuZGF0YS5mcmFtZSgpCgpyb3duYW1lcyhhbm5vdGF0aW9uc18yKSA8LSBhbm5vdGF0aW9uc18yJGdlbmUKYW5ub3RhdGlvbnNfMiAlPD4lIAogIGRwbHlyOjpzZWxlY3QoLWdlbmUpCgojIDIuMy41IEJ1aWxkaW5nIG5ldHdvcmtzCiMgaGlzdCA8LSBwbG90X2Rpc3RyaWJ1dGlvbihub2RlX2RlZ3JlZShjb2NvbmV0KSwgCiMgICAgICAgICAgICAgICAgICAgICAgICAgICBiPTEwLAojICAgICAgICAgICAgICAgICAgICAgICAgICAgeGxpbT1jKDAsMTQpLCAKIyAgICAgICAgICAgICAgICAgICAgICAgICAgIHlsaW09YygwLDIpLCAKIyAgICAgICAgICAgICAgICAgICAgICAgICAgIHhsYWI9Ik5vZGUgZGVncmVlIikKCiMgTGV0J3MgcnVuIHRoaXMgb24gRUdBRAp0ZXN0aW5nXzIgPC0gcnVuX0dCQShhcy5tYXRyaXgoY29jb25ldCksIAogICAgICAgICAgICAgICAgICAgYXMubWF0cml4KGFubm90YXRpb25zXzIpKQoKdGVzdGluZ18yW1sxXV0KYGBgCgpgYGB7cn0Kbm9OQV9nZW5lcyA8LSByZWFkLmNzdigibm9OQV9nZW5lcy5jc3YiLGhlYWRlciA9IDApICU+JSBwdWxsKFYxKQptYXBwaW5nX1hDSV8zIDwtIG1hcElkcyhvcmcuSHMuZWcuZGIsIAogICAgICAgICAgICAgICAgICBub05BX2dlbmVzLCAKICAgICAgICAgICAgICAgICAgJ0VOU0VNQkwnLAogICAgICAgICAgICAgICAgICAnU1lNQk9MJykKCm1hcHBpbmdfWENJXzMgJTw+JSAKICBhc190aWJibGUocm93bmFtZXMgPSAiZ2VuZSIpICU+JSAKICBkcGx5cjo6cmVuYW1lKEVOU0VNQkwgPSB2YWx1ZSkKCiMgUmVtb3ZlIHRoZSBOQXMKbWFwcGluZ19YQ0lfMyAlPD4lIGZpbHRlcighaXMubmEoRU5TRU1CTCkpCgojY2hlY2sgZm9yIDEtbWFueSBvciBtYW55LTEKbWFwcGluZ19YQ0lfMyAlPiUgCiAgZ3JvdXBfYnkoRU5TRU1CTCkgJT4lIAogIGRwbHlyOjpjb3VudCgpICU+JSAKICBmaWx0ZXIobiAhPSAxKQptYXBwaW5nX1hDSV8zICU+JSAKICBncm91cF9ieShnZW5lKSAlPiUgCiAgZHBseXI6OmNvdW50KCkgJT4lIAogIGZpbHRlcihuICE9IDEpCgphbm5vdGF0aW9uc18zIDwtIHRpYmJsZShnZW5lID0gcm93bmFtZXMoY29jb25ldCkpCmFubm90YXRpb25zXzMgPC0gYW5ub3RhdGlvbnNfMyAlPiUgCiAgbXV0YXRlKFhDSV9za2V3ID0gZ2VuZSAlaW4lIChtYXBwaW5nX1hDSV8zICU+JSAKICAgICAgICAgICAgICAgICAgICAgICAgICBwdWxsKEVOU0VNQkwpKSkgJT4lCiAgbXV0YXRlKFhDSV9za2V3ID0gYXMubnVtZXJpYyhYQ0lfc2tldykpIApkaW0oYW5ub3RhdGlvbnNfMykKCmFubm90YXRpb25zXzMgPC0gYW5ub3RhdGlvbnNfMyAlPiUgCiAgYXMuZGF0YS5mcmFtZSgpCgpyb3duYW1lcyhhbm5vdGF0aW9uc18zKSA8LSBhbm5vdGF0aW9uc18zJGdlbmUKYW5ub3RhdGlvbnNfMyAlPD4lIAogIGRwbHlyOjpzZWxlY3QoLWdlbmUpCgojIDIuMy41IEJ1aWxkaW5nIG5ldHdvcmtzCiMgaGlzdCA8LSBwbG90X2Rpc3RyaWJ1dGlvbihub2RlX2RlZ3JlZShjb2NvbmV0KSwgCiMgICAgICAgICAgICAgICAgICAgICAgICAgICBiPTEwLAojICAgICAgICAgICAgICAgICAgICAgICAgICAgeGxpbT1jKDAsMTQpLCAKIyAgICAgICAgICAgICAgICAgICAgICAgICAgIHlsaW09YygwLDIpLCAKIyAgICAgICAgICAgICAgICAgICAgICAgICAgIHhsYWI9Ik5vZGUgZGVncmVlIikKCiMgTGV0J3MgcnVuIHRoaXMgb24gRUdBRAp0ZXN0aW5nXzMgPC0gcnVuX0dCQShhcy5tYXRyaXgoY29jb25ldCksIAogICAgICAgICAgICAgICAgICAgYXMubWF0cml4KGFubm90YXRpb25zXzMpLCBtaW4gPSA1KQoKCgp0ZXN0aW5nXzNbWzFdXQpgYGAKCkxldCdzIGNoZWNrIHRoZSBjby1leHByZXNzaW9uIGZvciBnZW5lcyBzaG93aW5nIGhpZ2ggWENJIHNrZXcgZnJvbSB0aGUgaGVhdG1hcCB3aGVuIE5BIGNvdW50IGZpbHRlciA9IDExCgpgYGB7cn0KTkFfZmlsdGVyMTFfWENJX3NrZXdfZ2VuZXMgPC0gcmVhZC5jc3YoIk5BX2ZpbHRlcjExX1hDSV9za2V3X2dlbmVzLmNzdiIsaGVhZGVyID0gMCkgJT4lIHB1bGwoVjEpCm1hcHBpbmdfWENJXzQgPC0gbWFwSWRzKG9yZy5Icy5lZy5kYiwgCiAgICAgICAgICAgICAgICAgIE5BX2ZpbHRlcjExX1hDSV9za2V3X2dlbmVzLCAKICAgICAgICAgICAgICAgICAgJ0VOU0VNQkwnLAogICAgICAgICAgICAgICAgICAnU1lNQk9MJykKCm1hcHBpbmdfWENJXzQgJTw+JSAKICBhc190aWJibGUocm93bmFtZXMgPSAiZ2VuZSIpICU+JSAKICBkcGx5cjo6cmVuYW1lKEVOU0VNQkwgPSB2YWx1ZSkKCiMgUmVtb3ZlIHRoZSBOQXMKbWFwcGluZ19YQ0lfNCAlPD4lIGZpbHRlcighaXMubmEoRU5TRU1CTCkpCgojY2hlY2sgZm9yIDEtbWFueSBvciBtYW55LTEKbWFwcGluZ19YQ0lfNCAlPiUgCiAgZ3JvdXBfYnkoRU5TRU1CTCkgJT4lIAogIGRwbHlyOjpjb3VudCgpICU+JSAKICBmaWx0ZXIobiAhPSAxKQptYXBwaW5nX1hDSV80ICU+JSAKICBncm91cF9ieShnZW5lKSAlPiUgCiAgZHBseXI6OmNvdW50KCkgJT4lIAogIGZpbHRlcihuICE9IDEpCgphbm5vdGF0aW9uc180IDwtIHRpYmJsZShnZW5lID0gcm93bmFtZXMoY29jb25ldCkpCmFubm90YXRpb25zXzQgPC0gYW5ub3RhdGlvbnNfNCAlPiUgCiAgbXV0YXRlKFhDSV9za2V3ID0gZ2VuZSAlaW4lIChtYXBwaW5nX1hDSV80ICU+JSAKICAgICAgICAgICAgICAgICAgICAgICAgICBwdWxsKEVOU0VNQkwpKSkgJT4lCiAgbXV0YXRlKFhDSV9za2V3ID0gYXMubnVtZXJpYyhYQ0lfc2tldykpIApkaW0oYW5ub3RhdGlvbnNfNCkKCmFubm90YXRpb25zXzQgPC0gYW5ub3RhdGlvbnNfNCAlPiUgCiAgYXMuZGF0YS5mcmFtZSgpCgpyb3duYW1lcyhhbm5vdGF0aW9uc180KSA8LSBhbm5vdGF0aW9uc180JGdlbmUKYW5ub3RhdGlvbnNfNCAlPD4lIAogIGRwbHlyOjpzZWxlY3QoLWdlbmUpCgojIDIuMy41IEJ1aWxkaW5nIG5ldHdvcmtzCiMgaGlzdCA8LSBwbG90X2Rpc3RyaWJ1dGlvbihub2RlX2RlZ3JlZShjb2NvbmV0KSwgCiMgICAgICAgICAgICAgICAgICAgICAgICAgICBiPTEwLAojICAgICAgICAgICAgICAgICAgICAgICAgICAgeGxpbT1jKDAsMTQpLCAKIyAgICAgICAgICAgICAgICAgICAgICAgICAgIHlsaW09YygwLDIpLCAKIyAgICAgICAgICAgICAgICAgICAgICAgICAgIHhsYWI9Ik5vZGUgZGVncmVlIikKCiMgTGV0J3MgcnVuIHRoaXMgb24gRUdBRAp0ZXN0aW5nXzQgPC0gcnVuX0dCQShhcy5tYXRyaXgoY29jb25ldCksIAogICAgICAgICAgICAgICAgICAgYXMubWF0cml4KGFubm90YXRpb25zXzQpLCBtaW4gPSA1KQoKdGVzdGluZ180W1sxXV0KYGBgCgpMZXQncyBhbHNvIGNvbXBhcmUgd2hlbiB3ZSBzZXQgdGhlIHJlYWQgY291bnQgZmlsdGVyIGFzIDUgKE5BIGNvdW50cyA8PSA2KQoKYGBge3J9CnJlYWRmaWx0ZXI1X05BX2ZpbHRlcjZfWENJX3NrZXdfZ2VuZXMgPC0gcmVhZC5jc3YoInJlYWRmaWx0ZXI1X05BX2ZpbHRlcjZfWENJX3NrZXdfZ2VuZXMuY3N2IixoZWFkZXIgPSAwKSAlPiUgcHVsbChWMSkKbWFwcGluZ19YQ0lfNSA8LSBtYXBJZHMob3JnLkhzLmVnLmRiLCAKICAgICAgICAgICAgICAgICAgcmVhZGZpbHRlcjVfTkFfZmlsdGVyNl9YQ0lfc2tld19nZW5lcywgCiAgICAgICAgICAgICAgICAgICdFTlNFTUJMJywKICAgICAgICAgICAgICAgICAgJ1NZTUJPTCcpCgptYXBwaW5nX1hDSV81ICU8PiUgCiAgYXNfdGliYmxlKHJvd25hbWVzID0gImdlbmUiKSAlPiUgCiAgZHBseXI6OnJlbmFtZShFTlNFTUJMID0gdmFsdWUpCgojIFJlbW92ZSB0aGUgTkFzCm1hcHBpbmdfWENJXzUgJTw+JSBmaWx0ZXIoIWlzLm5hKEVOU0VNQkwpKQoKI2NoZWNrIGZvciAxLW1hbnkgb3IgbWFueS0xCm1hcHBpbmdfWENJXzUgJT4lIAogIGdyb3VwX2J5KEVOU0VNQkwpICU+JSAKICBkcGx5cjo6Y291bnQoKSAlPiUgCiAgZmlsdGVyKG4gIT0gMSkKbWFwcGluZ19YQ0lfNSAlPiUgCiAgZ3JvdXBfYnkoZ2VuZSkgJT4lIAogIGRwbHlyOjpjb3VudCgpICU+JSAKICBmaWx0ZXIobiAhPSAxKQoKYW5ub3RhdGlvbnNfNSA8LSB0aWJibGUoZ2VuZSA9IHJvd25hbWVzKGNvY29uZXQpKQphbm5vdGF0aW9uc181IDwtIGFubm90YXRpb25zXzUgJT4lIAogIG11dGF0ZShYQ0lfc2tldyA9IGdlbmUgJWluJSAobWFwcGluZ19YQ0lfNSAlPiUgCiAgICAgICAgICAgICAgICAgICAgICAgICAgcHVsbChFTlNFTUJMKSkpICU+JQogIG11dGF0ZShYQ0lfc2tldyA9IGFzLm51bWVyaWMoWENJX3NrZXcpKSAKZGltKGFubm90YXRpb25zXzUpCgphbm5vdGF0aW9uc181IDwtIGFubm90YXRpb25zXzUgJT4lIAogIGFzLmRhdGEuZnJhbWUoKQoKcm93bmFtZXMoYW5ub3RhdGlvbnNfNSkgPC0gYW5ub3RhdGlvbnNfNSRnZW5lCmFubm90YXRpb25zXzUgJTw+JSAKICBkcGx5cjo6c2VsZWN0KC1nZW5lKQoKIyAyLjMuNSBCdWlsZGluZyBuZXR3b3JrcwojIGhpc3QgPC0gcGxvdF9kaXN0cmlidXRpb24obm9kZV9kZWdyZWUoY29jb25ldCksIAojICAgICAgICAgICAgICAgICAgICAgICAgICAgYj0xMCwKIyAgICAgICAgICAgICAgICAgICAgICAgICAgIHhsaW09YygwLDE0KSwgCiMgICAgICAgICAgICAgICAgICAgICAgICAgICB5bGltPWMoMCwyKSwgCiMgICAgICAgICAgICAgICAgICAgICAgICAgICB4bGFiPSJOb2RlIGRlZ3JlZSIpCgojIExldCdzIHJ1biB0aGlzIG9uIEVHQUQKdGVzdGluZ181IDwtIHJ1bl9HQkEoYXMubWF0cml4KGNvY29uZXQpLCAKICAgICAgICAgICAgICAgICAgIGFzLm1hdHJpeChhbm5vdGF0aW9uc181KSwgbWluID0gNSkKP3J1bl9HQkEKdGVzdGluZ181W1sxXV0KYGBgCgpBcyBhIGxhc3QgcG9pbnQgb2YgcmVmZXJlbmNlLCBsZXQncyBwZXJmb3JtIEVHQUQgb24gYWxsIGdlbmVzIGluIHRoZSBoZWF0bWFwIGZvciByZWFkIGNvdW50IGZpbHRlciA9IDUuCgpgYGB7cn0KcmVhZGZpbHRlcjVfbm9OQV9hbGxfZ2VuZXMgPC0gcmVhZC5jc3YoInJlYWRmaWx0ZXI1X25vTkFfYWxsX2dlbmVzLmNzdiIsaGVhZGVyID0gMCkgJT4lIHB1bGwoVjEpCm1hcHBpbmdfWENJXzYgPC0gbWFwSWRzKG9yZy5Icy5lZy5kYiwgCiAgICAgICAgICAgICAgICAgIHJlYWRmaWx0ZXI1X25vTkFfYWxsX2dlbmVzLCAKICAgICAgICAgICAgICAgICAgJ0VOU0VNQkwnLAogICAgICAgICAgICAgICAgICAnU1lNQk9MJykKCm1hcHBpbmdfWENJXzYgJTw+JSAKICBhc190aWJibGUocm93bmFtZXMgPSAiZ2VuZSIpICU+JSAKICBkcGx5cjo6cmVuYW1lKEVOU0VNQkwgPSB2YWx1ZSkKCiMgUmVtb3ZlIHRoZSBOQXMKbWFwcGluZ19YQ0lfNiAlPD4lIGZpbHRlcighaXMubmEoRU5TRU1CTCkpCgojY2hlY2sgZm9yIDEtbWFueSBvciBtYW55LTEKbWFwcGluZ19YQ0lfNiAlPiUgCiAgZ3JvdXBfYnkoRU5TRU1CTCkgJT4lIAogIGRwbHlyOjpjb3VudCgpICU+JSAKICBmaWx0ZXIobiAhPSAxKQptYXBwaW5nX1hDSV82ICU+JSAKICBncm91cF9ieShnZW5lKSAlPiUgCiAgZHBseXI6OmNvdW50KCkgJT4lIAogIGZpbHRlcihuICE9IDEpCgphbm5vdGF0aW9uc182IDwtIHRpYmJsZShnZW5lID0gcm93bmFtZXMoY29jb25ldCkpCmFubm90YXRpb25zXzYgPC0gYW5ub3RhdGlvbnNfNiAlPiUgCiAgbXV0YXRlKFhDSV9za2V3ID0gZ2VuZSAlaW4lIChtYXBwaW5nX1hDSV82ICU+JSAKICAgICAgICAgICAgICAgICAgICAgICAgICBwdWxsKEVOU0VNQkwpKSkgJT4lCiAgbXV0YXRlKFhDSV9za2V3ID0gYXMubnVtZXJpYyhYQ0lfc2tldykpIApkaW0oYW5ub3RhdGlvbnNfNikKCmFubm90YXRpb25zXzYgPC0gYW5ub3RhdGlvbnNfNiAlPiUgCiAgYXMuZGF0YS5mcmFtZSgpCgpyb3duYW1lcyhhbm5vdGF0aW9uc182KSA8LSBhbm5vdGF0aW9uc182JGdlbmUKYW5ub3RhdGlvbnNfNiAlPD4lIAogIGRwbHlyOjpzZWxlY3QoLWdlbmUpCgojIDIuMy41IEJ1aWxkaW5nIG5ldHdvcmtzCiMgaGlzdCA8LSBwbG90X2Rpc3RyaWJ1dGlvbihub2RlX2RlZ3JlZShjb2NvbmV0KSwgCiMgICAgICAgICAgICAgICAgICAgICAgICAgICBiPTEwLAojICAgICAgICAgICAgICAgICAgICAgICAgICAgeGxpbT1jKDAsMTQpLCAKIyAgICAgICAgICAgICAgICAgICAgICAgICAgIHlsaW09YygwLDIpLCAKIyAgICAgICAgICAgICAgICAgICAgICAgICAgIHhsYWI9Ik5vZGUgZGVncmVlIikKCiMgTGV0J3MgcnVuIHRoaXMgb24gRUdBRAp0ZXN0aW5nXzYgPC0gcnVuX0dCQShhcy5tYXRyaXgoY29jb25ldCksIAogICAgICAgICAgICAgICAgICAgYXMubWF0cml4KGFubm90YXRpb25zXzYpLCBtaW4gPSA1KQoKdGVzdGluZ182W1sxXV0KYGBgCgpDb21wYXJlIHRoZSBleHByZXNzaW9uIHZhbHVlcyBvZiB0aGVzZSBnZW5lcyBmcm9tIHRoZSBjb3VudCBtYXRyaXghCgpgYGB7cn0KZDEgPC0gcmVhZFJEUygiL2hvbWUveW91a2ltL0J1bGtfUk5BX3NlcS9TVEFSX291dHB1dF9hbmFseXNpcy9BZnRlcl9RQy9QaWxvdF9EYXRhL2NvdW50X21hdHJpeF9QaWxvdF9EYXRhX0FmdGVyUUMucmRzIikKZDIgPC0gcmVhZFJEUygiL2hvbWUveW91a2ltL0J1bGtfUk5BX3NlcS9TVEFSX291dHB1dF9hbmFseXNpcy9BZnRlcl9RQy9TZWNvbmRfRGF0YS9jb3VudF9tYXRyaXhfU2Vjb25kX0RhdGFfQWZ0ZXJRQy5yZHMiKQpkMyA8LSByZWFkUkRTKCIvaG9tZS95b3VraW0vQnVsa19STkFfc2VxL1NUQVJfb3V0cHV0X2FuYWx5c2lzL1RoaXJkX2RhdGEvY291bnRfbWF0cml4XzIwMjMxMDE2LnJkcyIpCgptZXJnZWRfZXhwcnMgPC0gbWVyZ2UoZDEsIGQyLCBrZXkgPSBnZW5lKQptZXJnZWRfZXhwcnMgPC0gbWVyZ2UobWVyZ2VkX2V4cHJzLCBkMywga2V5ID0gZ2VuZSkKbWVyZ2VkX2V4cHJzIDwtIG1lcmdlZF9leHBycyAlPiUgCiAgZHBseXI6OnNlbGVjdCgtY29udGFpbnMoIjEyNzg4IikpICU+JSAKICBkcGx5cjo6c2VsZWN0KC1jb250YWlucygiMTI5MTkiKSkgJT4lIAogIGRwbHlyOjpzZWxlY3QoLWNvbnRhaW5zKCIxMjk4MCIpKSAlPiUgCiAgZHBseXI6OnNlbGVjdCgtY29udGFpbnMoIjEyOTQ0IikpICU+JSAKICAjIHRoZSBmb2xsb3dpbmcgY29kZSBpcyB0byBnZXQgb25seSB0aGUgRUdGUisvRUdGUi0gc2FtcGxlcyAobGlrZSB3ZSBkaWQgZm9yIHRoZSBUaGlyZCBkYXRhKQogIGRwbHlyOjpzZWxlY3QoLWNvbnRhaW5zKCJMIikpICU+JSAKICBkcGx5cjo6c2VsZWN0KC1jb250YWlucygibnMiKSkKbWVyZ2VkX2V4cHJzIDwtIG1lcmdlZF9leHBycyAlPiUgCiAgY29sdW1uX3RvX3Jvd25hbWVzKCJnZW5lIikKCm1lcmdlZF9leHBycyAlPiUgCiAgcm93U3VtcygpICU+JSAKICBtZWFuKCkKCiMgTGV0J3MgY2hlY2sgZm9yIHRoZSBleHByZXNzaW9uIGZyb20gbWFwcGluZ19YQ0lfNAptZXJnZWRfZXhwcnNbbWFwcGluZ19YQ0lfNCRFTlNFTUJMLF0gJT4lIAogIHJvd1N1bXMoKSAlPiUgCiAgbWVhbigpCgojIExldCdzIGNoZWNrIGZvciB0aGUgZXhwcmVzc2lvbiBmcm9tIG1hcHBpbmdfWENJXzUKbWVyZ2VkX2V4cHJzW21hcHBpbmdfWENJXzUkRU5TRU1CTCxdICU+JSAKICByb3dTdW1zKCkgJT4lIAogIG1lYW4oKQpgYGAKCg==