Load libaries and set colors for deciles and vigintiles
library(ggplot2)
library(reshape2)
library(cowplow)
library(scales)
library(tidyr)
library(tibble)
library(plyr)
library(colorspace)
# parameters
col_decile = rev(c('239,62,35', '246,138,31', '245,186,24', '244,235,24', '156,203,60', '76,183,72', '20,171,188', '0,120,178', '58,54,148', '113,44,145'))
col_decile = sapply(strsplit(col_decile, ","), function(x)
rgb(x[1], x[2], x[3], maxColorValue=255))
# for vigintiles, interpolate the color according the deciles color
col_vigintile = c()
for (idx in seq(9)) {
col_vigintile = c(col_vigintile, c(col_decile[idx], hex(mixcolor(alpha = 0.5, color1 = hex2RGB(col_decile[idx]), color2 = hex2RGB(col_decile[idx+1])))))
}
col_vigintile = c(col_vigintile, hex(mixcolor(alpha = 0.5, color1 = hex2RGB(col_vigintile[18]), color2 = hex2RGB("#FF0000"))), "#FF0000")
# minial size of genomic bins
MIN_SIZE = 1000
# whether to analyze the histone data, default no
ANALYZE_HISTONE = FALSE
## Main functions to analyze the histone/expression patterns
# histone marks
decile_analysis_histone <- function(cell_name, decile_col){
# load the table
table <- read.table(file=paste0("result/hg38_20kb_", cell_name, ".txt"), header = T, sep = '\t', comment.char = "", check.names = F)
# drop wins with zero size
table <- subset(table, size > MIN_SIZE)
# drop some columns
table$start <- NULL
table$stop <- NULL
# calculate the deciles
SON_deciles = quantile(table[, decile_col], probs = seq(0, 1, 0.1), na.rm = T)
table$deciles <- cut(table[, decile_col], breaks = SON_deciles, labels = paste0("Decile ", seq(1,10,1)))
# calculate the half-deciles
SON_vigintile = quantile(table[, decile_col], probs = seq(0, 1, 0.05), na.rm = T)
#table$vigintile <- cut(table[, decile_col], breaks = SON_vigintile, labels = paste0("Vigintile ", seq(1,20,1)))
# melt the data frame
table <- melt(table, id.vars = c("#chrom", "mid", "size", "deciles"), variable.name = "name", value.name = "value")
# check how many data are there
length(unique(table$name))
# split columns
table <- table %>% separate(name, c("cell", "target", "lab", "type", "id"), sep = '_', extra = "drop", fill = "right")
# drop NA
table <- subset(table, deciles %in% paste0("Decile ", seq(1,10,1)))
# for peaks: add the value as peak length in TSA-seq paper
if ("peaks" %in% table$type & ANALYZE_HISTONE) {
table_peak_length <- subset(table, type == "peaks")
table_peak_length <- ddply(table_peak_length, .(deciles, cell, target, lab, type, id), summarize, peak_length = sum(value))
table_peak_length <- ddply(table_peak_length, .(cell, target, lab, type, id), transform, total_peak_length = sum(peak_length))
table_peak_length$percent <- table_peak_length$peak_length/table_peak_length$total_peak_length
table_peak_length$name = paste(table_peak_length$target, table_peak_length$id, sep = '_')
# plot
plot_peak_length <- ggplot(table_peak_length, aes(x = deciles, y = percent, fill = deciles)) +
geom_col() +
geom_hline(yintercept = 0.0, size = 1) +
xlab("") +
ylab("Percent") +
scale_y_continuous(labels = percent, expand = c(0,0)) +
scale_fill_manual(name = "Deciles", values = col_decile) +
facet_wrap(~ name, ncol = 6) +
theme_bw() +
theme(plot.title = element_text(lineheight=.8, face="bold", size=rel(2))) +
theme(panel.border = element_blank()) +
theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank()) +
theme(axis.title.y = element_text(size=rel(1.3),margin=margin(0,10,0,0))) +
theme(axis.title.x = element_text(size=rel(1.3),margin=margin(10,0,0,0))) +
#theme(axis.text.x = element_text(size=rel(1.5), color = "black", angle = 45, hjust = 1, vjust = 1)) +
theme(axis.text.x = element_blank()) +
theme(axis.text.y = element_text(size=rel(1.5), color = "black")) +
theme(axis.line = element_line(color="black")) +
theme(axis.ticks.x = element_blank()) +
theme(strip.text = element_text(size=rel(1.0), face="bold")) +
theme(strip.background = element_blank())
pdf(file = paste0("figure_", cell_name, "_peak_length.pdf"), width = 15, height = 8)
print(plot_peak_length)
dev.off()
}
# for p-value signal use the average
if ("pval" %in% table$type & ANALYZE_HISTONE) {
table_pval_mean <- subset(table, type == "pval")
table_pval_mean$name = paste(table_pval_mean$target, table_pval_mean$id, sep = '_')
signal_list = unique(table_pval_mean$name)
# plot
pdf(file = paste0("figure_", cell_name, "_pval_boxplot.pdf"), width = 6, height = 4)
for (data_name in signal_list){
data <- subset(table_pval_mean, name == data_name)
data_y_max = quantile(data$value, probs = 0.99, na.rm = T)
plot_pval <- ggplot(data, aes(x = deciles, y = value, fill = deciles)) +
#geom_violin(trim = T) +
geom_boxplot(outlier.size = 0.7, outlier.shape = NA, width = 0.7) +
coord_cartesian(ylim = c(0, data_y_max)) +
xlab("") +
ylab("-log10 (P value)") +
ggtitle(data_name) +
scale_y_continuous(expand = c(0,0)) +
scale_fill_manual(name = "Deciles", values = col_decile) +
#facet_wrap(~ name, ncol = 6) +
theme_bw() +
theme(plot.title = element_text(lineheight=.8, face="bold", size=rel(2))) +
theme(panel.border = element_blank()) +
theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank()) +
theme(axis.title.y = element_text(size=rel(1.3),margin=margin(0,10,0,0))) +
theme(axis.title.x = element_text(size=rel(1.3),margin=margin(10,0,0,0))) +
#theme(axis.text.x = element_text(size=rel(1.5), color = "black", angle = 45, hjust = 1, vjust = 1)) +
theme(axis.text.x = element_blank()) +
theme(axis.text.y = element_text(size=rel(1.5), color = "black")) +
theme(axis.line = element_line(color="black")) +
theme(axis.ticks.x = element_blank()) +
theme(strip.text = element_text(size=rel(1.0), face="bold")) +
theme(strip.background = element_blank())
print(plot_pval)
}
dev.off()
}
return(list(data = table, decile_cutoff = SON_deciles, vigintile_cutoff = SON_vigintile))
}
# gene expression patterns
gene_expr <- function(cell_name, expr_name, data_anno) {
expr_table <- read.table(file=paste0("result/", expr_name, "_anno_rsem.genes.results"), header = T, sep = '\t', comment.char = "", check.names = F)
gene_anno <- read.table(file=paste0("result/", paste0("hg38_gencode_v24_gene_", cell_name, ".txt")), header = T, sep = '\t', comment.char = "", check.names = F)
# inner join
expr_table <- merge(expr_table, gene_anno, by = "gene_id", all = F)
# only use protein_coding
expr_table <- subset(expr_table, gene_type == 'protein_coding')
# get the TSA-seq deciles
TSA_list = colnames(expr_table)[grep('TSA', colnames(expr_table))]
for (TSA in TSA_list) {
# calculate the deciles
#TSA_deciles = quantile(expr_table[, TSA], probs = seq(0, 1, 0.1), na.rm = T)
if (grepl('SON', TSA)) {
# note use the TSA decile
TSA_deciles = data_anno$decile_cutoff
TSA_deciles[1] = -10
TSA_deciles[length(TSA_deciles)] = 10
expr_table[, paste0(TSA, ".deciles")] <- cut(expr_table[, TSA],
breaks = TSA_deciles,
labels = paste0("Decile ", seq(1,10,1)),
include.lowest = T,
right = T)
TSA_vigintiles = data_anno$vigintile_cutoff
TSA_vigintiles[1] = -10
TSA_vigintiles[length(TSA_vigintiles)] = 10
expr_table[, paste0(TSA, ".vigintiles")] <- cut(expr_table[, TSA],
breaks = TSA_vigintiles,
labels = paste0("Vigintile ", seq(1,20,1)),
include.lowest = T,
right = T)
}
}
# get the gene expression percentile
min_FPKM = 1e-3
# plot the gene boxplot
pdf(file = paste0("figure_", expr_name, "_gene_expr_boxplot.pdf"), width = 6, height = 4)
for (TSA in TSA_list) {
if (any(grepl('SON_TSA_2.0',TSA))) {
#TSA_decile_col = paste0(TSA, '.deciles')
TSA_decile_col = paste0(TSA, '.vigintiles')
plot_gene_expr_boxplot <- ggplot(subset(expr_table, !is.na(TSA_decile_col)), aes_string(x = TSA_decile_col, y = "FPKM", fill = TSA_decile_col)) +
geom_boxplot(outlier.shape = NA) +
xlab(TSA_decile_col) +
ylab("FPKM") +
ggtitle(cell_name) +
coord_cartesian(ylim = c(0, 100)) +
scale_x_discrete(limits = paste0("Vigintile ", seq(1,20,1))) +
#scale_fill_manual(name = "Deciles", values = col_decile, limits = paste0("Decile ", seq(1,10,1))) +
scale_fill_manual(name = "Vigintiles", values = col_vigintile, limits = paste0("Vigintile ", seq(1,20,1))) +
theme_bw() +
theme(plot.title = element_text(lineheight=.8, face="bold", size=rel(2))) +
theme(panel.border = element_blank()) +
theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank()) +
theme(axis.title.y = element_text(size=rel(1.5),margin=margin(0,10,0,0))) +
theme(axis.title.x = element_text(size=rel(1.5),margin=margin(10,0,0,0))) +
#theme(axis.text.x = element_text(size=rel(1.5), color = "black", angle = 45, hjust = 1, vjust = 1)) +
theme(axis.text.x = element_blank()) +
theme(axis.text.y = element_text(size=rel(1.5), color = "black")) +
theme(axis.line = element_line(color="black")) +
theme(axis.ticks.x = element_blank()) +
theme(legend.title = element_blank()) +
theme(legend.text = element_text(size=rel(1.5)))
# plot
print(plot_gene_expr_boxplot)
}
}
dev.off()
# plot 2D scatter plot
if (any(grepl('SON', TSA_list)) & any(grepl('LaminB', TSA_list)) & FALSE) {
#expr_table <- subset(expr_table, FPKM_percentile != 'Non-expressed')
expr_table$x_son = expr_table[, TSA_list[grep('SON', TSA_list)]]
expr_table$y_laminb = expr_table[, TSA_list[grep('LaminB', TSA_list)]]
# use interpolation to map the TSA-seq value to the window based percentile
# first get the length matched SON value
SON_match <- quantile(subset(data_anno$data, target == "SON")$value, probs = seq(0,1,length.out = nrow(expr_table)), na.rm = T)
expr_table$x_son_mapped <- approx(SON_match, expr_table$x_son, xout = SON_match, ties = "ordered", rule=2:2)$y
expr_table$x_son_rank = rank(expr_table$x_son_mapped)/nrow(expr_table)
#LaminB_match <- quantile(subset(data_anno$data, target = "LaminB")$value, probs = seq(0, 1, length.out = nrow(expr_table)), na.rm = T)
#expr_table$y_laminb_mapped <- approx(LaminB_match, expr_table$y_laminb, xout = LaminB_match, ties = "ordered", rule=2:2)$y
#expr_table$y_laminb_rank = rank(expr_table$y_laminb_mapped)/nrow(expr_table)
#
cal_per <- function(win_list, gene_value) {
if (gene_value < min(win_list, na.rm = T)) {
return(0.0)
} else if (gene_value > max(win_list, na.rm = T)) {
return(1.0)
} else {
return(sum(gene_value >= win_list)/length(win_list))
}
}
testFunc <- function(a) a *2
expr_table$x_son_rank = lapply(expr_table$x_son, function(x) cal_per(subset(data_anno$data, target == "SON")$value, x))
#expr_table$x_son_rank = lapply(expr_table[, "x_son"], function(x) testFunc(x[1]))
SON_min = min(expr_table$x_son, na.rm = T)
SON_max = max(expr_table$x_son, na.rm = T)
LaminB_min = min(expr_table$y_laminb, na.rm = T)
LaminB_max = max(expr_table$y_laminb, na.rm = T)
pdf(paste0("figure_", expr_name, "_gene_expr_2D_scatter.pdf"), width = 8, height = 8)
group_list = c("Non-expressed", fpkm_cutoff_label)
col_list = c("black", col_decile)
for (nn in seq(1, length(group_list))) {
group = group_list[nn]
color_point = col_list[nn]
plot_top_density <- ggplot(subset(expr_table, FPKM_percentile == group), aes(x = x_son)) +
geom_density(fill = color_point, alpha = 0.7) +
coord_cartesian(xlim = c(SON_min, SON_max)) +
scale_x_continuous(expand = c(0,0)) +
scale_y_continuous(expand = c(0,0), position = "right") +
xlab("") +
ylab("") +
ggtitle(paste(cell_name, group, sep=' ')) +
theme_bw() +
theme(plot.title = element_text(lineheight=.8, face="bold", size=rel(2))) +
theme(panel.border = element_blank()) +
theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank()) +
theme(axis.title = element_blank()) +
theme(axis.text.x = element_blank()) +
theme(axis.text.y = element_text(size=rel(1), color = "black")) +
theme(axis.line = element_line(color="black")) +
theme(axis.ticks.x = element_blank()) +
theme(legend.position = "none")
plot_right_density <- ggplot(subset(expr_table, FPKM_percentile == group), aes(x = y_laminb)) +
geom_density(fill = color_point, alpha = 0.7) +
coord_cartesian(xlim = c(LaminB_min, LaminB_max)) +
#scale_x_reverse(expand = c(0,0)) +
coord_flip() +
scale_x_continuous(expand = c(0,0)) +
scale_y_continuous(expand = c(0,0), position = "right") +
xlab("") +
ylab("") +
theme_bw() +
theme(plot.title = element_text(lineheight=.8, face="bold", size=rel(2))) +
theme(panel.border = element_blank()) +
theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank()) +
theme(axis.title = element_blank()) +
theme(axis.text.y = element_blank()) +
theme(axis.text.x = element_text(size=rel(1), color = "black")) +
theme(axis.line = element_line(color="black")) +
theme(axis.ticks.y = element_blank()) +
theme(legend.position = "none")
plot_scatter <- ggplot(expr_table, aes(x = x_son, y = y_laminb)) +
geom_point(alpha = 0.3, color = "lightgrey", size = 0.5) +
geom_point(data = subset(expr_table, FPKM_percentile == group), aes(x = x_son, y = y_laminb), alpha = 0.7, size = 0.75, color = color_point) +
coord_cartesian(xlim = c(SON_min, SON_max), ylim = c(LaminB_min, LaminB_max)) +
xlab("SON TSA-seq score") +
ylab("LaminB TSA-seq score") +
scale_y_continuous(expand = c(0,0)) +
scale_x_continuous(expand = c(0,0)) +
#scale_fill_manual(name = "Deciles", values = col_decile, limits = paste0("Decile ", seq(1,10,1))) +
theme_bw() +
theme(plot.title = element_text(lineheight=.8, face="bold", size=rel(2))) +
theme(panel.border = element_blank()) +
theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank()) +
theme(axis.title.y = element_text(size=rel(1.5),margin=margin(0,10,0,0))) +
theme(axis.title.x = element_text(size=rel(1.5),margin=margin(10,0,0,0))) +
theme(axis.text = element_text(size=rel(1.5), color = "black")) +
theme(axis.line = element_line(color="black")) +
theme(axis.ticks.x = element_blank()) +
theme(legend.position = "none")
p1 <- plot_top_density + plot_spacer() + plot_scatter + plot_right_density + plot_layout(nrow = 2, ncol = 2, widths = c(6, 1), heights = c(1,6))
print(p1)
}
dev.off()
}
# plot the distribution of gene
fpkm_cutoff = c(seq(0, 0.9, 0.1), seq(0.91, 1, 0.01))
fpkm_cutoff_label = c(paste0("Expr ", seq(0,80,10), '-', seq(10, 90, 10), "%"), paste0("Expr ", seq(90, 99, 1), '-', seq(91, 100, 1), "%"))
expr_percentile = quantile(subset(expr_table, FPKM > min_FPKM)[,"FPKM"], probs = fpkm_cutoff, na.rm = T)
expr_table$FPKM_percentile = "Non-expressed"
idx_list = which(expr_table$FPKM > min_FPKM)
value_list = as.character(cut(expr_table[which(expr_table$FPKM > min_FPKM), "FPKM"],
breaks = expr_percentile,
labels = fpkm_cutoff_label,
include.lowest = T))
expr_table[idx_list, "FPKM_percentile"] = value_list
expr_table$FPKM_percentile <- factor(expr_table$FPKM_percentile, levels = c("Non-expressed", fpkm_cutoff_label))
pdf(paste0("figure_", expr_name, "_gene_expr_dist.pdf"), width = 8, height = 8)
for (TSA in TSA_list) {
if (any(grepl('SON_TSA_2.0',TSA))) {
#TSA_decile_col = paste0(TSA, '.deciles')
TSA_decile_col = paste0(TSA, '.vigintiles')
ddply_col_list = c("FPKM_percentile", TSA_decile_col)
gene_expr_dist <- ddply(expr_table, ddply_col_list, summarize, count= length(gene_id))
gene_expr_dist <- ddply(gene_expr_dist, .(FPKM_percentile), transform, total = sum(count))
gene_expr_dist$per <- gene_expr_dist$count/gene_expr_dist$total
gene_expr_dist$FPKM_percentile <- factor(gene_expr_dist$FPKM_percentile, levels = c("Non-expressed", fpkm_cutoff_label))
plot_expr_dist <- ggplot(gene_expr_dist, aes_string(x = TSA_decile_col, y = "per", fill = TSA_decile_col)) +
geom_col()+
xlab(TSA_decile_col) +
ylab("Percent") +
scale_x_discrete(limits = paste0("Vigintile ", seq(1,20,1))) +
scale_y_continuous(labels = percent, expand = c(0,0), breaks = seq(0,0.6,0.1)) +
#scale_fill_manual(name = "Deciles", values = col_decile, limits = paste0("Decile ", seq(1,10,1))) +
scale_fill_manual(name = "Vigintiles", values = col_vigintile, limits = paste0("Vigintile ", seq(1,20,1))) +
facet_wrap(~ FPKM_percentile, ncol = 5) +
theme_bw() +
theme(plot.title = element_text(lineheight=.8, face="bold", size=rel(2))) +
theme(panel.border = element_blank()) +
theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank()) +
theme(axis.title.y = element_text(size=rel(1.5),margin=margin(0,10,0,0))) +
theme(axis.title.x = element_text(size=rel(1.5),margin=margin(10,0,0,0))) +
theme(axis.text.x = element_blank()) +
theme(axis.text.y = element_text(size=rel(1.5), color = "black")) +
theme(axis.line = element_line(color="black")) +
theme(axis.ticks.x = element_blank()) +
theme(legend.position = "none") +
theme(strip.text = element_text(size=rel(1.0), face="bold")) +
theme(strip.background = element_blank())
print(plot_expr_dist)
}
}
dev.off()
return(expr_table)
}
# housekeeping genes
load_housekeeping <- function (filename) {
table <- read.table(file=filename,
header = TRUE,
sep='\t',
comment.char = "",
stringsAsFactors = FALSE)
# select rows with no NA values in samples
filter_row <- complete.cases(table[, c("H1_SON_TSA_2.0", "K562_SON_TSA_2.0", "HCT116_SON_TSA_2.0", "HFF_SON_TSA_2.0")])
filter_table <- table[filter_row, ]
# drop wins with zero size
filter_table <- subset(filter_table, size > MIN_SIZE)
# check how many rows are kept
#nrow(filter_table)
#nrow(table)
# convert NA to -1
filter_table[which(is.na(filter_table$hk)), 'hk'] = -1
filter_table[which(is.na(filter_table$non_hk)), 'non_hk'] = -1
return(filter_table)
}
# plot housekeeping results
housekeeping_vigintile <- function(table, TSA, data_anno, title){
TSA_vigintiles = data_anno$vigintile_cutoff
TSA_vigintiles[1] = -10
TSA_vigintiles[length(TSA_vigintiles)] = 10
table$TSA_vigintiles <- cut(table[, TSA],
breaks = TSA_vigintiles,
labels = paste0("", seq(1,20,1)),
include.lowest = T,
right = T)
table$TSA_vigintiles <- factor(table$TSA_vigintiles, levels = paste0("", seq(1,20,1)))
result <- ddply(table, .(TSA_vigintiles), summarize,
count_bin_hk = sum(hk > -1),
count_bin_non_hk = sum(non_hk > -1),
count_gene_hk = length(unique(hk)) - 1,
count_gene_non_hk = length(unique(non_hk)) - 1, .drop = FALSE)
result$per_bin_hk = result$count_bin_hk/sum(result$count_bin_hk)
result$per_bin_non_hk = result$count_bin_non_hk/sum(result$count_bin_non_hk)
result$per_gene_hk = result$count_gene_hk/sum(result$count_gene_hk)
result$per_gene_non_hk = result$count_gene_non_hk/sum(result$count_gene_non_hk)
result = melt(result[, c("TSA_vigintiles", "per_bin_hk", "per_bin_non_hk", "per_gene_hk", "per_gene_non_hk")],
id.vars = c("TSA_vigintiles", "per_gene_hk", "per_gene_non_hk"),
variable.name = "group_bin",
value.name = "per_bin")
result = melt(result,
id.vars = c("TSA_vigintiles", "group_bin", "per_bin"),
variable.name = "group_gene",
value.name = "per_gene")
plot_vigintile <- ggplot(unique(result[ ,c("TSA_vigintiles", "per_gene", "group_gene")]), aes(x = TSA_vigintiles, y = per_gene, fill = group_gene)) +
geom_col(col="black", position = "dodge") +
xlab("TSA-seq vigintiles") +
ylab("Percentage") +
ggtitle("HFF SON TSA-seq 2.0") +
scale_y_continuous(labels = percent, expand = c(0,0)) +
scale_fill_manual(name="",values=c("per_gene_hk"="#e41a1c", "per_gene_non_hk"="#377eb8"),
labels = c("housekeeping genes", "non-housekeeping genes")) +
theme_bw() +
theme(plot.title = element_text(lineheight=.8, face="bold", size=rel(2))) +
theme(panel.border = element_blank()) +
theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank()) +
theme(axis.title.y = element_text(size=rel(1.5),margin=margin(0,10,0,0))) +
theme(axis.title.x = element_text(size=rel(1.5),margin=margin(10,0,0,0))) +
theme(axis.text = element_text(size=rel(1.2), color="black")) +
theme(axis.line = element_line(color="black")) +
theme(legend.position = c(0.4, 0.8), legend.text = element_text(size = rel(1.2))) +
coord_cartesian(ylim = c(0, 0.17))
return(plot_vigintile)
}
## load results and make figures
# load the results
data_H1 <- decile_analysis_histone("H1", "H1_SON_TSA_2.0")
data_K562 <- decile_analysis_histone("K562", "K562_SON_TSA_2.0")
data_HCT116 <- decile_analysis_histone("HCT116", "HCT116_SON_TSA_2.0")
data_HFF <- decile_analysis_histone("HFF", "HFF_SON_TSA_2.0")
expr_K562 <- gene_expr("K562", "K562", data_K562)
expr_HCT116 <- gene_expr("HCT116", "HCT116", data_HCT116)
expr_H1 <- gene_expr("H1", "H1", data_H1)
expr_HFF_GSE100576 <- gene_expr("HFF", "HFF_GSE100576", data_HFF)
expr_HFF_GSE64553 <- gene_expr("HFF", "HFF_GSE64553", data_HFF)
# report
write.table(expr_K562, file = "report/gencode_expr_K562.txt", sep = '\t', col.names = T, row.names = F, quote = F)
write.table(expr_HCT116, file = "report/gencode_expr_HCT116.txt", sep = '\t', col.names = T, row.names = F, quote = F)
write.table(expr_H1, file = "report/gencode_expr_H1.txt", sep = '\t', col.names = T, row.names = F, quote = F)
write.table(expr_HFF_GSE100576, file = "report/gencode_expr_HFF_GSE100576.txt", sep = '\t', col.names = T, row.names = F, quote = F)
write.table(expr_HFF_GSE64553, file = "report/gencode_expr_HFF_GSE64553.txt", sep = '\t', col.names = T, row.names = F, quote = F)
# housekeeping genes
table_housekeeping <- load_housekeeping("result/hg38_20kb_housekeeping.txt")
plot_vigintile_H1 <- housekeeping_vigintile(table_housekeeping, "H1_SON_TSA_2.0", data_H1, "H1 SON TSA-seq 2.0")
plot_vigintile_K562 <- housekeeping_vigintile(table_housekeeping, "K562_SON_TSA_2.0", data_K562, "K562 SON TSA-seq 2.0")
plot_vigintile_HCT116 <- housekeeping_vigintile(table_housekeeping, "HCT116_SON_TSA_2.0", data_HCT116, "HCT116 SON TSA-seq 2.0")
plot_vigintile_HFF <- housekeeping_vigintile(table_housekeeping, "HFF_SON_TSA_2.0", data_HFF, "HFF SON TSA-seq 2.0")
pdf(file = "figure_housekeeping_gene_vigintile.pdf", width = 14, height = 10)
plot_grid(plot_vigintile_K562, plot_vigintile_H1, plot_vigintile_HCT116, plot_vigintile_HFF, align = "h")
dev.off()
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKYXV0aG9yOiAiWWFuZyBaaGFuZyIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6IGRlZmF1bHQKICBodG1sX25vdGVib29rOiBkZWZhdWx0CmVtYWlsOiB6b2NlYW42MzZAZ21haWwuY29tCi0tLQoKIyMgTG9hZCBsaWJhcmllcyBhbmQgc2V0IGNvbG9ycyBmb3IgZGVjaWxlcyBhbmQgdmlnaW50aWxlcwpgYGB7cn0KbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHJlc2hhcGUyKQpsaWJyYXJ5KGNvd3Bsb3cpCmxpYnJhcnkoc2NhbGVzKQpsaWJyYXJ5KHRpZHlyKQpsaWJyYXJ5KHRpYmJsZSkKbGlicmFyeShwbHlyKQpsaWJyYXJ5KGNvbG9yc3BhY2UpCgojIHBhcmFtZXRlcnMKY29sX2RlY2lsZSA9IHJldihjKCcyMzksNjIsMzUnLCAnMjQ2LDEzOCwzMScsICcyNDUsMTg2LDI0JywgJzI0NCwyMzUsMjQnLCAnMTU2LDIwMyw2MCcsICc3NiwxODMsNzInLCAnMjAsMTcxLDE4OCcsICcwLDEyMCwxNzgnLCAnNTgsNTQsMTQ4JywgJzExMyw0NCwxNDUnKSkKY29sX2RlY2lsZSA9IHNhcHBseShzdHJzcGxpdChjb2xfZGVjaWxlLCAiLCIpLCBmdW5jdGlvbih4KQogICAgcmdiKHhbMV0sIHhbMl0sIHhbM10sIG1heENvbG9yVmFsdWU9MjU1KSkKIyBmb3IgdmlnaW50aWxlcywgaW50ZXJwb2xhdGUgdGhlIGNvbG9yIGFjY29yZGluZyB0aGUgZGVjaWxlcyBjb2xvcgpjb2xfdmlnaW50aWxlID0gYygpCmZvciAoaWR4IGluIHNlcSg5KSkgewogIGNvbF92aWdpbnRpbGUgPSBjKGNvbF92aWdpbnRpbGUsIGMoY29sX2RlY2lsZVtpZHhdLCBoZXgobWl4Y29sb3IoYWxwaGEgPSAwLjUsIGNvbG9yMSA9IGhleDJSR0IoY29sX2RlY2lsZVtpZHhdKSwgY29sb3IyID0gaGV4MlJHQihjb2xfZGVjaWxlW2lkeCsxXSkpKSkpCn0KY29sX3ZpZ2ludGlsZSA9IGMoY29sX3ZpZ2ludGlsZSwgaGV4KG1peGNvbG9yKGFscGhhID0gMC41LCBjb2xvcjEgPSBoZXgyUkdCKGNvbF92aWdpbnRpbGVbMThdKSwgY29sb3IyID0gaGV4MlJHQigiI0ZGMDAwMCIpKSksICIjRkYwMDAwIikKIyBtaW5pYWwgc2l6ZSBvZiBnZW5vbWljIGJpbnMKTUlOX1NJWkUgPSAxMDAwCiMgd2hldGhlciB0byBhbmFseXplIHRoZSBoaXN0b25lIGRhdGEsIGRlZmF1bHQgbm8KQU5BTFlaRV9ISVNUT05FID0gRkFMU0UKCiMjIE1haW4gZnVuY3Rpb25zIHRvIGFuYWx5emUgdGhlIGhpc3RvbmUvZXhwcmVzc2lvbiBwYXR0ZXJucwojIGhpc3RvbmUgbWFya3MKZGVjaWxlX2FuYWx5c2lzX2hpc3RvbmUgPC0gZnVuY3Rpb24oY2VsbF9uYW1lLCBkZWNpbGVfY29sKXsKICAjIGxvYWQgdGhlIHRhYmxlCiAgdGFibGUgPC0gcmVhZC50YWJsZShmaWxlPXBhc3RlMCgicmVzdWx0L2hnMzhfMjBrYl8iLCBjZWxsX25hbWUsICIudHh0IiksIGhlYWRlciA9IFQsIHNlcCA9ICdcdCcsIGNvbW1lbnQuY2hhciA9ICIiLCBjaGVjay5uYW1lcyA9IEYpCiAgIyBkcm9wIHdpbnMgd2l0aCB6ZXJvIHNpemUKICB0YWJsZSA8LSBzdWJzZXQodGFibGUsIHNpemUgPiBNSU5fU0laRSkKICAjIGRyb3Agc29tZSBjb2x1bW5zCiAgdGFibGUkc3RhcnQgPC0gTlVMTAogIHRhYmxlJHN0b3AgPC0gTlVMTAogICMgY2FsY3VsYXRlIHRoZSBkZWNpbGVzCiAgU09OX2RlY2lsZXMgPSBxdWFudGlsZSh0YWJsZVssIGRlY2lsZV9jb2xdLCBwcm9icyA9IHNlcSgwLCAxLCAwLjEpLCBuYS5ybSA9IFQpCiAgdGFibGUkZGVjaWxlcyA8LSBjdXQodGFibGVbLCBkZWNpbGVfY29sXSwgYnJlYWtzID0gU09OX2RlY2lsZXMsIGxhYmVscyA9IHBhc3RlMCgiRGVjaWxlICIsIHNlcSgxLDEwLDEpKSkKICAjIGNhbGN1bGF0ZSB0aGUgaGFsZi1kZWNpbGVzCiAgU09OX3ZpZ2ludGlsZSA9IHF1YW50aWxlKHRhYmxlWywgZGVjaWxlX2NvbF0sIHByb2JzID0gc2VxKDAsIDEsIDAuMDUpLCBuYS5ybSA9IFQpCiAgI3RhYmxlJHZpZ2ludGlsZSA8LSBjdXQodGFibGVbLCBkZWNpbGVfY29sXSwgYnJlYWtzID0gU09OX3ZpZ2ludGlsZSwgbGFiZWxzID0gcGFzdGUwKCJWaWdpbnRpbGUgIiwgc2VxKDEsMjAsMSkpKQogICMgbWVsdCB0aGUgZGF0YSBmcmFtZQogIHRhYmxlIDwtIG1lbHQodGFibGUsIGlkLnZhcnMgPSBjKCIjY2hyb20iLCAibWlkIiwgInNpemUiLCAiZGVjaWxlcyIpLCB2YXJpYWJsZS5uYW1lID0gIm5hbWUiLCB2YWx1ZS5uYW1lID0gInZhbHVlIikKICAjIGNoZWNrIGhvdyBtYW55IGRhdGEgYXJlIHRoZXJlCiAgbGVuZ3RoKHVuaXF1ZSh0YWJsZSRuYW1lKSkKICAjIHNwbGl0IGNvbHVtbnMKICB0YWJsZSA8LSB0YWJsZSAlPiUgc2VwYXJhdGUobmFtZSwgYygiY2VsbCIsICJ0YXJnZXQiLCAibGFiIiwgInR5cGUiLCAiaWQiKSwgc2VwID0gJ18nLCBleHRyYSA9ICJkcm9wIiwgZmlsbCA9ICJyaWdodCIpCiAgIyBkcm9wIE5BIAogIHRhYmxlIDwtIHN1YnNldCh0YWJsZSwgZGVjaWxlcyAlaW4lIHBhc3RlMCgiRGVjaWxlICIsIHNlcSgxLDEwLDEpKSkKICAKICAjIGZvciBwZWFrczogYWRkIHRoZSB2YWx1ZSBhcyBwZWFrIGxlbmd0aCBpbiBUU0Etc2VxIHBhcGVyCiAgaWYgKCJwZWFrcyIgJWluJSB0YWJsZSR0eXBlICYgQU5BTFlaRV9ISVNUT05FKSB7CiAgICB0YWJsZV9wZWFrX2xlbmd0aCA8LSBzdWJzZXQodGFibGUsIHR5cGUgPT0gInBlYWtzIikKICAgIHRhYmxlX3BlYWtfbGVuZ3RoIDwtIGRkcGx5KHRhYmxlX3BlYWtfbGVuZ3RoLCAuKGRlY2lsZXMsIGNlbGwsIHRhcmdldCwgbGFiLCB0eXBlLCBpZCksIHN1bW1hcml6ZSwgcGVha19sZW5ndGggPSBzdW0odmFsdWUpKQogICAgdGFibGVfcGVha19sZW5ndGggPC0gZGRwbHkodGFibGVfcGVha19sZW5ndGgsIC4oY2VsbCwgdGFyZ2V0LCBsYWIsIHR5cGUsIGlkKSwgdHJhbnNmb3JtLCB0b3RhbF9wZWFrX2xlbmd0aCA9IHN1bShwZWFrX2xlbmd0aCkpCiAgICB0YWJsZV9wZWFrX2xlbmd0aCRwZXJjZW50IDwtIHRhYmxlX3BlYWtfbGVuZ3RoJHBlYWtfbGVuZ3RoL3RhYmxlX3BlYWtfbGVuZ3RoJHRvdGFsX3BlYWtfbGVuZ3RoCiAgICB0YWJsZV9wZWFrX2xlbmd0aCRuYW1lID0gcGFzdGUodGFibGVfcGVha19sZW5ndGgkdGFyZ2V0LCB0YWJsZV9wZWFrX2xlbmd0aCRpZCwgc2VwID0gJ18nKQogICAgIyBwbG90CiAgICBwbG90X3BlYWtfbGVuZ3RoIDwtIGdncGxvdCh0YWJsZV9wZWFrX2xlbmd0aCwgYWVzKHggPSBkZWNpbGVzLCB5ID0gcGVyY2VudCwgZmlsbCA9IGRlY2lsZXMpKSArCiAgICAgIGdlb21fY29sKCkgKwogICAgICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLjAsIHNpemUgPSAxKSArCiAgICAgIHhsYWIoIiIpICsKICAgICAgeWxhYigiUGVyY2VudCIpICsKICAgICAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHBlcmNlbnQsIGV4cGFuZCA9IGMoMCwwKSkgKwogICAgICBzY2FsZV9maWxsX21hbnVhbChuYW1lID0gIkRlY2lsZXMiLCB2YWx1ZXMgPSBjb2xfZGVjaWxlKSArIAogICAgICBmYWNldF93cmFwKH4gbmFtZSwgbmNvbCA9IDYpICsKICAgICAgdGhlbWVfYncoKSArCiAgICAgIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQobGluZWhlaWdodD0uOCwgZmFjZT0iYm9sZCIsIHNpemU9cmVsKDIpKSkgKwogICAgICB0aGVtZShwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCkpICsKICAgICAgdGhlbWUocGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSkgKwogICAgICB0aGVtZShheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZT1yZWwoMS4zKSxtYXJnaW49bWFyZ2luKDAsMTAsMCwwKSkpICsKICAgICAgdGhlbWUoYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemU9cmVsKDEuMyksbWFyZ2luPW1hcmdpbigxMCwwLDAsMCkpKSArCiAgICAgICN0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplPXJlbCgxLjUpLCBjb2xvciA9ICJibGFjayIsIGFuZ2xlID0gNDUsIGhqdXN0ID0gMSwgdmp1c3QgPSAxKSkgKyAKICAgICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCkpICsKICAgICAgdGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZT1yZWwoMS41KSwgY29sb3IgPSAiYmxhY2siKSkgKwogICAgICB0aGVtZShheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3I9ImJsYWNrIikpICsKICAgICAgdGhlbWUoYXhpcy50aWNrcy54ID0gZWxlbWVudF9ibGFuaygpKSArCiAgICAgIHRoZW1lKHN0cmlwLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT1yZWwoMS4wKSwgZmFjZT0iYm9sZCIpKSArCiAgICAgIHRoZW1lKHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCkpCiAgICBwZGYoZmlsZSA9IHBhc3RlMCgiZmlndXJlXyIsIGNlbGxfbmFtZSwgIl9wZWFrX2xlbmd0aC5wZGYiKSwgd2lkdGggPSAxNSwgaGVpZ2h0ID0gOCkKICAgIHByaW50KHBsb3RfcGVha19sZW5ndGgpCiAgICBkZXYub2ZmKCkKICB9CiAgCiAgIyBmb3IgcC12YWx1ZSBzaWduYWwgdXNlIHRoZSBhdmVyYWdlCiAgaWYgKCJwdmFsIiAlaW4lIHRhYmxlJHR5cGUgJiBBTkFMWVpFX0hJU1RPTkUpIHsKICAgIHRhYmxlX3B2YWxfbWVhbiA8LSBzdWJzZXQodGFibGUsIHR5cGUgPT0gInB2YWwiKQogICAgdGFibGVfcHZhbF9tZWFuJG5hbWUgPSBwYXN0ZSh0YWJsZV9wdmFsX21lYW4kdGFyZ2V0LCB0YWJsZV9wdmFsX21lYW4kaWQsIHNlcCA9ICdfJykKICAgIHNpZ25hbF9saXN0ID0gdW5pcXVlKHRhYmxlX3B2YWxfbWVhbiRuYW1lKQogICAgIyBwbG90CiAgICBwZGYoZmlsZSA9IHBhc3RlMCgiZmlndXJlXyIsIGNlbGxfbmFtZSwgIl9wdmFsX2JveHBsb3QucGRmIiksIHdpZHRoID0gNiwgaGVpZ2h0ID0gNCkKICAgIGZvciAoZGF0YV9uYW1lIGluIHNpZ25hbF9saXN0KXsKICAgICAgZGF0YSA8LSBzdWJzZXQodGFibGVfcHZhbF9tZWFuLCBuYW1lID09IGRhdGFfbmFtZSkKICAgICAgZGF0YV95X21heCA9IHF1YW50aWxlKGRhdGEkdmFsdWUsIHByb2JzID0gMC45OSwgbmEucm0gPSBUKQogICAgICBwbG90X3B2YWwgPC0gZ2dwbG90KGRhdGEsIGFlcyh4ID0gZGVjaWxlcywgeSA9IHZhbHVlLCBmaWxsID0gZGVjaWxlcykpICsKICAgICAgICAjZ2VvbV92aW9saW4odHJpbSA9IFQpICsKICAgICAgICBnZW9tX2JveHBsb3Qob3V0bGllci5zaXplID0gMC43LCBvdXRsaWVyLnNoYXBlID0gTkEsIHdpZHRoID0gMC43KSArCiAgICAgICAgY29vcmRfY2FydGVzaWFuKHlsaW0gPSBjKDAsIGRhdGFfeV9tYXgpKSArCiAgICAgICAgeGxhYigiIikgKwogICAgICAgIHlsYWIoIi1sb2cxMCAoUCB2YWx1ZSkiKSArCiAgICAgICAgZ2d0aXRsZShkYXRhX25hbWUpICsKICAgICAgICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gYygwLDApKSArCiAgICAgICAgc2NhbGVfZmlsbF9tYW51YWwobmFtZSA9ICJEZWNpbGVzIiwgdmFsdWVzID0gY29sX2RlY2lsZSkgKyAKICAgICAgICAjZmFjZXRfd3JhcCh+IG5hbWUsIG5jb2wgPSA2KSArCiAgICAgICAgdGhlbWVfYncoKSArCiAgICAgICAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChsaW5laGVpZ2h0PS44LCBmYWNlPSJib2xkIiwgc2l6ZT1yZWwoMikpKSArCiAgICAgICAgdGhlbWUocGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpKSArCiAgICAgICAgdGhlbWUocGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSkgKwogICAgICAgIHRoZW1lKGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplPXJlbCgxLjMpLG1hcmdpbj1tYXJnaW4oMCwxMCwwLDApKSkgKwogICAgICAgIHRoZW1lKGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplPXJlbCgxLjMpLG1hcmdpbj1tYXJnaW4oMTAsMCwwLDApKSkgKwogICAgICAgICN0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplPXJlbCgxLjUpLCBjb2xvciA9ICJibGFjayIsIGFuZ2xlID0gNDUsIGhqdXN0ID0gMSwgdmp1c3QgPSAxKSkgKyAKICAgICAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSkgKwogICAgICAgIHRoZW1lKGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemU9cmVsKDEuNSksIGNvbG9yID0gImJsYWNrIikpICsKICAgICAgICB0aGVtZShheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3I9ImJsYWNrIikpICsKICAgICAgICB0aGVtZShheGlzLnRpY2tzLnggPSBlbGVtZW50X2JsYW5rKCkpICsKICAgICAgICB0aGVtZShzdHJpcC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9cmVsKDEuMCksIGZhY2U9ImJvbGQiKSkgKwogICAgICAgIHRoZW1lKHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCkpCiAgICAgIHByaW50KHBsb3RfcHZhbCkKICAgIH0KICAgIGRldi5vZmYoKQogIH0KICAKICByZXR1cm4obGlzdChkYXRhID0gdGFibGUsIGRlY2lsZV9jdXRvZmYgPSBTT05fZGVjaWxlcywgdmlnaW50aWxlX2N1dG9mZiA9IFNPTl92aWdpbnRpbGUpKQp9CiMgZ2VuZSBleHByZXNzaW9uIHBhdHRlcm5zCmdlbmVfZXhwciA8LSBmdW5jdGlvbihjZWxsX25hbWUsIGV4cHJfbmFtZSwgZGF0YV9hbm5vKSB7CiAgZXhwcl90YWJsZSA8LSByZWFkLnRhYmxlKGZpbGU9cGFzdGUwKCJyZXN1bHQvIiwgZXhwcl9uYW1lLCAiX2Fubm9fcnNlbS5nZW5lcy5yZXN1bHRzIiksIGhlYWRlciA9IFQsIHNlcCA9ICdcdCcsIGNvbW1lbnQuY2hhciA9ICIiLCBjaGVjay5uYW1lcyA9IEYpCiAgZ2VuZV9hbm5vIDwtIHJlYWQudGFibGUoZmlsZT1wYXN0ZTAoInJlc3VsdC8iLCBwYXN0ZTAoImhnMzhfZ2VuY29kZV92MjRfZ2VuZV8iLCBjZWxsX25hbWUsICIudHh0IikpLCBoZWFkZXIgPSBULCBzZXAgPSAnXHQnLCBjb21tZW50LmNoYXIgPSAiIiwgY2hlY2submFtZXMgPSBGKQogICMgaW5uZXIgam9pbgogIGV4cHJfdGFibGUgPC0gbWVyZ2UoZXhwcl90YWJsZSwgZ2VuZV9hbm5vLCBieSA9ICJnZW5lX2lkIiwgYWxsID0gRikKICAjIG9ubHkgdXNlIHByb3RlaW5fY29kaW5nCiAgZXhwcl90YWJsZSA8LSBzdWJzZXQoZXhwcl90YWJsZSwgZ2VuZV90eXBlID09ICdwcm90ZWluX2NvZGluZycpCiAgIyBnZXQgdGhlIFRTQS1zZXEgZGVjaWxlcwogIFRTQV9saXN0ID0gY29sbmFtZXMoZXhwcl90YWJsZSlbZ3JlcCgnVFNBJywgY29sbmFtZXMoZXhwcl90YWJsZSkpXQogIGZvciAoVFNBIGluIFRTQV9saXN0KSB7CiAgICAjIGNhbGN1bGF0ZSB0aGUgZGVjaWxlcwogICAgI1RTQV9kZWNpbGVzID0gcXVhbnRpbGUoZXhwcl90YWJsZVssIFRTQV0sIHByb2JzID0gc2VxKDAsIDEsIDAuMSksIG5hLnJtID0gVCkKICAgIGlmIChncmVwbCgnU09OJywgVFNBKSkgewogICAgICAjIG5vdGUgdXNlIHRoZSBUU0EgZGVjaWxlCiAgICAgIFRTQV9kZWNpbGVzID0gZGF0YV9hbm5vJGRlY2lsZV9jdXRvZmYKICAgICAgVFNBX2RlY2lsZXNbMV0gPSAtMTAKICAgICAgVFNBX2RlY2lsZXNbbGVuZ3RoKFRTQV9kZWNpbGVzKV0gPSAxMAogICAgICBleHByX3RhYmxlWywgcGFzdGUwKFRTQSwgIi5kZWNpbGVzIildIDwtIGN1dChleHByX3RhYmxlWywgVFNBXSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBUU0FfZGVjaWxlcywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBwYXN0ZTAoIkRlY2lsZSAiLCBzZXEoMSwxMCwxKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbmNsdWRlLmxvd2VzdCA9IFQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByaWdodCA9IFQpCiAgICAgIFRTQV92aWdpbnRpbGVzID0gZGF0YV9hbm5vJHZpZ2ludGlsZV9jdXRvZmYKICAgICAgVFNBX3ZpZ2ludGlsZXNbMV0gPSAtMTAKICAgICAgVFNBX3ZpZ2ludGlsZXNbbGVuZ3RoKFRTQV92aWdpbnRpbGVzKV0gPSAxMAogICAgICBleHByX3RhYmxlWywgcGFzdGUwKFRTQSwgIi52aWdpbnRpbGVzIildIDwtIGN1dChleHByX3RhYmxlWywgVFNBXSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBUU0FfdmlnaW50aWxlcywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBwYXN0ZTAoIlZpZ2ludGlsZSAiLCBzZXEoMSwyMCwxKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbmNsdWRlLmxvd2VzdCA9IFQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByaWdodCA9IFQpCiAgICB9CiAgfQogICMgZ2V0IHRoZSBnZW5lIGV4cHJlc3Npb24gcGVyY2VudGlsZQogIG1pbl9GUEtNID0gMWUtMwogICMgcGxvdCB0aGUgZ2VuZSBib3hwbG90CiAgcGRmKGZpbGUgPSBwYXN0ZTAoImZpZ3VyZV8iLCBleHByX25hbWUsICJfZ2VuZV9leHByX2JveHBsb3QucGRmIiksIHdpZHRoID0gNiwgaGVpZ2h0ID0gNCkKICBmb3IgKFRTQSBpbiBUU0FfbGlzdCkgewogICAgaWYgKGFueShncmVwbCgnU09OX1RTQV8yLjAnLFRTQSkpKSB7CiAgICAgICNUU0FfZGVjaWxlX2NvbCA9IHBhc3RlMChUU0EsICcuZGVjaWxlcycpCiAgICAgIFRTQV9kZWNpbGVfY29sID0gcGFzdGUwKFRTQSwgJy52aWdpbnRpbGVzJykKICAgICAgcGxvdF9nZW5lX2V4cHJfYm94cGxvdCA8LSBnZ3Bsb3Qoc3Vic2V0KGV4cHJfdGFibGUsICFpcy5uYShUU0FfZGVjaWxlX2NvbCkpLCBhZXNfc3RyaW5nKHggPSBUU0FfZGVjaWxlX2NvbCwgeSA9ICJGUEtNIiwgZmlsbCA9IFRTQV9kZWNpbGVfY29sKSkgKwogICAgICAgIGdlb21fYm94cGxvdChvdXRsaWVyLnNoYXBlID0gTkEpICsKICAgICAgICB4bGFiKFRTQV9kZWNpbGVfY29sKSArCiAgICAgICAgeWxhYigiRlBLTSIpICsKICAgICAgICBnZ3RpdGxlKGNlbGxfbmFtZSkgKwogICAgICAgIGNvb3JkX2NhcnRlc2lhbih5bGltID0gYygwLCAxMDApKSArCiAgICAgICAgc2NhbGVfeF9kaXNjcmV0ZShsaW1pdHMgPSBwYXN0ZTAoIlZpZ2ludGlsZSAiLCBzZXEoMSwyMCwxKSkpICsgCiAgICAgICAgI3NjYWxlX2ZpbGxfbWFudWFsKG5hbWUgPSAiRGVjaWxlcyIsIHZhbHVlcyA9IGNvbF9kZWNpbGUsIGxpbWl0cyA9IHBhc3RlMCgiRGVjaWxlICIsIHNlcSgxLDEwLDEpKSkgKyAKICAgICAgICBzY2FsZV9maWxsX21hbnVhbChuYW1lID0gIlZpZ2ludGlsZXMiLCB2YWx1ZXMgPSBjb2xfdmlnaW50aWxlLCBsaW1pdHMgPSBwYXN0ZTAoIlZpZ2ludGlsZSAiLCBzZXEoMSwyMCwxKSkpICsgCiAgICAgICAgdGhlbWVfYncoKSArCiAgICAgICAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChsaW5laGVpZ2h0PS44LCBmYWNlPSJib2xkIiwgc2l6ZT1yZWwoMikpKSArCiAgICAgICAgdGhlbWUocGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpKSArCiAgICAgICAgdGhlbWUocGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSkgKwogICAgICAgIHRoZW1lKGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplPXJlbCgxLjUpLG1hcmdpbj1tYXJnaW4oMCwxMCwwLDApKSkgKwogICAgICAgIHRoZW1lKGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplPXJlbCgxLjUpLG1hcmdpbj1tYXJnaW4oMTAsMCwwLDApKSkgKwogICAgICAgICN0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplPXJlbCgxLjUpLCBjb2xvciA9ICJibGFjayIsIGFuZ2xlID0gNDUsIGhqdXN0ID0gMSwgdmp1c3QgPSAxKSkgKyAKICAgICAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSkgKwogICAgICAgIHRoZW1lKGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemU9cmVsKDEuNSksIGNvbG9yID0gImJsYWNrIikpICsKICAgICAgICB0aGVtZShheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3I9ImJsYWNrIikpICsKICAgICAgICB0aGVtZShheGlzLnRpY2tzLnggPSBlbGVtZW50X2JsYW5rKCkpICsKICAgICAgICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsKICAgICAgICB0aGVtZShsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPXJlbCgxLjUpKSkKICAgICMgcGxvdAogICAgcHJpbnQocGxvdF9nZW5lX2V4cHJfYm94cGxvdCkKICAgIH0KICB9CiAgZGV2Lm9mZigpCiAgCiAgIyBwbG90IDJEIHNjYXR0ZXIgcGxvdAogIGlmIChhbnkoZ3JlcGwoJ1NPTicsIFRTQV9saXN0KSkgJiBhbnkoZ3JlcGwoJ0xhbWluQicsIFRTQV9saXN0KSkgJiBGQUxTRSkgewogICAgI2V4cHJfdGFibGUgPC0gc3Vic2V0KGV4cHJfdGFibGUsIEZQS01fcGVyY2VudGlsZSAhPSAnTm9uLWV4cHJlc3NlZCcpCiAgICBleHByX3RhYmxlJHhfc29uID0gZXhwcl90YWJsZVssIFRTQV9saXN0W2dyZXAoJ1NPTicsIFRTQV9saXN0KV1dCiAgICBleHByX3RhYmxlJHlfbGFtaW5iID0gZXhwcl90YWJsZVssIFRTQV9saXN0W2dyZXAoJ0xhbWluQicsIFRTQV9saXN0KV1dCiAgICAjIHVzZSBpbnRlcnBvbGF0aW9uIHRvIG1hcCB0aGUgVFNBLXNlcSB2YWx1ZSB0byB0aGUgd2luZG93IGJhc2VkIHBlcmNlbnRpbGUKICAgICMgZmlyc3QgZ2V0IHRoZSBsZW5ndGggbWF0Y2hlZCBTT04gdmFsdWUgCiAgICBTT05fbWF0Y2ggPC0gcXVhbnRpbGUoc3Vic2V0KGRhdGFfYW5ubyRkYXRhLCB0YXJnZXQgPT0gIlNPTiIpJHZhbHVlLCBwcm9icyA9IHNlcSgwLDEsbGVuZ3RoLm91dCA9IG5yb3coZXhwcl90YWJsZSkpLCBuYS5ybSA9IFQpCiAgICBleHByX3RhYmxlJHhfc29uX21hcHBlZCA8LSBhcHByb3goU09OX21hdGNoLCBleHByX3RhYmxlJHhfc29uLCB4b3V0ID0gU09OX21hdGNoLCB0aWVzID0gIm9yZGVyZWQiLCBydWxlPTI6MikkeQogICAgZXhwcl90YWJsZSR4X3Nvbl9yYW5rID0gcmFuayhleHByX3RhYmxlJHhfc29uX21hcHBlZCkvbnJvdyhleHByX3RhYmxlKQogICAgI0xhbWluQl9tYXRjaCA8LSBxdWFudGlsZShzdWJzZXQoZGF0YV9hbm5vJGRhdGEsIHRhcmdldCA9ICJMYW1pbkIiKSR2YWx1ZSwgcHJvYnMgPSBzZXEoMCwgMSwgbGVuZ3RoLm91dCA9IG5yb3coZXhwcl90YWJsZSkpLCBuYS5ybSA9IFQpCiAgICAjZXhwcl90YWJsZSR5X2xhbWluYl9tYXBwZWQgPC0gYXBwcm94KExhbWluQl9tYXRjaCwgZXhwcl90YWJsZSR5X2xhbWluYiwgeG91dCA9IExhbWluQl9tYXRjaCwgdGllcyA9ICJvcmRlcmVkIiwgcnVsZT0yOjIpJHkKICAgICNleHByX3RhYmxlJHlfbGFtaW5iX3JhbmsgPSByYW5rKGV4cHJfdGFibGUkeV9sYW1pbmJfbWFwcGVkKS9ucm93KGV4cHJfdGFibGUpCiAgICAjCiAgICBjYWxfcGVyIDwtIGZ1bmN0aW9uKHdpbl9saXN0LCBnZW5lX3ZhbHVlKSB7CiAgICAgIGlmIChnZW5lX3ZhbHVlIDwgbWluKHdpbl9saXN0LCBuYS5ybSA9IFQpKSB7CiAgICAgICAgcmV0dXJuKDAuMCkKICAgICAgfSBlbHNlIGlmIChnZW5lX3ZhbHVlID4gbWF4KHdpbl9saXN0LCBuYS5ybSA9IFQpKSB7CiAgICAgICAgcmV0dXJuKDEuMCkKICAgICAgfSBlbHNlIHsKICAgICAgICByZXR1cm4oc3VtKGdlbmVfdmFsdWUgPj0gd2luX2xpc3QpL2xlbmd0aCh3aW5fbGlzdCkpCiAgICAgIH0KICAgIH0KICAgIHRlc3RGdW5jIDwtIGZ1bmN0aW9uKGEpIGEgKjIKICAgIGV4cHJfdGFibGUkeF9zb25fcmFuayA9IGxhcHBseShleHByX3RhYmxlJHhfc29uLCBmdW5jdGlvbih4KSBjYWxfcGVyKHN1YnNldChkYXRhX2Fubm8kZGF0YSwgdGFyZ2V0ID09ICJTT04iKSR2YWx1ZSwgeCkpCiAgICAjZXhwcl90YWJsZSR4X3Nvbl9yYW5rID0gbGFwcGx5KGV4cHJfdGFibGVbLCAieF9zb24iXSwgZnVuY3Rpb24oeCkgdGVzdEZ1bmMoeFsxXSkpCiAgICBTT05fbWluID0gbWluKGV4cHJfdGFibGUkeF9zb24sIG5hLnJtID0gVCkKICAgIFNPTl9tYXggPSBtYXgoZXhwcl90YWJsZSR4X3NvbiwgbmEucm0gPSBUKQogICAgTGFtaW5CX21pbiA9IG1pbihleHByX3RhYmxlJHlfbGFtaW5iLCBuYS5ybSA9IFQpCiAgICBMYW1pbkJfbWF4ID0gbWF4KGV4cHJfdGFibGUkeV9sYW1pbmIsIG5hLnJtID0gVCkKICAgIHBkZihwYXN0ZTAoImZpZ3VyZV8iLCBleHByX25hbWUsICJfZ2VuZV9leHByXzJEX3NjYXR0ZXIucGRmIiksIHdpZHRoID0gOCwgaGVpZ2h0ID0gOCkKICAgIGdyb3VwX2xpc3QgPSBjKCJOb24tZXhwcmVzc2VkIiwgZnBrbV9jdXRvZmZfbGFiZWwpCiAgICBjb2xfbGlzdCA9IGMoImJsYWNrIiwgY29sX2RlY2lsZSkKICAgIGZvciAobm4gaW4gc2VxKDEsIGxlbmd0aChncm91cF9saXN0KSkpIHsKICAgICAgZ3JvdXAgPSBncm91cF9saXN0W25uXQogICAgICBjb2xvcl9wb2ludCA9IGNvbF9saXN0W25uXQogICAgICBwbG90X3RvcF9kZW5zaXR5IDwtIGdncGxvdChzdWJzZXQoZXhwcl90YWJsZSwgRlBLTV9wZXJjZW50aWxlID09IGdyb3VwKSwgYWVzKHggPSB4X3NvbikpICsKICAgICAgICBnZW9tX2RlbnNpdHkoZmlsbCA9IGNvbG9yX3BvaW50LCBhbHBoYSA9IDAuNykgKwogICAgICAgIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYyhTT05fbWluLCBTT05fbWF4KSkgKwogICAgICAgIHNjYWxlX3hfY29udGludW91cyhleHBhbmQgPSBjKDAsMCkpICsKICAgICAgICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gYygwLDApLCBwb3NpdGlvbiA9ICJyaWdodCIpICsKICAgICAgICB4bGFiKCIiKSArCiAgICAgICAgeWxhYigiIikgKwogICAgICAgIGdndGl0bGUocGFzdGUoY2VsbF9uYW1lLCBncm91cCwgc2VwPScgJykpICsKICAgICAgICB0aGVtZV9idygpICsKICAgICAgICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGxpbmVoZWlnaHQ9LjgsIGZhY2U9ImJvbGQiLCBzaXplPXJlbCgyKSkpICsKICAgICAgICB0aGVtZShwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCkpICsKICAgICAgICB0aGVtZShwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLCBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpKSArCiAgICAgICAgdGhlbWUoYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKwogICAgICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpKSArCiAgICAgICAgdGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZT1yZWwoMSksIGNvbG9yID0gImJsYWNrIikpICsKICAgICAgICB0aGVtZShheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3I9ImJsYWNrIikpICsKICAgICAgICB0aGVtZShheGlzLnRpY2tzLnggPSBlbGVtZW50X2JsYW5rKCkpICsKICAgICAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCiAgICAgIHBsb3RfcmlnaHRfZGVuc2l0eSA8LSBnZ3Bsb3Qoc3Vic2V0KGV4cHJfdGFibGUsIEZQS01fcGVyY2VudGlsZSA9PSBncm91cCksIGFlcyh4ID0geV9sYW1pbmIpKSArCiAgICAgICAgZ2VvbV9kZW5zaXR5KGZpbGwgPSBjb2xvcl9wb2ludCwgYWxwaGEgPSAwLjcpICsKICAgICAgICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoTGFtaW5CX21pbiwgTGFtaW5CX21heCkpICsKICAgICAgICAjc2NhbGVfeF9yZXZlcnNlKGV4cGFuZCA9IGMoMCwwKSkgKwogICAgICAgIGNvb3JkX2ZsaXAoKSArCiAgICAgICAgc2NhbGVfeF9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwwKSkgKwogICAgICAgIHNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBjKDAsMCksIHBvc2l0aW9uID0gInJpZ2h0IikgKwogICAgICAgIHhsYWIoIiIpICsKICAgICAgICB5bGFiKCIiKSArCiAgICAgICAgdGhlbWVfYncoKSArCiAgICAgICAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChsaW5laGVpZ2h0PS44LCBmYWNlPSJib2xkIiwgc2l6ZT1yZWwoMikpKSArCiAgICAgICAgdGhlbWUocGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpKSArCiAgICAgICAgdGhlbWUocGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSkgKwogICAgICAgIHRoZW1lKGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsKICAgICAgICB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSkgKwogICAgICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemU9cmVsKDEpLCBjb2xvciA9ICJibGFjayIpKSArCiAgICAgICAgdGhlbWUoYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG9yPSJibGFjayIpKSArCiAgICAgICAgdGhlbWUoYXhpcy50aWNrcy55ID0gZWxlbWVudF9ibGFuaygpKSArCiAgICAgICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQogICAgICBwbG90X3NjYXR0ZXIgPC0gZ2dwbG90KGV4cHJfdGFibGUsIGFlcyh4ID0geF9zb24sIHkgPSB5X2xhbWluYikpICsKICAgICAgICBnZW9tX3BvaW50KGFscGhhID0gMC4zLCBjb2xvciA9ICJsaWdodGdyZXkiLCBzaXplID0gMC41KSArCiAgICAgICAgZ2VvbV9wb2ludChkYXRhID0gc3Vic2V0KGV4cHJfdGFibGUsIEZQS01fcGVyY2VudGlsZSA9PSBncm91cCksIGFlcyh4ID0geF9zb24sIHkgPSB5X2xhbWluYiksIGFscGhhID0gMC43LCBzaXplID0gMC43NSwgY29sb3IgPSBjb2xvcl9wb2ludCkgKwogICAgICAgIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYyhTT05fbWluLCBTT05fbWF4KSwgeWxpbSA9IGMoTGFtaW5CX21pbiwgTGFtaW5CX21heCkpICsKICAgICAgICB4bGFiKCJTT04gVFNBLXNlcSBzY29yZSIpICsKICAgICAgICB5bGFiKCJMYW1pbkIgVFNBLXNlcSBzY29yZSIpICsKICAgICAgICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gYygwLDApKSArCiAgICAgICAgc2NhbGVfeF9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwwKSkgKwogICAgICAgICNzY2FsZV9maWxsX21hbnVhbChuYW1lID0gIkRlY2lsZXMiLCB2YWx1ZXMgPSBjb2xfZGVjaWxlLCBsaW1pdHMgPSBwYXN0ZTAoIkRlY2lsZSAiLCBzZXEoMSwxMCwxKSkpICsgCiAgICAgICAgdGhlbWVfYncoKSArCiAgICAgICAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChsaW5laGVpZ2h0PS44LCBmYWNlPSJib2xkIiwgc2l6ZT1yZWwoMikpKSArCiAgICAgICAgdGhlbWUocGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpKSArCiAgICAgICAgdGhlbWUocGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSkgKwogICAgICAgIHRoZW1lKGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplPXJlbCgxLjUpLG1hcmdpbj1tYXJnaW4oMCwxMCwwLDApKSkgKwogICAgICAgIHRoZW1lKGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplPXJlbCgxLjUpLG1hcmdpbj1tYXJnaW4oMTAsMCwwLDApKSkgKwogICAgICAgIHRoZW1lKGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPXJlbCgxLjUpLCBjb2xvciA9ICJibGFjayIpKSArCiAgICAgICAgdGhlbWUoYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG9yPSJibGFjayIpKSArCiAgICAgICAgdGhlbWUoYXhpcy50aWNrcy54ID0gZWxlbWVudF9ibGFuaygpKSArCiAgICAgICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQogICAgICBwMSA8LSBwbG90X3RvcF9kZW5zaXR5ICsgcGxvdF9zcGFjZXIoKSArIHBsb3Rfc2NhdHRlciArIHBsb3RfcmlnaHRfZGVuc2l0eSArIHBsb3RfbGF5b3V0KG5yb3cgPSAyLCBuY29sID0gMiwgd2lkdGhzID0gYyg2LCAxKSwgaGVpZ2h0cyA9IGMoMSw2KSkKICAgICAgcHJpbnQocDEpCiAgICB9CiAgICBkZXYub2ZmKCkKICB9CiAgCiAgIyBwbG90IHRoZSBkaXN0cmlidXRpb24gb2YgZ2VuZQogIGZwa21fY3V0b2ZmID0gYyhzZXEoMCwgMC45LCAwLjEpLCBzZXEoMC45MSwgMSwgMC4wMSkpCiAgZnBrbV9jdXRvZmZfbGFiZWwgPSBjKHBhc3RlMCgiRXhwciAiLCBzZXEoMCw4MCwxMCksICctJywgc2VxKDEwLCA5MCwgMTApLCAiJSIpLCAgcGFzdGUwKCJFeHByICIsIHNlcSg5MCwgOTksIDEpLCAnLScsIHNlcSg5MSwgMTAwLCAxKSwgIiUiKSkKICBleHByX3BlcmNlbnRpbGUgPSBxdWFudGlsZShzdWJzZXQoZXhwcl90YWJsZSwgRlBLTSA+IG1pbl9GUEtNKVssIkZQS00iXSwgcHJvYnMgPSBmcGttX2N1dG9mZiwgbmEucm0gPSBUKQogIGV4cHJfdGFibGUkRlBLTV9wZXJjZW50aWxlID0gIk5vbi1leHByZXNzZWQiCiAgaWR4X2xpc3QgPSB3aGljaChleHByX3RhYmxlJEZQS00gPiBtaW5fRlBLTSkKICB2YWx1ZV9saXN0ID0gYXMuY2hhcmFjdGVyKGN1dChleHByX3RhYmxlW3doaWNoKGV4cHJfdGFibGUkRlBLTSA+IG1pbl9GUEtNKSwgIkZQS00iXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gZXhwcl9wZXJjZW50aWxlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBmcGttX2N1dG9mZl9sYWJlbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW5jbHVkZS5sb3dlc3QgPSBUKSkKICBleHByX3RhYmxlW2lkeF9saXN0LCAiRlBLTV9wZXJjZW50aWxlIl0gPSB2YWx1ZV9saXN0CiAgZXhwcl90YWJsZSRGUEtNX3BlcmNlbnRpbGUgPC0gZmFjdG9yKGV4cHJfdGFibGUkRlBLTV9wZXJjZW50aWxlLCBsZXZlbHMgPSBjKCJOb24tZXhwcmVzc2VkIiwgZnBrbV9jdXRvZmZfbGFiZWwpKQogIHBkZihwYXN0ZTAoImZpZ3VyZV8iLCBleHByX25hbWUsICJfZ2VuZV9leHByX2Rpc3QucGRmIiksIHdpZHRoID0gOCwgaGVpZ2h0ID0gOCkKICBmb3IgKFRTQSBpbiBUU0FfbGlzdCkgewogICAgaWYgKGFueShncmVwbCgnU09OX1RTQV8yLjAnLFRTQSkpKSB7CiAgICAgICNUU0FfZGVjaWxlX2NvbCA9IHBhc3RlMChUU0EsICcuZGVjaWxlcycpCiAgICAgIFRTQV9kZWNpbGVfY29sID0gcGFzdGUwKFRTQSwgJy52aWdpbnRpbGVzJykKICAgICAgZGRwbHlfY29sX2xpc3QgPSBjKCJGUEtNX3BlcmNlbnRpbGUiLCBUU0FfZGVjaWxlX2NvbCkKICAgICAgZ2VuZV9leHByX2Rpc3QgPC0gZGRwbHkoZXhwcl90YWJsZSwgZGRwbHlfY29sX2xpc3QsIHN1bW1hcml6ZSwgY291bnQ9IGxlbmd0aChnZW5lX2lkKSkKICAgICAgZ2VuZV9leHByX2Rpc3QgPC0gZGRwbHkoZ2VuZV9leHByX2Rpc3QsIC4oRlBLTV9wZXJjZW50aWxlKSwgdHJhbnNmb3JtLCB0b3RhbCA9IHN1bShjb3VudCkpCiAgICAgIGdlbmVfZXhwcl9kaXN0JHBlciA8LSBnZW5lX2V4cHJfZGlzdCRjb3VudC9nZW5lX2V4cHJfZGlzdCR0b3RhbAogICAgICBnZW5lX2V4cHJfZGlzdCRGUEtNX3BlcmNlbnRpbGUgPC0gZmFjdG9yKGdlbmVfZXhwcl9kaXN0JEZQS01fcGVyY2VudGlsZSwgbGV2ZWxzID0gYygiTm9uLWV4cHJlc3NlZCIsIGZwa21fY3V0b2ZmX2xhYmVsKSkKICAgICAgcGxvdF9leHByX2Rpc3QgPC0gZ2dwbG90KGdlbmVfZXhwcl9kaXN0LCBhZXNfc3RyaW5nKHggPSBUU0FfZGVjaWxlX2NvbCwgeSA9ICJwZXIiLCBmaWxsID0gVFNBX2RlY2lsZV9jb2wpKSArCiAgICAgICAgZ2VvbV9jb2woKSsKICAgICAgICB4bGFiKFRTQV9kZWNpbGVfY29sKSArCiAgICAgICAgeWxhYigiUGVyY2VudCIpICsKICAgICAgICBzY2FsZV94X2Rpc2NyZXRlKGxpbWl0cyA9IHBhc3RlMCgiVmlnaW50aWxlICIsIHNlcSgxLDIwLDEpKSkgKyAKICAgICAgICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gcGVyY2VudCwgZXhwYW5kID0gYygwLDApLCBicmVha3MgPSBzZXEoMCwwLjYsMC4xKSkgKwogICAgICAgICNzY2FsZV9maWxsX21hbnVhbChuYW1lID0gIkRlY2lsZXMiLCB2YWx1ZXMgPSBjb2xfZGVjaWxlLCBsaW1pdHMgPSBwYXN0ZTAoIkRlY2lsZSAiLCBzZXEoMSwxMCwxKSkpICsgCiAgICAgICAgc2NhbGVfZmlsbF9tYW51YWwobmFtZSA9ICJWaWdpbnRpbGVzIiwgdmFsdWVzID0gY29sX3ZpZ2ludGlsZSwgbGltaXRzID0gcGFzdGUwKCJWaWdpbnRpbGUgIiwgc2VxKDEsMjAsMSkpKSArCiAgICAgICAgZmFjZXRfd3JhcCh+IEZQS01fcGVyY2VudGlsZSwgbmNvbCA9IDUpICsKICAgICAgICB0aGVtZV9idygpICsKICAgICAgICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGxpbmVoZWlnaHQ9LjgsIGZhY2U9ImJvbGQiLCBzaXplPXJlbCgyKSkpICsKICAgICAgICB0aGVtZShwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCkpICsKICAgICAgICB0aGVtZShwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLCBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpKSArCiAgICAgICAgdGhlbWUoYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemU9cmVsKDEuNSksbWFyZ2luPW1hcmdpbigwLDEwLDAsMCkpKSArCiAgICAgICAgdGhlbWUoYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemU9cmVsKDEuNSksbWFyZ2luPW1hcmdpbigxMCwwLDAsMCkpKSArCiAgICAgICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCkpICsKICAgICAgICB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplPXJlbCgxLjUpLCBjb2xvciA9ICJibGFjayIpKSArCiAgICAgICAgdGhlbWUoYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG9yPSJibGFjayIpKSArCiAgICAgICAgdGhlbWUoYXhpcy50aWNrcy54ID0gZWxlbWVudF9ibGFuaygpKSArCiAgICAgICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgICAgICAgdGhlbWUoc3RyaXAudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPXJlbCgxLjApLCBmYWNlPSJib2xkIikpICsKICAgICAgICB0aGVtZShzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpKQogICAgICBwcmludChwbG90X2V4cHJfZGlzdCkKICAgIH0KICB9CiAgZGV2Lm9mZigpCiAgCiAgcmV0dXJuKGV4cHJfdGFibGUpCn0KIyBob3VzZWtlZXBpbmcgZ2VuZXMKbG9hZF9ob3VzZWtlZXBpbmcgPC0gZnVuY3Rpb24gKGZpbGVuYW1lKSB7CiAgdGFibGUgPC0gcmVhZC50YWJsZShmaWxlPWZpbGVuYW1lLCAKICAgICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFRSVUUsIAogICAgICAgICAgICAgICAgICAgICAgc2VwPSdcdCcsIAogICAgICAgICAgICAgICAgICAgICAgY29tbWVudC5jaGFyID0gIiIsIAogICAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQogICMgc2VsZWN0IHJvd3Mgd2l0aCBubyBOQSB2YWx1ZXMgaW4gc2FtcGxlcwogIGZpbHRlcl9yb3cgPC0gY29tcGxldGUuY2FzZXModGFibGVbLCBjKCJIMV9TT05fVFNBXzIuMCIsICJLNTYyX1NPTl9UU0FfMi4wIiwgIkhDVDExNl9TT05fVFNBXzIuMCIsICJIRkZfU09OX1RTQV8yLjAiKV0pCiAgZmlsdGVyX3RhYmxlIDwtIHRhYmxlW2ZpbHRlcl9yb3csIF0KICAjIGRyb3Agd2lucyB3aXRoIHplcm8gc2l6ZQogIGZpbHRlcl90YWJsZSA8LSBzdWJzZXQoZmlsdGVyX3RhYmxlLCBzaXplID4gTUlOX1NJWkUpCiAgIyBjaGVjayBob3cgbWFueSByb3dzIGFyZSBrZXB0CiAgI25yb3coZmlsdGVyX3RhYmxlKQogICNucm93KHRhYmxlKQogICMgY29udmVydCBOQSB0byAtMQogIGZpbHRlcl90YWJsZVt3aGljaChpcy5uYShmaWx0ZXJfdGFibGUkaGspKSwgJ2hrJ10gPSAtMQogIGZpbHRlcl90YWJsZVt3aGljaChpcy5uYShmaWx0ZXJfdGFibGUkbm9uX2hrKSksICdub25faGsnXSA9IC0xCiAgcmV0dXJuKGZpbHRlcl90YWJsZSkKfQojIHBsb3QgaG91c2VrZWVwaW5nIHJlc3VsdHMKaG91c2VrZWVwaW5nX3ZpZ2ludGlsZSA8LSBmdW5jdGlvbih0YWJsZSwgVFNBLCBkYXRhX2Fubm8sIHRpdGxlKXsKICBUU0FfdmlnaW50aWxlcyA9IGRhdGFfYW5ubyR2aWdpbnRpbGVfY3V0b2ZmCiAgVFNBX3ZpZ2ludGlsZXNbMV0gPSAtMTAKICBUU0FfdmlnaW50aWxlc1tsZW5ndGgoVFNBX3ZpZ2ludGlsZXMpXSA9IDEwCiAgdGFibGUkVFNBX3ZpZ2ludGlsZXMgPC0gY3V0KHRhYmxlWywgVFNBXSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IFRTQV92aWdpbnRpbGVzLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gcGFzdGUwKCIiLCBzZXEoMSwyMCwxKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGluY2x1ZGUubG93ZXN0ID0gVCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgcmlnaHQgPSBUKQogIHRhYmxlJFRTQV92aWdpbnRpbGVzIDwtIGZhY3Rvcih0YWJsZSRUU0FfdmlnaW50aWxlcywgbGV2ZWxzID0gcGFzdGUwKCIiLCBzZXEoMSwyMCwxKSkpCiAgcmVzdWx0IDwtIGRkcGx5KHRhYmxlLCAuKFRTQV92aWdpbnRpbGVzKSwgc3VtbWFyaXplLCAKICAgICAgICAgICAgIGNvdW50X2Jpbl9oayA9IHN1bShoayA+IC0xKSwgCiAgICAgICAgICAgICBjb3VudF9iaW5fbm9uX2hrID0gc3VtKG5vbl9oayA+IC0xKSwgCiAgICAgICAgICAgICBjb3VudF9nZW5lX2hrID0gbGVuZ3RoKHVuaXF1ZShoaykpIC0gMSwgCiAgICAgICAgICAgICBjb3VudF9nZW5lX25vbl9oayA9IGxlbmd0aCh1bmlxdWUobm9uX2hrKSkgLSAxLCAuZHJvcCA9IEZBTFNFKQogIHJlc3VsdCRwZXJfYmluX2hrID0gcmVzdWx0JGNvdW50X2Jpbl9oay9zdW0ocmVzdWx0JGNvdW50X2Jpbl9oaykKICByZXN1bHQkcGVyX2Jpbl9ub25faGsgPSByZXN1bHQkY291bnRfYmluX25vbl9oay9zdW0ocmVzdWx0JGNvdW50X2Jpbl9ub25faGspCiAgcmVzdWx0JHBlcl9nZW5lX2hrID0gcmVzdWx0JGNvdW50X2dlbmVfaGsvc3VtKHJlc3VsdCRjb3VudF9nZW5lX2hrKQogIHJlc3VsdCRwZXJfZ2VuZV9ub25faGsgPSByZXN1bHQkY291bnRfZ2VuZV9ub25faGsvc3VtKHJlc3VsdCRjb3VudF9nZW5lX25vbl9oaykKICByZXN1bHQgPSBtZWx0KHJlc3VsdFssIGMoIlRTQV92aWdpbnRpbGVzIiwgInBlcl9iaW5faGsiLCAicGVyX2Jpbl9ub25faGsiLCAicGVyX2dlbmVfaGsiLCAicGVyX2dlbmVfbm9uX2hrIildLCAKICAgICAgICAgICBpZC52YXJzID0gYygiVFNBX3ZpZ2ludGlsZXMiLCAicGVyX2dlbmVfaGsiLCAicGVyX2dlbmVfbm9uX2hrIiksIAogICAgICAgICAgIHZhcmlhYmxlLm5hbWUgPSAiZ3JvdXBfYmluIiwKICAgICAgICAgICB2YWx1ZS5uYW1lID0gInBlcl9iaW4iKQogIHJlc3VsdCA9IG1lbHQocmVzdWx0LCAKICAgICAgICAgICBpZC52YXJzID0gYygiVFNBX3ZpZ2ludGlsZXMiLCAiZ3JvdXBfYmluIiwgInBlcl9iaW4iKSwgCiAgICAgICAgICAgdmFyaWFibGUubmFtZSA9ICJncm91cF9nZW5lIiwgCiAgICAgICAgICAgdmFsdWUubmFtZSA9ICJwZXJfZ2VuZSIpCiAgcGxvdF92aWdpbnRpbGUgPC0gZ2dwbG90KHVuaXF1ZShyZXN1bHRbICxjKCJUU0FfdmlnaW50aWxlcyIsICJwZXJfZ2VuZSIsICJncm91cF9nZW5lIildKSwgYWVzKHggPSBUU0FfdmlnaW50aWxlcywgeSA9IHBlcl9nZW5lLCBmaWxsID0gZ3JvdXBfZ2VuZSkpICsgCiAgICAgIGdlb21fY29sKGNvbD0iYmxhY2siLCBwb3NpdGlvbiA9ICJkb2RnZSIpICsgCiAgICAgIHhsYWIoIlRTQS1zZXEgdmlnaW50aWxlcyIpICsKICAgICAgeWxhYigiUGVyY2VudGFnZSIpICsKICAgICAgZ2d0aXRsZSgiSEZGIFNPTiBUU0Etc2VxIDIuMCIpICsKICAgICAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHBlcmNlbnQsIGV4cGFuZCA9IGMoMCwwKSkgKwogICAgICBzY2FsZV9maWxsX21hbnVhbChuYW1lPSIiLHZhbHVlcz1jKCJwZXJfZ2VuZV9oayI9IiNlNDFhMWMiLCAicGVyX2dlbmVfbm9uX2hrIj0iIzM3N2ViOCIpLAogICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJob3VzZWtlZXBpbmcgZ2VuZXMiLCAibm9uLWhvdXNla2VlcGluZyBnZW5lcyIpKSArCiAgICAgIHRoZW1lX2J3KCkgKwogICAgICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGxpbmVoZWlnaHQ9LjgsIGZhY2U9ImJvbGQiLCBzaXplPXJlbCgyKSkpICsKICAgICAgdGhlbWUocGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpKSArCiAgICAgIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCkpICsKICAgICAgdGhlbWUoYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemU9cmVsKDEuNSksbWFyZ2luPW1hcmdpbigwLDEwLDAsMCkpKSArCiAgICAgIHRoZW1lKGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplPXJlbCgxLjUpLG1hcmdpbj1tYXJnaW4oMTAsMCwwLDApKSkgKwogICAgICB0aGVtZShheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT1yZWwoMS4yKSwgY29sb3I9ImJsYWNrIikpICsKICAgICAgdGhlbWUoYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG9yPSJibGFjayIpKSArCiAgICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC40LCAwLjgpLCBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gcmVsKDEuMikpKSArCiAgICAgIGNvb3JkX2NhcnRlc2lhbih5bGltID0gYygwLCAwLjE3KSkKICByZXR1cm4ocGxvdF92aWdpbnRpbGUpCn0KCiMjIGxvYWQgcmVzdWx0cyBhbmQgbWFrZSBmaWd1cmVzIAojIGxvYWQgdGhlIHJlc3VsdHMKZGF0YV9IMSA8LSBkZWNpbGVfYW5hbHlzaXNfaGlzdG9uZSgiSDEiLCAiSDFfU09OX1RTQV8yLjAiKQpkYXRhX0s1NjIgPC0gZGVjaWxlX2FuYWx5c2lzX2hpc3RvbmUoIks1NjIiLCAiSzU2Ml9TT05fVFNBXzIuMCIpCmRhdGFfSENUMTE2IDwtIGRlY2lsZV9hbmFseXNpc19oaXN0b25lKCJIQ1QxMTYiLCAiSENUMTE2X1NPTl9UU0FfMi4wIikKZGF0YV9IRkYgPC0gZGVjaWxlX2FuYWx5c2lzX2hpc3RvbmUoIkhGRiIsICJIRkZfU09OX1RTQV8yLjAiKQpleHByX0s1NjIgPC0gZ2VuZV9leHByKCJLNTYyIiwgIks1NjIiLCBkYXRhX0s1NjIpCmV4cHJfSENUMTE2IDwtIGdlbmVfZXhwcigiSENUMTE2IiwgIkhDVDExNiIsIGRhdGFfSENUMTE2KQpleHByX0gxIDwtIGdlbmVfZXhwcigiSDEiLCAiSDEiLCBkYXRhX0gxKQpleHByX0hGRl9HU0UxMDA1NzYgPC0gZ2VuZV9leHByKCJIRkYiLCAiSEZGX0dTRTEwMDU3NiIsIGRhdGFfSEZGKQpleHByX0hGRl9HU0U2NDU1MyA8LSBnZW5lX2V4cHIoIkhGRiIsICJIRkZfR1NFNjQ1NTMiLCBkYXRhX0hGRikKCiMgcmVwb3J0CndyaXRlLnRhYmxlKGV4cHJfSzU2MiwgZmlsZSA9ICJyZXBvcnQvZ2VuY29kZV9leHByX0s1NjIudHh0Iiwgc2VwID0gJ1x0JywgY29sLm5hbWVzID0gVCwgcm93Lm5hbWVzID0gRiwgcXVvdGUgPSBGKQp3cml0ZS50YWJsZShleHByX0hDVDExNiwgZmlsZSA9ICJyZXBvcnQvZ2VuY29kZV9leHByX0hDVDExNi50eHQiLCBzZXAgPSAnXHQnLCBjb2wubmFtZXMgPSBULCByb3cubmFtZXMgPSBGLCBxdW90ZSA9IEYpCndyaXRlLnRhYmxlKGV4cHJfSDEsIGZpbGUgPSAicmVwb3J0L2dlbmNvZGVfZXhwcl9IMS50eHQiLCBzZXAgPSAnXHQnLCBjb2wubmFtZXMgPSBULCByb3cubmFtZXMgPSBGLCBxdW90ZSA9IEYpCndyaXRlLnRhYmxlKGV4cHJfSEZGX0dTRTEwMDU3NiwgZmlsZSA9ICJyZXBvcnQvZ2VuY29kZV9leHByX0hGRl9HU0UxMDA1NzYudHh0Iiwgc2VwID0gJ1x0JywgY29sLm5hbWVzID0gVCwgcm93Lm5hbWVzID0gRiwgcXVvdGUgPSBGKQp3cml0ZS50YWJsZShleHByX0hGRl9HU0U2NDU1MywgZmlsZSA9ICJyZXBvcnQvZ2VuY29kZV9leHByX0hGRl9HU0U2NDU1My50eHQiLCBzZXAgPSAnXHQnLCBjb2wubmFtZXMgPSBULCByb3cubmFtZXMgPSBGLCBxdW90ZSA9IEYpCgojIGhvdXNla2VlcGluZyBnZW5lcwp0YWJsZV9ob3VzZWtlZXBpbmcgPC0gbG9hZF9ob3VzZWtlZXBpbmcoInJlc3VsdC9oZzM4XzIwa2JfaG91c2VrZWVwaW5nLnR4dCIpCnBsb3RfdmlnaW50aWxlX0gxIDwtIGhvdXNla2VlcGluZ192aWdpbnRpbGUodGFibGVfaG91c2VrZWVwaW5nLCAiSDFfU09OX1RTQV8yLjAiLCBkYXRhX0gxLCAiSDEgU09OIFRTQS1zZXEgMi4wIikKcGxvdF92aWdpbnRpbGVfSzU2MiA8LSBob3VzZWtlZXBpbmdfdmlnaW50aWxlKHRhYmxlX2hvdXNla2VlcGluZywgIks1NjJfU09OX1RTQV8yLjAiLCBkYXRhX0s1NjIsICJLNTYyIFNPTiBUU0Etc2VxIDIuMCIpCnBsb3RfdmlnaW50aWxlX0hDVDExNiA8LSBob3VzZWtlZXBpbmdfdmlnaW50aWxlKHRhYmxlX2hvdXNla2VlcGluZywgIkhDVDExNl9TT05fVFNBXzIuMCIsIGRhdGFfSENUMTE2LCAiSENUMTE2IFNPTiBUU0Etc2VxIDIuMCIpCnBsb3RfdmlnaW50aWxlX0hGRiA8LSBob3VzZWtlZXBpbmdfdmlnaW50aWxlKHRhYmxlX2hvdXNla2VlcGluZywgIkhGRl9TT05fVFNBXzIuMCIsIGRhdGFfSEZGLCAiSEZGIFNPTiBUU0Etc2VxIDIuMCIpCnBkZihmaWxlID0gImZpZ3VyZV9ob3VzZWtlZXBpbmdfZ2VuZV92aWdpbnRpbGUucGRmIiwgd2lkdGggPSAxNCwgaGVpZ2h0ID0gMTApCnBsb3RfZ3JpZChwbG90X3ZpZ2ludGlsZV9LNTYyLCBwbG90X3ZpZ2ludGlsZV9IMSwgcGxvdF92aWdpbnRpbGVfSENUMTE2LCBwbG90X3ZpZ2ludGlsZV9IRkYsIGFsaWduID0gImgiKQpkZXYub2ZmKCkKYGBgCg==