1 Introduction

This document seeks to lay out my process in poking at the DNAsequencing results of a series of Pseudomonas aeruginosa PA14 and PAK strains.

If I understand Dr. Lee and co.’s goal, they wish to ensure that these strains are still reasonably close to the associated reference strains. I therefore am running my default trimming/mapping/variant search methods.

I have a single command that can run all of these commands at the same time, but I have been actively breaking my tools recently; so I decided to run them one at a time with the assumption that something would not work (but everything did work on the first try, so that was nice).

1.1 Downloading and sorting the data

I downloaded the .zip archive file using the link in Dr. Lee’s email. I did not save it though, so if we need to download the data again, we will have to go to him. I created my usual work directory ‘preprocessing/’ within this tree and moved it there. I unzipped it and moved each pair of reads to a directory which follows Dr. Lee’s desired naming convention.

I then created the directories: ‘reference/’ and ‘sample_sheets/’. The sample_sheets remained empty for a while, but I immediately downloaded the full genbank flat file for the Pseudomonas PAK strain from NCBI, found here:

https://www.ncbi.nlm.nih.gov/nuccore/NZ_CP020659

Note, that when downloading, one must hit the ‘customize view’ button on the right and ensure that the entire sequence and all annotations are included. Then hit the ‘send to’ button and send it to a file. This file I copied to reference/paeruginosa_pak.gb.

1.2 Creation of the pak reference

Given the full PAK genbank file, I converted it to the expected fasta/gff file for mapping:

cd reference
cyoa --method gb2gff --input paeruginosa_pak.gb

This command created a series of fasta and gff files which provide the coordinates for the various annotations (genes/cds/rRNA/intercds) and sequence for the genome, CDS nucleotides, and amino acids. I then copied the genome/gff files to my global reference directory and prepared it for usage by my favorite mapper:

cd ~/libraries/genome
cyoa --method indexhisat --species paeruginosa_pak

Now all of the pieces are in place for me to play. Each of the following steps was performed twice, once for the PA14 samples, once for the PAK samples. The only difference in the invocations was due to the fact that the PAK annotations provide different tags. E.g. I used the ‘Alias’ tag for PA14 and the ‘locus_tag’ tag for PAK. As a result I am only going to write down in this document the PA14 invocations and assume the reader can figure out the difference.

1.3 Trimming

I have a couple of trimming methods, in this instance I just used the default and will operate under the assumption that it is sufficient until I see otherwise.

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

The above command line invocation produced a series of trimming jobs which when examined look like this (I am only showing examples from PA14_exoUTY, and am leaving off the beginning and end).

1.3.1 Resulting trimmer script

## This is a portion of file:
##  preprocessing/PA14_exoUTY/scripts/01trim_7_UTY_S138_R1_001.sh

module add trimomatic
mkdir -p outputs/01trimomatic
## Note that trimomatic prints all output and errors to STDERR, so send both to output
trimmomatic PE \
  -threads 1 \
  -phred33 \
  7_UTY_S138_R1_001.fastq.gz 7_UTY_S138_R2_001.fastq.gz \
  7_UTY_S138_R1_001-trimmed_paired.fastq 7_UTY_S138_R1_001-trimmed_unpaired.fastq \
  7_UTY_S138_R2_001-trimmed_paired.fastq 7_UTY_S138_R2_001-trimmed_unpaired.fastq \
   ILLUMINACLIP:/fs/cbcb-software/RedHat-8-x86_64/local/cyoa/202302/prefix/lib/perl5/auto/share/dist/Bio-Adventure/genome/adapters.fa:2:20:10:2:keepBothReads  \
  SLIDINGWINDOW:4:20 MINLEN:50 \
  1>outputs/01trimomatic/7_UTY_S138_R1_001-trimomatic.stdout \
  2>outputs/01trimomatic/7_UTY_S138_R1_001-trimomatic.stderr
excepted=$( { grep "Exception" "outputs/01trimomatic/7_UTY_S138_R1_001-trimomatic.stdout" || test $? = 1; } )

One thing I did not include in the above: upon completion, the script aggressively compresses the trimmed output and symbolically links it to r1_trimmed.fastq.xz and r2_trimmed.fastq.xz. Thus any following steps can use the same input name (r1_trimmed.fastq.xz:r2_trimmed.fastq.xz).

1.4 Mapping

My default mappers run the actual alignment, convert it to a compressed/indexed bam, and count it against the reference genome. In this context, the counting is a little silly, but does have the potential to help find duplications and such.

cd preprocessing
start=$(pwd)
for i in $(/bin/ls -d PA14*); do
    cd $i
    cyoa --method hisat --input r1_trimmed.fastq.xz:r2_trimmed.fastq.xz \
         --stranded no --species paeruginosa_pa14 --gff_type gene --gff_tag Alias
    cd $start
done

## Here is what I ran for PAK
cd preprocessing
start=$(pwd)
for i in $(/bin/ls -d PAK*); do
    cd $i
    cyoa --method hisat --input r1_trimmed.fastq.xz:r2_trimmed.fastq.xz \
         --stranded no --species paeruginosa_pa01 --gff_type gene --gff_tag locus_tag
    cd $start
done

1.4.1 The resulting mapper script run by the cluster

Similarly, I am just putting the meaty part.

module add hisat2 samtools htseq bamtools
mkdir -p outputs/40hisat2_paeruginosa_pa14
hisat2 -x ${HOME}/libraries/genome/indexes/paeruginosa_pa14  \
  -p 8 \
  -q   -1 <(less /home/trey/sshfs/scratch/atb/dnaseq/paeruginosa_strains_202304/preprocessing/PA14_exoUTY/r1_trimmed.fastq.xz) -2 <(less /home/trey/sshfs/scratch/atb/dnaseq/paeruginosa_strains_202304/preprocessing/PA14_exoUTY/r2_trimmed.fastq.xz)  \
  --phred33 \
  --un outputs/40hisat2_paeruginosa_pa14/unaldis_paeruginosa_pa14_genome.fastq \
  --al outputs/40hisat2_paeruginosa_pa14/aldis_paeruginosa_pa14_genome.fastq \
  --un-conc outputs/40hisat2_paeruginosa_pa14/unalcon_paeruginosa_pa14_genome.fastq \
  --al-conc outputs/40hisat2_paeruginosa_pa14/alcon_paeruginosa_pa14_genome.fastq \
  -S outputs/40hisat2_paeruginosa_pa14/paeruginosa_pa14_genome.sam \
  2>outputs/40hisat2_paeruginosa_pa14/hisat2_paeruginosa_pa14_genome_PA14_exoUTY.stderr \
  1>outputs/40hisat2_paeruginosa_pa14/hisat2_paeruginosa_pa14_genome_PA14_exoUTY.stdout

1.4.2 Conversion to bam script

The above cyoa invocation also creates this script. It is a little long because it does some checks and creates a couple of filtered versions of the output.

module add samtools bamtools

echo "Starting samtools"
if [[ -f "outputs/40hisat2_paeruginosa_pa14/paeruginosa_pa14_genome.bam" && -f "outputs/40hisat2_paeruginosa_pa14/paeruginosa_pa14_genome.sam" ]]; then
  echo "Both the bam and sam files exist, rerunning."
elif [[ -f "outputs/40hisat2_paeruginosa_pa14/paeruginosa_pa14_genome.bam" ]]; then
  echo "The output file exists, quitting."
  exit 0
elif [[ ! -f "outputs/40hisat2_paeruginosa_pa14/paeruginosa_pa14_genome.sam" ]]; then
  echo "Could not find the samtools input file."
  exit 1
fi

## If a previous sort file exists due to running out of memory,
## then we need to get rid of them first.
## hg38_100_genome-sorted.bam.tmp.0000.bam
if [[ -f "outputs/40hisat2_paeruginosa_pa14/paeruginosa_pa14_genome.bam.tmp.000.bam" ]]; then
  rm -f outputs/40hisat2_paeruginosa_pa14/paeruginosa_pa14_genome.bam.tmp.*.bam
fi
samtools view -u -t ${HOME}/libraries/genome/paeruginosa_pa14.fasta \
  -S outputs/40hisat2_paeruginosa_pa14/paeruginosa_pa14_genome.sam -o outputs/40hisat2_paeruginosa_pa14/paeruginosa_pa14_genome.bam  \
  2>outputs/40hisat2_paeruginosa_pa14/paeruginosa_pa14_genome.bam_samtools.stderr \
  1>outputs/40hisat2_paeruginosa_pa14/paeruginosa_pa14_genome.bam_samtools.stdout

echo "First samtools command finished with $?"
samtools sort -l 9 outputs/40hisat2_paeruginosa_pa14/paeruginosa_pa14_genome.bam \
  -o outputs/40hisat2_paeruginosa_pa14/paeruginosa_pa14_genome-sorted.bam \
  2>>outputs/40hisat2_paeruginosa_pa14/paeruginosa_pa14_genome.bam_samtools.stderr \
  1>>outputs/40hisat2_paeruginosa_pa14/paeruginosa_pa14_genome.bam_samtools.stdout
rm outputs/40hisat2_paeruginosa_pa14/paeruginosa_pa14_genome.bam
rm outputs/40hisat2_paeruginosa_pa14/paeruginosa_pa14_genome.sam
mv outputs/40hisat2_paeruginosa_pa14/paeruginosa_pa14_genome-sorted.bam outputs/40hisat2_paeruginosa_pa14/paeruginosa_pa14_genome.bam
samtools index outputs/40hisat2_paeruginosa_pa14/paeruginosa_pa14_genome.bam \
  2>>outputs/40hisat2_paeruginosa_pa14/paeruginosa_pa14_genome.bam_samtools.stderr \
  1>>outputs/40hisat2_paeruginosa_pa14/paeruginosa_pa14_genome.bam_samtools.stdout
echo "Second samtools command finished with $?"
bamtools stats -in outputs/40hisat2_paeruginosa_pa14/paeruginosa_pa14_genome.bam \
  2>>outputs/40hisat2_paeruginosa_pa14/paeruginosa_pa14_genome.bam_samtools.stats 1>&2
echo "Bamtools finished with $?"

## The following will fail if this is single-ended.
samtools view -b -f 2 \
  -o outputs/40hisat2_paeruginosa_pa14/paeruginosa_pa14_genome-paired.bam \
  outputs/40hisat2_paeruginosa_pa14/paeruginosa_pa14_genome.bam \
  2>>outputs/40hisat2_paeruginosa_pa14/paeruginosa_pa14_genome.bam_samtools.stderr \
  1>>outputs/40hisat2_paeruginosa_pa14/paeruginosa_pa14_genome.bam_samtools.stdout
samtools index outputs/40hisat2_paeruginosa_pa14/paeruginosa_pa14_genome-paired.bam \
  2>>outputs/40hisat2_paeruginosa_pa14/paeruginosa_pa14_genome.bam_samtools.stderr \
  1>>outputs/40hisat2_paeruginosa_pa14/paeruginosa_pa14_genome.bam_samtools.stdout
bamtools stats -in outputs/40hisat2_paeruginosa_pa14/paeruginosa_pa14_genome-paired.bam \
  2>>outputs/40hisat2_paeruginosa_pa14/paeruginosa_pa14_genome.bam_samtools.stats 1>&2

bamtools filter -tag XM:0 \
  -in outputs/40hisat2_paeruginosa_pa14/paeruginosa_pa14_genome.bam \
  -out outputs/40hisat2_paeruginosa_pa14/paeruginosa_pa14_genome-sorted_nomismatch.bam \
  2>>outputs/40hisat2_paeruginosa_pa14/paeruginosa_pa14_genome.bam_samtools.stats 1>&2
echo "bamtools filter finished with: $?"
samtools index \
  outputs/40hisat2_paeruginosa_pa14/paeruginosa_pa14_genome-sorted_nomismatch.bam \
  2>>outputs/40hisat2_paeruginosa_pa14/paeruginosa_pa14_genome.bam_samtools.stderr \
  1>>outputs/40hisat2_paeruginosa_pa14/paeruginosa_pa14_genome.bam_samtools.stdout
echo "final samtools index finished with: $?"

1.4.3 Counting against the genome

Note that this step is not really useful for a dnaseq dataset in most instances. I also have the default orientation set to reverse because most of the samples off our sequencer are reversed; but that is likely not true for this dataset. If it turns out we actually care about these counts, I may need to come back and rerun these.

module add htseq

htseq-count \
  -q -f bam \
  -s reverse -a 0 \
   --type all  --idattr Alias \
  outputs/40hisat2_paeruginosa_pa14/paeruginosa_pa14_genome-paired.bam \
  /home/trey/libraries/genome/paeruginosa_pa14.gff \
  2>outputs/40hisat2_paeruginosa_pa14/paeruginosa_pa14_genome-paired_sreverse_all_Alias.stderr \
  1>outputs/40hisat2_paeruginosa_pa14/paeruginosa_pa14_genome-paired_sreverse_all_Alias.count
xz -f -9e outputs/40hisat2_paeruginosa_pa14/paeruginosa_pa14_genome-paired_sreverse_all_Alias.count

2 Creating a sample sheet

In order to play further with the data, I will need a sample sheet. So I will start out by creating a blank one in excel (libreoffice) which contains only the samplenames in the same format as my directories in preprocessing/.

Once completed, I can use it as the input for my hpgltools package and it should extract the interesting information from the preprocessing logs and fill out the sample sheet accordingly. Lets see if it works!

Here is the before:

knitr::kable(extract_metadata("sample_sheets/all_samples.xlsx"))
## Did not find the condition column in the sample sheet.
## Filling it in as undefined.
## Did not find the batch column in the sample sheet.
## Filling it in as undefined.
sampleid condition batch
PA14_exoUTY PA14_exoUTY undefined undefined
PA14_JC PA14_JC undefined undefined
PA14_lux PA14_lux undefined undefined
PA14_NBH PA14_NBH undefined undefined
PA14_pscD_A5 PA14_pscD_A5 undefined undefined
PA14_pscD_E4 PA14_pscD_E4 undefined undefined
PA14_xcp PA14_xcp undefined undefined
PA14_xcp_pscD PA14_xcp_pscD undefined undefined
PAK PAK undefined undefined
PAK_pscC PAK_pscC undefined undefined
PAK_xcp PAK_xcp undefined undefined
PAK_xcp_pscC PAK_xcp_pscC undefined undefined

Like I said, not much going on. Lets see what it looks like after I run the gatherer on it… (Note, I have been meaning to change this to drop the unused columns, but not yet).

spec <- make_dnaseq_spec()
queried_species <- c("paeruginosa_pak", "paeruginosa_pa01", "paeruginosa_pa14")
modified <- sm(gather_preprocessing_metadata("sample_sheets/all_samples.xlsx",
  species = queried_species, verbose = FALSE,
  specification = spec))
