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?
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)
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
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)
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"
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)
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"
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
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