1 Examples version: 20170905

I am trying out the examples from ‘Using R and Bioconductor for Proteomics Data Analysis.’ Most of the following is taken verbatim from that.

2 Setting up

Make sure I have the various required packages.

library("hpgltools")
tt <- sm(library("RforProteomics"))
tt <- sm(library("mzR"))
tt <- sm(library("msdata"))

3 Query 1 from Volker

What is the size distribution of all Mtb proteins containing PP and PPE? More specifically, how many of them are smaller than 30 kDa?

Of these proteins, how many have trypsin digestion sites?

3.1 Method

I have a database of the translated CDS sequences from MTb. Lets query it.

pattern <- "PE"
cds <- "mtb_cds.fasta"
mtb_cds <- Biostrings::readAAStringSet(filepath=cds)

pattern <- BiocGenerics::sapply(pattern, toString)
mtb_string <- BiocGenerics::sapply(mtb_cds, toString)
## Perform the search, this returns a list of hits by position and name of pattern.
mtb_pp <- AhoCorasickTrie::AhoCorasickSearch(keywords=pattern,
                                             text=mtb_string,
                                             alphabet="ascii")
## Melt this into a somewhat usable data frame.
pp_df <- reshape2::melt(data=mtb_pp, level=1)
## Drop the columns I don't want
pp_df <- pp_df[, c("L1", "value")]
## Drop the rows which have useless information
drop <- pp_df[["value"]] == "PP"
pp_df <- pp_df[!drop, ]
## Rename the columns to something useful
colnames(pp_df) <- c("ID", "position")
## Look at the first 20.
head(pp_df, n=20)
##        ID position
## 1  Rv0001       PE
## 2  Rv0001      333
## 3  Rv0002       PE
## 4  Rv0002      133
## 5  Rv0003       PE
## 6  Rv0003      119
## 7  Rv0003       PE
## 8  Rv0003      210
## 9  Rv0003       PE
## 10 Rv0003      221
## 11 Rv0005       PE
## 12 Rv0005      578
## 13 Rv0006       PE
## 14 Rv0006       42
## 15 Rv0006       PE
## 16 Rv0006      472
## 17 Rv0006       PE
## 18 Rv0006      585
## 19 Rv0006       PE
## 20 Rv0006      604
## Lets check that this is correct, Rv0007 has 43, 44, 61, 62, so that should be easy to check.
## Here are the first 80 AA of Rv0007        v  <- pos. 43     v <- pos. 61 v <- 74
## VTAPNEPGALSKGDGPNADGLVDRGGAHRAATGPGRIPDAGDPPPWQRAATRQSQAGHRQPPPVSHPEGRPTNPPAAADA
## Yay!

## Ok, so the actual question was, how many PP genes are there.
length(unique(pp_df[["ID"]]))
## [1] 2292
pp_hits <- unique(pp_df$ID)

3.2 Repeat for PPE

## What about PPE?
pattern <- "PPE"
pattern <- BiocGenerics::sapply(pattern, toString)
mtb_ppe <- AhoCorasickTrie::AhoCorasickSearch(keywords=pattern,
                                             text=mtb_string,
                                             alphabet="ascii")
ppe_df <- reshape2::melt(data=mtb_ppe, level=1)
ppe_df <- ppe_df[, c("L1", "value")]
drop <- ppe_df[["value"]] == "PPE"
ppe_df <- ppe_df[!drop, ]
colnames(ppe_df) <- c("ID", "position")
head(ppe_df, n=20)
##         ID position
## 2   Rv0001      332
## 4  Rv0014c      281
## 6  Rv0018c      390
## 8  Rv0020c      203
## 10 Rv0020c      341
## 12 Rv0043c      217
## 14 Rv0046c      348
## 16  Rv0050       46
## 18  Rv0050      630
## 20  Rv0073      225
## 22  Rv0096        4
## 24  Rv0111      452
## 26  Rv0148      206
## 28  Rv0185       47
## 30  Rv0186      531
## 32 Rv0198c      624
## 34 Rv0227c      417
## 36 Rv0256c       10
## 38 Rv0275c      224
## 40  Rv0280        8
length(unique(ppe_df[["ID"]]))
## [1] 280