knitr::kable(extract_metadata("sample_sheets/all_samples_modified.xlsx"))
## Did not find the condition column in the sample sheet.
## Filling it in as undefined.
## Did not find the batch column in the sample sheet.
## Filling it in as undefined.
rownames trimomaticinput trimomaticoutput trimomaticpercent hisatgenomesingleconcordantpaeruginosapak hisatgenomesingleconcordantpaeruginosapa01 hisatgenomesingleconcordantpaeruginosapa14 hisatgenomemulticoncordantpaeruginosapak hisatgenomemulticoncordantpaeruginosapa01 hisatgenomemulticoncordantpaeruginosapa14 hisatgenomesingleallpaeruginosapak hisatgenomesingleallpaeruginosapa01 hisatgenomesingleallpaeruginosapa14 hisatgenomemultiallpaeruginosapak hisatgenomemultiallpaeruginosapa01 hisatgenomemultiallpaeruginosapa14 gatkunpairedpaeruginosapak gatkunpairedpaeruginosapa01 gatkunpairedpaeruginosapa14 gatkpairedpaeruginosapak gatkpairedpaeruginosapa01 gatkpairedpaeruginosapa14 gatksupplementarypaeruginosapak gatksupplementarypaeruginosapa01 gatksupplementarypaeruginosapa14 gatkunmappedpaeruginosapak gatkunmappedpaeruginosapa01 gatkunmappedpaeruginosapa14 gatkunpairedduplicatespaeruginosapak gatkunpairedduplicatespaeruginosapa01 gatkunpairedduplicatespaeruginosapa14 gatkpairedduplicatespaeruginosapak gatkpairedduplicatespaeruginosapa01 gatkpairedduplicatespaeruginosapa14 gatkpairedoptduplicatespaeruginosapak gatkpairedoptduplicatespaeruginosapa01 gatkpairedoptduplicatespaeruginosapa14 gatkduplicatepctpaeruginosapak gatkduplicatepctpaeruginosapa01 gatkduplicatepctpaeruginosapa14 gatklibsizepaeruginosapak gatklibsizepaeruginosapa01 gatklibsizepaeruginosapa14 variantsobservedpaeruginosapak variantsobservedpaeruginosapa01 variantsobservedpaeruginosapa14 hisatcounttablepaeruginosapak hisatcounttablepaeruginosapa01 hisatcounttablepaeruginosapa14 variantsbygenefilepaeruginosapak variantsbygenefilepaeruginosapa01 variantsbygenefilepaeruginosapa14 variantsbcftablepaeruginosapak variantsbcftablepaeruginosapa01 variantsbcftablepaeruginosapa14 variantsmodifiedgenomepaeruginosapak variantsmodifiedgenomepaeruginosapa01 variantsmodifiedgenomepaeruginosapa14 variantsbcffilepaeruginosapak variantsbcffilepaeruginosapa01 variantsbcffilepaeruginosapa14 variantspenetrancefilepaeruginosapak variantspenetrancefilepaeruginosapa01 variantspenetrancefilepaeruginosapa14 condition batch
PA14_exoUTY PA14_exoUTY 4706372 4350712 0.924 3690737 NA 4280240 664 NA 25722 307644 NA 34468 664 NA 25722 0 4305962 83320 0 0 823232 81874 0.191184 10580352 199 preprocessing/PA14_exoUTY/outputs/40hisat2_paeruginosa_pak/paeruginosa_pak_genome-paired_sno_gene_locus_tag.count.xz preprocessing/PA14_exoUTY/outputs/40hisat2_paeruginosa_pa14/paeruginosa_pa14_genome-paired_sreverse_all_Alias.count.xz preprocessing/PA14_exoUTY/outputs/50freebayes_paeruginosa_pa14/variants_by_gene.txt.xz preprocessing/PA14_exoUTY/outputs/50freebayes_paeruginosa_pa14/all_tags.txt.xz preprocessing/PA14_exoUTY/outputs/50freebayes_paeruginosa_pa14/paeruginosa_pa14-PA14_exoUTY.fasta preprocessing/PA14_exoUTY/outputs/50freebayes_paeruginosa_pa14/paeruginosa_pa14.bcf preprocessing/PA14_exoUTY/outputs/50freebayes_paeruginosa_pa14/variants_penetrance.txt.xz undefined undefined
PA14_JC PA14_JC 5786839 5336197 0.922 4550108 NA 5275687 1028 NA 32239 330047 NA 23521 1028 NA 32239 0 5307926 107378 0 0 1127763 103875 0.212468 11426698 196 preprocessing/PA14_JC/outputs/40hisat2_paeruginosa_pak/paeruginosa_pak_genome-paired_sno_gene_locus_tag.count.xz preprocessing/PA14_JC/outputs/40hisat2_paeruginosa_pa14/paeruginosa_pa14_genome-paired_sreverse_all_Alias.count.xz preprocessing/PA14_JC/outputs/50freebayes_paeruginosa_pa14/variants_by_gene.txt.xz preprocessing/PA14_JC/outputs/50freebayes_paeruginosa_pa14/all_tags.txt.xz preprocessing/PA14_JC/outputs/50freebayes_paeruginosa_pa14/paeruginosa_pa14-PA14_JC.fasta preprocessing/PA14_JC/outputs/50freebayes_paeruginosa_pa14/paeruginosa_pa14.bcf preprocessing/PA14_JC/outputs/50freebayes_paeruginosa_pa14/variants_penetrance.txt.xz undefined undefined
PA14_lux PA14_lux 6622570 6099776 0.921 5205608 NA 6028065 999 NA 36827 354183 NA 24917 999 NA 36827 0 6064892 121596 0 0 1432296 127776 0.236162 11448973 197 preprocessing/PA14_lux/outputs/40hisat2_paeruginosa_pak/paeruginosa_pak_genome-paired_sno_gene_locus_tag.count.xz preprocessing/PA14_lux/outputs/40hisat2_paeruginosa_pa14/paeruginosa_pa14_genome-paired_sreverse_all_Alias.count.xz preprocessing/PA14_lux/outputs/50freebayes_paeruginosa_pa14/variants_by_gene.txt.xz preprocessing/PA14_lux/outputs/50freebayes_paeruginosa_pa14/all_tags.txt.xz preprocessing/PA14_lux/outputs/50freebayes_paeruginosa_pa14/paeruginosa_pa14-PA14_lux.fasta preprocessing/PA14_lux/outputs/50freebayes_paeruginosa_pa14/paeruginosa_pa14.bcf preprocessing/PA14_lux/outputs/50freebayes_paeruginosa_pa14/variants_penetrance.txt.xz undefined undefined
PA14_NBH PA14_NBH 5151127 4581433 0.889 3883544 NA 4516421 711 NA 26915 313829 NA 32560 711 NA 26915 0 4543336 88454 0 0 896720 83669 0.19737 10694127 196 preprocessing/PA14_NBH/outputs/40hisat2_paeruginosa_pak/paeruginosa_pak_genome-paired_sno_gene_locus_tag.count.xz preprocessing/PA14_NBH/outputs/40hisat2_paeruginosa_pa14/paeruginosa_pa14_genome-paired_sreverse_all_Alias.count.xz preprocessing/PA14_NBH/outputs/50freebayes_paeruginosa_pa14/variants_by_gene.txt.xz preprocessing/PA14_NBH/outputs/50freebayes_paeruginosa_pa14/all_tags.txt.xz preprocessing/PA14_NBH/outputs/50freebayes_paeruginosa_pa14/paeruginosa_pa14-PA14_NBH.fasta preprocessing/PA14_NBH/outputs/50freebayes_paeruginosa_pa14/paeruginosa_pa14.bcf preprocessing/PA14_NBH/outputs/50freebayes_paeruginosa_pa14/variants_penetrance.txt.xz undefined undefined
PA14_pscD_A5 PA14_pscD_A5 5898210 5417082 0.918 4579807 NA 5359077 989 NA 32852 338340 NA 21806 989 NA 32852 0 5391929 110988 0 0 1134595 110041 0.210425 11790543 204 preprocessing/PA14_pscD_A5/outputs/40hisat2_paeruginosa_pak/paeruginosa_pak_genome-paired_sno_gene_locus_tag.count.xz preprocessing/PA14_pscD_A5/outputs/40hisat2_paeruginosa_pa14/paeruginosa_pa14_genome-paired_sreverse_all_Alias.count.xz preprocessing/PA14_pscD_A5/outputs/50freebayes_paeruginosa_pa14/variants_by_gene.txt.xz preprocessing/PA14_pscD_A5/outputs/50freebayes_paeruginosa_pa14/all_tags.txt.xz preprocessing/PA14_pscD_A5/outputs/50freebayes_paeruginosa_pa14/paeruginosa_pa14-PA14_pscD_A5.fasta preprocessing/PA14_pscD_A5/outputs/50freebayes_paeruginosa_pa14/paeruginosa_pa14.bcf preprocessing/PA14_pscD_A5/outputs/50freebayes_paeruginosa_pa14/variants_penetrance.txt.xz undefined undefined
PA14_pscD_E4 PA14_pscD_E4 5854559 5418227 0.925 4589839 NA 5361935 920 NA 33424 325823 NA 19561 920 NA 33424 0 5395359 112228 0 0 1204336 118910 0.223217 10998073 203 preprocessing/PA14_pscD_E4/outputs/40hisat2_paeruginosa_pak/paeruginosa_pak_genome-paired_sno_gene_locus_tag.count.xz preprocessing/PA14_pscD_E4/outputs/40hisat2_paeruginosa_pa14/paeruginosa_pa14_genome-paired_sreverse_all_Alias.count.xz preprocessing/PA14_pscD_E4/outputs/50freebayes_paeruginosa_pa14/variants_by_gene.txt.xz preprocessing/PA14_pscD_E4/outputs/50freebayes_paeruginosa_pa14/all_tags.txt.xz preprocessing/PA14_pscD_E4/outputs/50freebayes_paeruginosa_pa14/paeruginosa_pa14-PA14_pscD_E4.fasta preprocessing/PA14_pscD_E4/outputs/50freebayes_paeruginosa_pa14/paeruginosa_pa14.bcf preprocessing/PA14_pscD_E4/outputs/50freebayes_paeruginosa_pa14/variants_penetrance.txt.xz undefined undefined
PA14_xcp PA14_xcp 5683132 5214875 0.918 4430035 NA 5134054 1300 NA 30352 356590 NA 39479 1300 NA 30352 0 5164406 97676 0 0 1092613 103935 0.211566 11202466 195 preprocessing/PA14_xcp/outputs/40hisat2_paeruginosa_pak/paeruginosa_pak_genome-paired_sno_gene_locus_tag.count.xz preprocessing/PA14_xcp/outputs/40hisat2_paeruginosa_pa14/paeruginosa_pa14_genome-paired_sreverse_all_Alias.count.xz preprocessing/PA14_xcp/outputs/50freebayes_paeruginosa_pa14/variants_by_gene.txt.xz preprocessing/PA14_xcp/outputs/50freebayes_paeruginosa_pa14/all_tags.txt.xz preprocessing/PA14_xcp/outputs/50freebayes_paeruginosa_pa14/paeruginosa_pa14-PA14_xcp.fasta preprocessing/PA14_xcp/outputs/50freebayes_paeruginosa_pa14/paeruginosa_pa14.bcf preprocessing/PA14_xcp/outputs/50freebayes_paeruginosa_pa14/variants_penetrance.txt.xz undefined undefined
PA14_xcp_pscD PA14_xcp_pscD 2026150 1514509 0.747 1223766 NA 1471930 191 NA 10238 137765 NA 27094 191 NA 10238 0 1482168 41804 0 0 188223 20007 0.126992 5857317 216 preprocessing/PA14_xcp_pscD/outputs/40hisat2_paeruginosa_pak/paeruginosa_pak_genome-paired_sno_gene_locus_tag.count.xz preprocessing/PA14_xcp_pscD/outputs/40hisat2_paeruginosa_pa14/paeruginosa_pa14_genome-paired_sreverse_all_Alias.count.xz preprocessing/PA14_xcp_pscD/outputs/50freebayes_paeruginosa_pa14/variants_by_gene.txt.xz preprocessing/PA14_xcp_pscD/outputs/50freebayes_paeruginosa_pa14/all_tags.txt.xz preprocessing/PA14_xcp_pscD/outputs/50freebayes_paeruginosa_pa14/paeruginosa_pa14-PA14_xcp_pscD.fasta preprocessing/PA14_xcp_pscD/outputs/50freebayes_paeruginosa_pa14/paeruginosa_pa14.bcf preprocessing/PA14_xcp_pscD/outputs/50freebayes_paeruginosa_pa14/variants_penetrance.txt.xz undefined undefined
PAK PAK 4779558 4318745 0.904 4049183 3925784 3731781 1093 22836 19482 179265 179736 308170 1093 22836 19482 168494 0 4092362 3948620 3393 99172 284272 0 126668 0 902830 873059 98051 94715 0.231327 0.221105 8530650 8207871 333 26786 preprocessing/PAK/outputs/40hisat2_paeruginosa_pak/paeruginosa_pak_genome-paired_sreverse_all_locus_tag.count.xz preprocessing/PAK/outputs/40hisat2_paeruginosa_pa01/paeruginosa_pa01_genome-paired_sno_gene_locus_tag.count.xz preprocessing/PAK/outputs/40hisat2_paeruginosa_pa14/paeruginosa_pa14_genome-paired_sno_gene_Alias.count.xz preprocessing/PAK/outputs/50freebayes_paeruginosa_pak/variants_by_gene.txt.xz preprocessing/PAK/outputs/50freebayes_paeruginosa_pa01/variants_by_gene.txt.xz preprocessing/PAK/outputs/50freebayes_paeruginosa_pak/all_tags.txt.xz preprocessing/PAK/outputs/50freebayes_paeruginosa_pa01/all_tags.txt.xz preprocessing/PAK/outputs/50freebayes_paeruginosa_pak/paeruginosa_pak-PAK.fasta preprocessing/PAK/outputs/50freebayes_paeruginosa_pak/paeruginosa_pak.bcf preprocessing/PAK/outputs/50freebayes_paeruginosa_pa01/paeruginosa_pa01.bcf preprocessing/PAK/outputs/50freebayes_paeruginosa_pak/variants_penetrance.txt.xz preprocessing/PAK/outputs/50freebayes_paeruginosa_pa01/variants_penetrance.txt.xz undefined undefined
PAK_pscC PAK_pscC 5734960 5271470 0.919 5090759 4853631 4638113 1343 29403 25963 116625 126483 266811 1343 29403 25963 115014 0 5097071 4883034 4081 142224 233784 0 92389 0 1102959 1056234 106532 102207 0.222938 0.216307 10771706 10325724 373 26856 preprocessing/PAK_pscC/outputs/40hisat2_paeruginosa_pak/paeruginosa_pak_genome-paired_sreverse_gene_locus_tag.count.xz preprocessing/PAK_pscC/outputs/40hisat2_paeruginosa_pa01/paeruginosa_pa01_genome-paired_sno_gene_locus_tag.count.xz preprocessing/PAK_pscC/outputs/40hisat2_paeruginosa_pa14/paeruginosa_pa14_genome-paired_sno_gene_Alias.count.xz preprocessing/PAK_pscC/outputs/50freebayes_paeruginosa_pak/variants_by_gene.txt.xz preprocessing/PAK_pscC/outputs/50freebayes_paeruginosa_pa01/variants_by_gene.txt.xz preprocessing/PAK_pscC/outputs/50freebayes_paeruginosa_pak/all_tags.txt.xz preprocessing/PAK_pscC/outputs/50freebayes_paeruginosa_pa01/all_tags.txt.xz preprocessing/PAK_pscC/outputs/50freebayes_paeruginosa_pak/paeruginosa_pak-PAK_pscC.fasta preprocessing/PAK_pscC/outputs/50freebayes_paeruginosa_pak/paeruginosa_pak.bcf preprocessing/PAK_pscC/outputs/50freebayes_paeruginosa_pa01/paeruginosa_pa01.bcf preprocessing/PAK_pscC/outputs/50freebayes_paeruginosa_pak/variants_penetrance.txt.xz preprocessing/PAK_pscC/outputs/50freebayes_paeruginosa_pa01/variants_penetrance.txt.xz undefined undefined
PAK_xcp PAK_xcp 4843414 4443669 0.917 4293814 4088322 3904688 977 24341 21592 96933 110085 229330 977 24341 21592 95387 0 4299065 4112663 3145 116582 193821 0 73985 0 889293 849827 90548 86772 0.213098 0.206637 9634785 9231062 363 26756 preprocessing/PAK_xcp/outputs/40hisat2_paeruginosa_pak/paeruginosa_pak_genome-paired_sreverse_gene_locus_tag.count.xz preprocessing/PAK_xcp/outputs/40hisat2_paeruginosa_pa01/paeruginosa_pa01_genome-paired_sno_gene_locus_tag.count.xz preprocessing/PAK_xcp/outputs/40hisat2_paeruginosa_pa14/paeruginosa_pa14_genome-paired_sno_gene_Alias.count.xz preprocessing/PAK_xcp/outputs/50freebayes_paeruginosa_pak/variants_by_gene.txt.xz preprocessing/PAK_xcp/outputs/50freebayes_paeruginosa_pa01/variants_by_gene.txt.xz preprocessing/PAK_xcp/outputs/50freebayes_paeruginosa_pak/all_tags.txt.xz preprocessing/PAK_xcp/outputs/50freebayes_paeruginosa_pa01/all_tags.txt.xz preprocessing/PAK_xcp/outputs/50freebayes_paeruginosa_pak/paeruginosa_pak-PAK_xcp.fasta preprocessing/PAK_xcp/outputs/50freebayes_paeruginosa_pak/paeruginosa_pak.bcf preprocessing/PAK_xcp/outputs/50freebayes_paeruginosa_pa01/paeruginosa_pa01.bcf preprocessing/PAK_xcp/outputs/50freebayes_paeruginosa_pak/variants_penetrance.txt.xz preprocessing/PAK_xcp/outputs/50freebayes_paeruginosa_pa01/variants_penetrance.txt.xz undefined undefined
PAK_xcp_pscC PAK_xcp_pscC 5195158 4611474 0.888 4344138 4220150 4008749 1070 23899 20629 177601 174592 308658 1070 23899 20629 168983 0 4376720 4244049 3164 102714 300525 0 129907 0 943947 917470 92772 89839 0.226149 0.216178 9299455 8989454 384 26949 preprocessing/PAK_xcp_pscC/outputs/40hisat2_paeruginosa_pak/paeruginosa_pak_genome-paired_sreverse_gene_locus_tag.count.xz preprocessing/PAK_xcp_pscC/outputs/40hisat2_paeruginosa_pa01/paeruginosa_pa01_genome-paired_sno_gene_locus_tag.count.xz preprocessing/PAK_xcp_pscC/outputs/40hisat2_paeruginosa_pa14/paeruginosa_pa14_genome-paired_sno_gene_Alias.count.xz preprocessing/PAK_xcp_pscC/outputs/50freebayes_paeruginosa_pak/variants_by_gene.txt.xz preprocessing/PAK_xcp_pscC/outputs/50freebayes_paeruginosa_pa01/variants_by_gene.txt.xz preprocessing/PAK_xcp_pscC/outputs/50freebayes_paeruginosa_pak/all_tags.txt.xz preprocessing/PAK_xcp_pscC/outputs/50freebayes_paeruginosa_pa01/all_tags.txt.xz preprocessing/PAK_xcp_pscC/outputs/50freebayes_paeruginosa_pak/paeruginosa_pak-PAK_xcp_pscC.fasta preprocessing/PAK_xcp_pscC/outputs/50freebayes_paeruginosa_pak/paeruginosa_pak.bcf preprocessing/PAK_xcp_pscC/outputs/50freebayes_paeruginosa_pa01/paeruginosa_pa01.bcf preprocessing/PAK_xcp_pscC/outputs/50freebayes_paeruginosa_pak/variants_penetrance.txt.xz preprocessing/PAK_xcp_pscC/outputs/50freebayes_paeruginosa_pa01/variants_penetrance.txt.xz undefined undefined

