Package dygraphs là giao diện của R với thư viện biểu đồ JavaScript. Nó cung cấp nhiều công cụ để làm việc với biểu đồ cho dữ liệu chuỗi thời gian trong R, bao gồm như:

Cài đặt dygraphs

Bạn có thể cài đặt từ CRAN với lệnh sau:

install.packages("dygraphs")

Bạn có thể sử dụng dygraphs trong R Console, với R Markdown documents và với Shiny applications. Dưới đây là một vài demos về dygraphs trong thư viện ví dụ.

Demos

Đây là một dygraphs đơn giản được tạo thành từmột vài đối tượng chuỗi thời gian:

library(dygraphs)
lungDeaths <- cbind(mdeaths, fdeaths)
dygraph(lungDeaths)

Chú ý rằng biểu đồ này hoàn toàn tương tác :Như là bạn có thể dùng chuột di chuyển dọc theo đương đồ thị để làm hiển thị các giá trị cụ thể của đồ thị. Bạn cũng có thể chọn vùng của biểu đồ để phóng(double-click zoom out.)

Bạn có thể tùy chỉnh dygraphs bằng việc thêm các lệnh bổ sung vào đối tượng dygraph ban đầu.

Thêm dyRangeSelector vào original graph:

dygraph(lungDeaths) %>% dyRangeSelector()

Chú ý ở ví dụ này sử dụng toán tử %>% (hoặc gọi là “pipe”) từ thư viện magrittr để tạo dygraph với range selector. Bạn có thể sử dụng toán tử này để tùy chỉnh axes, chuỗi và những tùy chọn khác. Ví dụ như:

dygraph(lungDeaths) %>%
  dySeries("mdeaths", label = "Male") %>%
  dySeries("fdeaths", label = "Female") %>%
  dyOptions(stackedGraph = TRUE) %>%
  dyRangeSelector(height = 20)

Có rất nhiều tùy biến để hiệu chỉnh Series và axis hiển thị sẵn sàng để dùng . Thậm chí có thể gộp một vài lower/value/upper style series trong một hiển thị với shaded bars. Dưới đây là một ví dụ để minh họa shaded bars, thêm tên cho biểu đồ,bỏ vẽ lưới cho trục X và sử dụng màu:

hw <- HoltWinters(ldeaths)
predicted <- predict(hw, n.ahead = 72, prediction.interval = TRUE)
dygraph(predicted, main = "Predicted Lung Deaths (UK)") %>%
  dyAxis("x", drawGrid = FALSE) %>%
  dySeries(c("lwr", "fit", "upr"), label = "Deaths") %>%
  dyOptions(colors = RColorBrewer::brewer.pal(3, "Set1"))

Ngoài ra trong Gallery cảu dygraph vẫn còn rất nhiều các ví dụ của các đặc tính sẵn sàng sử dụng để hiệu chỉnh dygraphs

Sử dụng dygraphs trong R console

Dygraphs được sử dụng trong R console tương tự như bất cứ hàm vẽ biểu đồ nào trong R. Ví dụ, dưới đây là code được sử dụng để vẽ chuỗi thời gian nhtemp sử dụng dygrpahs:

plot(nhtemp, main = "New Haven Temperatures", ylab = "Temp (F)")
dygraph(nhtemp, main = "New Haven Temperatures", ylab = "Temp (F)") 

Mặc định R Console sẽ hiện thị dygraphs ở ngoài cửa sổ trình duyệt

Nếu bạn sử dụng Rstudio nó sẽ được hiển hị ở Rstudio Viewe.

Sử dụng dygraphs trong R Markdown Figure size:

---
title: "My Document"
output:
  html_document:
    fig_width: 6
    fig_height: 4
---

Bạn cũng có thể thay đổi figure sizes on a per-chunk basis.(mặc định là 7x5):

dygraph(nhtemp, main = "New Haven Temperatures", ylab = "Temp (F)") 

GALLERY của dygraphs

1. Series Options

Series colors

lungDeaths <- cbind(ldeaths, mdeaths, fdeaths)
dygraph(lungDeaths, main = "Deaths from Lung Disease (UK)") %>%
  dyOptions(colors = RColorBrewer::brewer.pal(3, "Set2"))

Step plots

Trong dygraphs chuỗi thời gian hiện thị mặc định dưới dạng đường, tuy nhiên bạn cũng có thể vẽ biều đồ cho chuỗi thời gian dạng step chart:

lungDeaths <- cbind(mdeaths, fdeaths)
dygraph(lungDeaths, main = "Deaths from Lung Disease (UK)") %>%
  dyOptions(stepPlot = TRUE)

Filling Bạn có thể lấp khu vực bên dưới đường và có thể hiệu chỉnh giá trị alpha để lấp:

