Email:
RPubs: https://rpubs.com/datazerotohero/
Github: https://github.com/datazerotohero/


Visualisasi data adalah teknisi mengambil informasi dari data ke dalam konteks visual, seperti bagan, grafik, dan peta. Visualisasi data dimungkinkan untuk menangani data besar yang kecil dan peristiwa menjadi lebih mudah dipahami oleh otak manusia, dan visualisasi juga membuatnya lebih dapat diandalkan untuk mendeteksi pola, tren, dan outlier dalam kelompok data.

R adalah platform luar biasa untuk analisis data, yang mampu menciptakan hampir semua jenis grafik. Buku ini membantu Anda membuat visualisasi paling populer - dari plot cepat dan kotor hingga grafik siap publikasi. Di sini kita akan mempelajari cara memvisualisasikan data dari univariat, bivariat, dan multivariat. Dari yang sangat mendasar hingga uang muka, silakan ikuti instruksi berikut selangkah demi selangkah., and outliers in groups of the data.

1 Data Univariate

Plot univariat biasanya digunakan untuk melakukan distribusi data dari satu variabel. Variabel dapat dikategorikan (misalnya, jenis kelamin, ras, negara, kota, dll) atau kuantitatif (misalnya, usia, berat, inflasi, tingkat, dll).

1.1 Kategoris

Distribusi variabel kategoris tunggal biasanya diplot dengan bagan batang, pie-chart, atau (kurang umum) sebuah treemap.

1.1.1 Bagan Batang

Marriage dataset, saya ambil dari package mosaicData. Kami menggunakan bagan batang untuk menampilkan distribusi peserta pernikahan oleh Zodiak.

library(ggplot2)                                     # untuk visualisasi
#setwd("C:/Users/Bakti/Desktop/")                    # jangan lupa untuk mengatur direktori kerja
Marriage<- read.csv("C:/Nikita/Tugas/Data Structutes and Algorithms/WEEK 13/Marriage.csv")             # memuat data dari PC Anda'
ggplot(Marriage, aes(x = zodiacs)) +                 # plot distribusi 'Zodiak' 
  geom_bar(fill = "cornflowerblue",color= "black")+  # Anda dapat mengubah warna 
  theme_minimal() +                                  # gunakan tema minimal
  labs(x = "Zodiacs",                                # Anda dapat memodifikasi label dan plot judul
       y = "Frequency", 
       title = "Marriage Participants by Zodiacs")         
Categorical Bar Chart 1

Categorical Bar Chart 1

Bar dapat mewakili persen daripada hitungan. Untuk bagan batang (zodiak), kode aes(x=sign) sebenarnya adalah pintasan untuk aes(x = sign, y = .. count..) , di mana .. Menghitung.. adalah variabel khusus yang mewakili frekuensi dalam setiap kategori. Anda dapat menggunakan ini untuk menghitung persentase, dengan menentukan variabel y secara eksplisit.

Dalam R, warna dapat ditentukan baik dengan nama (e.g col = “red”) seperti yang Anda lihat pada gambar berikut.