3.3 Find overlap of trypsin products with these

tryp_hits <- cleaver::cleavageRanges(mtb_cds, enzym="trypsin")
tryp_df <- as.data.frame(tryp_hits)
tryp_names <- unique(tryp_df$group_name)

3.4 And the likely molecular weights

mtb_ppe <- mtb_cds[ppe_df$ID, ]
tryp_products <- cleaver::cleave(mtb_ppe, enzym="trypsin")
tryp_products_df <- as.data.frame(tryp_products)
tryp_products_df$mw <- Peptides::mw(tryp_products_df$value)
dim(tryp_products_df)
## [1] 11631     4
## What products are there from 10,000 Daltons to 30,000?
hist(tryp_products_df$mw, breaks=1000, xlim=c(10000, 30000), ylim=c(0, 15))

## Call these 'intermediate'
intermediate <- tryp_products_df
## Drop anything bigger than 30,000 Daltons
keepers <- intermediate$mw <= 30000
intermediate <- intermediate[keepers, ]
## Drop anything smaller than 10,000 Daltons
keepers <- intermediate$mw > 10000
intermediate <- intermediate[keepers, ]
unique(intermediate$group_name)
##  [1] "Rv0096"  "Rv0256c" "Rv0280"  "Rv0286"  "Rv0355c" "Rv0442c" "Rv0453"  "Rv0878c"
##  [9] "Rv0915c" "Rv1039c" "Rv1040c" "Rv1135c" "Rv1196"  "Rv1233c" "Rv1361c" "Rv1387" 
## [17] "Rv1548c" "Rv1753c" "Rv1789"  "Rv1808"  "Rv1917c" "Rv1918c" "Rv2264c" "Rv2352c"
## [25] "Rv2444c" "Rv2768c" "Rv2892c" "Rv3018c" "Rv3136"  "Rv3159c" "Rv3343c" "Rv3347c"
## [33] "Rv3350c" "Rv3478"  "Rv3494c" "Rv3533c" "Rv3558"  "Rv3873"

3.5 Look for overlap of high-specificity chymotrypsin cleavages

chtryp_hits <- cleaver::cleavageRanges(mtb_cds, enzym="chymotrypsin-high")
chtryp_df <- as.data.frame(chtryp_hits)
chtryp_names <- unique(chtryp_df$group_name)

3.6 The likely molecular weights for high-specificity chymotrypsin

In this case we look for C->[FYW] not before P.

mtb_ppe <- mtb_cds[ppe_df$ID, ]
tryp_products <- cleaver::cleave(mtb_ppe, enzym="chymotrypsin-high")
tryp_products_df <- as.data.frame(tryp_products)
tryp_products_df$mw <- Peptides::mw(tryp_products_df$value)
dim(tryp_products_df)
## [1] 10629     4
## What products are there from 10,000 Daltons to 30,000?
hist(tryp_products_df$mw, breaks=1000, xlim=c(10000, 30000), ylim=c(0, 15))

## Call these 'intermediate'
intermediate <- tryp_products_df
## Drop anything bigger than 30,000 Daltons
keepers <- intermediate$mw <= 30000
intermediate <- intermediate[keepers, ]
## Drop anything smaller than 10,000 Daltons
keepers <- intermediate$mw > 10000
intermediate <- intermediate[keepers, ]
unique(intermediate$group_name)
##  [1] "Rv0018c" "Rv0227c" "Rv0336"  "Rv0339c" "Rv0425c" "Rv0494"  "Rv0515"  "Rv0668" 
##  [9] "Rv0904c" "Rv1020"  "Rv1186c" "Rv1409"  "Rv1796"  "Rv1887"  "Rv1917c" "Rv2139" 
## [17] "Rv2207"  "Rv2264c" "Rv2352c" "Rv2444c" "Rv2578c" "Rv2847c" "Rv2921c" "Rv2931" 
## [25] "Rv2934"  "Rv2941"  "Rv3144c" "Rv3209"  "Rv3245c" "Rv3296"  "Rv3333c" "Rv3663c"
## [33] "Rv3721c" "Rv3723"  "Rv3763"  "Rv3823c" "Rv3835"  "Rv3873"  "Rv3886c" "Rv3903c"
## [41] "Rv3910"