dygraph(ldeaths, main = "Deaths from Lung Disease (UK)") %>%
  dyOptions(fillGraph = TRUE, fillAlpha = 0.4)

Point display

dygraph(ldeaths, main = "Deaths from Lung Disease (UK)") %>%
  dyOptions(drawPoints = TRUE, pointSize = 2)

Những hình dạng point khác nhau:

dygraph(ldeaths, main = "Deaths from Lung Disease (UK)") %>%
  dyOptions(drawPoints = TRUE, pointSize = 5)

Per-Series Options

dygraph(lungDeaths, main = "Deaths from Lung Disease (UK)") %>%
  dySeries("mdeaths", drawPoints = TRUE, color = "blue") %>%
  dySeries("fdeaths", stepPlot = TRUE, fillGraph = TRUE, color = "red")

Line Strokes

dygraph(ldeaths, main = "Deaths from Lung Disease (UK)") %>%
  dySeries("V1", strokeWidth = 2, strokePattern = "dashed")

dyGroup

lungDeaths <- cbind(mdeaths, fdeaths, ldeaths)
dygraph(lungDeaths, main = "Deaths from Lung Disease (UK)") %>%
  dySeries("fdeaths", stepPlot = TRUE, color = "red") 

# dyGroup(c("mdeaths", "ldeaths"), drawPoints = TRUE, color = c("blue", "green")) Không làm việc

2/ Series Highlighting

lungDeaths <- cbind(ldeaths, mdeaths, fdeaths)
dygraph(lungDeaths, main = "Deaths from Lung Disease (UK)") %>%
  dyHighlight(highlightCircleSize = 5, 
              highlightSeriesBackgroundAlpha = 0.2,
              hideOnMouseOut = FALSE)
dygraph(lungDeaths, main = "Deaths from Lung Disease (UK)") %>%
  dyHighlight(highlightSeriesOpts = list(strokeWidth = 3))

3. Axis Options

dygraph(nhtemp, main = "New Haven Temperatures") %>%
  dyAxis("y", label = "Temp (F)", valueRange = c(40, 60)) %>%
  dyOptions(axisLineWidth = 1.5, fillGraph = TRUE, drawGrid = FALSE)
dygraph(AirPassengers, main = "Airline Passengers / Month") %>%
  dyAxis("x", drawGrid = FALSE) %>%
  dyAxis("y", label = "Passengers (Thousands)") %>%
  dyOptions(includeZero = TRUE, 
            axisLineColor = "navy", 
            gridLineColor = "lightblue")
# define mts with distinct y-axis scales
temperature <- ts(frequency = 12, start = c(1980, 1),
  data = c(7.0, 6.9, 9.5, 14.5, 18.2, 21.5, 
           25.2, 26.5, 23.3, 18.3, 13.9, 9.6))
rainfall <- ts(frequency = 12, start = c(1980, 1),
  data = c(49.9, 71.5, 106.4, 129.2, 144.0, 176.0, 
           135.6, 148.5, 216.4, 194.1, 95.6, 54.4))
weather <- cbind(rainfall, temperature)
# assign the "rainfall" series to the y2 axis
dygraph(weather) %>%
  dySeries("rainfall", axis = 'y2')
dygraph(weather) %>%
  dyAxis("y", label = "Temperature (C)") %>%
  dyAxis("y2", label = "Rainfall", independentTicks = TRUE) %>%
  dySeries("rainfall", axis = 'y2')

4/ Labels và Legends Labels

dygraph(discoveries, 
        main = "Important Discoveries", 
        ylab = "Discoveries / Year")
dygraph(discoveries, main = "Important Discoveries") %>%
  dyAxis("y", label = "Discoveries / Year")

Legends

dygraph(nhtemp, main = "New Haven Temperatures") %>% 
  dySeries("V1", label = "Temperature (F)") %>%
  dyLegend(show = "always", hideOnMouseOut = FALSE)
dygraph(nhtemp, main = "New Haven Temperatures") %>% 
  dySeries("V1", label = "Temperature (F)") %>%
  dyLegend(show = "follow")
lungDeaths <- cbind(ldeaths,mdeaths,fdeaths)
dygraph(lungDeaths, main = "Deaths from Lung Disease (UK)") %>%
  dyLegend(width = 400)

Ngoài ra còn rất nhiều trong thư viện:

https://rstudio.github.io/dygraphs/gallery-timezones.html

