For miRNA target prediction, we expect most binding to occur in the 3’UTR regions. However, our reference genome Apulcra-genome.gff is not annotated with UTRs. We need to annotate those manually

First let’s take a look at what our reference Apulcra-genome.gff file looks like

grep -v '^#' ../data/Apulcra-genome.gff | cut -s -f 3 | sort | uniq -c | sort -rn
echo ""
head -10 ../data/Apulcra-genome.gff
##  209537 exon
##  201613 CDS
##   44371 gene
##   36447 mRNA
##    7924 tRNA
## 
## ##gff-version 3
## ntLink_0 funannotate gene    1105    7056    .   +   .   ID=FUN_000001;
## ntLink_0 funannotate mRNA    1105    7056    .   +   .   ID=FUN_000001-T1;Parent=FUN_000001;product=hypothetical protein;
## ntLink_0 funannotate exon    1105    1188    .   +   .   ID=FUN_000001-T1.exon1;Parent=FUN_000001-T1;
## ntLink_0 funannotate exon    1861    1941    .   +   .   ID=FUN_000001-T1.exon2;Parent=FUN_000001-T1;
## ntLink_0 funannotate exon    2762    2839    .   +   .   ID=FUN_000001-T1.exon3;Parent=FUN_000001-T1;
## ntLink_0 funannotate exon    5044    7056    .   +   .   ID=FUN_000001-T1.exon4;Parent=FUN_000001-T1;
## ntLink_0 funannotate CDS 1105    1188    .   +   0   ID=FUN_000001-T1.cds;Parent=FUN_000001-T1;
## ntLink_0 funannotate CDS 1861    1941    .   +   0   ID=FUN_000001-T1.cds;Parent=FUN_000001-T1;
## ntLink_0 funannotate CDS 2762    2839    .   +   0   ID=FUN_000001-T1.cds;Parent=FUN_000001-T1;

According to NCBI, the standard gff3 format should annotate regions like this:

gene1            ================================    ID=gene1
mRNA1            ================================    ID=mRNA1;Parent=gene1
exon1            ====                                Parent=mRNA1
five_prime_UTR   ==                                  Parent=mRNA1
CDS1               ==                                Parent=mRNA1
CDS2                     ==========                  Parent=mRNA1
CDS3                                  ====          Parent=mRNA1
three_prime_UTR                            ======    Parent=mRNA1

Note how a region annotated as mRNA should contain both the 5’UTR and 3’UTR, as well as all CDS regions.

Unfortunately, looking at our gff, it seems that Apulcra-genome.gff is formatted more like this:

gene1            ================================    ID=gene1
mRNA1            ================================    ID=mRNA1;Parent=gene1
exon1            ======                              Parent=mRNA1
CDS1             ======                              Parent=mRNA1
exon2                          ==================    Parent=mRNA1
CDS2                           ==================    Parent=mRNA1

Where an annotated mRNA contains only the CDS regions and internal UTR regions. That means there is no straightforward way to identify the actual 5’ and 3’ UTR regions from the gff.

Instead, we can just extract a best-guess region from immediately before and immediately after the annotated mRNA. Jill Ashey validated during our deep-dive work that a 1000bp region captures the 3’UTR well (see post and code here). This is also convenient because RNAhybrid accepts sequences of maximum 1000bp in length.

So we want to 1) isolate mRNA regions in Apulcra-genome.gff 2) determine sense of the annotated region (to inform which end is 5’ and which is 3’) 3) make new lines in gff that covers the 1000bp immediately preceding/following the mRNA, annotated appropriately as 5’/3’ UTR 4) ensure none of the newly annotated UTR regions overlap with an existing mRNA region – remove overlapping region from UTR annotation if necessary

Note: while the majority of miRNA functional binding occurs in the 3’UTR, it is possible for functional binding to occur in other regions, including the 5’UTR and CDS. I want to run target prediction for all regions, at least at first, so I want to annotate both the 3’ and 5’ UTR regions of our genome.

We’ll use a modified version of Jill’s code for this


# extract the mRNAs
grep $'\tmRNA\t' ../data/Apulcra-genome.gff > ../output/15-Apul-annotate-UTRs/Apulcra-genome-mRNA_only.gff

# Let's also isolate the CDS while we're at it
grep $'\tCDS\t' ../data/Apulcra-genome.gff > ../output/15-Apul-annotate-UTRs/Apulcra-genome-CDS_only.gff


# check
wc -l ../output/15-Apul-annotate-UTRs/Apulcra-genome-mRNA_only.gff
echo ""
head -5 ../output/15-Apul-annotate-UTRs/Apulcra-genome-mRNA_only.gff
## 36447 ../output/15-Apul-annotate-UTRs/Apulcra-genome-mRNA_only.gff
## 
## ntLink_0 funannotate mRNA    1105    7056    .   +   .   ID=FUN_000001-T1;Parent=FUN_000001;product=hypothetical protein;
## ntLink_0 funannotate mRNA    10215   15286   .   +   .   ID=FUN_000002-T1;Parent=FUN_000002;product=hypothetical protein;
## ntLink_0 funannotate mRNA    32057   33275   .   +   .   ID=FUN_000003-T1;Parent=FUN_000003;product=hypothetical protein;
## ntLink_0 funannotate mRNA    34824   42794   .   +   .   ID=FUN_000004-T1;Parent=FUN_000004;product=hypothetical protein;
## ntLink_0 funannotate mRNA    45953   51024   .   +   .   ID=FUN_000005-T1;Parent=FUN_000005;product=hypothetical protein;
cd ../output/15-Apul-annotate-UTRs

