Data Science Stream
Preparation
library(palmerpenguins)
library(plotly)
R code is provided in the code chunk below.
penguins_scatter <- plot_ly(data = penguins,
x = ~body_mass_g, y = ~flipper_length_mm,
color = ~sex, colors = "Set1",
type = "scatter", mode = "markers")
penguins_scatter <- penguins_scatter %>%
layout(title = "Scatter Plot of Penguin Data",
legend=list(title=list(text='Sex')),
xaxis = list(title = "Penguin Body Mass (grams)"),
yaxis = list(title = "Penguin Flipper Length (mm)"))
Adding Range Sliders to plotly
Plots
penguins_scatter %>% rangeslider()
Creating animated plotly
Plots in RStudio
Example code and output is shown below:
penguins_scatter_anim <- plot_ly(data = penguins,
x = ~body_mass_g, y = ~flipper_length_mm,
frame = ~year,
color = ~sex, colors = "Set1",
type = "scatter", mode = "markers")
penguins_scatter_anim <- penguins_scatter_anim %>%
layout(title = "Animated Scatter Plot of Penguin Data",
legend=list(title=list(text='Sex')),
xaxis = list(title = "Penguin Body Mass (grams)"),
yaxis = list(title = "Penguin Flipper Length (mm)"))
penguins_scatter_anim
Example code and output is shown below:
penguins_scatter_anim2 <- plot_ly(data = penguins,
x = ~body_mass_g, y = ~flipper_length_mm,
frame = ~species, text = ~year,
color = ~sex, colors = "Set1",
type = "scatter", mode = "markers")
penguins_scatter_anim2 <- penguins_scatter_anim2 %>%
layout(title = "Animated Scatter Plot of Penguin Data",
legend=list(title=list(text='Sex')),
xaxis = list(title = "Penguin Body Mass (grams)"),
yaxis = list(title = "Penguin Flipper Length (mm)"))
penguins_scatter_anim2
We notice that the Adelie and Chinstrap penguins have a similar range of body_mass_g
values, while the Gentoo penguins are typically much heavier.
Creating Combined plotly
Plots in RStudio
R code is provided in the code chunk below.
penguin_hist <- plot_ly(data = penguins,
x = ~body_mass_g,
color = ~island,
type = "histogram", alpha = 0.6)
penguin_hist <- penguin_hist %>% layout(yaxis = list(title = 'count'),
barmode ="overlay")
penguin_combined_plots <- subplot(penguins_scatter, penguin_hist,
nrows = 2, margin = 0.05)
penguin_combined_plots <- penguin_combined_plots %>%
layout(title = "Palmer Penguin Data",
xaxis = list(title = 'body_mass_g'),
yaxis = list(title = "flipper_length_mm"),
xaxis2 = list(title = 'body_mass_g'),
yaxis2 = list(title = "count"))
penguin_combined_plots
Example code and output is shown below:
penguin_combined_plots %>% layout(legend = list(title = list(
text ="Sex and Island Information")))
Example code and output is shown below:
penguin_box <- plot_ly(data = penguins,
x = ~species,
y = ~body_mass_g,
color = ~sex, colors = "Set1",
type = "box") %>% layout(boxmode = "group")
penguin_combined_plots2 <- subplot(penguins_scatter,
penguin_hist,
penguin_box,
nrows = 3, margin = 0.05)
penguin_combined_plots2 <- penguin_combined_plots2 %>%
layout(title = "Palmer Penguin Data",
legend = list(title = list(
text ="Sex and Island Information")),
xaxis = list(title = 'body_mass_g'),
yaxis = list(title = "flipper_length_mm"),
xaxis2 = list(title = 'body_mass_g'),
yaxis2 = list(title = "count"),
xaxis3 = list(title = 'species'),
yaxis3 = list(title = "body_mass_g"))
penguin_combined_plots2
Note that the colours have been specified to match those in the scatter plot, for consistency.
Extension: Adding Buttons to plotly
Plots
Example code and output is shown below:
penguins_plots <- plot_ly(data = penguins,
x = ~body_mass_g,
color = ~sex,
colors = "Set1",
opacity = 0.6) %>%
layout(
title = "Penguin's Body Mass Data",
updatemenus = list(
list(x = 1.2, y = 0.7, type = "dropdown",
# We really just have to focus on the code below
buttons = list(
list(method = "restyle",
label = "Histogram", # The button label
args = list(
list(type = list("histogram")))), # The plot type
list(method = "restyle",
label = "Box Plots",
args = list(
list(type = list("box"))))
))))
penguins_plots
penguins_plots %>% layout(barmode ="overlay")
Example code and output is shown below:
penguins_plots <- plot_ly(data = penguins,
x = ~body_mass_g,
color = ~sex,
colors = "Set1",
opacity = 0.6) %>%
layout(
title = "Penguin's Body Mass Data",
updatemenus = list(
list(x = 1.2, y = 0.7, type = "dropdown",
# We really just have to focus on the code below
buttons = list(
list(method = "restyle",
label = "Histogram", # The button label
args = list(
list(type = list("histogram")))), # The plot type
list(method = "restyle",
label = "Box Plots",
args = list(
list(type = list("box")))),
list(method = "restyle",
label = "Violin Plots",
args = list(
list(type = list("violin"),
box = list(visible = T ))))
))))
penguins_plots %>% layout(barmode ="overlay")
Example code and output is shown below:
penguins_plots2 <- plot_ly(data = penguins,
x = ~bill_length_mm,
color = ~species,
colors = "Set1",
opacity = 0.6) %>%
layout(
title = "Penguin's Bill Length Data",
updatemenus = list(
list(x = 1.2, y = 0.7, type = "dropdown",
# We really just have to focus on the code below
buttons = list(
list(method = "restyle",
label = "Violin Plots",
args = list(
list(type = list("violin"),
box = list(visible = T )))),
list(method = "restyle",
label = "Box Plots",
args = list(
list(type = list("box")))),
list(method = "restyle",
label = "Histogram",
args = list(
list(type = list("histogram"))))
))))
penguins_plots2 %>% layout(barmode ="overlay") %>% rangeslider()
No answer required.
That’s everything covered, well done!
References
Horst, Allison Marie, Alison Presmanes Hill, and Kristen B Gorman. 2020.
Palmerpenguins: Palmer Archipelago (Antarctica) Penguin Data.
https://doi.org/10.5281/zenodo.3960218.
Sievert, Carson. 2020.
Interactive Web-Based Data Visualization with r, Plotly, and Shiny. Chapman; Hall/CRC.
https://plotly-r.com.
These notes have been prepared by Rupert Kuveke. The copyright for the material in these notes resides with the author named above, with the Department of Mathematical and Physical Sciences and with La Trobe University. Copyright in this work is vested in La Trobe University including all La Trobe University branding and naming. Unless otherwise stated, material within this work is licensed under a Creative Commons Attribution-Non Commercial-Non Derivatives License
BY-NC-ND.
LS0tDQp0aXRsZTogIlNUTTEwMDE6IENvbXB1dGVyIExhYiA0QiBTb2x1dGlvbnMiDQpvdXRwdXQ6DQogIGJvb2tkb3duOjpodG1sX2RvY3VtZW50MjogDQogICAgdG9jOiB0cnVlDQogICAgdG9jX2Zsb2F0OiB0cnVlDQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KICAgIHRoZW1lOiByZWFkYWJsZQ0KICAgIGNvZGVfZm9sZGluZzogc2hvdw0KYmlibGlvZ3JhcGh5OiBTVE0xMDAxX0RTX0NMX3JlZmVyZW5jZXMuYmliIA0KbGluay1jaXRhdGlvbnM6IHllcw0KLS0tDQoNCjxzdHlsZT4NCiNUT0Mgew0KICBiYWNrZ3JvdW5kOiB1cmwoImh0dHBzOi8vd3d3LmxhdHJvYmUuZWR1LmF1L19tZWRpYS9sYS10cm9iZS1hcGkvdjUvaW1nL2xvZ28uc3ZnIik7DQogIGJhY2tncm91bmQtc2l6ZTogY29udGFpbjsNCiAgcGFkZGluZy10b3A6IDgwcHggIWltcG9ydGFudDsNCiAgYmFja2dyb3VuZC1yZXBlYXQ6IG5vLXJlcGVhdDsNCn0NCjwvc3R5bGU+DQoNCiMjIyBEYXRhIFNjaWVuY2UgU3RyZWFtIHstfQ0KDQojIyMgVG9waWMgNEI6IERhdGEgVmlzdWFsaXNhdGlvbiBJSUkgey19DQoNCjxicj4NCg0KRXhhbXBsZSBSIGNvZGUgc29sdXRpb25zIGZvciB0aGUgW0RhdGEgU2NpZW5jZSBDb21wdXRlciBMYWIgNF0oaHR0cHM6Ly9ycHVicy5jb20vTFRVX1NUTTEwMDEvRFNNQ0w0KSwgd2hpY2ggdXNlcyBkYXRhIGZyb20gdGhlIGBwYWxtZXJwZW5ndWluc2AgcGFja2FnZSBbQHBlbmd1aW5zXSwgYW5kIHRoZSBgcGxvdGx5YCBbQHBsb3RseV0sIGFyZSBwcmVzZW50ZWQgYmVsb3cuDQoNClRoaXMgY29tcHV0ZXIgbGFiIHdhcyBkZXNpZ25lZCB0byBydW4gYWxvbmdzaWRlIHRoZSBjb250ZW50IGluIFtTZWN0aW9uIDUgb2YgdGhlIERhdGEgVmlzdWFsaXNhdGlvbiBpbiBSIHN1cHBsZW1lbnRdKGh0dHBzOi8vYm9va2Rvd24ub3JnL3JlaGsvc3RtMTAwMV9kc21fZGF0YV92aXN1YWxpc2F0aW9uX2luX3IvYWR2YW5jZWQtcGxvdGx5LWNvbXB1dGVyLWxhYi00Yi5odG1sKS4gSXQgbWlnaHQgYmUgaGVscGZ1bCB0byBoYXZlIHRoaXMgY29udGVudCBvcGVuIHdoaWxlIHlvdSBsb29rIHRocm91Z2ggdGhlc2Ugc29sdXRpb25zLg0KDQojIFByZXBhcmF0aW9uDQoNCiMjDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gRiwgZWNobyA9IFR9DQpsaWJyYXJ5KHBhbG1lcnBlbmd1aW5zKQ0KbGlicmFyeShwbG90bHkpDQpgYGANCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBULCBpbmNsdWRlID0gRn0NCmxpYnJhcnkocGFsbWVycGVuZ3VpbnMpDQpsaWJyYXJ5KHBsb3RseSkNCmBgYA0KDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gVCwgaW5jbHVkZSA9IEZ9DQpwZW5ndWlucyA8LSBuYS5vbWl0KHBlbmd1aW5zKQ0KYGBgDQoNCiMjDQoNClIgY29kZSBpcyBwcm92aWRlZCBpbiB0aGUgY29kZSBjaHVuayBiZWxvdy4NCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtaGlkZSIsIGV2YWwgPSBULCBlY2hvID0gVH0NCnBlbmd1aW5zX3NjYXR0ZXIgPC0gcGxvdF9seShkYXRhID0gcGVuZ3VpbnMsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIHggPSB+Ym9keV9tYXNzX2csIHkgPSB+ZmxpcHBlcl9sZW5ndGhfbW0sIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gfnNleCwgY29sb3JzID0gIlNldDEiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAic2NhdHRlciIsIG1vZGUgPSAibWFya2VycyIpDQoNCnBlbmd1aW5zX3NjYXR0ZXIgPC0gcGVuZ3VpbnNfc2NhdHRlciAlPiUgDQogICAgICAgICAgICAgICAgICAgICAgICBsYXlvdXQodGl0bGUgPSAiU2NhdHRlciBQbG90IG9mIFBlbmd1aW4gRGF0YSIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZD1saXN0KHRpdGxlPWxpc3QodGV4dD0nU2V4JykpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHhheGlzID0gbGlzdCh0aXRsZSA9ICJQZW5ndWluIEJvZHkgTWFzcyAoZ3JhbXMpIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeWF4aXMgPSBsaXN0KHRpdGxlID0gIlBlbmd1aW4gRmxpcHBlciBMZW5ndGggKG1tKSIpKQ0KYGBgDQoNCiMgQWRkaW5nIFJhbmdlIFNsaWRlcnMgdG8gYHBsb3RseWAgUGxvdHMgeyNzbGlkZXJ9IA0KDQojIyB7I3NsaWRlcnN0YXJ0fQ0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IFQsIGVjaG8gPSBUfQ0KcGVuZ3VpbnNfc2NhdHRlciAlPiUgcmFuZ2VzbGlkZXIoKQ0KYGBgDQoNCiMjDQoNCk5vIGFuc3dlciByZXF1aXJlZC4NCg0KIyMNCg0KTm8gYW5zd2VyIHJlcXVpcmVkLg0KDQojIENyZWF0aW5nIGFuaW1hdGVkIGBwbG90bHlgIFBsb3RzIGluIFJTdHVkaW8geyNhbmltYXRpb25zfQ0KDQojIw0KDQpFeGFtcGxlIGNvZGUgYW5kIG91dHB1dCBpcyBzaG93biBiZWxvdzoNCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBULCBlY2hvID0gVH0NCnBlbmd1aW5zX3NjYXR0ZXJfYW5pbSA8LSBwbG90X2x5KGRhdGEgPSBwZW5ndWlucywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgeCA9IH5ib2R5X21hc3NfZywgeSA9IH5mbGlwcGVyX2xlbmd0aF9tbSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgZnJhbWUgPSB+eWVhciwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IH5zZXgsIGNvbG9ycyA9ICJTZXQxIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlID0gInNjYXR0ZXIiLCBtb2RlID0gIm1hcmtlcnMiKQ0KDQpwZW5ndWluc19zY2F0dGVyX2FuaW0gPC0gcGVuZ3VpbnNfc2NhdHRlcl9hbmltICU+JSANCiAgICAgICAgICAgICAgICAgICAgICAgIGxheW91dCh0aXRsZSA9ICJBbmltYXRlZCBTY2F0dGVyIFBsb3Qgb2YgUGVuZ3VpbiBEYXRhIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kPWxpc3QodGl0bGU9bGlzdCh0ZXh0PSdTZXgnKSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeGF4aXMgPSBsaXN0KHRpdGxlID0gIlBlbmd1aW4gQm9keSBNYXNzIChncmFtcykiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAiUGVuZ3VpbiBGbGlwcGVyIExlbmd0aCAobW0pIikpDQoNCnBlbmd1aW5zX3NjYXR0ZXJfYW5pbQ0KYGBgDQoNCiMjDQoNCk5vIGFuc3dlciByZXF1aXJlZC4NCg0KIyMNCg0KRXhhbXBsZSBjb2RlIGFuZCBvdXRwdXQgaXMgc2hvd24gYmVsb3c6DQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gVCwgZWNobyA9IFR9DQpwZW5ndWluc19zY2F0dGVyX2FuaW0yIDwtIHBsb3RfbHkoZGF0YSA9IHBlbmd1aW5zLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB4ID0gfmJvZHlfbWFzc19nLCB5ID0gfmZsaXBwZXJfbGVuZ3RoX21tLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBmcmFtZSA9IH5zcGVjaWVzLCB0ZXh0ID0gfnllYXIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSB+c2V4LCBjb2xvcnMgPSAiU2V0MSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZSA9ICJzY2F0dGVyIiwgbW9kZSA9ICJtYXJrZXJzIikNCg0KcGVuZ3VpbnNfc2NhdHRlcl9hbmltMiA8LSBwZW5ndWluc19zY2F0dGVyX2FuaW0yICU+JSANCiAgICAgICAgICAgICAgICAgICAgICAgIGxheW91dCh0aXRsZSA9ICJBbmltYXRlZCBTY2F0dGVyIFBsb3Qgb2YgUGVuZ3VpbiBEYXRhIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kPWxpc3QodGl0bGU9bGlzdCh0ZXh0PSdTZXgnKSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeGF4aXMgPSBsaXN0KHRpdGxlID0gIlBlbmd1aW4gQm9keSBNYXNzIChncmFtcykiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAiUGVuZ3VpbiBGbGlwcGVyIExlbmd0aCAobW0pIikpDQoNCnBlbmd1aW5zX3NjYXR0ZXJfYW5pbTINCmBgYA0KDQpXZSBub3RpY2UgdGhhdCB0aGUgQWRlbGllIGFuZCBDaGluc3RyYXAgcGVuZ3VpbnMgaGF2ZSBhIHNpbWlsYXIgcmFuZ2Ugb2YgYGJvZHlfbWFzc19nYCB2YWx1ZXMsIHdoaWxlIHRoZSBHZW50b28gcGVuZ3VpbnMgYXJlIHR5cGljYWxseSBtdWNoIGhlYXZpZXIuDQoNCiMgQ3JlYXRpbmcgQ29tYmluZWQgYHBsb3RseWAgUGxvdHMgaW4gUlN0dWRpbyB7I3N1YnBsb3RzfQ0KDQojIyB7I3N1YnBsb3R3YWxrdGhyb3VnaH0NCg0KUiBjb2RlIGlzIHByb3ZpZGVkIGluIHRoZSBjb2RlIGNodW5rIGJlbG93Lg0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIiwgZXZhbCA9IFQsIGVjaG8gPSBULCB3YXJuaW5nID0gRiwgbWVzc2FnZSA9IEYsIGZpZy5hbGlnbiA9ICJjZW50ZXIifQ0KcGVuZ3Vpbl9oaXN0IDwtIHBsb3RfbHkoZGF0YSA9IHBlbmd1aW5zLCANCiAgICAgICAgICAgICAgICAgICAgICAgIHggPSB+Ym9keV9tYXNzX2csIA0KICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSB+aXNsYW5kLCANCiAgICAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAiaGlzdG9ncmFtIiwgYWxwaGEgPSAwLjYpDQoNCnBlbmd1aW5faGlzdCA8LSBwZW5ndWluX2hpc3QgJT4lIGxheW91dCh5YXhpcyA9IGxpc3QodGl0bGUgPSAnY291bnQnKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYmFybW9kZSA9Im92ZXJsYXkiKQ0KYGBgDQoNCiMjIHsjY29tYmluZWR9DQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gVCwgZWNobyA9IFQsIHdhcm5pbmcgPSBGLCBtZXNzYWdlID0gRiwgZmlnLmFsaWduID0gImNlbnRlciIsIGZpZy5kaW0gPSBjKDgsIDgpfQ0KcGVuZ3Vpbl9jb21iaW5lZF9wbG90cyA8LSBzdWJwbG90KHBlbmd1aW5zX3NjYXR0ZXIsIHBlbmd1aW5faGlzdCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbnJvd3MgPSAyLCBtYXJnaW4gPSAwLjA1KSANCnBlbmd1aW5fY29tYmluZWRfcGxvdHMgPC0gcGVuZ3Vpbl9jb21iaW5lZF9wbG90cyAlPiUgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgbGF5b3V0KHRpdGxlID0gIlBhbG1lciBQZW5ndWluIERhdGEiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB4YXhpcyA9IGxpc3QodGl0bGUgPSAnYm9keV9tYXNzX2cnKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHlheGlzID0gbGlzdCh0aXRsZSA9ICJmbGlwcGVyX2xlbmd0aF9tbSIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB4YXhpczIgPSBsaXN0KHRpdGxlID0gJ2JvZHlfbWFzc19nJyksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5YXhpczIgPSBsaXN0KHRpdGxlID0gImNvdW50IikpDQpwZW5ndWluX2NvbWJpbmVkX3Bsb3RzDQpgYGANCg0KIyMNCg0KRXhhbXBsZSBjb2RlIGFuZCBvdXRwdXQgaXMgc2hvd24gYmVsb3c6DQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gVCwgZWNobyA9IFQsIHdhcm5pbmcgPSBGLCBtZXNzYWdlID0gRiwgZmlnLmFsaWduID0gImNlbnRlciIsIGZpZy5kaW0gPSBjKDgsIDgpfQ0KcGVuZ3Vpbl9jb21iaW5lZF9wbG90cyAlPiUgbGF5b3V0KGxlZ2VuZCA9IGxpc3QodGl0bGUgPSBsaXN0KA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGV4dCA9IlNleCBhbmQgSXNsYW5kIEluZm9ybWF0aW9uIikpKQ0KYGBgDQoNCiMjIHsjYm94cGxvdHN1YnBsb3R9DQoNCkV4YW1wbGUgY29kZSBhbmQgb3V0cHV0IGlzIHNob3duIGJlbG93Og0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IFQsIGVjaG8gPSBULCB3YXJuaW5nID0gRiwgbWVzc2FnZSA9IEYsIGZpZy5hbGlnbiA9ICJjZW50ZXIiLCBmaWcuZGltID0gYyg4LCAxMil9DQoNCnBlbmd1aW5fYm94IDwtIHBsb3RfbHkoZGF0YSA9IHBlbmd1aW5zLCANCiAgICAgICAgICAgICAgICAgICAgICAgIHggPSB+c3BlY2llcywNCiAgICAgICAgICAgICAgICAgICAgICAgeSA9IH5ib2R5X21hc3NfZywNCiAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gfnNleCwgY29sb3JzID0gIlNldDEiLA0KICAgICAgICAgICAgICAgICAgICAgICAgdHlwZSA9ICJib3giKSAlPiUgbGF5b3V0KGJveG1vZGUgPSAiZ3JvdXAiKQ0KDQpwZW5ndWluX2NvbWJpbmVkX3Bsb3RzMiA8LSBzdWJwbG90KHBlbmd1aW5zX3NjYXR0ZXIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwZW5ndWluX2hpc3QsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwZW5ndWluX2JveCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBucm93cyA9IDMsIG1hcmdpbiA9IDAuMDUpIA0KcGVuZ3Vpbl9jb21iaW5lZF9wbG90czIgPC0gcGVuZ3Vpbl9jb21iaW5lZF9wbG90czIgJT4lIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxheW91dCh0aXRsZSA9ICJQYWxtZXIgUGVuZ3VpbiBEYXRhIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kID0gbGlzdCh0aXRsZSA9IGxpc3QoDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0ZXh0ID0iU2V4IGFuZCBJc2xhbmQgSW5mb3JtYXRpb24iKSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHhheGlzID0gbGlzdCh0aXRsZSA9ICdib2R5X21hc3NfZycpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeWF4aXMgPSBsaXN0KHRpdGxlID0gImZsaXBwZXJfbGVuZ3RoX21tIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHhheGlzMiA9IGxpc3QodGl0bGUgPSAnYm9keV9tYXNzX2cnKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHlheGlzMiA9IGxpc3QodGl0bGUgPSAiY291bnQiKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB4YXhpczMgPSBsaXN0KHRpdGxlID0gJ3NwZWNpZXMnKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHlheGlzMyA9IGxpc3QodGl0bGUgPSAiYm9keV9tYXNzX2ciKSkNCg0KcGVuZ3Vpbl9jb21iaW5lZF9wbG90czIgDQpgYGANCg0KTm90ZSB0aGF0IHRoZSBjb2xvdXJzIGhhdmUgYmVlbiBzcGVjaWZpZWQgdG8gbWF0Y2ggdGhvc2UgaW4gdGhlIHNjYXR0ZXIgcGxvdCwgZm9yIGNvbnNpc3RlbmN5Lg0KDQojIEV4dGVuc2lvbjogQWRkaW5nIEJ1dHRvbnMgdG8gYHBsb3RseWAgUGxvdHMgeyNidXR0b259DQoNCiMjDQoNCk5vIGFuc3dlciByZXF1aXJlZC4NCg0KIyMgeyNidXR0b25zcGxvdH0NCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBULCBlY2hvID0gVCwgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGfQ0KcGVuZ3VpbnNfcGxvdHMgPC0gcGxvdF9seShkYXRhID0gcGVuZ3VpbnMsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICB4ID0gfmJvZHlfbWFzc19nLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSB+c2V4LCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3JzID0gIlNldDEiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgb3BhY2l0eSA9IDAuNikgJT4lIA0KICBsYXlvdXQoDQogICAgdGl0bGUgPSAiUGVuZ3VpbidzIEJvZHkgTWFzcyBEYXRhIiwNCiAgICB1cGRhdGVtZW51cyA9IGxpc3QoDQogICAgICBsaXN0KHggPSAxLjIsIHkgPSAwLjcsIHR5cGUgPSAiYnV0dG9ucyIsIA0KICAgICAgICAgDQogICAgICAgICAgICMgV2UgcmVhbGx5IGp1c3QgaGF2ZSB0byBmb2N1cyBvbiB0aGUgY29kZSBiZWxvdyAgIA0KICAgICAgICAgYnV0dG9ucyA9IGxpc3QoDQogICAgICAgICAgIA0KICAgICAgICAgIGxpc3QobWV0aG9kID0gInJlc3R5bGUiLA0KICAgICAgICAgICAgICAgbGFiZWwgPSAiSGlzdG9ncmFtIiwgIyBUaGUgYnV0dG9uIGxhYmVsDQogICAgICAgICAgICAgICBhcmdzID0gbGlzdCgNCiAgICAgICAgICAgICAgICBsaXN0KHR5cGUgPSBsaXN0KCJoaXN0b2dyYW0iKSkpKSAjIFRoZSBwbG90IHR5cGUNCiAgICApKSkpDQoNCnBlbmd1aW5zX3Bsb3RzDQpgYGANCiANCiMjIHsjYWRkYnV0dG9ufQ0KDQpFeGFtcGxlIGNvZGUgYW5kIG91dHB1dCBpcyBzaG93biBiZWxvdzoNCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBULCBlY2hvID0gVCwgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGfQ0KcGVuZ3VpbnNfcGxvdHMgPC0gcGxvdF9seShkYXRhID0gcGVuZ3VpbnMsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICB4ID0gfmJvZHlfbWFzc19nLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSB+c2V4LCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3JzID0gIlNldDEiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgb3BhY2l0eSA9IDAuNikgJT4lIA0KICBsYXlvdXQoDQogICAgdGl0bGUgPSAiUGVuZ3VpbidzIEJvZHkgTWFzcyBEYXRhIiwNCiAgICB1cGRhdGVtZW51cyA9IGxpc3QoDQogICAgICBsaXN0KHggPSAxLjIsIHkgPSAwLjcsIHR5cGUgPSAiYnV0dG9ucyIsIA0KICAgICAgICAgICANCiAgICAgICAgICMgV2UgcmVhbGx5IGp1c3QgaGF2ZSB0byBmb2N1cyBvbiB0aGUgY29kZSBiZWxvdyAgIA0KICAgICAgICAgYnV0dG9ucyA9IGxpc3QoDQogICAgICAgICAgIA0KICAgICAgICAgIGxpc3QobWV0aG9kID0gInJlc3R5bGUiLA0KICAgICAgICAgICAgICAgbGFiZWwgPSAiSGlzdG9ncmFtIiwgIyBUaGUgYnV0dG9uIGxhYmVsDQogICAgICAgICAgICAgICBhcmdzID0gbGlzdCgNCiAgICAgICAgICAgICAgICBsaXN0KHR5cGUgPSBsaXN0KCJoaXN0b2dyYW0iKSkpKSwgIyBUaGUgcGxvdCB0eXBlDQogICAgICAgICAgDQogICAgICAgICAgbGlzdChtZXRob2QgPSAicmVzdHlsZSIsDQogICAgICAgICAgICAgICBsYWJlbCA9ICJCb3ggUGxvdHMiLA0KICAgICAgICAgICAgICAgYXJncyA9IGxpc3QoDQogICAgICAgICAgICAgICAgbGlzdCh0eXBlID0gbGlzdCgiYm94IikpKSkNCiAgICApKSkpDQoNCnBlbmd1aW5zX3Bsb3RzIA0KYGBgDQoNCiMjIHsjdHdvYnV0dG9uc30NCg0KTm8gYW5zd2VyIHJlcXVpcmVkLg0KDQojIw0KDQpFeGFtcGxlIGNvZGUgYW5kIG91dHB1dCBpcyBzaG93biBiZWxvdzoNCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBULCBlY2hvID0gVCwgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGfQ0KcGVuZ3VpbnNfcGxvdHMgPC0gcGxvdF9seShkYXRhID0gcGVuZ3VpbnMsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICB4ID0gfmJvZHlfbWFzc19nLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSB+c2V4LCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3JzID0gIlNldDEiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgb3BhY2l0eSA9IDAuNikgJT4lIA0KICBsYXlvdXQoDQogICAgdGl0bGUgPSAiUGVuZ3VpbidzIEJvZHkgTWFzcyBEYXRhIiwNCiAgICB1cGRhdGVtZW51cyA9IGxpc3QoDQogICAgICBsaXN0KHggPSAxLjIsIHkgPSAwLjcsIHR5cGUgPSAiZHJvcGRvd24iLCANCiAgICAgICAgICAgDQogICAgICAgICAjIFdlIHJlYWxseSBqdXN0IGhhdmUgdG8gZm9jdXMgb24gdGhlIGNvZGUgYmVsb3cgICANCiAgICAgICAgIGJ1dHRvbnMgPSBsaXN0KA0KICAgICAgICAgICANCiAgICAgICAgICBsaXN0KG1ldGhvZCA9ICJyZXN0eWxlIiwNCiAgICAgICAgICAgICAgIGxhYmVsID0gIkhpc3RvZ3JhbSIsICMgVGhlIGJ1dHRvbiBsYWJlbA0KICAgICAgICAgICAgICAgYXJncyA9IGxpc3QoDQogICAgICAgICAgICAgICAgbGlzdCh0eXBlID0gbGlzdCgiaGlzdG9ncmFtIikpKSksICMgVGhlIHBsb3QgdHlwZQ0KICAgICAgICAgIA0KICAgICAgICAgIGxpc3QobWV0aG9kID0gInJlc3R5bGUiLA0KICAgICAgICAgICAgICAgbGFiZWwgPSAiQm94IFBsb3RzIiwNCiAgICAgICAgICAgICAgIGFyZ3MgPSBsaXN0KA0KICAgICAgICAgICAgICAgIGxpc3QodHlwZSA9IGxpc3QoImJveCIpKSkpDQogICAgKSkpKQ0KDQpwZW5ndWluc19wbG90cyANCmBgYA0KDQojIw0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IFQsIGVjaG8gPSBULCB3YXJuaW5nID0gRiwgbWVzc2FnZSA9IEZ9DQpwZW5ndWluc19wbG90cyAlPiUgbGF5b3V0KGJhcm1vZGUgPSJvdmVybGF5IikNCmBgYA0KDQojIw0KDQpFeGFtcGxlIGNvZGUgYW5kIG91dHB1dCBpcyBzaG93biBiZWxvdzoNCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBULCBlY2hvID0gVCwgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGfQ0KcGVuZ3VpbnNfcGxvdHMgPC0gcGxvdF9seShkYXRhID0gcGVuZ3VpbnMsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICB4ID0gfmJvZHlfbWFzc19nLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSB+c2V4LCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3JzID0gIlNldDEiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgb3BhY2l0eSA9IDAuNikgJT4lIA0KICBsYXlvdXQoDQogICAgdGl0bGUgPSAiUGVuZ3VpbidzIEJvZHkgTWFzcyBEYXRhIiwNCiAgICB1cGRhdGVtZW51cyA9IGxpc3QoDQogICAgICBsaXN0KHggPSAxLjIsIHkgPSAwLjcsIHR5cGUgPSAiZHJvcGRvd24iLCANCiAgICAgICAgICAgDQogICAgICAgICAjIFdlIHJlYWxseSBqdXN0IGhhdmUgdG8gZm9jdXMgb24gdGhlIGNvZGUgYmVsb3cgICANCiAgICAgICAgIGJ1dHRvbnMgPSBsaXN0KA0KICAgICAgICAgICANCiAgICAgICAgICBsaXN0KG1ldGhvZCA9ICJyZXN0eWxlIiwNCiAgICAgICAgICAgICAgIGxhYmVsID0gIkhpc3RvZ3JhbSIsICMgVGhlIGJ1dHRvbiBsYWJlbA0KICAgICAgICAgICAgICAgYXJncyA9IGxpc3QoDQogICAgICAgICAgICAgICAgbGlzdCh0eXBlID0gbGlzdCgiaGlzdG9ncmFtIikpKSksICMgVGhlIHBsb3QgdHlwZQ0KICAgICAgICAgIA0KICAgICAgICAgIGxpc3QobWV0aG9kID0gInJlc3R5bGUiLA0KICAgICAgICAgICAgICAgbGFiZWwgPSAiQm94IFBsb3RzIiwNCiAgICAgICAgICAgICAgIGFyZ3MgPSBsaXN0KA0KICAgICAgICAgICAgICAgIGxpc3QodHlwZSA9IGxpc3QoImJveCIpKSkpLA0KICAgICAgICAgIA0KICAgICAgICAgIGxpc3QobWV0aG9kID0gInJlc3R5bGUiLA0KICAgICAgICAgICAgICAgbGFiZWwgPSAiVmlvbGluIFBsb3RzIiwNCiAgICAgICAgICAgICAgIGFyZ3MgPSBsaXN0KA0KICAgICAgICAgICAgICAgIGxpc3QodHlwZSA9IGxpc3QoInZpb2xpbiIpLA0KICAgICAgICAgICAgICAgICAgICAgYm94ID0gbGlzdCh2aXNpYmxlID0gVCApKSkpDQogICAgKSkpKQ0KDQpwZW5ndWluc19wbG90cyAlPiUgbGF5b3V0KGJhcm1vZGUgPSJvdmVybGF5IikNCmBgYA0KDQojIw0KDQpFeGFtcGxlIGNvZGUgYW5kIG91dHB1dCBpcyBzaG93biBiZWxvdzoNCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBULCBlY2hvID0gVCwgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGfQ0KcGVuZ3VpbnNfcGxvdHMyIDwtIHBsb3RfbHkoZGF0YSA9IHBlbmd1aW5zLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHggPSB+YmlsbF9sZW5ndGhfbW0sIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSB+c3BlY2llcywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvcnMgPSAiU2V0MSIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgb3BhY2l0eSA9IDAuNikgJT4lIA0KICBsYXlvdXQoDQogICAgdGl0bGUgPSAiUGVuZ3VpbidzIEJpbGwgTGVuZ3RoIERhdGEiLA0KICAgIHVwZGF0ZW1lbnVzID0gbGlzdCgNCiAgICAgIGxpc3QoeCA9IDEuMiwgeSA9IDAuNywgdHlwZSA9ICJkcm9wZG93biIsIA0KICAgICAgICAgICANCiAgICAgICAgICMgV2UgcmVhbGx5IGp1c3QgaGF2ZSB0byBmb2N1cyBvbiB0aGUgY29kZSBiZWxvdyAgIA0KICAgICAgICAgYnV0dG9ucyA9IGxpc3QoDQogICAgICAgICAgIA0KICAgICAgICAgIGxpc3QobWV0aG9kID0gInJlc3R5bGUiLA0KICAgICAgICAgICAgICAgbGFiZWwgPSAiVmlvbGluIFBsb3RzIiwNCiAgICAgICAgICAgICAgIGFyZ3MgPSBsaXN0KA0KICAgICAgICAgICAgICAgIGxpc3QodHlwZSA9IGxpc3QoInZpb2xpbiIpLA0KICAgICAgICAgICAgICAgICAgICAgYm94ID0gbGlzdCh2aXNpYmxlID0gVCApKSkpLA0KICAgICAgICAgIA0KICAgICAgICAgIGxpc3QobWV0aG9kID0gInJlc3R5bGUiLA0KICAgICAgICAgICAgICAgbGFiZWwgPSAiQm94IFBsb3RzIiwNCiAgICAgICAgICAgICAgIGFyZ3MgPSBsaXN0KA0KICAgICAgICAgICAgICAgIGxpc3QodHlwZSA9IGxpc3QoImJveCIpKSkpLA0KICAgICAgICAgIA0KICAgICAgICAgIGxpc3QobWV0aG9kID0gInJlc3R5bGUiLA0KICAgICAgICAgICAgICAgbGFiZWwgPSAiSGlzdG9ncmFtIiwgDQogICAgICAgICAgICAgICBhcmdzID0gbGlzdCgNCiAgICAgICAgICAgICAgICBsaXN0KHR5cGUgPSBsaXN0KCJoaXN0b2dyYW0iKSkpKQ0KICAgICkpKSkNCg0KcGVuZ3VpbnNfcGxvdHMyICU+JSBsYXlvdXQoYmFybW9kZSA9Im92ZXJsYXkiKSAlPiUgcmFuZ2VzbGlkZXIoKQ0KYGBgDQoNCiMjDQoNCk5vIGFuc3dlciByZXF1aXJlZC4NCg0KPGJyPg0KDQoNCiMjIyMgVGhhdCdzIGV2ZXJ5dGhpbmcgY292ZXJlZCwgd2VsbCBkb25lISAjIyMjIHstfQ0KDQo8YnI+DQoNCiMgUmVmZXJlbmNlcyB7LSAjUmVmfQ0KPGRpdiBpZD0icmVmcyI+PC9kaXY+DQoNCjxicj4NCg0KPGZvbnQgY29sb3IgPSAiZ3JleSI+DQpUaGVzZSBub3RlcyBoYXZlIGJlZW4gcHJlcGFyZWQgYnkgUnVwZXJ0IEt1dmVrZS4gVGhlIGNvcHlyaWdodCBmb3IgdGhlIG1hdGVyaWFsIGluIHRoZXNlIG5vdGVzIHJlc2lkZXMgd2l0aCB0aGUgYXV0aG9yIG5hbWVkIGFib3ZlLCB3aXRoIHRoZSBEZXBhcnRtZW50IG9mIE1hdGhlbWF0aWNhbCBhbmQgUGh5c2ljYWwgU2NpZW5jZXMgYW5kIHdpdGggTGEgVHJvYmUgVW5pdmVyc2l0eS4gQ29weXJpZ2h0IGluIHRoaXMgd29yayBpcyB2ZXN0ZWQgaW4gTGEgVHJvYmUgVW5pdmVyc2l0eSBpbmNsdWRpbmcgYWxsIExhIFRyb2JlIFVuaXZlcnNpdHkgYnJhbmRpbmcgYW5kIG5hbWluZy4gVW5sZXNzIG90aGVyd2lzZSBzdGF0ZWQsIG1hdGVyaWFsIHdpdGhpbiB0aGlzIHdvcmsgaXMgbGljZW5zZWQgdW5kZXIgYSBDcmVhdGl2ZSBDb21tb25zIEF0dHJpYnV0aW9uLU5vbiBDb21tZXJjaWFsLU5vbiBEZXJpdmF0aXZlcyBMaWNlbnNlIA0KPGEgaHJlZiA9ICJodHRwczovL2NyZWF0aXZlY29tbW9ucy5vcmcvbGljZW5zZXMvYnktbmMtbmQvNC4wL0NDIiB0YXJnZXQ9Il9ibGFuayI+IEJZLU5DLU5ELiA8L2E+DQo8L2ZvbnQ+