Introduction

The content of this R notebook file includes all the codes requied for the Alternative PolyAdenylation (APA) analysis using 3p-Seq data. Refer to ‘PART 2- Alternative polyadenylation (APA) analysis using 3p-seq’ of the Protocol section. NOTE: To proceed with each step below to get the results, type the bash commands on Linux command-line then press Enter, or type the R codes in R console then press Enter.

0. Preparation: Installation of tools and packages used in this analysis

Ensure the related tools and packages used for APA analysis are installed correctly to guarantee the successful running of the following pipeline. As the analysis is required to be performed both on the Linux command-line and in the R console, and perl is expected to be used for data processing, ensure all of the environments are prepared at the beginning.

0.1 Installation of bash tools and packages

0.1.1 Installation of conda

conda is a popular and flexible package manager that allows convenient installation of packages with their dependencies across all platforms. To assist the further installation of related bash tools/packages required in this analysis, install conda at first by following the installing instruction of Anaconda (conda package manager) document at https://docs.anaconda.com/anaconda/install/. Ensure the system requirements and the installer are selected properly.

0.1.2 Installation of bash tools and packages using conda

Install all the required packages in this analysis using conda on Linux command-line.

conda install -c daler sratoolkit 
conda install -c conda-forge parallel
conda install -c bioconda bowtie sratoolkit cutadapt samtools bedtools deeptools

0.2 Installation of R packages

Install all the R packages required in this analysis in R console if not installed.

bioc_packages <- c("DEXSeq", "Rsubread", "EnhancedVolcano", "edgeR", "limma", "GenomicRanges") 
packages <- c("tidyverse", "BiocManager", "openxlsx") 
#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) 
}

1. Data downloading and pre-processing

1.1 Preparation of the project directory

Create the project directory APA_analysis and the raw data directory raw_data.

# Create and open the project directory
PROJ_DIR=APA_analysis/
mkdir $PROJ_DIR
cd $PROJ_DIR

# Create the raw data directory 
RAW_DATA=raw_data
mkdir -p $RAW_DATA

1.2 Raw data downloading and fastq files preparation

1.2.1 Raw data downloading

Given the Sequence Read Archive [SRA] Accession Ids (1553129 to 1553136) in sequence, download the raw data in parallel from SRA using the prefetch command from SRA toolkit (v2.10.8) and the parallel command from GNU parallel utility. Save the rsa files in the directory raw_data.

# Open the raw_data directory for raw data storage
cd $RAW_DATA

# Download SRA files using accession numbers
seq 1553129 1553136 | parallel prefetch SRR{}

1.2.2 fastq files preparation

Extract fastq.gz files from the archive using the fastq-dump command from the SRA toolkit. Ensure all the paths and names of the rsa files are listed correctly.

# Convert sra files to fastq files for alignment
parallel -j 3 fastq-dump --gzip --skip-technical --read-filter pass --dumpbase --split-e --clip --origfmt {} ::: SRR1553129/SRR1553129.sra SRR1553130/SRR1553130.sra SRR1553131/SRR1553131.sra SRR1553132/SRR1553132.sra SRR1553133/SRR1553133.sra SRR1553134/SRR1553134.sra SRR1553135/SRR1553135.sra SRR1553136/SRR1553136.sra

Unzip the compressed fastq.gz files in parallel using the command gunzip to obtain fastq files.