# Extract scaffold lengths
cat is ../../data/Apulchra-genome.fa | awk '$0 ~ ">" {if (NR > 1) {print c;} c=0;printf substr($0,2,100) "\t"; } $0 !~ ">" {c+=length($0);} END { print c; }' > Apul.Chromosome_lengths.txt

# Extract scaffold names
awk -F" " '{print $1}' Apul.Chromosome_lengths.txt > Apul.Chromosome_names.txt

# Check
wc -l Apul.Chromosome_lengths.txt 
echo ""
head -3 Apul.Chromosome_lengths.txt 
echo ""
head -3 Apul.Chromosome_names.txt 
## cat: is: No such file or directory
## 174 Apul.Chromosome_lengths.txt
## 
## ntLink_7 182921
## ntLink_8 37517065
## ntLink_0 96579
## 
## ntLink_7
## ntLink_8
## ntLink_0

The following code will sort the mRNA gff, extract 1kb down the 3’ end of mRNA, subtract portions of the 1kb flank (representing the 3’UTR) from any overlapping mRNA, and make fasta file of the 3’UTRs. It will do the same for 5’UTRs.

cd ../output/15-Apul-annotate-UTRs
export PATH="/home/shared/bedtools2/bin:$PATH"

echo "Sorting gffs by chromosome" $(date)

sortBed -faidx Apul.Chromosome_names.txt -i Apulcra-genome-mRNA_only.gff > Apul_GFFannotation.mRNA_sorted.gff

echo "Sorting complete!" $(date)

echo "Extracting 1kb 3' UTRs" $(date)

bedtools flank -i Apul_GFFannotation.mRNA_sorted.gff -g Apul.Chromosome_lengths.txt -l 0 -r 1000 -s | \
awk '{gsub("mRNA","3prime_UTR",$3); print $0 }' | \
awk '{if($5-$4 > 3)print $1"\t"$2"\t"$3"\t"$4"\t"$5"\t"$6"\t"$7"\t"$8"\t"$9}' | \
tr ' ' '\t' > Apul.GFFannotation.3UTR_1kb.gff

echo "Subtract portions of UTRs that overlap nearby genes" $(date)

bedtools subtract -a Apul.GFFannotation.3UTR_1kb.gff -b Apul_GFFannotation.mRNA_sorted.gff > Apul.GFFannotation.3UTR_1kb_corrected.gff 
echo "3' UTRs identified!" $(date)

echo "Extracting 3' UTR sequences" $(date)

bedtools getfasta -fi ../../data/Apulchra-genome.fa -bed Apul.GFFannotation.3UTR_1kb_corrected.gff -fo Apul_3UTR_1kb.fasta -fullHeader


echo "Extracting 1kb 5' UTRs" $(date)

bedtools flank -i Apul_GFFannotation.mRNA_sorted.gff -g Apul.Chromosome_lengths.txt -l 1000 -r 0 -s | \
awk '{gsub("mRNA","five_prime_UTR",$3); print $0 }' | \
awk '{if($5-$4 > 3)print $1"\t"$2"\t"$3"\t"$4"\t"$5"\t"$6"\t"$7"\t"$8"\t"$9}' | \
tr ' ' '\t' > Apul.GFFannotation.5UTR_1kb.gff

echo "Subtract portions of 5' UTRs that overlap nearby genes" $(date)

bedtools subtract -a Apul.GFFannotation.5UTR_1kb.gff -b Apul_GFFannotation.mRNA_sorted.gff > Apul.GFFannotation.5UTR_1kb_corrected.gff

echo "5' UTRs identified!" $(date)

echo "Extracting 5' UTR sequences" $(date)

bedtools getfasta -fi ../../data/Apulchra-genome.fa -bed Apul.GFFannotation.5UTR_1kb_corrected.gff -fo Apul_5UTR_1kb.fasta -fullHeader

echo "Sequence extraction complete!" $(date)
## Sorting gffs by chromosome Mon Dec 16 09:20:22 PST 2024
## Sorting complete! Mon Dec 16 09:20:22 PST 2024
## Extracting 1kb 3' UTRs Mon Dec 16 09:20:22 PST 2024
## Subtract portions of UTRs that overlap nearby genes Mon Dec 16 09:20:22 PST 2024
## 3' UTRs identified! Mon Dec 16 09:20:22 PST 2024
## Extracting 3' UTR sequences Mon Dec 16 09:20:22 PST 2024
## Extracting 1kb 5' UTRs Mon Dec 16 09:20:23 PST 2024
## Subtract portions of 5' UTRs that overlap nearby genes Mon Dec 16 09:20:23 PST 2024
## 5' UTRs identified! Mon Dec 16 09:20:23 PST 2024
## Extracting 5' UTR sequences Mon Dec 16 09:20:23 PST 2024
## Sequence extraction complete! Mon Dec 16 09:20:24 PST 2024

