1 Introduction

This document seeks to lay out the strategy employed for an examination of the sequencing results from a group of plasmid libraries from Pseudmonas aeruginosa PA01.

These samples are in 5 main libraries, each of which was done twice, once with a topoisomerase during isolation and once without.

The immediate goal of this experiment is to learn what ORFs exist in the library and what mutations have appeared in them.

2 Preprocessing

The preprocessing is mostly a normal DNA sequencing pipeline:

  1. Trim the reads.
  2. Map them against the plasmid, pDONR201.
  3. Map the unmapped reads from #2 against PA01.
  4. Count the reads by locus_tag to identify missing genes.
  5. Use freebayes to identify variant positions.
  6. Parse the freebayes output down to an easy to interpret format.

3 A suggestion from Nour

Nour made a good suggestion: gatk/picard provides a tool to mark/deduplicate sequence reads given a set of mapped reads. I applied that to this data set and noted some interesting differences. This is implemented as a method called ‘MarkDuplicates’ or some such, I will include a script stanza below showing its usage.

3.1 Trimming

This is my default trimomatic invocation.

cd preprocessing
start=$(pwd)
for i in $(/bin/ls); do
    cd ${i}
    cyoa --task trim --method trim --input $(/bin/ls *.gz | tr '\n' ':' | sed 's/:$//g')
    cd $start
done

Here is the script which resulted for the poola sample:

I still have an old check in it from an earlier version of trimomatic which would sometimes fail.

3.1.1 Individual trimming script

#SBATCH --export=ALL
#SBATCH --mail-type=NONE
#SBATCH --open-mode=append
#SBATCH --chdir=/fs/cbcb-lab/nelsayed/scratch/atb/dnaseq/paeruginosa_plasmids_2022/preprocessing/poola
#SBATCH --partition=dpart
#SBATCH --qos=workstation --nice=10
#SBATCH --nodes=1 --requeue
#SBATCH --time=24:00:00
#SBATCH --job-name=01trim_ORFeome_Pool_A_S1_R1_001
#SBATCH --mem=24G
#SBATCH --cpus-per-task=3
#SBATCH --output=outputs/log.txt.sbatch
set -o errexit
set -o errtrace
set -o pipefail
export LESS='--buffers 0'
script="$(pwd)/$0"
err() {
    echo "Error occurred:"
    awk 'NR>L-4 && NR<L+4 { printf "%-5d%3s%s\n",NR,(NR==L?">>>":""),$script }' L=$1 $script
}
trap 'err $LINENO' ERR

echo "## Started /fs/cbcb-lab/nelsayed/scratch/atb/dnaseq/paeruginosa_plasmids_2022/preprocessing/poola/scripts/01trim_ORFeome_Pool_A_S1_R1_001.sh at $(date) on $(hostname) with id ${SLURM_JOBID}." >> outputs/log.txt

module add trimomatic

## This call to trimomatic removes illumina and epicentre adapters from ORFeome_Pool_A_S1_R1_001.fastq.gz:ORFeome_Pool_A_S1_R2_001.fastq.gz.
## It also performs a sliding window removal of anything with quality <25;
## cutadapt provides an alternative to this tool.
## The original sequence data is recompressed and saved in the sequences/ directory.
mkdir -p outputs/01trimomatic
## Note that trimomatic prints all output and errors to STDERR, so send both to output
trimmomatic PE \
  -threads 1 \
  -phred33 \
  <(less r1.fastq.gz) <(less r2.fastq.gz) \
  ORFeome_Pool_A_S1_R1_001-trimmed_paired.fastq ORFeome_Pool_A_S1_R1_001-trimmed_unpaired.fastq \
  ORFeome_Pool_A_S1_R2_001-trimmed_paired.fastq ORFeome_Pool_A_S1_R2_001-trimmed_unpaired.fastq \
   ILLUMINACLIP:/cbcb/sw/RedHat-7-x86_64/common/local/perl/5.34/lib/site_perl/5.34.0/auto/share/dist/Bio-Adventure/genome/adapters.fa:2:20:10:2:keepBothReads \
  SLIDINGWINDOW:4:20 MINLEN:40 \
  1>outputs/01trimomatic/ORFeome_Pool_A_S1_R1_001-trimomatic.stdout \
  2>outputs/01trimomatic/ORFeome_Pool_A_S1_R1_001-trimomatic.stderr
excepted=$( { grep "Exception" "outputs/01trimomatic/ORFeome_Pool_A_S1_R1_001-trimomatic.stdout" || test $? = 1; } )
## The following is in case the illumina clipping fails, which it does if this has already been run I think.
if [[ "${excepted}" != "" ]]; then
  trimmomatic PE \
    -threads 1 \
    -phred33 \
    <(less r1.fastq.gz) <(less r2.fastq.gz) \
    ORFeome_Pool_A_S1_R1_001-trimmed_paired.fastq ORFeome_Pool_A_S1_R1_001-trimmed_unpaired.fastq \
    ORFeome_Pool_A_S1_R2_001-trimmed_paired.fastq ORFeome_Pool_A_S1_R2_001-trimmed_unpaired.fastq \
     SLIDINGWINDOW:4:25 MINLEN:50\
    1>outputs/01trimomatic/ORFeome_Pool_A_S1_R1_001-trimomatic.stdout \
    2>outputs/01trimomatic/ORFeome_Pool_A_S1_R1_001-trimomatic.stderr
fi
sleep 10
mv ORFeome_Pool_A_S1_R1_001-trimmed_paired.fastq ORFeome_Pool_A_S1_R1_001-trimmed.fastq
mv ORFeome_Pool_A_S1_R2_001-trimmed_paired.fastq ORFeome_Pool_A_S1_R2_001-trimmed.fastq

## Recompress the unpaired reads, this should not take long.
xz -9e -f ORFeome_Pool_A_S1_R1_001-trimmed_unpaired.fastq
xz -9e -f ORFeome_Pool_A_S1_R2_001-trimmed_unpaired.fastq
## Recompress the paired reads.
xz -9e -f ORFeome_Pool_A_S1_R1_001-trimmed.fastq
xz -9e -f ORFeome_Pool_A_S1_R2_001-trimmed.fastq
ln -sf ORFeome_Pool_A_S1_R1_001-trimmed.fastq.xz r1_trimmed.fastq.xz
ln -sf ORFeome_Pool_A_S1_R2_001-trimmed.fastq.xz r2_trimmed.fastq.xz


## The following lines give status codes and some logging
echo "## Job status: $? " >> outputs/log.txt
echo "## $(hostname) Finished ${SLURM_JOBID} 01trim_ORFeome_Pool_A_S1_R1_001.sh at $(date), it took $(( SECONDS / 60 )) minutes." >> outputs/log.txt

if [[ -x "$(command -v sstat)" && ! -z "${SLURM_JOBID}" ]]; then
  walltime=$(scontrol show job "${SLURM_JOBID}" | grep RunTime | perl -F'/\s+|=/' -lane '{print $F[2]}' 2>/dev/null)
  echo "#### walltime used by ${SLURM_JOBID} was: ${walltime:-null}" >> outputs/log.txt
  maxmem=$(sstat --format=MaxVMSize -n "${SLURM_JOBID}.batch" 2>/dev/null)
  echo "#### maximum memory used by ${SLURM_JOBID} was: ${maxmem:-null}" >> outputs/log.txt
  avecpu=$(sstat --format=AveCPU -n "${SLURM_JOBID}.batch" 2>/dev/null)
  echo "#### average cpu used by ${SLURM_JOBID} was: ${avecpu:-null}" >> outputs/log.txt
fi
## Adding a little logic to have skip finished jobs.
touch outputs/logs/01trim_ORFeome_Pool_A_S1_R1_001.finished

3.2 Mapping against pDONR

This was followed by my default mapping against the plasmid via hisat2.

I previously downloaded a copy of pDONR201 and ran the following on it:

cyoa --task index --method indexhisat --input pDONR201.fasta --species pDONR201

I did not generate a gff file for this, so htseq is not run.

The following invocation queues 2 jobs on the cluster, 1 for the mapping and 1 to sort/compress/index the alignment.

cd preprocessing
start=$(pwd)
for i in $(/bin/ls); do
    cd ${i}
    cyoa --task map --method hisat --species pDONR201 --input r1_trimmed.fastq.xz:r2_trimmed.fastq.xz
    cd $start
done

3.2.1 Individual pDONR mapping script

#!/usr/bin/env bash
#SBATCH --export=ALL
#SBATCH --mail-type=NONE
#SBATCH --open-mode=append
#SBATCH --chdir=/fs/cbcb-lab/nelsayed/scratch/atb/dnaseq/paeruginosa_plasmids_2022/preprocessing/poola
#SBATCH --partition=dpart
#SBATCH --qos=workstation --nice=10
#SBATCH --nodes=1 --requeue
#SBATCH --time=10:00:00
#SBATCH --job-name=40hisat2_pDONR201_genome
#SBATCH --mem=24G
#SBATCH --cpus-per-task=4
#SBATCH --output=outputs/log.txt.sbatch
set -o errexit
set -o errtrace
set -o pipefail
export LESS='--buffers 0'
script="$(pwd)/$0"
err() {
    echo "Error occurred:"
    awk 'NR>L-4 && NR<L+4 { printf "%-5d%3s%s\n",NR,(NR==L?">>>":""),$script }' L=$1 $script
}
trap 'err $LINENO' ERR

echo "## Started /fs/cbcb-lab/nelsayed/scratch/atb/dnaseq/paeruginosa_plasmids_2022/preprocessing/poola/scripts/40hisat2_pDONR201_genome.sh at $(date) on $(hostname) with id ${SLURM_JOBID}." >> outputs/log.txt

module add hisat2 samtools htseq bamtools

## This is a hisat2 alignment of  -1 <(less /fs/cbcb-lab/nelsayed/scratch/atb/dnaseq/paeruginosa_plasmids_2022/preprocessing/poola/r1_trimmed.fastq.xz) -2 <(less /fs/cbcb-lab/nelsayed/scratch/atb/dnaseq/paeruginosa_plasmids_2022/preprocessing/poola/r2_trimmed.fastq.xz)  against /cbcbhomes/abelew/libraries/genome/indexes/pDONR201

mkdir -p outputs/40hisat2_pDONR201
hisat2 -x /cbcbhomes/abelew/libraries/genome/indexes/pDONR201  \
  -p 4 \
  -q   -1 <(less /fs/cbcb-lab/nelsayed/scratch/atb/dnaseq/paeruginosa_plasmids_2022/preprocessing/poola/r1_trimmed.fastq.xz) -2 <(less /fs/cbcb-lab/nelsayed/scratch/atb/dnaseq/paeruginosa_plasmids_2022/preprocessing/poola/r2_trimmed.fastq.xz)  \
  --phred33 \
  --un outputs/40hisat2_pDONR201/poola_unaldis_pDONR201_genome.fastq \
  --al outputs/40hisat2_pDONR201/poola_aldis_pDONR201_genome.fastq \
  --un-conc outputs/40hisat2_pDONR201/poola_unalcon_pDONR201_genome.fastq \
  --al-conc outputs/40hisat2_pDONR201/poola_alcon_pDONR201_genome.fastq \
  -S outputs/40hisat2_pDONR201/poola_pDONR201_genome.sam \
  2>outputs/40hisat2_pDONR201/hisat2_pDONR201_genome_poola.stderr \
  1>outputs/40hisat2_pDONR201/hisat2_pDONR201_genome_poola.stdout


## The following lines give status codes and some logging
echo "## Job status: $? " >> outputs/log.txt
echo "## $(hostname) Finished ${SLURM_JOBID} 40hisat2_pDONR201_genome.sh at $(date), it took $(( SECONDS / 60 )) minutes." >> outputs/log.txt

if [[ -x "$(command -v sstat)" && ! -z "${SLURM_JOBID}" ]]; then
  walltime=$(scontrol show job "${SLURM_JOBID}" | grep RunTime | perl -F'/\s+|=/' -lane '{print $F[2]}' 2>/dev/null)
  echo "#### walltime used by ${SLURM_JOBID} was: ${walltime:-null}" >> outputs/log.txt
  maxmem=$(sstat --format=MaxVMSize -n "${SLURM_JOBID}.batch" 2>/dev/null)
  echo "#### maximum memory used by ${SLURM_JOBID} was: ${maxmem:-null}" >> outputs/log.txt
  avecpu=$(sstat --format=AveCPU -n "${SLURM_JOBID}.batch" 2>/dev/null)
  echo "#### average cpu used by ${SLURM_JOBID} was: ${avecpu:-null}" >> outputs/log.txt
fi
## Adding a little logic to have skip finished jobs.
touch outputs/logs/40hisat2_pDONR201_genome.finished

The cyoa invocation which generated the above script also creates the following script along with a group of compression jobs which aggressively compress the fastq output files. I leave that as an exercise to the reader.

#!/usr/bin/env bash
#SBATCH --export=ALL
#SBATCH --mail-type=NONE
#SBATCH --open-mode=append
#SBATCH --chdir=/fs/cbcb-lab/nelsayed/scratch/atb/dnaseq/paeruginosa_plasmids_2022/preprocessing/poola
#SBATCH --partition=dpart
#SBATCH --qos=throughput --nice=10
#SBATCH --nodes=1 --requeue
#SBATCH --time=12:00:00
#SBATCH --job-name=40_2s2b_hisat2_pDONR201
#SBATCH --mem=12G
#SBATCH --cpus-per-task=1
#SBATCH --output=outputs/log.txt.sbatch
set -o errexit
set -o errtrace
set -o pipefail
export LESS='--buffers 0'
script="$(pwd)/$0"
err() {
    echo "Error occurred:"
    awk 'NR>L-4 && NR<L+4 { printf "%-5d%3s%s\n",NR,(NR==L?">>>":""),$script }' L=$1 $script
}
trap 'err $LINENO' ERR

echo "## Started /fs/cbcb-lab/nelsayed/scratch/atb/dnaseq/paeruginosa_plasmids_2022/preprocessing/poola/scripts/40_2s2b_hisat2_pDONR201.sh at $(date) on $(hostname) with id ${SLURM_JOBID}." >> outputs/log.txt

module add samtools bamtools

## Converting the text sam to a compressed, sorted, indexed bamfile.
## Also printing alignment statistics to outputs/40hisat2_pDONR201/poola_pDONR201_genome.bam.stats
## This job depended on: 240110

echo "Starting samtools"
if [[ ! -f "outputs/40hisat2_pDONR201/poola_pDONR201_genome.sam" ]]; then
    echo "Could not find the samtools input file."
    exit 1
fi
samtools view -u -t /cbcbhomes/abelew/libraries/genome/pDONR201.fasta \
  -S outputs/40hisat2_pDONR201/poola_pDONR201_genome.sam -o outputs/40hisat2_pDONR201/poola_pDONR201_genome.bam  \
  2>outputs/40hisat2_pDONR201/poola_pDONR201_genome.bam.err 1>outputs/40hisat2_pDONR201/poola_pDONR201_genome.bam.out && \
echo 0
  samtools sort -l 9 outputs/40hisat2_pDONR201/poola_pDONR201_genome.bam -o outputs/40hisat2_pDONR201/poola_pDONR201_genome-sorted.bam \
  2>outputs/40hisat2_pDONR201/poola_pDONR201_genome-sorted.stderr \
  1>outputs/40hisat2_pDONR201/poola_pDONR201_genome-sorted.stdout && \
  rm outputs/40hisat2_pDONR201/poola_pDONR201_genome.bam && \
  rm outputs/40hisat2_pDONR201/poola_pDONR201_genome.sam && \
  mv outputs/40hisat2_pDONR201/poola_pDONR201_genome-sorted.bam outputs/40hisat2_pDONR201/poola_pDONR201_genome.bam &&  samtools index outputs/40hisat2_pDONR201/poola_pDONR201_genome.bam
echo 0
bamtools stats -in outputs/40hisat2_pDONR201/poola_pDONR201_genome.bam 2>outputs/40hisat2_pDONR201/poola_pDONR201_genome.bam.stats 1>&2
echo 0

## The following will fail if this is single-ended.
samtools view -b -f 2 -o outputs/40hisat2_pDONR201/poola_pDONR201_genome-paired.bam outputs/40hisat2_pDONR201/poola_pDONR201_genome.bam && samtools index outputs/40hisat2_pDONR201/poola_pDONR201_genome-paired.bam
bamtools stats -in outputs/40hisat2_pDONR201/poola_pDONR201_genome-paired.bam 2>outputs/40hisat2_pDONR201/poola_pDONR201_genome-paired.stats 1>&2
bamtools filter -tag XM:0 -in outputs/40hisat2_pDONR201/poola_pDONR201_genome.bam -out outputs/40hisat2_pDONR201/poola_pDONR201_genome-sorted_nomismatch.bam &&
  samtools index outputs/40hisat2_pDONR201/poola_pDONR201_genome-sorted_nomismatch.bam

## The following lines give status codes and some logging
echo "## Job status: $? " >> outputs/log.txt
echo "## $(hostname) Finished ${SLURM_JOBID} 40_2s2b_hisat2_pDONR201.sh at $(date), it took $(( SECONDS / 60 )) minutes." >> outputs/log.txt

if [[ -x "$(command -v sstat)" && ! -z "${SLURM_JOBID}" ]]; then
  walltime=$(scontrol show job "${SLURM_JOBID}" | grep RunTime | perl -F'/\s+|=/' -lane '{print $F[2]}' 2>/dev/null)
  echo "#### walltime used by ${SLURM_JOBID} was: ${walltime:-null}" >> outputs/log.txt
  maxmem=$(sstat --format=MaxVMSize -n "${SLURM_JOBID}.batch" 2>/dev/null)
  echo "#### maximum memory used by ${SLURM_JOBID} was: ${maxmem:-null}" >> outputs/log.txt
  avecpu=$(sstat --format=AveCPU -n "${SLURM_JOBID}.batch" 2>/dev/null)
  echo "#### average cpu used by ${SLURM_JOBID} was: ${avecpu:-null}" >> outputs/log.txt
fi
## Adding a little logic to have skip finished jobs.
touch outputs/logs/40_2s2b_hisat2_pDONR201.finished

3.3 Mapping against PA01

Before mapping against the PA01 strain, I previously downloaded the reference assembly from NCBI as a complete genbank file and put it in my “${LIBDIR}/genome/” directory as ‘paeruginosa_pa01.gb’.

I then performed the following to convert it to fasta/gff and index it (note I did this quite a while ago, I am just writing it here for completeness), also here is an extra salmon index operation for fun:

start=$(pwd)
cd ~/libraries/genome
cyoa --task convert --method gb2gff --input paeruginosa_pa01.gb
cyoa --task index --method indexhisat --input paeruginosa_pa01.fasta --species paeruginosa_pa01
cyoa --task index --method indexsalmon --input paeruginosa_pa01_cds.fasta --species paeruginosa_pa01
cd $start

This is nearly identical to the previous step, except it uses the unaligned reads from the previous step, and I explicitly state that I want htseq to count the genes by the locus_tag tags.

cd preprocessing
start=$(pwd)
for i in $(/bin/ls); do
    cd ${i}
    cyoa --task map --method hisat --species paeruginosa_pa01 \
         --htseq_type gene --htseq_id locus_tag \
         --input $(/bin/ls outputs/40hisat2_pDONR201/*unalcon*.fastq.xz | tr '\n' ':' | sed 's/:$//g')
    cd $start
done

The resulting scripts are nearly identical to those for pDONR, except of course the species used and inputs:

#!/usr/bin/env bash
#SBATCH --export=ALL
#SBATCH --mail-type=NONE
#SBATCH --open-mode=append
#SBATCH --chdir=/fs/cbcb-lab/nelsayed/scratch/atb/dnaseq/paeruginosa_plasmids_2022/preprocessing/poola
#SBATCH --partition=dpart
#SBATCH --qos=workstation --nice=10
#SBATCH --nodes=1 --requeue
#SBATCH --time=10:00:00
#SBATCH --job-name=40hisat2_paeruginosa_pa01_genome
#SBATCH --mem=24G
#SBATCH --cpus-per-task=4
#SBATCH --output=outputs/log.txt.sbatch
set -o errexit
set -o errtrace
set -o pipefail
export LESS='--buffers 0'
script="$(pwd)/$0"
err() {
    echo "Error occurred:"
    awk 'NR>L-4 && NR<L+4 { printf "%-5d%3s%s\n",NR,(NR==L?">>>":""),$script }' L=$1 $script
}
trap 'err $LINENO' ERR

echo "## Started /fs/cbcb-lab/nelsayed/scratch/atb/dnaseq/paeruginosa_plasmids_2022/preprocessing/poola/scripts/40hisat2_paeruginosa_pa01_genome.sh at $(date) on $(hostname) with id ${SLURM_JOBID}." >> outputs/log.txt

module add hisat2 samtools htseq bamtools

## This is a hisat2 alignment of  -1 <(less /fs/cbcb-lab/nelsayed/scratch/atb/dnaseq/paeruginosa_plasmids_2022/preprocessing/poola/outputs/40hisat2_pDONR201/poola_unalcon_pDONR201_genome.1.fastq.xz) -2 <(less /fs/cbcb-lab/nelsayed/scratch/atb/dnaseq/paeruginosa_plasmids_2022/preprocessing/poola/outputs/40hisat2_pDONR201/poola_unalcon_pDONR201_genome.2.fastq.xz)  against /cbcbhomes/abelew/libraries/genome/indexes/paeruginosa_pa01

mkdir -p outputs/40hisat2_paeruginosa_pa01
hisat2 -x /cbcbhomes/abelew/libraries/genome/indexes/paeruginosa_pa01  \
  -p 4 \
  -q   -1 <(less /fs/cbcb-lab/nelsayed/scratch/atb/dnaseq/paeruginosa_plasmids_2022/preprocessing/poola/outputs/40hisat2_pDONR201/poola_unalcon_pDONR201_genome.1.fastq.xz) -2 <(less /fs/cbcb-lab/nelsayed/scratch/atb/dnaseq/paeruginosa_plasmids_2022/preprocessing/poola/outputs/40hisat2_pDONR201/poola_unalcon_pDONR201_genome.2.fastq.xz)  \
  --phred33 \
  --un outputs/40hisat2_paeruginosa_pa01/poola_unaldis_paeruginosa_pa01_genome.fastq \
  --al outputs/40hisat2_paeruginosa_pa01/poola_aldis_paeruginosa_pa01_genome.fastq \
  --un-conc outputs/40hisat2_paeruginosa_pa01/poola_unalcon_paeruginosa_pa01_genome.fastq \
  --al-conc outputs/40hisat2_paeruginosa_pa01/poola_alcon_paeruginosa_pa01_genome.fastq \
  -S outputs/40hisat2_paeruginosa_pa01/poola_paeruginosa_pa01_genome.sam \
  2>outputs/40hisat2_paeruginosa_pa01/hisat2_paeruginosa_pa01_genome_poola.stderr \
  1>outputs/40hisat2_paeruginosa_pa01/hisat2_paeruginosa_pa01_genome_poola.stdout


## The following lines give status codes and some logging
echo "## Job status: $? " >> outputs/log.txt
echo "## $(hostname) Finished ${SLURM_JOBID} 40hisat2_paeruginosa_pa01_genome.sh at $(date), it took $(( SECONDS / 60 )) minutes." >> outputs/log.txt

if [[ -x "$(command -v sstat)" && ! -z "${SLURM_JOBID}" ]]; then
  walltime=$(scontrol show job "${SLURM_JOBID}" | grep RunTime | perl -F'/\s+|=/' -lane '{print $F[2]}' 2>/dev/null)
  echo "#### walltime used by ${SLURM_JOBID} was: ${walltime:-null}" >> outputs/log.txt
  maxmem=$(sstat --format=MaxVMSize -n "${SLURM_JOBID}.batch" 2>/dev/null)
  echo "#### maximum memory used by ${SLURM_JOBID} was: ${maxmem:-null}" >> outputs/log.txt
  avecpu=$(sstat --format=AveCPU -n "${SLURM_JOBID}.batch" 2>/dev/null)
  echo "#### average cpu used by ${SLURM_JOBID} was: ${avecpu:-null}" >> outputs/log.txt
fi
## Adding a little logic to have skip finished jobs.
touch outputs/logs/40hisat2_paeruginosa_pa01_genome.finished

I will leave the nearly identical conversion of the sam alignments to compressed/sorted/indexed bam as an exercise to the reader, but here is the resulting htseq script:

#!/usr/bin/env bash
#SBATCH --export=ALL
#SBATCH --mail-type=NONE
#SBATCH --open-mode=append
#SBATCH --chdir=/fs/cbcb-lab/nelsayed/scratch/atb/dnaseq/paeruginosa_plasmids_2022/preprocessing/poola
#SBATCH --partition=dpart
#SBATCH --qos=throughput --nice=10
#SBATCH --nodes=1 --requeue
#SBATCH --time=12:00:00
#SBATCH --job-name=40_3hts_poola_all_hisat2_paeruginosa_pa01_sno_gene_Alias
#SBATCH --mem=6G
#SBATCH --cpus-per-task=1
#SBATCH --output=outputs/log.txt.sbatch
set -o errexit
set -o errtrace
set -o pipefail
export LESS='--buffers 0'
script="$(pwd)/$0"
err() {
    echo "Error occurred:"
    awk 'NR>L-4 && NR<L+4 { printf "%-5d%3s%s\n",NR,(NR==L?">>>":""),$script }' L=$1 $script
}
trap 'err $LINENO' ERR

echo "## Started /fs/cbcb-lab/nelsayed/scratch/atb/dnaseq/paeruginosa_plasmids_2022/preprocessing/poola/scripts/40_3hts_poola_all_hisat2_paeruginosa_pa01_sno_gene_Alias.sh at $(date) on $(hostname) with id ${SLURM_JOBID}." >> outputs/log.txt

module add htseq

## Counting the number of hits in outputs/40hisat2_paeruginosa_pa01/poola_paeruginosa_pa01_genome-paired.bam for each feature found in /cbcbhomes/abelew/libraries/genome/paeruginosa_pa01.gff
htseq-count  --help 2>&1 | tail -n 3
htseq-count \
  -q -f bam -s no  --type gene  --idattr locus_tag \
  outputs/40hisat2_paeruginosa_pa01/poola_paeruginosa_pa01_genome-paired.bam \
  /cbcbhomes/abelew/libraries_fs/genome/paeruginosa_pa01.gff \
  2>outputs/40hisat2_paeruginosa_pa01/poola_paeruginosa_pa01_genome-paired_all_sno_gene_Alias.stderr \
  1>outputs/40hisat2_paeruginosa_pa01/poola_paeruginosa_pa01_genome-paired_all_sno_gene_Alias.count && \
    xz -f -9e outputs/40hisat2_paeruginosa_pa01/poola_paeruginosa_pa01_genome-paired_all_sno_gene_Alias.count 2>outputs/40hisat2_paeruginosa_pa01/poola_paeruginosa_pa01_genome-paired_all_sno_gene_Alias.stderr.xz 1>outputs/40hisat2_paeruginosa_pa01/poola_paeruginosa_pa01_genome-paired_all_sno_gene_Alias.count.xz


## The following lines give status codes and some logging
echo "## Job status: $? " >> outputs/log.txt
echo "## $(hostname) Finished ${SLURM_JOBID} 40_3hts_poola_all_hisat2_paeruginosa_pa01_sno_gene_Alias.sh at $(date), it took $(( SECONDS / 60 )) minutes." >> outputs/log.txt

if [[ -x "$(command -v sstat)" && ! -z "${SLURM_JOBID}" ]]; then
  walltime=$(scontrol show job "${SLURM_JOBID}" | grep RunTime | perl -F'/\s+|=/' -lane '{print $F[2]}' 2>/dev/null)
  echo "#### walltime used by ${SLURM_JOBID} was: ${walltime:-null}" >> outputs/log.txt
  maxmem=$(sstat --format=MaxVMSize -n "${SLURM_JOBID}.batch" 2>/dev/null)
  echo "#### maximum memory used by ${SLURM_JOBID} was: ${maxmem:-null}" >> outputs/log.txt
  avecpu=$(sstat --format=AveCPU -n "${SLURM_JOBID}.batch" 2>/dev/null)
  echo "#### average cpu used by ${SLURM_JOBID} was: ${avecpu:-null}" >> outputs/log.txt
fi
## Adding a little logic to have skip finished jobs.
touch outputs/logs/40_3hts_poola_all_hisat2_paeruginosa_pa01_sno_gene_locus_tag.finished

4 Deduplication

Nour’s suggestion resulted in the following addition (which has since been added as a precursor to my freebayes/mpileup invocation):

mkdir -p ${freebayes_dir}
gatk MarkDuplicates \\
  -I $options->{input} \\
  -O ${deduplicated} \\
  -M ${marked} --REMOVE_DUPLICATES true --COMPRESSION_LEVEL 9 \\
  2>${gatk_stdout} \\
  1>${gatk_stderr}
samtools index ${deduplicated}

5 Variant searching

I have two methods of variant searching in my repetoire, mpileup and freebayes. I only learned freebayes recently and so chose it. My current implementation also spins off a series of scripts to parse the freebayes output down into a simpler tsv format. Also, unlike the mapping script above, the freebayes invocation itself includes the conversion of the text vcf format to a sorted/compressed/index bcf format.

Note also that the previous step also creates 3 bam files:

  1. \({sample}_\){species}_genome.bam: All alignments
  2. \({sample}_\){species}_genome-paired.bam: All properly paired alignments, this is the default used by htseq when available.
  3. \({sample}_\){species}_genome-sorted_nomismatch.bam: This is filtered for the set of alignments with no mismatches.

Since running these samples, I added another suffix to the hisat output directories, depending on whether the -k parameter is used to limit the number of alignments reported. That is not particularly relevant here, but worth noting.

cd preprocessing
start=$(pwd)
for i in $(/bin/ls); do
    cd ${i}
    cyoa --task snp --method freebayes --species paeruginosa_pa01 \
         --input $(/bin/ls outputs/40hisat2_paeruginosa_pa01/*_genome.bam)
    cd $start
done

5.0.1 Single freebayes invocation

#!/usr/bin/env bash
#SBATCH --export=ALL
#SBATCH --mail-type=NONE
#SBATCH --open-mode=append
#SBATCH --chdir=/mnt/cbcb/fs01_abelew/cbcb-lab/nelsayed/scratch/atb/dnaseq/paeruginosa_plasmids_2022/preprocessing/poola
#SBATCH --partition=dpart
#SBATCH --qos=workstation --nice=10
#SBATCH --nodes=1 --requeue
#SBATCH --time=10:00:00
#SBATCH --job-name=40freebayes_paeruginosa_pa01
#SBATCH --mem=48G
#SBATCH --cpus-per-task=4
#SBATCH --output=outputs/log.txt.sbatch
set -o errexit
set -o errtrace
set -o pipefail
export LESS='--buffers 0'
script="$(pwd)/$0"
err() {
    echo "Error occurred:"
    awk 'NR>L-4 && NR<L+4 { printf "%-5d%3s%s\n",NR,(NR==L?">>>":""),$script }' L=$1 $script
}
trap 'err $LINENO' ERR

echo "## Started /mnt/cbcb/fs01_abelew/cbcb-lab/nelsayed/scratch/atb/dnaseq/paeruginosa_plasmids_2022/preprocessing/poola/scripts/40freebayes_paeruginosa_pa01.sh at $(date) on $(hostname) with id ${SLURM_JOBID}." >> outputs/log.txt

module add freebayes libgsl/2.7.1 libhts/1.13 samtools/1.13 bcftools vcftools

## Use freebayes, bcftools, and vcfutils to get some idea about how many variant positions are in the data.

mkdir -p outputs/40freebayes_paeruginosa_pa01
freebayes -f /home/trey/libraries_fs/genome/paeruginosa_pa01.fasta \
  -v outputs/40freebayes_paeruginosa_pa01/paeruginosa_pa01.vcf \
  outputs/40hisat2_paeruginosa_pa01/poola_paeruginosa_pa01_genome.bam \
  1>outputs/40freebayes_paeruginosa_pa01/paeruginosa_pa01.stdout \
  2>outputs/40freebayes_paeruginosa_pa01/paeruginosa_pa01.stderr
bcftools convert outputs/40freebayes_paeruginosa_pa01/paeruginosa_pa01.vcf \
  -Ob -o outputs/40freebayes_paeruginosa_pa01/paeruginosa_pa01.bcf \
  2>>outputs/40freebayes_paeruginosa_pa01/paeruginosa_pa01.stderr \
  1>outputs/40freebayes_paeruginosa_pa01/paeruginosa_pa01.stdout
bcftools index outputs/40freebayes_paeruginosa_pa01/paeruginosa_pa01.bcf \
  2>>outputs/40freebayes_paeruginosa_pa01/paeruginosa_pa01.stderr \
  1>>outputs/40freebayes_paeruginosa_pa01/paeruginosa_pa01.stdout
rm outputs/40freebayes_paeruginosa_pa01/paeruginosa_pa01.vcf


## The following lines give status codes and some logging
echo "Job status: $? " >> outputs/log.txt
echo "  $(hostname) Finished ${SLURM_JOBID} 40freebayes_paeruginosa_pa01.sh at $(date), it took $(( SECONDS / 60 )) minutes." >> outputs/log.txt

if [[ -x "$(command -v sstat)" && ! -z "${SLURM_JOBID}" ]]; then
  walltime=$(scontrol show job "${SLURM_JOBID}" | grep RunTime | perl -F'/\s+|=/' -lane '{print $F[2]}' | head -n 1 2>/dev/null)
  echo "  walltime used by ${SLURM_JOBID} was: ${walltime:-null}" >> outputs/log.txt
  maxmem=$(sstat --format=MaxVMSize -n "${SLURM_JOBID}.batch" 2>/dev/null)
  echo "  maximum memory used by ${SLURM_JOBID} was: ${maxmem:-null}" >> outputs/log.txt
  echo "" >> outputs/log.txt
fi
## Adding a little logic to have skip finished jobs.
touch outputs/logs/40freebayes_paeruginosa_pa01.finished

Upon completion we have a series of files which in theory should provide everything of interest to Vince. Lets poke at them in R!

6 Vince’s Requests

Here is my email from Vince with his initial, pared down queries:

" Based on the data you have analyzed, can you please share with me a Excel spreadsheet (or CSV file) with 2 columns for each library and each PA gene:

  1. Coverage (RPKM) or 0 for no reads
  2. Number of SNPs "

6.1 Annotation

I always grab my favorite annotation sources, even if I will not really use them. I usually copy the gff and fasta for the version of the genome I used to the local directory, but I did not bother for this.

pa01_gff <- load_gff_annotations("~/libraries_fs/genome/paeruginosa_pa01.gff", id_col="locus_tag")
## Trying attempt: rtracklayer::import.gff3(gff, sequenceRegionsAsSeqinfo = TRUE)
## Trying attempt: rtracklayer::import.gff3(gff, sequenceRegionsAsSeqinfo = FALSE)
## Had a successful gff import with rtracklayer::import.gff3(gff, sequenceRegionsAsSeqinfo = FALSE)
## Returning a df with 15 columns and 5697 rows.
rownames(pa01_gff) <- pa01_gff[["locus_tag"]]

6.2 Expressionset

Let us now pull out the htseq count tables in order to make an rpkm table. This uses the metadata sample sheet I created in the sample_sheets/ directory.

I bolded the two most important columns for the purposes of these tasks.

pa01_expt <- create_expt("sample_sheets/all_samples.xlsx", gene_info=pa01_gff)
## Reading the sample metadata.
## The sample definitions comprises: 10 rows(samples) and 9 columns(metadata fields).
## Matched 5697 annotations and counts.
## Bringing together the count matrix and gene information.
## Some annotations were lost in merging, setting them to 'undefined'.
## Saving the expressionset to 'expt.rda'.
## The final expressionset has 5697 rows and 10 columns.
pa01_rpkm <- normalize_expt(pa01_expt, convert="rpkm")
pa01_cpm <- normalize_expt(pa01_expt, convert="cpm")

## Plot a log-scale density of rpkm values.
plot_boxplot(pa01_expt)
## 12590 entries are 0.  We are on a log scale, adding 1 to the data.

plot_density(normalize_expt(pa01_rpkm, transform="log2", filter=TRUE))
## Removing 255 low-count genes (5442 remaining).
## transform_counts: Found 10104 values equal to 0, adding 1 to the matrix.
## $plot

## 
## $condition_summary
##    condition min   1st median  mean   3rd    max
## 1:         a   0 6.468  7.348 6.833 7.767  9.282
## 2:         b   0 6.174  7.332 6.796 7.811  9.268
## 3:         c   0 6.566  7.413 6.871 7.773  9.105
## 4:         d   0 6.535  7.335 6.756 7.766  9.166
## 5:       pmg   0 0.000  0.000 1.420 0.000 11.833
## 
## $batch_summary
##     batch min   1st median  mean   3rd   max
## 1: notopo   0 5.083  7.111 5.740 7.747 11.58
## 2:   topo   0 5.121  7.105 5.731 7.719 11.83
## 
## $sample_summary
##         sample min   1st median  mean   3rd    max
##  1:      poola   0 6.499  7.375 6.848 7.779  9.282
##  2:      poolb   0 6.119  7.318 6.783 7.827  9.268
##  3:      poolc   0 6.572  7.422 6.876 7.786  9.087
##  4:      poold   0 6.543  7.356 6.766 7.789  9.085
##  5:    poolpmg   0 0.000  0.000 1.425 0.000 11.584
##  6: poola_topo   0 6.441  7.319 6.818 7.756  9.165
##  7: poolb_topo   0 6.233  7.345 6.809 7.798  9.149
##  8: poolc_topo   0 6.560  7.405 6.866 7.762  9.105
##  9: poold_topo   0 6.529  7.308 6.746 7.739  9.166
## 10: poolpmg_tp   0 0.000  0.000 1.414 0.000 11.833
## 
## $table
##            id     sample counts condition  batch
##     1: PA0001      poola  7.797         a notopo
##     2: PA0002      poola  7.957         a notopo
##     3: PA0003      poola  8.387         a notopo
##     4: PA0004      poola  7.627         a notopo
##     5: PA0005      poola  7.700         a notopo
##    ---                                          
## 54416: PA5566 poolpmg_tp  0.000       pmg   topo
## 54417: PA5567 poolpmg_tp  0.000       pmg   topo
## 54418: PA5568 poolpmg_tp  0.000       pmg   topo
## 54419: PA5569 poolpmg_tp  0.000       pmg   topo
## 54420: PA5570 poolpmg_tp  0.000       pmg   topo
plot_nonzero(pa01_expt)$plot

rpkm_df <- as.data.frame(exprs(pa01_rpkm)) %>%
  round(2)
wanted_annotations <- fData(pa01_expt)[, c("gene", "locus_tag")]
wanted_data <- merge(wanted_annotations, rpkm_df,
                     by="row.names")
rownames(wanted_data) <- wanted_data[["Row.names"]]
wanted_data[["Row.names"]] <- NULL

7 Gathering mutations numbers by gene

I do not have an instant function for this task, but it should not prove difficult.

library(dplyr)
## 
## Attaching package: 'dplyr'
## The following object is masked from 'package:hpgltools':
## 
##     combine
## The following object is masked from 'package:testthat':
## 
##     matches
## The following objects are masked from 'package:GenomicRanges':
## 
##     intersect, setdiff, union
## The following object is masked from 'package:GenomeInfoDb':
## 
##     intersect
## The following objects are masked from 'package:IRanges':
## 
##     collapse, desc, intersect, setdiff, slice, union
## The following objects are masked from 'package:S4Vectors':
## 
##     first, intersect, rename, setdiff, setequal, union
## The following object is masked from 'package:matrixStats':
## 
##     count
## The following object is masked from 'package:Biobase':
## 
##     combine
## The following objects are masked from 'package:BiocGenerics':
## 
##     combine, intersect, setdiff, union
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
wanted_v2 <- wanted_data

for (sample in rownames(pData(pa01_expt))) {
  full_file <- glue::glue("preprocessing/{sample}/outputs/50freebayes_paeruginosa_pa01/all_tags.txt.xz")
  input_file <- glue::glue("preprocessing/{sample}/outputs/50freebayes_paeruginosa_pa01/variants_by_gene.txt.xz")
  if (!file.exists(full_file)) {
    next
  }
  input_data <- readr::read_tsv(
                           input_file, show_col_types=FALSE, skip=1,
                           col_names=c("gene", "chr", "position", "nt_string", "aa_string")) %>%
    as.data.frame()
  input_data[["from"]] <- gsub(x=input_data[["nt_string"]], pattern="(\\w+)_(\\w+)",
                               replacement="\\1", perl=TRUE)
  input_data[["to"]] <- gsub(x=input_data[["nt_string"]], pattern="(\\w+)_(\\w+)",
                             replacement="\\2", perl=TRUE)
  input_data[["encoded"]] <- paste0("chr_", input_data[["chr"]], "_pos_",
                                    input_data[["position"]], "_ref_",
                                    input_data[["from"]], "_alt_",
                                    input_data[["to"]])
  penetrance <- readr::read_tsv(full_file, show_col_types=FALSE) %>%
    select(c("position", "PAIRED"))
  input_data <- merge(input_data, penetrance, by.x="encoded", by.y="position", all.x=TRUE)
  input_column <- input_data %>%
    dplyr::group_by(gene) %>%
    dplyr::summarize(n=n())
  colnames(input_column) <- c("gene", paste0(sample, "_n"))
  penetrance_column <- input_data %>%
    group_by(gene) %>%
    dplyr::mutate(aa_penetrance=paste0(aa_string, '_', PAIRED, collapse=",")) %>%
    select(gene, aa_penetrance) %>%
    distinct()
  colnames(penetrance_column) <- c("gene", paste0(sample, "_aa_penetrance"))
  input_columns <- merge(input_column, penetrance_column, by="gene")
  wanted_v2 <- merge(wanted_v2, input_columns, by.x="row.names", by.y="gene", all.x=TRUE)
  rownames(wanted_v2) <- wanted_v2[["Row.names"]]
  wanted_v2[["Row.names"]] <- NULL
}
## New names:
## * DP -> DP...3
## * RO -> RO...8
## * AO -> AO...9
## * QR -> QR...12
## * QA -> QA...13
## * ...
## New names:
## * DP -> DP...3
## * RO -> RO...8
## * AO -> AO...9
## * QR -> QR...12
## * QA -> QA...13
## * ...
## New names:
## * DP -> DP...3
## * RO -> RO...8
## * AO -> AO...9
## * QR -> QR...12
## * QA -> QA...13
## * ...
## New names:
## * DP -> DP...3
## * RO -> RO...8
## * AO -> AO...9
## * QR -> QR...12
## * QA -> QA...13
## * ...
## New names:
## * DP -> DP...3
## * RO -> RO...8
## * AO -> AO...9
## * QR -> QR...12
## * QA -> QA...13
## * ...
## Write it out
written <- write_xlsx(data=wanted_v2, excel="excel/rpkm_mutations_per_gene_dedup.xlsx")

8 Try visualizing some of this information

pa_snp_expt <- count_expt_snps(pa01_expt, annot_column="freebayestable", snp_column="PAIRED")
## New names:
## * DP -> DP...3
## * RO -> RO...8
## * AO -> AO...9
## * QR -> QR...12
## * QA -> QA...13
## * ...
## New names:
## * DP -> DP...3
## * RO -> RO...8
## * AO -> AO...9
## * QR -> QR...12
## * QA -> QA...13
## * ...
## New names:
## * DP -> DP...3
## * RO -> RO...8
## * AO -> AO...9
## * QR -> QR...12
## * QA -> QA...13
## * ...
## New names:
## * DP -> DP...3
## * RO -> RO...8
## * AO -> AO...9
## * QR -> QR...12
## * QA -> QA...13
## * ...
## New names:
## * DP -> DP...3
## * RO -> RO...8
## * AO -> AO...9
## * QR -> QR...12
## * QA -> QA...13
## * ...
## New names:
## * DP -> DP...3
## * RO -> RO...8
## * AO -> AO...9
## * QR -> QR...12
## * QA -> QA...13
## * ...
## New names:
## * DP -> DP...3
## * RO -> RO...8
## * AO -> AO...9
## * QR -> QR...12
## * QA -> QA...13
## * ...
## New names:
## * DP -> DP...3
## * RO -> RO...8
## * AO -> AO...9
## * QR -> QR...12
## * QA -> QA...13
## * ...
## New names:
## * DP -> DP...3
## * RO -> RO...8
## * AO -> AO...9
## * QR -> QR...12
## * QA -> QA...13
## * ...
## New names:
## * DP -> DP...3
## * RO -> RO...8
## * AO -> AO...9
## * QR -> QR...12
## * QA -> QA...13
## * ...
plot_libsize(pa_snp_expt)$plot

test <- plot_corheat(pa_snp_expt)
pander::pander(sessionInfo())
message(paste0("This is hpgltools commit: ", get_git_commit()))
this_save <- paste0(gsub(pattern="\\.Rmd", replace="", x=rmd_file), "-v", ver, ".rda.xz")
message(paste0("Saving to ", this_save))
tmp <- sm(saveme(filename=this_save))
LS0tCnRpdGxlOiAiRXhhbWluaW5nIHRoZSBQc2V1ZG9tb25hcyBPUkZlb21lIHZpYSBkZWVwIHNlcXVlbmNpbmcgb2YgYSBwbGFzbWlkIGxpYnJhcnkuIgphdXRob3I6ICJhdGIgYWJlbGV3QGdtYWlsLmNvbSIKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgIGNvZGVfZm9sZGluZzogc2hvdwogICAgZmlnX2NhcHRpb246IHRydWUKICAgIGZpZ19oZWlnaHQ6IDcKICAgIGZpZ193aWR0aDogNwogICAgaGlnaGxpZ2h0OiB0YW5nbwogICAga2VlcF9tZDogZmFsc2UKICAgIG1vZGU6IHNlbGZjb250YWluZWQKICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQogICAgc2VsZl9jb250YWluZWQ6IHRydWUKICAgIHRoZW1lOiByZWFkYWJsZQogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6CiAgICAgIGNvbGxhcHNlZDogZmFsc2UKICAgICAgc21vb3RoX3Njcm9sbDogZmFsc2UKICBybWRmb3JtYXRzOjpyZWFkdGhlZG93bjoKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgIGNvZGVfZm9sZGluZzogc2hvdwogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgICBmaWdfY2FwdGlvbjogdHJ1ZQogICAgZmlnX2hlaWdodDogNwogICAgZmlnX3dpZHRoOiA3CiAgICBoaWdobGlnaHQ6IHRhbmdvCiAgICB3aWR0aDogMzAwCiAgICBrZWVwX21kOiBmYWxzZQogICAgbW9kZTogc2VsZmNvbnRhaW5lZAogICAgdG9jX2Zsb2F0OiB0cnVlCiAgQmlvY1N0eWxlOjpodG1sX2RvY3VtZW50OgogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgY29kZV9mb2xkaW5nOiBzaG93CiAgICBmaWdfY2FwdGlvbjogdHJ1ZQogICAgZmlnX2hlaWdodDogNwogICAgZmlnX3dpZHRoOiA3CiAgICBoaWdobGlnaHQ6IHRhbmdvCiAgICBrZWVwX21kOiBmYWxzZQogICAgbW9kZTogc2VsZmNvbnRhaW5lZAogICAgdG9jX2Zsb2F0OiB0cnVlCi0tLQoKPHN0eWxlIHR5cGU9InRleHQvY3NzIj4KYm9keSwgdGQgewogIGZvbnQtc2l6ZTogMTZweDsKfQpjb2RlLnJ7CiAgZm9udC1zaXplOiAxNnB4Owp9CnByZSB7CiBmb250LXNpemU6IDE2cHgKfQo8L3N0eWxlPgoKYGBge3Igb3B0aW9ucywgaW5jbHVkZT1GQUxTRX0KbGlicmFyeSgiaHBnbHRvb2xzIikKdHQgPC0gZGV2dG9vbHM6OmxvYWRfYWxsKCJ+L2hwZ2x0b29scyIpCmtuaXRyOjpvcHRzX2tuaXQkc2V0KHdpZHRoPTEyMCwKICAgICAgICAgICAgICAgICAgICAgcHJvZ3Jlc3M9VFJVRSwKICAgICAgICAgICAgICAgICAgICAgdmVyYm9zZT1UUlVFLAogICAgICAgICAgICAgICAgICAgICBlY2hvPVRSVUUpCmtuaXRyOjpvcHRzX2NodW5rJHNldChlcnJvcj1UUlVFLAogICAgICAgICAgICAgICAgICAgICAgZHBpPTk2KQpvbGRfb3B0aW9ucyA8LSBvcHRpb25zKGRpZ2l0cz00LAogICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnM9RkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAga25pdHIuZHVwbGljYXRlLmxhYmVsPSJhbGxvdyIpCmdncGxvdDI6OnRoZW1lX3NldChnZ3Bsb3QyOjp0aGVtZV9idyhiYXNlX3NpemU9MTApKQpydW5kYXRlIDwtIGZvcm1hdChTeXMuRGF0ZSgpLCBmb3JtYXQ9IiVZJW0lZCIpCnByZXZpb3VzX2ZpbGUgPC0gIiIKdmVyIDwtIGZvcm1hdChTeXMuRGF0ZSgpLCAiJVklbSVkIikKCiMjdG1wIDwtIHNtKGxvYWRtZShmaWxlbmFtZT1wYXN0ZTAoZ3N1YihwYXR0ZXJuPSJcXC5SbWQiLCByZXBsYWNlPSIiLCB4PXByZXZpb3VzX2ZpbGUpLCAiLXYiLCB2ZXIsICIucmRhLnh6IikpKQpybWRfZmlsZSA8LSAiaW5kZXguUm1kIgpgYGAKCiMgSW50cm9kdWN0aW9uCgpUaGlzIGRvY3VtZW50IHNlZWtzIHRvIGxheSBvdXQgdGhlIHN0cmF0ZWd5IGVtcGxveWVkIGZvciBhbgpleGFtaW5hdGlvbiBvZiB0aGUgc2VxdWVuY2luZyByZXN1bHRzIGZyb20gYSBncm91cCBvZiBwbGFzbWlkCmxpYnJhcmllcyBmcm9tIFBzZXVkbW9uYXMgYWVydWdpbm9zYSBQQTAxLgoKVGhlc2Ugc2FtcGxlcyBhcmUgaW4gNSBtYWluIGxpYnJhcmllcywgZWFjaCBvZiB3aGljaCB3YXMgZG9uZSB0d2ljZSwKb25jZSB3aXRoIGEgdG9wb2lzb21lcmFzZSBkdXJpbmcgaXNvbGF0aW9uIGFuZCBvbmNlIHdpdGhvdXQuCgpUaGUgaW1tZWRpYXRlIGdvYWwgb2YgdGhpcyBleHBlcmltZW50IGlzIHRvIGxlYXJuIHdoYXQgT1JGcyBleGlzdCBpbgp0aGUgbGlicmFyeSBhbmQgd2hhdCBtdXRhdGlvbnMgaGF2ZSBhcHBlYXJlZCBpbiB0aGVtLgoKIyBQcmVwcm9jZXNzaW5nCgpUaGUgcHJlcHJvY2Vzc2luZyBpcyBtb3N0bHkgYSBub3JtYWwgRE5BIHNlcXVlbmNpbmcgcGlwZWxpbmU6CgoxLiAgVHJpbSB0aGUgcmVhZHMuCjIuICBNYXAgdGhlbSBhZ2FpbnN0IHRoZSBwbGFzbWlkLCBwRE9OUjIwMS4KMy4gIE1hcCB0aGUgdW5tYXBwZWQgcmVhZHMgZnJvbSAjMiBhZ2FpbnN0IFBBMDEuCjQuICBDb3VudCB0aGUgcmVhZHMgYnkgbG9jdXNfdGFnIHRvIGlkZW50aWZ5IG1pc3NpbmcgZ2VuZXMuCjUuICBVc2UgZnJlZWJheWVzIHRvIGlkZW50aWZ5IHZhcmlhbnQgcG9zaXRpb25zLgo2LiAgUGFyc2UgdGhlIGZyZWViYXllcyBvdXRwdXQgZG93biB0byBhbiBlYXN5IHRvIGludGVycHJldCBmb3JtYXQuCgojIEEgc3VnZ2VzdGlvbiBmcm9tIE5vdXIKCk5vdXIgbWFkZSBhIGdvb2Qgc3VnZ2VzdGlvbjogZ2F0ay9waWNhcmQgcHJvdmlkZXMgYSB0b29sIHRvCm1hcmsvZGVkdXBsaWNhdGUgc2VxdWVuY2UgcmVhZHMgZ2l2ZW4gYSBzZXQgb2YgbWFwcGVkIHJlYWRzLiAgSQphcHBsaWVkIHRoYXQgdG8gdGhpcyBkYXRhIHNldCBhbmQgbm90ZWQgc29tZSBpbnRlcmVzdGluZyBkaWZmZXJlbmNlcy4KVGhpcyBpcyBpbXBsZW1lbnRlZCBhcyBhIG1ldGhvZCBjYWxsZWQgJ01hcmtEdXBsaWNhdGVzJyBvciBzb21lIHN1Y2gsCkkgd2lsbCBpbmNsdWRlIGEgc2NyaXB0IHN0YW56YSBiZWxvdyBzaG93aW5nIGl0cyB1c2FnZS4KCiMjIFRyaW1taW5nCgpUaGlzIGlzIG15IGRlZmF1bHQgdHJpbW9tYXRpYyBpbnZvY2F0aW9uLgoKCmBgYHtiYXNoIHRyaW1taW5nLCBldmFsPUZBTFNFfQpjZCBwcmVwcm9jZXNzaW5nCnN0YXJ0PSQocHdkKQpmb3IgaSBpbiAkKC9iaW4vbHMpOyBkbwogICAgY2QgJHtpfQogICAgY3lvYSAtLXRhc2sgdHJpbSAtLW1ldGhvZCB0cmltIC0taW5wdXQgJCgvYmluL2xzICouZ3ogfCB0ciAnXG4nICc6JyB8IHNlZCAncy86JC8vZycpCiAgICBjZCAkc3RhcnQKZG9uZQpgYGAKCkhlcmUgaXMgdGhlIHNjcmlwdCB3aGljaCByZXN1bHRlZCBmb3IgdGhlIHBvb2xhIHNhbXBsZToKCkkgc3RpbGwgaGF2ZSBhbiBvbGQgY2hlY2sgaW4gaXQgZnJvbSBhbiBlYXJsaWVyIHZlcnNpb24gb2YgdHJpbW9tYXRpYwp3aGljaCB3b3VsZCBzb21ldGltZXMgZmFpbC4KCiMjIyBJbmRpdmlkdWFsIHRyaW1taW5nIHNjcmlwdAoKYGBge2Jhc2ggcG9vbGFfdHJpbV9zY3JpcHQsIGV2YWw9RkFMU0V9CiNTQkFUQ0ggLS1leHBvcnQ9QUxMCiNTQkFUQ0ggLS1tYWlsLXR5cGU9Tk9ORQojU0JBVENIIC0tb3Blbi1tb2RlPWFwcGVuZAojU0JBVENIIC0tY2hkaXI9L2ZzL2NiY2ItbGFiL25lbHNheWVkL3NjcmF0Y2gvYXRiL2RuYXNlcS9wYWVydWdpbm9zYV9wbGFzbWlkc18yMDIyL3ByZXByb2Nlc3NpbmcvcG9vbGEKI1NCQVRDSCAtLXBhcnRpdGlvbj1kcGFydAojU0JBVENIIC0tcW9zPXdvcmtzdGF0aW9uIC0tbmljZT0xMAojU0JBVENIIC0tbm9kZXM9MSAtLXJlcXVldWUKI1NCQVRDSCAtLXRpbWU9MjQ6MDA6MDAKI1NCQVRDSCAtLWpvYi1uYW1lPTAxdHJpbV9PUkZlb21lX1Bvb2xfQV9TMV9SMV8wMDEKI1NCQVRDSCAtLW1lbT0yNEcKI1NCQVRDSCAtLWNwdXMtcGVyLXRhc2s9MwojU0JBVENIIC0tb3V0cHV0PW91dHB1dHMvbG9nLnR4dC5zYmF0Y2gKc2V0IC1vIGVycmV4aXQKc2V0IC1vIGVycnRyYWNlCnNldCAtbyBwaXBlZmFpbApleHBvcnQgTEVTUz0nLS1idWZmZXJzIDAnCnNjcmlwdD0iJChwd2QpLyQwIgplcnIoKSB7CiAgICBlY2hvICJFcnJvciBvY2N1cnJlZDoiCiAgICBhd2sgJ05SPkwtNCAmJiBOUjxMKzQgeyBwcmludGYgIiUtNWQlM3Mlc1xuIixOUiwoTlI9PUw/Ij4+PiI6IiIpLCRzY3JpcHQgfScgTD0kMSAkc2NyaXB0Cn0KdHJhcCAnZXJyICRMSU5FTk8nIEVSUgoKZWNobyAiIyMgU3RhcnRlZCAvZnMvY2JjYi1sYWIvbmVsc2F5ZWQvc2NyYXRjaC9hdGIvZG5hc2VxL3BhZXJ1Z2lub3NhX3BsYXNtaWRzXzIwMjIvcHJlcHJvY2Vzc2luZy9wb29sYS9zY3JpcHRzLzAxdHJpbV9PUkZlb21lX1Bvb2xfQV9TMV9SMV8wMDEuc2ggYXQgJChkYXRlKSBvbiAkKGhvc3RuYW1lKSB3aXRoIGlkICR7U0xVUk1fSk9CSUR9LiIgPj4gb3V0cHV0cy9sb2cudHh0Cgptb2R1bGUgYWRkIHRyaW1vbWF0aWMKCiMjIFRoaXMgY2FsbCB0byB0cmltb21hdGljIHJlbW92ZXMgaWxsdW1pbmEgYW5kIGVwaWNlbnRyZSBhZGFwdGVycyBmcm9tIE9SRmVvbWVfUG9vbF9BX1MxX1IxXzAwMS5mYXN0cS5nejpPUkZlb21lX1Bvb2xfQV9TMV9SMl8wMDEuZmFzdHEuZ3ouCiMjIEl0IGFsc28gcGVyZm9ybXMgYSBzbGlkaW5nIHdpbmRvdyByZW1vdmFsIG9mIGFueXRoaW5nIHdpdGggcXVhbGl0eSA8MjU7CiMjIGN1dGFkYXB0IHByb3ZpZGVzIGFuIGFsdGVybmF0aXZlIHRvIHRoaXMgdG9vbC4KIyMgVGhlIG9yaWdpbmFsIHNlcXVlbmNlIGRhdGEgaXMgcmVjb21wcmVzc2VkIGFuZCBzYXZlZCBpbiB0aGUgc2VxdWVuY2VzLyBkaXJlY3RvcnkuCm1rZGlyIC1wIG91dHB1dHMvMDF0cmltb21hdGljCiMjIE5vdGUgdGhhdCB0cmltb21hdGljIHByaW50cyBhbGwgb3V0cHV0IGFuZCBlcnJvcnMgdG8gU1RERVJSLCBzbyBzZW5kIGJvdGggdG8gb3V0cHV0CnRyaW1tb21hdGljIFBFIFwKICAtdGhyZWFkcyAxIFwKICAtcGhyZWQzMyBcCiAgPChsZXNzIHIxLmZhc3RxLmd6KSA8KGxlc3MgcjIuZmFzdHEuZ3opIFwKICBPUkZlb21lX1Bvb2xfQV9TMV9SMV8wMDEtdHJpbW1lZF9wYWlyZWQuZmFzdHEgT1JGZW9tZV9Qb29sX0FfUzFfUjFfMDAxLXRyaW1tZWRfdW5wYWlyZWQuZmFzdHEgXAogIE9SRmVvbWVfUG9vbF9BX1MxX1IyXzAwMS10cmltbWVkX3BhaXJlZC5mYXN0cSBPUkZlb21lX1Bvb2xfQV9TMV9SMl8wMDEtdHJpbW1lZF91bnBhaXJlZC5mYXN0cSBcCiAgIElMTFVNSU5BQ0xJUDovY2JjYi9zdy9SZWRIYXQtNy14ODZfNjQvY29tbW9uL2xvY2FsL3BlcmwvNS4zNC9saWIvc2l0ZV9wZXJsLzUuMzQuMC9hdXRvL3NoYXJlL2Rpc3QvQmlvLUFkdmVudHVyZS9nZW5vbWUvYWRhcHRlcnMuZmE6MjoyMDoxMDoyOmtlZXBCb3RoUmVhZHMgXAogIFNMSURJTkdXSU5ET1c6NDoyMCBNSU5MRU46NDAgXAogIDE+b3V0cHV0cy8wMXRyaW1vbWF0aWMvT1JGZW9tZV9Qb29sX0FfUzFfUjFfMDAxLXRyaW1vbWF0aWMuc3Rkb3V0IFwKICAyPm91dHB1dHMvMDF0cmltb21hdGljL09SRmVvbWVfUG9vbF9BX1MxX1IxXzAwMS10cmltb21hdGljLnN0ZGVycgpleGNlcHRlZD0kKCB7IGdyZXAgIkV4Y2VwdGlvbiIgIm91dHB1dHMvMDF0cmltb21hdGljL09SRmVvbWVfUG9vbF9BX1MxX1IxXzAwMS10cmltb21hdGljLnN0ZG91dCIgfHwgdGVzdCAkPyA9IDE7IH0gKQojIyBUaGUgZm9sbG93aW5nIGlzIGluIGNhc2UgdGhlIGlsbHVtaW5hIGNsaXBwaW5nIGZhaWxzLCB3aGljaCBpdCBkb2VzIGlmIHRoaXMgaGFzIGFscmVhZHkgYmVlbiBydW4gSSB0aGluay4KaWYgW1sgIiR7ZXhjZXB0ZWR9IiAhPSAiIiBdXTsgdGhlbgogIHRyaW1tb21hdGljIFBFIFwKICAgIC10aHJlYWRzIDEgXAogICAgLXBocmVkMzMgXAogICAgPChsZXNzIHIxLmZhc3RxLmd6KSA8KGxlc3MgcjIuZmFzdHEuZ3opIFwKICAgIE9SRmVvbWVfUG9vbF9BX1MxX1IxXzAwMS10cmltbWVkX3BhaXJlZC5mYXN0cSBPUkZlb21lX1Bvb2xfQV9TMV9SMV8wMDEtdHJpbW1lZF91bnBhaXJlZC5mYXN0cSBcCiAgICBPUkZlb21lX1Bvb2xfQV9TMV9SMl8wMDEtdHJpbW1lZF9wYWlyZWQuZmFzdHEgT1JGZW9tZV9Qb29sX0FfUzFfUjJfMDAxLXRyaW1tZWRfdW5wYWlyZWQuZmFzdHEgXAogICAgIFNMSURJTkdXSU5ET1c6NDoyNSBNSU5MRU46NTBcCiAgICAxPm91dHB1dHMvMDF0cmltb21hdGljL09SRmVvbWVfUG9vbF9BX1MxX1IxXzAwMS10cmltb21hdGljLnN0ZG91dCBcCiAgICAyPm91dHB1dHMvMDF0cmltb21hdGljL09SRmVvbWVfUG9vbF9BX1MxX1IxXzAwMS10cmltb21hdGljLnN0ZGVycgpmaQpzbGVlcCAxMAptdiBPUkZlb21lX1Bvb2xfQV9TMV9SMV8wMDEtdHJpbW1lZF9wYWlyZWQuZmFzdHEgT1JGZW9tZV9Qb29sX0FfUzFfUjFfMDAxLXRyaW1tZWQuZmFzdHEKbXYgT1JGZW9tZV9Qb29sX0FfUzFfUjJfMDAxLXRyaW1tZWRfcGFpcmVkLmZhc3RxIE9SRmVvbWVfUG9vbF9BX1MxX1IyXzAwMS10cmltbWVkLmZhc3RxCgojIyBSZWNvbXByZXNzIHRoZSB1bnBhaXJlZCByZWFkcywgdGhpcyBzaG91bGQgbm90IHRha2UgbG9uZy4KeHogLTllIC1mIE9SRmVvbWVfUG9vbF9BX1MxX1IxXzAwMS10cmltbWVkX3VucGFpcmVkLmZhc3RxCnh6IC05ZSAtZiBPUkZlb21lX1Bvb2xfQV9TMV9SMl8wMDEtdHJpbW1lZF91bnBhaXJlZC5mYXN0cQojIyBSZWNvbXByZXNzIHRoZSBwYWlyZWQgcmVhZHMuCnh6IC05ZSAtZiBPUkZlb21lX1Bvb2xfQV9TMV9SMV8wMDEtdHJpbW1lZC5mYXN0cQp4eiAtOWUgLWYgT1JGZW9tZV9Qb29sX0FfUzFfUjJfMDAxLXRyaW1tZWQuZmFzdHEKbG4gLXNmIE9SRmVvbWVfUG9vbF9BX1MxX1IxXzAwMS10cmltbWVkLmZhc3RxLnh6IHIxX3RyaW1tZWQuZmFzdHEueHoKbG4gLXNmIE9SRmVvbWVfUG9vbF9BX1MxX1IyXzAwMS10cmltbWVkLmZhc3RxLnh6IHIyX3RyaW1tZWQuZmFzdHEueHoKCgojIyBUaGUgZm9sbG93aW5nIGxpbmVzIGdpdmUgc3RhdHVzIGNvZGVzIGFuZCBzb21lIGxvZ2dpbmcKZWNobyAiIyMgSm9iIHN0YXR1czogJD8gIiA+PiBvdXRwdXRzL2xvZy50eHQKZWNobyAiIyMgJChob3N0bmFtZSkgRmluaXNoZWQgJHtTTFVSTV9KT0JJRH0gMDF0cmltX09SRmVvbWVfUG9vbF9BX1MxX1IxXzAwMS5zaCBhdCAkKGRhdGUpLCBpdCB0b29rICQoKCBTRUNPTkRTIC8gNjAgKSkgbWludXRlcy4iID4+IG91dHB1dHMvbG9nLnR4dAoKaWYgW1sgLXggIiQoY29tbWFuZCAtdiBzc3RhdCkiICYmICEgLXogIiR7U0xVUk1fSk9CSUR9IiBdXTsgdGhlbgogIHdhbGx0aW1lPSQoc2NvbnRyb2wgc2hvdyBqb2IgIiR7U0xVUk1fSk9CSUR9IiB8IGdyZXAgUnVuVGltZSB8IHBlcmwgLUYnL1xzK3w9LycgLWxhbmUgJ3twcmludCAkRlsyXX0nIDI+L2Rldi9udWxsKQogIGVjaG8gIiMjIyMgd2FsbHRpbWUgdXNlZCBieSAke1NMVVJNX0pPQklEfSB3YXM6ICR7d2FsbHRpbWU6LW51bGx9IiA+PiBvdXRwdXRzL2xvZy50eHQKICBtYXhtZW09JChzc3RhdCAtLWZvcm1hdD1NYXhWTVNpemUgLW4gIiR7U0xVUk1fSk9CSUR9LmJhdGNoIiAyPi9kZXYvbnVsbCkKICBlY2hvICIjIyMjIG1heGltdW0gbWVtb3J5IHVzZWQgYnkgJHtTTFVSTV9KT0JJRH0gd2FzOiAke21heG1lbTotbnVsbH0iID4+IG91dHB1dHMvbG9nLnR4dAogIGF2ZWNwdT0kKHNzdGF0IC0tZm9ybWF0PUF2ZUNQVSAtbiAiJHtTTFVSTV9KT0JJRH0uYmF0Y2giIDI+L2Rldi9udWxsKQogIGVjaG8gIiMjIyMgYXZlcmFnZSBjcHUgdXNlZCBieSAke1NMVVJNX0pPQklEfSB3YXM6ICR7YXZlY3B1Oi1udWxsfSIgPj4gb3V0cHV0cy9sb2cudHh0CmZpCiMjIEFkZGluZyBhIGxpdHRsZSBsb2dpYyB0byBoYXZlIHNraXAgZmluaXNoZWQgam9icy4KdG91Y2ggb3V0cHV0cy9sb2dzLzAxdHJpbV9PUkZlb21lX1Bvb2xfQV9TMV9SMV8wMDEuZmluaXNoZWQKYGBgCgojIyBNYXBwaW5nIGFnYWluc3QgcERPTlIKClRoaXMgd2FzIGZvbGxvd2VkIGJ5IG15IGRlZmF1bHQgbWFwcGluZyBhZ2FpbnN0IHRoZSBwbGFzbWlkIHZpYQpoaXNhdDIuCgpJIHByZXZpb3VzbHkgZG93bmxvYWRlZCBhIGNvcHkgb2YgcERPTlIyMDEgYW5kIHJhbiB0aGUgZm9sbG93aW5nIG9uCml0OgoKYGBge2Jhc2ggcGRvbnJfaW5kZXgsIGV2YWw9RkFMU0V9CmN5b2EgLS10YXNrIGluZGV4IC0tbWV0aG9kIGluZGV4aGlzYXQgLS1pbnB1dCBwRE9OUjIwMS5mYXN0YSAtLXNwZWNpZXMgcERPTlIyMDEKYGBgCgpJIGRpZCBub3QgZ2VuZXJhdGUgYSBnZmYgZmlsZSBmb3IgdGhpcywgc28gaHRzZXEgaXMgbm90IHJ1bi4KClRoZSBmb2xsb3dpbmcgaW52b2NhdGlvbiBxdWV1ZXMgMiBqb2JzIG9uIHRoZSBjbHVzdGVyLCAxIGZvciB0aGUKbWFwcGluZyBhbmQgMSB0byBzb3J0L2NvbXByZXNzL2luZGV4IHRoZSBhbGlnbm1lbnQuCgpgYGB7YmFzaCBwZG9ucl9oaXNhdCwgZXZhbD1GQUxTRX0KY2QgcHJlcHJvY2Vzc2luZwpzdGFydD0kKHB3ZCkKZm9yIGkgaW4gJCgvYmluL2xzKTsgZG8KICAgIGNkICR7aX0KICAgIGN5b2EgLS10YXNrIG1hcCAtLW1ldGhvZCBoaXNhdCAtLXNwZWNpZXMgcERPTlIyMDEgLS1pbnB1dCByMV90cmltbWVkLmZhc3RxLnh6OnIyX3RyaW1tZWQuZmFzdHEueHoKICAgIGNkICRzdGFydApkb25lCmBgYAoKIyMjIEluZGl2aWR1YWwgcERPTlIgbWFwcGluZyBzY3JpcHQKCmBgYHtiYXNoIHBkb25vcl9tYXBwaW5nX3NjcmlwdCwgZXZhbD1GQUxTRX0KIyEvdXNyL2Jpbi9lbnYgYmFzaAojU0JBVENIIC0tZXhwb3J0PUFMTAojU0JBVENIIC0tbWFpbC10eXBlPU5PTkUKI1NCQVRDSCAtLW9wZW4tbW9kZT1hcHBlbmQKI1NCQVRDSCAtLWNoZGlyPS9mcy9jYmNiLWxhYi9uZWxzYXllZC9zY3JhdGNoL2F0Yi9kbmFzZXEvcGFlcnVnaW5vc2FfcGxhc21pZHNfMjAyMi9wcmVwcm9jZXNzaW5nL3Bvb2xhCiNTQkFUQ0ggLS1wYXJ0aXRpb249ZHBhcnQKI1NCQVRDSCAtLXFvcz13b3Jrc3RhdGlvbiAtLW5pY2U9MTAKI1NCQVRDSCAtLW5vZGVzPTEgLS1yZXF1ZXVlCiNTQkFUQ0ggLS10aW1lPTEwOjAwOjAwCiNTQkFUQ0ggLS1qb2ItbmFtZT00MGhpc2F0Ml9wRE9OUjIwMV9nZW5vbWUKI1NCQVRDSCAtLW1lbT0yNEcKI1NCQVRDSCAtLWNwdXMtcGVyLXRhc2s9NAojU0JBVENIIC0tb3V0cHV0PW91dHB1dHMvbG9nLnR4dC5zYmF0Y2gKc2V0IC1vIGVycmV4aXQKc2V0IC1vIGVycnRyYWNlCnNldCAtbyBwaXBlZmFpbApleHBvcnQgTEVTUz0nLS1idWZmZXJzIDAnCnNjcmlwdD0iJChwd2QpLyQwIgplcnIoKSB7CiAgICBlY2hvICJFcnJvciBvY2N1cnJlZDoiCiAgICBhd2sgJ05SPkwtNCAmJiBOUjxMKzQgeyBwcmludGYgIiUtNWQlM3Mlc1xuIixOUiwoTlI9PUw/Ij4+PiI6IiIpLCRzY3JpcHQgfScgTD0kMSAkc2NyaXB0Cn0KdHJhcCAnZXJyICRMSU5FTk8nIEVSUgoKZWNobyAiIyMgU3RhcnRlZCAvZnMvY2JjYi1sYWIvbmVsc2F5ZWQvc2NyYXRjaC9hdGIvZG5hc2VxL3BhZXJ1Z2lub3NhX3BsYXNtaWRzXzIwMjIvcHJlcHJvY2Vzc2luZy9wb29sYS9zY3JpcHRzLzQwaGlzYXQyX3BET05SMjAxX2dlbm9tZS5zaCBhdCAkKGRhdGUpIG9uICQoaG9zdG5hbWUpIHdpdGggaWQgJHtTTFVSTV9KT0JJRH0uIiA+PiBvdXRwdXRzL2xvZy50eHQKCm1vZHVsZSBhZGQgaGlzYXQyIHNhbXRvb2xzIGh0c2VxIGJhbXRvb2xzCgojIyBUaGlzIGlzIGEgaGlzYXQyIGFsaWdubWVudCBvZiAgLTEgPChsZXNzIC9mcy9jYmNiLWxhYi9uZWxzYXllZC9zY3JhdGNoL2F0Yi9kbmFzZXEvcGFlcnVnaW5vc2FfcGxhc21pZHNfMjAyMi9wcmVwcm9jZXNzaW5nL3Bvb2xhL3IxX3RyaW1tZWQuZmFzdHEueHopIC0yIDwobGVzcyAvZnMvY2JjYi1sYWIvbmVsc2F5ZWQvc2NyYXRjaC9hdGIvZG5hc2VxL3BhZXJ1Z2lub3NhX3BsYXNtaWRzXzIwMjIvcHJlcHJvY2Vzc2luZy9wb29sYS9yMl90cmltbWVkLmZhc3RxLnh6KSAgYWdhaW5zdCAvY2JjYmhvbWVzL2FiZWxldy9saWJyYXJpZXMvZ2Vub21lL2luZGV4ZXMvcERPTlIyMDEKCm1rZGlyIC1wIG91dHB1dHMvNDBoaXNhdDJfcERPTlIyMDEKaGlzYXQyIC14IC9jYmNiaG9tZXMvYWJlbGV3L2xpYnJhcmllcy9nZW5vbWUvaW5kZXhlcy9wRE9OUjIwMSAgXAogIC1wIDQgXAogIC1xICAgLTEgPChsZXNzIC9mcy9jYmNiLWxhYi9uZWxzYXllZC9zY3JhdGNoL2F0Yi9kbmFzZXEvcGFlcnVnaW5vc2FfcGxhc21pZHNfMjAyMi9wcmVwcm9jZXNzaW5nL3Bvb2xhL3IxX3RyaW1tZWQuZmFzdHEueHopIC0yIDwobGVzcyAvZnMvY2JjYi1sYWIvbmVsc2F5ZWQvc2NyYXRjaC9hdGIvZG5hc2VxL3BhZXJ1Z2lub3NhX3BsYXNtaWRzXzIwMjIvcHJlcHJvY2Vzc2luZy9wb29sYS9yMl90cmltbWVkLmZhc3RxLnh6KSAgXAogIC0tcGhyZWQzMyBcCiAgLS11biBvdXRwdXRzLzQwaGlzYXQyX3BET05SMjAxL3Bvb2xhX3VuYWxkaXNfcERPTlIyMDFfZ2Vub21lLmZhc3RxIFwKICAtLWFsIG91dHB1dHMvNDBoaXNhdDJfcERPTlIyMDEvcG9vbGFfYWxkaXNfcERPTlIyMDFfZ2Vub21lLmZhc3RxIFwKICAtLXVuLWNvbmMgb3V0cHV0cy80MGhpc2F0Ml9wRE9OUjIwMS9wb29sYV91bmFsY29uX3BET05SMjAxX2dlbm9tZS5mYXN0cSBcCiAgLS1hbC1jb25jIG91dHB1dHMvNDBoaXNhdDJfcERPTlIyMDEvcG9vbGFfYWxjb25fcERPTlIyMDFfZ2Vub21lLmZhc3RxIFwKICAtUyBvdXRwdXRzLzQwaGlzYXQyX3BET05SMjAxL3Bvb2xhX3BET05SMjAxX2dlbm9tZS5zYW0gXAogIDI+b3V0cHV0cy80MGhpc2F0Ml9wRE9OUjIwMS9oaXNhdDJfcERPTlIyMDFfZ2Vub21lX3Bvb2xhLnN0ZGVyciBcCiAgMT5vdXRwdXRzLzQwaGlzYXQyX3BET05SMjAxL2hpc2F0Ml9wRE9OUjIwMV9nZW5vbWVfcG9vbGEuc3Rkb3V0CgoKIyMgVGhlIGZvbGxvd2luZyBsaW5lcyBnaXZlIHN0YXR1cyBjb2RlcyBhbmQgc29tZSBsb2dnaW5nCmVjaG8gIiMjIEpvYiBzdGF0dXM6ICQ/ICIgPj4gb3V0cHV0cy9sb2cudHh0CmVjaG8gIiMjICQoaG9zdG5hbWUpIEZpbmlzaGVkICR7U0xVUk1fSk9CSUR9IDQwaGlzYXQyX3BET05SMjAxX2dlbm9tZS5zaCBhdCAkKGRhdGUpLCBpdCB0b29rICQoKCBTRUNPTkRTIC8gNjAgKSkgbWludXRlcy4iID4+IG91dHB1dHMvbG9nLnR4dAoKaWYgW1sgLXggIiQoY29tbWFuZCAtdiBzc3RhdCkiICYmICEgLXogIiR7U0xVUk1fSk9CSUR9IiBdXTsgdGhlbgogIHdhbGx0aW1lPSQoc2NvbnRyb2wgc2hvdyBqb2IgIiR7U0xVUk1fSk9CSUR9IiB8IGdyZXAgUnVuVGltZSB8IHBlcmwgLUYnL1xzK3w9LycgLWxhbmUgJ3twcmludCAkRlsyXX0nIDI+L2Rldi9udWxsKQogIGVjaG8gIiMjIyMgd2FsbHRpbWUgdXNlZCBieSAke1NMVVJNX0pPQklEfSB3YXM6ICR7d2FsbHRpbWU6LW51bGx9IiA+PiBvdXRwdXRzL2xvZy50eHQKICBtYXhtZW09JChzc3RhdCAtLWZvcm1hdD1NYXhWTVNpemUgLW4gIiR7U0xVUk1fSk9CSUR9LmJhdGNoIiAyPi9kZXYvbnVsbCkKICBlY2hvICIjIyMjIG1heGltdW0gbWVtb3J5IHVzZWQgYnkgJHtTTFVSTV9KT0JJRH0gd2FzOiAke21heG1lbTotbnVsbH0iID4+IG91dHB1dHMvbG9nLnR4dAogIGF2ZWNwdT0kKHNzdGF0IC0tZm9ybWF0PUF2ZUNQVSAtbiAiJHtTTFVSTV9KT0JJRH0uYmF0Y2giIDI+L2Rldi9udWxsKQogIGVjaG8gIiMjIyMgYXZlcmFnZSBjcHUgdXNlZCBieSAke1NMVVJNX0pPQklEfSB3YXM6ICR7YXZlY3B1Oi1udWxsfSIgPj4gb3V0cHV0cy9sb2cudHh0CmZpCiMjIEFkZGluZyBhIGxpdHRsZSBsb2dpYyB0byBoYXZlIHNraXAgZmluaXNoZWQgam9icy4KdG91Y2ggb3V0cHV0cy9sb2dzLzQwaGlzYXQyX3BET05SMjAxX2dlbm9tZS5maW5pc2hlZApgYGAKClRoZSBjeW9hIGludm9jYXRpb24gd2hpY2ggZ2VuZXJhdGVkIHRoZSBhYm92ZSBzY3JpcHQgYWxzbyBjcmVhdGVzIHRoZQpmb2xsb3dpbmcgc2NyaXB0IGFsb25nIHdpdGggYSBncm91cCBvZiBjb21wcmVzc2lvbiBqb2JzIHdoaWNoCmFnZ3Jlc3NpdmVseSBjb21wcmVzcyB0aGUgZmFzdHEgb3V0cHV0IGZpbGVzLiAgSSBsZWF2ZSB0aGF0IGFzIGFuCmV4ZXJjaXNlIHRvIHRoZSByZWFkZXIuCgpgYGB7YmFzaCBzYW0yYmFtX3BET05SLCBldmFsPUZBTFNFfQojIS91c3IvYmluL2VudiBiYXNoCiNTQkFUQ0ggLS1leHBvcnQ9QUxMCiNTQkFUQ0ggLS1tYWlsLXR5cGU9Tk9ORQojU0JBVENIIC0tb3Blbi1tb2RlPWFwcGVuZAojU0JBVENIIC0tY2hkaXI9L2ZzL2NiY2ItbGFiL25lbHNheWVkL3NjcmF0Y2gvYXRiL2RuYXNlcS9wYWVydWdpbm9zYV9wbGFzbWlkc18yMDIyL3ByZXByb2Nlc3NpbmcvcG9vbGEKI1NCQVRDSCAtLXBhcnRpdGlvbj1kcGFydAojU0JBVENIIC0tcW9zPXRocm91Z2hwdXQgLS1uaWNlPTEwCiNTQkFUQ0ggLS1ub2Rlcz0xIC0tcmVxdWV1ZQojU0JBVENIIC0tdGltZT0xMjowMDowMAojU0JBVENIIC0tam9iLW5hbWU9NDBfMnMyYl9oaXNhdDJfcERPTlIyMDEKI1NCQVRDSCAtLW1lbT0xMkcKI1NCQVRDSCAtLWNwdXMtcGVyLXRhc2s9MQojU0JBVENIIC0tb3V0cHV0PW91dHB1dHMvbG9nLnR4dC5zYmF0Y2gKc2V0IC1vIGVycmV4aXQKc2V0IC1vIGVycnRyYWNlCnNldCAtbyBwaXBlZmFpbApleHBvcnQgTEVTUz0nLS1idWZmZXJzIDAnCnNjcmlwdD0iJChwd2QpLyQwIgplcnIoKSB7CiAgICBlY2hvICJFcnJvciBvY2N1cnJlZDoiCiAgICBhd2sgJ05SPkwtNCAmJiBOUjxMKzQgeyBwcmludGYgIiUtNWQlM3Mlc1xuIixOUiwoTlI9PUw/Ij4+PiI6IiIpLCRzY3JpcHQgfScgTD0kMSAkc2NyaXB0Cn0KdHJhcCAnZXJyICRMSU5FTk8nIEVSUgoKZWNobyAiIyMgU3RhcnRlZCAvZnMvY2JjYi1sYWIvbmVsc2F5ZWQvc2NyYXRjaC9hdGIvZG5hc2VxL3BhZXJ1Z2lub3NhX3BsYXNtaWRzXzIwMjIvcHJlcHJvY2Vzc2luZy9wb29sYS9zY3JpcHRzLzQwXzJzMmJfaGlzYXQyX3BET05SMjAxLnNoIGF0ICQoZGF0ZSkgb24gJChob3N0bmFtZSkgd2l0aCBpZCAke1NMVVJNX0pPQklEfS4iID4+IG91dHB1dHMvbG9nLnR4dAoKbW9kdWxlIGFkZCBzYW10b29scyBiYW10b29scwoKIyMgQ29udmVydGluZyB0aGUgdGV4dCBzYW0gdG8gYSBjb21wcmVzc2VkLCBzb3J0ZWQsIGluZGV4ZWQgYmFtZmlsZS4KIyMgQWxzbyBwcmludGluZyBhbGlnbm1lbnQgc3RhdGlzdGljcyB0byBvdXRwdXRzLzQwaGlzYXQyX3BET05SMjAxL3Bvb2xhX3BET05SMjAxX2dlbm9tZS5iYW0uc3RhdHMKIyMgVGhpcyBqb2IgZGVwZW5kZWQgb246IDI0MDExMAoKZWNobyAiU3RhcnRpbmcgc2FtdG9vbHMiCmlmIFtbICEgLWYgIm91dHB1dHMvNDBoaXNhdDJfcERPTlIyMDEvcG9vbGFfcERPTlIyMDFfZ2Vub21lLnNhbSIgXV07IHRoZW4KICAgIGVjaG8gIkNvdWxkIG5vdCBmaW5kIHRoZSBzYW10b29scyBpbnB1dCBmaWxlLiIKICAgIGV4aXQgMQpmaQpzYW10b29scyB2aWV3IC11IC10IC9jYmNiaG9tZXMvYWJlbGV3L2xpYnJhcmllcy9nZW5vbWUvcERPTlIyMDEuZmFzdGEgXAogIC1TIG91dHB1dHMvNDBoaXNhdDJfcERPTlIyMDEvcG9vbGFfcERPTlIyMDFfZ2Vub21lLnNhbSAtbyBvdXRwdXRzLzQwaGlzYXQyX3BET05SMjAxL3Bvb2xhX3BET05SMjAxX2dlbm9tZS5iYW0gIFwKICAyPm91dHB1dHMvNDBoaXNhdDJfcERPTlIyMDEvcG9vbGFfcERPTlIyMDFfZ2Vub21lLmJhbS5lcnIgMT5vdXRwdXRzLzQwaGlzYXQyX3BET05SMjAxL3Bvb2xhX3BET05SMjAxX2dlbm9tZS5iYW0ub3V0ICYmIFwKZWNobyAwCiAgc2FtdG9vbHMgc29ydCAtbCA5IG91dHB1dHMvNDBoaXNhdDJfcERPTlIyMDEvcG9vbGFfcERPTlIyMDFfZ2Vub21lLmJhbSAtbyBvdXRwdXRzLzQwaGlzYXQyX3BET05SMjAxL3Bvb2xhX3BET05SMjAxX2dlbm9tZS1zb3J0ZWQuYmFtIFwKICAyPm91dHB1dHMvNDBoaXNhdDJfcERPTlIyMDEvcG9vbGFfcERPTlIyMDFfZ2Vub21lLXNvcnRlZC5zdGRlcnIgXAogIDE+b3V0cHV0cy80MGhpc2F0Ml9wRE9OUjIwMS9wb29sYV9wRE9OUjIwMV9nZW5vbWUtc29ydGVkLnN0ZG91dCAmJiBcCiAgcm0gb3V0cHV0cy80MGhpc2F0Ml9wRE9OUjIwMS9wb29sYV9wRE9OUjIwMV9nZW5vbWUuYmFtICYmIFwKICBybSBvdXRwdXRzLzQwaGlzYXQyX3BET05SMjAxL3Bvb2xhX3BET05SMjAxX2dlbm9tZS5zYW0gJiYgXAogIG12IG91dHB1dHMvNDBoaXNhdDJfcERPTlIyMDEvcG9vbGFfcERPTlIyMDFfZ2Vub21lLXNvcnRlZC5iYW0gb3V0cHV0cy80MGhpc2F0Ml9wRE9OUjIwMS9wb29sYV9wRE9OUjIwMV9nZW5vbWUuYmFtICYmICBzYW10b29scyBpbmRleCBvdXRwdXRzLzQwaGlzYXQyX3BET05SMjAxL3Bvb2xhX3BET05SMjAxX2dlbm9tZS5iYW0KZWNobyAwCmJhbXRvb2xzIHN0YXRzIC1pbiBvdXRwdXRzLzQwaGlzYXQyX3BET05SMjAxL3Bvb2xhX3BET05SMjAxX2dlbm9tZS5iYW0gMj5vdXRwdXRzLzQwaGlzYXQyX3BET05SMjAxL3Bvb2xhX3BET05SMjAxX2dlbm9tZS5iYW0uc3RhdHMgMT4mMgplY2hvIDAKCiMjIFRoZSBmb2xsb3dpbmcgd2lsbCBmYWlsIGlmIHRoaXMgaXMgc2luZ2xlLWVuZGVkLgpzYW10b29scyB2aWV3IC1iIC1mIDIgLW8gb3V0cHV0cy80MGhpc2F0Ml9wRE9OUjIwMS9wb29sYV9wRE9OUjIwMV9nZW5vbWUtcGFpcmVkLmJhbSBvdXRwdXRzLzQwaGlzYXQyX3BET05SMjAxL3Bvb2xhX3BET05SMjAxX2dlbm9tZS5iYW0gJiYgc2FtdG9vbHMgaW5kZXggb3V0cHV0cy80MGhpc2F0Ml9wRE9OUjIwMS9wb29sYV9wRE9OUjIwMV9nZW5vbWUtcGFpcmVkLmJhbQpiYW10b29scyBzdGF0cyAtaW4gb3V0cHV0cy80MGhpc2F0Ml9wRE9OUjIwMS9wb29sYV9wRE9OUjIwMV9nZW5vbWUtcGFpcmVkLmJhbSAyPm91dHB1dHMvNDBoaXNhdDJfcERPTlIyMDEvcG9vbGFfcERPTlIyMDFfZ2Vub21lLXBhaXJlZC5zdGF0cyAxPiYyCmJhbXRvb2xzIGZpbHRlciAtdGFnIFhNOjAgLWluIG91dHB1dHMvNDBoaXNhdDJfcERPTlIyMDEvcG9vbGFfcERPTlIyMDFfZ2Vub21lLmJhbSAtb3V0IG91dHB1dHMvNDBoaXNhdDJfcERPTlIyMDEvcG9vbGFfcERPTlIyMDFfZ2Vub21lLXNvcnRlZF9ub21pc21hdGNoLmJhbSAmJgogIHNhbXRvb2xzIGluZGV4IG91dHB1dHMvNDBoaXNhdDJfcERPTlIyMDEvcG9vbGFfcERPTlIyMDFfZ2Vub21lLXNvcnRlZF9ub21pc21hdGNoLmJhbQoKIyMgVGhlIGZvbGxvd2luZyBsaW5lcyBnaXZlIHN0YXR1cyBjb2RlcyBhbmQgc29tZSBsb2dnaW5nCmVjaG8gIiMjIEpvYiBzdGF0dXM6ICQ/ICIgPj4gb3V0cHV0cy9sb2cudHh0CmVjaG8gIiMjICQoaG9zdG5hbWUpIEZpbmlzaGVkICR7U0xVUk1fSk9CSUR9IDQwXzJzMmJfaGlzYXQyX3BET05SMjAxLnNoIGF0ICQoZGF0ZSksIGl0IHRvb2sgJCgoIFNFQ09ORFMgLyA2MCApKSBtaW51dGVzLiIgPj4gb3V0cHV0cy9sb2cudHh0CgppZiBbWyAteCAiJChjb21tYW5kIC12IHNzdGF0KSIgJiYgISAteiAiJHtTTFVSTV9KT0JJRH0iIF1dOyB0aGVuCiAgd2FsbHRpbWU9JChzY29udHJvbCBzaG93IGpvYiAiJHtTTFVSTV9KT0JJRH0iIHwgZ3JlcCBSdW5UaW1lIHwgcGVybCAtRicvXHMrfD0vJyAtbGFuZSAne3ByaW50ICRGWzJdfScgMj4vZGV2L251bGwpCiAgZWNobyAiIyMjIyB3YWxsdGltZSB1c2VkIGJ5ICR7U0xVUk1fSk9CSUR9IHdhczogJHt3YWxsdGltZTotbnVsbH0iID4+IG91dHB1dHMvbG9nLnR4dAogIG1heG1lbT0kKHNzdGF0IC0tZm9ybWF0PU1heFZNU2l6ZSAtbiAiJHtTTFVSTV9KT0JJRH0uYmF0Y2giIDI+L2Rldi9udWxsKQogIGVjaG8gIiMjIyMgbWF4aW11bSBtZW1vcnkgdXNlZCBieSAke1NMVVJNX0pPQklEfSB3YXM6ICR7bWF4bWVtOi1udWxsfSIgPj4gb3V0cHV0cy9sb2cudHh0CiAgYXZlY3B1PSQoc3N0YXQgLS1mb3JtYXQ9QXZlQ1BVIC1uICIke1NMVVJNX0pPQklEfS5iYXRjaCIgMj4vZGV2L251bGwpCiAgZWNobyAiIyMjIyBhdmVyYWdlIGNwdSB1c2VkIGJ5ICR7U0xVUk1fSk9CSUR9IHdhczogJHthdmVjcHU6LW51bGx9IiA+PiBvdXRwdXRzL2xvZy50eHQKZmkKIyMgQWRkaW5nIGEgbGl0dGxlIGxvZ2ljIHRvIGhhdmUgc2tpcCBmaW5pc2hlZCBqb2JzLgp0b3VjaCBvdXRwdXRzL2xvZ3MvNDBfMnMyYl9oaXNhdDJfcERPTlIyMDEuZmluaXNoZWQKYGBgCgojIyBNYXBwaW5nIGFnYWluc3QgUEEwMQoKQmVmb3JlIG1hcHBpbmcgYWdhaW5zdCB0aGUgUEEwMSBzdHJhaW4sIEkgcHJldmlvdXNseSBkb3dubG9hZGVkIHRoZQpyZWZlcmVuY2UgYXNzZW1ibHkgZnJvbSBOQ0JJIGFzIGEgY29tcGxldGUgZ2VuYmFuayBmaWxlIGFuZCBwdXQgaXQgaW4KbXkgIiR7TElCRElSfS9nZW5vbWUvIiBkaXJlY3RvcnkgYXMgJ3BhZXJ1Z2lub3NhX3BhMDEuZ2InLgoKSSB0aGVuIHBlcmZvcm1lZCB0aGUgZm9sbG93aW5nIHRvIGNvbnZlcnQgaXQgdG8gZmFzdGEvZ2ZmIGFuZCBpbmRleAppdCAobm90ZSBJIGRpZCB0aGlzIHF1aXRlIGEgd2hpbGUgYWdvLCBJIGFtIGp1c3Qgd3JpdGluZyBpdCBoZXJlIGZvcgpjb21wbGV0ZW5lc3MpLCBhbHNvIGhlcmUgaXMgYW4gZXh0cmEgc2FsbW9uIGluZGV4IG9wZXJhdGlvbiBmb3IgZnVuOgoKYGBge2Jhc2ggY29udmVydF9pbmRleF9wYTAxLCBldmFsPUZBTFNFfQpzdGFydD0kKHB3ZCkKY2Qgfi9saWJyYXJpZXMvZ2Vub21lCmN5b2EgLS10YXNrIGNvbnZlcnQgLS1tZXRob2QgZ2IyZ2ZmIC0taW5wdXQgcGFlcnVnaW5vc2FfcGEwMS5nYgpjeW9hIC0tdGFzayBpbmRleCAtLW1ldGhvZCBpbmRleGhpc2F0IC0taW5wdXQgcGFlcnVnaW5vc2FfcGEwMS5mYXN0YSAtLXNwZWNpZXMgcGFlcnVnaW5vc2FfcGEwMQpjeW9hIC0tdGFzayBpbmRleCAtLW1ldGhvZCBpbmRleHNhbG1vbiAtLWlucHV0IHBhZXJ1Z2lub3NhX3BhMDFfY2RzLmZhc3RhIC0tc3BlY2llcyBwYWVydWdpbm9zYV9wYTAxCmNkICRzdGFydApgYGAKClRoaXMgaXMgbmVhcmx5IGlkZW50aWNhbCB0byB0aGUgcHJldmlvdXMgc3RlcCwgZXhjZXB0IGl0IHVzZXMgdGhlCnVuYWxpZ25lZCByZWFkcyBmcm9tIHRoZSBwcmV2aW91cyBzdGVwLCBhbmQgSSBleHBsaWNpdGx5IHN0YXRlIHRoYXQgSQp3YW50IGh0c2VxIHRvIGNvdW50IHRoZSBnZW5lcyBieSB0aGUgbG9jdXNfdGFnIHRhZ3MuCgpgYGB7YmFzaCBoaXNhdF9wYTAxX3VuYWxpZ25lZCwgZXZhbD1GQUxTRX0KY2QgcHJlcHJvY2Vzc2luZwpzdGFydD0kKHB3ZCkKZm9yIGkgaW4gJCgvYmluL2xzKTsgZG8KICAgIGNkICR7aX0KICAgIGN5b2EgLS10YXNrIG1hcCAtLW1ldGhvZCBoaXNhdCAtLXNwZWNpZXMgcGFlcnVnaW5vc2FfcGEwMSBcCiAgICAgICAgIC0taHRzZXFfdHlwZSBnZW5lIC0taHRzZXFfaWQgbG9jdXNfdGFnIFwKICAgICAgICAgLS1pbnB1dCAkKC9iaW4vbHMgb3V0cHV0cy80MGhpc2F0Ml9wRE9OUjIwMS8qdW5hbGNvbiouZmFzdHEueHogfCB0ciAnXG4nICc6JyB8IHNlZCAncy86JC8vZycpCiAgICBjZCAkc3RhcnQKZG9uZQpgYGAKClRoZSByZXN1bHRpbmcgc2NyaXB0cyBhcmUgbmVhcmx5IGlkZW50aWNhbCB0byB0aG9zZSBmb3IgcERPTlIsIGV4Y2VwdApvZiBjb3Vyc2UgdGhlIHNwZWNpZXMgdXNlZCBhbmQgaW5wdXRzOgoKYGBge2Jhc2ggaGlzYXRfcGFlcnVnaW5vc2EsIGV2YWw9RkFMU0V9CiMhL3Vzci9iaW4vZW52IGJhc2gKI1NCQVRDSCAtLWV4cG9ydD1BTEwKI1NCQVRDSCAtLW1haWwtdHlwZT1OT05FCiNTQkFUQ0ggLS1vcGVuLW1vZGU9YXBwZW5kCiNTQkFUQ0ggLS1jaGRpcj0vZnMvY2JjYi1sYWIvbmVsc2F5ZWQvc2NyYXRjaC9hdGIvZG5hc2VxL3BhZXJ1Z2lub3NhX3BsYXNtaWRzXzIwMjIvcHJlcHJvY2Vzc2luZy9wb29sYQojU0JBVENIIC0tcGFydGl0aW9uPWRwYXJ0CiNTQkFUQ0ggLS1xb3M9d29ya3N0YXRpb24gLS1uaWNlPTEwCiNTQkFUQ0ggLS1ub2Rlcz0xIC0tcmVxdWV1ZQojU0JBVENIIC0tdGltZT0xMDowMDowMAojU0JBVENIIC0tam9iLW5hbWU9NDBoaXNhdDJfcGFlcnVnaW5vc2FfcGEwMV9nZW5vbWUKI1NCQVRDSCAtLW1lbT0yNEcKI1NCQVRDSCAtLWNwdXMtcGVyLXRhc2s9NAojU0JBVENIIC0tb3V0cHV0PW91dHB1dHMvbG9nLnR4dC5zYmF0Y2gKc2V0IC1vIGVycmV4aXQKc2V0IC1vIGVycnRyYWNlCnNldCAtbyBwaXBlZmFpbApleHBvcnQgTEVTUz0nLS1idWZmZXJzIDAnCnNjcmlwdD0iJChwd2QpLyQwIgplcnIoKSB7CiAgICBlY2hvICJFcnJvciBvY2N1cnJlZDoiCiAgICBhd2sgJ05SPkwtNCAmJiBOUjxMKzQgeyBwcmludGYgIiUtNWQlM3Mlc1xuIixOUiwoTlI9PUw/Ij4+PiI6IiIpLCRzY3JpcHQgfScgTD0kMSAkc2NyaXB0Cn0KdHJhcCAnZXJyICRMSU5FTk8nIEVSUgoKZWNobyAiIyMgU3RhcnRlZCAvZnMvY2JjYi1sYWIvbmVsc2F5ZWQvc2NyYXRjaC9hdGIvZG5hc2VxL3BhZXJ1Z2lub3NhX3BsYXNtaWRzXzIwMjIvcHJlcHJvY2Vzc2luZy9wb29sYS9zY3JpcHRzLzQwaGlzYXQyX3BhZXJ1Z2lub3NhX3BhMDFfZ2Vub21lLnNoIGF0ICQoZGF0ZSkgb24gJChob3N0bmFtZSkgd2l0aCBpZCAke1NMVVJNX0pPQklEfS4iID4+IG91dHB1dHMvbG9nLnR4dAoKbW9kdWxlIGFkZCBoaXNhdDIgc2FtdG9vbHMgaHRzZXEgYmFtdG9vbHMKCiMjIFRoaXMgaXMgYSBoaXNhdDIgYWxpZ25tZW50IG9mICAtMSA8KGxlc3MgL2ZzL2NiY2ItbGFiL25lbHNheWVkL3NjcmF0Y2gvYXRiL2RuYXNlcS9wYWVydWdpbm9zYV9wbGFzbWlkc18yMDIyL3ByZXByb2Nlc3NpbmcvcG9vbGEvb3V0cHV0cy80MGhpc2F0Ml9wRE9OUjIwMS9wb29sYV91bmFsY29uX3BET05SMjAxX2dlbm9tZS4xLmZhc3RxLnh6KSAtMiA8KGxlc3MgL2ZzL2NiY2ItbGFiL25lbHNheWVkL3NjcmF0Y2gvYXRiL2RuYXNlcS9wYWVydWdpbm9zYV9wbGFzbWlkc18yMDIyL3ByZXByb2Nlc3NpbmcvcG9vbGEvb3V0cHV0cy80MGhpc2F0Ml9wRE9OUjIwMS9wb29sYV91bmFsY29uX3BET05SMjAxX2dlbm9tZS4yLmZhc3RxLnh6KSAgYWdhaW5zdCAvY2JjYmhvbWVzL2FiZWxldy9saWJyYXJpZXMvZ2Vub21lL2luZGV4ZXMvcGFlcnVnaW5vc2FfcGEwMQoKbWtkaXIgLXAgb3V0cHV0cy80MGhpc2F0Ml9wYWVydWdpbm9zYV9wYTAxCmhpc2F0MiAteCAvY2JjYmhvbWVzL2FiZWxldy9saWJyYXJpZXMvZ2Vub21lL2luZGV4ZXMvcGFlcnVnaW5vc2FfcGEwMSAgXAogIC1wIDQgXAogIC1xICAgLTEgPChsZXNzIC9mcy9jYmNiLWxhYi9uZWxzYXllZC9zY3JhdGNoL2F0Yi9kbmFzZXEvcGFlcnVnaW5vc2FfcGxhc21pZHNfMjAyMi9wcmVwcm9jZXNzaW5nL3Bvb2xhL291dHB1dHMvNDBoaXNhdDJfcERPTlIyMDEvcG9vbGFfdW5hbGNvbl9wRE9OUjIwMV9nZW5vbWUuMS5mYXN0cS54eikgLTIgPChsZXNzIC9mcy9jYmNiLWxhYi9uZWxzYXllZC9zY3JhdGNoL2F0Yi9kbmFzZXEvcGFlcnVnaW5vc2FfcGxhc21pZHNfMjAyMi9wcmVwcm9jZXNzaW5nL3Bvb2xhL291dHB1dHMvNDBoaXNhdDJfcERPTlIyMDEvcG9vbGFfdW5hbGNvbl9wRE9OUjIwMV9nZW5vbWUuMi5mYXN0cS54eikgIFwKICAtLXBocmVkMzMgXAogIC0tdW4gb3V0cHV0cy80MGhpc2F0Ml9wYWVydWdpbm9zYV9wYTAxL3Bvb2xhX3VuYWxkaXNfcGFlcnVnaW5vc2FfcGEwMV9nZW5vbWUuZmFzdHEgXAogIC0tYWwgb3V0cHV0cy80MGhpc2F0Ml9wYWVydWdpbm9zYV9wYTAxL3Bvb2xhX2FsZGlzX3BhZXJ1Z2lub3NhX3BhMDFfZ2Vub21lLmZhc3RxIFwKICAtLXVuLWNvbmMgb3V0cHV0cy80MGhpc2F0Ml9wYWVydWdpbm9zYV9wYTAxL3Bvb2xhX3VuYWxjb25fcGFlcnVnaW5vc2FfcGEwMV9nZW5vbWUuZmFzdHEgXAogIC0tYWwtY29uYyBvdXRwdXRzLzQwaGlzYXQyX3BhZXJ1Z2lub3NhX3BhMDEvcG9vbGFfYWxjb25fcGFlcnVnaW5vc2FfcGEwMV9nZW5vbWUuZmFzdHEgXAogIC1TIG91dHB1dHMvNDBoaXNhdDJfcGFlcnVnaW5vc2FfcGEwMS9wb29sYV9wYWVydWdpbm9zYV9wYTAxX2dlbm9tZS5zYW0gXAogIDI+b3V0cHV0cy80MGhpc2F0Ml9wYWVydWdpbm9zYV9wYTAxL2hpc2F0Ml9wYWVydWdpbm9zYV9wYTAxX2dlbm9tZV9wb29sYS5zdGRlcnIgXAogIDE+b3V0cHV0cy80MGhpc2F0Ml9wYWVydWdpbm9zYV9wYTAxL2hpc2F0Ml9wYWVydWdpbm9zYV9wYTAxX2dlbm9tZV9wb29sYS5zdGRvdXQKCgojIyBUaGUgZm9sbG93aW5nIGxpbmVzIGdpdmUgc3RhdHVzIGNvZGVzIGFuZCBzb21lIGxvZ2dpbmcKZWNobyAiIyMgSm9iIHN0YXR1czogJD8gIiA+PiBvdXRwdXRzL2xvZy50eHQKZWNobyAiIyMgJChob3N0bmFtZSkgRmluaXNoZWQgJHtTTFVSTV9KT0JJRH0gNDBoaXNhdDJfcGFlcnVnaW5vc2FfcGEwMV9nZW5vbWUuc2ggYXQgJChkYXRlKSwgaXQgdG9vayAkKCggU0VDT05EUyAvIDYwICkpIG1pbnV0ZXMuIiA+PiBvdXRwdXRzL2xvZy50eHQKCmlmIFtbIC14ICIkKGNvbW1hbmQgLXYgc3N0YXQpIiAmJiAhIC16ICIke1NMVVJNX0pPQklEfSIgXV07IHRoZW4KICB3YWxsdGltZT0kKHNjb250cm9sIHNob3cgam9iICIke1NMVVJNX0pPQklEfSIgfCBncmVwIFJ1blRpbWUgfCBwZXJsIC1GJy9ccyt8PS8nIC1sYW5lICd7cHJpbnQgJEZbMl19JyAyPi9kZXYvbnVsbCkKICBlY2hvICIjIyMjIHdhbGx0aW1lIHVzZWQgYnkgJHtTTFVSTV9KT0JJRH0gd2FzOiAke3dhbGx0aW1lOi1udWxsfSIgPj4gb3V0cHV0cy9sb2cudHh0CiAgbWF4bWVtPSQoc3N0YXQgLS1mb3JtYXQ9TWF4Vk1TaXplIC1uICIke1NMVVJNX0pPQklEfS5iYXRjaCIgMj4vZGV2L251bGwpCiAgZWNobyAiIyMjIyBtYXhpbXVtIG1lbW9yeSB1c2VkIGJ5ICR7U0xVUk1fSk9CSUR9IHdhczogJHttYXhtZW06LW51bGx9IiA+PiBvdXRwdXRzL2xvZy50eHQKICBhdmVjcHU9JChzc3RhdCAtLWZvcm1hdD1BdmVDUFUgLW4gIiR7U0xVUk1fSk9CSUR9LmJhdGNoIiAyPi9kZXYvbnVsbCkKICBlY2hvICIjIyMjIGF2ZXJhZ2UgY3B1IHVzZWQgYnkgJHtTTFVSTV9KT0JJRH0gd2FzOiAke2F2ZWNwdTotbnVsbH0iID4+IG91dHB1dHMvbG9nLnR4dApmaQojIyBBZGRpbmcgYSBsaXR0bGUgbG9naWMgdG8gaGF2ZSBza2lwIGZpbmlzaGVkIGpvYnMuCnRvdWNoIG91dHB1dHMvbG9ncy80MGhpc2F0Ml9wYWVydWdpbm9zYV9wYTAxX2dlbm9tZS5maW5pc2hlZApgYGAKCkkgd2lsbCBsZWF2ZSB0aGUgbmVhcmx5IGlkZW50aWNhbCBjb252ZXJzaW9uIG9mIHRoZSBzYW0gYWxpZ25tZW50cyB0bwpjb21wcmVzc2VkL3NvcnRlZC9pbmRleGVkIGJhbSBhcyBhbiBleGVyY2lzZSB0byB0aGUgcmVhZGVyLCBidXQgaGVyZQppcyB0aGUgcmVzdWx0aW5nIGh0c2VxIHNjcmlwdDoKCmBgYHtiYXNoIGh0c2VxX2V4YW1wbGUsIGV2YWw9RkFMU0V9CiMhL3Vzci9iaW4vZW52IGJhc2gKI1NCQVRDSCAtLWV4cG9ydD1BTEwKI1NCQVRDSCAtLW1haWwtdHlwZT1OT05FCiNTQkFUQ0ggLS1vcGVuLW1vZGU9YXBwZW5kCiNTQkFUQ0ggLS1jaGRpcj0vZnMvY2JjYi1sYWIvbmVsc2F5ZWQvc2NyYXRjaC9hdGIvZG5hc2VxL3BhZXJ1Z2lub3NhX3BsYXNtaWRzXzIwMjIvcHJlcHJvY2Vzc2luZy9wb29sYQojU0JBVENIIC0tcGFydGl0aW9uPWRwYXJ0CiNTQkFUQ0ggLS1xb3M9dGhyb3VnaHB1dCAtLW5pY2U9MTAKI1NCQVRDSCAtLW5vZGVzPTEgLS1yZXF1ZXVlCiNTQkFUQ0ggLS10aW1lPTEyOjAwOjAwCiNTQkFUQ0ggLS1qb2ItbmFtZT00MF8zaHRzX3Bvb2xhX2FsbF9oaXNhdDJfcGFlcnVnaW5vc2FfcGEwMV9zbm9fZ2VuZV9BbGlhcwojU0JBVENIIC0tbWVtPTZHCiNTQkFUQ0ggLS1jcHVzLXBlci10YXNrPTEKI1NCQVRDSCAtLW91dHB1dD1vdXRwdXRzL2xvZy50eHQuc2JhdGNoCnNldCAtbyBlcnJleGl0CnNldCAtbyBlcnJ0cmFjZQpzZXQgLW8gcGlwZWZhaWwKZXhwb3J0IExFU1M9Jy0tYnVmZmVycyAwJwpzY3JpcHQ9IiQocHdkKS8kMCIKZXJyKCkgewogICAgZWNobyAiRXJyb3Igb2NjdXJyZWQ6IgogICAgYXdrICdOUj5MLTQgJiYgTlI8TCs0IHsgcHJpbnRmICIlLTVkJTNzJXNcbiIsTlIsKE5SPT1MPyI+Pj4iOiIiKSwkc2NyaXB0IH0nIEw9JDEgJHNjcmlwdAp9CnRyYXAgJ2VyciAkTElORU5PJyBFUlIKCmVjaG8gIiMjIFN0YXJ0ZWQgL2ZzL2NiY2ItbGFiL25lbHNheWVkL3NjcmF0Y2gvYXRiL2RuYXNlcS9wYWVydWdpbm9zYV9wbGFzbWlkc18yMDIyL3ByZXByb2Nlc3NpbmcvcG9vbGEvc2NyaXB0cy80MF8zaHRzX3Bvb2xhX2FsbF9oaXNhdDJfcGFlcnVnaW5vc2FfcGEwMV9zbm9fZ2VuZV9BbGlhcy5zaCBhdCAkKGRhdGUpIG9uICQoaG9zdG5hbWUpIHdpdGggaWQgJHtTTFVSTV9KT0JJRH0uIiA+PiBvdXRwdXRzL2xvZy50eHQKCm1vZHVsZSBhZGQgaHRzZXEKCiMjIENvdW50aW5nIHRoZSBudW1iZXIgb2YgaGl0cyBpbiBvdXRwdXRzLzQwaGlzYXQyX3BhZXJ1Z2lub3NhX3BhMDEvcG9vbGFfcGFlcnVnaW5vc2FfcGEwMV9nZW5vbWUtcGFpcmVkLmJhbSBmb3IgZWFjaCBmZWF0dXJlIGZvdW5kIGluIC9jYmNiaG9tZXMvYWJlbGV3L2xpYnJhcmllcy9nZW5vbWUvcGFlcnVnaW5vc2FfcGEwMS5nZmYKaHRzZXEtY291bnQgIC0taGVscCAyPiYxIHwgdGFpbCAtbiAzCmh0c2VxLWNvdW50IFwKICAtcSAtZiBiYW0gLXMgbm8gIC0tdHlwZSBnZW5lICAtLWlkYXR0ciBsb2N1c190YWcgXAogIG91dHB1dHMvNDBoaXNhdDJfcGFlcnVnaW5vc2FfcGEwMS9wb29sYV9wYWVydWdpbm9zYV9wYTAxX2dlbm9tZS1wYWlyZWQuYmFtIFwKICAvY2JjYmhvbWVzL2FiZWxldy9saWJyYXJpZXNfZnMvZ2Vub21lL3BhZXJ1Z2lub3NhX3BhMDEuZ2ZmIFwKICAyPm91dHB1dHMvNDBoaXNhdDJfcGFlcnVnaW5vc2FfcGEwMS9wb29sYV9wYWVydWdpbm9zYV9wYTAxX2dlbm9tZS1wYWlyZWRfYWxsX3Nub19nZW5lX0FsaWFzLnN0ZGVyciBcCiAgMT5vdXRwdXRzLzQwaGlzYXQyX3BhZXJ1Z2lub3NhX3BhMDEvcG9vbGFfcGFlcnVnaW5vc2FfcGEwMV9nZW5vbWUtcGFpcmVkX2FsbF9zbm9fZ2VuZV9BbGlhcy5jb3VudCAmJiBcCiAgICB4eiAtZiAtOWUgb3V0cHV0cy80MGhpc2F0Ml9wYWVydWdpbm9zYV9wYTAxL3Bvb2xhX3BhZXJ1Z2lub3NhX3BhMDFfZ2Vub21lLXBhaXJlZF9hbGxfc25vX2dlbmVfQWxpYXMuY291bnQgMj5vdXRwdXRzLzQwaGlzYXQyX3BhZXJ1Z2lub3NhX3BhMDEvcG9vbGFfcGFlcnVnaW5vc2FfcGEwMV9nZW5vbWUtcGFpcmVkX2FsbF9zbm9fZ2VuZV9BbGlhcy5zdGRlcnIueHogMT5vdXRwdXRzLzQwaGlzYXQyX3BhZXJ1Z2lub3NhX3BhMDEvcG9vbGFfcGFlcnVnaW5vc2FfcGEwMV9nZW5vbWUtcGFpcmVkX2FsbF9zbm9fZ2VuZV9BbGlhcy5jb3VudC54egoKCiMjIFRoZSBmb2xsb3dpbmcgbGluZXMgZ2l2ZSBzdGF0dXMgY29kZXMgYW5kIHNvbWUgbG9nZ2luZwplY2hvICIjIyBKb2Igc3RhdHVzOiAkPyAiID4+IG91dHB1dHMvbG9nLnR4dAplY2hvICIjIyAkKGhvc3RuYW1lKSBGaW5pc2hlZCAke1NMVVJNX0pPQklEfSA0MF8zaHRzX3Bvb2xhX2FsbF9oaXNhdDJfcGFlcnVnaW5vc2FfcGEwMV9zbm9fZ2VuZV9BbGlhcy5zaCBhdCAkKGRhdGUpLCBpdCB0b29rICQoKCBTRUNPTkRTIC8gNjAgKSkgbWludXRlcy4iID4+IG91dHB1dHMvbG9nLnR4dAoKaWYgW1sgLXggIiQoY29tbWFuZCAtdiBzc3RhdCkiICYmICEgLXogIiR7U0xVUk1fSk9CSUR9IiBdXTsgdGhlbgogIHdhbGx0aW1lPSQoc2NvbnRyb2wgc2hvdyBqb2IgIiR7U0xVUk1fSk9CSUR9IiB8IGdyZXAgUnVuVGltZSB8IHBlcmwgLUYnL1xzK3w9LycgLWxhbmUgJ3twcmludCAkRlsyXX0nIDI+L2Rldi9udWxsKQogIGVjaG8gIiMjIyMgd2FsbHRpbWUgdXNlZCBieSAke1NMVVJNX0pPQklEfSB3YXM6ICR7d2FsbHRpbWU6LW51bGx9IiA+PiBvdXRwdXRzL2xvZy50eHQKICBtYXhtZW09JChzc3RhdCAtLWZvcm1hdD1NYXhWTVNpemUgLW4gIiR7U0xVUk1fSk9CSUR9LmJhdGNoIiAyPi9kZXYvbnVsbCkKICBlY2hvICIjIyMjIG1heGltdW0gbWVtb3J5IHVzZWQgYnkgJHtTTFVSTV9KT0JJRH0gd2FzOiAke21heG1lbTotbnVsbH0iID4+IG91dHB1dHMvbG9nLnR4dAogIGF2ZWNwdT0kKHNzdGF0IC0tZm9ybWF0PUF2ZUNQVSAtbiAiJHtTTFVSTV9KT0JJRH0uYmF0Y2giIDI+L2Rldi9udWxsKQogIGVjaG8gIiMjIyMgYXZlcmFnZSBjcHUgdXNlZCBieSAke1NMVVJNX0pPQklEfSB3YXM6ICR7YXZlY3B1Oi1udWxsfSIgPj4gb3V0cHV0cy9sb2cudHh0CmZpCiMjIEFkZGluZyBhIGxpdHRsZSBsb2dpYyB0byBoYXZlIHNraXAgZmluaXNoZWQgam9icy4KdG91Y2ggb3V0cHV0cy9sb2dzLzQwXzNodHNfcG9vbGFfYWxsX2hpc2F0Ml9wYWVydWdpbm9zYV9wYTAxX3Nub19nZW5lX2xvY3VzX3RhZy5maW5pc2hlZApgYGAKCiMgRGVkdXBsaWNhdGlvbgoKTm91cidzIHN1Z2dlc3Rpb24gcmVzdWx0ZWQgaW4gdGhlIGZvbGxvd2luZyBhZGRpdGlvbiAod2hpY2ggaGFzIHNpbmNlCmJlZW4gYWRkZWQgYXMgYSBwcmVjdXJzb3IgdG8gbXkgZnJlZWJheWVzL21waWxldXAgaW52b2NhdGlvbik6CgpgYGB7YmFzaCBmcmVlYmF5ZXMxLCBldmFsPUZBTFNFfQpta2RpciAtcCAke2ZyZWViYXllc19kaXJ9CmdhdGsgTWFya0R1cGxpY2F0ZXMgXFwKICAtSSAkb3B0aW9ucy0+e2lucHV0fSBcXAogIC1PICR7ZGVkdXBsaWNhdGVkfSBcXAogIC1NICR7bWFya2VkfSAtLVJFTU9WRV9EVVBMSUNBVEVTIHRydWUgLS1DT01QUkVTU0lPTl9MRVZFTCA5IFxcCiAgMj4ke2dhdGtfc3Rkb3V0fSBcXAogIDE+JHtnYXRrX3N0ZGVycn0Kc2FtdG9vbHMgaW5kZXggJHtkZWR1cGxpY2F0ZWR9CmBgYAoKIyBWYXJpYW50IHNlYXJjaGluZwoKSSBoYXZlIHR3byBtZXRob2RzIG9mIHZhcmlhbnQgc2VhcmNoaW5nIGluIG15IHJlcGV0b2lyZSwgbXBpbGV1cCBhbmQKZnJlZWJheWVzLiAgSSBvbmx5IGxlYXJuZWQgZnJlZWJheWVzIHJlY2VudGx5IGFuZCBzbyBjaG9zZSBpdC4gIE15CmN1cnJlbnQgaW1wbGVtZW50YXRpb24gYWxzbyBzcGlucyBvZmYgYSBzZXJpZXMgb2Ygc2NyaXB0cyB0byBwYXJzZSB0aGUKZnJlZWJheWVzIG91dHB1dCBkb3duIGludG8gYSBzaW1wbGVyIHRzdiBmb3JtYXQuICBBbHNvLCB1bmxpa2UgdGhlCm1hcHBpbmcgc2NyaXB0IGFib3ZlLCB0aGUgZnJlZWJheWVzIGludm9jYXRpb24gaXRzZWxmIGluY2x1ZGVzIHRoZQpjb252ZXJzaW9uIG9mIHRoZSB0ZXh0IHZjZiBmb3JtYXQgdG8gYSBzb3J0ZWQvY29tcHJlc3NlZC9pbmRleCBiY2YKZm9ybWF0LgoKTm90ZSBhbHNvIHRoYXQgdGhlIHByZXZpb3VzIHN0ZXAgYWxzbyBjcmVhdGVzIDMgYmFtIGZpbGVzOgoKMS4gICR7c2FtcGxlfV8ke3NwZWNpZXN9X2dlbm9tZS5iYW06IEFsbCBhbGlnbm1lbnRzCjIuICAke3NhbXBsZX1fJHtzcGVjaWVzfV9nZW5vbWUtcGFpcmVkLmJhbTogQWxsIHByb3Blcmx5IHBhaXJlZAogICAgYWxpZ25tZW50cywgdGhpcyBpcyB0aGUgZGVmYXVsdCB1c2VkIGJ5IGh0c2VxIHdoZW4gYXZhaWxhYmxlLgozLiAgJHtzYW1wbGV9XyR7c3BlY2llc31fZ2Vub21lLXNvcnRlZF9ub21pc21hdGNoLmJhbTogVGhpcyBpcwogICAgZmlsdGVyZWQgZm9yIHRoZSBzZXQgb2YgYWxpZ25tZW50cyB3aXRoIG5vIG1pc21hdGNoZXMuCgpTaW5jZSBydW5uaW5nIHRoZXNlIHNhbXBsZXMsIEkgYWRkZWQgYW5vdGhlciBzdWZmaXggdG8gdGhlIGhpc2F0Cm91dHB1dCBkaXJlY3RvcmllcywgZGVwZW5kaW5nIG9uIHdoZXRoZXIgdGhlIC1rIHBhcmFtZXRlciBpcyB1c2VkIHRvCmxpbWl0IHRoZSBudW1iZXIgb2YgYWxpZ25tZW50cyByZXBvcnRlZC4gIFRoYXQgaXMgbm90IHBhcnRpY3VsYXJseQpyZWxldmFudCBoZXJlLCBidXQgd29ydGggbm90aW5nLgoKYGBge2Jhc2ggZnJlZWJheWVzMiwgZXZhbD1GQUxTRX0KY2QgcHJlcHJvY2Vzc2luZwpzdGFydD0kKHB3ZCkKZm9yIGkgaW4gJCgvYmluL2xzKTsgZG8KICAgIGNkICR7aX0KICAgIGN5b2EgLS10YXNrIHNucCAtLW1ldGhvZCBmcmVlYmF5ZXMgLS1zcGVjaWVzIHBhZXJ1Z2lub3NhX3BhMDEgXAogICAgICAgICAtLWlucHV0ICQoL2Jpbi9scyBvdXRwdXRzLzQwaGlzYXQyX3BhZXJ1Z2lub3NhX3BhMDEvKl9nZW5vbWUuYmFtKQogICAgY2QgJHN0YXJ0CmRvbmUKYGBgCgojIyMgU2luZ2xlIGZyZWViYXllcyBpbnZvY2F0aW9uCgpgYGB7YmFzaCBmcmVlYmF5ZXNfc2NyaXB0LCBldmFsPUZBTFNFfQojIS91c3IvYmluL2VudiBiYXNoCiNTQkFUQ0ggLS1leHBvcnQ9QUxMCiNTQkFUQ0ggLS1tYWlsLXR5cGU9Tk9ORQojU0JBVENIIC0tb3Blbi1tb2RlPWFwcGVuZAojU0JBVENIIC0tY2hkaXI9L21udC9jYmNiL2ZzMDFfYWJlbGV3L2NiY2ItbGFiL25lbHNheWVkL3NjcmF0Y2gvYXRiL2RuYXNlcS9wYWVydWdpbm9zYV9wbGFzbWlkc18yMDIyL3ByZXByb2Nlc3NpbmcvcG9vbGEKI1NCQVRDSCAtLXBhcnRpdGlvbj1kcGFydAojU0JBVENIIC0tcW9zPXdvcmtzdGF0aW9uIC0tbmljZT0xMAojU0JBVENIIC0tbm9kZXM9MSAtLXJlcXVldWUKI1NCQVRDSCAtLXRpbWU9MTA6MDA6MDAKI1NCQVRDSCAtLWpvYi1uYW1lPTQwZnJlZWJheWVzX3BhZXJ1Z2lub3NhX3BhMDEKI1NCQVRDSCAtLW1lbT00OEcKI1NCQVRDSCAtLWNwdXMtcGVyLXRhc2s9NAojU0JBVENIIC0tb3V0cHV0PW91dHB1dHMvbG9nLnR4dC5zYmF0Y2gKc2V0IC1vIGVycmV4aXQKc2V0IC1vIGVycnRyYWNlCnNldCAtbyBwaXBlZmFpbApleHBvcnQgTEVTUz0nLS1idWZmZXJzIDAnCnNjcmlwdD0iJChwd2QpLyQwIgplcnIoKSB7CiAgICBlY2hvICJFcnJvciBvY2N1cnJlZDoiCiAgICBhd2sgJ05SPkwtNCAmJiBOUjxMKzQgeyBwcmludGYgIiUtNWQlM3Mlc1xuIixOUiwoTlI9PUw/Ij4+PiI6IiIpLCRzY3JpcHQgfScgTD0kMSAkc2NyaXB0Cn0KdHJhcCAnZXJyICRMSU5FTk8nIEVSUgoKZWNobyAiIyMgU3RhcnRlZCAvbW50L2NiY2IvZnMwMV9hYmVsZXcvY2JjYi1sYWIvbmVsc2F5ZWQvc2NyYXRjaC9hdGIvZG5hc2VxL3BhZXJ1Z2lub3NhX3BsYXNtaWRzXzIwMjIvcHJlcHJvY2Vzc2luZy9wb29sYS9zY3JpcHRzLzQwZnJlZWJheWVzX3BhZXJ1Z2lub3NhX3BhMDEuc2ggYXQgJChkYXRlKSBvbiAkKGhvc3RuYW1lKSB3aXRoIGlkICR7U0xVUk1fSk9CSUR9LiIgPj4gb3V0cHV0cy9sb2cudHh0Cgptb2R1bGUgYWRkIGZyZWViYXllcyBsaWJnc2wvMi43LjEgbGliaHRzLzEuMTMgc2FtdG9vbHMvMS4xMyBiY2Z0b29scyB2Y2Z0b29scwoKIyMgVXNlIGZyZWViYXllcywgYmNmdG9vbHMsIGFuZCB2Y2Z1dGlscyB0byBnZXQgc29tZSBpZGVhIGFib3V0IGhvdyBtYW55IHZhcmlhbnQgcG9zaXRpb25zIGFyZSBpbiB0aGUgZGF0YS4KCm1rZGlyIC1wIG91dHB1dHMvNDBmcmVlYmF5ZXNfcGFlcnVnaW5vc2FfcGEwMQpmcmVlYmF5ZXMgLWYgL2hvbWUvdHJleS9saWJyYXJpZXNfZnMvZ2Vub21lL3BhZXJ1Z2lub3NhX3BhMDEuZmFzdGEgXAogIC12IG91dHB1dHMvNDBmcmVlYmF5ZXNfcGFlcnVnaW5vc2FfcGEwMS9wYWVydWdpbm9zYV9wYTAxLnZjZiBcCiAgb3V0cHV0cy80MGhpc2F0Ml9wYWVydWdpbm9zYV9wYTAxL3Bvb2xhX3BhZXJ1Z2lub3NhX3BhMDFfZ2Vub21lLmJhbSBcCiAgMT5vdXRwdXRzLzQwZnJlZWJheWVzX3BhZXJ1Z2lub3NhX3BhMDEvcGFlcnVnaW5vc2FfcGEwMS5zdGRvdXQgXAogIDI+b3V0cHV0cy80MGZyZWViYXllc19wYWVydWdpbm9zYV9wYTAxL3BhZXJ1Z2lub3NhX3BhMDEuc3RkZXJyCmJjZnRvb2xzIGNvbnZlcnQgb3V0cHV0cy80MGZyZWViYXllc19wYWVydWdpbm9zYV9wYTAxL3BhZXJ1Z2lub3NhX3BhMDEudmNmIFwKICAtT2IgLW8gb3V0cHV0cy80MGZyZWViYXllc19wYWVydWdpbm9zYV9wYTAxL3BhZXJ1Z2lub3NhX3BhMDEuYmNmIFwKICAyPj5vdXRwdXRzLzQwZnJlZWJheWVzX3BhZXJ1Z2lub3NhX3BhMDEvcGFlcnVnaW5vc2FfcGEwMS5zdGRlcnIgXAogIDE+b3V0cHV0cy80MGZyZWViYXllc19wYWVydWdpbm9zYV9wYTAxL3BhZXJ1Z2lub3NhX3BhMDEuc3Rkb3V0CmJjZnRvb2xzIGluZGV4IG91dHB1dHMvNDBmcmVlYmF5ZXNfcGFlcnVnaW5vc2FfcGEwMS9wYWVydWdpbm9zYV9wYTAxLmJjZiBcCiAgMj4+b3V0cHV0cy80MGZyZWViYXllc19wYWVydWdpbm9zYV9wYTAxL3BhZXJ1Z2lub3NhX3BhMDEuc3RkZXJyIFwKICAxPj5vdXRwdXRzLzQwZnJlZWJheWVzX3BhZXJ1Z2lub3NhX3BhMDEvcGFlcnVnaW5vc2FfcGEwMS5zdGRvdXQKcm0gb3V0cHV0cy80MGZyZWViYXllc19wYWVydWdpbm9zYV9wYTAxL3BhZXJ1Z2lub3NhX3BhMDEudmNmCgoKIyMgVGhlIGZvbGxvd2luZyBsaW5lcyBnaXZlIHN0YXR1cyBjb2RlcyBhbmQgc29tZSBsb2dnaW5nCmVjaG8gIkpvYiBzdGF0dXM6ICQ/ICIgPj4gb3V0cHV0cy9sb2cudHh0CmVjaG8gIiAgJChob3N0bmFtZSkgRmluaXNoZWQgJHtTTFVSTV9KT0JJRH0gNDBmcmVlYmF5ZXNfcGFlcnVnaW5vc2FfcGEwMS5zaCBhdCAkKGRhdGUpLCBpdCB0b29rICQoKCBTRUNPTkRTIC8gNjAgKSkgbWludXRlcy4iID4+IG91dHB1dHMvbG9nLnR4dAoKaWYgW1sgLXggIiQoY29tbWFuZCAtdiBzc3RhdCkiICYmICEgLXogIiR7U0xVUk1fSk9CSUR9IiBdXTsgdGhlbgogIHdhbGx0aW1lPSQoc2NvbnRyb2wgc2hvdyBqb2IgIiR7U0xVUk1fSk9CSUR9IiB8IGdyZXAgUnVuVGltZSB8IHBlcmwgLUYnL1xzK3w9LycgLWxhbmUgJ3twcmludCAkRlsyXX0nIHwgaGVhZCAtbiAxIDI+L2Rldi9udWxsKQogIGVjaG8gIiAgd2FsbHRpbWUgdXNlZCBieSAke1NMVVJNX0pPQklEfSB3YXM6ICR7d2FsbHRpbWU6LW51bGx9IiA+PiBvdXRwdXRzL2xvZy50eHQKICBtYXhtZW09JChzc3RhdCAtLWZvcm1hdD1NYXhWTVNpemUgLW4gIiR7U0xVUk1fSk9CSUR9LmJhdGNoIiAyPi9kZXYvbnVsbCkKICBlY2hvICIgIG1heGltdW0gbWVtb3J5IHVzZWQgYnkgJHtTTFVSTV9KT0JJRH0gd2FzOiAke21heG1lbTotbnVsbH0iID4+IG91dHB1dHMvbG9nLnR4dAogIGVjaG8gIiIgPj4gb3V0cHV0cy9sb2cudHh0CmZpCiMjIEFkZGluZyBhIGxpdHRsZSBsb2dpYyB0byBoYXZlIHNraXAgZmluaXNoZWQgam9icy4KdG91Y2ggb3V0cHV0cy9sb2dzLzQwZnJlZWJheWVzX3BhZXJ1Z2lub3NhX3BhMDEuZmluaXNoZWQKYGBgCgpVcG9uIGNvbXBsZXRpb24gd2UgaGF2ZSBhIHNlcmllcyBvZiBmaWxlcyB3aGljaCBpbiB0aGVvcnkgc2hvdWxkCnByb3ZpZGUgZXZlcnl0aGluZyBvZiBpbnRlcmVzdCB0byBWaW5jZS4gIExldHMgcG9rZSBhdCB0aGVtIGluIFIhCgojIFZpbmNlJ3MgUmVxdWVzdHMKCkhlcmUgaXMgbXkgZW1haWwgZnJvbSBWaW5jZSB3aXRoIGhpcyBpbml0aWFsLCBwYXJlZCBkb3duIHF1ZXJpZXM6CgoiCkJhc2VkIG9uIHRoZSBkYXRhIHlvdSBoYXZlIGFuYWx5emVkLCBjYW4geW91IHBsZWFzZSBzaGFyZSB3aXRoIG1lIGEKRXhjZWwgc3ByZWFkc2hlZXQgKG9yIENTViBmaWxlKSB3aXRoIDIgY29sdW1ucyBmb3IgZWFjaCBsaWJyYXJ5IGFuZAplYWNoIFBBIGdlbmU6CgoxLiBDb3ZlcmFnZSAoUlBLTSkgb3IgMCBmb3Igbm8gcmVhZHMKMi4gTnVtYmVyIG9mIFNOUHMKIgoKIyMgQW5ub3RhdGlvbgoKSSBhbHdheXMgZ3JhYiBteSBmYXZvcml0ZSBhbm5vdGF0aW9uIHNvdXJjZXMsIGV2ZW4gaWYgSSB3aWxsIG5vdApyZWFsbHkgdXNlIHRoZW0uICBJIHVzdWFsbHkgY29weSB0aGUgZ2ZmIGFuZCBmYXN0YSBmb3IgdGhlIHZlcnNpb24gb2YKdGhlIGdlbm9tZSBJIHVzZWQgdG8gdGhlIGxvY2FsIGRpcmVjdG9yeSwgYnV0IEkgZGlkIG5vdCBib3RoZXIgZm9yIHRoaXMuCgpgYGB7ciBhbm5vdGF0aW9ufQpwYTAxX2dmZiA8LSBsb2FkX2dmZl9hbm5vdGF0aW9ucygifi9saWJyYXJpZXNfZnMvZ2Vub21lL3BhZXJ1Z2lub3NhX3BhMDEuZ2ZmIiwgaWRfY29sPSJsb2N1c190YWciKQpyb3duYW1lcyhwYTAxX2dmZikgPC0gcGEwMV9nZmZbWyJsb2N1c190YWciXV0KYGBgCgojIyBFeHByZXNzaW9uc2V0CgpMZXQgdXMgbm93IHB1bGwgb3V0IHRoZSBodHNlcSBjb3VudCB0YWJsZXMgaW4gb3JkZXIgdG8gbWFrZSBhbiBycGttCnRhYmxlLiAgVGhpcyB1c2VzIHRoZSBtZXRhZGF0YSBzYW1wbGUgc2hlZXQgSSBjcmVhdGVkIGluIHRoZQpzYW1wbGVfc2hlZXRzLyBkaXJlY3RvcnkuCgpJIGJvbGRlZCB0aGUgdHdvIG1vc3QgaW1wb3J0YW50IGNvbHVtbnMgZm9yIHRoZSBwdXJwb3NlcyBvZiB0aGVzZSB0YXNrcy4KCmBgYHtyIGV4cHJzfQpwYTAxX2V4cHQgPC0gY3JlYXRlX2V4cHQoInNhbXBsZV9zaGVldHMvYWxsX3NhbXBsZXMueGxzeCIsIGdlbmVfaW5mbz1wYTAxX2dmZikKcGEwMV9ycGttIDwtIG5vcm1hbGl6ZV9leHB0KHBhMDFfZXhwdCwgY29udmVydD0icnBrbSIpCnBhMDFfY3BtIDwtIG5vcm1hbGl6ZV9leHB0KHBhMDFfZXhwdCwgY29udmVydD0iY3BtIikKCiMjIFBsb3QgYSBsb2ctc2NhbGUgZGVuc2l0eSBvZiBycGttIHZhbHVlcy4KcGxvdF9ib3hwbG90KHBhMDFfZXhwdCkKcGxvdF9kZW5zaXR5KG5vcm1hbGl6ZV9leHB0KHBhMDFfcnBrbSwgdHJhbnNmb3JtPSJsb2cyIiwgZmlsdGVyPVRSVUUpKQpwbG90X25vbnplcm8ocGEwMV9leHB0KSRwbG90CgpycGttX2RmIDwtIGFzLmRhdGEuZnJhbWUoZXhwcnMocGEwMV9ycGttKSkgJT4lCiAgcm91bmQoMikKd2FudGVkX2Fubm90YXRpb25zIDwtIGZEYXRhKHBhMDFfZXhwdClbLCBjKCJnZW5lIiwgImxvY3VzX3RhZyIpXQp3YW50ZWRfZGF0YSA8LSBtZXJnZSh3YW50ZWRfYW5ub3RhdGlvbnMsIHJwa21fZGYsCiAgICAgICAgICAgICAgICAgICAgIGJ5PSJyb3cubmFtZXMiKQpyb3duYW1lcyh3YW50ZWRfZGF0YSkgPC0gd2FudGVkX2RhdGFbWyJSb3cubmFtZXMiXV0Kd2FudGVkX2RhdGFbWyJSb3cubmFtZXMiXV0gPC0gTlVMTApgYGAKCiMgR2F0aGVyaW5nIG11dGF0aW9ucyBudW1iZXJzIGJ5IGdlbmUKCkkgZG8gbm90IGhhdmUgYW4gaW5zdGFudCBmdW5jdGlvbiBmb3IgdGhpcyB0YXNrLCBidXQgaXQgc2hvdWxkIG5vdApwcm92ZSBkaWZmaWN1bHQuCgpgYGB7ciBnYXRoZXJfbXV0YXRpb25zfQpsaWJyYXJ5KGRwbHlyKQp3YW50ZWRfdjIgPC0gd2FudGVkX2RhdGEKCmZvciAoc2FtcGxlIGluIHJvd25hbWVzKHBEYXRhKHBhMDFfZXhwdCkpKSB7CiAgZnVsbF9maWxlIDwtIGdsdWU6OmdsdWUoInByZXByb2Nlc3Npbmcve3NhbXBsZX0vb3V0cHV0cy81MGZyZWViYXllc19wYWVydWdpbm9zYV9wYTAxL2FsbF90YWdzLnR4dC54eiIpCiAgaW5wdXRfZmlsZSA8LSBnbHVlOjpnbHVlKCJwcmVwcm9jZXNzaW5nL3tzYW1wbGV9L291dHB1dHMvNTBmcmVlYmF5ZXNfcGFlcnVnaW5vc2FfcGEwMS92YXJpYW50c19ieV9nZW5lLnR4dC54eiIpCiAgaWYgKCFmaWxlLmV4aXN0cyhmdWxsX2ZpbGUpKSB7CiAgICBuZXh0CiAgfQogIGlucHV0X2RhdGEgPC0gcmVhZHI6OnJlYWRfdHN2KAogICAgICAgICAgICAgICAgICAgICAgICAgICBpbnB1dF9maWxlLCBzaG93X2NvbF90eXBlcz1GQUxTRSwgc2tpcD0xLAogICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xfbmFtZXM9YygiZ2VuZSIsICJjaHIiLCAicG9zaXRpb24iLCAibnRfc3RyaW5nIiwgImFhX3N0cmluZyIpKSAlPiUKICAgIGFzLmRhdGEuZnJhbWUoKQogIGlucHV0X2RhdGFbWyJmcm9tIl1dIDwtIGdzdWIoeD1pbnB1dF9kYXRhW1sibnRfc3RyaW5nIl1dLCBwYXR0ZXJuPSIoXFx3KylfKFxcdyspIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcGxhY2VtZW50PSJcXDEiLCBwZXJsPVRSVUUpCiAgaW5wdXRfZGF0YVtbInRvIl1dIDwtIGdzdWIoeD1pbnB1dF9kYXRhW1sibnRfc3RyaW5nIl1dLCBwYXR0ZXJuPSIoXFx3KylfKFxcdyspIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXBsYWNlbWVudD0iXFwyIiwgcGVybD1UUlVFKQogIGlucHV0X2RhdGFbWyJlbmNvZGVkIl1dIDwtIHBhc3RlMCgiY2hyXyIsIGlucHV0X2RhdGFbWyJjaHIiXV0sICJfcG9zXyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlucHV0X2RhdGFbWyJwb3NpdGlvbiJdXSwgIl9yZWZfIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW5wdXRfZGF0YVtbImZyb20iXV0sICJfYWx0XyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlucHV0X2RhdGFbWyJ0byJdXSkKICBwZW5ldHJhbmNlIDwtIHJlYWRyOjpyZWFkX3RzdihmdWxsX2ZpbGUsIHNob3dfY29sX3R5cGVzPUZBTFNFKSAlPiUKICAgIHNlbGVjdChjKCJwb3NpdGlvbiIsICJQQUlSRUQiKSkKICBpbnB1dF9kYXRhIDwtIG1lcmdlKGlucHV0X2RhdGEsIHBlbmV0cmFuY2UsIGJ5Lng9ImVuY29kZWQiLCBieS55PSJwb3NpdGlvbiIsIGFsbC54PVRSVUUpCiAgaW5wdXRfY29sdW1uIDwtIGlucHV0X2RhdGEgJT4lCiAgICBkcGx5cjo6Z3JvdXBfYnkoZ2VuZSkgJT4lCiAgICBkcGx5cjo6c3VtbWFyaXplKG49bigpKQogIGNvbG5hbWVzKGlucHV0X2NvbHVtbikgPC0gYygiZ2VuZSIsIHBhc3RlMChzYW1wbGUsICJfbiIpKQogIHBlbmV0cmFuY2VfY29sdW1uIDwtIGlucHV0X2RhdGEgJT4lCiAgICBncm91cF9ieShnZW5lKSAlPiUKICAgIGRwbHlyOjptdXRhdGUoYWFfcGVuZXRyYW5jZT1wYXN0ZTAoYWFfc3RyaW5nLCAnXycsIFBBSVJFRCwgY29sbGFwc2U9IiwiKSkgJT4lCiAgICBzZWxlY3QoZ2VuZSwgYWFfcGVuZXRyYW5jZSkgJT4lCiAgICBkaXN0aW5jdCgpCiAgY29sbmFtZXMocGVuZXRyYW5jZV9jb2x1bW4pIDwtIGMoImdlbmUiLCBwYXN0ZTAoc2FtcGxlLCAiX2FhX3BlbmV0cmFuY2UiKSkKICBpbnB1dF9jb2x1bW5zIDwtIG1lcmdlKGlucHV0X2NvbHVtbiwgcGVuZXRyYW5jZV9jb2x1bW4sIGJ5PSJnZW5lIikKICB3YW50ZWRfdjIgPC0gbWVyZ2Uod2FudGVkX3YyLCBpbnB1dF9jb2x1bW5zLCBieS54PSJyb3cubmFtZXMiLCBieS55PSJnZW5lIiwgYWxsLng9VFJVRSkKICByb3duYW1lcyh3YW50ZWRfdjIpIDwtIHdhbnRlZF92MltbIlJvdy5uYW1lcyJdXQogIHdhbnRlZF92MltbIlJvdy5uYW1lcyJdXSA8LSBOVUxMCn0KCiMjIFdyaXRlIGl0IG91dAp3cml0dGVuIDwtIHdyaXRlX3hsc3goZGF0YT13YW50ZWRfdjIsIGV4Y2VsPSJleGNlbC9ycGttX211dGF0aW9uc19wZXJfZ2VuZV9kZWR1cC54bHN4IikKYGBgCgojIFRyeSB2aXN1YWxpemluZyBzb21lIG9mIHRoaXMgaW5mb3JtYXRpb24KCmBgYHtyIHZpc3VhbGl6ZV92YXJpYW50c30KcGFfc25wX2V4cHQgPC0gY291bnRfZXhwdF9zbnBzKHBhMDFfZXhwdCwgYW5ub3RfY29sdW1uPSJmcmVlYmF5ZXN0YWJsZSIsIHNucF9jb2x1bW49IlBBSVJFRCIpCnBsb3RfbGlic2l6ZShwYV9zbnBfZXhwdCkkcGxvdAoKdGVzdCA8LSBwbG90X2NvcmhlYXQocGFfc25wX2V4cHQpCgpgYGAKCgpgYGB7ciBzYXZlbWUsIGV2YWw9RkFMU0V9CnBhbmRlcjo6cGFuZGVyKHNlc3Npb25JbmZvKCkpCm1lc3NhZ2UocGFzdGUwKCJUaGlzIGlzIGhwZ2x0b29scyBjb21taXQ6ICIsIGdldF9naXRfY29tbWl0KCkpKQp0aGlzX3NhdmUgPC0gcGFzdGUwKGdzdWIocGF0dGVybj0iXFwuUm1kIiwgcmVwbGFjZT0iIiwgeD1ybWRfZmlsZSksICItdiIsIHZlciwgIi5yZGEueHoiKQptZXNzYWdlKHBhc3RlMCgiU2F2aW5nIHRvICIsIHRoaXNfc2F2ZSkpCnRtcCA8LSBzbShzYXZlbWUoZmlsZW5hbWU9dGhpc19zYXZlKSkKYGBgCg==