Estimated time: 15 Minutes
Motivation
Customizing plots can help us see patterns in the data or make the
claim(s) based on the data represented clearer.
Instructions
- Work independently in the main room, posting any questions that
arise to slack.
- Recommendations for writing your own code:
- Read function documentation
- Test out ideas - it’s okay to make mistakes and generate errors
- Use a search engine to look up errors or recommended solutions using
keywords
- We’ll review possible solutions after time is up as a group.
Exercise
Try doing the following to the pca_plot
, starting with
the “most popular” request and moving on to other customizations if you
have time:
- Add a title and subtitle to the plot
- Update the color palette to be color-blind friendly
- Add labels to show which samples correspond to which points
- Use shape instead of color to indicate groups on the PCA plot.
- Challenge: Change the legend title to “Iron Status”.
Example
Here is a copy of the code we just tested together to 1) pull the
underlying data from the PCA function and 2) change the theme of our PCA
plot to black and white.
pcaData <- plotPCA(rld, intgroup=c("condition"), returnData=TRUE)
percentVar <- round(100 * attr(pcaData, "percentVar")) # store PC axes (% variance)
# create custom plot object
PCACustom <- ggplot(pcaData, aes(PC1, PC2, color=condition)) +
geom_point(size=3) +
coord_fixed() +
theme_bw()
# add percentVar labels to *displayed plot*
PCACustom +
xlab(paste0("PC1: ",percentVar[1],"% variance")) +
ylab(paste0("PC2: ",percentVar[2],"% variance"))
# add percentVar labels to *stored plot object*
PCACustom2 <- PCACustom +
xlab(paste0("PC1: ",percentVar[1],"% variance")) +
ylab(paste0("PC2: ",percentVar[2],"% variance"))
Details & finding help
Add a title and subtitle to the ggplot plot
- Hint: use the
labs()
function or search for examples
- Remember that unless a change to a plot is assigned to an
object, although the change will be displayed it will not stored
for later reference or output to file
Possible solution
Example of possible :
?labs
PCACustom2 +
labs(title = "Iron Supplemented Mice", subtitle = "PCA of top 500 genes")
PCACustom3 <- PCACustom2 +
labs(title = "Iron Supplemented Mice", subtitle = "PCA of top 500 genes")
Add labels to show which samples correspond to which points
Possible solution
Example of possible solution:
?geom_label_repel
# display
PCACustom2 +
geom_text_repel(aes(label = name),
point.padding = 0.5,
box.padding = 0.5)
# save to new object
PCACustom4 <- PCACustom2 +
geom_text_repel(aes(label = name),
point.padding = 0.5,
box.padding = 0.5)
Make our color palette more color-blind friendly (with
RColorBrewer
)
Possible solution
Example of possible solution:
# look at pre-made color palettes from RColorBrewer
display.brewer.all(colorblindFriendly = TRUE)
# use RColorBrewer palette
PCACustom2 +
scale_colour_brewer(palette = "Set2")
# OR
# customize using manual color palette
# The R Cookbook palette with grey:
cbPalette <- c("#999999", "#E69F00", "#56B4E9", "#009E73", "#F0E442", "#0072B2", "#D55E00", "#CC79A7")
# To use for line and point colors, add manual color scaling with custom palette
PCACustom2 +
scale_colour_manual(values=cbPalette[2:3])
PCACustom5 <- PCACustom2 +
scale_colour_manual(values=cbPalette[2:3])
Use shape instead of color to indicate groups on the PCA plot.
Possible solution
Example of possible solution:
# generate new aesthetic mapping (with default shapes selected)
ggplot(pcaData, aes(PC1, PC2, shape=condition)) +
geom_point(size=3) +
coord_fixed() +
theme_bw() +
xlab(paste0("PC1: ",percentVar[1],"% variance")) +
ylab(paste0("PC2: ",percentVar[2],"% variance"))
# generate new aesthetic mapping (with manually selected shapes)
ggplot(pcaData, aes(PC1, PC2, shape=condition)) +
geom_point(size=3) +
scale_shape_manual(values = c(1, 4)) +
coord_fixed() +
theme_bw() +
xlab(paste0("PC1: ",percentVar[1],"% variance")) +
ylab(paste0("PC2: ",percentVar[2],"% variance"))
# create custom plot object with manual shapes
PCACustom6 <- ggplot(pcaData, aes(PC1, PC2, shape=condition)) +
geom_point(size=3) +
scale_shape_manual(values = c(1, 4)) +
coord_fixed() +
theme_bw() +
xlab(paste0("PC1: ",percentVar[1],"% variance")) +
ylab(paste0("PC2: ",percentVar[2],"% variance"))
Challenge: Change the legend title to “Iron Status”
- Hint, you can do this with the
labs()
function too,
using the corresponding aesthetic mapping (e.g. “color”).
- This
help thread with examples may also be useful
Possible solution
Example of possible solution:
# customize label for colour mapping
PCACustom2 +
guides(colour=guide_legend(title="Iron supplementation status"))
# alternatively specify label for aesthetic mapping
PCACustom2 +
labs(colour="Iron supplementation status")
# store custom plot as new object
PCACustom7 <- PCACustom2 +
labs(colour="Iron supplementation status")
Saving the result
If time permits, consider how you might save your favorite new PCA
plot (with an informative file name). Hint: Consider how we saved our
initial PCA plot in the previous module with ggsave()
.
Solution
Here are examples of some possible approaches:
pdf(file = file.path('outputs', 'figures', 'PCA_rlog_Titled.pdf'), width = 6, height = 6)
PCACustom3
dev.off()
ggsave(
filename = file.path('outputs', 'figures', 'PCA_rlog_Titled.pdf'),
plot = PCACustom3,
width = 6, height = 6, units = 'in')
LS0tCnRpdGxlOiAiSW5kZXBlbmRlbnQgRXhlcmNpc2UgLSBDdXN0b21pemUgYSBQQ0EgcGxvdCIKYXV0aG9yOiAiVU0gQmlvaW5mb3JtYXRpY3MgQ29yZSIKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6CiAgICAgICAgaHRtbF9kb2N1bWVudDoKICAgICAgICAgICAgaW5jbHVkZXM6CiAgICAgICAgICAgICAgICBpbl9oZWFkZXI6IGhlYWRlci5odG1sCiAgICAgICAgICAgIHRoZW1lOiBwYXBlcgogICAgICAgICAgICB0b2M6IHRydWUKICAgICAgICAgICAgdG9jX2RlcHRoOiA0CiAgICAgICAgICAgIHRvY19mbG9hdDogdHJ1ZQogICAgICAgICAgICBudW1iZXJfc2VjdGlvbnM6IGZhbHNlCiAgICAgICAgICAgIGZpZ19jYXB0aW9uOiB0cnVlCiAgICAgICAgICAgIG1hcmtkb3duOiBHRk0KICAgICAgICAgICAgY29kZV9kb3dubG9hZDogdHJ1ZQotLS0KCjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+CmJvZHksIHRkIHsKICAgZm9udC1zaXplOiAxOHB4Owp9CmNvZGUucnsKICBmb250LXNpemU6IDEycHg7Cn0KcHJlIHsKICBmb250LXNpemU6IDEycHgKfQo8L3N0eWxlPgoKYGBge3IsIGluY2x1ZGUgPSBGQUxTRX0Kc291cmNlKCIuLi9iaW4vY2h1bmstb3B0aW9ucy5SIikKa25pdHJfZmlnX3BhdGgoIjA5YS0iKQpgYGAKCmBgYHtyIE1vZHVsZXMsIGV2YWw9VFJVRSwgZWNobz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeShERVNlcTIpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeSh0aWR5cikKbGlicmFyeShkcGx5cikKbGlicmFyeShtYXRyaXhTdGF0cykKbGlicmFyeShnZ3JlcGVsKQpsaWJyYXJ5KHBoZWF0bWFwKQpsaWJyYXJ5KFJDb2xvckJyZXdlcikKIyBsb2FkKCJyZGF0YS9SdW5uaW5nRGF0YS5SRGF0YSIpCmBgYAoKRXN0aW1hdGVkIHRpbWU6ICoqMTUgTWludXRlcyoqCgoKIyBNb3RpdmF0aW9uCgpDdXN0b21pemluZyBwbG90cyBjYW4gaGVscCB1cyBzZWUgcGF0dGVybnMgaW4gdGhlIGRhdGEgb3IgbWFrZSB0aGUgY2xhaW0ocykgYmFzZWQgb24gdGhlIGRhdGEgcmVwcmVzZW50ZWQgY2xlYXJlci4KCiMgSW5zdHJ1Y3Rpb25zCgotIFdvcmsgaW5kZXBlbmRlbnRseSBpbiB0aGUgbWFpbiByb29tLCBwb3N0aW5nIGFueSBxdWVzdGlvbnMgdGhhdCBhcmlzZSB0byBzbGFjay4KLSBSZWNvbW1lbmRhdGlvbnMgZm9yIHdyaXRpbmcgeW91ciBvd24gY29kZToKICAtIFJlYWQgZnVuY3Rpb24gZG9jdW1lbnRhdGlvbgogIC0gVGVzdCBvdXQgaWRlYXMgLSBpdCdzIG9rYXkgdG8gbWFrZSBtaXN0YWtlcyBhbmQgZ2VuZXJhdGUgZXJyb3JzCiAgLSBVc2UgYSBzZWFyY2ggZW5naW5lIHRvIGxvb2sgdXAgZXJyb3JzIG9yIHJlY29tbWVuZGVkIHNvbHV0aW9ucyB1c2luZyBrZXl3b3JkcwotIFdlJ2xsIHJldmlldyBwb3NzaWJsZSBzb2x1dGlvbnMgYWZ0ZXIgdGltZSBpcyB1cCBhcyBhIGdyb3VwLgoKIyBFeGVyY2lzZQoKVHJ5IGRvaW5nIHRoZSBmb2xsb3dpbmcgdG8gdGhlIGBwY2FfcGxvdGAsIHN0YXJ0aW5nIHdpdGggdGhlICJtb3N0IHBvcHVsYXIiIHJlcXVlc3QgYW5kIG1vdmluZyBvbiB0byBvdGhlciBjdXN0b21pemF0aW9ucyBpZiB5b3UgaGF2ZSB0aW1lOgoKPiAqIEFkZCBhIHRpdGxlIGFuZCBzdWJ0aXRsZSB0byB0aGUgcGxvdAo+ICogVXBkYXRlIHRoZSBjb2xvciBwYWxldHRlIHRvIGJlIGNvbG9yLWJsaW5kIGZyaWVuZGx5Cj4gKiBBZGQgbGFiZWxzIHRvIHNob3cgd2hpY2ggc2FtcGxlcyBjb3JyZXNwb25kIHRvIHdoaWNoIHBvaW50cwo+ICogVXNlIHNoYXBlIGluc3RlYWQgb2YgY29sb3IgdG8gaW5kaWNhdGUgZ3JvdXBzIG9uIHRoZSBQQ0EgcGxvdC4KPiAqIF9DaGFsbGVuZ2VfOiBDaGFuZ2UgdGhlIGxlZ2VuZCB0aXRsZSB0byAiSXJvbiBTdGF0dXMiLiAKCiMgRXhhbXBsZQoKSGVyZSBpcyBhIGNvcHkgb2YgdGhlIGNvZGUgd2UganVzdCB0ZXN0ZWQgdG9nZXRoZXIgdG8gMSkgcHVsbCB0aGUgdW5kZXJseWluZyBkYXRhIGZyb20gdGhlIFBDQSBmdW5jdGlvbiBhbmQgMikgY2hhbmdlIHRoZSB0aGVtZSBvZiBvdXIgUENBIHBsb3QgdG8gYmxhY2sgYW5kIHdoaXRlLgoKYGBge3IgdGVzdF9QQ0F9CnBjYURhdGEgPC0gcGxvdFBDQShybGQsIGludGdyb3VwPWMoImNvbmRpdGlvbiIpLCByZXR1cm5EYXRhPVRSVUUpCnBlcmNlbnRWYXIgPC0gcm91bmQoMTAwICogYXR0cihwY2FEYXRhLCAicGVyY2VudFZhciIpKSAjIHN0b3JlIFBDIGF4ZXMgKCUgdmFyaWFuY2UpCgojIGNyZWF0ZSBjdXN0b20gcGxvdCBvYmplY3QKUENBQ3VzdG9tIDwtIGdncGxvdChwY2FEYXRhLCBhZXMoUEMxLCBQQzIsIGNvbG9yPWNvbmRpdGlvbikpICsKICBnZW9tX3BvaW50KHNpemU9MykgKwogIGNvb3JkX2ZpeGVkKCkgKwogIHRoZW1lX2J3KCkKCiMgYWRkIHBlcmNlbnRWYXIgbGFiZWxzIHRvICpkaXNwbGF5ZWQgcGxvdCoKUENBQ3VzdG9tICsgCiAgeGxhYihwYXN0ZTAoIlBDMTogIixwZXJjZW50VmFyWzFdLCIlIHZhcmlhbmNlIikpICsKICB5bGFiKHBhc3RlMCgiUEMyOiAiLHBlcmNlbnRWYXJbMl0sIiUgdmFyaWFuY2UiKSkKCiMgYWRkIHBlcmNlbnRWYXIgbGFiZWxzIHRvICpzdG9yZWQgcGxvdCBvYmplY3QqClBDQUN1c3RvbTIgPC0gUENBQ3VzdG9tICsgCiAgeGxhYihwYXN0ZTAoIlBDMTogIixwZXJjZW50VmFyWzFdLCIlIHZhcmlhbmNlIikpICsKICB5bGFiKHBhc3RlMCgiUEMyOiAiLHBlcmNlbnRWYXJbMl0sIiUgdmFyaWFuY2UiKSkKYGBgCgojIERldGFpbHMgJiBmaW5kaW5nIGhlbHAKCiMjIEFkZCBhIHRpdGxlIGFuZCBzdWJ0aXRsZSB0byB0aGUgZ2dwbG90IHBsb3QKCiAgKiBIaW50OiB1c2UgdGhlIGBsYWJzKClgIGZ1bmN0aW9uIG9yIHNlYXJjaCBmb3IgW2V4YW1wbGVzXShodHRwczovL3d3dy5kYXRhbm92aWEuY29tL2VuL2Jsb2cvZ2dwbG90LXRpdGxlLXN1YnRpdGxlLWFuZC1jYXB0aW9uLykKICAqIFJlbWVtYmVyIHRoYXQgdW5sZXNzIGEgY2hhbmdlIHRvIGEgcGxvdCBpcyBfYXNzaWduZWQgdG8gYW4gb2JqZWN0XywgYWx0aG91Z2ggdGhlIGNoYW5nZSB3aWxsIGJlIGRpc3BsYXllZCBpdCB3aWxsIG5vdCBzdG9yZWQgZm9yIGxhdGVyIHJlZmVyZW5jZSBvciBvdXRwdXQgdG8gZmlsZQogIAogIAo8ZGV0YWlscz4KPHN1bW1hcnk+UG9zc2libGUgc29sdXRpb248L3N1bW1hcnk+CgpFeGFtcGxlIG9mIHBvc3NpYmxlIDoKCmBgYHtyLCBldmFsPSBGQUxTRX0KP2xhYnMKUENBQ3VzdG9tMiArIAogIGxhYnModGl0bGUgPSAiSXJvbiBTdXBwbGVtZW50ZWQgTWljZSIsIHN1YnRpdGxlID0gIlBDQSBvZiB0b3AgNTAwIGdlbmVzIikKClBDQUN1c3RvbTMgPC0gUENBQ3VzdG9tMiArIAogIGxhYnModGl0bGUgPSAiSXJvbiBTdXBwbGVtZW50ZWQgTWljZSIsIHN1YnRpdGxlID0gIlBDQSBvZiB0b3AgNTAwIGdlbmVzIikKYGBgCgo8L2RldGFpbHM+Cjxicj4KCgojIyBBZGQgbGFiZWxzIHRvIHNob3cgd2hpY2ggc2FtcGxlcyBjb3JyZXNwb25kIHRvIHdoaWNoIHBvaW50cwoKICAqIEhpbnQ6IHVzZSB0aGUgYGdlb21fbGFiZWwoKWAgb3IgYGdlb21fbGFiZWxfcmVwZWwoKWAgZnVuY3Rpb25zIGFuZCB0aGUgYG5hbWVgIGNvbHVtbiBpbiBvdXIgYHBjYURhdGFgIG9iamVjdAogICogU2VlIFtleGFtcGxlIGluIHRoaXMgc3VwcG9ydCBwb3N0XShodHRwczovL3N1cHBvcnQuYmlvY29uZHVjdG9yLm9yZy9wLzkwNzkxLykgYW5kL29yIFthcHBseSBtb3JlIGdlbmVyYWwgcmVjb21tZW5kYXRpb25zIGZyb20gdGhpcyBSIGdyYXBoIGdhbGxlcnldKGh0dHBzOi8vci1ncmFwaC1nYWxsZXJ5LmNvbS8yNzUtYWRkLXRleHQtbGFiZWxzLXdpdGgtZ2dwbG90Mi5odG1sKQoKPGRldGFpbHM+CjxzdW1tYXJ5PlBvc3NpYmxlIHNvbHV0aW9uPC9zdW1tYXJ5PgoKRXhhbXBsZSBvZiBwb3NzaWJsZSBzb2x1dGlvbjoKCmBgYHtyLCBldmFsPSBGQUxTRX0KP2dlb21fbGFiZWxfcmVwZWwKCiMgZGlzcGxheQpQQ0FDdXN0b20yICsgCiAgZ2VvbV90ZXh0X3JlcGVsKGFlcyhsYWJlbCA9IG5hbWUpLCAKICAgICAgICAgICAgICAgICAgcG9pbnQucGFkZGluZyA9IDAuNSwgCiAgICAgICAgICAgICAgICAgIGJveC5wYWRkaW5nID0gMC41KQoKIyBzYXZlIHRvIG5ldyBvYmplY3QKUENBQ3VzdG9tNCA8LSBQQ0FDdXN0b20yICsgCiAgZ2VvbV90ZXh0X3JlcGVsKGFlcyhsYWJlbCA9IG5hbWUpLCAKICAgICAgICAgICAgICAgICAgcG9pbnQucGFkZGluZyA9IDAuNSwgCiAgICAgICAgICAgICAgICAgIGJveC5wYWRkaW5nID0gMC41KQogIApgYGAKCjwvZGV0YWlscz4KPGJyPgoKIyMgTWFrZSBvdXIgY29sb3IgcGFsZXR0ZSBtb3JlIGNvbG9yLWJsaW5kIGZyaWVuZGx5ICh3aXRoIGBSQ29sb3JCcmV3ZXJgKQogICogVXNlIGVpdGhlciB0aGUgYHNjYWxlX2NvbG91cl9icmV3ZXJgIGZ1bmN0aW9uIHRvIFtjaGFuZ2UgdGhlIGNvbG9yIG1hcHBpbmdzIGluIG91ciBwbG90XShodHRwczovL3N0YXRpc3RpY3NnbG9iZS5jb20vc2NhbGUtY29sb3VyLWZpbGwtYnJld2VyLXJjb2xvcmJyZXdlci1wYWNrYWdlLXIpIE9SIGBzY2FsZV9jb2xvdXJfbWFudWFsYCBmdW5jdGlvbiB0byBbY29tcGxldGVseSBjdXN0b21pemUgY29sb3IgY2hvaWNlc10oaHR0cDovL3d3dy5jb29rYm9vay1yLmNvbS9HcmFwaHMvQ29sb3JzXyhnZ3Bsb3QyKS8jYS1jb2xvcmJsaW5kLWZyaWVuZGx5LXBhbGV0dGUpCiAgKiBQcmUtZGVzaWduZWQgW2NvbG9yLWJsaW5kIGZyaWVuZGx5IHBhbGV0dGVzIGFyZSBhdmFpbGFibGVdKGh0dHA6Ly93d3cuY29va2Jvb2stci5jb20vR3JhcGhzL0NvbG9yc18oZ2dwbG90MikvI2EtY29sb3JibGluZC1mcmllbmRseS1wYWxldHRlKQogICAqIFdoZW4gY29uc2lkZXJpbmcgY29sb3IgY2hvaWNlcywgYmUgYXdhcmUgb2YgdGhlIFtwcmV2YWxlbmNlIGFuZCB0eXBlcyBvZiBjb2xvci1ibGluZG5lc3NdKGh0dHBzOi8vbWVkaXVtLmNvbS92ZXJzaW9uLTEvc2ltdWxhdGluZy12aXN1YWxpc2F0aW9ucy1pbi1yLWZvci1jb2xvdXItYmxpbmRuZXNzLTJmYWRkZGU2MzY5NSkgYW5kIGhvdyB0byBjaG9vc2UgbW9yZSBbZWFzaWx5IGRpc3Rpbmd1aXNoYWJsZSBjb2xvcnNdKGh0dHA6Ly9iY29ubmVsbHkubmV0L3Bvc3RzL2NyZWF0aW5nX2NvbG9yYmxpbmQtZnJpZW5kbHlfZmlndXJlcy8pCiAgKiBGb3IgcHVibGljYXRpb24gb3IgcHJlc2VudGF0aW9ucywgd2UgY2FuIGFsc28gY2hlY2sgdGhlIGZpbmFsIGZpZ3VyZXMgZm9yIHN1ZmZpY2llbnQgY29udHJhc3RzIHdpdGggYSBbY29sb3ItYmxpbmRuZXNzIHNpbXVsYXRvciB0b29sXShodHRwczovL3d3dy5jb2xvci1ibGluZG5lc3MuY29tL2NvYmxpcy1jb2xvci1ibGluZG5lc3Mtc2ltdWxhdG9yLykuIAogIAo8ZGV0YWlscz4KPHN1bW1hcnk+UG9zc2libGUgc29sdXRpb248L3N1bW1hcnk+CgpFeGFtcGxlIG9mIHBvc3NpYmxlIHNvbHV0aW9uOgoKYGBge3IsIGV2YWw9IEZBTFNFfQojIGxvb2sgYXQgcHJlLW1hZGUgY29sb3IgcGFsZXR0ZXMgZnJvbSBSQ29sb3JCcmV3ZXIKZGlzcGxheS5icmV3ZXIuYWxsKGNvbG9yYmxpbmRGcmllbmRseSA9IFRSVUUpCiMgdXNlIFJDb2xvckJyZXdlciBwYWxldHRlClBDQUN1c3RvbTIgKyAKICBzY2FsZV9jb2xvdXJfYnJld2VyKHBhbGV0dGUgPSAiU2V0MiIpCgojIE9SCiMgY3VzdG9taXplIHVzaW5nIG1hbnVhbCBjb2xvciBwYWxldHRlCiMgVGhlIFIgQ29va2Jvb2sgcGFsZXR0ZSB3aXRoIGdyZXk6CmNiUGFsZXR0ZSA8LSBjKCIjOTk5OTk5IiwgIiNFNjlGMDAiLCAiIzU2QjRFOSIsICIjMDA5RTczIiwgIiNGMEU0NDIiLCAiIzAwNzJCMiIsICIjRDU1RTAwIiwgIiNDQzc5QTciKQoKIyBUbyB1c2UgZm9yIGxpbmUgYW5kIHBvaW50IGNvbG9ycywgYWRkIG1hbnVhbCBjb2xvciBzY2FsaW5nIHdpdGggY3VzdG9tIHBhbGV0dGUKUENBQ3VzdG9tMiArIAogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzPWNiUGFsZXR0ZVsyOjNdKQoKUENBQ3VzdG9tNSA8LSBQQ0FDdXN0b20yICsgCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXM9Y2JQYWxldHRlWzI6M10pCiAgCmBgYAoKPC9kZXRhaWxzPgo8YnI+CiAgCiMjIFVzZSBzaGFwZSBpbnN0ZWFkIG9mIGNvbG9yIHRvIGluZGljYXRlIGdyb3VwcyBvbiB0aGUgUENBIHBsb3QuIAoKICAqIEhpbnQ6IHNpbmNlIGNvbG9yIGFuZCBzaGFwZSBhcmUgYm90aCBtYXBwaW5nIGFzcGVjdHMsIHNvIHdlJ2xsIG5lZWQgdG8gcmUtc3BlY2lmeSBzaGFwZSBpbnN0ZWFkIG9mIGNvbG9yIGluc3RlYWQgb2YgYWRkaW5nIGFuIGFkZGl0aW9uYWwgbGF5ZXJzIGxpa2UgdGhlIG90aGVyIHByb21wdHMgCiAgKiBUaGUgW2RvY3VtZW50YXRpb24gcGFnZSBmb3IgYGdlb21fcG9pbnRgXShodHRwczovL2dncGxvdDIudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2UvZ2VvbV9wb2ludC5odG1sKSBhbmQgW1IgZ3JhcGhpY3MgY29va2Jvb2tzXShodHRwczovL3ItZ3JhcGhpY3Mub3JnL3JlY2lwZS1zY2F0dGVyLXNoYXBlcykgaW5jbHVkZXMgc29tZSBleGFtcGxlcyBvZiBjaGFuZ2luZyB0aGUgc2hhcGVzIG9mIHRoZSBwbG90dGVkIHBvaW50cwogICogW1RoZXNlIFIgZ3JhcGggZ2FsbGVyeSBleGFtcGxlc10oaHR0cHM6Ly9yLWdyYXBoLWdhbGxlcnkuY29tLzI3NC1tYXAtYS12YXJpYWJsZS10by1nZ3Bsb3QyLXNjYXR0ZXJwbG90Lmh0bWwpIGNvdWxkIGFsc28gYmUgaGVscGZ1bAoKPGRldGFpbHM+CjxzdW1tYXJ5PlBvc3NpYmxlIHNvbHV0aW9uPC9zdW1tYXJ5PgoKRXhhbXBsZSBvZiBwb3NzaWJsZSBzb2x1dGlvbjoKCmBgYHtyLCBldmFsPSBGQUxTRX0KIyBnZW5lcmF0ZSBuZXcgYWVzdGhldGljIG1hcHBpbmcgKHdpdGggZGVmYXVsdCBzaGFwZXMgc2VsZWN0ZWQpCmdncGxvdChwY2FEYXRhLCBhZXMoUEMxLCBQQzIsIHNoYXBlPWNvbmRpdGlvbikpICsKICBnZW9tX3BvaW50KHNpemU9MykgKwogIGNvb3JkX2ZpeGVkKCkgKwogIHRoZW1lX2J3KCkgKyAKICB4bGFiKHBhc3RlMCgiUEMxOiAiLHBlcmNlbnRWYXJbMV0sIiUgdmFyaWFuY2UiKSkgKwogIHlsYWIocGFzdGUwKCJQQzI6ICIscGVyY2VudFZhclsyXSwiJSB2YXJpYW5jZSIpKQoKCiMgZ2VuZXJhdGUgbmV3IGFlc3RoZXRpYyBtYXBwaW5nICh3aXRoIG1hbnVhbGx5IHNlbGVjdGVkIHNoYXBlcykKZ2dwbG90KHBjYURhdGEsIGFlcyhQQzEsIFBDMiwgc2hhcGU9Y29uZGl0aW9uKSkgKwogIGdlb21fcG9pbnQoc2l6ZT0zKSArCiAgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcyA9IGMoMSwgNCkpICsKICBjb29yZF9maXhlZCgpICsKICB0aGVtZV9idygpICsgCiAgeGxhYihwYXN0ZTAoIlBDMTogIixwZXJjZW50VmFyWzFdLCIlIHZhcmlhbmNlIikpICsKICB5bGFiKHBhc3RlMCgiUEMyOiAiLHBlcmNlbnRWYXJbMl0sIiUgdmFyaWFuY2UiKSkKCiMgY3JlYXRlIGN1c3RvbSBwbG90IG9iamVjdCB3aXRoIG1hbnVhbCBzaGFwZXMKUENBQ3VzdG9tNiA8LSBnZ3Bsb3QocGNhRGF0YSwgYWVzKFBDMSwgUEMyLCBzaGFwZT1jb25kaXRpb24pKSArCiAgZ2VvbV9wb2ludChzaXplPTMpICsKICBzY2FsZV9zaGFwZV9tYW51YWwodmFsdWVzID0gYygxLCA0KSkgKwogIGNvb3JkX2ZpeGVkKCkgKwogIHRoZW1lX2J3KCkgKyAKICB4bGFiKHBhc3RlMCgiUEMxOiAiLHBlcmNlbnRWYXJbMV0sIiUgdmFyaWFuY2UiKSkgKwogIHlsYWIocGFzdGUwKCJQQzI6ICIscGVyY2VudFZhclsyXSwiJSB2YXJpYW5jZSIpKQoKYGBgCjwvZGV0YWlscz4KPGJyPgoKIyMgX0NoYWxsZW5nZV86IENoYW5nZSB0aGUgbGVnZW5kIHRpdGxlIHRvICJJcm9uIFN0YXR1cyIKCiAgKiBIaW50LCB5b3UgY2FuIGRvIHRoaXMgd2l0aCB0aGUgYGxhYnMoKWAgZnVuY3Rpb24gdG9vLCB1c2luZyB0aGUgY29ycmVzcG9uZGluZyBhZXN0aGV0aWMgbWFwcGluZyAoZS5nLiAiY29sb3IiKS4KICAqIFtUaGlzIGhlbHAgdGhyZWFkIHdpdGggZXhhbXBsZXNdKGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzE0NjIyNDIxL2hvdy10by1jaGFuZ2UtbGVnZW5kLXRpdGxlLWluLWdncGxvdCkgbWF5IGFsc28gYmUgdXNlZnVsCgo8ZGV0YWlscz4KPHN1bW1hcnk+UG9zc2libGUgc29sdXRpb248L3N1bW1hcnk+CgpFeGFtcGxlIG9mIHBvc3NpYmxlIHNvbHV0aW9uOgoKYGBge3IsIGV2YWw9IEZBTFNFfQojICBjdXN0b21pemUgbGFiZWwgZm9yIGNvbG91ciBtYXBwaW5nClBDQUN1c3RvbTIgKyAKICBndWlkZXMoY29sb3VyPWd1aWRlX2xlZ2VuZCh0aXRsZT0iSXJvbiBzdXBwbGVtZW50YXRpb24gc3RhdHVzIikpIAoKIyBhbHRlcm5hdGl2ZWx5IHNwZWNpZnkgbGFiZWwgZm9yIGFlc3RoZXRpYyBtYXBwaW5nClBDQUN1c3RvbTIgKyAKICBsYWJzKGNvbG91cj0iSXJvbiBzdXBwbGVtZW50YXRpb24gc3RhdHVzIikKCiMgc3RvcmUgY3VzdG9tIHBsb3QgYXMgbmV3IG9iamVjdApQQ0FDdXN0b203IDwtIFBDQUN1c3RvbTIgKyAKICBsYWJzKGNvbG91cj0iSXJvbiBzdXBwbGVtZW50YXRpb24gc3RhdHVzIikKICAKYGBgCjwvZGV0YWlscz4KPGJyPgoKIyBTYXZpbmcgdGhlIHJlc3VsdAoKSWYgdGltZSBwZXJtaXRzLCBjb25zaWRlciBob3cgeW91IG1pZ2h0IHNhdmUgeW91ciBmYXZvcml0ZSBuZXcgUENBIHBsb3QgKHdpdGggYW4gaW5mb3JtYXRpdmUgZmlsZSBuYW1lKS4gSGludDogQ29uc2lkZXIgaG93IHdlIHNhdmVkIG91ciBpbml0aWFsIFBDQSBwbG90IGluIHRoZSBwcmV2aW91cyBtb2R1bGUgd2l0aCBgZ2dzYXZlKClgLgoKPGRldGFpbHM+CjxzdW1tYXJ5PlNvbHV0aW9uPC9zdW1tYXJ5PgoKSGVyZSBhcmUgZXhhbXBsZXMgb2Ygc29tZSBwb3NzaWJsZSBhcHByb2FjaGVzOgoKYGBge3Igc2F2ZV9jdXN0b21fcGNhLCBldmFsID0gRkFMU0V9CnBkZihmaWxlID0gZmlsZS5wYXRoKCdvdXRwdXRzJywgJ2ZpZ3VyZXMnLCAnUENBX3Jsb2dfVGl0bGVkLnBkZicpLCB3aWR0aCA9IDYsIGhlaWdodCA9IDYpClBDQUN1c3RvbTMKZGV2Lm9mZigpCgpnZ3NhdmUoCiAgICBmaWxlbmFtZSA9IGZpbGUucGF0aCgnb3V0cHV0cycsICdmaWd1cmVzJywgJ1BDQV9ybG9nX1RpdGxlZC5wZGYnKSwKICAgIHBsb3QgPSBQQ0FDdXN0b20zLAogICAgd2lkdGggPSA2LCBoZWlnaHQgPSA2LCB1bml0cyA9ICdpbicpCmBgYAoKPC9kZXRhaWxzPgo8YnI+CgoKYGBge3IgV3JpdGVPdXQuUkRhdGEsIGV2YWw9RkFMU0UsIGVjaG89RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiNIaWRkZW4gY29kZSBibG9jayB0byB3cml0ZSBvdXQgZGF0YSBmb3Iga25pdHRpbmcKIyBzYXZlLmltYWdlKGZpbGUgPSAicmRhdGEvUnVubmluZ0RhdGEuUkRhdGEiKQpgYGAK