Motivations
Chức năng của Graph là truyền tải các insights cho người tiếp nhận thông tin. Khi nào chức năng này mà không làm được, hoặc gây hiểu lầm, hoặc não người mất quá nhiều thời gian để hiểu insight trong graph được trình bày, thì coi như graph chưa làm được chức năng của nó. Một Graph tốt thì nên là self-explanatory (tự cái hình đã giải thích mọi thứ). Một trong những ví dụ như vậy là hình số 2 của báo cáo có tên ĐÁNH GIÁ TÁC ĐỘNG CỦA COVID-19 ĐẾN NỀN KINH TẾ VÀ CÁC KHUYẾN NGHỊ CHÍNH SÁCH (có thể xem tại đây).
R Codes
Chúng ta có thể cải tiến lại hình số 2 như sau:
# Load some packages:
library(tidyverse)
library(lubridate)
# Data links (source: https://github.com/CSSEGISandData/COVID-19):
rm(list = ls())
link1 <- "https://raw.githubusercontent.com/CSSEGISandData/COVID-19/master/csse_covid_19_data/csse_covid_19_time_series/time_series_covid19_confirmed_global.csv"
# Clean and prepare data:
link1 %>%
read_csv() %>%
rename(Province = `Province/State`, Country = `Country/Region`) %>%
gather(Date, Confirmed, -Province, -Country, -Lat, -Long) %>%
mutate(Date = mdy(Date)) %>%
filter(Country == "Vietnam") -> df_confirmed
df_confirmed %>%
mutate(before = lag(Confirmed)) %>%
mutate(NewConfirmed = Confirmed - before) %>%
slice(-1) -> df_for_g1
end_stage1 <- dmy("13-02-2020")
start_stage2 <- dmy("06-03-2020")
end_stage2 <- dmy("21-03-2020")
df_for_g1 %>%
mutate(Stage = case_when(Date <= end_stage1 ~ "Stage 1",
Date > end_stage1 & Date < start_stage2 ~ "Standby",
Date >= start_stage2 & Date <= end_stage2 ~ "Stage 2",
TRUE ~ "Stage 3")) -> df
df %>%
select(TotalConfirmed = Confirmed, NewConfirmed, Date) %>%
gather(Type, Value, -Date) -> df_plot
df_plot$Date %>% unique() -> my_date
df_labels <- data.frame(Date = my_date)
df_labels %>%
mutate(label_x = case_when(Date == min(Date) ~ as.character(Date),
Date == end_stage1 ~ as.character(end_stage1),
Date == start_stage2 ~ as.character(start_stage2),
Date == end_stage2 ~ as.character(end_stage2),
Date == max(Date) ~ as.character(Date),
TRUE ~ "")) %>%
mutate(Stage = case_when(Date <= end_stage1 ~ "Stage 1",
Date > end_stage1 & Date < start_stage2 ~ "Standby",
Date >= start_stage2 & Date <= end_stage2 ~ "Stage 2",
TRUE ~ "Stage 3")) %>%
mutate(label_x = case_when(label_x == "2020-01-23" ~ "23-01",
label_x == "2020-02-13" ~ "13-02",
label_x == "2020-03-06" ~ "06-03",
label_x == "2020-03-21" ~ "21-03",
label_x == "2020-04-04" ~ "04-04",
TRUE ~ label_x)) -> df_labels
df_for_line <- df_plot %>%
filter(Type == "TotalConfirmed")
df_for_bar <- df_plot %>%
filter(Type != "TotalConfirmed") %>%
filter(Value > 0) %>%
mutate(newValue = Value*5)
# Dual y-axis (https://www.r-graph-gallery.com/line-chart-dual-Y-axis-ggplot2.html,
# https://stackoverflow.com/questions/17148679/construct-a-manual-legend-for-a-complicated-plot,
# https://stackoverflow.com/questions/3099219/ggplot-with-2-y-axes-on-each-side-and-different-scales):
library(extrafont)
my_font <- "Ubuntu Condensed"
my_colors <- c("#377eb8", "#e41a1c")
fill_colors <- c("#fff5f0", "#fcbba1", "#fc9272", "#fb6a4a")
k <- -0.5
h <- 250
ggplot() +
theme_minimal() +
geom_rect(aes(xmin = min(my_date) - 0.5, xmax = end_stage1, ymin = -Inf, ymax = Inf, fill = "Stage 1"), fill = "#a1d99b", alpha = 0.3, show.legend = FALSE) +
geom_rect(aes(xmin = end_stage1, xmax = start_stage2, ymin = -Inf, ymax = Inf, fill = "Standby"), fill = fill_colors[2], alpha = 0.3, show.legend = FALSE) +
geom_rect(aes(xmin = start_stage2, xmax = end_stage2, ymin = -Inf, ymax = Inf, fill = "Stage 2"), fill = fill_colors[3], alpha = 0.3, show.legend = FALSE) +
geom_rect(aes(xmin = end_stage2, xmax = max(my_date) + 0.5, ymin = -Inf, ymax = Inf, fill = "Stage 3"), fill = fill_colors[4], alpha = 0.3, show.legend = FALSE) +
# geom_col(data = df_for_bar, aes(Date, newValue), fill = my_colors[1], width = 0.9) +
# geom_line(data = df_for_line, aes(Date, Value), color = my_colors[2], size = 1.5) +
geom_col(data = df_for_bar, aes(Date, newValue, fill = "New", color = "New"), width = 0.8) +
geom_line(data = df_for_line, aes(Date, Value, color = "Total"), size = 1.5) +
scale_y_continuous(breaks = seq(0, 250, 25), name = "Total", sec.axis = sec_axis(~. / 5, name = "New", breaks = seq(0, 50, 5))) +
scale_colour_manual(name = "", values = my_colors, guide = guide_legend(override.aes = aes(fill = NA))) +
scale_fill_manual(name = "Bar", values = my_colors, guide = "none") +
# guides(color = guide_legend(reverse = TRUE)) +
theme(legend.position = "top") +
theme(panel.grid.minor.y = element_blank()) +
scale_x_date(limits = c(min(my_date) - 0.5, max(my_date) + 0.5), breaks = seq(min(my_date), max(my_date), 1), labels = df_labels$label_x, position = "top", expand = c(0, 0)) +
theme(panel.grid.major.x = element_blank(), panel.grid.minor.x = element_blank()) +
# theme(axis.title.y = element_text(color = my_colors[2], size = 13), axis.title.y.right = element_text(color = my_colors[1], size = 13)) +
theme(axis.text.y = element_text(color = my_colors[2], size = 13), axis.text.y.right = element_text(color = my_colors[1], size = 13)) +
labs(x = NULL, y = NULL, title = "Figure 1: Coronavirus cases in Vietnam, as of 04-04-2020", caption = "Source: Ministry of Health of Vietnam") +
theme(axis.title = element_blank()) +
theme(plot.margin = unit(c(1, 1, 1, 1), "cm")) +
geom_text(data = data.frame(Date = ymd("2020-01-23") + 10, Value = h), aes(Date, Value), label = "Stage 1", size = 4, vjust = k, family = my_font) +
geom_text(data = data.frame(Date = ymd("2020-02-13") + 11, Value = h), aes(Date, Value), label = "Standby", size = 4, vjust = k, family = my_font) +
geom_text(data = data.frame(Date = ymd("2020-03-06") + 7, Value = h), aes(Date, Value), label = "Stage 2", size = 4, vjust = k, family = my_font) +
geom_text(data = data.frame(Date = ymd("2020-03-21") + 7, Value = h), aes(Date, Value), label = "Stage 3", size = 4, vjust = k, family = my_font) +
theme(axis.text.x = element_text(size = 10, family = my_font)) +
theme(axis.text.y = element_text(size = 13, family = my_font)) +
theme(plot.title = element_text(family = my_font, size = 20)) +
theme(plot.caption = element_text(family = my_font, size = 10, color = "grey30")) +
theme(legend.text = element_text(family = my_font, size = 10, face = "bold", color = "grey30"))