3.7 Finally, the low-specificity chymotrypsin digestion products

cltryp_hits <- cleaver::cleavageRanges(mtb_cds, enzym="chymotrypsin-low")
cltryp_df <- as.data.frame(cltryp_hits)
cltryp_names <- unique(cltryp_df$group_name)

## All of the pp containing peptides have trypsin digestion products.
sum(pp_hits %in% tryp_names)
## [1] 2292

3.8 And Chymotrypsin-low size distribution

And here we look for C->[FYWML] not before P.

mtb_ppe <- mtb_cds[ppe_df$ID, ]
tryp_products <- cleaver::cleave(mtb_ppe, enzym="chymotrypsin-low")
tryp_products_df <- as.data.frame(tryp_products)
tryp_products_df$mw <- Peptides::mw(tryp_products_df$value)
dim(tryp_products_df)
## [1] 25847     4
## What products are there from 10,000 Daltons to 30,000?
hist(tryp_products_df$mw, breaks=1000, xlim=c(1000, 7000), ylim=c(0, 15))

## Call these 'intermediate'
intermediate <- tryp_products_df
## Drop anything bigger than 30,000 Daltons
keepers <- intermediate$mw <= 30000
intermediate <- intermediate[keepers, ]
## Drop anything smaller than 10,000 Daltons
keepers <- intermediate$mw > 10000
intermediate <- intermediate[keepers, ]
unique(intermediate$group_name)
## character(0)
LS0tCnRpdGxlOiAiVGVzdGluZyBvdXQgUHJvdGVvbWljcyBhbmFseXNlcy4iCmF1dGhvcjogImF0YiBhYmVsZXdAZ21haWwuY29tIgpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiCm91dHB1dDoKIGh0bWxfZG9jdW1lbnQ6CiAgY29kZV9kb3dubG9hZDogdHJ1ZQogIGNvZGVfZm9sZGluZzogc2hvdwogIGZpZ19jYXB0aW9uOiB0cnVlCiAgZmlnX2hlaWdodDogNwogIGZpZ193aWR0aDogNwogIGhpZ2hsaWdodDogZGVmYXVsdAogIGtlZXBfbWQ6IGZhbHNlCiAgbW9kZTogc2VsZmNvbnRhaW5lZAogIG51bWJlcl9zZWN0aW9uczogdHJ1ZQogIHNlbGZfY29udGFpbmVkOiB0cnVlCiAgdGhlbWU6IHJlYWRhYmxlCiAgdG9jOiB0cnVlCiAgdG9jX2Zsb2F0OgogICAgY29sbGFwc2VkOiBmYWxzZQogICAgc21vb3RoX3Njcm9sbDogZmFsc2UKLS0tCgo8c3R5bGU+CiAgYm9keSAubWFpbi1jb250YWluZXIgewogICAgbWF4LXdpZHRoOiAxNjAwcHg7CiAgfQo8L3N0eWxlPgoKYGBge3Igb3B0aW9ucywgaW5jbHVkZT1GQUxTRX0KbGlicmFyeShocGdsdG9vbHMpCnR0IDwtIGRldnRvb2xzOjpsb2FkX2FsbCgifi9ocGdsdG9vbHMiKQprbml0cjo6b3B0c19rbml0JHNldChwcm9ncmVzcz1UUlVFLAogICAgICAgICAgICAgICAgICAgICB2ZXJib3NlPVRSVUUsCiAgICAgICAgICAgICAgICAgICAgIHdpZHRoPTkwLAogICAgICAgICAgICAgICAgICAgICBlY2hvPVRSVUUpCmtuaXRyOjpvcHRzX2NodW5rJHNldChlcnJvcj1UUlVFLAogICAgICAgICAgICAgICAgICAgICAgZmlnLndpZHRoPTgsCiAgICAgICAgICAgICAgICAgICAgICBmaWcuaGVpZ2h0PTgsCiAgICAgICAgICAgICAgICAgICAgICBkcGk9OTYpCm9sZF9vcHRpb25zIDwtIG9wdGlvbnMoZGlnaXRzPTQsCiAgICAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycz1GQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICBrbml0ci5kdXBsaWNhdGUubGFiZWw9ImFsbG93IikKZ2dwbG90Mjo6dGhlbWVfc2V0KGdncGxvdDI6OnRoZW1lX2J3KGJhc2Vfc2l6ZT0xMCkpCnNldC5zZWVkKDEpCnZlciA8LSAiMjAxNzA5MDUiCnByZXZpb3VzX2ZpbGUgPC0gImluZGV4LlJtZCIKCnRtcCA8LSB0cnkoc20obG9hZG1lKGZpbGVuYW1lPXBhc3RlMChnc3ViKHBhdHRlcm49IlxcLlJtZCIsIHJlcGxhY2U9IiIsIHg9cHJldmlvdXNfZmlsZSksICItdiIsIHZlciwgIi5yZGEueHoiKSkpKQoKcm1kX2ZpbGUgPC0gIm51bWJlcl9wZV90cnlwc2luLlJtZCIKYGBgCgpgYGB7ciByZW5kZXJpbmcsIGluY2x1ZGU9RkFMU0UsIGV2YWw9RkFMU0V9CnJtYXJrZG93bjo6cmVuZGVyKHJtZF9maWxlKQoKcm1hcmtkb3duOjpyZW5kZXIocm1kX2ZpbGUsIG91dHB1dF9mb3JtYXQ9InBkZl9kb2N1bWVudCIpCmBgYAoKIyBFeGFtcGxlcyB2ZXJzaW9uOiBgciB2ZXJgCgpJIGFtIHRyeWluZyBvdXQgdGhlIGV4YW1wbGVzIGZyb20gJ1VzaW5nIFIgYW5kIEJpb2NvbmR1Y3RvciBmb3IgUHJvdGVvbWljcyBEYXRhIEFuYWx5c2lzLicKTW9zdCBvZiB0aGUgZm9sbG93aW5nIGlzIHRha2VuIHZlcmJhdGltIGZyb20gdGhhdC4KCiMgU2V0dGluZyB1cAoKTWFrZSBzdXJlIEkgaGF2ZSB0aGUgdmFyaW91cyByZXF1aXJlZCBwYWNrYWdlcy4KCmBgYHtyIHNldHVwfQpsaWJyYXJ5KCJocGdsdG9vbHMiKQp0dCA8LSBzbShsaWJyYXJ5KCJSZm9yUHJvdGVvbWljcyIpKQp0dCA8LSBzbShsaWJyYXJ5KCJtelIiKSkKdHQgPC0gc20obGlicmFyeSgibXNkYXRhIikpCmBgYAoKIyBRdWVyeSAxIGZyb20gVm9sa2VyCgpXaGF0IGlzIHRoZSBzaXplIGRpc3RyaWJ1dGlvbiBvZiBhbGwgTXRiIHByb3RlaW5zIGNvbnRhaW5pbmcgUFAgYW5kIFBQRT8gIE1vcmUKc3BlY2lmaWNhbGx5LCBob3cgbWFueSBvZiB0aGVtIGFyZSBzbWFsbGVyIHRoYW4gMzAga0RhPwoKT2YgdGhlc2UgcHJvdGVpbnMsIGhvdyBtYW55IGhhdmUgdHJ5cHNpbiBkaWdlc3Rpb24gc2l0ZXM/CgojIyBNZXRob2QKCkkgaGF2ZSBhIGRhdGFiYXNlIG9mIHRoZSB0cmFuc2xhdGVkIENEUyBzZXF1ZW5jZXMgZnJvbSBNVGIuICBMZXRzIHF1ZXJ5IGl0LgoKYGBge3IgcHBlfQpwYXR0ZXJuIDwtICJQRSIKY2RzIDwtICJtdGJfY2RzLmZhc3RhIgptdGJfY2RzIDwtIEJpb3N0cmluZ3M6OnJlYWRBQVN0cmluZ1NldChmaWxlcGF0aD1jZHMpCgpwYXR0ZXJuIDwtIEJpb2NHZW5lcmljczo6c2FwcGx5KHBhdHRlcm4sIHRvU3RyaW5nKQptdGJfc3RyaW5nIDwtIEJpb2NHZW5lcmljczo6c2FwcGx5KG10Yl9jZHMsIHRvU3RyaW5nKQojIyBQZXJmb3JtIHRoZSBzZWFyY2gsIHRoaXMgcmV0dXJucyBhIGxpc3Qgb2YgaGl0cyBieSBwb3NpdGlvbiBhbmQgbmFtZSBvZiBwYXR0ZXJuLgptdGJfcHAgPC0gQWhvQ29yYXNpY2tUcmllOjpBaG9Db3Jhc2lja1NlYXJjaChrZXl3b3Jkcz1wYXR0ZXJuLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0ZXh0PW10Yl9zdHJpbmcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFscGhhYmV0PSJhc2NpaSIpCiMjIE1lbHQgdGhpcyBpbnRvIGEgc29tZXdoYXQgdXNhYmxlIGRhdGEgZnJhbWUuCnBwX2RmIDwtIHJlc2hhcGUyOjptZWx0KGRhdGE9bXRiX3BwLCBsZXZlbD0xKQojIyBEcm9wIHRoZSBjb2x1bW5zIEkgZG9uJ3Qgd2FudApwcF9kZiA8LSBwcF9kZlssIGMoIkwxIiwgInZhbHVlIildCiMjIERyb3AgdGhlIHJvd3Mgd2hpY2ggaGF2ZSB1c2VsZXNzIGluZm9ybWF0aW9uCmRyb3AgPC0gcHBfZGZbWyJ2YWx1ZSJdXSA9PSAiUFAiCnBwX2RmIDwtIHBwX2RmWyFkcm9wLCBdCiMjIFJlbmFtZSB0aGUgY29sdW1ucyB0byBzb21ldGhpbmcgdXNlZnVsCmNvbG5hbWVzKHBwX2RmKSA8LSBjKCJJRCIsICJwb3NpdGlvbiIpCiMjIExvb2sgYXQgdGhlIGZpcnN0IDIwLgpoZWFkKHBwX2RmLCBuPTIwKQoKIyMgTGV0cyBjaGVjayB0aGF0IHRoaXMgaXMgY29ycmVjdCwgUnYwMDA3IGhhcyA0MywgNDQsIDYxLCA2Miwgc28gdGhhdCBzaG91bGQgYmUgZWFzeSB0byBjaGVjay4KIyMgSGVyZSBhcmUgdGhlIGZpcnN0IDgwIEFBIG9mIFJ2MDAwNyAgICAgICAgdiAgPC0gcG9zLiA0MyAgICAgdiA8LSBwb3MuIDYxIHYgPC0gNzQKIyMgVlRBUE5FUEdBTFNLR0RHUE5BREdMVkRSR0dBSFJBQVRHUEdSSVBEQUdEUFBQV1FSQUFUUlFTUUFHSFJRUFBQVlNIUEVHUlBUTlBQQUFBREEKIyMgWWF5IQoKIyMgT2ssIHNvIHRoZSBhY3R1YWwgcXVlc3Rpb24gd2FzLCBob3cgbWFueSBQUCBnZW5lcyBhcmUgdGhlcmUuCmxlbmd0aCh1bmlxdWUocHBfZGZbWyJJRCJdXSkpCnBwX2hpdHMgPC0gdW5pcXVlKHBwX2RmJElEKQpgYGAKCiMjIFJlcGVhdCBmb3IgUFBFCgpgYGB7ciBwcGV9CiMjIFdoYXQgYWJvdXQgUFBFPwpwYXR0ZXJuIDwtICJQUEUiCnBhdHRlcm4gPC0gQmlvY0dlbmVyaWNzOjpzYXBwbHkocGF0dGVybiwgdG9TdHJpbmcpCm10Yl9wcGUgPC0gQWhvQ29yYXNpY2tUcmllOjpBaG9Db3Jhc2lja1NlYXJjaChrZXl3b3Jkcz1wYXR0ZXJuLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0ZXh0PW10Yl9zdHJpbmcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFscGhhYmV0PSJhc2NpaSIpCnBwZV9kZiA8LSByZXNoYXBlMjo6bWVsdChkYXRhPW10Yl9wcGUsIGxldmVsPTEpCnBwZV9kZiA8LSBwcGVfZGZbLCBjKCJMMSIsICJ2YWx1ZSIpXQpkcm9wIDwtIHBwZV9kZltbInZhbHVlIl1dID09ICJQUEUiCnBwZV9kZiA8LSBwcGVfZGZbIWRyb3AsIF0KY29sbmFtZXMocHBlX2RmKSA8LSBjKCJJRCIsICJwb3NpdGlvbiIpCmhlYWQocHBlX2RmLCBuPTIwKQpsZW5ndGgodW5pcXVlKHBwZV9kZltbIklEIl1dKSkKYGBgCgojIyBGaW5kIG92ZXJsYXAgb2YgdHJ5cHNpbiBwcm9kdWN0cyB3aXRoIHRoZXNlCgpgYGB7ciBjbGVhdmVyfQp0cnlwX2hpdHMgPC0gY2xlYXZlcjo6Y2xlYXZhZ2VSYW5nZXMobXRiX2NkcywgZW56eW09InRyeXBzaW4iKQp0cnlwX2RmIDwtIGFzLmRhdGEuZnJhbWUodHJ5cF9oaXRzKQp0cnlwX25hbWVzIDwtIHVuaXF1ZSh0cnlwX2RmJGdyb3VwX25hbWUpCmBgYAoKIyMgQW5kIHRoZSBsaWtlbHkgbW9sZWN1bGFyIHdlaWdodHMKCmBgYHtyIHNpemVfZGlzdHJpYnV0aW9ufQptdGJfcHBlIDwtIG10Yl9jZHNbcHBlX2RmJElELCBdCnRyeXBfcHJvZHVjdHMgPC0gY2xlYXZlcjo6Y2xlYXZlKG10Yl9wcGUsIGVuenltPSJ0cnlwc2luIikKdHJ5cF9wcm9kdWN0c19kZiA8LSBhcy5kYXRhLmZyYW1lKHRyeXBfcHJvZHVjdHMpCnRyeXBfcHJvZHVjdHNfZGYkbXcgPC0gUGVwdGlkZXM6Om13KHRyeXBfcHJvZHVjdHNfZGYkdmFsdWUpCmRpbSh0cnlwX3Byb2R1Y3RzX2RmKQoKIyMgV2hhdCBwcm9kdWN0cyBhcmUgdGhlcmUgZnJvbSAxMCwwMDAgRGFsdG9ucyB0byAzMCwwMDA/Cmhpc3QodHJ5cF9wcm9kdWN0c19kZiRtdywgYnJlYWtzPTEwMDAsIHhsaW09YygxMDAwMCwgMzAwMDApLCB5bGltPWMoMCwgMTUpKQojIyBDYWxsIHRoZXNlICdpbnRlcm1lZGlhdGUnCmludGVybWVkaWF0ZSA8LSB0cnlwX3Byb2R1Y3RzX2RmCiMjIERyb3AgYW55dGhpbmcgYmlnZ2VyIHRoYW4gMzAsMDAwIERhbHRvbnMKa2VlcGVycyA8LSBpbnRlcm1lZGlhdGUkbXcgPD0gMzAwMDAKaW50ZXJtZWRpYXRlIDwtIGludGVybWVkaWF0ZVtrZWVwZXJzLCBdCiMjIERyb3AgYW55dGhpbmcgc21hbGxlciB0aGFuIDEwLDAwMCBEYWx0b25zCmtlZXBlcnMgPC0gaW50ZXJtZWRpYXRlJG13ID4gMTAwMDAKaW50ZXJtZWRpYXRlIDwtIGludGVybWVkaWF0ZVtrZWVwZXJzLCBdCnVuaXF1ZShpbnRlcm1lZGlhdGUkZ3JvdXBfbmFtZSkKYGBgCgojIyBMb29rIGZvciBvdmVybGFwIG9mIGhpZ2gtc3BlY2lmaWNpdHkgY2h5bW90cnlwc2luIGNsZWF2YWdlcwoKYGBge3IgY2xlYXZlcl9jaHltb2hpZ2h9CmNodHJ5cF9oaXRzIDwtIGNsZWF2ZXI6OmNsZWF2YWdlUmFuZ2VzKG10Yl9jZHMsIGVuenltPSJjaHltb3RyeXBzaW4taGlnaCIpCmNodHJ5cF9kZiA8LSBhcy5kYXRhLmZyYW1lKGNodHJ5cF9oaXRzKQpjaHRyeXBfbmFtZXMgPC0gdW5pcXVlKGNodHJ5cF9kZiRncm91cF9uYW1lKQpgYGAKCiMjIFRoZSBsaWtlbHkgbW9sZWN1bGFyIHdlaWdodHMgZm9yIGhpZ2gtc3BlY2lmaWNpdHkgY2h5bW90cnlwc2luCgpJbiB0aGlzIGNhc2Ugd2UgbG9vayBmb3IgQy0+W0ZZV10gbm90IGJlZm9yZSBQLgoKYGBge3Igc2l6ZV9kaXN0cmlidXRpb25fY2h5bW9oaWdofQptdGJfcHBlIDwtIG10Yl9jZHNbcHBlX2RmJElELCBdCnRyeXBfcHJvZHVjdHMgPC0gY2xlYXZlcjo6Y2xlYXZlKG10Yl9wcGUsIGVuenltPSJjaHltb3RyeXBzaW4taGlnaCIpCnRyeXBfcHJvZHVjdHNfZGYgPC0gYXMuZGF0YS5mcmFtZSh0cnlwX3Byb2R1Y3RzKQp0cnlwX3Byb2R1Y3RzX2RmJG13IDwtIFBlcHRpZGVzOjptdyh0cnlwX3Byb2R1Y3RzX2RmJHZhbHVlKQpkaW0odHJ5cF9wcm9kdWN0c19kZikKCiMjIFdoYXQgcHJvZHVjdHMgYXJlIHRoZXJlIGZyb20gMTAsMDAwIERhbHRvbnMgdG8gMzAsMDAwPwpoaXN0KHRyeXBfcHJvZHVjdHNfZGYkbXcsIGJyZWFrcz0xMDAwLCB4bGltPWMoMTAwMDAsIDMwMDAwKSwgeWxpbT1jKDAsIDE1KSkKIyMgQ2FsbCB0aGVzZSAnaW50ZXJtZWRpYXRlJwppbnRlcm1lZGlhdGUgPC0gdHJ5cF9wcm9kdWN0c19kZgojIyBEcm9wIGFueXRoaW5nIGJpZ2dlciB0aGFuIDMwLDAwMCBEYWx0b25zCmtlZXBlcnMgPC0gaW50ZXJtZWRpYXRlJG13IDw9IDMwMDAwCmludGVybWVkaWF0ZSA8LSBpbnRlcm1lZGlhdGVba2VlcGVycywgXQojIyBEcm9wIGFueXRoaW5nIHNtYWxsZXIgdGhhbiAxMCwwMDAgRGFsdG9ucwprZWVwZXJzIDwtIGludGVybWVkaWF0ZSRtdyA+IDEwMDAwCmludGVybWVkaWF0ZSA8LSBpbnRlcm1lZGlhdGVba2VlcGVycywgXQp1bmlxdWUoaW50ZXJtZWRpYXRlJGdyb3VwX25hbWUpCmBgYAoKIyMgRmluYWxseSwgdGhlIGxvdy1zcGVjaWZpY2l0eSBjaHltb3RyeXBzaW4gZGlnZXN0aW9uIHByb2R1Y3RzCgpgYGB7ciBjbGVhdmVyX2NoeW1vbG93fQpjbHRyeXBfaGl0cyA8LSBjbGVhdmVyOjpjbGVhdmFnZVJhbmdlcyhtdGJfY2RzLCBlbnp5bT0iY2h5bW90cnlwc2luLWxvdyIpCmNsdHJ5cF9kZiA8LSBhcy5kYXRhLmZyYW1lKGNsdHJ5cF9oaXRzKQpjbHRyeXBfbmFtZXMgPC0gdW5pcXVlKGNsdHJ5cF9kZiRncm91cF9uYW1lKQoKIyMgQWxsIG9mIHRoZSBwcCBjb250YWluaW5nIHBlcHRpZGVzIGhhdmUgdHJ5cHNpbiBkaWdlc3Rpb24gcHJvZHVjdHMuCnN1bShwcF9oaXRzICVpbiUgdHJ5cF9uYW1lcykKYGBgCgojIyBBbmQgQ2h5bW90cnlwc2luLWxvdyBzaXplIGRpc3RyaWJ1dGlvbgoKQW5kIGhlcmUgd2UgbG9vayBmb3IgQy0+W0ZZV01MXSBub3QgYmVmb3JlIFAuCgpgYGB7ciBzaXplX2Rpc3RyaWJ1dGlvbn0KbXRiX3BwZSA8LSBtdGJfY2RzW3BwZV9kZiRJRCwgXQp0cnlwX3Byb2R1Y3RzIDwtIGNsZWF2ZXI6OmNsZWF2ZShtdGJfcHBlLCBlbnp5bT0iY2h5bW90cnlwc2luLWxvdyIpCnRyeXBfcHJvZHVjdHNfZGYgPC0gYXMuZGF0YS5mcmFtZSh0cnlwX3Byb2R1Y3RzKQp0cnlwX3Byb2R1Y3RzX2RmJG13IDwtIFBlcHRpZGVzOjptdyh0cnlwX3Byb2R1Y3RzX2RmJHZhbHVlKQpkaW0odHJ5cF9wcm9kdWN0c19kZikKCiMjIFdoYXQgcHJvZHVjdHMgYXJlIHRoZXJlIGZyb20gMTAsMDAwIERhbHRvbnMgdG8gMzAsMDAwPwpoaXN0KHRyeXBfcHJvZHVjdHNfZGYkbXcsIGJyZWFrcz0xMDAwLCB4bGltPWMoMTAwMCwgNzAwMCksIHlsaW09YygwLCAxNSkpCiMjIENhbGwgdGhlc2UgJ2ludGVybWVkaWF0ZScKaW50ZXJtZWRpYXRlIDwtIHRyeXBfcHJvZHVjdHNfZGYKIyMgRHJvcCBhbnl0aGluZyBiaWdnZXIgdGhhbiAzMCwwMDAgRGFsdG9ucwprZWVwZXJzIDwtIGludGVybWVkaWF0ZSRtdyA8PSAzMDAwMAppbnRlcm1lZGlhdGUgPC0gaW50ZXJtZWRpYXRlW2tlZXBlcnMsIF0KIyMgRHJvcCBhbnl0aGluZyBzbWFsbGVyIHRoYW4gMTAsMDAwIERhbHRvbnMKa2VlcGVycyA8LSBpbnRlcm1lZGlhdGUkbXcgPiAxMDAwMAppbnRlcm1lZGlhdGUgPC0gaW50ZXJtZWRpYXRlW2tlZXBlcnMsIF0KdW5pcXVlKGludGVybWVkaWF0ZSRncm91cF9uYW1lKQpgYGAK