# Unzip the fastq.gz data to gain fastq files
parallel “gunzip {}” :::$RAW_DATA/*.fastq.gz

# Return to the project directory for reference downloading
cd .. 

1.3 Reference genome and annotations downloading

Download the reference genome and annotations for Mouse (Genome assembly mm10) in the project directory using the command wget, then decompress the genome annotation file using the command unzip. Download the reference file for the mouse chromosome sizes from UCSC using the command wget. The reference genome file and its annotations (the bowtie index of mm10) here are saved in the directory mm10, and the the reference file for the mouse chromosome sizes is named as mm10.chrom.sizes.txt.

# Download the bowtie index of mm10
wget https://genome-idx.s3.amazonaws.com/bt/mm10.zip
unzip mm10.zip

# Download the mm10.chrom.sizes
wget https://hgdownload-test.gi.ucsc.edu/goldenPath/mm10/bigZips/mm10.chrom.sizes

1.4 Data pre-processing, read alignment, files split and conversion

1.4.1 Data pre-processing

1.4.1.1 Illumina Adapter trimming

To remove the influence of adapter sequence on short reads (~35 bases) gained in polyA-seq, perform Illumina adapter trimming with the command cutadapt. Save the results in the newly created directory adapter.trimming.results.

TRIM_DIR=adapter.trimming.results
mkdir $TRIM_DIR

for fq in $RAW_DATA/*.fastq
do
OUTPUT=$(basename ${fq}| sed 's/_pass.fastq//g');
cutadapt -a "AGATCGGAAGAGC"  -m 15 -n 3 ${fq} | fastx_reverse_complement -i > $TRIM_DIR\/${OUTPUT}_trimmed.fastq
done

1.4.1.2 Obtaining sense strand sequence

Ensure the read sequence used for genome alignment is consistent with the sense strand sequence. According to the description of library preparation steps in the original article, polyA-seq was performed on the antisense strand. Therefore, after adapter trimming, perform reverse complementary operation using the fastx_reverse_complement command from FASTX toolkit to obtain the sense strand sequence.

TRIMRC_DIR=trimmed.rc.data
mkdir $TRIMRC_DIR

for fq in $RAW_DATA/*.fastq
do
OUTPUT=$(basename ${fq}| sed 's/_pass.fastq//g');
fastx_reverse_complement -i $TRIM_DIR\/${OUTPUT}_trimmed.fastq -o $TRIMRC_DIR\/${OUTPUT}_trimmed_rc.fastq
done

NOTE: This step is specific to the PolyA-seq assay used.

1.4.2 Read alignment

Map reads to mouse genome assembly using bowtie aligner and save the aligned SAM files in the directory bamfiles.

SAM_DIR=samfiles
mkdir $SAM_DIR

for fq in $RAW_DATA/*.fastq
do
OUTPUT=$(basename ${fq}| sed 's/_pass.fastq//g');
bowtie -n 2 -k 100 --best --strata -p 10  -x mm10/genome  --un unmapped_out $TRIMRC_DIR\/${OUTPUT}_trimmed_rc.fastq -S > $SAM_DIR\/${OUTPUT}.sam
done

1.4.3 File split, normalization and coversion

To prepare for genome coverage visualization, convert SAM files to BAM files, then sort and index BAM files for each sample using samtools commands before proceeding to further steps. Save the results in the directory bamfiles by typing the following commands and press Enter.

BAM_DIR=bamfiles
mkdir $BAM_DIR

for fq in $RAW_DATA/*.fastq
do
OUTPUT=$(basename ${fq}| sed 's/_pass.fastq//g');
samtools view -bS $SAM_DIR\/${OUTPUT}.sam -o $BAM_DIR\/${OUTPUT}.bam
samtools sort $BAM_DIR\/${OUTPUT}.bam -o $BAM_DIR\/${OUTPUT}.sorted.bam
samtools index $BAM_DIR\/${OUTPUT}.sorted.bam
done

To generate BigWig files for the read coverage visualization from BAM files, firstly apply genomeCoverageBed command from bedtools to generate bedgraph files, and use the parameter split and strand to split files based on their strand. Save the results in the directory bedgraphfiles by typing the following commands and press Enter.

BDG_DIR=bedgraphfiles
mkdir $BDG_DIR

for fq in $RAW_DATA/*.fastq
do
OUTPUT=$(basename ${fq}| sed 's/_pass.fastq//g');
# Generate genomeCoverageBed files
genomeCoverageBed -split -strand + -bga -ibam $BAM_DIR\/${OUTPUT}.sorted.bam | sort -k 1,1 -k2,2n > $BDG_DIR\/${OUTPUT}.bedgraph_fwd
genomeCoverageBed -split -strand - -bga -ibam $BAM_DIR\/${OUTPUT}.sorted.bam | sort -k 1,1 -k2,2n > $BDG_DIR\/${OUTPUT}.bedgraph_rev
done

If the counts are used for the read coverage visualization, the height of read peaks is influenced by library size. Therefore, normalization of read counts by sequencing depth/library size is a critical step in the read coverage visualization. Calculate the library size of each sample using the samtools command, and save the results as txt files in the directory librarysizefiles. Then use perl -lane command to calculate the counts per million (CPM) number normalized by the sample library size. Save the results in the directory bedgraphfiles.norm by typing the following commands and press Enter.

LBSIZE_DIR=librarysizefiles
mkdir $LBSIZE_DIR

BDGNORM_DIR=bedgraphfiles.norm
mkdir $BDGNORM_DIR

for fq in $RAW_DATA/*.fastq
do
OUTPUT=$(basename ${fq}| sed 's/_pass.fastq//g');
# Calculate the library size
samtools view -F 0x105 -c  $BAM_DIR\/${OUTPUT}.sorted.bam > $LBSIZE_DIR\/${OUTPUT}.temp

# Generate the normalized genomeCoverageBed files for the coverage visualisation to decide the flanking regions included in the annotation file
libsize=$(cat $LBSIZE_DIR\/${OUTPUT}.temp)
perl -lane '$xx=$F[3]*1e6/'$libsize'; print "$F[0]\t$F[1]\t$F[2]\t$xx"' $BDG_DIR\/${OUTPUT}.bedgraph_fwd > $BDGNORM_DIR\/${OUTPUT}.bedgraph_fwd_norm
perl -lane '$xx=$F[3]*1e6/'$libsize'; print "$F[0]\t$F[1]\t$F[2]\t$xx"' $BDG_DIR\/${OUTPUT}.bedgraph_rev > $BDGNORM_DIR\/${OUTPUT}.bedgraph_rev_norm
done

Generate BigWig files using the UCSC wigToBigWig command, and save the results in the directory wigToBigWig.

BWNORM_DIR=normalized.BigWigfiles
mkdir $BWNORM_DIR
for fq in $RAW_DATA/*.fastq
do
OUTPUT=$(basename ${fq}| sed 's/_pass.fastq//g');
# Generate bigwig files for the check in the genome browser/IGV
wigToBigWig $BDGNORM_DIR\/${OUTPUT}.bedgraph_fwd_norm mm10.chrom.sizes.txt $BWNORM_DIR\/${OUTPUT}.fwd.bw
wigToBigWig $BDGNORM_DIR\/${OUTPUT}.bedgraph_rev_norm mm10.chrom.sizes.txt $BWNORM_DIR\/${OUTPUT}.rev.bw
done

2. Preparation of pA sites annotations

NOTE: The processing steps of the pA sites annotation file is preformed firstly in R console, then on Linux command-line for the read coverage visualzation at pA sites and the generation of final annotation file.

2.1 pA sites annotations downloading

Download the pA site annotation file “atlas.clusters.2.0.GRCm38.96.tsv” from the PolyASite 2.0 database at https://polyasite.unibas.ch. To find the link for the download, select ‘Atlas’ on the top bar of the main website, then click the ‘Atlas BED file’ under the subtitle ‘Mus musculus: v2.0 (GRCm38.96)’. The tsv file is expected to start downloading automatically.

2.2 Loading required libraries

Type the following commands in R console and press Enter to load the required libraries in R.

library(magrittr)

2.4 Modifying the annotation format for adaption

As the packages used here for APA analysis are designed for differential exon usage and splicing analysis, the format of the annotations needs to be modified in two aspects for the adaption to APA analysis: 1) Change the format of unique feature IDs (namely unique pA site cluster IDs) from ‘group ID: feature ID: strand’ to group ID: feature ID’ (namely ‘gene ID: pA site cluster ID’). For the convenience of downstream analysis and to avoid duplicates in the pA site cluster IDs, remove the last common in each original cluster ID and replace the strand information + and - with letters F (Forward) and R(Reverse) individually; 2) Add ‘chr’ before each chromosome number; In addition to the changes above, for the visualization of reads coverage and pA cleavage sites: 3) Replace the pA site coordinates by their corresponding cleavage sites.

anno$name <- anno$strand %>% replace(. %in% "+", "F") %>% # Replace the strand information '+' with 'F'
  replace(. %in% "-", "R") %>% # Replace the strand information '-' with 'R'
  paste0(anno$gene_name, ":", anno$rep, .) # Generate new unique pA site cluster IDs

anno$chrom <- paste0("chr", anno$chrom) # change the format of chromosome column for downstream analysis

# Replace coordinates of pA sites with their corresponding cleavage sites
anno$chromStart <-  anno$rep -1
anno$chromEnd <-  anno$rep

2.5 Generation of annotation files for each strand

To prepare for the read coverage visualization at pA sites, split the processed annotation file into two sperate files according to the strand information, then save the two files separatly in bed format as ‘pA_annotation.fwd.bed’ and ‘pA_annotation.rev.bed’ in the current working directory.

# Generate annotation files for each strand for genomeCoverage plotting
anno %T>% 
  {
    anno.fwd = .[.$strand %in% "+", ]
    write.table(anno.fwd, file = "pA_annotation.fwd.bed", row.names = FALSE, col.names = FALSE, sep = "\t", quote = FALSE)
    } %>% {
    anno.rev <- .[.$strand %in% "-", ]
    write.table(anno.rev, file = "pA_annotation.rev.bed", row.names = FALSE, col.names = FALSE, sep = "\t", quote = FALSE)
  }

2.6 Saving the annotation file in bed format

Save the processed annotation file in bed format for further modification after pA sites read coverage visualization.

anno %>% write.table(file = "pA_annotation.bed", row.names = FALSE, col.names = FALSE, sep = "\t", quote = FALSE)

2.7 Read coverage visualization at pA sites

According to the 3p-seq protocol, the reads are expected to be mapped to the region around, rather than specifically at pA cleavage sites. Therefore, visualization to check the peak dispersion of mapped reads at pA sites to optimize the annotation file for this data is a critical step in the annotation file processing.

Visualize the peaks of aligned reads at pA cleavage sites using computeMatrix and plotHeatmap from deepTools by typing the following commands on Linux command-line and press Enter. Remeber to scale the distance upstream (-b) and downstream (-a) of the cleavage sites if the peak cannot be visualized completely. Save matrix results in the created directory compute.matrix.results, and the plots in pdf in the created directory plotHeatmap.results.

CM_DIR=compute.matrix.results
mkdir $CM_DIR

HM_DIR=plotHeatmap.results
mkdir $HM_DIR

for fq in $RAW_DATA/*.fastq
do
OUTPUT=$(basename ${fq}| sed 's/_pass.fastq//g');
# Visualizing the read coverage at pA sites
computeMatrix reference-point -S $BWNORM_DIR\/${OUTPUT}.fwd.bw -R pA_annotation.fwd.bed -a 100 -b 100 -o $CM_DIR\/${OUTPUT}.fwd.ud100.gz --referencePoint center --sortRegions no
plotHeatmap -m $CM_DIR\/${OUTPUT}.fwd.ud100.gz -o $HM_DIR\/${OUTPUT}.fwd.ud100.pdf

computeMatrix reference-point -S $BWNORM_DIR\/${OUTPUT}.rev.bw -R pA_annotation.rev.bed -a 100 -b 100 -o $CM_DIR\/${OUTPUT}.rev.ud100.gz --referencePoint center --sortRegions no
plotHeatmap -m $CM_DIR\/${OUTPUT}.rev.ud100.gz -o $HM_DIR\/${OUTPUT}.rev.ud100.pdf
done

2.8 Modifying pA site coordinates to generate final annotation file

NOTE: Through the visualization of read coverage at pA sites, it was found that the peaks of the mapped reads were mainly dispersed within ~60 bp upstream of the cleavage sites (Refer to the supplementary figure “merge.heatmap.png”). Therefore, coordinates of pA sites from the annotation file are supposed to be extended to 60 bp upstream of their cleavage sites in the analysis of the PolyA-seq data. Remember that depending on the specific 3’ end sequencing protocol used, this step will need to be optimized for assays other than PolyA-seq.

Extend the coordinates of pA sites to 60 bp upstream of their cleavage sites in the annotation file by typing the following command on the Linux command-line. Download the chromosome size annotation file from UCSC using the command wget. Save the processed annotation file in bed format for downstream analysis as “flanking60added.pA_annotation.bed”.

# Download the chromosome size annotation file 'mm10.chrom.sizes.txt'
wget https://hgdownload-test.gi.ucsc.edu/goldenPath/mm10/bigZips/mm10.chrom.sizes

# Change the added value based on the genomeCov results
bedtools slop -i pA_annotation.bed -g mm10.chrom.sizes.txt -l 60 -r 0 -s > flanking60added.pA_annotation.bed

NOTE: All the steps in the following sections are perfomed in R console.

3 Counting Reads

3.1 Loading required libraries

Type the following commands in R console and press Enter to load the required libraries in R.

c("Rsubread","tidyverse") %>% lapply(library, character.only = TRUE) %>% invisible
Warning message:
In readChar(file, size, TRUE) : truncating string with embedded nuls

3.2 Preparation of the annotation file in R

Load the pA site annotation information from the bed file using the read.table function, then select eight columns used for the differential APA analysis, including unique pA site cluster ID, chromosome name, start and end positions of the pA site cluster, strand on which the cluster is encoded, the overlapping gene ensembles and their corresponding gene symbols, and the representative pA site of the cluster. NOTE: Before running this step, ensure the path of the bed file is correct as the input of the read.table function.

anno <- read.table(file = "/Users/evelynzheng/3PSeq_files/flanking60added.pA_annotation.bed", stringsAsFactors = FALSE, check.names = FALSE, header = FALSE, sep = "")
colnames(anno) <- c("Chr", "Start", "End", "GeneID", "Score", "Strand", "repID", "Anno", "Symbol", "Ensembl")
anno <- dplyr::select(anno, GeneID, Chr, Start, End, Strand, Ensembl, Symbol, repID)

3.3 Applying feactureCounts to acquire the raw read counts

Read all bam files obtained as input for featureCounts() from Rsubread package to acquire the raw counts of reads mapped to each annotated pA site, the format of which is expected to be a matrix of counts associated with each feature with rows representing exons(features) and columns representing samples. Save the count table as the RData file “APA_countData.RData” for APA analysis using different tools. NOTE: Before running this step, ensure the folder containing bam files is in the current working directory.

# As the running of featureCounts requires around 25 G memory, during the generation of this Rmd file, this step was running on server and the result was saved as "countData.RData" and loaded in the next R session.
countData <- dir("bamfiles", pattern="sorted.bam$", full.names = TRUE) %>%  
  # Read all bam files as input for featureCounts
  featureCounts(annot.ext = anno, isGTFAnnotationFile = FALSE, minMQS = 0, useMetaFeatures = TRUE, 
                allowMultiOverlap = TRUE, largestOverlap = TRUE, strandSpecific = 1,
                countMultiMappingReads = TRUE, primaryOnly = TRUE, isPairedEnd = FALSE, nthreads = 12) %T>% 
save(file = "APA_countData.RData")

NOTE: Be conscious to change any of the parameters listed in the featureCounts function. Modify the strandSpecific parameter to ensure it is consistent with the sequencing direction of the 3’ end sequencing assay used (empirically, visualizing the data in a genome browser over genes on plus and minus strands will clarify this).

3.4 Applying non-specific filtering of countData

As filtering of the raw read counts can significantly improve the statistical robustness in differential pA site usage tests, apply read counts filtering on two levels: 1) On gene-level, remove genes with only one pA site, on which differential pA site usage cannot be defined; 2) On gene-level, remove genes annotated as NA, which will hinder the downstream processing and analysis; 3) On pA site-level, apply non-specific filtering based on coverage: filter counts by cpm (counts per million) less than 1 in x out of n samples, where x is the minimum number of replicates in any condition. n = 8 and x = 2 for this example data. NOTE: Before running this step, ensure the path of the RData file is correct as the input of the load function.

load(file = "/Users/evelynzheng/3PSeq_files/APA_countData.RData") # Skip this step if it is already loaded 

# Non-specific filtering: Remove the pA sites not differentially expressed in the samples
countData <-  countData$counts %>% as.data.frame %>% .[rowSums(edgeR::cpm(.) > 1) >= 2, ]
anno %<>% .[.$GeneID %in% rownames(countData), ] 

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

4 Differential polyadenylation sites usage analysis using DEXSeq and diffSplice pipelines

4.1 Using DEXSeq package

NOTE: As a contrast matrix cannot be defined for the DEXSeq pipeline, the differential APA analysis of each two experimental conditions has to be developed separatly. Here the differential APA analysis of the condition wild type (WT) and Mbnl 1/2 double knockout (DKO) is developed as an example to explain the procedure. The analysis of the contrast pairs WT and KD, WT and Ctrl are attached at the end.

4.1.1 Loading required libraries

Type the following commands in R console and press Enter to load the required libraries in R.

c("DEXSeq", "GenomicRanges") %>% lapply(library, character.only = TRUE) %>% invisible

4.1.2 Creating the sample table to define the experimental design

Create the sampleTable, which consists of details of each sample with its library-type and condition. Ensure the order of row names is consistent with the order of bam file names used by featureCounts to count the reads.

sampleTable1 <- data.frame(row.names = c("WT_1","WT_2","DKO_1","DKO_2"), 
                           condition = c(rep("WT", 2), rep("DKO", 2)), 
                           libType = rep("single-end", 4))

4.1.3 Preparing the pA sites information file using Bioconductor GRanges

pA site information in the form of GRanges objects (https://bioconductor.org/packages/release/bioc/html/GenomicRanges.html) is required as an input to create DEXSeq object. Extract the annotation from the processed annotation object to create PASinfo object for the preparation of DEXSeq object construction.

# Prepare the GRanges object for DEXSeqDataSet object construction
PASinfo <- GRanges(seqnames = anno$Chr,
                   ranges = IRanges(start = anno$Start, end = anno$End),
                   strand = Rle(anno$Strand))
mcols(PASinfo)$PASID <- anno$repID
mcols(PASinfo)$GeneEns <- anno$Ensembl
mcols(PASinfo)$GeneID <- anno$Symbol

# Prepare the new feature IDs, replace the strand information with letters to match the current pA site clusterID
new.featureID <- anno$Strand %>% as.character %>% replace(. %in% "+", "F") %>% replace(. %in% "-", "R") %>% paste0(as.character(anno$repID), .)

4.1.4 Creating the DEXSeq object

Create the DEXSeq object using the function DEXSeqDataSet to collect: 1) the filtered read counts generated in step 3; 2) sample information from the sampleTable; 3) design matrix which is generated based on the sampleData and a model formula notation for the differential APA testing; 4) feature information including pA site IDs and their corresponding gene symbols.

countData1 <- dplyr::select(countData, SRR1553129.sorted.bam, SRR1553130.sorted.bam, 
                            SRR1553131.sorted.bam, SRR1553132.sorted.bam) # Select the read counts of the condtion WT and DKO
colnames(countData1) <- rownames(sampleTable1) # Rename the columns of countData using sample names in sampleTable
dxd1 <- DEXSeqDataSet(countData = countData1,
                     sampleData = sampleTable1, 
                     design = ~ sample + exon + condition:exon,
                     featureID = new.featureID,
                     groupID = anno$Symbol,
                     featureRanges = PASinfo)
converting counts to integer mode
some variables in design formula are characters, converting to factors

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

# Explore/Inspect the DEXSeq object
head(counts(dxd1), 5)
                [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8]
Akap12:4359096F   74  110   21   29  492  671  203  201
Akap12:4359468F  492  671  203  201   74  110   21   29
Lats1:7716088F    46   82   41   25  283  416  320  257
Lats1:7716124F    85  108   82   66  244  390  279  216
Lats1:7716458F   198  308  238  191  131  190  123   91
colData(dxd1)
DataFrame with 8 rows and 4 columns
    sample condition     libType     exon
  <factor>  <factor> <character> <factor>
1    WT_1        WT   single-end   this  
2    WT_2        WT   single-end   this  
3    DKO_1       DKO  single-end   this  
4    DKO_2       DKO  single-end   this  
5    WT_1        WT   single-end   others
6    WT_2        WT   single-end   others
7    DKO_1       DKO  single-end   others
8    DKO_2       DKO  single-end   others
split(seq_len(ncol(dxd1)), colData(dxd1)$exon)
$others
[1] 5 6 7 8

$this
[1] 1 2 3 4
#head(featureCounts(dxd1), 5)
head(rowRanges(dxd1), 3)
GRanges object with 3 ranges and 7 metadata columns:
                  seqnames          ranges strand |     PASID            GeneEns      GeneID   featureID
                     <Rle>       <IRanges>  <Rle> | <integer>        <character> <character> <character>
  Akap12:4359096F    chr10 4359035-4359096      + |   4359096 ENSMUSG00000038587      Akap12    4359096F
  Akap12:4359468F    chr10 4359407-4359468      + |   4359468 ENSMUSG00000038587      Akap12    4359468F
   Lats1:7716088F    chr10 7716027-7716088      + |   7716088 ENSMUSG00000040021       Lats1    7716088F
                      groupID exonBaseMean exonBaseVar
                  <character>    <numeric>   <numeric>
  Akap12:4359096F      Akap12        58.50      1723.0
  Akap12:4359468F      Akap12       391.75     53347.6
   Lats1:7716088F       Lats1        48.50       579.0
  -------
  seqinfo: 21 sequences from an unspecified genome; no seqlengths
sampleAnnotation(dxd1)
DataFrame with 4 rows and 3 columns
    sample condition     libType
  <factor>  <factor> <character>
1    WT_1        WT   single-end
2    WT_2        WT   single-end
3    DKO_1       DKO  single-end
4    DKO_2       DKO  single-end

4.1.5 Defining the contrast pair in the countData

Define the contrast pair in the following analysis through defining the levels of conditions in DEXSeq object. If not defined, the conditions saved in DEXSeq object will be transferred to alphabetical order-based factors, and the contrast pairs will be “‘the condition with higher level’ - ‘the condition with lower level’”.

dxd1$condition <- factor(dxd1$condition, levels = c("WT", "DKO")) # The contrast pair will be "DKO - WT"

4.1.6 Normalization and dispersion Estimation

Similar to RNA-seq data, for 3’ end sequencing data perform normalization between samples (column-wise median of ratios for each sample) using the estimateSizeFactors function, and estimate the variation of the data using with the estimateDispersions function, then visualize the dispersion estimation result using the plotDispEsts function.

dxd1 %<>% estimateSizeFactors %>% estimateDispersions %T>% plotDispEsts # Visualization of the dispersion estimation result

4.1.7 Differential pA site usage testing

Test for differential pA site usage for each gene and generate the results using the function testForDEU, then estimate the fold change of pA site usage using the function estimateExonFoldChanges. Check the results using the function DEXSeqResults and set ‘FDR < 10%’ as the criterion for significantly differential pA sites.

dxd1 %<>% testForDEU %>% estimateExonFoldChanges(fitExpToVar = "condition") #Estimate fold changes
dxr1 <- DEXSeqResults(dxd1)
dxr1

LRT p-value: full vs reduced

DataFrame with 13283 rows and 12 columns
                    groupID   featureID exonBaseMean dispersion      stat      pvalue        padj        WT
                <character> <character>    <numeric>  <numeric> <numeric>   <numeric>   <numeric> <numeric>
Akap12:4359096F      Akap12    4359096F      58.4225 0.00798561 1.3333075   0.2482177   0.3425646   11.1258
Akap12:4359468F      Akap12    4359468F     391.4266 0.00697381 1.3993786   0.2368276   0.3295738   21.5595
Lats1:7716088F        Lats1    7716088F      47.9757 0.01022535 4.6834917   0.0304538   0.0586427   11.4030
Lats1:7716124F        Lats1    7716124F      84.7371 0.00775833 0.0257107   0.8726089   0.9083749   13.4728
Lats1:7716458F        Lats1    7716458F     231.1985 0.00454191 2.8345060   0.0922592   0.1519879   18.9816
...                     ...         ...          ...        ...       ...         ...         ...       ...
Msl3:168654204R        Msl3  168654204R      56.3907  0.0721362  0.961646 3.26773e-01 4.26271e-01   10.9169
Hccs:169311537R        Hccs  169311537R     292.6900  0.0134943 20.084690 7.40874e-06 3.36906e-05   18.5133
Hccs:169311802R        Hccs  169311802R      37.9711  0.0160051 18.511783 1.68857e-05 7.23621e-05   11.4216
Ddx3y:1260772R        Ddx3y    1260772R     626.7269  0.0205045  0.687067 4.07164e-01 5.07970e-01   25.3131
Ddx3y:1260874R        Ddx3y    1260874R      69.9770  0.0218784  0.656277 4.17877e-01 5.18608e-01   11.9097
                      DKO log2fold_DKO_WT                genomicData        countData
                <numeric>       <numeric>                  <GRanges>         <matrix>
Akap12:4359096F  10.29634     -0.26470603    chr10:4359035-4359096:+    74:110:21:...
Akap12:4359468F  21.96409      0.09049584    chr10:4359407-4359468:+  492:671:203:...
Lats1:7716088F    9.66163     -0.56319310    chr10:7716027-7716088:+     46:82:41:...
Lats1:7716124F   13.48425      0.00315177    chr10:7716063-7716124:+    85:108:82:...
Lats1:7716458F   19.64339      0.15382436    chr10:7716397-7716458:+  198:308:238:...
...                   ...             ...                        ...              ...
Msl3:168654204R  12.05918        0.347584 chrX:168654203-168654264:-     34:68:42:...
Hccs:169311537R  20.53603        0.468533 chrX:169311536-169311597:-  117:125:521:...
Hccs:169311802R   8.13324       -1.129432 chrX:169311801-169311862:-     39:27:43:...
Ddx3y:1260772R   24.81621       -0.107380     chrY:1260771-1260832:- 782:1010:366:...
Ddx3y:1260874R   12.52243        0.179070     chrY:1260873-1260934:-    71:119:37:...
#dxr  <-  na.omit(dxr)
mcols(dxr1)$description
 [1] "group/gene identifier"                                       
 [2] "feature/exon identifier"                                     
 [3] "mean of the counts across samples in each feature/exon"      
 [4] "exon dispersion estimate"                                    
 [5] "LRT statistic: full vs reduced"                              
 [6] "LRT p-value: full vs reduced"                                
 [7] "BH adjusted p-values"                                        
 [8] "exon usage coefficient"                                      
 [9] "exon usage coefficient"                                      
[10] "relative exon usage fold change"                             
[11] "GRanges object of the coordinates of the exon/feature"       
[12] "matrix of integer counts, of each column containing a sample"
## ----tallyPASs------------------------------------------------------------
table(dxr1$padj < 0.1) # Check the number of differential pA sites (FDR < 0.1)

FALSE  TRUE 
 5782  7501 
## ----tallyGenes------------------------------------------------------------
table(tapply(dxr1$padj < 0.1, dxr1$groupID, any)) # Check the number of gene overlapped with differential pA site clusters (FDR < 0.1)

FALSE  TRUE 
 1436  3152 

4.1.8 Visualization of the differential pA site usage results

# Select the top 100 significant differntial pA site usage
topdiff.PAS <- dxr1 %>% as.data.frame %>% rownames_to_column %>% arrange(padj) %$% groupID[1:100]
head(topdiff.PAS)
[1] "S100a7a"  "Kcnq1ot1" "Ppp2ca"   "Tpm1"     "Wwtr1"    "S100a7a" 

Use the plotDEXSeq function for visualization of differential pA usage result. Use different gene names to explore the results:

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

To display differential ployA usage use splicing = TRUE:

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

To display both experssion and differential ployA usage use splicing = TRUE and expression = TRUE:

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

Generate plots for all the top 300 significant genes using perGeneQValue function:

dxr1 %<>% .[!is.na(.$padj), ]   
dgene <- data.frame(perGeneQValue=perGeneQValue(dxr1)) %>% rownames_to_column("groupID")

dePAS1 <- dxr1 %>% data.frame() %>% 
  dplyr::select(-matches("dispersion|stat|countData|genomicData")) %>% 
  inner_join(dgene) %>% arrange(perGeneQValue) %>% distinct()
Joining, by = "groupID"
# Save the significant differential pA sites (order by perGeneQValue)
"openxlsx" %>% lapply(library, character.only = TRUE) %>% invisible
dePAS_sig1 <- dePAS1 %>% filter(padj < 0.1) %T>%
  write.xlsx(file = "3PSeq_DEXSeq_significant_genes.xlsx", sheetName = "DKO vs WT", firstRow = TRUE, headerStyle = createStyle(textDecoration = "BOLD", halign = "center"))

Check the information of the top 300 differntial pA sites.

DT::datatable(dePAS_sig1[1:300, ])

Generate plots of the top 300 differntial pA sites.

pdf(file= "3PSeq_DEXSeq_significant_genes.DKO_WT.top300.pdf")
sig_genes <- dePAS_sig1 %>% filter(perGeneQValue <= 0.01) %$% groupID %>% unique %>% .[1:300]

for (geneid in sig_genes) {
  nn <- dxr1 %>% .[.$groupID %in% geneid, ] %>% nrow
  if (nn > 1) {
    plotDEXSeq(dxr1, geneid, legend = TRUE, expression = FALSE, splicing = TRUE, cex.axis = 1.2, cex = 1.3, lwd = 2)
  }
}
dev.off()
null device 
          1 

Use the EnhancedVolcano package to visualise differential pA site usage of the contrast pair.

"EnhancedVolcano" %>% lapply(library, character.only = TRUE) %>% invisible
EnhancedVolcano(dePAS_sig1, lab = dePAS_sig1$groupID, x = 'log2fold_DKO_WT', y = 'pvalue', title = 'Volcano Plot', 
                subtitle = 'DKO vs WT', FCcutoff = 1, labSize = 4, legendPosition = "right", 
                caption = bquote(~Log[2]~ "Fold change cutoff, 1; FDR 10%"))

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

4.1.9 Developing differential pA site usage testing of the contrast pair “KD - WT”

countData2 <- dplyr::select(countData, SRR1553129.sorted.bam, SRR1553130.sorted.bam, 
                            SRR1553133.sorted.bam, SRR1553134.sorted.bam) # Select the read counts of the condtion WT and KD 
sampleTable2 <- data.frame(row.names = c("WT_1", "WT_2", "KD_1", "KD_2"), 
                          condition = c(rep("WT", 2), rep("KD", 2)), 
                          libType = rep("single-end", 4))
dxd2 <- DEXSeqDataSet(countData = countData2,
                     sampleData = sampleTable2, 
                     design = ~ sample + exon + condition:exon,
                     featureID = new.featureID,
                     groupID = anno$Symbol,
                     featureRanges = PASinfo)
converting counts to integer mode
some variables in design formula are characters, converting to factors
dxd2$condition <- factor(dxd2$condition, levels = c("WT", "KD"))
dxd2 %<>% estimateSizeFactors %>% estimateDispersions %>% testForDEU %>% estimateExonFoldChanges(fitExpToVar = "condition")
dxr2 <- DEXSeqResults(dxd2)

mcols(dxr2)$description
 [1] "group/gene identifier"                                       
 [2] "feature/exon identifier"                                     
 [3] "mean of the counts across samples in each feature/exon"      
 [4] "exon dispersion estimate"                                    
 [5] "LRT statistic: full vs reduced"                              
 [6] "LRT p-value: full vs reduced"                                
 [7] "BH adjusted p-values"                                        
 [8] "exon usage coefficient"                                      
 [9] "exon usage coefficient"                                      
[10] "relative exon usage fold change"                             
[11] "GRanges object of the coordinates of the exon/feature"       
[12] "matrix of integer counts, of each column containing a sample"
## ----tallyPASs------------------------------------------------------------
table(dxr2$padj < 0.1) # Check the number of differential pA sites (FDR < 0.1)

FALSE  TRUE 
 4745  8533 
## ----tallyGenes------------------------------------------------------------
table(tapply(dxr2$padj < 0.1, dxr2$groupID, any)) # Check the number of gene overlapped with differential pA site clusters (FDR < 0.1)

FALSE  TRUE 
 1047  3538 
# Selection and visualization of the results
topdiff.PAS2 <- dxr2 %>% as.data.frame %>% rownames_to_column %>% arrange(padj) %$% groupID[1:100]
head(topdiff.PAS2)         
[1] "Tpm1"  "Peg3"  "Peg3"  "Rragc" "Rragc" "Mbd2" 
plotDEXSeq(dxr2, "Tpm1", legend = TRUE, expression = FALSE, splicing = TRUE, cex.axis = 1.2, cex = 1.3, lwd = 2)

dxr2 %<>% .[!is.na(.$padj), ]
dgene2 <- data.frame(perGeneQValue=perGeneQValue(dxr2)) %>% rownames_to_column("groupID")

dePAS2 <- dxr2 %>% data.frame() %>% 
  dplyr::select(-matches("dispersion|stat|countData|genomicData")) %>% 
  inner_join(dgene2) %>% arrange(perGeneQValue) %>% distinct()
Joining, by = "groupID"
# Save the significant differential pA sites (order by perGeneQValue)
add.xlsx.sheet <- function(fileName, sheetName, data){
  wb <- loadWorkbook(fileName)
  addWorksheet(wb, sheetName = sheetName)
  freezePane(wb, sheet = sheetName, firstRow = TRUE)
  writeData(wb, sheet = sheetName, data, headerStyle = createStyle(textDecoration = "BOLD", halign = "center"))
  saveWorkbook(wb, file = fileName, overwrite = TRUE)
}
dePAS_sig2 <- dePAS2 %>% filter(padj < 0.1) %T>%
  add.xlsx.sheet(file = "3PSeq_DEXSeq_significant_genes.xlsx", "KD vs WT", .)
DT::datatable(dePAS_sig2[1:300, ])
EnhancedVolcano(dePAS_sig2, lab = dePAS_sig2$groupID, x = 'log2fold_KD_WT', y = 'pvalue', title = 'Volcano Plot', 
                subtitle = 'KD vs WT', FCcutoff = 1, labSize = 4, legendPosition = "right", 
                caption = bquote(~Log[2]~ "Fold change cutoff, 1; FDR 10%"))

4.1.10 Developing differential pA site usage testing of the contrast pair “Ctrl - WT”

countData3 <- dplyr::select(countData, SRR1553129.sorted.bam, SRR1553130.sorted.bam, 
                            SRR1553135.sorted.bam, SRR1553136.sorted.bam) # Select the read counts of the condtion WT and KD 
sampleTable3 <- data.frame(row.names = c("WT_1", "WT_2", "Ctrl_1", "Ctrl_2"), 
                          condition = c(rep("WT", 2), rep("Ctrl", 2)), 
                          libType = rep("single-end", 4))
dxd3 <- DEXSeqDataSet(countData = countData3,
                     sampleData = sampleTable3, 
                     design = ~ sample + exon + condition:exon,
                     featureID = new.featureID,
                     groupID = anno$Symbol,
                     featureRanges = PASinfo)
converting counts to integer mode
some variables in design formula are characters, converting to factors
dxd3$condition <- factor(dxd3$condition, levels = c("WT", "Ctrl"))
dxd3 %<>% estimateSizeFactors %>% estimateDispersions %>% testForDEU %>% estimateExonFoldChanges(fitExpToVar = "condition")
dxr3 <- DEXSeqResults(dxd3)

mcols(dxr3)$description
 [1] "group/gene identifier"                                       
 [2] "feature/exon identifier"                                     
 [3] "mean of the counts across samples in each feature/exon"      
 [4] "exon dispersion estimate"                                    
 [5] "LRT statistic: full vs reduced"                              
 [6] "LRT p-value: full vs reduced"                                
 [7] "BH adjusted p-values"                                        
 [8] "exon usage coefficient"                                      
 [9] "exon usage coefficient"                                      
[10] "relative exon usage fold change"                             
[11] "GRanges object of the coordinates of the exon/feature"       
[12] "matrix of integer counts, of each column containing a sample"
## ----tallyPASs------------------------------------------------------------
table(dxr3$padj < 0.1) # Check the number of differential pA sites (FDR < 0.1)

FALSE  TRUE 
 8232  5048 
## ----tallyGenes------------------------------------------------------------
table(tapply(dxr3$padj < 0.1, dxr3$groupID, any)) # Check the number of gene overlapped with differential pA site clusters (FDR < 0.1)

FALSE  TRUE 
 2193  2393 
# Selection and visualization of the results
topdiff.PAS3 <- dxr3 %>% as.data.frame %>% rownames_to_column %>% arrange(padj) %$% groupID[1:100]
head(topdiff.PAS3)         
[1] "Tpm1"    "Smc6"    "Tpm1"    "Smc6"    "Gm13341" "Zranb2" 
plotDEXSeq(dxr3, "Tpm1", legend = TRUE, expression = FALSE, splicing = TRUE, cex.axis = 1.2, cex = 1.3, lwd = 2)

dxr3 %<>% .[!is.na(.$padj), ]
dgene3 <- data.frame(perGeneQValue=perGeneQValue(dxr3)) %>% rownames_to_column("groupID")

dePAS3 <- dxr3 %>% data.frame() %>% 
  dplyr::select(-matches("dispersion|stat|countData|genomicData")) %>% 
  inner_join(dgene3) %>% arrange(perGeneQValue) %>% distinct()
Joining, by = "groupID"
# Save the significant differential pA sites (order by perGeneQValue)
dePAS_sig3 <- dePAS3 %>% filter(padj < 0.1) %T>%
  add.xlsx.sheet(file = "3PSeq_DEXSeq_significant_genes.xlsx", "Ctrl vs WT", .)
DT::datatable(dePAS_sig3[1:300, ])
EnhancedVolcano(dePAS_sig3, lab = dePAS_sig3$groupID, x = 'log2fold_Ctrl_WT', y = 'pvalue', title = 'Volcano Plot', 
                subtitle = 'Ctrl vs WT', FCcutoff = 1, labSize = 4, legendPosition = "right", 
                caption = bquote(~Log[2]~ "Fold change cutoff, 1; FDR 10%"))

4.2 Using diffSplice package

4.2.1 Loading required libraries

Type the following commands in R console and press Enter to load the required libraries in R.

c("limma", "edgeR") %>% lapply(library, character.only = TRUE) %>% invisible

4.2.2 Creating the sample table to define the experimental design

Create the sampleTable, which consists of details of each sample with its library-type and condition. Ensure the order of row names is consistent with the order of bam file names used by featureCounts to count the reads. Remember that as the contrast pairs can be defined directly in the diffSplice function, the sampleTable created in this step includes all the read counts of all conditions.

sampleTable <- data.frame(row.names = c("WT_1","WT_2","DKO_1","DKO_2", "KD_1", "KD_2", "Ctrl_1", "Ctrl_2"), 
                          condition = c(rep("WT", 2), rep("DKO", 2), rep("KD", 2), rep("Ctrl", 2)), 
                          libType = rep("single-end", 4))

4.2.3 Creating the DGEList object and processing

Create the DGEList using the function DGEList to collect the filtered read counts generated in step 3. Remember that as the contrast pairs can be defined directly in the next step, the DGEList object created in this step include all the read counts of all conditions. Then develop processing steps of the DGEList object includes: 1) Normalization of the read counts across samples using Trimmed Meand of M values (TMM normalization) by the function calcNormFactors; 2) Differential expression testing across all conditions using the function voomWithQualityWeights; 3) Linear modelling based on the count data using the function limFit; 4) Computing moderated statistical results using the function eBayes given the linear model fit from the limFit.

fit <- {
  dge <- DGEList(counts = countData) %>% calcNormFactors
  Treat <- factor(sampleTable$condition, levels = c("WT", "DKO", "KD", "Ctrl"))
  design <- model.matrix(~0 + Treat)
  colnames(design) <- levels(Treat)
  voomWithQualityWeights(dge, design, plot = FALSE)
} %>% lmFit(design) %>% eBayes

4.2.4 Defining contrasts and processing

Define the contrasts of interest for differential pA usage analysis using makeContrasts function. Then process the DGEList object fit generated in the previous step using the function contrasts.fit and eBayes to acquire the differential expression results of the contrast pairs.

contrast.matrix <- makeContrasts(DKO_vs_WT = DKO-WT, KD_vs_WT = KD-WT, Ctrl_vs_WT = Ctrl-WT, levels = design)
fit2 <- fit %>% contrasts.fit(contrast.matrix) %>% eBayes
summary(decideTests(fit2))
       DKO_vs_WT KD_vs_WT Ctrl_vs_WT
Down        4107     3643       1161
NotSig      4953     6038      10393
Up          4223     3602       1729

4.2.5 Differential pA site usage analysis

Develop the differential pA site usage analysis for each defined contrasct pair on the fitted model using the function diffSplice.

ex <- diffSplice(fit2, geneid = anno$Symbol, exonid = new.featureID)
Total number of exons:  13283 
Total number of genes:  4588 
Number of genes with 1 exon:  0 
Mean number of exons in a gene:  3 
Max number of exons in a gene:  27 
#Check the top significant results with topSplice
topSplice(ex)

NOTE: This step uses the PAS information object created in 4.1.3. Ensure it is loaded in the current working environment.

4.2.6 Visualizing the result of the contrast pair “DKO - WT”

Check the information of the top 300 differntial pA sites.

sig1 <-topSplice(ex, n = Inf, FDR = 0.1, coef = 1, test= "t", sort.by = "logFC") # Save the results sorted by logFC with FDR < 0.1
DT::datatable(sig1[1:300, ])

Check the information of 300 genes with the top differntial APA.

sig1.genes <-topSplice(ex, n = Inf, FDR = 0.1, coef = 1, test= "simes") # Save the results with FDR < 0.1
DT::datatable(sig1.genes[1:300, ])

Visualize the differential APA result using the plotSplice function and volcano plots.

pdf(file = "3PSeq_diffSplice_plotSplice_DKO_WT.pdf")
plotSplice(ex, coef = 1, geneid = "S100a7a", FDR = 0.1)
plotSplice(ex, coef = 1, geneid = "Tpm1", FDR = 0.1)
plotSplice(ex, coef = 1, geneid = "Smc6", FDR = 0.1)
dev.off()
null device 
          1 
pdf(file = "3PSeq_diffSplice_Volcano_DKO_WT.pdf", height = 10, width = 12)
EnhancedVolcano(sig1, lab = sig1$GeneID, xlab = bquote(~Log[2]~ 'fold change'), 
                x = 'logFC', y = 'P.Value', title = 'Volcano Plot', subtitle = 'DKO vs WT', 
                # pCutoff = 10e-16, 
                FCcutoff = 1, labSize = 8, legendPosition = "right")
dev.off()
null device 
          1 
# Save the list of top differential pA sites at FDR 10%
write.xlsx(sig1, file = "3PSeq_diffSplice_significant_genes.xlsx", sheetName = "DKO vs WT (pA site-level)", 
           firstRow = TRUE, headerStyle = createStyle(textDecoration = "BOLD", halign = "center"))

# Save the list of genes with top differential APA at FDR 10%
add.xlsx.sheet(file = "3PSeq_diffSplice_significant_genes.xlsx", "DKO vs WT (gene-level)", sig1.genes)

4.2.7 Visualizing the result of the contrast pair “KD - WT”

Check the information of the top 300 differntial pA sites.

sig2 <-topSplice(ex, n = Inf, FDR = 0.1, coef = 2, test= "t", sort.by = "logFC")
DT::datatable(sig2[1:300, ])

Check the information of 300 genes with the top differntial APA.

sig2.genes <-topSplice(ex, n = Inf, FDR = 0.1, coef = 2, test= "simes") # Save the results with FDR < 0.1
DT::datatable(sig2.genes[1:300, ])

Visualize the differential APA result using the plotSplice function and volcano plots.

pdf(file = "3PSeq_diffSplice_plotSplice_KD_WT.pdf")
plotSplice(ex, coef = 2, geneid = "Mbd2", FDR = 0.1)
plotSplice(ex, coef = 2, geneid = "Ssbp1", FDR = 0.1)
plotSplice(ex, coef = 2, geneid = "Pfn1", FDR = 0.1)
dev.off()
null device 
          1 
pdf(file = "3PSeq_diffSplice_Volcano_KD_WT.pdf", height = 10, width = 12)
EnhancedVolcano(sig2, lab = sig2$GeneID, xlab = bquote(~Log[2]~ 'fold change'), 
                x = 'logFC', y = 'P.Value', title = 'Volcano Plot', subtitle = 'KD vs WT', 
                # pCutoff = 10e-16, 
                FCcutoff = 1, labSize = 8, legendPosition = "right")
dev.off()
null device 
          1 
#Save the list of top differential pA sites at FDR 10%
add.xlsx.sheet(file = "3PSeq_diffSplice_significant_genes.xlsx", "KD vs WT (pA site-level)", sig2)

# Save the list of genes with top differential APA at FDR 10%
add.xlsx.sheet(file = "3PSeq_diffSplice_significant_genes.xlsx", "KD vs WT (gene-level)", sig2.genes)

4.2.8 Visualizing the result of the contrast pair “Ctrl - WT”

Check the information of the top 300 differntial pA sites.

sig3 <-topSplice(ex, n = Inf, FDR = 0.1, coef = 3, test= "t", sort.by = "logFC")
DT::datatable(sig3[1:300, ])

Check the information of 300 genes with the top differntial APA.

sig3.genes <-topSplice(ex, n = Inf, FDR = 0.1, coef = 3, test= "simes") # Save the results with FDR < 0.1
DT::datatable(sig3.genes[1:300, ])

Visualize the differential APA result using the plotSplice function and volcano plots.

pdf(file = "3PSeq_diffSplice_plotSplice_Ctrl_WT.pdf")
plotSplice(ex, coef = 3, geneid = "Mbd2", FDR = 0.1)
plotSplice(ex, coef = 3, geneid = "Ift81", FDR = 0.1)
plotSplice(ex, coef = 3, geneid = "Rps24", FDR = 0.1)
dev.off()
null device 
          1 
pdf(file = "3PSeq_diffSplice_Volcano_Ctrl_WT.pdf", height = 10, width = 12)
EnhancedVolcano(sig3, lab = sig3$GeneID, xlab = bquote(~Log[2]~ 'fold change'), 
                x = 'logFC', y = 'P.Value', title = 'Volcano Plot', subtitle = 'Ctrl vs WT', 
                # pCutoff = 10e-16, 
                FCcutoff = 1, labSize = 8, legendPosition = "right")
dev.off()
null device 
          1 
#Save the list of top differential pA sites at FDR 10%
add.xlsx.sheet(file = "3PSeq_diffSplice_significant_genes.xlsx", "Ctrl vs WT (pA site-level)", sig3)

# Save the list of genes with top differential APA at FDR 10%
add.xlsx.sheet(file = "3PSeq_diffSplice_significant_genes.xlsx", "Ctrl vs WT (gene-level)", sig3.genes)
LS0tCnRpdGxlOiAiSWRlbnRpZmljYXRpb24gb2YgQWx0ZXJuYXRpdmUgUG9seUFkZW55bGF0aW9uIChBUEEpIGluIDNwLXNlcSBkYXRhIgphdXRob3I6ICJZaW5nIFpoZW5nIgpkYXRlOiAiMjQvMDIvMjAyMSIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICB0aGVtZTogdW5pdGVkCiAgICB0b2M6IHllcwplZGl0b3Jfb3B0aW9uczoKICBjaHVua19vdXRwdXRfdHlwZTogaW5saW5lCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkKYGBgCgojIEludHJvZHVjdGlvbgpUaGUgY29udGVudCBvZiB0aGlzIFIgbm90ZWJvb2sgZmlsZSBpbmNsdWRlcyBhbGwgdGhlIGNvZGVzIHJlcXVpZWQgZm9yIHRoZSBBbHRlcm5hdGl2ZSBQb2x5QWRlbnlsYXRpb24gKEFQQSkgYW5hbHlzaXMgdXNpbmcgM3AtU2VxIGRhdGEuIFJlZmVyIHRvICdQQVJUIDItIEFsdGVybmF0aXZlIHBvbHlhZGVueWxhdGlvbiAoQVBBKSBhbmFseXNpcyB1c2luZyAzcC1zZXEnIG9mIHRoZSBQcm90b2NvbCBzZWN0aW9uLgpOT1RFOiBUbyBwcm9jZWVkIHdpdGggZWFjaCBzdGVwIGJlbG93IHRvIGdldCB0aGUgcmVzdWx0cywgdHlwZSB0aGUgYmFzaCBjb21tYW5kcyBvbiBMaW51eCBjb21tYW5kLWxpbmUgdGhlbiBwcmVzcyBFbnRlciwgb3IgdHlwZSB0aGUgUiBjb2RlcyBpbiBSIGNvbnNvbGUgdGhlbiBwcmVzcyBFbnRlci4gCgojIDAuIFByZXBhcmF0aW9uOiBJbnN0YWxsYXRpb24gb2YgdG9vbHMgYW5kIHBhY2thZ2VzIHVzZWQgaW4gdGhpcyBhbmFseXNpcwpFbnN1cmUgdGhlIHJlbGF0ZWQgdG9vbHMgYW5kIHBhY2thZ2VzIHVzZWQgZm9yIEFQQSBhbmFseXNpcyBhcmUgaW5zdGFsbGVkIGNvcnJlY3RseSB0byBndWFyYW50ZWUgdGhlIHN1Y2Nlc3NmdWwgcnVubmluZyBvZiB0aGUgZm9sbG93aW5nIHBpcGVsaW5lLiBBcyB0aGUgYW5hbHlzaXMgaXMgcmVxdWlyZWQgdG8gYmUgcGVyZm9ybWVkIGJvdGggb24gdGhlIExpbnV4IGNvbW1hbmQtbGluZSBhbmQgaW4gdGhlIFIgY29uc29sZSwgYW5kIHBlcmwgaXMgZXhwZWN0ZWQgdG8gYmUgdXNlZCBmb3IgZGF0YSBwcm9jZXNzaW5nLCBlbnN1cmUgYWxsIG9mIHRoZSBlbnZpcm9ubWVudHMgYXJlIHByZXBhcmVkIGF0IHRoZSBiZWdpbm5pbmcuCgojIyAwLjEgSW5zdGFsbGF0aW9uIG9mIGJhc2ggdG9vbHMgYW5kIHBhY2thZ2VzCiMjIyAwLjEuMSBJbnN0YWxsYXRpb24gb2YgYGNvbmRhYApgY29uZGFgIGlzIGEgcG9wdWxhciBhbmQgZmxleGlibGUgcGFja2FnZSBtYW5hZ2VyIHRoYXQgYWxsb3dzIGNvbnZlbmllbnQgaW5zdGFsbGF0aW9uIG9mIHBhY2thZ2VzIHdpdGggdGhlaXIgZGVwZW5kZW5jaWVzIGFjcm9zcyBhbGwgcGxhdGZvcm1zLiBUbyBhc3Npc3QgdGhlIGZ1cnRoZXIgaW5zdGFsbGF0aW9uIG9mIHJlbGF0ZWQgYmFzaCB0b29scy9wYWNrYWdlcyByZXF1aXJlZCBpbiB0aGlzIGFuYWx5c2lzLCBpbnN0YWxsIGBjb25kYWAgYXQgZmlyc3QgYnkgZm9sbG93aW5nIHRoZSBpbnN0YWxsaW5nIGluc3RydWN0aW9uIG9mIGBBbmFjb25kYWAgKGNvbmRhIHBhY2thZ2UgbWFuYWdlcikgZG9jdW1lbnQgYXQgaHR0cHM6Ly9kb2NzLmFuYWNvbmRhLmNvbS9hbmFjb25kYS9pbnN0YWxsLy4gRW5zdXJlIHRoZSBzeXN0ZW0gcmVxdWlyZW1lbnRzIGFuZCB0aGUgaW5zdGFsbGVyIGFyZSBzZWxlY3RlZCBwcm9wZXJseS4gCgojIyMgMC4xLjIgSW5zdGFsbGF0aW9uIG9mIGJhc2ggdG9vbHMgYW5kIHBhY2thZ2VzIHVzaW5nIGBjb25kYWAKSW5zdGFsbCBhbGwgdGhlIHJlcXVpcmVkIHBhY2thZ2VzIGluIHRoaXMgYW5hbHlzaXMgdXNpbmcgYGNvbmRhYCBvbiBMaW51eCBjb21tYW5kLWxpbmUuCmBgYHtiYXNoLCBldmFsPUZBTFNFfQpjb25kYSBpbnN0YWxsIC1jIGRhbGVyIHNyYXRvb2xraXQgCmNvbmRhIGluc3RhbGwgLWMgY29uZGEtZm9yZ2UgcGFyYWxsZWwKY29uZGEgaW5zdGFsbCAtYyBiaW9jb25kYSBib3d0aWUgc3JhdG9vbGtpdCBjdXRhZGFwdCBzYW10b29scyBiZWR0b29scyBkZWVwdG9vbHMKYGBgCgojIyAwLjIgSW5zdGFsbGF0aW9uIG9mIFIgcGFja2FnZXMKSW5zdGFsbCBhbGwgdGhlIFIgcGFja2FnZXMgcmVxdWlyZWQgaW4gdGhpcyBhbmFseXNpcyBpbiBSIGNvbnNvbGUgaWYgbm90IGluc3RhbGxlZC4KYGBge3IsIGV2YWw9RkFMU0V9CmJpb2NfcGFja2FnZXMgPC0gYygiREVYU2VxIiwgIlJzdWJyZWFkIiwgIkVuaGFuY2VkVm9sY2FubyIsICJlZGdlUiIsICJsaW1tYSIsICJHZW5vbWljUmFuZ2VzIikgCnBhY2thZ2VzIDwtIGMoInRpZHl2ZXJzZSIsICJCaW9jTWFuYWdlciIsICJvcGVueGxzeCIpIAojSW5zdGFsbCBpZiBub3QgYWxyZWFkeSBpbnN0YWxsZWQgCmluc3RhbGxlZF9wYWNrYWdlcyA8LSBwYWNrYWdlcyAlaW4lIHJvd25hbWVzKGluc3RhbGxlZC5wYWNrYWdlcygpKSAKaW5zdGFsbGVkX2Jpb2NfcGFja2FnZXMgPC0gYmlvY19wYWNrYWdlcyAlaW4lIHJvd25hbWVzKGluc3RhbGxlZC5wYWNrYWdlcygpKSAKaWYgKGFueShpbnN0YWxsZWRfcGFja2FnZXMgPT0gRkFMU0UpKSB7IAogIGluc3RhbGwucGFja2FnZXMocGFja2FnZXNbIWluc3RhbGxlZF9wYWNrYWdlc10sIGRlcGVuZGVuY2llcyA9IFRSVUUpIAogIEJpb2NNYW5hZ2VyOjppbnN0YWxsKHBhY2thZ2VzWyFpbnN0YWxsZWRfYmlvY19wYWNrYWdlc10sIGRlcGVuZGVuY2llcyA9IFRSVUUpIAp9CmBgYAoKIyAxLiBEYXRhIGRvd25sb2FkaW5nIGFuZCBwcmUtcHJvY2Vzc2luZyAKIyMgMS4xIFByZXBhcmF0aW9uIG9mIHRoZSBwcm9qZWN0IGRpcmVjdG9yeQpDcmVhdGUgdGhlIHByb2plY3QgZGlyZWN0b3J5IGBBUEFfYW5hbHlzaXNgIGFuZCB0aGUgcmF3IGRhdGEgZGlyZWN0b3J5IGByYXdfZGF0YWAuCmBgYHtiYXNoLCBldmFsPUZBTFNFfQojIENyZWF0ZSBhbmQgb3BlbiB0aGUgcHJvamVjdCBkaXJlY3RvcnkKUFJPSl9ESVI9QVBBX2FuYWx5c2lzLwpta2RpciAkUFJPSl9ESVIKY2QgJFBST0pfRElSCgojIENyZWF0ZSB0aGUgcmF3IGRhdGEgZGlyZWN0b3J5IApSQVdfREFUQT1yYXdfZGF0YQpta2RpciAtcCAkUkFXX0RBVEEKYGBgCgojIyAxLjIgUmF3IGRhdGEgZG93bmxvYWRpbmcgYW5kIGZhc3RxIGZpbGVzIHByZXBhcmF0aW9uCiMjIyAxLjIuMSBSYXcgZGF0YSBkb3dubG9hZGluZwpHaXZlbiB0aGUgU2VxdWVuY2UgUmVhZCBBcmNoaXZlIFtTUkFdIEFjY2Vzc2lvbiBJZHMgKDE1NTMxMjkgdG8gMTU1MzEzNikgaW4gc2VxdWVuY2UsIGRvd25sb2FkIHRoZSByYXcgZGF0YSBpbiBwYXJhbGxlbCBmcm9tIFNSQSB1c2luZyB0aGUgYHByZWZldGNoYCBjb21tYW5kIGZyb20gU1JBIHRvb2xraXQgKHYyLjEwLjgpIGFuZCB0aGUgYHBhcmFsbGVsYCBjb21tYW5kIGZyb20gR05VIHBhcmFsbGVsIHV0aWxpdHkuIFNhdmUgdGhlIHJzYSBmaWxlcyBpbiB0aGUgZGlyZWN0b3J5IGByYXdfZGF0YWAuCmBgYHtiYXNoLCBldmFsPUZBTFNFfQojIE9wZW4gdGhlIHJhd19kYXRhIGRpcmVjdG9yeSBmb3IgcmF3IGRhdGEgc3RvcmFnZQpjZCAkUkFXX0RBVEEKCiMgRG93bmxvYWQgU1JBIGZpbGVzIHVzaW5nIGFjY2Vzc2lvbiBudW1iZXJzCnNlcSAxNTUzMTI5IDE1NTMxMzYgfCBwYXJhbGxlbCBwcmVmZXRjaCBTUlJ7fQpgYGAKCiMjIyAxLjIuMiBmYXN0cSBmaWxlcyBwcmVwYXJhdGlvbgpFeHRyYWN0IGZhc3RxLmd6IGZpbGVzIGZyb20gdGhlIGFyY2hpdmUgdXNpbmcgdGhlIGBmYXN0cS1kdW1wYCBjb21tYW5kIGZyb20gdGhlIFNSQSB0b29sa2l0LiBFbnN1cmUgYWxsIHRoZSBwYXRocyBhbmQgbmFtZXMgb2YgdGhlIHJzYSBmaWxlcyBhcmUgbGlzdGVkIGNvcnJlY3RseS4KYGBge2Jhc2gsIGV2YWw9RkFMU0V9CiMgQ29udmVydCBzcmEgZmlsZXMgdG8gZmFzdHEgZmlsZXMgZm9yIGFsaWdubWVudApwYXJhbGxlbCAtaiAzIGZhc3RxLWR1bXAgLS1nemlwIC0tc2tpcC10ZWNobmljYWwgLS1yZWFkLWZpbHRlciBwYXNzIC0tZHVtcGJhc2UgLS1zcGxpdC1lIC0tY2xpcCAtLW9yaWdmbXQge30gOjo6IFNSUjE1NTMxMjkvU1JSMTU1MzEyOS5zcmEgU1JSMTU1MzEzMC9TUlIxNTUzMTMwLnNyYSBTUlIxNTUzMTMxL1NSUjE1NTMxMzEuc3JhIFNSUjE1NTMxMzIvU1JSMTU1MzEzMi5zcmEgU1JSMTU1MzEzMy9TUlIxNTUzMTMzLnNyYSBTUlIxNTUzMTM0L1NSUjE1NTMxMzQuc3JhIFNSUjE1NTMxMzUvU1JSMTU1MzEzNS5zcmEgU1JSMTU1MzEzNi9TUlIxNTUzMTM2LnNyYQpgYGAKClVuemlwIHRoZSBjb21wcmVzc2VkIGZhc3RxLmd6IGZpbGVzIGluIHBhcmFsbGVsIHVzaW5nIHRoZSBjb21tYW5kIGBndW56aXBgIHRvIG9idGFpbiBmYXN0cSBmaWxlcy4gCmBgYHtiYXNoLCBldmFsPUZBTFNFfQojIFVuemlwIHRoZSBmYXN0cS5neiBkYXRhIHRvIGdhaW4gZmFzdHEgZmlsZXMKcGFyYWxsZWwg4oCcZ3VuemlwIHt94oCdIDo6OiRSQVdfREFUQS8qLmZhc3RxLmd6CgojIFJldHVybiB0byB0aGUgcHJvamVjdCBkaXJlY3RvcnkgZm9yIHJlZmVyZW5jZSBkb3dubG9hZGluZwpjZCAuLiAKYGBgCgojIyAxLjMgUmVmZXJlbmNlIGdlbm9tZSBhbmQgYW5ub3RhdGlvbnMgZG93bmxvYWRpbmcKRG93bmxvYWQgdGhlIHJlZmVyZW5jZSBnZW5vbWUgYW5kIGFubm90YXRpb25zIGZvciBNb3VzZSAoR2Vub21lIGFzc2VtYmx5IG1tMTApIGluIHRoZSBwcm9qZWN0IGRpcmVjdG9yeSB1c2luZyB0aGUgY29tbWFuZCBgd2dldGAsIHRoZW4gZGVjb21wcmVzcyB0aGUgZ2Vub21lIGFubm90YXRpb24gZmlsZSB1c2luZyB0aGUgY29tbWFuZCBgdW56aXBgLiBEb3dubG9hZCB0aGUgcmVmZXJlbmNlIGZpbGUgZm9yIHRoZSBtb3VzZSBjaHJvbW9zb21lIHNpemVzIGZyb20gVUNTQyB1c2luZyB0aGUgY29tbWFuZCBgd2dldGAuIFRoZSByZWZlcmVuY2UgZ2Vub21lIGZpbGUgYW5kIGl0cyBhbm5vdGF0aW9ucyAodGhlIGJvd3RpZSBpbmRleCBvZiBtbTEwKSBoZXJlIGFyZSBzYXZlZCBpbiB0aGUgZGlyZWN0b3J5IGBtbTEwYCwgYW5kIHRoZSB0aGUgcmVmZXJlbmNlIGZpbGUgZm9yIHRoZSBtb3VzZSBjaHJvbW9zb21lIHNpemVzIGlzIG5hbWVkIGFzIGBtbTEwLmNocm9tLnNpemVzLnR4dGAuCmBgYHtiYXNoLCBldmFsPUZBTFNFfQojIERvd25sb2FkIHRoZSBib3d0aWUgaW5kZXggb2YgbW0xMAp3Z2V0IGh0dHBzOi8vZ2Vub21lLWlkeC5zMy5hbWF6b25hd3MuY29tL2J0L21tMTAuemlwCnVuemlwIG1tMTAuemlwCgojIERvd25sb2FkIHRoZSBtbTEwLmNocm9tLnNpemVzCndnZXQgaHR0cHM6Ly9oZ2Rvd25sb2FkLXRlc3QuZ2kudWNzYy5lZHUvZ29sZGVuUGF0aC9tbTEwL2JpZ1ppcHMvbW0xMC5jaHJvbS5zaXplcwpgYGAKCiMjIDEuNCBEYXRhIHByZS1wcm9jZXNzaW5nLCByZWFkIGFsaWdubWVudCwgZmlsZXMgc3BsaXQgYW5kIGNvbnZlcnNpb24KIyMjIDEuNC4xIERhdGEgcHJlLXByb2Nlc3NpbmcKIyMjIyAxLjQuMS4xIElsbHVtaW5hIEFkYXB0ZXIgdHJpbW1pbmcKVG8gcmVtb3ZlIHRoZSBpbmZsdWVuY2Ugb2YgYWRhcHRlciBzZXF1ZW5jZSBvbiBzaG9ydCByZWFkcyAofjM1IGJhc2VzKSBnYWluZWQgaW4gcG9seUEtc2VxLCBwZXJmb3JtIElsbHVtaW5hIGFkYXB0ZXIgdHJpbW1pbmcgd2l0aCB0aGUgY29tbWFuZCBgY3V0YWRhcHRgLiBTYXZlIHRoZSByZXN1bHRzIGluIHRoZSBuZXdseSBjcmVhdGVkIGRpcmVjdG9yeSBgYWRhcHRlci50cmltbWluZy5yZXN1bHRzYC4KYGBge2Jhc2gsIGV2YWw9RkFMU0V9ClRSSU1fRElSPWFkYXB0ZXIudHJpbW1pbmcucmVzdWx0cwpta2RpciAkVFJJTV9ESVIKCmZvciBmcSBpbiAkUkFXX0RBVEEvKi5mYXN0cQpkbwpPVVRQVVQ9JChiYXNlbmFtZSAke2ZxfXwgc2VkICdzL19wYXNzLmZhc3RxLy9nJyk7CmN1dGFkYXB0IC1hICJBR0FUQ0dHQUFHQUdDIiAgLW0gMTUgLW4gMyAke2ZxfSB8IGZhc3R4X3JldmVyc2VfY29tcGxlbWVudCAtaSA+ICRUUklNX0RJUlwvJHtPVVRQVVR9X3RyaW1tZWQuZmFzdHEKZG9uZQpgYGAKCiMjIyMgMS40LjEuMiBPYnRhaW5pbmcgc2Vuc2Ugc3RyYW5kIHNlcXVlbmNlCkVuc3VyZSB0aGUgcmVhZCBzZXF1ZW5jZSB1c2VkIGZvciBnZW5vbWUgYWxpZ25tZW50IGlzIGNvbnNpc3RlbnQgd2l0aCB0aGUgc2Vuc2Ugc3RyYW5kIHNlcXVlbmNlLiBBY2NvcmRpbmcgdG8gdGhlIGRlc2NyaXB0aW9uIG9mIGxpYnJhcnkgcHJlcGFyYXRpb24gc3RlcHMgaW4gdGhlIG9yaWdpbmFsIGFydGljbGUsIHBvbHlBLXNlcSB3YXMgcGVyZm9ybWVkIG9uIHRoZSBhbnRpc2Vuc2Ugc3RyYW5kLiBUaGVyZWZvcmUsIGFmdGVyIGFkYXB0ZXIgdHJpbW1pbmcsIHBlcmZvcm0gcmV2ZXJzZSBjb21wbGVtZW50YXJ5IG9wZXJhdGlvbiB1c2luZyB0aGUgYGZhc3R4X3JldmVyc2VfY29tcGxlbWVudGAgY29tbWFuZCBmcm9tIEZBU1RYIHRvb2xraXQgdG8gb2J0YWluIHRoZSBzZW5zZSBzdHJhbmQgc2VxdWVuY2UuCmBgYHtiYXNoLCBldmFsPUZBTFNFfQpUUklNUkNfRElSPXRyaW1tZWQucmMuZGF0YQpta2RpciAkVFJJTVJDX0RJUgoKZm9yIGZxIGluICRSQVdfREFUQS8qLmZhc3RxCmRvCk9VVFBVVD0kKGJhc2VuYW1lICR7ZnF9fCBzZWQgJ3MvX3Bhc3MuZmFzdHEvL2cnKTsKZmFzdHhfcmV2ZXJzZV9jb21wbGVtZW50IC1pICRUUklNX0RJUlwvJHtPVVRQVVR9X3RyaW1tZWQuZmFzdHEgLW8gJFRSSU1SQ19ESVJcLyR7T1VUUFVUfV90cmltbWVkX3JjLmZhc3RxCmRvbmUKYGBgCk5PVEU6IFRoaXMgc3RlcCBpcyBzcGVjaWZpYyB0byB0aGUgUG9seUEtc2VxIGFzc2F5IHVzZWQuIAoKIyMjIDEuNC4yIFJlYWQgYWxpZ25tZW50Ck1hcCByZWFkcyB0byBtb3VzZSBnZW5vbWUgYXNzZW1ibHkgdXNpbmcgYm93dGllIGFsaWduZXIgYW5kIHNhdmUgdGhlIGFsaWduZWQgU0FNIGZpbGVzIGluIHRoZSBkaXJlY3RvcnkgYGJhbWZpbGVzYC4KYGBge2Jhc2gsIGV2YWw9RkFMU0V9ClNBTV9ESVI9c2FtZmlsZXMKbWtkaXIgJFNBTV9ESVIKCmZvciBmcSBpbiAkUkFXX0RBVEEvKi5mYXN0cQpkbwpPVVRQVVQ9JChiYXNlbmFtZSAke2ZxfXwgc2VkICdzL19wYXNzLmZhc3RxLy9nJyk7CmJvd3RpZSAtbiAyIC1rIDEwMCAtLWJlc3QgLS1zdHJhdGEgLXAgMTAgIC14IG1tMTAvZ2Vub21lICAtLXVuIHVubWFwcGVkX291dCAkVFJJTVJDX0RJUlwvJHtPVVRQVVR9X3RyaW1tZWRfcmMuZmFzdHEgLVMgPiAkU0FNX0RJUlwvJHtPVVRQVVR9LnNhbQpkb25lCmBgYAoKIyMjIDEuNC4zIEZpbGUgc3BsaXQsIG5vcm1hbGl6YXRpb24gYW5kIGNvdmVyc2lvbgpUbyBwcmVwYXJlIGZvciBnZW5vbWUgY292ZXJhZ2UgdmlzdWFsaXphdGlvbiwgY29udmVydCBTQU0gZmlsZXMgdG8gQkFNIGZpbGVzLCB0aGVuIHNvcnQgYW5kIGluZGV4IEJBTSBmaWxlcyBmb3IgZWFjaCBzYW1wbGUgdXNpbmcgYHNhbXRvb2xzYCBjb21tYW5kcyBiZWZvcmUgcHJvY2VlZGluZyB0byBmdXJ0aGVyIHN0ZXBzLiBTYXZlIHRoZSByZXN1bHRzIGluIHRoZSBkaXJlY3RvcnkgYGJhbWZpbGVzYCBieSB0eXBpbmcgdGhlIGZvbGxvd2luZyBjb21tYW5kcyBhbmQgcHJlc3MgRW50ZXIuCmBgYHtiYXNoLCBldmFsPUZBTFNFfQpCQU1fRElSPWJhbWZpbGVzCm1rZGlyICRCQU1fRElSCgpmb3IgZnEgaW4gJFJBV19EQVRBLyouZmFzdHEKZG8KT1VUUFVUPSQoYmFzZW5hbWUgJHtmcX18IHNlZCAncy9fcGFzcy5mYXN0cS8vZycpOwpzYW10b29scyB2aWV3IC1iUyAkU0FNX0RJUlwvJHtPVVRQVVR9LnNhbSAtbyAkQkFNX0RJUlwvJHtPVVRQVVR9LmJhbQpzYW10b29scyBzb3J0ICRCQU1fRElSXC8ke09VVFBVVH0uYmFtIC1vICRCQU1fRElSXC8ke09VVFBVVH0uc29ydGVkLmJhbQpzYW10b29scyBpbmRleCAkQkFNX0RJUlwvJHtPVVRQVVR9LnNvcnRlZC5iYW0KZG9uZQpgYGAKClRvIGdlbmVyYXRlIEJpZ1dpZyBmaWxlcyBmb3IgdGhlIHJlYWQgY292ZXJhZ2UgdmlzdWFsaXphdGlvbiBmcm9tIEJBTSBmaWxlcywgZmlyc3RseSBhcHBseSBgZ2Vub21lQ292ZXJhZ2VCZWRgIGNvbW1hbmQgZnJvbSBgYmVkdG9vbHNgIHRvIGdlbmVyYXRlIGJlZGdyYXBoIGZpbGVzLCBhbmQgdXNlIHRoZSBwYXJhbWV0ZXIgYHNwbGl0YCBhbmQgYHN0cmFuZGAgdG8gc3BsaXQgZmlsZXMgYmFzZWQgb24gdGhlaXIgc3RyYW5kLiBTYXZlIHRoZSByZXN1bHRzIGluIHRoZSBkaXJlY3RvcnkgYGJlZGdyYXBoZmlsZXNgIGJ5IHR5cGluZyB0aGUgZm9sbG93aW5nIGNvbW1hbmRzIGFuZCBwcmVzcyBFbnRlci4KYGBge2Jhc2gsIGV2YWw9RkFMU0V9CkJER19ESVI9YmVkZ3JhcGhmaWxlcwpta2RpciAkQkRHX0RJUgoKZm9yIGZxIGluICRSQVdfREFUQS8qLmZhc3RxCmRvCk9VVFBVVD0kKGJhc2VuYW1lICR7ZnF9fCBzZWQgJ3MvX3Bhc3MuZmFzdHEvL2cnKTsKIyBHZW5lcmF0ZSBnZW5vbWVDb3ZlcmFnZUJlZCBmaWxlcwpnZW5vbWVDb3ZlcmFnZUJlZCAtc3BsaXQgLXN0cmFuZCArIC1iZ2EgLWliYW0gJEJBTV9ESVJcLyR7T1VUUFVUfS5zb3J0ZWQuYmFtIHwgc29ydCAtayAxLDEgLWsyLDJuID4gJEJER19ESVJcLyR7T1VUUFVUfS5iZWRncmFwaF9md2QKZ2Vub21lQ292ZXJhZ2VCZWQgLXNwbGl0IC1zdHJhbmQgLSAtYmdhIC1pYmFtICRCQU1fRElSXC8ke09VVFBVVH0uc29ydGVkLmJhbSB8IHNvcnQgLWsgMSwxIC1rMiwybiA+ICRCREdfRElSXC8ke09VVFBVVH0uYmVkZ3JhcGhfcmV2CmRvbmUKYGBgCgpJZiB0aGUgY291bnRzIGFyZSB1c2VkIGZvciB0aGUgcmVhZCBjb3ZlcmFnZSB2aXN1YWxpemF0aW9uLCB0aGUgaGVpZ2h0IG9mIHJlYWQgcGVha3MgaXMgaW5mbHVlbmNlZCBieSBsaWJyYXJ5IHNpemUuIFRoZXJlZm9yZSwgbm9ybWFsaXphdGlvbiBvZiByZWFkIGNvdW50cyBieSBzZXF1ZW5jaW5nIGRlcHRoL2xpYnJhcnkgc2l6ZSBpcyBhIGNyaXRpY2FsIHN0ZXAgaW4gdGhlIHJlYWQgY292ZXJhZ2UgdmlzdWFsaXphdGlvbi4gQ2FsY3VsYXRlIHRoZSBsaWJyYXJ5IHNpemUgb2YgZWFjaCBzYW1wbGUgdXNpbmcgdGhlIGBzYW10b29sc2AgY29tbWFuZCwgYW5kIHNhdmUgdGhlIHJlc3VsdHMgYXMgdHh0IGZpbGVzIGluIHRoZSBkaXJlY3RvcnkgYGxpYnJhcnlzaXplZmlsZXNgLiBUaGVuIHVzZSBgcGVybCAtbGFuZWAgY29tbWFuZCB0byBjYWxjdWxhdGUgdGhlIGNvdW50cyBwZXIgbWlsbGlvbiAoQ1BNKSBudW1iZXIgbm9ybWFsaXplZCBieSB0aGUgc2FtcGxlIGxpYnJhcnkgc2l6ZS4gU2F2ZSB0aGUgcmVzdWx0cyBpbiB0aGUgZGlyZWN0b3J5IGBiZWRncmFwaGZpbGVzLm5vcm1gIGJ5IHR5cGluZyB0aGUgZm9sbG93aW5nIGNvbW1hbmRzIGFuZCBwcmVzcyBFbnRlci4KYGBge2Jhc2gsIGV2YWw9RkFMU0V9CkxCU0laRV9ESVI9bGlicmFyeXNpemVmaWxlcwpta2RpciAkTEJTSVpFX0RJUgoKQkRHTk9STV9ESVI9YmVkZ3JhcGhmaWxlcy5ub3JtCm1rZGlyICRCREdOT1JNX0RJUgoKZm9yIGZxIGluICRSQVdfREFUQS8qLmZhc3RxCmRvCk9VVFBVVD0kKGJhc2VuYW1lICR7ZnF9fCBzZWQgJ3MvX3Bhc3MuZmFzdHEvL2cnKTsKIyBDYWxjdWxhdGUgdGhlIGxpYnJhcnkgc2l6ZQpzYW10b29scyB2aWV3IC1GIDB4MTA1IC1jICAkQkFNX0RJUlwvJHtPVVRQVVR9LnNvcnRlZC5iYW0gPiAkTEJTSVpFX0RJUlwvJHtPVVRQVVR9LnRlbXAKCiMgR2VuZXJhdGUgdGhlIG5vcm1hbGl6ZWQgZ2Vub21lQ292ZXJhZ2VCZWQgZmlsZXMgZm9yIHRoZSBjb3ZlcmFnZSB2aXN1YWxpc2F0aW9uIHRvIGRlY2lkZSB0aGUgZmxhbmtpbmcgcmVnaW9ucyBpbmNsdWRlZCBpbiB0aGUgYW5ub3RhdGlvbiBmaWxlCmxpYnNpemU9JChjYXQgJExCU0laRV9ESVJcLyR7T1VUUFVUfS50ZW1wKQpwZXJsIC1sYW5lICckeHg9JEZbM10qMWU2LyckbGlic2l6ZSc7IHByaW50ICIkRlswXVx0JEZbMV1cdCRGWzJdXHQkeHgiJyAkQkRHX0RJUlwvJHtPVVRQVVR9LmJlZGdyYXBoX2Z3ZCA+ICRCREdOT1JNX0RJUlwvJHtPVVRQVVR9LmJlZGdyYXBoX2Z3ZF9ub3JtCnBlcmwgLWxhbmUgJyR4eD0kRlszXSoxZTYvJyRsaWJzaXplJzsgcHJpbnQgIiRGWzBdXHQkRlsxXVx0JEZbMl1cdCR4eCInICRCREdfRElSXC8ke09VVFBVVH0uYmVkZ3JhcGhfcmV2ID4gJEJER05PUk1fRElSXC8ke09VVFBVVH0uYmVkZ3JhcGhfcmV2X25vcm0KZG9uZQpgYGAKCkdlbmVyYXRlIEJpZ1dpZyBmaWxlcyB1c2luZyB0aGUgVUNTQyBgd2lnVG9CaWdXaWdgIGNvbW1hbmQsIGFuZCBzYXZlIHRoZSByZXN1bHRzIGluIHRoZSBkaXJlY3RvcnkgYHdpZ1RvQmlnV2lnYC4KYGBge2Jhc2gsIGV2YWw9RkFMU0V9CkJXTk9STV9ESVI9bm9ybWFsaXplZC5CaWdXaWdmaWxlcwpta2RpciAkQldOT1JNX0RJUgpmb3IgZnEgaW4gJFJBV19EQVRBLyouZmFzdHEKZG8KT1VUUFVUPSQoYmFzZW5hbWUgJHtmcX18IHNlZCAncy9fcGFzcy5mYXN0cS8vZycpOwojIEdlbmVyYXRlIGJpZ3dpZyBmaWxlcyBmb3IgdGhlIGNoZWNrIGluIHRoZSBnZW5vbWUgYnJvd3Nlci9JR1YKd2lnVG9CaWdXaWcgJEJER05PUk1fRElSXC8ke09VVFBVVH0uYmVkZ3JhcGhfZndkX25vcm0gbW0xMC5jaHJvbS5zaXplcy50eHQgJEJXTk9STV9ESVJcLyR7T1VUUFVUfS5md2QuYncKd2lnVG9CaWdXaWcgJEJER05PUk1fRElSXC8ke09VVFBVVH0uYmVkZ3JhcGhfcmV2X25vcm0gbW0xMC5jaHJvbS5zaXplcy50eHQgJEJXTk9STV9ESVJcLyR7T1VUUFVUfS5yZXYuYncKZG9uZQpgYGAKCiMgMi4gUHJlcGFyYXRpb24gb2YgcEEgc2l0ZXMgYW5ub3RhdGlvbnMKTk9URTogVGhlIHByb2Nlc3Npbmcgc3RlcHMgb2YgdGhlIHBBIHNpdGVzIGFubm90YXRpb24gZmlsZSBpcyBwcmVmb3JtZWQgZmlyc3RseSBpbiBSIGNvbnNvbGUsIHRoZW4gb24gTGludXggY29tbWFuZC1saW5lIGZvciB0aGUgcmVhZCBjb3ZlcmFnZSB2aXN1YWx6YXRpb24gYXQgcEEgc2l0ZXMgYW5kIHRoZSBnZW5lcmF0aW9uIG9mIGZpbmFsIGFubm90YXRpb24gZmlsZS4gCgojIyAyLjEgcEEgc2l0ZXMgYW5ub3RhdGlvbnMgZG93bmxvYWRpbmcgCkRvd25sb2FkIHRoZSBwQSBzaXRlIGFubm90YXRpb24gZmlsZSAiYXRsYXMuY2x1c3RlcnMuMi4wLkdSQ20zOC45Ni50c3YiIGZyb20gdGhlIFBvbHlBU2l0ZSAyLjAgZGF0YWJhc2UgYXQgaHR0cHM6Ly9wb2x5YXNpdGUudW5pYmFzLmNoLiBUbyBmaW5kIHRoZSBsaW5rIGZvciB0aGUgZG93bmxvYWQsIHNlbGVjdCAnQXRsYXMnIG9uIHRoZSB0b3AgYmFyIG9mIHRoZSBtYWluIHdlYnNpdGUsIHRoZW4gY2xpY2sgdGhlICdBdGxhcyBCRUQgZmlsZScgdW5kZXIgdGhlIHN1YnRpdGxlICdNdXMgbXVzY3VsdXM6IHYyLjAgKEdSQ20zOC45NiknLiBUaGUgdHN2IGZpbGUgaXMgZXhwZWN0ZWQgdG8gc3RhcnQgZG93bmxvYWRpbmcgYXV0b21hdGljYWxseS4KCiMjIDIuMiBMb2FkaW5nIHJlcXVpcmVkIGxpYnJhcmllcwpUeXBlIHRoZSBmb2xsb3dpbmcgY29tbWFuZHMgaW4gUiBjb25zb2xlIGFuZCBwcmVzcyBFbnRlciB0byBsb2FkIHRoZSByZXF1aXJlZCBsaWJyYXJpZXMgaW4gUi4KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeShtYWdyaXR0cikKYGBgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiMjIDIuMyBTZWxlY3Rpb24gb2YgYW5hbHlzaXMtcmVsYXRlZCBpbmZvcm1hdGlvbiBpbiB0aGUgYW5ub3RhdGlvbiBmaWxlClJlYWQgdGhlIHVucHJvY2Vzc2VkIGFubm90YXRpb24gZmlsZSBpbiBSIGFzIGFuIGRhdGEgZnJhbWUgdXNpbmcgdGhlIGByZWFkLmRlbGltYCBmdW5jdGlvbi4gQXMgb25seSBhIGZyYWN0aW9uIG9mIHRoZSBpbmZvcm1hdGlvbiBpbmNsdWRlZCBpbiB0aGUgdW5wcm9jZXNzZWQgYW5ub3RhdGlvbiBmaWxlIGlzIHJlcXVpcmVkIGZvciB0aGUgQVBBIGFuYWx5c2lzLCBwZXJmb3JtIHRoZSBpbmZvcm1hdGlvbiBzZWxlY3Rpb24gb24gYm90aCBjb2x1bW4tbGV2ZWwgYW5kIHJvdy1sZXZlbC4gT24gY29sdW1uLWxldmVsLCBzZWxlY3QgdGhlIHRlbiBjb2x1bW5zIG9mIGNocm9tb3NvbWUgbmFtZSwgZ2Vub21lIGNvb3JkaW5hdGVzIChzdGFydCBhbmQgZW5kIHBvc2l0aW9ucyBvbiBjaHJvbW9zb21lKSwgdW5pcXVlIHBBIHNpdGUgSUQsIGF2ZXJhZ2UgZXhwcmVzc2lvbiBhY3Jvc3MgYWxsIHNhbXBsZXMgKHRhZ3MgcGVyIG1pbGxpb24sIHRwbSksIGVuY29kZWQgc3RyYW5kLCByZXByZXNlbnRhdGl2ZSBwQSBzaXRlIChuYW1lbHkgY2xlYXZhZ2Ugc2l0ZSksIHBBIHNpdGUgYW5ub3RhdGlvbiwgZ2VuZSBzeW1ib2wgYW5kIEVuc2VtYmwgZ2VuZSBJZC4gT24gcm93LWxldmVsLCByZXRhaW4gMydVVFIgcEEgc2l0ZXMgb24gdGhlIG1haW4gMjAgcGFpcnMgb2YgbW91c2UgY2hyb21vc29tZXMsIHdoaWNoIGFyZSBhbm5vdGF0ZWQgYXMgVGVybWluYWwgRXhvbiAoVEUpIG9yIDEwMDAgbnQgZG93bnN0cmVhbSBvZiBhbiBhbm5vdGF0ZWQgdGVybWluYWwgZXhvbiAoRFMpIGZvciBhbmFseXNpcy4KTk9URTogQmVmb3JlIHJ1bm5pbmcgdGhpcyBzdGVwLCBlbnN1cmUgdGhlIHBhdGggb2YgdGhlIHRzdiBmaWxlIGlzIGNvcnJlY3QgYXMgdGhlIGlucHV0IG9mIHRoZSBgcmVhZC5kZWxpbWAgZnVuY3Rpb24uCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmFubm8gPC0gcmVhZC5kZWxpbSgiL1VzZXJzL2V2ZWx5bnpoZW5nLzNQU2VxX2ZpbGVzL2F0bGFzLmNsdXN0ZXJzLjIuMC5HUkNtMzguOTYudHN2IiwgCiAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UsIGNoZWNrLm5hbWVzID0gRkFMU0UsIGhlYWRlciA9IFRSVUUpICU+JSAKICBkcGx5cjo6c2VsZWN0KGNocm9tLCBjaHJvbVN0YXJ0LCBjaHJvbUVuZCwgbmFtZSwgc2NvcmUsIHN0cmFuZCwgcmVwLCBhbm5vdGF0aW9uLCBnZW5lX25hbWUsIGdlbmVfaWQpICUkJQogIC5bKC4kYW5ub3RhdGlvbiAlaW4lICJURSIpIHwgKC4kYW5ub3RhdGlvbiAlaW4lICJEUyIpLCBdICUkJQogIC5bLiRjaHJvbSAlaW4lIGFzLmNoYXJhY3RlcihjKDE6MTksICJYIiwgIlkiKSksIF0KIyBTZWxlY3Qgb3VyIGludGVzcmVzdGVkIHBBIHNpdGVzIGFubm90YXRlZCBhcyBUZXJtaW5hbCBFeG9uIChURSksIDEsMDAwIG50IERvd25TdHJlYW0gb2YgYW4gYW5ub3RhdGVkIHRlcm1pbmFsIGV4b24gKERTKQpgYGAKCiMjIDIuNCBNb2RpZnlpbmcgdGhlIGFubm90YXRpb24gZm9ybWF0IGZvciBhZGFwdGlvbgpBcyB0aGUgcGFja2FnZXMgdXNlZCBoZXJlIGZvciBBUEEgYW5hbHlzaXMgYXJlIGRlc2lnbmVkIGZvciBkaWZmZXJlbnRpYWwgZXhvbiB1c2FnZSBhbmQgc3BsaWNpbmcgYW5hbHlzaXMsIHRoZSBmb3JtYXQgb2YgdGhlIGFubm90YXRpb25zIG5lZWRzIHRvIGJlIG1vZGlmaWVkIGluIHR3byBhc3BlY3RzIGZvciB0aGUgYWRhcHRpb24gdG8gQVBBIGFuYWx5c2lzOiAKMSkgQ2hhbmdlIHRoZSBmb3JtYXQgb2YgdW5pcXVlIGZlYXR1cmUgSURzIChuYW1lbHkgdW5pcXVlIHBBIHNpdGUgY2x1c3RlciBJRHMpIGZyb20g4oCYZ3JvdXAgSUQ6IGZlYXR1cmUgSUQ6IHN0cmFuZOKAmSB0byBncm91cCBJRDogZmVhdHVyZSBJROKAmSAobmFtZWx5ICdnZW5lIElEOiBwQSBzaXRlIGNsdXN0ZXIgSUQnKS4gRm9yIHRoZSBjb252ZW5pZW5jZSBvZiBkb3duc3RyZWFtIGFuYWx5c2lzIGFuZCB0byBhdm9pZCBkdXBsaWNhdGVzIGluIHRoZSBwQSBzaXRlIGNsdXN0ZXIgSURzLCByZW1vdmUgdGhlIGxhc3QgY29tbW9uIGluIGVhY2ggb3JpZ2luYWwgY2x1c3RlciBJRCBhbmQgcmVwbGFjZSB0aGUgc3RyYW5kIGluZm9ybWF0aW9uIGArYCBhbmQgYC1gIHdpdGggbGV0dGVycyBgRmAgKEZvcndhcmQpIGFuZCBgUmAoUmV2ZXJzZSkgaW5kaXZpZHVhbGx5OyAKMikgQWRkICdjaHInIGJlZm9yZSBlYWNoIGNocm9tb3NvbWUgbnVtYmVyOwpJbiBhZGRpdGlvbiB0byB0aGUgY2hhbmdlcyBhYm92ZSwgZm9yIHRoZSB2aXN1YWxpemF0aW9uIG9mIHJlYWRzIGNvdmVyYWdlIGFuZCBwQSBjbGVhdmFnZSBzaXRlczoKMykgUmVwbGFjZSB0aGUgcEEgc2l0ZSBjb29yZGluYXRlcyBieSB0aGVpciBjb3JyZXNwb25kaW5nIGNsZWF2YWdlIHNpdGVzLgpgYGB7cn0KYW5ubyRuYW1lIDwtIGFubm8kc3RyYW5kICU+JSByZXBsYWNlKC4gJWluJSAiKyIsICJGIikgJT4lICMgUmVwbGFjZSB0aGUgc3RyYW5kIGluZm9ybWF0aW9uICcrJyB3aXRoICdGJwogIHJlcGxhY2UoLiAlaW4lICItIiwgIlIiKSAlPiUgIyBSZXBsYWNlIHRoZSBzdHJhbmQgaW5mb3JtYXRpb24gJy0nIHdpdGggJ1InCiAgcGFzdGUwKGFubm8kZ2VuZV9uYW1lLCAiOiIsIGFubm8kcmVwLCAuKSAjIEdlbmVyYXRlIG5ldyB1bmlxdWUgcEEgc2l0ZSBjbHVzdGVyIElEcwoKYW5ubyRjaHJvbSA8LSBwYXN0ZTAoImNociIsIGFubm8kY2hyb20pICMgY2hhbmdlIHRoZSBmb3JtYXQgb2YgY2hyb21vc29tZSBjb2x1bW4gZm9yIGRvd25zdHJlYW0gYW5hbHlzaXMKCiMgUmVwbGFjZSBjb29yZGluYXRlcyBvZiBwQSBzaXRlcyB3aXRoIHRoZWlyIGNvcnJlc3BvbmRpbmcgY2xlYXZhZ2Ugc2l0ZXMKYW5ubyRjaHJvbVN0YXJ0IDwtICBhbm5vJHJlcCAtMQphbm5vJGNocm9tRW5kIDwtICBhbm5vJHJlcApgYGAKCiMjIDIuNSBHZW5lcmF0aW9uIG9mIGFubm90YXRpb24gZmlsZXMgZm9yIGVhY2ggc3RyYW5kClRvIHByZXBhcmUgZm9yIHRoZSByZWFkIGNvdmVyYWdlIHZpc3VhbGl6YXRpb24gYXQgcEEgc2l0ZXMsIHNwbGl0IHRoZSBwcm9jZXNzZWQgYW5ub3RhdGlvbiBmaWxlIGludG8gdHdvIHNwZXJhdGUgZmlsZXMgYWNjb3JkaW5nIHRvIHRoZSBzdHJhbmQgaW5mb3JtYXRpb24sIHRoZW4gc2F2ZSB0aGUgdHdvIGZpbGVzIHNlcGFyYXRseSBpbiBiZWQgZm9ybWF0IGFzICdwQV9hbm5vdGF0aW9uLmZ3ZC5iZWQnIGFuZCAncEFfYW5ub3RhdGlvbi5yZXYuYmVkJyBpbiB0aGUgY3VycmVudCB3b3JraW5nIGRpcmVjdG9yeS4KYGBge3J9CiMgR2VuZXJhdGUgYW5ub3RhdGlvbiBmaWxlcyBmb3IgZWFjaCBzdHJhbmQgZm9yIGdlbm9tZUNvdmVyYWdlIHBsb3R0aW5nCmFubm8gJVQ+JSAKICB7CiAgICBhbm5vLmZ3ZCA9IC5bLiRzdHJhbmQgJWluJSAiKyIsIF0KICAgIHdyaXRlLnRhYmxlKGFubm8uZndkLCBmaWxlID0gInBBX2Fubm90YXRpb24uZndkLmJlZCIsIHJvdy5uYW1lcyA9IEZBTFNFLCBjb2wubmFtZXMgPSBGQUxTRSwgc2VwID0gIlx0IiwgcXVvdGUgPSBGQUxTRSkKICAgIH0gJT4lIHsKICAgIGFubm8ucmV2IDwtIC5bLiRzdHJhbmQgJWluJSAiLSIsIF0KICAgIHdyaXRlLnRhYmxlKGFubm8ucmV2LCBmaWxlID0gInBBX2Fubm90YXRpb24ucmV2LmJlZCIsIHJvdy5uYW1lcyA9IEZBTFNFLCBjb2wubmFtZXMgPSBGQUxTRSwgc2VwID0gIlx0IiwgcXVvdGUgPSBGQUxTRSkKICB9CmBgYAoKIyMgMi42IFNhdmluZyB0aGUgYW5ub3RhdGlvbiBmaWxlIGluIGJlZCBmb3JtYXQKU2F2ZSB0aGUgcHJvY2Vzc2VkIGFubm90YXRpb24gZmlsZSBpbiBiZWQgZm9ybWF0IGZvciBmdXJ0aGVyIG1vZGlmaWNhdGlvbiBhZnRlciBwQSBzaXRlcyByZWFkIGNvdmVyYWdlIHZpc3VhbGl6YXRpb24uCmBgYHtyfQphbm5vICU+JSB3cml0ZS50YWJsZShmaWxlID0gInBBX2Fubm90YXRpb24uYmVkIiwgcm93Lm5hbWVzID0gRkFMU0UsIGNvbC5uYW1lcyA9IEZBTFNFLCBzZXAgPSAiXHQiLCBxdW90ZSA9IEZBTFNFKQpgYGAKCiMjIDIuNyBSZWFkIGNvdmVyYWdlIHZpc3VhbGl6YXRpb24gYXQgcEEgc2l0ZXMKQWNjb3JkaW5nIHRvIHRoZSAzcC1zZXEgcHJvdG9jb2wsIHRoZSByZWFkcyBhcmUgZXhwZWN0ZWQgdG8gYmUgbWFwcGVkIHRvIHRoZSByZWdpb24gYXJvdW5kLCByYXRoZXIgdGhhbiBzcGVjaWZpY2FsbHkgYXQgcEEgY2xlYXZhZ2Ugc2l0ZXMuIFRoZXJlZm9yZSwgdmlzdWFsaXphdGlvbiB0byBjaGVjayB0aGUgcGVhayBkaXNwZXJzaW9uIG9mIG1hcHBlZCByZWFkcyBhdCBwQSBzaXRlcyB0byBvcHRpbWl6ZSB0aGUgYW5ub3RhdGlvbiBmaWxlIGZvciB0aGlzIGRhdGEgaXMgYSBjcml0aWNhbCBzdGVwIGluIHRoZSBhbm5vdGF0aW9uIGZpbGUgcHJvY2Vzc2luZy4gCgpWaXN1YWxpemUgdGhlIHBlYWtzIG9mIGFsaWduZWQgcmVhZHMgYXQgcEEgY2xlYXZhZ2Ugc2l0ZXMgdXNpbmcgYGNvbXB1dGVNYXRyaXhgIGFuZCBgcGxvdEhlYXRtYXBgIGZyb20gZGVlcFRvb2xzIGJ5IHR5cGluZyB0aGUgZm9sbG93aW5nIGNvbW1hbmRzIG9uIExpbnV4IGNvbW1hbmQtbGluZSBhbmQgcHJlc3MgRW50ZXIuIFJlbWViZXIgdG8gc2NhbGUgdGhlIGRpc3RhbmNlIHVwc3RyZWFtICgtYikgYW5kIGRvd25zdHJlYW0gKC1hKSBvZiB0aGUgY2xlYXZhZ2Ugc2l0ZXMgaWYgdGhlIHBlYWsgY2Fubm90IGJlIHZpc3VhbGl6ZWQgY29tcGxldGVseS4gU2F2ZSBtYXRyaXggcmVzdWx0cyBpbiB0aGUgY3JlYXRlZCBkaXJlY3RvcnkgYGNvbXB1dGUubWF0cml4LnJlc3VsdHNgLCBhbmQgdGhlIHBsb3RzIGluIHBkZiBpbiB0aGUgY3JlYXRlZCBkaXJlY3RvcnkgYHBsb3RIZWF0bWFwLnJlc3VsdHNgLgpgYGB7YmFzaCBldmFsPUZBTFNFfQpDTV9ESVI9Y29tcHV0ZS5tYXRyaXgucmVzdWx0cwpta2RpciAkQ01fRElSCgpITV9ESVI9cGxvdEhlYXRtYXAucmVzdWx0cwpta2RpciAkSE1fRElSCgpmb3IgZnEgaW4gJFJBV19EQVRBLyouZmFzdHEKZG8KT1VUUFVUPSQoYmFzZW5hbWUgJHtmcX18IHNlZCAncy9fcGFzcy5mYXN0cS8vZycpOwojIFZpc3VhbGl6aW5nIHRoZSByZWFkIGNvdmVyYWdlIGF0IHBBIHNpdGVzCmNvbXB1dGVNYXRyaXggcmVmZXJlbmNlLXBvaW50IC1TICRCV05PUk1fRElSXC8ke09VVFBVVH0uZndkLmJ3IC1SIHBBX2Fubm90YXRpb24uZndkLmJlZCAtYSAxMDAgLWIgMTAwIC1vICRDTV9ESVJcLyR7T1VUUFVUfS5md2QudWQxMDAuZ3ogLS1yZWZlcmVuY2VQb2ludCBjZW50ZXIgLS1zb3J0UmVnaW9ucyBubwpwbG90SGVhdG1hcCAtbSAkQ01fRElSXC8ke09VVFBVVH0uZndkLnVkMTAwLmd6IC1vICRITV9ESVJcLyR7T1VUUFVUfS5md2QudWQxMDAucGRmCgpjb21wdXRlTWF0cml4IHJlZmVyZW5jZS1wb2ludCAtUyAkQldOT1JNX0RJUlwvJHtPVVRQVVR9LnJldi5idyAtUiBwQV9hbm5vdGF0aW9uLnJldi5iZWQgLWEgMTAwIC1iIDEwMCAtbyAkQ01fRElSXC8ke09VVFBVVH0ucmV2LnVkMTAwLmd6IC0tcmVmZXJlbmNlUG9pbnQgY2VudGVyIC0tc29ydFJlZ2lvbnMgbm8KcGxvdEhlYXRtYXAgLW0gJENNX0RJUlwvJHtPVVRQVVR9LnJldi51ZDEwMC5neiAtbyAkSE1fRElSXC8ke09VVFBVVH0ucmV2LnVkMTAwLnBkZgpkb25lCmBgYAoKIyMgMi44IE1vZGlmeWluZyBwQSBzaXRlIGNvb3JkaW5hdGVzIHRvIGdlbmVyYXRlIGZpbmFsIGFubm90YXRpb24gZmlsZQpOT1RFOiBUaHJvdWdoIHRoZSB2aXN1YWxpemF0aW9uIG9mIHJlYWQgY292ZXJhZ2UgYXQgcEEgc2l0ZXMsIGl0IHdhcyBmb3VuZCB0aGF0IHRoZSBwZWFrcyBvZiB0aGUgbWFwcGVkIHJlYWRzIHdlcmUgbWFpbmx5IGRpc3BlcnNlZCB3aXRoaW4gfjYwIGJwIHVwc3RyZWFtIG9mIHRoZSBjbGVhdmFnZSBzaXRlcyAoUmVmZXIgdG8gdGhlIHN1cHBsZW1lbnRhcnkgZmlndXJlIOKAnG1lcmdlLmhlYXRtYXAucG5n4oCdKS4gVGhlcmVmb3JlLCBjb29yZGluYXRlcyBvZiBwQSBzaXRlcyBmcm9tIHRoZSBhbm5vdGF0aW9uIGZpbGUgYXJlIHN1cHBvc2VkIHRvIGJlIGV4dGVuZGVkIHRvIDYwIGJwIHVwc3RyZWFtIG9mIHRoZWlyIGNsZWF2YWdlIHNpdGVzIGluIHRoZSBhbmFseXNpcyBvZiB0aGUgUG9seUEtc2VxIGRhdGEuIFJlbWVtYmVyIHRoYXQgZGVwZW5kaW5nIG9uIHRoZSBzcGVjaWZpYyAz4oCZIGVuZCBzZXF1ZW5jaW5nIHByb3RvY29sIHVzZWQsIHRoaXMgc3RlcCB3aWxsIG5lZWQgdG8gYmUgb3B0aW1pemVkIGZvciBhc3NheXMgb3RoZXIgdGhhbiBQb2x5QS1zZXEuCgpFeHRlbmQgdGhlIGNvb3JkaW5hdGVzIG9mIHBBIHNpdGVzIHRvIDYwIGJwIHVwc3RyZWFtIG9mIHRoZWlyIGNsZWF2YWdlIHNpdGVzIGluIHRoZSBhbm5vdGF0aW9uIGZpbGUgYnkgdHlwaW5nIHRoZSBmb2xsb3dpbmcgY29tbWFuZCBvbiB0aGUgTGludXggY29tbWFuZC1saW5lLiBEb3dubG9hZCB0aGUgY2hyb21vc29tZSBzaXplIGFubm90YXRpb24gZmlsZSBmcm9tIFVDU0MgdXNpbmcgdGhlIGNvbW1hbmQgYHdnZXRgLiBTYXZlIHRoZSBwcm9jZXNzZWQgYW5ub3RhdGlvbiBmaWxlIGluIGJlZCBmb3JtYXQgZm9yIGRvd25zdHJlYW0gYW5hbHlzaXMgYXMgImZsYW5raW5nNjBhZGRlZC5wQV9hbm5vdGF0aW9uLmJlZCIuCmBgYHtiYXNoLCBldmFsPUZBTFNFfQojIERvd25sb2FkIHRoZSBjaHJvbW9zb21lIHNpemUgYW5ub3RhdGlvbiBmaWxlICdtbTEwLmNocm9tLnNpemVzLnR4dCcKd2dldCBodHRwczovL2hnZG93bmxvYWQtdGVzdC5naS51Y3NjLmVkdS9nb2xkZW5QYXRoL21tMTAvYmlnWmlwcy9tbTEwLmNocm9tLnNpemVzCgojIENoYW5nZSB0aGUgYWRkZWQgdmFsdWUgYmFzZWQgb24gdGhlIGdlbm9tZUNvdiByZXN1bHRzCmJlZHRvb2xzIHNsb3AgLWkgcEFfYW5ub3RhdGlvbi5iZWQgLWcgbW0xMC5jaHJvbS5zaXplcy50eHQgLWwgNjAgLXIgMCAtcyA+IGZsYW5raW5nNjBhZGRlZC5wQV9hbm5vdGF0aW9uLmJlZApgYGAKTk9URTogQWxsIHRoZSBzdGVwcyBpbiB0aGUgZm9sbG93aW5nIHNlY3Rpb25zIGFyZSBwZXJmb21lZCBpbiBSIGNvbnNvbGUuIAoKIyAzIENvdW50aW5nIFJlYWRzCiMjIDMuMSBMb2FkaW5nIHJlcXVpcmVkIGxpYnJhcmllcwpUeXBlIHRoZSBmb2xsb3dpbmcgY29tbWFuZHMgaW4gUiBjb25zb2xlIGFuZCBwcmVzcyBFbnRlciB0byBsb2FkIHRoZSByZXF1aXJlZCBsaWJyYXJpZXMgaW4gUi4KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KYygiUnN1YnJlYWQiLCJ0aWR5dmVyc2UiKSAlPiUgbGFwcGx5KGxpYnJhcnksIGNoYXJhY3Rlci5vbmx5ID0gVFJVRSkgJT4lIGludmlzaWJsZQpgYGAKCiMjIDMuMiBQcmVwYXJhdGlvbiBvZiB0aGUgYW5ub3RhdGlvbiBmaWxlIGluIFIKTG9hZCB0aGUgcEEgc2l0ZSBhbm5vdGF0aW9uIGluZm9ybWF0aW9uIGZyb20gdGhlIGJlZCBmaWxlIHVzaW5nIHRoZSBgcmVhZC50YWJsZWAgZnVuY3Rpb24sIHRoZW4gc2VsZWN0IGVpZ2h0IGNvbHVtbnMgdXNlZCBmb3IgdGhlIGRpZmZlcmVudGlhbCBBUEEgYW5hbHlzaXMsIGluY2x1ZGluZyB1bmlxdWUgcEEgc2l0ZSBjbHVzdGVyIElELCBjaHJvbW9zb21lIG5hbWUsIHN0YXJ0IGFuZCBlbmQgcG9zaXRpb25zIG9mIHRoZSBwQSBzaXRlIGNsdXN0ZXIsIHN0cmFuZCBvbiB3aGljaCB0aGUgY2x1c3RlciBpcyBlbmNvZGVkLCB0aGUgb3ZlcmxhcHBpbmcgZ2VuZSBlbnNlbWJsZXMgYW5kIHRoZWlyIGNvcnJlc3BvbmRpbmcgZ2VuZSBzeW1ib2xzLCBhbmQgdGhlIHJlcHJlc2VudGF0aXZlIHBBIHNpdGUgb2YgdGhlIGNsdXN0ZXIuIApOT1RFOiBCZWZvcmUgcnVubmluZyB0aGlzIHN0ZXAsIGVuc3VyZSB0aGUgcGF0aCBvZiB0aGUgYmVkIGZpbGUgaXMgY29ycmVjdCBhcyB0aGUgaW5wdXQgb2YgdGhlIGByZWFkLnRhYmxlYCBmdW5jdGlvbi4KYGBge3J9CmFubm8gPC0gcmVhZC50YWJsZShmaWxlID0gIi9Vc2Vycy9ldmVseW56aGVuZy8zUFNlcV9maWxlcy9mbGFua2luZzYwYWRkZWQucEFfYW5ub3RhdGlvbi5iZWQiLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UsIGNoZWNrLm5hbWVzID0gRkFMU0UsIGhlYWRlciA9IEZBTFNFLCBzZXAgPSAiIikKY29sbmFtZXMoYW5ubykgPC0gYygiQ2hyIiwgIlN0YXJ0IiwgIkVuZCIsICJHZW5lSUQiLCAiU2NvcmUiLCAiU3RyYW5kIiwgInJlcElEIiwgIkFubm8iLCAiU3ltYm9sIiwgIkVuc2VtYmwiKQphbm5vIDwtIGRwbHlyOjpzZWxlY3QoYW5ubywgR2VuZUlELCBDaHIsIFN0YXJ0LCBFbmQsIFN0cmFuZCwgRW5zZW1ibCwgU3ltYm9sLCByZXBJRCkKYGBgCgojIyAzLjMgQXBwbHlpbmcgYGZlYWN0dXJlQ291bnRzYCB0byBhY3F1aXJlIHRoZSByYXcgcmVhZCBjb3VudHMKUmVhZCBhbGwgYmFtIGZpbGVzIG9idGFpbmVkIGFzIGlucHV0IGZvciBgZmVhdHVyZUNvdW50cygpYCBmcm9tIGBSc3VicmVhZGAgcGFja2FnZSB0byBhY3F1aXJlIHRoZSByYXcgY291bnRzIG9mIHJlYWRzIG1hcHBlZCB0byBlYWNoIGFubm90YXRlZCBwQSBzaXRlLCB0aGUgZm9ybWF0IG9mIHdoaWNoIGlzIGV4cGVjdGVkIHRvIGJlIGEgbWF0cml4IG9mIGNvdW50cyBhc3NvY2lhdGVkIHdpdGggZWFjaCBmZWF0dXJlIHdpdGggcm93cyByZXByZXNlbnRpbmcgZXhvbnMoZmVhdHVyZXMpIGFuZCBjb2x1bW5zIHJlcHJlc2VudGluZyBzYW1wbGVzLiBTYXZlIHRoZSBjb3VudCB0YWJsZSBhcyB0aGUgUkRhdGEgZmlsZSAiQVBBX2NvdW50RGF0YS5SRGF0YSIgZm9yIEFQQSBhbmFseXNpcyB1c2luZyBkaWZmZXJlbnQgdG9vbHMuCk5PVEU6IEJlZm9yZSBydW5uaW5nIHRoaXMgc3RlcCwgZW5zdXJlIHRoZSBmb2xkZXIgY29udGFpbmluZyBiYW0gZmlsZXMgaXMgaW4gdGhlIGN1cnJlbnQgd29ya2luZyBkaXJlY3RvcnkuCmBgYHtyLCBldmFsPUZBTFNFfQojIEFzIHRoZSBydW5uaW5nIG9mIGZlYXR1cmVDb3VudHMgcmVxdWlyZXMgYXJvdW5kIDI1IEcgbWVtb3J5LCBkdXJpbmcgdGhlIGdlbmVyYXRpb24gb2YgdGhpcyBSbWQgZmlsZSwgdGhpcyBzdGVwIHdhcyBydW5uaW5nIG9uIHNlcnZlciBhbmQgdGhlIHJlc3VsdCB3YXMgc2F2ZWQgYXMgImNvdW50RGF0YS5SRGF0YSIgYW5kIGxvYWRlZCBpbiB0aGUgbmV4dCBSIHNlc3Npb24uCmNvdW50RGF0YSA8LSBkaXIoImJhbWZpbGVzIiwgcGF0dGVybj0ic29ydGVkLmJhbSQiLCBmdWxsLm5hbWVzID0gVFJVRSkgJT4lICAKICAjIFJlYWQgYWxsIGJhbSBmaWxlcyBhcyBpbnB1dCBmb3IgZmVhdHVyZUNvdW50cwogIGZlYXR1cmVDb3VudHMoYW5ub3QuZXh0ID0gYW5ubywgaXNHVEZBbm5vdGF0aW9uRmlsZSA9IEZBTFNFLCBtaW5NUVMgPSAwLCB1c2VNZXRhRmVhdHVyZXMgPSBUUlVFLCAKICAgICAgICAgICAgICAgIGFsbG93TXVsdGlPdmVybGFwID0gVFJVRSwgbGFyZ2VzdE92ZXJsYXAgPSBUUlVFLCBzdHJhbmRTcGVjaWZpYyA9IDEsCiAgICAgICAgICAgICAgICBjb3VudE11bHRpTWFwcGluZ1JlYWRzID0gVFJVRSwgcHJpbWFyeU9ubHkgPSBUUlVFLCBpc1BhaXJlZEVuZCA9IEZBTFNFLCBudGhyZWFkcyA9IDEyKSAlVD4lIApzYXZlKGZpbGUgPSAiQVBBX2NvdW50RGF0YS5SRGF0YSIpCmBgYApOT1RFOiBCZSBjb25zY2lvdXMgdG8gY2hhbmdlIGFueSBvZiB0aGUgcGFyYW1ldGVycyBsaXN0ZWQgaW4gdGhlIGBmZWF0dXJlQ291bnRzYCBmdW5jdGlvbi4gTW9kaWZ5IHRoZSBgc3RyYW5kU3BlY2lmaWNgIHBhcmFtZXRlciB0byBlbnN1cmUgaXQgaXMgY29uc2lzdGVudCB3aXRoIHRoZSBzZXF1ZW5jaW5nIGRpcmVjdGlvbiBvZiB0aGUgM+KAmSBlbmQgc2VxdWVuY2luZyBhc3NheSB1c2VkIChlbXBpcmljYWxseSwgdmlzdWFsaXppbmcgdGhlIGRhdGEgaW4gYSBnZW5vbWUgYnJvd3NlciBvdmVyIGdlbmVzIG9uIHBsdXMgYW5kIG1pbnVzIHN0cmFuZHMgd2lsbCBjbGFyaWZ5IHRoaXMpLgoKIyMgMy40IEFwcGx5aW5nIG5vbi1zcGVjaWZpYyBmaWx0ZXJpbmcgb2YgY291bnREYXRhCkFzIGZpbHRlcmluZyBvZiB0aGUgcmF3IHJlYWQgY291bnRzIGNhbiBzaWduaWZpY2FudGx5IGltcHJvdmUgdGhlIHN0YXRpc3RpY2FsIHJvYnVzdG5lc3MgaW4gZGlmZmVyZW50aWFsIHBBIHNpdGUgdXNhZ2UgdGVzdHMsIGFwcGx5IHJlYWQgY291bnRzIGZpbHRlcmluZyBvbiB0d28gbGV2ZWxzOgoxKSBPbiBnZW5lLWxldmVsLCByZW1vdmUgZ2VuZXMgd2l0aCBvbmx5IG9uZSBwQSBzaXRlLCBvbiB3aGljaCBkaWZmZXJlbnRpYWwgcEEgc2l0ZSB1c2FnZSBjYW5ub3QgYmUgZGVmaW5lZDsKMikgT24gZ2VuZS1sZXZlbCwgcmVtb3ZlIGdlbmVzIGFubm90YXRlZCBhcyBOQSwgd2hpY2ggd2lsbCBoaW5kZXIgdGhlIGRvd25zdHJlYW0gcHJvY2Vzc2luZyBhbmQgYW5hbHlzaXM7CjMpIE9uIHBBIHNpdGUtbGV2ZWwsIGFwcGx5IG5vbi1zcGVjaWZpYyBmaWx0ZXJpbmcgYmFzZWQgb24gY292ZXJhZ2U6IGZpbHRlciBjb3VudHMgYnkgY3BtIChjb3VudHMgcGVyIG1pbGxpb24pIGxlc3MgdGhhbiAxIGluIHggb3V0IG9mIG4gc2FtcGxlcywgd2hlcmUgeCBpcyB0aGUgbWluaW11bSBudW1iZXIgb2YgcmVwbGljYXRlcyBpbiBhbnkgY29uZGl0aW9uLiBuID0gOCBhbmQgeCA9IDIgZm9yIHRoaXMgZXhhbXBsZSBkYXRhLgpOT1RFOiBCZWZvcmUgcnVubmluZyB0aGlzIHN0ZXAsIGVuc3VyZSB0aGUgcGF0aCBvZiB0aGUgUkRhdGEgZmlsZSBpcyBjb3JyZWN0IGFzIHRoZSBpbnB1dCBvZiB0aGUgYGxvYWRgIGZ1bmN0aW9uLgpgYGB7cn0KbG9hZChmaWxlID0gIi9Vc2Vycy9ldmVseW56aGVuZy8zUFNlcV9maWxlcy9BUEFfY291bnREYXRhLlJEYXRhIikgIyBTa2lwIHRoaXMgc3RlcCBpZiBpdCBpcyBhbHJlYWR5IGxvYWRlZCAKCiMgTm9uLXNwZWNpZmljIGZpbHRlcmluZzogUmVtb3ZlIHRoZSBwQSBzaXRlcyBub3QgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGluIHRoZSBzYW1wbGVzCmNvdW50RGF0YSA8LSAgY291bnREYXRhJGNvdW50cyAlPiUgYXMuZGF0YS5mcmFtZSAlPiUgLltyb3dTdW1zKGVkZ2VSOjpjcG0oLikgPiAxKSA+PSAyLCBdCmFubm8gJTw+JSAuWy4kR2VuZUlEICVpbiUgcm93bmFtZXMoY291bnREYXRhKSwgXSAKCiMgUmVtb3ZlIGdlbmVzIHdpdGggb25seSAxIHNpdGUgYW5kICBOQSBpbiBnZW5lSURzCmRuc2l0ZXMgPC0gYW5ubyAlPiUgZ3JvdXBfYnkoU3ltYm9sKSAlPiUgc3VtbWFyaXNlKG5zaXRlcz1uKCkpICU+JSBmaWx0ZXIobnNpdGVzID4gMSAmICFpcy5uYShTeW1ib2wpKQphbm5vICU8PiUgZmlsdGVyKFN5bWJvbCAlaW4lIGRuc2l0ZXMkU3ltYm9sKQpjb3VudERhdGEgJTw+JSAuW3Jvd25hbWVzKC4pICVpbiUgYW5ubyRHZW5lSUQsIF0KYGBgCgoKIyA0IERpZmZlcmVudGlhbCBwb2x5YWRlbnlsYXRpb24gc2l0ZXMgdXNhZ2UgYW5hbHlzaXMgdXNpbmcgREVYU2VxIGFuZCBkaWZmU3BsaWNlIHBpcGVsaW5lcwojIyA0LjEgVXNpbmcgREVYU2VxIHBhY2thZ2UgCk5PVEU6IEFzIGEgY29udHJhc3QgbWF0cml4IGNhbm5vdCBiZSBkZWZpbmVkIGZvciB0aGUgREVYU2VxIHBpcGVsaW5lLCB0aGUgZGlmZmVyZW50aWFsIEFQQSBhbmFseXNpcyBvZiBlYWNoIHR3byBleHBlcmltZW50YWwgY29uZGl0aW9ucyBoYXMgdG8gYmUgZGV2ZWxvcGVkIHNlcGFyYXRseS4gSGVyZSB0aGUgZGlmZmVyZW50aWFsIEFQQSBhbmFseXNpcyBvZiB0aGUgY29uZGl0aW9uIHdpbGQgdHlwZSAoV1QpIGFuZCBNYm5sIDEvMiBkb3VibGUga25vY2tvdXQgKERLTykgaXMgZGV2ZWxvcGVkIGFzIGFuIGV4YW1wbGUgdG8gZXhwbGFpbiB0aGUgcHJvY2VkdXJlLiBUaGUgYW5hbHlzaXMgb2YgdGhlIGNvbnRyYXN0IHBhaXJzIFdUIGFuZCBLRCwgV1QgYW5kIEN0cmwgYXJlIGF0dGFjaGVkIGF0IHRoZSBlbmQuCgojIyMgNC4xLjEgTG9hZGluZyByZXF1aXJlZCBsaWJyYXJpZXMKVHlwZSB0aGUgZm9sbG93aW5nIGNvbW1hbmRzIGluIFIgY29uc29sZSBhbmQgcHJlc3MgRW50ZXIgdG8gbG9hZCB0aGUgcmVxdWlyZWQgbGlicmFyaWVzIGluIFIuCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmMoIkRFWFNlcSIsICJHZW5vbWljUmFuZ2VzIikgJT4lIGxhcHBseShsaWJyYXJ5LCBjaGFyYWN0ZXIub25seSA9IFRSVUUpICU+JSBpbnZpc2libGUKYGBgCgojIyMgNC4xLjIgQ3JlYXRpbmcgdGhlIHNhbXBsZSB0YWJsZSB0byBkZWZpbmUgdGhlIGV4cGVyaW1lbnRhbCBkZXNpZ24KQ3JlYXRlIHRoZSBgc2FtcGxlVGFibGVgLCB3aGljaCBjb25zaXN0cyBvZiBkZXRhaWxzIG9mIGVhY2ggc2FtcGxlIHdpdGggaXRzIGxpYnJhcnktdHlwZSBhbmQgY29uZGl0aW9uLiBFbnN1cmUgdGhlIG9yZGVyIG9mIHJvdyBuYW1lcyBpcyBjb25zaXN0ZW50IHdpdGggdGhlIG9yZGVyIG9mIGJhbSBmaWxlIG5hbWVzIHVzZWQgYnkgYGZlYXR1cmVDb3VudHNgIHRvIGNvdW50IHRoZSByZWFkcy4gCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CnNhbXBsZVRhYmxlMSA8LSBkYXRhLmZyYW1lKHJvdy5uYW1lcyA9IGMoIldUXzEiLCJXVF8yIiwiREtPXzEiLCJES09fMiIpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgY29uZGl0aW9uID0gYyhyZXAoIldUIiwgMiksIHJlcCgiREtPIiwgMikpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgbGliVHlwZSA9IHJlcCgic2luZ2xlLWVuZCIsIDQpKQpgYGAKCiMjIyA0LjEuMyBQcmVwYXJpbmcgdGhlIHBBIHNpdGVzIGluZm9ybWF0aW9uIGZpbGUgdXNpbmcgQmlvY29uZHVjdG9yIEdSYW5nZXMKcEEgc2l0ZSBpbmZvcm1hdGlvbiBpbiB0aGUgZm9ybSBvZiBHUmFuZ2VzIG9iamVjdHMgKGh0dHBzOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9yZWxlYXNlL2Jpb2MvaHRtbC9HZW5vbWljUmFuZ2VzLmh0bWwpIGlzIHJlcXVpcmVkIGFzIGFuIGlucHV0IHRvIGNyZWF0ZSBERVhTZXEgb2JqZWN0LiBFeHRyYWN0IHRoZSBhbm5vdGF0aW9uIGZyb20gdGhlIHByb2Nlc3NlZCBhbm5vdGF0aW9uIG9iamVjdCB0byBjcmVhdGUgUEFTaW5mbyBvYmplY3QgZm9yIHRoZSBwcmVwYXJhdGlvbiBvZiBERVhTZXEgb2JqZWN0IGNvbnN0cnVjdGlvbi4KYGBge3J9CiMgUHJlcGFyZSB0aGUgR1JhbmdlcyBvYmplY3QgZm9yIERFWFNlcURhdGFTZXQgb2JqZWN0IGNvbnN0cnVjdGlvbgpQQVNpbmZvIDwtIEdSYW5nZXMoc2VxbmFtZXMgPSBhbm5vJENociwKICAgICAgICAgICAgICAgICAgIHJhbmdlcyA9IElSYW5nZXMoc3RhcnQgPSBhbm5vJFN0YXJ0LCBlbmQgPSBhbm5vJEVuZCksCiAgICAgICAgICAgICAgICAgICBzdHJhbmQgPSBSbGUoYW5ubyRTdHJhbmQpKQptY29scyhQQVNpbmZvKSRQQVNJRCA8LSBhbm5vJHJlcElECm1jb2xzKFBBU2luZm8pJEdlbmVFbnMgPC0gYW5ubyRFbnNlbWJsCm1jb2xzKFBBU2luZm8pJEdlbmVJRCA8LSBhbm5vJFN5bWJvbAoKIyBQcmVwYXJlIHRoZSBuZXcgZmVhdHVyZSBJRHMsIHJlcGxhY2UgdGhlIHN0cmFuZCBpbmZvcm1hdGlvbiB3aXRoIGxldHRlcnMgdG8gbWF0Y2ggdGhlIGN1cnJlbnQgcEEgc2l0ZSBjbHVzdGVySUQKbmV3LmZlYXR1cmVJRCA8LSBhbm5vJFN0cmFuZCAlPiUgYXMuY2hhcmFjdGVyICU+JSByZXBsYWNlKC4gJWluJSAiKyIsICJGIikgJT4lIHJlcGxhY2UoLiAlaW4lICItIiwgIlIiKSAlPiUgcGFzdGUwKGFzLmNoYXJhY3Rlcihhbm5vJHJlcElEKSwgLikKYGBgCgojIyMgNC4xLjQgQ3JlYXRpbmcgdGhlIERFWFNlcSBvYmplY3QKQ3JlYXRlIHRoZSBERVhTZXEgb2JqZWN0IHVzaW5nIHRoZSBmdW5jdGlvbiBgREVYU2VxRGF0YVNldGAgdG8gY29sbGVjdDogMSkgdGhlIGZpbHRlcmVkIHJlYWQgY291bnRzIGdlbmVyYXRlZCBpbiBzdGVwIDM7IDIpIHNhbXBsZSBpbmZvcm1hdGlvbiBmcm9tIHRoZSBgc2FtcGxlVGFibGVgOyAzKSBkZXNpZ24gbWF0cml4IHdoaWNoIGlzIGdlbmVyYXRlZCBiYXNlZCBvbiB0aGUgc2FtcGxlRGF0YSBhbmQgYSBtb2RlbCBmb3JtdWxhIG5vdGF0aW9uIGZvciB0aGUgZGlmZmVyZW50aWFsIEFQQSB0ZXN0aW5nOyA0KSBmZWF0dXJlIGluZm9ybWF0aW9uIGluY2x1ZGluZyBwQSBzaXRlIElEcyBhbmQgdGhlaXIgY29ycmVzcG9uZGluZyBnZW5lIHN5bWJvbHMuCmBgYHtyfQpjb3VudERhdGExIDwtIGRwbHlyOjpzZWxlY3QoY291bnREYXRhLCBTUlIxNTUzMTI5LnNvcnRlZC5iYW0sIFNSUjE1NTMxMzAuc29ydGVkLmJhbSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBTUlIxNTUzMTMxLnNvcnRlZC5iYW0sIFNSUjE1NTMxMzIuc29ydGVkLmJhbSkgIyBTZWxlY3QgdGhlIHJlYWQgY291bnRzIG9mIHRoZSBjb25kdGlvbiBXVCBhbmQgREtPCmNvbG5hbWVzKGNvdW50RGF0YTEpIDwtIHJvd25hbWVzKHNhbXBsZVRhYmxlMSkgIyBSZW5hbWUgdGhlIGNvbHVtbnMgb2YgY291bnREYXRhIHVzaW5nIHNhbXBsZSBuYW1lcyBpbiBzYW1wbGVUYWJsZQpkeGQxIDwtIERFWFNlcURhdGFTZXQoY291bnREYXRhID0gY291bnREYXRhMSwKICAgICAgICAgICAgICAgICAgICAgc2FtcGxlRGF0YSA9IHNhbXBsZVRhYmxlMSwgCiAgICAgICAgICAgICAgICAgICAgIGRlc2lnbiA9IH4gc2FtcGxlICsgZXhvbiArIGNvbmRpdGlvbjpleG9uLAogICAgICAgICAgICAgICAgICAgICBmZWF0dXJlSUQgPSBuZXcuZmVhdHVyZUlELAogICAgICAgICAgICAgICAgICAgICBncm91cElEID0gYW5ubyRTeW1ib2wsCiAgICAgICAgICAgICAgICAgICAgIGZlYXR1cmVSYW5nZXMgPSBQQVNpbmZvKQpgYGAKTk9URTogVGhpcyBzdGVwIG1pZ2h0IHRha2Ugc29tZSB0aW1lLiBPbmNlIHRoZSBvYmplY3QgaXMgY3JlYXRlZCwgZXhwbG9yZSB0aGUgY29sdW1ucyBmb3IgaW5pdGlhbCB1bmRlcnN0YW5kaW5nIG9mIGRhdGEuCgpgYGB7cn0KIyBFeHBsb3JlL0luc3BlY3QgdGhlIERFWFNlcSBvYmplY3QKaGVhZChjb3VudHMoZHhkMSksIDUpCmNvbERhdGEoZHhkMSkKc3BsaXQoc2VxX2xlbihuY29sKGR4ZDEpKSwgY29sRGF0YShkeGQxKSRleG9uKQojaGVhZChmZWF0dXJlQ291bnRzKGR4ZDEpLCA1KQpoZWFkKHJvd1JhbmdlcyhkeGQxKSwgMykKc2FtcGxlQW5ub3RhdGlvbihkeGQxKQpgYGAKCiMjIyA0LjEuNSBEZWZpbmluZyB0aGUgY29udHJhc3QgcGFpciBpbiB0aGUgY291bnREYXRhCkRlZmluZSB0aGUgY29udHJhc3QgcGFpciBpbiB0aGUgZm9sbG93aW5nIGFuYWx5c2lzIHRocm91Z2ggZGVmaW5pbmcgdGhlIGxldmVscyBvZiBjb25kaXRpb25zIGluIERFWFNlcSBvYmplY3QuIElmIG5vdCBkZWZpbmVkLCB0aGUgY29uZGl0aW9ucyBzYXZlZCBpbiBERVhTZXEgb2JqZWN0IHdpbGwgYmUgdHJhbnNmZXJyZWQgdG8gYWxwaGFiZXRpY2FsIG9yZGVyLWJhc2VkIGZhY3RvcnMsIGFuZCB0aGUgY29udHJhc3QgcGFpcnMgd2lsbCBiZSAiJ3RoZSBjb25kaXRpb24gd2l0aCBoaWdoZXIgbGV2ZWwnIC0gJ3RoZSBjb25kaXRpb24gd2l0aCBsb3dlciBsZXZlbCciLiAKYGBge3J9CmR4ZDEkY29uZGl0aW9uIDwtIGZhY3RvcihkeGQxJGNvbmRpdGlvbiwgbGV2ZWxzID0gYygiV1QiLCAiREtPIikpICMgVGhlIGNvbnRyYXN0IHBhaXIgd2lsbCBiZSAiREtPIC0gV1QiCmBgYAoKIyMjIDQuMS42IE5vcm1hbGl6YXRpb24gYW5kIGRpc3BlcnNpb24gRXN0aW1hdGlvbgpTaW1pbGFyIHRvIFJOQS1zZXEgZGF0YSwgZm9yIDPigJkgZW5kIHNlcXVlbmNpbmcgZGF0YSBwZXJmb3JtIG5vcm1hbGl6YXRpb24gYmV0d2VlbiBzYW1wbGVzIChjb2x1bW4td2lzZSBtZWRpYW4gb2YgcmF0aW9zIGZvciBlYWNoIHNhbXBsZSkgdXNpbmcgdGhlIGBlc3RpbWF0ZVNpemVGYWN0b3JzYCBmdW5jdGlvbiwgYW5kIGVzdGltYXRlIHRoZSB2YXJpYXRpb24gb2YgdGhlIGRhdGEgdXNpbmcgd2l0aCB0aGUgYGVzdGltYXRlRGlzcGVyc2lvbnNgIGZ1bmN0aW9uLCB0aGVuIHZpc3VhbGl6ZSB0aGUgZGlzcGVyc2lvbiBlc3RpbWF0aW9uIHJlc3VsdCB1c2luZyB0aGUgYHBsb3REaXNwRXN0c2AgZnVuY3Rpb24uCmBgYHtyLCBmaWcuYWxpZ249ImNlbnRlciJ9CmR4ZDEgJTw+JSBlc3RpbWF0ZVNpemVGYWN0b3JzICU+JSBlc3RpbWF0ZURpc3BlcnNpb25zICVUPiUgcGxvdERpc3BFc3RzICMgVmlzdWFsaXphdGlvbiBvZiB0aGUgZGlzcGVyc2lvbiBlc3RpbWF0aW9uIHJlc3VsdApgYGAKCiMjIyA0LjEuNyBEaWZmZXJlbnRpYWwgcEEgc2l0ZSB1c2FnZSB0ZXN0aW5nClRlc3QgZm9yIGRpZmZlcmVudGlhbCBwQSBzaXRlIHVzYWdlIGZvciBlYWNoIGdlbmUgYW5kIGdlbmVyYXRlIHRoZSByZXN1bHRzIHVzaW5nIHRoZSBmdW5jdGlvbiBgdGVzdEZvckRFVWAsIHRoZW4gZXN0aW1hdGUgdGhlIGZvbGQgY2hhbmdlIG9mIHBBIHNpdGUgdXNhZ2UgdXNpbmcgdGhlIGZ1bmN0aW9uIGBlc3RpbWF0ZUV4b25Gb2xkQ2hhbmdlc2AuIENoZWNrIHRoZSByZXN1bHRzIHVzaW5nIHRoZSBmdW5jdGlvbiBgREVYU2VxUmVzdWx0c2AgYW5kIHNldCDigJhGRFIgPCAxMCXigJkgYXMgdGhlIGNyaXRlcmlvbiBmb3Igc2lnbmlmaWNhbnRseSBkaWZmZXJlbnRpYWwgcEEgc2l0ZXMuCmBgYHtyfQpkeGQxICU8PiUgdGVzdEZvckRFVSAlPiUgZXN0aW1hdGVFeG9uRm9sZENoYW5nZXMoZml0RXhwVG9WYXIgPSAiY29uZGl0aW9uIikgI0VzdGltYXRlIGZvbGQgY2hhbmdlcwpkeHIxIDwtIERFWFNlcVJlc3VsdHMoZHhkMSkKZHhyMQojZHhyICA8LSAgbmEub21pdChkeHIpCm1jb2xzKGR4cjEpJGRlc2NyaXB0aW9uCiMjIC0tLS10YWxseVBBU3MtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KdGFibGUoZHhyMSRwYWRqIDwgMC4xKSAjIENoZWNrIHRoZSBudW1iZXIgb2YgZGlmZmVyZW50aWFsIHBBIHNpdGVzIChGRFIgPCAwLjEpCiMjIC0tLS10YWxseUdlbmVzLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCnRhYmxlKHRhcHBseShkeHIxJHBhZGogPCAwLjEsIGR4cjEkZ3JvdXBJRCwgYW55KSkgIyBDaGVjayB0aGUgbnVtYmVyIG9mIGdlbmUgb3ZlcmxhcHBlZCB3aXRoIGRpZmZlcmVudGlhbCBwQSBzaXRlIGNsdXN0ZXJzIChGRFIgPCAwLjEpCmBgYAoKIyMjIDQuMS44IFZpc3VhbGl6YXRpb24gb2YgdGhlIGRpZmZlcmVudGlhbCBwQSBzaXRlIHVzYWdlIHJlc3VsdHMKYGBge3J9CiMgU2VsZWN0IHRoZSB0b3AgMTAwIHNpZ25pZmljYW50IGRpZmZlcm50aWFsIHBBIHNpdGUgdXNhZ2UKdG9wZGlmZi5QQVMgPC0gZHhyMSAlPiUgYXMuZGF0YS5mcmFtZSAlPiUgcm93bmFtZXNfdG9fY29sdW1uICU+JSBhcnJhbmdlKHBhZGopICUkJSBncm91cElEWzE6MTAwXQpoZWFkKHRvcGRpZmYuUEFTKQpgYGAKClVzZSB0aGUgYHBsb3RERVhTZXFgIGZ1bmN0aW9uIGZvciB2aXN1YWxpemF0aW9uIG9mIGRpZmZlcmVudGlhbCBwQSB1c2FnZSByZXN1bHQuIFVzZSBkaWZmZXJlbnQgZ2VuZSBuYW1lcyB0byBleHBsb3JlIHRoZSByZXN1bHRzOgpgYGB7ciwgZmlnLmFsaWduPSJjZW50ZXIifQpwbG90REVYU2VxKGR4cjEsICJTMTAwYTdhIiwgbGVnZW5kID0gVFJVRSwgZXhwcmVzc2lvbiA9IEZBTFNFLCBzcGxpY2luZyA9IFRSVUUsIGNleC5heGlzID0gMS4yLCBjZXggPSAxLjMsIGx3ZCA9IDIpCmBgYAoKVG8gZGlzcGxheSBkaWZmZXJlbnRpYWwgcGxveUEgdXNhZ2UgdXNlIGBzcGxpY2luZyA9IFRSVUVgOgpgYGB7ciwgZmlnLmFsaWduPSJjZW50ZXIifQpwbG90REVYU2VxKGR4cjEsICJGb3NsMiIsIGxlZ2VuZCA9IFRSVUUsIGV4cHJlc3Npb24gPSBGQUxTRSwgc3BsaWNpbmcgPSBUUlVFLCBjZXguYXhpcyA9IDEuMiwgY2V4ID0gMS4zLCBsd2QgPSAyKQpgYGAKClRvIGRpc3BsYXkgYm90aCBleHBlcnNzaW9uIGFuZCBkaWZmZXJlbnRpYWwgcGxveUEgdXNhZ2UgdXNlIGBzcGxpY2luZyA9IFRSVUVgIGFuZCBgZXhwcmVzc2lvbiA9IFRSVUVgOgpgYGB7ciwgZmlnLmFsaWduPSJjZW50ZXIifQpwbG90REVYU2VxKGR4cjEsICJQYXBvbGEiLCBsZWdlbmQgPSBUUlVFLCBleHByZXNzaW9uID0gRkFMU0UsIHNwbGljaW5nID0gVFJVRSwgY2V4LmF4aXMgPSAxLjIsIGNleCA9IDEuMywgbHdkID0gMikKYGBgCgpHZW5lcmF0ZSBwbG90cyBmb3IgYWxsIHRoZSB0b3AgMzAwIHNpZ25pZmljYW50IGdlbmVzIHVzaW5nIGBwZXJHZW5lUVZhbHVlYCBmdW5jdGlvbjoKYGBge3J9CmR4cjEgJTw+JSAuWyFpcy5uYSguJHBhZGopLCBdCQpkZ2VuZSA8LSBkYXRhLmZyYW1lKHBlckdlbmVRVmFsdWU9cGVyR2VuZVFWYWx1ZShkeHIxKSkgJT4lIHJvd25hbWVzX3RvX2NvbHVtbigiZ3JvdXBJRCIpCgpkZVBBUzEgPC0gZHhyMSAlPiUgZGF0YS5mcmFtZSgpICU+JSAKICBkcGx5cjo6c2VsZWN0KC1tYXRjaGVzKCJkaXNwZXJzaW9ufHN0YXR8Y291bnREYXRhfGdlbm9taWNEYXRhIikpICU+JSAKICBpbm5lcl9qb2luKGRnZW5lKSAlPiUgYXJyYW5nZShwZXJHZW5lUVZhbHVlKSAlPiUgZGlzdGluY3QoKQoKIyBTYXZlIHRoZSBzaWduaWZpY2FudCBkaWZmZXJlbnRpYWwgcEEgc2l0ZXMgKG9yZGVyIGJ5IHBlckdlbmVRVmFsdWUpCiJvcGVueGxzeCIgJT4lIGxhcHBseShsaWJyYXJ5LCBjaGFyYWN0ZXIub25seSA9IFRSVUUpICU+JSBpbnZpc2libGUKZGVQQVNfc2lnMSA8LSBkZVBBUzEgJT4lIGZpbHRlcihwYWRqIDwgMC4xKSAlVD4lCiAgd3JpdGUueGxzeChmaWxlID0gIjNQU2VxX0RFWFNlcV9zaWduaWZpY2FudF9nZW5lcy54bHN4Iiwgc2hlZXROYW1lID0gIkRLTyB2cyBXVCIsIGZpcnN0Um93ID0gVFJVRSwgaGVhZGVyU3R5bGUgPSBjcmVhdGVTdHlsZSh0ZXh0RGVjb3JhdGlvbiA9ICJCT0xEIiwgaGFsaWduID0gImNlbnRlciIpKQpgYGAKQ2hlY2sgdGhlIGluZm9ybWF0aW9uIG9mIHRoZSB0b3AgMzAwIGRpZmZlcm50aWFsIHBBIHNpdGVzLgpgYGB7ciBtZXNzYWdlPVRSVUUsIHBhZ2VkLnByaW50PVRSVUV9CkRUOjpkYXRhdGFibGUoZGVQQVNfc2lnMVsxOjMwMCwgXSkKYGBgCkdlbmVyYXRlIHBsb3RzIG9mIHRoZSB0b3AgMzAwIGRpZmZlcm50aWFsIHBBIHNpdGVzLgpgYGB7cn0KcGRmKGZpbGU9ICIzUFNlcV9ERVhTZXFfc2lnbmlmaWNhbnRfZ2VuZXMuREtPX1dULnRvcDMwMC5wZGYiKQpzaWdfZ2VuZXMgPC0gZGVQQVNfc2lnMSAlPiUgZmlsdGVyKHBlckdlbmVRVmFsdWUgPD0gMC4wMSkgJSQlIGdyb3VwSUQgJT4lIHVuaXF1ZSAlPiUgLlsxOjMwMF0KCmZvciAoZ2VuZWlkIGluIHNpZ19nZW5lcykgewogIG5uIDwtIGR4cjEgJT4lIC5bLiRncm91cElEICVpbiUgZ2VuZWlkLCBdICU+JSBucm93CiAgaWYgKG5uID4gMSkgewogICAgcGxvdERFWFNlcShkeHIxLCBnZW5laWQsIGxlZ2VuZCA9IFRSVUUsIGV4cHJlc3Npb24gPSBGQUxTRSwgc3BsaWNpbmcgPSBUUlVFLCBjZXguYXhpcyA9IDEuMiwgY2V4ID0gMS4zLCBsd2QgPSAyKQogIH0KfQpkZXYub2ZmKCkKYGBgCgpVc2UgdGhlIGBFbmhhbmNlZFZvbGNhbm9gIHBhY2thZ2UgdG8gdmlzdWFsaXNlIGRpZmZlcmVudGlhbCBwQSBzaXRlIHVzYWdlIG9mIHRoZSBjb250cmFzdCBwYWlyLgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQoiRW5oYW5jZWRWb2xjYW5vIiAlPiUgbGFwcGx5KGxpYnJhcnksIGNoYXJhY3Rlci5vbmx5ID0gVFJVRSkgJT4lIGludmlzaWJsZQpgYGAKCmBgYHtyIGZpZy5hbGlnbj0iY2VudGVyIn0KRW5oYW5jZWRWb2xjYW5vKGRlUEFTX3NpZzEsIGxhYiA9IGRlUEFTX3NpZzEkZ3JvdXBJRCwgeCA9ICdsb2cyZm9sZF9ES09fV1QnLCB5ID0gJ3B2YWx1ZScsIHRpdGxlID0gJ1ZvbGNhbm8gUGxvdCcsIAogICAgICAgICAgICAgICAgc3VidGl0bGUgPSAnREtPIHZzIFdUJywgRkNjdXRvZmYgPSAxLCBsYWJTaXplID0gNCwgbGVnZW5kUG9zaXRpb24gPSAicmlnaHQiLCAKICAgICAgICAgICAgICAgIGNhcHRpb24gPSBicXVvdGUofkxvZ1syXX4gIkZvbGQgY2hhbmdlIGN1dG9mZiwgMTsgRkRSIDEwJSIpKQpgYGAKTk9URTogRW5oYW5jZWRWb2xjYW5vIHBhY2thZ2Ugd2FzIHVzZWQgdG8gZ2VuZXJhdGUgdGhlIGFib3ZlIHBsb3QsIHlvdSB3aWxsIG5lZWQgdG8gaW5zdGFsbCBpdCBpZiBub3QgYWxyZWFkeSBpbnN0YWxsZWQuCgojIyMgNC4xLjkgRGV2ZWxvcGluZyBkaWZmZXJlbnRpYWwgcEEgc2l0ZSB1c2FnZSB0ZXN0aW5nIG9mIHRoZSBjb250cmFzdCBwYWlyICJLRCAtIFdUIgpgYGB7cn0KY291bnREYXRhMiA8LSBkcGx5cjo6c2VsZWN0KGNvdW50RGF0YSwgU1JSMTU1MzEyOS5zb3J0ZWQuYmFtLCBTUlIxNTUzMTMwLnNvcnRlZC5iYW0sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgU1JSMTU1MzEzMy5zb3J0ZWQuYmFtLCBTUlIxNTUzMTM0LnNvcnRlZC5iYW0pICMgU2VsZWN0IHRoZSByZWFkIGNvdW50cyBvZiB0aGUgY29uZHRpb24gV1QgYW5kIEtEIApzYW1wbGVUYWJsZTIgPC0gZGF0YS5mcmFtZShyb3cubmFtZXMgPSBjKCJXVF8xIiwgIldUXzIiLCAiS0RfMSIsICJLRF8yIiksIAogICAgICAgICAgICAgICAgICAgICAgICAgIGNvbmRpdGlvbiA9IGMocmVwKCJXVCIsIDIpLCByZXAoIktEIiwgMikpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICBsaWJUeXBlID0gcmVwKCJzaW5nbGUtZW5kIiwgNCkpCmR4ZDIgPC0gREVYU2VxRGF0YVNldChjb3VudERhdGEgPSBjb3VudERhdGEyLAogICAgICAgICAgICAgICAgICAgICBzYW1wbGVEYXRhID0gc2FtcGxlVGFibGUyLCAKICAgICAgICAgICAgICAgICAgICAgZGVzaWduID0gfiBzYW1wbGUgKyBleG9uICsgY29uZGl0aW9uOmV4b24sCiAgICAgICAgICAgICAgICAgICAgIGZlYXR1cmVJRCA9IG5ldy5mZWF0dXJlSUQsCiAgICAgICAgICAgICAgICAgICAgIGdyb3VwSUQgPSBhbm5vJFN5bWJvbCwKICAgICAgICAgICAgICAgICAgICAgZmVhdHVyZVJhbmdlcyA9IFBBU2luZm8pCgpkeGQyJGNvbmRpdGlvbiA8LSBmYWN0b3IoZHhkMiRjb25kaXRpb24sIGxldmVscyA9IGMoIldUIiwgIktEIikpCmR4ZDIgJTw+JSBlc3RpbWF0ZVNpemVGYWN0b3JzICU+JSBlc3RpbWF0ZURpc3BlcnNpb25zICU+JSB0ZXN0Rm9yREVVICU+JSBlc3RpbWF0ZUV4b25Gb2xkQ2hhbmdlcyhmaXRFeHBUb1ZhciA9ICJjb25kaXRpb24iKQpkeHIyIDwtIERFWFNlcVJlc3VsdHMoZHhkMikKCm1jb2xzKGR4cjIpJGRlc2NyaXB0aW9uCiMjIC0tLS10YWxseVBBU3MtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KdGFibGUoZHhyMiRwYWRqIDwgMC4xKSAjIENoZWNrIHRoZSBudW1iZXIgb2YgZGlmZmVyZW50aWFsIHBBIHNpdGVzIChGRFIgPCAwLjEpCiMjIC0tLS10YWxseUdlbmVzLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCnRhYmxlKHRhcHBseShkeHIyJHBhZGogPCAwLjEsIGR4cjIkZ3JvdXBJRCwgYW55KSkgIyBDaGVjayB0aGUgbnVtYmVyIG9mIGdlbmUgb3ZlcmxhcHBlZCB3aXRoIGRpZmZlcmVudGlhbCBwQSBzaXRlIGNsdXN0ZXJzIChGRFIgPCAwLjEpCmBgYAoKYGBge3IsIGZpZy5hbGlnbj0iY2VudGVyIn0KIyBTZWxlY3Rpb24gYW5kIHZpc3VhbGl6YXRpb24gb2YgdGhlIHJlc3VsdHMKdG9wZGlmZi5QQVMyIDwtIGR4cjIgJT4lIGFzLmRhdGEuZnJhbWUgJT4lIHJvd25hbWVzX3RvX2NvbHVtbiAlPiUgYXJyYW5nZShwYWRqKSAlJCUgZ3JvdXBJRFsxOjEwMF0KaGVhZCh0b3BkaWZmLlBBUzIpICAgICAgICAgCgpwbG90REVYU2VxKGR4cjIsICJUcG0xIiwgbGVnZW5kID0gVFJVRSwgZXhwcmVzc2lvbiA9IEZBTFNFLCBzcGxpY2luZyA9IFRSVUUsIGNleC5heGlzID0gMS4yLCBjZXggPSAxLjMsIGx3ZCA9IDIpCmBgYAoKYGBge3J9CmR4cjIgJTw+JSAuWyFpcy5uYSguJHBhZGopLCBdCmRnZW5lMiA8LSBkYXRhLmZyYW1lKHBlckdlbmVRVmFsdWU9cGVyR2VuZVFWYWx1ZShkeHIyKSkgJT4lIHJvd25hbWVzX3RvX2NvbHVtbigiZ3JvdXBJRCIpCgpkZVBBUzIgPC0gZHhyMiAlPiUgZGF0YS5mcmFtZSgpICU+JSAKICBkcGx5cjo6c2VsZWN0KC1tYXRjaGVzKCJkaXNwZXJzaW9ufHN0YXR8Y291bnREYXRhfGdlbm9taWNEYXRhIikpICU+JSAKICBpbm5lcl9qb2luKGRnZW5lMikgJT4lIGFycmFuZ2UocGVyR2VuZVFWYWx1ZSkgJT4lIGRpc3RpbmN0KCkKCiMgU2F2ZSB0aGUgc2lnbmlmaWNhbnQgZGlmZmVyZW50aWFsIHBBIHNpdGVzIChvcmRlciBieSBwZXJHZW5lUVZhbHVlKQphZGQueGxzeC5zaGVldCA8LSBmdW5jdGlvbihmaWxlTmFtZSwgc2hlZXROYW1lLCBkYXRhKXsKICB3YiA8LSBsb2FkV29ya2Jvb2soZmlsZU5hbWUpCiAgYWRkV29ya3NoZWV0KHdiLCBzaGVldE5hbWUgPSBzaGVldE5hbWUpCiAgZnJlZXplUGFuZSh3Yiwgc2hlZXQgPSBzaGVldE5hbWUsIGZpcnN0Um93ID0gVFJVRSkKICB3cml0ZURhdGEod2IsIHNoZWV0ID0gc2hlZXROYW1lLCBkYXRhLCBoZWFkZXJTdHlsZSA9IGNyZWF0ZVN0eWxlKHRleHREZWNvcmF0aW9uID0gIkJPTEQiLCBoYWxpZ24gPSAiY2VudGVyIikpCiAgc2F2ZVdvcmtib29rKHdiLCBmaWxlID0gZmlsZU5hbWUsIG92ZXJ3cml0ZSA9IFRSVUUpCn0KZGVQQVNfc2lnMiA8LSBkZVBBUzIgJT4lIGZpbHRlcihwYWRqIDwgMC4xKSAlVD4lCiAgYWRkLnhsc3guc2hlZXQoZmlsZSA9ICIzUFNlcV9ERVhTZXFfc2lnbmlmaWNhbnRfZ2VuZXMueGxzeCIsICJLRCB2cyBXVCIsIC4pCmBgYApgYGB7ciBtZXNzYWdlPVRSVUUsIHBhZ2VkLnByaW50PVRSVUV9CkRUOjpkYXRhdGFibGUoZGVQQVNfc2lnMlsxOjMwMCwgXSkKYGBgCgpgYGB7ciwgZmlnLmFsaWduPSJjZW50ZXIifQpFbmhhbmNlZFZvbGNhbm8oZGVQQVNfc2lnMiwgbGFiID0gZGVQQVNfc2lnMiRncm91cElELCB4ID0gJ2xvZzJmb2xkX0tEX1dUJywgeSA9ICdwdmFsdWUnLCB0aXRsZSA9ICdWb2xjYW5vIFBsb3QnLCAKICAgICAgICAgICAgICAgIHN1YnRpdGxlID0gJ0tEIHZzIFdUJywgRkNjdXRvZmYgPSAxLCBsYWJTaXplID0gNCwgbGVnZW5kUG9zaXRpb24gPSAicmlnaHQiLCAKICAgICAgICAgICAgICAgIGNhcHRpb24gPSBicXVvdGUofkxvZ1syXX4gIkZvbGQgY2hhbmdlIGN1dG9mZiwgMTsgRkRSIDEwJSIpKQpgYGAKCiMjIyA0LjEuMTAgRGV2ZWxvcGluZyBkaWZmZXJlbnRpYWwgcEEgc2l0ZSB1c2FnZSB0ZXN0aW5nIG9mIHRoZSBjb250cmFzdCBwYWlyICJDdHJsIC0gV1QiCmBgYHtyfQpjb3VudERhdGEzIDwtIGRwbHlyOjpzZWxlY3QoY291bnREYXRhLCBTUlIxNTUzMTI5LnNvcnRlZC5iYW0sIFNSUjE1NTMxMzAuc29ydGVkLmJhbSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBTUlIxNTUzMTM1LnNvcnRlZC5iYW0sIFNSUjE1NTMxMzYuc29ydGVkLmJhbSkgIyBTZWxlY3QgdGhlIHJlYWQgY291bnRzIG9mIHRoZSBjb25kdGlvbiBXVCBhbmQgS0QgCnNhbXBsZVRhYmxlMyA8LSBkYXRhLmZyYW1lKHJvdy5uYW1lcyA9IGMoIldUXzEiLCAiV1RfMiIsICJDdHJsXzEiLCAiQ3RybF8yIiksIAogICAgICAgICAgICAgICAgICAgICAgICAgIGNvbmRpdGlvbiA9IGMocmVwKCJXVCIsIDIpLCByZXAoIkN0cmwiLCAyKSksIAogICAgICAgICAgICAgICAgICAgICAgICAgIGxpYlR5cGUgPSByZXAoInNpbmdsZS1lbmQiLCA0KSkKZHhkMyA8LSBERVhTZXFEYXRhU2V0KGNvdW50RGF0YSA9IGNvdW50RGF0YTMsCiAgICAgICAgICAgICAgICAgICAgIHNhbXBsZURhdGEgPSBzYW1wbGVUYWJsZTMsIAogICAgICAgICAgICAgICAgICAgICBkZXNpZ24gPSB+IHNhbXBsZSArIGV4b24gKyBjb25kaXRpb246ZXhvbiwKICAgICAgICAgICAgICAgICAgICAgZmVhdHVyZUlEID0gbmV3LmZlYXR1cmVJRCwKICAgICAgICAgICAgICAgICAgICAgZ3JvdXBJRCA9IGFubm8kU3ltYm9sLAogICAgICAgICAgICAgICAgICAgICBmZWF0dXJlUmFuZ2VzID0gUEFTaW5mbykKCmR4ZDMkY29uZGl0aW9uIDwtIGZhY3RvcihkeGQzJGNvbmRpdGlvbiwgbGV2ZWxzID0gYygiV1QiLCAiQ3RybCIpKQpkeGQzICU8PiUgZXN0aW1hdGVTaXplRmFjdG9ycyAlPiUgZXN0aW1hdGVEaXNwZXJzaW9ucyAlPiUgdGVzdEZvckRFVSAlPiUgZXN0aW1hdGVFeG9uRm9sZENoYW5nZXMoZml0RXhwVG9WYXIgPSAiY29uZGl0aW9uIikKZHhyMyA8LSBERVhTZXFSZXN1bHRzKGR4ZDMpCgptY29scyhkeHIzKSRkZXNjcmlwdGlvbgojIyAtLS0tdGFsbHlQQVNzLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCnRhYmxlKGR4cjMkcGFkaiA8IDAuMSkgIyBDaGVjayB0aGUgbnVtYmVyIG9mIGRpZmZlcmVudGlhbCBwQSBzaXRlcyAoRkRSIDwgMC4xKQojIyAtLS0tdGFsbHlHZW5lcy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQp0YWJsZSh0YXBwbHkoZHhyMyRwYWRqIDwgMC4xLCBkeHIzJGdyb3VwSUQsIGFueSkpICMgQ2hlY2sgdGhlIG51bWJlciBvZiBnZW5lIG92ZXJsYXBwZWQgd2l0aCBkaWZmZXJlbnRpYWwgcEEgc2l0ZSBjbHVzdGVycyAoRkRSIDwgMC4xKQpgYGAKCmBgYHtyLCBmaWcuYWxpZ249ImNlbnRlciJ9CiMgU2VsZWN0aW9uIGFuZCB2aXN1YWxpemF0aW9uIG9mIHRoZSByZXN1bHRzCnRvcGRpZmYuUEFTMyA8LSBkeHIzICU+JSBhcy5kYXRhLmZyYW1lICU+JSByb3duYW1lc190b19jb2x1bW4gJT4lIGFycmFuZ2UocGFkaikgJSQlIGdyb3VwSURbMToxMDBdCmhlYWQodG9wZGlmZi5QQVMzKSAgICAgICAgIAoKcGxvdERFWFNlcShkeHIzLCAiVHBtMSIsIGxlZ2VuZCA9IFRSVUUsIGV4cHJlc3Npb24gPSBGQUxTRSwgc3BsaWNpbmcgPSBUUlVFLCBjZXguYXhpcyA9IDEuMiwgY2V4ID0gMS4zLCBsd2QgPSAyKQpgYGAKCmBgYHtyfQpkeHIzICU8PiUgLlshaXMubmEoLiRwYWRqKSwgXQpkZ2VuZTMgPC0gZGF0YS5mcmFtZShwZXJHZW5lUVZhbHVlPXBlckdlbmVRVmFsdWUoZHhyMykpICU+JSByb3duYW1lc190b19jb2x1bW4oImdyb3VwSUQiKQoKZGVQQVMzIDwtIGR4cjMgJT4lIGRhdGEuZnJhbWUoKSAlPiUgCiAgZHBseXI6OnNlbGVjdCgtbWF0Y2hlcygiZGlzcGVyc2lvbnxzdGF0fGNvdW50RGF0YXxnZW5vbWljRGF0YSIpKSAlPiUgCiAgaW5uZXJfam9pbihkZ2VuZTMpICU+JSBhcnJhbmdlKHBlckdlbmVRVmFsdWUpICU+JSBkaXN0aW5jdCgpCgojIFNhdmUgdGhlIHNpZ25pZmljYW50IGRpZmZlcmVudGlhbCBwQSBzaXRlcyAob3JkZXIgYnkgcGVyR2VuZVFWYWx1ZSkKZGVQQVNfc2lnMyA8LSBkZVBBUzMgJT4lIGZpbHRlcihwYWRqIDwgMC4xKSAlVD4lCiAgYWRkLnhsc3guc2hlZXQoZmlsZSA9ICIzUFNlcV9ERVhTZXFfc2lnbmlmaWNhbnRfZ2VuZXMueGxzeCIsICJDdHJsIHZzIFdUIiwgLikKYGBgCgpgYGB7ciBtZXNzYWdlPVRSVUUsIHBhZ2VkLnByaW50PVRSVUV9CkRUOjpkYXRhdGFibGUoZGVQQVNfc2lnM1sxOjMwMCwgXSkKYGBgCgpgYGB7ciwgZmlnLmFsaWduPSJjZW50ZXIifQpFbmhhbmNlZFZvbGNhbm8oZGVQQVNfc2lnMywgbGFiID0gZGVQQVNfc2lnMyRncm91cElELCB4ID0gJ2xvZzJmb2xkX0N0cmxfV1QnLCB5ID0gJ3B2YWx1ZScsIHRpdGxlID0gJ1ZvbGNhbm8gUGxvdCcsIAogICAgICAgICAgICAgICAgc3VidGl0bGUgPSAnQ3RybCB2cyBXVCcsIEZDY3V0b2ZmID0gMSwgbGFiU2l6ZSA9IDQsIGxlZ2VuZFBvc2l0aW9uID0gInJpZ2h0IiwgCiAgICAgICAgICAgICAgICBjYXB0aW9uID0gYnF1b3RlKH5Mb2dbMl1+ICJGb2xkIGNoYW5nZSBjdXRvZmYsIDE7IEZEUiAxMCUiKSkKYGBgCgojIyA0LjIgVXNpbmcgZGlmZlNwbGljZSBwYWNrYWdlCiMjIyA0LjIuMSBMb2FkaW5nIHJlcXVpcmVkIGxpYnJhcmllcwpUeXBlIHRoZSBmb2xsb3dpbmcgY29tbWFuZHMgaW4gUiBjb25zb2xlIGFuZCBwcmVzcyBFbnRlciB0byBsb2FkIHRoZSByZXF1aXJlZCBsaWJyYXJpZXMgaW4gUi4KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KYygibGltbWEiLCAiZWRnZVIiKSAlPiUgbGFwcGx5KGxpYnJhcnksIGNoYXJhY3Rlci5vbmx5ID0gVFJVRSkgJT4lIGludmlzaWJsZQpgYGAKCiMjIyA0LjIuMiBDcmVhdGluZyB0aGUgc2FtcGxlIHRhYmxlIHRvIGRlZmluZSB0aGUgZXhwZXJpbWVudGFsIGRlc2lnbgpDcmVhdGUgdGhlIGBzYW1wbGVUYWJsZWAsIHdoaWNoIGNvbnNpc3RzIG9mIGRldGFpbHMgb2YgZWFjaCBzYW1wbGUgd2l0aCBpdHMgbGlicmFyeS10eXBlIGFuZCBjb25kaXRpb24uIEVuc3VyZSB0aGUgb3JkZXIgb2Ygcm93IG5hbWVzIGlzIGNvbnNpc3RlbnQgd2l0aCB0aGUgb3JkZXIgb2YgYmFtIGZpbGUgbmFtZXMgdXNlZCBieSBgZmVhdHVyZUNvdW50c2AgdG8gY291bnQgdGhlIHJlYWRzLiBSZW1lbWJlciB0aGF0IGFzIHRoZSBjb250cmFzdCBwYWlycyBjYW4gYmUgZGVmaW5lZCBkaXJlY3RseSBpbiB0aGUgZGlmZlNwbGljZSBmdW5jdGlvbiwgdGhlIGBzYW1wbGVUYWJsZWAgY3JlYXRlZCBpbiB0aGlzIHN0ZXAgaW5jbHVkZXMgYWxsIHRoZSByZWFkIGNvdW50cyBvZiBhbGwgY29uZGl0aW9ucy4KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0Kc2FtcGxlVGFibGUgPC0gZGF0YS5mcmFtZShyb3cubmFtZXMgPSBjKCJXVF8xIiwiV1RfMiIsIkRLT18xIiwiREtPXzIiLCAiS0RfMSIsICJLRF8yIiwgIkN0cmxfMSIsICJDdHJsXzIiKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgY29uZGl0aW9uID0gYyhyZXAoIldUIiwgMiksIHJlcCgiREtPIiwgMiksIHJlcCgiS0QiLCAyKSwgcmVwKCJDdHJsIiwgMikpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICBsaWJUeXBlID0gcmVwKCJzaW5nbGUtZW5kIiwgNCkpCmBgYAoKIyMjIDQuMi4zIENyZWF0aW5nIHRoZSBER0VMaXN0IG9iamVjdCBhbmQgcHJvY2Vzc2luZwpDcmVhdGUgdGhlIERHRUxpc3QgdXNpbmcgdGhlIGZ1bmN0aW9uIGBER0VMaXN0YCB0byBjb2xsZWN0IHRoZSBmaWx0ZXJlZCByZWFkIGNvdW50cyBnZW5lcmF0ZWQgaW4gc3RlcCAzLiBSZW1lbWJlciB0aGF0IGFzIHRoZSBjb250cmFzdCBwYWlycyBjYW4gYmUgZGVmaW5lZCBkaXJlY3RseSBpbiB0aGUgbmV4dCBzdGVwLCB0aGUgREdFTGlzdCBvYmplY3QgY3JlYXRlZCBpbiB0aGlzIHN0ZXAgaW5jbHVkZSBhbGwgdGhlIHJlYWQgY291bnRzIG9mIGFsbCBjb25kaXRpb25zLiBUaGVuIGRldmVsb3AgcHJvY2Vzc2luZyBzdGVwcyBvZiB0aGUgREdFTGlzdCBvYmplY3QgaW5jbHVkZXM6IAoxKSBOb3JtYWxpemF0aW9uIG9mIHRoZSByZWFkIGNvdW50cyBhY3Jvc3Mgc2FtcGxlcyB1c2luZyBUcmltbWVkIE1lYW5kIG9mIE0gdmFsdWVzIChUTU0gbm9ybWFsaXphdGlvbikgYnkgdGhlIGZ1bmN0aW9uIGBjYWxjTm9ybUZhY3RvcnNgOyAKMikgRGlmZmVyZW50aWFsIGV4cHJlc3Npb24gdGVzdGluZyBhY3Jvc3MgYWxsIGNvbmRpdGlvbnMgdXNpbmcgdGhlIGZ1bmN0aW9uIGB2b29tV2l0aFF1YWxpdHlXZWlnaHRzYDsKMykgTGluZWFyIG1vZGVsbGluZyBiYXNlZCBvbiB0aGUgY291bnQgZGF0YSB1c2luZyB0aGUgZnVuY3Rpb24gYGxpbUZpdGA7CjQpIENvbXB1dGluZyBtb2RlcmF0ZWQgc3RhdGlzdGljYWwgcmVzdWx0cyB1c2luZyB0aGUgZnVuY3Rpb24gYGVCYXllc2AgZ2l2ZW4gdGhlIGxpbmVhciBtb2RlbCBmaXQgZnJvbSB0aGUgYGxpbUZpdGAuCmBgYHtyfQpmaXQgPC0gewogIGRnZSA8LSBER0VMaXN0KGNvdW50cyA9IGNvdW50RGF0YSkgJT4lIGNhbGNOb3JtRmFjdG9ycwogIFRyZWF0IDwtIGZhY3RvcihzYW1wbGVUYWJsZSRjb25kaXRpb24sIGxldmVscyA9IGMoIldUIiwgIkRLTyIsICJLRCIsICJDdHJsIikpCiAgZGVzaWduIDwtIG1vZGVsLm1hdHJpeCh+MCArIFRyZWF0KQogIGNvbG5hbWVzKGRlc2lnbikgPC0gbGV2ZWxzKFRyZWF0KQogIHZvb21XaXRoUXVhbGl0eVdlaWdodHMoZGdlLCBkZXNpZ24sIHBsb3QgPSBGQUxTRSkKfSAlPiUgbG1GaXQoZGVzaWduKSAlPiUgZUJheWVzCmBgYAoKIyMjIDQuMi40IERlZmluaW5nIGNvbnRyYXN0cyBhbmQgcHJvY2Vzc2luZwpEZWZpbmUgdGhlIGNvbnRyYXN0cyBvZiBpbnRlcmVzdCBmb3IgZGlmZmVyZW50aWFsIHBBIHVzYWdlIGFuYWx5c2lzIHVzaW5nIGBtYWtlQ29udHJhc3RzYCBmdW5jdGlvbi4gVGhlbiBwcm9jZXNzIHRoZSBER0VMaXN0IG9iamVjdCBgZml0YCBnZW5lcmF0ZWQgaW4gdGhlIHByZXZpb3VzIHN0ZXAgdXNpbmcgdGhlIGZ1bmN0aW9uIGBjb250cmFzdHMuZml0YCBhbmQgYGVCYXllc2AgdG8gYWNxdWlyZSB0aGUgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gcmVzdWx0cyBvZiB0aGUgY29udHJhc3QgcGFpcnMuCmBgYHtyfQpjb250cmFzdC5tYXRyaXggPC0gbWFrZUNvbnRyYXN0cyhES09fdnNfV1QgPSBES08tV1QsIEtEX3ZzX1dUID0gS0QtV1QsIEN0cmxfdnNfV1QgPSBDdHJsLVdULCBsZXZlbHMgPSBkZXNpZ24pCmZpdDIgPC0gZml0ICU+JSBjb250cmFzdHMuZml0KGNvbnRyYXN0Lm1hdHJpeCkgJT4lIGVCYXllcwpzdW1tYXJ5KGRlY2lkZVRlc3RzKGZpdDIpKQpgYGAKCiMjIyA0LjIuNSBEaWZmZXJlbnRpYWwgcEEgc2l0ZSB1c2FnZSBhbmFseXNpcwpEZXZlbG9wIHRoZSBkaWZmZXJlbnRpYWwgcEEgc2l0ZSB1c2FnZSBhbmFseXNpcyBmb3IgZWFjaCBkZWZpbmVkIGNvbnRyYXNjdCBwYWlyIG9uIHRoZSBmaXR0ZWQgbW9kZWwgdXNpbmcgdGhlIGZ1bmN0aW9uIGBkaWZmU3BsaWNlYC4KYGBge3J9CmV4IDwtIGRpZmZTcGxpY2UoZml0MiwgZ2VuZWlkID0gYW5ubyRTeW1ib2wsIGV4b25pZCA9IG5ldy5mZWF0dXJlSUQpCgojQ2hlY2sgdGhlIHRvcCBzaWduaWZpY2FudCByZXN1bHRzIHdpdGggdG9wU3BsaWNlCnRvcFNwbGljZShleCkKYGBgCk5PVEU6IFRoaXMgc3RlcCB1c2VzIHRoZSBQQVMgaW5mb3JtYXRpb24gb2JqZWN0IGNyZWF0ZWQgaW4gNC4xLjMuIEVuc3VyZSBpdCBpcyBsb2FkZWQgaW4gdGhlIGN1cnJlbnQgd29ya2luZyBlbnZpcm9ubWVudC4KCiMjIyA0LjIuNiBWaXN1YWxpemluZyB0aGUgcmVzdWx0IG9mIHRoZSBjb250cmFzdCBwYWlyICJES08gLSBXVCIKQ2hlY2sgdGhlIGluZm9ybWF0aW9uIG9mIHRoZSB0b3AgMzAwIGRpZmZlcm50aWFsIHBBIHNpdGVzLgpgYGB7ciBtZXNzYWdlPVRSVUUsIHBhZ2VkLnByaW50PVRSVUV9CnNpZzEgPC10b3BTcGxpY2UoZXgsIG4gPSBJbmYsIEZEUiA9IDAuMSwgY29lZiA9IDEsIHRlc3Q9ICJ0Iiwgc29ydC5ieSA9ICJsb2dGQyIpICMgU2F2ZSB0aGUgcmVzdWx0cyBzb3J0ZWQgYnkgbG9nRkMgd2l0aCBGRFIgPCAwLjEKRFQ6OmRhdGF0YWJsZShzaWcxWzE6MzAwLCBdKQpgYGAKCkNoZWNrIHRoZSBpbmZvcm1hdGlvbiBvZiAzMDAgZ2VuZXMgd2l0aCB0aGUgdG9wIGRpZmZlcm50aWFsIEFQQS4KYGBge3IgbWVzc2FnZT1UUlVFLCBwYWdlZC5wcmludD1UUlVFfQpzaWcxLmdlbmVzIDwtdG9wU3BsaWNlKGV4LCBuID0gSW5mLCBGRFIgPSAwLjEsIGNvZWYgPSAxLCB0ZXN0PSAic2ltZXMiKSAjIFNhdmUgdGhlIHJlc3VsdHMgd2l0aCBGRFIgPCAwLjEKRFQ6OmRhdGF0YWJsZShzaWcxLmdlbmVzWzE6MzAwLCBdKQpgYGAKClZpc3VhbGl6ZSB0aGUgZGlmZmVyZW50aWFsIEFQQSByZXN1bHQgdXNpbmcgdGhlIGBwbG90U3BsaWNlYCBmdW5jdGlvbiBhbmQgdm9sY2FubyBwbG90cy4KYGBge3IsIGZpZy5hbGlnbj0iY2VudGVyIn0KcGRmKGZpbGUgPSAiM1BTZXFfZGlmZlNwbGljZV9wbG90U3BsaWNlX0RLT19XVC5wZGYiKQpwbG90U3BsaWNlKGV4LCBjb2VmID0gMSwgZ2VuZWlkID0gIlMxMDBhN2EiLCBGRFIgPSAwLjEpCnBsb3RTcGxpY2UoZXgsIGNvZWYgPSAxLCBnZW5laWQgPSAiVHBtMSIsIEZEUiA9IDAuMSkKcGxvdFNwbGljZShleCwgY29lZiA9IDEsIGdlbmVpZCA9ICJTbWM2IiwgRkRSID0gMC4xKQpkZXYub2ZmKCkKCnBkZihmaWxlID0gIjNQU2VxX2RpZmZTcGxpY2VfVm9sY2Fub19ES09fV1QucGRmIiwgaGVpZ2h0ID0gMTAsIHdpZHRoID0gMTIpCkVuaGFuY2VkVm9sY2FubyhzaWcxLCBsYWIgPSBzaWcxJEdlbmVJRCwgeGxhYiA9IGJxdW90ZSh+TG9nWzJdfiAnZm9sZCBjaGFuZ2UnKSwgCiAgICAgICAgICAgICAgICB4ID0gJ2xvZ0ZDJywgeSA9ICdQLlZhbHVlJywgdGl0bGUgPSAnVm9sY2FubyBQbG90Jywgc3VidGl0bGUgPSAnREtPIHZzIFdUJywgCiAgICAgICAgICAgICAgICAjIHBDdXRvZmYgPSAxMGUtMTYsIAogICAgICAgICAgICAgICAgRkNjdXRvZmYgPSAxLCBsYWJTaXplID0gOCwgbGVnZW5kUG9zaXRpb24gPSAicmlnaHQiKQpkZXYub2ZmKCkKCiMgU2F2ZSB0aGUgbGlzdCBvZiB0b3AgZGlmZmVyZW50aWFsIHBBIHNpdGVzIGF0IEZEUiAxMCUKd3JpdGUueGxzeChzaWcxLCBmaWxlID0gIjNQU2VxX2RpZmZTcGxpY2Vfc2lnbmlmaWNhbnRfZ2VuZXMueGxzeCIsIHNoZWV0TmFtZSA9ICJES08gdnMgV1QgKHBBIHNpdGUtbGV2ZWwpIiwgCiAgICAgICAgICAgZmlyc3RSb3cgPSBUUlVFLCBoZWFkZXJTdHlsZSA9IGNyZWF0ZVN0eWxlKHRleHREZWNvcmF0aW9uID0gIkJPTEQiLCBoYWxpZ24gPSAiY2VudGVyIikpCgojIFNhdmUgdGhlIGxpc3Qgb2YgZ2VuZXMgd2l0aCB0b3AgZGlmZmVyZW50aWFsIEFQQSBhdCBGRFIgMTAlCmFkZC54bHN4LnNoZWV0KGZpbGUgPSAiM1BTZXFfZGlmZlNwbGljZV9zaWduaWZpY2FudF9nZW5lcy54bHN4IiwgIkRLTyB2cyBXVCAoZ2VuZS1sZXZlbCkiLCBzaWcxLmdlbmVzKQoKYGBgCgojIyMgNC4yLjcgVmlzdWFsaXppbmcgdGhlIHJlc3VsdCBvZiB0aGUgY29udHJhc3QgcGFpciAiS0QgLSBXVCIKQ2hlY2sgdGhlIGluZm9ybWF0aW9uIG9mIHRoZSB0b3AgMzAwIGRpZmZlcm50aWFsIHBBIHNpdGVzLgpgYGB7ciBtZXNzYWdlPVRSVUUsIHBhZ2VkLnByaW50PVRSVUV9CnNpZzIgPC10b3BTcGxpY2UoZXgsIG4gPSBJbmYsIEZEUiA9IDAuMSwgY29lZiA9IDIsIHRlc3Q9ICJ0Iiwgc29ydC5ieSA9ICJsb2dGQyIpCkRUOjpkYXRhdGFibGUoc2lnMlsxOjMwMCwgXSkKYGBgCgpDaGVjayB0aGUgaW5mb3JtYXRpb24gb2YgMzAwIGdlbmVzIHdpdGggdGhlIHRvcCBkaWZmZXJudGlhbCBBUEEuCmBgYHtyIG1lc3NhZ2U9VFJVRSwgcGFnZWQucHJpbnQ9VFJVRX0Kc2lnMi5nZW5lcyA8LXRvcFNwbGljZShleCwgbiA9IEluZiwgRkRSID0gMC4xLCBjb2VmID0gMiwgdGVzdD0gInNpbWVzIikgIyBTYXZlIHRoZSByZXN1bHRzIHdpdGggRkRSIDwgMC4xCkRUOjpkYXRhdGFibGUoc2lnMi5nZW5lc1sxOjMwMCwgXSkKYGBgCgpWaXN1YWxpemUgdGhlIGRpZmZlcmVudGlhbCBBUEEgcmVzdWx0IHVzaW5nIHRoZSBgcGxvdFNwbGljZWAgZnVuY3Rpb24gYW5kIHZvbGNhbm8gcGxvdHMuCmBgYHtyLCBmaWcuYWxpZ249ImNlbnRlciJ9CnBkZihmaWxlID0gIjNQU2VxX2RpZmZTcGxpY2VfcGxvdFNwbGljZV9LRF9XVC5wZGYiKQpwbG90U3BsaWNlKGV4LCBjb2VmID0gMiwgZ2VuZWlkID0gIk1iZDIiLCBGRFIgPSAwLjEpCnBsb3RTcGxpY2UoZXgsIGNvZWYgPSAyLCBnZW5laWQgPSAiU3NicDEiLCBGRFIgPSAwLjEpCnBsb3RTcGxpY2UoZXgsIGNvZWYgPSAyLCBnZW5laWQgPSAiUGZuMSIsIEZEUiA9IDAuMSkKZGV2Lm9mZigpCgpwZGYoZmlsZSA9ICIzUFNlcV9kaWZmU3BsaWNlX1ZvbGNhbm9fS0RfV1QucGRmIiwgaGVpZ2h0ID0gMTAsIHdpZHRoID0gMTIpCkVuaGFuY2VkVm9sY2FubyhzaWcyLCBsYWIgPSBzaWcyJEdlbmVJRCwgeGxhYiA9IGJxdW90ZSh+TG9nWzJdfiAnZm9sZCBjaGFuZ2UnKSwgCiAgICAgICAgICAgICAgICB4ID0gJ2xvZ0ZDJywgeSA9ICdQLlZhbHVlJywgdGl0bGUgPSAnVm9sY2FubyBQbG90Jywgc3VidGl0bGUgPSAnS0QgdnMgV1QnLCAKICAgICAgICAgICAgICAgICMgcEN1dG9mZiA9IDEwZS0xNiwgCiAgICAgICAgICAgICAgICBGQ2N1dG9mZiA9IDEsIGxhYlNpemUgPSA4LCBsZWdlbmRQb3NpdGlvbiA9ICJyaWdodCIpCmRldi5vZmYoKQoKI1NhdmUgdGhlIGxpc3Qgb2YgdG9wIGRpZmZlcmVudGlhbCBwQSBzaXRlcyBhdCBGRFIgMTAlCmFkZC54bHN4LnNoZWV0KGZpbGUgPSAiM1BTZXFfZGlmZlNwbGljZV9zaWduaWZpY2FudF9nZW5lcy54bHN4IiwgIktEIHZzIFdUIChwQSBzaXRlLWxldmVsKSIsIHNpZzIpCgojIFNhdmUgdGhlIGxpc3Qgb2YgZ2VuZXMgd2l0aCB0b3AgZGlmZmVyZW50aWFsIEFQQSBhdCBGRFIgMTAlCmFkZC54bHN4LnNoZWV0KGZpbGUgPSAiM1BTZXFfZGlmZlNwbGljZV9zaWduaWZpY2FudF9nZW5lcy54bHN4IiwgIktEIHZzIFdUIChnZW5lLWxldmVsKSIsIHNpZzIuZ2VuZXMpCmBgYAoKIyMjIDQuMi44IFZpc3VhbGl6aW5nIHRoZSByZXN1bHQgb2YgdGhlIGNvbnRyYXN0IHBhaXIgIkN0cmwgLSBXVCIKQ2hlY2sgdGhlIGluZm9ybWF0aW9uIG9mIHRoZSB0b3AgMzAwIGRpZmZlcm50aWFsIHBBIHNpdGVzLgpgYGB7ciBtZXNzYWdlPVRSVUUsIHBhZ2VkLnByaW50PVRSVUV9CnNpZzMgPC10b3BTcGxpY2UoZXgsIG4gPSBJbmYsIEZEUiA9IDAuMSwgY29lZiA9IDMsIHRlc3Q9ICJ0Iiwgc29ydC5ieSA9ICJsb2dGQyIpCkRUOjpkYXRhdGFibGUoc2lnM1sxOjMwMCwgXSkKYGBgCgpDaGVjayB0aGUgaW5mb3JtYXRpb24gb2YgMzAwIGdlbmVzIHdpdGggdGhlIHRvcCBkaWZmZXJudGlhbCBBUEEuCmBgYHtyIG1lc3NhZ2U9VFJVRSwgcGFnZWQucHJpbnQ9VFJVRX0Kc2lnMy5nZW5lcyA8LXRvcFNwbGljZShleCwgbiA9IEluZiwgRkRSID0gMC4xLCBjb2VmID0gMywgdGVzdD0gInNpbWVzIikgIyBTYXZlIHRoZSByZXN1bHRzIHdpdGggRkRSIDwgMC4xCkRUOjpkYXRhdGFibGUoc2lnMy5nZW5lc1sxOjMwMCwgXSkKYGBgCgpWaXN1YWxpemUgdGhlIGRpZmZlcmVudGlhbCBBUEEgcmVzdWx0IHVzaW5nIHRoZSBgcGxvdFNwbGljZWAgZnVuY3Rpb24gYW5kIHZvbGNhbm8gcGxvdHMuCmBgYHtyLCBmaWcuYWxpZ249ImNlbnRlciJ9CnBkZihmaWxlID0gIjNQU2VxX2RpZmZTcGxpY2VfcGxvdFNwbGljZV9DdHJsX1dULnBkZiIpCnBsb3RTcGxpY2UoZXgsIGNvZWYgPSAzLCBnZW5laWQgPSAiTWJkMiIsIEZEUiA9IDAuMSkKcGxvdFNwbGljZShleCwgY29lZiA9IDMsIGdlbmVpZCA9ICJJZnQ4MSIsIEZEUiA9IDAuMSkKcGxvdFNwbGljZShleCwgY29lZiA9IDMsIGdlbmVpZCA9ICJScHMyNCIsIEZEUiA9IDAuMSkKZGV2Lm9mZigpCgpwZGYoZmlsZSA9ICIzUFNlcV9kaWZmU3BsaWNlX1ZvbGNhbm9fQ3RybF9XVC5wZGYiLCBoZWlnaHQgPSAxMCwgd2lkdGggPSAxMikKRW5oYW5jZWRWb2xjYW5vKHNpZzMsIGxhYiA9IHNpZzMkR2VuZUlELCB4bGFiID0gYnF1b3RlKH5Mb2dbMl1+ICdmb2xkIGNoYW5nZScpLCAKICAgICAgICAgICAgICAgIHggPSAnbG9nRkMnLCB5ID0gJ1AuVmFsdWUnLCB0aXRsZSA9ICdWb2xjYW5vIFBsb3QnLCBzdWJ0aXRsZSA9ICdDdHJsIHZzIFdUJywgCiAgICAgICAgICAgICAgICAjIHBDdXRvZmYgPSAxMGUtMTYsIAogICAgICAgICAgICAgICAgRkNjdXRvZmYgPSAxLCBsYWJTaXplID0gOCwgbGVnZW5kUG9zaXRpb24gPSAicmlnaHQiKQpkZXYub2ZmKCkKCiNTYXZlIHRoZSBsaXN0IG9mIHRvcCBkaWZmZXJlbnRpYWwgcEEgc2l0ZXMgYXQgRkRSIDEwJQphZGQueGxzeC5zaGVldChmaWxlID0gIjNQU2VxX2RpZmZTcGxpY2Vfc2lnbmlmaWNhbnRfZ2VuZXMueGxzeCIsICJDdHJsIHZzIFdUIChwQSBzaXRlLWxldmVsKSIsIHNpZzMpCgojIFNhdmUgdGhlIGxpc3Qgb2YgZ2VuZXMgd2l0aCB0b3AgZGlmZmVyZW50aWFsIEFQQSBhdCBGRFIgMTAlCmFkZC54bHN4LnNoZWV0KGZpbGUgPSAiM1BTZXFfZGlmZlNwbGljZV9zaWduaWZpY2FudF9nZW5lcy54bHN4IiwgIkN0cmwgdnMgV1QgKGdlbmUtbGV2ZWwpIiwgc2lnMy5nZW5lcykKYGBgCgojIyBQcmludCBTZXNzaW9uIEluZm8tCmBgYHtyfQogIHByaW50KHNlc3Npb25JbmZvKCkpCmBgYAoKCg==