1. Introduction

The Economist adalah majalah mingguan yang berfokus pada berita dan analisis tentang ekonomi, politik, bisnis, teknologi, dan budaya. Didirikan pada tahun 1843, majalah ini dikenal dengan sudut pandangnya yang global dan analisis yang mendalam terhadap isu-isu terkini. Hal menarik dari majalah ini yaitu adanya visualisasi berupa grafik/plot yang ditampilkan dengan menarik. Pada artikel ini, kami akan mencoba mereplikasi plot dari The Economist menggunakan library ggplot.

📌 Objektif:

Proyek ini menjelaskan bagaimana cara membuat replika visualisasi dari majalah The Economist. Hal ini dilakukan untuk membuktikan bahwa visualisasi tersebut dapat kita buat dan replika menggunakan bahasa pemrograman R menggunakan library ggplot.

✨ Manfaat:

Setelah Anda membaca artikel ini, diharapkan Anda:

  1. Mengerti dasar-dasar pemrograman R.
  2. Mengerti cara membuat visualisasi menggunakan library ggplot.
  3. Mengerti jenis plot yang sesuai berdasarkan data yang ada dan dapat menggunakan jenis plot tersebut untuk kasus yang berbeda dikemudian hari.

💡 Target Pembaca:

Artikel ini cocok bagi siapa saja yang ingin memulai belajar bahasa pemrograman R. Selain itu, artikel ini juga bermanfaat bagi seorang data analis agar menjadi rujukan dalam membuat visualisasi dalam berbagai kasus utamanya jika menggunakan library ggplot. Artikel ini bermanfaat untuk orang-orang yang tertarik dengan visualisasi data dan ingin mempelajari cara mereplikasi grafik-grafik yang terkenal dan kompleks. Dalam artikel ini dijelaskan flow dari pembersihan data, menyiapkan data untuk pembuatan plot, pembuatan plot itu sendiri, dan menggabungkan beberapa plot menjadi 1. Hal ini bisa dijadikan contoh jika ada pembaca yang akan melakukan hal yang sama.

📊 Jenis Plot:

Artikel ini akan membuat sebuah visualisasi yang terdiri dari kombinasi 3 plot sebagai berikut:

  1. Bubble Chart \(\rightarrow\) menampilkan jumlah individu yang diadili. Bubble chart memiliki bentuk berupa lingkaran. Semakin luas lingkaran, maka nilainya semakin tinggi.
  2. Stacked Bar Chart \(\rightarrow\) menampilkan jumlah individu yang diadili baik mendapat hukuman mati (executed) atau tidak dihukum mati (accused) pada suatu negara. Stacked bar chart atau diagram batang bertumpuk seperti namanya diagram tersebut berbentuk diagram batang yang terdiri dari 2 kategori (executed dan accused) yang disusun secara bertumpuk.
  3. Line Chart \(\rightarrow\) menampilkan grafik garis yang menunjukan track record dari individu yang diadili tiap tahun dalam rentang tahun 1300 s.d. 1850. Line chart berbentuk garis yang dapat menjelaskan tentang suatu data berdasarkan urutan waktu.

Setelah kita tahu tujuan, manfaat, dan hal yang akan kita lakukan, mari kita melangkah lebih lanjut untuk mempersiapkan pengerjaan project.

2. Background

2.1 Article

Pada tahun 1300 - 1850 an, praktik penuduhan seseorang memiliki ilmu sihir di Eropa lazim terjadi. Tuduhan ini sering digunakan oleh penguasa untuk mengendalikan masyarakat dan mengalihkan perhatian dari masalah yang lebih besar (sebagai pengalihan isu). Dalam artikel yang berjudul The political economy of witchcraft ini disajikan statistik jumlah orang yang dituduh memiliki ilmu sihir dan jumlah orang yang dieksekusi mati akibat tuduhan tersebut. Berikut adalah tampilan visualisasi yang akan di-replicate.

2.2 Import Library

Import beberapa library yang dibutuhkan untuk pembuatan replika visualisasi.

library(tidyverse) # data wrangling
library(ggplot2) # data visualization
library(dplyr) # data wrangling
library(grid) # create grid
library(gridExtra) # merge grid
library(png) # import plot to image
library(magick) # combine plot

3. Data Preprocessing

Sebelum membuat visualisasi, diperlukan proses Data Preprocessing untuk mengolah data terlebih dahulu untuk menyiapkan data yang bisa digunakan untuk visualisasi seperti mengubah tipe data ke bentuk yang sesuai, menghilangkan simbol, dan lain sebagainya.

3.1 Load Data

Pada kasus ini akan digunakan 2 data.

  • Dataset History diambil dari artikel Witch trials karya Peter T. Leeson dan Jacob W. Russ yang diterbitkan pada The Economic Journal (Table 1 Witch-trials Activity Across Countries, 1300–1850) yang berisi data negara di Eropa yang menjalankan proses pengadilan untuk orang-orang yang dituduh memiliki ilmu sihir.
  • Dataset Witch trials diambil dari Kaggle yang berisi data spesifik jumlah orang yang diadili pada rentang tahun 1300 - 1850.

Dataset history akan digunakan untuk membuat Bubble Chart dan Stacked Bar Chart. Sedangkan dataset witch trials digunakan untuk membuat Line Chart. Mari kita lakukan tahap preprocessing untuk kedua dataset tersebut.

A. Dataset History

history <- read.table("../data_input/history.csv", sep = " ", header = TRUE)
history

Penjelasan Data:

Berdasarkan tabel di atas dapat diketahui bahwa dataste history memiliki 15 data yang terdiri dari 8 kolom. Penjelasan dari kolom-kolom tersebut adalah sebagai berikut:

  • country = nama negara atau wilayah tempat pengadilan penyihir berlangsung.
  • population = jumlah populasi penduduk negara tersebut.
  • persons_tried = jumlah total individu yang diadili baik yang dihukum mati ataupun tidak.
  • per_cent_of_total_tried = persentase jumlah pengadilan pada negara tersebut dari total jumlah pengadilan di seluruh negara.
  • persons_tried_per_million = jumlah individu yang diadili per satu juta orang dalam populasi negara tersebut.
  • deaths = jumlah individu yang dihukum mati.
  • per_cent_of_total_deaths = persentase jumlah eksekusi pada negara tersebut dari total jumlah eksekusi yang terjadi di seluruh negara.
  • deaths_per_million = jumlah eksekusi per satu juta orang dalam populasi negara.

B. Dataset Witch trials

witch_trials <- read.csv("../data_input/witch_trials.csv")
witch_trials

Penjelasan Data:

Berdasarkan tabel di atas dapat diketahui bahwa dataste witch trials memiliki 10.940 data yang terdiri dari 12 kolom. Penjelasan dari kolom-kolom tersebut adalah sebagai berikut:

  • year = tahun.
  • decade = dekade (tiap 10 tahun).
  • century = abad.
  • tried = jumlah total individu yang diadili baik yang dihukum mati ataupun tidak.
  • deaths = jumlah individu yang dihukum mati.
  • city = nama kota.
  • gadm.adm2 = informasi geografi 1.
  • gadm.adm1 = informasi geografi 2.
  • gadm.adm0 = informasi geografi 0 (nama negara).
  • lon = longitude.
  • lat = latitude.
  • record.source = sumber data.

3.2 Subsetting

Tidak semua kolom akan digunakan untuk proses visualisasi. Oleh karena itu, untuk memudahkan pengolahan data, kolom-kolom yang tidak digunakan akan dihilangkan.

A. Dataset History

Pada dataset history, kolom yang akan digunakan yaitu country, persons_tried, persons_tried_per_million, dan deaths.

history = history %>% select(country, persons_tried, persons_tried_per_million, deaths)
history

B. Dataset Witch trials

Pada dataset witch trials, kolom yang akan digunakan yaitu decade, tried, dan deaths.

witch_trials = witch_trials %>% select(decade, tried, deaths)
witch_trials

3.3 Hapus Simbol

A. Dataset History

Terlihat pada tabel dataset history, bahwa terdapat pemisah ribuan dengan simbol koma (,). Hal ini perlu dihilangkan terlebih dahulu agar dapat dikonversi menjadi bentuk numerik.

history$persons_tried <- gsub(",", "", history$persons_tried)
history$persons_tried_per_million <- gsub(",", "", history$persons_tried_per_million)
history$deaths <- gsub(",", "", history$deaths)

Setelah dilakukan proses pembersihan pemisah ribuan, maka data history akan tampil sebagai berikut:

history

B. Dataset Witch trials

Tidak ada proses penghapusan simbol pada dataset witch trials.

3.4 Tipe Data

Tipe data setiap kolom perlu dicek untuk memastikan bahwa data-data yang ada memiliki tipe data yang tepat sehingga dapat dilakukan proses visualisasi yang sesuai dan data yang tersaji menjadi informatif.

A. Dataset History

str(history)
'data.frame':   15 obs. of  4 variables:
 $ country                  : chr  "Germany" "Switzerland" "France" "Scotland" ...
 $ persons_tried            : chr  "16474" "9796" "4159" "3563" ...
 $ persons_tried_per_million: chr  "1373" "9796" "225" "5090" ...
 $ deaths                   : chr  "6887" "5691" "1663" "190" ...

Insight

Dari struktur di atas, dapat diketahui bahwa beberapa kolom belum memiliki tipe data yang sesuai. Seperti kolom persons_tried, persons_tried_per_million, dan deaths yang masih memiliki tipe data chr yang seharusnya adalah numerik (num) dan kolom country yang harusnya adalah Factor. Untuk itu perlu dilakukan proses konversi tipe data yang sesuai.

history$country <- factor(history$country)
history$persons_tried <- as.numeric(history$persons_tried)
history$persons_tried_per_million <- as.numeric(history$persons_tried_per_million)
history$deaths <- as.numeric(history$deaths)

Setelah proses konversi tipe data, kemudian cek tipe data untuk tiap kolom kembali untuk memastikan bahwa kolom-kolom tersebut sudah memiliki tipe data yang tepat.

str(history)
'data.frame':   15 obs. of  4 variables:
 $ country                  : Factor w/ 15 levels "Belgium","England",..: 6 15 5 12 13 7 2 1 11 4 ...
 $ persons_tried            : num  16474 9796 4159 3563 1949 ...
 $ persons_tried_per_million: num  1373 9796 225 5090 229 ...
 $ deaths                   : num  6887 5691 1663 190 1 ...

Insight

Dilihat pada struktur di atas dapat disimpulkan bahwa tiap kolom sudah memiliki tipe data yang sesuai dan siap dilakukan proses selanjutnya.

B. Dataset Witch trials

str(witch_trials)
'data.frame':   10940 obs. of  3 variables:
 $ decade: int  1520 1530 1540 1580 1590 1600 1610 1620 1630 1640 ...
 $ tried : int  1 1 5 7 11 6 22 14 25 39 ...
 $ deaths: int  1 1 5 5 0 1 18 8 4 10 ...

Insight

Dilihat pada struktur di atas dapat disimpulkan bahwa kolom pada dataset witch trials sudah memiliki tipe data yang sesuai dan siap dilakukan proses selanjutnya.

3.5 Missing Value

Nilai kosong / missing value perlu ditangani agar data lengkap dan bisa digunakan untuk proses visualisasi.

A. Dataset History

Cek apakah dataset terdapat missing value atau tidak dengan menggunakan fungsi is.na().

sum(is.na(history))
[1] 0

Insight

Setelah dicek, dapat diketahui bahwa semua data telah lengkap dan tidak perlu dilakukan pengisian data kosong.

B. Dataset Witch Trials

Cek apakah dataset terdapat missing value atau tidak dengan menggunakan fungsi is.na().

sum(is.na(witch_trials))
[1] 3826

Insight

Setelah dicek, ternyata terdapat 3.826 data yang kosong. Untuk menangani ini, missing value akan diisi dengan nilai 0 yang artinya pada tahun tersebut tidak ada individu yang diadili.

# Isi missing value dengan 0
witch_trials[is.na(witch_trials)] <- 0

# Cek apakah masih ada missing value atau tidak
sum(is.na(witch_trials))
[1] 0

Insight

Setelah dilakukan proses pengisian nilai NA dengan 0, dapat diketahui bahwa dataset witch trials sudah lengkap dan tidak ada nilai 0.

3.6 Final Data

Berikut tampilan data setelah proses pre-processing dan siap digunakan untuk proses visualisasi.

A. Dataset History

history

B. Dataset Witch Trials

witch_trials

4. Create a Visualization

4.1 Bubble Chart

Untuk membuat Bubble Chart, hanya membutuhkan kolom persons_tried_per_million. Terlihat pada gambar referensi bahwa nilai pada bubble chart menggunakan nilai hasil formula persons_tried_per_million dibagi 10 lalu dibulatkan.

Berikut langkah-langkah yang perlu dilakukan:

  1. Mempersiapkan data persons_tried_per_million dari dataset history.
  2. Membagi data persons_tried_per_million dengan nilai 10 lalu dibulatkan menggunakan fungsi round().
  3. Membuat bubble chart.
# Mengambil data persons_tried_per_million
bubble_data <- history %>% select(persons_tried_per_million)
head(bubble_data)

Berdasarkan gambar referensi, nilai pada kolompersons_tried_per_million bukanlah nilai aslinya tetapi sudah dibagi menjadi 10.

# Membagi data dengan 10 lalu dibulatkan
bubble_data <- round(bubble_data / 10, 0)

# Proses membalik data dari yang teratas menjadi terbawah
bubble_data <- bubble_data %>%
  mutate(value_reserved = rev(persons_tried_per_million)) %>%
  select(-persons_tried_per_million) %>%
  rename(persons_tried_per_million = value_reserved)

# Menampilkan data
head(bubble_data)

Setelah data dipersiapkan, selanjutnya adalah proses untuk membuat Bubble Chart menggunakan fungsi ggplot().


Berikut langkah-langkah untuk membuat Bubble Chart.

  1. Mempersiapkan Data: Pertama, kita membuat sebuah tibble (struktur data dari package dplyr) yang akan menyimpan informasi untuk bubble chart. Di dalam tibble ini, kita menentukan nilai dari 3 parameter yaitu: x, y, dan size.

    • Parameter x \(\rightarrow\) diisi dengan nilai tetap 1 agar semua bubble terletak pada posisi yang sama secara horizontal.
    • Parameter y \(\rightarrow\) diisi dengan urutan dari 1 hingga banyaknya baris pada data, yang akan menjadi posisi vertikal dari setiap bubble.
    • Parameter size \(\rightarrow\) diisi data persons_tried_per_million.
  2. Membuat Bubble Chart: Selanjutnya, kita menggunakan fungsi ggplot() untuk membuat bubble chart. Di sini, kita mengatur mapping aesthetic (aes) untuk x, y, dan size dari data yang telah kita buat.

  3. Menambahkan Bubble: Dengan menggunakan fungsi geom_point(), kita menambahkan titik-titik (bubble) ke plot. Kita mengatur warna bubble menjadi coral, dengan transparansi (alpha) 0.7 dan ketebalan outline (stroke) sebesar 1.

  4. Menambahkan Label: Kita juga menambahkan label menggunakan fungsi geom_text(). Label ini akan menampilkan teks yang berada dekat dengan bubble, dengan posisi horizontal sedikit di sebelah kiri (x = 0.9) dan ukuran teks diatur menjadi 4 pt.

  5. Mengatur Rentang Ukuran Bubble: Untuk memastikan bahwa ukuran bubble terlihat proporsional, kita mengaturnya menggunakan fungsi scale_size_continuous() untuk mengatur rentang ukuran bubble antara 1 dan 18.

  6. Menerapkan Tema Minimalis: Dengan menggunakan theme_minimal(), kita mengubah tampilan plot menjadi lebih sederhana dan bersih.

  7. Mengatur Elemen Tampilan Plot: Kita melakukan beberapa penyesuaian pada elemen-elemen di plot menggunakan fungsi theme:

    • Menghapus judul sumbu x dan y, serta menghilangkan teks dan tanda pada sumbu menggunakan fungsi element_blank pada parameter axis.title.x, axis.text, dan axis.ticks.
    • Menghapus legend untuk membuat plot lebih bersih menggunaka niali none pada parameter legend.position.
    • Menghilangkan garis grid pada plot dengan cara menggunakan fungsi element_blank() pada parameter panel.grid.major, panel.grid.minor, dan panel.border.
    • Mengatur margin plot menjadi nol untuk memaksimalkan penggunaan ruang pada parameter plot.margin. Berisi 4 buah nilai yang memiliki urutan top, right, bottom, left.
  8. Menghapus Label/teks pada Sumbu X dan Y: Kita menambahkan labs() dengan nilai NULL untuk memastikan bahwa label pada sumbu x dan y tidak ditampilkan.

  9. Mengatur Rentang Sumbu X: Menggunakan xlim(), kita membatasi tampilan sumbu x antara 0 dan 1.5, untuk memastikan bubble terlihat jelas.

  10. Melakukan Cropping pada Plot: Terakhir, kita menggunakan coord_cartesian() untuk mengatur cropping sehingga hanya akan berfokus pada Bubble Chart saja.


Mari kita implementasikan ke dalam kode menggunakan bahasa R sebagai berikut:.

# Menentukan X, Y, dan Size untuk menentukan ukuran bubble
data <- tibble(
  x = 1,
  y = 1:nrow(bubble_data),
  size = c(bubble_data$persons_tried_per_million)
)

# Membuat bubble chart
bubble_chart <- ggplot(data, aes(x = x, y = y, size = size)) +
  # Menentukan warna, transparansi (aplha), dan lebar outline (stroke) bubble
  geom_point(color = "coral", alpha = 0.7, stroke = 1) +

  #Mengatur letak dan ukuran label
  geom_text(aes(x = 0.9, label = size), size = 4) +
    
  # Mengatur rentang ukuran bubble
  scale_size_continuous(range = c(1, 18)) + 
    
  # Mengatur agar tampilan plot minimalis
  theme_minimal() +
    
  # Mengatur beberapa element pada plot
  theme(
    # Menghapus judul, sumbu x, dan sumbu y
    axis.title.x = element_blank(),
    axis.text = element_blank(),
    axis.ticks = element_blank(),
    
    # Menghapus legend
    legend.position = "none",
    
    # Menghapus garis (grid)
    panel.grid.major = element_blank(),
    panel.grid.minor = element_blank(),
    panel.border = element_blank(),
    
    # Mengatur margin plot (top, right, bottom, left)
    plot.margin = margin(0, 0, 0, 0)
  ) +
    
  # Menghapus label pada sumbu X dan Y
  labs(
    x = NULL,
    y = NULL
  ) +
    
  # Mengatur range dari sumbu X
  xlim(0, 1.5) + 
    
  # Melakukan cropping
  coord_cartesian(xlim = c(0.9, 2))

# Menampilkan plot
bubble_chart

Setelah pembuatan bubble chart berhasil, saatnya untuk menyimpan plot tersebut untuk dikombinasikan dengan plot yang lain.

ggsave(filename = "../asset/bubble_chart.png", plot = bubble_chart, width = 6, height = 5, dpi = 300)

4.2 Stacked Bar Chart

Untuk membuat Stacked Bar Chart pada kasus ini hanya membutuhkan 4 data yaitu country, persons_tried, deaths, dan accused. Kolom accused berisi data orang yang diadili tetapi tidak dihukum mati. Kolom ini perlu kita buat sendiri dengan mengurangkan data pada kolom persons_tried dengan deaths.

Berikut langkah-langkah yang perlu dilakukan:

  1. Mempersiapkan data country, persons_tried, dan deaths dari dataset history.
  2. Membuat kolom accused dengan formula persons_tried - deaths.
  3. Mengubah data ke long format menggunakan fungsi pivot_longer.
  4. Membuat plot Stacked Bar Chart.
# Memilih data yang digunakan
data_plot = history %>% select(country, persons_tried,  deaths)

# Membuat kolom accused
data_plot$accused = data_plot$persons_tried - data_plot$deaths

# Menampilkan data
data_plot

Agar data kita bisa digunakan untuk visualisasi, perlu kita ubah formatnya dari wide format menjadi long format dengan fungsi pivot_longer.

# ubah format data dengan pivot_longer()
data_plot <- data_plot %>%
  pivot_longer(cols = c("accused", "deaths"), 
               names_to = "status", 
               values_to = "value")

# tampilkan data hasil format
print(data_plot)

Selanjutnya perlu kita ubah tipe data status menjadi kategorikal dengan menggunakan fungsi factor().

# Ubah tipe data ke factor dan mengubah urutan dari kolom status
data_plot <- data_plot %>%
  mutate(status = factor(status, levels = c("deaths", "accused")))

Setelah data dipersiapkan, selanjutnya adalah proses untuk membuat Stacked Bar Chart menggunakan fungsi ggplot().


