Objektif
Menampilkan data menggunakan R untuk memperoleh informasi yang dibutuhkan. Pada kesempatan ini akan dibahas membuat visualisasi menggunakan data kasus Covid19 di berbagai negara dan juga Indonesia. Package yang akan digunakan adalah ggplot2.
Data & Script
Beberapa hal yang dibutuhkan pada kesempatan ini :
Program R dan RStudio, diharapkan minimal R dengan versi 3.6.2 dan beberapa package yang dibutuhkan.
Anda dapat memperoleh materi workshop ini (data, script dan file Rmd) di repository github.
- Pilih Clone or download
- Kemudian pilih Download ZIP.
- Setelah selesai download, ekstrak file zip tersebut dan double-click file
coviz.Rproj.
Atau Anda juga dapat mengakses project tersebut yang sudah tersedia di RStudio Cloud https://rstudio.cloud/project/1333221. Anda perlu login atau membuat akun terlebih dahulu jika belum mempunyai akun RStudio Cloud. Semua materi dan package yang akan digunakan sudah tersedia.
Jika Anda ingin memperbarui data yang sudah disediakan, Anda dapat membuka dan menjalankan script pada file “get_raw_data.R” di dalam folder script. Tentu saja Anda perlu install terlebih dahulu package yang digunakan jika Anda belum mempunyai package tersebut pada library R Anda.
Package tambahan yang dibutuhkan untuk membuat visualisasi pada kesempatan ini adalah {dplyr}, {ggplot2} dan {scales}. Jika Anda belum menginstall package tersebut, silahkan jalankan perintah berikut.
install.packages(c("dplyr", "ggplot2", "scales"))
Selanjutnya kita dapat menggunakan package tersebut dengan menjalankan
library(dplyr)
library(ggplot2)
library(scales)
Pertama, kita siapkan data yang akan digunakan dengan cara import data dari file CSV yang sudah disediakan di folder data. Ada 2 file yang akan digunakan, yaitu world_covid.csv dan dailynational.csv. Import data tersebut menjadi dataframe bernama world_covid menggunakan fungsi read.csv() dengan argument file = adalah lokasi dan nama file CSV yang akan diimport, header = TRUE karena baris pertama adalah nama kolom, dan stringsAsFactors = FALSE agar semua kolom bertipe character di file CSV diimport sebagai character, bukan sebagai factor. Untuk lebih jelas tentang factor Anda bisa membaca dokumentasi factor.
Kemudian kita lihat struktur dari dataframe (banyaknya observasi dan variable, nama variable dan tipenya) yang sudah diimport dengan fungsi str(). Data world_covid adalah data mengenai banyaknya kasus covid di setiap negara/negara bagian dan populasi penduduknya yang diambil dari https://www.worldometers.info/coronavirus dan https://www.worldometers.info/world-population.
world_covid <- read.csv(file = "data/world_covid.csv", header = TRUE, stringsAsFactors = FALSE)
str(world_covid)
'data.frame': 209 obs. of 25 variables:
$ country : chr "USA" "Brazil" "Russia" "Spain" ...
$ total_cases : int 1828806 501985 405843 286509 274762 232997 190603 188882 183452 163942 ...
$ new_cases : int 11986 3545 9268 201 1936 333 8776 257 158 839 ...
$ total_deaths : int 105927 28872 4693 27127 38489 33415 5406 28802 8602 4540 ...
$ new_deaths : int 370 38 138 2 113 75 221 31 2 25 ...
$ total_recovered : int 539173 205371 171883 196958 0 157507 91830 68355 165200 127973 ...
$ active_cases : int 1183706 267742 229267 62424 0 42075 93367 91725 9650 31429 ...
$ serious_critical : int 17163 8318 2300 617 1559 435 8944 1319 720 648 ...
$ tot_cases_1m_pop : int 5528 2363 2781 6128 4049 3853 138 2894 2190 1946 ...
$ deaths_1m_pop : int 320 136 32 580 567 553 4 441 103 54 ...
$ total_tests : int 17505657 930013 10643124 4063843 4285738 3878739 3737027 1384633 3952971 2039194 ...
$ tests_1m_pop : int 52913 4378 72933 86921 63159 64144 2710 21217 47193 24201 ...
$ population : int 330838184 212430396 145929337 46753295 67855909 60469020 1378826256 65261548 83761616 84259813 ...
$ continent : chr "North America" "South America" "Europe" "Europe" ...
$ fatality_rate : num 0.0579 0.0575 0.0116 0.0947 0.1401 ...
$ population_2020 : int 331002651 212559417 145934462 46754778 67886011 60461826 1380004385 65273511 83783942 84339067 ...
$ yearly_change : num 0.0059 0.0072 0.0004 0.0004 0.0053 -0.0015 0.0099 0.0022 0.0032 0.0109 ...
$ net_change : int 1937734 1509890 62206 18002 355839 -88249 13586631 143783 266897 909452 ...
$ density_km2 : int 36 25 9 94 281 206 464 119 240 110 ...
$ land_area_km2 : int 9147420 8358140 16376870 498800 241930 294140 2973190 547557 348560 769630 ...
$ migrants_net : int 954806 21200 182456 40000 260650 148943 -532687 36527 543822 283922 ...
$ fert_rate : num 1.78 1.74 1.82 1.33 1.75 ...
$ med_age : int 38 33 40 45 40 47 28 42 46 32 ...
$ urban_pop_percent: num 0.828 0.876 0.737 0.803 0.832 0.695 0.35 0.815 0.763 0.757 ...
$ world_share : num 0.042 0.027 0.019 0.006 0.009 0.008 0.177 0.008 0.011 0.011 ...
Berikutnya kita lihat beberapa baris pertama dari data world_covid. Menggunakan fungsi head() untuk menampilkan beberapa baris pertama. Dengan n = 10 artinya menapilkan 10 baris pertama dari data world_covid.
head(world_covid, n = 10)
Top Fatality Rate Country
Dengan visualisasi yang pertama ini, kita akan menjawab pertanyaan “Negara apa saja yang memiliki Fatality Rate tertinggi?” dengan chart seperti berikut.

Untuk menghasilkan visualisasi seperti di atas kita akan menggunakan data world_covid dengan country dan fatality_rate sebagai variable untuk membuat visualisasi berupa barchart. Data yang digunakan adalah seperti berikut.
vis1 <- world_covid %>%
select(country, fatality_rate)
vis1
ggplot(data = vis1, mapping = aes(x = country, y = fatality_rate)) +
geom_bar(stat = "identity")

Sulit untuk memperoleh informasi yang baik dari hasil visualisasi di atas. Kita akan coba untuk membuat label nama negara-negara lebih terlihat dengan merubah posisi label nama negara miring 90 derajat.
# rotate x-axis label 90 deg
ggplot(data = vis1, mapping = aes(x = country, y = fatality_rate)) +
geom_bar(stat = "identity") +
theme(axis.text.x = element_text(angle = 90))

Ternyata masih belum cukup terlihat dan kita belum bisa mendapatkan informasi apapun dari chart tersebut.
Kita akan coba untuk mengurutkan nama-nama negara berdasarkan Fatality Rate, yaitu variable fatality_rate. Kita gunakan fungsi reorder(country, fatality_rate) untuk mengurutkan barchart berdasarkan Fatality Rate.
# sorted barchart
ggplot(data = vis1, mapping = aes(x = reorder(country, fatality_rate), y = fatality_rate)) +
geom_bar(stat = "identity") +
theme(axis.text.x = element_text(angle = 90))

Agar informasi dapat diperoleh dengan baik maka tidak semua negara akan kita tampilkan. Misalnya kita hanya perlu menampilkan daftar 20 negara berdasarkan Fatality Rate tertinggi. Dengan fungsi arrange() dan desc() dari package {dplyr} dan fungsi head() dengan n = 20 kita akan mendapatkan 20 negara dengan Fatality Rate tertinggi.
topn <- vis1 %>%
arrange(desc(fatality_rate)) %>%
head(n = 20)
topn
Selanjutnya dari data tersebut kita buat visualisasinya.
# sorted barchart
ggplot(data = topn, mapping = aes(x = reorder(country, fatality_rate), y = fatality_rate)) +
geom_bar(stat = "identity") +
theme(axis.text.x = element_text(angle = 90))

Kita sudah dapat menampilkan 20 negara dengan Fatality Rate tertinggi. Namun tentu saja untuk publikasi ataupun dari segi estetika masih perlu ditambahkan beberapa “aksesoris”.
Selanjutnya kita akan menambahkan judul chart dan merubah judul pada masing-masing sumbu x dan y. Kita dapat menambahkan judul chart dan masing-masing sumbu, kita dapat menggunakan fungsi labs(). Argumen title = untuk judul utama, x = untuk judul pada sumbu x, dan y = untuk judul sumbu y. Untuk judul sumbu, tetap mengikuti pada aes(). Karena
ggplot(data = topn, mapping = aes(x = reorder(country, fatality_rate), y = fatality_rate)) +
geom_bar(stat = "identity") +
theme(axis.text.x = element_text(angle = 90)) +
labs(title = "Top Fatality Rate Countries",
x = "Country",
y = "Fatality Rate")

Nilai Fatality Rate akan lebih mudah dimengerti jika ditampilkan dalam format persentase. Karena variable Fatality Rate ada pada sumbu y dan merupakan variable kontinu, maka kita dapat gunakan fungsi scale_y_continuous() dari {ggplot2}. Pada argumen label = kita gunakan fungsi percent_format() dari package {scales} agar tampilan label di sumbu Fatality Rate berupa persentase. Argumen accuracy = 1 pada fungsi percent_format() menandakan tidak ada angka dibelakang desimal.
# Custom axis scale
ggplot(data = topn, mapping = aes(x = reorder(country, fatality_rate), y = fatality_rate)) +
geom_bar(stat = "identity") +
theme(axis.text.x = element_text(angle = 90)) +
labs(title = "Top Fatality Rate Countries",
x = "Country",
y = "Fatality Rate") +
scale_y_continuous(labels = scales::percent_format(accuracy = 1))

Jika Anda ingin format persentase dengan satu angka di belakang desimal, gunakan accuracy = 0.1. Jika ingin dua angka di belakang desimal maka accuracy = 0.01, dan seterusnya.
Saya pribadi lebih suka ketika membuat visualisasi seperti pada kasus ini, barchart dengan label yang cukup panjang, membuatnya menjadi barchart horizontal. Karena orang awam pun akan dengan mudah mendapatkan informasi yang ingin kita sampaikan melalui visualisasi tersebut. Untuk membuat barchart horizontal, kita dapat digunakan fungsi coord_flip() dari package {ggplot2}. Fungsi ini akan mentransformasi sumbu-x (horizontal) menjadi sumbu-y (vertikal), dan sebaliknya.
Coba perhatikan bar negara Yaman. Pada chart sebelumnya bar negara Yaman berada pada ujung kanan sumbu-x. Dengan fungsi coord_flip() menjadi di ujung atas sumbu-y. Artinya coord_flip() mentransformasi dengan titik pojok kiri bawah sebagai titik tumpunya.
# Custom title - axis title
ggplot(data = topn, mapping = aes(x = reorder(country, fatality_rate), y = fatality_rate)) +
geom_bar(stat = "identity") +
theme(axis.text.x = element_text(angle = 90)) +
labs(title = "Top Fatality Rate Countries",
x = "Country",
y = "Fatality Rate") +
scale_y_continuous(labels = scales::percent_format(accuracy = 1)) +
coord_flip()

Tampilan label pada sumbu Fatality Rate harusnya tetap horizontal. Maka kita tidak perlu menggunakan theme(axis.text.x = element_text(angle = 90)) lagi.
# Custom title - axis title
ggplot(data = topn, mapping = aes(x = reorder(country, fatality_rate), y = fatality_rate)) +
geom_bar(stat = "identity") +
labs(title = "Top Fatality Rate Countries",
x = "Country",
y = "Fatality Rate") +
scale_y_continuous(labels = scales::percent_format(accuracy = 1)) +
coord_flip()

Kita dapat mengatur sebuah theme dari ggplot2 agar dapat digunakan pada chart ggplot yang kita hasilkan berikutnya tanpa harus menuliskan + theme_minimal() di setiap script dengan theme_set(theme_minimal()). Namun pada kesempatan ini di setiap script yang menghasilkan chart dari ggplot2 akan dituliskan + theme_minimal().
# Custom theme
ggplot(data = topn, mapping = aes(x = reorder(country, fatality_rate), y = fatality_rate)) +
geom_bar(stat = "identity") +
labs(title = "Top Fatality Rate Countries",
x = "Country",
y = "Fatality Rate") +
scale_y_continuous(labels = scales::percent_format(accuracy = 1)) +
coord_flip() +
theme_minimal()

