Introduction

NOTE: The context of this R notebook includes all the R code required for the analysis of Differential splicing analysis of bulk RNA-Seq data. Refer to the PART 1- Bulk RNA-Seq analysis of the Protocol section.

Installation of tools and R packages used in the analysis-

Conda is a popular and flexible package manager that allows convenient installation of packages with their dependencies across all platforms. Use ‘Anaconda’ (conda package manager) to install ‘conda’ which can be used to install the tools/packages required for the analysis.

Download ‘Anaconda’ according to the system requirements from https://www.anaconda.com/products/individual#Downloads and install it by following the prompts in graphical installer. Install all the packages using ‘conda’ by typing the following commands on linux command-line.

conda install -c daler sratoolkit  
conda install -c conda-forge parallel 
conda install -c bioconda star fastqc rmats rmats2sashimiplot   

To download all the R packages used in the protocol, type the following code in R console or R-studio.

bioc_packages <- c("DEXSeq", "Rsubread", "EnhancedVolcano", "rtracklayer", "edgeR", "limma", "maser")  
packages <- c("magrittr", "tidyverse","openxlsx", "BiocManager", "GenomicRanges") 

#Install if not already installed  
installed_packages <- packages %in% rownames(installed.packages())  
installed_bioc_packages <- bioc_packages %in% rownames(installed.packages())  
if (any(installed_packages == FALSE)) {  
  install.packages(packages[!installed_packages],dependencies = TRUE)  
  BiocManager::install(packages[!installed_bioc_packages], dependencies = TRUE)
} 

In this computational protocol, commands will be given as either R Notebook files (files with extension “.Rmd”), R code files (files with extension “.R”), or Linux Bash shell scripts (files with extension “.sh”). R Notebook (Rmd) files should be opened in RStudio using File|Open File…, and individual code chunks (which may be R commands or Bash shell commands) then run interactively by clicking the green arrow at the upper right. R code files can be run by opening in RStudio, or on the Linux command-line by prefacing with “Rscript”, e.g. Rscript example.R. Shell scripts are run on the Linux command-line by prefacing the script with the “sh” command e.g. sh example.sh.

1. Data downloading and pre-processing

The code snippets annotated below are available in the supplementary code file “Downloading_data_preprocessing.Rmd”, to follow the individual steps interactively, and are also provided as a bash script to be run in batch on the Linux command-line (sh downloading_data_preprocessing.sh).

1.1 Downloading the raw data.

Download the raw data from Sequence Read Archive [SRA] using the ‘prefetch’ command from SRA toolkit. Give the SRA Accession Ids in sequence in the following command to download them in parallel using GNU parallel using GNU parallel utility. Ensure SRA toolkit and GNU parallel is installed before proceeding this step. To download SRA files of accession ids from SRR10261601 to SRR10261606 in parallel, type the following commands on the linux command-line-

seq 10261601 10261606 | parallel prefetch SRR{} 

1.1.2 Extract the fastq files.

Extract the fastq files from the archive using ‘fastq-dump’ function of SRA toolkit. Use GNU parallel and give the names of all SRA files together.

parallel -j 3 fastq-dump --gzip --skip-technical --read-filter pass --dumpbase --split-e --clip --origfmt {} ::: <name of sra files together> 

1.1.3 Download reference genome and annotation

Download the reference genome and annotations for Mouse (Genome assembly GRCm39) from www.ensembl.org using the following at the Linux command-line. The ‘wget’ function downloads the destination file in the provided link and ‘gunzip’ will decompress the file. Store both genome and annotation file in specific variables for further use.

wget -nv -O annotation.gtf.gz http://ftp.ensembl.org/pub/release-103/gtf/mus_musculus/Mus_musculus.GRCm39.103.gtf.gz \ && gunzip -f annotation.gtf.gz 

wget -nv -O genome.fa.gz http://ftp.ensembl.org/pub/release-103/fasta/mus_musculus/dna/Mus_musculus.GRCm39.dna.primary_assembly.fa.gz \ && gunzip -f genome.fa.gz 
GTF=$(readlink -f annotation.gtf) 
GENOME=$(readlink -f genome.fa) 

1.2 Pre-processing

1.2.1 Quality Control