Berikut langkah-langkah untuk membuat Stacked Bar Chart.

  1. Membuat Warna Kustom: Menentukan warna untuk membedakan antara kategori deaths dan accused.

  2. Membuat Stacked Bar Chart: Selanjutnya, kita mulai membuat diagram. Namun plot ini disajikan dalam bentuk horizontal sehingga seolah-olah sumbu x berada di sumbu y dan sebaliknya.

    • Sumbu X \(\rightarrow\) nama negara.
    • Sumbu Y \(\rightarrow\) jumlah individu yang diadili.
    • Warna batang akan mencerminkan kategori yang berbeda executed dan accused.
  3. Mengatur Lebar Batang: Kita mengatur lebar batang agar terlihat proporsional dan tidak terlalu lebar, sehingga memberikan sedikit ruang antara batang-batang yang berdekatan. Lebar batang ditentukan menggunakan parameter width.

  4. Mengimplementasikan Warna Kustom: Warna yang telah ditentukan sebelumnya diterapkan pada plot, sehingga memudahkan pemahaman visual mengenai kategori yang ada. Parameter yang diatur yaitu scale_fill_manual.

  5. Menebalkan Garis Horizontal: Sebuah garis horizontal ditambahkan pada sumbu Y di titik nol menggunakan parameter geom_hline. Paramter yintercept diisi dengan nilai sumbu X di mana garis akan ditempatkan. Sedangkan lwd untuk mengatur tebal garis.

  6. Menghapus Label pada sumbu X dan Y: Untuk mendapatkan tampilan yang lebih bersih dan minimalis, label pada sumbu X dan Y dihapus menggunakan parameter labs.

  7. Mengubah Orientasi Plot: Orientasi plot diubah dari vertikal menjadi horizontal menggunakan parameter coord_flip.

  8. Mengatur Elemen Plot: Menggunakan parameter theme, berikut beberapa elemen yang diatur yaitu:

    • Menghapus grid di dalam plot menggunakan element_blank pada parameter panel.grid.
    • Mengatur posisi dan ukuran dari legend accused dan executed.
    • Mengatur batas tepi (margin) di mulai dari top, right, bottom, left dalam satuan centimeter (cm).
  9. Mengatur teks Sumbu Y: Diatur agar memiliki range antara 0 sampai 17.500 dengan jarak 2.500 menggunakan parameter breaks pada bagian scale_y_continuos. Selain itu, terapkan pemisah ribuan menggunakan parameter labels yang diisi dengan nilai scales::comma.

  10. Mengatur Legenda: Terakhir, legend diatur agar ditampilkan dalam satu baris, menjadikannya lebih rapi dan mudah dibaca.


Mari kita implementasikan ke dalam kode menggunakan bahasa R sebagai berikut:.

# Membuat warna kustom
custom_colors <- c("deaths" = "#0098a0", "accused" = "#70cbc4")

# Membuat stacked bar
stacked_bar_chart <- ggplot(data_plot, aes(x = reorder(country, value), y = value, fill = status)) +
  
  # Mengatur lebar bar
  geom_bar(stat = "identity", width = 0.8) +
  
  # Implementasi warna kustom ke bar
  scale_fill_manual(values = custom_colors, labels=c('Executed', 'Accused')) +
  
  # Membuat horizontal line pada sumbu Y pada titik 0 dengan lebar 0.25
  geom_hline(yintercept = 0,
             lwd = 0.25) +
  
  # Menghapus label pada sumbu X dan Y
  labs(x = "", y = "") + 
  
  # Menghubah orientasi plot dari vertical ke horizontal
  coord_flip() +
  
  # Mengatur agar tampilan plot minimalis
  theme_minimal() + 
  
  # Mengatur beberapa element pada plot
  theme(
    # Mengatur warna teks pada sumbu X dan Y
    axis.text.x = element_text(color = "black"),  # Align labels to the lef
    axis.text.y = element_text(hjust = 0, color = "black"),  # Align labels to the left
    
    # Mengatur garis yang akan ditampilkan di tengah-tengah plot
    panel.grid.minor = element_blank(),
    panel.grid.major.y = element_blank(),
    panel.grid.major.x = element_line(size = 0.75, color = "#cbccce"),
    
    # Mengatur posisi dan ukuran dari legend
    legend.title = element_blank(),
    legend.position = c(0.3, 1.1),
    legend.margin = margin(0, 0, 15, 0),
    legend.key.height = unit(0.3, "cm"),
    legend.key.width = unit(0.7, "cm"),
    legend.key.spacing.x = unit(0.9, "cm"),
    
    # Mengatur margin dari plot secara umum
    plot.margin = margin(0.7, 4, 0.5, 0, "cm") 
  ) + 
  
  # Mengatur teks pada sumbu Y
  scale_y_continuous(
    # Mengatur nilai dimulai dari 0 sampai 17.500 dengan jarak 2.500
    breaks = seq(0, 17500, by = 2500),
    
    # Mengatur batas/limit teks yang ditampilkan pada sumbu Y
    limits = c(0, 17500), 
    
    # Mengurangi ruang kosong pada kanan kiri atas bawah plot
    expand = expansion(mult = c(0, 0)),
    
    # Menambah pemisah ribuan pada sumbu Y
    labels = scales::comma,
    
    # Mengatur posisi sumbu Y
    position = "right"
  ) +
  
  # Menatur agar legend horizontal
  guides(fill = guide_legend(nrow = 1, byrow = TRUE, reverse = TRUE))
  
# Menampilkan plot
stacked_bar_chart

Setelah pembuatan stacked chart berhasil, saatnya untuk menyimpan plot tersebut untuk dikombinasikan dengan plot yang lain.

ggsave(filename = "../asset/stacked_bar_chart.png", plot = stacked_bar_chart, width = 6, height = 4, dpi = 300)

4.3 Line Chart

Untuk membuat Line Chart, akan digunakan dataset witch trails. Sama seperti 2 plot sebelumnya, untuk membuat Line Chart juga akan memanfaatkan library dari ggplot. Namun sebelum itu, kita perlu mempersiapkan datanya terlebih dahulu untuk menjumlahkan total individu yang diadili tiap dekade.

Berikut langkah-langkah yang perlu dilakukan:

  1. Mempersiapkan data decade, deaths, dan tried dari dataset witch_trials.
  2. Lakukan proses grouping dengan fungsi group_by untuk menjumlahkan total individu yang dihukum mati dan tidak menggunakan fungsi summarise.
  3. Membuat plot Line Chart.
# Proses groupBy untuk menjumlahkan deaths dan tried untuk tiap dekade
data_line_chart <- witch_trials %>%
  group_by(decade) %>%
  summarise(
    deaths = sum(deaths),
    tried = sum(tried)
  )

# Menampilkan data
data_line_chart

Setelah data sudah berhasil dijumlahkan untuk setiap dekade, selanjutnya yaitu pembuatan line plot menggunakan fungsi ggplot.


Berikut langkah-langkah untuk membuat Stacked Bar Chart.

  1. Membuat fungsi custom_labels yang digunakan untuk menampilkan teks pada sumbu X. Akan menampilkan tahun secara lengkap jika tahun tersebut tidak diakhiri dengan angka 50. Tapi jika diakhiri angka 50, hanya akan ditampilkan angka 50. Misalnya: Tahun 1800 akan ditampilkan 1800. Tahun 1850 akan ditampilkan 50.

  2. Membuat Line Plot: Dengan menggunakan fungsi geom_line, akan dibuat 2 garis yaitu garis untuk data deaths yang akan diberi label executed dan garis untuk data tried yang diberi label accused.

  3. Menghapus Label pada sumbu X dan Y: Untuk mendapatkan tampilan yang lebih bersih dan minimalis, label pada sumbu X dan Y dihapus menggunakan parameter labs.

  4. Mengatur Sumbu X: Pada sumbu X dengan fungsi scale_x_continuos, kita mengatur beberapa hal sebagai berikut:

    • Membuat major breaks pada sumbu X dengan range 1300 sampai 1850 yang dipisahkan setiap 50 satuan.
    • Membuat minor breaks pada sumbu X dengan range 1300 sampai 1850 yang dipisahkan setiap 10 satuan.
    • Menuliskan teks label pada sumbu X dengan memanggil fungsi custom_labels yang sudah dibuat di atas.
    • Menampilkan garis kecil sepanjang sumbu X dengan menggunakan fungsi guide_axis.
  5. Mengatur Sumbu Y: Pada sumbu Y dengan fungsi scale_y_continuos, kita mengatur beberapa hal sebagai berikut:

    • Membuat major breaks pada sumbu Y dengan range 0 sampai 6000 yang dipisahkan setiap 1000 satuan.
    • Mengatur ruang kosong di sekitar plot menggunakan fungsi expansion.
    • Menuliskan teks label pada sumbu Y yang dipisahkan dengan koma menggunakan parameter scales::comma.
    • Mengatur agar posisi sumbu Y berada pada sisi kanan menggunakan fungsi position.
  6. Mengatur Elemen Plot: Menggunakan parameter theme, berikut beberapa elemen yang diatur yaitu:

    • Menghapus grid di dalam plot menggunakan element_blank pada parameter panel.grid.
    • Mengatur tebal dan panjang (mayor ticks dan minor ticks) menggunakan fungsi axis.ticks.
    • Mengatur agar warna background foto dengan warna putih menggunakan fungsi plot.background.
  7. Menambahkan Highlight: Kita menyoroti rentang tahun antara 1555 hingga 1648 dengan menambahkan persegi panjang berwarna coral transparan. Hal ini membantu menarik perhatian pada periode tertentu yang relevan dengan data. Untuk melakukan hal ini, digunakan parameter geom pada fungsi annotate.

    • xmin \(\rightarrow\) nilai pada sumbu X untuk memulai highlight.
    • xmax \(\rightarrow\) nilai pada sumbu X untuk mengakhiri highlight.
    • ymin \(\rightarrow\) nilai pada sumbu Y untuk menentukan titik awal tinggi highlight.
    • ymax \(\rightarrow\) nilai pada sumbu Y untuk menentukan titik akhir tinggi highlight.
    • fill \(\rightarrow\) menentukan warna.
    • alpha \(\rightarrow\) menentukan tingkat transparansi. Semakin kecil semakin transparan.
  8. Menambahkan teks yang lain: Menggunakan parameter text pada fungsi annotate untuk menuliskan teks executed, accused, protestant reformation, dan catholic counter-reformation.

  9. Menambahkan garis titik titik pada teks protestant reformation menggunakan fungsi geom_segment.

  10. Menambahkan teks pada sisi kiri atas menggunakan fungsi geom_label.


Mari kita implementasikan ke dalam kode menggunakan bahasa R sebagai berikut:.


# Fungsi untuk membuat label custom
custom_labels <- function(x) {
  # Membuat temporary variable
  labels <- ""
  
  # Looping untuk cek data dekade. Jika mengandung 50, hanya ditulis angka 50. Jika tidak, akan ditulis lengkap.
  for (i in seq_along(x)) {
    if (grepl("50$", as.character(x[i]))) {
      labels[i] <- "50"
    } else {
      labels[i] <- as.character(x[i])
    }
  }
  
  # Mengembalikan nilai dekade
  return(labels)
}

# Membuat line plot
line_chart <- ggplot(data_line_chart, aes(x = decade)) +
  # Membuat line untuk kolom deaths
  geom_line(aes(y = deaths), color = "#0098a0", size = 2) +
  
  # Membuat line untuk kolom tried
  geom_line(aes(y = tried), color = "#70cbc4", size = 2) +
  
  # Menghapus label pada sumbu X dan Y
  labs(x = "", y = "") + 
  
  # Mengatur agar tampilan plot minimalis
  theme_minimal() +
  
  scale_x_continuous(
    breaks = seq(1300, 1850, by = 50), # Tentukan titik utama
    minor_breaks = seq(1300, 1850, by = 10), # Tentukan titik minor
    # labels = seq(1300, 1850, by = 50),
    labels = custom_labels,
    guide = guide_axis(minor.ticks = TRUE),
  ) +
  
  # Mengatur teks pada sumbu Y
  scale_y_continuous(
    # Mengatur nilai dimulai dari 0 sampai 6.000 dengan jarak 1.000
    breaks = seq(0, 6000, by = 1000),
    
    # Mengurangi ruang kosong pada kanan kiri atas bawah plot
    expand = expansion(mult = c(0, 0)),
    
    # Menambah pemisah ribuan pada sumbu Y
    labels = scales::comma,
    
    # Mengatur posisi sumbu Y
    position = "right"
  ) +
  
  # Mengatur beberapa element pada plot
  theme(
    # Mengatur warna teks pada sumbu X dan Y
    axis.text.x = element_text(color = "black"),  # Align labels to the lef
    axis.text.y = element_text(hjust = 0, color = "black"),  # Align labels to the left
    
    # Mengatur garis yang akan ditampilkan di tengah-tengah plot
    panel.grid.minor.x = element_blank(),
    panel.grid.minor.y = element_blank(),
    panel.grid.major.x = element_blank(),
    panel.grid.major.y = element_line(size = 0.75, color = "#cbccce"),
    
    # Mengatur ketebalan ticks
    axis.ticks.x = element_line(size = 0.6),
    #axis.minor.ticks.x.bottom = element_line(size = 0.3),
    
    # Mengatur panjang ticks
    axis.ticks.length = unit(0.2, "cm"),
    axis.minor.ticks.length = unit(0.1, "cm"),
    
    # Mengatur tebal garis sumbu X
    axis.line.x = element_line(size = 0.6),
    
    # Mengatur margin dari plot secara umum
    plot.margin = margin(1.5, 0, 0, 1, "cm") ,
    
    plot.background = element_rect(fill = "white", colour = NA)
  ) + 
  
  # Membuat highlight pada rentang tahun 1560 s.d. 1650
  annotate(geom = "rect", xmin = 1555, xmax = 1648, ymin = 0, ymax = Inf,
           fill = "coral", alpha = 0.2) +
  
  annotate("text", x = 1585, y = 250, label = "Executed", size = 4.2, hjust = 0) + 
  annotate("text", x = 1710, y = 850, label = "Accused", size = 4.2, hjust = 0) +
  annotate("text", x = 1515, y = 2720, label = "PROTESTANT", size = 4.2, hjust = 1) +
  annotate("text", x = 1515, y = 2320, label = "REFORMATION", size = 4.2, hjust = 1) +
  annotate("text", x = 1650, y = 5720, label = "CATHOLIC", size = 4.2, hjust = 0) +
  annotate("text", x = 1650, y = 5320, label = "COUNTER-REFORMATION", size = 4.2, hjust = 0) +
  
  geom_segment(aes(x = 1515, xend = 1515, y = 0, yend = 2200), 
               linetype = "dotted", color = "coral", size = 1.3) +
  
  geom_label(aes(x = -Inf, y = Inf, label = "By decade"), 
             hjust = 0, vjust = 0.8, size = 5, label.size = 0) 

# Menampilkan plot
line_chart

Setelah pembuatan line chart berhasil, saatnya untuk menyimpan plot tersebut untuk dikombinasikan dengan plot yang lain.

ggsave(filename = "../asset/line_chart.png", plot = line_chart, width = 6.6, height = 4.3, dpi = 300)

5. Final Result

Langkah terakhir yaitu menggabungkan semua plot menjadi satu kesatuan plot. Beberapa langkah yang harus dilakukan yaitu:

  1. Siapkan bubble_chart.png, stacked_bar_chart.png, dan line_chart.png.
  2. Memotong bagian plot yang diperlukan saja (cropping).
  3. Menggabungkan semua chart menjadi sebuah chart.
  4. Menambah header garis merah sebagai ciri khas The Economist.
  5. Tambahkan teks lain yang diperlukan.

Pertama yaitu menyiapkan gambar dari bubble chart yang sudah disimpan. Dalam hal ini, kita hanya membutuhkan bagian angka dan visualisasinya saja. Oleh karena itu, perlu kita buang sisi lain yang tidak digunakan.

# Membaca gambar bubble chart
bubble_chart_img <- image_read("../asset/bubble_chart.png")

# Menyimpan lebar dan tinggi plot
img_width <- image_info(bubble_chart_img)$width
img_height <- image_info(bubble_chart_img)$height

# Menambah white space di atas plot
blank_space <- image_blank(width = img_width, height = 200, color = "white")

# Melakukan proses stacking (menambah) blank space ke bubble chart
bubble_chart_img <- image_append(c(blank_space, bubble_chart_img), stack = TRUE)

# proses cropping dengan ukuran yang dibutuhkan
cropped_bubble_chart_img <- image_crop(bubble_chart_img, "330x1700+0+0")

# Menyimpan gambar hasil crop
image_write(cropped_bubble_chart_img, "../asset/cropped_bubble_chart.png")

Proses selanjutnya yaitu menggabungkan bubble chart yang sudah di crop dengan stacked bar chart.

# Menyimpan hasil kombinasi
png("../asset/bubble_bar_combined.png", width = 10, height = 7, units = "in", res = 300)

# Membaca plot bubble chart
img1 <- rasterGrob(as.raster(readPNG("../asset/cropped_bubble_chart.png")), interpolate = TRUE)

# Membaca plot stacked bar chart
img2 <- rasterGrob(as.raster(readPNG("../asset/stacked_bar_chart.png")), interpolate = TRUE)

# Proses menggabungkan plot
grid.arrange(img1, img2, ncol = 2, widths = c(0.4, 3.35))

Selanjutnya yaitu menggabungkan bubble chart dan stacked bar chart yang sudah digabungkan sebelumnya dengan line chart dan disimpan dengan nama all_chart_combined.png.

# Menyimpan gambar hasil kombinasi 3 plot
png("../asset/all_chart_combined.png", width = 7.28, height = 4.3, units = "in", res = 500)

# Membaca gambar kombinasi bubble chart dan stacked bar chart dengan line chart
bubble_bar_combined <- rasterGrob(as.raster(readPNG("../asset/bubble_bar_combined.png")), interpolate = TRUE)
line_chart <- rasterGrob(as.raster(readPNG("../asset/line_chart.png")), interpolate = TRUE)

# Menyiapkan halaman baru untuk meletakan smua plot
grid.newpage()

# Mengatur posisi dan ukuran gambar dari bubble_bar_combined
pushViewport(viewport(x = 0.02, y = 0.49, width = 0.75, just = "left"))

# Meletakan gambar bubble_bar_combined ke tempat yang sudah disediakan
grid.draw(bubble_bar_combined)
upViewport()

# Mengatur posisi dan ukuran gambar dari line chart
pushViewport(viewport(x = 0.43, y = 0.335, width = 0.37))

# Meletakan gambar line chart ke tempat yang sudah disediakan
grid.draw(line_chart)
upViewport()

Menambah header garis merah sebagai ciri khas The Economist dan menambahkan teks lain yang diperlukan seperti footnote yang menjelaskan sumber data yang digunakan. Berikut langkah-langkah yang dilakukan:

  1. Menambahkan garis merah tipis sepanjang plot menggunakan fungsi grid.react.

  2. Menambahkan kotak garis merah di sebelah kiri atas. Setelah garis tipis menggunakan fungsi grid.react.

  3. Menambahkan judul utama dan sub judul pada plot menggunakan fungsi grid.text.

    • Nilai x merupakan posisi teks secara horizontal. Semakin besar maka posisi semakin ke kanan.
    • Nilai y merupakan posisi teks secara vertikal. Semakin besar maka semakin ke atas.
    • Fungsi gpar untuk mengatur ukuran huruf.
  4. Menambahkan footnote di bawah plot yang menjelaskan sumber data yang dirujuk dalam pembuatan plot. Untuk membuat footnote digunakan fungsi grid.text,

  5. Membuat kotak persegi panjang di atas bubble chart menggunakan fungsi grid.react.

    • Nilai x merupakan posisi teks secara horizontal. Semakin besar maka posisi semakin ke kanan.
    • Nilai y merupakan posisi teks secara vertikal. Semakin besar maka semakin ke atas.
    • Fungsi width untuk mengatur lebar box.
    • Fungsi height untuk mengatur tinggi box.
    • Fungsi gpar untuk menentukan warna dan tingkat transparansi warna.
  6. Menambahkan teks di dalam kotak persegi panjang menggunakan fungsi grid.text.

  7. Membuat segitiga terbalik berukuran kecil di bawah kotak menggunakan fungsi grid.polygon.


Mari kita implementasikan ke dalam kode menggunakan bahasa R sebagai berikut:.

# Menambah header garis merah sebagai ciri khas The Economist
grid.rect(x = 1, y = 0.997,
          hjust = 1, vjust = 0,
          gp = gpar(fill='#E5001C',lwd=0))

grid.rect(x = 0.013, y = 0.94,
          hjust = 1, vjust = 0,
          gp = gpar(fill='#E5001C',lwd=0))

# Menambah judul pada plot
grid.text("Double, double toil and trouble",
          x = 0.330, y = 0.935,
          hjust = 1, vjust = 0,
          gp = gpar(fontsize=11, fontface="bold"))

grid.text("European witchcraft, 1300-1850",
          x = 0.235, y = 0.9,
          hjust = 1, vjust = 0,
          gp = gpar(fontsize=7.8))

# Menambah footnote 
grid.text('Source: "Witch Trials", by Peter Leeson and Jacob Russ,',
          x = 0.308, y = 0.12,
          hjust = 1, vjust = 0,
          gp = gpar(fontsize=5.67))

grid.text("Economic Journal,",
          x = 0.403, y = 0.12,
          hjust = 1, vjust = 0,
          gp = gpar(fontface = "italic", fontsize=5.67))

grid.text('August, 2017',
          x = 0.47, y = 0.12,
          hjust = 1, vjust = 0,
          gp = gpar(fontsize=5.67))

grid.text('Economist.com',
          x = 0.08, y = 0.09,
          hjust = 1, vjust = 0,
          gp = gpar(fontsize=5.67, col = "#C0C0C0"))

# Membuat orange box di atas bubble chart
grid.rect(x = 0.095, y = 0.805,
          width = 0.135, height = 0.074,
          gp = gpar(fill = "coral", alpha = 0.7, col = NA))

# Menambahkan teks di dalam orange box
grid.text('Witchcraft trials',
          x = 0.143, y = 0.815,
          hjust = 1, vjust = 0,
          gp = gpar(fontsize=7))

grid.text('per 100,000 people',
          x = 0.153, y = 0.785,
          hjust = 1, vjust = 0,
          gp = gpar(fontsize=7))

# Membuat segitiga terbalik yang ada di ornage box
x <- c(0.0785, 0.0725, 0.0845) 
y <- c(0.761, 0.768, 0.768)