Langkah berikutnya kita akan merubah warna bar dan grid dari chart tersebut.
Pertama kita rubah warna bar terlebih dahulu. Warna bar bisa kita rubah dengan menambahkan argumen fill =. Disini saya pribadi menggunakan warna "coral" dengan alpha = 0.7. Nilai alpha ini adalah tingkat transparansi warna atau opacity, antara 0 dan 1. Anda bisa memilih menggunakan warna lain seperti "red", "yellow", "green", "skyblue" dan lain-lain. Bisa juga menggunakan kode warna Hex (misalnya #E1404B) atau RGB (misalnya rgb(225, 64, 75, maxColorValue = 255)). Saya pribadi sering menggunakan color picker dari google search untuk memilih warna yang ingin digunakan.
# Change color & grid
ggplot(data = topn, mapping = aes(x = reorder(country, fatality_rate), y = fatality_rate)) +
geom_bar(stat = "identity", fill = "coral", alpha = 0.7) +
labs(title = "Top Fatality Rate Countries",
x = "Country",
y = "Fatality Rate") +
scale_y_continuous(labels = scales::percent_format(accuracy = 1)) +
coord_flip() +
theme_minimal()

Selanjutnya kita hilangkan garis major dan minor horizontal dari sumbu y dan garis minor vertikal dari sumbu x. Kita dapat melakukan ini dengan menggunakan fungsi theme() dari {ggplot2}. Argumen yang digunakan adalah panel.grid.major.y =, panel.grid.minor.y = dan panel.grid.major.x =. Fungsi element_blank() untuk setiap argumen tersebut menandakan atribut yang bersesuaian dengan argumen yang digunakan ditiadakan.
# Change color & grid
ggplot(data = topn, mapping = aes(x = reorder(country, fatality_rate), y = fatality_rate)) +
geom_bar(stat = "identity", fill = "coral", alpha = 0.7) +
labs(title = "Top Fatality Rate Countries",
x = "Country",
y = "Fatality Rate") +
scale_y_continuous(labels = scales::percent_format(accuracy = 1)) +
coord_flip() +
theme_minimal() +
theme(panel.grid.major.y = element_blank(),
panel.grid.minor.y = element_blank(),
panel.grid.minor.x = element_blank())

Kita sudah membuat chart yang cukup baik. Namun saya ingin menambahkan nilai Fatality Rate di setiap ujung bar agar informasi yang disampaikan lebih lengkap. Kita dapat gunakan fungsi geom_text() atau geom_label(), tinggal disesuaikan dengan selera masing-masing. Karena nilai dari variable fatality_rate antara 0 dan 1, maka perlu dikalikan 100 untuk mendapatkan nilai persentase sebagai text kemudian ditambahkan tanda “%” dengan fungsi paste0(). Untuk menyesuaikan ukuran text-nya kita gunakan argumen size =. Semakin besar nilai size = maka semakin besar juga ukuran text. Argumen hjust = -0.1 untuk mengatur posisi secara horizontal.
# Add label & y-limit
ggplot(data = topn, mapping = aes(x = reorder(country, fatality_rate), y = fatality_rate)) +
geom_bar(stat = "identity", fill = "coral", alpha = 0.7) +
labs(title = "Top Fatality Rate Countries",
x = "Country",
y = "Fatality Rate") +
scale_y_continuous(labels = scales::percent_format(accuracy = 1)) +
coord_flip() +
theme_minimal() +
theme(panel.grid.major.y = element_blank(),
panel.grid.minor.y = element_blank(),
panel.grid.minor.x = element_blank()) +
geom_text(aes(label = paste0(fatality_rate*100, "%")), size = 3, hjust = -0.1)

Seperti yang dilihat, ada text yang terpotong karena sumbu Fatality Rate yang kurang panjang. Kita dapat mengatur batas (limits) dari sumbu Fatality Rate ini dengan menambahkan argumen limits = c(<min>, <max>) pada fungsi scale_y_continuous(). Nilai <min> dan <max> adalah pangkal dan batas ujung sumbu. Karena nilai fatality_rate antara 0 dan 1 dan saya ingin batasnya sampai nilai 30% maka limits = c(0, 0.30). Saya pilih 30% hanya agar semua text dari masing-masing bar tidak ada yang terpotong.
# Add label & y-limit
ggplot(data = topn, mapping = aes(x = reorder(country, fatality_rate), y = fatality_rate)) +
geom_bar(stat = "identity", fill = "coral", alpha = 0.7) +
labs(title = "Top Fatality Rate Countries",
x = "Country",
y = "Fatality Rate") +
scale_y_continuous(labels = scales::percent_format(accuracy = 1), limits = c(0, 0.30)) +
coord_flip() +
theme_minimal() +
theme(panel.grid.major.y = element_blank(),
panel.grid.minor.y = element_blank(),
panel.grid.minor.x = element_blank()) +
geom_text(aes(label = paste0(fatality_rate*100, "%")), size = 3, hjust = -0.1)

Kita juga bisa membuat visualisasi seperti di atas berdasarkan banyaknya kasus yang terjadi di suatu negara.
# By Total Cases
## top 20 barchart
top20 <- world_covid %>%
arrange(desc(total_cases)) %>%
head(n = 20)
ggplot(data = top20, mapping = aes(x = reorder(country, total_cases), y = total_cases)) +
geom_bar(stat = "identity", fill = "coral", alpha = 0.7) +
geom_text(aes(label = formatC(total_cases, big.mark = ",", decimal.mark = ".", format = "d")), size = 3, hjust = -0.1) +
coord_flip() +
labs(title = "Top 20 Total Cases Countries",
x = "Country",
y = "Total Cases") +
theme_minimal() +
scale_y_continuous(labels = scales::number_format(big.mark = ","), limits = c(0, 2.0*10^6)) +
theme(panel.grid.major.y = element_blank(),
panel.grid.minor.y = element_blank(),
panel.grid.minor.x = element_blank())

Median Population Age vs Fatality Rate
Pada visualisasi kedua ini kita akan melakukan eksplorasi data world_covid menggunakan beberapa variable dengan visualisasi berupa bubble chart seperti berikut ini.

Untuk membuat bubble chart di ggplot2 kita dapat menggunakan geom_point(). Dasarnya adalah membuat scatter plot dengan geom_point() kemudian ukuran titiknya disesuaikan dengan nilai masing-masing. Untuk membuat scatter plot dibutuhkan dua variable numerik untuk menentukan koordinat x (horizontal) dan y (vertikal). Pada kasus ini kita akan menggunakan variable med_age yang merupakan median usia dari seluruh populasi penduduk di suatu negara sebagai sumbu x, dan variable fatality_rate sebagai sumbu y.
# Scatter plot
ggplot(data = world_covid, mapping = aes(x = med_age, y = fatality_rate)) +
geom_point() +
theme_minimal()

Belum banyak informasi yang bisa kita peroleh dari visualisasi tersebut kecuali satu titik yang berada paling atas yang jauh dari titik yang lain.
Kita atur tingkat transparansi dari masing-masing titik tersebut agar dapat terlihat apakah ada titik yang bertumpuk dengan alpha = 0.3.
# Point opacity
ggplot(data = world_covid, mapping = aes(x = med_age, y = fatality_rate)) +
geom_point(alpha = 0.3) +
theme_minimal()

Ternyata ada beberapa negara yang nilai med_age dan fatality_rate-nya hampir sama. Terlihat dari adanya titik yang lebih tebal warnanya dibanding titik lain karena titiknya bertumpuk.
Berikutnya kita atur ukuran dari masing-masing titik sesuai dengan nilai kepadatan penduduk per Km^2 pada negara tersebut, yaitu variable denisty_km2. Karena nilai yang akan kita gunakan adalah sebuah variable maka kita harus menuliskannya dalam sebuah mapping aes().
# Bubble chart size
ggplot(data = world_covid, mapping = aes(x = med_age, y = fatality_rate)) +
geom_point(aes(size = density_km2), alpha = 0.3) +
theme_minimal()

Kita juga bisa menyesuaikan warna masing-masing titik berdasarkan Continent-nya dengan menambahkan color = continent pada aes().
# Buble chart color
ggplot(data = world_covid, mapping = aes(x = med_age, y = fatality_rate)) +
geom_point(aes(size = density_km2, color = continent), alpha = 0.3) +
theme_minimal()

Karena Continent Europe dan North America mempunyai koordinat yang berdekatan dan warna yang hampir sama sehingga agak sulit membedakan continent tersebut. Kita dapat atur sendiri warna untuk masing-masing continent dengan fungsi scale_color_manual() atau scale_colour_manual().
# Custom color manual
ggplot(data = world_covid, mapping = aes(x = med_age, y = fatality_rate)) +
geom_point(aes(size = density_km2, color = continent), alpha = 0.3) +
theme_minimal() +
scale_colour_manual(values = c("Africa" = "#f53d3d",
"Asia" = "#f7a41e",
"Australia/Oceania" = "#4bdb57",
"Europe" = "#3da2eb",
"North America" = "#f227eb",
"South America" = "#8a0404"))

Tambahkan judul utama, judul sumbu. Ganti juga judul legend. Kita bisa lakukan hal ini dengan menggunakan fungsi labs().
# Custom title & axis title
ggplot(data = world_covid, mapping = aes(x = med_age, y = fatality_rate)) +
geom_point(aes(size = density_km2, color = continent), alpha = 0.3) +
theme_minimal() +
scale_colour_manual(values = c("Africa" = "#f53d3d",
"Asia" = "#f7a41e",
"Australia/Oceania" = "#4bdb57",
"Europe" = "#3da2eb",
"North America" = "#f227eb",
"South America" = "#8a0404")) +
labs(title = "Median Population Age vs Fatality Rate",
x = "Median Population Age",
y = "Fatality Rate",
size = "Population Density (/km2)",
color = "Continent")

Ganti format judul pada sumbu Fatality Rate menjadi persentase. Gunakan fungsi scale_y_continuous() seperti pada chart sebelumnya.
# Buble chart custom axis title
ggplot(data = world_covid, mapping = aes(x = med_age, y = fatality_rate)) +
geom_point(aes(size = density_km2, color = continent), alpha = 0.3) +
theme_minimal() +
labs(title = "Median Population Age vs Fatality Rate",
x = "Median Population Age",
y = "Fatality Rate",
size = "Population Density (/km2)",
color = "Continent") +
scale_colour_manual(values = c("Africa" = "#f53d3d",
"Asia" = "#f7a41e",
"Australia/Oceania" = "#4bdb57",
"Europe" = "#3da2eb",
"North America" = "#f227eb",
"South America" = "#8a0404")) +
scale_y_continuous(labels = scales::percent_format(accuracy = 1))

Jika visualisasi di atas masih dirasa sulit untuk memisahkan antar continent maka Anda dapat menggunakan fungsi facet_wrap( ~ continent).
# Buble chart custom axis title
ggplot(data = world_covid, mapping = aes(x = med_age, y = fatality_rate)) +
geom_point(aes(size = density_km2, color = continent), alpha = 0.6) +
theme_minimal() +
labs(title = "Median Population Age vs Fatality Rate",
x = "Median Population Age",
y = "Fatality Rate",
size = "Population Density (/km2)",
color = "Continent") +
scale_colour_manual(values = c("Africa" = "#f53d3d",
"Asia" = "#f7a41e",
"Australia/Oceania" = "#4bdb57",
"Europe" = "#3da2eb",
"North America" = "#f227eb",
"South America" = "#8a0404")) +
scale_y_continuous(labels = scales::percent_format(accuracy = 1)) +
facet_wrap(~ continent)

Indonesia Daily Cases Trend
Terakhir, kita akan membuat chart yang dapat menunjukkan pola penambahan kasus harian di Indonesia. Data yang digunakan adalah data dailynational yg diolah dari sumber https://bnpb-inacovid19.hub.arcgis.com/ melalui API.
Kita import kemudian lihat struktur dan beberapa baris pertama dari data tersebut.
dailynational <- read.csv("data/dailynational.csv", header = TRUE, stringsAsFactors = FALSE)
dailynational$dates <- as.Date(dailynational$dates)
str(dailynational)
'data.frame': 91 obs. of 18 variables:
$ dates : Date, format: "2020-03-02" "2020-03-03" ...
$ total_cases : int 2 2 2 2 4 4 6 19 27 34 ...
$ recovered : int 0 0 0 0 0 0 0 0 2 2 ...
$ deaths : int 0 0 0 0 0 0 0 0 0 1 ...
$ treated : int 2 2 2 2 4 4 6 19 25 31 ...
$ daily_cases : int 2 0 0 0 2 0 2 13 8 7 ...
$ daily_recovered : int 0 0 0 0 0 0 0 0 2 0 ...
$ daily_deaths : int 0 0 0 0 0 0 0 0 0 1 ...
$ daily_treated : int 2 0 0 0 2 0 2 13 6 6 ...
$ pct_recovered : num 0 0 0 0 0 ...
$ pct_deaths : num 0 0 0 0 0 ...
$ pct_treated : num 100 100 100 100 100 ...
$ examined_specimen : int NA NA NA NA NA NA NA NA NA NA ...
$ total_examined_specimen: int 339 341 372 388 450 454 483 543 694 793 ...
$ negative : int 335 337 356 371 422 422 445 487 648 744 ...
$ daily_new_specimen : int 0 2 31 16 62 4 29 60 151 99 ...
$ pdp : int NA NA NA NA NA NA NA NA NA NA ...
$ odp : int NA NA NA NA NA NA NA NA NA NA ...
head(dailynational, n = 10)
Karena data yang dimiliki adalah laporan harian, terdapat tanggal, dan kita ingin mengetahui pola kasus harian, maka akan lebih tepat jika kita gunakan chart garis (line chart) yang biasa digunakan untuk visualisasi data deret waktu atau time series.
Kita akan menampilkan kasus penambahan baru harian (daily_cases), sembuh harian (daily_recovered), menginggal harian (daily_deaths) dan sebuah trend untuk kasus penambahan baru harian pada line chart tersebut seperti berikut ini.

Yang perlu kita ingat adalah struktur data yang kita gunakan seperti berikut.
dailynational %>%
select(dates, daily_cases, daily_recovered, daily_deaths) %>%
head(n = 10)
Dengan ggplot2 kita akan membuat sebuah line chart untuk masing-masing variable tersebut terhadap variable tanggal. Karena yang menjadi sumbu-x ( horizontal ) adalah variable tanggal untuk semua variable kasus, maka aes(x = dates) dapat kita tuliskan satu kali saja di argumen mapping pada fungsi ggplot(), sedangkan masing-masing variable kasus kita tuliskan di masing-masing geom_line().
ggplot(data = dailynational, mapping = aes(x = dates)) +
geom_line(aes(y = daily_cases, color = "New")) +
geom_line(aes(y = daily_recovered, color = "Recovered")) +
geom_line(aes(y = daily_deaths, color = "Deaths")) +
theme_minimal()

Selanjutnya kita akan menyesuaikan warna untuk masing-masing kasus agar lebih cocok terhadap jenis kasusnya. Misalnya untuk kasus New kita gunakan warna kuning (yellow), Recovered dengan warna hijau (green), dan Deaths dengan warna merah (red). Tebalnya garis juga akan kita sesuaikan sehingga lebih tebal.
# Change color & line size
ggplot(data = dailynational, mapping = aes(x = dates)) +
geom_line(aes(y = daily_cases, color = "New"), size = 0.8) +
geom_line(aes(y = daily_recovered, color = "Recovered"), size = 0.8) +
geom_line(aes(y = daily_deaths, color = "Deaths"), size = 0.8) +
theme_minimal() +
scale_color_manual(name = "Cases", # judul legend
values = c("New" = "yellow",
"Recovered" = "green",
"Deaths" = "red"))

Agar visualisasinya lebih baik selanjutnya kita tambahkan judul chart dan judul masing-masing sumbu.
# Add title and axis title
ggplot(data = dailynational, mapping = aes(x = dates)) +
geom_line(aes(y = daily_cases, color = "New"), size = 0.8) +
geom_line(aes(y = daily_recovered, color = "Recovered"), size = 0.8) +
geom_line(aes(y = daily_deaths, color = "Deaths"), size = 0.8) +
theme_minimal() +
scale_color_manual(name = "Cases", # judul legend
values = c("New" = "yellow",
"Recovered" = "green",
"Deaths" = "red")) +
labs(title = "Indonesia Daily Cases Trend",
x = "Dates",
y = "Daily Cases")

Berikutnya kita rubah format label pada sumbu x (Dates) agar kita bisa mengetahui tanggalnya. Format yang akan digunakan adalah “dd/mm” karena tahunnya sama, yaitu 2020, maka tidak perlu ditampilkan. Karena nilai pada sumbu x adalah tanggal, maka kita dapat gunakan fungsi scale_x_date(). Kemudian argumen date_breaks = "2 weeks" artinya antar tanggal yang ditampilkan (major ticks) berjarak 2 minggu, sehingga minor ticks adalah 1 minggu.
# Change x-axis format
ggplot(data = dailynational, mapping = aes(x = dates)) +
geom_line(aes(y = daily_cases, color = "New"), size = 0.8) +
geom_line(aes(y = daily_recovered, color = "Recovered"), size = 0.8) +
geom_line(aes(y = daily_deaths, color = "Deaths"), size = 0.8) +
theme_minimal() +
scale_color_manual(name = "Cases", # judul legend
values = c("New" = "yellow",
"Recovered" = "green",
"Deaths" = "red")) +
labs(title = "Indonesia Daily Cases Trend",
x = "Dates",
y = "Daily Cases") +
scale_x_date(date_breaks = "2 weeks", date_labels = "%d/%m")

Selanjutnya kita akan menambahkan sebuah garis trend pada chart di atas yang menunjukkan pola penambahan kasus baru harian di Indonesia. Yang kita butuhkan adalah data tanggal (dates) dan jumlah kasus penambahan harian (daily_cases). Untuk membuat data trend-nya kita gunakan Kernel Smoothing dengan fungsi ksmooth dari package {stats} yang sudah menjadi bagian dari base R ketika kita install R. Tidak ada alasan khusus yang saya gunakan sebagai pedoman ketika memilih fungsi ini. Ada beberapa metode lain yang dapat digunakan untuk membuat trend seperti yang ditulis di artikel ini.
# Add trend line
ks <- ksmooth(x = dailynational$dates,
y = dailynational$daily_cases,
kernel = "normal",
bandwidth = 10,
x.points = dailynational$dates)
dailynational$daily_trend <- ks$y
Kita tambahkan data trend tersebut menggunakan geom_line().
ggplot(data = dailynational, mapping = aes(x = dates)) +
geom_line(aes(y = daily_cases, color = "New"), size = 0.8) +
geom_line(aes(y = daily_recovered, color = "Recovered"), size = 0.8) +
geom_line(aes(y = daily_deaths, color = "Deaths"), size = 0.8) +
theme_minimal() +
scale_color_manual(name = "Cases",
values = c("New" = "yellow",
"Recovered" = "green",
"Deaths" = "red",
"Trend" = "grey")) +
labs(title = "Indonesia Daily Cases Trend",
x = "Dates",
y = "Daily Cases") +
scale_x_date(date_breaks = "2 weeks", date_labels = "%d/%m") +
geom_line(aes(y = daily_trend, color = "Trend"), size = 0.8)

Untuk visualisasi ini saya pribadi lebih suka jika legend-nya diletakan di atas chart. Karena itu saya tambahkan fungsi theme(legend.position = "top").
# Custom legend & position
ggplot(data = dailynational, mapping = aes(x = dates)) +
geom_line(aes(y = daily_cases, color = "New"), size = 0.8) +
geom_line(aes(y = daily_recovered, color = "Recovered"), size = 0.8) +
geom_line(aes(y = daily_deaths, color = "Deaths"), size = 0.8) +
theme_minimal() +
scale_color_manual(name = "Cases",
values = c("New" = "yellow",
"Recovered" = "green",
"Deaths" = "red",
"Trend" = "grey")) +
labs(title = "Indonesia Daily Cases Trend",
x = "Dates",
y = "Daily Cases") +
scale_x_date(date_breaks = "2 weeks", date_labels = "%d/%m") +
geom_line(aes(y = daily_trend, color = "Trend"), size = 0.8) +
theme(legend.position = "top")

LS0tDQp0aXRsZTogIlZpc3VhbGlzYXNpIERhdGEgTWVuZ2d1bmFrYW4gUiINCnN1YnRpdGxlOiAiV2ViaW5hciBEYXRhIENsdWIgMDIgSnVuaSAyMDIwIChTdHVkaSBLYXN1cyBDb3ZpZDE5KSINCmF1dGhvcjogIkFlcCBIaWRheWF0dWxvaCINCmRhdGU6ICJMYXN0IFVwZGF0ZTogYHIgZm9ybWF0KFN5cy5EYXRlKCksICclZCAlQiAlWScpYCINCm91dHB1dDogDQogIGh0bWxfbm90ZWJvb2s6DQogICAgbnVtYmVyX3NlY3Rpb25zOiBubw0KICAgIHRoZW1lOiBzcGFjZWxhYg0KICAgIGRmX3ByaW50OiBwYWdlZA0KICAgIHRvYzogdHJ1ZQ0KICAgIHRvY19kZXB0aDogMg0KICAgIHRvY19mbG9hdDogdHJ1ZQ0KLS0tDQoNCjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+DQoNCmJvZHl7IC8qIE5vcm1hbCAgKi8NCiAgICAgIGZvbnQtc2l6ZTogMTRweDsNCiAgfQ0KdGQgeyAgLyogVGFibGUgICovDQogIGZvbnQtc2l6ZTogMTJweDsNCn0NCmgxLnRpdGxlIHsNCiAgZm9udC1zaXplOiAzOHB4Ow0KICBjb2xvcjogbGlnaHRibHVlOw0KICBmb250LXdlaWdodDogYm9sZDsNCn0NCmgxIHsgLyogSGVhZGVyIDEgKi8NCiAgZm9udC1zaXplOiAyNHB4Ow0KICBjb2xvcjogRGFya0JsdWU7DQp9DQpoMiB7IC8qIEhlYWRlciAyICovDQogIGZvbnQtc2l6ZTogMjBweDsNCiAgY29sb3I6IERhcmtCbHVlOw0KfQ0KaDMgeyAvKiBIZWFkZXIgMyAqLw0KICBmb250LXNpemU6IDE2cHg7DQojICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgY29sb3I6IERhcmtCbHVlOw0KfQ0KaDQgeyAvKiBIZWFkZXIgNCAqLw0KICBmb250LXNpemU6IDE0cHg7DQogIGNvbG9yOiBEYXJrQmx1ZTsNCn0NCmNvZGUucnsgLyogQ29kZSBibG9jayAqLw0KICAgIGZvbnQtc2l6ZTogMTJweDsNCn0NCnByZSB7IC8qIENvZGUgYmxvY2sgLSBkZXRlcm1pbmVzIGNvZGUgc3BhY2luZyBiZXR3ZWVuIGxpbmVzICovDQogICAgZm9udC1zaXplOiAxMnB4Ow0KfQ0KPC9zdHlsZT4NCg0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCiNrbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpDQprbml0cjo6b3B0c19jaHVuayRzZXQoDQoJZWNobyA9IFRSVUUsIA0KCWZpZy5hbGlnbiA9ICJjZW50ZXIiLA0KCWZpZy5zaG93ID0gImhvbGQiLA0KCW1lc3NhZ2UgPSBGQUxTRSwNCgl3YXJuaW5nID0gRkFMU0UsDQoJcmVzdWx0cyA9ICJob2xkIg0KKQ0KIA0Kb3B0aW9ucyhzY2lwZW4gPSA5OSkNCmBgYA0KDQojIE9iamVrdGlmDQoNCk1lbmFtcGlsa2FuIGRhdGEgbWVuZ2d1bmFrYW4gUiB1bnR1ayBtZW1wZXJvbGVoIGluZm9ybWFzaSB5YW5nIGRpYnV0dWhrYW4uIFBhZGEga2VzZW1wYXRhbiBpbmkgYWthbiBkaWJhaGFzIG1lbWJ1YXQgdmlzdWFsaXNhc2kgbWVuZ2d1bmFrYW4gZGF0YSBrYXN1cyBDb3ZpZDE5IGRpIGJlcmJhZ2FpIG5lZ2FyYSBkYW4ganVnYSBJbmRvbmVzaWEuIFBhY2thZ2UgeWFuZyBha2FuIGRpZ3VuYWthbiBhZGFsYWggW2dncGxvdDJdKGh0dHBzOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnLykuIA0KDQojIERhdGEgJiBTY3JpcHQNCg0KQmViZXJhcGEgaGFsIHlhbmcgZGlidXR1aGthbiBwYWRhIGtlc2VtcGF0YW4gaW5pIDoNCg0KKipQcm9ncmFtIFIgZGFuIFJTdHVkaW8qKiwgZGloYXJhcGthbiBtaW5pbWFsIFIgZGVuZ2FuIHZlcnNpIDMuNi4yIGRhbiBiZWJlcmFwYSBwYWNrYWdlIHlhbmcgZGlidXR1aGthbi4NCg0KQW5kYSBkYXBhdCBtZW1wZXJvbGVoIG1hdGVyaSB3b3Jrc2hvcCBpbmkgKGRhdGEsIHNjcmlwdCBkYW4gZmlsZSBSbWQpIGRpIDxhIGhyZWY9Imh0dHBzOi8vZ2l0aHViLmNvbS9hZXBoaWRheWF0dWxvaC9jb3ZpeiIgdGFyZ2V0PSJfYmxhbmsiPnJlcG9zaXRvcnkgZ2l0aHViPC9hPi4gIA0KDQoqIFBpbGloICoqQ2xvbmUgb3IgZG93bmxvYWQqKiAgDQoqIEtlbXVkaWFuIHBpbGloICoqRG93bmxvYWQgWklQKiouICANCiogU2V0ZWxhaCBzZWxlc2FpIGRvd25sb2FkLCBla3N0cmFrIGZpbGUgemlwIHRlcnNlYnV0IGRhbiBfZG91YmxlLWNsaWNrXyBmaWxlIGBjb3Zpei5ScHJvamAuDQoNCkF0YXUgQW5kYSBqdWdhIGRhcGF0IG1lbmdha3NlcyBwcm9qZWN0IHRlcnNlYnV0IHlhbmcgc3VkYWggdGVyc2VkaWEgZGkgUlN0dWRpbyBDbG91ZCA8YSBocmVmPSJodHRwczovL3JzdHVkaW8uY2xvdWQvcHJvamVjdC8xMzMzMjIxIj5odHRwczovL3JzdHVkaW8uY2xvdWQvcHJvamVjdC8xMzMzMjIxPC9hPi4gQW5kYSBwZXJsdSBsb2dpbiBhdGF1IG1lbWJ1YXQgYWt1biB0ZXJsZWJpaCBkYWh1bHUgamlrYSBiZWx1bSBtZW1wdW55YWkgYWt1biBSU3R1ZGlvIENsb3VkLiBTZW11YSBtYXRlcmkgZGFuIHBhY2thZ2UgeWFuZyBha2FuIGRpZ3VuYWthbiBzdWRhaCB0ZXJzZWRpYS4NCg0KSmlrYSBBbmRhIGluZ2luIG1lbXBlcmJhcnVpIGRhdGEgeWFuZyBzdWRhaCBkaXNlZGlha2FuLCBBbmRhIGRhcGF0IG1lbWJ1a2EgZGFuIG1lbmphbGFua2FuIHNjcmlwdCBwYWRhIGZpbGUgImdldF9yYXdfZGF0YS5SIiBkaSBkYWxhbSBmb2xkZXIgc2NyaXB0LiBUZW50dSBzYWphIEFuZGEgcGVybHUgaW5zdGFsbCB0ZXJsZWJpaCBkYWh1bHUgcGFja2FnZSB5YW5nIGRpZ3VuYWthbiBqaWthIEFuZGEgYmVsdW0gbWVtcHVueWFpIHBhY2thZ2UgdGVyc2VidXQgcGFkYSBsaWJyYXJ5IFIgQW5kYS4NCg0KUGFja2FnZSB0YW1iYWhhbiB5YW5nIGRpYnV0dWhrYW4gdW50dWsgbWVtYnVhdCB2aXN1YWxpc2FzaSBwYWRhIGtlc2VtcGF0YW4gaW5pIGFkYWxhaCB7ZHBseXJ9LCB7Z2dwbG90Mn0gZGFuIHtzY2FsZXN9LiBKaWthIEFuZGEgYmVsdW0gbWVuZ2luc3RhbGwgcGFja2FnZSB0ZXJzZWJ1dCwgc2lsYWhrYW4gamFsYW5rYW4gcGVyaW50YWggYmVyaWt1dC4NCg0KYGBge3IgZXZhbD1GQUxTRX0NCmluc3RhbGwucGFja2FnZXMoYygiZHBseXIiLCAiZ2dwbG90MiIsICJzY2FsZXMiKSkNCmBgYA0KDQpTZWxhbmp1dG55YSBraXRhIGRhcGF0IG1lbmdndW5ha2FuIHBhY2thZ2UgdGVyc2VidXQgZGVuZ2FuIG1lbmphbGFua2FuIA0KDQpgYGB7ciBwa2d9DQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShzY2FsZXMpDQpgYGANCg0KUGVydGFtYSwga2l0YSBzaWFwa2FuIGRhdGEgeWFuZyBha2FuIGRpZ3VuYWthbiBkZW5nYW4gY2FyYSBpbXBvcnQgZGF0YSBkYXJpIGZpbGUgQ1NWIHlhbmcgc3VkYWggZGlzZWRpYWthbiBkaSBmb2xkZXIgYGRhdGFgLiBBZGEgMiBmaWxlIHlhbmcgYWthbiBkaWd1bmFrYW4sIHlhaXR1IGB3b3JsZF9jb3ZpZC5jc3ZgIGRhbiBgZGFpbHluYXRpb25hbC5jc3ZgLiBJbXBvcnQgZGF0YSB0ZXJzZWJ1dCBtZW5qYWRpIGRhdGFmcmFtZSBiZXJuYW1hIGB3b3JsZF9jb3ZpZGAgbWVuZ2d1bmFrYW4gZnVuZ3NpIGByZWFkLmNzdigpYCBkZW5nYW4gYXJndW1lbnQgYGZpbGUgPSBgIGFkYWxhaCBsb2thc2kgZGFuIG5hbWEgZmlsZSBDU1YgeWFuZyBha2FuIGRpaW1wb3J0LCBgaGVhZGVyID0gVFJVRWAga2FyZW5hIGJhcmlzIHBlcnRhbWEgYWRhbGFoIG5hbWEga29sb20sIGRhbiBgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFYCBhZ2FyIHNlbXVhIGtvbG9tIGJlcnRpcGUgY2hhcmFjdGVyIGRpIGZpbGUgQ1NWIGRpaW1wb3J0IHNlYmFnYWkgY2hhcmFjdGVyLCBidWthbiBzZWJhZ2FpIGZhY3Rvci4gVW50dWsgbGViaWggamVsYXMgdGVudGFuZyBmYWN0b3IgQW5kYSBiaXNhIG1lbWJhY2EgW2Rva3VtZW50YXNpIGZhY3Rvcl0oaHR0cHM6Ly93d3cucmRvY3VtZW50YXRpb24ub3JnL3BhY2thZ2VzL2Jhc2UvdmVyc2lvbnMvMy42LjIvdG9waWNzL2ZhY3RvcikuIA0KDQpLZW11ZGlhbiBraXRhIGxpaGF0IHN0cnVrdHVyIGRhcmkgZGF0YWZyYW1lIChiYW55YWtueWEgb2JzZXJ2YXNpIGRhbiB2YXJpYWJsZSwgbmFtYSB2YXJpYWJsZSBkYW4gdGlwZW55YSkgeWFuZyBzdWRhaCBkaWltcG9ydCBkZW5nYW4gZnVuZ3NpIGBzdHIoKWAuIERhdGEgYHdvcmxkX2NvdmlkYCBhZGFsYWggZGF0YSBtZW5nZW5haSBiYW55YWtueWEga2FzdXMgY292aWQgZGkgc2V0aWFwIG5lZ2FyYS9uZWdhcmEgYmFnaWFuIGRhbiBwb3B1bGFzaSBwZW5kdWR1a255YSB5YW5nIGRpYW1iaWwgZGFyaSA8aHR0cHM6Ly93d3cud29ybGRvbWV0ZXJzLmluZm8vY29yb25hdmlydXM+IGRhbiA8aHR0cHM6Ly93d3cud29ybGRvbWV0ZXJzLmluZm8vd29ybGQtcG9wdWxhdGlvbj4uDQoNCmBgYHtyfQ0Kd29ybGRfY292aWQgPC0gcmVhZC5jc3YoZmlsZSA9ICJkYXRhL3dvcmxkX2NvdmlkLmNzdiIsIGhlYWRlciA9IFRSVUUsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSwgZmlsZUVuY29kaW5nID0gImxhdGluMSIpDQpzdHIod29ybGRfY292aWQpDQpgYGANCg0KQmVyaWt1dG55YSBraXRhIGxpaGF0IGJlYmVyYXBhIGJhcmlzIHBlcnRhbWEgZGFyaSBkYXRhIGB3b3JsZF9jb3ZpZGAuIE1lbmdndW5ha2FuIGZ1bmdzaSBgaGVhZCgpYCB1bnR1ayBtZW5hbXBpbGthbiBiZWJlcmFwYSBiYXJpcyBwZXJ0YW1hLiBEZW5nYW4gYG4gPSAxMGAgYXJ0aW55YSBtZW5hcGlsa2FuIDEwIGJhcmlzIHBlcnRhbWEgZGFyaSBkYXRhIGB3b3JsZF9jb3ZpZGAuIA0KDQpgYGB7cn0NCmhlYWQod29ybGRfY292aWQsIG4gPSAxMCkNCmBgYA0KDQoNCg0KIyBUb3AgRmF0YWxpdHkgUmF0ZSBDb3VudHJ5DQoNCkRlbmdhbiB2aXN1YWxpc2FzaSB5YW5nIHBlcnRhbWEgaW5pLCBraXRhIGFrYW4gbWVuamF3YWIgcGVydGFueWFhbiAiTmVnYXJhIGFwYSBzYWphIHlhbmcgbWVtaWxpa2kgX0ZhdGFsaXR5IFJhdGVfIHRlcnRpbmdnaT8iIGRlbmdhbiBjaGFydCBzZXBlcnRpIGJlcmlrdXQuDQoNCmBgYHtyIGVjaG89RkFMU0V9DQp3b3JsZF9jb3ZpZCAlPiUgDQogIGFycmFuZ2UoZGVzYyhmYXRhbGl0eV9yYXRlKSkgJT4lDQogIGhlYWQobiA9IDIwKSAlPiUgDQogIGdncGxvdChtYXBwaW5nID0gYWVzKHggPSByZW9yZGVyKGNvdW50cnksIGZhdGFsaXR5X3JhdGUpLCB5ID0gZmF0YWxpdHlfcmF0ZSkpICsgDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBmaWxsID0gImNvcmFsIiwgYWxwaGEgPSAwLjcpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHBhc3RlMChmYXRhbGl0eV9yYXRlKjEwMCwgIiUiKSksIHNpemUgPSAzLCBoanVzdCA9IC0wLjEpICsgDQogIGNvb3JkX2ZsaXAoKSArIA0KICBsYWJzKHRpdGxlID0gIlRvcCBGYXRhbGl0eSBSYXRlIENvdW50cmllcyIsDQogICAgICAgeCA9ICJDb3VudHJ5IiwNCiAgICAgICB5ID0gIkZhdGFsaXR5IFJhdGUiKSArDQogIHRoZW1lX21pbmltYWwoKSArIA0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpwZXJjZW50X2Zvcm1hdChhY2N1cmFjeSA9IDEpLCBsaW1pdHMgPSBjKDAsIDAuMzApKSArDQogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IueSA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vci55ID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBwYW5lbC5ncmlkLm1pbm9yLnggPSBlbGVtZW50X2JsYW5rKCkpDQpgYGANCg0KVW50dWsgbWVuZ2hhc2lsa2FuIHZpc3VhbGlzYXNpIHNlcGVydGkgZGkgYXRhcyBraXRhIGFrYW4gbWVuZ2d1bmFrYW4gZGF0YSBgd29ybGRfY292aWRgIGRlbmdhbiBgY291bnRyeWAgZGFuIGBmYXRhbGl0eV9yYXRlYCBzZWJhZ2FpIHZhcmlhYmxlIHVudHVrIG1lbWJ1YXQgdmlzdWFsaXNhc2kgYmVydXBhIGJhcmNoYXJ0LiBEYXRhIHlhbmcgZGlndW5ha2FuIGFkYWxhaCBzZXBlcnRpIGJlcmlrdXQuDQoNCmBgYHtyfQ0KdmlzMSA8LSB3b3JsZF9jb3ZpZCAlPiUgDQogIHNlbGVjdChjb3VudHJ5LCBmYXRhbGl0eV9yYXRlKQ0KdmlzMQ0KYGBgDQoNCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSA9IHZpczEsIG1hcHBpbmcgPSBhZXMoeCA9IGNvdW50cnksIHkgPSBmYXRhbGl0eV9yYXRlKSkgKyANCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpDQpgYGANCg0KU3VsaXQgdW50dWsgbWVtcGVyb2xlaCBpbmZvcm1hc2kgeWFuZyBiYWlrIGRhcmkgaGFzaWwgdmlzdWFsaXNhc2kgZGkgYXRhcy4gS2l0YSBha2FuIGNvYmEgdW50dWsgbWVtYnVhdCBsYWJlbCBuYW1hIG5lZ2FyYS1uZWdhcmEgbGViaWggdGVybGloYXQgZGVuZ2FuIG1lcnViYWggcG9zaXNpIGxhYmVsIG5hbWEgbmVnYXJhIG1pcmluZyA5MCBkZXJhamF0Lg0KDQpgYGB7cn0NCiMgcm90YXRlIHgtYXhpcyBsYWJlbCA5MCBkZWcNCmdncGxvdChkYXRhID0gdmlzMSwgbWFwcGluZyA9IGFlcyh4ID0gY291bnRyeSwgeSA9IGZhdGFsaXR5X3JhdGUpKSArIA0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKyANCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCkpDQpgYGANCg0KDQpUZXJueWF0YSBtYXNpaCBiZWx1bSBjdWt1cCB0ZXJsaWhhdCBkYW4ga2l0YSBiZWx1bSBiaXNhIG1lbmRhcGF0a2FuIGluZm9ybWFzaSBhcGFwdW4gZGFyaSBjaGFydCB0ZXJzZWJ1dC4NCg0KS2l0YSBha2FuIGNvYmEgdW50dWsgbWVuZ3VydXRrYW4gbmFtYS1uYW1hIG5lZ2FyYSBiZXJkYXNhcmthbiBfRmF0YWxpdHkgUmF0ZV8sIHlhaXR1IHZhcmlhYmxlIGBmYXRhbGl0eV9yYXRlYC4gS2l0YSBndW5ha2FuIGZ1bmdzaSBgcmVvcmRlcihjb3VudHJ5LCBmYXRhbGl0eV9yYXRlKWAgdW50dWsgbWVuZ3VydXRrYW4gYmFyY2hhcnQgYmVyZGFzYXJrYW4gX0ZhdGFsaXR5IFJhdGVfLg0KDQpgYGB7cn0NCiMgc29ydGVkIGJhcmNoYXJ0DQpnZ3Bsb3QoZGF0YSA9IHZpczEsIG1hcHBpbmcgPSBhZXMoeCA9IHJlb3JkZXIoY291bnRyeSwgZmF0YWxpdHlfcmF0ZSksIHkgPSBmYXRhbGl0eV9yYXRlKSkgKyANCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsgDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTApKQ0KDQpgYGANCg0KDQpBZ2FyIGluZm9ybWFzaSBkYXBhdCBkaXBlcm9sZWggZGVuZ2FuIGJhaWsgbWFrYSB0aWRhayBzZW11YSBuZWdhcmEgYWthbiBraXRhIHRhbXBpbGthbi4gTWlzYWxueWEga2l0YSBoYW55YSBwZXJsdSBtZW5hbXBpbGthbiBkYWZ0YXIgMjAgbmVnYXJhIGJlcmRhc2Fya2FuIF9GYXRhbGl0eSBSYXRlXyB0ZXJ0aW5nZ2kuIERlbmdhbiBmdW5nc2kgYGFycmFuZ2UoKWAgZGFuIGBkZXNjKClgIGRhcmkgcGFja2FnZSB7ZHBseXJ9IGRhbiBmdW5nc2kgYGhlYWQoKWAgZGVuZ2FuIGBuID0gMjBgIGtpdGEgYWthbiBtZW5kYXBhdGthbiAyMCBuZWdhcmEgZGVuZ2FuIF9GYXRhbGl0eSBSYXRlXyB0ZXJ0aW5nZ2kuDQoNCmBgYHtyfQ0KdG9wbiA8LSB2aXMxICU+JSANCiAgYXJyYW5nZShkZXNjKGZhdGFsaXR5X3JhdGUpKSAlPiUNCiAgaGVhZChuID0gMjApDQp0b3BuDQpgYGANCg0KU2VsYW5qdXRueWEgZGFyaSBkYXRhIHRlcnNlYnV0IGtpdGEgYnVhdCB2aXN1YWxpc2FzaW55YS4gDQpgYGB7cn0NCiMgc29ydGVkIGJhcmNoYXJ0DQpnZ3Bsb3QoZGF0YSA9IHRvcG4sIG1hcHBpbmcgPSBhZXMoeCA9IHJlb3JkZXIoY291bnRyeSwgZmF0YWxpdHlfcmF0ZSksIHkgPSBmYXRhbGl0eV9yYXRlKSkgKyANCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsgDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTApKQ0KDQpgYGANCg0KS2l0YSBzdWRhaCBkYXBhdCBtZW5hbXBpbGthbiAyMCBuZWdhcmEgZGVuZ2FuIF9GYXRhbGl0eSBSYXRlXyB0ZXJ0aW5nZ2kuIE5hbXVuIHRlbnR1IHNhamEgdW50dWsgcHVibGlrYXNpIGF0YXVwdW4gZGFyaSBzZWdpIGVzdGV0aWthIG1hc2loIHBlcmx1IGRpdGFtYmFoa2FuIGJlYmVyYXBhICJha3Nlc29yaXMiLg0KDQpTZWxhbmp1dG55YSBraXRhIGFrYW4gbWVuYW1iYWhrYW4ganVkdWwgY2hhcnQgZGFuIG1lcnViYWgganVkdWwgcGFkYSBtYXNpbmctbWFzaW5nIHN1bWJ1IHggZGFuIHkuIEtpdGEgZGFwYXQgbWVuYW1iYWhrYW4ganVkdWwgY2hhcnQgZGFuIG1hc2luZy1tYXNpbmcgc3VtYnUsIGtpdGEgZGFwYXQgbWVuZ2d1bmFrYW4gZnVuZ3NpIGBsYWJzKClgLiBBcmd1bWVuIGB0aXRsZSA9IGAgdW50dWsganVkdWwgdXRhbWEsIGB4ID0gYCB1bnR1ayBqdWR1bCBwYWRhIHN1bWJ1IHgsIGRhbiBgeSA9IGAgdW50dWsganVkdWwgc3VtYnUgeS4gVW50dWsganVkdWwgc3VtYnUsIHRldGFwIG1lbmdpa3V0aSBwYWRhIGBhZXMoKWAuIEthcmVuYQ0KDQpgYGB7cn0NCmdncGxvdChkYXRhID0gdG9wbiwgbWFwcGluZyA9IGFlcyh4ID0gcmVvcmRlcihjb3VudHJ5LCBmYXRhbGl0eV9yYXRlKSwgeSA9IGZhdGFsaXR5X3JhdGUpKSArIA0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKyANCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCkpICsNCiAgbGFicyh0aXRsZSA9ICJUb3AgRmF0YWxpdHkgUmF0ZSBDb3VudHJpZXMiLA0KICAgICAgIHggPSAiQ291bnRyeSIsDQogICAgICAgeSA9ICJGYXRhbGl0eSBSYXRlIikNCmBgYA0KDQpOaWxhaSBfRmF0YWxpdHkgUmF0ZV8gYWthbiBsZWJpaCBtdWRhaCBkaW1lbmdlcnRpIGppa2EgZGl0YW1waWxrYW4gZGFsYW0gZm9ybWF0IHBlcnNlbnRhc2UuIEthcmVuYSB2YXJpYWJsZSBfRmF0YWxpdHkgUmF0ZV8gYWRhIHBhZGEgc3VtYnUgeSBkYW4gbWVydXBha2FuIHZhcmlhYmxlIGtvbnRpbnUsIG1ha2Ega2l0YSBkYXBhdCBndW5ha2FuIGZ1bmdzaSBgc2NhbGVfeV9jb250aW51b3VzKClgIGRhcmkge2dncGxvdDJ9LiBQYWRhIGFyZ3VtZW4gYGxhYmVsID0gYCBraXRhIGd1bmFrYW4gZnVuZ3NpIGBwZXJjZW50X2Zvcm1hdCgpYCBkYXJpIHBhY2thZ2Uge3NjYWxlc30gYWdhciB0YW1waWxhbiBsYWJlbCBkaSBzdW1idSBfRmF0YWxpdHkgUmF0ZV8gYmVydXBhIHBlcnNlbnRhc2UuIEFyZ3VtZW4gYGFjY3VyYWN5ID0gMWAgcGFkYSBmdW5nc2kgYHBlcmNlbnRfZm9ybWF0KClgIG1lbmFuZGFrYW4gdGlkYWsgYWRhIGFuZ2thIGRpYmVsYWthbmcgZGVzaW1hbC4gDQoNCmBgYHtyfQ0KIyBDdXN0b20gYXhpcyBzY2FsZSANCmdncGxvdChkYXRhID0gdG9wbiwgbWFwcGluZyA9IGFlcyh4ID0gcmVvcmRlcihjb3VudHJ5LCBmYXRhbGl0eV9yYXRlKSwgeSA9IGZhdGFsaXR5X3JhdGUpKSArIA0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwKSkgKw0KICBsYWJzKHRpdGxlID0gIlRvcCBGYXRhbGl0eSBSYXRlIENvdW50cmllcyIsDQogICAgICAgeCA9ICJDb3VudHJ5IiwNCiAgICAgICB5ID0gIkZhdGFsaXR5IFJhdGUiKSArIA0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpwZXJjZW50X2Zvcm1hdChhY2N1cmFjeSA9IDEpKQ0KYGBgDQoNCkppa2EgQW5kYSBpbmdpbiBmb3JtYXQgcGVyc2VudGFzZSBkZW5nYW4gc2F0dSBhbmdrYSBkaSBiZWxha2FuZyBkZXNpbWFsLCBndW5ha2FuIGBhY2N1cmFjeSA9IDAuMWAuIEppa2EgaW5naW4gZHVhIGFuZ2thIGRpIGJlbGFrYW5nIGRlc2ltYWwgbWFrYSBgYWNjdXJhY3kgPSAwLjAxYCwgZGFuIHNldGVydXNueWEuDQoNClNheWEgcHJpYmFkaSBsZWJpaCBzdWthIGtldGlrYSBtZW1idWF0IHZpc3VhbGlzYXNpIHNlcGVydGkgcGFkYSBrYXN1cyBpbmksIGJhcmNoYXJ0IGRlbmdhbiBsYWJlbCB5YW5nIGN1a3VwIHBhbmphbmcsIG1lbWJ1YXRueWEgbWVuamFkaSBiYXJjaGFydCBob3Jpem9udGFsLiBLYXJlbmEgb3JhbmcgYXdhbSBwdW4gYWthbiBkZW5nYW4gbXVkYWggbWVuZGFwYXRrYW4gaW5mb3JtYXNpIHlhbmcgaW5naW4ga2l0YSBzYW1wYWlrYW4gbWVsYWx1aSB2aXN1YWxpc2FzaSB0ZXJzZWJ1dC4gVW50dWsgbWVtYnVhdCBiYXJjaGFydCBob3Jpem9udGFsLCBraXRhIGRhcGF0IGRpZ3VuYWthbiBmdW5nc2kgYGNvb3JkX2ZsaXAoKWAgZGFyaSBwYWNrYWdlIHtnZ3Bsb3QyfS4gRnVuZ3NpIGluaSBha2FuIG1lbnRyYW5zZm9ybWFzaSBzdW1idS14IChob3Jpem9udGFsKSBtZW5qYWRpIHN1bWJ1LXkgKHZlcnRpa2FsKSwgZGFuIHNlYmFsaWtueWEuDQoNCkNvYmEgcGVyaGF0aWthbiBiYXIgbmVnYXJhIFlhbWFuLiBQYWRhIGNoYXJ0IHNlYmVsdW1ueWEgYmFyIG5lZ2FyYSBZYW1hbiBiZXJhZGEgcGFkYSB1anVuZyBrYW5hbiBzdW1idS14LiBEZW5nYW4gZnVuZ3NpIGBjb29yZF9mbGlwKClgIG1lbmphZGkgZGkgdWp1bmcgYXRhcyBzdW1idS15LiBBcnRpbnlhIGBjb29yZF9mbGlwKClgIG1lbnRyYW5zZm9ybWFzaSBkZW5nYW4gdGl0aWsgcG9qb2sga2lyaSBiYXdhaCBzZWJhZ2FpIHRpdGlrIHR1bXB1bnlhLg0KDQpgYGB7cn0NCiMgQ3VzdG9tIHRpdGxlIC0gYXhpcyB0aXRsZQ0KZ2dwbG90KGRhdGEgPSB0b3BuLCBtYXBwaW5nID0gYWVzKHggPSByZW9yZGVyKGNvdW50cnksIGZhdGFsaXR5X3JhdGUpLCB5ID0gZmF0YWxpdHlfcmF0ZSkpICsgDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArIA0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwKSkgKw0KICBsYWJzKHRpdGxlID0gIlRvcCBGYXRhbGl0eSBSYXRlIENvdW50cmllcyIsDQogICAgICAgeCA9ICJDb3VudHJ5IiwNCiAgICAgICB5ID0gIkZhdGFsaXR5IFJhdGUiKSArIA0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpwZXJjZW50X2Zvcm1hdChhY2N1cmFjeSA9IDEpKSArDQogIGNvb3JkX2ZsaXAoKQ0KYGBgDQoNClRhbXBpbGFuIGxhYmVsIHBhZGEgc3VtYnUgX0ZhdGFsaXR5IFJhdGVfIGhhcnVzbnlhIHRldGFwIGhvcml6b250YWwuIE1ha2Ega2l0YSB0aWRhayBwZXJsdSBtZW5nZ3VuYWthbiBgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCkpYCBsYWdpLg0KDQpgYGB7cn0NCiMgQ3VzdG9tIHRpdGxlIC0gYXhpcyB0aXRsZQ0KZ2dwbG90KGRhdGEgPSB0b3BuLCBtYXBwaW5nID0gYWVzKHggPSByZW9yZGVyKGNvdW50cnksIGZhdGFsaXR5X3JhdGUpLCB5ID0gZmF0YWxpdHlfcmF0ZSkpICsgDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArIA0KICBsYWJzKHRpdGxlID0gIlRvcCBGYXRhbGl0eSBSYXRlIENvdW50cmllcyIsDQogICAgICAgeCA9ICJDb3VudHJ5IiwNCiAgICAgICB5ID0gIkZhdGFsaXR5IFJhdGUiKSArDQogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6OnBlcmNlbnRfZm9ybWF0KGFjY3VyYWN5ID0gMSkpICsNCiAgY29vcmRfZmxpcCgpDQpgYGANCg0KDQpLaXRhIGRhcGF0IG1lbmdhdHVyIHNlYnVhaCB0aGVtZSBkYXJpIGdncGxvdDIgYWdhciBkYXBhdCBkaWd1bmFrYW4gcGFkYSBjaGFydCBnZ3Bsb3QgeWFuZyBraXRhIGhhc2lsa2FuIGJlcmlrdXRueWEgdGFucGEgaGFydXMgbWVudWxpc2thbiBgKyB0aGVtZV9taW5pbWFsKClgIGRpIHNldGlhcCBzY3JpcHQgZGVuZ2FuIGB0aGVtZV9zZXQodGhlbWVfbWluaW1hbCgpKWAuIE5hbXVuIHBhZGEga2VzZW1wYXRhbiBpbmkgZGkgc2V0aWFwIHNjcmlwdCB5YW5nIG1lbmdoYXNpbGthbiBjaGFydCBkYXJpIGdncGxvdDIgYWthbiBkaXR1bGlza2FuIGArIHRoZW1lX21pbmltYWwoKWAuIA0KDQpgYGB7cn0NCiMgQ3VzdG9tIHRoZW1lIA0KZ2dwbG90KGRhdGEgPSB0b3BuLCBtYXBwaW5nID0gYWVzKHggPSByZW9yZGVyKGNvdW50cnksIGZhdGFsaXR5X3JhdGUpLCB5ID0gZmF0YWxpdHlfcmF0ZSkpICsgDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArIA0KICBsYWJzKHRpdGxlID0gIlRvcCBGYXRhbGl0eSBSYXRlIENvdW50cmllcyIsDQogICAgICAgeCA9ICJDb3VudHJ5IiwNCiAgICAgICB5ID0gIkZhdGFsaXR5IFJhdGUiKSArIA0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpwZXJjZW50X2Zvcm1hdChhY2N1cmFjeSA9IDEpKSArDQogIGNvb3JkX2ZsaXAoKSArDQogIHRoZW1lX21pbmltYWwoKQ0KDQpgYGANCg0KDQpMYW5na2FoIGJlcmlrdXRueWEga2l0YSBha2FuIG1lcnViYWggd2FybmEgYmFyIGRhbiBncmlkIGRhcmkgY2hhcnQgdGVyc2VidXQuIA0KDQpQZXJ0YW1hIGtpdGEgcnViYWggd2FybmEgYmFyIHRlcmxlYmloIGRhaHVsdS4gV2FybmEgYmFyIGJpc2Ega2l0YSBydWJhaCBkZW5nYW4gbWVuYW1iYWhrYW4gYXJndW1lbiBgZmlsbCA9IGAuIERpc2luaSBzYXlhIHByaWJhZGkgbWVuZ2d1bmFrYW4gd2FybmEgYCJjb3JhbCJgIGRlbmdhbiBgYWxwaGEgPSAwLjdgLiBOaWxhaSBgYWxwaGFgIGluaSBhZGFsYWggdGluZ2thdCB0cmFuc3BhcmFuc2kgd2FybmEgYXRhdSBfb3BhY2l0eV8sIGFudGFyYSAwIGRhbiAxLiBBbmRhIGJpc2EgbWVtaWxpaCBtZW5nZ3VuYWthbiB3YXJuYSBsYWluIHNlcGVydGkgYCJyZWQiYCwgYCJ5ZWxsb3ciYCwgYCJncmVlbiJgLCBgInNreWJsdWUiYCBkYW4gbGFpbi1sYWluLiBCaXNhIGp1Z2EgbWVuZ2d1bmFrYW4ga29kZSB3YXJuYSBIZXggKG1pc2FsbnlhIGAjRTE0MDRCYCkgYXRhdSBSR0IgKG1pc2FsbnlhIGByZ2IoMjI1LCA2NCwgNzUsIG1heENvbG9yVmFsdWUgPSAyNTUpYCkuIFNheWEgcHJpYmFkaSBzZXJpbmcgbWVuZ2d1bmFrYW4gPGEgaHJlZj0iaHR0cHM6Ly93d3cuZ29vZ2xlLmNvbS9zZWFyY2g/cT1jb2xvcitwaWNrZXIiIHRhcmdldD0iX2JsYW5rIj5jb2xvciBwaWNrZXIgZGFyaSBnb29nbGUgc2VhcmNoPC9hPiB1bnR1ayBtZW1pbGloIHdhcm5hIHlhbmcgaW5naW4gZGlndW5ha2FuLg0KDQpgYGB7cn0NCiMgQ2hhbmdlIGNvbG9yICYgZ3JpZA0KZ2dwbG90KGRhdGEgPSB0b3BuLCBtYXBwaW5nID0gYWVzKHggPSByZW9yZGVyKGNvdW50cnksIGZhdGFsaXR5X3JhdGUpLCB5ID0gZmF0YWxpdHlfcmF0ZSkpICsgDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBmaWxsID0gImNvcmFsIiwgYWxwaGEgPSAwLjcpICsNCiAgbGFicyh0aXRsZSA9ICJUb3AgRmF0YWxpdHkgUmF0ZSBDb3VudHJpZXMiLA0KICAgICAgIHggPSAiQ291bnRyeSIsDQogICAgICAgeSA9ICJGYXRhbGl0eSBSYXRlIikgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpwZXJjZW50X2Zvcm1hdChhY2N1cmFjeSA9IDEpKSArIA0KICBjb29yZF9mbGlwKCkgKyANCiAgdGhlbWVfbWluaW1hbCgpIA0KYGBgDQoNClNlbGFuanV0bnlhIGtpdGEgaGlsYW5na2FuIGdhcmlzIG1ham9yIGRhbiBtaW5vciBob3Jpem9udGFsIGRhcmkgc3VtYnUgeSBkYW4gZ2FyaXMgbWlub3IgdmVydGlrYWwgZGFyaSBzdW1idSB4LiBLaXRhIGRhcGF0IG1lbGFrdWthbiBpbmkgZGVuZ2FuIG1lbmdndW5ha2FuIGZ1bmdzaSBgdGhlbWUoKWAgZGFyaSB7Z2dwbG90Mn0uIEFyZ3VtZW4geWFuZyBkaWd1bmFrYW4gYWRhbGFoIGBwYW5lbC5ncmlkLm1ham9yLnkgPSBgLCBgcGFuZWwuZ3JpZC5taW5vci55ID0gYCBkYW4gYHBhbmVsLmdyaWQubWFqb3IueCA9IGAuIEZ1bmdzaSBgZWxlbWVudF9ibGFuaygpYCB1bnR1ayBzZXRpYXAgYXJndW1lbiB0ZXJzZWJ1dCBtZW5hbmRha2FuIGF0cmlidXQgeWFuZyBiZXJzZXN1YWlhbiBkZW5nYW4gYXJndW1lbiB5YW5nIGRpZ3VuYWthbiBkaXRpYWRha2FuLg0KDQpgYGB7cn0NCiMgQ2hhbmdlIGNvbG9yICYgZ3JpZA0KZ2dwbG90KGRhdGEgPSB0b3BuLCBtYXBwaW5nID0gYWVzKHggPSByZW9yZGVyKGNvdW50cnksIGZhdGFsaXR5X3JhdGUpLCB5ID0gZmF0YWxpdHlfcmF0ZSkpICsgDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBmaWxsID0gImNvcmFsIiwgYWxwaGEgPSAwLjcpICsNCiAgbGFicyh0aXRsZSA9ICJUb3AgRmF0YWxpdHkgUmF0ZSBDb3VudHJpZXMiLA0KICAgICAgIHggPSAiQ291bnRyeSIsDQogICAgICAgeSA9ICJGYXRhbGl0eSBSYXRlIikgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpwZXJjZW50X2Zvcm1hdChhY2N1cmFjeSA9IDEpKSArIA0KICBjb29yZF9mbGlwKCkgKyANCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgdGhlbWUocGFuZWwuZ3JpZC5tYWpvci55ID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBwYW5lbC5ncmlkLm1pbm9yLnkgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIHBhbmVsLmdyaWQubWlub3IueCA9IGVsZW1lbnRfYmxhbmsoKSkNCmBgYA0KDQpLaXRhIHN1ZGFoIG1lbWJ1YXQgY2hhcnQgeWFuZyBjdWt1cCBiYWlrLiBOYW11biBzYXlhIGluZ2luIG1lbmFtYmFoa2FuIG5pbGFpIF9GYXRhbGl0eSBSYXRlXyBkaSBzZXRpYXAgdWp1bmcgYmFyIGFnYXIgaW5mb3JtYXNpIHlhbmcgZGlzYW1wYWlrYW4gbGViaWggbGVuZ2thcC4gS2l0YSBkYXBhdCBndW5ha2FuIGZ1bmdzaSBgZ2VvbV90ZXh0KClgIGF0YXUgYGdlb21fbGFiZWwoKWAsIHRpbmdnYWwgZGlzZXN1YWlrYW4gZGVuZ2FuIHNlbGVyYSBtYXNpbmctbWFzaW5nLiBLYXJlbmEgbmlsYWkgZGFyaSB2YXJpYWJsZSBgZmF0YWxpdHlfcmF0ZWAgYW50YXJhIDAgZGFuIDEsIG1ha2EgcGVybHUgZGlrYWxpa2FuIDEwMCB1bnR1ayBtZW5kYXBhdGthbiBuaWxhaSBwZXJzZW50YXNlIHNlYmFnYWkgX3RleHRfIGtlbXVkaWFuIGRpdGFtYmFoa2FuIHRhbmRhICIlIiBkZW5nYW4gZnVuZ3NpIGBwYXN0ZTAoKWAuIFVudHVrIG1lbnllc3VhaWthbiB1a3VyYW4gX3RleHRfLW55YSBraXRhIGd1bmFrYW4gYXJndW1lbiBgc2l6ZSA9IGAuIFNlbWFraW4gYmVzYXIgbmlsYWkgYHNpemUgPSBgIG1ha2Egc2VtYWtpbiBiZXNhciBqdWdhIHVrdXJhbiBfdGV4dF8uIEFyZ3VtZW4gYGhqdXN0ID0gLTAuMWAgdW50dWsgbWVuZ2F0dXIgcG9zaXNpIHNlY2FyYSBob3Jpem9udGFsLiANCg0KYGBge3J9DQojIEFkZCBsYWJlbCAmIHktbGltaXQgDQpnZ3Bsb3QoZGF0YSA9IHRvcG4sIG1hcHBpbmcgPSBhZXMoeCA9IHJlb3JkZXIoY291bnRyeSwgZmF0YWxpdHlfcmF0ZSksIHkgPSBmYXRhbGl0eV9yYXRlKSkgKyANCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIGZpbGwgPSAiY29yYWwiLCBhbHBoYSA9IDAuNykgKyANCiAgbGFicyh0aXRsZSA9ICJUb3AgRmF0YWxpdHkgUmF0ZSBDb3VudHJpZXMiLA0KICAgICAgIHggPSAiQ291bnRyeSIsDQogICAgICAgeSA9ICJGYXRhbGl0eSBSYXRlIikgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpwZXJjZW50X2Zvcm1hdChhY2N1cmFjeSA9IDEpKSArDQogIGNvb3JkX2ZsaXAoKSArDQogIHRoZW1lX21pbmltYWwoKSArIA0KICB0aGVtZShwYW5lbC5ncmlkLm1ham9yLnkgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIHBhbmVsLmdyaWQubWlub3IueSA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vci54ID0gZWxlbWVudF9ibGFuaygpKSArIA0KICBnZW9tX3RleHQoYWVzKGxhYmVsID0gcGFzdGUwKGZhdGFsaXR5X3JhdGUqMTAwLCAiJSIpKSwgc2l6ZSA9IDMsIGhqdXN0ID0gLTAuMSkNCg0KYGBgDQoNClNlcGVydGkgeWFuZyBkaWxpaGF0LCBhZGEgX3RleHRfIHlhbmcgdGVycG90b25nIGthcmVuYSBzdW1idSBfRmF0YWxpdHkgUmF0ZV8geWFuZyBrdXJhbmcgcGFuamFuZy4gS2l0YSBkYXBhdCBtZW5nYXR1ciBiYXRhcyAobGltaXRzKSBkYXJpIHN1bWJ1IF9GYXRhbGl0eSBSYXRlXyBpbmkgZGVuZ2FuIG1lbmFtYmFoa2FuIGFyZ3VtZW4gYGxpbWl0cyA9IGMoPG1pbj4sIDxtYXg+KWAgcGFkYSBmdW5nc2kgYHNjYWxlX3lfY29udGludW91cygpYC4gTmlsYWkgYDxtaW4+YCBkYW4gYDxtYXg+YCBhZGFsYWggcGFuZ2thbCBkYW4gYmF0YXMgdWp1bmcgc3VtYnUuIEthcmVuYSBuaWxhaSBgZmF0YWxpdHlfcmF0ZWAgYW50YXJhIDAgZGFuIDEgZGFuIHNheWEgaW5naW4gYmF0YXNueWEgc2FtcGFpIG5pbGFpIDMwJSBtYWthIGBsaW1pdHMgPSBjKDAsIDAuMzApYC4gU2F5YSBwaWxpaCAzMCUgaGFueWEgYWdhciBzZW11YSBfdGV4dF8gZGFyaSBtYXNpbmctbWFzaW5nIGJhciB0aWRhayBhZGEgeWFuZyB0ZXJwb3RvbmcuDQoNCmBgYHtyfQ0KIyBBZGQgbGFiZWwgJiB5LWxpbWl0IA0KZ2dwbG90KGRhdGEgPSB0b3BuLCBtYXBwaW5nID0gYWVzKHggPSByZW9yZGVyKGNvdW50cnksIGZhdGFsaXR5X3JhdGUpLCB5ID0gZmF0YWxpdHlfcmF0ZSkpICsgDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBmaWxsID0gImNvcmFsIiwgYWxwaGEgPSAwLjcpICsgDQogIGxhYnModGl0bGUgPSAiVG9wIEZhdGFsaXR5IFJhdGUgQ291bnRyaWVzIiwNCiAgICAgICB4ID0gIkNvdW50cnkiLA0KICAgICAgIHkgPSAiRmF0YWxpdHkgUmF0ZSIpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6cGVyY2VudF9mb3JtYXQoYWNjdXJhY3kgPSAxKSwgbGltaXRzID0gYygwLCAwLjMwKSkgKw0KICBjb29yZF9mbGlwKCkgKw0KICB0aGVtZV9taW5pbWFsKCkgKyANCiAgdGhlbWUocGFuZWwuZ3JpZC5tYWpvci55ID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBwYW5lbC5ncmlkLm1pbm9yLnkgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIHBhbmVsLmdyaWQubWlub3IueCA9IGVsZW1lbnRfYmxhbmsoKSkgKyANCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHBhc3RlMChmYXRhbGl0eV9yYXRlKjEwMCwgIiUiKSksIHNpemUgPSAzLCBoanVzdCA9IC0wLjEpDQpgYGANCg0KS2l0YSBqdWdhIGJpc2EgbWVtYnVhdCB2aXN1YWxpc2FzaSBzZXBlcnRpIGRpIGF0YXMgYmVyZGFzYXJrYW4gYmFueWFrbnlhIGthc3VzIHlhbmcgdGVyamFkaSBkaSBzdWF0dSBuZWdhcmEuDQoNCmBgYHtyfQ0KIyBCeSBUb3RhbCBDYXNlcyANCiMjIHRvcCAyMCBiYXJjaGFydA0KdG9wMjAgPC0gd29ybGRfY292aWQgJT4lIA0KICBhcnJhbmdlKGRlc2ModG90YWxfY2FzZXMpKSAlPiUNCiAgaGVhZChuID0gMjApDQoNCmdncGxvdChkYXRhID0gdG9wMjAsIG1hcHBpbmcgPSBhZXMoeCA9IHJlb3JkZXIoY291bnRyeSwgdG90YWxfY2FzZXMpLCB5ID0gdG90YWxfY2FzZXMpKSArIA0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgZmlsbCA9ICJjb3JhbCIsIGFscGhhID0gMC43KSArDQogIGdlb21fdGV4dChhZXMobGFiZWwgPSBmb3JtYXRDKHRvdGFsX2Nhc2VzLCBiaWcubWFyayA9ICIsIiwgZGVjaW1hbC5tYXJrID0gIi4iLCBmb3JtYXQgPSAiZCIpKSwgc2l6ZSA9IDMsIGhqdXN0ID0gLTAuMSkgKyANCiAgY29vcmRfZmxpcCgpICsgDQogIGxhYnModGl0bGUgPSAiVG9wIDIwIFRvdGFsIENhc2VzIENvdW50cmllcyIsDQogICAgICAgeCA9ICJDb3VudHJ5IiwNCiAgICAgICB5ID0gIlRvdGFsIENhc2VzIikgKw0KICB0aGVtZV9taW5pbWFsKCkgKyANCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6bnVtYmVyX2Zvcm1hdChiaWcubWFyayA9ICIsIiksIGxpbWl0cyA9IGMoMCwgMi4wKjEwXjYpKSArDQogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IueSA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vci55ID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBwYW5lbC5ncmlkLm1pbm9yLnggPSBlbGVtZW50X2JsYW5rKCkpDQpgYGANCg0KIyBNZWRpYW4gUG9wdWxhdGlvbiBBZ2UgdnMgRmF0YWxpdHkgUmF0ZQ0KDQpQYWRhIHZpc3VhbGlzYXNpIGtlZHVhIGluaSBraXRhIGFrYW4gbWVsYWt1a2FuIGVrc3Bsb3Jhc2kgZGF0YSBgd29ybGRfY292aWRgIG1lbmdndW5ha2FuIGJlYmVyYXBhIHZhcmlhYmxlIGRlbmdhbiB2aXN1YWxpc2FzaSBiZXJ1cGEgYnViYmxlIGNoYXJ0IHNlcGVydGkgYmVyaWt1dCBpbmkuDQoNCmBgYHtyIGVjaG89RkFMU0V9DQojIEJ1YmxlIGNoYXJ0IA0KZ2dwbG90KGRhdGEgPSB3b3JsZF9jb3ZpZCwgbWFwcGluZyA9IGFlcyh4ID0gbWVkX2FnZSwgeSA9IGZhdGFsaXR5X3JhdGUpKSArIA0KICBnZW9tX3BvaW50KGFlcyhzaXplID0gZGVuc2l0eV9rbTIsIGNvbG9yID0gY29udGluZW50KSwgYWxwaGEgPSAwLjYpICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgbGFicyh0aXRsZSA9ICJNZWRpYW4gUG9wdWxhdGlvbiBBZ2UgdnMgRmF0YWxpdHkgUmF0ZSIsDQogICAgICAgeCA9ICJNZWRpYW4gUG9wdWxhdGlvbiBBZ2UiLA0KICAgICAgIHkgPSAiRmF0YWxpdHkgUmF0ZSIsDQogICAgICAgc2l6ZSA9ICJQb3B1bGF0aW9uIERlbnNpdHkgKC9rbTIpIiwNCiAgICAgICBjb2xvciA9ICJDb250aW5lbnQiKSArDQogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gYygiQWZyaWNhIiA9ICIjZjUzZDNkIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQXNpYSIgPSAiI2Y3YTQxZSIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkF1c3RyYWxpYS9PY2VhbmlhIiA9ICIjNGJkYjU3IiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRXVyb3BlIiA9ICIjM2RhMmViIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTm9ydGggQW1lcmljYSIgPSAiI2YyMjdlYiIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlNvdXRoIEFtZXJpY2EiID0gIiM4YTA0MDQiKSkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpwZXJjZW50X2Zvcm1hdChhY2N1cmFjeSA9IDEpKSArDQogIGZhY2V0X3dyYXAofiBjb250aW5lbnQpDQpgYGANCg0KVW50dWsgbWVtYnVhdCBidWJibGUgY2hhcnQgZGkgZ2dwbG90MiBraXRhIGRhcGF0IG1lbmdndW5ha2FuIGBnZW9tX3BvaW50KClgLiBEYXNhcm55YSBhZGFsYWggbWVtYnVhdCBfc2NhdHRlciBwbG90XyBkZW5nYW4gYGdlb21fcG9pbnQoKWAga2VtdWRpYW4gdWt1cmFuIHRpdGlrbnlhIGRpc2VzdWFpa2FuIGRlbmdhbiBuaWxhaSBtYXNpbmctbWFzaW5nLiBVbnR1ayBtZW1idWF0IF9zY2F0dGVyIHBsb3RfIGRpYnV0dWhrYW4gZHVhIHZhcmlhYmxlIG51bWVyaWsgdW50dWsgbWVuZW50dWthbiBrb29yZGluYXQgeCAoaG9yaXpvbnRhbCkgZGFuIHkgKHZlcnRpa2FsKS4gUGFkYSBrYXN1cyBpbmkga2l0YSBha2FuIG1lbmdndW5ha2FuIHZhcmlhYmxlIGBtZWRfYWdlYCB5YW5nIG1lcnVwYWthbiBtZWRpYW4gdXNpYSBkYXJpIHNlbHVydWggcG9wdWxhc2kgcGVuZHVkdWsgZGkgc3VhdHUgbmVnYXJhIHNlYmFnYWkgc3VtYnUgeCwgZGFuIHZhcmlhYmxlIGBmYXRhbGl0eV9yYXRlYCBzZWJhZ2FpIHN1bWJ1IHkuDQoNCmBgYHtyfQ0KIyBTY2F0dGVyIHBsb3QNCmdncGxvdChkYXRhID0gd29ybGRfY292aWQsIG1hcHBpbmcgPSBhZXMoeCA9IG1lZF9hZ2UsIHkgPSBmYXRhbGl0eV9yYXRlKSkgKyANCiAgZ2VvbV9wb2ludCgpICsNCiAgdGhlbWVfbWluaW1hbCgpIA0KYGBgDQoNCkJlbHVtIGJhbnlhayBpbmZvcm1hc2kgeWFuZyBiaXNhIGtpdGEgcGVyb2xlaCBkYXJpIHZpc3VhbGlzYXNpIHRlcnNlYnV0IGtlY3VhbGkgc2F0dSB0aXRpayB5YW5nIGJlcmFkYSBwYWxpbmcgYXRhcyB5YW5nIGphdWggZGFyaSB0aXRpayB5YW5nIGxhaW4uIA0KDQpLaXRhIGF0dXIgdGluZ2thdCB0cmFuc3BhcmFuc2kgZGFyaSBtYXNpbmctbWFzaW5nIHRpdGlrIHRlcnNlYnV0IGFnYXIgZGFwYXQgdGVybGloYXQgYXBha2FoIGFkYSB0aXRpayB5YW5nIGJlcnR1bXB1ayBkZW5nYW4gYGFscGhhID0gMC4zYC4NCg0KYGBge3J9DQojIFBvaW50IG9wYWNpdHkNCmdncGxvdChkYXRhID0gd29ybGRfY292aWQsIG1hcHBpbmcgPSBhZXMoeCA9IG1lZF9hZ2UsIHkgPSBmYXRhbGl0eV9yYXRlKSkgKyANCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuMykgKw0KICB0aGVtZV9taW5pbWFsKCkgDQpgYGANCg0KVGVybnlhdGEgYWRhIGJlYmVyYXBhIG5lZ2FyYSB5YW5nIG5pbGFpIGBtZWRfYWdlYCBkYW4gYGZhdGFsaXR5X3JhdGVgLW55YSBoYW1waXIgc2FtYS4gVGVybGloYXQgZGFyaSBhZGFueWEgdGl0aWsgeWFuZyBsZWJpaCB0ZWJhbCB3YXJuYW55YSBkaWJhbmRpbmcgdGl0aWsgbGFpbiBrYXJlbmEgdGl0aWtueWEgYmVydHVtcHVrLg0KDQpCZXJpa3V0bnlhIGtpdGEgYXR1ciB1a3VyYW4gZGFyaSBtYXNpbmctbWFzaW5nIHRpdGlrIHNlc3VhaSBkZW5nYW4gbmlsYWkga2VwYWRhdGFuIHBlbmR1ZHVrIHBlciBLbV4yIHBhZGEgbmVnYXJhIHRlcnNlYnV0LCB5YWl0dSB2YXJpYWJsZSBgZGVuaXN0eV9rbTJgLiBLYXJlbmEgbmlsYWkgeWFuZyBha2FuIGtpdGEgZ3VuYWthbiBhZGFsYWggc2VidWFoIHZhcmlhYmxlIG1ha2Ega2l0YSBoYXJ1cyBtZW51bGlza2FubnlhIGRhbGFtIHNlYnVhaCBtYXBwaW5nIGBhZXMoKWAuDQoNCmBgYHtyfQ0KIyBCdWJibGUgY2hhcnQgc2l6ZQ0KZ2dwbG90KGRhdGEgPSB3b3JsZF9jb3ZpZCwgbWFwcGluZyA9IGFlcyh4ID0gbWVkX2FnZSwgeSA9IGZhdGFsaXR5X3JhdGUpKSArIA0KICBnZW9tX3BvaW50KGFlcyhzaXplID0gZGVuc2l0eV9rbTIpLCBhbHBoYSA9IDAuMykgKw0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KDQpLaXRhIGp1Z2EgYmlzYSBtZW55ZXN1YWlrYW4gd2FybmEgbWFzaW5nLW1hc2luZyB0aXRpayBiZXJkYXNhcmthbiBfQ29udGluZW50Xy1ueWEgZGVuZ2FuIG1lbmFtYmFoa2FuIGBjb2xvciA9IGNvbnRpbmVudGAgcGFkYSBgYWVzKClgLg0KDQpgYGB7cn0NCiMgQnVibGUgY2hhcnQgY29sb3INCmdncGxvdChkYXRhID0gd29ybGRfY292aWQsIG1hcHBpbmcgPSBhZXMoeCA9IG1lZF9hZ2UsIHkgPSBmYXRhbGl0eV9yYXRlKSkgKyANCiAgZ2VvbV9wb2ludChhZXMoc2l6ZSA9IGRlbnNpdHlfa20yLCBjb2xvciA9IGNvbnRpbmVudCksIGFscGhhID0gMC4zKSArDQogIHRoZW1lX21pbmltYWwoKQ0KYGBgDQoNCkthcmVuYSBDb250aW5lbnQgRXVyb3BlIGRhbiBOb3J0aCBBbWVyaWNhIG1lbXB1bnlhaSBrb29yZGluYXQgeWFuZyBiZXJkZWthdGFuIGRhbiB3YXJuYSB5YW5nIGhhbXBpciBzYW1hIHNlaGluZ2dhIGFnYWsgc3VsaXQgbWVtYmVkYWthbiBjb250aW5lbnQgdGVyc2VidXQuIEtpdGEgZGFwYXQgYXR1ciBzZW5kaXJpIHdhcm5hIHVudHVrIG1hc2luZy1tYXNpbmcgY29udGluZW50IGRlbmdhbiBmdW5nc2kgYHNjYWxlX2NvbG9yX21hbnVhbCgpYCBhdGF1IGBzY2FsZV9jb2xvdXJfbWFudWFsKClgLiAgDQoNCmBgYHtyfQ0KIyBDdXN0b20gY29sb3IgbWFudWFsDQpnZ3Bsb3QoZGF0YSA9IHdvcmxkX2NvdmlkLCBtYXBwaW5nID0gYWVzKHggPSBtZWRfYWdlLCB5ID0gZmF0YWxpdHlfcmF0ZSkpICsgDQogIGdlb21fcG9pbnQoYWVzKHNpemUgPSBkZW5zaXR5X2ttMiwgY29sb3IgPSBjb250aW5lbnQpLCBhbHBoYSA9IDAuMykgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IGMoIkFmcmljYSIgPSAiI2Y1M2QzZCIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkFzaWEiID0gIiNmN2E0MWUiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJBdXN0cmFsaWEvT2NlYW5pYSIgPSAiIzRiZGI1NyIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkV1cm9wZSIgPSAiIzNkYTJlYiIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk5vcnRoIEFtZXJpY2EiID0gIiNmMjI3ZWIiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTb3V0aCBBbWVyaWNhIiA9ICIjOGEwNDA0IikpDQpgYGANCg0KVGFtYmFoa2FuIGp1ZHVsIHV0YW1hLCBqdWR1bCBzdW1idS4gR2FudGkganVnYSBqdWR1bCBsZWdlbmQuIEtpdGEgYmlzYSBsYWt1a2FuIGhhbCBpbmkgZGVuZ2FuIG1lbmdndW5ha2FuIGZ1bmdzaSBgbGFicygpYC4NCg0KYGBge3J9DQojIEN1c3RvbSB0aXRsZSAmIGF4aXMgdGl0bGUNCmdncGxvdChkYXRhID0gd29ybGRfY292aWQsIG1hcHBpbmcgPSBhZXMoeCA9IG1lZF9hZ2UsIHkgPSBmYXRhbGl0eV9yYXRlKSkgKyANCiAgZ2VvbV9wb2ludChhZXMoc2l6ZSA9IGRlbnNpdHlfa20yLCBjb2xvciA9IGNvbnRpbmVudCksIGFscGhhID0gMC4zKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gYygiQWZyaWNhIiA9ICIjZjUzZDNkIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQXNpYSIgPSAiI2Y3YTQxZSIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkF1c3RyYWxpYS9PY2VhbmlhIiA9ICIjNGJkYjU3IiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRXVyb3BlIiA9ICIjM2RhMmViIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTm9ydGggQW1lcmljYSIgPSAiI2YyMjdlYiIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlNvdXRoIEFtZXJpY2EiID0gIiM4YTA0MDQiKSkgKw0KICBsYWJzKHRpdGxlID0gIk1lZGlhbiBQb3B1bGF0aW9uIEFnZSB2cyBGYXRhbGl0eSBSYXRlIiwNCiAgICAgICB4ID0gIk1lZGlhbiBQb3B1bGF0aW9uIEFnZSIsDQogICAgICAgeSA9ICJGYXRhbGl0eSBSYXRlIiwNCiAgICAgICBzaXplID0gIlBvcHVsYXRpb24gRGVuc2l0eSAoL2ttMikiLA0KICAgICAgIGNvbG9yID0gIkNvbnRpbmVudCIpDQpgYGANCg0KR2FudGkgZm9ybWF0IGp1ZHVsIHBhZGEgc3VtYnUgX0ZhdGFsaXR5IFJhdGVfIG1lbmphZGkgcGVyc2VudGFzZS4gR3VuYWthbiBmdW5nc2kgYHNjYWxlX3lfY29udGludW91cygpYCBzZXBlcnRpIHBhZGEgY2hhcnQgc2ViZWx1bW55YS4NCg0KYGBge3J9DQojIEJ1YmxlIGNoYXJ0IGN1c3RvbSBheGlzIHRpdGxlDQpnZ3Bsb3QoZGF0YSA9IHdvcmxkX2NvdmlkLCBtYXBwaW5nID0gYWVzKHggPSBtZWRfYWdlLCB5ID0gZmF0YWxpdHlfcmF0ZSkpICsgDQogIGdlb21fcG9pbnQoYWVzKHNpemUgPSBkZW5zaXR5X2ttMiwgY29sb3IgPSBjb250aW5lbnQpLCBhbHBoYSA9IDAuMykgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICBsYWJzKHRpdGxlID0gIk1lZGlhbiBQb3B1bGF0aW9uIEFnZSB2cyBGYXRhbGl0eSBSYXRlIiwNCiAgICAgICB4ID0gIk1lZGlhbiBQb3B1bGF0aW9uIEFnZSIsDQogICAgICAgeSA9ICJGYXRhbGl0eSBSYXRlIiwNCiAgICAgICBzaXplID0gIlBvcHVsYXRpb24gRGVuc2l0eSAoL2ttMikiLA0KICAgICAgIGNvbG9yID0gIkNvbnRpbmVudCIpICsNCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBjKCJBZnJpY2EiID0gIiNmNTNkM2QiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJBc2lhIiA9ICIjZjdhNDFlIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQXVzdHJhbGlhL09jZWFuaWEiID0gIiM0YmRiNTciLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJFdXJvcGUiID0gIiMzZGEyZWIiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJOb3J0aCBBbWVyaWNhIiA9ICIjZjIyN2ViIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiU291dGggQW1lcmljYSIgPSAiIzhhMDQwNCIpKSArDQogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6OnBlcmNlbnRfZm9ybWF0KGFjY3VyYWN5ID0gMSkpDQpgYGANCg0KSmlrYSB2aXN1YWxpc2FzaSBkaSBhdGFzIG1hc2loIGRpcmFzYSBzdWxpdCB1bnR1ayBtZW1pc2Foa2FuIGFudGFyIGNvbnRpbmVudCBtYWthIEFuZGEgZGFwYXQgbWVuZ2d1bmFrYW4gZnVuZ3NpIGBmYWNldF93cmFwKCB+IGNvbnRpbmVudClgLg0KDQpgYGB7cn0NCiMgQnVibGUgY2hhcnQgY3VzdG9tIGF4aXMgdGl0bGUNCmdncGxvdChkYXRhID0gd29ybGRfY292aWQsIG1hcHBpbmcgPSBhZXMoeCA9IG1lZF9hZ2UsIHkgPSBmYXRhbGl0eV9yYXRlKSkgKyANCiAgZ2VvbV9wb2ludChhZXMoc2l6ZSA9IGRlbnNpdHlfa20yLCBjb2xvciA9IGNvbnRpbmVudCksIGFscGhhID0gMC42KSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIGxhYnModGl0bGUgPSAiTWVkaWFuIFBvcHVsYXRpb24gQWdlIHZzIEZhdGFsaXR5IFJhdGUiLA0KICAgICAgIHggPSAiTWVkaWFuIFBvcHVsYXRpb24gQWdlIiwNCiAgICAgICB5ID0gIkZhdGFsaXR5IFJhdGUiLA0KICAgICAgIHNpemUgPSAiUG9wdWxhdGlvbiBEZW5zaXR5ICgva20yKSIsDQogICAgICAgY29sb3IgPSAiQ29udGluZW50IikgKw0KICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IGMoIkFmcmljYSIgPSAiI2Y1M2QzZCIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkFzaWEiID0gIiNmN2E0MWUiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJBdXN0cmFsaWEvT2NlYW5pYSIgPSAiIzRiZGI1NyIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkV1cm9wZSIgPSAiIzNkYTJlYiIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk5vcnRoIEFtZXJpY2EiID0gIiNmMjI3ZWIiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTb3V0aCBBbWVyaWNhIiA9ICIjOGEwNDA0IikpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6cGVyY2VudF9mb3JtYXQoYWNjdXJhY3kgPSAxKSkgKw0KICBmYWNldF93cmFwKH4gY29udGluZW50KQ0KYGBgDQoNCg0KIyBJbmRvbmVzaWEgRGFpbHkgQ2FzZXMgVHJlbmQNCg0KVGVyYWtoaXIsIGtpdGEgYWthbiBtZW1idWF0IGNoYXJ0IHlhbmcgZGFwYXQgbWVudW5qdWtrYW4gcG9sYSBwZW5hbWJhaGFuIGthc3VzIGhhcmlhbiBkaSBJbmRvbmVzaWEuIERhdGEgeWFuZyBkaWd1bmFrYW4gYWRhbGFoIGRhdGEgYGRhaWx5bmF0aW9uYWxgIHlnIGRpb2xhaCBkYXJpIHN1bWJlciA8aHR0cHM6Ly9ibnBiLWluYWNvdmlkMTkuaHViLmFyY2dpcy5jb20vPiBtZWxhbHVpIFtBUEldKGh0dHBzOi8vc2VydmljZXM1LmFyY2dpcy5jb20vVlM2SGRLUzBWZklodjhDdC9hcmNnaXMvcmVzdC9zZXJ2aWNlcy9TdGF0aXN0aWtfUGVya2VtYmFuZ2FuX0NPVklEMTlfSW5kb25lc2lhL0ZlYXR1cmVTZXJ2ZXIvMC9xdWVyeT93aGVyZT0xJTNEMSZvdXRGaWVsZHM9KiZvdXRTUj00MzI2JmY9anNvbikuDQoNCktpdGEgaW1wb3J0IGtlbXVkaWFuIGxpaGF0IHN0cnVrdHVyIGRhbiBiZWJlcmFwYSBiYXJpcyBwZXJ0YW1hIGRhcmkgZGF0YSB0ZXJzZWJ1dC4NCg0KYGBge3J9DQpkYWlseW5hdGlvbmFsIDwtIHJlYWQuY3N2KCJkYXRhL2RhaWx5bmF0aW9uYWwuY3N2IiwgaGVhZGVyID0gVFJVRSwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQ0KZGFpbHluYXRpb25hbCRkYXRlcyA8LSBhcy5EYXRlKGRhaWx5bmF0aW9uYWwkZGF0ZXMpDQpzdHIoZGFpbHluYXRpb25hbCkNCmBgYA0KDQpgYGB7cn0NCmhlYWQoZGFpbHluYXRpb25hbCwgbiA9IDEwKQ0KYGBgDQoNCkthcmVuYSBkYXRhIHlhbmcgZGltaWxpa2kgYWRhbGFoIGxhcG9yYW4gaGFyaWFuLCB0ZXJkYXBhdCB0YW5nZ2FsLCBkYW4ga2l0YSBpbmdpbiBtZW5nZXRhaHVpIHBvbGEga2FzdXMgaGFyaWFuLCBtYWthIGFrYW4gbGViaWggdGVwYXQgamlrYSBraXRhIGd1bmFrYW4gY2hhcnQgZ2FyaXMgKF9saW5lIGNoYXJ0XykgeWFuZyBiaWFzYSBkaWd1bmFrYW4gdW50dWsgdmlzdWFsaXNhc2kgZGF0YSBkZXJldCB3YWt0dSBhdGF1IF90aW1lIHNlcmllc18uDQoNCktpdGEgYWthbiBtZW5hbXBpbGthbiBrYXN1cyBwZW5hbWJhaGFuIGJhcnUgaGFyaWFuIChgZGFpbHlfY2FzZXNgKSwgc2VtYnVoIGhhcmlhbiAoYGRhaWx5X3JlY292ZXJlZGApLCBtZW5naW5nZ2FsIGhhcmlhbiAoYGRhaWx5X2RlYXRoc2ApIGRhbiBzZWJ1YWggdHJlbmQgdW50dWsga2FzdXMgcGVuYW1iYWhhbiBiYXJ1IGhhcmlhbiBwYWRhIF9saW5lIGNoYXJ0XyB0ZXJzZWJ1dCBzZXBlcnRpIGJlcmlrdXQgaW5pLg0KDQpgYGB7ciBlY2hvPUZBTFNFfQ0Ka3MgPC0ga3Ntb290aCh4ID0gZGFpbHluYXRpb25hbCRkYXRlcywgDQogICAgICAgICAgICAgIHkgPSBkYWlseW5hdGlvbmFsJGRhaWx5X2Nhc2VzLCANCiAgICAgICAgICAgICAga2VybmVsID0gIm5vcm1hbCIsIA0KICAgICAgICAgICAgICBiYW5kd2lkdGggPSAxMCwgDQogICAgICAgICAgICAgIHgucG9pbnRzID0gZGFpbHluYXRpb25hbCRkYXRlcykNCmRhaWx5bmF0aW9uYWwkZGFpbHlfdHJlbmQgPC0ga3MkeQ0KDQpnZ3Bsb3QoZGF0YSA9IGRhaWx5bmF0aW9uYWwsIG1hcHBpbmcgPSBhZXMoeCA9IGRhdGVzKSkgKyANCiAgZ2VvbV9saW5lKGFlcyh5ID0gZGFpbHlfY2FzZXMsIGNvbG9yID0gIk5ldyIpLCBzaXplID0gMC44KSArIA0KICBnZW9tX2xpbmUoYWVzKHkgPSBkYWlseV9yZWNvdmVyZWQsIGNvbG9yID0gIlJlY292ZXJlZCIpLCBzaXplID0gMC44KSArIA0KICBnZW9tX2xpbmUoYWVzKHkgPSBkYWlseV9kZWF0aHMsIGNvbG9yID0gIkRlYXRocyIpLCBzaXplID0gMC44KSArIA0KICB0aGVtZV9taW5pbWFsKCkgKyANCiAgc2NhbGVfY29sb3JfbWFudWFsKG5hbWUgPSAiQ2FzZXMiLA0KICAgICAgICAgICAgICAgICAgICAgdmFsdWVzID0gYygiTmV3IiA9ICJ5ZWxsb3ciLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlJlY292ZXJlZCIgPSAiZ3JlZW4iLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkRlYXRocyIgPSAicmVkIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJUcmVuZCIgPSAiZ3JleSIpKSArIA0KICBsYWJzKHRpdGxlID0gIkluZG9uZXNpYSBEYWlseSBDYXNlcyBUcmVuZCIsDQogICAgICAgeCA9ICJEYXRlcyIsDQogICAgICAgeSA9ICJEYWlseSBDYXNlcyIpICsgDQogIHNjYWxlX3hfZGF0ZShkYXRlX2JyZWFrcyA9ICIyIHdlZWtzIiwgZGF0ZV9sYWJlbHMgPSAiJWQvJW0iKSArDQogIGdlb21fbGluZShhZXMoeSA9IGRhaWx5X3RyZW5kLCBjb2xvciA9ICJUcmVuZCIpLCBzaXplID0gMC44KSArIA0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAidG9wIikNCmRhaWx5bmF0aW9uYWwkZGFpbHlfdHJlbmQgPC0gTlVMTA0KYGBgDQoNCg0KWWFuZyBwZXJsdSBraXRhIGluZ2F0IGFkYWxhaCBzdHJ1a3R1ciBkYXRhIHlhbmcga2l0YSBndW5ha2FuIHNlcGVydGkgYmVyaWt1dC4NCg0KYGBge3J9DQpkYWlseW5hdGlvbmFsICU+JSANCiAgc2VsZWN0KGRhdGVzLCBkYWlseV9jYXNlcywgZGFpbHlfcmVjb3ZlcmVkLCBkYWlseV9kZWF0aHMpICU+JSANCiAgaGVhZChuID0gMTApDQpgYGANCg0KRGVuZ2FuIGBnZ3Bsb3QyYCBraXRhIGFrYW4gbWVtYnVhdCBzZWJ1YWggX2xpbmUgY2hhcnRfIHVudHVrIG1hc2luZy1tYXNpbmcgdmFyaWFibGUgdGVyc2VidXQgdGVyaGFkYXAgdmFyaWFibGUgdGFuZ2dhbC4gS2FyZW5hIHlhbmcgbWVuamFkaSBzdW1idS14ICggX2hvcml6b250YWxfICkgYWRhbGFoIHZhcmlhYmxlIHRhbmdnYWwgdW50dWsgc2VtdWEgdmFyaWFibGUga2FzdXMsIG1ha2EgYGFlcyh4ID0gZGF0ZXMpYCBkYXBhdCBraXRhIHR1bGlza2FuIHNhdHUga2FsaSBzYWphIGRpIGFyZ3VtZW4gYG1hcHBpbmdgIHBhZGEgZnVuZ3NpIGBnZ3Bsb3QoKWAsIHNlZGFuZ2thbiBtYXNpbmctbWFzaW5nIHZhcmlhYmxlIGthc3VzIGtpdGEgdHVsaXNrYW4gZGkgbWFzaW5nLW1hc2luZyBgZ2VvbV9saW5lKClgLiAgDQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGEgPSBkYWlseW5hdGlvbmFsLCBtYXBwaW5nID0gYWVzKHggPSBkYXRlcykpICsgDQogIGdlb21fbGluZShhZXMoeSA9IGRhaWx5X2Nhc2VzLCBjb2xvciA9ICJOZXciKSkgKyANCiAgZ2VvbV9saW5lKGFlcyh5ID0gZGFpbHlfcmVjb3ZlcmVkLCBjb2xvciA9ICJSZWNvdmVyZWQiKSkgKyANCiAgZ2VvbV9saW5lKGFlcyh5ID0gZGFpbHlfZGVhdGhzLCBjb2xvciA9ICJEZWF0aHMiKSkgKw0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KDQpTZWxhbmp1dG55YSBraXRhIGFrYW4gbWVueWVzdWFpa2FuIHdhcm5hIHVudHVrIG1hc2luZy1tYXNpbmcga2FzdXMgYWdhciBsZWJpaCBjb2NvayB0ZXJoYWRhcCBqZW5pcyBrYXN1c255YS4gTWlzYWxueWEgdW50dWsga2FzdXMgYE5ld2Aga2l0YSBndW5ha2FuIHdhcm5hIGt1bmluZyAoYHllbGxvd2ApLCBgUmVjb3ZlcmVkYCBkZW5nYW4gd2FybmEgaGlqYXUgKGBncmVlbmApLCBkYW4gYERlYXRoc2AgZGVuZ2FuIHdhcm5hIG1lcmFoIChgcmVkYCkuIFRlYmFsbnlhIGdhcmlzIGp1Z2EgYWthbiBraXRhIHNlc3VhaWthbiBzZWhpbmdnYSBsZWJpaCB0ZWJhbC4NCg0KYGBge3J9DQojIENoYW5nZSBjb2xvciAmIGxpbmUgc2l6ZQ0KZ2dwbG90KGRhdGEgPSBkYWlseW5hdGlvbmFsLCBtYXBwaW5nID0gYWVzKHggPSBkYXRlcykpICsgDQogIGdlb21fbGluZShhZXMoeSA9IGRhaWx5X2Nhc2VzLCBjb2xvciA9ICJOZXciKSwgc2l6ZSA9IDAuOCkgKyANCiAgZ2VvbV9saW5lKGFlcyh5ID0gZGFpbHlfcmVjb3ZlcmVkLCBjb2xvciA9ICJSZWNvdmVyZWQiKSwgc2l6ZSA9IDAuOCkgKyANCiAgZ2VvbV9saW5lKGFlcyh5ID0gZGFpbHlfZGVhdGhzLCBjb2xvciA9ICJEZWF0aHMiKSwgc2l6ZSA9IDAuOCkgKw0KICB0aGVtZV9taW5pbWFsKCkgKyANCiAgc2NhbGVfY29sb3JfbWFudWFsKG5hbWUgPSAiQ2FzZXMiLCAjIGp1ZHVsIGxlZ2VuZA0KICAgICAgICAgICAgICAgICAgICAgdmFsdWVzID0gYygiTmV3IiA9ICJ5ZWxsb3ciLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlJlY292ZXJlZCIgPSAiZ3JlZW4iLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkRlYXRocyIgPSAicmVkIikpDQpgYGANCg0KDQpBZ2FyIHZpc3VhbGlzYXNpbnlhIGxlYmloIGJhaWsgc2VsYW5qdXRueWEga2l0YSB0YW1iYWhrYW4ganVkdWwgY2hhcnQgZGFuIGp1ZHVsIG1hc2luZy1tYXNpbmcgc3VtYnUuDQoNCmBgYHtyfQ0KIyBBZGQgdGl0bGUgYW5kIGF4aXMgdGl0bGUNCmdncGxvdChkYXRhID0gZGFpbHluYXRpb25hbCwgbWFwcGluZyA9IGFlcyh4ID0gZGF0ZXMpKSArIA0KICBnZW9tX2xpbmUoYWVzKHkgPSBkYWlseV9jYXNlcywgY29sb3IgPSAiTmV3IiksIHNpemUgPSAwLjgpICsgDQogIGdlb21fbGluZShhZXMoeSA9IGRhaWx5X3JlY292ZXJlZCwgY29sb3IgPSAiUmVjb3ZlcmVkIiksIHNpemUgPSAwLjgpICsgDQogIGdlb21fbGluZShhZXMoeSA9IGRhaWx5X2RlYXRocywgY29sb3IgPSAiRGVhdGhzIiksIHNpemUgPSAwLjgpICsNCiAgdGhlbWVfbWluaW1hbCgpICsgDQogIHNjYWxlX2NvbG9yX21hbnVhbChuYW1lID0gIkNhc2VzIiwgIyBqdWR1bCBsZWdlbmQNCiAgICAgICAgICAgICAgICAgICAgIHZhbHVlcyA9IGMoIk5ldyIgPSAieWVsbG93IiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJSZWNvdmVyZWQiID0gImdyZWVuIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJEZWF0aHMiID0gInJlZCIpKSArIA0KICBsYWJzKHRpdGxlID0gIkluZG9uZXNpYSBEYWlseSBDYXNlcyBUcmVuZCIsDQogICAgICAgeCA9ICJEYXRlcyIsDQogICAgICAgeSA9ICJEYWlseSBDYXNlcyIpDQpgYGANCg0KQmVyaWt1dG55YSBraXRhIHJ1YmFoIGZvcm1hdCBsYWJlbCBwYWRhIHN1bWJ1IHggKERhdGVzKSBhZ2FyIGtpdGEgYmlzYSBtZW5nZXRhaHVpIHRhbmdnYWxueWEuIEZvcm1hdCB5YW5nIGFrYW4gZGlndW5ha2FuIGFkYWxhaCAiZGQvbW0iIGthcmVuYSB0YWh1bm55YSBzYW1hLCB5YWl0dSAyMDIwLCBtYWthIHRpZGFrIHBlcmx1IGRpdGFtcGlsa2FuLiBLYXJlbmEgbmlsYWkgcGFkYSBzdW1idSB4IGFkYWxhaCB0YW5nZ2FsLCBtYWthIGtpdGEgZGFwYXQgZ3VuYWthbiBmdW5nc2kgYHNjYWxlX3hfZGF0ZSgpYC4gS2VtdWRpYW4gYXJndW1lbiBgZGF0ZV9icmVha3MgPSAiMiB3ZWVrcyJgIGFydGlueWEgYW50YXIgdGFuZ2dhbCB5YW5nIGRpdGFtcGlsa2FuIChfbWFqb3IgdGlja3NfKSBiZXJqYXJhayAyIG1pbmdndSwgc2VoaW5nZ2EgX21pbm9yIHRpY2tzXyBhZGFsYWggMSBtaW5nZ3UuDQoNCmBgYHtyfQ0KIyBDaGFuZ2UgeC1heGlzIGZvcm1hdA0KZ2dwbG90KGRhdGEgPSBkYWlseW5hdGlvbmFsLCBtYXBwaW5nID0gYWVzKHggPSBkYXRlcykpICsgDQogIGdlb21fbGluZShhZXMoeSA9IGRhaWx5X2Nhc2VzLCBjb2xvciA9ICJOZXciKSwgc2l6ZSA9IDAuOCkgKyANCiAgZ2VvbV9saW5lKGFlcyh5ID0gZGFpbHlfcmVjb3ZlcmVkLCBjb2xvciA9ICJSZWNvdmVyZWQiKSwgc2l6ZSA9IDAuOCkgKyANCiAgZ2VvbV9saW5lKGFlcyh5ID0gZGFpbHlfZGVhdGhzLCBjb2xvciA9ICJEZWF0aHMiKSwgc2l6ZSA9IDAuOCkgKw0KICB0aGVtZV9taW5pbWFsKCkgKyANCiAgc2NhbGVfY29sb3JfbWFudWFsKG5hbWUgPSAiQ2FzZXMiLCAjIGp1ZHVsIGxlZ2VuZA0KICAgICAgICAgICAgICAgICAgICAgdmFsdWVzID0gYygiTmV3IiA9ICJ5ZWxsb3ciLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlJlY292ZXJlZCIgPSAiZ3JlZW4iLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkRlYXRocyIgPSAicmVkIikpICsgDQogIGxhYnModGl0bGUgPSAiSW5kb25lc2lhIERhaWx5IENhc2VzIFRyZW5kIiwNCiAgICAgICB4ID0gIkRhdGVzIiwNCiAgICAgICB5ID0gIkRhaWx5IENhc2VzIikgKyANCiAgc2NhbGVfeF9kYXRlKGRhdGVfYnJlYWtzID0gIjIgd2Vla3MiLCBkYXRlX2xhYmVscyA9ICIlZC8lbSIpDQpgYGANCg0KDQpTZWxhbmp1dG55YSBraXRhIGFrYW4gbWVuYW1iYWhrYW4gc2VidWFoIGdhcmlzIHRyZW5kIHBhZGEgY2hhcnQgZGkgYXRhcyB5YW5nIG1lbnVuanVra2FuIHBvbGEgcGVuYW1iYWhhbiBrYXN1cyBiYXJ1IGhhcmlhbiBkaSBJbmRvbmVzaWEuIFlhbmcga2l0YSBidXR1aGthbiBhZGFsYWggZGF0YSB0YW5nZ2FsIChgZGF0ZXNgKSBkYW4ganVtbGFoIGthc3VzIHBlbmFtYmFoYW4gaGFyaWFuIChgZGFpbHlfY2FzZXNgKS4gVW50dWsgbWVtYnVhdCBkYXRhIHRyZW5kLW55YSBraXRhIGd1bmFrYW4gX0tlcm5lbCBTbW9vdGhpbmdfIGRlbmdhbiBmdW5nc2kgYGtzbW9vdGhgIGRhcmkgcGFja2FnZSB7c3RhdHN9IHlhbmcgc3VkYWggbWVuamFkaSBiYWdpYW4gZGFyaSBiYXNlIFIga2V0aWthIGtpdGEgaW5zdGFsbCBSLiBUaWRhayBhZGEgYWxhc2FuIGtodXN1cyB5YW5nIHNheWEgZ3VuYWthbiBzZWJhZ2FpIHBlZG9tYW4ga2V0aWthIG1lbWlsaWggZnVuZ3NpIGluaS4gQWRhIGJlYmVyYXBhIG1ldG9kZSBsYWluIHlhbmcgZGFwYXQgZGlndW5ha2FuIHVudHVrIG1lbWJ1YXQgdHJlbmQgc2VwZXJ0aSB5YW5nIGRpdHVsaXMgZGkgW2FydGlrZWwgaW5pXShodHRwczovL3d3dy5kaXNwbGF5ci5jb20vc21vb3RoaW5nLXRpbWUtc2VyaWVzLWRhdGEvP3V0bV9tZWRpdW09RmVlZCZ1dG1fc291cmNlPVN5bmRpY2F0aW9uKS4NCg0KYGBge3J9DQojIEFkZCB0cmVuZCBsaW5lDQprcyA8LSBrc21vb3RoKHggPSBkYWlseW5hdGlvbmFsJGRhdGVzLCANCiAgICAgICAgICAgICAgeSA9IGRhaWx5bmF0aW9uYWwkZGFpbHlfY2FzZXMsIA0KICAgICAgICAgICAgICBrZXJuZWwgPSAibm9ybWFsIiwgDQogICAgICAgICAgICAgIGJhbmR3aWR0aCA9IDEwLCANCiAgICAgICAgICAgICAgeC5wb2ludHMgPSBkYWlseW5hdGlvbmFsJGRhdGVzKQ0KZGFpbHluYXRpb25hbCRkYWlseV90cmVuZCA8LSBrcyR5DQpgYGANCg0KS2l0YSB0YW1iYWhrYW4gZGF0YSB0cmVuZCB0ZXJzZWJ1dCBtZW5nZ3VuYWthbiBgZ2VvbV9saW5lKClgLg0KDQpgYGB7cn0NCmdncGxvdChkYXRhID0gZGFpbHluYXRpb25hbCwgbWFwcGluZyA9IGFlcyh4ID0gZGF0ZXMpKSArIA0KICBnZW9tX2xpbmUoYWVzKHkgPSBkYWlseV9jYXNlcywgY29sb3IgPSAiTmV3IiksIHNpemUgPSAwLjgpICsgDQogIGdlb21fbGluZShhZXMoeSA9IGRhaWx5X3JlY292ZXJlZCwgY29sb3IgPSAiUmVjb3ZlcmVkIiksIHNpemUgPSAwLjgpICsgDQogIGdlb21fbGluZShhZXMoeSA9IGRhaWx5X2RlYXRocywgY29sb3IgPSAiRGVhdGhzIiksIHNpemUgPSAwLjgpICsNCiAgdGhlbWVfbWluaW1hbCgpICsgDQogIHNjYWxlX2NvbG9yX21hbnVhbChuYW1lID0gIkNhc2VzIiwNCiAgICAgICAgICAgICAgICAgICAgIHZhbHVlcyA9IGMoIk5ldyIgPSAieWVsbG93IiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJSZWNvdmVyZWQiID0gImdyZWVuIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJEZWF0aHMiID0gInJlZCIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVHJlbmQiID0gImdyZXkiKSkgKyANCiAgbGFicyh0aXRsZSA9ICJJbmRvbmVzaWEgRGFpbHkgQ2FzZXMgVHJlbmQiLA0KICAgICAgIHggPSAiRGF0ZXMiLA0KICAgICAgIHkgPSAiRGFpbHkgQ2FzZXMiKSArIA0KICBzY2FsZV94X2RhdGUoZGF0ZV9icmVha3MgPSAiMiB3ZWVrcyIsIGRhdGVfbGFiZWxzID0gIiVkLyVtIikgKyANCiAgZ2VvbV9saW5lKGFlcyh5ID0gZGFpbHlfdHJlbmQsIGNvbG9yID0gIlRyZW5kIiksIHNpemUgPSAwLjgpDQoNCmBgYA0KDQpVbnR1ayB2aXN1YWxpc2FzaSBpbmkgc2F5YSBwcmliYWRpIGxlYmloIHN1a2EgamlrYSBsZWdlbmQtbnlhIGRpbGV0YWthbiBkaSBhdGFzIGNoYXJ0LiBLYXJlbmEgaXR1IHNheWEgdGFtYmFoa2FuIGZ1bmdzaSBgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpYC4NCg0KYGBge3J9DQojIEN1c3RvbSBsZWdlbmQgJiBwb3NpdGlvbg0KZ2dwbG90KGRhdGEgPSBkYWlseW5hdGlvbmFsLCBtYXBwaW5nID0gYWVzKHggPSBkYXRlcykpICsgDQogIGdlb21fbGluZShhZXMoeSA9IGRhaWx5X2Nhc2VzLCBjb2xvciA9ICJOZXciKSwgc2l6ZSA9IDAuOCkgKyANCiAgZ2VvbV9saW5lKGFlcyh5ID0gZGFpbHlfcmVjb3ZlcmVkLCBjb2xvciA9ICJSZWNvdmVyZWQiKSwgc2l6ZSA9IDAuOCkgKyANCiAgZ2VvbV9saW5lKGFlcyh5ID0gZGFpbHlfZGVhdGhzLCBjb2xvciA9ICJEZWF0aHMiKSwgc2l6ZSA9IDAuOCkgKyANCiAgdGhlbWVfbWluaW1hbCgpICsgDQogIHNjYWxlX2NvbG9yX21hbnVhbChuYW1lID0gIkNhc2VzIiwNCiAgICAgICAgICAgICAgICAgICAgIHZhbHVlcyA9IGMoIk5ldyIgPSAieWVsbG93IiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJSZWNvdmVyZWQiID0gImdyZWVuIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJEZWF0aHMiID0gInJlZCIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVHJlbmQiID0gImdyZXkiKSkgKyANCiAgbGFicyh0aXRsZSA9ICJJbmRvbmVzaWEgRGFpbHkgQ2FzZXMgVHJlbmQiLA0KICAgICAgIHggPSAiRGF0ZXMiLA0KICAgICAgIHkgPSAiRGFpbHkgQ2FzZXMiKSArIA0KICBzY2FsZV94X2RhdGUoZGF0ZV9icmVha3MgPSAiMiB3ZWVrcyIsIGRhdGVfbGFiZWxzID0gIiVkLyVtIikgKw0KICBnZW9tX2xpbmUoYWVzKHkgPSBkYWlseV90cmVuZCwgY29sb3IgPSAiVHJlbmQiKSwgc2l6ZSA9IDAuOCkgKyANCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpDQoNCmBgYA0KDQoNCg==