LS0tCnRpdGxlOiAnQ29yb25hdmlydXMgY2FzZXMgaW4gVmlldG5hbSAoTGluZSArIEJhciBWZXJzaW9uKScKYXV0aG9yOiAnQXV0aG9yOiBOZ3V5ZW4gQ2hpIER1bmcnCnN1YnRpdGxlOiAiRGFpbHkgR3JhcGggU2VyaWVzIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDogCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKICAgIGhpZ2hsaWdodDogemVuYnVybgogICAgIyBudW1iZXJfc2VjdGlvbnM6IHllcwogICAgdGhlbWU6ICJmbGF0bHkiCiAgICB0b2M6IFRSVUUKICAgIHRvY19mbG9hdDogVFJVRQotLS0KCmBgYHtyIHNldHVwLGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gNikKYGBgCgojIE1vdGl2YXRpb25zCgpDaOG7qWMgbsSDbmcgY+G7p2EgR3JhcGggbMOgIHRydXnhu4FuIHThuqNpIGPDoWMgaW5zaWdodHMgY2hvIG5nxrDhu51pIHRp4bq/cCBuaOG6rW4gdGjDtG5nIHRpbi4gS2hpIG7DoG8gY2jhu6ljIG7Eg25nIG7DoHkgbcOgIGtow7RuZyBsw6BtIMSRxrDhu6NjLCBob+G6t2MgZ8OieSBoaeG7g3UgbOG6p20sIGhv4bq3YyBuw6NvIG5nxrDhu51pIG3huqV0IHF1w6Egbmhp4buBdSB0aOG7nWkgZ2lhbiDEkeG7gyBoaeG7g3UgaW5zaWdodCB0cm9uZyBncmFwaCDEkcaw4bujYyB0csOsbmggYsOgeSwgdGjDrCBjb2kgbmjGsCBncmFwaCBjaMawYSBsw6BtIMSRxrDhu6NjIGNo4bupYyBuxINuZyBj4bunYSBuw7MuIE3hu5l0IEdyYXBoIHThu5F0IHRow6wgbsOqbiBsw6Agc2VsZi1leHBsYW5hdG9yeSAodOG7sSBjw6FpIGjDrG5oIMSRw6MgZ2nhuqNpIHRow61jaCBt4buNaSB0aOG7qSkuIE3hu5l0IHRyb25nIG5o4buvbmcgdsOtIGThu6UgbmjGsCB24bqteSBsw6AgaMOsbmggc+G7kSAyIGPhu6dhIGLDoW8gY8OhbyBjw7MgdMOqbiAqxJDDgU5IIEdJw4EgVMOBQyDEkOG7mE5HIEPhu6ZBIENPVklELTE5IMSQ4bq+TiBO4buATiBLSU5IIFThur4gVsOAIEPDgUMgS0hVWeG6vk4gTkdI4buKIENIw41OSCBTw4FDSCogKGPDsyB0aOG7gyB4ZW0gW3ThuqFpIMSRw6J5XShodHRwczovL25ldS5lZHUudm4vUmVzb3VyY2VzL0RvY3MvU3ViRG9tYWluL0hvbWVQYWdlL1Rob25nQmFvLzIwMjAvMjAyMF80L0Zvcm1hdEZhY3RvcnklMjBQREYlMjBKb2luZXIlMjBCSUElMjAxJTIwYmFvJTIwY2FvXzEucGRmP2ZiY2xpZD1Jd0FSMWY2WXpkZlNhREg2OWxmYTA1SG5Dd3BBdUVGMjVIU0QwcWtobkdKbVNUTXgtTFoyZUFERnF5V3hzKSkuIAoKIyBSIENvZGVzCgpDaMO6bmcgdGEgY8OzIHRo4buDIGPhuqNpIHRp4bq/biBs4bqhaSAgaMOsbmggc+G7kSAyIG5oxrAgc2F1OiAKCmBgYHtyLCBmaWcuaGVpZ2h0PTd9CiMgTG9hZCBzb21lIHBhY2thZ2VzOiAKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkobHVicmlkYXRlKQoKIyBEYXRhIGxpbmtzIChzb3VyY2U6IGh0dHBzOi8vZ2l0aHViLmNvbS9DU1NFR0lTYW5kRGF0YS9DT1ZJRC0xOSk6IApybShsaXN0ID0gbHMoKSkKbGluazEgPC0gImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9DU1NFR0lTYW5kRGF0YS9DT1ZJRC0xOS9tYXN0ZXIvY3NzZV9jb3ZpZF8xOV9kYXRhL2Nzc2VfY292aWRfMTlfdGltZV9zZXJpZXMvdGltZV9zZXJpZXNfY292aWQxOV9jb25maXJtZWRfZ2xvYmFsLmNzdiIKCiMgQ2xlYW4gYW5kIHByZXBhcmUgZGF0YTogCgpsaW5rMSAlPiUgCiAgcmVhZF9jc3YoKSAlPiUgCiAgcmVuYW1lKFByb3ZpbmNlID0gYFByb3ZpbmNlL1N0YXRlYCwgQ291bnRyeSA9IGBDb3VudHJ5L1JlZ2lvbmApICU+JSAKICBnYXRoZXIoRGF0ZSwgQ29uZmlybWVkLCAtUHJvdmluY2UsIC1Db3VudHJ5LCAtTGF0LCAtTG9uZykgJT4lIAogIG11dGF0ZShEYXRlID0gbWR5KERhdGUpKSAlPiUgCiAgZmlsdGVyKENvdW50cnkgPT0gIlZpZXRuYW0iKSAtPiBkZl9jb25maXJtZWQKCgoKZGZfY29uZmlybWVkICU+JSAKICBtdXRhdGUoYmVmb3JlID0gbGFnKENvbmZpcm1lZCkpICU+JSAKICBtdXRhdGUoTmV3Q29uZmlybWVkID0gQ29uZmlybWVkIC0gYmVmb3JlKSAlPiUgCiAgc2xpY2UoLTEpIC0+IGRmX2Zvcl9nMQoKZW5kX3N0YWdlMSA8LSBkbXkoIjEzLTAyLTIwMjAiKQpzdGFydF9zdGFnZTIgPC0gZG15KCIwNi0wMy0yMDIwIikKZW5kX3N0YWdlMiA8LSBkbXkoIjIxLTAzLTIwMjAiKQoKZGZfZm9yX2cxICU+JSAKICBtdXRhdGUoU3RhZ2UgPSBjYXNlX3doZW4oRGF0ZSA8PSBlbmRfc3RhZ2UxIH4gIlN0YWdlIDEiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgRGF0ZSA+IGVuZF9zdGFnZTEgJiBEYXRlIDwgc3RhcnRfc3RhZ2UyIH4gIlN0YW5kYnkiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgRGF0ZSA+PSBzdGFydF9zdGFnZTIgJiBEYXRlIDw9IGVuZF9zdGFnZTIgfiAiU3RhZ2UgMiIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICBUUlVFIH4gIlN0YWdlIDMiKSkgLT4gZGYKCmRmICU+JSAKICBzZWxlY3QoVG90YWxDb25maXJtZWQgPSBDb25maXJtZWQsIE5ld0NvbmZpcm1lZCwgRGF0ZSkgJT4lIAogIGdhdGhlcihUeXBlLCBWYWx1ZSwgLURhdGUpIC0+IGRmX3Bsb3QKCmRmX3Bsb3QkRGF0ZSAlPiUgdW5pcXVlKCkgLT4gbXlfZGF0ZQoKZGZfbGFiZWxzIDwtIGRhdGEuZnJhbWUoRGF0ZSA9IG15X2RhdGUpCgpkZl9sYWJlbHMgJT4lIAogIG11dGF0ZShsYWJlbF94ID0gY2FzZV93aGVuKERhdGUgPT0gbWluKERhdGUpIH4gYXMuY2hhcmFjdGVyKERhdGUpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBEYXRlID09IGVuZF9zdGFnZTEgfiBhcy5jaGFyYWN0ZXIoZW5kX3N0YWdlMSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIERhdGUgPT0gc3RhcnRfc3RhZ2UyIH4gYXMuY2hhcmFjdGVyKHN0YXJ0X3N0YWdlMiksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIERhdGUgPT0gZW5kX3N0YWdlMiB+IGFzLmNoYXJhY3RlcihlbmRfc3RhZ2UyKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRGF0ZSA9PSBtYXgoRGF0ZSkgfiBhcy5jaGFyYWN0ZXIoRGF0ZSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiAiIikpICU+JSAKICBtdXRhdGUoU3RhZ2UgPSBjYXNlX3doZW4oRGF0ZSA8PSBlbmRfc3RhZ2UxIH4gIlN0YWdlIDEiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgRGF0ZSA+IGVuZF9zdGFnZTEgJiBEYXRlIDwgc3RhcnRfc3RhZ2UyIH4gIlN0YW5kYnkiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgRGF0ZSA+PSBzdGFydF9zdGFnZTIgJiBEYXRlIDw9IGVuZF9zdGFnZTIgfiAiU3RhZ2UgMiIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICBUUlVFIH4gIlN0YWdlIDMiKSkgJT4lIAogIG11dGF0ZShsYWJlbF94ID0gY2FzZV93aGVuKGxhYmVsX3ggPT0gIjIwMjAtMDEtMjMiIH4gIjIzLTAxIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxfeCA9PSAiMjAyMC0wMi0xMyIgfiAiMTMtMDIiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbF94ID09ICIyMDIwLTAzLTA2IiB+ICIwNi0wMyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVsX3ggPT0gIjIwMjAtMDMtMjEiIH4gIjIxLTAzIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxfeCA9PSAiMjAyMC0wNC0wNCIgfiAiMDQtMDQiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUUlVFIH4gbGFiZWxfeCkpIC0+IGRmX2xhYmVscwoKCgpkZl9mb3JfbGluZSA8LSBkZl9wbG90ICU+JSAKICBmaWx0ZXIoVHlwZSA9PSAiVG90YWxDb25maXJtZWQiKQoKZGZfZm9yX2JhciA8LSBkZl9wbG90ICU+JSAKICBmaWx0ZXIoVHlwZSAhPSAiVG90YWxDb25maXJtZWQiKSAlPiUgCiAgZmlsdGVyKFZhbHVlID4gMCkgJT4lIAogIG11dGF0ZShuZXdWYWx1ZSA9IFZhbHVlKjUpCgojIER1YWwgeS1heGlzIChodHRwczovL3d3dy5yLWdyYXBoLWdhbGxlcnkuY29tL2xpbmUtY2hhcnQtZHVhbC1ZLWF4aXMtZ2dwbG90Mi5odG1sLCAKIyBodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy8xNzE0ODY3OS9jb25zdHJ1Y3QtYS1tYW51YWwtbGVnZW5kLWZvci1hLWNvbXBsaWNhdGVkLXBsb3QsIAojIGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzMwOTkyMTkvZ2dwbG90LXdpdGgtMi15LWF4ZXMtb24tZWFjaC1zaWRlLWFuZC1kaWZmZXJlbnQtc2NhbGVzKTogCgoKbGlicmFyeShleHRyYWZvbnQpCm15X2ZvbnQgPC0gIlVidW50dSBDb25kZW5zZWQiCgpteV9jb2xvcnMgPC0gYygiIzM3N2ViOCIsICIjZTQxYTFjIikKZmlsbF9jb2xvcnMgPC0gYygiI2ZmZjVmMCIsICIjZmNiYmExIiwgIiNmYzkyNzIiLCAiI2ZiNmE0YSIpCmsgPC0gLTAuNQpoIDwtIDI1MAoKZ2dwbG90KCkgKyAKICB0aGVtZV9taW5pbWFsKCkgKyAKICBnZW9tX3JlY3QoYWVzKHhtaW4gPSBtaW4obXlfZGF0ZSkgLSAwLjUsIHhtYXggPSBlbmRfc3RhZ2UxLCB5bWluID0gLUluZiwgeW1heCA9IEluZiwgZmlsbCA9ICJTdGFnZSAxIiksIGZpbGwgPSAiI2ExZDk5YiIsIGFscGhhID0gMC4zLCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArIAogIGdlb21fcmVjdChhZXMoeG1pbiA9IGVuZF9zdGFnZTEsIHhtYXggPSBzdGFydF9zdGFnZTIsIHltaW4gPSAtSW5mLCB5bWF4ID0gSW5mLCBmaWxsID0gIlN0YW5kYnkiKSwgZmlsbCA9IGZpbGxfY29sb3JzWzJdLCBhbHBoYSA9IDAuMywgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAKICBnZW9tX3JlY3QoYWVzKHhtaW4gPSBzdGFydF9zdGFnZTIsIHhtYXggPSBlbmRfc3RhZ2UyLCB5bWluID0gLUluZiwgeW1heCA9IEluZiwgZmlsbCA9ICJTdGFnZSAyIiksIGZpbGwgPSBmaWxsX2NvbG9yc1szXSwgYWxwaGEgPSAwLjMsIHNob3cubGVnZW5kID0gRkFMU0UpICsgCiAgZ2VvbV9yZWN0KGFlcyh4bWluID0gZW5kX3N0YWdlMiwgeG1heCA9IG1heChteV9kYXRlKSArIDAuNSwgeW1pbiA9IC1JbmYsIHltYXggPSBJbmYsIGZpbGwgPSAiU3RhZ2UgMyIpLCBmaWxsID0gZmlsbF9jb2xvcnNbNF0sIGFscGhhID0gMC4zLCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArIAogICMgZ2VvbV9jb2woZGF0YSA9IGRmX2Zvcl9iYXIsIGFlcyhEYXRlLCBuZXdWYWx1ZSksIGZpbGwgPSBteV9jb2xvcnNbMV0sIHdpZHRoID0gMC45KSArCiAgIyBnZW9tX2xpbmUoZGF0YSA9IGRmX2Zvcl9saW5lLCBhZXMoRGF0ZSwgVmFsdWUpLCBjb2xvciA9IG15X2NvbG9yc1syXSwgc2l6ZSA9IDEuNSkgKwogIGdlb21fY29sKGRhdGEgPSBkZl9mb3JfYmFyLCBhZXMoRGF0ZSwgbmV3VmFsdWUsIGZpbGwgPSAiTmV3IiwgY29sb3IgPSAiTmV3IiksIHdpZHRoID0gMC44KSArCiAgZ2VvbV9saW5lKGRhdGEgPSBkZl9mb3JfbGluZSwgYWVzKERhdGUsIFZhbHVlLCBjb2xvciA9ICJUb3RhbCIpLCBzaXplID0gMS41KSArICAKICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDI1MCwgMjUpLCBuYW1lID0gIlRvdGFsIiwgc2VjLmF4aXMgPSBzZWNfYXhpcyh+LiAvIDUsIG5hbWUgPSAiTmV3IiwgYnJlYWtzID0gc2VxKDAsIDUwLCA1KSkpICsgCiAgc2NhbGVfY29sb3VyX21hbnVhbChuYW1lID0gIiIsIHZhbHVlcyA9IG15X2NvbG9ycywgZ3VpZGUgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gYWVzKGZpbGwgPSBOQSkpKSArIAogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWUgPSAiQmFyIiwgdmFsdWVzID0gbXlfY29sb3JzLCBndWlkZSA9ICJub25lIikgKyAKICAjIGd1aWRlcyhjb2xvciA9IGd1aWRlX2xlZ2VuZChyZXZlcnNlID0gVFJVRSkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpICsgCiAgdGhlbWUocGFuZWwuZ3JpZC5taW5vci55ID0gZWxlbWVudF9ibGFuaygpKSArIAogIHNjYWxlX3hfZGF0ZShsaW1pdHMgPSBjKG1pbihteV9kYXRlKSAtIDAuNSwgbWF4KG15X2RhdGUpICsgMC41KSwgYnJlYWtzID0gc2VxKG1pbihteV9kYXRlKSwgbWF4KG15X2RhdGUpLCAxKSwgbGFiZWxzID0gZGZfbGFiZWxzJGxhYmVsX3gsIHBvc2l0aW9uID0gInRvcCIsIGV4cGFuZCA9IGMoMCwgMCkpICsgCiAgdGhlbWUocGFuZWwuZ3JpZC5tYWpvci54ID0gZWxlbWVudF9ibGFuaygpLCBwYW5lbC5ncmlkLm1pbm9yLnggPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgIyB0aGVtZShheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoY29sb3IgPSBteV9jb2xvcnNbMl0sIHNpemUgPSAxMyksIGF4aXMudGl0bGUueS5yaWdodCA9IGVsZW1lbnRfdGV4dChjb2xvciA9IG15X2NvbG9yc1sxXSwgc2l6ZSA9IDEzKSkgKyAgCiAgdGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoY29sb3IgPSBteV9jb2xvcnNbMl0sIHNpemUgPSAxMyksIGF4aXMudGV4dC55LnJpZ2h0ID0gZWxlbWVudF90ZXh0KGNvbG9yID0gbXlfY29sb3JzWzFdLCBzaXplID0gMTMpKSArIAogIGxhYnMoeCA9IE5VTEwsIHkgPSBOVUxMLCB0aXRsZSA9ICJGaWd1cmUgMTogQ29yb25hdmlydXMgY2FzZXMgaW4gVmlldG5hbSwgYXMgb2YgMDQtMDQtMjAyMCIsIGNhcHRpb24gPSAiU291cmNlOiBNaW5pc3RyeSBvZiBIZWFsdGggb2YgVmlldG5hbSIpICsgCiAgdGhlbWUoYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICB0aGVtZShwbG90Lm1hcmdpbiA9IHVuaXQoYygxLCAxLCAxLCAxKSwgImNtIikpICsgCiAgZ2VvbV90ZXh0KGRhdGEgPSBkYXRhLmZyYW1lKERhdGUgPSB5bWQoIjIwMjAtMDEtMjMiKSArIDEwLCBWYWx1ZSA9IGgpLCBhZXMoRGF0ZSwgVmFsdWUpLCBsYWJlbCA9ICJTdGFnZSAxIiwgc2l6ZSA9IDQsIHZqdXN0ID0gaywgZmFtaWx5ID0gbXlfZm9udCkgKyAKICBnZW9tX3RleHQoZGF0YSA9IGRhdGEuZnJhbWUoRGF0ZSA9IHltZCgiMjAyMC0wMi0xMyIpICsgMTEsIFZhbHVlID0gaCksIGFlcyhEYXRlLCBWYWx1ZSksIGxhYmVsID0gIlN0YW5kYnkiLCBzaXplID0gNCwgdmp1c3QgPSBrLCBmYW1pbHkgPSBteV9mb250KSArIAogIGdlb21fdGV4dChkYXRhID0gZGF0YS5mcmFtZShEYXRlID0geW1kKCIyMDIwLTAzLTA2IikgKyA3LCBWYWx1ZSA9IGgpLCBhZXMoRGF0ZSwgVmFsdWUpLCBsYWJlbCA9ICJTdGFnZSAyIiwgc2l6ZSA9IDQsIHZqdXN0ID0gaywgZmFtaWx5ID0gbXlfZm9udCkgKyAKICBnZW9tX3RleHQoZGF0YSA9IGRhdGEuZnJhbWUoRGF0ZSA9IHltZCgiMjAyMC0wMy0yMSIpICsgNywgVmFsdWUgPSBoKSwgYWVzKERhdGUsIFZhbHVlKSwgbGFiZWwgPSAiU3RhZ2UgMyIsIHNpemUgPSA0LCB2anVzdCA9IGssIGZhbWlseSA9IG15X2ZvbnQpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwLCBmYW1pbHkgPSBteV9mb250KSkgKyAKICB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTMsIGZhbWlseSA9IG15X2ZvbnQpKSArIAogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gbXlfZm9udCwgc2l6ZSA9IDIwKSkgKyAKICB0aGVtZShwbG90LmNhcHRpb24gPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gbXlfZm9udCwgc2l6ZSA9IDEwLCBjb2xvciA9ICJncmV5MzAiKSkgKyAKICB0aGVtZShsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSBteV9mb250LCBzaXplID0gMTAsIGZhY2UgPSAiYm9sZCIsIGNvbG9yID0gImdyZXkzMCIpKQogIAoKCmBgYAoK