I reran the missing PAK samples and looked into the logs. It may be the case that the PAK genome I downloaded is of somewhat lower quality than the PA14 and that is skewing the results somewhat.

Lets go one small step further. I have a series of modified genomes as well as the reference. We can do a quickie tree of them: First I will copy each modified genome to the tree/ directory and rename them to the sampleID.

start=$(pwd)
mkdir tree
cd preprocessing
for i in $(/bin/ls -d PA*); do
    cp $i/outputs/50*/paeruginosa_pak-*.fasta ${start}/tree/
    cp $i/outputs/50*/paeruginosa_pa14-*.fasta ${start}/tree/
done
cd $start
cp ~/libraries/genome/paeruginosa_pa14.fa ${start}/tree
cp ~/libraries/genome/paeruginosa_pak.fa ${start}/tree

Oh, it turns out that at the time of this writing, I forgot to run 3 samples, so this section will need to be redone. But I can at least run it for the samples that I didn’t forget.

funkytown <- genomic_sequence_phylo("tree", root = "paeruginosa_pa14")
## Reading tree/PA14_exoUTY.fasta
## Reading tree/PA14_JC.fasta
## Reading tree/PA14_lux.fasta
## Reading tree/PA14_NBH.fasta
## Reading tree/PA14_pscD_A5.fasta
## Reading tree/PA14_pscD_E4.fasta
## Reading tree/PA14_xcp_pscD.fasta
## Reading tree/paeruginosa_pa14.fasta
## Reading tree/paeruginosa_pak-PAK_pscC.fasta
## Reading tree/paeruginosa_pak-PAK_xcp_pscC.fasta
## Reading tree/paeruginosa_pak-PAK_xcp.fasta
## Reading tree/paeruginosa_pak-PAK.fasta
## Reading tree/paeruginosa_pak.fasta
## Reading tree/PAK_pscC.fasta
## Reading tree/PAK_xcp_pscC.fasta
## Reading tree/PAK_xcp.fasta
## Reading tree/PAK.fasta
plot(funkytown$phy)

3 Create an expressionset

The counts from hisat in theory are not very interesting for DNAseq data, except in this instance we want to see the coverage of the knockouts.

pa14_annot <- load_gff_annotations("~/libraries/genome/paeruginosa_pa14.gff", type = "gene", id_col = "Alias")
## Trying attempt: rtracklayer::import.gff3(gff, sequenceRegionsAsSeqinfo = TRUE)
## Had a successful gff import with rtracklayer::import.gff3(gff, sequenceRegionsAsSeqinfo = TRUE)
## Returning a df with 16 columns and 5979 rows.
rownames(pa14_annot) <- pa14_annot[["Alias"]]

pa14_expt <- create_expt("sample_sheets/all_samples_modified.xlsx", gene_info = pa14_annot,
                         file_column = "hisatcounttablepaeruginosapa14")
