Some more plotly examples

Today we are going to discuss a number of additional examples of using plotly.

To get started, plotly uses htmlwidgets. Take a look at the htmlwidgets website and give a few examples a try.

plotly uses htmlwidgets. So we should become familiar with them.

Example 1:

Using leaflet to make maps.

library(leaflet)
m <- leaflet() %>%
  addTiles() %>%  # Add default OpenStreetMap map tiles
  addMarkers(lng=174.768, lat=-36.852, popup="The birthplace of R")
m  # Print the map

Go to google and ask for the location of CSU East Bay. Search for “CSU East Bay lat and long”

Answer: 37.6563° N, 122.0567° W

First try. What goes google give us?

m <- leaflet() %>%
  addTiles() %>%  # Add default OpenStreetMap map tiles
  addMarkers(lng=37.6563 , lat=122.0567, popup="CSU East Bay?")
m  # Print the map

That is totally wrong.

m <- leaflet() %>%
  addTiles() %>%  # Add default OpenStreetMap map tiles
  addMarkers(lng=122.0567, lat=37.6563, popup="CSU East Bay?")
m  # Print the map

Still wrong, but at least we are on the planet now.

m <- leaflet() %>%
  addTiles() %>%  # Add default OpenStreetMap map tiles
  addMarkers(lng=-122.0567, lat=37.6563, popup="CSU East Bay")
m  # Print the map

Here is a link to the google developer page about lat and long. Key thing is lat comes before long.

m <- leaflet() %>%
  addTiles() %>%  # Add default OpenStreetMap map tiles
  addMarkers(lat=37.6563, lng=-122.0567, popup="CSU East Bay")
m  # Print the map

Here is the link to Google Maps API.

Finally here is OpenStreetMap. This is where the maps come from for leaflet. Can you use googlemaps?

Example 2:

plotly is given as a example.

library(ggplot2)
library(plotly)
p <- ggplot(data = diamonds, aes(x = cut, fill = clarity)) +
            geom_bar(position = "dodge")
ggplotly(p)
d <- diamonds[sample(nrow(diamonds), 500), ]
plot_ly(d, x = d$carat, y = d$price, 
        text = paste("Clarity: ", d$clarity),
        mode = "markers", color = d$carat, size = d$carat)
No trace type specified:
  Based on info supplied, a 'scatter' trace seems appropriate.
  Read more about this trace type -> https://plot.ly/r/reference/#scatter
No trace type specified:
  Based on info supplied, a 'scatter' trace seems appropriate.
  Read more about this trace type -> https://plot.ly/r/reference/#scatter

Example 3.

The python ploting library rbokeh.

library(rbokeh)
figure() %>%
  ly_points(Sepal.Length, Sepal.Width, data = iris,
    color = Species, glyph = Species,
    hover = list(Sepal.Length, Sepal.Width))

Example 4:

visNetwork.

library(networkD3)
data(MisLinks, MisNodes)
forceNetwork(Links = MisLinks, Nodes = MisNodes, Source = "source",
             Target = "target", Value = "value", NodeID = "name",
             Group = "group", opacity = 0.4)

Example 5:

Making tables DT.

library(DT)
datatable(iris, options = list(pageLength = 5))

Example 6:

One more thing, flexdashboards which used shiny. Here is a one example geoms dash.

Back to plotly.

Example.

A case study of housing sales in Texas. See this page in the book.

This is a very nice case study. You should read the chapter and try all of the plots.

library(plotly)
txhousing
p <- ggplot(txhousing, aes(date, median)) +
  geom_line(aes(group = city), alpha = 0.2)
p

subplot(
  p, ggplotly(p, tooltip = "city"), 
  ggplot(txhousing, aes(date, median)) + geom_bin2d(),
  ggplot(txhousing, aes(date, median)) + geom_hex(),
  nrows = 2, shareX = TRUE, shareY = TRUE,
  titleY = FALSE, titleX = FALSE
)
Removed 616 rows containing non-finite values (stat_bin2d).Removed 616 rows containing non-finite values (stat_binhex).
library(dplyr)
tx <- group_by(txhousing, city)
# initiate a plotly object with date on x and median on y
p <- plot_ly(tx, x = ~date, y = ~median)
# plotly_data() returns data associated with a plotly object
plotly_data(p)
p
No trace type specified:
  Based on info supplied, a 'scatter' trace seems appropriate.
  Read more about this trace type -> https://plot.ly/r/reference/#scatter