Check We expect, for an mRNA in + sense (forward strand), the 3’UTR region to be the 1000bp immediately following the mRNA, and the 5’UTR to be the 1000bp immediately before the mRNA (For a - sense mRNA, we would expect the opposite, with 3’ preceding the mRNA and 5’ following it.)

cd ../output/15-Apul-annotate-UTRs

head -1 Apul_GFFannotation.mRNA_sorted.gff
head -1 Apul.GFFannotation.3UTR_1kb_corrected.gff
head -1 Apul.GFFannotation.5UTR_1kb_corrected.gff
echo ""
head -2 Apul_GFFannotation.mRNA_sorted.gff | tail -1
head -2 Apul.GFFannotation.3UTR_1kb_corrected.gff | tail -1
head -2 Apul.GFFannotation.5UTR_1kb_corrected.gff | tail -1
echo ""
head -3 Apul_GFFannotation.mRNA_sorted.gff | tail -1
head -3 Apul.GFFannotation.3UTR_1kb_corrected.gff | tail -1
head -3 Apul.GFFannotation.5UTR_1kb_corrected.gff | tail -1
## ntLink_7 funannotate mRNA    79  4679    .   +   .   ID=FUN_002303-T1;Parent=FUN_002303;product=hypothetical protein;
## ntLink_7 funannotate 3prime_UTR  4680    5679    .   +   .   ID=FUN_002303-T1;Parent=FUN_002303;product=hypothetical
## ntLink_7 funannotate five_prime_UTR  1   78  .   +   .   ID=FUN_002303-T1;Parent=FUN_002303;product=hypothetical
## 
## ntLink_7 funannotate mRNA    12385   16904   .   -   .   ID=FUN_002304-T1;Parent=FUN_002304;product=hypothetical protein;
## ntLink_7 funannotate 3prime_UTR  11385   12384   .   -   .   ID=FUN_002304-T1;Parent=FUN_002304;product=hypothetical
## ntLink_7 funannotate five_prime_UTR  16905   17904   .   -   .   ID=FUN_002304-T1;Parent=FUN_002304;product=hypothetical
## 
## ntLink_7 funannotate mRNA    18480   24187   .   +   .   ID=FUN_002305-T1;Parent=FUN_002305;product=hypothetical protein;
## ntLink_7 funannotate 3prime_UTR  24188   25187   .   +   .   ID=FUN_002305-T1;Parent=FUN_002305;product=hypothetical
## ntLink_7 funannotate five_prime_UTR  17480   18479   .   +   .   ID=FUN_002305-T1;Parent=FUN_002305;product=hypothetical

We’re good!

Finally, we may want to use the simpler mRNA-associated FUN ids to denote 3UTR/5UTR regions, rather than the genomic location. Let’s create helper files for making those associations


FUNid_helper() {
    local input_gff="$1"
    local output_file="$2"

    # Check if the input file exists
    if [[ ! -f "$input_gff" ]]; then
        echo "Error: File $input_gff not found!"
        return 1
    fi

    # Process the GFF file
    awk 'BEGIN {OFS="\t"} {
        # Combine location
        location = $1 ":" $4-1 "-" $5;

        # Extract type
        type = $3;

        # Extract and format the attributes (last column)
        attributes = $9;
        gsub(";", "\t", attributes); # Replace semicolons with tabs

        # Print the desired output
        print location, type, attributes;
    }' "$input_gff" > "$output_file"

    echo "Processed $input_gff -> $output_file"
}