atau Anda dapat menetapkan warna dengan menggunakan triplet RGB heksadesimal (such as col = “#FFCC00”) More. Selain itu, Anda juga dapat menggunakan sistem warna lain seperti sistem warna yang diambil dari RColorBrewer package More dan package grDevices (Anda mungkin sudah memiliki dimuat ini) berisi sejumlah palet, ketik ini ?rainbow di Rconsole Anda. Mari kita pertimbangkan grafik berikut:

library(ggplot2)                                     # untuk visualisasi
ggplot(Marriage, 
   aes(x = zodiacs, 
       y = ..count.. / sum(..count..))) + 
  geom_bar(fill = rainbow(12), color= "azure4") +
  theme_minimal() +                                  # gunakan minimal tema
  labs(x = "Zodiacs", 
       y = "Percent", 
  title  = "Marriage Participants in Percent") +
  scale_y_continuous(labels = scales::percent)       # tambah simbol % ke label sumbu y
 Categorical Bar Chart 2

Categorical Bar Chart 2

Seringkali membantu untuk mengurutkan bar berdasarkan frekuensi. Dalam kode di bawah ini, frekuensi dihitung secara eksplisit. Kemudian fungsi reorder digunakan untuk mengurutkan kategori berdasarkan frekuensi. Opsi stat="identity" memberi tahu fungsi plotting untuk tidak menghitung hitungan, karena disediakan secara langsung.

library(dplyr)                                       # untuk manipulasi data
library(ggplot2)                                     # untuk manipulasi data
plotdata <- Marriage %>%                             # muat dataset
 count(zodiacs)                                      # jumlah peserta di setiap 'zodiak'
# plot bar dalam urutan naik
ggplot(plotdata,                                     
       aes(x = reorder(zodiacs, n), 
           y = n)) + 
  geom_bar(stat = "identity",
           fill = rainbow(12), 
           color= "azure4") +
   theme_minimal() +                                  # gunakan minimal tema
  labs(x = "Zodiacs", 
       y = "Frequency", 
  title  = "Sorting Categories")
Categorical Bar Chart 3

Categorical Bar Chart 3

Jika Anda mungkin ingin memberi label setiap batang dengan nilai numeriknya, periksa kode berikut:

library(dplyr)                                       # untuk manipulasi data
library(ggplot2)                                     # untuk visualisasi
library(scales)                                      # secara otomatis menentukan jeda/label 
plotdata <- Marriage %>%
  count(zodiacs) %>%
  mutate(pct = n / sum(n),
         pctlabel = paste0(round(pct*100), "%"))
# plot bar sebagai persentase, dalam urutan menurun dengan label batang
ggplot(plotdata, 
       aes(x = reorder(zodiacs, -pct),
           y = pct)) + 
  geom_bar(stat = "identity", 
           fill = rainbow(12), 
          color = "azure4") +
  geom_text(aes(label = pctlabel), 
            vjust = -0.25) +
  theme_minimal() +                                  # gunakan minimal tema
  scale_y_continuous(labels = percent) +
  labs(x = "Zodiacs", 
       y = "Percent", 
       title  = "Labeling Bars")
Categorical Bar Chart 4

Categorical Bar Chart 4

Kadang-kadang label kategori mungkin tumpang tindih, itu sangat menjengkelkan kan?. Jadi, Anda dapat memutar label sumbu.

library(ggplot2)                                     # untuk visualisasi
library(scales)                                      # secara otomatis menentukan istirahat / label 
# plot bar sebagai persentase, 
# dalam urutan menurun dengan label batang
ggplot(plotdata, 
       aes(x = reorder(zodiacs, -pct),
           y = pct)) + 
  geom_bar(stat = "identity", 
           fill = rainbow(12), 
           color = "azure4") +
  geom_text(aes(label = pctlabel), 
            vjust = -0.25) +
  scale_y_continuous(labels = percent) +
  theme_minimal() +                                  # gunakan tema minimal
  labs(x = "Zodiacs", 
       y = "Percent", 
       title  = "Overlapping Labels")+
  theme(axis.text.x = element_text(angle = 25, hjust = 1))
Categorical Bar Chart 5

Categorical Bar Chart 5

Atau, Anda dapat menangani situasi ini dengan membalik sumbu x dan y.

library(ggplot2)                                     # untuk visualisasi
library(scales)                                      # secara otomatis menentukan istirahat / label 
# plot bar sebagai persentase, 
# dalam urutan menurun dengan label batang
ggplot(plotdata, 
       aes(x = reorder(zodiacs, -pct),
           y = pct)) + 
  geom_bar(stat = "identity", 
           fill = rainbow(12), 
           color = "azure4") +
  geom_text(aes(label = pctlabel), 
            hjust = -0.10) +
  scale_y_continuous(labels = percent) +
  theme_minimal() +                                  # gunakan minimal tema
  labs(x = "Zodiak", 
       y = "Percent", 
       title  = "Overlapping Labels")+
  coord_flip()
Categorical Bar Chart 6

Categorical Bar Chart 6

1.1.2 Pie Chart

Pie charts kontroversial dalam statistik. Jika tujuan Anda adalah membandingkan frekuensi kategori, Anda lebih baik dengan bagan batang (manusia lebih baik menilai panjang batang daripada volume irisan pai). Jika tujuan Anda adalah membandingkan setiap kategori dengan keseluruhan (misalnya, bagian peserta apa yang hispanik dibandingkan dengan semua peserta), dan jumlah kategori kecil, maka bagan pai dapat berfungsi untuk Anda. Dibutuhkan sedikit lebih banyak kode untuk membuat bagan pai yang menarik di R.

Ini adalah contoh untuk membuat bagan pai ggplot2 dasar:

library(dplyr)                                       # untuk manipulasi data
library(ggplot2)                                     # untuk visualisasi
library(scales)                                      # secara otomatis menentukan istirahat / label 
# Persiapan data
plotdata <- Marriage %>%
  count(race) %>%
  arrange(desc(race)) %>%
  mutate(prop = round(n*100/sum(n), 1),
         lab.ypos = cumsum(prop) - 0.5*prop)
# buat Pie chart
mycols <- c("#0073C2FF", "#EFC000FF", "#868686FF", "#CD534CFF")
ggplot(plotdata, aes(x = "", y = prop, fill = race)) +
  geom_bar(width = 1, stat = "identity", color = "white") +
  coord_polar("y", start = 0)+
  geom_text(aes(y = lab.ypos, label = prop), color = "white")+
  scale_fill_manual(values = mycols) +
  theme_void()+
  labs(title = "Marriage Participants by Race")
Categorical Pie Chart

Categorical Pie Chart

Bagan donat hanyalah bagan pai sederhana dengan lubang di dalamnya. Satu-satunya perbedaan antara kode bagan pai adalah kita mengatur: x = 2 dan xlim = c(0,5, 2,5) untuk membuat lubang di dalam bagan pai. Selain itu, argumen width dalam fungsi geom_bar() tidak lagi diperlukan.

library(ggplot2)                                     # untuk visualisasi
library(scales)                                      # secara otomatis menentukan istirahat / label
# buat bagan Donat
ggplot(plotdata, aes(x = 2, y = prop, fill = race)) +
  geom_bar(stat = "identity", color = "white") +
  coord_polar(theta = "y", start = 0)+
  geom_text(aes(y = lab.ypos, label = prop), color = "white")+
  scale_fill_manual(values = mycols) +
  theme_void()+
  xlim(0.5, 2.5)+
  labs(title = "Marriage Participants by Race")
Categorical Donut Chart 1

Categorical Donut Chart 1

sekarang let’s dapatkan label mewah dan tambahkan, sambil menghapus legenda.

library(ggplot2)                                     # untuk visualisasi
library(scales)                                      # secara otomatis menentukan istirahat / label
# tambahkan label persen
plotdata$percent <- paste0(plotdata$race, "\n",
                         round(plotdata$prop), "%")
# membuat grafik Donat dalam persen
ggplot(plotdata, aes(x = 2, y = prop, fill = race)) +
  geom_bar(stat = "identity", color = "white") +
  coord_polar(theta = "y", start = 0)+
  geom_text(aes(y = lab.ypos, label = percent), color = "white")+
  scale_fill_manual(values = mycols) +
  theme_void()+
  xlim(0.5, 2.5)+
  labs(title = "Marriage Participants by Race")
Categorical Donut Chart 2

Categorical Donut Chart 2

  #tema(legend.position = "FALSE")

1.1.3 Tree Map

Alternatif untuk bagan pai adalah peta pohon. Tidak seperti bagan pai, ia dapat menangani variabel kategoris yang memiliki banyak tingkatan.

library(ggplot2)                                     # for visualization
library(treemapify)                                  # for visualization
library(scales)                                      # automatically determining breaks/labels
plotdata <- Marriage %>%
  count(officialTitle)
ggplot(plotdata, 
       aes(fill = officialTitle, 
           area = n)) +
  geom_treemap() + 
  labs(title = "Marriage Participants by Officiate")
Categorical Tree Map 1

Categorical Tree Map 1

Berikut adalah versi yang lebih berguna dengan label.

ggplot(plotdata, 
       aes(fill = officialTitle, 
           area = n, 
           label = officialTitle)) +
  geom_treemap() + 
  geom_treemap_text(colour = "white", 
                    place = "centre") +
  labs(title = "Marriage Participants by Officiate") +
  theme(legend.position = "none")
Categorical Tree Map 2

Categorical Tree Map 2

1.2 Berkelanjutan

Distribusi variabel kuantitatif tunggal biasanya diplot dengan plot histogram, kepadatan kernel, atau plot titik.

1.2.1 Histogram

Menggunakan kumpulan data Pernikahan, let’s plot usia peserta pernikahan.

library(ggplot2)                                     # for visualization
ggplot(Marriage, aes(x = age)) +
  geom_histogram(fill = "cornflowerblue", 
                 color = "white",bins = 20) + 
  theme_minimal() +                                  # use a minimal theme
  labs(title="Marriage Participants by age (Basic)",
       x = "Age")
Quantitative Histogram

Quantitative Histogram

Sebagian besar peserta tampaknya berada di awal 20’s mereka dengan kelompok lain di 40’s, mereka, dan kelompok yang jauh lebih kecil di usia enam puluhan dan awal tahun tujuh puluhan. Ini akan menjadi distribusi multimodal. Warna histogram dapat dimodifikasi menggunakan dua opsi:

  • Isi - warna isian untuk bar
  • warna - warna perbatasan di sekitar bar

Atau, Anda dapat menentukan ‘binwidth’, lebar tempat sampah yang diwakili oleh bar.

library(ggplot2)                                     # for visualization
library(scales)                                      # automatically determining breaks/labels 
ggplot(Marriage, 
       aes(x = age, 
           y= ..count.. / sum(..count..))) +
  geom_histogram(fill = "cornflowerblue", 
                 color = "white", 
                 binwidth = 5) +
  theme_minimal() +                                  # use a minimal theme
  labs(title="Marriage Participants by age (Alternative Bins and bandwidths)", 
       y = "Percent",
       x = "Age") +
  scale_y_continuous(labels = percent)
Quantitative Histogram 2

Quantitative Histogram 2

Seperti halnya bagan batang, sumbu y dapat mewakili hitungan atau persen dari total.

library(ggplot2)                                     # for visualization
library(scales)                                      # automatically determining breaks/labels 
ggplot(Marriage, 
       aes(x = age, 
           y= ..count.. / sum(..count..))) +
  geom_histogram(fill = "cornflowerblue", 
                 color = "white", 
                 binwidth = 5) +
  theme_minimal() +                                  # use a minimal theme
  labs(title="Marriage Participants by age (Percent)", 
       y = "Percent",
       x = "Age") +
  scale_y_continuous(labels = percent)
Quantitative Histogram 3

Quantitative Histogram 3

1.2.2 Kernel Density Plot

Alternatif untuk histogram adalah plot kepadatan kernel. Secara teknis, estimasi kepadatan kernel adalah metode non-parametrik untuk memperkirakan fungsi kerapatan probabilitas dari variabel acak berkelanjutan. (Apa??) Pada dasarnya, kami mencoba menggambar histogram yang dihaluskan, di mana area di bawah kurva sama dengan satu.

library(ggplot2)                                     # for visualization
ggplot(Marriage, aes(x = age)) +
  geom_density(fill = "indianred3") +
  theme_minimal() +                                  # use a minimal theme
  labs(title = "Marriage Participants by age")
Quantitative Kenel Density Plot

Quantitative Kenel Density Plot

Grafik menunjukkan distribusi skor. Misalnya, proporsi kasus antara 20 dan 40 tahun akan diwakili oleh area di bawah kurva antara 20 dan 40 pada sumbu x. Seperti halnya bagan sebelumnya, kita juga bisa menggunakan isian dan warna untuk menentukan warna isian dan batas.

1.2.3 Menghaluskan Parameter

Tingkat kehalusan dikendalikan oleh parameter bandwidth bw. Untuk menemukan nilai default untuk variabel tertentu, gunakan fungsi bw.nrd0. Nilai yang lebih besar akan menghasilkan lebih banyak kehalusan, sementara nilai yang lebih kecil akan menghasilkan lebih sedikit smoothing.

library(ggplot2)                                     # for visualization
bw.nrd0(Marriage$age)                                # default bandwidth for the age variable
## [1] 5.181946
ggplot(Marriage, aes(x = age)) +
  geom_density(fill = "deepskyblue", 
               bw = 1) +
  theme_minimal() +                                  # use a minimal theme
  labs(title = "Participants by age",
       subtitle = "bandwidth = 1")
Smoothing Parameter Plot

Smoothing Parameter Plot

Plot kepadatan kernel memungkinkan Anda untuk dengan mudah melihat skor mana yang paling sering dan yang relatif jarang. Namun mungkin sulit untuk menjelaskan arti sumbu y kepada non-ahli statistik. (Tapi itu akan membuat Anda terlihat benar-benar cerdas di pesta!)

1.2.4 Bagan Titik

Alternatif lain untuk histogram adalah bagan titik. Sekali lagi, variabel kuantitatif dibagi menjadi tempat sampah, tetapi daripada bilah ringkasan, setiap pengamatan diwakili oleh titik. Secara default, lebar titik sesuai dengan lebar biner, dan titik-titik ditumpuk, dengan setiap titik mewakili satu pengamatan. Ini bekerja paling baik ketika jumlah pengamatan kecil (katakanlah, kurang dari 150). Opsi isian dan warna dapat digunakan untuk menentukan warna isian dan batas masing-masing titik

library(ggplot2)                                     # for visualization
ggplot(Marriage, aes(x = age)) +
  geom_dotplot(fill = "gold", 
               color = "azure4",
               binwidth = 2) +
  theme_minimal() +                                  # use a minimal theme
  labs(title = "Participants by age",
       y = "Proportion",
       x = "Age")
Dot Chart

Dot Chart

Ada lebih banyak opsi yang tersedia. Click here untuk details dan contoh.

2 Bivariate Data

Grafik bivariat menampilkan hubungan antara dua variabel. Jenis grafik akan tergantung pada tingkat pengukuran variabel (kategoris atau kuantitatif)

2.1 Kategoris vs. Kategoris

2.1.1 Bagan Batang Tumpuk

Mari kita plot hubungan antara kelas mobil dan jenis penggerak (roda depan, roda belakang, atau penggerak 4 roda) untuk mobil diFuel economy dataset.

library(ggplot2)                                     # for visualization
mpg$drv<-factor(mpg$drv, 
                levels = c("f", "r", "4"),
                labels = c("front-wheel", "rear-wheel", "4-wheel"))
# stacked bar chart
ggplot(mpg, 
       aes(x = class, 
           fill = drv)) + 
  geom_bar(position = "fill") +
  theme_minimal() +                                  # use a minimal theme
  labs(y = "Proportion")
Stacked Bar Chart

Stacked Bar Chart

2.1.2 Bagan Batang Yang Dikelompokkan

Bagan batang yang dikelompokkan menempatkan bilah untuk variabel kategoris kedua berdampingan. Untuk membuat plot bilah yang dikelompokkan, gunakan opsi posisi = "tunggal". Perhatikan bahwa opsi ini hanya tersedia dalam versi pengembangan terbaru ggplot2, tetapi harus tersedia secara umum segera.

library(ggplot2)                                     # for visualization
ggplot(mpg, aes(x = class, fill = drv)) +
  theme_minimal() +                                  # use a minimal theme
  geom_bar(position = position_dodge(preserve = "single"))
Grouped Bar Chart

Grouped Bar Chart

2.1.3 Bagan Batang Tersegmentasi

Plot bar tersegmentasi adalah plot bar tumpuk di mana setiap batang mewakili 100 persen. Anda dapat membuat bagan batang tersegmentasi menggunakan opsi posisi = “terisi”. Jenis plot ini sangat berguna jika tujuannya adalah untuk membandingkan persentase kategori dalam satu variabel di setiap tingkat variabel lain. Misalnya, proporsi mobil berkenda roda depan naik saat Anda bergerak dari ringkas, ke ukuran sedang, ke minivan.

library(dplyr)                                       # for data manipulation
library(ggplot2)                                     # for visualization
library(scales)                                      # automatically determining breaks/labels 
# create a summary dataset (data manipulation)
plotdata <- mpg %>%
  group_by(class, drv) %>%
  dplyr::summarize(n = n()) %>%
  mutate(pct = n/sum(n),
         lbl = scales::percent(pct))
# create segmented bar chart
# adding labels to each segmen
ggplot(plotdata, 
       aes(x = factor(class),
           y = pct,
        fill = factor(drv))) +
  geom_bar(stat = "identity",
           position = "fill") +
  scale_y_continuous(breaks = seq(0, 1, .2), 
                     label = percent)+
  geom_text(aes(label = lbl),
            size = 3,
            position = position_stack(vjust = 0.5)) +
  scale_fill_brewer(palette = "Set2") +
  theme_minimal() +                                  # use a minimal theme
  labs(y = "Percent",
       fill = "Drive Train",
       x = "Class",
       title = "Automobile Drive by Class") +
  theme_minimal()
Segmented Bar Chart

Segmented Bar Chart

Catatan: Anda dapat menggunakan opsi tambahan untuk meningkatkan warna dan pelabelan. Dalam grafik di bawah ini

  • ‘faktor’ memodifikasi urutan kategori untuk variabel kelas dan urutan dan * ‘label’ untuk variabel drive
  • ‘scale_y_continuous’ memodifikasi label tanda centang sumbu y
  • ‘labs’ menyediakan judul dan mengubah label untuk sumbu x dan y dan legenda
  • ‘scale_fill_brewer’ mengubah skema warna isian
  • ‘theme_minimal’ menghapus latar belakang abu-abu dan mengubah warna grid

Fungsi lainnya dibahas lebih lengkap di bagian tentang Visualisasi Data Bab Lanjutan.

2.1.4 Plot Mosaik

Bagan mosaik dapat menampilkan hubungan antara variabel kategoris menggunakan persegi panjang yang areanya mewakili proporsi kasus untuk kombinasi tingkat tertentu. Warna ubin juga dapat menunjukkan hubungan derajat di antara variabel.

Meskipun bagan mosaik dapat dibuat dengan ggplot2 menggunakan ggmosaic paket, saya sarankan menggunakan paket vcd sebagai gantinya. Meskipun itu won’t buat grafik ggplot2, paket ini menyediakan pendekatan yang lebih komprehensif untuk memvisualisasikan data kategoris.

Orang-orang terpesona dengan Titanic (atau dengan Leo?). Dalam Titanic bencana, apa peran seks dan kelas bermain dalam bertahan hidup? Kita dapat memvisualisasikan hubungan antara ketiga variabel kategoris ini menggunakan kode di bawah ini.

# create a table
tbl <- xtabs(Freq ~ Survived +Class + Sex, Titanic)
ftable(tbl)
##                Sex Male Female
## Survived Class                
## No       1st        118      4
##          2nd        154     13
##          3rd        422    106
##          Crew       670      3
## Yes      1st         62    141
##          2nd         25     93
##          3rd         88     90
##          Crew       192     20
# create a mosaic plot from the table
library(vcd)
## Loading required package: grid
mosaic(tbl, main = "Titanic data")
Basic mosaic plot

Basic mosaic plot

Ukuran ubin sebanding dengan persentase kasus dalam kombinasi tingkat tersebut. Jelas lebih banyak penumpang tewas, daripada selamat. Mereka yang tewas terutama penumpang pria kelas 3 dan kru pria (kelompok terbesar).

Jika kita berasumsi bahwa ketiga variabel ini independen, kita dapat memeriksa residu dari model dan menaungi ubin agar sesuai. Dalam grafik di bawah ini, biru tua mewakili lebih banyak kasus daripada yang diharapkan diberikan kemerdekaan. Merah gelap mewakili lebih sedikit kasus dari yang diharapkan jika kemerdekaan memegang.

mosaic(tbl, 
       shade = TRUE,
       legend = TRUE,
       labeling_args = list(set_varnames = c(Sex = "Gender",
                                             Survived = "Survived",
                                             Class = "Passenger Class")),
       set_labels = list(Survived = c("No", "Yes"),
                         Class = c("1st", "2nd", "3rd", "Crew"),
                         Sex = c("F", "M")),
       main = "Titanic data")
Mosaic plot with shading

Mosaic plot with shading

Kita dapat melihat bahwa jika kelas, jenis kelamin, dan kelangsungan hidup mandiri, kita melihat lebih banyak kru pria binasa, dan perempuan kelas 1, 2 dan 3 yang bertahan hidup dari yang diharapkan. Sebaliknya, jauh lebih sedikit penumpang kelas 1 (baik pria maupun wanita) meninggal daripada yang diharapkan secara kebetulan. Dengan demikian asumsi kemerdekaan ditolak. (Spoiler alert: Leo doesn’t make it.)

2.2 Berkelanjutan vs. Kontinu

Hubungan antara dua variabel kuantitatif biasanya ditampilkan menggunakan sebar dan grafik garis.

2.2.1 Scatterplot

Sebar dibuat untuk mempelajari hubungan antara 2 variabel. Dengan demikian sering disertai dengan perhitungan koefisien korelasi, yang biasanya mencoba mengukur hubungan linier. Namun jenis hubungan lain dapat dideteksi menggunakan sebar, dan tugas umum terdiri dari pas dengan model yang menjelaskan Y dalam fungsi X. Berikut adalah beberapa pola yang dapat Anda deteksi melakukan scatterplot.

library(ggplot2)                                   # data visualization
library(hrbrthemes)                                # for the `theme_ipsum()` and legend
# Create data
d1 <- data.frame(x=seq(1,100), 
                 y=rnorm(100), 
                 name="No trend")
d2 <- d1 %>% 
mutate(y=x*10 + rnorm(100,sd=60)) %>%
mutate(name="Linear relationship")
d3 <- d1 %>%
mutate(y=x^2 + rnorm(100,sd=140)) %>%
mutate(name="Square")
d4 <- data.frame( x=seq(1,10,0.1), 
                  y=sin(seq(1,10,0.1)) + 
                    rnorm(91,sd=0.6)) %>% 
mutate(name="Sin")
don <- do.call(rbind, list(d1, d2, d3, d4))
# Plot
don %>%
  ggplot(aes(x=x, y=y)) +
    geom_point(color="#69b3a2", alpha=0.8) +
    theme_ipsum() +
    facet_wrap(~name, scale="free")
relationship scatterplots

relationship scatterplots

Tampilan paling sederhana dari dua variabel kuantitatif adalah sebar, dengan setiap variabel diwakili pada sumbu. Misalnya, menggunakan dataset Gaji, kita dapat merencanakan pengalaman (tyrs.since.phd) vs. gaji akademik (gaji) untuk college Professors.

library(ggplot2)                                     # for visualization
library(scales)                                      # automatically determining breaks/labels 
data(Salaries, package="carData")
# enhanced scatter plot
ggplot(Salaries, 
       aes(x = yrs.since.phd, 
           y = salary)) +
  geom_point(color="cornflowerblue", 
             size = 2, 
             alpha=.8) +
  scale_y_continuous(label = scales::dollar, 
                     limits = c(50000, 250000)) +
  scale_x_continuous(breaks = seq(0, 60, 10), 
                     limits=c(0, 60)) +
  theme_minimal() +                                  # use a minimal theme
  labs(x = "Years Since PhD",
       y = "",
       title = "Experience vs. Salary",
       subtitle = "9-month salary for 2008-2009")
Scatterplot 1

Scatterplot 1

Catatan: opsi geom_point dapat digunakan untuk mengubah

  • warna - warna titik
  • ukuran - ukuran poin
  • bentuk - bentuk titik
  • alpha - transparansi titik. Transparansi berkisar dari 0 (transparan) hingga 1 (buram), dan merupakan parameter yang berguna ketika titik tumpang tindih.

Fungsi scale_x_continuous dan ‘scale_y_continuous’ mengontrol penskalaan pada sumbu x dan y masing-masing. Kita dapat menggunakan opsi dan fungsi ini untuk membuat plot sebar yang lebih menarik.

2.2.2 Garis Pas Sebar

Seringkali berguna untuk meringkas hubungan yang ditampilkan dalam scatterplot, menggunakan garis paling pas. Banyak jenis garis yang didukung, termasuk linear, polinomial, dan nonparametric (loess). Secara default, batas kepercayaan 95% untuk garis-garis ini ditampilkan.

library(ggplot2)                                     # for visualization
ggplot(Salaries,
       aes(x = yrs.since.phd, 
           y = salary)) +
  geom_point(color= "cornflowerblue") +
  geom_smooth(method = "lm", color = "brown1")+
  theme_minimal() +                                  # use a minimal theme
  labs(x = "Years Since PhD",
       y = "",
       title = "Experience vs. Salary",
       subtitle = "9-month salary for 2008-2009")
Scatterplot Linear

Scatterplot Linear

Yang jelas, gaji meningkat dengan pengalaman. Namun, tampaknya ada penurunan di ujung yang tepat - profesor dengan pengalaman yang signifikan, mendapatkan gaji yang lebih rendah. Garis lurus tidak menangkap efek non-linear ini. Sebuah garis dengan tikungan akan lebih cocok di sini.

Garis regresi polinomial menyediakan garis pas dari formulir

\[\begin{equation} \label{eq:1} \hat{y}=\beta_0+\beta_1x+\beta_2x^2+\cdots+\beta_nx^2 \end{equation}\]

Biasanya garis kuadrat (satu tikungan), atau kubik (dua tikungan) digunakan. Jarang perlu menggunakan pesanan yang lebih tinggi \(( >3 )\) polinomial. Menerapkan pas kuadrat ke kumpulan data gaji menghasilkan hasil berikut.

library(ggplot2)                                     # for visualization
ggplot(Salaries, 
       aes(x = yrs.since.phd, 
           y = salary)) +
  geom_point(color= "cornflowerblue") +
  geom_smooth(method = "lm", 
              formula = y ~ poly(x, 2), 
              color = "yellow")+
  theme_minimal() +                                  # use a minimal theme
  labs(x = "Years Since PhD",
       y = "",
       title = "Experience vs. Salary",
       subtitle = "9-month salary for 2008-2009")
Scatterplot Quadratic

Scatterplot Quadratic

Akhirnya, garis fit nonparametric yang dihaluskan sering dapat memberikan gambaran yang baik tentang hubungan. Default dalam ggplot2 adalah loess garis yang merupakan singkatan dari pemhalusan sebabar tertimbang lokal.

library(ggplot2)                                     # for visualization
ggplot(Salaries, 
       aes(x = yrs.since.phd, 
           y = salary)) +
  geom_point(color="cornflowerblue", 
             size = 2, 
             alpha = 1) +
  geom_smooth(size = 1,
              color = "green") +
  scale_y_continuous(label = scales::dollar, 
                     limits = c(50000, 250000)) +
  scale_x_continuous(breaks = seq(0, 60, 10), 
                     limits = c(0, 60)) +
  theme_minimal() +                                  # use a minimal theme
  labs(x = "Years Since PhD",
       y = "",
       title = "Experience vs. Salary",
       subtitle = "9-month salary for 2008-2009") +
  theme_minimal()
Scatterplot Smoothed Nonparametric

Scatterplot Smoothed Nonparametric

2.3 Kategoris vs. Kontinu

Saat merencanakan hubungan antara variabel kategoris dan variabel kuantitatif, sejumlah besar jenis grafik tersedia. Ini termasuk bagan batang menggunakan statistik ringkasan, plot kepadatan kernel yang dikelompokkan, plot kotak berdampingan, plot biola berdampingan, plot rata-rata / sem, plot punggung bukit, dan plot Cleveland.

2.3.1 Bagan Batang

Di bagian sebelumnya, bagan batang digunakan untuk menampilkan jumlah kasus menurut kategori untuk satu variabel atau untuk dua variabel. Anda juga dapat menggunakan bagan batang untuk menampilkan statistik ringkasan lainnya (misalnya, sarana atau median) pada variabel kuantitatif untuk setiap tingkat variabel kategoris.

Misalnya, grafik berikut menampilkan gaji rata-rata untuk sampel profesor universitas berdasarkan peringkat akademik mereka.

library(dplyr)                                       # for data manipulation
library(ggplot2)                                     # for visualization
library(scales)                                      # automatically determining breaks/labels 
data(Salaries, package="carData")
# calculate mean salary for each rank
plotdata <- Salaries %>%
  group_by(rank) %>%
  dplyr::summarize(mean_salary = mean(salary))
# plot mean salaries in a more attractive fashion
mycols <- c("#CD534CFF", "#EFC000FF", "#0073C2FF")
ggplot(plotdata, 
       aes(x = factor(rank,
                      labels = c("Assistant\nProfessor",
                                 "Associate\nProfessor",
                                 "Full\nProfessor")), 
                      y = mean_salary)) +
  geom_bar(stat = "identity", 
           fill = mycols) +
  geom_text(aes(label = dollar(mean_salary)), 
            vjust = -0.25) +
  scale_y_continuous(breaks = seq(0, 130000, 20000), 
                     label = dollar) +
  theme_minimal() +                                  # use a minimal theme
  labs(title = "Mean Salary by Rank", 
       subtitle = "9-month academic salary for 2008-2009",
       x = "",
       y = "")
Bar Chart (Summary statistics)

Bar Chart (Summary statistics)

2.3.2 Plot Kepadatan Kernel Yang Dikelompokkan

Seseorang dapat membandingkan grup pada variabel numerik dengan mengganti plot kepadatan kernel dalam satu grafik. Mari kita plot distribusi gaji berdasarkan peringkat menggunakan plot kepadatan kernel.

ggplot(Salaries, 
       aes(x = salary, 
           fill = rank)) +
  geom_density(alpha = 0.4) +
  theme_minimal() +
  labs(title = "Salary distribution by rank")
Grouped Kernel Density Plots

Grouped Kernel Density Plots

Opsi alfa membuat plot kepadatan sebagian transparan sehingga kita dapat melihat apa yang terjadi di bawah tumpang tindih. Nilai alfa berkisar dari 0 (transparan) hingga 1 (buram). Grafik menjelaskan bahwa, secara umum, gaji naik dengan pangkat. Namun, kisaran gaji untuk profesor penuh sangat luas.

2.3.3 Plot Kotak

Plot kotak menampilkan persentil $ 25 ^{th}$ , median, dan persentil \(75^{th}\) dari sebuah distribusi. Kumis (garis vertikal) menangkap sekitar 99% dari distribusi normal, dan pengamatan di luar rentang ini diplot sebagai titik yang mewakili outlier (lihat gambar di bawah)

Box Plots

Box Plots

Plot kotak berdampingan sangat berguna untuk membandingkan grup (yaitu, tingkat variabel kategoris) pada variabel numerik. Mari kita plot distribusi gaji berdasarkan peringkat menggunakan box-plot. Plot kotak berang-berang memberikan metode perkiraan untuk memvisualisasikan apakah grup berbeda. Meskipun bukan tes formal, jika takik dua plot kotak tidak tumpang tindih, ada bukti kuat (kepercayaan 95%) bahwa median dari dua kelompok berbeda.

mycols <- c("#CD534CFF", "#EFC000FF", "#0073C2FF")
ggplot(Salaries, aes(x = rank, 
                     y = salary)) +
  geom_boxplot(notch = TRUE, 
               fill = mycols, 
               alpha = .7) +
  theme_minimal() +
  labs(title = "Salary Distribution by rank")
Box Plots

Box Plots

Dalam contoh di atas, ketiga grup tampak berbeda. Salah satu keunggulan boxplot adalah lebarnya biasanya tidak berarti. Ini memungkinkan Anda untuk membandingkan distribusi banyak grup dalam satu grafik.

2.3.4 Plot Biola

Plot biola mirip dengan plot kepadatan kernel tetapi dicerminkan dan diputar $ 90 ^ 0 $. Mari kita plot distribusi gaji berdasarkan peringkat menggunakan plot biola.

ggplot(Salaries, 
       aes(x = rank, 
           y = salary)) +
  geom_violin(fill = "azure1") +
  geom_boxplot(width = .2, 
               fill = mycols,
               outlier.color = "red",
               outlier.size = 2) + 
  theme_minimal() +
  labs(title = "Salary distribution by rank")
Violin Plots

Violin Plots

2.3.5 Plot Ridgeline

Plot punggung bukit (juga disebut plot sukacita) menampilkan distribusi variabel kuantitatif untuk beberapa kelompok. Mereka’re mirip dengan plot kepadatan kernel dengan vertikal faceting, tetapi mengambil lebih sedikit ruang. Plot Ridgeline dibuat dengan paket ggridges.

MenggunakanFuel economy dataset, Mari kita plot distribusi kota mengemudi mil per galon oleh kelas mobil.

library(dplyr)                                       # for data manipulation
library(ggplot2)                                     # for visulization
library(ggridges)                                    # to handle overlapping visulization
ggplot(mpg, 
       aes(x = cty, 
           y = class, 
           fill = class)) +
  geom_density_ridges(alpha = 0.7) + 
  theme_ridges() +
  labs("Highway mileage by auto class") +
  theme(legend.position = "none")
Ridgeline Plots

Ridgeline Plots

Saya telah menekan legenda di sini karena berlebihan (distribusi sudah dilabeli pada sumbu y). Tidak mengherankan, truk pickup memiliki jarak tempuh termiskin, sementara subkomputer dan mobil kompak cenderung mencapai peringkat. Namun, ada berbagai skor jarak tempuh gas untuk mobil-mobil kecil ini.

Perhatikan kemungkinan tumpang tindih distribusi adalah trade-off untuk grafik yang lebih ringkas. Anda dapat menambahkan transparansi jika tumpang tindih parah menggunakan geom_density_ridges(alfa = n), di mana n berkisar antara 0 (transparan) hingga 1 (buram). Lihat package vingnette untuk details.

2.3.6 Plot Baris

Metode populer untuk membandingkan grup pada variabel numerik adalah plot rata-rata dengan bilah kesalahan. Bilah kesalahan dapat mewakili simpangan baku, kesalahan standar rata-rata, atau interval kepercayaan diri. Di bagian ini, sarana plot yang baik dan kesalahan standar. Kita dapat menggunakan teknik yang sama untuk membandingkan gaji di seluruh pangkat dan jenis kelamin. (Secara teknis, ini tidak bivariate sejak itu merencanakan pangkat, jenis kelamin, dan gaji, tetapi tampaknya cocok di sini).

library(dplyr)                                       # for data manipulation
library(ggplot2)                                     # for visulization
library(ggridges)                                    # to handle overlapping visulization
# calculate means, standard deviations,
# standard errors, and 95% confidence 
# intervals by rank
plotdata <- Salaries %>%
  group_by(rank, sex) %>%
  dplyr::summarize(n = n(),
            mean = mean(salary),
            sd = sd(salary),
            se = sd/sqrt(n),
            ci = qt(0.975, df = n - 1) * sd / sqrt(n))
# improved means/standard error plot
pd <- position_dodge(0.2)
ggplot(plotdata, 
       aes(x = factor(rank, 
                      labels = c("Assistant\nProfessor",
                                 "Associate\nProfessor",
                                 "Full\nProfessor")), 
           y = mean, 
           group=sex, 
           color=sex)) +
  geom_point(position=pd, 
             size = 3) +
  geom_line(position = pd, 
            size = 1) +
  geom_errorbar(aes(ymin = mean - se, 
                    ymax = mean + se), 
                width = .1, 
                position = pd, 
                size = 1) +
  scale_y_continuous(label = scales::dollar) +
  scale_color_brewer(palette="Set1") +
  theme_minimal() +
  labs(title = "Mean salary by rank and sex",
       subtitle = "(mean +/- standard error)",
       x = "", 
       y = "",
       color = "Gender")
Line Plots

Line Plots

2.3.7 Plot Strip

Hubungan antara variabel pengelompokan dan variabel numerik dapat ditampilkan dengan plot sebar. Misalnya, plot pembagian gaji berdasarkan peringkat menggunakan plot strip. Plot sebar satu dimensi ini disebut plot strip. Sayangnya, kelebihan cetak poin membuat interpretasi menjadi sulit. Hubungan lebih mudah untuk melihat apakah poinnya gelisah. Pada dasarnya, angka acak kecil ditambahkan ke setiap koordinat y. Juga lebih mudah untuk membandingkan kelompok jika kita menggunakan warna.

library(ggplot2)                                     # for visulization
library(scales)                                      # scaling infrastructure
ggplot(Salaries, 
       aes(y = factor(rank,
                      labels = c("Assistant\nProfessor",
                                 "Associate\nProfessor",
                                 "Full\nProfessor")), 
           x = salary, 
           color = rank)) +
  geom_jitter(alpha = 0.7,
              size = 1.5) + 
  scale_x_continuous(label = dollar) +
  labs(title = "Academic Salary by Rank", 
       subtitle = "9-month salary for 2008-2009",
       x = "",
       y = "") +
  theme_minimal() +
  theme(legend.position = "none")
Strip Plots

Strip Plots

Opsi legend.position = "none" digunakan untuk menekan legenda (yang tidak diperlukan di sini). Plot jittered bekerja dengan baik ketika jumlah poin tidak terlalu besar.

2.3.8 Jitter dan Boxplots

Mungkin lebih mudah untuk memvisualisasikan distribusi jika kita menambahkan boxplot ke plot jitter. Beberapa opsi ditambahkan untuk membuat plot ini.

  • Untuk boxplot:
    • ukuran = 1 membuat garis lebih tebal
    • outlier.color = "hitam" membuat outlier hitam
    • outlier.shape = 1 menentukan lingkaran untuk outlier
    • outlier.size = 3 meningkatkan ukuran outlier
  • Untuk kegelisahan:
    • alpha = 0,5 membuat poin lebih transparan
    • width = .2 mengurangi jumlah jitter (.4 adalah default)

Akhirnya, $x $ dan $y $ $ dihormati menggunakan fungsi ‘coord_flip’ (yaitu, grafik dihidupkan di sisinya).

library(ggplot2)                                     # for visulization
library(scales)                                      # scaling infrastructure
ggplot(Salaries, 
       aes(x = factor(rank,
                      labels = c("Assistant\nProfessor",
                                 "Associate\nProfessor",
                                 "Full\nProfessor")), 
           y = salary, 
           color = rank)) +
  geom_boxplot(size=1,
               outlier.shape = 1,
               outlier.color = "black",
               outlier.size  = 3) +
  geom_jitter(alpha = 0.5, 
              width=.2) + 
  scale_y_continuous(label = dollar) +
  labs(title = "Academic Salary by Rank", 
       subtitle = "9-month salary for 2008-2009",
       x = "",
       y = "") +
  theme_minimal() +
  theme(legend.position = "none") +
  coord_flip()
Combining Jitter and Boxplots 1

Combining Jitter and Boxplots 1

Sebelum melanjutkan, ada baiknya menyebutkan fungsi ‘geom_boxjitter’ yang disediakan dalam paket ‘ggpol’. Ini menciptakan boxplot hibrida - setengah boxplot, setengah scatterplot.

library(ggplot2)                                     # for visulization
library(scales)                                      # scaling infrastructure
library(ggpol)                                       # hybrid boxplot -half scatterplot
ggplot(Salaries, 
       aes(x = factor(rank,
                      labels = c("Assistant\nProfessor",
                                 "Associate\nProfessor",
                                 "Full\nProfessor")), 
           y = salary, 
           fill=rank)) +
  geom_boxjitter(color="black",
                 jitter.color = "darkgrey",
                 errorbar.draw = TRUE) +
  scale_y_continuous(label = dollar) +
  labs(title = "Academic Salary by Rank", 
       subtitle = "9-month salary for 2008-2009",
       x = "",
       y = "") +
  theme_minimal() +
  theme(legend.position = "none")
Combining Jitter and Boxplots 2

Combining Jitter and Boxplots 2

2.3.9 Plot Beeswarm

Plot beeswarm (juga disebut plot sebaran biola) mirip dengan sebaran yang gelisah, karena mereka menampilkan distribusi variabel kuantitatif dengan merencanakan titik dengan cara yang mengurangi tumpang tindih. Selain itu, mereka juga membantu menampilkan kepadatan data di setiap titik (dengan cara yang mirip dengan plot biola). Melanjutkan contoh sebelumnya

library(ggplot2)                                     # for visulization
library(scales)                                      # scaling infrastructure
library(ggbeeswarm)                                  # reduces overlap
ggplot(Salaries, 
       aes(x = factor(rank,
                      labels = c("Assistant\nProfessor",
                                 "Associate\nProfessor",
                                 "Full\nProfessor")), 
           y = salary, 
           color = rank)) +
  geom_quasirandom(alpha = 0.7,
                   size = 1.5) + 
  scale_y_continuous(label = dollar) +
  labs(title = "Academic Salary by Rank", 
       subtitle = "9-month salary for 2008-2009",
       x = "",
       y = "") +
  theme_minimal() +
  theme(legend.position = "none")
Beeswarm Plots

Beeswarm Plots

Plot dibuat menggunakan fungsi geom_quasirandom sama. Plot ini dapat lebih mudah dibaca daripada plot strip sederhana yang gelisah. Untuk mempelajari lebih lanjut tentang plot ini, lihat plot gaya Beeswarm dengan ggplot2.

2.3.10 Bagan Titik Cleveland

Plot Cleveland berguna ketika Anda ingin membandingkan statistik numerik untuk sejumlah besar kelompok. Misalnya, Anda ingin membandingkan harapan hidup 2007 untuk negara Asia menggunakan kumpulan data gapminder.

library(dplyr)                                       # for data manipulation
library(ggplot2)                                     # for visulization
library(scales)                                      # scaling infrastructure
library(ggbeeswarm)                                  # reduces overlap
library(gapminder)                                   # for dataset `gapminder` 
data(gapminder, package="gapminder")                 # load dataset `gapminder` 
# subset Asian countries in 2007
library(dplyr)
plotdata <- gapminder %>%
  filter(continent == "Asia" & 
         year == 2007)
# Fancy Cleveland plot
ggplot(plotdata, 
       aes(x=lifeExp, 
           y=reorder(country, lifeExp))) +
  geom_point(color="blue", 
             size = 2) +
  geom_segment(aes(x = 40, 
               xend = lifeExp, 
               y = reorder(country, lifeExp), 
               yend = reorder(country, lifeExp)),
               color = "azure3") +
  labs (x = "Life Expectancy (years)",
        y = "",
        title = "Life Expectancy by Country",
        subtitle = "GapMinder data for Asia - 2007") +
  theme_minimal() + 
  theme(panel.grid.major = element_blank(),
        panel.grid.minor = element_blank())
Beeswarm Plots

Beeswarm Plots

Jepang jelas memiliki harapan hidup tertinggi, sementara Afghanistan memiliki yang terendah sejauh ini. Plot terakhir ini juga disebut grafik lolipop.

3 Data Multivariat

Grafik multivariat menampilkan hubungan di antara tiga variabel atau lebih. Ada dua metode umum untuk mengakomodasi beberapa variabel: pengelompokan dan faceting.

3.1 Pengelompokan

Dalam pengelompokan, nilai dari dua variabel pertama dipetakan ke sumbu x dan y. Kemudian variabel tambahan dipetakan ke karakteristik visual lainnya seperti warna, bentuk, ukuran, tipe garis, dan transparansi. Pengelompokan memungkinkan Anda merencanakan data untuk beberapa grup dalam satu grafik. Dengan menggunakan kumpulan data Gaji, mari kita tampilkan hubungan antara yrs.since.phd dan gaji.

library(carData)                                     # for dataset
library(ggplot2)                                     # for visulization
data(Salaries, package="carData")
ggplot(Salaries, aes(x = yrs.since.phd, 
                     y = salary, 
                     color=rank)) +
  geom_point() +
  theme_minimal() +
  labs(title = "Academic salary by rank and years since degree")
Multivariate Grouping Plot 1

Multivariate Grouping Plot 1

Selanjutnya, mari kita tambahkan jenis kelamin profesor, menggunakan bentuk poin untuk menunjukkan seks. Tingkatkan ukuran poin dengan baik dan tambahkan transparansi untuk membuat poin individu lebih jelas.

library(carData)                                     # for dataset
library(ggplot2)                                     # for visulization
ggplot(Salaries, 
       aes(x = yrs.since.phd, 
           y = salary, 
           color = rank, 
           shape = sex)) +
  geom_point(size = 3, alpha = .6) +
  theme_minimal() +
  labs(title = "Academic salary by rank, sex, and years since degree")
Multivariate Grouping Plot 2

Multivariate Grouping Plot 2

Kita tidak bisa mengatakan bahwa ini adalah grafis yang hebat. Ini sangat sibuk, dan mungkin sulit untuk membedakan laki-laki dari profesor perempuan. Faceting (dijelaskan di bagian berikutnya) mungkin akan menjadi pendekatan yang lebih baik.

Perhatikan perbedaan antara menentukan nilai konstanta (seperti ukuran = 3) dan pemetaan variabel ke karakteristik visual (misalnya, warna = peringkat). Pemetaan selalu ditempatkan dalam fungsi lebah, sementara penetapan nilai konstanta selalu muncul di luar fungsi lebah.

Berikut adalah contoh yang lebih bersih. Nah, grafik hubungan antara tahun sejak Ph.D. dan gaji menggunakan ukuran poin untuk menunjukkan tahun layanan. Ini disebut plot gelembung.

library(carData)                                     # for dataset
library(ggplot2)                                     # for visulization
ggplot(Salaries, 
       aes(x = yrs.since.phd, 
           y = salary, 
           color = rank, 
           size = yrs.service)) +
  geom_point(alpha = .8) +
  theme_minimal() +
  labs(title = "Academic salary by rank, years of service, and years since degree")
Multivariate Grouping Plot 3

Multivariate Grouping Plot 3

Jelas ada hubungan positif yang kuat antara tahun sejak Ph.D. dan tahun pelayanan. Asisten Profesor jatuh dalam 0-11 tahun sejak Ph.D. dan 0-10 tahun jangkauan layanan. Jelas, profesional yang sangat berpengalaman tidak tinggal di tingkat Asisten Profesor (mereka mungkin dipromosikan atau meninggalkan Universitas). Kami tidak menemukan demarkasi waktu yang sama antara Associate dan Full Professors. [Gelembung plot] () dijelaskan secara lebih rinci di bab selanjutnya.

Sebagai contoh akhir, mari kita lihat yrs.since.phd vs gaji dan tambahkan seks menggunakan warna dan garis paling cocok kuadrat.

library(carData)                                     # for dataset
library(ggplot2)                                     # for visulization
ggplot(Salaries, 
       aes(x = yrs.since.phd, 
           y = salary, 
           color = sex)) +
  geom_point(alpha = .4, 
             size = 3) +
  geom_smooth(se=FALSE, 
              method = "lm", 
              formula = y~poly(x,2), 
              size = 1.5) +
  labs(x = "Years Since Ph.D.",
       title = "Academic Salary by Sex and Years Experience",
       subtitle = "9-month salary for 2008-2009",
       y = "",
       color = "Sex") +
  scale_y_continuous(label = scales::dollar) +
  scale_color_brewer(palette = "Set1") +
  theme_minimal()
Multivariate Grouping Plot 4

Multivariate Grouping Plot 4

3.2 Faceting

3.2.1 Menghadapi 1

Pengelompokan memungkinkan Anda merencanakan beberapa variabel dalam satu grafik, menggunakan karakteristik visual seperti warna, bentuk, dan ukuran. Dalam faceting, grafik terdiri dari beberapa plot terpisah atau kelipatan kecil, satu untuk setiap tingkat variabel ketiga, atau kombinasi variabel. Paling mudah untuk memahami ini dengan contoh.

library(carData)                                     # for dataset
library(ggplot2)                                     # for visulization
ggplot(Salaries, aes(x = salary)) +
  geom_histogram(fill = "cornflowerblue",
                 color = "white") +
  facet_wrap(~rank, ncol = 1) +
  theme_minimal() +
  labs(title = "Salary histograms by rank")
Multivariate Faceting 1

Multivariate Faceting 1

Fungsi facet_wrap membuat grafik terpisah untuk setiap tingkat peringkat. Opsi ‘ncol’ mengontrol jumlah kolom. Dalam contoh berikutnya, dua variabel digunakan untuk menentukan aspek.

3.2.2 Menghadapi 2

Di sini, fungsi menetapkan seks ke baris dan peringkat ke kolom, membuat matriks 6 plot dalam satu grafik.

library(carData)                                     # for dataset
library(ggplot2)                                     # for visulization
ggplot(Salaries, aes(x = salary / 1000)) +
  geom_histogram(color = "white",
                 fill = "cornflowerblue") +
  facet_grid(sex ~ rank) +
  theme_minimal() +
  labs(title = "Salary histograms by sex and rank",
       x = "Salary ($1000)")
Multivariate Faceting 2

Multivariate Faceting 2

3.2.3 Menghadapi 3

Kita juga dapat menggabungkan pengelompokan dan faceting. Mari kita gunakan plot Mean /SE dan faceting untuk membandingkan gaji profesor pria dan wanita, dalam pangkat dan disiplin. Nah, gunakan warna untuk membedakan seks dan faceting untuk membuat plot untuk peringkat berdasarkan kombinasi disiplin.

library(carData)                                     # for dataset
library(ggplot2)                                     # for visulization
library(dplyr)                                       # data manipulation
# calculate means and standard erroes by sex,
# rank and discipline
plotdata <- Salaries %>%
  group_by(sex, rank, discipline) %>%
  dplyr::summarize(n = n(),
            mean = mean(salary),
            sd = sd(salary),
            se = sd / sqrt(n))
# create better labels for discipline
plotdata$discipline <- factor(plotdata$discipline,
                              labels = c("Theoretical",
                                         "Applied"))
# create plot
ggplot(plotdata, 
       aes(x = sex, 
           y = mean,
           color = sex)) +
  geom_point(size = 3) +
  geom_errorbar(aes(ymin = mean - se, 
                    ymax = mean + se),
                width = .1) +
  scale_y_continuous(breaks = seq(70000, 140000, 10000),
                     label = scales::dollar) +
  facet_grid(. ~ rank + discipline) +
  theme_bw() +
  theme(legend.position = "none",
        panel.grid.major.x = element_blank(),
        panel.grid.minor.y = element_blank()) +
  labs(x="", 
       y="", 
       title="Nine month academic salaries by gender, discipline, and rank",
       subtitle = "(Means and standard errors)") +
  scale_color_brewer(palette="Set1")
Multivariate Faceting 3

Multivariate Faceting 3

Pernyataan facet_grid(. ~ rank + discipline) tidak menentukan variabel baris (.) dan kolom yang ditentukan oleh kombinasi peringkat dan disiplin.

Fungsi theme() membuat tema hitam putih dan menghilangkan garis kisi vertikal dan garis kisi horizontal kecil. Fungsi scale_color_brewer() mengubah skema warna untuk titik dan bilah kesalahan.

Pada pandangan pertama, tampaknya mungkin ada perbedaan gender dalam gaji untuk rekan dan profesor penuh di bidang teoritis. Saya mengatakan

LS0tDQp0aXRsZTogIkRhdGEgU3RydWN0dXJlcyBhbmQgQWxnb3JpdGhtcyINCnN1YnRpdGxlOiAiTGFiMTMtVmlzdWFsaXphdGlvbiINCmF1dGhvcjogImJ5IE5pa2l0YSBJbmRyaXlhbmkiDQpkYXRlOiAiYHIgZm9ybWF0KFN5cy5EYXRlKCksICclQiAlZCwgJVknKWAiDQpvdXRwdXQ6IA0KICBodG1sX2RvY3VtZW50Og0KICAgIGRmX3ByaW50OiBwYWdlZA0KICAgIGNvZGVfZm9sZGluZzogaGlkZQ0KICAgIGNvZGVfZG93bmxvYWQ6IHllcw0KICAgIHRvYzogdHJ1ZQ0KICAgIHRvY19kZXB0aDogMw0KICAgIHRvY19mbG9hdDogDQogICAgICBjb2xsYXBzZWQ6IHRydWUNCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUNCiAgICB0aGVtZTogc2FuZHN0b25lDQogICAgY3NzOiBzdHlsZS5jc3MgICAgIyBkb24ndCBmb3JnZXQgdG8gZHdvbmxvYWQgdGhpcyBmaWxlIGFuZCBzZXQgdG8geW91ciB3b3JraW5nIGRpcmVjdG9yeSBhY2NvcmRpbmdseQ0KICAgIGhpZ2hsaWdodDogbW9ub2Nocm9tZQ0KLS0tDQogIA0KYGBge3IgTG9nbywgZWNobz1GQUxTRSxmaWcuYWxpZ249J2NlbnRlcicsIG91dC53aWR0aCA9ICc0MCUnfQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoImh0dHBzOi8vZ2l0aHViLmNvbS9CYWt0aS1TaXJlZ2FyL2ltYWdlcy9ibG9iL21hc3Rlci9sb2dvLnBuZz9yYXc9dHJ1ZSIpDQpgYGANCg0KRW1haWw6IG5pa2l0YWluZHJpeW5pQGdtYWlsLmNvbSAgPGJyPg0KUlB1YnM6IGh0dHBzOi8vcnB1YnMuY29tL2RhdGF6ZXJvdG9oZXJvLyA8YnI+DQpHaXRodWI6IGh0dHBzOi8vZ2l0aHViLmNvbS9kYXRhemVyb3RvaGVyby8NCg0KKioqIA0KDQoqKlZpc3VhbGlzYXNpIGRhdGEqKiBhZGFsYWggdGVrbmlzaSBtZW5nYW1iaWwgaW5mb3JtYXNpIGRhcmkgZGF0YSBrZSBkYWxhbSBrb250ZWtzIHZpc3VhbCwgc2VwZXJ0aSBiYWdhbiwgZ3JhZmlrLCBkYW4gcGV0YS4gVmlzdWFsaXNhc2kgZGF0YSBkaW11bmdraW5rYW4gdW50dWsgbWVuYW5nYW5pIGRhdGEgYmVzYXIgeWFuZyBrZWNpbCBkYW4gcGVyaXN0aXdhIG1lbmphZGkgbGViaWggbXVkYWggZGlwYWhhbWkgb2xlaCBvdGFrIG1hbnVzaWEsIGRhbiB2aXN1YWxpc2FzaSBqdWdhIG1lbWJ1YXRueWEgbGViaWggZGFwYXQgZGlhbmRhbGthbiB1bnR1ayBtZW5kZXRla3NpIHBvbGEsIHRyZW4sIGRhbiBvdXRsaWVyIGRhbGFtIGtlbG9tcG9rIGRhdGEuIA0KDQpSIGFkYWxhaCBwbGF0Zm9ybSBsdWFyIGJpYXNhIHVudHVrIGFuYWxpc2lzIGRhdGEsIHlhbmcgbWFtcHUgbWVuY2lwdGFrYW4gaGFtcGlyIHNlbXVhIGplbmlzIGdyYWZpay4gQnVrdSBpbmkgbWVtYmFudHUgQW5kYSBtZW1idWF0IHZpc3VhbGlzYXNpIHBhbGluZyBwb3B1bGVyIC0gZGFyaSBwbG90IGNlcGF0IGRhbiBrb3RvciBoaW5nZ2EgZ3JhZmlrIHNpYXAgcHVibGlrYXNpLiBEaSBzaW5pIGtpdGEgYWthbiBtZW1wZWxhamFyaSBjYXJhIG1lbXZpc3VhbGlzYXNpa2FuIGRhdGEgZGFyaSB1bml2YXJpYXQsIGJpdmFyaWF0LCBkYW4gbXVsdGl2YXJpYXQuIERhcmkgeWFuZyBzYW5nYXQgbWVuZGFzYXIgaGluZ2dhIHVhbmcgbXVrYSwgc2lsYWthbiBpa3V0aSBpbnN0cnVrc2kgYmVyaWt1dCBzZWxhbmdrYWggZGVtaSBzZWxhbmdrYWguLCBhbmQgb3V0bGllcnMgaW4gZ3JvdXBzIG9mIHRoZSBkYXRhLiANCg0KIyBEYXRhIFVuaXZhcmlhdGUNCg0KUGxvdCB1bml2YXJpYXQgYmlhc2FueWEgZGlndW5ha2FuIHVudHVrIG1lbGFrdWthbiBkaXN0cmlidXNpIGRhdGEgZGFyaSBzYXR1IHZhcmlhYmVsLiBWYXJpYWJlbCBkYXBhdCBkaWthdGVnb3Jpa2FuICoobWlzYWxueWEsIGplbmlzIGtlbGFtaW4sIHJhcywgbmVnYXJhLCBrb3RhLCBkbGwpKiBhdGF1IGt1YW50aXRhdGlmICoobWlzYWxueWEsIHVzaWEsIGJlcmF0LCBpbmZsYXNpLCB0aW5na2F0LCBkbGwpKi4NCg0KIyMgS2F0ZWdvcmlzIA0KDQpEaXN0cmlidXNpIHZhcmlhYmVsIGthdGVnb3JpcyB0dW5nZ2FsIGJpYXNhbnlhIGRpcGxvdCBkZW5nYW4gKmJhZ2FuIGJhdGFuZyosICpwaWUtY2hhcnQqLCBhdGF1IChrdXJhbmcgdW11bSkgc2VidWFoICp0cmVlbWFwKi4NCg0KIyMjIEJhZ2FuIEJhdGFuZw0KDQoNCiBbYE1hcnJpYWdlYF0oaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL0Jha3RpLVNpcmVnYXIvZGF0YXNldC9tYXN0ZXIvQm9va2Rvd24tRGF0YS1TY2llbmNlLWZvci1CZWdpbm5lcnMvTWFycmlhZ2UuY3N2KSBkYXRhc2V0LCBzYXlhIGFtYmlsIGRhcmkgcGFja2FnZSBbYG1vc2FpY0RhdGFgXShodHRwczovL3d3dy5yZG9jdW1lbnRhdGlvbi5vcmcvcGFja2FnZXMvbW9zYWljL3ZlcnNpb25zLzEuNy4wKS4gS2FtaSBtZW5nZ3VuYWthbiBiYWdhbiBiYXRhbmcgdW50dWsgbWVuYW1waWxrYW4gZGlzdHJpYnVzaSBwZXNlcnRhIHBlcm5pa2FoYW4gb2xlaCBgWm9kaWFrYC4gDQoNCmBgYHtyIFZpc3VhbGl6YXRpb24xLCBlY2hvPVQsZmlnLmFsaWduPSdjZW50ZXInLGZpZy5jYXA9IkNhdGVnb3JpY2FsIEJhciBDaGFydCAxIiwgb3V0LndpZHRoID0gJzEwMCUnfQ0KbGlicmFyeShnZ3Bsb3QyKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHVudHVrIHZpc3VhbGlzYXNpDQojc2V0d2QoIkM6L1VzZXJzL0Jha3RpL0Rlc2t0b3AvIikgICAgICAgICAgICAgICAgICAgICMgamFuZ2FuIGx1cGEgdW50dWsgbWVuZ2F0dXIgZGlyZWt0b3JpIGtlcmphDQpNYXJyaWFnZTwtIHJlYWQuY3N2KCJDOi9OaWtpdGEvVHVnYXMvRGF0YSBTdHJ1Y3R1dGVzIGFuZCBBbGdvcml0aG1zL1dFRUsgMTMvTWFycmlhZ2UuY3N2IikgICAgICAgICAgICAgIyBtZW11YXQgZGF0YSBkYXJpIFBDIEFuZGEnDQpnZ3Bsb3QoTWFycmlhZ2UsIGFlcyh4ID0gem9kaWFjcykpICsgICAgICAgICAgICAgICAgICMgcGxvdCBkaXN0cmlidXNpICdab2RpYWsnIA0KICBnZW9tX2JhcihmaWxsID0gImNvcm5mbG93ZXJibHVlIixjb2xvcj0gImJsYWNrIikrICAjIEFuZGEgZGFwYXQgbWVuZ3ViYWggd2FybmEgDQogIHRoZW1lX21pbmltYWwoKSArICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgZ3VuYWthbiB0ZW1hIG1pbmltYWwNCiAgbGFicyh4ID0gIlpvZGlhY3MiLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBBbmRhIGRhcGF0IG1lbW9kaWZpa2FzaSBsYWJlbCBkYW4gcGxvdCBqdWR1bA0KICAgICAgIHkgPSAiRnJlcXVlbmN5IiwgDQogICAgICAgdGl0bGUgPSAiTWFycmlhZ2UgUGFydGljaXBhbnRzIGJ5IFpvZGlhY3MiKSAgICAgICAgIA0KYGBgDQpCYXIgZGFwYXQgbWV3YWtpbGkgcGVyc2VuIGRhcmlwYWRhIGhpdHVuZ2FuLiBVbnR1ayBiYWdhbiBiYXRhbmcgKHpvZGlhayksIGtvZGUgYGFlcyh4PXNpZ24pYCBzZWJlbmFybnlhIGFkYWxhaCBwaW50YXNhbiB1bnR1ayBgYWVzKHggPSBzaWduLCB5ID0gLi4gY291bnQuLilgICwgZGkgbWFuYSAuLiBNZW5naGl0dW5nLi4gYWRhbGFoIHZhcmlhYmVsIGtodXN1cyB5YW5nIG1ld2FraWxpIGZyZWt1ZW5zaSBkYWxhbSBzZXRpYXAga2F0ZWdvcmkuIEFuZGEgZGFwYXQgbWVuZ2d1bmFrYW4gaW5pIHVudHVrIG1lbmdoaXR1bmcgcGVyc2VudGFzZSwgZGVuZ2FuIG1lbmVudHVrYW4gdmFyaWFiZWwgeSBzZWNhcmEgZWtzcGxpc2l0Lg0KDQpEYWxhbSBSLCB3YXJuYSBkYXBhdCBkaXRlbnR1a2FuIGJhaWsgZGVuZ2FuIG5hbWEgKGUuZyBgY29sID0g4oCccmVk4oCdYCkgc2VwZXJ0aSB5YW5nIEFuZGEgbGloYXQgcGFkYSBnYW1iYXIgYmVyaWt1dC4NCg0KYGBge3IgcmNvbG9yLCBlY2hvPUZBTFNFLGZpZy5hbGlnbj0nY2VudGVyJywgb3V0LndpZHRoID0gJzEwMCUnfQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoIkM6L05pa2l0YS9UdWdhcy9EYXRhIFN0cnVjdHV0ZXMgYW5kIEFsZ29yaXRobXMvV0VFSyAxMy9yY29sb3JzLnBuZyIpDQpgYGANCg0KYXRhdSBBbmRhIGRhcGF0IG1lbmV0YXBrYW4gd2FybmEgZGVuZ2FuIG1lbmdndW5ha2FuIHRyaXBsZXQgUkdCIGhla3NhZGVzaW1hbCAoc3VjaCBhcyBgY29sID0g4oCcI0ZGQ0MwMOKAnWApIFtNb3JlXShodHRwczovL2h0bWxjb2xvcmNvZGVzLmNvbS8pLiBTZWxhaW4gaXR1LCBBbmRhIGp1Z2EgZGFwYXQgbWVuZ2d1bmFrYW4gc2lzdGVtIHdhcm5hIGxhaW4gc2VwZXJ0aSBzaXN0ZW0gd2FybmEgeWFuZyBkaWFtYmlsIGRhcmkgYFJDb2xvckJyZXdlcmAgcGFja2FnZSBbTW9yZV0oaHR0cHM6Ly93d3cuZGF0YW5vdmlhLmNvbS9lbi9ibG9nL3RoZS1hLXotb2YtcmNvbG9yYnJld2VyLXBhbGV0dGUvIzp+OnRleHQ9UkNvbG9yQnJld2VyJTIwaXMlMjBhbiUyMFIlMjBwYWNrYWdlLGFuZCUyMGluJTIwUiUyMGJhc2UlMjBwbG90cy4pIGRhbiBwYWNrYWdlIGBnckRldmljZXNgIChBbmRhIG11bmdraW4gc3VkYWggbWVtaWxpa2kgZGltdWF0IGluaSkgYmVyaXNpIHNlanVtbGFoIHBhbGV0LCBrZXRpayBpbmkgYD9yYWluYm93YCBkaSBSY29uc29sZSBBbmRhLiBNYXJpIGtpdGEgcGVydGltYmFuZ2thbiBncmFmaWsgYmVyaWt1dDoNCg0KYGBge3IgVmlzdWFsaXphdGlvbjIsIGVjaG89VCxmaWcuYWxpZ249J2NlbnRlcicsZmlnLmNhcD0iIENhdGVnb3JpY2FsIEJhciBDaGFydCAyIiwgb3V0LndpZHRoID0gJzEwMCUnfQ0KbGlicmFyeShnZ3Bsb3QyKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHVudHVrIHZpc3VhbGlzYXNpDQpnZ3Bsb3QoTWFycmlhZ2UsIA0KICAgYWVzKHggPSB6b2RpYWNzLCANCiAgICAgICB5ID0gLi5jb3VudC4uIC8gc3VtKC4uY291bnQuLikpKSArIA0KICBnZW9tX2JhcihmaWxsID0gcmFpbmJvdygxMiksIGNvbG9yPSAiYXp1cmU0IikgKw0KICB0aGVtZV9taW5pbWFsKCkgKyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGd1bmFrYW4gbWluaW1hbCB0ZW1hDQogIGxhYnMoeCA9ICJab2RpYWNzIiwgDQogICAgICAgeSA9ICJQZXJjZW50IiwgDQogIHRpdGxlICA9ICJNYXJyaWFnZSBQYXJ0aWNpcGFudHMgaW4gUGVyY2VudCIpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6cGVyY2VudCkgICAgICAgIyB0YW1iYWggc2ltYm9sICUga2UgbGFiZWwgc3VtYnUgeQ0KYGBgDQoNClNlcmluZ2thbGkgbWVtYmFudHUgdW50dWsgbWVuZ3VydXRrYW4gYmFyIGJlcmRhc2Fya2FuIGZyZWt1ZW5zaS4gRGFsYW0ga29kZSBkaSBiYXdhaCBpbmksIGZyZWt1ZW5zaSBkaWhpdHVuZyBzZWNhcmEgZWtzcGxpc2l0LiBLZW11ZGlhbiBmdW5nc2kgYHJlb3JkZXJgIGRpZ3VuYWthbiB1bnR1ayBtZW5ndXJ1dGthbiBrYXRlZ29yaSBiZXJkYXNhcmthbiBmcmVrdWVuc2kuIE9wc2kgYHN0YXQ9ImlkZW50aXR5ImAgbWVtYmVyaSB0YWh1IGZ1bmdzaSBwbG90dGluZyB1bnR1ayB0aWRhayBtZW5naGl0dW5nIGhpdHVuZ2FuLCBrYXJlbmEgZGlzZWRpYWthbiBzZWNhcmEgbGFuZ3N1bmcuDQoNCmBgYHtyIFZpc3VhbGl6YXRpb24zLCBtZXNzYWdlPUZBTFNFLCBlY2hvPVQsZmlnLmFsaWduPSdjZW50ZXInLGZpZy5jYXA9IkNhdGVnb3JpY2FsIEJhciBDaGFydCAzIiwgb3V0LndpZHRoID0gJzEwMCUnfQ0KbGlicmFyeShkcGx5cikgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHVudHVrIG1hbmlwdWxhc2kgZGF0YQ0KbGlicmFyeShnZ3Bsb3QyKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHVudHVrIG1hbmlwdWxhc2kgZGF0YQ0KcGxvdGRhdGEgPC0gTWFycmlhZ2UgJT4lICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIG11YXQgZGF0YXNldA0KIGNvdW50KHpvZGlhY3MpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGp1bWxhaCBwZXNlcnRhIGRpIHNldGlhcCAnem9kaWFrJw0KIyBwbG90IGJhciBkYWxhbSB1cnV0YW4gbmFpaw0KZ2dwbG90KHBsb3RkYXRhLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICANCiAgICAgICBhZXMoeCA9IHJlb3JkZXIoem9kaWFjcywgbiksIA0KICAgICAgICAgICB5ID0gbikpICsgDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLA0KICAgICAgICAgICBmaWxsID0gcmFpbmJvdygxMiksIA0KICAgICAgICAgICBjb2xvcj0gImF6dXJlNCIpICsNCiAgIHRoZW1lX21pbmltYWwoKSArICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgZ3VuYWthbiBtaW5pbWFsIHRlbWENCiAgbGFicyh4ID0gIlpvZGlhY3MiLCANCiAgICAgICB5ID0gIkZyZXF1ZW5jeSIsIA0KICB0aXRsZSAgPSAiU29ydGluZyBDYXRlZ29yaWVzIikNCmBgYA0KDQpKaWthIEFuZGEgbXVuZ2tpbiBpbmdpbiBtZW1iZXJpIGxhYmVsIHNldGlhcCBiYXRhbmcgZGVuZ2FuIG5pbGFpIG51bWVyaWtueWEsIHBlcmlrc2Ega29kZSBiZXJpa3V0Og0KDQpgYGB7ciBWaXN1YWxpemF0aW9uNCwgbWVzc2FnZT1GQUxTRSwgZWNobz1ULGZpZy5hbGlnbj0nY2VudGVyJyxmaWcuY2FwPSJDYXRlZ29yaWNhbCBCYXIgQ2hhcnQgNCIsIG91dC53aWR0aCA9ICcxMDAlJ30NCmxpYnJhcnkoZHBseXIpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB1bnR1ayBtYW5pcHVsYXNpIGRhdGENCmxpYnJhcnkoZ2dwbG90MikgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB1bnR1ayB2aXN1YWxpc2FzaQ0KbGlicmFyeShzY2FsZXMpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHNlY2FyYSBvdG9tYXRpcyBtZW5lbnR1a2FuIGplZGEvbGFiZWwgDQpwbG90ZGF0YSA8LSBNYXJyaWFnZSAlPiUNCiAgY291bnQoem9kaWFjcykgJT4lDQogIG11dGF0ZShwY3QgPSBuIC8gc3VtKG4pLA0KICAgICAgICAgcGN0bGFiZWwgPSBwYXN0ZTAocm91bmQocGN0KjEwMCksICIlIikpDQojIHBsb3QgYmFyIHNlYmFnYWkgcGVyc2VudGFzZSwgZGFsYW0gdXJ1dGFuIG1lbnVydW4gZGVuZ2FuIGxhYmVsIGJhdGFuZw0KZ2dwbG90KHBsb3RkYXRhLCANCiAgICAgICBhZXMoeCA9IHJlb3JkZXIoem9kaWFjcywgLXBjdCksDQogICAgICAgICAgIHkgPSBwY3QpKSArIA0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgDQogICAgICAgICAgIGZpbGwgPSByYWluYm93KDEyKSwgDQogICAgICAgICAgY29sb3IgPSAiYXp1cmU0IikgKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsID0gcGN0bGFiZWwpLCANCiAgICAgICAgICAgIHZqdXN0ID0gLTAuMjUpICsNCiAgdGhlbWVfbWluaW1hbCgpICsgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBndW5ha2FuIG1pbmltYWwgdGVtYQ0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gcGVyY2VudCkgKw0KICBsYWJzKHggPSAiWm9kaWFjcyIsIA0KICAgICAgIHkgPSAiUGVyY2VudCIsIA0KICAgICAgIHRpdGxlICA9ICJMYWJlbGluZyBCYXJzIikNCmBgYA0KDQpLYWRhbmcta2FkYW5nIGxhYmVsIGthdGVnb3JpIG11bmdraW4gdHVtcGFuZyB0aW5kaWgsIGl0dSBzYW5nYXQgbWVuamVuZ2tlbGthbiBrYW4/LiBKYWRpLCBBbmRhIGRhcGF0IG1lbXV0YXIgbGFiZWwgc3VtYnUuDQoNCmBgYHtyIFZpc3VhbGl6YXRpb241LCBlY2hvPVQsZmlnLmFsaWduPSdjZW50ZXInLGZpZy5jYXA9IkNhdGVnb3JpY2FsIEJhciBDaGFydCA1Iiwgb3V0LndpZHRoID0gJzEwMCUnfQ0KbGlicmFyeShnZ3Bsb3QyKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHVudHVrIHZpc3VhbGlzYXNpDQpsaWJyYXJ5KHNjYWxlcykgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgc2VjYXJhIG90b21hdGlzIG1lbmVudHVrYW4gaXN0aXJhaGF0IC8gbGFiZWwgDQojIHBsb3QgYmFyIHNlYmFnYWkgcGVyc2VudGFzZSwgDQojIGRhbGFtIHVydXRhbiBtZW51cnVuIGRlbmdhbiBsYWJlbCBiYXRhbmcNCmdncGxvdChwbG90ZGF0YSwgDQogICAgICAgYWVzKHggPSByZW9yZGVyKHpvZGlhY3MsIC1wY3QpLA0KICAgICAgICAgICB5ID0gcGN0KSkgKyANCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIA0KICAgICAgICAgICBmaWxsID0gcmFpbmJvdygxMiksIA0KICAgICAgICAgICBjb2xvciA9ICJhenVyZTQiKSArDQogIGdlb21fdGV4dChhZXMobGFiZWwgPSBwY3RsYWJlbCksIA0KICAgICAgICAgICAgdmp1c3QgPSAtMC4yNSkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gcGVyY2VudCkgKw0KICB0aGVtZV9taW5pbWFsKCkgKyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGd1bmFrYW4gdGVtYSBtaW5pbWFsDQogIGxhYnMoeCA9ICJab2RpYWNzIiwgDQogICAgICAgeSA9ICJQZXJjZW50IiwgDQogICAgICAgdGl0bGUgID0gIk92ZXJsYXBwaW5nIExhYmVscyIpKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDI1LCBoanVzdCA9IDEpKQ0KYGBgDQoNCkF0YXUsIEFuZGEgZGFwYXQgbWVuYW5nYW5pIHNpdHVhc2kgaW5pIGRlbmdhbiBtZW1iYWxpayBzdW1idSBgeGAgZGFuIGB5YC4NCg0KYGBge3IgVmlzdWFsaXphdGlvbjYsIGVjaG89VCxmaWcuYWxpZ249J2NlbnRlcicsZmlnLmNhcD0iQ2F0ZWdvcmljYWwgQmFyIENoYXJ0IDYiLCBvdXQud2lkdGggPSAnMTAwJSd9DQpsaWJyYXJ5KGdncGxvdDIpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgdW50dWsgdmlzdWFsaXNhc2kNCmxpYnJhcnkoc2NhbGVzKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBzZWNhcmEgb3RvbWF0aXMgbWVuZW50dWthbiBpc3RpcmFoYXQgLyBsYWJlbCANCiMgcGxvdCBiYXIgc2ViYWdhaSBwZXJzZW50YXNlLCANCiMgZGFsYW0gdXJ1dGFuIG1lbnVydW4gZGVuZ2FuIGxhYmVsIGJhdGFuZw0KZ2dwbG90KHBsb3RkYXRhLCANCiAgICAgICBhZXMoeCA9IHJlb3JkZXIoem9kaWFjcywgLXBjdCksDQogICAgICAgICAgIHkgPSBwY3QpKSArIA0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgDQogICAgICAgICAgIGZpbGwgPSByYWluYm93KDEyKSwgDQogICAgICAgICAgIGNvbG9yID0gImF6dXJlNCIpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHBjdGxhYmVsKSwgDQogICAgICAgICAgICBoanVzdCA9IC0wLjEwKSArDQogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBwZXJjZW50KSArDQogIHRoZW1lX21pbmltYWwoKSArICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgZ3VuYWthbiBtaW5pbWFsIHRlbWENCiAgbGFicyh4ID0gIlpvZGlhayIsIA0KICAgICAgIHkgPSAiUGVyY2VudCIsIA0KICAgICAgIHRpdGxlICA9ICJPdmVybGFwcGluZyBMYWJlbHMiKSsNCiAgY29vcmRfZmxpcCgpDQpgYGANCg0KIyMjIFBpZSBDaGFydA0KDQpQaWUgY2hhcnRzIGtvbnRyb3ZlcnNpYWwgZGFsYW0gc3RhdGlzdGlrLiBKaWthIHR1anVhbiBBbmRhIGFkYWxhaCBtZW1iYW5kaW5na2FuIGZyZWt1ZW5zaSBrYXRlZ29yaSwgQW5kYSBsZWJpaCBiYWlrIGRlbmdhbiBiYWdhbiBiYXRhbmcgKG1hbnVzaWEgbGViaWggYmFpayBtZW5pbGFpIHBhbmphbmcgYmF0YW5nIGRhcmlwYWRhIHZvbHVtZSBpcmlzYW4gcGFpKS4gSmlrYSB0dWp1YW4gQW5kYSBhZGFsYWggbWVtYmFuZGluZ2thbiBzZXRpYXAga2F0ZWdvcmkgZGVuZ2FuIGtlc2VsdXJ1aGFuIChtaXNhbG55YSwgYmFnaWFuIHBlc2VydGEgYXBhIHlhbmcgaGlzcGFuaWsgZGliYW5kaW5na2FuIGRlbmdhbiBzZW11YSBwZXNlcnRhKSwgZGFuIGp1bWxhaCBrYXRlZ29yaSBrZWNpbCwgbWFrYSBiYWdhbiBwYWkgZGFwYXQgYmVyZnVuZ3NpIHVudHVrIEFuZGEuIERpYnV0dWhrYW4gc2VkaWtpdCBsZWJpaCBiYW55YWsga29kZSB1bnR1ayBtZW1idWF0IGJhZ2FuIHBhaSB5YW5nIG1lbmFyaWsgZGkgUi4NCg0KSW5pIGFkYWxhaCBjb250b2ggdW50dWsgbWVtYnVhdCBiYWdhbiBwYWkgZ2dwbG90MiBkYXNhcjoNCg0KYGBge3IgVmlzdWFsaXphdGlvbjcsZWNobz1ULGZpZy5hbGlnbj0nY2VudGVyJyxmaWcuY2FwPSJDYXRlZ29yaWNhbCBQaWUgQ2hhcnQiLCBvdXQud2lkdGggPSAnMTAwJSd9DQpsaWJyYXJ5KGRwbHlyKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgdW50dWsgbWFuaXB1bGFzaSBkYXRhDQpsaWJyYXJ5KGdncGxvdDIpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgdW50dWsgdmlzdWFsaXNhc2kNCmxpYnJhcnkoc2NhbGVzKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBzZWNhcmEgb3RvbWF0aXMgbWVuZW50dWthbiBpc3RpcmFoYXQgLyBsYWJlbCANCiMgUGVyc2lhcGFuIGRhdGENCnBsb3RkYXRhIDwtIE1hcnJpYWdlICU+JQ0KICBjb3VudChyYWNlKSAlPiUNCiAgYXJyYW5nZShkZXNjKHJhY2UpKSAlPiUNCiAgbXV0YXRlKHByb3AgPSByb3VuZChuKjEwMC9zdW0obiksIDEpLA0KICAgICAgICAgbGFiLnlwb3MgPSBjdW1zdW0ocHJvcCkgLSAwLjUqcHJvcCkNCiMgYnVhdCBQaWUgY2hhcnQNCm15Y29scyA8LSBjKCIjMDA3M0MyRkYiLCAiI0VGQzAwMEZGIiwgIiM4Njg2ODZGRiIsICIjQ0Q1MzRDRkYiKQ0KZ2dwbG90KHBsb3RkYXRhLCBhZXMoeCA9ICIiLCB5ID0gcHJvcCwgZmlsbCA9IHJhY2UpKSArDQogIGdlb21fYmFyKHdpZHRoID0gMSwgc3RhdCA9ICJpZGVudGl0eSIsIGNvbG9yID0gIndoaXRlIikgKw0KICBjb29yZF9wb2xhcigieSIsIHN0YXJ0ID0gMCkrDQogIGdlb21fdGV4dChhZXMoeSA9IGxhYi55cG9zLCBsYWJlbCA9IHByb3ApLCBjb2xvciA9ICJ3aGl0ZSIpKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBteWNvbHMpICsNCiAgdGhlbWVfdm9pZCgpKw0KICBsYWJzKHRpdGxlID0gIk1hcnJpYWdlIFBhcnRpY2lwYW50cyBieSBSYWNlIikNCmBgYA0KDQpCYWdhbiBkb25hdCBoYW55YWxhaCBiYWdhbiBwYWkgc2VkZXJoYW5hIGRlbmdhbiBsdWJhbmcgZGkgZGFsYW1ueWEuIFNhdHUtc2F0dW55YSBwZXJiZWRhYW4gYW50YXJhIGtvZGUgYmFnYW4gcGFpIGFkYWxhaCBraXRhIG1lbmdhdHVyOiBgeCA9IDJgIGRhbiBgeGxpbSA9IGMoMCw1LCAyLDUpYCB1bnR1ayBtZW1idWF0IGx1YmFuZyBkaSBkYWxhbSBiYWdhbiBwYWkuIFNlbGFpbiBpdHUsIGFyZ3VtZW4gYHdpZHRoYCBkYWxhbSBmdW5nc2kgYGdlb21fYmFyKClgIHRpZGFrIGxhZ2kgZGlwZXJsdWthbi4NCg0KYGBge3IgVmlzdWFsaXphdGlvbjgsIG1lc2FnZT1GLGVjaG89VCxmaWcuYWxpZ249J2NlbnRlcicsZmlnLmNhcD0iQ2F0ZWdvcmljYWwgRG9udXQgQ2hhcnQgMSIsIG91dC53aWR0aCA9ICcxMDAlJ30NCmxpYnJhcnkoZ2dwbG90MikgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB1bnR1ayB2aXN1YWxpc2FzaQ0KbGlicmFyeShzY2FsZXMpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHNlY2FyYSBvdG9tYXRpcyBtZW5lbnR1a2FuIGlzdGlyYWhhdCAvIGxhYmVsDQojIGJ1YXQgYmFnYW4gRG9uYXQNCmdncGxvdChwbG90ZGF0YSwgYWVzKHggPSAyLCB5ID0gcHJvcCwgZmlsbCA9IHJhY2UpKSArDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBjb2xvciA9ICJ3aGl0ZSIpICsNCiAgY29vcmRfcG9sYXIodGhldGEgPSAieSIsIHN0YXJ0ID0gMCkrDQogIGdlb21fdGV4dChhZXMoeSA9IGxhYi55cG9zLCBsYWJlbCA9IHByb3ApLCBjb2xvciA9ICJ3aGl0ZSIpKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBteWNvbHMpICsNCiAgdGhlbWVfdm9pZCgpKw0KICB4bGltKDAuNSwgMi41KSsNCiAgbGFicyh0aXRsZSA9ICJNYXJyaWFnZSBQYXJ0aWNpcGFudHMgYnkgUmFjZSIpDQpgYGANCnNla2FyYW5nIGxldOKAmXMgZGFwYXRrYW4gbGFiZWwgbWV3YWggZGFuIHRhbWJhaGthbiwgc2FtYmlsIG1lbmdoYXB1cyBsZWdlbmRhLg0KDQpgYGB7ciBWaXN1YWxpemF0aW9uOSwgZWNobz1ULGZpZy5hbGlnbj0nY2VudGVyJyxmaWcuY2FwPSJDYXRlZ29yaWNhbCBEb251dCBDaGFydCAyIiwgb3V0LndpZHRoID0gJzEwMCUnfQ0KbGlicmFyeShnZ3Bsb3QyKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHVudHVrIHZpc3VhbGlzYXNpDQpsaWJyYXJ5KHNjYWxlcykgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgc2VjYXJhIG90b21hdGlzIG1lbmVudHVrYW4gaXN0aXJhaGF0IC8gbGFiZWwNCiMgdGFtYmFoa2FuIGxhYmVsIHBlcnNlbg0KcGxvdGRhdGEkcGVyY2VudCA8LSBwYXN0ZTAocGxvdGRhdGEkcmFjZSwgIlxuIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICByb3VuZChwbG90ZGF0YSRwcm9wKSwgIiUiKQ0KIyBtZW1idWF0IGdyYWZpayBEb25hdCBkYWxhbSBwZXJzZW4NCmdncGxvdChwbG90ZGF0YSwgYWVzKHggPSAyLCB5ID0gcHJvcCwgZmlsbCA9IHJhY2UpKSArDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBjb2xvciA9ICJ3aGl0ZSIpICsNCiAgY29vcmRfcG9sYXIodGhldGEgPSAieSIsIHN0YXJ0ID0gMCkrDQogIGdlb21fdGV4dChhZXMoeSA9IGxhYi55cG9zLCBsYWJlbCA9IHBlcmNlbnQpLCBjb2xvciA9ICJ3aGl0ZSIpKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBteWNvbHMpICsNCiAgdGhlbWVfdm9pZCgpKw0KICB4bGltKDAuNSwgMi41KSsNCiAgbGFicyh0aXRsZSA9ICJNYXJyaWFnZSBQYXJ0aWNpcGFudHMgYnkgUmFjZSIpDQogICN0ZW1hKGxlZ2VuZC5wb3NpdGlvbiA9ICJGQUxTRSIpDQpgYGANCiMjIyBUcmVlIE1hcA0KDQpBbHRlcm5hdGlmIHVudHVrIGJhZ2FuIHBhaSBhZGFsYWggcGV0YSBwb2hvbi4gVGlkYWsgc2VwZXJ0aSBiYWdhbiBwYWksIGlhIGRhcGF0IG1lbmFuZ2FuaSB2YXJpYWJlbCBrYXRlZ29yaXMgeWFuZyBtZW1pbGlraSBiYW55YWsgdGluZ2thdGFuLg0KDQpgYGB7ciBWaXN1YWxpemF0aW9uMTAsIGVjaG89VCxmaWcuYWxpZ249J2NlbnRlcicsZmlnLmNhcD0iQ2F0ZWdvcmljYWwgVHJlZSBNYXAgMSIsIG91dC53aWR0aCA9ICcxMDAlJ30NCmxpYnJhcnkoZ2dwbG90MikgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBmb3IgdmlzdWFsaXphdGlvbg0KbGlicmFyeSh0cmVlbWFwaWZ5KSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGZvciB2aXN1YWxpemF0aW9uDQpsaWJyYXJ5KHNjYWxlcykgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgYXV0b21hdGljYWxseSBkZXRlcm1pbmluZyBicmVha3MvbGFiZWxzDQpwbG90ZGF0YSA8LSBNYXJyaWFnZSAlPiUNCiAgY291bnQob2ZmaWNpYWxUaXRsZSkNCmdncGxvdChwbG90ZGF0YSwgDQogICAgICAgYWVzKGZpbGwgPSBvZmZpY2lhbFRpdGxlLCANCiAgICAgICAgICAgYXJlYSA9IG4pKSArDQogIGdlb21fdHJlZW1hcCgpICsgDQogIGxhYnModGl0bGUgPSAiTWFycmlhZ2UgUGFydGljaXBhbnRzIGJ5IE9mZmljaWF0ZSIpDQpgYGANCg0KQmVyaWt1dCBhZGFsYWggdmVyc2kgeWFuZyBsZWJpaCBiZXJndW5hIGRlbmdhbiBsYWJlbC4NCg0KYGBge3IgVmlzdWFsaXphdGlvbjExLCBlY2hvPVQsZmlnLmFsaWduPSdjZW50ZXInLGZpZy5jYXA9IkNhdGVnb3JpY2FsIFRyZWUgTWFwIDIiLCBvdXQud2lkdGggPSAnMTAwJSd9DQpnZ3Bsb3QocGxvdGRhdGEsIA0KICAgICAgIGFlcyhmaWxsID0gb2ZmaWNpYWxUaXRsZSwgDQogICAgICAgICAgIGFyZWEgPSBuLCANCiAgICAgICAgICAgbGFiZWwgPSBvZmZpY2lhbFRpdGxlKSkgKw0KICBnZW9tX3RyZWVtYXAoKSArIA0KICBnZW9tX3RyZWVtYXBfdGV4dChjb2xvdXIgPSAid2hpdGUiLCANCiAgICAgICAgICAgICAgICAgICAgcGxhY2UgPSAiY2VudHJlIikgKw0KICBsYWJzKHRpdGxlID0gIk1hcnJpYWdlIFBhcnRpY2lwYW50cyBieSBPZmZpY2lhdGUiKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikNCmBgYA0KDQojIyBCZXJrZWxhbmp1dGFuDQoNCkRpc3RyaWJ1c2kgdmFyaWFiZWwga3VhbnRpdGF0aWYgdHVuZ2dhbCBiaWFzYW55YSBkaXBsb3QgZGVuZ2FuIHBsb3QgaGlzdG9ncmFtLCBba2VwYWRhdGFuIGtlcm5lbF0oKSwgYXRhdSBwbG90IHRpdGlrLg0KDQojIyMgSGlzdG9ncmFtDQoNCk1lbmdndW5ha2FuIGt1bXB1bGFuIGRhdGEgUGVybmlrYWhhbiwgbGV04oCZcyBwbG90IHVzaWEgcGVzZXJ0YSBwZXJuaWthaGFuLg0KDQpgYGB7ciBWaXN1YWxpemF0aW9uMTIsIGVjaG89VCxmaWcuYWxpZ249J2NlbnRlcicsZmlnLmNhcD0iUXVhbnRpdGF0aXZlIEhpc3RvZ3JhbSIsIG91dC53aWR0aCA9ICcxMDAlJ30NCmxpYnJhcnkoZ2dwbG90MikgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBmb3IgdmlzdWFsaXphdGlvbg0KZ2dwbG90KE1hcnJpYWdlLCBhZXMoeCA9IGFnZSkpICsNCiAgZ2VvbV9oaXN0b2dyYW0oZmlsbCA9ICJjb3JuZmxvd2VyYmx1ZSIsIA0KICAgICAgICAgICAgICAgICBjb2xvciA9ICJ3aGl0ZSIsYmlucyA9IDIwKSArIA0KICB0aGVtZV9taW5pbWFsKCkgKyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHVzZSBhIG1pbmltYWwgdGhlbWUNCiAgbGFicyh0aXRsZT0iTWFycmlhZ2UgUGFydGljaXBhbnRzIGJ5IGFnZSAoQmFzaWMpIiwNCiAgICAgICB4ID0gIkFnZSIpDQpgYGANClNlYmFnaWFuIGJlc2FyIHBlc2VydGEgdGFtcGFrbnlhIGJlcmFkYSBkaSBhd2FsIDIw4oCZcyBtZXJla2EgZGVuZ2FuIGtlbG9tcG9rIGxhaW4gZGkgIDQw4oCZcywgbWVyZWthLCBkYW4ga2Vsb21wb2sgeWFuZyBqYXVoIGxlYmloIGtlY2lsIGRpIHVzaWEgZW5hbSBwdWx1aGFuIGRhbiBhd2FsIHRhaHVuIHR1anVoIHB1bHVoYW4uIEluaSBha2FuIG1lbmphZGkgZGlzdHJpYnVzaSBtdWx0aW1vZGFsLiBXYXJuYSBoaXN0b2dyYW0gZGFwYXQgZGltb2RpZmlrYXNpIG1lbmdndW5ha2FuIGR1YSBvcHNpOg0KDQoqIElzaSAtIHdhcm5hIGlzaWFuIHVudHVrIGJhcg0KKiB3YXJuYSAtIHdhcm5hIHBlcmJhdGFzYW4gZGkgc2VraXRhciBiYXINCg0KQXRhdSwgQW5kYSBkYXBhdCBtZW5lbnR1a2FuICdiaW53aWR0aCcsIGxlYmFyIHRlbXBhdCBzYW1wYWggeWFuZyBkaXdha2lsaSBvbGVoIGJhci4NCg0KYGBge3IgVmlzdWFsaXphdGlvbjE0LCBlY2hvPVQsZmlnLmFsaWduPSdjZW50ZXInLGZpZy5jYXA9IlF1YW50aXRhdGl2ZSBIaXN0b2dyYW0gMiIsIG91dC53aWR0aCA9ICcxMDAlJ30NCmxpYnJhcnkoZ2dwbG90MikgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBmb3IgdmlzdWFsaXphdGlvbg0KbGlicmFyeShzY2FsZXMpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGF1dG9tYXRpY2FsbHkgZGV0ZXJtaW5pbmcgYnJlYWtzL2xhYmVscyANCmdncGxvdChNYXJyaWFnZSwgDQogICAgICAgYWVzKHggPSBhZ2UsIA0KICAgICAgICAgICB5PSAuLmNvdW50Li4gLyBzdW0oLi5jb3VudC4uKSkpICsNCiAgZ2VvbV9oaXN0b2dyYW0oZmlsbCA9ICJjb3JuZmxvd2VyYmx1ZSIsIA0KICAgICAgICAgICAgICAgICBjb2xvciA9ICJ3aGl0ZSIsIA0KICAgICAgICAgICAgICAgICBiaW53aWR0aCA9IDUpICsNCiAgdGhlbWVfbWluaW1hbCgpICsgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB1c2UgYSBtaW5pbWFsIHRoZW1lDQogIGxhYnModGl0bGU9Ik1hcnJpYWdlIFBhcnRpY2lwYW50cyBieSBhZ2UgKEFsdGVybmF0aXZlIEJpbnMgYW5kIGJhbmR3aWR0aHMpIiwgDQogICAgICAgeSA9ICJQZXJjZW50IiwNCiAgICAgICB4ID0gIkFnZSIpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHBlcmNlbnQpDQpgYGANCg0KU2VwZXJ0aSBoYWxueWEgYmFnYW4gYmF0YW5nLCBzdW1idSB5IGRhcGF0IG1ld2FraWxpIGhpdHVuZ2FuIGF0YXUgcGVyc2VuIGRhcmkgdG90YWwuDQoNCmBgYHtyIFZpc3VhbGl6YXRpb24xNSwgZWNobz1ULGZpZy5hbGlnbj0nY2VudGVyJyxmaWcuY2FwPSJRdWFudGl0YXRpdmUgSGlzdG9ncmFtIDMiLCBvdXQud2lkdGggPSAnMTAwJSd9DQpsaWJyYXJ5KGdncGxvdDIpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgZm9yIHZpc3VhbGl6YXRpb24NCmxpYnJhcnkoc2NhbGVzKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBhdXRvbWF0aWNhbGx5IGRldGVybWluaW5nIGJyZWFrcy9sYWJlbHMgDQpnZ3Bsb3QoTWFycmlhZ2UsIA0KICAgICAgIGFlcyh4ID0gYWdlLCANCiAgICAgICAgICAgeT0gLi5jb3VudC4uIC8gc3VtKC4uY291bnQuLikpKSArDQogIGdlb21faGlzdG9ncmFtKGZpbGwgPSAiY29ybmZsb3dlcmJsdWUiLCANCiAgICAgICAgICAgICAgICAgY29sb3IgPSAid2hpdGUiLCANCiAgICAgICAgICAgICAgICAgYmlud2lkdGggPSA1KSArDQogIHRoZW1lX21pbmltYWwoKSArICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgdXNlIGEgbWluaW1hbCB0aGVtZQ0KICBsYWJzKHRpdGxlPSJNYXJyaWFnZSBQYXJ0aWNpcGFudHMgYnkgYWdlIChQZXJjZW50KSIsIA0KICAgICAgIHkgPSAiUGVyY2VudCIsDQogICAgICAgeCA9ICJBZ2UiKSArDQogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBwZXJjZW50KQ0KYGBgDQoNCiMjIyBLZXJuZWwgRGVuc2l0eSBQbG90DQoNCkFsdGVybmF0aWYgdW50dWsgaGlzdG9ncmFtIGFkYWxhaCBwbG90IGtlcGFkYXRhbiBrZXJuZWwuIFNlY2FyYSB0ZWtuaXMsIGVzdGltYXNpIGtlcGFkYXRhbiBrZXJuZWwgYWRhbGFoIG1ldG9kZSBub24tcGFyYW1ldHJpayB1bnR1ayBtZW1wZXJraXJha2FuIGZ1bmdzaSBrZXJhcGF0YW4gcHJvYmFiaWxpdGFzIGRhcmkgdmFyaWFiZWwgYWNhayBiZXJrZWxhbmp1dGFuLiAoQXBhPz8pIFBhZGEgZGFzYXJueWEsIGthbWkgbWVuY29iYSBtZW5nZ2FtYmFyIGhpc3RvZ3JhbSB5YW5nIGRpaGFsdXNrYW4sIGRpIG1hbmEgYXJlYSBkaSBiYXdhaCBrdXJ2YSBzYW1hIGRlbmdhbiBzYXR1Lg0KDQpgYGB7ciBWaXN1YWxpemF0aW9uMTYsIGVjaG89VCxmaWcuYWxpZ249J2NlbnRlcicsZmlnLmNhcD0iUXVhbnRpdGF0aXZlIEtlbmVsIERlbnNpdHkgUGxvdCIsIG91dC53aWR0aCA9ICcxMDAlJ30NCmxpYnJhcnkoZ2dwbG90MikgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBmb3IgdmlzdWFsaXphdGlvbg0KZ2dwbG90KE1hcnJpYWdlLCBhZXMoeCA9IGFnZSkpICsNCiAgZ2VvbV9kZW5zaXR5KGZpbGwgPSAiaW5kaWFucmVkMyIpICsNCiAgdGhlbWVfbWluaW1hbCgpICsgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB1c2UgYSBtaW5pbWFsIHRoZW1lDQogIGxhYnModGl0bGUgPSAiTWFycmlhZ2UgUGFydGljaXBhbnRzIGJ5IGFnZSIpDQpgYGANCg0KR3JhZmlrIG1lbnVuanVra2FuIGRpc3RyaWJ1c2kgc2tvci4gTWlzYWxueWEsIHByb3BvcnNpIGthc3VzIGFudGFyYSAyMCBkYW4gNDAgdGFodW4gYWthbiBkaXdha2lsaSBvbGVoIGFyZWEgZGkgYmF3YWgga3VydmEgYW50YXJhIDIwIGRhbiA0MCBwYWRhIHN1bWJ1IHguICBTZXBlcnRpIGhhbG55YSBiYWdhbiBzZWJlbHVtbnlhLCBraXRhIGp1Z2EgYmlzYSBtZW5nZ3VuYWthbiBpc2lhbiBkYW4gd2FybmEgdW50dWsgbWVuZW50dWthbiB3YXJuYSBpc2lhbiBkYW4gYmF0YXMuDQoNCiMjIyBNZW5naGFsdXNrYW4gUGFyYW1ldGVyDQoNClRpbmdrYXQga2VoYWx1c2FuIGRpa2VuZGFsaWthbiBvbGVoIHBhcmFtZXRlciBiYW5kd2lkdGggYGJ3YC4gVW50dWsgbWVuZW11a2FuIG5pbGFpIGRlZmF1bHQgdW50dWsgdmFyaWFiZWwgdGVydGVudHUsIGd1bmFrYW4gZnVuZ3NpIGBidy5ucmQwYC4gTmlsYWkgeWFuZyBsZWJpaCBiZXNhciBha2FuIG1lbmdoYXNpbGthbiBsZWJpaCBiYW55YWsga2VoYWx1c2FuLCBzZW1lbnRhcmEgbmlsYWkgeWFuZyBsZWJpaCBrZWNpbCBha2FuIG1lbmdoYXNpbGthbiBsZWJpaCBzZWRpa2l0IHNtb290aGluZy4NCg0KYGBge3IgVmlzdWFsaXphdGlvbjE3LCBlY2hvPVQsZmlnLmFsaWduPSdjZW50ZXInLGZpZy5jYXA9IlNtb290aGluZyBQYXJhbWV0ZXIgUGxvdCIsIG91dC53aWR0aCA9ICcxMDAlJ30NCmxpYnJhcnkoZ2dwbG90MikgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBmb3IgdmlzdWFsaXphdGlvbg0KYncubnJkMChNYXJyaWFnZSRhZ2UpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGRlZmF1bHQgYmFuZHdpZHRoIGZvciB0aGUgYWdlIHZhcmlhYmxlDQpnZ3Bsb3QoTWFycmlhZ2UsIGFlcyh4ID0gYWdlKSkgKw0KICBnZW9tX2RlbnNpdHkoZmlsbCA9ICJkZWVwc2t5Ymx1ZSIsIA0KICAgICAgICAgICAgICAgYncgPSAxKSArDQogIHRoZW1lX21pbmltYWwoKSArICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgdXNlIGEgbWluaW1hbCB0aGVtZQ0KICBsYWJzKHRpdGxlID0gIlBhcnRpY2lwYW50cyBieSBhZ2UiLA0KICAgICAgIHN1YnRpdGxlID0gImJhbmR3aWR0aCA9IDEiKQ0KYGBgDQoNClBsb3Qga2VwYWRhdGFuIGtlcm5lbCBtZW11bmdraW5rYW4gQW5kYSB1bnR1ayBkZW5nYW4gbXVkYWggbWVsaWhhdCBza29yIG1hbmEgeWFuZyBwYWxpbmcgc2VyaW5nIGRhbiB5YW5nIHJlbGF0aWYgamFyYW5nLiBOYW11biBtdW5na2luIHN1bGl0IHVudHVrIG1lbmplbGFza2FuIGFydGkgc3VtYnUgeSBrZXBhZGEgbm9uLWFobGkgc3RhdGlzdGlrLiAoVGFwaSBpdHUgYWthbiBtZW1idWF0IEFuZGEgdGVybGloYXQgYmVuYXItYmVuYXIgY2VyZGFzIGRpIHBlc3RhISkNCg0KIyMjIEJhZ2FuIFRpdGlrDQoNCkFsdGVybmF0aWYgbGFpbiB1bnR1ayBoaXN0b2dyYW0gYWRhbGFoIGJhZ2FuIHRpdGlrLiBTZWthbGkgbGFnaSwgdmFyaWFiZWwga3VhbnRpdGF0aWYgZGliYWdpIG1lbmphZGkgdGVtcGF0IHNhbXBhaCwgdGV0YXBpIGRhcmlwYWRhIGJpbGFoIHJpbmdrYXNhbiwgc2V0aWFwIHBlbmdhbWF0YW4gZGl3YWtpbGkgb2xlaCB0aXRpay4gU2VjYXJhIGRlZmF1bHQsIGxlYmFyIHRpdGlrIHNlc3VhaSBkZW5nYW4gbGViYXIgYmluZXIsIGRhbiB0aXRpay10aXRpayBkaXR1bXB1aywgZGVuZ2FuIHNldGlhcCB0aXRpayBtZXdha2lsaSBzYXR1IHBlbmdhbWF0YW4uIEluaSBiZWtlcmphIHBhbGluZyBiYWlrIGtldGlrYSBqdW1sYWggcGVuZ2FtYXRhbiBrZWNpbCAoa2F0YWthbmxhaCwga3VyYW5nIGRhcmkgMTUwKS4gT3BzaSBpc2lhbiBkYW4gd2FybmEgZGFwYXQgZGlndW5ha2FuIHVudHVrIG1lbmVudHVrYW4gd2FybmEgaXNpYW4gZGFuIGJhdGFzIG1hc2luZy1tYXNpbmcgdGl0aWsNCg0KYGBge3IgVmlzdWFsaXphdGlvbjE4LCBlY2hvPVQsZmlnLmFsaWduPSdjZW50ZXInLGZpZy5jYXA9IkRvdCBDaGFydCIsIG91dC53aWR0aCA9ICcxMDAlJ30NCmxpYnJhcnkoZ2dwbG90MikgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBmb3IgdmlzdWFsaXphdGlvbg0KZ2dwbG90KE1hcnJpYWdlLCBhZXMoeCA9IGFnZSkpICsNCiAgZ2VvbV9kb3RwbG90KGZpbGwgPSAiZ29sZCIsIA0KICAgICAgICAgICAgICAgY29sb3IgPSAiYXp1cmU0IiwNCiAgICAgICAgICAgICAgIGJpbndpZHRoID0gMikgKw0KICB0aGVtZV9taW5pbWFsKCkgKyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHVzZSBhIG1pbmltYWwgdGhlbWUNCiAgbGFicyh0aXRsZSA9ICJQYXJ0aWNpcGFudHMgYnkgYWdlIiwNCiAgICAgICB5ID0gIlByb3BvcnRpb24iLA0KICAgICAgIHggPSAiQWdlIikNCmBgYA0KDQpBZGEgbGViaWggYmFueWFrIG9wc2kgeWFuZyB0ZXJzZWRpYS4gQ2xpY2sgW2hlcmVdKGh0dHA6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL2dlb21fZG90cGxvdC5odG1sKSB1bnR1ayBkZXRhaWxzIGRhbiBjb250b2guDQoNCiMgQml2YXJpYXRlIERhdGENCg0KR3JhZmlrIGJpdmFyaWF0IG1lbmFtcGlsa2FuIGh1YnVuZ2FuIGFudGFyYSBkdWEgdmFyaWFiZWwuIEplbmlzIGdyYWZpayBha2FuIHRlcmdhbnR1bmcgcGFkYSB0aW5na2F0IHBlbmd1a3VyYW4gdmFyaWFiZWwgKGthdGVnb3JpcyBhdGF1IGt1YW50aXRhdGlmKQ0KDQojIyBLYXRlZ29yaXMgdnMuIEthdGVnb3Jpcw0KDQojIyMgQmFnYW4gQmF0YW5nIFR1bXB1ayANCg0KTWFyaSBraXRhIHBsb3QgaHVidW5nYW4gYW50YXJhIGtlbGFzIG1vYmlsIGRhbiBqZW5pcyBwZW5nZ2VyYWsgKHJvZGEgZGVwYW4sIHJvZGEgYmVsYWthbmcsIGF0YXUgcGVuZ2dlcmFrIDQgcm9kYSkgdW50dWsgbW9iaWwgZGlbRnVlbCBlY29ub215IGRhdGFzZXRdKGh0dHBzOi8vYm9va2Rvd24ub3JnL0Jha3RpU2lyZWdhci9kYXRhLXNjaWVuY2UtZm9yLWJlZ2lubmVycy9kYXRhc2V0cy5odG1sI2Z1ZWwtZWNvbm9teS1kYXRhKS4NCg0KYGBge3IgVmlzdWFsaXphdGlvbjE5LCBlY2hvPVQsZmlnLmFsaWduPSdjZW50ZXInLGZpZy5jYXA9IlN0YWNrZWQgQmFyIENoYXJ0Iiwgb3V0LndpZHRoID0gJzEwMCUnfQ0KbGlicmFyeShnZ3Bsb3QyKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGZvciB2aXN1YWxpemF0aW9uDQptcGckZHJ2PC1mYWN0b3IobXBnJGRydiwgDQogICAgICAgICAgICAgICAgbGV2ZWxzID0gYygiZiIsICJyIiwgIjQiKSwNCiAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJmcm9udC13aGVlbCIsICJyZWFyLXdoZWVsIiwgIjQtd2hlZWwiKSkNCiMgc3RhY2tlZCBiYXIgY2hhcnQNCmdncGxvdChtcGcsIA0KICAgICAgIGFlcyh4ID0gY2xhc3MsIA0KICAgICAgICAgICBmaWxsID0gZHJ2KSkgKyANCiAgZ2VvbV9iYXIocG9zaXRpb24gPSAiZmlsbCIpICsNCiAgdGhlbWVfbWluaW1hbCgpICsgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB1c2UgYSBtaW5pbWFsIHRoZW1lDQogIGxhYnMoeSA9ICJQcm9wb3J0aW9uIikNCmBgYA0KDQojIyMgQmFnYW4gQmF0YW5nIFlhbmcgRGlrZWxvbXBva2thbiANCg0KQmFnYW4gYmF0YW5nIHlhbmcgZGlrZWxvbXBva2thbiBtZW5lbXBhdGthbiBiaWxhaCB1bnR1ayB2YXJpYWJlbCBrYXRlZ29yaXMga2VkdWEgYmVyZGFtcGluZ2FuLiBVbnR1ayBtZW1idWF0IHBsb3QgYmlsYWggeWFuZyBkaWtlbG9tcG9ra2FuLCBndW5ha2FuIG9wc2kgYHBvc2lzaSA9ICJ0dW5nZ2FsImAuIFBlcmhhdGlrYW4gYmFod2Egb3BzaSBpbmkgaGFueWEgdGVyc2VkaWEgZGFsYW0gdmVyc2kgcGVuZ2VtYmFuZ2FuIHRlcmJhcnUgZ2dwbG90MiwgdGV0YXBpIGhhcnVzIHRlcnNlZGlhIHNlY2FyYSB1bXVtIHNlZ2VyYS4NCg0KYGBge3IgVmlzdWFsaXphdGlvbjIwLCBlY2hvPVQsZmlnLmFsaWduPSdjZW50ZXInLGZpZy5jYXA9Ikdyb3VwZWQgQmFyIENoYXJ0Iiwgb3V0LndpZHRoID0gJzEwMCUnfQ0KbGlicmFyeShnZ3Bsb3QyKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGZvciB2aXN1YWxpemF0aW9uDQpnZ3Bsb3QobXBnLCBhZXMoeCA9IGNsYXNzLCBmaWxsID0gZHJ2KSkgKw0KICB0aGVtZV9taW5pbWFsKCkgKyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHVzZSBhIG1pbmltYWwgdGhlbWUNCiAgZ2VvbV9iYXIocG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZShwcmVzZXJ2ZSA9ICJzaW5nbGUiKSkNCmBgYA0KDQojIyMgQmFnYW4gQmF0YW5nIFRlcnNlZ21lbnRhc2kNCg0KUGxvdCBiYXIgdGVyc2VnbWVudGFzaSBhZGFsYWggcGxvdCBiYXIgdHVtcHVrIGRpIG1hbmEgc2V0aWFwIGJhdGFuZyBtZXdha2lsaSAxMDAgcGVyc2VuLiBBbmRhIGRhcGF0IG1lbWJ1YXQgYmFnYW4gYmF0YW5nIHRlcnNlZ21lbnRhc2kgbWVuZ2d1bmFrYW4gb3BzaSBwb3Npc2kgPSAidGVyaXNpIi4gSmVuaXMgcGxvdCBpbmkgc2FuZ2F0IGJlcmd1bmEgamlrYSB0dWp1YW5ueWEgYWRhbGFoIHVudHVrIG1lbWJhbmRpbmdrYW4gcGVyc2VudGFzZSBrYXRlZ29yaSBkYWxhbSBzYXR1IHZhcmlhYmVsIGRpIHNldGlhcCB0aW5na2F0IHZhcmlhYmVsIGxhaW4uIE1pc2FsbnlhLCBwcm9wb3JzaSBtb2JpbCBiZXJrZW5kYSByb2RhIGRlcGFuIG5haWsgc2FhdCBBbmRhIGJlcmdlcmFrIGRhcmkgcmluZ2thcywga2UgdWt1cmFuIHNlZGFuZywga2UgbWluaXZhbi4NCg0KYGBge3IgVmlzdWFsaXphdGlvbjIxLCBtZXNzYWdlPUZBTFNFLCBlY2hvPVQsZmlnLmFsaWduPSdjZW50ZXInLGZpZy5jYXA9IlNlZ21lbnRlZCBCYXIgQ2hhcnQiLCBvdXQud2lkdGggPSAnMTAwJSd9DQpsaWJyYXJ5KGRwbHlyKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgZm9yIGRhdGEgbWFuaXB1bGF0aW9uDQpsaWJyYXJ5KGdncGxvdDIpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgZm9yIHZpc3VhbGl6YXRpb24NCmxpYnJhcnkoc2NhbGVzKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBhdXRvbWF0aWNhbGx5IGRldGVybWluaW5nIGJyZWFrcy9sYWJlbHMgDQojIGNyZWF0ZSBhIHN1bW1hcnkgZGF0YXNldCAoZGF0YSBtYW5pcHVsYXRpb24pDQpwbG90ZGF0YSA8LSBtcGcgJT4lDQogIGdyb3VwX2J5KGNsYXNzLCBkcnYpICU+JQ0KICBkcGx5cjo6c3VtbWFyaXplKG4gPSBuKCkpICU+JQ0KICBtdXRhdGUocGN0ID0gbi9zdW0obiksDQogICAgICAgICBsYmwgPSBzY2FsZXM6OnBlcmNlbnQocGN0KSkNCiMgY3JlYXRlIHNlZ21lbnRlZCBiYXIgY2hhcnQNCiMgYWRkaW5nIGxhYmVscyB0byBlYWNoIHNlZ21lbg0KZ2dwbG90KHBsb3RkYXRhLCANCiAgICAgICBhZXMoeCA9IGZhY3RvcihjbGFzcyksDQogICAgICAgICAgIHkgPSBwY3QsDQogICAgICAgIGZpbGwgPSBmYWN0b3IoZHJ2KSkpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsDQogICAgICAgICAgIHBvc2l0aW9uID0gImZpbGwiKSArDQogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgMSwgLjIpLCANCiAgICAgICAgICAgICAgICAgICAgIGxhYmVsID0gcGVyY2VudCkrDQogIGdlb21fdGV4dChhZXMobGFiZWwgPSBsYmwpLA0KICAgICAgICAgICAgc2l6ZSA9IDMsDQogICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX3N0YWNrKHZqdXN0ID0gMC41KSkgKw0KICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIlNldDIiKSArDQogIHRoZW1lX21pbmltYWwoKSArICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgdXNlIGEgbWluaW1hbCB0aGVtZQ0KICBsYWJzKHkgPSAiUGVyY2VudCIsDQogICAgICAgZmlsbCA9ICJEcml2ZSBUcmFpbiIsDQogICAgICAgeCA9ICJDbGFzcyIsDQogICAgICAgdGl0bGUgPSAiQXV0b21vYmlsZSBEcml2ZSBieSBDbGFzcyIpICsNCiAgdGhlbWVfbWluaW1hbCgpDQpgYGANCg0KKipDYXRhdGFuOioqIEFuZGEgZGFwYXQgbWVuZ2d1bmFrYW4gb3BzaSB0YW1iYWhhbiB1bnR1ayBtZW5pbmdrYXRrYW4gd2FybmEgZGFuIHBlbGFiZWxhbi4gRGFsYW0gZ3JhZmlrIGRpIGJhd2FoIGluaQ0KDQoqICdmYWt0b3InIG1lbW9kaWZpa2FzaSB1cnV0YW4ga2F0ZWdvcmkgdW50dWsgdmFyaWFiZWwga2VsYXMgZGFuIHVydXRhbiBkYW4gKiAnbGFiZWwnIHVudHVrIHZhcmlhYmVsIGRyaXZlDQoqICdzY2FsZV95X2NvbnRpbnVvdXMnIG1lbW9kaWZpa2FzaSBsYWJlbCB0YW5kYSBjZW50YW5nIHN1bWJ1IHkNCiogJ2xhYnMnIG1lbnllZGlha2FuIGp1ZHVsIGRhbiBtZW5ndWJhaCBsYWJlbCB1bnR1ayBzdW1idSB4IGRhbiB5IGRhbiBsZWdlbmRhDQoqICdzY2FsZV9maWxsX2JyZXdlcicgbWVuZ3ViYWggc2tlbWEgd2FybmEgaXNpYW4NCiogJ3RoZW1lX21pbmltYWwnIG1lbmdoYXB1cyBsYXRhciBiZWxha2FuZyBhYnUtYWJ1IGRhbiBtZW5ndWJhaCB3YXJuYSBncmlkDQoNCkZ1bmdzaSBsYWlubnlhIGRpYmFoYXMgbGViaWggbGVuZ2thcCBkaSBiYWdpYW4gdGVudGFuZyBWaXN1YWxpc2FzaSBEYXRhIEJhYiBMYW5qdXRhbi4NCg0KIyMjIFBsb3QgTW9zYWlrDQoNCkJhZ2FuIG1vc2FpayBkYXBhdCBtZW5hbXBpbGthbiBodWJ1bmdhbiBhbnRhcmEgdmFyaWFiZWwga2F0ZWdvcmlzIG1lbmdndW5ha2FuIHBlcnNlZ2kgcGFuamFuZyB5YW5nIGFyZWFueWEgbWV3YWtpbGkgcHJvcG9yc2kga2FzdXMgdW50dWsga29tYmluYXNpIHRpbmdrYXQgdGVydGVudHUuIFdhcm5hIHViaW4ganVnYSBkYXBhdCBtZW51bmp1a2thbiBodWJ1bmdhbiBkZXJhamF0IGRpIGFudGFyYSB2YXJpYWJlbC4NCg0KTWVza2lwdW4gYmFnYW4gbW9zYWlrIGRhcGF0IGRpYnVhdCBkZW5nYW4gZ2dwbG90MiBtZW5nZ3VuYWthbiBbYGdnbW9zYWljYF0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL2dnbW9zYWljL3ZpZ25ldHRlcy9nZ21vc2FpYy5odG1sKSBwYWtldCwgc2F5YSBzYXJhbmthbiBtZW5nZ3VuYWthbiBwYWtldCB2Y2Qgc2ViYWdhaSBnYW50aW55YS4gTWVza2lwdW4gaXR1IHdvbuKAmXQgYnVhdCBncmFmaWsgZ2dwbG90MiwgcGFrZXQgaW5pIG1lbnllZGlha2FuIHBlbmRla2F0YW4geWFuZyBsZWJpaCBrb21wcmVoZW5zaWYgdW50dWsgbWVtdmlzdWFsaXNhc2lrYW4gZGF0YSBrYXRlZ29yaXMuDQoNCk9yYW5nLW9yYW5nIHRlcnBlc29uYSBkZW5nYW4gVGl0YW5pYyAoYXRhdSBkZW5nYW4gTGVvPykuIERhbGFtIFtUaXRhbmljXShodHRwczovL3N0YXQuZXRoei5jaC9SLW1hbnVhbC9SLWRldmVsL2xpYnJhcnkvZGF0YXNldHMvaHRtbC9UaXRhbmljLmh0bWwpIGJlbmNhbmEsIGFwYSBwZXJhbiBzZWtzIGRhbiBrZWxhcyBiZXJtYWluIGRhbGFtIGJlcnRhaGFuIGhpZHVwPyBLaXRhIGRhcGF0IG1lbXZpc3VhbGlzYXNpa2FuIGh1YnVuZ2FuIGFudGFyYSBrZXRpZ2EgdmFyaWFiZWwga2F0ZWdvcmlzIGluaSBtZW5nZ3VuYWthbiBrb2RlIGRpIGJhd2FoIGluaS4NCg0KYGBge3IgVmlzdWFsaXphdGlvbjIyLCBjYWNoZT1ULCBldmFsPVQsbWVzYWdlPUYsZmlnLmFsaWduPSdjZW50ZXInLGZpZy5jYXA9IkJhc2ljIG1vc2FpYyBwbG90Iiwgb3V0LndpZHRoID0gJzEwMCUnfQ0KIyBjcmVhdGUgYSB0YWJsZQ0KdGJsIDwtIHh0YWJzKEZyZXEgfiBTdXJ2aXZlZCArQ2xhc3MgKyBTZXgsIFRpdGFuaWMpDQpmdGFibGUodGJsKQ0KIyBjcmVhdGUgYSBtb3NhaWMgcGxvdCBmcm9tIHRoZSB0YWJsZQ0KbGlicmFyeSh2Y2QpDQptb3NhaWModGJsLCBtYWluID0gIlRpdGFuaWMgZGF0YSIpDQpgYGANCg0KVWt1cmFuIHViaW4gc2ViYW5kaW5nIGRlbmdhbiBwZXJzZW50YXNlIGthc3VzIGRhbGFtIGtvbWJpbmFzaSB0aW5na2F0IHRlcnNlYnV0LiBKZWxhcyBsZWJpaCBiYW55YWsgcGVudW1wYW5nIHRld2FzLCBkYXJpcGFkYSBzZWxhbWF0LiBNZXJla2EgeWFuZyB0ZXdhcyB0ZXJ1dGFtYSBwZW51bXBhbmcgcHJpYSBrZWxhcyAzIGRhbiBrcnUgcHJpYSAoa2Vsb21wb2sgdGVyYmVzYXIpLg0KDQpKaWthIGtpdGEgYmVyYXN1bXNpIGJhaHdhIGtldGlnYSB2YXJpYWJlbCBpbmkgaW5kZXBlbmRlbiwga2l0YSBkYXBhdCBtZW1lcmlrc2EgcmVzaWR1IGRhcmkgbW9kZWwgZGFuIG1lbmF1bmdpIHViaW4gYWdhciBzZXN1YWkuIERhbGFtIGdyYWZpayBkaSBiYXdhaCBpbmksIGJpcnUgdHVhIG1ld2FraWxpIGxlYmloIGJhbnlhayBrYXN1cyBkYXJpcGFkYSB5YW5nIGRpaGFyYXBrYW4gZGliZXJpa2FuIGtlbWVyZGVrYWFuLiBNZXJhaCBnZWxhcCBtZXdha2lsaSBsZWJpaCBzZWRpa2l0IGthc3VzIGRhcmkgeWFuZyBkaWhhcmFwa2FuIGppa2Ega2VtZXJkZWthYW4gbWVtZWdhbmcuDQoNCmBgYHtyIFZpc3VhbGl6YXRpb24yMywgY2FjaGU9VCwgZXZhbD1ULG1lc2FnZT1GLGZpZy5hbGlnbj0nY2VudGVyJyxmaWcuY2FwPSJNb3NhaWMgcGxvdCB3aXRoIHNoYWRpbmciLCBvdXQud2lkdGggPSAnMTAwJSd9DQptb3NhaWModGJsLCANCiAgICAgICBzaGFkZSA9IFRSVUUsDQogICAgICAgbGVnZW5kID0gVFJVRSwNCiAgICAgICBsYWJlbGluZ19hcmdzID0gbGlzdChzZXRfdmFybmFtZXMgPSBjKFNleCA9ICJHZW5kZXIiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgU3Vydml2ZWQgPSAiU3Vydml2ZWQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQ2xhc3MgPSAiUGFzc2VuZ2VyIENsYXNzIikpLA0KICAgICAgIHNldF9sYWJlbHMgPSBsaXN0KFN1cnZpdmVkID0gYygiTm8iLCAiWWVzIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgQ2xhc3MgPSBjKCIxc3QiLCAiMm5kIiwgIjNyZCIsICJDcmV3IiksDQogICAgICAgICAgICAgICAgICAgICAgICAgU2V4ID0gYygiRiIsICJNIikpLA0KICAgICAgIG1haW4gPSAiVGl0YW5pYyBkYXRhIikNCmBgYA0KS2l0YSBkYXBhdCBtZWxpaGF0IGJhaHdhIGppa2Ega2VsYXMsIGplbmlzIGtlbGFtaW4sIGRhbiBrZWxhbmdzdW5nYW4gaGlkdXAgbWFuZGlyaSwga2l0YSBtZWxpaGF0IGxlYmloIGJhbnlhayBrcnUgcHJpYSBiaW5hc2EsIGRhbiBwZXJlbXB1YW4ga2VsYXMgMSwgMiBkYW4gMyB5YW5nIGJlcnRhaGFuIGhpZHVwIGRhcmkgeWFuZyBkaWhhcmFwa2FuLiBTZWJhbGlrbnlhLCBqYXVoIGxlYmloIHNlZGlraXQgcGVudW1wYW5nIGtlbGFzIDEgKGJhaWsgcHJpYSBtYXVwdW4gd2FuaXRhKSBtZW5pbmdnYWwgZGFyaXBhZGEgeWFuZyBkaWhhcmFwa2FuIHNlY2FyYSBrZWJldHVsYW4uIERlbmdhbiBkZW1pa2lhbiBhc3Vtc2kga2VtZXJkZWthYW4gZGl0b2xhay4gKFNwb2lsZXIgYWxlcnQ6IExlbyBkb2VzbuKAmXQgbWFrZSBpdC4pDQoNCg0KIyMgQmVya2VsYW5qdXRhbiB2cy4gS29udGludQ0KDQpIdWJ1bmdhbiBhbnRhcmEgZHVhIHZhcmlhYmVsIGt1YW50aXRhdGlmIGJpYXNhbnlhIGRpdGFtcGlsa2FuIG1lbmdndW5ha2FuIHNlYmFyIGRhbiBncmFmaWsgZ2FyaXMuDQoNCiMjIyBTY2F0dGVycGxvdA0KDQpTZWJhciBkaWJ1YXQgdW50dWsgbWVtcGVsYWphcmkgaHVidW5nYW4gYW50YXJhIDIgdmFyaWFiZWwuIERlbmdhbiBkZW1pa2lhbiBzZXJpbmcgZGlzZXJ0YWkgZGVuZ2FuIHBlcmhpdHVuZ2FuIGtvZWZpc2llbiBrb3JlbGFzaSwgeWFuZyBiaWFzYW55YSBtZW5jb2JhIG1lbmd1a3VyIGh1YnVuZ2FuIGxpbmllci4gTmFtdW4gamVuaXMgaHVidW5nYW4gbGFpbiBkYXBhdCBkaWRldGVrc2kgbWVuZ2d1bmFrYW4gc2ViYXIsIGRhbiB0dWdhcyB1bXVtIHRlcmRpcmkgZGFyaSBwYXMgZGVuZ2FuIG1vZGVsIHlhbmcgbWVuamVsYXNrYW4gWSBkYWxhbSBmdW5nc2kgWC4gQmVyaWt1dCBhZGFsYWggYmViZXJhcGEgcG9sYSB5YW5nIGRhcGF0IEFuZGEgZGV0ZWtzaSBtZWxha3VrYW4gc2NhdHRlcnBsb3QuDQoNCmBgYHtyIFZpc3VhbGl6YXRpb24yNCwgd2FybmluZz1GLCBtZXNzYWdlPUYsIGNhY2hlPVQsIGV2YWw9VCxtZXNzYWdlPUYsZmlnLmFsaWduPSdjZW50ZXInLGZpZy5jYXA9InJlbGF0aW9uc2hpcCBzY2F0dGVycGxvdHMiLCBvdXQud2lkdGggPSAnMTAwJSd9DQpsaWJyYXJ5KGdncGxvdDIpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGRhdGEgdmlzdWFsaXphdGlvbg0KbGlicmFyeShocmJydGhlbWVzKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBmb3IgdGhlIGB0aGVtZV9pcHN1bSgpYCBhbmQgbGVnZW5kDQojIENyZWF0ZSBkYXRhDQpkMSA8LSBkYXRhLmZyYW1lKHg9c2VxKDEsMTAwKSwgDQogICAgICAgICAgICAgICAgIHk9cm5vcm0oMTAwKSwgDQogICAgICAgICAgICAgICAgIG5hbWU9Ik5vIHRyZW5kIikNCmQyIDwtIGQxICU+JSANCm11dGF0ZSh5PXgqMTAgKyBybm9ybSgxMDAsc2Q9NjApKSAlPiUNCm11dGF0ZShuYW1lPSJMaW5lYXIgcmVsYXRpb25zaGlwIikNCmQzIDwtIGQxICU+JQ0KbXV0YXRlKHk9eF4yICsgcm5vcm0oMTAwLHNkPTE0MCkpICU+JQ0KbXV0YXRlKG5hbWU9IlNxdWFyZSIpDQpkNCA8LSBkYXRhLmZyYW1lKCB4PXNlcSgxLDEwLDAuMSksIA0KICAgICAgICAgICAgICAgICAgeT1zaW4oc2VxKDEsMTAsMC4xKSkgKyANCiAgICAgICAgICAgICAgICAgICAgcm5vcm0oOTEsc2Q9MC42KSkgJT4lIA0KbXV0YXRlKG5hbWU9IlNpbiIpDQpkb24gPC0gZG8uY2FsbChyYmluZCwgbGlzdChkMSwgZDIsIGQzLCBkNCkpDQojIFBsb3QNCmRvbiAlPiUNCiAgZ2dwbG90KGFlcyh4PXgsIHk9eSkpICsNCiAgICBnZW9tX3BvaW50KGNvbG9yPSIjNjliM2EyIiwgYWxwaGE9MC44KSArDQogICAgdGhlbWVfaXBzdW0oKSArDQogICAgZmFjZXRfd3JhcCh+bmFtZSwgc2NhbGU9ImZyZWUiKQ0KYGBgDQoNClRhbXBpbGFuIHBhbGluZyBzZWRlcmhhbmEgZGFyaSBkdWEgdmFyaWFiZWwga3VhbnRpdGF0aWYgYWRhbGFoIHNlYmFyLCBkZW5nYW4gc2V0aWFwIHZhcmlhYmVsIGRpd2FraWxpIHBhZGEgc3VtYnUuIE1pc2FsbnlhLCBtZW5nZ3VuYWthbiBkYXRhc2V0IEdhamksIGtpdGEgZGFwYXQgbWVyZW5jYW5ha2FuIHBlbmdhbGFtYW4gKHR5cnMuc2luY2UucGhkKSB2cy4gZ2FqaSBha2FkZW1payAoZ2FqaSkgdW50dWsgW2NvbGxlZ2UgUHJvZmVzc29yc10oaHR0cHM6Ly9ib29rZG93bi5vcmcvQmFrdGlTaXJlZ2FyL2RhdGEtc2NpZW5jZS1mb3ItYmVnaW5uZXJzL2RhdGFzZXRzLmh0bWwjYWNhZGVtaWMtc2FsYXJpZXMpLg0KDQpgYGB7ciBWaXN1YWxpemF0aW9uMjUsIG1lc3NhZ2U9RkFMU0UsIGVjaG89VCxmaWcuYWxpZ249J2NlbnRlcicsZmlnLmNhcD0iU2NhdHRlcnBsb3QgMSIsIG91dC53aWR0aCA9ICcxMDAlJ30NCmxpYnJhcnkoZ2dwbG90MikgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBmb3IgdmlzdWFsaXphdGlvbg0KbGlicmFyeShzY2FsZXMpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGF1dG9tYXRpY2FsbHkgZGV0ZXJtaW5pbmcgYnJlYWtzL2xhYmVscyANCmRhdGEoU2FsYXJpZXMsIHBhY2thZ2U9ImNhckRhdGEiKQ0KIyBlbmhhbmNlZCBzY2F0dGVyIHBsb3QNCmdncGxvdChTYWxhcmllcywgDQogICAgICAgYWVzKHggPSB5cnMuc2luY2UucGhkLCANCiAgICAgICAgICAgeSA9IHNhbGFyeSkpICsNCiAgZ2VvbV9wb2ludChjb2xvcj0iY29ybmZsb3dlcmJsdWUiLCANCiAgICAgICAgICAgICBzaXplID0gMiwgDQogICAgICAgICAgICAgYWxwaGE9LjgpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVsID0gc2NhbGVzOjpkb2xsYXIsIA0KICAgICAgICAgICAgICAgICAgICAgbGltaXRzID0gYyg1MDAwMCwgMjUwMDAwKSkgKw0KICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDYwLCAxMCksIA0KICAgICAgICAgICAgICAgICAgICAgbGltaXRzPWMoMCwgNjApKSArDQogIHRoZW1lX21pbmltYWwoKSArICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgdXNlIGEgbWluaW1hbCB0aGVtZQ0KICBsYWJzKHggPSAiWWVhcnMgU2luY2UgUGhEIiwNCiAgICAgICB5ID0gIiIsDQogICAgICAgdGl0bGUgPSAiRXhwZXJpZW5jZSB2cy4gU2FsYXJ5IiwNCiAgICAgICBzdWJ0aXRsZSA9ICI5LW1vbnRoIHNhbGFyeSBmb3IgMjAwOC0yMDA5IikNCmBgYA0KDQoqKkNhdGF0YW46Kiogb3BzaSBgZ2VvbV9wb2ludGAgZGFwYXQgZGlndW5ha2FuIHVudHVrIG1lbmd1YmFoDQoNCiogYHdhcm5hYCAtIHdhcm5hIHRpdGlrDQoqIGB1a3VyYW5gIC0gdWt1cmFuIHBvaW4NCiogYGJlbnR1a2AgLSBiZW50dWsgdGl0aWsNCiogYGFscGhhYCAtIHRyYW5zcGFyYW5zaSB0aXRpay4gVHJhbnNwYXJhbnNpIGJlcmtpc2FyIGRhcmkgMCAodHJhbnNwYXJhbikgaGluZ2dhIDEgKGJ1cmFtKSwgZGFuIG1lcnVwYWthbiBwYXJhbWV0ZXIgeWFuZyBiZXJndW5hIGtldGlrYSB0aXRpayB0dW1wYW5nIHRpbmRpaC4NCg0KRnVuZ3NpIGBzY2FsZV94X2NvbnRpbnVvdXNgIGRhbiAnc2NhbGVfeV9jb250aW51b3VzJyBtZW5nb250cm9sIHBlbnNrYWxhYW4gcGFkYSBzdW1idSB4IGRhbiB5IG1hc2luZy1tYXNpbmcuIEtpdGEgZGFwYXQgbWVuZ2d1bmFrYW4gb3BzaSBkYW4gZnVuZ3NpIGluaSB1bnR1ayBtZW1idWF0IHBsb3Qgc2ViYXIgeWFuZyBsZWJpaCBtZW5hcmlrLg0KDQojIyMgR2FyaXMgUGFzIFNlYmFyIA0KDQpTZXJpbmdrYWxpIGJlcmd1bmEgdW50dWsgbWVyaW5na2FzIGh1YnVuZ2FuIHlhbmcgZGl0YW1waWxrYW4gZGFsYW0gc2NhdHRlcnBsb3QsIG1lbmdndW5ha2FuIGdhcmlzIHBhbGluZyBwYXMuIEJhbnlhayBqZW5pcyBnYXJpcyB5YW5nIGRpZHVrdW5nLCB0ZXJtYXN1ayBsaW5lYXIsIHBvbGlub21pYWwsIGRhbiBub25wYXJhbWV0cmljIChsb2VzcykuIFNlY2FyYSBkZWZhdWx0LCBiYXRhcyBrZXBlcmNheWFhbiA5NSUgdW50dWsgZ2FyaXMtZ2FyaXMgaW5pIGRpdGFtcGlsa2FuLg0KDQpgYGB7ciBWaXN1YWxpemF0aW9uMjYsIG1lc3NhZ2U9RkFMU0UsIGVjaG89VCxmaWcuYWxpZ249J2NlbnRlcicsZmlnLmNhcD0iU2NhdHRlcnBsb3QgTGluZWFyIiwgb3V0LndpZHRoID0gJzEwMCUnfQ0KbGlicmFyeShnZ3Bsb3QyKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGZvciB2aXN1YWxpemF0aW9uDQpnZ3Bsb3QoU2FsYXJpZXMsDQogICAgICAgYWVzKHggPSB5cnMuc2luY2UucGhkLCANCiAgICAgICAgICAgeSA9IHNhbGFyeSkpICsNCiAgZ2VvbV9wb2ludChjb2xvcj0gImNvcm5mbG93ZXJibHVlIikgKw0KICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBjb2xvciA9ICJicm93bjEiKSsNCiAgdGhlbWVfbWluaW1hbCgpICsgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB1c2UgYSBtaW5pbWFsIHRoZW1lDQogIGxhYnMoeCA9ICJZZWFycyBTaW5jZSBQaEQiLA0KICAgICAgIHkgPSAiIiwNCiAgICAgICB0aXRsZSA9ICJFeHBlcmllbmNlIHZzLiBTYWxhcnkiLA0KICAgICAgIHN1YnRpdGxlID0gIjktbW9udGggc2FsYXJ5IGZvciAyMDA4LTIwMDkiKQ0KYGBgDQoNCllhbmcgamVsYXMsIGdhamkgbWVuaW5na2F0IGRlbmdhbiBwZW5nYWxhbWFuLiBOYW11biwgdGFtcGFrbnlhIGFkYSBwZW51cnVuYW4gZGkgdWp1bmcgeWFuZyB0ZXBhdCAtIHByb2Zlc29yIGRlbmdhbiBwZW5nYWxhbWFuIHlhbmcgc2lnbmlmaWthbiwgbWVuZGFwYXRrYW4gZ2FqaSB5YW5nIGxlYmloIHJlbmRhaC4gR2FyaXMgbHVydXMgdGlkYWsgbWVuYW5na2FwIGVmZWsgbm9uLWxpbmVhciBpbmkuIFNlYnVhaCBnYXJpcyBkZW5nYW4gdGlrdW5nYW4gYWthbiBsZWJpaCBjb2NvayBkaSBzaW5pLg0KDQpHYXJpcyByZWdyZXNpIHBvbGlub21pYWwgbWVueWVkaWFrYW4gZ2FyaXMgcGFzIGRhcmkgZm9ybXVsaXINCg0KXGJlZ2lue2VxdWF0aW9ufQ0KICBcbGFiZWx7ZXE6MX0NClxoYXR7eX09XGJldGFfMCtcYmV0YV8xeCtcYmV0YV8yeF4yK1xjZG90cytcYmV0YV9ueF4yDQpcZW5ke2VxdWF0aW9ufQ0KDQpCaWFzYW55YSBnYXJpcyBrdWFkcmF0IChzYXR1IHRpa3VuZ2FuKSwgYXRhdSBrdWJpayAoZHVhIHRpa3VuZ2FuKSBkaWd1bmFrYW4uIEphcmFuZyBwZXJsdSBtZW5nZ3VuYWthbiBwZXNhbmFuIHlhbmcgbGViaWggdGluZ2dpICQoID4zICkkIHBvbGlub21pYWwuIE1lbmVyYXBrYW4gcGFzIGt1YWRyYXQga2Uga3VtcHVsYW4gZGF0YSBnYWppIG1lbmdoYXNpbGthbiBoYXNpbCBiZXJpa3V0Lg0KDQpgYGB7ciBWaXN1YWxpemF0aW9uMjcsIG1lc3NhZ2U9RkFMU0UsIGVjaG89VCxmaWcuYWxpZ249J2NlbnRlcicsZmlnLmNhcD0iU2NhdHRlcnBsb3QgUXVhZHJhdGljIiwgb3V0LndpZHRoID0gJzEwMCUnfQ0KbGlicmFyeShnZ3Bsb3QyKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGZvciB2aXN1YWxpemF0aW9uDQpnZ3Bsb3QoU2FsYXJpZXMsIA0KICAgICAgIGFlcyh4ID0geXJzLnNpbmNlLnBoZCwgDQogICAgICAgICAgIHkgPSBzYWxhcnkpKSArDQogIGdlb21fcG9pbnQoY29sb3I9ICJjb3JuZmxvd2VyYmx1ZSIpICsNCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgDQogICAgICAgICAgICAgIGZvcm11bGEgPSB5IH4gcG9seSh4LCAyKSwgDQogICAgICAgICAgICAgIGNvbG9yID0gInllbGxvdyIpKw0KICB0aGVtZV9taW5pbWFsKCkgKyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHVzZSBhIG1pbmltYWwgdGhlbWUNCiAgbGFicyh4ID0gIlllYXJzIFNpbmNlIFBoRCIsDQogICAgICAgeSA9ICIiLA0KICAgICAgIHRpdGxlID0gIkV4cGVyaWVuY2UgdnMuIFNhbGFyeSIsDQogICAgICAgc3VidGl0bGUgPSAiOS1tb250aCBzYWxhcnkgZm9yIDIwMDgtMjAwOSIpDQpgYGANCg0KQWtoaXJueWEsIGdhcmlzIGZpdCBub25wYXJhbWV0cmljIHlhbmcgZGloYWx1c2thbiBzZXJpbmcgZGFwYXQgbWVtYmVyaWthbiBnYW1iYXJhbiB5YW5nIGJhaWsgdGVudGFuZyBodWJ1bmdhbi4gRGVmYXVsdCBkYWxhbSBnZ3Bsb3QyIGFkYWxhaCBbbG9lc3NdKGh0dHBzOi8vd3d3LmltZS51bmljYW1wLmJyL35kaWFzL2xvZXNzLnBkZikgZ2FyaXMgeWFuZyBtZXJ1cGFrYW4gc2luZ2thdGFuIGRhcmkgcGVtaGFsdXNhbiBzZWJhYmFyIHRlcnRpbWJhbmcgbG9rYWwuDQoNCg0KYGBge3IgVmlzdWFsaXphdGlvbjI4LCBtZXNzYWdlPUZBTFNFLCBlY2hvPVQsZmlnLmFsaWduPSdjZW50ZXInLGZpZy5jYXA9IlNjYXR0ZXJwbG90IFNtb290aGVkIE5vbnBhcmFtZXRyaWMiLCBvdXQud2lkdGggPSAnMTAwJSd9DQpsaWJyYXJ5KGdncGxvdDIpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgZm9yIHZpc3VhbGl6YXRpb24NCmdncGxvdChTYWxhcmllcywgDQogICAgICAgYWVzKHggPSB5cnMuc2luY2UucGhkLCANCiAgICAgICAgICAgeSA9IHNhbGFyeSkpICsNCiAgZ2VvbV9wb2ludChjb2xvcj0iY29ybmZsb3dlcmJsdWUiLCANCiAgICAgICAgICAgICBzaXplID0gMiwgDQogICAgICAgICAgICAgYWxwaGEgPSAxKSArDQogIGdlb21fc21vb3RoKHNpemUgPSAxLA0KICAgICAgICAgICAgICBjb2xvciA9ICJncmVlbiIpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVsID0gc2NhbGVzOjpkb2xsYXIsIA0KICAgICAgICAgICAgICAgICAgICAgbGltaXRzID0gYyg1MDAwMCwgMjUwMDAwKSkgKw0KICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDYwLCAxMCksIA0KICAgICAgICAgICAgICAgICAgICAgbGltaXRzID0gYygwLCA2MCkpICsNCiAgdGhlbWVfbWluaW1hbCgpICsgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB1c2UgYSBtaW5pbWFsIHRoZW1lDQogIGxhYnMoeCA9ICJZZWFycyBTaW5jZSBQaEQiLA0KICAgICAgIHkgPSAiIiwNCiAgICAgICB0aXRsZSA9ICJFeHBlcmllbmNlIHZzLiBTYWxhcnkiLA0KICAgICAgIHN1YnRpdGxlID0gIjktbW9udGggc2FsYXJ5IGZvciAyMDA4LTIwMDkiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KYGBgDQoNCiMjIEthdGVnb3JpcyB2cy4gS29udGludQ0KDQpTYWF0IG1lcmVuY2FuYWthbiBodWJ1bmdhbiBhbnRhcmEgdmFyaWFiZWwga2F0ZWdvcmlzIGRhbiB2YXJpYWJlbCBrdWFudGl0YXRpZiwgc2VqdW1sYWggYmVzYXIgamVuaXMgZ3JhZmlrIHRlcnNlZGlhLiBJbmkgdGVybWFzdWsgYmFnYW4gYmF0YW5nIG1lbmdndW5ha2FuIHN0YXRpc3RpayByaW5na2FzYW4sIHBsb3Qga2VwYWRhdGFuIGtlcm5lbCB5YW5nIGRpa2Vsb21wb2trYW4sIHBsb3Qga290YWsgYmVyZGFtcGluZ2FuLCBwbG90IGJpb2xhIGJlcmRhbXBpbmdhbiwgcGxvdCByYXRhLXJhdGEgLyBzZW0sIHBsb3QgcHVuZ2d1bmcgYnVraXQsIGRhbiBwbG90IENsZXZlbGFuZC4NCg0KIyMjIEJhZ2FuIEJhdGFuZw0KDQpEaSBiYWdpYW4gc2ViZWx1bW55YSwgYmFnYW4gYmF0YW5nIGRpZ3VuYWthbiB1bnR1ayBtZW5hbXBpbGthbiBqdW1sYWgga2FzdXMgbWVudXJ1dCBrYXRlZ29yaSB1bnR1ayBzYXR1IHZhcmlhYmVsIGF0YXUgdW50dWsgZHVhIHZhcmlhYmVsLiBBbmRhIGp1Z2EgZGFwYXQgbWVuZ2d1bmFrYW4gYmFnYW4gYmF0YW5nIHVudHVrIG1lbmFtcGlsa2FuIHN0YXRpc3RpayByaW5na2FzYW4gbGFpbm55YSAobWlzYWxueWEsIHNhcmFuYSBhdGF1IG1lZGlhbikgcGFkYSB2YXJpYWJlbCBrdWFudGl0YXRpZiB1bnR1ayBzZXRpYXAgdGluZ2thdCB2YXJpYWJlbCBrYXRlZ29yaXMuDQoNCk1pc2FsbnlhLCBncmFmaWsgYmVyaWt1dCBtZW5hbXBpbGthbiBnYWppIHJhdGEtcmF0YSB1bnR1ayBzYW1wZWwgcHJvZmVzb3IgdW5pdmVyc2l0YXMgYmVyZGFzYXJrYW4gcGVyaW5na2F0IGFrYWRlbWlrIG1lcmVrYS4NCg0KYGBge3IgVmlzdWFsaXphdGlvbjI5LCBtZXNzYWdlPUZBTFNFLCBlY2hvPVQsZmlnLmFsaWduPSdjZW50ZXInLGZpZy5jYXA9IkJhciBDaGFydCAoU3VtbWFyeSBzdGF0aXN0aWNzKSIsIG91dC53aWR0aCA9ICcxMDAlJ30NCmxpYnJhcnkoZHBseXIpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBmb3IgZGF0YSBtYW5pcHVsYXRpb24NCmxpYnJhcnkoZ2dwbG90MikgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBmb3IgdmlzdWFsaXphdGlvbg0KbGlicmFyeShzY2FsZXMpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGF1dG9tYXRpY2FsbHkgZGV0ZXJtaW5pbmcgYnJlYWtzL2xhYmVscyANCmRhdGEoU2FsYXJpZXMsIHBhY2thZ2U9ImNhckRhdGEiKQ0KIyBjYWxjdWxhdGUgbWVhbiBzYWxhcnkgZm9yIGVhY2ggcmFuaw0KcGxvdGRhdGEgPC0gU2FsYXJpZXMgJT4lDQogIGdyb3VwX2J5KHJhbmspICU+JQ0KICBkcGx5cjo6c3VtbWFyaXplKG1lYW5fc2FsYXJ5ID0gbWVhbihzYWxhcnkpKQ0KIyBwbG90IG1lYW4gc2FsYXJpZXMgaW4gYSBtb3JlIGF0dHJhY3RpdmUgZmFzaGlvbg0KbXljb2xzIDwtIGMoIiNDRDUzNENGRiIsICIjRUZDMDAwRkYiLCAiIzAwNzNDMkZGIikNCmdncGxvdChwbG90ZGF0YSwgDQogICAgICAgYWVzKHggPSBmYWN0b3IocmFuaywNCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJBc3Npc3RhbnRcblByb2Zlc3NvciIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQXNzb2NpYXRlXG5Qcm9mZXNzb3IiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkZ1bGxcblByb2Zlc3NvciIpKSwgDQogICAgICAgICAgICAgICAgICAgICAgeSA9IG1lYW5fc2FsYXJ5KSkgKw0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgDQogICAgICAgICAgIGZpbGwgPSBteWNvbHMpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IGRvbGxhcihtZWFuX3NhbGFyeSkpLCANCiAgICAgICAgICAgIHZqdXN0ID0gLTAuMjUpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCAxMzAwMDAsIDIwMDAwKSwgDQogICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IGRvbGxhcikgKw0KICB0aGVtZV9taW5pbWFsKCkgKyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHVzZSBhIG1pbmltYWwgdGhlbWUNCiAgbGFicyh0aXRsZSA9ICJNZWFuIFNhbGFyeSBieSBSYW5rIiwgDQogICAgICAgc3VidGl0bGUgPSAiOS1tb250aCBhY2FkZW1pYyBzYWxhcnkgZm9yIDIwMDgtMjAwOSIsDQogICAgICAgeCA9ICIiLA0KICAgICAgIHkgPSAiIikNCmBgYA0KDQojIyMgUGxvdCBLZXBhZGF0YW4gS2VybmVsIFlhbmcgRGlrZWxvbXBva2thbg0KDQpTZXNlb3JhbmcgZGFwYXQgbWVtYmFuZGluZ2thbiBncnVwIHBhZGEgdmFyaWFiZWwgbnVtZXJpayBkZW5nYW4gbWVuZ2dhbnRpIHBsb3QgW2tlcGFkYXRhbiBrZXJuZWxdKCkgZGFsYW0gc2F0dSBncmFmaWsuIE1hcmkga2l0YSBwbG90IGRpc3RyaWJ1c2kgZ2FqaSBiZXJkYXNhcmthbiBwZXJpbmdrYXQgbWVuZ2d1bmFrYW4gcGxvdCBrZXBhZGF0YW4ga2VybmVsLg0KDQpgYGB7ciBWaXN1YWxpemF0aW9uMzAsIG1lc3NhZ2U9RkFMU0UsIGVjaG89VCxmaWcuYWxpZ249J2NlbnRlcicsZmlnLmNhcD0iR3JvdXBlZCBLZXJuZWwgRGVuc2l0eSBQbG90cyIsIG91dC53aWR0aCA9ICcxMDAlJ30NCmdncGxvdChTYWxhcmllcywgDQogICAgICAgYWVzKHggPSBzYWxhcnksIA0KICAgICAgICAgICBmaWxsID0gcmFuaykpICsNCiAgZ2VvbV9kZW5zaXR5KGFscGhhID0gMC40KSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIGxhYnModGl0bGUgPSAiU2FsYXJ5IGRpc3RyaWJ1dGlvbiBieSByYW5rIikNCmBgYA0KDQpPcHNpIGBhbGZhYCBtZW1idWF0IHBsb3Qga2VwYWRhdGFuIHNlYmFnaWFuIHRyYW5zcGFyYW4gc2VoaW5nZ2Ega2l0YSBkYXBhdCBtZWxpaGF0IGFwYSB5YW5nIHRlcmphZGkgZGkgYmF3YWggdHVtcGFuZyB0aW5kaWguIE5pbGFpIGFsZmEgYmVya2lzYXIgZGFyaSAwICh0cmFuc3BhcmFuKSBoaW5nZ2EgMSAoYnVyYW0pLiBHcmFmaWsgbWVuamVsYXNrYW4gYmFod2EsIHNlY2FyYSB1bXVtLCBnYWppIG5haWsgZGVuZ2FuIHBhbmdrYXQuIE5hbXVuLCBraXNhcmFuIGdhamkgdW50dWsgcHJvZmVzb3IgcGVudWggc2FuZ2F0IGx1YXMuDQoNCiMjIyBQbG90IEtvdGFrDQoNClBsb3Qga290YWsgbWVuYW1waWxrYW4gcGVyc2VudGlsICQgMjUgXnt0aH0kICwgbWVkaWFuLCBkYW4gcGVyc2VudGlsICQ3NV57dGh9JCBkYXJpIHNlYnVhaCBkaXN0cmlidXNpLiBLdW1pcyAoZ2FyaXMgdmVydGlrYWwpIG1lbmFuZ2thcCBzZWtpdGFyIDk5JSBkYXJpIGRpc3RyaWJ1c2kgbm9ybWFsLCBkYW4gcGVuZ2FtYXRhbiBkaSBsdWFyIHJlbnRhbmcgaW5pIGRpcGxvdCBzZWJhZ2FpIHRpdGlrIHlhbmcgbWV3YWtpbGkgb3V0bGllciAobGloYXQgZ2FtYmFyIGRpIGJhd2FoKQ0KDQpgYGB7ciBCb3hwbG90LCBlY2hvPUZBTFNFLGZpZy5hbGlnbj0nY2VudGVyJyxmaWcuY2FwPSJCb3ggUGxvdHMiLCBvdXQud2lkdGggPSAnMTAwJSd9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiQzovTmlraXRhL1R1Z2FzL0RhdGEgU3RydWN0dXRlcyBhbmQgQWxnb3JpdGhtcy9XRUVLIDEzL2JveHBsb3QucG5nIikNCmBgYA0KDQpQbG90IGtvdGFrIGJlcmRhbXBpbmdhbiBzYW5nYXQgYmVyZ3VuYSB1bnR1ayBtZW1iYW5kaW5na2FuIGdydXAgKHlhaXR1LCB0aW5na2F0IHZhcmlhYmVsIGthdGVnb3JpcykgcGFkYSB2YXJpYWJlbCBudW1lcmlrLiBNYXJpIGtpdGEgcGxvdCBkaXN0cmlidXNpIGdhamkgYmVyZGFzYXJrYW4gcGVyaW5na2F0IG1lbmdndW5ha2FuIGJveC1wbG90LiBQbG90IGtvdGFrIGJlcmFuZy1iZXJhbmcgbWVtYmVyaWthbiBtZXRvZGUgcGVya2lyYWFuIHVudHVrIG1lbXZpc3VhbGlzYXNpa2FuIGFwYWthaCBncnVwIGJlcmJlZGEuIE1lc2tpcHVuIGJ1a2FuIHRlcyBmb3JtYWwsIGppa2EgdGFraWsgZHVhIHBsb3Qga290YWsgdGlkYWsgdHVtcGFuZyB0aW5kaWgsIGFkYSBidWt0aSBrdWF0IChrZXBlcmNheWFhbiA5NSUpIGJhaHdhIG1lZGlhbiBkYXJpIGR1YSBrZWxvbXBvayBiZXJiZWRhLg0KDQpgYGB7ciBWaXN1YWxpemF0aW9uMzEsIG1lc3NhZ2U9RkFMU0UsIGVjaG89VCxmaWcuYWxpZ249J2NlbnRlcicsZmlnLmNhcD0iQm94IFBsb3RzIiwgb3V0LndpZHRoID0gJzEwMCUnfQ0KbXljb2xzIDwtIGMoIiNDRDUzNENGRiIsICIjRUZDMDAwRkYiLCAiIzAwNzNDMkZGIikNCmdncGxvdChTYWxhcmllcywgYWVzKHggPSByYW5rLCANCiAgICAgICAgICAgICAgICAgICAgIHkgPSBzYWxhcnkpKSArDQogIGdlb21fYm94cGxvdChub3RjaCA9IFRSVUUsIA0KICAgICAgICAgICAgICAgZmlsbCA9IG15Y29scywgDQogICAgICAgICAgICAgICBhbHBoYSA9IC43KSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIGxhYnModGl0bGUgPSAiU2FsYXJ5IERpc3RyaWJ1dGlvbiBieSByYW5rIikNCmBgYA0KDQpEYWxhbSBjb250b2ggZGkgYXRhcywga2V0aWdhIGdydXAgdGFtcGFrIGJlcmJlZGEuIFNhbGFoIHNhdHUga2V1bmdndWxhbiBib3hwbG90IGFkYWxhaCBsZWJhcm55YSBiaWFzYW55YSB0aWRhayBiZXJhcnRpLiBJbmkgbWVtdW5na2lua2FuIEFuZGEgdW50dWsgbWVtYmFuZGluZ2thbiBkaXN0cmlidXNpIGJhbnlhayBncnVwIGRhbGFtIHNhdHUgZ3JhZmlrLg0KDQojIyMgUGxvdCBCaW9sYQ0KDQpQbG90IGJpb2xhIG1pcmlwIGRlbmdhbiBwbG90IFtrZXBhZGF0YW4ga2VybmVsXSgpIHRldGFwaSBkaWNlcm1pbmthbiBkYW4gZGlwdXRhciAkIDkwIF4gMCAkLiBNYXJpIGtpdGEgcGxvdCBkaXN0cmlidXNpIGdhamkgYmVyZGFzYXJrYW4gcGVyaW5na2F0IG1lbmdndW5ha2FuIHBsb3QgYmlvbGEuDQoNCmBgYHtyIFZpc3VhbGl6YXRpb24zMiwgbWVzc2FnZT1GQUxTRSwgZWNobz1ULGZpZy5hbGlnbj0nY2VudGVyJyxmaWcuY2FwPSJWaW9saW4gUGxvdHMiLCBvdXQud2lkdGggPSAnMTAwJSd9DQpnZ3Bsb3QoU2FsYXJpZXMsIA0KICAgICAgIGFlcyh4ID0gcmFuaywgDQogICAgICAgICAgIHkgPSBzYWxhcnkpKSArDQogIGdlb21fdmlvbGluKGZpbGwgPSAiYXp1cmUxIikgKw0KICBnZW9tX2JveHBsb3Qod2lkdGggPSAuMiwgDQogICAgICAgICAgICAgICBmaWxsID0gbXljb2xzLA0KICAgICAgICAgICAgICAgb3V0bGllci5jb2xvciA9ICJyZWQiLA0KICAgICAgICAgICAgICAgb3V0bGllci5zaXplID0gMikgKyANCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgbGFicyh0aXRsZSA9ICJTYWxhcnkgZGlzdHJpYnV0aW9uIGJ5IHJhbmsiKQ0KYGBgDQoNCiMjIyBQbG90IFJpZGdlbGluZQ0KDQpQbG90IHB1bmdndW5nIGJ1a2l0IChqdWdhIGRpc2VidXQgcGxvdCBzdWthY2l0YSkgbWVuYW1waWxrYW4gZGlzdHJpYnVzaSB2YXJpYWJlbCBrdWFudGl0YXRpZiB1bnR1ayBiZWJlcmFwYSBrZWxvbXBvay4gTWVyZWth4oCZcmUgbWlyaXAgZGVuZ2FuIHBsb3QgW2tlcGFkYXRhbiBrZXJuZWxdKCkgZGVuZ2FuIHZlcnRpa2FsIFtmYWNldGluZ10oKSwgdGV0YXBpIG1lbmdhbWJpbCBsZWJpaCBzZWRpa2l0IHJ1YW5nLiBQbG90IFJpZGdlbGluZSBkaWJ1YXQgZGVuZ2FuIHBha2V0IGBnZ3JpZGdlc2AuDQoNCk1lbmdndW5ha2FuW0Z1ZWwgZWNvbm9teV0oaHR0cHM6Ly9ib29rZG93bi5vcmcvQmFrdGlTaXJlZ2FyL2RhdGEtc2NpZW5jZS1mb3ItYmVnaW5uZXJzL2RhdGFzZXRzLmh0bWwjZnVlbC1lY29ub215LWRhdGEpIGRhdGFzZXQsIE1hcmkga2l0YSBwbG90IGRpc3RyaWJ1c2kga290YSBtZW5nZW11ZGkgbWlsIHBlciBnYWxvbiBvbGVoIGtlbGFzIG1vYmlsLiANCg0KYGBge3IgVmlzdWFsaXphdGlvbjMzLCBtZXNzYWdlPUZBTFNFLCBlY2hvPVQsZmlnLmFsaWduPSdjZW50ZXInLGZpZy5jYXA9IlJpZGdlbGluZSBQbG90cyIsIG91dC53aWR0aCA9ICcxMDAlJ30NCmxpYnJhcnkoZHBseXIpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBmb3IgZGF0YSBtYW5pcHVsYXRpb24NCmxpYnJhcnkoZ2dwbG90MikgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBmb3IgdmlzdWxpemF0aW9uDQpsaWJyYXJ5KGdncmlkZ2VzKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgdG8gaGFuZGxlIG92ZXJsYXBwaW5nIHZpc3VsaXphdGlvbg0KZ2dwbG90KG1wZywgDQogICAgICAgYWVzKHggPSBjdHksIA0KICAgICAgICAgICB5ID0gY2xhc3MsIA0KICAgICAgICAgICBmaWxsID0gY2xhc3MpKSArDQogIGdlb21fZGVuc2l0eV9yaWRnZXMoYWxwaGEgPSAwLjcpICsgDQogIHRoZW1lX3JpZGdlcygpICsNCiAgbGFicygiSGlnaHdheSBtaWxlYWdlIGJ5IGF1dG8gY2xhc3MiKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikNCmBgYA0KDQpTYXlhIHRlbGFoIG1lbmVrYW4gbGVnZW5kYSBkaSBzaW5pIGthcmVuYSBiZXJsZWJpaGFuIChkaXN0cmlidXNpIHN1ZGFoIGRpbGFiZWxpIHBhZGEgc3VtYnUgeSkuIFRpZGFrIG1lbmdoZXJhbmthbiwgdHJ1ayBwaWNrdXAgbWVtaWxpa2kgamFyYWsgdGVtcHVoIHRlcm1pc2tpbiwgc2VtZW50YXJhIHN1YmtvbXB1dGVyIGRhbiBtb2JpbCBrb21wYWsgY2VuZGVydW5nIG1lbmNhcGFpIHBlcmluZ2thdC4gTmFtdW4sIGFkYSBiZXJiYWdhaSBza29yIGphcmFrIHRlbXB1aCBnYXMgdW50dWsgbW9iaWwtbW9iaWwga2VjaWwgaW5pLg0KDQpQZXJoYXRpa2FuIGtlbXVuZ2tpbmFuIHR1bXBhbmcgdGluZGloIGRpc3RyaWJ1c2kgYWRhbGFoIHRyYWRlLW9mZiB1bnR1ayBncmFmaWsgeWFuZyBsZWJpaCByaW5na2FzLiBBbmRhIGRhcGF0IG1lbmFtYmFoa2FuIHRyYW5zcGFyYW5zaSBqaWthIHR1bXBhbmcgdGluZGloIHBhcmFoIG1lbmdndW5ha2FuIGdlb21fZGVuc2l0eV9yaWRnZXMoYWxmYSA9IG4pLCBkaSBtYW5hIG4gYmVya2lzYXIgYW50YXJhIDAgKHRyYW5zcGFyYW4pIGhpbmdnYSAxIChidXJhbSkuIExpaGF0IFtwYWNrYWdlIHZpbmduZXR0ZV0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL2dncmlkZ2VzL3ZpZ25ldHRlcy9pbnRyb2R1Y3Rpb24uaHRtbCkgdW50dWsgZGV0YWlscy4NCg0KIyMjIFBsb3QgQmFyaXMNCg0KTWV0b2RlIHBvcHVsZXIgdW50dWsgbWVtYmFuZGluZ2thbiBncnVwIHBhZGEgdmFyaWFiZWwgbnVtZXJpayBhZGFsYWggcGxvdCByYXRhLXJhdGEgZGVuZ2FuIGJpbGFoIGtlc2FsYWhhbi4gQmlsYWgga2VzYWxhaGFuIGRhcGF0IG1ld2FraWxpIHNpbXBhbmdhbiBiYWt1LCBrZXNhbGFoYW4gc3RhbmRhciByYXRhLXJhdGEsIGF0YXUgaW50ZXJ2YWwga2VwZXJjYXlhYW4gZGlyaS4gRGkgYmFnaWFuIGluaSwgc2FyYW5hIHBsb3QgeWFuZyBiYWlrIGRhbiBrZXNhbGFoYW4gc3RhbmRhci4gS2l0YSBkYXBhdCBtZW5nZ3VuYWthbiB0ZWtuaWsgeWFuZyBzYW1hIHVudHVrIG1lbWJhbmRpbmdrYW4gZ2FqaSBkaSBzZWx1cnVoIHBhbmdrYXQgZGFuIGplbmlzIGtlbGFtaW4uIChTZWNhcmEgdGVrbmlzLCBpbmkgdGlkYWsgYml2YXJpYXRlIHNlamFrIGl0dSBtZXJlbmNhbmFrYW4gcGFuZ2thdCwgamVuaXMga2VsYW1pbiwgZGFuIGdhamksIHRldGFwaSB0YW1wYWtueWEgY29jb2sgZGkgc2luaSkuDQogDQpgYGB7ciBWaXN1YWxpemF0aW9uMzQsIG1lc3NhZ2U9RkFMU0UsIGVjaG89VCxmaWcuYWxpZ249J2NlbnRlcicsZmlnLmNhcD0iTGluZSBQbG90cyIsIG91dC53aWR0aCA9ICcxMDAlJ30NCmxpYnJhcnkoZHBseXIpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBmb3IgZGF0YSBtYW5pcHVsYXRpb24NCmxpYnJhcnkoZ2dwbG90MikgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBmb3IgdmlzdWxpemF0aW9uDQpsaWJyYXJ5KGdncmlkZ2VzKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgdG8gaGFuZGxlIG92ZXJsYXBwaW5nIHZpc3VsaXphdGlvbg0KIyBjYWxjdWxhdGUgbWVhbnMsIHN0YW5kYXJkIGRldmlhdGlvbnMsDQojIHN0YW5kYXJkIGVycm9ycywgYW5kIDk1JSBjb25maWRlbmNlIA0KIyBpbnRlcnZhbHMgYnkgcmFuaw0KcGxvdGRhdGEgPC0gU2FsYXJpZXMgJT4lDQogIGdyb3VwX2J5KHJhbmssIHNleCkgJT4lDQogIGRwbHlyOjpzdW1tYXJpemUobiA9IG4oKSwNCiAgICAgICAgICAgIG1lYW4gPSBtZWFuKHNhbGFyeSksDQogICAgICAgICAgICBzZCA9IHNkKHNhbGFyeSksDQogICAgICAgICAgICBzZSA9IHNkL3NxcnQobiksDQogICAgICAgICAgICBjaSA9IHF0KDAuOTc1LCBkZiA9IG4gLSAxKSAqIHNkIC8gc3FydChuKSkNCiMgaW1wcm92ZWQgbWVhbnMvc3RhbmRhcmQgZXJyb3IgcGxvdA0KcGQgPC0gcG9zaXRpb25fZG9kZ2UoMC4yKQ0KZ2dwbG90KHBsb3RkYXRhLCANCiAgICAgICBhZXMoeCA9IGZhY3RvcihyYW5rLCANCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJBc3Npc3RhbnRcblByb2Zlc3NvciIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQXNzb2NpYXRlXG5Qcm9mZXNzb3IiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkZ1bGxcblByb2Zlc3NvciIpKSwgDQogICAgICAgICAgIHkgPSBtZWFuLCANCiAgICAgICAgICAgZ3JvdXA9c2V4LCANCiAgICAgICAgICAgY29sb3I9c2V4KSkgKw0KICBnZW9tX3BvaW50KHBvc2l0aW9uPXBkLCANCiAgICAgICAgICAgICBzaXplID0gMykgKw0KICBnZW9tX2xpbmUocG9zaXRpb24gPSBwZCwgDQogICAgICAgICAgICBzaXplID0gMSkgKw0KICBnZW9tX2Vycm9yYmFyKGFlcyh5bWluID0gbWVhbiAtIHNlLCANCiAgICAgICAgICAgICAgICAgICAgeW1heCA9IG1lYW4gKyBzZSksIA0KICAgICAgICAgICAgICAgIHdpZHRoID0gLjEsIA0KICAgICAgICAgICAgICAgIHBvc2l0aW9uID0gcGQsIA0KICAgICAgICAgICAgICAgIHNpemUgPSAxKSArDQogIHNjYWxlX3lfY29udGludW91cyhsYWJlbCA9IHNjYWxlczo6ZG9sbGFyKSArDQogIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlPSJTZXQxIikgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICBsYWJzKHRpdGxlID0gIk1lYW4gc2FsYXJ5IGJ5IHJhbmsgYW5kIHNleCIsDQogICAgICAgc3VidGl0bGUgPSAiKG1lYW4gKy8tIHN0YW5kYXJkIGVycm9yKSIsDQogICAgICAgeCA9ICIiLCANCiAgICAgICB5ID0gIiIsDQogICAgICAgY29sb3IgPSAiR2VuZGVyIikNCmBgYA0KDQojIyMgUGxvdCBTdHJpcA0KDQpIdWJ1bmdhbiBhbnRhcmEgdmFyaWFiZWwgcGVuZ2Vsb21wb2thbiBkYW4gdmFyaWFiZWwgbnVtZXJpayBkYXBhdCBkaXRhbXBpbGthbiBkZW5nYW4gcGxvdCBzZWJhci4gTWlzYWxueWEsIHBsb3QgcGVtYmFnaWFuIGdhamkgYmVyZGFzYXJrYW4gcGVyaW5na2F0IG1lbmdndW5ha2FuIHBsb3Qgc3RyaXAuIFBsb3Qgc2ViYXIgc2F0dSBkaW1lbnNpIGluaSBkaXNlYnV0IHBsb3Qgc3RyaXAuIFNheWFuZ255YSwga2VsZWJpaGFuIGNldGFrIHBvaW4gbWVtYnVhdCBpbnRlcnByZXRhc2kgbWVuamFkaSBzdWxpdC4gSHVidW5nYW4gbGViaWggbXVkYWggdW50dWsgbWVsaWhhdCBhcGFrYWggcG9pbm55YSBnZWxpc2FoLiBQYWRhIGRhc2FybnlhLCBhbmdrYSBhY2FrIGtlY2lsIGRpdGFtYmFoa2FuIGtlIHNldGlhcCBrb29yZGluYXQgeS4gSnVnYSBsZWJpaCBtdWRhaCB1bnR1ayBtZW1iYW5kaW5na2FuIGtlbG9tcG9rIGppa2Ega2l0YSBtZW5nZ3VuYWthbiB3YXJuYS4NCg0KYGBge3IgVmlzdWFsaXphdGlvbjM1LCBtZXNzYWdlPUZBTFNFLCBlY2hvPVQsZmlnLmFsaWduPSdjZW50ZXInLGZpZy5jYXA9IlN0cmlwIFBsb3RzIiwgb3V0LndpZHRoID0gJzEwMCUnfQ0KbGlicmFyeShnZ3Bsb3QyKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGZvciB2aXN1bGl6YXRpb24NCmxpYnJhcnkoc2NhbGVzKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBzY2FsaW5nIGluZnJhc3RydWN0dXJlDQpnZ3Bsb3QoU2FsYXJpZXMsIA0KICAgICAgIGFlcyh5ID0gZmFjdG9yKHJhbmssDQogICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygiQXNzaXN0YW50XG5Qcm9mZXNzb3IiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkFzc29jaWF0ZVxuUHJvZmVzc29yIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJGdWxsXG5Qcm9mZXNzb3IiKSksIA0KICAgICAgICAgICB4ID0gc2FsYXJ5LCANCiAgICAgICAgICAgY29sb3IgPSByYW5rKSkgKw0KICBnZW9tX2ppdHRlcihhbHBoYSA9IDAuNywNCiAgICAgICAgICAgICAgc2l6ZSA9IDEuNSkgKyANCiAgc2NhbGVfeF9jb250aW51b3VzKGxhYmVsID0gZG9sbGFyKSArDQogIGxhYnModGl0bGUgPSAiQWNhZGVtaWMgU2FsYXJ5IGJ5IFJhbmsiLCANCiAgICAgICBzdWJ0aXRsZSA9ICI5LW1vbnRoIHNhbGFyeSBmb3IgMjAwOC0yMDA5IiwNCiAgICAgICB4ID0gIiIsDQogICAgICAgeSA9ICIiKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikNCmBgYA0KT3BzaSBgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiYCBkaWd1bmFrYW4gdW50dWsgbWVuZWthbiBsZWdlbmRhICh5YW5nIHRpZGFrIGRpcGVybHVrYW4gZGkgc2luaSkuIFBsb3Qgaml0dGVyZWQgYmVrZXJqYSBkZW5nYW4gYmFpayBrZXRpa2EganVtbGFoIHBvaW4gdGlkYWsgdGVybGFsdSBiZXNhci4NCg0KIyMjIEppdHRlciBkYW4gQm94cGxvdHMNCg0KTXVuZ2tpbiBsZWJpaCBtdWRhaCB1bnR1ayBtZW12aXN1YWxpc2FzaWthbiBkaXN0cmlidXNpIGppa2Ega2l0YSBtZW5hbWJhaGthbiBib3hwbG90IGtlIHBsb3Qgaml0dGVyLiBCZWJlcmFwYSBvcHNpIGRpdGFtYmFoa2FuIHVudHVrIG1lbWJ1YXQgcGxvdCBpbmkuDQoNCiogVW50dWsgYm94cGxvdDoNCiAgLSBgdWt1cmFuID0gMWAgbWVtYnVhdCBnYXJpcyBsZWJpaCB0ZWJhbA0KICAtIGBvdXRsaWVyLmNvbG9yID0gImhpdGFtImAgbWVtYnVhdCBvdXRsaWVyIGhpdGFtDQogIC0gYG91dGxpZXIuc2hhcGUgPSAxYCBtZW5lbnR1a2FuIGxpbmdrYXJhbiB1bnR1ayBvdXRsaWVyDQogIC0gYG91dGxpZXIuc2l6ZSA9IDNgIG1lbmluZ2thdGthbiB1a3VyYW4gb3V0bGllciANCiAgDQoqIFVudHVrIGtlZ2VsaXNhaGFuOg0KICAtIGBhbHBoYSA9IDAsNWAgbWVtYnVhdCBwb2luIGxlYmloIHRyYW5zcGFyYW4NCiAgLSBgd2lkdGggPSAuMmAgbWVuZ3VyYW5naSBqdW1sYWggaml0dGVyICguNCBhZGFsYWggZGVmYXVsdCkNCiAgDQpBa2hpcm55YSwgJHggJCBkYW4gJHkgJCAkIGRpaG9ybWF0aSBtZW5nZ3VuYWthbiBmdW5nc2kgJ2Nvb3JkX2ZsaXAnICh5YWl0dSwgZ3JhZmlrIGRpaGlkdXBrYW4gZGkgc2lzaW55YSkuDQpgYGB7ciBWaXN1YWxpemF0aW9uMzYsIG1lc3NhZ2U9RkFMU0UsIGVjaG89VCxmaWcuYWxpZ249J2NlbnRlcicsZmlnLmNhcD0iQ29tYmluaW5nIEppdHRlciBhbmQgQm94cGxvdHMgMSIsIG91dC53aWR0aCA9ICcxMDAlJ30NCmxpYnJhcnkoZ2dwbG90MikgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBmb3IgdmlzdWxpemF0aW9uDQpsaWJyYXJ5KHNjYWxlcykgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgc2NhbGluZyBpbmZyYXN0cnVjdHVyZQ0KZ2dwbG90KFNhbGFyaWVzLCANCiAgICAgICBhZXMoeCA9IGZhY3RvcihyYW5rLA0KICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIkFzc2lzdGFudFxuUHJvZmVzc29yIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJBc3NvY2lhdGVcblByb2Zlc3NvciIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRnVsbFxuUHJvZmVzc29yIikpLCANCiAgICAgICAgICAgeSA9IHNhbGFyeSwgDQogICAgICAgICAgIGNvbG9yID0gcmFuaykpICsNCiAgZ2VvbV9ib3hwbG90KHNpemU9MSwNCiAgICAgICAgICAgICAgIG91dGxpZXIuc2hhcGUgPSAxLA0KICAgICAgICAgICAgICAgb3V0bGllci5jb2xvciA9ICJibGFjayIsDQogICAgICAgICAgICAgICBvdXRsaWVyLnNpemUgID0gMykgKw0KICBnZW9tX2ppdHRlcihhbHBoYSA9IDAuNSwgDQogICAgICAgICAgICAgIHdpZHRoPS4yKSArIA0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWwgPSBkb2xsYXIpICsNCiAgbGFicyh0aXRsZSA9ICJBY2FkZW1pYyBTYWxhcnkgYnkgUmFuayIsIA0KICAgICAgIHN1YnRpdGxlID0gIjktbW9udGggc2FsYXJ5IGZvciAyMDA4LTIwMDkiLA0KICAgICAgIHggPSAiIiwNCiAgICAgICB5ID0gIiIpICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArDQogIGNvb3JkX2ZsaXAoKQ0KYGBgDQoNCg0KU2ViZWx1bSBtZWxhbmp1dGthbiwgYWRhIGJhaWtueWEgbWVueWVidXRrYW4gZnVuZ3NpIFsnZ2VvbV9ib3hqaXR0ZXInXShodHRwczovL3d3dy5yZG9jdW1lbnRhdGlvbi5vcmcvcGFja2FnZXMvZ2dwb2wvdmVyc2lvbnMvMC4wLjEvdG9waWNzL2dlb21fYm94aml0dGVyKSB5YW5nIGRpc2VkaWFrYW4gZGFsYW0gcGFrZXQgWydnZ3BvbCddKGh0dHBzOi8vZXJvY29hci5naXRodWIuaW8vZ2dwb2wvKS4gSW5pIG1lbmNpcHRha2FuIGJveHBsb3QgaGlicmlkYSAtIHNldGVuZ2FoIGJveHBsb3QsIHNldGVuZ2FoIHNjYXR0ZXJwbG90Lg0KDQpgYGB7ciBWaXN1YWxpemF0aW9uMzcsIG1lc3NhZ2U9RkFMU0UsIGVjaG89VCxmaWcuYWxpZ249J2NlbnRlcicsZmlnLmNhcD0iQ29tYmluaW5nIEppdHRlciBhbmQgQm94cGxvdHMgMiIsIG91dC53aWR0aCA9ICcxMDAlJ30NCmxpYnJhcnkoZ2dwbG90MikgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBmb3IgdmlzdWxpemF0aW9uDQpsaWJyYXJ5KHNjYWxlcykgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgc2NhbGluZyBpbmZyYXN0cnVjdHVyZQ0KbGlicmFyeShnZ3BvbCkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGh5YnJpZCBib3hwbG90IC1oYWxmIHNjYXR0ZXJwbG90DQpnZ3Bsb3QoU2FsYXJpZXMsIA0KICAgICAgIGFlcyh4ID0gZmFjdG9yKHJhbmssDQogICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygiQXNzaXN0YW50XG5Qcm9mZXNzb3IiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkFzc29jaWF0ZVxuUHJvZmVzc29yIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJGdWxsXG5Qcm9mZXNzb3IiKSksIA0KICAgICAgICAgICB5ID0gc2FsYXJ5LCANCiAgICAgICAgICAgZmlsbD1yYW5rKSkgKw0KICBnZW9tX2JveGppdHRlcihjb2xvcj0iYmxhY2siLA0KICAgICAgICAgICAgICAgICBqaXR0ZXIuY29sb3IgPSAiZGFya2dyZXkiLA0KICAgICAgICAgICAgICAgICBlcnJvcmJhci5kcmF3ID0gVFJVRSkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWwgPSBkb2xsYXIpICsNCiAgbGFicyh0aXRsZSA9ICJBY2FkZW1pYyBTYWxhcnkgYnkgUmFuayIsIA0KICAgICAgIHN1YnRpdGxlID0gIjktbW9udGggc2FsYXJ5IGZvciAyMDA4LTIwMDkiLA0KICAgICAgIHggPSAiIiwNCiAgICAgICB5ID0gIiIpICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQ0KYGBgDQoNCiMjIyBQbG90IEJlZXN3YXJtDQoNClBsb3QgYmVlc3dhcm0gKGp1Z2EgZGlzZWJ1dCBwbG90IHNlYmFyYW4gYmlvbGEpIG1pcmlwIGRlbmdhbiBzZWJhcmFuIHlhbmcgZ2VsaXNhaCwga2FyZW5hIG1lcmVrYSBtZW5hbXBpbGthbiBkaXN0cmlidXNpIHZhcmlhYmVsIGt1YW50aXRhdGlmIGRlbmdhbiBtZXJlbmNhbmFrYW4gdGl0aWsgZGVuZ2FuIGNhcmEgeWFuZyBtZW5ndXJhbmdpIHR1bXBhbmcgdGluZGloLiBTZWxhaW4gaXR1LCBtZXJla2EganVnYSBtZW1iYW50dSBtZW5hbXBpbGthbiBrZXBhZGF0YW4gZGF0YSBkaSBzZXRpYXAgdGl0aWsgKGRlbmdhbiBjYXJhIHlhbmcgbWlyaXAgZGVuZ2FuIFtwbG90IGJpb2xhXSgpKS4gTWVsYW5qdXRrYW4gY29udG9oIHNlYmVsdW1ueWENCg0KDQpgYGB7ciBWaXN1YWxpemF0aW9uMzgsIG1lc3NhZ2U9RkFMU0UsIGVjaG89VCxmaWcuYWxpZ249J2NlbnRlcicsZmlnLmNhcD0iQmVlc3dhcm0gUGxvdHMiLCBvdXQud2lkdGggPSAnMTAwJSd9DQpsaWJyYXJ5KGdncGxvdDIpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgZm9yIHZpc3VsaXphdGlvbg0KbGlicmFyeShzY2FsZXMpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHNjYWxpbmcgaW5mcmFzdHJ1Y3R1cmUNCmxpYnJhcnkoZ2diZWVzd2FybSkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyByZWR1Y2VzIG92ZXJsYXANCmdncGxvdChTYWxhcmllcywgDQogICAgICAgYWVzKHggPSBmYWN0b3IocmFuaywNCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJBc3Npc3RhbnRcblByb2Zlc3NvciIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQXNzb2NpYXRlXG5Qcm9mZXNzb3IiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkZ1bGxcblByb2Zlc3NvciIpKSwgDQogICAgICAgICAgIHkgPSBzYWxhcnksIA0KICAgICAgICAgICBjb2xvciA9IHJhbmspKSArDQogIGdlb21fcXVhc2lyYW5kb20oYWxwaGEgPSAwLjcsDQogICAgICAgICAgICAgICAgICAgc2l6ZSA9IDEuNSkgKyANCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVsID0gZG9sbGFyKSArDQogIGxhYnModGl0bGUgPSAiQWNhZGVtaWMgU2FsYXJ5IGJ5IFJhbmsiLCANCiAgICAgICBzdWJ0aXRsZSA9ICI5LW1vbnRoIHNhbGFyeSBmb3IgMjAwOC0yMDA5IiwNCiAgICAgICB4ID0gIiIsDQogICAgICAgeSA9ICIiKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikNCmBgYA0KUGxvdCBkaWJ1YXQgbWVuZ2d1bmFrYW4gZnVuZ3NpIGdlb21fcXVhc2lyYW5kb20gc2FtYS4gUGxvdCBpbmkgZGFwYXQgbGViaWggbXVkYWggZGliYWNhIGRhcmlwYWRhIHBsb3Qgc3RyaXAgc2VkZXJoYW5hIHlhbmcgZ2VsaXNhaC4gVW50dWsgbWVtcGVsYWphcmkgbGViaWggbGFuanV0IHRlbnRhbmcgcGxvdCBpbmksIGxpaGF0IFtwbG90IGdheWEgQmVlc3dhcm1dKGh0dHBzOi8vZ2l0aHViLmNvbS9lY2xhcmtlL2dnYmVlc3dhcm0pIGRlbmdhbiBnZ3Bsb3QyLg0KDQojIyMgQmFnYW4gVGl0aWsgQ2xldmVsYW5kDQoNClBsb3QgQ2xldmVsYW5kIGJlcmd1bmEga2V0aWthIEFuZGEgaW5naW4gbWVtYmFuZGluZ2thbiBzdGF0aXN0aWsgbnVtZXJpayB1bnR1ayBzZWp1bWxhaCBiZXNhciBrZWxvbXBvay4gTWlzYWxueWEsIEFuZGEgaW5naW4gbWVtYmFuZGluZ2thbiBoYXJhcGFuIGhpZHVwIDIwMDcgdW50dWsgbmVnYXJhIEFzaWEgbWVuZ2d1bmFrYW4ga3VtcHVsYW4gZGF0YSBbZ2FwbWluZGVyXSgpLg0KDQoNCmBgYHtyIFZpc3VhbGl6YXRpb24zOSwgbWVzc2FnZT1GQUxTRSwgZWNobz1ULGZpZy5hbGlnbj0nY2VudGVyJyxmaWcuY2FwPSJCZWVzd2FybSBQbG90cyIsIG91dC53aWR0aCA9ICcxMDAlJ30NCmxpYnJhcnkoZHBseXIpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBmb3IgZGF0YSBtYW5pcHVsYXRpb24NCmxpYnJhcnkoZ2dwbG90MikgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBmb3IgdmlzdWxpemF0aW9uDQpsaWJyYXJ5KHNjYWxlcykgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgc2NhbGluZyBpbmZyYXN0cnVjdHVyZQ0KbGlicmFyeShnZ2JlZXN3YXJtKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHJlZHVjZXMgb3ZlcmxhcA0KbGlicmFyeShnYXBtaW5kZXIpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGZvciBkYXRhc2V0IGBnYXBtaW5kZXJgIA0KZGF0YShnYXBtaW5kZXIsIHBhY2thZ2U9ImdhcG1pbmRlciIpICAgICAgICAgICAgICAgICAjIGxvYWQgZGF0YXNldCBgZ2FwbWluZGVyYCANCiMgc3Vic2V0IEFzaWFuIGNvdW50cmllcyBpbiAyMDA3DQpsaWJyYXJ5KGRwbHlyKQ0KcGxvdGRhdGEgPC0gZ2FwbWluZGVyICU+JQ0KICBmaWx0ZXIoY29udGluZW50ID09ICJBc2lhIiAmIA0KICAgICAgICAgeWVhciA9PSAyMDA3KQ0KIyBGYW5jeSBDbGV2ZWxhbmQgcGxvdA0KZ2dwbG90KHBsb3RkYXRhLCANCiAgICAgICBhZXMoeD1saWZlRXhwLCANCiAgICAgICAgICAgeT1yZW9yZGVyKGNvdW50cnksIGxpZmVFeHApKSkgKw0KICBnZW9tX3BvaW50KGNvbG9yPSJibHVlIiwgDQogICAgICAgICAgICAgc2l6ZSA9IDIpICsNCiAgZ2VvbV9zZWdtZW50KGFlcyh4ID0gNDAsIA0KICAgICAgICAgICAgICAgeGVuZCA9IGxpZmVFeHAsIA0KICAgICAgICAgICAgICAgeSA9IHJlb3JkZXIoY291bnRyeSwgbGlmZUV4cCksIA0KICAgICAgICAgICAgICAgeWVuZCA9IHJlb3JkZXIoY291bnRyeSwgbGlmZUV4cCkpLA0KICAgICAgICAgICAgICAgY29sb3IgPSAiYXp1cmUzIikgKw0KICBsYWJzICh4ID0gIkxpZmUgRXhwZWN0YW5jeSAoeWVhcnMpIiwNCiAgICAgICAgeSA9ICIiLA0KICAgICAgICB0aXRsZSA9ICJMaWZlIEV4cGVjdGFuY3kgYnkgQ291bnRyeSIsDQogICAgICAgIHN1YnRpdGxlID0gIkdhcE1pbmRlciBkYXRhIGZvciBBc2lhIC0gMjAwNyIpICsNCiAgdGhlbWVfbWluaW1hbCgpICsgDQogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCkpDQpgYGANCkplcGFuZyBqZWxhcyBtZW1pbGlraSBoYXJhcGFuIGhpZHVwIHRlcnRpbmdnaSwgc2VtZW50YXJhIEFmZ2hhbmlzdGFuIG1lbWlsaWtpIHlhbmcgdGVyZW5kYWggc2VqYXVoIGluaS4gUGxvdCB0ZXJha2hpciBpbmkganVnYSBkaXNlYnV0IGdyYWZpayBsb2xpcG9wLg0KDQojIERhdGEgTXVsdGl2YXJpYXQNCg0KR3JhZmlrIG11bHRpdmFyaWF0IG1lbmFtcGlsa2FuIGh1YnVuZ2FuIGRpIGFudGFyYSB0aWdhIHZhcmlhYmVsIGF0YXUgbGViaWguIEFkYSBkdWEgbWV0b2RlIHVtdW0gdW50dWsgbWVuZ2Frb21vZGFzaSBiZWJlcmFwYSB2YXJpYWJlbDogcGVuZ2Vsb21wb2thbiBkYW4gZmFjZXRpbmcuDQoNCiMjIFBlbmdlbG9tcG9rYW4NCg0KRGFsYW0gcGVuZ2Vsb21wb2thbiwgbmlsYWkgZGFyaSBkdWEgdmFyaWFiZWwgcGVydGFtYSBkaXBldGFrYW4ga2Ugc3VtYnUgeCBkYW4geS4gS2VtdWRpYW4gdmFyaWFiZWwgdGFtYmFoYW4gZGlwZXRha2FuIGtlIGthcmFrdGVyaXN0aWsgdmlzdWFsIGxhaW5ueWEgc2VwZXJ0aSB3YXJuYSwgYmVudHVrLCB1a3VyYW4sIHRpcGUgZ2FyaXMsIGRhbiB0cmFuc3BhcmFuc2kuIFBlbmdlbG9tcG9rYW4gbWVtdW5na2lua2FuIEFuZGEgbWVyZW5jYW5ha2FuIGRhdGEgdW50dWsgYmViZXJhcGEgZ3J1cCBkYWxhbSBzYXR1IGdyYWZpay4gRGVuZ2FuIG1lbmdndW5ha2FuIGt1bXB1bGFuIGRhdGEgW0dhamldKCksIG1hcmkga2l0YSB0YW1waWxrYW4gaHVidW5nYW4gYW50YXJhIHlycy5zaW5jZS5waGQgZGFuIGdhamkuDQoNCmBgYHtyIFZpc3VhbGl6YXRpb240MCwgbWVzc2FnZT1GQUxTRSwgZWNobz1ULGZpZy5hbGlnbj0nY2VudGVyJyxmaWcuY2FwPSJNdWx0aXZhcmlhdGUgR3JvdXBpbmcgUGxvdCAxIiwgb3V0LndpZHRoID0gJzEwMCUnfQ0KbGlicmFyeShjYXJEYXRhKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGZvciBkYXRhc2V0DQpsaWJyYXJ5KGdncGxvdDIpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgZm9yIHZpc3VsaXphdGlvbg0KZGF0YShTYWxhcmllcywgcGFja2FnZT0iY2FyRGF0YSIpDQpnZ3Bsb3QoU2FsYXJpZXMsIGFlcyh4ID0geXJzLnNpbmNlLnBoZCwgDQogICAgICAgICAgICAgICAgICAgICB5ID0gc2FsYXJ5LCANCiAgICAgICAgICAgICAgICAgICAgIGNvbG9yPXJhbmspKSArDQogIGdlb21fcG9pbnQoKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIGxhYnModGl0bGUgPSAiQWNhZGVtaWMgc2FsYXJ5IGJ5IHJhbmsgYW5kIHllYXJzIHNpbmNlIGRlZ3JlZSIpDQpgYGANCg0KU2VsYW5qdXRueWEsIG1hcmkga2l0YSB0YW1iYWhrYW4gamVuaXMga2VsYW1pbiBwcm9mZXNvciwgbWVuZ2d1bmFrYW4gYmVudHVrIHBvaW4gdW50dWsgbWVudW5qdWtrYW4gc2Vrcy4gVGluZ2thdGthbiB1a3VyYW4gcG9pbiBkZW5nYW4gYmFpayBkYW4gdGFtYmFoa2FuIHRyYW5zcGFyYW5zaSB1bnR1ayBtZW1idWF0IHBvaW4gaW5kaXZpZHUgbGViaWggamVsYXMuDQoNCmBgYHtyIFZpc3VhbGl6YXRpb240MSwgbWVzc2FnZT1GQUxTRSwgZWNobz1ULGZpZy5hbGlnbj0nY2VudGVyJyxmaWcuY2FwPSJNdWx0aXZhcmlhdGUgR3JvdXBpbmcgUGxvdCAyIiwgb3V0LndpZHRoID0gJzEwMCUnfQ0KbGlicmFyeShjYXJEYXRhKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGZvciBkYXRhc2V0DQpsaWJyYXJ5KGdncGxvdDIpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgZm9yIHZpc3VsaXphdGlvbg0KZ2dwbG90KFNhbGFyaWVzLCANCiAgICAgICBhZXMoeCA9IHlycy5zaW5jZS5waGQsIA0KICAgICAgICAgICB5ID0gc2FsYXJ5LCANCiAgICAgICAgICAgY29sb3IgPSByYW5rLCANCiAgICAgICAgICAgc2hhcGUgPSBzZXgpKSArDQogIGdlb21fcG9pbnQoc2l6ZSA9IDMsIGFscGhhID0gLjYpICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgbGFicyh0aXRsZSA9ICJBY2FkZW1pYyBzYWxhcnkgYnkgcmFuaywgc2V4LCBhbmQgeWVhcnMgc2luY2UgZGVncmVlIikNCmBgYA0KDQpLaXRhIHRpZGFrIGJpc2EgbWVuZ2F0YWthbiBiYWh3YSBpbmkgYWRhbGFoIGdyYWZpcyB5YW5nIGhlYmF0LiBJbmkgc2FuZ2F0IHNpYnVrLCBkYW4gbXVuZ2tpbiBzdWxpdCB1bnR1ayBtZW1iZWRha2FuIGxha2ktbGFraSBkYXJpIHByb2Zlc29yIHBlcmVtcHVhbi4gRmFjZXRpbmcgKGRpamVsYXNrYW4gZGkgYmFnaWFuIGJlcmlrdXRueWEpIG11bmdraW4gYWthbiBtZW5qYWRpIHBlbmRla2F0YW4geWFuZyBsZWJpaCBiYWlrLg0KDQo+IFBlcmhhdGlrYW4gcGVyYmVkYWFuIGFudGFyYSBtZW5lbnR1a2FuIG5pbGFpIGtvbnN0YW50YSAoc2VwZXJ0aSB1a3VyYW4gPSAzKSBkYW4gcGVtZXRhYW4gdmFyaWFiZWwga2Uga2FyYWt0ZXJpc3RpayB2aXN1YWwgKG1pc2FsbnlhLCB3YXJuYSA9IHBlcmluZ2thdCkuIFBlbWV0YWFuIHNlbGFsdSBkaXRlbXBhdGthbiBkYWxhbSBmdW5nc2kgbGViYWgsIHNlbWVudGFyYSBwZW5ldGFwYW4gbmlsYWkga29uc3RhbnRhIHNlbGFsdSBtdW5jdWwgZGkgbHVhciBmdW5nc2kgbGViYWguDQoNCkJlcmlrdXQgYWRhbGFoIGNvbnRvaCB5YW5nIGxlYmloIGJlcnNpaC4gTmFoLCBncmFmaWsgaHVidW5nYW4gYW50YXJhIHRhaHVuIHNlamFrIFBoLkQuIGRhbiBnYWppIG1lbmdndW5ha2FuIHVrdXJhbiBwb2luIHVudHVrIG1lbnVuanVra2FuIHRhaHVuIGxheWFuYW4uIEluaSBkaXNlYnV0IHBsb3QgZ2VsZW1idW5nLg0KDQoNCmBgYHtyIFZpc3VhbGl6YXRpb240MiwgbWVzc2FnZT1GQUxTRSwgZWNobz1ULGZpZy5hbGlnbj0nY2VudGVyJyxmaWcuY2FwPSJNdWx0aXZhcmlhdGUgR3JvdXBpbmcgUGxvdCAzIiwgb3V0LndpZHRoID0gJzEwMCUnfQ0KbGlicmFyeShjYXJEYXRhKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGZvciBkYXRhc2V0DQpsaWJyYXJ5KGdncGxvdDIpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgZm9yIHZpc3VsaXphdGlvbg0KZ2dwbG90KFNhbGFyaWVzLCANCiAgICAgICBhZXMoeCA9IHlycy5zaW5jZS5waGQsIA0KICAgICAgICAgICB5ID0gc2FsYXJ5LCANCiAgICAgICAgICAgY29sb3IgPSByYW5rLCANCiAgICAgICAgICAgc2l6ZSA9IHlycy5zZXJ2aWNlKSkgKw0KICBnZW9tX3BvaW50KGFscGhhID0gLjgpICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgbGFicyh0aXRsZSA9ICJBY2FkZW1pYyBzYWxhcnkgYnkgcmFuaywgeWVhcnMgb2Ygc2VydmljZSwgYW5kIHllYXJzIHNpbmNlIGRlZ3JlZSIpDQpgYGANCg0KSmVsYXMgYWRhIGh1YnVuZ2FuIHBvc2l0aWYgeWFuZyBrdWF0IGFudGFyYSB0YWh1biBzZWphayBQaC5ELiBkYW4gdGFodW4gcGVsYXlhbmFuLiBBc2lzdGVuIFByb2Zlc29yIGphdHVoIGRhbGFtIDAtMTEgdGFodW4gc2VqYWsgUGguRC4gZGFuIDAtMTAgdGFodW4gamFuZ2thdWFuIGxheWFuYW4uIEplbGFzLCBwcm9mZXNpb25hbCB5YW5nIHNhbmdhdCBiZXJwZW5nYWxhbWFuIHRpZGFrIHRpbmdnYWwgZGkgdGluZ2thdCBBc2lzdGVuIFByb2Zlc29yIChtZXJla2EgbXVuZ2tpbiBkaXByb21vc2lrYW4gYXRhdSBtZW5pbmdnYWxrYW4gVW5pdmVyc2l0YXMpLiBLYW1pIHRpZGFrIG1lbmVtdWthbiBkZW1hcmthc2kgd2FrdHUgeWFuZyBzYW1hIGFudGFyYSBBc3NvY2lhdGUgZGFuIEZ1bGwgUHJvZmVzc29ycy4gW0dlbGVtYnVuZyBwbG90XSAoKSBkaWplbGFza2FuIHNlY2FyYSBsZWJpaCByaW5jaSBkaSBiYWIgc2VsYW5qdXRueWEuDQoNClNlYmFnYWkgY29udG9oIGFraGlyLCBtYXJpIGtpdGEgbGloYXQgeXJzLnNpbmNlLnBoZCB2cyBnYWppIGRhbiB0YW1iYWhrYW4gc2VrcyBtZW5nZ3VuYWthbiB3YXJuYSBkYW4gW2dhcmlzIHBhbGluZyBjb2NvayBrdWFkcmF0XSgpLg0KDQpgYGB7ciBWaXN1YWxpemF0aW9uNDMsIG1lc3NhZ2U9RkFMU0UsIGVjaG89VCxmaWcuYWxpZ249J2NlbnRlcicsZmlnLmNhcD0iTXVsdGl2YXJpYXRlIEdyb3VwaW5nIFBsb3QgNCIsIG91dC53aWR0aCA9ICcxMDAlJ30NCmxpYnJhcnkoY2FyRGF0YSkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBmb3IgZGF0YXNldA0KbGlicmFyeShnZ3Bsb3QyKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGZvciB2aXN1bGl6YXRpb24NCmdncGxvdChTYWxhcmllcywgDQogICAgICAgYWVzKHggPSB5cnMuc2luY2UucGhkLCANCiAgICAgICAgICAgeSA9IHNhbGFyeSwgDQogICAgICAgICAgIGNvbG9yID0gc2V4KSkgKw0KICBnZW9tX3BvaW50KGFscGhhID0gLjQsIA0KICAgICAgICAgICAgIHNpemUgPSAzKSArDQogIGdlb21fc21vb3RoKHNlPUZBTFNFLCANCiAgICAgICAgICAgICAgbWV0aG9kID0gImxtIiwgDQogICAgICAgICAgICAgIGZvcm11bGEgPSB5fnBvbHkoeCwyKSwgDQogICAgICAgICAgICAgIHNpemUgPSAxLjUpICsNCiAgbGFicyh4ID0gIlllYXJzIFNpbmNlIFBoLkQuIiwNCiAgICAgICB0aXRsZSA9ICJBY2FkZW1pYyBTYWxhcnkgYnkgU2V4IGFuZCBZZWFycyBFeHBlcmllbmNlIiwNCiAgICAgICBzdWJ0aXRsZSA9ICI5LW1vbnRoIHNhbGFyeSBmb3IgMjAwOC0yMDA5IiwNCiAgICAgICB5ID0gIiIsDQogICAgICAgY29sb3IgPSAiU2V4IikgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWwgPSBzY2FsZXM6OmRvbGxhcikgKw0KICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZSA9ICJTZXQxIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KDQojIyBGYWNldGluZyB7LnRhYnNldCAudGFic2V0LWZhZGUgLnRhYnNldC1waWxsc30NCg0KIyMjIE1lbmdoYWRhcGkgMQ0KDQpQZW5nZWxvbXBva2FuIG1lbXVuZ2tpbmthbiBBbmRhIG1lcmVuY2FuYWthbiBiZWJlcmFwYSB2YXJpYWJlbCBkYWxhbSBzYXR1IGdyYWZpaywgbWVuZ2d1bmFrYW4ga2FyYWt0ZXJpc3RpayB2aXN1YWwgc2VwZXJ0aSB3YXJuYSwgYmVudHVrLCBkYW4gdWt1cmFuLiBEYWxhbSBmYWNldGluZywgZ3JhZmlrIHRlcmRpcmkgZGFyaSBiZWJlcmFwYSBwbG90IHRlcnBpc2FoIGF0YXUga2VsaXBhdGFuIGtlY2lsLCBzYXR1IHVudHVrIHNldGlhcCB0aW5na2F0IHZhcmlhYmVsIGtldGlnYSwgYXRhdSBrb21iaW5hc2kgdmFyaWFiZWwuIFBhbGluZyBtdWRhaCB1bnR1ayBtZW1haGFtaSBpbmkgZGVuZ2FuIGNvbnRvaC4NCg0KYGBge3IgVmlzdWFsaXphdGlvbjQ0LCBtZXNzYWdlPUZBTFNFLCBlY2hvPVQsZmlnLmFsaWduPSdjZW50ZXInLGZpZy5jYXA9Ik11bHRpdmFyaWF0ZSBGYWNldGluZyAxIiwgb3V0LndpZHRoID0gJzEwMCUnfQ0KbGlicmFyeShjYXJEYXRhKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGZvciBkYXRhc2V0DQpsaWJyYXJ5KGdncGxvdDIpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgZm9yIHZpc3VsaXphdGlvbg0KZ2dwbG90KFNhbGFyaWVzLCBhZXMoeCA9IHNhbGFyeSkpICsNCiAgZ2VvbV9oaXN0b2dyYW0oZmlsbCA9ICJjb3JuZmxvd2VyYmx1ZSIsDQogICAgICAgICAgICAgICAgIGNvbG9yID0gIndoaXRlIikgKw0KICBmYWNldF93cmFwKH5yYW5rLCBuY29sID0gMSkgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICBsYWJzKHRpdGxlID0gIlNhbGFyeSBoaXN0b2dyYW1zIGJ5IHJhbmsiKQ0KYGBgDQoNCkZ1bmdzaSBgZmFjZXRfd3JhcGAgbWVtYnVhdCBncmFmaWsgdGVycGlzYWggdW50dWsgc2V0aWFwIHRpbmdrYXQgcGVyaW5na2F0LiBPcHNpICduY29sJyBtZW5nb250cm9sIGp1bWxhaCBrb2xvbS4gRGFsYW0gY29udG9oIGJlcmlrdXRueWEsIGR1YSB2YXJpYWJlbCBkaWd1bmFrYW4gdW50dWsgbWVuZW50dWthbiBhc3Blay4gDQoNCiMjIyBNZW5naGFkYXBpIDINCg0KRGkgc2luaSwgZnVuZ3NpIG1lbmV0YXBrYW4gc2VrcyBrZSBiYXJpcyBkYW4gcGVyaW5na2F0IGtlIGtvbG9tLCBtZW1idWF0IG1hdHJpa3MgNiBwbG90IGRhbGFtIHNhdHUgZ3JhZmlrLg0KDQpgYGB7ciBWaXN1YWxpemF0aW9uNDUsIG1lc3NhZ2U9RkFMU0UsIGVjaG89VCxmaWcuYWxpZ249J2NlbnRlcicsZmlnLmNhcD0iTXVsdGl2YXJpYXRlIEZhY2V0aW5nIDIiLCBvdXQud2lkdGggPSAnMTAwJSd9DQpsaWJyYXJ5KGNhckRhdGEpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgZm9yIGRhdGFzZXQNCmxpYnJhcnkoZ2dwbG90MikgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBmb3IgdmlzdWxpemF0aW9uDQpnZ3Bsb3QoU2FsYXJpZXMsIGFlcyh4ID0gc2FsYXJ5IC8gMTAwMCkpICsNCiAgZ2VvbV9oaXN0b2dyYW0oY29sb3IgPSAid2hpdGUiLA0KICAgICAgICAgICAgICAgICBmaWxsID0gImNvcm5mbG93ZXJibHVlIikgKw0KICBmYWNldF9ncmlkKHNleCB+IHJhbmspICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgbGFicyh0aXRsZSA9ICJTYWxhcnkgaGlzdG9ncmFtcyBieSBzZXggYW5kIHJhbmsiLA0KICAgICAgIHggPSAiU2FsYXJ5ICgkMTAwMCkiKQ0KYGBgDQoNCiMjIyBNZW5naGFkYXBpIDMNCg0KS2l0YSBqdWdhIGRhcGF0IG1lbmdnYWJ1bmdrYW4gcGVuZ2Vsb21wb2thbiBkYW4gZmFjZXRpbmcuIE1hcmkga2l0YSBndW5ha2FuIHBsb3QgTWVhbiAvU0UgZGFuIGZhY2V0aW5nIHVudHVrIG1lbWJhbmRpbmdrYW4gZ2FqaSBwcm9mZXNvciBwcmlhIGRhbiB3YW5pdGEsIGRhbGFtIHBhbmdrYXQgZGFuIGRpc2lwbGluLiBOYWgsIGd1bmFrYW4gd2FybmEgdW50dWsgbWVtYmVkYWthbiBzZWtzIGRhbiBmYWNldGluZyB1bnR1ayBtZW1idWF0IHBsb3QgdW50dWsgcGVyaW5na2F0IGJlcmRhc2Fya2FuIGtvbWJpbmFzaSBkaXNpcGxpbi4NCg0KYGBge3IgVmlzdWFsaXphdGlvbjQ2LCBtZXNzYWdlPUZBTFNFLCBlY2hvPVQsZmlnLmFsaWduPSdjZW50ZXInLGZpZy5jYXA9Ik11bHRpdmFyaWF0ZSBGYWNldGluZyAzIiwgb3V0LndpZHRoID0gJzEwMCUnfQ0KbGlicmFyeShjYXJEYXRhKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGZvciBkYXRhc2V0DQpsaWJyYXJ5KGdncGxvdDIpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgZm9yIHZpc3VsaXphdGlvbg0KbGlicmFyeShkcGx5cikgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGRhdGEgbWFuaXB1bGF0aW9uDQojIGNhbGN1bGF0ZSBtZWFucyBhbmQgc3RhbmRhcmQgZXJyb2VzIGJ5IHNleCwNCiMgcmFuayBhbmQgZGlzY2lwbGluZQ0KcGxvdGRhdGEgPC0gU2FsYXJpZXMgJT4lDQogIGdyb3VwX2J5KHNleCwgcmFuaywgZGlzY2lwbGluZSkgJT4lDQogIGRwbHlyOjpzdW1tYXJpemUobiA9IG4oKSwNCiAgICAgICAgICAgIG1lYW4gPSBtZWFuKHNhbGFyeSksDQogICAgICAgICAgICBzZCA9IHNkKHNhbGFyeSksDQogICAgICAgICAgICBzZSA9IHNkIC8gc3FydChuKSkNCiMgY3JlYXRlIGJldHRlciBsYWJlbHMgZm9yIGRpc2NpcGxpbmUNCnBsb3RkYXRhJGRpc2NpcGxpbmUgPC0gZmFjdG9yKHBsb3RkYXRhJGRpc2NpcGxpbmUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJUaGVvcmV0aWNhbCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJBcHBsaWVkIikpDQojIGNyZWF0ZSBwbG90DQpnZ3Bsb3QocGxvdGRhdGEsIA0KICAgICAgIGFlcyh4ID0gc2V4LCANCiAgICAgICAgICAgeSA9IG1lYW4sDQogICAgICAgICAgIGNvbG9yID0gc2V4KSkgKw0KICBnZW9tX3BvaW50KHNpemUgPSAzKSArDQogIGdlb21fZXJyb3JiYXIoYWVzKHltaW4gPSBtZWFuIC0gc2UsIA0KICAgICAgICAgICAgICAgICAgICB5bWF4ID0gbWVhbiArIHNlKSwNCiAgICAgICAgICAgICAgICB3aWR0aCA9IC4xKSArDQogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoNzAwMDAsIDE0MDAwMCwgMTAwMDApLA0KICAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSBzY2FsZXM6OmRvbGxhcikgKw0KICBmYWNldF9ncmlkKC4gfiByYW5rICsgZGlzY2lwbGluZSkgKw0KICB0aGVtZV9idygpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLA0KICAgICAgICBwYW5lbC5ncmlkLm1ham9yLnggPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIHBhbmVsLmdyaWQubWlub3IueSA9IGVsZW1lbnRfYmxhbmsoKSkgKw0KICBsYWJzKHg9IiIsIA0KICAgICAgIHk9IiIsIA0KICAgICAgIHRpdGxlPSJOaW5lIG1vbnRoIGFjYWRlbWljIHNhbGFyaWVzIGJ5IGdlbmRlciwgZGlzY2lwbGluZSwgYW5kIHJhbmsiLA0KICAgICAgIHN1YnRpdGxlID0gIihNZWFucyBhbmQgc3RhbmRhcmQgZXJyb3JzKSIpICsNCiAgc2NhbGVfY29sb3JfYnJld2VyKHBhbGV0dGU9IlNldDEiKQ0KYGBgDQoNClBlcm55YXRhYW4gYGZhY2V0X2dyaWQoLiB+IHJhbmsgKyBkaXNjaXBsaW5lKWAgdGlkYWsgbWVuZW50dWthbiB2YXJpYWJlbCBiYXJpcyAoLikgZGFuIGtvbG9tIHlhbmcgZGl0ZW50dWthbiBvbGVoIGtvbWJpbmFzaSBwZXJpbmdrYXQgZGFuIGRpc2lwbGluLg0KDQpGdW5nc2kgYHRoZW1lKClgIG1lbWJ1YXQgdGVtYSBoaXRhbSBwdXRpaCBkYW4gbWVuZ2hpbGFuZ2thbiBnYXJpcyBraXNpIHZlcnRpa2FsIGRhbiBnYXJpcyBraXNpIGhvcml6b250YWwga2VjaWwuIEZ1bmdzaSBgc2NhbGVfY29sb3JfYnJld2VyKClgIG1lbmd1YmFoIHNrZW1hIHdhcm5hIHVudHVrIHRpdGlrIGRhbiBiaWxhaCBrZXNhbGFoYW4uDQoNClBhZGEgcGFuZGFuZ2FuIHBlcnRhbWEsIHRhbXBha255YSBtdW5na2luIGFkYSBwZXJiZWRhYW4gZ2VuZGVyIGRhbGFtIGdhamkgdW50dWsgcmVrYW4gZGFuIHByb2Zlc29yIHBlbnVoIGRpIGJpZGFuZyB0ZW9yaXRpcy4gU2F5YSBtZW5nYXRha2FuIOJtaWdodOIga2FyZW5hIGtpdGEgdGVsYWggbWVsYWt1a2FuIHBlbmd1amlhbiBoaXBvdGVzaXMgZm9ybWFsIGJlbHVtIChBTkNPVkEgZGFsYW0gaGFsIGluaSkuIExpaGF0IGJhZ2lhbiBbTWVuZ2t1c3RvbWlzYXNpXSgpIHVudHVrIG1lbXBlbGFqYXJpIHNlbGVuZ2thcG55YSB0ZW50YW5nIG1lbmdrdXN0b21pc2FzaSB0YW1waWxhbiBncmFmaWsuDQoNCiMjIyBNZW5naGFkYXBpIDQNCg0KU2ViYWdhaSBjb250b2ggYWtoaXIsIHdl4mxsIGJlcmdlc2VyIGtlIHNldCBkYXRhIGJhcnUgZGFuIG1lcmVuY2FuYWthbiBwZXJ1YmFoYW4gaGFyYXBhbiBoaWR1cCBkYXJpIHdha3R1IGtlIHdha3R1IHVudHVrIG5lZ2FyYS1uZWdhcmEgZGkgQXNpYS4gRGF0YSBiZXJhc2FsIGRhcmkga3VtcHVsYW4gZGF0YSBgZ2FwbWluZGVyYCBkYWxhbSBwYWtldCBgZ2FwbWluZGVyYC4gU2V0aWFwIG5lZ2FyYSBtdW5jdWwgZGFsYW0gYXNwZWtueWEgc2VuZGlyaS4gRnVuZ3NpIHRlbWEgZGlndW5ha2FuIHVudHVrIG1lbnllZGVyaGFuYWthbiB3YXJuYSBsYXRhciBiZWxha2FuZywgbWVtdXRhciB0ZWtzIHN1bWJ1IHgsIGRhbiBtZW1idWF0IHVrdXJhbiBmb250IGxlYmloIGtlY2lsLg0KDQpgYGB7ciBWaXN1YWxpemF0aW9uNDcsIG1lc3NhZ2U9RkFMU0UsIGVjaG89VCxmaWcuYWxpZ249J2NlbnRlcicsZmlnLmNhcD0iTXVsdGl2YXJpYXRlIEZhY2V0aW5nIDQiLCBvdXQud2lkdGggPSAnMTAwJSd9DQpsaWJyYXJ5KGdhcG1pbmRlcikgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgZm9yIGRhdGFzZXQNCmxpYnJhcnkoZ2dwbG90MikgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBmb3IgdmlzdWxpemF0aW9uDQpsaWJyYXJ5KGRwbHlyKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgZGF0YSBtYW5pcHVsYXRpb24NCiMgcGxvdCBsaWZlIGV4cGVjdGFuY3kgYnkgeWVhciBzZXBhcmF0ZWx5IA0KIyBmb3IgZWFjaCBjb3VudHJ5IGluIHRoZSBBbWVyaWNhcw0KZGF0YShnYXBtaW5kZXIsIHBhY2thZ2UgPSAiZ2FwbWluZGVyIikNCg0KIyBTZWxlY3QgdGhlIEFtZXJpY2FzIGRhdGENCnBsb3RkYXRhIDwtIGRwbHlyOjpmaWx0ZXIoZ2FwbWluZGVyLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgY29udGluZW50ID09ICJBc2lhIikNCg0KIyBwbG90IGxpZmUgZXhwZWN0YW5jeSBieSB5ZWFyLCBmb3IgZWFjaCBjb3VudHJ5DQpnZ3Bsb3QocGxvdGRhdGEsIGFlcyh4PXllYXIsIHkgPSBsaWZlRXhwKSkgKw0KICBnZW9tX2xpbmUoY29sb3I9ImdyZXkiKSArDQogIGdlb21fcG9pbnQoY29sb3I9ImJsdWUiKSArDQogIGZhY2V0X3dyYXAofmNvdW50cnkpICsgDQogIHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gOSkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaGp1c3QgPSAxKSkgKw0KICBsYWJzKHRpdGxlID0gIkNoYW5nZXMgaW4gTGlmZSBFeHBlY3RhbmN5IiwNCiAgICAgICB4ID0gIlllYXIiLA0KICAgICAgIHkgPSAiTGlmZSBFeHBlY3RhbmN5IikgDQpgYGANCg==