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ư:
- Tự động vẽ biều đồ với dữ liệu chuỗi thời gian dạng xts (hoặc bất kỳ đối tượng nào khác được chuyển về dạng xts)
- Cấu hình trục tọa độ (axis) mạnh mẽ và hiển thị hàng loạt(bao gồm tùy chọn second Y-axis)
- Nhiều tính năng tương tác, bao gồm zoom/pan và series/point highlighting
- Hiển thị upper/lower bars(ví dụng như khoảng dự báo-prediction intervals) quang chuỗi
- Những biều đồ khác nhau được che phủ bởi (shaded regions, event lines, và point annotations)
- Sử dụng R Console giống như các biểu đồ tiêu chuẩn của R(như là qua Rstudio Viewer)
- Nhúng trong R Markdown documents và Shiny Web applications dễ dàng và trơn tru.
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=