Assess the quality of raw reads with FASTQC (v0.11.9). Create an output folder and run fastqc with parallel on multiple input fastq files. Ensure the path of raw input files is correct before running the command. This step will generate quality report for each sample. Examine the reports to ensure the quality of reads is acceptable before doing further analysis.(Refer to the user manual for understanding the reports at https://www.bioinformatics.babraham.ac.uk/projects/fastqc/)

mkdir fastqc_out 
parallel "fastqc {} -o fastqc_out" ::: $RAW_DATA/*.fastq.gz 

NOTE: If necessary, perform adapter trimming with ‘cutadapt’ or ‘trimmomatic’ to remove sequencing into flanking adapters, which varies based on RNA fragment size and read length. In this analysis we skipped this step as the fraction of reads affected was minimal.

1.2.2 Read alignment

The next step in pre-processing includes mapping the reads to the reference genome. Firstly, build the index for the reference genome using ‘genomeGenerate’ function of STAR (Spliced Transcripts Alignment to a Reference v2.7.5c) and then align the raw reads to the reference (alternatively prebuilt indexes are available on the STAR website and can be used directly for read alignment).

Run the following commands as a bash script on the linux command-line.

#Build STAR index 
GDIR=STAR_indices 
mkdir $GDIR 
STAR --runMode genomeGenerate --genomeFastaFiles $GENOME --sjdbGTFfile $GTF --runThreadN 8 --genomeDir $GDIR 
ODIR=results/mapping 
mkdir -p $ODIR
#Align reads to the genome 
for fq1 in $RAW_DATA/*R1.fastq.gz;  
do 
fq2=$(echo $fq1 | sed 's/1.fastq.gz/2.fastq.gz/g'); 
OUTPUT=$(basename ${fq1}| sed 's/R1.fastq.gz//g'); 
STAR --genomeDir $GDIR \ 
 --runThreadN 12 \ 
 --readFilesCommand zcat \ 
 --readFilesIn  ${fq1} ${fq2}\ 
 --outFileNamePrefix $ODIR\/${OUTPUT} \ 
 --outSAMtype BAM SortedByCoordinate \ 
 --outSAMunmapped Within \ 
 --outSAMattributes Standard 
Done 

STAR aligner will generate and sort BAM files for each sample after read alignments. Bam files must be sorted before proceeding to further steps. Create a folder ‘bams’ and copy all the bam files to it.

2. Preparing Exon annotations

Run the supplementary file- “prepare_mm_exon_annotation.R” with the downloaded annotation in GTF format on the linux command-line to prepare the annotations. Save the given code as ‘prepare_mm_exon_annotation.R’ file and run it on the linux command-line as-

Rscript prepare_mm_exon_annotation.R annotation.gtf

Or run it on RStudio as:

#! /usr/bin/env Rscript
#To run- Rscript prepare_mm_annotation.R <GTF_file>
library(rtracklayer)
library(tidyverse)
args = commandArgs(trailingOnly = TRUE)
gff_mm <- args[1]
mgg <- import(gff_mm)

anno = mgg %>% data.frame %>% 
  mutate(seqnames = as.character(seqnames)) %>% 
  dplyr::filter(!grepl("chr[1-19]|X|Y", seqnames)) %>% 
  filter(type %in% "exon") %>% 
  select(seqnames, start,   end ,width, strand, gene_name,  transcript_id)  %>% distinct() %>%
  group_by(gene_name, seqnames, start, end, width, strand) %>% 
  summarise(transcript_ids = paste(transcript_id, sep="",collapse=",")) %>% 
  ungroup()  %>%  
  mutate(number = 1) %>% 
  arrange( seqnames,   start ,    end) %>%  group_by(gene_name, seqnames) %>% #
  mutate(ticker = cumsum(number)) %>% 
  mutate(ExonID = paste(gene_name, ".",seqnames,".", ticker, sep="")) %>%
  dplyr::select(-number, ticker) %>%
  add_count(gene_name) %>%
  data.frame 
colnames(anno) <- c("GeneID", "Chr", "Start", "End", "Width", "Strand", "TranscriptIDs", "Ticker", "ExonID", "n")
save(anno, file=paste("mm_exon_anno.RData", sep=""))

The GTF file contains multiple exon entries for different isoforms. This file is used to ‘collapse’ the multiple transcript IDs for each exon using a comma. It is an important step to define exon counting bins.

3 Counting Reads

The next step is to count the number of reads mapped to different transcripts/exons. Use RStudio to run the following commands or call R from command-line by typing ‘R’.

3.1 Load required libraries-

The following packages are required for this part of the analysis. Make sure to install the packages if they are not already installed.

packages <- c("DEXSeq","Rsubread","tidyverse", "magrittr","EnhancedVolcano", "edgeR","openxlsx")

#Load libraries
invisible(lapply(packages, library, character.only = TRUE))

3.2 Load the processed annotation file-

load("mm_exon_anno.RData")

3.3 Read the bam files obtained in step 1.2.2(after mapping) as input for ‘featureCounts’.

Read the folder containing bam files by first listing each file from the directory that ends with .bam. Use ‘featureCounts’ from the Rsubread package which takes bam files and processed GTF annotation (reference) as input to generate a matrix of counts associated with each feature with rows representing exons(features) and columns representing samples. Before running this step, make sure you have the folder containing bam files in the current working directory. Next, perform non-specific filtering to remove lowly expressed exons. Transform the data from raw scale to counts per million (cpm) using the cpm function from ‘edgeR’ package and keep the counts greater than one in at least three samples. Remove the genes with only one exon.

# Read all bam files as input for featureCounts
countData <- dir("bams", pattern=".bam$", full.names=T) %>%
  featureCounts(filesToCount,
                annot.ext=anno,
                isGTFAnnotationFile=FALSE,
                minMQS=0, useMetaFeatures=FALSE, 
                allowMultiOverlap=TRUE, 
                largestOverlap = TRUE, 
                countMultiMappingReads=FALSE, 
                primaryOnly=TRUE, 
                isPairedEnd=TRUE, 
                nthreads = 12)

# Non-specific filtering: Remove the exons with low counts 
isexpr <- rownames(countData$counts)[rowSums(cpm(countData$counts) > 1) >= 3] 
countData$counts <- countData$counts[rownames(countData$counts) %in% isexpr, ] 
anno <- anno %>% filter(GeneID %in% rownames(countData$counts)) 

# Remove genes with only 1 site and  NA in geneIDs
dn <- anno %>% group_by(GeneID) %>% summarise(nsites=n()) %>% filter(nsites > 1 & !is.na(GeneID))
anno <- anno %>% filter(GeneID %in% dn$GeneID)
countData$counts <- countData$counts[rownames(countData$counts) %in% anno$GeneID, ]

NOTE: Examine the library type(single-end or paired-end) and modify the parameters in featureCounts respectively.

4. Differential Splicing and Exon usage analysis

We describe two alternatives for this step: DEXSeq and DiffSplice. Either can be used and give similar results. For consistency, select DEXSeq if you prefer a DESeq2 package for DGE and use DiffSplice for a Limma-based DGE package.

4.1 Using DEXSeq pipeline for differential exon analysis

4.1.1 Create a sample table to define the experimental design

sampleTable = data.frame(
   row.names = c( "Mbnl1KO_Thymus_1", "Mbnl1KO_Thymus_2", "Mbnl1KO_Thymus_3", 
                 "WT_Thymus_1", "WT_Thymus_2", "WT_Thymus_3" ),
   condition = rep(c("Mbnl1_KO", "WT"),c(3,3)), libType = rep(c("paired-end")))

NOTE: The row names should be consistent with the bam file names used by featureCounts to count the reads. sampleTable consists of details of each sample which includes- library-type and condition. This is required to define the contrasts or test group for detecting differential usage.

4.1.2 Prepare the exon information file

Exon information in form of GRanges (genomic ranges) object is required as an input to create DEXSeq object. Match the gene Ids with the read counts and to create exoninfo object.

exoninfo = anno[anno$GeneID %in% rownames(countData$counts),]
exoninfo <- GRanges (seqnames=  anno$Chr, 
ranges= IRanges (start=anno$Start, end=anno$End, width = anno$Width), strand = Rle(anno$Strand))
mcols(exoninfo)$TranscriptIDs <- anno$TranscriptIDs
mcols(exoninfo)$Ticker <- anno$Ticker
mcols(exoninfo)$ExonID <- anno$ExonID
mcols(exoninfo)$n <- anno$n
mcols(exoninfo)$GeneID <- anno$GeneID

transcripts_l = strsplit(exoninfo$TranscriptIDs,  "\\,")

#Save the countData, sampleTable and exoninfo and transcripts in one object for further use 
save(countData, sampleTable, exoninfo, transcripts_l, file="AS_countdata.RData")

NOTE: Be careful with the names of the columns of the processed annotation file. With a different GTF file, the headers can change and cause a problem while running this step.

4.1.3 Create DEXSeq object

Create DEXSeq object using DEXSeqDataSet function. The DEXSeq object collects together read counts, exon feature information, and sample information. Use the read counts generated in step 3 and the exon information obtained from the previous step to create the DEXSeq object from the count matrix. The sampleData argument takes a data frame input defining the samples (and their attributes: library type and condition), ‘design’ uses sampleData to generate a design matrix for the differential testing using model formula notation. Note that a significant interaction term, condition:exon, indicates that the fraction of reads over a gene falling on a particular exon depends on the experimental condition i.e. there is AS. See DEXSeq documentation for a full description of setting the model formula for more complex experimental designs. For feature information- exon ids, corresponding gene and transcripts are required from the processed annotation file.

# Explore/Inspect the DEXSeq object
head(counts(dxd), 5)
                  [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11]
Mrpl15:Mrpl15.1.1   96  121   70   79   90   96 3399 4376 3223  3149  3340
Mrpl15:Mrpl15.1.2  697  862  636  616  699  657 2798 3635 2657  2612  2731
Mrpl15:Mrpl15.1.3   11    8    8   10    5   17 3484 4489 3285  3218  3425
Mrpl15:Mrpl15.1.4  498  642  498  434  516  465 2997 3855 2795  2794  2914
Mrpl15:Mrpl15.1.5  194  243  185  159  189  175 3301 4254 3108  3069  3241
                  [,12]
Mrpl15:Mrpl15.1.1  3080
Mrpl15:Mrpl15.1.2  2519
Mrpl15:Mrpl15.1.3  3159
Mrpl15:Mrpl15.1.4  2711
Mrpl15:Mrpl15.1.5  3001
colData(dxd)
DataFrame with 12 rows and 4 columns
              sample condition    libType     exon
            <factor>  <factor>   <factor> <factor>
1   Mbnl1KO_Thymus_1  Mbnl1_KO paired-end     this
2   Mbnl1KO_Thymus_2  Mbnl1_KO paired-end     this
3   Mbnl1KO_Thymus_3  Mbnl1_KO paired-end     this
4        WT_Thymus_1        WT paired-end     this
5        WT_Thymus_2        WT paired-end     this
...              ...       ...        ...      ...
8   Mbnl1KO_Thymus_2  Mbnl1_KO paired-end   others
9   Mbnl1KO_Thymus_3  Mbnl1_KO paired-end   others
10       WT_Thymus_1        WT paired-end   others
11       WT_Thymus_2        WT paired-end   others
12       WT_Thymus_3        WT paired-end   others
split(seq_len(ncol(dxd)), colData(dxd)$exon)
$this
[1] 1 2 3 4 5 6

$others
[1]  7  8  9 10 11 12
head(rowRanges(dxd),3)
GRanges object with 3 ranges and 10 metadata columns:
                    seqnames          ranges strand |      TranscriptIDs
                       <Rle>       <IRanges>  <Rle> |        <character>
  Mrpl15:Mrpl15.1.1        1 4843429-4844739      - | ENSMUST00000130201
  Mrpl15:Mrpl15.1.2        1 4843434-4847024      - | ENSMUST00000156816
  Mrpl15:Mrpl15.1.3        1 4844659-4844739      - | ENSMUST00000045689
                       Ticker      ExonID         n      GeneID   featureID
                    <numeric> <character> <integer> <character> <character>
  Mrpl15:Mrpl15.1.1         1  Mrpl15.1.1        15      Mrpl15  Mrpl15.1.1
  Mrpl15:Mrpl15.1.2         2  Mrpl15.1.2        15      Mrpl15  Mrpl15.1.2
  Mrpl15:Mrpl15.1.3         3  Mrpl15.1.3        15      Mrpl15  Mrpl15.1.3
                        groupID     exonBaseMean      exonBaseVar
                    <character>        <numeric>        <numeric>
  Mrpl15:Mrpl15.1.1      Mrpl15               92              306
  Mrpl15:Mrpl15.1.2      Mrpl15            694.5           7814.7
  Mrpl15:Mrpl15.1.3      Mrpl15 9.83333333333333 16.5666666666667
                           transcripts
                                <list>
  Mrpl15:Mrpl15.1.1 ENSMUST00000130201
  Mrpl15:Mrpl15.1.2 ENSMUST00000156816
  Mrpl15:Mrpl15.1.3 ENSMUST00000045689
  -------
  seqinfo: 21 sequences from an unspecified genome; no seqlengths
sampleAnnotation(dxd)
DataFrame with 6 rows and 3 columns
            sample condition    libType
          <factor>  <factor>   <factor>
1 Mbnl1KO_Thymus_1  Mbnl1_KO paired-end
2 Mbnl1KO_Thymus_2  Mbnl1_KO paired-end
3 Mbnl1KO_Thymus_3  Mbnl1_KO paired-end
4      WT_Thymus_1        WT paired-end
5      WT_Thymus_2        WT paired-end
6      WT_Thymus_3        WT paired-end

NOTE: This step might take some time. Once the object is created, explore the columns for initial understanding/exploration of the data.

# Explore/Inspect the DEXSeq object
head(counts(dxd), 5)
colData(dxd)
split(seq_len(ncol(dxd)), colData(dxd)$exon)
head(rowRanges(dxd),3)
sampleAnnotation(dxd)

4.1.4 Normalization and Dispersion Estimation

Next, perform normalization between samples and estimate the variance of the data, due to both Poisson count noise from the discrete nature of RNA-seq and biological variability, using the following commands.

dxd %<>% estimateSizeFactors %>% estimateDispersions %T>% plotDispEsts 

4.1.5 Testing for Differential exon Usage

After the estimation of variation, test for differential exon usage for each gene and generate the results. The results are calculated at FDR 10%.

plotDEXSeq(dxr, "Wnk1", legend=TRUE, cex.axis=1.2, cex=1.3, lwd=2, FDR = 0.1)

plotDEXSeq(dxr, "Tcf7", legend=TRUE, cex.axis=1.2, cex=1.3, lwd=2)

plotDEXSeq(dxr, "Mbnl1", legend=TRUE, cex.axis=1.2, cex=1.3, lwd=2)

plotDEXSeq(dxr, "Lef1", legend=TRUE, cex.axis=1.2, cex=1.3, lwd=2)

plotDEXSeq(dxr, "Ncor2", legend=TRUE, cex.axis=1.2, cex=1.3, lwd=2)

plotDEXSeq(dxr, "Mbnl2", legend=TRUE, cex.axis=1.2, cex=1.3, lwd=2)

4.1.6 Visualization of splicing events

Visualize splicing events for selected genes using the following command

plotDEXSeq(dxr, "Lef1", expression=FALSE, splicing=TRUE, legend=TRUE, cex.axis=1.2, cex=1.3, lwd=2)

plotDEXSeq(dxr, "Mbnl1", expression=FALSE, splicing=TRUE, legend=TRUE, cex.axis=1.2, cex=1.3, lwd=2)

To display transcripts use displayTranscripts=TRUE

plotDEXSeq(dxr, "Rps3a1", displayTranscripts=TRUE, legend=TRUE, cex.axis=1.2, cex=1.3, lwd=2)
plotDEXSeq(dxr, "Lef1", expression=FALSE, splicing=TRUE, legend=TRUE, cex.axis=1.2, cex=1.3, lwd=2)
plotDEXSeq(dxr, "Mbnl1", expression=FALSE, splicing=TRUE, legend=TRUE, cex.axis=1.2, cex=1.3, lwd=2)

Generate plots for all the significant genes

save.image("Mbnl1_KO_Dexseq.RData")

Volcano plot to visualize differentially expressed genes

EnhancedVolcano(dexon_sig, lab = dexon_sig$featureID, x = 'log2fold_WT_Mbnl1_KO', y = 'pvalue', title = 'Volcano Plot', subtitle = 'Mbnl1_KO vs WT (DEXSeq)', FCcutoff = 1, labSize = 4,legendPosition = "right",xlim= c(-4,4),caption = bquote(~Log[2]~ "Fold change cutoff, 2; FDR 10%"))

NOTE: EnhancedVolcano package was used to generate the above plot, install the package it if not already installed.

Save Rdata objects

save.image("Mbnl1_KO_Dexseq.RData")

4.2 Using Limma diffSplice for differential splicing analysis

Follow the R Notebook file “AS_analysis_RNASeq.Rmd”. Ensure steps 1-3 have been followed to prepare input files before proceeding further.

4.2.1 Load libraries

library(limma)
library(edgeR)

4.2.2 Non-specific Filtering-

Extract the matrix of read counts obtained in 3. Create a list of features using ‘DGEList’ function from edgeR package, where rows represent genes and columns represent samples. NOTE: As a non-specific filtering step, counts are filtered by cpm < 1 in x out of n samples, where x is the minimum number of replicates in any condition. n = 6 and x = 3 for this example data.

mycounts = countData$counts
#Change the rownames of the countdata to exon Ids instead of genes for unique rownames.
rownames(mycounts) = exoninfo$ExonID
dge <- DGEList(counts=mycounts)

#Non-specific Filtering
isexpr <- rowSums(cpm(dge) > 1) >=3
dge <- dge[isexpr,,keep.lib.sizes=FALSE]

#Extract the annotations for only filtered counts 
exoninfo = anno %>% filter(ExonID %in% rownames(dge$counts)) 

#Convert the exoninfo into GRanges object
exoninfo1 <- GRanges (seqnames=  exoninfo$Chr, 
ranges= IRanges (start=exoninfo$Start, end=exoninfo$End, width = exoninfo$Width), strand = Rle(exoninfo$Strand))
mcols(exoninfo1)$TranscriptIDs <- exoninfo$TranscriptIDs
mcols(exoninfo1)$Ticker <- exoninfo$Ticker
mcols(exoninfo1)$ExonID <- exoninfo$ExonID
mcols(exoninfo1)$n <- exoninfo$n
mcols(exoninfo1)$GeneID <- exoninfo$GeneID
transcripts_l = strsplit(exoninfo1$TranscriptIDs,  "\\,")

4.2.3 Normalize the counts-

Normalize the counts across samples, with ‘calcNormFactors’ function from ‘edgeR’ package using Trimmed Mean of M values (TMM normalization method). It will compute scaling factors to adjust library sizes.

dge <- calcNormFactors(dge)

4.2.4 Create design matrix for comparisons

Use sampleTable as generated in step 4.1.1 and create the design matrix. The design matrix characterizes the design. See the Limma User Guide [ref] chapters 8 & 9 for details on design matrices for more advanced experimental designs.

Treat <- factor(sampleTable$condition)
design <- model.matrix(~0+Treat)
colnames(design) <- levels(Treat)

4.2.5 Differential expression testing

Run ‘voom’ function of ‘limma’ package to process RNA-seq data to estimate variance and generate precision weights to correct for Poisson count noise, and the exon-level counts to log2-counts per million (logCPM). Then run linear modelling using ‘lmfit’ function to fit linear models to the expression data for each exon. Compute empirical Bayes statistics for fitted model using ‘eBayes’ function to detect differential exon expression by ranking the exons. Next, define a contrast matrix for the experimental comparisons of interest. Use ‘contrasts.fit’ to obtain coefficients and standard errors for each pair of comparison.

v <- voom(dge, design, plot=FALSE)
fit <- lmFit(v,design)
fit  <- eBayes(fit)
colnames(fit)
summary(decideTests(fit))

cont.matrix <- makeContrasts(
                Mbnl1_KO_WT = Mbnl1_KO - WT, 
                levels=design)
fit2  <- contrasts.fit(fit, cont.matrix)
summary(decideTests(fit2))

4.2.6 Differential Splicing analysis

Run ‘diffSplice’ on the fitted model to test the differences in exon usage of genes between wild-type and knockout and explore the top ranked results using ‘topSplice’ fuinction.: test=”t” gives a ranking of AS exons, test=”simes” gives a ranking of genes. Run diffSplice on the fitted model to test the differences in exon retention between the wild-type and knockout samples.

ex <- diffSplice(fit2, geneid = exoninfo1$GeneID, exonid = exoninfo1$ExonID)
#Check the top splicing results with topSplice
topSplice(ex)
#Exon-level statistics for splicing activity 
ts <-topSplice(ex, n=Inf, FDR=0.1, test= "t", sort.by = "logFC")
#Gene-level statistics for splicing activity
tg <- topSplice(ex, n=Inf, FDR=0.1, test = "simes")
ts %$% GeneID %>% unique %>% length
ts %$% ExonID %>% unique %>% length

NOTE: This step uses the exon information object created in 4.1.2. Make sure it is loaded in the current working environment.

4.2.7 Visualization

Plot the results with the ‘plotSplice’ function, giving gene of interest in the geneid argument. Save the top results sorted by log Fold change and also calculate per gene statictics using the “simes method”. First create a function to add different sheets as workbooks in excel. Generate a volcano plot to exhibit the exons.

Volcano plot-

Volcano plot showing up and downregulated genes at pCutoff = FDR 10% and FC 2.

EnhancedVolcano(ts, lab = ts$ExonID, selectLab = head((ts$ExonID),3000),xlab = bquote(~Log[2]~ 'fold change'), x = 'logFC', y = 'P.Value', title = 'Volcano Plot', subtitle = 'Mbnl1_KO vs WT (Limma_diffSplice)', FCcutoff = 1, labSize = 4,legendPosition = "right",caption = bquote(~Log[2]~ "Fold change cutoff, 1; FDR 10%"))

Save the R object

save.image("Mbnl1_KO_Limma_diffSplice.RData")

4.3 Using rMATS to identify different types of splicing events

4.3.1 Download and Install rMATS

Ensure the latest version of rMATS v4.1.1 (also known as rMATS turbo due to the increased processing time and less requirements of memory) is installed either using conda or github in the working directory.

4.3.2 Required Files

For pairwise splicing analysis, we will need sorted bam files for all the samples generated after the read alignment step using STAR. prepare text files for two conditions by copying the name of bam files (along with the path) separated by ‘,’ comma. Following commands should be run on the linux command-line-

mkdir rMATS_analysis
cd bams/
ls -pd "$PWD"/* | grep "WT" | tr '\n' ',' > Wt.txt
ls -pd "$PWD"/* | grep "Mb" | tr '\n' ',' > KO.txt
#Move the files to the main working directory
mv *.txt /rMATS_analysis
cd rMATS_analysis

4.3.3 Run rMATS

Run the python script rmats.py with the two input files generated in the previous step, along with the GTF annotation obtained in 1.1.3 by typing the following command on linux command-line.

python rmats_turbo/rmats.py --b1 KO.txt --b2 Wt.txt --gtf annotation.gtf -t paired --readLength 56 --nthread 8 --od rmats_out/ --tmp rmats_tmp

NOTE:The reference annotation in the form of a GTF file is also required. Make sure both the folder containing bam files (bams/) and annotation file (annotation.gtf) is present in the current path/provide the correct path in the above command. (AS_analysis) Check the parameters if the data is single-end, change the -t option.

The results will be in the specified output directory by the -o option(rmats_out). The –tmp option creates a temporary subdirectory to store all the intermediate files in the analysis. The output folder contains text files for different splicing events with p-values. The summary.txt file presents the table of all the events falling in different categories of AS events. The main output file that we will focus on is *.MATS.JCEC.txt, this includes both reads that span junctions defined by rmats (Junction Counts) and reads that do not cross an exon boundary (Exon Counts).

4.3.4 Exploring rMATS results

Use Bioconductor package called ‘maser’ to explore the rMATS results. Load the *.JCEC files for each of the 5 events and filter by coverage including an average of 5 reads.

library(maser) %>% invisible
Welcome to maser
mbnl1 <- maser("rMATS_analysis/rmats_out/", c("WT", "Mbnl1_KO"), ftype = "JCEC")
#Filtering out events 
mbnl1_filt <- filterByCoverage(mbnl1, avg_reads = 5)

4.3.4 Visualizing rMATS results

Select the significant splicing events at False Discovery Rate (FDR) 10% and minimum 10% change in Percent Spliced In (deltaPSI) using ‘topEvents’ function from ‘maser’ package. Next , check the gene events for individual genes of interest (sample gene-Wnk1) and plot PSI values for each splicing event of that gene. Generate a volcano plot by specifying the event type.

#Top splicing events at 10% FDR 
mbnl1_top <- topEvents(mbnl1_filt, fdr = 0.1, deltaPSI = 0.1)
mbnl1_top
A Maser object with 1012 splicing events.

Samples description: 
Label=WT     n=3 replicates
Label=Mbnl1_KO     n=3 replicates

Splicing events: 
A3SS.......... 33 events
A5SS.......... 69 events
SE.......... 706 events
RI.......... 123 events
MXE.......... 81 events
#Check the gene events for a particular gene
## Retrieve Wnk1 splicing events
mbnl1_wnk1 <- geneEvents(mbnl1_filt, geneS = "Wnk1", fdr = 0.1, deltaPSI = 0.1)
maser::display(mbnl1_wnk1, "SE")
plotGenePSI(mbnl1_wnk1, type = "SE", show_replicates = TRUE)


  volcano(mbnl1_filt, fdr = 0.1, deltaPSI = 0.1, type = "SE") + xlab("deltaPSI") +ylab("Log10 Adj. Pvalue")+ ggtitle("Volcano Plot of exon skipping events")

NOTE: By default the “summary.txt” obtained in rMATS output folder is generated for FDR 5%. To change the FDR or other parameters run the summary.py script located in rMATS source folder (rmats_turbo/rMATS_P/summary.py) as-

python rmats_turbo/rMATS_P/summary.py rmats_out --p-cutoff 0.1 --summary-prefix rmats_summary

Generate an excel table containing significant events (FDR 10%) of different types of AS events from rMATS ranalysis. Use “add.xlsx” function to generate an excel sheet summarizing all the rMATS result.

# Read rMATS summary and individual text files for all events and combine them into one table
  read.table('rMATS_analysis/rmats_summary_FDR_0.1_IncLevelDifference_0.txt', header = T) %T>%
  write.xlsx(file = "rMATS_output_summary.xlsx", sheetName = "rMATS_Summary", firstRow = TRUE, headerStyle = createStyle(textDecoration = "BOLD", halign = "center"))
  
  read.table('rMATS_analysis/rmats_out/SE.MATS.JCEC.txt', header = T) %>%
    filter(FDR <= 0.1) %>%  .[order(.$PValue),] %T>%
    add.xlsx.sheet(file = "rMATS_output_summary.xlsx", "SE.MATS.JCEC", .)
  read.table('rMATS_analysis/rmats_out/A5SS.MATS.JCEC.txt', header = T) %>%
    filter(FDR <= 0.1) %>%  .[order(.$PValue),] %T>%
    add.xlsx.sheet(file = "rMATS_output_summary.xlsx", "A5SS.MATS.JCEC", .)
  read.table('rMATS_analysis/rmats_out/A3SS.MATS.JCEC.txt', header = T) %>%
    filter(FDR <= 0.1) %>%  .[order(.$PValue),] %T>%
    add.xlsx.sheet(file = "rMATS_output_summary.xlsx", "A3SS.MATS.JCEC", .)
  read.table('rMATS_analysis/rmats_out/MXE.MATS.JCEC.txt', header = T) %>%
    filter(FDR <= 0.1) %>%  .[order(.$PValue),] %T>%
    add.xlsx.sheet(file = "rMATS_output_summary.xlsx", "MXE.MATS.JCEC", .)
  read.table('rMATS_analysis/rmats_out/RI.MATS.JCEC.txt', header = T) %>%
    filter(FDR <= 0.1) %>%  .[order(.$PValue),] %T>%
    add.xlsx.sheet(file = "rMATS_output_summary.xlsx", "RI.MATS.JCEC", .)
NA

Generate Sashimi plot for the splicing events result obtained with rMATS in form of text files using ‘rmats2shahimiplot’ package. Ensure the working directory is set to the rmats2sashimiplot folder, to run the python script by typing the following command on linux command-line.

python ./src/rmats2sashimiplot/rmats2sashimiplot.py --b1 ../bams/WT_Thymus_1.bam,../bams/WT_Thymus_2.bam,../bams/WT_Thymus_3.bam --b2 ../bams/Mbnl1KO_Thymus_1.bam,../bams/Mbnl1KO_Thymus_2.bam,../bams/Mbnl1KO_Thymus_3.bam -t SE -e ../rMATS_analysis/rmats_out/SE.MATS.JC.txt  --l1 WT --l2 Mbnl1_KO --exon_s 1 --intron_s 5 -o ../rMATS_analysis/rmats2shasmi_output

NOTE: This process can be time-consuming as it will generate the Sashimi plot for all the results in the events file. Choose the top results (gene names and exons) as displayed by the topEvents function from ‘maser’ and visualize the corresponding Sashimi plot.

NOTE: The results of rMATS for example, the chromosomes in SE.MATS.JC.txt are named with a ‘chr’ prefix by default, however the chromosomes in the input bam file varies according to given annotation file and are sometimes represented without the ‘chr’ prefix (Ensembl convention). This might cause errors when running rmats2sashimi. Hence, modify the Bam input using samtools to add ‘chr’ prefix before running rmats2sashimi for individual genes. Run the following command on the Linux command-line to modify all bam files and extract rMATS result for a particular gene, e.g., “Wnk1”. Replace Wnk1 with your gene of interest.

cd bams
for bam in ./*bam; do
    echo $bam
    samtools view -h $bam | sed -e '/^@SQ/s/SN\:/SN\:chr/' -e '/^[^@]/s/\t/\tchr/2' | samtools view -bS - > temporary.bam
    mv -f temporary.bam $bam
    samtools index $bam
done

#Extract rMATS output for 'Wnk1' gene
sed -n "1p;/Wnk1/p" SE.MATS.JCEC.txt > wnk1_SE_JCEC.txt
#Replace Wnk1 with your gene of interest.

Run the python command on the linux command-line to get the pdf files for each event of the sample gene wnk1.

python ./src/rmats2sashimiplot/rmats2sashimiplot.py --b1 ../bams/WT_Thymus_1.bam,../bams/WT_Thymus_2.bam,../bams/WT_Thymus_3.bam --b2 ../bams/Mbnl1KO_Thymus_1.bam,../bams/Mbnl1KO_Thymus_2.bam,../bams/Mbnl1KO_Thymus_3.bam -t SE -e ../rMATS_analysis/rmats_out/wnk1_SE_JCEC.txt  --l1 WT --l2 Mbnl1_KO --exon_s 1 --intron_s 5 -o ../rMATS_analysis/rmats2shasmi_output_wnk1

Note:Ensure the working directory is correct. This step will generate several plots for all the events of ‘Wnk1’ gene. Check the event id to select a specific output.

The Sashimi plot for each event type obtained from rmats2sashimi are shown here-

#Example gene 'Wnk1"
knitr::include_graphics("Wnk1-1.png")

#SE example gene 'Add3'
knitr::include_graphics("SE-1.png")

#A5SS example gene 'Baz2b'
knitr::include_graphics("A5SS-1.png")

#A3SS example gene 'Lsm14b'
knitr::include_graphics("A3SS-1.png")

#MXE example gene 'Mta1'
knitr::include_graphics("MXE-1.png")

#RI example gene 'Arpp21'
knitr::include_graphics("RI-1.png")

NOTE: The pdf was converted to image to include them in the RMarkdown file using ‘pdftoppm’ on the linux command-line. (Eg: pdftoppm A5SS_Baz2b.pdf A5SS -png)

LS0tCnRpdGxlOiAiSWRlbnRpZmljYXRpb24gb2YgQWx0ZXJuYXRpdmUgU3BsaWNpbmcgYW5kIHBvbHlhZGVueWxhdGlvbiBpbiBidWxrIFJOQS1zZXEgZGF0YSIKYXV0aG9yOiAiR3VuamFuIERpeGl0IgpkYXRlOiAiMjIvMDEvMjAyMSIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICB0aGVtZTogdW5pdGVkCiAgICB0b2M6IHllcwplZGl0b3Jfb3B0aW9uczoKICBjaHVua19vdXRwdXRfdHlwZTogaW5saW5lCi0tLQoKIyBJbnRyb2R1Y3Rpb24KTk9URTogVGhlIGNvbnRleHQgb2YgdGhpcyBSIG5vdGVib29rIGluY2x1ZGVzIGFsbCB0aGUgUiBjb2RlIHJlcXVpcmVkIGZvciB0aGUgYW5hbHlzaXMgb2YgRGlmZmVyZW50aWFsIHNwbGljaW5nIGFuYWx5c2lzIG9mIGJ1bGsgUk5BLVNlcSBkYXRhLiBSZWZlciB0byB0aGUgUEFSVCAxLSBCdWxrIFJOQS1TZXEgYW5hbHlzaXMgb2YgdGhlIFByb3RvY29sIHNlY3Rpb24uCgojIEluc3RhbGxhdGlvbiBvZiB0b29scyBhbmQgUiBwYWNrYWdlcyB1c2VkIGluIHRoZSBhbmFseXNpcy0gCgpDb25kYSBpcyBhIHBvcHVsYXIgYW5kIGZsZXhpYmxlIHBhY2thZ2UgbWFuYWdlciB0aGF0IGFsbG93cyBjb252ZW5pZW50IGluc3RhbGxhdGlvbiBvZiBwYWNrYWdlcyB3aXRoIHRoZWlyIGRlcGVuZGVuY2llcyBhY3Jvc3MgYWxsIHBsYXRmb3Jtcy4gVXNlICdBbmFjb25kYScgKGNvbmRhIHBhY2thZ2UgbWFuYWdlcikgdG8gaW5zdGFsbCAnY29uZGEnIHdoaWNoIGNhbiBiZSB1c2VkIHRvIGluc3RhbGwgdGhlIHRvb2xzL3BhY2thZ2VzIHJlcXVpcmVkIGZvciB0aGUgYW5hbHlzaXMuICAgCgpEb3dubG9hZCAnQW5hY29uZGEnIGFjY29yZGluZyB0byB0aGUgc3lzdGVtIHJlcXVpcmVtZW50cyBmcm9tIGh0dHBzOi8vd3d3LmFuYWNvbmRhLmNvbS9wcm9kdWN0cy9pbmRpdmlkdWFsI0Rvd25sb2FkcyBhbmQgaW5zdGFsbCBpdCBieSBmb2xsb3dpbmcgdGhlIHByb21wdHMgaW4gZ3JhcGhpY2FsIGluc3RhbGxlci4gSW5zdGFsbCBhbGwgdGhlIHBhY2thZ2VzIHVzaW5nICdjb25kYScgYnkgdHlwaW5nIHRoZSBmb2xsb3dpbmcgY29tbWFuZHMgb24gbGludXggY29tbWFuZC1saW5lLiAKCmBgYHtiYXNofQpjb25kYSBpbnN0YWxsIC1jIGRhbGVyIHNyYXRvb2xraXQgIApjb25kYSBpbnN0YWxsIC1jIGNvbmRhLWZvcmdlIHBhcmFsbGVsIApjb25kYSBpbnN0YWxsIC1jIGJpb2NvbmRhIHN0YXIgZmFzdHFjIHJtYXRzIHJtYXRzMnNhc2hpbWlwbG90ICAgCmBgYAoKVG8gZG93bmxvYWQgYWxsIHRoZSBSIHBhY2thZ2VzIHVzZWQgaW4gdGhlIHByb3RvY29sLCB0eXBlIHRoZSBmb2xsb3dpbmcgY29kZSBpbiBSIGNvbnNvbGUgb3IgUi1zdHVkaW8uIAoKYGBge3J9CmJpb2NfcGFja2FnZXMgPC0gYygiREVYU2VxIiwgIlJzdWJyZWFkIiwgIkVuaGFuY2VkVm9sY2FubyIsICJydHJhY2tsYXllciIsICJlZGdlUiIsICJsaW1tYSIsICJtYXNlciIpICAKcGFja2FnZXMgPC0gYygibWFncml0dHIiLCAidGlkeXZlcnNlIiwib3Blbnhsc3giLCAiQmlvY01hbmFnZXIiLCAiR2Vub21pY1JhbmdlcyIpIAoKI0luc3RhbGwgaWYgbm90IGFscmVhZHkgaW5zdGFsbGVkICAKaW5zdGFsbGVkX3BhY2thZ2VzIDwtIHBhY2thZ2VzICVpbiUgcm93bmFtZXMoaW5zdGFsbGVkLnBhY2thZ2VzKCkpICAKaW5zdGFsbGVkX2Jpb2NfcGFja2FnZXMgPC0gYmlvY19wYWNrYWdlcyAlaW4lIHJvd25hbWVzKGluc3RhbGxlZC5wYWNrYWdlcygpKSAgCmlmIChhbnkoaW5zdGFsbGVkX3BhY2thZ2VzID09IEZBTFNFKSkgeyAgCiAgaW5zdGFsbC5wYWNrYWdlcyhwYWNrYWdlc1shaW5zdGFsbGVkX3BhY2thZ2VzXSxkZXBlbmRlbmNpZXMgPSBUUlVFKSAgCiAgQmlvY01hbmFnZXI6Omluc3RhbGwocGFja2FnZXNbIWluc3RhbGxlZF9iaW9jX3BhY2thZ2VzXSwgZGVwZW5kZW5jaWVzID0gVFJVRSkKfSAKYGBgCgpJbiB0aGlzIGNvbXB1dGF0aW9uYWwgcHJvdG9jb2wsIGNvbW1hbmRzIHdpbGwgYmUgZ2l2ZW4gYXMgZWl0aGVyIFIgTm90ZWJvb2sgZmlsZXMgKGZpbGVzIHdpdGggZXh0ZW5zaW9uIOKAnC5SbWTigJ0pLCBSIGNvZGUgZmlsZXMgKGZpbGVzIHdpdGggZXh0ZW5zaW9uIOKAnC5S4oCdKSwgb3IgTGludXggQmFzaCBzaGVsbCBzY3JpcHRzIChmaWxlcyB3aXRoIGV4dGVuc2lvbiDigJwuc2jigJ0pLiBSIE5vdGVib29rIChSbWQpIGZpbGVzIHNob3VsZCBiZSBvcGVuZWQgaW4gUlN0dWRpbyB1c2luZyBGaWxlfE9wZW4gRmlsZeKApiwgYW5kIGluZGl2aWR1YWwgY29kZSBjaHVua3MgKHdoaWNoIG1heSBiZSBSIGNvbW1hbmRzIG9yIEJhc2ggc2hlbGwgY29tbWFuZHMpIHRoZW4gcnVuIGludGVyYWN0aXZlbHkgYnkgY2xpY2tpbmcgdGhlIGdyZWVuIGFycm93IGF0IHRoZSB1cHBlciByaWdodC4gUiBjb2RlIGZpbGVzIGNhbiBiZSBydW4gYnkgb3BlbmluZyBpbiBSU3R1ZGlvLCBvciBvbiB0aGUgTGludXggY29tbWFuZC1saW5lIGJ5IHByZWZhY2luZyB3aXRoIOKAnFJzY3JpcHTigJ0sIGUuZy4gUnNjcmlwdCBleGFtcGxlLlIuIFNoZWxsIHNjcmlwdHMgYXJlIHJ1biBvbiB0aGUgTGludXggY29tbWFuZC1saW5lIGJ5IHByZWZhY2luZyB0aGUgc2NyaXB0IHdpdGggdGhlIOKAnHNo4oCdIGNvbW1hbmQgZS5nLiBzaCBleGFtcGxlLnNoLiAKCiMgMS4gRGF0YSBkb3dubG9hZGluZyBhbmQgcHJlLXByb2Nlc3NpbmcgClRoZSBjb2RlIHNuaXBwZXRzIGFubm90YXRlZCBiZWxvdyBhcmUgYXZhaWxhYmxlIGluIHRoZSBzdXBwbGVtZW50YXJ5IGNvZGUgZmlsZSDigJxEb3dubG9hZGluZ19kYXRhX3ByZXByb2Nlc3NpbmcuUm1k4oCdLCB0byBmb2xsb3cgdGhlIGluZGl2aWR1YWwgc3RlcHMgaW50ZXJhY3RpdmVseSwgYW5kIGFyZSBhbHNvIHByb3ZpZGVkIGFzIGEgYmFzaCBzY3JpcHQgdG8gYmUgcnVuIGluIGJhdGNoIG9uIHRoZSBMaW51eCBjb21tYW5kLWxpbmUgKHNoIGRvd25sb2FkaW5nX2RhdGFfcHJlcHJvY2Vzc2luZy5zaCkuCgojIyMgMS4xICBEb3dubG9hZGluZyB0aGUgcmF3IGRhdGEuCkRvd25sb2FkIHRoZSByYXcgZGF0YSBmcm9tIFNlcXVlbmNlIFJlYWQgQXJjaGl2ZSBbU1JBXSB1c2luZyB0aGUgJ3ByZWZldGNoJyBjb21tYW5kIGZyb20gU1JBIHRvb2xraXQuIEdpdmUgdGhlIFNSQSBBY2Nlc3Npb24gSWRzIGluIHNlcXVlbmNlIGluIHRoZSBmb2xsb3dpbmcgY29tbWFuZCB0byBkb3dubG9hZCB0aGVtIGluIHBhcmFsbGVsIHVzaW5nIEdOVSBwYXJhbGxlbCB1c2luZyBHTlUgcGFyYWxsZWwgdXRpbGl0eS4gRW5zdXJlIFNSQSB0b29sa2l0IGFuZCBHTlUgcGFyYWxsZWwgaXMgaW5zdGFsbGVkIGJlZm9yZSBwcm9jZWVkaW5nIHRoaXMgc3RlcC4gClRvIGRvd25sb2FkIFNSQSBmaWxlcyBvZiBhY2Nlc3Npb24gaWRzIGZyb20gU1JSMTAyNjE2MDEgdG8gU1JSMTAyNjE2MDYgaW4gcGFyYWxsZWwsIHR5cGUgdGhlIGZvbGxvd2luZyBjb21tYW5kcyBvbiB0aGUgbGludXggY29tbWFuZC1saW5lLQpgYGB7YmFzaH0Kc2VxIDEwMjYxNjAxIDEwMjYxNjA2IHwgcGFyYWxsZWwgcHJlZmV0Y2ggU1JSe30gCmBgYAoKIyMjIDEuMS4yIEV4dHJhY3QgdGhlIGZhc3RxIGZpbGVzLgpFeHRyYWN0IHRoZSAgZmFzdHEgZmlsZXMgZnJvbSB0aGUgYXJjaGl2ZSB1c2luZyAnZmFzdHEtZHVtcCcgZnVuY3Rpb24gb2YgU1JBIHRvb2xraXQuIFVzZSBHTlUgcGFyYWxsZWwgYW5kIGdpdmUgdGhlIG5hbWVzIG9mIGFsbCBTUkEgZmlsZXMgdG9nZXRoZXIuCgpgYGB7YmFzaH0KcGFyYWxsZWwgLWogMyBmYXN0cS1kdW1wIC0tZ3ppcCAtLXNraXAtdGVjaG5pY2FsIC0tcmVhZC1maWx0ZXIgcGFzcyAtLWR1bXBiYXNlIC0tc3BsaXQtZSAtLWNsaXAgLS1vcmlnZm10IHt9IDo6OiA8bmFtZSBvZiBzcmEgZmlsZXMgdG9nZXRoZXI+IApgYGAKCiMjIyAxLjEuMyBEb3dubG9hZCByZWZlcmVuY2UgZ2Vub21lIGFuZCBhbm5vdGF0aW9uCkRvd25sb2FkIHRoZSByZWZlcmVuY2UgZ2Vub21lIGFuZCBhbm5vdGF0aW9ucyBmb3IgTW91c2UgKEdlbm9tZSBhc3NlbWJseSBHUkNtMzkpIGZyb20gd3d3LmVuc2VtYmwub3JnIHVzaW5nIHRoZSBmb2xsb3dpbmcgYXQgdGhlIExpbnV4IGNvbW1hbmQtbGluZS4gVGhlICd3Z2V0JyBmdW5jdGlvbiBkb3dubG9hZHMgdGhlIGRlc3RpbmF0aW9uIGZpbGUgaW4gdGhlIHByb3ZpZGVkIGxpbmsgYW5kICdndW56aXAnIHdpbGwgZGVjb21wcmVzcyB0aGUgZmlsZS4gU3RvcmUgYm90aCBnZW5vbWUgYW5kIGFubm90YXRpb24gZmlsZSBpbiBzcGVjaWZpYyB2YXJpYWJsZXMgZm9yIGZ1cnRoZXIgdXNlLiAgIAoKYGBge2Jhc2h9CndnZXQgLW52IC1PIGFubm90YXRpb24uZ3RmLmd6IGh0dHA6Ly9mdHAuZW5zZW1ibC5vcmcvcHViL3JlbGVhc2UtMTAzL2d0Zi9tdXNfbXVzY3VsdXMvTXVzX211c2N1bHVzLkdSQ20zOS4xMDMuZ3RmLmd6IFwgJiYgZ3VuemlwIC1mIGFubm90YXRpb24uZ3RmLmd6IAoKd2dldCAtbnYgLU8gZ2Vub21lLmZhLmd6IGh0dHA6Ly9mdHAuZW5zZW1ibC5vcmcvcHViL3JlbGVhc2UtMTAzL2Zhc3RhL211c19tdXNjdWx1cy9kbmEvTXVzX211c2N1bHVzLkdSQ20zOS5kbmEucHJpbWFyeV9hc3NlbWJseS5mYS5neiBcICYmIGd1bnppcCAtZiBnZW5vbWUuZmEuZ3ogCkdURj0kKHJlYWRsaW5rIC1mIGFubm90YXRpb24uZ3RmKSAKR0VOT01FPSQocmVhZGxpbmsgLWYgZ2Vub21lLmZhKSAKYGBgCgojIyAxLjIgUHJlLXByb2Nlc3NpbmcgCgojIyMgMS4yLjEgUXVhbGl0eSBDb250cm9sCkFzc2VzcyB0aGUgcXVhbGl0eSBvZiByYXcgcmVhZHMgd2l0aCBGQVNUUUMgKHYwLjExLjkpLiBDcmVhdGUgYW4gb3V0cHV0IGZvbGRlciBhbmQgcnVuIGZhc3RxYyB3aXRoIHBhcmFsbGVsIG9uIG11bHRpcGxlIGlucHV0IGZhc3RxIGZpbGVzLiBFbnN1cmUgdGhlIHBhdGggb2YgcmF3IGlucHV0IGZpbGVzIGlzIGNvcnJlY3QgYmVmb3JlIHJ1bm5pbmcgdGhlIGNvbW1hbmQuIFRoaXMgc3RlcCB3aWxsIGdlbmVyYXRlIHF1YWxpdHkgcmVwb3J0IGZvciBlYWNoIHNhbXBsZS4gRXhhbWluZSB0aGUgcmVwb3J0cyB0byBlbnN1cmUgdGhlIHF1YWxpdHkgb2YgcmVhZHMgaXMgYWNjZXB0YWJsZSBiZWZvcmUgZG9pbmcgZnVydGhlciBhbmFseXNpcy4oUmVmZXIgdG8gdGhlIHVzZXIgbWFudWFsIGZvciB1bmRlcnN0YW5kaW5nIHRoZSByZXBvcnRzIGF0IGh0dHBzOi8vd3d3LmJpb2luZm9ybWF0aWNzLmJhYnJhaGFtLmFjLnVrL3Byb2plY3RzL2Zhc3RxYy8pCgpgYGB7YmFzaH0KbWtkaXIgZmFzdHFjX291dCAKcGFyYWxsZWwgImZhc3RxYyB7fSAtbyBmYXN0cWNfb3V0IiA6OjogJFJBV19EQVRBLyouZmFzdHEuZ3ogCmBgYApOT1RFOiBJZiBuZWNlc3NhcnksIHBlcmZvcm0gYWRhcHRlciB0cmltbWluZyB3aXRoICdjdXRhZGFwdCcgb3IgJ3RyaW1tb21hdGljJyB0byByZW1vdmUgc2VxdWVuY2luZyBpbnRvIGZsYW5raW5nIGFkYXB0ZXJzLCB3aGljaCB2YXJpZXMgYmFzZWQgb24gUk5BIGZyYWdtZW50IHNpemUgYW5kIHJlYWQgbGVuZ3RoLiBJbiB0aGlzIGFuYWx5c2lzIHdlIHNraXBwZWQgdGhpcyBzdGVwIGFzIHRoZSBmcmFjdGlvbiBvZiByZWFkcyBhZmZlY3RlZCB3YXMgbWluaW1hbC4gCgoKIyMjIDEuMi4yIFJlYWQgYWxpZ25tZW50IApUaGUgbmV4dCBzdGVwIGluIHByZS1wcm9jZXNzaW5nIGluY2x1ZGVzIG1hcHBpbmcgdGhlIHJlYWRzIHRvIHRoZSByZWZlcmVuY2UgZ2Vub21lLiBGaXJzdGx5LCBidWlsZCB0aGUgaW5kZXggZm9yIHRoZSByZWZlcmVuY2UgZ2Vub21lIHVzaW5nICdnZW5vbWVHZW5lcmF0ZScgZnVuY3Rpb24gb2YgU1RBUiAoU3BsaWNlZCBUcmFuc2NyaXB0cyBBbGlnbm1lbnQgdG8gYSBSZWZlcmVuY2UgdjIuNy41YykgYW5kIHRoZW4gYWxpZ24gdGhlIHJhdyByZWFkcyB0byB0aGUgcmVmZXJlbmNlIChhbHRlcm5hdGl2ZWx5IHByZWJ1aWx0IGluZGV4ZXMgYXJlIGF2YWlsYWJsZSBvbiB0aGUgU1RBUiB3ZWJzaXRlIGFuZCBjYW4gYmUgdXNlZCBkaXJlY3RseSBmb3IgcmVhZCBhbGlnbm1lbnQpLiAKClJ1biB0aGUgZm9sbG93aW5nIGNvbW1hbmRzIGFzIGEgYmFzaCBzY3JpcHQgb24gdGhlIGxpbnV4IGNvbW1hbmQtbGluZS4gCmBgYHtiYXNofQojQnVpbGQgU1RBUiBpbmRleCAKR0RJUj1TVEFSX2luZGljZXMgCm1rZGlyICRHRElSIApTVEFSIC0tcnVuTW9kZSBnZW5vbWVHZW5lcmF0ZSAtLWdlbm9tZUZhc3RhRmlsZXMgJEdFTk9NRSAtLXNqZGJHVEZmaWxlICRHVEYgLS1ydW5UaHJlYWROIDggLS1nZW5vbWVEaXIgJEdESVIgCk9ESVI9cmVzdWx0cy9tYXBwaW5nIApta2RpciAtcCAkT0RJUgojQWxpZ24gcmVhZHMgdG8gdGhlIGdlbm9tZSAKZm9yIGZxMSBpbiAkUkFXX0RBVEEvKlIxLmZhc3RxLmd6OyAgCmRvIApmcTI9JChlY2hvICRmcTEgfCBzZWQgJ3MvMS5mYXN0cS5nei8yLmZhc3RxLmd6L2cnKTsgCk9VVFBVVD0kKGJhc2VuYW1lICR7ZnExfXwgc2VkICdzL1IxLmZhc3RxLmd6Ly9nJyk7IApTVEFSIC0tZ2Vub21lRGlyICRHRElSIFwgCiAtLXJ1blRocmVhZE4gMTIgXCAKIC0tcmVhZEZpbGVzQ29tbWFuZCB6Y2F0IFwgCiAtLXJlYWRGaWxlc0luICAke2ZxMX0gJHtmcTJ9XCAKIC0tb3V0RmlsZU5hbWVQcmVmaXggJE9ESVJcLyR7T1VUUFVUfSBcIAogLS1vdXRTQU10eXBlIEJBTSBTb3J0ZWRCeUNvb3JkaW5hdGUgXCAKIC0tb3V0U0FNdW5tYXBwZWQgV2l0aGluIFwgCiAtLW91dFNBTWF0dHJpYnV0ZXMgU3RhbmRhcmQgCkRvbmUgCmBgYAoKU1RBUiBhbGlnbmVyIHdpbGwgZ2VuZXJhdGUgYW5kIHNvcnQgQkFNIGZpbGVzIGZvciBlYWNoIHNhbXBsZSBhZnRlciByZWFkIGFsaWdubWVudHMuIEJhbSBmaWxlcyBtdXN0IGJlIHNvcnRlZCBiZWZvcmUgcHJvY2VlZGluZyB0byBmdXJ0aGVyIHN0ZXBzLiBDcmVhdGUgYSBmb2xkZXIgJ2JhbXMnIGFuZCBjb3B5IGFsbCB0aGUgYmFtIGZpbGVzIHRvIGl0LgoKIyAyLiBQcmVwYXJpbmcgRXhvbiBhbm5vdGF0aW9ucwpSdW4gdGhlIHN1cHBsZW1lbnRhcnkgZmlsZS0gInByZXBhcmVfbW1fZXhvbl9hbm5vdGF0aW9uLlIiIHdpdGggdGhlIGRvd25sb2FkZWQgYW5ub3RhdGlvbiBpbiBHVEYgZm9ybWF0IG9uIHRoZSBsaW51eCBjb21tYW5kLWxpbmUgdG8gcHJlcGFyZSB0aGUgYW5ub3RhdGlvbnMuIApTYXZlIHRoZSBnaXZlbiBjb2RlIGFzICdwcmVwYXJlX21tX2V4b25fYW5ub3RhdGlvbi5SJyBmaWxlIGFuZCBydW4gaXQgb24gdGhlIGxpbnV4IGNvbW1hbmQtbGluZSBhcy0KYGBge2Jhc2h9ClJzY3JpcHQgcHJlcGFyZV9tbV9leG9uX2Fubm90YXRpb24uUiBhbm5vdGF0aW9uLmd0ZgpgYGAKT3IgcnVuIGl0IG9uIFJTdHVkaW8gYXM6CmBgYHtyfQojISAvdXNyL2Jpbi9lbnYgUnNjcmlwdAojVG8gcnVuLSBSc2NyaXB0IHByZXBhcmVfbW1fYW5ub3RhdGlvbi5SIDxHVEZfZmlsZT4KbGlicmFyeShydHJhY2tsYXllcikKbGlicmFyeSh0aWR5dmVyc2UpCmFyZ3MgPSBjb21tYW5kQXJncyh0cmFpbGluZ09ubHkgPSBUUlVFKQpnZmZfbW0gPC0gYXJnc1sxXQptZ2cgPC0gaW1wb3J0KGdmZl9tbSkKCmFubm8gPSBtZ2cgJT4lIGRhdGEuZnJhbWUgJT4lIAogIG11dGF0ZShzZXFuYW1lcyA9IGFzLmNoYXJhY3RlcihzZXFuYW1lcykpICU+JSAKICBkcGx5cjo6ZmlsdGVyKCFncmVwbCgiY2hyWzEtMTldfFh8WSIsIHNlcW5hbWVzKSkgJT4lIAogIGZpbHRlcih0eXBlICVpbiUgImV4b24iKSAlPiUgCiAgc2VsZWN0KHNlcW5hbWVzLCBzdGFydCwgICBlbmQgLHdpZHRoLCBzdHJhbmQsIGdlbmVfbmFtZSwgIHRyYW5zY3JpcHRfaWQpICAlPiUgZGlzdGluY3QoKSAlPiUKICBncm91cF9ieShnZW5lX25hbWUsIHNlcW5hbWVzLCBzdGFydCwgZW5kLCB3aWR0aCwgc3RyYW5kKSAlPiUgCiAgc3VtbWFyaXNlKHRyYW5zY3JpcHRfaWRzID0gcGFzdGUodHJhbnNjcmlwdF9pZCwgc2VwPSIiLGNvbGxhcHNlPSIsIikpICU+JSAKICB1bmdyb3VwKCkgICU+JSAgCiAgbXV0YXRlKG51bWJlciA9IDEpICU+JSAKICBhcnJhbmdlKCBzZXFuYW1lcywgICBzdGFydCAsICAgIGVuZCkgJT4lICBncm91cF9ieShnZW5lX25hbWUsIHNlcW5hbWVzKSAlPiUgIwogIG11dGF0ZSh0aWNrZXIgPSBjdW1zdW0obnVtYmVyKSkgJT4lIAogIG11dGF0ZShFeG9uSUQgPSBwYXN0ZShnZW5lX25hbWUsICIuIixzZXFuYW1lcywiLiIsIHRpY2tlciwgc2VwPSIiKSkgJT4lCiAgZHBseXI6OnNlbGVjdCgtbnVtYmVyLCB0aWNrZXIpICU+JQogIGFkZF9jb3VudChnZW5lX25hbWUpICU+JQogIGRhdGEuZnJhbWUgCmNvbG5hbWVzKGFubm8pIDwtIGMoIkdlbmVJRCIsICJDaHIiLCAiU3RhcnQiLCAiRW5kIiwgIldpZHRoIiwgIlN0cmFuZCIsICJUcmFuc2NyaXB0SURzIiwgIlRpY2tlciIsICJFeG9uSUQiLCAibiIpCnNhdmUoYW5ubywgZmlsZT1wYXN0ZSgibW1fZXhvbl9hbm5vLlJEYXRhIiwgc2VwPSIiKSkKYGBgCgpUaGUgR1RGIGZpbGUgY29udGFpbnMgbXVsdGlwbGUgZXhvbiBlbnRyaWVzIGZvciBkaWZmZXJlbnQgaXNvZm9ybXMuIFRoaXMgZmlsZSBpcyB1c2VkIHRvIOKAmGNvbGxhcHNl4oCZIHRoZSBtdWx0aXBsZSB0cmFuc2NyaXB0IElEcyBmb3IgZWFjaCBleG9uIHVzaW5nIGEgY29tbWEuIEl0IGlzIGFuIGltcG9ydGFudCBzdGVwIHRvIGRlZmluZSBleG9uIGNvdW50aW5nIGJpbnMuCgojIDMgQ291bnRpbmcgUmVhZHMKVGhlIG5leHQgc3RlcCBpcyB0byBjb3VudCB0aGUgbnVtYmVyIG9mIHJlYWRzIG1hcHBlZCB0byBkaWZmZXJlbnQgdHJhbnNjcmlwdHMvZXhvbnMuIFVzZSBSU3R1ZGlvIHRvIHJ1biB0aGUgZm9sbG93aW5nIGNvbW1hbmRzIG9yIGNhbGwgUiBmcm9tIGNvbW1hbmQtbGluZSBieSB0eXBpbmcgJ1InLgoKIyMjIDMuMSBMb2FkIHJlcXVpcmVkIGxpYnJhcmllcy0KVGhlIGZvbGxvd2luZyBwYWNrYWdlcyBhcmUgcmVxdWlyZWQgZm9yIHRoaXMgcGFydCBvZiB0aGUgYW5hbHlzaXMuIE1ha2Ugc3VyZSB0byBpbnN0YWxsIHRoZSBwYWNrYWdlcyBpZiB0aGV5IGFyZSBub3QgYWxyZWFkeSBpbnN0YWxsZWQuIAoKYGBge3J9CnBhY2thZ2VzIDwtIGMoIkRFWFNlcSIsIlJzdWJyZWFkIiwidGlkeXZlcnNlIiwgIm1hZ3JpdHRyIiwiRW5oYW5jZWRWb2xjYW5vIiwgImVkZ2VSIiwib3Blbnhsc3giKQoKI0xvYWQgbGlicmFyaWVzCmludmlzaWJsZShsYXBwbHkocGFja2FnZXMsIGxpYnJhcnksIGNoYXJhY3Rlci5vbmx5ID0gVFJVRSkpCmBgYAoKIyMjIDMuMiBMb2FkIHRoZSBwcm9jZXNzZWQgYW5ub3RhdGlvbiBmaWxlLSAKCmBgYHtyfQpsb2FkKCJtbV9leG9uX2Fubm8uUkRhdGEiKQpgYGAKCiMjIyAzLjMgUmVhZCB0aGUgYmFtIGZpbGVzIG9idGFpbmVkIGluIHN0ZXAgMS4yLjIoYWZ0ZXIgbWFwcGluZykgYXMgaW5wdXQgZm9yICdmZWF0dXJlQ291bnRzJy4gIApSZWFkIHRoZSBmb2xkZXIgY29udGFpbmluZyBiYW0gZmlsZXMgYnkgZmlyc3QgbGlzdGluZyBlYWNoIGZpbGUgZnJvbSB0aGUgZGlyZWN0b3J5IHRoYXQgZW5kcyB3aXRoIC5iYW0uIFVzZSAnZmVhdHVyZUNvdW50cycgZnJvbSB0aGUgUnN1YnJlYWQgcGFja2FnZSB3aGljaCB0YWtlcyBiYW0gZmlsZXMgYW5kIHByb2Nlc3NlZCBHVEYgYW5ub3RhdGlvbiAocmVmZXJlbmNlKSBhcyBpbnB1dCB0byBnZW5lcmF0ZSBhIG1hdHJpeCBvZiBjb3VudHMgYXNzb2NpYXRlZCB3aXRoIGVhY2ggZmVhdHVyZSB3aXRoIHJvd3MgcmVwcmVzZW50aW5nIGV4b25zKGZlYXR1cmVzKSBhbmQgY29sdW1ucyByZXByZXNlbnRpbmcgc2FtcGxlcy4KQmVmb3JlIHJ1bm5pbmcgdGhpcyBzdGVwLCBtYWtlIHN1cmUgeW91IGhhdmUgdGhlIGZvbGRlciBjb250YWluaW5nIGJhbSBmaWxlcyBpbiB0aGUgY3VycmVudCB3b3JraW5nIGRpcmVjdG9yeS4gCk5leHQsIHBlcmZvcm0gbm9uLXNwZWNpZmljIGZpbHRlcmluZyB0byByZW1vdmUgbG93bHkgZXhwcmVzc2VkIGV4b25zLiBUcmFuc2Zvcm0gdGhlIGRhdGEgZnJvbSByYXcgc2NhbGUgdG8gY291bnRzIHBlciBtaWxsaW9uIChjcG0pIHVzaW5nIHRoZSBjcG0gZnVuY3Rpb24gZnJvbSAnZWRnZVInIHBhY2thZ2UgYW5kIGtlZXAgdGhlIGNvdW50cyBncmVhdGVyIHRoYW4gb25lIGluIGF0IGxlYXN0IHRocmVlIHNhbXBsZXMuIFJlbW92ZSB0aGUgZ2VuZXMgd2l0aCBvbmx5IG9uZSBleG9uLiAKCmBgYHtyfQojIFJlYWQgYWxsIGJhbSBmaWxlcyBhcyBpbnB1dCBmb3IgZmVhdHVyZUNvdW50cwpjb3VudERhdGEgPC0gZGlyKCJiYW1zIiwgcGF0dGVybj0iLmJhbSQiLCBmdWxsLm5hbWVzPVQpICU+JQogIGZlYXR1cmVDb3VudHMoZmlsZXNUb0NvdW50LAogICAgICAgICAgICAgICAgYW5ub3QuZXh0PWFubm8sCiAgICAgICAgICAgICAgICBpc0dURkFubm90YXRpb25GaWxlPUZBTFNFLAogICAgICAgICAgICAgICAgbWluTVFTPTAsIHVzZU1ldGFGZWF0dXJlcz1GQUxTRSwgCiAgICAgICAgICAgICAgICBhbGxvd011bHRpT3ZlcmxhcD1UUlVFLCAKICAgICAgICAgICAgICAgIGxhcmdlc3RPdmVybGFwID0gVFJVRSwgCiAgICAgICAgICAgICAgICBjb3VudE11bHRpTWFwcGluZ1JlYWRzPUZBTFNFLCAKICAgICAgICAgICAgICAgIHByaW1hcnlPbmx5PVRSVUUsIAogICAgICAgICAgICAgICAgaXNQYWlyZWRFbmQ9VFJVRSwgCiAgICAgICAgICAgICAgICBudGhyZWFkcyA9IDEyKQoKIyBOb24tc3BlY2lmaWMgZmlsdGVyaW5nOiBSZW1vdmUgdGhlIGV4b25zIHdpdGggbG93IGNvdW50cyAKaXNleHByIDwtIHJvd25hbWVzKGNvdW50RGF0YSRjb3VudHMpW3Jvd1N1bXMoY3BtKGNvdW50RGF0YSRjb3VudHMpID4gMSkgPj0gM10gCmNvdW50RGF0YSRjb3VudHMgPC0gY291bnREYXRhJGNvdW50c1tyb3duYW1lcyhjb3VudERhdGEkY291bnRzKSAlaW4lIGlzZXhwciwgXSAKYW5ubyA8LSBhbm5vICU+JSBmaWx0ZXIoR2VuZUlEICVpbiUgcm93bmFtZXMoY291bnREYXRhJGNvdW50cykpIAoKIyBSZW1vdmUgZ2VuZXMgd2l0aCBvbmx5IDEgc2l0ZSBhbmQgIE5BIGluIGdlbmVJRHMKZG4gPC0gYW5ubyAlPiUgZ3JvdXBfYnkoR2VuZUlEKSAlPiUgc3VtbWFyaXNlKG5zaXRlcz1uKCkpICU+JSBmaWx0ZXIobnNpdGVzID4gMSAmICFpcy5uYShHZW5lSUQpKQphbm5vIDwtIGFubm8gJT4lIGZpbHRlcihHZW5lSUQgJWluJSBkbiRHZW5lSUQpCmNvdW50RGF0YSRjb3VudHMgPC0gY291bnREYXRhJGNvdW50c1tyb3duYW1lcyhjb3VudERhdGEkY291bnRzKSAlaW4lIGFubm8kR2VuZUlELCBdCmBgYApOT1RFOiBFeGFtaW5lIHRoZSBsaWJyYXJ5IHR5cGUoc2luZ2xlLWVuZCBvciBwYWlyZWQtZW5kKSBhbmQgbW9kaWZ5IHRoZSBwYXJhbWV0ZXJzIGluIGZlYXR1cmVDb3VudHMgcmVzcGVjdGl2ZWx5LgoKIyA0LiBEaWZmZXJlbnRpYWwgU3BsaWNpbmcgYW5kIEV4b24gdXNhZ2UgYW5hbHlzaXMKV2UgZGVzY3JpYmUgdHdvIGFsdGVybmF0aXZlcyBmb3IgdGhpcyBzdGVwOiBERVhTZXEgYW5kIERpZmZTcGxpY2UuIEVpdGhlciBjYW4gYmUgdXNlZCBhbmQgZ2l2ZSBzaW1pbGFyIHJlc3VsdHMuIEZvciBjb25zaXN0ZW5jeSwgc2VsZWN0IERFWFNlcSBpZiB5b3UgcHJlZmVyIGEgREVTZXEyIHBhY2thZ2UgZm9yIERHRSBhbmQgdXNlIERpZmZTcGxpY2UgZm9yIGEgTGltbWEtYmFzZWQgREdFIHBhY2thZ2UuICAKCiMjIDQuMSBVc2luZyBERVhTZXEgcGlwZWxpbmUgZm9yIGRpZmZlcmVudGlhbCBleG9uIGFuYWx5c2lzCgojIyMgNC4xLjEgQ3JlYXRlIGEgc2FtcGxlIHRhYmxlIHRvIGRlZmluZSB0aGUgZXhwZXJpbWVudGFsIGRlc2lnbgpgYGB7cn0Kc2FtcGxlVGFibGUgPSBkYXRhLmZyYW1lKAogICByb3cubmFtZXMgPSBjKCAiTWJubDFLT19UaHltdXNfMSIsICJNYm5sMUtPX1RoeW11c18yIiwgIk1ibmwxS09fVGh5bXVzXzMiLCAKICAgICAgICAgICAgICAgICAiV1RfVGh5bXVzXzEiLCAiV1RfVGh5bXVzXzIiLCAiV1RfVGh5bXVzXzMiICksCiAgIGNvbmRpdGlvbiA9IHJlcChjKCJNYm5sMV9LTyIsICJXVCIpLGMoMywzKSksIGxpYlR5cGUgPSByZXAoYygicGFpcmVkLWVuZCIpKSkKYGBgCk5PVEU6IFRoZSByb3cgbmFtZXMgc2hvdWxkIGJlIGNvbnNpc3RlbnQgd2l0aCB0aGUgYmFtIGZpbGUgbmFtZXMgdXNlZCBieSBmZWF0dXJlQ291bnRzIHRvIGNvdW50IHRoZSByZWFkcy4gc2FtcGxlVGFibGUgY29uc2lzdHMgb2YgZGV0YWlscyBvZiBlYWNoIHNhbXBsZSB3aGljaCBpbmNsdWRlcy0gbGlicmFyeS10eXBlIGFuZCBjb25kaXRpb24uIFRoaXMgaXMgcmVxdWlyZWQgdG8gZGVmaW5lIHRoZSBjb250cmFzdHMgb3IgdGVzdCBncm91cCBmb3IgZGV0ZWN0aW5nIGRpZmZlcmVudGlhbCB1c2FnZS4KCiMjIyA0LjEuMiBQcmVwYXJlIHRoZSBleG9uIGluZm9ybWF0aW9uIGZpbGUKRXhvbiBpbmZvcm1hdGlvbiBpbiBmb3JtIG9mIEdSYW5nZXMgKGdlbm9taWMgcmFuZ2VzKSBvYmplY3QgaXMgcmVxdWlyZWQgYXMgYW4gaW5wdXQgdG8gY3JlYXRlIERFWFNlcSBvYmplY3QuIE1hdGNoIHRoZSBnZW5lIElkcyB3aXRoIHRoZSByZWFkIGNvdW50cyBhbmQgdG8gY3JlYXRlIGV4b25pbmZvIG9iamVjdC4KCmBgYHtyfQpleG9uaW5mbyA9IGFubm9bYW5ubyRHZW5lSUQgJWluJSByb3duYW1lcyhjb3VudERhdGEkY291bnRzKSxdCmV4b25pbmZvIDwtIEdSYW5nZXMgKHNlcW5hbWVzPSAgYW5ubyRDaHIsIApyYW5nZXM9IElSYW5nZXMgKHN0YXJ0PWFubm8kU3RhcnQsIGVuZD1hbm5vJEVuZCwgd2lkdGggPSBhbm5vJFdpZHRoKSwgc3RyYW5kID0gUmxlKGFubm8kU3RyYW5kKSkKbWNvbHMoZXhvbmluZm8pJFRyYW5zY3JpcHRJRHMgPC0gYW5ubyRUcmFuc2NyaXB0SURzCm1jb2xzKGV4b25pbmZvKSRUaWNrZXIgPC0gYW5ubyRUaWNrZXIKbWNvbHMoZXhvbmluZm8pJEV4b25JRCA8LSBhbm5vJEV4b25JRAptY29scyhleG9uaW5mbykkbiA8LSBhbm5vJG4KbWNvbHMoZXhvbmluZm8pJEdlbmVJRCA8LSBhbm5vJEdlbmVJRAoKdHJhbnNjcmlwdHNfbCA9IHN0cnNwbGl0KGV4b25pbmZvJFRyYW5zY3JpcHRJRHMsICAiXFwsIikKCiNTYXZlIHRoZSBjb3VudERhdGEsIHNhbXBsZVRhYmxlIGFuZCBleG9uaW5mbyBhbmQgdHJhbnNjcmlwdHMgaW4gb25lIG9iamVjdCBmb3IgZnVydGhlciB1c2UgCnNhdmUoY291bnREYXRhLCBzYW1wbGVUYWJsZSwgZXhvbmluZm8sIHRyYW5zY3JpcHRzX2wsIGZpbGU9IkFTX2NvdW50ZGF0YS5SRGF0YSIpCmBgYApOT1RFOiBCZSBjYXJlZnVsIHdpdGggdGhlIG5hbWVzIG9mIHRoZSBjb2x1bW5zIG9mIHRoZSBwcm9jZXNzZWQgYW5ub3RhdGlvbiBmaWxlLiBXaXRoIGEgZGlmZmVyZW50IEdURiBmaWxlLCB0aGUgaGVhZGVycyBjYW4gY2hhbmdlIGFuZCBjYXVzZSBhIHByb2JsZW0gd2hpbGUgcnVubmluZyB0aGlzIHN0ZXAuCgojIyMgNC4xLjMgQ3JlYXRlIERFWFNlcSBvYmplY3QKQ3JlYXRlIERFWFNlcSBvYmplY3QgdXNpbmcgREVYU2VxRGF0YVNldCBmdW5jdGlvbi4gVGhlIERFWFNlcSBvYmplY3QgY29sbGVjdHMgdG9nZXRoZXIgcmVhZCBjb3VudHMsIGV4b24gZmVhdHVyZSBpbmZvcm1hdGlvbiwgYW5kIHNhbXBsZSBpbmZvcm1hdGlvbi4gVXNlIHRoZSByZWFkIGNvdW50cyBnZW5lcmF0ZWQgaW4gc3RlcCAzIGFuZCB0aGUgZXhvbiBpbmZvcm1hdGlvbiBvYnRhaW5lZCBmcm9tIHRoZSBwcmV2aW91cyBzdGVwIHRvIGNyZWF0ZSB0aGUgREVYU2VxIG9iamVjdCBmcm9tIHRoZSBjb3VudCBtYXRyaXguIFRoZSBzYW1wbGVEYXRhIGFyZ3VtZW50IHRha2VzIGEgZGF0YSBmcmFtZSBpbnB1dCBkZWZpbmluZyB0aGUgc2FtcGxlcyAoYW5kIHRoZWlyIGF0dHJpYnV0ZXM6IGxpYnJhcnkgdHlwZSBhbmQgY29uZGl0aW9uKSwgJ2Rlc2lnbicgdXNlcyBzYW1wbGVEYXRhIHRvIGdlbmVyYXRlIGEgZGVzaWduIG1hdHJpeCBmb3IgdGhlIGRpZmZlcmVudGlhbCB0ZXN0aW5nIHVzaW5nIG1vZGVsIGZvcm11bGEgbm90YXRpb24uIE5vdGUgdGhhdCBhIHNpZ25pZmljYW50IGludGVyYWN0aW9uIHRlcm0sIGNvbmRpdGlvbjpleG9uLCBpbmRpY2F0ZXMgdGhhdCB0aGUgZnJhY3Rpb24gb2YgcmVhZHMgb3ZlciBhIGdlbmUgZmFsbGluZyBvbiBhIHBhcnRpY3VsYXIgZXhvbiBkZXBlbmRzIG9uIHRoZSBleHBlcmltZW50YWwgY29uZGl0aW9uIGkuZS4gdGhlcmUgaXMgQVMuIFNlZSBERVhTZXEgZG9jdW1lbnRhdGlvbiBmb3IgYSBmdWxsIGRlc2NyaXB0aW9uIG9mIHNldHRpbmcgdGhlIG1vZGVsIGZvcm11bGEgZm9yIG1vcmUgY29tcGxleCBleHBlcmltZW50YWwgZGVzaWducy4KRm9yIGZlYXR1cmUgaW5mb3JtYXRpb24tIGV4b24gaWRzLCBjb3JyZXNwb25kaW5nIGdlbmUgYW5kIHRyYW5zY3JpcHRzIGFyZSByZXF1aXJlZCBmcm9tIHRoZSBwcm9jZXNzZWQgYW5ub3RhdGlvbiBmaWxlLiAgCgpgYGB7cn0KZHhkID0gREVYU2VxRGF0YVNldCgKICAgY291bnREYXRhJGNvdW50cywKICAgc2FtcGxlRGF0YT1zYW1wbGVUYWJsZSwKICAgZGVzaWduPSB+IHNhbXBsZSArIGV4b24gKyBjb25kaXRpb246ZXhvbiwKICAgZmVhdHVyZUlEID0gZXhvbmluZm8kRXhvbklELAogICBncm91cElEID0gZXhvbmluZm8kR2VuZUlELAogICBmZWF0dXJlUmFuZ2VzID0gZXhvbmluZm8sCiAgIHRyYW5zY3JpcHRzID0gdHJhbnNjcmlwdHNfbCkKYGBgCgpOT1RFOiBUaGlzIHN0ZXAgbWlnaHQgdGFrZSBzb21lIHRpbWUuIE9uY2UgdGhlIG9iamVjdCBpcyBjcmVhdGVkLCBleHBsb3JlIHRoZSBjb2x1bW5zIGZvciBpbml0aWFsIHVuZGVyc3RhbmRpbmcvZXhwbG9yYXRpb24gb2YgdGhlIGRhdGEuCgpgYGB7cn0KIyBFeHBsb3JlL0luc3BlY3QgdGhlIERFWFNlcSBvYmplY3QKaGVhZChjb3VudHMoZHhkKSwgNSkKY29sRGF0YShkeGQpCnNwbGl0KHNlcV9sZW4obmNvbChkeGQpKSwgY29sRGF0YShkeGQpJGV4b24pCmhlYWQocm93UmFuZ2VzKGR4ZCksMykKc2FtcGxlQW5ub3RhdGlvbihkeGQpCmBgYAoKIyMjIDQuMS40IE5vcm1hbGl6YXRpb24gYW5kIERpc3BlcnNpb24gRXN0aW1hdGlvbgpOZXh0LCBwZXJmb3JtIG5vcm1hbGl6YXRpb24gYmV0d2VlbiBzYW1wbGVzIGFuZCBlc3RpbWF0ZSB0aGUgdmFyaWFuY2Ugb2YgdGhlIGRhdGEsIGR1ZSB0byBib3RoIFBvaXNzb24gY291bnQgbm9pc2UgZnJvbSB0aGUgZGlzY3JldGUgbmF0dXJlIG9mIFJOQS1zZXEgYW5kIGJpb2xvZ2ljYWwgdmFyaWFiaWxpdHksIHVzaW5nIHRoZSBmb2xsb3dpbmcgY29tbWFuZHMuCgpgYGB7cn0KZHhkICU8PiUgZXN0aW1hdGVTaXplRmFjdG9ycyAlPiUgZXN0aW1hdGVEaXNwZXJzaW9ucyAlVD4lIHBsb3REaXNwRXN0cyAKYGBgCgojIyMgNC4xLjUgVGVzdGluZyBmb3IgRGlmZmVyZW50aWFsIGV4b24gVXNhZ2UKQWZ0ZXIgdGhlIGVzdGltYXRpb24gb2YgdmFyaWF0aW9uLCB0ZXN0IGZvciBkaWZmZXJlbnRpYWwgZXhvbiB1c2FnZSBmb3IgZWFjaCBnZW5lIGFuZCBnZW5lcmF0ZSB0aGUgcmVzdWx0cy4gVGhlIHJlc3VsdHMgYXJlIGNhbGN1bGF0ZWQgYXQgRkRSIDEwJS4KYGBge3J9CmR4ZCAlPD4lIHRlc3RGb3JERVUgJT4lIGVzdGltYXRlRXhvbkZvbGRDaGFuZ2VzKGZpdEV4cFRvVmFyID0gImNvbmRpdGlvbiIpICNFc3RpbWF0ZSBmb2xkIGNoYW5nZXMKZHhyID0gREVYU2VxUmVzdWx0cyhkeGQpCmR4cgptY29scyhkeHIpJGRlc2NyaXB0aW9uCiMjIC0tLS10YWxseUV4b25zLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCnRhYmxlKGR4ciRwYWRqIDwgMC4xKQojIyAtLS0tdGFsbHlHZW5lcy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQp0YWJsZSh0YXBwbHkoZHhyJHBhZGogPCAwLjEsIGR4ciRncm91cElELCBhbnkpKQpgYGAKCiMjIyA0LjEuNiBWaXN1YWxpemF0aW9uIG9mIHNwbGljaW5nIGV2ZW50cwpWaXN1YWxpemUgc3BsaWNpbmcgZXZlbnRzIGZvciBzZWxlY3RlZCBnZW5lcyB1c2luZyB0aGUgZm9sbG93aW5nIGNvbW1hbmQKYGBge3J9CnBsb3RERVhTZXEoZHhyLCAiV25rMSIsIGxlZ2VuZD1UUlVFLCBjZXguYXhpcz0xLjIsIGNleD0xLjMsIGx3ZD0yKQpwbG90REVYU2VxKGR4ciwgIlRjZjciLCBsZWdlbmQ9VFJVRSwgY2V4LmF4aXM9MS4yLCBjZXg9MS4zLCBsd2Q9MikKcGxvdERFWFNlcShkeHIsICJNYm5sMSIsIGxlZ2VuZD1UUlVFLCBjZXguYXhpcz0xLjIsIGNleD0xLjMsIGx3ZD0yKQpwbG90REVYU2VxKGR4ciwgIkxlZjEiLCBsZWdlbmQ9VFJVRSwgY2V4LmF4aXM9MS4yLCBjZXg9MS4zLCBsd2Q9MikKcGxvdERFWFNlcShkeHIsICJOY29yMiIsIGxlZ2VuZD1UUlVFLCBjZXguYXhpcz0xLjIsIGNleD0xLjMsIGx3ZD0yKQpwbG90REVYU2VxKGR4ciwgIk1ibmwyIiwgbGVnZW5kPVRSVUUsIGNleC5heGlzPTEuMiwgY2V4PTEuMywgbHdkPTIpCmBgYAoKVG8gZGlzcGxheSB0cmFuc2NyaXB0cyB1c2UgZGlzcGxheVRyYW5zY3JpcHRzPVRSVUUKYGBge3J9CnBsb3RERVhTZXEoZHhyLCAiUnBzM2ExIiwgZGlzcGxheVRyYW5zY3JpcHRzPVRSVUUsIGxlZ2VuZD1UUlVFLCBjZXguYXhpcz0xLjIsIGNleD0xLjMsIGx3ZD0yKQpgYGAKCgpgYGB7cn0KcGxvdERFWFNlcShkeHIsICJMZWYxIiwgZXhwcmVzc2lvbj1GQUxTRSwgc3BsaWNpbmc9VFJVRSwgbGVnZW5kPVRSVUUsIGNleC5heGlzPTEuMiwgY2V4PTEuMywgbHdkPTIpCnBsb3RERVhTZXEoZHhyLCAiTWJubDEiLCBleHByZXNzaW9uPUZBTFNFLCBzcGxpY2luZz1UUlVFLCBsZWdlbmQ9VFJVRSwgY2V4LmF4aXM9MS4yLCBjZXg9MS4zLCBsd2Q9MikKYGBgCgpHZW5lcmF0ZSBwbG90cyBmb3IgYWxsIHRoZSBzaWduaWZpY2FudCBnZW5lcwpgYGB7cn0KZHhyID0gZHhyWyFpcy5uYShkeHIkcGFkaiksXQkKCWRnZW5lID0gZGF0YS5mcmFtZShwZXJHZW5lUVZhbHVlPXBlckdlbmVRVmFsdWUoZHhyKSkgJT4lIHJvd25hbWVzX3RvX2NvbHVtbigiZ3JvdXBJRCIpCgkKCWRleG9uID0gZHhyICU+JSBkYXRhLmZyYW1lKCkgJT4lIAoJCQlkcGx5cjo6c2VsZWN0KC1tYXRjaGVzKCJkaXNwZXJzaW9ufHN0YXR8Y291bnREYXRhfGdlbm9taWNEYXRhIikpICU+JSAKCQkJaW5uZXJfam9pbihkZ2VuZSkgJT4lIGFycmFuZ2UocGVyR2VuZVFWYWx1ZSkgJT4lIGRpc3RpbmN0KCkKCQoib3Blbnhsc3giICU+JSBsYXBwbHkobGlicmFyeSwgY2hhcmFjdGVyLm9ubHkgPSBUUlVFKSAlPiUgaW52aXNpYmxlCgpkZXhvbl9zaWcgPSBkZXhvbiAlPiUgZmlsdGVyKHBhZGogPCAwLjEpICAlPiUgZHBseXI6OnNlbGVjdCgtdHJhbnNjcmlwdHMpICU+JSAgLltvcmRlciguJHB2YWx1ZSksXSAlVD4lCndyaXRlLnhsc3goZmlsZSA9ICJSTkFTZXFfREVYU2VxX3NpZ25pZmljYW50X2dlbmVzLnhsc3giLCBzaGVldE5hbWUgPSAiTWJubDFfS08gdnMgV1QiLCBmaXJzdFJvdyA9IFRSVUUsIGhlYWRlclN0eWxlID0gY3JlYXRlU3R5bGUodGV4dERlY29yYXRpb24gPSAiQk9MRCIsIGhhbGlnbiA9ICJjZW50ZXIiKSkKIApwZGYoZmlsZT0gIkRFWFNlcV9zaWdfTWJubDFLT19GRFJfMTAucGRmIiwgaGVpZ2h0ID0gMTUsIHdpZHRoID0gMTUpCglzaWdfZ2VuZXMgPSBkZXhvbl9zaWcgJT4lIGZpbHRlcihwZXJHZW5lUVZhbHVlIDw9IDAuMSkgJSQlICBncm91cElEICU+JSB1bmlxdWUKCQoJZm9yIChnZW5laWQgaW4gc2lnX2dlbmVzICkgewoJCW5uID0gbnJvdyhkeHJbZHhyJGdyb3VwSUQgJWluJSBnZW5laWQsIF0pCgkJaWYgKG5uID4xICkgewoJCQlwbG90REVYU2VxKGR4ciwgZ2VuZWlkLCBsZWdlbmQ9VFJVRSxkaXNwbGF5VHJhbnNjcmlwdHM9VFJVRSwgc3BsaWNpbmc9VFJVRSwgIGNleC5heGlzPTEuMiwgY2V4PTEuMywgbHdkPTIgKQoJCX0KCX0KCWRldi5vZmYoKQpgYGAKClZvbGNhbm8gcGxvdCB0byB2aXN1YWxpemUgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzIApgYGB7cn0KRW5oYW5jZWRWb2xjYW5vKGRleG9uX3NpZywgbGFiID0gZGV4b25fc2lnJGZlYXR1cmVJRCwgeCA9ICdsb2cyZm9sZF9XVF9NYm5sMV9LTycsIHkgPSAncHZhbHVlJywgdGl0bGUgPSAnVm9sY2FubyBQbG90Jywgc3VidGl0bGUgPSAnTWJubDFfS08gdnMgV1QgKERFWFNlcSknLCBGQ2N1dG9mZiA9IDEsIGxhYlNpemUgPSA0LGxlZ2VuZFBvc2l0aW9uID0gInJpZ2h0Iix4bGltPSBjKC00LDQpLGNhcHRpb24gPSBicXVvdGUofkxvZ1syXX4gIkZvbGQgY2hhbmdlIGN1dG9mZiwgMjsgRkRSIDEwJSIpKQpgYGAKTk9URTogRW5oYW5jZWRWb2xjYW5vIHBhY2thZ2Ugd2FzIHVzZWQgdG8gZ2VuZXJhdGUgdGhlIGFib3ZlIHBsb3QsIGluc3RhbGwgdGhlIHBhY2thZ2UgaXQgaWYgbm90IGFscmVhZHkgaW5zdGFsbGVkLgoKIyMjIFNhdmUgUmRhdGEgb2JqZWN0cwpgYGB7cn0Kc2F2ZS5pbWFnZSgiTWJubDFfS09fRGV4c2VxLlJEYXRhIikKYGBgCgojIyA0LjIgVXNpbmcgTGltbWEgZGlmZlNwbGljZSBmb3IgZGlmZmVyZW50aWFsIHNwbGljaW5nIGFuYWx5c2lzCkZvbGxvdyB0aGUgUiBOb3RlYm9vayBmaWxlIOKAnEFTX2FuYWx5c2lzX1JOQVNlcS5SbWTigJ0uIEVuc3VyZSBzdGVwcyAxLTMgaGF2ZSBiZWVuIGZvbGxvd2VkIHRvIHByZXBhcmUgaW5wdXQgZmlsZXMgYmVmb3JlIHByb2NlZWRpbmcgZnVydGhlci4KCiMjIyA0LjIuMSBMb2FkIGxpYnJhcmllcwpgYGB7cn0KbGlicmFyeShsaW1tYSkKbGlicmFyeShlZGdlUikKYGBgCgojIyMgNC4yLjIgTm9uLXNwZWNpZmljIEZpbHRlcmluZy0KRXh0cmFjdCB0aGUgbWF0cml4IG9mIHJlYWQgY291bnRzIG9idGFpbmVkIGluIDMuIENyZWF0ZSBhIGxpc3Qgb2YgZmVhdHVyZXMgdXNpbmcg4oCYREdFTGlzdOKAmSBmdW5jdGlvbiBmcm9tIGVkZ2VSIHBhY2thZ2UsIHdoZXJlIHJvd3MgcmVwcmVzZW50IGdlbmVzIGFuZCBjb2x1bW5zIHJlcHJlc2VudCBzYW1wbGVzLiBOT1RFOiBBcyBhIG5vbi1zcGVjaWZpYyBmaWx0ZXJpbmcgc3RlcCwgY291bnRzIGFyZSBmaWx0ZXJlZCBieSBjcG0gPCAxIGluIHggb3V0IG9mIG4gc2FtcGxlcywgd2hlcmUgeCBpcyB0aGUgbWluaW11bSBudW1iZXIgb2YgcmVwbGljYXRlcyBpbiBhbnkgY29uZGl0aW9uLiBuID0gNiBhbmQgeCA9IDMgZm9yIHRoaXMgZXhhbXBsZSBkYXRhLgpgYGB7cn0KbXljb3VudHMgPSBjb3VudERhdGEkY291bnRzCiNDaGFuZ2UgdGhlIHJvd25hbWVzIG9mIHRoZSBjb3VudGRhdGEgdG8gZXhvbiBJZHMgaW5zdGVhZCBvZiBnZW5lcyBmb3IgdW5pcXVlIHJvd25hbWVzLgpyb3duYW1lcyhteWNvdW50cykgPSBleG9uaW5mbyRFeG9uSUQKZGdlIDwtIERHRUxpc3QoY291bnRzPW15Y291bnRzKQoKI05vbi1zcGVjaWZpYyBGaWx0ZXJpbmcKaXNleHByIDwtIHJvd1N1bXMoY3BtKGRnZSkgPiAxKSA+PTMKZGdlIDwtIGRnZVtpc2V4cHIsLGtlZXAubGliLnNpemVzPUZBTFNFXQoKI0V4dHJhY3QgdGhlIGFubm90YXRpb25zIGZvciBvbmx5IGZpbHRlcmVkIGNvdW50cyAKZXhvbmluZm8gPSBhbm5vICU+JSBmaWx0ZXIoRXhvbklEICVpbiUgcm93bmFtZXMoZGdlJGNvdW50cykpIAoKI0NvbnZlcnQgdGhlIGV4b25pbmZvIGludG8gR1JhbmdlcyBvYmplY3QKZXhvbmluZm8xIDwtIEdSYW5nZXMgKHNlcW5hbWVzPSAgZXhvbmluZm8kQ2hyLCAKcmFuZ2VzPSBJUmFuZ2VzIChzdGFydD1leG9uaW5mbyRTdGFydCwgZW5kPWV4b25pbmZvJEVuZCwgd2lkdGggPSBleG9uaW5mbyRXaWR0aCksIHN0cmFuZCA9IFJsZShleG9uaW5mbyRTdHJhbmQpKQptY29scyhleG9uaW5mbzEpJFRyYW5zY3JpcHRJRHMgPC0gZXhvbmluZm8kVHJhbnNjcmlwdElEcwptY29scyhleG9uaW5mbzEpJFRpY2tlciA8LSBleG9uaW5mbyRUaWNrZXIKbWNvbHMoZXhvbmluZm8xKSRFeG9uSUQgPC0gZXhvbmluZm8kRXhvbklECm1jb2xzKGV4b25pbmZvMSkkbiA8LSBleG9uaW5mbyRuCm1jb2xzKGV4b25pbmZvMSkkR2VuZUlEIDwtIGV4b25pbmZvJEdlbmVJRAp0cmFuc2NyaXB0c19sID0gc3Ryc3BsaXQoZXhvbmluZm8xJFRyYW5zY3JpcHRJRHMsICAiXFwsIikKYGBgCgojIyMgNC4yLjMgTm9ybWFsaXplIHRoZSBjb3VudHMtIApOb3JtYWxpemUgdGhlIGNvdW50cyBhY3Jvc3Mgc2FtcGxlcywgd2l0aCDigJhjYWxjTm9ybUZhY3RvcnPigJkgZnVuY3Rpb24gZnJvbSDigJhlZGdlUuKAmSBwYWNrYWdlIHVzaW5nIFRyaW1tZWQgTWVhbiBvZiBNIHZhbHVlcyAoVE1NIG5vcm1hbGl6YXRpb24gbWV0aG9kKS4gSXQgd2lsbCBjb21wdXRlIHNjYWxpbmcgZmFjdG9ycyB0byBhZGp1c3QgbGlicmFyeSBzaXplcy4KYGBge3J9CmRnZSA8LSBjYWxjTm9ybUZhY3RvcnMoZGdlKQpgYGAKCiMjIyA0LjIuNCBDcmVhdGUgZGVzaWduIG1hdHJpeCBmb3IgY29tcGFyaXNvbnMKVXNlIHNhbXBsZVRhYmxlIGFzIGdlbmVyYXRlZCBpbiBzdGVwIDQuMS4xIGFuZCBjcmVhdGUgdGhlIGRlc2lnbiBtYXRyaXguIFRoZSBkZXNpZ24gbWF0cml4IGNoYXJhY3Rlcml6ZXMgdGhlIGRlc2lnbi4gU2VlIHRoZSBMaW1tYSBVc2VyIEd1aWRlIFtyZWZdIGNoYXB0ZXJzIDggJiA5IGZvciBkZXRhaWxzIG9uIGRlc2lnbiBtYXRyaWNlcyBmb3IgbW9yZSBhZHZhbmNlZCBleHBlcmltZW50YWwgZGVzaWducy4KYGBge3J9ClRyZWF0IDwtIGZhY3RvcihzYW1wbGVUYWJsZSRjb25kaXRpb24pCmRlc2lnbiA8LSBtb2RlbC5tYXRyaXgofjArVHJlYXQpCmNvbG5hbWVzKGRlc2lnbikgPC0gbGV2ZWxzKFRyZWF0KQpgYGAKCiMjIyA0LjIuNSBEaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiB0ZXN0aW5nClJ1biDigJh2b29t4oCZIGZ1bmN0aW9uIG9mIOKAmGxpbW1h4oCZIHBhY2thZ2UgdG8gcHJvY2VzcyBSTkEtc2VxIGRhdGEgdG8gZXN0aW1hdGUgdmFyaWFuY2UgYW5kIGdlbmVyYXRlIHByZWNpc2lvbiB3ZWlnaHRzIHRvIGNvcnJlY3QgZm9yIFBvaXNzb24gY291bnQgbm9pc2UsIGFuZCB0aGUgZXhvbi1sZXZlbCBjb3VudHMgdG8gbG9nMi1jb3VudHMgcGVyIG1pbGxpb24gKGxvZ0NQTSkuIFRoZW4gcnVuIGxpbmVhciBtb2RlbGxpbmcgdXNpbmcg4oCYbG1maXTigJkgZnVuY3Rpb24gdG8gZml0IGxpbmVhciBtb2RlbHMgdG8gdGhlIGV4cHJlc3Npb24gZGF0YSBmb3IgZWFjaCBleG9uLiBDb21wdXRlIGVtcGlyaWNhbCBCYXllcyBzdGF0aXN0aWNzIGZvciBmaXR0ZWQgbW9kZWwgdXNpbmcg4oCYZUJheWVz4oCZIGZ1bmN0aW9uIHRvIGRldGVjdCBkaWZmZXJlbnRpYWwgZXhvbiBleHByZXNzaW9uIGJ5IHJhbmtpbmcgdGhlIGV4b25zLiBOZXh0LCBkZWZpbmUgYSBjb250cmFzdCBtYXRyaXggZm9yIHRoZSBleHBlcmltZW50YWwgY29tcGFyaXNvbnMgb2YgaW50ZXJlc3QuIFVzZSDigJhjb250cmFzdHMuZml04oCZIHRvIG9idGFpbiBjb2VmZmljaWVudHMgYW5kIHN0YW5kYXJkIGVycm9ycyBmb3IgZWFjaCBwYWlyIG9mIGNvbXBhcmlzb24uCmBgYHtyfQp2IDwtIHZvb20oZGdlLCBkZXNpZ24sIHBsb3Q9RkFMU0UpCmZpdCA8LSBsbUZpdCh2LGRlc2lnbikKZml0ICA8LSBlQmF5ZXMoZml0KQpjb2xuYW1lcyhmaXQpCnN1bW1hcnkoZGVjaWRlVGVzdHMoZml0KSkKCmNvbnQubWF0cml4IDwtIG1ha2VDb250cmFzdHMoCgkJCQlNYm5sMV9LT19XVCA9IE1ibmwxX0tPIC0gV1QsIAoJCQkJbGV2ZWxzPWRlc2lnbikKZml0MiAgPC0gY29udHJhc3RzLmZpdChmaXQsIGNvbnQubWF0cml4KQpzdW1tYXJ5KGRlY2lkZVRlc3RzKGZpdDIpKQpgYGAKCiMjIyA0LjIuNiBEaWZmZXJlbnRpYWwgU3BsaWNpbmcgYW5hbHlzaXMKUnVuIOKAmGRpZmZTcGxpY2XigJkgb24gdGhlIGZpdHRlZCBtb2RlbCB0byB0ZXN0IHRoZSBkaWZmZXJlbmNlcyBpbiBleG9uIHVzYWdlIG9mIGdlbmVzIGJldHdlZW4gd2lsZC10eXBlIGFuZCBrbm9ja291dCBhbmQgZXhwbG9yZSB0aGUgdG9wIHJhbmtlZCByZXN1bHRzIHVzaW5nIOKAmHRvcFNwbGljZeKAmSBmdWluY3Rpb24uOiB0ZXN0PeKAnXTigJ0gZ2l2ZXMgYSByYW5raW5nIG9mIEFTIGV4b25zLCB0ZXN0PeKAnXNpbWVz4oCdIGdpdmVzIGEgcmFua2luZyBvZiBnZW5lcy4gUnVuIGRpZmZTcGxpY2Ugb24gdGhlIGZpdHRlZCBtb2RlbCB0byB0ZXN0IHRoZSBkaWZmZXJlbmNlcyBpbiBleG9uIHJldGVudGlvbiBiZXR3ZWVuIHRoZSB3aWxkLXR5cGUgYW5kIGtub2Nrb3V0IHNhbXBsZXMuCgpgYGB7cn0KZXggPC0gZGlmZlNwbGljZShmaXQyLCBnZW5laWQgPSBleG9uaW5mbzEkR2VuZUlELCBleG9uaWQgPSBleG9uaW5mbzEkRXhvbklEKQojQ2hlY2sgdGhlIHRvcCBzcGxpY2luZyByZXN1bHRzIHdpdGggdG9wU3BsaWNlCnRvcFNwbGljZShleCkKI0V4b24tbGV2ZWwgc3RhdGlzdGljcyBmb3Igc3BsaWNpbmcgYWN0aXZpdHkgCnRzIDwtdG9wU3BsaWNlKGV4LCBuPUluZiwgRkRSPTAuMSwgdGVzdD0gInQiLCBzb3J0LmJ5ID0gImxvZ0ZDIikKI0dlbmUtbGV2ZWwgc3RhdGlzdGljcyBmb3Igc3BsaWNpbmcgYWN0aXZpdHkKdGcgPC0gdG9wU3BsaWNlKGV4LCBuPUluZiwgRkRSPTAuMSwgdGVzdCA9ICJzaW1lcyIpCnRzICUkJSBHZW5lSUQgJT4lIHVuaXF1ZSAlPiUgbGVuZ3RoCnRzICUkJSBFeG9uSUQgJT4lIHVuaXF1ZSAlPiUgbGVuZ3RoCmBgYAoKTk9URTogVGhpcyBzdGVwIHVzZXMgdGhlIGV4b24gaW5mb3JtYXRpb24gb2JqZWN0IGNyZWF0ZWQgaW4gNC4xLjIuIE1ha2Ugc3VyZSBpdCBpcyBsb2FkZWQgaW4gdGhlIGN1cnJlbnQgd29ya2luZyBlbnZpcm9ubWVudC4KCiMjIyA0LjIuNyBWaXN1YWxpemF0aW9uClBsb3QgdGhlIHJlc3VsdHMgd2l0aCB0aGUg4oCYcGxvdFNwbGljZeKAmSBmdW5jdGlvbiwgZ2l2aW5nIGdlbmUgb2YgaW50ZXJlc3QgaW4gdGhlIGdlbmVpZCBhcmd1bWVudC4gU2F2ZSB0aGUgdG9wIHJlc3VsdHMgc29ydGVkIGJ5IGxvZyBGb2xkIGNoYW5nZSBhbmQgYWxzbyBjYWxjdWxhdGUgcGVyIGdlbmUgc3RhdGljdGljcyB1c2luZyB0aGUgInNpbWVzIG1ldGhvZCIuIEZpcnN0IGNyZWF0ZSBhIGZ1bmN0aW9uIHRvIGFkZCBkaWZmZXJlbnQgc2hlZXRzIGFzIHdvcmtib29rcyBpbiBleGNlbC4gR2VuZXJhdGUgYSB2b2xjYW5vIHBsb3QgdG8gZXhoaWJpdCB0aGUgZXhvbnMuIApgYGB7cn0KI1Bsb3QgdG9wIDMgZ2VuZXMKcGxvdFNwbGljZShleCwgZ2VuZWlkPSJNYm5sMSIsIEZEUiA9IDAuMSkKcGxvdFNwbGljZShleCwgZ2VuZWlkPSJDY2RjODhjIiwgRkRSID0gMC4xKQpwbG90U3BsaWNlKGV4LCBnZW5laWQ9IlduazEiLCBGRFIgPSAwLjEpCiNQbG90IGV4cGVyaW1lbnRhbGx5IHZhbGlkYXRlZCBnZW5lcwpwbG90U3BsaWNlKGV4LCBnZW5laWQ9IkxlZjEiLCBGRFIgPSAwLjEpCnBsb3RTcGxpY2UoZXgsIGdlbmVpZD0iVGNmNyIsIEZEUiA9IDAuMSkKcGxvdFNwbGljZShleCwgZ2VuZWlkPSJOY29yMiIsIEZEUiA9IDAuMSkKCiNTYXZlIHRoZSBsaXN0IG9mIHRvcCBkaWZmZXJlbnRpYWxseSBzcGxpY2VkIGV4b25zIGF0IEZEUiAxMCUKc2lnIDwtIHRzW3RzJEZEUjwwLjEsXSAgJT4lICAuW29yZGVyKC4kUC5WYWx1ZSksXSAKd3JpdGUueGxzeChzaWcsIGZpbGUgPSAiUk5BU2VxX2RpZmZTcGxpY2Vfc2lnbmlmaWNhbnRfZ2VuZXMueGxzeCIsIHNoZWV0TmFtZSA9ICJNYm5sMV9LTyB2cyBXVCIsIGZpcnN0Um93ID0gVFJVRSwgaGVhZGVyU3R5bGUgPSBjcmVhdGVTdHlsZSh0ZXh0RGVjb3JhdGlvbiA9ICJCT0xEIiwgaGFsaWduID0gImNlbnRlciIpKQoKIyBGdW5jdGlvbiB0byBhZGQgck1BVFMgb3V0cHV0IGludG8gb25lIGV4Y2VsCmFkZC54bHN4LnNoZWV0IDwtIGZ1bmN0aW9uKGZpbGVOYW1lLCBzaGVldE5hbWUsIGRhdGEpewogIHdiIDwtIGxvYWRXb3JrYm9vayhmaWxlTmFtZSkKICBhZGRXb3Jrc2hlZXQod2IsIHNoZWV0TmFtZSA9IHNoZWV0TmFtZSkKICBmcmVlemVQYW5lKHdiLCBzaGVldCA9IHNoZWV0TmFtZSwgZmlyc3RSb3cgPSBUUlVFKQogIHdyaXRlRGF0YSh3Yiwgc2hlZXQgPSBzaGVldE5hbWUsIGRhdGEsIGhlYWRlclN0eWxlID0gY3JlYXRlU3R5bGUodGV4dERlY29yYXRpb24gPSAiQk9MRCIsIGhhbGlnbiA9ICJjZW50ZXIiKSkKICBzYXZlV29ya2Jvb2sod2IsIGZpbGUgPSBmaWxlTmFtZSwgb3ZlcndyaXRlID0gVFJVRSkKfQphZGQueGxzeC5zaGVldChmaWxlID0gIlJOQVNlcV9kaWZmU3BsaWNlX3NpZ25pZmljYW50X2dlbmVzLnhsc3giLCAiR2VuZS1sZXZlbCBTdGF0aXN0aWNzIiwgdHMxKQpgYGAKCiMjIyBWb2xjYW5vIHBsb3QtClZvbGNhbm8gcGxvdCBzaG93aW5nIHVwIGFuZCBkb3ducmVndWxhdGVkIGdlbmVzIGF0IHBDdXRvZmYgPSBGRFIgMTAlIGFuZCBGQyAyLgpgYGB7cn0KRW5oYW5jZWRWb2xjYW5vKHRzLCBsYWIgPSB0cyRFeG9uSUQsIHNlbGVjdExhYiA9IGhlYWQoKHRzJEV4b25JRCksMzAwMCkseGxhYiA9IGJxdW90ZSh+TG9nWzJdfiAnZm9sZCBjaGFuZ2UnKSwgeCA9ICdsb2dGQycsIHkgPSAnUC5WYWx1ZScsIHRpdGxlID0gJ1ZvbGNhbm8gUGxvdCcsIHN1YnRpdGxlID0gJ01ibmwxX0tPIHZzIFdUIChMaW1tYV9kaWZmU3BsaWNlKScsIEZDY3V0b2ZmID0gMSwgbGFiU2l6ZSA9IDQsbGVnZW5kUG9zaXRpb24gPSAicmlnaHQiLGNhcHRpb24gPSBicXVvdGUofkxvZ1syXX4gIkZvbGQgY2hhbmdlIGN1dG9mZiwgMTsgRkRSIDEwJSIpKQpgYGAKICAKIyMjIFNhdmUgdGhlIFIgb2JqZWN0CmBgYHtyfQpzYXZlLmltYWdlKCJNYm5sMV9LT19MaW1tYV9kaWZmU3BsaWNlLlJEYXRhIikKYGBgCgojIyA0LjMgVXNpbmcgck1BVFMgdG8gaWRlbnRpZnkgZGlmZmVyZW50IHR5cGVzIG9mIHNwbGljaW5nIGV2ZW50cwoKIyMjIDQuMy4xIERvd25sb2FkIGFuZCBJbnN0YWxsIHJNQVRTCkVuc3VyZSB0aGUgbGF0ZXN0IHZlcnNpb24gb2Ygck1BVFMgdjQuMS4xIChhbHNvIGtub3duIGFzIHJNQVRTIHR1cmJvIGR1ZSB0byB0aGUgaW5jcmVhc2VkIHByb2Nlc3NpbmcgdGltZSBhbmQgbGVzcyByZXF1aXJlbWVudHMgb2YgbWVtb3J5KSBpcyBpbnN0YWxsZWQgZWl0aGVyIHVzaW5nIGNvbmRhIG9yIGdpdGh1YiBpbiB0aGUgd29ya2luZyBkaXJlY3RvcnkuIAoKIyMjIDQuMy4yIFJlcXVpcmVkIEZpbGVzCkZvciBwYWlyd2lzZSBzcGxpY2luZyBhbmFseXNpcywgd2Ugd2lsbCBuZWVkIHNvcnRlZCBiYW0gZmlsZXMgZm9yIGFsbCB0aGUgc2FtcGxlcyBnZW5lcmF0ZWQgYWZ0ZXIgdGhlIHJlYWQgYWxpZ25tZW50IHN0ZXAgdXNpbmcgU1RBUi4gcHJlcGFyZSB0ZXh0IAlmaWxlcyBmb3IgdHdvIGNvbmRpdGlvbnMgYnkgY29weWluZyB0aGUgbmFtZSBvZiBiYW0gZmlsZXMgKGFsb25nIHdpdGggdGhlIHBhdGgpIHNlcGFyYXRlZCBieSDigJgs4oCZIGNvbW1hLiBGb2xsb3dpbmcgY29tbWFuZHMgc2hvdWxkIGJlIHJ1biBvbiB0aGUgbGludXggY29tbWFuZC1saW5lLSAKCmBgYHtiYXNofQpta2RpciByTUFUU19hbmFseXNpcwpjZCBiYW1zLwpscyAtcGQgIiRQV0QiLyogfCBncmVwICJXVCIgfCB0ciAnXG4nICcsJyA+IFd0LnR4dApscyAtcGQgIiRQV0QiLyogfCBncmVwICJNYiIgfCB0ciAnXG4nICcsJyA+IEtPLnR4dAojTW92ZSB0aGUgZmlsZXMgdG8gdGhlIG1haW4gd29ya2luZyBkaXJlY3RvcnkKbXYgKi50eHQgL3JNQVRTX2FuYWx5c2lzCmNkIHJNQVRTX2FuYWx5c2lzCmBgYAoKIyMjIDQuMy4zIFJ1biByTUFUUwoKUnVuIHRoZSBweXRob24gc2NyaXB0IHJtYXRzLnB5IHdpdGggdGhlIHR3byBpbnB1dCBmaWxlcyBnZW5lcmF0ZWQgaW4gdGhlIHByZXZpb3VzIHN0ZXAsIGFsb25nIHdpdGggdGhlIAlHVEYgYW5ub3RhdGlvbiBvYnRhaW5lZCBpbiAxLjEuMyBieSB0eXBpbmcgdGhlIGZvbGxvd2luZyBjb21tYW5kIG9uIGxpbnV4IGNvbW1hbmQtbGluZS4gCgpgYGB7cHl0aG9ufQpweXRob24gcm1hdHNfdHVyYm8vcm1hdHMucHkgLS1iMSBLTy50eHQgLS1iMiBXdC50eHQgLS1ndGYgYW5ub3RhdGlvbi5ndGYgLXQgcGFpcmVkIC0tcmVhZExlbmd0aCA1NiAtLW50aHJlYWQgOCAtLW9kIHJtYXRzX291dC8gLS10bXAgcm1hdHNfdG1wCmBgYAoKTk9URTpUaGUgcmVmZXJlbmNlIGFubm90YXRpb24gaW4gdGhlIGZvcm0gb2YgYSBHVEYgZmlsZSBpcyBhbHNvIHJlcXVpcmVkLiBNYWtlIHN1cmUgYm90aCB0aGUgZm9sZGVyIGNvbnRhaW5pbmcgYmFtIGZpbGVzIChiYW1zLykgYW5kIGFubm90YXRpb24gZmlsZSAoYW5ub3RhdGlvbi5ndGYpIGlzIHByZXNlbnQgaW4gdGhlIGN1cnJlbnQgcGF0aC9wcm92aWRlIHRoZSBjb3JyZWN0IHBhdGggaW4gdGhlIGFib3ZlIGNvbW1hbmQuIChBU19hbmFseXNpcykgQ2hlY2sgdGhlIHBhcmFtZXRlcnMgaWYgdGhlIGRhdGEgaXMgc2luZ2xlLWVuZCwgY2hhbmdlIHRoZSAtdCBvcHRpb24uIAoKVGhlIHJlc3VsdHMgd2lsbCBiZSBpbiB0aGUgc3BlY2lmaWVkIG91dHB1dCBkaXJlY3RvcnkgYnkgdGhlIC1vIG9wdGlvbihybWF0c19vdXQpLiBUaGUgLS10bXAgb3B0aW9uIGNyZWF0ZXMgYSB0ZW1wb3Jhcnkgc3ViZGlyZWN0b3J5IHRvIHN0b3JlIGFsbCB0aGUgaW50ZXJtZWRpYXRlIGZpbGVzIGluIHRoZSBhbmFseXNpcy4gVGhlIG91dHB1dCBmb2xkZXIgY29udGFpbnMgdGV4dCBmaWxlcyBmb3IgZGlmZmVyZW50IHNwbGljaW5nIGV2ZW50cyB3aXRoIHAtdmFsdWVzLiBUaGUgc3VtbWFyeS50eHQgZmlsZSBwcmVzZW50cyB0aGUgdGFibGUgb2YgYWxsIHRoZSBldmVudHMgZmFsbGluZyBpbiBkaWZmZXJlbnQgY2F0ZWdvcmllcyBvZiBBUyBldmVudHMuIFRoZSBtYWluIG91dHB1dCBmaWxlIHRoYXQgd2Ugd2lsbCBmb2N1cyBvbiBpcyAqLk1BVFMuSkNFQy50eHQsIHRoaXMgaW5jbHVkZXMgYm90aCByZWFkcyB0aGF0IHNwYW4ganVuY3Rpb25zIGRlZmluZWQgYnkgcm1hdHMgKEp1bmN0aW9uIENvdW50cykgYW5kIHJlYWRzIHRoYXQgZG8gbm90IGNyb3NzIGFuIGV4b24gYm91bmRhcnkgKEV4b24gQ291bnRzKS4gCgojIyMgNC4zLjQgRXhwbG9yaW5nIHJNQVRTIHJlc3VsdHMKClVzZSBCaW9jb25kdWN0b3IgcGFja2FnZSBjYWxsZWQgJ21hc2VyJyB0byBleHBsb3JlIHRoZSByTUFUUyByZXN1bHRzLiBMb2FkIHRoZSAqLkpDRUMgZmlsZXMgZm9yIGVhY2ggb2YgdGhlIDUgZXZlbnRzIGFuZCBmaWx0ZXIgYnkgY292ZXJhZ2UgaW5jbHVkaW5nIGFuIGF2ZXJhZ2Ugb2YgNSByZWFkcy4KYGBge3J9CmxpYnJhcnkobWFzZXIpICU+JSBpbnZpc2libGUKbWJubDEgPC0gbWFzZXIoInJNQVRTX2FuYWx5c2lzL3JtYXRzX291dC8iLCBjKCJXVCIsICJNYm5sMV9LTyIpLCBmdHlwZSA9ICJKQ0VDIikKI0ZpbHRlcmluZyBvdXQgZXZlbnRzIAptYm5sMV9maWx0IDwtIGZpbHRlckJ5Q292ZXJhZ2UobWJubDEsIGF2Z19yZWFkcyA9IDUpCmBgYAoKIyMjIDQuMy40IFZpc3VhbGl6aW5nIHJNQVRTIHJlc3VsdHMKU2VsZWN0IHRoZSBzaWduaWZpY2FudCBzcGxpY2luZyBldmVudHMgYXQgRmFsc2UgRGlzY292ZXJ5IFJhdGUgKEZEUikgMTAlIGFuZCBtaW5pbXVtIDEwJSBjaGFuZ2UgaW4gUGVyY2VudCBTcGxpY2VkIEluIChkZWx0YVBTSSkgdXNpbmcg4oCYdG9wRXZlbnRz4oCZIGZ1bmN0aW9uIGZyb20g4oCYbWFzZXLigJkgcGFja2FnZS4gTmV4dCAsIGNoZWNrIHRoZSBnZW5lIGV2ZW50cyBmb3IgaW5kaXZpZHVhbCBnZW5lcyBvZiBpbnRlcmVzdCAoc2FtcGxlIGdlbmUtV25rMSkgYW5kIHBsb3QgUFNJIHZhbHVlcyBmb3IgZWFjaCBzcGxpY2luZyBldmVudCBvZiB0aGF0IGdlbmUuIEdlbmVyYXRlIGEgdm9sY2FubyBwbG90IGJ5IHNwZWNpZnlpbmcgdGhlIGV2ZW50IHR5cGUuICAKCmBgYHtyfQojVG9wIHNwbGljaW5nIGV2ZW50cyBhdCAxMCUgRkRSIAptYm5sMV90b3AgPC0gdG9wRXZlbnRzKG1ibmwxX2ZpbHQsIGZkciA9IDAuMSwgZGVsdGFQU0kgPSAwLjEpCm1ibmwxX3RvcAojQ2hlY2sgdGhlIGdlbmUgZXZlbnRzIGZvciBhIHBhcnRpY3VsYXIgZ2VuZQojIyBSZXRyaWV2ZSBXbmsxIHNwbGljaW5nIGV2ZW50cwptYm5sMV93bmsxIDwtIGdlbmVFdmVudHMobWJubDFfZmlsdCwgZ2VuZVMgPSAiV25rMSIsIGZkciA9IDAuMSwgZGVsdGFQU0kgPSAwLjEpCm1hc2VyOjpkaXNwbGF5KG1ibmwxX3duazEsICJTRSIpCnBsb3RHZW5lUFNJKG1ibmwxX3duazEsIHR5cGUgPSAiU0UiLCBzaG93X3JlcGxpY2F0ZXMgPSBUUlVFKQoKICB2b2xjYW5vKG1ibmwxX2ZpbHQsIGZkciA9IDAuMSwgZGVsdGFQU0kgPSAwLjEsIHR5cGUgPSAiU0UiKSArIHhsYWIoImRlbHRhUFNJIikgK3lsYWIoIkxvZzEwIEFkai4gUHZhbHVlIikrIGdndGl0bGUoIlZvbGNhbm8gUGxvdCBvZiBleG9uIHNraXBwaW5nIGV2ZW50cyIpCmBgYApOT1RFOiBCeSBkZWZhdWx0IHRoZSAic3VtbWFyeS50eHQiIG9idGFpbmVkIGluIHJNQVRTIG91dHB1dCBmb2xkZXIgaXMgZ2VuZXJhdGVkIGZvciBGRFIgNSUuIFRvIGNoYW5nZSB0aGUgRkRSIG9yIG90aGVyIHBhcmFtZXRlcnMgcnVuIHRoZSBzdW1tYXJ5LnB5IHNjcmlwdCBsb2NhdGVkIGluIHJNQVRTIHNvdXJjZSBmb2xkZXIgKHJtYXRzX3R1cmJvL3JNQVRTX1Avc3VtbWFyeS5weSkgYXMtIAoKYGBge3B5dGhvbn0KcHl0aG9uIHJtYXRzX3R1cmJvL3JNQVRTX1Avc3VtbWFyeS5weSBybWF0c19vdXQgLS1wLWN1dG9mZiAwLjEgLS1zdW1tYXJ5LXByZWZpeCBybWF0c19zdW1tYXJ5CmBgYAogIApHZW5lcmF0ZSBhbiBleGNlbCB0YWJsZSBjb250YWluaW5nIHNpZ25pZmljYW50IGV2ZW50cyAoRkRSIDEwJSkgb2YgZGlmZmVyZW50IHR5cGVzIG9mIEFTIGV2ZW50cyBmcm9tIHJNQVRTIHJhbmFseXNpcy4gVXNlICJhZGQueGxzeCIgZnVuY3Rpb24gdG8gZ2VuZXJhdGUgYW4gZXhjZWwgc2hlZXQgc3VtbWFyaXppbmcgYWxsIHRoZSByTUFUUyByZXN1bHQuCgpgYGB7cn0KIyBSZWFkIHJNQVRTIHN1bW1hcnkgYW5kIGluZGl2aWR1YWwgdGV4dCBmaWxlcyBmb3IgYWxsIGV2ZW50cyBhbmQgY29tYmluZSB0aGVtIGludG8gb25lIHRhYmxlCiAgcmVhZC50YWJsZSgnck1BVFNfYW5hbHlzaXMvcm1hdHNfc3VtbWFyeV9GRFJfMC4xX0luY0xldmVsRGlmZmVyZW5jZV8wLnR4dCcsIGhlYWRlciA9IFQpICVUPiUKICB3cml0ZS54bHN4KGZpbGUgPSAick1BVFNfb3V0cHV0X3N1bW1hcnkueGxzeCIsIHNoZWV0TmFtZSA9ICJyTUFUU19TdW1tYXJ5IiwgZmlyc3RSb3cgPSBUUlVFLCBoZWFkZXJTdHlsZSA9IGNyZWF0ZVN0eWxlKHRleHREZWNvcmF0aW9uID0gIkJPTEQiLCBoYWxpZ24gPSAiY2VudGVyIikpCiAgCiAgcmVhZC50YWJsZSgnck1BVFNfYW5hbHlzaXMvcm1hdHNfb3V0L1NFLk1BVFMuSkNFQy50eHQnLCBoZWFkZXIgPSBUKSAlPiUKICAgIGZpbHRlcihGRFIgPD0gMC4xKSAlPiUgIC5bb3JkZXIoLiRQVmFsdWUpLF0gJVQ+JQogICAgYWRkLnhsc3guc2hlZXQoZmlsZSA9ICJyTUFUU19vdXRwdXRfc3VtbWFyeS54bHN4IiwgIlNFLk1BVFMuSkNFQyIsIC4pCiAgcmVhZC50YWJsZSgnck1BVFNfYW5hbHlzaXMvcm1hdHNfb3V0L0E1U1MuTUFUUy5KQ0VDLnR4dCcsIGhlYWRlciA9IFQpICU+JQogICAgZmlsdGVyKEZEUiA8PSAwLjEpICU+JSAgLltvcmRlciguJFBWYWx1ZSksXSAlVD4lCiAgICBhZGQueGxzeC5zaGVldChmaWxlID0gInJNQVRTX291dHB1dF9zdW1tYXJ5Lnhsc3giLCAiQTVTUy5NQVRTLkpDRUMiLCAuKQogIHJlYWQudGFibGUoJ3JNQVRTX2FuYWx5c2lzL3JtYXRzX291dC9BM1NTLk1BVFMuSkNFQy50eHQnLCBoZWFkZXIgPSBUKSAlPiUKICAgIGZpbHRlcihGRFIgPD0gMC4xKSAlPiUgIC5bb3JkZXIoLiRQVmFsdWUpLF0gJVQ+JQogICAgYWRkLnhsc3guc2hlZXQoZmlsZSA9ICJyTUFUU19vdXRwdXRfc3VtbWFyeS54bHN4IiwgIkEzU1MuTUFUUy5KQ0VDIiwgLikKICByZWFkLnRhYmxlKCdyTUFUU19hbmFseXNpcy9ybWF0c19vdXQvTVhFLk1BVFMuSkNFQy50eHQnLCBoZWFkZXIgPSBUKSAlPiUKICAgIGZpbHRlcihGRFIgPD0gMC4xKSAlPiUgIC5bb3JkZXIoLiRQVmFsdWUpLF0gJVQ+JQogICAgYWRkLnhsc3guc2hlZXQoZmlsZSA9ICJyTUFUU19vdXRwdXRfc3VtbWFyeS54bHN4IiwgIk1YRS5NQVRTLkpDRUMiLCAuKQogIHJlYWQudGFibGUoJ3JNQVRTX2FuYWx5c2lzL3JtYXRzX291dC9SSS5NQVRTLkpDRUMudHh0JywgaGVhZGVyID0gVCkgJT4lCiAgICBmaWx0ZXIoRkRSIDw9IDAuMSkgJT4lICAuW29yZGVyKC4kUFZhbHVlKSxdICVUPiUKICAgIGFkZC54bHN4LnNoZWV0KGZpbGUgPSAick1BVFNfb3V0cHV0X3N1bW1hcnkueGxzeCIsICJSSS5NQVRTLkpDRUMiLCAuKQoKYGBgCiAgCkdlbmVyYXRlIFNhc2hpbWkgcGxvdCBmb3IgdGhlIHNwbGljaW5nIGV2ZW50cyByZXN1bHQgb2J0YWluZWQgd2l0aCByTUFUUyBpbiBmb3JtIG9mIHRleHQgZmlsZXMgdXNpbmcg4oCYcm1hdHMyc2hhaGltaXBsb3TigJkgcGFja2FnZS4gRW5zdXJlIHRoZSB3b3JraW5nIGRpcmVjdG9yeSBpcyBzZXQgdG8gdGhlIHJtYXRzMnNhc2hpbWlwbG90IGZvbGRlciwgdG8gcnVuIHRoZSBweXRob24gc2NyaXB0IGJ5IHR5cGluZyB0aGUgZm9sbG93aW5nIGNvbW1hbmQgb24gbGludXggY29tbWFuZC1saW5lLgoKYGBge3B5dGhvbn0KcHl0aG9uIC4vc3JjL3JtYXRzMnNhc2hpbWlwbG90L3JtYXRzMnNhc2hpbWlwbG90LnB5IC0tYjEgLi4vYmFtcy9XVF9UaHltdXNfMS5iYW0sLi4vYmFtcy9XVF9UaHltdXNfMi5iYW0sLi4vYmFtcy9XVF9UaHltdXNfMy5iYW0gLS1iMiAuLi9iYW1zL01ibmwxS09fVGh5bXVzXzEuYmFtLC4uL2JhbXMvTWJubDFLT19UaHltdXNfMi5iYW0sLi4vYmFtcy9NYm5sMUtPX1RoeW11c18zLmJhbSAtdCBTRSAtZSAuLi9yTUFUU19hbmFseXNpcy9ybWF0c19vdXQvU0UuTUFUUy5KQy50eHQgIC0tbDEgV1QgLS1sMiBNYm5sMV9LTyAtLWV4b25fcyAxIC0taW50cm9uX3MgNSAtbyAuLi9yTUFUU19hbmFseXNpcy9ybWF0czJzaGFzbWlfb3V0cHV0CmBgYApOT1RFOiBUaGlzIHByb2Nlc3MgY2FuIGJlIHRpbWUtY29uc3VtaW5nIGFzIGl0IHdpbGwgZ2VuZXJhdGUgdGhlIFNhc2hpbWkgcGxvdCBmb3IgYWxsIHRoZSByZXN1bHRzIGluIHRoZSBldmVudHMgZmlsZS4gQ2hvb3NlIHRoZSB0b3AgcmVzdWx0cyAoZ2VuZSBuYW1lcyBhbmQgZXhvbnMpIGFzIGRpc3BsYXllZCBieSB0aGUgdG9wRXZlbnRzIGZ1bmN0aW9uIGZyb20gJ21hc2VyJyBhbmQgdmlzdWFsaXplIHRoZSBjb3JyZXNwb25kaW5nIFNhc2hpbWkgcGxvdC4KCk5PVEU6IFRoZSByZXN1bHRzIG9mIHJNQVRTIGZvciBleGFtcGxlLCB0aGUgY2hyb21vc29tZXMgaW4gU0UuTUFUUy5KQy50eHQgYXJlIG5hbWVkIHdpdGggYSDigJhjaHLigJkgcHJlZml4IGJ5IGRlZmF1bHQsIGhvd2V2ZXIgdGhlIGNocm9tb3NvbWVzIGluIHRoZSBpbnB1dCBiYW0gZmlsZSB2YXJpZXMgYWNjb3JkaW5nIHRvIGdpdmVuIGFubm90YXRpb24gZmlsZSBhbmQgYXJlIHNvbWV0aW1lcyByZXByZXNlbnRlZCB3aXRob3V0IHRoZSDigJhjaHLigJkgcHJlZml4IChFbnNlbWJsIGNvbnZlbnRpb24pLiBUaGlzIG1pZ2h0IGNhdXNlIGVycm9ycyB3aGVuIHJ1bm5pbmcgcm1hdHMyc2FzaGltaS4gSGVuY2UsIG1vZGlmeSB0aGUgQmFtIGlucHV0IHVzaW5nIHNhbXRvb2xzIHRvIGFkZCAnY2hyJyBwcmVmaXggYmVmb3JlIHJ1bm5pbmcgcm1hdHMyc2FzaGltaSBmb3IgaW5kaXZpZHVhbCBnZW5lcy4gUnVuIHRoZSBmb2xsb3dpbmcgY29tbWFuZCBvbiB0aGUgTGludXggY29tbWFuZC1saW5lIHRvIG1vZGlmeSBhbGwgYmFtIGZpbGVzIGFuZCBleHRyYWN0IHJNQVRTIHJlc3VsdCBmb3IgYSBwYXJ0aWN1bGFyIGdlbmUsIGUuZy4sICJXbmsxIi4gUmVwbGFjZSBXbmsxIHdpdGggeW91ciBnZW5lIG9mIGludGVyZXN0LgoKYGBge2Jhc2h9CmNkIGJhbXMKZm9yIGJhbSBpbiAuLypiYW07IGRvCiAgICBlY2hvICRiYW0KICAgIHNhbXRvb2xzIHZpZXcgLWggJGJhbSB8IHNlZCAtZSAnL15AU1Evcy9TTlw6L1NOXDpjaHIvJyAtZSAnL15bXkBdL3MvXHQvXHRjaHIvMicgfCBzYW10b29scyB2aWV3IC1iUyAtID4gdGVtcG9yYXJ5LmJhbQogICAgbXYgLWYgdGVtcG9yYXJ5LmJhbSAkYmFtCiAgICBzYW10b29scyBpbmRleCAkYmFtCmRvbmUKCiNFeHRyYWN0IHJNQVRTIG91dHB1dCBmb3IgJ1duazEnIGdlbmUKc2VkIC1uICIxcDsvV25rMS9wIiBTRS5NQVRTLkpDRUMudHh0ID4gd25rMV9TRV9KQ0VDLnR4dAojUmVwbGFjZSBXbmsxIHdpdGggeW91ciBnZW5lIG9mIGludGVyZXN0LgpgYGAKClJ1biB0aGUgcHl0aG9uIGNvbW1hbmQgb24gdGhlIGxpbnV4IGNvbW1hbmQtbGluZSB0byBnZXQgdGhlIHBkZiBmaWxlcyBmb3IgZWFjaCBldmVudCBvZiB0aGUgc2FtcGxlIGdlbmUgd25rMS4KCmBgYHtweXRob259CnB5dGhvbiAuL3NyYy9ybWF0czJzYXNoaW1pcGxvdC9ybWF0czJzYXNoaW1pcGxvdC5weSAtLWIxIC4uL2JhbXMvV1RfVGh5bXVzXzEuYmFtLC4uL2JhbXMvV1RfVGh5bXVzXzIuYmFtLC4uL2JhbXMvV1RfVGh5bXVzXzMuYmFtIC0tYjIgLi4vYmFtcy9NYm5sMUtPX1RoeW11c18xLmJhbSwuLi9iYW1zL01ibmwxS09fVGh5bXVzXzIuYmFtLC4uL2JhbXMvTWJubDFLT19UaHltdXNfMy5iYW0gLXQgU0UgLWUgLi4vck1BVFNfYW5hbHlzaXMvcm1hdHNfb3V0L3duazFfU0VfSkNFQy50eHQgIC0tbDEgV1QgLS1sMiBNYm5sMV9LTyAtLWV4b25fcyAxIC0taW50cm9uX3MgNSAtbyAuLi9yTUFUU19hbmFseXNpcy9ybWF0czJzaGFzbWlfb3V0cHV0X3duazEKYGBgCk5vdGU6RW5zdXJlIHRoZSB3b3JraW5nIGRpcmVjdG9yeSBpcyBjb3JyZWN0LiBUaGlzIHN0ZXAgd2lsbCBnZW5lcmF0ZSBzZXZlcmFsIHBsb3RzIGZvciBhbGwgdGhlIGV2ZW50cyBvZiAnV25rMScgZ2VuZS4gQ2hlY2sgdGhlIGV2ZW50IGlkIHRvIHNlbGVjdCBhIHNwZWNpZmljIG91dHB1dC4KClRoZSBTYXNoaW1pIHBsb3QgZm9yIGVhY2ggZXZlbnQgdHlwZSBvYnRhaW5lZCBmcm9tIHJtYXRzMnNhc2hpbWkgYXJlIHNob3duIGhlcmUtCgpgYGB7cn0KI0V4YW1wbGUgZ2VuZSAnV25rMSIKa25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoIlduazEtMS5wbmciKQojU0UgZXhhbXBsZSBnZW5lICdBZGQzJwprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiU0UtMS5wbmciKQojQTVTUyBleGFtcGxlIGdlbmUgJ0JhejJiJwprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiQTVTUy0xLnBuZyIpCiNBM1NTIGV4YW1wbGUgZ2VuZSAnTHNtMTRiJwprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiQTNTUy0xLnBuZyIpCiNNWEUgZXhhbXBsZSBnZW5lICdNdGExJwprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiTVhFLTEucG5nIikKI1JJIGV4YW1wbGUgZ2VuZSAnQXJwcDIxJwprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiUkktMS5wbmciKQpgYGAKCk5PVEU6IFRoZSBwZGYgd2FzIGNvbnZlcnRlZCB0byBpbWFnZSB0byBpbmNsdWRlIHRoZW0gaW4gdGhlIFJNYXJrZG93biBmaWxlIHVzaW5nICdwZGZ0b3BwbScgb24gdGhlIGxpbnV4IGNvbW1hbmQtbGluZS4gKEVnOiBwZGZ0b3BwbSBBNVNTX0JhejJiLnBkZiAgQTVTUyAtcG5nKQoKIyMgUHJpbnQgU2Vzc2lvbiBJbmZvLQpgYGB7cn0KICBwcmludChzZXNzaW9uSW5mbygpKQpgYGAK