grid.polygon(x = x, y = y, gp = gpar(fill = "coral", alpha = 0.7, col = NA))

Terkahir yaitu melakukan cropping untuk menyeleksi bagian yang penting saja. White space yang berlebih akan dihapus (crop). Lalu hasil crop akan disimpan dengan nama final.png dan merupakan hasil akhir dari proyek ini.

# Membaca plot yang berisi 3 chart
final_image <- image_read("../asset/all_chart_combined.png")

# proses cropping dengan ukuran yang dibutuhkan
cropped_final_image <- image_crop(final_image, "2300x1980+0+0")

# Menyimpan gambar hasil crop
image_write(cropped_final_image, "../asset/final.png")

Conclusion

Project replika visualisasi dari majalah The Economist menggunakan library ggplot merupakan salah satu cara cepat bagi Anda yang ingin belajar bahasa pemrograman R. Karena dengan mengerjakan project ini, Anda akan menggunakan berbagai fungsi yang ada di R. Selain itu, dengan mempelajari visualisasi yang ada di majalah The Economist, kita bisa belajar bagaimana cara membuat visualisasi yang tepat, menarik, dan insightful yang bisa menjelaskan suatu data dengan baik dalam bentuk visual.

Setelah berhasil melakukan replika visualisasi dari majalah The Economist, didapat bahwa hasil visualisasi tidak 100% mirip dengan visualisasi yang menjadi sumber rujukan. Hal ini dikarenakan beberapa faktor seperti ketidaktahuan penulis tentang jenis font yang digunakan dan lainnya. Untuk itu, improvement yang bisa dilakukan yaitu menggunakan jenis font yang sama seperti pada sumber rujukan agar hasil replika semakin mirip (bila memungkinkan).

