We need many packages to run the code in this notebook. Here are they

library(tidygenomics)  # latest development version can be installed by devtools::install_github("const-ae/tidygenomics")
library(biobroom)    # devtools::install_github("StoreyLab/biobroom")
library(tidyverse)
# source("http://www.bioconductor.org/biocLite.R")
# biocLite("DESeq2", ask=FALSE, suppressUpdates=TRUE)
# biocLite("limma", ask=FALSE, suppressUpdates=TRUE)
human_genes_22 <- readRDS(gzcon(url("https://s3-us-west-2.amazonaws.com/veri-analizi/human_genes_chr22.rds")))
human_snp_22 <- readRDS(gzcon(url("https://s3-us-west-2.amazonaws.com/veri-analizi/human_snp147_chr22.rds")))

Bioconductor

Bioconductor packages have their own repository. Some packages are both in CRAN and in Bioconductor, but some packages are solely on Bioconductor. Thus, for these packages you’ll get the following error:

> install.packages("TCGAbiolinks")
Installing package into '/usr/local/lib/R/site-library'
(as 'lib' is unspecified)
Warning in install.packages :
  package 'TCGAbiolinks' is not available (for R version 3.4.1)

But, same package can be installed with the following command:

# DO NOT RUN THIS! THIS IS AN EXAMPLE!
source("https://bioconductor.org/biocLite.R")
biocLite("TCGAbiolinks")

There’s app for that

There are many Bioconductor packages available for many domains. Please go over the Search page

To have a deeper insight about some Bioconductor packages please visit the Tutorials page.

Biological vectors/objects

There are many, specific and complicated objects in R. GRanges, BioString, ExpressionSet, SummarizedExperiment, RangedSummarizedExperiment are few of them.

Here’s visual representation of RangedSummarizedExperiment object:

SummarizedExperiment Object Structure (Source)

Genomic Ranges

More information about GRanges is located at its Bioconductor page. First PDF file (GenomicRanges Intro), chapter 2.3 mentions interval operations. Chapter 3 mentions GRangesList object (even more complicated) where list of ranges are grouped.

Tidygenomics

Interval operations of genomic ranges can be done in tibble formatted tables. The tables can be result of a pipe or any other dplyr/tidyverse command.

Examples are at: https://github.com/const-ae/tidygenomics

genome_intersect

library(tidygenomics)
library(tidyverse)
x1 <- data.frame(id = 1:4, 
                chromosome = c("chr1", "chr1", "chr2", "chr2"),
                start = c(100, 200, 300, 400),
                end = c(150, 250, 350, 450))
x2 <- data.frame(id = 1:4,
                 chromosome = c("chr1", "chr2", "chr2", "chr1"),
                 start = c(140, 210, 400, 300),
                 end = c(160, 240, 415, 320))
x1
x2
genome_intersect(x1, x2, by=c("chromosome", "start", "end"), mode="both")

genome_substract

x2 <- data.frame(id = 1:4,
                chromosome = c("chr1", "chr2", "chr1", "chr1"),
                start = c(120, 210, 300, 400),
                end = c(125, 240, 320, 415))
genome_subtract(x1, x2, by=c("chromosome", "start", "end"))

genome_join_closest

x2 <- data_frame(id = 1:4,
                 chromosome = c("chr1", "chr1", "chr1", "chr2"),
                 start = c(220, 210, 300, 400),
                 end = c(225, 240, 320, 415))
genome_join_closest(x1, x2, by=c("chromosome", "start", "end"), distance_column_name="distance", mode="left")
Column `chromosome` joining factor and character vector, coercing into character vector

genome_cluster

genome_cluster(x1, by=c("chromosome", "start", "end"))
genome_cluster(x1, by=c("chromosome", "start", "end"), max_distance=10)

genome_complement

genome_complement(x1, by=c("chromosome", "start", "end"))

genome_join

x2 <- data_frame(id = 1:4,
                 chromosome = c("chr1", "chr1", "chr1", "chr2"),
                 start = c(220, 210, 300, 400),
                 end = c(225, 240, 320, 415))
fuzzyjoin::genome_join(x1, x2, by=c("chromosome", "start", "end"), mode="inner")
Column `chromosome` joining factor and character vector, coercing into character vector
fuzzyjoin::genome_join(x1, x2, by=c("chromosome", "start", "end"), mode="left")
Column `chromosome` joining factor and character vector, coercing into character vector
fuzzyjoin::genome_join(x1, x2, by=c("chromosome", "start", "end"), mode="anti")
Column `chromosome` joining factor and character vector, coercing into character vector

tidygenomics example

human_genes_22            # loaded from url in first chunk
human_snp_22              # loaded from url in first chunk

Notice that column names and shapes of two files can be different (e.g., strand can be “-” or “-1”), other softwares or packages would complain about this issue. But being free of any restrictions, we can manipulate with dplyr verbs and then use them

Let’s see which snps are within human genes

human_genes_22 %>% 
  genome_intersect(human_snp_22, by=c("chrom", "start", "end"), mode="both")

Since this is tibble, let’s count snps per gene

human_genes_22 %>% 
  genome_intersect(human_snp_22, by=c("chrom", "start", "end"), mode="both") %>% 
  count(name.x)

Let’s normalize this with gene length

human_genes_22 %>% 
  genome_intersect(human_snp_22, by=c("chrom", "start", "end"), mode="both") %>% 
  count(name.x) %>% 
  inner_join(human_genes_22, by=c("name.x"="name")) %>% 
  mutate(snp_per_kb=n / ( (end-start) /1000) ) %>% 
  arrange(-snp_per_kb)

What about, snps within promoter regions? Let’s generate 1000 bp upstream of genes with dplyr verbs and then intersect.

human_genes_22 %>% 
  mutate(end=start-1, start=start-1000) %>% 
  genome_intersect(human_snp_22, by=c("chrom", "start", "end"), mode="both")

It works! But we made a big mistake, the genes on negative strand should have their upstream regions extended towards right, not left. So, we can use ifelse function to extend promoter regions according to strand value.

human_genes_22 %>% 
  mutate(p_end=ifelse(strand=="+", start-1, end+1000),
         p_start=ifelse(strand=="+", start-1000, end+1),
         start=p_start,
         end=p_end) %>% 
  genome_intersect(human_snp_22, by=c("chrom", "start", "end"), mode="both")

Also, notice that we didn’t use strand in intersections. We can use this column to see strand specific intersections. We already know that genes have strand specificity, andt the snp data contains strand information. Let’s find snps in promoter regions where promoter region strand and snp strand matches. For that, we use filter verb:

human_genes_22 %>% 
  mutate(p_end=ifelse(strand=="+", start-1, end+1000),
         p_start=ifelse(strand=="+", start-1000, end+1),
         start=p_start,
         end=p_end) %>% 
  genome_intersect(human_snp_22, by=c("chrom", "start", "end"), mode="both") %>% 
  filter(strand.x==strand.y)

As you can see, you can feed genome_intersect with a custom table and you can use genome_intersect within a pipe. That’s huge benefit allowing flexibility.

valr package

The pioneer in genome arithmetic is bedtools software which can perform any possible genome interval operation. Please check the tutorial page for numerous examples.

Tidygenomics package is trying to implement some of the basic operations done by bedtools software. To do more complicated and advanced operations some R packages interface with the bedtools software installed locally. But valr package performs most of the bedtools operations natively. Please visit its main page for more info.

#PLEASE DO NOT RUN, THIS IS JUST AN EXAMPLE
# devtools::install_github("rnabioco/valrdata")     #installs valr and valrdata
library(valr)
# valrdata::dnase_data %>% bind_rows(.id="id")
valrdata::repeat_data
valrdata::hg19_genes  # similar to human_genes dataset we have used in past

An extreme example, from valr examples page.

# PLEASE DO NOT RUN, THIS IS AN EXAMPLE
devtools::install_github("rnabioco/valrdata")
library(valr)
res <- valrdata::dnase_data %>%
  cross2(.,.) %>%
  map(lift(bed_jaccard)) %>%
  map('jaccard') %>%
  flatten_dbl() %>%
  matrix(nrow = 20, ncol = 20)

The output is 20x20 matrix having jaccard distances of 20 different data. Below is truncated portion of the result.

           [,1]      [,2]      [,3]      [,4]      [,5]      [,6]      [,7]      [,8]      [,9]     [,10] 
 [1,] 1.0000000 0.3543413 0.2405429 0.2200565 0.2272345 0.2084734 0.2061044 0.2016117 0.2152373 0.1906840 
 [2,] 0.3543413 1.0000000 0.2315717 0.2098134 0.2261107 0.1949926 0.1787604 0.1836408 0.1919568 0.1750850 
 [3,] 0.2405429 0.2315717 1.0000000 0.5014561 0.6125025 0.2749579 0.2522233 0.2598619 0.2795607 0.2473207 
 [4,] 0.2200565 0.2098134 0.5014561 1.0000000 0.5063695 0.2538434 0.2346832 0.2365053 0.2621719 0.2208662 
 [5,] 0.2272345 0.2261107 0.6125025 0.5063695 1.0000000 0.2702726 0.2264408 0.2487867 0.2621962 0.2350299 
 [6,] 0.2084734 0.1949926 0.2749579 0.2538434 0.2702726 1.0000000 0.4515375 0.6073708 0.5761303 0.5547881 
 [7,] 0.2061044 0.1787604 0.2522233 0.2346832 0.2264408 0.4515375 1.0000000 0.4818408 0.5238530 0.4808681 
 [8,] 0.2016117 0.1836408 0.2598619 0.2365053 0.2487867 0.6073708 0.4818408 1.0000000 0.5986499 0.5697326 
 [9,] 0.2152373 0.1919568 0.2795607 0.2621719 0.2621962 0.5761303 0.5238530 0.5986499 1.0000000 0.5384424 
[10,] 0.1906840 0.1750850 0.2473207 0.2208662 0.2350299 0.5547881 0.4808681 0.5697326 0.5384424 1.0000000 
...truncated...

Authors of valr package also put down a lecture titled “Practical Biological Data Analysis with R/RStudio” please check the lecture contents to have ideas about biological data analysis.


Biobroom package

This package is able convert many different biological objects into tidy format. Please visit its help page for more information, some of the examples are run below.

Limma example

set.seed(2014)
data <- matrix(rnorm(1000), ncol=4)
data[, 1:2] <- data[, 1:2] + .5  # add an effect
rownames(data) <- paste0("gene", 1:nrow(data))
des <- data.frame(treatment = c("a", "a", "b", "b"),confounding = rnorm(4))
lfit <- lmFit(data, model.matrix(~ treatment + confounding, des))
eb <- eBayes(lfit)
head(tidy(lfit))
head(tidy(eb))
 # the tidied form puts it in an ideal form for plotting
        ggplot(tidy(lfit), aes(estimate)) + geom_histogram(binwidth=1) +
            facet_wrap(~ term)
        ggplot(tidy(eb), aes(p.value)) + geom_histogram(binwidth=.2) +
            facet_wrap(~ term)

Expression Set Example

hammer
ExpressionSet (storageMode: lockedEnvironment)
assayData: 29516 features, 8 samples 
  element names: exprs 
protocolData: none
phenoData
  sampleNames: SRX020102 SRX020103 ... SRX020098-101 (8 total)
  varLabels: sample.id num.tech.reps ... Time (5 total)
  varMetadata: labelDescription
featureData
  featureNames: ENSRNOG00000000001 ENSRNOG00000000007 ... ENSRNOG00000045521 (29516 total)
  fvarLabels: gene
  fvarMetadata: labelDescription
experimentData: use 'experimentData(object)'
Annotation:  

Check out the tidied version of the data.

head(tidy(hammer))
head(tidy(hammer, addPheno = TRUE))

DESeq2 example

# source("https://bioconductor.org/biocLite.R")
# biocLite("airway")
library(DESeq2)
library(airway)
data(airway)
airway_se <- airway
airway_dds <- DESeqDataSet(airway_se, design = ~cell + dex)
colData(airway) %>% head()
DataFrame with 6 rows and 9 columns
           SampleName     cell      dex    albut        Run avgLength Experiment    Sample    BioSample
             <factor> <factor> <factor> <factor>   <factor> <integer>   <factor>  <factor>     <factor>
SRR1039508 GSM1275862   N61311    untrt    untrt SRR1039508       126  SRX384345 SRS508568 SAMN02422669
SRR1039509 GSM1275863   N61311      trt    untrt SRR1039509       126  SRX384346 SRS508567 SAMN02422675
SRR1039512 GSM1275866  N052611    untrt    untrt SRR1039512       126  SRX384349 SRS508571 SAMN02422678
SRR1039513 GSM1275867  N052611      trt    untrt SRR1039513        87  SRX384350 SRS508572 SAMN02422670
SRR1039516 GSM1275870  N080611    untrt    untrt SRR1039516       120  SRX384353 SRS508575 SAMN02422682
SRR1039517 GSM1275871  N080611      trt    untrt SRR1039517       126  SRX384354 SRS508576 SAMN02422673
rowRanges(airway) %>% head()
GRangesList object of length 6:
$ENSG00000000003 
GRanges object with 17 ranges and 2 metadata columns:
       seqnames               ranges strand |   exon_id       exon_name
          <Rle>            <IRanges>  <Rle> | <integer>     <character>
   [1]        X [99883667, 99884983]      - |    667145 ENSE00001459322
   [2]        X [99885756, 99885863]      - |    667146 ENSE00000868868
   [3]        X [99887482, 99887565]      - |    667147 ENSE00000401072
   [4]        X [99887538, 99887565]      - |    667148 ENSE00001849132
   [5]        X [99888402, 99888536]      - |    667149 ENSE00003554016
   ...      ...                  ...    ... .       ...             ...
  [13]        X [99890555, 99890743]      - |    667156 ENSE00003512331
  [14]        X [99891188, 99891686]      - |    667158 ENSE00001886883
  [15]        X [99891605, 99891803]      - |    667159 ENSE00001855382
  [16]        X [99891790, 99892101]      - |    667160 ENSE00001863395
  [17]        X [99894942, 99894988]      - |    667161 ENSE00001828996