LS0tDQp0aXRsZTogIkfDs2kgdGjGsCB2aeG7h24gZHlncmFwaHMgdHJvbmcgUiINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQpQYWNrYWdlICpkeWdyYXBocyogbMOgIGdpYW8gZGnhu4duIGPhu6dhIFIgduG7m2kgdGjGsCB2aeG7h24gYmnhu4N1IMSR4buTIEphdmFTY3JpcHQuIE7DsyBjdW5nIGPhuqVwIG5oaeG7gXUgY8O0bmcgY+G7pSDEkeG7gyBsw6BtIHZp4buHYyB24bubaSBiaeG7g3UgxJHhu5MgY2hvIGThu68gbGnhu4d1IGNodeG7l2kgdGjhu51pIGdpYW4gdHJvbmcgUiwgYmFvIGfhu5NtIG5oxrA6DQoNCi0gVOG7sSDEkeG7mW5nIHbhur0gYmnhu4F1IMSR4buTIHbhu5tpIGThu68gbGnhu4d1IGNodeG7l2kgdGjhu51pIGdpYW4gZOG6oW5nICp4dHMqIChob+G6t2MgYuG6pXQga+G7syDEkeG7kWkgdMaw4bujbmcgbsOgbyBraMOhYyDEkcaw4bujYyBjaHV54buDbiB24buBIGThuqFuZyB4dHMpDQotIEPhuqV1IGjDrG5oIHRy4bulYyB04buNYSDEkeG7mSAoYXhpcykgbeG6oW5oIG3hur0gdsOgIGhp4buDbiB0aOG7iyBow6BuZyBsb+G6oXQoYmFvIGfhu5NtIHTDuXkgY2jhu41uIHNlY29uZCBZLWF4aXMpDQotIE5oaeG7gXUgdMOtbmggbsSDbmcgdMawxqFuZyB0w6FjLCBiYW8gZ+G7k20gem9vbS9wYW4gdsOgIHNlcmllcy9wb2ludCBoaWdobGlnaHRpbmcNCi0gSGnhu4NuIHRo4buLIHVwcGVyL2xvd2VyIGJhcnModsOtIGThu6VuZyBuaMawIGtob+G6o25nIGThu7EgYsOhby1wcmVkaWN0aW9uIGludGVydmFscykgcXVhbmcgY2h14buXaSANCi0gTmjhu69uZyBiaeG7gXUgxJHhu5Mga2jDoWMgbmhhdSDEkcaw4bujYyBjaGUgcGjhu6cgYuG7n2kgKHNoYWRlZCByZWdpb25zLCBldmVudCBsaW5lcywgdsOgIHBvaW50IGFubm90YXRpb25zKQ0KLSBT4butIGThu6VuZyBSIENvbnNvbGUgZ2nhu5FuZyBuaMawIGPDoWMgYmnhu4N1IMSR4buTIHRpw6p1IGNodeG6qW4gY+G7p2EgUihuaMawIGzDoCBxdWEgUnN0dWRpbyBWaWV3ZXIpDQotIE5ow7puZyB0cm9uZyBSIE1hcmtkb3duIGRvY3VtZW50cyB2w6AgU2hpbnkgV2ViIGFwcGxpY2F0aW9ucyBk4buFIGTDoG5nIHbDoCB0csahbiB0cnUuDQoNCioqQ8OgaSDEkeG6t3QgZHlncmFwaHMqKg0KDQpC4bqhbiBjw7MgdGjhu4MgY8OgaSDEkeG6t3QgdOG7qyBDUkFOIHbhu5tpIGzhu4duaCBzYXU6DQpgYGB7cn0NCmluc3RhbGwucGFja2FnZXMoImR5Z3JhcGhzIikNCmBgYA0KDQpC4bqhbiBjw7MgdGjhu4Mgc+G7rSBk4bulbmcgKmR5Z3JhcGhzKiB0cm9uZyBSIENvbnNvbGUsIHbhu5tpIFIgTWFya2Rvd24gZG9jdW1lbnRzIHbDoCB24bubaSBTaGlueSBhcHBsaWNhdGlvbnMuIETGsOG7m2kgxJHDonkgbMOgIG3hu5l0IHbDoGkgKmRlbW9zKiB24buBIGR5Z3JhcGhzIHRyb25nIHRoxrAgdmnhu4duIHbDrSBk4bulLg0KDQoqKipEZW1vcyoqKg0KDQoNCsSQw6J5IGzDoCBt4buZdCBkeWdyYXBocyDEkcahbiBnaeG6o24gxJHGsOG7o2MgdOG6oW8gdGjDoG5oIHThu6tt4buZdCB2w6BpIMSR4buRaSB0xrDhu6NuZyBjaHXhu5dpIHRo4budaSBnaWFuOg0KYGBge3J9DQpsaWJyYXJ5KGR5Z3JhcGhzKQ0KbHVuZ0RlYXRocyA8LSBjYmluZChtZGVhdGhzLCBmZGVhdGhzKQ0KZHlncmFwaChsdW5nRGVhdGhzKQ0KDQpgYGANCg0KQ2jDuiDDvSBy4bqxbmcgYmnhu4N1IMSR4buTIG7DoHkgaG/DoG4gdG/DoG4gdMawxqFuZyB0w6FjIDpOaMawIGzDoCBi4bqhbiBjw7MgdGjhu4MgZMO5bmcgY2h14buZdCBkaSBjaHV54buDbiBk4buNYyB0aGVvIMSRxrDGoW5nIMSR4buTIHRo4buLIMSR4buDIGzDoG0gaGnhu4NuIHRo4buLIGPDoWMgZ2nDoSB0cuG7iyBj4bulIHRo4buDIGPhu6dhIMSR4buTIHRo4buLLiBC4bqhbiBjxaluZyBjw7MgdGjhu4MgY2jhu41uIHbDuW5nIGPhu6dhIGJp4buDdSDEkeG7kyDEkeG7gyBwaMOzbmcoZG91YmxlLWNsaWNrIHpvb20gb3V0LikNCg0KQuG6oW4gY8OzIHRo4buDIHTDuXkgY2jhu4luaCBkeWdyYXBocyBi4bqxbmcgdmnhu4djIHRow6ptIGPDoWMgbOG7h25oIGLhu5Ugc3VuZyB2w6BvIMSR4buRaSB0xrDhu6NuZyBkeWdyYXBoIGJhbiDEkeG6p3UuDQoNClRow6ptICpkeVJhbmdlU2VsZWN0b3IqIHbDoG8gb3JpZ2luYWwgZ3JhcGg6DQoNCmBgYHtyfQ0KZHlncmFwaChsdW5nRGVhdGhzKSAlPiUgZHlSYW5nZVNlbGVjdG9yKCkNCmBgYA0KDQoqKkNow7ogw70qKiDhu58gdsOtIGThu6UgbsOgeSBz4butIGThu6VuZyB0b8OhbiB04butICU+JSAoaG/hurdjIGfhu41pIGzDoCAicGlwZSIpIHThu6sgdGjGsCB2aeG7h24gKm1hZ3JpdHRyKiDEkeG7gyB04bqhbyBkeWdyYXBoIHbhu5tpIHJhbmdlIHNlbGVjdG9yLiBC4bqhbiBjw7MgdGjhu4Mgc+G7rSBk4bulbmcgdG/DoW4gdOG7rSBuw6B5IMSR4buDIHTDuXkgY2jhu4luaCBheGVzLCBjaHXhu5dpIHbDoCBuaOG7r25nIHTDuXkgY2jhu41uIGtow6FjLiBWw60gZOG7pSBuaMawOg0KDQpgYGB7cn0NCmR5Z3JhcGgobHVuZ0RlYXRocykgJT4lDQogIGR5U2VyaWVzKCJtZGVhdGhzIiwgbGFiZWwgPSAiTWFsZSIpICU+JQ0KICBkeVNlcmllcygiZmRlYXRocyIsIGxhYmVsID0gIkZlbWFsZSIpICU+JQ0KICBkeU9wdGlvbnMoc3RhY2tlZEdyYXBoID0gVFJVRSkgJT4lDQogIGR5UmFuZ2VTZWxlY3RvcihoZWlnaHQgPSAyMCkNCmBgYA0KDQpDw7MgcuG6pXQgbmhp4buBdSB0w7l5IGJp4bq/biDEkeG7gyBoaeG7h3UgY2jhu4luaCBTZXJpZXMgdsOgIGF4aXMgaGnhu4NuIHRo4buLIHPhurVuIHPDoG5nIMSR4buDIGTDuW5nIC4gVGjhuq1tIGNow60gY8OzIHRo4buDIGfhu5lwIG3hu5l0IHbDoGkgbG93ZXIvdmFsdWUvdXBwZXIgc3R5bGUgc2VyaWVzIHRyb25nIG3hu5l0IGhp4buDbiB0aOG7iyB24bubaSBzaGFkZWQgYmFycy4gRMaw4bubaSDEkcOieSBsw6AgbeG7mXQgdsOtIGThu6UgxJHhu4MgbWluaCBo4buNYSBzaGFkZWQgYmFycywgdGjDqm0gdMOqbiBjaG8gYmnhu4N1IMSR4buTLGLhu48gduG6vSBsxrDhu5tpIGNobyB0cuG7pWMgWCB2w6Agc+G7rSBk4bulbmcgbcOgdToNCmBgYHtyfQ0KaHcgPC0gSG9sdFdpbnRlcnMobGRlYXRocykNCnByZWRpY3RlZCA8LSBwcmVkaWN0KGh3LCBuLmFoZWFkID0gNzIsIHByZWRpY3Rpb24uaW50ZXJ2YWwgPSBUUlVFKQ0KDQpkeWdyYXBoKHByZWRpY3RlZCwgbWFpbiA9ICJQcmVkaWN0ZWQgTHVuZyBEZWF0aHMgKFVLKSIpICU+JQ0KICBkeUF4aXMoIngiLCBkcmF3R3JpZCA9IEZBTFNFKSAlPiUNCiAgZHlTZXJpZXMoYygibHdyIiwgImZpdCIsICJ1cHIiKSwgbGFiZWwgPSAiRGVhdGhzIikgJT4lDQogIGR5T3B0aW9ucyhjb2xvcnMgPSBSQ29sb3JCcmV3ZXI6OmJyZXdlci5wYWwoMywgIlNldDEiKSkNCmBgYA0KTmdvw6BpIHJhIHRyb25nIEdhbGxlcnkgY+G6o3UgZHlncmFwaCB24bqrbiBjw7JuIHLhuqV0IG5oaeG7gXUgY8OhYyB2w60gZOG7pSBj4bunYSBjw6FjIMSR4bq3YyB0w61uaCBz4bq1biBzw6BuZyBz4butIGThu6VuZyDEkeG7gyBoaeG7h3UgY2jhu4luaCBkeWdyYXBocw0KDQoqKipT4butIGThu6VuZyBkeWdyYXBocyB0cm9uZyBSIGNvbnNvbGUqKioNCg0KDQpEeWdyYXBocyDEkcaw4bujYyBz4butIGThu6VuZyB0cm9uZyBSIGNvbnNvbGUgdMawxqFuZyB04buxIG5oxrAgYuG6pXQgY+G7qSAgaMOgbSB24bq9IGJp4buDdSDEkeG7kyBuw6BvIHRyb25nIFIuIFbDrSBk4bulLCBkxrDhu5tpIMSRw6J5IGzDoCBjb2RlIMSRxrDhu6NjIHPhu60gZOG7pW5nIMSR4buDIHbhur0gY2h14buXaSB0aOG7nWkgZ2lhbiAqbmh0ZW1wKiBz4butIGThu6VuZyBkeWdycGFoczoNCmBgYHtyfQ0KcGxvdChuaHRlbXAsIG1haW4gPSAiTmV3IEhhdmVuIFRlbXBlcmF0dXJlcyIsIHlsYWIgPSAiVGVtcCAoRikiKQ0KZHlncmFwaChuaHRlbXAsIG1haW4gPSAiTmV3IEhhdmVuIFRlbXBlcmF0dXJlcyIsIHlsYWIgPSAiVGVtcCAoRikiKSANCmBgYA0KDQpN4bq3YyDEkeG7i25oIFIgQ29uc29sZSBz4bq9IGhp4buHbiB0aOG7iyBkeWdyYXBocyDhu58gbmdvw6BpIGPhu61hIHPhu5UgdHLDrG5oIGR1eeG7h3QNCg0KTuG6v3UgYuG6oW4gc+G7rSBk4bulbmcgUnN0dWRpbyBuw7Mgc+G6vSDEkcaw4bujYyBoaeG7g24gaOG7iyDhu58gIFJzdHVkaW8gVmlld2UuDQoNCioqKlPhu60gZOG7pW5nIGR5Z3JhcGhzIHRyb25nIFIgTWFya2Rvd24qKioNCioqRmlndXJlIHNpemUqKjoNCmBgYHtyfQ0KLS0tDQp0aXRsZTogIk15IERvY3VtZW50Ig0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIGZpZ193aWR0aDogNg0KICAgIGZpZ19oZWlnaHQ6IDQNCi0tLQ0KYGBgDQoNCkLhuqFuIGPFqW5nIGPDsyB0aOG7gyB0aGF5IMSR4buVaSBmaWd1cmUgc2l6ZXMgb24gYSBwZXItY2h1bmsgYmFzaXMuKG3hurdjIMSR4buLbmggbMOgIDd4NSk6DQoNCmBgYHtyLCBmaWcud2lkdGg9NiwgZmlnLmhlaWdodD0yLjV9DQpkeWdyYXBoKG5odGVtcCwgbWFpbiA9ICJOZXcgSGF2ZW4gVGVtcGVyYXR1cmVzIiwgeWxhYiA9ICJUZW1wIChGKSIpIA0KYGBgDQoNCioqKkdBTExFUlkgY+G7p2EgZHlncmFwaHMqKioNCg0KDQoqKjEuIFNlcmllcyBPcHRpb25zKioNCg0KDQoqKlNlcmllcyBjb2xvcnMqKg0KYGBge3J9DQpsdW5nRGVhdGhzIDwtIGNiaW5kKGxkZWF0aHMsIG1kZWF0aHMsIGZkZWF0aHMpDQpkeWdyYXBoKGx1bmdEZWF0aHMsIG1haW4gPSAiRGVhdGhzIGZyb20gTHVuZyBEaXNlYXNlIChVSykiKSAlPiUNCiAgZHlPcHRpb25zKGNvbG9ycyA9IFJDb2xvckJyZXdlcjo6YnJld2VyLnBhbCgzLCAiU2V0MiIpKQ0KYGBgDQoqKlN0ZXAgcGxvdHMqKg0KDQpUcm9uZyBkeWdyYXBocyBjaHXhu5dpIHRo4budaSBnaWFuIGhp4buHbiB0aOG7iyBt4bq3YyDEkeG7i25oIGTGsOG7m2kgZOG6oW5nIMSRxrDhu51uZywgdHV5IG5oacOqbiBi4bqhbiBjxaluZyBjw7MgdGjhu4MgduG6vSBiaeG7gXUgxJHhu5MgY2hvIGNodeG7l2kgdGjhu51pIGdpYW4gZOG6oW5nIHN0ZXAgY2hhcnQ6DQpgYGB7cn0NCmx1bmdEZWF0aHMgPC0gY2JpbmQobWRlYXRocywgZmRlYXRocykNCmR5Z3JhcGgobHVuZ0RlYXRocywgbWFpbiA9ICJEZWF0aHMgZnJvbSBMdW5nIERpc2Vhc2UgKFVLKSIpICU+JQ0KICBkeU9wdGlvbnMoc3RlcFBsb3QgPSBUUlVFKQ0KYGBgDQoNCioqRmlsbGluZyoqDQpC4bqhbiBjw7MgdGjhu4MgbOG6pXAga2h1IHbhu7FjIGLDqm4gZMaw4bubaSDEkcaw4budbmcgdsOgIGPDsyB0aOG7gyBoaeG7h3UgY2jhu4luaCBnacOhIHRy4buLIGFscGhhIMSR4buDIGzhuqVwOg0KYGBge3J9DQpkeWdyYXBoKGxkZWF0aHMsIG1haW4gPSAiRGVhdGhzIGZyb20gTHVuZyBEaXNlYXNlIChVSykiKSAlPiUNCiAgZHlPcHRpb25zKGZpbGxHcmFwaCA9IFRSVUUsIGZpbGxBbHBoYSA9IDAuNCkNCmBgYA0KKipQb2ludCBkaXNwbGF5KioNCmBgYHtyfQ0KZHlncmFwaChsZGVhdGhzLCBtYWluID0gIkRlYXRocyBmcm9tIEx1bmcgRGlzZWFzZSAoVUspIikgJT4lDQogIGR5T3B0aW9ucyhkcmF3UG9pbnRzID0gVFJVRSwgcG9pbnRTaXplID0gMikNCmBgYA0KTmjhu69uZyBow6xuaCBk4bqhbmcgcG9pbnQga2jDoWMgbmhhdToNCmBgYHtyfQ0KZHlncmFwaChsZGVhdGhzLCBtYWluID0gIkRlYXRocyBmcm9tIEx1bmcgRGlzZWFzZSAoVUspIikgJT4lDQogIGR5T3B0aW9ucyhkcmF3UG9pbnRzID0gVFJVRSwgcG9pbnRTaXplID0gNSkgDQojZHlPcHRpb25zKGRyYXdQb2ludHMgPSBUUlVFLCBwb2ludFNpemUgPSA1LCBwb2ludFNoYXBlID0gInRyaWFuZ2xlIikga2jDtG5nIGzDoG0gdmnhu4djLiBD4bqnbiBnaeG6o2kgdGjDrWNoDQpgYGANCioqUGVyLVNlcmllcyBPcHRpb25zKioNCmBgYHtyfQ0KZHlncmFwaChsdW5nRGVhdGhzLCBtYWluID0gIkRlYXRocyBmcm9tIEx1bmcgRGlzZWFzZSAoVUspIikgJT4lDQogIGR5U2VyaWVzKCJtZGVhdGhzIiwgZHJhd1BvaW50cyA9IFRSVUUsIGNvbG9yID0gImJsdWUiKSAlPiUNCiAgZHlTZXJpZXMoImZkZWF0aHMiLCBzdGVwUGxvdCA9IFRSVUUsIGZpbGxHcmFwaCA9IFRSVUUsIGNvbG9yID0gInJlZCIpDQpgYGANCioqTGluZSBTdHJva2VzKioNCmBgYHtyfQ0KZHlncmFwaChsZGVhdGhzLCBtYWluID0gIkRlYXRocyBmcm9tIEx1bmcgRGlzZWFzZSAoVUspIikgJT4lDQogIGR5U2VyaWVzKCJWMSIsIHN0cm9rZVdpZHRoID0gMiwgc3Ryb2tlUGF0dGVybiA9ICJkYXNoZWQiKQ0KYGBgDQoqKmR5R3JvdXAqKg0KYGBge3J9DQpsdW5nRGVhdGhzIDwtIGNiaW5kKG1kZWF0aHMsIGZkZWF0aHMsIGxkZWF0aHMpDQoNCmR5Z3JhcGgobHVuZ0RlYXRocywgbWFpbiA9ICJEZWF0aHMgZnJvbSBMdW5nIERpc2Vhc2UgKFVLKSIpICU+JQ0KICBkeVNlcmllcygiZmRlYXRocyIsIHN0ZXBQbG90ID0gVFJVRSwgY29sb3IgPSAicmVkIikgDQojIGR5R3JvdXAoYygibWRlYXRocyIsICJsZGVhdGhzIiksIGRyYXdQb2ludHMgPSBUUlVFLCBjb2xvciA9IGMoImJsdWUiLCAiZ3JlZW4iKSkgS2jDtG5nIGzDoG0gdmnhu4djDQpgYGANCg0KKioqMi8gU2VyaWVzIEhpZ2hsaWdodGluZyoqKg0KYGBge3J9DQpsdW5nRGVhdGhzIDwtIGNiaW5kKGxkZWF0aHMsIG1kZWF0aHMsIGZkZWF0aHMpDQpkeWdyYXBoKGx1bmdEZWF0aHMsIG1haW4gPSAiRGVhdGhzIGZyb20gTHVuZyBEaXNlYXNlIChVSykiKSAlPiUNCiAgZHlIaWdobGlnaHQoaGlnaGxpZ2h0Q2lyY2xlU2l6ZSA9IDUsIA0KICAgICAgICAgICAgICBoaWdobGlnaHRTZXJpZXNCYWNrZ3JvdW5kQWxwaGEgPSAwLjIsDQogICAgICAgICAgICAgIGhpZGVPbk1vdXNlT3V0ID0gRkFMU0UpDQpgYGANCmBgYHtyfQ0KZHlncmFwaChsdW5nRGVhdGhzLCBtYWluID0gIkRlYXRocyBmcm9tIEx1bmcgRGlzZWFzZSAoVUspIikgJT4lDQogIGR5SGlnaGxpZ2h0KGhpZ2hsaWdodFNlcmllc09wdHMgPSBsaXN0KHN0cm9rZVdpZHRoID0gMykpDQpgYGANCg0KKioqMy4gQXhpcyBPcHRpb25zKioqDQpgYGB7cn0NCmR5Z3JhcGgobmh0ZW1wLCBtYWluID0gIk5ldyBIYXZlbiBUZW1wZXJhdHVyZXMiKSAlPiUNCiAgZHlBeGlzKCJ5IiwgbGFiZWwgPSAiVGVtcCAoRikiLCB2YWx1ZVJhbmdlID0gYyg0MCwgNjApKSAlPiUNCiAgZHlPcHRpb25zKGF4aXNMaW5lV2lkdGggPSAxLjUsIGZpbGxHcmFwaCA9IFRSVUUsIGRyYXdHcmlkID0gRkFMU0UpDQpgYGANCmBgYHtyfQ0KZHlncmFwaChBaXJQYXNzZW5nZXJzLCBtYWluID0gIkFpcmxpbmUgUGFzc2VuZ2VycyAvIE1vbnRoIikgJT4lDQogIGR5QXhpcygieCIsIGRyYXdHcmlkID0gRkFMU0UpICU+JQ0KICBkeUF4aXMoInkiLCBsYWJlbCA9ICJQYXNzZW5nZXJzIChUaG91c2FuZHMpIikgJT4lDQogIGR5T3B0aW9ucyhpbmNsdWRlWmVybyA9IFRSVUUsIA0KICAgICAgICAgICAgYXhpc0xpbmVDb2xvciA9ICJuYXZ5IiwgDQogICAgICAgICAgICBncmlkTGluZUNvbG9yID0gImxpZ2h0Ymx1ZSIpDQpgYGANCmBgYHtyfQ0KIyBkZWZpbmUgbXRzIHdpdGggZGlzdGluY3QgeS1heGlzIHNjYWxlcw0KdGVtcGVyYXR1cmUgPC0gdHMoZnJlcXVlbmN5ID0gMTIsIHN0YXJ0ID0gYygxOTgwLCAxKSwNCiAgZGF0YSA9IGMoNy4wLCA2LjksIDkuNSwgMTQuNSwgMTguMiwgMjEuNSwgDQogICAgICAgICAgIDI1LjIsIDI2LjUsIDIzLjMsIDE4LjMsIDEzLjksIDkuNikpDQpyYWluZmFsbCA8LSB0cyhmcmVxdWVuY3kgPSAxMiwgc3RhcnQgPSBjKDE5ODAsIDEpLA0KICBkYXRhID0gYyg0OS45LCA3MS41LCAxMDYuNCwgMTI5LjIsIDE0NC4wLCAxNzYuMCwgDQogICAgICAgICAgIDEzNS42LCAxNDguNSwgMjE2LjQsIDE5NC4xLCA5NS42LCA1NC40KSkNCndlYXRoZXIgPC0gY2JpbmQocmFpbmZhbGwsIHRlbXBlcmF0dXJlKQ0KDQojIGFzc2lnbiB0aGUgInJhaW5mYWxsIiBzZXJpZXMgdG8gdGhlIHkyIGF4aXMNCmR5Z3JhcGgod2VhdGhlcikgJT4lDQogIGR5U2VyaWVzKCJyYWluZmFsbCIsIGF4aXMgPSAneTInKQ0KYGBgDQpgYGB7cn0NCmR5Z3JhcGgod2VhdGhlcikgJT4lDQogIGR5QXhpcygieSIsIGxhYmVsID0gIlRlbXBlcmF0dXJlIChDKSIpICU+JQ0KICBkeUF4aXMoInkyIiwgbGFiZWwgPSAiUmFpbmZhbGwiLCBpbmRlcGVuZGVudFRpY2tzID0gVFJVRSkgJT4lDQogIGR5U2VyaWVzKCJyYWluZmFsbCIsIGF4aXMgPSAneTInKQ0KYGBgDQoNCioqKjQvIExhYmVscyB2w6AgTGVnZW5kcyoqKg0KKipMYWJlbHMqKg0KYGBge3J9DQpkeWdyYXBoKGRpc2NvdmVyaWVzLCANCiAgICAgICAgbWFpbiA9ICJJbXBvcnRhbnQgRGlzY292ZXJpZXMiLCANCiAgICAgICAgeWxhYiA9ICJEaXNjb3ZlcmllcyAvIFllYXIiKQ0KYGBgDQpgYGB7cn0NCmR5Z3JhcGgoZGlzY292ZXJpZXMsIG1haW4gPSAiSW1wb3J0YW50IERpc2NvdmVyaWVzIikgJT4lDQogIGR5QXhpcygieSIsIGxhYmVsID0gIkRpc2NvdmVyaWVzIC8gWWVhciIpDQpgYGANCioqTGVnZW5kcyoqDQpgYGB7cn0NCmR5Z3JhcGgobmh0ZW1wLCBtYWluID0gIk5ldyBIYXZlbiBUZW1wZXJhdHVyZXMiKSAlPiUgDQogIGR5U2VyaWVzKCJWMSIsIGxhYmVsID0gIlRlbXBlcmF0dXJlIChGKSIpICU+JQ0KICBkeUxlZ2VuZChzaG93ID0gImFsd2F5cyIsIGhpZGVPbk1vdXNlT3V0ID0gRkFMU0UpDQpgYGANCmBgYHtyfQ0KZHlncmFwaChuaHRlbXAsIG1haW4gPSAiTmV3IEhhdmVuIFRlbXBlcmF0dXJlcyIpICU+JSANCiAgZHlTZXJpZXMoIlYxIiwgbGFiZWwgPSAiVGVtcGVyYXR1cmUgKEYpIikgJT4lDQogIGR5TGVnZW5kKHNob3cgPSAiZm9sbG93IikNCmBgYA0KYGBge3J9DQpsdW5nRGVhdGhzIDwtIGNiaW5kKGxkZWF0aHMsbWRlYXRocyxmZGVhdGhzKQ0KZHlncmFwaChsdW5nRGVhdGhzLCBtYWluID0gIkRlYXRocyBmcm9tIEx1bmcgRGlzZWFzZSAoVUspIikgJT4lDQogIGR5TGVnZW5kKHdpZHRoID0gNDAwKQ0KYGBgDQoNCk5nb8OgaSByYSBjw7JuIHLhuqV0IG5oaeG7gXUgdHJvbmcgdGjGsCB2aeG7h246DQoNCmh0dHBzOi8vcnN0dWRpby5naXRodWIuaW8vZHlncmFwaHMvZ2FsbGVyeS10aW1lem9uZXMuaHRtbA0KDQo=