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.

2.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.

2.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

2.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

2.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

2.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

3 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

3.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!

4 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 "

4.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"]]

4.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 7 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")

## 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

5 Gathering mutations numbers by gene

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

wanted_v2 <- wanted_data
for (sample in rownames(pData(pa01_expt))) {
  input_file <- glue::glue("preprocessing/{sample}/outputs/40freebayes_paeruginosa_pa01/paeruginosa_pa01_hits_by_gene.txt.xz")
  input_data <- readr::read_tsv(input_file, col_names=c("gene", "chr", "postion", "nt_string", "aa_string"))
  input_column <- input_data %>%
    dplyr::group_by(gene) %>%
    dplyr::summarize(n=n())
  colnames(input_column) <- c("gene", paste0(sample, "_n"))
  wanted_v2 <- merge(wanted_v2, input_column, by.x="row.names", by.y="gene")
  rownames(wanted_v2) <- wanted_v2[["Row.names"]]
  wanted_v2[["Row.names"]] <- NULL
}
## Rows: 2586 Columns: 5
## ── Column specification ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
## Delimiter: "\t"
## chr (4): gene, chr, nt_string, aa_string
## dbl (1): postion
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
## Rows: 2390 Columns: 5
## ── Column specification ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
## Delimiter: "\t"
## chr (4): gene, chr, nt_string, aa_string
## dbl (1): postion
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
## Rows: 2596 Columns: 5
## ── Column specification ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
## Delimiter: "\t"
## chr (4): gene, chr, nt_string, aa_string
## dbl (1): postion
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
## Rows: 2665 Columns: 5
## ── Column specification ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
## Delimiter: "\t"
## chr (4): gene, chr, nt_string, aa_string
## dbl (1): postion
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
## Rows: 245 Columns: 5
## ── Column specification ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
## Delimiter: "\t"
## chr (4): gene, chr, nt_string, aa_string
## dbl (1): postion
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
## Rows: 2776 Columns: 5
## ── Column specification ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
## Delimiter: "\t"
## chr (4): gene, chr, nt_string, aa_string
## dbl (1): postion
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
## Rows: 2734 Columns: 5
## ── Column specification ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
## Delimiter: "\t"
## chr (4): gene, chr, nt_string, aa_string
## dbl (1): postion
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
## Rows: 2816 Columns: 5
## ── Column specification ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
## Delimiter: "\t"
## chr (4): gene, chr, nt_string, aa_string
## dbl (1): postion
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
## Rows: 2921 Columns: 5
## ── Column specification ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
## Delimiter: "\t"
## chr (4): gene, chr, nt_string, aa_string
## dbl (1): postion
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
## Rows: 280 Columns: 5
## ── Column specification ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
## Delimiter: "\t"
## chr (4): gene, chr, nt_string, aa_string
## dbl (1): postion
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
## Write it out
written <- write_xlsx(data=wanted_v2, excel="excel/rpkm_mutations_per_gene.xlsx")
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+L2hwZ2x0b29scyIpCmtuaXRyOjpvcHRzX2tuaXQkc2V0KHdpZHRoPTEyMCwKICAgICAgICAgICAgICAgICAgICAgcHJvZ3Jlc3M9VFJVRSwKICAgICAgICAgICAgICAgICAgICAgdmVyYm9zZT1UUlVFLAogICAgICAgICAgICAgICAgICAgICBlY2hvPVRSVUUpCmtuaXRyOjpvcHRzX2NodW5rJHNldChlcnJvcj1UUlVFLAogICAgICAgICAgICAgICAgICAgICAgZHBpPTk2KQpvbGRfb3B0aW9ucyA8LSBvcHRpb25zKGRpZ2l0cz00LAogICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnM9RkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAga25pdHIuZHVwbGljYXRlLmxhYmVsPSJhbGxvdyIpCmdncGxvdDI6OnRoZW1lX3NldChnZ3Bsb3QyOjp0aGVtZV9idyhiYXNlX3NpemU9MTApKQpydW5kYXRlIDwtIGZvcm1hdChTeXMuRGF0ZSgpLCBmb3JtYXQ9IiVZJW0lZCIpCnByZXZpb3VzX2ZpbGUgPC0gIiIKdmVyIDwtIGZvcm1hdChTeXMuRGF0ZSgpLCAiJVklbSVkIikKCiMjdG1wIDwtIHNtKGxvYWRtZShmaWxlbmFtZT1wYXN0ZTAoZ3N1YihwYXR0ZXJuPSJcXC5SbWQiLCByZXBsYWNlPSIiLCB4PXByZXZpb3VzX2ZpbGUpLCAiLXYiLCB2ZXIsICIucmRhLnh6IikpKQpybWRfZmlsZSA8LSAiaW5kZXguUm1kIgpgYGAKCiMgSW50cm9kdWN0aW9uCgpUaGlzIGRvY3VtZW50IHNlZWtzIHRvIGxheSBvdXQgdGhlIHN0cmF0ZWd5IGVtcGxveWVkIGZvciBhbgpleGFtaW5hdGlvbiBvZiB0aGUgc2VxdWVuY2luZyByZXN1bHRzIGZyb20gYSBncm91cCBvZiBwbGFzbWlkCmxpYnJhcmllcyBmcm9tIFBzZXVkbW9uYXMgYWVydWdpbm9zYSBQQTAxLgoKVGhlc2Ugc2FtcGxlcyBhcmUgaW4gNSBtYWluIGxpYnJhcmllcywgZWFjaCBvZiB3aGljaCB3YXMgZG9uZSB0d2ljZSwKb25jZSB3aXRoIGEgdG9wb2lzb21lcmFzZSBkdXJpbmcgaXNvbGF0aW9uIGFuZCBvbmNlIHdpdGhvdXQuCgpUaGUgaW1tZWRpYXRlIGdvYWwgb2YgdGhpcyBleHBlcmltZW50IGlzIHRvIGxlYXJuIHdoYXQgT1JGcyBleGlzdCBpbgp0aGUgbGlicmFyeSBhbmQgd2hhdCBtdXRhdGlvbnMgaGF2ZSBhcHBlYXJlZCBpbiB0aGVtLgoKIyBQcmVwcm9jZXNzaW5nCgpUaGUgcHJlcHJvY2Vzc2luZyBpcyBtb3N0bHkgYSBub3JtYWwgRE5BIHNlcXVlbmNpbmcgcGlwZWxpbmU6CgoxLiAgVHJpbSB0aGUgcmVhZHMuCjIuICBNYXAgdGhlbSBhZ2FpbnN0IHRoZSBwbGFzbWlkLCBwRE9OUjIwMS4KMy4gIE1hcCB0aGUgdW5tYXBwZWQgcmVhZHMgZnJvbSAjMiBhZ2FpbnN0IFBBMDEuCjQuICBDb3VudCB0aGUgcmVhZHMgYnkgbG9jdXNfdGFnIHRvIGlkZW50aWZ5IG1pc3NpbmcgZ2VuZXMuCjUuICBVc2UgZnJlZWJheWVzIHRvIGlkZW50aWZ5IHZhcmlhbnQgcG9zaXRpb25zLgo2LiAgUGFyc2UgdGhlIGZyZWViYXllcyBvdXRwdXQgZG93biB0byBhbiBlYXN5IHRvIGludGVycHJldCBmb3JtYXQuCgojIyBUcmltbWluZwoKVGhpcyBpcyBteSBkZWZhdWx0IHRyaW1vbWF0aWMgaW52b2NhdGlvbi4KCgpgYGB7YmFzaCB0cmltbWluZywgZXZhbD1GQUxTRX0KY2QgcHJlcHJvY2Vzc2luZwpzdGFydD0kKHB3ZCkKZm9yIGkgaW4gJCgvYmluL2xzKTsgZG8KICAgIGNkICR7aX0KICAgIGN5b2EgLS10YXNrIHRyaW0gLS1tZXRob2QgdHJpbSAtLWlucHV0ICQoL2Jpbi9scyAqLmd6IHwgdHIgJ1xuJyAnOicgfCBzZWQgJ3MvOiQvL2cnKQogICAgY2QgJHN0YXJ0CmRvbmUKYGBgCgpIZXJlIGlzIHRoZSBzY3JpcHQgd2hpY2ggcmVzdWx0ZWQgZm9yIHRoZSBwb29sYSBzYW1wbGU6CgpJIHN0aWxsIGhhdmUgYW4gb2xkIGNoZWNrIGluIGl0IGZyb20gYW4gZWFybGllciB2ZXJzaW9uIG9mIHRyaW1vbWF0aWMKd2hpY2ggd291bGQgc29tZXRpbWVzIGZhaWwuCgojIyMgSW5kaXZpZHVhbCB0cmltbWluZyBzY3JpcHQKCmBgYHtiYXNoIHBvb2xhX3RyaW1fc2NyaXB0LCBldmFsPUZBTFNFfQojU0JBVENIIC0tZXhwb3J0PUFMTAojU0JBVENIIC0tbWFpbC10eXBlPU5PTkUKI1NCQVRDSCAtLW9wZW4tbW9kZT1hcHBlbmQKI1NCQVRDSCAtLWNoZGlyPS9mcy9jYmNiLWxhYi9uZWxzYXllZC9zY3JhdGNoL2F0Yi9kbmFzZXEvcGFlcnVnaW5vc2FfcGxhc21pZHNfMjAyMi9wcmVwcm9jZXNzaW5nL3Bvb2xhCiNTQkFUQ0ggLS1wYXJ0aXRpb249ZHBhcnQKI1NCQVRDSCAtLXFvcz13b3Jrc3RhdGlvbiAtLW5pY2U9MTAKI1NCQVRDSCAtLW5vZGVzPTEgLS1yZXF1ZXVlCiNTQkFUQ0ggLS10aW1lPTI0OjAwOjAwCiNTQkFUQ0ggLS1qb2ItbmFtZT0wMXRyaW1fT1JGZW9tZV9Qb29sX0FfUzFfUjFfMDAxCiNTQkFUQ0ggLS1tZW09MjRHCiNTQkFUQ0ggLS1jcHVzLXBlci10YXNrPTMKI1NCQVRDSCAtLW91dHB1dD1vdXRwdXRzL2xvZy50eHQuc2JhdGNoCnNldCAtbyBlcnJleGl0CnNldCAtbyBlcnJ0cmFjZQpzZXQgLW8gcGlwZWZhaWwKZXhwb3J0IExFU1M9Jy0tYnVmZmVycyAwJwpzY3JpcHQ9IiQocHdkKS8kMCIKZXJyKCkgewogICAgZWNobyAiRXJyb3Igb2NjdXJyZWQ6IgogICAgYXdrICdOUj5MLTQgJiYgTlI8TCs0IHsgcHJpbnRmICIlLTVkJTNzJXNcbiIsTlIsKE5SPT1MPyI+Pj4iOiIiKSwkc2NyaXB0IH0nIEw9JDEgJHNjcmlwdAp9CnRyYXAgJ2VyciAkTElORU5PJyBFUlIKCmVjaG8gIiMjIFN0YXJ0ZWQgL2ZzL2NiY2ItbGFiL25lbHNheWVkL3NjcmF0Y2gvYXRiL2RuYXNlcS9wYWVydWdpbm9zYV9wbGFzbWlkc18yMDIyL3ByZXByb2Nlc3NpbmcvcG9vbGEvc2NyaXB0cy8wMXRyaW1fT1JGZW9tZV9Qb29sX0FfUzFfUjFfMDAxLnNoIGF0ICQoZGF0ZSkgb24gJChob3N0bmFtZSkgd2l0aCBpZCAke1NMVVJNX0pPQklEfS4iID4+IG91dHB1dHMvbG9nLnR4dAoKbW9kdWxlIGFkZCB0cmltb21hdGljCgojIyBUaGlzIGNhbGwgdG8gdHJpbW9tYXRpYyByZW1vdmVzIGlsbHVtaW5hIGFuZCBlcGljZW50cmUgYWRhcHRlcnMgZnJvbSBPUkZlb21lX1Bvb2xfQV9TMV9SMV8wMDEuZmFzdHEuZ3o6T1JGZW9tZV9Qb29sX0FfUzFfUjJfMDAxLmZhc3RxLmd6LgojIyBJdCBhbHNvIHBlcmZvcm1zIGEgc2xpZGluZyB3aW5kb3cgcmVtb3ZhbCBvZiBhbnl0aGluZyB3aXRoIHF1YWxpdHkgPDI1OwojIyBjdXRhZGFwdCBwcm92aWRlcyBhbiBhbHRlcm5hdGl2ZSB0byB0aGlzIHRvb2wuCiMjIFRoZSBvcmlnaW5hbCBzZXF1ZW5jZSBkYXRhIGlzIHJlY29tcHJlc3NlZCBhbmQgc2F2ZWQgaW4gdGhlIHNlcXVlbmNlcy8gZGlyZWN0b3J5Lgpta2RpciAtcCBvdXRwdXRzLzAxdHJpbW9tYXRpYwojIyBOb3RlIHRoYXQgdHJpbW9tYXRpYyBwcmludHMgYWxsIG91dHB1dCBhbmQgZXJyb3JzIHRvIFNUREVSUiwgc28gc2VuZCBib3RoIHRvIG91dHB1dAp0cmltbW9tYXRpYyBQRSBcCiAgLXRocmVhZHMgMSBcCiAgLXBocmVkMzMgXAogIDwobGVzcyByMS5mYXN0cS5neikgPChsZXNzIHIyLmZhc3RxLmd6KSBcCiAgT1JGZW9tZV9Qb29sX0FfUzFfUjFfMDAxLXRyaW1tZWRfcGFpcmVkLmZhc3RxIE9SRmVvbWVfUG9vbF9BX1MxX1IxXzAwMS10cmltbWVkX3VucGFpcmVkLmZhc3RxIFwKICBPUkZlb21lX1Bvb2xfQV9TMV9SMl8wMDEtdHJpbW1lZF9wYWlyZWQuZmFzdHEgT1JGZW9tZV9Qb29sX0FfUzFfUjJfMDAxLXRyaW1tZWRfdW5wYWlyZWQuZmFzdHEgXAogICBJTExVTUlOQUNMSVA6L2NiY2Ivc3cvUmVkSGF0LTcteDg2XzY0L2NvbW1vbi9sb2NhbC9wZXJsLzUuMzQvbGliL3NpdGVfcGVybC81LjM0LjAvYXV0by9zaGFyZS9kaXN0L0Jpby1BZHZlbnR1cmUvZ2Vub21lL2FkYXB0ZXJzLmZhOjI6MjA6MTA6MjprZWVwQm90aFJlYWRzIFwKICBTTElESU5HV0lORE9XOjQ6MjAgTUlOTEVOOjQwIFwKICAxPm91dHB1dHMvMDF0cmltb21hdGljL09SRmVvbWVfUG9vbF9BX1MxX1IxXzAwMS10cmltb21hdGljLnN0ZG91dCBcCiAgMj5vdXRwdXRzLzAxdHJpbW9tYXRpYy9PUkZlb21lX1Bvb2xfQV9TMV9SMV8wMDEtdHJpbW9tYXRpYy5zdGRlcnIKZXhjZXB0ZWQ9JCggeyBncmVwICJFeGNlcHRpb24iICJvdXRwdXRzLzAxdHJpbW9tYXRpYy9PUkZlb21lX1Bvb2xfQV9TMV9SMV8wMDEtdHJpbW9tYXRpYy5zdGRvdXQiIHx8IHRlc3QgJD8gPSAxOyB9ICkKIyMgVGhlIGZvbGxvd2luZyBpcyBpbiBjYXNlIHRoZSBpbGx1bWluYSBjbGlwcGluZyBmYWlscywgd2hpY2ggaXQgZG9lcyBpZiB0aGlzIGhhcyBhbHJlYWR5IGJlZW4gcnVuIEkgdGhpbmsuCmlmIFtbICIke2V4Y2VwdGVkfSIgIT0gIiIgXV07IHRoZW4KICB0cmltbW9tYXRpYyBQRSBcCiAgICAtdGhyZWFkcyAxIFwKICAgIC1waHJlZDMzIFwKICAgIDwobGVzcyByMS5mYXN0cS5neikgPChsZXNzIHIyLmZhc3RxLmd6KSBcCiAgICBPUkZlb21lX1Bvb2xfQV9TMV9SMV8wMDEtdHJpbW1lZF9wYWlyZWQuZmFzdHEgT1JGZW9tZV9Qb29sX0FfUzFfUjFfMDAxLXRyaW1tZWRfdW5wYWlyZWQuZmFzdHEgXAogICAgT1JGZW9tZV9Qb29sX0FfUzFfUjJfMDAxLXRyaW1tZWRfcGFpcmVkLmZhc3RxIE9SRmVvbWVfUG9vbF9BX1MxX1IyXzAwMS10cmltbWVkX3VucGFpcmVkLmZhc3RxIFwKICAgICBTTElESU5HV0lORE9XOjQ6MjUgTUlOTEVOOjUwXAogICAgMT5vdXRwdXRzLzAxdHJpbW9tYXRpYy9PUkZlb21lX1Bvb2xfQV9TMV9SMV8wMDEtdHJpbW9tYXRpYy5zdGRvdXQgXAogICAgMj5vdXRwdXRzLzAxdHJpbW9tYXRpYy9PUkZlb21lX1Bvb2xfQV9TMV9SMV8wMDEtdHJpbW9tYXRpYy5zdGRlcnIKZmkKc2xlZXAgMTAKbXYgT1JGZW9tZV9Qb29sX0FfUzFfUjFfMDAxLXRyaW1tZWRfcGFpcmVkLmZhc3RxIE9SRmVvbWVfUG9vbF9BX1MxX1IxXzAwMS10cmltbWVkLmZhc3RxCm12IE9SRmVvbWVfUG9vbF9BX1MxX1IyXzAwMS10cmltbWVkX3BhaXJlZC5mYXN0cSBPUkZlb21lX1Bvb2xfQV9TMV9SMl8wMDEtdHJpbW1lZC5mYXN0cQoKIyMgUmVjb21wcmVzcyB0aGUgdW5wYWlyZWQgcmVhZHMsIHRoaXMgc2hvdWxkIG5vdCB0YWtlIGxvbmcuCnh6IC05ZSAtZiBPUkZlb21lX1Bvb2xfQV9TMV9SMV8wMDEtdHJpbW1lZF91bnBhaXJlZC5mYXN0cQp4eiAtOWUgLWYgT1JGZW9tZV9Qb29sX0FfUzFfUjJfMDAxLXRyaW1tZWRfdW5wYWlyZWQuZmFzdHEKIyMgUmVjb21wcmVzcyB0aGUgcGFpcmVkIHJlYWRzLgp4eiAtOWUgLWYgT1JGZW9tZV9Qb29sX0FfUzFfUjFfMDAxLXRyaW1tZWQuZmFzdHEKeHogLTllIC1mIE9SRmVvbWVfUG9vbF9BX1MxX1IyXzAwMS10cmltbWVkLmZhc3RxCmxuIC1zZiBPUkZlb21lX1Bvb2xfQV9TMV9SMV8wMDEtdHJpbW1lZC5mYXN0cS54eiByMV90cmltbWVkLmZhc3RxLnh6CmxuIC1zZiBPUkZlb21lX1Bvb2xfQV9TMV9SMl8wMDEtdHJpbW1lZC5mYXN0cS54eiByMl90cmltbWVkLmZhc3RxLnh6CgoKIyMgVGhlIGZvbGxvd2luZyBsaW5lcyBnaXZlIHN0YXR1cyBjb2RlcyBhbmQgc29tZSBsb2dnaW5nCmVjaG8gIiMjIEpvYiBzdGF0dXM6ICQ/ICIgPj4gb3V0cHV0cy9sb2cudHh0CmVjaG8gIiMjICQoaG9zdG5hbWUpIEZpbmlzaGVkICR7U0xVUk1fSk9CSUR9IDAxdHJpbV9PUkZlb21lX1Bvb2xfQV9TMV9SMV8wMDEuc2ggYXQgJChkYXRlKSwgaXQgdG9vayAkKCggU0VDT05EUyAvIDYwICkpIG1pbnV0ZXMuIiA+PiBvdXRwdXRzL2xvZy50eHQKCmlmIFtbIC14ICIkKGNvbW1hbmQgLXYgc3N0YXQpIiAmJiAhIC16ICIke1NMVVJNX0pPQklEfSIgXV07IHRoZW4KICB3YWxsdGltZT0kKHNjb250cm9sIHNob3cgam9iICIke1NMVVJNX0pPQklEfSIgfCBncmVwIFJ1blRpbWUgfCBwZXJsIC1GJy9ccyt8PS8nIC1sYW5lICd7cHJpbnQgJEZbMl19JyAyPi9kZXYvbnVsbCkKICBlY2hvICIjIyMjIHdhbGx0aW1lIHVzZWQgYnkgJHtTTFVSTV9KT0JJRH0gd2FzOiAke3dhbGx0aW1lOi1udWxsfSIgPj4gb3V0cHV0cy9sb2cudHh0CiAgbWF4bWVtPSQoc3N0YXQgLS1mb3JtYXQ9TWF4Vk1TaXplIC1uICIke1NMVVJNX0pPQklEfS5iYXRjaCIgMj4vZGV2L251bGwpCiAgZWNobyAiIyMjIyBtYXhpbXVtIG1lbW9yeSB1c2VkIGJ5ICR7U0xVUk1fSk9CSUR9IHdhczogJHttYXhtZW06LW51bGx9IiA+PiBvdXRwdXRzL2xvZy50eHQKICBhdmVjcHU9JChzc3RhdCAtLWZvcm1hdD1BdmVDUFUgLW4gIiR7U0xVUk1fSk9CSUR9LmJhdGNoIiAyPi9kZXYvbnVsbCkKICBlY2hvICIjIyMjIGF2ZXJhZ2UgY3B1IHVzZWQgYnkgJHtTTFVSTV9KT0JJRH0gd2FzOiAke2F2ZWNwdTotbnVsbH0iID4+IG91dHB1dHMvbG9nLnR4dApmaQojIyBBZGRpbmcgYSBsaXR0bGUgbG9naWMgdG8gaGF2ZSBza2lwIGZpbmlzaGVkIGpvYnMuCnRvdWNoIG91dHB1dHMvbG9ncy8wMXRyaW1fT1JGZW9tZV9Qb29sX0FfUzFfUjFfMDAxLmZpbmlzaGVkCmBgYAoKIyMgTWFwcGluZyBhZ2FpbnN0IHBET05SCgpUaGlzIHdhcyBmb2xsb3dlZCBieSBteSBkZWZhdWx0IG1hcHBpbmcgYWdhaW5zdCB0aGUgcGxhc21pZCB2aWEKaGlzYXQyLgoKSSBwcmV2aW91c2x5IGRvd25sb2FkZWQgYSBjb3B5IG9mIHBET05SMjAxIGFuZCByYW4gdGhlIGZvbGxvd2luZyBvbgppdDoKCmBgYHtiYXNoIHBkb25yX2luZGV4LCBldmFsPUZBTFNFfQpjeW9hIC0tdGFzayBpbmRleCAtLW1ldGhvZCBpbmRleGhpc2F0IC0taW5wdXQgcERPTlIyMDEuZmFzdGEgLS1zcGVjaWVzIHBET05SMjAxCmBgYAoKSSBkaWQgbm90IGdlbmVyYXRlIGEgZ2ZmIGZpbGUgZm9yIHRoaXMsIHNvIGh0c2VxIGlzIG5vdCBydW4uCgpUaGUgZm9sbG93aW5nIGludm9jYXRpb24gcXVldWVzIDIgam9icyBvbiB0aGUgY2x1c3RlciwgMSBmb3IgdGhlCm1hcHBpbmcgYW5kIDEgdG8gc29ydC9jb21wcmVzcy9pbmRleCB0aGUgYWxpZ25tZW50LgoKYGBge2Jhc2ggcGRvbnJfaGlzYXQsIGV2YWw9RkFMU0V9CmNkIHByZXByb2Nlc3NpbmcKc3RhcnQ9JChwd2QpCmZvciBpIGluICQoL2Jpbi9scyk7IGRvCiAgICBjZCAke2l9CiAgICBjeW9hIC0tdGFzayBtYXAgLS1tZXRob2QgaGlzYXQgLS1zcGVjaWVzIHBET05SMjAxIC0taW5wdXQgcjFfdHJpbW1lZC5mYXN0cS54ejpyMl90cmltbWVkLmZhc3RxLnh6CiAgICBjZCAkc3RhcnQKZG9uZQpgYGAKCiMjIyBJbmRpdmlkdWFsIHBET05SIG1hcHBpbmcgc2NyaXB0CgpgYGB7YmFzaCBwZG9ub3JfbWFwcGluZ19zY3JpcHQsIGV2YWw9RkFMU0V9CiMhL3Vzci9iaW4vZW52IGJhc2gKI1NCQVRDSCAtLWV4cG9ydD1BTEwKI1NCQVRDSCAtLW1haWwtdHlwZT1OT05FCiNTQkFUQ0ggLS1vcGVuLW1vZGU9YXBwZW5kCiNTQkFUQ0ggLS1jaGRpcj0vZnMvY2JjYi1sYWIvbmVsc2F5ZWQvc2NyYXRjaC9hdGIvZG5hc2VxL3BhZXJ1Z2lub3NhX3BsYXNtaWRzXzIwMjIvcHJlcHJvY2Vzc2luZy9wb29sYQojU0JBVENIIC0tcGFydGl0aW9uPWRwYXJ0CiNTQkFUQ0ggLS1xb3M9d29ya3N0YXRpb24gLS1uaWNlPTEwCiNTQkFUQ0ggLS1ub2Rlcz0xIC0tcmVxdWV1ZQojU0JBVENIIC0tdGltZT0xMDowMDowMAojU0JBVENIIC0tam9iLW5hbWU9NDBoaXNhdDJfcERPTlIyMDFfZ2Vub21lCiNTQkFUQ0ggLS1tZW09MjRHCiNTQkFUQ0ggLS1jcHVzLXBlci10YXNrPTQKI1NCQVRDSCAtLW91dHB1dD1vdXRwdXRzL2xvZy50eHQuc2JhdGNoCnNldCAtbyBlcnJleGl0CnNldCAtbyBlcnJ0cmFjZQpzZXQgLW8gcGlwZWZhaWwKZXhwb3J0IExFU1M9Jy0tYnVmZmVycyAwJwpzY3JpcHQ9IiQocHdkKS8kMCIKZXJyKCkgewogICAgZWNobyAiRXJyb3Igb2NjdXJyZWQ6IgogICAgYXdrICdOUj5MLTQgJiYgTlI8TCs0IHsgcHJpbnRmICIlLTVkJTNzJXNcbiIsTlIsKE5SPT1MPyI+Pj4iOiIiKSwkc2NyaXB0IH0nIEw9JDEgJHNjcmlwdAp9CnRyYXAgJ2VyciAkTElORU5PJyBFUlIKCmVjaG8gIiMjIFN0YXJ0ZWQgL2ZzL2NiY2ItbGFiL25lbHNheWVkL3NjcmF0Y2gvYXRiL2RuYXNlcS9wYWVydWdpbm9zYV9wbGFzbWlkc18yMDIyL3ByZXByb2Nlc3NpbmcvcG9vbGEvc2NyaXB0cy80MGhpc2F0Ml9wRE9OUjIwMV9nZW5vbWUuc2ggYXQgJChkYXRlKSBvbiAkKGhvc3RuYW1lKSB3aXRoIGlkICR7U0xVUk1fSk9CSUR9LiIgPj4gb3V0cHV0cy9sb2cudHh0Cgptb2R1bGUgYWRkIGhpc2F0MiBzYW10b29scyBodHNlcSBiYW10b29scwoKIyMgVGhpcyBpcyBhIGhpc2F0MiBhbGlnbm1lbnQgb2YgIC0xIDwobGVzcyAvZnMvY2JjYi1sYWIvbmVsc2F5ZWQvc2NyYXRjaC9hdGIvZG5hc2VxL3BhZXJ1Z2lub3NhX3BsYXNtaWRzXzIwMjIvcHJlcHJvY2Vzc2luZy9wb29sYS9yMV90cmltbWVkLmZhc3RxLnh6KSAtMiA8KGxlc3MgL2ZzL2NiY2ItbGFiL25lbHNheWVkL3NjcmF0Y2gvYXRiL2RuYXNlcS9wYWVydWdpbm9zYV9wbGFzbWlkc18yMDIyL3ByZXByb2Nlc3NpbmcvcG9vbGEvcjJfdHJpbW1lZC5mYXN0cS54eikgIGFnYWluc3QgL2NiY2Job21lcy9hYmVsZXcvbGlicmFyaWVzL2dlbm9tZS9pbmRleGVzL3BET05SMjAxCgpta2RpciAtcCBvdXRwdXRzLzQwaGlzYXQyX3BET05SMjAxCmhpc2F0MiAteCAvY2JjYmhvbWVzL2FiZWxldy9saWJyYXJpZXMvZ2Vub21lL2luZGV4ZXMvcERPTlIyMDEgIFwKICAtcCA0IFwKICAtcSAgIC0xIDwobGVzcyAvZnMvY2JjYi1sYWIvbmVsc2F5ZWQvc2NyYXRjaC9hdGIvZG5hc2VxL3BhZXJ1Z2lub3NhX3BsYXNtaWRzXzIwMjIvcHJlcHJvY2Vzc2luZy9wb29sYS9yMV90cmltbWVkLmZhc3RxLnh6KSAtMiA8KGxlc3MgL2ZzL2NiY2ItbGFiL25lbHNheWVkL3NjcmF0Y2gvYXRiL2RuYXNlcS9wYWVydWdpbm9zYV9wbGFzbWlkc18yMDIyL3ByZXByb2Nlc3NpbmcvcG9vbGEvcjJfdHJpbW1lZC5mYXN0cS54eikgIFwKICAtLXBocmVkMzMgXAogIC0tdW4gb3V0cHV0cy80MGhpc2F0Ml9wRE9OUjIwMS9wb29sYV91bmFsZGlzX3BET05SMjAxX2dlbm9tZS5mYXN0cSBcCiAgLS1hbCBvdXRwdXRzLzQwaGlzYXQyX3BET05SMjAxL3Bvb2xhX2FsZGlzX3BET05SMjAxX2dlbm9tZS5mYXN0cSBcCiAgLS11bi1jb25jIG91dHB1dHMvNDBoaXNhdDJfcERPTlIyMDEvcG9vbGFfdW5hbGNvbl9wRE9OUjIwMV9nZW5vbWUuZmFzdHEgXAogIC0tYWwtY29uYyBvdXRwdXRzLzQwaGlzYXQyX3BET05SMjAxL3Bvb2xhX2FsY29uX3BET05SMjAxX2dlbm9tZS5mYXN0cSBcCiAgLVMgb3V0cHV0cy80MGhpc2F0Ml9wRE9OUjIwMS9wb29sYV9wRE9OUjIwMV9nZW5vbWUuc2FtIFwKICAyPm91dHB1dHMvNDBoaXNhdDJfcERPTlIyMDEvaGlzYXQyX3BET05SMjAxX2dlbm9tZV9wb29sYS5zdGRlcnIgXAogIDE+b3V0cHV0cy80MGhpc2F0Ml9wRE9OUjIwMS9oaXNhdDJfcERPTlIyMDFfZ2Vub21lX3Bvb2xhLnN0ZG91dAoKCiMjIFRoZSBmb2xsb3dpbmcgbGluZXMgZ2l2ZSBzdGF0dXMgY29kZXMgYW5kIHNvbWUgbG9nZ2luZwplY2hvICIjIyBKb2Igc3RhdHVzOiAkPyAiID4+IG91dHB1dHMvbG9nLnR4dAplY2hvICIjIyAkKGhvc3RuYW1lKSBGaW5pc2hlZCAke1NMVVJNX0pPQklEfSA0MGhpc2F0Ml9wRE9OUjIwMV9nZW5vbWUuc2ggYXQgJChkYXRlKSwgaXQgdG9vayAkKCggU0VDT05EUyAvIDYwICkpIG1pbnV0ZXMuIiA+PiBvdXRwdXRzL2xvZy50eHQKCmlmIFtbIC14ICIkKGNvbW1hbmQgLXYgc3N0YXQpIiAmJiAhIC16ICIke1NMVVJNX0pPQklEfSIgXV07IHRoZW4KICB3YWxsdGltZT0kKHNjb250cm9sIHNob3cgam9iICIke1NMVVJNX0pPQklEfSIgfCBncmVwIFJ1blRpbWUgfCBwZXJsIC1GJy9ccyt8PS8nIC1sYW5lICd7cHJpbnQgJEZbMl19JyAyPi9kZXYvbnVsbCkKICBlY2hvICIjIyMjIHdhbGx0aW1lIHVzZWQgYnkgJHtTTFVSTV9KT0JJRH0gd2FzOiAke3dhbGx0aW1lOi1udWxsfSIgPj4gb3V0cHV0cy9sb2cudHh0CiAgbWF4bWVtPSQoc3N0YXQgLS1mb3JtYXQ9TWF4Vk1TaXplIC1uICIke1NMVVJNX0pPQklEfS5iYXRjaCIgMj4vZGV2L251bGwpCiAgZWNobyAiIyMjIyBtYXhpbXVtIG1lbW9yeSB1c2VkIGJ5ICR7U0xVUk1fSk9CSUR9IHdhczogJHttYXhtZW06LW51bGx9IiA+PiBvdXRwdXRzL2xvZy50eHQKICBhdmVjcHU9JChzc3RhdCAtLWZvcm1hdD1BdmVDUFUgLW4gIiR7U0xVUk1fSk9CSUR9LmJhdGNoIiAyPi9kZXYvbnVsbCkKICBlY2hvICIjIyMjIGF2ZXJhZ2UgY3B1IHVzZWQgYnkgJHtTTFVSTV9KT0JJRH0gd2FzOiAke2F2ZWNwdTotbnVsbH0iID4+IG91dHB1dHMvbG9nLnR4dApmaQojIyBBZGRpbmcgYSBsaXR0bGUgbG9naWMgdG8gaGF2ZSBza2lwIGZpbmlzaGVkIGpvYnMuCnRvdWNoIG91dHB1dHMvbG9ncy80MGhpc2F0Ml9wRE9OUjIwMV9nZW5vbWUuZmluaXNoZWQKYGBgCgpUaGUgY3lvYSBpbnZvY2F0aW9uIHdoaWNoIGdlbmVyYXRlZCB0aGUgYWJvdmUgc2NyaXB0IGFsc28gY3JlYXRlcyB0aGUKZm9sbG93aW5nIHNjcmlwdCBhbG9uZyB3aXRoIGEgZ3JvdXAgb2YgY29tcHJlc3Npb24gam9icyB3aGljaAphZ2dyZXNzaXZlbHkgY29tcHJlc3MgdGhlIGZhc3RxIG91dHB1dCBmaWxlcy4gIEkgbGVhdmUgdGhhdCBhcyBhbgpleGVyY2lzZSB0byB0aGUgcmVhZGVyLgoKYGBge2Jhc2ggc2FtMmJhbV9wRE9OUiwgZXZhbD1GQUxTRX0KIyEvdXNyL2Jpbi9lbnYgYmFzaAojU0JBVENIIC0tZXhwb3J0PUFMTAojU0JBVENIIC0tbWFpbC10eXBlPU5PTkUKI1NCQVRDSCAtLW9wZW4tbW9kZT1hcHBlbmQKI1NCQVRDSCAtLWNoZGlyPS9mcy9jYmNiLWxhYi9uZWxzYXllZC9zY3JhdGNoL2F0Yi9kbmFzZXEvcGFlcnVnaW5vc2FfcGxhc21pZHNfMjAyMi9wcmVwcm9jZXNzaW5nL3Bvb2xhCiNTQkFUQ0ggLS1wYXJ0aXRpb249ZHBhcnQKI1NCQVRDSCAtLXFvcz10aHJvdWdocHV0IC0tbmljZT0xMAojU0JBVENIIC0tbm9kZXM9MSAtLXJlcXVldWUKI1NCQVRDSCAtLXRpbWU9MTI6MDA6MDAKI1NCQVRDSCAtLWpvYi1uYW1lPTQwXzJzMmJfaGlzYXQyX3BET05SMjAxCiNTQkFUQ0ggLS1tZW09MTJHCiNTQkFUQ0ggLS1jcHVzLXBlci10YXNrPTEKI1NCQVRDSCAtLW91dHB1dD1vdXRwdXRzL2xvZy50eHQuc2JhdGNoCnNldCAtbyBlcnJleGl0CnNldCAtbyBlcnJ0cmFjZQpzZXQgLW8gcGlwZWZhaWwKZXhwb3J0IExFU1M9Jy0tYnVmZmVycyAwJwpzY3JpcHQ9IiQocHdkKS8kMCIKZXJyKCkgewogICAgZWNobyAiRXJyb3Igb2NjdXJyZWQ6IgogICAgYXdrICdOUj5MLTQgJiYgTlI8TCs0IHsgcHJpbnRmICIlLTVkJTNzJXNcbiIsTlIsKE5SPT1MPyI+Pj4iOiIiKSwkc2NyaXB0IH0nIEw9JDEgJHNjcmlwdAp9CnRyYXAgJ2VyciAkTElORU5PJyBFUlIKCmVjaG8gIiMjIFN0YXJ0ZWQgL2ZzL2NiY2ItbGFiL25lbHNheWVkL3NjcmF0Y2gvYXRiL2RuYXNlcS9wYWVydWdpbm9zYV9wbGFzbWlkc18yMDIyL3ByZXByb2Nlc3NpbmcvcG9vbGEvc2NyaXB0cy80MF8yczJiX2hpc2F0Ml9wRE9OUjIwMS5zaCBhdCAkKGRhdGUpIG9uICQoaG9zdG5hbWUpIHdpdGggaWQgJHtTTFVSTV9KT0JJRH0uIiA+PiBvdXRwdXRzL2xvZy50eHQKCm1vZHVsZSBhZGQgc2FtdG9vbHMgYmFtdG9vbHMKCiMjIENvbnZlcnRpbmcgdGhlIHRleHQgc2FtIHRvIGEgY29tcHJlc3NlZCwgc29ydGVkLCBpbmRleGVkIGJhbWZpbGUuCiMjIEFsc28gcHJpbnRpbmcgYWxpZ25tZW50IHN0YXRpc3RpY3MgdG8gb3V0cHV0cy80MGhpc2F0Ml9wRE9OUjIwMS9wb29sYV9wRE9OUjIwMV9nZW5vbWUuYmFtLnN0YXRzCiMjIFRoaXMgam9iIGRlcGVuZGVkIG9uOiAyNDAxMTAKCmVjaG8gIlN0YXJ0aW5nIHNhbXRvb2xzIgppZiBbWyAhIC1mICJvdXRwdXRzLzQwaGlzYXQyX3BET05SMjAxL3Bvb2xhX3BET05SMjAxX2dlbm9tZS5zYW0iIF1dOyB0aGVuCiAgICBlY2hvICJDb3VsZCBub3QgZmluZCB0aGUgc2FtdG9vbHMgaW5wdXQgZmlsZS4iCiAgICBleGl0IDEKZmkKc2FtdG9vbHMgdmlldyAtdSAtdCAvY2JjYmhvbWVzL2FiZWxldy9saWJyYXJpZXMvZ2Vub21lL3BET05SMjAxLmZhc3RhIFwKICAtUyBvdXRwdXRzLzQwaGlzYXQyX3BET05SMjAxL3Bvb2xhX3BET05SMjAxX2dlbm9tZS5zYW0gLW8gb3V0cHV0cy80MGhpc2F0Ml9wRE9OUjIwMS9wb29sYV9wRE9OUjIwMV9nZW5vbWUuYmFtICBcCiAgMj5vdXRwdXRzLzQwaGlzYXQyX3BET05SMjAxL3Bvb2xhX3BET05SMjAxX2dlbm9tZS5iYW0uZXJyIDE+b3V0cHV0cy80MGhpc2F0Ml9wRE9OUjIwMS9wb29sYV9wRE9OUjIwMV9nZW5vbWUuYmFtLm91dCAmJiBcCmVjaG8gMAogIHNhbXRvb2xzIHNvcnQgLWwgOSBvdXRwdXRzLzQwaGlzYXQyX3BET05SMjAxL3Bvb2xhX3BET05SMjAxX2dlbm9tZS5iYW0gLW8gb3V0cHV0cy80MGhpc2F0Ml9wRE9OUjIwMS9wb29sYV9wRE9OUjIwMV9nZW5vbWUtc29ydGVkLmJhbSBcCiAgMj5vdXRwdXRzLzQwaGlzYXQyX3BET05SMjAxL3Bvb2xhX3BET05SMjAxX2dlbm9tZS1zb3J0ZWQuc3RkZXJyIFwKICAxPm91dHB1dHMvNDBoaXNhdDJfcERPTlIyMDEvcG9vbGFfcERPTlIyMDFfZ2Vub21lLXNvcnRlZC5zdGRvdXQgJiYgXAogIHJtIG91dHB1dHMvNDBoaXNhdDJfcERPTlIyMDEvcG9vbGFfcERPTlIyMDFfZ2Vub21lLmJhbSAmJiBcCiAgcm0gb3V0cHV0cy80MGhpc2F0Ml9wRE9OUjIwMS9wb29sYV9wRE9OUjIwMV9nZW5vbWUuc2FtICYmIFwKICBtdiBvdXRwdXRzLzQwaGlzYXQyX3BET05SMjAxL3Bvb2xhX3BET05SMjAxX2dlbm9tZS1zb3J0ZWQuYmFtIG91dHB1dHMvNDBoaXNhdDJfcERPTlIyMDEvcG9vbGFfcERPTlIyMDFfZ2Vub21lLmJhbSAmJiAgc2FtdG9vbHMgaW5kZXggb3V0cHV0cy80MGhpc2F0Ml9wRE9OUjIwMS9wb29sYV9wRE9OUjIwMV9nZW5vbWUuYmFtCmVjaG8gMApiYW10b29scyBzdGF0cyAtaW4gb3V0cHV0cy80MGhpc2F0Ml9wRE9OUjIwMS9wb29sYV9wRE9OUjIwMV9nZW5vbWUuYmFtIDI+b3V0cHV0cy80MGhpc2F0Ml9wRE9OUjIwMS9wb29sYV9wRE9OUjIwMV9nZW5vbWUuYmFtLnN0YXRzIDE+JjIKZWNobyAwCgojIyBUaGUgZm9sbG93aW5nIHdpbGwgZmFpbCBpZiB0aGlzIGlzIHNpbmdsZS1lbmRlZC4Kc2FtdG9vbHMgdmlldyAtYiAtZiAyIC1vIG91dHB1dHMvNDBoaXNhdDJfcERPTlIyMDEvcG9vbGFfcERPTlIyMDFfZ2Vub21lLXBhaXJlZC5iYW0gb3V0cHV0cy80MGhpc2F0Ml9wRE9OUjIwMS9wb29sYV9wRE9OUjIwMV9nZW5vbWUuYmFtICYmIHNhbXRvb2xzIGluZGV4IG91dHB1dHMvNDBoaXNhdDJfcERPTlIyMDEvcG9vbGFfcERPTlIyMDFfZ2Vub21lLXBhaXJlZC5iYW0KYmFtdG9vbHMgc3RhdHMgLWluIG91dHB1dHMvNDBoaXNhdDJfcERPTlIyMDEvcG9vbGFfcERPTlIyMDFfZ2Vub21lLXBhaXJlZC5iYW0gMj5vdXRwdXRzLzQwaGlzYXQyX3BET05SMjAxL3Bvb2xhX3BET05SMjAxX2dlbm9tZS1wYWlyZWQuc3RhdHMgMT4mMgpiYW10b29scyBmaWx0ZXIgLXRhZyBYTTowIC1pbiBvdXRwdXRzLzQwaGlzYXQyX3BET05SMjAxL3Bvb2xhX3BET05SMjAxX2dlbm9tZS5iYW0gLW91dCBvdXRwdXRzLzQwaGlzYXQyX3BET05SMjAxL3Bvb2xhX3BET05SMjAxX2dlbm9tZS1zb3J0ZWRfbm9taXNtYXRjaC5iYW0gJiYKICBzYW10b29scyBpbmRleCBvdXRwdXRzLzQwaGlzYXQyX3BET05SMjAxL3Bvb2xhX3BET05SMjAxX2dlbm9tZS1zb3J0ZWRfbm9taXNtYXRjaC5iYW0KCiMjIFRoZSBmb2xsb3dpbmcgbGluZXMgZ2l2ZSBzdGF0dXMgY29kZXMgYW5kIHNvbWUgbG9nZ2luZwplY2hvICIjIyBKb2Igc3RhdHVzOiAkPyAiID4+IG91dHB1dHMvbG9nLnR4dAplY2hvICIjIyAkKGhvc3RuYW1lKSBGaW5pc2hlZCAke1NMVVJNX0pPQklEfSA0MF8yczJiX2hpc2F0Ml9wRE9OUjIwMS5zaCBhdCAkKGRhdGUpLCBpdCB0b29rICQoKCBTRUNPTkRTIC8gNjAgKSkgbWludXRlcy4iID4+IG91dHB1dHMvbG9nLnR4dAoKaWYgW1sgLXggIiQoY29tbWFuZCAtdiBzc3RhdCkiICYmICEgLXogIiR7U0xVUk1fSk9CSUR9IiBdXTsgdGhlbgogIHdhbGx0aW1lPSQoc2NvbnRyb2wgc2hvdyBqb2IgIiR7U0xVUk1fSk9CSUR9IiB8IGdyZXAgUnVuVGltZSB8IHBlcmwgLUYnL1xzK3w9LycgLWxhbmUgJ3twcmludCAkRlsyXX0nIDI+L2Rldi9udWxsKQogIGVjaG8gIiMjIyMgd2FsbHRpbWUgdXNlZCBieSAke1NMVVJNX0pPQklEfSB3YXM6ICR7d2FsbHRpbWU6LW51bGx9IiA+PiBvdXRwdXRzL2xvZy50eHQKICBtYXhtZW09JChzc3RhdCAtLWZvcm1hdD1NYXhWTVNpemUgLW4gIiR7U0xVUk1fSk9CSUR9LmJhdGNoIiAyPi9kZXYvbnVsbCkKICBlY2hvICIjIyMjIG1heGltdW0gbWVtb3J5IHVzZWQgYnkgJHtTTFVSTV9KT0JJRH0gd2FzOiAke21heG1lbTotbnVsbH0iID4+IG91dHB1dHMvbG9nLnR4dAogIGF2ZWNwdT0kKHNzdGF0IC0tZm9ybWF0PUF2ZUNQVSAtbiAiJHtTTFVSTV9KT0JJRH0uYmF0Y2giIDI+L2Rldi9udWxsKQogIGVjaG8gIiMjIyMgYXZlcmFnZSBjcHUgdXNlZCBieSAke1NMVVJNX0pPQklEfSB3YXM6ICR7YXZlY3B1Oi1udWxsfSIgPj4gb3V0cHV0cy9sb2cudHh0CmZpCiMjIEFkZGluZyBhIGxpdHRsZSBsb2dpYyB0byBoYXZlIHNraXAgZmluaXNoZWQgam9icy4KdG91Y2ggb3V0cHV0cy9sb2dzLzQwXzJzMmJfaGlzYXQyX3BET05SMjAxLmZpbmlzaGVkCmBgYAoKIyMgTWFwcGluZyBhZ2FpbnN0IFBBMDEKCkJlZm9yZSBtYXBwaW5nIGFnYWluc3QgdGhlIFBBMDEgc3RyYWluLCBJIHByZXZpb3VzbHkgZG93bmxvYWRlZCB0aGUKcmVmZXJlbmNlIGFzc2VtYmx5IGZyb20gTkNCSSBhcyBhIGNvbXBsZXRlIGdlbmJhbmsgZmlsZSBhbmQgcHV0IGl0IGluCm15ICIke0xJQkRJUn0vZ2Vub21lLyIgZGlyZWN0b3J5IGFzICdwYWVydWdpbm9zYV9wYTAxLmdiJy4KCkkgdGhlbiBwZXJmb3JtZWQgdGhlIGZvbGxvd2luZyB0byBjb252ZXJ0IGl0IHRvIGZhc3RhL2dmZiBhbmQgaW5kZXgKaXQgKG5vdGUgSSBkaWQgdGhpcyBxdWl0ZSBhIHdoaWxlIGFnbywgSSBhbSBqdXN0IHdyaXRpbmcgaXQgaGVyZSBmb3IKY29tcGxldGVuZXNzKSwgYWxzbyBoZXJlIGlzIGFuIGV4dHJhIHNhbG1vbiBpbmRleCBvcGVyYXRpb24gZm9yIGZ1bjoKCmBgYHtiYXNoIGNvbnZlcnRfaW5kZXhfcGEwMSwgZXZhbD1GQUxTRX0Kc3RhcnQ9JChwd2QpCmNkIH4vbGlicmFyaWVzL2dlbm9tZQpjeW9hIC0tdGFzayBjb252ZXJ0IC0tbWV0aG9kIGdiMmdmZiAtLWlucHV0IHBhZXJ1Z2lub3NhX3BhMDEuZ2IKY3lvYSAtLXRhc2sgaW5kZXggLS1tZXRob2QgaW5kZXhoaXNhdCAtLWlucHV0IHBhZXJ1Z2lub3NhX3BhMDEuZmFzdGEgLS1zcGVjaWVzIHBhZXJ1Z2lub3NhX3BhMDEKY3lvYSAtLXRhc2sgaW5kZXggLS1tZXRob2QgaW5kZXhzYWxtb24gLS1pbnB1dCBwYWVydWdpbm9zYV9wYTAxX2Nkcy5mYXN0YSAtLXNwZWNpZXMgcGFlcnVnaW5vc2FfcGEwMQpjZCAkc3RhcnQKYGBgCgpUaGlzIGlzIG5lYXJseSBpZGVudGljYWwgdG8gdGhlIHByZXZpb3VzIHN0ZXAsIGV4Y2VwdCBpdCB1c2VzIHRoZQp1bmFsaWduZWQgcmVhZHMgZnJvbSB0aGUgcHJldmlvdXMgc3RlcCwgYW5kIEkgZXhwbGljaXRseSBzdGF0ZSB0aGF0IEkKd2FudCBodHNlcSB0byBjb3VudCB0aGUgZ2VuZXMgYnkgdGhlIGxvY3VzX3RhZyB0YWdzLgoKYGBge2Jhc2ggaGlzYXRfcGEwMV91bmFsaWduZWQsIGV2YWw9RkFMU0V9CmNkIHByZXByb2Nlc3NpbmcKc3RhcnQ9JChwd2QpCmZvciBpIGluICQoL2Jpbi9scyk7IGRvCiAgICBjZCAke2l9CiAgICBjeW9hIC0tdGFzayBtYXAgLS1tZXRob2QgaGlzYXQgLS1zcGVjaWVzIHBhZXJ1Z2lub3NhX3BhMDEgXAogICAgICAgICAtLWh0c2VxX3R5cGUgZ2VuZSAtLWh0c2VxX2lkIGxvY3VzX3RhZyBcCiAgICAgICAgIC0taW5wdXQgJCgvYmluL2xzIG91dHB1dHMvNDBoaXNhdDJfcERPTlIyMDEvKnVuYWxjb24qLmZhc3RxLnh6IHwgdHIgJ1xuJyAnOicgfCBzZWQgJ3MvOiQvL2cnKQogICAgY2QgJHN0YXJ0CmRvbmUKYGBgCgpUaGUgcmVzdWx0aW5nIHNjcmlwdHMgYXJlIG5lYXJseSBpZGVudGljYWwgdG8gdGhvc2UgZm9yIHBET05SLCBleGNlcHQKb2YgY291cnNlIHRoZSBzcGVjaWVzIHVzZWQgYW5kIGlucHV0czoKCmBgYHtiYXNoIGhpc2F0X3BhZXJ1Z2lub3NhLCBldmFsPUZBTFNFfQojIS91c3IvYmluL2VudiBiYXNoCiNTQkFUQ0ggLS1leHBvcnQ9QUxMCiNTQkFUQ0ggLS1tYWlsLXR5cGU9Tk9ORQojU0JBVENIIC0tb3Blbi1tb2RlPWFwcGVuZAojU0JBVENIIC0tY2hkaXI9L2ZzL2NiY2ItbGFiL25lbHNheWVkL3NjcmF0Y2gvYXRiL2RuYXNlcS9wYWVydWdpbm9zYV9wbGFzbWlkc18yMDIyL3ByZXByb2Nlc3NpbmcvcG9vbGEKI1NCQVRDSCAtLXBhcnRpdGlvbj1kcGFydAojU0JBVENIIC0tcW9zPXdvcmtzdGF0aW9uIC0tbmljZT0xMAojU0JBVENIIC0tbm9kZXM9MSAtLXJlcXVldWUKI1NCQVRDSCAtLXRpbWU9MTA6MDA6MDAKI1NCQVRDSCAtLWpvYi1uYW1lPTQwaGlzYXQyX3BhZXJ1Z2lub3NhX3BhMDFfZ2Vub21lCiNTQkFUQ0ggLS1tZW09MjRHCiNTQkFUQ0ggLS1jcHVzLXBlci10YXNrPTQKI1NCQVRDSCAtLW91dHB1dD1vdXRwdXRzL2xvZy50eHQuc2JhdGNoCnNldCAtbyBlcnJleGl0CnNldCAtbyBlcnJ0cmFjZQpzZXQgLW8gcGlwZWZhaWwKZXhwb3J0IExFU1M9Jy0tYnVmZmVycyAwJwpzY3JpcHQ9IiQocHdkKS8kMCIKZXJyKCkgewogICAgZWNobyAiRXJyb3Igb2NjdXJyZWQ6IgogICAgYXdrICdOUj5MLTQgJiYgTlI8TCs0IHsgcHJpbnRmICIlLTVkJTNzJXNcbiIsTlIsKE5SPT1MPyI+Pj4iOiIiKSwkc2NyaXB0IH0nIEw9JDEgJHNjcmlwdAp9CnRyYXAgJ2VyciAkTElORU5PJyBFUlIKCmVjaG8gIiMjIFN0YXJ0ZWQgL2ZzL2NiY2ItbGFiL25lbHNheWVkL3NjcmF0Y2gvYXRiL2RuYXNlcS9wYWVydWdpbm9zYV9wbGFzbWlkc18yMDIyL3ByZXByb2Nlc3NpbmcvcG9vbGEvc2NyaXB0cy80MGhpc2F0Ml9wYWVydWdpbm9zYV9wYTAxX2dlbm9tZS5zaCBhdCAkKGRhdGUpIG9uICQoaG9zdG5hbWUpIHdpdGggaWQgJHtTTFVSTV9KT0JJRH0uIiA+PiBvdXRwdXRzL2xvZy50eHQKCm1vZHVsZSBhZGQgaGlzYXQyIHNhbXRvb2xzIGh0c2VxIGJhbXRvb2xzCgojIyBUaGlzIGlzIGEgaGlzYXQyIGFsaWdubWVudCBvZiAgLTEgPChsZXNzIC9mcy9jYmNiLWxhYi9uZWxzYXllZC9zY3JhdGNoL2F0Yi9kbmFzZXEvcGFlcnVnaW5vc2FfcGxhc21pZHNfMjAyMi9wcmVwcm9jZXNzaW5nL3Bvb2xhL291dHB1dHMvNDBoaXNhdDJfcERPTlIyMDEvcG9vbGFfdW5hbGNvbl9wRE9OUjIwMV9nZW5vbWUuMS5mYXN0cS54eikgLTIgPChsZXNzIC9mcy9jYmNiLWxhYi9uZWxzYXllZC9zY3JhdGNoL2F0Yi9kbmFzZXEvcGFlcnVnaW5vc2FfcGxhc21pZHNfMjAyMi9wcmVwcm9jZXNzaW5nL3Bvb2xhL291dHB1dHMvNDBoaXNhdDJfcERPTlIyMDEvcG9vbGFfdW5hbGNvbl9wRE9OUjIwMV9nZW5vbWUuMi5mYXN0cS54eikgIGFnYWluc3QgL2NiY2Job21lcy9hYmVsZXcvbGlicmFyaWVzL2dlbm9tZS9pbmRleGVzL3BhZXJ1Z2lub3NhX3BhMDEKCm1rZGlyIC1wIG91dHB1dHMvNDBoaXNhdDJfcGFlcnVnaW5vc2FfcGEwMQpoaXNhdDIgLXggL2NiY2Job21lcy9hYmVsZXcvbGlicmFyaWVzL2dlbm9tZS9pbmRleGVzL3BhZXJ1Z2lub3NhX3BhMDEgIFwKICAtcCA0IFwKICAtcSAgIC0xIDwobGVzcyAvZnMvY2JjYi1sYWIvbmVsc2F5ZWQvc2NyYXRjaC9hdGIvZG5hc2VxL3BhZXJ1Z2lub3NhX3BsYXNtaWRzXzIwMjIvcHJlcHJvY2Vzc2luZy9wb29sYS9vdXRwdXRzLzQwaGlzYXQyX3BET05SMjAxL3Bvb2xhX3VuYWxjb25fcERPTlIyMDFfZ2Vub21lLjEuZmFzdHEueHopIC0yIDwobGVzcyAvZnMvY2JjYi1sYWIvbmVsc2F5ZWQvc2NyYXRjaC9hdGIvZG5hc2VxL3BhZXJ1Z2lub3NhX3BsYXNtaWRzXzIwMjIvcHJlcHJvY2Vzc2luZy9wb29sYS9vdXRwdXRzLzQwaGlzYXQyX3BET05SMjAxL3Bvb2xhX3VuYWxjb25fcERPTlIyMDFfZ2Vub21lLjIuZmFzdHEueHopICBcCiAgLS1waHJlZDMzIFwKICAtLXVuIG91dHB1dHMvNDBoaXNhdDJfcGFlcnVnaW5vc2FfcGEwMS9wb29sYV91bmFsZGlzX3BhZXJ1Z2lub3NhX3BhMDFfZ2Vub21lLmZhc3RxIFwKICAtLWFsIG91dHB1dHMvNDBoaXNhdDJfcGFlcnVnaW5vc2FfcGEwMS9wb29sYV9hbGRpc19wYWVydWdpbm9zYV9wYTAxX2dlbm9tZS5mYXN0cSBcCiAgLS11bi1jb25jIG91dHB1dHMvNDBoaXNhdDJfcGFlcnVnaW5vc2FfcGEwMS9wb29sYV91bmFsY29uX3BhZXJ1Z2lub3NhX3BhMDFfZ2Vub21lLmZhc3RxIFwKICAtLWFsLWNvbmMgb3V0cHV0cy80MGhpc2F0Ml9wYWVydWdpbm9zYV9wYTAxL3Bvb2xhX2FsY29uX3BhZXJ1Z2lub3NhX3BhMDFfZ2Vub21lLmZhc3RxIFwKICAtUyBvdXRwdXRzLzQwaGlzYXQyX3BhZXJ1Z2lub3NhX3BhMDEvcG9vbGFfcGFlcnVnaW5vc2FfcGEwMV9nZW5vbWUuc2FtIFwKICAyPm91dHB1dHMvNDBoaXNhdDJfcGFlcnVnaW5vc2FfcGEwMS9oaXNhdDJfcGFlcnVnaW5vc2FfcGEwMV9nZW5vbWVfcG9vbGEuc3RkZXJyIFwKICAxPm91dHB1dHMvNDBoaXNhdDJfcGFlcnVnaW5vc2FfcGEwMS9oaXNhdDJfcGFlcnVnaW5vc2FfcGEwMV9nZW5vbWVfcG9vbGEuc3Rkb3V0CgoKIyMgVGhlIGZvbGxvd2luZyBsaW5lcyBnaXZlIHN0YXR1cyBjb2RlcyBhbmQgc29tZSBsb2dnaW5nCmVjaG8gIiMjIEpvYiBzdGF0dXM6ICQ/ICIgPj4gb3V0cHV0cy9sb2cudHh0CmVjaG8gIiMjICQoaG9zdG5hbWUpIEZpbmlzaGVkICR7U0xVUk1fSk9CSUR9IDQwaGlzYXQyX3BhZXJ1Z2lub3NhX3BhMDFfZ2Vub21lLnNoIGF0ICQoZGF0ZSksIGl0IHRvb2sgJCgoIFNFQ09ORFMgLyA2MCApKSBtaW51dGVzLiIgPj4gb3V0cHV0cy9sb2cudHh0CgppZiBbWyAteCAiJChjb21tYW5kIC12IHNzdGF0KSIgJiYgISAteiAiJHtTTFVSTV9KT0JJRH0iIF1dOyB0aGVuCiAgd2FsbHRpbWU9JChzY29udHJvbCBzaG93IGpvYiAiJHtTTFVSTV9KT0JJRH0iIHwgZ3JlcCBSdW5UaW1lIHwgcGVybCAtRicvXHMrfD0vJyAtbGFuZSAne3ByaW50ICRGWzJdfScgMj4vZGV2L251bGwpCiAgZWNobyAiIyMjIyB3YWxsdGltZSB1c2VkIGJ5ICR7U0xVUk1fSk9CSUR9IHdhczogJHt3YWxsdGltZTotbnVsbH0iID4+IG91dHB1dHMvbG9nLnR4dAogIG1heG1lbT0kKHNzdGF0IC0tZm9ybWF0PU1heFZNU2l6ZSAtbiAiJHtTTFVSTV9KT0JJRH0uYmF0Y2giIDI+L2Rldi9udWxsKQogIGVjaG8gIiMjIyMgbWF4aW11bSBtZW1vcnkgdXNlZCBieSAke1NMVVJNX0pPQklEfSB3YXM6ICR7bWF4bWVtOi1udWxsfSIgPj4gb3V0cHV0cy9sb2cudHh0CiAgYXZlY3B1PSQoc3N0YXQgLS1mb3JtYXQ9QXZlQ1BVIC1uICIke1NMVVJNX0pPQklEfS5iYXRjaCIgMj4vZGV2L251bGwpCiAgZWNobyAiIyMjIyBhdmVyYWdlIGNwdSB1c2VkIGJ5ICR7U0xVUk1fSk9CSUR9IHdhczogJHthdmVjcHU6LW51bGx9IiA+PiBvdXRwdXRzL2xvZy50eHQKZmkKIyMgQWRkaW5nIGEgbGl0dGxlIGxvZ2ljIHRvIGhhdmUgc2tpcCBmaW5pc2hlZCBqb2JzLgp0b3VjaCBvdXRwdXRzL2xvZ3MvNDBoaXNhdDJfcGFlcnVnaW5vc2FfcGEwMV9nZW5vbWUuZmluaXNoZWQKYGBgCgpJIHdpbGwgbGVhdmUgdGhlIG5lYXJseSBpZGVudGljYWwgY29udmVyc2lvbiBvZiB0aGUgc2FtIGFsaWdubWVudHMgdG8KY29tcHJlc3NlZC9zb3J0ZWQvaW5kZXhlZCBiYW0gYXMgYW4gZXhlcmNpc2UgdG8gdGhlIHJlYWRlciwgYnV0IGhlcmUKaXMgdGhlIHJlc3VsdGluZyBodHNlcSBzY3JpcHQ6CgpgYGB7YmFzaCBodHNlcV9leGFtcGxlLCBldmFsPUZBTFNFfQojIS91c3IvYmluL2VudiBiYXNoCiNTQkFUQ0ggLS1leHBvcnQ9QUxMCiNTQkFUQ0ggLS1tYWlsLXR5cGU9Tk9ORQojU0JBVENIIC0tb3Blbi1tb2RlPWFwcGVuZAojU0JBVENIIC0tY2hkaXI9L2ZzL2NiY2ItbGFiL25lbHNheWVkL3NjcmF0Y2gvYXRiL2RuYXNlcS9wYWVydWdpbm9zYV9wbGFzbWlkc18yMDIyL3ByZXByb2Nlc3NpbmcvcG9vbGEKI1NCQVRDSCAtLXBhcnRpdGlvbj1kcGFydAojU0JBVENIIC0tcW9zPXRocm91Z2hwdXQgLS1uaWNlPTEwCiNTQkFUQ0ggLS1ub2Rlcz0xIC0tcmVxdWV1ZQojU0JBVENIIC0tdGltZT0xMjowMDowMAojU0JBVENIIC0tam9iLW5hbWU9NDBfM2h0c19wb29sYV9hbGxfaGlzYXQyX3BhZXJ1Z2lub3NhX3BhMDFfc25vX2dlbmVfQWxpYXMKI1NCQVRDSCAtLW1lbT02RwojU0JBVENIIC0tY3B1cy1wZXItdGFzaz0xCiNTQkFUQ0ggLS1vdXRwdXQ9b3V0cHV0cy9sb2cudHh0LnNiYXRjaApzZXQgLW8gZXJyZXhpdApzZXQgLW8gZXJydHJhY2UKc2V0IC1vIHBpcGVmYWlsCmV4cG9ydCBMRVNTPSctLWJ1ZmZlcnMgMCcKc2NyaXB0PSIkKHB3ZCkvJDAiCmVycigpIHsKICAgIGVjaG8gIkVycm9yIG9jY3VycmVkOiIKICAgIGF3ayAnTlI+TC00ICYmIE5SPEwrNCB7IHByaW50ZiAiJS01ZCUzcyVzXG4iLE5SLChOUj09TD8iPj4+IjoiIiksJHNjcmlwdCB9JyBMPSQxICRzY3JpcHQKfQp0cmFwICdlcnIgJExJTkVOTycgRVJSCgplY2hvICIjIyBTdGFydGVkIC9mcy9jYmNiLWxhYi9uZWxzYXllZC9zY3JhdGNoL2F0Yi9kbmFzZXEvcGFlcnVnaW5vc2FfcGxhc21pZHNfMjAyMi9wcmVwcm9jZXNzaW5nL3Bvb2xhL3NjcmlwdHMvNDBfM2h0c19wb29sYV9hbGxfaGlzYXQyX3BhZXJ1Z2lub3NhX3BhMDFfc25vX2dlbmVfQWxpYXMuc2ggYXQgJChkYXRlKSBvbiAkKGhvc3RuYW1lKSB3aXRoIGlkICR7U0xVUk1fSk9CSUR9LiIgPj4gb3V0cHV0cy9sb2cudHh0Cgptb2R1bGUgYWRkIGh0c2VxCgojIyBDb3VudGluZyB0aGUgbnVtYmVyIG9mIGhpdHMgaW4gb3V0cHV0cy80MGhpc2F0Ml9wYWVydWdpbm9zYV9wYTAxL3Bvb2xhX3BhZXJ1Z2lub3NhX3BhMDFfZ2Vub21lLXBhaXJlZC5iYW0gZm9yIGVhY2ggZmVhdHVyZSBmb3VuZCBpbiAvY2JjYmhvbWVzL2FiZWxldy9saWJyYXJpZXMvZ2Vub21lL3BhZXJ1Z2lub3NhX3BhMDEuZ2ZmCmh0c2VxLWNvdW50ICAtLWhlbHAgMj4mMSB8IHRhaWwgLW4gMwpodHNlcS1jb3VudCBcCiAgLXEgLWYgYmFtIC1zIG5vICAtLXR5cGUgZ2VuZSAgLS1pZGF0dHIgbG9jdXNfdGFnIFwKICBvdXRwdXRzLzQwaGlzYXQyX3BhZXJ1Z2lub3NhX3BhMDEvcG9vbGFfcGFlcnVnaW5vc2FfcGEwMV9nZW5vbWUtcGFpcmVkLmJhbSBcCiAgL2NiY2Job21lcy9hYmVsZXcvbGlicmFyaWVzX2ZzL2dlbm9tZS9wYWVydWdpbm9zYV9wYTAxLmdmZiBcCiAgMj5vdXRwdXRzLzQwaGlzYXQyX3BhZXJ1Z2lub3NhX3BhMDEvcG9vbGFfcGFlcnVnaW5vc2FfcGEwMV9nZW5vbWUtcGFpcmVkX2FsbF9zbm9fZ2VuZV9BbGlhcy5zdGRlcnIgXAogIDE+b3V0cHV0cy80MGhpc2F0Ml9wYWVydWdpbm9zYV9wYTAxL3Bvb2xhX3BhZXJ1Z2lub3NhX3BhMDFfZ2Vub21lLXBhaXJlZF9hbGxfc25vX2dlbmVfQWxpYXMuY291bnQgJiYgXAogICAgeHogLWYgLTllIG91dHB1dHMvNDBoaXNhdDJfcGFlcnVnaW5vc2FfcGEwMS9wb29sYV9wYWVydWdpbm9zYV9wYTAxX2dlbm9tZS1wYWlyZWRfYWxsX3Nub19nZW5lX0FsaWFzLmNvdW50IDI+b3V0cHV0cy80MGhpc2F0Ml9wYWVydWdpbm9zYV9wYTAxL3Bvb2xhX3BhZXJ1Z2lub3NhX3BhMDFfZ2Vub21lLXBhaXJlZF9hbGxfc25vX2dlbmVfQWxpYXMuc3RkZXJyLnh6IDE+b3V0cHV0cy80MGhpc2F0Ml9wYWVydWdpbm9zYV9wYTAxL3Bvb2xhX3BhZXJ1Z2lub3NhX3BhMDFfZ2Vub21lLXBhaXJlZF9hbGxfc25vX2dlbmVfQWxpYXMuY291bnQueHoKCgojIyBUaGUgZm9sbG93aW5nIGxpbmVzIGdpdmUgc3RhdHVzIGNvZGVzIGFuZCBzb21lIGxvZ2dpbmcKZWNobyAiIyMgSm9iIHN0YXR1czogJD8gIiA+PiBvdXRwdXRzL2xvZy50eHQKZWNobyAiIyMgJChob3N0bmFtZSkgRmluaXNoZWQgJHtTTFVSTV9KT0JJRH0gNDBfM2h0c19wb29sYV9hbGxfaGlzYXQyX3BhZXJ1Z2lub3NhX3BhMDFfc25vX2dlbmVfQWxpYXMuc2ggYXQgJChkYXRlKSwgaXQgdG9vayAkKCggU0VDT05EUyAvIDYwICkpIG1pbnV0ZXMuIiA+PiBvdXRwdXRzL2xvZy50eHQKCmlmIFtbIC14ICIkKGNvbW1hbmQgLXYgc3N0YXQpIiAmJiAhIC16ICIke1NMVVJNX0pPQklEfSIgXV07IHRoZW4KICB3YWxsdGltZT0kKHNjb250cm9sIHNob3cgam9iICIke1NMVVJNX0pPQklEfSIgfCBncmVwIFJ1blRpbWUgfCBwZXJsIC1GJy9ccyt8PS8nIC1sYW5lICd7cHJpbnQgJEZbMl19JyAyPi9kZXYvbnVsbCkKICBlY2hvICIjIyMjIHdhbGx0aW1lIHVzZWQgYnkgJHtTTFVSTV9KT0JJRH0gd2FzOiAke3dhbGx0aW1lOi1udWxsfSIgPj4gb3V0cHV0cy9sb2cudHh0CiAgbWF4bWVtPSQoc3N0YXQgLS1mb3JtYXQ9TWF4Vk1TaXplIC1uICIke1NMVVJNX0pPQklEfS5iYXRjaCIgMj4vZGV2L251bGwpCiAgZWNobyAiIyMjIyBtYXhpbXVtIG1lbW9yeSB1c2VkIGJ5ICR7U0xVUk1fSk9CSUR9IHdhczogJHttYXhtZW06LW51bGx9IiA+PiBvdXRwdXRzL2xvZy50eHQKICBhdmVjcHU9JChzc3RhdCAtLWZvcm1hdD1BdmVDUFUgLW4gIiR7U0xVUk1fSk9CSUR9LmJhdGNoIiAyPi9kZXYvbnVsbCkKICBlY2hvICIjIyMjIGF2ZXJhZ2UgY3B1IHVzZWQgYnkgJHtTTFVSTV9KT0JJRH0gd2FzOiAke2F2ZWNwdTotbnVsbH0iID4+IG91dHB1dHMvbG9nLnR4dApmaQojIyBBZGRpbmcgYSBsaXR0bGUgbG9naWMgdG8gaGF2ZSBza2lwIGZpbmlzaGVkIGpvYnMuCnRvdWNoIG91dHB1dHMvbG9ncy80MF8zaHRzX3Bvb2xhX2FsbF9oaXNhdDJfcGFlcnVnaW5vc2FfcGEwMV9zbm9fZ2VuZV9sb2N1c190YWcuZmluaXNoZWQKYGBgCgojIFZhcmlhbnQgc2VhcmNoaW5nCgpJIGhhdmUgdHdvIG1ldGhvZHMgb2YgdmFyaWFudCBzZWFyY2hpbmcgaW4gbXkgcmVwZXRvaXJlLCBtcGlsZXVwIGFuZApmcmVlYmF5ZXMuICBJIG9ubHkgbGVhcm5lZCBmcmVlYmF5ZXMgcmVjZW50bHkgYW5kIHNvIGNob3NlIGl0LiAgTXkKY3VycmVudCBpbXBsZW1lbnRhdGlvbiBhbHNvIHNwaW5zIG9mZiBhIHNlcmllcyBvZiBzY3JpcHRzIHRvIHBhcnNlIHRoZQpmcmVlYmF5ZXMgb3V0cHV0IGRvd24gaW50byBhIHNpbXBsZXIgdHN2IGZvcm1hdC4gIEFsc28sIHVubGlrZSB0aGUKbWFwcGluZyBzY3JpcHQgYWJvdmUsIHRoZSBmcmVlYmF5ZXMgaW52b2NhdGlvbiBpdHNlbGYgaW5jbHVkZXMgdGhlCmNvbnZlcnNpb24gb2YgdGhlIHRleHQgdmNmIGZvcm1hdCB0byBhIHNvcnRlZC9jb21wcmVzc2VkL2luZGV4IGJjZgpmb3JtYXQuCgpOb3RlIGFsc28gdGhhdCB0aGUgcHJldmlvdXMgc3RlcCBhbHNvIGNyZWF0ZXMgMyBiYW0gZmlsZXM6CgoxLiAgJHtzYW1wbGV9XyR7c3BlY2llc31fZ2Vub21lLmJhbTogQWxsIGFsaWdubWVudHMKMi4gICR7c2FtcGxlfV8ke3NwZWNpZXN9X2dlbm9tZS1wYWlyZWQuYmFtOiBBbGwgcHJvcGVybHkgcGFpcmVkCiAgICBhbGlnbm1lbnRzLCB0aGlzIGlzIHRoZSBkZWZhdWx0IHVzZWQgYnkgaHRzZXEgd2hlbiBhdmFpbGFibGUuCjMuICAke3NhbXBsZX1fJHtzcGVjaWVzfV9nZW5vbWUtc29ydGVkX25vbWlzbWF0Y2guYmFtOiBUaGlzIGlzCiAgICBmaWx0ZXJlZCBmb3IgdGhlIHNldCBvZiBhbGlnbm1lbnRzIHdpdGggbm8gbWlzbWF0Y2hlcy4KClNpbmNlIHJ1bm5pbmcgdGhlc2Ugc2FtcGxlcywgSSBhZGRlZCBhbm90aGVyIHN1ZmZpeCB0byB0aGUgaGlzYXQKb3V0cHV0IGRpcmVjdG9yaWVzLCBkZXBlbmRpbmcgb24gd2hldGhlciB0aGUgLWsgcGFyYW1ldGVyIGlzIHVzZWQgdG8KbGltaXQgdGhlIG51bWJlciBvZiBhbGlnbm1lbnRzIHJlcG9ydGVkLiAgVGhhdCBpcyBub3QgcGFydGljdWxhcmx5CnJlbGV2YW50IGhlcmUsIGJ1dCB3b3J0aCBub3RpbmcuCgpgYGB7YmFzaCBmcmVlYmF5ZXMsIGV2YWw9RkFMU0V9CmNkIHByZXByb2Nlc3NpbmcKc3RhcnQ9JChwd2QpCmZvciBpIGluICQoL2Jpbi9scyk7IGRvCiAgICBjZCAke2l9CiAgICBjeW9hIC0tdGFzayBzbnAgLS1tZXRob2QgZnJlZWJheWVzIC0tc3BlY2llcyBwYWVydWdpbm9zYV9wYTAxIFwKICAgICAgICAgLS1pbnB1dCAkKC9iaW4vbHMgb3V0cHV0cy80MGhpc2F0Ml9wYWVydWdpbm9zYV9wYTAxLypfZ2Vub21lLmJhbSkKICAgIGNkICRzdGFydApkb25lCmBgYAoKIyMjIFNpbmdsZSBmcmVlYmF5ZXMgaW52b2NhdGlvbgoKYGBge2Jhc2ggZnJlZWJheWVzX3NjcmlwdCwgZXZhbD1GQUxTRX0KIyEvdXNyL2Jpbi9lbnYgYmFzaAojU0JBVENIIC0tZXhwb3J0PUFMTAojU0JBVENIIC0tbWFpbC10eXBlPU5PTkUKI1NCQVRDSCAtLW9wZW4tbW9kZT1hcHBlbmQKI1NCQVRDSCAtLWNoZGlyPS9tbnQvY2JjYi9mczAxX2FiZWxldy9jYmNiLWxhYi9uZWxzYXllZC9zY3JhdGNoL2F0Yi9kbmFzZXEvcGFlcnVnaW5vc2FfcGxhc21pZHNfMjAyMi9wcmVwcm9jZXNzaW5nL3Bvb2xhCiNTQkFUQ0ggLS1wYXJ0aXRpb249ZHBhcnQKI1NCQVRDSCAtLXFvcz13b3Jrc3RhdGlvbiAtLW5pY2U9MTAKI1NCQVRDSCAtLW5vZGVzPTEgLS1yZXF1ZXVlCiNTQkFUQ0ggLS10aW1lPTEwOjAwOjAwCiNTQkFUQ0ggLS1qb2ItbmFtZT00MGZyZWViYXllc19wYWVydWdpbm9zYV9wYTAxCiNTQkFUQ0ggLS1tZW09NDhHCiNTQkFUQ0ggLS1jcHVzLXBlci10YXNrPTQKI1NCQVRDSCAtLW91dHB1dD1vdXRwdXRzL2xvZy50eHQuc2JhdGNoCnNldCAtbyBlcnJleGl0CnNldCAtbyBlcnJ0cmFjZQpzZXQgLW8gcGlwZWZhaWwKZXhwb3J0IExFU1M9Jy0tYnVmZmVycyAwJwpzY3JpcHQ9IiQocHdkKS8kMCIKZXJyKCkgewogICAgZWNobyAiRXJyb3Igb2NjdXJyZWQ6IgogICAgYXdrICdOUj5MLTQgJiYgTlI8TCs0IHsgcHJpbnRmICIlLTVkJTNzJXNcbiIsTlIsKE5SPT1MPyI+Pj4iOiIiKSwkc2NyaXB0IH0nIEw9JDEgJHNjcmlwdAp9CnRyYXAgJ2VyciAkTElORU5PJyBFUlIKCmVjaG8gIiMjIFN0YXJ0ZWQgL21udC9jYmNiL2ZzMDFfYWJlbGV3L2NiY2ItbGFiL25lbHNheWVkL3NjcmF0Y2gvYXRiL2RuYXNlcS9wYWVydWdpbm9zYV9wbGFzbWlkc18yMDIyL3ByZXByb2Nlc3NpbmcvcG9vbGEvc2NyaXB0cy80MGZyZWViYXllc19wYWVydWdpbm9zYV9wYTAxLnNoIGF0ICQoZGF0ZSkgb24gJChob3N0bmFtZSkgd2l0aCBpZCAke1NMVVJNX0pPQklEfS4iID4+IG91dHB1dHMvbG9nLnR4dAoKbW9kdWxlIGFkZCBmcmVlYmF5ZXMgbGliZ3NsLzIuNy4xIGxpYmh0cy8xLjEzIHNhbXRvb2xzLzEuMTMgYmNmdG9vbHMgdmNmdG9vbHMKCiMjIFVzZSBmcmVlYmF5ZXMsIGJjZnRvb2xzLCBhbmQgdmNmdXRpbHMgdG8gZ2V0IHNvbWUgaWRlYSBhYm91dCBob3cgbWFueSB2YXJpYW50IHBvc2l0aW9ucyBhcmUgaW4gdGhlIGRhdGEuCgpta2RpciAtcCBvdXRwdXRzLzQwZnJlZWJheWVzX3BhZXJ1Z2lub3NhX3BhMDEKZnJlZWJheWVzIC1mIC9ob21lL3RyZXkvbGlicmFyaWVzX2ZzL2dlbm9tZS9wYWVydWdpbm9zYV9wYTAxLmZhc3RhIFwKICAtdiBvdXRwdXRzLzQwZnJlZWJheWVzX3BhZXJ1Z2lub3NhX3BhMDEvcGFlcnVnaW5vc2FfcGEwMS52Y2YgXAogIG91dHB1dHMvNDBoaXNhdDJfcGFlcnVnaW5vc2FfcGEwMS9wb29sYV9wYWVydWdpbm9zYV9wYTAxX2dlbm9tZS5iYW0gXAogIDE+b3V0cHV0cy80MGZyZWViYXllc19wYWVydWdpbm9zYV9wYTAxL3BhZXJ1Z2lub3NhX3BhMDEuc3Rkb3V0IFwKICAyPm91dHB1dHMvNDBmcmVlYmF5ZXNfcGFlcnVnaW5vc2FfcGEwMS9wYWVydWdpbm9zYV9wYTAxLnN0ZGVycgpiY2Z0b29scyBjb252ZXJ0IG91dHB1dHMvNDBmcmVlYmF5ZXNfcGFlcnVnaW5vc2FfcGEwMS9wYWVydWdpbm9zYV9wYTAxLnZjZiBcCiAgLU9iIC1vIG91dHB1dHMvNDBmcmVlYmF5ZXNfcGFlcnVnaW5vc2FfcGEwMS9wYWVydWdpbm9zYV9wYTAxLmJjZiBcCiAgMj4+b3V0cHV0cy80MGZyZWViYXllc19wYWVydWdpbm9zYV9wYTAxL3BhZXJ1Z2lub3NhX3BhMDEuc3RkZXJyIFwKICAxPm91dHB1dHMvNDBmcmVlYmF5ZXNfcGFlcnVnaW5vc2FfcGEwMS9wYWVydWdpbm9zYV9wYTAxLnN0ZG91dApiY2Z0b29scyBpbmRleCBvdXRwdXRzLzQwZnJlZWJheWVzX3BhZXJ1Z2lub3NhX3BhMDEvcGFlcnVnaW5vc2FfcGEwMS5iY2YgXAogIDI+Pm91dHB1dHMvNDBmcmVlYmF5ZXNfcGFlcnVnaW5vc2FfcGEwMS9wYWVydWdpbm9zYV9wYTAxLnN0ZGVyciBcCiAgMT4+b3V0cHV0cy80MGZyZWViYXllc19wYWVydWdpbm9zYV9wYTAxL3BhZXJ1Z2lub3NhX3BhMDEuc3Rkb3V0CnJtIG91dHB1dHMvNDBmcmVlYmF5ZXNfcGFlcnVnaW5vc2FfcGEwMS9wYWVydWdpbm9zYV9wYTAxLnZjZgoKCiMjIFRoZSBmb2xsb3dpbmcgbGluZXMgZ2l2ZSBzdGF0dXMgY29kZXMgYW5kIHNvbWUgbG9nZ2luZwplY2hvICJKb2Igc3RhdHVzOiAkPyAiID4+IG91dHB1dHMvbG9nLnR4dAplY2hvICIgICQoaG9zdG5hbWUpIEZpbmlzaGVkICR7U0xVUk1fSk9CSUR9IDQwZnJlZWJheWVzX3BhZXJ1Z2lub3NhX3BhMDEuc2ggYXQgJChkYXRlKSwgaXQgdG9vayAkKCggU0VDT05EUyAvIDYwICkpIG1pbnV0ZXMuIiA+PiBvdXRwdXRzL2xvZy50eHQKCmlmIFtbIC14ICIkKGNvbW1hbmQgLXYgc3N0YXQpIiAmJiAhIC16ICIke1NMVVJNX0pPQklEfSIgXV07IHRoZW4KICB3YWxsdGltZT0kKHNjb250cm9sIHNob3cgam9iICIke1NMVVJNX0pPQklEfSIgfCBncmVwIFJ1blRpbWUgfCBwZXJsIC1GJy9ccyt8PS8nIC1sYW5lICd7cHJpbnQgJEZbMl19JyB8IGhlYWQgLW4gMSAyPi9kZXYvbnVsbCkKICBlY2hvICIgIHdhbGx0aW1lIHVzZWQgYnkgJHtTTFVSTV9KT0JJRH0gd2FzOiAke3dhbGx0aW1lOi1udWxsfSIgPj4gb3V0cHV0cy9sb2cudHh0CiAgbWF4bWVtPSQoc3N0YXQgLS1mb3JtYXQ9TWF4Vk1TaXplIC1uICIke1NMVVJNX0pPQklEfS5iYXRjaCIgMj4vZGV2L251bGwpCiAgZWNobyAiICBtYXhpbXVtIG1lbW9yeSB1c2VkIGJ5ICR7U0xVUk1fSk9CSUR9IHdhczogJHttYXhtZW06LW51bGx9IiA+PiBvdXRwdXRzL2xvZy50eHQKICBlY2hvICIiID4+IG91dHB1dHMvbG9nLnR4dApmaQojIyBBZGRpbmcgYSBsaXR0bGUgbG9naWMgdG8gaGF2ZSBza2lwIGZpbmlzaGVkIGpvYnMuCnRvdWNoIG91dHB1dHMvbG9ncy80MGZyZWViYXllc19wYWVydWdpbm9zYV9wYTAxLmZpbmlzaGVkCmBgYAoKVXBvbiBjb21wbGV0aW9uIHdlIGhhdmUgYSBzZXJpZXMgb2YgZmlsZXMgd2hpY2ggaW4gdGhlb3J5IHNob3VsZApwcm92aWRlIGV2ZXJ5dGhpbmcgb2YgaW50ZXJlc3QgdG8gVmluY2UuICBMZXRzIHBva2UgYXQgdGhlbSBpbiBSIQoKIyBWaW5jZSdzIFJlcXVlc3RzCgpIZXJlIGlzIG15IGVtYWlsIGZyb20gVmluY2Ugd2l0aCBoaXMgaW5pdGlhbCwgcGFyZWQgZG93biBxdWVyaWVzOgoKIgpCYXNlZCBvbiB0aGUgZGF0YSB5b3UgaGF2ZSBhbmFseXplZCwgY2FuIHlvdSBwbGVhc2Ugc2hhcmUgd2l0aCBtZSBhCkV4Y2VsIHNwcmVhZHNoZWV0IChvciBDU1YgZmlsZSkgd2l0aCAyIGNvbHVtbnMgZm9yIGVhY2ggbGlicmFyeSBhbmQKZWFjaCBQQSBnZW5lOgoKMS4gQ292ZXJhZ2UgKFJQS00pIG9yIDAgZm9yIG5vIHJlYWRzCjIuIE51bWJlciBvZiBTTlBzCiIKCiMjIEFubm90YXRpb24KCkkgYWx3YXlzIGdyYWIgbXkgZmF2b3JpdGUgYW5ub3RhdGlvbiBzb3VyY2VzLCBldmVuIGlmIEkgd2lsbCBub3QKcmVhbGx5IHVzZSB0aGVtLiAgSSB1c3VhbGx5IGNvcHkgdGhlIGdmZiBhbmQgZmFzdGEgZm9yIHRoZSB2ZXJzaW9uIG9mCnRoZSBnZW5vbWUgSSB1c2VkIHRvIHRoZSBsb2NhbCBkaXJlY3RvcnksIGJ1dCBJIGRpZCBub3QgYm90aGVyIGZvciB0aGlzLgoKYGBge3IgYW5ub3RhdGlvbn0KcGEwMV9nZmYgPC0gbG9hZF9nZmZfYW5ub3RhdGlvbnMoIn4vbGlicmFyaWVzX2ZzL2dlbm9tZS9wYWVydWdpbm9zYV9wYTAxLmdmZiIsIGlkX2NvbD0ibG9jdXNfdGFnIikKcm93bmFtZXMocGEwMV9nZmYpIDwtIHBhMDFfZ2ZmW1sibG9jdXNfdGFnIl1dCmBgYAoKIyMgRXhwcmVzc2lvbnNldAoKTGV0IHVzIG5vdyBwdWxsIG91dCB0aGUgaHRzZXEgY291bnQgdGFibGVzIGluIG9yZGVyIHRvIG1ha2UgYW4gcnBrbQp0YWJsZS4gIFRoaXMgdXNlcyB0aGUgbWV0YWRhdGEgc2FtcGxlIHNoZWV0IEkgY3JlYXRlZCBpbiB0aGUKc2FtcGxlX3NoZWV0cy8gZGlyZWN0b3J5LgoKSSBib2xkZWQgdGhlIHR3byBtb3N0IGltcG9ydGFudCBjb2x1bW5zIGZvciB0aGUgcHVycG9zZXMgb2YgdGhlc2UgdGFza3MuCgpgYGB7ciBleHByc30KcGEwMV9leHB0IDwtIGNyZWF0ZV9leHB0KCJzYW1wbGVfc2hlZXRzL2FsbF9zYW1wbGVzLnhsc3giLCBnZW5lX2luZm89cGEwMV9nZmYpCnBhMDFfcnBrbSA8LSBub3JtYWxpemVfZXhwdChwYTAxX2V4cHQsIGNvbnZlcnQ9InJwa20iKQoKIyMgUGxvdCBhIGxvZy1zY2FsZSBkZW5zaXR5IG9mIHJwa20gdmFsdWVzLgpwbG90X2JveHBsb3QocGEwMV9leHB0KQpwbG90X2RlbnNpdHkobm9ybWFsaXplX2V4cHQocGEwMV9ycGttLCB0cmFuc2Zvcm09ImxvZzIiLCBmaWx0ZXI9VFJVRSkpCnBsb3Rfbm9uemVybyhwYTAxX2V4cHQpJHBsb3QKCnJwa21fZGYgPC0gYXMuZGF0YS5mcmFtZShleHBycyhwYTAxX3Jwa20pKSAlPiUKICByb3VuZCgyKQp3YW50ZWRfYW5ub3RhdGlvbnMgPC0gZkRhdGEocGEwMV9leHB0KVssIGMoImdlbmUiLCAibG9jdXNfdGFnIildCndhbnRlZF9kYXRhIDwtIG1lcmdlKHdhbnRlZF9hbm5vdGF0aW9ucywgcnBrbV9kZiwKICAgICAgICAgICAgICAgICAgICAgYnk9InJvdy5uYW1lcyIpCnJvd25hbWVzKHdhbnRlZF9kYXRhKSA8LSB3YW50ZWRfZGF0YVtbIlJvdy5uYW1lcyJdXQp3YW50ZWRfZGF0YVtbIlJvdy5uYW1lcyJdXSA8LSBOVUxMCmBgYAoKIyBHYXRoZXJpbmcgbXV0YXRpb25zIG51bWJlcnMgYnkgZ2VuZQoKSSBkbyBub3QgaGF2ZSBhbiBpbnN0YW50IGZ1bmN0aW9uIGZvciB0aGlzIHRhc2ssIGJ1dCBpdCBzaG91bGQgbm90CnByb3ZlIGRpZmZpY3VsdC4KCmBgYHtyIGdhdGhlcl9tdXRhdGlvbnN9CndhbnRlZF92MiA8LSB3YW50ZWRfZGF0YQpmb3IgKHNhbXBsZSBpbiByb3duYW1lcyhwRGF0YShwYTAxX2V4cHQpKSkgewogIGlucHV0X2ZpbGUgPC0gZ2x1ZTo6Z2x1ZSgicHJlcHJvY2Vzc2luZy97c2FtcGxlfS9vdXRwdXRzLzQwZnJlZWJheWVzX3BhZXJ1Z2lub3NhX3BhMDEvcGFlcnVnaW5vc2FfcGEwMV9oaXRzX2J5X2dlbmUudHh0Lnh6IikKICBpbnB1dF9kYXRhIDwtIHJlYWRyOjpyZWFkX3RzdihpbnB1dF9maWxlLCBjb2xfbmFtZXM9YygiZ2VuZSIsICJjaHIiLCAicG9zdGlvbiIsICJudF9zdHJpbmciLCAiYWFfc3RyaW5nIikpCiAgaW5wdXRfY29sdW1uIDwtIGlucHV0X2RhdGEgJT4lCiAgICBkcGx5cjo6Z3JvdXBfYnkoZ2VuZSkgJT4lCiAgICBkcGx5cjo6c3VtbWFyaXplKG49bigpKQogIGNvbG5hbWVzKGlucHV0X2NvbHVtbikgPC0gYygiZ2VuZSIsIHBhc3RlMChzYW1wbGUsICJfbiIpKQogIHdhbnRlZF92MiA8LSBtZXJnZSh3YW50ZWRfdjIsIGlucHV0X2NvbHVtbiwgYnkueD0icm93Lm5hbWVzIiwgYnkueT0iZ2VuZSIpCiAgcm93bmFtZXMod2FudGVkX3YyKSA8LSB3YW50ZWRfdjJbWyJSb3cubmFtZXMiXV0KICB3YW50ZWRfdjJbWyJSb3cubmFtZXMiXV0gPC0gTlVMTAp9CgojIyBXcml0ZSBpdCBvdXQKd3JpdHRlbiA8LSB3cml0ZV94bHN4KGRhdGE9d2FudGVkX3YyLCBleGNlbD0iZXhjZWwvcnBrbV9tdXRhdGlvbnNfcGVyX2dlbmUueGxzeCIpCmBgYAoKCmBgYHtyIHNhdmVtZSwgZXZhbD1GQUxTRX0KcGFuZGVyOjpwYW5kZXIoc2Vzc2lvbkluZm8oKSkKbWVzc2FnZShwYXN0ZTAoIlRoaXMgaXMgaHBnbHRvb2xzIGNvbW1pdDogIiwgZ2V0X2dpdF9jb21taXQoKSkpCnRoaXNfc2F2ZSA8LSBwYXN0ZTAoZ3N1YihwYXR0ZXJuPSJcXC5SbWQiLCByZXBsYWNlPSIiLCB4PXJtZF9maWxlKSwgIi12IiwgdmVyLCAiLnJkYS54eiIpCm1lc3NhZ2UocGFzdGUwKCJTYXZpbmcgdG8gIiwgdGhpc19zYXZlKSkKdG1wIDwtIHNtKHNhdmVtZShmaWxlbmFtZT10aGlzX3NhdmUpKQpgYGAK