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==