No scatter mode specifed:
  Setting the mode to markers
  Read more about this attribute -> https://plot.ly/r/reference/#scatter-mode
Ignoring 616 observationsNo trace type specified:
  Based on info supplied, a 'scatter' trace seems appropriate.
  Read more about this trace type -> https://plot.ly/r/reference/#scatter
No scatter mode specifed:
  Setting the mode to markers
  Read more about this attribute -> https://plot.ly/r/reference/#scatter-mode
Ignoring 616 observations
# add a line highlighting houston
add_lines(
  # plots one line per city since p knows city is a grouping variable
  add_lines(p, alpha = 0.2, name = "Texan Cities", hoverinfo = "none"),
  name = "Houston", data = filter(txhousing, city == "Houston")
)

data-plot-pipeline

allCities <- txhousing %>%
  group_by(city) %>%
  plot_ly(x = ~date, y = ~median) %>%
  add_lines(alpha = 0.2, name = "Texan Cities", hoverinfo = "none")
allCities %>%
  filter(city == "Houston") %>%
  add_lines(name = "Houston")
allCities %>%
  add_fun(function(plot) {
    plot %>% filter(city == "Houston") %>% add_lines(name = "Houston")
  }) %>%
  add_fun(function(plot) {
    plot %>% filter(city == "San Antonio") %>% 
      add_lines(name = "San Antonio")
  })

Using a function.

# reusable function for highlighting a particular city
layer_city <- function(plot, name) {
  plot %>% filter(city == name) %>% add_lines(name = name)
}
# reusable function for plotting overall median & IQR
layer_iqr <- function(plot) {
  plot %>%
    group_by(date) %>% 
    summarise(
      q1 = quantile(median, 0.25, na.rm = TRUE),
      m = median(median, na.rm = TRUE),
      q3 = quantile(median, 0.75, na.rm = TRUE)
    ) %>%
    add_lines(y = ~m, name = "median", color = I("black")) %>%
    add_ribbons(ymin = ~q1, ymax = ~q3, name = "IQR", color = I("black"))
}
allCities %>%
  add_fun(layer_iqr) %>%
  add_fun(layer_city, "Houston") %>%
  add_fun(layer_city, "San Antonio")
library(forecast)
layer_forecast <- function(plot) {
  d <- plotly_data(plot)
  series <- with(d, 
    ts(median, frequency = 12, start = c(2000, 1), end = c(2015, 7))
  )
  fore <- forecast(ets(series), h = 48, level = c(80, 95))
  plot %>%
    add_ribbons(x = time(fore$mean), ymin = fore$lower[, 2],
                ymax = fore$upper[, 2], color = I("gray95"), 
                name = "95% confidence", inherit = FALSE) %>%
    add_ribbons(x = time(fore$mean), ymin = fore$lower[, 1],
                ymax = fore$upper[, 1], color = I("gray80"), 
                name = "80% confidence", inherit = FALSE) %>%
    add_lines(x = time(fore$mean), y = fore$mean, color = I("blue"), 
              name = "prediction")
}
txhousing %>%
  group_by(city) %>%
  plot_ly(x = ~date, y = ~median) %>%
  add_lines(alpha = 0.2, name = "Texan Cities", hoverinfo = "none") %>%
  add_fun(layer_iqr) %>%
  add_fun(layer_forecast)

Click-and-drag

p <- ggplot(fortify(gold), aes(x, y)) + geom_line()
gg <- ggplotly(p)
layout(gg, dragmode = "pan")
rangeslider(gg)

mtcars

p <- ggplot(mtcars, aes(x = wt, y = mpg)) +
   geom_point() + geom_smooth()
p %>%
  ggplotly(layerData = 2, originalData = FALSE) %>%
  plotly_data()
`geom_smooth()` using method = 'loess' and formula 'y ~ x'
p %>%
  ggplotly(layerData = 2, originalData = F) %>%
  add_fun(function(p) {
    p %>% slice(which.max(se)) %>%
      add_segments(x = ~x, xend = ~x, y = ~ymin, yend = ~ymax) %>%
      add_annotations("Maximum uncertainty", ax = 60)
  }) %>%
  add_fun(function(p) {
    p %>% slice(which.min(se)) %>%
      add_segments(x = ~x, xend = ~x, y = ~ymin, yend = ~ymax) %>%
      add_annotations("Minimum uncertainty")
  })
