Here we run an analysis pipeline in R for the identification of circRNA. The pipeline attempts to recreate parts of the KNIFE pipeline described in the paper by Szabo et al. (2015). The code provided here uses the systemPipeR package with functions designed to run the pipleline on a local desktop. Consult the systemPipeR documentation to modify the code so it can be run on a cluster.
Prepartory steps
To install and resolve bioinformatics software dependecies, you can use the Bioconda package manager. First download Miniconda, then create an environment, activate it, add bioconda channels, and install the required software. For example, here we create a local environment named rnaseq and install bowtie2 and trim-galore.
conda create -n rnaseq python=2 anaconda
source activate rnaseq
conda config --add channels conda-forge
conda config --add channels defaults
conda config --add channels r
conda config --add channels bioconda
conda install bowtie2
conda install trim-galore
1. Create project directories
First start a project and create subdirectories under the project’s working directory
folders <- c(
"parameters", "src",
"data/genome_data/hg19/fasta",
"data/genome_data/hg19/bowtie2_index",
"data/biosamples",
"results/trimmed",
"results/aligned/genome",
"results/aligned/ribosomal",
"results/aligned/junctions_reg",
"results/aligned/junctions_scrambled",
"/results/analysis/reports"
)
for (i in 1:length(folders)) {
dir.create(paste(getwd(), folders[i], sep = "/"), recursive = TRUE)
}
2. Download genome data
To get started, we need to download the hg19_fastas.tar.gz archive that contains fasta files for the ribosome, the linear junctions, the scrambled junctions, and the reference genome. The compressed tar file is available at this link
3. Build index files
We first load the systemPipeR package and other required packages. We also create external files for the command-line software. These are the param files that specify the parameters and the target files that contain the relative path locations of any required data files.
#source("http://bioconductor.org/biocLite.R")
#biocLite("tgirke/systemPipeR", dependencies = TRUE)
library(systemPipeR)
library(dplyr)
The index files are large and might take a long time to download on a regular connection. It could be faster to just run the Bowtie index builder to create the index files. These two lines build the indices. The param and target files are available here.
args_bowtie2_index <- systemArgs(sysma = "./parameters/bowtie2_index.param",
mytargets = "./parameters/hg19_fasta_filepaths.txt")
runCommandline(args = args_bowtie2_index)
4. Download sample data
We use the same HER2 Positive Breast Tumor dataset used in the KNIFE as sample data. The data is made up of paired-end reads from a total RNA rRNA-depleted RNA-seq library. More details about the experiment can be found here. To ensure reproducibility and data provenance, we download the SRA data files directly from the GEO database.
download.file("ftp://ftp-trace.ncbi.nlm.nih.gov/sra/sra-instant/reads/ByExp/sra/SRX/SRX374/SRX374866/SRR1027187/SRR1027187.sra", "./data/biosamples/SRR1027187.sra")
Next we convert the SRA file to fastq using fastq-dump. The options provided specify the following: split the paired reads into two files, dump the biological reads only, and compress the output using gzip. We also keep the defaults by not adding read ids and leaving the quality encoding as ASCII+33.
system("fastq-dump --split-files --skip-technical --gzip ./data/biosamples/SRR1027187.sra --outdir ./data/biosamples/")
5. Trim data
Reads in the raw data can contain low quality bases and partial adapter sequences that needs to be removed. Trimmed reads that are too short should also be filtered in order to obtain good alignment.
args_trim <- systemArgs(sysma = "./parameters/trim_galore.param",
mytargets = "./parameters/data_filepaths.txt")
trim_command <- paste(sysargs(args_trim)[1],
unlist(strsplit(sysargs(args_trim)[2], " "))[8],
sep = " ")
(trim_command)
[1] "trim_galore --length 50 --paired --output_dir /hd2/KNIFE_project_01/results/trimmed/ /hd2/KNIFE_project_01/data/biosamples/SRR1027187_1.fastq.gz /hd2/KNIFE_project_01/data/biosamples/SRR1027187_2.fastq.gz"
Although we are treating each paired end read independently, we add the the –paired option argument in the trim_galore command to peform a validation test that ensures that both sequence pairs have a certain minimum length specified by the option --length. To run trim-galore we use the system function
system(trim_command)
6. Run data quality control checks
Lastly, we generate and plot FASTQ quality summary statistics. The fastqc manual provides a good explantion of the fastqc plots. First we define a helper function
runFastqPlots <- function(file_list, report_name) {
fqlist <- bplapply(seq_along(file_list),
function(x)
seeFastq(
fastq = file_list[x],
batchsize = 100000,
klength = 8
),
BPPARAM = MulticoreParam(workers = 4))
png(
sprintf(
"./results/analysis/reports/fastq_report_%s.png",
report_name
),
height = 800,
width = 200* length(fqlist),
)
seeFastqPlot(unlist(fqlist, recursive = FALSE))
dev.off()
}
then we run it on the raw data
runFastqPlots(file_list = infile1(args_trim),
report_name = "SRR1027187")
and on the trimmed data
data_trimmed_filepaths <- paste0(outfile1(args_trim),
SampleName(args_trim),
c("_val_1.fq.gz", "_val_2.fq.gz"))
names(data_trimmed_filepaths) <- paste0(SampleName(args_trim),"_trimmed")
runFastqPlots(file_list = data_trimmed_filepaths,
report_name = "SRR1027187_trimmed")
Next we plot the reports side by side
The plots for the trimmed data look much better. Although the mean quality distribution for the reads is bimodal, there are no low quality peaks.
Alignment Steps
Now we independently align each paired-end read dataset to four separate Bowtie2 indices: for the genome, ribosomal RNA, linear exon–exon junctions, and scrambled exon–exon junctions. First we load Bowtie2 parameter files and filepaths
args_align_genome <- systemArgs(sysma = "./parameters/bowtie2_align_genome.param",
mytargets = "./parameters/data_trimmed_filepaths.txt")
args_align_ribo <- systemArgs(sysma = "./parameters/bowtie2_align_ribosomal.param",
mytargets = "./parameters/data_trimmed_filepaths.txt")
args_align_juncts_reg <- systemArgs(sysma = "./parameters/bowtie2_align_junctions_reg.param",
mytargets = "./parameters/data_trimmed_filepaths.txt")
args_align_juncts_scram <- systemArgs(sysma = "./parameters/bowtie2_align_junctions_scrambled.param",
mytargets = "./parameters/data_trimmed_filepaths.txt")
then we run the Bowtie2 alignment commands through systemPipeR
# align to genome
genome_bam_files <- runCommandline(args = args_align_genome)
# align to ribosome
ribo_bam_files <- runCommandline(args = args_align_ribo)
# align to linear junctions
juncts_reg_bam_files <- runCommandline(args = args_align_juncts_reg)
# align to srambled junctions
juncts_scram_files <- runCommandline(args = args_align_juncts_scram)
We can open “submitargs01_log” for each alignment to display the alignment summary and the system command used. For the genome alignment, we can open the file inside R like this
readLines(file.path("./results/aligned/genome","submitargs01_log"))
[1] "bowtie2 -p 8 --no-unal --score-min L,0,-0.24 --rdg 50,50 --rfg 50,50 -x /hd2/KNIFE_project_01/data/genome_data/hg19/bowtie2_index/hg19_genome -U /hd2/KNIFE_project_01/results/trimmed/SRR1027187_1_val_1.fq.gz -S /hd2/KNIFE_project_01/results/aligned/genome/SRR1027187_1_val_1.sam"
[2] "19308315 reads; of these:"
[3] " 19308315 (100.00%) were unpaired; of these:"
[4] " 6692805 (34.66%) aligned 0 times"
[5] " 8628824 (44.69%) aligned exactly 1 time"
[6] " 3986686 (20.65%) aligned >1 times"
[7] "65.34% overall alignment rate"
[8] "bowtie2 -p 8 --no-unal --score-min L,0,-0.24 --rdg 50,50 --rfg 50,50 -x /hd2/KNIFE_project_01/data/genome_data/hg19/bowtie2_index/hg19_genome -U /hd2/KNIFE_project_01/results/trimmed/SRR1027187_2_val_2.fq.gz -S /hd2/KNIFE_project_01/results/aligned/genome/SRR1027187_2_val_2.sam"
[9] "19308315 reads; of these:"
[10] " 19308315 (100.00%) were unpaired; of these:"
[11] " 6753734 (34.98%) aligned 0 times"
[12] " 8563704 (44.35%) aligned exactly 1 time"
[13] " 3990877 (20.67%) aligned >1 times"
[14] "65.02% overall alignment rate"
Preprocessing steps
The previous alignment step produced 8 BAM files. Two paired-end BAM files for each of the genome, ribosome, linear junctions, and scrambled junctions alignments. We are interested in junctional reads, those reads from the 8 files that align to a scrambled or linear junctions. Since the majority of the reads in the genome and ribosome BAM files are not junctional, we need to filter them out.
1. Get read IDs for genomic mate pairs
Since the next preprocessing steps are memory intensive, we can intially filter out reads in which both pair mates align to the genome or ribosome. We can also filter out unaligned reads and return lists of read IDs in which both pairs align to the indicies, or in which one pair mate aligns but the other doesn’t. To proceed we need to get the names of those reads that are found in both R1 and R2 genomic alignment files.
getReadIds <- function(bamfile) {
scanBam(bamfile,
param = ScanBamParam(flag = scanBamFlag(isUnmappedQuery = FALSE),
what = "qname"))[[1]][[1]]
}
getCommonGenomicPairedReads <- function(genome_bamfiles, ribo_bamfiles) {
genome_reads <- bplapply(genome_bamfiles,
getReadIds,
BPPARAM = MulticoreParam(workers = 2))
ribosomal_reads <- bplapply(ribo_bamfiles,
getReadIds,
BPPARAM = MulticoreParam(workers = 2))
genomic_reads_1 <- union(genome_reads[[1]], ribosomal_reads[[1]])
genomic_reads_2 <- union(genome_reads[[2]], ribosomal_reads[[2]])
common_reads <- intersect(genomic_reads_1, genomic_reads_2)
r1_not_r2 <- setdiff(genomic_reads_1, genomic_reads_2)
r2_not_r1 <- setdiff(genomic_reads_2, genomic_reads_1)
return(
list(
common_reads = common_reads,
r1_not_r2 = r1_not_r2,
r1_not_r2 = r2_not_r1
)
)
}
Note that code above uses the parallel lapply function bplapply. If you system has less than 32G RAM, you can modify the code to run with one worker or use lapply instead. Here we retrieve the location of the aligned files and feed them into the function defined above.
genome_aligned <- outpaths(args_align_genome)
ribo_aligned <- outpaths(args_align_ribo)
reads_to_ignore <- getCommonGenomicPairedReads(genome_aligned, ribo_aligned)
2. Read alignment data and run initial filtering
Now we use these read IDs to create two filters. The first one filters out genomic reads in which both mates (R1 and R2) align. The second one is a junctional filter that keeps only junctional reads that overlap a particular junction by at least 8 nucleotides and that also satify the first filter.
genomic_reads_filter <-
FilterRules(list(
include = function(x) !(mcols(x)$qname %in% reads_to_ignore$common_reads)
))
MIDPOINT <- 150
JUNC_OVERLAP <- 8
junction_reads_filter <-
FilterRules(list(
include = function(x)
(
(MIDPOINT + JUNC_OVERLAP + 1 - width(x) <= start(x)) &
(start(x) <= MIDPOINT - JUNC_OVERLAP + 1) &
!(mcols(x)$qname %in% reads_to_ignore$common_reads)
)
))
As part of the filtering step, we also update the alignment score for the junction reads such that the N-penalty is corrected.
countAmbigousBases <- function(align_obj) {
sapply(gregexpr("N0|0N", mcols(align_obj)$MD),
function(x)
if (attributes(x)$match.length[1] == -1)
return(0)
else
return(length(x))
)
}
#MD and XN do agree
filterAlignments <-
function(bamfile, param, filter, updateScore = TRUE) {
x <- readGAlignments(file = bamfile, param = param)
x <- subsetByFilter(x, filter)
seqlevels(x) <- seqlevelsInUse(x)
# correct for N-penalty in junction alignments and update alignment score
if (updateScore) {
mcols(x)$AS <- mcols(x)$AS + countAmbigousBases(x)
}
return(x)
}
# get file paths
linear_junctions_aligned <- outpaths(args_align_juncts_reg)
scrambled_junctions_aligned <- outpaths(args_align_juncts_scram)
# run filters
param <-
ScanBamParam(
what = c("qname","mapq"), # get the read name and the mapping quality
flag = scanBamFlag(isUnmappedQuery = FALSE),
tag = c("AS", "XS", "XN", "MD") # get tags
)
genomic_filtered <- bplapply(
genome_aligned,
filterAlignments,
param = param,
filter = genomic_reads_filter,
updateScore = FALSE,
BPPARAM = MulticoreParam(workers = 2)
)
linear_junctions_filtered <- bplapply(
linear_junctions_aligned,
filterAlignments,
param = param,
filter = junction_reads_filter,
BPPARAM = MulticoreParam(workers = 2)
)
scrambled_junctions_filtered <- bplapply(
scrambled_junctions_aligned,
filterAlignments,
param = param,
filter = junction_reads_filter,
BPPARAM = MulticoreParam(workers = 2)
)
Let’s take a look at first few rows of the GAlignment object for the linear junctions R1 aligned reads
linear_junctions_filtered[[1]][1:3,] %>% as.data.frame()
We can also examine the frequency of the cigar strings
linear_junctions_filtered[[1]] %>%
cigar() %>%
table %>%
sort(decreasing = TRUE) %>%
head()
.
60M 59M 58M 57M 56M 55M
397762 138477 45942 13278 3761 3041
3. Convert GAlignment objects into dataframes
toDataFrame <- function(x) {
x <- x %>%
as.data.frame() %>%
subset(select = -c(end, cigar, qwidth, njunc, XS, MD))
colnames(x)[1:8] <- c("junction", "str", "pos", "len",
"id", "MQ", "AS", "XN")
x$junction <- as.character(x$junction)
x$str <- as.character(x$str)
return(x)
}
appendJunctionTags <- function(x) {
df <- x %>%
seqnames() %>%
as.character() %>%
lapply(., function(y) unlist(strsplit(y, "[:]|[|]"))) %>%
do.call(rbind, .) %>%
data.frame(stringsAsFactors = FALSE)
colnames(df) <- c("jChr", "jGene1", "jPos1", "jGene2",
"jPos2", "jType", "jStr")
df$jPos1 <- as.integer(df$jPos1)
df$jPos2 <- as.integer(df$jPos2)
mcols(x) <- cbind(mcols(x), df)
return(toDataFrame(x))
}
genomic_filtered_dt <- bplapply(
genomic_filtered,
toDataFrame,
BPPARAM = MulticoreParam(workers = 2)
)
linear_junctions_filtered_dt <- bplapply(
linear_junctions_filtered,
appendJunctionTags,
BPPARAM = MulticoreParam(workers = 2)
)
scrambled_junctions_filtered_dt <- bplapply(
scrambled_junctions_filtered,
appendJunctionTags,
BPPARAM = MulticoreParam(workers = 2)
)
Let’s take a look at the dataframe for the linear junctions R1 aligned reads
linear_junctions_filtered_dt[[1]][1:3,]
4. Filter R1 junctional reads and pair them with R2 mates
A read (R1) is a linear junction read if it aligns to a linear junction and does not align to the genome or ribosome. In the code these reads are stored in linear_1. A read R1 is a scrambled junction read if it aligns to a scrambled junction and does not align to the genome or ribosome or to the linear junction. Scrambled junction reads are stored in scrambled_1. The read mate R2 can be a genomic, linear junction, or a scrambled junction read as determined by whehter the read name can be found in the R2 aligned reads. An R1 read can have a single or multiple R2 mates. Some have none at all. These are the unmapped reads.
createJunctionsReadsDF <- function(genomic,
linear,
scrambled,
r1_not_r2,
suffixes = c(".R1", ".R2g", ".R2l", ".R2s")) {
linear_1 <- linear[[1]][!(linear[[1]]$id %in% r1_not_r2), ]
scrambled_1 <- scrambled[[1]][!(scrambled[[1]]$id %in% c(r1_not_r2, linear_1$id)), ]
genomic_mate <- genomic[[2]][(genomic[[2]]$id %in% c(linear_1$id,scrambled_1$id)), ]
linear_mate <- linear[[2]][(linear[[2]]$id %in% c(linear_1$id,scrambled_1$id)), ]
scrambled_mate <- scrambled[[2]][(scrambled[[2]]$id %in% c(linear_1$id, scrambled_1$id)), ]
linear_genomic <- left_join(linear_1,
genomic_mate,
by = "id",
suffix = suffixes[c(1, 2)])
linear_linear <- left_join(linear_1,
linear_mate ,
by = "id",
suffix = suffixes[c(1, 3)])
linear_scrambled <- left_join(linear_1,
scrambled_mate,
by = "id",
suffix = suffixes[c(1, 4)])
scrambled_genomic <- left_join(scrambled_1,
genomic_mate,
by = "id",
suffix = suffixes[c(1, 2)])
scrambled_linear <- left_join(scrambled_1,
linear_mate ,
by = "id",
suffix = suffixes[c(1, 3)])
scrambled_scrambled <- left_join(scrambled_1,
scrambled_mate,
by = "id",
suffix = suffixes[c(1, 4)])
linear_df <- cbind(linear_linear[, 1:15],
linear_genomic[, 16:22],
linear_linear[, 16:29],
linear_scrambled[, 16:29])
scrambled_df <- cbind(scrambled_linear[, 1:15],
scrambled_genomic[, 16:22],
scrambled_linear[, 16:29],
scrambled_scrambled[, 16:29])
return(list(linear_df = linear_df, scrambled_df = scrambled_df))
}
R1R2MatesJunctionReads <- createJunctionsReadsDF(genomic_filtered_dt,
linear_junctions_filtered_dt,
scrambled_junctions_filtered_dt,
reads_to_ignore$r1_not_r2)
5. Classify Mate Reads
Here we classify mate reads based on alignment score. We also create a new variable ascore that averages the alignment scores of the two matched reads.
classsifyMateReads <- function(df, suffixes = c(".R1", ".R2g", ".R2l", ".R2s")) {
scores_df <- df[, paste0("AS", suffixes)]
nMates <- apply(scores_df[, 2:4] , 1, function(x) sum(!is.na(x)))
max_score <- apply(scores_df[, 2:4] , 1, which.max)
mate <- sapply(max_score, function(x)
if (is.null(attributes(x)))
{return(0)}
else
{return(as.integer(x))}
)
matescore <- scores_df[cbind(1:nrow(scores_df), (mate + 1))]
ASmean <- 0.5 * (scores_df$AS.R1 + matescore)
df <- cbind(df,
ASmean = ASmean,
nMates = nMates,
mate = mate)
return(df)
}
linear_df <-
R1R2MatesJunctionReads$linear_df %>%
classsifyMateReads()
scrambled_df <-
R1R2MatesJunctionReads$scrambled_df %>%
classsifyMateReads()
cat( " number of linear junction reads",
NROW(linear_df), "\n",
"number of scrambled junction reads",
NROW(scrambled_df), "\n",
"total number of junctional reads:",
NROW(linear_df) + NROW(scrambled_df), "\n",
"number of junctions with aligned reads:",
length(unique(linear_df$junction.R1)) +
length(unique(scrambled_df$junction.R1)))
number of linear junction reads 600658
number of scrambled junction reads 1599
total number of junctional reads: 602257
number of junctions with aligned reads: 108949
Let’s take a look at the dataframe for the linear junctions R1 aligned reads
scrambled_df[1:3, ]
Let’s also output the frequency of R1 reads that have 0, 1, 2, or 3 potential paired-end mate reads (R2)
linear_junction_mapcateg <-
linear_df$nMates %>%
table %>%
as.data.frame()
scrambled_junction_mapcateg <-
scrambled_df$nMates %>%
table %>%
as.data.frame()
names_df <- c("Unmapped","OneMateMatch", "TwoMateMatchs", "ThreeMateMatchs")
rownames(linear_junction_mapcateg) <- names_df
rownames(scrambled_junction_mapcateg) <- names_df
linear_junction_mapcateg
scrambled_junction_mapcateg
6. Classify Junction Reads
We split the reads into reads that have a mapped mate R2 and those that do not. We intially assign all reads as uncategorized (i.e. class=0).
linear_reads_split <- split(linear_df, linear_df$nMates == 0)
scrambled_reads_split <- split(scrambled_df, scrambled_df$nMates == 0)
unmapped_reads <- rbind(linear_reads_split[[2]], scrambled_reads_split[[2]])[,1:15]
linear_reads_mapped <- cbind(linear_reads_split[[1]] , class = 0)
scrambled_reads_mapped <- cbind(scrambled_reads_split[[1]] , class = 0)
Let’s take a look at the unmapped reads
unmapped_reads[1:3,]
Now we proceed to classify scrambled junctional reads as circular or decoy and linear junctional reads as linear or anomalous.
classifyCircularPairedMates <- function(x) {
cond1 <- x$str.R1 != x[, c("str.R2g", "str.R2l", "str.R2s")][cbind(1:nrow(x), x$mate)]
cond2 <- x$jChr.R1 == x[, c("junction.R2g", "jChr.R2l", "jChr.R2s")][cbind(1:nrow(x), x$mate)]
cond3 <- x$mate == 3
cond4 <- x$junction.R1 == x$junction.R2s
x$class[(cond1 & cond2 & cond3 & cond4)] <- 1 #(circ)
return(x)
}
classifyCircularLinearMates <- function(x, BUFFER = 15, MIDPOINT = 150) {
r1_start <- pmin(x$jPos1.R1, x$jPos2.R1)
r1_end <- pmax(x$jPos1.R1, x$jPos2.R1)
r2_start <- pmin(x$jPos1.R2l, x$jPos2.R2l) - MIDPOINT + x$pos.R2l
r2_end <- pmin(x$jPos1.R2l, x$jPos2.R2l) - MIDPOINT + x$pos.R2l + x$len.R2l - 1
cond1 <- x$str.R1 != x[, c("str.R2g", "str.R2l", "str.R2s")][cbind(1:nrow(x), x$mate)]
cond2 <- x$jChr.R1 == x[, c("junction.R2g", "jChr.R2l", "jChr.R2s")][cbind(1:nrow(x), x$mate)]
cond3 <- r2_start >= r1_start - BUFFER
cond4 <- r2_start <= r1_end + BUFFER
cond5 <- r2_end >= r1_start - BUFFER
cond6 <- r2_end <= r1_end + BUFFER
cond7 <- x$mate == 2
cond8 <- !(x$class %in% c(1))
x$class[(cond1 & cond2 & cond3 & cond4 & cond5 & cond6 & cond7 & cond8)] <- 2 #(circ_lin)
return(x)
}
classifyCircularGenomicMates <- function(x, BUFFER = 15, MIDPOINT = 150) {
r1_start <- pmin(x$pos.R1, x$jPos2.R1)
r1_end <- pmax(x$jPos1.R1, x$jPos2.R1)
r2_start <- x$pos.R2g
r2_end <- x$pos.R2g + x$len.R2g - 1
cond1 <- x$str.R1 != x[, c("str.R2g", "str.R2l", "str.R2s")][cbind(1:nrow(x), x$mate)]
cond2 <- x$jChr.R1 == x[, c("junction.R2g", "jChr.R2l", "jChr.R2s")][cbind(1:nrow(x), x$mate)]
cond3 <- r2_start >= r1_start - BUFFER
cond4 <- r2_start <= r1_end + BUFFER
cond5 <- r2_end >= r1_start - BUFFER
cond6 <- r2_end <= r1_end + BUFFER
cond7 <- x$mate == 1
cond8 <- !(x$class %in% c(1, 2))
x$class[(cond1 & cond2 & cond3 & cond4 & cond5 & cond6 & cond7 & cond8 )] <- 3 #(circ_gen)
return(x)
}
classifyCircularIgnore <- function(x, BUFFER = 15, MIDPOINT = 150) {
r1_start <- pmin(x$jPos1.R1, x$jPos2.R1) - MIDPOINT + x$pos.R1
r1_end <- r1_start + x$len.R1 - 1
cond1 <- x$str.R1 != x[, c("str.R2g", "str.R2l", "str.R2s")][cbind(1:nrow(x), x$mate)]
cond2 <- x$jChr.R1 == x[, c("junction.R2g", "jChr.R2l", "jChr.R2s")][cbind(1:nrow(x), x$mate)]
cond3 <- r1_start >= pmin(x$jPos1.R2s, x$jPos2.R2s) - BUFFER
cond4 <- r1_start <= pmax(x$jPos1.R2s, x$jPos2.R2s) + BUFFER
cond5 <- r1_end >= pmin(x$jPos1.R2s, x$jPos2.R2s) - BUFFER
cond6 <- r1_end <= pmax(x$jPos1.R2s, x$jPos2.R2s) + BUFFER
cond7 <- x$mate == 3
cond8 <- !(x$class %in% c(1, 2, 3))
x$class[(cond1 & cond2 & cond3 & cond4 & cond5 & cond6 & cond7 & cond8)] <- 4
return(x)
}
classifyCircularDecoy <- function(x) {
cond1 <- x$str.R1 != x[, c("str.R2g", "str.R2l", "str.R2s")][cbind(1:nrow(x), x$mate)]
cond2 <- x$jChr.R1 == x[, c("junction.R2g", "jChr.R2l", "jChr.R2s")][cbind(1:nrow(x), x$mate)]
cond3 <- !(x$class %in% c(1, 2, 3, 4))
cond4 <- x$mate == 3
x$class[(cond1 & cond2 & cond3) ] <- 5
x$class[!(cond1 & cond2) ] <- 5
return(x)
}
classifyLinearPairedMates <- function(x, BUFFER = 15, MIDPOINT = 150) {
r1_start <- pmin(x$jPos1.R1, x$jPos2.R1) - MIDPOINT + x$pos.R1 # myCoord
r2_start <- pmin(x$jPos1.R2l, x$jPos2.R2l) - MIDPOINT + x$pos.R2l #mateCoord
cond1 <- x$str.R1 != x[, c("str.R2g", "str.R2l", "str.R2s")][cbind(1:nrow(x), x$mate)]
cond2 <- x$jChr.R1 == x[, c("junction.R2g", "jChr.R2l", "jChr.R2s")][cbind(1:nrow(x), x$mate)]
cond3_p <- r2_start >= r1_start - BUFFER
cond3_m <- r1_start >= r2_start - BUFFER
cond3 <- ifelse(x$str.R1 == "+", cond3_p, cond3_m)
cond4 <- x$mate == 2
x$class[(cond1 & cond2 & cond3 & cond4)] <- 1
return(x)
}
classifyLinearGenomicMates <- function(x, BUFFER = 15, MIDPOINT = 150) {
r1_start <- pmin(x$jPos1.R1, x$jPos2.R1) - MIDPOINT + x$pos.R1
r2_start <- x$pos.R2g
cond1 <- x$str.R1 != x[, c("str.R2g", "str.R2l", "str.R2s")][cbind(1:nrow(x), x$mate)]
cond2 <- x$jChr.R1 == x[, c("junction.R2g", "jChr.R2l", "jChr.R2s")][cbind(1:nrow(x), x$mate)]
cond3_p <- r2_start >= r1_start - BUFFER
cond3_m <- r1_start >= r2_start - BUFFER
cond3 <- ifelse(x$str.R1 == "+", cond3_p, cond3_m)
cond4 <- x$mate == 1
cond5 <- !(x$class %in% c(1))
x$class[(cond1 & cond2 & cond3 & cond4 & cond5)] <- 2
return(x)
}
classifyLinearIgnore <- function(x) {
cond1 <- x$str.R1 != x[, c("str.R2g", "str.R2l", "str.R2s")][cbind(1:nrow(x), x$mate)]
cond2 <- x$jChr.R1 == x[, c("junction.R2g", "jChr.R2l", "jChr.R2s")][cbind(1:nrow(x), x$mate)]
cond3 <- x$mate == 3
x$class[(cond1 & cond2 & cond3 )] <- 3
return(x)
}
classifyLinearAnomaly <- function(x) {
cond1 <- x$str.R1 != x[, c("str.R2g", "str.R2l", "str.R2s")][cbind(1:nrow(x), x$mate)]
cond2 <- x$jChr.R1 == x[, c("junction.R2g", "jChr.R2l", "jChr.R2s")][cbind(1:nrow(x), x$mate)]
x$class[!(cond1 & cond2)] <- 4
x$class[x$class == 0] <- 4
return(x)
}
linear_reads_mapped <-
linear_reads_mapped %>%
classifyLinearPairedMates() %>%
classifyLinearGenomicMates() %>%
classifyLinearIgnore() %>%
classifyLinearAnomaly()
scrambled_reads_mapped <-
scrambled_reads_mapped %>%
classifyCircularPairedMates() %>%
classifyCircularLinearMates() %>%
classifyCircularGenomicMates %>%
classifyCircularIgnore %>%
classifyCircularDecoy
linear_reads_mapped[]
The frequency of read categories are as follows:
linear_junction_reads <-
linear_reads_mapped$class %>%
tabulate %>%
as.data.frame()
scrambled_junction_reads <-
scrambled_reads_mapped$class %>%
tabulate %>%
as.data.frame()
rownames(linear_junction_reads) <- c("Linear.l",
"Linear.g",
"Ignore",
"Anomaly")
rownames(scrambled_junction_reads) <- c("Circular.s",
"Circular.l",
"Circular.g",
"Ignore",
"Decoy")
linear_junction_reads
scrambled_junction_reads
cat( " number of linear reads:",
sum(linear_junction_reads[1:2,]), "\n",
"number of circular reads:",
sum(scrambled_junction_reads[1:3,]), "\n",
"total number of anomaly reads:",
sum(linear_junction_reads[4,]), "\n",
"number of decoy reads:",
sum(scrambled_junction_reads[5,]))
number of linear reads: 540360
number of circular reads: 1265
total number of anomaly reads: 14474
number of decoy reads: 117
Reorder columns
refcols <- c("id","class", "mate")
linear_reads_mapped <- linear_reads_mapped[, c(refcols,
setdiff(names(linear_reads_mapped),
refcols))]
scrambled_reads_mapped <- scrambled_reads_mapped[, c(refcols,
setdiff(names(scrambled_reads_mapped),
refcols))]
scrambled_reads_mapped
Save data
write.table(linear_reads_mapped,
"linear_reads_mapped.txt",
row.names = FALSE,
sep = "\t")
write.table(scrambled_reads_mapped,
"scrambled_reads_mapped.txt",
row.names = FALSE,
sep = "\t")
saveRDS(scrambled_reads_mapped, "scrambled_reads_mapped.rds")
saveRDS(linear_reads_mapped, "linear_reads_mapped.rds")
#scrambled_reads_mapped <- readRDS("scrambled_reads_mapped.rds")
#linear_reads_mapped <- readRDS("linear_reads_mapped.rds")
sessionInfo()
R version 3.3.2 (2016-10-31)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Ubuntu 16.04.1 LTS
locale:
[1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C LC_TIME=en_US.UTF-8 LC_COLLATE=en_US.UTF-8 LC_MONETARY=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8
[7] LC_PAPER=en_US.UTF-8 LC_NAME=C LC_ADDRESS=C LC_TELEPHONE=C LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C
attached base packages:
[1] stats4 parallel stats graphics grDevices utils datasets methods base
other attached packages:
[1] dplyr_0.5.0 systemPipeR_1.9.2 ShortRead_1.32.0 GenomicAlignments_1.10.0 SummarizedExperiment_1.4.0 Biobase_2.34.0
[7] BiocParallel_1.8.1 Rsamtools_1.26.1 Biostrings_2.42.1 XVector_0.14.0 GenomicRanges_1.26.1 GenomeInfoDb_1.10.2
[13] IRanges_2.8.1 S4Vectors_0.12.1 BiocGenerics_0.20.0
loaded via a namespace (and not attached):
[1] Rcpp_0.12.8 locfit_1.5-9.1 lattice_0.20-34 GO.db_3.4.0 assertthat_0.1 digest_0.6.10 R6_2.2.0
[8] plyr_1.8.4 BatchJobs_1.6 backports_1.0.4 RSQLite_1.1-1 ggplot2_2.2.1 zlibbioc_1.20.0 GenomicFeatures_1.26.2
[15] lazyeval_0.2.0 annotate_1.52.1 Matrix_1.2-7.1 checkmate_1.8.2 GOstats_2.40.0 splines_3.3.2 stringr_1.1.0
[22] pheatmap_1.0.8 RCurl_1.95-4.8 biomaRt_2.30.0 munsell_0.4.3 sendmailR_1.2-1 rtracklayer_1.34.1 base64enc_0.1-3
[29] BBmisc_1.10 fail_1.3 tibble_1.2 edgeR_3.16.5 XML_3.98-1.5 AnnotationForge_1.16.0 bitops_1.0-6
[36] grid_3.3.2 RBGL_1.50.0 xtable_1.8-2 GSEABase_1.36.0 gtable_0.2.0 DBI_0.5-1 magrittr_1.5
[43] scales_0.4.1 graph_1.52.0 stringi_1.1.2 hwriter_1.3.2 genefilter_1.56.0 limma_3.30.7 latticeExtra_0.6-28
[50] brew_1.0-6 rjson_0.2.15 RColorBrewer_1.1-2 tools_3.3.2 Category_2.40.0 survival_2.40-1 AnnotationDbi_1.36.0
[57] colorspace_1.3-2 memoise_1.0.0 knitr_1.15.1
LS0tCnRpdGxlOiAiSWRlbnRpZmljYXRpb24gYW5kIHF1YW50aWZpY2F0aW9uIG9mIGNpcmN1bGFyIFJOQSBmcm9tIFJOQS1TZXEgZGF0YSIKYXV0aG9yOiAiUmljayBGYXJvdW5pIgpkYXRlOiAiSmFudWFyeSA0LCAyMDE3IgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKYmlibGlvZ3JhcGh5OiBiaWJmaWxlLmJpYgotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIGNhY2hlID0gVFJVRSwgY29tbWVudCA9ICIiLCB3aWR0aCA9IDEyMCApCmBgYAoKSGVyZSB3ZSBydW4gYW4gYW5hbHlzaXMgcGlwZWxpbmUgaW4gUiBmb3IgdGhlIGlkZW50aWZpY2F0aW9uIG9mIGNpcmNSTkEuIFRoZSBwaXBlbGluZSBhdHRlbXB0cyB0byByZWNyZWF0ZSBwYXJ0cyBvZiB0aGUgW0tOSUZFIHBpcGVsaW5lXShodHRwczovL2dpdGh1Yi5jb20vbGluZGFzemFiby9LTklGRSkgZGVzY3JpYmVkIGluIHRoZSBwYXBlciBieSBAU3phYm8yMDE1LiBUaGUgY29kZSBwcm92aWRlZCBoZXJlIHVzZXMgdGhlICpzeXN0ZW1QaXBlUiogcGFja2FnZSB3aXRoIGZ1bmN0aW9ucyBkZXNpZ25lZCB0byBydW4gdGhlIHBpcGxlbGluZSBvbiBhIGxvY2FsIGRlc2t0b3AuIENvbnN1bHQgdGhlICpzeXN0ZW1QaXBlUiogZG9jdW1lbnRhdGlvbiB0byBtb2RpZnkgdGhlIGNvZGUgc28gaXQgY2FuIGJlIHJ1biBvbiBhIGNsdXN0ZXIuCgoKIyMgUHJlcGFydG9yeSBzdGVwcwoKVG8gaW5zdGFsbCBhbmQgcmVzb2x2ZSBiaW9pbmZvcm1hdGljcyBzb2Z0d2FyZSBkZXBlbmRlY2llcywgeW91IGNhbiB1c2UgdGhlIFtCaW9jb25kYV0oaHR0cDovL2Jpb2NvbmRhLmdpdGh1Yi5pby8pIHBhY2thZ2UgbWFuYWdlci4gRmlyc3QgZG93bmxvYWQgW01pbmljb25kYV0oaHR0cDovL2NvbmRhLnB5ZGF0YS5vcmcvbWluaWNvbmRhLmh0bWwpLCB0aGVuIGNyZWF0ZSBhbiBlbnZpcm9ubWVudCwgYWN0aXZhdGUgaXQsIGFkZCBiaW9jb25kYSBjaGFubmVscywgYW5kIGluc3RhbGwgdGhlIHJlcXVpcmVkIHNvZnR3YXJlLiBGb3IgZXhhbXBsZSwgaGVyZSB3ZSBjcmVhdGUgYSBsb2NhbCBlbnZpcm9ubWVudCBuYW1lZCAqcm5hc2VxKiBhbmQgaW5zdGFsbCAqYm93dGllMiogYW5kICp0cmltLWdhbG9yZSouCgpgYGAKY29uZGEgY3JlYXRlIC1uIHJuYXNlcSBweXRob249MiBhbmFjb25kYQpzb3VyY2UgYWN0aXZhdGUgcm5hc2VxCmNvbmRhIGNvbmZpZyAtLWFkZCBjaGFubmVscyBjb25kYS1mb3JnZQpjb25kYSBjb25maWcgLS1hZGQgY2hhbm5lbHMgZGVmYXVsdHMKY29uZGEgY29uZmlnIC0tYWRkIGNoYW5uZWxzIHIKY29uZGEgY29uZmlnIC0tYWRkIGNoYW5uZWxzIGJpb2NvbmRhCmNvbmRhIGluc3RhbGwgYm93dGllMgpjb25kYSBpbnN0YWxsIHRyaW0tZ2Fsb3JlCmBgYAojIyMgMS4gQ3JlYXRlIHByb2plY3QgZGlyZWN0b3JpZXMgCgpGaXJzdCBzdGFydCBhIHByb2plY3QgYW5kIGNyZWF0ZSBzdWJkaXJlY3RvcmllcyB1bmRlciB0aGUgcHJvamVjdCdzIHdvcmtpbmcgZGlyZWN0b3J5CgpgYGB7ciAsIGV2YWw9RkFMU0V9CmZvbGRlcnMgPC0gYygKICAicGFyYW1ldGVycyIsICJzcmMiLAogICJkYXRhL2dlbm9tZV9kYXRhL2hnMTkvZmFzdGEiLAogICJkYXRhL2dlbm9tZV9kYXRhL2hnMTkvYm93dGllMl9pbmRleCIsCiAgImRhdGEvYmlvc2FtcGxlcyIsCiAgInJlc3VsdHMvdHJpbW1lZCIsCiAgInJlc3VsdHMvYWxpZ25lZC9nZW5vbWUiLAogICJyZXN1bHRzL2FsaWduZWQvcmlib3NvbWFsIiwKICAicmVzdWx0cy9hbGlnbmVkL2p1bmN0aW9uc19yZWciLAogICJyZXN1bHRzL2FsaWduZWQvanVuY3Rpb25zX3NjcmFtYmxlZCIsCiAgIi9yZXN1bHRzL2FuYWx5c2lzL3JlcG9ydHMiCikgCgpmb3IgKGkgaW4gMTpsZW5ndGgoZm9sZGVycykpICB7IAogIGRpci5jcmVhdGUocGFzdGUoZ2V0d2QoKSwgZm9sZGVyc1tpXSwgc2VwID0gIi8iKSwgcmVjdXJzaXZlID0gVFJVRSkgCn0KCmBgYAoKIyMjIDIuIERvd25sb2FkIGdlbm9tZSBkYXRhCgpUbyBnZXQgc3RhcnRlZCwgd2UgbmVlZCB0byBkb3dubG9hZCB0aGUgKmhnMTlfZmFzdGFzLnRhci5neiogYXJjaGl2ZSB0aGF0IGNvbnRhaW5zIGZhc3RhIGZpbGVzIGZvciB0aGUgcmlib3NvbWUsIHRoZSBsaW5lYXIganVuY3Rpb25zLCB0aGUgc2NyYW1ibGVkIGp1bmN0aW9ucywgYW5kIHRoZSByZWZlcmVuY2UgZ2Vub21lLiBUaGUgY29tcHJlc3NlZCB0YXIgZmlsZSBpcyBhdmFpbGFibGUgYXQgdGhpcyBbbGlua10oaHR0cHM6Ly9tZWdhLm56LyNGIVJ0c0NIQ1FiIWZ5eFlOV2pvQ2VmNUllMzYxdlV4aUEhSjRkSDFicUwpCgojIyMgMy4gQnVpbGQgaW5kZXggZmlsZXMKCldlIGZpcnN0IGxvYWQgdGhlICpzeXN0ZW1QaXBlUiogcGFja2FnZSBhbmQgb3RoZXIgcmVxdWlyZWQgcGFja2FnZXMuIFdlIGFsc28gY3JlYXRlIGV4dGVybmFsIGZpbGVzIGZvciB0aGUgY29tbWFuZC1saW5lIHNvZnR3YXJlLiBUaGVzZSBhcmUgdGhlICpwYXJhbSogZmlsZXMgdGhhdCBzcGVjaWZ5IHRoZSBwYXJhbWV0ZXJzIGFuZCB0aGUgKnRhcmdldCogZmlsZXMgdGhhdCBjb250YWluIHRoZSByZWxhdGl2ZSBwYXRoIGxvY2F0aW9ucyBvZiBhbnkgcmVxdWlyZWQgZGF0YSBmaWxlcy4KCmBgYHtyICwgbWVzc2FnZT1GQUxTRX0gCiNzb3VyY2UoImh0dHA6Ly9iaW9jb25kdWN0b3Iub3JnL2Jpb2NMaXRlLlIiKQojYmlvY0xpdGUoInRnaXJrZS9zeXN0ZW1QaXBlUiIsIGRlcGVuZGVuY2llcyA9IFRSVUUpCmxpYnJhcnkoc3lzdGVtUGlwZVIpCmxpYnJhcnkoZHBseXIpCmBgYAoKVGhlIGluZGV4IGZpbGVzIGFyZSBsYXJnZSBhbmQgbWlnaHQgdGFrZSBhIGxvbmcgdGltZSB0byBkb3dubG9hZCBvbiBhIHJlZ3VsYXIgY29ubmVjdGlvbi4gSXQgY291bGQgYmUgZmFzdGVyIHRvIGp1c3QgcnVuIHRoZSBCb3d0aWUgaW5kZXggYnVpbGRlciB0byBjcmVhdGUgdGhlIGluZGV4IGZpbGVzLiBUaGVzZSB0d28gbGluZXMgYnVpbGQgdGhlIGluZGljZXMuIFRoZSBwYXJhbSBhbmQgdGFyZ2V0IGZpbGVzIGFyZSBhdmFpbGFibGUgW2hlcmVdKGh0dHBzOi8vZ2l0aHViLmNvbS9yZmFyb3VuaS9yZmFyb3VuaS5naXRodWIuaW8vdHJlZS9tYXN0ZXIvYXNzZXRzL3Byb2plY3RzL2NpcmNSTkEpLgoKYGBge3IgLCBldmFsPUZBTFNFfSAKYXJnc19ib3d0aWUyX2luZGV4IDwtIHN5c3RlbUFyZ3Moc3lzbWEgPSAiLi9wYXJhbWV0ZXJzL2Jvd3RpZTJfaW5kZXgucGFyYW0iLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbXl0YXJnZXRzID0gIi4vcGFyYW1ldGVycy9oZzE5X2Zhc3RhX2ZpbGVwYXRocy50eHQiKQpydW5Db21tYW5kbGluZShhcmdzID0gYXJnc19ib3d0aWUyX2luZGV4KQpgYGAKCiMjIyA0LiBEb3dubG9hZCBzYW1wbGUgZGF0YQoKV2UgdXNlIHRoZSBzYW1lIEhFUjIgUG9zaXRpdmUgQnJlYXN0IFR1bW9yIGRhdGFzZXQgdXNlZCBpbiB0aGUgS05JRkUgYXMgc2FtcGxlIGRhdGEuIFRoZSBkYXRhIGlzIG1hZGUgdXAgb2YgcGFpcmVkLWVuZCByZWFkcyBmcm9tIGEgdG90YWwgUk5BIHJSTkEtZGVwbGV0ZWQgUk5BLXNlcSBsaWJyYXJ5LiBNb3JlIGRldGFpbHMgYWJvdXQgdGhlIGV4cGVyaW1lbnQgY2FuIGJlIGZvdW5kIFtoZXJlXShodHRwczovL3d3dy5uY2JpLm5sbS5uaWguZ292L2dlby9xdWVyeS9hY2MuY2dpP2FjYz1HU00xMjYxMDMyKS4gVG8gZW5zdXJlIHJlcHJvZHVjaWJpbGl0eSBhbmQgZGF0YSBwcm92ZW5hbmNlLCB3ZSBkb3dubG9hZCB0aGUgU1JBIGRhdGEgZmlsZXMgZGlyZWN0bHkgZnJvbSB0aGUgR0VPIGRhdGFiYXNlLiAKCmBgYHtyICwgZXZhbD1GQUxTRX0KZG93bmxvYWQuZmlsZSgiZnRwOi8vZnRwLXRyYWNlLm5jYmkubmxtLm5paC5nb3Yvc3JhL3NyYS1pbnN0YW50L3JlYWRzL0J5RXhwL3NyYS9TUlgvU1JYMzc0L1NSWDM3NDg2Ni9TUlIxMDI3MTg3L1NSUjEwMjcxODcuc3JhIiwgIi4vZGF0YS9iaW9zYW1wbGVzL1NSUjEwMjcxODcuc3JhIikKYGBgCgpOZXh0IHdlIGNvbnZlcnQgdGhlIFNSQSBmaWxlIHRvIGZhc3RxIHVzaW5nICpmYXN0cS1kdW1wKi4gVGhlIG9wdGlvbnMgcHJvdmlkZWQgc3BlY2lmeSB0aGUgZm9sbG93aW5nOiBzcGxpdCB0aGUgcGFpcmVkIHJlYWRzIGludG8gdHdvIGZpbGVzLCBkdW1wIHRoZSBiaW9sb2dpY2FsIHJlYWRzIG9ubHksIGFuZCBjb21wcmVzcyB0aGUgb3V0cHV0IHVzaW5nIGd6aXAuIFdlIGFsc28ga2VlcCB0aGUgZGVmYXVsdHMgYnkgbm90IGFkZGluZyByZWFkIGlkcyBhbmQgbGVhdmluZyB0aGUgcXVhbGl0eSBlbmNvZGluZyBhcyBBU0NJSSszMy4KCmBgYHtyICwgZXZhbD1GQUxTRX0Kc3lzdGVtKCJmYXN0cS1kdW1wIC0tc3BsaXQtZmlsZXMgLS1za2lwLXRlY2huaWNhbCAtLWd6aXAgLi9kYXRhL2Jpb3NhbXBsZXMvU1JSMTAyNzE4Ny5zcmEgLS1vdXRkaXIgLi9kYXRhL2Jpb3NhbXBsZXMvIikKYGBgCgojIyMgNS4gVHJpbSBkYXRhCgpSZWFkcyBpbiB0aGUgcmF3IGRhdGEgY2FuIGNvbnRhaW4gbG93IHF1YWxpdHkgYmFzZXMgYW5kIHBhcnRpYWwgYWRhcHRlciBzZXF1ZW5jZXMgdGhhdCBuZWVkcyB0byBiZSByZW1vdmVkLiBUcmltbWVkIHJlYWRzIHRoYXQgYXJlIHRvbyBzaG9ydCBzaG91bGQgYWxzbyBiZSBmaWx0ZXJlZCBpbiBvcmRlciB0byBvYnRhaW4gZ29vZCBhbGlnbm1lbnQuICAgIAoKYGBge3IgfQoKYXJnc190cmltIDwtIHN5c3RlbUFyZ3Moc3lzbWEgPSAiLi9wYXJhbWV0ZXJzL3RyaW1fZ2Fsb3JlLnBhcmFtIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG15dGFyZ2V0cyA9ICIuL3BhcmFtZXRlcnMvZGF0YV9maWxlcGF0aHMudHh0IikKdHJpbV9jb21tYW5kIDwtIHBhc3RlKHN5c2FyZ3MoYXJnc190cmltKVsxXSwgCiAgICAgICAgICAgICAgICAgICAgICB1bmxpc3Qoc3Ryc3BsaXQoc3lzYXJncyhhcmdzX3RyaW0pWzJdLCAiICIpKVs4XSwKICAgICAgICAgICAgICAgICAgICAgIHNlcCA9ICIgIikKKHRyaW1fY29tbWFuZCkKYGBgCgpBbHRob3VnaCB3ZSBhcmUgdHJlYXRpbmcgZWFjaCBwYWlyZWQgZW5kIHJlYWQgaW5kZXBlbmRlbnRseSwgd2UgYWRkIHRoZSB0aGUgKi0tcGFpcmVkKiBvcHRpb24gYXJndW1lbnQgaW4gdGhlIHRyaW1fZ2Fsb3JlIGNvbW1hbmQgdG8gcGVmb3JtIGEgdmFsaWRhdGlvbiB0ZXN0IHRoYXQgZW5zdXJlcyB0aGF0IGJvdGggc2VxdWVuY2UgcGFpcnMgaGF2ZSBhIGNlcnRhaW4gbWluaW11bSBsZW5ndGggc3BlY2lmaWVkIGJ5IHRoZSBvcHRpb24gLSotbGVuZ3RoKi4gVG8gcnVuIHRyaW0tZ2Fsb3JlIHdlIHVzZSB0aGUgKnN5c3RlbSogZnVuY3Rpb24gCgpgYGB7ciAsIGV2YWw9RkFMU0V9CnN5c3RlbSh0cmltX2NvbW1hbmQpCmBgYAoKIyMjIDYuIFJ1biBkYXRhIHF1YWxpdHkgY29udHJvbCBjaGVja3MKCkxhc3RseSwgd2UgZ2VuZXJhdGUgYW5kIHBsb3QgRkFTVFEgcXVhbGl0eSBzdW1tYXJ5IHN0YXRpc3RpY3MuICBUaGUgW2Zhc3RxYyBtYW51YWxdKGh0dHBzOi8vYmlvZi1lZHUuY29sb3JhZG8uZWR1L3ZpZGVvcy9kb3dlbGwtc2hvcnQtcmVhZC1jbGFzcy9kYXktNC9mYXN0cWMtbWFudWFsKSBwcm92aWRlcyBhIGdvb2QgZXhwbGFudGlvbiBvZiB0aGUgZmFzdHFjIHBsb3RzLiBGaXJzdCB3ZSBkZWZpbmUgYSBoZWxwZXIgZnVuY3Rpb24KCmBgYHtyICwgZXZhbD1GQUxTRX0KcnVuRmFzdHFQbG90cyA8LSBmdW5jdGlvbihmaWxlX2xpc3QsIHJlcG9ydF9uYW1lKSB7CiAgZnFsaXN0IDwtIGJwbGFwcGx5KHNlcV9hbG9uZyhmaWxlX2xpc3QpLAogICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbih4KQogICAgICAgICAgICAgICAgICAgICAgIHNlZUZhc3RxKAogICAgICAgICAgICAgICAgICAgICAgICAgZmFzdHEgPSBmaWxlX2xpc3RbeF0sCiAgICAgICAgICAgICAgICAgICAgICAgICBiYXRjaHNpemUgPSAxMDAwMDAsCiAgICAgICAgICAgICAgICAgICAgICAgICBrbGVuZ3RoID0gOAogICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICAgICAgIEJQUEFSQU0gPSBNdWx0aWNvcmVQYXJhbSh3b3JrZXJzID0gNCkpCiAgcG5nKAogICAgc3ByaW50ZigKICAgICAgIi4vcmVzdWx0cy9hbmFseXNpcy9yZXBvcnRzL2Zhc3RxX3JlcG9ydF8lcy5wbmciLAogICAgICByZXBvcnRfbmFtZQogICAgKSwKICAgIGhlaWdodCA9IDgwMCwKICAgIHdpZHRoID0gMjAwKiBsZW5ndGgoZnFsaXN0KSwKICApCiAgc2VlRmFzdHFQbG90KHVubGlzdChmcWxpc3QsIHJlY3Vyc2l2ZSA9IEZBTFNFKSkKICBkZXYub2ZmKCkKfQpgYGAKCnRoZW4gd2UgcnVuIGl0IG9uIHRoZSByYXcgZGF0YQoKYGBge3IgLCBldmFsPUZBTFNFfSAKcnVuRmFzdHFQbG90cyhmaWxlX2xpc3QgPSBpbmZpbGUxKGFyZ3NfdHJpbSksCiAgICAgICAgICAgICAgcmVwb3J0X25hbWUgPSAiU1JSMTAyNzE4NyIpCmBgYAoKYW5kIG9uIHRoZSB0cmltbWVkIGRhdGEKCmBgYHtyICwgZXZhbD1GQUxTRX0KZGF0YV90cmltbWVkX2ZpbGVwYXRocyA8LSBwYXN0ZTAob3V0ZmlsZTEoYXJnc190cmltKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgU2FtcGxlTmFtZShhcmdzX3RyaW0pLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYygiX3ZhbF8xLmZxLmd6IiwgIl92YWxfMi5mcS5neiIpKQpuYW1lcyhkYXRhX3RyaW1tZWRfZmlsZXBhdGhzKSA8LSBwYXN0ZTAoU2FtcGxlTmFtZShhcmdzX3RyaW0pLCJfdHJpbW1lZCIpCgpydW5GYXN0cVBsb3RzKGZpbGVfbGlzdCA9IGRhdGFfdHJpbW1lZF9maWxlcGF0aHMsCiAgICAgICAgICAgICAgcmVwb3J0X25hbWUgPSAiU1JSMTAyNzE4N190cmltbWVkIikKYGBgCgpOZXh0IHdlIHBsb3QgdGhlIHJlcG9ydHMgc2lkZSBieSBzaWRlCgohW2Zhc3RxY3Bsb3RzXSguL3Jlc3VsdHMvYW5hbHlzaXMvcmVwb3J0cy9mYXN0cV9yZXBvcnRfU1JSMTAyNzE4Ny5wbmcpICFbZmFzdHFjX3RyaW1tZWRfcGxvdHNdKC4vcmVzdWx0cy9hbmFseXNpcy9yZXBvcnRzL2Zhc3RxX3JlcG9ydF9TUlIxMDI3MTg3X3RyaW1tZWQucG5nKQoKVGhlIHBsb3RzIGZvciB0aGUgdHJpbW1lZCBkYXRhIGxvb2sgbXVjaCBiZXR0ZXIuIEFsdGhvdWdoIHRoZSBtZWFuIHF1YWxpdHkgZGlzdHJpYnV0aW9uIGZvciB0aGUgcmVhZHMgaXMgYmltb2RhbCwgdGhlcmUgYXJlIG5vIGxvdyBxdWFsaXR5IHBlYWtzLiAKCiMjIEFsaWdubWVudCBTdGVwcwoKTm93IHdlIGluZGVwZW5kZW50bHkgYWxpZ24gZWFjaCBwYWlyZWQtZW5kIHJlYWQgZGF0YXNldCB0byBmb3VyIHNlcGFyYXRlIEJvd3RpZTIgaW5kaWNlczogZm9yIHRoZSBnZW5vbWUsIHJpYm9zb21hbCBSTkEsIGxpbmVhciBleG9u4oCTZXhvbiBqdW5jdGlvbnMsIGFuZCBzY3JhbWJsZWQgZXhvbuKAk2V4b24ganVuY3Rpb25zLiBGaXJzdCB3ZSBsb2FkIEJvd3RpZTIgcGFyYW1ldGVyIGZpbGVzIGFuZCBmaWxlcGF0aHMgCgpgYGB7ciB9IAphcmdzX2FsaWduX2dlbm9tZSA8LSBzeXN0ZW1BcmdzKHN5c21hID0gIi4vcGFyYW1ldGVycy9ib3d0aWUyX2FsaWduX2dlbm9tZS5wYXJhbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbXl0YXJnZXRzID0gIi4vcGFyYW1ldGVycy9kYXRhX3RyaW1tZWRfZmlsZXBhdGhzLnR4dCIpCmFyZ3NfYWxpZ25fcmlibyA8LSBzeXN0ZW1BcmdzKHN5c21hID0gIi4vcGFyYW1ldGVycy9ib3d0aWUyX2FsaWduX3JpYm9zb21hbC5wYXJhbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG15dGFyZ2V0cyA9ICIuL3BhcmFtZXRlcnMvZGF0YV90cmltbWVkX2ZpbGVwYXRocy50eHQiKQphcmdzX2FsaWduX2p1bmN0c19yZWcgPC0gc3lzdGVtQXJncyhzeXNtYSA9ICIuL3BhcmFtZXRlcnMvYm93dGllMl9hbGlnbl9qdW5jdGlvbnNfcmVnLnBhcmFtIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbXl0YXJnZXRzID0gIi4vcGFyYW1ldGVycy9kYXRhX3RyaW1tZWRfZmlsZXBhdGhzLnR4dCIpCmFyZ3NfYWxpZ25fanVuY3RzX3NjcmFtIDwtIHN5c3RlbUFyZ3Moc3lzbWEgPSAiLi9wYXJhbWV0ZXJzL2Jvd3RpZTJfYWxpZ25fanVuY3Rpb25zX3NjcmFtYmxlZC5wYXJhbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbXl0YXJnZXRzID0gIi4vcGFyYW1ldGVycy9kYXRhX3RyaW1tZWRfZmlsZXBhdGhzLnR4dCIpCmBgYAoKdGhlbiB3ZSBydW4gdGhlIEJvd3RpZTIgYWxpZ25tZW50IGNvbW1hbmRzIHRocm91Z2ggKnN5c3RlbVBpcGVSKgpgYGB7ciAsIGV2YWw9RkFMU0V9IAojIGFsaWduIHRvIGdlbm9tZQpnZW5vbWVfYmFtX2ZpbGVzIDwtIHJ1bkNvbW1hbmRsaW5lKGFyZ3MgPSBhcmdzX2FsaWduX2dlbm9tZSkKIyBhbGlnbiB0byByaWJvc29tZQpyaWJvX2JhbV9maWxlcyA8LSBydW5Db21tYW5kbGluZShhcmdzID0gYXJnc19hbGlnbl9yaWJvKQojIGFsaWduIHRvIGxpbmVhciBqdW5jdGlvbnMKanVuY3RzX3JlZ19iYW1fZmlsZXMgPC0gcnVuQ29tbWFuZGxpbmUoYXJncyA9IGFyZ3NfYWxpZ25fanVuY3RzX3JlZykKIyBhbGlnbiB0byBzcmFtYmxlZCBqdW5jdGlvbnMKanVuY3RzX3NjcmFtX2ZpbGVzIDwtIHJ1bkNvbW1hbmRsaW5lKGFyZ3MgPSBhcmdzX2FsaWduX2p1bmN0c19zY3JhbSkKYGBgCgpXZSBjYW4gb3BlbiAic3VibWl0YXJnczAxX2xvZyIgZm9yIGVhY2ggYWxpZ25tZW50IHRvIGRpc3BsYXkgdGhlIGFsaWdubWVudCBzdW1tYXJ5IGFuZCB0aGUgc3lzdGVtIGNvbW1hbmQgdXNlZC4gRm9yIHRoZSBnZW5vbWUgYWxpZ25tZW50LCB3ZSBjYW4gb3BlbiB0aGUgZmlsZSBpbnNpZGUgUiBsaWtlIHRoaXMKYGBge3IsIHNpemU9J2Zvb3Rub3Rlc2l6ZSd9CnJlYWRMaW5lcyhmaWxlLnBhdGgoIi4vcmVzdWx0cy9hbGlnbmVkL2dlbm9tZSIsInN1Ym1pdGFyZ3MwMV9sb2ciKSkKYGBgCiMjIFByZXByb2Nlc3Npbmcgc3RlcHMKClRoZSBwcmV2aW91cyBhbGlnbm1lbnQgc3RlcCBwcm9kdWNlZCA4IEJBTSBmaWxlcy4gVHdvIHBhaXJlZC1lbmQgQkFNIGZpbGVzIGZvciBlYWNoIG9mIHRoZSBnZW5vbWUsIHJpYm9zb21lLCBsaW5lYXIganVuY3Rpb25zLCBhbmQgc2NyYW1ibGVkIGp1bmN0aW9ucyBhbGlnbm1lbnRzLiBXZSBhcmUgaW50ZXJlc3RlZCBpbiBqdW5jdGlvbmFsIHJlYWRzLCB0aG9zZSByZWFkcyBmcm9tIHRoZSA4IGZpbGVzIHRoYXQgYWxpZ24gdG8gYSBzY3JhbWJsZWQgb3IgbGluZWFyIGp1bmN0aW9ucy4gU2luY2UgdGhlIG1ham9yaXR5IG9mIHRoZSByZWFkcyBpbiB0aGUgZ2Vub21lIGFuZCByaWJvc29tZSBCQU0gZmlsZXMgYXJlIG5vdCBqdW5jdGlvbmFsLCB3ZSBuZWVkIHRvIGZpbHRlciB0aGVtIG91dC4KCiMjIyAgMS4gR2V0IHJlYWQgSURzIGZvciBnZW5vbWljIG1hdGUgcGFpcnMKClNpbmNlIHRoZSBuZXh0IHByZXByb2Nlc3Npbmcgc3RlcHMgYXJlIG1lbW9yeSBpbnRlbnNpdmUsIHdlIGNhbiBpbnRpYWxseSBmaWx0ZXIgb3V0IHJlYWRzIGluIHdoaWNoIGJvdGggcGFpciBtYXRlcyBhbGlnbiB0byB0aGUgZ2Vub21lIG9yIHJpYm9zb21lLiBXZSBjYW4gYWxzbyBmaWx0ZXIgb3V0IHVuYWxpZ25lZCByZWFkcyBhbmQgcmV0dXJuIGxpc3RzIG9mIHJlYWQgSURzIGluIHdoaWNoIGJvdGggcGFpcnMgYWxpZ24gdG8gdGhlIGluZGljaWVzLCBvciBpbiB3aGljaCBvbmUgcGFpciBtYXRlIGFsaWducyBidXQgdGhlIG90aGVyIGRvZXNuJ3QuIFRvIHByb2NlZWQgd2UgbmVlZCB0byBnZXQgdGhlIG5hbWVzIG9mIHRob3NlIHJlYWRzIHRoYXQgYXJlIGZvdW5kIGluIGJvdGggUjEgYW5kIFIyICBnZW5vbWljIGFsaWdubWVudCBmaWxlcy4gCgpgYGB7ciB9IAoKZ2V0UmVhZElkcyA8LSBmdW5jdGlvbihiYW1maWxlKSB7CiAgc2NhbkJhbShiYW1maWxlLAogICAgICAgICAgcGFyYW0gPSBTY2FuQmFtUGFyYW0oZmxhZyA9IHNjYW5CYW1GbGFnKGlzVW5tYXBwZWRRdWVyeSA9IEZBTFNFKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHdoYXQgPSAicW5hbWUiKSlbWzFdXVtbMV1dCiAgfQoKZ2V0Q29tbW9uR2Vub21pY1BhaXJlZFJlYWRzIDwtIGZ1bmN0aW9uKGdlbm9tZV9iYW1maWxlcywgcmlib19iYW1maWxlcykgewogIAogIGdlbm9tZV9yZWFkcyA8LSBicGxhcHBseShnZW5vbWVfYmFtZmlsZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGdldFJlYWRJZHMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIEJQUEFSQU0gPSBNdWx0aWNvcmVQYXJhbSh3b3JrZXJzID0gMikpCiAgcmlib3NvbWFsX3JlYWRzIDwtIGJwbGFwcGx5KHJpYm9fYmFtZmlsZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdldFJlYWRJZHMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEJQUEFSQU0gPSBNdWx0aWNvcmVQYXJhbSh3b3JrZXJzID0gMikpCiAgCiAgZ2Vub21pY19yZWFkc18xIDwtIHVuaW9uKGdlbm9tZV9yZWFkc1tbMV1dLCByaWJvc29tYWxfcmVhZHNbWzFdXSkKICBnZW5vbWljX3JlYWRzXzIgPC0gdW5pb24oZ2Vub21lX3JlYWRzW1syXV0sIHJpYm9zb21hbF9yZWFkc1tbMl1dKQogIGNvbW1vbl9yZWFkcyA8LSBpbnRlcnNlY3QoZ2Vub21pY19yZWFkc18xLCBnZW5vbWljX3JlYWRzXzIpCiAgcjFfbm90X3IyIDwtIHNldGRpZmYoZ2Vub21pY19yZWFkc18xLCBnZW5vbWljX3JlYWRzXzIpCiAgcjJfbm90X3IxIDwtIHNldGRpZmYoZ2Vub21pY19yZWFkc18yLCBnZW5vbWljX3JlYWRzXzEpCiAgCiAgcmV0dXJuKAogICAgbGlzdCgKICAgICAgY29tbW9uX3JlYWRzID0gY29tbW9uX3JlYWRzLAogICAgICByMV9ub3RfcjIgPSByMV9ub3RfcjIsCiAgICAgIHIxX25vdF9yMiA9IHIyX25vdF9yMQogICAgKQogICkKfQpgYGAgIAoKTm90ZSB0aGF0IGNvZGUgYWJvdmUgdXNlcyB0aGUgcGFyYWxsZWwgbGFwcGx5IGZ1bmN0aW9uICpicGxhcHBseSouIElmIHlvdSBzeXN0ZW0gaGFzIGxlc3MgdGhhbiAzMkcgUkFNLCB5b3UgY2FuIG1vZGlmeSB0aGUgY29kZSB0byBydW4gd2l0aCBvbmUgd29ya2VyIG9yIHVzZSAqbGFwcGx5KiBpbnN0ZWFkLiBIZXJlIHdlIHJldHJpZXZlIHRoZSBsb2NhdGlvbiBvZiB0aGUgYWxpZ25lZCBmaWxlcyBhbmQgZmVlZCB0aGVtIGludG8gdGhlIGZ1bmN0aW9uIGRlZmluZWQgYWJvdmUuCmBgYHtyIH0gCmdlbm9tZV9hbGlnbmVkIDwtIG91dHBhdGhzKGFyZ3NfYWxpZ25fZ2Vub21lKQpyaWJvX2FsaWduZWQgPC0gb3V0cGF0aHMoYXJnc19hbGlnbl9yaWJvKQoKcmVhZHNfdG9faWdub3JlIDwtIGdldENvbW1vbkdlbm9taWNQYWlyZWRSZWFkcyhnZW5vbWVfYWxpZ25lZCwgcmlib19hbGlnbmVkKQpgYGAKCiMjIyAgMi4gUmVhZCBhbGlnbm1lbnQgZGF0YSBhbmQgcnVuIGluaXRpYWwgZmlsdGVyaW5nCgpOb3cgd2UgdXNlIHRoZXNlIHJlYWQgSURzIHRvIGNyZWF0ZSB0d28gZmlsdGVycy4gVGhlIGZpcnN0IG9uZSBmaWx0ZXJzIG91dCBnZW5vbWljIHJlYWRzIGluIHdoaWNoIGJvdGggbWF0ZXMgKFIxIGFuZCBSMikgYWxpZ24uIFRoZSBzZWNvbmQgb25lIGlzIGEganVuY3Rpb25hbCBmaWx0ZXIgdGhhdCBrZWVwcyBvbmx5IGp1bmN0aW9uYWwgcmVhZHMgdGhhdCBvdmVybGFwIGEgcGFydGljdWxhciBqdW5jdGlvbiBieSBhdCBsZWFzdCA4IG51Y2xlb3RpZGVzIGFuZCB0aGF0IGFsc28gc2F0aWZ5IHRoZSBmaXJzdCBmaWx0ZXIuIAogCmBgYHtyfQpnZW5vbWljX3JlYWRzX2ZpbHRlciA8LQogIEZpbHRlclJ1bGVzKGxpc3QoCiAgICBpbmNsdWRlID0gZnVuY3Rpb24oeCkgIShtY29scyh4KSRxbmFtZSAlaW4lIHJlYWRzX3RvX2lnbm9yZSRjb21tb25fcmVhZHMpCiAgKSkKCk1JRFBPSU5UIDwtIDE1MApKVU5DX09WRVJMQVAgPC0gOAoKanVuY3Rpb25fcmVhZHNfZmlsdGVyIDwtCiAgRmlsdGVyUnVsZXMobGlzdCgKICAgIGluY2x1ZGUgPSBmdW5jdGlvbih4KQogICAgICAoCiAgICAgICAgKE1JRFBPSU5UICsgSlVOQ19PVkVSTEFQICsgMSAtIHdpZHRoKHgpIDw9IHN0YXJ0KHgpKSAmCiAgICAgICAgKHN0YXJ0KHgpIDw9IE1JRFBPSU5UIC0gSlVOQ19PVkVSTEFQICsgMSkgJgogICAgICAgICEobWNvbHMoeCkkcW5hbWUgJWluJSByZWFkc190b19pZ25vcmUkY29tbW9uX3JlYWRzKQogICAgICApCiAgKSkKYGBgCgpBcyBwYXJ0IG9mIHRoZSBmaWx0ZXJpbmcgc3RlcCwgd2UgYWxzbyB1cGRhdGUgdGhlIGFsaWdubWVudCBzY29yZSBmb3IgdGhlIGp1bmN0aW9uIHJlYWRzIHN1Y2ggdGhhdCB0aGUgTi1wZW5hbHR5IGlzIGNvcnJlY3RlZC4KYGBge3J9Cgpjb3VudEFtYmlnb3VzQmFzZXMgPC0gZnVuY3Rpb24oYWxpZ25fb2JqKSB7CiAgc2FwcGx5KGdyZWdleHByKCJOMHwwTiIsIG1jb2xzKGFsaWduX29iaikkTUQpLCAKICAgICAgICAgZnVuY3Rpb24oeCkgCiAgICAgICAgICAgaWYgKGF0dHJpYnV0ZXMoeCkkbWF0Y2gubGVuZ3RoWzFdID09IC0xKSAKICAgICAgICAgICAgIHJldHVybigwKSAKICAgICAgICAgZWxzZSAKICAgICAgICAgICByZXR1cm4obGVuZ3RoKHgpKQogICkKfQoKI01EIGFuZCBYTiBkbyBhZ3JlZSAKZmlsdGVyQWxpZ25tZW50cyA8LQogIGZ1bmN0aW9uKGJhbWZpbGUsIHBhcmFtLCBmaWx0ZXIsIHVwZGF0ZVNjb3JlID0gVFJVRSkgewogICAgeCA8LSByZWFkR0FsaWdubWVudHMoZmlsZSA9IGJhbWZpbGUsIHBhcmFtID0gcGFyYW0pCiAgICB4IDwtIHN1YnNldEJ5RmlsdGVyKHgsIGZpbHRlcikKICAgIHNlcWxldmVscyh4KSA8LSBzZXFsZXZlbHNJblVzZSh4KQogICAgIyBjb3JyZWN0IGZvciBOLXBlbmFsdHkgaW4ganVuY3Rpb24gYWxpZ25tZW50cyBhbmQgdXBkYXRlIGFsaWdubWVudCBzY29yZQogICAgaWYgKHVwZGF0ZVNjb3JlKSB7CiAgICAgIG1jb2xzKHgpJEFTIDwtIG1jb2xzKHgpJEFTICsgY291bnRBbWJpZ291c0Jhc2VzKHgpCiAgICB9CiAgICByZXR1cm4oeCkKICB9CgojIGdldCBmaWxlIHBhdGhzCmxpbmVhcl9qdW5jdGlvbnNfYWxpZ25lZCA8LSBvdXRwYXRocyhhcmdzX2FsaWduX2p1bmN0c19yZWcpCnNjcmFtYmxlZF9qdW5jdGlvbnNfYWxpZ25lZCA8LSBvdXRwYXRocyhhcmdzX2FsaWduX2p1bmN0c19zY3JhbSkKCiMgcnVuIGZpbHRlcnMKCnBhcmFtIDwtCiAgU2NhbkJhbVBhcmFtKAogICAgd2hhdCA9IGMoInFuYW1lIiwibWFwcSIpLCAjIGdldCB0aGUgcmVhZCBuYW1lIGFuZCB0aGUgbWFwcGluZyBxdWFsaXR5IAogICAgZmxhZyA9IHNjYW5CYW1GbGFnKGlzVW5tYXBwZWRRdWVyeSA9IEZBTFNFKSwKICAgIHRhZyA9IGMoIkFTIiwgIlhTIiwgIlhOIiwgIk1EIikgIyBnZXQgdGFncwogICkKCmdlbm9taWNfZmlsdGVyZWQgPC0gYnBsYXBwbHkoCiAgZ2Vub21lX2FsaWduZWQsCiAgZmlsdGVyQWxpZ25tZW50cywKICBwYXJhbSA9IHBhcmFtLAogIGZpbHRlciA9IGdlbm9taWNfcmVhZHNfZmlsdGVyLAogIHVwZGF0ZVNjb3JlID0gRkFMU0UsCiAgQlBQQVJBTSA9IE11bHRpY29yZVBhcmFtKHdvcmtlcnMgPSAyKQopCgpsaW5lYXJfanVuY3Rpb25zX2ZpbHRlcmVkIDwtIGJwbGFwcGx5KAogIGxpbmVhcl9qdW5jdGlvbnNfYWxpZ25lZCwKICBmaWx0ZXJBbGlnbm1lbnRzLAogIHBhcmFtID0gcGFyYW0sCiAgZmlsdGVyID0ganVuY3Rpb25fcmVhZHNfZmlsdGVyLAogIEJQUEFSQU0gPSBNdWx0aWNvcmVQYXJhbSh3b3JrZXJzID0gMikKKQoKc2NyYW1ibGVkX2p1bmN0aW9uc19maWx0ZXJlZCA8LSBicGxhcHBseSgKICBzY3JhbWJsZWRfanVuY3Rpb25zX2FsaWduZWQsCiAgZmlsdGVyQWxpZ25tZW50cywKICBwYXJhbSA9IHBhcmFtLAogIGZpbHRlciA9IGp1bmN0aW9uX3JlYWRzX2ZpbHRlciwKICBCUFBBUkFNID0gTXVsdGljb3JlUGFyYW0od29ya2VycyA9IDIpCikKYGBgCgpMZXQncyB0YWtlIGEgbG9vayBhdCBmaXJzdCBmZXcgcm93cyBvZiB0aGUgR0FsaWdubWVudCBvYmplY3QgZm9yIHRoZSBsaW5lYXIganVuY3Rpb25zIFIxIGFsaWduZWQgcmVhZHMKYGBge3J9CmxpbmVhcl9qdW5jdGlvbnNfZmlsdGVyZWRbWzFdXVsxOjMsXSAlPiUgYXMuZGF0YS5mcmFtZSgpCmBgYApXZSBjYW4gYWxzbyBleGFtaW5lIHRoZSBmcmVxdWVuY3kgb2YgdGhlIGNpZ2FyIHN0cmluZ3MgCmBgYHtyfSAKbGluZWFyX2p1bmN0aW9uc19maWx0ZXJlZFtbMV1dICU+JSAKICBjaWdhcigpICU+JSAKICB0YWJsZSAlPiUgCiAgc29ydChkZWNyZWFzaW5nID0gVFJVRSkgJT4lIAogIGhlYWQoKSAKYGBgCgojIyMgIDMuIENvbnZlcnQgR0FsaWdubWVudCBvYmplY3RzIGludG8gZGF0YWZyYW1lcwoKCmBgYHtyfQp0b0RhdGFGcmFtZSA8LSBmdW5jdGlvbih4KSB7CiAgeCA8LSB4ICU+JSAgCiAgICBhcy5kYXRhLmZyYW1lKCkgJT4lIAogICAgc3Vic2V0KHNlbGVjdCA9IC1jKGVuZCwgY2lnYXIsIHF3aWR0aCwgbmp1bmMsIFhTLCBNRCkpCiAgY29sbmFtZXMoeClbMTo4XSA8LSAJYygianVuY3Rpb24iLCAic3RyIiwgInBvcyIsICJsZW4iLAogICAgICAgICAgICAgICAgICAgICAgICAgImlkIiwgIk1RIiwgIkFTIiwgIlhOIikJCQogIHgkanVuY3Rpb24gPC0gYXMuY2hhcmFjdGVyKHgkanVuY3Rpb24pCiAgeCRzdHIgPC0gYXMuY2hhcmFjdGVyKHgkc3RyKQogIHJldHVybih4KQp9CgphcHBlbmRKdW5jdGlvblRhZ3MgPC0gZnVuY3Rpb24oeCkgewogIGRmIDwtICB4ICU+JSAKICAgIHNlcW5hbWVzKCkgJT4lIAogICAgYXMuY2hhcmFjdGVyKCkgJT4lIAogICAgbGFwcGx5KC4sIGZ1bmN0aW9uKHkpIHVubGlzdChzdHJzcGxpdCh5LCAiWzpdfFt8XSIpKSkgJT4lCiAgICBkby5jYWxsKHJiaW5kLCAuKSAlPiUKICAgIGRhdGEuZnJhbWUoc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQogIGNvbG5hbWVzKGRmKSA8LSBjKCJqQ2hyIiwgImpHZW5lMSIsICJqUG9zMSIsICJqR2VuZTIiLCAKICAgICAgICAgICAgICAgICAgICAialBvczIiLCAialR5cGUiLCAialN0ciIpCiAgZGYkalBvczEgPC0gYXMuaW50ZWdlcihkZiRqUG9zMSkKICBkZiRqUG9zMiA8LSBhcy5pbnRlZ2VyKGRmJGpQb3MyKQogIG1jb2xzKHgpIDwtIGNiaW5kKG1jb2xzKHgpLCBkZikKICByZXR1cm4odG9EYXRhRnJhbWUoeCkpCn0KCmdlbm9taWNfZmlsdGVyZWRfZHQgPC0gYnBsYXBwbHkoCiAgZ2Vub21pY19maWx0ZXJlZCwKICB0b0RhdGFGcmFtZSwKICBCUFBBUkFNID0gTXVsdGljb3JlUGFyYW0od29ya2VycyA9IDIpCikKCmxpbmVhcl9qdW5jdGlvbnNfZmlsdGVyZWRfZHQgPC0gYnBsYXBwbHkoCiAgbGluZWFyX2p1bmN0aW9uc19maWx0ZXJlZCwKICBhcHBlbmRKdW5jdGlvblRhZ3MsCiAgQlBQQVJBTSA9IE11bHRpY29yZVBhcmFtKHdvcmtlcnMgPSAyKQopCgpzY3JhbWJsZWRfanVuY3Rpb25zX2ZpbHRlcmVkX2R0IDwtIGJwbGFwcGx5KAogIHNjcmFtYmxlZF9qdW5jdGlvbnNfZmlsdGVyZWQsCiAgYXBwZW5kSnVuY3Rpb25UYWdzLAogIEJQUEFSQU0gPSBNdWx0aWNvcmVQYXJhbSh3b3JrZXJzID0gMikKKQoKYGBgCgpMZXQncyB0YWtlIGEgbG9vayBhdCB0aGUgZGF0YWZyYW1lIGZvciB0aGUgbGluZWFyIGp1bmN0aW9ucyBSMSBhbGlnbmVkIHJlYWRzCmBgYHtyfQpsaW5lYXJfanVuY3Rpb25zX2ZpbHRlcmVkX2R0W1sxXV1bMTozLF0KYGBgCgoKIyMjICA0LiAgRmlsdGVyIFIxIGp1bmN0aW9uYWwgcmVhZHMgYW5kIHBhaXIgdGhlbSB3aXRoIFIyIG1hdGVzCgpBIHJlYWQgKFIxKSBpcyBhIGxpbmVhciBqdW5jdGlvbiByZWFkIGlmIGl0IGFsaWducyB0byBhIGxpbmVhciBqdW5jdGlvbiBhbmQgZG9lcyBub3QgYWxpZ24gdG8gdGhlIGdlbm9tZSBvciByaWJvc29tZS4gSW4gdGhlIGNvZGUgdGhlc2UgcmVhZHMgYXJlIHN0b3JlZCBpbiAqbGluZWFyXzEqLiBBIHJlYWQgUjEgaXMgYSBzY3JhbWJsZWQganVuY3Rpb24gcmVhZCBpZiBpdCBhbGlnbnMgdG8gYSBzY3JhbWJsZWQganVuY3Rpb24gYW5kIGRvZXMgbm90IGFsaWduIHRvIHRoZSBnZW5vbWUgb3Igcmlib3NvbWUgb3IgdG8gdGhlIGxpbmVhciBqdW5jdGlvbi4gU2NyYW1ibGVkIGp1bmN0aW9uIHJlYWRzIGFyZSBzdG9yZWQgaW4gKnNjcmFtYmxlZF8xKi4gVGhlIHJlYWQgbWF0ZSBSMiBjYW4gYmUgYSBnZW5vbWljLCBsaW5lYXIganVuY3Rpb24sIG9yIGEgc2NyYW1ibGVkIGp1bmN0aW9uIHJlYWQgYXMgZGV0ZXJtaW5lZCBieSB3aGVodGVyIHRoZSByZWFkIG5hbWUgY2FuIGJlIGZvdW5kIGluIHRoZSBSMiBhbGlnbmVkIHJlYWRzLiBBbiBSMSByZWFkIGNhbiBoYXZlIGEgc2luZ2xlIG9yIG11bHRpcGxlIFIyIG1hdGVzLiBTb21lIGhhdmUgbm9uZSBhdCBhbGwuIFRoZXNlIGFyZSB0aGUgdW5tYXBwZWQgcmVhZHMuCmBgYHtyfQpjcmVhdGVKdW5jdGlvbnNSZWFkc0RGIDwtIGZ1bmN0aW9uKGdlbm9taWMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGluZWFyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNjcmFtYmxlZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByMV9ub3RfcjIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VmZml4ZXMgPSBjKCIuUjEiLCAiLlIyZyIsICIuUjJsIiwgIi5SMnMiKSkgewogCiAgbGluZWFyXzEgPC0gbGluZWFyW1sxXV1bIShsaW5lYXJbWzFdXSRpZCAlaW4lIHIxX25vdF9yMiksIF0KICBzY3JhbWJsZWRfMSA8LSBzY3JhbWJsZWRbWzFdXVshKHNjcmFtYmxlZFtbMV1dJGlkICVpbiUgYyhyMV9ub3RfcjIsIGxpbmVhcl8xJGlkKSksIF0KICAKICBnZW5vbWljX21hdGUgPC0gZ2Vub21pY1tbMl1dWyhnZW5vbWljW1syXV0kaWQgJWluJSBjKGxpbmVhcl8xJGlkLHNjcmFtYmxlZF8xJGlkKSksIF0KICBsaW5lYXJfbWF0ZSA8LSBsaW5lYXJbWzJdXVsobGluZWFyW1syXV0kaWQgJWluJSBjKGxpbmVhcl8xJGlkLHNjcmFtYmxlZF8xJGlkKSksIF0KICBzY3JhbWJsZWRfbWF0ZSA8LSBzY3JhbWJsZWRbWzJdXVsoc2NyYW1ibGVkW1syXV0kaWQgJWluJSBjKGxpbmVhcl8xJGlkLCBzY3JhbWJsZWRfMSRpZCkpLCBdCiAgCiAgbGluZWFyX2dlbm9taWMgPC0gbGVmdF9qb2luKGxpbmVhcl8xLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZW5vbWljX21hdGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gImlkIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1ZmZpeCA9IHN1ZmZpeGVzW2MoMSwgMildKQogIAogIGxpbmVhcl9saW5lYXIgPC0gbGVmdF9qb2luKGxpbmVhcl8xLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpbmVhcl9tYXRlICwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9ICJpZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VmZml4ID0gc3VmZml4ZXNbYygxLCAzKV0pCiAgCiAgbGluZWFyX3NjcmFtYmxlZCA8LSBsZWZ0X2pvaW4obGluZWFyXzEsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNjcmFtYmxlZF9tYXRlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gImlkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdWZmaXggPSBzdWZmaXhlc1tjKDEsIDQpXSkKICAKICBzY3JhbWJsZWRfZ2Vub21pYyA8LSBsZWZ0X2pvaW4oc2NyYW1ibGVkXzEsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZW5vbWljX21hdGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gImlkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VmZml4ID0gc3VmZml4ZXNbYygxLCAyKV0pCiAgCiAgc2NyYW1ibGVkX2xpbmVhciA8LSBsZWZ0X2pvaW4oc2NyYW1ibGVkXzEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGluZWFyX21hdGUgLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gImlkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdWZmaXggPSBzdWZmaXhlc1tjKDEsIDMpXSkKICAKICBzY3JhbWJsZWRfc2NyYW1ibGVkIDwtIGxlZnRfam9pbihzY3JhbWJsZWRfMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzY3JhbWJsZWRfbWF0ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9ICJpZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VmZml4ID0gc3VmZml4ZXNbYygxLCA0KV0pCiAgCiAgbGluZWFyX2RmIDwtIGNiaW5kKGxpbmVhcl9saW5lYXJbLCAxOjE1XSwgCiAgICAgICAgICAgICAgICAgICAgIGxpbmVhcl9nZW5vbWljWywgMTY6MjJdLAogICAgICAgICAgICAgICAgICAgICBsaW5lYXJfbGluZWFyWywgMTY6MjldLCAKICAgICAgICAgICAgICAgICAgICAgbGluZWFyX3NjcmFtYmxlZFssIDE2OjI5XSkKICAKICBzY3JhbWJsZWRfZGYgPC0gY2JpbmQoc2NyYW1ibGVkX2xpbmVhclssIDE6MTVdLAogICAgICAgICAgICAgICAgICAgICAgICBzY3JhbWJsZWRfZ2Vub21pY1ssIDE2OjIyXSwKICAgICAgICAgICAgICAgICAgICAgICAgc2NyYW1ibGVkX2xpbmVhclssIDE2OjI5XSwKICAgICAgICAgICAgICAgICAgICAgICAgc2NyYW1ibGVkX3NjcmFtYmxlZFssIDE2OjI5XSkKICAKICByZXR1cm4obGlzdChsaW5lYXJfZGYgPSBsaW5lYXJfZGYsIHNjcmFtYmxlZF9kZiA9IHNjcmFtYmxlZF9kZikpCn0KClIxUjJNYXRlc0p1bmN0aW9uUmVhZHMgPC0gY3JlYXRlSnVuY3Rpb25zUmVhZHNERihnZW5vbWljX2ZpbHRlcmVkX2R0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGluZWFyX2p1bmN0aW9uc19maWx0ZXJlZF9kdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNjcmFtYmxlZF9qdW5jdGlvbnNfZmlsdGVyZWRfZHQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWFkc190b19pZ25vcmUkcjFfbm90X3IyKQpgYGAKCiMjIyAgNS4gIENsYXNzaWZ5IE1hdGUgUmVhZHMKCkhlcmUgd2UgY2xhc3NpZnkgbWF0ZSByZWFkcyBiYXNlZCBvbiBhbGlnbm1lbnQgc2NvcmUuIFdlIGFsc28gY3JlYXRlIGEgbmV3IHZhcmlhYmxlICphc2NvcmUqIHRoYXQgYXZlcmFnZXMgdGhlIGFsaWdubWVudCBzY29yZXMgb2YgdGhlIHR3byBtYXRjaGVkIHJlYWRzLgpgYGB7cn0KY2xhc3NzaWZ5TWF0ZVJlYWRzIDwtIGZ1bmN0aW9uKGRmLCBzdWZmaXhlcyA9IGMoIi5SMSIsICIuUjJnIiwgIi5SMmwiLCAiLlIycyIpKSB7CiAgICAKICAgIHNjb3Jlc19kZiA8LSBkZlssIHBhc3RlMCgiQVMiLCBzdWZmaXhlcyldCiAgICBuTWF0ZXMgPC0gYXBwbHkoc2NvcmVzX2RmWywgMjo0XSAsIDEsIGZ1bmN0aW9uKHgpIHN1bSghaXMubmEoeCkpKQogICAgbWF4X3Njb3JlIDwtIGFwcGx5KHNjb3Jlc19kZlssIDI6NF0gLCAxLCB3aGljaC5tYXgpCiAgICAgIAogICAgbWF0ZSA8LSBzYXBwbHkobWF4X3Njb3JlLCBmdW5jdGlvbih4KSAKICAgICAgaWYgKGlzLm51bGwoYXR0cmlidXRlcyh4KSkpIAogICAgICAgIHtyZXR1cm4oMCl9IAogICAgICBlbHNlIAogICAgICAgIHtyZXR1cm4oYXMuaW50ZWdlcih4KSl9CiAgICAgICkKICAgIAogICAgbWF0ZXNjb3JlIDwtIHNjb3Jlc19kZltjYmluZCgxOm5yb3coc2NvcmVzX2RmKSwgKG1hdGUgKyAxKSldCiAgICBBU21lYW4gPC0gMC41ICogKHNjb3Jlc19kZiRBUy5SMSArIG1hdGVzY29yZSkKICAgIGRmIDwtIGNiaW5kKGRmLCAKICAgICAgICAgICAgICAgIEFTbWVhbiA9IEFTbWVhbiwKICAgICAgICAgICAgICAgIG5NYXRlcyA9IG5NYXRlcywKICAgICAgICAgICAgICAgIG1hdGUgPSBtYXRlKQogICAgcmV0dXJuKGRmKQogIH0KCmxpbmVhcl9kZiA8LSAKICBSMVIyTWF0ZXNKdW5jdGlvblJlYWRzJGxpbmVhcl9kZiAlPiUKICBjbGFzc3NpZnlNYXRlUmVhZHMoKQpzY3JhbWJsZWRfZGYgPC0gCiAgUjFSMk1hdGVzSnVuY3Rpb25SZWFkcyRzY3JhbWJsZWRfZGYgJT4lCiAgY2xhc3NzaWZ5TWF0ZVJlYWRzKCkKCmNhdCggIiBudW1iZXIgb2YgbGluZWFyIGp1bmN0aW9uIHJlYWRzIiwKICAgICBOUk9XKGxpbmVhcl9kZiksICJcbiIsCiAgICAgIm51bWJlciBvZiBzY3JhbWJsZWQganVuY3Rpb24gcmVhZHMiLAogICAgIE5ST1coc2NyYW1ibGVkX2RmKSwgIlxuIiwKICAidG90YWwgbnVtYmVyIG9mIGp1bmN0aW9uYWwgcmVhZHM6IiwgCiAgICBOUk9XKGxpbmVhcl9kZikgKyBOUk9XKHNjcmFtYmxlZF9kZiksICJcbiIsCiAgICAibnVtYmVyIG9mIGp1bmN0aW9ucyB3aXRoIGFsaWduZWQgcmVhZHM6IiwKICAgIGxlbmd0aCh1bmlxdWUobGluZWFyX2RmJGp1bmN0aW9uLlIxKSkgKwogICAgICBsZW5ndGgodW5pcXVlKHNjcmFtYmxlZF9kZiRqdW5jdGlvbi5SMSkpKQpgYGAgICAgCiAgICAgIApMZXQncyB0YWtlIGEgbG9vayBhdCB0aGUgZGF0YWZyYW1lIGZvciB0aGUgbGluZWFyIGp1bmN0aW9ucyBSMSBhbGlnbmVkIHJlYWRzCmBgYHtyfQpzY3JhbWJsZWRfZGZbMTozLCBdICAKYGBgCgpMZXQncyBhbHNvIG91dHB1dCB0aGUgZnJlcXVlbmN5IG9mIFIxIHJlYWRzIHRoYXQgaGF2ZSAwLCAxLCAyLCBvciAzIHBvdGVudGlhbCBwYWlyZWQtZW5kIG1hdGUgcmVhZHMgKFIyKQpgYGB7cn0KbGluZWFyX2p1bmN0aW9uX21hcGNhdGVnIDwtIAogIGxpbmVhcl9kZiRuTWF0ZXMgJT4lCiAgdGFibGUgICU+JQogIGFzLmRhdGEuZnJhbWUoKQoKc2NyYW1ibGVkX2p1bmN0aW9uX21hcGNhdGVnIDwtIAogIHNjcmFtYmxlZF9kZiRuTWF0ZXMgJT4lICAKICB0YWJsZSAlPiUgIAogIGFzLmRhdGEuZnJhbWUoKSAKCm5hbWVzX2RmIDwtIGMoIlVubWFwcGVkIiwiT25lTWF0ZU1hdGNoIiwgIlR3b01hdGVNYXRjaHMiLCAiVGhyZWVNYXRlTWF0Y2hzIikKcm93bmFtZXMobGluZWFyX2p1bmN0aW9uX21hcGNhdGVnKSA8LSBuYW1lc19kZgpyb3duYW1lcyhzY3JhbWJsZWRfanVuY3Rpb25fbWFwY2F0ZWcpIDwtIG5hbWVzX2RmCmxpbmVhcl9qdW5jdGlvbl9tYXBjYXRlZyAKc2NyYW1ibGVkX2p1bmN0aW9uX21hcGNhdGVnIApgYGAKCiMjIyAgNi4gIENsYXNzaWZ5IEp1bmN0aW9uIFJlYWRzCgpXZSBzcGxpdCB0aGUgcmVhZHMgaW50byByZWFkcyB0aGF0IGhhdmUgYSBtYXBwZWQgbWF0ZSBSMiBhbmQgdGhvc2UgdGhhdCBkbyBub3QuIFdlIGludGlhbGx5IGFzc2lnbiBhbGwgcmVhZHMgYXMgdW5jYXRlZ29yaXplZCAoaS5lLiBjbGFzcz0wKS4KYGBge3J9ICAgICAgICAgICAgICAgICAgICAKbGluZWFyX3JlYWRzX3NwbGl0IDwtIHNwbGl0KGxpbmVhcl9kZiwgbGluZWFyX2RmJG5NYXRlcyA9PSAwKQpzY3JhbWJsZWRfcmVhZHNfc3BsaXQgPC0gc3BsaXQoc2NyYW1ibGVkX2RmLCBzY3JhbWJsZWRfZGYkbk1hdGVzID09IDApCnVubWFwcGVkX3JlYWRzIDwtIHJiaW5kKGxpbmVhcl9yZWFkc19zcGxpdFtbMl1dLCBzY3JhbWJsZWRfcmVhZHNfc3BsaXRbWzJdXSlbLDE6MTVdCgpsaW5lYXJfcmVhZHNfbWFwcGVkIDwtIGNiaW5kKGxpbmVhcl9yZWFkc19zcGxpdFtbMV1dICwgY2xhc3MgPSAwKQpzY3JhbWJsZWRfcmVhZHNfbWFwcGVkIDwtIGNiaW5kKHNjcmFtYmxlZF9yZWFkc19zcGxpdFtbMV1dICwgY2xhc3MgPSAwKQpgYGAKCkxldCdzIHRha2UgYSBsb29rIGF0IHRoZSB1bm1hcHBlZCByZWFkcwpgYGB7cn0KdW5tYXBwZWRfcmVhZHNbMTozLF0KYGBgCgpOb3cgd2UgcHJvY2VlZCB0byBjbGFzc2lmeSBzY3JhbWJsZWQganVuY3Rpb25hbCByZWFkcyBhcyBjaXJjdWxhciBvciBkZWNveSBhbmQgbGluZWFyIGp1bmN0aW9uYWwgcmVhZHMgYXMgbGluZWFyIG9yIGFub21hbG91cy4KYGBge3J9CgpjbGFzc2lmeUNpcmN1bGFyUGFpcmVkTWF0ZXMgPC0gZnVuY3Rpb24oeCkgewogIAogIGNvbmQxIDwtIHgkc3RyLlIxICE9IHhbLCBjKCJzdHIuUjJnIiwgInN0ci5SMmwiLCAic3RyLlIycyIpXVtjYmluZCgxOm5yb3coeCksIHgkbWF0ZSldCiAgY29uZDIgPC0geCRqQ2hyLlIxID09IHhbLCBjKCJqdW5jdGlvbi5SMmciLCAiakNoci5SMmwiLCAiakNoci5SMnMiKV1bY2JpbmQoMTpucm93KHgpLCB4JG1hdGUpXQogIGNvbmQzIDwtIHgkbWF0ZSA9PSAzIAogIGNvbmQ0IDwtIHgkanVuY3Rpb24uUjEgPT0geCRqdW5jdGlvbi5SMnMKICB4JGNsYXNzWyhjb25kMSAmIGNvbmQyICYgY29uZDMgJiBjb25kNCldIDwtIDEgIyhjaXJjKQogIAogIHJldHVybih4KQp9CgpjbGFzc2lmeUNpcmN1bGFyTGluZWFyTWF0ZXMgPC0gZnVuY3Rpb24oeCwgQlVGRkVSID0gMTUsIE1JRFBPSU5UID0gMTUwKSB7CiAgCiAgcjFfc3RhcnQgPC0gcG1pbih4JGpQb3MxLlIxLCB4JGpQb3MyLlIxKSAKICByMV9lbmQgPC0gcG1heCh4JGpQb3MxLlIxLCB4JGpQb3MyLlIxKSAKICByMl9zdGFydCA8LSBwbWluKHgkalBvczEuUjJsLCB4JGpQb3MyLlIybCkgIC0gTUlEUE9JTlQgKyAgeCRwb3MuUjJsCiAgcjJfZW5kIDwtIHBtaW4oeCRqUG9zMS5SMmwsIHgkalBvczIuUjJsKSAgLSBNSURQT0lOVCArICB4JHBvcy5SMmwgKyAgeCRsZW4uUjJsIC0gMQogIAogIGNvbmQxIDwtIHgkc3RyLlIxICE9IHhbLCBjKCJzdHIuUjJnIiwgInN0ci5SMmwiLCAic3RyLlIycyIpXVtjYmluZCgxOm5yb3coeCksIHgkbWF0ZSldCiAgY29uZDIgPC0geCRqQ2hyLlIxID09IHhbLCBjKCJqdW5jdGlvbi5SMmciLCAiakNoci5SMmwiLCAiakNoci5SMnMiKV1bY2JpbmQoMTpucm93KHgpLCB4JG1hdGUpXQogIGNvbmQzIDwtIHIyX3N0YXJ0ID49ICByMV9zdGFydCAtIEJVRkZFUgogIGNvbmQ0IDwtIHIyX3N0YXJ0IDw9ICByMV9lbmQgKyBCVUZGRVIKICBjb25kNSA8LSByMl9lbmQgPj0gcjFfc3RhcnQgLSBCVUZGRVIKICBjb25kNiA8LSByMl9lbmQgPD0gcjFfZW5kICsgQlVGRkVSIAogIGNvbmQ3IDwtIHgkbWF0ZSA9PSAyIAogIGNvbmQ4IDwtICEoeCRjbGFzcyAlaW4lIGMoMSkpCiAgCiAgeCRjbGFzc1soY29uZDEgJiBjb25kMiAmIGNvbmQzICYgY29uZDQgJiBjb25kNSAmIGNvbmQ2ICYgY29uZDcgJiBjb25kOCldIDwtIDIgIyhjaXJjX2xpbikKICByZXR1cm4oeCkKfQoKY2xhc3NpZnlDaXJjdWxhckdlbm9taWNNYXRlcyA8LSBmdW5jdGlvbih4LCBCVUZGRVIgPSAxNSwgTUlEUE9JTlQgPSAxNTApIHsKICAKICByMV9zdGFydCA8LSBwbWluKHgkcG9zLlIxLCB4JGpQb3MyLlIxKSAKICByMV9lbmQgPC0gcG1heCh4JGpQb3MxLlIxLCB4JGpQb3MyLlIxKSAKICByMl9zdGFydCA8LSB4JHBvcy5SMmcKICByMl9lbmQgPC0geCRwb3MuUjJnICsgeCRsZW4uUjJnIC0gMQogIAogIGNvbmQxIDwtIHgkc3RyLlIxICE9IHhbLCBjKCJzdHIuUjJnIiwgInN0ci5SMmwiLCAic3RyLlIycyIpXVtjYmluZCgxOm5yb3coeCksIHgkbWF0ZSldCiAgY29uZDIgPC0geCRqQ2hyLlIxID09IHhbLCBjKCJqdW5jdGlvbi5SMmciLCAiakNoci5SMmwiLCAiakNoci5SMnMiKV1bY2JpbmQoMTpucm93KHgpLCB4JG1hdGUpXQogIGNvbmQzIDwtIHIyX3N0YXJ0ID49ICByMV9zdGFydCAtIEJVRkZFUgogIGNvbmQ0IDwtIHIyX3N0YXJ0IDw9ICByMV9lbmQgKyBCVUZGRVIKICBjb25kNSA8LSByMl9lbmQgPj0gcjFfc3RhcnQgLSBCVUZGRVIKICBjb25kNiA8LSByMl9lbmQgPD0gcjFfZW5kICsgQlVGRkVSCiAgY29uZDcgPC0geCRtYXRlID09IDEgCiAgY29uZDggPC0gISh4JGNsYXNzICVpbiUgYygxLCAyKSkKICAKICB4JGNsYXNzWyhjb25kMSAmIGNvbmQyICYgY29uZDMgJiBjb25kNCAmIGNvbmQ1ICYgY29uZDYgJiBjb25kNyAmIGNvbmQ4ICldIDwtIDMgIyhjaXJjX2dlbikKICByZXR1cm4oeCkKfQoKY2xhc3NpZnlDaXJjdWxhcklnbm9yZSA8LSBmdW5jdGlvbih4LCBCVUZGRVIgPSAxNSwgTUlEUE9JTlQgPSAxNTApIHsKICAKICByMV9zdGFydCA8LSBwbWluKHgkalBvczEuUjEsIHgkalBvczIuUjEpIC0gTUlEUE9JTlQgKyB4JHBvcy5SMSAKICByMV9lbmQgPC0gcjFfc3RhcnQgKyB4JGxlbi5SMSAtIDEKICAKICBjb25kMSA8LSB4JHN0ci5SMSAhPSB4WywgYygic3RyLlIyZyIsICJzdHIuUjJsIiwgInN0ci5SMnMiKV1bY2JpbmQoMTpucm93KHgpLCB4JG1hdGUpXQogIGNvbmQyIDwtIHgkakNoci5SMSA9PSB4WywgYygianVuY3Rpb24uUjJnIiwgImpDaHIuUjJsIiwgImpDaHIuUjJzIildW2NiaW5kKDE6bnJvdyh4KSwgeCRtYXRlKV0KICBjb25kMyA8LSByMV9zdGFydCA+PSBwbWluKHgkalBvczEuUjJzLCB4JGpQb3MyLlIycykgLSBCVUZGRVIKICBjb25kNCA8LSByMV9zdGFydCA8PSBwbWF4KHgkalBvczEuUjJzLCB4JGpQb3MyLlIycykgKyBCVUZGRVIgCiAgY29uZDUgPC0gcjFfZW5kID49IHBtaW4oeCRqUG9zMS5SMnMsIHgkalBvczIuUjJzKSAtIEJVRkZFUgogIGNvbmQ2IDwtIHIxX2VuZCA8PSBwbWF4KHgkalBvczEuUjJzLCB4JGpQb3MyLlIycykgKyBCVUZGRVIKICBjb25kNyA8LSB4JG1hdGUgPT0gMwogIGNvbmQ4IDwtICEoeCRjbGFzcyAlaW4lIGMoMSwgMiwgMykpCgogIHgkY2xhc3NbKGNvbmQxICYgY29uZDIgJiBjb25kMyAmIGNvbmQ0ICYgY29uZDUgJiBjb25kNiAmIGNvbmQ3ICYgY29uZDgpXSA8LSA0CiAgcmV0dXJuKHgpCn0KCmNsYXNzaWZ5Q2lyY3VsYXJEZWNveSA8LSBmdW5jdGlvbih4KSB7CiAgY29uZDEgPC0geCRzdHIuUjEgIT0geFssIGMoInN0ci5SMmciLCAic3RyLlIybCIsICJzdHIuUjJzIildW2NiaW5kKDE6bnJvdyh4KSwgeCRtYXRlKV0KICBjb25kMiA8LSB4JGpDaHIuUjEgPT0geFssIGMoImp1bmN0aW9uLlIyZyIsICJqQ2hyLlIybCIsICJqQ2hyLlIycyIpXVtjYmluZCgxOm5yb3coeCksIHgkbWF0ZSldCiAgY29uZDMgPC0gISh4JGNsYXNzICVpbiUgYygxLCAyLCAzLCA0KSkKICBjb25kNCA8LSB4JG1hdGUgPT0gMwoKICB4JGNsYXNzWyhjb25kMSAmIGNvbmQyICYgY29uZDMpIF0gPC0gNSAKICB4JGNsYXNzWyEoY29uZDEgJiBjb25kMikgXSA8LSA1IAogIAogIHJldHVybih4KQp9CgpjbGFzc2lmeUxpbmVhclBhaXJlZE1hdGVzIDwtIGZ1bmN0aW9uKHgsIEJVRkZFUiA9IDE1LCBNSURQT0lOVCA9IDE1MCkgewogIAogIHIxX3N0YXJ0IDwtIHBtaW4oeCRqUG9zMS5SMSwgeCRqUG9zMi5SMSkgLSBNSURQT0lOVCArIHgkcG9zLlIxICMgbXlDb29yZAogIHIyX3N0YXJ0IDwtIHBtaW4oeCRqUG9zMS5SMmwsIHgkalBvczIuUjJsKSAgLSBNSURQT0lOVCArICB4JHBvcy5SMmwgI21hdGVDb29yZAogIAogIGNvbmQxIDwtIHgkc3RyLlIxICE9IHhbLCBjKCJzdHIuUjJnIiwgInN0ci5SMmwiLCAic3RyLlIycyIpXVtjYmluZCgxOm5yb3coeCksIHgkbWF0ZSldCiAgY29uZDIgPC0geCRqQ2hyLlIxID09IHhbLCBjKCJqdW5jdGlvbi5SMmciLCAiakNoci5SMmwiLCAiakNoci5SMnMiKV1bY2JpbmQoMTpucm93KHgpLCB4JG1hdGUpXQogIGNvbmQzX3AgPC0gcjJfc3RhcnQgPj0gcjFfc3RhcnQgLSBCVUZGRVIKICBjb25kM19tIDwtIHIxX3N0YXJ0ID49IHIyX3N0YXJ0IC0gQlVGRkVSCiAgY29uZDMgPC0gaWZlbHNlKHgkc3RyLlIxID09ICIrIiwgY29uZDNfcCwgY29uZDNfbSkKICBjb25kNCA8LSB4JG1hdGUgPT0gMiAKICB4JGNsYXNzWyhjb25kMSAmIGNvbmQyICYgY29uZDMgJiBjb25kNCldIDwtIDEgCiAgCiAgcmV0dXJuKHgpCn0KCmNsYXNzaWZ5TGluZWFyR2Vub21pY01hdGVzIDwtIGZ1bmN0aW9uKHgsIEJVRkZFUiA9IDE1LCBNSURQT0lOVCA9IDE1MCkgewogIAogIHIxX3N0YXJ0IDwtIHBtaW4oeCRqUG9zMS5SMSwgeCRqUG9zMi5SMSkgLSBNSURQT0lOVCArIHgkcG9zLlIxCiAgcjJfc3RhcnQgPC0geCRwb3MuUjJnCiAgCiAgY29uZDEgPC0geCRzdHIuUjEgIT0geFssIGMoInN0ci5SMmciLCAic3RyLlIybCIsICJzdHIuUjJzIildW2NiaW5kKDE6bnJvdyh4KSwgeCRtYXRlKV0KICBjb25kMiA8LSB4JGpDaHIuUjEgPT0geFssIGMoImp1bmN0aW9uLlIyZyIsICJqQ2hyLlIybCIsICJqQ2hyLlIycyIpXVtjYmluZCgxOm5yb3coeCksIHgkbWF0ZSldCiAgY29uZDNfcCA8LSByMl9zdGFydCA+PSByMV9zdGFydCAtIEJVRkZFUgogIGNvbmQzX20gPC0gcjFfc3RhcnQgPj0gcjJfc3RhcnQgLSBCVUZGRVIKICBjb25kMyA8LSBpZmVsc2UoeCRzdHIuUjEgPT0gIisiLCBjb25kM19wLCBjb25kM19tKQogIGNvbmQ0IDwtIHgkbWF0ZSA9PSAxIAogIGNvbmQ1IDwtICEoeCRjbGFzcyAlaW4lIGMoMSkpCiAgeCRjbGFzc1soY29uZDEgJiBjb25kMiAmIGNvbmQzICYgY29uZDQgJiBjb25kNSldIDwtIDIgCiAgcmV0dXJuKHgpCn0KCmNsYXNzaWZ5TGluZWFySWdub3JlIDwtIGZ1bmN0aW9uKHgpIHsKICAKICBjb25kMSA8LSB4JHN0ci5SMSAhPSB4WywgYygic3RyLlIyZyIsICJzdHIuUjJsIiwgInN0ci5SMnMiKV1bY2JpbmQoMTpucm93KHgpLCB4JG1hdGUpXQogIGNvbmQyIDwtIHgkakNoci5SMSA9PSB4WywgYygianVuY3Rpb24uUjJnIiwgImpDaHIuUjJsIiwgImpDaHIuUjJzIildW2NiaW5kKDE6bnJvdyh4KSwgeCRtYXRlKV0KICBjb25kMyA8LSB4JG1hdGUgPT0gMyAKICB4JGNsYXNzWyhjb25kMSAmIGNvbmQyICYgY29uZDMgKV0gPC0gMyAKICByZXR1cm4oeCkKfQoKY2xhc3NpZnlMaW5lYXJBbm9tYWx5IDwtIGZ1bmN0aW9uKHgpIHsKICAKICBjb25kMSA8LSB4JHN0ci5SMSAhPSB4WywgYygic3RyLlIyZyIsICJzdHIuUjJsIiwgInN0ci5SMnMiKV1bY2JpbmQoMTpucm93KHgpLCB4JG1hdGUpXQogIGNvbmQyIDwtIHgkakNoci5SMSA9PSB4WywgYygianVuY3Rpb24uUjJnIiwgImpDaHIuUjJsIiwgImpDaHIuUjJzIildW2NiaW5kKDE6bnJvdyh4KSwgeCRtYXRlKV0KICB4JGNsYXNzWyEoY29uZDEgJiBjb25kMildIDwtIDQgCiAgeCRjbGFzc1t4JGNsYXNzID09IDBdIDwtIDQKCiAgcmV0dXJuKHgpCn0KCmxpbmVhcl9yZWFkc19tYXBwZWQgPC0gIAogIGxpbmVhcl9yZWFkc19tYXBwZWQgJT4lIAogIGNsYXNzaWZ5TGluZWFyUGFpcmVkTWF0ZXMoKSAlPiUKICBjbGFzc2lmeUxpbmVhckdlbm9taWNNYXRlcygpICU+JQogIGNsYXNzaWZ5TGluZWFySWdub3JlKCkgICU+JSAKICBjbGFzc2lmeUxpbmVhckFub21hbHkoKSAgCgpzY3JhbWJsZWRfcmVhZHNfbWFwcGVkIDwtICAKICBzY3JhbWJsZWRfcmVhZHNfbWFwcGVkICU+JSAKICBjbGFzc2lmeUNpcmN1bGFyUGFpcmVkTWF0ZXMoKSAlPiUKICBjbGFzc2lmeUNpcmN1bGFyTGluZWFyTWF0ZXMoKSAlPiUKICBjbGFzc2lmeUNpcmN1bGFyR2Vub21pY01hdGVzICAlPiUKICBjbGFzc2lmeUNpcmN1bGFySWdub3JlICAlPiUKICBjbGFzc2lmeUNpcmN1bGFyRGVjb3kKCmBgYCAKVGhlIGZyZXF1ZW5jeSBvZiByZWFkIGNhdGVnb3JpZXMgYXJlIGFzIGZvbGxvd3M6CmBgYHtyfQpsaW5lYXJfanVuY3Rpb25fcmVhZHMgPC0gCiAgbGluZWFyX3JlYWRzX21hcHBlZCRjbGFzcyAlPiUKICB0YWJ1bGF0ZSAgJT4lCiAgYXMuZGF0YS5mcmFtZSgpCgpzY3JhbWJsZWRfanVuY3Rpb25fcmVhZHMgPC0gCiAgc2NyYW1ibGVkX3JlYWRzX21hcHBlZCRjbGFzcyAlPiUKICB0YWJ1bGF0ZSAlPiUKICBhcy5kYXRhLmZyYW1lKCkgCgpyb3duYW1lcyhsaW5lYXJfanVuY3Rpb25fcmVhZHMpIDwtIGMoIkxpbmVhci5sIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJMaW5lYXIuZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiSWdub3JlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJBbm9tYWx5IikKCnJvd25hbWVzKHNjcmFtYmxlZF9qdW5jdGlvbl9yZWFkcykgPC0gYygiQ2lyY3VsYXIucyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ2lyY3VsYXIubCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ2lyY3VsYXIuZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiSWdub3JlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJEZWNveSIpCmxpbmVhcl9qdW5jdGlvbl9yZWFkcyAKc2NyYW1ibGVkX2p1bmN0aW9uX3JlYWRzCmBgYApgYGB7cn0KY2F0KCAiIG51bWJlciBvZiBsaW5lYXIgcmVhZHM6IiwKICAgICBzdW0obGluZWFyX2p1bmN0aW9uX3JlYWRzWzE6MixdKSwgIlxuIiwKICAgICAibnVtYmVyIG9mIGNpcmN1bGFyIHJlYWRzOiIsCiAgICAgc3VtKHNjcmFtYmxlZF9qdW5jdGlvbl9yZWFkc1sxOjMsXSksICJcbiIsCiAgInRvdGFsIG51bWJlciBvZiBhbm9tYWx5IHJlYWRzOiIsIAogICAgc3VtKGxpbmVhcl9qdW5jdGlvbl9yZWFkc1s0LF0pLCAiXG4iLAogICAgIm51bWJlciBvZiAgZGVjb3kgcmVhZHM6IiwKICAgIHN1bShzY3JhbWJsZWRfanVuY3Rpb25fcmVhZHNbNSxdKSkKYGBgCgpSZW9yZGVyIGNvbHVtbnMgCmBgYHtyfQpyZWZjb2xzIDwtIGMoImlkIiwiY2xhc3MiLCAibWF0ZSIpCmxpbmVhcl9yZWFkc19tYXBwZWQgPC0gbGluZWFyX3JlYWRzX21hcHBlZFssIGMocmVmY29scywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZXRkaWZmKG5hbWVzKGxpbmVhcl9yZWFkc19tYXBwZWQpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVmY29scykpXQpzY3JhbWJsZWRfcmVhZHNfbWFwcGVkIDwtIHNjcmFtYmxlZF9yZWFkc19tYXBwZWRbLCBjKHJlZmNvbHMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2V0ZGlmZihuYW1lcyhzY3JhbWJsZWRfcmVhZHNfbWFwcGVkKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlZmNvbHMpKV0KCnNjcmFtYmxlZF9yZWFkc19tYXBwZWQKYGBgCgpTYXZlIGRhdGEKYGBge3J9CndyaXRlLnRhYmxlKGxpbmVhcl9yZWFkc19tYXBwZWQsCiAgICAgICAgICAgICJsaW5lYXJfcmVhZHNfbWFwcGVkLnR4dCIsIAogICAgICAgICAgICByb3cubmFtZXMgPSBGQUxTRSwKICAgICAgICAgICAgc2VwID0gIlx0IikKCndyaXRlLnRhYmxlKHNjcmFtYmxlZF9yZWFkc19tYXBwZWQsCiAgICAgICAgICAgICJzY3JhbWJsZWRfcmVhZHNfbWFwcGVkLnR4dCIsIAogICAgICAgICAgICByb3cubmFtZXMgPSBGQUxTRSwKICAgICAgICAgICAgc2VwID0gIlx0IikKc2F2ZVJEUyhzY3JhbWJsZWRfcmVhZHNfbWFwcGVkLCAic2NyYW1ibGVkX3JlYWRzX21hcHBlZC5yZHMiKQpzYXZlUkRTKGxpbmVhcl9yZWFkc19tYXBwZWQsICJsaW5lYXJfcmVhZHNfbWFwcGVkLnJkcyIpCgojc2NyYW1ibGVkX3JlYWRzX21hcHBlZCA8LSByZWFkUkRTKCJzY3JhbWJsZWRfcmVhZHNfbWFwcGVkLnJkcyIpCiNsaW5lYXJfcmVhZHNfbWFwcGVkIDwtIHJlYWRSRFMoImxpbmVhcl9yZWFkc19tYXBwZWQucmRzIikKCmBgYAoKYGBge3J9CnNlc3Npb25JbmZvKCkKYGBgCgojIFJlZmVyZW5jZXMK