## Reading the sample metadata.
## Did not find the condition column in the sample sheet.
## Filling it in as undefined.
## Did not find the batch column in the sample sheet.
## Filling it in as undefined.
## The sample definitions comprises: 12 rows(samples) and 66 columns(metadata fields).
## Matched 5979 annotations and counts.
## Bringing together the count matrix and gene information.
## Some annotations were lost in merging, setting them to 'undefined'.
## Error in (function (classes, fdef, mtable) : unable to find an inherited method for function 'subset_expt' for signature '"ExpressionSet"'
pa14_write <- write_expt(pa14_expt, excel = "excel/pa14_strains.xlsx")
## Deleting the file excel/pa14_strains.xlsx before writing the tables.
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'exprs': object 'pa14_expt' not found
pak_annot <- load_gff_annotations("~/libraries/genome/paeruginosa_pak.gff", type = "gene", 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 35 columns and 5871 rows.
rownames(pak_annot) <- pak_annot[["locus_tag"]]
pak_expt <- create_expt("sample_sheets/all_samples_modified.xlsx", file_column = "hisatcounttablepaeruginosapak")
## Reading the sample metadata.
## Did not find the condition column in the sample sheet.
## Filling it in as undefined.
## Did not find the batch column in the sample sheet.
## Filling it in as undefined.
## The sample definitions comprises: 12 rows(samples) and 66 columns(metadata fields).
## Matched 5871 annotations and counts.
## Bringing together the count matrix and gene information.
## Error in (function (classes, fdef, mtable) : unable to find an inherited method for function 'subset_expt' for signature '"ExpressionSet"'
pak_write <- write_expt(pak_expt, excel = "excel/pak_strains.xlsx")
## Deleting the file excel/pak_strains.xlsx before writing the tables.
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'exprs': object 'pak_expt' not found

4 Write variants by sample

pa14_variants <- pData(pa14_expt)[["variantspenetrancefilepaeruginosapa14"]]
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': object 'pa14_expt' not found
names(pa14_variants) <- rownames(pData(pa14_expt))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'rownames': error in evaluating the argument 'object' in selecting a method for function 'pData': object 'pa14_expt' not found
start <- init_xlsx(excel = "excel/pa14_variants.xlsx")
## Deleting the file excel/pa14_variants.xlsx before writing the tables.
wb <- start[["wb"]]
for (s in seq_len(length(pa14_variants))) {
  sample_name <- names(pa14_variants)[[s]]
  if (pa14_variants[[s]] == "") {
    next
  }
  sample_data <- readr::read_tsv(pa14_variants[[s]])
  if (nrow(sample_data) == 0) {
    next
  }
  written <- write_xlsx(data = sample_data, sheet = sample_name, wb = wb)
}
## Error in eval(expr, envir, enclos): object 'pa14_variants' not found
saved <- openxlsx::saveWorkbook(written[["workbook"]], file = "excel/pa14_variants.xlsx")
## Error in "Workbook" %in% class(wb): object 'written' not found
pak_variants <- pData(pak_expt)[["variantspenetrancefilepaeruginosapak"]]
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': object 'pak_expt' not found
names(pak_variants) <- rownames(pData(pak_expt))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'rownames': error in evaluating the argument 'object' in selecting a method for function 'pData': object 'pak_expt' not found
start <- init_xlsx(excel = "excel/pak_variants.xlsx")
## Deleting the file excel/pak_variants.xlsx before writing the tables.
wb <- start[["wb"]]
for (s in seq_len(length(pak_variants))) {
  sample_name <- names(pak_variants)[[s]]
  if (pak_variants[[s]] == "") {
    next
  }
  sample_data <- readr::read_tsv(pak_variants[[s]])
  if (nrow(sample_data) == 0) {
    next
  }
  written <- write_xlsx(data = sample_data, sheet = sample_name, wb = wb)
}
## Error in eval(expr, envir, enclos): object 'pak_variants' not found
saved <- openxlsx::saveWorkbook(written[["workbook"]], file = "excel/pak_variants.xlsx")
## Error in "Workbook" %in% class(wb): object 'written' not found

5 Write mutations by sample

In this following block we will instead write out the nt/aa mutations of CDS/proteins.

pa14_mutations <- pData(pa14_expt)[["variantsbygenefilepaeruginosapa14"]]
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': object 'pa14_expt' not found
names(pa14_mutations) <- rownames(pData(pa14_expt))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'rownames': error in evaluating the argument 'object' in selecting a method for function 'pData': object 'pa14_expt' not found
start <- init_xlsx(excel = "excel/pa14_mutations.xlsx")
## Deleting the file excel/pa14_mutations.xlsx before writing the tables.
wb <- start[["wb"]]
for (s in seq_len(length(pa14_mutations))) {
  sample_name <- names(pa14_mutations)[[s]]
  if (pa14_mutations[[s]] == "") {
    next
  }
  sample_data <- readr::read_tsv(pa14_mutations[[s]])
  if (nrow(sample_data) == 0) {
    next
  }
  written <- write_xlsx(data = sample_data, sheet = sample_name, wb = wb)
}
## Error in eval(expr, envir, enclos): object 'pa14_mutations' not found
saved <- openxlsx::saveWorkbook(written[["workbook"]], file = "excel/pa14_mutations.xlsx")
## Error in "Workbook" %in% class(wb): object 'written' not found
pak_mutations <- pData(pak_expt)[["variantsbygenefilepaeruginosapak"]]
## Error in h(simpleError(msg, call)): error in evaluating the argument 'object' in selecting a method for function 'pData': object 'pak_expt' not found
names(pak_mutations) <- rownames(pData(pak_expt))
## Error in h(simpleError(msg, call)): error in evaluating the argument 'x' in selecting a method for function 'rownames': error in evaluating the argument 'object' in selecting a method for function 'pData': object 'pak_expt' not found
start <- init_xlsx(excel = "excel/pak_mutations.xlsx")
## Deleting the file excel/pak_mutations.xlsx before writing the tables.
wb <- start[["wb"]]
for (s in seq_len(length(pak_mutations))) {
  sample_name <- names(pak_mutations)[[s]]
  if (pak_mutations[[s]] == "") {
    next
  }
  sample_data <- readr::read_tsv(pak_mutations[[s]])
  if (nrow(sample_data) == 0) {
    next
  }
  written <- write_xlsx(data = sample_data, sheet = sample_name, wb = wb)
}
## Error in eval(expr, envir, enclos): object 'pak_mutations' not found
saved <- openxlsx::saveWorkbook(written[["workbook"]], file = "excel/pak_mutations.xlsx")
## Error in "Workbook" %in% class(wb): object 'written' not found
pander::pander(sessionInfo())

R version 4.2.0 (2022-04-22)

Platform: x86_64-pc-linux-gnu (64-bit)

locale: LC_CTYPE=en_US.UTF-8, LC_NUMERIC=C, LC_TIME=en_US.UTF-8, LC_COLLATE=en_US.UTF-8, LC_MONETARY=en_US.UTF-8, LC_MESSAGES=en_US.UTF-8, LC_PAPER=en_US.UTF-8, LC_NAME=C, LC_ADDRESS=C, LC_TELEPHONE=C, LC_MEASUREMENT=en_US.UTF-8 and LC_IDENTIFICATION=C

attached base packages: stats4, stats, graphics, grDevices, utils, datasets, methods and base

other attached packages: hpgltools(v.1.0), testthat(v.3.1.7), reticulate(v.1.28), SummarizedExperiment(v.1.28.0), GenomicRanges(v.1.50.2), GenomeInfoDb(v.1.34.9), IRanges(v.2.32.0), S4Vectors(v.0.36.2), MatrixGenerics(v.1.10.0), matrixStats(v.0.63.0), Biobase(v.2.58.0) and BiocGenerics(v.0.44.0)

loaded via a namespace (and not attached): rappdirs(v.0.3.3), rtracklayer(v.1.58.0), tidyr(v.1.3.0), ggplot2(v.3.4.2), clusterGeneration(v.1.3.7), bit64(v.4.0.5), knitr(v.1.42), DelayedArray(v.0.24.0), data.table(v.1.14.8), KEGGREST(v.1.38.0), RCurl(v.1.98-1.12), doParallel(v.1.0.17), generics(v.0.1.3), GenomicFeatures(v.1.50.4), callr(v.3.7.3), RhpcBLASctl(v.0.23-42), cowplot(v.1.1.1), usethis(v.2.1.6), RSQLite(v.2.3.1), shadowtext(v.0.1.2), bit(v.4.0.5), enrichplot(v.1.18.3), xml2(v.1.3.3), httpuv(v.1.6.9), viridis(v.0.6.2), xfun(v.0.38), hms(v.1.1.3), jquerylib(v.0.1.4), evaluate(v.0.20), promises(v.1.2.0.1), fansi(v.1.0.4), restfulr(v.0.0.15), progress(v.1.2.2), caTools(v.1.18.2), dbplyr(v.2.3.2), igraph(v.1.4.1), DBI(v.1.1.3), htmlwidgets(v.1.6.2), purrr(v.1.0.1), ellipsis(v.0.3.2), dplyr(v.1.1.1), backports(v.1.4.1), annotate(v.1.76.0), aod(v.1.3.2), biomaRt(v.2.54.1), vctrs(v.0.6.1), remotes(v.2.4.2), cachem(v.1.0.7), withr(v.2.5.0), ggforce(v.0.4.1), HDO.db(v.0.99.1), GenomicAlignments(v.1.34.1), treeio(v.1.22.0), prettyunits(v.1.1.1), kmer(v.1.1.2), DOSE(v.3.24.2), ape(v.5.7-1), lazyeval(v.0.2.2), crayon(v.1.5.2), genefilter(v.1.80.3), edgeR(v.3.40.2), pkgconfig(v.2.0.3), tweenr(v.2.0.2), nlme(v.3.1-162), pkgload(v.1.3.2), devtools(v.2.4.5), rlang(v.1.1.0), lifecycle(v.1.0.3), miniUI(v.0.1.1.1), downloader(v.0.4), filelock(v.1.0.2), BiocFileCache(v.2.6.1), rprojroot(v.2.0.3), polyclip(v.1.10-4), graph(v.1.76.0), Matrix(v.1.5-4), aplot(v.0.1.10), boot(v.1.3-28.1), processx(v.3.8.0), png(v.0.1-8), viridisLite(v.0.4.1), rjson(v.0.2.21), bitops(v.1.0-7), gson(v.0.1.0), KernSmooth(v.2.23-20), pander(v.0.6.5), Biostrings(v.2.66.0), blob(v.1.2.4), phylogram(v.2.1.0), stringr(v.1.5.0), qvalue(v.2.30.0), remaCor(v.0.0.11), gridGraphics(v.0.5-1), scales(v.1.2.1), memoise(v.2.0.1), GSEABase(v.1.60.0), magrittr(v.2.0.3), plyr(v.1.8.8), gplots(v.3.1.3), zlibbioc(v.1.44.0), compiler(v.4.2.0), scatterpie(v.0.1.8), BiocIO(v.1.8.0), RColorBrewer(v.1.1-3), lme4(v.1.1-32), Rsamtools(v.2.14.0), cli(v.3.6.1), XVector(v.0.38.0), urlchecker(v.1.0.1), patchwork(v.1.1.2), ps(v.1.7.4), MASS(v.7.3-58.3), mgcv(v.1.8-41), tidyselect(v.1.2.0), stringi(v.1.7.12), highr(v.0.10), yaml(v.2.3.7), GOSemSim(v.2.24.0), locfit(v.1.5-9.7), ggrepel(v.0.9.3), grid(v.4.2.0), sass(v.0.4.5), fastmatch(v.1.1-3), tools(v.4.2.0), parallel(v.4.2.0), rstudioapi(v.0.14), foreach(v.1.5.2), gridExtra(v.2.3), farver(v.2.1.1), ggraph(v.2.1.0), digest(v.0.6.31), shiny(v.1.7.4), Rcpp(v.1.0.10), broom(v.1.0.4), later(v.1.3.0), httr(v.1.4.5), AnnotationDbi(v.1.60.2), Rdpack(v.2.4), colorspace(v.2.1-0), brio(v.1.1.3), XML(v.3.99-0.14), fs(v.1.6.1), splines(v.4.2.0), yulab.utils(v.0.0.6), PROPER(v.1.30.0), tidytree(v.0.4.2), graphlayouts(v.0.8.4), ggplotify(v.0.1.0), plotly(v.4.10.1), sessioninfo(v.1.2.2), xtable(v.1.8-4), jsonlite(v.1.8.4), nloptr(v.2.0.3), ggtree(v.3.6.2), tidygraph(v.1.2.3), ggfun(v.0.0.9), R6(v.2.5.1), RUnit(v.0.4.32), profvis(v.0.3.7), pillar(v.1.9.0), htmltools(v.0.5.5), mime(v.0.12), glue(v.1.6.2), fastmap(v.1.1.1), minqa(v.1.2.5), clusterProfiler(v.4.6.2), BiocParallel(v.1.32.6), codetools(v.0.2-19), fgsea(v.1.24.0), pkgbuild(v.1.4.0), mvtnorm(v.1.1-3), utf8(v.1.2.3), lattice(v.0.20-45), bslib(v.0.4.2), tibble(v.3.2.1), sva(v.3.46.0), pbkrtest(v.0.5.2), curl(v.5.0.0), gtools(v.3.9.4), zip(v.2.2.2), GO.db(v.3.16.0), openxlsx(v.4.2.5.2), survival(v.3.5-5), limma(v.3.54.2), rmarkdown(v.2.21), desc(v.1.4.2), munsell(v.0.5.0), GenomeInfoDbData(v.1.2.9), iterators(v.1.0.14), variancePartition(v.1.28.9), reshape2(v.1.4.4), gtable(v.0.3.3) and rbibutils(v.2.2.13)

message("This is hpgltools commit: ", get_git_commit())
## If you wish to reproduce this exact build of hpgltools, invoke the following:
## > git clone http://github.com/abelew/hpgltools.git
## > git reset bbc75e24b763faa635cb62c86fc51c0efb3424a1
## This is hpgltools commit: Fri Apr 21 14:33:16 2023 -0400: bbc75e24b763faa635cb62c86fc51c0efb3424a1
this_save <- paste0(gsub(pattern = "\\.Rmd", replace = "", x = rmd_file), "-v", ver, ".rda.xz")
message("Saving to ", this_save)
## Saving to index-v20230501.rda.xz
tmp <- sm(saveme(filename = this_save))
LS0tCnRpdGxlOiAiUXVlcnlpbmcgYSBzZXQgb2YgUHNldWRvbW9uYXMgc3RyYWlucy4iCmF1dGhvcjogImF0YiBhYmVsZXdAZ21haWwuY29tIgpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgY29kZV9mb2xkaW5nOiBzaG93CiAgICBmaWdfY2FwdGlvbjogdHJ1ZQogICAgZmlnX2hlaWdodDogNwogICAgZmlnX3dpZHRoOiA3CiAgICBoaWdobGlnaHQ6IHplbmJ1cm4KICAgIGtlZXBfbWQ6IGZhbHNlCiAgICBtb2RlOiBzZWxmY29udGFpbmVkCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICAgIHNlbGZfY29udGFpbmVkOiB0cnVlCiAgICB0aGVtZTogcmVhZGFibGUKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OgogICAgICBjb2xsYXBzZWQ6IGZhbHNlCiAgICAgIHNtb290aF9zY3JvbGw6IGZhbHNlCiAgcm1kZm9ybWF0czo6cmVhZHRoZWRvd246CiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgICBjb2RlX2ZvbGRpbmc6IHNob3cKICAgIGRmX3ByaW50OiBwYWdlZAogICAgZmlnX2NhcHRpb246IHRydWUKICAgIGZpZ19oZWlnaHQ6IDcKICAgIGZpZ193aWR0aDogNwogICAgaGlnaGxpZ2h0OiB6ZW5idXJuCiAgICB3aWR0aDogMzAwCiAgICBrZWVwX21kOiBmYWxzZQogICAgbW9kZTogc2VsZmNvbnRhaW5lZAogICAgdG9jX2Zsb2F0OiB0cnVlCiAgQmlvY1N0eWxlOjpodG1sX2RvY3VtZW50OgogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgY29kZV9mb2xkaW5nOiBzaG93CiAgICBmaWdfY2FwdGlvbjogdHJ1ZQogICAgZmlnX2hlaWdodDogNwogICAgZmlnX3dpZHRoOiA3CiAgICBoaWdobGlnaHQ6IHplbmJ1cm4KICAgIGtlZXBfbWQ6IGZhbHNlCiAgICBtb2RlOiBzZWxmY29udGFpbmVkCiAgICB0b2NfZmxvYXQ6IHRydWUKLS0tCgo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPgpib2R5LCB0ZCB7CiAgZm9udC1zaXplOiAxNnB4Owp9CmNvZGUucnsKICBmb250LXNpemU6IDE2cHg7Cn0KcHJlIHsKIGZvbnQtc2l6ZTogMTZweAp9Cjwvc3R5bGU+CgpgYGB7ciBvcHRpb25zLCBpbmNsdWRlPUZBTFNFfQpsaWJyYXJ5KGhwZ2x0b29scykKbGlicmFyeShyZXRpY3VsYXRlKQp0dCA8LSBkZXZ0b29sczo6bG9hZF9hbGwoIn4vaHBnbHRvb2xzIikKa25pdHI6Om9wdHNfa25pdCRzZXQoCiAgd2lkdGggPSAxMjAsIHByb2dyZXNzID0gVFJVRSwgdmVyYm9zZSA9IFRSVUUsIGVjaG8gPSBUUlVFKQprbml0cjo6b3B0c19jaHVuayRzZXQoZXJyb3IgPSBUUlVFLCBkcGkgPSA5NikKbHVhX2ZpbHRlcnMgPC0gcm1hcmtkb3duOjpwYW5kb2NfbHVhX2ZpbHRlcl9hcmdzKCJwYW5kb2Mtem90eHQubHVhIikKb2xkX29wdGlvbnMgPC0gb3B0aW9ucygKICBkaWdpdHMgPSA0LCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UsIGtuaXRyLmR1cGxpY2F0ZS5sYWJlbCA9ICJhbGxvdyIpCmdncGxvdDI6OnRoZW1lX3NldChnZ3Bsb3QyOjp0aGVtZV9idyhiYXNlX3NpemUgPSAxMCkpCnJ1bmRhdGUgPC0gZm9ybWF0KFN5cy5EYXRlKCksIGZvcm1hdCA9ICIlWSVtJWQiKQpwcmV2aW91c19maWxlIDwtICIiCnZlciA8LSBmb3JtYXQoU3lzLkRhdGUoKSwgIiVZJW0lZCIpCgojI3RtcCA8LSBzbShsb2FkbWUoZmlsZW5hbWU9cGFzdGUwKGdzdWIocGF0dGVybj0iXFwuUm1kIiwgcmVwbGFjZT0iIiwgeD1wcmV2aW91c19maWxlKSwgIi12IiwgdmVyLCAiLnJkYS54eiIpKSkKcm1kX2ZpbGUgPC0gImluZGV4LlJtZCIKYGBgCgojIEludHJvZHVjdGlvbgoKVGhpcyBkb2N1bWVudCBzZWVrcyB0byBsYXkgb3V0IG15IHByb2Nlc3MgaW4gcG9raW5nIGF0IHRoZQpETkFzZXF1ZW5jaW5nIHJlc3VsdHMgb2YgYSBzZXJpZXMgb2YgUHNldWRvbW9uYXMgYWVydWdpbm9zYSBQQTE0IGFuZApQQUsgc3RyYWlucy4KCklmIEkgdW5kZXJzdGFuZCBEci4gTGVlIGFuZCBjby4ncyBnb2FsLCB0aGV5IHdpc2ggdG8gZW5zdXJlIHRoYXQgdGhlc2UKc3RyYWlucyBhcmUgc3RpbGwgcmVhc29uYWJseSBjbG9zZSB0byB0aGUgYXNzb2NpYXRlZCByZWZlcmVuY2UKc3RyYWlucy4gIEkgdGhlcmVmb3JlIGFtIHJ1bm5pbmcgbXkgZGVmYXVsdCB0cmltbWluZy9tYXBwaW5nL3ZhcmlhbnQKc2VhcmNoIG1ldGhvZHMuCgpJIGhhdmUgYSBzaW5nbGUgY29tbWFuZCB0aGF0IGNhbiBydW4gYWxsIG9mIHRoZXNlIGNvbW1hbmRzIGF0IHRoZSBzYW1lCnRpbWUsIGJ1dCBJIGhhdmUgYmVlbiBhY3RpdmVseSBicmVha2luZyBteSB0b29scyByZWNlbnRseTsgc28gSQpkZWNpZGVkIHRvIHJ1biB0aGVtIG9uZSBhdCBhIHRpbWUgd2l0aCB0aGUgYXNzdW1wdGlvbiB0aGF0IHNvbWV0aGluZwp3b3VsZCBub3Qgd29yayAoYnV0IGV2ZXJ5dGhpbmcgZGlkIHdvcmsgb24gdGhlIGZpcnN0IHRyeSwgc28gdGhhdCB3YXMgbmljZSkuCgojIyBEb3dubG9hZGluZyBhbmQgc29ydGluZyB0aGUgZGF0YQoKSSBkb3dubG9hZGVkIHRoZSAuemlwIGFyY2hpdmUgZmlsZSB1c2luZyB0aGUgbGluayBpbiBEci4gTGVlJ3MgZW1haWwuCkkgZGlkIG5vdCBzYXZlIGl0IHRob3VnaCwgc28gaWYgd2UgbmVlZCB0byBkb3dubG9hZCB0aGUgZGF0YSBhZ2Fpbiwgd2UKd2lsbCBoYXZlIHRvIGdvIHRvIGhpbS4gIEkgY3JlYXRlZCBteSB1c3VhbCB3b3JrIGRpcmVjdG9yeQoncHJlcHJvY2Vzc2luZy8nIHdpdGhpbiB0aGlzIHRyZWUgYW5kIG1vdmVkIGl0IHRoZXJlLiAgSSB1bnppcHBlZCBpdAphbmQgbW92ZWQgZWFjaCBwYWlyIG9mIHJlYWRzIHRvIGEgZGlyZWN0b3J5IHdoaWNoIGZvbGxvd3MgRHIuIExlZSdzCmRlc2lyZWQgbmFtaW5nIGNvbnZlbnRpb24uCgpJIHRoZW4gY3JlYXRlZCB0aGUgZGlyZWN0b3JpZXM6ICdyZWZlcmVuY2UvJyBhbmQgJ3NhbXBsZV9zaGVldHMvJy4KVGhlIHNhbXBsZV9zaGVldHMgcmVtYWluZWQgZW1wdHkgZm9yIGEgd2hpbGUsIGJ1dCBJIGltbWVkaWF0ZWx5CmRvd25sb2FkZWQgdGhlIF9mdWxsXyBnZW5iYW5rIGZsYXQgZmlsZSBmb3IgdGhlIFBzZXVkb21vbmFzIFBBSyBzdHJhaW4KZnJvbSBOQ0JJLCBmb3VuZCBoZXJlOgoKaHR0cHM6Ly93d3cubmNiaS5ubG0ubmloLmdvdi9udWNjb3JlL05aX0NQMDIwNjU5CgpOb3RlLCB0aGF0IHdoZW4gZG93bmxvYWRpbmcsIG9uZSBtdXN0IGhpdCB0aGUgJ2N1c3RvbWl6ZSB2aWV3JyBidXR0b24Kb24gdGhlIHJpZ2h0IGFuZCBlbnN1cmUgdGhhdCB0aGUgZW50aXJlIHNlcXVlbmNlIGFuZCBhbGwgYW5ub3RhdGlvbnMKYXJlIGluY2x1ZGVkLiAgVGhlbiBoaXQgdGhlICdzZW5kIHRvJyBidXR0b24gYW5kIHNlbmQgaXQgdG8gYSBmaWxlLgpUaGlzIGZpbGUgSSBjb3BpZWQgdG8gcmVmZXJlbmNlL3BhZXJ1Z2lub3NhX3Bhay5nYi4KCiMjIENyZWF0aW9uIG9mIHRoZSBwYWsgcmVmZXJlbmNlCgpHaXZlbiB0aGUgZnVsbCBQQUsgZ2VuYmFuayBmaWxlLCBJIGNvbnZlcnRlZCBpdCB0byB0aGUgZXhwZWN0ZWQKZmFzdGEvZ2ZmIGZpbGUgZm9yIG1hcHBpbmc6CgpgYGB7YmFzaCBjb252ZXJ0X2diLCBldmFsPUZBTFNFfQpjZCByZWZlcmVuY2UKY3lvYSAtLW1ldGhvZCBnYjJnZmYgLS1pbnB1dCBwYWVydWdpbm9zYV9wYWsuZ2IKYGBgCgpUaGlzIGNvbW1hbmQgY3JlYXRlZCBhIHNlcmllcyBvZiBmYXN0YSBhbmQgZ2ZmIGZpbGVzIHdoaWNoIHByb3ZpZGUgdGhlCmNvb3JkaW5hdGVzIGZvciB0aGUgdmFyaW91cyBhbm5vdGF0aW9ucyAoZ2VuZXMvY2RzL3JSTkEvaW50ZXJjZHMpIGFuZApzZXF1ZW5jZSBmb3IgdGhlIGdlbm9tZSwgQ0RTIG51Y2xlb3RpZGVzLCBhbmQgYW1pbm8gYWNpZHMuICBJIHRoZW4KY29waWVkIHRoZSBnZW5vbWUvZ2ZmIGZpbGVzIHRvIG15IGdsb2JhbCByZWZlcmVuY2UgZGlyZWN0b3J5IGFuZApwcmVwYXJlZCBpdCBmb3IgdXNhZ2UgYnkgbXkgZmF2b3JpdGUgbWFwcGVyOgoKYGBge2Jhc2ggaW5kZXhfcGFrLCBldmFsPUZBTFNFfQpjZCB+L2xpYnJhcmllcy9nZW5vbWUKY3lvYSAtLW1ldGhvZCBpbmRleGhpc2F0IC0tc3BlY2llcyBwYWVydWdpbm9zYV9wYWsKYGBgCgpOb3cgYWxsIG9mIHRoZSBwaWVjZXMgYXJlIGluIHBsYWNlIGZvciBtZSB0byBwbGF5LiAgRWFjaCBvZiB0aGUKZm9sbG93aW5nIHN0ZXBzIHdhcyBwZXJmb3JtZWQgdHdpY2UsIG9uY2UgZm9yIHRoZSBQQTE0IHNhbXBsZXMsIG9uY2UKZm9yIHRoZSBQQUsgc2FtcGxlcy4gIFRoZSBvbmx5IGRpZmZlcmVuY2UgaW4gdGhlIGludm9jYXRpb25zIHdhcyBkdWUKdG8gdGhlIGZhY3QgdGhhdCB0aGUgUEFLIGFubm90YXRpb25zIHByb3ZpZGUgZGlmZmVyZW50IHRhZ3MuICBFLmcuIEkKdXNlZCB0aGUgJ0FsaWFzJyB0YWcgZm9yIFBBMTQgYW5kIHRoZSAnbG9jdXNfdGFnJyB0YWcgZm9yIFBBSy4gIEFzIGEKcmVzdWx0IEkgYW0gb25seSBnb2luZyB0byB3cml0ZSBkb3duIGluIHRoaXMgZG9jdW1lbnQgdGhlIFBBMTQKaW52b2NhdGlvbnMgYW5kIGFzc3VtZSB0aGUgcmVhZGVyIGNhbiBmaWd1cmUgb3V0IHRoZSBkaWZmZXJlbmNlLgoKIyMgVHJpbW1pbmcKCkkgaGF2ZSBhIGNvdXBsZSBvZiB0cmltbWluZyBtZXRob2RzLCBpbiB0aGlzIGluc3RhbmNlIEkganVzdCB1c2VkIHRoZQpkZWZhdWx0IGFuZCB3aWxsIG9wZXJhdGUgdW5kZXIgdGhlIGFzc3VtcHRpb24gdGhhdCBpdCBpcyBzdWZmaWNpZW50CnVudGlsIEkgc2VlIG90aGVyd2lzZS4KCmBgYHtiYXNoIGN5b2FfdHJpbSwgZXZhbD1GQUxTRX0KY2QgcHJlcHJvY2Vzc2luZwpzdGFydD0kKHB3ZCkKZm9yIGkgaW4gJCgvYmluL2xzIC1kIFBBMTQqKTsgZG8KICAgIGNkICRpCiAgICBjeW9hIC0tbWV0aG9kIHRyaW0gLS1pbnB1dCAkKC9iaW4vbHMgKi5mYXN0cS5neiB8IHRyICdcbicgJzonIHwgc2VkICdzLzokLy9nJykKICAgIGNkICRzdGFydApkb25lCmBgYAoKVGhlIGFib3ZlIGNvbW1hbmQgbGluZSBpbnZvY2F0aW9uIHByb2R1Y2VkIGEgc2VyaWVzIG9mIHRyaW1taW5nIGpvYnMKd2hpY2ggd2hlbiBleGFtaW5lZCBsb29rIGxpa2UgdGhpcyAoSSBhbSBvbmx5IHNob3dpbmcgZXhhbXBsZXMgZnJvbQpQQTE0X2V4b1VUWSwgYW5kIGFtIGxlYXZpbmcgb2ZmIHRoZSBiZWdpbm5pbmcgYW5kIGVuZCkuCgojIyMgUmVzdWx0aW5nIHRyaW1tZXIgc2NyaXB0CgpgYGB7YmFzaCB0cmltb21hdGljLCBldmFsPUZBTFNFfQojIyBUaGlzIGlzIGEgcG9ydGlvbiBvZiBmaWxlOgojIyAgcHJlcHJvY2Vzc2luZy9QQTE0X2V4b1VUWS9zY3JpcHRzLzAxdHJpbV83X1VUWV9TMTM4X1IxXzAwMS5zaAoKbW9kdWxlIGFkZCB0cmltb21hdGljCm1rZGlyIC1wIG91dHB1dHMvMDF0cmltb21hdGljCiMjIE5vdGUgdGhhdCB0cmltb21hdGljIHByaW50cyBhbGwgb3V0cHV0IGFuZCBlcnJvcnMgdG8gU1RERVJSLCBzbyBzZW5kIGJvdGggdG8gb3V0cHV0CnRyaW1tb21hdGljIFBFIFwKICAtdGhyZWFkcyAxIFwKICAtcGhyZWQzMyBcCiAgN19VVFlfUzEzOF9SMV8wMDEuZmFzdHEuZ3ogN19VVFlfUzEzOF9SMl8wMDEuZmFzdHEuZ3ogXAogIDdfVVRZX1MxMzhfUjFfMDAxLXRyaW1tZWRfcGFpcmVkLmZhc3RxIDdfVVRZX1MxMzhfUjFfMDAxLXRyaW1tZWRfdW5wYWlyZWQuZmFzdHEgXAogIDdfVVRZX1MxMzhfUjJfMDAxLXRyaW1tZWRfcGFpcmVkLmZhc3RxIDdfVVRZX1MxMzhfUjJfMDAxLXRyaW1tZWRfdW5wYWlyZWQuZmFzdHEgXAogICBJTExVTUlOQUNMSVA6L2ZzL2NiY2Itc29mdHdhcmUvUmVkSGF0LTgteDg2XzY0L2xvY2FsL2N5b2EvMjAyMzAyL3ByZWZpeC9saWIvcGVybDUvYXV0by9zaGFyZS9kaXN0L0Jpby1BZHZlbnR1cmUvZ2Vub21lL2FkYXB0ZXJzLmZhOjI6MjA6MTA6MjprZWVwQm90aFJlYWRzICBcCiAgU0xJRElOR1dJTkRPVzo0OjIwIE1JTkxFTjo1MCBcCiAgMT5vdXRwdXRzLzAxdHJpbW9tYXRpYy83X1VUWV9TMTM4X1IxXzAwMS10cmltb21hdGljLnN0ZG91dCBcCiAgMj5vdXRwdXRzLzAxdHJpbW9tYXRpYy83X1VUWV9TMTM4X1IxXzAwMS10cmltb21hdGljLnN0ZGVycgpleGNlcHRlZD0kKCB7IGdyZXAgIkV4Y2VwdGlvbiIgIm91dHB1dHMvMDF0cmltb21hdGljLzdfVVRZX1MxMzhfUjFfMDAxLXRyaW1vbWF0aWMuc3Rkb3V0IiB8fCB0ZXN0ICQ/ID0gMTsgfSApCmBgYAoKT25lIHRoaW5nIEkgZGlkIG5vdCBpbmNsdWRlIGluIHRoZSBhYm92ZTogdXBvbiBjb21wbGV0aW9uLCB0aGUgc2NyaXB0CmFnZ3Jlc3NpdmVseSBjb21wcmVzc2VzIHRoZSB0cmltbWVkIG91dHB1dCBhbmQgc3ltYm9saWNhbGx5IGxpbmtzIGl0CnRvIHIxX3RyaW1tZWQuZmFzdHEueHogYW5kIHIyX3RyaW1tZWQuZmFzdHEueHouICBUaHVzIGFueSBmb2xsb3dpbmcKc3RlcHMgY2FuIHVzZSB0aGUgc2FtZSBpbnB1dCBuYW1lIChyMV90cmltbWVkLmZhc3RxLnh6OnIyX3RyaW1tZWQuZmFzdHEueHopLgoKIyMgTWFwcGluZwoKTXkgZGVmYXVsdCBtYXBwZXJzIHJ1biB0aGUgYWN0dWFsIGFsaWdubWVudCwgY29udmVydCBpdCB0byBhCmNvbXByZXNzZWQvaW5kZXhlZCBiYW0sIGFuZCBjb3VudCBpdCBhZ2FpbnN0IHRoZSByZWZlcmVuY2UgZ2Vub21lLiAgSW4KdGhpcyBjb250ZXh0LCB0aGUgY291bnRpbmcgaXMgYSBsaXR0bGUgc2lsbHksIGJ1dCBkb2VzIGhhdmUgdGhlCnBvdGVudGlhbCB0byBoZWxwIGZpbmQgZHVwbGljYXRpb25zIGFuZCBzdWNoLgoKCmBgYHtiYXNoIGhpc2F0X2N5b2EsIGV2YWw9RkFMU0V9CmNkIHByZXByb2Nlc3NpbmcKc3RhcnQ9JChwd2QpCmZvciBpIGluICQoL2Jpbi9scyAtZCBQQTE0Kik7IGRvCiAgICBjZCAkaQogICAgY3lvYSAtLW1ldGhvZCBoaXNhdCAtLWlucHV0IHIxX3RyaW1tZWQuZmFzdHEueHo6cjJfdHJpbW1lZC5mYXN0cS54eiBcCiAgICAgICAgIC0tc3RyYW5kZWQgbm8gLS1zcGVjaWVzIHBhZXJ1Z2lub3NhX3BhMTQgLS1nZmZfdHlwZSBnZW5lIC0tZ2ZmX3RhZyBBbGlhcwogICAgY2QgJHN0YXJ0CmRvbmUKCiMjIEhlcmUgaXMgd2hhdCBJIHJhbiBmb3IgUEFLCmNkIHByZXByb2Nlc3NpbmcKc3RhcnQ9JChwd2QpCmZvciBpIGluICQoL2Jpbi9scyAtZCBQQUsqKTsgZG8KICAgIGNkICRpCiAgICBjeW9hIC0tbWV0aG9kIGhpc2F0IC0taW5wdXQgcjFfdHJpbW1lZC5mYXN0cS54ejpyMl90cmltbWVkLmZhc3RxLnh6IFwKICAgICAgICAgLS1zdHJhbmRlZCBubyAtLXNwZWNpZXMgcGFlcnVnaW5vc2FfcGEwMSAtLWdmZl90eXBlIGdlbmUgLS1nZmZfdGFnIGxvY3VzX3RhZwogICAgY2QgJHN0YXJ0CmRvbmUKYGBgCgojIyMgVGhlIHJlc3VsdGluZyBtYXBwZXIgc2NyaXB0IHJ1biBieSB0aGUgY2x1c3RlcgoKU2ltaWxhcmx5LCBJIGFtIGp1c3QgcHV0dGluZyB0aGUgbWVhdHkgcGFydC4KCmBgYHtiYXNoIGhpc2F0X21hcHBlciwgZXZhbD1GQUxTRX0KbW9kdWxlIGFkZCBoaXNhdDIgc2FtdG9vbHMgaHRzZXEgYmFtdG9vbHMKbWtkaXIgLXAgb3V0cHV0cy80MGhpc2F0Ml9wYWVydWdpbm9zYV9wYTE0Cmhpc2F0MiAteCAke0hPTUV9L2xpYnJhcmllcy9nZW5vbWUvaW5kZXhlcy9wYWVydWdpbm9zYV9wYTE0ICBcCiAgLXAgOCBcCiAgLXEgICAtMSA8KGxlc3MgL2hvbWUvdHJleS9zc2hmcy9zY3JhdGNoL2F0Yi9kbmFzZXEvcGFlcnVnaW5vc2Ffc3RyYWluc18yMDIzMDQvcHJlcHJvY2Vzc2luZy9QQTE0X2V4b1VUWS9yMV90cmltbWVkLmZhc3RxLnh6KSAtMiA8KGxlc3MgL2hvbWUvdHJleS9zc2hmcy9zY3JhdGNoL2F0Yi9kbmFzZXEvcGFlcnVnaW5vc2Ffc3RyYWluc18yMDIzMDQvcHJlcHJvY2Vzc2luZy9QQTE0X2V4b1VUWS9yMl90cmltbWVkLmZhc3RxLnh6KSAgXAogIC0tcGhyZWQzMyBcCiAgLS11biBvdXRwdXRzLzQwaGlzYXQyX3BhZXJ1Z2lub3NhX3BhMTQvdW5hbGRpc19wYWVydWdpbm9zYV9wYTE0X2dlbm9tZS5mYXN0cSBcCiAgLS1hbCBvdXRwdXRzLzQwaGlzYXQyX3BhZXJ1Z2lub3NhX3BhMTQvYWxkaXNfcGFlcnVnaW5vc2FfcGExNF9nZW5vbWUuZmFzdHEgXAogIC0tdW4tY29uYyBvdXRwdXRzLzQwaGlzYXQyX3BhZXJ1Z2lub3NhX3BhMTQvdW5hbGNvbl9wYWVydWdpbm9zYV9wYTE0X2dlbm9tZS5mYXN0cSBcCiAgLS1hbC1jb25jIG91dHB1dHMvNDBoaXNhdDJfcGFlcnVnaW5vc2FfcGExNC9hbGNvbl9wYWVydWdpbm9zYV9wYTE0X2dlbm9tZS5mYXN0cSBcCiAgLVMgb3V0cHV0cy80MGhpc2F0Ml9wYWVydWdpbm9zYV9wYTE0L3BhZXJ1Z2lub3NhX3BhMTRfZ2Vub21lLnNhbSBcCiAgMj5vdXRwdXRzLzQwaGlzYXQyX3BhZXJ1Z2lub3NhX3BhMTQvaGlzYXQyX3BhZXJ1Z2lub3NhX3BhMTRfZ2Vub21lX1BBMTRfZXhvVVRZLnN0ZGVyciBcCiAgMT5vdXRwdXRzLzQwaGlzYXQyX3BhZXJ1Z2lub3NhX3BhMTQvaGlzYXQyX3BhZXJ1Z2lub3NhX3BhMTRfZ2Vub21lX1BBMTRfZXhvVVRZLnN0ZG91dApgYGAKCiMjIyBDb252ZXJzaW9uIHRvIGJhbSBzY3JpcHQKClRoZSBhYm92ZSBjeW9hIGludm9jYXRpb24gYWxzbyBjcmVhdGVzIHRoaXMgc2NyaXB0LiAgSXQgaXMgYSBsaXR0bGUKbG9uZyBiZWNhdXNlIGl0IGRvZXMgc29tZSBjaGVja3MgYW5kIGNyZWF0ZXMgYSBjb3VwbGUgb2YgZmlsdGVyZWQKdmVyc2lvbnMgb2YgdGhlIG91dHB1dC4KCmBgYHtiYXNoIHNhbTJiYW1fc2NyaXB0LCBldmFsPUZBTFNFfQptb2R1bGUgYWRkIHNhbXRvb2xzIGJhbXRvb2xzCgplY2hvICJTdGFydGluZyBzYW10b29scyIKaWYgW1sgLWYgIm91dHB1dHMvNDBoaXNhdDJfcGFlcnVnaW5vc2FfcGExNC9wYWVydWdpbm9zYV9wYTE0X2dlbm9tZS5iYW0iICYmIC1mICJvdXRwdXRzLzQwaGlzYXQyX3BhZXJ1Z2lub3NhX3BhMTQvcGFlcnVnaW5vc2FfcGExNF9nZW5vbWUuc2FtIiBdXTsgdGhlbgogIGVjaG8gIkJvdGggdGhlIGJhbSBhbmQgc2FtIGZpbGVzIGV4aXN0LCByZXJ1bm5pbmcuIgplbGlmIFtbIC1mICJvdXRwdXRzLzQwaGlzYXQyX3BhZXJ1Z2lub3NhX3BhMTQvcGFlcnVnaW5vc2FfcGExNF9nZW5vbWUuYmFtIiBdXTsgdGhlbgogIGVjaG8gIlRoZSBvdXRwdXQgZmlsZSBleGlzdHMsIHF1aXR0aW5nLiIKICBleGl0IDAKZWxpZiBbWyAhIC1mICJvdXRwdXRzLzQwaGlzYXQyX3BhZXJ1Z2lub3NhX3BhMTQvcGFlcnVnaW5vc2FfcGExNF9nZW5vbWUuc2FtIiBdXTsgdGhlbgogIGVjaG8gIkNvdWxkIG5vdCBmaW5kIHRoZSBzYW10b29scyBpbnB1dCBmaWxlLiIKICBleGl0IDEKZmkKCiMjIElmIGEgcHJldmlvdXMgc29ydCBmaWxlIGV4aXN0cyBkdWUgdG8gcnVubmluZyBvdXQgb2YgbWVtb3J5LAojIyB0aGVuIHdlIG5lZWQgdG8gZ2V0IHJpZCBvZiB0aGVtIGZpcnN0LgojIyBoZzM4XzEwMF9nZW5vbWUtc29ydGVkLmJhbS50bXAuMDAwMC5iYW0KaWYgW1sgLWYgIm91dHB1dHMvNDBoaXNhdDJfcGFlcnVnaW5vc2FfcGExNC9wYWVydWdpbm9zYV9wYTE0X2dlbm9tZS5iYW0udG1wLjAwMC5iYW0iIF1dOyB0aGVuCiAgcm0gLWYgb3V0cHV0cy80MGhpc2F0Ml9wYWVydWdpbm9zYV9wYTE0L3BhZXJ1Z2lub3NhX3BhMTRfZ2Vub21lLmJhbS50bXAuKi5iYW0KZmkKc2FtdG9vbHMgdmlldyAtdSAtdCAke0hPTUV9L2xpYnJhcmllcy9nZW5vbWUvcGFlcnVnaW5vc2FfcGExNC5mYXN0YSBcCiAgLVMgb3V0cHV0cy80MGhpc2F0Ml9wYWVydWdpbm9zYV9wYTE0L3BhZXJ1Z2lub3NhX3BhMTRfZ2Vub21lLnNhbSAtbyBvdXRwdXRzLzQwaGlzYXQyX3BhZXJ1Z2lub3NhX3BhMTQvcGFlcnVnaW5vc2FfcGExNF9nZW5vbWUuYmFtICBcCiAgMj5vdXRwdXRzLzQwaGlzYXQyX3BhZXJ1Z2lub3NhX3BhMTQvcGFlcnVnaW5vc2FfcGExNF9nZW5vbWUuYmFtX3NhbXRvb2xzLnN0ZGVyciBcCiAgMT5vdXRwdXRzLzQwaGlzYXQyX3BhZXJ1Z2lub3NhX3BhMTQvcGFlcnVnaW5vc2FfcGExNF9nZW5vbWUuYmFtX3NhbXRvb2xzLnN0ZG91dAoKZWNobyAiRmlyc3Qgc2FtdG9vbHMgY29tbWFuZCBmaW5pc2hlZCB3aXRoICQ/IgpzYW10b29scyBzb3J0IC1sIDkgb3V0cHV0cy80MGhpc2F0Ml9wYWVydWdpbm9zYV9wYTE0L3BhZXJ1Z2lub3NhX3BhMTRfZ2Vub21lLmJhbSBcCiAgLW8gb3V0cHV0cy80MGhpc2F0Ml9wYWVydWdpbm9zYV9wYTE0L3BhZXJ1Z2lub3NhX3BhMTRfZ2Vub21lLXNvcnRlZC5iYW0gXAogIDI+Pm91dHB1dHMvNDBoaXNhdDJfcGFlcnVnaW5vc2FfcGExNC9wYWVydWdpbm9zYV9wYTE0X2dlbm9tZS5iYW1fc2FtdG9vbHMuc3RkZXJyIFwKICAxPj5vdXRwdXRzLzQwaGlzYXQyX3BhZXJ1Z2lub3NhX3BhMTQvcGFlcnVnaW5vc2FfcGExNF9nZW5vbWUuYmFtX3NhbXRvb2xzLnN0ZG91dApybSBvdXRwdXRzLzQwaGlzYXQyX3BhZXJ1Z2lub3NhX3BhMTQvcGFlcnVnaW5vc2FfcGExNF9nZW5vbWUuYmFtCnJtIG91dHB1dHMvNDBoaXNhdDJfcGFlcnVnaW5vc2FfcGExNC9wYWVydWdpbm9zYV9wYTE0X2dlbm9tZS5zYW0KbXYgb3V0cHV0cy80MGhpc2F0Ml9wYWVydWdpbm9zYV9wYTE0L3BhZXJ1Z2lub3NhX3BhMTRfZ2Vub21lLXNvcnRlZC5iYW0gb3V0cHV0cy80MGhpc2F0Ml9wYWVydWdpbm9zYV9wYTE0L3BhZXJ1Z2lub3NhX3BhMTRfZ2Vub21lLmJhbQpzYW10b29scyBpbmRleCBvdXRwdXRzLzQwaGlzYXQyX3BhZXJ1Z2lub3NhX3BhMTQvcGFlcnVnaW5vc2FfcGExNF9nZW5vbWUuYmFtIFwKICAyPj5vdXRwdXRzLzQwaGlzYXQyX3BhZXJ1Z2lub3NhX3BhMTQvcGFlcnVnaW5vc2FfcGExNF9nZW5vbWUuYmFtX3NhbXRvb2xzLnN0ZGVyciBcCiAgMT4+b3V0cHV0cy80MGhpc2F0Ml9wYWVydWdpbm9zYV9wYTE0L3BhZXJ1Z2lub3NhX3BhMTRfZ2Vub21lLmJhbV9zYW10b29scy5zdGRvdXQKZWNobyAiU2Vjb25kIHNhbXRvb2xzIGNvbW1hbmQgZmluaXNoZWQgd2l0aCAkPyIKYmFtdG9vbHMgc3RhdHMgLWluIG91dHB1dHMvNDBoaXNhdDJfcGFlcnVnaW5vc2FfcGExNC9wYWVydWdpbm9zYV9wYTE0X2dlbm9tZS5iYW0gXAogIDI+Pm91dHB1dHMvNDBoaXNhdDJfcGFlcnVnaW5vc2FfcGExNC9wYWVydWdpbm9zYV9wYTE0X2dlbm9tZS5iYW1fc2FtdG9vbHMuc3RhdHMgMT4mMgplY2hvICJCYW10b29scyBmaW5pc2hlZCB3aXRoICQ/IgoKIyMgVGhlIGZvbGxvd2luZyB3aWxsIGZhaWwgaWYgdGhpcyBpcyBzaW5nbGUtZW5kZWQuCnNhbXRvb2xzIHZpZXcgLWIgLWYgMiBcCiAgLW8gb3V0cHV0cy80MGhpc2F0Ml9wYWVydWdpbm9zYV9wYTE0L3BhZXJ1Z2lub3NhX3BhMTRfZ2Vub21lLXBhaXJlZC5iYW0gXAogIG91dHB1dHMvNDBoaXNhdDJfcGFlcnVnaW5vc2FfcGExNC9wYWVydWdpbm9zYV9wYTE0X2dlbm9tZS5iYW0gXAogIDI+Pm91dHB1dHMvNDBoaXNhdDJfcGFlcnVnaW5vc2FfcGExNC9wYWVydWdpbm9zYV9wYTE0X2dlbm9tZS5iYW1fc2FtdG9vbHMuc3RkZXJyIFwKICAxPj5vdXRwdXRzLzQwaGlzYXQyX3BhZXJ1Z2lub3NhX3BhMTQvcGFlcnVnaW5vc2FfcGExNF9nZW5vbWUuYmFtX3NhbXRvb2xzLnN0ZG91dApzYW10b29scyBpbmRleCBvdXRwdXRzLzQwaGlzYXQyX3BhZXJ1Z2lub3NhX3BhMTQvcGFlcnVnaW5vc2FfcGExNF9nZW5vbWUtcGFpcmVkLmJhbSBcCiAgMj4+b3V0cHV0cy80MGhpc2F0Ml9wYWVydWdpbm9zYV9wYTE0L3BhZXJ1Z2lub3NhX3BhMTRfZ2Vub21lLmJhbV9zYW10b29scy5zdGRlcnIgXAogIDE+Pm91dHB1dHMvNDBoaXNhdDJfcGFlcnVnaW5vc2FfcGExNC9wYWVydWdpbm9zYV9wYTE0X2dlbm9tZS5iYW1fc2FtdG9vbHMuc3Rkb3V0CmJhbXRvb2xzIHN0YXRzIC1pbiBvdXRwdXRzLzQwaGlzYXQyX3BhZXJ1Z2lub3NhX3BhMTQvcGFlcnVnaW5vc2FfcGExNF9nZW5vbWUtcGFpcmVkLmJhbSBcCiAgMj4+b3V0cHV0cy80MGhpc2F0Ml9wYWVydWdpbm9zYV9wYTE0L3BhZXJ1Z2lub3NhX3BhMTRfZ2Vub21lLmJhbV9zYW10b29scy5zdGF0cyAxPiYyCgpiYW10b29scyBmaWx0ZXIgLXRhZyBYTTowIFwKICAtaW4gb3V0cHV0cy80MGhpc2F0Ml9wYWVydWdpbm9zYV9wYTE0L3BhZXJ1Z2lub3NhX3BhMTRfZ2Vub21lLmJhbSBcCiAgLW91dCBvdXRwdXRzLzQwaGlzYXQyX3BhZXJ1Z2lub3NhX3BhMTQvcGFlcnVnaW5vc2FfcGExNF9nZW5vbWUtc29ydGVkX25vbWlzbWF0Y2guYmFtIFwKICAyPj5vdXRwdXRzLzQwaGlzYXQyX3BhZXJ1Z2lub3NhX3BhMTQvcGFlcnVnaW5vc2FfcGExNF9nZW5vbWUuYmFtX3NhbXRvb2xzLnN0YXRzIDE+JjIKZWNobyAiYmFtdG9vbHMgZmlsdGVyIGZpbmlzaGVkIHdpdGg6ICQ/IgpzYW10b29scyBpbmRleCBcCiAgb3V0cHV0cy80MGhpc2F0Ml9wYWVydWdpbm9zYV9wYTE0L3BhZXJ1Z2lub3NhX3BhMTRfZ2Vub21lLXNvcnRlZF9ub21pc21hdGNoLmJhbSBcCiAgMj4+b3V0cHV0cy80MGhpc2F0Ml9wYWVydWdpbm9zYV9wYTE0L3BhZXJ1Z2lub3NhX3BhMTRfZ2Vub21lLmJhbV9zYW10b29scy5zdGRlcnIgXAogIDE+Pm91dHB1dHMvNDBoaXNhdDJfcGFlcnVnaW5vc2FfcGExNC9wYWVydWdpbm9zYV9wYTE0X2dlbm9tZS5iYW1fc2FtdG9vbHMuc3Rkb3V0CmVjaG8gImZpbmFsIHNhbXRvb2xzIGluZGV4IGZpbmlzaGVkIHdpdGg6ICQ/IgpgYGAKCiMjIyBDb3VudGluZyBhZ2FpbnN0IHRoZSBnZW5vbWUKCk5vdGUgdGhhdCB0aGlzIHN0ZXAgaXMgbm90IHJlYWxseSB1c2VmdWwgZm9yIGEgZG5hc2VxIGRhdGFzZXQgaW4gbW9zdAppbnN0YW5jZXMuICBJIGFsc28gaGF2ZSB0aGUgZGVmYXVsdCBvcmllbnRhdGlvbiBzZXQgdG8gcmV2ZXJzZSBiZWNhdXNlCm1vc3Qgb2YgdGhlIHNhbXBsZXMgb2ZmIG91ciBzZXF1ZW5jZXIgYXJlIHJldmVyc2VkOyBidXQgdGhhdCBpcyBsaWtlbHkKbm90IHRydWUgZm9yIHRoaXMgZGF0YXNldC4gIElmIGl0IHR1cm5zIG91dCB3ZSBhY3R1YWxseSBjYXJlIGFib3V0CnRoZXNlIGNvdW50cywgSSBtYXkgbmVlZCB0byBjb21lIGJhY2sgYW5kIHJlcnVuIHRoZXNlLgoKYGBge2Jhc2ggY291bnRpbmdfc2NyaXB0LCBldmFsPUZBTFNFfQptb2R1bGUgYWRkIGh0c2VxCgpodHNlcS1jb3VudCBcCiAgLXEgLWYgYmFtIFwKICAtcyByZXZlcnNlIC1hIDAgXAogICAtLXR5cGUgYWxsICAtLWlkYXR0ciBBbGlhcyBcCiAgb3V0cHV0cy80MGhpc2F0Ml9wYWVydWdpbm9zYV9wYTE0L3BhZXJ1Z2lub3NhX3BhMTRfZ2Vub21lLXBhaXJlZC5iYW0gXAogIC9ob21lL3RyZXkvbGlicmFyaWVzL2dlbm9tZS9wYWVydWdpbm9zYV9wYTE0LmdmZiBcCiAgMj5vdXRwdXRzLzQwaGlzYXQyX3BhZXJ1Z2lub3NhX3BhMTQvcGFlcnVnaW5vc2FfcGExNF9nZW5vbWUtcGFpcmVkX3NyZXZlcnNlX2FsbF9BbGlhcy5zdGRlcnIgXAogIDE+b3V0cHV0cy80MGhpc2F0Ml9wYWVydWdpbm9zYV9wYTE0L3BhZXJ1Z2lub3NhX3BhMTRfZ2Vub21lLXBhaXJlZF9zcmV2ZXJzZV9hbGxfQWxpYXMuY291bnQKeHogLWYgLTllIG91dHB1dHMvNDBoaXNhdDJfcGFlcnVnaW5vc2FfcGExNC9wYWVydWdpbm9zYV9wYTE0X2dlbm9tZS1wYWlyZWRfc3JldmVyc2VfYWxsX0FsaWFzLmNvdW50CmBgYAoKIyMgVmFyaWFudCBzZWFyY2gKCkkgdGVuZCB0byBsaWtlIHRvIHVzZSBmcmVlYmF5ZXMgZm9yIHRoaXMuICBJdCBpcyBhIGxpdHRsZQpjb25zZXJ2YXRpdmUsIGJ1dCBJIHRoaW5rIGl0IHNlZW1zIHRvIHdvcmsgcXVpdGUgd2VsbC4gIEkgY2FuIGFsc28gdXNlCm1waWxldXAgYW5kIHNuaXBweS4gIGZyZWViYXllcyBhbmQgbXBpbGV1cCBhcmUgc2V0dXAgdG8gZmVlZCBhCnBvc3QtcHJvY2Vzc2luZyBzY3JpcHQgd2hpY2ggSSB0aGluayBpcyBraW5kIG9mIGZ1biBhbmQgd2lsbCBiZQpkZWNyaWJlZCBtb21lbnRhcmlseS4KCmBgYHtiYXNoIGN5b2FfZnJlZWJheWVzLCBldmFsPUZBTFNFfQpjZCBwcmVwcm9jZXNzaW5nCnN0YXJ0PSQocHdkKQpmb3IgaSBpbiAkKC9iaW4vbHMgLWQgUEExNCopOyBkbwogICAgY2QgJGkKICAgIGN5b2EgLS1tZXRob2QgZnJlZWJheWVzIFwKICAgICAgICAgLS1pbnB1dCBvdXRwdXRzLzQwaGlzYXQyX3BhZXJ1Z2lub3NhX3BhMTQvcGFlcnVnaW5vc2FfcGExNF9nZW5vbWUtcGFpcmVkLmJhbSBcCiAgICAgICAgIC0tc3BlY2llcyBwYWVydWdpbm9zYV9wYTE0IC0tZ2ZmX3R5cGUgZ2VuZSAtLWdmZl90YWcgQWxpYXMgLS1pbnRyb24gMAogICAgY2QgJHN0YXJ0CmRvbmUKCiMjIEhlcmUgaXMgd2hhdCBJIHJhbiBmb3IgUEFLCmZvciBpIGluICQoL2Jpbi9scyAtZCBQQUsqKTsgZG8KICAgIGNkICRpCiAgICBjeW9hIC0tbWV0aG9kIGZyZWViYXllcyBcCiAgICAgICAgIC0taW5wdXQgb3V0cHV0cy80MGhpc2F0Ml9wYWVydWdpbm9zYV9wYTAxL3BhZXJ1Z2lub3NhX3BhMDFfZ2Vub21lLXBhaXJlZC5iYW0gXAogICAgICAgICAtLXNwZWNpZXMgcGFlcnVnaW5vc2FfcGEwMSAtLWdmZl90eXBlIGdlbmUgLS1nZmZfdGFnIGxvY3VzX3RhZyAtLWludHJvbiAwCiAgICBjZCAkc3RhcnQKZG9uZQpgYGAKCiMjIyBGcmVlYmF5ZXMgc2NyaXB0CgpVbmxpa2UgaGlzYXQsIEkgaW5jbHVkZSB0aGUgY29udmVyc2lvbiB0byB0aGUKYmluYXJ5L2NvbXByZXNzZWQvaW5kZXhlZCBmb3JtYXQgd2l0aCB0aGUgaW52b2NhdGlvbiBvZiB0aGUgdmFyaWFudApzZWFyY2guICBJIGFsc28gaW5jbHVkZSB0aGUgZHVwbGljYXRlIHNlYXJjaCBmdW5jdGlvbmFsaXR5IGZyb20gZ2F0ay4KCmBgYHtiYXNoIGZyZWViYXllc19zY3JpcHQsIGV2YWw9RkFMU0V9Cm1vZHVsZSBhZGQgZ2F0ayBmcmVlYmF5ZXMgbGliZ3NsIGxpYmh0cyBzYW10b29scyBiY2Z0b29scyB2Y2Z0b29scwoKbWtkaXIgLXAgb3V0cHV0cy81MGZyZWViYXllc19wYWVydWdpbm9zYV9wYTE0CmdhdGsgTWFya0R1cGxpY2F0ZXMgXAogIC1JIG91dHB1dHMvNDBoaXNhdDJfcGFlcnVnaW5vc2FfcGExNC9wYWVydWdpbm9zYV9wYTE0X2dlbm9tZS5iYW0gXAogIC1PIG91dHB1dHMvNTBmcmVlYmF5ZXNfcGFlcnVnaW5vc2FfcGExNC9wYWVydWdpbm9zYV9wYTE0X2dlbm9tZV9kZWR1cGxpY2F0ZWQuYmFtIFwKICAtTSBvdXRwdXRzLzUwZnJlZWJheWVzX3BhZXJ1Z2lub3NhX3BhMTQvZGVkdXBsaWNhdGlvbl9zdGF0cy50eHQgLS1SRU1PVkVfRFVQTElDQVRFUyB0cnVlIC0tQ09NUFJFU1NJT05fTEVWRUwgOSBcCiAgMj5vdXRwdXRzLzUwZnJlZWJheWVzX3BhZXJ1Z2lub3NhX3BhMTQvZGVkdXBsaWNhdGlvbi5zdGRlcnIgXAogIDE+b3V0cHV0cy81MGZyZWViYXllc19wYWVydWdpbm9zYV9wYTE0L2RlZHVwbGljYXRpb24uc3Rkb3V0CmVjaG8gIkZpbmlzaGVkIGdhdGsgZGVkdXBsaWNhdGlvbi4iID4+IG91dHB1dHMvNTBmcmVlYmF5ZXNfcGFlcnVnaW5vc2FfcGExNC9wYWVydWdpbm9zYV9wYTE0LnN0ZG91dApzYW10b29scyBpbmRleCBvdXRwdXRzLzUwZnJlZWJheWVzX3BhZXJ1Z2lub3NhX3BhMTQvcGFlcnVnaW5vc2FfcGExNF9nZW5vbWVfZGVkdXBsaWNhdGVkLmJhbQplY2hvICJGaW5pc2hlZCBzYW10b29scyBpbmRleC4iID4+IG91dHB1dHMvNTBmcmVlYmF5ZXNfcGFlcnVnaW5vc2FfcGExNC9wYWVydWdpbm9zYV9wYTE0LnN0ZG91dApmcmVlYmF5ZXMgLWYgL2hvbWUvdHJleS9saWJyYXJpZXMvZ2Vub21lL3BhZXJ1Z2lub3NhX3BhMTQuZmFzdGEgXAogIC12IG91dHB1dHMvNTBmcmVlYmF5ZXNfcGFlcnVnaW5vc2FfcGExNC9wYWVydWdpbm9zYV9wYTE0LnZjZiBcCiAgb3V0cHV0cy81MGZyZWViYXllc19wYWVydWdpbm9zYV9wYTE0L3BhZXJ1Z2lub3NhX3BhMTRfZ2Vub21lX2RlZHVwbGljYXRlZC5iYW0gXAogIDE+Pm91dHB1dHMvNTBmcmVlYmF5ZXNfcGFlcnVnaW5vc2FfcGExNC9wYWVydWdpbm9zYV9wYTE0LnN0ZG91dCBcCiAgMj4+b3V0cHV0cy81MGZyZWViYXllc19wYWVydWdpbm9zYV9wYTE0L3BhZXJ1Z2lub3NhX3BhMTQuc3RkZXJyCmVjaG8gIkZpbmlzaGVkIGZyZWViYXllcy4iID4+IG91dHB1dHMvNTBmcmVlYmF5ZXNfcGFlcnVnaW5vc2FfcGExNC9wYWVydWdpbm9zYV9wYTE0LnN0ZG91dApiY2Z0b29scyBjb252ZXJ0IG91dHB1dHMvNTBmcmVlYmF5ZXNfcGFlcnVnaW5vc2FfcGExNC9wYWVydWdpbm9zYV9wYTE0LnZjZiBcCiAgLU9iIC1vIG91dHB1dHMvNTBmcmVlYmF5ZXNfcGFlcnVnaW5vc2FfcGExNC9wYWVydWdpbm9zYV9wYTE0LmJjZiBcCiAgMj4+b3V0cHV0cy81MGZyZWViYXllc19wYWVydWdpbm9zYV9wYTE0L3BhZXJ1Z2lub3NhX3BhMTQuc3RkZXJyIFwKICAxPj5vdXRwdXRzLzUwZnJlZWJheWVzX3BhZXJ1Z2lub3NhX3BhMTQvcGFlcnVnaW5vc2FfcGExNC5zdGRvdXQKZWNobyAiRmluaXNoZWQgYmNmdG9vbHMgY29udmVydC4iID4+IG91dHB1dHMvNTBmcmVlYmF5ZXNfcGFlcnVnaW5vc2FfcGExNC9wYWVydWdpbm9zYV9wYTE0LnN0ZG91dApiY2Z0b29scyBpbmRleCBvdXRwdXRzLzUwZnJlZWJheWVzX3BhZXJ1Z2lub3NhX3BhMTQvcGFlcnVnaW5vc2FfcGExNC5iY2YgXAogIDI+Pm91dHB1dHMvNTBmcmVlYmF5ZXNfcGFlcnVnaW5vc2FfcGExNC9wYWVydWdpbm9zYV9wYTE0LnN0ZGVyciBcCiAgMT4+b3V0cHV0cy81MGZyZWViYXllc19wYWVydWdpbm9zYV9wYTE0L3BhZXJ1Z2lub3NhX3BhMTQuc3Rkb3V0CmVjaG8gIkZpbmlzaGVkIGJjZnRvb2xzIGluZGV4LiIgPj4gb3V0cHV0cy81MGZyZWViYXllc19wYWVydWdpbm9zYV9wYTE0L3BhZXJ1Z2lub3NhX3BhMTQuc3Rkb3V0CnJtIG91dHB1dHMvNTBmcmVlYmF5ZXNfcGFlcnVnaW5vc2FfcGExNC9wYWVydWdpbm9zYV9wYTE0LnZjZgpgYGAKClRoZSByZXN1bHQgZnJvbSB0aGUgYWJvdmUgZnJlZWJheWVzIHNjcmlwdCBpcyBhIGJjZiBjb250YWluaW5nIHRoZQpoaWdoLXF1YWxpdHkgb2JzZXJ2ZWQgdmFyaWFudHMuICBUaGUgY3lvYSBpbnZvY2F0aW9uIGFsc28gY3JlYXRlcyB0aGUKZm9sbG93aW5nIHNjcmlwdCwgd2hpY2ggd2lsbCByZXF1aXJlIGEgYml0IG9mIGV4cGxhbmF0aW9uLgoKYGBge3BlcmwgY3JlYXRlX3ZhcmlhbnRfZnVuLCBldmFsPUZBTFNFfQp1c2UgQmlvOjpBZHZlbnR1cmU6OlNOUDsKbXkgJHJlc3VsdCA9ICRoLT5CaW86OkFkdmVudHVyZTo6U05QOjpTTlBfUmF0aW9fV29ya2VyKAogIGlucHV0ID0+ICdvdXRwdXRzLzUwZnJlZWJheWVzX3BhZXJ1Z2lub3NhX3BhMTQvcGFlcnVnaW5vc2FfcGExNC5iY2YnLAogIHNwZWNpZXMgPT4gJ3BhZXJ1Z2lub3NhX3BhMTQnLAogIHZjZl9tZXRob2QgPT4gJ2ZyZWViYXllcycsCiAgdmNmX2N1dG9mZiA9PiAnNScsCiAgdmNmX21pbnBjdCA9PiAnMC44JywKICBnZmZfdGFnID0+ICdBbGlhcycsCiAgZ2ZmX3R5cGUgPT4gJ2dlbmUnLAogIG91dHB1dF9kaXIgPT4gJ291dHB1dHMvNTBmcmVlYmF5ZXNfcGFlcnVnaW5vc2FfcGExNCcsCiAgb3V0cHV0ID0+ICdvdXRwdXRzLzUwZnJlZWJheWVzX3BhZXJ1Z2lub3NhX3BhMTQvYWxsX3RhZ3MudHh0JywKICBvdXRwdXRfY291bnQgPT4gJ291dHB1dHMvNTBmcmVlYmF5ZXNfcGFlcnVnaW5vc2FfcGExNC9jb3VudC50eHQnLAogIG91dHB1dF9nZW5vbWUgPT4gJ291dHB1dHMvNTBmcmVlYmF5ZXNfcGFlcnVnaW5vc2FfcGExNC9tb2RpZmllZC5mYXN0YScsCiAgb3V0cHV0X2J5X2dlbmUgPT4gJ291dHB1dHMvNTBmcmVlYmF5ZXNfcGFlcnVnaW5vc2FfcGExNC92YXJpYW50c19ieV9nZW5lLnR4dCcsCiAgb3V0cHV0X3BrbSA9PiAnb3V0cHV0cy81MGZyZWViYXllc19wYWVydWdpbm9zYV9wYTE0L3BrbS50eHQnLAopOwpgYGAKClRoZSBmdW5jdGlvbiAnU05QX1JhdGlvX1dvcmtlcigpJyByZWFkcyB0aGUgcmVmZXJlbmNlIGdlbm9tZSwgdGhlIHNldApvZiB2YXJpYW50cywgYW5kIHRoZSBnZW5vbWUgYW5ub3RhdGlvbnMgaW4gb3JkZXIgdG8gY3JlYXRlIGEgbmV3IGNvcHkKb2YgdGhlIGdlbm9tZSAobW9kaWZpZWQuZmFzdGEpIHdoaWNoIHNob3VsZCBiZSBlcXVpdmFsZW50IHRvIHRoZSBpbnB1dApyZWFkcy4gIEl0IGFsc28gcmV3cml0ZXMgdGhlIGJjZiBkYXRhIGludG8gYSBtYXRyaXggd2hpY2ggaXMgZWFzaWVyIHRvCnBsYXkgd2l0aCBpbiBSL3B5dGhvbiAoYWxsX3RhZ3MudHh0KS4gIEZpbmFsbHksIGl0IHVzZXMgdGhlIGFubm90YXRpb24KaW5mb3JtYXRpb24gdG8gZXhwbGljaXRseSBzaG93IHRoZSBhbWlubyBhY2lkIHN1YnN0aXRpb25zIG9ic2VydmVkIGluCmV2ZXJ5IE9SRiAodmFyaWFudHNfYnlfZ2VuZS50eHQpLiAgSW4gdGhlb3J5IGl0IHNob3VsZCBhbHNvIGdpdmUgYQpycGttLWVzcXVlIGNvcHkgb2YgdGhlIHZhcmlhbnRzIG9ic2VydmVkIC8gT1JGLCBidXQgSSB0dXJuZWQgdGhhdCBvZmYKYmVjYXVzZSBpdCBkb2Vzbid0IHNlZW0gdmVyeSB1c2VmdWwgYW5kIGl0IGlzIGEgbGl0dGxlIHRyaWNreSB0byBnZXQKcmlnaHQuCgojIENyZWF0aW5nIGEgc2FtcGxlIHNoZWV0CgpJbiBvcmRlciB0byBwbGF5IGZ1cnRoZXIgd2l0aCB0aGUgZGF0YSwgSSB3aWxsIG5lZWQgYSBzYW1wbGUgc2hlZXQuClNvIEkgd2lsbCBzdGFydCBvdXQgYnkgY3JlYXRpbmcgYSBibGFuayBvbmUgaW4gZXhjZWwgKGxpYnJlb2ZmaWNlKQp3aGljaCBjb250YWlucyBvbmx5IHRoZSBzYW1wbGVuYW1lcyBpbiB0aGUgc2FtZSBmb3JtYXQgYXMgbXkKZGlyZWN0b3JpZXMgaW4gcHJlcHJvY2Vzc2luZy8uCgpPbmNlIGNvbXBsZXRlZCwgSSBjYW4gdXNlIGl0IGFzIHRoZSBpbnB1dCBmb3IgbXkgaHBnbHRvb2xzIHBhY2thZ2UgYW5kCml0IHNob3VsZCBleHRyYWN0IHRoZSBpbnRlcmVzdGluZyBpbmZvcm1hdGlvbiBmcm9tIHRoZSBwcmVwcm9jZXNzaW5nCmxvZ3MgYW5kIGZpbGwgb3V0IHRoZSBzYW1wbGUgc2hlZXQgYWNjb3JkaW5nbHkuICBMZXRzIHNlZSBpZiBpdCB3b3JrcyEKCkhlcmUgaXMgdGhlIGJlZm9yZToKCmBgYHtyIHNhbXBsZV9zaGVldF9zdGFydH0Ka25pdHI6OmthYmxlKGV4dHJhY3RfbWV0YWRhdGEoInNhbXBsZV9zaGVldHMvYWxsX3NhbXBsZXMueGxzeCIpKQpgYGAKCkxpa2UgSSBzYWlkLCBub3QgbXVjaCBnb2luZyBvbi4gIExldHMgc2VlIHdoYXQgaXQgbG9va3MgbGlrZSBhZnRlciBJCnJ1biB0aGUgZ2F0aGVyZXIgb24gaXQuLi4gKE5vdGUsIEkgaGF2ZSBiZWVuIG1lYW5pbmcgdG8gY2hhbmdlIHRoaXMgdG8KZHJvcCB0aGUgdW51c2VkIGNvbHVtbnMsIGJ1dCBub3QgeWV0KS4KCmBgYHtyIHNhbXBsZV9zaGVldF9wb3N0fQpzcGVjIDwtIG1ha2VfZG5hc2VxX3NwZWMoKQpxdWVyaWVkX3NwZWNpZXMgPC0gYygicGFlcnVnaW5vc2FfcGFrIiwgInBhZXJ1Z2lub3NhX3BhMDEiLCAicGFlcnVnaW5vc2FfcGExNCIpCm1vZGlmaWVkIDwtIHNtKGdhdGhlcl9wcmVwcm9jZXNzaW5nX21ldGFkYXRhKCJzYW1wbGVfc2hlZXRzL2FsbF9zYW1wbGVzLnhsc3giLAogIHNwZWNpZXMgPSBxdWVyaWVkX3NwZWNpZXMsIHZlcmJvc2UgPSBGQUxTRSwKICBzcGVjaWZpY2F0aW9uID0gc3BlYykpCmtuaXRyOjprYWJsZShleHRyYWN0X21ldGFkYXRhKCJzYW1wbGVfc2hlZXRzL2FsbF9zYW1wbGVzX21vZGlmaWVkLnhsc3giKSkKYGBgCgpJIHJlcmFuIHRoZSBtaXNzaW5nIFBBSyBzYW1wbGVzIGFuZCBsb29rZWQgaW50byB0aGUgbG9ncy4gIEl0IG1heSBiZQp0aGUgY2FzZSB0aGF0IHRoZSBQQUsgZ2Vub21lIEkgZG93bmxvYWRlZCBpcyBvZiBzb21ld2hhdCBsb3dlciBxdWFsaXR5CnRoYW4gdGhlIFBBMTQgYW5kIHRoYXQgaXMgc2tld2luZyB0aGUgcmVzdWx0cyBzb21ld2hhdC4KCkxldHMgZ28gb25lIHNtYWxsIHN0ZXAgZnVydGhlci4gIEkgaGF2ZSBhIHNlcmllcyBvZiBtb2RpZmllZCBnZW5vbWVzCmFzIHdlbGwgYXMgdGhlIHJlZmVyZW5jZS4gIFdlIGNhbiBkbyBhIHF1aWNraWUgdHJlZSBvZiB0aGVtOgpGaXJzdCBJIHdpbGwgY29weSBlYWNoIG1vZGlmaWVkIGdlbm9tZSB0byB0aGUgdHJlZS8gZGlyZWN0b3J5IGFuZApyZW5hbWUgdGhlbSB0byB0aGUgc2FtcGxlSUQuCgpgYGB7YmFzaCBjb3B5X21vZGlmaWVkLCBldmFsPUZBTFNFfQpzdGFydD0kKHB3ZCkKbWtkaXIgdHJlZQpjZCBwcmVwcm9jZXNzaW5nCmZvciBpIGluICQoL2Jpbi9scyAtZCBQQSopOyBkbwogICAgY3AgJGkvb3V0cHV0cy81MCovcGFlcnVnaW5vc2FfcGFrLSouZmFzdGEgJHtzdGFydH0vdHJlZS8KICAgIGNwICRpL291dHB1dHMvNTAqL3BhZXJ1Z2lub3NhX3BhMTQtKi5mYXN0YSAke3N0YXJ0fS90cmVlLwpkb25lCmNkICRzdGFydApjcCB+L2xpYnJhcmllcy9nZW5vbWUvcGFlcnVnaW5vc2FfcGExNC5mYSAke3N0YXJ0fS90cmVlCmNwIH4vbGlicmFyaWVzL2dlbm9tZS9wYWVydWdpbm9zYV9wYWsuZmEgJHtzdGFydH0vdHJlZQpgYGAKCk9oLCBpdCB0dXJucyBvdXQgdGhhdCBhdCB0aGUgdGltZSBvZiB0aGlzIHdyaXRpbmcsIEkgZm9yZ290IHRvIHJ1biAzCnNhbXBsZXMsIHNvIHRoaXMgc2VjdGlvbiB3aWxsIG5lZWQgdG8gYmUgcmVkb25lLiAgQnV0IEkgY2FuIGF0IGxlYXN0CnJ1biBpdCBmb3IgdGhlIHNhbXBsZXMgdGhhdCBJIGRpZG4ndCBmb3JnZXQuCgpgYGB7ciBxdWlja190cmVlfQpmdW5reXRvd24gPC0gZ2Vub21pY19zZXF1ZW5jZV9waHlsbygidHJlZSIsIHJvb3QgPSAicGFlcnVnaW5vc2FfcGExNCIpCnBsb3QoZnVua3l0b3duJHBoeSkKYGBgCgojIENyZWF0ZSBhbiBleHByZXNzaW9uc2V0CgpUaGUgY291bnRzIGZyb20gaGlzYXQgaW4gdGhlb3J5IGFyZSBub3QgdmVyeSBpbnRlcmVzdGluZyBmb3IgRE5Bc2VxCmRhdGEsIGV4Y2VwdCBpbiB0aGlzIGluc3RhbmNlIHdlIHdhbnQgdG8gc2VlIHRoZSBjb3ZlcmFnZSBvZiB0aGUga25vY2tvdXRzLgoKYGBge3IgZXhwdH0KcGExNF9hbm5vdCA8LSBsb2FkX2dmZl9hbm5vdGF0aW9ucygifi9saWJyYXJpZXMvZ2Vub21lL3BhZXJ1Z2lub3NhX3BhMTQuZ2ZmIiwgdHlwZSA9ICJnZW5lIiwgaWRfY29sID0gIkFsaWFzIikKcm93bmFtZXMocGExNF9hbm5vdCkgPC0gcGExNF9hbm5vdFtbIkFsaWFzIl1dCgpwYTE0X2V4cHQgPC0gY3JlYXRlX2V4cHQoInNhbXBsZV9zaGVldHMvYWxsX3NhbXBsZXNfbW9kaWZpZWQueGxzeCIsIGdlbmVfaW5mbyA9IHBhMTRfYW5ub3QsCiAgICAgICAgICAgICAgICAgICAgICAgICBmaWxlX2NvbHVtbiA9ICJoaXNhdGNvdW50dGFibGVwYWVydWdpbm9zYXBhMTQiKQpwYTE0X3dyaXRlIDwtIHdyaXRlX2V4cHQocGExNF9leHB0LCBleGNlbCA9ICJleGNlbC9wYTE0X3N0cmFpbnMueGxzeCIpCgpwYWtfYW5ub3QgPC0gbG9hZF9nZmZfYW5ub3RhdGlvbnMoIn4vbGlicmFyaWVzL2dlbm9tZS9wYWVydWdpbm9zYV9wYWsuZ2ZmIiwgdHlwZSA9ICJnZW5lIiwgaWRfY29sID0gImxvY3VzX3RhZyIpCnJvd25hbWVzKHBha19hbm5vdCkgPC0gcGFrX2Fubm90W1sibG9jdXNfdGFnIl1dCnBha19leHB0IDwtIGNyZWF0ZV9leHB0KCJzYW1wbGVfc2hlZXRzL2FsbF9zYW1wbGVzX21vZGlmaWVkLnhsc3giLCBmaWxlX2NvbHVtbiA9ICJoaXNhdGNvdW50dGFibGVwYWVydWdpbm9zYXBhayIpCnBha193cml0ZSA8LSB3cml0ZV9leHB0KHBha19leHB0LCBleGNlbCA9ICJleGNlbC9wYWtfc3RyYWlucy54bHN4IikKYGBgCgojIFdyaXRlIHZhcmlhbnRzIGJ5IHNhbXBsZQoKYGBge3Igd3JpdGVfdmFyaWFudHN9CnBhMTRfdmFyaWFudHMgPC0gcERhdGEocGExNF9leHB0KVtbInZhcmlhbnRzcGVuZXRyYW5jZWZpbGVwYWVydWdpbm9zYXBhMTQiXV0KbmFtZXMocGExNF92YXJpYW50cykgPC0gcm93bmFtZXMocERhdGEocGExNF9leHB0KSkKc3RhcnQgPC0gaW5pdF94bHN4KGV4Y2VsID0gImV4Y2VsL3BhMTRfdmFyaWFudHMueGxzeCIpCndiIDwtIHN0YXJ0W1sid2IiXV0KZm9yIChzIGluIHNlcV9sZW4obGVuZ3RoKHBhMTRfdmFyaWFudHMpKSkgewogIHNhbXBsZV9uYW1lIDwtIG5hbWVzKHBhMTRfdmFyaWFudHMpW1tzXV0KICBpZiAocGExNF92YXJpYW50c1tbc11dID09ICIiKSB7CiAgICBuZXh0CiAgfQogIHNhbXBsZV9kYXRhIDwtIHJlYWRyOjpyZWFkX3RzdihwYTE0X3ZhcmlhbnRzW1tzXV0pCiAgaWYgKG5yb3coc2FtcGxlX2RhdGEpID09IDApIHsKICAgIG5leHQKICB9CiAgd3JpdHRlbiA8LSB3cml0ZV94bHN4KGRhdGEgPSBzYW1wbGVfZGF0YSwgc2hlZXQgPSBzYW1wbGVfbmFtZSwgd2IgPSB3YikKfQpzYXZlZCA8LSBvcGVueGxzeDo6c2F2ZVdvcmtib29rKHdyaXR0ZW5bWyJ3b3JrYm9vayJdXSwgZmlsZSA9ICJleGNlbC9wYTE0X3ZhcmlhbnRzLnhsc3giKQoKcGFrX3ZhcmlhbnRzIDwtIHBEYXRhKHBha19leHB0KVtbInZhcmlhbnRzcGVuZXRyYW5jZWZpbGVwYWVydWdpbm9zYXBhayJdXQpuYW1lcyhwYWtfdmFyaWFudHMpIDwtIHJvd25hbWVzKHBEYXRhKHBha19leHB0KSkKc3RhcnQgPC0gaW5pdF94bHN4KGV4Y2VsID0gImV4Y2VsL3Bha192YXJpYW50cy54bHN4IikKd2IgPC0gc3RhcnRbWyJ3YiJdXQpmb3IgKHMgaW4gc2VxX2xlbihsZW5ndGgocGFrX3ZhcmlhbnRzKSkpIHsKICBzYW1wbGVfbmFtZSA8LSBuYW1lcyhwYWtfdmFyaWFudHMpW1tzXV0KICBpZiAocGFrX3ZhcmlhbnRzW1tzXV0gPT0gIiIpIHsKICAgIG5leHQKICB9CiAgc2FtcGxlX2RhdGEgPC0gcmVhZHI6OnJlYWRfdHN2KHBha192YXJpYW50c1tbc11dKQogIGlmIChucm93KHNhbXBsZV9kYXRhKSA9PSAwKSB7CiAgICBuZXh0CiAgfQogIHdyaXR0ZW4gPC0gd3JpdGVfeGxzeChkYXRhID0gc2FtcGxlX2RhdGEsIHNoZWV0ID0gc2FtcGxlX25hbWUsIHdiID0gd2IpCn0Kc2F2ZWQgPC0gb3Blbnhsc3g6OnNhdmVXb3JrYm9vayh3cml0dGVuW1sid29ya2Jvb2siXV0sIGZpbGUgPSAiZXhjZWwvcGFrX3ZhcmlhbnRzLnhsc3giKQpgYGAKCgojIFdyaXRlIG11dGF0aW9ucyBieSBzYW1wbGUKCkluIHRoaXMgZm9sbG93aW5nIGJsb2NrIHdlIHdpbGwgaW5zdGVhZCB3cml0ZSBvdXQgdGhlIG50L2FhIG11dGF0aW9ucyBvZiBDRFMvcHJvdGVpbnMuCgpgYGB7ciB3cml0ZV9tdXRhdGlvbnN9CnBhMTRfbXV0YXRpb25zIDwtIHBEYXRhKHBhMTRfZXhwdClbWyJ2YXJpYW50c2J5Z2VuZWZpbGVwYWVydWdpbm9zYXBhMTQiXV0KbmFtZXMocGExNF9tdXRhdGlvbnMpIDwtIHJvd25hbWVzKHBEYXRhKHBhMTRfZXhwdCkpCnN0YXJ0IDwtIGluaXRfeGxzeChleGNlbCA9ICJleGNlbC9wYTE0X211dGF0aW9ucy54bHN4IikKd2IgPC0gc3RhcnRbWyJ3YiJdXQpmb3IgKHMgaW4gc2VxX2xlbihsZW5ndGgocGExNF9tdXRhdGlvbnMpKSkgewogIHNhbXBsZV9uYW1lIDwtIG5hbWVzKHBhMTRfbXV0YXRpb25zKVtbc11dCiAgaWYgKHBhMTRfbXV0YXRpb25zW1tzXV0gPT0gIiIpIHsKICAgIG5leHQKICB9CiAgc2FtcGxlX2RhdGEgPC0gcmVhZHI6OnJlYWRfdHN2KHBhMTRfbXV0YXRpb25zW1tzXV0pCiAgaWYgKG5yb3coc2FtcGxlX2RhdGEpID09IDApIHsKICAgIG5leHQKICB9CiAgd3JpdHRlbiA8LSB3cml0ZV94bHN4KGRhdGEgPSBzYW1wbGVfZGF0YSwgc2hlZXQgPSBzYW1wbGVfbmFtZSwgd2IgPSB3YikKfQpzYXZlZCA8LSBvcGVueGxzeDo6c2F2ZVdvcmtib29rKHdyaXR0ZW5bWyJ3b3JrYm9vayJdXSwgZmlsZSA9ICJleGNlbC9wYTE0X211dGF0aW9ucy54bHN4IikKCnBha19tdXRhdGlvbnMgPC0gcERhdGEocGFrX2V4cHQpW1sidmFyaWFudHNieWdlbmVmaWxlcGFlcnVnaW5vc2FwYWsiXV0KbmFtZXMocGFrX211dGF0aW9ucykgPC0gcm93bmFtZXMocERhdGEocGFrX2V4cHQpKQpzdGFydCA8LSBpbml0X3hsc3goZXhjZWwgPSAiZXhjZWwvcGFrX211dGF0aW9ucy54bHN4IikKd2IgPC0gc3RhcnRbWyJ3YiJdXQpmb3IgKHMgaW4gc2VxX2xlbihsZW5ndGgocGFrX211dGF0aW9ucykpKSB7CiAgc2FtcGxlX25hbWUgPC0gbmFtZXMocGFrX211dGF0aW9ucylbW3NdXQogIGlmIChwYWtfbXV0YXRpb25zW1tzXV0gPT0gIiIpIHsKICAgIG5leHQKICB9CiAgc2FtcGxlX2RhdGEgPC0gcmVhZHI6OnJlYWRfdHN2KHBha19tdXRhdGlvbnNbW3NdXSkKICBpZiAobnJvdyhzYW1wbGVfZGF0YSkgPT0gMCkgewogICAgbmV4dAogIH0KICB3cml0dGVuIDwtIHdyaXRlX3hsc3goZGF0YSA9IHNhbXBsZV9kYXRhLCBzaGVldCA9IHNhbXBsZV9uYW1lLCB3YiA9IHdiKQp9CnNhdmVkIDwtIG9wZW54bHN4OjpzYXZlV29ya2Jvb2sod3JpdHRlbltbIndvcmtib29rIl1dLCBmaWxlID0gImV4Y2VsL3Bha19tdXRhdGlvbnMueGxzeCIpCmBgYAoKCmBgYHtyIHNhdmVtZX0KcGFuZGVyOjpwYW5kZXIoc2Vzc2lvbkluZm8oKSkKbWVzc2FnZSgiVGhpcyBpcyBocGdsdG9vbHMgY29tbWl0OiAiLCBnZXRfZ2l0X2NvbW1pdCgpKQp0aGlzX3NhdmUgPC0gcGFzdGUwKGdzdWIocGF0dGVybiA9ICJcXC5SbWQiLCByZXBsYWNlID0gIiIsIHggPSBybWRfZmlsZSksICItdiIsIHZlciwgIi5yZGEueHoiKQptZXNzYWdlKCJTYXZpbmcgdG8gIiwgdGhpc19zYXZlKQp0bXAgPC0gc20oc2F2ZW1lKGZpbGVuYW1lID0gdGhpc19zYXZlKSkKYGBgCg==