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.
Preprocessing
The preprocessing is mostly a normal DNA sequencing pipeline:
- Trim the reads.
- Map them against the plasmid, pDONR201.
- Map the unmapped reads from #2 against PA01.
- Count the reads by locus_tag to identify missing genes.
- Use freebayes to identify variant positions.
- Parse the freebayes output down to an easy to interpret format.
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.
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.
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
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
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
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
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}
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:
- \({sample}_\){species}_genome.bam: All alignments
- \({sample}_\){species}_genome-paired.bam: All properly paired alignments, this is the default used by htseq when available.
- \({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
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!
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:
- Coverage (RPKM) or 0 for no reads
- Number of SNPs "
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"]]
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
Gathering mutations numbers by gene
I do not have an instant function for this task, but it should not prove difficult.
##
## 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")
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==