`geom_smooth()` using method = 'loess' and formula 'y ~ x'

Add a new chunk by clicking the Insert Chunk button on the toolbar or by pressing Ctrl+Alt+I.

When you save the notebook, an HTML file containing the code and output will be saved alongside it (click the Preview button or press Ctrl+Shift+K to preview the HTML file).

The preview shows you a rendered HTML copy of the contents of the editor. Consequently, unlike Knit, Preview does not run any R code chunks. Instead, the output of the chunk when it was last run in the editor is displayed.

LS0tCnRpdGxlOiAicGxvdGx5IGV4YW1wbGVzIDIiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCiMgU29tZSBtb3JlIHBsb3RseSBleGFtcGxlcwoKVG9kYXkgd2UgYXJlIGdvaW5nIHRvIGRpc2N1c3MgYSBudW1iZXIgb2YgYWRkaXRpb25hbCBleGFtcGxlcyBvZiB1c2luZyBwbG90bHkuCgpUbyBnZXQgc3RhcnRlZCwgcGxvdGx5IHVzZXMgW2h0bWx3aWRnZXRzXShodHRwOi8vd3d3Lmh0bWx3aWRnZXRzLm9yZy8pLiAgVGFrZSBhIGxvb2sgYXQgdGhlIGh0bWx3aWRnZXRzIHdlYnNpdGUgYW5kIGdpdmUgYSBmZXcgZXhhbXBsZXMgYSB0cnkuCgpwbG90bHkgdXNlcyBodG1sd2lkZ2V0cy4gIFNvIHdlIHNob3VsZCBiZWNvbWUgZmFtaWxpYXIgd2l0aCB0aGVtLgoKIyMjIEV4YW1wbGUgMTogClVzaW5nIFtsZWFmbGV0XShodHRwOi8vcnN0dWRpby5naXRodWIuaW8vbGVhZmxldC8pIHRvIG1ha2UgbWFwcy4KCmBgYHtyfQpsaWJyYXJ5KGxlYWZsZXQpCgptIDwtIGxlYWZsZXQoKSAlPiUKICBhZGRUaWxlcygpICU+JSAgIyBBZGQgZGVmYXVsdCBPcGVuU3RyZWV0TWFwIG1hcCB0aWxlcwogIGFkZE1hcmtlcnMobG5nPTE3NC43NjgsIGxhdD0tMzYuODUyLCBwb3B1cD0iVGhlIGJpcnRocGxhY2Ugb2YgUiIpCm0gICMgUHJpbnQgdGhlIG1hcApgYGAKCkdvIHRvIGdvb2dsZSBhbmQgYXNrIGZvciB0aGUgbG9jYXRpb24gb2YgQ1NVIEVhc3QgQmF5LiAgU2VhcmNoIGZvciAiQ1NVIEVhc3QgQmF5IGxhdCBhbmQgbG9uZyIKCkFuc3dlcjogMzcuNjU2M8KwIE4sIDEyMi4wNTY3wrAgVwoKIyBGaXJzdCB0cnkuICBXaGF0IGdvZXMgZ29vZ2xlIGdpdmUgdXM/CgpgYGB7cn0KbSA8LSBsZWFmbGV0KCkgJT4lCiAgYWRkVGlsZXMoKSAlPiUgICMgQWRkIGRlZmF1bHQgT3BlblN0cmVldE1hcCBtYXAgdGlsZXMKICBhZGRNYXJrZXJzKGxuZz0zNy42NTYzICwgbGF0PTEyMi4wNTY3LCBwb3B1cD0iQ1NVIEVhc3QgQmF5PyIpCm0gICMgUHJpbnQgdGhlIG1hcApgYGAKClRoYXQgaXMgdG90YWxseSB3cm9uZy4KCmBgYHtyfQptIDwtIGxlYWZsZXQoKSAlPiUKICBhZGRUaWxlcygpICU+JSAgIyBBZGQgZGVmYXVsdCBPcGVuU3RyZWV0TWFwIG1hcCB0aWxlcwogIGFkZE1hcmtlcnMobG5nPTEyMi4wNTY3LCBsYXQ9MzcuNjU2MywgcG9wdXA9IkNTVSBFYXN0IEJheT8iKQptICAjIFByaW50IHRoZSBtYXAKYGBgCgpTdGlsbCB3cm9uZywgYnV0IGF0IGxlYXN0IHdlIGFyZSBvbiB0aGUgcGxhbmV0IG5vdy4KCmBgYHtyfQptIDwtIGxlYWZsZXQoKSAlPiUKICBhZGRUaWxlcygpICU+JSAgIyBBZGQgZGVmYXVsdCBPcGVuU3RyZWV0TWFwIG1hcCB0aWxlcwogIGFkZE1hcmtlcnMobG5nPS0xMjIuMDU2NywgbGF0PTM3LjY1NjMsIHBvcHVwPSJDU1UgRWFzdCBCYXkiKQptICAjIFByaW50IHRoZSBtYXAKYGBgCgpIZXJlIGlzIGEgbGluayB0byB0aGUgZ29vZ2xlIGRldmVsb3BlciBwYWdlIGFib3V0IFtsYXQgYW5kIGxvbmddKGh0dHBzOi8vc3VwcG9ydC5nb29nbGUuY29tL21hcHMvYW5zd2VyLzE4NTM5P2NvPUdFTklFLlBsYXRmb3JtJTNERGVza3RvcCZobD1lbikuICBLZXkgdGhpbmcgaXMgbGF0IGNvbWVzIGJlZm9yZSBsb25nLgoKYGBge3J9Cm0gPC0gbGVhZmxldCgpICU+JQogIGFkZFRpbGVzKCkgJT4lICAjIEFkZCBkZWZhdWx0IE9wZW5TdHJlZXRNYXAgbWFwIHRpbGVzCiAgYWRkTWFya2VycyhsYXQ9MzcuNjU2MywgbG5nPS0xMjIuMDU2NywgcG9wdXA9IkNTVSBFYXN0IEJheSIpCm0gICMgUHJpbnQgdGhlIG1hcApgYGAKCkhlcmUgaXMgdGhlIGxpbmsgdG8gW0dvb2dsZSBNYXBzIEFQSV0oaHR0cHM6Ly9kZXZlbG9wZXJzLmdvb2dsZS5jb20vbWFwcy9kb2N1bWVudGF0aW9uL2dlb2NvZGluZy9pbnRybykuCgpGaW5hbGx5IGhlcmUgaXMgW09wZW5TdHJlZXRNYXBdKGh0dHBzOi8vZGV2ZWxvcGVycy5nb29nbGUuY29tL21hcHMvZG9jdW1lbnRhdGlvbi9nZW9jb2RpbmcvaW50cm8pLiAgVGhpcyBpcyB3aGVyZSB0aGUgbWFwcyBjb21lIGZyb20gZm9yIGxlYWZsZXQuICBDYW4geW91IHVzZSBnb29nbGVtYXBzPwoKIyMjIEV4YW1wbGUgMjogCnBsb3RseSBpcyBnaXZlbiBhcyBhIGV4YW1wbGUuCgpgYGB7cn0KbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHBsb3RseSkKcCA8LSBnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzLCBhZXMoeCA9IGN1dCwgZmlsbCA9IGNsYXJpdHkpKSArCiAgICAgICAgICAgIGdlb21fYmFyKHBvc2l0aW9uID0gImRvZGdlIikKZ2dwbG90bHkocCkKYGBgCgpgYGB7cn0KZCA8LSBkaWFtb25kc1tzYW1wbGUobnJvdyhkaWFtb25kcyksIDUwMCksIF0KcGxvdF9seShkLCB4ID0gZCRjYXJhdCwgeSA9IGQkcHJpY2UsIAogICAgICAgIHRleHQgPSBwYXN0ZSgiQ2xhcml0eTogIiwgZCRjbGFyaXR5KSwKICAgICAgICBtb2RlID0gIm1hcmtlcnMiLCBjb2xvciA9IGQkY2FyYXQsIHNpemUgPSBkJGNhcmF0KQpgYGAKCiMjIyBFeGFtcGxlIDMuIApUaGUgcHl0aG9uIHBsb3RpbmcgbGlicmFyeSByYm9rZWguCgpgYGB7cn0KbGlicmFyeShyYm9rZWgpCmZpZ3VyZSgpICU+JQogIGx5X3BvaW50cyhTZXBhbC5MZW5ndGgsIFNlcGFsLldpZHRoLCBkYXRhID0gaXJpcywKICAgIGNvbG9yID0gU3BlY2llcywgZ2x5cGggPSBTcGVjaWVzLAogICAgaG92ZXIgPSBsaXN0KFNlcGFsLkxlbmd0aCwgU2VwYWwuV2lkdGgpKQpgYGAKCiMjIEV4YW1wbGUgNDogCnZpc05ldHdvcmsuCgpgYGB7cn0KbGlicmFyeShuZXR3b3JrRDMpCmRhdGEoTWlzTGlua3MsIE1pc05vZGVzKQpmb3JjZU5ldHdvcmsoTGlua3MgPSBNaXNMaW5rcywgTm9kZXMgPSBNaXNOb2RlcywgU291cmNlID0gInNvdXJjZSIsCiAgICAgICAgICAgICBUYXJnZXQgPSAidGFyZ2V0IiwgVmFsdWUgPSAidmFsdWUiLCBOb2RlSUQgPSAibmFtZSIsCiAgICAgICAgICAgICBHcm91cCA9ICJncm91cCIsIG9wYWNpdHkgPSAwLjQpCmBgYAoKIyMgRXhhbXBsZSA1OiAKTWFraW5nIHRhYmxlcyBEVC4KCmBgYHtyfQpsaWJyYXJ5KERUKQpkYXRhdGFibGUoaXJpcywgb3B0aW9ucyA9IGxpc3QocGFnZUxlbmd0aCA9IDUpKQpgYGAKCiMjRXhhbXBsZSA2OiAKT25lIG1vcmUgdGhpbmcsIFtmbGV4ZGFzaGJvYXJkc10oaHR0cDovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbS9mbGV4ZGFzaGJvYXJkLykgd2hpY2ggdXNlZCBbc2hpbnldKGh0dHA6Ly9ybWFya2Rvd24ucnN0dWRpby5jb20vZmxleGRhc2hib2FyZC9zaGlueS5odG1sKS4gIEhlcmUgaXMgYSBvbmUgZXhhbXBsZSBbZ2VvbXMgZGFzaF0oaHR0cHM6Ly9iZXRhLnJzdHVkaW9jb25uZWN0LmNvbS9qamFsbGFpcmUvaHRtbHdpZGdldHMtZ2dwbG90bHktZ2VvbXMvaHRtbHdpZGdldHMtZ2dwbG90bHktZ2VvbXMuaHRtbCNnZW9tX2RlbnNpdHkpLgoKCiMgQmFjayB0byBwbG90bHkuCgojIyMgRXhhbXBsZS4KQSBjYXNlIHN0dWR5IG9mIGhvdXNpbmcgc2FsZXMgaW4gVGV4YXMuICBTZWUgdGhpcyBbcGFnZV0oaHR0cHM6Ly9wbG90bHktYm9vay5jcHNpZXZlcnQubWUvYS1jYXNlLXN0dWR5LW9mLWhvdXNpbmctc2FsZXMtaW4tdGV4YXMuaHRtbCkgaW4gdGhlIGJvb2suCgpUaGlzIGlzIGEgdmVyeSBuaWNlIGNhc2Ugc3R1ZHkuICBZb3Ugc2hvdWxkIHJlYWQgdGhlIGNoYXB0ZXIgYW5kIHRyeSBhbGwgb2YgdGhlIHBsb3RzLgoKYGBge3J9CmxpYnJhcnkocGxvdGx5KQp0eGhvdXNpbmcKYGBgCgpgYGB7cn0KcCA8LSBnZ3Bsb3QodHhob3VzaW5nLCBhZXMoZGF0ZSwgbWVkaWFuKSkgKwogIGdlb21fbGluZShhZXMoZ3JvdXAgPSBjaXR5KSwgYWxwaGEgPSAwLjIpCnAKYGBgCgoKYGBge3J9CnN1YnBsb3QoCiAgcCwgZ2dwbG90bHkocCwgdG9vbHRpcCA9ICJjaXR5IiksIAogIGdncGxvdCh0eGhvdXNpbmcsIGFlcyhkYXRlLCBtZWRpYW4pKSArIGdlb21fYmluMmQoKSwKICBnZ3Bsb3QodHhob3VzaW5nLCBhZXMoZGF0ZSwgbWVkaWFuKSkgKyBnZW9tX2hleCgpLAogIG5yb3dzID0gMiwgc2hhcmVYID0gVFJVRSwgc2hhcmVZID0gVFJVRSwKICB0aXRsZVkgPSBGQUxTRSwgdGl0bGVYID0gRkFMU0UKKQpgYGAKCgpgYGB7cn0KbGlicmFyeShkcGx5cikKdHggPC0gZ3JvdXBfYnkodHhob3VzaW5nLCBjaXR5KQojIGluaXRpYXRlIGEgcGxvdGx5IG9iamVjdCB3aXRoIGRhdGUgb24geCBhbmQgbWVkaWFuIG9uIHkKcCA8LSBwbG90X2x5KHR4LCB4ID0gfmRhdGUsIHkgPSB+bWVkaWFuKQojIHBsb3RseV9kYXRhKCkgcmV0dXJucyBkYXRhIGFzc29jaWF0ZWQgd2l0aCBhIHBsb3RseSBvYmplY3QKcGxvdGx5X2RhdGEocCkKcApgYGAKCmBgYHtyfQojIGFkZCBhIGxpbmUgaGlnaGxpZ2h0aW5nIGhvdXN0b24KYWRkX2xpbmVzKAogICMgcGxvdHMgb25lIGxpbmUgcGVyIGNpdHkgc2luY2UgcCBrbm93cyBjaXR5IGlzIGEgZ3JvdXBpbmcgdmFyaWFibGUKICBhZGRfbGluZXMocCwgYWxwaGEgPSAwLjIsIG5hbWUgPSAiVGV4YW4gQ2l0aWVzIiwgaG92ZXJpbmZvID0gIm5vbmUiKSwKICBuYW1lID0gIkhvdXN0b24iLCBkYXRhID0gZmlsdGVyKHR4aG91c2luZywgY2l0eSA9PSAiSG91c3RvbiIpCikKYGBgCgoKIyMjIGRhdGEtcGxvdC1waXBlbGluZQoKYGBge3J9CmFsbENpdGllcyA8LSB0eGhvdXNpbmcgJT4lCiAgZ3JvdXBfYnkoY2l0eSkgJT4lCiAgcGxvdF9seSh4ID0gfmRhdGUsIHkgPSB+bWVkaWFuKSAlPiUKICBhZGRfbGluZXMoYWxwaGEgPSAwLjIsIG5hbWUgPSAiVGV4YW4gQ2l0aWVzIiwgaG92ZXJpbmZvID0gIm5vbmUiKQoKYWxsQ2l0aWVzICU+JQogIGZpbHRlcihjaXR5ID09ICJIb3VzdG9uIikgJT4lCiAgYWRkX2xpbmVzKG5hbWUgPSAiSG91c3RvbiIpCmBgYAoKYGBge3J9CmFsbENpdGllcyAlPiUKICBhZGRfZnVuKGZ1bmN0aW9uKHBsb3QpIHsKICAgIHBsb3QgJT4lIGZpbHRlcihjaXR5ID09ICJIb3VzdG9uIikgJT4lIGFkZF9saW5lcyhuYW1lID0gIkhvdXN0b24iKQogIH0pICU+JQogIGFkZF9mdW4oZnVuY3Rpb24ocGxvdCkgewogICAgcGxvdCAlPiUgZmlsdGVyKGNpdHkgPT0gIlNhbiBBbnRvbmlvIikgJT4lIAogICAgICBhZGRfbGluZXMobmFtZSA9ICJTYW4gQW50b25pbyIpCiAgfSkKYGBgCgpVc2luZyBhIGZ1bmN0aW9uLgoKYGBge3J9CiMgcmV1c2FibGUgZnVuY3Rpb24gZm9yIGhpZ2hsaWdodGluZyBhIHBhcnRpY3VsYXIgY2l0eQpsYXllcl9jaXR5IDwtIGZ1bmN0aW9uKHBsb3QsIG5hbWUpIHsKICBwbG90ICU+JSBmaWx0ZXIoY2l0eSA9PSBuYW1lKSAlPiUgYWRkX2xpbmVzKG5hbWUgPSBuYW1lKQp9CgojIHJldXNhYmxlIGZ1bmN0aW9uIGZvciBwbG90dGluZyBvdmVyYWxsIG1lZGlhbiAmIElRUgpsYXllcl9pcXIgPC0gZnVuY3Rpb24ocGxvdCkgewogIHBsb3QgJT4lCiAgICBncm91cF9ieShkYXRlKSAlPiUgCiAgICBzdW1tYXJpc2UoCiAgICAgIHExID0gcXVhbnRpbGUobWVkaWFuLCAwLjI1LCBuYS5ybSA9IFRSVUUpLAogICAgICBtID0gbWVkaWFuKG1lZGlhbiwgbmEucm0gPSBUUlVFKSwKICAgICAgcTMgPSBxdWFudGlsZShtZWRpYW4sIDAuNzUsIG5hLnJtID0gVFJVRSkKICAgICkgJT4lCiAgICBhZGRfbGluZXMoeSA9IH5tLCBuYW1lID0gIm1lZGlhbiIsIGNvbG9yID0gSSgiYmxhY2siKSkgJT4lCiAgICBhZGRfcmliYm9ucyh5bWluID0gfnExLCB5bWF4ID0gfnEzLCBuYW1lID0gIklRUiIsIGNvbG9yID0gSSgiYmxhY2siKSkKfQoKYWxsQ2l0aWVzICU+JQogIGFkZF9mdW4obGF5ZXJfaXFyKSAlPiUKICBhZGRfZnVuKGxheWVyX2NpdHksICJIb3VzdG9uIikgJT4lCiAgYWRkX2Z1bihsYXllcl9jaXR5LCAiU2FuIEFudG9uaW8iKQpgYGAKCmBgYHtyfQpsaWJyYXJ5KGZvcmVjYXN0KQpsYXllcl9mb3JlY2FzdCA8LSBmdW5jdGlvbihwbG90KSB7CiAgZCA8LSBwbG90bHlfZGF0YShwbG90KQogIHNlcmllcyA8LSB3aXRoKGQsIAogICAgdHMobWVkaWFuLCBmcmVxdWVuY3kgPSAxMiwgc3RhcnQgPSBjKDIwMDAsIDEpLCBlbmQgPSBjKDIwMTUsIDcpKQogICkKICBmb3JlIDwtIGZvcmVjYXN0KGV0cyhzZXJpZXMpLCBoID0gNDgsIGxldmVsID0gYyg4MCwgOTUpKQogIHBsb3QgJT4lCiAgICBhZGRfcmliYm9ucyh4ID0gdGltZShmb3JlJG1lYW4pLCB5bWluID0gZm9yZSRsb3dlclssIDJdLAogICAgICAgICAgICAgICAgeW1heCA9IGZvcmUkdXBwZXJbLCAyXSwgY29sb3IgPSBJKCJncmF5OTUiKSwgCiAgICAgICAgICAgICAgICBuYW1lID0gIjk1JSBjb25maWRlbmNlIiwgaW5oZXJpdCA9IEZBTFNFKSAlPiUKICAgIGFkZF9yaWJib25zKHggPSB0aW1lKGZvcmUkbWVhbiksIHltaW4gPSBmb3JlJGxvd2VyWywgMV0sCiAgICAgICAgICAgICAgICB5bWF4ID0gZm9yZSR1cHBlclssIDFdLCBjb2xvciA9IEkoImdyYXk4MCIpLCAKICAgICAgICAgICAgICAgIG5hbWUgPSAiODAlIGNvbmZpZGVuY2UiLCBpbmhlcml0ID0gRkFMU0UpICU+JQogICAgYWRkX2xpbmVzKHggPSB0aW1lKGZvcmUkbWVhbiksIHkgPSBmb3JlJG1lYW4sIGNvbG9yID0gSSgiYmx1ZSIpLCAKICAgICAgICAgICAgICBuYW1lID0gInByZWRpY3Rpb24iKQp9Cgp0eGhvdXNpbmcgJT4lCiAgZ3JvdXBfYnkoY2l0eSkgJT4lCiAgcGxvdF9seSh4ID0gfmRhdGUsIHkgPSB+bWVkaWFuKSAlPiUKICBhZGRfbGluZXMoYWxwaGEgPSAwLjIsIG5hbWUgPSAiVGV4YW4gQ2l0aWVzIiwgaG92ZXJpbmZvID0gIm5vbmUiKSAlPiUKICBhZGRfZnVuKGxheWVyX2lxcikgJT4lCiAgYWRkX2Z1bihsYXllcl9mb3JlY2FzdCkKYGBgCgojIyMgQ2xpY2stYW5kLWRyYWcKCmBgYHtyfQpwIDwtIGdncGxvdChmb3J0aWZ5KGdvbGQpLCBhZXMoeCwgeSkpICsgZ2VvbV9saW5lKCkKZ2cgPC0gZ2dwbG90bHkocCkKbGF5b3V0KGdnLCBkcmFnbW9kZSA9ICJwYW4iKQpgYGAKCmBgYHtyfQpyYW5nZXNsaWRlcihnZykKYGBgCgojIyMgbXRjYXJzCgpgYGB7cn0KcCA8LSBnZ3Bsb3QobXRjYXJzLCBhZXMoeCA9IHd0LCB5ID0gbXBnKSkgKwogICBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aCgpCnAgJT4lCiAgZ2dwbG90bHkobGF5ZXJEYXRhID0gMiwgb3JpZ2luYWxEYXRhID0gRkFMU0UpICU+JQogIHBsb3RseV9kYXRhKCkKYGBgCgoKYGBge3J9CnAgJT4lCiAgZ2dwbG90bHkobGF5ZXJEYXRhID0gMiwgb3JpZ2luYWxEYXRhID0gRikgJT4lCiAgYWRkX2Z1bihmdW5jdGlvbihwKSB7CiAgICBwICU+JSBzbGljZSh3aGljaC5tYXgoc2UpKSAlPiUKICAgICAgYWRkX3NlZ21lbnRzKHggPSB+eCwgeGVuZCA9IH54LCB5ID0gfnltaW4sIHllbmQgPSB+eW1heCkgJT4lCiAgICAgIGFkZF9hbm5vdGF0aW9ucygiTWF4aW11bSB1bmNlcnRhaW50eSIsIGF4ID0gNjApCiAgfSkgJT4lCiAgYWRkX2Z1bihmdW5jdGlvbihwKSB7CiAgICBwICU+JSBzbGljZSh3aGljaC5taW4oc2UpKSAlPiUKICAgICAgYWRkX3NlZ21lbnRzKHggPSB+eCwgeGVuZCA9IH54LCB5ID0gfnltaW4sIHllbmQgPSB+eW1heCkgJT4lCiAgICAgIGFkZF9hbm5vdGF0aW9ucygiTWluaW11bSB1bmNlcnRhaW50eSIpCiAgfSkKYGBgCgoKQWRkIGEgbmV3IGNodW5rIGJ5IGNsaWNraW5nIHRoZSAqSW5zZXJ0IENodW5rKiBidXR0b24gb24gdGhlIHRvb2xiYXIgb3IgYnkgcHJlc3NpbmcgKkN0cmwrQWx0K0kqLgoKV2hlbiB5b3Ugc2F2ZSB0aGUgbm90ZWJvb2ssIGFuIEhUTUwgZmlsZSBjb250YWluaW5nIHRoZSBjb2RlIGFuZCBvdXRwdXQgd2lsbCBiZSBzYXZlZCBhbG9uZ3NpZGUgaXQgKGNsaWNrIHRoZSAqUHJldmlldyogYnV0dG9uIG9yIHByZXNzICpDdHJsK1NoaWZ0K0sqIHRvIHByZXZpZXcgdGhlIEhUTUwgZmlsZSkuCgpUaGUgcHJldmlldyBzaG93cyB5b3UgYSByZW5kZXJlZCBIVE1MIGNvcHkgb2YgdGhlIGNvbnRlbnRzIG9mIHRoZSBlZGl0b3IuIENvbnNlcXVlbnRseSwgdW5saWtlICpLbml0KiwgKlByZXZpZXcqIGRvZXMgbm90IHJ1biBhbnkgUiBjb2RlIGNodW5rcy4gSW5zdGVhZCwgdGhlIG91dHB1dCBvZiB0aGUgY2h1bmsgd2hlbiBpdCB3YXMgbGFzdCBydW4gaW4gdGhlIGVkaXRvciBpcyBkaXNwbGF5ZWQuCg==