...
<5 more elements>
-------
seqinfo: 722 sequences (1 circular) from an unspecified genome
assay(airway) %>% head()
                SRR1039508 SRR1039509 SRR1039512 SRR1039513 SRR1039516 SRR1039517 SRR1039520 SRR1039521
ENSG00000000003        679        448        873        408       1138       1047        770        572
ENSG00000000005          0          0          0          0          0          0          0          0
ENSG00000000419        467        515        621        365        587        799        417        508
ENSG00000000457        260        211        263        164        245        331        233        229
ENSG00000000460         60         55         40         35         78         63         76         60
ENSG00000000938          0          0          2          0          1          0          0          0
tidy(airway_dds) %>%  head()
# differential expression analysis
deseq <- DESeq(airway_dds)
estimating size factors
estimating dispersions
gene-wise dispersion estimates
mean-dispersion relationship
final dispersion estimates
fitting model and testing
results <- results(deseq)
# tidy results
tidy(results) %>% head()

Announcement

Volunteer students are wanted, who would help gathering lecture materials into book format via bookdown


Last Updated 2018-01-03

LS0tCnRpdGxlOiB8ICAgCiB8IERhdGEgQW5hbHlzaXMgYW5kIFZpc3VhbGl6YXRpb24gIAogfCBMZXNzb24gMTUgICAKIHwgQmlvbG9naWNhbCBEYXRhIC0gVGlkeSBXYXkKYXV0aG9yOiAiYWxwZXIgeWlsbWF6IgpkYXRlOiAiRGVjZW1iZXIgMjZ0aCwgMjAxNyIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKV2UgbmVlZCBtYW55IHBhY2thZ2VzIHRvIHJ1biB0aGUgY29kZSBpbiB0aGlzIG5vdGVib29rLiBIZXJlIGFyZSB0aGV5CgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KHRpZHlnZW5vbWljcykgICMgbGF0ZXN0IGRldmVsb3BtZW50IHZlcnNpb24gY2FuIGJlIGluc3RhbGxlZCBieSBkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoImNvbnN0LWFlL3RpZHlnZW5vbWljcyIpCmxpYnJhcnkoYmlvYnJvb20pICAgICMgZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCJTdG9yZXlMYWIvYmlvYnJvb20iKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKIyBzb3VyY2UoImh0dHA6Ly93d3cuYmlvY29uZHVjdG9yLm9yZy9iaW9jTGl0ZS5SIikKIyBiaW9jTGl0ZSgiREVTZXEyIiwgYXNrPUZBTFNFLCBzdXBwcmVzc1VwZGF0ZXM9VFJVRSkKIyBiaW9jTGl0ZSgibGltbWEiLCBhc2s9RkFMU0UsIHN1cHByZXNzVXBkYXRlcz1UUlVFKQoKaHVtYW5fZ2VuZXNfMjIgPC0gcmVhZFJEUyhnemNvbih1cmwoImh0dHBzOi8vczMtdXMtd2VzdC0yLmFtYXpvbmF3cy5jb20vdmVyaS1hbmFsaXppL2h1bWFuX2dlbmVzX2NocjIyLnJkcyIpKSkKaHVtYW5fc25wXzIyIDwtIHJlYWRSRFMoZ3pjb24odXJsKCJodHRwczovL3MzLXVzLXdlc3QtMi5hbWF6b25hd3MuY29tL3ZlcmktYW5hbGl6aS9odW1hbl9zbnAxNDdfY2hyMjIucmRzIikpKQpgYGAKCiMgQmlvY29uZHVjdG9yCgpCaW9jb25kdWN0b3IgcGFja2FnZXMgaGF2ZSB0aGVpciBvd24gcmVwb3NpdG9yeS4gU29tZSBwYWNrYWdlcyBhcmUgYm90aCBpbiBDUkFOIGFuZCBpbiBCaW9jb25kdWN0b3IsIGJ1dCBzb21lIHBhY2thZ2VzIGFyZSBzb2xlbHkgb24gQmlvY29uZHVjdG9yLiBUaHVzLCBmb3IgdGhlc2UgcGFja2FnZXMgeW91J2xsIGdldCB0aGUgZm9sbG93aW5nIGVycm9yOgoKYGBgCj4gaW5zdGFsbC5wYWNrYWdlcygiVENHQWJpb2xpbmtzIikKSW5zdGFsbGluZyBwYWNrYWdlIGludG8gJy91c3IvbG9jYWwvbGliL1Ivc2l0ZS1saWJyYXJ5JwooYXMgJ2xpYicgaXMgdW5zcGVjaWZpZWQpCldhcm5pbmcgaW4gaW5zdGFsbC5wYWNrYWdlcyA6CiAgcGFja2FnZSAnVENHQWJpb2xpbmtzJyBpcyBub3QgYXZhaWxhYmxlIChmb3IgUiB2ZXJzaW9uIDMuNC4xKQpgYGAKCkJ1dCwgc2FtZSBwYWNrYWdlIGNhbiBiZSBpbnN0YWxsZWQgd2l0aCB0aGUgZm9sbG93aW5nIGNvbW1hbmQ6CgpgYGB7ciBldmFsPUZBTFNFfQojIERPIE5PVCBSVU4gVEhJUyEgVEhJUyBJUyBBTiBFWEFNUExFIQpzb3VyY2UoImh0dHBzOi8vYmlvY29uZHVjdG9yLm9yZy9iaW9jTGl0ZS5SIikKYmlvY0xpdGUoIlRDR0FiaW9saW5rcyIpCmBgYAoKIyMgVGhlcmUncyBhcHAgZm9yIHRoYXQKClRoZXJlIGFyZSBtYW55IEJpb2NvbmR1Y3RvciBwYWNrYWdlcyBhdmFpbGFibGUgZm9yIG1hbnkgZG9tYWlucy4gUGxlYXNlIGdvIG92ZXIgdGhlIFtTZWFyY2ggcGFnZV0oaHR0cDovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvcmVsZWFzZS9CaW9jVmlld3MuaHRtbCNfX19Tb2Z0d2FyZSkKClRvIGhhdmUgYSBkZWVwZXIgaW5zaWdodCBhYm91dCBzb21lIEJpb2NvbmR1Y3RvciBwYWNrYWdlcyBwbGVhc2UgdmlzaXQgdGhlIFtUdXRvcmlhbHNdKGh0dHBzOi8vc3VwcG9ydC5iaW9jb25kdWN0b3Iub3JnL3QvVHV0b3JpYWxzLykgcGFnZS4KCgojIEJpb2xvZ2ljYWwgdmVjdG9ycy9vYmplY3RzCgpUaGVyZSBhcmUgbWFueSwgc3BlY2lmaWMgYW5kIGNvbXBsaWNhdGVkIG9iamVjdHMgaW4gUi4gKkdSYW5nZXMqLCAqQmlvU3RyaW5nKiwgKkV4cHJlc3Npb25TZXQqLCAqU3VtbWFyaXplZEV4cGVyaW1lbnQqLCAqUmFuZ2VkU3VtbWFyaXplZEV4cGVyaW1lbnQqIGFyZSBmZXcgb2YgdGhlbS4KCkhlcmUncyB2aXN1YWwgcmVwcmVzZW50YXRpb24gb2YgUmFuZ2VkU3VtbWFyaXplZEV4cGVyaW1lbnQgb2JqZWN0OgoKIVtTdW1tYXJpemVkRXhwZXJpbWVudCBPYmplY3QgU3RydWN0dXJlXShodHRwczovL21lZGlhLm5hdHVyZS5jb20vZnVsbC9uYXR1cmUtYXNzZXRzL25tZXRoL2pvdXJuYWwvdjEyL24yL2ltYWdlcy9ubWV0aC4zMjUyLUYyLmpwZykKKFtTb3VyY2VdKGh0dHBzOi8vbWVkaWEubmF0dXJlLmNvbS9mdWxsL25hdHVyZS1hc3NldHMvbm1ldGgvam91cm5hbC92MTIvbjIvaW1hZ2VzL25tZXRoLjMyNTItRjIuanBnKSkKCgojIyBHZW5vbWljIFJhbmdlcwoKTW9yZSBpbmZvcm1hdGlvbiBhYm91dCBHUmFuZ2VzIGlzIGxvY2F0ZWQgYXQgW2l0cyBCaW9jb25kdWN0b3IgcGFnZV0oaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy9odG1sL0dlbm9taWNSYW5nZXMuaHRtbCkuIEZpcnN0IFBERiBmaWxlIChbR2Vub21pY1JhbmdlcyBJbnRyb10oaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy92aWduZXR0ZXMvR2Vub21pY1Jhbmdlcy9pbnN0L2RvYy9HZW5vbWljUmFuZ2VzSW50cm9kdWN0aW9uLnBkZikpLCBjaGFwdGVyIDIuMyBtZW50aW9ucyBpbnRlcnZhbCBvcGVyYXRpb25zLiBDaGFwdGVyIDMgbWVudGlvbnMgR1Jhbmdlc0xpc3Qgb2JqZWN0IChldmVuIG1vcmUgY29tcGxpY2F0ZWQpIHdoZXJlIGxpc3Qgb2YgcmFuZ2VzIGFyZSBncm91cGVkLgoKIyMgVGlkeWdlbm9taWNzCgpJbnRlcnZhbCBvcGVyYXRpb25zIG9mIGdlbm9taWMgcmFuZ2VzIGNhbiBiZSBkb25lIGluICoqdGliYmxlKiogZm9ybWF0dGVkIHRhYmxlcy4gVGhlIHRhYmxlcyBjYW4gYmUgcmVzdWx0IG9mIGEgcGlwZSBvciBhbnkgb3RoZXIgZHBseXIvdGlkeXZlcnNlIGNvbW1hbmQuCgpFeGFtcGxlcyBhcmUgYXQ6IGh0dHBzOi8vZ2l0aHViLmNvbS9jb25zdC1hZS90aWR5Z2Vub21pY3MKCiMjIyBnZW5vbWVfaW50ZXJzZWN0CgpgYGB7cn0KbGlicmFyeSh0aWR5Z2Vub21pY3MpCmxpYnJhcnkodGlkeXZlcnNlKQpgYGAKCmBgYHtyfQp4MSA8LSBkYXRhLmZyYW1lKGlkID0gMTo0LCAKICAgICAgICAgICAgICAgIGNocm9tb3NvbWUgPSBjKCJjaHIxIiwgImNocjEiLCAiY2hyMiIsICJjaHIyIiksCiAgICAgICAgICAgICAgICBzdGFydCA9IGMoMTAwLCAyMDAsIDMwMCwgNDAwKSwKICAgICAgICAgICAgICAgIGVuZCA9IGMoMTUwLCAyNTAsIDM1MCwgNDUwKSkKCngyIDwtIGRhdGEuZnJhbWUoaWQgPSAxOjQsCiAgICAgICAgICAgICAgICAgY2hyb21vc29tZSA9IGMoImNocjEiLCAiY2hyMiIsICJjaHIyIiwgImNocjEiKSwKICAgICAgICAgICAgICAgICBzdGFydCA9IGMoMTQwLCAyMTAsIDQwMCwgMzAwKSwKICAgICAgICAgICAgICAgICBlbmQgPSBjKDE2MCwgMjQwLCA0MTUsIDMyMCkpCmBgYAoKYGBge3J9CngxCmBgYAoKYGBge3J9CngyCmBgYAoKYGBge3J9Cmdlbm9tZV9pbnRlcnNlY3QoeDEsIHgyLCBieT1jKCJjaHJvbW9zb21lIiwgInN0YXJ0IiwgImVuZCIpLCBtb2RlPSJib3RoIikKYGBgCgojIyMgZ2Vub21lX3N1YnN0cmFjdAoKYGBge3J9CngyIDwtIGRhdGEuZnJhbWUoaWQgPSAxOjQsCiAgICAgICAgICAgICAgICBjaHJvbW9zb21lID0gYygiY2hyMSIsICJjaHIyIiwgImNocjEiLCAiY2hyMSIpLAogICAgICAgICAgICAgICAgc3RhcnQgPSBjKDEyMCwgMjEwLCAzMDAsIDQwMCksCiAgICAgICAgICAgICAgICBlbmQgPSBjKDEyNSwgMjQwLCAzMjAsIDQxNSkpCgpnZW5vbWVfc3VidHJhY3QoeDEsIHgyLCBieT1jKCJjaHJvbW9zb21lIiwgInN0YXJ0IiwgImVuZCIpKQpgYGAKCgojIyMgZ2Vub21lX2pvaW5fY2xvc2VzdAoKYGBge3J9CngyIDwtIGRhdGFfZnJhbWUoaWQgPSAxOjQsCiAgICAgICAgICAgICAgICAgY2hyb21vc29tZSA9IGMoImNocjEiLCAiY2hyMSIsICJjaHIxIiwgImNocjIiKSwKICAgICAgICAgICAgICAgICBzdGFydCA9IGMoMjIwLCAyMTAsIDMwMCwgNDAwKSwKICAgICAgICAgICAgICAgICBlbmQgPSBjKDIyNSwgMjQwLCAzMjAsIDQxNSkpCmdlbm9tZV9qb2luX2Nsb3Nlc3QoeDEsIHgyLCBieT1jKCJjaHJvbW9zb21lIiwgInN0YXJ0IiwgImVuZCIpLCBkaXN0YW5jZV9jb2x1bW5fbmFtZT0iZGlzdGFuY2UiLCBtb2RlPSJsZWZ0IikKYGBgCgoKIyMjIGdlbm9tZV9jbHVzdGVyCgpgYGB7cn0KZ2Vub21lX2NsdXN0ZXIoeDEsIGJ5PWMoImNocm9tb3NvbWUiLCAic3RhcnQiLCAiZW5kIikpCmBgYAoKYGBge3J9Cmdlbm9tZV9jbHVzdGVyKHgxLCBieT1jKCJjaHJvbW9zb21lIiwgInN0YXJ0IiwgImVuZCIpLCBtYXhfZGlzdGFuY2U9MTApCmBgYAoKIyMjIGdlbm9tZV9jb21wbGVtZW50CgpgYGB7cn0KZ2Vub21lX2NvbXBsZW1lbnQoeDEsIGJ5PWMoImNocm9tb3NvbWUiLCAic3RhcnQiLCAiZW5kIikpCmBgYAoKIyMjIGdlbm9tZV9qb2luCgpgYGB7cn0KeDIgPC0gZGF0YV9mcmFtZShpZCA9IDE6NCwKICAgICAgICAgICAgICAgICBjaHJvbW9zb21lID0gYygiY2hyMSIsICJjaHIxIiwgImNocjEiLCAiY2hyMiIpLAogICAgICAgICAgICAgICAgIHN0YXJ0ID0gYygyMjAsIDIxMCwgMzAwLCA0MDApLAogICAgICAgICAgICAgICAgIGVuZCA9IGMoMjI1LCAyNDAsIDMyMCwgNDE1KSkKZnV6enlqb2luOjpnZW5vbWVfam9pbih4MSwgeDIsIGJ5PWMoImNocm9tb3NvbWUiLCAic3RhcnQiLCAiZW5kIiksIG1vZGU9ImlubmVyIikKYGBgCgpgYGB7cn0KZnV6enlqb2luOjpnZW5vbWVfam9pbih4MSwgeDIsIGJ5PWMoImNocm9tb3NvbWUiLCAic3RhcnQiLCAiZW5kIiksIG1vZGU9ImxlZnQiKQpgYGAKCmBgYHtyfQpmdXp6eWpvaW46Omdlbm9tZV9qb2luKHgxLCB4MiwgYnk9YygiY2hyb21vc29tZSIsICJzdGFydCIsICJlbmQiKSwgbW9kZT0iYW50aSIpCmBgYAoKIyMgdGlkeWdlbm9taWNzIGV4YW1wbGUKCmBgYHtyfQpodW1hbl9nZW5lc18yMiAgICAgICAgICAgICMgbG9hZGVkIGZyb20gdXJsIGluIGZpcnN0IGNodW5rCmh1bWFuX3NucF8yMiAgICAgICAgICAgICAgIyBsb2FkZWQgZnJvbSB1cmwgaW4gZmlyc3QgY2h1bmsKYGBgCgo+IE5vdGljZSB0aGF0IGNvbHVtbiBuYW1lcyBhbmQgc2hhcGVzIG9mIHR3byBmaWxlcyBjYW4gYmUgZGlmZmVyZW50IChlLmcuLCBzdHJhbmQgY2FuIGJlICItIiBvciAiLTEiKSwgb3RoZXIgc29mdHdhcmVzIG9yIHBhY2thZ2VzIHdvdWxkIGNvbXBsYWluIGFib3V0IHRoaXMgaXNzdWUuIEJ1dCBiZWluZyBmcmVlIG9mIGFueSByZXN0cmljdGlvbnMsIHdlIGNhbiBtYW5pcHVsYXRlIHdpdGggZHBseXIgdmVyYnMgYW5kIHRoZW4gdXNlIHRoZW0KCkxldCdzIHNlZSB3aGljaCBzbnBzIGFyZSB3aXRoaW4gaHVtYW4gZ2VuZXMKCmBgYHtyfQpodW1hbl9nZW5lc18yMiAlPiUgCiAgZ2Vub21lX2ludGVyc2VjdChodW1hbl9zbnBfMjIsIGJ5PWMoImNocm9tIiwgInN0YXJ0IiwgImVuZCIpLCBtb2RlPSJib3RoIikKYGBgCgpTaW5jZSB0aGlzIGlzIHRpYmJsZSwgbGV0J3MgY291bnQgc25wcyBwZXIgZ2VuZQoKYGBge3J9Cmh1bWFuX2dlbmVzXzIyICU+JSAKICBnZW5vbWVfaW50ZXJzZWN0KGh1bWFuX3NucF8yMiwgYnk9YygiY2hyb20iLCAic3RhcnQiLCAiZW5kIiksIG1vZGU9ImJvdGgiKSAlPiUgCiAgY291bnQobmFtZS54KQpgYGAKCkxldCdzIG5vcm1hbGl6ZSB0aGlzIHdpdGggZ2VuZSBsZW5ndGgKCmBgYHtyfQpodW1hbl9nZW5lc18yMiAlPiUgCiAgZ2Vub21lX2ludGVyc2VjdChodW1hbl9zbnBfMjIsIGJ5PWMoImNocm9tIiwgInN0YXJ0IiwgImVuZCIpLCBtb2RlPSJib3RoIikgJT4lIAogIGNvdW50KG5hbWUueCkgJT4lIAogIGlubmVyX2pvaW4oaHVtYW5fZ2VuZXNfMjIsIGJ5PWMoIm5hbWUueCI9Im5hbWUiKSkgJT4lIAogIG11dGF0ZShzbnBfcGVyX2tiPW4gLyAoIChlbmQtc3RhcnQpIC8xMDAwKSApICU+JSAKICBhcnJhbmdlKC1zbnBfcGVyX2tiKQpgYGAKCldoYXQgYWJvdXQsIHNucHMgd2l0aGluIHByb21vdGVyIHJlZ2lvbnM/IExldCdzIGdlbmVyYXRlIDEwMDAgYnAgdXBzdHJlYW0gb2YgZ2VuZXMgd2l0aCBkcGx5ciB2ZXJicyBhbmQgdGhlbiBpbnRlcnNlY3QuCgpgYGB7cn0KaHVtYW5fZ2VuZXNfMjIgJT4lIAogIG11dGF0ZShlbmQ9c3RhcnQtMSwgc3RhcnQ9c3RhcnQtMTAwMCkgJT4lIAogIGdlbm9tZV9pbnRlcnNlY3QoaHVtYW5fc25wXzIyLCBieT1jKCJjaHJvbSIsICJzdGFydCIsICJlbmQiKSwgbW9kZT0iYm90aCIpCmBgYAoKSXQgd29ya3MhIEJ1dCB3ZSBtYWRlIGEgYmlnIG1pc3Rha2UsIHRoZSBnZW5lcyBvbiBuZWdhdGl2ZSBzdHJhbmQgc2hvdWxkIGhhdmUgdGhlaXIgdXBzdHJlYW0gcmVnaW9ucyBleHRlbmRlZCB0b3dhcmRzIHJpZ2h0LCBub3QgbGVmdC4gU28sIHdlIGNhbiB1c2UgYGlmZWxzZWAgZnVuY3Rpb24gdG8gZXh0ZW5kIHByb21vdGVyIHJlZ2lvbnMgYWNjb3JkaW5nIHRvIHN0cmFuZCB2YWx1ZS4KCmBgYHtyfQpodW1hbl9nZW5lc18yMiAlPiUgCiAgbXV0YXRlKHBfZW5kPWlmZWxzZShzdHJhbmQ9PSIrIiwgc3RhcnQtMSwgZW5kKzEwMDApLAogICAgICAgICBwX3N0YXJ0PWlmZWxzZShzdHJhbmQ9PSIrIiwgc3RhcnQtMTAwMCwgZW5kKzEpLAogICAgICAgICBzdGFydD1wX3N0YXJ0LAogICAgICAgICBlbmQ9cF9lbmQpICU+JSAKICBnZW5vbWVfaW50ZXJzZWN0KGh1bWFuX3NucF8yMiwgYnk9YygiY2hyb20iLCAic3RhcnQiLCAiZW5kIiksIG1vZGU9ImJvdGgiKQpgYGAKCkFsc28sIG5vdGljZSB0aGF0IHdlIGRpZG4ndCB1c2UgYHN0cmFuZGAgaW4gaW50ZXJzZWN0aW9ucy4gV2UgY2FuIHVzZSB0aGlzIGNvbHVtbiB0byBzZWUgc3RyYW5kIHNwZWNpZmljIGludGVyc2VjdGlvbnMuIFdlIGFscmVhZHkga25vdyB0aGF0IGdlbmVzIGhhdmUgc3RyYW5kIHNwZWNpZmljaXR5LCBhbmR0IHRoZSBzbnAgZGF0YSBjb250YWlucyBzdHJhbmQgaW5mb3JtYXRpb24uIExldCdzIGZpbmQgc25wcyBpbiBwcm9tb3RlciByZWdpb25zIHdoZXJlIHByb21vdGVyIHJlZ2lvbiBzdHJhbmQgYW5kIHNucCBzdHJhbmQgbWF0Y2hlcy4gRm9yIHRoYXQsIHdlIHVzZSBgZmlsdGVyYCB2ZXJiOgoKYGBge3J9Cmh1bWFuX2dlbmVzXzIyICU+JSAKICBtdXRhdGUocF9lbmQ9aWZlbHNlKHN0cmFuZD09IisiLCBzdGFydC0xLCBlbmQrMTAwMCksCiAgICAgICAgIHBfc3RhcnQ9aWZlbHNlKHN0cmFuZD09IisiLCBzdGFydC0xMDAwLCBlbmQrMSksCiAgICAgICAgIHN0YXJ0PXBfc3RhcnQsCiAgICAgICAgIGVuZD1wX2VuZCkgJT4lIAogIGdlbm9tZV9pbnRlcnNlY3QoaHVtYW5fc25wXzIyLCBieT1jKCJjaHJvbSIsICJzdGFydCIsICJlbmQiKSwgbW9kZT0iYm90aCIpICU+JSAKICBmaWx0ZXIoc3RyYW5kLng9PXN0cmFuZC55KQpgYGAKCkFzIHlvdSBjYW4gc2VlLCB5b3UgY2FuIGZlZWQgYGdlbm9tZV9pbnRlcnNlY3RgIHdpdGggYSBjdXN0b20gdGFibGUgYW5kIHlvdSBjYW4gdXNlIGBnZW5vbWVfaW50ZXJzZWN0YCB3aXRoaW4gYSBwaXBlLiBUaGF0J3MgaHVnZSBiZW5lZml0IGFsbG93aW5nIGZsZXhpYmlsaXR5LgoKIyMgdmFsciBwYWNrYWdlCgpUaGUgcGlvbmVlciBpbiBnZW5vbWUgYXJpdGhtZXRpYyBpcyBbYmVkdG9vbHNdKGh0dHA6Ly9iZWR0b29scy5yZWFkdGhlZG9jcy5pby9lbi9sYXRlc3QvKSBzb2Z0d2FyZSB3aGljaCBjYW4gcGVyZm9ybSBhbnkgcG9zc2libGUgZ2Vub21lIGludGVydmFsIG9wZXJhdGlvbi4gUGxlYXNlIGNoZWNrIHRoZSBbdHV0b3JpYWwgcGFnZV0oaHR0cDovL3F1aW5sYW5sYWIub3JnL3R1dG9yaWFscy9iZWR0b29scy9iZWR0b29scy5odG1sKSBmb3IgbnVtZXJvdXMgZXhhbXBsZXMuIAoKVGlkeWdlbm9taWNzIHBhY2thZ2UgaXMgdHJ5aW5nIHRvIGltcGxlbWVudCAqc29tZSogb2YgdGhlICpiYXNpYyogb3BlcmF0aW9ucyBkb25lIGJ5IGBiZWR0b29sc2Agc29mdHdhcmUuIFRvIGRvIG1vcmUgY29tcGxpY2F0ZWQgYW5kIGFkdmFuY2VkIG9wZXJhdGlvbnMgc29tZSBSIHBhY2thZ2VzIGludGVyZmFjZSB3aXRoIHRoZSBgYmVkdG9vbHNgIHNvZnR3YXJlIGluc3RhbGxlZCBsb2NhbGx5LiBCdXQgYHZhbHJgIHBhY2thZ2UgcGVyZm9ybXMgKm1vc3QqIG9mIHRoZSBgYmVkdG9vbHNgIG9wZXJhdGlvbnMgbmF0aXZlbHkuIFBsZWFzZSB2aXNpdCBpdHMgW21haW4gcGFnZV0oaHR0cHM6Ly9ybmFiaW9jby5naXRodWIuaW8vdmFsci8pIGZvciBtb3JlIGluZm8uCgpgYGB7cn0KI1BMRUFTRSBETyBOT1QgUlVOLCBUSElTIElTIEpVU1QgQU4gRVhBTVBMRQoKIyBkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoInJuYWJpb2NvL3ZhbHJkYXRhIikgICAgICNpbnN0YWxscyB2YWxyIGFuZCB2YWxyZGF0YQpsaWJyYXJ5KHZhbHIpCiMgdmFscmRhdGE6OmRuYXNlX2RhdGEgJT4lIGJpbmRfcm93cyguaWQ9ImlkIikKdmFscmRhdGE6OnJlcGVhdF9kYXRhCnZhbHJkYXRhOjpoZzE5X2dlbmVzICAjIHNpbWlsYXIgdG8gaHVtYW5fZ2VuZXMgZGF0YXNldCB3ZSBoYXZlIHVzZWQgaW4gcGFzdApgYGAKCkFuIGV4dHJlbWUgZXhhbXBsZSwgZnJvbSBgdmFscmAgW2V4YW1wbGVzIHBhZ2VdKGh0dHBzOi8vZ2l0aHViLmNvbS9ybmFiaW9jby92YWxyZGF0YS9ibG9iL21hc3Rlci9pbnN0L3ZhbHItZXhhbXBsZXMvY29kZV9kZW1vcy5yKS4KCmBgYHtyIGV2YWw9RkFMU0V9CiMgUExFQVNFIERPIE5PVCBSVU4sIFRISVMgSVMgQU4gRVhBTVBMRQpkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoInJuYWJpb2NvL3ZhbHJkYXRhIikKbGlicmFyeSh2YWxyKQpyZXMgPC0gdmFscmRhdGE6OmRuYXNlX2RhdGEgJT4lCiAgY3Jvc3MyKC4sLikgJT4lCiAgbWFwKGxpZnQoYmVkX2phY2NhcmQpKSAlPiUKICBtYXAoJ2phY2NhcmQnKSAlPiUKICBmbGF0dGVuX2RibCgpICU+JQogIG1hdHJpeChucm93ID0gMjAsIG5jb2wgPSAyMCkKYGBgCgpUaGUgb3V0cHV0IGlzIDIweDIwIG1hdHJpeCBoYXZpbmcgamFjY2FyZCBkaXN0YW5jZXMgb2YgMjAgZGlmZmVyZW50IGRhdGEuIEJlbG93IGlzIHRydW5jYXRlZCBwb3J0aW9uIG9mIHRoZSByZXN1bHQuCgpgYGAKICAgICAgICAgICBbLDFdICAgICAgWywyXSAgICAgIFssM10gICAgICBbLDRdICAgICAgWyw1XSAgICAgIFssNl0gICAgICBbLDddICAgICAgWyw4XSAgICAgIFssOV0gICAgIFssMTBdIAogWzEsXSAxLjAwMDAwMDAgMC4zNTQzNDEzIDAuMjQwNTQyOSAwLjIyMDA1NjUgMC4yMjcyMzQ1IDAuMjA4NDczNCAwLjIwNjEwNDQgMC4yMDE2MTE3IDAuMjE1MjM3MyAwLjE5MDY4NDAgCiBbMixdIDAuMzU0MzQxMyAxLjAwMDAwMDAgMC4yMzE1NzE3IDAuMjA5ODEzNCAwLjIyNjExMDcgMC4xOTQ5OTI2IDAuMTc4NzYwNCAwLjE4MzY0MDggMC4xOTE5NTY4IDAuMTc1MDg1MCAKIFszLF0gMC4yNDA1NDI5IDAuMjMxNTcxNyAxLjAwMDAwMDAgMC41MDE0NTYxIDAuNjEyNTAyNSAwLjI3NDk1NzkgMC4yNTIyMjMzIDAuMjU5ODYxOSAwLjI3OTU2MDcgMC4yNDczMjA3IAogWzQsXSAwLjIyMDA1NjUgMC4yMDk4MTM0IDAuNTAxNDU2MSAxLjAwMDAwMDAgMC41MDYzNjk1IDAuMjUzODQzNCAwLjIzNDY4MzIgMC4yMzY1MDUzIDAuMjYyMTcxOSAwLjIyMDg2NjIgCiBbNSxdIDAuMjI3MjM0NSAwLjIyNjExMDcgMC42MTI1MDI1IDAuNTA2MzY5NSAxLjAwMDAwMDAgMC4yNzAyNzI2IDAuMjI2NDQwOCAwLjI0ODc4NjcgMC4yNjIxOTYyIDAuMjM1MDI5OSAKIFs2LF0gMC4yMDg0NzM0IDAuMTk0OTkyNiAwLjI3NDk1NzkgMC4yNTM4NDM0IDAuMjcwMjcyNiAxLjAwMDAwMDAgMC40NTE1Mzc1IDAuNjA3MzcwOCAwLjU3NjEzMDMgMC41NTQ3ODgxIAogWzcsXSAwLjIwNjEwNDQgMC4xNzg3NjA0IDAuMjUyMjIzMyAwLjIzNDY4MzIgMC4yMjY0NDA4IDAuNDUxNTM3NSAxLjAwMDAwMDAgMC40ODE4NDA4IDAuNTIzODUzMCAwLjQ4MDg2ODEgCiBbOCxdIDAuMjAxNjExNyAwLjE4MzY0MDggMC4yNTk4NjE5IDAuMjM2NTA1MyAwLjI0ODc4NjcgMC42MDczNzA4IDAuNDgxODQwOCAxLjAwMDAwMDAgMC41OTg2NDk5IDAuNTY5NzMyNiAKIFs5LF0gMC4yMTUyMzczIDAuMTkxOTU2OCAwLjI3OTU2MDcgMC4yNjIxNzE5IDAuMjYyMTk2MiAwLjU3NjEzMDMgMC41MjM4NTMwIDAuNTk4NjQ5OSAxLjAwMDAwMDAgMC41Mzg0NDI0IApbMTAsXSAwLjE5MDY4NDAgMC4xNzUwODUwIDAuMjQ3MzIwNyAwLjIyMDg2NjIgMC4yMzUwMjk5IDAuNTU0Nzg4MSAwLjQ4MDg2ODEgMC41Njk3MzI2IDAuNTM4NDQyNCAxLjAwMDAwMDAgCi4uLnRydW5jYXRlZC4uLgpgYGAKCj4gQXV0aG9ycyBvZiBgdmFscmAgcGFja2FnZSBhbHNvIHB1dCBkb3duIGEgbGVjdHVyZSB0aXRsZWQgKiJQcmFjdGljYWwgQmlvbG9naWNhbCBEYXRhIEFuYWx5c2lzIHdpdGggUi9SU3R1ZGlvIiogcGxlYXNlIGNoZWNrIHRoZSBbbGVjdHVyZSBjb250ZW50c10oaHR0cHM6Ly9ybmFiaW9jby5naXRodWIuaW8vZWRhLykgdG8gaGF2ZSBpZGVhcyBhYm91dCBiaW9sb2dpY2FsIGRhdGEgYW5hbHlzaXMuCgoqKioKCiMgQmlvYnJvb20gcGFja2FnZQoKVGhpcyBwYWNrYWdlIGlzIGFibGUgY29udmVydCBtYW55IGRpZmZlcmVudCBiaW9sb2dpY2FsIG9iamVjdHMgaW50byB0aWR5IGZvcm1hdC4gUGxlYXNlIHZpc2l0IGl0cyBbaGVscCBwYWdlXShodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvcmVsZWFzZS9iaW9jL3ZpZ25ldHRlcy9iaW9icm9vbS9pbnN0L2RvYy9iaW9icm9vbV92aWduZXR0ZS5odG1sKSBmb3IgbW9yZSBpbmZvcm1hdGlvbiwgc29tZSBvZiB0aGUgZXhhbXBsZXMgYXJlIHJ1biBiZWxvdy4KCiMjIExpbW1hIGV4YW1wbGUKCmBgYHtyIHdhcm5pbmc9RkFMU0UsIGVycm9yPUZBTFNFfQpsaWJyYXJ5KGxpbW1hKSAgCmBgYAoKYGBge3J9CnNldC5zZWVkKDIwMTQpCmRhdGEgPC0gbWF0cml4KHJub3JtKDEwMDApLCBuY29sPTQpCmRhdGFbLCAxOjJdIDwtIGRhdGFbLCAxOjJdICsgLjUgICMgYWRkIGFuIGVmZmVjdApyb3duYW1lcyhkYXRhKSA8LSBwYXN0ZTAoImdlbmUiLCAxOm5yb3coZGF0YSkpCmRlcyA8LSBkYXRhLmZyYW1lKHRyZWF0bWVudCA9IGMoImEiLCAiYSIsICJiIiwgImIiKSxjb25mb3VuZGluZyA9IHJub3JtKDQpKQpgYGAKCgpgYGB7cn0KbGZpdCA8LSBsbUZpdChkYXRhLCBtb2RlbC5tYXRyaXgofiB0cmVhdG1lbnQgKyBjb25mb3VuZGluZywgZGVzKSkKZWIgPC0gZUJheWVzKGxmaXQpCmhlYWQodGlkeShsZml0KSkKaGVhZCh0aWR5KGViKSkKYGBgCgpgYGB7ciBldmFsPUZBTFNFfQogIyB0aGUgdGlkaWVkIGZvcm0gcHV0cyBpdCBpbiBhbiBpZGVhbCBmb3JtIGZvciBwbG90dGluZwogICAgICAgIGdncGxvdCh0aWR5KGxmaXQpLCBhZXMoZXN0aW1hdGUpKSArIGdlb21faGlzdG9ncmFtKGJpbndpZHRoPTEpICsKICAgICAgICAgICAgZmFjZXRfd3JhcCh+IHRlcm0pCiAgICAgICAgZ2dwbG90KHRpZHkoZWIpLCBhZXMocC52YWx1ZSkpICsgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGg9LjIpICsKICAgICAgICAgICAgZmFjZXRfd3JhcCh+IHRlcm0pCmBgYAoKCiMjIEV4cHJlc3Npb24gU2V0IEV4YW1wbGUKCmBgYHtyIHdhcm5pbmc9RkFMU0UsIGVycm9yPUZBTFNFfQpsaWJyYXJ5KEJpb2Jhc2UpCmhhbW1lcgpgYGAKCkNoZWNrIG91dCB0aGUgdGlkaWVkIHZlcnNpb24gb2YgdGhlIGRhdGEuCgpgYGB7cn0KaGVhZCh0aWR5KGhhbW1lcikpCmBgYAoKYGBge3J9CmhlYWQodGlkeShoYW1tZXIsIGFkZFBoZW5vID0gVFJVRSkpCmBgYAoKIyMgREVTZXEyIGV4YW1wbGUKCmBgYHtyIHdhcm5pbmc9RkFMU0UsIGVycm9yPUZBTFNFfQojIHNvdXJjZSgiaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL2Jpb2NMaXRlLlIiKQojIGJpb2NMaXRlKCJhaXJ3YXkiKQpgYGAKCmBgYHtyIHdhcm5pbmc9RkFMU0UsIGVycm9yPUZBTFNFfQpsaWJyYXJ5KERFU2VxMikKbGlicmFyeShhaXJ3YXkpCmBgYAoKYGBge3J9CmRhdGEoYWlyd2F5KQphaXJ3YXlfc2UgPC0gYWlyd2F5CmBgYAoKYGBge3J9CmFpcndheV9kZHMgPC0gREVTZXFEYXRhU2V0KGFpcndheV9zZSwgZGVzaWduID0gfmNlbGwgKyBkZXgpCgpjb2xEYXRhKGFpcndheSkgJT4lIGhlYWQoKQpgYGAKCmBgYHtyfQpyb3dSYW5nZXMoYWlyd2F5KSAlPiUgaGVhZCgpCmBgYAoKYGBge3J9CmFzc2F5KGFpcndheSkgJT4lIGhlYWQoKQpgYGAKCmBgYHtyfQp0aWR5KGFpcndheV9kZHMpICU+JSAgaGVhZCgpCmBgYAoKYGBge3J9CiMgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYW5hbHlzaXMKZGVzZXEgPC0gREVTZXEoYWlyd2F5X2RkcykKcmVzdWx0cyA8LSByZXN1bHRzKGRlc2VxKQojIHRpZHkgcmVzdWx0cwp0aWR5KHJlc3VsdHMpICU+JSBoZWFkKCkKYGBgCgojIEFubm91bmNlbWVudAoKVm9sdW50ZWVyIHN0dWRlbnRzIGFyZSB3YW50ZWQsIHdobyB3b3VsZCBoZWxwIGdhdGhlcmluZyBsZWN0dXJlIG1hdGVyaWFscyBpbnRvIGJvb2sgZm9ybWF0IHZpYSBib29rZG93biAKCioqKgoKTGFzdCBVcGRhdGVkIGByIFN5cy5EYXRlKClgCg==