FUNid_helper "../output/15-Apul-annotate-UTRs/Apulcra-genome-mRNA_only.gff" "../output/15-Apul-annotate-UTRs/Apul-mRNA-FUNids.txt"
FUNid_helper "../output/15-Apul-annotate-UTRs/Apul.GFFannotation.3UTR_1kb_corrected.gff" "../output/15-Apul-annotate-UTRs/Apul-3UTR-FUNids.txt"
FUNid_helper "../output/15-Apul-annotate-UTRs/Apul.GFFannotation.5UTR_1kb_corrected.gff" "../output/15-Apul-annotate-UTRs/Apul-5UTR-FUNids.txt"
## Processed ../output/15-Apul-annotate-UTRs/Apulcra-genome-mRNA_only.gff -> ../output/15-Apul-annotate-UTRs/Apul-mRNA-FUNids.txt
## Processed ../output/15-Apul-annotate-UTRs/Apul.GFFannotation.3UTR_1kb_corrected.gff -> ../output/15-Apul-annotate-UTRs/Apul-3UTR-FUNids.txt
## Processed ../output/15-Apul-annotate-UTRs/Apul.GFFannotation.5UTR_1kb_corrected.gff -> ../output/15-Apul-annotate-UTRs/Apul-5UTR-FUNids.txt
LS0tCnRpdGxlOiAiMTEtQXB1bC1hbm5vdGF0ZS1VVFJzIgphdXRob3I6ICJLYXRobGVlbiBEdXJraW4iCmRhdGU6ICIyMDI0LTExLTIwIgphbHdheXNfYWxsb3dfaHRtbDogdHJ1ZQpvdXRwdXQ6IAogIGJvb2tkb3duOjpodG1sX2RvY3VtZW50MjoKICAgIHRoZW1lOiBjb3NtbwogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQogICAgY29kZV9mb2xkaW5nOiBzaG93CiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgZ2l0aHViX2RvY3VtZW50OgogICAgdG9jOiB0cnVlCiAgICB0b2NfZGVwdGg6IDMKICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQogICAgaHRtbF9wcmV2aWV3OiB0cnVlIAplZGl0b3Jfb3B0aW9uczogCiAgbWFya2Rvd246IAogICAgd3JhcDogNzIKLS0tCgpGb3IgbWlSTkEgdGFyZ2V0IHByZWRpY3Rpb24sIHdlIGV4cGVjdCBtb3N0IGJpbmRpbmcgdG8gb2NjdXIgaW4gdGhlIDMnVVRSIHJlZ2lvbnMuIEhvd2V2ZXIsIG91ciByZWZlcmVuY2UgZ2Vub21lIGBBcHVsY3JhLWdlbm9tZS5nZmZgIGlzIG5vdCBhbm5vdGF0ZWQgd2l0aCBVVFJzLiBXZSBuZWVkIHRvIGFubm90YXRlIHRob3NlIG1hbnVhbGx5CgpGaXJzdCBsZXQncyB0YWtlIGEgbG9vayBhdCB3aGF0IG91ciByZWZlcmVuY2UgYEFwdWxjcmEtZ2Vub21lLmdmZmAgZmlsZSBsb29rcyBsaWtlCgpgYGB7ciwgZW5naW5lPSdiYXNoJ30KZ3JlcCAtdiAnXiMnIC4uL2RhdGEvQXB1bGNyYS1nZW5vbWUuZ2ZmIHwgY3V0IC1zIC1mIDMgfCBzb3J0IHwgdW5pcSAtYyB8IHNvcnQgLXJuCmVjaG8gIiIKaGVhZCAtMTAgLi4vZGF0YS9BcHVsY3JhLWdlbm9tZS5nZmYKYGBgCgpBY2NvcmRpbmcgdG8gW05DQkldKGh0dHBzOi8vd3d3Lm5jYmkubmxtLm5paC5nb3YvZ2VuYmFuay9nZW5vbWVzX2dmZi8pLCB0aGUgc3RhbmRhcmQgZ2ZmMyBmb3JtYXQgc2hvdWxkIGFubm90YXRlIHJlZ2lvbnMgbGlrZSB0aGlzOgoKYGBgICAgICAgICAgCmdlbmUxICAgICAgICAgICAgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0gICAgSUQ9Z2VuZTEKbVJOQTEgICAgICAgICAgICA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSAgICBJRD1tUk5BMTtQYXJlbnQ9Z2VuZTEKZXhvbjEgICAgICAgICAgICA9PT09ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBQYXJlbnQ9bVJOQTEKZml2ZV9wcmltZV9VVFIgICA9PSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBQYXJlbnQ9bVJOQTEKQ0RTMSAgICAgICAgICAgICAgID09ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBQYXJlbnQ9bVJOQTEKQ0RTMiAgICAgICAgICAgICAgICAgICAgID09PT09PT09PT0gICAgICAgICAgICAgICAgICBQYXJlbnQ9bVJOQTEKQ0RTMyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA9PT09ICAgICAgICAgIFBhcmVudD1tUk5BMQp0aHJlZV9wcmltZV9VVFIgICAgICAgICAgICAgICAgICAgICAgICAgICAgPT09PT09ICAgIFBhcmVudD1tUk5BMQpgYGAKTm90ZSBob3cgYSByZWdpb24gYW5ub3RhdGVkIGFzIGBtUk5BYCBzaG91bGQgY29udGFpbiBib3RoIHRoZSA1J1VUUiBhbmQgMydVVFIsIGFzIHdlbGwgYXMgYWxsIENEUyByZWdpb25zLgoKVW5mb3J0dW5hdGVseSwgbG9va2luZyBhdCBvdXIgZ2ZmLCBpdCBzZWVtcyB0aGF0IGBBcHVsY3JhLWdlbm9tZS5nZmZgIGlzIGZvcm1hdHRlZCBtb3JlIGxpa2UgdGhpczoKCmBgYApnZW5lMSAgICAgICAgICAgID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09ICAgIElEPWdlbmUxCm1STkExICAgICAgICAgICAgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0gICAgSUQ9bVJOQTE7UGFyZW50PWdlbmUxCmV4b24xICAgICAgICAgICAgPT09PT09ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUGFyZW50PW1STkExCkNEUzEgICAgICAgICAgICAgPT09PT09ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUGFyZW50PW1STkExCmV4b24yICAgICAgICAgICAgICAgICAgICAgICAgICA9PT09PT09PT09PT09PT09PT0gICAgUGFyZW50PW1STkExCkNEUzIgICAgICAgICAgICAgICAgICAgICAgICAgICA9PT09PT09PT09PT09PT09PT0gICAgUGFyZW50PW1STkExCmBgYApXaGVyZSBhbiBhbm5vdGF0ZWQgbVJOQSBjb250YWlucyBvbmx5IHRoZSBDRFMgcmVnaW9ucyBhbmQgaW50ZXJuYWwgVVRSIHJlZ2lvbnMuIFRoYXQgbWVhbnMgdGhlcmUgaXMgbm8gc3RyYWlnaHRmb3J3YXJkIHdheSB0byBpZGVudGlmeSB0aGUgYWN0dWFsIDUnIGFuZCAzJyBVVFIgcmVnaW9ucyBmcm9tIHRoZSBnZmYuIAoKSW5zdGVhZCwgd2UgY2FuIGp1c3QgZXh0cmFjdCBhIGJlc3QtZ3Vlc3MgcmVnaW9uIGZyb20gaW1tZWRpYXRlbHkgYmVmb3JlIGFuZCBpbW1lZGlhdGVseSBhZnRlciB0aGUgYW5ub3RhdGVkIG1STkEuIEppbGwgQXNoZXkgdmFsaWRhdGVkIGR1cmluZyBvdXIgYGRlZXAtZGl2ZWAgd29yayB0aGF0IGEgMTAwMGJwIHJlZ2lvbiBjYXB0dXJlcyB0aGUgMydVVFIgd2VsbCAoc2VlIHBvc3QgYW5kIGNvZGUgW2hlcmVdKGh0dHBzOi8vZ2l0aHViLmNvbS9KaWxsQXNoZXkvSmlsbEFzaGV5X1B1dG5hbV9MYWJfTm90ZWJvb2svYmxvYi9tYXN0ZXIvX3Bvc3RzLzIwMjQtMDYtMTUtZTUtZGVlcGRpdmUtbWlSTkEtVGFyZ2V0UHJlZGljdGlvbi5tZCkpLiBUaGlzIGlzIGFsc28gY29udmVuaWVudCBiZWNhdXNlIFJOQWh5YnJpZCBhY2NlcHRzIHNlcXVlbmNlcyBvZiBtYXhpbXVtIDEwMDBicCBpbiBsZW5ndGguIAoKU28gd2Ugd2FudCB0byAKMSkgaXNvbGF0ZSBtUk5BIHJlZ2lvbnMgaW4gYEFwdWxjcmEtZ2Vub21lLmdmZmAKMikgZGV0ZXJtaW5lIHNlbnNlIG9mIHRoZSBhbm5vdGF0ZWQgcmVnaW9uICh0byBpbmZvcm0gd2hpY2ggZW5kIGlzIDUnIGFuZCB3aGljaCBpcyAzJykKMykgbWFrZSBuZXcgbGluZXMgaW4gZ2ZmIHRoYXQgY292ZXJzIHRoZSAxMDAwYnAgaW1tZWRpYXRlbHkgcHJlY2VkaW5nL2ZvbGxvd2luZyB0aGUgbVJOQSwgYW5ub3RhdGVkIGFwcHJvcHJpYXRlbHkgYXMgNScvMycgVVRSCjQpIGVuc3VyZSBub25lIG9mIHRoZSBuZXdseSBhbm5vdGF0ZWQgVVRSIHJlZ2lvbnMgb3ZlcmxhcCB3aXRoIGFuIGV4aXN0aW5nIG1STkEgcmVnaW9uIC0tIHJlbW92ZSBvdmVybGFwcGluZyByZWdpb24gZnJvbSBVVFIgYW5ub3RhdGlvbiBpZiBuZWNlc3NhcnkKCk5vdGU6IHdoaWxlIHRoZSBtYWpvcml0eSBvZiBtaVJOQSBmdW5jdGlvbmFsIGJpbmRpbmcgb2NjdXJzIGluIHRoZSAzJ1VUUiwgaXQgaXMgcG9zc2libGUgZm9yIGZ1bmN0aW9uYWwgYmluZGluZyB0byBvY2N1ciBpbiBvdGhlciByZWdpb25zLCBpbmNsdWRpbmcgdGhlIDUnVVRSIGFuZCBDRFMuIEkgd2FudCB0byBydW4gdGFyZ2V0IHByZWRpY3Rpb24gZm9yIGFsbCByZWdpb25zLCBhdCBsZWFzdCBhdCBmaXJzdCwgc28gSSB3YW50IHRvIGFubm90YXRlIGJvdGggdGhlIDMnIGFuZCA1JyBVVFIgcmVnaW9ucyBvZiBvdXIgZ2Vub21lLgoKV2UnbGwgdXNlIGEgbW9kaWZpZWQgdmVyc2lvbiBvZiBKaWxsJ3MgY29kZSBmb3IgdGhpcwoKYGBge3IsIGVuZ2luZT0nYmFzaCd9CgojIGV4dHJhY3QgdGhlIG1STkFzCmdyZXAgJCdcdG1STkFcdCcgLi4vZGF0YS9BcHVsY3JhLWdlbm9tZS5nZmYgPiAuLi9vdXRwdXQvMTUtQXB1bC1hbm5vdGF0ZS1VVFJzL0FwdWxjcmEtZ2Vub21lLW1STkFfb25seS5nZmYKCiMgTGV0J3MgYWxzbyBpc29sYXRlIHRoZSBDRFMgd2hpbGUgd2UncmUgYXQgaXQKZ3JlcCAkJ1x0Q0RTXHQnIC4uL2RhdGEvQXB1bGNyYS1nZW5vbWUuZ2ZmID4gLi4vb3V0cHV0LzE1LUFwdWwtYW5ub3RhdGUtVVRScy9BcHVsY3JhLWdlbm9tZS1DRFNfb25seS5nZmYKCgojIGNoZWNrCndjIC1sIC4uL291dHB1dC8xNS1BcHVsLWFubm90YXRlLVVUUnMvQXB1bGNyYS1nZW5vbWUtbVJOQV9vbmx5LmdmZgplY2hvICIiCmhlYWQgLTUgLi4vb3V0cHV0LzE1LUFwdWwtYW5ub3RhdGUtVVRScy9BcHVsY3JhLWdlbm9tZS1tUk5BX29ubHkuZ2ZmCmBgYAoKCmBgYHtyLCBlbmdpbmU9J2Jhc2gnfQpjZCAuLi9vdXRwdXQvMTUtQXB1bC1hbm5vdGF0ZS1VVFJzCgojIEV4dHJhY3Qgc2NhZmZvbGQgbGVuZ3RocwpjYXQgaXMgLi4vLi4vZGF0YS9BcHVsY2hyYS1nZW5vbWUuZmEgfCBhd2sgJyQwIH4gIj4iIHtpZiAoTlIgPiAxKSB7cHJpbnQgYzt9IGM9MDtwcmludGYgc3Vic3RyKCQwLDIsMTAwKSAiXHQiOyB9ICQwICF+ICI+IiB7Yys9bGVuZ3RoKCQwKTt9IEVORCB7IHByaW50IGM7IH0nID4gQXB1bC5DaHJvbW9zb21lX2xlbmd0aHMudHh0CgojIEV4dHJhY3Qgc2NhZmZvbGQgbmFtZXMKYXdrIC1GIiAiICd7cHJpbnQgJDF9JyBBcHVsLkNocm9tb3NvbWVfbGVuZ3Rocy50eHQgPiBBcHVsLkNocm9tb3NvbWVfbmFtZXMudHh0CgojIENoZWNrCndjIC1sIEFwdWwuQ2hyb21vc29tZV9sZW5ndGhzLnR4dCAKZWNobyAiIgpoZWFkIC0zIEFwdWwuQ2hyb21vc29tZV9sZW5ndGhzLnR4dCAKZWNobyAiIgpoZWFkIC0zIEFwdWwuQ2hyb21vc29tZV9uYW1lcy50eHQgCgpgYGAKClRoZSBmb2xsb3dpbmcgY29kZSB3aWxsIHNvcnQgdGhlIG1STkEgZ2ZmLCBleHRyYWN0IDFrYiBkb3duIHRoZSAzJyBlbmQgb2YgbVJOQSwgc3VidHJhY3QgcG9ydGlvbnMgb2YgdGhlIDFrYiBmbGFuayAocmVwcmVzZW50aW5nIHRoZSAzJ1VUUikgZnJvbSBhbnkgb3ZlcmxhcHBpbmcgbVJOQSwgYW5kIG1ha2UgZmFzdGEgZmlsZSBvZiB0aGUgMydVVFJzLiBJdCB3aWxsIGRvIHRoZSBzYW1lIGZvciA1J1VUUnMuCgpgYGB7ciwgZW5naW5lPSdiYXNoJ30KY2QgLi4vb3V0cHV0LzE1LUFwdWwtYW5ub3RhdGUtVVRScwpleHBvcnQgUEFUSD0iL2hvbWUvc2hhcmVkL2JlZHRvb2xzMi9iaW46JFBBVEgiCgplY2hvICJTb3J0aW5nIGdmZnMgYnkgY2hyb21vc29tZSIgJChkYXRlKQoKc29ydEJlZCAtZmFpZHggQXB1bC5DaHJvbW9zb21lX25hbWVzLnR4dCAtaSBBcHVsY3JhLWdlbm9tZS1tUk5BX29ubHkuZ2ZmID4gQXB1bF9HRkZhbm5vdGF0aW9uLm1STkFfc29ydGVkLmdmZgoKZWNobyAiU29ydGluZyBjb21wbGV0ZSEiICQoZGF0ZSkKCmVjaG8gIkV4dHJhY3RpbmcgMWtiIDMnIFVUUnMiICQoZGF0ZSkKCmJlZHRvb2xzIGZsYW5rIC1pIEFwdWxfR0ZGYW5ub3RhdGlvbi5tUk5BX3NvcnRlZC5nZmYgLWcgQXB1bC5DaHJvbW9zb21lX2xlbmd0aHMudHh0IC1sIDAgLXIgMTAwMCAtcyB8IFwKYXdrICd7Z3N1YigibVJOQSIsIjNwcmltZV9VVFIiLCQzKTsgcHJpbnQgJDAgfScgfCBcCmF3ayAne2lmKCQ1LSQ0ID4gMylwcmludCAkMSJcdCIkMiJcdCIkMyJcdCIkNCJcdCIkNSJcdCIkNiJcdCIkNyJcdCIkOCJcdCIkOX0nIHwgXAp0ciAnICcgJ1x0JyA+IEFwdWwuR0ZGYW5ub3RhdGlvbi4zVVRSXzFrYi5nZmYKCmVjaG8gIlN1YnRyYWN0IHBvcnRpb25zIG9mIFVUUnMgdGhhdCBvdmVybGFwIG5lYXJieSBnZW5lcyIgJChkYXRlKQoKYmVkdG9vbHMgc3VidHJhY3QgLWEgQXB1bC5HRkZhbm5vdGF0aW9uLjNVVFJfMWtiLmdmZiAtYiBBcHVsX0dGRmFubm90YXRpb24ubVJOQV9zb3J0ZWQuZ2ZmID4gQXB1bC5HRkZhbm5vdGF0aW9uLjNVVFJfMWtiX2NvcnJlY3RlZC5nZmYgCmVjaG8gIjMnIFVUUnMgaWRlbnRpZmllZCEiICQoZGF0ZSkKCmVjaG8gIkV4dHJhY3RpbmcgMycgVVRSIHNlcXVlbmNlcyIgJChkYXRlKQoKYmVkdG9vbHMgZ2V0ZmFzdGEgLWZpIC4uLy4uL2RhdGEvQXB1bGNocmEtZ2Vub21lLmZhIC1iZWQgQXB1bC5HRkZhbm5vdGF0aW9uLjNVVFJfMWtiX2NvcnJlY3RlZC5nZmYgLWZvIEFwdWxfM1VUUl8xa2IuZmFzdGEgLWZ1bGxIZWFkZXIKCgplY2hvICJFeHRyYWN0aW5nIDFrYiA1JyBVVFJzIiAkKGRhdGUpCgpiZWR0b29scyBmbGFuayAtaSBBcHVsX0dGRmFubm90YXRpb24ubVJOQV9zb3J0ZWQuZ2ZmIC1nIEFwdWwuQ2hyb21vc29tZV9sZW5ndGhzLnR4dCAtbCAxMDAwIC1yIDAgLXMgfCBcCmF3ayAne2dzdWIoIm1STkEiLCJmaXZlX3ByaW1lX1VUUiIsJDMpOyBwcmludCAkMCB9JyB8IFwKYXdrICd7aWYoJDUtJDQgPiAzKXByaW50ICQxIlx0IiQyIlx0IiQzIlx0IiQ0Ilx0IiQ1Ilx0IiQ2Ilx0IiQ3Ilx0IiQ4Ilx0IiQ5fScgfCBcCnRyICcgJyAnXHQnID4gQXB1bC5HRkZhbm5vdGF0aW9uLjVVVFJfMWtiLmdmZgoKZWNobyAiU3VidHJhY3QgcG9ydGlvbnMgb2YgNScgVVRScyB0aGF0IG92ZXJsYXAgbmVhcmJ5IGdlbmVzIiAkKGRhdGUpCgpiZWR0b29scyBzdWJ0cmFjdCAtYSBBcHVsLkdGRmFubm90YXRpb24uNVVUUl8xa2IuZ2ZmIC1iIEFwdWxfR0ZGYW5ub3RhdGlvbi5tUk5BX3NvcnRlZC5nZmYgPiBBcHVsLkdGRmFubm90YXRpb24uNVVUUl8xa2JfY29ycmVjdGVkLmdmZgoKZWNobyAiNScgVVRScyBpZGVudGlmaWVkISIgJChkYXRlKQoKZWNobyAiRXh0cmFjdGluZyA1JyBVVFIgc2VxdWVuY2VzIiAkKGRhdGUpCgpiZWR0b29scyBnZXRmYXN0YSAtZmkgLi4vLi4vZGF0YS9BcHVsY2hyYS1nZW5vbWUuZmEgLWJlZCBBcHVsLkdGRmFubm90YXRpb24uNVVUUl8xa2JfY29ycmVjdGVkLmdmZiAtZm8gQXB1bF81VVRSXzFrYi5mYXN0YSAtZnVsbEhlYWRlcgoKZWNobyAiU2VxdWVuY2UgZXh0cmFjdGlvbiBjb21wbGV0ZSEiICQoZGF0ZSkKYGBgCgpDaGVjayBXZSBleHBlY3QsIGZvciBhbiBtUk5BIGluICsgc2Vuc2UgKGZvcndhcmQgc3RyYW5kKSwgdGhlIDMnVVRSIHJlZ2lvbiB0byBiZSB0aGUgMTAwMGJwIGltbWVkaWF0ZWx5IGZvbGxvd2luZyB0aGUgbVJOQSwgYW5kIHRoZSA1J1VUUiB0byBiZSB0aGUgMTAwMGJwIGltbWVkaWF0ZWx5IGJlZm9yZSB0aGUgbVJOQQooRm9yIGEgLSBzZW5zZSBtUk5BLCB3ZSB3b3VsZCBleHBlY3QgdGhlIG9wcG9zaXRlLCB3aXRoIDMnIHByZWNlZGluZyB0aGUgbVJOQSBhbmQgNScgZm9sbG93aW5nIGl0LikKYGBge3IsIGVuZ2luZT0nYmFzaCd9CmNkIC4uL291dHB1dC8xNS1BcHVsLWFubm90YXRlLVVUUnMKCmhlYWQgLTEgQXB1bF9HRkZhbm5vdGF0aW9uLm1STkFfc29ydGVkLmdmZgpoZWFkIC0xIEFwdWwuR0ZGYW5ub3RhdGlvbi4zVVRSXzFrYl9jb3JyZWN0ZWQuZ2ZmCmhlYWQgLTEgQXB1bC5HRkZhbm5vdGF0aW9uLjVVVFJfMWtiX2NvcnJlY3RlZC5nZmYKZWNobyAiIgpoZWFkIC0yIEFwdWxfR0ZGYW5ub3RhdGlvbi5tUk5BX3NvcnRlZC5nZmYgfCB0YWlsIC0xCmhlYWQgLTIgQXB1bC5HRkZhbm5vdGF0aW9uLjNVVFJfMWtiX2NvcnJlY3RlZC5nZmYgfCB0YWlsIC0xCmhlYWQgLTIgQXB1bC5HRkZhbm5vdGF0aW9uLjVVVFJfMWtiX2NvcnJlY3RlZC5nZmYgfCB0YWlsIC0xCmVjaG8gIiIKaGVhZCAtMyBBcHVsX0dGRmFubm90YXRpb24ubVJOQV9zb3J0ZWQuZ2ZmIHwgdGFpbCAtMQpoZWFkIC0zIEFwdWwuR0ZGYW5ub3RhdGlvbi4zVVRSXzFrYl9jb3JyZWN0ZWQuZ2ZmIHwgdGFpbCAtMQpoZWFkIC0zIEFwdWwuR0ZGYW5ub3RhdGlvbi41VVRSXzFrYl9jb3JyZWN0ZWQuZ2ZmIHwgdGFpbCAtMQoKYGBgCgpXZSdyZSBnb29kIQoKRmluYWxseSwgd2UgbWF5IHdhbnQgdG8gdXNlIHRoZSBzaW1wbGVyIG1STkEtYXNzb2NpYXRlZCBGVU4gaWRzIHRvIGRlbm90ZSAzVVRSLzVVVFIgcmVnaW9ucywgcmF0aGVyIHRoYW4gdGhlIGdlbm9taWMgbG9jYXRpb24uIExldCdzIGNyZWF0ZSBoZWxwZXIgZmlsZXMgZm9yIG1ha2luZyB0aG9zZSBhc3NvY2lhdGlvbnMKCmBgYHtyLCBlbmdpbmU9J2Jhc2gnfQoKRlVOaWRfaGVscGVyKCkgewogICAgbG9jYWwgaW5wdXRfZ2ZmPSIkMSIKICAgIGxvY2FsIG91dHB1dF9maWxlPSIkMiIKCiAgICAjIENoZWNrIGlmIHRoZSBpbnB1dCBmaWxlIGV4aXN0cwogICAgaWYgW1sgISAtZiAiJGlucHV0X2dmZiIgXV07IHRoZW4KICAgICAgICBlY2hvICJFcnJvcjogRmlsZSAkaW5wdXRfZ2ZmIG5vdCBmb3VuZCEiCiAgICAgICAgcmV0dXJuIDEKICAgIGZpCgogICAgIyBQcm9jZXNzIHRoZSBHRkYgZmlsZQogICAgYXdrICdCRUdJTiB7T0ZTPSJcdCJ9IHsKICAgICAgICAjIENvbWJpbmUgbG9jYXRpb24KICAgICAgICBsb2NhdGlvbiA9ICQxICI6IiAkNC0xICItIiAkNTsKCiAgICAgICAgIyBFeHRyYWN0IHR5cGUKICAgICAgICB0eXBlID0gJDM7CgogICAgICAgICMgRXh0cmFjdCBhbmQgZm9ybWF0IHRoZSBhdHRyaWJ1dGVzIChsYXN0IGNvbHVtbikKICAgICAgICBhdHRyaWJ1dGVzID0gJDk7CiAgICAgICAgZ3N1YigiOyIsICJcdCIsIGF0dHJpYnV0ZXMpOyAjIFJlcGxhY2Ugc2VtaWNvbG9ucyB3aXRoIHRhYnMKCiAgICAgICAgIyBQcmludCB0aGUgZGVzaXJlZCBvdXRwdXQKICAgICAgICBwcmludCBsb2NhdGlvbiwgdHlwZSwgYXR0cmlidXRlczsKICAgIH0nICIkaW5wdXRfZ2ZmIiA+ICIkb3V0cHV0X2ZpbGUiCgogICAgZWNobyAiUHJvY2Vzc2VkICRpbnB1dF9nZmYgLT4gJG91dHB1dF9maWxlIgp9CgpGVU5pZF9oZWxwZXIgIi4uL291dHB1dC8xNS1BcHVsLWFubm90YXRlLVVUUnMvQXB1bGNyYS1nZW5vbWUtbVJOQV9vbmx5LmdmZiIgIi4uL291dHB1dC8xNS1BcHVsLWFubm90YXRlLVVUUnMvQXB1bC1tUk5BLUZVTmlkcy50eHQiCkZVTmlkX2hlbHBlciAiLi4vb3V0cHV0LzE1LUFwdWwtYW5ub3RhdGUtVVRScy9BcHVsLkdGRmFubm90YXRpb24uM1VUUl8xa2JfY29ycmVjdGVkLmdmZiIgIi4uL291dHB1dC8xNS1BcHVsLWFubm90YXRlLVVUUnMvQXB1bC0zVVRSLUZVTmlkcy50eHQiCkZVTmlkX2hlbHBlciAiLi4vb3V0cHV0LzE1LUFwdWwtYW5ub3RhdGUtVVRScy9BcHVsLkdGRmFubm90YXRpb24uNVVUUl8xa2JfY29ycmVjdGVkLmdmZiIgIi4uL291dHB1dC8xNS1BcHVsLWFubm90YXRlLVVUUnMvQXB1bC01VVRSLUZVTmlkcy50eHQiCgoKYGBgCgo=