LS0tDQp0aXRsZTogIlJlcGxpY2F0ZSBFdXJvcGVhbiBXaXRjaGNyYWZ0IFBsb3QiDQphdXRob3I6IEFkaSBaYWVudWwgTXVzdGFxaW0NCmRhdGU6IDEyIFNlcHRlbWJlciAyMDI0DQpvdXRwdXQ6IA0KICBodG1sX25vdGVib29rOg0KICAgIHRoZW1lOiB1bml0ZWQNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZmxvYXQ6IHRydWUNCiAgICB0b2NfZGVwdGg6IDINCi0tLQ0KDQo8c3R5bGU+DQpib2R5IHsNCnRleHQtYWxpZ246IGp1c3RpZnkNCn0NCjwvc3R5bGU+DQoNCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQojIGNsZWFyLXVwIHRoZSBlbnZpcm9ubWVudA0Kcm0obGlzdCA9IGxzKCkpDQoNCiMgY2h1bmsgb3B0aW9ucw0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KA0KICBtZXNzYWdlID0gRkFMU0UsDQogIHdhcm5pbmcgPSBGQUxTRSwNCiAgZmlnLmFsaWduID0gImNlbnRlciINCikNCmBgYA0KDQoNCiMgMS4gSW50cm9kdWN0aW9uDQpbVGhlIEVjb25vbWlzdF0oaHR0cHM6Ly93d3cuZWNvbm9taXN0LmNvbS8pIGFkYWxhaCBtYWphbGFoIG1pbmdndWFuIHlhbmcgYmVyZm9rdXMgcGFkYSBiZXJpdGEgZGFuIGFuYWxpc2lzIHRlbnRhbmcgZWtvbm9taSwgcG9saXRpaywgYmlzbmlzLCB0ZWtub2xvZ2ksIGRhbiBidWRheWEuIERpZGlyaWthbiBwYWRhIHRhaHVuIDE4NDMsIG1hamFsYWggaW5pIGRpa2VuYWwgZGVuZ2FuIHN1ZHV0IHBhbmRhbmdueWEgeWFuZyBnbG9iYWwgZGFuIGFuYWxpc2lzIHlhbmcgbWVuZGFsYW0gdGVyaGFkYXAgaXN1LWlzdSB0ZXJraW5pLiBIYWwgbWVuYXJpayBkYXJpIG1hamFsYWggaW5pIHlhaXR1IGFkYW55YSB2aXN1YWxpc2FzaSBiZXJ1cGEgZ3JhZmlrL3Bsb3QgeWFuZyBkaXRhbXBpbGthbiBkZW5nYW4gbWVuYXJpay4gUGFkYSBhcnRpa2VsIGluaSwga2FtaSBha2FuIG1lbmNvYmEgbWVyZXBsaWthc2kgcGxvdCBkYXJpIFRoZSBFY29ub21pc3QgbWVuZ2d1bmFrYW4gbGlicmFyeSAqKmdncGxvdCoqLg0KDQrwn5OMICoqT2JqZWt0aWY6KioNCg0KUHJveWVrIGluaSBtZW5qZWxhc2thbiBiYWdhaW1hbmEgY2FyYSBtZW1idWF0IHJlcGxpa2EgdmlzdWFsaXNhc2kgZGFyaSBtYWphbGFoIFRoZSBFY29ub21pc3QuIEhhbCBpbmkgZGlsYWt1a2FuIHVudHVrIG1lbWJ1a3Rpa2FuIGJhaHdhIHZpc3VhbGlzYXNpIHRlcnNlYnV0IGRhcGF0IGtpdGEgYnVhdCBkYW4gcmVwbGlrYSBtZW5nZ3VuYWthbiBiYWhhc2EgcGVtcm9ncmFtYW4gUiBtZW5nZ3VuYWthbiBsaWJyYXJ5ICoqZ2dwbG90KiouDQoNCuKcqCAqKk1hbmZhYXQ6KioNCg0KU2V0ZWxhaCBBbmRhIG1lbWJhY2EgYXJ0aWtlbCBpbmksIGRpaGFyYXBrYW4gQW5kYToNCg0KMS4gTWVuZ2VydGkgZGFzYXItZGFzYXIgcGVtcm9ncmFtYW4gUi4NCjIuIE1lbmdlcnRpIGNhcmEgbWVtYnVhdCB2aXN1YWxpc2FzaSBtZW5nZ3VuYWthbiBsaWJyYXJ5ICoqZ2dwbG90KiouDQozLiBNZW5nZXJ0aSBqZW5pcyBwbG90IHlhbmcgc2VzdWFpIGJlcmRhc2Fya2FuIGRhdGEgeWFuZyBhZGEgZGFuIGRhcGF0IG1lbmdndW5ha2FuIGplbmlzIHBsb3QgdGVyc2VidXQgdW50dWsga2FzdXMgeWFuZyBiZXJiZWRhIGRpa2VtdWRpYW4gaGFyaS4NCg0K8J+SoSAqKlRhcmdldCBQZW1iYWNhOioqDQoNCkFydGlrZWwgaW5pIGNvY29rIGJhZ2kgc2lhcGEgc2FqYSB5YW5nIGluZ2luIG1lbXVsYWkgYmVsYWphciBiYWhhc2EgcGVtcm9ncmFtYW4gUi4gU2VsYWluIGl0dSwgYXJ0aWtlbCBpbmkganVnYSBiZXJtYW5mYWF0IGJhZ2kgc2VvcmFuZyBkYXRhIGFuYWxpcyBhZ2FyIG1lbmphZGkgcnVqdWthbiBkYWxhbSBtZW1idWF0IHZpc3VhbGlzYXNpIGRhbGFtIGJlcmJhZ2FpIGthc3VzIHV0YW1hbnlhIGppa2EgbWVuZ2d1bmFrYW4gbGlicmFyeSAqKmdncGxvdCoqLiBBcnRpa2VsIGluaSBiZXJtYW5mYWF0IHVudHVrIG9yYW5nLW9yYW5nIHlhbmcgdGVydGFyaWsgZGVuZ2FuIHZpc3VhbGlzYXNpIGRhdGEgZGFuIGluZ2luIG1lbXBlbGFqYXJpIGNhcmEgbWVyZXBsaWthc2kgZ3JhZmlrLWdyYWZpayB5YW5nIHRlcmtlbmFsIGRhbiBrb21wbGVrcy4gRGFsYW0gYXJ0aWtlbCBpbmkgZGlqZWxhc2thbiBmbG93IGRhcmkgcGVtYmVyc2loYW4gZGF0YSwgbWVueWlhcGthbiBkYXRhIHVudHVrIHBlbWJ1YXRhbiBwbG90LCBwZW1idWF0YW4gcGxvdCBpdHUgc2VuZGlyaSwgZGFuIG1lbmdnYWJ1bmdrYW4gYmViZXJhcGEgcGxvdCBtZW5qYWRpIDEuIEhhbCBpbmkgYmlzYSBkaWphZGlrYW4gY29udG9oIGppa2EgYWRhIHBlbWJhY2EgeWFuZyBha2FuIG1lbGFrdWthbiBoYWwgeWFuZyBzYW1hLg0KDQrwn5OKICoqSmVuaXMgUGxvdDoqKg0KDQpBcnRpa2VsIGluaSBha2FuIG1lbWJ1YXQgc2VidWFoIHZpc3VhbGlzYXNpIHlhbmcgdGVyZGlyaSBkYXJpIGtvbWJpbmFzaSAzIHBsb3Qgc2ViYWdhaSBiZXJpa3V0Og0KDQoxLiAqKkJ1YmJsZSBDaGFydCoqICRccmlnaHRhcnJvdyQgbWVuYW1waWxrYW4ganVtbGFoIGluZGl2aWR1IHlhbmcgZGlhZGlsaS4gQnViYmxlIGNoYXJ0IG1lbWlsaWtpIGJlbnR1ayBiZXJ1cGEgbGluZ2thcmFuLiBTZW1ha2luIGx1YXMgbGluZ2thcmFuLCBtYWthIG5pbGFpbnlhIHNlbWFraW4gdGluZ2dpLg0KMi4gKipTdGFja2VkIEJhciBDaGFydCoqICRccmlnaHRhcnJvdyQgbWVuYW1waWxrYW4ganVtbGFoIGluZGl2aWR1IHlhbmcgZGlhZGlsaSBiYWlrIG1lbmRhcGF0IGh1a3VtYW4gbWF0aSAoZXhlY3V0ZWQpIGF0YXUgdGlkYWsgZGlodWt1bSBtYXRpIChhY2N1c2VkKSBwYWRhIHN1YXR1IG5lZ2FyYS4gU3RhY2tlZCBiYXIgY2hhcnQgYXRhdSBkaWFncmFtIGJhdGFuZyBiZXJ0dW1wdWsgc2VwZXJ0aSBuYW1hbnlhIGRpYWdyYW0gdGVyc2VidXQgYmVyYmVudHVrIGRpYWdyYW0gYmF0YW5nIHlhbmcgdGVyZGlyaSBkYXJpIDIga2F0ZWdvcmkgKGV4ZWN1dGVkIGRhbiBhY2N1c2VkKSB5YW5nIGRpc3VzdW4gc2VjYXJhIGJlcnR1bXB1ay4NCjMuICoqTGluZSBDaGFydCoqICRccmlnaHRhcnJvdyQgbWVuYW1waWxrYW4gZ3JhZmlrIGdhcmlzIHlhbmcgbWVudW5qdWthbiB0cmFjayByZWNvcmQgZGFyaSBpbmRpdmlkdSB5YW5nIGRpYWRpbGkgdGlhcCB0YWh1biBkYWxhbSByZW50YW5nIHRhaHVuIDEzMDAgcy5kLiAxODUwLiBMaW5lIGNoYXJ0IGJlcmJlbnR1ayBnYXJpcyB5YW5nIGRhcGF0IG1lbmplbGFza2FuIHRlbnRhbmcgc3VhdHUgZGF0YSBiZXJkYXNhcmthbiB1cnV0YW4gd2FrdHUuDQoNCg0KU2V0ZWxhaCBraXRhIHRhaHUgdHVqdWFuLCBtYW5mYWF0LCBkYW4gaGFsIHlhbmcgYWthbiBraXRhIGxha3VrYW4sIG1hcmkga2l0YSBtZWxhbmdrYWggbGViaWggbGFuanV0IHVudHVrIG1lbXBlcnNpYXBrYW4gcGVuZ2VyamFhbiBwcm9qZWN0Lg0KDQojIDIuIEJhY2tncm91bmQgey50YWJzZXR9DQojIyAyLjEgQXJ0aWNsZQ0KUGFkYSB0YWh1biAxMzAwIC0gMTg1MCBhbiwgcHJha3RpayBwZW51ZHVoYW4gc2VzZW9yYW5nIG1lbWlsaWtpIGlsbXUgc2loaXIgZGkgRXJvcGEgbGF6aW0gdGVyamFkaS4gVHVkdWhhbiBpbmkgc2VyaW5nIGRpZ3VuYWthbiBvbGVoIHBlbmd1YXNhIHVudHVrIG1lbmdlbmRhbGlrYW4gbWFzeWFyYWthdCBkYW4gbWVuZ2FsaWhrYW4gcGVyaGF0aWFuIGRhcmkgbWFzYWxhaCB5YW5nIGxlYmloIGJlc2FyIChzZWJhZ2FpIHBlbmdhbGloYW4gaXN1KS4gRGFsYW0gYXJ0aWtlbCB5YW5nIGJlcmp1ZHVsIFtUaGUgcG9saXRpY2FsIGVjb25vbXkgb2Ygd2l0Y2hjcmFmdF0oaHR0cHM6Ly93d3cuZWNvbm9taXN0LmNvbS9ncmFwaGljLWRldGFpbC8yMDE3LzEwLzMxL3RoZS1wb2xpdGljYWwtZWNvbm9teS1vZi13aXRjaGNyYWZ0KSBpbmkgZGlzYWppa2FuIHN0YXRpc3RpayBqdW1sYWggb3JhbmcgeWFuZyBkaXR1ZHVoIG1lbWlsaWtpIGlsbXUgc2loaXIgZGFuIGp1bWxhaCBvcmFuZyB5YW5nIGRpZWtzZWt1c2kgbWF0aSBha2liYXQgdHVkdWhhbiB0ZXJzZWJ1dC4gQmVyaWt1dCBhZGFsYWggdGFtcGlsYW4gdmlzdWFsaXNhc2kgeWFuZyBha2FuIGRpLXJlcGxpY2F0ZS4NCg0KIVtdKC4uL2Fzc2V0L0dyYXBoLmpwZykNCg0KIyMgMi4yIEltcG9ydCBMaWJyYXJ5DQpJbXBvcnQgYmViZXJhcGEgbGlicmFyeSB5YW5nIGRpYnV0dWhrYW4gdW50dWsgcGVtYnVhdGFuIHJlcGxpa2EgdmlzdWFsaXNhc2kuDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpsaWJyYXJ5KHRpZHl2ZXJzZSkgIyBkYXRhIHdyYW5nbGluZw0KbGlicmFyeShnZ3Bsb3QyKSAjIGRhdGEgdmlzdWFsaXphdGlvbg0KbGlicmFyeShkcGx5cikgIyBkYXRhIHdyYW5nbGluZw0KbGlicmFyeShncmlkKSAjIGNyZWF0ZSBncmlkDQpsaWJyYXJ5KGdyaWRFeHRyYSkgIyBtZXJnZSBncmlkDQpsaWJyYXJ5KHBuZykgIyBpbXBvcnQgcGxvdCB0byBpbWFnZQ0KbGlicmFyeShtYWdpY2spICMgY29tYmluZSBwbG90DQpgYGANCg0KDQojIDMuIERhdGEgUHJlcHJvY2Vzc2luZyB7LnRhYnNldH0NCg0KU2ViZWx1bSBtZW1idWF0IHZpc3VhbGlzYXNpLCBkaXBlcmx1a2FuIHByb3NlcyBEYXRhIFByZXByb2Nlc3NpbmcgdW50dWsgbWVuZ29sYWggZGF0YSB0ZXJsZWJpaCBkYWh1bHUgdW50dWsgbWVueWlhcGthbiBkYXRhIHlhbmcgYmlzYSBkaWd1bmFrYW4gdW50dWsgdmlzdWFsaXNhc2kgc2VwZXJ0aSBtZW5ndWJhaCB0aXBlIGRhdGEga2UgYmVudHVrIHlhbmcgc2VzdWFpLCBtZW5naGlsYW5na2FuIHNpbWJvbCwgZGFuIGxhaW4gc2ViYWdhaW55YS4NCg0KIyMgMy4xIExvYWQgRGF0YQ0KDQpQYWRhIGthc3VzIGluaSBha2FuIGRpZ3VuYWthbiAyIGRhdGEuIA0KDQoqICoqRGF0YXNldCBIaXN0b3J5KiogZGlhbWJpbCBkYXJpIGFydGlrZWwgWypXaXRjaCB0cmlhbHMqXShodHRwczovL29ubGluZWxpYnJhcnkud2lsZXkuY29tL2RvaS9hYnMvMTAuMTExMS9lY29qLjEyNDk4KSBrYXJ5YSBQZXRlciBULiBMZWVzb24gZGFuIEphY29iIFcuIFJ1c3MgeWFuZyBkaXRlcmJpdGthbiBwYWRhICoqVGhlIEVjb25vbWljIEpvdXJuYWwqKiAoKlRhYmxlIDEgV2l0Y2gtdHJpYWxzIEFjdGl2aXR5IEFjcm9zcyBDb3VudHJpZXMqLCAxMzAw4oCTMTg1MCkgeWFuZyBiZXJpc2kgZGF0YSBuZWdhcmEgZGkgRXJvcGEgeWFuZyBtZW5qYWxhbmthbiBwcm9zZXMgcGVuZ2FkaWxhbiB1bnR1ayBvcmFuZy1vcmFuZyB5YW5nIGRpdHVkdWggbWVtaWxpa2kgaWxtdSBzaWhpci4NCiogKipEYXRhc2V0IFdpdGNoIHRyaWFscyoqICBkaWFtYmlsIGRhcmkgWypLYWdnbGUqXShodHRwczovL3d3dy5rYWdnbGUuY29tL2RhdGFzZXRzL21pY2hhZWxicnlhbnRkcy93aXRjaC10cmlhbHMpIHlhbmcgYmVyaXNpIGRhdGEgc3Blc2lmaWsganVtbGFoIG9yYW5nIHlhbmcgZGlhZGlsaSBwYWRhIHJlbnRhbmcgdGFodW4gMTMwMCAtIDE4NTAuDQoNCkRhdGFzZXQgaGlzdG9yeSBha2FuIGRpZ3VuYWthbiB1bnR1ayBtZW1idWF0ICoqQnViYmxlIENoYXJ0KiogZGFuICoqU3RhY2tlZCBCYXIgQ2hhcnQqKi4gU2VkYW5na2FuIGRhdGFzZXQgd2l0Y2ggdHJpYWxzIGRpZ3VuYWthbiB1bnR1ayBtZW1idWF0ICoqTGluZSBDaGFydCoqLiBNYXJpIGtpdGEgbGFrdWthbiB0YWhhcCBwcmVwcm9jZXNzaW5nIHVudHVrIGtlZHVhIGRhdGFzZXQgdGVyc2VidXQuDQoNCiMjIyBBLiBEYXRhc2V0IEhpc3RvcnkNCg0KYGBge3Igd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0NCmhpc3RvcnkgPC0gcmVhZC50YWJsZSgiLi4vZGF0YV9pbnB1dC9oaXN0b3J5LmNzdiIsIHNlcCA9ICIgIiwgaGVhZGVyID0gVFJVRSkNCmhpc3RvcnkNCmBgYA0KKipQZW5qZWxhc2FuIERhdGE6KioNCg0KQmVyZGFzYXJrYW4gdGFiZWwgZGkgYXRhcyBkYXBhdCBkaWtldGFodWkgYmFod2EgZGF0YXN0ZSBoaXN0b3J5IG1lbWlsaWtpIDE1IGRhdGEgeWFuZyB0ZXJkaXJpIGRhcmkgOCBrb2xvbS4gUGVuamVsYXNhbiBkYXJpIGtvbG9tLWtvbG9tIHRlcnNlYnV0IGFkYWxhaCBzZWJhZ2FpIGJlcmlrdXQ6DQoNCiogKipjb3VudHJ5KiogPSBuYW1hIG5lZ2FyYSBhdGF1IHdpbGF5YWggdGVtcGF0IHBlbmdhZGlsYW4gcGVueWloaXIgYmVybGFuZ3N1bmcuDQoqICoqcG9wdWxhdGlvbioqID0ganVtbGFoIHBvcHVsYXNpIHBlbmR1ZHVrIG5lZ2FyYSB0ZXJzZWJ1dC4NCiogKipwZXJzb25zX3RyaWVkKiogPSBqdW1sYWggdG90YWwgaW5kaXZpZHUgeWFuZyBkaWFkaWxpIGJhaWsgeWFuZyBkaWh1a3VtIG1hdGkgYXRhdXB1biB0aWRhay4NCiogKipwZXJfY2VudF9vZl90b3RhbF90cmllZCoqID0gcGVyc2VudGFzZSBqdW1sYWggcGVuZ2FkaWxhbiBwYWRhIG5lZ2FyYSB0ZXJzZWJ1dCBkYXJpIHRvdGFsIGp1bWxhaCBwZW5nYWRpbGFuIGRpIHNlbHVydWggbmVnYXJhLg0KKiAqKnBlcnNvbnNfdHJpZWRfcGVyX21pbGxpb24qKiA9IGp1bWxhaCBpbmRpdmlkdSB5YW5nIGRpYWRpbGkgcGVyIHNhdHUganV0YSBvcmFuZyBkYWxhbSBwb3B1bGFzaSBuZWdhcmEgdGVyc2VidXQuDQoqICoqZGVhdGhzKiogPSBqdW1sYWggaW5kaXZpZHUgeWFuZyBkaWh1a3VtIG1hdGkuDQoqICoqcGVyX2NlbnRfb2ZfdG90YWxfZGVhdGhzKiogPSBwZXJzZW50YXNlIGp1bWxhaCBla3Nla3VzaSBwYWRhIG5lZ2FyYSB0ZXJzZWJ1dCBkYXJpIHRvdGFsIGp1bWxhaCBla3Nla3VzaSB5YW5nIHRlcmphZGkgZGkgc2VsdXJ1aCBuZWdhcmEuDQoqICoqZGVhdGhzX3Blcl9taWxsaW9uKiogPSBqdW1sYWggZWtzZWt1c2kgcGVyIHNhdHUganV0YSBvcmFuZyBkYWxhbSBwb3B1bGFzaSBuZWdhcmEuDQoNCiMjIyBCLiBEYXRhc2V0IFdpdGNoIHRyaWFscw0KDQpgYGB7ciB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQ0Kd2l0Y2hfdHJpYWxzIDwtIHJlYWQuY3N2KCIuLi9kYXRhX2lucHV0L3dpdGNoX3RyaWFscy5jc3YiKQ0Kd2l0Y2hfdHJpYWxzDQpgYGANCioqUGVuamVsYXNhbiBEYXRhOioqDQoNCkJlcmRhc2Fya2FuIHRhYmVsIGRpIGF0YXMgZGFwYXQgZGlrZXRhaHVpIGJhaHdhIGRhdGFzdGUgd2l0Y2ggdHJpYWxzIG1lbWlsaWtpIDEwLjk0MCBkYXRhIHlhbmcgdGVyZGlyaSBkYXJpIDEyIGtvbG9tLiBQZW5qZWxhc2FuIGRhcmkga29sb20ta29sb20gdGVyc2VidXQgYWRhbGFoIHNlYmFnYWkgYmVyaWt1dDoNCg0KKiAqKnllYXIqKiA9IHRhaHVuLg0KKiAqKmRlY2FkZSoqID0gZGVrYWRlICh0aWFwIDEwIHRhaHVuKS4NCiogKipjZW50dXJ5KiogPSBhYmFkLg0KKiAqKnRyaWVkKiogPSBqdW1sYWggdG90YWwgaW5kaXZpZHUgeWFuZyBkaWFkaWxpIGJhaWsgeWFuZyBkaWh1a3VtIG1hdGkgYXRhdXB1biB0aWRhay4NCiogKipkZWF0aHMqKiA9IGp1bWxhaCBpbmRpdmlkdSB5YW5nIGRpaHVrdW0gbWF0aS4NCiogKipjaXR5KiogPSBuYW1hIGtvdGEuDQoqICoqZ2FkbS5hZG0yKiogPSBpbmZvcm1hc2kgZ2VvZ3JhZmkgMS4NCiogKipnYWRtLmFkbTEqKiA9IGluZm9ybWFzaSBnZW9ncmFmaSAyLg0KKiAqKmdhZG0uYWRtMCoqID0gaW5mb3JtYXNpIGdlb2dyYWZpIDAgKG5hbWEgbmVnYXJhKS4NCiogKipsb24qKiA9IGxvbmdpdHVkZS4NCiogKipsYXQqKiA9IGxhdGl0dWRlLg0KKiAqKnJlY29yZC5zb3VyY2UqKiA9IHN1bWJlciBkYXRhLg0KDQojIyAzLjIgU3Vic2V0dGluZw0KVGlkYWsgc2VtdWEga29sb20gYWthbiBkaWd1bmFrYW4gdW50dWsgcHJvc2VzIHZpc3VhbGlzYXNpLiBPbGVoIGthcmVuYSBpdHUsIHVudHVrIG1lbXVkYWhrYW4gcGVuZ29sYWhhbiBkYXRhLCBrb2xvbS1rb2xvbSB5YW5nIHRpZGFrIGRpZ3VuYWthbiBha2FuIGRpaGlsYW5na2FuLg0KDQojIyMgQS4gRGF0YXNldCBIaXN0b3J5DQpQYWRhIGRhdGFzZXQgaGlzdG9yeSwga29sb20geWFuZyBha2FuIGRpZ3VuYWthbiB5YWl0dSBgY291bnRyeWAsIGBwZXJzb25zX3RyaWVkYCwgYHBlcnNvbnNfdHJpZWRfcGVyX21pbGxpb25gLCBkYW4gYGRlYXRoc2AuDQoNCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9DQpoaXN0b3J5IDwtIGhpc3RvcnkgJT4lIHNlbGVjdChjb3VudHJ5LCBwZXJzb25zX3RyaWVkLCBwZXJzb25zX3RyaWVkX3Blcl9taWxsaW9uLCBkZWF0aHMpDQpoaXN0b3J5DQpgYGANCg0KIyMjIEIuIERhdGFzZXQgV2l0Y2ggdHJpYWxzDQpQYWRhIGRhdGFzZXQgd2l0Y2ggdHJpYWxzLCBrb2xvbSB5YW5nIGFrYW4gZGlndW5ha2FuIHlhaXR1IGBkZWNhZGVgLCBgdHJpZWRgLCBkYW4gYGRlYXRoc2AuDQoNCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9DQp3aXRjaF90cmlhbHMgPC0gd2l0Y2hfdHJpYWxzICU+JSBzZWxlY3QoZGVjYWRlLCB0cmllZCwgZGVhdGhzKQ0Kd2l0Y2hfdHJpYWxzDQpgYGANCg0KDQojIyAzLjMgSGFwdXMgU2ltYm9sDQojIyMgQS4gRGF0YXNldCBIaXN0b3J5DQoNClRlcmxpaGF0IHBhZGEgdGFiZWwgZGF0YXNldCBoaXN0b3J5LCBiYWh3YSB0ZXJkYXBhdCBwZW1pc2FoIHJpYnVhbiBkZW5nYW4gc2ltYm9sIGtvbWEgKCwpLiBIYWwgaW5pIHBlcmx1IGRpaGlsYW5na2FuIHRlcmxlYmloIGRhaHVsdSBhZ2FyIGRhcGF0IGRpa29udmVyc2kgbWVuamFkaSBiZW50dWsgbnVtZXJpay4NCg0KYGBge3Igd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0NCmhpc3RvcnkkcGVyc29uc190cmllZCA8LSBnc3ViKCIsIiwgIiIsIGhpc3RvcnkkcGVyc29uc190cmllZCkNCmhpc3RvcnkkcGVyc29uc190cmllZF9wZXJfbWlsbGlvbiA8LSBnc3ViKCIsIiwgIiIsIGhpc3RvcnkkcGVyc29uc190cmllZF9wZXJfbWlsbGlvbikNCmhpc3RvcnkkZGVhdGhzIDwtIGdzdWIoIiwiLCAiIiwgaGlzdG9yeSRkZWF0aHMpDQpgYGANCg0KU2V0ZWxhaCBkaWxha3VrYW4gcHJvc2VzIHBlbWJlcnNpaGFuIHBlbWlzYWggcmlidWFuLCBtYWthIGRhdGEgYGhpc3RvcnlgIGFrYW4gdGFtcGlsIHNlYmFnYWkgYmVyaWt1dDoNCg0KYGBge3Igd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0NCmhpc3RvcnkNCmBgYA0KDQojIyMgQi4gRGF0YXNldCBXaXRjaCB0cmlhbHMNClRpZGFrIGFkYSBwcm9zZXMgcGVuZ2hhcHVzYW4gc2ltYm9sIHBhZGEgZGF0YXNldCB3aXRjaCB0cmlhbHMuDQoNCiMjIDMuNCBUaXBlIERhdGENCg0KVGlwZSBkYXRhIHNldGlhcCBrb2xvbSBwZXJsdSBkaWNlayB1bnR1ayBtZW1hc3Rpa2FuIGJhaHdhIGRhdGEtZGF0YSB5YW5nIGFkYSBtZW1pbGlraSB0aXBlIGRhdGEgeWFuZyB0ZXBhdCBzZWhpbmdnYSBkYXBhdCBkaWxha3VrYW4gcHJvc2VzIHZpc3VhbGlzYXNpIHlhbmcgc2VzdWFpIGRhbiBkYXRhIHlhbmcgdGVyc2FqaSBtZW5qYWRpIGluZm9ybWF0aWYuDQoNCiMjIyBBLiBEYXRhc2V0IEhpc3RvcnkNCg0KYGBge3Igd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0NCnN0cihoaXN0b3J5KQ0KYGBgDQoqKkluc2lnaHQqKg0KDQpEYXJpIHN0cnVrdHVyIGRpIGF0YXMsIGRhcGF0IGRpa2V0YWh1aSBiYWh3YSBiZWJlcmFwYSBrb2xvbSBiZWx1bSBtZW1pbGlraSB0aXBlIGRhdGEgeWFuZyBzZXN1YWkuIFNlcGVydGkga29sb20gYHBlcnNvbnNfdHJpZWRgLCBgcGVyc29uc190cmllZF9wZXJfbWlsbGlvbmAsIGRhbiBgZGVhdGhzYCB5YW5nIG1hc2loIG1lbWlsaWtpIHRpcGUgZGF0YSBjaHIgeWFuZyBzZWhhcnVzbnlhIGFkYWxhaCBudW1lcmlrIChudW0pIGRhbiBrb2xvbSBgY291bnRyeWAgeWFuZyBoYXJ1c255YSBhZGFsYWggRmFjdG9yLiBVbnR1ayBpdHUgcGVybHUgZGlsYWt1a2FuIHByb3NlcyBrb252ZXJzaSB0aXBlIGRhdGEgeWFuZyBzZXN1YWkuDQoNCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9DQpoaXN0b3J5JGNvdW50cnkgPC0gZmFjdG9yKGhpc3RvcnkkY291bnRyeSkNCmhpc3RvcnkkcGVyc29uc190cmllZCA8LSBhcy5udW1lcmljKGhpc3RvcnkkcGVyc29uc190cmllZCkNCmhpc3RvcnkkcGVyc29uc190cmllZF9wZXJfbWlsbGlvbiA8LSBhcy5udW1lcmljKGhpc3RvcnkkcGVyc29uc190cmllZF9wZXJfbWlsbGlvbikNCmhpc3RvcnkkZGVhdGhzIDwtIGFzLm51bWVyaWMoaGlzdG9yeSRkZWF0aHMpDQpgYGANCg0KU2V0ZWxhaCBwcm9zZXMga29udmVyc2kgdGlwZSBkYXRhLCBrZW11ZGlhbiBjZWsgdGlwZSBkYXRhIHVudHVrIHRpYXAga29sb20ga2VtYmFsaSB1bnR1ayBtZW1hc3Rpa2FuIGJhaHdhIGtvbG9tLWtvbG9tIHRlcnNlYnV0IHN1ZGFoIG1lbWlsaWtpIHRpcGUgZGF0YSB5YW5nIHRlcGF0Lg0KDQpgYGB7ciB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQ0Kc3RyKGhpc3RvcnkpDQpgYGANCioqSW5zaWdodCoqDQoNCkRpbGloYXQgcGFkYSBzdHJ1a3R1ciBkaSBhdGFzIGRhcGF0IGRpc2ltcHVsa2FuIGJhaHdhIHRpYXAga29sb20gc3VkYWggbWVtaWxpa2kgdGlwZSBkYXRhIHlhbmcgc2VzdWFpIGRhbiBzaWFwIGRpbGFrdWthbiBwcm9zZXMgc2VsYW5qdXRueWEuDQoNCiMjIyBCLiBEYXRhc2V0IFdpdGNoIHRyaWFscw0KDQpgYGB7ciB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQ0Kc3RyKHdpdGNoX3RyaWFscykNCmBgYA0KKipJbnNpZ2h0KioNCg0KRGlsaWhhdCBwYWRhIHN0cnVrdHVyIGRpIGF0YXMgZGFwYXQgZGlzaW1wdWxrYW4gYmFod2Ega29sb20gcGFkYSBkYXRhc2V0IHdpdGNoIHRyaWFscyBzdWRhaCBtZW1pbGlraSB0aXBlIGRhdGEgeWFuZyBzZXN1YWkgZGFuIHNpYXAgZGlsYWt1a2FuIHByb3NlcyBzZWxhbmp1dG55YS4NCg0KDQojIyAzLjUgTWlzc2luZyBWYWx1ZQ0KTmlsYWkga29zb25nIC8gKm1pc3NpbmcgdmFsdWUqIHBlcmx1IGRpdGFuZ2FuaSBhZ2FyIGRhdGEgbGVuZ2thcCBkYW4gYmlzYSBkaWd1bmFrYW4gdW50dWsgcHJvc2VzIHZpc3VhbGlzYXNpLg0KDQojIyMgQS4gRGF0YXNldCBIaXN0b3J5DQpDZWsgYXBha2FoIGRhdGFzZXQgdGVyZGFwYXQgbWlzc2luZyB2YWx1ZSBhdGF1IHRpZGFrIGRlbmdhbiBtZW5nZ3VuYWthbiBmdW5nc2kgYGlzLm5hKClgLg0KDQpgYGB7ciB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQ0Kc3VtKGlzLm5hKGhpc3RvcnkpKQ0KYGBgDQoqKkluc2lnaHQqKg0KDQpTZXRlbGFoIGRpY2VrLCBkYXBhdCBkaWtldGFodWkgYmFod2Egc2VtdWEgZGF0YSB0ZWxhaCBsZW5na2FwIGRhbiB0aWRhayBwZXJsdSBkaWxha3VrYW4gcGVuZ2lzaWFuIGRhdGEga29zb25nLg0KDQojIyMgQi4gRGF0YXNldCBXaXRjaCBUcmlhbHMNCg0KQ2VrIGFwYWthaCBkYXRhc2V0IHRlcmRhcGF0IG1pc3NpbmcgdmFsdWUgYXRhdSB0aWRhayBkZW5nYW4gbWVuZ2d1bmFrYW4gZnVuZ3NpIGBpcy5uYSgpYC4NCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9DQpzdW0oaXMubmEod2l0Y2hfdHJpYWxzKSkNCmBgYA0KKipJbnNpZ2h0KioNCg0KU2V0ZWxhaCBkaWNlaywgdGVybnlhdGEgdGVyZGFwYXQgMy44MjYgZGF0YSB5YW5nIGtvc29uZy4gVW50dWsgbWVuYW5nYW5pIGluaSwgbWlzc2luZyB2YWx1ZSBha2FuIGRpaXNpIGRlbmdhbiBuaWxhaSAwIHlhbmcgYXJ0aW55YSBwYWRhIHRhaHVuIHRlcnNlYnV0IHRpZGFrIGFkYSBpbmRpdmlkdSB5YW5nIGRpYWRpbGkuDQoNCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9DQojIElzaSBtaXNzaW5nIHZhbHVlIGRlbmdhbiAwDQp3aXRjaF90cmlhbHNbaXMubmEod2l0Y2hfdHJpYWxzKV0gPC0gMA0KDQojIENlayBhcGFrYWggbWFzaWggYWRhIG1pc3NpbmcgdmFsdWUgYXRhdSB0aWRhaw0Kc3VtKGlzLm5hKHdpdGNoX3RyaWFscykpDQpgYGANCg0KKipJbnNpZ2h0KioNCg0KU2V0ZWxhaCBkaWxha3VrYW4gcHJvc2VzIHBlbmdpc2lhbiBuaWxhaSBOQSBkZW5nYW4gMCwgZGFwYXQgZGlrZXRhaHVpIGJhaHdhIGRhdGFzZXQgd2l0Y2ggdHJpYWxzIHN1ZGFoIGxlbmdrYXAgZGFuIHRpZGFrIGFkYSBuaWxhaSAwLg0KDQojIyAzLjYgRmluYWwgRGF0YQ0KDQpCZXJpa3V0IHRhbXBpbGFuIGRhdGEgc2V0ZWxhaCBwcm9zZXMgcHJlLXByb2Nlc3NpbmcgZGFuIHNpYXAgZGlndW5ha2FuIHVudHVrIHByb3NlcyB2aXN1YWxpc2FzaS4NCg0KIyMjIEEuIERhdGFzZXQgSGlzdG9yeQ0KYGBge3Igd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0NCmhpc3RvcnkNCmBgYA0KDQojIyMgQi4gRGF0YXNldCBXaXRjaCBUcmlhbHMNCg0KYGBge3Igd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0NCndpdGNoX3RyaWFscw0KYGBgDQoNCg0KIyA0LiBDcmVhdGUgYSBWaXN1YWxpemF0aW9uIHsudGFic2V0fQ0KIyMgNC4xIEJ1YmJsZSBDaGFydA0KDQpVbnR1ayBtZW1idWF0ICoqQnViYmxlIENoYXJ0KiosIGhhbnlhIG1lbWJ1dHVoa2FuIGtvbG9tIGBwZXJzb25zX3RyaWVkX3Blcl9taWxsaW9uYC4gVGVybGloYXQgcGFkYSBnYW1iYXIgcmVmZXJlbnNpIGJhaHdhIG5pbGFpIHBhZGEgYnViYmxlIGNoYXJ0IG1lbmdndW5ha2FuIG5pbGFpIGhhc2lsIGZvcm11bGEgYHBlcnNvbnNfdHJpZWRfcGVyX21pbGxpb25gIGRpYmFnaSAxMCBsYWx1IGRpYnVsYXRrYW4uDQoNCkJlcmlrdXQgbGFuZ2thaC1sYW5na2FoIHlhbmcgcGVybHUgZGlsYWt1a2FuOg0KDQoxLiBNZW1wZXJzaWFwa2FuIGRhdGEgYHBlcnNvbnNfdHJpZWRfcGVyX21pbGxpb25gIGRhcmkgZGF0YXNldCBoaXN0b3J5Lg0KMi4gTWVtYmFnaSBkYXRhIGBwZXJzb25zX3RyaWVkX3Blcl9taWxsaW9uYCBkZW5nYW4gbmlsYWkgMTAgbGFsdSBkaWJ1bGF0a2FuIG1lbmdndW5ha2FuIGZ1bmdzaSBgcm91bmQoKWAuDQozLiBNZW1idWF0IGJ1YmJsZSBjaGFydC4NCg0KYGBge3Igd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0NCiMgTWVuZ2FtYmlsIGRhdGEgcGVyc29uc190cmllZF9wZXJfbWlsbGlvbg0KYnViYmxlX2RhdGEgPC0gaGlzdG9yeSAlPiUgc2VsZWN0KHBlcnNvbnNfdHJpZWRfcGVyX21pbGxpb24pDQoNCiMgTWVuYW1waWxrYW4gZGF0YQ0KaGVhZChidWJibGVfZGF0YSkNCmBgYA0KDQpCZXJkYXNhcmthbiBnYW1iYXIgcmVmZXJlbnNpLCBuaWxhaSBwYWRhIGtvbG9tYHBlcnNvbnNfdHJpZWRfcGVyX21pbGxpb25gIGJ1a2FubGFoIG5pbGFpIGFzbGlueWEgdGV0YXBpIHN1ZGFoIGRpYmFnaSBtZW5qYWRpIDEwLg0KDQoNCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9DQojIE1lbWJhZ2kgZGF0YSBkZW5nYW4gMTAgbGFsdSBkaWJ1bGF0a2FuDQpidWJibGVfZGF0YSA8LSByb3VuZChidWJibGVfZGF0YSAvIDEwLCAwKQ0KDQojIFByb3NlcyBtZW1iYWxpayBkYXRhIGRhcmkgeWFuZyB0ZXJhdGFzIG1lbmphZGkgdGVyYmF3YWgNCmJ1YmJsZV9kYXRhIDwtIGJ1YmJsZV9kYXRhICU+JQ0KICBtdXRhdGUodmFsdWVfcmVzZXJ2ZWQgPSByZXYocGVyc29uc190cmllZF9wZXJfbWlsbGlvbikpICU+JQ0KICBzZWxlY3QoLXBlcnNvbnNfdHJpZWRfcGVyX21pbGxpb24pICU+JQ0KICByZW5hbWUocGVyc29uc190cmllZF9wZXJfbWlsbGlvbiA9IHZhbHVlX3Jlc2VydmVkKQ0KDQojIE1lbmFtcGlsa2FuIGRhdGENCmhlYWQoYnViYmxlX2RhdGEpDQpgYGANClNldGVsYWggZGF0YSBkaXBlcnNpYXBrYW4sIHNlbGFuanV0bnlhIGFkYWxhaCBwcm9zZXMgdW50dWsgbWVtYnVhdCAqKkJ1YmJsZSBDaGFydCoqIG1lbmdndW5ha2FuIGZ1bmdzaSBgZ2dwbG90KClgLiANCg0KPGhyIHN0eWxlPSJib3JkZXI6IDFweCBzb2xpZCBibGFjazsiPg0KDQpCZXJpa3V0IGxhbmdrYWgtbGFuZ2thaCB1bnR1ayBtZW1idWF0ICoqQnViYmxlIENoYXJ0KiouDQoNCjEuIE1lbXBlcnNpYXBrYW4gRGF0YTogUGVydGFtYSwga2l0YSBtZW1idWF0IHNlYnVhaCB0aWJibGUgKHN0cnVrdHVyIGRhdGEgZGFyaSBwYWNrYWdlIGRwbHlyKSB5YW5nIGFrYW4gbWVueWltcGFuIGluZm9ybWFzaSB1bnR1ayBidWJibGUgY2hhcnQuIERpIGRhbGFtIHRpYmJsZSBpbmksIGtpdGEgbWVuZW50dWthbiBuaWxhaSBkYXJpIDMgcGFyYW1ldGVyIHlhaXR1OiB4LCB5LCBkYW4gc2l6ZS4NCg0KICAgIC0gUGFyYW1ldGVyIGB4YCAkXHJpZ2h0YXJyb3ckIGRpaXNpIGRlbmdhbiBuaWxhaSB0ZXRhcCAxIGFnYXIgc2VtdWEgYnViYmxlIHRlcmxldGFrIHBhZGEgcG9zaXNpIHlhbmcgc2FtYSBzZWNhcmEgaG9yaXpvbnRhbC4NCiAgICAtIFBhcmFtZXRlciBgeWAgJFxyaWdodGFycm93JCBkaWlzaSBkZW5nYW4gdXJ1dGFuIGRhcmkgMSBoaW5nZ2EgYmFueWFrbnlhIGJhcmlzIHBhZGEgZGF0YSwgeWFuZyBha2FuIG1lbmphZGkgcG9zaXNpIHZlcnRpa2FsIGRhcmkgc2V0aWFwIGJ1YmJsZS4NCiAgICAtIFBhcmFtZXRlciBgc2l6ZWAgJFxyaWdodGFycm93JCBkaWlzaSBkYXRhIGBwZXJzb25zX3RyaWVkX3Blcl9taWxsaW9uYC4NCiAgICANCg0KMi4gTWVtYnVhdCBCdWJibGUgQ2hhcnQ6IFNlbGFuanV0bnlhLCBraXRhIG1lbmdndW5ha2FuIGZ1bmdzaSAqKmdncGxvdCgpKiogdW50dWsgbWVtYnVhdCBidWJibGUgY2hhcnQuIERpIHNpbmksIGtpdGEgbWVuZ2F0dXIgbWFwcGluZyBhZXN0aGV0aWMgKGFlcykgdW50dWsgeCwgeSwgZGFuIHNpemUgZGFyaSBkYXRhIHlhbmcgdGVsYWgga2l0YSBidWF0Lg0KDQozLiBNZW5hbWJhaGthbiBCdWJibGU6IERlbmdhbiBtZW5nZ3VuYWthbiBmdW5nc2kgYGdlb21fcG9pbnQoKWAsIGtpdGEgbWVuYW1iYWhrYW4gdGl0aWstdGl0aWsgKGJ1YmJsZSkga2UgcGxvdC4gS2l0YSBtZW5nYXR1ciB3YXJuYSBidWJibGUgbWVuamFkaSBjb3JhbCwgZGVuZ2FuIHRyYW5zcGFyYW5zaSAoYWxwaGEpIDAuNyBkYW4ga2V0ZWJhbGFuIG91dGxpbmUgKHN0cm9rZSkgc2ViZXNhciAxLg0KDQo0LiBNZW5hbWJhaGthbiBMYWJlbDogS2l0YSBqdWdhIG1lbmFtYmFoa2FuIGxhYmVsIG1lbmdndW5ha2FuIGZ1bmdzaSBgZ2VvbV90ZXh0KClgLiBMYWJlbCBpbmkgYWthbiBtZW5hbXBpbGthbiB0ZWtzIHlhbmcgYmVyYWRhIGRla2F0IGRlbmdhbiBidWJibGUsIGRlbmdhbiBwb3Npc2kgaG9yaXpvbnRhbCBzZWRpa2l0IGRpIHNlYmVsYWgga2lyaSAoeCA9IDAuOSkgZGFuIHVrdXJhbiB0ZWtzIGRpYXR1ciBtZW5qYWRpIDQgcHQuDQoNCjUuIE1lbmdhdHVyIFJlbnRhbmcgVWt1cmFuIEJ1YmJsZTogVW50dWsgbWVtYXN0aWthbiBiYWh3YSB1a3VyYW4gYnViYmxlIHRlcmxpaGF0IHByb3BvcnNpb25hbCwga2l0YSBtZW5nYXR1cm55YSBtZW5nZ3VuYWthbiBmdW5nc2kgYHNjYWxlX3NpemVfY29udGludW91cygpYCB1bnR1ayBtZW5nYXR1ciByZW50YW5nIHVrdXJhbiBidWJibGUgYW50YXJhIDEgZGFuIDE4Lg0KDQo2LiBNZW5lcmFwa2FuIFRlbWEgTWluaW1hbGlzOiBEZW5nYW4gbWVuZ2d1bmFrYW4gYHRoZW1lX21pbmltYWwoKWAsIGtpdGEgbWVuZ3ViYWggdGFtcGlsYW4gcGxvdCBtZW5qYWRpIGxlYmloIHNlZGVyaGFuYSBkYW4gYmVyc2loLg0KDQo3LiBNZW5nYXR1ciBFbGVtZW4gVGFtcGlsYW4gUGxvdDogS2l0YSBtZWxha3VrYW4gYmViZXJhcGEgcGVueWVzdWFpYW4gcGFkYSBlbGVtZW4tZWxlbWVuIGRpIHBsb3QgbWVuZ2d1bmFrYW4gZnVuZ3NpIGB0aGVtZWA6DQoNCiAgICAtIE1lbmdoYXB1cyBqdWR1bCBzdW1idSB4IGRhbiB5LCBzZXJ0YSBtZW5naGlsYW5na2FuIHRla3MgZGFuIHRhbmRhIHBhZGEgc3VtYnUgbWVuZ2d1bmFrYW4gZnVuZ3NpIGBlbGVtZW50X2JsYW5rYCBwYWRhIHBhcmFtZXRlciBgYXhpcy50aXRsZS54YCwgYGF4aXMudGV4dGAsIGRhbiBgYXhpcy50aWNrc2AuDQogICAgLSBNZW5naGFwdXMgbGVnZW5kIHVudHVrIG1lbWJ1YXQgcGxvdCBsZWJpaCBiZXJzaWggbWVuZ2d1bmFrYSBuaWFsaSBgbm9uZWAgcGFkYSBwYXJhbWV0ZXIgYGxlZ2VuZC5wb3NpdGlvbmAuDQogICAgLSBNZW5naGlsYW5na2FuIGdhcmlzIGdyaWQgcGFkYSBwbG90IGRlbmdhbiBjYXJhIG1lbmdndW5ha2FuIGZ1bmdzaSBgZWxlbWVudF9ibGFuaygpYCBwYWRhIHBhcmFtZXRlciBgcGFuZWwuZ3JpZC5tYWpvcmAsIGBwYW5lbC5ncmlkLm1pbm9yYCwgZGFuIGBwYW5lbC5ib3JkZXJgLg0KICAgIC0gTWVuZ2F0dXIgbWFyZ2luIHBsb3QgbWVuamFkaSBub2wgdW50dWsgbWVtYWtzaW1hbGthbiBwZW5nZ3VuYWFuIHJ1YW5nIHBhZGEgcGFyYW1ldGVyIGBwbG90Lm1hcmdpbmAuIEJlcmlzaSA0IGJ1YWggbmlsYWkgeWFuZyBtZW1pbGlraSB1cnV0YW4gdG9wLCByaWdodCwgYm90dG9tLCBsZWZ0Lg0KICAgIA0KDQo4LiBNZW5naGFwdXMgTGFiZWwvdGVrcyBwYWRhIFN1bWJ1IFggZGFuIFk6IEtpdGEgbWVuYW1iYWhrYW4gbGFicygpIGRlbmdhbiBuaWxhaSBgTlVMTGAgdW50dWsgbWVtYXN0aWthbiBiYWh3YSBsYWJlbCBwYWRhIHN1bWJ1IHggZGFuIHkgdGlkYWsgZGl0YW1waWxrYW4uDQoNCjkuIE1lbmdhdHVyIFJlbnRhbmcgU3VtYnUgWDogTWVuZ2d1bmFrYW4gYHhsaW0oKWAsIGtpdGEgbWVtYmF0YXNpIHRhbXBpbGFuIHN1bWJ1IHggYW50YXJhIDAgZGFuIDEuNSwgdW50dWsgbWVtYXN0aWthbiBidWJibGUgdGVybGloYXQgamVsYXMuDQoNCjEwLiBNZWxha3VrYW4gQ3JvcHBpbmcgcGFkYSBQbG90OiBUZXJha2hpciwga2l0YSBtZW5nZ3VuYWthbiBgY29vcmRfY2FydGVzaWFuKClgIHVudHVrIG1lbmdhdHVyIGNyb3BwaW5nIHNlaGluZ2dhIGhhbnlhIGFrYW4gYmVyZm9rdXMgcGFkYSBCdWJibGUgQ2hhcnQgc2FqYS4NCg0KPGhyIHN0eWxlPSJib3JkZXI6IDFweCBzb2xpZCBibGFjazsiPg0KDQpNYXJpIGtpdGEgaW1wbGVtZW50YXNpa2FuIGtlIGRhbGFtIGtvZGUgbWVuZ2d1bmFrYW4gYmFoYXNhIFIgc2ViYWdhaSBiZXJpa3V0Oi4NCg0KYGBge3Igd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0NCiMgTWVuZW50dWthbiBYLCBZLCBkYW4gU2l6ZSB1bnR1ayBtZW5lbnR1a2FuIHVrdXJhbiBidWJibGUNCmRhdGEgPC0gdGliYmxlKA0KICB4ID0gMSwNCiAgeSA9IDE6bnJvdyhidWJibGVfZGF0YSksDQogIHNpemUgPSBjKGJ1YmJsZV9kYXRhJHBlcnNvbnNfdHJpZWRfcGVyX21pbGxpb24pDQopDQoNCiMgTWVtYnVhdCBidWJibGUgY2hhcnQNCmJ1YmJsZV9jaGFydCA8LSBnZ3Bsb3QoZGF0YSwgYWVzKHggPSB4LCB5ID0geSwgc2l6ZSA9IHNpemUpKSArDQogICMgTWVuZW50dWthbiB3YXJuYSwgdHJhbnNwYXJhbnNpIChhcGxoYSksIGRhbiBsZWJhciBvdXRsaW5lIChzdHJva2UpIGJ1YmJsZQ0KICBnZW9tX3BvaW50KGNvbG9yID0gImNvcmFsIiwgYWxwaGEgPSAwLjcsIHN0cm9rZSA9IDEpICsNCg0KICAjIE1lbmdhdHVyIGxldGFrIGRhbiB1a3VyYW4gbGFiZWwNCiAgZ2VvbV90ZXh0KGFlcyh4ID0gMC45LCBsYWJlbCA9IHNpemUpLCBzaXplID0gNCkgKw0KICAgIA0KICAjIE1lbmdhdHVyIHJlbnRhbmcgdWt1cmFuIGJ1YmJsZQ0KICBzY2FsZV9zaXplX2NvbnRpbnVvdXMocmFuZ2UgPSBjKDEsIDE4KSkgKyANCiAgICANCiAgIyBNZW5nYXR1ciBhZ2FyIHRhbXBpbGFuIHBsb3QgbWluaW1hbGlzDQogIHRoZW1lX21pbmltYWwoKSArDQogICAgDQogICMgTWVuZ2F0dXIgYmViZXJhcGEgZWxlbWVudCBwYWRhIHBsb3QNCiAgdGhlbWUoDQogICAgIyBNZW5naGFwdXMganVkdWwsIHN1bWJ1IHgsIGRhbiBzdW1idSB5DQogICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLA0KICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBheGlzLnRpY2tzID0gZWxlbWVudF9ibGFuaygpLA0KICAgIA0KICAgICMgTWVuZ2hhcHVzIGxlZ2VuZA0KICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwNCiAgICANCiAgICAjIE1lbmdoYXB1cyBnYXJpcyAoZ3JpZCkNCiAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLA0KICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksDQogICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpLA0KICAgIA0KICAgICMgTWVuZ2F0dXIgbWFyZ2luIHBsb3QgKHRvcCwgcmlnaHQsIGJvdHRvbSwgbGVmdCkNCiAgICBwbG90Lm1hcmdpbiA9IG1hcmdpbigwLCAwLCAwLCAwKQ0KICApICsNCiAgICANCiAgIyBNZW5naGFwdXMgbGFiZWwgcGFkYSBzdW1idSBYIGRhbiBZDQogIGxhYnMoDQogICAgeCA9IE5VTEwsDQogICAgeSA9IE5VTEwNCiAgKSArDQogICAgDQogICMgTWVuZ2F0dXIgcmFuZ2UgZGFyaSBzdW1idSBYDQogIHhsaW0oMCwgMS41KSArIA0KICAgIA0KICAjIE1lbGFrdWthbiBjcm9wcGluZw0KICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoMC45LCAyKSkNCg0KIyBNZW5hbXBpbGthbiBwbG90DQpidWJibGVfY2hhcnQNCmBgYA0KU2V0ZWxhaCBwZW1idWF0YW4gYnViYmxlIGNoYXJ0IGJlcmhhc2lsLCBzYWF0bnlhIHVudHVrIG1lbnlpbXBhbiBwbG90IHRlcnNlYnV0IHVudHVrIGRpa29tYmluYXNpa2FuIGRlbmdhbiBwbG90IHlhbmcgbGFpbi4NCg0KYGBge3Igd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0NCmdnc2F2ZShmaWxlbmFtZSA9ICIuLi9hc3NldC9idWJibGVfY2hhcnQucG5nIiwgcGxvdCA9IGJ1YmJsZV9jaGFydCwgd2lkdGggPSA2LCBoZWlnaHQgPSA1LCBkcGkgPSAzMDApDQpgYGANCg0KDQojIyA0LjIgU3RhY2tlZCBCYXIgQ2hhcnQNCg0KVW50dWsgbWVtYnVhdCAqKlN0YWNrZWQgQmFyIENoYXJ0KiogcGFkYSBrYXN1cyBpbmkgaGFueWEgbWVtYnV0dWhrYW4gNCBkYXRhIHlhaXR1IGBjb3VudHJ5YCwgYHBlcnNvbnNfdHJpZWRgLCBgZGVhdGhzYCwgZGFuIGBhY2N1c2VkYC4gS29sb20gYGFjY3VzZWRgIGJlcmlzaSBkYXRhIG9yYW5nIHlhbmcgZGlhZGlsaSB0ZXRhcGkgdGlkYWsgZGlodWt1bSBtYXRpLiBLb2xvbSBpbmkgcGVybHUga2l0YSBidWF0IHNlbmRpcmkgZGVuZ2FuIG1lbmd1cmFuZ2thbiBkYXRhIHBhZGEga29sb20gYHBlcnNvbnNfdHJpZWRgIGRlbmdhbiBgZGVhdGhzYC4NCg0KQmVyaWt1dCBsYW5na2FoLWxhbmdrYWggeWFuZyBwZXJsdSBkaWxha3VrYW46DQoNCjEuIE1lbXBlcnNpYXBrYW4gZGF0YSBgY291bnRyeWAsIGBwZXJzb25zX3RyaWVkYCwgZGFuIGBkZWF0aHNgIGRhcmkgZGF0YXNldCBoaXN0b3J5Lg0KMi4gTWVtYnVhdCBrb2xvbSBgYWNjdXNlZGAgZGVuZ2FuIGZvcm11bGEgYHBlcnNvbnNfdHJpZWRgIC0gYGRlYXRoc2AuDQozLiBNZW5ndWJhaCBkYXRhIGtlIGxvbmcgZm9ybWF0IG1lbmdndW5ha2FuIGZ1bmdzaSBgcGl2b3RfbG9uZ2VyYC4NCjQuIE1lbWJ1YXQgcGxvdCAqKlN0YWNrZWQgQmFyIENoYXJ0KiouDQoNCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9DQojIE1lbWlsaWggZGF0YSB5YW5nIGRpZ3VuYWthbg0KZGF0YV9wbG90IDwtIGhpc3RvcnkgJT4lIHNlbGVjdChjb3VudHJ5LCBwZXJzb25zX3RyaWVkLCBkZWF0aHMpDQoNCiMgTWVtYnVhdCBrb2xvbSBhY2N1c2VkDQpkYXRhX3Bsb3QkYWNjdXNlZCA8LSBkYXRhX3Bsb3QkcGVyc29uc190cmllZCAtIGRhdGFfcGxvdCRkZWF0aHMNCg0KIyBNZW5hbXBpbGthbiBkYXRhDQpkYXRhX3Bsb3QNCmBgYA0KDQpBZ2FyIGRhdGEga2l0YSBiaXNhIGRpZ3VuYWthbiB1bnR1ayB2aXN1YWxpc2FzaSwgcGVybHUga2l0YSB1YmFoIGZvcm1hdG55YSBkYXJpICp3aWRlIGZvcm1hdCogbWVuamFkaSAqbG9uZyBmb3JtYXQqIGRlbmdhbiBmdW5nc2kgYHBpdm90X2xvbmdlcmAuDQoNCg0KYGBge3Igd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0NCiMgdWJhaCBmb3JtYXQgZGF0YSBkZW5nYW4gcGl2b3RfbG9uZ2VyKCkNCmRhdGFfcGxvdCA8LSBkYXRhX3Bsb3QgJT4lDQogIHBpdm90X2xvbmdlcihjb2xzID0gYygiYWNjdXNlZCIsICJkZWF0aHMiKSwgDQogICAgICAgICAgICAgICBuYW1lc190byA9ICJzdGF0dXMiLCANCiAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJ2YWx1ZSIpDQoNCiMgdGFtcGlsa2FuIGRhdGEgaGFzaWwgZm9ybWF0DQpwcmludChkYXRhX3Bsb3QpDQpgYGANCg0KU2VsYW5qdXRueWEgcGVybHUga2l0YSB1YmFoIHRpcGUgZGF0YSBgc3RhdHVzYCBtZW5qYWRpIGthdGVnb3Jpa2FsIGRlbmdhbiBtZW5nZ3VuYWthbiBmdW5nc2kgYGZhY3RvcigpYC4NCg0KYGBge3Igd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0NCiMgVWJhaCB0aXBlIGRhdGEga2UgZmFjdG9yIGRhbiBtZW5ndWJhaCB1cnV0YW4gZGFyaSBrb2xvbSBzdGF0dXMNCmRhdGFfcGxvdCA8LSBkYXRhX3Bsb3QgJT4lDQogIG11dGF0ZShzdGF0dXMgPSBmYWN0b3Ioc3RhdHVzLCBsZXZlbHMgPSBjKCJkZWF0aHMiLCAiYWNjdXNlZCIpKSkNCmBgYA0KDQpTZXRlbGFoIGRhdGEgZGlwZXJzaWFwa2FuLCBzZWxhbmp1dG55YSBhZGFsYWggcHJvc2VzIHVudHVrIG1lbWJ1YXQgKipTdGFja2VkIEJhciBDaGFydCoqIG1lbmdndW5ha2FuIGZ1bmdzaSBgZ2dwbG90KClgLg0KDQo8aHIgc3R5bGU9ImJvcmRlcjogMXB4IHNvbGlkIGJsYWNrOyI+DQoNCkJlcmlrdXQgbGFuZ2thaC1sYW5na2FoIHVudHVrIG1lbWJ1YXQgKipTdGFja2VkIEJhciBDaGFydCoqLg0KDQoxLiBNZW1idWF0IFdhcm5hIEt1c3RvbTogTWVuZW50dWthbiB3YXJuYSB1bnR1ayBtZW1iZWRha2FuIGFudGFyYSBrYXRlZ29yaSAqKmRlYXRocyoqIGRhbiAqKmFjY3VzZWQqKi4NCg0KMi4gTWVtYnVhdCBTdGFja2VkIEJhciBDaGFydDogU2VsYW5qdXRueWEsIGtpdGEgbXVsYWkgbWVtYnVhdCBkaWFncmFtLiBOYW11biBwbG90IGluaSBkaXNhamlrYW4gZGFsYW0gYmVudHVrIGhvcml6b250YWwgc2VoaW5nZ2Egc2VvbGFoLW9sYWggc3VtYnUgeCBiZXJhZGEgZGkgc3VtYnUgeSBkYW4gc2ViYWxpa255YS4NCg0KICAgIC0gU3VtYnUgWCAkXHJpZ2h0YXJyb3ckIG5hbWEgbmVnYXJhLg0KICAgIC0gU3VtYnUgWSAkXHJpZ2h0YXJyb3ckIGp1bWxhaCBpbmRpdmlkdSB5YW5nIGRpYWRpbGkuIA0KICAgIC0gV2FybmEgYmF0YW5nIGFrYW4gbWVuY2VybWlua2FuIGthdGVnb3JpIHlhbmcgYmVyYmVkYSAqKmV4ZWN1dGVkKiogZGFuICoqYWNjdXNlZCoqLiANCiAgDQoNCjMuIE1lbmdhdHVyIExlYmFyIEJhdGFuZzogS2l0YSBtZW5nYXR1ciBsZWJhciBiYXRhbmcgYWdhciB0ZXJsaWhhdCBwcm9wb3JzaW9uYWwgZGFuIHRpZGFrIHRlcmxhbHUgbGViYXIsIHNlaGluZ2dhIG1lbWJlcmlrYW4gc2VkaWtpdCBydWFuZyBhbnRhcmEgYmF0YW5nLWJhdGFuZyB5YW5nIGJlcmRla2F0YW4uIExlYmFyIGJhdGFuZyBkaXRlbnR1a2FuIG1lbmdndW5ha2FuIHBhcmFtZXRlciBgd2lkdGhgLg0KDQo0LiBNZW5naW1wbGVtZW50YXNpa2FuIFdhcm5hIEt1c3RvbTogV2FybmEgeWFuZyB0ZWxhaCBkaXRlbnR1a2FuIHNlYmVsdW1ueWEgZGl0ZXJhcGthbiBwYWRhIHBsb3QsIHNlaGluZ2dhIG1lbXVkYWhrYW4gcGVtYWhhbWFuIHZpc3VhbCBtZW5nZW5haSBrYXRlZ29yaSB5YW5nIGFkYS4gUGFyYW1ldGVyIHlhbmcgZGlhdHVyIHlhaXR1IGBzY2FsZV9maWxsX21hbnVhbGAuDQoNCjUuIE1lbmViYWxrYW4gR2FyaXMgSG9yaXpvbnRhbDogU2VidWFoIGdhcmlzIGhvcml6b250YWwgZGl0YW1iYWhrYW4gcGFkYSBzdW1idSBZIGRpIHRpdGlrIG5vbCBtZW5nZ3VuYWthbiBwYXJhbWV0ZXIgYGdlb21faGxpbmVgLiBQYXJhbXRlciBgeWludGVyY2VwdGAgZGlpc2kgZGVuZ2FuIG5pbGFpIHN1bWJ1IFggZGkgbWFuYSBnYXJpcyBha2FuIGRpdGVtcGF0a2FuLiBTZWRhbmdrYW4gYGx3ZGAgdW50dWsgbWVuZ2F0dXIgdGViYWwgZ2FyaXMuDQoNCjYuIE1lbmdoYXB1cyBMYWJlbCBwYWRhIHN1bWJ1IFggZGFuIFk6IFVudHVrIG1lbmRhcGF0a2FuIHRhbXBpbGFuIHlhbmcgbGViaWggYmVyc2loIGRhbiBtaW5pbWFsaXMsIGxhYmVsIHBhZGEgc3VtYnUgWCBkYW4gWSBkaWhhcHVzIG1lbmdndW5ha2FuIHBhcmFtZXRlciBgbGFic2AuDQoNCjcuIE1lbmd1YmFoIE9yaWVudGFzaSBQbG90OiBPcmllbnRhc2kgcGxvdCBkaXViYWggZGFyaSB2ZXJ0aWthbCBtZW5qYWRpIGhvcml6b250YWwgbWVuZ2d1bmFrYW4gcGFyYW1ldGVyIGBjb29yZF9mbGlwYC4NCg0KOC4gTWVuZ2F0dXIgRWxlbWVuIFBsb3Q6IE1lbmdndW5ha2FuIHBhcmFtZXRlciBgdGhlbWVgLCBiZXJpa3V0IGJlYmVyYXBhIGVsZW1lbiB5YW5nIGRpYXR1ciB5YWl0dToNCg0KICAgIC0gTWVuZ2hhcHVzIGdyaWQgZGkgZGFsYW0gcGxvdCBtZW5nZ3VuYWthbiBgZWxlbWVudF9ibGFua2AgcGFkYSBwYXJhbWV0ZXIgYHBhbmVsLmdyaWRgLg0KICAgIC0gTWVuZ2F0dXIgcG9zaXNpIGRhbiB1a3VyYW4gZGFyaSBsZWdlbmQgKiphY2N1c2VkKiogZGFuICoqZXhlY3V0ZWQqKi4NCiAgICAtIE1lbmdhdHVyIGJhdGFzIHRlcGkgKG1hcmdpbikgZGkgbXVsYWkgZGFyaSB0b3AsIHJpZ2h0LCBib3R0b20sIGxlZnQgZGFsYW0gc2F0dWFuIGNlbnRpbWV0ZXIgKGNtKS4NCg0KOS4gTWVuZ2F0dXIgdGVrcyBTdW1idSBZOiBEaWF0dXIgYWdhciBtZW1pbGlraSByYW5nZSBhbnRhcmEgMCBzYW1wYWkgMTcuNTAwIGRlbmdhbiBqYXJhayAyLjUwMCBtZW5nZ3VuYWthbiBwYXJhbWV0ZXIgYGJyZWFrc2AgcGFkYSBiYWdpYW4gYHNjYWxlX3lfY29udGludW9zYC4gU2VsYWluIGl0dSwgdGVyYXBrYW4gcGVtaXNhaCByaWJ1YW4gbWVuZ2d1bmFrYW4gcGFyYW1ldGVyIGBsYWJlbHNgIHlhbmcgZGlpc2kgZGVuZ2FuIG5pbGFpIGBzY2FsZXM6OmNvbW1hYC4NCg0KMTAuIE1lbmdhdHVyIExlZ2VuZGE6IFRlcmFraGlyLCBsZWdlbmQgZGlhdHVyIGFnYXIgZGl0YW1waWxrYW4gZGFsYW0gc2F0dSBiYXJpcywgbWVuamFkaWthbm55YSBsZWJpaCByYXBpIGRhbiBtdWRhaCBkaWJhY2EuIA0KDQo8aHIgc3R5bGU9ImJvcmRlcjogMXB4IHNvbGlkIGJsYWNrOyI+DQoNCk1hcmkga2l0YSBpbXBsZW1lbnRhc2lrYW4ga2UgZGFsYW0ga29kZSBtZW5nZ3VuYWthbiBiYWhhc2EgUiBzZWJhZ2FpIGJlcmlrdXQ6Lg0KDQoNCmBgYHtyIG1lc3NhZ2UgPSBGQUxTRX0NCiMgTWVtYnVhdCB3YXJuYSBrdXN0b20NCmN1c3RvbV9jb2xvcnMgPC0gYygiZGVhdGhzIiA9ICIjMDA5OGEwIiwgImFjY3VzZWQiID0gIiM3MGNiYzQiKQ0KDQojIE1lbWJ1YXQgc3RhY2tlZCBiYXINCnN0YWNrZWRfYmFyX2NoYXJ0IDwtIGdncGxvdChkYXRhX3Bsb3QsIGFlcyh4ID0gcmVvcmRlcihjb3VudHJ5LCB2YWx1ZSksIHkgPSB2YWx1ZSwgZmlsbCA9IHN0YXR1cykpICsNCiAgDQogICMgTWVuZ2F0dXIgbGViYXIgYmFyDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCB3aWR0aCA9IDAuOCkgKw0KICANCiAgIyBJbXBsZW1lbnRhc2kgd2FybmEga3VzdG9tIGtlIGJhcg0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjdXN0b21fY29sb3JzLCBsYWJlbHM9YygnRXhlY3V0ZWQnLCAnQWNjdXNlZCcpKSArDQogIA0KICAjIE1lbWJ1YXQgaG9yaXpvbnRhbCBsaW5lIHBhZGEgc3VtYnUgWSBwYWRhIHRpdGlrIDAgZGVuZ2FuIGxlYmFyIDAuMjUNCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCwNCiAgICAgICAgICAgICBsd2QgPSAwLjI1KSArDQogIA0KICAjIE1lbmdoYXB1cyBsYWJlbCBwYWRhIHN1bWJ1IFggZGFuIFkNCiAgbGFicyh4ID0gIiIsIHkgPSAiIikgKyANCiAgDQogICMgTWVuZ2h1YmFoIG9yaWVudGFzaSBwbG90IGRhcmkgdmVydGljYWwga2UgaG9yaXpvbnRhbA0KICBjb29yZF9mbGlwKCkgKw0KICANCiAgIyBNZW5nYXR1ciBhZ2FyIHRhbXBpbGFuIHBsb3QgbWluaW1hbGlzDQogIHRoZW1lX21pbmltYWwoKSArIA0KICANCiAgIyBNZW5nYXR1ciBiZWJlcmFwYSBlbGVtZW50IHBhZGEgcGxvdA0KICB0aGVtZSgNCiAgICAjIE1lbmdhdHVyIHdhcm5hIHRla3MgcGFkYSBzdW1idSBYIGRhbiBZDQogICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoY29sb3IgPSAiYmxhY2siKSwgICMgQWxpZ24gbGFiZWxzIHRvIHRoZSBsZWYNCiAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAsIGNvbG9yID0gImJsYWNrIiksICAjIEFsaWduIGxhYmVscyB0byB0aGUgbGVmdA0KICAgIA0KICAgICMgTWVuZ2F0dXIgZ2FyaXMgeWFuZyBha2FuIGRpdGFtcGlsa2FuIGRpIHRlbmdhaC10ZW5nYWggcGxvdA0KICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksDQogICAgcGFuZWwuZ3JpZC5tYWpvci55ID0gZWxlbWVudF9ibGFuaygpLA0KICAgIHBhbmVsLmdyaWQubWFqb3IueCA9IGVsZW1lbnRfbGluZShzaXplID0gMC43NSwgY29sb3IgPSAiI2NiY2NjZSIpLA0KICAgIA0KICAgICMgTWVuZ2F0dXIgcG9zaXNpIGRhbiB1a3VyYW4gZGFyaSBsZWdlbmQNCiAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksDQogICAgbGVnZW5kLnBvc2l0aW9uID0gYygwLjMsIDEuMSksDQogICAgbGVnZW5kLm1hcmdpbiA9IG1hcmdpbigwLCAwLCAxNSwgMCksDQogICAgbGVnZW5kLmtleS5oZWlnaHQgPSB1bml0KDAuMywgImNtIiksDQogICAgbGVnZW5kLmtleS53aWR0aCA9IHVuaXQoMC43LCAiY20iKSwNCiAgICBsZWdlbmQua2V5LnNwYWNpbmcueCA9IHVuaXQoMC45LCAiY20iKSwNCiAgICANCiAgICAjIE1lbmdhdHVyIG1hcmdpbiBkYXJpIHBsb3Qgc2VjYXJhIHVtdW0NCiAgICBwbG90Lm1hcmdpbiA9IG1hcmdpbigwLjcsIDQsIDAuNSwgMCwgImNtIikgDQogICkgKyANCiAgDQogICMgTWVuZ2F0dXIgdGVrcyBwYWRhIHN1bWJ1IFkNCiAgc2NhbGVfeV9jb250aW51b3VzKA0KICAgICMgTWVuZ2F0dXIgbmlsYWkgZGltdWxhaSBkYXJpIDAgc2FtcGFpIDE3LjUwMCBkZW5nYW4gamFyYWsgMi41MDANCiAgICBicmVha3MgPSBzZXEoMCwgMTc1MDAsIGJ5ID0gMjUwMCksDQogICAgDQogICAgIyBNZW5nYXR1ciBiYXRhcy9saW1pdCB0ZWtzIHlhbmcgZGl0YW1waWxrYW4gcGFkYSBzdW1idSBZDQogICAgbGltaXRzID0gYygwLCAxNzUwMCksIA0KICAgIA0KICAgICMgTWVuZ3VyYW5naSBydWFuZyBrb3NvbmcgcGFkYSBrYW5hbiBraXJpIGF0YXMgYmF3YWggcGxvdA0KICAgIGV4cGFuZCA9IGV4cGFuc2lvbihtdWx0ID0gYygwLCAwKSksDQogICAgDQogICAgIyBNZW5hbWJhaCBwZW1pc2FoIHJpYnVhbiBwYWRhIHN1bWJ1IFkNCiAgICBsYWJlbHMgPSBzY2FsZXM6OmNvbW1hLA0KICAgIA0KICAgICMgTWVuZ2F0dXIgcG9zaXNpIHN1bWJ1IFkNCiAgICBwb3NpdGlvbiA9ICJyaWdodCINCiAgKSArDQogIA0KICAjIE1lbmF0dXIgYWdhciBsZWdlbmQgaG9yaXpvbnRhbA0KICBndWlkZXMoZmlsbCA9IGd1aWRlX2xlZ2VuZChucm93ID0gMSwgYnlyb3cgPSBUUlVFLCByZXZlcnNlID0gVFJVRSkpDQogIA0KIyBNZW5hbXBpbGthbiBwbG90DQpzdGFja2VkX2Jhcl9jaGFydA0KYGBgDQoNClNldGVsYWggcGVtYnVhdGFuIHN0YWNrZWQgY2hhcnQgYmVyaGFzaWwsIHNhYXRueWEgdW50dWsgbWVueWltcGFuIHBsb3QgdGVyc2VidXQgdW50dWsgZGlrb21iaW5hc2lrYW4gZGVuZ2FuIHBsb3QgeWFuZyBsYWluLg0KDQpgYGB7ciB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQ0KZ2dzYXZlKGZpbGVuYW1lID0gIi4uL2Fzc2V0L3N0YWNrZWRfYmFyX2NoYXJ0LnBuZyIsIHBsb3QgPSBzdGFja2VkX2Jhcl9jaGFydCwgd2lkdGggPSA2LCBoZWlnaHQgPSA0LCBkcGkgPSAzMDApDQpgYGANCg0KDQojIyA0LjMgTGluZSBDaGFydA0KDQpVbnR1ayBtZW1idWF0ICoqTGluZSBDaGFydCoqLCBha2FuIGRpZ3VuYWthbiBkYXRhc2V0IHdpdGNoIHRyYWlscy4gU2FtYSBzZXBlcnRpIDIgcGxvdCBzZWJlbHVtbnlhLCB1bnR1ayBtZW1idWF0ICoqTGluZSBDaGFydCoqIGp1Z2EgYWthbiBtZW1hbmZhYXRrYW4gbGlicmFyeSBkYXJpIGBnZ3Bsb3RgLiBOYW11biBzZWJlbHVtIGl0dSwga2l0YSBwZXJsdSBtZW1wZXJzaWFwa2FuIGRhdGFueWEgdGVybGViaWggZGFodWx1IHVudHVrIG1lbmp1bWxhaGthbiB0b3RhbCBpbmRpdmlkdSB5YW5nIGRpYWRpbGkgdGlhcCBkZWthZGUuDQoNCkJlcmlrdXQgbGFuZ2thaC1sYW5na2FoIHlhbmcgcGVybHUgZGlsYWt1a2FuOg0KDQoxLiBNZW1wZXJzaWFwa2FuIGRhdGEgYGRlY2FkZWAsIGBkZWF0aHNgLCBkYW4gYHRyaWVkYCBkYXJpIGRhdGFzZXQgd2l0Y2hfdHJpYWxzLg0KMi4gTGFrdWthbiBwcm9zZXMgZ3JvdXBpbmcgZGVuZ2FuIGZ1bmdzaSBgZ3JvdXBfYnlgIHVudHVrIG1lbmp1bWxhaGthbiB0b3RhbCBpbmRpdmlkdSB5YW5nIGRpaHVrdW0gbWF0aSBkYW4gdGlkYWsgbWVuZ2d1bmFrYW4gZnVuZ3NpIGBzdW1tYXJpc2VgLg0KNC4gTWVtYnVhdCBwbG90ICoqTGluZSBDaGFydCoqLg0KDQpgYGB7ciB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQ0KIyBQcm9zZXMgZ3JvdXBCeSB1bnR1ayBtZW5qdW1sYWhrYW4gZGVhdGhzIGRhbiB0cmllZCB1bnR1ayB0aWFwIGRla2FkZQ0KZGF0YV9saW5lX2NoYXJ0IDwtIHdpdGNoX3RyaWFscyAlPiUNCiAgZ3JvdXBfYnkoZGVjYWRlKSAlPiUNCiAgc3VtbWFyaXNlKA0KICAgIGRlYXRocyA9IHN1bShkZWF0aHMpLA0KICAgIHRyaWVkID0gc3VtKHRyaWVkKQ0KICApDQoNCiMgTWVuYW1waWxrYW4gZGF0YQ0KZGF0YV9saW5lX2NoYXJ0DQpgYGANCg0KU2V0ZWxhaCBkYXRhIHN1ZGFoIGJlcmhhc2lsIGRpanVtbGFoa2FuIHVudHVrIHNldGlhcCBkZWthZGUsIHNlbGFuanV0bnlhIHlhaXR1IHBlbWJ1YXRhbiBsaW5lIHBsb3QgbWVuZ2d1bmFrYW4gZnVuZ3NpIGBnZ3Bsb3RgLg0KDQo8aHIgc3R5bGU9ImJvcmRlcjogMXB4IHNvbGlkIGJsYWNrOyI+DQoNCkJlcmlrdXQgbGFuZ2thaC1sYW5na2FoIHVudHVrIG1lbWJ1YXQgKipTdGFja2VkIEJhciBDaGFydCoqLg0KDQoxLiBNZW1idWF0IGZ1bmdzaSBgY3VzdG9tX2xhYmVsc2AgeWFuZyBkaWd1bmFrYW4gdW50dWsgbWVuYW1waWxrYW4gdGVrcyBwYWRhIHN1bWJ1IFguIEFrYW4gbWVuYW1waWxrYW4gdGFodW4gc2VjYXJhIGxlbmdrYXAgamlrYSB0YWh1biB0ZXJzZWJ1dCB0aWRhayBkaWFraGlyaSBkZW5nYW4gYW5na2EgNTAuIFRhcGkgamlrYSBkaWFraGlyaSBhbmdrYSA1MCwgaGFueWEgYWthbiBkaXRhbXBpbGthbiBhbmdrYSA1MC4gTWlzYWxueWE6IFRhaHVuIDE4MDAgYWthbiBkaXRhbXBpbGthbiAxODAwLiBUYWh1biAxODUwIGFrYW4gZGl0YW1waWxrYW4gNTAuDQoNCjIuIE1lbWJ1YXQgTGluZSBQbG90OiBEZW5nYW4gbWVuZ2d1bmFrYW4gZnVuZ3NpIGBnZW9tX2xpbmVgLCBha2FuIGRpYnVhdCAyIGdhcmlzIHlhaXR1IGdhcmlzIHVudHVrIGRhdGEgYGRlYXRoc2AgeWFuZyBha2FuIGRpYmVyaSBsYWJlbCAqKmV4ZWN1dGVkKiogZGFuIGdhcmlzIHVudHVrIGRhdGEgYHRyaWVkYCB5YW5nIGRpYmVyaSBsYWJlbCAqKmFjY3VzZWQqKi4NCg0KMy4gTWVuZ2hhcHVzIExhYmVsIHBhZGEgc3VtYnUgWCBkYW4gWTogVW50dWsgbWVuZGFwYXRrYW4gdGFtcGlsYW4geWFuZyBsZWJpaCBiZXJzaWggZGFuIG1pbmltYWxpcywgbGFiZWwgcGFkYSBzdW1idSBYIGRhbiBZIGRpaGFwdXMgbWVuZ2d1bmFrYW4gcGFyYW1ldGVyIGBsYWJzYC4NCg0KNC4gTWVuZ2F0dXIgU3VtYnUgWDogUGFkYSBzdW1idSBYIGRlbmdhbiBmdW5nc2kgYHNjYWxlX3hfY29udGludW9zYCwga2l0YSBtZW5nYXR1ciBiZWJlcmFwYSBoYWwgc2ViYWdhaSBiZXJpa3V0Og0KICAgICAgDQogICAgLSBNZW1idWF0IG1ham9yIGJyZWFrcyBwYWRhIHN1bWJ1IFggZGVuZ2FuIHJhbmdlIDEzMDAgc2FtcGFpIDE4NTAgeWFuZyBkaXBpc2Foa2FuIHNldGlhcCA1MCBzYXR1YW4uIA0KICAgIC0gTWVtYnVhdCBtaW5vciBicmVha3MgcGFkYSBzdW1idSBYIGRlbmdhbiByYW5nZSAxMzAwIHNhbXBhaSAxODUwIHlhbmcgZGlwaXNhaGthbiBzZXRpYXAgMTAgc2F0dWFuLg0KICAgIC0gTWVudWxpc2thbiB0ZWtzIGxhYmVsIHBhZGEgc3VtYnUgWCBkZW5nYW4gbWVtYW5nZ2lsIGZ1bmdzaSBgY3VzdG9tX2xhYmVsc2AgeWFuZyBzdWRhaCBkaWJ1YXQgZGkgYXRhcy4NCiAgICAtIE1lbmFtcGlsa2FuIGdhcmlzIGtlY2lsIHNlcGFuamFuZyBzdW1idSBYIGRlbmdhbiBtZW5nZ3VuYWthbiBmdW5nc2kgYGd1aWRlX2F4aXNgLg0KDQo1LiBNZW5nYXR1ciBTdW1idSBZOiBQYWRhIHN1bWJ1IFkgZGVuZ2FuIGZ1bmdzaSBgc2NhbGVfeV9jb250aW51b3NgLCBraXRhIG1lbmdhdHVyIGJlYmVyYXBhIGhhbCBzZWJhZ2FpIGJlcmlrdXQ6DQogICAgICANCiAgICAtIE1lbWJ1YXQgbWFqb3IgYnJlYWtzIHBhZGEgc3VtYnUgWSBkZW5nYW4gcmFuZ2UgMCBzYW1wYWkgNjAwMCB5YW5nIGRpcGlzYWhrYW4gc2V0aWFwIDEwMDAgc2F0dWFuLiANCiAgICAtIE1lbmdhdHVyIHJ1YW5nIGtvc29uZyBkaSBzZWtpdGFyIHBsb3QgbWVuZ2d1bmFrYW4gZnVuZ3NpIGBleHBhbnNpb25gLg0KICAgIC0gTWVudWxpc2thbiB0ZWtzIGxhYmVsIHBhZGEgc3VtYnUgWSB5YW5nIGRpcGlzYWhrYW4gZGVuZ2FuIGtvbWEgbWVuZ2d1bmFrYW4gcGFyYW1ldGVyIGBzY2FsZXM6OmNvbW1hYC4NCiAgICAtIE1lbmdhdHVyIGFnYXIgcG9zaXNpIHN1bWJ1IFkgYmVyYWRhIHBhZGEgc2lzaSBrYW5hbiBtZW5nZ3VuYWthbiBmdW5nc2kgYHBvc2l0aW9uYC4NCg0KNi4gTWVuZ2F0dXIgRWxlbWVuIFBsb3Q6IE1lbmdndW5ha2FuIHBhcmFtZXRlciBgdGhlbWVgLCBiZXJpa3V0IGJlYmVyYXBhIGVsZW1lbiB5YW5nIGRpYXR1ciB5YWl0dToNCg0KICAgIC0gTWVuZ2hhcHVzIGdyaWQgZGkgZGFsYW0gcGxvdCBtZW5nZ3VuYWthbiBgZWxlbWVudF9ibGFua2AgcGFkYSBwYXJhbWV0ZXIgYHBhbmVsLmdyaWRgLg0KICAgIC0gTWVuZ2F0dXIgdGViYWwgZGFuIHBhbmphbmcgKG1heW9yIHRpY2tzIGRhbiBtaW5vciB0aWNrcykgbWVuZ2d1bmFrYW4gZnVuZ3NpIGBheGlzLnRpY2tzYC4NCiAgICAtIE1lbmdhdHVyIGFnYXIgd2FybmEgYmFja2dyb3VuZCBmb3RvIGRlbmdhbiB3YXJuYSBwdXRpaCBtZW5nZ3VuYWthbiBmdW5nc2kgYHBsb3QuYmFja2dyb3VuZGAuDQoNCjcuIE1lbmFtYmFoa2FuIEhpZ2hsaWdodDogS2l0YSBtZW55b3JvdGkgcmVudGFuZyB0YWh1biBhbnRhcmEgMTU1NSBoaW5nZ2EgMTY0OCBkZW5nYW4gbWVuYW1iYWhrYW4gcGVyc2VnaSBwYW5qYW5nIGJlcndhcm5hIGNvcmFsIHRyYW5zcGFyYW4uIEhhbCBpbmkgbWVtYmFudHUgbWVuYXJpayBwZXJoYXRpYW4gcGFkYSBwZXJpb2RlIHRlcnRlbnR1IHlhbmcgcmVsZXZhbiBkZW5nYW4gZGF0YS4gVW50dWsgbWVsYWt1a2FuIGhhbCBpbmksIGRpZ3VuYWthbiBwYXJhbWV0ZXIgYGdlb21gIHBhZGEgZnVuZ3NpIGBhbm5vdGF0ZWAuDQoNCiAgICAtIGB4bWluYCAkXHJpZ2h0YXJyb3ckIG5pbGFpIHBhZGEgc3VtYnUgWCB1bnR1ayBtZW11bGFpIGhpZ2hsaWdodC4NCiAgICAtIGB4bWF4YCAkXHJpZ2h0YXJyb3ckIG5pbGFpIHBhZGEgc3VtYnUgWCB1bnR1ayBtZW5nYWtoaXJpIGhpZ2hsaWdodC4NCiAgICAtIGB5bWluYCAkXHJpZ2h0YXJyb3ckIG5pbGFpIHBhZGEgc3VtYnUgWSB1bnR1ayBtZW5lbnR1a2FuIHRpdGlrIGF3YWwgdGluZ2dpIGhpZ2hsaWdodC4NCiAgICAtIGB5bWF4YCAkXHJpZ2h0YXJyb3ckIG5pbGFpIHBhZGEgc3VtYnUgWSB1bnR1ayBtZW5lbnR1a2FuIHRpdGlrIGFraGlyIHRpbmdnaSBoaWdobGlnaHQuDQogICAgLSBgZmlsbGAgJFxyaWdodGFycm93JCBtZW5lbnR1a2FuIHdhcm5hLg0KICAgIC0gYGFscGhhYCAkXHJpZ2h0YXJyb3ckIG1lbmVudHVrYW4gdGluZ2thdCB0cmFuc3BhcmFuc2kuIFNlbWFraW4ga2VjaWwgc2VtYWtpbiB0cmFuc3BhcmFuLg0KICAgIA0KOC4gTWVuYW1iYWhrYW4gdGVrcyB5YW5nIGxhaW46IE1lbmdndW5ha2FuIHBhcmFtZXRlciBgdGV4dGAgcGFkYSBmdW5nc2kgYGFubm90YXRlYCB1bnR1ayBtZW51bGlza2FuIHRla3MgYGV4ZWN1dGVkYCwgYGFjY3VzZWRgLCBgcHJvdGVzdGFudCByZWZvcm1hdGlvbmAsIGRhbiBgY2F0aG9saWMgY291bnRlci1yZWZvcm1hdGlvbmAuDQoNCjkuIE1lbmFtYmFoa2FuIGdhcmlzIHRpdGlrIHRpdGlrIHBhZGEgdGVrcyAqcHJvdGVzdGFudCByZWZvcm1hdGlvbiogbWVuZ2d1bmFrYW4gZnVuZ3NpIGBnZW9tX3NlZ21lbnRgLg0KDQoxMC4gTWVuYW1iYWhrYW4gdGVrcyBwYWRhIHNpc2kga2lyaSBhdGFzIG1lbmdndW5ha2FuIGZ1bmdzaSBgZ2VvbV9sYWJlbGAuDQoNCg0KPGhyIHN0eWxlPSJib3JkZXI6IDFweCBzb2xpZCBibGFjazsiPg0KDQpNYXJpIGtpdGEgaW1wbGVtZW50YXNpa2FuIGtlIGRhbGFtIGtvZGUgbWVuZ2d1bmFrYW4gYmFoYXNhIFIgc2ViYWdhaSBiZXJpa3V0Oi4NCg0KDQpgYGB7ciB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQ0KDQojIEZ1bmdzaSB1bnR1ayBtZW1idWF0IGxhYmVsIGN1c3RvbQ0KY3VzdG9tX2xhYmVscyA8LSBmdW5jdGlvbih4KSB7DQogICMgTWVtYnVhdCB0ZW1wb3JhcnkgdmFyaWFibGUNCiAgbGFiZWxzIDwtICIiDQogIA0KICAjIExvb3BpbmcgdW50dWsgY2VrIGRhdGEgZGVrYWRlLiBKaWthIG1lbmdhbmR1bmcgNTAsIGhhbnlhIGRpdHVsaXMgYW5na2EgNTAuIEppa2EgdGlkYWssIGFrYW4gZGl0dWxpcyBsZW5na2FwLg0KICBmb3IgKGkgaW4gc2VxX2Fsb25nKHgpKSB7DQogICAgaWYgKGdyZXBsKCI1MCQiLCBhcy5jaGFyYWN0ZXIoeFtpXSkpKSB7DQogICAgICBsYWJlbHNbaV0gPC0gIjUwIg0KICAgIH0gZWxzZSB7DQogICAgICBsYWJlbHNbaV0gPC0gYXMuY2hhcmFjdGVyKHhbaV0pDQogICAgfQ0KICB9DQogIA0KICAjIE1lbmdlbWJhbGlrYW4gbmlsYWkgZGVrYWRlDQogIHJldHVybihsYWJlbHMpDQp9DQoNCiMgTWVtYnVhdCBsaW5lIHBsb3QNCmxpbmVfY2hhcnQgPC0gZ2dwbG90KGRhdGFfbGluZV9jaGFydCwgYWVzKHggPSBkZWNhZGUpKSArDQogICMgTWVtYnVhdCBsaW5lIHVudHVrIGtvbG9tIGRlYXRocw0KICBnZW9tX2xpbmUoYWVzKHkgPSBkZWF0aHMpLCBjb2xvciA9ICIjMDA5OGEwIiwgc2l6ZSA9IDIpICsNCiAgDQogICMgTWVtYnVhdCBsaW5lIHVudHVrIGtvbG9tIHRyaWVkDQogIGdlb21fbGluZShhZXMoeSA9IHRyaWVkKSwgY29sb3IgPSAiIzcwY2JjNCIsIHNpemUgPSAyKSArDQogIA0KICAjIE1lbmdoYXB1cyBsYWJlbCBwYWRhIHN1bWJ1IFggZGFuIFkNCiAgbGFicyh4ID0gIiIsIHkgPSAiIikgKyANCiAgDQogICMgTWVuZ2F0dXIgYWdhciB0YW1waWxhbiBwbG90IG1pbmltYWxpcw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICANCiAgc2NhbGVfeF9jb250aW51b3VzKA0KICAgIGJyZWFrcyA9IHNlcSgxMzAwLCAxODUwLCBieSA9IDUwKSwgIyBUZW50dWthbiB0aXRpayB1dGFtYQ0KICAgIG1pbm9yX2JyZWFrcyA9IHNlcSgxMzAwLCAxODUwLCBieSA9IDEwKSwgIyBUZW50dWthbiB0aXRpayBtaW5vcg0KICAgIGxhYmVscyA9IGN1c3RvbV9sYWJlbHMsDQogICAgZ3VpZGUgPSBndWlkZV9heGlzKG1pbm9yLnRpY2tzID0gVFJVRSksDQogICkgKw0KICANCiAgIyBNZW5nYXR1ciB0ZWtzIHBhZGEgc3VtYnUgWQ0KICBzY2FsZV95X2NvbnRpbnVvdXMoDQogICAgIyBNZW5nYXR1ciBuaWxhaSBkaW11bGFpIGRhcmkgMCBzYW1wYWkgNi4wMDAgZGVuZ2FuIGphcmFrIDEuMDAwDQogICAgYnJlYWtzID0gc2VxKDAsIDYwMDAsIGJ5ID0gMTAwMCksDQogICAgDQogICAgIyBNZW5ndXJhbmdpIHJ1YW5nIGtvc29uZyBwYWRhIGthbmFuIGtpcmkgYXRhcyBiYXdhaCBwbG90DQogICAgZXhwYW5kID0gZXhwYW5zaW9uKG11bHQgPSBjKDAsIDApKSwNCiAgICANCiAgICAjIE1lbmFtYmFoIHBlbWlzYWggcmlidWFuIHBhZGEgc3VtYnUgWQ0KICAgIGxhYmVscyA9IHNjYWxlczo6Y29tbWEsDQogICAgDQogICAgIyBNZW5nYXR1ciBwb3Npc2kgc3VtYnUgWQ0KICAgIHBvc2l0aW9uID0gInJpZ2h0Ig0KICApICsNCiAgDQogICMgTWVuZ2F0dXIgYmViZXJhcGEgZWxlbWVudCBwYWRhIHBsb3QNCiAgdGhlbWUoDQogICAgIyBNZW5nYXR1ciB3YXJuYSB0ZWtzIHBhZGEgc3VtYnUgWCBkYW4gWQ0KICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGNvbG9yID0gImJsYWNrIiksICAjIEFsaWduIGxhYmVscyB0byB0aGUgbGVmDQogICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLCBjb2xvciA9ICJibGFjayIpLCAgIyBBbGlnbiBsYWJlbHMgdG8gdGhlIGxlZnQNCiAgICANCiAgICAjIE1lbmdhdHVyIGdhcmlzIHlhbmcgYWthbiBkaXRhbXBpbGthbiBkaSB0ZW5nYWgtdGVuZ2FoIHBsb3QNCiAgICBwYW5lbC5ncmlkLm1pbm9yLnggPSBlbGVtZW50X2JsYW5rKCksDQogICAgcGFuZWwuZ3JpZC5taW5vci55ID0gZWxlbWVudF9ibGFuaygpLA0KICAgIHBhbmVsLmdyaWQubWFqb3IueCA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBwYW5lbC5ncmlkLm1ham9yLnkgPSBlbGVtZW50X2xpbmUoc2l6ZSA9IDAuNzUsIGNvbG9yID0gIiNjYmNjY2UiKSwNCiAgICANCiAgICAjIE1lbmdhdHVyIGtldGViYWxhbiB0aWNrcw0KICAgIGF4aXMudGlja3MueCA9IGVsZW1lbnRfbGluZShzaXplID0gMC42KSwNCiAgICANCiAgICAjIE1lbmdhdHVyIHBhbmphbmcgdGlja3MNCiAgICBheGlzLnRpY2tzLmxlbmd0aCA9IHVuaXQoMC4yLCAiY20iKSwNCiAgICBheGlzLm1pbm9yLnRpY2tzLmxlbmd0aCA9IHVuaXQoMC4xLCAiY20iKSwNCiAgICANCiAgICAjIE1lbmdhdHVyIHRlYmFsIGdhcmlzIHN1bWJ1IFgNCiAgICBheGlzLmxpbmUueCA9IGVsZW1lbnRfbGluZShzaXplID0gMC42KSwNCiAgICANCiAgICAjIE1lbmdhdHVyIG1hcmdpbiBkYXJpIHBsb3Qgc2VjYXJhIHVtdW0NCiAgICBwbG90Lm1hcmdpbiA9IG1hcmdpbigxLjUsIDAsIDAsIDEsICJjbSIpICwNCiAgICANCiAgICBwbG90LmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJ3aGl0ZSIsIGNvbG91ciA9IE5BKQ0KICApICsgDQogIA0KICAjIE1lbWJ1YXQgaGlnaGxpZ2h0IHBhZGEgcmVudGFuZyB0YWh1biAxNTU1IHMuZC4gMTY0OA0KICBhbm5vdGF0ZShnZW9tID0gInJlY3QiLCB4bWluID0gMTU1NSwgeG1heCA9IDE2NDgsIHltaW4gPSAwLCB5bWF4ID0gSW5mLA0KICAgICAgICAgICBmaWxsID0gImNvcmFsIiwgYWxwaGEgPSAwLjIpICsNCiAgDQogIGFubm90YXRlKCJ0ZXh0IiwgeCA9IDE1ODUsIHkgPSAyNTAsIGxhYmVsID0gIkV4ZWN1dGVkIiwgc2l6ZSA9IDQuMiwgaGp1c3QgPSAwKSArIA0KICBhbm5vdGF0ZSgidGV4dCIsIHggPSAxNzEwLCB5ID0gODUwLCBsYWJlbCA9ICJBY2N1c2VkIiwgc2l6ZSA9IDQuMiwgaGp1c3QgPSAwKSArDQogIGFubm90YXRlKCJ0ZXh0IiwgeCA9IDE1MTUsIHkgPSAyNzIwLCBsYWJlbCA9ICJQUk9URVNUQU5UIiwgc2l6ZSA9IDQuMiwgaGp1c3QgPSAxKSArDQogIGFubm90YXRlKCJ0ZXh0IiwgeCA9IDE1MTUsIHkgPSAyMzIwLCBsYWJlbCA9ICJSRUZPUk1BVElPTiIsIHNpemUgPSA0LjIsIGhqdXN0ID0gMSkgKw0KICBhbm5vdGF0ZSgidGV4dCIsIHggPSAxNjUwLCB5ID0gNTcyMCwgbGFiZWwgPSAiQ0FUSE9MSUMiLCBzaXplID0gNC4yLCBoanVzdCA9IDApICsNCiAgYW5ub3RhdGUoInRleHQiLCB4ID0gMTY1MCwgeSA9IDUzMjAsIGxhYmVsID0gIkNPVU5URVItUkVGT1JNQVRJT04iLCBzaXplID0gNC4yLCBoanVzdCA9IDApICsNCiAgDQogIGdlb21fc2VnbWVudChhZXMoeCA9IDE1MTUsIHhlbmQgPSAxNTE1LCB5ID0gMCwgeWVuZCA9IDIyMDApLCANCiAgICAgICAgICAgICAgIGxpbmV0eXBlID0gImRvdHRlZCIsIGNvbG9yID0gImNvcmFsIiwgc2l6ZSA9IDEuMykgKw0KICANCiAgZ2VvbV9sYWJlbChhZXMoeCA9IC1JbmYsIHkgPSBJbmYsIGxhYmVsID0gIkJ5IGRlY2FkZSIpLCANCiAgICAgICAgICAgICBoanVzdCA9IDAsIHZqdXN0ID0gMC44LCBzaXplID0gNSwgbGFiZWwuc2l6ZSA9IDApIA0KDQojIE1lbmFtcGlsa2FuIHBsb3QNCmxpbmVfY2hhcnQNCmBgYA0KDQpTZXRlbGFoIHBlbWJ1YXRhbiBsaW5lIGNoYXJ0IGJlcmhhc2lsLCBzYWF0bnlhIHVudHVrIG1lbnlpbXBhbiBwbG90IHRlcnNlYnV0IHVudHVrIGRpa29tYmluYXNpa2FuIGRlbmdhbiBwbG90IHlhbmcgbGFpbi4NCg0KYGBge3Igd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0NCmdnc2F2ZShmaWxlbmFtZSA9ICIuLi9hc3NldC9saW5lX2NoYXJ0LnBuZyIsIHBsb3QgPSBsaW5lX2NoYXJ0LCB3aWR0aCA9IDYuNiwgaGVpZ2h0ID0gNC4zLCBkcGkgPSAzMDApDQpgYGANCg0KDQojIDUuIEZpbmFsIFJlc3VsdA0KDQpMYW5na2FoIHRlcmFraGlyIHlhaXR1IG1lbmdnYWJ1bmdrYW4gc2VtdWEgcGxvdCBtZW5qYWRpIHNhdHUga2VzYXR1YW4gcGxvdC4gQmViZXJhcGEgbGFuZ2thaCB5YW5nIGhhcnVzIGRpbGFrdWthbiB5YWl0dToNCg0KMS4gU2lhcGthbiBgYnViYmxlX2NoYXJ0LnBuZ2AsIGBzdGFja2VkX2Jhcl9jaGFydC5wbmdgLCBkYW4gYGxpbmVfY2hhcnQucG5nYC4NCjIuIE1lbW90b25nIGJhZ2lhbiBwbG90IHlhbmcgZGlwZXJsdWthbiBzYWphIChjcm9wcGluZykuDQozLiBNZW5nZ2FidW5na2FuIHNlbXVhIGNoYXJ0IG1lbmphZGkgc2VidWFoIGNoYXJ0Lg0KNC4gTWVuYW1iYWggaGVhZGVyIGdhcmlzIG1lcmFoIHNlYmFnYWkgY2lyaSBraGFzIFRoZSBFY29ub21pc3QuDQo1LiBUYW1iYWhrYW4gdGVrcyBsYWluIHlhbmcgZGlwZXJsdWthbi4NCg0KUGVydGFtYSB5YWl0dSBtZW55aWFwa2FuIGdhbWJhciBkYXJpIGJ1YmJsZSBjaGFydCB5YW5nIHN1ZGFoIGRpc2ltcGFuLiBEYWxhbSBoYWwgaW5pLCBraXRhIGhhbnlhIG1lbWJ1dHVoa2FuIGJhZ2lhbiBhbmdrYSBkYW4gdmlzdWFsaXNhc2lueWEgc2FqYS4gT2xlaCBrYXJlbmEgaXR1LCBwZXJsdSBraXRhIGJ1YW5nIHNpc2kgbGFpbiB5YW5nIHRpZGFrIGRpZ3VuYWthbi4NCg0KYGBge3Igd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0NCiMgTWVtYmFjYSBnYW1iYXIgYnViYmxlIGNoYXJ0DQpidWJibGVfY2hhcnRfaW1nIDwtIGltYWdlX3JlYWQoIi4uL2Fzc2V0L2J1YmJsZV9jaGFydC5wbmciKQ0KDQojIE1lbnlpbXBhbiBsZWJhciBkYW4gdGluZ2dpIHBsb3QNCmltZ193aWR0aCA8LSBpbWFnZV9pbmZvKGJ1YmJsZV9jaGFydF9pbWcpJHdpZHRoDQppbWdfaGVpZ2h0IDwtIGltYWdlX2luZm8oYnViYmxlX2NoYXJ0X2ltZykkaGVpZ2h0DQoNCiMgTWVuYW1iYWggd2hpdGUgc3BhY2UgZGkgYXRhcyBwbG90DQpibGFua19zcGFjZSA8LSBpbWFnZV9ibGFuayh3aWR0aCA9IGltZ193aWR0aCwgaGVpZ2h0ID0gMjAwLCBjb2xvciA9ICJ3aGl0ZSIpDQoNCiMgTWVsYWt1a2FuIHByb3NlcyBzdGFja2luZyAobWVuYW1iYWgpIGJsYW5rIHNwYWNlIGtlIGJ1YmJsZSBjaGFydA0KYnViYmxlX2NoYXJ0X2ltZyA8LSBpbWFnZV9hcHBlbmQoYyhibGFua19zcGFjZSwgYnViYmxlX2NoYXJ0X2ltZyksIHN0YWNrID0gVFJVRSkNCg0KIyBwcm9zZXMgY3JvcHBpbmcgZGVuZ2FuIHVrdXJhbiB5YW5nIGRpYnV0dWhrYW4NCmNyb3BwZWRfYnViYmxlX2NoYXJ0X2ltZyA8LSBpbWFnZV9jcm9wKGJ1YmJsZV9jaGFydF9pbWcsICIzMzB4MTcwMCswKzAiKQ0KDQojIE1lbnlpbXBhbiBnYW1iYXIgaGFzaWwgY3JvcA0KaW1hZ2Vfd3JpdGUoY3JvcHBlZF9idWJibGVfY2hhcnRfaW1nLCAiLi4vYXNzZXQvY3JvcHBlZF9idWJibGVfY2hhcnQucG5nIikNCg0KYGBgDQoNClByb3NlcyBzZWxhbmp1dG55YSB5YWl0dSBtZW5nZ2FidW5na2FuICoqYnViYmxlIGNoYXJ0KiogIHlhbmcgc3VkYWggZGkgY3JvcCBkZW5nYW4gKipzdGFja2VkIGJhciBjaGFydCoqLg0KDQpgYGB7ciB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQ0KIyBNZW55aW1wYW4gaGFzaWwga29tYmluYXNpDQpwbmcoIi4uL2Fzc2V0L2J1YmJsZV9iYXJfY29tYmluZWQucG5nIiwgd2lkdGggPSAxMCwgaGVpZ2h0ID0gNywgdW5pdHMgPSAiaW4iLCByZXMgPSAzMDApDQoNCiMgTWVtYmFjYSBwbG90IGJ1YmJsZSBjaGFydA0KaW1nMSA8LSByYXN0ZXJHcm9iKGFzLnJhc3RlcihyZWFkUE5HKCIuLi9hc3NldC9jcm9wcGVkX2J1YmJsZV9jaGFydC5wbmciKSksIGludGVycG9sYXRlID0gVFJVRSkNCg0KIyBNZW1iYWNhIHBsb3Qgc3RhY2tlZCBiYXIgY2hhcnQNCmltZzIgPC0gcmFzdGVyR3JvYihhcy5yYXN0ZXIocmVhZFBORygiLi4vYXNzZXQvc3RhY2tlZF9iYXJfY2hhcnQucG5nIikpLCBpbnRlcnBvbGF0ZSA9IFRSVUUpDQoNCiMgUHJvc2VzIG1lbmdnYWJ1bmdrYW4gcGxvdA0KZ3JpZC5hcnJhbmdlKGltZzEsIGltZzIsIG5jb2wgPSAyLCB3aWR0aHMgPSBjKDAuNCwgMy4zNSkpDQoNCmBgYA0KDQpTZWxhbmp1dG55YSB5YWl0dSBtZW5nZ2FidW5na2FuICoqYnViYmxlIGNoYXJ0KiogZGFuICoqc3RhY2tlZCBiYXIgY2hhcnQqKiB5YW5nIHN1ZGFoIGRpZ2FidW5na2FuIHNlYmVsdW1ueWEgZGVuZ2FuICoqbGluZSBjaGFydCoqIGRhbiBkaXNpbXBhbiBkZW5nYW4gbmFtYSBgYWxsX2NoYXJ0X2NvbWJpbmVkLnBuZ2AuDQoNCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9DQojIE1lbnlpbXBhbiBnYW1iYXIgaGFzaWwga29tYmluYXNpIDMgcGxvdA0KcG5nKCIuLi9hc3NldC9hbGxfY2hhcnRfY29tYmluZWQucG5nIiwgd2lkdGggPSA3LjI4LCBoZWlnaHQgPSA0LjMsIHVuaXRzID0gImluIiwgcmVzID0gNTAwKQ0KDQojIE1lbWJhY2EgZ2FtYmFyIGtvbWJpbmFzaSBidWJibGUgY2hhcnQgZGFuIHN0YWNrZWQgYmFyIGNoYXJ0IGRlbmdhbiBsaW5lIGNoYXJ0DQpidWJibGVfYmFyX2NvbWJpbmVkIDwtIHJhc3Rlckdyb2IoYXMucmFzdGVyKHJlYWRQTkcoIi4uL2Fzc2V0L2J1YmJsZV9iYXJfY29tYmluZWQucG5nIikpLCBpbnRlcnBvbGF0ZSA9IFRSVUUpDQpsaW5lX2NoYXJ0IDwtIHJhc3Rlckdyb2IoYXMucmFzdGVyKHJlYWRQTkcoIi4uL2Fzc2V0L2xpbmVfY2hhcnQucG5nIikpLCBpbnRlcnBvbGF0ZSA9IFRSVUUpDQoNCiMgTWVueWlhcGthbiBoYWxhbWFuIGJhcnUgdW50dWsgbWVsZXRha2FuIHNtdWEgcGxvdA0KZ3JpZC5uZXdwYWdlKCkNCg0KIyBNZW5nYXR1ciBwb3Npc2kgZGFuIHVrdXJhbiBnYW1iYXIgZGFyaSBidWJibGVfYmFyX2NvbWJpbmVkDQpwdXNoVmlld3BvcnQodmlld3BvcnQoeCA9IDAuMDIsIHkgPSAwLjQ5LCB3aWR0aCA9IDAuNzUsIGp1c3QgPSAibGVmdCIpKQ0KDQojIE1lbGV0YWthbiBnYW1iYXIgYnViYmxlX2Jhcl9jb21iaW5lZCBrZSB0ZW1wYXQgeWFuZyBzdWRhaCBkaXNlZGlha2FuDQpncmlkLmRyYXcoYnViYmxlX2Jhcl9jb21iaW5lZCkNCnVwVmlld3BvcnQoKQ0KDQojIE1lbmdhdHVyIHBvc2lzaSBkYW4gdWt1cmFuIGdhbWJhciBkYXJpIGxpbmUgY2hhcnQNCnB1c2hWaWV3cG9ydCh2aWV3cG9ydCh4ID0gMC40MywgeSA9IDAuMzM1LCB3aWR0aCA9IDAuMzcpKQ0KDQojIE1lbGV0YWthbiBnYW1iYXIgbGluZSBjaGFydCBrZSB0ZW1wYXQgeWFuZyBzdWRhaCBkaXNlZGlha2FuDQpncmlkLmRyYXcobGluZV9jaGFydCkNCnVwVmlld3BvcnQoKQ0KYGBgDQoNCk1lbmFtYmFoIGhlYWRlciBnYXJpcyBtZXJhaCBzZWJhZ2FpIGNpcmkga2hhcyBUaGUgRWNvbm9taXN0IGRhbiBtZW5hbWJhaGthbiB0ZWtzIGxhaW4geWFuZyBkaXBlcmx1a2FuIHNlcGVydGkgZm9vdG5vdGUgeWFuZyBtZW5qZWxhc2thbiBzdW1iZXIgZGF0YSB5YW5nIGRpZ3VuYWthbi4gQmVyaWt1dCBsYW5na2FoLWxhbmdrYWggeWFuZyBkaWxha3VrYW46DQoNCjEuIE1lbmFtYmFoa2FuIGdhcmlzIG1lcmFoIHRpcGlzIHNlcGFuamFuZyBwbG90IG1lbmdndW5ha2FuIGZ1bmdzaSBgZ3JpZC5yZWFjdGAuDQoyLiBNZW5hbWJhaGthbiBrb3RhayBnYXJpcyBtZXJhaCBkaSBzZWJlbGFoIGtpcmkgYXRhcy4gU2V0ZWxhaCBnYXJpcyB0aXBpcyBtZW5nZ3VuYWthbiBmdW5nc2kgYGdyaWQucmVhY3RgLg0KMy4gTWVuYW1iYWhrYW4ganVkdWwgdXRhbWEgZGFuIHN1YiBqdWR1bCBwYWRhIHBsb3QgbWVuZ2d1bmFrYW4gZnVuZ3NpIGBncmlkLnRleHRgLiANCg0KICAgIC0gTmlsYWkgYHhgIG1lcnVwYWthbiBwb3Npc2kgdGVrcyBzZWNhcmEgaG9yaXpvbnRhbC4gU2VtYWtpbiBiZXNhciBtYWthIHBvc2lzaSBzZW1ha2luIGtlIGthbmFuLiANCiAgICAtIE5pbGFpIGB5YCBtZXJ1cGFrYW4gcG9zaXNpIHRla3Mgc2VjYXJhIHZlcnRpa2FsLiBTZW1ha2luIGJlc2FyIG1ha2Egc2VtYWtpbiBrZSBhdGFzLg0KICAgIC0gRnVuZ3NpIGBncGFyYCB1bnR1ayBtZW5nYXR1ciB1a3VyYW4gaHVydWYuDQogICAgDQo0LiBNZW5hbWJhaGthbiBmb290bm90ZSBkaSBiYXdhaCBwbG90IHlhbmcgbWVuamVsYXNrYW4gc3VtYmVyIGRhdGEgeWFuZyBkaXJ1anVrIGRhbGFtIHBlbWJ1YXRhbiBwbG90LiBVbnR1ayBtZW1idWF0IGZvb3Rub3RlIGRpZ3VuYWthbiBmdW5nc2kgYGdyaWQudGV4dGAsDQo1LiBNZW1idWF0IGtvdGFrIHBlcnNlZ2kgcGFuamFuZyBkaSBhdGFzIGJ1YmJsZSBjaGFydCBtZW5nZ3VuYWthbiBmdW5nc2kgYGdyaWQucmVhY3RgLg0KDQogICAgLSBOaWxhaSBgeGAgbWVydXBha2FuIHBvc2lzaSB0ZWtzIHNlY2FyYSBob3Jpem9udGFsLiBTZW1ha2luIGJlc2FyIG1ha2EgcG9zaXNpIHNlbWFraW4ga2Uga2FuYW4uIA0KICAgIC0gTmlsYWkgYHlgIG1lcnVwYWthbiBwb3Npc2kgdGVrcyBzZWNhcmEgdmVydGlrYWwuIFNlbWFraW4gYmVzYXIgbWFrYSBzZW1ha2luIGtlIGF0YXMuDQogICAgLSBGdW5nc2kgYHdpZHRoYCB1bnR1ayBtZW5nYXR1ciBsZWJhciBib3guDQogICAgLSBGdW5nc2kgYGhlaWdodGAgdW50dWsgbWVuZ2F0dXIgdGluZ2dpIGJveC4NCiAgICAtIEZ1bmdzaSBgZ3BhcmAgdW50dWsgbWVuZW50dWthbiB3YXJuYSBkYW4gdGluZ2thdCB0cmFuc3BhcmFuc2kgd2FybmEuDQogICAgDQo2LiBNZW5hbWJhaGthbiB0ZWtzIGRpIGRhbGFtIGtvdGFrIHBlcnNlZ2kgcGFuamFuZyBtZW5nZ3VuYWthbiBmdW5nc2kgYGdyaWQudGV4dGAuDQo3LiBNZW1idWF0IHNlZ2l0aWdhIHRlcmJhbGlrIGJlcnVrdXJhbiBrZWNpbCBkaSBiYXdhaCBrb3RhayBtZW5nZ3VuYWthbiBmdW5nc2kgYGdyaWQucG9seWdvbmAuDQoNCjxociBzdHlsZT0iYm9yZGVyOiAxcHggc29saWQgYmxhY2s7Ij4NCg0KTWFyaSBraXRhIGltcGxlbWVudGFzaWthbiBrZSBkYWxhbSBrb2RlIG1lbmdndW5ha2FuIGJhaGFzYSBSIHNlYmFnYWkgYmVyaWt1dDouDQoNCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9DQojIE1lbmFtYmFoIGhlYWRlciBnYXJpcyBtZXJhaCBzZWJhZ2FpIGNpcmkga2hhcyBUaGUgRWNvbm9taXN0DQpncmlkLnJlY3QoeCA9IDEsIHkgPSAwLjk5NywNCiAgICAgICAgICBoanVzdCA9IDEsIHZqdXN0ID0gMCwNCiAgICAgICAgICBncCA9IGdwYXIoZmlsbD0nI0U1MDAxQycsbHdkPTApKQ0KDQpncmlkLnJlY3QoeCA9IDAuMDEzLCB5ID0gMC45NCwNCiAgICAgICAgICBoanVzdCA9IDEsIHZqdXN0ID0gMCwNCiAgICAgICAgICBncCA9IGdwYXIoZmlsbD0nI0U1MDAxQycsbHdkPTApKQ0KDQojIE1lbmFtYmFoIGp1ZHVsIHBhZGEgcGxvdA0KZ3JpZC50ZXh0KCJEb3VibGUsIGRvdWJsZSB0b2lsIGFuZCB0cm91YmxlIiwNCiAgICAgICAgICB4ID0gMC4zMzAsIHkgPSAwLjkzNSwNCiAgICAgICAgICBoanVzdCA9IDEsIHZqdXN0ID0gMCwNCiAgICAgICAgICBncCA9IGdwYXIoZm9udHNpemU9MTEsIGZvbnRmYWNlPSJib2xkIikpDQoNCmdyaWQudGV4dCgiRXVyb3BlYW4gd2l0Y2hjcmFmdCwgMTMwMC0xODUwIiwNCiAgICAgICAgICB4ID0gMC4yMzUsIHkgPSAwLjksDQogICAgICAgICAgaGp1c3QgPSAxLCB2anVzdCA9IDAsDQogICAgICAgICAgZ3AgPSBncGFyKGZvbnRzaXplPTcuOCkpDQoNCiMgTWVuYW1iYWggZm9vdG5vdGUgDQpncmlkLnRleHQoJ1NvdXJjZTogIldpdGNoIFRyaWFscyIsIGJ5IFBldGVyIExlZXNvbiBhbmQgSmFjb2IgUnVzcywnLA0KICAgICAgICAgIHggPSAwLjMwOCwgeSA9IDAuMTIsDQogICAgICAgICAgaGp1c3QgPSAxLCB2anVzdCA9IDAsDQogICAgICAgICAgZ3AgPSBncGFyKGZvbnRzaXplPTUuNjcpKQ0KDQpncmlkLnRleHQoIkVjb25vbWljIEpvdXJuYWwsIiwNCiAgICAgICAgICB4ID0gMC40MDMsIHkgPSAwLjEyLA0KICAgICAgICAgIGhqdXN0ID0gMSwgdmp1c3QgPSAwLA0KICAgICAgICAgIGdwID0gZ3Bhcihmb250ZmFjZSA9ICJpdGFsaWMiLCBmb250c2l6ZT01LjY3KSkNCg0KZ3JpZC50ZXh0KCdBdWd1c3QsIDIwMTcnLA0KICAgICAgICAgIHggPSAwLjQ3LCB5ID0gMC4xMiwNCiAgICAgICAgICBoanVzdCA9IDEsIHZqdXN0ID0gMCwNCiAgICAgICAgICBncCA9IGdwYXIoZm9udHNpemU9NS42NykpDQoNCmdyaWQudGV4dCgnRWNvbm9taXN0LmNvbScsDQogICAgICAgICAgeCA9IDAuMDgsIHkgPSAwLjA5LA0KICAgICAgICAgIGhqdXN0ID0gMSwgdmp1c3QgPSAwLA0KICAgICAgICAgIGdwID0gZ3Bhcihmb250c2l6ZT01LjY3LCBjb2wgPSAiI0MwQzBDMCIpKQ0KDQojIE1lbWJ1YXQgb3JhbmdlIGJveCBkaSBhdGFzIGJ1YmJsZSBjaGFydA0KZ3JpZC5yZWN0KHggPSAwLjA5NSwgeSA9IDAuODA1LA0KICAgICAgICAgIHdpZHRoID0gMC4xMzUsIGhlaWdodCA9IDAuMDc0LA0KICAgICAgICAgIGdwID0gZ3BhcihmaWxsID0gImNvcmFsIiwgYWxwaGEgPSAwLjcsIGNvbCA9IE5BKSkNCg0KIyBNZW5hbWJhaGthbiB0ZWtzIGRpIGRhbGFtIG9yYW5nZSBib3gNCmdyaWQudGV4dCgnV2l0Y2hjcmFmdCB0cmlhbHMnLA0KICAgICAgICAgIHggPSAwLjE0MywgeSA9IDAuODE1LA0KICAgICAgICAgIGhqdXN0ID0gMSwgdmp1c3QgPSAwLA0KICAgICAgICAgIGdwID0gZ3Bhcihmb250c2l6ZT03KSkNCg0KZ3JpZC50ZXh0KCdwZXIgMTAwLDAwMCBwZW9wbGUnLA0KICAgICAgICAgIHggPSAwLjE1MywgeSA9IDAuNzg1LA0KICAgICAgICAgIGhqdXN0ID0gMSwgdmp1c3QgPSAwLA0KICAgICAgICAgIGdwID0gZ3Bhcihmb250c2l6ZT03KSkNCg0KIyBNZW1idWF0IHNlZ2l0aWdhIHRlcmJhbGlrIHlhbmcgYWRhIGRpIG9ybmFnZSBib3gNCnggPC0gYygwLjA3ODUsIDAuMDcyNSwgMC4wODQ1KSANCnkgPC0gYygwLjc2MSwgMC43NjgsIDAuNzY4KQ0KDQpncmlkLnBvbHlnb24oeCA9IHgsIHkgPSB5LCBncCA9IGdwYXIoZmlsbCA9ICJjb3JhbCIsIGFscGhhID0gMC43LCBjb2wgPSBOQSkpDQoNCmBgYA0KDQpUZXJrYWhpciB5YWl0dSBtZWxha3VrYW4gY3JvcHBpbmcgdW50dWsgbWVueWVsZWtzaSBiYWdpYW4geWFuZyBwZW50aW5nIHNhamEuIFdoaXRlIHNwYWNlIHlhbmcgYmVybGViaWggYWthbiBkaWhhcHVzIChjcm9wKS4gTGFsdSBoYXNpbCBjcm9wIGFrYW4gZGlzaW1wYW4gZGVuZ2FuIG5hbWEgYGZpbmFsLnBuZ2AgZGFuIG1lcnVwYWthbiBoYXNpbCBha2hpciBkYXJpIHByb3llayBpbmkuDQoNCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9DQojIE1lbWJhY2EgcGxvdCB5YW5nIGJlcmlzaSAzIGNoYXJ0DQpmaW5hbF9pbWFnZSA8LSBpbWFnZV9yZWFkKCIuLi9hc3NldC9hbGxfY2hhcnRfY29tYmluZWQucG5nIikNCg0KIyBwcm9zZXMgY3JvcHBpbmcgZGVuZ2FuIHVrdXJhbiB5YW5nIGRpYnV0dWhrYW4NCmNyb3BwZWRfZmluYWxfaW1hZ2UgPC0gaW1hZ2VfY3JvcChmaW5hbF9pbWFnZSwgIjIzMDB4MTk4MCswKzAiKQ0KDQojIE1lbnlpbXBhbiBnYW1iYXIgaGFzaWwgY3JvcA0KaW1hZ2Vfd3JpdGUoY3JvcHBlZF9maW5hbF9pbWFnZSwgIi4uL2Fzc2V0L2ZpbmFsLnBuZyIpDQpgYGANCg0KIVtdKC4uL2Fzc2V0L2ZpbmFsLnBuZykNCg0KIyBDb25jbHVzaW9uDQoNClByb2plY3QgcmVwbGlrYSB2aXN1YWxpc2FzaSBkYXJpIG1hamFsYWggVGhlIEVjb25vbWlzdCBtZW5nZ3VuYWthbiBsaWJyYXJ5IGBnZ3Bsb3RgIG1lcnVwYWthbiBzYWxhaCBzYXR1IGNhcmEgY2VwYXQgYmFnaSBBbmRhIHlhbmcgaW5naW4gYmVsYWphciBiYWhhc2EgcGVtcm9ncmFtYW4gUi4gS2FyZW5hIGRlbmdhbiBtZW5nZXJqYWthbiBwcm9qZWN0IGluaSwgQW5kYSBha2FuIG1lbmdndW5ha2FuIGJlcmJhZ2FpIGZ1bmdzaSB5YW5nIGFkYSBkaSBSLiBTZWxhaW4gaXR1LCBkZW5nYW4gbWVtcGVsYWphcmkgdmlzdWFsaXNhc2kgeWFuZyBhZGEgZGkgbWFqYWxhaCBUaGUgRWNvbm9taXN0LCBraXRhIGJpc2EgYmVsYWphciBiYWdhaW1hbmEgY2FyYSBtZW1idWF0IHZpc3VhbGlzYXNpIHlhbmcgdGVwYXQsIG1lbmFyaWssIGRhbiBpbnNpZ2h0ZnVsIHlhbmcgYmlzYSBtZW5qZWxhc2thbiBzdWF0dSBkYXRhIGRlbmdhbiBiYWlrIGRhbGFtIGJlbnR1ayB2aXN1YWwuDQoNCg0KU2V0ZWxhaCBiZXJoYXNpbCBtZWxha3VrYW4gcmVwbGlrYSB2aXN1YWxpc2FzaSBkYXJpIG1hamFsYWggVGhlIEVjb25vbWlzdCwgZGlkYXBhdCBiYWh3YSBoYXNpbCB2aXN1YWxpc2FzaSB0aWRhayAxMDAlIG1pcmlwIGRlbmdhbiB2aXN1YWxpc2FzaSB5YW5nIG1lbmphZGkgc3VtYmVyIHJ1anVrYW4uIEhhbCBpbmkgZGlrYXJlbmFrYW4gYmViZXJhcGEgZmFrdG9yIHNlcGVydGkga2V0aWRha3RhaHVhbiBwZW51bGlzIHRlbnRhbmcgamVuaXMgZm9udCB5YW5nIGRpZ3VuYWthbiBkYW4gbGFpbm55YS4gVW50dWsgaXR1LCBpbXByb3ZlbWVudCB5YW5nIGJpc2EgZGlsYWt1a2FuIHlhaXR1IG1lbmdndW5ha2FuIGplbmlzIGZvbnQgeWFuZyBzYW1hIHNlcGVydGkgcGFkYSBzdW1iZXIgcnVqdWthbiBhZ2FyIGhhc2lsIHJlcGxpa2Egc2VtYWtpbiBtaXJpcCAoYmlsYSBtZW11bmdraW5rYW4pLg0KDQoNCiMgUmVmZXJlbmNlcw0KDQoxLiAqKltUaGUgUG9saXRpY2FsIEVjb25vbXkgb2YgV2l0Y2hjcmFmdF0qKiAtIGh0dHBzOi8vd3d3LmVjb25vbWlzdC5jb20vZ3JhcGhpYy1kZXRhaWwvMjAxNy8xMC8zMS90aGUtcG9saXRpY2FsLWVjb25vbXktb2Ytd2l0Y2hjcmFmdA0KMi4gKipbV2l0Y2ggVHJpYWxzIFBhcGVyXSoqIC0gaHR0cHM6Ly9vbmxpbmVsaWJyYXJ5LndpbGV5LmNvbS9kb2kvYWJzLzEwLjExMTEvZWNvai4xMjQ5OA0KDQo8ZGl2IGNsYXNzPSJ0b2NpZnktZXh0ZW5kLXBhZ2UiIGRhdGEtdW5pcXVlPSJ0b2NpZnktZXh0ZW5kLXBhZ2UiIHN0eWxlPSJoZWlnaHQ6IDAiPjwvZGl2Pg==